Compare commits
	
		
			4 Commits
		
	
	
		
			router-tes
			...
			1.7.6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					1db7ac55b4 | ||
| 
						 | 
					82b3383834 | ||
| 
						 | 
					ac240d524c | ||
| 
						 | 
					1b034f6125 | 
@@ -42,7 +42,6 @@ jobs:
 | 
			
		||||
            - ~/.npm
 | 
			
		||||
            - ~/.cache
 | 
			
		||||
            - node_modules
 | 
			
		||||
      - run: npm run lint
 | 
			
		||||
      - run: npm run test:coverage -- --browsers=<<parameters.browser>> || <<parameters.always-pass>>
 | 
			
		||||
      - store_test_results:
 | 
			
		||||
          path: dist/reports/tests/
 | 
			
		||||
@@ -57,38 +56,14 @@ workflows:
 | 
			
		||||
          browser: ChromeHeadless
 | 
			
		||||
          always-pass: false
 | 
			
		||||
      - test:
 | 
			
		||||
          name: node12-firefoxESR-build-only
 | 
			
		||||
          name: node12-firefoxESR
 | 
			
		||||
          node-version: lts/erbium
 | 
			
		||||
          browser: FirefoxESR
 | 
			
		||||
          always-pass: true
 | 
			
		||||
      - test:
 | 
			
		||||
          name: node14-chrome-build-only
 | 
			
		||||
          name: node14-chrome
 | 
			
		||||
          node-version: lts/fermium
 | 
			
		||||
          browser: ChromeHeadless
 | 
			
		||||
          always-pass: true
 | 
			
		||||
  nightly:
 | 
			
		||||
    jobs:
 | 
			
		||||
      - test:
 | 
			
		||||
          name: node10-chrome-nightly
 | 
			
		||||
          node-version: lts/dubnium
 | 
			
		||||
          browser: ChromeHeadless
 | 
			
		||||
          always-pass: false
 | 
			
		||||
      - test:
 | 
			
		||||
          name: node12-firefoxESR-nightly
 | 
			
		||||
          node-version: lts/erbium
 | 
			
		||||
          browser: FirefoxESR
 | 
			
		||||
          always-pass: false
 | 
			
		||||
      - test:
 | 
			
		||||
          name: node14-chrome-nightly
 | 
			
		||||
          node-version: lts/fermium
 | 
			
		||||
          browser: ChromeHeadless
 | 
			
		||||
          always-pass: false
 | 
			
		||||
    triggers:
 | 
			
		||||
      - schedule:
 | 
			
		||||
          cron: "0 0 * * *"
 | 
			
		||||
          filters:
 | 
			
		||||
            branches:
 | 
			
		||||
              only:
 | 
			
		||||
                - master      
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							@@ -2,7 +2,6 @@
 | 
			
		||||
 | 
			
		||||
* [ ] Have you followed the guidelines in our [Contributing document](https://github.com/nasa/openmct/blob/master/CONTRIBUTING.md)?
 | 
			
		||||
* [ ] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/nasa/openmct/pulls) for the same update/change?
 | 
			
		||||
* [ ] Is this change backwards compatible? For example, developers won't need to change how they are calling the API or how they've extended core plugins such as Tables or Plots.
 | 
			
		||||
 | 
			
		||||
### Author Checklist
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,33 +0,0 @@
 | 
			
		||||
 | 
			
		||||
name: "CodeQL"
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ master ]
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: '28 21 * * 3'
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  analyze:
 | 
			
		||||
    name: Analyze
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      actions: read
 | 
			
		||||
      contents: read
 | 
			
		||||
      security-events: write
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout repository
 | 
			
		||||
      uses: actions/checkout@v2
 | 
			
		||||
 | 
			
		||||
    # Initializes the CodeQL tools for scanning.
 | 
			
		||||
    - name: Initialize CodeQL
 | 
			
		||||
      uses: github/codeql-action/init@v1
 | 
			
		||||
      with:
 | 
			
		||||
        languages: javascript
 | 
			
		||||
 | 
			
		||||
    - name: Autobuild
 | 
			
		||||
      uses: github/codeql-action/autobuild@v1
 | 
			
		||||
 | 
			
		||||
    - name: Perform CodeQL Analysis
 | 
			
		||||
      uses: github/codeql-action/analyze@v1
 | 
			
		||||
							
								
								
									
										2
									
								
								API.md
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								API.md
									
									
									
									
									
								
							@@ -996,7 +996,7 @@ reveal additional information when the mouse cursor is hovered over it.
 | 
			
		||||
A common use case for indicators is to convey the state of some external system such as a 
 | 
			
		||||
persistence backend or HTTP server. So long as this system is accessible via HTTP request, 
 | 
			
		||||
Open MCT provides a general purpose indicator to show whether the server is available and 
 | 
			
		||||
returning a 2xx status code. The URL Status Indicator is made available as a default plugin. See
 | 
			
		||||
returing a 2xx status code. The URL Status Indicator is made available as a default plugin. See
 | 
			
		||||
the [documentation](./src/plugins/URLIndicatorPlugin) for details on how to install and configure the 
 | 
			
		||||
URL Status Indicator.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -317,7 +317,6 @@ checklist).
 | 
			
		||||
### Reviewer Checklist
 | 
			
		||||
 | 
			
		||||
* [ ] Changes appear to address issue?
 | 
			
		||||
* [ ] Changes appear not to be breaking changes?
 | 
			
		||||
* [ ] Appropriate unit tests included?
 | 
			
		||||
* [ ] Code style and in-line documentation are appropriate?
 | 
			
		||||
* [ ] Commit messages meet standards?
 | 
			
		||||
 
 | 
			
		||||
@@ -423,7 +423,7 @@ which can help with this, however.
 | 
			
		||||
  instead of separate approaches for static and substitutable
 | 
			
		||||
  dependencies.
 | 
			
		||||
* Removes need to understand Angular's DI mechanism.
 | 
			
		||||
