Compare commits
	
		
			290 Commits
		
	
	
		
			condition-
			...
			improve-te
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ffca1a8f33 | ||
| 
						 | 
					04598b6cf1 | ||
| 
						 | 
					43628ad9d6 | ||
| 
						 | 
					67bea86bc8 | ||
| 
						 | 
					f8b5c8e6a1 | ||
| 
						 | 
					4eb4cbfffc | ||
| 
						 | 
					eda01abcbc | ||
| 
						 | 
					694b8f4666 | ||
| 
						 | 
					bbb271a678 | ||
| 
						 | 
					fec1438806 | ||
| 
						 | 
					e43a23f1c5 | ||
| 
						 | 
					28f19ec310 | ||
| 
						 | 
					f934454c25 | ||
| 
						 | 
					eb49ffae02 | ||
| 
						 | 
					5751012872 | ||
| 
						 | 
					b672b50359 | ||
| 
						 | 
					eaf8faa1f1 | ||
| 
						 | 
					aa041e04cf | ||
| 
						 | 
					24e7ea143a | ||
| 
						 | 
					79d5d9c4d0 | ||
| 
						 | 
					b5bfdc4418 | ||
| 
						 | 
					59730c60ec | ||
| 
						 | 
					4a87a5d847 | ||
| 
						 | 
					421c09ec2c | ||
| 
						 | 
					0679b246b8 | ||
| 
						 | 
					83f9c6c528 | ||
| 
						 | 
					a5f3ba6259 | ||
| 
						 | 
					a70facf0c8 | ||
| 
						 | 
					447fe94325 | ||
| 
						 | 
					8e2b666766 | ||
| 
						 | 
					dcbfbdbb89 | ||
| 
						 | 
					4c76bf34ab | ||
| 
						 | 
					81b7a9d3e0 | ||
| 
						 | 
					dc573c479c | ||
| 
						 | 
					23303c910e | ||
| 
						 | 
					3282934cf6 | ||
| 
						 | 
					c157fab081 | ||
| 
						 | 
					7c07b66cc9 | ||
| 
						 | 
					7a906ccf5c | ||
| 
						 | 
					ff7debfb81 | ||
| 
						 | 
					92ba103f45 | ||
| 
						 | 
					2c2d8d6b56 | ||
| 
						 | 
					cfadb9f4fd | ||
| 
						 | 
					396817b2d1 | ||
| 
						 | 
					96eb6d6b74 | ||
| 
						 | 
					cb5d47f66f | ||
| 
						 | 
					ea90d02d66 | ||
| 
						 | 
					95f73d8eb8 | ||
| 
						 | 
					a37c686993 | ||
| 
						 | 
					f12166097c | ||
| 
						 | 
					d103a22fa0 | ||
| 
						 | 
					04a60cfcbb | ||
| 
						 | 
					8d723960f4 | ||
| 
						 | 
					6d3cd2c699 | ||
| 
						 | 
					87bf94fe0a | ||
| 
						 | 
					af93823b6f | ||
| 
						 | 
					4a39ddf425 | ||
| 
						 | 
					83c273b976 | ||
| 
						 | 
					7dd81beb03 | ||
| 
						 | 
					1842d3923c | ||
| 
						 | 
					26838635b6 | ||
| 
						 | 
					11f2c35bb2 | ||
| 
						 | 
					766f48c1ba | ||
| 
						 | 
					da7b93f9b3 | ||
| 
						 | 
					99c095a69f | ||
| 
						 | 
					f885e83505 | ||
| 
						 | 
					928bc4c68a | ||
| 
						 | 
					d5539c7ae4 | ||
| 
						 | 
					c86a104fb6 | ||
| 
						 | 
					46fedc1a30 | ||
| 
						 | 
					3b6ef9b44b | ||
| 
						 | 
					c68edd9b7d | ||
| 
						 | 
					11574b7c40 | ||
| 
						 | 
					abc2cd2413 | ||
| 
						 | 
					5d74882646 | ||
| 
						 | 
					9fe7f230e6 | ||
| 
						 | 
					de4c5b3729 | ||
| 
						 | 
					2a7901914a | ||
| 
						 | 
					73b0fc6f79 | ||
| 
						 | 
					ddef16795c | ||
| 
						 | 
					d188b9a056 | ||
| 
						 | 
					f510f3edd0 | ||
| 
						 | 
					e05b0bb562 | ||
| 
						 | 
					713c5e9fb7 | ||
| 
						 | 
					17bca04560 | ||
| 
						 | 
					61bdadc33c | ||
| 
						 | 
					e0c5bca47d | ||
| 
						 | 
					cdc7c1af64 | ||
| 
						 | 
					3158baa998 | ||
| 
						 | 
					698508fde4 | ||
| 
						 | 
					68a96989e1 | ||
| 
						 | 
					46a6a43234 | ||
| 
						 | 
					d41fc27b55 | ||
| 
						 | 
					24bb96cc90 | ||
| 
						 | 
					483ee173d6 | ||
| 
						 | 
					469e93d916 | ||
| 
						 | 
					f96dfcc942 | ||
| 
						 | 
					063a6c0e51 | ||
| 
						 | 
					7c289d76b6 | ||
| 
						 | 
					c617a440eb | ||
| 
						 | 
					8f81a45b9b | ||
| 
						 | 
					666459be87 | ||
| 
						 | 
					53df89aa5d | ||
| 
						 | 
					8f553f6327 | ||
| 
						 | 
					f91a64483b | ||
| 
						 | 
					de8d63c09d | ||
| 
						 | 
					58b4a6ebf5 | ||
| 
						 | 
					9d45080526 | ||
| 
						 | 
					d3fe2a6811 | ||
| 
						 | 
					97b37edce4 | ||
| 
						 | 
					d3443518d6 | ||
| 
						 | 
					c33314a4bf | ||
| 
						 | 
					59eb034ab4 | ||
| 
						 | 
					67d53fb62b | ||
| 
						 | 
					0fd637c0e9 | ||
| 
						 | 
					169cc6617a | ||
| 
						 | 
					a946325e95 | ||
| 
						 | 
					1beba78111 | ||
| 
						 | 
					2edfeaa606 | ||
| 
						 | 
					2a1f9fd063 | ||
| 
						 | 
					63fe92f8ea | ||
| 
						 | 
					35f303ffa4 | ||
| 
						 | 
					dd70bb470f | ||
| 
						 | 
					250fee125a | ||
| 
						 | 
					956029c786 | ||
| 
						 | 
					b9ab599c35 | ||
| 
						 | 
					5a690932e9 | ||
| 
						 | 
					b00757150e | ||
| 
						 | 
					8515a411bd | ||
| 
						 | 
					3034ec016a | ||
| 
						 | 
					a81af1ce34 | ||
| 
						 | 
					64c5725687 | ||
| 
						 | 
					72a3248123 | ||
| 
						 | 
					ee4a81bdfd | ||
| 
						 | 
					b90eb80584 | ||
| 
						 | 
					ebaf702c59 | ||
| 
						 | 
					3956cd1c06 | ||
| 
						 | 
					064cf6747e | ||
| 
						 | 
					8512b634c7 | ||
| 
						 | 
					4220a8a68a | ||
| 
						 | 
					e7e5116773 | ||
| 
						 | 
					079201273e | ||
| 
						 | 
					e4c9f156a7 | ||
| 
						 | 
					42eeeea374 | ||
| 
						 | 
					7b060509f5 | ||
| 
						 | 
					3ca9e9ae56 | ||
| 
						 | 
					984bede43b | ||
| 
						 | 
					87d838c690 | ||
| 
						 | 
					072bf361de | ||
| 
						 | 
					4e39d9fb84 | ||
| 
						 | 
					14eaf4e899 | ||
| 
						 | 
					a31d10e708 | ||
| 
						 | 
					f9e88321a3 | ||
| 
						 | 
					9e12203b86 | ||
| 
						 | 
					a5326a7b95 | ||
| 
						 | 
					84874f22e6 | ||
| 
						 | 
					d00e8b68a5 | ||
| 
						 | 
					2907d6d79c | ||
| 
						 | 
					a5a4bb87c5 | ||
| 
						 | 
					389589d7f7 | ||
| 
						 | 
					c02cbd1ba7 | ||
| 
						 | 
					90b475e17b | ||
| 
						 | 
					89a298f5b3 | ||
| 
						 | 
					f990a14a3c | ||
| 
						 | 
					6b00af6ece | ||
| 
						 | 
					5ec8ac95c1 | ||
| 
						 | 
					5f061001d6 | ||
| 
						 | 
					ef3c4ccf47 | ||
| 
						 | 
					1a86204637 | ||
| 
						 | 
					0a1959df38 | ||
| 
						 | 
					03a690a158 | ||
| 
						 | 
					459a055455 | ||
| 
						 | 
					502d29dd25 | ||
| 
						 | 
					bf947a8835 | ||
| 
						 | 
					f0fd0a9cc7 | ||
| 
						 | 
					7282792da1 | ||
| 
						 | 
					ec0291c54d | ||
| 
						 | 
					4413c29abb | ||
| 
						 | 
					91e1a144ed | ||
| 
						 | 
					53440c31d5 | ||
| 
						 | 
					5128af2531 | ||
| 
						 | 
					eedc0f13bc | ||
| 
						 | 
					4172fdf1d5 | ||
| 
						 | 
					7c200df4c4 | ||
| 
						 | 
					d1b28e079a | ||
| 
						 | 
					23ec838643 | ||
| 
						 | 
					271c619c63 | ||
| 
						 | 
					0552769670 | ||
| 
						 | 
					99a9f5b3ba | ||
| 
						 | 
					4b535ade31 | ||
| 
						 | 
					de8f8088e2 | ||
| 
						 | 
					32c16416d3 | ||
| 
						 | 
					0cf27c349b | ||
| 
						 | 
					015aa8c637 | ||
| 
						 | 
					4a07ddbefc | ||
| 
						 | 
					a81009541c | ||
| 
						 | 
					1b680cfaca | ||
| 
						 | 
					311ff003c0 | ||
| 
						 | 
					0cae61444d | ||
| 
						 | 
					28a603def8 | ||
| 
						 | 
					d2b7407674 | ||
| 
						 | 
					fff89a6384 | ||
| 
						 | 
					953b95f79c | ||
| 
						 | 
					6ff5ce78e1 | ||
| 
						 | 
					0857fd95a7 | ||
| 
						 | 
					a1f2608e7c | ||
| 
						 | 
					b7cea7b955 | ||
| 
						 | 
					32da19a486 | ||
| 
						 | 
					39d7dc8372 | ||
| 
						 | 
					f76f537be7 | ||
| 
						 | 
					a681d67e05 | ||
| 
						 | 
					0a634eb490 | ||
| 
						 | 
					fcca8fa8d9 | ||
| 
						 | 
					829eecf1ae | ||
| 
						 | 
					dc7f83754a | ||
| 
						 | 
					51498c0e75 | ||
| 
						 | 
					6dbdebbe05 | ||
| 
						 | 
					c994227d5d | ||
| 
						 | 
					761ca7ad56 | ||
| 
						 | 
					7f49a7bc99 | ||
| 
						 | 
					ca022b8a28 | ||
| 
						 | 
					6f500d0d0b | ||
| 
						 | 
					07d101ac1c | ||
| 
						 | 
					cdf0dd0c10 | ||
| 
						 | 
					c27c347d29 | ||
| 
						 | 
					dc54eef2c9 | ||
| 
						 | 
					57a68a24de | ||
| 
						 | 
					2c1b4b4cfc | ||
| 
						 | 
					437e8a0263 | ||
| 
						 | 
					0a2e912091 | ||
| 
						 | 
					87513a14b7 | ||
| 
						 | 
					168c040f3c | ||
| 
						 | 
					78487a48f6 | ||
| 
						 | 
					a5a197680d | ||
| 
						 | 
					d42bd44485 | ||
| 
						 | 
					77b705ecc8 | ||
| 
						 | 
					48af39a584 | ||
| 
						 | 
					ec978f3a35 | ||
| 
						 | 
					f13714e0c4 | ||
| 
						 | 
					42ac3ef9af | ||
| 
						 | 
					26ffe8efde | ||
| 
						 | 
					87b4000b12 | ||
| 
						 | 
					f790c9bd39 | ||
| 
						 | 
					096c9688d5 | ||
| 
						 | 
					1f19f480ce | ||
| 
						 | 
					44f48a3e2a | ||
| 
						 | 
					60fce4a003 | ||
| 
						 | 
					f04b5b689e | ||
| 
						 | 
					05d981768e | ||
| 
						 | 
					e4a6c21101 | ||
| 
						 | 
					d51dd8b7d0 | ||
| 
						 | 
					aed5377ad2 | ||
| 
						 | 
					459b2060a5 | ||
| 
						 | 
					cbeb25c583 | ||
| 
						 | 
					b38a9ad4ce | ||
| 
						 | 
					ecf3e19f16 | ||
| 
						 | 
					3b82fd5d8b | ||
| 
						 | 
					e08b4ff0ab | ||
| 
						 | 
					1f3ec77bf1 | ||
| 
						 | 
					3f61db2067 | ||
| 
						 | 
					ce1fdbddda | ||
| 
						 | 
					5332d136b7 | ||
| 
						 | 
					983ed7f0e7 | ||
| 
						 | 
					11978cd869 | ||
| 
						 | 
					00d1b5e69f | ||
| 
						 | 
					6b4cd25417 | ||
| 
						 | 
					243b9cac24 | ||
| 
						 | 
					60aecfe27e | ||
| 
						 | 
					b7fffeab1c | ||
| 
						 | 
					84f0d49d6f | ||
| 
						 | 
					090e89d524 | ||
| 
						 | 
					90dd53e954 | ||
| 
						 | 
					8975bc8c55 | ||
| 
						 | 
					15a7d03e74 | ||
| 
						 | 
					1dc9743484 | ||
| 
						 | 
					6095872682 | ||
| 
						 | 
					dba55867f4 | ||
| 
						 | 
					0da80c2a67 | ||
| 
						 | 
					084df5329a | ||
| 
						 | 
					49ff0c79db | ||
| 
						 | 
					7a4b967a01 | ||
| 
						 | 
					ddfa611c44 | ||
| 
						 | 
					24bade2284 | ||
| 
						 | 
					4a5e106709 | ||
| 
						 | 
					cf9336dae9 | ||
| 
						 | 
					7f32d196e4 | ||
| 
						 | 
					897d05276a | ||
| 
						 | 
					3e6509ce6f | ||
| 
						 | 
					9b8b63a0d8 | ||
| 
						 | 
					a1b1fa464e | 
@@ -20,8 +20,8 @@ jobs:
 | 
			
		||||
            paths:
 | 
			
		||||
              - node_modules
 | 
			
		||||
        - run:
 | 
			
		||||
            name: npm run test
 | 
			
		||||
            command: npm run test
 | 
			
		||||
            name: npm run test:coverage
 | 
			
		||||
            command: npm run test:coverage
 | 
			
		||||
        - run:
 | 
			
		||||
            name: npm run lint
 | 
			
		||||
            command: npm run lint
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,8 @@ module.exports = {
 | 
			
		||||
    },
 | 
			
		||||
    "extends": [
 | 
			
		||||
        "eslint:recommended",
 | 
			
		||||
        "plugin:vue/recommended"
 | 
			
		||||
        "plugin:vue/recommended",
 | 
			
		||||
        "plugin:you-dont-need-lodash-underscore/compatible"
 | 
			
		||||
    ],
 | 
			
		||||
    "parser": "vue-eslint-parser",
 | 
			
		||||
    "parserOptions": {
 | 
			
		||||
@@ -22,6 +23,9 @@ module.exports = {
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "rules": {
 | 
			
		||||
        "you-dont-need-lodash-underscore/omit": "off",
 | 
			
		||||
        "you-dont-need-lodash-underscore/throttle": "off",
 | 
			
		||||
        "you-dont-need-lodash-underscore/flatten": "off",
 | 
			
		||||
        "no-bitwise": "error",
 | 
			
		||||
        "curly": "error",
 | 
			
		||||
        "eqeqeq": "error",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								API.md
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								API.md
									
									
									
									
									
								
							@@ -427,8 +427,8 @@ Each telemetry value description has an object defining hints.  Keys in this thi
 | 
			
		||||
 | 
			
		||||
Known hints:
 | 
			
		||||
 | 
			
		||||
* `domain`: Indicates that the value represents the "input" of a datum. Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
 | 
			
		||||
* `range`: Indicates that the value is the "output" of a datum.  Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
 | 
			
		||||
* `domain`: Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
 | 
			
		||||
* `range`: Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
 | 
			
		||||
* `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available.
 | 
			
		||||
 | 
			
		||||
##### The Time Conductor and Telemetry 
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,7 @@ the name chosen could not be mistaken for a topic or master branch.
 | 
			
		||||
### Merging
 | 
			
		||||
 | 
			
		||||
When development is complete on an issue, the first step toward merging it
 | 
			
		||||
back into the master branch is to file a Pull Request. The contributions
 | 
			
		||||
back into the master branch is to file a Pull Request (PR). The contributions
 | 
			
		||||
should meet code, test, and commit message standards as described below,
 | 
			
		||||
and the pull request should include a completed author checklist, also
 | 
			
		||||
as described below. Pull requests may be assigned to specific team
 | 
			
		||||
@@ -114,6 +114,15 @@ request. When the reviewer is satisfied, they should add a comment to
 | 
			
		||||
the pull request containing the reviewer checklist (from below) and complete
 | 
			
		||||
the merge back to the master branch.
 | 
			
		||||
 | 
			
		||||
Additionally:
 | 
			
		||||
* Every pull request must link to the issue that it addresses. Eg. “Addresses #1234” or “Closes #1234”. This is the responsibility of the pull request’s __author__. If no issue exists, create one.
 | 
			
		||||
* Every __author__ must include testing instructions. These instructions should identify the areas of code affected, and some minimal test steps. If addressing a bug, reproduction steps should be included, if they were not included in the original issue. If reproduction steps were included on the original issue, and are sufficient, refer to them.
 | 
			
		||||
* A pull request that closes an issue should say so in the description. Including the text “Closes #1234” will cause the linked issue to be automatically closed when the pull request is merged. This is the responsibility of the pull request’s __author__.
 | 
			
		||||
* When a pull request is merged, and the corresponding issue closed, the __reviewer__ must add the tag “unverified” to the original issue. This will indicate that although the issue is closed, it has not been tested yet.
 | 
			
		||||
* Every PR must have two reviewers assigned, though only one approval is necessary for merge.
 | 
			
		||||
* Changes to API require approval by a senior developer.
 | 
			
		||||
* When creating a PR, it is the author's responsibility to apply any priority label from the issue to the PR as well. This helps with prioritization.
 | 
			
		||||
 | 
			
		||||
## Standards
 | 
			
		||||
 | 
			
		||||
Contributions to Open MCT are expected to meet the following standards.
 | 
			
		||||
@@ -292,6 +301,7 @@ checklist).
 | 
			
		||||
2. Unit tests included and/or updated with changes?
 | 
			
		||||
3. Command line build passes?
 | 
			
		||||
4. Changes have been smoke-tested?
 | 
			
		||||
5. Testing instructions included?
 | 
			
		||||
 | 
			
		||||
### Reviewer Checklist
 | 
			
		||||
 | 
			
		||||
@@ -299,3 +309,4 @@ checklist).
 | 
			
		||||
2. Appropriate unit tests included?
 | 
			
		||||
3. Code style and in-line documentation are appropriate?
 | 
			
		||||
4. Commit messages meet standards?
 | 
			
		||||
5. Has associated issue been labelled `unverified`? (only applicable if this PR closes the issue)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<!--
 | 
			
		||||
 Open MCT, Copyright (c) 2014-2018, United States Government
 | 
			
		||||
 Open MCT, Copyright (c) 2014-2020, United States Government
 | 
			
		||||
 as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 Administration. All rights reserved.
 | 
			
		||||
 | 
			
		||||
@@ -18,4 +18,4 @@
 | 
			
		||||
 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.
 | 
			
		||||
-->
 | 
			
		||||
-->
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2018, United States Government
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2020, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
@@ -18,4 +18,4 @@
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 
 | 
			
		||||
@@ -1,80 +0,0 @@
 | 
			
		||||
<!doctype html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <title>Code coverage report for All files</title>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <link rel="stylesheet" href="prettify.css" />
 | 
			
		||||
    <link rel="stylesheet" href="base.css" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
    <style type='text/css'>
 | 
			
		||||
        .coverage-summary .sorter {
 | 
			
		||||
            background-image: url(sort-arrow-sprite.png);
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
<div class='wrapper'>
 | 
			
		||||
  <div class='pad1'>
 | 
			
		||||
    <h1>
 | 
			
		||||
      /
 | 
			
		||||
    </h1>
 | 
			
		||||
    <div class='clearfix'>
 | 
			
		||||
      <div class='fl pad1y space-right2'>
 | 
			
		||||
        <span class="strong">100% </span>
 | 
			
		||||
        <span class="quiet">Statements</span>
 | 
			
		||||
        <span class='fraction'>0/0</span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class='fl pad1y space-right2'>
 | 
			
		||||
        <span class="strong">100% </span>
 | 
			
		||||
        <span class="quiet">Branches</span>
 | 
			
		||||
        <span class='fraction'>0/0</span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class='fl pad1y space-right2'>
 | 
			
		||||
        <span class="strong">100% </span>
 | 
			
		||||
        <span class="quiet">Functions</span>
 | 
			
		||||
        <span class='fraction'>0/0</span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class='fl pad1y space-right2'>
 | 
			
		||||
        <span class="strong">100% </span>
 | 
			
		||||
        <span class="quiet">Lines</span>
 | 
			
		||||
        <span class='fraction'>0/0</span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class='status-line high'></div>
 | 
			
		||||
<div class="pad1">
 | 
			
		||||
<table class="coverage-summary">
 | 
			
		||||
<thead>
 | 
			
		||||
<tr>
 | 
			
		||||
   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
 | 
			
		||||
   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
 | 
			
		||||
   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
 | 
			
		||||
   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
 | 
			
		||||
   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
 | 
			
		||||
   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
 | 
			
		||||
   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
 | 
			
		||||
   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
 | 
			
		||||
   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
 | 
			
		||||
   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
 | 
			
		||||
</tr>
 | 
			
		||||
</thead>
 | 
			
		||||
<tbody></tbody>
 | 
			
		||||
</table>
 | 
			
		||||
</div><div class='push'></div><!-- for sticky footer -->
 | 
			
		||||
</div><!-- /wrapper -->
 | 
			
		||||
<div class='footer quiet pad2 space-top1 center small'>
 | 
			
		||||
  Code coverage
 | 
			
		||||
  generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Wed Dec 11 2019 13:15:10 GMT-0800 (Pacific Standard Time)
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
<script src="prettify.js"></script>
 | 
			
		||||
<script>
 | 
			
		||||
window.onload = function () {
 | 
			
		||||
        if (typeof prettyPrint === 'function') {
 | 
			
		||||
            prettyPrint();
 | 
			
		||||
        }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<script src="sorter.js"></script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 209 B  | 
@@ -1,158 +0,0 @@
 | 
			
		||||
var addSorting = (function () {
 | 
			
		||||
    "use strict";
 | 
			
		||||
    var cols,
 | 
			
		||||
        currentSort = {
 | 
			
		||||
            index: 0,
 | 
			
		||||
            desc: false
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    // returns the summary table element
 | 
			
		||||
    function getTable() { return document.querySelector('.coverage-summary'); }
 | 
			
		||||
    // returns the thead element of the summary table
 | 
			
		||||
    function getTableHeader() { return getTable().querySelector('thead tr'); }
 | 
			
		||||
    // returns the tbody element of the summary table
 | 
			
		||||
    function getTableBody() { return getTable().querySelector('tbody'); }
 | 
			
		||||
    // returns the th element for nth column
 | 
			
		||||
    function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; }
 | 
			
		||||
 | 
			
		||||
    // loads all columns
 | 
			
		||||
    function loadColumns() {
 | 
			
		||||
        var colNodes = getTableHeader().querySelectorAll('th'),
 | 
			
		||||
            colNode,
 | 
			
		||||
            cols = [],
 | 
			
		||||
            col,
 | 
			
		||||
            i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < colNodes.length; i += 1) {
 | 
			
		||||
            colNode = colNodes[i];
 | 
			
		||||
            col = {
 | 
			
		||||
                key: colNode.getAttribute('data-col'),
 | 
			
		||||
                sortable: !colNode.getAttribute('data-nosort'),
 | 
			
		||||
                type: colNode.getAttribute('data-type') || 'string'
 | 
			
		||||
            };
 | 
			
		||||
            cols.push(col);
 | 
			
		||||
            if (col.sortable) {
 | 
			
		||||
                col.defaultDescSort = col.type === 'number';
 | 
			
		||||
                colNode.innerHTML = colNode.innerHTML + '<span class="sorter"></span>';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return cols;
 | 
			
		||||
    }
 | 
			
		||||
    // attaches a data attribute to every tr element with an object
 | 
			
		||||
    // of data values keyed by column name
 | 
			
		||||
    function loadRowData(tableRow) {
 | 
			
		||||
        var tableCols = tableRow.querySelectorAll('td'),
 | 
			
		||||
            colNode,
 | 
			
		||||
            col,
 | 
			
		||||
            data = {},
 | 
			
		||||
            i,
 | 
			
		||||
            val;
 | 
			
		||||
        for (i = 0; i < tableCols.length; i += 1) {
 | 
			
		||||
            colNode = tableCols[i];
 | 
			
		||||
            col = cols[i];
 | 
			
		||||
            val = colNode.getAttribute('data-value');
 | 
			
		||||
            if (col.type === 'number') {
 | 
			
		||||
                val = Number(val);
 | 
			
		||||
            }
 | 
			
		||||
            data[col.key] = val;
 | 
			
		||||
        }
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
    // loads all row data
 | 
			
		||||
    function loadData() {
 | 
			
		||||
        var rows = getTableBody().querySelectorAll('tr'),
 | 
			
		||||
            i;
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < rows.length; i += 1) {
 | 
			
		||||
            rows[i].data = loadRowData(rows[i]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // sorts the table using the data for the ith column
 | 
			
		||||
    function sortByIndex(index, desc) {
 | 
			
		||||
        var key = cols[index].key,
 | 
			
		||||
            sorter = function (a, b) {
 | 
			
		||||
                a = a.data[key];
 | 
			
		||||
                b = b.data[key];
 | 
			
		||||
                return a < b ? -1 : a > b ? 1 : 0;
 | 
			
		||||
            },
 | 
			
		||||
            finalSorter = sorter,
 | 
			
		||||
            tableBody = document.querySelector('.coverage-summary tbody'),
 | 
			
		||||
            rowNodes = tableBody.querySelectorAll('tr'),
 | 
			
		||||
            rows = [],
 | 
			
		||||
            i;
 | 
			
		||||
 | 
			
		||||
        if (desc) {
 | 
			
		||||
            finalSorter = function (a, b) {
 | 
			
		||||
                return -1 * sorter(a, b);
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < rowNodes.length; i += 1) {
 | 
			
		||||
            rows.push(rowNodes[i]);
 | 
			
		||||
            tableBody.removeChild(rowNodes[i]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rows.sort(finalSorter);
 | 
			
		||||
 | 
			
		||||
        for (i = 0; i < rows.length; i += 1) {
 | 
			
		||||
            tableBody.appendChild(rows[i]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // removes sort indicators for current column being sorted
 | 
			
		||||
    function removeSortIndicators() {
 | 
			
		||||
        var col = getNthColumn(currentSort.index),
 | 
			
		||||
            cls = col.className;
 | 
			
		||||
 | 
			
		||||
        cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
 | 
			
		||||
        col.className = cls;
 | 
			
		||||
    }
 | 
			
		||||
    // adds sort indicators for current column being sorted
 | 
			
		||||
    function addSortIndicators() {
 | 
			
		||||
        getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted';
 | 
			
		||||
    }
 | 
			
		||||
    // adds event listeners for all sorter widgets
 | 
			
		||||
    function enableUI() {
 | 
			
		||||
        var i,
 | 
			
		||||
            el,
 | 
			
		||||
            ithSorter = function ithSorter(i) {
 | 
			
		||||
                var col = cols[i];
 | 
			
		||||
 | 
			
		||||
                return function () {
 | 
			
		||||
                    var desc = col.defaultDescSort;
 | 
			
		||||
 | 
			
		||||
                    if (currentSort.index === i) {
 | 
			
		||||
                        desc = !currentSort.desc;
 | 
			
		||||
                    }
 | 
			
		||||
                    sortByIndex(i, desc);
 | 
			
		||||
                    removeSortIndicators();
 | 
			
		||||
                    currentSort.index = i;
 | 
			
		||||
                    currentSort.desc = desc;
 | 
			
		||||
                    addSortIndicators();
 | 
			
		||||
                };
 | 
			
		||||
            };
 | 
			
		||||
        for (i =0 ; i < cols.length; i += 1) {
 | 
			
		||||
            if (cols[i].sortable) {
 | 
			
		||||
                // add the click event handler on the th so users
 | 
			
		||||
                // dont have to click on those tiny arrows
 | 
			
		||||
                el = getNthColumn(i).querySelector('.sorter').parentElement;
 | 
			
		||||
                if (el.addEventListener) {
 | 
			
		||||
                    el.addEventListener('click', ithSorter(i));
 | 
			
		||||
                } else {
 | 
			
		||||
                    el.attachEvent('onclick', ithSorter(i));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // adds sorting functionality to the UI
 | 
			
		||||
    return function () {
 | 
			
		||||
        if (!getTable()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        cols = loadColumns();
 | 
			
		||||
        loadData(cols);
 | 
			
		||||
        addSortIndicators();
 | 
			
		||||
        enableUI();
 | 
			
		||||
    };
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
window.addEventListener('load', addSorting);
 | 
			
		||||
@@ -9,7 +9,8 @@ define([
 | 
			
		||||
            values: [
 | 
			
		||||
                {
 | 
			
		||||
                    key: "name",
 | 
			
		||||
                    name: "Name"
 | 
			
		||||
                    name: "Name",
 | 
			
		||||
                    format: "string"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "utc",
 | 
			
		||||
@@ -49,7 +50,8 @@ define([
 | 
			
		||||
            values: [
 | 
			
		||||
                {
 | 
			
		||||
                    key: "name",
 | 
			
		||||
                    name: "Name"
 | 
			
		||||
                    name: "Name",
 | 
			
		||||
                    format: "string"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "utc",
 | 
			
		||||
@@ -98,7 +100,7 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
 | 
			
		||||
        return _.extend(
 | 
			
		||||
        return Object.assign(
 | 
			
		||||
            {},
 | 
			
		||||
            domainObject.telemetry,
 | 
			
		||||
            METADATA_BY_TYPE[domainObject.type]
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ define([
 | 
			
		||||
        period: 10,
 | 
			
		||||
        offset: 0,
 | 
			
		||||
        dataRateInHz: 1,
 | 
			
		||||
        randomness: 0,
 | 
			
		||||
        phase: 0
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -52,7 +53,8 @@ define([
 | 
			
		||||
            'period',
 | 
			
		||||
            'offset',
 | 
			
		||||
            'dataRateInHz',
 | 
			
		||||
            'phase'
 | 
			
		||||
            'phase',
 | 
			
		||||
            'randomness'
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        request = request || {};
 | 
			
		||||
 
 | 
			
		||||
@@ -65,8 +65,8 @@
 | 
			
		||||
                        name: data.name,
 | 
			
		||||
                        utc: nextStep,
 | 
			
		||||
                        yesterday: nextStep - 60*60*24*1000,
 | 
			
		||||
                        sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase),
 | 
			
		||||
                        cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase)
 | 
			
		||||
                        sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness),
 | 
			
		||||
                        cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase, data.randomness)
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                nextStep += step;
 | 
			
		||||
@@ -99,6 +99,7 @@
 | 
			
		||||
        var offset = request.offset;
 | 
			
		||||
        var dataRateInHz = request.dataRateInHz;
 | 
			
		||||
        var phase = request.phase;
 | 
			
		||||
        var randomness = request.randomness;
 | 
			
		||||
 | 
			
		||||
        var step = 1000 / dataRateInHz;
 | 
			
		||||
        var nextStep = start - (start % step) + step;
 | 
			
		||||
@@ -110,8 +111,8 @@
 | 
			
		||||
                name: request.name,
 | 
			
		||||
                utc: nextStep,
 | 
			
		||||
                yesterday: nextStep - 60*60*24*1000,
 | 
			
		||||
                sin: sin(nextStep, period, amplitude, offset, phase),
 | 
			
		||||
                cos: cos(nextStep, period, amplitude, offset, phase)
 | 
			
		||||
                sin: sin(nextStep, period, amplitude, offset, phase, randomness),
 | 
			
		||||
                cos: cos(nextStep, period, amplitude, offset, phase, randomness)
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        self.postMessage({
 | 
			
		||||
@@ -120,14 +121,14 @@
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function cos(timestamp, period, amplitude, offset, phase) {
 | 
			
		||||
    function cos(timestamp, period, amplitude, offset, phase, randomness) {
 | 
			
		||||
        return amplitude *
 | 
			
		||||
            Math.cos(phase + (timestamp / period / 1000 * Math.PI * 2)) + offset;
 | 
			
		||||
            Math.cos(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sin(timestamp, period, amplitude, offset, phase) {
 | 
			
		||||
    function sin(timestamp, period, amplitude, offset, phase, randomness) {
 | 
			
		||||
        return amplitude *
 | 
			
		||||
            Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + offset;
 | 
			
		||||
            Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + (amplitude * Math.random() * randomness) + offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sendError(error, message) {
 | 
			
		||||
 
 | 
			
		||||
@@ -122,6 +122,17 @@ define([
 | 
			
		||||
                        "telemetry",
 | 
			
		||||
                        "phase"
 | 
			
		||||
                    ]
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: "Randomness",
 | 
			
		||||
                    control: "numberfield",
 | 
			
		||||
                    cssClass: "l-input-sm l-numeric",
 | 
			
		||||
                    key: "randomness",
 | 
			
		||||
                    required: true,
 | 
			
		||||
                    property: [
 | 
			
		||||
                        "telemetry",
 | 
			
		||||
                        "randomness"
 | 
			
		||||
                    ]
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            initialize: function (object) {
 | 
			
		||||
@@ -130,7 +141,8 @@ define([
 | 
			
		||||
                    amplitude: 1,
 | 
			
		||||
                    offset: 0,
 | 
			
		||||
                    dataRateInHz: 1,
 | 
			
		||||
                    phase: 0
 | 
			
		||||
                    phase: 0,
 | 
			
		||||
                    randomness: 0
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,14 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="example">{{ msg }}</div>
 | 
			
		||||
<div class="example">{{ msg }}</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
  data () {
 | 
			
		||||
    return {
 | 
			
		||||
      msg: 'Hello world!'
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            msg: 'Hello world!'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<!--
 | 
			
		||||
 Open MCT, Copyright (c) 2014-2017, United States Government
 | 
			
		||||
 Open MCT, Copyright (c) 2014-2020, United States Government
 | 
			
		||||
 as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 Administration. All rights reserved.
 | 
			
		||||
 | 
			
		||||
@@ -43,9 +43,9 @@
 | 
			
		||||
            openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        openmct.install(openmct.plugins.Snow());
 | 
			
		||||
        openmct.install(openmct.plugins.MyItems());
 | 
			
		||||
        openmct.install(openmct.plugins.LocalStorage());
 | 
			
		||||
        openmct.install(openmct.plugins.Espresso());
 | 
			
		||||
        openmct.install(openmct.plugins.MyItems());
 | 
			
		||||
        openmct.install(openmct.plugins.Generator());
 | 
			
		||||
        openmct.install(openmct.plugins.ExampleImagery());
 | 
			
		||||
        openmct.install(openmct.plugins.UTCTimeSystem());
 | 
			
		||||
 
 | 
			
		||||
@@ -24,16 +24,27 @@
 | 
			
		||||
 | 
			
		||||
const devMode = process.env.NODE_ENV !== 'production';
 | 
			
		||||
const browsers = [process.env.NODE_ENV === 'debug' ? 'ChromeDebugging' : 'ChromeHeadless'];
 | 
			
		||||
const coverageEnabled = process.env.COVERAGE === 'true';
 | 
			
		||||
const reporters = ['progress', 'html'];
 | 
			
		||||
 | 
			
		||||
if (coverageEnabled) {
 | 
			
		||||
    reporters.push('coverage-istanbul');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = (config) => {
 | 
			
		||||
    const webpackConfig = require('./webpack.config.js');
 | 
			
		||||
    delete webpackConfig.output;
 | 
			
		||||
 | 
			
		||||
    if (!devMode) {
 | 
			
		||||
    if (!devMode || coverageEnabled) {
 | 
			
		||||
        webpackConfig.module.rules.push({
 | 
			
		||||
            test: /\.js$/,
 | 
			
		||||
            exclude: /node_modules|example/,
 | 
			
		||||
            use: 'istanbul-instrumenter-loader'
 | 
			
		||||
            exclude: /node_modules|example|lib|dist/,
 | 
			
		||||
            use: {
 | 
			
		||||
                loader: 'istanbul-instrumenter-loader',
 | 
			
		||||
                options: {
 | 
			
		||||
                    esModules: true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -45,11 +56,7 @@ module.exports = (config) => {
 | 
			
		||||
            'src/**/*Spec.js'
 | 
			
		||||
        ],
 | 
			
		||||
        port: 9876,
 | 
			
		||||
        reporters: [
 | 
			
		||||
            'progress',
 | 
			
		||||
            'coverage',
 | 
			
		||||
            'html'
 | 
			
		||||
        ],
 | 
			
		||||
        reporters: reporters,
 | 
			
		||||
        browsers: browsers,
 | 
			
		||||
        customLaunchers: {
 | 
			
		||||
            ChromeDebugging: {
 | 
			
		||||
@@ -61,27 +68,27 @@ module.exports = (config) => {
 | 
			
		||||
        colors: true,
 | 
			
		||||
        logLevel: config.LOG_INFO,
 | 
			
		||||
        autoWatch: true,
 | 
			
		||||
        coverageReporter: {
 | 
			
		||||
            dir: process.env.CIRCLE_ARTIFACTS ?
 | 
			
		||||
                process.env.CIRCLE_ARTIFACTS + '/coverage' :
 | 
			
		||||
                "dist/reports/coverage",
 | 
			
		||||
            check: {
 | 
			
		||||
                global: {
 | 
			
		||||
                    lines: 80,
 | 
			
		||||
                    excludes: ['src/plugins/plot/**/*.js']
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        // HTML test reporting.
 | 
			
		||||
        htmlReporter: {
 | 
			
		||||
            outputDir: "dist/reports/tests",
 | 
			
		||||
            preserveDescribeNesting: true,
 | 
			
		||||
            foldAll: false
 | 
			
		||||
        },
 | 
			
		||||
        coverageIstanbulReporter: {
 | 
			
		||||
            fixWebpackSourcePaths: true,
 | 
			
		||||
            dir: process.env.CIRCLE_ARTIFACTS ?
 | 
			
		||||
                process.env.CIRCLE_ARTIFACTS + '/coverage' :
 | 
			
		||||
                "dist/reports/coverage",
 | 
			
		||||
            reports: ['html', 'lcovonly', 'text-summary'],
 | 
			
		||||
            thresholds: {
 | 
			
		||||
                global: {
 | 
			
		||||
                    lines: 62
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        preprocessors: {
 | 
			
		||||
            // add webpack as preprocessor
 | 
			
		||||
            'platform/**/*Spec.js': [ 'webpack', 'sourcemap' ],
 | 
			
		||||
            'src/**/*Spec.js': [ 'webpack', 'sourcemap' ]
 | 
			
		||||
            'platform/**/*Spec.js': ['webpack', 'sourcemap'],
 | 
			
		||||
            'src/**/*Spec.js': ['webpack', 'sourcemap']
 | 
			
		||||
        },
 | 
			
		||||
        webpack: webpackConfig,
 | 
			
		||||
        webpackMiddleware: {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								package.json
									
									
									
									
									
								
							@@ -4,8 +4,7 @@
 | 
			
		||||
  "description": "The Open MCT core platform",
 | 
			
		||||
  "dependencies": {},
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "acorn": "6.2.0",
 | 
			
		||||
    "angular": "1.4.14",
 | 
			
		||||
    "angular": "1.7.9",
 | 
			
		||||
    "angular-route": "1.4.14",
 | 
			
		||||
    "babel-eslint": "8.2.6",
 | 
			
		||||
    "comma-separated-values": "^3.6.4",
 | 
			
		||||
@@ -25,6 +24,7 @@
 | 
			
		||||
    "d3-time-format": "2.1.x",
 | 
			
		||||
    "eslint": "5.2.0",
 | 
			
		||||
    "eslint-plugin-vue": "^6.0.0",
 | 
			
		||||
    "eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0",
 | 
			
		||||
    "eventemitter3": "^1.2.0",
 | 
			
		||||
    "exports-loader": "^0.7.0",
 | 
			
		||||
    "express": "^4.13.1",
 | 
			
		||||
@@ -43,19 +43,20 @@
 | 
			
		||||
    "karma-chrome-launcher": "^2.2.0",
 | 
			
		||||
    "karma-cli": "^1.0.1",
 | 
			
		||||
    "karma-coverage": "^1.1.2",
 | 
			
		||||
    "karma-coverage-istanbul-reporter": "^2.1.1",
 | 
			
		||||
    "karma-html-reporter": "^0.2.7",
 | 
			
		||||
    "karma-jasmine": "^1.1.2",
 | 
			
		||||
    "karma-sourcemap-loader": "^0.3.7",
 | 
			
		||||
    "karma-webpack": "^3.0.0",
 | 
			
		||||
    "location-bar": "^3.0.1",
 | 
			
		||||
    "lodash": "^3.10.1",
 | 
			
		||||
    "lodash": "^4.17.12",
 | 
			
		||||
    "markdown-toc": "^0.11.7",
 | 
			
		||||
    "marked": "^0.3.5",
 | 
			
		||||
    "mini-css-extract-plugin": "^0.4.1",
 | 
			
		||||
    "minimist": "^1.1.1",
 | 
			
		||||
    "moment": "^2.11.1",
 | 
			
		||||
    "moment": "2.25.3",
 | 
			
		||||
    "moment-duration-format": "^2.2.2",
 | 
			
		||||
    "moment-timezone": "^0.5.21",
 | 
			
		||||
    "moment-timezone": "0.5.28",
 | 
			
		||||
    "node-bourbon": "^4.2.3",
 | 
			
		||||
    "node-sass": "^4.9.2",
 | 
			
		||||
    "painterro": "^0.2.65",
 | 
			
		||||
@@ -76,14 +77,16 @@
 | 
			
		||||
    "zepto": "^1.2.0"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "clean": "rm -rf ./dist",
 | 
			
		||||
    "start": "node app.js",
 | 
			
		||||
    "lint": "eslint platform example src/**/*.{js,vue} openmct.js",
 | 
			
		||||
    "lint:fix": "eslint platform example src/**/*.{js,vue} openmct.js --fix",
 | 
			
		||||
    "lint": "eslint platform example src --ext .js,.vue openmct.js",
 | 
			
		||||
    "lint:fix": "eslint platform example src --ext .js,.vue openmct.js --fix",
 | 
			
		||||
    "build:prod": "cross-env NODE_ENV=production webpack",
 | 
			
		||||
    "build:dev": "webpack",
 | 
			
		||||
    "build:watch": "webpack --watch",
 | 
			
		||||
    "test": "karma start --single-run",
 | 
			
		||||
    "test-debug": "cross-env NODE_ENV=debug karma start --no-single-run",
 | 
			
		||||
    "test:debug": "cross-env NODE_ENV=debug karma start --no-single-run",
 | 
			
		||||
    "test:coverage": "./scripts/test-coverage.sh",
 | 
			
		||||
    "test:watch": "karma start --no-single-run",
 | 
			
		||||
    "verify": "concurrently 'npm:test' 'npm:lint'",
 | 
			
		||||
    "jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(
 | 
			
		||||
    ['../../../../../src/api/objects/object-utils'],
 | 
			
		||||
    ['objectUtils'],
 | 
			
		||||
    function (objectUtils) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@
 | 
			
		||||
 * @namespace platform/containment
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    ['../../../src/api/objects/object-utils'],
 | 
			
		||||
    ['objectUtils'],
 | 
			
		||||
    function (objectUtils) {
 | 
			
		||||
 | 
			
		||||
        function PersistableCompositionPolicy(openmct) {
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,7 @@ define(
 | 
			
		||||
                baseContext = context || {};
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var actionContext = _.extend({}, baseContext);
 | 
			
		||||
            var actionContext = Object.assign({}, baseContext);
 | 
			
		||||
            actionContext.domainObject = this.domainObject;
 | 
			
		||||
 | 
			
		||||
            return this.actionService.getActions(actionContext);
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,11 @@ define([
 | 
			
		||||
                bootstrapper
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        // Override of angular1.6 ! hashPrefix
 | 
			
		||||
        app.config(['$locationProvider', function ($locationProvider) {
 | 
			
		||||
            $locationProvider.hashPrefix('');
 | 
			
		||||
        }]);
 | 
			
		||||
 | 
			
		||||
        // Apply logging levels; this must be done now, before the
 | 
			
		||||
        // first log statement.
 | 
			
		||||
        new LogLevel(logLevel).configure(app, $log);
 | 
			
		||||
 
 | 
			
		||||
@@ -121,7 +121,7 @@ define(['lodash'], function (_) {
 | 
			
		||||
     */
 | 
			
		||||
    ExportAsJSONAction.prototype.rewriteLink = function (child, parent) {
 | 
			
		||||
        this.externalIdentifiers.push(this.getId(child));
 | 
			
		||||
        var index = _.findIndex(parent.composition, function (id) {
 | 
			
		||||
        var index = parent.composition.findIndex(id => {
 | 
			
		||||
            return _.isEqual(child.identifier, id);
 | 
			
		||||
        });
 | 
			
		||||
        var copyOfChild = this.copyObject(child);
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
 * this source code distribution or the Licensing information page available
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
define(['zepto', '../../../../src/api/objects/object-utils.js'], function ($, objectUtils) {
 | 
			
		||||
define(['zepto', 'objectUtils'], function ($, objectUtils) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The ImportAsJSONAction is available from context menus and allows a user
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ define(
 | 
			
		||||
        var CONNECTED = {
 | 
			
		||||
                text: "Connected",
 | 
			
		||||
                glyphClass: "ok",
 | 
			
		||||
                statusClass: "s-status-ok",
 | 
			
		||||
                statusClass: "s-status-on",
 | 
			
		||||
                description: "Connected to the domain object database."
 | 
			
		||||
            },
 | 
			
		||||
            DISCONNECTED = {
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@ define([
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "key": "ELASTIC_PATH",
 | 
			
		||||
                        "value": "mct/domain_object",
 | 
			
		||||
                        "value": "mct/_doc",
 | 
			
		||||
                        "priority": "fallback"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ define(
 | 
			
		||||
        var CONNECTED = {
 | 
			
		||||
                text: "Connected",
 | 
			
		||||
                glyphClass: "ok",
 | 
			
		||||
                statusClass: "s-status-ok",
 | 
			
		||||
                statusClass: "s-status-on",
 | 
			
		||||
                description: "Connected to the domain object database."
 | 
			
		||||
            },
 | 
			
		||||
            DISCONNECTED = {
 | 
			
		||||
 
 | 
			
		||||
@@ -32,9 +32,9 @@ define(
 | 
			
		||||
        // JSLint doesn't like underscore-prefixed properties,
 | 
			
		||||
        // so hide them here.
 | 
			
		||||
        var SRC = "_source",
 | 
			
		||||
            REV = "_version",
 | 
			
		||||
            ID = "_id",
 | 
			
		||||
            CONFLICT = 409;
 | 
			
		||||
            CONFLICT = 409,
 | 
			
		||||
            SEQ_NO = "_seq_no",
 | 
			
		||||
            PRIMARY_TERM = "_primary_term";
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The ElasticPersistenceProvider reads and writes JSON documents
 | 
			
		||||
@@ -104,7 +104,8 @@ define(
 | 
			
		||||
        // Get a domain object model out of ElasticSearch's response
 | 
			
		||||
        ElasticPersistenceProvider.prototype.getModel = function (response) {
 | 
			
		||||
            if (response && response[SRC]) {
 | 
			
		||||
                this.revs[response[ID]] = response[REV];
 | 
			
		||||
                this.revs[response[SEQ_NO]] = response[SEQ_NO];
 | 
			
		||||
                this.revs[response[PRIMARY_TERM]] = response[PRIMARY_TERM];
 | 
			
		||||
                return response[SRC];
 | 
			
		||||
            } else {
 | 
			
		||||
                return undefined;
 | 
			
		||||
@@ -116,7 +117,8 @@ define(
 | 
			
		||||
        // indicate that the request failed.
 | 
			
		||||
        ElasticPersistenceProvider.prototype.checkResponse = function (response, key) {
 | 
			
		||||
            if (response && !response.error) {
 | 
			
		||||
                this.revs[key] = response[REV];
 | 
			
		||||
                this.revs[SEQ_NO] = response[SEQ_NO];
 | 
			
		||||
                this.revs[PRIMARY_TERM] = response[PRIMARY_TERM];
 | 
			
		||||
                return response;
 | 
			
		||||
            } else {
 | 
			
		||||
                return this.handleError(response, key);
 | 
			
		||||
@@ -147,7 +149,7 @@ define(
 | 
			
		||||
            function checkUpdate(response) {
 | 
			
		||||
                return self.checkResponse(response, key);
 | 
			
		||||
            }
 | 
			
		||||
            return this.put(key, value, { version: this.revs[key] })
 | 
			
		||||
            return this.put(key, value)
 | 
			
		||||
                .then(checkUpdate);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -85,7 +85,7 @@ define(
 | 
			
		||||
            it("allows object creation", function () {
 | 
			
		||||
                var model = { someKey: "some value" };
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_version": 1 }
 | 
			
		||||
                    data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1 }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.createObject("testSpace", "abc", model).then(capture);
 | 
			
		||||
                expect(mockHttp).toHaveBeenCalledWith({
 | 
			
		||||
@@ -100,7 +100,7 @@ define(
 | 
			
		||||
            it("allows object models to be read back", function () {
 | 
			
		||||
                var model = { someKey: "some value" };
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_version": 1, "_source": model }
 | 
			
		||||
                    data: { "_id": "abc", "_seq_no": 1, "_primary_term": 1, "_source": model }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.readObject("testSpace", "abc").then(capture);
 | 
			
		||||
                expect(mockHttp).toHaveBeenCalledWith({
 | 
			
		||||
@@ -117,19 +117,19 @@ define(
 | 
			
		||||
 | 
			
		||||
                // First do a read to populate rev tags...
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_version": 42, "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.readObject("testSpace", "abc");
 | 
			
		||||
 | 
			
		||||
                // Now perform an update
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_version": 43, "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_seq_no": 1, "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.updateObject("testSpace", "abc", model).then(capture);
 | 
			
		||||
                expect(mockHttp).toHaveBeenCalledWith({
 | 
			
		||||
                    url: "/test/db/abc",
 | 
			
		||||
                    method: "PUT",
 | 
			
		||||
                    params: { version: 42 },
 | 
			
		||||
                    params: undefined,
 | 
			
		||||
                    data: model
 | 
			
		||||
                });
 | 
			
		||||
                expect(capture.calls.mostRecent().args[0]).toBeTruthy();
 | 
			
		||||
@@ -138,13 +138,13 @@ define(
 | 
			
		||||
            it("allows object deletion", function () {
 | 
			
		||||
                // First do a read to populate rev tags...
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_version": 42, "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.readObject("testSpace", "abc");
 | 
			
		||||
 | 
			
		||||
                // Now perform an update
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_version": 42, "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.deleteObject("testSpace", "abc", {}).then(capture);
 | 
			
		||||
                expect(mockHttp).toHaveBeenCalledWith({
 | 
			
		||||
@@ -167,13 +167,13 @@ define(
 | 
			
		||||
                expect(capture).toHaveBeenCalledWith(undefined);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("handles rejection due to version", function () {
 | 
			
		||||
            it("handles rejection due to _seq_no", function () {
 | 
			
		||||
                var model = { someKey: "some value" },
 | 
			
		||||
                    mockErrorCallback = jasmine.createSpy('error');
 | 
			
		||||
 | 
			
		||||
                // First do a read to populate rev tags...
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_version": 42, "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_seq_no": 1, "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.readObject("testSpace", "abc");
 | 
			
		||||
 | 
			
		||||
@@ -196,7 +196,7 @@ define(
 | 
			
		||||
 | 
			
		||||
                // First do a read to populate rev tags...
 | 
			
		||||
                mockHttp.and.returnValue(mockPromise({
 | 
			
		||||
                    data: { "_id": "abc", "_version": 42, "_source": {} }
 | 
			
		||||
                    data: { "_id": "abc", "_seq_no": 1, "_source": {} }
 | 
			
		||||
                }));
 | 
			
		||||
                provider.readObject("testSpace", "abc");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
 * Module defining GenericSearchProvider. Created by shale on 07/16/2015.
 | 
			
		||||
 */
 | 
			
		||||
define([
 | 
			
		||||
    '../../../../src/api/objects/object-utils',
 | 
			
		||||
    'objectUtils',
 | 
			
		||||
    'lodash'
 | 
			
		||||
], function (
 | 
			
		||||
    objectUtils,
 | 
			
		||||
@@ -191,7 +191,7 @@ define([
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var domainObject = objectUtils.toNewFormat(model, id);
 | 
			
		||||
        var composition = _.find(this.openmct.composition.registry, function (p) {
 | 
			
		||||
        var composition = this.openmct.composition.registry.find(p => {
 | 
			
		||||
            return p.appliesTo(domainObject);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
 */
 | 
			
		||||
define(
 | 
			
		||||
    [
 | 
			
		||||
        '../../../src/api/objects/object-utils',
 | 
			
		||||
        'objectUtils',
 | 
			
		||||
        'lodash'
 | 
			
		||||
    ],
 | 
			
		||||
    function (
 | 
			
		||||
@@ -235,7 +235,7 @@ define(
 | 
			
		||||
            var defaultRange = metadata.valuesForHints(['range'])[0];
 | 
			
		||||
            defaultRange = defaultRange ? defaultRange.key : undefined;
 | 
			
		||||
 | 
			
		||||
            var sourceMap = _.indexBy(metadata.values(), 'key');
 | 
			
		||||
            var sourceMap = _.keyBy(metadata.values(), 'key');
 | 
			
		||||
 | 
			
		||||
            var isLegacyProvider = telemetryAPI.findRequestProvider(domainObject) ===
 | 
			
		||||
                telemetryAPI.legacyProvider;
 | 
			
		||||
@@ -300,7 +300,7 @@ define(
 | 
			
		||||
            var defaultRange = metadata.valuesForHints(['range'])[0];
 | 
			
		||||
            defaultRange = defaultRange ? defaultRange.key : undefined;
 | 
			
		||||
 | 
			
		||||
            var sourceMap = _.indexBy(metadata.values(), 'key');
 | 
			
		||||
            var sourceMap = _.keyBy(metadata.values(), 'key');
 | 
			
		||||
 | 
			
		||||
            var isLegacyProvider = telemetryAPI.findSubscriptionProvider(domainObject) ===
 | 
			
		||||
                telemetryAPI.legacyProvider;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								scripts/test-coverage.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										2
									
								
								scripts/test-coverage.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
export NODE_OPTIONS=--max_old_space_size=4096
 | 
			
		||||
cross-env COVERAGE=true karma start --single-run
 | 
			
		||||
							
								
								
									
										44
									
								
								src/MCT.js
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								src/MCT.js
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2019, United States Government
 | 
			
		||||
 * Open MCT, Copyright (c) 2014-2020, United States Government
 | 
			
		||||
 * as represented by the Administrator of the National Aeronautics and Space
 | 
			
		||||
 * Administration. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
@@ -28,7 +28,7 @@ define([
 | 
			
		||||
    './api/api',
 | 
			
		||||
    './api/overlays/OverlayAPI',
 | 
			
		||||
    './selection/Selection',
 | 
			
		||||
    './api/objects/object-utils',
 | 
			
		||||
    'objectUtils',
 | 
			
		||||
    './plugins/plugins',
 | 
			
		||||
    './adapter/indicators/legacy-indicators-plugin',
 | 
			
		||||
    './plugins/buildInfo/plugin',
 | 
			
		||||
@@ -249,7 +249,7 @@ define([
 | 
			
		||||
        this.legacyRegistry = new BundleRegistry();
 | 
			
		||||
        installDefaultBundles(this.legacyRegistry);
 | 
			
		||||
 | 
			
		||||
        // Plugin's that are installed by default
 | 
			
		||||
        // Plugins that are installed by default
 | 
			
		||||
 | 
			
		||||
        this.install(this.plugins.Plot());
 | 
			
		||||
        this.install(this.plugins.TelemetryTable());
 | 
			
		||||
@@ -265,6 +265,7 @@ define([
 | 
			
		||||
        this.install(this.plugins.ImportExport());
 | 
			
		||||
        this.install(this.plugins.WebPage());
 | 
			
		||||
        this.install(this.plugins.Condition());
 | 
			
		||||
        this.install(this.plugins.ConditionWidget());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MCT.prototype = Object.create(EventEmitter.prototype);
 | 
			
		||||
@@ -349,17 +350,13 @@ define([
 | 
			
		||||
     * @param {HTMLElement} [domElement] the DOM element in which to run
 | 
			
		||||
     *        MCT; if undefined, MCT will be run in the body of the document
 | 
			
		||||
     */
 | 
			
		||||
    MCT.prototype.start = function (domElement) {
 | 
			
		||||
    MCT.prototype.start = function (domElement = document.body, isHeadlessMode = false) {
 | 
			
		||||
        if (!this.plugins.DisplayLayout._installed) {
 | 
			
		||||
            this.install(this.plugins.DisplayLayout({
 | 
			
		||||
                showAsView: ['summary-widget']
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!domElement) {
 | 
			
		||||
            domElement = document.body;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.element = domElement;
 | 
			
		||||
 | 
			
		||||
        this.legacyExtension('runs', {
 | 
			
		||||
@@ -399,24 +396,31 @@ define([
 | 
			
		||||
                // something has depended upon objectService.  Cool, right?
 | 
			
		||||
                this.$injector.get('objectService');
 | 
			
		||||
 | 
			
		||||
                var appLayout = new Vue({
 | 
			
		||||
                    components: {
 | 
			
		||||
                        'Layout': Layout.default
 | 
			
		||||
                    },
 | 
			
		||||
                    provide: {
 | 
			
		||||
                        openmct: this
 | 
			
		||||
                    },
 | 
			
		||||
                    template: '<Layout ref="layout"></Layout>'
 | 
			
		||||
                });
 | 
			
		||||
                domElement.appendChild(appLayout.$mount().$el);
 | 
			
		||||
                if (!isHeadlessMode) {
 | 
			
		||||
                    var appLayout = new Vue({
 | 
			
		||||
                        components: {
 | 
			
		||||
                            'Layout': Layout.default
 | 
			
		||||
                        },
 | 
			
		||||
                        provide: {
 | 
			
		||||
                            openmct: this
 | 
			
		||||
                        },
 | 
			
		||||
                        template: '<Layout ref="layout"></Layout>'
 | 
			
		||||
                    });
 | 
			
		||||
                    domElement.appendChild(appLayout.$mount().$el);
 | 
			
		||||
 | 
			
		||||
                this.layout = appLayout.$refs.layout;
 | 
			
		||||
                Browse(this);
 | 
			
		||||
                    this.layout = appLayout.$refs.layout;
 | 
			
		||||
                    Browse(this);
 | 
			
		||||
                }
 | 
			
		||||
                this.router.start();
 | 
			
		||||
                this.emit('start');
 | 
			
		||||
            }.bind(this));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    MCT.prototype.startHeadless = function () {
 | 
			
		||||
        let unreachableNode = document.createElement('div');
 | 
			
		||||
        return this.start(unreachableNode, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Install a plugin in MCT.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,11 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    './MCT',
 | 
			
		||||
    './plugins/plugins',
 | 
			
		||||
    'legacyRegistry'
 | 
			
		||||
], function (MCT, plugins, legacyRegistry) {
 | 
			
		||||
    xdescribe("MCT", function () {
 | 
			
		||||
    'legacyRegistry',
 | 
			
		||||
    'testUtils'
 | 
			
		||||
], function (plugins, legacyRegistry, testUtils) {
 | 
			
		||||
    describe("MCT", function () {
 | 
			
		||||
        var openmct;
 | 
			
		||||
        var mockPlugin;
 | 
			
		||||
        var mockPlugin2;
 | 
			
		||||
@@ -38,7 +38,7 @@ define([
 | 
			
		||||
            mockListener = jasmine.createSpy('listener');
 | 
			
		||||
            oldBundles = legacyRegistry.list();
 | 
			
		||||
 | 
			
		||||
            openmct = new MCT();
 | 
			
		||||
            openmct = testUtils.createOpenMct();
 | 
			
		||||
 | 
			
		||||
            openmct.install(mockPlugin);
 | 
			
		||||
            openmct.install(mockPlugin2);
 | 
			
		||||
@@ -63,8 +63,11 @@ define([
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe("start", function () {
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                openmct.start();
 | 
			
		||||
            let appHolder;
 | 
			
		||||
            beforeEach(function (done) {
 | 
			
		||||
                appHolder = document.createElement("div");
 | 
			
		||||
                openmct.on('start', done);
 | 
			
		||||
                openmct.start(appHolder);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("calls plugins for configuration", function () {
 | 
			
		||||
@@ -75,25 +78,51 @@ define([
 | 
			
		||||
            it("emits a start event", function () {
 | 
			
		||||
                expect(mockListener).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("Renders the application into the provided container element", function () {
 | 
			
		||||
                let openMctShellElements = appHolder.querySelectorAll('div.l-shell');
 | 
			
		||||
                expect(openMctShellElements.length).toBe(1);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe("startHeadless", function () {
 | 
			
		||||
            beforeEach(function (done) {
 | 
			
		||||
                openmct.on('start', done);
 | 
			
		||||
                openmct.startHeadless();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("calls plugins for configuration", function () {
 | 
			
		||||
                expect(mockPlugin).toHaveBeenCalledWith(openmct);
 | 
			
		||||
                expect(mockPlugin2).toHaveBeenCalledWith(openmct);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("emits a start event", function () {
 | 
			
		||||
                expect(mockListener).toHaveBeenCalled();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("Does not render Open MCT", function () {
 | 
			
		||||
                let openMctShellElements = document.body.querySelectorAll('div.l-shell');
 | 
			
		||||
                expect(openMctShellElements.length).toBe(0);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe("setAssetPath", function () {
 | 
			
		||||
            var testAssetPath;
 | 
			
		||||
 | 
			
		||||
            beforeEach(function () {
 | 
			
		||||
                testAssetPath = "some/path";
 | 
			
		||||
                openmct.legacyExtension = jasmine.createSpy('legacyExtension');
 | 
			
		||||
                openmct.setAssetPath(testAssetPath);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("internally configures the path for assets", function () {
 | 
			
		||||
                expect(openmct.legacyExtension).toHaveBeenCalledWith(
 | 
			
		||||
                    'constants',
 | 
			
		||||
                    {
 | 
			
		||||
                        key: "ASSETS_PATH",
 | 
			
		||||
                        value: testAssetPath
 | 
			
		||||
                    }
 | 
			
		||||
                );
 | 
			
		||||
            it("configures the path for assets", function () {
 | 
			
		||||
                testAssetPath = "some/path/";
 | 
			
		||||
                openmct.setAssetPath(testAssetPath);
 | 
			
		||||
                expect(openmct.getAssetPath()).toBe(testAssetPath);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it("adds a trailing /", function () {
 | 
			
		||||
                testAssetPath = "some/path";
 | 
			
		||||
                openmct.setAssetPath(testAssetPath);
 | 
			
		||||
                expect(openmct.getAssetPath()).toBe(testAssetPath + "/");
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    '../../api/objects/object-utils'
 | 
			
		||||
    'objectUtils'
 | 
			
		||||
], function (objectUtils) {
 | 
			
		||||
    function ActionDialogDecorator(mct, actionService) {
 | 
			
		||||
        this.mct = mct;
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define(['../../api/objects/object-utils'], function (objectUtils) {
 | 
			
		||||
define(['objectUtils'], function (objectUtils) {
 | 
			
		||||
    function AdapterCapability(domainObject) {
 | 
			
		||||
        this.domainObject = domainObject;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
 * Module defining AlternateCompositionCapability. Created by vwoeltje on 11/7/14.
 | 
			
		||||
 */
 | 
			
		||||
define([
 | 
			
		||||
    '../../api/objects/object-utils',
 | 
			
		||||
    'objectUtils',
 | 
			
		||||
    '../../../platform/core/src/capabilities/ContextualDomainObject'
 | 
			
		||||
], function (objectUtils, ContextualDomainObject) {
 | 
			
		||||
    function AlternateCompositionCapability($injector, domainObject) {
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ define([
 | 
			
		||||
            var capability = viewConstructor(domainObject);
 | 
			
		||||
            var oldInvoke = capability.invoke.bind(capability);
 | 
			
		||||
 | 
			
		||||
            /* eslint-disable you-dont-need-lodash-underscore/map */
 | 
			
		||||
            capability.invoke = function () {
 | 
			
		||||
                var availableViews = oldInvoke();
 | 
			
		||||
                var newDomainObject = capability
 | 
			
		||||
@@ -52,6 +53,8 @@ define([
 | 
			
		||||
                    .map('view')
 | 
			
		||||
                    .value();
 | 
			
		||||
            };
 | 
			
		||||
            /* eslint-enable you-dont-need-lodash-underscore/map */
 | 
			
		||||
 | 
			
		||||
            return capability;
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    '../capabilities/AlternateCompositionCapability',
 | 
			
		||||
    '../../api/objects/object-utils'
 | 
			
		||||
    'objectUtils'
 | 
			
		||||
], function (
 | 
			
		||||
    AlternateCompositionCapability,
 | 
			
		||||
    objectUtils
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    '../../api/objects/object-utils'
 | 
			
		||||
    'objectUtils'
 | 
			
		||||
], function (
 | 
			
		||||
    utils
 | 
			
		||||
) {
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,7 @@ define([
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    TimeSettingsURLHandler.prototype.parseQueryParams = function () {
 | 
			
		||||
        var searchParams = _.pick(this.$location.search(), _.values(SEARCH));
 | 
			
		||||
        var searchParams = _.pick(this.$location.search(), Object.values(SEARCH));
 | 
			
		||||
        var parsedParams = {
 | 
			
		||||
            clock: searchParams[SEARCH.MODE],
 | 
			
		||||
            timeSystem: searchParams[SEARCH.TIME_SYSTEM]
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    '../../api/objects/object-utils'
 | 
			
		||||
    'objectUtils'
 | 
			
		||||
], function (
 | 
			
		||||
    utils
 | 
			
		||||
) {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    '../../api/objects/object-utils'
 | 
			
		||||
    'objectUtils'
 | 
			
		||||
], function (
 | 
			
		||||
    objectUtils
 | 
			
		||||
) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
define([
 | 
			
		||||
    './LegacyViewProvider',
 | 
			
		||||
    './TypeInspectorViewProvider',
 | 
			
		||||
    '../../api/objects/object-utils'
 | 
			
		||||
    'objectUtils'
 | 
			
		||||
], function (
 | 
			
		||||
    LegacyViewProvider,
 | 
			
		||||
    TypeInspectorViewProvider,
 | 
			
		||||
 
 | 
			
		||||
@@ -70,7 +70,7 @@ define([
 | 
			
		||||
     * @memberof module:openmct.CompositionAPI#
 | 
			
		||||
     */
 | 
			
		||||
    CompositionAPI.prototype.get = function (domainObject) {
 | 
			
		||||
        var provider = _.find(this.registry, function (p) {
 | 
			
		||||
        var provider = this.registry.find(p => {
 | 
			
		||||
            return p.appliesTo(domainObject);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -122,7 +122,7 @@ define([
 | 
			
		||||
            throw new Error('Event not supported by composition: ' + event);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var index = _.findIndex(this.listeners[event], function (l) {
 | 
			
		||||
        var index = this.listeners[event].findIndex(l => {
 | 
			
		||||
            return l.callback === callback && l.context === context;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    'lodash',
 | 
			
		||||
    '../objects/object-utils'
 | 
			
		||||
    'objectUtils'
 | 
			
		||||
], function (
 | 
			
		||||
    _,
 | 
			
		||||
    objectUtils
 | 
			
		||||
@@ -143,7 +143,7 @@ define([
 | 
			
		||||
        var keyString = objectUtils.makeKeyString(domainObject.identifier);
 | 
			
		||||
        var objectListeners = this.listeningTo[keyString];
 | 
			
		||||
 | 
			
		||||
        var index = _.findIndex(objectListeners[event], function (l) {
 | 
			
		||||
        var index = objectListeners[event].findIndex(l => {
 | 
			
		||||
            return l.callback === callback && l.context === context;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -196,8 +196,8 @@ define([
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    DefaultCompositionProvider.prototype.includes = function (parent, childId) {
 | 
			
		||||
        return parent.composition.findIndex(composee =>
 | 
			
		||||
            this.publicAPI.objects.areIdsEqual(composee, childId)) !== -1;
 | 
			
		||||
        return parent.composition.some(composee =>
 | 
			
		||||
            this.publicAPI.objects.areIdsEqual(composee, childId));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    './object-utils.js',
 | 
			
		||||
    'objectUtils',
 | 
			
		||||
    'lodash'
 | 
			
		||||
], function (
 | 
			
		||||
    utils,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
define([
 | 
			
		||||
    'lodash',
 | 
			
		||||
    './object-utils',
 | 
			
		||||
    'objectUtils',
 | 
			
		||||
    './MutableObject',
 | 
			
		||||
    './RootRegistry',
 | 
			
		||||
    './RootObjectProvider',
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ define([
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    RootRegistry.prototype.addRoot = function (key) {
 | 
			
		||||
        if (isKey(key) || (_.isArray(key) && _.every(key, isKey))) {
 | 
			
		||||
        if (isKey(key) || (Array.isArray(key) && key.every(isKey))) {
 | 
			
		||||
            this.providers.push(function () {
 | 
			
		||||
                return key;
 | 
			
		||||
            });
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
define([
 | 
			
		||||
    '../object-utils'
 | 
			
		||||
    'objectUtils'
 | 
			
		||||
], function (
 | 
			
		||||
    objectUtils
 | 
			
		||||
) {
 | 
			
		||||
 
 | 
			
		||||
@@ -85,9 +85,9 @@ define([
 | 
			
		||||
                            value: +e.value
 | 
			
		||||
                        };
 | 
			
		||||
                    }), 'e.value');
 | 
			
		||||
                valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
 | 
			
		||||
                valueMetadata.max = _.max(valueMetadata.values);
 | 
			
		||||
                valueMetadata.min = _.min(valueMetadata.values);
 | 
			
		||||
                valueMetadata.values = valueMetadata.enumerations.map(e => e.value);
 | 
			
		||||
                valueMetadata.max = Math.max(valueMetadata.values);
 | 
			
		||||
                valueMetadata.min = Math.min(valueMetadata.values);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            valueMetadatas.push(valueMetadata);
 | 
			
		||||
@@ -103,7 +103,7 @@ define([
 | 
			
		||||
        var metadata = domainObject.telemetry || {};
 | 
			
		||||
        if (this.typeHasTelemetry(domainObject)) {
 | 
			
		||||
            var typeMetadata = this.typeService.getType(domainObject.type).typeDef.telemetry;
 | 
			
		||||
            _.extend(metadata, typeMetadata);
 | 
			
		||||
            Object.assign(metadata, typeMetadata);
 | 
			
		||||
            if (!metadata.values) {
 | 
			
		||||
                metadata.values = valueMetadatasFromOldFormat(metadata);
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ define([
 | 
			
		||||
    './TelemetryMetadataManager',
 | 
			
		||||
    './TelemetryValueFormatter',
 | 
			
		||||
    './DefaultMetadataProvider',
 | 
			
		||||
    '../objects/object-utils',
 | 
			
		||||
    'objectUtils',
 | 
			
		||||
    'lodash'
 | 
			
		||||
], function (
 | 
			
		||||
    TelemetryMetadataManager,
 | 
			
		||||
@@ -370,7 +370,7 @@ define([
 | 
			
		||||
    TelemetryAPI.prototype.commonValuesForHints = function (metadatas, hints) {
 | 
			
		||||
        var options = metadatas.map(function (metadata) {
 | 
			
		||||
            var values = metadata.valuesForHints(hints);
 | 
			
		||||
            return _.indexBy(values, 'key');
 | 
			
		||||
            return _.keyBy(values, 'key');
 | 
			
		||||
        }).reduce(function (a, b) {
 | 
			
		||||
            var results = {};
 | 
			
		||||
            Object.keys(a).forEach(function (key) {
 | 
			
		||||
@@ -383,7 +383,7 @@ define([
 | 
			
		||||
        var sortKeys = hints.map(function (h) {
 | 
			
		||||
            return 'hints.' + h;
 | 
			
		||||
        });
 | 
			
		||||
        return _.sortByAll(options, sortKeys);
 | 
			
		||||
        return _.sortBy(options, sortKeys);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -57,13 +57,13 @@ define([
 | 
			
		||||
 | 
			
		||||
        if (valueMetadata.format === 'enum') {
 | 
			
		||||
            if (!valueMetadata.values) {
 | 
			
		||||
                valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
 | 
			
		||||
                valueMetadata.values = valueMetadata.enumerations.map(e => e.value);
 | 
			
		||||
            }
 | 
			
		||||
            if (!valueMetadata.hasOwnProperty('max')) {
 | 
			
		||||
                valueMetadata.max = _.max(valueMetadata.values) + 1;
 | 
			
		||||
                valueMetadata.max = Math.max(valueMetadata.values) + 1;
 | 
			
		||||
            }
 | 
			
		||||
            if (!valueMetadata.hasOwnProperty('min')) {
 | 
			
		||||
                valueMetadata.min = _.min(valueMetadata.values) - 1;
 | 
			
		||||
                valueMetadata.min = Math.min(valueMetadata.values) - 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -81,7 +81,7 @@ define([
 | 
			
		||||
    function TelemetryMetadataManager(metadata) {
 | 
			
		||||
        this.metadata = metadata;
 | 
			
		||||
 | 
			
		||||
        this.valueMetadatas = this.metadata.values.map(applyReasonableDefaults);
 | 
			
		||||
        this.valueMetadatas = this.metadata.values ? this.metadata.values.map(applyReasonableDefaults) : [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -121,7 +121,7 @@ define([
 | 
			
		||||
                return metadata.hints[hint];
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        return _.sortByAll(matchingMetadata, ...iteratees);
 | 
			
		||||
        return _.sortBy(matchingMetadata, ...iteratees);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    TelemetryMetadataManager.prototype.getFilterableValues = function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -81,6 +81,24 @@ define([
 | 
			
		||||
                return printj.sprintf(formatString, baseFormat.call(this, value));
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        if (valueMetadata.format === 'string') {
 | 
			
		||||
            this.formatter.parse = function (value) {
 | 
			
		||||
                if (value === undefined) {
 | 
			
		||||
                    return '';
 | 
			
		||||
                }
 | 
			
		||||
                if (typeof value === 'string') {
 | 
			
		||||
                    return value;
 | 
			
		||||
                } else {
 | 
			
		||||
                    return value.toString();
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            this.formatter.format = function (value) {
 | 
			
		||||
                return value;
 | 
			
		||||
            };
 | 
			
		||||
            this.formatter.validate = function (value) {
 | 
			
		||||
                return typeof value === 'string';
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TelemetryValueFormatter.prototype.parse = function (datum) {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,22 +21,24 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<table class="c-table c-lad-table">
 | 
			
		||||
    <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <th>Name</th>
 | 
			
		||||
            <th>Timestamp</th>
 | 
			
		||||
            <th>Value</th>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody>
 | 
			
		||||
        <lad-row
 | 
			
		||||
            v-for="item in items"
 | 
			
		||||
            :key="item.key"
 | 
			
		||||
            :domain-object="item.domainObject"
 | 
			
		||||
        />
 | 
			
		||||
    </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
<div class="c-lad-table-wrapper">
 | 
			
		||||
    <table class="c-table c-lad-table">
 | 
			
		||||
        <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
                <th>Name</th>
 | 
			
		||||
                <th>Timestamp</th>
 | 
			
		||||
                <th>Value</th>
 | 
			
		||||
            </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody>
 | 
			
		||||
            <lad-row
 | 
			
		||||
                v-for="item in items"
 | 
			
		||||
                :key="item.key"
 | 
			
		||||
                :domain-object="item.domainObject"
 | 
			
		||||
            />
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
@@ -73,7 +75,7 @@ export default {
 | 
			
		||||
            this.items.push(item);
 | 
			
		||||
        },
 | 
			
		||||
        removeItem(identifier) {
 | 
			
		||||
            let index = _.findIndex(this.items, (item) => this.openmct.objects.makeKeyString(identifier) === item.key);
 | 
			
		||||
            let index = this.items.findIndex(item => this.openmct.objects.makeKeyString(identifier) === item.key);
 | 
			
		||||
 | 
			
		||||
            this.items.splice(index, 1);
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -102,7 +102,7 @@ export default {
 | 
			
		||||
            this.compositions.push({composition, addCallback, removeCallback});
 | 
			
		||||
        },
 | 
			
		||||
        removePrimary(identifier) {
 | 
			
		||||
            let index = _.findIndex(this.primaryTelemetryObjects, (primary) => this.openmct.objects.makeKeyString(identifier) === primary.key),
 | 
			
		||||
            let index = this.primaryTelemetryObjects.findIndex(primary => this.openmct.objects.makeKeyString(identifier) === primary.key),
 | 
			
		||||
                primary = this.primaryTelemetryObjects[index];
 | 
			
		||||
 | 
			
		||||
            this.$set(this.secondaryTelemetryObjects, primary.key, undefined);
 | 
			
		||||
@@ -130,7 +130,7 @@ export default {
 | 
			
		||||
        removeSecondary(primary) {
 | 
			
		||||
            return (identifier) => {
 | 
			
		||||
                let array = this.secondaryTelemetryObjects[primary.key],
 | 
			
		||||
                    index = _.findIndex(array, (secondary) => this.openmct.objects.makeKeyString(identifier) === secondary.key);
 | 
			
		||||
                    index = array.findIndex(secondary => this.openmct.objects.makeKeyString(identifier) === secondary.key);
 | 
			
		||||
 | 
			
		||||
                array.splice(index, 1);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ define(
 | 
			
		||||
        // DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
 | 
			
		||||
        // PENDING: Still trying to connect, and haven't failed yet.
 | 
			
		||||
        var CONNECTED = {
 | 
			
		||||
                statusClass: "s-status-ok"
 | 
			
		||||
                statusClass: "s-status-on"
 | 
			
		||||
            },
 | 
			
		||||
            PENDING = {
 | 
			
		||||
                statusClass: "s-status-warning-lo"
 | 
			
		||||
 
 | 
			
		||||
@@ -122,7 +122,7 @@ define(
 | 
			
		||||
                it("indicates success if connection is nominal", function () {
 | 
			
		||||
                    jasmine.clock().tick(pluginOptions.interval + 1);
 | 
			
		||||
                    ajaxOptions.success();
 | 
			
		||||
                    expect(indicatorElement.classList.contains('s-status-ok')).toBe(true);
 | 
			
		||||
                    expect(indicatorElement.classList.contains('s-status-on')).toBe(true);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                it("indicates an error when the server cannot be reached", function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,13 +23,14 @@
 | 
			
		||||
import EventEmitter from 'EventEmitter';
 | 
			
		||||
import uuid from 'uuid';
 | 
			
		||||
import TelemetryCriterion from "./criterion/TelemetryCriterion";
 | 
			
		||||
import { TRIGGER } from "./utils/constants";
 | 
			
		||||
import {computeCondition} from "./utils/evaluator";
 | 
			
		||||
import { evaluateResults } from './utils/evaluator';
 | 
			
		||||
import { getLatestTimestamp } from './utils/time';
 | 
			
		||||
import AllTelemetryCriterion from "./criterion/AllTelemetryCriterion";
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
* conditionConfiguration = {
 | 
			
		||||
*   id: uuid,
 | 
			
		||||
*   trigger: 'any'/'all',
 | 
			
		||||
*   trigger: 'any'/'all'/'not','xor',
 | 
			
		||||
*   criteria: [
 | 
			
		||||
*       {
 | 
			
		||||
*           telemetry: '',
 | 
			
		||||
@@ -48,20 +49,51 @@ export default class ConditionClass extends EventEmitter {
 | 
			
		||||
     * @param conditionConfiguration: {id: uuid,trigger: enum, criteria: Array of {id: uuid, operation: enum, input: Array, metaDataKey: string, key: {domainObject.identifier} }
 | 
			
		||||
     * @param openmct
 | 
			
		||||
     */
 | 
			
		||||
    constructor(conditionConfiguration, openmct) {
 | 
			
		||||
    constructor(conditionConfiguration, openmct, conditionManager) {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.conditionManager = conditionManager;
 | 
			
		||||
        this.id = conditionConfiguration.id;
 | 
			
		||||
        this.criteria = [];
 | 
			
		||||
        this.criteriaResults = {};
 | 
			
		||||
        this.result = undefined;
 | 
			
		||||
        this.timeSystems = this.openmct.time.getAllTimeSystems();
 | 
			
		||||
        if (conditionConfiguration.configuration.criteria) {
 | 
			
		||||
            this.createCriteria(conditionConfiguration.configuration.criteria);
 | 
			
		||||
        }
 | 
			
		||||
        this.trigger = conditionConfiguration.configuration.trigger;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getResult(datum) {
 | 
			
		||||
        if (!datum || !datum.id) {
 | 
			
		||||
            console.log('no data received');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.isTelemetryUsed(datum.id)) {
 | 
			
		||||
 | 
			
		||||
            this.criteria.forEach(criterion => {
 | 
			
		||||
                if (this.isAnyOrAllTelemetry(criterion)) {
 | 
			
		||||
                    criterion.getResult(datum, this.conditionManager.telemetryObjects);
 | 
			
		||||
                } else {
 | 
			
		||||
                    criterion.getResult(datum);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            this.result = evaluateResults(this.criteria.map(criterion => criterion.result), this.trigger);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isAnyOrAllTelemetry(criterion) {
 | 
			
		||||
        return (criterion.telemetry && (criterion.telemetry === 'all' || criterion.telemetry === 'any'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isTelemetryUsed(id) {
 | 
			
		||||
        return this.criteria.some(criterion => {
 | 
			
		||||
            return this.isAnyOrAllTelemetry(criterion) || criterion.telemetryObjectIdAsString === id;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update(conditionConfiguration) {
 | 
			
		||||
        this.updateTrigger(conditionConfiguration.configuration.trigger);
 | 
			
		||||
        this.updateCriteria(conditionConfiguration.configuration.criteria);
 | 
			
		||||
@@ -70,14 +102,14 @@ export default class ConditionClass extends EventEmitter {
 | 
			
		||||
    updateTrigger(trigger) {
 | 
			
		||||
        if (this.trigger !== trigger) {
 | 
			
		||||
            this.trigger = trigger;
 | 
			
		||||
            this.handleConditionUpdated();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    generateCriterion(criterionConfiguration) {
 | 
			
		||||
        return {
 | 
			
		||||
            id: uuid(),
 | 
			
		||||
            id: criterionConfiguration.id || uuid(),
 | 
			
		||||
            telemetry: criterionConfiguration.telemetry || '',
 | 
			
		||||
            telemetryObject: this.conditionManager.telemetryObjects[this.openmct.objects.makeKeyString(criterionConfiguration.telemetry)],
 | 
			
		||||
            operation: criterionConfiguration.operation || '',
 | 
			
		||||
            input: criterionConfiguration.input === undefined ? [] : criterionConfiguration.input,
 | 
			
		||||
            metadata: criterionConfiguration.metadata || ''
 | 
			
		||||
@@ -95,14 +127,24 @@ export default class ConditionClass extends EventEmitter {
 | 
			
		||||
        this.createCriteria(criterionConfigurations);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateTelemetry() {
 | 
			
		||||
        this.criteria.forEach((criterion) => {
 | 
			
		||||
            criterion.updateTelemetry(this.conditionManager.telemetryObjects);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *  adds criterion to the condition.
 | 
			
		||||
     */
 | 
			
		||||
    addCriterion(criterionConfiguration) {
 | 
			
		||||
        let criterion;
 | 
			
		||||
        let criterionConfigurationWithId = this.generateCriterion(criterionConfiguration || null);
 | 
			
		||||
        let criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
 | 
			
		||||
        if (criterionConfiguration.telemetry && (criterionConfiguration.telemetry === 'any' || criterionConfiguration.telemetry === 'all')) {
 | 
			
		||||
            criterion = new AllTelemetryCriterion(criterionConfigurationWithId, this.openmct);
 | 
			
		||||
        } else {
 | 
			
		||||
            criterion = new TelemetryCriterion(criterionConfigurationWithId, this.openmct);
 | 
			
		||||
        }
 | 
			
		||||
        criterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
 | 
			
		||||
        criterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
 | 
			
		||||
        if (!this.criteria) {
 | 
			
		||||
            this.criteria = [];
 | 
			
		||||
        }
 | 
			
		||||
@@ -131,22 +173,11 @@ export default class ConditionClass extends EventEmitter {
 | 
			
		||||
            const newCriterionConfiguration = this.generateCriterion(criterionConfiguration);
 | 
			
		||||
            let newCriterion = new TelemetryCriterion(newCriterionConfiguration, this.openmct);
 | 
			
		||||
            newCriterion.on('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
 | 
			
		||||
            newCriterion.on('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
 | 
			
		||||
 | 
			
		||||
            let criterion = found.item;
 | 
			
		||||
            criterion.unsubscribe();
 | 
			
		||||
            criterion.off('criterionUpdated', (obj) => this.handleCriterionUpdated(obj));
 | 
			
		||||
            criterion.off('criterionResultUpdated', (obj) => this.handleCriterionResult(obj));
 | 
			
		||||
            this.criteria.splice(found.index, 1, newCriterion);
 | 
			
		||||
            if (this.criteriaResults[criterion.id] !== undefined) {
 | 
			
		||||
                delete this.criteriaResults[criterion.id];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    removeCriterion(id) {
 | 
			
		||||
        if (this.destroyCriterion(id)) {
 | 
			
		||||
            this.handleConditionUpdated();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -154,15 +185,12 @@ export default class ConditionClass extends EventEmitter {
 | 
			
		||||
        let found = this.findCriterion(id);
 | 
			
		||||
        if (found) {
 | 
			
		||||
            let criterion = found.item;
 | 
			
		||||
            criterion.destroy();
 | 
			
		||||
            // TODO this is passing the wrong args
 | 
			
		||||
            criterion.off('criterionUpdated', (result) => {
 | 
			
		||||
                this.handleCriterionUpdated(id, result);
 | 
			
		||||
            criterion.off('criterionUpdated', (obj) => {
 | 
			
		||||
                this.handleCriterionUpdated(obj);
 | 
			
		||||
            });
 | 
			
		||||
            criterion.destroy();
 | 
			
		||||
            this.criteria.splice(found.index, 1);
 | 
			
		||||
            if (this.criteriaResults[criterion.id] !== undefined) {
 | 
			
		||||
                delete this.criteriaResults[criterion.id];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
@@ -172,38 +200,38 @@ export default class ConditionClass extends EventEmitter {
 | 
			
		||||
        let found = this.findCriterion(criterion.id);
 | 
			
		||||
        if (found) {
 | 
			
		||||
            this.criteria[found.index] = criterion.data;
 | 
			
		||||
            this.subscribe();
 | 
			
		||||
            // TODO nothing is listening to this
 | 
			
		||||
            this.emitEvent('conditionUpdated', {
 | 
			
		||||
                trigger: this.trigger,
 | 
			
		||||
                criteria: this.criteria
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    requestLADConditionResult() {
 | 
			
		||||
        let latestTimestamp;
 | 
			
		||||
        let criteriaResults = {};
 | 
			
		||||
        const criteriaRequests = this.criteria
 | 
			
		||||
            .map(criterion => criterion.requestLAD(this.conditionManager.telemetryObjects));
 | 
			
		||||
 | 
			
		||||
        return Promise.all(criteriaRequests)
 | 
			
		||||
            .then(results => {
 | 
			
		||||
                results.forEach(resultObj => {
 | 
			
		||||
                    const { id, data, data: { result } } = resultObj;
 | 
			
		||||
                    if (this.findCriterion(id)) {
 | 
			
		||||
                        criteriaResults[id] = !!result;
 | 
			
		||||
                    }
 | 
			
		||||
                    latestTimestamp = getLatestTimestamp(
 | 
			
		||||
                        latestTimestamp,
 | 
			
		||||
                        data,
 | 
			
		||||
                        this.timeSystems,
 | 
			
		||||
                        this.openmct.time.timeSystem()
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
                return {
 | 
			
		||||
                    id: this.id,
 | 
			
		||||
                    data: Object.assign(
 | 
			
		||||
                        {},
 | 
			
		||||
                        latestTimestamp,
 | 
			
		||||
                        { result: evaluateResults(Object.values(criteriaResults), this.trigger) }
 | 
			
		||||
                    )
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleCriterionResult(eventData) {
 | 
			
		||||
        const id = eventData.id;
 | 
			
		||||
 | 
			
		||||
        if (this.findCriterion(id)) {
 | 
			
		||||
            this.criteriaResults[id] = eventData.data.result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.handleConditionUpdated(eventData.data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    subscribe() {
 | 
			
		||||
        // TODO it looks like on any single criterion update subscriptions fire for all criteria
 | 
			
		||||
        this.criteria.forEach((criterion) => {
 | 
			
		||||
            criterion.subscribe();
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleConditionUpdated(datum) {
 | 
			
		||||
        // trigger an updated event so that consumers can react accordingly
 | 
			
		||||
        this.evaluate();
 | 
			
		||||
        this.emitEvent('conditionResultUpdated',
 | 
			
		||||
            Object.assign({}, datum, { result: this.result })
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getCriteria() {
 | 
			
		||||
@@ -219,21 +247,7 @@ export default class ConditionClass extends EventEmitter {
 | 
			
		||||
        return success;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    evaluate() {
 | 
			
		||||
        this.result = computeCondition(this.criteriaResults, this.trigger === TRIGGER.ALL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    emitEvent(eventName, data) {
 | 
			
		||||
        this.emit(eventName, {
 | 
			
		||||
            id: this.id,
 | 
			
		||||
            data: data
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    destroy() {
 | 
			
		||||
        if (typeof this.stopObservingForChanges === 'function') {
 | 
			
		||||
            this.stopObservingForChanges();
 | 
			
		||||
        }
 | 
			
		||||
        this.destroyCriteria();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import Condition from "./Condition";
 | 
			
		||||
import { getLatestTimestamp } from './utils/time';
 | 
			
		||||
import uuid from "uuid";
 | 
			
		||||
import EventEmitter from 'EventEmitter';
 | 
			
		||||
 | 
			
		||||
@@ -29,17 +30,50 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
        super();
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.conditionSetDomainObject = conditionSetDomainObject;
 | 
			
		||||
        this.timeAPI = this.openmct.time;
 | 
			
		||||
        this.latestTimestamp = {};
 | 
			
		||||
        this.timeSystems = this.openmct.time.getAllTimeSystems();
 | 
			
		||||
        this.composition = this.openmct.composition.get(conditionSetDomainObject);
 | 
			
		||||
        this.composition.on('add', this.subscribeToTelemetry, this);
 | 
			
		||||
        this.composition.on('remove', this.unsubscribeFromTelemetry, this);
 | 
			
		||||
        this.compositionLoad = this.composition.load();
 | 
			
		||||
        this.subscriptions = {};
 | 
			
		||||
        this.telemetryObjects = {};
 | 
			
		||||
        this.testData = {conditionTestData: [], applied: false};
 | 
			
		||||
        this.initialize();
 | 
			
		||||
 | 
			
		||||
        this.stopObservingForChanges = this.openmct.objects.observe(this.conditionSetDomainObject, '*', (newDomainObject) => {
 | 
			
		||||
            this.conditionSetDomainObject = newDomainObject;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    subscribeToTelemetry(endpoint) {
 | 
			
		||||
        const id = this.openmct.objects.makeKeyString(endpoint.identifier);
 | 
			
		||||
        if (this.subscriptions[id]) {
 | 
			
		||||
            console.log('subscription already exists');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.telemetryObjects[id] = Object.assign({}, endpoint, {telemetryMetaData: this.openmct.telemetry.getMetadata(endpoint).valueMetadatas});
 | 
			
		||||
        this.subscriptions[id] = this.openmct.telemetry.subscribe(
 | 
			
		||||
            endpoint,
 | 
			
		||||
            this.telemetryReceived.bind(this, endpoint)
 | 
			
		||||
        );
 | 
			
		||||
        this.updateConditionTelemetry();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unsubscribeFromTelemetry(endpointIdentifier) {
 | 
			
		||||
        const id = this.openmct.objects.makeKeyString(endpointIdentifier);
 | 
			
		||||
        if (!this.subscriptions[id]) {
 | 
			
		||||
            console.log('no subscription to remove');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.subscriptions[id]();
 | 
			
		||||
        delete this.subscriptions[id];
 | 
			
		||||
        delete this.telemetryObjects[id];
 | 
			
		||||
        this.removeConditionTelemetry();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.conditionResults = {};
 | 
			
		||||
        this.conditionClassCollection = [];
 | 
			
		||||
        if (this.conditionSetDomainObject.configuration.conditionCollection.length) {
 | 
			
		||||
            this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
 | 
			
		||||
@@ -48,6 +82,34 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateConditionTelemetry() {
 | 
			
		||||
        this.conditionClassCollection.forEach((condition) => condition.updateTelemetry());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    removeConditionTelemetry() {
 | 
			
		||||
        let conditionsChanged = false;
 | 
			
		||||
        this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration) => {
 | 
			
		||||
            conditionConfiguration.configuration.criteria.forEach((criterion, index) => {
 | 
			
		||||
                const isAnyAllTelemetry = criterion.telemetry && (criterion.telemetry === 'any' || criterion.telemetry === 'all');
 | 
			
		||||
                if (!isAnyAllTelemetry) {
 | 
			
		||||
                    const found = Object.values(this.telemetryObjects).find((telemetryObject) => {
 | 
			
		||||
                        return this.openmct.objects.areIdsEqual(telemetryObject.identifier, criterion.telemetry);
 | 
			
		||||
                    });
 | 
			
		||||
                    if (!found) {
 | 
			
		||||
                        criterion.telemetry = '';
 | 
			
		||||
                        criterion.metadata = '';
 | 
			
		||||
                        criterion.input = [];
 | 
			
		||||
                        criterion.operation = '';
 | 
			
		||||
                        conditionsChanged = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        if (conditionsChanged) {
 | 
			
		||||
            this.persistConditions();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateCondition(conditionConfiguration, index) {
 | 
			
		||||
        let condition = this.conditionClassCollection[index];
 | 
			
		||||
        condition.update(conditionConfiguration);
 | 
			
		||||
@@ -56,18 +118,12 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initCondition(conditionConfiguration, index) {
 | 
			
		||||
        let condition = new Condition(conditionConfiguration, this.openmct);
 | 
			
		||||
        condition.on('conditionResultUpdated', this.handleConditionResult.bind(this));
 | 
			
		||||
        let condition = new Condition(conditionConfiguration, this.openmct, this);
 | 
			
		||||
        if (index !== undefined) {
 | 
			
		||||
            this.conditionClassCollection.splice(index + 1, 0, condition);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.conditionClassCollection.unshift(condition);
 | 
			
		||||
        }
 | 
			
		||||
        //There are no criteria for a default condition and hence no subscriptions.
 | 
			
		||||
        //Hence the conditionResult must be manually triggered for it.
 | 
			
		||||
        if (conditionConfiguration.isDefault) {
 | 
			
		||||
            this.handleConditionResult();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createCondition(conditionConfiguration) {
 | 
			
		||||
@@ -89,6 +145,7 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
                    output: 'false',
 | 
			
		||||
                    trigger: 'all',
 | 
			
		||||
                    criteria: [{
 | 
			
		||||
                        id: uuid(),
 | 
			
		||||
                        telemetry: '',
 | 
			
		||||
                        operation: '',
 | 
			
		||||
                        input: [],
 | 
			
		||||
@@ -107,11 +164,13 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cloneCondition(conditionConfiguration, index) {
 | 
			
		||||
        this.createAndSaveCondition(index, conditionConfiguration);
 | 
			
		||||
        let clonedConfig = JSON.parse(JSON.stringify(conditionConfiguration));
 | 
			
		||||
        clonedConfig.configuration.criteria.forEach((criterion) => criterion.id = uuid());
 | 
			
		||||
        this.createAndSaveCondition(index, clonedConfig);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createAndSaveCondition(index, conditionConfiguration) {
 | 
			
		||||
        let newCondition = this.createCondition(conditionConfiguration);
 | 
			
		||||
        const newCondition = this.createCondition(conditionConfiguration);
 | 
			
		||||
        if (index !== undefined) {
 | 
			
		||||
            this.conditionSetDomainObject.configuration.conditionCollection.splice(index + 1, 0, newCondition);
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -123,22 +182,16 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
    removeCondition(index) {
 | 
			
		||||
        let condition = this.conditionClassCollection[index];
 | 
			
		||||
        condition.destroyCriteria();
 | 
			
		||||
        condition.off('conditionResultUpdated', this.handleConditionResult.bind(this));
 | 
			
		||||
        condition.destroy();
 | 
			
		||||
        this.conditionClassCollection.splice(index, 1);
 | 
			
		||||
        this.conditionSetDomainObject.configuration.conditionCollection.splice(index, 1);
 | 
			
		||||
        if (this.conditionResults[condition.id] !== undefined) {
 | 
			
		||||
            delete this.conditionResults[condition.id];
 | 
			
		||||
        }
 | 
			
		||||
        this.persistConditions();
 | 
			
		||||
        this.handleConditionResult();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    findConditionById(id) {
 | 
			
		||||
        return this.conditionClassCollection.find(conditionClass => conditionClass.id === id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //this.$set(this.conditionClassCollection, reorderEvent.newIndex, oldConditions[reorderEvent.oldIndex]);
 | 
			
		||||
    reorderConditions(reorderPlan) {
 | 
			
		||||
        let oldConditions = Array.from(this.conditionSetDomainObject.configuration.conditionCollection);
 | 
			
		||||
        let newCollection = [];
 | 
			
		||||
@@ -150,26 +203,109 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
        this.persistConditions();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleConditionResult(resultObj) {
 | 
			
		||||
    getCurrentCondition() {
 | 
			
		||||
        const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
 | 
			
		||||
        let currentCondition = conditionCollection[conditionCollection.length-1];
 | 
			
		||||
 | 
			
		||||
        if (resultObj) {
 | 
			
		||||
            const id = resultObj.id;
 | 
			
		||||
            if (this.findConditionById(id)) {
 | 
			
		||||
                this.conditionResults[id] = resultObj.data.result;
 | 
			
		||||
            }
 | 
			
		||||
            this.updateTimestamp(resultObj.data);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < conditionCollection.length - 1; i++) {
 | 
			
		||||
            if (this.conditionResults[conditionCollection[i].id]) {
 | 
			
		||||
            const condition = this.findConditionById(conditionCollection[i].id)
 | 
			
		||||
            if (condition.result) {
 | 
			
		||||
                //first condition to be true wins
 | 
			
		||||
                currentCondition = conditionCollection[i];
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return currentCondition;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getCurrentConditionLAD(conditionResults) {
 | 
			
		||||
        const conditionCollection = this.conditionSetDomainObject.configuration.conditionCollection;
 | 
			
		||||
        let currentCondition = conditionCollection[conditionCollection.length-1];
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < conditionCollection.length - 1; i++) {
 | 
			
		||||
            if (conditionResults[conditionCollection[i].id]) {
 | 
			
		||||
                //first condition to be true wins
 | 
			
		||||
                currentCondition = conditionCollection[i];
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return currentCondition;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    requestLADConditionSetOutput() {
 | 
			
		||||
        if (!this.conditionClassCollection.length) {
 | 
			
		||||
            return Promise.resolve([]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.compositionLoad.then(() => {
 | 
			
		||||
            let latestTimestamp;
 | 
			
		||||
            let conditionResults = {};
 | 
			
		||||
            const conditionRequests = this.conditionClassCollection
 | 
			
		||||
                .map(condition => condition.requestLADConditionResult());
 | 
			
		||||
 | 
			
		||||
            return Promise.all(conditionRequests)
 | 
			
		||||
                .then((results) => {
 | 
			
		||||
                    results.forEach(resultObj => {
 | 
			
		||||
                        const { id, data, data: { result } } = resultObj;
 | 
			
		||||
                        if (this.findConditionById(id)) {
 | 
			
		||||
                            conditionResults[id] = !!result;
 | 
			
		||||
                        }
 | 
			
		||||
                        latestTimestamp = getLatestTimestamp(
 | 
			
		||||
                            latestTimestamp,
 | 
			
		||||
                            data,
 | 
			
		||||
                            this.timeSystems,
 | 
			
		||||
                            this.openmct.time.timeSystem()
 | 
			
		||||
                        );
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    if (!Object.values(latestTimestamp).some(timeSystem => timeSystem)) {
 | 
			
		||||
                        return [];
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    const currentCondition = this.getCurrentConditionLAD(conditionResults);
 | 
			
		||||
                    const currentOutput = Object.assign(
 | 
			
		||||
                        {
 | 
			
		||||
                            output: currentCondition.configuration.output,
 | 
			
		||||
                            id: this.conditionSetDomainObject.identifier,
 | 
			
		||||
                            conditionId: currentCondition.id
 | 
			
		||||
                        },
 | 
			
		||||
                        latestTimestamp
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    return [currentOutput];
 | 
			
		||||
                });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isTelemetryUsed(endpoint) {
 | 
			
		||||
        const id = this.openmct.objects.makeKeyString(endpoint.identifier);
 | 
			
		||||
 | 
			
		||||
        for(const condition of this.conditionClassCollection) {
 | 
			
		||||
            if (condition.isTelemetryUsed(id)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    telemetryReceived(endpoint, datum) {
 | 
			
		||||
        if (!this.isTelemetryUsed(endpoint)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const normalizedDatum = this.createNormalizedDatum(datum, endpoint);
 | 
			
		||||
        const timeSystemKey = this.openmct.time.timeSystem().key;
 | 
			
		||||
        let timestamp = {};
 | 
			
		||||
        timestamp[timeSystemKey] = normalizedDatum[timeSystemKey];
 | 
			
		||||
 | 
			
		||||
        this.conditionClassCollection.forEach(condition => {
 | 
			
		||||
            condition.getResult(normalizedDatum);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const currentCondition = this.getCurrentCondition();
 | 
			
		||||
 | 
			
		||||
        this.emit('conditionSetResultUpdated',
 | 
			
		||||
            Object.assign(
 | 
			
		||||
                {
 | 
			
		||||
@@ -177,19 +313,41 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
                    id: this.conditionSetDomainObject.identifier,
 | 
			
		||||
                    conditionId: currentCondition.id
 | 
			
		||||
                },
 | 
			
		||||
                this.latestTimestamp
 | 
			
		||||
                timestamp
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateTimestamp(timestamp) {
 | 
			
		||||
        this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
 | 
			
		||||
            if (!this.latestTimestamp[timeSystem.key]
 | 
			
		||||
                || timestamp[timeSystem.key] > this.latestTimestamp[timeSystem.key]
 | 
			
		||||
            ) {
 | 
			
		||||
                this.latestTimestamp[timeSystem.key] = timestamp[timeSystem.key];
 | 
			
		||||
    getTestData(metadatum) {
 | 
			
		||||
        let data = undefined;
 | 
			
		||||
        if (this.testData.applied) {
 | 
			
		||||
            const found = this.testData.conditionTestInputs.find((testInput) => (testInput.metadata === metadatum.source));
 | 
			
		||||
            if (found) {
 | 
			
		||||
                data = found.value;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createNormalizedDatum(telemetryDatum, endpoint) {
 | 
			
		||||
        const id = this.openmct.objects.makeKeyString(endpoint.identifier);
 | 
			
		||||
        const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
 | 
			
		||||
 | 
			
		||||
        const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
 | 
			
		||||
            const testValue = this.getTestData(metadatum);
 | 
			
		||||
            const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
 | 
			
		||||
            datum[metadatum.key] = testValue !== undefined ?  formatter.parse(testValue) : formatter.parse(telemetryDatum[metadatum.source]);
 | 
			
		||||
            return datum;
 | 
			
		||||
        }, {});
 | 
			
		||||
 | 
			
		||||
        normalizedDatum.id = id;
 | 
			
		||||
 | 
			
		||||
        return normalizedDatum;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateTestData(testData) {
 | 
			
		||||
        this.testData = testData;
 | 
			
		||||
        this.openmct.objects.mutate(this.conditionSetDomainObject, 'configuration.conditionTestData', this.testData.conditionTestInputs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    persistConditions() {
 | 
			
		||||
@@ -197,11 +355,16 @@ export default class ConditionManager extends EventEmitter {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    destroy() {
 | 
			
		||||
        this.composition.off('add', this.subscribeToTelemetry, this);
 | 
			
		||||
        this.composition.off('remove', this.unsubscribeFromTelemetry, this);
 | 
			
		||||
        Object.values(this.subscriptions).forEach(unsubscribe => unsubscribe());
 | 
			
		||||
        delete this.subscriptions;
 | 
			
		||||
 | 
			
		||||
        if(this.stopObservingForChanges) {
 | 
			
		||||
            this.stopObservingForChanges();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.conditionClassCollection.forEach((condition) => {
 | 
			
		||||
            condition.off('conditionResultUpdated', this.handleConditionResult);
 | 
			
		||||
            condition.destroy();
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,9 @@ describe('ConditionManager', () => {
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    let mockComposition;
 | 
			
		||||
    let loader;
 | 
			
		||||
    let mockTimeSystems;
 | 
			
		||||
 | 
			
		||||
    function mockAngularComponents() {
 | 
			
		||||
        let mockInjector = jasmine.createSpyObj('$injector', ['get']);
 | 
			
		||||
@@ -71,9 +74,33 @@ describe('ConditionManager', () => {
 | 
			
		||||
        openmct.$injector = mockInjector;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    beforeAll(function () {
 | 
			
		||||
    beforeEach(function () {
 | 
			
		||||
 | 
			
		||||
        mockAngularComponents();
 | 
			
		||||
        mockListener = jasmine.createSpy('mockListener');
 | 
			
		||||
        loader = {};
 | 
			
		||||
        loader.promise = new Promise(function (resolve, reject) {
 | 
			
		||||
            loader.resolve = resolve;
 | 
			
		||||
            loader.reject = reject;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        mockComposition = jasmine.createSpyObj('compositionCollection', [
 | 
			
		||||
            'load',
 | 
			
		||||
            'on',
 | 
			
		||||
            'off'
 | 
			
		||||
        ]);
 | 
			
		||||
        mockComposition.load.and.callFake(() => {
 | 
			
		||||
            setTimeout(() => {
 | 
			
		||||
                loader.resolve();
 | 
			
		||||
            });
 | 
			
		||||
            return loader.promise;
 | 
			
		||||
        });
 | 
			
		||||
        mockComposition.on('add', mockListener);
 | 
			
		||||
        mockComposition.on('remove', mockListener);
 | 
			
		||||
        openmct.composition = jasmine.createSpyObj('compositionAPI', [
 | 
			
		||||
            'get'
 | 
			
		||||
        ]);
 | 
			
		||||
        openmct.composition.get.and.returnValue(mockComposition);
 | 
			
		||||
 | 
			
		||||
        openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString', 'observe', 'mutate']);
 | 
			
		||||
        openmct.objects.get.and.returnValues(new Promise(function (resolve, reject) {
 | 
			
		||||
@@ -84,10 +111,17 @@ describe('ConditionManager', () => {
 | 
			
		||||
        openmct.objects.makeKeyString.and.returnValue(conditionSetDomainObject.identifier.key);
 | 
			
		||||
        openmct.objects.observe.and.returnValue(function () {});
 | 
			
		||||
        openmct.objects.mutate.and.returnValue(function () {});
 | 
			
		||||
 | 
			
		||||
        mockTimeSystems = {
 | 
			
		||||
            key: 'utc'
 | 
			
		||||
        };
 | 
			
		||||
        openmct.time = jasmine.createSpyObj('time', ['getAllTimeSystems']);
 | 
			
		||||
        openmct.time.getAllTimeSystems.and.returnValue([mockTimeSystems]);
 | 
			
		||||
 | 
			
		||||
        conditionMgr = new ConditionManager(conditionSetDomainObject, openmct);
 | 
			
		||||
        mockListener = jasmine.createSpy('mockListener');
 | 
			
		||||
 | 
			
		||||
        conditionMgr.on('conditionSetResultUpdated', mockListener);
 | 
			
		||||
        conditionMgr.on('telemetryReceived', mockListener);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('creates a conditionCollection with a default condition', function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,6 @@ describe('ConditionSetCompositionPolicy', () => {
 | 
			
		||||
    let testTelemetryObject;
 | 
			
		||||
    let openmct = {};
 | 
			
		||||
    let parentDomainObject;
 | 
			
		||||
    let composition;
 | 
			
		||||
 | 
			
		||||
    beforeAll(function () {
 | 
			
		||||
        testTelemetryObject = {
 | 
			
		||||
@@ -57,7 +56,6 @@ describe('ConditionSetCompositionPolicy', () => {
 | 
			
		||||
        openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject']);
 | 
			
		||||
        policy = new ConditionSetCompositionPolicy(openmct);
 | 
			
		||||
        parentDomainObject = {};
 | 
			
		||||
        composition = {};
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('returns true for object types that are not conditionSets', function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,25 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
export default class ConditionSetMetadataProvider {
 | 
			
		||||
    constructor(openmct) {
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
@@ -21,15 +43,33 @@ export default class ConditionSetMetadataProvider {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getMetadata(domainObject) {
 | 
			
		||||
        const enumerations = domainObject.configuration.conditionCollection
 | 
			
		||||
            .map((condition, index) => {
 | 
			
		||||
                return {
 | 
			
		||||
                    string: condition.configuration.output,
 | 
			
		||||
                    value: index
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            values: this.getDomains().concat([
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'Output',
 | 
			
		||||
                    key: 'output',
 | 
			
		||||
                    format: 'string',
 | 
			
		||||
                    key: "state",
 | 
			
		||||
                    source: "output",
 | 
			
		||||
                    name: "State",
 | 
			
		||||
                    format: "enum",
 | 
			
		||||
                    enumerations: enumerations,
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 1
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "output",
 | 
			
		||||
                    name: "Value",
 | 
			
		||||
                    format: "string",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 2
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ])
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -25,27 +25,62 @@ import ConditionManager from './ConditionManager'
 | 
			
		||||
export default class ConditionSetTelemetryProvider {
 | 
			
		||||
    constructor(openmct) {
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.conditionManagerPool = {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isTelemetryObject(domainObject) {
 | 
			
		||||
        return domainObject.type === 'conditionSet';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    supportsRequest(domainObject, options) {
 | 
			
		||||
        return false;
 | 
			
		||||
    supportsRequest(domainObject) {
 | 
			
		||||
        return domainObject.type === 'conditionSet';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    supportsSubscribe(domainObject) {
 | 
			
		||||
        return domainObject.type === 'conditionSet';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    request(domainObject) {
 | 
			
		||||
        let conditionManager = this.getConditionManager(domainObject);
 | 
			
		||||
 | 
			
		||||
        return conditionManager.requestLADConditionSetOutput()
 | 
			
		||||
            .then(latestOutput => {
 | 
			
		||||
                return latestOutput;
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    subscribe(domainObject, callback) {
 | 
			
		||||
        let conditionManager = new ConditionManager(domainObject, this.openmct);
 | 
			
		||||
        let conditionManager = this.getConditionManager(domainObject);
 | 
			
		||||
 | 
			
		||||
        conditionManager.on('conditionSetResultUpdated', callback);
 | 
			
		||||
 | 
			
		||||
        return function unsubscribe() {
 | 
			
		||||
            conditionManager.off('conditionSetResultUpdated');
 | 
			
		||||
            conditionManager.destroy();
 | 
			
		||||
        return this.destroyConditionManager.bind(this, this.openmct.objects.makeKeyString(domainObject.identifier));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * returns conditionManager instance for corresponding domain object
 | 
			
		||||
     * creates the instance if it is not yet created
 | 
			
		||||
     * @private
 | 
			
		||||
     */
 | 
			
		||||
    getConditionManager(domainObject) {
 | 
			
		||||
        const id = this.openmct.objects.makeKeyString(domainObject.identifier);
 | 
			
		||||
 | 
			
		||||
        if (!this.conditionManagerPool[id]) {
 | 
			
		||||
            this.conditionManagerPool[id] = new ConditionManager(domainObject, this.openmct);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.conditionManagerPool[id];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * cleans up and destroys conditionManager instance for corresponding domain object id
 | 
			
		||||
     * can be called manually for views that only request but do not subscribe to data
 | 
			
		||||
     */
 | 
			
		||||
    destroyConditionManager(id) {
 | 
			
		||||
        if (this.conditionManagerPool[id]) {
 | 
			
		||||
            this.conditionManagerPool[id].off('conditionSetResultUpdated');
 | 
			
		||||
            this.conditionManagerPool[id].destroy();
 | 
			
		||||
            delete this.conditionManagerPool[id];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								src/plugins/condition/ConditionSetViewPolicy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/plugins/condition/ConditionSetViewPolicy.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
function ConditionSetViewPolicy() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConditionSetViewPolicy.prototype.allow = function (view, domainObject) {
 | 
			
		||||
    if (domainObject.getModel().type === 'conditionSet') {
 | 
			
		||||
        return view.key === 'conditionSet.view';
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default ConditionSetViewPolicy;
 | 
			
		||||
@@ -30,7 +30,7 @@ export default class ConditionSetViewProvider {
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.name = 'Conditions View';
 | 
			
		||||
        this.key = 'conditionSet.view';
 | 
			
		||||
        this.cssClass = 'icon-conditional'; // TODO: replace with class for new icon
 | 
			
		||||
        this.cssClass = 'icon-conditional';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    canView(domainObject) {
 | 
			
		||||
 
 | 
			
		||||
@@ -25,35 +25,52 @@ import {TRIGGER} from "./utils/constants";
 | 
			
		||||
import TelemetryCriterion from "./criterion/TelemetryCriterion";
 | 
			
		||||
 | 
			
		||||
let openmct = {},
 | 
			
		||||
    mockListener,
 | 
			
		||||
    testConditionDefinition,
 | 
			
		||||
    testTelemetryObject,
 | 
			
		||||
    conditionObj;
 | 
			
		||||
    conditionObj,
 | 
			
		||||
    conditionManager,
 | 
			
		||||
    mockTelemetryReceived,
 | 
			
		||||
    mockTimeSystems;
 | 
			
		||||
 | 
			
		||||
describe("The condition", function () {
 | 
			
		||||
 | 
			
		||||
    beforeEach (() => {
 | 
			
		||||
        mockListener = jasmine.createSpy('listener');
 | 
			
		||||
        conditionManager = jasmine.createSpyObj('conditionManager',
 | 
			
		||||
            ['on']
 | 
			
		||||
        );
 | 
			
		||||
        mockTelemetryReceived = jasmine.createSpy('listener');
 | 
			
		||||
        conditionManager.on('telemetryReceived', mockTelemetryReceived);
 | 
			
		||||
 | 
			
		||||
        testTelemetryObject = {
 | 
			
		||||
            identifier:{ namespace: "", key: "test-object"},
 | 
			
		||||
            type: "test-object",
 | 
			
		||||
            name: "Test Object",
 | 
			
		||||
            telemetry: {
 | 
			
		||||
                values: [{
 | 
			
		||||
                    key: "some-key",
 | 
			
		||||
                    name: "Some attribute",
 | 
			
		||||
                    key: "value",
 | 
			
		||||
                    name: "Value",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 2
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "utc",
 | 
			
		||||
                    name: "Time",
 | 
			
		||||
                    format: "utc",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        domain: 1
 | 
			
		||||
                    }
 | 
			
		||||
                }, {
 | 
			
		||||
                    key: "some-other-key",
 | 
			
		||||
                    name: "Another attribute",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 1
 | 
			
		||||
                    }
 | 
			
		||||
                    key: "testSource",
 | 
			
		||||
                    source: "value",
 | 
			
		||||
                    name: "Test",
 | 
			
		||||
                    format: "string"
 | 
			
		||||
                }]
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        conditionManager.telemetryObjects = {
 | 
			
		||||
            "test-object": testTelemetryObject
 | 
			
		||||
        };
 | 
			
		||||
        openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']);
 | 
			
		||||
        openmct.objects.get.and.returnValue(new Promise(function (resolve, reject) {
 | 
			
		||||
            resolve(testTelemetryObject);
 | 
			
		||||
@@ -63,14 +80,23 @@ describe("The condition", function () {
 | 
			
		||||
        openmct.telemetry.subscribe.and.returnValue(function () {});
 | 
			
		||||
        openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values);
 | 
			
		||||
 | 
			
		||||
        mockTimeSystems = {
 | 
			
		||||
            key: 'utc'
 | 
			
		||||
        };
 | 
			
		||||
        openmct.time = jasmine.createSpyObj('time', ['getAllTimeSystems']);
 | 
			
		||||
        openmct.time.getAllTimeSystems.and.returnValue([mockTimeSystems]);
 | 
			
		||||
 | 
			
		||||
        testConditionDefinition = {
 | 
			
		||||
            id: '123-456',
 | 
			
		||||
            configuration: {
 | 
			
		||||
                name: 'mock condition',
 | 
			
		||||
                output: 'mock output',
 | 
			
		||||
                trigger: TRIGGER.ANY,
 | 
			
		||||
                criteria: [
 | 
			
		||||
                    {
 | 
			
		||||
                        id: '1234-5678-9999-0000',
 | 
			
		||||
                        operation: 'equalTo',
 | 
			
		||||
                        input: false,
 | 
			
		||||
                        input: ['0'],
 | 
			
		||||
                        metadata: 'value',
 | 
			
		||||
                        telemetry: testTelemetryObject.identifier
 | 
			
		||||
                    }
 | 
			
		||||
@@ -80,14 +106,12 @@ describe("The condition", function () {
 | 
			
		||||
 | 
			
		||||
        conditionObj = new Condition(
 | 
			
		||||
            testConditionDefinition,
 | 
			
		||||
            openmct
 | 
			
		||||
            openmct,
 | 
			
		||||
            conditionManager
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        conditionObj.on('conditionUpdated', mockListener);
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("generates criteria with an id", function () {
 | 
			
		||||
    it("generates criteria with the correct properties", function () {
 | 
			
		||||
        const testCriterion = testConditionDefinition.configuration.criteria[0];
 | 
			
		||||
        let criterion = conditionObj.generateCriterion(testCriterion);
 | 
			
		||||
        expect(criterion.id).toBeDefined();
 | 
			
		||||
@@ -119,4 +143,38 @@ describe("The condition", function () {
 | 
			
		||||
        expect(result).toBeTrue();
 | 
			
		||||
        expect(conditionObj.criteria.length).toEqual(0);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("gets the result of a condition when new telemetry data is received", function () {
 | 
			
		||||
        conditionObj.getResult({
 | 
			
		||||
            value: '0',
 | 
			
		||||
            utc: 'Hi',
 | 
			
		||||
            id: testTelemetryObject.identifier.key
 | 
			
		||||
        });
 | 
			
		||||
        expect(conditionObj.result).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("gets the result of a condition when new telemetry data is received", function () {
 | 
			
		||||
        conditionObj.getResult({
 | 
			
		||||
            value: '1',
 | 
			
		||||
            utc: 'Hi',
 | 
			
		||||
            id: testTelemetryObject.identifier.key
 | 
			
		||||
        });
 | 
			
		||||
        expect(conditionObj.result).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("keeps the old result new telemetry data is not used by it", function () {
 | 
			
		||||
        conditionObj.getResult({
 | 
			
		||||
            value: '0',
 | 
			
		||||
            utc: 'Hi',
 | 
			
		||||
            id: testTelemetryObject.identifier.key
 | 
			
		||||
        });
 | 
			
		||||
        expect(conditionObj.result).toBeTrue();
 | 
			
		||||
 | 
			
		||||
        conditionObj.getResult({
 | 
			
		||||
            value: '1',
 | 
			
		||||
            utc: 'Hi',
 | 
			
		||||
            id: '1234'
 | 
			
		||||
        });
 | 
			
		||||
        expect(conditionObj.result).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -23,18 +23,43 @@
 | 
			
		||||
import EventEmitter from 'EventEmitter';
 | 
			
		||||
 | 
			
		||||
export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
    constructor(conditionalStyleConfiguration, openmct) {
 | 
			
		||||
    constructor(styleConfiguration, openmct, callback, suppressSubscriptionOnEdit) {
 | 
			
		||||
        super();
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        if (conditionalStyleConfiguration && conditionalStyleConfiguration.conditionSetIdentifier) {
 | 
			
		||||
            this.initialize(conditionalStyleConfiguration);
 | 
			
		||||
        this.callback = callback;
 | 
			
		||||
        if (suppressSubscriptionOnEdit) {
 | 
			
		||||
            this.openmct.editor.on('isEditing', this.toggleSubscription.bind(this));
 | 
			
		||||
        }
 | 
			
		||||
        if (styleConfiguration) {
 | 
			
		||||
            this.initialize(styleConfiguration);
 | 
			
		||||
            if (styleConfiguration.conditionSetIdentifier) {
 | 
			
		||||
                this.subscribeToConditionSet();
 | 
			
		||||
            } else {
 | 
			
		||||
                this.applyStaticStyle();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toggleSubscription(isEditing) {
 | 
			
		||||
        this.isEditing = isEditing;
 | 
			
		||||
        if (this.isEditing) {
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
            }
 | 
			
		||||
            if (this.conditionSetIdentifier) {
 | 
			
		||||
                this.applySelectedConditionStyle();
 | 
			
		||||
            }
 | 
			
		||||
        } else if (this.conditionSetIdentifier) {
 | 
			
		||||
            this.subscribeToConditionSet();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initialize(conditionalStyleConfiguration) {
 | 
			
		||||
        this.conditionSetIdentifier = conditionalStyleConfiguration.conditionSetIdentifier;
 | 
			
		||||
        this.updateConditionStylesMap(conditionalStyleConfiguration.styles || []);
 | 
			
		||||
    initialize(styleConfiguration) {
 | 
			
		||||
        this.conditionSetIdentifier = styleConfiguration.conditionSetIdentifier;
 | 
			
		||||
        this.staticStyle = styleConfiguration.staticStyle;
 | 
			
		||||
        this.selectedConditionId = styleConfiguration.selectedConditionId;
 | 
			
		||||
        this.defaultConditionId = styleConfiguration.defaultConditionId;
 | 
			
		||||
        this.updateConditionStylesMap(styleConfiguration.styles || []);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    subscribeToConditionSet() {
 | 
			
		||||
@@ -42,20 +67,31 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
            this.stopProvidingTelemetry();
 | 
			
		||||
        }
 | 
			
		||||
        this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
 | 
			
		||||
            this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, output => this.handleConditionSetResultUpdated(output));
 | 
			
		||||
            this.openmct.telemetry.request(conditionSetDomainObject)
 | 
			
		||||
                .then(output => {
 | 
			
		||||
                    if (output && output.length) {
 | 
			
		||||
                        this.handleConditionSetResultUpdated(output[0]);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateConditionalStyleConfig(conditionalStyleConfiguration) {
 | 
			
		||||
        if (!conditionalStyleConfiguration || !conditionalStyleConfiguration.conditionSetIdentifier) {
 | 
			
		||||
    updateObjectStyleConfig(styleConfiguration) {
 | 
			
		||||
        if (!styleConfiguration || !styleConfiguration.conditionSetIdentifier) {
 | 
			
		||||
            this.initialize(styleConfiguration || {});
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        } else {
 | 
			
		||||
            let isNewConditionSet = !this.conditionSetIdentifier ||
 | 
			
		||||
                                    this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, conditionalStyleConfiguration.conditionSetIdentifier);
 | 
			
		||||
            this.initialize(conditionalStyleConfiguration);
 | 
			
		||||
            //Only resubscribe if the conditionSet has changed.
 | 
			
		||||
            if (isNewConditionSet) {
 | 
			
		||||
                this.subscribeToConditionSet();
 | 
			
		||||
                                    !this.openmct.objects.areIdsEqual(this.conditionSetIdentifier, styleConfiguration.conditionSetIdentifier);
 | 
			
		||||
            this.initialize(styleConfiguration);
 | 
			
		||||
            if (this.isEditing) {
 | 
			
		||||
                this.applySelectedConditionStyle();
 | 
			
		||||
            } else {
 | 
			
		||||
                //Only resubscribe if the conditionSet has changed.
 | 
			
		||||
                if (isNewConditionSet) {
 | 
			
		||||
                    this.subscribeToConditionSet();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -63,7 +99,11 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
    updateConditionStylesMap(conditionStyles) {
 | 
			
		||||
        let conditionStyleMap = {};
 | 
			
		||||
        conditionStyles.forEach((conditionStyle) => {
 | 
			
		||||
            conditionStyleMap[conditionStyle.conditionId] = conditionStyle.style;
 | 
			
		||||
            if (conditionStyle.conditionId) {
 | 
			
		||||
                conditionStyleMap[conditionStyle.conditionId] = conditionStyle.style;
 | 
			
		||||
            } else {
 | 
			
		||||
                conditionStyleMap.static = conditionStyle.style;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        this.conditionalStyleMap = conditionStyleMap;
 | 
			
		||||
    }
 | 
			
		||||
@@ -74,30 +114,49 @@ export default class StyleRuleManager extends EventEmitter {
 | 
			
		||||
            if (foundStyle !== this.currentStyle) {
 | 
			
		||||
                this.currentStyle = foundStyle;
 | 
			
		||||
            }
 | 
			
		||||
            this.updateDomainObjectStyle();
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this.currentStyle !== this.defaultStyle) {
 | 
			
		||||
                this.currentStyle = this.defaultStyle;
 | 
			
		||||
            }
 | 
			
		||||
            this.applyStaticStyle();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.updateDomainObjectStyle();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateDomainObjectStyle() {
 | 
			
		||||
        this.emit('conditionalStyleUpdated', this.currentStyle)
 | 
			
		||||
        if (this.callback) {
 | 
			
		||||
            this.callback(Object.assign({}, this.currentStyle));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    applySelectedConditionStyle() {
 | 
			
		||||
        const conditionId = this.selectedConditionId || this.defaultConditionId;
 | 
			
		||||
        if (!conditionId) {
 | 
			
		||||
            this.applyStaticStyle();
 | 
			
		||||
        } else if (this.conditionalStyleMap[conditionId]) {
 | 
			
		||||
            this.currentStyle = this.conditionalStyleMap[conditionId];
 | 
			
		||||
            this.updateDomainObjectStyle();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    applyStaticStyle() {
 | 
			
		||||
        if (this.staticStyle) {
 | 
			
		||||
            this.currentStyle = this.staticStyle.style;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this.currentStyle) {
 | 
			
		||||
                Object.keys(this.currentStyle).forEach(key => {
 | 
			
		||||
                    this.currentStyle[key] = '__no_value';
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.updateDomainObjectStyle();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    destroy() {
 | 
			
		||||
        if (this.currentStyle) {
 | 
			
		||||
            Object.keys(this.currentStyle).forEach(key => {
 | 
			
		||||
                this.currentStyle[key] = 'inherit';
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        this.updateDomainObjectStyle();
 | 
			
		||||
        this.applyStaticStyle();
 | 
			
		||||
        if (this.stopProvidingTelemetry) {
 | 
			
		||||
            this.stopProvidingTelemetry();
 | 
			
		||||
        }
 | 
			
		||||
        delete this.stopProvidingTelemetry;
 | 
			
		||||
        this.conditionSetIdentifier = undefined;
 | 
			
		||||
        this.isEditing = undefined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,200 +21,190 @@
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div v-if="isEditing"
 | 
			
		||||
     class="c-condition c-condition--edit js-condition-drag-wrapper"
 | 
			
		||||
<div class="c-condition-h"
 | 
			
		||||
     :class="{ 'is-drag-target': draggingOver }"
 | 
			
		||||
     @dragover.prevent
 | 
			
		||||
     @drop.prevent="dropCondition($event, conditionIndex)"
 | 
			
		||||
     @dragenter="dragEnter($event, conditionIndex)"
 | 
			
		||||
     @dragleave="dragLeave($event, conditionIndex)"
 | 
			
		||||
>
 | 
			
		||||
    <!-- Edit view -->
 | 
			
		||||
    <div class="c-condition__header">
 | 
			
		||||
        <span class="c-condition__drag-grippy c-grippy c-grippy--vertical-drag"
 | 
			
		||||
              title="Drag to reorder conditions"
 | 
			
		||||
              :class="[{ 'is-enabled': !condition.isDefault }, { 'hide-nice': condition.isDefault }]"
 | 
			
		||||
              :draggable="!condition.isDefault"
 | 
			
		||||
              @dragstart="dragStart"
 | 
			
		||||
              @dragstop="dragStop"
 | 
			
		||||
              @dragover.stop
 | 
			
		||||
        ></span>
 | 
			
		||||
 | 
			
		||||
        <span class="c-condition__disclosure c-disclosure-triangle c-tree__item__view-control is-enabled"
 | 
			
		||||
              :class="{ 'c-disclosure-triangle--expanded': expanded }"
 | 
			
		||||
              @click="expanded = !expanded"
 | 
			
		||||
        ></span>
 | 
			
		||||
 | 
			
		||||
        <span class="c-condition__name">{{ condition.configuration.name }}</span>
 | 
			
		||||
        <!-- TODO: description should be derived from criteria -->
 | 
			
		||||
        <span v-if="condition.isDefault"
 | 
			
		||||
              class="c-condition__summary"
 | 
			
		||||
        >
 | 
			
		||||
            When all else fails
 | 
			
		||||
        </span>
 | 
			
		||||
        <span v-else
 | 
			
		||||
              class="c-condition__summary"
 | 
			
		||||
        >
 | 
			
		||||
            <template v-if="!canEvaluateCriteria">
 | 
			
		||||
                Define criteria
 | 
			
		||||
            </template>
 | 
			
		||||
            <template v-else>
 | 
			
		||||
                When
 | 
			
		||||
                <span v-for="(criterion, index) in condition.configuration.criteria"
 | 
			
		||||
                      :key="index"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ getRule(criterion, index) }}
 | 
			
		||||
                    <template v-if="!isLastCriterion">
 | 
			
		||||
                        {{ getConjunction }}
 | 
			
		||||
                    </template>
 | 
			
		||||
                </span>
 | 
			
		||||
            </template>
 | 
			
		||||
        </span>
 | 
			
		||||
 | 
			
		||||
        <div class="c-condition__buttons">
 | 
			
		||||
            <button v-if="!condition.isDefault"
 | 
			
		||||
                    class="c-click-icon c-condition__duplicate-button icon-duplicate"
 | 
			
		||||
                    title="Duplicate this condition"
 | 
			
		||||
                    @click="cloneCondition"
 | 
			
		||||
            ></button>
 | 
			
		||||
 | 
			
		||||
            <button v-if="!condition.isDefault"
 | 
			
		||||
                    class="c-click-icon c-condition__delete-button icon-trash"
 | 
			
		||||
                    title="Delete this condition"
 | 
			
		||||
                    @click="removeCondition"
 | 
			
		||||
            ></button>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-if="expanded"
 | 
			
		||||
         class="c-condition__definition c-cdef"
 | 
			
		||||
    <div class="c-condition-h__drop-target"></div>
 | 
			
		||||
    <div v-if="isEditing"
 | 
			
		||||
         :class="{'is-current': condition.id === currentConditionId}"
 | 
			
		||||
         class="c-condition c-condition--edit"
 | 
			
		||||
    >
 | 
			
		||||
        <span class="c-cdef__separator c-row-separator"></span>
 | 
			
		||||
        <span class="c-cdef__label">Condition Name</span>
 | 
			
		||||
        <span class="c-cdef__controls">
 | 
			
		||||
            <input v-model="condition.configuration.name"
 | 
			
		||||
                   class="t-condition-input__name"
 | 
			
		||||
                   type="text"
 | 
			
		||||
                   @blur="persist"
 | 
			
		||||
            >
 | 
			
		||||
        </span>
 | 
			
		||||
        <!-- Edit view -->
 | 
			
		||||
        <div class="c-condition__header">
 | 
			
		||||
            <span class="c-condition__drag-grippy c-grippy c-grippy--vertical-drag"
 | 
			
		||||
                  title="Drag to reorder conditions"
 | 
			
		||||
                  :class="[{ 'is-enabled': !condition.isDefault }, { 'hide-nice': condition.isDefault }]"
 | 
			
		||||
                  :draggable="!condition.isDefault"
 | 
			
		||||
                  @dragstart="dragStart"
 | 
			
		||||
                  @dragend="dragEnd"
 | 
			
		||||
            ></span>
 | 
			
		||||
 | 
			
		||||
        <span class="c-cdef__label">Output</span>
 | 
			
		||||
        <span class="c-cdef__controls">
 | 
			
		||||
            <select v-model="selectedOutputSelection"
 | 
			
		||||
                    @change="setOutputValue"
 | 
			
		||||
            >
 | 
			
		||||
                <option value="">- Select Output -</option>
 | 
			
		||||
                <option v-for="option in outputOptions"
 | 
			
		||||
                        :key="option"
 | 
			
		||||
                        :value="option"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ initCap(option) }}
 | 
			
		||||
                </option>
 | 
			
		||||
            </select>
 | 
			
		||||
            <input v-if="selectedOutputSelection === outputOptions[2]"
 | 
			
		||||
                   v-model="condition.configuration.output"
 | 
			
		||||
                   class="t-condition-name-input"
 | 
			
		||||
                   type="text"
 | 
			
		||||
                   @blur="persist"
 | 
			
		||||
            >
 | 
			
		||||
        </span>
 | 
			
		||||
            <span class="c-condition__disclosure c-disclosure-triangle c-tree__item__view-control is-enabled"
 | 
			
		||||
                  :class="{ 'c-disclosure-triangle--expanded': expanded }"
 | 
			
		||||
                  @click="expanded = !expanded"
 | 
			
		||||
            ></span>
 | 
			
		||||
 | 
			
		||||
        <div v-if="!condition.isDefault"
 | 
			
		||||
             class="c-cdef__match-and-criteria"
 | 
			
		||||
        >
 | 
			
		||||
            <span class="c-cdef__separator c-row-separator"></span>
 | 
			
		||||
            <span class="c-cdef__label">Match</span>
 | 
			
		||||
            <span class="c-cdef__controls">
 | 
			
		||||
                <select v-model="condition.configuration.trigger"
 | 
			
		||||
                        @change="persist"
 | 
			
		||||
                >
 | 
			
		||||
                    <option value="all">when all criteria are met</option>
 | 
			
		||||
                    <option value="any">when any criteria are met</option>
 | 
			
		||||
                </select>
 | 
			
		||||
            <span class="c-condition__name">{{ condition.configuration.name }}</span>
 | 
			
		||||
            <span class="c-condition__summary">
 | 
			
		||||
                <template v-if="!canEvaluateCriteria">
 | 
			
		||||
                    Define criteria
 | 
			
		||||
                </template>
 | 
			
		||||
                <span v-else>
 | 
			
		||||
                    <condition-description :show-label="false"
 | 
			
		||||
                                           :condition="condition"
 | 
			
		||||
                    />
 | 
			
		||||
                </span>
 | 
			
		||||
            </span>
 | 
			
		||||
 | 
			
		||||
            <template v-if="telemetry.length">
 | 
			
		||||
                <div v-for="(criterion, index) in condition.configuration.criteria"
 | 
			
		||||
                     :key="index"
 | 
			
		||||
                     class="c-cdef__criteria"
 | 
			
		||||
            <div class="c-condition__buttons">
 | 
			
		||||
                <button v-if="!condition.isDefault"
 | 
			
		||||
                        class="c-click-icon c-condition__duplicate-button icon-duplicate"
 | 
			
		||||
                        title="Duplicate this condition"
 | 
			
		||||
                        @click="cloneCondition"
 | 
			
		||||
                ></button>
 | 
			
		||||
 | 
			
		||||
                <button v-if="!condition.isDefault"
 | 
			
		||||
                        class="c-click-icon c-condition__delete-button icon-trash"
 | 
			
		||||
                        title="Delete this condition"
 | 
			
		||||
                        @click="removeCondition"
 | 
			
		||||
                ></button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-if="expanded"
 | 
			
		||||
             class="c-condition__definition c-cdef"
 | 
			
		||||
        >
 | 
			
		||||
            <span class="c-cdef__separator c-row-separator"></span>
 | 
			
		||||
            <span class="c-cdef__label">Condition Name</span>
 | 
			
		||||
            <span class="c-cdef__controls">
 | 
			
		||||
                <input v-model="condition.configuration.name"
 | 
			
		||||
                       class="t-condition-input__name"
 | 
			
		||||
                       type="text"
 | 
			
		||||
                       @change="persist"
 | 
			
		||||
                >
 | 
			
		||||
                    <Criterion :telemetry="telemetry"
 | 
			
		||||
                               :criterion="criterion"
 | 
			
		||||
                               :index="index"
 | 
			
		||||
                               :trigger="condition.configuration.trigger"
 | 
			
		||||
                               :is-default="condition.configuration.criteria.length === 1"
 | 
			
		||||
                               @persist="persist"
 | 
			
		||||
                    />
 | 
			
		||||
                    <div class="c-cdef__criteria__buttons">
 | 
			
		||||
                        <button class="c-click-icon c-cdef__criteria-duplicate-button icon-duplicate"
 | 
			
		||||
                                title="Duplicate this criteria"
 | 
			
		||||
                                @click="cloneCriterion(index)"
 | 
			
		||||
                        ></button>
 | 
			
		||||
                        <button v-if="!(condition.configuration.criteria.length === 1)"
 | 
			
		||||
                                class="c-click-icon c-cdef__criteria-duplicate-button icon-trash"
 | 
			
		||||
                                title="Delete this criteria"
 | 
			
		||||
                                @click="removeCriterion(index)"
 | 
			
		||||
                        ></button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </template>
 | 
			
		||||
            <div class="c-cdef__separator c-row-separator"></div>
 | 
			
		||||
            <div class="c-cdef__controls"
 | 
			
		||||
                 :disabled="!telemetry.length"
 | 
			
		||||
            </span>
 | 
			
		||||
 | 
			
		||||
            <span class="c-cdef__label">Output</span>
 | 
			
		||||
            <span class="c-cdef__controls">
 | 
			
		||||
                <span class="c-cdef__control">
 | 
			
		||||
                    <select v-model="selectedOutputSelection"
 | 
			
		||||
                            @change="setOutputValue"
 | 
			
		||||
                    >
 | 
			
		||||
                        <option v-for="option in outputOptions"
 | 
			
		||||
                                :key="option"
 | 
			
		||||
                                :value="option"
 | 
			
		||||
                        >
 | 
			
		||||
                            {{ initCap(option) }}
 | 
			
		||||
                        </option>
 | 
			
		||||
                    </select>
 | 
			
		||||
                </span>
 | 
			
		||||
                <span class="c-cdef__control">
 | 
			
		||||
                    <input v-if="selectedOutputSelection === outputOptions[2]"
 | 
			
		||||
                           v-model="condition.configuration.output"
 | 
			
		||||
                           class="t-condition-name-input"
 | 
			
		||||
                           type="text"
 | 
			
		||||
                           @change="persist"
 | 
			
		||||
                    >
 | 
			
		||||
                </span>
 | 
			
		||||
            </span>
 | 
			
		||||
 | 
			
		||||
            <div v-if="!condition.isDefault"
 | 
			
		||||
                 class="c-cdef__match-and-criteria"
 | 
			
		||||
            >
 | 
			
		||||
                <button
 | 
			
		||||
                    class="c-cdef__add-criteria-button c-button c-button--labeled icon-plus"
 | 
			
		||||
                    @click="addCriteria"
 | 
			
		||||
                <span class="c-cdef__separator c-row-separator"></span>
 | 
			
		||||
                <span class="c-cdef__label">Match</span>
 | 
			
		||||
                <span class="c-cdef__controls">
 | 
			
		||||
                    <select v-model="condition.configuration.trigger"
 | 
			
		||||
                            @change="persist"
 | 
			
		||||
                    >
 | 
			
		||||
                        <option v-for="option in triggers"
 | 
			
		||||
                                :key="option.value"
 | 
			
		||||
                                :value="option.value"
 | 
			
		||||
                        > {{ option.label }}</option>
 | 
			
		||||
                    </select>
 | 
			
		||||
                </span>
 | 
			
		||||
 | 
			
		||||
                <template v-if="telemetry.length || condition.configuration.criteria.length">
 | 
			
		||||
                    <div v-for="(criterion, index) in condition.configuration.criteria"
 | 
			
		||||
                         :key="criterion.id"
 | 
			
		||||
                         class="c-cdef__criteria"
 | 
			
		||||
                    >
 | 
			
		||||
                        <Criterion :telemetry="telemetry"
 | 
			
		||||
                                   :criterion="criterion"
 | 
			
		||||
                                   :index="index"
 | 
			
		||||
                                   :trigger="condition.configuration.trigger"
 | 
			
		||||
                                   :is-default="condition.configuration.criteria.length === 1"
 | 
			
		||||
                                   @persist="persist"
 | 
			
		||||
                        />
 | 
			
		||||
                        <div class="c-cdef__criteria__buttons">
 | 
			
		||||
                            <button class="c-click-icon c-cdef__criteria-duplicate-button icon-duplicate"
 | 
			
		||||
                                    title="Duplicate this criteria"
 | 
			
		||||
                                    @click="cloneCriterion(index)"
 | 
			
		||||
                            ></button>
 | 
			
		||||
                            <button v-if="!(condition.configuration.criteria.length === 1)"
 | 
			
		||||
                                    class="c-click-icon c-cdef__criteria-duplicate-button icon-trash"
 | 
			
		||||
                                    title="Delete this criteria"
 | 
			
		||||
                                    @click="removeCriterion(index)"
 | 
			
		||||
                            ></button>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </template>
 | 
			
		||||
                <div class="c-cdef__separator c-row-separator"></div>
 | 
			
		||||
                <div class="c-cdef__controls"
 | 
			
		||||
                     :disabled="!telemetry.length"
 | 
			
		||||
                >
 | 
			
		||||
                    <span class="c-button__label">Add Criteria</span>
 | 
			
		||||
                </button>
 | 
			
		||||
                    <button
 | 
			
		||||
                        class="c-cdef__add-criteria-button c-button c-button--labeled icon-plus"
 | 
			
		||||
                        @click="addCriteria"
 | 
			
		||||
                    >
 | 
			
		||||
                        <span class="c-button__label">Add Criteria</span>
 | 
			
		||||
                    </button>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
<div v-else
 | 
			
		||||
     class="c-condition c-condition--browse"
 | 
			
		||||
>
 | 
			
		||||
    <!-- Browse view -->
 | 
			
		||||
    <div class="c-condition__header">
 | 
			
		||||
        <span class="c-condition__name">
 | 
			
		||||
            {{ condition.configuration.name }}
 | 
			
		||||
        </span>
 | 
			
		||||
        <span class="c-condition__output">
 | 
			
		||||
            Output: {{ condition.configuration.output }}
 | 
			
		||||
        </span>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-if="condition.isDefault"
 | 
			
		||||
         class="c-condition__summary"
 | 
			
		||||
    >
 | 
			
		||||
        When all else fails
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-else
 | 
			
		||||
         class="c-condition__summary"
 | 
			
		||||
         class="c-condition c-condition--browse"
 | 
			
		||||
         :class="{'is-current': condition.id === currentConditionId}"
 | 
			
		||||
    >
 | 
			
		||||
        <template v-if="!canEvaluateCriteria">
 | 
			
		||||
            Define criteria
 | 
			
		||||
        </template>
 | 
			
		||||
        <template v-else>
 | 
			
		||||
            When
 | 
			
		||||
            <span v-for="(criterion, index) in condition.configuration.criteria"
 | 
			
		||||
                  :key="index"
 | 
			
		||||
            >
 | 
			
		||||
                {{ getRule(criterion, index) }}
 | 
			
		||||
                <template v-if="!isLastCriterion">
 | 
			
		||||
                    {{ getConjunction }}
 | 
			
		||||
                </template>
 | 
			
		||||
        <!-- Browse view -->
 | 
			
		||||
        <div class="c-condition__header">
 | 
			
		||||
            <span class="c-condition__name">
 | 
			
		||||
                {{ condition.configuration.name }}
 | 
			
		||||
            </span>
 | 
			
		||||
        </template>
 | 
			
		||||
            <span class="c-condition__output">
 | 
			
		||||
                Output: {{ condition.configuration.output }}
 | 
			
		||||
            </span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="c-condition__summary">
 | 
			
		||||
            <condition-description :show-label="false"
 | 
			
		||||
                                   :condition="condition"
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import Criterion from './Criterion.vue';
 | 
			
		||||
import { OPERATIONS } from '../utils/operations';
 | 
			
		||||
import ConditionDescription from "./ConditionDescription.vue";
 | 
			
		||||
import { TRIGGER, TRIGGER_LABEL } from "@/plugins/condition/utils/constants";
 | 
			
		||||
import uuid from 'uuid';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
    components: {
 | 
			
		||||
        Criterion
 | 
			
		||||
        Criterion,
 | 
			
		||||
        ConditionDescription
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
        currentConditionId: {
 | 
			
		||||
            type: String,
 | 
			
		||||
            default: ''
 | 
			
		||||
        },
 | 
			
		||||
        condition: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
@@ -231,6 +221,14 @@ export default {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            required: true,
 | 
			
		||||
            default: () => []
 | 
			
		||||
        },
 | 
			
		||||
        isDragging: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            default: false
 | 
			
		||||
        },
 | 
			
		||||
        moveIndex: {
 | 
			
		||||
            type: Number,
 | 
			
		||||
            default: 0
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
@@ -240,25 +238,36 @@ export default {
 | 
			
		||||
            trigger: 'all',
 | 
			
		||||
            selectedOutputSelection: '',
 | 
			
		||||
            outputOptions: ['false', 'true', 'string'],
 | 
			
		||||
            criterionIndex: 0
 | 
			
		||||
            criterionIndex: 0,
 | 
			
		||||
            draggingOver: false,
 | 
			
		||||
            isDefault: this.condition.isDefault
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        triggers() {
 | 
			
		||||
            const keys = Object.keys(TRIGGER);
 | 
			
		||||
            const triggerOptions = [];
 | 
			
		||||
            keys.forEach((trigger) => {
 | 
			
		||||
                triggerOptions.push({
 | 
			
		||||
                    value: TRIGGER[trigger],
 | 
			
		||||
                    label: TRIGGER_LABEL[TRIGGER[trigger]]
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            return triggerOptions;
 | 
			
		||||
        },
 | 
			
		||||
        canEvaluateCriteria: function () {
 | 
			
		||||
            let criteria = this.condition.configuration.criteria;
 | 
			
		||||
            let lastCriterion = criteria[criteria.length - 1];
 | 
			
		||||
            if (lastCriterion.telemetry &&
 | 
			
		||||
                lastCriterion.operation &&
 | 
			
		||||
                (lastCriterion.input.length ||
 | 
			
		||||
                lastCriterion.operation === 'isDefined' ||
 | 
			
		||||
                lastCriterion.operation === 'isUndefined')) {
 | 
			
		||||
                return true;
 | 
			
		||||
            } else {
 | 
			
		||||
                return false;
 | 
			
		||||
            if (criteria.length) {
 | 
			
		||||
                let lastCriterion = criteria[criteria.length - 1];
 | 
			
		||||
                if (lastCriterion.telemetry &&
 | 
			
		||||
                    lastCriterion.operation &&
 | 
			
		||||
                    (lastCriterion.input.length ||
 | 
			
		||||
                        lastCriterion.operation === 'isDefined' ||
 | 
			
		||||
                        lastCriterion.operation === 'isUndefined')) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        getConjunction: function () {
 | 
			
		||||
            return this.condition.configuration.trigger === 'all' ? 'and' : 'or';
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
@@ -268,20 +277,6 @@ export default {
 | 
			
		||||
        this.setOutputSelection();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        getRule(criterion, index) {
 | 
			
		||||
            return `${criterion.telemetry.name} ${criterion.telemetry.fieldName} ${this.findDescription(criterion.operation, criterion.input)}`;
 | 
			
		||||
        },
 | 
			
		||||
        isLastCriterion(index) {
 | 
			
		||||
            return index === this.condition.configuration.criteria.length - 1;
 | 
			
		||||
        },
 | 
			
		||||
        findDescription(operation, values) {
 | 
			
		||||
            for (let i=0, ii= OPERATIONS.length; i < ii; i++) {
 | 
			
		||||
                if (operation === OPERATIONS[i].name) {
 | 
			
		||||
                    return OPERATIONS[i].getDescription(values);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        },
 | 
			
		||||
        setOutputSelection() {
 | 
			
		||||
            let conditionOutput = this.condition.configuration.output;
 | 
			
		||||
            if (conditionOutput) {
 | 
			
		||||
@@ -302,6 +297,7 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        addCriteria() {
 | 
			
		||||
            const criteriaObject = {
 | 
			
		||||
                id: uuid(),
 | 
			
		||||
                telemetry: '',
 | 
			
		||||
                operation: '',
 | 
			
		||||
                input: '',
 | 
			
		||||
@@ -312,11 +308,39 @@ export default {
 | 
			
		||||
        dragStart(e) {
 | 
			
		||||
            e.dataTransfer.setData('dragging', e.target); // required for FF to initiate drag
 | 
			
		||||
            e.dataTransfer.effectAllowed = "copyMove";
 | 
			
		||||
            e.dataTransfer.setDragImage(e.target.closest('.js-condition-drag-wrapper'), 0, 0);
 | 
			
		||||
            e.dataTransfer.setDragImage(e.target.closest('.c-condition-h'), 0, 0);
 | 
			
		||||
            this.$emit('setMoveIndex', this.conditionIndex);
 | 
			
		||||
        },
 | 
			
		||||
        dragStop(e) {
 | 
			
		||||
            e.dataTransfer.clearData();
 | 
			
		||||
        dragEnd(event) {
 | 
			
		||||
            this.dragStarted = false;
 | 
			
		||||
            event.dataTransfer.clearData();
 | 
			
		||||
            this.$emit('dragComplete');
 | 
			
		||||
        },
 | 
			
		||||
        dropCondition(event, targetIndex) {
 | 
			
		||||
            if (!this.isDragging) { return }
 | 
			
		||||
            if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
 | 
			
		||||
            if (this.isValidTarget(targetIndex)) {
 | 
			
		||||
                this.dragElement = undefined;
 | 
			
		||||
                this.draggingOver = false;
 | 
			
		||||
                this.$emit('dropCondition', targetIndex);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        dragEnter(event, targetIndex) {
 | 
			
		||||
            if (!this.isDragging) { return }
 | 
			
		||||
            if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
 | 
			
		||||
            if (this.isValidTarget(targetIndex)) {
 | 
			
		||||
                this.dragElement = event.target.parentElement;
 | 
			
		||||
                this.draggingOver = true;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        dragLeave(event) {
 | 
			
		||||
            if (event.target.parentElement === this.dragElement) {
 | 
			
		||||
                this.draggingOver = false;
 | 
			
		||||
                this.dragElement = undefined;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        isValidTarget(targetIndex) {
 | 
			
		||||
            return this.moveIndex !== targetIndex;
 | 
			
		||||
        },
 | 
			
		||||
        destroy() {
 | 
			
		||||
        },
 | 
			
		||||
@@ -331,16 +355,13 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        removeCriterion(index) {
 | 
			
		||||
            this.condition.configuration.criteria.splice(index, 1);
 | 
			
		||||
            this.persist()
 | 
			
		||||
            this.persist();
 | 
			
		||||
        },
 | 
			
		||||
        cloneCriterion(index) {
 | 
			
		||||
            const clonedCriterion = {...this.condition.configuration.criteria[index]};
 | 
			
		||||
            const clonedCriterion = JSON.parse(JSON.stringify(this.condition.configuration.criteria[index]));
 | 
			
		||||
            clonedCriterion.id = uuid();
 | 
			
		||||
            this.condition.configuration.criteria.splice(index + 1, 0, clonedCriterion);
 | 
			
		||||
            this.persist()
 | 
			
		||||
        },
 | 
			
		||||
        hasTelemetry(identifier) {
 | 
			
		||||
            // TODO: check parent condition.composition.hasTelemetry
 | 
			
		||||
            return this.currentCriteria && identifier;
 | 
			
		||||
            this.persist();
 | 
			
		||||
        },
 | 
			
		||||
        persist() {
 | 
			
		||||
            this.$emit('updateCondition', {
 | 
			
		||||
@@ -348,7 +369,7 @@ export default {
 | 
			
		||||
                index: this.conditionIndex
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        initCap: function (str) {
 | 
			
		||||
        initCap(str) {
 | 
			
		||||
            return str.charAt(0).toUpperCase() + str.slice(1)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<section id="conditionCollection"
 | 
			
		||||
         class="c-cs__conditions"
 | 
			
		||||
         :class="{ 'is-expanded': expanded }"
 | 
			
		||||
>
 | 
			
		||||
    <div class="c-cs__header c-section__header">
 | 
			
		||||
@@ -53,28 +52,25 @@
 | 
			
		||||
            <span class="c-cs-button__label">Add Condition</span>
 | 
			
		||||
        </button>
 | 
			
		||||
 | 
			
		||||
        <div class="c-cs__conditions-h">
 | 
			
		||||
            <div v-for="(condition, index) in conditionCollection"
 | 
			
		||||
                 :key="condition.id"
 | 
			
		||||
                 class="c-condition-h"
 | 
			
		||||
            >
 | 
			
		||||
                <div v-if="isEditing"
 | 
			
		||||
                     class="c-c__drag-ghost"
 | 
			
		||||
                     @drop.prevent="dropCondition"
 | 
			
		||||
                     @dragenter="dragEnter"
 | 
			
		||||
                     @dragleave="dragLeave"
 | 
			
		||||
                     @dragover.prevent
 | 
			
		||||
                ></div>
 | 
			
		||||
                <Condition :condition="condition"
 | 
			
		||||
                           :condition-index="index"
 | 
			
		||||
                           :telemetry="telemetryObjs"
 | 
			
		||||
                           :is-editing="isEditing"
 | 
			
		||||
                           @updateCondition="updateCondition"
 | 
			
		||||
                           @removeCondition="removeCondition"
 | 
			
		||||
                           @cloneCondition="cloneCondition"
 | 
			
		||||
                           @setMoveIndex="setMoveIndex"
 | 
			
		||||
                />
 | 
			
		||||
            </div>
 | 
			
		||||
        <div class="c-cs__conditions-h"
 | 
			
		||||
             :class="{ 'is-active-dragging': isDragging }"
 | 
			
		||||
        >
 | 
			
		||||
            <Condition v-for="(condition, index) in conditionCollection"
 | 
			
		||||
                       :key="condition.id"
 | 
			
		||||
                       :condition="condition"
 | 
			
		||||
                       :current-condition-id="currentConditionId"
 | 
			
		||||
                       :condition-index="index"
 | 
			
		||||
                       :telemetry="telemetryObjs"
 | 
			
		||||
                       :is-editing="isEditing"
 | 
			
		||||
                       :move-index="moveIndex"
 | 
			
		||||
                       :is-dragging="isDragging"
 | 
			
		||||
                       @updateCondition="updateCondition"
 | 
			
		||||
                       @removeCondition="removeCondition"
 | 
			
		||||
                       @cloneCondition="cloneCondition"
 | 
			
		||||
                       @setMoveIndex="setMoveIndex"
 | 
			
		||||
                       @dragComplete="dragComplete"
 | 
			
		||||
                       @dropCondition="dropCondition"
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</section>
 | 
			
		||||
@@ -90,7 +86,17 @@ export default {
 | 
			
		||||
        Condition
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
        isEditing: Boolean
 | 
			
		||||
        isEditing: Boolean,
 | 
			
		||||
        testData: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true,
 | 
			
		||||
            default: () => {
 | 
			
		||||
                return {
 | 
			
		||||
                    applied: false,
 | 
			
		||||
                    conditionTestInputs: []
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
@@ -99,14 +105,29 @@ export default {
 | 
			
		||||
            conditionResults: {},
 | 
			
		||||
            conditions: [],
 | 
			
		||||
            telemetryObjs: [],
 | 
			
		||||
            moveIndex: Number,
 | 
			
		||||
            isDragging: false
 | 
			
		||||
            moveIndex: undefined,
 | 
			
		||||
            isDragging: false,
 | 
			
		||||
            defaultOutput: undefined,
 | 
			
		||||
            dragCounter: 0,
 | 
			
		||||
            currentConditionId: ''
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        defaultOutput(newOutput, oldOutput) {
 | 
			
		||||
            this.$emit('updateDefaultOutput', newOutput);
 | 
			
		||||
        },
 | 
			
		||||
        testData: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.updateTestData();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
        this.composition.off('add', this.addTelemetryObject);
 | 
			
		||||
        this.composition.off('remove', this.removeTelemetryObject);
 | 
			
		||||
        if(this.conditionManager) {
 | 
			
		||||
            this.conditionManager.off('conditionSetResultUpdated', this.handleConditionSetResultUpdated);
 | 
			
		||||
            this.conditionManager.destroy();
 | 
			
		||||
        }
 | 
			
		||||
        if (this.stopObservingForChanges) {
 | 
			
		||||
@@ -121,23 +142,30 @@ export default {
 | 
			
		||||
        this.conditionCollection = this.domainObject.configuration.conditionCollection;
 | 
			
		||||
        this.observeForChanges();
 | 
			
		||||
        this.conditionManager = new ConditionManager(this.domainObject, this.openmct);
 | 
			
		||||
        this.conditionManager.on('conditionSetResultUpdated', (data) => {
 | 
			
		||||
            this.$emit('conditionSetResultUpdated', data);
 | 
			
		||||
        })
 | 
			
		||||
        this.conditionManager.on('conditionSetResultUpdated', this.handleConditionSetResultUpdated);
 | 
			
		||||
        this.updateDefaultCondition();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        handleConditionSetResultUpdated(data) {
 | 
			
		||||
            this.currentConditionId = data.conditionId;
 | 
			
		||||
            this.$emit('conditionSetResultUpdated', data)
 | 
			
		||||
        },
 | 
			
		||||
        observeForChanges() {
 | 
			
		||||
            this.stopObservingForChanges = this.openmct.objects.observe(this.domainObject, 'configuration.conditionCollection', (newConditionCollection) => {
 | 
			
		||||
                this.conditionCollection = newConditionCollection;
 | 
			
		||||
                this.updateDefaultCondition();
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        updateDefaultCondition() {
 | 
			
		||||
            const defaultCondition = this.domainObject.configuration.conditionCollection
 | 
			
		||||
                .find(conditionConfiguration => conditionConfiguration.isDefault);
 | 
			
		||||
            this.defaultOutput = defaultCondition.configuration.output;
 | 
			
		||||
        },
 | 
			
		||||
        setMoveIndex(index) {
 | 
			
		||||
            this.moveIndex = index;
 | 
			
		||||
            this.isDragging = true;
 | 
			
		||||
        },
 | 
			
		||||
        dropCondition(e) {
 | 
			
		||||
            let targetIndex = Array.from(document.querySelectorAll('.c-c__drag-ghost')).indexOf(e.target);
 | 
			
		||||
            if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
 | 
			
		||||
        dropCondition(targetIndex) {
 | 
			
		||||
            const oldIndexArr = Object.keys(this.conditionCollection);
 | 
			
		||||
            const move = function (arr, old_index, new_index) {
 | 
			
		||||
                while (old_index < 0) {
 | 
			
		||||
@@ -163,25 +191,16 @@ export default {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.reorder(reorderPlan);
 | 
			
		||||
 | 
			
		||||
            e.target.classList.remove("dragging");
 | 
			
		||||
        },
 | 
			
		||||
        dragComplete() {
 | 
			
		||||
            this.isDragging = false;
 | 
			
		||||
        },
 | 
			
		||||
        dragEnter(e) {
 | 
			
		||||
            if (!this.isDragging) { return }
 | 
			
		||||
            let targetIndex = Array.from(document.querySelectorAll('.c-c__drag-ghost')).indexOf(e.target);
 | 
			
		||||
            if (targetIndex > this.moveIndex) { targetIndex-- } // for 'downward' move
 | 
			
		||||
            if (this.moveIndex === targetIndex) { return }
 | 
			
		||||
            e.target.classList.add("dragging");
 | 
			
		||||
        },
 | 
			
		||||
        dragLeave(e) {
 | 
			
		||||
            e.target.classList.remove("dragging");
 | 
			
		||||
        },
 | 
			
		||||
        addTelemetryObject(domainObject) {
 | 
			
		||||
            this.telemetryObjs.push(domainObject);
 | 
			
		||||
            this.$emit('telemetryUpdated', this.telemetryObjs);
 | 
			
		||||
        },
 | 
			
		||||
        removeTelemetryObject(identifier) {
 | 
			
		||||
            let index = _.findIndex(this.telemetryObjs, (obj) => {
 | 
			
		||||
            let index = this.telemetryObjs.findIndex(obj => {
 | 
			
		||||
                let objId = this.openmct.objects.makeKeyString(obj.identifier);
 | 
			
		||||
                let id = this.openmct.objects.makeKeyString(identifier);
 | 
			
		||||
                return objId === id;
 | 
			
		||||
@@ -204,6 +223,9 @@ export default {
 | 
			
		||||
        },
 | 
			
		||||
        cloneCondition(data) {
 | 
			
		||||
            this.conditionManager.cloneCondition(data.condition, data.index);
 | 
			
		||||
        },
 | 
			
		||||
        updateTestData() {
 | 
			
		||||
            this.conditionManager.updateTestData(this.testData);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										154
									
								
								src/plugins/condition/components/ConditionDescription.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/plugins/condition/components/ConditionDescription.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* 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.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div class="c-style__condition-desc">
 | 
			
		||||
    <span v-if="showLabel && condition"
 | 
			
		||||
          class="c-style__condition-desc__name c-condition__name"
 | 
			
		||||
    >
 | 
			
		||||
        {{ condition.configuration.name }}
 | 
			
		||||
    </span>
 | 
			
		||||
    <span v-for="(criterionDescription, index) in criterionDescriptions"
 | 
			
		||||
          :key="criterionDescription"
 | 
			
		||||
          class="c-style__condition-desc__text"
 | 
			
		||||
    >
 | 
			
		||||
        <template v-if="!index">When</template>
 | 
			
		||||
        {{ criterionDescription }}
 | 
			
		||||
        <template v-if="index < (criterionDescriptions.length-1)">{{ triggerDescription }}</template>
 | 
			
		||||
    </span>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { TRIGGER } from "@/plugins/condition/utils/constants";
 | 
			
		||||
import { OPERATIONS } from "@/plugins/condition/utils/operations";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    name: 'ConditionDescription',
 | 
			
		||||
    inject: [
 | 
			
		||||
        'openmct'
 | 
			
		||||
    ],
 | 
			
		||||
    props: {
 | 
			
		||||
        showLabel: {
 | 
			
		||||
            type: Boolean,
 | 
			
		||||
            default: false
 | 
			
		||||
        },
 | 
			
		||||
        condition: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            default() {
 | 
			
		||||
                return undefined;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            criterionDescriptions: [],
 | 
			
		||||
            triggerDescription: ''
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        condition: {
 | 
			
		||||
            handler(val) {
 | 
			
		||||
                this.getConditionDescription();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.getConditionDescription();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        getTriggerDescription(trigger) {
 | 
			
		||||
            let description = '';
 | 
			
		||||
            switch(trigger) {
 | 
			
		||||
            case TRIGGER.ANY:
 | 
			
		||||
            case TRIGGER.XOR:
 | 
			
		||||
                description = 'or';
 | 
			
		||||
                break;
 | 
			
		||||
            case TRIGGER.ALL:
 | 
			
		||||
            case TRIGGER.NOT: description = 'and';
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            return description;
 | 
			
		||||
        },
 | 
			
		||||
        getConditionDescription() {
 | 
			
		||||
            if (this.condition) {
 | 
			
		||||
                this.triggerDescription =  this.getTriggerDescription(this.condition.configuration.trigger);
 | 
			
		||||
                this.criterionDescriptions = [];
 | 
			
		||||
                this.condition.configuration.criteria.forEach((criterion, index) => {
 | 
			
		||||
                    this.getCriterionDescription(criterion, index);
 | 
			
		||||
                });
 | 
			
		||||
                if (this.condition.isDefault) {
 | 
			
		||||
                    this.criterionDescriptions.splice(0, 0, 'all else fails');
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                this.criterionDescriptions = [];
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        getCriterionDescription(criterion, index) {
 | 
			
		||||
            if (!criterion.telemetry) {
 | 
			
		||||
                let description = `Unknown ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
 | 
			
		||||
                this.criterionDescriptions.splice(index, 0, description);
 | 
			
		||||
            } else if (criterion.telemetry === 'all' || criterion.telemetry === 'any') {
 | 
			
		||||
                const telemetryDescription = criterion.telemetry === 'all' ? 'All telemetry' : 'Any telemetry';
 | 
			
		||||
                let description = `${telemetryDescription} ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
 | 
			
		||||
                this.criterionDescriptions.splice(index, 0, description);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
 | 
			
		||||
                    if (telemetryObject.type === 'unknown') {
 | 
			
		||||
                        let description = `Unknown ${criterion.metadata} ${this.getOperatorText(criterion.operation, criterion.input)}`;
 | 
			
		||||
                        this.criterionDescriptions.splice(index, 0, description);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        let metadataValue = criterion.metadata;
 | 
			
		||||
                        let inputValue = criterion.input;
 | 
			
		||||
                        if (criterion.metadata) {
 | 
			
		||||
                            this.telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
 | 
			
		||||
 | 
			
		||||
                            const metadataObj = this.telemetryMetadata.valueMetadatas.find((metadata) => metadata.key === criterion.metadata);
 | 
			
		||||
                            if (metadataObj) {
 | 
			
		||||
                                if (metadataObj.name) {
 | 
			
		||||
                                    metadataValue = metadataObj.name;
 | 
			
		||||
                                }
 | 
			
		||||
                                if(metadataObj.enumerations && inputValue.length) {
 | 
			
		||||
                                    if (metadataObj.enumerations[inputValue[0]] && metadataObj.enumerations[inputValue[0]].string) {
 | 
			
		||||
                                        inputValue = [metadataObj.enumerations[inputValue[0]].string];
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        let description = `${telemetryObject.name} ${metadataValue} ${this.getOperatorText(criterion.operation, inputValue)}`;
 | 
			
		||||
                        if (this.criterionDescriptions[index]) {
 | 
			
		||||
                            this.criterionDescriptions[index] = description;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            this.criterionDescriptions.splice(index, 0, description);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        getOperatorText(operationName, values) {
 | 
			
		||||
            const found = OPERATIONS.find((operation) => operation.name === operationName);
 | 
			
		||||
            return found ? found.getDescription(values) : '';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										89
									
								
								src/plugins/condition/components/ConditionError.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/plugins/condition/components/ConditionError.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* 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.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div v-if="conditionErrors.length"
 | 
			
		||||
     class="c-condition__errors"
 | 
			
		||||
>
 | 
			
		||||
    <div v-for="(error, index) in conditionErrors"
 | 
			
		||||
         :key="index"
 | 
			
		||||
         class="u-alert u-alert--block u-alert--with-icon"
 | 
			
		||||
    >{{ error.message.errorText }} {{ error.additionalInfo }}
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
 | 
			
		||||
import { ERROR } from "@/plugins/condition/utils/constants";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    name: 'ConditionError',
 | 
			
		||||
    inject: [
 | 
			
		||||
        'openmct'
 | 
			
		||||
    ],
 | 
			
		||||
    props: {
 | 
			
		||||
        condition: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            default() {
 | 
			
		||||
                return undefined;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            conditionErrors: []
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.getConditionErrors();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        getConditionErrors() {
 | 
			
		||||
            if (this.condition) {
 | 
			
		||||
                this.condition.configuration.criteria.forEach((criterion, index) => {
 | 
			
		||||
                    this.getCriterionErrors(criterion, index);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        getCriterionErrors(criterion, index) {
 | 
			
		||||
            if (!criterion.telemetry) {
 | 
			
		||||
                this.conditionErrors.push({
 | 
			
		||||
                    message: ERROR.TELEMETRY_NOT_FOUND,
 | 
			
		||||
                    additionalInfo: ''
 | 
			
		||||
                });
 | 
			
		||||
            } else {
 | 
			
		||||
                if (criterion.telemetry !== 'all' && criterion.telemetry !== 'any') {
 | 
			
		||||
                    this.openmct.objects.get(criterion.telemetry).then((telemetryObject) => {
 | 
			
		||||
                        if (telemetryObject.type === 'unknown') {
 | 
			
		||||
                            this.conditionErrors.push({
 | 
			
		||||
                                message: ERROR.TELEMETRY_NOT_FOUND,
 | 
			
		||||
                                additionalInfo: criterion.telemetry ? `Key: ${this.openmct.objects.makeKeyString(criterion.telemetry)}` : ''
 | 
			
		||||
                            });
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
@@ -23,19 +23,33 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="c-cs">
 | 
			
		||||
    <section class="c-cs__current-output c-section">
 | 
			
		||||
        <div class="c-cs__header c-section__header">
 | 
			
		||||
            <span class="c-cs__header-label c-section__label">Current Output</span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="c-cs__content c-cs__current-output-value">
 | 
			
		||||
            <template v-if="currentConditionOutput">
 | 
			
		||||
                {{ currentConditionOutput }}
 | 
			
		||||
            </template>
 | 
			
		||||
            <template v-else>No output selected</template>
 | 
			
		||||
            <span class="c-cs__current-output-value__label">Current Output</span>
 | 
			
		||||
            <span class="c-cs__current-output-value__value">
 | 
			
		||||
                <template v-if="currentConditionOutput">
 | 
			
		||||
                    {{ currentConditionOutput }}
 | 
			
		||||
                </template>
 | 
			
		||||
                <template v-else>
 | 
			
		||||
                    {{ defaultConditionOutput }}
 | 
			
		||||
                </template>
 | 
			
		||||
            </span>
 | 
			
		||||
        </div>
 | 
			
		||||
    </section>
 | 
			
		||||
    <TestData :is-editing="isEditing" />
 | 
			
		||||
    <ConditionCollection :is-editing="isEditing"
 | 
			
		||||
                         @conditionSetResultUpdated="updateCurrentOutput" />
 | 
			
		||||
    <div class="c-cs__test-data-and-conditions-w">
 | 
			
		||||
        <TestData class="c-cs__test-data"
 | 
			
		||||
                  :is-editing="isEditing"
 | 
			
		||||
                  :test-data="testData"
 | 
			
		||||
                  :telemetry="telemetryObjs"
 | 
			
		||||
                  @updateTestData="updateTestData"
 | 
			
		||||
        />
 | 
			
		||||
        <ConditionCollection class="c-cs__conditions"
 | 
			
		||||
                             :is-editing="isEditing"
 | 
			
		||||
                             :test-data="testData"
 | 
			
		||||
                             @conditionSetResultUpdated="updateCurrentOutput"
 | 
			
		||||
                             @updateDefaultOutput="updateDefaultOutput"
 | 
			
		||||
                             @telemetryUpdated="updateTelemetry"
 | 
			
		||||
        />
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -54,20 +68,31 @@ export default {
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            currentConditionOutput: ''
 | 
			
		||||
            currentConditionOutput: '',
 | 
			
		||||
            defaultConditionOutput: '',
 | 
			
		||||
            telemetryObjs: [],
 | 
			
		||||
            testData: {}
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.conditionSetIdentifier = this.openmct.objects.makeKeyString(this.domainObject.identifier);
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        if (this.stopProvidingTelemetry) {
 | 
			
		||||
            this.stopProvidingTelemetry();
 | 
			
		||||
        }
 | 
			
		||||
        this.testData = {
 | 
			
		||||
            applied: false,
 | 
			
		||||
            conditionTestInputs: this.domainObject.configuration.conditionTestData || []
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        updateCurrentOutput(currentConditionResult) {
 | 
			
		||||
            this.currentConditionOutput = currentConditionResult.output;
 | 
			
		||||
        },
 | 
			
		||||
        updateDefaultOutput(output) {
 | 
			
		||||
            this.currentConditionOutput = output;
 | 
			
		||||
        },
 | 
			
		||||
        updateTelemetry(telemetryObjs) {
 | 
			
		||||
            this.telemetryObjs = telemetryObjs;
 | 
			
		||||
        },
 | 
			
		||||
        updateTestData(testData) {
 | 
			
		||||
            this.testData = testData;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,41 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* 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.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div class="u-contents">
 | 
			
		||||
    <div class="c-cdef__separator c-row-separator"></div>
 | 
			
		||||
    <span class="c-cdef__label">{{ setRowLabel }}</span>
 | 
			
		||||
    <span class="c-cdef__controls">
 | 
			
		||||
        <span class="c-cdef__control">
 | 
			
		||||
            <select v-model="selectedTelemetry"
 | 
			
		||||
            <select ref="telemetrySelect"
 | 
			
		||||
                    v-model="criterion.telemetry"
 | 
			
		||||
                    @change="updateMetadataOptions"
 | 
			
		||||
            >
 | 
			
		||||
                <option value="">- Select Telemetry -</option>
 | 
			
		||||
                <option value="all">all telemetry</option>
 | 
			
		||||
                <option value="any">any telemetry</option>
 | 
			
		||||
                <option v-for="telemetryOption in telemetry"
 | 
			
		||||
                        :key="telemetryOption.identifier.key"
 | 
			
		||||
                        :value="openmct.objects.makeKeyString(telemetryOption.identifier)"
 | 
			
		||||
                        :value="telemetryOption.identifier"
 | 
			
		||||
                >
 | 
			
		||||
                    {{ telemetryOption.name }}
 | 
			
		||||
                </option>
 | 
			
		||||
@@ -19,7 +44,8 @@
 | 
			
		||||
        <span v-if="criterion.telemetry"
 | 
			
		||||
              class="c-cdef__control"
 | 
			
		||||
        >
 | 
			
		||||
            <select v-model="criterion.metadata"
 | 
			
		||||
            <select ref="metadataSelect"
 | 
			
		||||
                    v-model="criterion.metadata"
 | 
			
		||||
                    @change="updateOperations"
 | 
			
		||||
            >
 | 
			
		||||
                <option value="">- Select Field -</option>
 | 
			
		||||
@@ -35,7 +61,7 @@
 | 
			
		||||
              class="c-cdef__control"
 | 
			
		||||
        >
 | 
			
		||||
            <select v-model="criterion.operation"
 | 
			
		||||
                    @change="updateOperationInputVisibility"
 | 
			
		||||
                    @change="updateInputVisibilityAndValues"
 | 
			
		||||
            >
 | 
			
		||||
                <option value="">- Select Comparison -</option>
 | 
			
		||||
                <option v-for="option in filteredOps"
 | 
			
		||||
@@ -45,16 +71,34 @@
 | 
			
		||||
                    {{ option.text }}
 | 
			
		||||
                </option>
 | 
			
		||||
            </select>
 | 
			
		||||
            <span v-for="(item, inputIndex) in inputCount"
 | 
			
		||||
                  :key="inputIndex"
 | 
			
		||||
                  class="c-cdef__control__inputs"
 | 
			
		||||
            >
 | 
			
		||||
                <input v-model="criterion.input[inputIndex]"
 | 
			
		||||
                       class="c-cdef__control__input"
 | 
			
		||||
                       :type="setInputType"
 | 
			
		||||
                       @blur="persist"
 | 
			
		||||
            <template v-if="!enumerations.length">
 | 
			
		||||
                <span v-for="(item, inputIndex) in inputCount"
 | 
			
		||||
                      :key="inputIndex"
 | 
			
		||||
                      class="c-cdef__control__inputs"
 | 
			
		||||
                >
 | 
			
		||||
                <span v-if="inputIndex < inputCount-1">and</span>
 | 
			
		||||
                    <input v-model="criterion.input[inputIndex]"
 | 
			
		||||
                           class="c-cdef__control__input"
 | 
			
		||||
                           :type="setInputType"
 | 
			
		||||
                           @blur="persist"
 | 
			
		||||
                    >
 | 
			
		||||
                    <span v-if="inputIndex < inputCount-1">and</span>
 | 
			
		||||
                </span>
 | 
			
		||||
            </template>
 | 
			
		||||
            <span v-else>
 | 
			
		||||
                <span v-if="inputCount && criterion.operation"
 | 
			
		||||
                      class="c-cdef__control"
 | 
			
		||||
                >
 | 
			
		||||
                    <select v-model="criterion.input[0]"
 | 
			
		||||
                            @change="persist"
 | 
			
		||||
                    >
 | 
			
		||||
                        <option v-for="option in enumerations"
 | 
			
		||||
                                :key="option.string"
 | 
			
		||||
                                :value="option.value.toString()"
 | 
			
		||||
                        >
 | 
			
		||||
                            {{ option.string }}
 | 
			
		||||
                        </option>
 | 
			
		||||
                    </select>
 | 
			
		||||
                </span>
 | 
			
		||||
            </span>
 | 
			
		||||
        </span>
 | 
			
		||||
    </span>
 | 
			
		||||
@@ -63,6 +107,7 @@
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { OPERATIONS } from '../utils/operations';
 | 
			
		||||
import { INPUT_TYPES } from '../utils/operations';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
@@ -86,18 +131,14 @@ export default {
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        let selectedTelemetry = '';
 | 
			
		||||
        if (this.criterion.telemetry) {
 | 
			
		||||
            selectedTelemetry = this.openmct.objects.makeKeyString(this.criterion.telemetry)
 | 
			
		||||
        }
 | 
			
		||||
        return {
 | 
			
		||||
            telemetryMetadata: {},
 | 
			
		||||
            telemetryMetadataOptions: {},
 | 
			
		||||
            telemetryMetadataOptions: [],
 | 
			
		||||
            operations: OPERATIONS,
 | 
			
		||||
            inputCount: 0,
 | 
			
		||||
            rowLabel: '',
 | 
			
		||||
            operationFormat: '',
 | 
			
		||||
            selectedTelemetry
 | 
			
		||||
            enumerations: [],
 | 
			
		||||
            inputTypes: INPUT_TYPES
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
@@ -106,16 +147,16 @@ export default {
 | 
			
		||||
            return (this.index !== 0 ? operator : '') + 'when';
 | 
			
		||||
        },
 | 
			
		||||
        filteredOps: function () {
 | 
			
		||||
            return [...this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1)];
 | 
			
		||||
            return this.operations.filter(op => op.appliesTo.indexOf(this.operationFormat) !== -1);
 | 
			
		||||
        },
 | 
			
		||||
        setInputType: function () {
 | 
			
		||||
            let type = '';
 | 
			
		||||
            for (let i = 0; i < this.filteredOps.length; i++) {
 | 
			
		||||
                if (this.criterion.operation === this.filteredOps[i].name) {
 | 
			
		||||
                    if (this.filteredOps[i].appliesTo.length === 1) {
 | 
			
		||||
                        type = this.filteredOps[i].appliesTo[0];
 | 
			
		||||
                    if (this.filteredOps[i].appliesTo.length) {
 | 
			
		||||
                        type = this.inputTypes[this.filteredOps[i].appliesTo[0]];
 | 
			
		||||
                    } else {
 | 
			
		||||
                        type = 'string'
 | 
			
		||||
                        type = 'text'
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -123,70 +164,128 @@ export default {
 | 
			
		||||
            return type;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        telemetry: {
 | 
			
		||||
            handler(newTelemetry, oldTelemetry) {
 | 
			
		||||
                this.checkTelemetry();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.updateMetadataOptions();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        getOperationFormat() {
 | 
			
		||||
            this.telemetryMetadata.valueMetadatas.forEach((value, index) => {
 | 
			
		||||
                if (value.key === this.criterion.metadata) {
 | 
			
		||||
                    let valueMetadata = this.telemetryMetadataOptions[index];
 | 
			
		||||
                    if (valueMetadata.enumerations !== undefined) {
 | 
			
		||||
                        this.operationFormat = 'enum';
 | 
			
		||||
                    } else if (valueMetadata.hints.hasOwnProperty('range')) {
 | 
			
		||||
                        this.operationFormat = 'number';
 | 
			
		||||
                    } else if (valueMetadata.hints.hasOwnProperty('domain')) {
 | 
			
		||||
                        this.operationFormat = 'number';
 | 
			
		||||
                    } else if (valueMetadata.key === 'name') {
 | 
			
		||||
                        this.operationFormat = 'string';
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.operationFormat = 'string';
 | 
			
		||||
        checkTelemetry() {
 | 
			
		||||
            if(this.criterion.telemetry) {
 | 
			
		||||
                if (this.criterion.telemetry === 'any' || this.criterion.telemetry === 'all') {
 | 
			
		||||
                    this.updateMetadataOptions();
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (!this.telemetry.find((telemetryObj) => this.openmct.objects.areIdsEqual(this.criterion.telemetry, telemetryObj.identifier))) {
 | 
			
		||||
                        //telemetry being used was removed. So reset this criterion.
 | 
			
		||||
                        this.criterion.telemetry = '';
 | 
			
		||||
                        this.criterion.metadata = '';
 | 
			
		||||
                        this.criterion.input = [];
 | 
			
		||||
                        this.criterion.operation = '';
 | 
			
		||||
                        this.persist();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        updateOperationFormat() {
 | 
			
		||||
            this.enumerations = [];
 | 
			
		||||
            let foundMetadata = this.telemetryMetadataOptions.find((value) => {
 | 
			
		||||
                return value.key === this.criterion.metadata;
 | 
			
		||||
            });
 | 
			
		||||
            if (foundMetadata) {
 | 
			
		||||
                if (foundMetadata.enumerations !== undefined) {
 | 
			
		||||
                    this.operationFormat = 'enum';
 | 
			
		||||
                    this.enumerations = foundMetadata.enumerations;
 | 
			
		||||
                } else if (foundMetadata.format === 'string' || foundMetadata.format === 'number') {
 | 
			
		||||
                    this.operationFormat = foundMetadata.format;
 | 
			
		||||
                } else if (foundMetadata.hints.hasOwnProperty('range')) {
 | 
			
		||||
                    this.operationFormat = 'number';
 | 
			
		||||
                } else if (foundMetadata.hints.hasOwnProperty('domain')) {
 | 
			
		||||
                    this.operationFormat = 'number';
 | 
			
		||||
                } else if (foundMetadata.key === 'name') {
 | 
			
		||||
                    this.operationFormat = 'string';
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.operationFormat = 'number';
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this.updateInputVisibilityAndValues();
 | 
			
		||||
        },
 | 
			
		||||
        updateMetadataOptions(ev) {
 | 
			
		||||
            if (ev) {
 | 
			
		||||
                this.clearDependentFields(ev.target);
 | 
			
		||||
                this.persist();
 | 
			
		||||
            }
 | 
			
		||||
            if (this.criterion.telemetry) {
 | 
			
		||||
                const telemetry = (this.criterion.telemetry === 'all' || this.criterion.telemetry === 'any') ? this.telemetry : [{
 | 
			
		||||
                    identifier: this.criterion.telemetry
 | 
			
		||||
                }];
 | 
			
		||||
 | 
			
		||||
                let telemetryPromises = telemetry.map((telemetryObject) => this.openmct.objects.get(telemetryObject.identifier));
 | 
			
		||||
                Promise.all(telemetryPromises).then(telemetryObjects => {
 | 
			
		||||
                    this.telemetryMetadataOptions = [];
 | 
			
		||||
                    telemetryObjects.forEach(telemetryObject => {
 | 
			
		||||
                        let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
 | 
			
		||||
                        this.addMetaDataOptions(telemetryMetadata.values());
 | 
			
		||||
                    });
 | 
			
		||||
                    this.updateOperations();
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        addMetaDataOptions(options) {
 | 
			
		||||
            if (!this.telemetryMetadataOptions) {
 | 
			
		||||
                this.telemetryMetadataOptions = options;
 | 
			
		||||
            }
 | 
			
		||||
            options.forEach((option) => {
 | 
			
		||||
                const found = this.telemetryMetadataOptions.find((metadataOption) => {
 | 
			
		||||
                    return (metadataOption.key && (metadataOption.key === option.key)) && (metadataOption.name && (metadataOption.name === option.name))
 | 
			
		||||
                });
 | 
			
		||||
                if (!found) {
 | 
			
		||||
                    this.telemetryMetadataOptions.push(option);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        updateMetadataOptions(ev) {
 | 
			
		||||
            if (ev) {this.clearInputs()}
 | 
			
		||||
            if (this.selectedTelemetry) {
 | 
			
		||||
                this.openmct.objects.get(this.selectedTelemetry).then((telemetryObject) => {
 | 
			
		||||
                    this.criterion.telemetry.name = telemetryObject.name;
 | 
			
		||||
                    this.telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
 | 
			
		||||
                    this.telemetryMetadataOptions = this.telemetryMetadata.values();
 | 
			
		||||
                    this.updateOperations();
 | 
			
		||||
                    this.updateOperationInputVisibility();
 | 
			
		||||
                });
 | 
			
		||||
            } else {
 | 
			
		||||
                this.criterion.metadata = '';
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        updateOperations(ev) {
 | 
			
		||||
            this.updateOperationFormat();
 | 
			
		||||
            if (ev) {
 | 
			
		||||
                this.criterion.telemetry.fieldName = ev.target.options[ev.target.selectedIndex].text;
 | 
			
		||||
                this.clearInputs()
 | 
			
		||||
                this.clearDependentFields(ev.target);
 | 
			
		||||
                this.persist();
 | 
			
		||||
            }
 | 
			
		||||
            this.getOperationFormat();
 | 
			
		||||
            this.persist();
 | 
			
		||||
        },
 | 
			
		||||
        updateOperationInputVisibility(ev) {
 | 
			
		||||
        updateInputVisibilityAndValues(ev) {
 | 
			
		||||
            if (ev) {
 | 
			
		||||
                this.criterion.input = [];
 | 
			
		||||
                this.inputCount = 0;
 | 
			
		||||
                this.clearDependentFields();
 | 
			
		||||
                this.persist();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (let i = 0; i < this.filteredOps.length; i++) {
 | 
			
		||||
                if (this.criterion.operation === this.filteredOps[i].name) {
 | 
			
		||||
                    this.inputCount = this.filteredOps[i].inputCount;
 | 
			
		||||
                    if (!this.inputCount) {this.criterion.input = []}
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this.persist();
 | 
			
		||||
            if (!this.inputCount) {
 | 
			
		||||
                this.criterion.input = [];
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        clearInputs() {
 | 
			
		||||
            this.criterion.operation = '';
 | 
			
		||||
            this.criterion.input = [];
 | 
			
		||||
            this.inputCount = 0;
 | 
			
		||||
        },
 | 
			
		||||
        updateMetadataSelection() {
 | 
			
		||||
            this.updateOperationInputVisibility();
 | 
			
		||||
        clearDependentFields(el) {
 | 
			
		||||
            if (el === this.$refs.telemetrySelect) {
 | 
			
		||||
                this.criterion.metadata = '';
 | 
			
		||||
            } else if (el === this.$refs.metadataSelect) {
 | 
			
		||||
                if (!this.filteredOps.find(operation => operation.name === this.criterion.operation)) {
 | 
			
		||||
                    this.criterion.operation = '';
 | 
			
		||||
                    this.criterion.input = this.enumerations.length ? [this.enumerations[0].value.toString()] : [];
 | 
			
		||||
                    this.inputCount = 0;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (this.enumerations.length && !this.criterion.input.length) {
 | 
			
		||||
                    this.criterion.input = [this.enumerations[0].value.toString()];
 | 
			
		||||
                }
 | 
			
		||||
                this.inputCount = 0;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        persist() {
 | 
			
		||||
            this.$emit('persist', this.criterion);
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<section v-show="isEditing"
 | 
			
		||||
         id="test-data"
 | 
			
		||||
         class="c-cs__test-data"
 | 
			
		||||
         :class="{ 'is-expanded': expanded }"
 | 
			
		||||
>
 | 
			
		||||
    <div class="c-cs__header c-section__header">
 | 
			
		||||
@@ -37,36 +36,85 @@
 | 
			
		||||
    <div v-if="expanded"
 | 
			
		||||
         class="c-cs__content"
 | 
			
		||||
    >
 | 
			
		||||
        <label class="c-toggle-switch">
 | 
			
		||||
            <input
 | 
			
		||||
                type="checkbox"
 | 
			
		||||
                :checked="isApplied"
 | 
			
		||||
                @change="applyTestData"
 | 
			
		||||
        <div class="c-cs__test-data__controls c-cdef__controls"
 | 
			
		||||
             :disabled="!telemetry.length"
 | 
			
		||||
        >
 | 
			
		||||
            <label class="c-toggle-switch">
 | 
			
		||||
                <input
 | 
			
		||||
                    type="checkbox"
 | 
			
		||||
                    :checked="isApplied"
 | 
			
		||||
                    @change="applyTestData"
 | 
			
		||||
                >
 | 
			
		||||
                <span class="c-toggle-switch__slider"></span>
 | 
			
		||||
                <span class="c-toggle-switch__label">Apply Test Data</span>
 | 
			
		||||
            </label>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="c-cs-tests">
 | 
			
		||||
            <span v-for="(testInput, tIndex) in testInputs"
 | 
			
		||||
                  :key="tIndex"
 | 
			
		||||
                  class="c-test-datum c-cs-test"
 | 
			
		||||
            >
 | 
			
		||||
            <span class="c-toggle-switch__slider"></span>
 | 
			
		||||
            <span class="c-toggle-switch__label">Apply Test Data</span>
 | 
			
		||||
        </label>
 | 
			
		||||
        <div class="c-cs-test-h">
 | 
			
		||||
            <div v-for="n in 5"
 | 
			
		||||
                 :key="n"
 | 
			
		||||
                 class="c-test-datum"
 | 
			
		||||
            >
 | 
			
		||||
                <span class="c-test-datum__label">Set</span>
 | 
			
		||||
                <div class="c-test-datum__controls">
 | 
			
		||||
                    <select>
 | 
			
		||||
                        <option>- Select Input -</option>
 | 
			
		||||
                    </select>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="c-test-datum__buttons">
 | 
			
		||||
                <span class="c-cs-test__label">Set</span>
 | 
			
		||||
                <span class="c-cs-test__controls">
 | 
			
		||||
                    <span class="c-cdef__control">
 | 
			
		||||
                        <select v-model="testInput.telemetry"
 | 
			
		||||
                                @change="updateMetadata(testInput)"
 | 
			
		||||
                        >
 | 
			
		||||
                            <option value="">- Select Telemetry -</option>
 | 
			
		||||
                            <option v-for="(telemetryOption, index) in telemetry"
 | 
			
		||||
                                    :key="index"
 | 
			
		||||
                                    :value="telemetryOption.identifier"
 | 
			
		||||
                            >
 | 
			
		||||
                                {{ telemetryOption.name }}
 | 
			
		||||
                            </option>
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <span v-if="testInput.telemetry"
 | 
			
		||||
                          class="c-cdef__control"
 | 
			
		||||
                    >
 | 
			
		||||
                        <select v-model="testInput.metadata"
 | 
			
		||||
                                @change="updateTestData"
 | 
			
		||||
                        >
 | 
			
		||||
                            <option value="">- Select Field -</option>
 | 
			
		||||
                            <option v-for="(option, index) in telemetryMetadataOptions[getId(testInput.telemetry)]"
 | 
			
		||||
                                    :key="index"
 | 
			
		||||
                                    :value="option.key"
 | 
			
		||||
                            >
 | 
			
		||||
                                {{ option.name }}
 | 
			
		||||
                            </option>
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <span v-if="testInput.metadata"
 | 
			
		||||
                          class="c-cdef__control__inputs"
 | 
			
		||||
                    >
 | 
			
		||||
                        <input v-model="testInput.value"
 | 
			
		||||
                               placeholder="Enter test input"
 | 
			
		||||
                               type="text"
 | 
			
		||||
                               class="c-cdef__control__input"
 | 
			
		||||
                               @change="updateTestData"
 | 
			
		||||
                        >
 | 
			
		||||
                    </span>
 | 
			
		||||
                </span>
 | 
			
		||||
                <div class="c-cs-test__buttons">
 | 
			
		||||
                    <button class="c-click-icon c-test-data__duplicate-button icon-duplicate"
 | 
			
		||||
                            title="Duplicate this test data value"
 | 
			
		||||
                            title="Duplicate this test datum"
 | 
			
		||||
                            @click="addTestInput(testInput)"
 | 
			
		||||
                    ></button>
 | 
			
		||||
                    <button class="c-click-icon c-test-data__delete-button icon-trash"
 | 
			
		||||
                            title="Delete this test data value"
 | 
			
		||||
                            title="Delete this test datum"
 | 
			
		||||
                            @click="removeTestInput(tIndex)"
 | 
			
		||||
                    ></button>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            </span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <button
 | 
			
		||||
            v-show="isEditing"
 | 
			
		||||
            id="addTestDatum"
 | 
			
		||||
            class="c-button c-button--major icon-plus labeled"
 | 
			
		||||
            @click="addTestInput"
 | 
			
		||||
        >
 | 
			
		||||
            <span class="c-cs-button__label">Add Test Datum</span>
 | 
			
		||||
        </button>
 | 
			
		||||
    </div>
 | 
			
		||||
</section>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -75,17 +123,113 @@
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
    props: {
 | 
			
		||||
        isEditing: Boolean
 | 
			
		||||
        isEditing: Boolean,
 | 
			
		||||
        telemetry: {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            required: true,
 | 
			
		||||
            default: () => []
 | 
			
		||||
        },
 | 
			
		||||
        testData: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true,
 | 
			
		||||
            default: () => {
 | 
			
		||||
                return {
 | 
			
		||||
                    applied: false,
 | 
			
		||||
                    conditionTestInputs: []
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            expanded: true,
 | 
			
		||||
            isApplied: true
 | 
			
		||||
            isApplied: false,
 | 
			
		||||
            testInputs: [],
 | 
			
		||||
            telemetryMetadataOptions: {}
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        isEditing(editing) {
 | 
			
		||||
            if (!editing) {
 | 
			
		||||
                this.resetApplied();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        telemetry: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.initializeMetadata();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        },
 | 
			
		||||
        testData: {
 | 
			
		||||
            handler() {
 | 
			
		||||
                this.initialize();
 | 
			
		||||
            },
 | 
			
		||||
            deep: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        this.resetApplied();
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.initialize();
 | 
			
		||||
        this.initializeMetadata();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        applyTestData(ev) {
 | 
			
		||||
            this.$emit('change', ev.target.checked);
 | 
			
		||||
        applyTestData() {
 | 
			
		||||
            this.isApplied = !this.isApplied;
 | 
			
		||||
            this.updateTestData();
 | 
			
		||||
        },
 | 
			
		||||
        initialize() {
 | 
			
		||||
            if (this.testData && this.testData.conditionTestInputs) {
 | 
			
		||||
                this.testInputs = this.testData.conditionTestInputs;
 | 
			
		||||
            }
 | 
			
		||||
            if (!this.testInputs.length) {
 | 
			
		||||
                this.addTestInput();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        initializeMetadata() {
 | 
			
		||||
            this.telemetry.forEach((telemetryObject) => {
 | 
			
		||||
                const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
 | 
			
		||||
                let telemetryMetadata = this.openmct.telemetry.getMetadata(telemetryObject);
 | 
			
		||||
                this.telemetryMetadataOptions[id] = telemetryMetadata.values().slice();
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        addTestInput(testInput) {
 | 
			
		||||
            this.testInputs.push(Object.assign({
 | 
			
		||||
                telemetry: '',
 | 
			
		||||
                metadata: '',
 | 
			
		||||
                input: ''
 | 
			
		||||
            }, testInput));
 | 
			
		||||
        },
 | 
			
		||||
        removeTestInput(index) {
 | 
			
		||||
            this.testInputs.splice(index, 1);
 | 
			
		||||
            this.updateTestData();
 | 
			
		||||
        },
 | 
			
		||||
        getId(identifier) {
 | 
			
		||||
            if (identifier) {
 | 
			
		||||
                return this.openmct.objects.makeKeyString(identifier);
 | 
			
		||||
            }
 | 
			
		||||
            return [];
 | 
			
		||||
        },
 | 
			
		||||
        updateMetadata(testInput) {
 | 
			
		||||
            if (testInput.telemetry) {
 | 
			
		||||
                const id = this.openmct.objects.makeKeyString(testInput.telemetry);
 | 
			
		||||
                if(this.telemetryMetadataOptions[id]) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                let telemetryMetadata = this.openmct.telemetry.getMetadata(testInput);
 | 
			
		||||
                this.telemetryMetadataOptions[id] = telemetryMetadata.values().slice();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        resetApplied() {
 | 
			
		||||
            this.isApplied = false;
 | 
			
		||||
            this.updateTestData();
 | 
			
		||||
        },
 | 
			
		||||
        updateTestData() {
 | 
			
		||||
            this.$emit('updateTestData', {
 | 
			
		||||
                applied: this.isApplied,
 | 
			
		||||
                conditionTestInputs: this.testInputs
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,89 +0,0 @@
 | 
			
		||||
.c-cs {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
 | 
			
		||||
    &__content {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
 | 
			
		||||
        > * {
 | 
			
		||||
            flex: 0 0 auto;
 | 
			
		||||
            overflow: hidden;
 | 
			
		||||
            + * {
 | 
			
		||||
                margin-top: $interiorMarginSm;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .c-button {
 | 
			
		||||
            align-self: start;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .is-editing & {
 | 
			
		||||
        // Add some space to kick away from blue editing border indication
 | 
			
		||||
        padding: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    section {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__conditions-h {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        padding-right: $interiorMarginSm;
 | 
			
		||||
 | 
			
		||||
        > * + * {
 | 
			
		||||
            margin-top: $interiorMarginSm;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__conditions {
 | 
			
		||||
        > * + * {
 | 
			
		||||
            margin-top: $interiorMarginSm;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .hint {
 | 
			
		||||
        padding: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /************************** SPECIFIC ITEMS */
 | 
			
		||||
    &__current-output-value {
 | 
			
		||||
        font-size: 1.25em;
 | 
			
		||||
        padding: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************** TEST DATA */
 | 
			
		||||
.c-cs-test-h {
 | 
			
		||||
    flex: 1 1 auto;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
    padding-right: $interiorMarginSm;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-top: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-test-datum {
 | 
			
		||||
    > * {
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
        + * {
 | 
			
		||||
            margin-left: $interiorMargin;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    &__controls {
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,111 +0,0 @@
 | 
			
		||||
.c-condition,
 | 
			
		||||
.c-test-datum {
 | 
			
		||||
    @include discreteItem();
 | 
			
		||||
    display: flex;
 | 
			
		||||
    padding: $interiorMargin;
 | 
			
		||||
 | 
			
		||||
    &--edit {
 | 
			
		||||
        line-height: 160%; // For layout when inputs wrap, like in criteria
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-condition {
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-top: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
    &--browse {
 | 
			
		||||
        .c-condition__summary {
 | 
			
		||||
            border-top: 1px solid $colorInteriorBorder;
 | 
			
		||||
            padding-top: $interiorMargin;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /***************************** HEADER */
 | 
			
		||||
    &__header {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: flex-start;
 | 
			
		||||
        align-content: stretch;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
 | 
			
		||||
        > * {
 | 
			
		||||
            flex: 0 0 auto;
 | 
			
		||||
            + * {
 | 
			
		||||
                margin-left: $interiorMarginSm;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    &__name {
 | 
			
		||||
        font-weight: bold;
 | 
			
		||||
        align-self: baseline; // Fixes bold line-height offset problem
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__output,
 | 
			
		||||
    &__summary {
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************** CONDITION DEFINITION, EDITING */
 | 
			
		||||
.c-cdef {
 | 
			
		||||
    display: grid;
 | 
			
		||||
    grid-row-gap: $interiorMarginSm;
 | 
			
		||||
    grid-column-gap: $interiorMargin;
 | 
			
		||||
    grid-auto-columns: min-content 1fr max-content;
 | 
			
		||||
    align-items: start;
 | 
			
		||||
    min-width: 150px;
 | 
			
		||||
    margin-left: 29px;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
 | 
			
		||||
    &__criteria,
 | 
			
		||||
    &__match-and-criteria {
 | 
			
		||||
        display: contents;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__label {
 | 
			
		||||
        grid-column: 1;
 | 
			
		||||
        text-align: right;
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__separator {
 | 
			
		||||
        grid-column: 1 / span 3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__controls {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-wrap: wrap;
 | 
			
		||||
        align-items: flex-start;
 | 
			
		||||
        //line-height: 200%;
 | 
			
		||||
        grid-column: 2;
 | 
			
		||||
 | 
			
		||||
        > * > * {
 | 
			
		||||
            margin-right: $interiorMarginSm;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__buttons {
 | 
			
		||||
        grid-column: 3;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-c__drag-ghost {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    min-height: $interiorMarginSm;
 | 
			
		||||
 | 
			
		||||
    //&:before {
 | 
			
		||||
    //    @include test();
 | 
			
		||||
    //    content: '';
 | 
			
		||||
    //    display: block;
 | 
			
		||||
    //    z-index: 2;
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    &.dragging {
 | 
			
		||||
        min-height: 5em;
 | 
			
		||||
        //border: solid 1px blue;
 | 
			
		||||
        background-color: lightblue;
 | 
			
		||||
        border-radius: 2px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										318
									
								
								src/plugins/condition/components/conditionals.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								src/plugins/condition/components/conditionals.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,318 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
/***************************** DRAGGING */
 | 
			
		||||
.is-active-dragging {
 | 
			
		||||
    .c-condition-h__drop-target {
 | 
			
		||||
        height: 3px;
 | 
			
		||||
        margin-bottom: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-condition-h {
 | 
			
		||||
    &__drop-target {
 | 
			
		||||
        border-radius: $controlCr;
 | 
			
		||||
        height: 0;
 | 
			
		||||
        min-height: 0;
 | 
			
		||||
        transition: background-color, height;
 | 
			
		||||
        transition-duration: 150ms;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-drag-target {
 | 
			
		||||
        .c-condition > * {
 | 
			
		||||
            pointer-events: none; // Keeps the JS drop handler from being intercepted by internal elements
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .c-condition-h__drop-target {
 | 
			
		||||
            background-color: rgba($colorKey, 0.7);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-cs {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
 | 
			
		||||
    /************************** CONDITION SET LAYOUT */
 | 
			
		||||
    &__current-output {
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__test-data-and-conditions-w {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__test-data,
 | 
			
		||||
    &__conditions {
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__test-data {
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
        max-height: 50%;
 | 
			
		||||
 | 
			
		||||
        &.is-expanded {
 | 
			
		||||
            margin-bottom: $interiorMargin * 4;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__conditions {
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
 | 
			
		||||
        > * + * {
 | 
			
		||||
            margin-top: $interiorMarginSm;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__content {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        flex: 0 1 auto;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
 | 
			
		||||
        > * {
 | 
			
		||||
            flex: 0 0 auto;
 | 
			
		||||
            overflow: hidden;
 | 
			
		||||
            + * {
 | 
			
		||||
                margin-top: $interiorMarginSm;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .c-button {
 | 
			
		||||
            align-self: start;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .is-editing & {
 | 
			
		||||
        // Add some space to kick away from blue editing border indication
 | 
			
		||||
        padding: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    section {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__conditions-h {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        overflow: auto;
 | 
			
		||||
        padding-right: $interiorMarginSm;
 | 
			
		||||
 | 
			
		||||
        > * + * {
 | 
			
		||||
            margin-top: $interiorMarginSm;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .hint {
 | 
			
		||||
        padding: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /************************** SPECIFIC ITEMS */
 | 
			
		||||
    &__current-output-value {
 | 
			
		||||
        flex-direction: row;
 | 
			
		||||
        align-items: baseline;
 | 
			
		||||
        padding: 0 $interiorMargin $interiorMarginLg $interiorMargin;
 | 
			
		||||
 | 
			
		||||
        > * {
 | 
			
		||||
            padding: $interiorMargin 0; // Must do this to align label and value
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &__label {
 | 
			
		||||
            color: $colorInspectorSectionHeaderFg;
 | 
			
		||||
            opacity: 0.9;
 | 
			
		||||
            text-transform: uppercase;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &__value {
 | 
			
		||||
            $p: $interiorMargin * 3;
 | 
			
		||||
            font-size: 1.25em;
 | 
			
		||||
            margin-left: $interiorMargin;
 | 
			
		||||
            padding-left: $p;
 | 
			
		||||
            padding-right: $p;
 | 
			
		||||
            background: rgba(black, 0.2);
 | 
			
		||||
            border-radius: 5px;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************** CONDITIONS AND TEST DATUM ELEMENTS */
 | 
			
		||||
.c-condition,
 | 
			
		||||
.c-test-datum {
 | 
			
		||||
    @include discreteItem();
 | 
			
		||||
    display: flex;
 | 
			
		||||
    padding: $interiorMargin;
 | 
			
		||||
    line-height: 170%; // Aligns text with controls like selects
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-cdef,
 | 
			
		||||
.c-cs-test {
 | 
			
		||||
    &__controls {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
        flex-wrap: wrap;
 | 
			
		||||
 | 
			
		||||
        > * > * {
 | 
			
		||||
            margin-right: $interiorMarginSm;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__buttons {
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-condition {
 | 
			
		||||
    border: 1px solid transparent;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    min-width: 400px;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-top: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
    &--browse {
 | 
			
		||||
        .c-condition__summary {
 | 
			
		||||
            border-top: 1px solid $colorInteriorBorder;
 | 
			
		||||
            padding-top: $interiorMargin;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /***************************** HEADER */
 | 
			
		||||
    &__header {
 | 
			
		||||
        $h: 22px;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        align-items: start;
 | 
			
		||||
        align-content: stretch;
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
        min-height: $h;
 | 
			
		||||
        line-height: $h;
 | 
			
		||||
 | 
			
		||||
        > * {
 | 
			
		||||
            flex: 0 0 auto;
 | 
			
		||||
            + * {
 | 
			
		||||
                margin-left: $interiorMarginSm;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__drag-grippy {
 | 
			
		||||
        transform: translateY(50%);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__name {
 | 
			
		||||
        font-weight: bold;
 | 
			
		||||
        align-self: baseline; // Fixes bold line-height offset problem
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__output,
 | 
			
		||||
    &__summary {
 | 
			
		||||
        flex: 1 1 auto;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.is-current {
 | 
			
		||||
        $c: $colorBodyFg;
 | 
			
		||||
        border-color: rgba($c, 0.2);
 | 
			
		||||
        background: rgba($c, 0.2);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************** CONDITION DEFINITION, EDITING */
 | 
			
		||||
.c-cdef {
 | 
			
		||||
    display: grid;
 | 
			
		||||
    grid-row-gap: $interiorMarginSm;
 | 
			
		||||
    grid-column-gap: $interiorMargin;
 | 
			
		||||
    grid-auto-columns: min-content 1fr max-content;
 | 
			
		||||
    align-items: start;
 | 
			
		||||
    min-width: 150px;
 | 
			
		||||
    margin-left: 29px;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
 | 
			
		||||
    &__criteria,
 | 
			
		||||
    &__match-and-criteria {
 | 
			
		||||
        display: contents;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__label {
 | 
			
		||||
        grid-column: 1;
 | 
			
		||||
        text-align: right;
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__separator {
 | 
			
		||||
        grid-column: 1 / span 3;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__controls {
 | 
			
		||||
        align-items: flex-start;
 | 
			
		||||
        grid-column: 2;
 | 
			
		||||
 | 
			
		||||
        > * > * {
 | 
			
		||||
            margin-right: $interiorMarginSm;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__buttons {
 | 
			
		||||
        grid-column: 3;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-c__drag-ghost {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    min-height: $interiorMarginSm;
 | 
			
		||||
 | 
			
		||||
    &.dragging {
 | 
			
		||||
        min-height: 5em;
 | 
			
		||||
        background-color: lightblue;
 | 
			
		||||
        border-radius: 2px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***************************** TEST DATA */
 | 
			
		||||
.c-cs__test-data {
 | 
			
		||||
    &__controls {
 | 
			
		||||
        flex: 0 0 auto;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-cs-tests {
 | 
			
		||||
    flex: 0 1 auto;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
    padding-right: $interiorMarginSm;
 | 
			
		||||
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-top: $interiorMarginSm;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-cs-test {
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-left: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,185 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* 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.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<li class="c-tree__item-h">
 | 
			
		||||
    <div
 | 
			
		||||
        class="c-tree__item"
 | 
			
		||||
        :class="{ 'is-alias': isAlias, 'is-navigated-object': navigated }"
 | 
			
		||||
        @click="handleItemSelected(node.object, node)"
 | 
			
		||||
    >
 | 
			
		||||
        <view-control
 | 
			
		||||
            v-model="expanded"
 | 
			
		||||
            class="c-tree__item__view-control"
 | 
			
		||||
            :enabled="hasChildren"
 | 
			
		||||
            :propagate="false"
 | 
			
		||||
        />
 | 
			
		||||
        <div class="c-tree__item__label c-object-label">
 | 
			
		||||
            <div
 | 
			
		||||
                class="c-tree__item__type-icon c-object-label__type-icon"
 | 
			
		||||
                :class="typeClass"
 | 
			
		||||
            ></div>
 | 
			
		||||
            <div class="c-tree__item__name c-object-label__name">{{ node.object.name }}</div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <ul
 | 
			
		||||
        v-if="expanded"
 | 
			
		||||
        class="c-tree"
 | 
			
		||||
    >
 | 
			
		||||
        <li
 | 
			
		||||
            v-if="isLoading && !loaded"
 | 
			
		||||
            class="c-tree__item-h"
 | 
			
		||||
        >
 | 
			
		||||
            <div class="c-tree__item loading">
 | 
			
		||||
                <span class="c-tree__item__label">Loading...</span>
 | 
			
		||||
            </div>
 | 
			
		||||
        </li>
 | 
			
		||||
        <condition-set-dialog-tree-item
 | 
			
		||||
            v-for="child in children"
 | 
			
		||||
            :key="child.id"
 | 
			
		||||
            :node="child"
 | 
			
		||||
            :selected-item="selectedItem"
 | 
			
		||||
            :handle-item-selected="handleItemSelected"
 | 
			
		||||
        />
 | 
			
		||||
    </ul>
 | 
			
		||||
</li>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import viewControl from '@/ui/components/viewControl.vue';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    name: 'ConditionSetDialogTreeItem',
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
    components: {
 | 
			
		||||
        viewControl
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
        node: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        },
 | 
			
		||||
        selectedItem: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            default() {
 | 
			
		||||
                return undefined;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        handleItemSelected: {
 | 
			
		||||
            type: Function,
 | 
			
		||||
            default() {
 | 
			
		||||
                return (item) => {};
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            hasChildren: false,
 | 
			
		||||
            isLoading: false,
 | 
			
		||||
            loaded: false,
 | 
			
		||||
            children: [],
 | 
			
		||||
            expanded: false
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        navigated() {
 | 
			
		||||
            const itemId = this.selectedItem && this.selectedItem.itemId;
 | 
			
		||||
            const isSelectedObject = itemId && this.openmct.objects.areIdsEqual(this.node.object.identifier, itemId);
 | 
			
		||||
            if (isSelectedObject && this.node.objectPath && this.node.objectPath.length > 1) {
 | 
			
		||||
                const isParent = this.openmct.objects.areIdsEqual(this.node.objectPath[1].identifier, this.selectedItem.parentId);
 | 
			
		||||
                return isSelectedObject && isParent;
 | 
			
		||||
            }
 | 
			
		||||
            return isSelectedObject;
 | 
			
		||||
        },
 | 
			
		||||
        isAlias() {
 | 
			
		||||
            let parent = this.node.objectPath[1];
 | 
			
		||||
            if (!parent) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            let parentKeyString = this.openmct.objects.makeKeyString(parent.identifier);
 | 
			
		||||
            return parentKeyString !== this.node.object.location;
 | 
			
		||||
        },
 | 
			
		||||
        typeClass() {
 | 
			
		||||
            let type = this.openmct.types.get(this.node.object.type);
 | 
			
		||||
            if (!type) {
 | 
			
		||||
                return 'icon-object-unknown';
 | 
			
		||||
            }
 | 
			
		||||
            return type.definition.cssClass;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    watch: {
 | 
			
		||||
        expanded() {
 | 
			
		||||
            if (!this.hasChildren) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (!this.loaded && !this.isLoading) {
 | 
			
		||||
                this.composition = this.openmct.composition.get(this.domainObject);
 | 
			
		||||
                this.composition.on('add', this.addChild);
 | 
			
		||||
                this.composition.on('remove', this.removeChild);
 | 
			
		||||
                this.composition.load().then(this.finishLoading);
 | 
			
		||||
                this.isLoading = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.domainObject = this.node.object;
 | 
			
		||||
        let removeListener = this.openmct.objects.observe(this.domainObject, '*', (newObject) => {
 | 
			
		||||
            this.domainObject = newObject;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.$once('hook:destroyed', removeListener);
 | 
			
		||||
        if (this.openmct.composition.get(this.node.object)) {
 | 
			
		||||
            this.hasChildren = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        this.expanded = false;
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
        if (this.composition) {
 | 
			
		||||
            this.composition.off('add', this.addChild);
 | 
			
		||||
            this.composition.off('remove', this.removeChild);
 | 
			
		||||
            delete this.composition;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        addChild(child) {
 | 
			
		||||
            this.children.push({
 | 
			
		||||
                id: this.openmct.objects.makeKeyString(child.identifier),
 | 
			
		||||
                object: child,
 | 
			
		||||
                objectPath: [child].concat(this.node.objectPath),
 | 
			
		||||
                navigateToParent: this.navigateToPath
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        removeChild(identifier) {
 | 
			
		||||
            let removeId = this.openmct.objects.makeKeyString(identifier);
 | 
			
		||||
            this.children = this.children
 | 
			
		||||
                .filter(c => c.id !== removeId);
 | 
			
		||||
        },
 | 
			
		||||
        finishLoading() {
 | 
			
		||||
            this.isLoading = false;
 | 
			
		||||
            this.loaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
@@ -0,0 +1,172 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* 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.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div class="u-contents">
 | 
			
		||||
    <div class="c-overlay__top-bar">
 | 
			
		||||
        <div class="c-overlay__dialog-title">Select Condition Set</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="c-overlay__contents-main c-selector c-tree-and-search">
 | 
			
		||||
        <div class="c-tree-and-search__search">
 | 
			
		||||
            <search ref="shell-search"
 | 
			
		||||
                    class="c-search"
 | 
			
		||||
                    :value="searchValue"
 | 
			
		||||
                    @input="searchTree"
 | 
			
		||||
                    @clear="searchTree"
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- loading -->
 | 
			
		||||
        <div v-if="isLoading"
 | 
			
		||||
             class="c-tree-and-search__loading loading"
 | 
			
		||||
        ></div>
 | 
			
		||||
        <!-- end loading -->
 | 
			
		||||
 | 
			
		||||
        <div v-if="(allTreeItems.length === 0) || (searchValue && filteredTreeItems.length === 0)"
 | 
			
		||||
             class="c-tree-and-search__no-results"
 | 
			
		||||
        >
 | 
			
		||||
            No results found
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <!-- main tree -->
 | 
			
		||||
        <ul v-if="!isLoading"
 | 
			
		||||
            v-show="!searchValue"
 | 
			
		||||
            class="c-tree-and-search__tree c-tree"
 | 
			
		||||
        >
 | 
			
		||||
            <condition-set-dialog-tree-item
 | 
			
		||||
                v-for="treeItem in allTreeItems"
 | 
			
		||||
                :key="treeItem.id"
 | 
			
		||||
                :node="treeItem"
 | 
			
		||||
                :selected-item="selectedItem"
 | 
			
		||||
                :handle-item-selected="handleItemSelection"
 | 
			
		||||
            />
 | 
			
		||||
        </ul>
 | 
			
		||||
        <!-- end main tree -->
 | 
			
		||||
 | 
			
		||||
        <!-- search tree -->
 | 
			
		||||
        <ul v-if="searchValue"
 | 
			
		||||
            class="c-tree-and-search__tree c-tree"
 | 
			
		||||
        >
 | 
			
		||||
            <condition-set-dialog-tree-item
 | 
			
		||||
                v-for="treeItem in filteredTreeItems"
 | 
			
		||||
                :key="treeItem.id"
 | 
			
		||||
                :node="treeItem"
 | 
			
		||||
                :selected-item="selectedItem"
 | 
			
		||||
                :handle-item-selected="handleItemSelection"
 | 
			
		||||
            />
 | 
			
		||||
        </ul>
 | 
			
		||||
        <!-- end search tree -->
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import search from '@/ui/components/search.vue';
 | 
			
		||||
import ConditionSetDialogTreeItem from './ConditionSetDialogTreeItem.vue';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct'],
 | 
			
		||||
    name: 'ConditionSetSelectorDialog',
 | 
			
		||||
    components: {
 | 
			
		||||
        search,
 | 
			
		||||
        ConditionSetDialogTreeItem
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            expanded: false,
 | 
			
		||||
            searchValue: '',
 | 
			
		||||
            allTreeItems: [],
 | 
			
		||||
            filteredTreeItems: [],
 | 
			
		||||
            isLoading: false,
 | 
			
		||||
            selectedItem: undefined
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.searchService = this.openmct.$injector.get('searchService');
 | 
			
		||||
        this.getAllChildren();
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        getAllChildren() {
 | 
			
		||||
            this.isLoading = true;
 | 
			
		||||
            this.openmct.objects.get('ROOT')
 | 
			
		||||
                .then(root => {
 | 
			
		||||
                    return this.openmct.composition.get(root).load()
 | 
			
		||||
                })
 | 
			
		||||
                .then(children => {
 | 
			
		||||
                    this.isLoading = false;
 | 
			
		||||
                    this.allTreeItems = children.map(c => {
 | 
			
		||||
                        return {
 | 
			
		||||
                            id: this.openmct.objects.makeKeyString(c.identifier),
 | 
			
		||||
                            object: c,
 | 
			
		||||
                            objectPath: [c],
 | 
			
		||||
                            navigateToParent: '/browse'
 | 
			
		||||
                        };
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
        },
 | 
			
		||||
        getFilteredChildren() {
 | 
			
		||||
            this.searchService.query(this.searchValue).then(children => {
 | 
			
		||||
                this.filteredTreeItems = children.hits.map(child => {
 | 
			
		||||
 | 
			
		||||
                    let context = child.object.getCapability('context'),
 | 
			
		||||
                        object = child.object.useCapability('adapter'),
 | 
			
		||||
                        objectPath = [],
 | 
			
		||||
                        navigateToParent;
 | 
			
		||||
 | 
			
		||||
                    if (context) {
 | 
			
		||||
                        objectPath = context.getPath().slice(1)
 | 
			
		||||
                            .map(oldObject => oldObject.useCapability('adapter'))
 | 
			
		||||
                            .reverse();
 | 
			
		||||
                        navigateToParent = '/browse/' + objectPath.slice(1)
 | 
			
		||||
                            .map((parent) => this.openmct.objects.makeKeyString(parent.identifier))
 | 
			
		||||
                            .join('/');
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return {
 | 
			
		||||
                        id: this.openmct.objects.makeKeyString(object.identifier),
 | 
			
		||||
                        object,
 | 
			
		||||
                        objectPath,
 | 
			
		||||
                        navigateToParent
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        searchTree(value) {
 | 
			
		||||
            this.searchValue = value;
 | 
			
		||||
 | 
			
		||||
            if (this.searchValue !== '') {
 | 
			
		||||
                this.getFilteredChildren();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        handleItemSelection(item, node) {
 | 
			
		||||
            if (item && item.type === 'conditionSet') {
 | 
			
		||||
                const parentId = (node.objectPath && node.objectPath.length > 1) ? node.objectPath[1].identifier : undefined;
 | 
			
		||||
                this.selectedItem = {
 | 
			
		||||
                    itemId: item.identifier,
 | 
			
		||||
                    parentId
 | 
			
		||||
                };
 | 
			
		||||
                this.$emit('conditionSetSelected', item);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
@@ -1,69 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
    <div v-if="conditionStyle"
 | 
			
		||||
         class="holder c-c-button-wrapper align-left"
 | 
			
		||||
    >
 | 
			
		||||
        <div>{{ conditionStyle.conditionName }}</div>
 | 
			
		||||
        <span :style="conditionStyle.style">ABC</span>
 | 
			
		||||
        <toolbar-color-picker v-if="conditionStyle.style.border"
 | 
			
		||||
                              :options="borderColorOption"
 | 
			
		||||
                              @change="updateStyleValue"
 | 
			
		||||
        />
 | 
			
		||||
        <toolbar-color-picker v-if="conditionStyle.style.backgroundColor"
 | 
			
		||||
                              :options="backgroundColorOption"
 | 
			
		||||
                              @change="updateStyleValue"
 | 
			
		||||
        />
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
 | 
			
		||||
import ToolbarColorPicker from "@/ui/toolbar/components/toolbar-color-picker.vue";
 | 
			
		||||
export default {
 | 
			
		||||
    components: {
 | 
			
		||||
        ToolbarColorPicker
 | 
			
		||||
    },
 | 
			
		||||
    inject: [
 | 
			
		||||
        'openmct'
 | 
			
		||||
    ],
 | 
			
		||||
    props: {
 | 
			
		||||
        conditionStyle: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            condition: null
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        borderColorOption() {
 | 
			
		||||
            return {
 | 
			
		||||
                icon: 'icon-line-horz',
 | 
			
		||||
                title: 'Set border color',
 | 
			
		||||
                value: this.conditionStyle.style.border.replace('1px solid ', ''),
 | 
			
		||||
                property: 'border'
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        backgroundColorOption() {
 | 
			
		||||
            return {
 | 
			
		||||
                icon: 'icon-paint-bucket',
 | 
			
		||||
                title: 'Set background color',
 | 
			
		||||
                value: this.conditionStyle.style.backgroundColor,
 | 
			
		||||
                property: 'backgroundColor'
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        updateStyleValue(value, item) {
 | 
			
		||||
            if (item.property === 'border') {
 | 
			
		||||
                value = '1px solid ' + value;
 | 
			
		||||
            }
 | 
			
		||||
            this.conditionStyle.style[item.property] = value;
 | 
			
		||||
            this.$emit('persist', this.conditionStyle)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
@@ -1,164 +1,457 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* 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.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
    <div v-if="!conditionalStyles.length"
 | 
			
		||||
         class="holder c-c-button-wrapper align-left"
 | 
			
		||||
    >
 | 
			
		||||
        <button
 | 
			
		||||
            class="c-c-button c-c-button--minor add-criteria-button"
 | 
			
		||||
            @click="addConditionSet"
 | 
			
		||||
        >
 | 
			
		||||
            <span class="c-c-button__label">Use conditional styling</span>
 | 
			
		||||
        </button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-else>
 | 
			
		||||
        <div class="holder c-c-button-wrapper align-left">
 | 
			
		||||
            <button
 | 
			
		||||
                class="c-c-button c-c-button--minor add-criteria-button"
 | 
			
		||||
                @click="removeConditionSet"
 | 
			
		||||
<div class="c-inspector__styles c-inspect-styles">
 | 
			
		||||
    <template v-if="!conditionSetDomainObject">
 | 
			
		||||
        <div class="c-inspect-styles__header">
 | 
			
		||||
            Object Style
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="c-inspect-styles__content">
 | 
			
		||||
            <div v-if="staticStyle"
 | 
			
		||||
                 class="c-inspect-styles__style"
 | 
			
		||||
            >
 | 
			
		||||
                <span class="c-c-button__label">Remove conditional styling</span>
 | 
			
		||||
                <style-editor class="c-inspect-styles__editor"
 | 
			
		||||
                              :style-item="staticStyle"
 | 
			
		||||
                              :is-editing="isEditing"
 | 
			
		||||
                              @persist="updateStaticStyle"
 | 
			
		||||
                />
 | 
			
		||||
            </div>
 | 
			
		||||
            <button
 | 
			
		||||
                id="addConditionSet"
 | 
			
		||||
                class="c-button c-button--major c-toggle-styling-button labeled"
 | 
			
		||||
                @click="addConditionSet"
 | 
			
		||||
            >
 | 
			
		||||
                <span class="c-cs-button__label">Use Conditional Styling...</span>
 | 
			
		||||
            </button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <ul>
 | 
			
		||||
            <li v-for="conditionStyle in conditionalStyles"
 | 
			
		||||
                :key="conditionStyle.conditionId"
 | 
			
		||||
    </template>
 | 
			
		||||
    <template v-else>
 | 
			
		||||
        <div class="c-inspect-styles__header">
 | 
			
		||||
            Conditional Object Styles
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="c-inspect-styles__content c-inspect-styles__condition-set">
 | 
			
		||||
            <a v-if="conditionSetDomainObject"
 | 
			
		||||
               class="c-object-label icon-conditional"
 | 
			
		||||
               :href="navigateToPath"
 | 
			
		||||
               @click="navigateOrPreview"
 | 
			
		||||
            >
 | 
			
		||||
                <conditional-style :condition-style="conditionStyle"
 | 
			
		||||
                                   @persist="updateConditionalStyle"
 | 
			
		||||
                <span class="c-object-label__name">{{ conditionSetDomainObject.name }}</span>
 | 
			
		||||
            </a>
 | 
			
		||||
            <template v-if="isEditing">
 | 
			
		||||
                <button
 | 
			
		||||
                    id="changeConditionSet"
 | 
			
		||||
                    class="c-button labeled"
 | 
			
		||||
                    @click="addConditionSet"
 | 
			
		||||
                >
 | 
			
		||||
                    <span class="c-button__label">Change...</span>
 | 
			
		||||
                </button>
 | 
			
		||||
 | 
			
		||||
                <button class="c-click-icon icon-x"
 | 
			
		||||
                        title="Remove conditional styles"
 | 
			
		||||
                        @click="removeConditionSet"
 | 
			
		||||
                ></button>
 | 
			
		||||
            </template>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div v-if="conditionsLoaded"
 | 
			
		||||
             class="c-inspect-styles__conditions"
 | 
			
		||||
        >
 | 
			
		||||
            <div v-for="(conditionStyle, index) in conditionalStyles"
 | 
			
		||||
                 :key="index"
 | 
			
		||||
                 class="c-inspect-styles__condition"
 | 
			
		||||
                 :class="{'is-current': conditionStyle.conditionId === selectedConditionId}"
 | 
			
		||||
                 @click="applySelectedConditionStyle(conditionStyle.conditionId)"
 | 
			
		||||
            >
 | 
			
		||||
                <condition-error :show-label="true"
 | 
			
		||||
                                 :condition="getCondition(conditionStyle.conditionId)"
 | 
			
		||||
                />
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
                <condition-description :show-label="true"
 | 
			
		||||
                                       :condition="getCondition(conditionStyle.conditionId)"
 | 
			
		||||
                />
 | 
			
		||||
                <style-editor class="c-inspect-styles__editor"
 | 
			
		||||
                              :style-item="conditionStyle"
 | 
			
		||||
                              :is-editing="isEditing"
 | 
			
		||||
                              @persist="updateConditionalStyle"
 | 
			
		||||
                />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </template>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
 | 
			
		||||
import ConditionalStyle from "./ConditionalStyle.vue";
 | 
			
		||||
import StyleEditor from "./StyleEditor.vue";
 | 
			
		||||
import ConditionSetSelectorDialog from "./ConditionSetSelectorDialog.vue";
 | 
			
		||||
import ConditionDescription from "@/plugins/condition/components/ConditionDescription.vue";
 | 
			
		||||
import ConditionError from "@/plugins/condition/components/ConditionError.vue";
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
 | 
			
		||||
import {getApplicableStylesForItem} from "@/plugins/condition/utils/styleUtils";
 | 
			
		||||
import isEmpty from 'lodash/isEmpty';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    name: 'ConditionalStylesView',
 | 
			
		||||
    components: {
 | 
			
		||||
        ConditionalStyle
 | 
			
		||||
        ConditionDescription,
 | 
			
		||||
        ConditionError,
 | 
			
		||||
        StyleEditor
 | 
			
		||||
    },
 | 
			
		||||
    inject: [
 | 
			
		||||
        'openmct',
 | 
			
		||||
        'domainObject'
 | 
			
		||||
        'selection'
 | 
			
		||||
    ],
 | 
			
		||||
    props: {
 | 
			
		||||
        itemId: {
 | 
			
		||||
            type: String,
 | 
			
		||||
            default: ''
 | 
			
		||||
        },
 | 
			
		||||
        initialStyles: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            default() {
 | 
			
		||||
                return undefined;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            conditionalStyles: []
 | 
			
		||||
            conditionalStyles: [],
 | 
			
		||||
            staticStyle: undefined,
 | 
			
		||||
            conditionSetDomainObject: undefined,
 | 
			
		||||
            isEditing: this.openmct.editor.isEditing(),
 | 
			
		||||
            conditions: undefined,
 | 
			
		||||
            conditionsLoaded: false,
 | 
			
		||||
            navigateToPath: '',
 | 
			
		||||
            selectedConditionId: ''
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
        this.removeListeners();
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        if (this.domainObject.configuration && this.domainObject.configuration.conditionalStyle) {
 | 
			
		||||
            if (this.itemId) {
 | 
			
		||||
                let conditionalStyle = this.domainObject.configuration.conditionalStyle[this.itemId];
 | 
			
		||||
                if (conditionalStyle) {
 | 
			
		||||
                    this.conditionalStyles = conditionalStyle.styles || [];
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                this.conditionalStyles = this.domainObject.configuration.conditionalStyle.styles || [];
 | 
			
		||||
        this.itemId = '';
 | 
			
		||||
        this.getDomainObjectFromSelection();
 | 
			
		||||
        this.previewAction = new PreviewAction(this.openmct);
 | 
			
		||||
        if (this.domainObject.configuration && this.domainObject.configuration.objectStyles) {
 | 
			
		||||
            let objectStyles = this.itemId ? this.domainObject.configuration.objectStyles[this.itemId] : this.domainObject.configuration.objectStyles;
 | 
			
		||||
            this.initializeStaticStyle(objectStyles);
 | 
			
		||||
            if (objectStyles && objectStyles.conditionSetIdentifier) {
 | 
			
		||||
                this.openmct.objects.get(objectStyles.conditionSetIdentifier).then(this.initialize);
 | 
			
		||||
                this.conditionalStyles = objectStyles.styles;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            this.initializeStaticStyle();
 | 
			
		||||
        }
 | 
			
		||||
        this.openmct.editor.on('isEditing', this.setEditState);
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        addConditionSet() {
 | 
			
		||||
            //TODO: this.conditionSetIdentifier will be set by the UI before calling this
 | 
			
		||||
            this.conditionSetIdentifier = {
 | 
			
		||||
                namespace: '',
 | 
			
		||||
                key: "81088c8a-4b80-41fe-9d07-fda8b22d6f5f"
 | 
			
		||||
            };
 | 
			
		||||
            this.initializeConditionalStyles();
 | 
			
		||||
        isItemType(type, item) {
 | 
			
		||||
            return item && (item.type === type);
 | 
			
		||||
        },
 | 
			
		||||
        removeConditionSet() {
 | 
			
		||||
            //TODO: Handle the case where domainObject has items with styles but we're trying to remove the styles on the domainObject itself
 | 
			
		||||
            this.conditionSetIdentifier = '';
 | 
			
		||||
            this.conditionalStyles = [];
 | 
			
		||||
            let domainObjectConditionalStyle =  (this.domainObject.configuration && this.domainObject.configuration.conditionalStyle) || {};
 | 
			
		||||
            if (domainObjectConditionalStyle) {
 | 
			
		||||
                if (this.itemId) {
 | 
			
		||||
                    domainObjectConditionalStyle[this.itemId] = undefined;
 | 
			
		||||
                    delete domainObjectConditionalStyle[this.itemId];
 | 
			
		||||
        getDomainObjectFromSelection() {
 | 
			
		||||
            let layoutItem;
 | 
			
		||||
            let domainObject;
 | 
			
		||||
 | 
			
		||||
            if (this.selection[0].length > 1) {
 | 
			
		||||
                //If there are more than 1 items in the this.selection[0] list, the first one could either be a sub domain object OR a layout drawing control.
 | 
			
		||||
                //The second item in the this.selection[0] list is the container object (usually a layout)
 | 
			
		||||
                layoutItem = this.selection[0][0].context.layoutItem;
 | 
			
		||||
                const item = this.selection[0][0].context.item;
 | 
			
		||||
                this.canHide = true;
 | 
			
		||||
                if (item &&
 | 
			
		||||
                    (!layoutItem || (this.isItemType('subobject-view', layoutItem)))) {
 | 
			
		||||
                    domainObject = item;
 | 
			
		||||
                } else {
 | 
			
		||||
                    domainObjectConditionalStyle.conditionSetIdentifier = undefined;
 | 
			
		||||
                    delete domainObjectConditionalStyle.conditionSetIdentifier;
 | 
			
		||||
                    domainObjectConditionalStyle.styles = undefined;
 | 
			
		||||
                    delete domainObjectConditionalStyle.styles;
 | 
			
		||||
                }
 | 
			
		||||
                if (_.isEmpty(domainObjectConditionalStyle)) {
 | 
			
		||||
                    domainObjectConditionalStyle = undefined;
 | 
			
		||||
                    domainObject = this.selection[0][1].context.item;
 | 
			
		||||
                    if (layoutItem) {
 | 
			
		||||
                        this.itemId = layoutItem.id;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                domainObject = this.selection[0][0].context.item;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.persist(domainObjectConditionalStyle);
 | 
			
		||||
 | 
			
		||||
        },
 | 
			
		||||
        initializeConditionalStyles() {
 | 
			
		||||
            this.openmct.objects.get(this.conditionSetIdentifier).then((conditionSetDomainObject) => {
 | 
			
		||||
                conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
 | 
			
		||||
                    this.conditionalStyles.push({
 | 
			
		||||
                        conditionId: conditionConfiguration.id,
 | 
			
		||||
                        conditionName: conditionConfiguration.name,
 | 
			
		||||
                        style: Object.assign({}, this.initialStyles)
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
                let domainObjectConditionalStyle =  (this.domainObject.configuration && this.domainObject.configuration.conditionalStyle) || {};
 | 
			
		||||
                let conditionalStyle = {
 | 
			
		||||
                    conditionSetIdentifier: this.conditionSetIdentifier,
 | 
			
		||||
                    styles: this.conditionalStyles
 | 
			
		||||
                };
 | 
			
		||||
                if (this.itemId) {
 | 
			
		||||
                    this.persist({
 | 
			
		||||
                        ...domainObjectConditionalStyle,
 | 
			
		||||
                        [this.itemId]: conditionalStyle
 | 
			
		||||
                    });
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.persist({
 | 
			
		||||
                        ...domainObjectConditionalStyle,
 | 
			
		||||
                        ...conditionalStyle
 | 
			
		||||
                    });
 | 
			
		||||
            this.domainObject = domainObject;
 | 
			
		||||
            this.initialStyles = getApplicableStylesForItem(domainObject, layoutItem);
 | 
			
		||||
            this.$nextTick(() => {
 | 
			
		||||
                this.removeListeners();
 | 
			
		||||
                if (this.domainObject) {
 | 
			
		||||
                    this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
 | 
			
		||||
                    this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        removeListeners() {
 | 
			
		||||
            if (this.stopObserving) {
 | 
			
		||||
                this.stopObserving();
 | 
			
		||||
            }
 | 
			
		||||
            if (this.stopObservingItems) {
 | 
			
		||||
                this.stopObservingItems();
 | 
			
		||||
            }
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        initialize(conditionSetDomainObject) {
 | 
			
		||||
            //If there are new conditions in the conditionSet we need to set those styles to default
 | 
			
		||||
            this.conditionSetDomainObject = conditionSetDomainObject;
 | 
			
		||||
            this.enableConditionSetNav();
 | 
			
		||||
            this.initializeConditionalStyles();
 | 
			
		||||
        },
 | 
			
		||||
        setEditState(isEditing) {
 | 
			
		||||
            this.isEditing = isEditing;
 | 
			
		||||
            if (this.isEditing) {
 | 
			
		||||
                if (this.stopProvidingTelemetry) {
 | 
			
		||||
                    this.stopProvidingTelemetry();
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                this.subscribeToConditionSet();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        addConditionSet() {
 | 
			
		||||
            let conditionSetDomainObject;
 | 
			
		||||
            const handleItemSelection = (item) => {
 | 
			
		||||
                if (item) {
 | 
			
		||||
                    conditionSetDomainObject = item;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            const dismissDialog = (overlay, initialize) => {
 | 
			
		||||
                overlay.dismiss();
 | 
			
		||||
                if (initialize && conditionSetDomainObject) {
 | 
			
		||||
                    this.conditionSetDomainObject = conditionSetDomainObject;
 | 
			
		||||
                    this.conditionalStyles = [];
 | 
			
		||||
                    this.initializeConditionalStyles();
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            let vm = new Vue({
 | 
			
		||||
                provide: {
 | 
			
		||||
                    openmct: this.openmct
 | 
			
		||||
                },
 | 
			
		||||
                components: {ConditionSetSelectorDialog},
 | 
			
		||||
                data() {
 | 
			
		||||
                    return {
 | 
			
		||||
                        handleItemSelection
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                template: '<condition-set-selector-dialog @conditionSetSelected="handleItemSelection"></condition-set-selector-dialog>'
 | 
			
		||||
            }).$mount();
 | 
			
		||||
 | 
			
		||||
            let overlay = this.openmct.overlays.overlay({
 | 
			
		||||
                element: vm.$el,
 | 
			
		||||
                size: 'small',
 | 
			
		||||
                buttons: [
 | 
			
		||||
                    {
 | 
			
		||||
                        label: 'OK',
 | 
			
		||||
                        emphasis: 'true',
 | 
			
		||||
                        callback: () => dismissDialog(overlay, true)
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        label: 'Cancel',
 | 
			
		||||
                        callback: () => dismissDialog(overlay, false)
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                onDestroy: () => vm.$destroy()
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        enableConditionSetNav() {
 | 
			
		||||
            this.openmct.objects.getOriginalPath(this.conditionSetDomainObject.identifier).then(
 | 
			
		||||
                (objectPath) => {
 | 
			
		||||
                    this.objectPath = objectPath;
 | 
			
		||||
                    this.navigateToPath = '#/browse/' + this.objectPath
 | 
			
		||||
                        .map(o => o && this.openmct.objects.makeKeyString(o.identifier))
 | 
			
		||||
                        .reverse()
 | 
			
		||||
                        .join('/');
 | 
			
		||||
                }
 | 
			
		||||
            );
 | 
			
		||||
        },
 | 
			
		||||
        navigateOrPreview(event) {
 | 
			
		||||
            // If editing, display condition set in Preview overlay; otherwise nav to it while browsing
 | 
			
		||||
            if (this.openmct.editor.isEditing()) {
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
                this.previewAction.invoke(this.objectPath);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        removeConditionSet() {
 | 
			
		||||
            this.conditionSetDomainObject = undefined;
 | 
			
		||||
            this.conditionalStyles = [];
 | 
			
		||||
            let domainObjectStyles =  (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
 | 
			
		||||
            if (this.itemId) {
 | 
			
		||||
                domainObjectStyles[this.itemId].conditionSetIdentifier = undefined;
 | 
			
		||||
                domainObjectStyles[this.itemId].selectedConditionId = undefined;
 | 
			
		||||
                domainObjectStyles[this.itemId].defaultConditionId = undefined;
 | 
			
		||||
                delete domainObjectStyles[this.itemId].conditionSetIdentifier;
 | 
			
		||||
                domainObjectStyles[this.itemId].styles = undefined;
 | 
			
		||||
                delete domainObjectStyles[this.itemId].styles;
 | 
			
		||||
                if (isEmpty(domainObjectStyles[this.itemId])) {
 | 
			
		||||
                    delete domainObjectStyles[this.itemId];
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                domainObjectStyles.conditionSetIdentifier = undefined;
 | 
			
		||||
                domainObjectStyles.selectedConditionId = undefined;
 | 
			
		||||
                domainObjectStyles.defaultConditionId = undefined;
 | 
			
		||||
                delete domainObjectStyles.conditionSetIdentifier;
 | 
			
		||||
                domainObjectStyles.styles = undefined;
 | 
			
		||||
                delete domainObjectStyles.styles;
 | 
			
		||||
            }
 | 
			
		||||
            if (isEmpty(domainObjectStyles)) {
 | 
			
		||||
                domainObjectStyles = undefined;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.persist(domainObjectStyles);
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        updateDomainObjectItemStyles(newItems) {
 | 
			
		||||
            //check that all items that have been styles still exist. Otherwise delete those styles
 | 
			
		||||
            let domainObjectStyles =  (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
 | 
			
		||||
            let itemsToRemove = [];
 | 
			
		||||
            let keys = Object.keys(domainObjectStyles);
 | 
			
		||||
            //TODO: Need an easier way to find which properties are itemIds
 | 
			
		||||
            keys.forEach((key) => {
 | 
			
		||||
                const keyIsItemId = (key !== 'styles') &&
 | 
			
		||||
                    (key !== 'staticStyle') &&
 | 
			
		||||
                    (key !== 'defaultConditionId') &&
 | 
			
		||||
                    (key !== 'selectedConditionId') &&
 | 
			
		||||
                    (key !== 'conditionSetIdentifier');
 | 
			
		||||
                if (keyIsItemId) {
 | 
			
		||||
                    if (!(newItems.find(item => item.id === key))) {
 | 
			
		||||
                        itemsToRemove.push(key);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            if (itemsToRemove.length) {
 | 
			
		||||
                this.removeItemStyles(itemsToRemove, domainObjectStyles);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        removeItemStyles(itemIds, domainObjectStyles) {
 | 
			
		||||
            itemIds.forEach(itemId => {
 | 
			
		||||
                if (domainObjectStyles[itemId]) {
 | 
			
		||||
                    domainObjectStyles[itemId] = undefined;
 | 
			
		||||
                    delete domainObjectStyles[this.itemId];
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            if (isEmpty(domainObjectStyles)) {
 | 
			
		||||
                domainObjectStyles = undefined;
 | 
			
		||||
            }
 | 
			
		||||
            this.persist(domainObjectStyles);
 | 
			
		||||
        },
 | 
			
		||||
        initializeConditionalStyles() {
 | 
			
		||||
            if (!this.conditions) {
 | 
			
		||||
                this.conditions = {};
 | 
			
		||||
            }
 | 
			
		||||
            let conditionalStyles = [];
 | 
			
		||||
            this.conditionSetDomainObject.configuration.conditionCollection.forEach((conditionConfiguration, index) => {
 | 
			
		||||
                if (conditionConfiguration.isDefault) {
 | 
			
		||||
                    this.selectedConditionId = conditionConfiguration.id;
 | 
			
		||||
                }
 | 
			
		||||
                this.conditions[conditionConfiguration.id] = conditionConfiguration;
 | 
			
		||||
                let foundStyle = this.findStyleByConditionId(conditionConfiguration.id);
 | 
			
		||||
                if (foundStyle) {
 | 
			
		||||
                    foundStyle.style = Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles, foundStyle.style);
 | 
			
		||||
                    conditionalStyles.push(foundStyle);
 | 
			
		||||
                } else {
 | 
			
		||||
                    conditionalStyles.splice(index, 0, {
 | 
			
		||||
                        conditionId: conditionConfiguration.id,
 | 
			
		||||
                        style: Object.assign((this.canHide ? { isStyleInvisible: '' } : {}), this.initialStyles)
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            //we're doing this so that we remove styles for any conditions that have been removed from the condition set
 | 
			
		||||
            this.conditionalStyles = conditionalStyles;
 | 
			
		||||
            this.conditionsLoaded = true;
 | 
			
		||||
            this.persist(this.getDomainObjectConditionalStyle(this.selectedConditionId));
 | 
			
		||||
            if (!this.isEditing) {
 | 
			
		||||
                this.subscribeToConditionSet();
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        subscribeToConditionSet() {
 | 
			
		||||
            if (this.stopProvidingTelemetry) {
 | 
			
		||||
                this.stopProvidingTelemetry();
 | 
			
		||||
            }
 | 
			
		||||
            if (this.conditionSetDomainObject) {
 | 
			
		||||
                this.openmct.telemetry.request(this.conditionSetDomainObject)
 | 
			
		||||
                    .then(output => {
 | 
			
		||||
                        if (output && output.length) {
 | 
			
		||||
                            this.handleConditionSetResultUpdated(output[0]);
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                this.stopProvidingTelemetry = this.openmct.telemetry.subscribe(this.conditionSetDomainObject, this.handleConditionSetResultUpdated.bind(this));
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        handleConditionSetResultUpdated(resultData) {
 | 
			
		||||
            this.selectedConditionId = resultData ? resultData.conditionId : '';
 | 
			
		||||
        },
 | 
			
		||||
        initializeStaticStyle(objectStyles) {
 | 
			
		||||
            let staticStyle = objectStyles && objectStyles.staticStyle;
 | 
			
		||||
            if (staticStyle) {
 | 
			
		||||
                this.staticStyle = {
 | 
			
		||||
                    style: Object.assign({}, this.initialStyles, staticStyle.style)
 | 
			
		||||
                };
 | 
			
		||||
            } else {
 | 
			
		||||
                this.staticStyle = {
 | 
			
		||||
                    style: Object.assign({}, this.initialStyles)
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        findStyleByConditionId(id) {
 | 
			
		||||
            return this.conditionalStyles.find(conditionalStyle => conditionalStyle.conditionId === id);
 | 
			
		||||
        },
 | 
			
		||||
        updateStaticStyle(staticStyle) {
 | 
			
		||||
            this.staticStyle = staticStyle;
 | 
			
		||||
            this.persist(this.getDomainObjectConditionalStyle());
 | 
			
		||||
        },
 | 
			
		||||
        updateConditionalStyle(conditionStyle) {
 | 
			
		||||
            let found = this.findStyleByConditionId(conditionStyle.conditionId);
 | 
			
		||||
            if (found) {
 | 
			
		||||
                found.style = conditionStyle.style;
 | 
			
		||||
                let domainObjectConditionalStyle =  this.domainObject.configuration.conditionalStyle || {};
 | 
			
		||||
 | 
			
		||||
                if (this.itemId) {
 | 
			
		||||
                    let itemConditionalStyle = domainObjectConditionalStyle[this.itemId];
 | 
			
		||||
                    if (itemConditionalStyle) {
 | 
			
		||||
                        this.persist({
 | 
			
		||||
                            ...domainObjectConditionalStyle,
 | 
			
		||||
                            [this.itemId]: {
 | 
			
		||||
                                ...itemConditionalStyle,
 | 
			
		||||
                                styles: this.conditionalStyles
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    domainObjectConditionalStyle.styles = this.conditionalStyles;
 | 
			
		||||
                    this.persist(domainObjectConditionalStyle);
 | 
			
		||||
                }
 | 
			
		||||
                this.selectedConditionId = found.conditionId;
 | 
			
		||||
                this.persist(this.getDomainObjectConditionalStyle());
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        persist(conditionalStyle) {
 | 
			
		||||
            this.openmct.objects.mutate(this.domainObject, 'configuration.conditionalStyle', conditionalStyle);
 | 
			
		||||
        getDomainObjectConditionalStyle(defaultConditionId) {
 | 
			
		||||
            let objectStyle = {
 | 
			
		||||
                styles: this.conditionalStyles,
 | 
			
		||||
                staticStyle: this.staticStyle,
 | 
			
		||||
                selectedConditionId: this.selectedConditionId
 | 
			
		||||
            };
 | 
			
		||||
            if (defaultConditionId) {
 | 
			
		||||
                objectStyle.defaultConditionId = defaultConditionId;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.conditionSetDomainObject) {
 | 
			
		||||
                objectStyle.conditionSetIdentifier = this.conditionSetDomainObject.identifier;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let domainObjectStyles =  (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
 | 
			
		||||
 | 
			
		||||
            if (this.itemId) {
 | 
			
		||||
                domainObjectStyles[this.itemId] = objectStyle;
 | 
			
		||||
            } else {
 | 
			
		||||
                //we're deconstructing here to ensure that if an item within a domainObject already had a style we don't lose it
 | 
			
		||||
                domainObjectStyles = {
 | 
			
		||||
                    ...domainObjectStyles,
 | 
			
		||||
                    ...objectStyle
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return domainObjectStyles;
 | 
			
		||||
        },
 | 
			
		||||
        getCondition(id) {
 | 
			
		||||
            return this.conditions ? this.conditions[id] : {};
 | 
			
		||||
        },
 | 
			
		||||
        applySelectedConditionStyle(conditionId) {
 | 
			
		||||
            this.selectedConditionId = conditionId;
 | 
			
		||||
            this.persist(this.getDomainObjectConditionalStyle());
 | 
			
		||||
        },
 | 
			
		||||
        persist(style) {
 | 
			
		||||
            this.openmct.objects.mutate(this.domainObject, 'configuration.objectStyles', style);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,270 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* 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.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div class="c-inspector__styles c-inspect-styles">
 | 
			
		||||
    <div class="c-inspect-styles__header">
 | 
			
		||||
        Object Style
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="c-inspect-styles__content">
 | 
			
		||||
        <div v-if="isStaticAndConditionalStyles"
 | 
			
		||||
             class="c-inspect-styles__mixed-static-and-conditional u-alert u-alert--block u-alert--with-icon"
 | 
			
		||||
        >
 | 
			
		||||
            Your selection includes one or more items that use Conditional Styling. Applying a static style below will replace any Conditional Styling with the new choice.
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-if="staticStyle"
 | 
			
		||||
             class="c-inspect-styles__style"
 | 
			
		||||
        >
 | 
			
		||||
            <style-editor class="c-inspect-styles__editor"
 | 
			
		||||
                          :style-item="staticStyle"
 | 
			
		||||
                          :is-editing="isEditing"
 | 
			
		||||
                          :mixed-styles="mixedStyles"
 | 
			
		||||
                          @persist="updateStaticStyle"
 | 
			
		||||
            />
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
 | 
			
		||||
import StyleEditor from "./StyleEditor.vue";
 | 
			
		||||
import PreviewAction from "@/ui/preview/PreviewAction.js";
 | 
			
		||||
import { getApplicableStylesForItem, getConsolidatedStyleValues, getConditionalStyleForItem } from "@/plugins/condition/utils/styleUtils";
 | 
			
		||||
import isEmpty from 'lodash/isEmpty';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    name: 'MultiSelectStylesView',
 | 
			
		||||
    components: {
 | 
			
		||||
        StyleEditor
 | 
			
		||||
    },
 | 
			
		||||
    inject: [
 | 
			
		||||
        'openmct',
 | 
			
		||||
        'selection'
 | 
			
		||||
    ],
 | 
			
		||||
    data() {
 | 
			
		||||
        return {
 | 
			
		||||
            staticStyle: undefined,
 | 
			
		||||
            isEditing: this.openmct.editor.isEditing(),
 | 
			
		||||
            mixedStyles: [],
 | 
			
		||||
            isStaticAndConditionalStyles: false
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    destroyed() {
 | 
			
		||||
        this.removeListeners();
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.items = [];
 | 
			
		||||
        this.previewAction = new PreviewAction(this.openmct);
 | 
			
		||||
        this.getObjectsAndItemsFromSelection();
 | 
			
		||||
        this.initializeStaticStyle();
 | 
			
		||||
        this.openmct.editor.on('isEditing', this.setEditState);
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        isItemType(type, item) {
 | 
			
		||||
            return item && (item.type === type);
 | 
			
		||||
        },
 | 
			
		||||
        hasConditionalStyles(domainObject, id) {
 | 
			
		||||
            return getConditionalStyleForItem(domainObject, id) !== undefined;
 | 
			
		||||
        },
 | 
			
		||||
        getObjectsAndItemsFromSelection() {
 | 
			
		||||
            let domainObject;
 | 
			
		||||
            let subObjects = [];
 | 
			
		||||
 | 
			
		||||
            //multiple selection
 | 
			
		||||
            let itemInitialStyles = [];
 | 
			
		||||
            let itemStyle;
 | 
			
		||||
            this.selection.forEach((selectionItem) => {
 | 
			
		||||
                const item = selectionItem[0].context.item;
 | 
			
		||||
                const layoutItem = selectionItem[0].context.layoutItem;
 | 
			
		||||
                if (item && this.isItemType('subobject-view', layoutItem)) {
 | 
			
		||||
                    subObjects.push(item);
 | 
			
		||||
                    itemStyle = getApplicableStylesForItem(item);
 | 
			
		||||
                    if (!this.isStaticAndConditionalStyles) {
 | 
			
		||||
                        this.isStaticAndConditionalStyles = this.hasConditionalStyles(item);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    domainObject = selectionItem[1].context.item;
 | 
			
		||||
                    itemStyle = getApplicableStylesForItem(domainObject, layoutItem || item);
 | 
			
		||||
                    this.items.push({
 | 
			
		||||
                        id: layoutItem.id,
 | 
			
		||||
                        applicableStyles: itemStyle
 | 
			
		||||
                    });
 | 
			
		||||
                    if (!this.isStaticAndConditionalStyles) {
 | 
			
		||||
                        this.isStaticAndConditionalStyles = this.hasConditionalStyles(domainObject, layoutItem.id);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                itemInitialStyles.push(itemStyle);
 | 
			
		||||
            });
 | 
			
		||||
            const {styles, mixedStyles} = getConsolidatedStyleValues(itemInitialStyles);
 | 
			
		||||
            this.initialStyles = styles;
 | 
			
		||||
            this.mixedStyles = mixedStyles;
 | 
			
		||||
 | 
			
		||||
            this.domainObject = domainObject;
 | 
			
		||||
            this.removeListeners();
 | 
			
		||||
            if (this.domainObject) {
 | 
			
		||||
                this.stopObserving = this.openmct.objects.observe(this.domainObject, '*', newDomainObject => this.domainObject = newDomainObject);
 | 
			
		||||
                this.stopObservingItems = this.openmct.objects.observe(this.domainObject, 'configuration.items', this.updateDomainObjectItemStyles);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            subObjects.forEach(this.registerListener);
 | 
			
		||||
        },
 | 
			
		||||
        updateDomainObjectItemStyles(newItems) {
 | 
			
		||||
            //check that all items that have been styles still exist. Otherwise delete those styles
 | 
			
		||||
            let keys = Object.keys(this.domainObject.configuration.objectStyles || {});
 | 
			
		||||
            keys.forEach((key) => {
 | 
			
		||||
                if ((key !== 'styles') &&
 | 
			
		||||
                    (key !== 'staticStyle') &&
 | 
			
		||||
                    (key !== 'conditionSetIdentifier')) {
 | 
			
		||||
                    if (!(newItems.find(item => item.id === key))) {
 | 
			
		||||
                        this.removeItemStyles(key);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        },
 | 
			
		||||
        registerListener(domainObject) {
 | 
			
		||||
            let id = this.openmct.objects.makeKeyString(domainObject.identifier);
 | 
			
		||||
 | 
			
		||||
            if (!this.domainObjectsById) {
 | 
			
		||||
                this.domainObjectsById = {};
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!this.domainObjectsById[id]) {
 | 
			
		||||
                this.domainObjectsById[id] = domainObject;
 | 
			
		||||
                this.observeObject(domainObject, id);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        observeObject(domainObject, id) {
 | 
			
		||||
            let unobserveObject = this.openmct.objects.observe(domainObject, '*', function (newObject) {
 | 
			
		||||
                this.domainObjectsById[id] = JSON.parse(JSON.stringify(newObject));
 | 
			
		||||
            }.bind(this));
 | 
			
		||||
            this.unObserveObjects.push(unobserveObject);
 | 
			
		||||
        },
 | 
			
		||||
        removeListeners() {
 | 
			
		||||
            if (this.stopObserving) {
 | 
			
		||||
                this.stopObserving();
 | 
			
		||||
            }
 | 
			
		||||
            if (this.stopObservingItems) {
 | 
			
		||||
                this.stopObservingItems();
 | 
			
		||||
            }
 | 
			
		||||
            if (this.unObserveObjects) {
 | 
			
		||||
                this.unObserveObjects.forEach((unObserveObject) => {
 | 
			
		||||
                    unObserveObject();
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            this.unObserveObjects = [];
 | 
			
		||||
        },
 | 
			
		||||
        removeItemStyles(itemId) {
 | 
			
		||||
            let domainObjectStyles =  (this.domainObject.configuration && this.domainObject.configuration.objectStyles) || {};
 | 
			
		||||
            if (itemId && domainObjectStyles[itemId]) {
 | 
			
		||||
                domainObjectStyles[itemId] = undefined;
 | 
			
		||||
                delete domainObjectStyles[this.itemId];
 | 
			
		||||
 | 
			
		||||
                if (isEmpty(domainObjectStyles)) {
 | 
			
		||||
                    domainObjectStyles = undefined;
 | 
			
		||||
                }
 | 
			
		||||
                this.persist(this.domainObject, domainObjectStyles);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        removeConditionalStyles(domainObjectStyles, itemId) {
 | 
			
		||||
            if (itemId) {
 | 
			
		||||
                domainObjectStyles[itemId].conditionSetIdentifier = undefined;
 | 
			
		||||
                delete domainObjectStyles[itemId].conditionSetIdentifier;
 | 
			
		||||
                domainObjectStyles[itemId].styles = undefined;
 | 
			
		||||
                delete domainObjectStyles[itemId].styles;
 | 
			
		||||
            } else {
 | 
			
		||||
                domainObjectStyles.conditionSetIdentifier = undefined;
 | 
			
		||||
                delete domainObjectStyles.conditionSetIdentifier;
 | 
			
		||||
                domainObjectStyles.styles = undefined;
 | 
			
		||||
                delete domainObjectStyles.styles;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        setEditState(isEditing) {
 | 
			
		||||
            this.isEditing = isEditing;
 | 
			
		||||
        },
 | 
			
		||||
        initializeStaticStyle() {
 | 
			
		||||
            this.staticStyle = {
 | 
			
		||||
                style: Object.assign({}, this.initialStyles)
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        updateStaticStyle(staticStyle, property) {
 | 
			
		||||
            //update the static style for each of the layoutItems as well as each sub object item
 | 
			
		||||
            this.staticStyle = staticStyle;
 | 
			
		||||
            this.persist(this.domainObject, this.getDomainObjectStyle(this.domainObject, property, this.items));
 | 
			
		||||
            if (this.domainObjectsById) {
 | 
			
		||||
                const keys = Object.keys(this.domainObjectsById);
 | 
			
		||||
                keys.forEach(key => {
 | 
			
		||||
                    let domainObject = this.domainObjectsById[key];
 | 
			
		||||
                    this.persist(domainObject, this.getDomainObjectStyle(domainObject, property));
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            this.isStaticAndConditionalStyles = false;
 | 
			
		||||
            let foundIndex = this.mixedStyles.indexOf(property);
 | 
			
		||||
            if (foundIndex > -1) {
 | 
			
		||||
                this.mixedStyles.splice(foundIndex, 1);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        getDomainObjectStyle(domainObject, property, items) {
 | 
			
		||||
            let domainObjectStyles =  (domainObject.configuration && domainObject.configuration.objectStyles) || {};
 | 
			
		||||
 | 
			
		||||
            if (items) {
 | 
			
		||||
                items.forEach(item => {
 | 
			
		||||
                    let itemStaticStyle = {};
 | 
			
		||||
                    if (domainObjectStyles[item.id] && domainObjectStyles[item.id].staticStyle) {
 | 
			
		||||
                        itemStaticStyle = domainObjectStyles[item.id].staticStyle.style;
 | 
			
		||||
                    }
 | 
			
		||||
                    Object.keys(item.applicableStyles).forEach(key => {
 | 
			
		||||
                        if (property === key) {
 | 
			
		||||
                            itemStaticStyle[key] = this.staticStyle.style[key];
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                    if (this.isStaticAndConditionalStyles) {
 | 
			
		||||
                        this.removeConditionalStyles(domainObjectStyles, item.id);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (isEmpty(itemStaticStyle)) {
 | 
			
		||||
                        itemStaticStyle = undefined;
 | 
			
		||||
                        domainObjectStyles[item.id] = undefined;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        domainObjectStyles[item.id] = Object.assign({}, { staticStyle: { style: itemStaticStyle } });
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            } else {
 | 
			
		||||
                if (!domainObjectStyles.staticStyle) {
 | 
			
		||||
                    domainObjectStyles.staticStyle = {
 | 
			
		||||
                        style: {}
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (this.isStaticAndConditionalStyles) {
 | 
			
		||||
                    this.removeConditionalStyles(domainObjectStyles);
 | 
			
		||||
                }
 | 
			
		||||
                domainObjectStyles.staticStyle.style[property] = this.staticStyle.style[property];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return domainObjectStyles;
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        persist(domainObject, style) {
 | 
			
		||||
            this.openmct.objects.mutate(domainObject, 'configuration.objectStyles', style);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										215
									
								
								src/plugins/condition/components/inspector/StyleEditor.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								src/plugins/condition/components/inspector/StyleEditor.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,215 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* 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.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<div class="c-style">
 | 
			
		||||
    <span :class="[
 | 
			
		||||
              { 'is-style-invisible': styleItem.style.isStyleInvisible },
 | 
			
		||||
              { 'c-style-thumb--mixed': mixedStyles.indexOf('backgroundColor') > -1 }
 | 
			
		||||
          ]"
 | 
			
		||||
          :style="[styleItem.style.imageUrl ? { backgroundImage:'url(' + styleItem.style.imageUrl + ')'} : itemStyle ]"
 | 
			
		||||
          class="c-style-thumb"
 | 
			
		||||
    >
 | 
			
		||||
        <span class="c-style-thumb__text"
 | 
			
		||||
              :class="{ 'hide-nice': !hasProperty(styleItem.style.color) }"
 | 
			
		||||
        >
 | 
			
		||||
            ABC
 | 
			
		||||
        </span>
 | 
			
		||||
    </span>
 | 
			
		||||
    <span class="c-toolbar">
 | 
			
		||||
        <toolbar-color-picker v-if="hasProperty(styleItem.style.border)"
 | 
			
		||||
                              class="c-style__toolbar-button--border-color u-menu-to--center"
 | 
			
		||||
                              :options="borderColorOption"
 | 
			
		||||
                              @change="updateStyleValue"
 | 
			
		||||
        />
 | 
			
		||||
        <toolbar-color-picker v-if="hasProperty(styleItem.style.backgroundColor)"
 | 
			
		||||
                              class="c-style__toolbar-button--background-color u-menu-to--center"
 | 
			
		||||
                              :options="backgroundColorOption"
 | 
			
		||||
                              @change="updateStyleValue"
 | 
			
		||||
        />
 | 
			
		||||
        <toolbar-color-picker v-if="hasProperty(styleItem.style.color)"
 | 
			
		||||
                              class="c-style__toolbar-button--color u-menu-to--center"
 | 
			
		||||
                              :options="colorOption"
 | 
			
		||||
                              @change="updateStyleValue"
 | 
			
		||||
        />
 | 
			
		||||
        <toolbar-button v-if="hasProperty(styleItem.style.imageUrl)"
 | 
			
		||||
                        class="c-style__toolbar-button--image-url"
 | 
			
		||||
                        :options="imageUrlOption"
 | 
			
		||||
                        @change="updateStyleValue"
 | 
			
		||||
        />
 | 
			
		||||
        <toolbar-toggle-button v-if="hasProperty(styleItem.style.isStyleInvisible)"
 | 
			
		||||
                               class="c-style__toolbar-button--toggle-visible"
 | 
			
		||||
                               :options="isStyleInvisibleOption"
 | 
			
		||||
                               @change="updateStyleValue"
 | 
			
		||||
        />
 | 
			
		||||
    </span>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
 | 
			
		||||
import ToolbarColorPicker from "@/ui/toolbar/components/toolbar-color-picker.vue";
 | 
			
		||||
import ToolbarButton from "@/ui/toolbar/components/toolbar-button.vue";
 | 
			
		||||
import ToolbarToggleButton from "@/ui/toolbar/components/toolbar-toggle-button.vue";
 | 
			
		||||
import {STYLE_CONSTANTS} from "@/plugins/condition/utils/constants";
 | 
			
		||||
import {getStylesWithoutNoneValue} from "@/plugins/condition/utils/styleUtils";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    name: 'StyleEditor',
 | 
			
		||||
    components: {
 | 
			
		||||
        ToolbarButton,
 | 
			
		||||
        ToolbarColorPicker,
 | 
			
		||||
        ToolbarToggleButton
 | 
			
		||||
    },
 | 
			
		||||
    inject: [
 | 
			
		||||
        'openmct'
 | 
			
		||||
    ],
 | 
			
		||||
    props: {
 | 
			
		||||
        isEditing: {
 | 
			
		||||
            type: Boolean
 | 
			
		||||
        },
 | 
			
		||||
        mixedStyles: {
 | 
			
		||||
            type: Array,
 | 
			
		||||
            default() {
 | 
			
		||||
                return [];
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        styleItem: {
 | 
			
		||||
            type: Object,
 | 
			
		||||
            required: true
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        itemStyle() {
 | 
			
		||||
            return getStylesWithoutNoneValue(this.styleItem.style);
 | 
			
		||||
        },
 | 
			
		||||
        borderColorOption() {
 | 
			
		||||
            let value = this.styleItem.style.border.replace('1px solid ', '');
 | 
			
		||||
            return {
 | 
			
		||||
                icon: 'icon-line-horz',
 | 
			
		||||
                title: STYLE_CONSTANTS.borderColorTitle,
 | 
			
		||||
                value: this.normalizeValueForSwatch(value),
 | 
			
		||||
                property: 'border',
 | 
			
		||||
                isEditing: this.isEditing,
 | 
			
		||||
                nonSpecific: this.mixedStyles.indexOf('border') > -1
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        backgroundColorOption() {
 | 
			
		||||
            let value = this.styleItem.style.backgroundColor;
 | 
			
		||||
            return {
 | 
			
		||||
                icon: 'icon-paint-bucket',
 | 
			
		||||
                title: STYLE_CONSTANTS.backgroundColorTitle,
 | 
			
		||||
                value: this.normalizeValueForSwatch(value),
 | 
			
		||||
                property: 'backgroundColor',
 | 
			
		||||
                isEditing: this.isEditing,
 | 
			
		||||
                nonSpecific: this.mixedStyles.indexOf('backgroundColor') > -1
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        colorOption() {
 | 
			
		||||
            let value = this.styleItem.style.color;
 | 
			
		||||
            return {
 | 
			
		||||
                icon: 'icon-font',
 | 
			
		||||
                title: STYLE_CONSTANTS.textColorTitle,
 | 
			
		||||
                value: this.normalizeValueForSwatch(value),
 | 
			
		||||
                property: 'color',
 | 
			
		||||
                isEditing: this.isEditing,
 | 
			
		||||
                nonSpecific: this.mixedStyles.indexOf('color') > -1
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        imageUrlOption() {
 | 
			
		||||
            return {
 | 
			
		||||
                icon: 'icon-image',
 | 
			
		||||
                title: STYLE_CONSTANTS.imagePropertiesTitle,
 | 
			
		||||
                dialog: {
 | 
			
		||||
                    name: "Image Properties",
 | 
			
		||||
                    sections: [
 | 
			
		||||
                        {
 | 
			
		||||
                            rows: [
 | 
			
		||||
                                {
 | 
			
		||||
                                    key: "url",
 | 
			
		||||
                                    control: "textfield",
 | 
			
		||||
                                    name: "Image URL",
 | 
			
		||||
                                    "cssClass": "l-input-lg"
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
                },
 | 
			
		||||
                property: 'imageUrl',
 | 
			
		||||
                formKeys: ['url'],
 | 
			
		||||
                value: {url: this.styleItem.style.imageUrl},
 | 
			
		||||
                isEditing: this.isEditing,
 | 
			
		||||
                nonSpecific: this.mixedStyles.indexOf('imageUrl') > -1
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        isStyleInvisibleOption() {
 | 
			
		||||
            return {
 | 
			
		||||
                value: this.styleItem.style.isStyleInvisible,
 | 
			
		||||
                property: 'isStyleInvisible',
 | 
			
		||||
                isEditing: this.isEditing,
 | 
			
		||||
                options: [
 | 
			
		||||
                    {
 | 
			
		||||
                        value: '',
 | 
			
		||||
                        icon: 'icon-eye-disabled',
 | 
			
		||||
                        title: STYLE_CONSTANTS.visibilityHidden
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        value: STYLE_CONSTANTS.isStyleInvisible,
 | 
			
		||||
                        icon: 'icon-eye-open',
 | 
			
		||||
                        title: STYLE_CONSTANTS.visibilityVisible
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        hasProperty(property) {
 | 
			
		||||
            return property !== undefined;
 | 
			
		||||
        },
 | 
			
		||||
        normalizeValueForSwatch(value) {
 | 
			
		||||
            if (value && value.indexOf('__no_value') > -1) {
 | 
			
		||||
                return value.replace('__no_value', 'transparent');
 | 
			
		||||
            }
 | 
			
		||||
            return value;
 | 
			
		||||
        },
 | 
			
		||||
        normalizeValueForStyle(value) {
 | 
			
		||||
            if (value && value === 'transparent') {
 | 
			
		||||
                return '__no_value';
 | 
			
		||||
            }
 | 
			
		||||
            return value;
 | 
			
		||||
        },
 | 
			
		||||
        updateStyleValue(value, item) {
 | 
			
		||||
            value = this.normalizeValueForStyle(value);
 | 
			
		||||
            if (item.property === 'border') {
 | 
			
		||||
                value = '1px solid ' + value;
 | 
			
		||||
            }
 | 
			
		||||
            if (value && (value.url !== undefined)) {
 | 
			
		||||
                this.styleItem.style[item.property] = value.url;
 | 
			
		||||
            } else {
 | 
			
		||||
                this.styleItem.style[item.property] = value;
 | 
			
		||||
            }
 | 
			
		||||
            this.$emit('persist', this.styleItem, item.property);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
@@ -0,0 +1,149 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
/********************************************* INSPECTOR STYLES TAB */
 | 
			
		||||
.c-inspect-styles {
 | 
			
		||||
    > * + * {
 | 
			
		||||
        margin-top: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__content,
 | 
			
		||||
    &__conditions,
 | 
			
		||||
    &__condition {
 | 
			
		||||
        > * + * {
 | 
			
		||||
            margin-top: $interiorMargin;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__content {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__condition-set {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: row;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
 | 
			
		||||
        .c-object-label {
 | 
			
		||||
            flex: 1 1 auto;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .c-button {
 | 
			
		||||
            flex: 0 0 auto;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__style,
 | 
			
		||||
    &__condition {
 | 
			
		||||
        padding: $interiorMargin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &__condition {
 | 
			
		||||
        @include discreteItem();
 | 
			
		||||
        border: 1px solid transparent;
 | 
			
		||||
        pointer-events: none; // Prevent selecting when the object isn't being edited
 | 
			
		||||
 | 
			
		||||
        &.is-current {
 | 
			
		||||
            $c: $colorBodyFg;
 | 
			
		||||
            border-color: rgba($c, 0.2);
 | 
			
		||||
            background: rgba($c, 0.2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .is-editing & {
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
            pointer-events: initial;
 | 
			
		||||
            transition: $transOut;
 | 
			
		||||
 | 
			
		||||
            &:hover {
 | 
			
		||||
                background: rgba($colorBodyFg, 0.1);
 | 
			
		||||
                transition: $transIn;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            &.is-current {
 | 
			
		||||
                $c: $editUIColorBg;
 | 
			
		||||
                border-color: $c;
 | 
			
		||||
                background: rgba($c, 0.1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .c-style {
 | 
			
		||||
        padding: 2px; // Allow a bit of room for thumb box-shadow
 | 
			
		||||
 | 
			
		||||
        &__condition-desc {
 | 
			
		||||
            @include ellipsize();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-inspect-styles__style {
 | 
			
		||||
    .is-editing & {
 | 
			
		||||
        border-bottom: 1px solid $colorInteriorBorder;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.l-shell:not(.is-editing) .c-inspect-styles {
 | 
			
		||||
    .c-toolbar {
 | 
			
		||||
        // Disabled-look toolbar when not editing
 | 
			
		||||
        pointer-events: none;
 | 
			
		||||
        cursor: inherit;
 | 
			
		||||
 | 
			
		||||
        // Hide control buttons, like image URL
 | 
			
		||||
        [class*='--image-url'] {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Make buttons look disabled by knocking back icon, not swatch element
 | 
			
		||||
        .c-icon-button {
 | 
			
		||||
            &:before {
 | 
			
		||||
                opacity: $controlDisabledOpacity;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.c-toggle-styling-button {
 | 
			
		||||
    display: none;
 | 
			
		||||
 | 
			
		||||
    .is-editing & {
 | 
			
		||||
        display: block;
 | 
			
		||||
        align-self: flex-end;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.is-style-invisible {
 | 
			
		||||
    display: none !important;
 | 
			
		||||
 | 
			
		||||
    .is-editing & {
 | 
			
		||||
        display: block !important;
 | 
			
		||||
        opacity: 0.2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.c-style-thumb {
 | 
			
		||||
        display: block !important;
 | 
			
		||||
        background-color: transparent !important;
 | 
			
		||||
        border-color: transparent !important;
 | 
			
		||||
        @include bgCheckerboard($size: 10px, $imp: true);
 | 
			
		||||
        opacity: 1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										166
									
								
								src/plugins/condition/criterion/AllTelemetryCriterion.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/plugins/condition/criterion/AllTelemetryCriterion.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 TelemetryCriterion from './TelemetryCriterion';
 | 
			
		||||
import { evaluateResults } from "../utils/evaluator";
 | 
			
		||||
import { getLatestTimestamp } from '../utils/time';
 | 
			
		||||
 | 
			
		||||
export default class AllTelemetryCriterion extends TelemetryCriterion {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Subscribes/Unsubscribes to telemetry and emits the result
 | 
			
		||||
     * of operations performed on the telemetry data returned and a given input value.
 | 
			
		||||
     * @constructor
 | 
			
		||||
     * @param telemetryDomainObjectDefinition {id: uuid, operation: enum, input: Array, metadata: string, key: {domainObject.identifier} }
 | 
			
		||||
     * @param openmct
 | 
			
		||||
     */
 | 
			
		||||
    constructor(telemetryDomainObjectDefinition, openmct) {
 | 
			
		||||
        super(telemetryDomainObjectDefinition, openmct);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.telemetryObjects = { ...this.telemetryDomainObjectDefinition.telemetryObjects };
 | 
			
		||||
        this.telemetryDataCache = {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isValid() {
 | 
			
		||||
        return (this.telemetry === 'any' || this.telemetry === 'all') && this.metadata && this.operation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateTelemetry(telemetryObjects) {
 | 
			
		||||
        this.telemetryObjects = { ...telemetryObjects };
 | 
			
		||||
        this.removeTelemetryDataCache();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    removeTelemetryDataCache() {
 | 
			
		||||
        const telemetryCacheIds = Object.keys(this.telemetryDataCache);
 | 
			
		||||
        Object.values(this.telemetryObjects).forEach(telemetryObject => {
 | 
			
		||||
            const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
 | 
			
		||||
            const foundIndex = telemetryCacheIds.indexOf(id);
 | 
			
		||||
            if (foundIndex > -1) {
 | 
			
		||||
                telemetryCacheIds.splice(foundIndex, 1);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        telemetryCacheIds.forEach(id => {
 | 
			
		||||
            delete (this.telemetryDataCache[id]);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    formatData(data, telemetryObjects) {
 | 
			
		||||
        if (data) {
 | 
			
		||||
            this.telemetryDataCache[data.id] = this.computeResult(data);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let keys = Object.keys(telemetryObjects);
 | 
			
		||||
        keys.forEach((key) => {
 | 
			
		||||
            let telemetryObject = telemetryObjects[key];
 | 
			
		||||
            const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
 | 
			
		||||
            if (this.telemetryDataCache[id] === undefined) {
 | 
			
		||||
                this.telemetryDataCache[id] = false;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const datum = {
 | 
			
		||||
            result: evaluateResults(Object.values(this.telemetryDataCache), this.telemetry)
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (data) {
 | 
			
		||||
            this.openmct.time.getAllTimeSystems().forEach(timeSystem => {
 | 
			
		||||
                datum[timeSystem.key] = data[timeSystem.key]
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return datum;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getResult(data, telemetryObjects) {
 | 
			
		||||
        const validatedData = this.isValid() ? data : {};
 | 
			
		||||
 | 
			
		||||
        if (validatedData) {
 | 
			
		||||
            this.telemetryDataCache[validatedData.id] = this.computeResult(validatedData);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Object.values(telemetryObjects).forEach(telemetryObject => {
 | 
			
		||||
            const id = this.openmct.objects.makeKeyString(telemetryObject.identifier);
 | 
			
		||||
            if (this.telemetryDataCache[id] === undefined) {
 | 
			
		||||
                this.telemetryDataCache[id] = false;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.result = evaluateResults(Object.values(this.telemetryDataCache), this.telemetry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    requestLAD(telemetryObjects) {
 | 
			
		||||
        const options = {
 | 
			
		||||
            strategy: 'latest',
 | 
			
		||||
            size: 1
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (!this.isValid()) {
 | 
			
		||||
            return this.formatData({}, telemetryObjects);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let keys = Object.keys(Object.assign({}, telemetryObjects));
 | 
			
		||||
        const telemetryRequests = keys
 | 
			
		||||
            .map(key => this.openmct.telemetry.request(
 | 
			
		||||
                telemetryObjects[key],
 | 
			
		||||
                options
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
        let telemetryDataCache = {};
 | 
			
		||||
        return Promise.all(telemetryRequests)
 | 
			
		||||
            .then(telemetryRequestsResults => {
 | 
			
		||||
                let latestTimestamp;
 | 
			
		||||
                const timeSystems = this.openmct.time.getAllTimeSystems();
 | 
			
		||||
                const timeSystem = this.openmct.time.timeSystem();
 | 
			
		||||
 | 
			
		||||
                telemetryRequestsResults.forEach((results, index) => {
 | 
			
		||||
                    const latestDatum = results.length ? results[results.length - 1] : {};
 | 
			
		||||
                    const datumId = keys[index];
 | 
			
		||||
                    const normalizedDatum = this.createNormalizedDatum(latestDatum, telemetryObjects[datumId]);
 | 
			
		||||
 | 
			
		||||
                    telemetryDataCache[datumId] = this.computeResult(normalizedDatum);
 | 
			
		||||
 | 
			
		||||
                    latestTimestamp = getLatestTimestamp(
 | 
			
		||||
                        latestTimestamp,
 | 
			
		||||
                        normalizedDatum,
 | 
			
		||||
                        timeSystems,
 | 
			
		||||
                        timeSystem
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                const datum = {
 | 
			
		||||
                    result: evaluateResults(Object.values(telemetryDataCache), this.telemetry),
 | 
			
		||||
                    ...latestTimestamp
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                return {
 | 
			
		||||
                    id: this.id,
 | 
			
		||||
                    data: datum
 | 
			
		||||
                };
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    destroy() {
 | 
			
		||||
        delete this.telemetryObjects;
 | 
			
		||||
        delete this.telemetryDataCache;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import EventEmitter from 'EventEmitter';
 | 
			
		||||
import {OPERATIONS} from '../utils/operations';
 | 
			
		||||
import { OPERATIONS } from '../utils/operations';
 | 
			
		||||
 | 
			
		||||
export default class TelemetryCriterion extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
@@ -36,37 +36,89 @@ export default class TelemetryCriterion extends EventEmitter {
 | 
			
		||||
        super();
 | 
			
		||||
 | 
			
		||||
        this.openmct = openmct;
 | 
			
		||||
        this.objectAPI = this.openmct.objects;
 | 
			
		||||
        this.telemetryAPI = this.openmct.telemetry;
 | 
			
		||||
        this.timeAPI = this.openmct.time;
 | 
			
		||||
        this.telemetryDomainObjectDefinition = telemetryDomainObjectDefinition;
 | 
			
		||||
        this.id = telemetryDomainObjectDefinition.id;
 | 
			
		||||
        this.telemetry = telemetryDomainObjectDefinition.telemetry;
 | 
			
		||||
        this.operation = telemetryDomainObjectDefinition.operation;
 | 
			
		||||
        this.input = telemetryDomainObjectDefinition.input;
 | 
			
		||||
        this.metadata = telemetryDomainObjectDefinition.metadata;
 | 
			
		||||
        this.subscription = null;
 | 
			
		||||
        this.telemetryObjectIdAsString = null;
 | 
			
		||||
        this.objectAPI.get(this.objectAPI.makeKeyString(this.telemetry)).then((obj) => this.initialize(obj));
 | 
			
		||||
    }
 | 
			
		||||
        this.result = undefined;
 | 
			
		||||
 | 
			
		||||
    initialize(obj) {
 | 
			
		||||
        this.telemetryObject = obj;
 | 
			
		||||
        this.telemetryObjectIdAsString = this.objectAPI.makeKeyString(this.telemetryObject.identifier);
 | 
			
		||||
        this.initialize();
 | 
			
		||||
        this.emitEvent('criterionUpdated', this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handleSubscription(data) {
 | 
			
		||||
    initialize() {
 | 
			
		||||
        this.telemetryObject = this.telemetryDomainObjectDefinition.telemetryObject;
 | 
			
		||||
        this.telemetryObjectIdAsString = this.openmct.objects.makeKeyString(this.telemetryDomainObjectDefinition.telemetry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isValid() {
 | 
			
		||||
        return this.telemetryObject && this.metadata && this.operation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateTelemetry(telemetryObjects) {
 | 
			
		||||
        this.telemetryObject = telemetryObjects[this.telemetryObjectIdAsString];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createNormalizedDatum(telemetryDatum, endpoint) {
 | 
			
		||||
        const id = this.openmct.objects.makeKeyString(endpoint.identifier);
 | 
			
		||||
        const metadata = this.openmct.telemetry.getMetadata(endpoint).valueMetadatas;
 | 
			
		||||
 | 
			
		||||
        const normalizedDatum = Object.values(metadata).reduce((datum, metadatum) => {
 | 
			
		||||
            const formatter = this.openmct.telemetry.getValueFormatter(metadatum);
 | 
			
		||||
            datum[metadatum.key] = formatter.parse(telemetryDatum[metadatum.source]);
 | 
			
		||||
            return datum;
 | 
			
		||||
        }, {});
 | 
			
		||||
 | 
			
		||||
        normalizedDatum.id = id;
 | 
			
		||||
 | 
			
		||||
        return normalizedDatum;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    formatData(data) {
 | 
			
		||||
        const datum = {
 | 
			
		||||
            result: this.computeResult(data)
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (data) {
 | 
			
		||||
            // TODO check back to see if we should format times here
 | 
			
		||||
            this.timeAPI.getAllTimeSystems().forEach(timeSystem => {
 | 
			
		||||
            this.openmct.time.getAllTimeSystems().forEach(timeSystem => {
 | 
			
		||||
                datum[timeSystem.key] = data[timeSystem.key]
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return datum;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        this.emitEvent('criterionResultUpdated', datum);
 | 
			
		||||
    getResult(data) {
 | 
			
		||||
        const validatedData = this.isValid() ? data : {};
 | 
			
		||||
        this.result = this.computeResult(validatedData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    requestLAD() {
 | 
			
		||||
        const options = {
 | 
			
		||||
            strategy: 'latest',
 | 
			
		||||
            size: 1
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (!this.isValid()) {
 | 
			
		||||
            return {
 | 
			
		||||
                id: this.id,
 | 
			
		||||
                data: this.formatData({})
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.openmct.telemetry.request(
 | 
			
		||||
            this.telemetryObject,
 | 
			
		||||
            options
 | 
			
		||||
        ).then(results => {
 | 
			
		||||
            const latestDatum = results.length ? results[results.length - 1] : {};
 | 
			
		||||
            const normalizedDatum = this.createNormalizedDatum(latestDatum, this.telemetryObject);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                id: this.id,
 | 
			
		||||
                data: this.formatData(normalizedDatum)
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    findOperation(operation) {
 | 
			
		||||
@@ -88,7 +140,7 @@ export default class TelemetryCriterion extends EventEmitter {
 | 
			
		||||
                this.input.forEach(input => params.push(input));
 | 
			
		||||
            }
 | 
			
		||||
            if (typeof comparator === 'function') {
 | 
			
		||||
                result = comparator(params);
 | 
			
		||||
                result = !!comparator(params);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
@@ -101,40 +153,9 @@ export default class TelemetryCriterion extends EventEmitter {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isValid() {
 | 
			
		||||
        return this.telemetryObject && this.metadata && this.operation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *  Subscribes to the telemetry object and returns an unsubscribe function
 | 
			
		||||
     *  If the telemetry is not valid, returns nothing
 | 
			
		||||
     */
 | 
			
		||||
    subscribe() {
 | 
			
		||||
        if (this.isValid()) {
 | 
			
		||||
            this.unsubscribe();
 | 
			
		||||
            this.subscription = this.telemetryAPI.subscribe(this.telemetryObject, (datum) => {
 | 
			
		||||
                this.handleSubscription(datum);
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.handleSubscription();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *  Calls an unsubscribe function returned by subscribe() and deletes any initialized data
 | 
			
		||||
     */
 | 
			
		||||
    unsubscribe() {
 | 
			
		||||
        //unsubscribe from telemetry source
 | 
			
		||||
        if (typeof this.subscription === 'function') {
 | 
			
		||||
            this.subscription();
 | 
			
		||||
        }
 | 
			
		||||
        delete this.subscription;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    destroy() {
 | 
			
		||||
        this.unsubscribe();
 | 
			
		||||
        this.emitEvent('criterionRemoved');
 | 
			
		||||
        delete this.telemetryObjectIdAsString;
 | 
			
		||||
        delete this.telemetryObject;
 | 
			
		||||
        delete this.telemetryObjectIdAsString;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,6 @@ import TelemetryCriterion from "./TelemetryCriterion";
 | 
			
		||||
 | 
			
		||||
let openmct = {},
 | 
			
		||||
    mockListener,
 | 
			
		||||
    mockListener2,
 | 
			
		||||
    testCriterionDefinition,
 | 
			
		||||
    testTelemetryObject,
 | 
			
		||||
    telemetryCriterion;
 | 
			
		||||
@@ -37,30 +36,39 @@ describe("The telemetry criterion", function () {
 | 
			
		||||
            type: "test-object",
 | 
			
		||||
            name: "Test Object",
 | 
			
		||||
            telemetry: {
 | 
			
		||||
                values: [{
 | 
			
		||||
                    key: "some-key",
 | 
			
		||||
                    name: "Some attribute",
 | 
			
		||||
                valueMetadatas: [{
 | 
			
		||||
                    key: "value",
 | 
			
		||||
                    name: "Value",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 2
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    key: "utc",
 | 
			
		||||
                    name: "Time",
 | 
			
		||||
                    format: "utc",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        domain: 1
 | 
			
		||||
                    }
 | 
			
		||||
                }, {
 | 
			
		||||
                    key: "some-other-key",
 | 
			
		||||
                    name: "Another attribute",
 | 
			
		||||
                    hints: {
 | 
			
		||||
                        range: 1
 | 
			
		||||
                    }
 | 
			
		||||
                    key: "testSource",
 | 
			
		||||
                    source: "value",
 | 
			
		||||
                    name: "Test",
 | 
			
		||||
                    format: "string"
 | 
			
		||||
                }]
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        openmct.objects = jasmine.createSpyObj('objects', ['get', 'makeKeyString']);
 | 
			
		||||
        openmct.objects.get.and.returnValue(new Promise(function (resolve, reject) {
 | 
			
		||||
            resolve(testTelemetryObject);
 | 
			
		||||
        }));
 | 
			
		||||
        openmct.objects.makeKeyString.and.returnValue(testTelemetryObject.identifier.key);
 | 
			
		||||
        openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata"]);
 | 
			
		||||
        openmct.telemetry = jasmine.createSpyObj('telemetry', ['isTelemetryObject', "subscribe", "getMetadata", "getValueFormatter"]);
 | 
			
		||||
        openmct.telemetry.isTelemetryObject.and.returnValue(true);
 | 
			
		||||
        openmct.telemetry.subscribe.and.returnValue(function () {});
 | 
			
		||||
        openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry.values);
 | 
			
		||||
        openmct.telemetry.getValueFormatter.and.returnValue({
 | 
			
		||||
            parse: function (value) {
 | 
			
		||||
                return value;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        openmct.telemetry.getMetadata.and.returnValue(testTelemetryObject.telemetry);
 | 
			
		||||
 | 
			
		||||
        openmct.time = jasmine.createSpyObj('timeAPI',
 | 
			
		||||
            ['timeSystem', 'bounds', 'getAllTimeSystems']
 | 
			
		||||
@@ -71,13 +79,14 @@ describe("The telemetry criterion", function () {
 | 
			
		||||
 | 
			
		||||
        testCriterionDefinition = {
 | 
			
		||||
            id: 'test-criterion-id',
 | 
			
		||||
            telemetry: openmct.objects.makeKeyString(testTelemetryObject.identifier)
 | 
			
		||||
            telemetry: openmct.objects.makeKeyString(testTelemetryObject.identifier),
 | 
			
		||||
            operation: 'textContains',
 | 
			
		||||
            metadata: 'value',
 | 
			
		||||
            input: ['Hell'],
 | 
			
		||||
            telemetryObject: testTelemetryObject
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        mockListener = jasmine.createSpy('listener');
 | 
			
		||||
        mockListener2 = jasmine.createSpy('updatedListener', (data) => {
 | 
			
		||||
            console.log(data);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        telemetryCriterion = new TelemetryCriterion(
 | 
			
		||||
            testCriterionDefinition,
 | 
			
		||||
@@ -85,39 +94,28 @@ describe("The telemetry criterion", function () {
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        telemetryCriterion.on('criterionResultUpdated', mockListener);
 | 
			
		||||
        telemetryCriterion.on('criterionUpdated', mockListener2);
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("initializes with a telemetry objectId as string", function () {
 | 
			
		||||
        telemetryCriterion.initialize(testTelemetryObject);
 | 
			
		||||
        expect(telemetryCriterion.telemetryObjectIdAsString).toEqual(testTelemetryObject.identifier.key);
 | 
			
		||||
        expect(mockListener2).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("subscribes to telemetry providers", function () {
 | 
			
		||||
        telemetryCriterion.subscribe();
 | 
			
		||||
        expect(telemetryCriterion.subscription).toBeDefined();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("emits update event on new data from telemetry providers", function () {
 | 
			
		||||
        spyOn(telemetryCriterion, 'emitEvent').and.callThrough();
 | 
			
		||||
        telemetryCriterion.handleSubscription({
 | 
			
		||||
            key: 'some-key',
 | 
			
		||||
            source: 'testSource',
 | 
			
		||||
            testSource: 'Hello'
 | 
			
		||||
    it("returns a result on new data from relevant telemetry providers", function () {
 | 
			
		||||
        telemetryCriterion.getResult({
 | 
			
		||||
            value: 'Hello',
 | 
			
		||||
            utc: 'Hi',
 | 
			
		||||
            id: testTelemetryObject.identifier.key
 | 
			
		||||
        });
 | 
			
		||||
        expect(telemetryCriterion.emitEvent).toHaveBeenCalled();
 | 
			
		||||
        expect(mockListener).toHaveBeenCalled();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it("un-subscribes from telemetry providers", function () {
 | 
			
		||||
        telemetryCriterion.subscribe();
 | 
			
		||||
        expect(telemetryCriterion.subscription).toBeDefined();
 | 
			
		||||
        telemetryCriterion.destroy();
 | 
			
		||||
        expect(telemetryCriterion.subscription).toBeUndefined();
 | 
			
		||||
        expect(telemetryCriterion.telemetryObjectIdAsString).toBeUndefined();
 | 
			
		||||
        expect(telemetryCriterion.telemetryObject).toBeUndefined();
 | 
			
		||||
        expect(telemetryCriterion.result).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // it("does not return a result on new data from irrelavant telemetry providers", function () {
 | 
			
		||||
    //     telemetryCriterion.getResult({
 | 
			
		||||
    //         value: 'Hello',
 | 
			
		||||
    //         utc: 'Hi',
 | 
			
		||||
    //         id: '1234'
 | 
			
		||||
    //     });
 | 
			
		||||
    //     expect(telemetryCriterion.result).toBeFalse();
 | 
			
		||||
    // });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ import ConditionSetViewProvider from './ConditionSetViewProvider.js';
 | 
			
		||||
import ConditionSetCompositionPolicy from "./ConditionSetCompositionPolicy";
 | 
			
		||||
import ConditionSetMetadataProvider from './ConditionSetMetadataProvider';
 | 
			
		||||
import ConditionSetTelemetryProvider from './ConditionSetTelemetryProvider';
 | 
			
		||||
import ConditionSetViewPolicy from './ConditionSetViewPolicy';
 | 
			
		||||
import uuid from "uuid";
 | 
			
		||||
 | 
			
		||||
export default function ConditionPlugin() {
 | 
			
		||||
@@ -32,17 +33,18 @@ export default function ConditionPlugin() {
 | 
			
		||||
        openmct.types.addType('conditionSet', {
 | 
			
		||||
            name: 'Condition Set',
 | 
			
		||||
            key: 'conditionSet',
 | 
			
		||||
            description: 'A set of one or more conditions based on user-specified criteria.',
 | 
			
		||||
            description: 'Monitor and evaluate telemetry values in real-time with a wide variety of criteria. Use to control the styling of many objects in Open MCT.',
 | 
			
		||||
            creatable: true,
 | 
			
		||||
            cssClass: 'icon-conditional',  // TODO: replace with class for new icon
 | 
			
		||||
            cssClass: 'icon-conditional',
 | 
			
		||||
            initialize: function (domainObject) {
 | 
			
		||||
                domainObject.configuration = {
 | 
			
		||||
                    conditionTestData: [],
 | 
			
		||||
                    conditionCollection: [{
 | 
			
		||||
                        isDefault: true,
 | 
			
		||||
                        id: uuid(),
 | 
			
		||||
                        configuration: {
 | 
			
		||||
                            name: 'Default',
 | 
			
		||||
                            output: 'false',
 | 
			
		||||
                            output: 'Default',
 | 
			
		||||
                            trigger: 'all',
 | 
			
		||||
                            criteria: []
 | 
			
		||||
                        },
 | 
			
		||||
@@ -53,7 +55,10 @@ export default function ConditionPlugin() {
 | 
			
		||||
                domainObject.telemetry = {};
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        openmct.legacyExtension('policies', {
 | 
			
		||||
            category: 'view',
 | 
			
		||||
            implementation: ConditionSetViewPolicy
 | 
			
		||||
        });
 | 
			
		||||
        openmct.composition.addPolicy(new ConditionSetCompositionPolicy(openmct).allow);
 | 
			
		||||
        openmct.telemetry.addProvider(new ConditionSetMetadataProvider(openmct));
 | 
			
		||||
        openmct.telemetry.addProvider(new ConditionSetTelemetryProvider(openmct));
 | 
			
		||||
 
 | 
			
		||||
@@ -20,25 +20,21 @@
 | 
			
		||||
 * at runtime from the About dialog for additional information.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
import { createOpenMct } from "testTools";
 | 
			
		||||
import { createOpenMct } from "testUtils";
 | 
			
		||||
import ConditionPlugin from "./plugin";
 | 
			
		||||
 | 
			
		||||
let openmct = createOpenMct();
 | 
			
		||||
openmct.install(new ConditionPlugin());
 | 
			
		||||
 | 
			
		||||
let conditionSetDefinition;
 | 
			
		||||
let mockConditionSetDomainObject;
 | 
			
		||||
let element;
 | 
			
		||||
let child;
 | 
			
		||||
 | 
			
		||||
describe('the plugin', function () {
 | 
			
		||||
    let conditionSetDefinition;
 | 
			
		||||
    let mockConditionSetDomainObject;
 | 
			
		||||
    let element;
 | 
			
		||||
    let child;
 | 
			
		||||
    let openmct;
 | 
			
		||||
 | 
			
		||||
    beforeAll((done) => {
 | 
			
		||||
        openmct = createOpenMct();
 | 
			
		||||
        openmct.install(new ConditionPlugin());
 | 
			
		||||
 | 
			
		||||
        conditionSetDefinition = openmct.types.get('conditionSet').definition;
 | 
			
		||||
        const appHolder = document.createElement('div');
 | 
			
		||||
        appHolder.style.width = '640px';
 | 
			
		||||
        appHolder.style.height = '480px';
 | 
			
		||||
 | 
			
		||||
        element = document.createElement('div');
 | 
			
		||||
        child = document.createElement('div');
 | 
			
		||||
@@ -55,7 +51,7 @@ describe('the plugin', function () {
 | 
			
		||||
        conditionSetDefinition.initialize(mockConditionSetDomainObject);
 | 
			
		||||
 | 
			
		||||
        openmct.on('start', done);
 | 
			
		||||
        openmct.start(appHolder);
 | 
			
		||||
        openmct.startHeadless();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let mockConditionSetObject = {
 | 
			
		||||
@@ -82,7 +78,10 @@ describe('the plugin', function () {
 | 
			
		||||
        it('provides a view', () => {
 | 
			
		||||
            const testViewObject = {
 | 
			
		||||
                id:"test-object",
 | 
			
		||||
                type: "conditionSet"
 | 
			
		||||
                type: "conditionSet",
 | 
			
		||||
                configuration: {
 | 
			
		||||
                    conditionCollection: []
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            const applicableViews = openmct.objectViews.get(testViewObject);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,54 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
export const TRIGGER = {
 | 
			
		||||
    ANY: 'any',
 | 
			
		||||
    ALL: 'all'
 | 
			
		||||
    ALL: 'all',
 | 
			
		||||
    NOT: 'not',
 | 
			
		||||
    XOR: 'xor'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const TRIGGER_LABEL = {
 | 
			
		||||
    'any': 'when any criteria are met',
 | 
			
		||||
    'all': 'when all criteria are met',
 | 
			
		||||
    'not': 'when no criteria are met',
 | 
			
		||||
    'xor': 'when only one criteria is met'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const STYLE_CONSTANTS = {
 | 
			
		||||
    isStyleInvisible: 'is-style-invisible',
 | 
			
		||||
    borderColorTitle: 'Set border color',
 | 
			
		||||
    textColorTitle: 'Set text color',
 | 
			
		||||
    backgroundColorTitle: 'Set background color',
 | 
			
		||||
    imagePropertiesTitle: 'Edit image properties',
 | 
			
		||||
    visibilityHidden: 'Hidden',
 | 
			
		||||
    visibilityVisible: 'Visible'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const ERROR = {
 | 
			
		||||
    'TELEMETRY_NOT_FOUND': {
 | 
			
		||||
        errorText: 'Telemetry not found for criterion'
 | 
			
		||||
    },
 | 
			
		||||
    'CONDITION_NOT_FOUND': {
 | 
			
		||||
        errorText: 'Condition not found'
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,68 @@
 | 
			
		||||
export const computeCondition = (resultMap, allMustBeTrue) => {
 | 
			
		||||
    let result = false;
 | 
			
		||||
    for (let key in resultMap) {
 | 
			
		||||
        if (resultMap.hasOwnProperty(key)) {
 | 
			
		||||
            result = resultMap[key];
 | 
			
		||||
            if (allMustBeTrue && !result) {
 | 
			
		||||
                //If we want all conditions to be true, then even one negative result should break.
 | 
			
		||||
                break;
 | 
			
		||||
            } else if (!allMustBeTrue && result) {
 | 
			
		||||
                //If we want at least one condition to be true, then even one positive result should break.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 { TRIGGER } from "./constants";
 | 
			
		||||
 | 
			
		||||
export const evaluateResults = (results, trigger) => {
 | 
			
		||||
    if (trigger && trigger === TRIGGER.XOR) {
 | 
			
		||||
        return matchExact(results, 1);
 | 
			
		||||
    } else if (trigger && trigger === TRIGGER.NOT) {
 | 
			
		||||
        return matchExact(results, 0);
 | 
			
		||||
    } else if (trigger && trigger === TRIGGER.ALL) {
 | 
			
		||||
        return matchAll(results);
 | 
			
		||||
    } else {
 | 
			
		||||
        return matchAny(results);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function matchAll(results) {
 | 
			
		||||
    for (const result of results) {
 | 
			
		||||
        if (!result) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function matchAny(results) {
 | 
			
		||||
    for (const result of results) {
 | 
			
		||||
        if (result) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function matchExact(results, target) {
 | 
			
		||||
    let matches = 0;
 | 
			
		||||
    for (const result of results) {
 | 
			
		||||
        if (result) {
 | 
			
		||||
            matches++;
 | 
			
		||||
        }
 | 
			
		||||
        if (matches > target) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return matches === target;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										204
									
								
								src/plugins/condition/utils/evaluatorSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								src/plugins/condition/utils/evaluatorSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,204 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 { evaluateResults } from './evaluator';
 | 
			
		||||
import { TRIGGER } from './constants';
 | 
			
		||||
 | 
			
		||||
describe('evaluate results', () => {
 | 
			
		||||
    // const allTrue = [true, true, true, true, true];
 | 
			
		||||
    // const oneTrue = [false, false, false, false, true];
 | 
			
		||||
    // const multipleTrue = [false, true, false, true, false];
 | 
			
		||||
    // const noneTrue = [false, false, false, false, false];
 | 
			
		||||
    // const allTrueWithUndefined = [true, true, true, undefined, true];
 | 
			
		||||
    // const oneTrueWithUndefined = [undefined, undefined, undefined, undefined, true];
 | 
			
		||||
    // const multipleTrueWithUndefined = [true, undefined, true, undefined, true];
 | 
			
		||||
    // const allUndefined = [undefined, undefined, undefined, undefined, undefined];
 | 
			
		||||
    // const singleTrue = [true];
 | 
			
		||||
    // const singleFalse = [false];
 | 
			
		||||
    // const singleUndefined = [undefined];
 | 
			
		||||
    // const empty = [];
 | 
			
		||||
 | 
			
		||||
    const tests = [
 | 
			
		||||
        {
 | 
			
		||||
            name: 'allTrue',
 | 
			
		||||
            values: [true, true, true, true, true],
 | 
			
		||||
            any: true,
 | 
			
		||||
            all: true,
 | 
			
		||||
            not: false,
 | 
			
		||||
            xor: false
 | 
			
		||||
        }, {
 | 
			
		||||
            name: 'oneTrue',
 | 
			
		||||
            values: [false, false, false, false, true],
 | 
			
		||||
            any: true,
 | 
			
		||||
            all: false,
 | 
			
		||||
            not: false,
 | 
			
		||||
            xor: true
 | 
			
		||||
        }, {
 | 
			
		||||
            name: 'multipleTrue',
 | 
			
		||||
            values: [false, true, false, true, false],
 | 
			
		||||
            any: true,
 | 
			
		||||
            all: false,
 | 
			
		||||
            not: false,
 | 
			
		||||
            xor: false
 | 
			
		||||
        }, {
 | 
			
		||||
            name: 'noneTrue',
 | 
			
		||||
            values: [false, false, false, false, false],
 | 
			
		||||
            any: false,
 | 
			
		||||
            all: false,
 | 
			
		||||
            not: true,
 | 
			
		||||
            xor: false
 | 
			
		||||
        }, {
 | 
			
		||||
            name: 'allTrueWithUndefined',
 | 
			
		||||
            values: [true, true, true, undefined, true],
 | 
			
		||||
            any: true,
 | 
			
		||||
            all: false,
 | 
			
		||||
            not: false,
 | 
			
		||||
            xor: false
 | 
			
		||||
        }, {
 | 
			
		||||
            name: 'oneTrueWithUndefined',
 | 
			
		||||
            values: [undefined, undefined, undefined, undefined, true],
 | 
			
		||||
            any: true,
 | 
			
		||||
            all: false,
 | 
			
		||||
            not: false,
 | 
			
		||||
            xor: true
 | 
			
		||||
        }, {
 | 
			
		||||
            name: 'multipleTrueWithUndefined',
 | 
			
		||||
            values: [true, undefined, true, undefined, true],
 | 
			
		||||
            any: true,
 | 
			
		||||
            all: false,
 | 
			
		||||
            not: false,
 | 
			
		||||
            xor: false
 | 
			
		||||
        }, {
 | 
			
		||||
            name: 'allUndefined',
 | 
			
		||||
            values: [undefined, undefined, undefined, undefined, undefined],
 | 
			
		||||
            any: false,
 | 
			
		||||
            all: false,
 | 
			
		||||
            not: true,
 | 
			
		||||
            xor: false
 | 
			
		||||
        }, {
 | 
			
		||||
            name: 'singleTrue',
 | 
			
		||||
            values: [true],
 | 
			
		||||
            any: true,
 | 
			
		||||
            all: true,
 | 
			
		||||
            not: false,
 | 
			
		||||
            xor: true
 | 
			
		||||
        }, {
 | 
			
		||||
            name: 'singleFalse',
 | 
			
		||||
            values: [false],
 | 
			
		||||
            any: false,
 | 
			
		||||
            all: false,
 | 
			
		||||
            not: true,
 | 
			
		||||
            xor: false
 | 
			
		||||
        }, {
 | 
			
		||||
            name: 'singleUndefined',
 | 
			
		||||
            values: [undefined],
 | 
			
		||||
            any: false,
 | 
			
		||||
            all: false,
 | 
			
		||||
            not: true,
 | 
			
		||||
            xor: false
 | 
			
		||||
        }
 | 
			
		||||
        // , {
 | 
			
		||||
        //     name: 'empty',
 | 
			
		||||
        //     values: [],
 | 
			
		||||
        //     any: false,
 | 
			
		||||
        //     all: false,
 | 
			
		||||
        //     not: true,
 | 
			
		||||
        //     xor: false
 | 
			
		||||
        // }
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    describe(`based on trigger ${TRIGGER.ANY}`, () => {
 | 
			
		||||
        it('should evaluate to expected result', () => {
 | 
			
		||||
            tests.forEach(test => {
 | 
			
		||||
                const result = evaluateResults(test.values, TRIGGER.ANY);
 | 
			
		||||
                expect(result).toEqual(test[TRIGGER.ANY])
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe(`based on trigger ${TRIGGER.ALL}`, () => {
 | 
			
		||||
        it('should evaluate to expected result', () => {
 | 
			
		||||
            tests.forEach(test => {
 | 
			
		||||
                const result = evaluateResults(test.values, TRIGGER.ALL);
 | 
			
		||||
                expect(result).toEqual(test[TRIGGER.ALL])
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe(`based on trigger ${TRIGGER.NOT}`, () => {
 | 
			
		||||
        it('should evaluate to expected result', () => {
 | 
			
		||||
            tests.forEach(test => {
 | 
			
		||||
                const result = evaluateResults(test.values, TRIGGER.NOT);
 | 
			
		||||
                expect(result).toEqual(test[TRIGGER.NOT])
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe(`based on trigger ${TRIGGER.XOR}`, () => {
 | 
			
		||||
        it('should evaluate to expected result', () => {
 | 
			
		||||
            tests.forEach(test => {
 | 
			
		||||
                const result = evaluateResults(test.values, TRIGGER.XOR);
 | 
			
		||||
                expect(result).toEqual(test[TRIGGER.XOR])
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // it('should evaluate to true if trigger is NOT', () => {
 | 
			
		||||
    //     const results = {
 | 
			
		||||
    //         result: false,
 | 
			
		||||
    //         result1: false,
 | 
			
		||||
    //         result2: false
 | 
			
		||||
    //     };
 | 
			
		||||
    //     const result = computeConditionByLimit(results, 0);
 | 
			
		||||
    //     expect(result).toBeTrue();
 | 
			
		||||
    // });
 | 
			
		||||
 | 
			
		||||
    // it('should evaluate to false if trigger is NOT', () => {
 | 
			
		||||
    //     const results = {
 | 
			
		||||
    //         result: true,
 | 
			
		||||
    //         result1: false,
 | 
			
		||||
    //         result2: false
 | 
			
		||||
    //     };
 | 
			
		||||
    //     const result = computeConditionByLimit(results, 0);
 | 
			
		||||
    //     expect(result).toBeFalse();
 | 
			
		||||
    // });
 | 
			
		||||
 | 
			
		||||
    // it('should evaluate to true if trigger is XOR', () => {
 | 
			
		||||
    //     const results = {
 | 
			
		||||
    //         result: false,
 | 
			
		||||
    //         result1: true,
 | 
			
		||||
    //         result2: false
 | 
			
		||||
    //     };
 | 
			
		||||
    //     const result = computeConditionByLimit(results, 1);
 | 
			
		||||
    //     expect(result).toBeTrue();
 | 
			
		||||
    // });
 | 
			
		||||
 | 
			
		||||
    // it('should evaluate to false if trigger is XOR', () => {
 | 
			
		||||
    //     const results = {
 | 
			
		||||
    //         result: false,
 | 
			
		||||
    //         result1: true,
 | 
			
		||||
    //         result2: true
 | 
			
		||||
    //     };
 | 
			
		||||
    //     const result = computeConditionByLimit(results, 1);
 | 
			
		||||
    //     expect(result).toBeFalse();
 | 
			
		||||
    // });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,80 +1,121 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
const convertToNumbers = (input) => {
 | 
			
		||||
    let numberInputs = [];
 | 
			
		||||
    input.forEach(inputValue => numberInputs.push(Number(inputValue)));
 | 
			
		||||
    return numberInputs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const convertToStrings = (input) => {
 | 
			
		||||
    let stringInputs = [];
 | 
			
		||||
    input.forEach(inputValue => stringInputs.push(inputValue !== undefined ? inputValue.toString() : ''));
 | 
			
		||||
    return stringInputs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const joinValues = (values, length) => {
 | 
			
		||||
    return values.slice(0, length).join(', ');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const OPERATIONS = [
 | 
			
		||||
    {
 | 
			
		||||
        name: 'equalTo',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            return input[0] === input[1];
 | 
			
		||||
            return Number(input[0]) === Number(input[1]);
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is equal to',
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is ' + values[0];
 | 
			
		||||
            return ' is ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        name: 'notEqualTo',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            return input[0] !== input[1];
 | 
			
		||||
            return Number(input[0]) !== Number(input[1]);
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is not equal to',
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is not ' + values[0];
 | 
			
		||||
            return ' is not ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        name: 'greaterThan',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            return input[0] > input[1];
 | 
			
		||||
            return Number(input[0]) > Number(input[1]);
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is greater than',
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' > ' + values[0];
 | 
			
		||||
            return ' > ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        name: 'lessThan',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            return input[0] < input[1];
 | 
			
		||||
            return Number(input[0]) < Number(input[1]);
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is less than',
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' < ' + values[0];
 | 
			
		||||
            return ' < ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        name: 'greaterThanOrEq',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            return input[0] >= input[1];
 | 
			
		||||
            return Number(input[0]) >= Number(input[1]);
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is greater than or equal to',
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' >= ' + values[0];
 | 
			
		||||
            return ' >= ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        name: 'lessThanOrEq',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            return input[0] <= input[1];
 | 
			
		||||
            return Number(input[0]) <= Number(input[1]);
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is less than or equal to',
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' <= ' + values[0];
 | 
			
		||||
            return ' <= ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        name: 'between',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            return input[0] > input[1] && input[0] < input[2];
 | 
			
		||||
            let numberInputs = convertToNumbers(input);
 | 
			
		||||
            let larger = Math.max(...numberInputs.slice(1,3));
 | 
			
		||||
            let smaller = Math.min(...numberInputs.slice(1,3));
 | 
			
		||||
            return (numberInputs[0] > smaller) && (numberInputs[0] < larger);
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is between',
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
@@ -86,7 +127,10 @@ export const OPERATIONS = [
 | 
			
		||||
    {
 | 
			
		||||
        name: 'notBetween',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            return input[0] < input[1] || input[0] > input[2];
 | 
			
		||||
            let numberInputs = convertToNumbers(input);
 | 
			
		||||
            let larger = Math.max(...numberInputs.slice(1,3));
 | 
			
		||||
            let smaller = Math.min(...numberInputs.slice(1,3));
 | 
			
		||||
            return (numberInputs[0] < smaller) || (numberInputs[0] > larger);
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is not between',
 | 
			
		||||
        appliesTo: ['number'],
 | 
			
		||||
@@ -104,7 +148,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['string'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' contains ' + values[0];
 | 
			
		||||
            return ' contains ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -116,7 +160,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['string'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' does not contain ' + values[0];
 | 
			
		||||
            return ' does not contain ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -128,7 +172,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['string'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' starts with ' + values[0];
 | 
			
		||||
            return ' starts with ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -140,7 +184,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['string'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' ends with ' + values[0];
 | 
			
		||||
            return ' ends with ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -152,7 +196,7 @@ export const OPERATIONS = [
 | 
			
		||||
        appliesTo: ['string'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is exactly ' + values[0];
 | 
			
		||||
            return ' is exactly ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -182,25 +226,67 @@ export const OPERATIONS = [
 | 
			
		||||
    {
 | 
			
		||||
        name: 'enumValueIs',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            return input[0] === input[1];
 | 
			
		||||
            let stringInputs = convertToStrings(input);
 | 
			
		||||
            return stringInputs[0] === stringInputs[1];
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is',
 | 
			
		||||
        appliesTo: ['enum'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is ' + values[0];
 | 
			
		||||
            return ' is ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        name: 'enumValueIsNot',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            return input[0] !== input[1];
 | 
			
		||||
            let stringInputs = convertToStrings(input);
 | 
			
		||||
            return stringInputs[0] !== stringInputs[1];
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is not',
 | 
			
		||||
        appliesTo: ['enum'],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is not ' + values[0];
 | 
			
		||||
            return ' is not ' + joinValues(values, 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        name: 'valueIs',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            const lhsValue = input[0] !== undefined ? input[0].toString() : '';
 | 
			
		||||
            if (input[1]) {
 | 
			
		||||
                const values = input[1].split(',');
 | 
			
		||||
                return values.find((value) => lhsValue === value.toString().trim());
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is one of',
 | 
			
		||||
        appliesTo: ["string", "number"],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is one of ' + values[0];
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        name: 'valueIsNot',
 | 
			
		||||
        operation: function (input) {
 | 
			
		||||
            const lhsValue = input[0] !== undefined ? input[0].toString() : '';
 | 
			
		||||
            if (input[1]) {
 | 
			
		||||
                const values = input[1].split(',');
 | 
			
		||||
                const found = values.find((value) => lhsValue === value.toString().trim());
 | 
			
		||||
                return !found;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        },
 | 
			
		||||
        text: 'is not one of',
 | 
			
		||||
        appliesTo: ["string", "number"],
 | 
			
		||||
        inputCount: 1,
 | 
			
		||||
        getDescription: function (values) {
 | 
			
		||||
            return ' is not one of ' + values[0];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const INPUT_TYPES = {
 | 
			
		||||
    'string': 'text',
 | 
			
		||||
    'number': 'number'
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										142
									
								
								src/plugins/condition/utils/operationsSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								src/plugins/condition/utils/operationsSpec.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 { OPERATIONS } from "./operations";
 | 
			
		||||
let isOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIs');
 | 
			
		||||
let isNotOneOfOperation = OPERATIONS.find((operation) => operation.name === 'valueIsNot');
 | 
			
		||||
let isBetween = OPERATIONS.find((operation) => operation.name === 'between');
 | 
			
		||||
let isNotBetween = OPERATIONS.find((operation) => operation.name === 'notBetween');
 | 
			
		||||
let enumIsOperation = OPERATIONS.find((operation) => operation.name === 'enumValueIs');
 | 
			
		||||
let enumIsNotOperation = OPERATIONS.find((operation) => operation.name === 'enumValueIsNot');
 | 
			
		||||
 | 
			
		||||
describe('operations', function () {
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isOneOf to true for number inputs', () => {
 | 
			
		||||
        const inputs = [45, "5,6,45,8"];
 | 
			
		||||
        expect(!!isOneOfOperation.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isOneOf to true for string inputs', () => {
 | 
			
		||||
        const inputs = ["45", " 45, 645, 4,8 "];
 | 
			
		||||
        expect(!!isOneOfOperation.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isNotOneOf to true for number inputs', () => {
 | 
			
		||||
        const inputs = [45, "5,6,4,8"];
 | 
			
		||||
        expect(!!isNotOneOfOperation.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isNotOneOf to true for string inputs', () => {
 | 
			
		||||
        const inputs = ["45", " 5,645, 4,8 "];
 | 
			
		||||
        expect(!!isNotOneOfOperation.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isOneOf to false for number inputs', () => {
 | 
			
		||||
        const inputs = [4, "5, 6, 7, 8"];
 | 
			
		||||
        expect(!!isOneOfOperation.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isOneOf to false for string inputs', () => {
 | 
			
		||||
        const inputs = ["4", "5,645 ,7,8"];
 | 
			
		||||
        expect(!!isOneOfOperation.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isNotOneOf to false for number inputs', () => {
 | 
			
		||||
        const inputs = [4, "5,4, 7,8"];
 | 
			
		||||
        expect(!!isNotOneOfOperation.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isNotOneOf to false for string inputs', () => {
 | 
			
		||||
        const inputs = ["4", "5,46,4,8"];
 | 
			
		||||
        expect(!!isNotOneOfOperation.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isBetween to true', () => {
 | 
			
		||||
        const inputs = ["4", "3", "89"];
 | 
			
		||||
        expect(!!isBetween.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isNotBetween to true', () => {
 | 
			
		||||
        const inputs = ["45", "100", "89"];
 | 
			
		||||
        expect(!!isNotBetween.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isBetween to false', () => {
 | 
			
		||||
        const inputs = ["4", "100", "89"];
 | 
			
		||||
        expect(!!isBetween.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate isNotBetween to false', () => {
 | 
			
		||||
        const inputs = ["45", "30", "50"];
 | 
			
		||||
        expect(!!isNotBetween.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate enumValueIs to true for number inputs', () => {
 | 
			
		||||
        const inputs = [1, "1"];
 | 
			
		||||
        expect(!!enumIsOperation.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate enumValueIs to true for string inputs', () => {
 | 
			
		||||
        const inputs = ["45", "45"];
 | 
			
		||||
        expect(!!enumIsOperation.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate enumValueIsNot to true for number inputs', () => {
 | 
			
		||||
        const inputs = [45, "46"];
 | 
			
		||||
        expect(!!enumIsNotOperation.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate enumValueIsNot to true for string inputs', () => {
 | 
			
		||||
        const inputs = ["45", "46"];
 | 
			
		||||
        expect(!!enumIsNotOperation.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate enumValueIs to false for number inputs', () => {
 | 
			
		||||
        const inputs = [1, "2"];
 | 
			
		||||
        expect(!!enumIsOperation.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate enumValueIs to false for string inputs', () => {
 | 
			
		||||
        const inputs = ["45", "46"];
 | 
			
		||||
        expect(!!enumIsOperation.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate enumValueIsNot to false for number inputs', () => {
 | 
			
		||||
        const inputs = [45, "45"];
 | 
			
		||||
        expect(!!enumIsNotOperation.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate enumValueIsNot to false for string inputs', () => {
 | 
			
		||||
        const inputs = ["45", "45"];
 | 
			
		||||
        expect(!!enumIsNotOperation.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate enumValueIs to false for undefined input', () => {
 | 
			
		||||
        const inputs = [undefined, "45"];
 | 
			
		||||
        expect(!!enumIsOperation.operation(inputs)).toBeFalse();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should evaluate enumValueIsNot to true for undefined input', () => {
 | 
			
		||||
        const inputs = [undefined, "45"];
 | 
			
		||||
        expect(!!enumIsNotOperation.operation(inputs)).toBeTrue();
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
@@ -1,19 +1,174 @@
 | 
			
		||||
export const getStyleProp = (key, defaultValue) => {
 | 
			
		||||
    let styleProp = undefined;
 | 
			
		||||
    switch(key) {
 | 
			
		||||
    case 'fill': styleProp = {
 | 
			
		||||
        backgroundColor: defaultValue || 'none'
 | 
			
		||||
    };
 | 
			
		||||
        break;
 | 
			
		||||
    case 'stroke': styleProp = {
 | 
			
		||||
        border: '1px solid ' + defaultValue || 'none'
 | 
			
		||||
    };
 | 
			
		||||
        break;
 | 
			
		||||
    case 'color': styleProp = {
 | 
			
		||||
        color: defaultValue || 'inherit'
 | 
			
		||||
    };
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 isEmpty from 'lodash/isEmpty';
 | 
			
		||||
 | 
			
		||||
    return styleProp;
 | 
			
		||||
const NONE_VALUE = '__no_value';
 | 
			
		||||
 | 
			
		||||
const styleProps = {
 | 
			
		||||
    backgroundColor: {
 | 
			
		||||
        svgProperty: 'fill',
 | 
			
		||||
        noneValue: NONE_VALUE,
 | 
			
		||||
        applicableForType: type => {
 | 
			
		||||
            return !type ? true : (type === 'text-view' ||
 | 
			
		||||
                                      type === 'telemetry-view' ||
 | 
			
		||||
                                      type === 'box-view' ||
 | 
			
		||||
                                      type === 'subobject-view');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    border: {
 | 
			
		||||
        svgProperty: 'stroke',
 | 
			
		||||
        noneValue: NONE_VALUE,
 | 
			
		||||
        applicableForType: type => {
 | 
			
		||||
            return !type ? true : (type === 'text-view' ||
 | 
			
		||||
                                            type === 'telemetry-view' ||
 | 
			
		||||
                                            type === 'box-view' ||
 | 
			
		||||
                                            type === 'image-view' ||
 | 
			
		||||
                                            type === 'line-view'||
 | 
			
		||||
                                            type === 'subobject-view');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    color: {
 | 
			
		||||
        svgProperty: 'color',
 | 
			
		||||
        noneValue: NONE_VALUE,
 | 
			
		||||
        applicableForType: type => {
 | 
			
		||||
            return !type ? true : (type === 'text-view' ||
 | 
			
		||||
                                    type === 'telemetry-view'||
 | 
			
		||||
                                    type === 'subobject-view');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    imageUrl: {
 | 
			
		||||
        svgProperty: 'url',
 | 
			
		||||
        noneValue: '',
 | 
			
		||||
        applicableForType: type => {
 | 
			
		||||
            return !type ? false : type === 'image-view';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const aggregateStyleValues = (accumulator, currentStyle) => {
 | 
			
		||||
    const styleKeys = Object.keys(currentStyle);
 | 
			
		||||
    const properties = Object.keys(styleProps);
 | 
			
		||||
    properties.forEach((property) => {
 | 
			
		||||
        if (!accumulator[property]) {
 | 
			
		||||
            accumulator[property] = [];
 | 
			
		||||
        }
 | 
			
		||||
        const found = styleKeys.find(key => key === property);
 | 
			
		||||
        if (found) {
 | 
			
		||||
            accumulator[property].push(currentStyle[found]);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    return accumulator;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Returns a union of styles used by multiple items.
 | 
			
		||||
// Styles that are common to all items but don't have the same value are added to the mixedStyles list
 | 
			
		||||
export const getConsolidatedStyleValues = (multipleItemStyles) => {
 | 
			
		||||
    let aggregatedStyleValues = multipleItemStyles.reduce(aggregateStyleValues, {});
 | 
			
		||||
 | 
			
		||||
    let styleValues = {};
 | 
			
		||||
    let mixedStyles = [];
 | 
			
		||||
    const properties = Object.keys(styleProps);
 | 
			
		||||
    properties.forEach((property) => {
 | 
			
		||||
        const values = aggregatedStyleValues[property];
 | 
			
		||||
        if (values.length) {
 | 
			
		||||
            if (values.every(value => value === values[0])) {
 | 
			
		||||
                styleValues[property] = values[0];
 | 
			
		||||
            } else {
 | 
			
		||||
                styleValues[property] = '';
 | 
			
		||||
                mixedStyles.push(property);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    return {
 | 
			
		||||
        styles: styleValues,
 | 
			
		||||
        mixedStyles
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getStaticStyleForItem = (domainObject, id) => {
 | 
			
		||||
    let domainObjectStyles = domainObject && domainObject.configuration && domainObject.configuration.objectStyles;
 | 
			
		||||
    if (domainObjectStyles) {
 | 
			
		||||
        if (id) {
 | 
			
		||||
            if(domainObjectStyles[id] && domainObjectStyles[id].staticStyle) {
 | 
			
		||||
                return domainObjectStyles[id].staticStyle.style;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (domainObjectStyles.staticStyle) {
 | 
			
		||||
            return domainObjectStyles.staticStyle.style;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getConditionalStyleForItem = (domainObject, id) => {
 | 
			
		||||
    let domainObjectStyles = domainObject && domainObject.configuration && domainObject.configuration.objectStyles;
 | 
			
		||||
    if (domainObjectStyles) {
 | 
			
		||||
        if (id) {
 | 
			
		||||
            if (domainObjectStyles[id] && domainObjectStyles[id].conditionSetIdentifier) {
 | 
			
		||||
                return domainObjectStyles[id].styles;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (domainObjectStyles.staticStyle) {
 | 
			
		||||
            return domainObjectStyles.styles;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//Returns either existing static styles or uses SVG defaults if available
 | 
			
		||||
export const getApplicableStylesForItem = (domainObject, item) => {
 | 
			
		||||
    const type = item && item.type;
 | 
			
		||||
    const id = item && item.id;
 | 
			
		||||
    let style = {};
 | 
			
		||||
 | 
			
		||||
    let staticStyle = getStaticStyleForItem(domainObject, id);
 | 
			
		||||
 | 
			
		||||
    const properties = Object.keys(styleProps);
 | 
			
		||||
    properties.forEach(property => {
 | 
			
		||||
        const styleProp = styleProps[property];
 | 
			
		||||
        if (styleProp.applicableForType(type)) {
 | 
			
		||||
            let defaultValue;
 | 
			
		||||
            if (staticStyle) {
 | 
			
		||||
                defaultValue = staticStyle[property];
 | 
			
		||||
            } else if (item) {
 | 
			
		||||
                defaultValue = item[styleProp.svgProperty];
 | 
			
		||||
            }
 | 
			
		||||
            style[property] = defaultValue === undefined ? styleProp.noneValue : defaultValue;
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return style;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getStylesWithoutNoneValue = (style) => {
 | 
			
		||||
    if (isEmpty(style) || !style) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    let styleObj = {};
 | 
			
		||||
    const keys = Object.keys(style);
 | 
			
		||||
    keys.forEach(key => {
 | 
			
		||||
        if ((typeof style[key] === 'string')) {
 | 
			
		||||
            if (style[key].indexOf('__no_value') > -1) {
 | 
			
		||||
                style[key] = '';
 | 
			
		||||
            } else {
 | 
			
		||||
                styleObj[key] = style[key];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    return styleObj;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										52
									
								
								src/plugins/condition/utils/time.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/plugins/condition/utils/time.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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.
 | 
			
		||||
 *****************************************************************************/
 | 
			
		||||
 | 
			
		||||
export const getLatestTimestamp = (
 | 
			
		||||
    currentTimestamp,
 | 
			
		||||
    compareTimestamp,
 | 
			
		||||
    timeSystems,
 | 
			
		||||
    currentTimeSystem
 | 
			
		||||
) => {
 | 
			
		||||
    let latest = { ...currentTimestamp };
 | 
			
		||||
    const compare = { ...compareTimestamp };
 | 
			
		||||
    const key = currentTimeSystem.key;
 | 
			
		||||
 | 
			
		||||
    if (!latest || !latest[key]) {
 | 
			
		||||
        latest = updateLatestTimeStamp(compare, timeSystems)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (compare[key] > latest[key]) {
 | 
			
		||||
        latest = updateLatestTimeStamp(compare, timeSystems)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return latest;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function updateLatestTimeStamp(timestamp, timeSystems) {
 | 
			
		||||
    let latest = {};
 | 
			
		||||
 | 
			
		||||
    timeSystems.forEach(timeSystem => {
 | 
			
		||||
        latest[timeSystem.key] = timestamp[timeSystem.key];
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return latest;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										64
									
								
								src/plugins/conditionWidget/ConditionWidgetViewProvider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/plugins/conditionWidget/ConditionWidgetViewProvider.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 * 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 ConditionWidgetComponent from './components/ConditionWidget.vue';
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default function ConditionWidget(openmct) {
 | 
			
		||||
    return {
 | 
			
		||||
        key: 'conditionWidget',
 | 
			
		||||
        name: 'Condition Widget',
 | 
			
		||||
        cssClass: 'icon-condition-widget',
 | 
			
		||||
        canView: function (domainObject) {
 | 
			
		||||
            return domainObject.type === 'conditionWidget';
 | 
			
		||||
        },
 | 
			
		||||
        canEdit: function (domainObject) {
 | 
			
		||||
            return domainObject.type === 'conditionWidget';
 | 
			
		||||
        },
 | 
			
		||||
        view: function (domainObject) {
 | 
			
		||||
            let component;
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                show: function (element) {
 | 
			
		||||
                    component =  new Vue({
 | 
			
		||||
                        el: element,
 | 
			
		||||
                        components: {
 | 
			
		||||
                            ConditionWidgetComponent: ConditionWidgetComponent
 | 
			
		||||
                        },
 | 
			
		||||
                        provide: {
 | 
			
		||||
                            openmct,
 | 
			
		||||
                            domainObject
 | 
			
		||||
                        },
 | 
			
		||||
                        template: '<condition-widget-component></condition-widget-component>'
 | 
			
		||||
                    });
 | 
			
		||||
                },
 | 
			
		||||
                destroy: function (element) {
 | 
			
		||||
                    component.$destroy();
 | 
			
		||||
                    component = undefined;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        priority: function () {
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										61
									
								
								src/plugins/conditionWidget/components/ConditionWidget.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/plugins/conditionWidget/components/ConditionWidget.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
* 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.
 | 
			
		||||
*****************************************************************************/
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
<component :is="urlDefined ? 'a' : 'span'"
 | 
			
		||||
           class="c-condition-widget"
 | 
			
		||||
           :href="urlDefined ? internalDomainObject.url : null"
 | 
			
		||||
>
 | 
			
		||||
    <div class="c-condition-widget__label">
 | 
			
		||||
        {{ internalDomainObject.label }}
 | 
			
		||||
    </div>
 | 
			
		||||
</component>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
export default {
 | 
			
		||||
    inject: ['openmct', 'domainObject'],
 | 
			
		||||
    data: function () {
 | 
			
		||||
        return {
 | 
			
		||||
            internalDomainObject: this.domainObject
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    computed: {
 | 
			
		||||
        urlDefined() {
 | 
			
		||||
            return this.internalDomainObject.url && this.internalDomainObject.url.length > 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.unlisten = this.openmct.objects.observe(this.internalDomainObject, '*', this.updateInternalDomainObject);
 | 
			
		||||
    },
 | 
			
		||||
    beforeDestroy() {
 | 
			
		||||
        if (this.unlisten) {
 | 
			
		||||
            this.unlisten();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        updateInternalDomainObject(domainObject) {
 | 
			
		||||
            this.internalDomainObject = domainObject;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user