Compare commits
	
		
			45 Commits
		
	
	
		
			eslint-upd
			...
			v1.2-RC6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					2d19975917 | ||
| 
						 | 
					29e7942a4e | ||
| 
						 | 
					4f853016d6 | ||
| 
						 | 
					abbb31f384 | ||
| 
						 | 
					ae8ed2d71f | ||
| 
						 | 
					9ecb257e5d | ||
| 
						 | 
					5e2d8106a5 | ||
| 
						 | 
					32aa412d1e | ||
| 
						 | 
					cb7f431fdf | ||
| 
						 | 
					e667b22b3c | ||
| 
						 | 
					7d51d9c1eb | ||
| 
						 | 
					9124f4f566 | ||
| 
						 | 
					d9baa94970 | ||
| 
						 | 
					afeb89a51a | ||
| 
						 | 
					07992f0b2a | ||
| 
						 | 
					a5c4508578 | ||
| 
						 | 
					a4fab3ce8a | ||
| 
						 | 
					97d80f57cc | ||
| 
						 | 
					41138a1731 | ||
| 
						 | 
					a54a2f8f84 | ||
| 
						 | 
					5bbe710552 | ||
| 
						 | 
					f2d34d7c33 | ||
| 
						 | 
					8fa1770885 | ||
| 
						 | 
					7221dc1ac6 | ||
| 
						 | 
					25bb9939d6 | ||
| 
						 | 
					e7e12504f2 | ||
| 
						 | 
					63bf856d89 | ||
| 
						 | 
					e3dcd51f8d | ||
| 
						 | 
					cb63f4eca1 | ||
| 
						 | 
					3f60c3c0f1 | ||
| 
						 | 
					16bb22e834 | ||
| 
						 | 
					b1467548da | ||
| 
						 | 
					baa7c0bc58 | ||
| 
						 | 
					73b81e38e7 | ||
| 
						 | 
					8b088b7a2c | ||
| 
						 | 
					894da25461 | ||
| 
						 | 
					87d63806b9 | ||
| 
						 | 
					f0e7f8cfc0 | ||
| 
						 | 
					db597e1e93 | ||
| 
						 | 
					98db273f5d | ||
| 
						 | 
					8a6f944655 | ||
| 
						 | 
					bacad24811 | ||
| 
						 | 
					8cc58946cf | ||
| 
						 | 
					3338bc1000 | ||
| 
						 | 
					80c20b3d05 | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -37,4 +37,7 @@ protractor/logs
 | 
			
		||||
# npm-debug log
 | 
			
		||||
npm-debug.log
 | 
			
		||||
 | 
			
		||||
# karma reports
 | 
			
		||||
report.*.json
 | 
			
		||||
 | 
			
		||||
package-lock.json
 | 
			
		||||
 
 | 
			
		||||
@@ -113,7 +113,10 @@
 | 
			
		||||
        openmct.install(openmct.plugins.LADTable());
 | 
			
		||||
        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']));
 | 
			
		||||
        openmct.install(openmct.plugins.ClearData(
 | 
			
		||||
            ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
 | 
			
		||||
            {indicator: true}
 | 
			
		||||
        ));
 | 
			
		||||
        openmct.start();
 | 
			
		||||
    </script>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
/*global module,process*/
 | 
			
		||||
 | 
			
		||||
const devMode = process.env.NODE_ENV !== 'production';
 | 
			
		||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
 | 
			
		||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'FirefoxHeadless'];
 | 
			
		||||
const coverageEnabled = process.env.COVERAGE === 'true';
 | 
			
		||||
const reporters = ['progress', 'html'];
 | 
			
		||||
 | 
			
		||||
@@ -95,6 +95,7 @@ module.exports = (config) => {
 | 
			
		||||
            stats: 'errors-only',
 | 
			
		||||
            logLevel: 'warn'
 | 
			
		||||
        },
 | 
			
		||||
        singleRun: true
 | 
			
		||||
        singleRun: true,
 | 
			
		||||
        browserNoActivityTimeout: 90000
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@
 | 
			
		||||
    "jsdoc": "^3.3.2",
 | 
			
		||||
    "karma": "^2.0.3",
 | 
			
		||||
    "karma-chrome-launcher": "^2.2.0",
 | 
			
		||||
    "karma-firefox-launcher": "^1.3.0",
 | 
			
		||||
    "karma-cli": "^1.0.1",
 | 
			
		||||
    "karma-coverage": "^1.1.2",
 | 
			
		||||
    "karma-coverage-istanbul-reporter": "^2.1.1",
 | 
			
		||||
@@ -59,7 +60,7 @@
 | 
			
		||||
    "moment-timezone": "0.5.28",
 | 
			
		||||
    "node-bourbon": "^4.2.3",
 | 
			
		||||
    "node-sass": "^4.9.2",
 | 
			
		||||
    "painterro": "^0.2.65",
 | 
			
		||||
    "painterro": "^1.0.35",
 | 
			
		||||
    "printj": "^1.2.1",
 | 
			
		||||
    "raw-loader": "^0.5.1",
 | 
			
		||||
    "request": "^2.69.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -31,13 +31,13 @@
 | 
			
		||||
    </mct-form>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="c-overlay__button-bar">
 | 
			
		||||
    <a class='c-button c-button--major'
 | 
			
		||||
    <button class='c-button c-button--major'
 | 
			
		||||
       ng-class="{ disabled: !createForm.$valid }"
 | 
			
		||||
       ng-click="ngModel.confirm()">
 | 
			
		||||
        OK
 | 
			
		||||
    </a>
 | 
			
		||||
    <a class='c-button  '
 | 
			
		||||
    </button>
 | 
			
		||||
    <button class='c-button  '
 | 
			
		||||
       ng-click="ngModel.cancel()">
 | 
			
		||||
        Cancel
 | 
			
		||||
    </a>
 | 
			
		||||
    </button>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -31,13 +31,13 @@
 | 
			
		||||
        </mct-include>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="c-overlay__button-bar">
 | 
			
		||||
        <a ng-repeat="option in ngModel.dialog.options"
 | 
			
		||||
        <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}}
 | 
			
		||||
        </a>
 | 
			
		||||
        </button>
 | 
			
		||||
    </div>
 | 
			
		||||