* Improves usability of documentation (`typeService` is an
 | 
			
		||||
* Improves useability of documentation (`typeService` is an
 | 
			
		||||
  instance of `CompositeService` and implements `TypeService`
 | 
			
		||||
  so you can easily traverse links in the JSDoc.)
 | 
			
		||||
* Can be used more easily from Web Workers, allowing services
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
## Legacy Documentation
 | 
			
		||||
 | 
			
		||||
As we transition to a new API, the following documentation for the old API
 | 
			
		||||
(which is supported during the transition) may be useful as well:
 | 
			
		||||
(which is supported during the transtion) may be useful as well:
 | 
			
		||||
 | 
			
		||||
 * The [Architecture Overview](architecture/) describes the concepts used
 | 
			
		||||
 throughout Open MCT, and gives a high level overview of the platform's design.
 | 
			
		||||
 
 | 
			
		||||
@@ -28,15 +28,6 @@ define([
 | 
			
		||||
                        domain: 2
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "cos",
 | 
			
		||||
                    name: "Cosine",
 | 
			
		||||
                    unit: "deg",
 | 
			
		||||
                    formatString: '%0.2f',
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        domain: 3
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                // Need to enable "LocalTimeSystem" plugin to make use of this
 | 
			
		||||
                // {
 | 
			
		||||
                //     key: "local",
 | 
			
		||||
@@ -118,100 +109,6 @@ define([
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        'example.spectral-generator': {
 | 
			
		||||
            values: [
 | 
			
		||||
                {
 | 
			
		||||
                    key: "name",
 | 
			
		||||
                    name: "Name",
 | 
			
		||||
                    format: "string"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "utc",
 | 
			
		||||
                    name: "Time",
 | 
			
		||||
                    format: "utc",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        domain: 1
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "wavelength",
 | 
			
		||||
                    name: "Wavelength",
 | 
			
		||||
                    unit: "Hz",
 | 
			
		||||
                    formatString: '%0.2f',
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        domain: 2,
 | 
			
		||||
                        spectralAttribute: true
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "cos",
 | 
			
		||||
                    name: "Cosine",
 | 
			
		||||
                    unit: "deg",
 | 
			
		||||
                    formatString: '%0.2f',
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 2,
 | 
			
		||||
                        spectralAttribute: true
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        'example.spectral-aggregate-generator': {
 | 
			
		||||
            values: [
 | 
			
		||||
                {
 | 
			
		||||
                    key: "name",
 | 
			
		||||
                    name: "Name",
 | 
			
		||||
                    format: "string"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "utc",
 | 
			
		||||
                    name: "Time",
 | 
			
		||||
                    format: "utc",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        domain: 1
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "ch1",
 | 
			
		||||
                    name: "Channel 1",
 | 
			
		||||
                    format: "string",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 1
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "ch2",
 | 
			
		||||
                    name: "Channel 2",
 | 
			
		||||
                    format: "string",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 2
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "ch3",
 | 
			
		||||
                    name: "Channel 3",
 | 
			
		||||
                    format: "string",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 3
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "ch4",
 | 
			
		||||
                    name: "Channel 4",
 | 
			
		||||
                    format: "string",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 4
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "ch5",
 | 
			
		||||
                    name: "Channel 5",
 | 
			
		||||
                    format: "string",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 5
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
 | 
			
		||||
], function (
 | 
			
		||||
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    function SpectralAggregateGeneratorProvider() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function pointForTimestamp(timestamp, count, name) {
 | 
			
		||||
        return {
 | 
			
		||||
            name: name,
 | 
			
		||||
            utc: String(Math.floor(timestamp / count) * count),
 | 
			
		||||
            ch1: String(Math.floor(timestamp / count) % 1),
 | 
			
		||||
            ch2: String(Math.floor(timestamp / count) % 2),
 | 
			
		||||
            ch3: String(Math.floor(timestamp / count) % 3),
 | 
			
		||||
            ch4: String(Math.floor(timestamp / count) % 4),
 | 
			
		||||
            ch5: String(Math.floor(timestamp / count) % 5)
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SpectralAggregateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) {
 | 
			
		||||
        return domainObject.type === 'example.spectral-aggregate-generator';
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    SpectralAggregateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
 | 
			
		||||
        var count = 5000;
 | 
			
		||||
 | 
			
		||||
        var interval = setInterval(function () {
 | 
			
		||||
            var now = Date.now();
 | 
			
		||||
            var datum = pointForTimestamp(now, count, domainObject.name);
 | 
			
		||||
            callback(datum);
 | 
			
		||||
        }, count);
 | 
			
		||||
 | 
			
		||||
        return function () {
 | 
			
		||||
            clearInterval(interval);
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    SpectralAggregateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
 | 
			
		||||
        return domainObject.type === 'example.spectral-aggregate-generator';
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    SpectralAggregateGeneratorProvider.prototype.request = function (domainObject, options) {
 | 
			
		||||
        var start = options.start;
 | 
			
		||||
        var end = Math.min(Date.now(), options.end); // no future values
 | 
			
		||||
        var count = 5000;
 | 
			
		||||
        if (options.strategy === 'latest' || options.size === 1) {
 | 
			
		||||
            start = end;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var data = [];
 | 
			
		||||
        while (start <= end && data.length < 5000) {
 | 
			
		||||
            data.push(pointForTimestamp(start, count, domainObject.name));
 | 
			
		||||
            start += count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Promise.resolve(data);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return SpectralAggregateGeneratorProvider;
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
@@ -1,102 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    './WorkerInterface'
 | 
			
		||||
], function (
 | 
			
		||||
    WorkerInterface
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    var REQUEST_DEFAULTS = {
 | 
			
		||||
        amplitude: 1,
 | 
			
		||||
        wavelength: 1,
 | 
			
		||||
        period: 10,
 | 
			
		||||
        offset: 0,
 | 
			
		||||
        dataRateInHz: 1,
 | 
			
		||||
        randomness: 0,
 | 
			
		||||
        phase: 0
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    function SpectralGeneratorProvider() {
 | 
			
		||||
        this.workerInterface = new WorkerInterface();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    SpectralGeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
 | 
			
		||||
        return domainObject.type === 'example.spectral-generator';
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    SpectralGeneratorProvider.prototype.supportsRequest =
 | 
			
		||||
        SpectralGeneratorProvider.prototype.supportsSubscribe =
 | 
			
		||||
            SpectralGeneratorProvider.prototype.canProvideTelemetry;
 | 
			
		||||
 | 
			
		||||
    SpectralGeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request = {}) {
 | 
			
		||||
        var props = [
 | 
			
		||||
            'amplitude',
 | 
			
		||||
            'wavelength',
 | 
			
		||||
            'period',
 | 
			
		||||
            'offset',
 | 
			
		||||
            'dataRateInHz',
 | 
			
		||||
            'phase',
 | 
			
		||||
            'randomness'
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        var workerRequest = {};
 | 
			
		||||
 | 
			
		||||
        props.forEach(function (prop) {
 | 
			
		||||
            if (domainObject.telemetry && Object.prototype.hasOwnProperty.call(domainObject.telemetry, prop)) {
 | 
			
		||||
                workerRequest[prop] = domainObject.telemetry[prop];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (request && Object.prototype.hasOwnProperty.call(request, prop)) {
 | 
			
		||||
                workerRequest[prop] = request[prop];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!Object.prototype.hasOwnProperty.call(workerRequest, prop)) {
 | 
			
		||||
                workerRequest[prop] = REQUEST_DEFAULTS[prop];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            workerRequest[prop] = Number(workerRequest[prop]);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        workerRequest.name = domainObject.name;
 | 
			
		||||
 | 
			
		||||
        return workerRequest;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    SpectralGeneratorProvider.prototype.request = function (domainObject, request) {
 | 
			
		||||
        var workerRequest = this.makeWorkerRequest(domainObject, request);
 | 
			
		||||
        workerRequest.start = request.start;
 | 
			
		||||
        workerRequest.end = request.end;
 | 
			
		||||
        workerRequest.spectra = true;
 | 
			
		||||
 | 
			
		||||
        return this.workerInterface.request(workerRequest);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    SpectralGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
 | 
			
		||||
        var workerRequest = this.makeWorkerRequest(domainObject, {});
 | 
			
		||||
        workerRequest.spectra = true;
 | 
			
		||||
 | 
			
		||||
        return this.workerInterface.subscribe(workerRequest, callback);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return SpectralGeneratorProvider;
 | 
			
		||||
});
 | 
			
		||||
@@ -63,7 +63,7 @@ define([
 | 
			
		||||
 | 
			
		||||
    StateGeneratorProvider.prototype.request = function (domainObject, options) {
 | 
			
		||||
        var start = options.start;
 | 
			
		||||
        var end = Math.min(Date.now(), options.end); // no future values
 | 
			
		||||
        var end = options.end;
 | 
			
		||||
        var duration = domainObject.telemetry.duration * 1000;
 | 
			
		||||
        if (options.strategy === 'latest' || options.size === 1) {
 | 
			
		||||
            start = end;
 | 
			
		||||
 
 | 
			
		||||
@@ -54,38 +54,23 @@
 | 
			
		||||
        var start = Date.now();
 | 
			
		||||
        var step = 1000 / data.dataRateInHz;
 | 
			
		||||
        var nextStep = start - (start % step) + step;
 | 
			
		||||
        let work;
 | 
			
		||||
        if (data.spectra) {
 | 
			
		||||
            work = function (now) {
 | 
			
		||||
                while (nextStep < now) {
 | 
			
		||||
                    const messageCopy = Object.create(message);
 | 
			
		||||
                    message.data.start = nextStep - (60 * 1000);
 | 
			
		||||
                    message.data.end = nextStep;
 | 
			
		||||
                    onRequest(messageCopy);
 | 
			
		||||
                    nextStep += step;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return nextStep;
 | 
			
		||||
            };
 | 
			
		||||
        } else {
 | 
			
		||||
            work = function (now) {
 | 
			
		||||
                while (nextStep < now) {
 | 
			
		||||
                    self.postMessage({
 | 
			
		||||
                        id: message.id,
 | 
			
		||||
                        data: {
 | 
			
		||||
                            name: data.name,
 | 
			
		||||
                            utc: nextStep,
 | 
			
		||||
                            yesterday: nextStep - 60 * 60 * 24 * 1000,
 | 
			
		||||
                            sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
 | 
			
		||||
                            wavelength: wavelength(start, nextStep),
 | 
			
		||||
                            cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness)
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    nextStep += step;
 | 
			
		||||
                }
 | 
			
		||||
        function work(now) {
 | 
			
		||||
            while (nextStep < now) {
 | 
			
		||||
                self.postMessage({
 | 
			
		||||
                    id: message.id,
 | 
			
		||||
                    data: {
 | 
			
		||||
                        name: data.name,
 | 
			
		||||
                        utc: nextStep,
 | 
			
		||||
                        yesterday: nextStep - 60 * 60 * 24 * 1000,
 | 
			
		||||
                        sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
 | 
			
		||||
                        cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness)
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                nextStep += step;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                return nextStep;
 | 
			
		||||
            };
 | 
			
		||||
            return nextStep;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subscriptions[message.id] = work;
 | 
			
		||||
@@ -126,21 +111,13 @@
 | 
			
		||||
                utc: nextStep,
 | 
			
		||||
                yesterday: nextStep - 60 * 60 * 24 * 1000,
 | 
			
		||||
                sin: sin(nextStep, period, amplitude, offset, phase, randomness),
 | 
			
		||||
                wavelength: wavelength(start, nextStep),
 | 
			
		||||
                cos: cos(nextStep, period, amplitude, offset, phase, randomness)
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.postMessage({
 | 
			
		||||
            id: message.id,
 | 
			
		||||
            data: request.spectra ? {
 | 
			
		||||
                wavelength: data.map((item) => {
 | 
			
		||||
                    return item.wavelength;
 | 
			
		||||
                }),
 | 
			
		||||
                cos: data.map((item) => {
 | 
			
		||||
                    return item.cos;
 | 
			
		||||
                })
 | 
			
		||||
            } : data
 | 
			
		||||
            data: data
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -154,10 +131,6 @@
 | 
			
		||||
            * Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function wavelength(start, nextStep) {
 | 
			
		||||
        return (nextStep - start) / 10;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sendError(error, message) {
 | 
			
		||||
        self.postMessage({
 | 
			
		||||
            error: error.name + ': ' + error.message,
 | 
			
		||||
 
 | 
			
		||||
@@ -24,15 +24,11 @@ define([
 | 
			
		||||
    "./GeneratorProvider",
 | 
			
		||||
    "./SinewaveLimitProvider",
 | 
			
		||||
    "./StateGeneratorProvider",
 | 
			
		||||
    "./SpectralGeneratorProvider",
 | 
			
		||||
    "./SpectralAggregateGeneratorProvider",
 | 
			
		||||
    "./GeneratorMetadataProvider"
 | 
			
		||||
], function (
 | 
			
		||||
    GeneratorProvider,
 | 
			
		||||
    SinewaveLimitProvider,
 | 
			
		||||
    StateGeneratorProvider,
 | 
			
		||||
    SpectralGeneratorProvider,
 | 
			
		||||
    SpectralAggregateGeneratorProvider,
 | 
			
		||||
    GeneratorMetadataProvider
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
@@ -65,37 +61,6 @@ define([
 | 
			
		||||
 | 
			
		||||
        openmct.telemetry.addProvider(new StateGeneratorProvider());
 | 
			
		||||
 | 
			
		||||
        openmct.types.addType("example.spectral-generator", {
 | 
			
		||||
            name: "Spectral Generator",
 | 
			
		||||
            description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
 | 
			
		||||
            cssClass: "icon-generator-telemetry",
 | 
			
		||||
            creatable: true,
 | 
			
		||||
            initialize: function (object) {
 | 
			
		||||
                object.telemetry = {
 | 
			
		||||
                    period: 10,
 | 
			
		||||
                    amplitude: 1,
 | 
			
		||||
                    wavelength: 1,
 | 
			
		||||
                    frequency: 1,
 | 
			
		||||
                    offset: 0,
 | 
			
		||||
                    dataRateInHz: 1,
 | 
			
		||||
                    phase: 0,
 | 
			
		||||
                    randomness: 0
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        openmct.telemetry.addProvider(new SpectralGeneratorProvider());
 | 
			
		||||
 | 
			
		||||
        openmct.types.addType("example.spectral-aggregate-generator", {
 | 
			
		||||
            name: "Spectral Aggregate Generator",
 | 
			
		||||
            description: "For development use. Generates example streaming telemetry data using a simple state algorithm.",
 | 
			
		||||
            cssClass: "icon-generator-telemetry",
 | 
			
		||||
            creatable: true,
 | 
			
		||||
            initialize: function (object) {
 | 
			
		||||
                object.telemetry = {};
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        openmct.telemetry.addProvider(new SpectralAggregateGeneratorProvider());
 | 
			
		||||
 | 
			
		||||
        openmct.types.addType("generator", {
 | 
			
		||||
            name: "Sine Wave Generator",
 | 
			
		||||
            description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
 | 
			
		||||
 
 | 
			
		||||
@@ -152,7 +152,7 @@
 | 
			
		||||
        <h2>How to Use Glyphs</h2>
 | 
			
		||||
        <div class="cols cols1-1">
 | 
			
		||||
            <div class="col">
 | 
			
		||||
                <p>The easiest way to use a glyph is to include its CSS class in an element. The CSS adds a pseudo <code>:before</code> HTML element to whatever element it's attached to that makes proper use of the symbols font.</p>
 | 
			
		||||
                <p>The easiest way to use a glyph is to include its CSS class in an element. The CSS adds a psuedo <code>:before</code> HTML element to whatever element it's attached to that makes proper use of the symbols font.</p>
 | 
			
		||||
                <p>Alternately, you can use the <code>.ui-symbol</code> class in an object that contains encoded HTML entities. This method is only recommended if you cannot use the aforementioned CSS class approach.</p>
 | 
			
		||||
            </div>
 | 
			
		||||
            <mct-example><a class="s-button icon-gear" title="Settings"></a>
 | 
			
		||||
 
 | 
			
		||||
@@ -195,7 +195,6 @@
 | 
			
		||||
            ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
 | 
			
		||||
            {indicator: true}
 | 
			
		||||
        ));
 | 
			
		||||
        openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
 | 
			
		||||
        openmct.start();
 | 
			
		||||
    </script>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
const devMode = process.env.NODE_ENV !== 'production';
 | 
			
		||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
 | 
			
		||||
const coverageEnabled = process.env.COVERAGE === 'true';
 | 
			
		||||
const reporters = ['spec', 'html', 'junit'];
 | 
			
		||||
const reporters = ['progress', 'html', 'junit'];
 | 
			
		||||
 | 
			
		||||
if (coverageEnabled) {
 | 
			
		||||
    reporters.push('coverage-istanbul');
 | 
			
		||||
@@ -60,7 +60,7 @@ module.exports = (config) => {
 | 
			
		||||
        client: {
 | 
			
		||||
            jasmine: {
 | 
			
		||||
                random: false,
 | 
			
		||||
                timeoutInterval: 5000
 | 
			
		||||
                timeoutInterval: 30000
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        customLaunchers: {
 | 
			
		||||
@@ -88,6 +88,11 @@ module.exports = (config) => {
 | 
			
		||||
            outputFile: "test-results.xml",
 | 
			
		||||
            useBrowserName: false
 | 
			
		||||
        },
 | 
			
		||||
        browserConsoleLogOptions: {
 | 
			
		||||
            level: "error",
 | 
			
		||||
            format: "%b %T: %m",
 | 
			
		||||
            terminal: true
 | 
			
		||||
        },
 | 
			
		||||
        coverageIstanbulReporter: {
 | 
			
		||||
            fixWebpackSourcePaths: true,
 | 
			
		||||
            dir: process.env.CIRCLE_ARTIFACTS
 | 
			
		||||
@@ -100,15 +105,6 @@ module.exports = (config) => {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        specReporter: {
 | 
			
		||||
            maxLogLines: 5,
 | 
			
		||||
            suppressErrorSummary: true,
 | 
			
		||||
            suppressFailed: false,
 | 
			
		||||
            suppressPassed: false,
 | 
			
		||||
            suppressSkipped: true,
 | 
			
		||||
            showSpecTiming: true,
 | 
			
		||||
            failFast: false
 | 
			
		||||
        },
 | 
			
		||||
        preprocessors: {
 | 
			
		||||
            'indexTest.js': ['webpack', 'sourcemap']
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								package.json
									
									
									
									
									
								
							@@ -1,7 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "openmct",
 | 
			
		||||
  "version": "1.7.8-SNAPSHOT",
 | 
			
		||||
  "version": "1.7.6-SNAPSHOT",
 | 
			
		||||
  "description": "The Open MCT core platform",
 | 
			
		||||
  "dependencies": {},
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "angular": ">=1.8.0",
 | 
			
		||||
    "angular-route": "1.4.14",
 | 
			
		||||
@@ -11,9 +12,16 @@
 | 
			
		||||
    "copy-webpack-plugin": "^4.5.2",
 | 
			
		||||
    "cross-env": "^6.0.3",
 | 
			
		||||
    "css-loader": "^1.0.0",
 | 
			
		||||
    "d3-array": "1.2.x",
 | 
			
		||||
    "d3-axis": "1.0.x",
 | 
			
		||||
    "d3-collection": "1.0.x",
 | 
			
		||||
    "d3-color": "1.0.x",
 | 
			
		||||
    "d3-format": "1.2.x",
 | 
			
		||||
    "d3-interpolate": "1.1.x",
 | 
			
		||||
    "d3-scale": "1.0.x",
 | 
			
		||||
    "d3-selection": "1.3.x",
 | 
			
		||||
    "d3-time": "1.0.x",
 | 
			
		||||
    "d3-time-format": "2.1.x",
 | 
			
		||||
    "eslint": "7.0.0",
 | 
			
		||||
    "eslint-plugin-vue": "^7.5.0",
 | 
			
		||||
    "eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
 | 
			
		||||
@@ -33,15 +41,14 @@
 | 
			
		||||
    "jsdoc": "^3.3.2",
 | 
			
		||||
    "karma": "6.3.4",
 | 
			
		||||
    "karma-chrome-launcher": "3.1.0",
 | 
			
		||||
    "karma-firefox-launcher": "2.1.1",
 | 
			
		||||
    "karma-cli": "2.0.0",
 | 
			
		||||
    "karma-coverage": "2.0.3",
 | 
			
		||||
    "karma-coverage-istanbul-reporter": "3.0.3",
 | 
			
		||||
    "karma-firefox-launcher": "2.1.1",
 | 
			
		||||
    "karma-junit-reporter": "2.0.1",
 | 
			
		||||
    "karma-html-reporter": "0.2.7",
 | 
			
		||||
    "karma-jasmine": "4.0.1",
 | 
			
		||||
    "karma-junit-reporter": "2.0.1",
 | 
			
		||||
    "karma-sourcemap-loader": "0.3.8",
 | 
			
		||||
    "karma-spec-reporter": "0.0.32",
 | 
			
		||||
    "karma-webpack": "4.0.2",
 | 
			
		||||
    "location-bar": "^3.0.1",
 | 
			
		||||
    "lodash": "^4.17.12",
 | 
			
		||||
@@ -55,8 +62,6 @@
 | 
			
		||||
    "node-bourbon": "^4.2.3",
 | 
			
		||||
    "node-sass": "^4.14.1",
 | 
			
		||||
    "painterro": "^1.2.56",
 | 
			
		||||
    "plotly.js-basic-dist": "^2.5.0",
 | 
			
		||||
    "plotly.js-gl2d-dist": "^2.5.0",
 | 
			
		||||
    "printj": "^1.2.1",
 | 
			
		||||
    "raw-loader": "^0.5.1",
 | 
			
		||||
    "request": "^2.69.0",
 | 
			
		||||
@@ -65,7 +70,6 @@
 | 
			
		||||
    "uuid": "^3.3.3",
 | 
			
		||||
    "v8-compile-cache": "^1.1.0",
 | 
			
		||||
    "vue": "2.5.6",
 | 
			
		||||
    "vue-eslint-parser": "7.11.0",
 | 
			
		||||
    "vue-loader": "^15.2.6",
 | 
			
		||||
    "vue-template-compiler": "2.5.6",
 | 
			
		||||
    "webpack": "^4.16.2",
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ define(
 | 
			
		||||
         *
 | 
			
		||||
         * @param {DomainObject} domainObject the domain object to navigate to
 | 
			
		||||
         * @param {Boolean} force if true, force navigation to occur.
 | 
			
		||||
         * @returns {Boolean} true if navigation occurred, otherwise false.
 | 
			
		||||
         * @returns {Boolean} true if navigation occured, otherwise false.
 | 
			
		||||
         */
 | 
			
		||||
        NavigationService.prototype.setNavigation = function (domainObject, force) {
 | 
			
		||||
            if (force) {
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,8 @@ define(
 | 
			
		||||
         * or finish() are called.
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.edit = function () {
 | 
			
		||||
            console.warn('DEPRECATED: cannot edit via edit capability, use openmct.editor instead.');
 | 
			
		||||
 | 
			
		||||
            if (!this.openmct.editor.isEditing()) {
 | 
			
		||||
                this.openmct.editor.edit();
 | 
			
		||||
                this.domainObject.getCapability('status').set('editing', true);
 | 
			
		||||
@@ -80,6 +82,8 @@ define(
 | 
			
		||||
         * @returns {*}
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.save = function () {
 | 
			
		||||
            console.warn('DEPRECATED: cannot save via edit capability, use openmct.editor instead.');
 | 
			
		||||
 | 
			
		||||
            return Promise.resolve();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -91,6 +95,8 @@ define(
 | 
			
		||||
         * @returns {*}
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.finish = function () {
 | 
			
		||||
            console.warn('DEPRECATED: cannot finish via edit capability, use openmct.editor instead.');
 | 
			
		||||
 | 
			
		||||
            return Promise.resolve();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,14 +25,15 @@ define([
 | 
			
		||||
], function (
 | 
			
		||||
    moment
 | 
			
		||||
) {
 | 
			
		||||
    const DATE_FORMAT = "YYYY-MM-DD HH:mm:ss.SSS";
 | 
			
		||||
    const DATE_FORMATS = [
 | 
			
		||||
        DATE_FORMAT,
 | 
			
		||||
        DATE_FORMAT + "Z",
 | 
			
		||||
        "YYYY-MM-DD HH:mm:ss",
 | 
			
		||||
        "YYYY-MM-DD HH:mm",
 | 
			
		||||
        "YYYY-MM-DD"
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss.SSS",
 | 
			
		||||
        DATE_FORMATS = [
 | 
			
		||||
            DATE_FORMAT,
 | 
			
		||||
            DATE_FORMAT + "Z",
 | 
			
		||||
            "YYYY-MM-DD HH:mm:ss",
 | 
			
		||||
            "YYYY-MM-DD HH:mm",
 | 
			
		||||
            "YYYY-MM-DD"
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @typedef Scale
 | 
			
		||||
@@ -52,27 +53,15 @@ define([
 | 
			
		||||
        this.key = "utc";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} formatString
 | 
			
		||||
     * @returns the value of formatString if the value is a string type and exists in the DATE_FORMATS array; otherwise the DATE_FORMAT value.
 | 
			
		||||
     */
 | 
			
		||||
    function validateFormatString(formatString) {
 | 
			
		||||
        return typeof formatString === 'string' && DATE_FORMATS.includes(formatString) ? formatString : DATE_FORMAT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {number} value The value to format.
 | 
			
		||||
     * @param {string} formatString The string format to format. Default "YYYY-MM-DD HH:mm:ss.SSS" + "Z"
 | 
			
		||||
     * @returns {string} the formatted date(s) according to the proper parameter of formatString or the default value of "YYYY-MM-DD HH:mm:ss.SSS" + "Z".
 | 
			
		||||
     * If multiple values were requested, then an array of
 | 
			
		||||
     * @returns {string} the formatted date(s). If multiple values were requested, then an array of
 | 
			
		||||
     * formatted values will be returned. Where a value could not be formatted, `undefined` will be returned at its position
 | 
			
		||||
     * in the array.
 | 
			
		||||
     */
 | 
			
		||||
    UTCTimeFormat.prototype.format = function (value, formatString) {
 | 
			
		||||
    UTCTimeFormat.prototype.format = function (value) {
 | 
			
		||||
        if (value !== undefined) {
 | 
			
		||||
            const format = validateFormatString(formatString);
 | 
			
		||||
 | 
			
		||||
            return moment.utc(value).format(format) + (formatString ? '' : 'Z');
 | 
			
		||||
            return moment.utc(value).format(DATE_FORMAT) + "Z";
 | 
			
		||||
        } else {
 | 
			
		||||
            return value;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,14 +21,28 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    "./src/AgentService"
 | 
			
		||||
    "./src/MCTDevice",
 | 
			
		||||
    "./src/AgentService",
 | 
			
		||||
    "./src/DeviceClassifier"
 | 
			
		||||
], function (
 | 
			
		||||
    AgentService
 | 
			
		||||
    MCTDevice,
 | 
			
		||||
    AgentService,
 | 
			
		||||
    DeviceClassifier
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        name: "platform/commonUI/mobile",
 | 
			
		||||
        definition: {
 | 
			
		||||
            "extensions": {
 | 
			
		||||
                "directives": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "mctDevice",
 | 
			
		||||
                        "implementation": MCTDevice,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "agentService"
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "services": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "agentService",
 | 
			
		||||
@@ -37,6 +51,15 @@ define([
 | 
			
		||||
                            "$window"
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "runs": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "implementation": DeviceClassifier,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "agentService",
 | 
			
		||||
                            "$document"
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -20,12 +20,122 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(["../../../../src/utils/agent/Agent.js"], function (Agent) {
 | 
			
		||||
    function AngularAgentServiceWrapper(window) {
 | 
			
		||||
        const AS = Agent.default;
 | 
			
		||||
/**
 | 
			
		||||
 * Provides features which support variant behavior on mobile devices.
 | 
			
		||||
 *
 | 
			
		||||
 * @namespace platform/commonUI/mobile
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
 | 
			
		||||
        return new AS(window);
 | 
			
		||||
        /**
 | 
			
		||||
         * The query service handles calls for browser and userAgent
 | 
			
		||||
         * info using a comparison between the userAgent and key
 | 
			
		||||
         * device names
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @param $window Angular-injected instance of the window
 | 
			
		||||
         * @memberof platform/commonUI/mobile
 | 
			
		||||
         */
 | 
			
		||||
        function AgentService($window) {
 | 
			
		||||
            var userAgent = $window.navigator.userAgent,
 | 
			
		||||
                matches = userAgent.match(/iPad|iPhone|Android/i) || [];
 | 
			
		||||
 | 
			
		||||
            this.userAgent = userAgent;
 | 
			
		||||
            this.mobileName = matches[0];
 | 
			
		||||
            this.$window = $window;
 | 
			
		||||
            this.touchEnabled = ($window.ontouchstart !== undefined);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the user is on a mobile device.
 | 
			
		||||
         * @returns {boolean} true on mobile
 | 
			
		||||
         */
 | 
			
		||||
        AgentService.prototype.isMobile = function () {
 | 
			
		||||
            return Boolean(this.mobileName);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the user is on a phone-sized mobile device.
 | 
			
		||||
         * @returns {boolean} true on a phone
 | 
			
		||||
         */
 | 
			
		||||
        AgentService.prototype.isPhone = function () {
 | 
			
		||||
            if (this.isMobile()) {
 | 
			
		||||
                if (this.isAndroidTablet()) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else if (this.mobileName === 'iPad') {
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the user is on a tablet sized android device
 | 
			
		||||
         * @returns {boolean} true on an android tablet
 | 
			
		||||
         */
 | 
			
		||||
        AgentService.prototype.isAndroidTablet = function () {
 | 
			
		||||
            if (this.mobileName === 'Android') {
 | 
			
		||||
                if (this.isPortrait() && window.innerWidth >= 768) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                } else if (this.isLandscape() && window.innerHeight >= 768) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the user is on a tablet-sized mobile device.
 | 
			
		||||
         * @returns {boolean} true on a tablet
 | 
			
		||||
         */
 | 
			
		||||
        AgentService.prototype.isTablet = function () {
 | 
			
		||||
            return (this.isMobile() && !this.isPhone() && this.mobileName !== 'Android') || (this.isMobile() && this.isAndroidTablet());
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the user's device is in a portrait-style
 | 
			
		||||
         * orientation (display width is narrower than display height.)
 | 
			
		||||
         * @returns {boolean} true in portrait mode
 | 
			
		||||
         */
 | 
			
		||||
        AgentService.prototype.isPortrait = function () {
 | 
			
		||||
            return this.$window.innerWidth < this.$window.innerHeight;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the user's device is in a landscape-style
 | 
			
		||||
         * orientation (display width is greater than display height.)
 | 
			
		||||
         * @returns {boolean} true in landscape mode
 | 
			
		||||
         */
 | 
			
		||||
        AgentService.prototype.isLandscape = function () {
 | 
			
		||||
            return !this.isPortrait();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the user's device supports a touch interface.
 | 
			
		||||
         * @returns {boolean} true if touch is supported
 | 
			
		||||
         */
 | 
			
		||||
        AgentService.prototype.isTouch = function () {
 | 
			
		||||
            return this.touchEnabled;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check if the user agent matches a certain named device,
 | 
			
		||||
         * as indicated by checking for a case-insensitive substring
 | 
			
		||||
         * match.
 | 
			
		||||
         * @param {string} name the name to check for
 | 
			
		||||
         * @returns {boolean} true if the user agent includes that name
 | 
			
		||||
         */
 | 
			
		||||
        AgentService.prototype.isBrowser = function (name) {
 | 
			
		||||
            name = name.toLowerCase();
 | 
			
		||||
 | 
			
		||||
            return this.userAgent.toLowerCase().indexOf(name) !== -1;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return AgentService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return AngularAgentServiceWrapper;
 | 
			
		||||
});
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,96 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
import AgentService from "./AgentService";
 | 
			
		||||
 | 
			
		||||
const TEST_USER_AGENTS = {
 | 
			
		||||
    DESKTOP:
 | 
			
		||||
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36",
 | 
			
		||||
    IPAD:
 | 
			
		||||
    "Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
 | 
			
		||||
    IPHONE:
 | 
			
		||||
    "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe("The AgentService", function () {
 | 
			
		||||
    let testWindow;
 | 
			
		||||
    let agentService;
 | 
			
		||||
 | 
			
		||||
    beforeEach(function () {
 | 
			
		||||
        testWindow = {
 | 
			
		||||
            innerWidth: 640,
 | 
			
		||||
            innerHeight: 480,
 | 
			
		||||
            navigator: {
 | 
			
		||||
                userAgent: TEST_USER_AGENTS.DESKTOP
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("recognizes desktop devices as non-mobile", function () {
 | 
			
		||||
        testWindow.navigator.userAgent = TEST_USER_AGENTS.DESKTOP;
 | 
			
		||||
        agentService = new AgentService(testWindow);
 | 
			
		||||
        expect(agentService.isMobile()).toBeFalsy();
 | 
			
		||||
        expect(agentService.isPhone()).toBeFalsy();
 | 
			
		||||
        expect(agentService.isTablet()).toBeFalsy();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("detects iPhones", function () {
 | 
			
		||||
        testWindow.navigator.userAgent = TEST_USER_AGENTS.IPHONE;
 | 
			
		||||
        agentService = new AgentService(testWindow);
 | 
			
		||||
        expect(agentService.isMobile()).toBeTruthy();
 | 
			
		||||
        expect(agentService.isPhone()).toBeTruthy();
 | 
			
		||||
        expect(agentService.isTablet()).toBeFalsy();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("detects iPads", function () {
 | 
			
		||||
        testWindow.navigator.userAgent = TEST_USER_AGENTS.IPAD;
 | 
			
		||||
        agentService = new AgentService(testWindow);
 | 
			
		||||
        expect(agentService.isMobile()).toBeTruthy();
 | 
			
		||||
        expect(agentService.isPhone()).toBeFalsy();
 | 
			
		||||
        expect(agentService.isTablet()).toBeTruthy();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("detects display orientation", function () {
 | 
			
		||||
        agentService = new AgentService(testWindow);
 | 
			
		||||
        testWindow.innerWidth = 1024;
 | 
			
		||||
        testWindow.innerHeight = 400;
 | 
			
		||||
        expect(agentService.isPortrait()).toBeFalsy();
 | 
			
		||||
        expect(agentService.isLandscape()).toBeTruthy();
 | 
			
		||||
        testWindow.innerWidth = 400;
 | 
			
		||||
        testWindow.innerHeight = 1024;
 | 
			
		||||
        expect(agentService.isPortrait()).toBeTruthy();
 | 
			
		||||
        expect(agentService.isLandscape()).toBeFalsy();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("detects touch support", function () {
 | 
			
		||||
        testWindow.ontouchstart = null;
 | 
			
		||||
        expect(new AgentService(testWindow).isTouch()).toBe(true);
 | 
			
		||||
        delete testWindow.ontouchstart;
 | 
			
		||||
        expect(new AgentService(testWindow).isTouch()).toBe(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("allows for checking browser type", function () {
 | 
			
		||||
        testWindow.navigator.userAgent = "Chromezilla Safarifox";
 | 
			
		||||
        agentService = new AgentService(testWindow);
 | 
			
		||||
        expect(agentService.isBrowser("Chrome")).toBe(true);
 | 
			
		||||
        expect(agentService.isBrowser("Firefox")).toBe(false);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										72
									
								
								platform/commonUI/mobile/src/DeviceClassifier.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								platform/commonUI/mobile/src/DeviceClassifier.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['./DeviceMatchers'],
 | 
			
		||||
    function (DeviceMatchers) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Runs at application startup and adds a subset of the following
 | 
			
		||||
         * CSS classes to the body of the document, depending on device
 | 
			
		||||
         * attributes:
 | 
			
		||||
         *
 | 
			
		||||
         * * `mobile`: Phones or tablets.
 | 
			
		||||
         * * `phone`: Phones specifically.
 | 
			
		||||
         * * `tablet`: Tablets specifically.
 | 
			
		||||
         * * `desktop`: Non-mobile devices.
 | 
			
		||||
         * * `portrait`: Devices in a portrait-style orientation.
 | 
			
		||||
         * * `landscape`: Devices in a landscape-style orientation.
 | 
			
		||||
         * * `touch`: Device supports touch events.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {platform/commonUI/mobile.AgentService} agentService
 | 
			
		||||
         *        the service used to examine the user agent
 | 
			
		||||
         * @param $document Angular's jqLite-wrapped document element
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function MobileClassifier(agentService, $document) {
 | 
			
		||||
            var body = $document.find('body');
 | 
			
		||||
 | 
			
		||||
            Object.keys(DeviceMatchers).forEach(function (key, index, array) {
 | 
			
		||||
                if (DeviceMatchers[key](agentService)) {
 | 
			
		||||
                    body.addClass(key);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (agentService.isMobile()) {
 | 
			
		||||
                var mediaQuery = window.matchMedia('(orientation: landscape)');
 | 
			
		||||
 | 
			
		||||
                mediaQuery.addListener(function (event) {
 | 
			
		||||
                    if (event.matches) {
 | 
			
		||||
                        body.removeClass('portrait');
 | 
			
		||||
                        body.addClass('landscape');
 | 
			
		||||
                    } else {
 | 
			
		||||
                        body.removeClass('landscape');
 | 
			
		||||
                        body.addClass('portrait');
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return MobileClassifier;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -19,41 +19,40 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
define(function () {
 | 
			
		||||
 | 
			
		||||
import Clock from './components/Clock.vue';
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default function ClockViewProvider(openmct) {
 | 
			
		||||
    /**
 | 
			
		||||
     * An object containing key-value pairs, where keys are symbolic of
 | 
			
		||||
     * device attributes, and values are functions that take the
 | 
			
		||||
     * `agentService` as inputs and return boolean values indicating
 | 
			
		||||
     * whether or not the current device has these attributes.
 | 
			
		||||
     *
 | 
			
		||||
     * For internal use by the mobile support bundle.
 | 
			
		||||
     *
 | 
			
		||||
     * @memberof platform/commonUI/mobile
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    return {
 | 
			
		||||
        key: 'clock.view',
 | 
			
		||||
        name: 'Clock',
 | 
			
		||||
        cssClass: 'icon-clock',
 | 
			
		||||
        canView(domainObject) {
 | 
			
		||||
            return domainObject.type === 'clock';
 | 
			
		||||
        mobile: function (agentService) {
 | 
			
		||||
            return agentService.isMobile();
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        view: function (domainObject) {
 | 
			
		||||
            let component;
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                show: function (element) {
 | 
			
		||||
                    component = new Vue({
 | 
			
		||||
                        el: element,
 | 
			
		||||
                        components: {
 | 
			
		||||
                            Clock
 | 
			
		||||
                        },
 | 
			
		||||
                        provide: {
 | 
			
		||||
                            openmct,
 | 
			
		||||
                            domainObject
 | 
			
		||||
                        },
 | 
			
		||||
                        template: '<clock />'
 | 
			
		||||
                    });
 | 
			
		||||
                },
 | 
			
		||||
                destroy: function () {
 | 
			
		||||
                    component.$destroy();
 | 
			
		||||
                    component = undefined;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        phone: function (agentService) {
 | 
			
		||||
            return agentService.isPhone();
 | 
			
		||||
        },
 | 
			
		||||
        tablet: function (agentService) {
 | 
			
		||||
            return agentService.isTablet();
 | 
			
		||||
        },
 | 
			
		||||
        desktop: function (agentService) {
 | 
			
		||||
            return !agentService.isMobile();
 | 
			
		||||
        },
 | 
			
		||||
        portrait: function (agentService) {
 | 
			
		||||
            return agentService.isPortrait();
 | 
			
		||||
        },
 | 
			
		||||
        landscape: function (agentService) {
 | 
			
		||||
            return agentService.isLandscape();
 | 
			
		||||
        },
 | 
			
		||||
        touch: function (agentService) {
 | 
			
		||||
            return agentService.isTouch();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										88
									
								
								platform/commonUI/mobile/src/MCTDevice.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								platform/commonUI/mobile/src/MCTDevice.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['./DeviceMatchers'],
 | 
			
		||||
    function (DeviceMatchers) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The `mct-device` directive, when applied as an attribute,
 | 
			
		||||
         * only includes the element when the device being used matches
 | 
			
		||||
         * a set of characteristics required.
 | 
			
		||||
         *
 | 
			
		||||
         * Required characteristics are given as space-separated strings
 | 
			
		||||
         * as the value to this attribute, e.g.:
 | 
			
		||||
         *
 | 
			
		||||
         *    <span mct-device="mobile portrait">Hello world!</span>
 | 
			
		||||
         *
 | 
			
		||||
         * ...will only show Hello world! when viewed on a mobile device
 | 
			
		||||
         * in the portrait orientation.
 | 
			
		||||
         *
 | 
			
		||||
         * Valid device characteristics to detect are:
 | 
			
		||||
         *
 | 
			
		||||
         * * `mobile`: Phones or tablets.
 | 
			
		||||
         * * `phone`: Phones specifically.
 | 
			
		||||
         * * `tablet`: Tablets specifically.
 | 
			
		||||
         * * `desktop`: Non-mobile devices.
 | 
			
		||||
         * * `portrait`: Devices in a portrait-style orientation.
 | 
			
		||||
         * * `landscape`: Devices in a landscape-style orientation.
 | 
			
		||||
         * * `touch`: Device supports touch events.
 | 
			
		||||
         *
 | 
			
		||||
         * @param {AgentService} agentService used to detect device type
 | 
			
		||||
         *        based on information about the user agent
 | 
			
		||||
         */
 | 
			
		||||
        function MCTDevice(agentService) {
 | 
			
		||||
 | 
			
		||||
            function deviceMatches(tokens) {
 | 
			
		||||
                tokens = tokens || "";
 | 
			
		||||
 | 
			
		||||
                return tokens.split(" ").every(function (token) {
 | 
			
		||||
                    var fn = DeviceMatchers[token];
 | 
			
		||||
 | 
			
		||||
                    return fn && fn(agentService);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function link(scope, element, attrs, ctrl, transclude) {
 | 
			
		||||
                if (deviceMatches(attrs.mctDevice)) {
 | 
			
		||||
                    transclude(function (clone) {
 | 
			
		||||
                        element.replaceWith(clone);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                link: link,
 | 
			
		||||
                // We are transcluding the whole element (like ng-if)
 | 
			
		||||
                transclude: 'element',
 | 
			
		||||
                // 1 more than ng-if
 | 
			
		||||
                priority: 601,
 | 
			
		||||
                // Also terminal, since element will be transcluded
 | 
			
		||||
                terminal: true,
 | 
			
		||||
                // Only apply as an attribute
 | 
			
		||||
                restrict: "A"
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return MCTDevice;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										99
									
								
								platform/commonUI/mobile/test/AgentServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								platform/commonUI/mobile/test/AgentServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/AgentService"],
 | 
			
		||||
    function (AgentService) {
 | 
			
		||||
 | 
			
		||||
        var TEST_USER_AGENTS = {
 | 
			
		||||
            DESKTOP: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36",
 | 
			
		||||
            IPAD: "Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
 | 
			
		||||
            IPHONE: "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53"
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        describe("The AgentService", function () {
 | 
			
		||||
            var testWindow, agentService;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testWindow = {
 | 
			
		||||
                    innerWidth: 640,
 | 
			
		||||
                    innerHeight: 480,
 | 
			
		||||
                    navigator: {
 | 
			
		||||
                        userAgent: TEST_USER_AGENTS.DESKTOP
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("recognizes desktop devices as non-mobile", function () {
 | 
			
		||||
                testWindow.navigator.userAgent = TEST_USER_AGENTS.DESKTOP;
 | 
			
		||||
                agentService = new AgentService(testWindow);
 | 
			
		||||
                expect(agentService.isMobile()).toBeFalsy();
 | 
			
		||||
                expect(agentService.isPhone()).toBeFalsy();
 | 
			
		||||
                expect(agentService.isTablet()).toBeFalsy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("detects iPhones", function () {
 | 
			
		||||
                testWindow.navigator.userAgent = TEST_USER_AGENTS.IPHONE;
 | 
			
		||||
                agentService = new AgentService(testWindow);
 | 
			
		||||
                expect(agentService.isMobile()).toBeTruthy();
 | 
			
		||||
                expect(agentService.isPhone()).toBeTruthy();
 | 
			
		||||
                expect(agentService.isTablet()).toBeFalsy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("detects iPads", function () {
 | 
			
		||||
                testWindow.navigator.userAgent = TEST_USER_AGENTS.IPAD;
 | 
			
		||||
                agentService = new AgentService(testWindow);
 | 
			
		||||
                expect(agentService.isMobile()).toBeTruthy();
 | 
			
		||||
                expect(agentService.isPhone()).toBeFalsy();
 | 
			
		||||
                expect(agentService.isTablet()).toBeTruthy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("detects display orientation", function () {
 | 
			
		||||
                agentService = new AgentService(testWindow);
 | 
			
		||||
                testWindow.innerWidth = 1024;
 | 
			
		||||
                testWindow.innerHeight = 400;
 | 
			
		||||
                expect(agentService.isPortrait()).toBeFalsy();
 | 
			
		||||
                expect(agentService.isLandscape()).toBeTruthy();
 | 
			
		||||
                testWindow.innerWidth = 400;
 | 
			
		||||
                testWindow.innerHeight = 1024;
 | 
			
		||||
                expect(agentService.isPortrait()).toBeTruthy();
 | 
			
		||||
                expect(agentService.isLandscape()).toBeFalsy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("detects touch support", function () {
 | 
			
		||||
                testWindow.ontouchstart = null;
 | 
			
		||||
                expect(new AgentService(testWindow).isTouch())
 | 
			
		||||
                    .toBe(true);
 | 
			
		||||
                delete testWindow.ontouchstart;
 | 
			
		||||
                expect(new AgentService(testWindow).isTouch())
 | 
			
		||||
                    .toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows for checking browser type", function () {
 | 
			
		||||
                testWindow.navigator.userAgent = "Chromezilla Safarifox";
 | 
			
		||||
                agentService = new AgentService(testWindow);
 | 
			
		||||
                expect(agentService.isBrowser("Chrome")).toBe(true);
 | 
			
		||||
                expect(agentService.isBrowser("Firefox")).toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										109
									
								
								platform/commonUI/mobile/test/DeviceClassifierSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								platform/commonUI/mobile/test/DeviceClassifierSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/DeviceClassifier", "../src/DeviceMatchers"],
 | 
			
		||||
    function (DeviceClassifier, DeviceMatchers) {
 | 
			
		||||
 | 
			
		||||
        var AGENT_SERVICE_METHODS = [
 | 
			
		||||
                'isMobile',
 | 
			
		||||
                'isPhone',
 | 
			
		||||
                'isTablet',
 | 
			
		||||
                'isPortrait',
 | 
			
		||||
                'isLandscape',
 | 
			
		||||
                'isTouch'
 | 
			
		||||
            ],
 | 
			
		||||
            TEST_PERMUTATIONS = [
 | 
			
		||||
                ['isMobile', 'isPhone', 'isTouch', 'isPortrait'],
 | 
			
		||||
                ['isMobile', 'isPhone', 'isTouch', 'isLandscape'],
 | 
			
		||||
                ['isMobile', 'isTablet', 'isTouch', 'isPortrait'],
 | 
			
		||||
                ['isMobile', 'isTablet', 'isTouch', 'isLandscape'],
 | 
			
		||||
                ['isTouch'],
 | 
			
		||||
                []
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
        describe("DeviceClassifier", function () {
 | 
			
		||||
            var mockAgentService,
 | 
			
		||||
                mockDocument,
 | 
			
		||||
                mockBody;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockAgentService = jasmine.createSpyObj(
 | 
			
		||||
                    'agentService',
 | 
			
		||||
                    AGENT_SERVICE_METHODS
 | 
			
		||||
                );
 | 
			
		||||
                mockDocument = jasmine.createSpyObj(
 | 
			
		||||
                    '$document',
 | 
			
		||||
                    ['find']
 | 
			
		||||
                );
 | 
			
		||||
                mockBody = jasmine.createSpyObj(
 | 
			
		||||
                    'body',
 | 
			
		||||
                    ['addClass']
 | 
			
		||||
                );
 | 
			
		||||
                mockDocument.find.and.callFake(function (sel) {
 | 
			
		||||
                    return sel === 'body' && mockBody;
 | 
			
		||||
                });
 | 
			
		||||
                AGENT_SERVICE_METHODS.forEach(function (m) {
 | 
			
		||||
                    mockAgentService[m].and.returnValue(false);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            TEST_PERMUTATIONS.forEach(function (trueMethods) {
 | 
			
		||||
                var summary = trueMethods.length === 0
 | 
			
		||||
                    ? "device has no detected characteristics"
 | 
			
		||||
                    : "device " + (trueMethods.join(", "));
 | 
			
		||||
 | 
			
		||||
                describe("when " + summary, function () {
 | 
			
		||||
                    var classifier; // eslint-disable-line
 | 
			
		||||
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        trueMethods.forEach(function (m) {
 | 
			
		||||
                            mockAgentService[m].and.returnValue(true);
 | 
			
		||||
                        });
 | 
			
		||||
                        classifier = new DeviceClassifier(
 | 
			
		||||
                            mockAgentService,
 | 
			
		||||
                            mockDocument
 | 
			
		||||
                        );
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("adds classes for matching, detected characteristics", function () {
 | 
			
		||||
                        Object.keys(DeviceMatchers).filter(function (m) {
 | 
			
		||||
                            return DeviceMatchers[m](mockAgentService);
 | 
			
		||||
                        }).forEach(function (key) {
 | 
			
		||||
                            expect(mockBody.addClass)
 | 
			
		||||
                                .toHaveBeenCalledWith(key);
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("does not add classes for non-matching characteristics", function () {
 | 
			
		||||
                        Object.keys(DeviceMatchers).filter(function (m) {
 | 
			
		||||
                            return !DeviceMatchers[m](mockAgentService);
 | 
			
		||||
                        }).forEach(function (key) {
 | 
			
		||||
                            expect(mockBody.addClass)
 | 
			
		||||
                                .not.toHaveBeenCalledWith(key);
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										78
									
								
								platform/commonUI/mobile/test/DeviceMatchersSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								platform/commonUI/mobile/test/DeviceMatchersSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/DeviceMatchers"],
 | 
			
		||||
    function (DeviceMatchers) {
 | 
			
		||||
 | 
			
		||||
        describe("DeviceMatchers", function () {
 | 
			
		||||
            var mockAgentService;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockAgentService = jasmine.createSpyObj(
 | 
			
		||||
                    'agentService',
 | 
			
		||||
                    [
 | 
			
		||||
                        'isMobile',
 | 
			
		||||
                        'isPhone',
 | 
			
		||||
                        'isTablet',
 | 
			
		||||
                        'isPortrait',
 | 
			
		||||
                        'isLandscape',
 | 
			
		||||
                        'isTouch'
 | 
			
		||||
                    ]
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("detects when a device is a desktop device", function () {
 | 
			
		||||
                mockAgentService.isMobile.and.returnValue(false);
 | 
			
		||||
                expect(DeviceMatchers.desktop(mockAgentService))
 | 
			
		||||
                    .toBe(true);
 | 
			
		||||
                mockAgentService.isMobile.and.returnValue(true);
 | 
			
		||||
                expect(DeviceMatchers.desktop(mockAgentService))
 | 
			
		||||
                    .toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            function method(deviceType) {
 | 
			
		||||
                return "is" + deviceType[0].toUpperCase() + deviceType.slice(1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            [
 | 
			
		||||
                "mobile",
 | 
			
		||||
                "phone",
 | 
			
		||||
                "tablet",
 | 
			
		||||
                "landscape",
 | 
			
		||||
                "portrait",
 | 
			
		||||
                "landscape",
 | 
			
		||||
                "touch"
 | 
			
		||||
            ].forEach(function (deviceType) {
 | 
			
		||||
                it("detects when a device is a " + deviceType + " device", function () {
 | 
			
		||||
                    mockAgentService[method(deviceType)].and.returnValue(true);
 | 
			
		||||
                    expect(DeviceMatchers[deviceType](mockAgentService))
 | 
			
		||||
                        .toBe(true);
 | 
			
		||||
                    mockAgentService[method(deviceType)].and.returnValue(false);
 | 
			
		||||
                    expect(DeviceMatchers[deviceType](mockAgentService))
 | 
			
		||||
                        .toBe(false);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										168
									
								
								platform/commonUI/mobile/test/MCTDeviceSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								platform/commonUI/mobile/test/MCTDeviceSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['../src/MCTDevice'],
 | 
			
		||||
    function (MCTDevice) {
 | 
			
		||||
 | 
			
		||||
        var JQLITE_METHODS = ['replaceWith'];
 | 
			
		||||
 | 
			
		||||
        describe("The mct-device directive", function () {
 | 
			
		||||
            var mockAgentService,
 | 
			
		||||
                mockTransclude,
 | 
			
		||||
                mockElement,
 | 
			
		||||
                mockClone,
 | 
			
		||||
                testAttrs,
 | 
			
		||||
                directive;
 | 
			
		||||
 | 
			
		||||
            function link() {
 | 
			
		||||
                directive.link(null, mockElement, testAttrs, null, mockTransclude);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockAgentService = jasmine.createSpyObj(
 | 
			
		||||
                    "agentService",
 | 
			
		||||
                    ["isMobile", "isPhone", "isTablet", "isPortrait", "isLandscape"]
 | 
			
		||||
                );
 | 
			
		||||
                mockTransclude = jasmine.createSpy("$transclude");
 | 
			
		||||
                mockElement = jasmine.createSpyObj(name, JQLITE_METHODS);
 | 
			
		||||
                mockClone = jasmine.createSpyObj(name, JQLITE_METHODS);
 | 
			
		||||
 | 
			
		||||
                mockTransclude.and.callFake(function (fn) {
 | 
			
		||||
                    fn(mockClone);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // Look desktop-like by default
 | 
			
		||||
                mockAgentService.isLandscape.and.returnValue(true);
 | 
			
		||||
 | 
			
		||||
                testAttrs = {};
 | 
			
		||||
 | 
			
		||||
                directive = new MCTDevice(mockAgentService);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            function expectInclusion() {
 | 
			
		||||
                expect(mockElement.replaceWith)
 | 
			
		||||
                    .toHaveBeenCalledWith(mockClone);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            function expectExclusion() {
 | 
			
		||||
                expect(mockElement.replaceWith).not.toHaveBeenCalled();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            it("is applicable at the attribute level", function () {
 | 
			
		||||
                expect(directive.restrict).toEqual("A");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("transcludes at the element level", function () {
 | 
			
		||||
                expect(directive.transclude).toEqual('element');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("has a greater priority number than ng-if", function () {
 | 
			
		||||
                expect(directive.priority > 600).toBeTruthy();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("restricts element inclusion for mobile devices", function () {
 | 
			
		||||
                testAttrs.mctDevice = "mobile";
 | 
			
		||||
                link();
 | 
			
		||||
                expectExclusion();
 | 
			
		||||
 | 
			
		||||
                mockAgentService.isMobile.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
                expectInclusion();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("restricts element inclusion for tablet devices", function () {
 | 
			
		||||
                testAttrs.mctDevice = "tablet";
 | 
			
		||||
                mockAgentService.isMobile.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
                expectExclusion();
 | 
			
		||||
 | 
			
		||||
                mockAgentService.isTablet.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
                expectInclusion();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("restricts element inclusion for phone devices", function () {
 | 
			
		||||
                testAttrs.mctDevice = "phone";
 | 
			
		||||
                mockAgentService.isMobile.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
                expectExclusion();
 | 
			
		||||
 | 
			
		||||
                mockAgentService.isPhone.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
                expectInclusion();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("restricts element inclusion for desktop devices", function () {
 | 
			
		||||
                testAttrs.mctDevice = "desktop";
 | 
			
		||||
                mockAgentService.isMobile.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
                expectExclusion();
 | 
			
		||||
 | 
			
		||||
                mockAgentService.isMobile.and.returnValue(false);
 | 
			
		||||
                link();
 | 
			
		||||
                expectInclusion();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("restricts element inclusion for portrait orientation", function () {
 | 
			
		||||
                testAttrs.mctDevice = "portrait";
 | 
			
		||||
                link();
 | 
			
		||||
                expectExclusion();
 | 
			
		||||
 | 
			
		||||
                mockAgentService.isPortrait.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
                expectInclusion();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("restricts element inclusion for landscape orientation", function () {
 | 
			
		||||
                testAttrs.mctDevice = "landscape";
 | 
			
		||||
                mockAgentService.isLandscape.and.returnValue(false);
 | 
			
		||||
                mockAgentService.isPortrait.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
                expectExclusion();
 | 
			
		||||
 | 
			
		||||
                mockAgentService.isLandscape.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
                expectInclusion();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("allows multiple device characteristics to be requested", function () {
 | 
			
		||||
                // Won't try to test every permutation here, just
 | 
			
		||||
                // make sure the multi-characteristic feature has support.
 | 
			
		||||
                testAttrs.mctDevice = "portrait mobile";
 | 
			
		||||
                link();
 | 
			
		||||
                // Neither portrait nor mobile, not called
 | 
			
		||||
                expectExclusion();
 | 
			
		||||
 | 
			
		||||
                mockAgentService.isPortrait.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
 | 
			
		||||
                // Was portrait, but not mobile, so no
 | 
			
		||||
                expectExclusion();
 | 
			
		||||
 | 
			
		||||
                mockAgentService.isMobile.and.returnValue(true);
 | 
			
		||||
                link();
 | 
			
		||||
                expectInclusion();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -379,7 +379,7 @@ define([
 | 
			
		||||
                    {
 | 
			
		||||
                        "name": "Math.uuid.js",
 | 
			
		||||
                        "version": "1.4.7",
 | 
			
		||||
                        "description": "Unique identifier generation (code adapted.)",
 | 
			
		||||
                        "description": "Unique identifer generation (code adapted.)",
 | 
			
		||||
                        "author": "Robert Kieffer",
 | 
			
		||||
                        "website": "https://github.com/broofa/node-uuid",
 | 
			
		||||
                        "copyright": "Copyright (c) 2010-2012 Robert Kieffer",
 | 
			
		||||
 
 | 
			
		||||
@@ -21,24 +21,32 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    "moment-timezone",
 | 
			
		||||
    "./src/indicators/ClockIndicator",
 | 
			
		||||
    "./src/services/TickerService",
 | 
			
		||||
    "./src/services/TimerService",
 | 
			
		||||
    "./src/controllers/ClockController",
 | 
			
		||||
    "./src/controllers/TimerController",
 | 
			
		||||
    "./src/controllers/RefreshingController",
 | 
			
		||||
    "./src/actions/StartTimerAction",
 | 
			
		||||
    "./src/actions/RestartTimerAction",
 | 
			
		||||
    "./src/actions/StopTimerAction",
 | 
			
		||||
    "./src/actions/PauseTimerAction",
 | 
			
		||||
    "./res/templates/clock.html",
 | 
			
		||||
    "./res/templates/timer.html"
 | 
			
		||||
], function (
 | 
			
		||||
    MomentTimezone,
 | 
			
		||||
    ClockIndicator,
 | 
			
		||||
    TickerService,
 | 
			
		||||
    TimerService,
 | 
			
		||||
    ClockController,
 | 
			
		||||
    TimerController,
 | 
			
		||||
    RefreshingController,
 | 
			
		||||
    StartTimerAction,
 | 
			
		||||
    RestartTimerAction,
 | 
			
		||||
    StopTimerAction,
 | 
			
		||||
    PauseTimerAction,
 | 
			
		||||
    clockTemplate,
 | 
			
		||||
    timerTemplate
 | 
			
		||||
) {
 | 
			
		||||
    return {
 | 
			
		||||
@@ -65,6 +73,16 @@ define([
 | 
			
		||||
                        "value": "YYYY/MM/DD HH:mm:ss"
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "indicators": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "implementation": ClockIndicator,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "tickerService",
 | 
			
		||||
                            "CLOCK_INDICATOR_FORMAT"
 | 
			
		||||
                        ],
 | 
			
		||||
                        "priority": "preferred"
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "services": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "tickerService",
 | 
			
		||||
@@ -81,6 +99,14 @@ define([
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "controllers": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "ClockController",
 | 
			
		||||
                        "implementation": ClockController,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "$scope",
 | 
			
		||||
                            "tickerService"
 | 
			
		||||
                        ]
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "TimerController",
 | 
			
		||||
                        "implementation": TimerController,
 | 
			
		||||
@@ -100,6 +126,12 @@ define([
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "views": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "clock",
 | 
			
		||||
                        "type": "clock",
 | 
			
		||||
                        "editable": false,
 | 
			
		||||
                        "template": clockTemplate
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "timer",
 | 
			
		||||
                        "type": "timer",
 | 
			
		||||
@@ -154,6 +186,70 @@ define([
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "types": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "clock",
 | 
			
		||||
                        "name": "Clock",
 | 
			
		||||
                        "cssClass": "icon-clock",
 | 
			
		||||
                        "description": "A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.",
 | 
			
		||||
                        "priority": 101,
 | 
			
		||||
                        "features": [
 | 
			
		||||
                            "creation"
 | 
			
		||||
                        ],
 | 
			
		||||
                        "properties": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "key": "clockFormat",
 | 
			
		||||
                                "name": "Display Format",
 | 
			
		||||
                                "control": "composite",
 | 
			
		||||
                                "items": [
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "control": "select",
 | 
			
		||||
                                        "options": [
 | 
			
		||||
                                            {
 | 
			
		||||
                                                "value": "YYYY/MM/DD hh:mm:ss",
 | 
			
		||||
                                                "name": "YYYY/MM/DD hh:mm:ss"
 | 
			
		||||
                                            },
 | 
			
		||||
                                            {
 | 
			
		||||
                                                "value": "YYYY/DDD hh:mm:ss",
 | 
			
		||||
                                                "name": "YYYY/DDD hh:mm:ss"
 | 
			
		||||
                                            },
 | 
			
		||||
                                            {
 | 
			
		||||
                                                "value": "hh:mm:ss",
 | 
			
		||||
                                                "name": "hh:mm:ss"
 | 
			
		||||
                                            }
 | 
			
		||||
                                        ],
 | 
			
		||||
                                        "cssClass": "l-inline"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "control": "select",
 | 
			
		||||
                                        "options": [
 | 
			
		||||
                                            {
 | 
			
		||||
                                                "value": "clock12",
 | 
			
		||||
                                                "name": "12hr"
 | 
			
		||||
                                            },
 | 
			
		||||
                                            {
 | 
			
		||||
                                                "value": "clock24",
 | 
			
		||||
                                                "name": "24hr"
 | 
			
		||||
                                            }
 | 
			
		||||
                                        ],
 | 
			
		||||
                                        "cssClass": "l-inline"
 | 
			
		||||
                                    }
 | 
			
		||||
                                ]
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "key": "timezone",
 | 
			
		||||
                                "name": "Timezone",
 | 
			
		||||
                                "control": "autocomplete",
 | 
			
		||||
                                "options": MomentTimezone.tz.names()
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "model": {
 | 
			
		||||
                            "clockFormat": [
 | 
			
		||||
                                "YYYY/MM/DD hh:mm:ss",
 | 
			
		||||
                                "clock12"
 | 
			
		||||
                            ],
 | 
			
		||||
                            "timezone": "UTC"
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "timer",
 | 
			
		||||
                        "name": "Timer",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								platform/features/clock/res/templates/clock.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								platform/features/clock/res/templates/clock.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
<!--
 | 
			
		||||
 Open MCT, Copyright (c) 2009-2016, United States Government
 | 
			
		||||
 as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 Administration. All rights reserved.
 | 
			
		||||
 | 
			
		||||
 Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 You may obtain a copy of the License at
 | 
			
		||||
 http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 | 
			
		||||
 Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 License for the specific language governing permissions and limitations
 | 
			
		||||
 under the License.
 | 
			
		||||
 | 
			
		||||
 Open MCT includes source code licensed under additional open source
 | 
			
		||||
 licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
<div class="c-clock l-time-display u-style-receiver js-style-receiver" ng-controller="ClockController as clock">
 | 
			
		||||
	<div class="c-clock__timezone">
 | 
			
		||||
		{{clock.zone()}}
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="c-clock__value">
 | 
			
		||||
		{{clock.text()}}
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="c-clock__ampm">
 | 
			
		||||
		{{clock.ampm()}}
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										110
									
								
								platform/features/clock/src/controllers/ClockController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								platform/features/clock/src/controllers/ClockController.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2016, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    'moment',
 | 
			
		||||
    'moment-timezone'
 | 
			
		||||
],
 | 
			
		||||
function (
 | 
			
		||||
    moment,
 | 
			
		||||
    momentTimezone
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
         * Controller for views of a Clock domain object.
 | 
			
		||||
         *
 | 
			
		||||
         * @constructor
 | 
			
		||||
         * @memberof platform/features/clock
 | 
			
		||||
         * @param {angular.Scope} $scope the Angular scope
 | 
			
		||||
         * @param {platform/features/clock.TickerService} tickerService
 | 
			
		||||
         *        a service used to align behavior with clock ticks
 | 
			
		||||
         */
 | 
			
		||||
    function ClockController($scope, tickerService) {
 | 
			
		||||
        var lastTimestamp,
 | 
			
		||||
            unlisten,
 | 
			
		||||
            timeFormat,
 | 
			
		||||
            zoneName,
 | 
			
		||||
            self = this;
 | 
			
		||||
 | 
			
		||||
        function update() {
 | 
			
		||||
            var m = zoneName
 | 
			
		||||
                ? moment.utc(lastTimestamp).tz(zoneName) : moment.utc(lastTimestamp);
 | 
			
		||||
            self.zoneAbbr = m.zoneAbbr();
 | 
			
		||||
            self.textValue = timeFormat && m.format(timeFormat);
 | 
			
		||||
            self.ampmValue = m.format("A"); // Just the AM or PM part
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function tick(timestamp) {
 | 
			
		||||
            lastTimestamp = timestamp;
 | 
			
		||||
            update();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function updateModel(model) {
 | 
			
		||||
            var baseFormat;
 | 
			
		||||
            if (model !== undefined) {
 | 
			
		||||
                baseFormat = model.clockFormat[0];
 | 
			
		||||
 | 
			
		||||
                self.use24 = model.clockFormat[1] === 'clock24';
 | 
			
		||||
                timeFormat = self.use24
 | 
			
		||||
                    ? baseFormat.replace('hh', "HH") : baseFormat;
 | 
			
		||||
                // If wrong timezone is provided, the UTC will be used
 | 
			
		||||
                zoneName = momentTimezone.tz.names().includes(model.timezone)
 | 
			
		||||
                    ? model.timezone : "UTC";
 | 
			
		||||
                update();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Pull in the model (clockFormat and timezone) from the domain object model
 | 
			
		||||
        $scope.$watch('model', updateModel);
 | 
			
		||||
 | 
			
		||||
        // Listen for clock ticks ... and stop listening on destroy
 | 
			
		||||
        unlisten = tickerService.listen(tick);
 | 
			
		||||
        $scope.$on('$destroy', unlisten);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
         * Get the clock's time zone, as displayable text.
 | 
			
		||||
         * @returns {string}
 | 
			
		||||
         */
 | 
			
		||||
    ClockController.prototype.zone = function () {
 | 
			
		||||
        return this.zoneAbbr;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
         * Get the current time, as displayable text.
 | 
			
		||||
         * @returns {string}
 | 
			
		||||
         */
 | 
			
		||||
    ClockController.prototype.text = function () {
 | 
			
		||||
        return this.textValue;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
         * Get the text to display to qualify a time as AM or PM.
 | 
			
		||||
         * @returns {string}
 | 
			
		||||
         */
 | 
			
		||||
    ClockController.prototype.ampm = function () {
 | 
			
		||||
        return this.use24 ? '' : this.ampmValue;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return ClockController;
 | 
			
		||||
}
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										65
									
								
								platform/features/clock/src/indicators/ClockIndicator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								platform/features/clock/src/indicators/ClockIndicator.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2016, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['moment'],
 | 
			
		||||
    function (moment) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Indicator that displays the current UTC time in the status area.
 | 
			
		||||
         * @implements {Indicator}
 | 
			
		||||
         * @memberof platform/features/clock
 | 
			
		||||
         * @param {platform/features/clock.TickerService} tickerService
 | 
			
		||||
         *        a service used to align behavior with clock ticks
 | 
			
		||||
         * @param {string} indicatorFormat format string for timestamps
 | 
			
		||||
         *        shown in this indicator
 | 
			
		||||
         */
 | 
			
		||||
        function ClockIndicator(tickerService, indicatorFormat) {
 | 
			
		||||
            var self = this;
 | 
			
		||||
 | 
			
		||||
            this.text = "";
 | 
			
		||||
 | 
			
		||||
            tickerService.listen(function (timestamp) {
 | 
			
		||||
                self.text = moment.utc(timestamp)
 | 
			
		||||
                    .format(indicatorFormat) + " UTC";
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ClockIndicator.prototype.getGlyphClass = function () {
 | 
			
		||||
            return "";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ClockIndicator.prototype.getCssClass = function () {
 | 
			
		||||
            return "t-indicator-clock icon-clock no-minify c-indicator--not-clickable";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ClockIndicator.prototype.getText = function () {
 | 
			
		||||
            return this.text;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ClockIndicator.prototype.getDescription = function () {
 | 
			
		||||
            return "";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return ClockIndicator;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										107
									
								
								platform/features/clock/test/controllers/ClockControllerSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								platform/features/clock/test/controllers/ClockControllerSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2017, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/controllers/ClockController"],
 | 
			
		||||
    function (ClockController) {
 | 
			
		||||
 | 
			
		||||
        // Wed, 03 Jun 2015 17:56:14 GMT
 | 
			
		||||
        var TEST_TIMESTAMP = 1433354174000;
 | 
			
		||||
 | 
			
		||||
        describe("A clock view's controller", function () {
 | 
			
		||||
            var mockScope,
 | 
			
		||||
                mockTicker,
 | 
			
		||||
                mockUnticker,
 | 
			
		||||
                controller;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']);
 | 
			
		||||
                mockTicker = jasmine.createSpyObj('ticker', ['listen']);
 | 
			
		||||
                mockUnticker = jasmine.createSpy('unticker');
 | 
			
		||||
 | 
			
		||||
                mockTicker.listen.and.returnValue(mockUnticker);
 | 
			
		||||
 | 
			
		||||
                controller = new ClockController(mockScope, mockTicker);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("watches for model (clockFormat and timezone) from the domain object model", function () {
 | 
			
		||||
                expect(mockScope.$watch).toHaveBeenCalledWith(
 | 
			
		||||
                    "model",
 | 
			
		||||
                    jasmine.any(Function)
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("subscribes to clock ticks", function () {
 | 
			
		||||
                expect(mockTicker.listen)
 | 
			
		||||
                    .toHaveBeenCalledWith(jasmine.any(Function));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("unsubscribes to ticks when destroyed", function () {
 | 
			
		||||
                // Make sure $destroy is being listened for...
 | 
			
		||||
                expect(mockScope.$on.calls.mostRecent().args[0]).toEqual('$destroy');
 | 
			
		||||
                expect(mockUnticker).not.toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                // ...and makes sure that its listener unsubscribes from ticker
 | 
			
		||||
                mockScope.$on.calls.mostRecent().args[1]();
 | 
			
		||||
                expect(mockUnticker).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("formats using the format string from the model", function () {
 | 
			
		||||
                mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
 | 
			
		||||
                mockScope.$watch.calls.mostRecent().args[1]({
 | 
			
		||||
                    "clockFormat": [
 | 
			
		||||
                        "YYYY-DDD hh:mm:ss",
 | 
			
		||||
                        "clock24"
 | 
			
		||||
                    ],
 | 
			
		||||
                    "timezone": "Canada/Eastern"
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                expect(controller.zone()).toEqual("EDT");
 | 
			
		||||
                expect(controller.text()).toEqual("2015-154 13:56:14");
 | 
			
		||||
                expect(controller.ampm()).toEqual("");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("formats 12-hour time", function () {
 | 
			
		||||
                mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
 | 
			
		||||
                mockScope.$watch.calls.mostRecent().args[1]({
 | 
			
		||||
                    "clockFormat": [
 | 
			
		||||
                        "YYYY-DDD hh:mm:ss",
 | 
			
		||||
                        "clock12"
 | 
			
		||||
                    ],
 | 
			
		||||
                    "timezone": ""
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                expect(controller.zone()).toEqual("UTC");
 | 
			
		||||
                expect(controller.text()).toEqual("2015-154 05:56:14");
 | 
			
		||||
                expect(controller.ampm()).toEqual("PM");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not throw exceptions when model is undefined", function () {
 | 
			
		||||
                mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
 | 
			
		||||
                expect(function () {
 | 
			
		||||
                    mockScope.$watch.calls.mostRecent().args[1](undefined);
 | 
			
		||||
                }).not.toThrow();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2009-2016, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/indicators/ClockIndicator"],
 | 
			
		||||
    function (ClockIndicator) {
 | 
			
		||||
 | 
			
		||||
        // Wed, 03 Jun 2015 17:56:14 GMT
 | 
			
		||||
        var TEST_TIMESTAMP = 1433354174000,
 | 
			
		||||
            TEST_FORMAT = "YYYY-DDD HH:mm:ss";
 | 
			
		||||
 | 
			
		||||
        describe("The clock indicator", function () {
 | 
			
		||||
            var mockTicker,
 | 
			
		||||
                mockUnticker,
 | 
			
		||||
                indicator;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockTicker = jasmine.createSpyObj('ticker', ['listen']);
 | 
			
		||||
                mockUnticker = jasmine.createSpy('unticker');
 | 
			
		||||
 | 
			
		||||
                mockTicker.listen.and.returnValue(mockUnticker);
 | 
			
		||||
 | 
			
		||||
                indicator = new ClockIndicator(mockTicker, TEST_FORMAT);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("displays the current time", function () {
 | 
			
		||||
                mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP);
 | 
			
		||||
                expect(indicator.getText()).toEqual("2015-154 17:56:14 UTC");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("implements the Indicator interface", function () {
 | 
			
		||||
                expect(indicator.getCssClass()).toEqual(jasmine.any(String));
 | 
			
		||||
                expect(indicator.getText()).toEqual(jasmine.any(String));
 | 
			
		||||
                expect(indicator.getDescription()).toEqual(jasmine.any(String));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -30,8 +30,8 @@ define([
 | 
			
		||||
 | 
			
		||||
    return function ImportExportPlugin() {
 | 
			
		||||
        return function (openmct) {
 | 
			
		||||
            ExportAsJSONAction.prototype.appliesTo = function (context) {
 | 
			
		||||
                return this.openmct.$injector.get('policyService')
 | 
			
		||||
            ExportAsJSONAction.appliesTo = function (context) {
 | 
			
		||||
                return openmct.$injector.get('policyService')
 | 
			
		||||
                    .allow("creation", context.domainObject.getCapability("type")
 | 
			
		||||
                    );
 | 
			
		||||
            };
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ define(
 | 
			
		||||
    ],
 | 
			
		||||
    function (ExportAsJSONAction, domainObjectFactory, MCT, AdapterCapability) {
 | 
			
		||||
 | 
			
		||||
        describe("The export JSON action", function () {
 | 
			
		||||
        xdescribe("The export JSON action", function () {
 | 
			
		||||
 | 
			
		||||
            var context,
 | 
			
		||||
                action,
 | 
			
		||||
@@ -102,7 +102,7 @@ define(
 | 
			
		||||
                expect(action).toBeDefined();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            xit("doesn't export non-creatable objects in tree", function () {
 | 
			
		||||
            it("doesn't export non-creatable objects in tree", function () {
 | 
			
		||||
                var nonCreatableType = {
 | 
			
		||||
                    hasFeature:
 | 
			
		||||
                        function (feature) {
 | 
			
		||||
@@ -149,7 +149,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            xit("can export self-containing objects", function () {
 | 
			
		||||
            it("can export self-containing objects", function () {
 | 
			
		||||
                var parent = domainObjectFactory({
 | 
			
		||||
                    name: 'parent',
 | 
			
		||||
                    model: {
 | 
			
		||||
@@ -191,7 +191,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            xit("exports links to external objects as new objects", function () {
 | 
			
		||||
            it("exports links to external objects as new objects", function () {
 | 
			
		||||
                var parent = domainObjectFactory({
 | 
			
		||||
                    name: 'parent',
 | 
			
		||||
                    model: {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ define(
 | 
			
		||||
    ],
 | 
			
		||||
    function (ImportAsJSONAction, domainObjectFactory) {
 | 
			
		||||
 | 
			
		||||
        describe("The import JSON action", function () {
 | 
			
		||||
        xdescribe("The import JSON action", function () {
 | 
			
		||||
 | 
			
		||||
            var context = {};
 | 
			
		||||
            var action,
 | 
			
		||||
@@ -146,7 +146,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            xit("can import self-containing objects", function () {
 | 
			
		||||
            it("can import self-containing objects", function () {
 | 
			
		||||
                var compDomainObject = domainObjectFactory({
 | 
			
		||||
                    name: 'compObject',
 | 
			
		||||
                    model: { name: 'compObject'},
 | 
			
		||||
@@ -198,7 +198,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            xit("assigns new ids to each imported object", function () {
 | 
			
		||||
            it("assigns new ids to each imported object", function () {
 | 
			
		||||
                dialogService.getUserInput.and.returnValue(Promise.resolve(
 | 
			
		||||
                    {
 | 
			
		||||
                        selectFile: {
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ define(
 | 
			
		||||
         * @param $interval Angular's $interval service
 | 
			
		||||
         * @param {string} space the name of the persistence space being served
 | 
			
		||||
         * @param {string} root the root of the path to ElasticSearch
 | 
			
		||||
         * @param {string} path the path to domain objects within ElasticSearch
 | 
			
		||||
         * @param {stirng} path the path to domain objects within ElasticSearch
 | 
			
		||||
         */
 | 
			
		||||
        function ElasticPersistenceProvider($http, $q, space, root, path) {
 | 
			
		||||
            this.spaces = [space];
 | 
			
		||||
 
 | 
			
		||||
@@ -136,7 +136,7 @@ define([
 | 
			
		||||
         * @memberof module:openmct.MCT#
 | 
			
		||||
         * @name conductor
 | 
			
		||||
         */
 | 
			
		||||
        this.time = new api.TimeAPI(this);
 | 
			
		||||
        this.time = new api.TimeAPI();
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * An interface for interacting with the composition of domain objects.
 | 
			
		||||
@@ -287,7 +287,6 @@ define([
 | 
			
		||||
        this.install(this.plugins.ViewLargeAction());
 | 
			
		||||
        this.install(this.plugins.ObjectInterceptors());
 | 
			
		||||
        this.install(this.plugins.NonEditableFolder());
 | 
			
		||||
        this.install(this.plugins.DeviceClassifier());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MCT.prototype = Object.create(EventEmitter.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,8 @@ export default function LegacyActionAdapter(openmct, legacyActions) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        console.warn(`DEPRECATION WARNING: Action ${action.definition.key} in bundle ${action.bundle.path} is non-contextual and should be migrated.`);
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ define([
 | 
			
		||||
    './capabilities/APICapabilityDecorator',
 | 
			
		||||
    './policies/AdaptedViewPolicy',
 | 
			
		||||
    './runs/AlternateCompositionInitializer',
 | 
			
		||||
    './runs/TypeDeprecationChecker',
 | 
			
		||||
    './runs/LegacyTelemetryProvider',
 | 
			
		||||
    './runs/RegisterLegacyTypes',
 | 
			
		||||
    './services/LegacyObjectAPIInterceptor',
 | 
			
		||||
@@ -45,6 +46,7 @@ define([
 | 
			
		||||
    APICapabilityDecorator,
 | 
			
		||||
    AdaptedViewPolicy,
 | 
			
		||||
    AlternateCompositionInitializer,
 | 
			
		||||
    TypeDeprecationChecker,
 | 
			
		||||
    LegacyTelemetryProvider,
 | 
			
		||||
    RegisterLegacyTypes,
 | 
			
		||||
    LegacyObjectAPIInterceptor,
 | 
			
		||||
@@ -133,6 +135,10 @@ define([
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                runs: [
 | 
			
		||||
                    {
 | 
			
		||||
                        implementation: TypeDeprecationChecker,
 | 
			
		||||
                        depends: ["types[]"]
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        implementation: AlternateCompositionInitializer,
 | 
			
		||||
                        depends: ["openmct"]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,12 @@ define([
 | 
			
		||||
 | 
			
		||||
) {
 | 
			
		||||
    function RegisterLegacyTypes(types, openmct) {
 | 
			
		||||
        types.forEach(function (legacyDefinition) {
 | 
			
		||||
            if (!openmct.types.get(legacyDefinition.key)) {
 | 
			
		||||
                console.warn(`DEPRECATION WARNING: Migrate type ${legacyDefinition.key} from ${legacyDefinition.bundle.path} to use the new Types API.  Legacy type support will be removed soon.`);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        openmct.types.importLegacyTypes(types);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * Open openmct, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * Open openmct is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
@@ -14,27 +14,33 @@
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * Open openmct includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
export default function timelineInterceptor(openmct) {
 | 
			
		||||
define([
 | 
			
		||||
 | 
			
		||||
    openmct.objects.addGetInterceptor({
 | 
			
		||||
        appliesTo: (identifier, domainObject) => {
 | 
			
		||||
            return domainObject && domainObject.type === 'time-strip';
 | 
			
		||||
        },
 | 
			
		||||
        invoke: (identifier, object) => {
 | 
			
		||||
], function (
 | 
			
		||||
 | 
			
		||||
            if (object && object.configuration === undefined) {
 | 
			
		||||
                object.configuration = {
 | 
			
		||||
                    useIndependentTime: true
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
            return object;
 | 
			
		||||
    function checkForDeprecatedFunctionality(typeDef) {
 | 
			
		||||
        if (Object.prototype.hasOwnProperty.call(typeDef, 'telemetry')) {
 | 
			
		||||
            console.warn(
 | 
			
		||||
                'DEPRECATION WARNING: Telemetry data on type '
 | 
			
		||||
                + 'registrations will be deprecated in a future version, '
 | 
			
		||||
                + 'please convert to a custom telemetry metadata provider '
 | 
			
		||||
                + 'for type: ' + typeDef.key
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function TypeDeprecationChecker(types) {
 | 
			
		||||
        types.forEach(checkForDeprecatedFunctionality);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return TypeDeprecationChecker;
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
@@ -15,6 +15,8 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    function LegacyViewProvider(legacyView, openmct, convertToLegacyObject) {
 | 
			
		||||
        console.warn(`DEPRECATION WARNING: Migrate ${legacyView.key} from ${legacyView.bundle.path} to use the new View APIs.  Legacy view support will be removed soon.`);
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            key: legacyView.key,
 | 
			
		||||
            name: legacyView.name,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ define([
 | 
			
		||||
 | 
			
		||||
) {
 | 
			
		||||
    function TypeInspectorViewProvider(typeDefinition, openmct, convertToLegacyObject) {
 | 
			
		||||
        console.warn(`DEPRECATION WARNING: Migrate ${typeDefinition.key} from ${typeDefinition.bundle.path} to use the new Inspector View APIs.  Legacy Inspector view support will be removed soon.`);
 | 
			
		||||
        let representation = openmct.$injector.get('representations[]')
 | 
			
		||||
            .filter((r) => r.key === typeDefinition.inspector)[0];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,9 @@ class ActionsAPI extends EventEmitter {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getCachedActionCollection(objectPath, view) {
 | 
			
		||||
        return this._actionCollections.get(view);
 | 
			
		||||
        let cachedActionCollection = this._actionCollections.get(view);
 | 
			
		||||
 | 
			
		||||
        return cachedActionCollection;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _newActionCollection(objectPath, view, skipEnvironmentObservers) {
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@ define([
 | 
			
		||||
    StatusAPI
 | 
			
		||||
) {
 | 
			
		||||
    return {
 | 
			
		||||
        TimeAPI: TimeAPI.default,
 | 
			
		||||
        TimeAPI: TimeAPI,
 | 
			
		||||
        ObjectAPI: ObjectAPI,
 | 
			
		||||
        CompositionAPI: CompositionAPI,
 | 
			
		||||
        TypeRegistry: TypeRegistry,
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ import EventEmitter from 'EventEmitter';
 | 
			
		||||
 *
 | 
			
		||||
 * @typedef {object} NotificationModel
 | 
			
		||||
 * @property {string} message The message to be displayed by the notification
 | 
			
		||||
 * @property {number | 'unknown'} [progress] The progress of some ongoing task. Should be a number between 0 and 100, or
 | 
			
		||||
 * @property {number | 'unknown'} [progress] The progres of some ongoing task. Should be a number between 0 and 100, or
 | 
			
		||||
 * with the string literal 'unknown'.
 | 
			
		||||
 * @property {string} [progressText] A message conveying progress of some ongoing task.
 | 
			
		||||
 | 
			
		||||
@@ -98,7 +98,7 @@ export default class NotificationAPI extends EventEmitter {
 | 
			
		||||
     * Present an alert to the user.
 | 
			
		||||
     * @param {string} message The message to display to the user.
 | 
			
		||||
     * @param {Object} [options] object with following properties
 | 
			
		||||
     *      autoDismissTimeout: {number} in milliseconds to automatically dismisses notification
 | 
			
		||||
     *      autoDismissTimeout: {number} in miliseconds to automatically dismisses notification
 | 
			
		||||
     *      link: {Object} Add a link to notifications for navigation
 | 
			
		||||
     *              onClick: callback function
 | 
			
		||||
     *              cssClass: css class name to add style on link
 | 
			
		||||
@@ -119,7 +119,7 @@ export default class NotificationAPI extends EventEmitter {
 | 
			
		||||
     * Present an error message to the user
 | 
			
		||||
     * @param {string} message
 | 
			
		||||
     * @param {Object} [options] object with following properties
 | 
			
		||||
     *      autoDismissTimeout: {number} in milliseconds to automatically dismisses notification
 | 
			
		||||
     *      autoDismissTimeout: {number} in miliseconds to automatically dismisses notification
 | 
			
		||||
     *      link: {Object} Add a link to notifications for navigation
 | 
			
		||||
     *              onClick: callback function
 | 
			
		||||
     *              cssClass: css class name to add style on link
 | 
			
		||||
 
 | 
			
		||||
@@ -182,12 +182,6 @@ ObjectAPI.prototype.get = function (identifier, abortSignal) {
 | 
			
		||||
    let objectPromise = provider.get(identifier, abortSignal).then(result => {
 | 
			
		||||
        delete this.cache[keystring];
 | 
			
		||||
        result = this.applyGetInterceptors(identifier, result);
 | 
			
		||||
        if (result.isMutable) {
 | 
			
		||||
            result.$refresh(result);
 | 
			
		||||
        } else {
 | 
			
		||||
            let mutableDomainObject = this._toMutable(result);
 | 
			
		||||
            mutableDomainObject.$refresh(result);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    });
 | 
			
		||||
@@ -304,15 +298,10 @@ ObjectAPI.prototype.save = function (domainObject) {
 | 
			
		||||
                savedResolve = resolve;
 | 
			
		||||
            });
 | 
			
		||||
            domainObject.persisted = persistedTime;
 | 
			
		||||
            const newObjectPromise = provider.create(domainObject);
 | 
			
		||||
            if (newObjectPromise) {
 | 
			
		||||
                newObjectPromise.then(response => {
 | 
			
		||||
                    this.mutate(domainObject, 'persisted', persistedTime);
 | 
			
		||||
                    savedResolve(response);
 | 
			
		||||
                });
 | 
			
		||||
            } else {
 | 
			
		||||
                result = Promise.reject(`[ObjectAPI][save] Object provider returned ${newObjectPromise} when creating new object.`);
 | 
			
		||||
            }
 | 
			
		||||
            provider.create(domainObject).then((response) => {
 | 
			
		||||
                this.mutate(domainObject, 'persisted', persistedTime);
 | 
			
		||||
                savedResolve(response);
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            domainObject.persisted = persistedTime;
 | 
			
		||||
            this.mutate(domainObject, 'persisted', persistedTime);
 | 
			
		||||
@@ -369,20 +358,6 @@ ObjectAPI.prototype.applyGetInterceptors = function (identifier, domainObject) {
 | 
			
		||||
    return domainObject;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Return relative url path from a given object path
 | 
			
		||||
 * eg: #/browse/mine/cb56f6bf-c900-43b7-b923-2e3b64b412db/6e89e858-77ce-46e4-a1ad-749240286497/....
 | 
			
		||||
 * @param {Array} objectPath
 | 
			
		||||
 * @returns {string} relative url for object
 | 
			
		||||
 */
 | 
			
		||||
ObjectAPI.prototype.getRelativePath = function (objectPath) {
 | 
			
		||||
    return objectPath
 | 
			
		||||
        .map(p => this.makeKeyString(p.identifier))
 | 
			
		||||
        .reverse()
 | 
			
		||||
        .join('/')
 | 
			
		||||
    ;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Modify a domain object.
 | 
			
		||||
 * @param {module:openmct.DomainObject} object the object to mutate
 | 
			
		||||
 
 | 
			
		||||
@@ -10,37 +10,28 @@ const cssClasses = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Overlay extends EventEmitter {
 | 
			
		||||
    constructor({
 | 
			
		||||
        buttons,
 | 
			
		||||
        autoHide = true,
 | 
			
		||||
        dismissable = true,
 | 
			
		||||
        element,
 | 
			
		||||
        onDestroy,
 | 
			
		||||
        size
 | 
			
		||||
    } = {}) {
 | 
			
		||||
    constructor(options) {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        this.dismissable = options.dismissable !== false;
 | 
			
		||||
        this.container = document.createElement('div');
 | 
			
		||||
        this.container.classList.add('l-overlay-wrapper', cssClasses[size]);
 | 
			
		||||
 | 
			
		||||
        this.autoHide = autoHide;
 | 
			
		||||
        this.dismissable = dismissable !== false;
 | 
			
		||||
        this.container.classList.add('l-overlay-wrapper', cssClasses[options.size]);
 | 
			
		||||
 | 
			
		||||
        this.component = new Vue({
 | 
			
		||||
            components: {
 | 
			
		||||
                OverlayComponent: OverlayComponent
 | 
			
		||||
            },
 | 
			
		||||
            provide: {
 | 
			
		||||
                dismiss: this.dismiss.bind(this),
 | 
			
		||||
                element,
 | 
			
		||||
                buttons,
 | 
			
		||||
                element: options.element,
 | 
			
		||||
                buttons: options.buttons,
 | 
			
		||||
                dismissable: this.dismissable
 | 
			
		||||
            },
 | 
			
		||||
            components: {
 | 
			
		||||
                OverlayComponent: OverlayComponent
 | 
			
		||||
            },
 | 
			
		||||
            template: '<overlay-component></overlay-component>'
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (onDestroy) {
 | 
			
		||||
            this.once('destroy', onDestroy);
 | 
			
		||||
        if (options.onDestroy) {
 | 
			
		||||
            this.once('destroy', options.onDestroy);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,10 +30,7 @@ class OverlayAPI {
 | 
			
		||||
     */
 | 
			
		||||
    showOverlay(overlay) {
 | 
			
		||||
        if (this.activeOverlays.length) {
 | 
			
		||||
            const previousOverlay = this.activeOverlays[this.activeOverlays.length - 1];
 | 
			
		||||
            if (previousOverlay.autoHide) {
 | 
			
		||||
                previousOverlay.container.classList.add('invisible');
 | 
			
		||||
            }
 | 
			
		||||
            this.activeOverlays[this.activeOverlays.length - 1].container.classList.add('invisible');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.activeOverlays.push(overlay);
 | 
			
		||||
@@ -63,7 +60,7 @@ class OverlayAPI {
 | 
			
		||||
     * A description of option properties that can be passed into the overlay
 | 
			
		||||
     * @typedef options
 | 
			
		||||
        * @property {object} element DOMElement that is to be inserted/shown on the overlay
 | 
			
		||||
        * @property {string} size preferred size of the overlay (large, small, fit)
 | 
			
		||||
        * @property {string} size prefered size of the overlay (large, small, fit)
 | 
			
		||||
        * @property {array} buttons optional button objects with label and callback properties
 | 
			
		||||
        * @property {function} onDestroy callback to be called when overlay is destroyed
 | 
			
		||||
        * @property {boolean} dismissable allow user to dismiss overlay by using esc, and clicking away
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@
 | 
			
		||||
        ></button>
 | 
			
		||||
        <div
 | 
			
		||||
            ref="element"
 | 
			
		||||
            class="c-overlay__contents js-notebook-snapshot-item-wrapper"
 | 
			
		||||
            class="c-overlay__contents"
 | 
			
		||||
            tabindex="0"
 | 
			
		||||
        ></div>
 | 
			
		||||
        <div
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,6 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
const { TelemetryCollection } = require("./TelemetryCollection");
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    '../../plugins/displayLayout/CustomStringFormatter',
 | 
			
		||||
    './TelemetryMetadataManager',
 | 
			
		||||
@@ -180,6 +178,12 @@ define([
 | 
			
		||||
     * @memberof module:openmct.TelemetryAPI~TelemetryProvider#
 | 
			
		||||
     */
 | 
			
		||||
    TelemetryAPI.prototype.canProvideTelemetry = function (domainObject) {
 | 
			
		||||
        console.warn(
 | 
			
		||||
            'DEPRECATION WARNING: openmct.telemetry.canProvideTelemetry '
 | 
			
		||||
            + 'will not be supported in future versions of Open MCT.  Please '
 | 
			
		||||
            + 'use openmct.telemetry.isTelemetryObject instead.'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return Boolean(this.findSubscriptionProvider(domainObject))
 | 
			
		||||
               || Boolean(this.findRequestProvider(domainObject));
 | 
			
		||||
    };
 | 
			
		||||
@@ -269,28 +273,6 @@ define([
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Request telemetry collection for a domain object.
 | 
			
		||||
     * The `options` argument allows you to specify filters
 | 
			
		||||
     * (start, end, etc.), sort order, and strategies for retrieving
 | 
			
		||||
     * telemetry (aggregation, latest available, etc.).
 | 
			
		||||
     *
 | 
			
		||||
     * @method requestCollection
 | 
			
		||||
     * @memberof module:openmct.TelemetryAPI~TelemetryProvider#
 | 
			
		||||
     * @param {module:openmct.DomainObject} domainObject the object
 | 
			
		||||
     *        which has associated telemetry
 | 
			
		||||
     * @param {module:openmct.TelemetryAPI~TelemetryRequest} options
 | 
			
		||||
     *        options for this telemetry collection request
 | 
			
		||||
     * @returns {TelemetryCollection} a TelemetryCollection instance
 | 
			
		||||
     */
 | 
			
		||||
    TelemetryAPI.prototype.requestCollection = function (domainObject, options = {}) {
 | 
			
		||||
        return new TelemetryCollection(
 | 
			
		||||
            this.openmct,
 | 
			
		||||
            domainObject,
 | 
			
		||||
            options
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Request historical telemetry for a domain object.
 | 
			
		||||
     * The `options` argument allows you to specify filters
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,396 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2020, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import EventEmitter from 'EventEmitter';
 | 
			
		||||
 | 
			
		||||
const ERRORS = {
 | 
			
		||||
    TIMESYSTEM_KEY: 'All telemetry metadata must have a telemetry value with a key that matches the key of the active time system.',
 | 
			
		||||
    LOADED: 'Telemetry Collection has already been loaded.'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** Class representing a Telemetry Collection. */
 | 
			
		||||
 | 
			
		||||
export class TelemetryCollection extends EventEmitter {
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a Telemetry Collection
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {object} openmct - Openm MCT
 | 
			
		||||
     * @param  {object} domainObject - Domain Object to user for telemetry collection
 | 
			
		||||
     * @param  {object} options - Any options passed in for request/subscribe
 | 
			
		||||
     */
 | 
			
		||||
    constructor(openmct, domainObject, options) {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        this.loaded = false;
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.domainObject = domainObject;
 | 
			
		||||
        this.boundedTelemetry = [];
 | 
			
		||||
        this.futureBuffer = [];
 | 
			
		||||
        this.parseTime = undefined;
 | 
			
		||||
        this.metadata = this.openmct.telemetry.getMetadata(domainObject);
 | 
			
		||||
        this.unsubscribe = undefined;
 | 
			
		||||
        this.historicalProvider = undefined;
 | 
			
		||||
        this.options = options;
 | 
			
		||||
        this.pageState = undefined;
 | 
			
		||||
        this.lastBounds = undefined;
 | 
			
		||||
        this.requestAbort = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This will start the requests for historical and realtime data,
 | 
			
		||||
     * as well as setting up initial values and watchers
 | 
			
		||||
     */
 | 
			
		||||
    load() {
 | 
			
		||||
        if (this.loaded) {
 | 
			
		||||
            this._error(ERRORS.LOADED);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._timeSystem(this.openmct.time.timeSystem());
 | 
			
		||||
        this.lastBounds = this.openmct.time.bounds();
 | 
			
		||||
 | 
			
		||||
        this._watchBounds();
 | 
			
		||||
        this._watchTimeSystem();
 | 
			
		||||
 | 
			
		||||
        this._initiateHistoricalRequests();
 | 
			
		||||
        this._initiateSubscriptionTelemetry();
 | 
			
		||||
 | 
			
		||||
        this.loaded = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * can/should be called by the requester of the telemetry collection
 | 
			
		||||
     * to remove any listeners
 | 
			
		||||
     */
 | 
			
		||||
    destroy() {
 | 
			
		||||
        if (this.requestAbort) {
 | 
			
		||||
            this.requestAbort.abort();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._unwatchBounds();
 | 
			
		||||
        this._unwatchTimeSystem();
 | 
			
		||||
        if (this.unsubscribe) {
 | 
			
		||||
            this.unsubscribe();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.removeAllListeners();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This will start the requests for historical and realtime data,
 | 
			
		||||
     * as well as setting up initial values and watchers
 | 
			
		||||
     */
 | 
			
		||||
    getAll() {
 | 
			
		||||
        return this.boundedTelemetry;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets up  the telemetry collection for historical requests,
 | 
			
		||||
     * this uses the "standardizeRequestOptions" from Telemetry API
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _initiateHistoricalRequests() {
 | 
			
		||||
        this.openmct.telemetry.standardizeRequestOptions(this.options);
 | 
			
		||||
        this.historicalProvider = this.openmct.telemetry.
 | 
			
		||||
            findRequestProvider(this.domainObject, this.options);
 | 
			
		||||
 | 
			
		||||
        this._requestHistoricalTelemetry();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If a historical provider exists, then historical requests will be made
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    async _requestHistoricalTelemetry() {
 | 
			
		||||
        if (!this.historicalProvider) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let historicalData;
 | 
			
		||||
 | 
			
		||||
        this.options.onPartialResponse = this._processNewTelemetry.bind(this);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            this.requestAbort = new AbortController();
 | 
			
		||||
            this.options.signal = this.requestAbort.signal;
 | 
			
		||||
            historicalData = await this.historicalProvider.request(this.domainObject, this.options);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (error.name !== 'AbortError') {
 | 
			
		||||
                console.error('Error requesting telemetry data...');
 | 
			
		||||
                this._error(error);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.requestAbort = undefined;
 | 
			
		||||
 | 
			
		||||
        this._processNewTelemetry(historicalData);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This uses the built in subscription function from Telemetry API
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _initiateSubscriptionTelemetry() {
 | 
			
		||||
 | 
			
		||||
        if (this.unsubscribe) {
 | 
			
		||||
            this.unsubscribe();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.unsubscribe = this.openmct.telemetry
 | 
			
		||||
            .subscribe(
 | 
			
		||||
                this.domainObject,
 | 
			
		||||
                datum => this._processNewTelemetry(datum),
 | 
			
		||||
                this.options
 | 
			
		||||
            );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Filter any new telemetry (add/page, historical, subscription) based on
 | 
			
		||||
     * time bounds and dupes
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {(Object|Object[])} telemetryData - telemetry data object or
 | 
			
		||||
     * array of telemetry data objects
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _processNewTelemetry(telemetryData) {
 | 
			
		||||
        let data = Array.isArray(telemetryData) ? telemetryData : [telemetryData];
 | 
			
		||||
        let parsedValue;
 | 
			
		||||
        let beforeStartOfBounds;
 | 
			
		||||
        let afterEndOfBounds;
 | 
			
		||||
        let added = [];
 | 
			
		||||
 | 
			
		||||
        for (let datum of data) {
 | 
			
		||||
            parsedValue = this.parseTime(datum);
 | 
			
		||||
            beforeStartOfBounds = parsedValue < this.lastBounds.start;
 | 
			
		||||
            afterEndOfBounds = parsedValue > this.lastBounds.end;
 | 
			
		||||
 | 
			
		||||
            if (!afterEndOfBounds && !beforeStartOfBounds) {
 | 
			
		||||
                let isDuplicate = false;
 | 
			
		||||
                let startIndex = this._sortedIndex(datum);
 | 
			
		||||
                let endIndex = undefined;
 | 
			
		||||
 | 
			
		||||
                // dupe check
 | 
			
		||||
                if (startIndex !== this.boundedTelemetry.length) {
 | 
			
		||||
                    endIndex = _.sortedLastIndexBy(
 | 
			
		||||
                        this.boundedTelemetry,
 | 
			
		||||
                        datum,
 | 
			
		||||
                        boundedDatum => this.parseTime(boundedDatum)
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    if (endIndex > startIndex) {
 | 
			
		||||
                        let potentialDupes = this.boundedTelemetry.slice(startIndex, endIndex);
 | 
			
		||||
 | 
			
		||||
                        isDuplicate = potentialDupes.some(_.isEqual.bind(undefined, datum));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!isDuplicate) {
 | 
			
		||||
                    let index = endIndex || startIndex;
 | 
			
		||||
 | 
			
		||||
                    this.boundedTelemetry.splice(index, 0, datum);
 | 
			
		||||
                    added.push(datum);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            } else if (afterEndOfBounds) {
 | 
			
		||||
                this.futureBuffer.push(datum);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (added.length) {
 | 
			
		||||
            this.emit('add', added);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Finds the correct insertion point for the given telemetry datum.
 | 
			
		||||
     * Leverages lodash's `sortedIndexBy` function which implements a binary search.
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _sortedIndex(datum) {
 | 
			
		||||
        if (this.boundedTelemetry.length === 0) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let parsedValue = this.parseTime(datum);
 | 
			
		||||
        let lastValue = this.parseTime(this.boundedTelemetry[this.boundedTelemetry.length - 1]);
 | 
			
		||||
 | 
			
		||||
        if (parsedValue > lastValue || parsedValue === lastValue) {
 | 
			
		||||
            return this.boundedTelemetry.length;
 | 
			
		||||
        } else {
 | 
			
		||||
            return _.sortedIndexBy(
 | 
			
		||||
                this.boundedTelemetry,
 | 
			
		||||
                datum,
 | 
			
		||||
                boundedDatum => this.parseTime(boundedDatum)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * when the start time, end time, or both have been updated.
 | 
			
		||||
     * data could be added OR removed here we update the current
 | 
			
		||||
     * bounded telemetry
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {TimeConductorBounds} bounds The newly updated bounds
 | 
			
		||||
     * @param  {boolean} [tick] `true` if the bounds update was due to
 | 
			
		||||
     * a "tick" event (ie. was an automatic update), false otherwise.
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _bounds(bounds, isTick) {
 | 
			
		||||
        let startChanged = this.lastBounds.start !== bounds.start;
 | 
			
		||||
        let endChanged = this.lastBounds.end !== bounds.end;
 | 
			
		||||
 | 
			
		||||
        this.lastBounds = bounds;
 | 
			
		||||
 | 
			
		||||
        if (isTick) {
 | 
			
		||||
            // need to check futureBuffer and need to check
 | 
			
		||||
            // if anything has fallen out of bounds
 | 
			
		||||
            let startIndex = 0;
 | 
			
		||||
            let endIndex = 0;
 | 
			
		||||
 | 
			
		||||
            let discarded = [];
 | 
			
		||||
            let added = [];
 | 
			
		||||
            let testDatum = {};
 | 
			
		||||
 | 
			
		||||
            if (startChanged) {
 | 
			
		||||
                testDatum[this.timeKey] = bounds.start;
 | 
			
		||||
                // Calculate the new index of the first item within the bounds
 | 
			
		||||
                startIndex = _.sortedIndexBy(
 | 
			
		||||
                    this.boundedTelemetry,
 | 
			
		||||
                    testDatum,
 | 
			
		||||
                    datum => this.parseTime(datum)
 | 
			
		||||
                );
 | 
			
		||||
                discarded = this.boundedTelemetry.splice(0, startIndex);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (endChanged) {
 | 
			
		||||
                testDatum[this.timeKey] = bounds.end;
 | 
			
		||||
                // Calculate the new index of the last item in bounds
 | 
			
		||||
                endIndex = _.sortedLastIndexBy(
 | 
			
		||||
                    this.futureBuffer,
 | 
			
		||||
                    testDatum,
 | 
			
		||||
                    datum => this.parseTime(datum)
 | 
			
		||||
                );
 | 
			
		||||
                added = this.futureBuffer.splice(0, endIndex);
 | 
			
		||||
                this.boundedTelemetry = [...this.boundedTelemetry, ...added];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (discarded.length > 0) {
 | 
			
		||||
                this.emit('remove', discarded);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (added.length > 0) {
 | 
			
		||||
                this.emit('add', added);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            // user bounds change, reset
 | 
			
		||||
            this._reset();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * whenever the time system is updated need to update related values in
 | 
			
		||||
     * the Telemetry Collection and reset the telemetry collection
 | 
			
		||||
     *
 | 
			
		||||
     * @param  {TimeSystem} timeSystem - the value of the currently applied
 | 
			
		||||
     * Time System
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _timeSystem(timeSystem) {
 | 
			
		||||
        let domains = this.metadata.valuesForHints(['domain']);
 | 
			
		||||
        let domain = domains.find((d) => d.key === timeSystem.key);
 | 
			
		||||
 | 
			
		||||
        if (domain === undefined) {
 | 
			
		||||
            this._error(ERRORS.TIMESYSTEM_KEY);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // timeKey is used to create a dummy datum used for sorting
 | 
			
		||||
        this.timeKey = domain.source; // this defaults to key if no source is set
 | 
			
		||||
        let metadataValue = this.metadata.value(timeSystem.key) || { format: timeSystem.key };
 | 
			
		||||
        let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
 | 
			
		||||
 | 
			
		||||
        this.parseTime = (datum) => {
 | 
			
		||||
            return valueFormatter.parse(datum);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this._reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reset the telemetry data of the collection, and re-request
 | 
			
		||||
     * historical telemetry
 | 
			
		||||
     * @private
 | 
			
		||||
     *
 | 
			
		||||
     * @todo handle subscriptions more granually
 | 
			
		||||
     */
 | 
			
		||||
    _reset() {
 | 
			
		||||
        this.boundedTelemetry = [];
 | 
			
		||||
        this.futureBuffer = [];
 | 
			
		||||
 | 
			
		||||
        this.emit('clear');
 | 
			
		||||
 | 
			
		||||
        this._requestHistoricalTelemetry();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * adds the _bounds callback to the 'bounds' timeAPI listener
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _watchBounds() {
 | 
			
		||||
        this.openmct.time.on('bounds', this._bounds, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * removes the _bounds callback from the 'bounds' timeAPI listener
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _unwatchBounds() {
 | 
			
		||||
        this.openmct.time.off('bounds', this._bounds, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * adds the _timeSystem callback to the 'timeSystem' timeAPI listener
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _watchTimeSystem() {
 | 
			
		||||
        this.openmct.time.on('timeSystem', this._timeSystem, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * removes the _timeSystem callback from the 'timeSystem' timeAPI listener
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _unwatchTimeSystem() {
 | 
			
		||||
        this.openmct.time.off('timeSystem', this._timeSystem, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * will throw a new Error, for passed in message
 | 
			
		||||
     * @param  {string} message Message describing the error
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    _error(message) {
 | 
			
		||||
        throw new Error(message);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -31,6 +31,11 @@ define([
 | 
			
		||||
        valueMetadata.hints = valueMetadata.hints || {};
 | 
			
		||||
 | 
			
		||||
        if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'x')) {
 | 
			
		||||
            console.warn(
 | 
			
		||||
                'DEPRECATION WARNING: `x` hints should be replaced with '
 | 
			
		||||
                + '`domain` hints moving forward.  '
 | 
			
		||||
                + 'https://github.com/nasa/openmct/issues/1546'
 | 
			
		||||
            );
 | 
			
		||||
            if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'domain')) {
 | 
			
		||||
                valueMetadata.hints.domain = valueMetadata.hints.x;
 | 
			
		||||
            }
 | 
			
		||||
@@ -39,6 +44,11 @@ define([
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'y')) {
 | 
			
		||||
            console.warn(
 | 
			
		||||
                'DEPRECATION WARNING: `y` hints should be replaced with '
 | 
			
		||||
                + '`range` hints moving forward.  '
 | 
			
		||||
                + 'https://github.com/nasa/openmct/issues/1546'
 | 
			
		||||
            );
 | 
			
		||||
            if (!Object.prototype.hasOwnProperty.call(valueMetadata.hints, 'range')) {
 | 
			
		||||
                valueMetadata.hints.range = valueMetadata.hints.y;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,106 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import TimeContext from "./TimeContext";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The GlobalContext handles getting and setting time of the openmct application in general.
 | 
			
		||||
 * Views will use this context unless they specify an alternate/independent time context
 | 
			
		||||
 */
 | 
			
		||||
class GlobalTimeContext extends TimeContext {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        //The Time Of Interest
 | 
			
		||||
        this.toi = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get or set the start and end time of the time conductor. Basic validation
 | 
			
		||||
     * of bounds is performed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {module:openmct.TimeAPI~TimeConductorBounds} newBounds
 | 
			
		||||
     * @throws {Error} Validation error
 | 
			
		||||
     * @fires module:openmct.TimeAPI~bounds
 | 
			
		||||
     * @returns {module:openmct.TimeAPI~TimeConductorBounds}
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method bounds
 | 
			
		||||
     */
 | 
			
		||||
    bounds(newBounds) {
 | 
			
		||||
        if (arguments.length > 0) {
 | 
			
		||||
            super.bounds.call(this, ...arguments);
 | 
			
		||||
            // If a bounds change results in a TOI outside of the current
 | 
			
		||||
            // bounds, unset it
 | 
			
		||||
            if (this.toi < newBounds.start || this.toi > newBounds.end) {
 | 
			
		||||
                this.timeOfInterest(undefined);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //Return a copy to prevent direct mutation of time conductor bounds.
 | 
			
		||||
        return JSON.parse(JSON.stringify(this.boundsVal));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update bounds based on provided time and current offsets
 | 
			
		||||
     * @private
 | 
			
		||||
     * @param {number} timestamp A time from which bounds will be calculated
 | 
			
		||||
     * using current offsets.
 | 
			
		||||
     */
 | 
			
		||||
    tick(timestamp) {
 | 
			
		||||
        super.tick.call(this, ...arguments);
 | 
			
		||||
 | 
			
		||||
        // If a bounds change results in a TOI outside of the current
 | 
			
		||||
        // bounds, unset it
 | 
			
		||||
        if (this.toi < this.boundsVal.start || this.toi > this.boundsVal.end) {
 | 
			
		||||
            this.timeOfInterest(undefined);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get or set the Time of Interest. The Time of Interest is a single point
 | 
			
		||||
     * in time, and constitutes the temporal focus of application views. It can
 | 
			
		||||
     * be manipulated by the user from the time conductor or from other views.
 | 
			
		||||
     * The time of interest can effectively be unset by assigning a value of
 | 
			
		||||
     * 'undefined'.
 | 
			
		||||
     * @fires module:openmct.TimeAPI~timeOfInterest
 | 
			
		||||
     * @param newTOI
 | 
			
		||||
     * @returns {number} the current time of interest
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method timeOfInterest
 | 
			
		||||
     */
 | 
			
		||||
    timeOfInterest(newTOI) {
 | 
			
		||||
        if (arguments.length > 0) {
 | 
			
		||||
            this.toi = newTOI;
 | 
			
		||||
            /**
 | 
			
		||||
             * The Time of Interest has moved.
 | 
			
		||||
             * @event timeOfInterest
 | 
			
		||||
             * @memberof module:openmct.TimeAPI~
 | 
			
		||||
             * @property {number} Current time of interest
 | 
			
		||||
             */
 | 
			
		||||
            this.emit('timeOfInterest', this.toi);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.toi;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default GlobalTimeContext;
 | 
			
		||||
@@ -1,94 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import TimeContext from "./TimeContext";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The IndependentTimeContext handles getting and setting time of the openmct application in general.
 | 
			
		||||
 * Views will use the GlobalTimeContext unless they specify an alternate/independent time context here.
 | 
			
		||||
 */
 | 
			
		||||
class IndependentTimeContext extends TimeContext {
 | 
			
		||||
    constructor(globalTimeContext, key) {
 | 
			
		||||
        super();
 | 
			
		||||
        this.key = key;
 | 
			
		||||
 | 
			
		||||
        this.globalTimeContext = globalTimeContext;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the active clock. Tick source will be immediately subscribed to
 | 
			
		||||
     * and ticking will begin. Offsets from 'now' must also be provided. A clock
 | 
			
		||||
     * can be unset by calling {@link stopClock}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {Clock || string} keyOrClock The clock to activate, or its key
 | 
			
		||||
     * @param {ClockOffsets} offsets on each tick these will be used to calculate
 | 
			
		||||
     * the start and end bounds. This maintains a sliding time window of a fixed
 | 
			
		||||
     * width that automatically updates.
 | 
			
		||||
     * @fires module:openmct.TimeAPI~clock
 | 
			
		||||
     * @return {Clock} the currently active clock;
 | 
			
		||||
     */
 | 
			
		||||
    clock(keyOrClock, offsets) {
 | 
			
		||||
        if (arguments.length === 2) {
 | 
			
		||||
            let clock;
 | 
			
		||||
 | 
			
		||||
            if (typeof keyOrClock === 'string') {
 | 
			
		||||
                clock = this.globalTimeContext.clocks.get(keyOrClock);
 | 
			
		||||
                if (clock === undefined) {
 | 
			
		||||
                    throw "Unknown clock '" + keyOrClock + "'. Has it been registered with 'addClock'?";
 | 
			
		||||
                }
 | 
			
		||||
            } else if (typeof keyOrClock === 'object') {
 | 
			
		||||
                clock = keyOrClock;
 | 
			
		||||
                if (!this.globalTimeContext.clocks.has(clock.key)) {
 | 
			
		||||
                    throw "Unknown clock '" + keyOrClock.key + "'. Has it been registered with 'addClock'?";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const previousClock = this.activeClock;
 | 
			
		||||
            if (previousClock !== undefined) {
 | 
			
		||||
                previousClock.off("tick", this.tick);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.activeClock = clock;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * The active clock has changed. Clock can be unset by calling {@link stopClock}
 | 
			
		||||
             * @event clock
 | 
			
		||||
             * @memberof module:openmct.TimeAPI~
 | 
			
		||||
             * @property {Clock} clock The newly activated clock, or undefined
 | 
			
		||||
             * if the system is no longer following a clock source
 | 
			
		||||
             */
 | 
			
		||||
            this.emit("clock", this.activeClock);
 | 
			
		||||
 | 
			
		||||
            if (this.activeClock !== undefined) {
 | 
			
		||||
                this.clockOffsets(offsets);
 | 
			
		||||
                this.activeClock.on("tick", this.tick);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else if (arguments.length === 1) {
 | 
			
		||||
            throw "When setting the clock, clock offsets must also be provided";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.activeClock;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default IndependentTimeContext;
 | 
			
		||||
@@ -20,35 +20,51 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import GlobalTimeContext from "./GlobalTimeContext";
 | 
			
		||||
import IndependentTimeContext from "@/api/time/IndependentTimeContext";
 | 
			
		||||
define(['EventEmitter'], function (EventEmitter) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The public API for setting and querying the temporal state of the
 | 
			
		||||
     * application. The concept of time is integral to Open MCT, and at least
 | 
			
		||||
     * one {@link TimeSystem}, as well as some default time bounds must be
 | 
			
		||||
     * registered and enabled via {@link TimeAPI.addTimeSystem} and
 | 
			
		||||
     * {@link TimeAPI.timeSystem} respectively for Open MCT to work.
 | 
			
		||||
     *
 | 
			
		||||
     * Time-sensitive views will typically respond to changes to bounds or other
 | 
			
		||||
     * properties of the time conductor and update the data displayed based on
 | 
			
		||||
     * the temporal state of the application. The current time bounds are also
 | 
			
		||||
     * used in queries for historical data.
 | 
			
		||||
     *
 | 
			
		||||
     * The TimeAPI extends the EventEmitter class. A number of events are
 | 
			
		||||
     * fired when properties of the time conductor change, which are documented
 | 
			
		||||
     * below.
 | 
			
		||||
     *
 | 
			
		||||
     * @interface
 | 
			
		||||
     * @memberof module:openmct
 | 
			
		||||
     */
 | 
			
		||||
    function TimeAPI() {
 | 
			
		||||
        EventEmitter.call(this);
 | 
			
		||||
 | 
			
		||||
        //The Time System
 | 
			
		||||
        this.system = undefined;
 | 
			
		||||
        //The Time Of Interest
 | 
			
		||||
        this.toi = undefined;
 | 
			
		||||
 | 
			
		||||
        this.boundsVal = {
 | 
			
		||||
            start: undefined,
 | 
			
		||||
            end: undefined
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.timeSystems = new Map();
 | 
			
		||||
        this.clocks = new Map();
 | 
			
		||||
        this.activeClock = undefined;
 | 
			
		||||
        this.offsets = undefined;
 | 
			
		||||
 | 
			
		||||
        this.tick = this.tick.bind(this);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* The public API for setting and querying the temporal state of the
 | 
			
		||||
* application. The concept of time is integral to Open MCT, and at least
 | 
			
		||||
* one {@link TimeSystem}, as well as some default time bounds must be
 | 
			
		||||
* registered and enabled via {@link TimeAPI.addTimeSystem} and
 | 
			
		||||
* {@link TimeAPI.timeSystem} respectively for Open MCT to work.
 | 
			
		||||
*
 | 
			
		||||
* Time-sensitive views will typically respond to changes to bounds or other
 | 
			
		||||
* properties of the time conductor and update the data displayed based on
 | 
			
		||||
* the temporal state of the application. The current time bounds are also
 | 
			
		||||
* used in queries for historical data.
 | 
			
		||||
*
 | 
			
		||||
* The TimeAPI extends the GlobalTimeContext which in turn extends the TimeContext/EventEmitter class. A number of events are
 | 
			
		||||
* fired when properties of the time conductor change, which are documented
 | 
			
		||||
* below.
 | 
			
		||||
*
 | 
			
		||||
* @interface
 | 
			
		||||
* @memberof module:openmct
 | 
			
		||||
*/
 | 
			
		||||
class TimeAPI extends GlobalTimeContext {
 | 
			
		||||
    constructor(openmct) {
 | 
			
		||||
        super();
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.independentContexts = new Map();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TimeAPI.prototype = Object.create(EventEmitter.prototype);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A TimeSystem provides meaning to the values returned by the TimeAPI. Open
 | 
			
		||||
     * MCT supports multiple different types of time values, although all are
 | 
			
		||||
@@ -78,16 +94,16 @@ class TimeAPI extends GlobalTimeContext {
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @param {TimeSystem} timeSystem A time system object.
 | 
			
		||||
     */
 | 
			
		||||
    addTimeSystem(timeSystem) {
 | 
			
		||||
    TimeAPI.prototype.addTimeSystem = function (timeSystem) {
 | 
			
		||||
        this.timeSystems.set(timeSystem.key, timeSystem);
 | 
			
		||||
    }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @returns {TimeSystem[]}
 | 
			
		||||
     */
 | 
			
		||||
    getAllTimeSystems() {
 | 
			
		||||
    TimeAPI.prototype.getAllTimeSystems = function () {
 | 
			
		||||
        return Array.from(this.timeSystems.values());
 | 
			
		||||
    }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clocks provide a timing source that is used to
 | 
			
		||||
@@ -110,81 +126,340 @@ class TimeAPI extends GlobalTimeContext {
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @param {Clock} clock
 | 
			
		||||
     */
 | 
			
		||||
    addClock(clock) {
 | 
			
		||||
    TimeAPI.prototype.addClock = function (clock) {
 | 
			
		||||
        this.clocks.set(clock.key, clock);
 | 
			
		||||
    }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @returns {Clock[]}
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     */
 | 
			
		||||
    getAllClocks() {
 | 
			
		||||
    TimeAPI.prototype.getAllClocks = function () {
 | 
			
		||||
        return Array.from(this.clocks.values());
 | 
			
		||||
    }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get or set an independent time context which follows the TimeAPI timeSystem,
 | 
			
		||||
     * but with different offsets for a given domain object
 | 
			
		||||
     * @param {key | string} key The identifier key of the domain object these offsets are set for
 | 
			
		||||
     * @param {ClockOffsets | TimeBounds} value This maintains a sliding time window of a fixed width that automatically updates
 | 
			
		||||
     * @param {key | string} clockKey the real time clock key currently in use
 | 
			
		||||
     * Validate the given bounds. This can be used for pre-validation of bounds,
 | 
			
		||||
     * for example by views validating user inputs.
 | 
			
		||||
     * @param {TimeBounds} bounds The start and end time of the conductor.
 | 
			
		||||
     * @returns {string | true} A validation error, or true if valid
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method addIndependentTimeContext
 | 
			
		||||
     * @method validateBounds
 | 
			
		||||
     */
 | 
			
		||||
    addIndependentContext(key, value, clockKey) {
 | 
			
		||||
        let timeContext = this.independentContexts.get(key);
 | 
			
		||||
        if (!timeContext) {
 | 
			
		||||
            timeContext = new IndependentTimeContext(this, key);
 | 
			
		||||
            this.independentContexts.set(key, timeContext);
 | 
			
		||||
    TimeAPI.prototype.validateBounds = function (bounds) {
 | 
			
		||||
        if ((bounds.start === undefined)
 | 
			
		||||
            || (bounds.end === undefined)
 | 
			
		||||
            || isNaN(bounds.start)
 | 
			
		||||
            || isNaN(bounds.end)
 | 
			
		||||
        ) {
 | 
			
		||||
            return "Start and end must be specified as integer values";
 | 
			
		||||
        } else if (bounds.start > bounds.end) {
 | 
			
		||||
            return "Specified start date exceeds end bound";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (clockKey) {
 | 
			
		||||
            timeContext.clock(clockKey, value);
 | 
			
		||||
        } else {
 | 
			
		||||
            timeContext.stopClock();
 | 
			
		||||
            timeContext.bounds(value);
 | 
			
		||||
        return true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate the given offsets. This can be used for pre-validation of
 | 
			
		||||
     * offsets, for example by views validating user inputs.
 | 
			
		||||
     * @param {ClockOffsets} offsets The start and end offsets from a 'now' value.
 | 
			
		||||
     * @returns {string | true} A validation error, or true if valid
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method validateBounds
 | 
			
		||||
     */
 | 
			
		||||
    TimeAPI.prototype.validateOffsets = function (offsets) {
 | 
			
		||||
        if ((offsets.start === undefined)
 | 
			
		||||
            || (offsets.end === undefined)
 | 
			
		||||
            || isNaN(offsets.start)
 | 
			
		||||
            || isNaN(offsets.end)
 | 
			
		||||
        ) {
 | 
			
		||||
            return "Start and end offsets must be specified as integer values";
 | 
			
		||||
        } else if (offsets.start >= offsets.end) {
 | 
			
		||||
            return "Specified start offset must be < end offset";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('timeContext', key);
 | 
			
		||||
 | 
			
		||||
        return () => {
 | 
			
		||||
            this.independentContexts.delete(key);
 | 
			
		||||
            timeContext.emit('timeContext', key);
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
        return true;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the independent time context which follows the TimeAPI timeSystem,
 | 
			
		||||
     * but with different offsets.
 | 
			
		||||
     * @param {key | string} key The identifier key of the domain object these offsets
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method getIndependentTimeContext
 | 
			
		||||
     * @typedef {Object} TimeBounds
 | 
			
		||||
     * @property {number} start The start time displayed by the time conductor
 | 
			
		||||
     * in ms since epoch. Epoch determined by currently active time system
 | 
			
		||||
     * @property {number} end The end time displayed by the time conductor in ms
 | 
			
		||||
     * since epoch.
 | 
			
		||||
     * @memberof module:openmct.TimeAPI~
 | 
			
		||||
     */
 | 
			
		||||
    getIndependentContext(key) {
 | 
			
		||||
        return this.independentContexts.get(key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the a timeContext for a view based on it's objectPath. If there is any object in the objectPath with an independent time context, it will be returned.
 | 
			
		||||
     * Otherwise, the global time context will be returned.
 | 
			
		||||
     * @param { Array } objectPath The view's objectPath
 | 
			
		||||
     * Get or set the start and end time of the time conductor. Basic validation
 | 
			
		||||
     * of bounds is performed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {module:openmct.TimeAPI~TimeConductorBounds} newBounds
 | 
			
		||||
     * @throws {Error} Validation error
 | 
			
		||||
     * @fires module:openmct.TimeAPI~bounds
 | 
			
		||||
     * @returns {module:openmct.TimeAPI~TimeConductorBounds}
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method getContextForView
 | 
			
		||||
     * @method bounds
 | 
			
		||||
     */
 | 
			
		||||
    getContextForView(objectPath) {
 | 
			
		||||
        let timeContext = this;
 | 
			
		||||
 | 
			
		||||
        objectPath.forEach(item => {
 | 
			
		||||
            const key = this.openmct.objects.makeKeyString(item.identifier);
 | 
			
		||||
            if (this.independentContexts.get(key)) {
 | 
			
		||||
                timeContext = this.independentContexts.get(key);
 | 
			
		||||
    TimeAPI.prototype.bounds = function (newBounds) {
 | 
			
		||||
        if (arguments.length > 0) {
 | 
			
		||||
            const validationResult = this.validateBounds(newBounds);
 | 
			
		||||
            if (validationResult !== true) {
 | 
			
		||||
                throw new Error(validationResult);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return timeContext;
 | 
			
		||||
    }
 | 
			
		||||
            //Create a copy to avoid direct mutation of conductor bounds
 | 
			
		||||
            this.boundsVal = JSON.parse(JSON.stringify(newBounds));
 | 
			
		||||
            /**
 | 
			
		||||
             * The start time, end time, or both have been updated.
 | 
			
		||||
             * @event bounds
 | 
			
		||||
             * @memberof module:openmct.TimeAPI~
 | 
			
		||||
             * @property {TimeConductorBounds} bounds The newly updated bounds
 | 
			
		||||
             * @property {boolean} [tick] `true` if the bounds update was due to
 | 
			
		||||
             * a "tick" event (ie. was an automatic update), false otherwise.
 | 
			
		||||
             */
 | 
			
		||||
            this.emit('bounds', this.boundsVal, false);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
            // If a bounds change results in a TOI outside of the current
 | 
			
		||||
            // bounds, unset it
 | 
			
		||||
            if (this.toi < newBounds.start || this.toi > newBounds.end) {
 | 
			
		||||
                this.timeOfInterest(undefined);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
export default TimeAPI;
 | 
			
		||||
        //Return a copy to prevent direct mutation of time conductor bounds.
 | 
			
		||||
        return JSON.parse(JSON.stringify(this.boundsVal));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get or set the time system of the TimeAPI.
 | 
			
		||||
     * @param {TimeSystem | string} timeSystem
 | 
			
		||||
     * @param {module:openmct.TimeAPI~TimeConductorBounds} bounds
 | 
			
		||||
     * @fires module:openmct.TimeAPI~timeSystem
 | 
			
		||||
     * @returns {TimeSystem} The currently applied time system
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method timeSystem
 | 
			
		||||
     */
 | 
			
		||||
    TimeAPI.prototype.timeSystem = function (timeSystemOrKey, bounds) {
 | 
			
		||||
        if (arguments.length >= 1) {
 | 
			
		||||
            if (arguments.length === 1 && !this.activeClock) {
 | 
			
		||||
                throw new Error(
 | 
			
		||||
                    "Must specify bounds when changing time system without "
 | 
			
		||||
                    + "an active clock."
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let timeSystem;
 | 
			
		||||
 | 
			
		||||
            if (timeSystemOrKey === undefined) {
 | 
			
		||||
                throw "Please provide a time system";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (typeof timeSystemOrKey === 'string') {
 | 
			
		||||
                timeSystem = this.timeSystems.get(timeSystemOrKey);
 | 
			
		||||
 | 
			
		||||
                if (timeSystem === undefined) {
 | 
			
		||||
                    throw "Unknown time system " + timeSystemOrKey + ". Has it been registered with 'addTimeSystem'?";
 | 
			
		||||
                }
 | 
			
		||||
            } else if (typeof timeSystemOrKey === 'object') {
 | 
			
		||||
                timeSystem = timeSystemOrKey;
 | 
			
		||||
 | 
			
		||||
                if (!this.timeSystems.has(timeSystem.key)) {
 | 
			
		||||
                    throw "Unknown time system " + timeSystem.key + ". Has it been registered with 'addTimeSystem'?";
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                throw "Attempt to set invalid time system in Time API. Please provide a previously registered time system object or key";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.system = timeSystem;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * The time system used by the time
 | 
			
		||||
             * conductor has changed. A change in Time System will always be
 | 
			
		||||
             * followed by a bounds event specifying new query bounds.
 | 
			
		||||
             *
 | 
			
		||||
             * @event module:openmct.TimeAPI~timeSystem
 | 
			
		||||
             * @property {TimeSystem} The value of the currently applied
 | 
			
		||||
             * Time System
 | 
			
		||||
             * */
 | 
			
		||||
            this.emit('timeSystem', this.system);
 | 
			
		||||
            if (bounds) {
 | 
			
		||||
                this.bounds(bounds);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.system;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get or set the Time of Interest. The Time of Interest is a single point
 | 
			
		||||
     * in time, and constitutes the temporal focus of application views. It can
 | 
			
		||||
     * be manipulated by the user from the time conductor or from other views.
 | 
			
		||||
     * The time of interest can effectively be unset by assigning a value of
 | 
			
		||||
     * 'undefined'.
 | 
			
		||||
     * @fires module:openmct.TimeAPI~timeOfInterest
 | 
			
		||||
     * @param newTOI
 | 
			
		||||
     * @returns {number} the current time of interest
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method timeOfInterest
 | 
			
		||||
     */
 | 
			
		||||
    TimeAPI.prototype.timeOfInterest = function (newTOI) {
 | 
			
		||||
        if (arguments.length > 0) {
 | 
			
		||||
            this.toi = newTOI;
 | 
			
		||||
            /**
 | 
			
		||||
             * The Time of Interest has moved.
 | 
			
		||||
             * @event timeOfInterest
 | 
			
		||||
             * @memberof module:openmct.TimeAPI~
 | 
			
		||||
             * @property {number} Current time of interest
 | 
			
		||||
             */
 | 
			
		||||
            this.emit('timeOfInterest', this.toi);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.toi;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update bounds based on provided time and current offsets
 | 
			
		||||
     * @private
 | 
			
		||||
     * @param {number} timestamp A time from which boudns will be calculated
 | 
			
		||||
     * using current offsets.
 | 
			
		||||
     */
 | 
			
		||||
    TimeAPI.prototype.tick = function (timestamp) {
 | 
			
		||||
        const newBounds = {
 | 
			
		||||
            start: timestamp + this.offsets.start,
 | 
			
		||||
            end: timestamp + this.offsets.end
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.boundsVal = newBounds;
 | 
			
		||||
        this.emit('bounds', this.boundsVal, true);
 | 
			
		||||
 | 
			
		||||
        // If a bounds change results in a TOI outside of the current
 | 
			
		||||
        // bounds, unset it
 | 
			
		||||
        if (this.toi < newBounds.start || this.toi > newBounds.end) {
 | 
			
		||||
            this.timeOfInterest(undefined);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the active clock. Tick source will be immediately subscribed to
 | 
			
		||||
     * and ticking will begin. Offsets from 'now' must also be provided. A clock
 | 
			
		||||
     * can be unset by calling {@link stopClock}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {Clock || string} The clock to activate, or its key
 | 
			
		||||
     * @param {ClockOffsets} offsets on each tick these will be used to calculate
 | 
			
		||||
     * the start and end bounds. This maintains a sliding time window of a fixed
 | 
			
		||||
     * width that automatically updates.
 | 
			
		||||
     * @fires module:openmct.TimeAPI~clock
 | 
			
		||||
     * @return {Clock} the currently active clock;
 | 
			
		||||
     */
 | 
			
		||||
    TimeAPI.prototype.clock = function (keyOrClock, offsets) {
 | 
			
		||||
        if (arguments.length === 2) {
 | 
			
		||||
            let clock;
 | 
			
		||||
 | 
			
		||||
            if (typeof keyOrClock === 'string') {
 | 
			
		||||
                clock = this.clocks.get(keyOrClock);
 | 
			
		||||
                if (clock === undefined) {
 | 
			
		||||
                    throw "Unknown clock '" + keyOrClock + "'. Has it been registered with 'addClock'?";
 | 
			
		||||
                }
 | 
			
		||||
            } else if (typeof keyOrClock === 'object') {
 | 
			
		||||
                clock = keyOrClock;
 | 
			
		||||
                if (!this.clocks.has(clock.key)) {
 | 
			
		||||
                    throw "Unknown clock '" + keyOrClock.key + "'. Has it been registered with 'addClock'?";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const previousClock = this.activeClock;
 | 
			
		||||
            if (previousClock !== undefined) {
 | 
			
		||||
                previousClock.off("tick", this.tick);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.activeClock = clock;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * The active clock has changed. Clock can be unset by calling {@link stopClock}
 | 
			
		||||
             * @event clock
 | 
			
		||||
             * @memberof module:openmct.TimeAPI~
 | 
			
		||||
             * @property {Clock} clock The newly activated clock, or undefined
 | 
			
		||||
             * if the system is no longer following a clock source
 | 
			
		||||
             */
 | 
			
		||||
            this.emit("clock", this.activeClock);
 | 
			
		||||
 | 
			
		||||
            if (this.activeClock !== undefined) {
 | 
			
		||||
                this.clockOffsets(offsets);
 | 
			
		||||
                this.activeClock.on("tick", this.tick);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else if (arguments.length === 1) {
 | 
			
		||||
            throw "When setting the clock, clock offsets must also be provided";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.activeClock;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clock offsets are used to calculate temporal bounds when the system is
 | 
			
		||||
     * ticking on a clock source.
 | 
			
		||||
     *
 | 
			
		||||
     * @typedef {object} ClockOffsets
 | 
			
		||||
     * @property {number} start A time span relative to the current value of the
 | 
			
		||||
     * ticking clock, from which start bounds will be calculated. This value must
 | 
			
		||||
     * be < 0. When a clock is active, bounds will be calculated automatically
 | 
			
		||||
     * based on the value provided by the clock, and the defined clock offsets.
 | 
			
		||||
     * @property {number} end A time span relative to the current value of the
 | 
			
		||||
     * ticking clock, from which end bounds will be calculated. This value must
 | 
			
		||||
     * be >= 0.
 | 
			
		||||
     */
 | 
			
		||||
    /**
 | 
			
		||||
     * Get or set the currently applied clock offsets. If no parameter is provided,
 | 
			
		||||
     * the current value will be returned. If provided, the new value will be
 | 
			
		||||
     * used as the new clock offsets.
 | 
			
		||||
     * @param {ClockOffsets} offsets
 | 
			
		||||
     * @returns {ClockOffsets}
 | 
			
		||||
     */
 | 
			
		||||
    TimeAPI.prototype.clockOffsets = function (offsets) {
 | 
			
		||||
        if (arguments.length > 0) {
 | 
			
		||||
 | 
			
		||||
            const validationResult = this.validateOffsets(offsets);
 | 
			
		||||
            if (validationResult !== true) {
 | 
			
		||||
                throw new Error(validationResult);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.offsets = offsets;
 | 
			
		||||
 | 
			
		||||
            const currentValue = this.activeClock.currentValue();
 | 
			
		||||
            const newBounds = {
 | 
			
		||||
                start: currentValue + offsets.start,
 | 
			
		||||
                end: currentValue + offsets.end
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.bounds(newBounds);
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Event that is triggered when clock offsets change.
 | 
			
		||||
             * @event clockOffsets
 | 
			
		||||
             * @memberof module:openmct.TimeAPI~
 | 
			
		||||
             * @property {ClockOffsets} clockOffsets The newly activated clock
 | 
			
		||||
             * offsets.
 | 
			
		||||
             */
 | 
			
		||||
            this.emit("clockOffsets", offsets);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.offsets;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Stop the currently active clock from ticking, and unset it. This will
 | 
			
		||||
     * revert all views to showing a static time frame defined by the current
 | 
			
		||||
     * bounds.
 | 
			
		||||
     */
 | 
			
		||||
    TimeAPI.prototype.stopClock = function () {
 | 
			
		||||
        if (this.activeClock) {
 | 
			
		||||
            this.clock(undefined, undefined);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return TimeAPI;
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -19,243 +19,241 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
import TimeAPI from "./TimeAPI";
 | 
			
		||||
import {createOpenMct} from "utils/testing";
 | 
			
		||||
 | 
			
		||||
describe("The Time API", function () {
 | 
			
		||||
    let api;
 | 
			
		||||
    let timeSystemKey;
 | 
			
		||||
    let timeSystem;
 | 
			
		||||
    let clockKey;
 | 
			
		||||
    let clock;
 | 
			
		||||
    let bounds;
 | 
			
		||||
    let eventListener;
 | 
			
		||||
    let toi;
 | 
			
		||||
    let openmct;
 | 
			
		||||
 | 
			
		||||
    beforeEach(function () {
 | 
			
		||||
        openmct = createOpenMct();
 | 
			
		||||
        api = new TimeAPI(openmct);
 | 
			
		||||
        timeSystemKey = "timeSystemKey";
 | 
			
		||||
        timeSystem = {key: timeSystemKey};
 | 
			
		||||
        clockKey = "someClockKey";
 | 
			
		||||
        clock = jasmine.createSpyObj("clock", [
 | 
			
		||||
            "on",
 | 
			
		||||
            "off",
 | 
			
		||||
            "currentValue"
 | 
			
		||||
        ]);
 | 
			
		||||
        clock.currentValue.and.returnValue(100);
 | 
			
		||||
        clock.key = clockKey;
 | 
			
		||||
        bounds = {
 | 
			
		||||
            start: 0,
 | 
			
		||||
            end: 1
 | 
			
		||||
        };
 | 
			
		||||
        eventListener = jasmine.createSpy("eventListener");
 | 
			
		||||
        toi = 111;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Supports setting and querying of time of interest", function () {
 | 
			
		||||
        expect(api.timeOfInterest()).not.toBe(toi);
 | 
			
		||||
        api.timeOfInterest(toi);
 | 
			
		||||
        expect(api.timeOfInterest()).toBe(toi);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Allows setting of valid bounds", function () {
 | 
			
		||||
        bounds = {
 | 
			
		||||
            start: 0,
 | 
			
		||||
            end: 1
 | 
			
		||||
        };
 | 
			
		||||
        expect(api.bounds()).not.toBe(bounds);
 | 
			
		||||
        expect(api.bounds.bind(api, bounds)).not.toThrow();
 | 
			
		||||
        expect(api.bounds()).toEqual(bounds);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Disallows setting of invalid bounds", function () {
 | 
			
		||||
        bounds = {
 | 
			
		||||
            start: 1,
 | 
			
		||||
            end: 0
 | 
			
		||||
        };
 | 
			
		||||
        expect(api.bounds()).not.toEqual(bounds);
 | 
			
		||||
        expect(api.bounds.bind(api, bounds)).toThrow();
 | 
			
		||||
        expect(api.bounds()).not.toEqual(bounds);
 | 
			
		||||
 | 
			
		||||
        bounds = {start: 1};
 | 
			
		||||
        expect(api.bounds()).not.toEqual(bounds);
 | 
			
		||||
        expect(api.bounds.bind(api, bounds)).toThrow();
 | 
			
		||||
        expect(api.bounds()).not.toEqual(bounds);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Allows setting of previously registered time system with bounds", function () {
 | 
			
		||||
        api.addTimeSystem(timeSystem);
 | 
			
		||||
        expect(api.timeSystem()).not.toBe(timeSystem);
 | 
			
		||||
        expect(function () {
 | 
			
		||||
            api.timeSystem(timeSystem, bounds);
 | 
			
		||||
        }).not.toThrow();
 | 
			
		||||
        expect(api.timeSystem()).toBe(timeSystem);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Disallows setting of time system without bounds", function () {
 | 
			
		||||
        api.addTimeSystem(timeSystem);
 | 
			
		||||
        expect(api.timeSystem()).not.toBe(timeSystem);
 | 
			
		||||
        expect(function () {
 | 
			
		||||
            api.timeSystem(timeSystemKey);
 | 
			
		||||
        }).toThrow();
 | 
			
		||||
        expect(api.timeSystem()).not.toBe(timeSystem);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("allows setting of timesystem without bounds with clock", function () {
 | 
			
		||||
        api.addTimeSystem(timeSystem);
 | 
			
		||||
        api.addClock(clock);
 | 
			
		||||
        api.clock(clockKey, {
 | 
			
		||||
            start: 0,
 | 
			
		||||
            end: 1
 | 
			
		||||
        });
 | 
			
		||||
        expect(api.timeSystem()).not.toBe(timeSystem);
 | 
			
		||||
        expect(function () {
 | 
			
		||||
            api.timeSystem(timeSystemKey);
 | 
			
		||||
        }).not.toThrow();
 | 
			
		||||
        expect(api.timeSystem()).toBe(timeSystem);
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Emits an event when time system changes", function () {
 | 
			
		||||
        api.addTimeSystem(timeSystem);
 | 
			
		||||
        expect(eventListener).not.toHaveBeenCalled();
 | 
			
		||||
        api.on("timeSystem", eventListener);
 | 
			
		||||
        api.timeSystem(timeSystemKey, bounds);
 | 
			
		||||
        expect(eventListener).toHaveBeenCalledWith(timeSystem);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Emits an event when time of interest changes", function () {
 | 
			
		||||
        expect(eventListener).not.toHaveBeenCalled();
 | 
			
		||||
        api.on("timeOfInterest", eventListener);
 | 
			
		||||
        api.timeOfInterest(toi);
 | 
			
		||||
        expect(eventListener).toHaveBeenCalledWith(toi);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Emits an event when bounds change", function () {
 | 
			
		||||
        expect(eventListener).not.toHaveBeenCalled();
 | 
			
		||||
        api.on("bounds", eventListener);
 | 
			
		||||
        api.bounds(bounds);
 | 
			
		||||
        expect(eventListener).toHaveBeenCalledWith(bounds, false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("If bounds are set and TOI lies inside them, do not change TOI", function () {
 | 
			
		||||
        api.timeOfInterest(6);
 | 
			
		||||
        api.bounds({
 | 
			
		||||
            start: 1,
 | 
			
		||||
            end: 10
 | 
			
		||||
        });
 | 
			
		||||
        expect(api.timeOfInterest()).toEqual(6);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("If bounds are set and TOI lies outside them, reset TOI", function () {
 | 
			
		||||
        api.timeOfInterest(11);
 | 
			
		||||
        api.bounds({
 | 
			
		||||
            start: 1,
 | 
			
		||||
            end: 10
 | 
			
		||||
        });
 | 
			
		||||
        expect(api.timeOfInterest()).toBeUndefined();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Maintains delta during tick", function () {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Allows registered time system to be activated", function () {
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Allows a registered tick source to be activated", function () {
 | 
			
		||||
        const mockTickSource = jasmine.createSpyObj("mockTickSource", [
 | 
			
		||||
            "on",
 | 
			
		||||
            "off",
 | 
			
		||||
            "currentValue"
 | 
			
		||||
        ]);
 | 
			
		||||
        mockTickSource.key = 'mockTickSource';
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe(" when enabling a tick source", function () {
 | 
			
		||||
        let mockTickSource;
 | 
			
		||||
        let anotherMockTickSource;
 | 
			
		||||
        const mockOffsets = {
 | 
			
		||||
            start: 0,
 | 
			
		||||
            end: 1
 | 
			
		||||
        };
 | 
			
		||||
define(['./TimeAPI'], function (TimeAPI) {
 | 
			
		||||
    describe("The Time API", function () {
 | 
			
		||||
        let api;
 | 
			
		||||
        let timeSystemKey;
 | 
			
		||||
        let timeSystem;
 | 
			
		||||
        let clockKey;
 | 
			
		||||
        let clock;
 | 
			
		||||
        let bounds;
 | 
			
		||||
        let eventListener;
 | 
			
		||||
        let toi;
 | 
			
		||||
 | 
			
		||||
        beforeEach(function () {
 | 
			
		||||
            mockTickSource = jasmine.createSpyObj("clock", [
 | 
			
		||||
            api = new TimeAPI();
 | 
			
		||||
            timeSystemKey = "timeSystemKey";
 | 
			
		||||
            timeSystem = {key: timeSystemKey};
 | 
			
		||||
            clockKey = "someClockKey";
 | 
			
		||||
            clock = jasmine.createSpyObj("clock", [
 | 
			
		||||
                "on",
 | 
			
		||||
                "off",
 | 
			
		||||
                "currentValue"
 | 
			
		||||
            ]);
 | 
			
		||||
            mockTickSource.currentValue.and.returnValue(10);
 | 
			
		||||
            clock.currentValue.and.returnValue(100);
 | 
			
		||||
            clock.key = clockKey;
 | 
			
		||||
            bounds = {
 | 
			
		||||
                start: 0,
 | 
			
		||||
                end: 1
 | 
			
		||||
            };
 | 
			
		||||
            eventListener = jasmine.createSpy("eventListener");
 | 
			
		||||
            toi = 111;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Supports setting and querying of time of interest", function () {
 | 
			
		||||
            expect(api.timeOfInterest()).not.toBe(toi);
 | 
			
		||||
            api.timeOfInterest(toi);
 | 
			
		||||
            expect(api.timeOfInterest()).toBe(toi);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Allows setting of valid bounds", function () {
 | 
			
		||||
            bounds = {
 | 
			
		||||
                start: 0,
 | 
			
		||||
                end: 1
 | 
			
		||||
            };
 | 
			
		||||
            expect(api.bounds()).not.toBe(bounds);
 | 
			
		||||
            expect(api.bounds.bind(api, bounds)).not.toThrow();
 | 
			
		||||
            expect(api.bounds()).toEqual(bounds);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Disallows setting of invalid bounds", function () {
 | 
			
		||||
            bounds = {
 | 
			
		||||
                start: 1,
 | 
			
		||||
                end: 0
 | 
			
		||||
            };
 | 
			
		||||
            expect(api.bounds()).not.toEqual(bounds);
 | 
			
		||||
            expect(api.bounds.bind(api, bounds)).toThrow();
 | 
			
		||||
            expect(api.bounds()).not.toEqual(bounds);
 | 
			
		||||
 | 
			
		||||
            bounds = {start: 1};
 | 
			
		||||
            expect(api.bounds()).not.toEqual(bounds);
 | 
			
		||||
            expect(api.bounds.bind(api, bounds)).toThrow();
 | 
			
		||||
            expect(api.bounds()).not.toEqual(bounds);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Allows setting of previously registered time system with bounds", function () {
 | 
			
		||||
            api.addTimeSystem(timeSystem);
 | 
			
		||||
            expect(api.timeSystem()).not.toBe(timeSystem);
 | 
			
		||||
            expect(function () {
 | 
			
		||||
                api.timeSystem(timeSystem, bounds);
 | 
			
		||||
            }).not.toThrow();
 | 
			
		||||
            expect(api.timeSystem()).toBe(timeSystem);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Disallows setting of time system without bounds", function () {
 | 
			
		||||
            api.addTimeSystem(timeSystem);
 | 
			
		||||
            expect(api.timeSystem()).not.toBe(timeSystem);
 | 
			
		||||
            expect(function () {
 | 
			
		||||
                api.timeSystem(timeSystemKey);
 | 
			
		||||
            }).toThrow();
 | 
			
		||||
            expect(api.timeSystem()).not.toBe(timeSystem);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("allows setting of timesystem without bounds with clock", function () {
 | 
			
		||||
            api.addTimeSystem(timeSystem);
 | 
			
		||||
            api.addClock(clock);
 | 
			
		||||
            api.clock(clockKey, {
 | 
			
		||||
                start: 0,
 | 
			
		||||
                end: 1
 | 
			
		||||
            });
 | 
			
		||||
            expect(api.timeSystem()).not.toBe(timeSystem);
 | 
			
		||||
            expect(function () {
 | 
			
		||||
                api.timeSystem(timeSystemKey);
 | 
			
		||||
            }).not.toThrow();
 | 
			
		||||
            expect(api.timeSystem()).toBe(timeSystem);
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Emits an event when time system changes", function () {
 | 
			
		||||
            api.addTimeSystem(timeSystem);
 | 
			
		||||
            expect(eventListener).not.toHaveBeenCalled();
 | 
			
		||||
            api.on("timeSystem", eventListener);
 | 
			
		||||
            api.timeSystem(timeSystemKey, bounds);
 | 
			
		||||
            expect(eventListener).toHaveBeenCalledWith(timeSystem);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Emits an event when time of interest changes", function () {
 | 
			
		||||
            expect(eventListener).not.toHaveBeenCalled();
 | 
			
		||||
            api.on("timeOfInterest", eventListener);
 | 
			
		||||
            api.timeOfInterest(toi);
 | 
			
		||||
            expect(eventListener).toHaveBeenCalledWith(toi);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Emits an event when bounds change", function () {
 | 
			
		||||
            expect(eventListener).not.toHaveBeenCalled();
 | 
			
		||||
            api.on("bounds", eventListener);
 | 
			
		||||
            api.bounds(bounds);
 | 
			
		||||
            expect(eventListener).toHaveBeenCalledWith(bounds, false);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("If bounds are set and TOI lies inside them, do not change TOI", function () {
 | 
			
		||||
            api.timeOfInterest(6);
 | 
			
		||||
            api.bounds({
 | 
			
		||||
                start: 1,
 | 
			
		||||
                end: 10
 | 
			
		||||
            });
 | 
			
		||||
            expect(api.timeOfInterest()).toEqual(6);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("If bounds are set and TOI lies outside them, reset TOI", function () {
 | 
			
		||||
            api.timeOfInterest(11);
 | 
			
		||||
            api.bounds({
 | 
			
		||||
                start: 1,
 | 
			
		||||
                end: 10
 | 
			
		||||
            });
 | 
			
		||||
            expect(api.timeOfInterest()).toBeUndefined();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Maintains delta during tick", function () {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Allows registered time system to be activated", function () {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Allows a registered tick source to be activated", function () {
 | 
			
		||||
            const mockTickSource = jasmine.createSpyObj("mockTickSource", [
 | 
			
		||||
                "on",
 | 
			
		||||
                "off",
 | 
			
		||||
                "currentValue"
 | 
			
		||||
            ]);
 | 
			
		||||
            mockTickSource.key = 'mockTickSource';
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe(" when enabling a tick source", function () {
 | 
			
		||||
            let mockTickSource;
 | 
			
		||||
            let anotherMockTickSource;
 | 
			
		||||
            const mockOffsets = {
 | 
			
		||||
                start: 0,
 | 
			
		||||
                end: 1
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockTickSource = jasmine.createSpyObj("clock", [
 | 
			
		||||
                    "on",
 | 
			
		||||
                    "off",
 | 
			
		||||
                    "currentValue"
 | 
			
		||||
                ]);
 | 
			
		||||
                mockTickSource.currentValue.and.returnValue(10);
 | 
			
		||||
                mockTickSource.key = "mts";
 | 
			
		||||
 | 
			
		||||
                anotherMockTickSource = jasmine.createSpyObj("clock", [
 | 
			
		||||
                    "on",
 | 
			
		||||
                    "off",
 | 
			
		||||
                    "currentValue"
 | 
			
		||||
                ]);
 | 
			
		||||
                anotherMockTickSource.key = "amts";
 | 
			
		||||
                anotherMockTickSource.currentValue.and.returnValue(10);
 | 
			
		||||
 | 
			
		||||
                api.addClock(mockTickSource);
 | 
			
		||||
                api.addClock(anotherMockTickSource);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("sets bounds based on current value", function () {
 | 
			
		||||
                api.clock("mts", mockOffsets);
 | 
			
		||||
                expect(api.bounds()).toEqual({
 | 
			
		||||
                    start: 10,
 | 
			
		||||
                    end: 11
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("a new tick listener is registered", function () {
 | 
			
		||||
                api.clock("mts", mockOffsets);
 | 
			
		||||
                expect(mockTickSource.on).toHaveBeenCalledWith("tick", jasmine.any(Function));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("listener of existing tick source is reregistered", function () {
 | 
			
		||||
                api.clock("mts", mockOffsets);
 | 
			
		||||
                api.clock("amts", mockOffsets);
 | 
			
		||||
                expect(mockTickSource.off).toHaveBeenCalledWith("tick", jasmine.any(Function));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("Allows the active clock to be set and unset", function () {
 | 
			
		||||
                expect(api.clock()).toBeUndefined();
 | 
			
		||||
                api.clock("mts", mockOffsets);
 | 
			
		||||
                expect(api.clock()).toBeDefined();
 | 
			
		||||
                api.stopClock();
 | 
			
		||||
                expect(api.clock()).toBeUndefined();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("on tick, observes offsets, and indicates tick in bounds callback", function () {
 | 
			
		||||
            const mockTickSource = jasmine.createSpyObj("clock", [
 | 
			
		||||
                "on",
 | 
			
		||||
                "off",
 | 
			
		||||
                "currentValue"
 | 
			
		||||
            ]);
 | 
			
		||||
            mockTickSource.currentValue.and.returnValue(100);
 | 
			
		||||
            let tickCallback;
 | 
			
		||||
            const boundsCallback = jasmine.createSpy("boundsCallback");
 | 
			
		||||
            const clockOffsets = {
 | 
			
		||||
                start: -100,
 | 
			
		||||
                end: 100
 | 
			
		||||
            };
 | 
			
		||||
            mockTickSource.key = "mts";
 | 
			
		||||
 | 
			
		||||
            anotherMockTickSource = jasmine.createSpyObj("clock", [
 | 
			
		||||
                "on",
 | 
			
		||||
                "off",
 | 
			
		||||
                "currentValue"
 | 
			
		||||
            ]);
 | 
			
		||||
            anotherMockTickSource.key = "amts";
 | 
			
		||||
            anotherMockTickSource.currentValue.and.returnValue(10);
 | 
			
		||||
 | 
			
		||||
            api.addClock(mockTickSource);
 | 
			
		||||
            api.addClock(anotherMockTickSource);
 | 
			
		||||
            api.clock("mts", clockOffsets);
 | 
			
		||||
 | 
			
		||||
            api.on("bounds", boundsCallback);
 | 
			
		||||
 | 
			
		||||
            tickCallback = mockTickSource.on.calls.mostRecent().args[1];
 | 
			
		||||
            tickCallback(1000);
 | 
			
		||||
            expect(boundsCallback).toHaveBeenCalledWith({
 | 
			
		||||
                start: 900,
 | 
			
		||||
                end: 1100
 | 
			
		||||
            }, true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("sets bounds based on current value", function () {
 | 
			
		||||
            api.clock("mts", mockOffsets);
 | 
			
		||||
            expect(api.bounds()).toEqual({
 | 
			
		||||
                start: 10,
 | 
			
		||||
                end: 11
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("a new tick listener is registered", function () {
 | 
			
		||||
            api.clock("mts", mockOffsets);
 | 
			
		||||
            expect(mockTickSource.on).toHaveBeenCalledWith("tick", jasmine.any(Function));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("listener of existing tick source is reregistered", function () {
 | 
			
		||||
            api.clock("mts", mockOffsets);
 | 
			
		||||
            api.clock("amts", mockOffsets);
 | 
			
		||||
            expect(mockTickSource.off).toHaveBeenCalledWith("tick", jasmine.any(Function));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("Allows the active clock to be set and unset", function () {
 | 
			
		||||
            expect(api.clock()).toBeUndefined();
 | 
			
		||||
            api.clock("mts", mockOffsets);
 | 
			
		||||
            expect(api.clock()).toBeDefined();
 | 
			
		||||
            api.stopClock();
 | 
			
		||||
            expect(api.clock()).toBeUndefined();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("on tick, observes offsets, and indicates tick in bounds callback", function () {
 | 
			
		||||
        const mockTickSource = jasmine.createSpyObj("clock", [
 | 
			
		||||
            "on",
 | 
			
		||||
            "off",
 | 
			
		||||
            "currentValue"
 | 
			
		||||
        ]);
 | 
			
		||||
        mockTickSource.currentValue.and.returnValue(100);
 | 
			
		||||
        let tickCallback;
 | 
			
		||||
        const boundsCallback = jasmine.createSpy("boundsCallback");
 | 
			
		||||
        const clockOffsets = {
 | 
			
		||||
            start: -100,
 | 
			
		||||
            end: 100
 | 
			
		||||
        };
 | 
			
		||||
        mockTickSource.key = "mts";
 | 
			
		||||
 | 
			
		||||
        api.addClock(mockTickSource);
 | 
			
		||||
        api.clock("mts", clockOffsets);
 | 
			
		||||
 | 
			
		||||
        api.on("bounds", boundsCallback);
 | 
			
		||||
 | 
			
		||||
        tickCallback = mockTickSource.on.calls.mostRecent().args[1];
 | 
			
		||||
        tickCallback(1000);
 | 
			
		||||
        expect(boundsCallback).toHaveBeenCalledWith({
 | 
			
		||||
            start: 900,
 | 
			
		||||
            end: 1100
 | 
			
		||||
        }, true);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,360 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import EventEmitter from 'EventEmitter';
 | 
			
		||||
 | 
			
		||||
class TimeContext extends EventEmitter {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        //The Time System
 | 
			
		||||
        this.timeSystems = new Map();
 | 
			
		||||
 | 
			
		||||
        this.system = undefined;
 | 
			
		||||
 | 
			
		||||
        this.clocks = new Map();
 | 
			
		||||
 | 
			
		||||
        this.boundsVal = {
 | 
			
		||||
            start: undefined,
 | 
			
		||||
            end: undefined
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.activeClock = undefined;
 | 
			
		||||
        this.offsets = undefined;
 | 
			
		||||
 | 
			
		||||
        this.tick = this.tick.bind(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get or set the time system of the TimeAPI.
 | 
			
		||||
     * @param {TimeSystem | string} timeSystem
 | 
			
		||||
     * @param {module:openmct.TimeAPI~TimeConductorBounds} bounds
 | 
			
		||||
     * @fires module:openmct.TimeAPI~timeSystem
 | 
			
		||||
     * @returns {TimeSystem} The currently applied time system
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method timeSystem
 | 
			
		||||
     */
 | 
			
		||||
    timeSystem(timeSystemOrKey, bounds) {
 | 
			
		||||
        if (arguments.length >= 1) {
 | 
			
		||||
            if (arguments.length === 1 && !this.activeClock) {
 | 
			
		||||
                throw new Error(
 | 
			
		||||
                    "Must specify bounds when changing time system without "
 | 
			
		||||
                    + "an active clock."
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let timeSystem;
 | 
			
		||||
 | 
			
		||||
            if (timeSystemOrKey === undefined) {
 | 
			
		||||
                throw "Please provide a time system";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (typeof timeSystemOrKey === 'string') {
 | 
			
		||||
                timeSystem = this.timeSystems.get(timeSystemOrKey);
 | 
			
		||||
 | 
			
		||||
                if (timeSystem === undefined) {
 | 
			
		||||
                    throw "Unknown time system " + timeSystemOrKey + ". Has it been registered with 'addTimeSystem'?";
 | 
			
		||||
                }
 | 
			
		||||
            } else if (typeof timeSystemOrKey === 'object') {
 | 
			
		||||
                timeSystem = timeSystemOrKey;
 | 
			
		||||
 | 
			
		||||
                if (!this.timeSystems.has(timeSystem.key)) {
 | 
			
		||||
                    throw "Unknown time system " + timeSystem.key + ". Has it been registered with 'addTimeSystem'?";
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                throw "Attempt to set invalid time system in Time API. Please provide a previously registered time system object or key";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.system = timeSystem;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * The time system used by the time
 | 
			
		||||
             * conductor has changed. A change in Time System will always be
 | 
			
		||||
             * followed by a bounds event specifying new query bounds.
 | 
			
		||||
             *
 | 
			
		||||
             * @event module:openmct.TimeAPI~timeSystem
 | 
			
		||||
             * @property {TimeSystem} The value of the currently applied
 | 
			
		||||
             * Time System
 | 
			
		||||
             * */
 | 
			
		||||
            this.emit('timeSystem', this.system);
 | 
			
		||||
            if (bounds) {
 | 
			
		||||
                this.bounds(bounds);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.system;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clock offsets are used to calculate temporal bounds when the system is
 | 
			
		||||
     * ticking on a clock source.
 | 
			
		||||
     *
 | 
			
		||||
     * @typedef {object} ValidationResult
 | 
			
		||||
     * @property {boolean} valid Result of the validation - true or false.
 | 
			
		||||
     * @property {string} message An error message if valid is false.
 | 
			
		||||
     */
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate the given bounds. This can be used for pre-validation of bounds,
 | 
			
		||||
     * for example by views validating user inputs.
 | 
			
		||||
     * @param {TimeBounds} bounds The start and end time of the conductor.
 | 
			
		||||
     * @returns {ValidationResult} A validation error, or true if valid
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method validateBounds
 | 
			
		||||
     */
 | 
			
		||||
    validateBounds(bounds) {
 | 
			
		||||
        if ((bounds.start === undefined)
 | 
			
		||||
            || (bounds.end === undefined)
 | 
			
		||||
            || isNaN(bounds.start)
 | 
			
		||||
            || isNaN(bounds.end)
 | 
			
		||||
        ) {
 | 
			
		||||
            return {
 | 
			
		||||
                valid: false,
 | 
			
		||||
                message: "Start and end must be specified as integer values"
 | 
			
		||||
            };
 | 
			
		||||
        } else if (bounds.start > bounds.end) {
 | 
			
		||||
            return {
 | 
			
		||||
                valid: false,
 | 
			
		||||
                message: "Specified start date exceeds end bound"
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            valid: true,
 | 
			
		||||
            message: ''
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get or set the start and end time of the time conductor. Basic validation
 | 
			
		||||
     * of bounds is performed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {module:openmct.TimeAPI~TimeConductorBounds} newBounds
 | 
			
		||||
     * @throws {Error} Validation error
 | 
			
		||||
     * @fires module:openmct.TimeAPI~bounds
 | 
			
		||||
     * @returns {module:openmct.TimeAPI~TimeConductorBounds}
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method bounds
 | 
			
		||||
     */
 | 
			
		||||
    bounds(newBounds) {
 | 
			
		||||
        if (arguments.length > 0) {
 | 
			
		||||
            const validationResult = this.validateBounds(newBounds);
 | 
			
		||||
            if (validationResult.valid !== true) {
 | 
			
		||||
                throw new Error(validationResult.message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //Create a copy to avoid direct mutation of conductor bounds
 | 
			
		||||
            this.boundsVal = JSON.parse(JSON.stringify(newBounds));
 | 
			
		||||
            /**
 | 
			
		||||
             * The start time, end time, or both have been updated.
 | 
			
		||||
             * @event bounds
 | 
			
		||||
             * @memberof module:openmct.TimeAPI~
 | 
			
		||||
             * @property {TimeConductorBounds} bounds The newly updated bounds
 | 
			
		||||
             * @property {boolean} [tick] `true` if the bounds update was due to
 | 
			
		||||
             * a "tick" event (ie. was an automatic update), false otherwise.
 | 
			
		||||
             */
 | 
			
		||||
            this.emit('bounds', this.boundsVal, false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //Return a copy to prevent direct mutation of time conductor bounds.
 | 
			
		||||
        return JSON.parse(JSON.stringify(this.boundsVal));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Validate the given offsets. This can be used for pre-validation of
 | 
			
		||||
     * offsets, for example by views validating user inputs.
 | 
			
		||||
     * @param {ClockOffsets} offsets The start and end offsets from a 'now' value.
 | 
			
		||||
     * @returns { ValidationResult } A validation error, and true/false if valid or not
 | 
			
		||||
     * @memberof module:openmct.TimeAPI#
 | 
			
		||||
     * @method validateOffsets
 | 
			
		||||
     */
 | 
			
		||||
    validateOffsets(offsets) {
 | 
			
		||||
        if ((offsets.start === undefined)
 | 
			
		||||
            || (offsets.end === undefined)
 | 
			
		||||
            || isNaN(offsets.start)
 | 
			
		||||
            || isNaN(offsets.end)
 | 
			
		||||
        ) {
 | 
			
		||||
            return {
 | 
			
		||||
                valid: false,
 | 
			
		||||
                message: "Start and end offsets must be specified as integer values"
 | 
			
		||||
            };
 | 
			
		||||
        } else if (offsets.start >= offsets.end) {
 | 
			
		||||
            return {
 | 
			
		||||
                valid: false,
 | 
			
		||||
                message: "Specified start offset must be < end offset"
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            valid: true,
 | 
			
		||||
            message: ''
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @typedef {Object} TimeBounds
 | 
			
		||||
     * @property {number} start The start time displayed by the time conductor
 | 
			
		||||
     * in ms since epoch. Epoch determined by currently active time system
 | 
			
		||||
     * @property {number} end The end time displayed by the time conductor in ms
 | 
			
		||||
     * since epoch.
 | 
			
		||||
     * @memberof module:openmct.TimeAPI~
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clock offsets are used to calculate temporal bounds when the system is
 | 
			
		||||
     * ticking on a clock source.
 | 
			
		||||
     *
 | 
			
		||||
     * @typedef {object} ClockOffsets
 | 
			
		||||
     * @property {number} start A time span relative to the current value of the
 | 
			
		||||
     * ticking clock, from which start bounds will be calculated. This value must
 | 
			
		||||
     * be < 0. When a clock is active, bounds will be calculated automatically
 | 
			
		||||
     * based on the value provided by the clock, and the defined clock offsets.
 | 
			
		||||
     * @property {number} end A time span relative to the current value of the
 | 
			
		||||
     * ticking clock, from which end bounds will be calculated. This value must
 | 
			
		||||
     * be >= 0.
 | 
			
		||||
     */
 | 
			
		||||
    /**
 | 
			
		||||
     * Get or set the currently applied clock offsets. If no parameter is provided,
 | 
			
		||||
     * the current value will be returned. If provided, the new value will be
 | 
			
		||||
     * used as the new clock offsets.
 | 
			
		||||
     * @param {ClockOffsets} offsets
 | 
			
		||||
     * @returns {ClockOffsets}
 | 
			
		||||
     */
 | 
			
		||||
    clockOffsets(offsets) {
 | 
			
		||||
        if (arguments.length > 0) {
 | 
			
		||||
 | 
			
		||||
            const validationResult = this.validateOffsets(offsets);
 | 
			
		||||
            if (validationResult.valid !== true) {
 | 
			
		||||
                throw new Error(validationResult.message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.offsets = offsets;
 | 
			
		||||
 | 
			
		||||
            const currentValue = this.activeClock.currentValue();
 | 
			
		||||
            const newBounds = {
 | 
			
		||||
                start: currentValue + offsets.start,
 | 
			
		||||
                end: currentValue + offsets.end
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.bounds(newBounds);
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Event that is triggered when clock offsets change.
 | 
			
		||||
             * @event clockOffsets
 | 
			
		||||
             * @memberof module:openmct.TimeAPI~
 | 
			
		||||
             * @property {ClockOffsets} clockOffsets The newly activated clock
 | 
			
		||||
             * offsets.
 | 
			
		||||
             */
 | 
			
		||||
            this.emit("clockOffsets", offsets);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.offsets;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Stop the currently active clock from ticking, and unset it. This will
 | 
			
		||||
     * revert all views to showing a static time frame defined by the current
 | 
			
		||||
     * bounds.
 | 
			
		||||
     */
 | 
			
		||||
    stopClock() {
 | 
			
		||||
        if (this.activeClock) {
 | 
			
		||||
            this.clock(undefined, undefined);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the active clock. Tick source will be immediately subscribed to
 | 
			
		||||
     * and ticking will begin. Offsets from 'now' must also be provided. A clock
 | 
			
		||||
     * can be unset by calling {@link stopClock}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {Clock || string} keyOrClock The clock to activate, or its key
 | 
			
		||||
     * @param {ClockOffsets} offsets on each tick these will be used to calculate
 | 
			
		||||
     * the start and end bounds. This maintains a sliding time window of a fixed
 | 
			
		||||
     * width that automatically updates.
 | 
			
		||||
     * @fires module:openmct.TimeAPI~clock
 | 
			
		||||
     * @return {Clock} the currently active clock;
 | 
			
		||||
     */
 | 
			
		||||
    clock(keyOrClock, offsets) {
 | 
			
		||||
        if (arguments.length === 2) {
 | 
			
		||||
            let clock;
 | 
			
		||||
 | 
			
		||||
            if (typeof keyOrClock === 'string') {
 | 
			
		||||
                clock = this.clocks.get(keyOrClock);
 | 
			
		||||
                if (clock === undefined) {
 | 
			
		||||
                    throw "Unknown clock '" + keyOrClock + "'. Has it been registered with 'addClock'?";
 | 
			
		||||
                }
 | 
			
		||||
            } else if (typeof keyOrClock === 'object') {
 | 
			
		||||
                clock = keyOrClock;
 | 
			
		||||
                if (!this.clocks.has(clock.key)) {
 | 
			
		||||
                    throw "Unknown clock '" + keyOrClock.key + "'. Has it been registered with 'addClock'?";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const previousClock = this.activeClock;
 | 
			
		||||
            if (previousClock !== undefined) {
 | 
			
		||||
                previousClock.off("tick", this.tick);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.activeClock = clock;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * The active clock has changed. Clock can be unset by calling {@link stopClock}
 | 
			
		||||
             * @event clock
 | 
			
		||||
             * @memberof module:openmct.TimeAPI~
 | 
			
		||||
             * @property {Clock} clock The newly activated clock, or undefined
 | 
			
		||||
             * if the system is no longer following a clock source
 | 
			
		||||
             */
 | 
			
		||||
            this.emit("clock", this.activeClock);
 | 
			
		||||
 | 
			
		||||
            if (this.activeClock !== undefined) {
 | 
			
		||||
                this.clockOffsets(offsets);
 | 
			
		||||
                this.activeClock.on("tick", this.tick);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else if (arguments.length === 1) {
 | 
			
		||||
            throw "When setting the clock, clock offsets must also be provided";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.activeClock;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update bounds based on provided time and current offsets
 | 
			
		||||
     * @param {number} timestamp A time from which bounds will be calculated
 | 
			
		||||
     * using current offsets.
 | 
			
		||||
     */
 | 
			
		||||
    tick(timestamp) {
 | 
			
		||||
        if (!this.activeClock) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const newBounds = {
 | 
			
		||||
            start: timestamp + this.offsets.start,
 | 
			
		||||
            end: timestamp + this.offsets.end
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.boundsVal = newBounds;
 | 
			
		||||
        this.emit('bounds', this.boundsVal, true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default TimeContext;
 | 
			
		||||
@@ -1,155 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import TimeAPI from "./TimeAPI";
 | 
			
		||||
import {createOpenMct} from "utils/testing";
 | 
			
		||||
describe("The Independent Time API", function () {
 | 
			
		||||
    let api;
 | 
			
		||||
    let domainObjectKey;
 | 
			
		||||
    let clockKey;
 | 
			
		||||
    let clock;
 | 
			
		||||
    let bounds;
 | 
			
		||||
    let independentBounds;
 | 
			
		||||
    let eventListener;
 | 
			
		||||
    let openmct;
 | 
			
		||||
 | 
			
		||||
    beforeEach(function () {
 | 
			
		||||
        openmct = createOpenMct();
 | 
			
		||||
        api = new TimeAPI(openmct);
 | 
			
		||||
        clockKey = "someClockKey";
 | 
			
		||||
        clock = jasmine.createSpyObj("clock", [
 | 
			
		||||
            "on",
 | 
			
		||||
            "off",
 | 
			
		||||
            "currentValue"
 | 
			
		||||
        ]);
 | 
			
		||||
        clock.currentValue.and.returnValue(100);
 | 
			
		||||
        clock.key = clockKey;
 | 
			
		||||
        api.addClock(clock);
 | 
			
		||||
        domainObjectKey = 'test-key';
 | 
			
		||||
        bounds = {
 | 
			
		||||
            start: 0,
 | 
			
		||||
            end: 1
 | 
			
		||||
        };
 | 
			
		||||
        api.bounds(bounds);
 | 
			
		||||
        independentBounds = {
 | 
			
		||||
            start: 10,
 | 
			
		||||
            end: 11
 | 
			
		||||
        };
 | 
			
		||||
        eventListener = jasmine.createSpy("eventListener");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Creates an independent time context", () => {
 | 
			
		||||
        let destroyTimeContext = api.addIndependentContext(domainObjectKey, independentBounds);
 | 
			
		||||
        let timeContext = api.getIndependentContext(domainObjectKey);
 | 
			
		||||
        expect(timeContext.bounds()).toEqual(independentBounds);
 | 
			
		||||
        destroyTimeContext();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Gets an independent time context given the objectPath", () => {
 | 
			
		||||
        let destroyTimeContext = api.addIndependentContext(domainObjectKey, independentBounds);
 | 
			
		||||
        let timeContext = api.getContextForView([{
 | 
			
		||||
            identifier: {
 | 
			
		||||
                namespace: '',
 | 
			
		||||
                key: 'blah'
 | 
			
		||||
            }
 | 
			
		||||
        }, { identifier: domainObjectKey }]);
 | 
			
		||||
        expect(timeContext.bounds()).toEqual(independentBounds);
 | 
			
		||||
        destroyTimeContext();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("defaults to the global time context given the objectPath", () => {
 | 
			
		||||
        let destroyTimeContext = api.addIndependentContext(domainObjectKey, independentBounds);
 | 
			
		||||
        let timeContext = api.getContextForView([{
 | 
			
		||||
            identifier: {
 | 
			
		||||
                namespace: '',
 | 
			
		||||
                key: 'blah'
 | 
			
		||||
            }
 | 
			
		||||
        }]);
 | 
			
		||||
        expect(timeContext.bounds()).toEqual(bounds);
 | 
			
		||||
        destroyTimeContext();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Allows setting of valid bounds", function () {
 | 
			
		||||
        bounds = {
 | 
			
		||||
            start: 0,
 | 
			
		||||
            end: 1
 | 
			
		||||
        };
 | 
			
		||||
        let destroyTimeContext = api.addIndependentContext(domainObjectKey, independentBounds);
 | 
			
		||||
        let timeContext = api.getContextForView([{identifier: domainObjectKey}]);
 | 
			
		||||
        expect(timeContext.bounds()).not.toEqual(bounds);
 | 
			
		||||
        timeContext.bounds(bounds);
 | 
			
		||||
        expect(timeContext.bounds()).toEqual(bounds);
 | 
			
		||||
        destroyTimeContext();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Disallows setting of invalid bounds", function () {
 | 
			
		||||
        bounds = {
 | 
			
		||||
            start: 1,
 | 
			
		||||
            end: 0
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let destroyTimeContext = api.addIndependentContext(domainObjectKey, independentBounds);
 | 
			
		||||
        let timeContext = api.getContextForView([{identifier: domainObjectKey}]);
 | 
			
		||||
        expect(timeContext.bounds()).not.toBe(bounds);
 | 
			
		||||
 | 
			
		||||
        expect(timeContext.bounds.bind(timeContext, bounds)).toThrow();
 | 
			
		||||
        expect(timeContext.bounds()).not.toEqual(bounds);
 | 
			
		||||
 | 
			
		||||
        bounds = {start: 1};
 | 
			
		||||
        expect(timeContext.bounds()).not.toEqual(bounds);
 | 
			
		||||
        expect(timeContext.bounds.bind(timeContext, bounds)).toThrow();
 | 
			
		||||
        expect(timeContext.bounds()).not.toEqual(bounds);
 | 
			
		||||
        destroyTimeContext();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("Emits an event when bounds change", function () {
 | 
			
		||||
        let destroyTimeContext = api.addIndependentContext(domainObjectKey, independentBounds);
 | 
			
		||||
        let timeContext = api.getContextForView([{identifier: domainObjectKey}]);
 | 
			
		||||
        expect(eventListener).not.toHaveBeenCalled();
 | 
			
		||||
        timeContext.on('bounds', eventListener);
 | 
			
		||||
        timeContext.bounds(bounds);
 | 
			
		||||
        expect(eventListener).toHaveBeenCalledWith(bounds, false);
 | 
			
		||||
        destroyTimeContext();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe(" when using real time clock", function () {
 | 
			
		||||
        const mockOffsets = {
 | 
			
		||||
            start: 10,
 | 
			
		||||
            end: 11
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        it("Emits an event when bounds change based on current value", function () {
 | 
			
		||||
            let destroyTimeContext = api.addIndependentContext(domainObjectKey, independentBounds);
 | 
			
		||||
            let timeContext = api.getContextForView([{identifier: domainObjectKey}]);
 | 
			
		||||
            expect(eventListener).not.toHaveBeenCalled();
 | 
			
		||||
            timeContext.clock('someClockKey', mockOffsets);
 | 
			
		||||
            timeContext.on('bounds', eventListener);
 | 
			
		||||
            timeContext.tick(10);
 | 
			
		||||
            expect(eventListener).toHaveBeenCalledWith({
 | 
			
		||||
                start: 20,
 | 
			
		||||
                end: 21
 | 
			
		||||
            }, true);
 | 
			
		||||
            destroyTimeContext();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -63,6 +63,12 @@ define(['./Type'], function (Type) {
 | 
			
		||||
     */
 | 
			
		||||
    TypeRegistry.prototype.standardizeType = function (typeDef) {
 | 
			
		||||
        if (Object.prototype.hasOwnProperty.call(typeDef, 'label')) {
 | 
			
		||||
            console.warn(
 | 
			
		||||
                'DEPRECATION WARNING typeDef: ' + typeDef.label + '.  '
 | 
			
		||||
                + '`label` is deprecated in type definitions.  Please use '
 | 
			
		||||
                + '`name` instead.  This will cause errors in a future version '
 | 
			
		||||
                + 'of Open MCT.  For more information, see '
 | 
			
		||||
                + 'https://github.com/nasa/openmct/issues/1568');
 | 
			
		||||
            if (!typeDef.name) {
 | 
			
		||||
                typeDef.name = typeDef.label;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -78,9 +78,6 @@ class ImageExporter {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return html2canvas(element, {
 | 
			
		||||
            useCORS: true,
 | 
			
		||||
            allowTaint: true,
 | 
			
		||||
            logging: false,
 | 
			
		||||
            onclone: function (document) {
 | 
			
		||||
                if (className) {
 | 
			
		||||
                    const clonedElement = document.getElementById(exportId);
 | 
			
		||||
@@ -90,7 +87,7 @@ class ImageExporter {
 | 
			
		||||
                element.id = oldId;
 | 
			
		||||
            },
 | 
			
		||||
            removeContainer: true // Set to false to debug what html2canvas renders
 | 
			
		||||
        }).then(canvas => {
 | 
			
		||||
        }).then(function (canvas) {
 | 
			
		||||
            dialog.dismiss();
 | 
			
		||||
 | 
			
		||||
            return new Promise(function (resolve, reject) {
 | 
			
		||||
@@ -105,10 +102,9 @@ class ImageExporter {
 | 
			
		||||
 | 
			
		||||
                return canvas.toBlob(blob => resolve({ blob }), mimeType);
 | 
			
		||||
            });
 | 
			
		||||
        }).catch(error => {
 | 
			
		||||
        }, function (error) {
 | 
			
		||||
            console.log('error capturing image', error);
 | 
			
		||||
            dialog.dismiss();
 | 
			
		||||
 | 
			
		||||
            console.error('error capturing image', error);
 | 
			
		||||
            const errorDialog = overlays.dialog({
 | 
			
		||||
                iconClass: 'error',
 | 
			
		||||
                message: 'Image was not captured successfully!',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
import Agent from "../../utils/agent/Agent";
 | 
			
		||||
import DeviceClassifier from "./src/DeviceClassifier";
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
    return (openmct) => {
 | 
			
		||||
        openmct.on("start", () => {
 | 
			
		||||
            const agent = new Agent(window);
 | 
			
		||||
            DeviceClassifier(agent, window.document);
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Runs at application startup and adds a subset of the following
 | 
			
		||||
 * CSS classes to the body of the document, depending on device
 | 
			
		||||
 * attributes:
 | 
			
		||||
 *
 | 
			
		||||
 * * `mobile`: Phones or tablets.
 | 
			
		||||
 * * `phone`: Phones specifically.
 | 
			
		||||
 * * `tablet`: Tablets specifically.
 | 
			
		||||
 * * `desktop`: Non-mobile devices.
 | 
			
		||||
 * * `portrait`: Devices in a portrait-style orientation.
 | 
			
		||||
 * * `landscape`: Devices in a landscape-style orientation.
 | 
			
		||||
 * * `touch`: Device supports touch events.
 | 
			
		||||
 *
 | 
			
		||||
 * @param {utils/agent/Agent} agent
 | 
			
		||||
 *        the service used to examine the user agent
 | 
			
		||||
 * @param document the HTML DOM document object
 | 
			
		||||
 * @constructor
 | 
			
		||||
 */
 | 
			
		||||
import DeviceMatchers from "./DeviceMatchers";
 | 
			
		||||
 | 
			
		||||
export default (agent, document) => {
 | 
			
		||||
    const body = document.body;
 | 
			
		||||
 | 
			
		||||
    Object.keys(DeviceMatchers).forEach((key, index, array) => {
 | 
			
		||||
        if (DeviceMatchers[key](agent)) {
 | 
			
		||||
            body.classList.add(key);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (agent.isMobile()) {
 | 
			
		||||
        const mediaQuery = window.matchMedia("(orientation: landscape)");
 | 
			
		||||
        function eventHandler(event) {
 | 
			
		||||
            console.log("changed");
 | 
			
		||||
            if (event.matches) {
 | 
			
		||||
                body.classList.remove("portrait");
 | 
			
		||||
                body.classList.add("landscape");
 | 
			
		||||
            } else {
 | 
			
		||||
                body.classList.remove("landscape");
 | 
			
		||||
                body.classList.add("portrait");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (mediaQuery.addEventListener) {
 | 
			
		||||
            mediaQuery.addEventListener(`change`, eventHandler);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Deprecated 'MediaQueryList' API, <Safari 14, IE, <Edge 16
 | 
			
		||||
            mediaQuery.addListener(eventHandler);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -1,105 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
import DeviceClassifier from "./DeviceClassifier";
 | 
			
		||||
import DeviceMatchers from "./DeviceMatchers";
 | 
			
		||||
 | 
			
		||||
const AGENT_METHODS = [
 | 
			
		||||
    "isMobile",
 | 
			
		||||
    "isPhone",
 | 
			
		||||
    "isTablet",
 | 
			
		||||
    "isPortrait",
 | 
			
		||||
    "isLandscape",
 | 
			
		||||
    "isTouch"
 | 
			
		||||
];
 | 
			
		||||
const TEST_PERMUTATIONS = [
 | 
			
		||||
    ["isMobile", "isPhone", "isTouch", "isPortrait"],
 | 
			
		||||
    ["isMobile", "isPhone", "isTouch", "isLandscape"],
 | 
			
		||||
    ["isMobile", "isTablet", "isTouch", "isPortrait"],
 | 
			
		||||
    ["isMobile", "isTablet", "isTouch", "isLandscape"],
 | 
			
		||||
    ["isTouch"],
 | 
			
		||||
    []
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
describe("DeviceClassifier", function () {
 | 
			
		||||
    let mockAgent;
 | 
			
		||||
    let mockDocument;
 | 
			
		||||
    let mockClassList;
 | 
			
		||||
 | 
			
		||||
    beforeEach(function () {
 | 
			
		||||
        mockAgent = jasmine.createSpyObj(
 | 
			
		||||
            "agent",
 | 
			
		||||
            AGENT_METHODS
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        mockClassList = jasmine.createSpyObj("classList", ["add"]);
 | 
			
		||||
 | 
			
		||||
        mockDocument = jasmine.createSpyObj(
 | 
			
		||||
            "document",
 | 
			
		||||
            {},
 | 
			
		||||
            { body: { classList: mockClassList } }
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        AGENT_METHODS.forEach(function (m) {
 | 
			
		||||
            mockAgent[m].and.returnValue(false);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    TEST_PERMUTATIONS.forEach(function (trueMethods) {
 | 
			
		||||
        const summary =
 | 
			
		||||
      trueMethods.length === 0
 | 
			
		||||
          ? "device has no detected characteristics"
 | 
			
		||||
          : "device " + trueMethods.join(", ");
 | 
			
		||||
 | 
			
		||||
        describe("when " + summary, function () {
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                trueMethods.forEach(function (m) {
 | 
			
		||||
                    mockAgent[m].and.returnValue(true);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                // eslint-disable-next-line no-new
 | 
			
		||||
                DeviceClassifier(mockAgent, mockDocument);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("adds classes for matching, detected characteristics", function () {
 | 
			
		||||
                Object.keys(DeviceMatchers)
 | 
			
		||||
                    .filter(function (m) {
 | 
			
		||||
                        return DeviceMatchers[m](mockAgent);
 | 
			
		||||
                    })
 | 
			
		||||
                    .forEach(function (key) {
 | 
			
		||||
                        expect(mockDocument.body.classList.add).toHaveBeenCalledWith(key);
 | 
			
		||||
                    });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not add classes for non-matching characteristics", function () {
 | 
			
		||||
                Object.keys(DeviceMatchers)
 | 
			
		||||
                    .filter(function (m) {
 | 
			
		||||
                        return !DeviceMatchers[m](mockAgent);
 | 
			
		||||
                    })
 | 
			
		||||
                    .forEach(function (key) {
 | 
			
		||||
                        expect(mockDocument.body.classList.add).not.toHaveBeenCalledWith(
 | 
			
		||||
                            key
 | 
			
		||||
                        );
 | 
			
		||||
                    });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An object containing key-value pairs, where keys are symbolic of
 | 
			
		||||
 * device attributes, and values are functions that take the
 | 
			
		||||
 * `agent` as inputs and return boolean values indicating
 | 
			
		||||
 * whether or not the current device has these attributes.
 | 
			
		||||
 *
 | 
			
		||||
 * For internal use by the mobile support bundle.
 | 
			
		||||
 *
 | 
			
		||||
 * @memberof src/plugins/DeviceClassifier
 | 
			
		||||
 * @private
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    mobile: function (agent) {
 | 
			
		||||
        return agent.isMobile();
 | 
			
		||||
    },
 | 
			
		||||
    phone: function (agent) {
 | 
			
		||||
        return agent.isPhone();
 | 
			
		||||
    },
 | 
			
		||||
    tablet: function (agent) {
 | 
			
		||||
        return agent.isTablet();
 | 
			
		||||
    },
 | 
			
		||||
    desktop: function (agent) {
 | 
			
		||||
        return !agent.isMobile();
 | 
			
		||||
    },
 | 
			
		||||
    portrait: function (agent) {
 | 
			
		||||
        return agent.isPortrait();
 | 
			
		||||
    },
 | 
			
		||||
    landscape: function (agent) {
 | 
			
		||||
        return agent.isLandscape();
 | 
			
		||||
    },
 | 
			
		||||
    touch: function (agent) {
 | 
			
		||||
        return agent.isTouch();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -1,65 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
import DeviceMatchers from "./DeviceMatchers";
 | 
			
		||||
 | 
			
		||||
describe("DeviceMatchers", function () {
 | 
			
		||||
    let mockAgent;
 | 
			
		||||
 | 
			
		||||
    beforeEach(function () {
 | 
			
		||||
        mockAgent = jasmine.createSpyObj("agent", [
 | 
			
		||||
            "isMobile",
 | 
			
		||||
            "isPhone",
 | 
			
		||||
            "isTablet",
 | 
			
		||||
            "isPortrait",
 | 
			
		||||
            "isLandscape",
 | 
			
		||||
            "isTouch"
 | 
			
		||||
        ]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("detects when a device is a desktop device", function () {
 | 
			
		||||
        mockAgent.isMobile.and.returnValue(false);
 | 
			
		||||
        expect(DeviceMatchers.desktop(mockAgent)).toBe(true);
 | 
			
		||||
        mockAgent.isMobile.and.returnValue(true);
 | 
			
		||||
        expect(DeviceMatchers.desktop(mockAgent)).toBe(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    function method(deviceType) {
 | 
			
		||||
        return "is" + deviceType[0].toUpperCase() + deviceType.slice(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [
 | 
			
		||||
        "mobile",
 | 
			
		||||
        "phone",
 | 
			
		||||
        "tablet",
 | 
			
		||||
        "landscape",
 | 
			
		||||
        "portrait",
 | 
			
		||||
        "landscape",
 | 
			
		||||
        "touch"
 | 
			
		||||
    ].forEach(function (deviceType) {
 | 
			
		||||
        it("detects when a device is a " + deviceType + " device", function () {
 | 
			
		||||
            mockAgent[method(deviceType)].and.returnValue(true);
 | 
			
		||||
            expect(DeviceMatchers[deviceType](mockAgent)).toBe(true);
 | 
			
		||||
            mockAgent[method(deviceType)].and.returnValue(false);
 | 
			
		||||
            expect(DeviceMatchers[deviceType](mockAgent)).toBe(false);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,78 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
function inSelectionPath(openmct, domainObject) {
 | 
			
		||||
    const domainObjectIdentifier = domainObject.identifier;
 | 
			
		||||
 | 
			
		||||
    return openmct.selection.get().some(selectionPath => {
 | 
			
		||||
        return selectionPath.some(objectInPath => {
 | 
			
		||||
            const objectInPathIdentifier = objectInPath.context.item.identifier;
 | 
			
		||||
 | 
			
		||||
            return openmct.objects.areIdsEqual(objectInPathIdentifier, domainObjectIdentifier);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class ClearDataAction {
 | 
			
		||||
    constructor(openmct, appliesToObjects) {
 | 
			
		||||
        this.name = 'Clear Data for Object';
 | 
			
		||||
        this.key = 'clear-data-action';
 | 
			
		||||
        this.description = 'Clears current data for object, unsubscribes and resubscribes to data';
 | 
			
		||||
        this.cssClass = 'icon-clear-data';
 | 
			
		||||
 | 
			
		||||
        this._openmct = openmct;
 | 
			
		||||
        this._appliesToObjects = appliesToObjects;
 | 
			
		||||
    }
 | 
			
		||||
    invoke(objectPath) {
 | 
			
		||||
        let domainObject = null;
 | 
			
		||||
        if (objectPath) {
 | 
			
		||||
            domainObject = objectPath[0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._openmct.objectViews.emit('clearData', domainObject);
 | 
			
		||||
    }
 | 
			
		||||
    appliesTo(objectPath) {
 | 
			
		||||
        if (!objectPath) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const contextualDomainObject = objectPath[0];
 | 
			
		||||
        // first check to see if this action applies to this sort of object at all
 | 
			
		||||
        const appliesToThisObject = this._appliesToObjects.some(type => {
 | 
			
		||||
            return contextualDomainObject.type === type;
 | 
			
		||||
        });
 | 
			
		||||
        if (!appliesToThisObject) {
 | 
			
		||||
            // we've selected something not applicable
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const objectInSelectionPath = inSelectionPath(this._openmct, contextualDomainObject);
 | 
			
		||||
        if (objectInSelectionPath) {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            // if this it doesn't match up, check to see if we're in a composition (i.e., layout)
 | 
			
		||||
            const routerPath = this._openmct.router.path[0];
 | 
			
		||||
 | 
			
		||||
            return routerPath.type === 'layout';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -20,24 +20,22 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<li class="c-inspect-properties__row">
 | 
			
		||||
    <div class="c-inspect-properties__label">
 | 
			
		||||
        {{ detail.name }}
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="c-inspect-properties__value">
 | 
			
		||||
        {{ detail.value }}
 | 
			
		||||
    </div>
 | 
			
		||||
</li>
 | 
			
		||||
</template>
 | 
			
		||||
export default class ClearDataAction {
 | 
			
		||||
    constructor(openmct, appliesToObjects) {
 | 
			
		||||
        this.name = 'Clear Data for Object';
 | 
			
		||||
        this.key = 'clear-data-action';
 | 
			
		||||
        this.description = 'Clears current data for object, unsubscribes and resubscribes to data';
 | 
			
		||||
        this.cssClass = 'icon-clear-data';
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
    props: {
 | 
			
		||||
        detail: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
        this._openmct = openmct;
 | 
			
		||||
        this._appliesToObjects = appliesToObjects;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
    invoke(objectPath) {
 | 
			
		||||
        this._openmct.objectViews.emit('clearData', objectPath[0]);
 | 
			
		||||
    }
 | 
			
		||||
    appliesTo(objectPath) {
 | 
			
		||||
        let contextualDomainObject = objectPath[0];
 | 
			
		||||
 | 
			
		||||
        return this._appliesToObjects.filter(type => contextualDomainObject.type === type).length;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    './components/globalClearIndicator.vue',
 | 
			
		||||
    './ClearDataAction',
 | 
			
		||||
    './clearDataAction',
 | 
			
		||||
    'vue'
 | 
			
		||||
], function (
 | 
			
		||||
    GlobaClearIndicator,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,140 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import ClearDataActionPlugin from '../plugin.js';
 | 
			
		||||
import ClearDataAction from '../ClearDataAction.js';
 | 
			
		||||
 | 
			
		||||
describe('When the Clear Data Plugin is installed,', () => {
 | 
			
		||||
    const mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']);
 | 
			
		||||
    const mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']);
 | 
			
		||||
    const mockActionsProvider = jasmine.createSpyObj('actions', ['register']);
 | 
			
		||||
    const goodMockSelectionPath = [[{
 | 
			
		||||
        context: {
 | 
			
		||||
            item: {
 | 
			
		||||
                identifier: {
 | 
			
		||||
                    key: 'apple',
 | 
			
		||||
                    namespace: ''
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }]];
 | 
			
		||||
 | 
			
		||||
    const openmct = {
 | 
			
		||||
        objectViews: mockObjectViews,
 | 
			
		||||
        indicators: mockIndicatorProvider,
 | 
			
		||||
        actions: mockActionsProvider,
 | 
			
		||||
        install: function (plugin) {
 | 
			
		||||
            plugin(this);
 | 
			
		||||
        },
 | 
			
		||||
        selection: {
 | 
			
		||||
            get: function () {
 | 
			
		||||
                return goodMockSelectionPath;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        objects: {
 | 
			
		||||
            areIdsEqual: function (obj1, obj2) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const mockObjectPath = [
 | 
			
		||||
        {
 | 
			
		||||
            name: 'mockObject1',
 | 
			
		||||
            type: 'apple'
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            name: 'mockObject2',
 | 
			
		||||
            type: 'banana'
 | 
			
		||||
        }
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    it('Global Clear Indicator is installed', () => {
 | 
			
		||||
        openmct.install(ClearDataActionPlugin([]));
 | 
			
		||||
 | 
			
		||||
        expect(mockIndicatorProvider.add).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('Clear Data context menu action is installed', () => {
 | 
			
		||||
        openmct.install(ClearDataActionPlugin([]));
 | 
			
		||||
 | 
			
		||||
        expect(mockActionsProvider.register).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('clear data action emits a clearData event when invoked', () => {
 | 
			
		||||
        const action = new ClearDataAction(openmct);
 | 
			
		||||
 | 
			
		||||
        action.invoke(mockObjectPath);
 | 
			
		||||
 | 
			
		||||
        expect(mockObjectViews.emit).toHaveBeenCalledWith('clearData', mockObjectPath[0]);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('clears data on applicable objects', () => {
 | 
			
		||||
        let action = new ClearDataAction(openmct, ['apple']);
 | 
			
		||||
 | 
			
		||||
        const actionApplies = action.appliesTo(mockObjectPath);
 | 
			
		||||
 | 
			
		||||
        expect(actionApplies).toBe(true);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('does not clear data on inapplicable objects', () => {
 | 
			
		||||
        let action = new ClearDataAction(openmct, ['pineapple']);
 | 
			
		||||
 | 
			
		||||
        const actionApplies = action.appliesTo(mockObjectPath);
 | 
			
		||||
 | 
			
		||||
        expect(actionApplies).toBe(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('does not clear data if not in the selection path and not a layout', () => {
 | 
			
		||||
        openmct.objects = {
 | 
			
		||||
            areIdsEqual: function (obj1, obj2) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        openmct.router = {
 | 
			
		||||
            path: [{type: 'not-a-layout'}]
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let action = new ClearDataAction(openmct, ['apple']);
 | 
			
		||||
 | 
			
		||||
        const actionApplies = action.appliesTo(mockObjectPath);
 | 
			
		||||
 | 
			
		||||
        expect(actionApplies).toBe(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('does clear data if not in the selection path and is a layout', () => {
 | 
			
		||||
        openmct.objects = {
 | 
			
		||||
            areIdsEqual: function (obj1, obj2) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        openmct.router = {
 | 
			
		||||
            path: [{type: 'layout'}]
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let action = new ClearDataAction(openmct, ['apple']);
 | 
			
		||||
 | 
			
		||||
        const actionApplies = action.appliesTo(mockObjectPath);
 | 
			
		||||
 | 
			
		||||
        expect(actionApplies).toBe(true);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										64
									
								
								src/plugins/clearData/test/clearDataActionSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/plugins/clearData/test/clearDataActionSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import ClearDataActionPlugin from '../plugin.js';
 | 
			
		||||
import ClearDataAction from '../clearDataAction.js';
 | 
			
		||||
 | 
			
		||||
describe('When the Clear Data Plugin is installed,', function () {
 | 
			
		||||
    const mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']);
 | 
			
		||||
    const mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']);
 | 
			
		||||
    const mockActionsProvider = jasmine.createSpyObj('actions', ['register']);
 | 
			
		||||
 | 
			
		||||
    const openmct = {
 | 
			
		||||
        objectViews: mockObjectViews,
 | 
			
		||||
        indicators: mockIndicatorProvider,
 | 
			
		||||
        actions: mockActionsProvider,
 | 
			
		||||
        install: function (plugin) {
 | 
			
		||||
            plugin(this);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const mockObjectPath = [
 | 
			
		||||
        {name: 'mockObject1'},
 | 
			
		||||
        {name: 'mockObject2'}
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    it('Global Clear Indicator is installed', function () {
 | 
			
		||||
        openmct.install(ClearDataActionPlugin([]));
 | 
			
		||||
 | 
			
		||||
        expect(mockIndicatorProvider.add).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('Clear Data context menu action is installed', function () {
 | 
			
		||||
        openmct.install(ClearDataActionPlugin([]));
 | 
			
		||||
 | 
			
		||||
        expect(mockActionsProvider.register).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('clear data action emits a clearData event when invoked', function () {
 | 
			
		||||
        let action = new ClearDataAction(openmct);
 | 
			
		||||
 | 
			
		||||
        action.invoke(mockObjectPath);
 | 
			
		||||
 | 
			
		||||
        expect(mockObjectViews.emit).toHaveBeenCalledWith('clearData', mockObjectPath[0]);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,99 +0,0 @@
 | 
			
		||||
<!--
 | 
			
		||||
 Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 Administration. All rights reserved.
 | 
			
		||||
 | 
			
		||||
 Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 You may obtain a copy of the License at
 | 
			
		||||
 http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 | 
			
		||||
 Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 License for the specific language governing permissions and limitations
 | 
			
		||||
 under the License.
 | 
			
		||||
 | 
			
		||||
 Open MCT includes source code licensed under additional open source
 | 
			
		||||
 licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div class="l-angular-ov-wrapper">
 | 
			
		||||
    <div class="u-contents">
 | 
			
		||||
        <div class="c-clock l-time-display u-style-receiver js-style-receiver">
 | 
			
		||||
            <div class="c-clock__timezone">
 | 
			
		||||
                {{ timeZoneAbbr }}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="c-clock__value">
 | 
			
		||||
                {{ timeTextValue }}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="c-clock__ampm">
 | 
			
		||||
                {{ timeAmPm }}
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import moment from 'moment';
 | 
			
		||||
import momentTimezone from 'moment-timezone';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct', 'domainObject'],
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            lastTimestamp: null
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        configuration() {
 | 
			
		||||
            return this.domainObject.configuration;
 | 
			
		||||
        },
 | 
			
		||||
        baseFormat() {
 | 
			
		||||
            return this.configuration.baseFormat;
 | 
			
		||||
        },
 | 
			
		||||
        use24() {
 | 
			
		||||
            return this.configuration.use24 === 'clock24';
 | 
			
		||||
        },
 | 
			
		||||
        timezone() {
 | 
			
		||||
            return this.configuration.timezone;
 | 
			
		||||
        },
 | 
			
		||||
        timeFormat() {
 | 
			
		||||
            return this.use24 ? this.baseFormat.replace('hh', "HH") : this.baseFormat;
 | 
			
		||||
        },
 | 
			
		||||
        zoneName() {
 | 
			
		||||
            return momentTimezone.tz.names().includes(this.timezone) ? this.timezone : "UTC";
 | 
			
		||||
        },
 | 
			
		||||
        momentTime() {
 | 
			
		||||
            return this.zoneName ? moment.utc(this.lastTimestamp).tz(this.zoneName) : moment.utc(this.lastTimestamp);
 | 
			
		||||
        },
 | 
			
		||||
        timeZoneAbbr() {
 | 
			
		||||
            return this.momentTime.zoneAbbr();
 | 
			
		||||
        },
 | 
			
		||||
        timeTextValue() {
 | 
			
		||||
            return this.timeFormat && this.momentTime.format(this.timeFormat);
 | 
			
		||||
        },
 | 
			
		||||
        timeAmPm() {
 | 
			
		||||
            return this.use24 ? '' : this.momentTime.format("A");
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        const TickerService = this.openmct.$injector.get('tickerService');
 | 
			
		||||
        this.unlisten = TickerService.listen(this.tick);
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        if (this.unlisten) {
 | 
			
		||||
            this.unlisten();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        tick(timestamp) {
 | 
			
		||||
            this.lastTimestamp = timestamp;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
@@ -1,64 +0,0 @@
 | 
			
		||||
<!--
 | 
			
		||||
 Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 Administration. All rights reserved.
 | 
			
		||||
 | 
			
		||||
 Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 You may obtain a copy of the License at
 | 
			
		||||
 http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 | 
			
		||||
 Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 License for the specific language governing permissions and limitations
 | 
			
		||||
 under the License.
 | 
			
		||||
 | 
			
		||||
 Open MCT includes source code licensed under additional open source
 | 
			
		||||
 licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 this source code distribution or the Licensing information page available
 | 
			
		||||
 at runtime from the About dialog for additional information.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div class="c-indicator t-indicator-clock icon-clock no-minify c-indicator--not-clickable">
 | 
			
		||||
    <span class="label c-indicator__label">
 | 
			
		||||
        {{ timeTextValue }}
 | 
			
		||||
    </span>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import moment from 'moment';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
    props: {
 | 
			
		||||
        indicatorFormat: {
 | 
			
		||||
            type: String,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            timeTextValue: null
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.openmct.on('start', () => {
 | 
			
		||||
            const TickerService = this.openmct.$injector.get('tickerService');
 | 
			
		||||
            this.unlisten = TickerService.listen(this.tick);
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        if (this.unlisten) {
 | 
			
		||||
            this.unlisten();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        tick(timestamp) {
 | 
			
		||||
            this.timeTextValue = `${moment.utc(timestamp).format(this.indicatorFormat)} UTC`;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
@@ -1,154 +0,0 @@
 | 
			
		||||
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import ClockViewProvider from './ClockViewProvider';
 | 
			
		||||
import ClockIndicator from './components/ClockIndicator.vue';
 | 
			
		||||
 | 
			
		||||
import momentTimezone from 'moment-timezone';
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default function ClockPlugin(options) {
 | 
			
		||||
    return function install(openmct) {
 | 
			
		||||
        const CLOCK_INDICATOR_FORMAT = 'YYYY/MM/DD HH:mm:ss';
 | 
			
		||||
        openmct.types.addType('clock', {
 | 
			
		||||
            name: 'Clock',
 | 
			
		||||
            description: 'A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.',
 | 
			
		||||
            creatable: true,
 | 
			
		||||
            cssClass: 'icon-clock',
 | 
			
		||||
            initialize: function (domainObject) {
 | 
			
		||||
                domainObject.configuration = {
 | 
			
		||||
                    baseFormat: 'YYYY/MM/DD hh:mm:ss',
 | 
			
		||||
                    use24: 'clock12',
 | 
			
		||||
                    timezone: 'UTC'
 | 
			
		||||
                };
 | 
			
		||||
            },
 | 
			
		||||
            "form": [
 | 
			
		||||
                {
 | 
			
		||||
                    "key": "displayFormat",
 | 
			
		||||
                    "name": "Display Format",
 | 
			
		||||
                    control: 'select',
 | 
			
		||||
                    options: [
 | 
			
		||||
                        {
 | 
			
		||||
                            value: 'YYYY/MM/DD hh:mm:ss',
 | 
			
		||||
                            name: 'YYYY/MM/DD hh:mm:ss'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            value: 'YYYY/DDD hh:mm:ss',
 | 
			
		||||
                            name: 'YYYY/DDD hh:mm:ss'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            value: 'hh:mm:ss',
 | 
			
		||||
                            name: 'hh:mm:ss'
 | 
			
		||||
                        }
 | 
			
		||||
                    ],
 | 
			
		||||
                    cssClass: 'l-inline',
 | 
			
		||||
                    property: [
 | 
			
		||||
                        'configuration',
 | 
			
		||||
                        'baseFormat'
 | 
			
		||||
                    ]
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    control: 'select',
 | 
			
		||||
                    options: [
 | 
			
		||||
                        {
 | 
			
		||||
                            value: 'clock12',
 | 
			
		||||
                            name: '12hr'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            value: 'clock24',
 | 
			
		||||
                            name: '24hr'
 | 
			
		||||
                        }
 | 
			
		||||
                    ],
 | 
			
		||||
                    cssClass: 'l-inline',
 | 
			
		||||
                    property: [
 | 
			
		||||
                        'configuration',
 | 
			
		||||
                        'use24'
 | 
			
		||||
                    ]
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "key": "timezone",
 | 
			
		||||
                    "name": "Timezone",
 | 
			
		||||
                    "control": "autocomplete",
 | 
			
		||||
                    "options": momentTimezone.tz.names(),
 | 
			
		||||
                    property: [
 | 
			
		||||
                        'configuration',
 | 
			
		||||
                        'timezone'
 | 
			
		||||
                    ]
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        });
 | 
			
		||||
        openmct.objectViews.addProvider(new ClockViewProvider(openmct));
 | 
			
		||||
 | 
			
		||||
        if (options && options.enableClockIndicator === true) {
 | 
			
		||||
            const clockIndicator = new Vue ({
 | 
			
		||||
                components: {
 | 
			
		||||
                    ClockIndicator
 | 
			
		||||
                },
 | 
			
		||||
                provide: {
 | 
			
		||||
                    openmct
 | 
			
		||||
                },
 | 
			
		||||
                data() {
 | 
			
		||||
                    return {
 | 
			
		||||
                        indicatorFormat: CLOCK_INDICATOR_FORMAT
 | 
			
		||||
                    };
 | 
			
		||||
                },
 | 
			
		||||
                template: '<ClockIndicator :indicator-format="indicatorFormat" />'
 | 
			
		||||
            });
 | 
			
		||||
            const indicator = {
 | 
			
		||||
                element: clockIndicator.$mount().$el,
 | 
			
		||||
                key: 'clock-indicator'
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            openmct.indicators.add(indicator);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        openmct.objects.addGetInterceptor({
 | 
			
		||||
            appliesTo: (identifier, domainObject) => {
 | 
			
		||||
                return domainObject && domainObject.type === 'clock';
 | 
			
		||||
            },
 | 
			
		||||
            invoke: (identifier, domainObject) => {
 | 
			
		||||
                if (domainObject.configuration) {
 | 
			
		||||
                    return domainObject;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (domainObject.clockFormat
 | 
			
		||||
                    && domainObject.timezone) {
 | 
			
		||||
                    const baseFormat = domainObject.clockFormat[0];
 | 
			
		||||
                    const use24 = domainObject.clockFormat[1];
 | 
			
		||||
                    const timezone = domainObject.timezone;
 | 
			
		||||
 | 
			
		||||
                    domainObject.configuration = {
 | 
			
		||||
                        baseFormat,
 | 
			
		||||
                        use24,
 | 
			
		||||
                        timezone
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    openmct.objects.mutate(domainObject, 'configuration', domainObject.configuration);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return domainObject;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -1,231 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import { createOpenMct, resetApplicationState } from 'utils/testing';
 | 
			
		||||
import clockPlugin from './plugin';
 | 
			
		||||
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
describe("Clock plugin:", () => {
 | 
			
		||||
    let openmct;
 | 
			
		||||
    let clockDefinition;
 | 
			
		||||
    let element;
 | 
			
		||||
    let child;
 | 
			
		||||
    let appHolder;
 | 
			
		||||
 | 
			
		||||
    let clockDomainObject;
 | 
			
		||||
 | 
			
		||||
    function setupClock(enableClockIndicator) {
 | 
			
		||||
        return new Promise((resolve, reject) => {
 | 
			
		||||
            clockDomainObject = {
 | 
			
		||||
                identifier: {
 | 
			
		||||
                    key: 'clock',
 | 
			
		||||
                    namespace: 'test-namespace'
 | 
			
		||||
                },
 | 
			
		||||
                type: 'clock'
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            appHolder = document.createElement('div');
 | 
			
		||||
            appHolder.style.width = '640px';
 | 
			
		||||
            appHolder.style.height = '480px';
 | 
			
		||||
            document.body.appendChild(appHolder);
 | 
			
		||||
 | 
			
		||||
            openmct = createOpenMct();
 | 
			
		||||
 | 
			
		||||
            element = document.createElement('div');
 | 
			
		||||
            child = document.createElement('div');
 | 
			
		||||
            element.appendChild(child);
 | 
			
		||||
 | 
			
		||||
            openmct.install(clockPlugin({ enableClockIndicator }));
 | 
			
		||||
 | 
			
		||||
            clockDefinition = openmct.types.get('clock').definition;
 | 
			
		||||
            clockDefinition.initialize(clockDomainObject);
 | 
			
		||||
 | 
			
		||||
            openmct.on('start', resolve);
 | 
			
		||||
            openmct.start(appHolder);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    describe("Clock view:", () => {
 | 
			
		||||
        let clockViewProvider;
 | 
			
		||||
        let clockView;
 | 
			
		||||
        let clockViewObject;
 | 
			
		||||
        let mutableClockObject;
 | 
			
		||||
 | 
			
		||||
        beforeEach(async () => {
 | 
			
		||||
            await setupClock(true);
 | 
			
		||||
 | 
			
		||||
            clockViewObject = {
 | 
			
		||||
                ...clockDomainObject,
 | 
			
		||||
                id: "test-object",
 | 
			
		||||
                name: 'Clock',
 | 
			
		||||
                configuration: {
 | 
			
		||||
                    baseFormat: 'YYYY/MM/DD hh:mm:ss',
 | 
			
		||||
                    use24: 'clock12',
 | 
			
		||||
                    timezone: 'UTC'
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(clockViewObject));
 | 
			
		||||
            spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
 | 
			
		||||
 | 
			
		||||
            const applicableViews = openmct.objectViews.get(clockViewObject, [clockViewObject]);
 | 
			
		||||
            clockViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'clock.view');
 | 
			
		||||
 | 
			
		||||
            mutableClockObject = await openmct.objects.getMutable(clockViewObject.identifier);
 | 
			
		||||
 | 
			
		||||
            clockView = clockViewProvider.view(mutableClockObject);
 | 
			
		||||
            clockView.show(child);
 | 
			
		||||
 | 
			
		||||
            await Vue.nextTick();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        afterEach(() => {
 | 
			
		||||
            clockView.destroy();
 | 
			
		||||
            openmct.objects.destroyMutable(mutableClockObject);
 | 
			
		||||
            if (appHolder) {
 | 
			
		||||
                appHolder.remove();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return resetApplicationState(openmct);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("has name as Clock", () => {
 | 
			
		||||
            expect(clockDefinition.name).toEqual('Clock');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("is creatable", () => {
 | 
			
		||||
            expect(clockDefinition.creatable).toEqual(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("provides clock view", () => {
 | 
			
		||||
            expect(clockViewProvider).toBeDefined();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("renders clock element", () => {
 | 
			
		||||
            const clockElement = element.querySelectorAll('.c-clock');
 | 
			
		||||
            expect(clockElement.length).toBe(1);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("renders major elements", () => {
 | 
			
		||||
            const clockElement = element.querySelector('.c-clock');
 | 
			
		||||
            const timezone = clockElement.querySelector('.c-clock__timezone');
 | 
			
		||||
            const time = clockElement.querySelector('.c-clock__value');
 | 
			
		||||
            const amPm = clockElement.querySelector('.c-clock__ampm');
 | 
			
		||||
            const hasMajorElements = Boolean(timezone && time && amPm);
 | 
			
		||||
 | 
			
		||||
            expect(hasMajorElements).toBe(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("renders time in UTC", () => {
 | 
			
		||||
            const clockElement = element.querySelector('.c-clock');
 | 
			
		||||
            const timezone = clockElement.querySelector('.c-clock__timezone').textContent.trim();
 | 
			
		||||
 | 
			
		||||
            expect(timezone).toBe('UTC');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("updates the 24 hour option in the configuration", (done) => {
 | 
			
		||||
            expect(clockDomainObject.configuration.use24).toBe('clock12');
 | 
			
		||||
            const new24Option = 'clock24';
 | 
			
		||||
 | 
			
		||||
            openmct.objects.observe(clockViewObject, 'configuration', (changedDomainObject) => {
 | 
			
		||||
                expect(changedDomainObject.use24).toBe(new24Option);
 | 
			
		||||
                done();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            openmct.objects.mutate(clockViewObject, 'configuration.use24', new24Option);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("updates the timezone option in the configuration", (done) => {
 | 
			
		||||
            expect(clockDomainObject.configuration.timezone).toBe('UTC');
 | 
			
		||||
            const newZone = 'CST6CDT';
 | 
			
		||||
 | 
			
		||||
            openmct.objects.observe(clockViewObject, 'configuration', (changedDomainObject) => {
 | 
			
		||||
                expect(changedDomainObject.timezone).toBe(newZone);
 | 
			
		||||
                done();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            openmct.objects.mutate(clockViewObject, 'configuration.timezone', newZone);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("updates the time format option in the configuration", (done) => {
 | 
			
		||||
            expect(clockDomainObject.configuration.baseFormat).toBe('YYYY/MM/DD hh:mm:ss');
 | 
			
		||||
            const newFormat = 'hh:mm:ss';
 | 
			
		||||
 | 
			
		||||
            openmct.objects.observe(clockViewObject, 'configuration', (changedDomainObject) => {
 | 
			
		||||
                expect(changedDomainObject.baseFormat).toBe(newFormat);
 | 
			
		||||
                done();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            openmct.objects.mutate(clockViewObject, 'configuration.baseFormat', newFormat);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe("Clock Indicator view:", () => {
 | 
			
		||||
        let clockIndicator;
 | 
			
		||||
 | 
			
		||||
        afterEach(() => {
 | 
			
		||||
            if (clockIndicator) {
 | 
			
		||||
                clockIndicator.remove();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            clockIndicator = undefined;
 | 
			
		||||
            if (appHolder) {
 | 
			
		||||
                appHolder.remove();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return resetApplicationState(openmct);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("doesn't exist", async () => {
 | 
			
		||||
            await setupClock(false);
 | 
			
		||||
 | 
			
		||||
            clockIndicator = openmct.indicators.indicatorObjects
 | 
			
		||||
                .find(indicator => indicator.key === 'clock-indicator');
 | 
			
		||||
 | 
			
		||||
            const clockIndicatorMissing = clockIndicator === null || clockIndicator === undefined;
 | 
			
		||||
            expect(clockIndicatorMissing).toBe(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("exists", async () => {
 | 
			
		||||
            await setupClock(true);
 | 
			
		||||
 | 
			
		||||
            clockIndicator = openmct.indicators.indicatorObjects
 | 
			
		||||
                .find(indicator => indicator.key === 'clock-indicator').element;
 | 
			
		||||
 | 
			
		||||
            const hasClockIndicator = clockIndicator !== null && clockIndicator !== undefined;
 | 
			
		||||
            expect(hasClockIndicator).toBe(true);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("contains text", async () => {
 | 
			
		||||
            await setupClock(true);
 | 
			
		||||
 | 
			
		||||
            clockIndicator = openmct.indicators.indicatorObjects
 | 
			
		||||
                .find(indicator => indicator.key === 'clock-indicator').element;
 | 
			
		||||
 | 
			
		||||
            const clockIndicatorText = clockIndicator.textContent.trim();
 | 
			
		||||
            const textIncludesUTC = clockIndicatorText.includes('UTC');
 | 
			
		||||
 | 
			
		||||
            expect(textIncludesUTC).toBe(true);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -30,10 +30,10 @@
 | 
			
		||||
            <div v-if="staticStyle"
 | 
			
		||||
                 class="c-inspect-styles__style"
 | 
			
		||||
            >
 | 
			
		||||
                <StyleEditor class="c-inspect-styles__editor"
 | 
			
		||||
                             :style-item="staticStyle"
 | 
			
		||||
                             :is-editing="isEditing"
 | 
			
		||||
                             @persist="updateStaticStyle"
 | 
			
		||||
                <style-editor class="c-inspect-styles__editor"
 | 
			
		||||
                              :style-item="staticStyle"
 | 
			
		||||
                              :is-editing="isEditing"
 | 
			
		||||
                              @persist="updateStaticStyle"
 | 
			
		||||
                />
 | 
			
		||||
            </div>
 | 
			
		||||
            <button
 | 
			
		||||
@@ -87,10 +87,10 @@
 | 
			
		||||
                <condition-description :show-label="true"
 | 
			
		||||
                                       :condition="getCondition(conditionStyle.conditionId)"
 | 
			
		||||
                />
 | 
			
		||||
                <StyleEditor class="c-inspect-styles__editor"
 | 
			
		||||
                             :style-item="conditionStyle"
 | 
			
		||||
                             :is-editing="isEditing"
 | 
			
		||||
                             @persist="updateConditionalStyle"
 | 
			
		||||
                <style-editor class="c-inspect-styles__editor"
 | 
			
		||||
                              :style-item="conditionStyle"
 | 
			
		||||
                              :is-editing="isEditing"
 | 
			
		||||
                              @persist="updateConditionalStyle"
 | 
			
		||||
                />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -240,10 +240,10 @@ export default {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let vm = new Vue({
 | 
			
		||||
                components: {ConditionSetSelectorDialog},
 | 
			
		||||
                provide: {
 | 
			
		||||
                    openmct: this.openmct
 | 
			
		||||
                },
 | 
			
		||||
                components: {ConditionSetSelectorDialog},
 | 
			
		||||
                data() {
 | 
			
		||||
                    return {
 | 
			
		||||
                        handleItemSelection
 | 
			
		||||
@@ -273,7 +273,10 @@ export default {
 | 
			
		||||
            this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
 | 
			
		||||
                (objectPath) => {
 | 
			
		||||
                    this.objectPath = objectPath;
 | 
			
		||||
                    this.navigateToPath = '#/browse/' + this.openmct.objects.getRelativePath(this.objectPath);
 | 
			
		||||
                    this.navigateToPath = '#/browse/' + this.objectPath
 | 
			
		||||
                        .map(o => o && this.openmct.objects.makeKeyString(o.identifier))
 | 
			
		||||
                        .reverse()
 | 
			
		||||
                        .join('/');
 | 
			
		||||
                }
 | 
			
		||||
            );
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -40,13 +40,13 @@
 | 
			
		||||
            <div v-if="staticStyle"
 | 
			
		||||
                 class="c-inspect-styles__style"
 | 
			
		||||
            >
 | 
			
		||||
                <StyleEditor class="c-inspect-styles__editor"
 | 
			
		||||
                             :style-item="staticStyle"
 | 
			
		||||
                             :is-editing="allowEditing"
 | 
			
		||||
                             :mixed-styles="mixedStyles"
 | 
			
		||||
                             :non-specific-font-properties="nonSpecificFontProperties"
 | 
			
		||||
                             @persist="updateStaticStyle"
 | 
			
		||||
                             @save-style="saveStyle"
 | 
			
		||||
                <style-editor class="c-inspect-styles__editor"
 | 
			
		||||
                              :style-item="staticStyle"
 | 
			
		||||
                              :is-editing="allowEditing"
 | 
			
		||||
                              :mixed-styles="mixedStyles"
 | 
			
		||||
                              :non-specific-font-properties="nonSpecificFontProperties"
 | 
			
		||||
                              @persist="updateStaticStyle"
 | 
			
		||||
                              @save-style="saveStyle"
 | 
			
		||||
                />
 | 
			
		||||
            </div>
 | 
			
		||||
            <button
 | 
			
		||||
@@ -108,12 +108,12 @@
 | 
			
		||||
                <condition-description :show-label="true"
 | 
			
		||||
                                       :condition="getCondition(conditionStyle.conditionId)"
 | 
			
		||||
                />
 | 
			
		||||
                <StyleEditor class="c-inspect-styles__editor"
 | 
			
		||||
                             :style-item="conditionStyle"
 | 
			
		||||
                             :non-specific-font-properties="nonSpecificFontProperties"
 | 
			
		||||
                             :is-editing="allowEditing"
 | 
			
		||||
                             @persist="updateConditionalStyle"
 | 
			
		||||
                             @save-style="saveStyle"
 | 
			
		||||
                <style-editor class="c-inspect-styles__editor"
 | 
			
		||||
                              :style-item="conditionStyle"
 | 
			
		||||
                              :non-specific-font-properties="nonSpecificFontProperties"
 | 
			
		||||
                              :is-editing="allowEditing"
 | 
			
		||||
                              @persist="updateConditionalStyle"
 | 
			
		||||
                              @save-style="saveStyle"
 | 
			
		||||
                />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -141,7 +141,6 @@ const NON_STYLEABLE_CONTAINER_TYPES = [
 | 
			
		||||
const NON_STYLEABLE_LAYOUT_ITEM_TYPES = [
 | 
			
		||||
    'line-view',
 | 
			
		||||
    'box-view',
 | 
			
		||||
    'ellipse-view',
 | 
			
		||||
    'image-view'
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@@ -297,7 +296,10 @@ export default {
 | 
			
		||||
            this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
 | 
			
		||||
                (objectPath) => {
 | 
			
		||||
                    this.objectPath = objectPath;
 | 
			
		||||
                    this.navigateToPath = '#/browse/' + this.openmct.objects.getRelativePath(this.objectPath);
 | 
			
		||||
                    this.navigateToPath = '#/browse/' + this.objectPath
 | 
			
		||||
                        .map(o => o && this.openmct.objects.makeKeyString(o.identifier))
 | 
			
		||||
                        .reverse()
 | 
			
		||||
                        .join('/');
 | 
			
		||||
                }
 | 
			
		||||
            );
 | 
			
		||||
        },
 | 
			
		||||
@@ -319,7 +321,7 @@ export default {
 | 
			
		||||
            if (item) {
 | 
			
		||||
                const type = this.openmct.types.get(item.type);
 | 
			
		||||
                if (type && type.definition) {
 | 
			
		||||
                    creatable = (type.definition.creatable !== undefined && (type.definition.creatable === 'true' || type.definition.creatable === true));
 | 
			
		||||
                    creatable = (type.definition.creatable === true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -556,10 +558,10 @@ export default {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let vm = new Vue({
 | 
			
		||||
                components: {ConditionSetSelectorDialog},
 | 
			
		||||
                provide: {
 | 
			
		||||
                    openmct: this.openmct
 | 
			
		||||
                },
 | 
			
		||||
                components: {ConditionSetSelectorDialog},
 | 
			
		||||
                data() {
 | 
			
		||||
                    return {
 | 
			
		||||
                        handleItemSelection
 | 
			
		||||
 
 | 
			
		||||
@@ -230,7 +230,7 @@ describe('the plugin', function () {
 | 
			
		||||
        };
 | 
			
		||||
        const staticStyle = {
 | 
			
		||||
            "style": {
 | 
			
		||||
                "backgroundColor": "#666666",
 | 
			
		||||
                "backgroundColor": "#717171",
 | 
			
		||||
                "border": "1px solid #00ffff"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
@@ -238,7 +238,7 @@ describe('the plugin', function () {
 | 
			
		||||
            "conditionId": "39584410-cbf9-499e-96dc-76f27e69885d",
 | 
			
		||||
            "style": {
 | 
			
		||||
                "isStyleInvisible": "",
 | 
			
		||||
                "backgroundColor": "#666666",
 | 
			
		||||
                "backgroundColor": "#717171",
 | 
			
		||||
                "border": "1px solid #ffff00"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
@@ -250,7 +250,7 @@ describe('the plugin', function () {
 | 
			
		||||
                "configuration": {
 | 
			
		||||
                    "items": [
 | 
			
		||||
                        {
 | 
			
		||||
                            "fill": "#666666",
 | 
			
		||||
                            "fill": "#717171",
 | 
			
		||||
                            "stroke": "",
 | 
			
		||||
                            "x": 1,
 | 
			
		||||
                            "y": 1,
 | 
			
		||||
@@ -259,22 +259,12 @@ describe('the plugin', function () {
 | 
			
		||||
                            "type": "box-view",
 | 
			
		||||
                            "id": "89b88746-d325-487b-aec4-11b79afff9e8"
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            "fill": "#666666",
 | 
			
		||||
                            "stroke": "",
 | 
			
		||||
                            "x": 1,
 | 
			
		||||
                            "y": 1,
 | 
			
		||||
                            "width": 10,
 | 
			
		||||
                            "height": 5,
 | 
			
		||||
                            "type": "ellipse-view",
 | 
			
		||||
                            "id": "19b88746-d325-487b-aec4-11b79afff9z8"
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            "x": 18,
 | 
			
		||||
                            "y": 9,
 | 
			
		||||
                            "x2": 23,
 | 
			
		||||
                            "y2": 4,
 | 
			
		||||
                            "stroke": "#666666",
 | 
			
		||||
                            "stroke": "#717171",
 | 
			
		||||
                            "type": "line-view",
 | 
			
		||||
                            "id": "57d49a28-7863-43bd-9593-6570758916f0"
 | 
			
		||||
                        },
 | 
			
		||||
@@ -309,12 +299,12 @@ describe('the plugin', function () {
 | 
			
		||||
                "y": 9,
 | 
			
		||||
                "x2": 23,
 | 
			
		||||
                "y2": 4,
 | 
			
		||||
                "stroke": "#666666",
 | 
			
		||||
                "stroke": "#717171",
 | 
			
		||||
                "type": "line-view",
 | 
			
		||||
                "id": "57d49a28-7863-43bd-9593-6570758916f0"
 | 
			
		||||
            };
 | 
			
		||||
            boxLayoutItem = {
 | 
			
		||||
                "fill": "#666666",
 | 
			
		||||
                "fill": "#717171",
 | 
			
		||||
                "stroke": "",
 | 
			
		||||
                "x": 1,
 | 
			
		||||
                "y": 1,
 | 
			
		||||
 
 | 
			
		||||
@@ -29,10 +29,9 @@ const styleProps = {
 | 
			
		||||
        noneValue: NONE_VALUE,
 | 
			
		||||
        applicableForType: type => {
 | 
			
		||||
            return !type ? true : (type === 'text-view'
 | 
			
		||||
                                            || type === 'telemetry-view'
 | 
			
		||||
                                            || type === 'box-view'
 | 
			
		||||
                                            || type === 'ellipse-view'
 | 
			
		||||
                                            || type === 'subobject-view');
 | 
			
		||||
                                      || type === 'telemetry-view'
 | 
			
		||||
                                      || type === 'box-view'
 | 
			
		||||
                                      || type === 'subobject-view');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    border: {
 | 
			
		||||
@@ -42,7 +41,6 @@ const styleProps = {
 | 
			
		||||
            return !type ? true : (type === 'text-view'
 | 
			
		||||
                                            || type === 'telemetry-view'
 | 
			
		||||
                                            || type === 'box-view'
 | 
			
		||||
                                            || type === 'ellipse-view'
 | 
			
		||||
                                            || type === 'image-view'
 | 
			
		||||
                                            || type === 'line-view'
 | 
			
		||||
                                            || type === 'subobject-view');
 | 
			
		||||
 
 | 
			
		||||
@@ -38,16 +38,12 @@ a.c-condition-widget {
 | 
			
		||||
 | 
			
		||||
// Make Condition Widget expand when in a hidden frame Layout context
 | 
			
		||||
// For both static and Flexible Layouts
 | 
			
		||||
.c-so-view--conditionWidget.c-so-view--no-frame {
 | 
			
		||||
    .c-condition-widget {
 | 
			
		||||
        @include abs();
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        justify-content: center;
 | 
			
		||||
        padding: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .c-so-view__frame-controls { display: none; }
 | 
			
		||||
.c-so-view--no-frame > .c-so-view__object-view > .c-condition-widget {
 | 
			
		||||
    @include abs();
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add some margin when a Condition Widget is in a Flexible Layout
 | 
			
		||||
 
 | 
			
		||||
@@ -149,7 +149,6 @@ define(['lodash'], function (_) {
 | 
			
		||||
                        return type === 'text-view'
 | 
			
		||||
                            || type === 'telemetry-view'
 | 
			
		||||
                            || type === 'box-view'
 | 
			
		||||
                            || type === 'ellipse-view'
 | 
			
		||||
                            || type === 'image-view'
 | 
			
		||||
                            || type === 'line-view'
 | 
			
		||||
                            || type === 'subobject-view';
 | 
			
		||||
@@ -181,10 +180,6 @@ define(['lodash'], function (_) {
 | 
			
		||||
                                    "name": "Box",
 | 
			
		||||
                                    "class": "icon-box-round-corners"
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    "name": "Ellipse",
 | 
			
		||||
                                    "class": "icon-circle"
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    "name": "Line",
 | 
			
		||||
                                    "class": "icon-line-horz"
 | 
			
		||||
@@ -750,7 +745,7 @@ define(['lodash'], function (_) {
 | 
			
		||||
                        if (toolbar.remove.length === 0) {
 | 
			
		||||
                            toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)];
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (layoutItem.type === 'box-view' || layoutItem.type === 'ellipse-view') {
 | 
			
		||||
                    } else if (layoutItem.type === 'box-view') {
 | 
			
		||||
                        if (toolbar.position.length === 0) {
 | 
			
		||||
                            toolbar.position = [
 | 
			
		||||
                                getStackOrder(selectedParent, selectionPath),
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ import conditionalStylesMixin from '../mixins/objectStyles-mixin';
 | 
			
		||||
export default {
 | 
			
		||||
    makeDefinition() {
 | 
			
		||||
        return {
 | 
			
		||||
            fill: '#666666',
 | 
			
		||||
            fill: '#717171',
 | 
			
		||||
            stroke: '',
 | 
			
		||||
            x: 1,
 | 
			
		||||
            y: 1,
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,6 @@ import uuid from 'uuid';
 | 
			
		||||
import SubobjectView from './SubobjectView.vue';
 | 
			
		||||
import TelemetryView from './TelemetryView.vue';
 | 
			
		||||
import BoxView from './BoxView.vue';
 | 
			
		||||
import EllipseView from './EllipseView.vue';
 | 
			
		||||
import TextView from './TextView.vue';
 | 
			
		||||
import LineView from './LineView.vue';
 | 
			
		||||
import ImageView from './ImageView.vue';
 | 
			
		||||
@@ -113,7 +112,6 @@ const ITEM_TYPE_VIEW_MAP = {
 | 
			
		||||
    'subobject-view': SubobjectView,
 | 
			
		||||
    'telemetry-view': TelemetryView,
 | 
			
		||||
    'box-view': BoxView,
 | 
			
		||||
    'ellipse-view': EllipseView,
 | 
			
		||||
    'line-view': LineView,
 | 
			
		||||
    'text-view': TextView,
 | 
			
		||||
    'image-view': ImageView
 | 
			
		||||
 
 | 
			
		||||
@@ -28,19 +28,19 @@
 | 
			
		||||
>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-frame-edit__handle c-frame-edit__handle--nw"
 | 
			
		||||
        @mousedown.left="startResize([1,1], [-1,-1], $event)"
 | 
			
		||||
        @mousedown="startResize([1,1], [-1,-1], $event)"
 | 
			
		||||
    ></div>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-frame-edit__handle c-frame-edit__handle--ne"
 | 
			
		||||
        @mousedown.left="startResize([0,1], [1,-1], $event)"
 | 
			
		||||
        @mousedown="startResize([0,1], [1,-1], $event)"
 | 
			
		||||
    ></div>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-frame-edit__handle c-frame-edit__handle--sw"
 | 
			
		||||
        @mousedown.left="startResize([1,0], [-1,1], $event)"
 | 
			
		||||
        @mousedown="startResize([1,0], [-1,1], $event)"
 | 
			
		||||
    ></div>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-frame-edit__handle c-frame-edit__handle--se"
 | 
			
		||||
        @mousedown.left="startResize([0,0], [1,1], $event)"
 | 
			
		||||
        @mousedown="startResize([0,0], [1,1], $event)"
 | 
			
		||||
    ></div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,122 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
* as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
* Administration. All rights reserved.
 | 
			
		||||
*
 | 
			
		||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
* "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
* You may obtain a copy of the License at
 | 
			
		||||
* http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
*
 | 
			
		||||
* Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
* License for the specific language governing permissions and limitations
 | 
			
		||||
* under the License.
 | 
			
		||||
*
 | 
			
		||||
* Open MCT includes source code licensed under additional open source
 | 
			
		||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
* this source code distribution or the Licensing information page available
 | 
			
		||||
* at runtime from the About dialog for additional information.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<layout-frame
 | 
			
		||||
    :item="item"
 | 
			
		||||
    :grid-size="gridSize"
 | 
			
		||||
    :is-editing="isEditing"
 | 
			
		||||
    @move="(gridDelta) => $emit('move', gridDelta)"
 | 
			
		||||
    @endMove="() => $emit('endMove')"
 | 
			
		||||
>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-ellipse-view u-style-receiver js-style-receiver"
 | 
			
		||||
        :class="[styleClass]"
 | 
			
		||||
        :style="style"
 | 
			
		||||
    ></div>
 | 
			
		||||
</layout-frame>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import LayoutFrame from './LayoutFrame.vue';
 | 
			
		||||
import conditionalStylesMixin from '../mixins/objectStyles-mixin';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    makeDefinition() {
 | 
			
		||||
        return {
 | 
			
		||||
            fill: '#666666',
 | 
			
		||||
            stroke: '',
 | 
			
		||||
            x: 1,
 | 
			
		||||
            y: 1,
 | 
			
		||||
            width: 10,
 | 
			
		||||
            height: 10
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    components: {
 | 
			
		||||
        LayoutFrame
 | 
			
		||||
    },
 | 
			
		||||
    mixins: [conditionalStylesMixin],
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
    props: {
 | 
			
		||||
        item: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        },
 | 
			
		||||
        gridSize: {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            required: true,
 | 
			
		||||
            validator: (arr) => arr && arr.length === 2
 | 
			
		||||
                    && arr.every(el => typeof el === 'number')
 | 
			
		||||
        },
 | 
			
		||||
        index: {
 | 
			
		||||
            type: Number,
 | 
			
		||||
            required: true
 | 
			
		||||
        },
 | 
			
		||||
        initSelect: Boolean,
 | 
			
		||||
        isEditing: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        style() {
 | 
			
		||||
            if (this.itemStyle) {
 | 
			
		||||
                return this.itemStyle;
 | 
			
		||||
            } else {
 | 
			
		||||
                return {
 | 
			
		||||
                    backgroundColor: this.item.fill,
 | 
			
		||||
                    border: this.item.stroke ? '1px solid ' + this.item.stroke : ''
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        index(newIndex) {
 | 
			
		||||
            if (!this.context) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.context.index = newIndex;
 | 
			
		||||
        },
 | 
			
		||||
        item(newItem) {
 | 
			
		||||
            if (!this.context) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.context.layoutItem = newItem;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.context = {
 | 
			
		||||
            layoutItem: this.item,
 | 
			
		||||
            index: this.index
 | 
			
		||||
        };
 | 
			
		||||
        this.removeSelectable = this.openmct.selection.selectable(
 | 
			
		||||
            this.$el, this.context, this.initSelect);
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
        if (this.removeSelectable) {
 | 
			
		||||
            this.removeSelectable();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
@@ -33,7 +33,7 @@
 | 
			
		||||
    <slot></slot>
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-frame__move-bar"
 | 
			
		||||
        @mousedown.left="startMove($event)"
 | 
			
		||||
        @mousedown="isEditing ? startMove([1,1], [0,0], $event) : null"
 | 
			
		||||
    ></div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -93,11 +93,7 @@ export default {
 | 
			
		||||
                return value - this.initialPosition[index];
 | 
			
		||||
            }.bind(this));
 | 
			
		||||
        },
 | 
			
		||||
        startMove(event, posFactor = [1, 1], dimFactor = [0, 0]) {
 | 
			
		||||
            if (!this.isEditing) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        startMove(posFactor, dimFactor, event) {
 | 
			
		||||
            document.body.addEventListener('mousemove', this.continueMove);
 | 
			
		||||
            document.body.addEventListener('mouseup', this.endMove);
 | 
			
		||||
            this.dragPosition = {
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@
 | 
			
		||||
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-frame__move-bar"
 | 
			
		||||
        @mousedown.left="startDrag($event)"
 | 
			
		||||
        @mousedown="startDrag($event)"
 | 
			
		||||
    ></div>
 | 
			
		||||
    <div
 | 
			
		||||
        v-if="showFrameEdit"
 | 
			
		||||
@@ -96,7 +96,7 @@ export default {
 | 
			
		||||
            y: 10,
 | 
			
		||||
            x2: 10,
 | 
			
		||||
            y2: 5,
 | 
			
		||||
            stroke: '#666666'
 | 
			
		||||
            stroke: '#717171'
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    mixins: [conditionalStylesMixin],
 | 
			
		||||
 
 | 
			
		||||
@@ -72,7 +72,7 @@
 | 
			
		||||
<script>
 | 
			
		||||
import LayoutFrame from './LayoutFrame.vue';
 | 
			
		||||
import conditionalStylesMixin from "../mixins/objectStyles-mixin";
 | 
			
		||||
import { getDefaultNotebook, getNotebookSectionAndPage } from '@/plugins/notebook/utils/notebook-storage.js';
 | 
			
		||||
import { getDefaultNotebook } from '@/plugins/notebook/utils/notebook-storage.js';
 | 
			
		||||
 | 
			
		||||
const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5];
 | 
			
		||||
const DEFAULT_POSITION = [1, 1];
 | 
			
		||||
@@ -336,15 +336,12 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        async getContextMenuActions() {
 | 
			
		||||
            const defaultNotebook = getDefaultNotebook();
 | 
			
		||||
            const domainObject = defaultNotebook && await this.openmct.objects.get(defaultNotebook.notebookMeta.identifier);
 | 
			
		||||
 | 
			
		||||
            let defaultNotebookName;
 | 
			
		||||
            if (defaultNotebook) {
 | 
			
		||||
                const domainObject = await this.openmct.objects.get(defaultNotebook.identifier);
 | 
			
		||||
                const { section, page } = getNotebookSectionAndPage(domainObject, defaultNotebook.defaultSectionId, defaultNotebook.defaultPageId);
 | 
			
		||||
                if (section && page) {
 | 
			
		||||
                    const defaultPath = domainObject && `${domainObject.name} - ${section.name} - ${page.name}`;
 | 
			
		||||
                    defaultNotebookName = `Copy to Notebook ${defaultPath}`;
 | 
			
		||||
                }
 | 
			
		||||
                const defaultPath = domainObject && `${domainObject.name} - ${defaultNotebook.section.name} - ${defaultNotebook.page.name}`;
 | 
			
		||||
                defaultNotebookName = `Copy to Notebook ${defaultPath}`;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return CONTEXT_MENU_ACTIONS
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
.c-box-view,
 | 
			
		||||
.c-ellipse-view {
 | 
			
		||||
.c-box-view {
 | 
			
		||||
    border-width: $drawingObjBorderW !important;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: stretch;
 | 
			
		||||
@@ -9,10 +8,6 @@
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-ellipse-view {
 | 
			
		||||
    border-radius: 50%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-line-view {
 | 
			
		||||
    &.c-frame {
 | 
			
		||||
        box-shadow: none !important;
 | 
			
		||||
 
 | 
			
		||||
@@ -47,8 +47,8 @@
 | 
			
		||||
 | 
			
		||||
.is-editing {
 | 
			
		||||
    .l-shell__main-container {
 | 
			
		||||
        [s-selected],
 | 
			
		||||
        [s-selected-parent] {
 | 
			
		||||
        &[s-selected],
 | 
			
		||||
        &[s-selected-parent] {
 | 
			
		||||
            // Display grid and allow edit marquee to display in main layout holder when editing
 | 
			
		||||
            > .l-layout {
 | 
			
		||||
                background: $editUIGridColorBg;
 | 
			
		||||
 
 | 
			
		||||
@@ -186,7 +186,7 @@ describe('the plugin', function () {
 | 
			
		||||
                'configuration': {
 | 
			
		||||
                    'items': [
 | 
			
		||||
                        {
 | 
			
		||||
                            'fill': '#666666',
 | 
			
		||||
                            'fill': '#717171',
 | 
			
		||||
                            'stroke': '',
 | 
			
		||||
                            'x': 1,
 | 
			
		||||
                            'y': 1,
 | 
			
		||||
@@ -195,22 +195,12 @@ describe('the plugin', function () {
 | 
			
		||||
                            'type': 'box-view',
 | 
			
		||||
                            'id': '89b88746-d325-487b-aec4-11b79afff9e8'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            'fill': '#666666',
 | 
			
		||||
                            'stroke': '',
 | 
			
		||||
                            'x': 1,
 | 
			
		||||
                            'y': 1,
 | 
			
		||||
                            'width': 10,
 | 
			
		||||
                            'height': 10,
 | 
			
		||||
                            'type': 'ellipse-view',
 | 
			
		||||
                            'id': '19b88746-d325-487b-aec4-11b79afff9z8'
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            'x': 18,
 | 
			
		||||
                            'y': 9,
 | 
			
		||||
                            'x2': 23,
 | 
			
		||||
                            'y2': 4,
 | 
			
		||||
                            'stroke': '#666666',
 | 
			
		||||
                            'stroke': '#717171',
 | 
			
		||||
                            'type': 'line-view',
 | 
			
		||||
                            'id': '57d49a28-7863-43bd-9593-6570758916f0'
 | 
			
		||||
                        },
 | 
			
		||||
@@ -351,7 +341,7 @@ describe('the plugin', function () {
 | 
			
		||||
        it('provides controls including separators', () => {
 | 
			
		||||
            const displayLayoutToolbar = openmct.toolbars.get(selection);
 | 
			
		||||
 | 
			
		||||
            expect(displayLayoutToolbar.length).toBe(7);
 | 
			
		||||
            expect(displayLayoutToolbar.length).toBe(9);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,73 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
import ImageryTimeView from './components/ImageryTimeView.vue';
 | 
			
		||||
import Vue from "vue";
 | 
			
		||||
 | 
			
		||||
export default function ImageryTimestripViewProvider(openmct) {
 | 
			
		||||
    const type = 'example.imagery.time-strip.view';
 | 
			
		||||
 | 
			
		||||
    function hasImageTelemetry(domainObject) {
 | 
			
		||||
        const metadata = openmct.telemetry.getMetadata(domainObject);
 | 
			
		||||
        if (!metadata) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return metadata.valuesForHints(['image']).length > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        key: type,
 | 
			
		||||
        name: 'Imagery Timestrip View',
 | 
			
		||||
        cssClass: 'icon-image',
 | 
			
		||||
        canView: function (domainObject, objectPath) {
 | 
			
		||||
            let isChildOfTimeStrip = objectPath.find(object => object.type === 'time-strip');
 | 
			
		||||
 | 
			
		||||
            return hasImageTelemetry(domainObject) && isChildOfTimeStrip && !openmct.router.isNavigatedObject(objectPath);
 | 
			
		||||
        },
 | 
			
		||||
        view: function (domainObject, objectPath) {
 | 
			
		||||
            let component;
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                show: function (element) {
 | 
			
		||||
                    component = new Vue({
 | 
			
		||||
                        el: element,
 | 
			
		||||
                        components: {
 | 
			
		||||
                            ImageryTimeView
 | 
			
		||||
                        },
 | 
			
		||||
                        provide: {
 | 
			
		||||
                            openmct: openmct,
 | 
			
		||||
                            domainObject: domainObject,
 | 
			
		||||
                            objectPath: objectPath
 | 
			
		||||
                        },
 | 
			
		||||
                        template: '<imagery-time-view></imagery-time-view>'
 | 
			
		||||
 | 
			
		||||
                    });
 | 
			
		||||
                },
 | 
			
		||||
 | 
			
		||||
                destroy: function () {
 | 
			
		||||
                    component.$destroy();
 | 
			
		||||
                    component = undefined;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import ImageryViewComponent from './components/ImageryView.vue';
 | 
			
		||||
import ImageryViewLayout from './components/ImageryViewLayout.vue';
 | 
			
		||||
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,7 @@ export default class ImageryView {
 | 
			
		||||
        this.component = new Vue({
 | 
			
		||||
            el: element,
 | 
			
		||||
            components: {
 | 
			
		||||
                'imagery-view': ImageryViewComponent
 | 
			
		||||
                ImageryViewLayout
 | 
			
		||||
            },
 | 
			
		||||
            provide: {
 | 
			
		||||
                openmct: this.openmct,
 | 
			
		||||
@@ -22,8 +22,7 @@ export default class ImageryView {
 | 
			
		||||
                objectPath: this.objectPath,
 | 
			
		||||
                currentView: this
 | 
			
		||||
            },
 | 
			
		||||
            template: '<imagery-view ref="ImageryContainer"></imagery-view>'
 | 
			
		||||
 | 
			
		||||
            template: '<imagery-view-layout ref="ImageryLayout"></imagery-view-layout>'
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user