Compare commits
	
		
			67 Commits
		
	
	
		
			readme-upd
			...
			notebook-c
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					03e9afb475 | ||
| 
						 | 
					2664f5e6a1 | ||
| 
						 | 
					63f9cd449f | ||
| 
						 | 
					54220f547b | ||
| 
						 | 
					93d967c2b3 | ||
| 
						 | 
					1226459c6f | ||
| 
						 | 
					d7c9c9cb98 | ||
| 
						 | 
					24aca93370 | ||
| 
						 | 
					80355689ce | ||
| 
						 | 
					2131ef2397 | ||
| 
						 | 
					04c8961dfb | ||
| 
						 | 
					8c23d02cb5 | ||
| 
						 | 
					339de9fc58 | ||
| 
						 | 
					48c22369a1 | ||
| 
						 | 
					6506077f4d | ||
| 
						 | 
					b1b4266ff3 | ||
| 
						 | 
					42b0148f93 | ||
| 
						 | 
					9461ad8edd | ||
| 
						 | 
					40055ba955 | ||
| 
						 | 
					9cb85ad176 | ||
| 
						 | 
					f2b2953a5d | ||
| 
						 | 
					62de310686 | ||
| 
						 | 
					4b9ff67e49 | ||
| 
						 | 
					d5e32ec494 | ||
| 
						 | 
					38880ba3d1 | ||
| 
						 | 
					a99ce7733c | ||
| 
						 | 
					9f48764210 | ||
| 
						 | 
					a1aaa0dd41 | ||
| 
						 | 
					bee15e98c8 | ||
| 
						 | 
					092bbe547d | ||
| 
						 | 
					6cbe05317c | ||
| 
						 | 
					3b92fcdf6c | ||
| 
						 | 
					6dde54bd25 | ||
| 
						 | 
					359e7377ac | ||
| 
						 | 
					9f4190f781 | ||
| 
						 | 
					f3fc991a74 | ||
| 
						 | 
					2564e75fc9 | ||
| 
						 | 
					f42fe78acf | ||
| 
						 | 
					fe928a1386 | ||
| 
						 | 
					b329ed6ed5 | ||
| 
						 | 
					9b7a0d7e4c | ||
| 
						 | 
					5c15e53abb | ||
| 
						 | 
					f58b3881f2 | ||
| 
						 | 
					071a13b219 | ||
| 
						 | 
					ca66898e51 | ||
| 
						 | 
					94c7b2343a | ||
| 
						 | 
					c397c336ab | ||
| 
						 | 
					eea23f2caf | ||
| 
						 | 
					6665641c02 | ||
| 
						 | 
					c3ebf52dd2 | ||
| 
						 | 
					f8f2e7da9b | ||
| 
						 | 
					240f58b2d0 | ||
| 
						 | 
					7d3baee7b5 | ||
| 
						 | 
					1f5cb7ca42 | ||
| 
						 | 
					4a7ebe326c | ||
| 
						 | 
					10da314a4a | ||
| 
						 | 
					b3ceccd7fb | ||
| 
						 | 
					1bde4c9a0c | ||
| 
						 | 
					4b85360446 | ||
| 
						 | 
					41b860a547 | ||
| 
						 | 
					254b3db966 | ||
| 
						 | 
					cbb3f32d1e | ||
| 
						 | 
					e3bf72e77f | ||
| 
						 | 
					0b63b782cf | ||
| 
						 | 
					da39fd0c70 | ||
| 
						 | 
					96dd581a67 | ||
| 
						 | 
					2a1e322230 | 
@@ -1,36 +1,93 @@
 | 
			
		||||
version: 2
 | 
			
		||||
jobs:
 | 
			
		||||
  build:
 | 
			
		||||
version: 2.1
 | 
			
		||||
executors:
 | 
			
		||||
  linux:
 | 
			
		||||
    docker:
 | 
			
		||||
        - image: circleci/node:13-browsers
 | 
			
		||||
          environment:
 | 
			
		||||
            CHROME_BIN: "/usr/bin/google-chrome"
 | 
			
		||||
    steps:
 | 
			
		||||
        - checkout
 | 
			
		||||
        - run:
 | 
			
		||||
            name: Update npm
 | 
			
		||||
            command: 'sudo npm install -g npm@latest'
 | 
			
		||||
        - restore_cache:
 | 
			
		||||
            key: dependency-cache-{{ checksum "package.json" }}
 | 
			
		||||
        - run:
 | 
			
		||||
            name: Installing dependencies (npm install)
 | 
			
		||||
            command: npm install
 | 
			
		||||
        - save_cache:
 | 
			
		||||
            key: dependency-cache-{{ checksum "package.json" }}
 | 
			
		||||
            paths:
 | 
			
		||||
              - node_modules
 | 
			
		||||
        - run:
 | 
			
		||||
            name: npm run test:coverage
 | 
			
		||||
            command: npm run test:coverage
 | 
			
		||||
        - run:
 | 
			
		||||
            name: npm run lint
 | 
			
		||||
            command: npm run lint
 | 
			
		||||
        - store_artifacts:
 | 
			
		||||
            path: dist
 | 
			
		||||
            prefix: dist
 | 
			
		||||
 | 
			
		||||
workflows:
 | 
			
		||||
  version: 2
 | 
			
		||||
      - image: cimg/base:stable
 | 
			
		||||
orbs:
 | 
			
		||||
  node: circleci/node@4.5.1
 | 
			
		||||
  browser-tools: circleci/browser-tools@1.1.3
 | 
			
		||||
jobs:
 | 
			
		||||
  test:
 | 
			
		||||
    parameters:
 | 
			
		||||
      node-version:
 | 
			
		||||
        type: string
 | 
			
		||||
      browser:
 | 
			
		||||
        type: string
 | 
			
		||||
      always-pass:
 | 
			
		||||
        type: boolean  
 | 
			
		||||
    executor: linux
 | 
			
		||||
    steps:
 | 
			
		||||
      - checkout
 | 
			
		||||
      - restore_cache:
 | 
			
		||||
          key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}
 | 
			
		||||
      - node/install:
 | 
			
		||||
          node-version: << parameters.node-version >>
 | 
			
		||||
      - node/install-packages:
 | 
			
		||||
          override-ci-command: npm install
 | 
			
		||||
      - when: # Just to save time until caching saves the browser bin
 | 
			
		||||
          condition:
 | 
			
		||||
            equal: [ "FirefoxESR", <<parameters.browser>> ]
 | 
			
		||||
          steps:
 | 
			
		||||
            - browser-tools/install-firefox:
 | 
			
		||||
                version: "78.11.0esr" #https://archive.mozilla.org/pub/firefox/releases/          
 | 
			
		||||
      - when: # Just to save time until caching saves the browser bin
 | 
			
		||||
          condition:
 | 
			
		||||
            equal: [ "ChromeHeadless", <<parameters.browser>> ]
 | 
			
		||||
          steps:
 | 
			
		||||
            - browser-tools/install-chrome:
 | 
			
		||||
                replace-existing: false
 | 
			
		||||
      - save_cache:
 | 
			
		||||
          key: deps-{{ .Branch }}--<< parameters.node-version >>--{{ checksum "package.json" }}
 | 
			
		||||
          paths:
 | 
			
		||||
            - ~/.npm
 | 
			
		||||
            - ~/.cache
 | 
			
		||||
            - node_modules
 | 
			
		||||
      - run: npm run test:coverage -- --browsers=<<parameters.browser>> || <<parameters.always-pass>>
 | 
			
		||||
      - store_test_results:
 | 
			
		||||
          path: dist/reports/tests/
 | 
			
		||||
      - store_artifacts:
 | 
			
		||||
          path: dist/reports/
 | 
			
		||||
workflows:
 | 
			
		||||
  matrix-tests:
 | 
			
		||||
    jobs:
 | 
			
		||||
      - build
 | 
			
		||||
      - test:
 | 
			
		||||
          name: node10-chrome
 | 
			
		||||
          node-version: lts/dubnium
 | 
			
		||||
          browser: ChromeHeadless
 | 
			
		||||
          always-pass: false
 | 
			
		||||
      - test:
 | 
			
		||||
          name: node12-firefoxESR-build-only
 | 
			
		||||
          node-version: lts/erbium
 | 
			
		||||
          browser: FirefoxESR
 | 
			
		||||
          always-pass: true
 | 
			
		||||
      - test:
 | 
			
		||||
          name: node14-chrome-build-only
 | 
			
		||||
          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      
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							@@ -1,11 +1,12 @@
 | 
			
		||||
<!--- This is for filing bugs. If you have a general question, please -->
 | 
			
		||||
<!--- visit https://github.com/nasa/openmct/discussions -->
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
name: Bug Report
 | 
			
		||||
name: Bug report
 | 
			
		||||
about: File a Bug !
 | 
			
		||||
title: ''
 | 
			
		||||
labels: type:bug
 | 
			
		||||
assignees: ''
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<!--- Focus on user impact in the title. Use the Summary Field to -->
 | 
			
		||||
<!--- describe the problem technically. -->
 | 
			
		||||
 | 
			
		||||
@@ -35,7 +36,7 @@ about: File a Bug !
 | 
			
		||||
 | 
			
		||||
#### Environment
 | 
			
		||||
* Open MCT Version: <!--- date of build, version, or SHA -->
 | 
			
		||||
* Deployment Type: <!--- npm dev? VIPER Dev? openmct-yams? -->
 | 
			
		||||
* Deployment Type: <!--- npm dev? VIPER Dev? openmct-yamcs? -->
 | 
			
		||||
* OS:
 | 
			
		||||
* Browser:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							@@ -1 +1,5 @@
 | 
			
		||||
blank_issues_enabled: false
 | 
			
		||||
blank_issues_enabled: true
 | 
			
		||||
contact_links:
 | 
			
		||||
  - name: Discussions
 | 
			
		||||
    url: https://github.com/nasa/openmct/discussions
 | 
			
		||||
    about: Got a question?
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								.github/ISSUE_TEMPLATE/enhancement-request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								.github/ISSUE_TEMPLATE/enhancement-request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
---
 | 
			
		||||
name: Enhancement request
 | 
			
		||||
about: Suggest an enhancement or new improvement for this project
 | 
			
		||||
title: ''
 | 
			
		||||
labels: type:enhancement
 | 
			
		||||
assignees: ''
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
**Is your feature request related to a problem? Please describe.**
 | 
			
		||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
 | 
			
		||||
 | 
			
		||||
**Describe the solution you'd like**
 | 
			
		||||
A clear and concise description of what you want to happen.
 | 
			
		||||
 | 
			
		||||
**Describe alternatives you've considered**
 | 
			
		||||
A clear and concise description of any alternative solutions or features you've considered.
 | 
			
		||||
 | 
			
		||||
**Additional context**
 | 
			
		||||
Add any other context or screenshots about the feature request here.
 | 
			
		||||
							
								
								
									
										33
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/workflows/lighthouse.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/lighthouse.yml
									
									
									
									
										vendored
									
									
								
							@@ -13,6 +13,8 @@ jobs:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
        with:
 | 
			
		||||
          ref: ${{ github.event.inputs.version }}
 | 
			
		||||
      - uses: actions/setup-node@v1
 | 
			
		||||
      - uses: actions/setup-node@v2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: '14'
 | 
			
		||||
      - run: npm install && npm install -g @lhci/cli #Don't want to include this in our deps
 | 
			
		||||
      - run: lhci autorun
 | 
			
		||||
							
								
								
									
										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 
 | 
			
		||||
returing a 2xx status code. The URL Status Indicator is made available as a default plugin. See
 | 
			
		||||
returning 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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,8 @@ Open MCT (Open Mission Control Technologies) is a next-generation mission contro
 | 
			
		||||
 | 
			
		||||
