Compare commits
	
		
			1 Commits
		
	
	
		
			time-condu
			...
			refresh-op
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					4992b99aa5 | 
@@ -20,8 +20,8 @@ jobs:
 | 
			
		||||
            paths:
 | 
			
		||||
              - node_modules
 | 
			
		||||
        - run:
 | 
			
		||||
            name: npm run test:coverage
 | 
			
		||||
            command: npm run test:coverage
 | 
			
		||||
            name: npm run test
 | 
			
		||||
            command: npm run test
 | 
			
		||||
        - run:
 | 
			
		||||
            name: npm run lint
 | 
			
		||||
            command: npm run lint
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,14 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="example">{{ msg }}</div>
 | 
			
		||||
  <div class="example">{{ msg }}</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            msg: 'Hello world!'
 | 
			
		||||
        }
 | 
			
		||||
  data () {
 | 
			
		||||
    return {
 | 
			
		||||
      msg: 'Hello world!'
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										39
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								index.html
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
<!--
 | 
			
		||||
 Open MCT, Copyright (c) 2014-2020, United States Government
 | 
			
		||||
 Open MCT, Copyright (c) 2014-2017, United States Government
 | 
			
		||||
 as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 Administration. All rights reserved.
 | 
			
		||||
 | 
			
		||||
@@ -34,8 +34,8 @@
 | 
			
		||||
    <body>
 | 
			
		||||
    </body>
 | 
			
		||||
    <script>
 | 
			
		||||
        const THIRTY_SECONDS = 30 * 1000;
 | 
			
		||||
        const THIRTY_MINUTES = THIRTY_SECONDS * 60;
 | 
			
		||||
        const FIVE_MINUTES = 5 * 60 * 1000;
 | 
			
		||||
        const THIRTY_MINUTES = 30 * 60 * 1000;
 | 
			
		||||
 | 
			
		||||
        [
 | 
			
		||||
            'example/eventGenerator'
 | 
			
		||||
@@ -43,9 +43,9 @@
 | 
			
		||||
            openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        openmct.install(openmct.plugins.LocalStorage());
 | 
			
		||||
        openmct.install(openmct.plugins.Espresso());
 | 
			
		||||
        openmct.install(openmct.plugins.MyItems());
 | 
			
		||||
        openmct.install(openmct.plugins.LocalStorage());
 | 
			
		||||
        openmct.install(openmct.plugins.Generator());
 | 
			
		||||
        openmct.install(openmct.plugins.ExampleImagery());
 | 
			
		||||
        openmct.install(openmct.plugins.UTCTimeSystem());
 | 
			
		||||
@@ -63,34 +63,7 @@
 | 
			
		||||
                    bounds: {
 | 
			
		||||
                        start: Date.now() - THIRTY_MINUTES,
 | 
			
		||||
                        end: Date.now()
 | 
			
		||||
                    },
 | 
			
		||||
                    presets: [
 | 
			
		||||
                        {
 | 
			
		||||
                            label: 'Last Day',
 | 
			
		||||
                            bounds: {
 | 
			
		||||
                                start: Date.now() - 1000 * 60 * 60 * 24,
 | 
			
		||||
                                end: Date.now()
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: 'Last 2 hours',
 | 
			
		||||
                            bounds: {
 | 
			
		||||
                                start: Date.now() - 1000 * 60 * 60 * 2,
 | 
			
		||||
                                end: Date.now()
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: 'Last hour',
 | 
			
		||||
                            bounds: {
 | 
			
		||||
                                start: Date.now() - 1000 * 60 * 60,
 | 
			
		||||
                                end: Date.now()
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    ],
 | 
			
		||||
                    // maximum entries to retain in conductor history
 | 
			
		||||
                    records: 10,
 | 
			
		||||
                    // maximum duration between start and end bounds
 | 
			
		||||
                    limit: 1000 * 60 * 60 * 24
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: "Realtime",
 | 
			
		||||
@@ -98,7 +71,7 @@
 | 
			
		||||
                    clock: 'local',
 | 
			
		||||
                    clockOffsets: {
 | 
			
		||||
                        start: - THIRTY_MINUTES,
 | 
			
		||||
                        end: THIRTY_SECONDS
 | 
			
		||||
                        end: FIVE_MINUTES
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
 
 | 
			
		||||
@@ -24,27 +24,16 @@
 | 
			
		||||
 | 
			
		||||
const devMode = process.env.NODE_ENV !== 'production';
 | 
			
		||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
 | 
			
		||||
const coverageEnabled = process.env.COVERAGE === 'true';
 | 
			
		||||
const reporters = ['progress', 'html'];
 | 
			
		||||
 | 
			
		||||
if (coverageEnabled) {
 | 
			
		||||
    reporters.push('coverage-istanbul');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = (config) => {
 | 
			
		||||
    const webpackConfig = require('./webpack.config.js');
 | 
			
		||||
    delete webpackConfig.output;
 | 
			
		||||
 | 
			
		||||
    if (!devMode || coverageEnabled) {
 | 
			
		||||
    if (!devMode) {
 | 
			
		||||
        webpackConfig.module.rules.push({
 | 
			
		||||
            test: /\.js$/,
 | 
			
		||||
            exclude: /node_modules|example|lib|dist/,
 | 
			
		||||
            use: {
 | 
			
		||||
                loader: 'istanbul-instrumenter-loader',
 | 
			
		||||
                options: {
 | 
			
		||||
                    esModules: true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            exclude: /node_modules|example/,
 | 
			
		||||
            use: 'istanbul-instrumenter-loader'
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -56,7 +45,11 @@ module.exports = (config) => {
 | 
			
		||||
            'src/**/*Spec.js'
 | 
			
		||||
        ],
 | 
			
		||||
        port: 9876,
 | 
			
		||||
        reporters: reporters,
 | 
			
		||||
        reporters: [
 | 
			
		||||
            'progress',
 | 
			
		||||
            'coverage',
 | 
			
		||||
            'html'
 | 
			
		||||
        ],
 | 
			
		||||
        browsers: browsers,
 | 
			
		||||
        customLaunchers: {
 | 
			
		||||
            ChromeDebugging: {
 | 
			
		||||
@@ -68,27 +61,27 @@ module.exports = (config) => {
 | 
			
		||||
        colors: true,
 | 
			
		||||
        logLevel: config.LOG_INFO,
 | 
			
		||||
        autoWatch: true,
 | 
			
		||||
        coverageReporter: {
 | 
			
		||||
            dir: process.env.CIRCLE_ARTIFACTS ?
 | 
			
		||||
                process.env.CIRCLE_ARTIFACTS + '/coverage' :
 | 
			
		||||
                "dist/reports/coverage",
 | 
			
		||||
            check: {
 | 
			
		||||
                global: {
 | 
			
		||||
                    lines: 80,
 | 
			
		||||
                    excludes: ['src/plugins/plot/**/*.js']
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        // HTML test reporting.
 | 
			
		||||
        htmlReporter: {
 | 
			
		||||
            outputDir: "dist/reports/tests",
 | 
			
		||||
            preserveDescribeNesting: true,
 | 
			
		||||
            foldAll: false
 | 
			
		||||
        },
 | 
			
		||||
        coverageIstanbulReporter: {
 | 
			
		||||
            fixWebpackSourcePaths: true,
 | 
			
		||||
            dir: process.env.CIRCLE_ARTIFACTS ?
 | 
			
		||||
                process.env.CIRCLE_ARTIFACTS + '/coverage' :
 | 
			
		||||
                "dist/reports/coverage",
 | 
			
		||||
            reports: ['html', 'lcovonly', 'text-summary'],
 | 
			
		||||
            thresholds: {
 | 
			
		||||
                global: {
 | 
			
		||||
                    lines: 62
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        preprocessors: {
 | 
			
		||||
            'platform/**/*Spec.js': ['webpack', 'sourcemap'],
 | 
			
		||||
            'src/**/*Spec.js': ['webpack', 'sourcemap']
 | 
			
		||||
            // add webpack as preprocessor
 | 
			
		||||
            'platform/**/*Spec.js': [ 'webpack', 'sourcemap' ],
 | 
			
		||||
            'src/**/*Spec.js': [ 'webpack', 'sourcemap' ]
 | 
			
		||||
        },
 | 
			
		||||
        webpack: webpackConfig,
 | 
			
		||||
        webpackMiddleware: {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								package.json
									
									
									
									
									
								
							@@ -4,7 +4,8 @@
 | 
			
		||||
  "description": "The Open MCT core platform",
 | 
			
		||||
  "dependencies": {},
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "angular": "1.7.9",
 | 
			
		||||
    "acorn": "6.2.0",
 | 
			
		||||
    "angular": "1.4.14",
 | 
			
		||||
    "angular-route": "1.4.14",
 | 
			
		||||
    "babel-eslint": "8.2.6",
 | 
			
		||||
    "comma-separated-values": "^3.6.4",
 | 
			
		||||
@@ -42,7 +43,6 @@
 | 
			
		||||
    "karma-chrome-launcher": "^2.2.0",
 | 
			
		||||
    "karma-cli": "^1.0.1",
 | 
			
		||||
    "karma-coverage": "^1.1.2",
 | 
			
		||||
    "karma-coverage-istanbul-reporter": "^2.1.1",
 | 
			
		||||
    "karma-html-reporter": "^0.2.7",
 | 
			
		||||
    "karma-jasmine": "^1.1.2",
 | 
			
		||||
    "karma-sourcemap-loader": "^0.3.7",
 | 
			
		||||
@@ -53,9 +53,9 @@
 | 
			
		||||
    "marked": "^0.3.5",
 | 
			
		||||
    "mini-css-extract-plugin": "^0.4.1",
 | 
			
		||||
    "minimist": "^1.1.1",
 | 
			
		||||
    "moment": "2.25.3",
 | 
			
		||||
    "moment": "^2.11.1",
 | 
			
		||||
    "moment-duration-format": "^2.2.2",
 | 
			
		||||
    "moment-timezone": "0.5.28",
 | 
			
		||||
    "moment-timezone": "^0.5.21",
 | 
			
		||||
    "node-bourbon": "^4.2.3",
 | 
			
		||||
    "node-sass": "^4.9.2",
 | 
			
		||||
    "painterro": "^0.2.65",
 | 
			
		||||
@@ -76,16 +76,14 @@
 | 
			
		||||
    "zepto": "^1.2.0"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "clean": "rm -rf ./dist",
 | 
			
		||||
    "start": "node app.js",
 | 
			
		||||
    "lint": "eslint platform example src --ext .js,.vue openmct.js",
 | 
			
		||||
    "lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
 | 
			
		||||
    "lint": "eslint platform example src/**/*.{js,vue} openmct.js",
 | 
			
		||||
    "lint:fix": "eslint platform example src/**/*.{js,vue} openmct.js --fix",
 | 
			
		||||
    "build:prod": "cross-env NODE_ENV=production webpack",
 | 
			
		||||
    "build:dev": "webpack",
 | 
			
		||||
    "build:watch": "webpack --watch",
 | 
			
		||||
    "test": "karma start --single-run",
 | 
			
		||||
    "test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
 | 
			
		||||
    "test:coverage": "./scripts/test-coverage.sh",
 | 
			
		||||
    "test-debug": "cross-env NODE_ENV=debug karma start --no-single-run",
 | 
			
		||||
    "test:watch": "karma start --no-single-run",
 | 
			
		||||
    "verify": "concurrently 'npm:test' 'npm:lint'",
 | 
			
		||||
    "jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
 | 
			
		||||
 
 | 
			
		||||
@@ -87,11 +87,6 @@ define([
 | 
			
		||||
                bootstrapper
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        // Override of angular1.6 ! hashPrefix
 | 
			
		||||
        app.config(['$locationProvider', function ($locationProvider) {
 | 
			
		||||
            $locationProvider.hashPrefix('');
 | 
			
		||||
        }]);
 | 
			
		||||
 | 
			
		||||
        // Apply logging levels; this must be done now, before the
 | 
			
		||||
        // first log statement.
 | 
			
		||||
        new LogLevel(logLevel).configure(app, $log);
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ define([
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "ELASTIC_PATH",
 | 
			
		||||
                        "value": "mct/_doc",
 | 
			
		||||
                        "value": "mct/domain_object",
 | 
			
		||||
                        "priority": "fallback"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
 
 | 
			
		||||
@@ -32,9 +32,9 @@ define(
 | 
			
		||||
        // JSLint doesn't like underscore-prefixed properties,
 | 
			
		||||
        // so hide them here.
 | 
			
		||||
        var SRC = "_source",
 | 
			
		||||
            CONFLICT = 409,
 | 
			
		||||
            SEQ_NO = "_seq_no",
 | 
			
		||||
            PRIMARY_TERM = "_primary_term";
 | 
			
		||||
            REV = "_version",
 | 
			
		||||
            ID = "_id",
 | 
			
		||||
            CONFLICT = 409;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The ElasticPersistenceProvider reads and writes JSON documents
 | 
			
		||||
@@ -104,8 +104,7 @@ define(
 | 
			
		||||
        // Get a domain object model out of ElasticSearch's response
 | 
			
		||||
        ElasticPersistenceProvider.prototype.getModel = function (response) {
 | 
			
		||||
            if (response && response[SRC]) {
 | 
			
		||||
                this.revs[response[SEQ_NO]] = response[SEQ_NO];
 | 
			
		||||
                this.revs[response[PRIMARY_TERM]] = response[PRIMARY_TERM];
 | 
			
		||||
                this.revs[response[ID]] = response[REV];
 | 
			
		||||
                return response[SRC];
 | 
			
		||||
            } else {
 | 
			
		||||
                return undefined;
 | 
			
		||||
@@ -117,8 +116,7 @@ define(
 | 
			
		||||
        // indicate that the request failed.
 | 
			
		||||
        ElasticPersistenceProvider.prototype.checkResponse = function (response, key) {
 | 
			
		||||
            if (response && !response.error) {
 | 
			
		||||
                this.revs[SEQ_NO] = response[SEQ_NO];
 | 
			
		||||
                this.revs[PRIMARY_TERM] = response[PRIMARY_TERM];
 | 
			
		||||
                this.revs[key] = response[REV];
 | 
			
		||||
                return response;
 | 
			
		||||
            } else {
 | 
			
		||||
                return this.handleError(response, key);
 | 
			
		||||
@@ -149,7 +147,7 @@ define(
 | 
			
		||||
            function checkUpdate(response) {
 | 
			
		||||
                return self.checkResponse(response, key);
 | 
			
		||||
            }
 | 
			
		||||
            return this.put(key, value)
 | 
			
		||||
            return this.put(key, value, { version: this.revs[key] })
 | 
			
		||||
                .then(checkUpdate);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -85,7 +85,7 @@ define(
 | 
			
		||||
            it("allows object creation", function () {
 | 
			
		||||
                var model = { someKey: "some value" };
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1 }
 | 
			
		||||
                    data: { "_id": "abc", "_version": 1 }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.createObject("testSpace", "abc", model).then(capture);
 | 
			
		||||
                expect(mockHttp).toHaveBeenCalledWith({
 | 
			
		||||
@@ -100,7 +100,7 @@ define(
 | 
			
		||||
            it("allows object models to be read back", function () {
 | 
			
		||||
                var model = { someKey: "some value" };
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1, "_source": model }
 | 
			
		||||
                    data: { "_id": "abc", "_version": 1, "_source": model }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.readObject("testSpace", "abc").then(capture);
 | 
			
		||||
                expect(mockHttp).toHaveBeenCalledWith({
 | 
			
		||||
@@ -117,19 +117,19 @@ define(
 | 
			
		||||
 | 
			
		||||
                // First do a read to populate rev tags...
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_version": 42, "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.readObject("testSpace", "abc");
 | 
			
		||||
 | 
			
		||||
                // Now perform an update
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_seq_no": 1, "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_version": 43, "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.updateObject("testSpace", "abc", model).then(capture);
 | 
			
		||||
                expect(mockHttp).toHaveBeenCalledWith({
 | 
			
		||||
                    url: "/test/db/abc",
 | 
			
		||||
                    method: "PUT",
 | 
			
		||||
                    params: undefined,
 | 
			
		||||
                    params: { version: 42 },
 | 
			
		||||
                    data: model
 | 
			
		||||
                });
 | 
			
		||||
                expect(capture.calls.mostRecent().args[0]).toBeTruthy();
 | 
			
		||||
@@ -138,13 +138,13 @@ define(
 | 
			
		||||
            it("allows object deletion", function () {
 | 
			
		||||
                // First do a read to populate rev tags...
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_version": 42, "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.readObject("testSpace", "abc");
 | 
			
		||||
 | 
			
		||||
                // Now perform an update
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_version": 42, "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.deleteObject("testSpace", "abc", {}).then(capture);
 | 
			
		||||
                expect(mockHttp).toHaveBeenCalledWith({
 | 
			
		||||
@@ -167,13 +167,13 @@ define(
 | 
			
		||||
                expect(capture).toHaveBeenCalledWith(undefined);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("handles rejection due to _seq_no", function () {
 | 
			
		||||
            it("handles rejection due to version", function () {
 | 
			
		||||
                var model = { someKey: "some value" },
 | 
			
		||||
                    mockErrorCallback = jasmine.createSpy('error');
 | 
			
		||||
 | 
			
		||||
                // First do a read to populate rev tags...
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_seq_no": 1, "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_version": 42, "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.readObject("testSpace", "abc");
 | 
			
		||||
 | 
			
		||||
@@ -196,7 +196,7 @@ define(
 | 
			
		||||
 | 
			
		||||
                // First do a read to populate rev tags...
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_seq_no": 1, "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_version": 42, "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.readObject("testSpace", "abc");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +0,0 @@
 | 
			
		||||
export NODE_OPTIONS=--max_old_space_size=4096
 | 
			
		||||
cross-env COVERAGE=true karma start --single-run
 | 
			
		||||
@@ -69,7 +69,9 @@ export default class ConditionClass extends EventEmitter {
 | 
			
		||||
            console.log('no data received');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this.isTelemetryUsed(datum.id)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.criteria.forEach(criterion => {
 | 
			
		||||
            if (this.isAnyOrAllTelemetry(criterion)) {
 | 
			
		||||
                criterion.getResult(datum, this.conditionManager.telemetryObjects);
 | 
			
		||||
@@ -204,7 +206,7 @@ export default class ConditionClass extends EventEmitter {
 | 
			
		||||
        let latestTimestamp;
 | 
			
		||||
        let criteriaResults = {};
 | 
			
		||||
        const criteriaRequests = this.criteria
 | 
			
		||||
            .map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
 | 
			
		||||
            .map(criterion => criterion.requestLAD({telemetryObjects: this.conditionManager.telemetryObjects}));
 | 
			
		||||
 | 
			
		||||
        return Promise.all(criteriaRequests)
 | 
			
		||||
            .then(results => {
 | 
			
		||||
 
 | 
			
		||||
@@ -55,8 +55,9 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
        this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
 | 
			
		||||
        this.subscriptions[id] = this.openmct.telemetry.subscribe(
 | 
			
		||||
            endpoint,
 | 
			
		||||
            this.telemetryReceived.bind(this, endpoint)
 | 
			
		||||
            this.telemetryReceived.bind(this, id)
 | 
			
		||||
        );
 | 
			
		||||
        // TODO check if this is needed
 | 
			
		||||
        this.updateConditionTelemetry();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -70,7 +71,6 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
        this.subscriptions[id]();
 | 
			
		||||
        delete this.subscriptions[id];
 | 
			
		||||
        delete this.telemetryObjects[id];
 | 
			
		||||
        this.removeConditionTelemetry();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initialize() {
 | 
			
		||||
@@ -86,30 +86,6 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
        this.conditionClassCollection.forEach((condition) => condition.updateTelemetry());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    removeConditionTelemetry() {
 | 
			
		||||
        let conditionsChanged = false;
 | 
			
		||||
        this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration) => {
 | 
			
		||||
            conditionConfiguration.configuration.criteria.forEach((criterion, index) => {
 | 
			
		||||
                const isAnyAllTelemetry = criterion.telemetry && (criterion.telemetry === 'any' || criterion.telemetry === 'all');
 | 
			
		||||
                if (!isAnyAllTelemetry) {
 | 
			
		||||
                    const found = Object.values(this.telemetryObjects).find((telemetryObject) => {
 | 
			
		||||
                        return this.openmct.objects.areIdsEqual(telemetryObject.identifier, criterion.telemetry);
 | 
			
		||||
                    });
 | 
			
		||||
                    if (!found) {
 | 
			
		||||
                        criterion.telemetry = '';
 | 
			
		||||
                        criterion.metadata = '';
 | 
			
		||||
                        criterion.input = [];
 | 
			
		||||
                        criterion.operation = '';
 | 
			
		||||
                        conditionsChanged = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        if (conditionsChanged) {
 | 
			
		||||
            this.persistConditions();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateCondition(conditionConfiguration, index) {
 | 
			
		||||
        let condition = this.conditionClassCollection[index];
 | 
			
		||||
        condition.update(conditionConfiguration);
 | 
			
		||||
@@ -258,13 +234,9 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
                            this.openmct.time.timeSystem()
 | 
			
		||||
                        );
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    if (!Object.values(latestTimestamp).some(timeSystem => timeSystem)) {
 | 
			
		||||
                        return [];
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    const currentCondition = this.getCurrentConditionLAD(conditionResults);
 | 
			
		||||
                    const currentOutput = Object.assign(
 | 
			
		||||
 | 
			
		||||
                    return Object.assign(
 | 
			
		||||
                        {
 | 
			
		||||
                            output: currentCondition.configuration.output,
 | 
			
		||||
                            id: this.conditionSetDomainObject.identifier,
 | 
			
		||||
@@ -272,30 +244,12 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
                        },
 | 
			
		||||
                        latestTimestamp
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    return [currentOutput];
 | 
			
		||||
                });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isTelemetryUsed(endpoint) {
 | 
			
		||||
        const id = this.openmct.objects.makeKeyString(endpoint.identifier);
 | 
			
		||||
 | 
			
		||||
        for(const condition of this.conditionClassCollection) {
 | 
			
		||||
            if (condition.isTelemetryUsed(id)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    telemetryReceived(endpoint, datum) {
 | 
			
		||||
        if (!this.isTelemetryUsed(endpoint)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const normalizedDatum = this.createNormalizedDatum(datum, endpoint);
 | 
			
		||||
    telemetryReceived(id, datum) {
 | 
			
		||||
        const normalizedDatum = this.createNormalizedDatum(datum, id);
 | 
			
		||||
        const timeSystemKey = this.openmct.time.timeSystem().key;
 | 
			
		||||
        let timestamp = {};
 | 
			
		||||
        timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
 | 
			
		||||
@@ -329,11 +283,8 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createNormalizedDatum(telemetryDatum, endpoint) {
 | 
			
		||||
        const id = this.openmct.objects.makeKeyString(endpoint.identifier);
 | 
			
		||||
        const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
 | 
			
		||||
 | 
			
		||||
        const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
 | 
			
		||||
    createNormalizedDatum(telemetryDatum, id) {
 | 
			
		||||
        const normalizedDatum = Object.values(this.telemetryObjects[id].telemetryMetaData).reduce((datum, metadatum) => {
 | 
			
		||||
            const testValue = this.getTestData(metadatum);
 | 
			
		||||
            const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
 | 
			
		||||
            datum[metadatum.key] = testValue !== undefined ?  formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);
 | 
			
		||||
 
 | 
			
		||||
@@ -54,22 +54,13 @@ export default class ConditionSetMetadataProvider {
 | 
			
		||||
        return {
 | 
			
		||||
            values: this.getDomains().concat([
 | 
			
		||||
                {
 | 
			
		||||
                    key: "state",
 | 
			
		||||
                    source: "output",
 | 
			
		||||
                    name: "State",
 | 
			
		||||
                    format: "enum",
 | 
			
		||||
                    name: 'Output',
 | 
			
		||||
                    key: 'output',
 | 
			
		||||
                    format: 'enum',
 | 
			
		||||
                    enumerations: enumerations,
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 1
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "output",
 | 
			
		||||
                    name: "Value",
 | 
			
		||||
                    format: "string",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 2
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ])
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ export default class ConditionSetTelemetryProvider {
 | 
			
		||||
 | 
			
		||||
        return conditionManager.requestLADConditionSetOutput()
 | 
			
		||||
            .then(latestOutput => {
 | 
			
		||||
                return latestOutput;
 | 
			
		||||
                return latestOutput ? [latestOutput] : [];
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,13 +23,10 @@
 | 
			
		||||
import EventEmitter from 'EventEmitter';
 | 
			
		||||
 | 
			
		||||
export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
    constructor(styleConfiguration, openmct, callback, suppressSubscriptionOnEdit) {
 | 
			
		||||
    constructor(styleConfiguration, openmct, callback) {
 | 
			
		||||
        super();
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.callback = callback;
 | 
			
		||||
        if (suppressSubscriptionOnEdit) {
 | 
			
		||||
            this.openmct.editor.on('isEditing', this.toggleSubscription.bind(this));
 | 
			
		||||
        }
 | 
			
		||||
        if (styleConfiguration) {
 | 
			
		||||
            this.initialize(styleConfiguration);
 | 
			
		||||
            if (styleConfiguration.conditionSetIdentifier) {
 | 
			
		||||
@@ -40,25 +37,9 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toggleSubscription(isEditing) {
 | 
			
		||||
        this.isEditing = isEditing;
 | 
			
		||||
        if (this.isEditing) {
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
            }
 | 
			
		||||
            if (this.conditionSetIdentifier) {
 | 
			
		||||
                this.applySelectedConditionStyle();
 | 
			
		||||
            }
 | 
			
		||||
        } else if (this.conditionSetIdentifier) {
 | 
			
		||||
            this.subscribeToConditionSet();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initialize(styleConfiguration) {
 | 
			
		||||
        this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
 | 
			
		||||
        this.staticStyle = styleConfiguration.staticStyle;
 | 
			
		||||
        this.selectedConditionId = styleConfiguration.selectedConditionId;
 | 
			
		||||
        this.defaultConditionId = styleConfiguration.defaultConditionId;
 | 
			
		||||
        this.updateConditionStylesMap(styleConfiguration.styles || []);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -73,7 +54,7 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
                        this.handleConditionSetResultUpdated(output[0]);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
 | 
			
		||||
            this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, output => this.handleConditionSetResultUpdated(output));
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -85,13 +66,9 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
            let isNewConditionSet = !this.conditionSetIdentifier ||
 | 
			
		||||
                                    !this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
 | 
			
		||||
            this.initialize(styleConfiguration);
 | 
			
		||||
            if (this.isEditing) {
 | 
			
		||||
                this.applySelectedConditionStyle();
 | 
			
		||||
            } else {
 | 
			
		||||
                //Only resubscribe if the conditionSet has changed.
 | 
			
		||||
                if (isNewConditionSet) {
 | 
			
		||||
                    this.subscribeToConditionSet();
 | 
			
		||||
                }
 | 
			
		||||
            //Only resubscribe if the conditionSet has changed.
 | 
			
		||||
            if (isNewConditionSet) {
 | 
			
		||||
                this.subscribeToConditionSet();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -126,16 +103,6 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    applySelectedConditionStyle() {
 | 
			
		||||
        const conditionId = this.selectedConditionId || this.defaultConditionId;
 | 
			
		||||
        if (!conditionId) {
 | 
			
		||||
            this.applyStaticStyle();
 | 
			
		||||
        } else if (this.conditionalStyleMap[conditionId]) {
 | 
			
		||||
            this.currentStyle = this.conditionalStyleMap[conditionId];
 | 
			
		||||
            this.updateDomainObjectStyle();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    applyStaticStyle() {
 | 
			
		||||
        if (this.staticStyle) {
 | 
			
		||||
            this.currentStyle = this.staticStyle.style;
 | 
			
		||||
@@ -156,7 +123,6 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
        }
 | 
			
		||||
        delete this.stopProvidingTelemetry;
 | 
			
		||||
        this.conditionSetIdentifier = undefined;
 | 
			
		||||
        this.isEditing = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,6 @@
 | 
			
		||||
>
 | 
			
		||||
    <div class="c-condition-h__drop-target"></div>
 | 
			
		||||
    <div v-if="isEditing"
 | 
			
		||||
         :class="{'is-current': condition.id === currentConditionId}"
 | 
			
		||||
         class="c-condition c-condition--edit"
 | 
			
		||||
    >
 | 
			
		||||
        <!-- Edit view -->
 | 
			
		||||
@@ -168,7 +167,6 @@
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-else
 | 
			
		||||
         class="c-condition c-condition--browse"
 | 
			
		||||
         :class="{'is-current': condition.id === currentConditionId}"
 | 
			
		||||
    >
 | 
			
		||||
        <!-- Browse view -->
 | 
			
		||||
        <div class="c-condition__header">
 | 
			
		||||
@@ -201,10 +199,6 @@ export default {
 | 
			
		||||
        ConditionDescription
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
        currentConditionId: {
 | 
			
		||||
            type: String,
 | 
			
		||||
            default: ''
 | 
			
		||||
        },
 | 
			
		||||
        condition: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,6 @@
 | 
			
		||||
            <Condition v-for="(condition, index) in conditionCollection"
 | 
			
		||||
                       :key="condition.id"
 | 
			
		||||
                       :condition="condition"
 | 
			
		||||
                       :current-condition-id="currentConditionId"
 | 
			
		||||
                       :condition-index="index"
 | 
			
		||||
                       :telemetry="telemetryObjs"
 | 
			
		||||
                       :is-editing="isEditing"
 | 
			
		||||
@@ -108,8 +107,7 @@ export default {
 | 
			
		||||
            moveIndex: undefined,
 | 
			
		||||
            isDragging: false,
 | 
			
		||||
            defaultOutput: undefined,
 | 
			
		||||
            dragCounter: 0,
 | 
			
		||||
            currentConditionId: ''
 | 
			
		||||
            dragCounter: 0
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
@@ -147,7 +145,6 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        handleConditionSetResultUpdated(data) {
 | 
			
		||||
            this.currentConditionId = data.conditionId;
 | 
			
		||||
            this.$emit('conditionSetResultUpdated', data)
 | 
			
		||||
        },
 | 
			
		||||
        observeForChanges() {
 | 
			
		||||
 
 | 
			
		||||
@@ -190,7 +190,6 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-condition {
 | 
			
		||||
    border: 1px solid transparent;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    min-width: 400px;
 | 
			
		||||
 | 
			
		||||
@@ -235,12 +234,6 @@
 | 
			
		||||
    &__summary {
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-current {
 | 
			
		||||
        $c: $colorBodyFg;
 | 
			
		||||
        border-color: rgba($c, 0.2);
 | 
			
		||||
        background: rgba($c, 0.2);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************** CONDITION DEFINITION, EDITING */
 | 
			
		||||
 
 | 
			
		||||
@@ -79,8 +79,6 @@
 | 
			
		||||
            <div v-for="(conditionStyle, index) in conditionalStyles"
 | 
			
		||||
                 :key="index"
 | 
			
		||||
                 class="c-inspect-styles__condition"
 | 
			
		||||
                 :class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
 | 
			
		||||
                 @click="applySelectedConditionStyle(conditionStyle.conditionId)"
 | 
			
		||||
            >
 | 
			
		||||
                <condition-error :show-label="true"
 | 
			
		||||
                                 :condition="getCondition(conditionStyle.conditionId)"
 | 
			
		||||
@@ -128,8 +126,7 @@ export default {
 | 
			
		||||
            isEditing: this.openmct.editor.isEditing(),
 | 
			
		||||
            conditions: undefined,
 | 
			
		||||
            conditionsLoaded: false,
 | 
			
		||||
            navigateToPath: '',
 | 
			
		||||
            selectedConditionId: ''
 | 
			
		||||
            navigateToPath: ''
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
@@ -194,9 +191,6 @@ export default {
 | 
			
		||||
            if (this.stopObservingItems) {
 | 
			
		||||
                this.stopObservingItems();
 | 
			
		||||
            }
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        initialize(conditionSetDomainObject) {
 | 
			
		||||
            //If there are new conditions in the conditionSet we need to set those styles to default
 | 
			
		||||
@@ -206,13 +200,6 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        setEditState(isEditing) {
 | 
			
		||||
            this.isEditing = isEditing;
 | 
			
		||||
            if (this.isEditing) {
 | 
			
		||||
                if (this.stopProvidingTelemetry) {
 | 
			
		||||
                    this.stopProvidingTelemetry();
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                this.subscribeToConditionSet();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        addConditionSet() {
 | 
			
		||||
            let conditionSetDomainObject;
 | 
			
		||||
@@ -283,8 +270,6 @@ export default {
 | 
			
		||||
            let domainObjectStyles =  (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
 | 
			
		||||
            if (this.itemId) {
 | 
			
		||||
                domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
 | 
			
		||||
                domainObjectStyles[this.itemId].selectedConditionId = undefined;
 | 
			
		||||
                domainObjectStyles[this.itemId].defaultConditionId = undefined;
 | 
			
		||||
                delete domainObjectStyles[this.itemId].conditionSetIdentifier;
 | 
			
		||||
                domainObjectStyles[this.itemId].styles = undefined;
 | 
			
		||||
                delete domainObjectStyles[this.itemId].styles;
 | 
			
		||||
@@ -293,8 +278,6 @@ export default {
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                domainObjectStyles.conditionSetIdentifier = undefined;
 | 
			
		||||
                domainObjectStyles.selectedConditionId = undefined;
 | 
			
		||||
                domainObjectStyles.defaultConditionId = undefined;
 | 
			
		||||
                delete domainObjectStyles.conditionSetIdentifier;
 | 
			
		||||
                domainObjectStyles.styles = undefined;
 | 
			
		||||
                delete domainObjectStyles.styles;
 | 
			
		||||
@@ -304,23 +287,16 @@ export default {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.persist(domainObjectStyles);
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        updateDomainObjectItemStyles(newItems) {
 | 
			
		||||
            //check that all items that have been styles still exist. Otherwise delete those styles
 | 
			
		||||
            let domainObjectStyles =  (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
 | 
			
		||||
            let itemsToRemove = [];
 | 
			
		||||
            let keys = Object.keys(domainObjectStyles);
 | 
			
		||||
            //TODO: Need an easier way to find which properties are itemIds
 | 
			
		||||
            keys.forEach((key) => {
 | 
			
		||||
                const keyIsItemId = (key !== 'styles') &&
 | 
			
		||||
                if ((key !== 'styles') &&
 | 
			
		||||
                    (key !== 'staticStyle') &&
 | 
			
		||||
                    (key !== 'defaultConditionId') &&
 | 
			
		||||
                    (key !== 'selectedConditionId') &&
 | 
			
		||||
                    (key !== 'conditionSetIdentifier');
 | 
			
		||||
                if (keyIsItemId) {
 | 
			
		||||
                    (key !== 'conditionSetIdentifier')) {
 | 
			
		||||
                    if (!(newItems.find(item => item.id === key))) {
 | 
			
		||||
                        itemsToRemove.push(key);
 | 
			
		||||
                    }
 | 
			
		||||
@@ -348,9 +324,6 @@ export default {
 | 
			
		||||
            }
 | 
			
		||||
            let conditionalStyles = [];
 | 
			
		||||
            this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
 | 
			
		||||
                if (conditionConfiguration.isDefault) {
 | 
			
		||||
                    this.selectedConditionId = conditionConfiguration.id;
 | 
			
		||||
                }
 | 
			
		||||
                this.conditions[conditionConfiguration.id] = conditionConfiguration;
 | 
			
		||||
                let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
 | 
			
		||||
                if (foundStyle) {
 | 
			
		||||
@@ -366,27 +339,7 @@ export default {
 | 
			
		||||
            //we're doing this so that we remove styles for any conditions that have been removed from the condition set
 | 
			
		||||
            this.conditionalStyles = conditionalStyles;
 | 
			
		||||
            this.conditionsLoaded = true;
 | 
			
		||||
            this.persist(this.getDomainObjectConditionalStyle(this.selectedConditionId));
 | 
			
		||||
            if (!this.isEditing) {
 | 
			
		||||
                this.subscribeToConditionSet();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        subscribeToConditionSet() {
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
            }
 | 
			
		||||
            if (this.conditionSetDomainObject) {
 | 
			
		||||
                this.openmct.telemetry.request(this.conditionSetDomainObject)
 | 
			
		||||
                    .then(output => {
 | 
			
		||||
                        if (output && output.length) {
 | 
			
		||||
                            this.handleConditionSetResultUpdated(output[0]);
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(this.conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        handleConditionSetResultUpdated(resultData) {
 | 
			
		||||
            this.selectedConditionId = resultData ? resultData.conditionId : '';
 | 
			
		||||
            this.persist(this.getDomainObjectConditionalStyle());
 | 
			
		||||
        },
 | 
			
		||||
        initializeStaticStyle(objectStyles) {
 | 
			
		||||
            let staticStyle = objectStyles && objectStyles.staticStyle;
 | 
			
		||||
@@ -411,19 +364,14 @@ export default {
 | 
			
		||||
            let found = this.findStyleByConditionId(conditionStyle.conditionId);
 | 
			
		||||
            if (found) {
 | 
			
		||||
                found.style = conditionStyle.style;
 | 
			
		||||
                this.selectedConditionId = found.conditionId;
 | 
			
		||||
                this.persist(this.getDomainObjectConditionalStyle());
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        getDomainObjectConditionalStyle(defaultConditionId) {
 | 
			
		||||
        getDomainObjectConditionalStyle() {
 | 
			
		||||
            let objectStyle = {
 | 
			
		||||
                styles: this.conditionalStyles,
 | 
			
		||||
                staticStyle: this.staticStyle,
 | 
			
		||||
                selectedConditionId: this.selectedConditionId
 | 
			
		||||
                staticStyle: this.staticStyle
 | 
			
		||||
            };
 | 
			
		||||
            if (defaultConditionId) {
 | 
			
		||||
                objectStyle.defaultConditionId = defaultConditionId;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.conditionSetDomainObject) {
 | 
			
		||||
                objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
 | 
			
		||||
            }
 | 
			
		||||
@@ -445,10 +393,6 @@ export default {
 | 
			
		||||
        getCondition(id) {
 | 
			
		||||
            return this.conditions ? this.conditions[id] : {};
 | 
			
		||||
        },
 | 
			
		||||
        applySelectedConditionStyle(conditionId) {
 | 
			
		||||
            this.selectedConditionId = conditionId;
 | 
			
		||||
            this.persist(this.getDomainObjectConditionalStyle());
 | 
			
		||||
        },
 | 
			
		||||
        persist(style) {
 | 
			
		||||
            this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -60,31 +60,6 @@
 | 
			
		||||
 | 
			
		||||
    &__condition {
 | 
			
		||||
        @include discreteItem();
 | 
			
		||||
        border: 1px solid transparent;
 | 
			
		||||
        pointer-events: none; // Prevent selecting when the object isn't being edited
 | 
			
		||||
 | 
			
		||||
        &.is-current {
 | 
			
		||||
            $c: $colorBodyFg;
 | 
			
		||||
            border-color: rgba($c, 0.2);
 | 
			
		||||
            background: rgba($c, 0.2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .is-editing & {
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
            pointer-events: initial;
 | 
			
		||||
            transition: $transOut;
 | 
			
		||||
 | 
			
		||||
            &:hover {
 | 
			
		||||
                background: rgba($colorBodyFg, 0.1);
 | 
			
		||||
                transition: $transIn;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &.is-current {
 | 
			
		||||
                $c: $editUIColorBg;
 | 
			
		||||
                border-color: $c;
 | 
			
		||||
                background: rgba($c, 0.1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .c-style {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@
 | 
			
		||||
 | 
			
		||||
import TelemetryCriterion from './TelemetryCriterion';
 | 
			
		||||
import { evaluateResults } from "../utils/evaluator";
 | 
			
		||||
import { getLatestTimestamp } from '../utils/time';
 | 
			
		||||
 | 
			
		||||
export default class AllTelemetryCriterion extends TelemetryCriterion {
 | 
			
		||||
 | 
			
		||||
@@ -48,21 +47,6 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
 | 
			
		||||
 | 
			
		||||
    updateTelemetry(telemetryObjects) {
 | 
			
		||||
        this.telemetryObjects = { ...telemetryObjects };
 | 
			
		||||
        this.removeTelemetryDataCache();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    removeTelemetryDataCache() {
 | 
			
		||||
        const telemetryCacheIds = Object.keys(this.telemetryDataCache);
 | 
			
		||||
        Object.values(this.telemetryObjects).forEach(telemetryObject => {
 | 
			
		||||
            const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
 | 
			
		||||
            const foundIndex = telemetryCacheIds.indexOf(id);
 | 
			
		||||
            if (foundIndex > -1) {
 | 
			
		||||
                telemetryCacheIds.splice(foundIndex, 1);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        telemetryCacheIds.forEach(id => {
 | 
			
		||||
            delete (this.telemetryDataCache[id]);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    formatData(data, telemetryObjects) {
 | 
			
		||||
@@ -108,53 +92,40 @@ export default class AllTelemetryCriterion extends TelemetryCriterion {
 | 
			
		||||
        this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    requestLAD(telemetryObjects) {
 | 
			
		||||
        const options = {
 | 
			
		||||
            strategy: 'latest',
 | 
			
		||||
            size: 1
 | 
			
		||||
        };
 | 
			
		||||
    requestLAD(options) {
 | 
			
		||||
        options = Object.assign({},
 | 
			
		||||
            options,
 | 
			
		||||
            {
 | 
			
		||||
                strategy: 'latest',
 | 
			
		||||
                size: 1
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (!this.isValid()) {
 | 
			
		||||
            return this.formatData({}, telemetryObjects);
 | 
			
		||||
            return this.formatData({}, options.telemetryObjects);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let keys = Object.keys(Object.assign({}, telemetryObjects));
 | 
			
		||||
        let keys = Object.keys(Object.assign({}, options.telemetryObjects));
 | 
			
		||||
        const telemetryRequests = keys
 | 
			
		||||
            .map(key => this.openmct.telemetry.request(
 | 
			
		||||
                telemetryObjects[key],
 | 
			
		||||
                options.telemetryObjects[key],
 | 
			
		||||
                options
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
        let telemetryDataCache = {};
 | 
			
		||||
        return Promise.all(telemetryRequests)
 | 
			
		||||
            .then(telemetryRequestsResults => {
 | 
			
		||||
                let latestTimestamp;
 | 
			
		||||
                const timeSystems = this.openmct.time.getAllTimeSystems();
 | 
			
		||||
                const timeSystem = this.openmct.time.timeSystem();
 | 
			
		||||
 | 
			
		||||
                let latestDatum;
 | 
			
		||||
                telemetryRequestsResults.forEach((results, index) => {
 | 
			
		||||
                    const latestDatum = results.length ? results[results.length - 1] : {};
 | 
			
		||||
                    const datumId = keys[index];
 | 
			
		||||
                    const normalizedDatum = this.createNormalizedDatum(latestDatum, telemetryObjects[datumId]);
 | 
			
		||||
 | 
			
		||||
                    telemetryDataCache[datumId] = this.computeResult(normalizedDatum);
 | 
			
		||||
 | 
			
		||||
                    latestTimestamp = getLatestTimestamp(
 | 
			
		||||
                        latestTimestamp,
 | 
			
		||||
                        normalizedDatum,
 | 
			
		||||
                        timeSystems,
 | 
			
		||||
                        timeSystem
 | 
			
		||||
                    );
 | 
			
		||||
                    latestDatum = results.length ? results[results.length - 1] : {};
 | 
			
		||||
                    if (index < telemetryRequestsResults.length-1) {
 | 
			
		||||
                        if (latestDatum) {
 | 
			
		||||
                            this.telemetryDataCache[latestDatum.id] = this.computeResult(latestDatum);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                const datum = {
 | 
			
		||||
                    result: evaluateResults(Object.values(telemetryDataCache), this.telemetry),
 | 
			
		||||
                    ...latestTimestamp
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                return {
 | 
			
		||||
                    id: this.id,
 | 
			
		||||
                    data: datum
 | 
			
		||||
                    data: this.formatData(latestDatum, options.telemetryObjects)
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -61,21 +61,6 @@ export default class TelemetryCriterion extends EventEmitter {
 | 
			
		||||
        this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createNormalizedDatum(telemetryDatum, endpoint) {
 | 
			
		||||
        const id = this.openmct.objects.makeKeyString(endpoint.identifier);
 | 
			
		||||
        const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
 | 
			
		||||
 | 
			
		||||
        const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
 | 
			
		||||
            const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
 | 
			
		||||
            datum[metadatum.key] = formatter.parse(telemetryDatum[metadatum.source]);
 | 
			
		||||
            return datum;
 | 
			
		||||
        }, {});
 | 
			
		||||
 | 
			
		||||
        normalizedDatum.id = id;
 | 
			
		||||
 | 
			
		||||
        return normalizedDatum;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    formatData(data) {
 | 
			
		||||
        const datum = {
 | 
			
		||||
            result: this.computeResult(data)
 | 
			
		||||
@@ -94,11 +79,14 @@ export default class TelemetryCriterion extends EventEmitter {
 | 
			
		||||
        this.result = this.computeResult(validatedData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    requestLAD() {
 | 
			
		||||
        const options = {
 | 
			
		||||
            strategy: 'latest',
 | 
			
		||||
            size: 1
 | 
			
		||||
        };
 | 
			
		||||
    requestLAD(options) {
 | 
			
		||||
        options = Object.assign({},
 | 
			
		||||
            options,
 | 
			
		||||
            {
 | 
			
		||||
                strategy: 'latest',
 | 
			
		||||
                size: 1
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (!this.isValid()) {
 | 
			
		||||
            return {
 | 
			
		||||
@@ -112,11 +100,9 @@ export default class TelemetryCriterion extends EventEmitter {
 | 
			
		||||
            options
 | 
			
		||||
        ).then(results => {
 | 
			
		||||
            const latestDatum = results.length ? results[results.length - 1] : {};
 | 
			
		||||
            const normalizedDatum = this.createNormalizedDatum(latestDatum, this.telemetryObject);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                id: this.id,
 | 
			
		||||
                data: this.formatData(normalizedDatum)
 | 
			
		||||
                data: this.formatData(latestDatum)
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -34,10 +34,6 @@ const convertToStrings = (input) => {
 | 
			
		||||
    return stringInputs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const joinValues = (values, length) => {
 | 
			
		||||
    return values.slice(0, length).join(', ');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const OPERATIONS = [
 | 
			
		||||
    {
 | 
			
		||||
        name: 'equalTo',
 | 
			
		||||
@@ -48,7 +44,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is ' + joinValues(values, 1);
 | 
			
		||||
            return ' is ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -60,7 +56,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is not ' + joinValues(values, 1);
 | 
			
		||||
            return ' is not ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -72,7 +68,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' > ' + joinValues(values, 1);
 | 
			
		||||
            return ' > ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -84,7 +80,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' < ' + joinValues(values, 1);
 | 
			
		||||
            return ' < ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -96,7 +92,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' >= ' + joinValues(values, 1);
 | 
			
		||||
            return ' >= ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -108,7 +104,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' <= ' + joinValues(values, 1);
 | 
			
		||||
            return ' <= ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -150,7 +146,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['string'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' contains ' + joinValues(values, 1);
 | 
			
		||||
            return ' contains ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -162,7 +158,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['string'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' does not contain ' + joinValues(values, 1);
 | 
			
		||||
            return ' does not contain ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -174,7 +170,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['string'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' starts with ' + joinValues(values, 1);
 | 
			
		||||
            return ' starts with ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -186,7 +182,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['string'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' ends with ' + joinValues(values, 1);
 | 
			
		||||
            return ' ends with ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -198,7 +194,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['string'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is exactly ' + joinValues(values, 1);
 | 
			
		||||
            return ' is exactly ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -235,7 +231,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['enum'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is ' + joinValues(values, 1);
 | 
			
		||||
            return ' is ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -248,7 +244,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['enum'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is not ' + joinValues(values, 1);
 | 
			
		||||
            return ' is not ' + values.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -52,7 +52,7 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        initObjectStyles() {
 | 
			
		||||
            if (!this.styleRuleManager) {
 | 
			
		||||
                this.styleRuleManager = new StyleRuleManager(this.objectStyle, this.openmct, this.updateStyle.bind(this), true);
 | 
			
		||||
                this.styleRuleManager = new StyleRuleManager(this.objectStyle, this.openmct, this.updateStyle.bind(this));
 | 
			
		||||
            } else {
 | 
			
		||||
                this.styleRuleManager.updateObjectStyleConfig(this.objectStyle);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ define([], function () {
 | 
			
		||||
        this.timeFormat = 'local-format';
 | 
			
		||||
        this.durationFormat = 'duration';
 | 
			
		||||
 | 
			
		||||
        this.isUTCBased = true;
 | 
			
		||||
        this.isUTCBased = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return LocalTimeSystem;
 | 
			
		||||
 
 | 
			
		||||
@@ -162,31 +162,19 @@ export default {
 | 
			
		||||
            }).show(this.embed.snapshot.src);
 | 
			
		||||
        },
 | 
			
		||||
        changeLocation() {
 | 
			
		||||
            this.openmct.time.stopClock();
 | 
			
		||||
            this.openmct.time.bounds({
 | 
			
		||||
                start: this.embed.bounds.start,
 | 
			
		||||
                end: this.embed.bounds.end
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            const link = this.embed.historicLink;
 | 
			
		||||
            if (!link) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const bounds = this.openmct.time.bounds();
 | 
			
		||||
            const isTimeBoundChanged = this.embed.bounds.start !== bounds.start
 | 
			
		||||
                && this.embed.bounds.end !== bounds.end;
 | 
			
		||||
            const isFixedTimespanMode = !this.openmct.time.clock();
 | 
			
		||||
 | 
			
		||||
            window.location.href = link;
 | 
			
		||||
 | 
			
		||||
            let message = '';
 | 
			
		||||
            if (isTimeBoundChanged) {
 | 
			
		||||
                this.openmct.time.bounds({
 | 
			
		||||
                    start: this.embed.bounds.start,
 | 
			
		||||
                    end: this.embed.bounds.end
 | 
			
		||||
                });
 | 
			
		||||
                message = 'Time bound values changed';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!isFixedTimespanMode) {
 | 
			
		||||
                message = 'Time bound values changed to fixed timespan mode';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const message = 'Time bounds changed to fixed timespan mode';
 | 
			
		||||
            this.openmct.notifications.alert(message);
 | 
			
		||||
        },
 | 
			
		||||
        formatTime(unixTime, timeFormat) {
 | 
			
		||||
 
 | 
			
		||||
@@ -221,7 +221,7 @@ export default {
 | 
			
		||||
            return position;
 | 
			
		||||
        },
 | 
			
		||||
        formatTime(unixTime, timeFormat) {
 | 
			
		||||
            return Moment.utc(unixTime).format(timeFormat);
 | 
			
		||||
            return Moment(unixTime).format(timeFormat);
 | 
			
		||||
        },
 | 
			
		||||
        moveSnapshot(snapshotId) {
 | 
			
		||||
            const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
 | 
			
		||||
 
 | 
			
		||||
@@ -40,18 +40,6 @@ export default {
 | 
			
		||||
            default() {
 | 
			
		||||
                return {};
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        ignoreLink: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            default() {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        objectPath: {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            default() {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
@@ -109,27 +97,17 @@ export default {
 | 
			
		||||
            this.showMenu = false;
 | 
			
		||||
        },
 | 
			
		||||
        snapshot(notebook) {
 | 
			
		||||
            this.hideMenu();
 | 
			
		||||
            let element = document.getElementsByClassName("l-shell__main-container")[0];
 | 
			
		||||
            const bounds = this.openmct.time.bounds();
 | 
			
		||||
            const objectPath = this.openmct.router.path;
 | 
			
		||||
            const snapshotMeta = {
 | 
			
		||||
                bounds,
 | 
			
		||||
                link: window.location.href,
 | 
			
		||||
                objectPath,
 | 
			
		||||
                openmct: this.openmct
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.$nextTick(() => {
 | 
			
		||||
                const element = document.querySelector('.c-overlay__contents')
 | 
			
		||||
                    || document.getElementsByClassName('l-shell__main-container')[0];
 | 
			
		||||
 | 
			
		||||
                const bounds = this.openmct.time.bounds();
 | 
			
		||||
                const link = !this.ignoreLink
 | 
			
		||||
                    ? window.location.href
 | 
			
		||||
                    : null;
 | 
			
		||||
 | 
			
		||||
                const objectPath = this.objectPath || this.openmct.router.path;
 | 
			
		||||
                const snapshotMeta = {
 | 
			
		||||
                    bounds,
 | 
			
		||||
                    link,
 | 
			
		||||
                    objectPath,
 | 
			
		||||
                    openmct: this.openmct
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                this.notebookSnapshot.capture(snapshotMeta, notebook.type, element);
 | 
			
		||||
            });
 | 
			
		||||
            this.notebookSnapshot.capture(snapshotMeta, notebook.type, element);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -495,7 +495,7 @@ export default {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (id !== defaultNotebookSection.id) {
 | 
			
		||||
            if (section.id !== defaultNotebookSection.id) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
-->
 | 
			
		||||
<div ng-controller="StackedPlotController as stackedPlot"
 | 
			
		||||
      class="c-plot c-plot--stacked holder holder-plot has-control-bar">
 | 
			
		||||
    <div class="c-control-bar" ng-show="!stackedPlot.hideExportButtons">
 | 
			
		||||
    <div class="l-control-bar" ng-show="!stackedPlot.hideExportButtons">
 | 
			
		||||
       <span class="c-button-set c-button-set--strip-h">
 | 
			
		||||
          <button class="c-button icon-download"
 | 
			
		||||
             ng-click="stackedPlot.exportPNG()"
 | 
			
		||||
 
 | 
			
		||||
@@ -54,29 +54,27 @@ function (
 | 
			
		||||
     * @constructor
 | 
			
		||||
     */
 | 
			
		||||
    function MCTChartController($scope) {
 | 
			
		||||
        this.$onInit = () => {
 | 
			
		||||
            this.$scope = $scope;
 | 
			
		||||
            this.isDestroyed = false;
 | 
			
		||||
            this.lines = [];
 | 
			
		||||
            this.pointSets = [];
 | 
			
		||||
            this.alarmSets = [];
 | 
			
		||||
            this.offset = {};
 | 
			
		||||
            this.config = $scope.config;
 | 
			
		||||
            this.listenTo(this.$scope, '$destroy', this.destroy, this);
 | 
			
		||||
            this.draw = this.draw.bind(this);
 | 
			
		||||
            this.scheduleDraw = this.scheduleDraw.bind(this);
 | 
			
		||||
            this.seriesElements = new WeakMap();
 | 
			
		||||
        this.$scope = $scope;
 | 
			
		||||
        this.isDestroyed = false;
 | 
			
		||||
        this.lines = [];
 | 
			
		||||
        this.pointSets = [];
 | 
			
		||||
        this.alarmSets = [];
 | 
			
		||||
        this.offset = {};
 | 
			
		||||
        this.config = $scope.config;
 | 
			
		||||
        this.listenTo(this.$scope, '$destroy', this.destroy, this);
 | 
			
		||||
        this.draw = this.draw.bind(this);
 | 
			
		||||
        this.scheduleDraw = this.scheduleDraw.bind(this);
 | 
			
		||||
        this.seriesElements = new WeakMap();
 | 
			
		||||
 | 
			
		||||
            this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
 | 
			
		||||
            this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
 | 
			
		||||
            this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
 | 
			
		||||
            this.listenTo(this.config.xAxis, 'change:key', this.clearOffset, this);
 | 
			
		||||
            this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
 | 
			
		||||
            this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
 | 
			
		||||
            this.$scope.$watch('highlights', this.scheduleDraw);
 | 
			
		||||
            this.$scope.$watch('rectangles', this.scheduleDraw);
 | 
			
		||||
            this.config.series.forEach(this.onSeriesAdd, this);
 | 
			
		||||
        }
 | 
			
		||||
        this.listenTo(this.config.series, 'add', this.onSeriesAdd, this);
 | 
			
		||||
        this.listenTo(this.config.series, 'remove', this.onSeriesRemove, this);
 | 
			
		||||
        this.listenTo(this.config.yAxis, 'change:key', this.clearOffset, this);
 | 
			
		||||
        this.listenTo(this.config.xAxis, 'change:key', this.clearOffset, this);
 | 
			
		||||
        this.listenTo(this.config.yAxis, 'change', this.scheduleDraw);
 | 
			
		||||
        this.listenTo(this.config.xAxis, 'change', this.scheduleDraw);
 | 
			
		||||
        this.$scope.$watch('highlights', this.scheduleDraw);
 | 
			
		||||
        this.$scope.$watch('rectangles', this.scheduleDraw);
 | 
			
		||||
        this.config.series.forEach(this.onSeriesAdd, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    eventHelpers.extend(MCTChartController.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -34,27 +34,25 @@ define([
 | 
			
		||||
     * values near the cursor.
 | 
			
		||||
     */
 | 
			
		||||
    function MCTPlotController($scope, $element, $window) {
 | 
			
		||||
        this.$onInit = () => {
 | 
			
		||||
            this.$scope = $scope;
 | 
			
		||||
            this.$scope.config = this.config;
 | 
			
		||||
            this.$scope.plot = this;
 | 
			
		||||
            this.$element = $element;
 | 
			
		||||
            this.$window = $window;
 | 
			
		||||
        this.$scope = $scope;
 | 
			
		||||
        this.$scope.config = this.config;
 | 
			
		||||
        this.$scope.plot = this;
 | 
			
		||||
        this.$element = $element;
 | 
			
		||||
        this.$window = $window;
 | 
			
		||||
 | 
			
		||||
            this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
 | 
			
		||||
            this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
 | 
			
		||||
        this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
 | 
			
		||||
        this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
 | 
			
		||||
 | 
			
		||||
            this.pan = undefined;
 | 
			
		||||
            this.marquee = undefined;
 | 
			
		||||
        this.pan = undefined;
 | 
			
		||||
        this.marquee = undefined;
 | 
			
		||||
 | 
			
		||||
            this.chartElementBounds = undefined;
 | 
			
		||||
            this.tickUpdate = false;
 | 
			
		||||
        this.chartElementBounds = undefined;
 | 
			
		||||
        this.tickUpdate = false;
 | 
			
		||||
 | 
			
		||||
            this.$scope.plotHistory = this.plotHistory = [];
 | 
			
		||||
            this.listenTo(this.$scope, 'plot:clearHistory', this.clear, this);
 | 
			
		||||
        this.$scope.plotHistory = this.plotHistory = [];
 | 
			
		||||
        this.listenTo(this.$scope, 'plot:clearHistory', this.clear, this);
 | 
			
		||||
 | 
			
		||||
            this.initialize();
 | 
			
		||||
        }
 | 
			
		||||
        this.initialize();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.$inject = ['$scope', '$element', '$window'];
 | 
			
		||||
@@ -67,10 +65,10 @@ define([
 | 
			
		||||
        }
 | 
			
		||||
        this.$canvas = this.$element.find('canvas');
 | 
			
		||||
 | 
			
		||||
        this.listenTo(this.$canvas, 'click', this.onMouseClick, this);
 | 
			
		||||
        this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this);
 | 
			
		||||
        this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
 | 
			
		||||
        this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
 | 
			
		||||
        this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
 | 
			
		||||
 | 
			
		||||
        this.watchForMarquee();
 | 
			
		||||
    };
 | 
			
		||||
@@ -78,6 +76,7 @@ define([
 | 
			
		||||
    MCTPlotController.prototype.initialize = function () {
 | 
			
		||||
        this.$canvas = this.$element.find('canvas');
 | 
			
		||||
 | 
			
		||||
        this.listenTo(this.$canvas, 'click', this.onMouseClick, this);
 | 
			
		||||
        this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this);
 | 
			
		||||
        this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
 | 
			
		||||
        this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
 | 
			
		||||
@@ -209,6 +208,23 @@ define([
 | 
			
		||||
        this.highlightValues(point);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.onMouseClick = function ($event) {
 | 
			
		||||
        const isClick = this.isMouseClick();
 | 
			
		||||
        if (this.pan) {
 | 
			
		||||
            this.endPan($event);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.marquee) {
 | 
			
		||||
            this.endMarquee($event);
 | 
			
		||||
        }
 | 
			
		||||
        this.$scope.$apply();
 | 
			
		||||
 | 
			
		||||
        if (!this.$scope.highlights.length || !isClick) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.highlightValues = function (point) {
 | 
			
		||||
        this.highlightPoint = point;
 | 
			
		||||
        this.$scope.$emit('plot:highlight:update', point);
 | 
			
		||||
@@ -256,23 +272,11 @@ define([
 | 
			
		||||
    MCTPlotController.prototype.onMouseUp = function ($event) {
 | 
			
		||||
        this.stopListening(this.$window, 'mouseup', this.onMouseUp, this);
 | 
			
		||||
        this.stopListening(this.$window, 'mousemove', this.trackMousePosition, this);
 | 
			
		||||
 | 
			
		||||
        if (this.isMouseClick()) {
 | 
			
		||||
            this.$scope.lockHighlightPoint = !this.$scope.lockHighlightPoint;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.allowPan) {
 | 
			
		||||
            return this.endPan($event);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.allowMarquee) {
 | 
			
		||||
            return this.endMarquee($event);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCTPlotController.prototype.isMouseClick = function () {
 | 
			
		||||
        if (!this.marquee) {
 | 
			
		||||
            return false;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const { start, end } = this.marquee;
 | 
			
		||||
 
 | 
			
		||||
@@ -114,17 +114,15 @@ define([
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function MCTTicksController($scope, $element) {
 | 
			
		||||
        this.$onInit = () => {
 | 
			
		||||
            this.$scope = $scope;
 | 
			
		||||
            this.$element = $element;
 | 
			
		||||
        this.$scope = $scope;
 | 
			
		||||
        this.$element = $element;
 | 
			
		||||
 | 
			
		||||
            this.tickCount = 4;
 | 
			
		||||
            this.tickUpdate = false;
 | 
			
		||||
            this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this);
 | 
			
		||||
            this.listenTo(this.axis, 'change:format', this.updateTicks, this);
 | 
			
		||||
            this.listenTo(this.$scope, '$destroy', this.stopListening, this);
 | 
			
		||||
            this.updateTicks();
 | 
			
		||||
        }
 | 
			
		||||
        this.tickCount = 4;
 | 
			
		||||
        this.tickUpdate = false;
 | 
			
		||||
        this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this);
 | 
			
		||||
        this.listenTo(this.axis, 'change:format', this.updateTicks, this);
 | 
			
		||||
        this.listenTo(this.$scope, '$destroy', this.stopListening, this);
 | 
			
		||||
        this.updateTicks();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MCTTicksController.$inject = ['$scope', '$element'];
 | 
			
		||||
 
 | 
			
		||||
@@ -81,8 +81,7 @@ define(
 | 
			
		||||
                        clonedElement.classList.add(className);
 | 
			
		||||
                    }
 | 
			
		||||
                    element.id = oldId;
 | 
			
		||||
                },
 | 
			
		||||
                removeContainer: true // Set to false to debug what html2canvas renders
 | 
			
		||||
                }
 | 
			
		||||
            }).then(function (canvas) {
 | 
			
		||||
                dialog.dismiss();
 | 
			
		||||
                return new Promise(function (resolve, reject) {
 | 
			
		||||
 
 | 
			
		||||
@@ -227,9 +227,8 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    PlotController.prototype.stopLoading = function () {
 | 
			
		||||
        this.$scope.$evalAsync(() => {
 | 
			
		||||
            this.$scope.pending -= 1;
 | 
			
		||||
        });
 | 
			
		||||
        this.$scope.pending -= 1;
 | 
			
		||||
        this.$scope.$digest();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -88,8 +88,7 @@ define([
 | 
			
		||||
    var bundleMap = {
 | 
			
		||||
        LocalStorage: 'platform/persistence/local',
 | 
			
		||||
        MyItems: 'platform/features/my-items',
 | 
			
		||||
        CouchDB: 'platform/persistence/couch',
 | 
			
		||||
        Elasticsearch: 'platform/persistence/elastic'
 | 
			
		||||
        CouchDB: 'platform/persistence/couch'
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) {
 | 
			
		||||
 
 | 
			
		||||
@@ -165,16 +165,7 @@
 | 
			
		||||
/******************************* LEGACY */
 | 
			
		||||
.s-status-taking-snapshot,
 | 
			
		||||
.overlay.snapshot {
 | 
			
		||||
    .c-table {
 | 
			
		||||
        &__body-w {
 | 
			
		||||
            overflow: auto; // Handle overflow-y issues with tables and html2canvas
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &-control-bar {
 | 
			
		||||
            display: none;
 | 
			
		||||
            + * {
 | 
			
		||||
                margin-top: 0 !important;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // Handle overflow-y issues with tables and html2canvas
 | 
			
		||||
    // Replaces .l-sticky-headers .l-tabular-body { overflow: auto; }
 | 
			
		||||
    .c-table__body-w { overflow: auto; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,12 +22,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div
 | 
			
		||||
    class="c-conductor"
 | 
			
		||||
    :class="[
 | 
			
		||||
        { 'is-zooming': isZooming },
 | 
			
		||||
        { 'is-panning': isPanning },
 | 
			
		||||
        { 'alt-pressed': altPressed },
 | 
			
		||||
        isFixed ? 'is-fixed-mode' : 'is-realtime-mode'
 | 
			
		||||
    ]"
 | 
			
		||||
    :class="[isFixed ? 'is-fixed-mode' : 'is-realtime-mode']"
 | 
			
		||||
>
 | 
			
		||||
    <form
 | 
			
		||||
        ref="conductorForm"
 | 
			
		||||
@@ -57,7 +52,7 @@
 | 
			
		||||
                    type="text"
 | 
			
		||||
                    autocorrect="off"
 | 
			
		||||
                    spellcheck="false"
 | 
			
		||||
                    @change="validateAllBounds('startDate'); submitForm()"
 | 
			
		||||
                    @change="validateAllBounds(); submitForm()"
 | 
			
		||||
                >
 | 
			
		||||
                <date-picker
 | 
			
		||||
                    v-if="isFixed && isUTCBased"
 | 
			
		||||
@@ -97,7 +92,7 @@
 | 
			
		||||
                    autocorrect="off"
 | 
			
		||||
                    spellcheck="false"
 | 
			
		||||
                    :disabled="!isFixed"
 | 
			
		||||
                    @change="validateAllBounds('endDate'); submitForm()"
 | 
			
		||||
                    @change="validateAllBounds(); submitForm()"
 | 
			
		||||
                >
 | 
			
		||||
                <date-picker
 | 
			
		||||
                    v-if="isFixed && isUTCBased"
 | 
			
		||||
@@ -127,25 +122,14 @@
 | 
			
		||||
 | 
			
		||||
            <conductor-axis
 | 
			
		||||
                class="c-conductor__ticks"
 | 
			
		||||
                :view-bounds="viewBounds"
 | 
			
		||||
                :is-fixed="isFixed"
 | 
			
		||||
                :alt-pressed="altPressed"
 | 
			
		||||
                @endPan="endPan"
 | 
			
		||||
                @endZoom="endZoom"
 | 
			
		||||
                @panAxis="pan"
 | 
			
		||||
                @zoomAxis="zoom"
 | 
			
		||||
                :bounds="rawBounds"
 | 
			
		||||
                @panAxis="setViewFromBounds"
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="c-conductor__controls">
 | 
			
		||||
            <!-- Mode, time system menu buttons and duration slider -->
 | 
			
		||||
            <ConductorMode class="c-conductor__mode-select" />
 | 
			
		||||
            <ConductorTimeSystem class="c-conductor__time-system-select" />
 | 
			
		||||
            <ConductorHistory
 | 
			
		||||
                v-if="isFixed"
 | 
			
		||||
                class="c-conductor__history-select"
 | 
			
		||||
                :bounds="openmct.time.bounds()"
 | 
			
		||||
                :time-system="timeSystem"
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
        <input
 | 
			
		||||
            type="submit"
 | 
			
		||||
@@ -161,7 +145,6 @@ import ConductorTimeSystem from './ConductorTimeSystem.vue';
 | 
			
		||||
import DatePicker from './DatePicker.vue';
 | 
			
		||||
import ConductorAxis from './ConductorAxis.vue';
 | 
			
		||||
import ConductorModeIcon from './ConductorModeIcon.vue';
 | 
			
		||||
import ConductorHistory from './ConductorHistory.vue'
 | 
			
		||||
 | 
			
		||||
const DEFAULT_DURATION_FORMATTER = 'duration';
 | 
			
		||||
 | 
			
		||||
@@ -172,8 +155,7 @@ export default {
 | 
			
		||||
        ConductorTimeSystem,
 | 
			
		||||
        DatePicker,
 | 
			
		||||
        ConductorAxis,
 | 
			
		||||
        ConductorModeIcon,
 | 
			
		||||
        ConductorHistory
 | 
			
		||||
        ConductorModeIcon
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        let bounds = this.openmct.time.bounds();
 | 
			
		||||
@@ -183,7 +165,6 @@ export default {
 | 
			
		||||
        let durationFormatter = this.getFormatter(timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            timeSystem: timeSystem,
 | 
			
		||||
            timeFormatter: timeFormatter,
 | 
			
		||||
            durationFormatter: durationFormatter,
 | 
			
		||||
            offsets: {
 | 
			
		||||
@@ -194,64 +175,29 @@ export default {
 | 
			
		||||
                start: timeFormatter.format(bounds.start),
 | 
			
		||||
                end: timeFormatter.format(bounds.end)
 | 
			
		||||
            },
 | 
			
		||||
            viewBounds: {
 | 
			
		||||
            rawBounds: {
 | 
			
		||||
                start: bounds.start,
 | 
			
		||||
                end: bounds.end
 | 
			
		||||
            },
 | 
			
		||||
            isFixed: this.openmct.time.clock() === undefined,
 | 
			
		||||
            isUTCBased: timeSystem.isUTCBased,
 | 
			
		||||
            showDatePicker: false,
 | 
			
		||||
            altPressed: false,
 | 
			
		||||
            isPanning: false,
 | 
			
		||||
            isZooming: false
 | 
			
		||||
            showDatePicker: false
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    created() {
 | 
			
		||||
        document.addEventListener('keydown', (e) => {
 | 
			
		||||
            if (e.key === 'Alt') {
 | 
			
		||||
                this.altPressed = true;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        document.addEventListener('keyup', (e) => {
 | 
			
		||||
            if (e.key === 'Alt') {
 | 
			
		||||
                this.altPressed = false;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.timeSystem())));
 | 
			
		||||
 | 
			
		||||
        this.openmct.time.on('bounds', this.setViewFromBounds);
 | 
			
		||||
        this.openmct.time.on('timeSystem', this.setTimeSystem);
 | 
			
		||||
        this.openmct.time.on('clock', this.setViewFromClock);
 | 
			
		||||
        this.openmct.time.on('clockOffsets', this.setViewFromOffsets)
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        pan(bounds) {
 | 
			
		||||
            this.isPanning = true;
 | 
			
		||||
            this.setViewFromBounds(bounds);
 | 
			
		||||
        },
 | 
			
		||||
        endPan(bounds) {
 | 
			
		||||
            this.isPanning = false;
 | 
			
		||||
            if (bounds) {
 | 
			
		||||
                this.openmct.time.bounds(bounds);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        zoom(bounds) {
 | 
			
		||||
            this.isZooming = true;
 | 
			
		||||
            this.formattedBounds.start = this.timeFormatter.format(bounds.start);
 | 
			
		||||
            this.formattedBounds.end = this.timeFormatter.format(bounds.end);
 | 
			
		||||
        },
 | 
			
		||||
        endZoom(bounds) {
 | 
			
		||||
            const _bounds = bounds ? bounds : this.openmct.time.bounds();
 | 
			
		||||
            this.isZooming = false;
 | 
			
		||||
 | 
			
		||||
            this.openmct.time.bounds(_bounds);
 | 
			
		||||
        },
 | 
			
		||||
        setTimeSystem(timeSystem) {
 | 
			
		||||
            this.timeSystem = timeSystem
 | 
			
		||||
            this.timeFormatter = this.getFormatter(timeSystem.timeFormat);
 | 
			
		||||
            this.durationFormatter = this.getFormatter(
 | 
			
		||||
                timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
 | 
			
		||||
 | 
			
		||||
            this.isUTCBased = timeSystem.isUTCBased;
 | 
			
		||||
        },
 | 
			
		||||
        setOffsetsFromView($event) {
 | 
			
		||||
@@ -291,8 +237,8 @@ export default {
 | 
			
		||||
        setViewFromBounds(bounds) {
 | 
			
		||||
            this.formattedBounds.start = this.timeFormatter.format(bounds.start);
 | 
			
		||||
            this.formattedBounds.end = this.timeFormatter.format(bounds.end);
 | 
			
		||||
            this.viewBounds.start = bounds.start;
 | 
			
		||||
            this.viewBounds.end = bounds.end;
 | 
			
		||||
            this.rawBounds.start = bounds.start;
 | 
			
		||||
            this.rawBounds.end = bounds.end;
 | 
			
		||||
        },
 | 
			
		||||
        setViewFromOffsets(offsets) {
 | 
			
		||||
            this.offsets.start = this.durationFormatter.format(Math.abs(offsets.start));
 | 
			
		||||
@@ -305,15 +251,6 @@ export default {
 | 
			
		||||
                this.setOffsetsFromView();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        getBoundsLimit() {
 | 
			
		||||
            const configuration = this.configuration.menuOptions
 | 
			
		||||
                .filter(option => option.timeSystem ===  this.timeSystem.key)
 | 
			
		||||
                .find(option => option.limit);
 | 
			
		||||
 | 
			
		||||
            const limit = configuration ? configuration.limit : undefined;
 | 
			
		||||
 | 
			
		||||
            return limit;
 | 
			
		||||
        },
 | 
			
		||||
        clearAllValidation() {
 | 
			
		||||
            if (this.isFixed) {
 | 
			
		||||
                [this.$refs.startDate, this.$refs.endDate].forEach(this.clearValidationForInput);
 | 
			
		||||
@@ -325,37 +262,37 @@ export default {
 | 
			
		||||
            input.setCustomValidity('');
 | 
			
		||||
            input.title = '';
 | 
			
		||||
        },
 | 
			
		||||
        validateAllBounds(ref) {
 | 
			
		||||
            const input = this.$refs[ref];
 | 
			
		||||
            let validationResult = true;
 | 
			
		||||
        validateAllBounds() {
 | 
			
		||||
            return [this.$refs.startDate, this.$refs.endDate].every((input) => {
 | 
			
		||||
                let validationResult = true;
 | 
			
		||||
                let formattedDate;
 | 
			
		||||
 | 
			
		||||
            const formattedDate = input === this.$refs.startDate
 | 
			
		||||
                ? this.formattedBounds.start
 | 
			
		||||
                : this.formattedBounds.end
 | 
			
		||||
            ;
 | 
			
		||||
 | 
			
		||||
            if (!this.timeFormatter.validate(formattedDate)) {
 | 
			
		||||
                validationResult = 'Invalid date';
 | 
			
		||||
            } else {
 | 
			
		||||
                let boundsValues = {
 | 
			
		||||
                    start: this.timeFormatter.parse(this.formattedBounds.start),
 | 
			
		||||
                    end: this.timeFormatter.parse(this.formattedBounds.end)
 | 
			
		||||
                };
 | 
			
		||||
                const limit = this.getBoundsLimit();
 | 
			
		||||
 | 
			
		||||
                if (
 | 
			
		||||
                    this.timeSystem.isUTCBased
 | 
			
		||||
                    && limit
 | 
			
		||||
                    && boundsValues.end - boundsValues.start > limit
 | 
			
		||||
                ) {
 | 
			
		||||
                    validationResult = "Start and end difference exceeds allowable limit";
 | 
			
		||||
                if (input === this.$refs.startDate) {
 | 
			
		||||
                    formattedDate = this.formattedBounds.start;
 | 
			
		||||
                } else {
 | 
			
		||||
                    formattedDate = this.formattedBounds.end;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!this.timeFormatter.validate(formattedDate)) {
 | 
			
		||||
                    validationResult = 'Invalid date';
 | 
			
		||||
                } else {
 | 
			
		||||
                    let boundsValues = {
 | 
			
		||||
                        start: this.timeFormatter.parse(this.formattedBounds.start),
 | 
			
		||||
                        end: this.timeFormatter.parse(this.formattedBounds.end)
 | 
			
		||||
                    };
 | 
			
		||||
                    validationResult = this.openmct.time.validateBounds(boundsValues);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.handleValidationResult(input, validationResult);
 | 
			
		||||
                if (validationResult !== true) {
 | 
			
		||||
                    input.setCustomValidity(validationResult);
 | 
			
		||||
                    input.title = validationResult;
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else {
 | 
			
		||||
                    input.setCustomValidity('');
 | 
			
		||||
                    input.title = '';
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        validateAllOffsets(event) {
 | 
			
		||||
            return [this.$refs.startOffset, this.$refs.endOffset].every((input) => {
 | 
			
		||||
@@ -378,20 +315,17 @@ export default {
 | 
			
		||||
                    validationResult = this.openmct.time.validateOffsets(offsetValues);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.handleValidationResult(input, validationResult);
 | 
			
		||||
                if (validationResult !== true) {
 | 
			
		||||
                    input.setCustomValidity(validationResult);
 | 
			
		||||
                    input.title = validationResult;
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else {
 | 
			
		||||
                    input.setCustomValidity('');
 | 
			
		||||
                    input.title = '';
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        handleValidationResult(input, validationResult) {
 | 
			
		||||
            if (validationResult !== true) {
 | 
			
		||||
                input.setCustomValidity(validationResult);
 | 
			
		||||
                input.title = validationResult;
 | 
			
		||||
                return false;
 | 
			
		||||
            } else {
 | 
			
		||||
                input.setCustomValidity('');
 | 
			
		||||
                input.title = '';
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        submitForm() {
 | 
			
		||||
            // Allow Vue model to catch up to user input.
 | 
			
		||||
            // Submitting form will cause validation messages to display (but only if triggered by button click)
 | 
			
		||||
@@ -404,12 +338,12 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        startDateSelected(date) {
 | 
			
		||||
            this.formattedBounds.start = this.timeFormatter.format(date);
 | 
			
		||||
            this.validateAllBounds('startDate');
 | 
			
		||||
            this.validateAllBounds();
 | 
			
		||||
            this.submitForm();
 | 
			
		||||
        },
 | 
			
		||||
        endDateSelected(date) {
 | 
			
		||||
            this.formattedBounds.end = this.timeFormatter.format(date);
 | 
			
		||||
            this.validateAllBounds('endDate');
 | 
			
		||||
            this.validateAllBounds();
 | 
			
		||||
            this.submitForm();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -24,12 +24,7 @@
 | 
			
		||||
    ref="axisHolder"
 | 
			
		||||
    class="c-conductor-axis"
 | 
			
		||||
    @mousedown="dragStart($event)"
 | 
			
		||||
>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-conductor-axis__zoom-indicator"
 | 
			
		||||
        :style="zoomStyle"
 | 
			
		||||
    ></div>
 | 
			
		||||
</div>
 | 
			
		||||
></div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
@@ -48,35 +43,14 @@ const PIXELS_PER_TICK_WIDE = 200;
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
    props: {
 | 
			
		||||
        viewBounds: {
 | 
			
		||||
        bounds: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        },
 | 
			
		||||
        isFixed: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            required: true
 | 
			
		||||
        },
 | 
			
		||||
        altPressed: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            inPanMode: false,
 | 
			
		||||
            dragStartX: undefined,
 | 
			
		||||
            dragX: undefined,
 | 
			
		||||
            zoomStyle: {}
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        inZoomMode() {
 | 
			
		||||
            return !this.inPanMode;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        viewBounds: {
 | 
			
		||||
            handler() {
 | 
			
		||||
        bounds: {
 | 
			
		||||
            handler(bounds) {
 | 
			
		||||
                this.setScale();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
@@ -84,23 +58,18 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        let axisHolder = this.$refs.axisHolder;
 | 
			
		||||
        this.height = axisHolder.offsetHeight;
 | 
			
		||||
        this.width = axisHolder.clientWidth;
 | 
			
		||||
        const rect = axisHolder.getBoundingClientRect();
 | 
			
		||||
        this.left = Math.round(rect.left);
 | 
			
		||||
 | 
			
		||||
        let height = axisHolder.offsetHeight;
 | 
			
		||||
        let vis = d3Selection.select(axisHolder)
 | 
			
		||||
            .append("svg:svg")
 | 
			
		||||
            .attr("width", "100%")
 | 
			
		||||
            .attr("height", this.height);
 | 
			
		||||
 | 
			
		||||
            .attr("height", height);
 | 
			
		||||
 | 
			
		||||
        this.width = this.$refs.axisHolder.clientWidth;
 | 
			
		||||
        this.xAxis = d3Axis.axisTop();
 | 
			
		||||
        this.dragging = false;
 | 
			
		||||
 | 
			
		||||
        // draw x axis with labels. CSS is used to position them.
 | 
			
		||||
        this.axisElement = vis.append("g")
 | 
			
		||||
            .attr("class", "axis");
 | 
			
		||||
        this.axisElement = vis.append("g");
 | 
			
		||||
 | 
			
		||||
        this.setViewFromTimeSystem(this.openmct.time.timeSystem());
 | 
			
		||||
        this.setScale();
 | 
			
		||||
@@ -114,15 +83,12 @@ export default {
 | 
			
		||||
    methods: {
 | 
			
		||||
        setScale() {
 | 
			
		||||
            let timeSystem = this.openmct.time.timeSystem();
 | 
			
		||||
            let bounds = this.bounds;
 | 
			
		||||
 | 
			
		||||
            if (timeSystem.isUTCBased) {
 | 
			
		||||
                this.xScale.domain(
 | 
			
		||||
                    [new Date(this.viewBounds.start), new Date(this.viewBounds.end)]
 | 
			
		||||
                );
 | 
			
		||||
                this.xScale.domain([new Date(bounds.start), new Date(bounds.end)]);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.xScale.domain(
 | 
			
		||||
                    [this.viewBounds.start, this.viewBounds.end]
 | 
			
		||||
                );
 | 
			
		||||
                this.xScale.domain([bounds.start, bounds.end]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.xAxis.scale(this.xScale);
 | 
			
		||||
@@ -136,7 +102,7 @@ export default {
 | 
			
		||||
                this.xAxis.ticks(this.width / PIXELS_PER_TICK);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.msPerPixel = (this.viewBounds.end - this.viewBounds.start) / this.width;
 | 
			
		||||
            this.msPerPixel = (bounds.end - bounds.start) / this.width;
 | 
			
		||||
        },
 | 
			
		||||
        setViewFromTimeSystem(timeSystem) {
 | 
			
		||||
            //The D3 scale used depends on the type of time system as d3
 | 
			
		||||
@@ -154,8 +120,9 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        getActiveFormatter() {
 | 
			
		||||
            let timeSystem = this.openmct.time.timeSystem();
 | 
			
		||||
            let isFixed = this.openmct.time.clock() === undefined;
 | 
			
		||||
 | 
			
		||||
            if (this.isFixed) {
 | 
			
		||||
            if (isFixed) {
 | 
			
		||||
                return this.getFormatter(timeSystem.timeFormat);
 | 
			
		||||
            } else {
 | 
			
		||||
                return this.getFormatter(timeSystem.durationFormat || DEFAULT_DURATION_FORMATTER);
 | 
			
		||||
@@ -167,128 +134,42 @@ export default {
 | 
			
		||||
            }).formatter;
 | 
			
		||||
        },
 | 
			
		||||
        dragStart($event) {
 | 
			
		||||
            if (this.isFixed) {
 | 
			
		||||
            let isFixed = this.openmct.time.clock() === undefined;
 | 
			
		||||
            if (isFixed) {
 | 
			
		||||
                this.dragStartX = $event.clientX;
 | 
			
		||||
 | 
			
		||||
                if (this.altPressed) {
 | 
			
		||||
                    this.inPanMode = true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                document.addEventListener('mousemove', this.drag);
 | 
			
		||||
                document.addEventListener('mouseup', this.dragEnd, {
 | 
			
		||||
                    once: true
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                if (this.inZoomMode) {
 | 
			
		||||
                    this.startZoom();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        drag($event) {
 | 
			
		||||
            if (!this.dragging) {
 | 
			
		||||
                this.dragging = true;
 | 
			
		||||
 | 
			
		||||
                requestAnimationFrame(() => {
 | 
			
		||||
                    this.dragX = $event.clientX;
 | 
			
		||||
                    this.inPanMode ? this.pan() : this.zoom();
 | 
			
		||||
                requestAnimationFrame(()=>{
 | 
			
		||||
                    let deltaX = $event.clientX - this.dragStartX;
 | 
			
		||||
                    let percX = deltaX / this.width;
 | 
			
		||||
                    let bounds = this.openmct.time.bounds();
 | 
			
		||||
                    let deltaTime = bounds.end - bounds.start;
 | 
			
		||||
                    let newStart = bounds.start - percX * deltaTime;
 | 
			
		||||
                    this.$emit('panAxis',{
 | 
			
		||||
                        start: newStart,
 | 
			
		||||
                        end: newStart + deltaTime
 | 
			
		||||
                    });
 | 
			
		||||
                    this.dragging = false;
 | 
			
		||||
                });
 | 
			
		||||
                })
 | 
			
		||||
            } else {
 | 
			
		||||
                console.log('Rejected drag due to RAF cap');
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        dragEnd() {
 | 
			
		||||
            this.inPanMode ? this.endPan() : this.endZoom();
 | 
			
		||||
 | 
			
		||||
            document.removeEventListener('mousemove', this.drag);
 | 
			
		||||
            this.dragStartX = undefined;
 | 
			
		||||
            this.dragX = undefined;
 | 
			
		||||
        },
 | 
			
		||||
        pan() {
 | 
			
		||||
            const panBounds = this.getPanBounds();
 | 
			
		||||
            this.$emit('panAxis', panBounds);
 | 
			
		||||
        },
 | 
			
		||||
        endPan() {
 | 
			
		||||
            const panBounds = this.dragStartX && this.dragX && this.dragStartX !== this.dragX
 | 
			
		||||
                ? this.getPanBounds()
 | 
			
		||||
                : undefined;
 | 
			
		||||
            this.$emit('endPan', panBounds);
 | 
			
		||||
            this.inPanMode = false;
 | 
			
		||||
        },
 | 
			
		||||
        getPanBounds() {
 | 
			
		||||
            const bounds = this.openmct.time.bounds();
 | 
			
		||||
            const deltaTime = bounds.end - bounds.start;
 | 
			
		||||
            const deltaX = this.dragX - this.dragStartX;
 | 
			
		||||
            const percX = deltaX / this.width;
 | 
			
		||||
            const panStart = bounds.start - percX * deltaTime;
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                start: panStart,
 | 
			
		||||
                end: panStart + deltaTime
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        startZoom() {
 | 
			
		||||
            const x = this.scaleToBounds(this.dragStartX);
 | 
			
		||||
 | 
			
		||||
            this.zoomStyle = {
 | 
			
		||||
                left: `${this.dragStartX - this.left}px`
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.$emit('zoomAxis', {
 | 
			
		||||
                start: x,
 | 
			
		||||
                end: x
 | 
			
		||||
            this.openmct.time.bounds({
 | 
			
		||||
                start: this.bounds.start,
 | 
			
		||||
                end: this.bounds.end
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        zoom() {
 | 
			
		||||
            const zoomRange = this.getZoomRange();
 | 
			
		||||
 | 
			
		||||
            this.zoomStyle = {
 | 
			
		||||
                left: `${zoomRange.start - this.left}px`,
 | 
			
		||||
                width: `${zoomRange.end - zoomRange.start}px`
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.$emit('zoomAxis', {
 | 
			
		||||
                start: this.scaleToBounds(zoomRange.start),
 | 
			
		||||
                end: this.scaleToBounds(zoomRange.end)
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        endZoom() {
 | 
			
		||||
            const zoomRange = this.dragStartX && this.dragX && this.dragStartX !== this.dragX
 | 
			
		||||
                ? this.getZoomRange()
 | 
			
		||||
                : undefined;
 | 
			
		||||
 | 
			
		||||
            const zoomBounds = zoomRange
 | 
			
		||||
                ? {
 | 
			
		||||
                    start: this.scaleToBounds(zoomRange.start),
 | 
			
		||||
                    end: this.scaleToBounds(zoomRange.end)
 | 
			
		||||
                }
 | 
			
		||||
                : this.openmct.time.bounds();
 | 
			
		||||
 | 
			
		||||
            this.zoomStyle = {};
 | 
			
		||||
            this.$emit('endZoom', zoomBounds);
 | 
			
		||||
        },
 | 
			
		||||
        getZoomRange() {
 | 
			
		||||
            const leftBound = this.left;
 | 
			
		||||
            const rightBound = this.left + this.width;
 | 
			
		||||
 | 
			
		||||
            const zoomStart = this.dragX < leftBound
 | 
			
		||||
                ? leftBound
 | 
			
		||||
                : Math.min(this.dragX, this.dragStartX);
 | 
			
		||||
 | 
			
		||||
            const zoomEnd = this.dragX > rightBound
 | 
			
		||||
                ? rightBound
 | 
			
		||||
                : Math.max(this.dragX, this.dragStartX);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                start: zoomStart,
 | 
			
		||||
                end: zoomEnd
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        scaleToBounds(value) {
 | 
			
		||||
            const bounds = this.openmct.time.bounds();
 | 
			
		||||
            const timeDelta = bounds.end - bounds.start;
 | 
			
		||||
            const valueDelta = value - this.left;
 | 
			
		||||
            const offset = valueDelta / this.width * timeDelta;
 | 
			
		||||
            return bounds.start + offset;
 | 
			
		||||
        },
 | 
			
		||||
        resize() {
 | 
			
		||||
            if (this.$refs.axisHolder.clientWidth !== this.width) {
 | 
			
		||||
                this.width = this.$refs.axisHolder.clientWidth;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,198 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT Web, Copyright (c) 2014-2018, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT Web includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
<template>
 | 
			
		||||
<div class="c-ctrl-wrapper c-ctrl-wrapper--menus-up">
 | 
			
		||||
    <button class="c-button--menu c-history-button icon-history"
 | 
			
		||||
            @click.prevent="toggle"
 | 
			
		||||
    >
 | 
			
		||||
        <span class="c-button__label">History</span>
 | 
			
		||||
    </button>
 | 
			
		||||
    <div v-if="open"
 | 
			
		||||
         class="c-menu c-conductor__history-menu"
 | 
			
		||||
    >
 | 
			
		||||
        <ul v-if="hasHistoryPresets">
 | 
			
		||||
            <li
 | 
			
		||||
                v-for="preset in presets"
 | 
			
		||||
                :key="preset.label"
 | 
			
		||||
                class="icon-clock"
 | 
			
		||||
                @click="selectTimespan(preset.bounds)"
 | 
			
		||||
            >
 | 
			
		||||
                {{ preset.label }}
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
 | 
			
		||||
        <div
 | 
			
		||||
            v-if="hasHistoryPresets"
 | 
			
		||||
            class="c-menu__section-separator"
 | 
			
		||||
        ></div>
 | 
			
		||||
 | 
			
		||||
        <div class="c-menu__section-hint">
 | 
			
		||||
            Past timeframes, ordered by latest first
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <ul>
 | 
			
		||||
            <li
 | 
			
		||||
                v-for="(timespan, index) in historyForCurrentTimeSystem"
 | 
			
		||||
                :key="index"
 | 
			
		||||
                class="icon-history"
 | 
			
		||||
                @click="selectTimespan(timespan)"
 | 
			
		||||
            >
 | 
			
		||||
                {{ formatTime(timespan.start) }} - {{ formatTime(timespan.end) }}
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import toggleMixin from '../../ui/mixins/toggle-mixin';
 | 
			
		||||
 | 
			
		||||
const LOCAL_STORAGE_HISTORY_KEY = 'tcHistory';
 | 
			
		||||
const DEFAULT_RECORDS = 10;
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct', 'configuration'],
 | 
			
		||||
    mixins: [toggleMixin],
 | 
			
		||||
    props: {
 | 
			
		||||
        bounds: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        },
 | 
			
		||||
        timeSystem: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            history: {}, // contains arrays of timespans {start, end}, array key is time system key
 | 
			
		||||
            presets: []
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        hasHistoryPresets() {
 | 
			
		||||
            return this.timeSystem.isUTCBased && this.presets.length;
 | 
			
		||||
        },
 | 
			
		||||
        historyForCurrentTimeSystem() {
 | 
			
		||||
            const history = this.history[this.timeSystem.key];
 | 
			
		||||
 | 
			
		||||
            return history;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        bounds: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.addTimespan();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        },
 | 
			
		||||
        timeSystem: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.loadConfiguration();
 | 
			
		||||
                this.addTimespan();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        },
 | 
			
		||||
        history: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.persistHistoryToLocalStorage();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.getHistoryFromLocalStorage();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        getHistoryFromLocalStorage() {
 | 
			
		||||
            if (localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY)) {
 | 
			
		||||
                this.history = JSON.parse(localStorage.getItem(LOCAL_STORAGE_HISTORY_KEY))
 | 
			
		||||
            } else {
 | 
			
		||||
                this.history = {};
 | 
			
		||||
                this.persistHistoryToLocalStorage();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        persistHistoryToLocalStorage() {
 | 
			
		||||
            localStorage.setItem(LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(this.history));
 | 
			
		||||
        },
 | 
			
		||||
        addTimespan() {
 | 
			
		||||
            const key = this.timeSystem.key;
 | 
			
		||||
            let [...currentHistory] = this.history[key] || [];
 | 
			
		||||
            const timespan = {
 | 
			
		||||
                start: this.bounds.start,
 | 
			
		||||
                end: this.bounds.end
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            const isNotEqual = function (entry) {
 | 
			
		||||
                const start = entry.start !== this.start;
 | 
			
		||||
                const end = entry.end !== this.end;
 | 
			
		||||
 | 
			
		||||
                return start || end;
 | 
			
		||||
            };
 | 
			
		||||
            currentHistory = currentHistory.filter(isNotEqual, timespan);
 | 
			
		||||
 | 
			
		||||
            while (currentHistory.length >= this.records) {
 | 
			
		||||
                currentHistory.pop();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            currentHistory.unshift(timespan);
 | 
			
		||||
            this.history[key] = currentHistory;
 | 
			
		||||
        },
 | 
			
		||||
        selectTimespan(timespan) {
 | 
			
		||||
            this.openmct.time.bounds(timespan);
 | 
			
		||||
        },
 | 
			
		||||
        selectHours(hours) {
 | 
			
		||||
            const now = Date.now();
 | 
			
		||||
            this.selectTimespan({
 | 
			
		||||
                start: now - hours * 60 * 60 * 1000,
 | 
			
		||||
                end: now
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        loadConfiguration() {
 | 
			
		||||
            const configurations = this.configuration.menuOptions
 | 
			
		||||
                .filter(option => option.timeSystem ===  this.timeSystem.key);
 | 
			
		||||
 | 
			
		||||
            this.presets = this.loadPresets(configurations);
 | 
			
		||||
            this.records = this.loadRecords(configurations);
 | 
			
		||||
        },
 | 
			
		||||
        loadPresets(configurations) {
 | 
			
		||||
            const configuration = configurations.find(option => option.presets);
 | 
			
		||||
            const presets = configuration ? configuration.presets : [];
 | 
			
		||||
 | 
			
		||||
            return presets;
 | 
			
		||||
        },
 | 
			
		||||
        loadRecords(configurations) {
 | 
			
		||||
            const configuration = configurations.find(option => option.records);
 | 
			
		||||
            const records = configuration ? configuration.records : DEFAULT_RECORDS;
 | 
			
		||||
 | 
			
		||||
            return records;
 | 
			
		||||
        },
 | 
			
		||||
        formatTime(time) {
 | 
			
		||||
            const formatter = this.openmct.telemetry.getValueFormatter({
 | 
			
		||||
                format: this.timeSystem.timeFormat
 | 
			
		||||
            }).formatter;
 | 
			
		||||
 | 
			
		||||
            return formatter.format(time);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
@@ -110,7 +110,7 @@ export default {
 | 
			
		||||
            if (clock === undefined) {
 | 
			
		||||
                return {
 | 
			
		||||
                    key: 'fixed',
 | 
			
		||||
                    name: 'Fixed Timespan',
 | 
			
		||||
                    name: 'Fixed Timespan Mode',
 | 
			
		||||
                    description: 'Query and explore data that falls between two fixed datetimes.',
 | 
			
		||||
                    cssClass: 'icon-tabular'
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@
 | 
			
		||||
        text-rendering: geometricPrecision;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        > g.axis {
 | 
			
		||||
        > g {
 | 
			
		||||
            // Overall Tick holder
 | 
			
		||||
            transform: translateY($tickYPos);
 | 
			
		||||
            path {
 | 
			
		||||
@@ -44,6 +44,7 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body.desktop .is-fixed-mode & {
 | 
			
		||||
        @include cursorGrab();
 | 
			
		||||
        background-size: 3px 30%;
 | 
			
		||||
        background-color: $colorBodyBgSubtle;
 | 
			
		||||
        box-shadow: inset rgba(black, 0.4) 0 1px 1px;
 | 
			
		||||
@@ -54,6 +55,17 @@
 | 
			
		||||
            stroke: $colorBodyBgSubtle;
 | 
			
		||||
            transition: $transOut;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &:hover,
 | 
			
		||||
        &:active {
 | 
			
		||||
            $c: $colorKeySubtle;
 | 
			
		||||
            background-color: $c;
 | 
			
		||||
            transition: $transIn;
 | 
			
		||||
            svg text {
 | 
			
		||||
                stroke: $c;
 | 
			
		||||
                transition: $transIn;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .is-realtime-mode & {
 | 
			
		||||
 
 | 
			
		||||
@@ -57,65 +57,6 @@
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-fixed-mode {
 | 
			
		||||
        .c-conductor-axis {
 | 
			
		||||
            &__zoom-indicator {
 | 
			
		||||
                border: 1px solid transparent;
 | 
			
		||||
                display: none; // Hidden by default
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &:not(.is-panning),
 | 
			
		||||
        &:not(.is-zooming) {
 | 
			
		||||
            .c-conductor-axis {
 | 
			
		||||
                &:hover,
 | 
			
		||||
                &:active {
 | 
			
		||||
                    cursor: col-resize;
 | 
			
		||||
                    filter: $timeConductorAxisHoverFilter;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.is-panning,
 | 
			
		||||
        &.is-zooming {
 | 
			
		||||
            .c-conductor-input input {
 | 
			
		||||
                // Styles for inputs while zooming or panning
 | 
			
		||||
                background: rgba($timeConductorActiveBg, 0.4);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.alt-pressed {
 | 
			
		||||
            .c-conductor-axis:hover {
 | 
			
		||||
                // When alt is being pressed and user is hovering over the axis, set the cursor
 | 
			
		||||
                @include cursorGrab();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.is-panning {
 | 
			
		||||
            .c-conductor-axis {
 | 
			
		||||
                @include cursorGrab();
 | 
			
		||||
                background-color: $timeConductorActivePanBg;
 | 
			
		||||
                transition: $transIn;
 | 
			
		||||
 | 
			
		||||
                svg text {
 | 
			
		||||
                    stroke: $timeConductorActivePanBg;
 | 
			
		||||
                    transition: $transIn;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &.is-zooming {
 | 
			
		||||
            .c-conductor-axis__zoom-indicator {
 | 
			
		||||
                display: block;
 | 
			
		||||
                position: absolute;
 | 
			
		||||
                background: rgba($timeConductorActiveBg, 0.4);
 | 
			
		||||
                border-left-color: $timeConductorActiveBg;
 | 
			
		||||
                border-right-color: $timeConductorActiveBg;
 | 
			
		||||
                top: 0; bottom: 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-realtime-mode {
 | 
			
		||||
        .c-conductor__time-bounds {
 | 
			
		||||
            grid-template-columns: 20px auto 1fr auto auto;
 | 
			
		||||
 
 | 
			
		||||
@@ -142,9 +142,6 @@ $colorTimeHov: pullForward($colorTime, 10%);
 | 
			
		||||
$colorTimeSubtle: pushBack($colorTime, 20%);
 | 
			
		||||
$colorTOI: $colorBodyFg; // was $timeControllerToiLineColor
 | 
			
		||||
$colorTOIHov: $colorTime; // was $timeControllerToiLineColorHov
 | 
			
		||||
$timeConductorAxisHoverFilter: brightness(1.2);
 | 
			
		||||
$timeConductorActiveBg: $colorKey;
 | 
			
		||||
$timeConductorActivePanBg: #226074;
 | 
			
		||||
 | 
			
		||||
/************************************************** BROWSING */
 | 
			
		||||
$browseFrameColor: pullForward($colorBodyBg, 10%);
 | 
			
		||||
 
 | 
			
		||||
@@ -146,9 +146,6 @@ $colorTimeHov: pullForward($colorTime, 10%);
 | 
			
		||||
$colorTimeSubtle: pushBack($colorTime, 20%);
 | 
			
		||||
$colorTOI: $colorBodyFg; // was $timeControllerToiLineColor
 | 
			
		||||
$colorTOIHov: $colorTime; // was $timeControllerToiLineColorHov
 | 
			
		||||
$timeConductorAxisHoverFilter: brightness(1.2);
 | 
			
		||||
$timeConductorActiveBg: $colorKey;
 | 
			
		||||
$timeConductorActivePanBg: #226074;
 | 
			
		||||
 | 
			
		||||
/************************************************** BROWSING */
 | 
			
		||||
$browseFrameColor: pullForward($colorBodyBg, 10%);
 | 
			
		||||
 
 | 
			
		||||
@@ -132,7 +132,7 @@ $colorPausedFg: #fff;
 | 
			
		||||
// Base variations
 | 
			
		||||
$colorBodyBgSubtle: pullForward($colorBodyBg, 5%);
 | 
			
		||||
$colorBodyBgSubtleHov: pushBack($colorKey, 50%);
 | 
			
		||||
$colorKeySubtle: pushBack($colorKey, 20%);
 | 
			
		||||
$colorKeySubtle: pushBack($colorKey, 10%);
 | 
			
		||||
 | 
			
		||||
// Time Colors
 | 
			
		||||
$colorTime: #618cff;
 | 
			
		||||
@@ -142,9 +142,6 @@ $colorTimeHov: pushBack($colorTime, 5%);
 | 
			
		||||
$colorTimeSubtle: pushBack($colorTime, 20%);
 | 
			
		||||
$colorTOI: $colorBodyFg; // was $timeControllerToiLineColor
 | 
			
		||||
$colorTOIHov: $colorTime; // was $timeControllerToiLineColorHov
 | 
			
		||||
$timeConductorAxisHoverFilter: brightness(0.8);
 | 
			
		||||
$timeConductorActiveBg: $colorKey;
 | 
			
		||||
$timeConductorActivePanBg: #A0CDE1;
 | 
			
		||||
 | 
			
		||||
/************************************************** BROWSING */
 | 
			
		||||
$browseFrameColor: pullForward($colorBodyBg, 10%);
 | 
			
		||||
 
 | 
			
		||||
@@ -462,17 +462,9 @@ select {
 | 
			
		||||
    text-shadow: $shdwMenuText;
 | 
			
		||||
    padding: $interiorMarginSm;
 | 
			
		||||
    box-shadow: $shdwMenu;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    display: block;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    z-index: 100;
 | 
			
		||||
 | 
			
		||||
    > * {
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
        //+ * {
 | 
			
		||||
        //    margin-top: $interiorMarginSm;
 | 
			
		||||
        //}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@mixin menuInner() {
 | 
			
		||||
@@ -510,23 +502,6 @@ select {
 | 
			
		||||
.c-menu {
 | 
			
		||||
    @include menuOuter();
 | 
			
		||||
    @include menuInner();
 | 
			
		||||
 | 
			
		||||
    &__section-hint {
 | 
			
		||||
        $m: $interiorMargin;
 | 
			
		||||
        margin: $m 0;
 | 
			
		||||
        padding: $m nth($menuItemPad, 2) 0 nth($menuItemPad, 2);
 | 
			
		||||
 | 
			
		||||
        opacity: 0.6;
 | 
			
		||||
        font-size: 0.9em;
 | 
			
		||||
        font-style: italic;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__section-separator {
 | 
			
		||||
        $m: $interiorMargin;
 | 
			
		||||
        border-top: 1px solid $colorInteriorBorder;
 | 
			
		||||
        margin: $m 0;
 | 
			
		||||
        padding: $m nth($menuItemPad, 2) 0 nth($menuItemPad, 2);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-super-menu {
 | 
			
		||||
@@ -590,20 +565,6 @@ select {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/******************************************************** CONTROL BARS */
 | 
			
		||||
.c-control-bar {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-left: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__label {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************** PALETTES */
 | 
			
		||||
.c-palette {
 | 
			
		||||
 
 | 
			
		||||
@@ -40,58 +40,28 @@ mct-plot {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-plot,
 | 
			
		||||
.gl-plot {
 | 
			
		||||
    .s-status-taking-snapshot & {
 | 
			
		||||
        .c-control-bar {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
        .gl-plot-y-label__select {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-plot {
 | 
			
		||||
    //$p: $mainViewPad;
 | 
			
		||||
    @include abs($mainViewPad);
 | 
			
		||||
    //position: absolute;
 | 
			
		||||
    //top: $p; right: $p; bottom: $p; left: $p;
 | 
			
		||||
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-top: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .l-control-bar {
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .l-view-section {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
    }
 | 
			
		||||
    $p: $mainViewPad;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: $p; right: $p; bottom: $p; left: $p;
 | 
			
		||||
 | 
			
		||||
    &--stacked {
 | 
			
		||||
        .child-frame {
 | 
			
		||||
            .has-control-bar {
 | 
			
		||||
                .c-control-bar {
 | 
			
		||||
                    // Hides buttons per plot element in a stacked plot
 | 
			
		||||
                    display: none;
 | 
			
		||||
        .l-view-section {
 | 
			
		||||
            //  Make this a flex container
 | 
			
		||||
            display: flex;
 | 
			
		||||
            flex-flow: column nowrap;
 | 
			
		||||
            .gl-plot.child-frame {
 | 
			
		||||
                mct-plot {
 | 
			
		||||
                    display: flex;
 | 
			
		||||
                    flex: 1 1 auto;
 | 
			
		||||
                    height: 100%;
 | 
			
		||||
                    position: relative;
 | 
			
		||||
                }
 | 
			
		||||
                flex: 1 1 auto;
 | 
			
		||||
                &:not(:first-child) {
 | 
			
		||||
                    margin-top: $interiorMargin;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            mct-plot {
 | 
			
		||||
                display: flex;
 | 
			
		||||
                flex: 1 1 auto;
 | 
			
		||||
                height: 100%;
 | 
			
		||||
                position: relative;
 | 
			
		||||
            }
 | 
			
		||||
            flex: 1 1 auto;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .s-status-timeconductor-unsynced .holder-plot {
 | 
			
		||||
@@ -100,6 +70,7 @@ mct-plot {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -215,7 +186,7 @@ mct-plot {
 | 
			
		||||
            left: 0; top: 0; right: auto; bottom: 0;
 | 
			
		||||
            padding-left: 5px;
 | 
			
		||||
            text-orientation: mixed;
 | 
			
		||||
            //overflow: hidden;
 | 
			
		||||
            overflow: hidden;
 | 
			
		||||
            writing-mode: vertical-lr;
 | 
			
		||||
            &:before {
 | 
			
		||||
                // Icon denoting configurability
 | 
			
		||||
@@ -368,11 +339,11 @@ mct-plot {
 | 
			
		||||
    z-index: -10;
 | 
			
		||||
 | 
			
		||||
    .l-view-section {
 | 
			
		||||
        //$m: $interiorMargin;
 | 
			
		||||
        //top: $m !important;
 | 
			
		||||
        //right: $m;
 | 
			
		||||
        //bottom: $m;
 | 
			
		||||
        //left: $m;
 | 
			
		||||
        $m: $interiorMargin;
 | 
			
		||||
        top: $m !important;
 | 
			
		||||
        right: $m;
 | 
			
		||||
        bottom: $m;
 | 
			
		||||
        left: $m;
 | 
			
		||||
 | 
			
		||||
        .s-status-timeconductor-unsynced .holder-plot {
 | 
			
		||||
            .t-object-alert.t-alert-unsynced {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,53 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/******************************************************************* VIEWS */
 | 
			
		||||
// From _views.scss
 | 
			
		||||
// Legacy overlay and stacked plots depend on this for now
 | 
			
		||||
// Styles for sub-dividing views generically
 | 
			
		||||
.l-control-bar {
 | 
			
		||||
    // Element that can be placed above l-view-section, holds controls, buttons, etc.
 | 
			
		||||
    height: $controlBarH;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-control-bar {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-left: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__label {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.l-view-section {
 | 
			
		||||
    @include abs();
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.has-control-bar {
 | 
			
		||||
    .l-view-section {
 | 
			
		||||
        top: $controlBarH + $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.child-frame {
 | 
			
		||||
    .has-control-bar {
 | 
			
		||||
        .l-control-bar,
 | 
			
		||||
        .c-control-bar {
 | 
			
		||||
            // Hides buttons per plot element in a stacked plot
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
        .l-view-section {
 | 
			
		||||
            top: 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*********************************************************************** CLOCKS AND TIMERS */
 | 
			
		||||
.c-clock,
 | 
			
		||||
.c-timer {
 | 
			
		||||
 
 | 
			
		||||
@@ -388,21 +388,7 @@
 | 
			
		||||
.s-status-taking-snapshot,
 | 
			
		||||
.overlay.snapshot {
 | 
			
		||||
    // Handle overflow-y issues with tables and html2canvas
 | 
			
		||||
    background: $colorBodyBg; // Prevent html2canvas from using white background
 | 
			
		||||
    color: $colorBodyFg;
 | 
			
		||||
    padding: $interiorMarginSm !important; // Prevents items from going right to the edge of the image
 | 
			
		||||
 | 
			
		||||
    .l-sticky-headers .l-tabular-body { overflow: auto; }
 | 
			
		||||
    .l-browse-bar {
 | 
			
		||||
        display: none; // Suppress browse-bar when snapshotting from view-large overlay
 | 
			
		||||
        + * {
 | 
			
		||||
            margin-top: 0 !important; // Remove margin from any following elements
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    * {
 | 
			
		||||
        box-shadow: none !important; // Prevent html2canvas problems with box-shadow
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-notebook-snapshot {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
import MCT from 'MCT';
 | 
			
		||||
 | 
			
		||||
export function createOpenMct() {
 | 
			
		||||
    delete require.cache[require.resolve('openmct')];
 | 
			
		||||
 | 
			
		||||
    const openmct = new MCT();
 | 
			
		||||
    openmct.install(openmct.plugins.LocalStorage());
 | 
			
		||||
    openmct.install(openmct.plugins.UTCTimeSystem());
 | 
			
		||||
 
 | 
			
		||||
@@ -59,8 +59,6 @@
 | 
			
		||||
<script>
 | 
			
		||||
import ObjectView from './ObjectView.vue'
 | 
			
		||||
import ContextMenuDropDown from './contextMenuDropDown.vue';
 | 
			
		||||
import PreviewHeader from '@/ui/preview/preview-header.vue';
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
const SIMPLE_CONTENT_TYPES = [
 | 
			
		||||
    'clock',
 | 
			
		||||
@@ -118,41 +116,13 @@ export default {
 | 
			
		||||
                childElement = parentElement.children[0];
 | 
			
		||||
 | 
			
		||||
            this.openmct.overlays.overlay({
 | 
			
		||||
                element: this.getOverlayElement(childElement),
 | 
			
		||||
                element: childElement,
 | 
			
		||||
                size: 'large',
 | 
			
		||||
                onDestroy() {
 | 
			
		||||
                    parentElement.append(childElement);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        getOverlayElement(childElement) {
 | 
			
		||||
            const fragment = new DocumentFragment();
 | 
			
		||||
            const header = this.getPreviewHeader();
 | 
			
		||||
            fragment.append(header);
 | 
			
		||||
            fragment.append(childElement);
 | 
			
		||||
 | 
			
		||||
            return fragment;
 | 
			
		||||
        },
 | 
			
		||||
        getPreviewHeader() {
 | 
			
		||||
            const domainObject = this.objectPath[0];
 | 
			
		||||
            const preview = new Vue({
 | 
			
		||||
                components: {
 | 
			
		||||
                    PreviewHeader
 | 
			
		||||
                },
 | 
			
		||||
                provide: {
 | 
			
		||||
                    openmct: this.openmct,
 | 
			
		||||
                    objectPath: this.objectPath
 | 
			
		||||
                },
 | 
			
		||||
                data() {
 | 
			
		||||
                    return {
 | 
			
		||||
                        domainObject
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                template: '<PreviewHeader :domainObject="domainObject" :hideViewSwitcher="true" :showNotebookMenuSwitcher="true"></PreviewHeader>'
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return preview.$mount().$el;
 | 
			
		||||
        },
 | 
			
		||||
        getSelectionContext() {
 | 
			
		||||
            return this.$refs.objectView.getSelectionContext();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -201,7 +201,7 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        initObjectStyles() {
 | 
			
		||||
            if (!this.styleRuleManager) {
 | 
			
		||||
                this.styleRuleManager = new StyleRuleManager((this.currentObject.configuration && this.currentObject.configuration.objectStyles), this.openmct, this.updateStyle.bind(this), true);
 | 
			
		||||
                this.styleRuleManager = new StyleRuleManager((this.currentObject.configuration && this.currentObject.configuration.objectStyles), this.openmct, this.updateStyle.bind(this));
 | 
			
		||||
            } else {
 | 
			
		||||
                this.styleRuleManager.updateObjectStyleConfig(this.currentObject.configuration && this.currentObject.configuration.objectStyles);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -23,12 +23,9 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:not(.c-so-view--no-frame) {
 | 
			
		||||
        background: $colorBodyBg;
 | 
			
		||||
        border: $browseFrameBorder;
 | 
			
		||||
        padding: $interiorMargin;
 | 
			
		||||
 | 
			
		||||
        .is-editing & {
 | 
			
		||||
            background: rgba($colorBodyBg, 0.8);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &--no-frame {
 | 
			
		||||
 
 | 
			
		||||
@@ -108,7 +108,7 @@ export default {
 | 
			
		||||
                let object = selection[0][0].context.item;
 | 
			
		||||
                if (object) {
 | 
			
		||||
                    let type = this.openmct.types.get(object.type);
 | 
			
		||||
                    this.showStyles = this.isLayoutObject(selection[0], object.type) || this.isCreatableObject(object, type);
 | 
			
		||||
                    this.showStyles = (this.excludeObjectTypes.indexOf(object.type) < 0) && type.definition.creatable;
 | 
			
		||||
                }
 | 
			
		||||
                if (!this.currentTabbedView.key || (!this.showStyles && this.currentTabbedView.key === this.tabbedViews[1].key))
 | 
			
		||||
                {
 | 
			
		||||
@@ -116,14 +116,6 @@ export default {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        isLayoutObject(selection, objectType) {
 | 
			
		||||
            //we allow conditionSets to be styled if they're part of a layout
 | 
			
		||||
            return selection.length > 1 &&
 | 
			
		||||
                ((objectType === 'conditionSet') || (this.excludeObjectTypes.indexOf(objectType) < 0));
 | 
			
		||||
        },
 | 
			
		||||
        isCreatableObject(object, type) {
 | 
			
		||||
            return (this.excludeObjectTypes.indexOf(object.type) < 0) && type.definition.creatable;
 | 
			
		||||
        },
 | 
			
		||||
        updateCurrentTab(view) {
 | 
			
		||||
            this.currentTabbedView = view;
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,6 @@
 | 
			
		||||
        <!-- Action buttons -->
 | 
			
		||||
        <NotebookMenuSwitcher v-if="notebookEnabled"
 | 
			
		||||
                              :domain-object="domainObject"
 | 
			
		||||
                              :object-path="openmct.router.path"
 | 
			
		||||
                              class="c-notebook-snapshot-menubutton"
 | 
			
		||||
        />
 | 
			
		||||
        <div class="l-browse-bar__actions">
 | 
			
		||||
@@ -199,6 +198,8 @@ export default {
 | 
			
		||||
        updateName(event) {
 | 
			
		||||
            if (event.target.innerText !== this.domainObject.name && event.target.innerText.match(/\S/)) {
 | 
			
		||||
                this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText);
 | 
			
		||||
            } else {
 | 
			
		||||
                event.target.innerText = this.domainObject.name;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        updateNameOnEnterKeyPress(event) {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,12 +21,28 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
<template>
 | 
			
		||||
<div class="l-preview-window">
 | 
			
		||||
    <PreviewHeader
 | 
			
		||||
        :current-view="currentView"
 | 
			
		||||
        :domain-object="domainObject"
 | 
			
		||||
        :views="views"
 | 
			
		||||
        @setView="setView"
 | 
			
		||||
    />
 | 
			
		||||
    <div class="l-browse-bar">
 | 
			
		||||
        <div class="l-browse-bar__start">
 | 
			
		||||
            <div
 | 
			
		||||
                class="l-browse-bar__object-name--w"
 | 
			
		||||
                :class="type.cssClass"
 | 
			
		||||
            >
 | 
			
		||||
                <span class="l-browse-bar__object-name">
 | 
			
		||||
                    {{ domainObject.name }}
 | 
			
		||||
                </span>
 | 
			
		||||
                <context-menu-drop-down :object-path="objectPath" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="l-browse-bar__end">
 | 
			
		||||
            <div class="l-browse-bar__actions">
 | 
			
		||||
                <view-switcher
 | 
			
		||||
                    :views="views"
 | 
			
		||||
                    :current-view="currentView"
 | 
			
		||||
                    @setView="setView"
 | 
			
		||||
                />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="l-preview-window__object-view">
 | 
			
		||||
        <div ref="objectView"></div>
 | 
			
		||||
    </div>
 | 
			
		||||
@@ -34,11 +50,13 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import PreviewHeader from './preview-header.vue';
 | 
			
		||||
import ContextMenuDropDown from '../../ui/components/contextMenuDropDown.vue';
 | 
			
		||||
import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    components: {
 | 
			
		||||
        PreviewHeader
 | 
			
		||||
        ContextMenuDropDown,
 | 
			
		||||
        ViewSwitcher
 | 
			
		||||
    },
 | 
			
		||||
    inject: [
 | 
			
		||||
        'openmct',
 | 
			
		||||
@@ -46,9 +64,12 @@ export default {
 | 
			
		||||
    ],
 | 
			
		||||
    data() {
 | 
			
		||||
        let domainObject = this.objectPath[0];
 | 
			
		||||
        let type = this.openmct.types.get(domainObject.type);
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            domainObject: domainObject,
 | 
			
		||||
            type: type,
 | 
			
		||||
            notebookEnabled: false,
 | 
			
		||||
            viewKey: undefined
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
@@ -76,7 +97,6 @@ export default {
 | 
			
		||||
                this.view.destroy();
 | 
			
		||||
                this.$refs.objectView.innerHTML = '';
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            delete this.view;
 | 
			
		||||
            delete this.viewContainer;
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -36,12 +36,7 @@ export default class PreviewAction {
 | 
			
		||||
         * Dependencies
 | 
			
		||||
         */
 | 
			
		||||
        this._openmct = openmct;
 | 
			
		||||
 | 
			
		||||
        if (PreviewAction.isVisible === undefined) {
 | 
			
		||||
            PreviewAction.isVisible = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    invoke(objectPath) {
 | 
			
		||||
        let preview = new Vue({
 | 
			
		||||
            components: {
 | 
			
		||||
@@ -64,27 +59,16 @@ export default class PreviewAction {
 | 
			
		||||
                    callback: () => overlay.dismiss()
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            onDestroy: () => {
 | 
			
		||||
                PreviewAction.isVisible = false;
 | 
			
		||||
                preview.$destroy()
 | 
			
		||||
            }
 | 
			
		||||
            onDestroy: () => preview.$destroy()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        PreviewAction.isVisible = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    appliesTo(objectPath) {
 | 
			
		||||
        return !PreviewAction.isVisible && !this._isNavigatedObject(objectPath);
 | 
			
		||||
        return !this._isNavigatedObject(objectPath)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _isNavigatedObject(objectPath) {
 | 
			
		||||
        let targetObject = objectPath[0];
 | 
			
		||||
        let navigatedObject = this._openmct.router.path[0];
 | 
			
		||||
        return targetObject.identifier.namespace === navigatedObject.identifier.namespace &&
 | 
			
		||||
            targetObject.identifier.key === navigatedObject.identifier.key;
 | 
			
		||||
    }
 | 
			
		||||
    _preventPreview(objectPath) {
 | 
			
		||||
        const noPreviewTypes = ['folder'];
 | 
			
		||||
        return noPreviewTypes.includes(objectPath[0].type);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,92 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="l-browse-bar">
 | 
			
		||||
    <div class="l-browse-bar__start">
 | 
			
		||||
        <div
 | 
			
		||||
            class="l-browse-bar__object-name--w"
 | 
			
		||||
            :class="type.cssClass"
 | 
			
		||||
        >
 | 
			
		||||
            <span class="l-browse-bar__object-name">
 | 
			
		||||
                {{ domainObject.name }}
 | 
			
		||||
            </span>
 | 
			
		||||
            <context-menu-drop-down :object-path="objectPath" />
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="l-browse-bar__end">
 | 
			
		||||
        <div class="l-browse-bar__actions">
 | 
			
		||||
            <view-switcher
 | 
			
		||||
                :v-if="!hideViewSwitcher"
 | 
			
		||||
                :views="views"
 | 
			
		||||
                :current-view="currentView"
 | 
			
		||||
                @setView="setView"
 | 
			
		||||
            />
 | 
			
		||||
            <NotebookMenuSwitcher v-if="showNotebookMenuSwitcher"
 | 
			
		||||
                                  :domain-object="domainObject"
 | 
			
		||||
                                  :ignore-link="true"
 | 
			
		||||
                                  :object-path="objectPath"
 | 
			
		||||
                                  class="c-notebook-snapshot-menubutton"
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import ContextMenuDropDown from '../../ui/components/contextMenuDropDown.vue';
 | 
			
		||||
import NotebookMenuSwitcher from '@/plugins/notebook/components/notebook-menu-switcher.vue';
 | 
			
		||||
import ViewSwitcher from '../../ui/layout/ViewSwitcher.vue';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: [
 | 
			
		||||
        'openmct',
 | 
			
		||||
        'objectPath'
 | 
			
		||||
    ],
 | 
			
		||||
    components: {
 | 
			
		||||
        ContextMenuDropDown,
 | 
			
		||||
        NotebookMenuSwitcher,
 | 
			
		||||
        ViewSwitcher
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
        currentView: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            default: () => {
 | 
			
		||||
                return {};
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        domainObject: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            default: () => {
 | 
			
		||||
                return {};
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        hideViewSwitcher: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            default: () => {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        showNotebookMenuSwitcher: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            default: () => {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        views: {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            default: () => {
 | 
			
		||||
                return [];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            type: this.openmct.types.get(this.domainObject.type)
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        setView(view) {
 | 
			
		||||
            this.$emit('setView', view);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
		Reference in New Issue
	
	Block a user