Compare commits
	
		
			26 Commits
		
	
	
		
			custom-col
			...
			fix-resize
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 74538dd00c | ||
|   | 63f9cd449f | ||
|   | 54220f547b | ||
|   | 93d967c2b3 | ||
|   | 1226459c6f | ||
|   | d7c9c9cb98 | ||
|   | 2131ef2397 | ||
|   | 48c22369a1 | ||
|   | 6506077f4d | ||
|   | b1b4266ff3 | ||
|   | 42b0148f93 | ||
|   | 9461ad8edd | ||
|   | 40055ba955 | ||
|   | 9cb85ad176 | ||
|   | f2b2953a5d | ||
|   | 62de310686 | ||
|   | 4b9ff67e49 | ||
|   | d5e32ec494 | ||
|   | 38880ba3d1 | ||
|   | a99ce7733c | ||
|   | 9f48764210 | ||
|   | a1aaa0dd41 | ||
|   | bee15e98c8 | ||
|   | 092bbe547d | ||
|   | 6cbe05317c | ||
|   | 3b92fcdf6c | 
| @@ -56,14 +56,38 @@ workflows: | ||||
|           browser: ChromeHeadless | ||||
|           always-pass: false | ||||
|       - test: | ||||
|           name: node12-firefoxESR | ||||
|           name: node12-firefoxESR-build-only | ||||
|           node-version: lts/erbium | ||||
|           browser: FirefoxESR | ||||
|           always-pass: true | ||||
|       - test: | ||||
|           name: node14-chrome | ||||
|           name: node14-chrome-build-only | ||||
|           node-version: lts/fermium | ||||
|           browser: ChromeHeadless | ||||
|           always-pass: true | ||||
|   nightly: | ||||
|     jobs: | ||||
|       - test: | ||||
|           name: node10-chrome-nightly | ||||
|           node-version: lts/dubnium | ||||
|           browser: ChromeHeadless | ||||
|           always-pass: false | ||||
|       - test: | ||||
|           name: node12-firefoxESR-nightly | ||||
|           node-version: lts/erbium | ||||
|           browser: FirefoxESR | ||||
|           always-pass: false | ||||
|       - test: | ||||
|           name: node14-chrome-nightly | ||||
|           node-version: lts/fermium | ||||
|           browser: ChromeHeadless | ||||
|           always-pass: false | ||||
|     triggers: | ||||
|       - schedule: | ||||
|           cron: "0 0 * * *" | ||||
|           filters: | ||||
|             branches: | ||||
|               only: | ||||
|                 - master       | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										33
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
|  | ||||
| name: "CodeQL" | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     branches: [ master ] | ||||
|   schedule: | ||||
|     - cron: '28 21 * * 3' | ||||
|  | ||||
| jobs: | ||||
|   analyze: | ||||
|     name: Analyze | ||||
|     runs-on: ubuntu-latest | ||||
|     permissions: | ||||
|       actions: read | ||||
|       contents: read | ||||
|       security-events: write | ||||
|  | ||||
|     steps: | ||||
|     - name: Checkout repository | ||||
|       uses: actions/checkout@v2 | ||||
|  | ||||
|     # Initializes the CodeQL tools for scanning. | ||||
|     - name: Initialize CodeQL | ||||
|       uses: github/codeql-action/init@v1 | ||||
|       with: | ||||
|         languages: javascript | ||||
|  | ||||
|     - name: Autobuild | ||||
|       uses: github/codeql-action/autobuild@v1 | ||||
|  | ||||
|     - name: Perform CodeQL Analysis | ||||
|       uses: github/codeql-action/analyze@v1 | ||||
							
								
								
									
										2
									
								
								API.md
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								API.md
									
									
									
									
									
								
							| @@ -996,7 +996,7 @@ reveal additional information when the mouse cursor is hovered over it. | ||||