Please visit our [Official Site](https://nasa.github.io/openmct/) and [Getting Started Guide](https://nasa.github.io/openmct/getting-started/)
 | 
			
		||||
 | 
			
		||||
Once you've created something amazing with Open MCT, showcase your work in our GitHub Discussions [Show and Tell](https://github.com/nasa/openmct/discussions/categories/show-and-tell) section. We love seeing unique and wonderful implementations of Open MCT!
 | 
			
		||||
 | 
			
		||||
## See Open MCT in Action
 | 
			
		||||
 | 
			
		||||
Try Open MCT now with our [live demo](https://openmct-demo.herokuapp.com/).
 | 
			
		||||
 
 | 
			
		||||
@@ -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 useability of documentation (`typeService` is an
 | 
			
		||||
* Improves usability of documentation (`typeService` is an
 | 
			
		||||
  instance of `CompositeService` and implements `TypeService`
 | 
			
		||||
  so you can easily traverse links in the JSDoc.)
 | 
			
		||||
* Can be used more easily from Web Workers, allowing services
 | 
			
		||||
 
 | 
			
		||||
@@ -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 transtion) may be useful as well:
 | 
			
		||||
(which is supported during the transition) may be useful as well:
 | 
			
		||||
 | 
			
		||||
 * The [Architecture Overview](architecture/) describes the concepts used
 | 
			
		||||
 throughout Open MCT, and gives a high level overview of the platform's design.
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ define([
 | 
			
		||||
 | 
			
		||||
    StateGeneratorProvider.prototype.request = function (domainObject, options) {
 | 
			
		||||
        var start = options.start;
 | 
			
		||||
        var end = options.end;
 | 
			
		||||
        var end = Math.min(Date.now(), options.end); // no future values
 | 
			
		||||
        var duration = domainObject.telemetry.duration * 1000;
 | 
			
		||||
        if (options.strategy === 'latest' || options.size === 1) {
 | 
			
		||||
            start = end;
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,10 @@ define([
 | 
			
		||||
        ];
 | 
			
		||||
        const IMAGE_DELAY = 20000;
 | 
			
		||||
 | 
			
		||||
        function getCompassValues(min, max) {
 | 
			
		||||
            return min + Math.random() * (max - min);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function pointForTimestamp(timestamp, name) {
 | 
			
		||||
            const url = IMAGE_SAMPLES[Math.floor(timestamp / IMAGE_DELAY) % IMAGE_SAMPLES.length];
 | 
			
		||||
            const urlItems = url.split('/');
 | 
			
		||||
@@ -59,6 +63,9 @@ define([
 | 
			
		||||
                utc: Math.floor(timestamp / IMAGE_DELAY) * IMAGE_DELAY,
 | 
			
		||||
                local: Math.floor(timestamp / IMAGE_DELAY) * IMAGE_DELAY,
 | 
			
		||||
                url,
 | 
			
		||||
                sunOrientation: getCompassValues(0, 360),
 | 
			
		||||
                cameraPan: getCompassValues(0, 360),
 | 
			
		||||
                heading: getCompassValues(0, 360),
 | 
			
		||||
                imageDownloadName
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import Vue from 'Vue';
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import HelloWorld from './HelloWorld.vue';
 | 
			
		||||
 | 
			
		||||
function SimpleVuePlugin() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 psuedo <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 pseudo <code>:before</code> HTML element to whatever element it's attached to that makes proper use of the symbols font.</p>
 | 
			
		||||
                <p>Alternately, you can use the <code>.ui-symbol</code> class in an object that contains encoded HTML entities. This method is only recommended if you cannot use the aforementioned CSS class approach.</p>
 | 
			
		||||
            </div>
 | 
			
		||||
            <mct-example><a class="s-button icon-gear" title="Settings"></a>
 | 
			
		||||
 
 | 
			
		||||
@@ -88,6 +88,7 @@
 | 
			
		||||
        openmct.install(openmct.plugins.ExampleImagery());
 | 
			
		||||
        openmct.install(openmct.plugins.PlanLayout());
 | 
			
		||||
        openmct.install(openmct.plugins.Timeline());
 | 
			
		||||
        openmct.install(openmct.plugins.Hyperlink());
 | 
			
		||||
        openmct.install(openmct.plugins.UTCTimeSystem());
 | 
			
		||||
        openmct.install(openmct.plugins.AutoflowView({
 | 
			
		||||
            type: "telemetry.panel"
 | 
			
		||||
@@ -194,6 +195,7 @@
 | 
			
		||||
            ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'],
 | 
			
		||||
            {indicator: true}
 | 
			
		||||
        ));
 | 
			
		||||
        openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
 | 
			
		||||
        openmct.start();
 | 
			
		||||
    </script>
 | 
			
		||||
</html>
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,9 @@
 | 
			
		||||
/*global module,process*/
 | 
			
		||||
 | 
			
		||||
const devMode = process.env.NODE_ENV !== 'production';
 | 
			
		||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'FirefoxHeadless'];
 | 
			
		||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
 | 
			
		||||
const coverageEnabled = process.env.COVERAGE === 'true';
 | 
			
		||||
const reporters = ['progress', 'html'];
 | 
			
		||||
const reporters = ['progress', 'html', 'junit'];
 | 
			
		||||
 | 
			
		||||
if (coverageEnabled) {
 | 
			
		||||
    reporters.push('coverage-istanbul');
 | 
			
		||||
@@ -59,7 +59,8 @@ module.exports = (config) => {
 | 
			
		||||
        browsers: browsers,
 | 
			
		||||
        client: {
 | 
			
		||||
            jasmine: {
 | 
			
		||||
                random: false
 | 
			
		||||
                random: false,
 | 
			
		||||
                timeoutInterval: 30000
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        customLaunchers: {
 | 
			
		||||
@@ -67,6 +68,10 @@ module.exports = (config) => {
 | 
			
		||||
                base: 'Chrome',
 | 
			
		||||
                flags: ['--remote-debugging-port=9222'],
 | 
			
		||||
                debug: true
 | 
			
		||||
            },
 | 
			
		||||
            FirefoxESR: {
 | 
			
		||||
                base: 'FirefoxHeadless',
 | 
			
		||||
                name: 'FirefoxESR'
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        colors: true,
 | 
			
		||||
@@ -78,12 +83,21 @@ module.exports = (config) => {
 | 
			
		||||
            preserveDescribeNesting: true,
 | 
			
		||||
            foldAll: false
 | 
			
		||||
        },
 | 
			
		||||
        browserConsoleLogOptions: { level: "error",  format: "%b %T: %m",  terminal: true },
 | 
			
		||||
        junitReporter: {
 | 
			
		||||
            outputDir: "dist/reports/tests",
 | 
			
		||||
            outputFile: "test-results.xml",
 | 
			
		||||
            useBrowserName: false
 | 
			
		||||
        },
 | 
			
		||||
        browserConsoleLogOptions: {
 | 
			
		||||
            level: "error",
 | 
			
		||||
            format: "%b %T: %m",
 | 
			
		||||
            terminal: true
 | 
			
		||||
        },
 | 
			
		||||
        coverageIstanbulReporter: {
 | 
			
		||||
            fixWebpackSourcePaths: true,
 | 
			
		||||
            dir: process.env.CIRCLE_ARTIFACTS ?
 | 
			
		||||
                process.env.CIRCLE_ARTIFACTS + '/coverage' :
 | 
			
		||||
                "dist/reports/coverage",
 | 
			
		||||
            dir: process.env.CIRCLE_ARTIFACTS
 | 
			
		||||
                ? process.env.CIRCLE_ARTIFACTS + '/coverage'
 | 
			
		||||
                : "dist/reports/coverage",
 | 
			
		||||
            reports: ['html', 'lcovonly', 'text-summary'],
 | 
			
		||||
            thresholds: {
 | 
			
		||||
                global: {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								package.json
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "openmct",
 | 
			
		||||
  "version": "1.7.4-SNAPSHOT",
 | 
			
		||||
  "version": "1.7.8-SNAPSHOT",
 | 
			
		||||
  "description": "The Open MCT core platform",
 | 
			
		||||
  "dependencies": {},
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
@@ -34,20 +34,21 @@
 | 
			
		||||
    "git-rev-sync": "^1.4.0",
 | 
			
		||||
    "glob": ">= 3.0.0",
 | 
			
		||||
    "html-loader": "^0.5.5",
 | 
			
		||||
    "html2canvas": "^1.0.0-alpha.12",
 | 
			
		||||
    "html2canvas": "^1.0.0-rc.7",
 | 
			
		||||
    "imports-loader": "^0.8.0",
 | 
			
		||||
    "istanbul-instrumenter-loader": "^3.0.1",
 | 
			
		||||
    "jasmine-core": "^3.1.0",
 | 
			
		||||
    "jasmine-core": "^3.7.1",
 | 
			
		||||
    "jsdoc": "^3.3.2",
 | 
			
		||||
    "karma": "5.1.1",
 | 
			
		||||
    "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": "1.3.0",
 | 
			
		||||
    "karma-junit-reporter": "2.0.1",
 | 
			
		||||
    "karma-html-reporter": "0.2.7",
 | 
			
		||||
    "karma-jasmine": "3.3.1",
 | 
			
		||||
    "karma-sourcemap-loader": "0.3.7",
 | 
			
		||||
    "karma-jasmine": "4.0.1",
 | 
			
		||||
    "karma-sourcemap-loader": "0.3.8",
 | 
			
		||||
    "karma-webpack": "4.0.2",
 | 
			
		||||
    "location-bar": "^3.0.1",
 | 
			
		||||
    "lodash": "^4.17.12",
 | 
			
		||||
@@ -89,6 +90,7 @@
 | 
			
		||||
    "test": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run",
 | 
			
		||||
    "test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
 | 
			
		||||
    "test:coverage": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" COVERAGE=true karma start --single-run",
 | 
			
		||||
    "test:coverage:firefox": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --single-run --browsers=FirefoxHeadless",
 | 
			
		||||
    "test:watch": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" karma start --no-single-run",
 | 
			
		||||
    "verify": "concurrently 'npm:test' 'npm:lint'",
 | 
			
		||||
    "jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
 | 
			
		||||
@@ -100,6 +102,9 @@
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "https://github.com/nasa/openmct.git"
 | 
			
		||||
  },
 | 
			
		||||
  "engines": {
 | 
			
		||||
    "node": ">=10.10.2 <16.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "author": "",
 | 
			
		||||
  "license": "Apache-2.0",
 | 
			
		||||
  "private": true
 | 
			
		||||
 
 | 
			
		||||
@@ -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 occured, otherwise false.
 | 
			
		||||
         * @returns {Boolean} true if navigation occurred, otherwise false.
 | 
			
		||||
         */
 | 
			
		||||
        NavigationService.prototype.setNavigation = function (domainObject, force) {
 | 
			
		||||
            if (force) {
 | 
			
		||||
 
 | 
			
		||||
@@ -33,10 +33,6 @@ define([
 | 
			
		||||
    "./src/actions/CancelAction",
 | 
			
		||||
    "./src/policies/EditPersistableObjectsPolicy",
 | 
			
		||||
    "./src/representers/EditRepresenter",
 | 
			
		||||
    "./src/capabilities/EditorCapability",
 | 
			
		||||
    "./src/capabilities/TransactionCapabilityDecorator",
 | 
			
		||||
    "./src/services/TransactionManager",
 | 
			
		||||
    "./src/services/TransactionService",
 | 
			
		||||
    "./src/creation/CreateMenuController",
 | 
			
		||||
    "./src/creation/LocatorController",
 | 
			
		||||
    "./src/creation/CreationPolicy",
 | 
			
		||||
@@ -62,10 +58,6 @@ define([
 | 
			
		||||
    CancelAction,
 | 
			
		||||
    EditPersistableObjectsPolicy,
 | 
			
		||||
    EditRepresenter,
 | 
			
		||||
    EditorCapability,
 | 
			
		||||
    TransactionCapabilityDecorator,
 | 
			
		||||
    TransactionManager,
 | 
			
		||||
    TransactionService,
 | 
			
		||||
    CreateMenuController,
 | 
			
		||||
    LocatorController,
 | 
			
		||||
    CreationPolicy,
 | 
			
		||||
@@ -263,26 +255,6 @@ define([
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "components": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "type": "decorator",
 | 
			
		||||
                        "provides": "capabilityService",
 | 
			
		||||
                        "implementation": TransactionCapabilityDecorator,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "$q",
 | 
			
		||||
                            "transactionManager"
 | 
			
		||||
                        ],
 | 
			
		||||
                        "priority": "fallback"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "type": "provider",
 | 
			
		||||
                        "provides": "transactionService",
 | 
			
		||||
                        "implementation": TransactionService,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "$q",
 | 
			
		||||
                            "$log",
 | 
			
		||||
                            "cacheService"
 | 
			
		||||
                        ]
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "CreateActionProvider",
 | 
			
		||||
                        "provides": "actionService",
 | 
			
		||||
@@ -313,33 +285,12 @@ define([
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "capabilities": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "editor",
 | 
			
		||||
                        "name": "Editor Capability",
 | 
			
		||||
                        "description": "Provides transactional editing capabilities",
 | 
			
		||||
                        "implementation": EditorCapability,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "transactionService",
 | 
			
		||||
                            "openmct"
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "controls": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "locator",
 | 
			
		||||
                        "template": locatorTemplate
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "services": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "transactionManager",
 | 
			
		||||
                        "implementation": TransactionManager,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "transactionService"
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "runs": [
 | 
			
		||||
                    {
 | 
			
		||||
                        depends: [
 | 
			
		||||
 
 | 
			
		||||
@@ -1,113 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * A capability that implements an editing 'session' for a domain
 | 
			
		||||
         * object. An editing session is initiated via a call to .edit().
 | 
			
		||||
         * Once initiated, any persist operations will be queued pending a
 | 
			
		||||
         * subsequent call to [.save()](@link #save) or [.finish()](@link
 | 
			
		||||
         * #finish).
 | 
			
		||||
         * @param transactionService
 | 
			
		||||
         * @param domainObject
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function EditorCapability(
 | 
			
		||||
            transactionService,
 | 
			
		||||
            openmct,
 | 
			
		||||
            domainObject
 | 
			
		||||
        ) {
 | 
			
		||||
            this.transactionService = transactionService;
 | 
			
		||||
            this.openmct = openmct;
 | 
			
		||||
            this.domainObject = domainObject;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Initiate an editing session. This will start a transaction during
 | 
			
		||||
         * which any persist operations will be deferred until either save()
 | 
			
		||||
         * 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);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Determines whether this object, or any of its ancestors are
 | 
			
		||||
         * currently being edited.
 | 
			
		||||
         * @returns boolean
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.inEditContext = function () {
 | 
			
		||||
            return this.openmct.editor.isEditing();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Is this the root editing object (ie. the object that the user
 | 
			
		||||
         * clicked 'edit' on)?
 | 
			
		||||
         * @returns {*}
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.isEditContextRoot = function () {
 | 
			
		||||
            return this.openmct.editor.isEditing();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Save any unsaved changes from this editing session. This will
 | 
			
		||||
         * end the current transaction and continue with a new one.
 | 
			
		||||
         * @returns {*}
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.save = function () {
 | 
			
		||||
            console.warn('DEPRECATED: cannot save via edit capability, use openmct.editor instead.');
 | 
			
		||||
 | 
			
		||||
            return Promise.resolve();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        EditorCapability.prototype.invoke = EditorCapability.prototype.edit;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Finish the current editing session. This will discard any pending
 | 
			
		||||
         * persist operations
 | 
			
		||||
         * @returns {*}
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.finish = function () {
 | 
			
		||||
            console.warn('DEPRECATED: cannot finish via edit capability, use openmct.editor instead.');
 | 
			
		||||
 | 
			
		||||
            return Promise.resolve();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @returns {boolean} true if there have been any domain model
 | 
			
		||||
         * modifications since the last persist, false otherwise.
 | 
			
		||||
         */
 | 
			
		||||
        EditorCapability.prototype.dirty = function () {
 | 
			
		||||
            return this.transactionService.size() > 0;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return EditorCapability;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,75 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['./TransactionalPersistenceCapability'],
 | 
			
		||||
    function (TransactionalPersistenceCapability) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Wraps the [PersistenceCapability]{@link PersistenceCapability} with
 | 
			
		||||
         * transactional capabilities.
 | 
			
		||||
         * @param $q
 | 
			
		||||
         * @param transactionService
 | 
			
		||||
         * @param capabilityService
 | 
			
		||||
         * @see TransactionalPersistenceCapability
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function TransactionCapabilityDecorator(
 | 
			
		||||
            $q,
 | 
			
		||||
            transactionService,
 | 
			
		||||
            capabilityService
 | 
			
		||||
        ) {
 | 
			
		||||
            this.capabilityService = capabilityService;
 | 
			
		||||
            this.transactionService = transactionService;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Decorate PersistenceCapability to queue persistence calls when a
 | 
			
		||||
         * transaction is in progress.
 | 
			
		||||
         */
 | 
			
		||||
        TransactionCapabilityDecorator.prototype.getCapabilities = function () {
 | 
			
		||||
            var self = this,
 | 
			
		||||
                capabilities = this.capabilityService.getCapabilities
 | 
			
		||||
                    .apply(this.capabilityService, arguments),
 | 
			
		||||
                persistenceCapability = capabilities.persistence;
 | 
			
		||||
 | 
			
		||||
            capabilities.persistence = function (domainObject) {
 | 
			
		||||
                var original =
 | 
			
		||||
                    (typeof persistenceCapability === 'function')
 | 
			
		||||
                        ? persistenceCapability(domainObject)
 | 
			
		||||
                        : persistenceCapability;
 | 
			
		||||
 | 
			
		||||
                return new TransactionalPersistenceCapability(
 | 
			
		||||
                    self.$q,
 | 
			
		||||
                    self.transactionService,
 | 
			
		||||
                    original,
 | 
			
		||||
                    domainObject
 | 
			
		||||
                );
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return capabilities;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return TransactionCapabilityDecorator;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,91 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Wraps persistence capability to enable transactions. Transactions
 | 
			
		||||
         * will cause persist calls not to be invoked immediately, but
 | 
			
		||||
         * rather queued until [EditorCapability.save()]{@link EditorCapability#save}
 | 
			
		||||
         * or [EditorCapability.cancel()]{@link EditorCapability#cancel} are
 | 
			
		||||
         * called.
 | 
			
		||||
         * @memberof platform/commonUI/edit/capabilities
 | 
			
		||||
         * @param $q
 | 
			
		||||
         * @param transactionManager
 | 
			
		||||
         * @param persistenceCapability
 | 
			
		||||
         * @param domainObject
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function TransactionalPersistenceCapability(
 | 
			
		||||
            $q,
 | 
			
		||||
            transactionManager,
 | 
			
		||||
            persistenceCapability,
 | 
			
		||||
            domainObject
 | 
			
		||||
        ) {
 | 
			
		||||
            this.transactionManager = transactionManager;
 | 
			
		||||
            this.persistenceCapability = persistenceCapability;
 | 
			
		||||
            this.domainObject = domainObject;
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The wrapped persist function. If a transaction is active, persist
 | 
			
		||||
         * will be queued until the transaction is committed or cancelled.
 | 
			
		||||
         * @returns {*}
 | 
			
		||||
         */
 | 
			
		||||
        TransactionalPersistenceCapability.prototype.persist = function () {
 | 
			
		||||
            var wrappedPersistence = this.persistenceCapability;
 | 
			
		||||
 | 
			
		||||
            if (this.transactionManager.isActive()) {
 | 
			
		||||
                this.transactionManager.addToTransaction(
 | 
			
		||||
                    this.domainObject.getId(),
 | 
			
		||||
                    wrappedPersistence.persist.bind(wrappedPersistence),
 | 
			
		||||
                    wrappedPersistence.refresh.bind(wrappedPersistence)
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                //Need to return a promise from this function
 | 
			
		||||
                return this.$q.when(true);
 | 
			
		||||
            } else {
 | 
			
		||||
                return this.persistenceCapability.persist();
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        TransactionalPersistenceCapability.prototype.refresh = function () {
 | 
			
		||||
            this.transactionManager
 | 
			
		||||
                .clearTransactionsFor(this.domainObject.getId());
 | 
			
		||||
 | 
			
		||||
            return this.persistenceCapability.refresh();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        TransactionalPersistenceCapability.prototype.getSpace = function () {
 | 
			
		||||
            return this.persistenceCapability.getSpace();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        TransactionalPersistenceCapability.prototype.persisted = function () {
 | 
			
		||||
            return this.persistenceCapability.persisted();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return TransactionalPersistenceCapability;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,99 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
define([], function () {
 | 
			
		||||
    /**
 | 
			
		||||
     * A Transaction represents a set of changes that are intended to
 | 
			
		||||
     * be kept or discarded as a unit.
 | 
			
		||||
     * @param $log Angular's `$log` service, for logging messages
 | 
			
		||||
     * @constructor
 | 
			
		||||
     * @memberof platform/commonUI/edit/services
 | 
			
		||||
     */
 | 
			
		||||
    function Transaction($log) {
 | 
			
		||||
        this.$log = $log;
 | 
			
		||||
        this.callbacks = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add a change to the current transaction, as expressed by functions
 | 
			
		||||
     * to either keep or discard the change.
 | 
			
		||||
     * @param {Function} commit called when the transaction is committed
 | 
			
		||||
     * @param {Function} cancel called when the transaction is cancelled
 | 
			
		||||
     * @returns {Function) a function which may be called to remove this
 | 
			
		||||
     *          pair of callbacks from the transaction
 | 
			
		||||
     */
 | 
			
		||||
    Transaction.prototype.add = function (commit, cancel) {
 | 
			
		||||
        var callback = {
 | 
			
		||||
            commit: commit,
 | 
			
		||||
            cancel: cancel
 | 
			
		||||
        };
 | 
			
		||||
        this.callbacks.push(callback);
 | 
			
		||||
 | 
			
		||||
        return function () {
 | 
			
		||||
            this.callbacks = this.callbacks.filter(function (c) {
 | 
			
		||||
                return c !== callback;
 | 
			
		||||
            });
 | 
			
		||||
        }.bind(this);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the number of changes in the current transaction.
 | 
			
		||||
     * @returns {number} the size of the current transaction
 | 
			
		||||
     */
 | 
			
		||||
    Transaction.prototype.size = function () {
 | 
			
		||||
        return this.callbacks.length;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Keep all changes associated with this transaction.
 | 
			
		||||
     * @method {platform/commonUI/edit/services.Transaction#commit}
 | 
			
		||||
     * @returns {Promise} a promise which will resolve when all callbacks
 | 
			
		||||
     *          have been handled.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Discard all changes associated with this transaction.
 | 
			
		||||
     * @method {platform/commonUI/edit/services.Transaction#cancel}
 | 
			
		||||
     * @returns {Promise} a promise which will resolve when all callbacks
 | 
			
		||||
     *          have been handled.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    ['commit', 'cancel'].forEach(function (method) {
 | 
			
		||||
        Transaction.prototype[method] = function () {
 | 
			
		||||
            var promises = [];
 | 
			
		||||
            var callback;
 | 
			
		||||
 | 
			
		||||
            while (this.callbacks.length > 0) {
 | 
			
		||||
                callback = this.callbacks.shift();
 | 
			
		||||
                try {
 | 
			
		||||
                    promises.push(callback[method]());
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
                    this.$log
 | 
			
		||||
                        .error("Error trying to " + method + " transaction.");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Promise.all(promises);
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return Transaction;
 | 
			
		||||
});
 | 
			
		||||
@@ -1,119 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([], function () {
 | 
			
		||||
    /**
 | 
			
		||||
     * Manages transactions to support the TransactionalPersistenceCapability.
 | 
			
		||||
     * This assumes that all commit/cancel callbacks for a given domain
 | 
			
		||||
     * object are equivalent, and only need to be added once to any active
 | 
			
		||||
     * transaction. Violating this assumption may cause unexpected behavior.
 | 
			
		||||
     * @constructor
 | 
			
		||||
     * @memberof platform/commonUI/edit
 | 
			
		||||
     */
 | 
			
		||||
    function TransactionManager(transactionService) {
 | 
			
		||||
        this.transactionService = transactionService;
 | 
			
		||||
        this.clearTransactionFns = {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a transaction is currently active.
 | 
			
		||||
     * @returns {boolean} true if there is a transaction active
 | 
			
		||||
     */
 | 
			
		||||
    TransactionManager.prototype.isActive = function () {
 | 
			
		||||
        return this.transactionService.isActive();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if callbacks associated with this domain object have already
 | 
			
		||||
     * been added to the active transaction.
 | 
			
		||||
     * @private
 | 
			
		||||
     * @param {string} id the identifier of the domain object to check
 | 
			
		||||
     * @returns {boolean} true if callbacks have been added
 | 
			
		||||
     */
 | 
			
		||||
    TransactionManager.prototype.isScheduled = function (id) {
 | 
			
		||||
        return Boolean(this.clearTransactionFns[id]);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add callbacks associated with this domain object to the active
 | 
			
		||||
     * transaction. Both callbacks are expected to return promises that
 | 
			
		||||
     * resolve when their associated behavior is complete.
 | 
			
		||||
     *
 | 
			
		||||
     * If callbacks associated with this domain object have already been
 | 
			
		||||
     * added to the active transaction, this call will be ignored.
 | 
			
		||||
     *
 | 
			
		||||
     * @param {string} id the identifier of the associated domain object
 | 
			
		||||
     * @param {Function} onCommit behavior to invoke when committing transaction
 | 
			
		||||
     * @param {Function} onCancel behavior to invoke when cancelling transaction
 | 
			
		||||
     */
 | 
			
		||||
    TransactionManager.prototype.addToTransaction = function (
 | 
			
		||||
        id,
 | 
			
		||||
        onCommit,
 | 
			
		||||
        onCancel
 | 
			
		||||
    ) {
 | 
			
		||||
        var release = this.releaseClearFn.bind(this, id);
 | 
			
		||||
 | 
			
		||||
        function chain(promiseFn, nextFn) {
 | 
			
		||||
            return function () {
 | 
			
		||||
                return promiseFn().then(nextFn);
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Clear any existing persistence calls for object with given ID. This ensures only the most recent persistence
 | 
			
		||||
         * call is executed. This should prevent stale objects being persisted and overwriting fresh ones.
 | 
			
		||||
         */
 | 
			
		||||
        if (this.isScheduled(id)) {
 | 
			
		||||
            this.clearTransactionsFor(id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.clearTransactionFns[id] =
 | 
			
		||||
            this.transactionService.addToTransaction(
 | 
			
		||||
                chain(onCommit, release),
 | 
			
		||||
                chain(onCancel, release)
 | 
			
		||||
            );
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Remove any callbacks associated with this domain object from the
 | 
			
		||||
     * active transaction.
 | 
			
		||||
     * @param {string} id the identifier for the domain object
 | 
			
		||||
     */
 | 
			
		||||
    TransactionManager.prototype.clearTransactionsFor = function (id) {
 | 
			
		||||
        if (this.isScheduled(id)) {
 | 
			
		||||
            this.clearTransactionFns[id]();
 | 
			
		||||
            this.releaseClearFn(id);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Release the cached "remove from transaction" function that has been
 | 
			
		||||
     * stored in association with this domain object.
 | 
			
		||||
     * @param {string} id the identifier for the domain object
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    TransactionManager.prototype.releaseClearFn = function (id) {
 | 
			
		||||
        delete this.clearTransactionFns[id];
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return TransactionManager;
 | 
			
		||||
});
 | 
			
		||||
@@ -1,138 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
define(
 | 
			
		||||
    ['./Transaction', './NestedTransaction'],
 | 
			
		||||
    function (Transaction, NestedTransaction) {
 | 
			
		||||
        /**
 | 
			
		||||
         * Implements an application-wide transaction state. Once a
 | 
			
		||||
         * transaction is started, calls to
 | 
			
		||||
         * [PersistenceCapability.persist()]{@link PersistenceCapability#persist}
 | 
			
		||||
         * will be deferred until a subsequent call to
 | 
			
		||||
         * [TransactionService.commit]{@link TransactionService#commit} is made.
 | 
			
		||||
         *
 | 
			
		||||
         * @memberof platform/commonUI/edit/services
 | 
			
		||||
         * @param $q
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function TransactionService($q, $log, cacheService) {
 | 
			
		||||
            this.$q = $q;
 | 
			
		||||
            this.$log = $log;
 | 
			
		||||
            this.cacheService = cacheService;
 | 
			
		||||
            this.transactions = [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Starts a transaction. While a transaction is active all calls to
 | 
			
		||||
         * [PersistenceCapability.persist](@link PersistenceCapability#persist)
 | 
			
		||||
         * will be queued until [commit]{@link #commit} or [cancel]{@link
 | 
			
		||||
         * #cancel} are called
 | 
			
		||||
         */
 | 
			
		||||
        TransactionService.prototype.startTransaction = function () {
 | 
			
		||||
            var transaction = this.isActive()
 | 
			
		||||
                ? new NestedTransaction(this.transactions[0])
 | 
			
		||||
                : new Transaction(this.$log);
 | 
			
		||||
 | 
			
		||||
            this.transactions.push(transaction);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @returns {boolean} If true, indicates that a transaction is in progress
 | 
			
		||||
         */
 | 
			
		||||
        TransactionService.prototype.isActive = function () {
 | 
			
		||||
            return this.transactions.length > 0;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Adds provided functions to a queue to be called on
 | 
			
		||||
         * [.commit()]{@link #commit} or
 | 
			
		||||
         * [.cancel()]{@link #commit}
 | 
			
		||||
         * @param onCommit A function to call on commit
 | 
			
		||||
         * @param onCancel A function to call on cancel
 | 
			
		||||
         */
 | 
			
		||||
        TransactionService.prototype.addToTransaction = function (onCommit, onCancel) {
 | 
			
		||||
            if (this.isActive()) {
 | 
			
		||||
                return this.activeTransaction().add(onCommit, onCancel);
 | 
			
		||||
            } else {
 | 
			
		||||
                //Log error because this is a programming error if it occurs.
 | 
			
		||||
                this.$log.error("No transaction in progress");
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the transaction at the top of the stack.
 | 
			
		||||
         * @private
 | 
			
		||||
         */
 | 
			
		||||
        TransactionService.prototype.activeTransaction = function () {
 | 
			
		||||
            return this.transactions[this.transactions.length - 1];
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * All persist calls deferred since the beginning of the transaction
 | 
			
		||||
         * will be committed.  If this is the last transaction, clears the
 | 
			
		||||
         * cache.
 | 
			
		||||
         *
 | 
			
		||||
         * @returns {Promise} resolved when all persist operations have
 | 
			
		||||
         * completed. Will reject if any commit operations fail
 | 
			
		||||
         */
 | 
			
		||||
        TransactionService.prototype.commit = function () {
 | 
			
		||||
            var transaction = this.transactions.pop();
 | 
			
		||||
            if (!transaction) {
 | 
			
		||||
                return Promise.reject();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!this.isActive()) {
 | 
			
		||||
                return transaction.commit()
 | 
			
		||||
                    .then(function (r) {
 | 
			
		||||
                        this.cacheService.flush();
 | 
			
		||||
 | 
			
		||||
                        return r;
 | 
			
		||||
                    }.bind(this));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return transaction.commit();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Cancel the current transaction, replacing any dirty objects from
 | 
			
		||||
         * persistence. Not a true rollback, as it cannot be used to undo any
 | 
			
		||||
         * persist calls that were successful in the event one of a batch of
 | 
			
		||||
         * persists failing.
 | 
			
		||||
         *
 | 
			
		||||
         * @returns {*}
 | 
			
		||||
         */
 | 
			
		||||
        TransactionService.prototype.cancel = function () {
 | 
			
		||||
            var transaction = this.transactions.pop();
 | 
			
		||||
 | 
			
		||||
            return transaction ? transaction.cancel() : Promise.reject();
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Get the size (the number of commit/cancel callbacks) of
 | 
			
		||||
         * the active transaction.
 | 
			
		||||
         * @returns {number} size of the active transaction
 | 
			
		||||
         */
 | 
			
		||||
        TransactionService.prototype.size = function () {
 | 
			
		||||
            return this.isActive() ? this.activeTransaction().size() : 0;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return TransactionService;
 | 
			
		||||
    });
 | 
			
		||||
@@ -1,192 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/capabilities/EditorCapability"],
 | 
			
		||||
    function (EditorCapability) {
 | 
			
		||||
 | 
			
		||||
        xdescribe("The editor capability", function () {
 | 
			
		||||
            var mockDomainObject,
 | 
			
		||||
                capabilities,
 | 
			
		||||
                mockParentObject,
 | 
			
		||||
                mockTransactionService,
 | 
			
		||||
                mockStatusCapability,
 | 
			
		||||
                mockParentStatus,
 | 
			
		||||
                mockContextCapability,
 | 
			
		||||
                capability;
 | 
			
		||||
 | 
			
		||||
            function fastPromise(val) {
 | 
			
		||||
                return {
 | 
			
		||||
                    then: function (callback) {
 | 
			
		||||
                        return callback(val);
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    ["getId", "getModel", "hasCapability", "getCapability", "useCapability"]
 | 
			
		||||
                );
 | 
			
		||||
                mockParentObject = jasmine.createSpyObj(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    ["getId", "getModel", "hasCapability", "getCapability", "useCapability"]
 | 
			
		||||
                );
 | 
			
		||||
                mockTransactionService = jasmine.createSpyObj(
 | 
			
		||||
                    "transactionService",
 | 
			
		||||
                    [
 | 
			
		||||
                        "startTransaction",
 | 
			
		||||
                        "size",
 | 
			
		||||
                        "commit",
 | 
			
		||||
                        "cancel"
 | 
			
		||||
                    ]
 | 
			
		||||
                );
 | 
			
		||||
                mockTransactionService.commit.and.returnValue(fastPromise());
 | 
			
		||||
                mockTransactionService.cancel.and.returnValue(fastPromise());
 | 
			
		||||
                mockTransactionService.isActive = jasmine.createSpy('isActive');
 | 
			
		||||
 | 
			
		||||
                mockStatusCapability = jasmine.createSpyObj(
 | 
			
		||||
                    "statusCapability",
 | 
			
		||||
                    ["get", "set"]
 | 
			
		||||
                );
 | 
			
		||||
                mockParentStatus = jasmine.createSpyObj(
 | 
			
		||||
                    "statusCapability",
 | 
			
		||||
                    ["get", "set"]
 | 
			
		||||
                );
 | 
			
		||||
                mockContextCapability = jasmine.createSpyObj(
 | 
			
		||||
                    "contextCapability",
 | 
			
		||||
                    ["getParent"]
 | 
			
		||||
                );
 | 
			
		||||
                mockContextCapability.getParent.and.returnValue(mockParentObject);
 | 
			
		||||
 | 
			
		||||
                capabilities = {
 | 
			
		||||
                    context: mockContextCapability,
 | 
			
		||||
                    status: mockStatusCapability
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                mockDomainObject.hasCapability.and.callFake(function (name) {
 | 
			
		||||
                    return capabilities[name] !== undefined;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockDomainObject.getCapability.and.callFake(function (name) {
 | 
			
		||||
                    return capabilities[name];
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockParentObject.getCapability.and.returnValue(mockParentStatus);
 | 
			
		||||
                mockParentObject.hasCapability.and.returnValue(false);
 | 
			
		||||
 | 
			
		||||
                capability = new EditorCapability(
 | 
			
		||||
                    mockTransactionService,
 | 
			
		||||
                    mockDomainObject
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("starts a transaction when edit is invoked", function () {
 | 
			
		||||
                capability.edit();
 | 
			
		||||
                expect(mockTransactionService.startTransaction).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("sets editing status on object", function () {
 | 
			
		||||
                capability.edit();
 | 
			
		||||
                expect(mockStatusCapability.set).toHaveBeenCalledWith("editing", true);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("uses editing status to determine editing context root", function () {
 | 
			
		||||
                capability.edit();
 | 
			
		||||
                mockStatusCapability.get.and.returnValue(false);
 | 
			
		||||
                expect(capability.isEditContextRoot()).toBe(false);
 | 
			
		||||
                mockStatusCapability.get.and.returnValue(true);
 | 
			
		||||
                expect(capability.isEditContextRoot()).toBe(true);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("inEditingContext returns true if parent object is being"
 | 
			
		||||
                + " edited", function () {
 | 
			
		||||
                mockStatusCapability.get.and.returnValue(false);
 | 
			
		||||
                mockParentStatus.get.and.returnValue(false);
 | 
			
		||||
                expect(capability.inEditContext()).toBe(false);
 | 
			
		||||
                mockParentStatus.get.and.returnValue(true);
 | 
			
		||||
                expect(capability.inEditContext()).toBe(true);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("save", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    capability.edit();
 | 
			
		||||
                    capability.save();
 | 
			
		||||
                });
 | 
			
		||||
                it("commits the transaction", function () {
 | 
			
		||||
                    expect(mockTransactionService.commit).toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
                it("begins a new transaction", function () {
 | 
			
		||||
                    expect(mockTransactionService.startTransaction).toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("finish", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockTransactionService.isActive.and.returnValue(true);
 | 
			
		||||
                    capability.edit();
 | 
			
		||||
                    capability.finish();
 | 
			
		||||
                });
 | 
			
		||||
                it("cancels the transaction", function () {
 | 
			
		||||
                    expect(mockTransactionService.cancel).toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
                it("resets the edit state", function () {
 | 
			
		||||
                    expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("finish", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockTransactionService.isActive.and.returnValue(false);
 | 
			
		||||
                    capability.edit();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("does not cancel transaction when transaction is not active", function () {
 | 
			
		||||
                    capability.finish();
 | 
			
		||||
                    expect(mockTransactionService.cancel).not.toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("returns a promise", function () {
 | 
			
		||||
                    expect(capability.finish() instanceof Promise).toBe(true);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("dirty", function () {
 | 
			
		||||
                var model = {};
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockDomainObject.getModel.and.returnValue(model);
 | 
			
		||||
                    capability.edit();
 | 
			
		||||
                    capability.finish();
 | 
			
		||||
                });
 | 
			
		||||
                it("returns true if the object has been modified since it"
 | 
			
		||||
                    + " was last persisted", function () {
 | 
			
		||||
                    mockTransactionService.size.and.returnValue(0);
 | 
			
		||||
                    expect(capability.dirty()).toBe(false);
 | 
			
		||||
                    mockTransactionService.size.and.returnValue(1);
 | 
			
		||||
                    expect(capability.dirty()).toBe(true);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,111 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [
 | 
			
		||||
        "../../src/capabilities/TransactionalPersistenceCapability"
 | 
			
		||||
    ],
 | 
			
		||||
    function (TransactionalPersistenceCapability) {
 | 
			
		||||
 | 
			
		||||
        function fastPromise(val) {
 | 
			
		||||
            return {
 | 
			
		||||
                then: function (callback) {
 | 
			
		||||
                    return callback(val);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        describe("The transactional persistence decorator", function () {
 | 
			
		||||
            var mockQ,
 | 
			
		||||
                mockTransactionManager,
 | 
			
		||||
                mockPersistence,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                testId,
 | 
			
		||||
                capability;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testId = "test-id";
 | 
			
		||||
 | 
			
		||||
                mockQ = jasmine.createSpyObj("$q", ["when"]);
 | 
			
		||||
                mockQ.when.and.callFake(function (val) {
 | 
			
		||||
                    return fastPromise(val);
 | 
			
		||||
                });
 | 
			
		||||
                mockTransactionManager = jasmine.createSpyObj(
 | 
			
		||||
                    "transactionService",
 | 
			
		||||
                    ["isActive", "addToTransaction", "clearTransactionsFor"]
 | 
			
		||||
                );
 | 
			
		||||
                mockPersistence = jasmine.createSpyObj(
 | 
			
		||||
                    "persistenceCapability",
 | 
			
		||||
                    ["persist", "refresh", "getSpace"]
 | 
			
		||||
                );
 | 
			
		||||
                mockPersistence.persist.and.returnValue(fastPromise());
 | 
			
		||||
                mockPersistence.refresh.and.returnValue(fastPromise());
 | 
			
		||||
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    ["getModel", "getId"]
 | 
			
		||||
                );
 | 
			
		||||
                mockDomainObject.getModel.and.returnValue({persisted: 1});
 | 
			
		||||
                mockDomainObject.getId.and.returnValue(testId);
 | 
			
		||||
 | 
			
		||||
                capability = new TransactionalPersistenceCapability(
 | 
			
		||||
                    mockQ,
 | 
			
		||||
                    mockTransactionManager,
 | 
			
		||||
                    mockPersistence,
 | 
			
		||||
                    mockDomainObject
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("if no transaction is active, passes through to persistence"
 | 
			
		||||
                + " provider", function () {
 | 
			
		||||
                mockTransactionManager.isActive.and.returnValue(false);
 | 
			
		||||
                capability.persist();
 | 
			
		||||
                expect(mockPersistence.persist).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("if transaction is active, persist and cancel calls are"
 | 
			
		||||
                + " queued", function () {
 | 
			
		||||
                mockTransactionManager.isActive.and.returnValue(true);
 | 
			
		||||
                capability.persist();
 | 
			
		||||
                expect(mockTransactionManager.addToTransaction).toHaveBeenCalled();
 | 
			
		||||
                mockTransactionManager.addToTransaction.calls.mostRecent().args[1]();
 | 
			
		||||
                expect(mockPersistence.persist).toHaveBeenCalled();
 | 
			
		||||
                mockTransactionManager.addToTransaction.calls.mostRecent().args[2]();
 | 
			
		||||
                expect(mockPersistence.refresh).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("wraps getSpace", function () {
 | 
			
		||||
                mockPersistence.getSpace.and.returnValue('foo');
 | 
			
		||||
                expect(capability.getSpace()).toEqual('foo');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("clears transactions and delegates refresh calls", function () {
 | 
			
		||||
                capability.refresh();
 | 
			
		||||
                expect(mockTransactionManager.clearTransactionsFor)
 | 
			
		||||
                    .toHaveBeenCalledWith(testId);
 | 
			
		||||
                expect(mockPersistence.refresh)
 | 
			
		||||
                    .toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,75 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(["../../src/services/NestedTransaction"], function (NestedTransaction) {
 | 
			
		||||
    var TRANSACTION_METHODS = ['add', 'commit', 'cancel', 'size'];
 | 
			
		||||
 | 
			
		||||
    describe("A NestedTransaction", function () {
 | 
			
		||||
        var mockTransaction,
 | 
			
		||||
            nestedTransaction;
 | 
			
		||||
 | 
			
		||||
        beforeEach(function () {
 | 
			
		||||
            mockTransaction =
 | 
			
		||||
                jasmine.createSpyObj('transaction', TRANSACTION_METHODS);
 | 
			
		||||
            nestedTransaction = new NestedTransaction(mockTransaction);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("exposes a Transaction's interface", function () {
 | 
			
		||||
            TRANSACTION_METHODS.forEach(function (method) {
 | 
			
		||||
                expect(nestedTransaction[method])
 | 
			
		||||
                    .toEqual(jasmine.any(Function));
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe("when callbacks are added", function () {
 | 
			
		||||
            var mockCommit,
 | 
			
		||||
                mockCancel;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockCommit = jasmine.createSpy('commit');
 | 
			
		||||
                mockCancel = jasmine.createSpy('cancel');
 | 
			
		||||
                nestedTransaction.add(mockCommit, mockCancel);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not interact with its parent transaction", function () {
 | 
			
		||||
                TRANSACTION_METHODS.forEach(function (method) {
 | 
			
		||||
                    expect(mockTransaction[method])
 | 
			
		||||
                        .not.toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("and the transaction is committed", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    nestedTransaction.commit();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("adds to its parent transaction", function () {
 | 
			
		||||
                    expect(mockTransaction.add).toHaveBeenCalledWith(
 | 
			
		||||
                        jasmine.any(Function),
 | 
			
		||||
                        jasmine.any(Function)
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -1,141 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/services/TransactionManager"],
 | 
			
		||||
    function (TransactionManager) {
 | 
			
		||||
        describe("TransactionManager", function () {
 | 
			
		||||
            var mockTransactionService,
 | 
			
		||||
                testId,
 | 
			
		||||
                mockOnCommit,
 | 
			
		||||
                mockOnCancel,
 | 
			
		||||
                mockRemoves,
 | 
			
		||||
                mockPromise,
 | 
			
		||||
                manager;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockRemoves = [];
 | 
			
		||||
                mockTransactionService = jasmine.createSpyObj(
 | 
			
		||||
                    "transactionService",
 | 
			
		||||
                    ["addToTransaction", "isActive"]
 | 
			
		||||
                );
 | 
			
		||||
                mockOnCommit = jasmine.createSpy('commit');
 | 
			
		||||
                mockOnCancel = jasmine.createSpy('cancel');
 | 
			
		||||
                testId = 'test-id';
 | 
			
		||||
                mockPromise = jasmine.createSpyObj('promise', ['then']);
 | 
			
		||||
 | 
			
		||||
                mockOnCommit.and.returnValue(mockPromise);
 | 
			
		||||
                mockOnCancel.and.returnValue(mockPromise);
 | 
			
		||||
 | 
			
		||||
                mockTransactionService.addToTransaction.and.callFake(function () {
 | 
			
		||||
                    var mockRemove =
 | 
			
		||||
                        jasmine.createSpy('remove-' + mockRemoves.length);
 | 
			
		||||
                    mockRemoves.push(mockRemove);
 | 
			
		||||
 | 
			
		||||
                    return mockRemove;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                manager = new TransactionManager(mockTransactionService);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("delegates isActive calls", function () {
 | 
			
		||||
                [false, true].forEach(function (state) {
 | 
			
		||||
                    mockTransactionService.isActive.and.returnValue(state);
 | 
			
		||||
                    expect(manager.isActive()).toBe(state);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when addToTransaction is called", function () {
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    manager.addToTransaction(
 | 
			
		||||
                        testId,
 | 
			
		||||
                        mockOnCommit,
 | 
			
		||||
                        mockOnCancel
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("adds callbacks to the active transaction", function () {
 | 
			
		||||
                    expect(mockTransactionService.addToTransaction)
 | 
			
		||||
                        .toHaveBeenCalledWith(
 | 
			
		||||
                            jasmine.any(Function),
 | 
			
		||||
                            jasmine.any(Function)
 | 
			
		||||
                        );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("invokes passed-in callbacks from its own callbacks", function () {
 | 
			
		||||
                    expect(mockOnCommit).not.toHaveBeenCalled();
 | 
			
		||||
                    mockTransactionService.addToTransaction
 | 
			
		||||
                        .calls.mostRecent().args[0]();
 | 
			
		||||
                    expect(mockOnCommit).toHaveBeenCalled();
 | 
			
		||||
 | 
			
		||||
                    expect(mockOnCancel).not.toHaveBeenCalled();
 | 
			
		||||
                    mockTransactionService.addToTransaction
 | 
			
		||||
                        .calls.mostRecent().args[1]();
 | 
			
		||||
                    expect(mockOnCancel).toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("Adds callbacks to transaction", function () {
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        spyOn(manager, 'clearTransactionsFor');
 | 
			
		||||
                        manager.clearTransactionsFor.and.callThrough();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("and clears pending calls if same object", function () {
 | 
			
		||||
                        manager.addToTransaction(
 | 
			
		||||
                            testId,
 | 
			
		||||
                            jasmine.createSpy(),
 | 
			
		||||
                            jasmine.createSpy()
 | 
			
		||||
                        );
 | 
			
		||||
                        expect(manager.clearTransactionsFor).toHaveBeenCalledWith(testId);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("and does not clear pending calls if different object", function () {
 | 
			
		||||
                        manager.addToTransaction(
 | 
			
		||||
                            'other-id',
 | 
			
		||||
                            jasmine.createSpy(),
 | 
			
		||||
                            jasmine.createSpy()
 | 
			
		||||
                        );
 | 
			
		||||
                        expect(manager.clearTransactionsFor).not.toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    afterEach(function () {
 | 
			
		||||
                        expect(mockTransactionService.addToTransaction.calls.count()).toEqual(2);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("does not remove callbacks from the transaction", function () {
 | 
			
		||||
                    expect(mockRemoves[0]).not.toHaveBeenCalled();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("and clearTransactionsFor is subsequently called", function () {
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        manager.clearTransactionsFor(testId);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("removes callbacks from the transaction", function () {
 | 
			
		||||
                        expect(mockRemoves[0]).toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,139 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/services/TransactionService"],
 | 
			
		||||
    function (TransactionService) {
 | 
			
		||||
 | 
			
		||||
        describe("The Transaction Service", function () {
 | 
			
		||||
            var mockQ,
 | 
			
		||||
                mockLog,
 | 
			
		||||
                mockCacheService,
 | 
			
		||||
                transactionService;
 | 
			
		||||
 | 
			
		||||
            function fastPromise(val) {
 | 
			
		||||
                return {
 | 
			
		||||
                    then: function (callback) {
 | 
			
		||||
                        return fastPromise(callback(val));
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockQ = jasmine.createSpyObj("$q", ["all"]);
 | 
			
		||||
                mockCacheService = jasmine.createSpyObj("cacheService", ["flush"]);
 | 
			
		||||
                mockQ.all.and.returnValue(fastPromise());
 | 
			
		||||
                mockLog = jasmine.createSpyObj("$log", ["error"]);
 | 
			
		||||
                transactionService = new TransactionService(mockQ, mockLog, mockCacheService);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("isActive returns true if a transaction is in progress", function () {
 | 
			
		||||
                expect(transactionService.isActive()).toBe(false);
 | 
			
		||||
                transactionService.startTransaction();
 | 
			
		||||
                expect(transactionService.isActive()).toBe(true);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("addToTransaction queues onCommit and onCancel functions", function () {
 | 
			
		||||
                var onCommit = jasmine.createSpy('onCommit'),
 | 
			
		||||
                    onCancel = jasmine.createSpy('onCancel');
 | 
			
		||||
 | 
			
		||||
                transactionService.startTransaction();
 | 
			
		||||
                transactionService.addToTransaction(onCommit, onCancel);
 | 
			
		||||
                expect(transactionService.size()).toBe(1);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("size function returns size of commit and cancel queues", function () {
 | 
			
		||||
                var onCommit = jasmine.createSpy('onCommit'),
 | 
			
		||||
                    onCancel = jasmine.createSpy('onCancel');
 | 
			
		||||
 | 
			
		||||
                transactionService.startTransaction();
 | 
			
		||||
                transactionService.addToTransaction(onCommit, onCancel);
 | 
			
		||||
                transactionService.addToTransaction(onCommit, onCancel);
 | 
			
		||||
                transactionService.addToTransaction(onCommit, onCancel);
 | 
			
		||||
                expect(transactionService.size()).toBe(3);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("commit", function () {
 | 
			
		||||
                var onCommits;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    onCommits = [0, 1, 2].map(function (val) {
 | 
			
		||||
                        return jasmine.createSpy("onCommit" + val);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    transactionService.startTransaction();
 | 
			
		||||
                    onCommits.forEach(transactionService.addToTransaction.bind(transactionService));
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("commit calls all queued commit functions", function () {
 | 
			
		||||
                    expect(transactionService.size()).toBe(3);
 | 
			
		||||
 | 
			
		||||
                    return transactionService.commit().then(() => {
 | 
			
		||||
                        onCommits.forEach(function (spy) {
 | 
			
		||||
                            expect(spy).toHaveBeenCalled();
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("commit resets active state and clears queues", function () {
 | 
			
		||||
                    return transactionService.commit().then(() => {
 | 
			
		||||
                        expect(transactionService.isActive()).toBe(false);
 | 
			
		||||
                        expect(transactionService.size()).toBe(0);
 | 
			
		||||
                        expect(transactionService.size()).toBe(0);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("cancel", function () {
 | 
			
		||||
                var onCancels;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    onCancels = [0, 1, 2].map(function (val) {
 | 
			
		||||
                        return jasmine.createSpy("onCancel" + val);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    transactionService.startTransaction();
 | 
			
		||||
                    onCancels.forEach(function (onCancel) {
 | 
			
		||||
                        transactionService.addToTransaction(undefined, onCancel);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("cancel calls all queued cancel functions", function () {
 | 
			
		||||
                    expect(transactionService.size()).toBe(3);
 | 
			
		||||
                    transactionService.cancel();
 | 
			
		||||
                    onCancels.forEach(function (spy) {
 | 
			
		||||
                        expect(spy).toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("cancel resets active state and clears queues", function () {
 | 
			
		||||
                    transactionService.cancel();
 | 
			
		||||
                    expect(transactionService.isActive()).toBe(false);
 | 
			
		||||
                    expect(transactionService.size()).toBe(0);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,109 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/services/Transaction"],
 | 
			
		||||
    function (Transaction) {
 | 
			
		||||
 | 
			
		||||
        describe("A Transaction", function () {
 | 
			
		||||
            var mockLog,
 | 
			
		||||
                transaction;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockLog = jasmine.createSpyObj(
 | 
			
		||||
                    '$log',
 | 
			
		||||
                    ['warn', 'info', 'error', 'debug']
 | 
			
		||||
                );
 | 
			
		||||
                transaction = new Transaction(mockLog);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("initially has a size of zero", function () {
 | 
			
		||||
                expect(transaction.size()).toEqual(0);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            describe("when callbacks are added", function () {
 | 
			
		||||
                var mockCommit,
 | 
			
		||||
                    mockCancel,
 | 
			
		||||
                    remove;
 | 
			
		||||
 | 
			
		||||
                beforeEach(function () {
 | 
			
		||||
                    mockCommit = jasmine.createSpy('commit');
 | 
			
		||||
                    mockCancel = jasmine.createSpy('cancel');
 | 
			
		||||
                    remove = transaction.add(mockCommit, mockCancel);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("reports a new size", function () {
 | 
			
		||||
                    expect(transaction.size()).toEqual(1);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("returns a function to remove those callbacks", function () {
 | 
			
		||||
                    expect(remove).toEqual(jasmine.any(Function));
 | 
			
		||||
                    remove();
 | 
			
		||||
                    expect(transaction.size()).toEqual(0);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("and the transaction is committed", function () {
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        transaction.commit();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("triggers the commit callback", function () {
 | 
			
		||||
                        expect(mockCommit).toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("does not trigger the cancel callback", function () {
 | 
			
		||||
                        expect(mockCancel).not.toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("and the transaction is cancelled", function () {
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        transaction.cancel();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("triggers the cancel callback", function () {
 | 
			
		||||
                        expect(mockCancel).toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("does not trigger the commit callback", function () {
 | 
			
		||||
                        expect(mockCommit).not.toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                describe("and an exception is encountered during commit", function () {
 | 
			
		||||
                    beforeEach(function () {
 | 
			
		||||
                        mockCommit.and.callFake(function () {
 | 
			
		||||
                            throw new Error("test error");
 | 
			
		||||
                        });
 | 
			
		||||
                        transaction.commit();
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    it("logs an error", function () {
 | 
			
		||||
                        expect(mockLog.error).toHaveBeenCalled();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@@ -21,28 +21,14 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    "./src/MCTDevice",
 | 
			
		||||
    "./src/AgentService",
 | 
			
		||||
    "./src/DeviceClassifier"
 | 
			
		||||
    "./src/AgentService"
 | 
			
		||||
], function (
 | 
			
		||||
    MCTDevice,
 | 
			
		||||
    AgentService,
 | 
			
		||||
    DeviceClassifier
 | 
			
		||||
    AgentService
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        name: "platform/commonUI/mobile",
 | 
			
		||||
        definition: {
 | 
			
		||||
            "extensions": {
 | 
			
		||||
                "directives": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "mctDevice",
 | 
			
		||||
                        "implementation": MCTDevice,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "agentService"
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "services": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "agentService",
 | 
			
		||||
@@ -51,15 +37,6 @@ define([
 | 
			
		||||
                            "$window"
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "runs": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "implementation": DeviceClassifier,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "agentService",
 | 
			
		||||
                            "$document"
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -20,122 +20,12 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Provides features which support variant behavior on mobile devices.
 | 
			
		||||
 *
 | 
			
		||||
 * @namespace platform/commonUI/mobile
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
define(["../../../../src/utils/agent/Agent.js"], function (Agent) {
 | 
			
		||||
    function AngularAgentServiceWrapper(window) {
 | 
			
		||||
        const AS = Agent.default;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 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 new AS(window);
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
    return AngularAgentServiceWrapper;
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										96
									
								
								platform/commonUI/mobile/src/AgentServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								platform/commonUI/mobile/src/AgentServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses 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);
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['./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;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,88 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['./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;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,99 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/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);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,109 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../src/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);
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,78 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../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);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,168 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -45,7 +45,6 @@ define([
 | 
			
		||||
    "./src/capabilities/MutationCapability",
 | 
			
		||||
    "./src/capabilities/DelegationCapability",
 | 
			
		||||
    "./src/capabilities/InstantiationCapability",
 | 
			
		||||
    "./src/runs/TransactingMutationListener",
 | 
			
		||||
    "./src/services/Now",
 | 
			
		||||
    "./src/services/Throttle",
 | 
			
		||||
    "./src/services/Topic",
 | 
			
		||||
@@ -75,7 +74,6 @@ define([
 | 
			
		||||
    MutationCapability,
 | 
			
		||||
    DelegationCapability,
 | 
			
		||||
    InstantiationCapability,
 | 
			
		||||
    TransactingMutationListener,
 | 
			
		||||
    Now,
 | 
			
		||||
    Throttle,
 | 
			
		||||
    Topic,
 | 
			
		||||
@@ -363,12 +361,6 @@ define([
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "runs": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "implementation": TransactingMutationListener,
 | 
			
		||||
                        "depends": ["topic", "transactionService", "cacheService"]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "constants": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "PERSISTENCE_SPACE",
 | 
			
		||||
@@ -379,7 +371,7 @@ define([
 | 
			
		||||
                    {
 | 
			
		||||
                        "name": "Math.uuid.js",
 | 
			
		||||
                        "version": "1.4.7",
 | 
			
		||||
                        "description": "Unique identifer generation (code adapted.)",
 | 
			
		||||
                        "description": "Unique identifier generation (code adapted.)",
 | 
			
		||||
                        "author": "Robert Kieffer",
 | 
			
		||||
                        "website": "https://github.com/broofa/node-uuid",
 | 
			
		||||
                        "copyright": "Copyright (c) 2010-2012 Robert Kieffer",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,112 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ["../../src/runs/TransactingMutationListener"],
 | 
			
		||||
    function (TransactingMutationListener) {
 | 
			
		||||
 | 
			
		||||
        describe("TransactingMutationListener", function () {
 | 
			
		||||
            var mockTopic,
 | 
			
		||||
                mockMutationTopic,
 | 
			
		||||
                mockCacheService,
 | 
			
		||||
                mockTransactionService,
 | 
			
		||||
                mockDomainObject,
 | 
			
		||||
                mockModel,
 | 
			
		||||
                mockPersistence;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockTopic = jasmine.createSpy('topic');
 | 
			
		||||
                mockMutationTopic =
 | 
			
		||||
                    jasmine.createSpyObj('mutation', ['listen']);
 | 
			
		||||
                mockCacheService =
 | 
			
		||||
                    jasmine.createSpyObj('cacheService', [
 | 
			
		||||
                        'put'
 | 
			
		||||
                    ]);
 | 
			
		||||
                mockTransactionService =
 | 
			
		||||
                    jasmine.createSpyObj('transactionService', [
 | 
			
		||||
                        'isActive',
 | 
			
		||||
                        'startTransaction',
 | 
			
		||||
                        'commit'
 | 
			
		||||
                    ]);
 | 
			
		||||
                mockDomainObject = jasmine.createSpyObj(
 | 
			
		||||
                    'domainObject',
 | 
			
		||||
                    ['getId', 'getCapability', 'getModel']
 | 
			
		||||
                );
 | 
			
		||||
                mockPersistence = jasmine.createSpyObj(
 | 
			
		||||
                    'persistence',
 | 
			
		||||
                    ['persist', 'refresh', 'persisted']
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                mockTopic.and.callFake(function (t) {
 | 
			
		||||
                    expect(t).toBe('mutation');
 | 
			
		||||
 | 
			
		||||
                    return mockMutationTopic;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                mockDomainObject.getId.and.returnValue('mockId');
 | 
			
		||||
                mockDomainObject.getCapability.and.callFake(function (c) {
 | 
			
		||||
                    expect(c).toBe('persistence');
 | 
			
		||||
 | 
			
		||||
                    return mockPersistence;
 | 
			
		||||
                });
 | 
			
		||||
                mockModel = {};
 | 
			
		||||
                mockDomainObject.getModel.and.returnValue(mockModel);
 | 
			
		||||
 | 
			
		||||
                mockPersistence.persisted.and.returnValue(true);
 | 
			
		||||
 | 
			
		||||
                return new TransactingMutationListener(
 | 
			
		||||
                    mockTopic,
 | 
			
		||||
                    mockTransactionService,
 | 
			
		||||
                    mockCacheService
 | 
			
		||||
                );
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("listens for mutation", function () {
 | 
			
		||||
                expect(mockMutationTopic.listen)
 | 
			
		||||
                    .toHaveBeenCalledWith(jasmine.any(Function));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("calls persist if the model has changed", function () {
 | 
			
		||||
                mockModel.persisted = Date.now();
 | 
			
		||||
 | 
			
		||||
                //Mark the model dirty by setting the mutated date later than the last persisted date.
 | 
			
		||||
                mockModel.modified = mockModel.persisted + 1;
 | 
			
		||||
 | 
			
		||||
                mockMutationTopic.listen.calls.mostRecent()
 | 
			
		||||
                    .args[0](mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                expect(mockPersistence.persist).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("does not call persist if the model has not changed", function () {
 | 
			
		||||
                mockModel.persisted = Date.now();
 | 
			
		||||
 | 
			
		||||
                mockModel.modified = mockModel.persisted;
 | 
			
		||||
 | 
			
		||||
                mockMutationTopic.listen.calls.mostRecent()
 | 
			
		||||
                    .args[0](mockDomainObject);
 | 
			
		||||
 | 
			
		||||
                expect(mockPersistence.persist).not.toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -21,32 +21,24 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
@@ -73,16 +65,6 @@ define([
 | 
			
		||||
                        "value": "YYYY/MM/DD HH:mm:ss"
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "indicators": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "implementation": ClockIndicator,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "tickerService",
 | 
			
		||||
                            "CLOCK_INDICATOR_FORMAT"
 | 
			
		||||
                        ],
 | 
			
		||||
                        "priority": "preferred"
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "services": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "tickerService",
 | 
			
		||||
@@ -99,14 +81,6 @@ define([
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "controllers": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "ClockController",
 | 
			
		||||
                        "implementation": ClockController,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "$scope",
 | 
			
		||||
                            "tickerService"
 | 
			
		||||
                        ]
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "TimerController",
 | 
			
		||||
                        "implementation": TimerController,
 | 
			
		||||
@@ -126,12 +100,6 @@ define([
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "views": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "clock",
 | 
			
		||||
                        "type": "clock",
 | 
			
		||||
                        "editable": false,
 | 
			
		||||
                        "template": clockTemplate
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "timer",
 | 
			
		||||
                        "type": "timer",
 | 
			
		||||
@@ -181,75 +149,11 @@ define([
 | 
			
		||||
                        ],
 | 
			
		||||
                        "category": "contextual",
 | 
			
		||||
                        "name": "Stop",
 | 
			
		||||
                        "cssClass": "icon-box",
 | 
			
		||||
                        "cssClass": "icon-box-round-corners",
 | 
			
		||||
                        "priority": "preferred"
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "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",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,110 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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;
 | 
			
		||||
}
 | 
			
		||||
);
 | 
			
		||||
@@ -1,65 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,107 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -101,7 +101,7 @@ define(
 | 
			
		||||
                    name: "Pause"
 | 
			
		||||
                });
 | 
			
		||||
                mockStop.getMetadata.and.returnValue({
 | 
			
		||||
                    cssClass: "icon-box",
 | 
			
		||||
                    cssClass: "icon-box-round-corners",
 | 
			
		||||
                    name: "Stop"
 | 
			
		||||
                });
 | 
			
		||||
                mockScope.domainObject = mockDomainObject;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,58 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,120 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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/HyperlinkController',
 | 
			
		||||
    './res/templates/hyperlink.html'
 | 
			
		||||
], function (
 | 
			
		||||
    HyperlinkController,
 | 
			
		||||
    hyperlinkTemplate
 | 
			
		||||
) {
 | 
			
		||||
    return {
 | 
			
		||||
        name: "platform/features/hyperlink",
 | 
			
		||||
        definition: {
 | 
			
		||||
            "name": "Hyperlink",
 | 
			
		||||
            "description": "Insert a hyperlink to reference a link",
 | 
			
		||||
            "extensions": {
 | 
			
		||||
                "types": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "hyperlink",
 | 
			
		||||
                        "name": "Hyperlink",
 | 
			
		||||
                        "cssClass": "icon-chain-links",
 | 
			
		||||
                        "description": "A hyperlink to redirect to a different link",
 | 
			
		||||
                        "features": ["creation"],
 | 
			
		||||
                        "properties": [
 | 
			
		||||
                            {
 | 
			
		||||
                                "key": "url",
 | 
			
		||||
                                "name": "URL",
 | 
			
		||||
                                "control": "textfield",
 | 
			
		||||
                                "required": true,
 | 
			
		||||
                                "cssClass": "l-input-lg"
 | 
			
		||||
                            },
 | 
			
		||||
 | 
			
		||||
                            {
 | 
			
		||||
                                "key": "displayText",
 | 
			
		||||
                                "name": "Text to Display",
 | 
			
		||||
                                "control": "textfield",
 | 
			
		||||
                                "required": true,
 | 
			
		||||
                                "cssClass": "l-input-lg"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "key": "displayFormat",
 | 
			
		||||
                                "name": "Display Format",
 | 
			
		||||
                                "control": "select",
 | 
			
		||||
                                "options": [
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "name": "Link",
 | 
			
		||||
                                        "value": "link"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "value": "button",
 | 
			
		||||
                                        "name": "Button"
 | 
			
		||||
                                    }
 | 
			
		||||
                                ],
 | 
			
		||||
                                "cssClass": "l-inline"
 | 
			
		||||
                            },
 | 
			
		||||
                            {
 | 
			
		||||
                                "key": "openNewTab",
 | 
			
		||||
                                "name": "Tab to Open Hyperlink",
 | 
			
		||||
                                "control": "select",
 | 
			
		||||
                                "options": [
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "name": "Open in this tab",
 | 
			
		||||
                                        "value": "thisTab"
 | 
			
		||||
                                    },
 | 
			
		||||
                                    {
 | 
			
		||||
                                        "value": "newTab",
 | 
			
		||||
                                        "name": "Open in a new tab"
 | 
			
		||||
                                    }
 | 
			
		||||
                                ],
 | 
			
		||||
                                "cssClass": "l-inline"
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        ],
 | 
			
		||||
                        "model": {
 | 
			
		||||
                            "displayFormat": "link",
 | 
			
		||||
                            "openNewTab": "thisTab",
 | 
			
		||||
                            "removeTitle": true
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "views": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "hyperlink",
 | 
			
		||||
                        "type": "hyperlink",
 | 
			
		||||
                        "name": "Hyperlink Display",
 | 
			
		||||
                        "template": hyperlinkTemplate,
 | 
			
		||||
                        "editable": false
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "controllers": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "HyperlinkController",
 | 
			
		||||
                        "implementation": HyperlinkController,
 | 
			
		||||
                        "depends": ["$scope"]
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
});
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This bundle adds the Hyperlink object type, which can be used to add hyperlinks as a domain Object type
 | 
			
		||||
 and into display Layouts as either a button or link that can be chosen to open in either the same tab or
 | 
			
		||||
 create a new tab to open the link in
 | 
			
		||||
 * @namespace platform/features/hyperlink
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [],
 | 
			
		||||
    function () {
 | 
			
		||||
        function HyperlinkController($scope) {
 | 
			
		||||
            this.$scope = $scope;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**Function to analyze the location in which to open the hyperlink
 | 
			
		||||
        @returns true if the hyperlink is chosen to open in a different tab, false if the same tab
 | 
			
		||||
        **/
 | 
			
		||||
        HyperlinkController.prototype.openNewTab = function () {
 | 
			
		||||
            if (this.$scope.domainObject.getModel().openNewTab === "thisTab") {
 | 
			
		||||
                return false;
 | 
			
		||||
            } else {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**Function to specify the format in which the hyperlink should be created
 | 
			
		||||
        @returns true if the hyperlink is chosen to be created as a button, false if a link
 | 
			
		||||
        **/
 | 
			
		||||
        HyperlinkController.prototype.isButton = function () {
 | 
			
		||||
            if (this.$scope.domainObject.getModel().displayFormat === "link") {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return HyperlinkController;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
);
 | 
			
		||||
@@ -1,89 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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/HyperlinkController"],
 | 
			
		||||
    function (HyperlinkController) {
 | 
			
		||||
 | 
			
		||||
        describe("The controller for hyperlinks", function () {
 | 
			
		||||
            var domainObject,
 | 
			
		||||
                controller,
 | 
			
		||||
                scope;
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                scope = jasmine.createSpyObj(
 | 
			
		||||
                    "$scope",
 | 
			
		||||
                    ["domainObject"]
 | 
			
		||||
                );
 | 
			
		||||
                domainObject = jasmine.createSpyObj(
 | 
			
		||||
                    "domainObject",
 | 
			
		||||
                    ["getModel"]
 | 
			
		||||
                );
 | 
			
		||||
                scope.domainObject = domainObject;
 | 
			
		||||
                controller = new HyperlinkController(scope);
 | 
			
		||||
            });
 | 
			
		||||
            it("knows when it should open a new tab", function () {
 | 
			
		||||
                scope.domainObject.getModel.and.returnValue({
 | 
			
		||||
                    "displayFormat": "link",
 | 
			
		||||
                    "openNewTab": "newTab",
 | 
			
		||||
                    "showTitle": false
 | 
			
		||||
                }
 | 
			
		||||
                );
 | 
			
		||||
                controller = new HyperlinkController(scope);
 | 
			
		||||
                expect(controller.openNewTab())
 | 
			
		||||
                    .toBe(true);
 | 
			
		||||
            });
 | 
			
		||||
            it("knows when it is a button", function () {
 | 
			
		||||
                scope.domainObject.getModel.and.returnValue({
 | 
			
		||||
                    "displayFormat": "button",
 | 
			
		||||
                    "openNewTab": "thisTab",
 | 
			
		||||
                    "showTitle": false
 | 
			
		||||
                }
 | 
			
		||||
                );
 | 
			
		||||
                controller = new HyperlinkController(scope);
 | 
			
		||||
                expect(controller.isButton())
 | 
			
		||||
                    .toEqual(true);
 | 
			
		||||
            });
 | 
			
		||||
            it("knows when it should open in the same tab", function () {
 | 
			
		||||
                scope.domainObject.getModel.and.returnValue({
 | 
			
		||||
                    "displayFormat": "link",
 | 
			
		||||
                    "openNewTab": "thisTab",
 | 
			
		||||
                    "showTitle": false
 | 
			
		||||
                }
 | 
			
		||||
                );
 | 
			
		||||
                controller = new HyperlinkController(scope);
 | 
			
		||||
                expect(controller.openNewTab())
 | 
			
		||||
                    .toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
            it("knows when it is a link", function () {
 | 
			
		||||
                scope.domainObject.getModel.and.returnValue({
 | 
			
		||||
                    "displayFormat": "link",
 | 
			
		||||
                    "openNewTab": "thisTab",
 | 
			
		||||
                    "showTitle": false
 | 
			
		||||
                }
 | 
			
		||||
                );
 | 
			
		||||
                controller = new HyperlinkController(scope);
 | 
			
		||||
                expect(controller.openNewTab())
 | 
			
		||||
                    .toBe(false);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
This bundle provides the Timeline domain object type, as well
 | 
			
		||||
as other associated domain object types and relevant views.
 | 
			
		||||
 | 
			
		||||
# Implementation notes
 | 
			
		||||
 | 
			
		||||
## Model Properties
 | 
			
		||||
 | 
			
		||||
The properties below record properties relevant to using and
 | 
			
		||||
understanding timelines based on their JSON representation.
 | 
			
		||||
Additional common properties, such as `modified`
 | 
			
		||||
or `persisted` timestamps, may also be present.
 | 
			
		||||
 | 
			
		||||
### Timeline Model
 | 
			
		||||
 | 
			
		||||
A timeline's model looks like:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
{
 | 
			
		||||
    "type": "timeline",
 | 
			
		||||
    "start": {
 | 
			
		||||
        "timestamp": <number> (milliseconds since epoch),
 | 
			
		||||
        "epoch": <string> (currently, always "SET")
 | 
			
		||||
    },
 | 
			
		||||
    "capacity": <number> (optional; battery capacity in watt-hours)
 | 
			
		||||
    "composition": <string[]> (array of identifiers for contained objects)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The identifiers in a timeline's `composition` field should refer to
 | 
			
		||||
other Timeline objects, or to Activity objects.
 | 
			
		||||
 | 
			
		||||
### Activity Model
 | 
			
		||||
 | 
			
		||||
An activity's model looks like:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
{
 | 
			
		||||
    "type": "activity",
 | 
			
		||||
    "start": {
 | 
			
		||||
        "timestamp": <number> (milliseconds since epoch),
 | 
			
		||||
        "epoch": <string> (currently, always "SET")
 | 
			
		||||
    },
 | 
			
		||||
    "duration": {
 | 
			
		||||
        "timestamp": <number> (duration of this activity, in milliseconds)
 | 
			
		||||
        "epoch": "SET" (this is ignored)
 | 
			
		||||
    },
 | 
			
		||||
    "relationships": {
 | 
			
		||||
        "modes": <string[]> (array of applicable Activity Mode ids)
 | 
			
		||||
    },
 | 
			
		||||
    "link": <string> (optional; URL linking to associated external resource)
 | 
			
		||||
    "composition": <string[]> (array of identifiers for contained objects)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The identifiers in a timeline's `composition` field should only refer to
 | 
			
		||||
other Activity objects.
 | 
			
		||||
 | 
			
		||||
### Activity Mode Model
 | 
			
		||||
 | 
			
		||||
An activity mode's model looks like:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
{
 | 
			
		||||
    "type": "mode",
 | 
			
		||||
    "resources": {
 | 
			
		||||
        "comms": <number> (communications utilization, in Kbps)
 | 
			
		||||
        "power": <number> (power utilization, in watts)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
<div>
 | 
			
		||||
    Timeline, Activity and Activity Mode objects have been deprecated and will no longer be supported.
 | 
			
		||||
</div>
 | 
			
		||||
<div>
 | 
			
		||||
    Please open an issue in the
 | 
			
		||||
    <a href="https://github.com/nasa/openmct/issues" target="_blank">
 | 
			
		||||
        Open MCT Issue tracker
 | 
			
		||||
    </a>
 | 
			
		||||
    if you have any questions about the timeline plugin.
 | 
			
		||||
</div>
 | 
			
		||||
@@ -30,8 +30,8 @@ define([
 | 
			
		||||
 | 
			
		||||
    return function ImportExportPlugin() {
 | 
			
		||||
        return function (openmct) {
 | 
			
		||||
            ExportAsJSONAction.appliesTo = function (context) {
 | 
			
		||||
                return openmct.$injector.get('policyService')
 | 
			
		||||
            ExportAsJSONAction.prototype.appliesTo = function (context) {
 | 
			
		||||
                return this.openmct.$injector.get('policyService')
 | 
			
		||||
                    .allow("creation", context.domainObject.getCapability("type")
 | 
			
		||||
                    );
 | 
			
		||||
            };
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ define(
 | 
			
		||||
    ],
 | 
			
		||||
    function (ExportAsJSONAction, domainObjectFactory, MCT, AdapterCapability) {
 | 
			
		||||
 | 
			
		||||
        xdescribe("The export JSON action", function () {
 | 
			
		||||
        describe("The export JSON action", function () {
 | 
			
		||||
 | 
			
		||||
            var context,
 | 
			
		||||
                action,
 | 
			
		||||
@@ -102,7 +102,7 @@ define(
 | 
			
		||||
                expect(action).toBeDefined();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("doesn't export non-creatable objects in tree", function () {
 | 
			
		||||
            xit("doesn't export non-creatable objects in tree", function () {
 | 
			
		||||
                var nonCreatableType = {
 | 
			
		||||
                    hasFeature:
 | 
			
		||||
                        function (feature) {
 | 
			
		||||
@@ -149,7 +149,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("can export self-containing objects", function () {
 | 
			
		||||
            xit("can export self-containing objects", function () {
 | 
			
		||||
                var parent = domainObjectFactory({
 | 
			
		||||
                    name: 'parent',
 | 
			
		||||
                    model: {
 | 
			
		||||
@@ -191,7 +191,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("exports links to external objects as new objects", function () {
 | 
			
		||||
            xit("exports links to external objects as new objects", function () {
 | 
			
		||||
                var parent = domainObjectFactory({
 | 
			
		||||
                    name: 'parent',
 | 
			
		||||
                    model: {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ define(
 | 
			
		||||
    ],
 | 
			
		||||
    function (ImportAsJSONAction, domainObjectFactory) {
 | 
			
		||||
 | 
			
		||||
        xdescribe("The import JSON action", function () {
 | 
			
		||||
        describe("The import JSON action", function () {
 | 
			
		||||
 | 
			
		||||
            var context = {};
 | 
			
		||||
            var action,
 | 
			
		||||
@@ -146,7 +146,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("can import self-containing objects", function () {
 | 
			
		||||
            xit("can import self-containing objects", function () {
 | 
			
		||||
                var compDomainObject = domainObjectFactory({
 | 
			
		||||
                    name: 'compObject',
 | 
			
		||||
                    model: { name: 'compObject'},
 | 
			
		||||
@@ -198,7 +198,7 @@ define(
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("assigns new ids to each imported object", function () {
 | 
			
		||||
            xit("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 {stirng} path the path to domain objects within ElasticSearch
 | 
			
		||||
         * @param {string} path the path to domain objects within ElasticSearch
 | 
			
		||||
         */
 | 
			
		||||
        function ElasticPersistenceProvider($http, $q, space, root, path) {
 | 
			
		||||
            this.spaces = [space];
 | 
			
		||||
 
 | 
			
		||||
@@ -122,6 +122,7 @@ define([
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.destroy = this.destroy.bind(this);
 | 
			
		||||
        /**
 | 
			
		||||
         * Tracks current selection state of the application.
 | 
			
		||||
         * @private
 | 
			
		||||
@@ -262,7 +263,7 @@ define([
 | 
			
		||||
        // Plugins that are installed by default
 | 
			
		||||
 | 
			
		||||
        this.install(this.plugins.Plot());
 | 
			
		||||
        this.install(this.plugins.TelemetryTable());
 | 
			
		||||
        this.install(this.plugins.TelemetryTable.default());
 | 
			
		||||
        this.install(PreviewPlugin.default());
 | 
			
		||||
        this.install(LegacyIndicatorsPlugin());
 | 
			
		||||
        this.install(LicensesPlugin.default());
 | 
			
		||||
@@ -283,8 +284,10 @@ define([
 | 
			
		||||
        this.install(this.plugins.NotificationIndicator());
 | 
			
		||||
        this.install(this.plugins.NewFolderAction());
 | 
			
		||||
        this.install(this.plugins.ViewDatumAction());
 | 
			
		||||
        this.install(this.plugins.ViewLargeAction());
 | 
			
		||||
        this.install(this.plugins.ObjectInterceptors());
 | 
			
		||||
        this.install(this.plugins.NonEditableFolder());
 | 
			
		||||
        this.install(this.plugins.DeviceClassifier());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MCT.prototype = Object.create(EventEmitter.prototype);
 | 
			
		||||
@@ -434,6 +437,8 @@ define([
 | 
			
		||||
                    Browse(this);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                window.addEventListener('beforeunload', this.destroy);
 | 
			
		||||
 | 
			
		||||
                this.router.start();
 | 
			
		||||
                this.emit('start');
 | 
			
		||||
            }.bind(this));
 | 
			
		||||
@@ -457,6 +462,7 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCT.prototype.destroy = function () {
 | 
			
		||||
        window.removeEventListener('beforeunload', this.destroy);
 | 
			
		||||
        this.emit('destroy');
 | 
			
		||||
        this.router.destroy();
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -36,8 +36,7 @@ define([
 | 
			
		||||
    './views/installLegacyViews',
 | 
			
		||||
    './policies/LegacyCompositionPolicyAdapter',
 | 
			
		||||
    './actions/LegacyActionAdapter',
 | 
			
		||||
    './services/LegacyPersistenceAdapter',
 | 
			
		||||
    './services/ExportImageService'
 | 
			
		||||
    './services/LegacyPersistenceAdapter'
 | 
			
		||||
], function (
 | 
			
		||||
    ActionDialogDecorator,
 | 
			
		||||
    AdapterCapability,
 | 
			
		||||
@@ -54,8 +53,7 @@ define([
 | 
			
		||||
    installLegacyViews,
 | 
			
		||||
    legacyCompositionPolicyAdapter,
 | 
			
		||||
    LegacyActionAdapter,
 | 
			
		||||
    LegacyPersistenceAdapter,
 | 
			
		||||
    ExportImageService
 | 
			
		||||
    LegacyPersistenceAdapter
 | 
			
		||||
) {
 | 
			
		||||
    return {
 | 
			
		||||
        name: 'src/adapter',
 | 
			
		||||
@@ -84,13 +82,6 @@ define([
 | 
			
		||||
                            "identifierService",
 | 
			
		||||
                            "cacheService"
 | 
			
		||||
                        ]
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "exportImageService",
 | 
			
		||||
                        "implementation": ExportImageService,
 | 
			
		||||
                        "depends": [
 | 
			
		||||
                            "dialogService"
 | 
			
		||||
                        ]
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                components: [
 | 
			
		||||
 
 | 
			
		||||
@@ -173,10 +173,11 @@ define([
 | 
			
		||||
        const limitEvaluator = oldObject.getCapability("limit");
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            limits: function () {
 | 
			
		||||
                return limitEvaluator.limits();
 | 
			
		||||
            limits: () => {
 | 
			
		||||
                return limitEvaluator.limits.then !== undefined
 | 
			
		||||
                    ? limitEvaluator.limits()
 | 
			
		||||
                    : Promise.resolve(limitEvaluator.limits());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,218 +0,0 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Module defining ExportImageService. Created by hudsonfoo on 09/02/16
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [
 | 
			
		||||
        "html2canvas",
 | 
			
		||||
        "saveAs"
 | 
			
		||||
    ],
 | 
			
		||||
    function (
 | 
			
		||||
        html2canvas,
 | 
			
		||||
        { saveAs }
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The export image service will export any HTML node to
 | 
			
		||||
         * JPG, or PNG.
 | 
			
		||||
         * @param {object} dialogService
 | 
			
		||||
         * @constructor
 | 
			
		||||
         */
 | 
			
		||||
        function ExportImageService(dialogService) {
 | 
			
		||||
            this.dialogService = dialogService;
 | 
			
		||||
            this.exportCount = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Converts an HTML element into a PNG or JPG Blob.
 | 
			
		||||
         * @private
 | 
			
		||||
         * @param {node} element that will be converted to an image
 | 
			
		||||
         * @param {object} options Image options.
 | 
			
		||||
         * @returns {promise}
 | 
			
		||||
         */
 | 
			
		||||
        ExportImageService.prototype.renderElement = function (element, {imageType, className, thumbnailSize}) {
 | 
			
		||||
            const self = this;
 | 
			
		||||
            const dialogService = this.dialogService;
 | 
			
		||||
            const dialog = dialogService.showBlockingMessage({
 | 
			
		||||
                title: "Capturing...",
 | 
			
		||||
                hint: "Capturing an image",
 | 
			
		||||
                unknownProgress: true,
 | 
			
		||||
                severity: "info",
 | 
			
		||||
                delay: true
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            let mimeType = "image/png";
 | 
			
		||||
            if (imageType === "jpg") {
 | 
			
		||||
                mimeType = "image/jpeg";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let exportId = undefined;
 | 
			
		||||
            let oldId = undefined;
 | 
			
		||||
            if (className) {
 | 
			
		||||
                exportId = 'export-element-' + this.exportCount;
 | 
			
		||||
                this.exportCount++;
 | 
			
		||||
                oldId = element.id;
 | 
			
		||||
                element.id = exportId;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return html2canvas(element, {
 | 
			
		||||
                onclone: function (document) {
 | 
			
		||||
                    if (className) {
 | 
			
		||||
                        const clonedElement = document.getElementById(exportId);
 | 
			
		||||
                        clonedElement.classList.add(className);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    element.id = oldId;
 | 
			
		||||
                },
 | 
			
		||||
                removeContainer: true // Set to false to debug what html2canvas renders
 | 
			
		||||
            }).then(function (canvas) {
 | 
			
		||||
                dialog.dismiss();
 | 
			
		||||
 | 
			
		||||
                return new Promise(function (resolve, reject) {
 | 
			
		||||
                    if (thumbnailSize) {
 | 
			
		||||
                        const thumbnail = self.getThumbnail(canvas, mimeType, thumbnailSize);
 | 
			
		||||
 | 
			
		||||
                        return canvas.toBlob(blob => resolve({
 | 
			
		||||
                            blob,
 | 
			
		||||
                            thumbnail
 | 
			
		||||
                        }), mimeType);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return canvas.toBlob(blob => resolve({ blob }), mimeType);
 | 
			
		||||
                });
 | 
			
		||||
            }, function (error) {
 | 
			
		||||
                console.log('error capturing image', error);
 | 
			
		||||
                dialog.dismiss();
 | 
			
		||||
                const errorDialog = dialogService.showBlockingMessage({
 | 
			
		||||
                    title: "Error capturing image",
 | 
			
		||||
                    severity: "error",
 | 
			
		||||
                    hint: "Image was not captured successfully!",
 | 
			
		||||
                    options: [{
 | 
			
		||||
                        label: "OK",
 | 
			
		||||
                        callback: function () {
 | 
			
		||||
                            errorDialog.dismiss();
 | 
			
		||||
                        }
 | 
			
		||||
                    }]
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ExportImageService.prototype.getThumbnail = function (canvas, mimeType, size) {
 | 
			
		||||
            const thumbnailCanvas = document.createElement('canvas');
 | 
			
		||||
            thumbnailCanvas.setAttribute('width', size.width);
 | 
			
		||||
            thumbnailCanvas.setAttribute('height', size.height);
 | 
			
		||||
            const ctx = thumbnailCanvas.getContext('2d');
 | 
			
		||||
            ctx.globalCompositeOperation = "copy";
 | 
			
		||||
            ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, size.width, size.height);
 | 
			
		||||
 | 
			
		||||
            return thumbnailCanvas.toDataURL(mimeType);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Takes a screenshot of a DOM node and exports to JPG.
 | 
			
		||||
         * @param {node} element to be exported
 | 
			
		||||
         * @param {string} filename the exported image
 | 
			
		||||
         * @param {string} className to be added to element before capturing (optional)
 | 
			
		||||
         * @returns {promise}
 | 
			
		||||
         */
 | 
			
		||||
        ExportImageService.prototype.exportJPG = function (element, filename, className) {
 | 
			
		||||
            const processedFilename = replaceDotsWithUnderscores(filename);
 | 
			
		||||
 | 
			
		||||
            return this.renderElement(element, {
 | 
			
		||||
                imageType: 'jpg',
 | 
			
		||||
                className
 | 
			
		||||
            })
 | 
			
		||||
                .then(function (img) {
 | 
			
		||||
                    saveAs(img.blob, processedFilename);
 | 
			
		||||
                });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Takes a screenshot of a DOM node and exports to PNG.
 | 
			
		||||
         * @param {node} element to be exported
 | 
			
		||||
         * @param {string} filename the exported image
 | 
			
		||||
         * @param {string} className to be added to element before capturing (optional)
 | 
			
		||||
         * @returns {promise}
 | 
			
		||||
         */
 | 
			
		||||
        ExportImageService.prototype.exportPNG = function (element, filename, className) {
 | 
			
		||||
            const processedFilename = replaceDotsWithUnderscores(filename);
 | 
			
		||||
 | 
			
		||||
            return this.renderElement(element, {
 | 
			
		||||
                imageType: 'png',
 | 
			
		||||
                className
 | 
			
		||||
            })
 | 
			
		||||
                .then(function (img) {
 | 
			
		||||
                    saveAs(img.blob, processedFilename);
 | 
			
		||||
                });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Takes a screenshot of a DOM node in PNG format.
 | 
			
		||||
         * @param {node} element to be exported
 | 
			
		||||
         * @param {string} filename the exported image
 | 
			
		||||
         * @returns {promise}
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        ExportImageService.prototype.exportPNGtoSRC = function (element, options) {
 | 
			
		||||
 | 
			
		||||
            return this.renderElement(element, {
 | 
			
		||||
                imageType: 'png',
 | 
			
		||||
                ...options
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        function replaceDotsWithUnderscores(filename) {
 | 
			
		||||
            const regex = /\./gi;
 | 
			
		||||
 | 
			
		||||
            return filename.replace(regex, '_');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * canvas.toBlob() not supported in IE < 10, Opera, and Safari. This polyfill
 | 
			
		||||
         * implements the method in browsers that would not otherwise support it.
 | 
			
		||||
         * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
 | 
			
		||||
         */
 | 
			
		||||
        function polyfillToBlob() {
 | 
			
		||||
            if (!HTMLCanvasElement.prototype.toBlob) {
 | 
			
		||||
                Object.defineProperty(HTMLCanvasElement.prototype, "toBlob", {
 | 
			
		||||
                    value: function (callback, mimeType, quality) {
 | 
			
		||||
                        const binStr = atob(this.toDataURL(mimeType, quality).split(',')[1]);
 | 
			
		||||
                        const len = binStr.length;
 | 
			
		||||
                        const arr = new Uint8Array(len);
 | 
			
		||||
 | 
			
		||||
                        for (let i = 0; i < len; i++) {
 | 
			
		||||
                            arr[i] = binStr.charCodeAt(i);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        callback(new Blob([arr], {type: mimeType || "image/png"}));
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        polyfillToBlob();
 | 
			
		||||
 | 
			
		||||
        return ExportImageService;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
@@ -34,7 +34,6 @@ export default class Editor extends EventEmitter {
 | 
			
		||||
     * Initiate an editing session. This will start a transaction during
 | 
			
		||||
     * which any persist operations will be deferred until either save()
 | 
			
		||||
     * or finish() are called.
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    edit() {
 | 
			
		||||
        if (this.editing === true) {
 | 
			
		||||
@@ -42,8 +41,8 @@ export default class Editor extends EventEmitter {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.editing = true;
 | 
			
		||||
        this.getTransactionService().startTransaction();
 | 
			
		||||
        this.emit('isEditing', true);
 | 
			
		||||
        this.openmct.objects.startTransaction();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -56,41 +55,24 @@ export default class Editor extends EventEmitter {
 | 
			
		||||
    /**
 | 
			
		||||
     * Save any unsaved changes from this editing session. This will
 | 
			
		||||
     * end the current transaction.
 | 
			
		||||
     *
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    save() {
 | 
			
		||||
        return this.getTransactionService().commit().then((result) => {
 | 
			
		||||
            this.editing = false;
 | 
			
		||||
            this.emit('isEditing', false);
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }).catch((error) => {
 | 
			
		||||
            throw error;
 | 
			
		||||
        });
 | 
			
		||||
        return this.openmct.objects.CommitAllTransactions()
 | 
			
		||||
            .then(() => {
 | 
			
		||||
                this.editing = false;
 | 
			
		||||
                this.emit('isEditing', false);
 | 
			
		||||
            }).catch(error => {
 | 
			
		||||
                throw error;
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * End the currently active transaction and discard unsaved changes.
 | 
			
		||||
     *
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    cancel() {
 | 
			
		||||
        let cancelPromise = this.getTransactionService().cancel();
 | 
			
		||||
        this.editing = false;
 | 
			
		||||
        this.emit('isEditing', false);
 | 
			
		||||
 | 
			
		||||
        return cancelPromise;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    getTransactionService() {
 | 
			
		||||
        if (!this.transactionService) {
 | 
			
		||||
            this.transactionService = this.openmct.$injector.get('transactionService');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.transactionService;
 | 
			
		||||
        return this.openmct.objects.CancelAllTransactions();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -46,8 +46,6 @@ class ActionCollection extends EventEmitter {
 | 
			
		||||
            this._observeObjectPath();
 | 
			
		||||
            this.openmct.editor.on('isEditing', this._updateActions);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._initializeActions();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    disable(actionKeys) {
 | 
			
		||||
@@ -156,19 +154,10 @@ class ActionCollection extends EventEmitter {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _initializeActions() {
 | 
			
		||||
        Object.keys(this.applicableActions).forEach(key => {
 | 
			
		||||
            this.applicableActions[key].callBack = () => {
 | 
			
		||||
                return this.applicableActions[key].invoke(this.objectPath, this.view);
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateActions() {
 | 
			
		||||
        let newApplicableActions = this.openmct.actions._applicableActions(this.objectPath, this.view);
 | 
			
		||||
 | 
			
		||||
        this.applicableActions = this._mergeOldAndNewActions(this.applicableActions, newApplicableActions);
 | 
			
		||||
        this._initializeActions();
 | 
			
		||||
        this._update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ class ActionsAPI extends EventEmitter {
 | 
			
		||||
        this._groupOrder = ['windowing', 'undefined', 'view', 'action', 'json'];
 | 
			
		||||
 | 
			
		||||
        this.register = this.register.bind(this);
 | 
			
		||||
        this.get = this.get.bind(this);
 | 
			
		||||
        this.getActionsCollection = this.getActionsCollection.bind(this);
 | 
			
		||||
        this._applicableActions = this._applicableActions.bind(this);
 | 
			
		||||
        this._updateCachedActionCollections = this._updateCachedActionCollections.bind(this);
 | 
			
		||||
    }
 | 
			
		||||
@@ -43,12 +43,14 @@ class ActionsAPI extends EventEmitter {
 | 
			
		||||
        this._allActions[actionDefinition.key] = actionDefinition;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get(objectPath, view) {
 | 
			
		||||
        if (view) {
 | 
			
		||||
    getAction(key) {
 | 
			
		||||
        return this._allActions[key];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getActionsCollection(objectPath, view) {
 | 
			
		||||
        if (view) {
 | 
			
		||||
            return this._getCachedActionCollection(objectPath, view) || this._newActionCollection(objectPath, view, true);
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            return this._newActionCollection(objectPath, view, true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -57,25 +59,24 @@ class ActionsAPI extends EventEmitter {
 | 
			
		||||
        this._groupOrder = groupArray;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _get(objectPath, view) {
 | 
			
		||||
        let actionCollection = this._newActionCollection(objectPath, view);
 | 
			
		||||
 | 
			
		||||
        this._actionCollections.set(view, actionCollection);
 | 
			
		||||
        actionCollection.on('destroy', this._updateCachedActionCollections);
 | 
			
		||||
 | 
			
		||||
        return actionCollection;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _getCachedActionCollection(objectPath, view) {
 | 
			
		||||
        let cachedActionCollection = this._actionCollections.get(view);
 | 
			
		||||
 | 
			
		||||
        return cachedActionCollection;
 | 
			
		||||
        return this._actionCollections.get(view);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _newActionCollection(objectPath, view, skipEnvironmentObservers) {
 | 
			
		||||
        let applicableActions = this._applicableActions(objectPath, view);
 | 
			
		||||
 | 
			
		||||
        return new ActionCollection(applicableActions, objectPath, view, this._openmct, skipEnvironmentObservers);
 | 
			
		||||
        const actionCollection = new ActionCollection(applicableActions, objectPath, view, this._openmct, skipEnvironmentObservers);
 | 
			
		||||
        if (view) {
 | 
			
		||||
            this._cacheActionCollection(view, actionCollection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return actionCollection;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _cacheActionCollection(view, actionCollection) {
 | 
			
		||||
        this._actionCollections.set(view, actionCollection);
 | 
			
		||||
        actionCollection.on('destroy', this._updateCachedActionCollections);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _updateCachedActionCollections(key) {
 | 
			
		||||
 
 | 
			
		||||
@@ -106,7 +106,7 @@ describe('The Actions API', () => {
 | 
			
		||||
        it("adds action to ActionsAPI", () => {
 | 
			
		||||
            actionsAPI.register(mockAction);
 | 
			
		||||
 | 
			
		||||
            let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
 | 
			
		||||
            let actionCollection = actionsAPI.getActionsCollection(mockObjectPath, mockViewContext1);
 | 
			
		||||
            let action = actionCollection.getActionsObject()[mockAction.key];
 | 
			
		||||
 | 
			
		||||
            expect(action.key).toEqual(mockAction.key);
 | 
			
		||||
@@ -121,21 +121,21 @@ describe('The Actions API', () => {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("returns an ActionCollection when invoked with an objectPath only", () => {
 | 
			
		||||
            let actionCollection = actionsAPI.get(mockObjectPath);
 | 
			
		||||
            let actionCollection = actionsAPI.getActionsCollection(mockObjectPath);
 | 
			
		||||
            let instanceOfActionCollection = actionCollection instanceof ActionCollection;
 | 
			
		||||
 | 
			
		||||
            expect(instanceOfActionCollection).toBeTrue();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("returns an ActionCollection when invoked with an objectPath and view", () => {
 | 
			
		||||
            let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
 | 
			
		||||
            let actionCollection = actionsAPI.getActionsCollection(mockObjectPath, mockViewContext1);
 | 
			
		||||
            let instanceOfActionCollection = actionCollection instanceof ActionCollection;
 | 
			
		||||
 | 
			
		||||
            expect(instanceOfActionCollection).toBeTrue();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("returns relevant actions when invoked with objectPath only", () => {
 | 
			
		||||
            let actionCollection = actionsAPI.get(mockObjectPath);
 | 
			
		||||
            let actionCollection = actionsAPI.getActionsCollection(mockObjectPath);
 | 
			
		||||
            let action = actionCollection.getActionsObject()[mockObjectPathAction.key];
 | 
			
		||||
 | 
			
		||||
            expect(action.key).toEqual(mockObjectPathAction.key);
 | 
			
		||||
@@ -143,7 +143,7 @@ describe('The Actions API', () => {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it("returns relevant actions when invoked with objectPath and view", () => {
 | 
			
		||||
            let actionCollection = actionsAPI.get(mockObjectPath, mockViewContext1);
 | 
			
		||||
            let actionCollection = actionsAPI.getActionsCollection(mockObjectPath, mockViewContext1);
 | 
			
		||||
            let action = actionCollection.getActionsObject()[mockAction.key];
 | 
			
		||||
 | 
			
		||||
            expect(action.key).toEqual(mockAction.key);
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ import Menu, { MENU_PLACEMENT } from './menu.js';
 | 
			
		||||
 * @property {Boolean} isDisabled adds disable class if true
 | 
			
		||||
 * @property {String} name Menu item text
 | 
			
		||||
 * @property {String} description Menu item description
 | 
			
		||||
 * @property {Function} callBack callback function: invoked when item is clicked
 | 
			
		||||
 * @property {Function} onItemClicked callback function: invoked when item is clicked
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -66,12 +66,27 @@ class MenuAPI {
 | 
			
		||||
     * @param {Array.<Action>|Array.<Array.<Action>>} actions collection of actions{@link Action} or collection of groups of actions {@link Action}
 | 
			
		||||
     * @param {MenuOptions} [menuOptions] [Optional] The {@link MenuOptions} options for Menu
 | 
			
		||||
     */
 | 
			
		||||
    showMenu(x, y, actions, menuOptions) {
 | 
			
		||||
        this._createMenuComponent(x, y, actions, menuOptions);
 | 
			
		||||
    showMenu(x, y, items, menuOptions) {
 | 
			
		||||
        this._createMenuComponent(x, y, items, menuOptions);
 | 
			
		||||
 | 
			
		||||
        this.menuComponent.showMenu();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    actionsToMenuItems(actions, objectPath, view) {
 | 
			
		||||
        return actions.map(action => {
 | 
			
		||||
            const isActionGroup = Array.isArray(action);
 | 
			
		||||
            if (isActionGroup) {
 | 
			
		||||
                action = this.actionsToMenuItems(action, objectPath, view);
 | 
			
		||||
            } else {
 | 
			
		||||
                action.onItemClicked = () => {
 | 
			
		||||
                    action.invoke(objectPath, view);
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return action;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show popup menu with description of item on hover
 | 
			
		||||
     * @param {number} x x-coordinates for popup
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ describe ('The Menu API', () => {
 | 
			
		||||
                name: 'Test Action 1',
 | 
			
		||||
                cssClass: 'icon-clock',
 | 
			
		||||
                description: 'This is a test action',
 | 
			
		||||
                callBack: () => {
 | 
			
		||||
                onItemClicked: () => {
 | 
			
		||||
                    result = 'Test Action 1 Invoked';
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
@@ -66,7 +66,7 @@ describe ('The Menu API', () => {
 | 
			
		||||
                name: 'Test Action 2',
 | 
			
		||||
                cssClass: 'icon-clock',
 | 
			
		||||
                description: 'This is a test action',
 | 
			
		||||
                callBack: () => {
 | 
			
		||||
                onItemClicked: () => {
 | 
			
		||||
                    result = 'Test Action 2 Invoked';
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@
 | 
			
		||||
                :key="action.name"
 | 
			
		||||
                :class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
 | 
			
		||||
                :title="action.description"
 | 
			
		||||
                @click="action.callBack"
 | 
			
		||||
                @click="action.onItemClicked"
 | 
			
		||||
            >
 | 
			
		||||
                {{ action.name }}
 | 
			
		||||
            </li>
 | 
			
		||||
@@ -36,7 +36,7 @@
 | 
			
		||||
            :key="action.name"
 | 
			
		||||
            :class="action.cssClass"
 | 
			
		||||
            :title="action.description"
 | 
			
		||||
            @click="action.callBack"
 | 
			
		||||
            @click="action.onItemClicked"
 | 
			
		||||
        >
 | 
			
		||||
            {{ action.name }}
 | 
			
		||||
        </li>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@
 | 
			
		||||
                :key="action.name"
 | 
			
		||||
                :class="[action.cssClass, action.isDisabled ? 'disabled' : '']"
 | 
			
		||||
                :title="action.description"
 | 
			
		||||
                @click="action.callBack"
 | 
			
		||||
                @click="action.onItemClicked"
 | 
			
		||||
                @mouseover="toggleItemDescription(action)"
 | 
			
		||||
                @mouseleave="toggleItemDescription()"
 | 
			
		||||
            >
 | 
			
		||||
@@ -42,7 +42,7 @@
 | 
			
		||||
            :key="action.name"
 | 
			
		||||
            :class="action.cssClass"
 | 
			
		||||
            :title="action.description"
 | 
			
		||||
            @click="action.callBack"
 | 
			
		||||
            @click="action.onItemClicked"
 | 
			
		||||
            @mouseover="toggleItemDescription(action)"
 | 
			
		||||
            @mouseleave="toggleItemDescription()"
 | 
			
		||||
        >
 | 
			
		||||
 
 | 
			
		||||
@@ -71,12 +71,12 @@ class Menu extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
    showMenu() {
 | 
			
		||||
        this.component = new Vue({
 | 
			
		||||
            provide: {
 | 
			
		||||
                options: this.options
 | 
			
		||||
            },
 | 
			
		||||
            components: {
 | 
			
		||||
                MenuComponent
 | 
			
		||||
            },
 | 
			
		||||
            provide: {
 | 
			
		||||
                options: this.options
 | 
			
		||||
            },
 | 
			
		||||
            template: '<menu-component />'
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -85,12 +85,12 @@ class Menu extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
    showSuperMenu() {
 | 
			
		||||
        this.component = new Vue({
 | 
			
		||||
            provide: {
 | 
			
		||||
                options: this.options
 | 
			
		||||
            },
 | 
			
		||||
            components: {
 | 
			
		||||
                SuperMenuComponent
 | 
			
		||||
            },
 | 
			
		||||
            provide: {
 | 
			
		||||
                options: this.options
 | 
			
		||||
            },
 | 
			
		||||
            template: '<super-menu-component />'
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 progres of some ongoing task. Should be a number between 0 and 100, or
 | 
			
		||||
 * @property {number | 'unknown'} [progress] The progress 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 miliseconds to automatically dismisses notification
 | 
			
		||||
     *      autoDismissTimeout: {number} in milliseconds 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 miliseconds to automatically dismisses notification
 | 
			
		||||
     *      autoDismissTimeout: {number} in milliseconds 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								src/api/objects/ConflictError.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/api/objects/ConflictError.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
export default class ConflictError extends Error {
 | 
			
		||||
}
 | 
			
		||||
@@ -26,6 +26,8 @@ import RootRegistry from './RootRegistry';
 | 
			
		||||
import RootObjectProvider from './RootObjectProvider';
 | 
			
		||||
import EventEmitter from 'EventEmitter';
 | 
			
		||||
import InterceptorRegistry from './InterceptorRegistry';
 | 
			
		||||
import TransactionManager from './TransactionManager';
 | 
			
		||||
import ConflictError from './ConflictError';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Utilities for loading, saving, and manipulating domain objects.
 | 
			
		||||
@@ -34,12 +36,14 @@ import InterceptorRegistry from './InterceptorRegistry';
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
function ObjectAPI(typeRegistry, openmct) {
 | 
			
		||||
    this.openmct = openmct;
 | 
			
		||||
 | 
			
		||||
    this.typeRegistry = typeRegistry;
 | 
			
		||||
    this.eventEmitter = new EventEmitter();
 | 
			
		||||
    this.providers = {};
 | 
			
		||||
    this.rootRegistry = new RootRegistry();
 | 
			
		||||
    this.injectIdentifierService = function () {
 | 
			
		||||
        this.identifierService = openmct.$injector.get("identifierService");
 | 
			
		||||
        this.identifierService = this.openmct.$injector.get("identifierService");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.rootProvider = new RootObjectProvider(this.rootRegistry);
 | 
			
		||||
@@ -47,6 +51,10 @@ function ObjectAPI(typeRegistry, openmct) {
 | 
			
		||||
    this.interceptorRegistry = new InterceptorRegistry();
 | 
			
		||||
 | 
			
		||||
    this.SYNCHRONIZED_OBJECT_TYPES = ['notebook', 'plan'];
 | 
			
		||||
 | 
			
		||||
    this.errors = {
 | 
			
		||||
        Conflict: ConflictError
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -312,6 +320,27 @@ ObjectAPI.prototype.save = function (domainObject) {
 | 
			
		||||
    return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * After entering into edit mode, creates a new instance of TransactionManager to keep track of changes in Objects
 | 
			
		||||
 */
 | 
			
		||||
ObjectAPI.prototype.startTransaction = function () {
 | 
			
		||||
    this.transactionManager = new TransactionManager();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * When in edit mode, after save action commit/persists all stored transactions
 | 
			
		||||
 */
 | 
			
		||||
ObjectAPI.prototype.CommitAllTransactions = function () {
 | 
			
		||||
    return this.transactionManager.CommitAllTransactions(this.save.bind(this));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * When in edit mode, after cancel action discard all stored transactions
 | 
			
		||||
 */
 | 
			
		||||
ObjectAPI.prototype.CancelAllTransactions = function () {
 | 
			
		||||
    return this.transactionManager.CancelAllTransactions();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add a root-level object.
 | 
			
		||||
 * @param {module:openmct.ObjectAPI~Identifier|function} an array of
 | 
			
		||||
@@ -358,6 +387,20 @@ 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
 | 
			
		||||
@@ -387,6 +430,12 @@ ObjectAPI.prototype.mutate = function (domainObject, path, value) {
 | 
			
		||||
        //Destroy temporary mutable object
 | 
			
		||||
        this.destroyMutable(mutableDomainObject);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.transactionManager && this.openmct.editor.isEditing()) {
 | 
			
		||||
        this.transactionManager.addTransaction(domainObject);
 | 
			
		||||
    } else {
 | 
			
		||||
        this.save(domainObject);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -399,25 +448,25 @@ ObjectAPI.prototype._toMutable = function (object) {
 | 
			
		||||
        mutableObject = object;
 | 
			
		||||
    } else {
 | 
			
		||||
        mutableObject = MutableDomainObject.createMutable(object, this.eventEmitter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check if provider supports realtime updates
 | 
			
		||||
    let identifier = utils.parseKeyString(mutableObject.identifier);
 | 
			
		||||
    let provider = this.getProvider(identifier);
 | 
			
		||||
        // Check if provider supports realtime updates
 | 
			
		||||
        let identifier = utils.parseKeyString(mutableObject.identifier);
 | 
			
		||||
        let provider = this.getProvider(identifier);
 | 
			
		||||
 | 
			
		||||
    if (provider !== undefined
 | 
			
		||||
        && provider.observe !== undefined
 | 
			
		||||
        && this.SYNCHRONIZED_OBJECT_TYPES.includes(object.type)) {
 | 
			
		||||
        let unobserve = provider.observe(identifier, (updatedModel) => {
 | 
			
		||||
            if (updatedModel.persisted > mutableObject.modified) {
 | 
			
		||||
                //Don't replace with a stale model. This can happen on slow connections when multiple mutations happen
 | 
			
		||||
                //in rapid succession and intermediate persistence states are returned by the observe function.
 | 
			
		||||
                mutableObject.$refresh(updatedModel);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        mutableObject.$on('$_destroy', () => {
 | 
			
		||||
            unobserve();
 | 
			
		||||
        });
 | 
			
		||||
        if (provider !== undefined
 | 
			
		||||
            && provider.observe !== undefined
 | 
			
		||||
            && this.SYNCHRONIZED_OBJECT_TYPES.includes(object.type)) {
 | 
			
		||||
            let unobserve = provider.observe(identifier, (updatedModel) => {
 | 
			
		||||
                if (updatedModel.persisted > mutableObject.modified) {
 | 
			
		||||
                    //Don't replace with a stale model. This can happen on slow connections when multiple mutations happen
 | 
			
		||||
                    //in rapid succession and intermediate persistence states are returned by the observe function.
 | 
			
		||||
                    mutableObject.$refresh(updatedModel);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            mutableObject.$on('$_destroy', () => {
 | 
			
		||||
                unobserve();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return mutableObject;
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,10 @@ describe("The Object API", () => {
 | 
			
		||||
 | 
			
		||||
        openmct.$injector.get.and.returnValue(mockIdentifierService);
 | 
			
		||||
        objectAPI = new ObjectAPI(typeRegistry, openmct);
 | 
			
		||||
 | 
			
		||||
        openmct.editor = {};
 | 
			
		||||
        openmct.editor.isEditing = () => false;
 | 
			
		||||
 | 
			
		||||
        mockDomainObject = {
 | 
			
		||||
            identifier: {
 | 
			
		||||
                namespace: TEST_NAMESPACE,
 | 
			
		||||
 
 | 
			
		||||
@@ -20,22 +20,35 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
export default class TransactionManager {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.transactions = new Set();
 | 
			
		||||
    }
 | 
			
		||||
    invoke(objectPath) {
 | 
			
		||||
        this._openmct.objectViews.emit('clearData', objectPath[0]);
 | 
			
		||||
    }
 | 
			
		||||
    appliesTo(objectPath) {
 | 
			
		||||
        let contextualDomainObject = objectPath[0];
 | 
			
		||||
 | 
			
		||||
        return this._appliesToObjects.filter(type => contextualDomainObject.type === type).length;
 | 
			
		||||
    addTransaction(object) {
 | 
			
		||||
        this.transactions.add(object);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CancelAllTransactions() {
 | 
			
		||||
        return this.clearTransactions();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CommitAllTransactions(save) {
 | 
			
		||||
        const promisesArray = [];
 | 
			
		||||
        this.transactions.forEach(o => {
 | 
			
		||||
            promisesArray.push(save(o));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.clearTransactions();
 | 
			
		||||
 | 
			
		||||
        return Promise.all(promisesArray);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    clearTransactions() {
 | 
			
		||||
        this.transactions = new Set();
 | 
			
		||||
        // TODO:
 | 
			
		||||
        // call `this.opemct.objects.refresh()`
 | 
			
		||||
 | 
			
		||||
        return Promise.resolve();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -10,28 +10,37 @@ const cssClasses = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Overlay extends EventEmitter {
 | 
			
		||||
    constructor(options) {
 | 
			
		||||
    constructor({
 | 
			
		||||
        buttons,
 | 
			
		||||
        autoHide = true,
 | 
			
		||||
        dismissable = true,
 | 
			
		||||
        element,
 | 
			
		||||
        onDestroy,
 | 
			
		||||
        size
 | 
			
		||||
    } = {}) {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        this.dismissable = options.dismissable !== false;
 | 
			
		||||
        this.container = document.createElement('div');
 | 
			
		||||
        this.container.classList.add('l-overlay-wrapper', cssClasses[options.size]);
 | 
			
		||||
        this.container.classList.add('l-overlay-wrapper', cssClasses[size]);
 | 
			
		||||
 | 
			
		||||
        this.autoHide = autoHide;
 | 
			
		||||
        this.dismissable = dismissable !== false;
 | 
			
		||||
 | 
			
		||||
        this.component = new Vue({
 | 
			
		||||
            provide: {
 | 
			
		||||
                dismiss: this.dismiss.bind(this),
 | 
			
		||||
                element: options.element,
 | 
			
		||||
                buttons: options.buttons,
 | 
			
		||||
                dismissable: this.dismissable
 | 
			
		||||
            },
 | 
			
		||||
            components: {
 | 
			
		||||
                OverlayComponent: OverlayComponent
 | 
			
		||||
            },
 | 
			
		||||
            provide: {
 | 
			
		||||
                dismiss: this.dismiss.bind(this),
 | 
			
		||||
                element,
 | 
			
		||||
                buttons,
 | 
			
		||||
                dismissable: this.dismissable
 | 
			
		||||
            },
 | 
			
		||||
            template: '<overlay-component></overlay-component>'
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (options.onDestroy) {
 | 
			
		||||
            this.once('destroy', options.onDestroy);
 | 
			
		||||
        if (onDestroy) {
 | 
			
		||||
            this.once('destroy', onDestroy);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,10 @@ class OverlayAPI {
 | 
			
		||||
     */
 | 
			
		||||
    showOverlay(overlay) {
 | 
			
		||||
        if (this.activeOverlays.length) {
 | 
			
		||||
            this.activeOverlays[this.activeOverlays.length - 1].container.classList.add('invisible');
 | 
			
		||||
            const previousOverlay = this.activeOverlays[this.activeOverlays.length - 1];
 | 
			
		||||
            if (previousOverlay.autoHide) {
 | 
			
		||||
                previousOverlay.container.classList.add('invisible');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.activeOverlays.push(overlay);
 | 
			
		||||
@@ -60,7 +63,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 prefered size of the overlay (large, small, fit)
 | 
			
		||||
        * @property {string} size preferred 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"
 | 
			
		||||
            class="c-overlay__contents js-notebook-snapshot-item-wrapper"
 | 
			
		||||
            tabindex="0"
 | 
			
		||||
        ></div>
 | 
			
		||||
        <div
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,7 @@
 | 
			
		||||
 | 
			
		||||
    &__outer {
 | 
			
		||||
        @include abs();
 | 
			
		||||
        background: $overlayColorBg;
 | 
			
		||||
        color: $overlayColorFg;
 | 
			
		||||
        background: $colorBodyBg;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        padding: $overlayInnerMargin;
 | 
			
		||||
@@ -30,7 +29,6 @@
 | 
			
		||||
 | 
			
		||||
    &__close-button {
 | 
			
		||||
        $p: $interiorMargin + 2px;
 | 
			
		||||
        color: $overlayColorFg;
 | 
			
		||||
        font-size: 1.5em;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: $p; right: $p;
 | 
			
		||||
@@ -82,11 +80,6 @@
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .c-button,
 | 
			
		||||
    .c-click-icon {
 | 
			
		||||
        filter: $overlayBrightnessAdjust;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .c-object-label__name {
 | 
			
		||||
        filter: $objectLabelNameFilter;
 | 
			
		||||
    }
 | 
			
		||||
@@ -103,6 +96,7 @@ body.desktop {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Overlay types, styling for desktop. Appended to .l-overlay-wrapper element.
 | 
			
		||||
    .l-overlay-large,
 | 
			
		||||
    .l-overlay-small,
 | 
			
		||||
    .l-overlay-fit {
 | 
			
		||||
        .c-overlay__outer {
 | 
			
		||||
@@ -124,12 +118,8 @@ body.desktop {
 | 
			
		||||
        $tbPad: floor($pad * 0.8);
 | 
			
		||||
        $lrPad: $pad;
 | 
			
		||||
        .c-overlay {
 | 
			
		||||
            &__blocker {
 | 
			
		||||
                display: none;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &__outer {
 | 
			
		||||
                @include overlaySizing($overlayOuterMarginFullscreen);
 | 
			
		||||
                @include overlaySizing($overlayOuterMarginLarge);
 | 
			
		||||
                padding: $tbPad $lrPad;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,8 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
const { TelemetryCollection } = require("./TelemetryCollection");
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    '../../plugins/displayLayout/CustomStringFormatter',
 | 
			
		||||
    './TelemetryMetadataManager',
 | 
			
		||||
@@ -273,6 +275,28 @@ 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
											
										
									
								
							
							
								
								
									
										390
									
								
								src/api/telemetry/TelemetryCollection.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										390
									
								
								src/api/telemetry/TelemetryCollection.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,390 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            this.requestAbort = new AbortController();
 | 
			
		||||
            this.options.signal = this.requestAbort.signal;
 | 
			
		||||
            historicalData = await this.historicalProvider.request(this.domainObject, this.options);
 | 
			
		||||
            this.requestAbort = undefined;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error('Error requesting telemetry data...');
 | 
			
		||||
            this.requestAbort = undefined;
 | 
			
		||||
            this._error(error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										189
									
								
								src/exporters/ImageExporter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/exporters/ImageExporter.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,189 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class defining an image exporter for JPG/PNG output.
 | 
			
		||||
 * Originally created by hudsonfoo on 09/02/16
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
function replaceDotsWithUnderscores(filename) {
 | 
			
		||||
    const regex = /\./gi;
 | 
			
		||||
 | 
			
		||||
    return filename.replace(regex, '_');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
import {saveAs} from 'file-saver/FileSaver';
 | 
			
		||||
import html2canvas from 'html2canvas';
 | 
			
		||||
import uuid from 'uuid';
 | 
			
		||||
 | 
			
		||||
class ImageExporter {
 | 
			
		||||
    constructor(openmct) {
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
        * Converts an HTML element into a PNG or JPG Blob.
 | 
			
		||||
        * @private
 | 
			
		||||
        * @param {node} element that will be converted to an image
 | 
			
		||||
        * @param {object} options Image options.
 | 
			
		||||
        * @returns {promise}
 | 
			
		||||
        */
 | 
			
		||||
    renderElement(element, { imageType, className, thumbnailSize }) {
 | 
			
		||||
        const self = this;
 | 
			
		||||
        const overlays = this.openmct.overlays;
 | 
			
		||||
        const dialog = overlays.dialog({
 | 
			
		||||
            iconClass: 'info',
 | 
			
		||||
            message: 'Caputuring an image',
 | 
			
		||||
            buttons: [
 | 
			
		||||
                {
 | 
			
		||||
                    label: 'Cancel',
 | 
			
		||||
                    emphasis: true,
 | 
			
		||||
                    callback: function () {
 | 
			
		||||
                        dialog.dismiss();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let mimeType = 'image/png';
 | 
			
		||||
        if (imageType === 'jpg') {
 | 
			
		||||
            mimeType = 'image/jpeg';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let exportId = undefined;
 | 
			
		||||
        let oldId = undefined;
 | 
			
		||||
        if (className) {
 | 
			
		||||
            const newUUID = uuid();
 | 
			
		||||
            exportId = `$export-element-${newUUID}`;
 | 
			
		||||
            oldId = element.id;
 | 
			
		||||
            element.id = exportId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return html2canvas(element, {
 | 
			
		||||
            useCORS: true,
 | 
			
		||||
            allowTaint: true,
 | 
			
		||||
            logging: false,
 | 
			
		||||
            onclone: function (document) {
 | 
			
		||||
                if (className) {
 | 
			
		||||
                    const clonedElement = document.getElementById(exportId);
 | 
			
		||||
                    clonedElement.classList.add(className);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                element.id = oldId;
 | 
			
		||||
            },
 | 
			
		||||
            removeContainer: true // Set to false to debug what html2canvas renders
 | 
			
		||||
        }).then(canvas => {
 | 
			
		||||
            dialog.dismiss();
 | 
			
		||||
 | 
			
		||||
            return new Promise(function (resolve, reject) {
 | 
			
		||||
                if (thumbnailSize) {
 | 
			
		||||
                    const thumbnail = self.getThumbnail(canvas, mimeType, thumbnailSize);
 | 
			
		||||
 | 
			
		||||
                    return canvas.toBlob(blob => resolve({
 | 
			
		||||
                        blob,
 | 
			
		||||
                        thumbnail
 | 
			
		||||
                    }), mimeType);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return canvas.toBlob(blob => resolve({ blob }), mimeType);
 | 
			
		||||
            });
 | 
			
		||||
        }).catch(error => {
 | 
			
		||||
            dialog.dismiss();
 | 
			
		||||
 | 
			
		||||
            console.error('error capturing image', error);
 | 
			
		||||
            const errorDialog = overlays.dialog({
 | 
			
		||||
                iconClass: 'error',
 | 
			
		||||
                message: 'Image was not captured successfully!',
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        label: "OK",
 | 
			
		||||
                        emphasis: true,
 | 
			
		||||
                        callback: function () {
 | 
			
		||||
                            errorDialog.dismiss();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getThumbnail(canvas, mimeType, size) {
 | 
			
		||||
        const thumbnailCanvas = document.createElement('canvas');
 | 
			
		||||
        thumbnailCanvas.setAttribute('width', size.width);
 | 
			
		||||
        thumbnailCanvas.setAttribute('height', size.height);
 | 
			
		||||
        const ctx = thumbnailCanvas.getContext('2d');
 | 
			
		||||
        ctx.globalCompositeOperation = "copy";
 | 
			
		||||
        ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, size.width, size.height);
 | 
			
		||||
 | 
			
		||||
        return thumbnailCanvas.toDataURL(mimeType);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Takes a screenshot of a DOM node and exports to JPG.
 | 
			
		||||
     * @param {node} element to be exported
 | 
			
		||||
     * @param {string} filename the exported image
 | 
			
		||||
     * @param {string} className to be added to element before capturing (optional)
 | 
			
		||||
     * @returns {promise}
 | 
			
		||||
     */
 | 
			
		||||
    async exportJPG(element, filename, className) {
 | 
			
		||||
        const processedFilename = replaceDotsWithUnderscores(filename);
 | 
			
		||||
 | 
			
		||||
        const img = await this.renderElement(element, {
 | 
			
		||||
            imageType: 'jpg',
 | 
			
		||||
            className
 | 
			
		||||
        });
 | 
			
		||||
        saveAs(img.blob, processedFilename);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Takes a screenshot of a DOM node and exports to PNG.
 | 
			
		||||
     * @param {node} element to be exported
 | 
			
		||||
     * @param {string} filename the exported image
 | 
			
		||||
     * @param {string} className to be added to element before capturing (optional)
 | 
			
		||||
     * @returns {promise}
 | 
			
		||||
     */
 | 
			
		||||
    async exportPNG(element, filename, className) {
 | 
			
		||||
        const processedFilename = replaceDotsWithUnderscores(filename);
 | 
			
		||||
 | 
			
		||||
        const img = await this.renderElement(element, {
 | 
			
		||||
            imageType: 'png',
 | 
			
		||||
            className
 | 
			
		||||
        });
 | 
			
		||||
        saveAs(img.blob, processedFilename);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Takes a screenshot of a DOM node in PNG format.
 | 
			
		||||
     * @param {node} element to be exported
 | 
			
		||||
     * @param {string} filename the exported image
 | 
			
		||||
     * @returns {promise}
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    exportPNGtoSRC(element, options) {
 | 
			
		||||
        return this.renderElement(element, {
 | 
			
		||||
            imageType: 'png',
 | 
			
		||||
            ...options
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default ImageExporter;
 | 
			
		||||
 | 
			
		||||
@@ -20,35 +20,39 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    [
 | 
			
		||||
        "../../src/capabilities/TransactionalPersistenceCapability",
 | 
			
		||||
        "../../src/capabilities/TransactionCapabilityDecorator"
 | 
			
		||||
    ],
 | 
			
		||||
    function (TransactionalPersistenceCapability, TransactionCapabilityDecorator) {
 | 
			
		||||
import ImageExporter from './ImageExporter';
 | 
			
		||||
import { createOpenMct, resetApplicationState } from '../utils/testing';
 | 
			
		||||
 | 
			
		||||
        describe("The transaction capability decorator", function () {
 | 
			
		||||
            var mockQ,
 | 
			
		||||
                mockTransactionService,
 | 
			
		||||
                mockCapabilityService,
 | 
			
		||||
                provider;
 | 
			
		||||
describe('The Image Exporter', () => {
 | 
			
		||||
    let openmct;
 | 
			
		||||
    let imageExporter;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                mockQ = {};
 | 
			
		||||
                mockTransactionService = {};
 | 
			
		||||
                mockCapabilityService = jasmine.createSpyObj("capabilityService", ["getCapabilities"]);
 | 
			
		||||
                mockCapabilityService.getCapabilities.and.returnValue({
 | 
			
		||||
                    persistence: function () {}
 | 
			
		||||
                });
 | 
			
		||||
    beforeEach(() => {
 | 
			
		||||
        openmct = createOpenMct();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
                provider = new TransactionCapabilityDecorator(mockQ, mockTransactionService, mockCapabilityService);
 | 
			
		||||
    afterEach(() => {
 | 
			
		||||
        return resetApplicationState(openmct);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
            it("decorates the persistence capability", function () {
 | 
			
		||||
                var capabilities = provider.getCapabilities();
 | 
			
		||||
                expect(capabilities.persistence({}) instanceof TransactionalPersistenceCapability).toBe(true);
 | 
			
		||||
            });
 | 
			
		||||
    describe("basic instatation", () => {
 | 
			
		||||
        it("can be instatiated", () => {
 | 
			
		||||
            imageExporter = new ImageExporter(openmct);
 | 
			
		||||
 | 
			
		||||
            expect(imageExporter).not.toEqual(null);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
        it("can render an element to a blob", async () => {
 | 
			
		||||
            const mockHeadElement = document.createElement("h1");
 | 
			
		||||
            const mockTextNode = document.createTextNode('foo bar');
 | 
			
		||||
            mockHeadElement.appendChild(mockTextNode);
 | 
			
		||||
            document.body.appendChild(mockHeadElement);
 | 
			
		||||
            imageExporter = new ImageExporter(openmct);
 | 
			
		||||
            const returnedBlob = await imageExporter.renderElement(document.body, {
 | 
			
		||||
                imageType: 'png'
 | 
			
		||||
            });
 | 
			
		||||
            expect(returnedBlob).not.toEqual(null);
 | 
			
		||||
            expect(returnedBlob.blob).not.toEqual(null);
 | 
			
		||||
            expect(returnedBlob.blob).toBeInstanceOf(Blob);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -38,8 +38,6 @@ const DEFAULTS = [
 | 
			
		||||
    'platform/exporters',
 | 
			
		||||
    'platform/telemetry',
 | 
			
		||||
    'platform/features/clock',
 | 
			
		||||
    'platform/features/hyperlink',
 | 
			
		||||
    'platform/features/timeline',
 | 
			
		||||
    'platform/forms',
 | 
			
		||||
    'platform/identity',
 | 
			
		||||
    'platform/persistence/aggregator',
 | 
			
		||||
@@ -82,9 +80,7 @@ define([
 | 
			
		||||
    '../platform/exporters/bundle',
 | 
			
		||||
    '../platform/features/clock/bundle',
 | 
			
		||||
    '../platform/features/my-items/bundle',
 | 
			
		||||
    '../platform/features/hyperlink/bundle',
 | 
			
		||||
    '../platform/features/static-markup/bundle',
 | 
			
		||||
    '../platform/features/timeline/bundle',
 | 
			
		||||
    '../platform/forms/bundle',
 | 
			
		||||
    '../platform/framework/bundle',
 | 
			
		||||
    '../platform/framework/src/load/Bundle',
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								src/plugins/DeviceClassifier/plugin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/plugins/DeviceClassifier/plugin.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses 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);
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										72
									
								
								src/plugins/DeviceClassifier/src/DeviceClassifier.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/plugins/DeviceClassifier/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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										105
									
								
								src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses 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
 | 
			
		||||
                        );
 | 
			
		||||
                    });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										57
									
								
								src/plugins/DeviceClassifier/src/DeviceMatchers.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/plugins/DeviceClassifier/src/DeviceMatchers.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses 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();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										65
									
								
								src/plugins/DeviceClassifier/src/DeviceMatchersSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/plugins/DeviceClassifier/src/DeviceMatchersSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2021, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 | 
			
		||||
 * "License"); you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0.
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 | 
			
		||||
 * License for the specific language governing permissions and limitations
 | 
			
		||||
 * under the License.
 | 
			
		||||
 *
 | 
			
		||||
 * Open MCT includes source code licensed under additional open source
 | 
			
		||||
 * licenses. See the Open Source Licenses 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);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -19,8 +19,8 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
import LadTableSet from './components/LadTableSet.vue';
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
import LadTableSetView from './LadTableSetView';
 | 
			
		||||
 | 
			
		||||
export default function LADTableSetViewProvider(openmct) {
 | 
			
		||||
    return {
 | 
			
		||||
@@ -34,32 +34,7 @@ export default function LADTableSetViewProvider(openmct) {
 | 
			
		||||
            return domainObject.type === 'LadTableSet';
 | 
			
		||||
        },
 | 
			
		||||
        view: function (domainObject, objectPath) {
 | 
			
		||||
            let component;
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                show: function (element) {
 | 
			
		||||
                    component = new Vue({
 | 
			
		||||
                        el: element,
 | 
			
		||||
                        components: {
 | 
			
		||||
                            LadTableSet: LadTableSet
 | 
			
		||||
                        },
 | 
			
		||||
                        provide: {
 | 
			
		||||
                            openmct,
 | 
			
		||||
                            objectPath
 | 
			
		||||
                        },
 | 
			
		||||
                        data() {
 | 
			
		||||
                            return {
 | 
			
		||||
                                domainObject
 | 
			
		||||
                            };
 | 
			
		||||
                        },
 | 
			
		||||
                        template: '<lad-table-set :domain-object="domainObject"></lad-table-set>'
 | 
			
		||||
                    });
 | 
			
		||||
                },
 | 
			
		||||
                destroy: function (element) {
 | 
			
		||||
                    component.$destroy();
 | 
			
		||||
                    component = undefined;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            return new LadTableSetView(openmct, domainObject, objectPath);
 | 
			
		||||
        },
 | 
			
		||||
        priority: function () {
 | 
			
		||||
            return 1;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								src/plugins/LADTable/LADTableView.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/plugins/LADTable/LADTableView.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
import LadTable from './components/LADTable.vue';
 | 
			
		||||
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default class LADTableView {
 | 
			
		||||
    constructor(openmct, domainObject, objectPath) {
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.domainObject = domainObject;
 | 
			
		||||
        this.objectPath = objectPath;
 | 
			
		||||
        this.component = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    show(element) {
 | 
			
		||||
        this.component = new Vue({
 | 
			
		||||
            el: element,
 | 
			
		||||
            components: {
 | 
			
		||||
                LadTable
 | 
			
		||||
            },
 | 
			
		||||
            provide: {
 | 
			
		||||
                openmct: this.openmct,
 | 
			
		||||
                currentView: this
 | 
			
		||||
            },
 | 
			
		||||
            data: () => {
 | 
			
		||||
                return {
 | 
			
		||||
                    domainObject: this.domainObject,
 | 
			
		||||
                    objectPath: this.objectPath
 | 
			
		||||
                };
 | 
			
		||||
            },
 | 
			
		||||
            template: '<lad-table ref="ladTable" :domain-object="domainObject" :object-path="objectPath"></lad-table>'
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getViewContext() {
 | 
			
		||||
        if (!this.component) {
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.component.$refs.ladTable.getViewContext();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    destroy(element) {
 | 
			
		||||
        this.component.$destroy();
 | 
			
		||||
        this.component = undefined;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -19,50 +19,30 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
import LadTable from './components/LADTable.vue';
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default function LADTableViewProvider(openmct) {
 | 
			
		||||
    return {
 | 
			
		||||
        key: 'LadTable',
 | 
			
		||||
        name: 'LAD Table',
 | 
			
		||||
        cssClass: 'icon-tabular-lad',
 | 
			
		||||
        canView: function (domainObject) {
 | 
			
		||||
            return domainObject.type === 'LadTable';
 | 
			
		||||
        },
 | 
			
		||||
        canEdit: function (domainObject) {
 | 
			
		||||
            return domainObject.type === 'LadTable';
 | 
			
		||||
        },
 | 
			
		||||
        view: function (domainObject, objectPath) {
 | 
			
		||||
            let component;
 | 
			
		||||
import LADTableView from './LADTableView';
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                show: function (element) {
 | 
			
		||||
                    component = new Vue({
 | 
			
		||||
                        el: element,
 | 
			
		||||
                        components: {
 | 
			
		||||
                            LadTableComponent: LadTable
 | 
			
		||||
                        },
 | 
			
		||||
                        provide: {
 | 
			
		||||
                            openmct
 | 
			
		||||
                        },
 | 
			
		||||
                        data: () => {
 | 
			
		||||
                            return {
 | 
			
		||||
                                domainObject,
 | 
			
		||||
                                objectPath
 | 
			
		||||
                            };
 | 
			
		||||
                        },
 | 
			
		||||
                        template: '<lad-table-component :domain-object="domainObject" :object-path="objectPath"></lad-table-component>'
 | 
			
		||||
                    });
 | 
			
		||||
                },
 | 
			
		||||
                destroy: function (element) {
 | 
			
		||||
                    component.$destroy();
 | 
			
		||||
                    component = undefined;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        priority: function () {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
export default class LADTableViewProvider {
 | 
			
		||||
    constructor(openmct) {
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.name = 'LAD Table';
 | 
			
		||||
        this.key = 'LadTable';
 | 
			
		||||
        this.cssClass = 'icon-tabular-lad';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    canView(domainObject) {
 | 
			
		||||
        return domainObject.type === 'LadTable';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    canEdit(domainObject) {
 | 
			
		||||
        return domainObject.type === 'LadTable';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    view(domainObject, objectPath) {
 | 
			
		||||
        return new LADTableView(this.openmct, domainObject, objectPath);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    priority(domainObject) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								src/plugins/LADTable/LadTableSetView.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/plugins/LADTable/LadTableSetView.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
import LadTableSet from './components/LadTableSet.vue';
 | 
			
		||||
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default class LadTableSetView {
 | 
			
		||||
    constructor(openmct, domainObject, objectPath) {
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.domainObject = domainObject;
 | 
			
		||||
        this.objectPath = objectPath;
 | 
			
		||||
        this.component = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    show(element) {
 | 
			
		||||
        this.component = new Vue({
 | 
			
		||||
            el: element,
 | 
			
		||||
            components: {
 | 
			
		||||
                LadTableSet
 | 
			
		||||
            },
 | 
			
		||||
            provide: {
 | 
			
		||||
                openmct: this.openmct,
 | 
			
		||||
                objectPath: this.objectPath,
 | 
			
		||||
                currentView: this
 | 
			
		||||
            },
 | 
			
		||||
            data: () => {
 | 
			
		||||
                return {
 | 
			
		||||
                    domainObject: this.domainObject
 | 
			
		||||
                };
 | 
			
		||||
            },
 | 
			
		||||
            template: '<lad-table-set ref="ladTableSet" :domain-object="domainObject"></lad-table-set>'
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getViewContext() {
 | 
			
		||||
        if (!this.component) {
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.component.$refs.ladTableSet.getViewContext();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    destroy(element) {
 | 
			
		||||
        this.component.$destroy();
 | 
			
		||||
        this.component = undefined;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -50,7 +50,7 @@ const CONTEXT_MENU_ACTIONS = [
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
    inject: ['openmct', 'currentView'],
 | 
			
		||||
    props: {
 | 
			
		||||
        domainObject: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
@@ -167,25 +167,23 @@ export default {
 | 
			
		||||
            this.resetValues();
 | 
			
		||||
            this.timestampKey = timeSystem.key;
 | 
			
		||||
        },
 | 
			
		||||
        getView() {
 | 
			
		||||
            return {
 | 
			
		||||
                getViewContext: () => {
 | 
			
		||||
                    return {
 | 
			
		||||
                        viewHistoricalData: true,
 | 
			
		||||
                        viewDatumAction: true,
 | 
			
		||||
                        getDatum: () => {
 | 
			
		||||
                            return this.datum;
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
        updateViewContext() {
 | 
			
		||||
            this.$emit('rowContextClick', {
 | 
			
		||||
                viewHistoricalData: true,
 | 
			
		||||
                viewDatumAction: true,
 | 
			
		||||
                getDatum: () => {
 | 
			
		||||
                    return this.datum;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        showContextMenu(event) {
 | 
			
		||||
            let actionCollection = this.openmct.actions.get(this.objectPath, this.getView());
 | 
			
		||||
            let allActions = actionCollection.getActionsObject();
 | 
			
		||||
            let applicableActions = CONTEXT_MENU_ACTIONS.map(key => allActions[key]);
 | 
			
		||||
            this.updateViewContext();
 | 
			
		||||
 | 
			
		||||
            this.openmct.menus.showMenu(event.x, event.y, applicableActions);
 | 
			
		||||
            const actions = CONTEXT_MENU_ACTIONS.map(key => this.openmct.actions.getAction(key));
 | 
			
		||||
            const menuItems = this.openmct.menus.actionsToMenuItems(actions, this.objectPath, this.currentView);
 | 
			
		||||
            if (menuItems.length) {
 | 
			
		||||
                this.openmct.menus.showMenu(event.x, event.y, menuItems);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        resetValues() {
 | 
			
		||||
            this.value = '---';
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@
 | 
			
		||||
                :domain-object="ladRow.domainObject"
 | 
			
		||||
                :path-to-table="objectPath"
 | 
			
		||||
                :has-units="hasUnits"
 | 
			
		||||
                @rowContextClick="updateViewContext"
 | 
			
		||||
            />
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
@@ -51,7 +52,7 @@ export default {
 | 
			
		||||
    components: {
 | 
			
		||||
        LadRow
 | 
			
		||||
    },
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
    inject: ['openmct', 'currentView'],
 | 
			
		||||
    props: {
 | 
			
		||||
        domainObject: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
@@ -64,7 +65,8 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            items: []
 | 
			
		||||
            items: [],
 | 
			
		||||
            viewContext: {}
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
@@ -114,6 +116,12 @@ export default {
 | 
			
		||||
            let metadataWithUnits = valueMetadatas.filter(metadatum => metadatum.unit);
 | 
			
		||||
 | 
			
		||||
            return metadataWithUnits.length > 0;
 | 
			
		||||
        },
 | 
			
		||||
        updateViewContext(rowContext) {
 | 
			
		||||
            this.viewContext.row = rowContext;
 | 
			
		||||
        },
 | 
			
		||||
        getViewContext() {
 | 
			
		||||
            return this.viewContext;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,7 @@
 | 
			
		||||
                :domain-object="ladRow.domainObject"
 | 
			
		||||
                :path-to-table="ladTable.objectPath"
 | 
			
		||||
                :has-units="hasUnits"
 | 
			
		||||
                @rowContextClick="updateViewContext"
 | 
			
		||||
            />
 | 
			
		||||
        </template>
 | 
			
		||||
    </tbody>
 | 
			
		||||
@@ -61,7 +62,7 @@ export default {
 | 
			
		||||
    components: {
 | 
			
		||||
        LadRow
 | 
			
		||||
    },
 | 
			
		||||
    inject: ['openmct', 'objectPath'],
 | 
			
		||||
    inject: ['openmct', 'objectPath', 'currentView'],
 | 
			
		||||
    props: {
 | 
			
		||||
        domainObject: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
@@ -72,7 +73,8 @@ export default {
 | 
			
		||||
        return {
 | 
			
		||||
            ladTableObjects: [],
 | 
			
		||||
            ladTelemetryObjects: {},
 | 
			
		||||
            compositions: []
 | 
			
		||||
            compositions: [],
 | 
			
		||||
            viewContext: {}
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
@@ -166,6 +168,12 @@ export default {
 | 
			
		||||
 | 
			
		||||
                this.$set(this.ladTelemetryObjects, ladTable.key, telemetryObjects);
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        updateViewContext(rowContext) {
 | 
			
		||||
            this.viewContext.row = rowContext;
 | 
			
		||||
        },
 | 
			
		||||
        getViewContext() {
 | 
			
		||||
            return this.viewContext;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,7 @@ export default class URLTimeSettingsSynchronizer {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.updateTimeSettings();
 | 
			
		||||
        this.openmct.router.on('change:params', this.updateTimeSettings);
 | 
			
		||||
 | 
			
		||||
        TIME_EVENTS.forEach(event => {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										78
									
								
								src/plugins/clearData/ClearDataAction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/plugins/clearData/ClearDataAction.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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
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';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user