</mct-container>
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
    <div class="c-overlay__outer">
 | 
			
		||||
        <button ng-click="ngModel.cancel()"
 | 
			
		||||
           ng-if="ngModel.cancel"
 | 
			
		||||
           class="c-click-icon c-overlay__close-button icon-x-in-circle"></button>
 | 
			
		||||
           class="c-click-icon c-overlay__close-button icon-x"></button>
 | 
			
		||||
        <div class="c-overlay__contents" ng-transclude></div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -36,8 +36,6 @@ define(
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        EditPersistableObjectsPolicy.prototype.allow = function (action, context) {
 | 
			
		||||
            var identifier;
 | 
			
		||||
            var provider;
 | 
			
		||||
            var domainObject = context.domainObject;
 | 
			
		||||
            var key = action.getMetadata().key;
 | 
			
		||||
            var category = (context || {}).category;
 | 
			
		||||
@@ -46,9 +44,8 @@ define(
 | 
			
		||||
            // 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') {
 | 
			
		||||
                identifier = objectUtils.parseKeyString(domainObject.getId());
 | 
			
		||||
                provider = this.openmct.objects.getProvider(identifier);
 | 
			
		||||
                return provider.save !== undefined;
 | 
			
		||||
                let newStyleObject = objectUtils.toNewFormat(domainObject, domainObject.getId());
 | 
			
		||||
                return this.openmct.objects.isPersistable(newStyleObject);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ define(
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockObjectAPI = jasmine.createSpyObj('objectAPI', [
 | 
			
		||||
                    'getProvider'
 | 
			
		||||
                    'isPersistable'
 | 
			
		||||
                ]);
 | 
			
		||||
 | 
			
		||||
                mockAPI = {
 | 
			
		||||
@@ -69,34 +69,31 @@ define(
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("Applies to edit action", function () {
 | 
			
		||||
                mockObjectAPI.getProvider.and.returnValue({});
 | 
			
		||||
                expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
 | 
			
		||||
                expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                policy.allow(mockEditAction, testContext);
 | 
			
		||||
                expect(mockObjectAPI.getProvider).toHaveBeenCalled();
 | 
			
		||||
                expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("Applies to properties action", function () {
 | 
			
		||||
                mockObjectAPI.getProvider.and.returnValue({});
 | 
			
		||||
                expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
 | 
			
		||||
                expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                policy.allow(mockPropertiesAction, testContext);
 | 
			
		||||
                expect(mockObjectAPI.getProvider).toHaveBeenCalled();
 | 
			
		||||
                expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not apply to other actions", function () {
 | 
			
		||||
                mockObjectAPI.getProvider.and.returnValue({});
 | 
			
		||||
                expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
 | 
			
		||||
                expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                policy.allow(mockOtherAction, testContext);
 | 
			
		||||
                expect(mockObjectAPI.getProvider).not.toHaveBeenCalled();
 | 
			
		||||
                expect(mockObjectAPI.isPersistable).not.toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("Tests object provider for editability", function () {
 | 
			
		||||
                mockObjectAPI.getProvider.and.returnValue({});
 | 
			
		||||
                mockObjectAPI.isPersistable.and.returnValue(false);
 | 
			
		||||
                expect(policy.allow(mockEditAction, testContext)).toBe(false);
 | 
			
		||||
                expect(mockObjectAPI.getProvider).toHaveBeenCalled();
 | 
			
		||||
                mockObjectAPI.getProvider.and.returnValue({save: function () {}});
 | 
			
		||||
                expect(mockObjectAPI.isPersistable).toHaveBeenCalled();
 | 
			
		||||
                mockObjectAPI.isPersistable.and.returnValue(true);
 | 
			
		||||
                expect(policy.allow(mockEditAction, testContext)).toBe(true);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,13 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class="c-object-label">
 | 
			
		||||
    <div class="c-object-label__type-icon {{type.getCssClass()}}" ng-class="{ 'l-icon-link':location.isLink() }"></div>
 | 
			
		||||
<div class="c-object-label"
 | 
			
		||||
     ng-class="{ 'is-missing': model.status === 'missing' }"
 | 
			
		||||
>
 | 
			
		||||
    <div class="c-object-label__type-icon {{type.getCssClass()}}"
 | 
			
		||||
         ng-class="{ 'l-icon-link':location.isLink() }"
 | 
			
		||||
    >
 | 
			
		||||
        <span class="is-missing__indicator" title="This item is missing"></span>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class='c-object-label__name'>{{model.name}}</div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -48,9 +48,8 @@ define(
 | 
			
		||||
            // 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())) {
 | 
			
		||||
                var identifier = objectUtils.parseKeyString(parent.getId());
 | 
			
		||||
                var provider = this.openmct.objects.getProvider(identifier);
 | 
			
		||||
                return provider.save !== undefined;
 | 
			
		||||
                let newStyleObject = objectUtils.toNewFormat(parent, parent.getId());
 | 
			
		||||
                return this.openmct.objects.isPersistable(newStyleObject);
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ define(
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                objectAPI = jasmine.createSpyObj('objectsAPI', [
 | 
			
		||||
                    'getProvider'
 | 
			
		||||
                    'isPersistable'
 | 
			
		||||
                ]);
 | 
			
		||||
 | 
			
		||||
                mockOpenMCT = {
 | 
			
		||||
@@ -51,10 +51,6 @@ define(
 | 
			
		||||
                    'isEditContextRoot'
 | 
			
		||||
                ]);
 | 
			
		||||
                mockParent.getCapability.and.returnValue(mockEditorCapability);
 | 
			
		||||
 | 
			
		||||
                objectAPI.getProvider.and.returnValue({
 | 
			
		||||
                    save: function () {}
 | 
			
		||||
                });
 | 
			
		||||
                persistableCompositionPolicy = new PersistableCompositionPolicy(mockOpenMCT);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
@@ -65,19 +61,22 @@ define(
 | 
			
		||||
 | 
			
		||||
            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.getProvider.and.returnValue({});
 | 
			
		||||
                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.getProvider).not.toHaveBeenCalled();
 | 
			
		||||
                expect(objectAPI.isPersistable).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                mockEditorCapability.isEditContextRoot.and.returnValue(false);
 | 
			
		||||
                objectAPI.isPersistable.and.returnValue(true);
 | 
			
		||||
                expect(persistableCompositionPolicy.allow(mockParent, mockChild)).toBe(true);
 | 
			
		||||
                expect(objectAPI.getProvider).toHaveBeenCalled();
 | 
			
		||||
                expect(objectAPI.isPersistable).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -297,7 +297,8 @@ define([
 | 
			
		||||
                            "persistenceService",
 | 
			
		||||
                            "identifierService",
 | 
			
		||||
                            "notificationService",
 | 
			
		||||
                            "$q"
 | 
			
		||||
                            "$q",
 | 
			
		||||
                            "openmct"
 | 
			
		||||
                        ]
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,8 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    function () {
 | 
			
		||||
define(["objectUtils"],
 | 
			
		||||
    function (objectUtils) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Defines the `persistence` capability, used to trigger the
 | 
			
		||||
@@ -47,6 +47,7 @@ define(
 | 
			
		||||
            identifierService,
 | 
			
		||||
            notificationService,
 | 
			
		||||
            $q,
 | 
			
		||||
            openmct,
 | 
			
		||||
            domainObject
 | 
			
		||||
        ) {
 | 
			
		||||
            // Cache modified timestamp
 | 
			
		||||
@@ -58,6 +59,7 @@ define(
 | 
			
		||||
            this.persistenceService = persistenceService;
 | 
			
		||||
            this.notificationService = notificationService;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.openmct = openmct;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -66,7 +68,7 @@ define(
 | 
			
		||||
         */
 | 
			
		||||
        function rejectIfFalsey(value, $q) {
 | 
			
		||||
            if (!value) {
 | 
			
		||||
                return $q.reject("Error persisting object");
 | 
			
		||||
                return Promise.reject("Error persisting object");
 | 
			
		||||
            } else {
 | 
			
		||||
                return value;
 | 
			
		||||
            }
 | 
			
		||||
@@ -98,7 +100,7 @@ define(
 | 
			
		||||
                dismissable: true
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return $q.reject(error);
 | 
			
		||||
            return Promise.reject(error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -110,34 +112,16 @@ define(
 | 
			
		||||
         */
 | 
			
		||||
        PersistenceCapability.prototype.persist = function () {
 | 
			
		||||
            var self = this,
 | 
			
		||||
                domainObject = this.domainObject,
 | 
			
		||||
                model = domainObject.getModel(),
 | 
			
		||||
                modified = model.modified,
 | 
			
		||||
                persisted = model.persisted,
 | 
			
		||||
                persistenceService = this.persistenceService,
 | 
			
		||||
                persistenceFn = persisted !== undefined ?
 | 
			
		||||
                    this.persistenceService.updateObject :
 | 
			
		||||
                    this.persistenceService.createObject;
 | 
			
		||||
                domainObject = this.domainObject;
 | 
			
		||||
 | 
			
		||||
            if (persisted !== undefined && persisted === modified) {
 | 
			
		||||
                return this.$q.when(true);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Update persistence timestamp...
 | 
			
		||||
            domainObject.useCapability("mutation", function (m) {
 | 
			
		||||
                m.persisted = modified;
 | 
			
		||||
            }, modified);
 | 
			
		||||
 | 
			
		||||
            // ...and persist
 | 
			
		||||
            return persistenceFn.apply(persistenceService, [
 | 
			
		||||
                this.getSpace(),
 | 
			
		||||
                this.getKey(),
 | 
			
		||||
                domainObject.getModel()
 | 
			
		||||
            ]).then(function (result) {
 | 
			
		||||
                return rejectIfFalsey(result, self.$q);
 | 
			
		||||
            }).catch(function (error) {
 | 
			
		||||
                return notifyOnError(error, domainObject, self.notificationService, self.$q);
 | 
			
		||||
            });
 | 
			
		||||
            let newStyleObject = objectUtils.toNewFormat(domainObject.getModel(), domainObject.getId());
 | 
			
		||||
            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);
 | 
			
		||||
                });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
@@ -40,7 +39,8 @@ define(
 | 
			
		||||
                model,
 | 
			
		||||
                SPACE = "some space",
 | 
			
		||||
                persistence,
 | 
			
		||||
                happyPromise;
 | 
			
		||||
                mockOpenMCT,
 | 
			
		||||
                mockNewStyleDomainObject;
 | 
			
		||||
 | 
			
		||||
            function asPromise(value, doCatch) {
 | 
			
		||||
                return (value || {}).then ? value : {
 | 
			
		||||
@@ -56,7 +56,6 @@ define(
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                happyPromise = asPromise(true);
 | 
			
		||||
                model = { someKey: "some value", name: "domain object"};
 | 
			
		||||
 | 
			
		||||
                mockPersistenceService = jasmine.createSpyObj(
 | 
			
		||||
@@ -94,12 +93,23 @@ define(
 | 
			
		||||
                    },
 | 
			
		||||
                    useCapability: jasmine.createSpy()
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                mockNewStyleDomainObject = Object.assign({}, model);
 | 
			
		||||
                mockNewStyleDomainObject.identifier = {
 | 
			
		||||
                    namespace: "",
 | 
			
		||||
                    key: id
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 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);
 | 
			
		||||
@@ -110,51 +120,28 @@ define(
 | 
			
		||||
                    mockIdentifierService,
 | 
			
		||||
                    mockNofificationService,
 | 
			
		||||
                    mockQ,
 | 
			
		||||
                    mockOpenMCT,
 | 
			
		||||
                    mockDomainObject
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("successful persistence", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockPersistenceService.updateObject.and.returnValue(happyPromise);
 | 
			
		||||
                    mockPersistenceService.createObject.and.returnValue(happyPromise);
 | 
			
		||||
                    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(mockPersistenceService.createObject).not.toHaveBeenCalled();
 | 
			
		||||
                    expect(mockOpenMCT.objects.save).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
 | 
			
		||||
                    expect(mockPersistenceService.createObject).toHaveBeenCalledWith(
 | 
			
		||||
                        SPACE,
 | 
			
		||||
                        key,
 | 
			
		||||
                        model
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("updates previously persisted objects with the persistence service", function () {
 | 
			
		||||
                    // Verify precondition; no call made during constructor
 | 
			
		||||
                    expect(mockPersistenceService.updateObject).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                    model.persisted = 12321;
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
 | 
			
		||||
                    expect(mockPersistenceService.updateObject).toHaveBeenCalledWith(
 | 
			
		||||
                        SPACE,
 | 
			
		||||
                        key,
 | 
			
		||||
                        model
 | 
			
		||||
                    );
 | 
			
		||||
                    expect(mockOpenMCT.objects.save).toHaveBeenCalledWith(mockNewStyleDomainObject);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("reports which persistence space an object belongs to", function () {
 | 
			
		||||
                    expect(persistence.getSpace()).toEqual(SPACE);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("updates persisted timestamp on persistence", function () {
 | 
			
		||||
                    model.modified = 12321;
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
                    expect(model.persisted).toEqual(12321);
 | 
			
		||||
                });
 | 
			
		||||
                it("refreshes the domain object model from persistence", function () {
 | 
			
		||||
                    var refreshModel = {someOtherKey: "some other value"};
 | 
			
		||||
                    model.persisted = 1;
 | 
			
		||||
@@ -165,30 +152,37 @@ define(
 | 
			
		||||
 | 
			
		||||
                it("does not trigger error notification on successful" +
 | 
			
		||||
                    " persistence", function () {
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
                    expect(mockQ.reject).not.toHaveBeenCalled();
 | 
			
		||||
                    expect(mockNofificationService.error).not.toHaveBeenCalled();
 | 
			
		||||
                    let rejected = false;
 | 
			
		||||
                    return persistence.persist()
 | 
			
		||||
                        .catch(() => rejected = true)
 | 
			
		||||
                        .then(() => {
 | 
			
		||||
                            expect(rejected).toBe(false);
 | 
			
		||||
                            expect(mockNofificationService.error).not.toHaveBeenCalled();
 | 
			
		||||
                        });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("unsuccessful persistence", function () {
 | 
			
		||||
                var sadPromise = {
 | 
			
		||||
                    then: function (callback) {
 | 
			
		||||
                        return asPromise(callback(0), true);
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockPersistenceService.createObject.and.returnValue(sadPromise);
 | 
			
		||||
                    mockOpenMCT.objects.save.and.returnValue(Promise.resolve(false));
 | 
			
		||||
                });
 | 
			
		||||
                it("rejects on falsey persistence result", function () {
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
                    expect(mockQ.reject).toHaveBeenCalled();
 | 
			
		||||
                    let rejected = false;
 | 
			
		||||
                    return persistence.persist()
 | 
			
		||||
                        .catch(() => rejected = true)
 | 
			
		||||
                        .then(() => {
 | 
			
		||||
                            expect(rejected).toBe(true);
 | 
			
		||||
                        });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("notifies user on persistence failure", function () {
 | 
			
		||||
                    persistence.persist();
 | 
			
		||||
                    expect(mockQ.reject).toHaveBeenCalled();
 | 
			
		||||
                    expect(mockNofificationService.error).toHaveBeenCalled();
 | 
			
		||||
                    let rejected = false;
 | 
			
		||||
                    return persistence.persist()
 | 
			
		||||
                        .catch(() => rejected = true)
 | 
			
		||||
                        .then(() => {
 | 
			
		||||
                            expect(rejected).toBe(true);
 | 
			
		||||
                            expect(mockNofificationService.error).toHaveBeenCalled();
 | 
			
		||||
                        });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class="c-clock l-time-display" ng-controller="ClockController as clock">
 | 
			
		||||
<div class="c-clock l-time-display u-style-receiver js-style-receiver" ng-controller="ClockController as clock">
 | 
			
		||||
	<div class="c-clock__timezone">
 | 
			
		||||
		{{clock.zone()}}
 | 
			
		||||
	</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class="c-timer is-{{timer.timerState}}" ng-controller="TimerController as timer">
 | 
			
		||||
<div class="c-timer u-style-receiver js-style-receiver is-{{timer.timerState}}" ng-controller="TimerController as timer">
 | 
			
		||||
    <div class="c-timer__controls">
 | 
			
		||||
        <button ng-click="timer.clickStopButton()"
 | 
			
		||||
                ng-hide="timer.timerState == 'stopped'"
 | 
			
		||||
 
 | 
			
		||||
@@ -29,9 +29,9 @@
 | 
			
		||||
                       type="text" tabindex="10000"
 | 
			
		||||
                       ng-model="ngModel.input"
 | 
			
		||||
                       ng-keyup="controller.search()"/>
 | 
			
		||||
                <a class="c-search__clear-input clear-icon icon-x-in-circle"
 | 
			
		||||
                <button class="c-search__clear-input clear-icon icon-x-in-circle"
 | 
			
		||||
                   ng-class="{show: !(ngModel.input === '' || ngModel.input === undefined)}"
 | 
			
		||||
                   ng-click="ngModel.input = ''; controller.search()"></a>
 | 
			
		||||
                   ng-click="ngModel.input = ''; controller.search()"></button>
 | 
			
		||||
                <!-- To prevent double triggering of clicks on click away, render
 | 
			
		||||
                   non-clickable version of the button when menu active-->
 | 
			
		||||
                <a ng-if="!toggle.isActive()" class="menu-icon context-available"
 | 
			
		||||
@@ -45,16 +45,16 @@
 | 
			
		||||
                </mct-include>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <a class="c-button c-search__btn-cancel"
 | 
			
		||||
            <button class="c-button c-search__btn-cancel"
 | 
			
		||||
               ng-show="!(ngModel.input === '' || ngModel.input === undefined)"
 | 
			
		||||
               ng-click="ngModel.input = ''; ngModel.checkAll = true; menuController.checkAll(); controller.search()">
 | 
			
		||||
                Cancel</a>
 | 
			
		||||
                Cancel</button>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="active-filter-display flex-elem holder"
 | 
			
		||||
             ng-class="{invisible: ngModel.filtersString === '' || ngModel.filtersString === undefined || !ngModel.search}">
 | 
			
		||||
            <a class="clear-filters icon-x-in-circle s-icon-button"
 | 
			
		||||
               ng-click="ngModel.checkAll = true; menuController.checkAll()"></a>Filtered by: {{ ngModel.filtersString }}
 | 
			
		||||
            <button class="clear-filters icon-x-in-circle s-icon-button"
 | 
			
		||||
               ng-click="ngModel.checkAll = true; menuController.checkAll()"></button>Filtered by: {{ ngModel.filtersString }}
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="flex-elem holder results-msg" ng-model="ngModel" ng-show="!loading && ngModel.search">
 | 
			
		||||
@@ -72,7 +72,7 @@
 | 
			
		||||
                                ng-model="ngModel"
 | 
			
		||||
                                class="l-flex-row flex-elem grows">
 | 
			
		||||
            </mct-representation>
 | 
			
		||||
            <a class="load-more-button s-button vsm" ng-if="controller.areMore()" ng-click="controller.loadMore()">More Results</a>
 | 
			
		||||
            <button class="load-more-button s-button vsm" ng-if="controller.areMore()" ng-click="controller.loadMore()">More Results</button>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -25,10 +25,11 @@ define([
 | 
			
		||||
], function (
 | 
			
		||||
    utils
 | 
			
		||||
) {
 | 
			
		||||
    function ObjectServiceProvider(eventEmitter, objectService, instantiate, topic) {
 | 
			
		||||
    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();
 | 
			
		||||
@@ -68,16 +69,53 @@ define([
 | 
			
		||||
        removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ObjectServiceProvider.prototype.save = function (object) {
 | 
			
		||||
        var key = object.key;
 | 
			
		||||
    ObjectServiceProvider.prototype.create = async function (object) {
 | 
			
		||||
        let model = utils.toOldFormat(object);
 | 
			
		||||
 | 
			
		||||
        return object.getCapability('persistence')
 | 
			
		||||
            .persist()
 | 
			
		||||
            .then(function () {
 | 
			
		||||
                return utils.toNewFormat(object, key);
 | 
			
		||||
            });
 | 
			
		||||
        return this.getPersistenceService().createObject(
 | 
			
		||||
            this.getSpace(utils.makeKeyString(object.identifier)),
 | 
			
		||||
            object.identifier.key,
 | 
			
		||||
            model
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    ObjectServiceProvider.prototype.update = async function (object) {
 | 
			
		||||
        let model = utils.toOldFormat(object);
 | 
			
		||||
 | 
			
		||||
        return this.getPersistenceService().updateObject(
 | 
			
		||||
            this.getSpace(utils.makeKeyString(object.identifier)),
 | 
			
		||||
            object.identifier.key,
 | 
			
		||||
            model
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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!
 | 
			
		||||
    };
 | 
			
		||||
@@ -118,7 +156,8 @@ define([
 | 
			
		||||
                eventEmitter,
 | 
			
		||||
                objectService,
 | 
			
		||||
                instantiate,
 | 
			
		||||
                topic
 | 
			
		||||
                topic,
 | 
			
		||||
                openmct.$injector
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -101,14 +101,25 @@ define([
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Save this domain object in its current state.
 | 
			
		||||
     * Create the given domain object in the corresponding persistence store
 | 
			
		||||
     *
 | 
			
		||||
     * @method save
 | 
			
		||||
     * @method create
 | 
			
		||||
     * @memberof module:openmct.ObjectProvider#
 | 
			
		||||
     * @param {module:openmct.DomainObject} domainObject the domain object to
 | 
			
		||||
     *        save
 | 
			
		||||
     *        create
 | 
			
		||||
     * @returns {Promise} a promise which will resolve when the domain object
 | 
			
		||||
     *          has been saved, or be rejected if it cannot be saved
 | 
			
		||||
     *          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
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -161,8 +172,41 @@ define([
 | 
			
		||||
        throw new Error('Delete not implemented');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ObjectAPI.prototype.save = function () {
 | 
			
		||||
        throw new Error('Save not implemented');
 | 
			
		||||
    ObjectAPI.prototype.isPersistable = function (domainObject) {
 | 
			
		||||
        let provider = this.getProvider(domainObject.identifier);
 | 
			
		||||
        return provider !== undefined &&
 | 
			
		||||
            provider.create !== undefined &&
 | 
			
		||||
            provider.update !== undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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 result;
 | 
			
		||||
 | 
			
		||||
        if (!this.isPersistable(domainObject)) {
 | 
			
		||||
            result = Promise.reject('Object provider does not support saving');
 | 
			
		||||
        } else if (hasAlreadyBeenPersisted(domainObject)) {
 | 
			
		||||
            result = Promise.resolve(true);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (domainObject.persisted === undefined) {
 | 
			
		||||
                this.mutate(domainObject, 'persisted', domainObject.modified);
 | 
			
		||||
                result = provider.create(domainObject);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.mutate(domainObject, 'persisted', domainObject.modified);
 | 
			
		||||
                result = provider.update(domainObject);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -276,5 +320,9 @@ define([
 | 
			
		||||
     * @memberof module:openmct
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    function hasAlreadyBeenPersisted(domainObject) {
 | 
			
		||||
        return domainObject.persisted !== undefined &&
 | 
			
		||||
            domainObject.persisted === domainObject.modified;
 | 
			
		||||
    }
 | 
			
		||||
    return ObjectAPI;
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								src/api/objects/ObjectAPISpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/api/objects/ObjectAPISpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
import ObjectAPI from './ObjectAPI.js';
 | 
			
		||||
 | 
			
		||||
describe("The Object API", () => {
 | 
			
		||||
    let objectAPI;
 | 
			
		||||
    let mockDomainObject;
 | 
			
		||||
    const TEST_NAMESPACE = "test-namespace";
 | 
			
		||||
    const FIFTEEN_MINUTES = 15 * 60 * 1000;
 | 
			
		||||
 | 
			
		||||
    beforeEach(() => {
 | 
			
		||||
        objectAPI = new ObjectAPI();
 | 
			
		||||
        mockDomainObject = {
 | 
			
		||||
            identifier: {
 | 
			
		||||
                namespace: TEST_NAMESPACE,
 | 
			
		||||
                key: "test-key"
 | 
			
		||||
            },
 | 
			
		||||
            name: "test object",
 | 
			
		||||
            type: "test-type"
 | 
			
		||||
        };
 | 
			
		||||
    })
 | 
			
		||||
    describe("The save function", () => {
 | 
			
		||||
        it("Rejects if no provider available", () => {
 | 
			
		||||
            let rejected = false;
 | 
			
		||||
            return objectAPI.save(mockDomainObject)
 | 
			
		||||
                .catch(() => rejected = true)
 | 
			
		||||
                .then(() => expect(rejected).toBe(true));
 | 
			
		||||
        });
 | 
			
		||||
        describe("when a provider is available", () => {
 | 
			
		||||
            let mockProvider;
 | 
			
		||||
            beforeEach(() => {
 | 
			
		||||
                mockProvider = jasmine.createSpyObj("mock provider", [
 | 
			
		||||
                    "create",
 | 
			
		||||
                    "update"
 | 
			
		||||
                ]);
 | 
			
		||||
                objectAPI.addProvider(TEST_NAMESPACE, mockProvider);
 | 
			
		||||
            })
 | 
			
		||||
            it("Calls 'create' on provider if object is new", () => {
 | 
			
		||||
                objectAPI.save(mockDomainObject);
 | 
			
		||||
                expect(mockProvider.create).toHaveBeenCalled();
 | 
			
		||||
                expect(mockProvider.update).not.toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
            it("Calls 'update' on provider if object is not new", () => {
 | 
			
		||||
                mockDomainObject.persisted = Date.now() - FIFTEEN_MINUTES;
 | 
			
		||||
                mockDomainObject.modified = Date.now();
 | 
			
		||||
 | 
			
		||||
                objectAPI.save(mockDomainObject);
 | 
			
		||||
                expect(mockProvider.create).not.toHaveBeenCalled();
 | 
			
		||||
                expect(mockProvider.update).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("Does not persist if the object is unchanged", () => {
 | 
			
		||||
                mockDomainObject.persisted =
 | 
			
		||||
                    mockDomainObject.modified = Date.now();
 | 
			
		||||
 | 
			
		||||
                objectAPI.save(mockDomainObject);
 | 
			
		||||
                expect(mockProvider.create).not.toHaveBeenCalled();
 | 
			
		||||
                expect(mockProvider.update).not.toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    })
 | 
			
		||||
});
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
    <div class="c-overlay__outer">
 | 
			
		||||
        <button
 | 
			
		||||
            v-if="dismissable"
 | 
			
		||||
            class="c-click-icon c-overlay__close-button icon-x-in-circle"
 | 
			
		||||
            class="c-click-icon c-overlay__close-button icon-x"
 | 
			
		||||
            @click="destroy"
 | 
			
		||||
        ></button>
 | 
			
		||||
        <div
 | 
			
		||||
 
 | 
			
		||||
@@ -29,13 +29,12 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__close-button {
 | 
			
		||||
        $p: $interiorMargin;
 | 
			
		||||
        border-radius: 100% !important;
 | 
			
		||||
        $p: $interiorMargin + 2px;
 | 
			
		||||
        color: $overlayColorFg;
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        font-size: 1.25em;
 | 
			
		||||
        font-size: 1.5em;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: $p; right: $p;
 | 
			
		||||
        z-index: 99;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__contents {
 | 
			
		||||
@@ -43,7 +42,7 @@
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        outline: none;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__top-bar {
 | 
			
		||||
@@ -87,6 +86,10 @@
 | 
			
		||||
    .c-click-icon {
 | 
			
		||||
        filter: $overlayBrightnessAdjust;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .c-object-label__name {
 | 
			
		||||
        filter: $objectLabelNameFilter;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body.desktop {
 | 
			
		||||
@@ -100,7 +103,6 @@ body.desktop {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Overlay types, styling for desktop. Appended to .l-overlay-wrapper element.
 | 
			
		||||
    .l-overlay-large,
 | 
			
		||||
    .l-overlay-small,
 | 
			
		||||
    .l-overlay-fit {
 | 
			
		||||
        .c-overlay__outer {
 | 
			
		||||
@@ -118,8 +120,28 @@ body.desktop {
 | 
			
		||||
 | 
			
		||||
    .l-overlay-large {
 | 
			
		||||
        // Default
 | 
			
		||||
        .c-overlay__outer {
 | 
			
		||||
            @include overlaySizing($overlayOuterMarginLg);
 | 
			
		||||
        $pad: $interiorMarginLg;
 | 
			
		||||
        $tbPad: floor($pad * 0.8);
 | 
			
		||||
        $lrPad: $pad;
 | 
			
		||||
        .c-overlay {
 | 
			
		||||
            &__blocker {
 | 
			
		||||
                display: none;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &__outer {
 | 
			
		||||
                @include overlaySizing($overlayOuterMarginFullscreen);
 | 
			
		||||
                padding: $tbPad $lrPad;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &__close-button {
 | 
			
		||||
                //top: $interiorMargin;
 | 
			
		||||
                //right: $interiorMargin;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .l-browse-bar {
 | 
			
		||||
            margin-right: 50px; // Don't cover close button
 | 
			
		||||
            margin-bottom: $interiorMargin;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -139,4 +161,4 @@ body.desktop {
 | 
			
		||||
            min-width: 20%;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -334,8 +334,8 @@ define([
 | 
			
		||||
            });
 | 
			
		||||
            if (subscriber.callbacks.length === 0) {
 | 
			
		||||
                subscriber.unsubscribe();
 | 
			
		||||
                delete this.subscribeCache[keyString];
 | 
			
		||||
            }
 | 
			
		||||
            delete this.subscribeCache[keyString];
 | 
			
		||||
        }.bind(this);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -156,6 +156,29 @@ define([
 | 
			
		||||
                expect(callbacktwo).not.toHaveBeenCalledWith('anotherValue');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('only deletes subscription cache when there are no more subscribers', function () {
 | 
			
		||||
                var unsubFunc = jasmine.createSpy('unsubscribe');
 | 
			
		||||
                telemetryProvider.subscribe.and.returnValue(unsubFunc);
 | 
			
		||||
                telemetryProvider.supportsSubscribe.and.returnValue(true);
 | 
			
		||||
                telemetryAPI.addProvider(telemetryProvider);
 | 
			
		||||
 | 
			
		||||
                var callback = jasmine.createSpy('callback');
 | 
			
		||||
                var callbacktwo = jasmine.createSpy('callback two');
 | 
			
		||||
                var callbackThree = jasmine.createSpy('callback three');
 | 
			
		||||
                var unsubscribe = telemetryAPI.subscribe(domainObject, callback);
 | 
			
		||||
                var unsubscribeTwo = telemetryAPI.subscribe(domainObject, callbacktwo);
 | 
			
		||||
 | 
			
		||||
                expect(telemetryProvider.subscribe.calls.count()).toBe(1);
 | 
			
		||||
                unsubscribe();
 | 
			
		||||
                var unsubscribeThree = telemetryAPI.subscribe(domainObject, callbackThree);
 | 
			
		||||
                // Regression test for where subscription cache was deleted on each unsubscribe, resulting in
 | 
			
		||||
                // superfluous additional subscriptions. If the subscription cache is being deleted on each unsubscribe,
 | 
			
		||||
                // then a subsequent subscribe will result in a new subscription at the provider.
 | 
			
		||||
                expect(telemetryProvider.subscribe.calls.count()).toBe(1);
 | 
			
		||||
                unsubscribeTwo();
 | 
			
		||||
                unsubscribeThree();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('does subscribe/unsubscribe', function () {
 | 
			
		||||
                var unsubFunc = jasmine.createSpy('unsubscribe');
 | 
			
		||||
                telemetryProvider.subscribe.and.returnValue(unsubFunc);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div class="c-lad-table-wrapper">
 | 
			
		||||
<div class="c-lad-table-wrapper u-style-receiver js-style-receiver">
 | 
			
		||||
    <table class="c-table c-lad-table">
 | 
			
		||||
        <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
 
 | 
			
		||||
@@ -29,24 +29,28 @@ define([
 | 
			
		||||
    ClearDataAction,
 | 
			
		||||
    Vue
 | 
			
		||||
) {
 | 
			
		||||
    return function plugin(appliesToObjects) {
 | 
			
		||||
    return function plugin(appliesToObjects, options = {indicator: true}) {
 | 
			
		||||
        let installIndicator = options.indicator;
 | 
			
		||||
 | 
			
		||||
        appliesToObjects = appliesToObjects || [];
 | 
			
		||||
 | 
			
		||||
        return function install(openmct) {
 | 
			
		||||
            let component = new Vue ({
 | 
			
		||||
                    provide: {
 | 
			
		||||
                        openmct
 | 
			
		||||
                    },
 | 
			
		||||
                    components: {
 | 
			
		||||
                        GlobalClearIndicator: GlobaClearIndicator.default
 | 
			
		||||
                    },
 | 
			
		||||
                    template: '<GlobalClearIndicator></GlobalClearIndicator>'
 | 
			
		||||
                }),
 | 
			
		||||
                indicator = {
 | 
			
		||||
                    element: component.$mount().$el
 | 
			
		||||
                };
 | 
			
		||||
            if (installIndicator) {
 | 
			
		||||
                let component = new Vue ({
 | 
			
		||||
                        provide: {
 | 
			
		||||
                            openmct
 | 
			
		||||
                        },
 | 
			
		||||
                        components: {
 | 
			
		||||
                            GlobalClearIndicator: GlobaClearIndicator.default
 | 
			
		||||
                        },
 | 
			
		||||
                        template: '<GlobalClearIndicator></GlobalClearIndicator>'
 | 
			
		||||
                    }),
 | 
			
		||||
                    indicator = {
 | 
			
		||||
                        element: component.$mount().$el
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
            openmct.indicators.add(indicator);
 | 
			
		||||
                openmct.indicators.add(indicator);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            openmct.contextMenu.registerAction(new ClearDataAction.default(openmct, appliesToObjects));
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,7 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
        if (this.isEditing) {
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
                delete this.stopProvidingTelemetry;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.conditionSetIdentifier) {
 | 
			
		||||
                this.applySelectedConditionStyle();
 | 
			
		||||
@@ -66,6 +67,7 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
    subscribeToConditionSet() {
 | 
			
		||||
        if (this.stopProvidingTelemetry) {
 | 
			
		||||
            this.stopProvidingTelemetry();
 | 
			
		||||
            delete this.stopProvidingTelemetry;
 | 
			
		||||
        }
 | 
			
		||||
        this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
 | 
			
		||||
            this.openmct.telemetry.request(conditionSetDomainObject)
 | 
			
		||||
@@ -154,8 +156,8 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
        this.applyStaticStyle();
 | 
			
		||||
        if (this.stopProvidingTelemetry) {
 | 
			
		||||
            this.stopProvidingTelemetry();
 | 
			
		||||
            delete this.stopProvidingTelemetry;
 | 
			
		||||
        }
 | 
			
		||||
        delete this.stopProvidingTelemetry;
 | 
			
		||||
        this.conditionSetIdentifier = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -197,6 +197,7 @@ export default {
 | 
			
		||||
            }
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
                delete this.stopProvidingTelemetry;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        initialize(conditionSetDomainObject) {
 | 
			
		||||
@@ -210,6 +211,7 @@ export default {
 | 
			
		||||
            if (this.isEditing) {
 | 
			
		||||
                if (this.stopProvidingTelemetry) {
 | 
			
		||||
                    this.stopProvidingTelemetry();
 | 
			
		||||
                    delete this.stopProvidingTelemetry;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                this.subscribeToConditionSet();
 | 
			
		||||
@@ -307,6 +309,7 @@ export default {
 | 
			
		||||
            this.persist(domainObjectStyles);
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
                delete this.stopProvidingTelemetry;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        updateDomainObjectItemStyles(newItems) {
 | 
			
		||||
@@ -375,6 +378,7 @@ export default {
 | 
			
		||||
        subscribeToConditionSet() {
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
                delete this.stopProvidingTelemetry;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.conditionSetDomainObject) {
 | 
			
		||||
                this.openmct.telemetry.request(this.conditionSetDomainObject)
 | 
			
		||||
 
 | 
			
		||||
@@ -190,6 +190,7 @@ export default {
 | 
			
		||||
            if (this.isEditing) {
 | 
			
		||||
                if (this.stopProvidingTelemetry) {
 | 
			
		||||
                    this.stopProvidingTelemetry();
 | 
			
		||||
                    delete this.stopProvidingTelemetry;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                this.subscribeToConditionSet();
 | 
			
		||||
@@ -325,6 +326,7 @@ export default {
 | 
			
		||||
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
                delete this.stopProvidingTelemetry;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.unObserveObjects) {
 | 
			
		||||
@@ -337,6 +339,7 @@ export default {
 | 
			
		||||
        subscribeToConditionSet() {
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
                delete this.stopProvidingTelemetry;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.conditionSetDomainObject) {
 | 
			
		||||
                this.openmct.telemetry.request(this.conditionSetDomainObject)
 | 
			
		||||
@@ -494,6 +497,7 @@ export default {
 | 
			
		||||
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
                delete this.stopProvidingTelemetry;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        removeConditionalStyles(domainObjectStyles, itemId) {
 | 
			
		||||
 
 | 
			
		||||
@@ -127,7 +127,7 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
 | 
			
		||||
 | 
			
		||||
        if (validatedData) {
 | 
			
		||||
            if (this.isStalenessCheck()) {
 | 
			
		||||
                if (this.stalenessSubscription[validatedData.id]) {
 | 
			
		||||
                if (this.stalenessSubscription && this.stalenessSubscription[validatedData.id]) {
 | 
			
		||||
                    this.stalenessSubscription[validatedData.id].update(validatedData);
 | 
			
		||||
                }
 | 
			
		||||
                this.telemetryDataCache[validatedData.id] = false;
 | 
			
		||||
 
 | 
			
		||||
@@ -142,12 +142,14 @@ export default class TelemetryCriterion extends EventEmitter {
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let telemetryObject = this.telemetryObject;
 | 
			
		||||
 | 
			
		||||
        return this.openmct.telemetry.request(
 | 
			
		||||
            this.telemetryObject,
 | 
			
		||||
            options
 | 
			
		||||
        ).then(results => {
 | 
			
		||||
            const latestDatum = results.length ? results[results.length - 1] : {};
 | 
			
		||||
            const normalizedDatum = this.createNormalizedDatum(latestDatum, this.telemetryObject);
 | 
			
		||||
            const normalizedDatum = this.createNormalizedDatum(latestDatum, telemetryObject);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                id: this.id,
 | 
			
		||||
@@ -201,7 +203,7 @@ export default class TelemetryCriterion extends EventEmitter {
 | 
			
		||||
        let inputValue;
 | 
			
		||||
        if (metadataObject) {
 | 
			
		||||
            if(metadataObject.enumerations && input.length) {
 | 
			
		||||
                const enumeration = metadataObject.enumerations[input[0]];
 | 
			
		||||
                const enumeration = metadataObject.enumerations.find((item) => item.value.toString() === input[0].toString());
 | 
			
		||||
                if (enumeration !== undefined && enumeration.string) {
 | 
			
		||||
                    inputValue = [enumeration.string];
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,12 +21,14 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import TelemetryCriterion from "./TelemetryCriterion";
 | 
			
		||||
import { getMockTelemetry } from "utils/testing";
 | 
			
		||||
 | 
			
		||||
let openmct = {},
 | 
			
		||||
    mockListener,
 | 
			
		||||
    testCriterionDefinition,
 | 
			
		||||
    testTelemetryObject,
 | 
			
		||||
    telemetryCriterion;
 | 
			
		||||
    telemetryCriterion,
 | 
			
		||||
    mockTelemetry = getMockTelemetry();
 | 
			
		||||
 | 
			
		||||
describe("The telemetry criterion", function () {
 | 
			
		||||
 | 
			
		||||
@@ -60,7 +62,7 @@ describe("The telemetry criterion", function () {
 | 
			
		||||
        };
 | 
			
		||||
        openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']);
 | 
			
		||||
        openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key);
 | 
			
		||||
        openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata", "getValueFormatter"]);
 | 
			
		||||
        openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata", "getValueFormatter", "request"]);
 | 
			
		||||
        openmct.telemetry.isTelemetryObject.and.returnValue(true);
 | 
			
		||||
        openmct.telemetry.subscribe.and.returnValue(function () {});
 | 
			
		||||
        openmct.telemetry.getValueFormatter.and.returnValue({
 | 
			
		||||
@@ -109,4 +111,30 @@ describe("The telemetry criterion", function () {
 | 
			
		||||
        });
 | 
			
		||||
        expect(telemetryCriterion.result).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('the LAD request', () => {
 | 
			
		||||
        beforeEach(async () => {
 | 
			
		||||
            let telemetryRequestResolve;
 | 
			
		||||
            let telemetryRequestPromise = new Promise((resolve) => {
 | 
			
		||||
                telemetryRequestResolve = resolve;
 | 
			
		||||
            });
 | 
			
		||||
            openmct.telemetry.request.and.callFake(() => {
 | 
			
		||||
                setTimeout(() => {
 | 
			
		||||
                    telemetryRequestResolve(mockTelemetry);
 | 
			
		||||
                }, 100);
 | 
			
		||||
                return telemetryRequestPromise;
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("returns results for slow LAD requests", async function () {
 | 
			
		||||
            const criteriaRequest = telemetryCriterion.requestLAD();
 | 
			
		||||
            telemetryCriterion.destroy();
 | 
			
		||||
            expect(telemetryCriterion.telemetryObject).toBeUndefined();
 | 
			
		||||
            setTimeout(() => {
 | 
			
		||||
                criteriaRequest.then((result) => {
 | 
			
		||||
                    expect(result).toBeDefined();
 | 
			
		||||
                });
 | 
			
		||||
            }, 300);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<component :is="urlDefined ? 'a' : 'span'"
 | 
			
		||||
           class="c-condition-widget"
 | 
			
		||||
           class="c-condition-widget u-style-receiver js-style-receiver"
 | 
			
		||||
           :href="urlDefined ? internalDomainObject.url : null"
 | 
			
		||||
>
 | 
			
		||||
    <div class="c-condition-widget__label">
 | 
			
		||||
 
 | 
			
		||||
@@ -124,7 +124,117 @@ define(['lodash'], function (_) {
 | 
			
		||||
                        'telemetry.plot.overlay-multi': [
 | 
			
		||||
                            VIEW_TYPES['telemetry.plot.stacked']
 | 
			
		||||
                        ]
 | 
			
		||||
                    };
 | 
			
		||||
                    },
 | 
			
		||||
                    SMALL_FONT_SIZES = [
 | 
			
		||||
                        {
 | 
			
		||||
                            name: 'Default Size',
 | 
			
		||||
                            value: 'default'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '8px',
 | 
			
		||||
                            value: '8'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '9px',
 | 
			
		||||
                            value: '9'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '10px',
 | 
			
		||||
                            value: '10'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '11px',
 | 
			
		||||
                            value: '11'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '12px',
 | 
			
		||||
                            value: '12'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '13px',
 | 
			
		||||
                            value: '13'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '14px',
 | 
			
		||||
                            value: '14'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '16px',
 | 
			
		||||
                            value: '16'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '18px',
 | 
			
		||||
                            value: '18'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '20px',
 | 
			
		||||
                            value: '20'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '24px',
 | 
			
		||||
                            value: '24'
 | 
			
		||||
                        }
 | 
			
		||||
                    ],
 | 
			
		||||
                    LARGE_FONT_SIZES = [
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '28px',
 | 
			
		||||
                            value: '28'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '32px',
 | 
			
		||||
                            value: '32'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '36px',
 | 
			
		||||
                            value: '36'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '42px',
 | 
			
		||||
                            value: '42'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '48px',
 | 
			
		||||
                            value: '48'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '72px',
 | 
			
		||||
                            value: '72'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '96px',
 | 
			
		||||
                            value: '96'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: '128px',
 | 
			
		||||
                            value: '128'
 | 
			
		||||
                        }
 | 
			
		||||
                    ],
 | 
			
		||||
                    FONTS = [
 | 
			
		||||
                        {
 | 
			
		||||
                            name: 'Default',
 | 
			
		||||
                            value: 'default'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: 'Bold',
 | 
			
		||||
                            value: 'default-bold'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: 'Narrow',
 | 
			
		||||
                            value: 'narrow'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: 'Narrow Bold',
 | 
			
		||||
                            value: 'narrow-bold'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: 'Monospace',
 | 
			
		||||
                            value: 'monospace'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: 'Monospace Bold',
 | 
			
		||||
                            value: 'monospace-bold'
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                function getUserInput(form) {
 | 
			
		||||
                    return openmct.$injector.get('dialogService').getUserInput(form, {});
 | 
			
		||||
@@ -378,25 +488,136 @@ define(['lodash'], function (_) {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                function getTextSizeMenu(selectedParent, selection) {
 | 
			
		||||
                    const TEXT_SIZE = [8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96, 128];
 | 
			
		||||
                    return {
 | 
			
		||||
                        control: "select-menu",
 | 
			
		||||
                        domainObject: selectedParent,
 | 
			
		||||
                        applicableSelectedItems: selection.filter(selectionPath => {
 | 
			
		||||
                            let type = selectionPath[0].context.layoutItem.type;
 | 
			
		||||
                            return type === 'text-view' || type === 'telemetry-view';
 | 
			
		||||
                        }),
 | 
			
		||||
                        property: function (selectionPath) {
 | 
			
		||||
                            return getPath(selectionPath) + ".size";
 | 
			
		||||
                        },
 | 
			
		||||
                        title: "Set text size",
 | 
			
		||||
                        options: TEXT_SIZE.map(size => {
 | 
			
		||||
                            return {
 | 
			
		||||
                                value: size + "px"
 | 
			
		||||
                            };
 | 
			
		||||
                        })
 | 
			
		||||
                    };
 | 
			
		||||
                function getAvailableFontSizeOptions(selection) {
 | 
			
		||||
                    let sizeOptions = 'big';
 | 
			
		||||
 | 
			
		||||
                    selection.forEach(selectable => {
 | 
			
		||||
                        if (selectable[0].context.item) {
 | 
			
		||||
                            if (selectable[0].context.item.type.includes('plot') ||
 | 
			
		||||
                            selectable[0].context.item.type.includes('table')) {
 | 
			
		||||
                                sizeOptions = 'small';
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    if (sizeOptions === 'small') {
 | 
			
		||||
                        return SMALL_FONT_SIZES;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return SMALL_FONT_SIZES.concat(LARGE_FONT_SIZES);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                function getFontSizeMenu(selectedParent, selection) {
 | 
			
		||||
 | 
			
		||||
                    if (selection.length === 1) {
 | 
			
		||||
                        let primary = selection[0][0];
 | 
			
		||||
                        let type = primary.context.layoutItem.type;
 | 
			
		||||
 | 
			
		||||
                        if (type === 'subobject-view') {
 | 
			
		||||
                            let objectType = primary.context.item.type;
 | 
			
		||||
 | 
			
		||||
                            if (objectType === 'layout' ||
 | 
			
		||||
                                objectType === 'flexible-layout' ||
 | 
			
		||||
                                objectType === 'tabs') {
 | 
			
		||||
                                return;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        return {
 | 
			
		||||
                            control: 'select-menu',
 | 
			
		||||
                            domainObject: selectedParent,
 | 
			
		||||
                            icon: "icon-font-size",
 | 
			
		||||
                            applicableSelectedItems: selection,
 | 
			
		||||
                            property: (selectionPath) => {
 | 
			
		||||
                                return getPath(selectionPath) + '.fontSize';
 | 
			
		||||
                            },
 | 
			
		||||
                            title: "Set font size",
 | 
			
		||||
                            options: getAvailableFontSizeOptions(selection)
 | 
			
		||||
                        };
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return {
 | 
			
		||||
                            control: 'select-menu',
 | 
			
		||||
                            domainObject: selectedParent,
 | 
			
		||||
                            icon: "icon-font-size",
 | 
			
		||||
                            applicableSelectedItems: selection.filter(selectionPath => {
 | 
			
		||||
                                let type = selectionPath[0].context.layoutItem.type;
 | 
			
		||||
 | 
			
		||||
                                if (type === 'line-view' || type === 'box-view') {
 | 
			
		||||
                                    return false;
 | 
			
		||||
                                } else if (type === 'subobject-view') {
 | 
			
		||||
                                    let objectType = selectionPath[0].context.item.type;
 | 
			
		||||
 | 
			
		||||
                                    if (objectType === 'layout' ||
 | 
			
		||||
                                        objectType === 'flexible-layout' ||
 | 
			
		||||
                                        objectType === 'tabs') {
 | 
			
		||||
                                        return false;
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                                return true;
 | 
			
		||||
                            }),
 | 
			
		||||
                            property: (selectionPath) => {
 | 
			
		||||
                                return getPath(selectionPath) + '.fontSize';
 | 
			
		||||
                            },
 | 
			
		||||
                            title: "Set font size",
 | 
			
		||||
                            options: getAvailableFontSizeOptions(selection)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                function getFontMenu(selectedParent, selection) {
 | 
			
		||||
                    if (selection.length === 1) {
 | 
			
		||||
                        let primary = selection[0][0];
 | 
			
		||||
                        let type = primary.context.layoutItem.type;
 | 
			
		||||
 | 
			
		||||
                        if (type === 'subobject-view') {
 | 
			
		||||
                            let objectType = primary.context.item.type;
 | 
			
		||||
 | 
			
		||||
                            if (objectType === 'layout' ||
 | 
			
		||||
                                objectType === 'flexible-layout' ||
 | 
			
		||||
                                objectType === 'tabs') {
 | 
			
		||||
                                return;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        return {
 | 
			
		||||
                            control: 'select-menu',
 | 
			
		||||
                            domainObject: selectedParent,
 | 
			
		||||
                            icon: "icon-font",
 | 
			
		||||
                            applicableSelectedItems: selection,
 | 
			
		||||
                            property: (selectionPath) => {
 | 
			
		||||
                                return getPath(selectionPath) + '.font';
 | 
			
		||||
                            },
 | 
			
		||||
                            title: "Set font style",
 | 
			
		||||
                            options: FONTS
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return {
 | 
			
		||||
                            control: 'select-menu',
 | 
			
		||||
                            domainObject: selectedParent,
 | 
			
		||||
                            icon: "icon-font",
 | 
			
		||||
                            applicableSelectedItems: selection.filter(selectionPath => {
 | 
			
		||||
                                let type = selectionPath[0].context.layoutItem.type;
 | 
			
		||||
 | 
			
		||||
                                if (type === 'line-view' || type === 'box-view') {
 | 
			
		||||
                                    return false;
 | 
			
		||||
                                } else if (type === 'subobject-view') {
 | 
			
		||||
                                    let objectType = selectionPath[0].context.item.type;
 | 
			
		||||
 | 
			
		||||
                                    if (objectType === 'layout' ||
 | 
			
		||||
                                    objectType === 'flexible-layout' ||
 | 
			
		||||
                                    objectType === 'tabs') {
 | 
			
		||||
                                        return false;
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                                return true;
 | 
			
		||||
                            }),
 | 
			
		||||
                            property: (selectionPath) => {
 | 
			
		||||
                                return getPath(selectionPath) + '.font';
 | 
			
		||||
                            },
 | 
			
		||||
                            title: "Set font style",
 | 
			
		||||
                            options: FONTS
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                function getTextButton(selectedParent, selection) {
 | 
			
		||||
@@ -409,7 +630,7 @@ define(['lodash'], function (_) {
 | 
			
		||||
                        property: function (selectionPath) {
 | 
			
		||||
                            return getPath(selectionPath);
 | 
			
		||||
                        },
 | 
			
		||||
                        icon: "icon-font",
 | 
			
		||||
                        icon: "icon-pencil",
 | 
			
		||||
                        title: "Edit text properties",
 | 
			
		||||
                        dialog: DIALOG_FORM.text
 | 
			
		||||
                    };
 | 
			
		||||
@@ -586,7 +807,8 @@ define(['lodash'], function (_) {
 | 
			
		||||
                    'display-mode': [],
 | 
			
		||||
                    'telemetry-value': [],
 | 
			
		||||
                    'style': [],
 | 
			
		||||
                    'text-style': [],
 | 
			
		||||
                    'font-size': [],
 | 
			
		||||
                    'font-family': [],
 | 
			
		||||
                    'position': [],
 | 
			
		||||
                    'duplicate': [],
 | 
			
		||||
                    'remove': []
 | 
			
		||||
@@ -622,6 +844,16 @@ define(['lodash'], function (_) {
 | 
			
		||||
                        if (toolbar.viewSwitcher.length === 0) {
 | 
			
		||||
                            toolbar.viewSwitcher = [getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)];
 | 
			
		||||
                        }
 | 
			
		||||
                        if (toolbar['font-size'].length === 0) {
 | 
			
		||||
                            toolbar['font-size'] = [
 | 
			
		||||
                                getFontSizeMenu(selectedParent, selectedObjects)
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        if (toolbar['font-family'].length === 0) {
 | 
			
		||||
                            toolbar['font-family'] = [
 | 
			
		||||
                                getFontMenu(selectedParent, selectedObjects)
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (layoutItem.type === 'telemetry-view') {
 | 
			
		||||
                        if (toolbar['display-mode'].length === 0) {
 | 
			
		||||
                            toolbar['display-mode'] = [getDisplayModeMenu(selectedParent, selectedObjects)];
 | 
			
		||||
@@ -629,9 +861,14 @@ define(['lodash'], function (_) {
 | 
			
		||||
                        if (toolbar['telemetry-value'].length === 0) {
 | 
			
		||||
                            toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selectedObjects)];
 | 
			
		||||
                        }
 | 
			
		||||
                        if (toolbar['text-style'].length === 0) {
 | 
			
		||||
                            toolbar['text-style'] = [
 | 
			
		||||
                                getTextSizeMenu(selectedParent, selectedObjects)
 | 
			
		||||
                        if (toolbar['font-size'].length === 0) {
 | 
			
		||||
                            toolbar['font-size'] = [
 | 
			
		||||
                                getFontSizeMenu(selectedParent, selectedObjects)
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        if (toolbar['font-family'].length === 0) {
 | 
			
		||||
                            toolbar['font-family'] = [
 | 
			
		||||
                                getFontMenu(selectedParent, selectedObjects)
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        if (toolbar.position.length === 0) {
 | 
			
		||||
@@ -650,9 +887,14 @@ define(['lodash'], function (_) {
 | 
			
		||||
                            toolbar.viewSwitcher = [getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)];
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (layoutItem.type === 'text-view') {
 | 
			
		||||
                        if (toolbar['text-style'].length === 0) {
 | 
			
		||||
                            toolbar['text-style'] = [
 | 
			
		||||
                                getTextSizeMenu(selectedParent, selectedObjects)
 | 
			
		||||
                        if (toolbar['font-size'].length === 0) {
 | 
			
		||||
                            toolbar['font-size'] = [
 | 
			
		||||
                                getFontSizeMenu(selectedParent, selectedObjects)
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        if (toolbar['font-family'].length === 0) {
 | 
			
		||||
                            toolbar['font-family'] = [
 | 
			
		||||
                                getFontMenu(selectedParent, selectedObjects)
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        if (toolbar.position.length === 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@
 | 
			
		||||
    @endMove="() => $emit('endMove')"
 | 
			
		||||
>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-box-view"
 | 
			
		||||
        class="c-box-view u-style-receiver js-style-receiver"
 | 
			
		||||
        :class="[styleClass]"
 | 
			
		||||
        :style="style"
 | 
			
		||||
    ></div>
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div
 | 
			
		||||
    class="l-layout"
 | 
			
		||||
    class="l-layout u-style-receiver js-style-receiver"
 | 
			
		||||
    :class="{
 | 
			
		||||
        'is-multi-selected': selectedLayoutItems.length > 1,
 | 
			
		||||
        'allow-editing': isEditing
 | 
			
		||||
@@ -612,6 +612,13 @@ export default {
 | 
			
		||||
                object.composition.push(...composition);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (object.modified || object.persisted) {
 | 
			
		||||
                object.modified = undefined;
 | 
			
		||||
                object.persisted = undefined;
 | 
			
		||||
                delete object.modified;
 | 
			
		||||
                delete object.persisted;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            object.name = objectName;
 | 
			
		||||
            object.identifier = identifier;
 | 
			
		||||
            object.location = parentKeyString;
 | 
			
		||||
 
 | 
			
		||||
@@ -81,6 +81,7 @@ export default {
 | 
			
		||||
        style() {
 | 
			
		||||
            let backgroundImage = 'url(' + this.item.url + ')';
 | 
			
		||||
            let border = '1px solid ' + this.item.stroke;
 | 
			
		||||
 | 
			
		||||
            if (this.itemStyle) {
 | 
			
		||||
                if (this.itemStyle.imageUrl !== undefined) {
 | 
			
		||||
                    backgroundImage = 'url(' + this.itemStyle.imageUrl + ')';
 | 
			
		||||
 
 | 
			
		||||
@@ -25,14 +25,14 @@
 | 
			
		||||
    class="l-layout__frame c-frame"
 | 
			
		||||
    :class="{
 | 
			
		||||
        'no-frame': !item.hasFrame,
 | 
			
		||||
        'u-inspectable': inspectable
 | 
			
		||||
        'u-inspectable': inspectable,
 | 
			
		||||
        'is-in-small-container': size.width < 600 || size.height < 600
 | 
			
		||||
    }"
 | 
			
		||||
    :style="style"
 | 
			
		||||
>
 | 
			
		||||
    <slot></slot>
 | 
			
		||||
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-frame-edit__move"
 | 
			
		||||
        class="c-frame__move-bar"
 | 
			
		||||
        @mousedown="isEditing ? startMove([1,1], [0,0], $event) : null"
 | 
			
		||||
    ></div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -61,6 +61,13 @@ export default {
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        size() {
 | 
			
		||||
            let {width, height} = this.item;
 | 
			
		||||
            return {
 | 
			
		||||
                width: (this.gridSize[0] * width),
 | 
			
		||||
                height: (this.gridSize[1] * height)
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        style() {
 | 
			
		||||
            let {x, y, width, height} = this.item;
 | 
			
		||||
            return {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div
 | 
			
		||||
    class="l-layout__frame c-frame no-frame"
 | 
			
		||||
    class="l-layout__frame c-frame no-frame c-line-view"
 | 
			
		||||
    :class="[styleClass]"
 | 
			
		||||
    :style="style"
 | 
			
		||||
>
 | 
			
		||||
@@ -31,14 +31,20 @@
 | 
			
		||||
        height="100%"
 | 
			
		||||
    >
 | 
			
		||||
        <line
 | 
			
		||||
            class="c-line-view__hover-indicator"
 | 
			
		||||
            v-bind="linePosition"
 | 
			
		||||
            vector-effect="non-scaling-stroke"
 | 
			
		||||
        />
 | 
			
		||||
        <line
 | 
			
		||||
            class="c-line-view__line"
 | 
			
		||||
            v-bind="linePosition"
 | 
			
		||||
            :stroke="stroke"
 | 
			
		||||
            stroke-width="2"
 | 
			
		||||
            vector-effect="non-scaling-stroke"
 | 
			
		||||
        />
 | 
			
		||||
    </svg>
 | 
			
		||||
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-frame-edit__move"
 | 
			
		||||
        class="c-frame__move-bar"
 | 
			
		||||
        @mousedown="startDrag($event)"
 | 
			
		||||
    ></div>
 | 
			
		||||
    <div
 | 
			
		||||
@@ -49,7 +55,8 @@
 | 
			
		||||
            class="c-frame-edit__handle"
 | 
			
		||||
            :class="startHandleClass"
 | 
			
		||||
            @mousedown="startDrag($event, 'start')"
 | 
			
		||||
        ></div>
 | 
			
		||||
        >
 | 
			
		||||
        </div>
 | 
			
		||||
        <div
 | 
			
		||||
            class="c-frame-edit__handle"
 | 
			
		||||
            :class="endHandleClass"
 | 
			
		||||
@@ -68,14 +75,18 @@ const START_HANDLE_QUADRANTS = {
 | 
			
		||||
    1: 'c-frame-edit__handle--sw',
 | 
			
		||||
    2: 'c-frame-edit__handle--se',
 | 
			
		||||
    3: 'c-frame-edit__handle--ne',
 | 
			
		||||
    4: 'c-frame-edit__handle--nw'
 | 
			
		||||
    4: 'c-frame-edit__handle--nw',
 | 
			
		||||
    5: 'c-frame-edit__handle--nw',
 | 
			
		||||
    6: 'c-frame-edit__handle--ne'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const END_HANDLE_QUADRANTS = {
 | 
			
		||||
    1: 'c-frame-edit__handle--ne',
 | 
			
		||||
    2: 'c-frame-edit__handle--nw',
 | 
			
		||||
    3: 'c-frame-edit__handle--sw',
 | 
			
		||||
    4: 'c-frame-edit__handle--se'
 | 
			
		||||
    4: 'c-frame-edit__handle--se',
 | 
			
		||||
    5: 'c-frame-edit__handle--sw',
 | 
			
		||||
    6: 'c-frame-edit__handle--nw'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
@@ -158,6 +169,12 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        vectorQuadrant() {
 | 
			
		||||
            let {x, y, x2, y2} = this.position;
 | 
			
		||||
            if (x2 === x) {
 | 
			
		||||
                return 5; // Vertical line
 | 
			
		||||
            }
 | 
			
		||||
            if (y2 === y) {
 | 
			
		||||
                return 6; // Horizontal line
 | 
			
		||||
            }
 | 
			
		||||
            if (x2 > x) {
 | 
			
		||||
                if (y2 < y) {
 | 
			
		||||
                    return 1;
 | 
			
		||||
@@ -170,21 +187,27 @@ export default {
 | 
			
		||||
            return 3;
 | 
			
		||||
        },
 | 
			
		||||
        linePosition() {
 | 
			
		||||
            return this.vectorQuadrant % 2 !== 0
 | 
			
		||||
                // odd vectorQuadrant slopes up
 | 
			
		||||
                ? {
 | 
			
		||||
                    x1: '0%',
 | 
			
		||||
                    y1: '100%',
 | 
			
		||||
                    x2: '100%',
 | 
			
		||||
                    y2: '0%'
 | 
			
		||||
                }
 | 
			
		||||
                // even vectorQuadrant slopes down
 | 
			
		||||
                : {
 | 
			
		||||
                    x1: '0%',
 | 
			
		||||
                    y1: '0%',
 | 
			
		||||
                    x2: '100%',
 | 
			
		||||
                    y2: '100%'
 | 
			
		||||
                };
 | 
			
		||||
            let pos = {};
 | 
			
		||||
            switch(this.vectorQuadrant) {
 | 
			
		||||
            case 1:
 | 
			
		||||
            case 3:
 | 
			
		||||
                // slopes up
 | 
			
		||||
                pos = {x1: '0%', y1: '100%', x2: '100%', y2: '0%'};
 | 
			
		||||
                break;
 | 
			
		||||
            case 5:
 | 
			
		||||
                // vertical
 | 
			
		||||
                pos = {x1: '0%', y1: '0%', x2: '0%', y2: '100%'};
 | 
			
		||||
                break;
 | 
			
		||||
            case 6:
 | 
			
		||||
                // horizontal
 | 
			
		||||
                pos = {x1: '0%', y1: '0%', x2: '100%', y2: '0%'};
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                // slopes down
 | 
			
		||||
                pos = {x1: '0%', y1: '0%', x2: '100%', y2: '100%'};
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            return pos;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
@@ -209,8 +232,7 @@ export default {
 | 
			
		||||
            layoutItem: this.item,
 | 
			
		||||
            index: this.index
 | 
			
		||||
        };
 | 
			
		||||
        this.removeSelectable = this.openmct.selection.selectable(
 | 
			
		||||
            this.$el, this.context, this.initSelect);
 | 
			
		||||
        this.removeSelectable = this.openmct.selection.selectable(this.$el, this.context, this.initSelect);
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
        if (this.removeSelectable) {
 | 
			
		||||
@@ -224,12 +246,17 @@ export default {
 | 
			
		||||
            document.body.addEventListener('mousemove', this.continueDrag);
 | 
			
		||||
            document.body.addEventListener('mouseup', this.endDrag);
 | 
			
		||||
            this.startPosition = [event.pageX, event.pageY];
 | 
			
		||||
            this.dragPosition = {
 | 
			
		||||
                x: this.item.x,
 | 
			
		||||
                y: this.item.y,
 | 
			
		||||
                x2: this.item.x2,
 | 
			
		||||
                y2: this.item.y2
 | 
			
		||||
            };
 | 
			
		||||
            let {x, y, x2, y2} = this.item;
 | 
			
		||||
            this.dragPosition = {x, y, x2, y2};
 | 
			
		||||
            if (x === x2 || y === y2) {
 | 
			
		||||
                if (y > y2 || x < x2) {
 | 
			
		||||
                    if (this.dragging === 'start') {
 | 
			
		||||
                        this.dragging = 'end';
 | 
			
		||||
                    } else if (this.dragging === 'end') {
 | 
			
		||||
                        this.dragging = 'start';
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            event.preventDefault();
 | 
			
		||||
        },
 | 
			
		||||
        continueDrag(event) {
 | 
			
		||||
@@ -263,7 +290,7 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        calculateDragPosition(pxDeltaX, pxDeltaY) {
 | 
			
		||||
            let gridDeltaX = Math.round(pxDeltaX / this.gridSize[0]);
 | 
			
		||||
            let gridDeltaY = Math.round(pxDeltaY / this.gridSize[0]); // TODO: should this be gridSize[1]?
 | 
			
		||||
            let gridDeltaY = Math.round(pxDeltaY / this.gridSize[1]);
 | 
			
		||||
            let {x, y, x2, y2} = this.item;
 | 
			
		||||
            let dragPosition = {x, y, x2, y2};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,8 @@
 | 
			
		||||
        :object-path="currentObjectPath"
 | 
			
		||||
        :has-frame="item.hasFrame"
 | 
			
		||||
        :show-edit-view="false"
 | 
			
		||||
        :font-size="item.fontSize"
 | 
			
		||||
        :font="item.font"
 | 
			
		||||
    />
 | 
			
		||||
</layout-frame>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -73,6 +75,8 @@ export default {
 | 
			
		||||
            y: position[1],
 | 
			
		||||
            identifier: domainObject.identifier,
 | 
			
		||||
            hasFrame: hasFrameByDefault(domainObject.type),
 | 
			
		||||
            fontSize: 'default',
 | 
			
		||||
            font: 'default',
 | 
			
		||||
            viewKey
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
@@ -120,7 +124,6 @@ export default {
 | 
			
		||||
            if (!this.context) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.context.layoutItem = newItem;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -30,11 +30,19 @@
 | 
			
		||||
>
 | 
			
		||||
    <div
 | 
			
		||||
        v-if="domainObject"
 | 
			
		||||
        class="c-telemetry-view"
 | 
			
		||||
        :class="styleClass"
 | 
			
		||||
        class="u-style-receiver c-telemetry-view"
 | 
			
		||||
        :class="{
 | 
			
		||||
            styleClass,
 | 
			
		||||
            'is-missing': domainObject.status === 'missing'
 | 
			
		||||
        }"
 | 
			
		||||
        :style="styleObject"
 | 
			
		||||
        :data-font-size="item.fontSize"
 | 
			
		||||
        :data-font="item.font"
 | 
			
		||||
        @contextmenu.prevent="showContextMenu"
 | 
			
		||||
    >
 | 
			
		||||
        <div class="is-missing__indicator"
 | 
			
		||||
             title="This item is missing"
 | 
			
		||||
        ></div>
 | 
			
		||||
        <div
 | 
			
		||||
            v-if="showLabel"
 | 
			
		||||
            class="c-telemetry-view__label"
 | 
			
		||||
@@ -83,7 +91,8 @@ export default {
 | 
			
		||||
            stroke: "",
 | 
			
		||||
            fill: "",
 | 
			
		||||
            color: "",
 | 
			
		||||
            size: "13px"
 | 
			
		||||
            fontSize: 'default',
 | 
			
		||||
            font: 'default'
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    inject: ['openmct', 'objectPath'],
 | 
			
		||||
@@ -130,10 +139,15 @@ export default {
 | 
			
		||||
            return displayMode === 'all' || displayMode === 'value';
 | 
			
		||||
        },
 | 
			
		||||
        styleObject() {
 | 
			
		||||
            return Object.assign({}, {
 | 
			
		||||
                fontSize: this.item.size
 | 
			
		||||
            }, this.itemStyle);
 | 
			
		||||
            let size;
 | 
			
		||||
            //for legacy size support
 | 
			
		||||
            if (!this.item.fontSize) {
 | 
			
		||||
                size = this.item.size
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Object.assign({}, {
 | 
			
		||||
                size
 | 
			
		||||
            }, this.itemStyle);
 | 
			
		||||
        },
 | 
			
		||||
        fieldName() {
 | 
			
		||||
            return this.valueMetadata && this.valueMetadata.name;
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,9 @@
 | 
			
		||||
    @endMove="() => $emit('endMove')"
 | 
			
		||||
>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-text-view"
 | 
			
		||||
        class="c-text-view u-style-receiver js-style-receiver"
 | 
			
		||||
        :data-font-size="item.fontSize"
 | 
			
		||||
        :data-font="item.font"
 | 
			
		||||
        :class="[styleClass]"
 | 
			
		||||
        :style="style"
 | 
			
		||||
    >
 | 
			
		||||
@@ -47,13 +49,14 @@ export default {
 | 
			
		||||
        return {
 | 
			
		||||
            fill: '',
 | 
			
		||||
            stroke: '',
 | 
			
		||||
            size: '13px',
 | 
			
		||||
            color: '',
 | 
			
		||||
            x: 1,
 | 
			
		||||
            y: 1,
 | 
			
		||||
            width: 10,
 | 
			
		||||
            height: 5,
 | 
			
		||||
            text: element.text
 | 
			
		||||
            text: element.text,
 | 
			
		||||
            fontSize: 'default',
 | 
			
		||||
            font: 'default'
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
@@ -84,8 +87,14 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        style() {
 | 
			
		||||
            let size;
 | 
			
		||||
            //legacy size support
 | 
			
		||||
            if (!this.item.fontSize) {
 | 
			
		||||
                size = this.item.size;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Object.assign({
 | 
			
		||||
                fontSize: this.item.size
 | 
			
		||||
                size
 | 
			
		||||
            }, this.itemStyle);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								src/plugins/displayLayout/components/box-and-line-views.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/plugins/displayLayout/components/box-and-line-views.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
.c-box-view {
 | 
			
		||||
    border-width: $drawingObjBorderW !important;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: stretch;
 | 
			
		||||
 | 
			
		||||
    .c-frame & {
 | 
			
		||||
        @include abs();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-line-view {
 | 
			
		||||
    &.c-frame {
 | 
			
		||||
        box-shadow: none !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .c-frame-edit {
 | 
			
		||||
        border: none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .c-handle-info {
 | 
			
		||||
        background: rgba(#999, 0.2);
 | 
			
		||||
        padding: 2px;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 5px; left: 5px;
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    svg {
 | 
			
		||||
        // Prevent clipping when line is horizontal and vertical
 | 
			
		||||
        min-height: 1px;
 | 
			
		||||
        min-width: 1px;
 | 
			
		||||
        // Must use !important to counteract setting in normalize.min.css
 | 
			
		||||
        overflow: visible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__line {
 | 
			
		||||
        stroke-linecap: round;
 | 
			
		||||
        stroke-width: $drawingObjBorderW;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__hover-indicator {
 | 
			
		||||
        display: none;
 | 
			
		||||
        opacity: 0.5;
 | 
			
		||||
        stroke: $editFrameColorHov;
 | 
			
		||||
        stroke-width: $drawingObjBorderW + 4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .is-editing & {
 | 
			
		||||
        // Needed to allow line to be moved
 | 
			
		||||
        $w: 4px;
 | 
			
		||||
        min-width: $w;
 | 
			
		||||
        min-height: $w;
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
            [class*='__hover-indicator'] {
 | 
			
		||||
                display: inline;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
.c-box-view {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: stretch;
 | 
			
		||||
 | 
			
		||||
    .c-frame & {
 | 
			
		||||
        @include abs();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -7,9 +7,13 @@
 | 
			
		||||
    > *:first-child {
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-in-small-container {
 | 
			
		||||
        //background: rgba(blue, 0.1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-frame-edit__move {
 | 
			
		||||
.c-frame__move-bar {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -29,7 +33,7 @@
 | 
			
		||||
            border: $editFrameSelectedBorder;
 | 
			
		||||
            box-shadow: $editFrameSelectedShdw;
 | 
			
		||||
 | 
			
		||||
            .c-frame-edit__move {
 | 
			
		||||
            .c-frame__move-bar {
 | 
			
		||||
                cursor: move;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -37,7 +41,7 @@
 | 
			
		||||
 | 
			
		||||
    /******************* DEFAULT STYLES FOR -EDIT__MOVE */
 | 
			
		||||
    // All object types
 | 
			
		||||
    .c-frame-edit__move {
 | 
			
		||||
    .c-frame__move-bar {
 | 
			
		||||
        @include abs();
 | 
			
		||||
        display: block;
 | 
			
		||||
    }
 | 
			
		||||
@@ -52,7 +56,7 @@
 | 
			
		||||
            transition-delay: $moveBarOutDelay;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        + .c-frame-edit__move {
 | 
			
		||||
        + .c-frame__move-bar {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -61,14 +65,14 @@
 | 
			
		||||
    .l-layout {
 | 
			
		||||
        /******************* 0 - 1 ITEM SELECTED */
 | 
			
		||||
        &:not(.is-multi-selected) {
 | 
			
		||||
            > .l-layout__frame[s-selected] {
 | 
			
		||||
            > .l-layout__frame {
 | 
			
		||||
                > .c-so-view.has-complex-content {
 | 
			
		||||
                    > .c-so-view__local-controls {
 | 
			
		||||
                        transition: transform $transOutTime ease-in-out;
 | 
			
		||||
                        transition-delay: $moveBarOutDelay;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    + .c-frame-edit__move {
 | 
			
		||||
                    + .c-frame__move-bar {
 | 
			
		||||
                        transition: $transOut;
 | 
			
		||||
                        transition-delay: $moveBarOutDelay;
 | 
			
		||||
                        @include userSelectNone();
 | 
			
		||||
@@ -89,7 +93,7 @@
 | 
			
		||||
                            $lrOffset: 25%;
 | 
			
		||||
                            @include grippy($editFrameMovebarColorFg);
 | 
			
		||||
                            content: '';
 | 
			
		||||
                            display: block;
 | 
			
		||||
                            display: none;
 | 
			
		||||
                            position: absolute;
 | 
			
		||||
                            top: $tbOffset;
 | 
			
		||||
                            right: $lrOffset;
 | 
			
		||||
@@ -111,7 +115,7 @@
 | 
			
		||||
                            transition-delay: 0s;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        + .c-frame-edit__move {
 | 
			
		||||
                        + .c-frame__move-bar {
 | 
			
		||||
                            transition: $transIn;
 | 
			
		||||
                            transition-delay: 0s;
 | 
			
		||||
                            height: $editFrameMovebarH;
 | 
			
		||||
@@ -119,12 +123,19 @@
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            > .l-layout__frame[s-selected] {
 | 
			
		||||
                > .c-so-view.has-complex-content {
 | 
			
		||||
                    + .c-frame__move-bar:before {
 | 
			
		||||
                        display: block;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /******************* > 1 ITEMS SELECTED */
 | 
			
		||||
        &.is-multi-selected {
 | 
			
		||||
            .l-layout__frame[s-selected] {
 | 
			
		||||
                > .c-so-view.has-complex-content + .c-frame-edit__move {
 | 
			
		||||
                > .c-so-view.has-complex-content + .c-frame__move-bar {
 | 
			
		||||
                    display: block;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,4 +26,15 @@
 | 
			
		||||
        @include abs();
 | 
			
		||||
        border: 1px solid transparent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @include isMissing($absPos: true);
 | 
			
		||||
 | 
			
		||||
    .is-missing__indicator {
 | 
			
		||||
        top: 0;
 | 
			
		||||
        left: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-missing {
 | 
			
		||||
        border: $borderMissing;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,18 @@
 | 
			
		||||
<template>
 | 
			
		||||
<a
 | 
			
		||||
    class="l-grid-view__item c-grid-item"
 | 
			
		||||
    :class="{ 'is-alias': item.isAlias === true }"
 | 
			
		||||
    :class="{
 | 
			
		||||
        'is-alias': item.isAlias === true,
 | 
			
		||||
        'is-missing': item.model.status === 'missing',
 | 
			
		||||
        'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1
 | 
			
		||||
    }"
 | 
			
		||||
    :href="objectLink"
 | 
			
		||||
>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-grid-item__type-icon"
 | 
			
		||||
        :class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'"
 | 
			
		||||
    ></div>
 | 
			
		||||
    >
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="c-grid-item__details">
 | 
			
		||||
        <!-- Name and metadata -->
 | 
			
		||||
        <div
 | 
			
		||||
@@ -22,6 +27,9 @@
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="c-grid-item__controls">
 | 
			
		||||
        <div class="is-missing__indicator"
 | 
			
		||||
             title="This item is missing"
 | 
			
		||||
        ></div>
 | 
			
		||||
        <div
 | 
			
		||||
            class="icon-people"
 | 
			
		||||
            title="Shared"
 | 
			
		||||
 
 | 
			
		||||
@@ -7,13 +7,19 @@
 | 
			
		||||
    <td class="c-list-item__name">
 | 
			
		||||
        <a
 | 
			
		||||
            ref="objectLink"
 | 
			
		||||
            class="c-object-label"
 | 
			
		||||
            :class="{ 'is-missing': item.model.status === 'missing' }"
 | 
			
		||||
            :href="objectLink"
 | 
			
		||||
        >
 | 
			
		||||
            <div
 | 
			
		||||
                class="c-list-item__type-icon"
 | 
			
		||||
                class="c-object-label__type-icon c-list-item__type-icon"
 | 
			
		||||
                :class="item.type.cssClass"
 | 
			
		||||
            ></div>
 | 
			
		||||
            <div class="c-list-item__name-value">{{ item.model.name }}</div>
 | 
			
		||||
            >
 | 
			
		||||
                <span class="is-missing__indicator"
 | 
			
		||||
                      title="This item is missing"
 | 
			
		||||
                ></span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="c-object-label__name c-list-item__name">{{ item.model.name }}</div>
 | 
			
		||||
        </a>
 | 
			
		||||
    </td>
 | 
			
		||||
    <td class="c-list-item__type">
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,15 @@
 | 
			
		||||
        // Object is an alias to an original.
 | 
			
		||||
        [class*='__type-icon'] {
 | 
			
		||||
            @include isAlias();
 | 
			
		||||
            color: $colorIconAliasForKeyFilter;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-missing {
 | 
			
		||||
        @include isMissing();
 | 
			
		||||
 | 
			
		||||
        [class*='__type-icon'],
 | 
			
		||||
        [class*='__details'] {
 | 
			
		||||
            opacity: $opacityMissing;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -85,15 +93,14 @@
 | 
			
		||||
    body.desktop & {
 | 
			
		||||
        $transOutMs: 300ms;
 | 
			
		||||
        flex-flow: column nowrap;
 | 
			
		||||
        transition: background $transOutMs ease-in-out;
 | 
			
		||||
        transition: $transOutMs ease-in-out;
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
            background: $colorItemBgHov;
 | 
			
		||||
            filter: $filterItemHoverFg;
 | 
			
		||||
            transition: $transIn;
 | 
			
		||||
 | 
			
		||||
            .c-grid-item__type-icon {
 | 
			
		||||
                filter: $colorKeyFilterHov;
 | 
			
		||||
                transform: scale(1);
 | 
			
		||||
                transform: scale(1.1);
 | 
			
		||||
                transition: $transInBounce;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -103,7 +110,7 @@
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &__controls {
 | 
			
		||||
            align-items: start;
 | 
			
		||||
            align-items: baseline;
 | 
			
		||||
            flex: 0 0 auto;
 | 
			
		||||
            order: 1;
 | 
			
		||||
            .c-info-button,
 | 
			
		||||
@@ -115,7 +122,6 @@
 | 
			
		||||
            font-size: floor($gridItemDesk / 3);
 | 
			
		||||
            margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
 | 
			
		||||
            order: 2;
 | 
			
		||||
            transform: scale(0.9);
 | 
			
		||||
            transform-origin: center;
 | 
			
		||||
            transition: all $transOutMs ease-in-out;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +1,17 @@
 | 
			
		||||
/******************************* LIST ITEM */
 | 
			
		||||
.c-list-item {
 | 
			
		||||
    &__name a {
 | 
			
		||||
        display: flex;
 | 
			
		||||
 | 
			
		||||
        > * + * { margin-left: $interiorMarginSm; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__type-icon {
 | 
			
		||||
        // Have to do it this way instead of using icon-* class, due to need to apply alias to the icon
 | 
			
		||||
        color: $colorKey;
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        width: 1em;
 | 
			
		||||
        margin-right:$interiorMarginSm;
 | 
			
		||||
        color: $colorItemTreeIcon;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__name-value {
 | 
			
		||||
    &__name {
 | 
			
		||||
        @include ellipsize();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-alias {
 | 
			
		||||
        // Object is an alias to an original.
 | 
			
		||||
        [class*='__type-icon'] {
 | 
			
		||||
            &:after {
 | 
			
		||||
                color: $colorIconAlias;
 | 
			
		||||
                content: $glyph-icon-link;
 | 
			
		||||
                font-family: symbolsfont;
 | 
			
		||||
                display: block;
 | 
			
		||||
                position: absolute;
 | 
			
		||||
                text-shadow: rgba(black, 0.5) 0 1px 2px;
 | 
			
		||||
                top: auto; left: -1px; bottom: 1px; right: auto;
 | 
			
		||||
                transform-origin: bottom left;
 | 
			
		||||
                transform: scale(0.65);
 | 
			
		||||
            }
 | 
			
		||||
            @include isAlias();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,8 @@
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
 | 
			
		||||
            &:hover {
 | 
			
		||||
                background: $colorListItemBgHov;
 | 
			
		||||
                background: $colorItemTreeHoverBg;
 | 
			
		||||
                filter: $filterHov;
 | 
			
		||||
                transition: $transIn;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -29,13 +29,13 @@
 | 
			
		||||
        >
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="c-imagery__control-bar">
 | 
			
		||||
            <div class="c-imagery__timestamp">{{ getTime() }}</div>
 | 
			
		||||
            <div class="c-imagery__timestamp u-style-receiver js-style-receiver">{{ getTime() }}</div>
 | 
			
		||||
            <div class="h-local-controls flex-elem">
 | 
			
		||||
                <a
 | 
			
		||||
                <button
 | 
			
		||||
                    class="c-button icon-pause pause-play"
 | 
			
		||||
                    :class="{'is-paused': paused()}"
 | 
			
		||||
                    @click="paused(!paused())"
 | 
			
		||||
                ></a>
 | 
			
		||||
                ></button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
@@ -228,8 +228,8 @@ export default {
 | 
			
		||||
        subscribe() {
 | 
			
		||||
            this.unsubscribe = this.openmct.telemetry
 | 
			
		||||
                .subscribe(this.domainObject, (datum) => {
 | 
			
		||||
                    let parsedTimestamp = this.timeFormat.parse(datum[this.timeKey]),
 | 
			
		||||
                        bounds = this.openmct.time.bounds();
 | 
			
		||||
                    let parsedTimestamp = this.timeFormat.parse(datum);
 | 
			
		||||
                    let bounds = this.openmct.time.bounds();
 | 
			
		||||
 | 
			
		||||
                    if(parsedTimestamp >= bounds.start && parsedTimestamp <= bounds.end) {
 | 
			
		||||
                        this.updateHistory(datum);
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,7 @@
 | 
			
		||||
        height: 135px;
 | 
			
		||||
        overflow-x: auto;
 | 
			
		||||
        overflow-y: hidden;
 | 
			
		||||
        padding-bottom: $interiorMarginSm;
 | 
			
		||||
 | 
			
		||||
        &.is-paused {
 | 
			
		||||
            background: rgba($colorPausedBg, 0.4);
 | 
			
		||||
@@ -99,7 +100,7 @@
 | 
			
		||||
.c-imagery {
 | 
			
		||||
    .h-local-controls--overlay-content {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        right: $interiorMargin; top: $interiorMargin;
 | 
			
		||||
        left: $interiorMargin; top: $interiorMargin;
 | 
			
		||||
        z-index: 2;
 | 
			
		||||
        background: $colorLocalControlOvrBg;
 | 
			
		||||
        border-radius: $basicCr;
 | 
			
		||||
 
 | 
			
		||||
@@ -248,7 +248,8 @@ export default {
 | 
			
		||||
        previewEmbed() {
 | 
			
		||||
            const self = this;
 | 
			
		||||
            const previewAction = new PreviewAction(self.openmct);
 | 
			
		||||
            previewAction.invoke(JSON.parse(self.embed.objectPath));
 | 
			
		||||
            this.openmct.objects.get(self.embed.domainObject.identifier)
 | 
			
		||||
                .then(domainObject => previewAction.invoke([domainObject]));
 | 
			
		||||
        },
 | 
			
		||||
        removeEmbed(success) {
 | 
			
		||||
            if (!success) {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,16 @@
 | 
			
		||||
<div class="c-snapshots-h">
 | 
			
		||||
    <div class="l-browse-bar">
 | 
			
		||||
        <div class="l-browse-bar__start">
 | 
			
		||||
            <div class="l-browse-bar__object-name--w icon-notebook">
 | 
			
		||||
                <div class="l-browse-bar__object-name">
 | 
			
		||||
                    Notebook Snapshots
 | 
			
		||||
                    <span v-if="snapshots.length"
 | 
			
		||||
                          class="l-browse-bar__object-details"
 | 
			
		||||
                    > {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
 | 
			
		||||
                    </span>
 | 
			
		||||
            <div class="l-browse-bar__object-name--w">
 | 
			
		||||
                <div class="l-browse-bar__object-name c-object-label">
 | 
			
		||||
                    <div class="c-object-label__type-icon icon-notebook"></div>
 | 
			
		||||
                    <div class="c-object-label__name">
 | 
			
		||||
                        Notebook Snapshots
 | 
			
		||||
                        <span v-if="snapshots.length"
 | 
			
		||||
                              class="l-browse-bar__object-details"
 | 
			
		||||
                        > {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
 | 
			
		||||
                        </span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <PopupMenu v-if="snapshots.length > 0"
 | 
			
		||||
                           :popup-menu-items="popupMenuItems"
 | 
			
		||||
 
 | 
			
		||||
@@ -95,8 +95,7 @@ export const createNewEmbed = (snapshotMeta, snapshot = '') => {
 | 
			
		||||
        id: 'embed-' + date,
 | 
			
		||||
        name,
 | 
			
		||||
        snapshot,
 | 
			
		||||
        type,
 | 
			
		||||
        objectPath: JSON.stringify(objectPath)
 | 
			
		||||
        type
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div
 | 
			
		||||
    v-if="notifications.length > 0"
 | 
			
		||||
    v-show="notifications.length > 0"
 | 
			
		||||
    class="c-indicator c-indicator--clickable icon-bell"
 | 
			
		||||
    :class="[severityClass]"
 | 
			
		||||
>
 | 
			
		||||
 
 | 
			
		||||
@@ -21,24 +21,34 @@
 | 
			
		||||
-->
 | 
			
		||||
<div class="gl-plot plot-legend-{{legend.get('position')}} {{legend.get('expanded')? 'plot-legend-expanded' : 'plot-legend-collapsed'}}">
 | 
			
		||||
    <div class="c-plot-legend gl-plot-legend"
 | 
			
		||||
         ng-class="{ 'hover-on-plot': !!highlights.length }"
 | 
			
		||||
         ng-show="legend.get('position') !== 'hidden'">
 | 
			
		||||
         ng-class="{
 | 
			
		||||
            'hover-on-plot': !!highlights.length,
 | 
			
		||||
            'is-legend-hidden': legend.get('hideLegendWhenSmall')
 | 
			
		||||
        }"
 | 
			
		||||
    >
 | 
			
		||||
        <div class="c-plot-legend__view-control gl-plot-legend__view-control c-disclosure-triangle is-enabled"
 | 
			
		||||
            ng-class="{ 'c-disclosure-triangle--expanded': legend.get('expanded') }"
 | 
			
		||||
            ng-click="legend.set('expanded', !legend.get('expanded'));">
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="c-plot-legend__wrapper">
 | 
			
		||||
        <div class="c-plot-legend__wrapper"
 | 
			
		||||
            ng-class="{ 'is-cursor-locked':  !!lockHighlightPoint }">
 | 
			
		||||
 | 
			
		||||
            <!-- COLLAPSED PLOT LEGEND -->
 | 
			
		||||
            <div class="plot-wrapper-collapsed-legend"
 | 
			
		||||
                 ng-class="{'icon-cursor-lock': !!lockHighlightPoint}">
 | 
			
		||||
                 ng-class="{'is-cursor-locked': !!lockHighlightPoint }">
 | 
			
		||||
                <div class="c-state-indicator__alert-cursor-lock icon-cursor-lock" title="Cursor is point locked. Click anywhere in the plot to unlock."></div>
 | 
			
		||||
                <div class="plot-legend-item"
 | 
			
		||||
                      ng-repeat="series in series track by $index">
 | 
			
		||||
                     ng-class="{
 | 
			
		||||
                        'is-missing': series.domainObject.status === 'missing'
 | 
			
		||||
                    }"
 | 
			
		||||
                     ng-repeat="series in series track by $index"
 | 
			
		||||
                >
 | 
			
		||||
                    <div class="plot-series-swatch-and-name">
 | 
			
		||||
                        <span class="plot-series-color-swatch"
 | 
			
		||||
                              ng-style="{ 'background-color': series.get('color').asHexString() }">
 | 
			
		||||
                        </span>
 | 
			
		||||
                        <span class="is-missing__indicator" title="This item is missing"></span>
 | 
			
		||||
                        <span class="plot-series-name">{{ series.get('name') }}</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="plot-series-value hover-value-enabled value-to-display-{{ legend.get('valueToShowWhenCollapsed') }} {{ series.closest.mctLimitState.cssClass }}"
 | 
			
		||||
@@ -55,7 +65,10 @@
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <!-- EXPANDED PLOT LEGEND -->
 | 
			
		||||
            <div class="plot-wrapper-expanded-legend">
 | 
			
		||||
            <div class="plot-wrapper-expanded-legend"
 | 
			
		||||
                 ng-class="{'is-cursor-locked': !!lockHighlightPoint }"
 | 
			
		||||
            >
 | 
			
		||||
                <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>
 | 
			
		||||
                        <tr>
 | 
			
		||||
@@ -76,12 +89,17 @@
 | 
			
		||||
                            </th>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                    </thead>
 | 
			
		||||
                    <tr ng-repeat="series in series" class="plot-legend-item">
 | 
			
		||||
                        <td class="plot-series-swatch-and-name"
 | 
			
		||||
                            ng-class="{'icon-cursor-lock': !!lockHighlightPoint}">
 | 
			
		||||
                    <tr ng-repeat="series in series"
 | 
			
		||||
                        class="plot-legend-item"
 | 
			
		||||
                        ng-class="{
 | 
			
		||||
                            'is-missing': series.domainObject.status === 'missing'
 | 
			
		||||
                        }"
 | 
			
		||||
                    >
 | 
			
		||||
                        <td class="plot-series-swatch-and-name">
 | 
			
		||||
                            <span class="plot-series-color-swatch"
 | 
			
		||||
                                  ng-style="{ 'background-color': series.get('color').asHexString() }">
 | 
			
		||||
                            </span>
 | 
			
		||||
                            <span class="is-missing__indicator" title="This item is missing"></span>
 | 
			
		||||
                            <span class="plot-series-name">{{ series.get('name') }}</span>
 | 
			
		||||
                        </td>
 | 
			
		||||
 | 
			
		||||
@@ -117,7 +135,7 @@
 | 
			
		||||
    <div class="plot-wrapper-axis-and-display-area flex-elem grows">
 | 
			
		||||
        <div class="gl-plot-axis-area gl-plot-y has-local-controls"
 | 
			
		||||
             ng-style="{
 | 
			
		||||
                 width: (tickWidth + 30) + 'px'
 | 
			
		||||
                 width: (tickWidth + 20) + 'px'
 | 
			
		||||
             }">
 | 
			
		||||
 | 
			
		||||
            <div class="gl-plot-label gl-plot-y-label"
 | 
			
		||||
@@ -134,7 +152,6 @@
 | 
			
		||||
                    {{option.name}}
 | 
			
		||||
                </option>
 | 
			
		||||
            </select>
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            <mct-ticks axis="yAxis">
 | 
			
		||||
                <div ng-repeat="tick in ticks track by tick.value"
 | 
			
		||||
@@ -148,17 +165,15 @@
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="gl-plot-wrapper-display-area-and-x-axis"
 | 
			
		||||
             ng-style="{
 | 
			
		||||
                     left: (tickWidth + 30) + 'px'
 | 
			
		||||
                 }">
 | 
			
		||||
            <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>
 | 
			
		||||
                <span class="l-state-indicators__alert-cursor-lock icon-cursor-lock"
 | 
			
		||||
                      title="Telemetry point selection is locked. Click anywhere in the plot to unlock."
 | 
			
		||||
                      ng-if="lockHighlightPoint"></span>
 | 
			
		||||
            </div>
 | 
			
		||||
                 left: (tickWidth + 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>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <mct-ticks axis="xAxis">
 | 
			
		||||
                    <div class="gl-plot-hash hash-v"
 | 
			
		||||
                         ng-repeat="tick in ticks track by tick.value"
 | 
			
		||||
@@ -217,9 +232,9 @@
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="gl-plot-axis-area gl-plot-x">
 | 
			
		||||
            <div class="gl-plot-axis-area gl-plot-x has-local-controls">
 | 
			
		||||
                <mct-ticks axis="xAxis">
 | 
			
		||||
                     <div ng-repeat="tick in ticks track by tick.value"
 | 
			
		||||
                     <div ng-repeat="tick in ticks track by tick.text"
 | 
			
		||||
                          class="gl-plot-tick gl-plot-x-tick-label"
 | 
			
		||||
                          ng-style="{
 | 
			
		||||
                              left: (100 * (tick.value - min) / interval) + '%'
 | 
			
		||||
@@ -229,9 +244,21 @@
 | 
			
		||||
                     </div>
 | 
			
		||||
                </mct-ticks>
 | 
			
		||||
 | 
			
		||||
                <div class="gl-plot-label gl-plot-x-label">
 | 
			
		||||
                <div
 | 
			
		||||
                    class="gl-plot-label gl-plot-x-label"
 | 
			
		||||
                    ng-class="{'icon-gear': (xKeyOptions.length > 1 && series.length === 1)}"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ xAxis.get('label') }}
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <select
 | 
			
		||||
                    ng-if="plot.isEnabledXKeyToggle()"
 | 
			
		||||
                    ng-model="selectedXKeyOption.key"
 | 
			
		||||
                    ng-change="plot.toggleXKeyOption()"
 | 
			
		||||
                    class="gl-plot-x-label__select local-controls--hidden"
 | 
			
		||||
                    ng-options="option.key as option.name for option in xKeyOptions"
 | 
			
		||||
                >
 | 
			
		||||
                </select>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="grid-row">
 | 
			
		||||
                    <div class="grid-cell label"
 | 
			
		||||
                         title="The line rendering style for this series.">Line Style</div>
 | 
			
		||||
                         title="The rendering method to join lines for this series.">Line Method</div>
 | 
			
		||||
                    <div class="grid-cell value">{{ {
 | 
			
		||||
                        'none': 'None',
 | 
			
		||||
                        'linear': 'Linear interpolation',
 | 
			
		||||
@@ -56,7 +56,7 @@
 | 
			
		||||
                    <div class="grid-cell label"
 | 
			
		||||
                         title="Whether markers are displayed, and their size.">Markers</div>
 | 
			
		||||
                    <div class="grid-cell value">
 | 
			
		||||
                        {{series.get('markers') ? "Enabled: " + series.get('markerSize') + "px" : "Disabled"}}
 | 
			
		||||
                        {{ series.markerOptionsDisplayText() }}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="grid-row">
 | 
			
		||||
@@ -114,6 +114,11 @@
 | 
			
		||||
                     title="The position of the legend relative to the plot display area.">Position</div>
 | 
			
		||||
                <div class="grid-cell value capitalize">{{ config.legend.get('position') }}</div>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li class="grid-row">
 | 
			
		||||
                <div class="grid-cell label"
 | 
			
		||||
                     title="Hide the legend when the plot is small">Hide when plot small</div>
 | 
			
		||||
                <div class="grid-cell value">{{ config.legend.get('hideLegendWhenSmall') ? "Yes" : "No" }}</div>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li class="grid-row">
 | 
			
		||||
                <div class="grid-cell label"
 | 
			
		||||
                     title="Show the legend expanded by default">Expand by Default</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="grid-row">
 | 
			
		||||
                    <div class="grid-cell label"
 | 
			
		||||
                         title="The line rendering style for this series.">Line Style</div>
 | 
			
		||||
                         title="The rendering method to join lines for this series.">Line Method</div>
 | 
			
		||||
                    <div class="grid-cell value">
 | 
			
		||||
                        <select ng-model="form.interpolate">
 | 
			
		||||
                            <option value="none">None</option>
 | 
			
		||||
@@ -64,12 +64,27 @@
 | 
			
		||||
                <li class="grid-row">
 | 
			
		||||
                    <div class="grid-cell label"
 | 
			
		||||
                         title="Whether markers are displayed.">Markers</div>
 | 
			
		||||
                    <div class="grid-cell value"><input type="checkbox" ng-model="form.markers"/></div>
 | 
			
		||||
                    <div class="grid-cell value">
 | 
			
		||||
                        <input type="checkbox" ng-model="form.markers"/>
 | 
			
		||||
                        <select
 | 
			
		||||
                            ng-show="form.markers"
 | 
			
		||||
                            ng-model="form.markerShape">
 | 
			
		||||
                            <option
 | 
			
		||||
                                ng-repeat="option in markerShapeOptions"
 | 
			
		||||
                                value="{{ option.value }}"
 | 
			
		||||
                                ng-selected="option.value == form.markerShape"
 | 
			
		||||
                            >
 | 
			
		||||
                                {{ option.name }}
 | 
			
		||||
                            </option>
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="grid-row">
 | 
			
		||||
                    <div class="grid-cell label"
 | 
			
		||||
                         title="Display markers visually denoting points in alarm.">Alarm Markers</div>
 | 
			
		||||
                    <div class="grid-cell value"><input type="checkbox" ng-model="form.alarmMarkers"/></div>
 | 
			
		||||
                    <div class="grid-cell value">
 | 
			
		||||
                        <input type="checkbox" ng-model="form.alarmMarkers"/>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </li>
 | 
			
		||||
                <li class="grid-row" ng-show="form.markers || form.alarmMarkers">
 | 
			
		||||
                    <div class="grid-cell label"
 | 
			
		||||
@@ -159,7 +174,6 @@
 | 
			
		||||
                     title="The position of the legend relative to the plot display area.">Position</div>
 | 
			
		||||
                <div class="grid-cell value">
 | 
			
		||||
                    <select ng-model="form.position">
 | 
			
		||||
                        <option value="hidden">Hidden</option>
 | 
			
		||||
                        <option value="top">Top</option>
 | 
			
		||||
                        <option value="right">Right</option>
 | 
			
		||||
                        <option value="bottom">Bottom</option>
 | 
			
		||||
@@ -167,6 +181,11 @@
 | 
			
		||||
                    </select>
 | 
			
		||||
                </div>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li class="grid-row">
 | 
			
		||||
                <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 type="checkbox" ng-model="form.hideLegendWhenSmall"/></div>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li class="grid-row">
 | 
			
		||||
                <div class="grid-cell label"
 | 
			
		||||
                     title="Show the legend expanded by default">Expand by default</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="l-view-section">
 | 
			
		||||
    <div class="l-view-section u-style-receiver js-style-receiver">
 | 
			
		||||
        <div class="c-loading--overlay loading"
 | 
			
		||||
            ng-show="!!pending"></div>
 | 
			
		||||
        <mct-plot config="controller.config"
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@
 | 
			
		||||
                title="Toggle cursor guides">
 | 
			
		||||
        </button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="l-view-section">
 | 
			
		||||
    <div class="l-view-section u-style-receiver js-style-receiver">
 | 
			
		||||
        <div class="c-loading--overlay loading"
 | 
			
		||||
             ng-show="!!currentRequest.pending"></div>
 | 
			
		||||
        <div class="gl-plot child-frame u-inspectable"
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,6 @@ function (
 | 
			
		||||
            this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
 | 
			
		||||
            this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
 | 
			
		||||
            this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
 | 
			
		||||
            this.listenTo(this.config.xAxis, 'change:key', this.clearOffset, this);
 | 
			
		||||
            this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
 | 
			
		||||
            this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
 | 
			
		||||
            this.$scope.$watch('highlights', this.scheduleDraw);
 | 
			
		||||
@@ -81,7 +80,14 @@ function (
 | 
			
		||||
 | 
			
		||||
    MCTChartController.$inject = ['$scope'];
 | 
			
		||||
 | 
			
		||||
    MCTChartController.prototype.reDraw = function (mode, o, series) {
 | 
			
		||||
        this.changeInterpolate(mode, o, series);
 | 
			
		||||
        this.changeMarkers(mode, o, series);
 | 
			
		||||
        this.changeAlarmMarkers(mode, o, series);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTChartController.prototype.onSeriesAdd = function (series) {
 | 
			
		||||
        this.listenTo(series, 'change:xKey', this.reDraw, this);
 | 
			
		||||
        this.listenTo(series, 'change:interpolate', this.changeInterpolate, this);
 | 
			
		||||
        this.listenTo(series, 'change:markers', this.changeMarkers, this);
 | 
			
		||||
        this.listenTo(series, 'change:alarmMarkers', this.changeAlarmMarkers, this);
 | 
			
		||||
@@ -371,7 +377,8 @@ function (
 | 
			
		||||
            chartElement.getBuffer(),
 | 
			
		||||
            chartElement.color().asRGBAArray(),
 | 
			
		||||
            chartElement.count,
 | 
			
		||||
            chartElement.series.get('markerSize')
 | 
			
		||||
            chartElement.series.get('markerSize'),
 | 
			
		||||
            chartElement.series.get('markerShape')
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -395,9 +402,10 @@ function (
 | 
			
		||||
                this.offset.yVal(highlight.point, highlight.series)
 | 
			
		||||
            ]),
 | 
			
		||||
            color = highlight.series.get('color').asRGBAArray(),
 | 
			
		||||
            pointCount = 1;
 | 
			
		||||
            pointCount = 1,
 | 
			
		||||
            shape = highlight.series.get('markerShape');
 | 
			
		||||
 | 
			
		||||
        this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE);
 | 
			
		||||
        this.drawAPI.drawPoints(points, color, pointCount, HIGHLIGHT_SIZE, shape);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTChartController.prototype.drawRectangles = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,7 @@ define([
 | 
			
		||||
            return {
 | 
			
		||||
                position: 'top',
 | 
			
		||||
                expandByDefault: false,
 | 
			
		||||
                hideLegendWhenSmall: false,
 | 
			
		||||
                valueToShowWhenCollapsed: 'nearestValue',
 | 
			
		||||
                showTimestampWhenExpanded: true,
 | 
			
		||||
                showValueWhenExpanded: true,
 | 
			
		||||
 
 | 
			
		||||
@@ -25,12 +25,14 @@ define([
 | 
			
		||||
    'lodash',
 | 
			
		||||
    '../configuration/Model',
 | 
			
		||||
    '../lib/extend',
 | 
			
		||||
    'EventEmitter'
 | 
			
		||||
    'EventEmitter',
 | 
			
		||||
    '../draw/MarkerShapes'
 | 
			
		||||
], function (
 | 
			
		||||
    _,
 | 
			
		||||
    Model,
 | 
			
		||||
    extend,
 | 
			
		||||
    EventEmitter
 | 
			
		||||
    EventEmitter,
 | 
			
		||||
    MARKER_SHAPES
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -56,6 +58,7 @@ define([
 | 
			
		||||
     *                `linear` (points are connected via straight lines), or
 | 
			
		||||
     *                `stepAfter` (points are connected by steps).
 | 
			
		||||
     * `markers`: boolean, whether or not this series should render with markers.
 | 
			
		||||
     * `markerShape`: string, shape of markers.
 | 
			
		||||
     * `markerSize`: number, size in pixels of markers for this series.
 | 
			
		||||
     * `alarmMarkers`: whether or not to display alarm markers for this series.
 | 
			
		||||
     * `stats`: An object that tracks the min and max y values observed in this
 | 
			
		||||
@@ -101,6 +104,7 @@ define([
 | 
			
		||||
                xKey: options.collection.plot.xAxis.get('key'),
 | 
			
		||||
                yKey: range.key,
 | 
			
		||||
                markers: true,
 | 
			
		||||
                markerShape: 'point',
 | 
			
		||||
                markerSize: 2.0,
 | 
			
		||||
                alarmMarkers: true
 | 
			
		||||
            };
 | 
			
		||||
@@ -410,6 +414,31 @@ define([
 | 
			
		||||
            } else {
 | 
			
		||||
                this.filters = deepCopiedFilters;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        getDisplayRange: function (xKey) {
 | 
			
		||||
            const unsortedData = this.data;
 | 
			
		||||
            this.data = [];
 | 
			
		||||
            unsortedData.forEach(point => this.add(point, false));
 | 
			
		||||
 | 
			
		||||
            const minValue = this.getXVal(this.data[0]);
 | 
			
		||||
            const maxValue = this.getXVal(this.data[this.data.length-1]);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                min: minValue,
 | 
			
		||||
                max: maxValue
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        markerOptionsDisplayText: function () {
 | 
			
		||||
            const showMarkers = this.get('markers');
 | 
			
		||||
            if (!showMarkers) {
 | 
			
		||||
                return "Disabled";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const markerShapeKey = this.get('markerShape');
 | 
			
		||||
            const markerShape = MARKER_SHAPES[markerShapeKey].label;
 | 
			
		||||
            const markerSize = this.get('markerSize');
 | 
			
		||||
 | 
			
		||||
            return `${markerShape}: ${markerSize}px`;
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,7 @@ define([
 | 
			
		||||
                this.set('range', this.get('range'));
 | 
			
		||||
            }
 | 
			
		||||
            this.listenTo(this, 'change:key', this.changeKey, this);
 | 
			
		||||
            this.listenTo(this, 'resetSeries', this.resetSeries, this);
 | 
			
		||||
        },
 | 
			
		||||
        changeKey: function (newKey) {
 | 
			
		||||
            var series = this.plot.series.first();
 | 
			
		||||
@@ -62,8 +63,13 @@ define([
 | 
			
		||||
                });
 | 
			
		||||
                this.set('label', newKey);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.plot.series.forEach(function (plotSeries) {
 | 
			
		||||
                plotSeries.set('xKey', newKey);
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        resetSeries: function () {
 | 
			
		||||
            this.plot.series.forEach(function (plotSeries) {
 | 
			
		||||
                plotSeries.reset();
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -23,10 +23,12 @@
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    'EventEmitter',
 | 
			
		||||
    '../lib/eventHelpers'
 | 
			
		||||
    '../lib/eventHelpers',
 | 
			
		||||
    './MarkerShapes'
 | 
			
		||||
], function (
 | 
			
		||||
    EventEmitter,
 | 
			
		||||
    eventHelpers
 | 
			
		||||
    eventHelpers,
 | 
			
		||||
    MARKER_SHAPES
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -121,18 +123,17 @@ define([
 | 
			
		||||
        buf,
 | 
			
		||||
        color,
 | 
			
		||||
        points,
 | 
			
		||||
        pointSize
 | 
			
		||||
        pointSize,
 | 
			
		||||
        shape
 | 
			
		||||
    ) {
 | 
			
		||||
        var i = 0,
 | 
			
		||||
            offset = pointSize / 2;
 | 
			
		||||
        const drawC2DShape = MARKER_SHAPES[shape].drawC2D.bind(this);
 | 
			
		||||
 | 
			
		||||
        this.setColor(color);
 | 
			
		||||
 | 
			
		||||
        for (; i < points; i++) {
 | 
			
		||||
            this.c2d.fillRect(
 | 
			
		||||
                this.x(buf[i * 2]) - offset,
 | 
			
		||||
                this.y(buf[i * 2 + 1]) - offset,
 | 
			
		||||
                pointSize,
 | 
			
		||||
        for (let i = 0; i < points; i++) {
 | 
			
		||||
            drawC2DShape(
 | 
			
		||||
                this.x(buf[i * 2]),
 | 
			
		||||
                this.y(buf[i * 2 + 1]),
 | 
			
		||||
                pointSize
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -23,30 +23,64 @@
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    'EventEmitter',
 | 
			
		||||
    '../lib/eventHelpers'
 | 
			
		||||
    '../lib/eventHelpers',
 | 
			
		||||
    './MarkerShapes'
 | 
			
		||||
], function (
 | 
			
		||||
    EventEmitter,
 | 
			
		||||
    eventHelpers
 | 
			
		||||
    eventHelpers,
 | 
			
		||||
    MARKER_SHAPES
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    // WebGL shader sources (for drawing plain colors)
 | 
			
		||||
    var FRAGMENT_SHADER = [
 | 
			
		||||
            "precision mediump float;",
 | 
			
		||||
            "uniform vec4 uColor;",
 | 
			
		||||
            "void main(void) {",
 | 
			
		||||
            "gl_FragColor = uColor;",
 | 
			
		||||
            "}"
 | 
			
		||||
        ].join('\n'),
 | 
			
		||||
        VERTEX_SHADER = [
 | 
			
		||||
            "attribute vec2 aVertexPosition;",
 | 
			
		||||
            "uniform vec2 uDimensions;",
 | 
			
		||||
            "uniform vec2 uOrigin;",
 | 
			
		||||
            "uniform float uPointSize;",
 | 
			
		||||
            "void main(void) {",
 | 
			
		||||
            "gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);",
 | 
			
		||||
            "gl_PointSize = uPointSize;",
 | 
			
		||||
            "}"
 | 
			
		||||
        ].join('\n');
 | 
			
		||||
    const FRAGMENT_SHADER = `
 | 
			
		||||
        precision mediump float;
 | 
			
		||||
        uniform vec4 uColor;
 | 
			
		||||
        uniform int uMarkerShape;
 | 
			
		||||
        
 | 
			
		||||
        void main(void) {
 | 
			
		||||
            gl_FragColor = uColor;
 | 
			
		||||
 | 
			
		||||
            if (uMarkerShape > 1) {
 | 
			
		||||
                vec2 clipSpacePointCoord = 2.0 * gl_PointCoord - 1.0;
 | 
			
		||||
 | 
			
		||||
                if (uMarkerShape == 2) { // circle
 | 
			
		||||
                    float distance = length(clipSpacePointCoord);
 | 
			
		||||
 | 
			
		||||
                    if (distance > 1.0) {
 | 
			
		||||
                        discard;
 | 
			
		||||
                    }
 | 
			
		||||
                } else if (uMarkerShape == 3) { // diamond
 | 
			
		||||
                    float distance = abs(clipSpacePointCoord.x) + abs(clipSpacePointCoord.y);
 | 
			
		||||
 | 
			
		||||
                    if (distance > 1.0) {
 | 
			
		||||
                        discard;
 | 
			
		||||
                    }
 | 
			
		||||
                } else if (uMarkerShape == 4) { // triangle
 | 
			
		||||
                    float x = clipSpacePointCoord.x;
 | 
			
		||||
                    float y = clipSpacePointCoord.y;
 | 
			
		||||
                    float distance = 2.0 * x - 1.0;
 | 
			
		||||
                    float distance2 = -2.0 * x - 1.0;
 | 
			
		||||
 | 
			
		||||
                    if (distance > y || distance2 > y) {
 | 
			
		||||
                        discard;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    `;
 | 
			
		||||
 | 
			
		||||
    const VERTEX_SHADER = `
 | 
			
		||||
        attribute vec2 aVertexPosition;
 | 
			
		||||
        uniform vec2 uDimensions;
 | 
			
		||||
        uniform vec2 uOrigin;
 | 
			
		||||
        uniform float uPointSize;
 | 
			
		||||
        
 | 
			
		||||
        void main(void) {
 | 
			
		||||
            gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);
 | 
			
		||||
            gl_PointSize = uPointSize;
 | 
			
		||||
        }
 | 
			
		||||
    `;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a draw api utilizing WebGL.
 | 
			
		||||
@@ -90,6 +124,7 @@ define([
 | 
			
		||||
        this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
 | 
			
		||||
        this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
 | 
			
		||||
        this.gl.compileShader(this.vertexShader);
 | 
			
		||||
 | 
			
		||||
        this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
 | 
			
		||||
        this.gl.shaderSource(this.fragmentShader, FRAGMENT_SHADER);
 | 
			
		||||
        this.gl.compileShader(this.fragmentShader);
 | 
			
		||||
@@ -105,6 +140,7 @@ define([
 | 
			
		||||
        // shader programs (to pass values into shaders at draw-time)
 | 
			
		||||
        this.aVertexPosition = this.gl.getAttribLocation(this.program, "aVertexPosition");
 | 
			
		||||
        this.uColor = this.gl.getUniformLocation(this.program, "uColor");
 | 
			
		||||
        this.uMarkerShape = this.gl.getUniformLocation(this.program, "uMarkerShape");
 | 
			
		||||
        this.uDimensions = this.gl.getUniformLocation(this.program, "uDimensions");
 | 
			
		||||
        this.uOrigin = this.gl.getUniformLocation(this.program, "uOrigin");
 | 
			
		||||
        this.uPointSize = this.gl.getUniformLocation(this.program, "uPointSize");
 | 
			
		||||
@@ -114,9 +150,6 @@ define([
 | 
			
		||||
        // Create a buffer to holds points which will be drawn
 | 
			
		||||
        this.buffer = this.gl.createBuffer();
 | 
			
		||||
 | 
			
		||||
        // Use a line width of 2.0 for legibility
 | 
			
		||||
        this.gl.lineWidth(2.0);
 | 
			
		||||
 | 
			
		||||
        // Enable blending, for smoothness
 | 
			
		||||
        this.gl.enable(this.gl.BLEND);
 | 
			
		||||
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
 | 
			
		||||
@@ -138,14 +171,18 @@ define([
 | 
			
		||||
            ((v - this.origin[1]) / this.dimensions[1]) * this.height;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    DrawWebGL.prototype.doDraw = function (drawType, buf, color, points) {
 | 
			
		||||
    DrawWebGL.prototype.doDraw = function (drawType, buf, color, points, shape) {
 | 
			
		||||
        if (this.isContextLost) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const shapeCode = MARKER_SHAPES[shape] ? MARKER_SHAPES[shape].drawWebGL : 0;
 | 
			
		||||
 | 
			
		||||
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
 | 
			
		||||
        this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW);
 | 
			
		||||
        this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
 | 
			
		||||
        this.gl.uniform4fv(this.uColor, color);
 | 
			
		||||
        this.gl.uniform1i(this.uMarkerShape, shapeCode)
 | 
			
		||||
        this.gl.drawArrays(drawType, 0, points);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -210,12 +247,12 @@ define([
 | 
			
		||||
     * Draw the buffer as points.
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize) {
 | 
			
		||||
    DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize, shape) {
 | 
			
		||||
        if (this.isContextLost) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.gl.uniform1f(this.uPointSize, pointSize);
 | 
			
		||||
        this.doDraw(this.gl.POINTS, buf, color, points);
 | 
			
		||||
        this.doDraw(this.gl.POINTS, buf, color, points, shape);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								src/plugins/plot/src/draw/MarkerShapes.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								src/plugins/plot/src/draw/MarkerShapes.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2020, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (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 () {
 | 
			
		||||
    /**
 | 
			
		||||
     * @label string (required) display name of shape
 | 
			
		||||
     * @drawWebGL integer (unique, required) index provided to WebGL Fragment Shader
 | 
			
		||||
     * @drawC2D function (required) canvas2d draw function
 | 
			
		||||
     */
 | 
			
		||||
    const MARKER_SHAPES = {
 | 
			
		||||
        point: {
 | 
			
		||||
            label: 'Point',
 | 
			
		||||
            drawWebGL: 1,
 | 
			
		||||
            drawC2D: function (x, y, size) {
 | 
			
		||||
                const offset = size / 2;
 | 
			
		||||
 | 
			
		||||
                this.c2d.fillRect(x - offset, y - offset, size, size);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        circle: {
 | 
			
		||||
            label: 'Circle',
 | 
			
		||||
            drawWebGL: 2,
 | 
			
		||||
            drawC2D: function (x, y, size) {
 | 
			
		||||
                const radius = size / 2;
 | 
			
		||||
 | 
			
		||||
                this.c2d.beginPath();
 | 
			
		||||
                this.c2d.arc(x, y, radius, 0, 2 * Math.PI, false);
 | 
			
		||||
                this.c2d.closePath();
 | 
			
		||||
                this.c2d.fill();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        diamond: {
 | 
			
		||||
            label: 'Diamond',
 | 
			
		||||
            drawWebGL: 3,
 | 
			
		||||
            drawC2D: function (x, y, size) {
 | 
			
		||||
                const offset = size / 2;
 | 
			
		||||
                const top = [x, y + offset];
 | 
			
		||||
                const right = [x + offset, y];
 | 
			
		||||
                const bottom = [x, y - offset];
 | 
			
		||||
                const left = [x - offset, y];
 | 
			
		||||
 | 
			
		||||
                this.c2d.beginPath();
 | 
			
		||||
                this.c2d.moveTo(...top);
 | 
			
		||||
                this.c2d.lineTo(...right);
 | 
			
		||||
                this.c2d.lineTo(...bottom);
 | 
			
		||||
                this.c2d.lineTo(...left);
 | 
			
		||||
                this.c2d.closePath();
 | 
			
		||||
                this.c2d.fill();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        triangle: {
 | 
			
		||||
            label: 'Triangle',
 | 
			
		||||
            drawWebGL: 4,
 | 
			
		||||
            drawC2D: function (x, y, size) {
 | 
			
		||||
                const offset = size / 2;
 | 
			
		||||
                const v1 = [x, y - offset];
 | 
			
		||||
                const v2 = [x - offset, y + offset];
 | 
			
		||||
                const v3 = [x + offset, y + offset];
 | 
			
		||||
 | 
			
		||||
                this.c2d.beginPath();
 | 
			
		||||
                this.c2d.moveTo(...v1);
 | 
			
		||||
                this.c2d.lineTo(...v2);
 | 
			
		||||
                this.c2d.lineTo(...v3);
 | 
			
		||||
                this.c2d.closePath();
 | 
			
		||||
                this.c2d.fill();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return MARKER_SHAPES;
 | 
			
		||||
});
 | 
			
		||||
@@ -32,6 +32,11 @@ define([
 | 
			
		||||
                modelProp: 'position',
 | 
			
		||||
                objectPath: 'configuration.legend.position'
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                modelProp: 'hideLegendWhenSmall',
 | 
			
		||||
                coerce: Boolean,
 | 
			
		||||
                objectPath: 'configuration.legend.hideLegendWhenSmall'
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                modelProp: 'expandByDefault',
 | 
			
		||||
                coerce: Boolean,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,9 +22,11 @@
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    './PlotModelFormController',
 | 
			
		||||
    '../draw/MarkerShapes',
 | 
			
		||||
    'lodash'
 | 
			
		||||
], function (
 | 
			
		||||
    PlotModelFormController,
 | 
			
		||||
    MARKER_SHAPES,
 | 
			
		||||
    _
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
@@ -93,6 +95,13 @@ define([
 | 
			
		||||
                        value: o.key
 | 
			
		||||
                    };
 | 
			
		||||
                });
 | 
			
		||||
            this.$scope.markerShapeOptions = Object.entries(MARKER_SHAPES)
 | 
			
		||||
                .map(([key, obj]) => {
 | 
			
		||||
                    return {
 | 
			
		||||
                        name: obj.label,
 | 
			
		||||
                        value: key
 | 
			
		||||
                    };
 | 
			
		||||
                });
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        fields: [
 | 
			
		||||
@@ -108,6 +117,10 @@ define([
 | 
			
		||||
                modelProp: 'markers',
 | 
			
		||||
                objectPath: dynamicPathForKey('markers')
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                modelProp: 'markerShape',
 | 
			
		||||
                objectPath: dynamicPathForKey('markerShape')
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                modelProp: 'markerSize',
 | 
			
		||||
                coerce: Number,
 | 
			
		||||
 
 | 
			
		||||
@@ -71,8 +71,6 @@ define([
 | 
			
		||||
        this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
 | 
			
		||||
        this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
 | 
			
		||||
        this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
 | 
			
		||||
 | 
			
		||||
        this.watchForMarquee();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.initialize = function () {
 | 
			
		||||
@@ -83,11 +81,6 @@ define([
 | 
			
		||||
        this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
 | 
			
		||||
        this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
 | 
			
		||||
 | 
			
		||||
        this.watchForMarquee();
 | 
			
		||||
 | 
			
		||||
        this.listenTo(this.$window, 'keydown', this.toggleInteractionMode, this);
 | 
			
		||||
        this.listenTo(this.$window, 'keyup', this.resetInteractionMode, this);
 | 
			
		||||
 | 
			
		||||
        this.$scope.rectangles = [];
 | 
			
		||||
        this.$scope.tickWidth = 0;
 | 
			
		||||
 | 
			
		||||
@@ -108,12 +101,32 @@ define([
 | 
			
		||||
        this.listenTo(this.$scope, 'plot:tickWidth', this.onTickWidthChange, this);
 | 
			
		||||
        this.listenTo(this.$scope, 'plot:highlight:set', this.onPlotHighlightSet, this);
 | 
			
		||||
        this.listenTo(this.$scope, 'plot:reinitializeCanvas', this.initCanvas, this);
 | 
			
		||||
        this.listenTo(this.config.xAxis, 'resetSeries', this.setUpXAxisOptions, this);
 | 
			
		||||
        this.listenTo(this.config.xAxis, 'change:displayRange', this.onXAxisChange, this);
 | 
			
		||||
        this.listenTo(this.config.yAxis, 'change:displayRange', this.onYAxisChange, this);
 | 
			
		||||
 | 
			
		||||
        this.setUpXAxisOptions();
 | 
			
		||||
        this.setUpYAxisOptions();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.setUpXAxisOptions = function () {
 | 
			
		||||
        const xAxisKey = this.config.xAxis.get('key');
 | 
			
		||||
 | 
			
		||||
        if (this.$scope.series.length === 1) {
 | 
			
		||||
            let metadata = this.$scope.series[0].metadata;
 | 
			
		||||
 | 
			
		||||
            this.$scope.xKeyOptions = metadata
 | 
			
		||||
                .valuesForHints(['domain'])
 | 
			
		||||
                .map(function (o) {
 | 
			
		||||
                    return {
 | 
			
		||||
                        name: o.name,
 | 
			
		||||
                        key: o.key
 | 
			
		||||
                    };
 | 
			
		||||
                });
 | 
			
		||||
            this.$scope.selectedXKeyOption = this.getXKeyOption(xAxisKey);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.setUpYAxisOptions = function () {
 | 
			
		||||
        if (this.$scope.series.length === 1) {
 | 
			
		||||
            let metadata = this.$scope.series[0].metadata;
 | 
			
		||||
@@ -243,12 +256,16 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.onMouseDown = function ($event) {
 | 
			
		||||
        // do not monitor drag events on browser context click
 | 
			
		||||
        if (event.ctrlKey) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.listenTo(this.$window, 'mouseup', this.onMouseUp, this);
 | 
			
		||||
        this.listenTo(this.$window, 'mousemove', this.trackMousePosition, this);
 | 
			
		||||
        if (this.allowPan) {
 | 
			
		||||
        if (event.altKey) {
 | 
			
		||||
            return this.startPan($event);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.allowMarquee) {
 | 
			
		||||
        } else {
 | 
			
		||||
            return this.startMarquee($event);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
@@ -261,11 +278,11 @@ define([
 | 
			
		||||
            this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.allowPan) {
 | 
			
		||||
        if (this.pan) {
 | 
			
		||||
            return this.endPan($event);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.allowMarquee) {
 | 
			
		||||
        if (this.marquee) {
 | 
			
		||||
            return this.endMarquee($event);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
@@ -289,6 +306,9 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.startMarquee = function ($event) {
 | 
			
		||||
        this.$canvas.removeClass('plot-drag');
 | 
			
		||||
        this.$canvas.addClass('plot-marquee');
 | 
			
		||||
 | 
			
		||||
        this.trackMousePosition($event);
 | 
			
		||||
        if (this.positionOverPlot) {
 | 
			
		||||
            this.freeze();
 | 
			
		||||
@@ -444,6 +464,9 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.startPan = function ($event) {
 | 
			
		||||
        this.$canvas.addClass('plot-drag');
 | 
			
		||||
        this.$canvas.removeClass('plot-marquee');
 | 
			
		||||
 | 
			
		||||
        this.trackMousePosition($event);
 | 
			
		||||
        this.freeze();
 | 
			
		||||
        this.pan = {
 | 
			
		||||
@@ -486,32 +509,6 @@ define([
 | 
			
		||||
        this.$scope.$emit('user:viewport:change:end');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.watchForMarquee = function () {
 | 
			
		||||
        this.$canvas.removeClass('plot-drag');
 | 
			
		||||
        this.$canvas.addClass('plot-marquee');
 | 
			
		||||
        this.allowPan = false;
 | 
			
		||||
        this.allowMarquee = true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.watchForPan = function () {
 | 
			
		||||
        this.$canvas.addClass('plot-drag');
 | 
			
		||||
        this.$canvas.removeClass('plot-marquee');
 | 
			
		||||
        this.allowPan = true;
 | 
			
		||||
        this.allowMarquee = false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.toggleInteractionMode = function (event) {
 | 
			
		||||
        if (event.keyCode === 18) { // control key.
 | 
			
		||||
            this.watchForPan();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.resetInteractionMode = function (event) {
 | 
			
		||||
        if (event.keyCode === 18) {
 | 
			
		||||
            this.watchForMarquee();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.freeze = function () {
 | 
			
		||||
        this.config.yAxis.set('frozen', true);
 | 
			
		||||
        this.config.xAxis.set('frozen', true);
 | 
			
		||||
@@ -543,6 +540,22 @@ define([
 | 
			
		||||
        this.cursorGuide = !this.cursorGuide;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.getXKeyOption = function (key) {
 | 
			
		||||
        return this.$scope.xKeyOptions.find(option => option.key === key);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.isEnabledXKeyToggle = function () {
 | 
			
		||||
        const isSinglePlot = this.$scope.xKeyOptions.length > 1 && this.$scope.series.length === 1;
 | 
			
		||||
        const isFrozen = this.config.xAxis.get('frozen');
 | 
			
		||||
        const inRealTimeMode = this.config.openmct.time.clock();
 | 
			
		||||
 | 
			
		||||
        return isSinglePlot && !isFrozen && !inRealTimeMode;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.toggleXKeyOption = function () {
 | 
			
		||||
        this.config.xAxis.set('key', this.$scope.selectedXKeyOption.key);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.toggleYAxisLabel = function (label, options, series) {
 | 
			
		||||
        let yAxisObject = options.filter(o => o.name === label)[0];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -122,6 +122,7 @@ define([
 | 
			
		||||
            this.tickUpdate = false;
 | 
			
		||||
            this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this);
 | 
			
		||||
            this.listenTo(this.axis, 'change:format', this.updateTicks, this);
 | 
			
		||||
            this.listenTo(this.axis, 'change:key', this.updateTicksForceRegeneration, this);
 | 
			
		||||
            this.listenTo(this.$scope, '$destroy', this.stopListening, this);
 | 
			
		||||
            this.updateTicks();
 | 
			
		||||
        }
 | 
			
		||||
@@ -133,12 +134,18 @@ define([
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Determine whether ticks should be regenerated for a given range.
 | 
			
		||||
     * Ticks are updated a) if they don't exist, b) if the existing ticks are
 | 
			
		||||
     * outside of given range, or c) if the range exceeds the size of the tick
 | 
			
		||||
     * range by more than one tick step.
 | 
			
		||||
     * Ticks are updated
 | 
			
		||||
     * a) if they don't exist,
 | 
			
		||||
     * b) if existing ticks are outside of given range,
 | 
			
		||||
     * c) if range exceeds size of tick range by more than one tick step,
 | 
			
		||||
     * d) if forced to regenerate (ex. changing x-axis metadata).
 | 
			
		||||
     *
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    MCTTicksController.prototype.shouldRegenerateTicks = function (range) {
 | 
			
		||||
    MCTTicksController.prototype.shouldRegenerateTicks = function (range, forceRegeneration) {
 | 
			
		||||
        if (forceRegeneration) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.tickRange || !this.$scope.ticks || !this.$scope.ticks.length) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
@@ -166,7 +173,11 @@ define([
 | 
			
		||||
        return ticks(range.min, range.max, number);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTTicksController.prototype.updateTicks = function () {
 | 
			
		||||
    MCTTicksController.prototype.updateTicksForceRegeneration = function () {
 | 
			
		||||
        this.updateTicks(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MCTTicksController.prototype.updateTicks = function (forceRegeneration = false) {
 | 
			
		||||
        var range = this.axis.get('displayRange');
 | 
			
		||||
        if (!range) {
 | 
			
		||||
            delete this.$scope.min;
 | 
			
		||||
@@ -184,7 +195,7 @@ define([
 | 
			
		||||
        this.$scope.min = range.min;
 | 
			
		||||
        this.$scope.max = range.max;
 | 
			
		||||
        this.$scope.interval = Math.abs(range.min - range.max);
 | 
			
		||||
        if (this.shouldRegenerateTicks(range)) {
 | 
			
		||||
        if (this.shouldRegenerateTicks(range, forceRegeneration)) {
 | 
			
		||||
            var newTicks = this.getTicks();
 | 
			
		||||
            this.tickRange = {
 | 
			
		||||
                min: Math.min.apply(Math, newTicks),
 | 
			
		||||
@@ -242,6 +253,7 @@ define([
 | 
			
		||||
            this.$scope.$emit('plot:tickWidth', tickWidth);
 | 
			
		||||
            this.shouldCheckWidth = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.$scope.$digest();
 | 
			
		||||
        this.tickUpdate = false;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -130,6 +130,9 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    PlotController.prototype.addSeries = function (series) {
 | 
			
		||||
        this.listenTo(series, 'change:xKey', function (xKey) {
 | 
			
		||||
            this.setDisplayRange(series, xKey);
 | 
			
		||||
        }, this);
 | 
			
		||||
        this.listenTo(series, 'change:yKey', function () {
 | 
			
		||||
            this.loadSeriesData(series);
 | 
			
		||||
        }, this);
 | 
			
		||||
@@ -141,6 +144,16 @@ define([
 | 
			
		||||
        this.loadSeriesData(series);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    PlotController.prototype.setDisplayRange = function (series, xKey) {
 | 
			
		||||
        if (this.config.series.models.length !== 1) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const displayRange = series.getDisplayRange(xKey);
 | 
			
		||||
        this.config.xAxis.set('range', displayRange);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    PlotController.prototype.removeSeries = function (plotSeries) {
 | 
			
		||||
        this.stopListening(plotSeries);
 | 
			
		||||
    };
 | 
			
		||||
@@ -162,6 +175,7 @@ define([
 | 
			
		||||
 | 
			
		||||
    PlotController.prototype.onTimeSystemChange = function (timeSystem) {
 | 
			
		||||
        this.config.xAxis.set('key', timeSystem.key);
 | 
			
		||||
        this.config.xAxis.emit('resetSeries');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    PlotController.prototype.destroy = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
# Espresso Theme
 | 
			
		||||
A light colored theme for the Open MCT user interface.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
```js
 | 
			
		||||
openmct.install(openmct.plugins.Snow());
 | 
			
		||||
```
 | 
			
		||||
@@ -17,6 +17,19 @@
 | 
			
		||||
            margin-right: $interiorMarginSm;
 | 
			
		||||
            opacity: 0.7;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &__label {
 | 
			
		||||
            flex: 1 1 auto;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &__close-btn {
 | 
			
		||||
            flex: 0 0 auto;
 | 
			
		||||
            pointer-events: all;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        > * + * {
 | 
			
		||||
            margin-left: $interiorMargin;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__object-holder {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-tabs-view__tabs-holder c-tabs"
 | 
			
		||||
        :class="{
 | 
			
		||||
            'is-dragging': isDragging,
 | 
			
		||||
            'is-dragging': isDragging && allowEditing,
 | 
			
		||||
            'is-mouse-over': allowDrop
 | 
			
		||||
        }"
 | 
			
		||||
    >
 | 
			
		||||
@@ -19,18 +19,21 @@
 | 
			
		||||
        >
 | 
			
		||||
            Drag objects here to add them to this view.
 | 
			
		||||
        </div>
 | 
			
		||||
        <button
 | 
			
		||||
        <div
 | 
			
		||||
            v-for="(tab,index) in tabsList"
 | 
			
		||||
            :key="index"
 | 
			
		||||
            class="c-tabs-view__tab c-tab"
 | 
			
		||||
            :class="[
 | 
			
		||||
                {'is-current': isCurrent(tab)},
 | 
			
		||||
                tab.type.definition.cssClass
 | 
			
		||||
            ]"
 | 
			
		||||
            class="c-tab c-tabs-view__tab"
 | 
			
		||||
            :class="{
 | 
			
		||||
                'is-current': isCurrent(tab)
 | 
			
		||||
            }"
 | 
			
		||||
            @click="showTab(tab, index)"
 | 
			
		||||
        >
 | 
			
		||||
            <span class="c-button__label">{{ tab.domainObject.name }}</span>
 | 
			
		||||
        </button>
 | 
			
		||||
            <span class="c-button__label c-tabs-view__tab__label">{{ tab.domainObject.name }}</span>
 | 
			
		||||
            <button v-if="isEditing"
 | 
			
		||||
                    class="icon-x c-click-icon c-tabs-view__tab__close-btn"
 | 
			
		||||
                    @click="showRemoveDialog(index)"
 | 
			
		||||
            ></button>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div
 | 
			
		||||
        v-for="(tab, index) in tabsList"
 | 
			
		||||
@@ -38,15 +41,6 @@
 | 
			
		||||
        class="c-tabs-view__object-holder"
 | 
			
		||||
        :class="{'c-tabs-view__object-holder--hidden': !isCurrent(tab)}"
 | 
			
		||||
    >
 | 
			
		||||
        <div
 | 
			
		||||
            v-if="currentTab"
 | 
			
		||||
            class="c-tabs-view__object-name c-object-label l-browse-bar__object-name--w"
 | 
			
		||||
            :class="currentTab.type.definition.cssClass"
 | 
			
		||||
        >
 | 
			
		||||
            <div class="l-browse-bar__object-name c-object-label__name">
 | 
			
		||||
                {{ currentTab.domainObject.name }}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <object-view
 | 
			
		||||
            v-if="internalDomainObject.keep_alive ? currentTab : isCurrent(tab)"
 | 
			
		||||
            class="c-tabs-view__object"
 | 
			
		||||
@@ -58,6 +52,13 @@
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import ObjectView from '../../../ui/components/ObjectView.vue';
 | 
			
		||||
import RemoveAction from '../../remove/RemoveAction.js';
 | 
			
		||||
import {
 | 
			
		||||
    getSearchParam,
 | 
			
		||||
    setSearchParam,
 | 
			
		||||
    deleteSearchParam
 | 
			
		||||
} from 'utils/openmctLocation';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
var unknownObjectType = {
 | 
			
		||||
    definition: {
 | 
			
		||||
@@ -71,53 +72,114 @@ export default {
 | 
			
		||||
    components: {
 | 
			
		||||
        ObjectView
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
        isEditing: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data: function () {
 | 
			
		||||
        let keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            internalDomainObject: this.domainObject,
 | 
			
		||||
            currentTab: {},
 | 
			
		||||
            currentTabIndex: undefined,
 | 
			
		||||
            tabsList: [],
 | 
			
		||||
            setCurrentTab: true,
 | 
			
		||||
            isDragging: false,
 | 
			
		||||
            allowDrop: false
 | 
			
		||||
            allowDrop: false,
 | 
			
		||||
            searchTabKey: `tabs.pos.${keyString}`
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        allowEditing() {
 | 
			
		||||
            return !this.internalDomainObject.locked && this.isEditing;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        if (this.composition) {
 | 
			
		||||
            this.composition.on('add', this.addItem);
 | 
			
		||||
            this.composition.on('remove', this.removeItem);
 | 
			
		||||
            this.composition.on('reorder', this.onReorder);
 | 
			
		||||
            this.composition.load().then(() => {
 | 
			
		||||
                let currentTabIndex = this.domainObject.currentTabIndex;
 | 
			
		||||
                let currentTabIndexFromURL = getSearchParam(this.searchTabKey);
 | 
			
		||||
                let currentTabIndexFromDomainObject = this.internalDomainObject.currentTabIndex;
 | 
			
		||||
 | 
			
		||||
                if (currentTabIndex !== undefined && this.tabsList.length > currentTabIndex) {
 | 
			
		||||
                    this.currentTab = this.tabsList[currentTabIndex];
 | 
			
		||||
                if (currentTabIndexFromURL !== null) {
 | 
			
		||||
                    this.setCurrentTabByIndex(currentTabIndexFromURL);
 | 
			
		||||
                } else if (currentTabIndexFromDomainObject !== undefined) {
 | 
			
		||||
                    this.setCurrentTabByIndex(currentTabIndexFromDomainObject);
 | 
			
		||||
                    this.storeCurrentTabIndexInURL(currentTabIndexFromDomainObject);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.unsubscribe = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
 | 
			
		||||
 | 
			
		||||
        this.RemoveAction = new RemoveAction(this.openmct);
 | 
			
		||||
        document.addEventListener('dragstart', this.dragstart);
 | 
			
		||||
        document.addEventListener('dragend', this.dragend);
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        this.persistCurrentTabIndex(this.currentTabIndex);
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
        this.composition.off('add', this.addItem);
 | 
			
		||||
        this.composition.off('remove', this.removeItem);
 | 
			
		||||
        this.composition.off('reorder', this.onReorder);
 | 
			
		||||
 | 
			
		||||
        this.unsubscribe();
 | 
			
		||||
        this.clearCurrentTabIndexFromURL();
 | 
			
		||||
 | 
			
		||||
        document.removeEventListener('dragstart', this.dragstart);
 | 
			
		||||
        document.removeEventListener('dragend', this.dragend);
 | 
			
		||||
    },
 | 
			
		||||
    methods:{
 | 
			
		||||
        setCurrentTabByIndex(index) {
 | 
			
		||||
            if (this.tabsList[index]) {
 | 
			
		||||
                this.currentTab = this.tabsList[index];
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        showTab(tab, index) {
 | 
			
		||||
            if (index !== undefined) {
 | 
			
		||||
                this.storeCurrentTabIndex(index);
 | 
			
		||||
                this.storeCurrentTabIndexInURL(index);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.currentTab = tab;
 | 
			
		||||
        },
 | 
			
		||||
        showRemoveDialog(index) {
 | 
			
		||||
            if(!this.tabsList[index]) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let activeTab = this.tabsList[index];
 | 
			
		||||
            let childDomainObject = activeTab.domainObject
 | 
			
		||||
 | 
			
		||||
            let prompt = this.openmct.overlays.dialog({
 | 
			
		||||
                iconClass: 'alert',
 | 
			
		||||
                message: `This action will remove this tab from the Tabs Layout. Do you want to continue?`,
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        label: 'Ok',
 | 
			
		||||
                        emphasis: 'true',
 | 
			
		||||
                        callback: () => {
 | 
			
		||||
                            this.removeFromComposition(childDomainObject);
 | 
			
		||||
                            prompt.dismiss();
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        label: 'Cancel',
 | 
			
		||||
                        callback: () => {
 | 
			
		||||
                            prompt.dismiss();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        removeFromComposition(childDomainObject) {
 | 
			
		||||
            this.composition.remove(childDomainObject);
 | 
			
		||||
        },
 | 
			
		||||
        addItem(domainObject) {
 | 
			
		||||
            let type = this.openmct.types.get(domainObject.type) || unknownObjectType,
 | 
			
		||||
                tabItem = {
 | 
			
		||||
@@ -133,6 +195,10 @@ export default {
 | 
			
		||||
                this.setCurrentTab = false;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        reset() {
 | 
			
		||||
            this.currentTab = {};
 | 
			
		||||
            this.setCurrentTab = true;
 | 
			
		||||
        },
 | 
			
		||||
        removeItem(identifier) {
 | 
			
		||||
            let pos = this.tabsList.findIndex(tab =>
 | 
			
		||||
                    tab.domainObject.identifier.namespace === identifier.namespace && tab.domainObject.identifier.key === identifier.key
 | 
			
		||||
@@ -144,6 +210,10 @@ export default {
 | 
			
		||||
            if (this.isCurrent(tabToBeRemoved)) {
 | 
			
		||||
                this.showTab(this.tabsList[this.tabsList.length - 1], this.tabsList.length - 1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!this.tabsList.length) {
 | 
			
		||||
                this.reset();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        onReorder(reorderPlan) {
 | 
			
		||||
            let oldTabs = this.tabsList.slice();
 | 
			
		||||
@@ -154,7 +224,7 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        onDrop(e) {
 | 
			
		||||
            this.setCurrentTab = true;
 | 
			
		||||
            this.storeCurrentTabIndex(this.tabsList.length);
 | 
			
		||||
            this.storeCurrentTabIndexInURL(this.tabsList.length);
 | 
			
		||||
        },
 | 
			
		||||
        dragstart(e) {
 | 
			
		||||
            if (e.dataTransfer.types.includes('openmct/domain-object-path')) {
 | 
			
		||||
@@ -177,8 +247,19 @@ export default {
 | 
			
		||||
        updateInternalDomainObject(domainObject) {
 | 
			
		||||
            this.internalDomainObject = domainObject;
 | 
			
		||||
        },
 | 
			
		||||
        storeCurrentTabIndex(index) {
 | 
			
		||||
        persistCurrentTabIndex(index) {
 | 
			
		||||
            this.openmct.objects.mutate(this.internalDomainObject, 'currentTabIndex', index);
 | 
			
		||||
        },
 | 
			
		||||
        storeCurrentTabIndexInURL(index) {
 | 
			
		||||
            let currentTabIndexInURL = getSearchParam(this.searchTabKey);
 | 
			
		||||
 | 
			
		||||
            if (index !== currentTabIndexInURL) {
 | 
			
		||||
                setSearchParam(this.searchTabKey, index);
 | 
			
		||||
                this.currentTabIndex = index;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        clearCurrentTabIndexFromURL() {
 | 
			
		||||
            deleteSearchParam(this.searchTabKey);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -42,20 +42,28 @@ define([
 | 
			
		||||
                let component;
 | 
			
		||||
 | 
			
		||||
                return {
 | 
			
		||||
                    show: function (element) {
 | 
			
		||||
                    show: function (element, editMode) {
 | 
			
		||||
                        component =  new Vue({
 | 
			
		||||
                            el: element,
 | 
			
		||||
                            components: {
 | 
			
		||||
                                TabsComponent: TabsComponent.default
 | 
			
		||||
                            },
 | 
			
		||||
                            data() {
 | 
			
		||||
                                return {
 | 
			
		||||
                                    isEditing: editMode
 | 
			
		||||
                                };
 | 
			
		||||
                            },
 | 
			
		||||
                            provide: {
 | 
			
		||||
                                openmct,
 | 
			
		||||
                                domainObject,
 | 
			
		||||
                                composition: openmct.composition.get(domainObject)
 | 
			
		||||
                            },
 | 
			
		||||
                            template: '<tabs-component></tabs-component>'
 | 
			
		||||
                            template: '<tabs-component :isEditing="isEditing"></tabs-component>'
 | 
			
		||||
                        });
 | 
			
		||||
                    },
 | 
			
		||||
                    onEditModeChange(editMode) {
 | 
			
		||||
                        component.isEditing = editMode;
 | 
			
		||||
                    },
 | 
			
		||||
                    destroy: function (element) {
 | 
			
		||||
                        component.$destroy();
 | 
			
		||||
                        component = undefined;
 | 
			
		||||
 
 | 
			
		||||
@@ -74,6 +74,10 @@ define([], function () {
 | 
			
		||||
            return this.cellLimitClasses;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getContextualDomainObject(openmct, objectKeyString) {
 | 
			
		||||
            return openmct.objects.get(objectKeyString);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        getContextMenuActions() {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -46,11 +46,23 @@ define(
 | 
			
		||||
                filter = filter.trim().toLowerCase();
 | 
			
		||||
 | 
			
		||||
                let rowsToFilter = this.getRowsToFilter(columnKey, filter);
 | 
			
		||||
 | 
			
		||||
                if (filter.length === 0) {
 | 
			
		||||
                    delete this.columnFilters[columnKey];
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.columnFilters[columnKey] = filter;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.rows = rowsToFilter.filter(this.matchesFilters, this);
 | 
			
		||||
                this.emit('filter');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            setColumnRegexFilter(columnKey, filter) {
 | 
			
		||||
                filter = filter.trim();
 | 
			
		||||
 | 
			
		||||
                let rowsToFilter = this.masterCollection.getRows();
 | 
			
		||||
 | 
			
		||||
                this.columnFilters[columnKey] = new RegExp(filter);
 | 
			
		||||
                this.rows = rowsToFilter.filter(this.matchesFilters, this);
 | 
			
		||||
                this.emit('filter');
 | 
			
		||||
            }
 | 
			
		||||
@@ -70,6 +82,10 @@ define(
 | 
			
		||||
             * @private
 | 
			
		||||
             */
 | 
			
		||||
            isSubsetOfCurrentFilter(columnKey, filter) {
 | 
			
		||||
                if (this.columnFilters[columnKey] instanceof RegExp) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return this.columnFilters[columnKey] &&
 | 
			
		||||
                    filter.startsWith(this.columnFilters[columnKey]) &&
 | 
			
		||||
                    // startsWith check will otherwise fail when filter cleared
 | 
			
		||||
@@ -96,7 +112,11 @@ define(
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    doesMatchFilters = formattedValue.toLowerCase().indexOf(this.columnFilters[key]) !== -1;
 | 
			
		||||
                    if (this.columnFilters[key] instanceof RegExp) {
 | 
			
		||||
                        doesMatchFilters = this.columnFilters[key].test(formattedValue);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        doesMatchFilters = formattedValue.toLowerCase().indexOf(this.columnFilters[key]) !== -1;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                return doesMatchFilters;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										56
									
								
								src/plugins/telemetryTable/components/sizing-row.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/plugins/telemetryTable/components/sizing-row.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
<template>
 | 
			
		||||
<tr class="c-telemetry-table__sizing-tr"><td>SIZING ROW</td></tr>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
    props: {
 | 
			
		||||
        isEditing: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            default: false
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        isEditing: function (isEditing) {
 | 
			
		||||
            if (isEditing) {
 | 
			
		||||
                this.pollForRowHeight();
 | 
			
		||||
            } else {
 | 
			
		||||
                this.clearPoll();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.$nextTick().then(() => {
 | 
			
		||||
            this.height = this.$el.offsetHeight;
 | 
			
		||||
            this.$emit('change-height', this.height);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (this.isEditing) {
 | 
			
		||||
            this.pollForRowHeight();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
        this.clearPoll();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        pollForRowHeight() {
 | 
			
		||||
            this.clearPoll();
 | 
			
		||||
            this.pollID = window.setInterval(this.heightPoll, 300);
 | 
			
		||||
        },
 | 
			
		||||
        clearPoll() {
 | 
			
		||||
            if (this.pollID) {
 | 
			
		||||
                window.clearInterval(this.pollID);
 | 
			
		||||
                this.pollID = undefined;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        heightPoll() {
 | 
			
		||||
            let height = this.$el.offsetHeight;
 | 
			
		||||
 | 
			
		||||
            if (height !== this.height) {
 | 
			
		||||
                this.$emit('change-height', height);
 | 
			
		||||
                this.height = height;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
@@ -171,7 +171,7 @@ export default {
 | 
			
		||||
        showContextMenu: function (event) {
 | 
			
		||||
            event.preventDefault();
 | 
			
		||||
 | 
			
		||||
            this.openmct.objects.get(this.row.objectKeyString).then((domainObject) => {
 | 
			
		||||
            this.row.getContextualDomainObject(this.openmct, this.row.objectKeyString).then(domainObject => {
 | 
			
		||||
                let contextualObjectPath = this.objectPath.slice();
 | 
			
		||||
                contextualObjectPath.unshift(domainObject);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,9 @@
 | 
			
		||||
 | 
			
		||||
.c-telemetry-table {
 | 
			
		||||
    // Table that displays telemetry in a scrolling body area
 | 
			
		||||
 | 
			
		||||
    @include fontAndSize();
 | 
			
		||||
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-flow: column nowrap;
 | 
			
		||||
    justify-content: flex-start;
 | 
			
		||||
@@ -54,6 +57,16 @@
 | 
			
		||||
                width: 100%;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &__filter {
 | 
			
		||||
            .c-table__search {
 | 
			
		||||
                padding-top: 0;
 | 
			
		||||
                padding-bottom: 0;
 | 
			
		||||
            }
 | 
			
		||||
            .is-in-small-container & {
 | 
			
		||||
                display: none;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__headers__label {
 | 
			
		||||
@@ -86,6 +99,10 @@
 | 
			
		||||
        height: 0; // Fixes Chrome 73 overflow bug
 | 
			
		||||
        overflow-x: auto;
 | 
			
		||||
        overflow-y: scroll;
 | 
			
		||||
 | 
			
		||||
        .is-editing & {
 | 
			
		||||
            pointer-events: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /******************************* TABLES */
 | 
			
		||||
@@ -98,7 +115,7 @@
 | 
			
		||||
            display: flex; // flex-flow defaults to row nowrap (which is what we want) so no need to define
 | 
			
		||||
            align-items: stretch;
 | 
			
		||||
            position: absolute;
 | 
			
		||||
            height: 18px; // Needed when a row has empty values in its cells
 | 
			
		||||
            min-height: 18px; // Needed when a row has empty values in its cells
 | 
			
		||||
 | 
			
		||||
            &.is-selected {
 | 
			
		||||
                background-color: $colorSelectedBg !important;
 | 
			
		||||
@@ -136,9 +153,15 @@
 | 
			
		||||
            white-space: nowrap;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__sizing-tr {
 | 
			
		||||
        // A row element used to determine sizing of rows based on font size
 | 
			
		||||
        visibility: hidden;
 | 
			
		||||
        pointer-events: none;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************* EDITING */
 | 
			
		||||
/******************************* SPECIFIC CASE WRAPPERS */
 | 
			
		||||
.is-editing {
 | 
			
		||||
    .c-telemetry-table__headers__labels {
 | 
			
		||||
        th[draggable],
 | 
			
		||||
@@ -158,8 +181,10 @@
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.paused {
 | 
			
		||||
    border: 1px solid #ff9900;
 | 
			
		||||
.is-paused {
 | 
			
		||||
    .c-table__body-w {
 | 
			
		||||
        border: 1px solid rgba($colorPausedBg, 0.8);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************* LEGACY */
 | 
			
		||||
 
 | 
			
		||||
@@ -20,13 +20,16 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
<template>
 | 
			
		||||
<div class="c-table-wrapper">
 | 
			
		||||
<div class="c-table-wrapper"
 | 
			
		||||
     :class="{ 'is-paused': paused }"
 | 
			
		||||
>
 | 
			
		||||
    <!-- main contolbar  start-->
 | 
			
		||||
    <div v-if="!marking.useAlternateControlBar"
 | 
			
		||||
         class="c-table-control-bar c-control-bar"
 | 
			
		||||
    >
 | 
			
		||||
        <button
 | 
			
		||||
            v-if="allowExport"
 | 
			
		||||
            v-show="!markedRows.length"
 | 
			
		||||
            class="c-button icon-download labeled"
 | 
			
		||||
            title="Export this view's data"
 | 
			
		||||
            @click="exportAllDataAsCSV()"
 | 
			
		||||
@@ -122,10 +125,10 @@
 | 
			
		||||
    <!-- alternate controlbar end  -->
 | 
			
		||||
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
 | 
			
		||||
        class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar u-style-receiver js-style-receiver"
 | 
			
		||||
        :class="{
 | 
			
		||||
            'loading': loading,
 | 
			
		||||
            'paused' : paused
 | 
			
		||||
            'is-paused' : paused
 | 
			
		||||
        }"
 | 
			
		||||
    >
 | 
			
		||||
        <div :style="{ 'max-width': widthWithScroll, 'min-width': '150px'}">
 | 
			
		||||
@@ -187,7 +190,17 @@
 | 
			
		||||
                                class="c-table__search"
 | 
			
		||||
                                @input="filterChanged(key)"
 | 
			
		||||
                                @clear="clearFilter(key)"
 | 
			
		||||
                            />
 | 
			
		||||
                            >
 | 
			
		||||
 | 
			
		||||
                                <button
 | 
			
		||||
                                    class="c-search__use-regex"
 | 
			
		||||
                                    :class="{ 'is-active': enableRegexSearch[key] }"
 | 
			
		||||
                                    title="Click to enable regex: enter a string with slashes, like this: /regex_exp/"
 | 
			
		||||
                                    @click="toggleRegex(key)"
 | 
			
		||||
                                >
 | 
			
		||||
                                    /R/
 | 
			
		||||
                                </button>
 | 
			
		||||
                            </search>
 | 
			
		||||
                        </table-column-header>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                </thead>
 | 
			
		||||
@@ -231,6 +244,10 @@
 | 
			
		||||
            class="c-telemetry-table__sizing js-telemetry-table__sizing"
 | 
			
		||||
            :style="sizingTableWidth"
 | 
			
		||||
        >
 | 
			
		||||
            <sizing-row
 | 
			
		||||
                :is-editing="isEditing"
 | 
			
		||||
                @change-height="setRowHeight"
 | 
			
		||||
            />
 | 
			
		||||
            <tr>
 | 
			
		||||
                <template v-for="(title, key) in headers">
 | 
			
		||||
                    <th
 | 
			
		||||
@@ -263,6 +280,7 @@ import TelemetryFilterIndicator from './TelemetryFilterIndicator.vue';
 | 
			
		||||
import CSVExporter from '../../../exporters/CSVExporter.js';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
 | 
			
		||||
import SizingRow from './sizing-row.vue';
 | 
			
		||||
 | 
			
		||||
const VISIBLE_ROW_COUNT = 100;
 | 
			
		||||
const ROW_HEIGHT = 17;
 | 
			
		||||
@@ -275,7 +293,8 @@ export default {
 | 
			
		||||
        TableColumnHeader,
 | 
			
		||||
        search,
 | 
			
		||||
        TelemetryFilterIndicator,
 | 
			
		||||
        ToggleSwitch
 | 
			
		||||
        ToggleSwitch,
 | 
			
		||||
        SizingRow
 | 
			
		||||
    },
 | 
			
		||||
    inject: ['table', 'openmct', 'objectPath'],
 | 
			
		||||
    props: {
 | 
			
		||||
@@ -338,7 +357,8 @@ export default {
 | 
			
		||||
            paused: false,
 | 
			
		||||
            markedRows: [],
 | 
			
		||||
            isShowingMarkedRowsOnly: false,
 | 
			
		||||
            hideHeaders: configuration.hideHeaders
 | 
			
		||||
            hideHeaders: configuration.hideHeaders,
 | 
			
		||||
            enableRegexSearch: {}
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
@@ -490,7 +510,7 @@ export default {
 | 
			
		||||
            let columnWidths = {},
 | 
			
		||||
                totalWidth = 0,
 | 
			
		||||
                headerKeys = Object.keys(this.headers),
 | 
			
		||||
                sizingTableRow = this.sizingTable.children[0],
 | 
			
		||||
                sizingTableRow = this.sizingTable.children[1],
 | 
			
		||||
                sizingCells = sizingTableRow.children;
 | 
			
		||||
 | 
			
		||||
            headerKeys.forEach((headerKey, headerIndex, array)=>{
 | 
			
		||||
@@ -547,6 +567,17 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        filterChanged(columnKey) {
 | 
			
		||||
            this.table.filteredRows.setColumnFilter(columnKey, this.filters[columnKey]);
 | 
			
		||||
 | 
			
		||||
            if (this.enableRegexSearch[columnKey]) {
 | 
			
		||||
                if (this.isCompleteRegex(this.filters[columnKey])) {
 | 
			
		||||
                    this.table.filteredRows.setColumnRegexFilter(columnKey, this.filters[columnKey].slice(1,-1));
 | 
			
		||||
                } else {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                this.table.filteredRows.setColumnFilter(columnKey, this.filters[columnKey]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.setHeight();
 | 
			
		||||
        },
 | 
			
		||||
        clearFilter(columnKey) {
 | 
			
		||||
@@ -873,6 +904,23 @@ export default {
 | 
			
		||||
            this.isAutosizeEnabled = true;
 | 
			
		||||
 | 
			
		||||
            this.$nextTick().then(this.calculateColumnWidths);
 | 
			
		||||
        },
 | 
			
		||||
        setRowHeight(height) {
 | 
			
		||||
            this.rowHeight = height;
 | 
			
		||||
            this.setHeight();
 | 
			
		||||
            this.calculateTableSize();
 | 
			
		||||
            this.clearRowsAndRerender();
 | 
			
		||||
        },
 | 
			
		||||
        toggleRegex(key) {
 | 
			
		||||
            this.$set(this.filters, key, '');
 | 
			
		||||
            if (this.enableRegexSearch[key] === undefined) {
 | 
			
		||||
                this.$set(this.enableRegexSearch, key, true)
 | 
			
		||||
            } else {
 | 
			
		||||
                this.$set(this.enableRegexSearch, key, !this.enableRegexSearch[key]);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        isCompleteRegex(string) {
 | 
			
		||||
            return (string.length > 2 && string[0] === '/' && string[string.length - 1] === '/')
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -143,7 +143,7 @@
 | 
			
		||||
            <ConductorHistory
 | 
			
		||||
                v-if="isFixed"
 | 
			
		||||
                class="c-conductor__history-select"
 | 
			
		||||
                :bounds="openmct.time.bounds()"
 | 
			
		||||
                :bounds="bounds"
 | 
			
		||||
                :time-system="timeSystem"
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -190,6 +190,10 @@ export default {
 | 
			
		||||
                start: offsets && durationFormatter.format(Math.abs(offsets.start)),
 | 
			
		||||
                end: offsets && durationFormatter.format(Math.abs(offsets.end))
 | 
			
		||||
            },
 | 
			
		||||
            bounds: {
 | 
			
		||||
                start: bounds.start,
 | 
			
		||||
                end: bounds.end
 | 
			
		||||
            },
 | 
			
		||||
            formattedBounds: {
 | 
			
		||||
                start: timeFormatter.format(bounds.start),
 | 
			
		||||
                end: timeFormatter.format(bounds.end)
 | 
			
		||||
@@ -210,7 +214,7 @@ export default {
 | 
			
		||||
        document.addEventListener('keydown', this.handleKeyDown);
 | 
			
		||||
        document.addEventListener('keyup', this.handleKeyUp);
 | 
			
		||||
        this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.timeSystem())));
 | 
			
		||||
        this.openmct.time.on('bounds', this.setViewFromBounds);
 | 
			
		||||
        this.openmct.time.on('bounds', this.handleNewBounds);
 | 
			
		||||
        this.openmct.time.on('timeSystem', this.setTimeSystem);
 | 
			
		||||
        this.openmct.time.on('clock', this.setViewFromClock);
 | 
			
		||||
        this.openmct.time.on('clockOffsets', this.setViewFromOffsets)
 | 
			
		||||
@@ -220,6 +224,13 @@ export default {
 | 
			
		||||
        document.removeEventListener('keyup', this.handleKeyUp);
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        handleNewBounds(bounds) {
 | 
			
		||||
            this.setBounds(bounds);
 | 
			
		||||
            this.setViewFromBounds(bounds);
 | 
			
		||||
        },
 | 
			
		||||
        setBounds(bounds) {
 | 
			
		||||
            this.bounds = bounds;
 | 
			
		||||
        },
 | 
			
		||||
        handleKeyDown(event) {
 | 
			
		||||
            if (event.key === 'Alt') {
 | 
			
		||||
                this.altPressed = true;
 | 
			
		||||
@@ -246,10 +257,13 @@ export default {
 | 
			
		||||
            this.formattedBounds.end = this.timeFormatter.format(bounds.end);
 | 
			
		||||
        },
 | 
			
		||||
        endZoom(bounds) {
 | 
			
		||||
            const _bounds = bounds ? bounds : this.openmct.time.bounds();
 | 
			
		||||
            this.isZooming = false;
 | 
			
		||||
 | 
			
		||||
            this.openmct.time.bounds(_bounds);
 | 
			
		||||
            if (bounds) {
 | 
			
		||||
                this.handleNewBounds(bounds);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.setViewFromBounds(this.bounds);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        setTimeSystem(timeSystem) {
 | 
			
		||||
            this.timeSystem = timeSystem
 | 
			
		||||
 
 | 
			
		||||
@@ -207,7 +207,7 @@ export default {
 | 
			
		||||
            this.$emit('panAxis', panBounds);
 | 
			
		||||
        },
 | 
			
		||||
        endPan() {
 | 
			
		||||
            const panBounds = this.dragStartX && this.dragX && this.dragStartX !== this.dragX
 | 
			
		||||
            const panBounds = this.isChangingViewBounds()
 | 
			
		||||
                ? this.getPanBounds()
 | 
			
		||||
                : undefined;
 | 
			
		||||
            this.$emit('endPan', panBounds);
 | 
			
		||||
@@ -251,16 +251,14 @@ export default {
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        endZoom() {
 | 
			
		||||
            const zoomRange = this.dragStartX && this.dragX && this.dragStartX !== this.dragX
 | 
			
		||||
                ? this.getZoomRange()
 | 
			
		||||
                : undefined;
 | 
			
		||||
 | 
			
		||||
            const zoomBounds = zoomRange
 | 
			
		||||
                ? {
 | 
			
		||||
            let zoomBounds;
 | 
			
		||||
            if (this.isChangingViewBounds()) {
 | 
			
		||||
                const zoomRange = this.getZoomRange();
 | 
			
		||||
                zoomBounds = {
 | 
			
		||||
                    start: this.scaleToBounds(zoomRange.start),
 | 
			
		||||
                    end: this.scaleToBounds(zoomRange.end)
 | 
			
		||||
                }
 | 
			
		||||
                : this.openmct.time.bounds();
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.zoomStyle = {};
 | 
			
		||||
            this.$emit('endZoom', zoomBounds);
 | 
			
		||||
@@ -289,6 +287,9 @@ export default {
 | 
			
		||||
            const offset = valueDelta / this.width * timeDelta;
 | 
			
		||||
            return bounds.start + offset;
 | 
			
		||||
        },
 | 
			
		||||
        isChangingViewBounds() {
 | 
			
		||||
            return this.dragStartX && this.dragX && this.dragStartX !== this.dragX;
 | 
			
		||||
        },
 | 
			
		||||
        resize() {
 | 
			
		||||
            if (this.$refs.axisHolder.clientWidth !== this.width) {
 | 
			
		||||
                this.setAxisDimensions();
 | 
			
		||||
 
 | 
			
		||||
@@ -84,7 +84,12 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            history: {}, // contains arrays of timespans {start, end}, array key is time system key
 | 
			
		||||
            /**
 | 
			
		||||
             * previous bounds entries available for easy re-use
 | 
			
		||||
             * @history array of timespans
 | 
			
		||||
             * @timespans {start, end} number representing timestamp
 | 
			
		||||
             */
 | 
			
		||||
            history: this.getHistoryFromLocalStorage(),
 | 
			
		||||
            presets: []
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -111,22 +116,20 @@ export default {
 | 
			
		||||
                this.addTimespan();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        },
 | 
			
		||||
        history: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.persistHistoryToLocalStorage();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.getHistoryFromLocalStorage();
 | 
			
		||||
        this.initializeHistoryIfNoHistory();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        getHistoryFromLocalStorage() {
 | 
			
		||||
            if (localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY)) {
 | 
			
		||||
                this.history = JSON.parse(localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY))
 | 
			
		||||
            } else {
 | 
			
		||||
            const localStorageHistory = localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY);
 | 
			
		||||
            const history = localStorageHistory ? JSON.parse(localStorageHistory) : undefined;
 | 
			
		||||
 | 
			
		||||
            return history;
 | 
			
		||||
        },
 | 
			
		||||
        initializeHistoryIfNoHistory() {
 | 
			
		||||
            if (!this.history) {
 | 
			
		||||
                this.history = {};
 | 
			
		||||
                this.persistHistoryToLocalStorage();
 | 
			
		||||
            }
 | 
			
		||||
@@ -156,6 +159,8 @@ export default {
 | 
			
		||||
 | 
			
		||||
            currentHistory.unshift(timespan);
 | 
			
		||||
            this.history[key] = currentHistory;
 | 
			
		||||
 | 
			
		||||
            this.persistHistoryToLocalStorage();
 | 
			
		||||
        },
 | 
			
		||||
        selectTimespan(timespan) {
 | 
			
		||||
            this.openmct.time.bounds(timespan);
 | 
			
		||||
 
 | 
			
		||||
@@ -84,6 +84,10 @@ $filterHov: brightness(1.3); // Tree, location items
 | 
			
		||||
$colorSelectedBg: pushBack($colorKey, 10%);
 | 
			
		||||
$colorSelectedFg: pullForward($colorBodyFg, 20%);
 | 
			
		||||
 | 
			
		||||
// Object labels
 | 
			
		||||
$objectLabelTypeIconOpacity: 0.7;
 | 
			
		||||
$objectLabelNameFilter: brightness(1.3);
 | 
			
		||||
 | 
			
		||||
// Layout
 | 
			
		||||
$shellMainPad: 4px 0;
 | 
			
		||||
$shellPanePad: $interiorMargin, 7px;
 | 
			
		||||
@@ -94,7 +98,7 @@ $sideBarHeaderBg: rgba($colorBodyFg, 0.2);
 | 
			
		||||
$sideBarHeaderFg: rgba($colorBodyFg, 0.7);
 | 
			
		||||
 | 
			
		||||
// Status colors, mainly used for messaging and item ancillary symbols
 | 
			
		||||
$colorStatusFg: #999;
 | 
			
		||||
$colorStatusFg: #888;
 | 
			
		||||
$colorStatusDefault: #ccc;
 | 
			
		||||
$colorStatusInfo: #60ba7b;
 | 
			
		||||
$colorStatusInfoFilter: invert(58%) sepia(44%) saturate(405%) hue-rotate(85deg) brightness(102%) contrast(92%);
 | 
			
		||||
@@ -151,6 +155,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
 | 
			
		||||
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
 | 
			
		||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
 | 
			
		||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
 | 
			
		||||
$filterItemHoverFg: brightness(1.2) contrast(1.1);
 | 
			
		||||
$filterItemMissing: brightness(0.6) grayscale(1);
 | 
			
		||||
$opacityMissing: 0.5;
 | 
			
		||||
$borderMissing: 1px dashed $colorAlert !important;
 | 
			
		||||
 | 
			
		||||
/************************************************** EDITING */
 | 
			
		||||
$editUIColor: $uiColor; // Base color
 | 
			
		||||
@@ -201,9 +209,9 @@ $colorBtnMajorBg: $colorKey;
 | 
			
		||||
$colorBtnMajorBgHov: $colorKeyHov;
 | 
			
		||||
$colorBtnMajorFg: $colorKeyFg;
 | 
			
		||||
$colorBtnMajorFgHov: pushBack($colorBtnMajorFg, 10%);
 | 
			
		||||
$colorBtnCautionBg: #f16f6f;
 | 
			
		||||
$colorBtnCautionBg: $colorStatusAlert;
 | 
			
		||||
$colorBtnCautionBgHov: #f1504e;
 | 
			
		||||
$colorBtnCautionFg: $colorBtnFg;
 | 
			
		||||
$colorBtnCautionFg: $colorBtnBg;
 | 
			
		||||
$colorBtnActiveBg: $colorOk;
 | 
			
		||||
$colorBtnActiveFg: $colorOkFg;
 | 
			
		||||
$colorBtnSelectedBg: $colorSelectedBg;
 | 
			
		||||
@@ -331,7 +339,7 @@ $shdwItemText: none;
 | 
			
		||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
 | 
			
		||||
$colorTabBodyBg: $colorBodyBg;
 | 
			
		||||
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
 | 
			
		||||
$colorTabHeaderBg: rgba($colorBodyFg, 0.2);
 | 
			
		||||
$colorTabHeaderBg: rgba($colorBodyFg, 0.15);
 | 
			
		||||
$colorTabHeaderFg: $colorBodyFg;
 | 
			
		||||
$colorTabHeaderBorder: $colorBodyBg;
 | 
			
		||||
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
 | 
			
		||||
@@ -349,7 +357,7 @@ $stylePlotHash: dashed;
 | 
			
		||||
$colorPlotAreaBorder: $colorInteriorBorder;
 | 
			
		||||
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
 | 
			
		||||
$legendHoverValueBg: rgba($colorBodyFg, 0.2);
 | 
			
		||||
$legendTableHeadBg: rgba($colorBodyFg, 0.15);
 | 
			
		||||
$legendTableHeadBg: $colorTabHeaderBg;
 | 
			
		||||
 | 
			
		||||
// Tree
 | 
			
		||||
$colorTreeBg: transparent;
 | 
			
		||||
 
 | 
			
		||||
@@ -88,6 +88,10 @@ $filterHov: brightness(1.3); // Tree, location items
 | 
			
		||||
$colorSelectedBg: pushBack($colorKey, 10%);
 | 
			
		||||
$colorSelectedFg: pullForward($colorBodyFg, 20%);
 | 
			
		||||
 | 
			
		||||
// Object labels
 | 
			
		||||
$objectLabelTypeIconOpacity: 0.7;
 | 
			
		||||
$objectLabelNameFilter: brightness(1.3);
 | 
			
		||||
 | 
			
		||||
// Layout
 | 
			
		||||
$shellMainPad: 4px 0;
 | 
			
		||||
$shellPanePad: $interiorMargin, 7px;
 | 
			
		||||
@@ -155,6 +159,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
 | 
			
		||||
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
 | 
			
		||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
 | 
			
		||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
 | 
			
		||||
$filterItemHoverFg: brightness(1.2) contrast(1.1);
 | 
			
		||||
$filterItemMissing: contrast(0.2);
 | 
			
		||||
$opacityMissing: 0.5;
 | 
			
		||||
$borderMissing: 1px dashed $colorAlert !important;
 | 
			
		||||
 | 
			
		||||
/************************************************** EDITING */
 | 
			
		||||
$editUIColor: $uiColor; // Base color
 | 
			
		||||
@@ -205,9 +213,9 @@ $colorBtnMajorBg: $colorKey;
 | 
			
		||||
$colorBtnMajorBgHov: $colorKeyHov;
 | 
			
		||||
$colorBtnMajorFg: $colorKeyFg;
 | 
			
		||||
$colorBtnMajorFgHov: pushBack($colorBtnMajorFg, 10%);
 | 
			
		||||
$colorBtnCautionBg: #f16f6f;
 | 
			
		||||
$colorBtnCautionBg: $colorStatusAlert;
 | 
			
		||||
$colorBtnCautionBgHov: #f1504e;
 | 
			
		||||
$colorBtnCautionFg: $colorBtnFg;
 | 
			
		||||
$colorBtnCautionFg: $colorBtnBg;
 | 
			
		||||
$colorBtnActiveBg: $colorOk;
 | 
			
		||||
$colorBtnActiveFg: $colorOkFg;
 | 
			
		||||
$colorBtnSelectedBg: $colorSelectedBg;
 | 
			
		||||
@@ -335,7 +343,7 @@ $shdwItemText: none;
 | 
			
		||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
 | 
			
		||||
$colorTabBodyBg: $colorBodyBg;
 | 
			
		||||
$colorTabBodyFg: pullForward($colorBodyFg, 20%);
 | 
			
		||||
$colorTabHeaderBg: rgba($colorBodyFg, 0.2);
 | 
			
		||||
$colorTabHeaderBg: rgba($colorBodyFg, 0.15);
 | 
			
		||||
$colorTabHeaderFg: $colorBodyFg;
 | 
			
		||||
$colorTabHeaderBorder: $colorBodyBg;
 | 
			
		||||
$colorTabGroupHeaderBg: pullForward($colorBodyBg, 5%);
 | 
			
		||||
 
 | 
			
		||||
@@ -80,10 +80,14 @@ $uiColor: #289fec; // Resize bars, splitter bars, etc.
 | 
			
		||||
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
 | 
			
		||||
$colorA: #999;
 | 
			
		||||
$colorAHov: $colorKey;
 | 
			
		||||
$filterHov: brightness(1.3); // Tree, location items
 | 
			
		||||
$filterHov: brightness(0.8) contrast(2); // Tree, location items
 | 
			
		||||
$colorSelectedBg: pushBack($colorKey, 40%);
 | 
			
		||||
$colorSelectedFg: pullForward($colorBodyFg, 10%);
 | 
			
		||||
 | 
			
		||||
// Object labels
 | 
			
		||||
$objectLabelTypeIconOpacity: 0.5;
 | 
			
		||||
$objectLabelNameFilter: brightness(0.5);
 | 
			
		||||
 | 
			
		||||
// Layout
 | 
			
		||||
$shellMainPad: 4px 0;
 | 
			
		||||
$shellPanePad: $interiorMargin, 7px;
 | 
			
		||||
@@ -151,6 +155,10 @@ $browseFrameColor: pullForward($colorBodyBg, 10%);
 | 
			
		||||
$browseFrameBorder: 1px solid $browseFrameColor; // Frames in Disp and Flex Layouts when frame is showing
 | 
			
		||||
$browseSelectableShdwHov: rgba($colorBodyFg, 0.3) 0 0 3px;
 | 
			
		||||
$browseSelectedBorder: 1px solid rgba($colorBodyFg, 0.4);
 | 
			
		||||
$filterItemHoverFg: brightness(0.9);
 | 
			
		||||
$filterItemMissing: contrast(0.2);
 | 
			
		||||
$opacityMissing: 0.4;
 | 
			
		||||
$borderMissing: 1px dashed $colorAlert !important;
 | 
			
		||||
 | 
			
		||||
/************************************************** EDITING */
 | 
			
		||||
$editUIColor: $uiColor; // Base color
 | 
			
		||||
@@ -220,7 +228,7 @@ $colorDisclosureCtrlHov: rgba($colorBodyFg, 0.7);
 | 
			
		||||
$btnStdH: 24px;
 | 
			
		||||
$colorCursorGuide: rgba(black, 0.6);
 | 
			
		||||
$shdwCursorGuide: rgba(white, 0.4) 0 0 2px;
 | 
			
		||||
$colorLocalControlOvrBg: rgba($colorBodyFg, 0.8);
 | 
			
		||||
$colorLocalControlOvrBg: rgba($colorBodyBg, 0.8);
 | 
			
		||||
$colorSelectBg: $colorBtnBg; // This must be a solid color, not a gradient, due to usage of SVG bg in selects
 | 
			
		||||
$colorSelectFg: $colorBtnFg;
 | 
			
		||||
$colorSelectArw: lighten($colorBtnBg, 20%);
 | 
			
		||||
 
 | 
			
		||||
@@ -40,11 +40,10 @@ $inputTextP: $inputTextPTopBtm $inputTextPLeftRight;
 | 
			
		||||
$menuLineH: 1.5rem;
 | 
			
		||||
$treeItemIndent: 16px;
 | 
			
		||||
$treeTypeIconW: 18px;
 | 
			
		||||
$overlayOuterMarginLg: 5%;
 | 
			
		||||
$overlayOuterMarginFullscreen: 0%;
 | 
			
		||||
$overlayOuterMarginDialog: 20%;
 | 
			
		||||
$overlayInnerMargin: 25px;
 | 
			
		||||
$mainViewPad: 2px;
 | 
			
		||||
$mainViewPad: 0px;
 | 
			
		||||
/*************** Items */
 | 
			
		||||
$itemPadLR: 5px;
 | 
			
		||||
$gridItemDesk: 175px;
 | 
			
		||||
@@ -57,11 +56,11 @@ $tabularTdPadTB: 2px;
 | 
			
		||||
$plotYBarW: 60px;
 | 
			
		||||
$plotYLabelMinH: 20px;
 | 
			
		||||
$plotYLabelW: 10px;
 | 
			
		||||
$plotXBarH: 35px;
 | 
			
		||||
$plotXBarH: 32px;
 | 
			
		||||
$plotLegendH: 20px;
 | 
			
		||||
$plotLegendWidthCollapsed: 20%;
 | 
			
		||||
$plotLegendWidthExpanded: 50%;
 | 
			
		||||
$plotSwatchD: 10px;
 | 
			
		||||
$plotSwatchD: 12px;
 | 
			
		||||
$plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW); // 1: Top, 2: right, 3: bottom, 4: left
 | 
			
		||||
$plotMinH: 95px;
 | 
			
		||||
$controlBarH: 25px;
 | 
			
		||||
@@ -90,6 +89,8 @@ $messageIconD: 80px;
 | 
			
		||||
$messageListIconD: 32px;
 | 
			
		||||
/*************** Tables */
 | 
			
		||||
$tableResizeColHitareaD: 6px;
 | 
			
		||||
/*************** Misc */
 | 
			
		||||
$drawingObjBorderW: 3px;
 | 
			
		||||
 | 
			
		||||
/************************** MOBILE */
 | 
			
		||||
$mobileMenuIconD: 24px; // Used
 | 
			
		||||
@@ -101,6 +102,8 @@ $colorProgressBarHolder: rgba(black, 0.1);
 | 
			
		||||
$colorProgressBar: #0085ad;
 | 
			
		||||
$progressAnimW: 500px;
 | 
			
		||||
$progressBarMinH: 6px;
 | 
			
		||||
/************************** FONT STYLING */
 | 
			
		||||
$listFontSizes: 8,9,10,11,12,13,14,16,18,20,24,28,32,36,42,48,72,96,128;
 | 
			
		||||
 | 
			
		||||
/************************** GLYPH CHAR UNICODES */
 | 
			
		||||
$glyph-icon-alert-rect: '\e900';
 | 
			
		||||
 
 | 
			
		||||
@@ -57,11 +57,6 @@ button {
 | 
			
		||||
        line-height: 90%;
 | 
			
		||||
        padding: 3px 10px;
 | 
			
		||||
 | 
			
		||||
        @include hover() {
 | 
			
		||||
            background: $colorBtnBgHov;
 | 
			
		||||
            color: $colorBtnFgHov;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @include desktop() {
 | 
			
		||||
            font-size: 6px;
 | 
			
		||||
        }
 | 
			
		||||
@@ -411,7 +406,17 @@ select {
 | 
			
		||||
 | 
			
		||||
.c-tab {
 | 
			
		||||
    // Used in Tab View, generic tabs
 | 
			
		||||
    background: $colorBtnBg;
 | 
			
		||||
    $notchSize: 7px;
 | 
			
		||||
    $clipPath:
 | 
			
		||||
        polygon(
 | 
			
		||||
            0% 0%,
 | 
			
		||||
            calc(100% - #{$notchSize}) 0%,
 | 
			
		||||
            100% #{$notchSize},
 | 
			
		||||
            100% calc(100% - #{$notchSize}),
 | 
			
		||||
            100% 100%,
 | 
			
		||||
            0% 100%
 | 
			
		||||
        );
 | 
			
		||||
    background: rgba($colorBtnBg, 0.7);
 | 
			
		||||
    color: $colorBtnFg;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    display: flex;
 | 
			
		||||
@@ -420,21 +425,15 @@ select {
 | 
			
		||||
    margin: 1px 1px 0 0;
 | 
			
		||||
    padding: $interiorMargin $interiorMarginLg;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
    clip-path: $clipPath;
 | 
			
		||||
    -webkit-clip-path: $clipPath; // Safari
 | 
			
		||||
 | 
			
		||||
    --notchSize: 7px;
 | 
			
		||||
 | 
			
		||||
    clip-path:
 | 
			
		||||
        polygon(
 | 
			
		||||
            0% 0%,
 | 
			
		||||
            calc(100% - var(--notchSize)) 0%,
 | 
			
		||||
            100% var(--notchSize),
 | 
			
		||||
            100% calc(100% - var(--notchSize)),
 | 
			
		||||
            100% 100%,
 | 
			
		||||
            0% 100%
 | 
			
		||||
        );
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-left: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @include hover() {
 | 
			
		||||
        background: $colorBtnBgHov;
 | 
			
		||||
        filter: $filterHov;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-current {
 | 
			
		||||
 
 | 
			
		||||
@@ -96,6 +96,37 @@ body.desktop {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** FONTS */
 | 
			
		||||
@mixin fontAndSize() {
 | 
			
		||||
    @each $size in $listFontSizes {
 | 
			
		||||
        &[data-font-size="#{$size}"] {
 | 
			
		||||
            font-size: #{$size}px;
 | 
			
		||||
 | 
			
		||||
            // Set row heights in telemetry tables
 | 
			
		||||
            tr {
 | 
			
		||||
                min-height: #{$size + ($tabularTdPadTB * 2)};
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &[data-font*="bold"] {
 | 
			
		||||
        font-weight: bold;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &[data-font*="narrow"] {
 | 
			
		||||
        font-family: 'Arial Narrow', sans-serif;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &[data-font*="monospace"] {
 | 
			
		||||
        font-family: 'Andale Mono', sans-serif;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.u-style-receiver {
 | 
			
		||||
    @include fontAndSize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** HTML ENTITIES */
 | 
			
		||||
a {
 | 
			
		||||
    color: $colorA;
 | 
			
		||||
@@ -206,10 +237,6 @@ body.desktop .has-local-controls {
 | 
			
		||||
    &:hover {
 | 
			
		||||
        box-shadow: $browseSelectableShdwHov;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &[s-selected] {
 | 
			
		||||
        border: $browseSelectedBorder;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**************************** EDITING */
 | 
			
		||||
@@ -230,7 +257,7 @@ body.desktop .has-local-controls {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** STATES */
 | 
			
		||||
@mixin  spinner($b: 5px, $c: $colorKey) {
 | 
			
		||||
@mixin  spinner($b: 5, $c: $colorKey) {
 | 
			
		||||
    animation-name: rotation-centered;
 | 
			
		||||
    animation-duration: 0.5s;
 | 
			
		||||
    animation-iteration-count: infinite;
 | 
			
		||||
@@ -280,13 +307,13 @@ body.desktop .has-local-controls {
 | 
			
		||||
    }
 | 
			
		||||
    &.c-tree__item {
 | 
			
		||||
        $d: $waitSpinnerTreeD;
 | 
			
		||||
        $spinnerL: 19px + $d/2;
 | 
			
		||||
        $spinnerL: 19 + $d/2;
 | 
			
		||||
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        padding-left: $spinnerL + $d/2 + $interiorMargin;
 | 
			
		||||
        background: $colorLoadingBg;
 | 
			
		||||
        min-height: 5px + $d;
 | 
			
		||||
        min-height: 5 + $d;
 | 
			
		||||
 | 
			
		||||
        .c-tree__item__label {
 | 
			
		||||
            font-style: italic;
 | 
			
		||||
@@ -295,7 +322,7 @@ body.desktop .has-local-controls {
 | 
			
		||||
        &:before {
 | 
			
		||||
            height: $d;
 | 
			
		||||
            width: $d;
 | 
			
		||||
            border-width: 4px;
 | 
			
		||||
            border-width: 4;
 | 
			
		||||
            left: $spinnerL;
 | 
			
		||||
        }
 | 
			
		||||
        &:after {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2018, United States Government
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2020, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
@@ -27,53 +27,65 @@ mct-plot {
 | 
			
		||||
/*********************** STACKED PLOT LAYOUT */
 | 
			
		||||
.is-editing {
 | 
			
		||||
    .gl-plot.child-frame {
 | 
			
		||||
        &:hover {
 | 
			
		||||
        @include hover {
 | 
			
		||||
            background: rgba($editUIColorBg, 0.1);
 | 
			
		||||
            box-shadow: inset rgba($editUIColorBg, 0.8) 0 0 0 1px;
 | 
			
		||||
            box-shadow: inset rgba($editUIColorBg, 0.3) 0 0 0 1px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &[s-selected] {
 | 
			
		||||
            border: 1px solid $editUIColorFg !important;
 | 
			
		||||
            color: $editUIColorFg !important;
 | 
			
		||||
            box-shadow: $editFrameSelectedShdw;
 | 
			
		||||
            background: rgba($editUIColorBg, 0.2);
 | 
			
		||||
            box-shadow: inset rgba($editUIColorBg, 0.8) 0 0 0 1px;
 | 
			
		||||
            z-index: 2;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .plot-wrapper-axis-and-display-area {
 | 
			
		||||
        pointer-events: none;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-plot,
 | 
			
		||||
.gl-plot {
 | 
			
		||||
    .s-status-taking-snapshot & {
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
 | 
			
		||||
        .s-status-taking-snapshot & {
 | 
			
		||||
        .c-control-bar {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
        .gl-plot-x-label__select,
 | 
			
		||||
        .gl-plot-y-label__select {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*********************** MISSING ITEM INDICATORS */
 | 
			
		||||
    .is-missing__indicator {
 | 
			
		||||
        display: none;
 | 
			
		||||
    }
 | 
			
		||||
    .is-missing {
 | 
			
		||||
        @include isMissing();
 | 
			
		||||
        .is-missing__indicator {
 | 
			
		||||
            font-size: 0.8em;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-plot {
 | 
			
		||||
    //$p: $mainViewPad;
 | 
			
		||||
    @include abs($mainViewPad);
 | 
			
		||||
    //position: absolute;
 | 
			
		||||
    //top: $p; right: $p; bottom: $p; left: $p;
 | 
			
		||||
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-top: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .l-control-bar {
 | 
			
		||||
    .c-control-bar {
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
        margin-bottom: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .l-view-section {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &--stacked {
 | 
			
		||||
@@ -100,20 +112,23 @@ mct-plot {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    .is-in-small-container & {
 | 
			
		||||
        .c-control-bar {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.gl-plot {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    min-height: $plotMinH;
 | 
			
		||||
    flex: 1 1 auto;
 | 
			
		||||
 | 
			
		||||
    /*********************** AXIS AND DISPLAY AREA */
 | 
			
		||||
    .plot-wrapper-axis-and-display-area {
 | 
			
		||||
        position: relative;
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        min-height: $plotMinH;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .gl-plot-wrapper-display-area-and-x-axis {
 | 
			
		||||
@@ -158,6 +173,14 @@ mct-plot {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.gl-plot-x {
 | 
			
		||||
            @include hover {
 | 
			
		||||
                .gl-plot-x-label__select {
 | 
			
		||||
                    display: block;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .gl-plot-coords {
 | 
			
		||||
@@ -194,9 +217,7 @@ mct-plot {
 | 
			
		||||
        &.gl-plot-y-label {
 | 
			
		||||
            display: block;
 | 
			
		||||
            left: 0; top: 0; right: auto; bottom: 0;
 | 
			
		||||
            padding-left: 5px;
 | 
			
		||||
            text-orientation: mixed;
 | 
			
		||||
            //overflow: hidden;
 | 
			
		||||
            writing-mode: vertical-lr;
 | 
			
		||||
            &:before {
 | 
			
		||||
                // Icon denoting configurability
 | 
			
		||||
@@ -205,11 +226,19 @@ mct-plot {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .gl-plot-x-label__select {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        left: 50%;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        transform: translateX(-50%);
 | 
			
		||||
        z-index: 10;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .gl-plot-y-label__select {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 50%;
 | 
			
		||||
        transform: translateY(-50%);
 | 
			
		||||
        left: 20px;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        z-index: 10;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -261,7 +290,7 @@ mct-plot {
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: $m;
 | 
			
		||||
        right: $m;
 | 
			
		||||
        left: $m;
 | 
			
		||||
        z-index: 9;
 | 
			
		||||
 | 
			
		||||
        &__reset {
 | 
			
		||||
@@ -274,15 +303,18 @@ mct-plot {
 | 
			
		||||
            top: $m;
 | 
			
		||||
            right: $m;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .c-button {
 | 
			
		||||
            box-shadow: $colorLocalControlOvrBg 0 0 0 2px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .l-state-indicators {
 | 
			
		||||
        color: $colorPausedBg;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        display: block;
 | 
			
		||||
        font-size: 1.5em;
 | 
			
		||||
        pointer-events: none;
 | 
			
		||||
        top: $interiorMarginSm;
 | 
			
		||||
        cursor: help;
 | 
			
		||||
        font-size: 1.2em;
 | 
			
		||||
        bottom: $interiorMarginSm;
 | 
			
		||||
        left: $interiorMarginSm;
 | 
			
		||||
        z-index: 2;
 | 
			
		||||
 | 
			
		||||
@@ -341,11 +373,11 @@ mct-plot {
 | 
			
		||||
 | 
			
		||||
.gl-plot-tick {
 | 
			
		||||
    &.gl-plot-x-tick-label {
 | 
			
		||||
        top: $interiorMargin;
 | 
			
		||||
        top: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
    &.gl-plot-y-tick-label {
 | 
			
		||||
        right: $interiorMargin;
 | 
			
		||||
        left: $interiorMargin;
 | 
			
		||||
        right: $interiorMarginSm;
 | 
			
		||||
        left: auto;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -368,12 +400,6 @@ mct-plot {
 | 
			
		||||
    z-index: -10;
 | 
			
		||||
 | 
			
		||||
    .l-view-section {
 | 
			
		||||
        //$m: $interiorMargin;
 | 
			
		||||
        //top: $m !important;
 | 
			
		||||
        //right: $m;
 | 
			
		||||
        //bottom: $m;
 | 
			
		||||
        //left: $m;
 | 
			
		||||
 | 
			
		||||
        .s-status-timeconductor-unsynced .holder-plot {
 | 
			
		||||
            .t-object-alert.t-alert-unsynced {
 | 
			
		||||
                display: none;
 | 
			
		||||
@@ -429,14 +455,18 @@ mct-plot {
 | 
			
		||||
/****************** _LEGEND.SCSS */
 | 
			
		||||
.gl-plot-legend,
 | 
			
		||||
.c-plot-legend {
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
 | 
			
		||||
    &__wrapper {
 | 
			
		||||
        // Holds view-control and both collapsed and expanded legends
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        overflow: auto; // Prevents collapsed legend from forcing scrollbars on higher parent containers
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        padding: 2px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__view-control {
 | 
			
		||||
        padding-top: 2px;
 | 
			
		||||
        padding-top: 4px;
 | 
			
		||||
        margin-right: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -444,8 +474,22 @@ mct-plot {
 | 
			
		||||
        @include propertiesHeader();
 | 
			
		||||
        margin-bottom: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .is-in-small-container & {
 | 
			
		||||
        &.is-legend-hidden {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-plot--stacked {
 | 
			
		||||
    .is-legend-hidden {
 | 
			
		||||
        // Always show the legend in a stacked plot
 | 
			
		||||
        display: flex !important;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.gl-plot-legend {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: flex-start;
 | 
			
		||||
@@ -481,15 +525,21 @@ mct-plot {
 | 
			
		||||
/***************** GENERAL STYLES, ALL STATES */
 | 
			
		||||
.plot-legend-item {
 | 
			
		||||
    // General styles for legend items, both expanded and collapsed legend states
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-left: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .plot-series-color-swatch {
 | 
			
		||||
        border-radius: $smallCr;
 | 
			
		||||
        border-radius: 30%; //$smallCr;
 | 
			
		||||
        border: 1px solid $colorBodyBg;
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
        height: $plotSwatchD;
 | 
			
		||||
        width: $plotSwatchD;
 | 
			
		||||
    }
 | 
			
		||||
    .plot-series-name {
 | 
			
		||||
        display: inline;
 | 
			
		||||
        @include ellipsize();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .plot-series-value {
 | 
			
		||||
@@ -497,11 +547,27 @@ mct-plot {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.plot-series-swatch-and-name {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex: 0 1 auto;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-left: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.plot-wrapper-expanded-legend {
 | 
			
		||||
    flex: 1 1 auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.gl-plot {
 | 
			
		||||
.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
 | 
			
		||||
.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
 | 
			
		||||
.plot-legend-left .gl-plot-legend { margin-right: $interiorMargin; }
 | 
			
		||||
.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
 | 
			
		||||
 | 
			
		||||
.gl-plot,
 | 
			
		||||
.c-plot {
 | 
			
		||||
    &.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
 | 
			
		||||
    &.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
 | 
			
		||||
 | 
			
		||||
@@ -519,21 +585,14 @@ mct-plot {
 | 
			
		||||
 | 
			
		||||
        .plot-legend-item {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            align-items: center;
 | 
			
		||||
            justify-content: stretch;
 | 
			
		||||
            &:not(:first-child) {
 | 
			
		||||
                margin-left: $interiorMarginLg;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .plot-series-swatch-and-name,
 | 
			
		||||
            .plot-series-value {
 | 
			
		||||
                @include ellipsize();
 | 
			
		||||
                flex: 1 1 auto;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .plot-series-swatch-and-name {
 | 
			
		||||
                margin-right: $interiorMarginSm;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            .plot-series-value {
 | 
			
		||||
                text-align: left;
 | 
			
		||||
            }
 | 
			
		||||
@@ -543,7 +602,7 @@ mct-plot {
 | 
			
		||||
    /***************** GENERAL STYLES, EXPANDED */
 | 
			
		||||
    &.plot-legend-expanded {
 | 
			
		||||
        .gl-plot-legend {
 | 
			
		||||
            // max-height: 70%;
 | 
			
		||||
             max-height: 70%;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .plot-wrapper-expanded-legend {
 | 
			
		||||
@@ -554,8 +613,10 @@ mct-plot {
 | 
			
		||||
 | 
			
		||||
    /***************** TOP OR BOTTOM */
 | 
			
		||||
    &.plot-legend-top,
 | 
			
		||||
    &.plot-legend-bottom {
 | 
			
		||||
    &.plot-legend-bottom,
 | 
			
		||||
    &.plot-legend-hidden {
 | 
			
		||||
        // General styles when legend is on the top or bottom
 | 
			
		||||
        // -hidden included for legacy plots
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
 | 
			
		||||
        &.plot-legend-collapsed {
 | 
			
		||||
@@ -564,6 +625,11 @@ mct-plot {
 | 
			
		||||
                display: flex;
 | 
			
		||||
                flex: 1 1 auto;
 | 
			
		||||
                overflow: hidden;
 | 
			
		||||
 | 
			
		||||
                > .plot-legend-item + .plot-legend-item {
 | 
			
		||||
                    // Space between plot items
 | 
			
		||||
                    margin-left: $interiorMarginLg;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -572,6 +638,7 @@ mct-plot {
 | 
			
		||||
    &.plot-legend-left,
 | 
			
		||||
    &.plot-legend-right {
 | 
			
		||||
        // General styles when legend is on left or right
 | 
			
		||||
 | 
			
		||||
        .gl-plot-legend {
 | 
			
		||||
            max-height: inherit;
 | 
			
		||||
        }
 | 
			
		||||
@@ -595,12 +662,18 @@ mct-plot {
 | 
			
		||||
                min-width: 0;
 | 
			
		||||
                flex: 1 1 auto;
 | 
			
		||||
                overflow-y: auto;
 | 
			
		||||
 | 
			
		||||
                > * + * {
 | 
			
		||||
                    // Space between plot items
 | 
			
		||||
                    margin-top: $interiorMarginSm;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .plot-legend-item {
 | 
			
		||||
                margin-bottom: 1px;
 | 
			
		||||
                margin-bottom: $interiorMarginSm;
 | 
			
		||||
                margin-left: 0;
 | 
			
		||||
                flex-wrap: wrap;
 | 
			
		||||
                flex-wrap: nowrap;
 | 
			
		||||
                .plot-series-swatch-and-name {
 | 
			
		||||
                    @include ellipsize();
 | 
			
		||||
                    flex: 0 1 auto;
 | 
			
		||||
                    min-width: 20%;
 | 
			
		||||
                }
 | 
			
		||||
@@ -624,6 +697,31 @@ mct-plot {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************** STACKED PLOT LEGEND OVERRIDES */
 | 
			
		||||
.c-plot--stacked {
 | 
			
		||||
    // Always show the legend on top, ignore any position setting
 | 
			
		||||
    .c-plot,
 | 
			
		||||
    .gl-plot {
 | 
			
		||||
        flex-direction: column !important;
 | 
			
		||||
 | 
			
		||||
        .c-plot-legend,
 | 
			
		||||
        .gl-plot-legend {
 | 
			
		||||
            margin: 0;
 | 
			
		||||
            margin-bottom: $interiorMargin;
 | 
			
		||||
            order: 1 !important;
 | 
			
		||||
            width: 100% !important;
 | 
			
		||||
 | 
			
		||||
            .plot-wrapper-collapsed-legend {
 | 
			
		||||
                flex-direction: row !important;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .plot-wrapper-axis-and-display-area {
 | 
			
		||||
            order: 2 !important;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************** CURSOR GUIDES */
 | 
			
		||||
[class*='c-cursor-guide'] {
 | 
			
		||||
    box-shadow: $shdwCursorGuide;
 | 
			
		||||
@@ -654,3 +752,24 @@ mct-plot {
 | 
			
		||||
        display: inline-block !important;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*********************** CURSOR LOCK INDICATOR */
 | 
			
		||||
[class*='c-state-indicator__alert-cursor-lock'] {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[class*='is-cursor-locked'] {
 | 
			
		||||
    background: rgba($colorInfo, 0.1);
 | 
			
		||||
 | 
			
		||||
    [class*='c-state-indicator__alert-cursor-lock'] {
 | 
			
		||||
        @include userSelectNone();
 | 
			
		||||
        color: $colorInfo;
 | 
			
		||||
        display: block;
 | 
			
		||||
        margin-right: $interiorMarginSm;
 | 
			
		||||
 | 
			
		||||
        &[class*='--verbose'] {
 | 
			
		||||
            padding: $interiorMarginSm;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -52,6 +52,7 @@
 | 
			
		||||
    $ctrlW: 22px;
 | 
			
		||||
 | 
			
		||||
    &__controls {
 | 
			
		||||
        font-size: 1rem !important;
 | 
			
		||||
        margin-right: 0;
 | 
			
		||||
        min-width: 0;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
@@ -62,7 +63,7 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__direction {
 | 
			
		||||
        font-size: 0.9em;
 | 
			
		||||
        font-size: 0.9rem !important;
 | 
			
		||||
        margin-right: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -117,6 +117,33 @@
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin isMissing($absPos: false) {
 | 
			
		||||
    // Common styles to be applied to tree items, object labels, grid and list item views
 | 
			
		||||
    //opacity: 0.7;
 | 
			
		||||
    //pointer-events: none; // Don't think we can do this, as disables title hover on icon element
 | 
			
		||||
 | 
			
		||||
    .is-missing__indicator {
 | 
			
		||||
        display: none ;
 | 
			
		||||
        text-shadow: $colorBodyBg 0 0 2px;
 | 
			
		||||
        color: $colorAlert;
 | 
			
		||||
        font-family: symbolsfont;
 | 
			
		||||
 | 
			
		||||
        &:before {
 | 
			
		||||
            content: $glyph-icon-alert-triangle;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @if $absPos {
 | 
			
		||||
        .is-missing__indicator {
 | 
			
		||||
            position: absolute;
 | 
			
		||||
            z-index: 3;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-missing .is-missing__indicator,
 | 
			
		||||
    .is-missing .is-missing__indicator { display: block !important; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) {
 | 
			
		||||
    background-image: linear-gradient(-45deg,
 | 
			
		||||
        rgba($c, $a) 25%, transparent 25%,
 | 
			
		||||
@@ -416,28 +443,18 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @include hover() {
 | 
			
		||||
        background: $colorBtnBgHov;
 | 
			
		||||
        color: $colorBtnFgHov;
 | 
			
		||||
        filter: $filterHov;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &[class*="--major"],
 | 
			
		||||
    &[class*='is-active']{
 | 
			
		||||
        background: $colorBtnMajorBg;
 | 
			
		||||
        color: $colorBtnMajorFg;
 | 
			
		||||
 | 
			
		||||
        @include hover() {
 | 
			
		||||
            background: $colorBtnMajorBgHov;
 | 
			
		||||
            color: $colorBtnMajorFgHov;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &[class*='--caution'] {
 | 
			
		||||
        background: $colorBtnCautionBg;
 | 
			
		||||
        color: $colorBtnCautionFg;
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
            background: $colorBtnCautionBgHov;
 | 
			
		||||
        }
 | 
			
		||||
        background: $colorBtnCautionBg !important;
 | 
			
		||||
        color: $colorBtnCautionFg !important;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -49,8 +49,6 @@ table {
 | 
			
		||||
    td {
 | 
			
		||||
        vertical-align: top;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    a { color: $colorBtnMajorBg; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.is-editing {
 | 
			
		||||
@@ -92,14 +90,28 @@ div.c-table {
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-top: $interiorMarginSm;
 | 
			
		||||
    .is-in-small-container & {
 | 
			
		||||
        &:not(.is-paused) {
 | 
			
		||||
            .c-table-control-bar {
 | 
			
		||||
                display: none;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .c-table-control-bar {
 | 
			
		||||
            .c-click-icon,
 | 
			
		||||
            .c-button {
 | 
			
		||||
                &__label {
 | 
			
		||||
                    display: none;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-table-control-bar {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex: 0 0 auto;
 | 
			
		||||
    //margin-bottom: $interiorMarginSm; // This approach to allow margin to go away when control bar is hidden
 | 
			
		||||
    padding: $interiorMarginSm 0;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-left: $interiorMarginSm;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
  "metadata": {
 | 
			
		||||
    "name": "Open MCT Symbols 16px",
 | 
			
		||||
    "lastOpened": 0,
 | 
			
		||||
    "created": 1593102875898
 | 
			
		||||
    "created": 1596146160781
 | 
			
		||||
  },
 | 
			
		||||
  "iconSets": [
 | 
			
		||||
    {
 | 
			
		||||
@@ -752,7 +752,7 @@
 | 
			
		||||
          "tempChar": ""
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "order": 114,
 | 
			
		||||
          "order": 189,
 | 
			
		||||
          "id": 4,
 | 
			
		||||
          "name": "icon-font-size",
 | 
			
		||||
          "prevSize": 24,
 | 
			
		||||
@@ -2686,17 +2686,26 @@
 | 
			
		||||
        {
 | 
			
		||||
          "id": 4,
 | 
			
		||||
          "paths": [
 | 
			
		||||
            "M842.841 380.048h-120.956l-52.382 139.676 52.918 141.12 59.942-159.84 62.361 166.314h-119.884l34.019 90.717h119.884l39.695 105.836h105.836l-181.434-483.823z",
 | 
			
		||||
            "M263.903 160.129l-263.903 703.742h153.944l57.729-153.944h280.397l57.729 153.944h153.944l-263.903-703.742zM261.154 577.976l90.717-241.911 90.717 241.911z"
 | 
			
		||||
            "M1148 416h-152l-65.82 175.54 66.5 177.32 75.32-200.86 78.38 209h-150.66l42.76 114h150.64l49.88 133h133l-228-608z",
 | 
			
		||||
            "M384 0l-384 1024h224l84-224h408l84 224h224l-384-1024zM380 608l132-352 132 352z"
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [
 | 
			
		||||
            {},
 | 
			
		||||
            {}
 | 
			
		||||
          ],
 | 
			
		||||
          "attrs": [],
 | 
			
		||||
          "grid": 16,
 | 
			
		||||
          "tags": [
 | 
			
		||||
            "icon-font-size-alt1"
 | 
			
		||||
          ],
 | 
			
		||||
          "isMulticolor": false,
 | 
			
		||||
          "isMulticolor2": false,
 | 
			
		||||
          "colorPermutations": {
 | 
			
		||||
            "12552552551": []
 | 
			
		||||
          }
 | 
			
		||||
            "12552552551": [
 | 
			
		||||
              {},
 | 
			
		||||
              {}
 | 
			
		||||
            ]
 | 
			
		||||
          },
 | 
			
		||||
          "width": 1376
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "id": 141,
 | 
			
		||||
 
 | 
			
		||||
@@ -100,7 +100,7 @@
 | 
			
		||||
<glyph unicode="" 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="" 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="" 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="" glyph-name="icon-font-size" d="M842.841 451.952h-120.956l-52.382-139.676 52.918-141.12 59.942 159.84 62.361-166.314h-119.884l34.019-90.717h119.884l39.695-105.836h105.836l-181.434 483.823zM263.903 671.871l-263.903-703.742h153.944l57.729 153.944h280.397l57.729-153.944h153.944l-263.903 703.742zM261.154 254.024l90.717 241.911 90.717-241.911z" />
 | 
			
		||||
<glyph unicode="" glyph-name="icon-font-size" horiz-adv-x="1376" d="M1148 416h-152l-65.82-175.54 66.5-177.32 75.32 200.86 78.38-209h-150.66l42.76-114h150.64l49.88-133h133l-228 608zM384 832l-384-1024h224l84 224h408l84-224h224l-384 1024zM380 224l132 352 132-352z" />
 | 
			
		||||
<glyph unicode="" 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="" 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="" 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" />
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB  | 
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user