| A common use case for indicators is to convey the state of some external system such as a  | ||||
| persistence backend or HTTP server. So long as this system is accessible via HTTP request,  | ||||
| Open MCT provides a general purpose indicator to show whether the server is available and  | ||||
| returing a 2xx status code. The URL Status Indicator is made available as a default plugin. See | ||||
| returning a 2xx status code. The URL Status Indicator is made available as a default plugin. See | ||||
| the [documentation](./src/plugins/URLIndicatorPlugin) for details on how to install and configure the  | ||||
| URL Status Indicator. | ||||
|  | ||||
|   | ||||
| @@ -423,7 +423,7 @@ which can help with this, however. | ||||
|   instead of separate approaches for static and substitutable | ||||
|   dependencies. | ||||
| * Removes need to understand Angular's DI mechanism. | ||||
| * Improves useability of documentation (`typeService` is an | ||||
| * Improves usability of documentation (`typeService` is an | ||||
|   instance of `CompositeService` and implements `TypeService` | ||||
|   so you can easily traverse links in the JSDoc.) | ||||
| * Can be used more easily from Web Workers, allowing services | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
| ## Legacy Documentation | ||||
|  | ||||
| As we transition to a new API, the following documentation for the old API | ||||
| (which is supported during the transtion) may be useful as well: | ||||
| (which is supported during the transition) may be useful as well: | ||||
|  | ||||
|  * The [Architecture Overview](architecture/) describes the concepts used | ||||
|  throughout Open MCT, and gives a high level overview of the platform's design. | ||||
|   | ||||
| @@ -152,7 +152,7 @@ | ||||
|         <h2>How to Use Glyphs</h2> | ||||
|         <div class="cols cols1-1"> | ||||
|             <div class="col"> | ||||
|                 <p>The easiest way to use a glyph is to include its CSS class in an element. The CSS adds a psuedo <code>:before</code> HTML element to whatever element it's attached to that makes proper use of the symbols font.</p> | ||||
|                 <p>The easiest way to use a glyph is to include its CSS class in an element. The CSS adds a pseudo <code>:before</code> HTML element to whatever element it's attached to that makes proper use of the symbols font.</p> | ||||
|                 <p>Alternately, you can use the <code>.ui-symbol</code> class in an object that contains encoded HTML entities. This method is only recommended if you cannot use the aforementioned CSS class approach.</p> | ||||
|             </div> | ||||
|             <mct-example><a class="s-button icon-gear" title="Settings"></a> | ||||
|   | ||||
| @@ -195,6 +195,7 @@ | ||||
|             ['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked'], | ||||
|             {indicator: true} | ||||
|         )); | ||||
|         openmct.install(openmct.plugins.Clock({ enableClockIndicator: true })); | ||||
|         openmct.start(); | ||||
|     </script> | ||||
| </html> | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "openmct", | ||||
|   "version": "1.7.6-SNAPSHOT", | ||||
|   "version": "1.7.8-SNAPSHOT", | ||||
|   "description": "The Open MCT core platform", | ||||
|   "dependencies": {}, | ||||
|   "devDependencies": { | ||||
|   | ||||
| @@ -64,7 +64,7 @@ define( | ||||
|          * | ||||
|          * @param {DomainObject} domainObject the domain object to navigate to | ||||
|          * @param {Boolean} force if true, force navigation to occur. | ||||
|          * @returns {Boolean} true if navigation occured, otherwise false. | ||||
|          * @returns {Boolean} true if navigation occurred, otherwise false. | ||||
|          */ | ||||
|         NavigationService.prototype.setNavigation = function (domainObject, force) { | ||||
|             if (force) { | ||||
|   | ||||
| @@ -21,28 +21,14 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     "./src/MCTDevice", | ||||
|     "./src/AgentService", | ||||
|     "./src/DeviceClassifier" | ||||
|     "./src/AgentService" | ||||
| ], function ( | ||||
|     MCTDevice, | ||||
|     AgentService, | ||||
|     DeviceClassifier | ||||
|     AgentService | ||||
| ) { | ||||
|  | ||||
|     return { | ||||
|         name: "platform/commonUI/mobile", | ||||
|         definition: { | ||||
|             "extensions": { | ||||
|                 "directives": [ | ||||
|                     { | ||||
|                         "key": "mctDevice", | ||||
|                         "implementation": MCTDevice, | ||||
|                         "depends": [ | ||||
|                             "agentService" | ||||
|                         ] | ||||
|                     } | ||||
|                 ], | ||||
|                 "services": [ | ||||
|                     { | ||||
|                         "key": "agentService", | ||||
| @@ -51,15 +37,6 @@ define([ | ||||
|                             "$window" | ||||
|                         ] | ||||
|                     } | ||||
|                 ], | ||||
|                 "runs": [ | ||||
|                     { | ||||
|                         "implementation": DeviceClassifier, | ||||
|                         "depends": [ | ||||
|                             "agentService", | ||||
|                             "$document" | ||||
|                         ] | ||||
|                     } | ||||
|                 ] | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -20,122 +20,12 @@ | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /** | ||||
|  * Provides features which support variant behavior on mobile devices. | ||||
|  * | ||||
|  * @namespace platform/commonUI/mobile | ||||
|  */ | ||||
| define( | ||||
|     [], | ||||
|     function () { | ||||
| define(["../../../../src/utils/agent/Agent.js"], function (Agent) { | ||||
|     function AngularAgentServiceWrapper(window) { | ||||
|         const AS = Agent.default; | ||||
|  | ||||
|         /** | ||||
|          * The query service handles calls for browser and userAgent | ||||
|          * info using a comparison between the userAgent and key | ||||
|          * device names | ||||
|          * @constructor | ||||
|          * @param $window Angular-injected instance of the window | ||||
|          * @memberof platform/commonUI/mobile | ||||
|          */ | ||||
|         function AgentService($window) { | ||||
|             var userAgent = $window.navigator.userAgent, | ||||
|                 matches = userAgent.match(/iPad|iPhone|Android/i) || []; | ||||
|  | ||||
|             this.userAgent = userAgent; | ||||
|             this.mobileName = matches[0]; | ||||
|             this.$window = $window; | ||||
|             this.touchEnabled = ($window.ontouchstart !== undefined); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Check if the user is on a mobile device. | ||||
|          * @returns {boolean} true on mobile | ||||
|          */ | ||||
|         AgentService.prototype.isMobile = function () { | ||||
|             return Boolean(this.mobileName); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Check if the user is on a phone-sized mobile device. | ||||
|          * @returns {boolean} true on a phone | ||||
|          */ | ||||
|         AgentService.prototype.isPhone = function () { | ||||
|             if (this.isMobile()) { | ||||
|                 if (this.isAndroidTablet()) { | ||||
|                     return false; | ||||
|                 } else if (this.mobileName === 'iPad') { | ||||
|                     return false; | ||||
|                 } else { | ||||
|                     return true; | ||||
|                 } | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Check if the user is on a tablet sized android device | ||||
|          * @returns {boolean} true on an android tablet | ||||
|          */ | ||||
|         AgentService.prototype.isAndroidTablet = function () { | ||||
|             if (this.mobileName === 'Android') { | ||||
|                 if (this.isPortrait() && window.innerWidth >= 768) { | ||||
|                     return true; | ||||
|                 } else if (this.isLandscape() && window.innerHeight >= 768) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Check if the user is on a tablet-sized mobile device. | ||||
|          * @returns {boolean} true on a tablet | ||||
|          */ | ||||
|         AgentService.prototype.isTablet = function () { | ||||
|             return (this.isMobile() && !this.isPhone() && this.mobileName !== 'Android') || (this.isMobile() && this.isAndroidTablet()); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Check if the user's device is in a portrait-style | ||||
|          * orientation (display width is narrower than display height.) | ||||
|          * @returns {boolean} true in portrait mode | ||||
|          */ | ||||
|         AgentService.prototype.isPortrait = function () { | ||||
|             return this.$window.innerWidth < this.$window.innerHeight; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Check if the user's device is in a landscape-style | ||||
|          * orientation (display width is greater than display height.) | ||||
|          * @returns {boolean} true in landscape mode | ||||
|          */ | ||||
|         AgentService.prototype.isLandscape = function () { | ||||
|             return !this.isPortrait(); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Check if the user's device supports a touch interface. | ||||
|          * @returns {boolean} true if touch is supported | ||||
|          */ | ||||
|         AgentService.prototype.isTouch = function () { | ||||
|             return this.touchEnabled; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Check if the user agent matches a certain named device, | ||||
|          * as indicated by checking for a case-insensitive substring | ||||
|          * match. | ||||
|          * @param {string} name the name to check for | ||||
|          * @returns {boolean} true if the user agent includes that name | ||||
|          */ | ||||
|         AgentService.prototype.isBrowser = function (name) { | ||||
|             name = name.toLowerCase(); | ||||
|  | ||||
|             return this.userAgent.toLowerCase().indexOf(name) !== -1; | ||||
|         }; | ||||
|  | ||||
|         return AgentService; | ||||
|         return new AS(window); | ||||
|     } | ||||
| ); | ||||
|  | ||||
|     return AngularAgentServiceWrapper; | ||||
| }); | ||||
|   | ||||
							
								
								
									
										96
									
								
								platform/commonUI/mobile/src/AgentServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								platform/commonUI/mobile/src/AgentServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| import AgentService from "./AgentService"; | ||||
|  | ||||
| const TEST_USER_AGENTS = { | ||||
|     DESKTOP: | ||||
|     "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36", | ||||
|     IPAD: | ||||
|     "Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53", | ||||
|     IPHONE: | ||||
|     "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53" | ||||
| }; | ||||
|  | ||||
| describe("The AgentService", function () { | ||||
|     let testWindow; | ||||
|     let agentService; | ||||
|  | ||||
|     beforeEach(function () { | ||||
|         testWindow = { | ||||
|             innerWidth: 640, | ||||
|             innerHeight: 480, | ||||
|             navigator: { | ||||
|                 userAgent: TEST_USER_AGENTS.DESKTOP | ||||
|             } | ||||
|         }; | ||||
|     }); | ||||
|  | ||||
|     it("recognizes desktop devices as non-mobile", function () { | ||||
|         testWindow.navigator.userAgent = TEST_USER_AGENTS.DESKTOP; | ||||
|         agentService = new AgentService(testWindow); | ||||
|         expect(agentService.isMobile()).toBeFalsy(); | ||||
|         expect(agentService.isPhone()).toBeFalsy(); | ||||
|         expect(agentService.isTablet()).toBeFalsy(); | ||||
|     }); | ||||
|  | ||||
|     it("detects iPhones", function () { | ||||
|         testWindow.navigator.userAgent = TEST_USER_AGENTS.IPHONE; | ||||
|         agentService = new AgentService(testWindow); | ||||
|         expect(agentService.isMobile()).toBeTruthy(); | ||||
|         expect(agentService.isPhone()).toBeTruthy(); | ||||
|         expect(agentService.isTablet()).toBeFalsy(); | ||||
|     }); | ||||
|  | ||||
|     it("detects iPads", function () { | ||||
|         testWindow.navigator.userAgent = TEST_USER_AGENTS.IPAD; | ||||
|         agentService = new AgentService(testWindow); | ||||
|         expect(agentService.isMobile()).toBeTruthy(); | ||||
|         expect(agentService.isPhone()).toBeFalsy(); | ||||
|         expect(agentService.isTablet()).toBeTruthy(); | ||||
|     }); | ||||
|  | ||||
|     it("detects display orientation", function () { | ||||
|         agentService = new AgentService(testWindow); | ||||
|         testWindow.innerWidth = 1024; | ||||
|         testWindow.innerHeight = 400; | ||||
|         expect(agentService.isPortrait()).toBeFalsy(); | ||||
|         expect(agentService.isLandscape()).toBeTruthy(); | ||||
|         testWindow.innerWidth = 400; | ||||
|         testWindow.innerHeight = 1024; | ||||
|         expect(agentService.isPortrait()).toBeTruthy(); | ||||
|         expect(agentService.isLandscape()).toBeFalsy(); | ||||
|     }); | ||||
|  | ||||
|     it("detects touch support", function () { | ||||
|         testWindow.ontouchstart = null; | ||||
|         expect(new AgentService(testWindow).isTouch()).toBe(true); | ||||
|         delete testWindow.ontouchstart; | ||||
|         expect(new AgentService(testWindow).isTouch()).toBe(false); | ||||
|     }); | ||||
|  | ||||
|     it("allows for checking browser type", function () { | ||||
|         testWindow.navigator.userAgent = "Chromezilla Safarifox"; | ||||
|         agentService = new AgentService(testWindow); | ||||
|         expect(agentService.isBrowser("Chrome")).toBe(true); | ||||
|         expect(agentService.isBrowser("Firefox")).toBe(false); | ||||
|     }); | ||||
| }); | ||||
| @@ -1,72 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ['./DeviceMatchers'], | ||||
|     function (DeviceMatchers) { | ||||
|  | ||||
|         /** | ||||
|          * Runs at application startup and adds a subset of the following | ||||
|          * CSS classes to the body of the document, depending on device | ||||
|          * attributes: | ||||
|          * | ||||
|          * * `mobile`: Phones or tablets. | ||||
|          * * `phone`: Phones specifically. | ||||
|          * * `tablet`: Tablets specifically. | ||||
|          * * `desktop`: Non-mobile devices. | ||||
|          * * `portrait`: Devices in a portrait-style orientation. | ||||
|          * * `landscape`: Devices in a landscape-style orientation. | ||||
|          * * `touch`: Device supports touch events. | ||||
|          * | ||||
|          * @param {platform/commonUI/mobile.AgentService} agentService | ||||
|          *        the service used to examine the user agent | ||||
|          * @param $document Angular's jqLite-wrapped document element | ||||
|          * @constructor | ||||
|          */ | ||||
|         function MobileClassifier(agentService, $document) { | ||||
|             var body = $document.find('body'); | ||||
|  | ||||
|             Object.keys(DeviceMatchers).forEach(function (key, index, array) { | ||||
|                 if (DeviceMatchers[key](agentService)) { | ||||
|                     body.addClass(key); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             if (agentService.isMobile()) { | ||||
|                 var mediaQuery = window.matchMedia('(orientation: landscape)'); | ||||
|  | ||||
|                 mediaQuery.addListener(function (event) { | ||||
|                     if (event.matches) { | ||||
|                         body.removeClass('portrait'); | ||||
|                         body.addClass('landscape'); | ||||
|                     } else { | ||||
|                         body.removeClass('landscape'); | ||||
|                         body.addClass('portrait'); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return MobileClassifier; | ||||
|  | ||||
|     } | ||||
| ); | ||||
| @@ -1,88 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ['./DeviceMatchers'], | ||||
|     function (DeviceMatchers) { | ||||
|  | ||||
|         /** | ||||
|          * The `mct-device` directive, when applied as an attribute, | ||||
|          * only includes the element when the device being used matches | ||||
|          * a set of characteristics required. | ||||
|          * | ||||
|          * Required characteristics are given as space-separated strings | ||||
|          * as the value to this attribute, e.g.: | ||||
|          * | ||||
|          *    <span mct-device="mobile portrait">Hello world!</span> | ||||
|          * | ||||
|          * ...will only show Hello world! when viewed on a mobile device | ||||
|          * in the portrait orientation. | ||||
|          * | ||||
|          * Valid device characteristics to detect are: | ||||
|          * | ||||
|          * * `mobile`: Phones or tablets. | ||||
|          * * `phone`: Phones specifically. | ||||
|          * * `tablet`: Tablets specifically. | ||||
|          * * `desktop`: Non-mobile devices. | ||||
|          * * `portrait`: Devices in a portrait-style orientation. | ||||
|          * * `landscape`: Devices in a landscape-style orientation. | ||||
|          * * `touch`: Device supports touch events. | ||||
|          * | ||||
|          * @param {AgentService} agentService used to detect device type | ||||
|          *        based on information about the user agent | ||||
|          */ | ||||
|         function MCTDevice(agentService) { | ||||
|  | ||||
|             function deviceMatches(tokens) { | ||||
|                 tokens = tokens || ""; | ||||
|  | ||||
|                 return tokens.split(" ").every(function (token) { | ||||
|                     var fn = DeviceMatchers[token]; | ||||
|  | ||||
|                     return fn && fn(agentService); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             function link(scope, element, attrs, ctrl, transclude) { | ||||
|                 if (deviceMatches(attrs.mctDevice)) { | ||||
|                     transclude(function (clone) { | ||||
|                         element.replaceWith(clone); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return { | ||||
|                 link: link, | ||||
|                 // We are transcluding the whole element (like ng-if) | ||||
|                 transclude: 'element', | ||||
|                 // 1 more than ng-if | ||||
|                 priority: 601, | ||||
|                 // Also terminal, since element will be transcluded | ||||
|                 terminal: true, | ||||
|                 // Only apply as an attribute | ||||
|                 restrict: "A" | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         return MCTDevice; | ||||
|     } | ||||
| ); | ||||
| @@ -1,99 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ["../src/AgentService"], | ||||
|     function (AgentService) { | ||||
|  | ||||
|         var TEST_USER_AGENTS = { | ||||
|             DESKTOP: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36", | ||||
|             IPAD: "Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53", | ||||
|             IPHONE: "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53" | ||||
|         }; | ||||
|  | ||||
|         describe("The AgentService", function () { | ||||
|             var testWindow, agentService; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 testWindow = { | ||||
|                     innerWidth: 640, | ||||
|                     innerHeight: 480, | ||||
|                     navigator: { | ||||
|                         userAgent: TEST_USER_AGENTS.DESKTOP | ||||
|                     } | ||||
|                 }; | ||||
|             }); | ||||
|  | ||||
|             it("recognizes desktop devices as non-mobile", function () { | ||||
|                 testWindow.navigator.userAgent = TEST_USER_AGENTS.DESKTOP; | ||||
|                 agentService = new AgentService(testWindow); | ||||
|                 expect(agentService.isMobile()).toBeFalsy(); | ||||
|                 expect(agentService.isPhone()).toBeFalsy(); | ||||
|                 expect(agentService.isTablet()).toBeFalsy(); | ||||
|             }); | ||||
|  | ||||
|             it("detects iPhones", function () { | ||||
|                 testWindow.navigator.userAgent = TEST_USER_AGENTS.IPHONE; | ||||
|                 agentService = new AgentService(testWindow); | ||||
|                 expect(agentService.isMobile()).toBeTruthy(); | ||||
|                 expect(agentService.isPhone()).toBeTruthy(); | ||||
|                 expect(agentService.isTablet()).toBeFalsy(); | ||||
|             }); | ||||
|  | ||||
|             it("detects iPads", function () { | ||||
|                 testWindow.navigator.userAgent = TEST_USER_AGENTS.IPAD; | ||||
|                 agentService = new AgentService(testWindow); | ||||
|                 expect(agentService.isMobile()).toBeTruthy(); | ||||
|                 expect(agentService.isPhone()).toBeFalsy(); | ||||
|                 expect(agentService.isTablet()).toBeTruthy(); | ||||
|             }); | ||||
|  | ||||
|             it("detects display orientation", function () { | ||||
|                 agentService = new AgentService(testWindow); | ||||
|                 testWindow.innerWidth = 1024; | ||||
|                 testWindow.innerHeight = 400; | ||||
|                 expect(agentService.isPortrait()).toBeFalsy(); | ||||
|                 expect(agentService.isLandscape()).toBeTruthy(); | ||||
|                 testWindow.innerWidth = 400; | ||||
|                 testWindow.innerHeight = 1024; | ||||
|                 expect(agentService.isPortrait()).toBeTruthy(); | ||||
|                 expect(agentService.isLandscape()).toBeFalsy(); | ||||
|             }); | ||||
|  | ||||
|             it("detects touch support", function () { | ||||
|                 testWindow.ontouchstart = null; | ||||
|                 expect(new AgentService(testWindow).isTouch()) | ||||
|                     .toBe(true); | ||||
|                 delete testWindow.ontouchstart; | ||||
|                 expect(new AgentService(testWindow).isTouch()) | ||||
|                     .toBe(false); | ||||
|             }); | ||||
|  | ||||
|             it("allows for checking browser type", function () { | ||||
|                 testWindow.navigator.userAgent = "Chromezilla Safarifox"; | ||||
|                 agentService = new AgentService(testWindow); | ||||
|                 expect(agentService.isBrowser("Chrome")).toBe(true); | ||||
|                 expect(agentService.isBrowser("Firefox")).toBe(false); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -1,109 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ["../src/DeviceClassifier", "../src/DeviceMatchers"], | ||||
|     function (DeviceClassifier, DeviceMatchers) { | ||||
|  | ||||
|         var AGENT_SERVICE_METHODS = [ | ||||
|                 'isMobile', | ||||
|                 'isPhone', | ||||
|                 'isTablet', | ||||
|                 'isPortrait', | ||||
|                 'isLandscape', | ||||
|                 'isTouch' | ||||
|             ], | ||||
|             TEST_PERMUTATIONS = [ | ||||
|                 ['isMobile', 'isPhone', 'isTouch', 'isPortrait'], | ||||
|                 ['isMobile', 'isPhone', 'isTouch', 'isLandscape'], | ||||
|                 ['isMobile', 'isTablet', 'isTouch', 'isPortrait'], | ||||
|                 ['isMobile', 'isTablet', 'isTouch', 'isLandscape'], | ||||
|                 ['isTouch'], | ||||
|                 [] | ||||
|             ]; | ||||
|  | ||||
|         describe("DeviceClassifier", function () { | ||||
|             var mockAgentService, | ||||
|                 mockDocument, | ||||
|                 mockBody; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockAgentService = jasmine.createSpyObj( | ||||
|                     'agentService', | ||||
|                     AGENT_SERVICE_METHODS | ||||
|                 ); | ||||
|                 mockDocument = jasmine.createSpyObj( | ||||
|                     '$document', | ||||
|                     ['find'] | ||||
|                 ); | ||||
|                 mockBody = jasmine.createSpyObj( | ||||
|                     'body', | ||||
|                     ['addClass'] | ||||
|                 ); | ||||
|                 mockDocument.find.and.callFake(function (sel) { | ||||
|                     return sel === 'body' && mockBody; | ||||
|                 }); | ||||
|                 AGENT_SERVICE_METHODS.forEach(function (m) { | ||||
|                     mockAgentService[m].and.returnValue(false); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             TEST_PERMUTATIONS.forEach(function (trueMethods) { | ||||
|                 var summary = trueMethods.length === 0 | ||||
|                     ? "device has no detected characteristics" | ||||
|                     : "device " + (trueMethods.join(", ")); | ||||
|  | ||||
|                 describe("when " + summary, function () { | ||||
|                     var classifier; // eslint-disable-line | ||||
|  | ||||
|                     beforeEach(function () { | ||||
|                         trueMethods.forEach(function (m) { | ||||
|                             mockAgentService[m].and.returnValue(true); | ||||
|                         }); | ||||
|                         classifier = new DeviceClassifier( | ||||
|                             mockAgentService, | ||||
|                             mockDocument | ||||
|                         ); | ||||
|                     }); | ||||
|  | ||||
|                     it("adds classes for matching, detected characteristics", function () { | ||||
|                         Object.keys(DeviceMatchers).filter(function (m) { | ||||
|                             return DeviceMatchers[m](mockAgentService); | ||||
|                         }).forEach(function (key) { | ||||
|                             expect(mockBody.addClass) | ||||
|                                 .toHaveBeenCalledWith(key); | ||||
|                         }); | ||||
|                     }); | ||||
|  | ||||
|                     it("does not add classes for non-matching characteristics", function () { | ||||
|                         Object.keys(DeviceMatchers).filter(function (m) { | ||||
|                             return !DeviceMatchers[m](mockAgentService); | ||||
|                         }).forEach(function (key) { | ||||
|                             expect(mockBody.addClass) | ||||
|                                 .not.toHaveBeenCalledWith(key); | ||||
|                         }); | ||||
|                     }); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -1,78 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ["../src/DeviceMatchers"], | ||||
|     function (DeviceMatchers) { | ||||
|  | ||||
|         describe("DeviceMatchers", function () { | ||||
|             var mockAgentService; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockAgentService = jasmine.createSpyObj( | ||||
|                     'agentService', | ||||
|                     [ | ||||
|                         'isMobile', | ||||
|                         'isPhone', | ||||
|                         'isTablet', | ||||
|                         'isPortrait', | ||||
|                         'isLandscape', | ||||
|                         'isTouch' | ||||
|                     ] | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("detects when a device is a desktop device", function () { | ||||
|                 mockAgentService.isMobile.and.returnValue(false); | ||||
|                 expect(DeviceMatchers.desktop(mockAgentService)) | ||||
|                     .toBe(true); | ||||
|                 mockAgentService.isMobile.and.returnValue(true); | ||||
|                 expect(DeviceMatchers.desktop(mockAgentService)) | ||||
|                     .toBe(false); | ||||
|             }); | ||||
|  | ||||
|             function method(deviceType) { | ||||
|                 return "is" + deviceType[0].toUpperCase() + deviceType.slice(1); | ||||
|             } | ||||
|  | ||||
|             [ | ||||
|                 "mobile", | ||||
|                 "phone", | ||||
|                 "tablet", | ||||
|                 "landscape", | ||||
|                 "portrait", | ||||
|                 "landscape", | ||||
|                 "touch" | ||||
|             ].forEach(function (deviceType) { | ||||
|                 it("detects when a device is a " + deviceType + " device", function () { | ||||
|                     mockAgentService[method(deviceType)].and.returnValue(true); | ||||
|                     expect(DeviceMatchers[deviceType](mockAgentService)) | ||||
|                         .toBe(true); | ||||
|                     mockAgentService[method(deviceType)].and.returnValue(false); | ||||
|                     expect(DeviceMatchers[deviceType](mockAgentService)) | ||||
|                         .toBe(false); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -1,168 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ['../src/MCTDevice'], | ||||
|     function (MCTDevice) { | ||||
|  | ||||
|         var JQLITE_METHODS = ['replaceWith']; | ||||
|  | ||||
|         describe("The mct-device directive", function () { | ||||
|             var mockAgentService, | ||||
|                 mockTransclude, | ||||
|                 mockElement, | ||||
|                 mockClone, | ||||
|                 testAttrs, | ||||
|                 directive; | ||||
|  | ||||
|             function link() { | ||||
|                 directive.link(null, mockElement, testAttrs, null, mockTransclude); | ||||
|             } | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockAgentService = jasmine.createSpyObj( | ||||
|                     "agentService", | ||||
|                     ["isMobile", "isPhone", "isTablet", "isPortrait", "isLandscape"] | ||||
|                 ); | ||||
|                 mockTransclude = jasmine.createSpy("$transclude"); | ||||
|                 mockElement = jasmine.createSpyObj(name, JQLITE_METHODS); | ||||
|                 mockClone = jasmine.createSpyObj(name, JQLITE_METHODS); | ||||
|  | ||||
|                 mockTransclude.and.callFake(function (fn) { | ||||
|                     fn(mockClone); | ||||
|                 }); | ||||
|  | ||||
|                 // Look desktop-like by default | ||||
|                 mockAgentService.isLandscape.and.returnValue(true); | ||||
|  | ||||
|                 testAttrs = {}; | ||||
|  | ||||
|                 directive = new MCTDevice(mockAgentService); | ||||
|             }); | ||||
|  | ||||
|             function expectInclusion() { | ||||
|                 expect(mockElement.replaceWith) | ||||
|                     .toHaveBeenCalledWith(mockClone); | ||||
|             } | ||||
|  | ||||
|             function expectExclusion() { | ||||
|                 expect(mockElement.replaceWith).not.toHaveBeenCalled(); | ||||
|             } | ||||
|  | ||||
|             it("is applicable at the attribute level", function () { | ||||
|                 expect(directive.restrict).toEqual("A"); | ||||
|             }); | ||||
|  | ||||
|             it("transcludes at the element level", function () { | ||||
|                 expect(directive.transclude).toEqual('element'); | ||||
|             }); | ||||
|  | ||||
|             it("has a greater priority number than ng-if", function () { | ||||
|                 expect(directive.priority > 600).toBeTruthy(); | ||||
|             }); | ||||
|  | ||||
|             it("restricts element inclusion for mobile devices", function () { | ||||
|                 testAttrs.mctDevice = "mobile"; | ||||
|                 link(); | ||||
|                 expectExclusion(); | ||||
|  | ||||
|                 mockAgentService.isMobile.and.returnValue(true); | ||||
|                 link(); | ||||
|                 expectInclusion(); | ||||
|             }); | ||||
|  | ||||
|             it("restricts element inclusion for tablet devices", function () { | ||||
|                 testAttrs.mctDevice = "tablet"; | ||||
|                 mockAgentService.isMobile.and.returnValue(true); | ||||
|                 link(); | ||||
|                 expectExclusion(); | ||||
|  | ||||
|                 mockAgentService.isTablet.and.returnValue(true); | ||||
|                 link(); | ||||
|                 expectInclusion(); | ||||
|             }); | ||||
|  | ||||
|             it("restricts element inclusion for phone devices", function () { | ||||
|                 testAttrs.mctDevice = "phone"; | ||||
|                 mockAgentService.isMobile.and.returnValue(true); | ||||
|                 link(); | ||||
|                 expectExclusion(); | ||||
|  | ||||
|                 mockAgentService.isPhone.and.returnValue(true); | ||||
|                 link(); | ||||
|                 expectInclusion(); | ||||
|             }); | ||||
|  | ||||
|             it("restricts element inclusion for desktop devices", function () { | ||||
|                 testAttrs.mctDevice = "desktop"; | ||||
|                 mockAgentService.isMobile.and.returnValue(true); | ||||
|                 link(); | ||||
|                 expectExclusion(); | ||||
|  | ||||
|                 mockAgentService.isMobile.and.returnValue(false); | ||||
|                 link(); | ||||
|                 expectInclusion(); | ||||
|             }); | ||||
|  | ||||
|             it("restricts element inclusion for portrait orientation", function () { | ||||
|                 testAttrs.mctDevice = "portrait"; | ||||
|                 link(); | ||||
|                 expectExclusion(); | ||||
|  | ||||
|                 mockAgentService.isPortrait.and.returnValue(true); | ||||
|                 link(); | ||||
|                 expectInclusion(); | ||||
|             }); | ||||
|  | ||||
|             it("restricts element inclusion for landscape orientation", function () { | ||||
|                 testAttrs.mctDevice = "landscape"; | ||||
|                 mockAgentService.isLandscape.and.returnValue(false); | ||||
|                 mockAgentService.isPortrait.and.returnValue(true); | ||||
|                 link(); | ||||
|                 expectExclusion(); | ||||
|  | ||||
|                 mockAgentService.isLandscape.and.returnValue(true); | ||||
|                 link(); | ||||
|                 expectInclusion(); | ||||
|             }); | ||||
|  | ||||
|             it("allows multiple device characteristics to be requested", function () { | ||||
|                 // Won't try to test every permutation here, just | ||||
|                 // make sure the multi-characteristic feature has support. | ||||
|                 testAttrs.mctDevice = "portrait mobile"; | ||||
|                 link(); | ||||
|                 // Neither portrait nor mobile, not called | ||||
|                 expectExclusion(); | ||||
|  | ||||
|                 mockAgentService.isPortrait.and.returnValue(true); | ||||
|                 link(); | ||||
|  | ||||
|                 // Was portrait, but not mobile, so no | ||||
|                 expectExclusion(); | ||||
|  | ||||
|                 mockAgentService.isMobile.and.returnValue(true); | ||||
|                 link(); | ||||
|                 expectInclusion(); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -379,7 +379,7 @@ define([ | ||||
|                     { | ||||
|                         "name": "Math.uuid.js", | ||||
|                         "version": "1.4.7", | ||||
|                         "description": "Unique identifer generation (code adapted.)", | ||||
|                         "description": "Unique identifier generation (code adapted.)", | ||||
|                         "author": "Robert Kieffer", | ||||
|                         "website": "https://github.com/broofa/node-uuid", | ||||
|                         "copyright": "Copyright (c) 2010-2012 Robert Kieffer", | ||||
|   | ||||
| @@ -21,32 +21,24 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     "moment-timezone", | ||||
|     "./src/indicators/ClockIndicator", | ||||
|     "./src/services/TickerService", | ||||
|     "./src/services/TimerService", | ||||
|     "./src/controllers/ClockController", | ||||
|     "./src/controllers/TimerController", | ||||
|     "./src/controllers/RefreshingController", | ||||
|     "./src/actions/StartTimerAction", | ||||
|     "./src/actions/RestartTimerAction", | ||||
|     "./src/actions/StopTimerAction", | ||||
|     "./src/actions/PauseTimerAction", | ||||
|     "./res/templates/clock.html", | ||||
|     "./res/templates/timer.html" | ||||
| ], function ( | ||||
|     MomentTimezone, | ||||
|     ClockIndicator, | ||||
|     TickerService, | ||||
|     TimerService, | ||||
|     ClockController, | ||||
|     TimerController, | ||||
|     RefreshingController, | ||||
|     StartTimerAction, | ||||
|     RestartTimerAction, | ||||
|     StopTimerAction, | ||||
|     PauseTimerAction, | ||||
|     clockTemplate, | ||||
|     timerTemplate | ||||
| ) { | ||||
|     return { | ||||
| @@ -73,16 +65,6 @@ define([ | ||||
|                         "value": "YYYY/MM/DD HH:mm:ss" | ||||
|                     } | ||||
|                 ], | ||||
|                 "indicators": [ | ||||
|                     { | ||||
|                         "implementation": ClockIndicator, | ||||
|                         "depends": [ | ||||
|                             "tickerService", | ||||
|                             "CLOCK_INDICATOR_FORMAT" | ||||
|                         ], | ||||
|                         "priority": "preferred" | ||||
|                     } | ||||
|                 ], | ||||
|                 "services": [ | ||||
|                     { | ||||
|                         "key": "tickerService", | ||||
| @@ -99,14 +81,6 @@ define([ | ||||
|                     } | ||||
|                 ], | ||||
|                 "controllers": [ | ||||
|                     { | ||||
|                         "key": "ClockController", | ||||
|                         "implementation": ClockController, | ||||
|                         "depends": [ | ||||
|                             "$scope", | ||||
|                             "tickerService" | ||||
|                         ] | ||||
|                     }, | ||||
|                     { | ||||
|                         "key": "TimerController", | ||||
|                         "implementation": TimerController, | ||||
| @@ -126,12 +100,6 @@ define([ | ||||
|                     } | ||||
|                 ], | ||||
|                 "views": [ | ||||
|                     { | ||||
|                         "key": "clock", | ||||
|                         "type": "clock", | ||||
|                         "editable": false, | ||||
|                         "template": clockTemplate | ||||
|                     }, | ||||
|                     { | ||||
|                         "key": "timer", | ||||
|                         "type": "timer", | ||||
| @@ -186,70 +154,6 @@ define([ | ||||
|                     } | ||||
|                 ], | ||||
|                 "types": [ | ||||
|                     { | ||||
|                         "key": "clock", | ||||
|                         "name": "Clock", | ||||
|                         "cssClass": "icon-clock", | ||||
|                         "description": "A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.", | ||||
|                         "priority": 101, | ||||
|                         "features": [ | ||||
|                             "creation" | ||||
|                         ], | ||||
|                         "properties": [ | ||||
|                             { | ||||
|                                 "key": "clockFormat", | ||||
|                                 "name": "Display Format", | ||||
|                                 "control": "composite", | ||||
|                                 "items": [ | ||||
|                                     { | ||||
|                                         "control": "select", | ||||
|                                         "options": [ | ||||
|                                             { | ||||
|                                                 "value": "YYYY/MM/DD hh:mm:ss", | ||||
|                                                 "name": "YYYY/MM/DD hh:mm:ss" | ||||
|                                             }, | ||||
|                                             { | ||||
|                                                 "value": "YYYY/DDD hh:mm:ss", | ||||
|                                                 "name": "YYYY/DDD hh:mm:ss" | ||||
|                                             }, | ||||
|                                             { | ||||
|                                                 "value": "hh:mm:ss", | ||||
|                                                 "name": "hh:mm:ss" | ||||
|                                             } | ||||
|                                         ], | ||||
|                                         "cssClass": "l-inline" | ||||
|                                     }, | ||||
|                                     { | ||||
|                                         "control": "select", | ||||
|                                         "options": [ | ||||
|                                             { | ||||
|                                                 "value": "clock12", | ||||
|                                                 "name": "12hr" | ||||
|                                             }, | ||||
|                                             { | ||||
|                                                 "value": "clock24", | ||||
|                                                 "name": "24hr" | ||||
|                                             } | ||||
|                                         ], | ||||
|                                         "cssClass": "l-inline" | ||||
|                                     } | ||||
|                                 ] | ||||
|                             }, | ||||
|                             { | ||||
|                                 "key": "timezone", | ||||
|                                 "name": "Timezone", | ||||
|                                 "control": "autocomplete", | ||||
|                                 "options": MomentTimezone.tz.names() | ||||
|                             } | ||||
|                         ], | ||||
|                         "model": { | ||||
|                             "clockFormat": [ | ||||
|                                 "YYYY/MM/DD hh:mm:ss", | ||||
|                                 "clock12" | ||||
|                             ], | ||||
|                             "timezone": "UTC" | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         "key": "timer", | ||||
|                         "name": "Timer", | ||||
|   | ||||
| @@ -1,32 +0,0 @@ | ||||
| <!-- | ||||
|  Open MCT, Copyright (c) 2009-2016, United States Government | ||||
|  as represented by the Administrator of the National Aeronautics and Space | ||||
|  Administration. All rights reserved. | ||||
|  | ||||
|  Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  "License"); you may not use this file except in compliance with the License. | ||||
|  You may obtain a copy of the License at | ||||
|  http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  | ||||
|  Unless required by applicable law or agreed to in writing, software | ||||
|  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  License for the specific language governing permissions and limitations | ||||
|  under the License. | ||||
|  | ||||
|  Open MCT includes source code licensed under additional open source | ||||
|  licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div class="c-clock l-time-display u-style-receiver js-style-receiver" ng-controller="ClockController as clock"> | ||||
| 	<div class="c-clock__timezone"> | ||||
| 		{{clock.zone()}} | ||||
| 	</div> | ||||
| 	<div class="c-clock__value"> | ||||
| 		{{clock.text()}} | ||||
| 	</div> | ||||
| 	<div class="c-clock__ampm"> | ||||
| 		{{clock.ampm()}} | ||||
| 	</div> | ||||
| </div> | ||||
| @@ -1,110 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2009-2016, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     'moment', | ||||
|     'moment-timezone' | ||||
| ], | ||||
| function ( | ||||
|     moment, | ||||
|     momentTimezone | ||||
| ) { | ||||
|  | ||||
|     /** | ||||
|          * Controller for views of a Clock domain object. | ||||
|          * | ||||
|          * @constructor | ||||
|          * @memberof platform/features/clock | ||||
|          * @param {angular.Scope} $scope the Angular scope | ||||
|          * @param {platform/features/clock.TickerService} tickerService | ||||
|          *        a service used to align behavior with clock ticks | ||||
|          */ | ||||
|     function ClockController($scope, tickerService) { | ||||
|         var lastTimestamp, | ||||
|             unlisten, | ||||
|             timeFormat, | ||||
|             zoneName, | ||||
|             self = this; | ||||
|  | ||||
|         function update() { | ||||
|             var m = zoneName | ||||
|                 ? moment.utc(lastTimestamp).tz(zoneName) : moment.utc(lastTimestamp); | ||||
|             self.zoneAbbr = m.zoneAbbr(); | ||||
|             self.textValue = timeFormat && m.format(timeFormat); | ||||
|             self.ampmValue = m.format("A"); // Just the AM or PM part | ||||
|         } | ||||
|  | ||||
|         function tick(timestamp) { | ||||
|             lastTimestamp = timestamp; | ||||
|             update(); | ||||
|         } | ||||
|  | ||||
|         function updateModel(model) { | ||||
|             var baseFormat; | ||||
|             if (model !== undefined) { | ||||
|                 baseFormat = model.clockFormat[0]; | ||||
|  | ||||
|                 self.use24 = model.clockFormat[1] === 'clock24'; | ||||
|                 timeFormat = self.use24 | ||||
|                     ? baseFormat.replace('hh', "HH") : baseFormat; | ||||
|                 // If wrong timezone is provided, the UTC will be used | ||||
|                 zoneName = momentTimezone.tz.names().includes(model.timezone) | ||||
|                     ? model.timezone : "UTC"; | ||||
|                 update(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Pull in the model (clockFormat and timezone) from the domain object model | ||||
|         $scope.$watch('model', updateModel); | ||||
|  | ||||
|         // Listen for clock ticks ... and stop listening on destroy | ||||
|         unlisten = tickerService.listen(tick); | ||||
|         $scope.$on('$destroy', unlisten); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|          * Get the clock's time zone, as displayable text. | ||||
|          * @returns {string} | ||||
|          */ | ||||
|     ClockController.prototype.zone = function () { | ||||
|         return this.zoneAbbr; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|          * Get the current time, as displayable text. | ||||
|          * @returns {string} | ||||
|          */ | ||||
|     ClockController.prototype.text = function () { | ||||
|         return this.textValue; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|          * Get the text to display to qualify a time as AM or PM. | ||||
|          * @returns {string} | ||||
|          */ | ||||
|     ClockController.prototype.ampm = function () { | ||||
|         return this.use24 ? '' : this.ampmValue; | ||||
|     }; | ||||
|  | ||||
|     return ClockController; | ||||
| } | ||||
| ); | ||||
| @@ -1,65 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2009-2016, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ['moment'], | ||||
|     function (moment) { | ||||
|  | ||||
|         /** | ||||
|          * Indicator that displays the current UTC time in the status area. | ||||
|          * @implements {Indicator} | ||||
|          * @memberof platform/features/clock | ||||
|          * @param {platform/features/clock.TickerService} tickerService | ||||
|          *        a service used to align behavior with clock ticks | ||||
|          * @param {string} indicatorFormat format string for timestamps | ||||
|          *        shown in this indicator | ||||
|          */ | ||||
|         function ClockIndicator(tickerService, indicatorFormat) { | ||||
|             var self = this; | ||||
|  | ||||
|             this.text = ""; | ||||
|  | ||||
|             tickerService.listen(function (timestamp) { | ||||
|                 self.text = moment.utc(timestamp) | ||||
|                     .format(indicatorFormat) + " UTC"; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         ClockIndicator.prototype.getGlyphClass = function () { | ||||
|             return ""; | ||||
|         }; | ||||
|  | ||||
|         ClockIndicator.prototype.getCssClass = function () { | ||||
|             return "t-indicator-clock icon-clock no-minify c-indicator--not-clickable"; | ||||
|         }; | ||||
|  | ||||
|         ClockIndicator.prototype.getText = function () { | ||||
|             return this.text; | ||||
|         }; | ||||
|  | ||||
|         ClockIndicator.prototype.getDescription = function () { | ||||
|             return ""; | ||||
|         }; | ||||
|  | ||||
|         return ClockIndicator; | ||||
|     } | ||||
| ); | ||||
| @@ -1,107 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2009-2017, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ["../../src/controllers/ClockController"], | ||||
|     function (ClockController) { | ||||
|  | ||||
|         // Wed, 03 Jun 2015 17:56:14 GMT | ||||
|         var TEST_TIMESTAMP = 1433354174000; | ||||
|  | ||||
|         describe("A clock view's controller", function () { | ||||
|             var mockScope, | ||||
|                 mockTicker, | ||||
|                 mockUnticker, | ||||
|                 controller; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockScope = jasmine.createSpyObj('$scope', ['$watch', '$on']); | ||||
|                 mockTicker = jasmine.createSpyObj('ticker', ['listen']); | ||||
|                 mockUnticker = jasmine.createSpy('unticker'); | ||||
|  | ||||
|                 mockTicker.listen.and.returnValue(mockUnticker); | ||||
|  | ||||
|                 controller = new ClockController(mockScope, mockTicker); | ||||
|             }); | ||||
|  | ||||
|             it("watches for model (clockFormat and timezone) from the domain object model", function () { | ||||
|                 expect(mockScope.$watch).toHaveBeenCalledWith( | ||||
|                     "model", | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("subscribes to clock ticks", function () { | ||||
|                 expect(mockTicker.listen) | ||||
|                     .toHaveBeenCalledWith(jasmine.any(Function)); | ||||
|             }); | ||||
|  | ||||
|             it("unsubscribes to ticks when destroyed", function () { | ||||
|                 // Make sure $destroy is being listened for... | ||||
|                 expect(mockScope.$on.calls.mostRecent().args[0]).toEqual('$destroy'); | ||||
|                 expect(mockUnticker).not.toHaveBeenCalled(); | ||||
|  | ||||
|                 // ...and makes sure that its listener unsubscribes from ticker | ||||
|                 mockScope.$on.calls.mostRecent().args[1](); | ||||
|                 expect(mockUnticker).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("formats using the format string from the model", function () { | ||||
|                 mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP); | ||||
|                 mockScope.$watch.calls.mostRecent().args[1]({ | ||||
|                     "clockFormat": [ | ||||
|                         "YYYY-DDD hh:mm:ss", | ||||
|                         "clock24" | ||||
|                     ], | ||||
|                     "timezone": "Canada/Eastern" | ||||
|                 }); | ||||
|  | ||||
|                 expect(controller.zone()).toEqual("EDT"); | ||||
|                 expect(controller.text()).toEqual("2015-154 13:56:14"); | ||||
|                 expect(controller.ampm()).toEqual(""); | ||||
|             }); | ||||
|  | ||||
|             it("formats 12-hour time", function () { | ||||
|                 mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP); | ||||
|                 mockScope.$watch.calls.mostRecent().args[1]({ | ||||
|                     "clockFormat": [ | ||||
|                         "YYYY-DDD hh:mm:ss", | ||||
|                         "clock12" | ||||
|                     ], | ||||
|                     "timezone": "" | ||||
|                 }); | ||||
|  | ||||
|                 expect(controller.zone()).toEqual("UTC"); | ||||
|                 expect(controller.text()).toEqual("2015-154 05:56:14"); | ||||
|                 expect(controller.ampm()).toEqual("PM"); | ||||
|             }); | ||||
|  | ||||
|             it("does not throw exceptions when model is undefined", function () { | ||||
|                 mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP); | ||||
|                 expect(function () { | ||||
|                     mockScope.$watch.calls.mostRecent().args[1](undefined); | ||||
|                 }).not.toThrow(); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -1,58 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2009-2016, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     ["../../src/indicators/ClockIndicator"], | ||||
|     function (ClockIndicator) { | ||||
|  | ||||
|         // Wed, 03 Jun 2015 17:56:14 GMT | ||||
|         var TEST_TIMESTAMP = 1433354174000, | ||||
|             TEST_FORMAT = "YYYY-DDD HH:mm:ss"; | ||||
|  | ||||
|         describe("The clock indicator", function () { | ||||
|             var mockTicker, | ||||
|                 mockUnticker, | ||||
|                 indicator; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockTicker = jasmine.createSpyObj('ticker', ['listen']); | ||||
|                 mockUnticker = jasmine.createSpy('unticker'); | ||||
|  | ||||
|                 mockTicker.listen.and.returnValue(mockUnticker); | ||||
|  | ||||
|                 indicator = new ClockIndicator(mockTicker, TEST_FORMAT); | ||||
|             }); | ||||
|  | ||||
|             it("displays the current time", function () { | ||||
|                 mockTicker.listen.calls.mostRecent().args[0](TEST_TIMESTAMP); | ||||
|                 expect(indicator.getText()).toEqual("2015-154 17:56:14 UTC"); | ||||
|             }); | ||||
|  | ||||
|             it("implements the Indicator interface", function () { | ||||
|                 expect(indicator.getCssClass()).toEqual(jasmine.any(String)); | ||||
|                 expect(indicator.getText()).toEqual(jasmine.any(String)); | ||||
|                 expect(indicator.getDescription()).toEqual(jasmine.any(String)); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -30,8 +30,8 @@ define([ | ||||
|  | ||||
|     return function ImportExportPlugin() { | ||||
|         return function (openmct) { | ||||
|             ExportAsJSONAction.appliesTo = function (context) { | ||||
|                 return openmct.$injector.get('policyService') | ||||
|             ExportAsJSONAction.prototype.appliesTo = function (context) { | ||||
|                 return this.openmct.$injector.get('policyService') | ||||
|                     .allow("creation", context.domainObject.getCapability("type") | ||||
|                     ); | ||||
|             }; | ||||
|   | ||||
| @@ -29,7 +29,7 @@ define( | ||||
|     ], | ||||
|     function (ExportAsJSONAction, domainObjectFactory, MCT, AdapterCapability) { | ||||
|  | ||||
|         xdescribe("The export JSON action", function () { | ||||
|         describe("The export JSON action", function () { | ||||
|  | ||||
|             var context, | ||||
|                 action, | ||||
| @@ -102,7 +102,7 @@ define( | ||||
|                 expect(action).toBeDefined(); | ||||
|             }); | ||||
|  | ||||
|             it("doesn't export non-creatable objects in tree", function () { | ||||
|             xit("doesn't export non-creatable objects in tree", function () { | ||||
|                 var nonCreatableType = { | ||||
|                     hasFeature: | ||||
|                         function (feature) { | ||||
| @@ -149,7 +149,7 @@ define( | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("can export self-containing objects", function () { | ||||
|             xit("can export self-containing objects", function () { | ||||
|                 var parent = domainObjectFactory({ | ||||
|                     name: 'parent', | ||||
|                     model: { | ||||
| @@ -191,7 +191,7 @@ define( | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("exports links to external objects as new objects", function () { | ||||
|             xit("exports links to external objects as new objects", function () { | ||||
|                 var parent = domainObjectFactory({ | ||||
|                     name: 'parent', | ||||
|                     model: { | ||||
|   | ||||
| @@ -27,7 +27,7 @@ define( | ||||
|     ], | ||||
|     function (ImportAsJSONAction, domainObjectFactory) { | ||||
|  | ||||
|         xdescribe("The import JSON action", function () { | ||||
|         describe("The import JSON action", function () { | ||||
|  | ||||
|             var context = {}; | ||||
|             var action, | ||||
| @@ -146,7 +146,7 @@ define( | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("can import self-containing objects", function () { | ||||
|             xit("can import self-containing objects", function () { | ||||
|                 var compDomainObject = domainObjectFactory({ | ||||
|                     name: 'compObject', | ||||
|                     model: { name: 'compObject'}, | ||||
| @@ -198,7 +198,7 @@ define( | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("assigns new ids to each imported object", function () { | ||||
|             xit("assigns new ids to each imported object", function () { | ||||
|                 dialogService.getUserInput.and.returnValue(Promise.resolve( | ||||
|                     { | ||||
|                         selectFile: { | ||||
|   | ||||
| @@ -47,7 +47,7 @@ define( | ||||
|          * @param $interval Angular's $interval service | ||||
|          * @param {string} space the name of the persistence space being served | ||||
|          * @param {string} root the root of the path to ElasticSearch | ||||
|          * @param {stirng} path the path to domain objects within ElasticSearch | ||||
|          * @param {string} path the path to domain objects within ElasticSearch | ||||
|          */ | ||||
|         function ElasticPersistenceProvider($http, $q, space, root, path) { | ||||
|             this.spaces = [space]; | ||||
|   | ||||
| @@ -287,6 +287,7 @@ define([ | ||||
|         this.install(this.plugins.ViewLargeAction()); | ||||
|         this.install(this.plugins.ObjectInterceptors()); | ||||
|         this.install(this.plugins.NonEditableFolder()); | ||||
|         this.install(this.plugins.DeviceClassifier()); | ||||
|     } | ||||
|  | ||||
|     MCT.prototype = Object.create(EventEmitter.prototype); | ||||
|   | ||||
| @@ -60,9 +60,7 @@ class ActionsAPI extends EventEmitter { | ||||
|     } | ||||
|  | ||||
|     _getCachedActionCollection(objectPath, view) { | ||||
|         let cachedActionCollection = this._actionCollections.get(view); | ||||
|  | ||||
|         return cachedActionCollection; | ||||
|         return this._actionCollections.get(view); | ||||
|     } | ||||
|  | ||||
|     _newActionCollection(objectPath, view, skipEnvironmentObservers) { | ||||
|   | ||||
| @@ -42,7 +42,7 @@ import EventEmitter from 'EventEmitter'; | ||||
|  * | ||||
|  * @typedef {object} NotificationModel | ||||
|  * @property {string} message The message to be displayed by the notification | ||||
|  * @property {number | 'unknown'} [progress] The progres of some ongoing task. Should be a number between 0 and 100, or | ||||
|  * @property {number | 'unknown'} [progress] The progress of some ongoing task. Should be a number between 0 and 100, or | ||||
|  * with the string literal 'unknown'. | ||||
|  * @property {string} [progressText] A message conveying progress of some ongoing task. | ||||
|  | ||||
| @@ -98,7 +98,7 @@ export default class NotificationAPI extends EventEmitter { | ||||
|      * Present an alert to the user. | ||||
|      * @param {string} message The message to display to the user. | ||||
|      * @param {Object} [options] object with following properties | ||||
|      *      autoDismissTimeout: {number} in miliseconds to automatically dismisses notification | ||||
|      *      autoDismissTimeout: {number} in milliseconds to automatically dismisses notification | ||||
|      *      link: {Object} Add a link to notifications for navigation | ||||
|      *              onClick: callback function | ||||
|      *              cssClass: css class name to add style on link | ||||
| @@ -119,7 +119,7 @@ export default class NotificationAPI extends EventEmitter { | ||||
|      * Present an error message to the user | ||||
|      * @param {string} message | ||||
|      * @param {Object} [options] object with following properties | ||||
|      *      autoDismissTimeout: {number} in miliseconds to automatically dismisses notification | ||||
|      *      autoDismissTimeout: {number} in milliseconds to automatically dismisses notification | ||||
|      *      link: {Object} Add a link to notifications for navigation | ||||
|      *              onClick: callback function | ||||
|      *              cssClass: css class name to add style on link | ||||
|   | ||||
| @@ -358,6 +358,20 @@ ObjectAPI.prototype.applyGetInterceptors = function (identifier, domainObject) { | ||||
|     return domainObject; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Return relative url path from a given object path | ||||
|  * eg: #/browse/mine/cb56f6bf-c900-43b7-b923-2e3b64b412db/6e89e858-77ce-46e4-a1ad-749240286497/.... | ||||
|  * @param {Array} objectPath | ||||
|  * @returns {string} relative url for object | ||||
|  */ | ||||
| ObjectAPI.prototype.getRelativePath = function (objectPath) { | ||||
|     return objectPath | ||||
|         .map(p => this.makeKeyString(p.identifier)) | ||||
|         .reverse() | ||||
|         .join('/') | ||||
|     ; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Modify a domain object. | ||||
|  * @param {module:openmct.DomainObject} object the object to mutate | ||||
|   | ||||
| @@ -10,28 +10,37 @@ const cssClasses = { | ||||
| }; | ||||
|  | ||||
| class Overlay extends EventEmitter { | ||||
|     constructor(options) { | ||||
|     constructor({ | ||||
|         buttons, | ||||
|         autoHide = true, | ||||
|         dismissable = true, | ||||
|         element, | ||||
|         onDestroy, | ||||
|         size | ||||
|     } = {}) { | ||||
|         super(); | ||||
|  | ||||
|         this.dismissable = options.dismissable !== false; | ||||
|         this.container = document.createElement('div'); | ||||
|         this.container.classList.add('l-overlay-wrapper', cssClasses[options.size]); | ||||
|         this.container.classList.add('l-overlay-wrapper', cssClasses[size]); | ||||
|  | ||||
|         this.autoHide = autoHide; | ||||
|         this.dismissable = dismissable !== false; | ||||
|  | ||||
|         this.component = new Vue({ | ||||
|             provide: { | ||||
|                 dismiss: this.dismiss.bind(this), | ||||
|                 element: options.element, | ||||
|                 buttons: options.buttons, | ||||
|                 dismissable: this.dismissable | ||||
|             }, | ||||
|             components: { | ||||
|                 OverlayComponent: OverlayComponent | ||||
|             }, | ||||
|             provide: { | ||||
|                 dismiss: this.dismiss.bind(this), | ||||
|                 element, | ||||
|                 buttons, | ||||
|                 dismissable: this.dismissable | ||||
|             }, | ||||
|             template: '<overlay-component></overlay-component>' | ||||
|         }); | ||||
|  | ||||
|         if (options.onDestroy) { | ||||
|             this.once('destroy', options.onDestroy); | ||||
|         if (onDestroy) { | ||||
|             this.once('destroy', onDestroy); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -30,7 +30,10 @@ class OverlayAPI { | ||||
|      */ | ||||
|     showOverlay(overlay) { | ||||
|         if (this.activeOverlays.length) { | ||||
|             this.activeOverlays[this.activeOverlays.length - 1].container.classList.add('invisible'); | ||||
|             const previousOverlay = this.activeOverlays[this.activeOverlays.length - 1]; | ||||
|             if (previousOverlay.autoHide) { | ||||
|                 previousOverlay.container.classList.add('invisible'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.activeOverlays.push(overlay); | ||||
| @@ -60,7 +63,7 @@ class OverlayAPI { | ||||
|      * A description of option properties that can be passed into the overlay | ||||
|      * @typedef options | ||||
|         * @property {object} element DOMElement that is to be inserted/shown on the overlay | ||||
|         * @property {string} size prefered size of the overlay (large, small, fit) | ||||
|         * @property {string} size preferred size of the overlay (large, small, fit) | ||||
|         * @property {array} buttons optional button objects with label and callback properties | ||||
|         * @property {function} onDestroy callback to be called when overlay is destroyed | ||||
|         * @property {boolean} dismissable allow user to dismiss overlay by using esc, and clicking away | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
|         ></button> | ||||
|         <div | ||||
|             ref="element" | ||||
|             class="c-overlay__contents" | ||||
|             class="c-overlay__contents js-notebook-snapshot-item-wrapper" | ||||
|             tabindex="0" | ||||
|         ></div> | ||||
|         <div | ||||
|   | ||||
| @@ -281,7 +281,7 @@ define([ | ||||
|      * (start, end, etc.), sort order, and strategies for retrieving | ||||
|      * telemetry (aggregation, latest available, etc.). | ||||
|      * | ||||
|      * @method requestTelemetryCollection | ||||
|      * @method requestCollection | ||||
|      * @memberof module:openmct.TelemetryAPI~TelemetryProvider# | ||||
|      * @param {module:openmct.DomainObject} domainObject the object | ||||
|      *        which has associated telemetry | ||||
| @@ -289,7 +289,7 @@ define([ | ||||
|      *        options for this telemetry collection request | ||||
|      * @returns {TelemetryCollection} a TelemetryCollection instance | ||||
|      */ | ||||
|     TelemetryAPI.prototype.requestTelemetryCollection = function (domainObject, options = {}) { | ||||
|     TelemetryAPI.prototype.requestCollection = function (domainObject, options = {}) { | ||||
|         return new TelemetryCollection( | ||||
|             this.openmct, | ||||
|             domainObject, | ||||
|   | ||||
| @@ -603,7 +603,7 @@ describe('Telemetry API', function () { | ||||
|         }); | ||||
|  | ||||
|         it('when requested, returns an instance of telemetry collection', () => { | ||||
|             const telemetryCollection = telemetryAPI.requestTelemetryCollection(domainObject); | ||||
|             const telemetryCollection = telemetryAPI.requestCollection(domainObject); | ||||
|  | ||||
|             expect(telemetryCollection).toBeInstanceOf(TelemetryCollection); | ||||
|         }); | ||||
|   | ||||
| @@ -23,6 +23,11 @@ | ||||
| import _ from 'lodash'; | ||||
| import EventEmitter from 'EventEmitter'; | ||||
|  | ||||
| const ERRORS = { | ||||
|     TIMESYSTEM_KEY: 'All telemetry metadata must have a telemetry value with a key that matches the key of the active time system.', | ||||
|     LOADED: 'Telemetry Collection has already been loaded.' | ||||
| }; | ||||
|  | ||||
| /** Class representing a Telemetry Collection. */ | ||||
|  | ||||
| export class TelemetryCollection extends EventEmitter { | ||||
| @@ -57,7 +62,7 @@ export class TelemetryCollection extends EventEmitter { | ||||
|      */ | ||||
|     load() { | ||||
|         if (this.loaded) { | ||||
|             throw new Error('Telemetry Collection has already been loaded.'); | ||||
|             this._error(ERRORS.LOADED); | ||||
|         } | ||||
|  | ||||
|         this._timeSystem(this.openmct.time.timeSystem()); | ||||
| @@ -123,13 +128,13 @@ export class TelemetryCollection extends EventEmitter { | ||||
|  | ||||
|         try { | ||||
|             this.requestAbort = new AbortController(); | ||||
|             this.options.abortSignal = this.requestAbort.signal; | ||||
|             this.options.signal = this.requestAbort.signal; | ||||
|             historicalData = await this.historicalProvider.request(this.domainObject, this.options); | ||||
|             this.requestAbort = undefined; | ||||
|         } catch (error) { | ||||
|             console.error('Error requesting telemetry data...'); | ||||
|             this.requestAbort = undefined; | ||||
|             throw new Error(error); | ||||
|             this._error(error); | ||||
|         } | ||||
|  | ||||
|         this._processNewTelemetry(historicalData); | ||||
| @@ -189,7 +194,7 @@ export class TelemetryCollection extends EventEmitter { | ||||
|                     if (endIndex > startIndex) { | ||||
|                         let potentialDupes = this.boundedTelemetry.slice(startIndex, endIndex); | ||||
|  | ||||
|                         isDuplicate = potentialDupes.some(_.isEqual(undefined, datum)); | ||||
|                         isDuplicate = potentialDupes.some(_.isEqual.bind(undefined, datum)); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
| @@ -307,8 +312,16 @@ export class TelemetryCollection extends EventEmitter { | ||||
|      * @private | ||||
|      */ | ||||
|     _timeSystem(timeSystem) { | ||||
|         this.timeKey = timeSystem.key; | ||||
|         let metadataValue = this.metadata.value(this.timeKey) || { format: this.timeKey }; | ||||
|         let domains = this.metadata.valuesForHints(['domain']); | ||||
|         let domain = domains.find((d) => d.key === timeSystem.key); | ||||
|  | ||||
|         if (domain === undefined) { | ||||
|             this._error(ERRORS.TIMESYSTEM_KEY); | ||||
|         } | ||||
|  | ||||
|         // timeKey is used to create a dummy datum used for sorting | ||||
|         this.timeKey = domain.source; // this defaults to key if no source is set | ||||
|         let metadataValue = this.metadata.value(timeSystem.key) || { format: timeSystem.key }; | ||||
|         let valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue); | ||||
|  | ||||
|         this.parseTime = (datum) => { | ||||
| @@ -329,6 +342,8 @@ export class TelemetryCollection extends EventEmitter { | ||||
|         this.boundedTelemetry = []; | ||||
|         this.futureBuffer = []; | ||||
|  | ||||
|         this.emit('clear'); | ||||
|  | ||||
|         this._requestHistoricalTelemetry(); | ||||
|     } | ||||
|  | ||||
| @@ -363,4 +378,13 @@ export class TelemetryCollection extends EventEmitter { | ||||
|     _unwatchTimeSystem() { | ||||
|         this.openmct.time.off('timeSystem', this._timeSystem, this); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * will throw a new Error, for passed in message | ||||
|      * @param  {string} message Message describing the error | ||||
|      * @private | ||||
|      */ | ||||
|     _error(message) { | ||||
|         throw new Error(message); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -90,7 +90,7 @@ class ImageExporter { | ||||
|                 element.id = oldId; | ||||
|             }, | ||||
|             removeContainer: true // Set to false to debug what html2canvas renders | ||||
|         }).then(function (canvas) { | ||||
|         }).then(canvas => { | ||||
|             dialog.dismiss(); | ||||
|  | ||||
|             return new Promise(function (resolve, reject) { | ||||
| @@ -105,9 +105,10 @@ class ImageExporter { | ||||
|  | ||||
|                 return canvas.toBlob(blob => resolve({ blob }), mimeType); | ||||
|             }); | ||||
|         }, function (error) { | ||||
|             console.log('error capturing image', error); | ||||
|         }).catch(error => { | ||||
|             dialog.dismiss(); | ||||
|  | ||||
|             console.error('error capturing image', error); | ||||
|             const errorDialog = overlays.dialog({ | ||||
|                 iconClass: 'error', | ||||
|                 message: 'Image was not captured successfully!', | ||||
|   | ||||
| @@ -19,23 +19,14 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| import Agent from "../../utils/agent/Agent"; | ||||
| import DeviceClassifier from "./src/DeviceClassifier"; | ||||
| 
 | ||||
| export default class ClearDataAction { | ||||
|     constructor(openmct, appliesToObjects) { | ||||
|         this.name = 'Clear Data for Object'; | ||||
|         this.key = 'clear-data-action'; | ||||
|         this.description = 'Clears current data for object, unsubscribes and resubscribes to data'; | ||||
|         this.cssClass = 'icon-clear-data'; | ||||
| 
 | ||||
|         this._openmct = openmct; | ||||
|         this._appliesToObjects = appliesToObjects; | ||||
|     } | ||||
|     invoke(objectPath) { | ||||
|         this._openmct.objectViews.emit('clearData', objectPath[0]); | ||||
|     } | ||||
|     appliesTo(objectPath) { | ||||
|         let contextualDomainObject = objectPath[0]; | ||||
| 
 | ||||
|         return this._appliesToObjects.filter(type => contextualDomainObject.type === type).length; | ||||
|     } | ||||
| } | ||||
| export default () => { | ||||
|     return (openmct) => { | ||||
|         openmct.on("start", () => { | ||||
|             const agent = new Agent(window); | ||||
|             DeviceClassifier(agent, window.document); | ||||
|         }); | ||||
|     }; | ||||
| }; | ||||
							
								
								
									
										72
									
								
								src/plugins/DeviceClassifier/src/DeviceClassifier.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/plugins/DeviceClassifier/src/DeviceClassifier.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /** | ||||
|  * Runs at application startup and adds a subset of the following | ||||
|  * CSS classes to the body of the document, depending on device | ||||
|  * attributes: | ||||
|  * | ||||
|  * * `mobile`: Phones or tablets. | ||||
|  * * `phone`: Phones specifically. | ||||
|  * * `tablet`: Tablets specifically. | ||||
|  * * `desktop`: Non-mobile devices. | ||||
|  * * `portrait`: Devices in a portrait-style orientation. | ||||
|  * * `landscape`: Devices in a landscape-style orientation. | ||||
|  * * `touch`: Device supports touch events. | ||||
|  * | ||||
|  * @param {utils/agent/Agent} agent | ||||
|  *        the service used to examine the user agent | ||||
|  * @param document the HTML DOM document object | ||||
|  * @constructor | ||||
|  */ | ||||
| import DeviceMatchers from "./DeviceMatchers"; | ||||
|  | ||||
| export default (agent, document) => { | ||||
|     const body = document.body; | ||||
|  | ||||
|     Object.keys(DeviceMatchers).forEach((key, index, array) => { | ||||
|         if (DeviceMatchers[key](agent)) { | ||||
|             body.classList.add(key); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     if (agent.isMobile()) { | ||||
|         const mediaQuery = window.matchMedia("(orientation: landscape)"); | ||||
|         function eventHandler(event) { | ||||
|             console.log("changed"); | ||||
|             if (event.matches) { | ||||
|                 body.classList.remove("portrait"); | ||||
|                 body.classList.add("landscape"); | ||||
|             } else { | ||||
|                 body.classList.remove("landscape"); | ||||
|                 body.classList.add("portrait"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (mediaQuery.addEventListener) { | ||||
|             mediaQuery.addEventListener(`change`, eventHandler); | ||||
|         } else { | ||||
|             // Deprecated 'MediaQueryList' API, <Safari 14, IE, <Edge 16 | ||||
|             mediaQuery.addListener(eventHandler); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										105
									
								
								src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/plugins/DeviceClassifier/src/DeviceClassifierSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| import DeviceClassifier from "./DeviceClassifier"; | ||||
| import DeviceMatchers from "./DeviceMatchers"; | ||||
|  | ||||
| const AGENT_METHODS = [ | ||||
|     "isMobile", | ||||
|     "isPhone", | ||||
|     "isTablet", | ||||
|     "isPortrait", | ||||
|     "isLandscape", | ||||
|     "isTouch" | ||||
| ]; | ||||
| const TEST_PERMUTATIONS = [ | ||||
|     ["isMobile", "isPhone", "isTouch", "isPortrait"], | ||||
|     ["isMobile", "isPhone", "isTouch", "isLandscape"], | ||||
|     ["isMobile", "isTablet", "isTouch", "isPortrait"], | ||||
|     ["isMobile", "isTablet", "isTouch", "isLandscape"], | ||||
|     ["isTouch"], | ||||
|     [] | ||||
| ]; | ||||
|  | ||||
| describe("DeviceClassifier", function () { | ||||
|     let mockAgent; | ||||
|     let mockDocument; | ||||
|     let mockClassList; | ||||
|  | ||||
|     beforeEach(function () { | ||||
|         mockAgent = jasmine.createSpyObj( | ||||
|             "agent", | ||||
|             AGENT_METHODS | ||||
|         ); | ||||
|  | ||||
|         mockClassList = jasmine.createSpyObj("classList", ["add"]); | ||||
|  | ||||
|         mockDocument = jasmine.createSpyObj( | ||||
|             "document", | ||||
|             {}, | ||||
|             { body: { classList: mockClassList } } | ||||
|         ); | ||||
|  | ||||
|         AGENT_METHODS.forEach(function (m) { | ||||
|             mockAgent[m].and.returnValue(false); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     TEST_PERMUTATIONS.forEach(function (trueMethods) { | ||||
|         const summary = | ||||
|       trueMethods.length === 0 | ||||
|           ? "device has no detected characteristics" | ||||
|           : "device " + trueMethods.join(", "); | ||||
|  | ||||
|         describe("when " + summary, function () { | ||||
|             beforeEach(function () { | ||||
|                 trueMethods.forEach(function (m) { | ||||
|                     mockAgent[m].and.returnValue(true); | ||||
|                 }); | ||||
|  | ||||
|                 // eslint-disable-next-line no-new | ||||
|                 DeviceClassifier(mockAgent, mockDocument); | ||||
|             }); | ||||
|  | ||||
|             it("adds classes for matching, detected characteristics", function () { | ||||
|                 Object.keys(DeviceMatchers) | ||||
|                     .filter(function (m) { | ||||
|                         return DeviceMatchers[m](mockAgent); | ||||
|                     }) | ||||
|                     .forEach(function (key) { | ||||
|                         expect(mockDocument.body.classList.add).toHaveBeenCalledWith(key); | ||||
|                     }); | ||||
|             }); | ||||
|  | ||||
|             it("does not add classes for non-matching characteristics", function () { | ||||
|                 Object.keys(DeviceMatchers) | ||||
|                     .filter(function (m) { | ||||
|                         return !DeviceMatchers[m](mockAgent); | ||||
|                     }) | ||||
|                     .forEach(function (key) { | ||||
|                         expect(mockDocument.body.classList.add).not.toHaveBeenCalledWith( | ||||
|                             key | ||||
|                         ); | ||||
|                     }); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
							
								
								
									
										57
									
								
								src/plugins/DeviceClassifier/src/DeviceMatchers.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/plugins/DeviceClassifier/src/DeviceMatchers.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /** | ||||
|  * An object containing key-value pairs, where keys are symbolic of | ||||
|  * device attributes, and values are functions that take the | ||||
|  * `agent` as inputs and return boolean values indicating | ||||
|  * whether or not the current device has these attributes. | ||||
|  * | ||||
|  * For internal use by the mobile support bundle. | ||||
|  * | ||||
|  * @memberof src/plugins/DeviceClassifier | ||||
|  * @private | ||||
|  */ | ||||
|  | ||||
| export default { | ||||
|     mobile: function (agent) { | ||||
|         return agent.isMobile(); | ||||
|     }, | ||||
|     phone: function (agent) { | ||||
|         return agent.isPhone(); | ||||
|     }, | ||||
|     tablet: function (agent) { | ||||
|         return agent.isTablet(); | ||||
|     }, | ||||
|     desktop: function (agent) { | ||||
|         return !agent.isMobile(); | ||||
|     }, | ||||
|     portrait: function (agent) { | ||||
|         return agent.isPortrait(); | ||||
|     }, | ||||
|     landscape: function (agent) { | ||||
|         return agent.isLandscape(); | ||||
|     }, | ||||
|     touch: function (agent) { | ||||
|         return agent.isTouch(); | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										65
									
								
								src/plugins/DeviceClassifier/src/DeviceMatchersSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/plugins/DeviceClassifier/src/DeviceMatchersSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| import DeviceMatchers from "./DeviceMatchers"; | ||||
|  | ||||
| describe("DeviceMatchers", function () { | ||||
|     let mockAgent; | ||||
|  | ||||
|     beforeEach(function () { | ||||
|         mockAgent = jasmine.createSpyObj("agent", [ | ||||
|             "isMobile", | ||||
|             "isPhone", | ||||
|             "isTablet", | ||||
|             "isPortrait", | ||||
|             "isLandscape", | ||||
|             "isTouch" | ||||
|         ]); | ||||
|     }); | ||||
|  | ||||
|     it("detects when a device is a desktop device", function () { | ||||
|         mockAgent.isMobile.and.returnValue(false); | ||||
|         expect(DeviceMatchers.desktop(mockAgent)).toBe(true); | ||||
|         mockAgent.isMobile.and.returnValue(true); | ||||
|         expect(DeviceMatchers.desktop(mockAgent)).toBe(false); | ||||
|     }); | ||||
|  | ||||
|     function method(deviceType) { | ||||
|         return "is" + deviceType[0].toUpperCase() + deviceType.slice(1); | ||||
|     } | ||||
|  | ||||
|     [ | ||||
|         "mobile", | ||||
|         "phone", | ||||
|         "tablet", | ||||
|         "landscape", | ||||
|         "portrait", | ||||
|         "landscape", | ||||
|         "touch" | ||||
|     ].forEach(function (deviceType) { | ||||
|         it("detects when a device is a " + deviceType + " device", function () { | ||||
|             mockAgent[method(deviceType)].and.returnValue(true); | ||||
|             expect(DeviceMatchers[deviceType](mockAgent)).toBe(true); | ||||
|             mockAgent[method(deviceType)].and.returnValue(false); | ||||
|             expect(DeviceMatchers[deviceType](mockAgent)).toBe(false); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| @@ -1,50 +0,0 @@ | ||||
| <template> | ||||
| <thead> | ||||
|     <tr> | ||||
|         <th>Name</th> | ||||
|         <th>Timestamp</th> | ||||
|         <th v-for="name in columnNames" | ||||
|             :key="name" | ||||
|         > | ||||
|             {{ name }} | ||||
|         </th> | ||||
|         <th v-if="hasUnits">Unit</th> | ||||
|     </tr> | ||||
| </thead> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|  | ||||
| export default { | ||||
|     props: { | ||||
|         item: { | ||||
|             type: Object, | ||||
|             required: true | ||||
|         }, | ||||
|         columnNames: { | ||||
|             type: Array, | ||||
|             required: true | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|         return {}; | ||||
|     }, | ||||
|     computed: { | ||||
|         hasUnits() { | ||||
|             // let itemsWithUnits = this.items.filter((item) => { | ||||
|             //     let metadata = this.openmct.telemetry.getMetadata(item.domainObject); | ||||
|  | ||||
|             //     return this.metadataHasUnits(metadata.valueMetadatas); | ||||
|  | ||||
|             // }); | ||||
|  | ||||
|             // return itemsWithUnits.length !== 0; | ||||
|             return false; | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         // console.log(this.names); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| </script> | ||||
| @@ -29,11 +29,9 @@ | ||||
|     <td class="js-first-data">{{ domainObject.name }}</td> | ||||
|     <td class="js-second-data">{{ formattedTimestamp }}</td> | ||||
|     <td | ||||
|         v-for="name in columnNames" | ||||
|         :key="name" | ||||
|         class="js-third-data" | ||||
|         :class="valueClass" | ||||
|     >{{ value[name] }}</td> | ||||
|     >{{ value }}</td> | ||||
|     <td | ||||
|         v-if="hasUnits" | ||||
|         class="js-units" | ||||
| @@ -65,10 +63,6 @@ export default { | ||||
|         hasUnits: { | ||||
|             type: Boolean, | ||||
|             requred: true | ||||
|         }, | ||||
|         columnNames: { | ||||
|             type: Array, | ||||
|             required: true | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
| @@ -88,7 +82,6 @@ export default { | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         console.log(this.colNames); | ||||
|         this.metadata = this.openmct.telemetry.getMetadata(this.domainObject); | ||||
|         this.formats = this.openmct.telemetry.getFormatMap(this.metadata); | ||||
|         this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier); | ||||
| @@ -103,17 +96,12 @@ export default { | ||||
|  | ||||
|         this.timestampKey = this.openmct.time.timeSystem().key; | ||||
|  | ||||
|         // this.valueMetadata = this | ||||
|         //     .metadata | ||||
|         //     .valuesForHints(['range'])[0]; | ||||
|  | ||||
|         // this.valueKey = this.valueMetadata.key; | ||||
|         this.valueMetadata = this | ||||
|             .metadata | ||||
|             .valuesForHints(['range']); | ||||
|         // console.log(this.valueMetadata); | ||||
|         this.valueKey = this.valueMetadata.map(value => value.key); | ||||
|         // console.log(this.valueKey); | ||||
|             .valuesForHints(['range'])[0]; | ||||
|  | ||||
|         this.valueKey = this.valueMetadata.key; | ||||
|  | ||||
|         this.unsubscribe = this.openmct | ||||
|             .telemetry | ||||
|             .subscribe(this.domainObject, this.updateValues); | ||||
| @@ -135,18 +123,9 @@ export default { | ||||
|             let limit; | ||||
|  | ||||
|             if (this.shouldUpdate(newTimestamp)) { | ||||
|                 // console.log(datum); | ||||
|                 this.datum = datum; | ||||
|                 this.timestamp = newTimestamp; | ||||
|                 // console.log(this.formats); | ||||
|                 // this.value = this.formats[this.valueKey].format(datum); | ||||
|                 this.value = {}; | ||||
|                 this.valueKey.forEach(key => { | ||||
|                     let formattedDatum = this.formats[key].format(datum); | ||||
|                     this.value[key] = formattedDatum; | ||||
|                     limit = this.limitEvaluator.evaluate(formattedDatum, this.valueMetadata); | ||||
|                 }); | ||||
|                 // console.log(this.value); | ||||
|                 this.value = this.formats[this.valueKey].format(datum); | ||||
|                 limit = this.limitEvaluator.evaluate(datum, this.valueMetadata); | ||||
|                 if (limit) { | ||||
|                     this.valueClass = limit.cssClass; | ||||
|   | ||||
| @@ -22,17 +22,19 @@ | ||||
|  | ||||
| <template> | ||||
| <div class="c-lad-table-wrapper u-style-receiver js-style-receiver"> | ||||
|     <table v-for="ladRow in items" | ||||
|            :key="ladRow.key" | ||||
|            class="c-table c-lad-table" | ||||
|     > | ||||
|         <lad-head | ||||
|             :item="ladRow" | ||||
|             :column-names="columnNames(ladRow)" | ||||
|         /> | ||||
|     <table class="c-table c-lad-table"> | ||||
|         <thead> | ||||
|             <tr> | ||||
|                 <th>Name</th> | ||||
|                 <th>Timestamp</th> | ||||
|                 <th>Value</th> | ||||
|                 <th v-if="hasUnits">Unit</th> | ||||
|             </tr> | ||||
|         </thead> | ||||
|         <tbody> | ||||
|             <lad-row | ||||
|                 :column-names="columnNames(ladRow)" | ||||
|                 v-for="ladRow in items" | ||||
|                 :key="ladRow.key" | ||||
|                 :domain-object="ladRow.domainObject" | ||||
|                 :path-to-table="objectPath" | ||||
|                 :has-units="hasUnits" | ||||
| @@ -45,12 +47,10 @@ | ||||
|  | ||||
| <script> | ||||
| import LadRow from './LADRow.vue'; | ||||
| import LadHead from './LADHead.vue'; | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
|         LadRow, | ||||
|         LadHead | ||||
|         LadRow | ||||
|     }, | ||||
|     inject: ['openmct', 'currentView'], | ||||
|     props: { | ||||
| @@ -94,13 +94,6 @@ export default { | ||||
|         this.composition.off('reorder', this.reorder); | ||||
|     }, | ||||
|     methods: { | ||||
|         columnNames(item) { | ||||
|             let metadata = this.openmct.telemetry.getMetadata(item.domainObject); | ||||
|             let valueMetadata = metadata | ||||
|                 .valuesForHints(['range']); | ||||
|  | ||||
|             return valueMetadata.map(value => value.key); | ||||
|         }, | ||||
|         addItem(domainObject) { | ||||
|             let item = {}; | ||||
|             item.domainObject = domainObject; | ||||
|   | ||||
							
								
								
									
										78
									
								
								src/plugins/clearData/ClearDataAction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/plugins/clearData/ClearDataAction.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| function inSelectionPath(openmct, domainObject) { | ||||
|     const domainObjectIdentifier = domainObject.identifier; | ||||
|  | ||||
|     return openmct.selection.get().some(selectionPath => { | ||||
|         return selectionPath.some(objectInPath => { | ||||
|             const objectInPathIdentifier = objectInPath.context.item.identifier; | ||||
|  | ||||
|             return openmct.objects.areIdsEqual(objectInPathIdentifier, domainObjectIdentifier); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export default class ClearDataAction { | ||||
|     constructor(openmct, appliesToObjects) { | ||||
|         this.name = 'Clear Data for Object'; | ||||
|         this.key = 'clear-data-action'; | ||||
|         this.description = 'Clears current data for object, unsubscribes and resubscribes to data'; | ||||
|         this.cssClass = 'icon-clear-data'; | ||||
|  | ||||
|         this._openmct = openmct; | ||||
|         this._appliesToObjects = appliesToObjects; | ||||
|     } | ||||
|     invoke(objectPath) { | ||||
|         let domainObject = null; | ||||
|         if (objectPath) { | ||||
|             domainObject = objectPath[0]; | ||||
|         } | ||||
|  | ||||
|         this._openmct.objectViews.emit('clearData', domainObject); | ||||
|     } | ||||
|     appliesTo(objectPath) { | ||||
|         if (!objectPath) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         const contextualDomainObject = objectPath[0]; | ||||
|         // first check to see if this action applies to this sort of object at all | ||||
|         const appliesToThisObject = this._appliesToObjects.some(type => { | ||||
|             return contextualDomainObject.type === type; | ||||
|         }); | ||||
|         if (!appliesToThisObject) { | ||||
|             // we've selected something not applicable | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         const objectInSelectionPath = inSelectionPath(this._openmct, contextualDomainObject); | ||||
|         if (objectInSelectionPath) { | ||||
|             return true; | ||||
|         } else { | ||||
|             // if this it doesn't match up, check to see if we're in a composition (i.e., layout) | ||||
|             const routerPath = this._openmct.router.path[0]; | ||||
|  | ||||
|             return routerPath.type === 'layout'; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -22,7 +22,7 @@ | ||||
|  | ||||
| define([ | ||||
|     './components/globalClearIndicator.vue', | ||||
|     './clearDataAction', | ||||
|     './ClearDataAction', | ||||
|     'vue' | ||||
| ], function ( | ||||
|     GlobaClearIndicator, | ||||
|   | ||||
							
								
								
									
										140
									
								
								src/plugins/clearData/test/ClearDataActionSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/plugins/clearData/test/ClearDataActionSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| import ClearDataActionPlugin from '../plugin.js'; | ||||
| import ClearDataAction from '../ClearDataAction.js'; | ||||
|  | ||||
| describe('When the Clear Data Plugin is installed,', () => { | ||||
|     const mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']); | ||||
|     const mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']); | ||||
|     const mockActionsProvider = jasmine.createSpyObj('actions', ['register']); | ||||
|     const goodMockSelectionPath = [[{ | ||||
|         context: { | ||||
|             item: { | ||||
|                 identifier: { | ||||
|                     key: 'apple', | ||||
|                     namespace: '' | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }]]; | ||||
|  | ||||
|     const openmct = { | ||||
|         objectViews: mockObjectViews, | ||||
|         indicators: mockIndicatorProvider, | ||||
|         actions: mockActionsProvider, | ||||
|         install: function (plugin) { | ||||
|             plugin(this); | ||||
|         }, | ||||
|         selection: { | ||||
|             get: function () { | ||||
|                 return goodMockSelectionPath; | ||||
|             } | ||||
|         }, | ||||
|         objects: { | ||||
|             areIdsEqual: function (obj1, obj2) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     const mockObjectPath = [ | ||||
|         { | ||||
|             name: 'mockObject1', | ||||
|             type: 'apple' | ||||
|         }, | ||||
|         { | ||||
|             name: 'mockObject2', | ||||
|             type: 'banana' | ||||
|         } | ||||
|     ]; | ||||
|  | ||||
|     it('Global Clear Indicator is installed', () => { | ||||
|         openmct.install(ClearDataActionPlugin([])); | ||||
|  | ||||
|         expect(mockIndicatorProvider.add).toHaveBeenCalled(); | ||||
|     }); | ||||
|  | ||||
|     it('Clear Data context menu action is installed', () => { | ||||
|         openmct.install(ClearDataActionPlugin([])); | ||||
|  | ||||
|         expect(mockActionsProvider.register).toHaveBeenCalled(); | ||||
|     }); | ||||
|  | ||||
|     it('clear data action emits a clearData event when invoked', () => { | ||||
|         const action = new ClearDataAction(openmct); | ||||
|  | ||||
|         action.invoke(mockObjectPath); | ||||
|  | ||||
|         expect(mockObjectViews.emit).toHaveBeenCalledWith('clearData', mockObjectPath[0]); | ||||
|     }); | ||||
|  | ||||
|     it('clears data on applicable objects', () => { | ||||
|         let action = new ClearDataAction(openmct, ['apple']); | ||||
|  | ||||
|         const actionApplies = action.appliesTo(mockObjectPath); | ||||
|  | ||||
|         expect(actionApplies).toBe(true); | ||||
|     }); | ||||
|  | ||||
|     it('does not clear data on inapplicable objects', () => { | ||||
|         let action = new ClearDataAction(openmct, ['pineapple']); | ||||
|  | ||||
|         const actionApplies = action.appliesTo(mockObjectPath); | ||||
|  | ||||
|         expect(actionApplies).toBe(false); | ||||
|     }); | ||||
|  | ||||
|     it('does not clear data if not in the selection path and not a layout', () => { | ||||
|         openmct.objects = { | ||||
|             areIdsEqual: function (obj1, obj2) { | ||||
|                 return false; | ||||
|             } | ||||
|         }; | ||||
|         openmct.router = { | ||||
|             path: [{type: 'not-a-layout'}] | ||||
|         }; | ||||
|  | ||||
|         let action = new ClearDataAction(openmct, ['apple']); | ||||
|  | ||||
|         const actionApplies = action.appliesTo(mockObjectPath); | ||||
|  | ||||
|         expect(actionApplies).toBe(false); | ||||
|     }); | ||||
|  | ||||
|     it('does clear data if not in the selection path and is a layout', () => { | ||||
|         openmct.objects = { | ||||
|             areIdsEqual: function (obj1, obj2) { | ||||
|                 return false; | ||||
|             } | ||||
|         }; | ||||
|         openmct.router = { | ||||
|             path: [{type: 'layout'}] | ||||
|         }; | ||||
|  | ||||
|         let action = new ClearDataAction(openmct, ['apple']); | ||||
|  | ||||
|         const actionApplies = action.appliesTo(mockObjectPath); | ||||
|  | ||||
|         expect(actionApplies).toBe(true); | ||||
|     }); | ||||
| }); | ||||
| @@ -1,64 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| import ClearDataActionPlugin from '../plugin.js'; | ||||
| import ClearDataAction from '../clearDataAction.js'; | ||||
|  | ||||
| describe('When the Clear Data Plugin is installed,', function () { | ||||
|     const mockObjectViews = jasmine.createSpyObj('objectViews', ['emit']); | ||||
|     const mockIndicatorProvider = jasmine.createSpyObj('indicators', ['add']); | ||||
|     const mockActionsProvider = jasmine.createSpyObj('actions', ['register']); | ||||
|  | ||||
|     const openmct = { | ||||
|         objectViews: mockObjectViews, | ||||
|         indicators: mockIndicatorProvider, | ||||
|         actions: mockActionsProvider, | ||||
|         install: function (plugin) { | ||||
|             plugin(this); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     const mockObjectPath = [ | ||||
|         {name: 'mockObject1'}, | ||||
|         {name: 'mockObject2'} | ||||
|     ]; | ||||
|  | ||||
|     it('Global Clear Indicator is installed', function () { | ||||
|         openmct.install(ClearDataActionPlugin([])); | ||||
|  | ||||
|         expect(mockIndicatorProvider.add).toHaveBeenCalled(); | ||||
|     }); | ||||
|  | ||||
|     it('Clear Data context menu action is installed', function () { | ||||
|         openmct.install(ClearDataActionPlugin([])); | ||||
|  | ||||
|         expect(mockActionsProvider.register).toHaveBeenCalled(); | ||||
|     }); | ||||
|  | ||||
|     it('clear data action emits a clearData event when invoked', function () { | ||||
|         let action = new ClearDataAction(openmct); | ||||
|  | ||||
|         action.invoke(mockObjectPath); | ||||
|  | ||||
|         expect(mockObjectViews.emit).toHaveBeenCalledWith('clearData', mockObjectPath[0]); | ||||
|     }); | ||||
| }); | ||||
| @@ -19,40 +19,41 @@ | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
| define(function () { | ||||
| 
 | ||||
|     /** | ||||
|      * An object containing key-value pairs, where keys are symbolic of | ||||
|      * device attributes, and values are functions that take the | ||||
|      * `agentService` as inputs and return boolean values indicating | ||||
|      * whether or not the current device has these attributes. | ||||
|      * | ||||
|      * For internal use by the mobile support bundle. | ||||
|      * | ||||
|      * @memberof platform/commonUI/mobile | ||||
|      * @private | ||||
|      */ | ||||
| import Clock from './components/Clock.vue'; | ||||
| import Vue from 'vue'; | ||||
| 
 | ||||
| export default function ClockViewProvider(openmct) { | ||||
|     return { | ||||
|         mobile: function (agentService) { | ||||
|             return agentService.isMobile(); | ||||
|         key: 'clock.view', | ||||
|         name: 'Clock', | ||||
|         cssClass: 'icon-clock', | ||||
|         canView(domainObject) { | ||||
|             return domainObject.type === 'clock'; | ||||
|         }, | ||||
|         phone: function (agentService) { | ||||
|             return agentService.isPhone(); | ||||
|         }, | ||||
|         tablet: function (agentService) { | ||||
|             return agentService.isTablet(); | ||||
|         }, | ||||
|         desktop: function (agentService) { | ||||
|             return !agentService.isMobile(); | ||||
|         }, | ||||
|         portrait: function (agentService) { | ||||
|             return agentService.isPortrait(); | ||||
|         }, | ||||
|         landscape: function (agentService) { | ||||
|             return agentService.isLandscape(); | ||||
|         }, | ||||
|         touch: function (agentService) { | ||||
|             return agentService.isTouch(); | ||||
| 
 | ||||
|         view: function (domainObject) { | ||||
|             let component; | ||||
| 
 | ||||
|             return { | ||||
|                 show: function (element) { | ||||
|                     component = new Vue({ | ||||
|                         el: element, | ||||
|                         components: { | ||||
|                             Clock | ||||
|                         }, | ||||
|                         provide: { | ||||
|                             openmct, | ||||
|                             domainObject | ||||
|                         }, | ||||
|                         template: '<clock />' | ||||
|                     }); | ||||
|                 }, | ||||
|                 destroy: function () { | ||||
|                     component.$destroy(); | ||||
|                     component = undefined; | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|     }; | ||||
| }); | ||||
| } | ||||
							
								
								
									
										99
									
								
								src/plugins/clock/components/Clock.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/plugins/clock/components/Clock.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| <!-- | ||||
|  Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  as represented by the Administrator of the National Aeronautics and Space | ||||
|  Administration. All rights reserved. | ||||
|  | ||||
|  Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  "License"); you may not use this file except in compliance with the License. | ||||
|  You may obtain a copy of the License at | ||||
|  http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  | ||||
|  Unless required by applicable law or agreed to in writing, software | ||||
|  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  License for the specific language governing permissions and limitations | ||||
|  under the License. | ||||
|  | ||||
|  Open MCT includes source code licensed under additional open source | ||||
|  licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
|  | ||||
| <template> | ||||
| <div class="l-angular-ov-wrapper"> | ||||
|     <div class="u-contents"> | ||||
|         <div class="c-clock l-time-display u-style-receiver js-style-receiver"> | ||||
|             <div class="c-clock__timezone"> | ||||
|                 {{ timeZoneAbbr }} | ||||
|             </div> | ||||
|             <div class="c-clock__value"> | ||||
|                 {{ timeTextValue }} | ||||
|             </div> | ||||
|             <div class="c-clock__ampm"> | ||||
|                 {{ timeAmPm }} | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import moment from 'moment'; | ||||
| import momentTimezone from 'moment-timezone'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct', 'domainObject'], | ||||
|     data() { | ||||
|         return { | ||||
|             lastTimestamp: null | ||||
|         }; | ||||
|     }, | ||||
|     computed: { | ||||
|         configuration() { | ||||
|             return this.domainObject.configuration; | ||||
|         }, | ||||
|         baseFormat() { | ||||
|             return this.configuration.baseFormat; | ||||
|         }, | ||||
|         use24() { | ||||
|             return this.configuration.use24 === 'clock24'; | ||||
|         }, | ||||
|         timezone() { | ||||
|             return this.configuration.timezone; | ||||
|         }, | ||||
|         timeFormat() { | ||||
|             return this.use24 ? this.baseFormat.replace('hh', "HH") : this.baseFormat; | ||||
|         }, | ||||
|         zoneName() { | ||||
|             return momentTimezone.tz.names().includes(this.timezone) ? this.timezone : "UTC"; | ||||
|         }, | ||||
|         momentTime() { | ||||
|             return this.zoneName ? moment.utc(this.lastTimestamp).tz(this.zoneName) : moment.utc(this.lastTimestamp); | ||||
|         }, | ||||
|         timeZoneAbbr() { | ||||
|             return this.momentTime.zoneAbbr(); | ||||
|         }, | ||||
|         timeTextValue() { | ||||
|             return this.timeFormat && this.momentTime.format(this.timeFormat); | ||||
|         }, | ||||
|         timeAmPm() { | ||||
|             return this.use24 ? '' : this.momentTime.format("A"); | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         const TickerService = this.openmct.$injector.get('tickerService'); | ||||
|         this.unlisten = TickerService.listen(this.tick); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         if (this.unlisten) { | ||||
|             this.unlisten(); | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         tick(timestamp) { | ||||
|             this.lastTimestamp = timestamp; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| </script> | ||||
							
								
								
									
										64
									
								
								src/plugins/clock/components/ClockIndicator.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/plugins/clock/components/ClockIndicator.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| <!-- | ||||
|  Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  as represented by the Administrator of the National Aeronautics and Space | ||||
|  Administration. All rights reserved. | ||||
|  | ||||
|  Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  "License"); you may not use this file except in compliance with the License. | ||||
|  You may obtain a copy of the License at | ||||
|  http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  | ||||
|  Unless required by applicable law or agreed to in writing, software | ||||
|  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  License for the specific language governing permissions and limitations | ||||
|  under the License. | ||||
|  | ||||
|  Open MCT includes source code licensed under additional open source | ||||
|  licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
|  | ||||
| <template> | ||||
| <div class="c-indicator t-indicator-clock icon-clock no-minify c-indicator--not-clickable"> | ||||
|     <span class="label c-indicator__label"> | ||||
|         {{ timeTextValue }} | ||||
|     </span> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import moment from 'moment'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     props: { | ||||
|         indicatorFormat: { | ||||
|             type: String, | ||||
|             required: true | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             timeTextValue: null | ||||
|         }; | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.openmct.on('start', () => { | ||||
|             const TickerService = this.openmct.$injector.get('tickerService'); | ||||
|             this.unlisten = TickerService.listen(this.tick); | ||||
|         }); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         if (this.unlisten) { | ||||
|             this.unlisten(); | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         tick(timestamp) { | ||||
|             this.timeTextValue = `${moment.utc(timestamp).format(this.indicatorFormat)} UTC`; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| </script> | ||||
							
								
								
									
										154
									
								
								src/plugins/clock/plugin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/plugins/clock/plugin.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | ||||
|  | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| import ClockViewProvider from './ClockViewProvider'; | ||||
| import ClockIndicator from './components/ClockIndicator.vue'; | ||||
|  | ||||
| import momentTimezone from 'moment-timezone'; | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| export default function ClockPlugin(options) { | ||||
|     return function install(openmct) { | ||||
|         const CLOCK_INDICATOR_FORMAT = 'YYYY/MM/DD HH:mm:ss'; | ||||
|         openmct.types.addType('clock', { | ||||
|             name: 'Clock', | ||||
|             description: 'A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.', | ||||
|             creatable: true, | ||||
|             cssClass: 'icon-clock', | ||||
|             initialize: function (domainObject) { | ||||
|                 domainObject.configuration = { | ||||
|                     baseFormat: 'YYYY/MM/DD hh:mm:ss', | ||||
|                     use24: 'clock12', | ||||
|                     timezone: 'UTC' | ||||
|                 }; | ||||
|             }, | ||||
|             "form": [ | ||||
|                 { | ||||
|                     "key": "displayFormat", | ||||
|                     "name": "Display Format", | ||||
|                     control: 'select', | ||||
|                     options: [ | ||||
|                         { | ||||
|                             value: 'YYYY/MM/DD hh:mm:ss', | ||||
|                             name: 'YYYY/MM/DD hh:mm:ss' | ||||
|                         }, | ||||
|                         { | ||||
|                             value: 'YYYY/DDD hh:mm:ss', | ||||
|                             name: 'YYYY/DDD hh:mm:ss' | ||||
|                         }, | ||||
|                         { | ||||
|                             value: 'hh:mm:ss', | ||||
|                             name: 'hh:mm:ss' | ||||
|                         } | ||||
|                     ], | ||||
|                     cssClass: 'l-inline', | ||||
|                     property: [ | ||||
|                         'configuration', | ||||
|                         'baseFormat' | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     control: 'select', | ||||
|                     options: [ | ||||
|                         { | ||||
|                             value: 'clock12', | ||||
|                             name: '12hr' | ||||
|                         }, | ||||
|                         { | ||||
|                             value: 'clock24', | ||||
|                             name: '24hr' | ||||
|                         } | ||||
|                     ], | ||||
|                     cssClass: 'l-inline', | ||||
|                     property: [ | ||||
|                         'configuration', | ||||
|                         'use24' | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "timezone", | ||||
|                     "name": "Timezone", | ||||
|                     "control": "autocomplete", | ||||
|                     "options": momentTimezone.tz.names(), | ||||
|                     property: [ | ||||
|                         'configuration', | ||||
|                         'timezone' | ||||
|                     ] | ||||
|                 } | ||||
|             ] | ||||
|         }); | ||||
|         openmct.objectViews.addProvider(new ClockViewProvider(openmct)); | ||||
|  | ||||
|         if (options && options.enableClockIndicator === true) { | ||||
|             const clockIndicator = new Vue ({ | ||||
|                 components: { | ||||
|                     ClockIndicator | ||||
|                 }, | ||||
|                 provide: { | ||||
|                     openmct | ||||
|                 }, | ||||
|                 data() { | ||||
|                     return { | ||||
|                         indicatorFormat: CLOCK_INDICATOR_FORMAT | ||||
|                     }; | ||||
|                 }, | ||||
|                 template: '<ClockIndicator :indicator-format="indicatorFormat" />' | ||||
|             }); | ||||
|             const indicator = { | ||||
|                 element: clockIndicator.$mount().$el, | ||||
|                 key: 'clock-indicator' | ||||
|             }; | ||||
|  | ||||
|             openmct.indicators.add(indicator); | ||||
|         } | ||||
|  | ||||
|         openmct.objects.addGetInterceptor({ | ||||
|             appliesTo: (identifier, domainObject) => { | ||||
|                 return domainObject && domainObject.type === 'clock'; | ||||
|             }, | ||||
|             invoke: (identifier, domainObject) => { | ||||
|                 if (domainObject.configuration) { | ||||
|                     return domainObject; | ||||
|                 } | ||||
|  | ||||
|                 if (domainObject.clockFormat | ||||
|                     && domainObject.timezone) { | ||||
|                     const baseFormat = domainObject.clockFormat[0]; | ||||
|                     const use24 = domainObject.clockFormat[1]; | ||||
|                     const timezone = domainObject.timezone; | ||||
|  | ||||
|                     domainObject.configuration = { | ||||
|                         baseFormat, | ||||
|                         use24, | ||||
|                         timezone | ||||
|                     }; | ||||
|  | ||||
|                     openmct.objects.mutate(domainObject, 'configuration', domainObject.configuration); | ||||
|                 } | ||||
|  | ||||
|                 return domainObject; | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										231
									
								
								src/plugins/clock/pluginSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								src/plugins/clock/pluginSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,231 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| import { createOpenMct, resetApplicationState } from 'utils/testing'; | ||||
| import clockPlugin from './plugin'; | ||||
|  | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| describe("Clock plugin:", () => { | ||||
|     let openmct; | ||||
|     let clockDefinition; | ||||
|     let element; | ||||
|     let child; | ||||
|     let appHolder; | ||||
|  | ||||
|     let clockDomainObject; | ||||
|  | ||||
|     function setupClock(enableClockIndicator) { | ||||
|         return new Promise((resolve, reject) => { | ||||
|             clockDomainObject = { | ||||
|                 identifier: { | ||||
|                     key: 'clock', | ||||
|                     namespace: 'test-namespace' | ||||
|                 }, | ||||
|                 type: 'clock' | ||||
|             }; | ||||
|  | ||||
|             appHolder = document.createElement('div'); | ||||
|             appHolder.style.width = '640px'; | ||||
|             appHolder.style.height = '480px'; | ||||
|             document.body.appendChild(appHolder); | ||||
|  | ||||
|             openmct = createOpenMct(); | ||||
|  | ||||
|             element = document.createElement('div'); | ||||
|             child = document.createElement('div'); | ||||
|             element.appendChild(child); | ||||
|  | ||||
|             openmct.install(clockPlugin({ enableClockIndicator })); | ||||
|  | ||||
|             clockDefinition = openmct.types.get('clock').definition; | ||||
|             clockDefinition.initialize(clockDomainObject); | ||||
|  | ||||
|             openmct.on('start', resolve); | ||||
|             openmct.start(appHolder); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     describe("Clock view:", () => { | ||||
|         let clockViewProvider; | ||||
|         let clockView; | ||||
|         let clockViewObject; | ||||
|         let mutableClockObject; | ||||
|  | ||||
|         beforeEach(async () => { | ||||
|             await setupClock(true); | ||||
|  | ||||
|             clockViewObject = { | ||||
|                 ...clockDomainObject, | ||||
|                 id: "test-object", | ||||
|                 name: 'Clock', | ||||
|                 configuration: { | ||||
|                     baseFormat: 'YYYY/MM/DD hh:mm:ss', | ||||
|                     use24: 'clock12', | ||||
|                     timezone: 'UTC' | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             spyOn(openmct.objects, 'get').and.returnValue(Promise.resolve(clockViewObject)); | ||||
|             spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true)); | ||||
|  | ||||
|             const applicableViews = openmct.objectViews.get(clockViewObject, [clockViewObject]); | ||||
|             clockViewProvider = applicableViews.find(viewProvider => viewProvider.key === 'clock.view'); | ||||
|  | ||||
|             mutableClockObject = await openmct.objects.getMutable(clockViewObject.identifier); | ||||
|  | ||||
|             clockView = clockViewProvider.view(mutableClockObject); | ||||
|             clockView.show(child); | ||||
|  | ||||
|             await Vue.nextTick(); | ||||
|         }); | ||||
|  | ||||
|         afterEach(() => { | ||||
|             clockView.destroy(); | ||||
|             openmct.objects.destroyMutable(mutableClockObject); | ||||
|             if (appHolder) { | ||||
|                 appHolder.remove(); | ||||
|             } | ||||
|  | ||||
|             return resetApplicationState(openmct); | ||||
|         }); | ||||
|  | ||||
|         it("has name as Clock", () => { | ||||
|             expect(clockDefinition.name).toEqual('Clock'); | ||||
|         }); | ||||
|  | ||||
|         it("is creatable", () => { | ||||
|             expect(clockDefinition.creatable).toEqual(true); | ||||
|         }); | ||||
|  | ||||
|         it("provides clock view", () => { | ||||
|             expect(clockViewProvider).toBeDefined(); | ||||
|         }); | ||||
|  | ||||
|         it("renders clock element", () => { | ||||
|             const clockElement = element.querySelectorAll('.c-clock'); | ||||
|             expect(clockElement.length).toBe(1); | ||||
|         }); | ||||
|  | ||||
|         it("renders major elements", () => { | ||||
|             const clockElement = element.querySelector('.c-clock'); | ||||
|             const timezone = clockElement.querySelector('.c-clock__timezone'); | ||||
|             const time = clockElement.querySelector('.c-clock__value'); | ||||
|             const amPm = clockElement.querySelector('.c-clock__ampm'); | ||||
|             const hasMajorElements = Boolean(timezone && time && amPm); | ||||
|  | ||||
|             expect(hasMajorElements).toBe(true); | ||||
|         }); | ||||
|  | ||||
|         it("renders time in UTC", () => { | ||||
|             const clockElement = element.querySelector('.c-clock'); | ||||
|             const timezone = clockElement.querySelector('.c-clock__timezone').textContent.trim(); | ||||
|  | ||||
|             expect(timezone).toBe('UTC'); | ||||
|         }); | ||||
|  | ||||
|         it("updates the 24 hour option in the configuration", (done) => { | ||||
|             expect(clockDomainObject.configuration.use24).toBe('clock12'); | ||||
|             const new24Option = 'clock24'; | ||||
|  | ||||
|             openmct.objects.observe(clockViewObject, 'configuration', (changedDomainObject) => { | ||||
|                 expect(changedDomainObject.use24).toBe(new24Option); | ||||
|                 done(); | ||||
|             }); | ||||
|  | ||||
|             openmct.objects.mutate(clockViewObject, 'configuration.use24', new24Option); | ||||
|         }); | ||||
|  | ||||
|         it("updates the timezone option in the configuration", (done) => { | ||||
|             expect(clockDomainObject.configuration.timezone).toBe('UTC'); | ||||
|             const newZone = 'CST6CDT'; | ||||
|  | ||||
|             openmct.objects.observe(clockViewObject, 'configuration', (changedDomainObject) => { | ||||
|                 expect(changedDomainObject.timezone).toBe(newZone); | ||||
|                 done(); | ||||
|             }); | ||||
|  | ||||
|             openmct.objects.mutate(clockViewObject, 'configuration.timezone', newZone); | ||||
|         }); | ||||
|  | ||||
|         it("updates the time format option in the configuration", (done) => { | ||||
|             expect(clockDomainObject.configuration.baseFormat).toBe('YYYY/MM/DD hh:mm:ss'); | ||||
|             const newFormat = 'hh:mm:ss'; | ||||
|  | ||||
|             openmct.objects.observe(clockViewObject, 'configuration', (changedDomainObject) => { | ||||
|                 expect(changedDomainObject.baseFormat).toBe(newFormat); | ||||
|                 done(); | ||||
|             }); | ||||
|  | ||||
|             openmct.objects.mutate(clockViewObject, 'configuration.baseFormat', newFormat); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     describe("Clock Indicator view:", () => { | ||||
|         let clockIndicator; | ||||
|  | ||||
|         afterEach(() => { | ||||
|             if (clockIndicator) { | ||||
|                 clockIndicator.remove(); | ||||
|             } | ||||
|  | ||||
|             clockIndicator = undefined; | ||||
|             if (appHolder) { | ||||
|                 appHolder.remove(); | ||||
|             } | ||||
|  | ||||
|             return resetApplicationState(openmct); | ||||
|         }); | ||||
|  | ||||
|         it("doesn't exist", async () => { | ||||
|             await setupClock(false); | ||||
|  | ||||
|             clockIndicator = openmct.indicators.indicatorObjects | ||||
|                 .find(indicator => indicator.key === 'clock-indicator'); | ||||
|  | ||||
|             const clockIndicatorMissing = clockIndicator === null || clockIndicator === undefined; | ||||
|             expect(clockIndicatorMissing).toBe(true); | ||||
|         }); | ||||
|  | ||||
|         it("exists", async () => { | ||||
|             await setupClock(true); | ||||
|  | ||||
|             clockIndicator = openmct.indicators.indicatorObjects | ||||
|                 .find(indicator => indicator.key === 'clock-indicator').element; | ||||
|  | ||||
|             const hasClockIndicator = clockIndicator !== null && clockIndicator !== undefined; | ||||
|             expect(hasClockIndicator).toBe(true); | ||||
|         }); | ||||
|  | ||||
|         it("contains text", async () => { | ||||
|             await setupClock(true); | ||||
|  | ||||
|             clockIndicator = openmct.indicators.indicatorObjects | ||||
|                 .find(indicator => indicator.key === 'clock-indicator').element; | ||||
|  | ||||
|             const clockIndicatorText = clockIndicator.textContent.trim(); | ||||
|             const textIncludesUTC = clockIndicatorText.includes('UTC'); | ||||
|  | ||||
|             expect(textIncludesUTC).toBe(true); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| @@ -273,10 +273,7 @@ export default { | ||||
|             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('/'); | ||||
|                     this.navigateToPath = '#/browse/' + this.openmct.objects.getRelativePath(this.objectPath); | ||||
|                 } | ||||
|             ); | ||||
|         }, | ||||
|   | ||||
| @@ -141,6 +141,7 @@ const NON_STYLEABLE_CONTAINER_TYPES = [ | ||||
| const NON_STYLEABLE_LAYOUT_ITEM_TYPES = [ | ||||
|     'line-view', | ||||
|     'box-view', | ||||
|     'ellipse-view', | ||||
|     'image-view' | ||||
| ]; | ||||
|  | ||||
| @@ -296,10 +297,7 @@ export default { | ||||
|             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('/'); | ||||
|                     this.navigateToPath = '#/browse/' + this.openmct.objects.getRelativePath(this.objectPath); | ||||
|                 } | ||||
|             ); | ||||
|         }, | ||||
| @@ -321,7 +319,7 @@ export default { | ||||
|             if (item) { | ||||
|                 const type = this.openmct.types.get(item.type); | ||||
|                 if (type && type.definition) { | ||||
|                     creatable = (type.definition.creatable === true); | ||||
|                     creatable = (type.definition.creatable !== undefined && (type.definition.creatable === 'true' || type.definition.creatable === true)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -230,7 +230,7 @@ describe('the plugin', function () { | ||||
|         }; | ||||
|         const staticStyle = { | ||||
|             "style": { | ||||
|                 "backgroundColor": "#717171", | ||||
|                 "backgroundColor": "#666666", | ||||
|                 "border": "1px solid #00ffff" | ||||
|             } | ||||
|         }; | ||||
| @@ -238,7 +238,7 @@ describe('the plugin', function () { | ||||
|             "conditionId": "39584410-cbf9-499e-96dc-76f27e69885d", | ||||
|             "style": { | ||||
|                 "isStyleInvisible": "", | ||||
|                 "backgroundColor": "#717171", | ||||
|                 "backgroundColor": "#666666", | ||||
|                 "border": "1px solid #ffff00" | ||||
|             } | ||||
|         }; | ||||
| @@ -250,7 +250,7 @@ describe('the plugin', function () { | ||||
|                 "configuration": { | ||||
|                     "items": [ | ||||
|                         { | ||||
|                             "fill": "#717171", | ||||
|                             "fill": "#666666", | ||||
|                             "stroke": "", | ||||
|                             "x": 1, | ||||
|                             "y": 1, | ||||
| @@ -259,12 +259,22 @@ describe('the plugin', function () { | ||||
|                             "type": "box-view", | ||||
|                             "id": "89b88746-d325-487b-aec4-11b79afff9e8" | ||||
|                         }, | ||||
|                         { | ||||
|                             "fill": "#666666", | ||||
|                             "stroke": "", | ||||
|                             "x": 1, | ||||
|                             "y": 1, | ||||
|                             "width": 10, | ||||
|                             "height": 5, | ||||
|                             "type": "ellipse-view", | ||||
|                             "id": "19b88746-d325-487b-aec4-11b79afff9z8" | ||||
|                         }, | ||||
|                         { | ||||
|                             "x": 18, | ||||
|                             "y": 9, | ||||
|                             "x2": 23, | ||||
|                             "y2": 4, | ||||
|                             "stroke": "#717171", | ||||
|                             "stroke": "#666666", | ||||
|                             "type": "line-view", | ||||
|                             "id": "57d49a28-7863-43bd-9593-6570758916f0" | ||||
|                         }, | ||||
| @@ -299,12 +309,12 @@ describe('the plugin', function () { | ||||
|                 "y": 9, | ||||
|                 "x2": 23, | ||||
|                 "y2": 4, | ||||
|                 "stroke": "#717171", | ||||
|                 "stroke": "#666666", | ||||
|                 "type": "line-view", | ||||
|                 "id": "57d49a28-7863-43bd-9593-6570758916f0" | ||||
|             }; | ||||
|             boxLayoutItem = { | ||||
|                 "fill": "#717171", | ||||
|                 "fill": "#666666", | ||||
|                 "stroke": "", | ||||
|                 "x": 1, | ||||
|                 "y": 1, | ||||
|   | ||||
| @@ -29,9 +29,10 @@ const styleProps = { | ||||
|         noneValue: NONE_VALUE, | ||||
|         applicableForType: type => { | ||||
|             return !type ? true : (type === 'text-view' | ||||
|                                       || type === 'telemetry-view' | ||||
|                                       || type === 'box-view' | ||||
|                                       || type === 'subobject-view'); | ||||
|                                             || type === 'telemetry-view' | ||||
|                                             || type === 'box-view' | ||||
|                                             || type === 'ellipse-view' | ||||
|                                             || type === 'subobject-view'); | ||||
|         } | ||||
|     }, | ||||
|     border: { | ||||
| @@ -41,6 +42,7 @@ const styleProps = { | ||||
|             return !type ? true : (type === 'text-view' | ||||
|                                             || type === 'telemetry-view' | ||||
|                                             || type === 'box-view' | ||||
|                                             || type === 'ellipse-view' | ||||
|                                             || type === 'image-view' | ||||
|                                             || type === 'line-view' | ||||
|                                             || type === 'subobject-view'); | ||||
|   | ||||
| @@ -149,6 +149,7 @@ define(['lodash'], function (_) { | ||||
|                         return type === 'text-view' | ||||
|                             || type === 'telemetry-view' | ||||
|                             || type === 'box-view' | ||||
|                             || type === 'ellipse-view' | ||||
|                             || type === 'image-view' | ||||
|                             || type === 'line-view' | ||||
|                             || type === 'subobject-view'; | ||||
| @@ -180,6 +181,10 @@ define(['lodash'], function (_) { | ||||
|                                     "name": "Box", | ||||
|                                     "class": "icon-box-round-corners" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "name": "Ellipse", | ||||
|                                     "class": "icon-circle" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "name": "Line", | ||||
|                                     "class": "icon-line-horz" | ||||
| @@ -745,7 +750,7 @@ define(['lodash'], function (_) { | ||||
|                         if (toolbar.remove.length === 0) { | ||||
|                             toolbar.remove = [getRemoveButton(selectedParent, selectionPath, selectedObjects)]; | ||||
|                         } | ||||
|                     } else if (layoutItem.type === 'box-view') { | ||||
|                     } else if (layoutItem.type === 'box-view' || layoutItem.type === 'ellipse-view') { | ||||
|                         if (toolbar.position.length === 0) { | ||||
|                             toolbar.position = [ | ||||
|                                 getStackOrder(selectedParent, selectionPath), | ||||
|   | ||||
| @@ -43,7 +43,7 @@ import conditionalStylesMixin from '../mixins/objectStyles-mixin'; | ||||
| export default { | ||||
|     makeDefinition() { | ||||
|         return { | ||||
|             fill: '#717171', | ||||
|             fill: '#666666', | ||||
|             stroke: '', | ||||
|             x: 1, | ||||
|             y: 1, | ||||
|   | ||||
| @@ -76,6 +76,7 @@ import uuid from 'uuid'; | ||||
| import SubobjectView from './SubobjectView.vue'; | ||||
| import TelemetryView from './TelemetryView.vue'; | ||||
| import BoxView from './BoxView.vue'; | ||||
| import EllipseView from './EllipseView.vue'; | ||||
| import TextView from './TextView.vue'; | ||||
| import LineView from './LineView.vue'; | ||||
| import ImageView from './ImageView.vue'; | ||||
| @@ -112,6 +113,7 @@ const ITEM_TYPE_VIEW_MAP = { | ||||
|     'subobject-view': SubobjectView, | ||||
|     'telemetry-view': TelemetryView, | ||||
|     'box-view': BoxView, | ||||
|     'ellipse-view': EllipseView, | ||||
|     'line-view': LineView, | ||||
|     'text-view': TextView, | ||||
|     'image-view': ImageView | ||||
|   | ||||
| @@ -28,19 +28,19 @@ | ||||
| > | ||||
|     <div | ||||
|         class="c-frame-edit__handle c-frame-edit__handle--nw" | ||||
|         @mousedown="startResize([1,1], [-1,-1], $event)" | ||||
|         @mousedown.left="startResize([1,1], [-1,-1], $event)" | ||||
|     ></div> | ||||
|     <div | ||||
|         class="c-frame-edit__handle c-frame-edit__handle--ne" | ||||
|         @mousedown="startResize([0,1], [1,-1], $event)" | ||||
|         @mousedown.left="startResize([0,1], [1,-1], $event)" | ||||
|     ></div> | ||||
|     <div | ||||
|         class="c-frame-edit__handle c-frame-edit__handle--sw" | ||||
|         @mousedown="startResize([1,0], [-1,1], $event)" | ||||
|         @mousedown.left="startResize([1,0], [-1,1], $event)" | ||||
|     ></div> | ||||
|     <div | ||||
|         class="c-frame-edit__handle c-frame-edit__handle--se" | ||||
|         @mousedown="startResize([0,0], [1,1], $event)" | ||||
|         @mousedown.left="startResize([0,0], [1,1], $event)" | ||||
|     ></div> | ||||
| </div> | ||||
| </template> | ||||
|   | ||||
							
								
								
									
										122
									
								
								src/plugins/displayLayout/components/EllipseView.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/plugins/displayLayout/components/EllipseView.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| /***************************************************************************** | ||||
| * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
| * as represented by the Administrator of the National Aeronautics and Space | ||||
| * Administration. All rights reserved. | ||||
| * | ||||
| * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
| * "License"); you may not use this file except in compliance with the License. | ||||
| * You may obtain a copy of the License at | ||||
| * http://www.apache.org/licenses/LICENSE-2.0. | ||||
| * | ||||
| * Unless required by applicable law or agreed to in writing, software | ||||
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
| * License for the specific language governing permissions and limitations | ||||
| * under the License. | ||||
| * | ||||
| * Open MCT includes source code licensed under additional open source | ||||
| * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
| * this source code distribution or the Licensing information page available | ||||
| * at runtime from the About dialog for additional information. | ||||
| *****************************************************************************/ | ||||
|  | ||||
| <template> | ||||
| <layout-frame | ||||
|     :item="item" | ||||
|     :grid-size="gridSize" | ||||
|     :is-editing="isEditing" | ||||
|     @move="(gridDelta) => $emit('move', gridDelta)" | ||||
|     @endMove="() => $emit('endMove')" | ||||
| > | ||||
|     <div | ||||
|         class="c-ellipse-view u-style-receiver js-style-receiver" | ||||
|         :class="[styleClass]" | ||||
|         :style="style" | ||||
|     ></div> | ||||
| </layout-frame> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import LayoutFrame from './LayoutFrame.vue'; | ||||
| import conditionalStylesMixin from '../mixins/objectStyles-mixin'; | ||||
|  | ||||
| export default { | ||||
|     makeDefinition() { | ||||
|         return { | ||||
|             fill: '#666666', | ||||
|             stroke: '', | ||||
|             x: 1, | ||||
|             y: 1, | ||||
|             width: 10, | ||||
|             height: 10 | ||||
|         }; | ||||
|     }, | ||||
|     components: { | ||||
|         LayoutFrame | ||||
|     }, | ||||
|     mixins: [conditionalStylesMixin], | ||||
|     inject: ['openmct'], | ||||
|     props: { | ||||
|         item: { | ||||
|             type: Object, | ||||
|             required: true | ||||
|         }, | ||||
|         gridSize: { | ||||
|             type: Array, | ||||
|             required: true, | ||||
|             validator: (arr) => arr && arr.length === 2 | ||||
|                     && arr.every(el => typeof el === 'number') | ||||
|         }, | ||||
|         index: { | ||||
|             type: Number, | ||||
|             required: true | ||||
|         }, | ||||
|         initSelect: Boolean, | ||||
|         isEditing: { | ||||
|             type: Boolean, | ||||
|             required: true | ||||
|         } | ||||
|     }, | ||||
|     computed: { | ||||
|         style() { | ||||
|             if (this.itemStyle) { | ||||
|                 return this.itemStyle; | ||||
|             } else { | ||||
|                 return { | ||||
|                     backgroundColor: this.item.fill, | ||||
|                     border: this.item.stroke ? '1px solid ' + this.item.stroke : '' | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     watch: { | ||||
|         index(newIndex) { | ||||
|             if (!this.context) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this.context.index = newIndex; | ||||
|         }, | ||||
|         item(newItem) { | ||||
|             if (!this.context) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this.context.layoutItem = newItem; | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.context = { | ||||
|             layoutItem: this.item, | ||||
|             index: this.index | ||||
|         }; | ||||
|         this.removeSelectable = this.openmct.selection.selectable( | ||||
|             this.$el, this.context, this.initSelect); | ||||
|     }, | ||||
|     destroyed() { | ||||
|         if (this.removeSelectable) { | ||||
|             this.removeSelectable(); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| </script> | ||||
| @@ -33,7 +33,7 @@ | ||||
|     <slot></slot> | ||||
|     <div | ||||
|         class="c-frame__move-bar" | ||||
|         @mousedown="isEditing ? startMove([1,1], [0,0], $event) : null" | ||||
|         @mousedown.left="startMove($event)" | ||||
|     ></div> | ||||
| </div> | ||||
| </template> | ||||
| @@ -93,7 +93,11 @@ export default { | ||||
|                 return value - this.initialPosition[index]; | ||||
|             }.bind(this)); | ||||
|         }, | ||||
|         startMove(posFactor, dimFactor, event) { | ||||
|         startMove(event, posFactor = [1, 1], dimFactor = [0, 0]) { | ||||
|             if (!this.isEditing) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             document.body.addEventListener('mousemove', this.continueMove); | ||||
|             document.body.addEventListener('mouseup', this.endMove); | ||||
|             this.dragPosition = { | ||||
|   | ||||
| @@ -45,7 +45,7 @@ | ||||
|  | ||||
|     <div | ||||
|         class="c-frame__move-bar" | ||||
|         @mousedown="startDrag($event)" | ||||
|         @mousedown.left="startDrag($event)" | ||||
|     ></div> | ||||
|     <div | ||||
|         v-if="showFrameEdit" | ||||
| @@ -96,7 +96,7 @@ export default { | ||||
|             y: 10, | ||||
|             x2: 10, | ||||
|             y2: 5, | ||||
|             stroke: '#717171' | ||||
|             stroke: '#666666' | ||||
|         }; | ||||
|     }, | ||||
|     mixins: [conditionalStylesMixin], | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| .c-box-view { | ||||
| .c-box-view, | ||||
| .c-ellipse-view { | ||||
|     border-width: $drawingObjBorderW !important; | ||||
|     display: flex; | ||||
|     align-items: stretch; | ||||
| @@ -8,6 +9,10 @@ | ||||
|     } | ||||
| } | ||||
|  | ||||
| .c-ellipse-view { | ||||
|     border-radius: 50%; | ||||
| } | ||||
|  | ||||
| .c-line-view { | ||||
|     &.c-frame { | ||||
|         box-shadow: none !important; | ||||
|   | ||||
| @@ -186,7 +186,7 @@ describe('the plugin', function () { | ||||
|                 'configuration': { | ||||
|                     'items': [ | ||||
|                         { | ||||
|                             'fill': '#717171', | ||||
|                             'fill': '#666666', | ||||
|                             'stroke': '', | ||||
|                             'x': 1, | ||||
|                             'y': 1, | ||||
| @@ -195,12 +195,22 @@ describe('the plugin', function () { | ||||
|                             'type': 'box-view', | ||||
|                             'id': '89b88746-d325-487b-aec4-11b79afff9e8' | ||||
|                         }, | ||||
|                         { | ||||
|                             'fill': '#666666', | ||||
|                             'stroke': '', | ||||
|                             'x': 1, | ||||
|                             'y': 1, | ||||
|                             'width': 10, | ||||
|                             'height': 10, | ||||
|                             'type': 'ellipse-view', | ||||
|                             'id': '19b88746-d325-487b-aec4-11b79afff9z8' | ||||
|                         }, | ||||
|                         { | ||||
|                             'x': 18, | ||||
|                             'y': 9, | ||||
|                             'x2': 23, | ||||
|                             'y2': 4, | ||||
|                             'stroke': '#717171', | ||||
|                             'stroke': '#666666', | ||||
|                             'type': 'line-view', | ||||
|                             'id': '57d49a28-7863-43bd-9593-6570758916f0' | ||||
|                         }, | ||||
| @@ -341,7 +351,7 @@ describe('the plugin', function () { | ||||
|         it('provides controls including separators', () => { | ||||
|             const displayLayoutToolbar = openmct.toolbars.get(selection); | ||||
|  | ||||
|             expect(displayLayoutToolbar.length).toBe(9); | ||||
|             expect(displayLayoutToolbar.length).toBe(7); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -140,6 +140,7 @@ import SearchResults from './SearchResults.vue'; | ||||
| import Sidebar from './Sidebar.vue'; | ||||
| import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSectionId, setDefaultNotebookPageId } from '../utils/notebook-storage'; | ||||
| import { addNotebookEntry, createNewEmbed, getEntryPosById, getNotebookEntries, mutateObject } from '../utils/notebook-entries'; | ||||
| import { saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from '../utils/notebook-image'; | ||||
| import { NOTEBOOK_VIEW_TYPE } from '../notebook-constants'; | ||||
| import objectUtils from 'objectUtils'; | ||||
|  | ||||
| @@ -385,9 +386,13 @@ export default { | ||||
|             const snapshotId = event.dataTransfer.getData('openmct/snapshot/id'); | ||||
|             if (snapshotId.length) { | ||||
|                 const snapshot = this.snapshotContainer.getSnapshot(snapshotId); | ||||
|                 this.newEntry(snapshot); | ||||
|                 this.newEntry(snapshot.embedObject); | ||||
|                 this.snapshotContainer.removeSnapshot(snapshotId); | ||||
|  | ||||
|                 const namespace = this.domainObject.identifier.namespace; | ||||
|                 const notebookImageDomainObject = updateNamespaceOfDomainObject(snapshot.notebookImageDomainObject, namespace); | ||||
|                 saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
| @@ -451,12 +456,9 @@ export default { | ||||
|                 : undefined; | ||||
|         }, | ||||
|         getDefaultNotebookObject() { | ||||
|             const oldNotebookStorage = getDefaultNotebook(); | ||||
|             if (!oldNotebookStorage) { | ||||
|                 return null; | ||||
|             } | ||||
|             const defaultNotebook = getDefaultNotebook(); | ||||
|  | ||||
|             return this.openmct.objects.get(oldNotebookStorage.identifier); | ||||
|             return defaultNotebook && this.openmct.objects.get(defaultNotebook.identifier); | ||||
|         }, | ||||
|         getLinktoNotebook() { | ||||
|             const objectPath = this.openmct.router.path; | ||||
|   | ||||
| @@ -40,7 +40,7 @@ export default { | ||||
|     components: { | ||||
|         PopupMenu | ||||
|     }, | ||||
|     inject: ['openmct'], | ||||
|     inject: ['openmct', 'snapshotContainer'], | ||||
|     props: { | ||||
|         embed: { | ||||
|             type: Object, | ||||
| @@ -48,6 +48,12 @@ export default { | ||||
|                 return {}; | ||||
|             } | ||||
|         }, | ||||
|         isSnapshotContainer: { | ||||
|             type: Boolean, | ||||
|             default() { | ||||
|                 return false; | ||||
|             } | ||||
|         }, | ||||
|         removeActionString: { | ||||
|             type: String, | ||||
|             default() { | ||||
| @@ -135,6 +141,14 @@ export default { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (this.isSnapshotContainer) { | ||||
|                 const snapshot = this.snapshotContainer.getSnapshot(this.embed.id); | ||||
|                 const fullSizeImageURL = snapshot.notebookImageDomainObject.configuration.fullSizeImageURL; | ||||
|                 painterroInstance.show(fullSizeImageURL); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this.openmct.objects.get(fullSizeImageObjectIdentifier) | ||||
|                 .then(object => { | ||||
|                     painterroInstance.show(object.configuration.fullSizeImageURL); | ||||
| @@ -190,6 +204,14 @@ export default { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (this.isSnapshotContainer) { | ||||
|                 const snapshot = this.snapshotContainer.getSnapshot(this.embed.id); | ||||
|                 const fullSizeImageURL = snapshot.notebookImageDomainObject.configuration.fullSizeImageURL; | ||||
|                 this.openSnapshotOverlay(fullSizeImageURL); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this.openmct.objects.get(fullSizeImageObjectIdentifier) | ||||
|                 .then(object => { | ||||
|                     this.openSnapshotOverlay(object.configuration.fullSizeImageURL); | ||||
| @@ -259,8 +281,20 @@ export default { | ||||
|         updateSnapshot(snapshotObject) { | ||||
|             this.embed.snapshot.thumbnailImage = snapshotObject.thumbnailImage; | ||||
|  | ||||
|             updateNotebookImageDomainObject(this.openmct, this.embed.snapshot.fullSizeImageObjectIdentifier, snapshotObject.fullSizeImage); | ||||
|             this.updateNotebookImageDomainObjectSnapshot(snapshotObject); | ||||
|             this.updateEmbed(this.embed); | ||||
|         }, | ||||
|         updateNotebookImageDomainObjectSnapshot(snapshotObject) { | ||||
|             if (this.isSnapshotContainer) { | ||||
|                 const snapshot = this.snapshotContainer.getSnapshot(this.embed.id); | ||||
|  | ||||
|                 snapshot.embedObject.snapshot.thumbnailImage = snapshotObject.thumbnailImage; | ||||
|                 snapshot.notebookImageDomainObject.configuration.fullSizeImageURL = snapshotObject.fullSizeImage.src; | ||||
|  | ||||
|                 this.snapshotContainer.updateSnapshot(snapshot); | ||||
|             } else { | ||||
|                 updateNotebookImageDomainObject(this.openmct, this.embed.snapshot.fullSizeImageObjectIdentifier, snapshotObject.fullSizeImage); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|   | ||||
| @@ -102,9 +102,11 @@ | ||||
|  | ||||
| <script> | ||||
| import NotebookEmbed from './NotebookEmbed.vue'; | ||||
| import { createNewEmbed } from '../utils/notebook-entries'; | ||||
| import Moment from 'moment'; | ||||
| import TextHighlight from '../../../utils/textHighlight/TextHighlight.vue'; | ||||
| import { createNewEmbed } from '../utils/notebook-entries'; | ||||
| import { saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from '../utils/notebook-image'; | ||||
|  | ||||
| import Moment from 'moment'; | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
| @@ -210,8 +212,12 @@ export default { | ||||
|             const snapshotId = $event.dataTransfer.getData('openmct/snapshot/id'); | ||||
|             if (snapshotId.length) { | ||||
|                 const snapshot = this.snapshotContainer.getSnapshot(snapshotId); | ||||
|                 this.entry.embeds.push(snapshot.embedObject); | ||||
|                 this.snapshotContainer.removeSnapshot(snapshotId); | ||||
|                 this.entry.embeds.push(snapshot); | ||||
|  | ||||
|                 const namespace = this.domainObject.identifier.namespace; | ||||
|                 const notebookImageDomainObject = updateNamespaceOfDomainObject(snapshot.notebookImageDomainObject, namespace); | ||||
|                 saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject); | ||||
|             } else { | ||||
|                 const data = $event.dataTransfer.getData('openmct/domain-object-path'); | ||||
|                 const objectPath = JSON.parse(data); | ||||
|   | ||||
| @@ -17,19 +17,26 @@ | ||||
|  | ||||
| <script> | ||||
| import Snapshot from '../snapshot'; | ||||
| import { getDefaultNotebook, getNotebookSectionAndPage, validateNotebookStorageObject } from '../utils/notebook-storage'; | ||||
| import { getDefaultNotebook, validateNotebookStorageObject } from '../utils/notebook-storage'; | ||||
| import { NOTEBOOK_DEFAULT, NOTEBOOK_SNAPSHOT } from '../notebook-constants'; | ||||
| import { getMenuItems } from '../utils/notebook-snapshot-menu'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     props: { | ||||
|         currentView: { | ||||
|             type: Object, | ||||
|             default() { | ||||
|                 return {}; | ||||
|             } | ||||
|         }, | ||||
|         domainObject: { | ||||
|             type: Object, | ||||
|             default() { | ||||
|                 return {}; | ||||
|             } | ||||
|         }, | ||||
|         ignoreLink: { | ||||
|         isPreview: { | ||||
|             type: Boolean, | ||||
|             default() { | ||||
|                 return false; | ||||
| @@ -50,66 +57,53 @@ export default { | ||||
|     }, | ||||
|     mounted() { | ||||
|         validateNotebookStorageObject(); | ||||
|         this.getDefaultNotebookObject(); | ||||
|  | ||||
|         this.notebookSnapshot = new Snapshot(this.openmct); | ||||
|         this.setDefaultNotebookStatus(); | ||||
|     }, | ||||
|     methods: { | ||||
|         getDefaultNotebookObject() { | ||||
|             const defaultNotebook = getDefaultNotebook(); | ||||
|         getPreviewObjectLink() { | ||||
|             const relativePath = this.openmct.objects.getRelativePath(this.objectPath); | ||||
|             const urlParams = this.openmct.router.getParams(); | ||||
|             urlParams.view = this.currentView.key; | ||||
|  | ||||
|             return defaultNotebook && this.openmct.objects.get(defaultNotebook.identifier); | ||||
|             const urlParamsString = Object.entries(urlParams) | ||||
|                 .map(([key, value]) => `${key}=${value}`) | ||||
|                 .join('&'); | ||||
|  | ||||
|             return `#/browse/${relativePath}?${urlParamsString}`; | ||||
|         }, | ||||
|         async showMenu(event) { | ||||
|             const notebookTypes = []; | ||||
|             const menuItemOptions = { | ||||
|                 default: { | ||||
|                     cssClass: 'icon-notebook', | ||||
|                     name: `Save to Notebook`, | ||||
|                     onItemClicked: () => this.snapshot(NOTEBOOK_DEFAULT, event.target) | ||||
|                 }, | ||||
|                 snapshot: { | ||||
|                     cssClass: 'icon-camera', | ||||
|                     name: 'Save to Notebook Snapshots', | ||||
|                     onItemClicked: () => this.snapshot(NOTEBOOK_SNAPSHOT, event.target) | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             const notebookTypes = await getMenuItems(this.openmct, menuItemOptions); | ||||
|             const elementBoundingClientRect = this.$el.getBoundingClientRect(); | ||||
|             const x = elementBoundingClientRect.x; | ||||
|             const y = elementBoundingClientRect.y + elementBoundingClientRect.height; | ||||
|  | ||||
|             const defaultNotebookObject = await this.getDefaultNotebookObject(); | ||||
|             if (defaultNotebookObject) { | ||||
|                 const defaultNotebook = getDefaultNotebook(); | ||||
|                 const { section, page } = getNotebookSectionAndPage(defaultNotebookObject, defaultNotebook.defaultSectionId, defaultNotebook.defaultPageId); | ||||
|                 if (section && page) { | ||||
|                     const name = defaultNotebookObject.name; | ||||
|                     const sectionName = section.name; | ||||
|                     const pageName = page.name; | ||||
|                     const defaultPath = `${name} - ${sectionName} - ${pageName}`; | ||||
|  | ||||
|                     notebookTypes.push({ | ||||
|                         cssClass: 'icon-notebook', | ||||
|                         name: `Save to Notebook ${defaultPath}`, | ||||
|                         onItemClicked: () => { | ||||
|                             return this.snapshot(NOTEBOOK_DEFAULT); | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             notebookTypes.push({ | ||||
|                 cssClass: 'icon-camera', | ||||
|                 name: 'Save to Notebook Snapshots', | ||||
|                 onItemClicked: () => { | ||||
|                     return this.snapshot(NOTEBOOK_SNAPSHOT); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             this.openmct.menus.showMenu(x, y, notebookTypes); | ||||
|         }, | ||||
|         snapshot(notebookType) { | ||||
|         snapshot(notebookType, target) { | ||||
|             this.$nextTick(() => { | ||||
|                 const element = document.querySelector('.c-overlay__contents') | ||||
|                     || document.getElementsByClassName('l-shell__main-container')[0]; | ||||
|  | ||||
|                 const bounds = this.openmct.time.bounds(); | ||||
|                 const link = !this.ignoreLink | ||||
|                     ? window.location.hash | ||||
|                     : null; | ||||
|  | ||||
|                 const wrapper = target && target.closest('.js-notebook-snapshot-item-wrapper') | ||||
|                     || document; | ||||
|                 const element = wrapper.querySelector('.js-notebook-snapshot-item'); | ||||
|                 const objectPath = this.objectPath || this.openmct.router.path; | ||||
|                 const link = this.isPreview | ||||
|                     ? this.getPreviewObjectLink() | ||||
|                     : window.location.hash; | ||||
|                 const snapshotMeta = { | ||||
|                     bounds, | ||||
|                     bounds: this.openmct.time.bounds(), | ||||
|                     link, | ||||
|                     objectPath, | ||||
|                     openmct: this.openmct | ||||
|   | ||||
| @@ -27,15 +27,15 @@ | ||||
|     </div><!-- closes l-browse-bar --> | ||||
|     <div class="c-snapshots"> | ||||
|         <span v-for="snapshot in snapshots" | ||||
|               :key="snapshot.id" | ||||
|               :key="snapshot.embedObject.id" | ||||
|               draggable="true" | ||||
|               @dragstart="startEmbedDrag(snapshot, $event)" | ||||
|         > | ||||
|             <NotebookEmbed ref="notebookEmbed" | ||||
|                            :key="snapshot.id" | ||||
|                            :embed="snapshot" | ||||
|                            :key="snapshot.embedObject.id" | ||||
|                            :embed="snapshot.embedObject" | ||||
|                            :is-snapshot-container="true" | ||||
|                            :remove-action-string="'Delete Snapshot'" | ||||
|                            @updateEmbed="updateSnapshot" | ||||
|                            @removeEmbed="removeSnapshot" | ||||
|             /> | ||||
|         </span> | ||||
| @@ -119,11 +119,8 @@ export default { | ||||
|             this.snapshots = this.snapshotContainer.getSnapshots(); | ||||
|         }, | ||||
|         startEmbedDrag(snapshot, event) { | ||||
|             event.dataTransfer.setData('text/plain', snapshot.id); | ||||
|             event.dataTransfer.setData('openmct/snapshot/id', snapshot.id); | ||||
|         }, | ||||
|         updateSnapshot(snapshot) { | ||||
|             this.snapshotContainer.updateSnapshot(snapshot); | ||||
|             event.dataTransfer.setData('text/plain', snapshot.embedObject.id); | ||||
|             event.dataTransfer.setData('openmct/snapshot/id', snapshot.embedObject.id); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|   | ||||
| @@ -18,13 +18,18 @@ export default class SnapshotContainer extends EventEmitter { | ||||
|         return SnapshotContainer.instance; | ||||
|     } | ||||
|  | ||||
|     addSnapshot(embedObject) { | ||||
|     addSnapshot(notebookImageDomainObject, embedObject) { | ||||
|         const snapshots = this.getSnapshots(); | ||||
|         if (snapshots.length >= NOTEBOOK_SNAPSHOT_MAX_COUNT) { | ||||
|             snapshots.pop(); | ||||
|         } | ||||
|  | ||||
|         snapshots.unshift(embedObject); | ||||
|         const snapshotObject = { | ||||
|             notebookImageDomainObject, | ||||
|             embedObject | ||||
|         }; | ||||
|  | ||||
|         snapshots.unshift(snapshotObject); | ||||
|  | ||||
|         return this.saveSnapshots(snapshots); | ||||
|     } | ||||
| @@ -32,7 +37,7 @@ export default class SnapshotContainer extends EventEmitter { | ||||
|     getSnapshot(id) { | ||||
|         const snapshots = this.getSnapshots(); | ||||
|  | ||||
|         return snapshots.find(s => s.id === id); | ||||
|         return snapshots.find(s => s.embedObject.id === id); | ||||
|     } | ||||
|  | ||||
|     getSnapshots() { | ||||
| @@ -47,7 +52,7 @@ export default class SnapshotContainer extends EventEmitter { | ||||
|         } | ||||
|  | ||||
|         const snapshots = this.getSnapshots(); | ||||
|         const filteredsnapshots = snapshots.filter(snapshot => snapshot.id !== id); | ||||
|         const filteredsnapshots = snapshots.filter(snapshot => snapshot.embedObject.id !== id); | ||||
|  | ||||
|         return this.saveSnapshots(filteredsnapshots); | ||||
|     } | ||||
| @@ -73,7 +78,7 @@ export default class SnapshotContainer extends EventEmitter { | ||||
|     updateSnapshot(snapshot) { | ||||
|         const snapshots = this.getSnapshots(); | ||||
|         const updatedSnapshots = snapshots.map(s => { | ||||
|             return s.id === snapshot.id | ||||
|             return s.embedObject.id === snapshot.embedObject.id | ||||
|                 ? snapshot | ||||
|                 : s; | ||||
|         }); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { addNotebookEntry, createNewEmbed } from './utils/notebook-entries'; | ||||
| import { getDefaultNotebook, getNotebookSectionAndPage, getDefaultNotebookLink, setDefaultNotebook } from './utils/notebook-storage'; | ||||
| import { NOTEBOOK_DEFAULT } from '@/plugins/notebook/notebook-constants'; | ||||
| import { createNotebookImageDomainObject, DEFAULT_SIZE } from './utils/notebook-image'; | ||||
| import { createNotebookImageDomainObject, saveNotebookImageDomainObject, updateNamespaceOfDomainObject, DEFAULT_SIZE } from './utils/notebook-image'; | ||||
|  | ||||
| import SnapshotContainer from './snapshot-container'; | ||||
| import ImageExporter from '../../exporters/ImageExporter'; | ||||
| @@ -35,29 +35,28 @@ export default class Snapshot { | ||||
|      * @private | ||||
|      */ | ||||
|     _saveSnapShot(notebookType, fullSizeImageURL, thumbnailImageURL, snapshotMeta) { | ||||
|         createNotebookImageDomainObject(this.openmct, fullSizeImageURL) | ||||
|             .then(object => { | ||||
|                 const thumbnailImage = { src: thumbnailImageURL || '' }; | ||||
|                 const snapshot = { | ||||
|                     fullSizeImageObjectIdentifier: object.identifier, | ||||
|                     thumbnailImage | ||||
|                 }; | ||||
|                 const embed = createNewEmbed(snapshotMeta, snapshot); | ||||
|                 if (notebookType === NOTEBOOK_DEFAULT) { | ||||
|                     this._saveToDefaultNoteBook(embed); | ||||
|         const object = createNotebookImageDomainObject(fullSizeImageURL); | ||||
|         const thumbnailImage = { src: thumbnailImageURL || '' }; | ||||
|         const snapshot = { | ||||
|             fullSizeImageObjectIdentifier: object.identifier, | ||||
|             thumbnailImage | ||||
|         }; | ||||
|         const embed = createNewEmbed(snapshotMeta, snapshot); | ||||
|         if (notebookType === NOTEBOOK_DEFAULT) { | ||||
|             const notebookStorage = getDefaultNotebook(); | ||||
|  | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 this._saveToNotebookSnapshots(embed); | ||||
|             }); | ||||
|             this._saveToDefaultNoteBook(notebookStorage, embed); | ||||
|             const notebookImageDomainObject = updateNamespaceOfDomainObject(object, notebookStorage.identifier.namespace); | ||||
|             saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject); | ||||
|         } else { | ||||
|             this._saveToNotebookSnapshots(object, embed); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @private | ||||
|      */ | ||||
|     _saveToDefaultNoteBook(embed) { | ||||
|         const notebookStorage = getDefaultNotebook(); | ||||
|     _saveToDefaultNoteBook(notebookStorage, embed) { | ||||
|         this.openmct.objects.get(notebookStorage.identifier) | ||||
|             .then(async (domainObject) => { | ||||
|                 addNotebookEntry(this.openmct, domainObject, notebookStorage, embed); | ||||
| @@ -85,19 +84,22 @@ export default class Snapshot { | ||||
|     /** | ||||
|      * @private | ||||
|      */ | ||||
|     _saveToNotebookSnapshots(embed) { | ||||
|         this.snapshotContainer.addSnapshot(embed); | ||||
|     _saveToNotebookSnapshots(notebookImageDomainObject, embed) { | ||||
|         this.snapshotContainer.addSnapshot(notebookImageDomainObject, embed); | ||||
|     } | ||||
|  | ||||
|     _showNotification(msg, url) { | ||||
|         const options = { | ||||
|             autoDismissTimeout: 30000, | ||||
|             link: { | ||||
|             autoDismissTimeout: 30000 | ||||
|         }; | ||||
|  | ||||
|         if (!this.openmct.editor.isEditing()) { | ||||
|             options.link = { | ||||
|                 cssClass: '', | ||||
|                 text: 'click to view', | ||||
|                 onClick: this._navigateToNotebook(url) | ||||
|             } | ||||
|         }; | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         this.openmct.notifications.info(msg, options); | ||||
|     } | ||||
| @@ -108,7 +110,8 @@ export default class Snapshot { | ||||
|         } | ||||
|  | ||||
|         return () => { | ||||
|             window.location.href = window.location.origin + url; | ||||
|             const path = window.location.href.split('#'); | ||||
|             window.location.href = path[0] + url; | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,14 +5,14 @@ export const DEFAULT_SIZE = { | ||||
|     height: 30 | ||||
| }; | ||||
|  | ||||
| export function createNotebookImageDomainObject(openmct, fullSizeImageURL) { | ||||
| export function createNotebookImageDomainObject(fullSizeImageURL) { | ||||
|     const identifier = { | ||||
|         key: uuid(), | ||||
|         namespace: '' | ||||
|     }; | ||||
|     const viewType = 'notebookSnapshotImage'; | ||||
|  | ||||
|     const object = { | ||||
|     return { | ||||
|         name: 'Notebook Snapshot Image', | ||||
|         type: viewType, | ||||
|         identifier, | ||||
| @@ -20,21 +20,6 @@ export function createNotebookImageDomainObject(openmct, fullSizeImageURL) { | ||||
|             fullSizeImageURL | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     return new Promise((resolve, reject) => { | ||||
|         openmct.objects.save(object) | ||||
|             .then(result => { | ||||
|                 if (result) { | ||||
|                     resolve(object); | ||||
|                 } | ||||
|  | ||||
|                 reject(); | ||||
|             }) | ||||
|             .catch(e => { | ||||
|                 console.error(e); | ||||
|                 reject(); | ||||
|             }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function getThumbnailURLFromCanvas(canvas, size = DEFAULT_SIZE) { | ||||
| @@ -67,6 +52,23 @@ export function getThumbnailURLFromimageUrl(imageUrl, size = DEFAULT_SIZE) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function saveNotebookImageDomainObject(openmct, object) { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         openmct.objects.save(object) | ||||
|             .then(result => { | ||||
|                 if (result) { | ||||
|                     resolve(object); | ||||
|                 } else { | ||||
|                     reject(); | ||||
|                 } | ||||
|             }) | ||||
|             .catch(e => { | ||||
|                 console.error(e); | ||||
|                 reject(); | ||||
|             }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function updateNotebookImageDomainObject(openmct, identifier, fullSizeImage) { | ||||
|     openmct.objects.get(identifier) | ||||
|         .then(domainObject => { | ||||
| @@ -76,3 +78,9 @@ export function updateNotebookImageDomainObject(openmct, identifier, fullSizeIma | ||||
|             openmct.objects.mutate(domainObject, 'configuration', configuration); | ||||
|         }); | ||||
| } | ||||
|  | ||||
| export function updateNamespaceOfDomainObject(object, namespace) { | ||||
|     object.identifier.namespace = namespace; | ||||
|  | ||||
|     return object; | ||||
| } | ||||
|   | ||||
| @@ -1,16 +1,18 @@ | ||||
| import { createNotebookImageDomainObject, getThumbnailURLFromimageUrl } from './notebook-image'; | ||||
| import { createNotebookImageDomainObject, getThumbnailURLFromimageUrl, saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from './notebook-image'; | ||||
| import { mutateObject } from './notebook-entries'; | ||||
|  | ||||
| const IMAGE_MIGRATION_VER = "v1"; | ||||
|  | ||||
| export function notebookImageMigration(openmct, domainObject) { | ||||
|     const configuration = domainObject.configuration; | ||||
|     const notebookEntries = configuration.entries; | ||||
|  | ||||
|     const imageMigrationVer = configuration.imageMigrationVer; | ||||
|     if (imageMigrationVer && imageMigrationVer === 'v1') { | ||||
|     if (imageMigrationVer && imageMigrationVer === IMAGE_MIGRATION_VER) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     configuration.imageMigrationVer = 'v1'; | ||||
|     configuration.imageMigrationVer = IMAGE_MIGRATION_VER; | ||||
|  | ||||
|     // to avoid muliple notebookImageMigration calls updating images. | ||||
|     mutateObject(openmct, domainObject, 'configuration', configuration); | ||||
| @@ -27,14 +29,16 @@ export function notebookImageMigration(openmct, domainObject) { | ||||
|                     const fullSizeImageURL = snapshot.src; | ||||
|                     if (fullSizeImageURL) { | ||||
|                         const thumbnailImageURL = await getThumbnailURLFromimageUrl(fullSizeImageURL); | ||||
|                         const notebookImageDomainObject = await createNotebookImageDomainObject(openmct, fullSizeImageURL); | ||||
|  | ||||
|                         const object = createNotebookImageDomainObject(fullSizeImageURL); | ||||
|                         const notebookImageDomainObject = updateNamespaceOfDomainObject(object, domainObject.identifier.namespace); | ||||
|                         embed.snapshot = { | ||||
|                             fullSizeImageObjectIdentifier: notebookImageDomainObject.identifier, | ||||
|                             thumbnailImage: { src: thumbnailImageURL || '' } | ||||
|                         }; | ||||
|  | ||||
|                         mutateObject(openmct, domainObject, 'configuration.entries', notebookEntries); | ||||
|  | ||||
|                         saveNotebookImageDomainObject(openmct, notebookImageDomainObject); | ||||
|                     } | ||||
|                 }); | ||||
|             }); | ||||
|   | ||||
							
								
								
									
										31
									
								
								src/plugins/notebook/utils/notebook-snapshot-menu.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/plugins/notebook/utils/notebook-snapshot-menu.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import { getDefaultNotebook, getNotebookSectionAndPage } from './notebook-storage'; | ||||
|  | ||||
| export async function getMenuItems(openmct, menuItemOptions) { | ||||
|     const notebookTypes = []; | ||||
|  | ||||
|     const defaultNotebook = getDefaultNotebook(); | ||||
|     const defaultNotebookObject = defaultNotebook && await openmct.objects.get(defaultNotebook.identifier); | ||||
|     if (defaultNotebookObject) { | ||||
|         const { section, page } = getNotebookSectionAndPage(defaultNotebookObject, defaultNotebook.defaultSectionId, defaultNotebook.defaultPageId); | ||||
|         if (section && page) { | ||||
|             const name = defaultNotebookObject.name; | ||||
|             const sectionName = section.name; | ||||
|             const pageName = page.name; | ||||
|             const defaultPath = `${name} - ${sectionName} - ${pageName}`; | ||||
|  | ||||
|             notebookTypes.push({ | ||||
|                 cssClass: menuItemOptions.default.cssClass, | ||||
|                 name: `${menuItemOptions.default.name} ${defaultPath}`, | ||||
|                 onItemClicked: menuItemOptions.default.onItemClicked | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     notebookTypes.push({ | ||||
|         cssClass: menuItemOptions.snapshot.cssClass, | ||||
|         name: menuItemOptions.snapshot.name, | ||||
|         onItemClicked: menuItemOptions.snapshot.onItemClicked | ||||
|     }); | ||||
|  | ||||
|     return notebookTypes; | ||||
| } | ||||
| @@ -71,11 +71,7 @@ export async function getDefaultNotebookLink(openmct, domainObject = null) { | ||||
|     } | ||||
|  | ||||
|     const path = await openmct.objects.getOriginalPath(domainObject.identifier) | ||||
|         .then(objectPath => objectPath | ||||
|             .map(o => o && openmct.objects.makeKeyString(o.identifier)) | ||||
|             .reverse() | ||||
|             .join('/') | ||||
|         ); | ||||
|         .then(openmct.objects.getRelativePath); | ||||
|     const { defaultPageId, defaultSectionId } = getDefaultNotebook(); | ||||
|  | ||||
|     return `#/browse/${path}?sectionId=${defaultSectionId}&pageId=${defaultPageId}`; | ||||
| @@ -103,10 +99,13 @@ export function validateNotebookStorageObject() { | ||||
|  | ||||
|     let valid = false; | ||||
|     if (notebookStorage) { | ||||
|         Object.entries(notebookStorage).forEach(([key, value]) => { | ||||
|         const oldInvalidKeys = ['notebookMeta', 'page', 'section']; | ||||
|         valid = Object.entries(notebookStorage).every(([key, value]) => { | ||||
|             const validKey = key !== undefined && key !== null; | ||||
|             const validValue = value !== undefined && value !== null; | ||||
|             valid = validKey && validValue; | ||||
|             const hasOldInvalidKeys = oldInvalidKeys.includes(key); | ||||
|  | ||||
|             return validKey && validValue && !hasOldInvalidKeys; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -323,6 +323,8 @@ export default { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             this.offsetWidth = this.$parent.$refs.plotWrapper.offsetWidth; | ||||
|  | ||||
|             this.startLoading(); | ||||
|             const options = { | ||||
|                 size: this.$parent.$refs.plotWrapper.offsetWidth, | ||||
| @@ -391,12 +393,31 @@ export default { | ||||
|             }); | ||||
|         }, | ||||
|  | ||||
|         clearData() { | ||||
|         clearSeries() { | ||||
|             this.config.series.forEach(function (series) { | ||||
|                 series.reset(); | ||||
|             }); | ||||
|         }, | ||||
|  | ||||
|         compositionPathContainsId(domainObjectToClear) { | ||||
|             return domainObjectToClear.composition.some((compositionIdentifier) => { | ||||
|                 return this.openmct.objects.areIdsEqual(compositionIdentifier, this.domainObject.identifier); | ||||
|             }); | ||||
|         }, | ||||
|  | ||||
|         clearData(domainObjectToClear) { | ||||
|             // If we don't have an object to clear (global), or the IDs are equal, just clear the data. | ||||
|             // If we have an object to clear, but the IDs don't match, we need to check the composition | ||||
|             // of the object we've been asked to clear to see if it contains the id we're looking for. | ||||
|             // This happens with stacked plots for example. | ||||
|             // If we find the ID, clear the plot. | ||||
|             if (!domainObjectToClear | ||||
|             || this.openmct.objects.areIdsEqual(domainObjectToClear.identifier, this.domainObject.identifier) | ||||
|             || this.compositionPathContainsId(domainObjectToClear)) { | ||||
|                 this.clearSeries(); | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         setDisplayRange(series, xKey) { | ||||
|             if (this.config.series.length !== 1) { | ||||
|                 return; | ||||
| @@ -1008,7 +1029,8 @@ export default { | ||||
|             this.$emit('statusUpdated', status); | ||||
|         }, | ||||
|         handleWindowResize() { | ||||
|             if (this.offsetWidth !== this.$parent.$refs.plotWrapper.offsetWidth) { | ||||
|             if (this.$parent.$refs.plotWrapper | ||||
|                 && (this.offsetWidth !== this.$parent.$refs.plotWrapper.offsetWidth)) { | ||||
|                 this.offsetWidth = this.$parent.$refs.plotWrapper.offsetWidth; | ||||
|                 this.config.series.models.forEach(this.loadSeriesData, this); | ||||
|             } | ||||
|   | ||||
| @@ -68,7 +68,9 @@ define([ | ||||
|     './performanceIndicator/plugin', | ||||
|     './CouchDBSearchFolder/plugin', | ||||
|     './timeline/plugin', | ||||
|     './hyperlink/plugin' | ||||
|     './hyperlink/plugin', | ||||
|     './clock/plugin', | ||||
|     './DeviceClassifier/plugin' | ||||
| ], function ( | ||||
|     _, | ||||
|     UTCTimeSystem, | ||||
| @@ -117,7 +119,9 @@ define([ | ||||
|     PerformanceIndicator, | ||||
|     CouchDBSearchFolder, | ||||
|     Timeline, | ||||
|     Hyperlink | ||||
|     Hyperlink, | ||||
|     Clock, | ||||
|     DeviceClassifier | ||||
| ) { | ||||
|     const bundleMap = { | ||||
|         LocalStorage: 'platform/persistence/local', | ||||
| @@ -221,6 +225,8 @@ define([ | ||||
|     plugins.CouchDBSearchFolder = CouchDBSearchFolder.default; | ||||
|     plugins.Timeline = Timeline.default; | ||||
|     plugins.Hyperlink = Hyperlink.default; | ||||
|     plugins.Clock = Clock.default; | ||||
|     plugins.DeviceClassifier = DeviceClassifier.default; | ||||
|  | ||||
|     return plugins; | ||||
| }); | ||||
|   | ||||
| @@ -36,7 +36,7 @@ define([ | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convets all objects in an object make from old format objects to new | ||||
|      * Converts all objects in an object make from old format objects to new | ||||
|      * format objects. | ||||
|      */ | ||||
|     function convertToNewObjects(oldObjectMap) { | ||||
|   | ||||
| @@ -115,7 +115,7 @@ define([ | ||||
|             expect(condition.evaluate(telemetryState)).toBe(true); | ||||
|         }); | ||||
|  | ||||
|         it('can evalute "if any object matches"', function () { | ||||
|         it('can evaluate "if any object matches"', function () { | ||||
|             condition = new SummaryWidgetCondition({ | ||||
|                 object: 'any', | ||||
|                 key: 'raw', | ||||
|   | ||||
| @@ -379,7 +379,7 @@ define(['../src/ConditionManager'], function (ConditionManager) { | ||||
|             telemetryRequests[1].resolve([mockTelemetryValues.mockCompObject2]); | ||||
|         }); | ||||
|  | ||||
|         it('updates its LAD cache upon recieving telemetry and invokes the appropriate handlers', function () { | ||||
|         it('updates its LAD cache upon receiving telemetry and invokes the appropriate handlers', function () { | ||||
|             mockTelemetryAPI.triggerTelemetryCallback('mockCompObject1'); | ||||
|             expect(conditionManager.subscriptionCache.mockCompObject1.property1).toEqual('Its a different string'); | ||||
|             mockTelemetryAPI.triggerTelemetryCallback('mockCompObject2'); | ||||
|   | ||||
| @@ -48,7 +48,7 @@ | ||||
|         display: flex; | ||||
|         flex-flow: column nowrap; | ||||
|         flex: 1 1 auto; | ||||
|         height: 0; // Chrome 73 oveflow bug fix | ||||
|         height: 0; // Chrome 73 overflow bug fix | ||||
|     } | ||||
|  | ||||
|     &__empty-message { | ||||
|   | ||||
| @@ -149,10 +149,11 @@ define([ | ||||
|             this.removeTelemetryCollection(keyString); | ||||
|  | ||||
|             this.telemetryCollections[keyString] = this.openmct.telemetry | ||||
|                 .requestTelemetryCollection(telemetryObject, requestOptions); | ||||
|                 .requestCollection(telemetryObject, requestOptions); | ||||
|  | ||||
|             this.telemetryCollections[keyString].on('remove', telemetryRemover); | ||||
|             this.telemetryCollections[keyString].on('add', telemetryProcessor); | ||||
|             this.telemetryCollections[keyString].on('clear', this.tableRows.clear); | ||||
|             this.telemetryCollections[keyString].load(); | ||||
|  | ||||
|             this.decrementOutstandingRequests(); | ||||
|   | ||||
| @@ -20,7 +20,7 @@ | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| import PreviewHeader from '@/ui/preview/preview-header.vue'; | ||||
| import Preview from '@/ui/preview/Preview.vue'; | ||||
|  | ||||
| import Vue from 'vue'; | ||||
|  | ||||
| @@ -46,7 +46,7 @@ export default class ViewLargeAction { | ||||
|             throw new Error(message); | ||||
|         } | ||||
|  | ||||
|         this._expand(objectPath, childElement, view); | ||||
|         this._expand(objectPath, childElement); | ||||
|     } | ||||
|  | ||||
|     appliesTo(objectPath, view = {}) { | ||||
| @@ -58,49 +58,29 @@ export default class ViewLargeAction { | ||||
|         return viewLargeAction; | ||||
|     } | ||||
|  | ||||
|     _expand(objectPath, childElement, view) { | ||||
|     _expand(objectPath, childElement) { | ||||
|         const parentElement = childElement.parentElement; | ||||
|  | ||||
|         this.overlay = this.openmct.overlays.overlay({ | ||||
|             element: this._getOverlayElement(objectPath, childElement, view), | ||||
|             element: this._getPreview(objectPath), | ||||
|             size: 'large', | ||||
|             autoHide: false, | ||||
|             onDestroy() { | ||||
|                 parentElement.append(childElement); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     _getOverlayElement(objectPath, childElement, view) { | ||||
|         const fragment = new DocumentFragment(); | ||||
|         const header = this._getPreviewHeader(objectPath, view); | ||||
|         fragment.append(header); | ||||
|  | ||||
|         const wrapper = document.createElement('div'); | ||||
|         wrapper.classList.add('l-preview-window__object-view'); | ||||
|         wrapper.append(childElement); | ||||
|         fragment.append(wrapper); | ||||
|  | ||||
|         return fragment; | ||||
|     } | ||||
|  | ||||
|     _getPreviewHeader(objectPath, view) { | ||||
|         const domainObject = objectPath[0]; | ||||
|         const actionCollection = this.openmct.actions.getActionsCollection(objectPath, view); | ||||
|     _getPreview(objectPath) { | ||||
|         const preview = new Vue({ | ||||
|             components: { | ||||
|                 PreviewHeader | ||||
|                 Preview | ||||
|             }, | ||||
|             provide: { | ||||
|                 openmct: this.openmct, | ||||
|                 objectPath: this.objectPath | ||||
|                 objectPath | ||||
|             }, | ||||
|             data() { | ||||
|                 return { | ||||
|                     domainObject, | ||||
|                     actionCollection | ||||
|                 }; | ||||
|             }, | ||||
|             template: '<PreviewHeader :actionCollection="actionCollection" :domainObject="domainObject" :hideViewSwitcher="true" :showNotebookMenuSwitcher="true"></PreviewHeader>' | ||||
|             template: '<Preview></Preview>' | ||||
|         }); | ||||
|  | ||||
|         return preview.$mount().$el; | ||||
|   | ||||
| @@ -154,6 +154,7 @@ $glyph-icon-flag: '\e92a'; | ||||
| $glyph-icon-eye-disabled: '\e92b'; | ||||
| $glyph-icon-notebook-page: '\e92c'; | ||||
| $glyph-icon-unlocked: '\e92d'; | ||||
| $glyph-icon-circle: '\e92e'; | ||||
| $glyph-icon-arrows-right-left: '\ea00'; | ||||
| $glyph-icon-arrows-up-down: '\ea01'; | ||||
| $glyph-icon-bullet: '\ea02'; | ||||
| @@ -257,6 +258,7 @@ $glyph-icon-conditional: '\eb27'; | ||||
| $glyph-icon-condition-widget: '\eb28'; | ||||
| $glyph-icon-alphanumeric: '\eb29'; | ||||
| $glyph-icon-image-telemetry: '\eb2a'; | ||||
| $glyph-icon-telemetry-aggregate: '\eb2b'; | ||||
|  | ||||
| /************************** GLYPHS AS DATA URI */ | ||||
| // Only objects have been converted, for use in Create menu and folder views | ||||
|   | ||||
| @@ -85,6 +85,7 @@ | ||||
| .icon-eye-disabled {  @include glyphBefore($glyph-icon-eye-disabled); } | ||||
| .icon-notebook-page {  @include glyphBefore($glyph-icon-notebook-page); } | ||||
| .icon-unlocked {  @include glyphBefore($glyph-icon-unlocked); } | ||||
| .icon-circle {  @include glyphBefore($glyph-icon-circle); } | ||||
| .icon-arrows-right-left {  @include glyphBefore($glyph-icon-arrows-right-left); } | ||||
| .icon-arrows-up-down {  @include glyphBefore($glyph-icon-arrows-up-down); } | ||||
| .icon-bullet {  @include glyphBefore($glyph-icon-bullet); } | ||||
| @@ -188,6 +189,7 @@ | ||||
| .icon-condition-widget {  @include glyphBefore($glyph-icon-condition-widget); } | ||||
| .icon-alphanumeric {  @include glyphBefore($glyph-icon-alphanumeric); } | ||||
| .icon-image-telemetry {  @include glyphBefore($glyph-icon-image-telemetry); } | ||||
| .icon-telemetry-aggregate {  @include glyphBefore($glyph-icon-telemetry-aggregate); } | ||||
|  | ||||
| /************************** 12 PX CLASSES */ | ||||
| // TODO: sync with 16px redo as of 10/25/18 | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "metadata": { | ||||
|     "name": "Open MCT Symbols 16px", | ||||
|     "lastOpened": 0, | ||||
|     "created": 1621648023886 | ||||
|     "created": 1629996145999 | ||||
|   }, | ||||
|   "iconSets": [ | ||||
|     { | ||||
| @@ -375,13 +375,21 @@ | ||||
|           "code": 59693, | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 197, | ||||
|           "id": 169, | ||||
|           "name": "icon-circle", | ||||
|           "prevSize": 24, | ||||
|           "code": 59694, | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 27, | ||||
|           "id": 105, | ||||
|           "name": "icon-arrows-right-left", | ||||
|           "prevSize": 24, | ||||
|           "code": 59904, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 26, | ||||
| @@ -389,7 +397,7 @@ | ||||
|           "name": "icon-arrows-up-down", | ||||
|           "prevSize": 24, | ||||
|           "code": 59905, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 68, | ||||
| @@ -397,7 +405,7 @@ | ||||
|           "name": "icon-bullet", | ||||
|           "prevSize": 24, | ||||
|           "code": 59906, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 150, | ||||
| @@ -405,7 +413,7 @@ | ||||
|           "prevSize": 24, | ||||
|           "code": 59907, | ||||
|           "name": "icon-calendar", | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 45, | ||||
| @@ -413,7 +421,7 @@ | ||||
|           "name": "icon-chain-links", | ||||
|           "prevSize": 24, | ||||
|           "code": 59908, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 73, | ||||
| @@ -421,7 +429,7 @@ | ||||
|           "name": "icon-download", | ||||
|           "prevSize": 24, | ||||
|           "code": 59909, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 39, | ||||
| @@ -429,7 +437,7 @@ | ||||
|           "name": "icon-duplicate", | ||||
|           "prevSize": 24, | ||||
|           "code": 59910, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 50, | ||||
| @@ -437,7 +445,7 @@ | ||||
|           "name": "icon-folder-new", | ||||
|           "prevSize": 24, | ||||
|           "code": 59911, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 138, | ||||
| @@ -445,7 +453,7 @@ | ||||
|           "name": "icon-fullscreen-collapse", | ||||
|           "prevSize": 24, | ||||
|           "code": 59912, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 139, | ||||
| @@ -453,7 +461,7 @@ | ||||
|           "name": "icon-fullscreen-expand", | ||||
|           "prevSize": 24, | ||||
|           "code": 59913, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 122, | ||||
| @@ -461,7 +469,7 @@ | ||||
|           "name": "icon-layers", | ||||
|           "prevSize": 24, | ||||
|           "code": 59914, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 151, | ||||
| @@ -469,7 +477,7 @@ | ||||
|           "name": "icon-line-horz", | ||||
|           "prevSize": 24, | ||||
|           "code": 59915, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 100, | ||||
| @@ -477,7 +485,7 @@ | ||||
|           "name": "icon-magnify", | ||||
|           "prevSize": 24, | ||||
|           "code": 59916, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 99, | ||||
| @@ -485,7 +493,7 @@ | ||||
|           "name": "icon-magnify-in", | ||||
|           "prevSize": 24, | ||||
|           "code": 59917, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 101, | ||||
| @@ -493,7 +501,7 @@ | ||||
|           "name": "icon-magnify-out-v2", | ||||
|           "prevSize": 24, | ||||
|           "code": 59918, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 103, | ||||
| @@ -501,7 +509,7 @@ | ||||
|           "name": "icon-menu", | ||||
|           "prevSize": 24, | ||||
|           "code": 59919, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 124, | ||||
| @@ -509,7 +517,7 @@ | ||||
|           "name": "icon-move", | ||||
|           "prevSize": 24, | ||||
|           "code": 59920, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 7, | ||||
| @@ -517,7 +525,7 @@ | ||||
|           "name": "icon-new-window", | ||||
|           "prevSize": 24, | ||||
|           "code": 59921, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 63, | ||||
| @@ -525,7 +533,7 @@ | ||||
|           "name": "icon-paint-bucket-v2", | ||||
|           "prevSize": 24, | ||||
|           "code": 59922, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 15, | ||||
| @@ -533,7 +541,7 @@ | ||||
|           "name": "icon-pencil", | ||||
|           "prevSize": 24, | ||||
|           "code": 59923, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 54, | ||||
| @@ -541,7 +549,7 @@ | ||||
|           "name": "icon-pencil-edit-in-place", | ||||
|           "prevSize": 24, | ||||
|           "code": 59924, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 40, | ||||
| @@ -549,7 +557,7 @@ | ||||
|           "name": "icon-play", | ||||
|           "prevSize": 24, | ||||
|           "code": 59925, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 125, | ||||
| @@ -557,7 +565,7 @@ | ||||
|           "name": "icon-pause", | ||||
|           "prevSize": 24, | ||||
|           "code": 59926, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 119, | ||||
| @@ -565,7 +573,7 @@ | ||||
|           "name": "icon-plot-resource", | ||||
|           "prevSize": 24, | ||||
|           "code": 59927, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 48, | ||||
| @@ -573,7 +581,7 @@ | ||||
|           "name": "icon-pointer-left", | ||||
|           "prevSize": 24, | ||||
|           "code": 59928, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 47, | ||||
| @@ -581,7 +589,7 @@ | ||||
|           "name": "icon-pointer-right", | ||||
|           "prevSize": 24, | ||||
|           "code": 59929, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 85, | ||||
| @@ -589,7 +597,7 @@ | ||||
|           "name": "icon-refresh", | ||||
|           "prevSize": 24, | ||||
|           "code": 59930, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 55, | ||||
| @@ -597,7 +605,7 @@ | ||||
|           "name": "icon-save", | ||||
|           "prevSize": 24, | ||||
|           "code": 59931, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 56, | ||||
| @@ -605,7 +613,7 @@ | ||||
|           "name": "icon-save-as", | ||||
|           "prevSize": 24, | ||||
|           "code": 59932, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 58, | ||||
| @@ -613,7 +621,7 @@ | ||||
|           "name": "icon-sine", | ||||
|           "prevSize": 24, | ||||
|           "code": 59933, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 113, | ||||
| @@ -621,7 +629,7 @@ | ||||
|           "name": "icon-font", | ||||
|           "prevSize": 24, | ||||
|           "code": 59934, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 41, | ||||
| @@ -629,7 +637,7 @@ | ||||
|           "name": "icon-thumbs-strip", | ||||
|           "prevSize": 24, | ||||
|           "code": 59935, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 146, | ||||
| @@ -637,7 +645,7 @@ | ||||
|           "name": "icon-two-parts-both", | ||||
|           "prevSize": 24, | ||||
|           "code": 59936, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 145, | ||||
| @@ -645,7 +653,7 @@ | ||||
|           "name": "icon-two-parts-one-only", | ||||
|           "prevSize": 24, | ||||
|           "code": 59937, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 82, | ||||
| @@ -653,7 +661,7 @@ | ||||
|           "name": "icon-resync", | ||||
|           "prevSize": 24, | ||||
|           "code": 59938, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 86, | ||||
| @@ -661,7 +669,7 @@ | ||||
|           "name": "icon-reset", | ||||
|           "prevSize": 24, | ||||
|           "code": 59939, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 61, | ||||
| @@ -669,7 +677,7 @@ | ||||
|           "name": "icon-x-in-circle", | ||||
|           "prevSize": 24, | ||||
|           "code": 59940, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 84, | ||||
| @@ -677,7 +685,7 @@ | ||||
|           "name": "icon-brightness", | ||||
|           "prevSize": 24, | ||||
|           "code": 59941, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 83, | ||||
| @@ -685,7 +693,7 @@ | ||||
|           "name": "icon-contrast", | ||||
|           "prevSize": 24, | ||||
|           "code": 59942, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 87, | ||||
| @@ -693,7 +701,7 @@ | ||||
|           "name": "icon-expand", | ||||
|           "prevSize": 24, | ||||
|           "code": 59943, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 89, | ||||
| @@ -701,7 +709,7 @@ | ||||
|           "name": "icon-list-view", | ||||
|           "prevSize": 24, | ||||
|           "code": 59944, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 133, | ||||
| @@ -709,7 +717,7 @@ | ||||
|           "name": "icon-grid-snap-to", | ||||
|           "prevSize": 24, | ||||
|           "code": 59945, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 132, | ||||
| @@ -717,7 +725,7 @@ | ||||
|           "name": "icon-grid-snap-no", | ||||
|           "prevSize": 24, | ||||
|           "code": 59946, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 94, | ||||
| @@ -725,7 +733,7 @@ | ||||
|           "name": "icon-frame-show", | ||||
|           "prevSize": 24, | ||||
|           "code": 59947, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 95, | ||||
| @@ -733,7 +741,7 @@ | ||||
|           "name": "icon-frame-hide", | ||||
|           "prevSize": 24, | ||||
|           "code": 59948, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 97, | ||||
| @@ -741,7 +749,7 @@ | ||||
|           "name": "icon-import", | ||||
|           "prevSize": 24, | ||||
|           "code": 59949, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 96, | ||||
| @@ -749,7 +757,7 @@ | ||||
|           "name": "icon-export", | ||||
|           "prevSize": 24, | ||||
|           "code": 59950, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 194, | ||||
| @@ -757,7 +765,7 @@ | ||||
|           "name": "icon-font-size", | ||||
|           "prevSize": 24, | ||||
|           "code": 59951, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 163, | ||||
| @@ -765,7 +773,7 @@ | ||||
|           "name": "icon-clear-data", | ||||
|           "prevSize": 24, | ||||
|           "code": 59952, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 173, | ||||
| @@ -773,7 +781,7 @@ | ||||
|           "name": "icon-history", | ||||
|           "prevSize": 24, | ||||
|           "code": 59953, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 181, | ||||
| @@ -781,7 +789,7 @@ | ||||
|           "name": "icon-arrow-up-to-parent", | ||||
|           "prevSize": 24, | ||||
|           "code": 59954, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 184, | ||||
| @@ -789,7 +797,7 @@ | ||||
|           "name": "icon-crosshair-in-circle", | ||||
|           "prevSize": 24, | ||||
|           "code": 59955, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 185, | ||||
| @@ -797,7 +805,7 @@ | ||||
|           "name": "icon-target", | ||||
|           "prevSize": 24, | ||||
|           "code": 59956, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 187, | ||||
| @@ -805,7 +813,7 @@ | ||||
|           "name": "icon-items-collapse", | ||||
|           "prevSize": 24, | ||||
|           "code": 59957, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 188, | ||||
| @@ -813,7 +821,7 @@ | ||||
|           "name": "icon-items-expand", | ||||
|           "prevSize": 24, | ||||
|           "code": 59958, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 190, | ||||
| @@ -821,7 +829,7 @@ | ||||
|           "name": "icon-3-dots", | ||||
|           "prevSize": 24, | ||||
|           "code": 59959, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 193, | ||||
| @@ -829,7 +837,7 @@ | ||||
|           "name": "icon-grid-on", | ||||
|           "prevSize": 24, | ||||
|           "code": 59960, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 192, | ||||
| @@ -837,7 +845,7 @@ | ||||
|           "name": "icon-grid-off", | ||||
|           "prevSize": 24, | ||||
|           "code": 59961, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 191, | ||||
| @@ -845,7 +853,7 @@ | ||||
|           "name": "icon-camera", | ||||
|           "prevSize": 24, | ||||
|           "code": 59962, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 196, | ||||
| @@ -853,7 +861,7 @@ | ||||
|           "name": "icon-folders-collapse", | ||||
|           "prevSize": 24, | ||||
|           "code": 59963, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 144, | ||||
| @@ -861,7 +869,7 @@ | ||||
|           "name": "icon-activity", | ||||
|           "prevSize": 24, | ||||
|           "code": 60160, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 104, | ||||
| @@ -869,7 +877,7 @@ | ||||
|           "name": "icon-activity-mode", | ||||
|           "prevSize": 24, | ||||
|           "code": 60161, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 137, | ||||
| @@ -877,7 +885,7 @@ | ||||
|           "name": "icon-autoflow-tabular", | ||||
|           "prevSize": 24, | ||||
|           "code": 60162, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 115, | ||||
| @@ -885,7 +893,7 @@ | ||||
|           "name": "icon-clock", | ||||
|           "prevSize": 24, | ||||
|           "code": 60163, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 2, | ||||
| @@ -893,7 +901,7 @@ | ||||
|           "name": "icon-database", | ||||
|           "prevSize": 24, | ||||
|           "code": 60164, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 3, | ||||
| @@ -901,7 +909,7 @@ | ||||
|           "name": "icon-database-query", | ||||
|           "prevSize": 24, | ||||
|           "code": 60165, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 67, | ||||
| @@ -909,7 +917,7 @@ | ||||
|           "name": "icon-dataset", | ||||
|           "prevSize": 24, | ||||
|           "code": 60166, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 59, | ||||
| @@ -917,7 +925,7 @@ | ||||
|           "name": "icon-datatable", | ||||
|           "prevSize": 24, | ||||
|           "code": 60167, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 136, | ||||
| @@ -925,7 +933,7 @@ | ||||
|           "name": "icon-dictionary", | ||||
|           "prevSize": 24, | ||||
|           "code": 60168, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 51, | ||||
| @@ -933,7 +941,7 @@ | ||||
|           "name": "icon-folder", | ||||
|           "prevSize": 24, | ||||
|           "code": 60169, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 147, | ||||
| @@ -941,7 +949,7 @@ | ||||
|           "name": "icon-image", | ||||
|           "prevSize": 24, | ||||
|           "code": 60170, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 4, | ||||
| @@ -949,7 +957,7 @@ | ||||
|           "name": "icon-layout", | ||||
|           "prevSize": 24, | ||||
|           "code": 60171, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 24, | ||||
| @@ -957,7 +965,7 @@ | ||||
|           "name": "icon-object", | ||||
|           "prevSize": 24, | ||||
|           "code": 60172, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 52, | ||||
| @@ -965,7 +973,7 @@ | ||||
|           "name": "icon-object-unknown", | ||||
|           "prevSize": 24, | ||||
|           "code": 60173, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 105, | ||||
| @@ -973,7 +981,7 @@ | ||||
|           "name": "icon-packet", | ||||
|           "prevSize": 24, | ||||
|           "code": 60174, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 126, | ||||
| @@ -981,7 +989,7 @@ | ||||
|           "name": "icon-page", | ||||
|           "prevSize": 24, | ||||
|           "code": 60175, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 130, | ||||
| @@ -989,7 +997,7 @@ | ||||
|           "name": "icon-plot-overlay", | ||||
|           "prevSize": 24, | ||||
|           "code": 60176, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 80, | ||||
| @@ -997,7 +1005,7 @@ | ||||
|           "name": "icon-plot-stacked", | ||||
|           "prevSize": 24, | ||||
|           "code": 60177, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 134, | ||||
| @@ -1005,7 +1013,7 @@ | ||||
|           "name": "icon-session", | ||||
|           "prevSize": 24, | ||||
|           "code": 60178, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 109, | ||||
| @@ -1013,7 +1021,7 @@ | ||||
|           "name": "icon-tabular", | ||||
|           "prevSize": 24, | ||||
|           "code": 60179, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 107, | ||||
| @@ -1021,7 +1029,7 @@ | ||||
|           "name": "icon-tabular-lad", | ||||
|           "prevSize": 24, | ||||
|           "code": 60180, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 106, | ||||
| @@ -1029,7 +1037,7 @@ | ||||
|           "name": "icon-tabular-lad-set", | ||||
|           "prevSize": 24, | ||||
|           "code": 60181, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 70, | ||||
| @@ -1037,7 +1045,7 @@ | ||||
|           "name": "icon-tabular-realtime", | ||||
|           "prevSize": 24, | ||||
|           "code": 60182, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 60, | ||||
| @@ -1045,7 +1053,7 @@ | ||||
|           "name": "icon-tabular-scrolling", | ||||
|           "prevSize": 24, | ||||
|           "code": 60183, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 131, | ||||
| @@ -1053,7 +1061,7 @@ | ||||
|           "name": "icon-telemetry", | ||||
|           "prevSize": 24, | ||||
|           "code": 60184, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 108, | ||||
| @@ -1061,7 +1069,7 @@ | ||||
|           "name": "icon-timeline", | ||||
|           "prevSize": 24, | ||||
|           "code": 60185, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 81, | ||||
| @@ -1069,7 +1077,7 @@ | ||||
|           "name": "icon-timer", | ||||
|           "prevSize": 24, | ||||
|           "code": 60186, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 69, | ||||
| @@ -1077,7 +1085,7 @@ | ||||
|           "name": "icon-topic", | ||||
|           "prevSize": 24, | ||||
|           "code": 60187, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 79, | ||||
| @@ -1085,7 +1093,7 @@ | ||||
|           "name": "icon-box-with-dashed-lines-v2", | ||||
|           "prevSize": 24, | ||||
|           "code": 60188, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 90, | ||||
| @@ -1093,7 +1101,7 @@ | ||||
|           "name": "icon-summary-widget", | ||||
|           "prevSize": 24, | ||||
|           "code": 60189, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 92, | ||||
| @@ -1101,7 +1109,7 @@ | ||||
|           "name": "icon-notebook", | ||||
|           "prevSize": 24, | ||||
|           "code": 60190, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 168, | ||||
| @@ -1109,7 +1117,7 @@ | ||||
|           "name": "icon-tabs-view", | ||||
|           "prevSize": 24, | ||||
|           "code": 60191, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 117, | ||||
| @@ -1117,7 +1125,7 @@ | ||||
|           "name": "icon-flexible-layout", | ||||
|           "prevSize": 24, | ||||
|           "code": 60192, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 166, | ||||
| @@ -1125,7 +1133,7 @@ | ||||
|           "name": "icon-generator-sine", | ||||
|           "prevSize": 24, | ||||
|           "code": 60193, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 167, | ||||
| @@ -1133,7 +1141,7 @@ | ||||
|           "name": "icon-generator-event", | ||||
|           "prevSize": 24, | ||||
|           "code": 60194, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 165, | ||||
| @@ -1141,7 +1149,7 @@ | ||||
|           "name": "icon-gauge-v2", | ||||
|           "prevSize": 24, | ||||
|           "code": 60195, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 170, | ||||
| @@ -1149,7 +1157,7 @@ | ||||
|           "name": "icon-spectra", | ||||
|           "prevSize": 24, | ||||
|           "code": 60196, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 171, | ||||
| @@ -1157,7 +1165,7 @@ | ||||
|           "name": "icon-telemetry-spectra", | ||||
|           "prevSize": 24, | ||||
|           "code": 60197, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 172, | ||||
| @@ -1165,7 +1173,7 @@ | ||||
|           "name": "icon-pushbutton", | ||||
|           "prevSize": 24, | ||||
|           "code": 60198, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 174, | ||||
| @@ -1173,7 +1181,7 @@ | ||||
|           "name": "icon-conditional", | ||||
|           "prevSize": 24, | ||||
|           "code": 60199, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 178, | ||||
| @@ -1181,7 +1189,7 @@ | ||||
|           "name": "icon-condition-widget", | ||||
|           "prevSize": 24, | ||||
|           "code": 60200, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 180, | ||||
| @@ -1189,7 +1197,7 @@ | ||||
|           "name": "icon-alphanumeric", | ||||
|           "prevSize": 24, | ||||
|           "code": 60201, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 183, | ||||
| @@ -1197,7 +1205,15 @@ | ||||
|           "name": "icon-image-telemetry", | ||||
|           "prevSize": 24, | ||||
|           "code": 60202, | ||||
|           "tempChar": "" | ||||
|           "tempChar": "" | ||||
|         }, | ||||
|         { | ||||
|           "order": 198, | ||||
|           "id": 170, | ||||
|           "name": "icon-telemetry-aggregate", | ||||
|           "prevSize": 24, | ||||
|           "code": 60203, | ||||
|           "tempChar": "" | ||||
|         } | ||||
|       ], | ||||
|       "id": 0, | ||||
| @@ -2000,6 +2016,26 @@ | ||||
|             ] | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "id": 169, | ||||
|           "paths": [ | ||||
|             "M1024 512c0 282.77-229.23 512-512 512s-512-229.23-512-512c0-282.77 229.23-512 512-512s512 229.23 512 512z" | ||||
|           ], | ||||
|           "attrs": [ | ||||
|             {} | ||||
|           ], | ||||
|           "isMulticolor": false, | ||||
|           "isMulticolor2": false, | ||||
|           "grid": 16, | ||||
|           "tags": [ | ||||
|             "icon-circle" | ||||
|           ], | ||||
|           "colorPermutations": { | ||||
|             "12552552551": [ | ||||
|               {} | ||||
|             ] | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "id": 105, | ||||
|           "paths": [ | ||||
| @@ -3784,6 +3820,32 @@ | ||||
|               {} | ||||
|             ] | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "id": 170, | ||||
|           "paths": [ | ||||
|             "M78 395.44c14-41.44 37.48-100.8 69.2-148.36 38.62-57.78 82.38-87.080 130.14-87.080s91.5 29.3 130 87.080c31.72 47.56 55.14 106.92 69.2 148.36 30.88 90.96 63.12 134.98 78 146.54 14.94-11.56 47.2-55.58 78-146.54 14-41.44 37.48-100.8 69.22-148.36q27.8-41.7 59.12-63.5c-75.7-111.377-201.81-183.58-344.783-183.58-0.034 0-0.068 0-0.103 0l0.006-0c-229.76 0-416 186.24-416 416-0 0.071-0 0.156-0 0.24 0 39.119 5.396 76.977 15.484 112.871l-0.704-2.931c16.78-21.74 40.4-63.34 63.22-130.74z", | ||||
|             "M754 436.56c-14 41.44-37.48 100.8-69.2 148.36-38.56 57.78-82.32 87.080-130 87.080s-91.5-29.3-130-87.080c-31.72-47.56-55.14-106.92-69.2-148.36-30.88-90.96-63.14-134.98-78-146.54-14.94 11.56-47.2 55.58-78 146.54-14.38 41.44-37.8 100.8-69.6 148.36q-27.8 41.7-59.12 63.5c75.7 111.378 201.81 183.58 344.783 183.58 0.119 0 0.237-0 0.356-0l-0.019 0c229.76 0 416-186.24 416-416 0-0.071 0-0.156 0-0.24 0-39.119-5.396-76.977-15.484-112.871l0.704 2.931c-16.78 21.74-40.4 63.34-63.22 130.74z", | ||||
|             "M921.56 334.62c4.098 24.449 6.44 52.617 6.44 81.332 0 0.017-0 0.034-0 0.051l0-0.003c0 0.095 0 0.208 0 0.32 0 282.593-229.087 511.68-511.68 511.68-0.113 0-0.225-0-0.338-0l0.018 0c-0.014 0-0.031 0-0.048 0-28.716 0-56.884-2.342-84.325-6.845l2.993 0.405c72.483 63.623 168.109 102.44 272.802 102.44 0.203 0 0.406-0 0.61-0l-0.031 0c229.76 0 416-186.24 416-416 0-0.172 0-0.375 0-0.578 0-104.692-38.817-200.319-102.844-273.271l0.404 0.47z" | ||||
|           ], | ||||
|           "attrs": [ | ||||
|             {}, | ||||
|             {}, | ||||
|             {} | ||||
|           ], | ||||
|           "isMulticolor": false, | ||||
|           "isMulticolor2": false, | ||||
|           "grid": 16, | ||||
|           "tags": [ | ||||
|             "icon-telemetry-aggregate" | ||||
|           ], | ||||
|           "colorPermutations": { | ||||
|             "12552552551": [ | ||||
|               {}, | ||||
|               {}, | ||||
|               {} | ||||
|             ] | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       "invisible": false, | ||||
|   | ||||
| @@ -53,6 +53,7 @@ | ||||
| <glyph unicode="" glyph-name="icon-eye-disabled" d="M209.46 223.32q-7.46 9.86-14.26 20.28c-14.737 21.984-27.741 47.184-37.759 73.847l-0.841 2.553c11.078 29.259 24.068 54.443 39.51 77.869l-0.91-1.469c23.221 34.963 50.705 64.8 82.207 89.793l0.793 0.607c57.663 45.719 130.179 75.053 209.311 79.947l1.069 0.053 114.48 140.88c-27.366 5.017-58.869 7.898-91.041 7.92h-0.019c-245.8 0-452.2-168-510.8-395.6 21.856-82.93 60.906-154.847 113.325-214.773l-0.525 0.613zM814.76 416.92q7.52-10 14.44-20.52c14.737-21.984 27.741-47.184 37.759-73.847l0.841-2.553c-10.859-29.216-23.863-54.416-39.447-77.748l0.847 1.348c-23.221-34.963-50.705-64.8-82.207-89.793l-0.793-0.607c-57.762-45.834-130.437-75.216-209.743-80.049l-1.057-0.051-114.46-140.86c27.346-4.988 58.817-7.84 90.955-7.84 0.037 0 0.074 0 0.111 0h-0.005c245.8 0 452.2 168 510.8 395.6-21.856 82.93-60.906 154.847-113.325 214.773l0.525-0.613zM832 832l-832-1024h192l832 1024h-192z" /> | ||||
| <glyph unicode="" glyph-name="icon-notebook-page" d="M830 770h-830l-4-702c0-106.6 87.4-194 194-194h640c106.6 0 194 87.4 194 194v508c0 106.8-87.4 194-194 194zM832 386l-384-384-192 192v256l192-192 384 384v-256z" /> | ||||
| <glyph unicode="" glyph-name="icon-unlocked" d="M768 832c-141.339-0.114-255.886-114.661-256-255.989v-128.011h-448c-35.301-0.113-63.887-28.699-64-63.989v-512.011c0.113-35.301 28.699-63.887 63.989-64h638.011c35.301 0.113 63.887 28.699 64 63.989v512.011c-0.113 35.301-28.699 63.887-63.989 64h-62.011v128c0 70.692 57.308 128 128 128s128-57.308 128-128v0-128h128v128c-0.114 141.339-114.661 255.886-255.989 256h-0.011z" /> | ||||
| <glyph unicode="" glyph-name="icon-circle" d="M1024 320c0-282.77-229.23-512-512-512s-512 229.23-512 512c0 282.77 229.23 512 512 512s512-229.23 512-512z" /> | ||||
| <glyph unicode="" glyph-name="icon-arrows-right-left" d="M1024 320l-448-512v1024zM448 832l-448-512 448-512z" /> | ||||
| <glyph unicode="" glyph-name="icon-arrows-up-down" d="M512 832l512-448h-1024zM0 256l512-448 512 448z" /> | ||||
| <glyph unicode="" glyph-name="icon-bullet" d="M832 80c0-44-36-80-80-80h-480c-44 0-80 36-80 80v480c0 44 36 80 80 80h480c44 0 80-36 80-80v-480z" /> | ||||
| @@ -156,4 +157,5 @@ | ||||
| <glyph unicode="" glyph-name="icon-condition-widget" d="M832 832h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM512 64l-384 256 384 256 384-256z" /> | ||||
| <glyph unicode="" glyph-name="icon-alphanumeric" d="M535.6 301.4c-8.4-1.6-17.2-3-26.2-4s-18.2-2.4-27.2-4c-10.196-1.861-18.808-4.010-27.21-6.633l1.61 0.433c-8.609-2.674-16.105-6.348-22.89-10.987l0.29 0.187c-6.693-4.517-12.283-10.107-16.663-16.585l-0.137-0.215c-4.6-6.8-7.4-15.6-8.8-26s-0.4-18.4 2.4-25.2c2.746-6.688 7.224-12.195 12.881-16.122l0.119-0.078c5.967-4.053 13.057-6.94 20.704-8.161l0.296-0.039c7.592-1.527 16.319-2.4 25.25-2.4 0.123 0 0.246 0 0.369 0h-0.019c22.2 0 39.6 3.6 52.6 11s23.2 16.2 30.2 26.4c6.273 8.873 11.271 19.191 14.426 30.285l0.174 0.715c1.853 6.809 3.601 15.41 4.855 24.169l0.145 1.231 5.2 41.6c-5.4-4.217-11.723-7.564-18.583-9.689l-0.417-0.111c-6.489-2.241-14.362-4.255-22.444-5.662l-0.956-0.138zM1024 448v192h-152l24 192h-192l-24-192h-256l24 192h-192l-24-192h-232v-192h208l-32-256h-176v-192h152l-24-192h192l24 192h256l-24-192h192l24 192h232v192h-208l32 256zM702.8 420.2l-26.4-211.8c-2.231-15.809-3.537-34.122-3.6-52.727v-0.073c0-16.8 2.2-29.4 6.4-37.8h-113.4c-1.342 5.556-2.338 12.122-2.781 18.84l-0.019 0.36c-0.261 3.524-0.409 7.634-0.409 11.778 0 2.962 0.076 5.907 0.226 8.832l-0.017-0.41c-18.663-17.401-41.395-30.694-66.597-38.289l-1.203-0.311c-22.627-6.956-48.639-10.974-75.586-11h-0.014c-0.764-0.011-1.666-0.018-2.569-0.018-18.098 0-35.598 2.563-52.156 7.345l1.325-0.328c-15.991 4.512-29.851 12.090-41.545 22.122l0.145-0.122c-11.233 9.982-19.792 22.733-24.624 37.192l-0.176 0.608c-5.2 15.2-6.4 33.4-3.8 54.4s9.4 42.2 19.4 57.2c9.524 14.399 21.535 26.346 35.532 35.512l0.468 0.288c13.387 8.662 28.922 15.533 45.512 19.765l1.088 0.235c13.436 3.792 30.801 7.554 48.47 10.41l2.93 0.39c17 2.6 33.8 4.6 50.4 6.2 16.628 1.527 31.69 4.070 46.349 7.643l-2.149-0.443c13 3 23.6 7.6 31.6 13.6s12.6 15 13.6 26.4 0.8 21.8-2.4 28.8c-2.849 6.902-7.542 12.56-13.468 16.517l-0.132 0.083c-6.217 4.011-13.604 6.78-21.543 7.774l-0.257 0.026c-7.897 1.277-17 2.007-26.274 2.007-0.537 0-1.073-0.002-1.609-0.007l0.082 0.001c-22 0-40-4.6-53.8-14.2s-23-25.2-28-47.2h-111.8c4.8 26.2 14.2 48 27.8 65.4 13.475 16.978 29.89 30.968 48.574 41.377l0.826 0.423c18.192 10.038 39.297 17.806 61.619 22.175l1.381 0.225c20.488 4.162 44.053 6.563 68.171 6.6h0.029c21.8-0.005 43.239-1.532 64.222-4.479l-2.422 0.279c20.641-2.809 39.324-8.783 56.401-17.461l-1.001 0.461c15.909-8.108 28.858-20.031 37.967-34.601l0.233-0.399c9-15 12.2-34.8 9-59.6z" /> | ||||
| <glyph unicode="" glyph-name="icon-image-telemetry" d="M512 832c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM783.6 48.4c-69.581-69.675-165.757-112.776-272-112.776-212.298 0-384.4 172.102-384.4 384.4s172.102 384.4 384.4 384.4c212.298 0 384.4-172.102 384.4-384.4 0-0.008 0-0.017 0-0.025v0.001c0.001-0.264 0.001-0.575 0.001-0.887 0-105.769-42.964-201.503-112.391-270.703l-0.010-0.010zM704 448l-128-128-192 192-192-192c0-176.731 143.269-320 320-320s320 143.269 320 320v0z" /> | ||||
| <glyph unicode="" glyph-name="icon-telemetry-aggregate" d="M78 436.56c14 41.44 37.48 100.8 69.2 148.36 38.62 57.78 82.38 87.080 130.14 87.080s91.5-29.3 130-87.080c31.72-47.56 55.14-106.92 69.2-148.36 30.88-90.96 63.12-134.98 78-146.54 14.94 11.56 47.2 55.58 78 146.54 14 41.44 37.48 100.8 69.22 148.36q27.8 41.7 59.12 63.5c-75.7 111.377-201.81 183.58-344.783 183.58-0.034 0-0.068 0-0.103 0h0.006c-229.76 0-416-186.24-416-416 0-0.071 0-0.156 0-0.24 0-39.119 5.396-76.977 15.484-112.871l-0.704 2.931c16.78 21.74 40.4 63.34 63.22 130.74zM754 395.44c-14-41.44-37.48-100.8-69.2-148.36-38.56-57.78-82.32-87.080-130-87.080s-91.5 29.3-130 87.080c-31.72 47.56-55.14 106.92-69.2 148.36-30.88 90.96-63.14 134.98-78 146.54-14.94-11.56-47.2-55.58-78-146.54-14.38-41.44-37.8-100.8-69.6-148.36q-27.8-41.7-59.12-63.5c75.7-111.378 201.81-183.58 344.783-183.58 0.119 0 0.237 0 0.356 0h-0.019c229.76 0 416 186.24 416 416 0 0.071 0 0.156 0 0.24 0 39.119-5.396 76.977-15.484 112.871l0.704-2.931c-16.78-21.74-40.4-63.34-63.22-130.74zM921.56 497.38c4.098-24.449 6.44-52.617 6.44-81.332 0-0.017 0-0.034 0-0.051v0.003c0-0.095 0-0.208 0-0.32 0-282.593-229.087-511.68-511.68-511.68-0.113 0-0.225 0-0.338 0h0.018c-0.014 0-0.031 0-0.048 0-28.716 0-56.884 2.342-84.325 6.845l2.993-0.405c72.483-63.623 168.109-102.44 272.802-102.44 0.203 0 0.406 0 0.61 0h-0.031c229.76 0 416 186.24 416 416 0 0.172 0 0.375 0 0.578 0 104.692-38.817 200.319-102.844 273.271l0.404-0.47z" /> | ||||
| </font></defs></svg> | ||||
| Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 61 KiB | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -39,13 +39,7 @@ export function paramsToArray(openmct) { | ||||
| } | ||||
|  | ||||
| export function identifierToString(openmct, objectPath) { | ||||
|     let identifier = '#/browse/' + objectPath.map(function (o) { | ||||
|         return o && openmct.objects.makeKeyString(o.identifier); | ||||
|     }) | ||||
|         .reverse() | ||||
|         .join('/'); | ||||
|  | ||||
|     return identifier; | ||||
|     return '#/browse/' + openmct.objects.getRelativePath(objectPath); | ||||
| } | ||||
|  | ||||
| export default function objectPathToUrl(openmct, objectPath) { | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
|  *****************************************************************************/ | ||||
| <template> | ||||
| <div | ||||
|     class="c-so-view" | ||||
|     class="c-so-view js-notebook-snapshot-item-wrapper" | ||||
|     :class="[ | ||||
|         statusClass, | ||||
|         'c-so-view--' + domainObject.type, | ||||
| @@ -56,6 +56,11 @@ | ||||
|                 'has-complex-content': complexContent | ||||
|             }" | ||||
|         > | ||||
|             <NotebookMenuSwitcher v-if="notebookEnabled" | ||||
|                                   :domain-object="domainObject" | ||||
|                                   :object-path="objectPath" | ||||
|                                   class="c-notebook-snapshot-menubutton" | ||||
|             /> | ||||
|             <div v-if="statusBarItems.length > 0" | ||||
|                  class="c-so-view__frame-controls__btns" | ||||
|             > | ||||
| @@ -80,7 +85,7 @@ | ||||
|  | ||||
|     <object-view | ||||
|         ref="objectView" | ||||
|         class="c-so-view__object-view js-object-view" | ||||
|         class="c-so-view__object-view js-object-view js-notebook-snapshot-item" | ||||
|         :show-edit-view="showEditView" | ||||
|         :object-path="objectPath" | ||||
|         :layout-font-size="layoutFontSize" | ||||
| @@ -92,6 +97,7 @@ | ||||
|  | ||||
| <script> | ||||
| import ObjectView from './ObjectView.vue'; | ||||
| import NotebookMenuSwitcher from '@/plugins/notebook/components/NotebookMenuSwitcher.vue'; | ||||
|  | ||||
| const SIMPLE_CONTENT_TYPES = [ | ||||
|     'clock', | ||||
| @@ -103,7 +109,8 @@ const SIMPLE_CONTENT_TYPES = [ | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
|         ObjectView | ||||
|         ObjectView, | ||||
|         NotebookMenuSwitcher | ||||
|     }, | ||||
|     inject: ['openmct'], | ||||
|     props: { | ||||
| @@ -139,6 +146,7 @@ export default { | ||||
|         return { | ||||
|             cssClass, | ||||
|             complexContent, | ||||
|             notebookEnabled: this.openmct.types.get('notebook'), | ||||
|             statusBarItems: [], | ||||
|             status: '' | ||||
|         }; | ||||
|   | ||||
| @@ -53,7 +53,7 @@ | ||||
|             class="l-shell__pane-tree" | ||||
|             handle="after" | ||||
|             label="Browse" | ||||
|             collapsable | ||||
|             hide-param="hideTree" | ||||
|             @start-resizing="onStartResizing" | ||||
|             @end-resizing="onEndResizing" | ||||
|         > | ||||
| @@ -90,7 +90,7 @@ | ||||
|             /> | ||||
|             <object-view | ||||
|                 ref="browseObject" | ||||
|                 class="l-shell__main-container js-main-container" | ||||
|                 class="l-shell__main-container js-main-container js-notebook-snapshot-item" | ||||
|                 data-selectable | ||||
|                 :show-edit-view="true" | ||||
|                 @change-action-collection="setActionCollection" | ||||
| @@ -104,7 +104,7 @@ | ||||
|             class="l-shell__pane-inspector l-pane--holds-multipane" | ||||
|             handle="before" | ||||
|             label="Inspect" | ||||
|             collapsable | ||||
|             hide-param="hideInspector" | ||||
|             @start-resizing="onStartResizing" | ||||
|             @end-resizing="onEndResizing" | ||||
|         > | ||||
|   | ||||
							
								
								
									
										171
									
								
								src/ui/layout/LayoutSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								src/ui/layout/LayoutSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2021, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| import { | ||||
|     createOpenMct, | ||||
|     resetApplicationState | ||||
| } from 'utils/testing'; | ||||
| import Vue from 'vue'; | ||||
| import Layout from './Layout.vue'; | ||||
|  | ||||
| describe('Open MCT Layout:', () => { | ||||
|     let openmct; | ||||
|     let element; | ||||
|     let components; | ||||
|  | ||||
|     beforeEach((done) => { | ||||
|         openmct = createOpenMct(); | ||||
|         openmct.on('start', done); | ||||
|  | ||||
|         // to silence error from BrowseBar.vue | ||||
|         spyOn(openmct.objectViews, 'get') | ||||
|             .and.callFake(() => []); | ||||
|  | ||||
|         openmct.startHeadless(); | ||||
|     }); | ||||
|  | ||||
|     afterEach(() => { | ||||
|         return resetApplicationState(openmct); | ||||
|     }); | ||||
|  | ||||
|     describe('the pane:', () => { | ||||
|         it('is displayed on layout load', async () => { | ||||
|             await createLayout(); | ||||
|             await Vue.nextTick(); | ||||
|  | ||||
|             Object.entries(components).forEach(([name, component]) => { | ||||
|                 expect( | ||||
|                     component.pane | ||||
|                 ).toBeTruthy(); | ||||
|  | ||||
|                 expect( | ||||
|                     isCollapsed(component.pane) | ||||
|                 ).toBeFalse(); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('is collapsed on layout load if specified by a hide param', async () => { | ||||
|             setHideParams(); | ||||
|  | ||||
|             await createLayout(); | ||||
|             await Vue.nextTick(); | ||||
|  | ||||
|             Object.entries(components).forEach(([name, component]) => { | ||||
|                 expect( | ||||
|                     isCollapsed(component.pane) | ||||
|                 ).toBeTrue(); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('on toggle collapses if expanded', async () => { | ||||
|             await createLayout(); | ||||
|             toggleCollapseButtons(); | ||||
|             await Vue.nextTick(); | ||||
|  | ||||
|             Object.entries(components).forEach(([name, component]) => { | ||||
|                 expect( | ||||
|                     openmct.router.getSearchParam(component.param) | ||||
|                 ).toEqual('true'); | ||||
|  | ||||
|                 expect( | ||||
|                     isCollapsed(component.pane) | ||||
|                 ).toBeTrue(); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('on toggle expands if collapsed', async () => { | ||||
|             setHideParams(); | ||||
|  | ||||
|             await createLayout(); | ||||
|             toggleExpandButtons(); | ||||
|             await Vue.nextTick(); | ||||
|  | ||||
|             Object.entries(components).forEach(([name, component]) => { | ||||
|                 expect( | ||||
|                     openmct.router.getSearchParam(component.param) | ||||
|                 ).not.toEqual('true'); | ||||
|  | ||||
|                 expect( | ||||
|                     isCollapsed(component.pane) | ||||
|                 ).toBeFalse(); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     async function createLayout() { | ||||
|         const el = document.createElement('div'); | ||||
|         const child = document.createElement('div'); | ||||
|         el.appendChild(child); | ||||
|  | ||||
|         element = await new Vue({ | ||||
|             el, | ||||
|             components: { | ||||
|                 Layout | ||||
|             }, | ||||
|             provide: { | ||||
|                 openmct | ||||
|             }, | ||||
|             template: `<Layout ref="layout"/>` | ||||
|         }).$mount().$el; | ||||
|  | ||||
|         setComponents(); | ||||
|     } | ||||
|  | ||||
|     function setComponents() { | ||||
|         components = { | ||||
|             tree: { | ||||
|                 param: 'hideTree', | ||||
|                 pane: element.querySelector('.l-shell__pane-tree'), | ||||
|                 collapseButton: element.querySelector('.l-shell__pane-tree .l-pane__collapse-button'), | ||||
|                 expandButton: element.querySelector('.l-shell__pane-tree .l-pane__expand-button') | ||||
|             }, | ||||
|             inspector: { | ||||
|                 param: 'hideInspector', | ||||
|                 pane: element.querySelector('.l-shell__pane-inspector'), | ||||
|                 collapseButton: element.querySelector('.l-shell__pane-inspector .l-pane__collapse-button'), | ||||
|                 expandButton: element.querySelector('.l-shell__pane-inspector .l-pane__expand-button') | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     function isCollapsed(el) { | ||||
|         return el.classList.contains('l-pane--collapsed'); | ||||
|     } | ||||
|  | ||||
|     function setHideParams() { | ||||
|         Object.entries(components).forEach(([name, component]) => { | ||||
|             openmct.router.setSearchParam(component.param, true); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function toggleCollapseButtons() { | ||||
|         Object.entries(components).forEach(([name, component]) => { | ||||
|             component.collapseButton.click(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function toggleExpandButtons() { | ||||
|         Object.entries(components).forEach(([name, component]) => { | ||||
|             component.expandButton.click(); | ||||
|         }); | ||||
|     } | ||||
| }); | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user