Compare commits
	
		
			23 Commits
		
	
	
		
			vista-4.1.
			...
			select-tab
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					faa1d01499 | ||
| 
						 | 
					a9c245a7db | ||
| 
						 | 
					85dd5ce00c | ||
| 
						 | 
					ec86ebd692 | ||
| 
						 | 
					6c69694dca | ||
| 
						 | 
					0b8bf682a4 | ||
| 
						 | 
					e72ba5e8bf | ||
| 
						 | 
					3caba5efac | ||
| 
						 | 
					efdd80bd57 | ||
| 
						 | 
					832c4d9816 | ||
| 
						 | 
					3a5024d38d | ||
| 
						 | 
					dcd6334036 | ||
| 
						 | 
					728b39164e | ||
| 
						 | 
					884aec8ea0 | ||
| 
						 | 
					216f447578 | ||
| 
						 | 
					c38d810658 | ||
| 
						 | 
					f5c48b7bf6 | ||
| 
						 | 
					d0e08f1d9a | ||
| 
						 | 
					72ea7b80fd | ||
| 
						 | 
					35d0c02bc5 | ||
| 
						 | 
					abd7506b45 | ||
| 
						 | 
					526b4aa07e | ||
| 
						 | 
					b5e23963d4 | 
@@ -55,7 +55,7 @@
 | 
			
		||||
    "node-bourbon": "^4.2.3",
 | 
			
		||||
    "node-sass": "^4.9.2",
 | 
			
		||||
    "painterro": "^0.2.65",
 | 
			
		||||
    "printj": "^1.1.0",
 | 
			
		||||
    "printj": "^1.2.1",
 | 
			
		||||
    "raw-loader": "^0.5.1",
 | 
			
		||||
    "request": "^2.69.0",
 | 
			
		||||
    "split": "^1.0.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -64,12 +64,30 @@ define(['zepto'], function ($) {
 | 
			
		||||
        var tree = this.generateNewIdentifiers(objTree);
 | 
			
		||||
        var rootId = tree.rootId;
 | 
			
		||||
        var rootObj = this.instantiate(tree.openmct[rootId], rootId);
 | 
			
		||||
        var newStyleParent = parent.useCapability('adapter');
 | 
			
		||||
        var newStyleRootObj = rootObj.useCapability('adapter');
 | 
			
		||||
 | 
			
		||||
        // Instantiate all objects in tree with their newly genereated ids,
 | 
			
		||||
        // adding each to its rightful parent's composition
 | 
			
		||||
        rootObj.getCapability("location").setPrimaryLocation(parent.getId());
 | 
			
		||||
        this.deepInstantiate(rootObj, tree.openmct, []);
 | 
			
		||||
        parent.getCapability("composition").add(rootObj);
 | 
			
		||||
        if (this.openmct.composition.checkPolicy(newStyleParent, newStyleRootObj)) {
 | 
			
		||||
            // Instantiate all objects in tree with their newly generated ids,
 | 
			
		||||
            // adding each to its rightful parent's composition
 | 
			
		||||
            rootObj.getCapability("location").setPrimaryLocation(parent.getId());
 | 
			
		||||
            this.deepInstantiate(rootObj, tree.openmct, []);
 | 
			
		||||
            parent.getCapability("composition").add(rootObj);
 | 
			
		||||
        } else {
 | 
			
		||||
            var dialog = this.openmct.overlays.dialog({
 | 
			
		||||
                iconClass: 'alert',
 | 
			
		||||
                message: "We're sorry, but you cannot import that object type into this object.",
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        label: "Ok",
 | 
			
		||||
                        emphasis: true,
 | 
			
		||||
                        callback: function () {
 | 
			
		||||
                            dialog.dismiss();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ImportAsJSONAction.prototype.deepInstantiate = function (parent, tree, seen) {
 | 
			
		||||
@@ -80,15 +98,17 @@ define(['zepto'], function ($) {
 | 
			
		||||
            var newObj;
 | 
			
		||||
 | 
			
		||||
            seen.push(parent.getId());
 | 
			
		||||
            parentModel.composition.forEach(function (childId, index) {
 | 
			
		||||
                if (!tree[childId] || seen.includes(childId)) {
 | 
			
		||||
 | 
			
		||||
            parentModel.composition.forEach(function (childId) {
 | 
			
		||||
                let keystring = this.openmct.objects.makeKeyString(childId);
 | 
			
		||||
 | 
			
		||||
                if (!tree[keystring] || seen.includes(keystring)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                newObj = this.instantiate(tree[childId], childId);
 | 
			
		||||
                parent.getCapability("composition").add(newObj);
 | 
			
		||||
                newObj = this.instantiate(tree[keystring], keystring);
 | 
			
		||||
                newObj.getCapability("location")
 | 
			
		||||
                    .setPrimaryLocation(tree[childId].location);
 | 
			
		||||
                    .setPrimaryLocation(tree[keystring].location);
 | 
			
		||||
                this.deepInstantiate(newObj, tree, seen);
 | 
			
		||||
            }, this);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ define([
 | 
			
		||||
    './runs/RegisterLegacyTypes',
 | 
			
		||||
    './services/LegacyObjectAPIInterceptor',
 | 
			
		||||
    './views/installLegacyViews',
 | 
			
		||||
    './policies/legacyCompositionPolicyAdapter',
 | 
			
		||||
    './policies/LegacyCompositionPolicyAdapter',
 | 
			
		||||
    './actions/LegacyActionAdapter'
 | 
			
		||||
], function (
 | 
			
		||||
    legacyRegistry,
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ define([
 | 
			
		||||
            cssClass: representation.cssClass,
 | 
			
		||||
            description: representation.description,
 | 
			
		||||
            canView: function (selection) {
 | 
			
		||||
                if (selection.length === 0 || selection[0].length === 0) {
 | 
			
		||||
                if (selection.length !== 1 || selection[0].length === 0) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,20 @@ define([
 | 
			
		||||
            publicAPI = {};
 | 
			
		||||
            publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
 | 
			
		||||
                'get',
 | 
			
		||||
                'mutate'
 | 
			
		||||
                'mutate',
 | 
			
		||||
                'observe',
 | 
			
		||||
                'areIdsEqual'
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            publicAPI.objects.areIdsEqual.and.callFake(function (id1, id2) {
 | 
			
		||||
                return id1.namespace === id2.namespace && id1.key === id2.key;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            publicAPI.composition = jasmine.createSpyObj('CompositionAPI', [
 | 
			
		||||
                'checkPolicy'
 | 
			
		||||
            ]);
 | 
			
		||||
            publicAPI.composition.checkPolicy.and.returnValue(true);
 | 
			
		||||
 | 
			
		||||
            publicAPI.objects.eventEmitter = jasmine.createSpyObj('eventemitter', [
 | 
			
		||||
                'on'
 | 
			
		||||
            ]);
 | 
			
		||||
@@ -91,7 +103,7 @@ define([
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    listener = jasmine.createSpy('reorderListener');
 | 
			
		||||
                    composition.on('reorder', listener);
 | 
			
		||||
                    
 | 
			
		||||
 | 
			
		||||
                    return composition.load();
 | 
			
		||||
                });
 | 
			
		||||
                it('', function () {
 | 
			
		||||
@@ -119,49 +131,16 @@ define([
 | 
			
		||||
                    expect(newComposition[2].key).toEqual('a');
 | 
			
		||||
                })
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // TODO: Implement add/removal in new default provider.
 | 
			
		||||
            xit('synchronizes changes between instances', function () {
 | 
			
		||||
                var otherComposition = compositionAPI.get(domainObject);
 | 
			
		||||
                var addListener = jasmine.createSpy('addListener');
 | 
			
		||||
                var removeListener = jasmine.createSpy('removeListener');
 | 
			
		||||
                var otherAddListener = jasmine.createSpy('otherAddListener');
 | 
			
		||||
                var otherRemoveListener = jasmine.createSpy('otherRemoveListener');
 | 
			
		||||
            it('supports adding an object to composition', function () {
 | 
			
		||||
                let addListener = jasmine.createSpy('addListener');
 | 
			
		||||
                let mockChildObject = {
 | 
			
		||||
                    identifier: {key: 'mock-key', namespace: ''}
 | 
			
		||||
                };
 | 
			
		||||
                composition.on('add', addListener);
 | 
			
		||||
                composition.on('remove', removeListener);
 | 
			
		||||
                otherComposition.on('add', otherAddListener);
 | 
			
		||||
                otherComposition.on('remove', otherRemoveListener);
 | 
			
		||||
                composition.add(mockChildObject);
 | 
			
		||||
 | 
			
		||||
                return Promise.all([composition.load(), otherComposition.load()])
 | 
			
		||||
                    .then(function () {
 | 
			
		||||
                        expect(addListener).toHaveBeenCalled();
 | 
			
		||||
                        expect(otherAddListener).toHaveBeenCalled();
 | 
			
		||||
                        expect(removeListener).not.toHaveBeenCalled();
 | 
			
		||||
                        expect(otherRemoveListener).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                        var object = addListener.calls.mostRecent().args[0];
 | 
			
		||||
                        composition.remove(object);
 | 
			
		||||
                        expect(removeListener).toHaveBeenCalled();
 | 
			
		||||
                        expect(otherRemoveListener).toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                        addListener.reset();
 | 
			
		||||
                        otherAddListener.reset();
 | 
			
		||||
                        composition.add(object);
 | 
			
		||||
                        expect(addListener).toHaveBeenCalled();
 | 
			
		||||
                        expect(otherAddListener).toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                        removeListener.reset();
 | 
			
		||||
                        otherRemoveListener.reset();
 | 
			
		||||
                        otherComposition.remove(object);
 | 
			
		||||
                        expect(removeListener).toHaveBeenCalled();
 | 
			
		||||
                        expect(otherRemoveListener).toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                        addListener.reset();
 | 
			
		||||
                        otherAddListener.reset();
 | 
			
		||||
                        otherComposition.add(object);
 | 
			
		||||
                        expect(addListener).toHaveBeenCalled();
 | 
			
		||||
                        expect(otherAddListener).toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
                expect(domainObject.composition.length).toBe(4);
 | 
			
		||||
                expect(domainObject.composition[3]).toEqual(mockChildObject.identifier);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -184,7 +163,9 @@ define([
 | 
			
		||||
                                key: 'thing'
 | 
			
		||||
                            }
 | 
			
		||||
                        ]);
 | 
			
		||||
                    }
 | 
			
		||||
                    },
 | 
			
		||||
                    add: jasmine.createSpy('add'),
 | 
			
		||||
                    remove: jasmine.createSpy('remove')
 | 
			
		||||
                };
 | 
			
		||||
                domainObject = {
 | 
			
		||||
                    identifier: {
 | 
			
		||||
@@ -214,6 +195,25 @@ define([
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            describe('Calling add or remove', function () {
 | 
			
		||||
                let mockChildObject;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockChildObject = {
 | 
			
		||||
                        identifier: {key: 'mock-key', namespace: ''}
 | 
			
		||||
                    };
 | 
			
		||||
                    composition.add(mockChildObject);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it('calls add on the provider', function () {
 | 
			
		||||
                    expect(customProvider.add).toHaveBeenCalledWith(domainObject, mockChildObject.identifier);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it('calls remove on the provider', function () {
 | 
			
		||||
                    composition.remove(mockChildObject);
 | 
			
		||||
                    expect(customProvider.remove).toHaveBeenCalledWith(domainObject, mockChildObject.identifier);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe('dynamic custom composition', function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -75,9 +75,7 @@ define([
 | 
			
		||||
            throw new Error('Event not supported by composition: ' + event);
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.mutationListener) {
 | 
			
		||||
            this.mutationListener = this.publicAPI.objects.observe(this.domainObject, '*', (newDomainObject) => {
 | 
			
		||||
                this.domainObject = newDomainObject;
 | 
			
		||||
            })
 | 
			
		||||
            this._synchronize();
 | 
			
		||||
        }
 | 
			
		||||
        if (this.provider.on && this.provider.off) {
 | 
			
		||||
            if (event === 'add') {
 | 
			
		||||
@@ -134,10 +132,8 @@ define([
 | 
			
		||||
 | 
			
		||||
        this.listeners[event].splice(index, 1);
 | 
			
		||||
        if (this.listeners[event].length === 0) {
 | 
			
		||||
            if (this.mutationListener) {
 | 
			
		||||
                this.mutationListener();
 | 
			
		||||
                delete this.mutationListener;
 | 
			
		||||
            }
 | 
			
		||||
            this._destroy();
 | 
			
		||||
 | 
			
		||||
            // Remove provider listener if this is the last callback to
 | 
			
		||||
            // be removed.
 | 
			
		||||
            if (this.provider.off && this.provider.on) {
 | 
			
		||||
@@ -181,6 +177,9 @@ define([
 | 
			
		||||
     */
 | 
			
		||||
    CompositionCollection.prototype.add = function (child, skipMutate) {
 | 
			
		||||
        if (!skipMutate) {
 | 
			
		||||
            if (!this.publicAPI.composition.checkPolicy(this.domainObject, child)) {
 | 
			
		||||
                throw `Object of type ${child.type} cannot be added to object of type ${this.domainObject.type}`;
 | 
			
		||||
            }
 | 
			
		||||
            this.provider.add(this.domainObject, child.identifier);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.emit('add', child);
 | 
			
		||||
@@ -272,6 +271,19 @@ define([
 | 
			
		||||
        this.remove(child, true);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    CompositionCollection.prototype._synchronize = function () {
 | 
			
		||||
        this.mutationListener = this.publicAPI.objects.observe(this.domainObject, '*', (newDomainObject) => {
 | 
			
		||||
            this.domainObject = JSON.parse(JSON.stringify(newDomainObject));
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    CompositionCollection.prototype._destroy = function () {
 | 
			
		||||
        if (this.mutationListener) {
 | 
			
		||||
            this.mutationListener();
 | 
			
		||||
            delete this.mutationListener;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Emit events.
 | 
			
		||||
     * @private
 | 
			
		||||
 
 | 
			
		||||
@@ -48,24 +48,11 @@ define([
 | 
			
		||||
        this.listeningTo = {};
 | 
			
		||||
        this.onMutation = this.onMutation.bind(this);
 | 
			
		||||
 | 
			
		||||
        this.cannotContainDuplicates = this.cannotContainDuplicates.bind(this);
 | 
			
		||||
        this.cannotContainItself = this.cannotContainItself.bind(this);
 | 
			
		||||
 | 
			
		||||
        compositionAPI.addPolicy(this.cannotContainDuplicates);
 | 
			
		||||
        compositionAPI.addPolicy(this.cannotContainItself);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    DefaultCompositionProvider.prototype.cannotContainDuplicates = function (parent, child) {
 | 
			
		||||
        return this.appliesTo(parent) &&
 | 
			
		||||
            parent.composition.findIndex((composeeId) => {
 | 
			
		||||
                return composeeId.namespace === child.identifier.namespace &&
 | 
			
		||||
                    composeeId.key === child.identifier.key;
 | 
			
		||||
            }) === -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
@@ -199,9 +186,18 @@ define([
 | 
			
		||||
     * @memberof module:openmct.CompositionProvider#
 | 
			
		||||
     * @method add
 | 
			
		||||
     */
 | 
			
		||||
    DefaultCompositionProvider.prototype.add = function (domainObject, child) {
 | 
			
		||||
        throw new Error('Default Provider does not implement adding.');
 | 
			
		||||
        // TODO: this needs to be synchronized via mutation
 | 
			
		||||
    DefaultCompositionProvider.prototype.add = function (parent, childId) {
 | 
			
		||||
        if (!this.includes(parent, childId)) {
 | 
			
		||||
            parent.composition.push(childId);
 | 
			
		||||
            this.publicAPI.objects.mutate(parent, 'composition', parent.composition);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    /**
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    DefaultCompositionProvider.prototype.includes = function (parent, childId) {
 | 
			
		||||
        return parent.composition.findIndex(composee =>
 | 
			
		||||
            this.publicAPI.objects.areIdsEqual(composee, childId)) !== -1;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    './components/LadTable.vue',
 | 
			
		||||
    './components/LADTable.vue',
 | 
			
		||||
    'vue'
 | 
			
		||||
], function (
 | 
			
		||||
    LadTableComponent,
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import lodash from 'lodash';
 | 
			
		||||
import LadRow from './LadRow.vue';
 | 
			
		||||
import LadRow from './LADRow.vue';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct', 'domainObject'],
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
    import lodash from 'lodash';
 | 
			
		||||
    import LadRow from './LadRow.vue';
 | 
			
		||||
    import LadRow from './LADRow.vue';
 | 
			
		||||
 | 
			
		||||
    export default {
 | 
			
		||||
    inject: ['openmct', 'domainObject'],
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										77
									
								
								src/plugins/displayLayout/AlphanumericFormatViewProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/plugins/displayLayout/AlphanumericFormatViewProvider.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2018, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    './components/AlphanumericFormatView.vue',
 | 
			
		||||
    'vue'
 | 
			
		||||
], function (AlphanumericFormatView, Vue) {
 | 
			
		||||
 | 
			
		||||
    function AlphanumericFormatViewProvider(openmct, options) {
 | 
			
		||||
        function isTelemetryObject(selectionPath) {
 | 
			
		||||
            let selectedObject = selectionPath[0].context.item;
 | 
			
		||||
            let parentObject = selectionPath[1].context.item;
 | 
			
		||||
            return parentObject && 
 | 
			
		||||
                parentObject.type === 'layout' &&
 | 
			
		||||
                selectedObject &&
 | 
			
		||||
                openmct.telemetry.isTelemetryObject(selectedObject) &&
 | 
			
		||||
                !options.showAsView.includes(selectedObject.type)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            key: 'alphanumeric-format',
 | 
			
		||||
            name: 'Alphanumeric Format',
 | 
			
		||||
            canView: function (selection) {
 | 
			
		||||
                if (selection.length === 0 || selection[0].length === 1) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return selection.every(isTelemetryObject);
 | 
			
		||||
            },
 | 
			
		||||
            view: function (selection) {
 | 
			
		||||
                let component;
 | 
			
		||||
                return {
 | 
			
		||||
                    show: function (element) {
 | 
			
		||||
                        component = new Vue({
 | 
			
		||||
                            provide: {
 | 
			
		||||
                                openmct
 | 
			
		||||
                            },
 | 
			
		||||
                            components: {
 | 
			
		||||
                                AlphanumericFormatView: AlphanumericFormatView.default
 | 
			
		||||
                            },
 | 
			
		||||
                            template: '<alphanumeric-format-view></alphanumeric-format-view>',
 | 
			
		||||
                            el: element
 | 
			
		||||
                        });
 | 
			
		||||
                    },
 | 
			
		||||
                    destroy: function () {
 | 
			
		||||
                        component.$destroy();
 | 
			
		||||
                        component = undefined;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            priority: function () {
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return AlphanumericFormatViewProvider;
 | 
			
		||||
});
 | 
			
		||||
@@ -0,0 +1,90 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2018, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
    <div class="c-properties" v-if="isEditing">
 | 
			
		||||
        <div class="c-properties__header">Alphanumeric Format</div>
 | 
			
		||||
        <ul class="c-properties__section">
 | 
			
		||||
            <li class="c-properties__row">
 | 
			
		||||
                <div class="c-properties__label" title="Printf formatting for the selected telemetry">
 | 
			
		||||
                    <label for="telemetryPrintfFormat">Format</label>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="c-properties__value">
 | 
			
		||||
                    <input id="telemetryPrintfFormat"
 | 
			
		||||
                        type="text"
 | 
			
		||||
                        @change="formatTelemetry"
 | 
			
		||||
                        :value="telemetryFormat"
 | 
			
		||||
                        :placeholder="nonMixedFormat ? '' : 'Mixed'"
 | 
			
		||||
                    >
 | 
			
		||||
                </div>
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
    export default {
 | 
			
		||||
        inject: ['openmct'],
 | 
			
		||||
        data() {
 | 
			
		||||
            let selectionPath = this.openmct.selection.get()[0];
 | 
			
		||||
            return {
 | 
			
		||||
                isEditing: this.openmct.editor.isEditing(),
 | 
			
		||||
                telemetryFormat: undefined,
 | 
			
		||||
                nonMixedFormat: false
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        methods: {
 | 
			
		||||
            toggleEdit(isEditing) {
 | 
			
		||||
                this.isEditing = isEditing;
 | 
			
		||||
            },
 | 
			
		||||
            formatTelemetry(event) {
 | 
			
		||||
                let newFormat = event.currentTarget.value;
 | 
			
		||||
                this.openmct.selection.get().forEach(selectionPath => {
 | 
			
		||||
                    selectionPath[0].context.updateTelemetryFormat(newFormat);    
 | 
			
		||||
                });
 | 
			
		||||
                this.telemetryFormat = newFormat;
 | 
			
		||||
            },
 | 
			
		||||
            handleSelection(selection) {
 | 
			
		||||
                if (selection.length === 0 || selection[0].length < 2) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let format = selection[0][0].context.layoutItem.format;
 | 
			
		||||
                this.nonMixedFormat = selection.every(selectionPath => {
 | 
			
		||||
                    return selectionPath[0].context.layoutItem.format === format;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                this.telemetryFormat = this.nonMixedFormat ? format : '';
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        mounted() {
 | 
			
		||||
            this.openmct.editor.on('isEditing', this.toggleEdit);
 | 
			
		||||
            this.openmct.selection.on('change', this.handleSelection);
 | 
			
		||||
            this.handleSelection(this.openmct.selection.get());
 | 
			
		||||
        },
 | 
			
		||||
        destroyed() {
 | 
			
		||||
            this.openmct.editor.off('isEditing', this.toggleEdit);
 | 
			
		||||
            this.openmct.selection.off('change', this.handleSelection);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
@@ -48,7 +48,8 @@
 | 
			
		||||
                   :multiSelect="selectedLayoutItems.length > 1"
 | 
			
		||||
                   @move="move"
 | 
			
		||||
                   @endMove="endMove"
 | 
			
		||||
                   @endLineResize='endLineResize'>
 | 
			
		||||
                   @endLineResize='endLineResize'
 | 
			
		||||
                   @formatChanged='updateTelemetryFormat'>
 | 
			
		||||
        </component>
 | 
			
		||||
        <edit-marquee v-if='showMarquee'
 | 
			
		||||
                      :gridSize="gridSize"
 | 
			
		||||
@@ -557,6 +558,11 @@
 | 
			
		||||
                    this.layoutItems.splice(itemIndex, 1);
 | 
			
		||||
                    this.layoutItems.splice(newIndex, 0, items[itemIndex]);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            updateTelemetryFormat(item, format) {
 | 
			
		||||
                let index = _.findIndex(this.layoutItems, item);
 | 
			
		||||
                item.format = format;
 | 
			
		||||
                this.mutate(`configuration.items[${index}]`, item);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        mounted() {
 | 
			
		||||
 
 | 
			
		||||
@@ -79,6 +79,7 @@
 | 
			
		||||
 | 
			
		||||
 <script>
 | 
			
		||||
    import LayoutFrame from './LayoutFrame.vue'
 | 
			
		||||
    import printj from 'printj'
 | 
			
		||||
 | 
			
		||||
    const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5],
 | 
			
		||||
          DEFAULT_POSITION = [1, 1];
 | 
			
		||||
@@ -143,6 +144,10 @@
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (this.item.format) {
 | 
			
		||||
                    return printj.sprintf(this.item.format, this.datum[this.valueMetadata.key]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return this.valueFormatter && this.valueFormatter.format(this.datum);
 | 
			
		||||
            },
 | 
			
		||||
            telemetryClass() {
 | 
			
		||||
@@ -168,6 +173,9 @@
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.context.index = newIndex;
 | 
			
		||||
            },
 | 
			
		||||
            item(newItem) {
 | 
			
		||||
                this.context.layoutItem = newItem;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        methods: {
 | 
			
		||||
@@ -219,10 +227,14 @@
 | 
			
		||||
                this.context = {
 | 
			
		||||
                    item: domainObject,
 | 
			
		||||
                    layoutItem: this.item,
 | 
			
		||||
                    index: this.index
 | 
			
		||||
                    index: this.index,
 | 
			
		||||
                    updateTelemetryFormat: this.updateTelemetryFormat
 | 
			
		||||
                };
 | 
			
		||||
                this.removeSelectable = this.openmct.selection.selectable(
 | 
			
		||||
                    this.$el, this.context, this.initSelect);
 | 
			
		||||
            },
 | 
			
		||||
            updateTelemetryFormat(format) {
 | 
			
		||||
                this.$emit('formatChanged', this.item, format);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        mounted() {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,8 @@ import Vue from 'vue'
 | 
			
		||||
import objectUtils from '../../api/objects/object-utils.js'
 | 
			
		||||
import DisplayLayoutType from './DisplayLayoutType.js'
 | 
			
		||||
import DisplayLayoutToolbar from './DisplayLayoutToolbar.js'
 | 
			
		||||
import AlphaNumericFormatViewProvider from './AlphaNumericFormatViewProvider.js'
 | 
			
		||||
 | 
			
		||||
export default function DisplayLayoutPlugin(options) {
 | 
			
		||||
    return function (openmct) {
 | 
			
		||||
        openmct.objectViews.addProvider({
 | 
			
		||||
@@ -76,7 +78,8 @@ export default function DisplayLayoutPlugin(options) {
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        openmct.types.addType('layout', DisplayLayoutType());
 | 
			
		||||
        openmct.toolbars.addProvider(new DisplayLayoutToolbar(openmct));
 | 
			
		||||
        openmct.toolbars.addProvider(new DisplayLayoutToolbar(openmct, options));
 | 
			
		||||
        openmct.inspectorViews.addProvider(new AlphaNumericFormatViewProvider(openmct, options));
 | 
			
		||||
        openmct.composition.addPolicy((parent, child) => {
 | 
			
		||||
            if (parent.type === 'layout' && child.type === 'folder') {
 | 
			
		||||
                return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -115,11 +115,13 @@ define([
 | 
			
		||||
 | 
			
		||||
    Collection.prototype.remove = function (model) {
 | 
			
		||||
        var index = this.indexOf(model);
 | 
			
		||||
 | 
			
		||||
        if (index === -1) {
 | 
			
		||||
            throw new Error('model not found in collection.');
 | 
			
		||||
        }
 | 
			
		||||
        this.models.splice(index, 1);
 | 
			
		||||
 | 
			
		||||
        this.emit('remove', model, index);
 | 
			
		||||
        this.models.splice(index, 1);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Collection.prototype.destroy = function (model) {
 | 
			
		||||
 
 | 
			
		||||
@@ -100,19 +100,33 @@ define([
 | 
			
		||||
        removeTelemetryObject: function (identifier) {
 | 
			
		||||
            var plotObject = this.plot.get('domainObject');
 | 
			
		||||
            if (plotObject.type === 'telemetry.plot.overlay') {
 | 
			
		||||
                var index = _.findIndex(plotObject.configuration.series, function (s) {
 | 
			
		||||
 | 
			
		||||
                var persistedIndex = _.findIndex(plotObject.configuration.series, function (s) {
 | 
			
		||||
                    return _.isEqual(identifier, s.identifier);
 | 
			
		||||
                });
 | 
			
		||||
                this.remove(this.at(index));
 | 
			
		||||
                // Because this is triggered by a composition change, we have
 | 
			
		||||
                // to defer mutation of our plot object, otherwise we might
 | 
			
		||||
                // mutate an outdated version of the plotObject.
 | 
			
		||||
                setTimeout(function () {
 | 
			
		||||
                    var newPlotObject = this.plot.get('domainObject');
 | 
			
		||||
                    var cSeries = newPlotObject.configuration.series.slice();
 | 
			
		||||
                    cSeries.splice(index, 1);
 | 
			
		||||
                    this.openmct.objects.mutate(newPlotObject, 'configuration.series', cSeries);
 | 
			
		||||
                }.bind(this));
 | 
			
		||||
 | 
			
		||||
                var configIndex = _.findIndex(this.models, function (m) {
 | 
			
		||||
                    return _.isEqual(m.domainObject.identifier, identifier);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                /*
 | 
			
		||||
                    when cancelling out of edit mode, the config store and domain object are out of sync
 | 
			
		||||
                    thus it is necesarry to check both and remove the models that are no longer in composition
 | 
			
		||||
                */
 | 
			
		||||
                if (persistedIndex === -1) {
 | 
			
		||||
                    this.remove(this.at(configIndex));
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.remove(this.at(persistedIndex));
 | 
			
		||||
                    // Because this is triggered by a composition change, we have
 | 
			
		||||
                    // to defer mutation of our plot object, otherwise we might
 | 
			
		||||
                    // mutate an outdated version of the plotObject.
 | 
			
		||||
                    setTimeout(function () {
 | 
			
		||||
                        var newPlotObject = this.plot.get('domainObject');
 | 
			
		||||
                        var cSeries = newPlotObject.configuration.series.slice();
 | 
			
		||||
                        cSeries.splice(persistedIndex, 1);
 | 
			
		||||
                        this.openmct.objects.mutate(newPlotObject, 'configuration.series', cSeries);
 | 
			
		||||
                    }.bind(this));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        onSeriesAdd: function (series) {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,23 +25,11 @@ define([
 | 
			
		||||
 | 
			
		||||
    function ConfigStore() {
 | 
			
		||||
        this.store = {};
 | 
			
		||||
        this.tracking = {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ConfigStore.prototype.track = function (id) {
 | 
			
		||||
        if (!this.tracking[id]) {
 | 
			
		||||
            this.tracking[id] = 0;
 | 
			
		||||
        }
 | 
			
		||||
        this.tracking[id] += 1;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ConfigStore.prototype.untrack = function (id) {
 | 
			
		||||
        this.tracking[id] -= 1;
 | 
			
		||||
        if (this.tracking[id] <= 0) {
 | 
			
		||||
            delete this.tracking[id];
 | 
			
		||||
            this.store[id].destroy();
 | 
			
		||||
            delete this.store[id];
 | 
			
		||||
        }
 | 
			
		||||
    ConfigStore.prototype.deleteStore = function (id) {
 | 
			
		||||
        this.store[id].destroy();
 | 
			
		||||
        delete this.store[id];
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ConfigStore.prototype.add = function (id, config) {
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,6 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    PlotOptionsController.prototype.destroy = function () {
 | 
			
		||||
        configStore.untrack(this.configId);
 | 
			
		||||
        this.stopListening();
 | 
			
		||||
        this.unlisten();
 | 
			
		||||
    };
 | 
			
		||||
@@ -60,7 +59,7 @@ define([
 | 
			
		||||
            this.$timeout(this.setUpScope.bind(this));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        configStore.track(this.configId);
 | 
			
		||||
 | 
			
		||||
        this.config = this.$scope.config = config;
 | 
			
		||||
        this.$scope.plotSeries = [];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -282,11 +282,19 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.zoom = function (zoomDirection, zoomFactor) {
 | 
			
		||||
        var currentXaxis = this.$scope.xAxis.get('displayRange'),
 | 
			
		||||
            currentYaxis = this.$scope.yAxis.get('displayRange');
 | 
			
		||||
 | 
			
		||||
        // when there is no plot data, the ranges can be undefined
 | 
			
		||||
        // in which case we should not perform zoom
 | 
			
		||||
        if (!currentXaxis || !currentYaxis) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.freeze();
 | 
			
		||||
        this.trackHistory();
 | 
			
		||||
        var currentXaxis = this.$scope.xAxis.get('displayRange'),
 | 
			
		||||
            currentYaxis = this.$scope.yAxis.get('displayRange'),
 | 
			
		||||
            xAxisDist= (currentXaxis.max - currentXaxis.min) * zoomFactor,
 | 
			
		||||
 | 
			
		||||
        var xAxisDist= (currentXaxis.max - currentXaxis.min) * zoomFactor,
 | 
			
		||||
            yAxisDist = (currentYaxis.max - currentYaxis.min) * zoomFactor;
 | 
			
		||||
 | 
			
		||||
        if (zoomDirection === 'in') {
 | 
			
		||||
@@ -322,12 +330,19 @@ define([
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let xDisplayRange = this.$scope.xAxis.get('displayRange'),
 | 
			
		||||
            yDisplayRange = this.$scope.yAxis.get('displayRange');
 | 
			
		||||
 | 
			
		||||
        // when there is no plot data, the ranges can be undefined
 | 
			
		||||
        // in which case we should not perform zoom
 | 
			
		||||
        if (!xDisplayRange || !yDisplayRange) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.freeze();
 | 
			
		||||
        window.clearTimeout(this.stillZooming);
 | 
			
		||||
 | 
			
		||||
        let xDisplayRange = this.$scope.xAxis.get('displayRange'),
 | 
			
		||||
            yDisplayRange = this.$scope.yAxis.get('displayRange'),
 | 
			
		||||
            xAxisDist = (xDisplayRange.max - xDisplayRange.min),
 | 
			
		||||
        let xAxisDist = (xDisplayRange.max - xDisplayRange.min),
 | 
			
		||||
            yAxisDist = (yDisplayRange.max - yDisplayRange.min),
 | 
			
		||||
            xDistMouseToMax = xDisplayRange.max - this.positionOverPlot.x,
 | 
			
		||||
            xDistMouseToMin = this.positionOverPlot.x - xDisplayRange.min,
 | 
			
		||||
 
 | 
			
		||||
@@ -148,7 +148,6 @@ define([
 | 
			
		||||
            });
 | 
			
		||||
            configStore.add(configId, config);
 | 
			
		||||
        }
 | 
			
		||||
        configStore.track(configId);
 | 
			
		||||
        return config;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -157,7 +156,8 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    PlotController.prototype.destroy = function () {
 | 
			
		||||
        configStore.untrack(this.config.id);
 | 
			
		||||
        configStore.deleteStore(this.config.id);
 | 
			
		||||
 | 
			
		||||
        this.stopListening();
 | 
			
		||||
        if (this.checkForSize) {
 | 
			
		||||
            clearInterval(this.checkForSize);
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
define([
 | 
			
		||||
    'lodash',
 | 
			
		||||
    './utcTimeSystem/plugin',
 | 
			
		||||
    './localTimeSystem/plugin',
 | 
			
		||||
    '../../example/generator/plugin',
 | 
			
		||||
    './autoflow/AutoflowTabularPlugin',
 | 
			
		||||
    './timeConductor/plugin',
 | 
			
		||||
@@ -46,6 +47,7 @@ define([
 | 
			
		||||
], function (
 | 
			
		||||
    _,
 | 
			
		||||
    UTCTimeSystem,
 | 
			
		||||
    LocalTimeSystem,
 | 
			
		||||
    GeneratorPlugin,
 | 
			
		||||
    AutoflowPlugin,
 | 
			
		||||
    TimeConductorPlugin,
 | 
			
		||||
@@ -81,6 +83,7 @@ define([
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    plugins.UTCTimeSystem = UTCTimeSystem;
 | 
			
		||||
    plugins.LocalTimeSystem = LocalTimeSystem;
 | 
			
		||||
 | 
			
		||||
    plugins.ImportExport = ImportExport;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -70,16 +70,14 @@ define([
 | 
			
		||||
         */
 | 
			
		||||
        function onValueInput(event) {
 | 
			
		||||
            var elem = event.target,
 | 
			
		||||
                value = (isNaN(elem.valueAsNumber) ? elem.value : elem.valueAsNumber),
 | 
			
		||||
                value = isNaN(Number(elem.value)) ? elem.value : Number(elem.value),
 | 
			
		||||
                inputIndex = self.valueInputs.indexOf(elem);
 | 
			
		||||
 | 
			
		||||
            if (elem.tagName.toUpperCase() === 'INPUT') {
 | 
			
		||||
                self.eventEmitter.emit('change', {
 | 
			
		||||
                    value: value,
 | 
			
		||||
                    property: 'values[' + inputIndex + ']',
 | 
			
		||||
                    index: self.index
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            self.eventEmitter.emit('change', {
 | 
			
		||||
                value: value,
 | 
			
		||||
                property: 'values[' + inputIndex + ']',
 | 
			
		||||
                index: self.index
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.listenTo(this.deleteButton, 'click', this.remove, this);
 | 
			
		||||
@@ -108,8 +106,7 @@ define([
 | 
			
		||||
        Object.values(this.selects).forEach(function (select) {
 | 
			
		||||
            $('.t-configuration', self.domElement).append(select.getDOM());
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.listenTo($(this.domElement), 'input', onValueInput);
 | 
			
		||||
        this.listenTo($('.t-value-inputs', this.domElement), 'input', onValueInput);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Condition.prototype.getDOM = function (container) {
 | 
			
		||||
@@ -167,7 +164,9 @@ define([
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * When an operation is selected, create the appropriate value inputs
 | 
			
		||||
     * and add them to the view
 | 
			
		||||
     * and add them to the view. If an operation is of type enum, create
 | 
			
		||||
     * a drop-down menu instead.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {string} operation The key of currently selected operation
 | 
			
		||||
     */
 | 
			
		||||
    Condition.prototype.generateValueInputs = function (operation) {
 | 
			
		||||
@@ -176,25 +175,49 @@ define([
 | 
			
		||||
            inputCount,
 | 
			
		||||
            inputType,
 | 
			
		||||
            newInput,
 | 
			
		||||
            index = 0;
 | 
			
		||||
            index = 0,
 | 
			
		||||
            emitChange = false;
 | 
			
		||||
 | 
			
		||||
        inputArea.html('');
 | 
			
		||||
        this.valueInputs = [];
 | 
			
		||||
        this.config.values = [];
 | 
			
		||||
 | 
			
		||||
        if (evaluator.getInputCount(operation)) {
 | 
			
		||||
            inputCount = evaluator.getInputCount(operation);
 | 
			
		||||
            inputType = evaluator.getInputType(operation);
 | 
			
		||||
 | 
			
		||||
            while (index < inputCount) {
 | 
			
		||||
                if (!this.config.values[index]) {
 | 
			
		||||
                    this.config.values[index] = (inputType === 'number' ? 0 : '');
 | 
			
		||||
                if (inputType === 'select') {
 | 
			
		||||
                    newInput = $('<select>' + this.generateSelectOptions() + '</select>');
 | 
			
		||||
                    emitChange = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.config.values[index] = inputType === 'number' ? 0 : '';
 | 
			
		||||
                    newInput = $('<input type = "' + inputType + '" value = "' + this.config.values[index] + '"> </input>');
 | 
			
		||||
                }
 | 
			
		||||
                newInput = $('<input type = "' + inputType + '" value = "' + this.config.values[index] + '"> </input>');
 | 
			
		||||
 | 
			
		||||
                this.valueInputs.push(newInput.get(0));
 | 
			
		||||
                inputArea.append(newInput);
 | 
			
		||||
                index += 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (emitChange) {
 | 
			
		||||
                this.eventEmitter.emit('change', {
 | 
			
		||||
                    value: Number(newInput[0].options[0].value),
 | 
			
		||||
                    property: 'values[0]',
 | 
			
		||||
                    index: this.index
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Condition.prototype.generateSelectOptions = function () {
 | 
			
		||||
        let telemetryMetadata = this.conditionManager.getTelemetryMetadata(this.config.object);
 | 
			
		||||
        let options = '';
 | 
			
		||||
        telemetryMetadata[this.config.key].enumerations.forEach(enumeration => {
 | 
			
		||||
            options += '<option value="' + enumeration.value + '">'+ enumeration.string + '</option>';
 | 
			
		||||
        });
 | 
			
		||||
        return options;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return Condition;
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,8 @@ define([], function () {
 | 
			
		||||
         */
 | 
			
		||||
        this.inputTypes = {
 | 
			
		||||
            number: 'number',
 | 
			
		||||
            string: 'text'
 | 
			
		||||
            string: 'text',
 | 
			
		||||
            enum: 'select'
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -34,7 +35,8 @@ define([], function () {
 | 
			
		||||
         */
 | 
			
		||||
        this.inputValidators = {
 | 
			
		||||
            number: this.validateNumberInput,
 | 
			
		||||
            string: this.validateStringInput
 | 
			
		||||
            string: this.validateStringInput,
 | 
			
		||||
            enum: this.validateNumberInput
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
@@ -201,7 +203,7 @@ define([], function () {
 | 
			
		||||
                    return typeof input[0] === 'undefined';
 | 
			
		||||
                },
 | 
			
		||||
                text: 'is undefined',
 | 
			
		||||
                appliesTo: ['string', 'number'],
 | 
			
		||||
                appliesTo: ['string', 'number', 'enum'],
 | 
			
		||||
                inputCount: 0,
 | 
			
		||||
                getDescription: function () {
 | 
			
		||||
                    return ' is undefined';
 | 
			
		||||
@@ -212,11 +214,33 @@ define([], function () {
 | 
			
		||||
                    return typeof input[0] !== 'undefined';
 | 
			
		||||
                },
 | 
			
		||||
                text: 'is defined',
 | 
			
		||||
                appliesTo: ['string', 'number'],
 | 
			
		||||
                appliesTo: ['string', 'number', 'enum'],
 | 
			
		||||
                inputCount: 0,
 | 
			
		||||
                getDescription: function () {
 | 
			
		||||
                    return ' is defined';
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            enumValueIs: {
 | 
			
		||||
                operation: function (input) {
 | 
			
		||||
                    return input[0] === input[1];
 | 
			
		||||
                },
 | 
			
		||||
                text: 'is',
 | 
			
		||||
                appliesTo: ['enum'],
 | 
			
		||||
                inputCount: 1,
 | 
			
		||||
                getDescription: function (values) {
 | 
			
		||||
                    return ' == ' + values[0];
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            enumValueIsNot: {
 | 
			
		||||
                operation: function (input) {
 | 
			
		||||
                    return input[0] !== input[1];
 | 
			
		||||
                },
 | 
			
		||||
                text: 'is not',
 | 
			
		||||
                appliesTo: ['enum'],
 | 
			
		||||
                inputCount: 1,
 | 
			
		||||
                getDescription: function (values) {
 | 
			
		||||
                    return ' != ' + values[0];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
@@ -310,13 +334,16 @@ define([], function () {
 | 
			
		||||
            validator;
 | 
			
		||||
 | 
			
		||||
        if (cache[object] && typeof cache[object][key] !== 'undefined') {
 | 
			
		||||
            telemetryValue = [cache[object][key]];
 | 
			
		||||
            let value = cache[object][key];
 | 
			
		||||
            telemetryValue = [isNaN(Number(value)) ? value : Number(value)];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        op = this.operations[operation] && this.operations[operation].operation;
 | 
			
		||||
        input = telemetryValue && telemetryValue.concat(values);
 | 
			
		||||
        validator = op && this.inputValidators[this.operations[operation].appliesTo[0]];
 | 
			
		||||
 | 
			
		||||
        if (op && input && validator) {
 | 
			
		||||
            if (this.operations[operation].appliesTo.length === 2) {
 | 
			
		||||
            if (this.operations[operation].appliesTo.length > 1) {
 | 
			
		||||
                return (this.validateNumberInput(input) || this.validateStringInput(input)) && op(input);
 | 
			
		||||
            } else {
 | 
			
		||||
                return validator(input) && op(input);
 | 
			
		||||
@@ -372,7 +399,7 @@ define([], function () {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns true only of the given operation applies to a given type
 | 
			
		||||
     * Returns true only if the given operation applies to a given type
 | 
			
		||||
     * @param {string} key The key of the operation
 | 
			
		||||
     * @param {string} type The value type to query
 | 
			
		||||
     * @returns {boolean} True if the condition applies, false otherwise
 | 
			
		||||
 
 | 
			
		||||
@@ -130,7 +130,9 @@ define ([
 | 
			
		||||
        this.telemetryTypesById[objectId] = {};
 | 
			
		||||
        Object.values(this.telemetryMetadataById[objectId]).forEach(function (valueMetadata) {
 | 
			
		||||
            var type;
 | 
			
		||||
            if (valueMetadata.hints.hasOwnProperty('range')) {
 | 
			
		||||
            if (valueMetadata.enumerations !== undefined) {
 | 
			
		||||
                type = 'enum';
 | 
			
		||||
            } else if (valueMetadata.hints.hasOwnProperty('range')) {
 | 
			
		||||
                type = 'number';
 | 
			
		||||
            } else if (valueMetadata.hints.hasOwnProperty('domain')) {
 | 
			
		||||
                type = 'number';
 | 
			
		||||
@@ -163,11 +165,18 @@ define ([
 | 
			
		||||
     * @param {datum} datum The new data from the telemetry source
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    ConditionManager.prototype.handleSubscriptionCallback = function (objId, datum) {
 | 
			
		||||
        this.subscriptionCache[objId] = datum;
 | 
			
		||||
    ConditionManager.prototype.handleSubscriptionCallback = function (objId, telemetryDatum) {
 | 
			
		||||
        this.subscriptionCache[objId] = this.createNormalizedDatum(objId, telemetryDatum);
 | 
			
		||||
        this.eventEmitter.emit('receiveTelemetry');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    ConditionManager.prototype.createNormalizedDatum = function (objId, telemetryDatum) {
 | 
			
		||||
        return Object.values(this.telemetryMetadataById[objId]).reduce((normalizedDatum, metadatum) => {
 | 
			
		||||
            normalizedDatum[metadatum.key] = telemetryDatum[metadatum.source];
 | 
			
		||||
            return normalizedDatum;
 | 
			
		||||
        }, {});
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Event handler for an add event in this Summary Widget's composition.
 | 
			
		||||
     * Sets up subscription handlers and parses its property types.
 | 
			
		||||
@@ -236,6 +245,7 @@ define ([
 | 
			
		||||
                id.namespace === identifier.namespace;
 | 
			
		||||
        });
 | 
			
		||||
        delete this.compositionObjs[objectId];
 | 
			
		||||
        delete this.subscriptionCache[objectId];
 | 
			
		||||
        this.subscriptions[objectId](); //unsubscribe from telemetry source
 | 
			
		||||
        delete this.subscriptions[objectId];
 | 
			
		||||
        this.eventEmitter.emit('remove', identifier);
 | 
			
		||||
 
 | 
			
		||||
@@ -110,9 +110,11 @@ define([
 | 
			
		||||
 | 
			
		||||
        type = self.manager.getTelemetryPropertyType(self.config.object, key);
 | 
			
		||||
 | 
			
		||||
        self.operationKeys = operations.filter(function (operation) {
 | 
			
		||||
            return self.evaluator.operationAppliesTo(operation, type);
 | 
			
		||||
        });
 | 
			
		||||
        if (type !== undefined) {
 | 
			
		||||
            self.operationKeys = operations.filter(function (operation) {
 | 
			
		||||
                return self.evaluator.operationAppliesTo(operation, type);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OperationSelect.prototype.destroy = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ define([
 | 
			
		||||
        return this.openmct.time.getAllTimeSystems().map(function (ts, i) {
 | 
			
		||||
            return {
 | 
			
		||||
                key: ts.key,
 | 
			
		||||
                name: 'UTC',
 | 
			
		||||
                name: ts.name,
 | 
			
		||||
                format: ts.timeFormat,
 | 
			
		||||
                hints: {
 | 
			
		||||
                    domain: i
 | 
			
		||||
@@ -64,7 +64,7 @@ define([
 | 
			
		||||
            // Generally safe assumption is that we have one domain per timeSystem.
 | 
			
		||||
            values: this.getDomains().concat([
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'state',
 | 
			
		||||
                    name: 'State',
 | 
			
		||||
                    key: 'state',
 | 
			
		||||
                    source: 'ruleIndex',
 | 
			
		||||
                    format: 'enum',
 | 
			
		||||
 
 | 
			
		||||
@@ -174,7 +174,7 @@ define([
 | 
			
		||||
                return typeof input[0] === 'undefined';
 | 
			
		||||
            },
 | 
			
		||||
            text: 'is undefined',
 | 
			
		||||
            appliesTo: ['string', 'number'],
 | 
			
		||||
            appliesTo: ['string', 'number', 'enum'],
 | 
			
		||||
            inputCount: 0,
 | 
			
		||||
            getDescription: function () {
 | 
			
		||||
                return ' is undefined';
 | 
			
		||||
@@ -185,11 +185,33 @@ define([
 | 
			
		||||
                return typeof input[0] !== 'undefined';
 | 
			
		||||
            },
 | 
			
		||||
            text: 'is defined',
 | 
			
		||||
            appliesTo: ['string', 'number'],
 | 
			
		||||
            appliesTo: ['string', 'number', 'enum'],
 | 
			
		||||
            inputCount: 0,
 | 
			
		||||
            getDescription: function () {
 | 
			
		||||
                return ' is defined';
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        enumValueIs: {
 | 
			
		||||
            operation: function (input) {
 | 
			
		||||
                return input[0] === input[1];
 | 
			
		||||
            },
 | 
			
		||||
            text: 'is',
 | 
			
		||||
            appliesTo: ['enum'],
 | 
			
		||||
            inputCount: 1,
 | 
			
		||||
            getDescription: function (values) {
 | 
			
		||||
                return ' == ' + values[0];
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        enumValueIsNot: {
 | 
			
		||||
            operation: function (input) {
 | 
			
		||||
                return input[0] !== input[1];
 | 
			
		||||
            },
 | 
			
		||||
            text: 'is not',
 | 
			
		||||
            appliesTo: ['enum'],
 | 
			
		||||
            inputCount: 1,
 | 
			
		||||
            getDescription: function (values) {
 | 
			
		||||
                return ' != ' + values[0];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ define([
 | 
			
		||||
            key: 'table-configuration',
 | 
			
		||||
            name: 'Telemetry Table Configuration',
 | 
			
		||||
            canView: function (selection) {
 | 
			
		||||
                if (selection.length === 0 || selection[0].length === 0) {
 | 
			
		||||
                if (selection.length !== 1 || selection[0].length === 0) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                let object = selection[0][0].context.item;
 | 
			
		||||
 
 | 
			
		||||
@@ -45,8 +45,10 @@ define([
 | 
			
		||||
            this.subscriptions = {};
 | 
			
		||||
            this.tableComposition = undefined;
 | 
			
		||||
            this.telemetryObjects = [];
 | 
			
		||||
            this.rowBuffer = [];
 | 
			
		||||
            this.outstandingRequests = 0;
 | 
			
		||||
            this.configuration = new TelemetryTableConfiguration(domainObject, openmct);
 | 
			
		||||
            this.paused = false;
 | 
			
		||||
 | 
			
		||||
            this.addTelemetryObject = this.addTelemetryObject.bind(this);
 | 
			
		||||
            this.removeTelemetryObject = this.removeTelemetryObject.bind(this);
 | 
			
		||||
@@ -202,7 +204,11 @@ define([
 | 
			
		||||
                if (!this.telemetryObjects.includes(telemetryObject)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
 | 
			
		||||
 | 
			
		||||
                if (!this.paused) {
 | 
			
		||||
                    let row = new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator);
 | 
			
		||||
                    this.boundedRows.add(row);
 | 
			
		||||
                }
 | 
			
		||||
            }, subscribeOptions);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -234,6 +240,17 @@ define([
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pause() {
 | 
			
		||||
            this.paused = true;
 | 
			
		||||
            this.boundedRows.unsubscribeFromBounds();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        unpause() {
 | 
			
		||||
            this.paused = false;
 | 
			
		||||
            this.boundedRows.subscribeToBounds();
 | 
			
		||||
            this.refreshData();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        destroy() {
 | 
			
		||||
            this.boundedRows.destroy();
 | 
			
		||||
            this.filteredRows.destroy();
 | 
			
		||||
 
 | 
			
		||||
@@ -31,9 +31,9 @@ define(
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
        class BoundedTableRowCollection extends SortedTableRowCollection {
 | 
			
		||||
            constructor (openmct) {
 | 
			
		||||
            constructor(openmct) {
 | 
			
		||||
                super();
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                this.futureBuffer = new SortedTableRowCollection();
 | 
			
		||||
                this.openmct = openmct;
 | 
			
		||||
 | 
			
		||||
@@ -43,15 +43,17 @@ define(
 | 
			
		||||
                this.sortByTimeSystem(openmct.time.timeSystem());
 | 
			
		||||
 | 
			
		||||
                this.lastBounds = openmct.time.bounds();
 | 
			
		||||
                openmct.time.on('bounds', this.bounds);
 | 
			
		||||
 | 
			
		||||
                this.subscribeToBounds();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            addOne (item) {
 | 
			
		||||
            addOne(item) {
 | 
			
		||||
                let parsedValue = this.getValueForSortColumn(item);
 | 
			
		||||
                // Insert into either in-bounds array, or the future buffer.
 | 
			
		||||
                // Data in the future buffer will be re-evaluated for possible 
 | 
			
		||||
                // Data in the future buffer will be re-evaluated for possible
 | 
			
		||||
                // insertion on next bounds change
 | 
			
		||||
                let beforeStartOfBounds = this.parseTime(item.datum[this.sortOptions.key]) < this.lastBounds.start;
 | 
			
		||||
                let afterEndOfBounds = this.parseTime(item.datum[this.sortOptions.key]) > this.lastBounds.end;
 | 
			
		||||
                let beforeStartOfBounds = parsedValue < this.lastBounds.start;
 | 
			
		||||
                let afterEndOfBounds = parsedValue > this.lastBounds.end;
 | 
			
		||||
 | 
			
		||||
                if (!afterEndOfBounds && !beforeStartOfBounds) {
 | 
			
		||||
                    return super.addOne(item);
 | 
			
		||||
@@ -86,13 +88,13 @@ define(
 | 
			
		||||
             * @fires TelemetryCollection#discarded
 | 
			
		||||
             * @param bounds
 | 
			
		||||
             */
 | 
			
		||||
            bounds (bounds) {
 | 
			
		||||
            bounds(bounds) {
 | 
			
		||||
                let startChanged = this.lastBounds.start !== bounds.start;
 | 
			
		||||
                let endChanged = this.lastBounds.end !== bounds.end;
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                let startIndex = 0;
 | 
			
		||||
                let endIndex = 0;
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                let discarded = [];
 | 
			
		||||
                let added = [];
 | 
			
		||||
                let testValue = {
 | 
			
		||||
@@ -135,9 +137,21 @@ define(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            destroy() {
 | 
			
		||||
            getValueForSortColumn(row) {
 | 
			
		||||
                return this.parseTime(row.datum[this.sortOptions.key]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            unsubscribeFromBounds() {
 | 
			
		||||
                this.openmct.time.off('bounds', this.bounds);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            subscribeToBounds() {
 | 
			
		||||
                this.openmct.time.on('bounds', this.bounds);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            destroy() {
 | 
			
		||||
                this.unsubscribeFromBounds();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    return BoundedTableRowCollection;
 | 
			
		||||
});
 | 
			
		||||
        return BoundedTableRowCollection;
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@ define(
 | 
			
		||||
                    if (rowsAdded.length > 0) {
 | 
			
		||||
                        this.emit('add', rowsAdded);
 | 
			
		||||
                    }
 | 
			
		||||
                    this.dupeCheck = true;    
 | 
			
		||||
                    this.dupeCheck = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    let wasAdded = this.addOne(rows);
 | 
			
		||||
                    if (wasAdded) {
 | 
			
		||||
@@ -115,11 +115,10 @@ define(
 | 
			
		||||
                if (this.rows.length === 0) {
 | 
			
		||||
                    return 0;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                const sortOptionsKey = this.sortOptions.key;
 | 
			
		||||
                const testRowValue = testRow.datum[sortOptionsKey];
 | 
			
		||||
                const firstValue = this.rows[0].datum[sortOptionsKey];
 | 
			
		||||
                const lastValue = this.rows[this.rows.length - 1].datum[sortOptionsKey];
 | 
			
		||||
 | 
			
		||||
                const testRowValue = this.getValueForSortColumn(testRow);
 | 
			
		||||
                const firstValue = this.getValueForSortColumn(this.rows[0]);
 | 
			
		||||
                const lastValue = this.getValueForSortColumn(this.rows[this.rows.length - 1]);
 | 
			
		||||
 | 
			
		||||
                lodashFunction = lodashFunction || _.sortedIndex;
 | 
			
		||||
 | 
			
		||||
@@ -133,7 +132,7 @@ define(
 | 
			
		||||
                        return 0;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return lodashFunction(rows, testRow, (thisRow) => {
 | 
			
		||||
                            return thisRow.datum[sortOptionsKey];
 | 
			
		||||
                            return this.getValueForSortColumn(thisRow);
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -147,7 +146,7 @@ define(
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // Use a custom comparison function to support descending sort.
 | 
			
		||||
                        return lodashFunction(rows, testRow, (thisRow) => {
 | 
			
		||||
                            const thisRowValue = thisRow.datum[sortOptionsKey];
 | 
			
		||||
                            const thisRowValue = this.getValueForSortColumn(thisRow);
 | 
			
		||||
                            if (testRowValue === thisRowValue) {
 | 
			
		||||
                                return EQUAL;
 | 
			
		||||
                            } else if (testRowValue < thisRowValue) {
 | 
			
		||||
@@ -206,7 +205,7 @@ define(
 | 
			
		||||
                    this.emit('sort');
 | 
			
		||||
                }
 | 
			
		||||
                // Return duplicate to avoid direct modification of underlying object
 | 
			
		||||
                return Object.assign({}, this.sortOptions); 
 | 
			
		||||
                return Object.assign({}, this.sortOptions);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            removeAllRowsForObject(objectKeyString) {
 | 
			
		||||
@@ -218,25 +217,32 @@ define(
 | 
			
		||||
                    }
 | 
			
		||||
                    return true;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                this.emit('remove', removed);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            getValueForSortColumn(row) {
 | 
			
		||||
                return row.datum[this.sortOptions.key];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            remove(removedRows) {
 | 
			
		||||
                this.rows = this.rows.filter(row => {
 | 
			
		||||
                    return removedRows.indexOf(row) === -1;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                this.emit('remove', removedRows);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            getRows () {
 | 
			
		||||
            getRows() {
 | 
			
		||||
                return this.rows;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            clear() {
 | 
			
		||||
                let removedRows = this.rows;
 | 
			
		||||
                this.rows = [];
 | 
			
		||||
 | 
			
		||||
                this.emit('remove', removedRows);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    return SortedTableRowCollection;
 | 
			
		||||
});
 | 
			
		||||
        return SortedTableRowCollection;
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,13 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
<template>
 | 
			
		||||
<tr :style="{ top: rowTop }" :class="rowLimitClass">
 | 
			
		||||
<tr :style="{ top: rowTop }"
 | 
			
		||||
    class="noselect"
 | 
			
		||||
    :class="[
 | 
			
		||||
        rowLimitClass,
 | 
			
		||||
        {'is-selected': marked}
 | 
			
		||||
    ]"
 | 
			
		||||
    @click="markRow">
 | 
			
		||||
    <td v-for="(title, key) in headers" 
 | 
			
		||||
        :key="key"
 | 
			
		||||
        :style="columnWidths[key] === undefined ? {} : { width: columnWidths[key] + 'px', 'max-width': columnWidths[key] + 'px'}"
 | 
			
		||||
@@ -30,6 +36,15 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
    .noselect {
 | 
			
		||||
    -webkit-touch-callout: none; /* iOS Safari */
 | 
			
		||||
        -webkit-user-select: none; /* Safari */
 | 
			
		||||
        -khtml-user-select: none; /* Konqueror HTML */
 | 
			
		||||
        -moz-user-select: none; /* Firefox */
 | 
			
		||||
            -ms-user-select: none; /* Internet Explorer/Edge */
 | 
			
		||||
                user-select: none; /* Non-prefixed version, currently
 | 
			
		||||
                                    supported by Chrome and Opera */
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
@@ -69,6 +84,11 @@ export default {
 | 
			
		||||
            type: Number,
 | 
			
		||||
            required: false,
 | 
			
		||||
            default: 0
 | 
			
		||||
        },
 | 
			
		||||
        marked: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            required: false,
 | 
			
		||||
            default: false
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
@@ -79,6 +99,17 @@ export default {
 | 
			
		||||
            this.formattedRow = row.getFormattedDatum(this.headers);
 | 
			
		||||
            this.rowLimitClass = row.getRowLimitClass();
 | 
			
		||||
            this.cellLimitClasses = row.getCellLimitClasses();
 | 
			
		||||
        },
 | 
			
		||||
        markRow: function (event) {
 | 
			
		||||
            if (event.shiftKey) {
 | 
			
		||||
                this.$emit('markMultipleConcurrent', this.rowIndex);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (this.marked) {
 | 
			
		||||
                    this.$emit('unmark', this.rowIndex);
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.$emit('mark', this.rowIndex);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    // TODO: use computed properties
 | 
			
		||||
 
 | 
			
		||||
@@ -20,80 +20,111 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
<template>
 | 
			
		||||
<div class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
 | 
			
		||||
     :class="{'loading': loading}">
 | 
			
		||||
    <div class="c-table__control-bar c-control-bar">
 | 
			
		||||
<div class="c-table-wrapper">
 | 
			
		||||
    <div class="c-table-control-bar c-control-bar">
 | 
			
		||||
        <button class="c-button icon-download labeled"
 | 
			
		||||
           v-on:click="exportAsCSV()"
 | 
			
		||||
           v-on:click="exportAllDataAsCSV()"
 | 
			
		||||
           title="Export This View's Data">
 | 
			
		||||
            <span class="c-button__label">Export As CSV</span>
 | 
			
		||||
            <span class="c-button__label">Export Table Data</span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="c-button icon-download labeled"
 | 
			
		||||
            v-show="markedRows.length"
 | 
			
		||||
            v-on:click="exportMarkedDataAsCSV()"
 | 
			
		||||
            title="Export Marked Rows As CSV">
 | 
			
		||||
            <span class="c-button__label">Export Marked Rows</span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button class="c-button icon-x labeled"
 | 
			
		||||
            v-show="markedRows.length"
 | 
			
		||||
            v-on:click="unmarkAllRows()"
 | 
			
		||||
            title="Unmark All Rows">
 | 
			
		||||
            <span class="c-button__label">Unmark All Rows</span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <div class="c-separator"></div>
 | 
			
		||||
        <button class="c-button icon-pause pause-play labeled"
 | 
			
		||||
                :class=" paused ? 'icon-play is-paused' : 'icon-pause'"
 | 
			
		||||
            v-on:click="togglePauseByButton()"
 | 
			
		||||
            :title="paused ? 'Continue Data Flow' : 'Pause Data Flow'">
 | 
			
		||||
            <span class="c-button__label">
 | 
			
		||||
                {{paused ? 'Play' : 'Pause'}}
 | 
			
		||||
            </span>
 | 
			
		||||
        </button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-if="isDropTargetActive" class="c-telemetry-table__drop-target" :style="dropTargetStyle"></div>
 | 
			
		||||
    <!-- Headers table -->
 | 
			
		||||
    <div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable" :style="{ 'max-width': widthWithScroll}">
 | 
			
		||||
        <table class="c-table__headers c-telemetry-table__headers">
 | 
			
		||||
            <thead>
 | 
			
		||||
                <tr class="c-telemetry-table__headers__labels">
 | 
			
		||||
                    <table-column-header
 | 
			
		||||
                        v-for="(title, key, headerIndex) in headers"
 | 
			
		||||
                        :key="key"
 | 
			
		||||
                        :headerKey="key"
 | 
			
		||||
                        :headerIndex="headerIndex"
 | 
			
		||||
                        @sort="sortBy(key)"
 | 
			
		||||
                        @resizeColumn="resizeColumn"
 | 
			
		||||
                        @dropTargetOffsetChanged="setDropTargetOffset"
 | 
			
		||||
                        @dropTargetActive="dropTargetActive"
 | 
			
		||||
                        @reorderColumn="reorderColumn"
 | 
			
		||||
                        @resizeColumnEnd="updateConfiguredColumnWidths"
 | 
			
		||||
                        :columnWidth="columnWidths[key]"
 | 
			
		||||
                        :sortOptions="sortOptions"
 | 
			
		||||
                        :isEditing="isEditing"
 | 
			
		||||
                    ><span class="c-telemetry-table__headers__label">{{title}}</span>
 | 
			
		||||
                    </table-column-header>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr class="c-telemetry-table__headers__filter">
 | 
			
		||||
                    <table-column-header
 | 
			
		||||
                        v-for="(title, key, headerIndex) in headers"
 | 
			
		||||
                        :key="key"
 | 
			
		||||
                        :headerKey="key"
 | 
			
		||||
                        :headerIndex="headerIndex"
 | 
			
		||||
                        @resizeColumn="resizeColumn"
 | 
			
		||||
                        @dropTargetOffsetChanged="setDropTargetOffset"
 | 
			
		||||
                        @dropTargetActive="dropTargetActive"
 | 
			
		||||
                        @reorderColumn="reorderColumn"
 | 
			
		||||
                        @resizeColumnEnd="updateConfiguredColumnWidths"
 | 
			
		||||
                        :columnWidth="columnWidths[key]"
 | 
			
		||||
                        :isEditing="isEditing"
 | 
			
		||||
                        >
 | 
			
		||||
                        <search class="c-table__search"
 | 
			
		||||
                            v-model="filters[key]"
 | 
			
		||||
                            v-on:input="filterChanged(key)"
 | 
			
		||||
                            v-on:clear="clearFilter(key)" />
 | 
			
		||||
                    </table-column-header>
 | 
			
		||||
                </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
        </table>
 | 
			
		||||
    </div>
 | 
			
		||||
    <!-- Content table -->
 | 
			
		||||
    <div class="c-table__body-w c-telemetry-table__body-w js-telemetry-table__body-w" @scroll="scroll" :style="{ 'max-width': widthWithScroll}">
 | 
			
		||||
        <div class="c-telemetry-table__scroll-forcer" :style="{ width: totalWidth + 'px' }"></div>
 | 
			
		||||
        <table class="c-table__body c-telemetry-table__body js-telemetry-table__content"
 | 
			
		||||
               :style="{ height: totalHeight + 'px'}">
 | 
			
		||||
            <tbody>
 | 
			
		||||
                <telemetry-table-row v-for="(row, rowIndex) in visibleRows"
 | 
			
		||||
                    :headers="headers"
 | 
			
		||||
                    :columnWidths="columnWidths"
 | 
			
		||||
                    :rowIndex="rowIndex"
 | 
			
		||||
                    :rowOffset="rowOffset"
 | 
			
		||||
                    :rowHeight="rowHeight"
 | 
			
		||||
                    :row="row">
 | 
			
		||||
                </telemetry-table-row>
 | 
			
		||||
            </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
    </div>
 | 
			
		||||
    <!-- Sizing table -->
 | 
			
		||||
    <table class="c-telemetry-table__sizing js-telemetry-table__sizing" :style="sizingTableWidth">
 | 
			
		||||
 | 
			
		||||
    <div class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
 | 
			
		||||
         :class="{
 | 
			
		||||
         'loading': loading,
 | 
			
		||||
         'paused' : paused
 | 
			
		||||
        }">
 | 
			
		||||
        <div v-if="isDropTargetActive" class="c-telemetry-table__drop-target" :style="dropTargetStyle"></div>
 | 
			
		||||
        <!-- Headers table -->
 | 
			
		||||
        <div class="c-telemetry-table__headers-w js-table__headers-w" ref="headersTable" :style="{ 'max-width': widthWithScroll}">
 | 
			
		||||
            <table class="c-table__headers c-telemetry-table__headers">
 | 
			
		||||
                <thead>
 | 
			
		||||
                    <tr class="c-telemetry-table__headers__labels">
 | 
			
		||||
                        <table-column-header
 | 
			
		||||
                            v-for="(title, key, headerIndex) in headers"
 | 
			
		||||
                            :key="key"
 | 
			
		||||
                            :headerKey="key"
 | 
			
		||||
                            :headerIndex="headerIndex"
 | 
			
		||||
                            @sort="sortBy(key)"
 | 
			
		||||
                            @resizeColumn="resizeColumn"
 | 
			
		||||
                            @dropTargetOffsetChanged="setDropTargetOffset"
 | 
			
		||||
                            @dropTargetActive="dropTargetActive"
 | 
			
		||||
                            @reorderColumn="reorderColumn"
 | 
			
		||||
                            @resizeColumnEnd="updateConfiguredColumnWidths"
 | 
			
		||||
                            :columnWidth="columnWidths[key]"
 | 
			
		||||
                            :sortOptions="sortOptions"
 | 
			
		||||
                            :isEditing="isEditing"
 | 
			
		||||
                        ><span class="c-telemetry-table__headers__label">{{title}}</span>
 | 
			
		||||
                        </table-column-header>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                    <tr class="c-telemetry-table__headers__filter">
 | 
			
		||||
                        <table-column-header
 | 
			
		||||
                            v-for="(title, key, headerIndex) in headers"
 | 
			
		||||
                            :key="key"
 | 
			
		||||
                            :headerKey="key"
 | 
			
		||||
                            :headerIndex="headerIndex"
 | 
			
		||||
                            @resizeColumn="resizeColumn"
 | 
			
		||||
                            @dropTargetOffsetChanged="setDropTargetOffset"
 | 
			
		||||
                            @dropTargetActive="dropTargetActive"
 | 
			
		||||
                            @reorderColumn="reorderColumn"
 | 
			
		||||
                            @resizeColumnEnd="updateConfiguredColumnWidths"
 | 
			
		||||
                            :columnWidth="columnWidths[key]"
 | 
			
		||||
                            :isEditing="isEditing"
 | 
			
		||||
                            >
 | 
			
		||||
                            <search class="c-table__search"
 | 
			
		||||
                                v-model="filters[key]"
 | 
			
		||||
                                v-on:input="filterChanged(key)"
 | 
			
		||||
                                v-on:clear="clearFilter(key)" />
 | 
			
		||||
                        </table-column-header>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                </thead>
 | 
			
		||||
            </table>
 | 
			
		||||
        </div>
 | 
			
		||||
        <!-- Content table -->
 | 
			
		||||
        <div class="c-table__body-w c-telemetry-table__body-w js-telemetry-table__body-w" @scroll="scroll" :style="{ 'max-width': widthWithScroll}">
 | 
			
		||||
            <div class="c-telemetry-table__scroll-forcer" :style="{ width: totalWidth + 'px' }"></div>
 | 
			
		||||
            <table class="c-table__body c-telemetry-table__body js-telemetry-table__content"
 | 
			
		||||
                   :style="{ height: totalHeight + 'px'}">
 | 
			
		||||
                <tbody>
 | 
			
		||||
                    <telemetry-table-row v-for="(row, rowIndex) in visibleRows"
 | 
			
		||||
                        :key="rowIndex"
 | 
			
		||||
                        :headers="headers"
 | 
			
		||||
                        :columnWidths="columnWidths"
 | 
			
		||||
                        :rowIndex="rowIndex"
 | 
			
		||||
                        :rowOffset="rowOffset"
 | 
			
		||||
                        :rowHeight="rowHeight"
 | 
			
		||||
                        :row="row"
 | 
			
		||||
                        :marked="row.marked"
 | 
			
		||||
                        @mark="markRow"
 | 
			
		||||
                        @unmark="unmarkRow"
 | 
			
		||||
                        @markMultipleConcurrent="markMultipleConcurrentRows">
 | 
			
		||||
                    </telemetry-table-row>
 | 
			
		||||
                </tbody>
 | 
			
		||||
            </table>
 | 
			
		||||
        </div>
 | 
			
		||||
        <!-- Sizing table -->
 | 
			
		||||
        <table class="c-telemetry-table__sizing js-telemetry-table__sizing" :style="sizingTableWidth">
 | 
			
		||||
        <tr>
 | 
			
		||||
            <template v-for="(title, key) in headers">
 | 
			
		||||
            <th :key="key" :style="{ width: configuredColumnWidths[key] + 'px', 'max-width': configuredColumnWidths[key] + 'px'}">{{title}}</th>
 | 
			
		||||
@@ -105,7 +136,8 @@
 | 
			
		||||
            :row="sizingRowData">
 | 
			
		||||
        </telemetry-table-row>
 | 
			
		||||
    </table>
 | 
			
		||||
</div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div><!-- closes c-table-wrapper -->
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
@@ -216,6 +248,10 @@
 | 
			
		||||
                align-items: stretch;
 | 
			
		||||
                position: absolute;
 | 
			
		||||
                height: 18px; // Needed when a row has empty values in its cells
 | 
			
		||||
 | 
			
		||||
                &.is-selected {
 | 
			
		||||
                    background-color: $colorSelectedBg;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            td {
 | 
			
		||||
@@ -266,6 +302,10 @@
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .paused {
 | 
			
		||||
        border: 1px solid #ff9900;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /******************************* LEGACY */
 | 
			
		||||
    .s-status-taking-snapshot,
 | 
			
		||||
    .overlay.snapshot {
 | 
			
		||||
@@ -328,7 +368,10 @@ export default {
 | 
			
		||||
            dropOffsetLeft: undefined,
 | 
			
		||||
            isDropTargetActive: false,
 | 
			
		||||
            isAutosizeEnabled: configuration.autosize,
 | 
			
		||||
            scrollW: 0
 | 
			
		||||
            scrollW: 0,
 | 
			
		||||
            markCounter: 0,
 | 
			
		||||
            paused: false,
 | 
			
		||||
            markedRows: []
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
@@ -472,10 +515,12 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        filterChanged(columnKey) {
 | 
			
		||||
            this.table.filteredRows.setColumnFilter(columnKey, this.filters[columnKey]);
 | 
			
		||||
            this.setHeight();
 | 
			
		||||
        },
 | 
			
		||||
        clearFilter(columnKey) {
 | 
			
		||||
            this.filters[columnKey] = '';
 | 
			
		||||
            this.table.filteredRows.setColumnFilter(columnKey, '');
 | 
			
		||||
            this.setHeight();
 | 
			
		||||
        },
 | 
			
		||||
        rowsAdded (rows) {
 | 
			
		||||
            this.setHeight();
 | 
			
		||||
@@ -512,15 +557,27 @@ export default {
 | 
			
		||||
            // which causes subsequent scroll to use an out of date height.
 | 
			
		||||
            this.contentTable.style.height = this.totalHeight + 'px'; 
 | 
			
		||||
        },
 | 
			
		||||
        exportAsCSV() {
 | 
			
		||||
        exportAsCSV(data) {
 | 
			
		||||
            const headerKeys = Object.keys(this.headers);
 | 
			
		||||
            const justTheData = this.table.filteredRows.getRows()
 | 
			
		||||
                .map(row => row.getFormattedDatum(this.headers));
 | 
			
		||||
            this.csvExporter.export(justTheData, {
 | 
			
		||||
 | 
			
		||||
            this.csvExporter.export(data, {
 | 
			
		||||
                filename: this.table.domainObject.name + '.csv',
 | 
			
		||||
                headers: headerKeys
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        exportAllDataAsCSV() {
 | 
			
		||||
            const justTheData = this.table.filteredRows.getRows()
 | 
			
		||||
                .map(row => row.getFormattedDatum(this.headers));
 | 
			
		||||
 | 
			
		||||
            this.exportAsCSV(justTheData);
 | 
			
		||||
        },
 | 
			
		||||
        exportMarkedDataAsCSV() {
 | 
			
		||||
            const data = this.table.filteredRows.getRows()
 | 
			
		||||
                .filter(row => row.marked === true)
 | 
			
		||||
                .map(row => row.getFormattedDatum(this.headers));
 | 
			
		||||
 | 
			
		||||
            this.exportAsCSV(data);
 | 
			
		||||
        },
 | 
			
		||||
        outstandingRequests(loading) {
 | 
			
		||||
            this.loading = loading;
 | 
			
		||||
        },
 | 
			
		||||
@@ -609,7 +666,110 @@ export default {
 | 
			
		||||
                scrollTop = this.scrollable.scrollTop;
 | 
			
		||||
            }, RESIZE_POLL_INTERVAL);
 | 
			
		||||
        },
 | 
			
		||||
        pause(pausedByButton) {
 | 
			
		||||
            if (pausedByButton) {
 | 
			
		||||
                this.pausedByButton = true;
 | 
			
		||||
            }
 | 
			
		||||
            this.paused = true;
 | 
			
		||||
            this.table.pause();
 | 
			
		||||
        },
 | 
			
		||||
        unpause(unpausedByButton) {
 | 
			
		||||
            if (unpausedByButton) {
 | 
			
		||||
                this.paused = false;
 | 
			
		||||
                this.table.unpause();
 | 
			
		||||
                this.markedRows = [];
 | 
			
		||||
                this.pausedByButton = false;
 | 
			
		||||
            } else {
 | 
			
		||||
                if (!this.pausedByButton) {
 | 
			
		||||
                    this.paused = false;
 | 
			
		||||
                    this.table.unpause();
 | 
			
		||||
                    this.markedRows = [];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
        },
 | 
			
		||||
        togglePauseByButton() {
 | 
			
		||||
            if (this.paused) {
 | 
			
		||||
                this.unpause(true);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.pause(true);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        undoMarkedRows(unpause) {
 | 
			
		||||
            this.markedRows.forEach(r => r.marked = false);
 | 
			
		||||
            this.markedRows = [];
 | 
			
		||||
        },
 | 
			
		||||
        unmarkRow(rowIndex) {
 | 
			
		||||
            this.undoMarkedRows();
 | 
			
		||||
            this.unpause();
 | 
			
		||||
        },
 | 
			
		||||
        markRow(rowIndex) {
 | 
			
		||||
            let insertMethod = 'unshift';
 | 
			
		||||
 | 
			
		||||
            if (this.markedRows.length && !this.ctrlKeyPressed) {
 | 
			
		||||
                this.undoMarkedRows();
 | 
			
		||||
                insertMethod = 'push';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let markedRow = this.visibleRows[rowIndex];
 | 
			
		||||
 | 
			
		||||
            this.$set(markedRow, 'marked', true);
 | 
			
		||||
            this.pause();
 | 
			
		||||
 | 
			
		||||
            this.markedRows[insertMethod](markedRow);
 | 
			
		||||
        },
 | 
			
		||||
        unmarkAllRows(skipUnpause) {
 | 
			
		||||
            this.markedRows.forEach(row => row.marked = false);
 | 
			
		||||
            this.markedRows = [];
 | 
			
		||||
            this.unpause();
 | 
			
		||||
        },
 | 
			
		||||
        markMultipleConcurrentRows(rowIndex) {
 | 
			
		||||
 | 
			
		||||
            if (!this.markedRows.length) {
 | 
			
		||||
                this.markRow(rowIndex);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (this.markedRows.length > 1) {
 | 
			
		||||
                    this.markedRows.forEach((r,i) => {
 | 
			
		||||
                        if (i !== 0) {
 | 
			
		||||
                            r.marked = false;
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    this.markedRows.splice(1);
 | 
			
		||||
                }
 | 
			
		||||
                let lastRowToBeMarked = this.visibleRows[rowIndex];
 | 
			
		||||
 | 
			
		||||
                let allRows = this.table.filteredRows.getRows(),
 | 
			
		||||
                    firstRowIndex = allRows.indexOf(this.markedRows[0]),
 | 
			
		||||
                    lastRowIndex = allRows.indexOf(lastRowToBeMarked);
 | 
			
		||||
 | 
			
		||||
                //supports backward selection
 | 
			
		||||
                if (lastRowIndex < firstRowIndex) {
 | 
			
		||||
                    let temp = lastRowIndex;
 | 
			
		||||
                
 | 
			
		||||
                    lastRowIndex = firstRowIndex;
 | 
			
		||||
                    firstRowIndex = temp - 1;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                for (var i = firstRowIndex + 1; i <= lastRowIndex; i++) {
 | 
			
		||||
                    let row = allRows[i];
 | 
			
		||||
                    row.marked = true;
 | 
			
		||||
                    this.markedRows.push(row);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.$forceUpdate();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        keydown(event) {
 | 
			
		||||
            if ((event.ctrlKey || event.metaKey)) {
 | 
			
		||||
                this.ctrlKeyPressed = true;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        keyup(event) {
 | 
			
		||||
            if ((event.ctrlKey || event.key.toLowerCase() === 'meta')) {
 | 
			
		||||
                console.log('up');
 | 
			
		||||
                this.ctrlKeyPressed = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    created() {
 | 
			
		||||
        this.filterChanged = _.debounce(this.filterChanged, 500);
 | 
			
		||||
@@ -642,6 +802,9 @@ export default {
 | 
			
		||||
        this.calculateScrollbarWidth();
 | 
			
		||||
 | 
			
		||||
        this.table.initialize();
 | 
			
		||||
 | 
			
		||||
        document.addEventListener('keydown', this.keydown);
 | 
			
		||||
        document.addEventListener('keyup', this.keyup);
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
        this.table.off('object-added', this.addObject);
 | 
			
		||||
@@ -660,6 +823,9 @@ export default {
 | 
			
		||||
        this.table.configuration.destroy();
 | 
			
		||||
 | 
			
		||||
        this.table.destroy();
 | 
			
		||||
 | 
			
		||||
        document.removeEventListener('keydown', this.keydown);
 | 
			
		||||
        document.removeEventListener('keyup', this.keyup);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -585,15 +585,15 @@ select {
 | 
			
		||||
    margin-right: $m;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-separator {
 | 
			
		||||
    @include cToolbarSeparator();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-toolbar {
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-left: 2px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__button {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__separator {
 | 
			
		||||
        @include cToolbarSeparator();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -76,23 +76,43 @@ div.c-table {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-table-wrapper {
 | 
			
		||||
    // Wraps .c-control-bar and .c-table
 | 
			
		||||
    @include abs();
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
 | 
			
		||||
    > .c-table {
 | 
			
		||||
        height: auto;
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-top: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-table-control-bar {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex: 0 0 auto;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-left: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-table {
 | 
			
		||||
    // Can be used by any type of table, scrolling, LAD, etc.
 | 
			
		||||
    $min-w: 50px;
 | 
			
		||||
 | 
			
		||||
    width: 100%;
 | 
			
		||||
 | 
			
		||||
    &__control-bar,
 | 
			
		||||
    &__headers-w {
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /******************************* ELEMENTS */
 | 
			
		||||
 | 
			
		||||
    &__control-bar {
 | 
			
		||||
        margin-bottom: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    thead tr,
 | 
			
		||||
    &.c-table__headers {
 | 
			
		||||
        background: $colorTabHeaderBg;
 | 
			
		||||
 
 | 
			
		||||
@@ -123,7 +123,12 @@
 | 
			
		||||
        .c-click-icon,
 | 
			
		||||
        .c-button {
 | 
			
		||||
            // Shrink buttons a bit when they appear in a frame
 | 
			
		||||
            font-size: 0.8em;
 | 
			
		||||
            align-items: baseline;
 | 
			
		||||
            font-size: 0.85em;
 | 
			
		||||
            padding: 3px 5px;
 | 
			
		||||
            &:before {
 | 
			
		||||
                font-size: 0.8em;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -34,10 +34,10 @@ export default {
 | 
			
		||||
        this.currentObject = this.object;
 | 
			
		||||
        this.updateView();
 | 
			
		||||
        this.$el.addEventListener('dragover', this.onDragOver);
 | 
			
		||||
        this.$el.addEventListener('drop', this.addObjectToParent);
 | 
			
		||||
        this.$el.addEventListener('drop', this.editIfEditable, {
 | 
			
		||||
            capture: true
 | 
			
		||||
        });
 | 
			
		||||
        this.$el.addEventListener('drop', this.addObjectToParent);
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        clear() {
 | 
			
		||||
@@ -57,6 +57,10 @@ export default {
 | 
			
		||||
                this.removeSelectable();
 | 
			
		||||
                delete this.removeSelectable;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.composition) {
 | 
			
		||||
                this.composition._destroy();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        invokeEditModeHandler(editMode) {
 | 
			
		||||
            this.currentView.onEditModeChange(editMode);
 | 
			
		||||
@@ -70,6 +74,13 @@ export default {
 | 
			
		||||
            if (!this.currentObject) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            this.composition = this.openmct.composition.get(this.currentObject);
 | 
			
		||||
            if (this.composition) {
 | 
			
		||||
                this.composition._synchronize();
 | 
			
		||||
                this.loadComposition();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.viewContainer = document.createElement('div');
 | 
			
		||||
            this.viewContainer.classList.add('c-object-view','u-contents');
 | 
			
		||||
            this.$el.append(this.viewContainer);
 | 
			
		||||
@@ -112,6 +123,10 @@ export default {
 | 
			
		||||
                delete this.removeSelectable;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.composition) {
 | 
			
		||||
                this.composition._destroy();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.currentObject = object;
 | 
			
		||||
            this.unlisten = this.openmct.objects.observe(this.currentObject, '*', (mutatedObject) => {
 | 
			
		||||
                this.currentObject = mutatedObject;
 | 
			
		||||
@@ -120,6 +135,9 @@ export default {
 | 
			
		||||
            this.viewKey = viewKey;
 | 
			
		||||
            this.updateView(immediatelySelect);
 | 
			
		||||
        },
 | 
			
		||||
        loadComposition() {
 | 
			
		||||
            return this.composition.load();
 | 
			
		||||
        },
 | 
			
		||||
        getSelectionContext() {
 | 
			
		||||
            if (this.currentView.getSelectionContext) {
 | 
			
		||||
                return this.currentView.getSelectionContext();
 | 
			
		||||
@@ -133,10 +151,12 @@ export default {
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        addObjectToParent(event) {
 | 
			
		||||
            if (this.hasComposableDomainObject(event)) {
 | 
			
		||||
            if (this.hasComposableDomainObject(event) && this.composition) {
 | 
			
		||||
                let composableDomainObject = this.getComposableDomainObject(event);
 | 
			
		||||
                this.currentObject.composition.push(composableDomainObject.identifier);
 | 
			
		||||
                this.openmct.objects.mutate(this.currentObject, 'composition', this.currentObject.composition);
 | 
			
		||||
                this.loadComposition().then(() => {
 | 
			
		||||
                    this.composition.add(composableDomainObject);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
                event.stopPropagation();
 | 
			
		||||
            }
 | 
			
		||||
@@ -155,6 +175,7 @@ export default {
 | 
			
		||||
        editIfEditable(event) {
 | 
			
		||||
            let provider = this.getViewProvider();
 | 
			
		||||
            if (provider && 
 | 
			
		||||
                provider.canEdit &&
 | 
			
		||||
                provider.canEdit(this.currentObject) &&
 | 
			
		||||
                !this.openmct.editor.isEditing()) {
 | 
			
		||||
                    this.openmct.editor.edit();
 | 
			
		||||
 
 | 
			
		||||
@@ -25,10 +25,6 @@ import _ from 'lodash';
 | 
			
		||||
        },
 | 
			
		||||
        methods: {
 | 
			
		||||
            updateSelection(selection) {
 | 
			
		||||
                if (_.isEqual(this.selection, selection)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.selection = selection;
 | 
			
		||||
 | 
			
		||||
                if (this.selectedViews) {
 | 
			
		||||
@@ -38,10 +34,6 @@ import _ from 'lodash';
 | 
			
		||||
                    this.$el.innerHTML = '';
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (selection.length > 1) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.selectedViews = this.openmct.inspectorViews.get(selection);
 | 
			
		||||
                this.selectedViews.forEach(selectedView => {
 | 
			
		||||
                    let viewContainer = document.createElement('div');
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,7 @@ const webpackConfig = {
 | 
			
		||||
            "bourbon": "bourbon.scss",
 | 
			
		||||
            "vue": path.join(__dirname, "node_modules/vue/dist/vue.js"),
 | 
			
		||||
            "d3-scale": path.join(__dirname, "node_modules/d3-scale/build/d3-scale.min.js"),
 | 
			
		||||
            "printj": path.join(__dirname, "node_modules/printj/dist/printj.min.js"),
 | 
			
		||||
            "styles": path.join(__dirname, "src/styles-new")
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user