Compare commits
	
		
			3 Commits
		
	
	
		
			missing-it
			...
			filters-ap
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | b0b160d71e | ||
|   | 9c4553dd90 | ||
|   | 86e6885fb9 | 
| @@ -33,7 +33,13 @@ define([ | ||||
|                     formatString: '%0.2f', | ||||
|                     hints: { | ||||
|                         range: 1 | ||||
|                     } | ||||
|                     }, | ||||
|                     filters: ['equals'] | ||||
|                     /* | ||||
|                     filters: [{ | ||||
|                         comparator: 'equals', | ||||
|                         possibleValues: [1,2,3,4] | ||||
|                     }]*/ | ||||
|                 }, | ||||
|                 { | ||||
|                     key: "cos", | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
|         <meta name="apple-mobile-web-app-capable" content="yes"> | ||||
|         <title></title> | ||||
|         <script src="dist/openmct.js"></script> | ||||
|         <link rel="stylesheet" href="dist/styles/openmct.css"> | ||||
|         <link rel="stylesheet" href="dist/openmct.css"> | ||||
|         <link rel="icon" type="image/png" href="dist/favicons/favicon-32x32.png" sizes="32x32"> | ||||
|         <link rel="icon" type="image/png" href="dist/favicons/favicon-96x96.png" sizes="96x96"> | ||||
|         <link rel="icon" type="image/png" href="dist/favicons/favicon-16x16.png" sizes="16x16"> | ||||
| @@ -54,9 +54,6 @@ | ||||
|         openmct.install(openmct.plugins.AutoflowView({ | ||||
|             type: "telemetry.panel" | ||||
|         })); | ||||
|         openmct.install(openmct.plugins.DisplayLayout({ | ||||
|             showAsView: ['summary-widget', 'example.imagery'] | ||||
|         })); | ||||
|         openmct.install(openmct.plugins.Conductor({ | ||||
|             menuOptions: [ | ||||
|                 { | ||||
| @@ -84,9 +81,6 @@ | ||||
|         openmct.install(openmct.plugins.Tabs()); | ||||
|         openmct.install(openmct.plugins.FlexibleLayout()); | ||||
|         openmct.install(openmct.plugins.LADTable()); | ||||
|         openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay'])); | ||||
|         openmct.install(openmct.plugins.ObjectMigration()); | ||||
|         openmct.install(openmct.plugins.GoToOriginalAction()); | ||||
|         openmct.start(); | ||||
|     </script> | ||||
| </html> | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
|     "eventemitter3": "^1.2.0", | ||||
|     "exports-loader": "^0.7.0", | ||||
|     "express": "^4.13.1", | ||||
|     "fast-sass-loader": "1.4.6", | ||||
|     "fast-sass-loader": "^1.4.5", | ||||
|     "file-loader": "^1.1.11", | ||||
|     "file-saver": "^1.3.8", | ||||
|     "git-rev-sync": "^1.4.0", | ||||
| @@ -55,7 +55,7 @@ | ||||
|     "node-bourbon": "^4.2.3", | ||||
|     "node-sass": "^4.9.2", | ||||
|     "painterro": "^0.2.65", | ||||
|     "printj": "^1.2.1", | ||||
|     "printj": "^1.1.0", | ||||
|     "raw-loader": "^0.5.1", | ||||
|     "request": "^2.69.0", | ||||
|     "split": "^1.0.0", | ||||
|   | ||||
| @@ -31,6 +31,7 @@ define([ | ||||
|     "./src/navigation/NavigateAction", | ||||
|     "./src/navigation/OrphanNavigationHandler", | ||||
|     "./src/windowing/NewTabAction", | ||||
|     "./src/windowing/WindowTitler", | ||||
|     "./res/templates/browse.html", | ||||
|     "./res/templates/browse-object.html", | ||||
|     "./res/templates/browse/object-header.html", | ||||
| @@ -51,6 +52,7 @@ define([ | ||||
|     NavigateAction, | ||||
|     OrphanNavigationHandler, | ||||
|     NewTabAction, | ||||
|     WindowTitler, | ||||
|     browseTemplate, | ||||
|     browseObjectTemplate, | ||||
|     objectHeaderTemplate, | ||||
| @@ -224,6 +226,14 @@ define([ | ||||
|                 } | ||||
|             ], | ||||
|             "runs": [ | ||||
|                 { | ||||
|                     "implementation": WindowTitler, | ||||
|                     "depends": [ | ||||
|                         "navigationService", | ||||
|                         "$rootScope", | ||||
|                         "$document" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "implementation": OrphanNavigationHandler, | ||||
|                     "depends": [ | ||||
|   | ||||
							
								
								
									
										51
									
								
								platform/commonUI/browse/src/windowing/WindowTitler.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								platform/commonUI/browse/src/windowing/WindowTitler.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     [], | ||||
|     function () { | ||||
|  | ||||
|         /** | ||||
|          * Updates the title of the current window to reflect the name | ||||
|          * of the currently navigated-to domain object. | ||||
|          * @memberof platform/commonUI/browse | ||||
|          * @constructor | ||||
|          */ | ||||
|         function WindowTitler(navigationService, $rootScope, $document) { | ||||
|             // Look up name of the navigated domain object... | ||||
|             function getNavigatedObjectName() { | ||||
|                 var navigatedObject = navigationService.getNavigation(); | ||||
|                 return navigatedObject && navigatedObject.getModel().name; | ||||
|             } | ||||
|  | ||||
|             // Set the window title... | ||||
|             function setTitle(name) { | ||||
|                 $document[0].title = name; | ||||
|             } | ||||
|  | ||||
|             // Watch the former, and invoke the latter | ||||
|             $rootScope.$watch(getNavigatedObjectName, setTitle); | ||||
|         } | ||||
|  | ||||
|         return WindowTitler; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										78
									
								
								platform/commonUI/browse/test/windowing/WindowTitlerSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								platform/commonUI/browse/test/windowing/WindowTitlerSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, 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. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| /** | ||||
|  * WindowTitlerSpec. Created by vwoeltje on 11/6/14. | ||||
|  */ | ||||
| define( | ||||
|     ["../../src/windowing/WindowTitler"], | ||||
|     function (WindowTitler) { | ||||
|  | ||||
|         describe("The window titler", function () { | ||||
|             var mockNavigationService, | ||||
|                 mockRootScope, | ||||
|                 mockDocument, | ||||
|                 mockDomainObject, | ||||
|                 titler; // eslint-disable-line | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockNavigationService = jasmine.createSpyObj( | ||||
|                     'navigationService', | ||||
|                     ['getNavigation'] | ||||
|                 ); | ||||
|                 mockRootScope = jasmine.createSpyObj( | ||||
|                     '$rootScope', | ||||
|                     ['$watch'] | ||||
|                 ); | ||||
|                 mockDomainObject = jasmine.createSpyObj( | ||||
|                     'domainObject', | ||||
|                     ['getModel'] | ||||
|                 ); | ||||
|                 mockDocument = [{}]; | ||||
|  | ||||
|                 mockDomainObject.getModel.and.returnValue({ name: 'Test name' }); | ||||
|                 mockNavigationService.getNavigation.and.returnValue(mockDomainObject); | ||||
|  | ||||
|                 titler = new WindowTitler( | ||||
|                     mockNavigationService, | ||||
|                     mockRootScope, | ||||
|                     mockDocument | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("listens for changes to the name of the navigated object", function () { | ||||
|                 expect(mockRootScope.$watch).toHaveBeenCalledWith( | ||||
|                     jasmine.any(Function), | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|                 expect(mockRootScope.$watch.calls.mostRecent().args[0]()) | ||||
|                     .toEqual('Test name'); | ||||
|             }); | ||||
|  | ||||
|             it("sets the title to the name of the navigated object", function () { | ||||
|                 mockRootScope.$watch.calls.mostRecent().args[1]("Some name"); | ||||
|                 expect(mockDocument[0].title).toEqual("Some name"); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -28,7 +28,6 @@ define([ | ||||
|     "./res/templates/dialog.html", | ||||
|     "./res/templates/overlay-blocking-message.html", | ||||
|     "./res/templates/message.html", | ||||
|     "./res/templates/notification-message.html", | ||||
|     "./res/templates/overlay-message-list.html", | ||||
|     "./res/templates/overlay.html", | ||||
|     'legacyRegistry' | ||||
| @@ -40,7 +39,6 @@ define([ | ||||
|     dialogTemplate, | ||||
|     overlayBlockingMessageTemplate, | ||||
|     messageTemplate, | ||||
|     notificationMessageTemplate, | ||||
|     overlayMessageListTemplate, | ||||
|     overlayTemplate, | ||||
|     legacyRegistry | ||||
| @@ -65,8 +63,7 @@ define([ | ||||
|                     "depends": [ | ||||
|                         "$document", | ||||
|                         "$compile", | ||||
|                         "$rootScope", | ||||
|                         "$timeout" | ||||
|                         "$rootScope" | ||||
|                     ] | ||||
|                 } | ||||
|             ], | ||||
| @@ -91,10 +88,6 @@ define([ | ||||
|                     "key": "message", | ||||
|                     "template": messageTemplate | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "notification-message", | ||||
|                     "template": notificationMessageTemplate | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "overlay-message-list", | ||||
|                     "template": overlayMessageListTemplate | ||||
|   | ||||
| @@ -19,24 +19,24 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div class="c-overlay__top-bar"> | ||||
|     <div class="c-overlay__dialog-title">{{ngModel.title}}</div> | ||||
|     <div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div> | ||||
| <div class="abs top-bar"> | ||||
|     <div class="dialog-title">{{ngModel.title}}</div> | ||||
|     <div class="hint">All fields marked <span class="req icon-asterisk"></span> are required.</div> | ||||
| </div> | ||||
| <div class='c-overlay__contents-main'> | ||||
| <div class='abs editor'> | ||||
|     <mct-form ng-model="ngModel.value" | ||||
|               structure="ngModel.structure" | ||||
|               class="validates" | ||||
|               name="createForm"> | ||||
|     </mct-form> | ||||
| </div> | ||||
| <div class="c-overlay__button-bar"> | ||||
|     <a class='c-button c-button--major' | ||||
| <div class="abs bottom-bar"> | ||||
|     <a class='s-button major' | ||||
|        ng-class="{ disabled: !createForm.$valid }" | ||||
|        ng-click="ngModel.confirm()"> | ||||
|         OK | ||||
|     </a> | ||||
|     <a class='c-button  ' | ||||
|     <a class='s-button' | ||||
|        ng-click="ngModel.cancel()"> | ||||
|         Cancel | ||||
|     </a> | ||||
|   | ||||
| @@ -1,32 +1,25 @@ | ||||
| <div class="c-message" | ||||
| <div class="l-message" | ||||
|      ng-class="'message-severity-' + ngModel.severity"> | ||||
|     <div class="w-message-contents"> | ||||
|         <div class="c-message__top-bar"> | ||||
|             <div class="c-message__title">{{ngModel.title}}</div> | ||||
|         </div> | ||||
|         <div class="c-message__hint" ng-hide="ngModel.hint === undefined"> | ||||
|             {{ngModel.hint}} | ||||
|             <span ng-if="ngModel.timestamp !== undefined">[{{ngModel.timestamp}}]</span> | ||||
|         <div class="top-bar"> | ||||
|             <div class="title">{{ngModel.message}}</div> | ||||
|         </div> | ||||
|         <div class="message-body"> | ||||
|             <div class="message-action"> | ||||
|                 {{ngModel.actionText}} | ||||
|             </div> | ||||
|             <mct-include key="'progress-bar'" | ||||
|                          ng-model="ngModel" | ||||
|                          ng-show="ngModel.progress !== undefined || ngModel.unknownProgress"></mct-include> | ||||
|                          ng-show="ngModel.progressPerc !== undefined"></mct-include> | ||||
|         </div> | ||||
|         <div class="c-overlay__button-bar"> | ||||
|             <button ng-repeat="dialogOption in ngModel.options" | ||||
|                class="c-button" | ||||
|                ng-click="dialogOption.callback()"> | ||||
|                 {{dialogOption.label}} | ||||
|             </button> | ||||
|             <button class="c-button c-button--major" | ||||
|                ng-if="ngModel.primaryOption" | ||||
|                ng-click="ngModel.primaryOption.callback()"> | ||||
|                 {{ngModel.primaryOption.label}} | ||||
|             </button> | ||||
|         <div class="bottom-bar"> | ||||
|             <a ng-repeat="dialogOption in ngModel.options" | ||||
|                 class="s-button" | ||||
|                 ng-click="dialogOption.callback()"> | ||||
|                     {{dialogOption.label}} | ||||
|             </a> | ||||
|             <a class="s-button major" | ||||
|                 ng-if="ngModel.primaryOption" | ||||
|                 ng-click="ngModel.primaryOption.callback()"> | ||||
|                     {{ngModel.primaryOption.label}} | ||||
|             </a> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| </div> | ||||
|   | ||||
| @@ -1,25 +0,0 @@ | ||||
| <div class="c-message" | ||||
|      ng-class="'message-severity-' + ngModel.severity"> | ||||
|     <div class="w-message-contents"> | ||||
|         <div class="c-message__top-bar"> | ||||
|             <div class="c-message__title">{{ngModel.message}}</div> | ||||
|         </div> | ||||
|         <div class="message-body"> | ||||
|             <mct-include key="'progress-bar'" | ||||
|                          ng-model="ngModel" | ||||
|                          ng-show="ngModel.progressPerc !== undefined"></mct-include> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="c-overlay__button-bar"> | ||||
|         <button ng-repeat="dialogOption in ngModel.options" | ||||
|                 class="c-button" | ||||
|                 ng-click="dialogOption.callback()"> | ||||
|             {{dialogOption.label}} | ||||
|         </button> | ||||
|         <button class="c-button c-button--major" | ||||
|                 ng-if="ngModel.primaryOption" | ||||
|                 ng-click="ngModel.primaryOption.callback()"> | ||||
|             {{ngModel.primaryOption.label}} | ||||
|         </button> | ||||
|     </div> | ||||
| </div> | ||||
| @@ -1,23 +1,22 @@ | ||||
| <mct-container key="overlay"> | ||||
|     <div class="t-message-list c-overlay__contents"> | ||||
|         <div class="c-overlay__top-bar"> | ||||
|             <div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div> | ||||
|             <div class="c-overlay__dialog-hint">Displaying {{ngModel.dialog.messages.length}} message<span | ||||
|                     ng-show="ngModel.dialog.messages.length > 1 || | ||||
|                             ngModel.dialog.messages.length == 0">s</span> | ||||
|     <div class="t-message-list"> | ||||
|         <div class="top-bar"> | ||||
|             <div class="dialog-title">{{ngModel.dialog.title}}</div> | ||||
|             <div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1 || | ||||
|                                                                                                   ngModel.dialog.messages.length == 0">s</span> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="w-messages c-overlay__messages"> | ||||
|         <div class="w-messages"> | ||||
|             <mct-include | ||||
|                 ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'" | ||||
|                 key="'notification-message'" ng-model="msg.model"></mct-include> | ||||
|                 key="'message'" ng-model="msg.model"></mct-include> | ||||
|         </div> | ||||
|         <div class="c-overlay__bottom-bar"> | ||||
|             <button ng-repeat="dialogAction in ngModel.dialog.actions" | ||||
|                class="c-button c-button--major" | ||||
|         <div class="bottom-bar"> | ||||
|             <a ng-repeat="dialogAction in ngModel.dialog.actions" | ||||
|                class="s-button major" | ||||
|                ng-click="dialogAction.action()"> | ||||
|                 {{dialogAction.label}} | ||||
|             </button> | ||||
|             </a> | ||||
|         </div> | ||||
|     </div> | ||||
| </mct-container> | ||||
|   | ||||
| @@ -19,18 +19,18 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <mct-container key="c-overlay__contents"> | ||||
|     <div class=c-overlay__top-bar"> | ||||
|         <div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div> | ||||
|         <div class="c-overlay__dialog-hint hint">{{ngModel.dialog.hint}}</div> | ||||
| <mct-container key="overlay"> | ||||
|     <div class="abs top-bar"> | ||||
|         <div class="dialog-title">{{ngModel.dialog.title}}</div> | ||||
|         <div class="hint">{{ngModel.dialog.hint}}</div> | ||||
|     </div> | ||||
|     <div class='c-overlay__contents-main'> | ||||
|     <div class='abs editor'> | ||||
|         <mct-include key="ngModel.dialog.template" | ||||
|                      parameters="ngModel.dialog.parameters" | ||||
|                      ng-model="ngModel.dialog.model"> | ||||
|         </mct-include> | ||||
|     </div> | ||||
|     <div class="c-overlay__button-bar"> | ||||
|     <div class="abs bottom-bar"> | ||||
|         <a ng-repeat="option in ngModel.dialog.options" | ||||
|            href='' | ||||
|            class="s-button lg" | ||||
|   | ||||
| @@ -19,12 +19,12 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div class="c-overlay l-overlay-small" ng-class="{'delayEntry100ms' : ngModel.delay}"> | ||||
|     <div class="c-overlay__blocker"></div> | ||||
|     <div class="c-overlay__outer"> | ||||
|         <button ng-click="ngModel.cancel()" | ||||
| <div class="abs overlay l-dialog" ng-class="{'delayEntry100ms' : ngModel.delay}"> | ||||
|     <div class="abs blocker"></div> | ||||
|     <div class="abs outer-holder"> | ||||
|         <a ng-click="ngModel.cancel()" | ||||
|            ng-if="ngModel.cancel" | ||||
|            class="c-click-icon c-overlay__close-button icon-x-in-circle"></button> | ||||
|         <div class="c-overlay__contents" ng-transclude></div> | ||||
|            class="close icon-x-in-circle"></a> | ||||
|         <div class="abs inner-holder contents" ng-transclude></div> | ||||
|     </div> | ||||
| </div> | ||||
|   | ||||
| @@ -44,9 +44,8 @@ define( | ||||
|          * @memberof platform/commonUI/dialog | ||||
|          * @constructor | ||||
|          */ | ||||
|         function OverlayService($document, $compile, $rootScope, $timeout) { | ||||
|         function OverlayService($document, $compile, $rootScope) { | ||||
|             this.$compile = $compile; | ||||
|             this.$timeout = $timeout; | ||||
|  | ||||
|             // Don't include $document and $rootScope directly; | ||||
|             // avoids https://docs.angularjs.org/error/ng/cpws | ||||
| @@ -94,14 +93,12 @@ define( | ||||
|             scope.key = key; | ||||
|             scope.typeClass = typeClass || 't-dialog'; | ||||
|  | ||||
|             this.$timeout(() => { | ||||
|                 // Create the overlay element and add it to the document's body | ||||
|                 element = this.$compile(TEMPLATE)(scope); | ||||
|                  | ||||
|                 // Append so that most recent dialog is last in DOM. This means the most recent dialog will be on top when | ||||
|                 // multiple overlays with the same z-index are active. | ||||
|                 this.findBody().append(element); | ||||
|             }); | ||||
|             // Create the overlay element and add it to the document's body | ||||
|             element = this.$compile(TEMPLATE)(scope); | ||||
|              | ||||
|             // Append so that most recent dialog is last in DOM. This means the most recent dialog will be on top when | ||||
|             // multiple overlays with the same z-index are active. | ||||
|             this.findBody().append(element); | ||||
|  | ||||
|             return { | ||||
|                 dismiss: dismiss | ||||
|   | ||||
| @@ -35,20 +35,16 @@ define( | ||||
|                 mockTemplate, | ||||
|                 mockElement, | ||||
|                 mockScope, | ||||
|                 mockTimeout, | ||||
|                 overlayService; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockDocument = jasmine.createSpyObj("$document", ["find"]); | ||||
|                 mockCompile = jasmine.createSpy("$compile"); | ||||
|                 mockRootScope = jasmine.createSpyObj("$rootScope", ["$new"]); | ||||
|                 mockBody = jasmine.createSpyObj("body", ["append"]); | ||||
|                 mockBody = jasmine.createSpyObj("body", ["prepend"]); | ||||
|                 mockTemplate = jasmine.createSpy("template"); | ||||
|                 mockElement = jasmine.createSpyObj("element", ["remove"]); | ||||
|                 mockScope = jasmine.createSpyObj("scope", ["$destroy"]); | ||||
|                 mockTimeout = function (callback) { | ||||
|                     callback(); | ||||
|                 } | ||||
|  | ||||
|                 mockDocument.find.and.returnValue(mockBody); | ||||
|                 mockCompile.and.returnValue(mockTemplate); | ||||
| @@ -58,8 +54,7 @@ define( | ||||
|                 overlayService = new OverlayService( | ||||
|                     mockDocument, | ||||
|                     mockCompile, | ||||
|                     mockRootScope, | ||||
|                     mockTimeout | ||||
|                     mockRootScope | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
| @@ -72,7 +67,7 @@ define( | ||||
|  | ||||
|             it("adds the templated element to the body", function () { | ||||
|                 overlayService.createOverlay("test", {}); | ||||
|                 expect(mockBody.append).toHaveBeenCalledWith(mockElement); | ||||
|                 expect(mockBody.prepend).toHaveBeenCalledWith(mockElement); | ||||
|             }); | ||||
|  | ||||
|             it("places the provided model/key in its template's scope", function () { | ||||
|   | ||||
| @@ -160,7 +160,7 @@ define([ | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "remove", | ||||
|                     "category": "legacy", | ||||
|                     "category": "contextual", | ||||
|                     "implementation": RemoveAction, | ||||
|                     "cssClass": "icon-trash", | ||||
|                     "name": "Remove", | ||||
|   | ||||
| @@ -49,7 +49,7 @@ define( | ||||
|                     name: "Properties", | ||||
|                     rows: this.properties.map(function (property, index) { | ||||
|                         // Property definition is same as form row definition | ||||
|                         var row = JSON.parse(JSON.stringify(property.getDefinition())); | ||||
|                         var row = Object.create(property.getDefinition()); | ||||
|                         row.key = index; | ||||
|                         return row; | ||||
|                     }).filter(function (row) { | ||||
|   | ||||
| @@ -23,7 +23,11 @@ | ||||
| /** | ||||
|  * Module defining RemoveAction. Created by vwoeltje on 11/17/14. | ||||
|  */ | ||||
| define([], function () { | ||||
| define([ | ||||
|     './RemoveDialog' | ||||
| ], function ( | ||||
|     RemoveDialog | ||||
| ) { | ||||
|  | ||||
|     /** | ||||
|      * Construct an action which will remove the provided object manifestation. | ||||
| @@ -110,7 +114,12 @@ define([], function () { | ||||
|             return parent.useCapability('mutation', doMutate); | ||||
|         } | ||||
|  | ||||
|         removeFromContext(); | ||||
|         /* | ||||
|          * Pass in the function to remove the domain object so it can be | ||||
|          * associated with an 'OK' button press | ||||
|          */ | ||||
|         dialog = new RemoveDialog(this.openmct, domainObject, removeFromContext); | ||||
|         dialog.show(); | ||||
|     }; | ||||
|  | ||||
|     // Object needs to have a parent for Remove to be applicable | ||||
|   | ||||
							
								
								
									
										72
									
								
								platform/commonUI/edit/src/actions/RemoveDialog.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								platform/commonUI/edit/src/actions/RemoveDialog.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-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([], function () { | ||||
|  | ||||
|     /** | ||||
|      * @callback removeCallback | ||||
|      * @param {DomainObject} domainObject the domain object to be removed | ||||
|      */ | ||||
|  | ||||
|     /** | ||||
|      * Construct a new Remove dialog. | ||||
|      * | ||||
|      * @param {DialogService} dialogService the service that shows the dialog | ||||
|      * @param {DomainObject} domainObject the domain object to be removed | ||||
|      * @param {removeCallback} removeCallback callback that handles removal of the domain object | ||||
|      * @memberof platform/commonUI/edit | ||||
|      * @constructor | ||||
|      */ | ||||
|     function RemoveDialog(openmct, domainObject, removeCallback) { | ||||
|         this.openmct = openmct; | ||||
|         this.domainObject = domainObject; | ||||
|         this.removeCallback = removeCallback; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Display a dialog to confirm the removal of a domain object. | ||||
|      */ | ||||
|     RemoveDialog.prototype.show = function () { | ||||
|         let dialog = this.openmct.overlays.dialog({ | ||||
|             title: 'Remove ' + this.domainObject.getModel().name, | ||||
|             iconClass: 'alert', | ||||
|             message: 'Warning! This action will permanently remove this object. Are you sure you want to continue?', | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     label: 'OK', | ||||
|                     callback: () => { | ||||
|                         this.removeCallback(); | ||||
|                         dialog.dismiss(); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     label: 'Cancel', | ||||
|                     callback: () => { | ||||
|                         dialog.dismiss(); | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     return RemoveDialog; | ||||
| }); | ||||
| @@ -92,7 +92,16 @@ function ( | ||||
|          * @memberof platform/commonUI/edit.SaveAction# | ||||
|          */ | ||||
|     SaveAsAction.prototype.perform = function () { | ||||
|         return this.save(); | ||||
|         // Discard the current root view (which will be the editing | ||||
|         // UI, which will have been pushed atop the Browse UI.) | ||||
|         function returnToBrowse(object) { | ||||
|             if (object) { | ||||
|                 object.getCapability("action").perform("navigate"); | ||||
|             } | ||||
|             return object; | ||||
|         } | ||||
|  | ||||
|         return this.save().then(returnToBrowse); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
| @@ -162,6 +171,9 @@ function ( | ||||
|         function saveAfterClone(clonedObject) { | ||||
|             return this.openmct.editor.save().then(() => { | ||||
|                 // Force mutation for search indexing | ||||
|                 clonedObject.useCapability('mutation', (model) => { | ||||
|                     return model; | ||||
|                 }); | ||||
|                 return clonedObject; | ||||
|             }) | ||||
|         } | ||||
| @@ -170,14 +182,6 @@ function ( | ||||
|             return fetchObject(clonedObject.getId()) | ||||
|         } | ||||
|  | ||||
|         function indexForSearch(savedObject) { | ||||
|             savedObject.useCapability('mutation', (model) => { | ||||
|                 return model; | ||||
|             }); | ||||
|  | ||||
|             return savedObject; | ||||
|         } | ||||
|  | ||||
|         function onSuccess(object) { | ||||
|             self.notificationService.info("Save Succeeded"); | ||||
|             return object; | ||||
| @@ -188,7 +192,7 @@ function ( | ||||
|             if (reason !== "user canceled") { | ||||
|                 self.notificationService.error("Save Failed"); | ||||
|             } | ||||
|             throw reason; | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return getParent(domainObject) | ||||
| @@ -199,7 +203,6 @@ function ( | ||||
|             .then(undirtyOriginals) | ||||
|             .then(saveAfterClone) | ||||
|             .then(finishEditing) | ||||
|             .then(indexForSearch) | ||||
|             .then(hideBlockingDialog) | ||||
|             .then(onSuccess) | ||||
|             .catch(onFailure); | ||||
|   | ||||
| @@ -64,6 +64,7 @@ define( | ||||
|          * @returns boolean | ||||
|          */ | ||||
|         EditorCapability.prototype.inEditContext = function () { | ||||
|             console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.'); | ||||
|             return this.openmct.editor.isEditing(); | ||||
|         }; | ||||
|  | ||||
| @@ -73,6 +74,7 @@ define( | ||||
|          * @returns {*} | ||||
|          */ | ||||
|         EditorCapability.prototype.isEditContextRoot = function () { | ||||
|             console.warn('DEPRECATION WARNING: isEditing checks must be done via openmct.editor.'); | ||||
|             return this.openmct.editor.isEditing(); | ||||
|         }; | ||||
|  | ||||
|   | ||||
| @@ -67,38 +67,20 @@ define( | ||||
|                 openmct = this.openmct, | ||||
|                 newObject; | ||||
|  | ||||
|             function onSave() { | ||||
|                 // openmct.editor.save(); | ||||
|             } | ||||
|  | ||||
|             function onCancel() { | ||||
|                 openmct.editor.cancel(); | ||||
|             } | ||||
|  | ||||
|             function isFirstViewEditable(domainObject) { | ||||
|                 let firstView = openmct.objectViews.get(domainObject)[0]; | ||||
|  | ||||
|                 return firstView && firstView.canEdit && firstView.canEdit(domainObject); | ||||
|             } | ||||
|  | ||||
|             function navigateAndEdit(object) { | ||||
|                 let objectPath = object.getCapability('context').getPath(), | ||||
|                     url = '#/browse/' + objectPath | ||||
|                         .slice(1) | ||||
|                         .map(function (o) { | ||||
|                             return o && openmct.objects.makeKeyString(o.getId()); | ||||
|                         }) | ||||
|                         .join('/'); | ||||
|  | ||||
|                 window.location.href = url; | ||||
|  | ||||
|                 if (isFirstViewEditable(object.useCapability('adapter'))) { | ||||
|                     openmct.editor.edit(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             newModel.type = this.type.getKey(); | ||||
|             newModel.location = this.parent.getId(); | ||||
|             newObject = this.parent.useCapability('instantiation', newModel); | ||||
|  | ||||
|             openmct.editor.edit(); | ||||
|             newObject.getCapability("action").perform("save-as").then(navigateAndEdit, onCancel); | ||||
|             newObject.getCapability("action").perform("save-as").then(onSave, onCancel); | ||||
|             // TODO: support editing object without saving object first. | ||||
|             // Which means we have to toggle createwizard afterwards.  For now, | ||||
|             // We will disable this. | ||||
|   | ||||
| @@ -66,7 +66,7 @@ define( | ||||
|                 name: "Properties", | ||||
|                 rows: this.properties.map(function (property, index) { | ||||
|                     // Property definition is same as form row definition | ||||
|                     var row = JSON.parse(JSON.stringify(property.getDefinition())); | ||||
|                     var row = Object.create(property.getDefinition()); | ||||
|  | ||||
|                     // Use index as the key into the formValue; | ||||
|                     // this correlates to the indexing provided by | ||||
|   | ||||
| @@ -77,19 +77,14 @@ define([], function () { | ||||
|                 return promiseFn().then(nextFn); | ||||
|             }; | ||||
|         } | ||||
|         /** | ||||
|          * Clear any existing persistence calls for object with given ID. This ensures only the most recent persistence | ||||
|          * call is executed. This should prevent stale objects being persisted and overwriting fresh ones. | ||||
|          */ | ||||
|         if (this.isScheduled(id)) { | ||||
|             this.clearTransactionsFor(id); | ||||
|         } | ||||
|  | ||||
|         this.clearTransactionFns[id] = | ||||
|             this.transactionService.addToTransaction( | ||||
|                 chain(onCommit, release), | ||||
|                 chain(onCancel, release) | ||||
|             ); | ||||
|         if (!this.isScheduled(id)) { | ||||
|             this.clearTransactionFns[id] = | ||||
|                 this.transactionService.addToTransaction( | ||||
|                     chain(onCommit, release), | ||||
|                     chain(onCancel, release) | ||||
|                 ); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -93,33 +93,24 @@ define( | ||||
|                     expect(mockOnCancel).toHaveBeenCalled(); | ||||
|                 }); | ||||
|  | ||||
|                 describe("Adds callbacks to transaction", function () { | ||||
|                     beforeEach(function () { | ||||
|                         spyOn(manager, 'clearTransactionsFor'); | ||||
|                         manager.clearTransactionsFor.and.callThrough(); | ||||
|                     }); | ||||
|                 it("ignores subsequent calls for the same object", function () { | ||||
|                     manager.addToTransaction( | ||||
|                         testId, | ||||
|                         jasmine.createSpy(), | ||||
|                         jasmine.createSpy() | ||||
|                     ); | ||||
|                     expect(mockTransactionService.addToTransaction.calls.count()) | ||||
|                         .toEqual(1); | ||||
|                 }); | ||||
|  | ||||
|                     it("and clears pending calls if same object", function () { | ||||
|                         manager.addToTransaction( | ||||
|                             testId, | ||||
|                             jasmine.createSpy(), | ||||
|                             jasmine.createSpy() | ||||
|                         ); | ||||
|                         expect(manager.clearTransactionsFor).toHaveBeenCalledWith(testId); | ||||
|                     }); | ||||
|  | ||||
|                     it("and does not clear pending calls if different object", function () { | ||||
|                         manager.addToTransaction( | ||||
|                             'other-id', | ||||
|                             jasmine.createSpy(), | ||||
|                             jasmine.createSpy() | ||||
|                         ); | ||||
|                         expect(manager.clearTransactionsFor).not.toHaveBeenCalled(); | ||||
|                     }); | ||||
|  | ||||
|                     afterEach(function () { | ||||
|                         expect(mockTransactionService.addToTransaction.calls.count()).toEqual(2); | ||||
|                     }); | ||||
|                 it("accepts subsequent calls for other objects", function () { | ||||
|                     manager.addToTransaction( | ||||
|                         'other-id', | ||||
|                         jasmine.createSpy(), | ||||
|                         jasmine.createSpy() | ||||
|                     ); | ||||
|                     expect(mockTransactionService.addToTransaction.calls.count()) | ||||
|                         .toEqual(2); | ||||
|                 }); | ||||
|  | ||||
|                 it("does not remove callbacks from the transaction", function () { | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div class="c-object-label"> | ||||
|     <div class="c-object-label__type-icon {{type.getCssClass()}}" ng-class="{ 'l-icon-link':location.isLink() }"></div> | ||||
|     <div class='c-object-label__name'>{{model.name}}</div> | ||||
| <div class="t-object-label l-flex-row flex-elem grows"> | ||||
|     <div class="t-item-icon flex-elem {{type.getCssClass()}}" ng-class="{ 'l-icon-link':location.isLink() }"></div> | ||||
|     <div class='t-title-label flex-elem grows'>{{model.name}}</div> | ||||
| </div> | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| <div ng-controller="BannerController" ng-show="active.notification" | ||||
|      class="c-message-banner {{active.notification.model.severity}}" ng-class="{ | ||||
|      class="l-message-banner s-message-banner {{active.notification.model.severity}}" ng-class="{ | ||||
|      'minimized': active.notification.model.minimized, | ||||
|      'new': !active.notification.model.minimized}" | ||||
|      ng-click="maximize(active.notification)"> | ||||
|     <span class="c-message-banner__message"> | ||||
|     <span class="banner-elem label"> | ||||
|         {{active.notification.model.title}} | ||||
|     </span> | ||||
|     <span ng-show="active.notification.model.progress !== undefined || active.notification.model.unknownProgress"> | ||||
|         <mct-include key="'progress-bar'" class="c-message-banner__progress-bar" | ||||
|         <mct-include key="'progress-bar'" class="banner-elem" | ||||
|                      ng-model="active.notification.model"> | ||||
|         </mct-include> | ||||
|     </span> | ||||
| @@ -16,5 +16,5 @@ | ||||
|        ng-click="action(active.notification.model.primaryOption.callback, $event)"> | ||||
|         {{active.notification.model.primaryOption.label}} | ||||
|     </a> | ||||
|     <button class="c-message-banner__close-button c-click-icon icon-x-in-circle" ng-click="dismiss(active.notification, $event)"></button> | ||||
|     <a class="banner-elem close icon-x" ng-click="dismiss(active.notification, $event)"></a> | ||||
| </div> | ||||
|   | ||||
| @@ -20,11 +20,14 @@ | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <span ng-controller="ToggleController as toggle"> | ||||
|     <div class="u-contents" ng-controller="TreeNodeController as treeNode"> | ||||
|         <div class="c-tree__item menus-to-left" | ||||
|             ng-class="{selected: treeNode.isSelected()}"> | ||||
|             <span class='c-disclosure-triangle c-tree__item__view-control' | ||||
|                 ng-class="{ 'is-enabled': model.composition !== undefined, 'c-disclosure-triangle--expanded': toggle.isActive() }" | ||||
|     <span ng-controller="TreeNodeController as treeNode"> | ||||
|         <span | ||||
|             class="tree-item menus-to-left" | ||||
|             ng-class="{selected: treeNode.isSelected()}" | ||||
|             > | ||||
|             <span | ||||
|                 class='ui-symbol view-control flex-elem' | ||||
|                 ng-class="{ 'has-children': model.composition !== undefined, expanded: toggle.isActive() }" | ||||
|                 ng-click="toggle.toggle(); treeNode.trackExpansion()" | ||||
|                 > | ||||
|             </span> | ||||
| @@ -36,15 +39,19 @@ | ||||
|                 ng-click="treeNode.select()" | ||||
|                 > | ||||
|             </mct-representation> | ||||
|         </div> | ||||
|         <div class="u-contents" | ||||
|         </span> | ||||
|         <span | ||||
|             class="tree-item-subtree" | ||||
|             ng-show="toggle.isActive()" | ||||
|             ng-if="model.composition !== undefined"> | ||||
|             ng-if="model.composition !== undefined" | ||||
|             > | ||||
|  | ||||
|             <mct-representation key="'subtree'" | ||||
|                                 ng-model="ngModel" | ||||
|                                 parameters="parameters" | ||||
|                                 mct-object="treeNode.hasBeenExpanded() && domainObject"> | ||||
|             </mct-representation> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|         </span> | ||||
|     </span> | ||||
| </span> | ||||
|   | ||||
| @@ -19,8 +19,8 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <ul class="c-tree"> | ||||
|     <li class="c-tree__item-h"> | ||||
| <ul class="tree"> | ||||
|     <li> | ||||
|         <mct-representation key="'tree-node'" | ||||
|                             mct-object="domainObject" | ||||
|                             ng-model="ngModel" | ||||
|   | ||||
| @@ -1,2 +1,4 @@ | ||||
| <span class="c-tree__item js-tree__item"></span> | ||||
| <span class="c-tree__item-subtree"></span> | ||||
| <span class="tree-item menus-to-left"> | ||||
| </span> | ||||
| <span class="tree-item-subtree"> | ||||
| </span> | ||||
|   | ||||
| @@ -1 +1,2 @@ | ||||
| <span class='c-disclosure-triangle c-tree__item__view-control'></span> | ||||
| <span class='ui-symbol view-control flex-elem'> | ||||
| </span> | ||||
|   | ||||
| @@ -1,4 +1,6 @@ | ||||
| <div class="rep-object-label c-object-label c-tree__item__label"> | ||||
|     <div class="c-object-label__type-icon c-tree__item__type-icon t-item-icon"></div> | ||||
|     <div class="c-object-label__name c-tree__item__name t-title-label"></div> | ||||
| </div> | ||||
| <span class="rep-object-label"> | ||||
|     <div class="t-object-label l-flex-row flex-elem grows"> | ||||
|         <div class="t-item-icon flex-elem"></div> | ||||
|         <div class='t-title-label flex-elem grows'></div> | ||||
|     </div> | ||||
| </span> | ||||
|   | ||||
| @@ -54,7 +54,6 @@ define( | ||||
|                     if (isDestroyed) { | ||||
|                         return; | ||||
|                     } | ||||
|  | ||||
|                     var removeSelectable = openmct.selection.selectable( | ||||
|                         element[0], | ||||
|                         scope.$eval(attrs.mctSelectable), | ||||
|   | ||||
| @@ -37,9 +37,9 @@ define([ | ||||
|         this.expanded = state; | ||||
|  | ||||
|         if (state) { | ||||
|             this.el.addClass('c-disclosure-triangle--expanded'); | ||||
|             this.el.addClass('expanded'); | ||||
|         } else { | ||||
|             this.el.removeClass('c-disclosure-triangle--expanded'); | ||||
|             this.el.removeClass('expanded'); | ||||
|         } | ||||
|  | ||||
|         this.callbacks.forEach(function (callback) { | ||||
|   | ||||
| @@ -28,7 +28,7 @@ define([ | ||||
| ], function ($, nodeTemplate, ToggleView, TreeLabelView) { | ||||
|  | ||||
|     function TreeNodeView(gestureService, subtreeFactory, selectFn, openmct) { | ||||
|         this.li = $('<li class="c-tree__item-h">'); | ||||
|         this.li = $('<li>'); | ||||
|         this.openmct = openmct; | ||||
|         this.statusClasses = []; | ||||
|  | ||||
| @@ -38,7 +38,7 @@ define([ | ||||
|                 if (!this.subtreeView) { | ||||
|                     this.subtreeView = subtreeFactory(); | ||||
|                     this.subtreeView.model(this.activeObject); | ||||
|                     this.li.find('.c-tree__item-subtree').eq(0) | ||||
|                     this.li.find('.tree-item-subtree').eq(0) | ||||
|                         .append($(this.subtreeView.elements())); | ||||
|                 } | ||||
|                 $(this.subtreeView.elements()).removeClass('hidden'); | ||||
| @@ -85,9 +85,9 @@ define([ | ||||
|             var obj = domainObject.useCapability('adapter'); | ||||
|             var hasComposition =  this.openmct.composition.get(obj) !== undefined; | ||||
|             if (hasComposition) { | ||||
|                 $(this.toggleView.elements()).addClass('is-enabled'); | ||||
|                 $(this.toggleView.elements()).removeClass('no-children'); | ||||
|             } else { | ||||
|                 $(this.toggleView.elements()).removeClass('is-enabled'); | ||||
|                 $(this.toggleView.elements()).addClass('no-children'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -120,7 +120,7 @@ define([ | ||||
|             selectedIdPath = getIdPath(domainObject); | ||||
|  | ||||
|         if (this.onSelectionPath) { | ||||
|             this.li.find('.js-tree__item').eq(0).removeClass('is-selected'); | ||||
|             this.li.find('.tree-item').eq(0).removeClass('selected'); | ||||
|             if (this.subtreeView) { | ||||
|                 this.subtreeView.value(undefined); | ||||
|             } | ||||
| @@ -136,7 +136,7 @@ define([ | ||||
|  | ||||
|         if (this.onSelectionPath) { | ||||
|             if (activeIdPath.length === selectedIdPath.length) { | ||||
|                 this.li.find('.js-tree__item').eq(0).addClass('is-selected'); | ||||
|                 this.li.find('.tree-item').eq(0).addClass('selected'); | ||||
|             } else { | ||||
|                 // Expand to reveal the selection | ||||
|                 this.toggleView.value(true); | ||||
|   | ||||
| @@ -27,7 +27,7 @@ define([ | ||||
| ], function ($, TreeNodeView, spinnerTemplate) { | ||||
|  | ||||
|     function TreeView(gestureService, openmct, selectFn) { | ||||
|         this.ul = $('<ul class="c-tree"></ul>'); | ||||
|         this.ul = $('<ul class="tree"></ul>'); | ||||
|         this.nodeViews = []; | ||||
|         this.callbacks = []; | ||||
|         this.selectFn = selectFn || this.value.bind(this); | ||||
|   | ||||
| @@ -43,10 +43,23 @@ define([], function () { | ||||
|         var mutationTopic = topic('mutation'); | ||||
|         mutationTopic.listen(function (domainObject) { | ||||
|             var persistence = domainObject.getCapability('persistence'); | ||||
|             var wasActive = transactionService.isActive(); | ||||
|             cacheService.put(domainObject.getId(), domainObject.getModel()); | ||||
|  | ||||
|             if (hasChanged(domainObject)) { | ||||
|                 persistence.persist(); | ||||
|  | ||||
|                 if (!wasActive) { | ||||
|                     transactionService.startTransaction(); | ||||
|                 } | ||||
|  | ||||
|                 transactionService.addToTransaction( | ||||
|                     persistence.persist.bind(persistence), | ||||
|                     persistence.refresh.bind(persistence) | ||||
|                 ); | ||||
|  | ||||
|                 if (!wasActive) { | ||||
|                     transactionService.commit(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -24,27 +24,22 @@ define( | ||||
|     ["../../src/runs/TransactingMutationListener"], | ||||
|     function (TransactingMutationListener) { | ||||
|  | ||||
|         describe("TransactingMutationListener", function () { | ||||
|         xdescribe("TransactingMutationListener", function () { | ||||
|             var mockTopic, | ||||
|                 mockMutationTopic, | ||||
|                 mockCacheService, | ||||
|                 mockTransactionService, | ||||
|                 mockDomainObject, | ||||
|                 mockModel, | ||||
|                 mockPersistence; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockTopic = jasmine.createSpy('topic'); | ||||
|                 mockMutationTopic = | ||||
|                     jasmine.createSpyObj('mutation', ['listen']); | ||||
|                 mockCacheService = | ||||
|                     jasmine.createSpyObj('cacheService', [ | ||||
|                         'put' | ||||
|                     ]); | ||||
|                 mockTransactionService = | ||||
|                     jasmine.createSpyObj('transactionService', [ | ||||
|                         'isActive', | ||||
|                         'startTransaction', | ||||
|                         'addToTransaction', | ||||
|                         'commit' | ||||
|                     ]); | ||||
|                 mockDomainObject = jasmine.createSpyObj( | ||||
| @@ -57,24 +52,18 @@ define( | ||||
|                 ); | ||||
|  | ||||
|                 mockTopic.and.callFake(function (t) { | ||||
|                     expect(t).toBe('mutation'); | ||||
|                     return mockMutationTopic; | ||||
|                     return (t === 'mutation') && mockMutationTopic; | ||||
|                 }); | ||||
|  | ||||
|                 mockDomainObject.getId.and.returnValue('mockId'); | ||||
|                 mockDomainObject.getCapability.and.callFake(function (c) { | ||||
|                     expect(c).toBe('persistence'); | ||||
|                     return mockPersistence; | ||||
|                     return (c === 'persistence') && mockPersistence; | ||||
|                 }); | ||||
|                 mockModel = {}; | ||||
|                 mockDomainObject.getModel.and.returnValue(mockModel); | ||||
|  | ||||
|                 mockPersistence.persisted.and.returnValue(true); | ||||
|  | ||||
|                 return new TransactingMutationListener( | ||||
|                     mockTopic, | ||||
|                     mockTransactionService, | ||||
|                     mockCacheService | ||||
|                     mockTransactionService | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
| @@ -83,27 +72,48 @@ define( | ||||
|                     .toHaveBeenCalledWith(jasmine.any(Function)); | ||||
|             }); | ||||
|  | ||||
|             it("calls persist if the model has changed", function () { | ||||
|                 mockModel.persisted = Date.now(); | ||||
|             [false, true].forEach(function (isActive) { | ||||
|                 var verb = isActive ? "is" : "isn't"; | ||||
|  | ||||
|                 //Mark the model dirty by setting the mutated date later than the last persisted date. | ||||
|                 mockModel.modified = mockModel.persisted + 1; | ||||
|                 function onlyWhenInactive(expectation) { | ||||
|                     return isActive ? expectation.not : expectation; | ||||
|                 } | ||||
|  | ||||
|                 mockMutationTopic.listen.calls.mostRecent() | ||||
|                     .args[0](mockDomainObject); | ||||
|                 describe("when a transaction " + verb + " active", function () { | ||||
|                     var innerVerb = isActive ? "does" : "doesn't"; | ||||
|  | ||||
|                 expect(mockPersistence.persist).toHaveBeenCalled(); | ||||
|             }); | ||||
|                     beforeEach(function () { | ||||
|                         mockTransactionService.isActive.and.returnValue(isActive); | ||||
|                     }); | ||||
|  | ||||
|             it("does not call persist if the model has not changed", function () { | ||||
|                 mockModel.persisted = Date.now(); | ||||
|                     describe("and mutation occurs", function () { | ||||
|                         beforeEach(function () { | ||||
|                             mockMutationTopic.listen.calls.mostRecent() | ||||
|                                 .args[0](mockDomainObject); | ||||
|                         }); | ||||
|  | ||||
|                 mockModel.modified = mockModel.persisted; | ||||
|  | ||||
|                 mockMutationTopic.listen.calls.mostRecent() | ||||
|                     .args[0](mockDomainObject); | ||||
|                         it(innerVerb + " start a new transaction", function () { | ||||
|                             onlyWhenInactive( | ||||
|                                 expect(mockTransactionService.startTransaction) | ||||
|                             ).toHaveBeenCalled(); | ||||
|                         }); | ||||
|  | ||||
|                 expect(mockPersistence.persist).not.toHaveBeenCalled(); | ||||
|                         it("adds to the active transaction", function () { | ||||
|                             expect(mockTransactionService.addToTransaction) | ||||
|                                 .toHaveBeenCalledWith( | ||||
|                                     jasmine.any(Function), | ||||
|                                     jasmine.any(Function) | ||||
|                                 ); | ||||
|                         }); | ||||
|  | ||||
|                         it(innerVerb + " immediately commit", function () { | ||||
|                             onlyWhenInactive( | ||||
|                                 expect(mockTransactionService.commit) | ||||
|                             ).toHaveBeenCalled(); | ||||
|                         }); | ||||
|                     }); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
|   | ||||
| @@ -24,6 +24,7 @@ define([ | ||||
|     "./src/actions/MoveAction", | ||||
|     "./src/actions/CopyAction", | ||||
|     "./src/actions/LinkAction", | ||||
|     "./src/actions/GoToOriginalAction", | ||||
|     "./src/actions/SetPrimaryLocationAction", | ||||
|     "./src/services/LocatingCreationDecorator", | ||||
|     "./src/services/LocatingObjectDecorator", | ||||
| @@ -40,6 +41,7 @@ define([ | ||||
|     MoveAction, | ||||
|     CopyAction, | ||||
|     LinkAction, | ||||
|     GoToOriginalAction, | ||||
|     SetPrimaryLocationAction, | ||||
|     LocatingCreationDecorator, | ||||
|     LocatingObjectDecorator, | ||||
| @@ -102,6 +104,14 @@ define([ | ||||
|                         "linkService" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "follow", | ||||
|                     "name": "Go To Original", | ||||
|                     "description": "Go to the original, un-linked instance of this object.", | ||||
|                     "cssClass": "", | ||||
|                     "category": "contextual", | ||||
|                     "implementation": GoToOriginalAction | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "locate", | ||||
|                     "name": "Set Primary Location", | ||||
|   | ||||
							
								
								
									
										60
									
								
								platform/entanglement/src/actions/GoToOriginalAction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								platform/entanglement/src/actions/GoToOriginalAction.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT is licensed under the Apache License, Version 2.0 (the | ||||
|  * "License"); you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0. | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||
|  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||
|  * License for the specific language governing permissions and limitations | ||||
|  * under the License. | ||||
|  * | ||||
|  * Open MCT includes source code licensed under additional open source | ||||
|  * licenses. See the Open Source Licenses file (LICENSES.md) included with | ||||
|  * this source code distribution or the Licensing information page available | ||||
|  * at runtime from the About dialog for additional information. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define( | ||||
|     function () { | ||||
|  | ||||
|         /** | ||||
|          * Implements the "Go To Original" action, which follows a link back | ||||
|          * to an original instance of an object. | ||||
|          * | ||||
|          * @implements {Action} | ||||
|          * @constructor | ||||
|          * @private | ||||
|          * @memberof platform/entanglement | ||||
|          * @param {ActionContext} context the context in which the action | ||||
|          *        will be performed | ||||
|          */ | ||||
|         function GoToOriginalAction(context) { | ||||
|             this.domainObject = context.domainObject; | ||||
|         } | ||||
|  | ||||
|         GoToOriginalAction.prototype.perform = function () { | ||||
|             return this.domainObject.getCapability("location").getOriginal() | ||||
|                 .then(function (originalObject) { | ||||
|                     var actionCapability = | ||||
|                         originalObject.getCapability("action"); | ||||
|                     return actionCapability && | ||||
|                             actionCapability.perform("navigate"); | ||||
|                 }); | ||||
|         }; | ||||
|  | ||||
|         GoToOriginalAction.appliesTo = function (context) { | ||||
|             var domainObject = context.domainObject; | ||||
|             return domainObject && domainObject.hasCapability("location") && | ||||
|                 domainObject.getCapability("location").isLink(); | ||||
|         }; | ||||
|  | ||||
|         return GoToOriginalAction; | ||||
|     } | ||||
| ); | ||||
|  | ||||
							
								
								
									
										93
									
								
								platform/entanglement/test/actions/GoToOriginalActionSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								platform/entanglement/test/actions/GoToOriginalActionSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, 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/actions/GoToOriginalAction', | ||||
|         '../DomainObjectFactory', | ||||
|         '../ControlledPromise' | ||||
|     ], | ||||
|     function (GoToOriginalAction, domainObjectFactory, ControlledPromise) { | ||||
|  | ||||
|         describe("The 'go to original' action", function () { | ||||
|             var testContext, | ||||
|                 originalDomainObject, | ||||
|                 mockLocationCapability, | ||||
|                 mockOriginalActionCapability, | ||||
|                 originalPromise, | ||||
|                 action; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockLocationCapability = jasmine.createSpyObj( | ||||
|                     'location', | ||||
|                     ['isLink', 'isOriginal', 'getOriginal'] | ||||
|                 ); | ||||
|                 mockOriginalActionCapability = jasmine.createSpyObj( | ||||
|                     'action', | ||||
|                     ['perform', 'getActions'] | ||||
|                 ); | ||||
|                 originalPromise = new ControlledPromise(); | ||||
|                 mockLocationCapability.getOriginal.and.returnValue(originalPromise); | ||||
|                 mockLocationCapability.isLink.and.returnValue(true); | ||||
|                 mockLocationCapability.isOriginal.and.callFake(function () { | ||||
|                     return !mockLocationCapability.isLink(); | ||||
|                 }); | ||||
|                 testContext = { | ||||
|                     domainObject: domainObjectFactory({ | ||||
|                         capabilities: { | ||||
|                             location: mockLocationCapability | ||||
|                         } | ||||
|                     }) | ||||
|                 }; | ||||
|                 originalDomainObject = domainObjectFactory({ | ||||
|                     capabilities: { | ||||
|                         action: mockOriginalActionCapability | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 action = new GoToOriginalAction(testContext); | ||||
|             }); | ||||
|  | ||||
|             it("is applicable to links", function () { | ||||
|                 expect(GoToOriginalAction.appliesTo(testContext)) | ||||
|                     .toBeTruthy(); | ||||
|             }); | ||||
|  | ||||
|             it("is not applicable to originals", function () { | ||||
|                 mockLocationCapability.isLink.and.returnValue(false); | ||||
|                 expect(GoToOriginalAction.appliesTo(testContext)) | ||||
|                     .toBeFalsy(); | ||||
|             }); | ||||
|  | ||||
|             it("navigates to original objects when performed", function () { | ||||
|                 expect(mockOriginalActionCapability.perform) | ||||
|                     .not.toHaveBeenCalled(); | ||||
|                 action.perform(); | ||||
|                 originalPromise.resolve(originalDomainObject); | ||||
|                 expect(mockOriginalActionCapability.perform) | ||||
|                     .toHaveBeenCalledWith('navigate'); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| @@ -24,10 +24,10 @@ | ||||
|         <button ng-click="timer.clickStopButton()" | ||||
|                 ng-hide="timer.timerState == 'stopped'" | ||||
|                 title="Reset" | ||||
|                 class="c-timer__ctrl-reset c-icon-button c-icon-button--major icon-reset"></button> | ||||
|                 class="c-timer__ctrl-reset c-click-icon c-click-icon--major icon-reset"></button> | ||||
|         <button ng-click="timer.clickButton()" | ||||
|                 title="{{timer.buttonText()}}" | ||||
|                 class="c-timer__ctrl-pause-play c-icon-button c-icon-button--major {{timer.buttonCssClass()}}"></button> | ||||
|                 class="c-timer__ctrl-pause-play c-click-icon c-click-icon--major {{timer.buttonCssClass()}}"></button> | ||||
|     </div> | ||||
|     <div class="c-timer__direction {{timer.signClass()}}" | ||||
|         ng-hide="!timer.signClass()"></div> | ||||
|   | ||||
| @@ -45,6 +45,7 @@ define([ | ||||
|                             "key": "url", | ||||
|                             "name": "URL", | ||||
|                             "control": "textfield", | ||||
|                             "pattern": "^(ftp|https?)\\:\\/\\/", | ||||
|                             "required": true, | ||||
|                             "cssClass": "l-input-lg" | ||||
|                         }, | ||||
|   | ||||
| @@ -1,18 +1,22 @@ | ||||
| <div class="t-imagery c-imagery" ng-controller="ImageryController as imagery"> | ||||
| <div class="t-imagery" ng-controller="ImageryController as imagery"> | ||||
|     <mct-split-pane class='abs' anchor="bottom" alias="imagery"> | ||||
|     <div class="split-pane-component has-local-controls l-image-main-wrapper l-flex-col"> | ||||
|         <div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover l-flex-row c-imagery__lc"> | ||||
|             <span class="holder flex-elem grows c-imagery__lc__sliders"> | ||||
|     <div class="split-pane-component has-local-controls l-image-main-wrapper l-flex-col" | ||||
|         ng-mouseenter="showLocalControls = true;" | ||||
|         ng-mouseleave="showLocalControls = false;"> | ||||
|         <div class="h-local-controls h-local-controls-overlay-content h-local-controls-trans s-local-controls local-controls-hidden l-flex-row"> | ||||
|             <span class="holder flex-elem grows"> | ||||
|                 <input class="icon-brightness" type="range" | ||||
|                        min="0" | ||||
|                        max="500" | ||||
|                        ng-model="filters.brightness" /> | ||||
|                        ng-model="filters.brightness"> | ||||
|                 </input> | ||||
|                 <input class="icon-contrast" type="range" | ||||
|                        min="0" | ||||
|                        max="500" | ||||
|                        ng-model="filters.contrast" /> | ||||
|                        ng-model="filters.contrast"> | ||||
|                 </input> | ||||
|             </span> | ||||
|             <span class="holder flex-elem t-reset-btn-holder c-imagery__lc__reset-btn"> | ||||
|             <span class="holder flex-elem t-reset-btn-holder"> | ||||
|                 <a class="s-icon-button icon-reset t-btn-reset" | ||||
|                    ng-click="filters = { brightness: 100, contrast: 100 }"></a> | ||||
|             </span> | ||||
| @@ -29,14 +33,14 @@ | ||||
|  | ||||
|         <div class="l-image-main-controlbar flex-elem l-flex-row"> | ||||
|             <div class="l-datetime-w flex-elem grows"> | ||||
|                 <a class="c-button show-thumbs sm hidden icon-thumbs-strip" | ||||
|                 <a class="s-button show-thumbs sm hidden icon-thumbs-strip" | ||||
|                     ng-click="showThumbsBubble = (showThumbsBubble) ? false:true"></a> | ||||
|                 <span class="l-time">{{imagery.getTime()}}</span> | ||||
|             </div> | ||||
|             <div class="h-local-controls flex-elem"> | ||||
|                 <a class="c-button icon-pause pause-play" | ||||
|                 <a class="s-button pause-play" | ||||
|                     ng-click="imagery.paused(!imagery.paused())" | ||||
|                     ng-class="{ 'is-paused': imagery.paused() }"></a> | ||||
|                     ng-class="{ paused: imagery.paused() }"></a> | ||||
|                 <a href="" | ||||
|                     class="s-button l-mag s-mag vsm icon-reset" | ||||
|                     ng-click="clipped = false" | ||||
|   | ||||
| @@ -47,6 +47,7 @@ define([ | ||||
|                             "key": "url", | ||||
|                             "name": "URL", | ||||
|                             "control": "textfield", | ||||
|                             "pattern": "^(ftp|https?)\\:\\/\\/", | ||||
|                             "required": true, | ||||
|                             "cssClass": "l-input-lg" | ||||
|                         } | ||||
|   | ||||
| @@ -19,13 +19,13 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <form name="mctForm" novalidate class="form c-form" autocomplete="off"> | ||||
| <form name="mctForm" novalidate class="form l-flex-col"> | ||||
|     <span ng-repeat="section in structure.sections" | ||||
|           class="l-form-section c-form__section {{ section.cssClass }}"> | ||||
|         <h2 class="c-form__header" ng-if="section.name"> | ||||
|           class="l-form-section l-flex-col flex-elem {{ section.cssClass }}"> | ||||
|         <h2 class="section-header flex-elem" ng-if="section.name"> | ||||
|             {{section.name}} | ||||
|         </h2> | ||||
|         <ng-form class="form-row c-form__row validates {{ section.cssClass }}" | ||||
|         <ng-form class="form-row validates l-flex-row flex-elem {{ section.cssClass }}" | ||||
|                  ng-class="{ | ||||
|                  first:$index < 1, | ||||
|                  req: row.required, | ||||
| @@ -37,11 +37,11 @@ | ||||
|                  }" | ||||
|                  name="mctFormInner" | ||||
|                  ng-repeat="row in section.rows"> | ||||
|             <div class='c-form__row__label label flex-elem' title="{{row.description}}"> | ||||
|             <div class='label flex-elem' title="{{row.description}}"> | ||||
|                 {{row.name}} | ||||
|             </div> | ||||
|             <div class='c-form__row__controls controls flex-elem'> | ||||
|                 <div class="c-form__controls-wrapper wrapper" ng-if="row.control"> | ||||
|             <div class='controls flex-elem'> | ||||
|                 <div class="wrapper" ng-if="row.control"> | ||||
|                     <mct-control key="row.control" | ||||
|                                  ng-model="ngModel" | ||||
|                                  ng-required="row.required" | ||||
|   | ||||
| @@ -29,13 +29,12 @@ define( | ||||
|         function SnapshotPreviewController($scope, openmct) { | ||||
|  | ||||
|             $scope.previewImage = function (imageUrl) { | ||||
|                 let imageDiv = document.createElement('div'); | ||||
|                 imageDiv.classList = 'image-main s-image-main'; | ||||
|                 imageDiv.style.backgroundImage = `url(${imageUrl})`; | ||||
|                 let image = document.createElement('img'); | ||||
|                 image.src = imageUrl; | ||||
|  | ||||
|                 let previewImageOverlay = openmct.overlays.overlay( | ||||
|                     { | ||||
|                         element: imageDiv, | ||||
|                         element: image, | ||||
|                         size: 'large', | ||||
|                         buttons: [ | ||||
|                             { | ||||
|   | ||||
| @@ -64,30 +64,12 @@ define(['zepto'], function ($) { | ||||
|         var tree = this.generateNewIdentifiers(objTree); | ||||
|         var rootId = tree.rootId; | ||||
|         var rootObj = this.instantiate(tree.openmct[rootId], rootId); | ||||
|         var newStyleParent = parent.useCapability('adapter'); | ||||
|         var newStyleRootObj = rootObj.useCapability('adapter'); | ||||
|  | ||||
|         if (this.openmct.composition.checkPolicy(newStyleParent, newStyleRootObj)) { | ||||
|             // Instantiate all objects in tree with their newly generated ids, | ||||
|             // adding each to its rightful parent's composition | ||||
|             rootObj.getCapability("location").setPrimaryLocation(parent.getId()); | ||||
|             this.deepInstantiate(rootObj, tree.openmct, []); | ||||
|             parent.getCapability("composition").add(rootObj); | ||||
|         } else { | ||||
|             var dialog = this.openmct.overlays.dialog({ | ||||
|                 iconClass: 'alert', | ||||
|                 message: "We're sorry, but you cannot import that object type into this object.", | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         label: "Ok", | ||||
|                         emphasis: true, | ||||
|                         callback: function () { | ||||
|                             dialog.dismiss(); | ||||
|                         } | ||||
|                     } | ||||
|                 ] | ||||
|             }); | ||||
|         } | ||||
|         // Instantiate all objects in tree with their newly genereated ids, | ||||
|         // adding each to its rightful parent's composition | ||||
|         rootObj.getCapability("location").setPrimaryLocation(parent.getId()); | ||||
|         this.deepInstantiate(rootObj, tree.openmct, []); | ||||
|         parent.getCapability("composition").add(rootObj); | ||||
|     }; | ||||
|  | ||||
|     ImportAsJSONAction.prototype.deepInstantiate = function (parent, tree, seen) { | ||||
| @@ -98,17 +80,15 @@ define(['zepto'], function ($) { | ||||
|             var newObj; | ||||
|  | ||||
|             seen.push(parent.getId()); | ||||
|  | ||||
|             parentModel.composition.forEach(function (childId) { | ||||
|                 let keystring = this.openmct.objects.makeKeyString(childId); | ||||
|  | ||||
|                 if (!tree[keystring] || seen.includes(keystring)) { | ||||
|             parentModel.composition.forEach(function (childId, index) { | ||||
|                 if (!tree[childId] || seen.includes(childId)) { | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 newObj = this.instantiate(tree[keystring], keystring); | ||||
|                 newObj = this.instantiate(tree[childId], childId); | ||||
|                 parent.getCapability("composition").add(newObj); | ||||
|                 newObj.getCapability("location") | ||||
|                     .setPrimaryLocation(tree[keystring].location); | ||||
|                     .setPrimaryLocation(tree[childId].location); | ||||
|                 this.deepInstantiate(newObj, tree, seen); | ||||
|             }, this); | ||||
|         } | ||||
|   | ||||
| @@ -37,17 +37,75 @@ define( | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Discard failures | ||||
|          * Handle persistence failures by providing the user with a | ||||
|          * dialog summarizing these failures, and giving the option | ||||
|          * to overwrite/cancel as appropriate. | ||||
|          * @param {Array} failures persistence failures, as prepared | ||||
|          *        by PersistenceQueueHandler | ||||
|          * @memberof platform/persistence/queue.PersistenceFailureHandler# | ||||
|          */ | ||||
|         PersistenceFailureHandler.prototype.handle = function handleFailures(failures) { | ||||
|  | ||||
|             // Prepare dialog for display | ||||
|             var dialogModel = new PersistenceFailureDialog(failures), | ||||
|                 revisionErrors = dialogModel.model.revised, | ||||
|                 $q = this.$q; | ||||
|  | ||||
|             // Refresh revision information for the domain object associated | ||||
|             // with this persistence failure | ||||
|             function refresh(failure) { | ||||
|                 // Refresh the domain object to the latest from persistence | ||||
|                 return failure.persistence.refresh(); | ||||
|             } | ||||
|  | ||||
|             // Issue a new persist call for the domain object associated with | ||||
|             // this failure. | ||||
|             function persist(failure) { | ||||
|                 // Note that we reissue the persist request here, but don't | ||||
|                 // return it, to avoid a circular wait. We trust that the | ||||
|                 // PersistenceQueue will behave correctly on the next round | ||||
|                 // of flushing. | ||||
|                 failure.requeue(); | ||||
|             } | ||||
|  | ||||
|             // Retry persistence (overwrite) for this set of failed attempts | ||||
|             function retry(failuresToRetry) { | ||||
|                 var models = {}; | ||||
|  | ||||
|                 // Cache a copy of the model | ||||
|                 function cacheModel(failure) { | ||||
|                     // Clone... | ||||
|                     models[failure.id] = JSON.parse(JSON.stringify( | ||||
|                         failure.domainObject.getModel() | ||||
|                     )); | ||||
|                 } | ||||
|  | ||||
|                 // Mutate a domain object to restore its model | ||||
|                 function remutate(failure) { | ||||
|                     var model = models[failure.id]; | ||||
|                     return failure.domainObject.useCapability( | ||||
|                         "mutation", | ||||
|                         function () { | ||||
|                             return model; | ||||
|                         }, | ||||
|                         model.modified | ||||
|                     ); | ||||
|                 } | ||||
|  | ||||
|                 // Cache the object models we might want to save | ||||
|                 failuresToRetry.forEach(cacheModel); | ||||
|  | ||||
|                 // Strategy here: | ||||
|                 // * Cache all of the models we might want to save (above) | ||||
|                 // * Refresh all domain objects (so they are latest versions) | ||||
|                 // * Re-insert the cached domain object models | ||||
|                 // * Invoke persistence again | ||||
|                 return $q.all(failuresToRetry.map(refresh)).then(function () { | ||||
|                     return $q.all(failuresToRetry.map(remutate)); | ||||
|                 }).then(function () { | ||||
|                     return $q.all(failuresToRetry.map(persist)); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             // Discard changes for a failed refresh | ||||
|             function discard(failure) { | ||||
|                 var persistence = | ||||
| @@ -60,7 +118,19 @@ define( | ||||
|                 return $q.all(failuresToDiscard.map(discard)); | ||||
|             } | ||||
|  | ||||
|             return discardAll(revisionErrors); | ||||
|             // Handle user input (did they choose to overwrite?) | ||||
|             function handleChoice(key) { | ||||
|                 // If so, try again | ||||
|                 if (key === PersistenceFailureConstants.OVERWRITE_KEY) { | ||||
|                     return retry(revisionErrors); | ||||
|                 } else { | ||||
|                     return discardAll(revisionErrors); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Prompt for user input, the overwrite if they said so. | ||||
|             return this.dialogService.getUserChoice(dialogModel) | ||||
|                 .then(handleChoice, handleChoice); | ||||
|         }; | ||||
|  | ||||
|         return PersistenceFailureHandler; | ||||
|   | ||||
| @@ -74,14 +74,43 @@ define( | ||||
|                 handler = new PersistenceFailureHandler(mockQ, mockDialogService); | ||||
|             }); | ||||
|  | ||||
|             it("discards on handle", function () { | ||||
|             it("shows a dialog to handle failures", function () { | ||||
|                 handler.handle(mockFailures); | ||||
|                 expect(mockDialogService.getUserChoice).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("overwrites on request", function () { | ||||
|                 mockQ.all.and.returnValue(asPromise([])); | ||||
|                 handler.handle(mockFailures); | ||||
|                 // User chooses overwrite | ||||
|                 mockPromise.then.calls.mostRecent().args[0](Constants.OVERWRITE_KEY); | ||||
|                 // Should refresh, remutate, and requeue all objects | ||||
|                 mockFailures.forEach(function (mockFailure, i) { | ||||
|                     expect(mockFailure.persistence.refresh).toHaveBeenCalled(); | ||||
|                     expect(mockFailure.requeue).toHaveBeenCalled(); | ||||
|                     expect(mockFailure.domainObject.useCapability).toHaveBeenCalledWith( | ||||
|                         'mutation', | ||||
|                         jasmine.any(Function), | ||||
|                         i // timestamp | ||||
|                     ); | ||||
|                     expect(mockFailure.domainObject.useCapability.calls.mostRecent().args[1]()) | ||||
|                         .toEqual({ id: mockFailure.id, modified: i }); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|             it("discards on request", function () { | ||||
|                 mockQ.all.and.returnValue(asPromise([])); | ||||
|                 handler.handle(mockFailures); | ||||
|                 // User chooses overwrite | ||||
|                 mockPromise.then.calls.mostRecent().args[0](false); | ||||
|                 // Should refresh, but not remutate, and requeue all objects | ||||
|                 mockFailures.forEach(function (mockFailure) { | ||||
|                     expect(mockFailure.persistence.refresh).toHaveBeenCalled(); | ||||
|                     expect(mockFailure.requeue).not.toHaveBeenCalled(); | ||||
|                     expect(mockFailure.domainObject.useCapability).not.toHaveBeenCalled(); | ||||
|                 }); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
|   | ||||
| @@ -45,7 +45,7 @@ | ||||
|                 </mct-include> | ||||
|             </div> | ||||
|  | ||||
|             <a class="c-button c-search__btn-cancel" | ||||
|             <a class="s-button c-search__btn-cancel" | ||||
|                ng-show="!(ngModel.input === '' || ngModel.input === undefined)" | ||||
|                ng-click="ngModel.input = ''; ngModel.checkAll = true; menuController.checkAll(); controller.search()"> | ||||
|                 Cancel</a> | ||||
|   | ||||
							
								
								
									
										34
									
								
								src/MCT.js
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								src/MCT.js
									
									
									
									
									
								
							| @@ -44,9 +44,6 @@ define([ | ||||
|     '../platform/core/src/objects/DomainObjectImpl', | ||||
|     '../platform/core/src/capabilities/ContextualDomainObject', | ||||
|     './ui/preview/plugin', | ||||
|     './api/Branding', | ||||
|     './plugins/licenses/plugin', | ||||
|     './plugins/remove/plugin', | ||||
|     'vue' | ||||
| ], function ( | ||||
|     EventEmitter, | ||||
| @@ -72,9 +69,6 @@ define([ | ||||
|     DomainObjectImpl, | ||||
|     ContextualDomainObject, | ||||
|     PreviewPlugin, | ||||
|     BrandingAPI, | ||||
|     LicensesPlugin, | ||||
|     RemoveActionPlugin, | ||||
|     Vue | ||||
| ) { | ||||
|     /** | ||||
| @@ -95,13 +89,6 @@ define([ | ||||
|      */ | ||||
|     function MCT() { | ||||
|         EventEmitter.call(this); | ||||
|         this.buildInfo = { | ||||
|             version: __OPENMCT_VERSION__, | ||||
|             buildDate: __OPENMCT_BUILD_DATE__, | ||||
|             revision: __OPENMCT_REVISION__, | ||||
|             branch: __OPENMCT_BUILD_BRANCH__ | ||||
|         }; | ||||
|  | ||||
|         this.legacyBundle = { extensions: { | ||||
|             services: [ | ||||
|                 { | ||||
| @@ -241,21 +228,16 @@ define([ | ||||
|  | ||||
|         this.contextMenu = new api.ContextMenuRegistry(); | ||||
|  | ||||
|         this.router = new ApplicationRouter(); | ||||
|  | ||||
|         this.branding = BrandingAPI.default; | ||||
|  | ||||
|         this.legacyRegistry = defaultRegistry; | ||||
|         this.install(this.plugins.Plot()); | ||||
|         this.install(this.plugins.TelemetryTable()); | ||||
|         this.install(this.plugins.DisplayLayout()); | ||||
|         this.install(PreviewPlugin.default()); | ||||
|         this.install(LegacyIndicatorsPlugin()); | ||||
|         this.install(LicensesPlugin.default()); | ||||
|         this.install(RemoveActionPlugin.default()); | ||||
|  | ||||
|         if (typeof BUILD_CONSTANTS !== 'undefined') { | ||||
|             this.install(buildInfoPlugin(BUILD_CONSTANTS)); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     MCT.prototype = Object.create(EventEmitter.prototype); | ||||
| @@ -326,12 +308,6 @@ define([ | ||||
|      *        MCT; if undefined, MCT will be run in the body of the document | ||||
|      */ | ||||
|     MCT.prototype.start = function (domElement) { | ||||
|         if (!this.plugins.DisplayLayout._installed) { | ||||
|             this.install(this.plugins.DisplayLayout({ | ||||
|                 showAsView: ['summary-widget'] | ||||
|             })); | ||||
|         } | ||||
|  | ||||
|         if (!domElement) { | ||||
|             domElement = document.body; | ||||
|         } | ||||
| @@ -355,8 +331,12 @@ define([ | ||||
|         legacyRegistry.register('adapter', this.legacyBundle); | ||||
|         legacyRegistry.enable('adapter'); | ||||
|  | ||||
|         this.install(LegacyIndicatorsPlugin()); | ||||
|  | ||||
|         this.router = new ApplicationRouter(); | ||||
|  | ||||
|         this.router.route(/^\/$/, () => { | ||||
|             this.router.setPath('/browse/'); | ||||
|             this.router.setPath('/browse/mine'); | ||||
|         }); | ||||
|  | ||||
|         /** | ||||
|   | ||||
| @@ -33,25 +33,20 @@ export default class LegacyContextMenuAction { | ||||
|     } | ||||
|  | ||||
|     invoke(objectPath) { | ||||
|         this.openmct.objects.getRoot().then((root) => { | ||||
|             let pathWithRoot = objectPath.slice(); | ||||
|             pathWithRoot.push(root); | ||||
|         let context = { | ||||
|             category: 'contextual', | ||||
|             domainObject: this.openmct.legacyObject(objectPath) | ||||
|         } | ||||
|         let legacyAction = new this.LegacyAction(context); | ||||
|  | ||||
|             let context = { | ||||
|                 category: 'contextual', | ||||
|                 domainObject: this.openmct.legacyObject(pathWithRoot) | ||||
|             } | ||||
|             let legacyAction = new this.LegacyAction(context); | ||||
|  | ||||
|             if (!legacyAction.getMetadata) { | ||||
|                 let metadata = Object.create(this.LegacyAction.definition); | ||||
|                 metadata.context = context; | ||||
|                 legacyAction.getMetadata = function () { | ||||
|                     return metadata; | ||||
|                 }.bind(legacyAction); | ||||
|             } | ||||
|             legacyAction.perform(); | ||||
|         }); | ||||
|         if (!legacyAction.getMetadata) { | ||||
|             let metadata = Object.create(this.LegacyAction.definition); | ||||
|             metadata.context = context; | ||||
|             legacyAction.getMetadata = function () { | ||||
|                 return metadata; | ||||
|             }.bind(legacyAction); | ||||
|         } | ||||
|         legacyAction.perform(); | ||||
|     } | ||||
|  | ||||
|     appliesTo(objectPath) { | ||||
|   | ||||
| @@ -36,7 +36,7 @@ define([ | ||||
|     './runs/RegisterLegacyTypes', | ||||
|     './services/LegacyObjectAPIInterceptor', | ||||
|     './views/installLegacyViews', | ||||
|     './policies/LegacyCompositionPolicyAdapter', | ||||
|     './policies/legacyCompositionPolicyAdapter', | ||||
|     './actions/LegacyActionAdapter' | ||||
| ], function ( | ||||
|     legacyRegistry, | ||||
|   | ||||
| @@ -137,7 +137,8 @@ define([ | ||||
|         function callbackWrapper(series) { | ||||
|             callback(createDatum(domainObject, metadata, series, series.getPointCount() - 1)); | ||||
|         } | ||||
|         return capability.subscribe(callbackWrapper, request) || function () {}; | ||||
|  | ||||
|         return capability.subscribe(callbackWrapper, request); | ||||
|     }; | ||||
|  | ||||
|     LegacyTelemetryProvider.prototype.supportsLimits = function (domainObject) { | ||||
| @@ -157,7 +158,7 @@ define([ | ||||
|  | ||||
|         return { | ||||
|             evaluate: function (datum, property) { | ||||
|                 return limitEvaluator.evaluate(datum, property && property.key); | ||||
|                 return limitEvaluator.evaluate(datum, property.key); | ||||
|             } | ||||
|         }; | ||||
|     }; | ||||
|   | ||||
| @@ -57,10 +57,8 @@ define([ | ||||
|         }.bind(this); | ||||
|  | ||||
|         handleLegacyMutation = function (legacyObject) { | ||||
|             var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId()), | ||||
|                 keystring = utils.makeKeyString(newStyleObject.identifier); | ||||
|  | ||||
|             this.eventEmitter.emit(keystring + ":*", newStyleObject); | ||||
|             var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId()); | ||||
|             this.eventEmitter.emit(newStyleObject.identifier.key + ":*", newStyleObject); | ||||
|             this.eventEmitter.emit('mutation', newStyleObject); | ||||
|         }.bind(this); | ||||
|  | ||||
|   | ||||
| @@ -45,30 +45,15 @@ define([ | ||||
|             view: function (domainObject) { | ||||
|                 let $rootScope = openmct.$injector.get('$rootScope'); | ||||
|                 let templateLinker = openmct.$injector.get('templateLinker'); | ||||
|                 let scope = $rootScope.$new(true); | ||||
|                 let scope = $rootScope.$new(); | ||||
|                 let legacyObject = convertToLegacyObject(domainObject); | ||||
|                 let isDestroyed = false; | ||||
|                 let unlistenToStatus; | ||||
|                 let element; | ||||
|                 scope.domainObject = legacyObject; | ||||
|                 scope.model = legacyObject.getModel(); | ||||
|                 let child; | ||||
|                 let parent; | ||||
|  | ||||
|  | ||||
|                 return { | ||||
|                     show: function (container) { | ||||
|                         parent = container; | ||||
|                         child = document.createElement('div'); | ||||
|                         parent.appendChild(child); | ||||
|                         let statusCapability = legacyObject.getCapability('status'); | ||||
|                         unlistenToStatus = statusCapability.listen((newStatus) => { | ||||
|                             child.classList.remove('s-status-timeconductor-unsynced'); | ||||
|  | ||||
|                             if (newStatus.includes('timeconductor-unsynced')) { | ||||
|                                 child.classList.add('s-status-timeconductor-unsynced'); | ||||
|                             } | ||||
|                         }); | ||||
|  | ||||
|                         // TODO: implement "gestures" support ? | ||||
|                         let uses = legacyView.uses || []; | ||||
|                         let promises = []; | ||||
| @@ -89,13 +74,12 @@ define([ | ||||
|                             uses.forEach(function (key, i) { | ||||
|                                 scope[key] = results[i]; | ||||
|                             }); | ||||
|                             element = openmct.$angular.element(child); | ||||
|                             templateLinker.link( | ||||
|                                 scope, | ||||
|                                 element, | ||||
|                                 openmct.$angular.element(container), | ||||
|                                 legacyView | ||||
|                             ); | ||||
|                             child.classList.add('u-contents'); | ||||
|                             container.classList.add('u-contents'); | ||||
|                         } | ||||
|  | ||||
|                         if (promises.length) { | ||||
| @@ -109,12 +93,7 @@ define([ | ||||
|                         } | ||||
|                     }, | ||||
|                     destroy: function () { | ||||
|                         element.off(); | ||||
|                         element.remove(); | ||||
|                         scope.$destroy(); | ||||
|                         element = null; | ||||
|                         scope = null; | ||||
|                         unlistenToStatus(); | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|   | ||||
| @@ -25,34 +25,25 @@ define([ | ||||
|             cssClass: representation.cssClass, | ||||
|             description: representation.description, | ||||
|             canView: function (selection) { | ||||
|                 if (selection.length !== 1 || selection[0].length === 0) { | ||||
|                 if (!selection[0] || !selection[0].context.item) { | ||||
|                     return false; | ||||
|                 } | ||||
|  | ||||
|                 let selectionContext = selection[0][0].context; | ||||
|  | ||||
|                 if (!selectionContext.item) { | ||||
|                     return false; | ||||
|                 } | ||||
|  | ||||
|                 return selectionContext.item.type === typeDefinition.key; | ||||
|                 let domainObject = selection[0].context.item; | ||||
|                 return domainObject.type === typeDefinition.key; | ||||
|             }, | ||||
|             view: function (selection) { | ||||
|                 let domainObject = selection[0][0].context.item; | ||||
|                 let domainObject = selection[0].context.item; | ||||
|                 let $rootScope = openmct.$injector.get('$rootScope'); | ||||
|                 let templateLinker = openmct.$injector.get('templateLinker'); | ||||
|                 let scope = $rootScope.$new(true); | ||||
|                 let scope = $rootScope.$new(); | ||||
|                 let legacyObject = convertToLegacyObject(domainObject); | ||||
|                 let isDestroyed = false; | ||||
|                 let element; | ||||
|                 scope.domainObject = legacyObject; | ||||
|                 scope.model = legacyObject.getModel(); | ||||
|  | ||||
|  | ||||
|                 return { | ||||
|                     show: function (container) { | ||||
|                         let child = document.createElement('div'); | ||||
|                         container.appendChild(child); | ||||
|                         // TODO: implement "gestures" support ? | ||||
|                         let uses = representation.uses || []; | ||||
|                         let promises = []; | ||||
| @@ -73,10 +64,9 @@ define([ | ||||
|                             uses.forEach(function (key, i) { | ||||
|                                 scope[key] = results[i]; | ||||
|                             }); | ||||
|                             element = openmct.$angular.element(child) | ||||
|                             templateLinker.link( | ||||
|                                 scope, | ||||
|                                 element, | ||||
|                                 openmct.$angular.element(container), | ||||
|                                 representation | ||||
|                             ); | ||||
|                             container.style.height = '100%'; | ||||
| @@ -93,11 +83,7 @@ define([ | ||||
|                         } | ||||
|                     }, | ||||
|                     destroy: function () { | ||||
|                         element.off(); | ||||
|                         element.remove(); | ||||
|                         scope.$destroy(); | ||||
|                         element = null; | ||||
|                         scope = null; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2019, 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. | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| let brandingOptions = {}; | ||||
|  | ||||
| /** | ||||
|  * @typedef {Object} BrandingOptions | ||||
|  * @memberOf openmct/branding | ||||
|  * @property {string} smallLogoImage URL to the image to use as the applications logo. | ||||
|  * This logo will appear on every screen and when clicked will launch the about dialog. | ||||
|  * @property {string} aboutHtml Custom content for the about screen. When defined the | ||||
|  * supplied content will be inserted at the start of the about dialog, and the default | ||||
|  * Open MCT splash logo will be suppressed. | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Set branding options for the application. These will override certain visual elements  | ||||
|  * of the application and allow for customization of the application. | ||||
|  * @param {BrandingOptions} options | ||||
|  */ | ||||
| export default function Branding(options) { | ||||
|     if (arguments.length === 1) { | ||||
|         brandingOptions = options; | ||||
|     } | ||||
|     return brandingOptions; | ||||
| } | ||||
| @@ -28,6 +28,11 @@ export default class Editor extends EventEmitter { | ||||
|         super(); | ||||
|         this.editing = false; | ||||
|         this.openmct = openmct; | ||||
|         document.addEventListener('drop', (event) => { | ||||
|             if (!this.isEditing()) { | ||||
|                 this.edit(); | ||||
|             } | ||||
|         }, {capture: true}); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -74,11 +79,9 @@ export default class Editor extends EventEmitter { | ||||
|      * @private | ||||
|      */ | ||||
|     cancel() { | ||||
|         let cancelPromise = this.getTransactionService().cancel(); | ||||
|         this.getTransactionService().cancel(); | ||||
|         this.editing = false; | ||||
|         this.emit('isEditing', false); | ||||
|  | ||||
|         return cancelPromise; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -21,23 +21,7 @@ define([ | ||||
|             topicService.and.returnValue(mutationTopic); | ||||
|             publicAPI = {}; | ||||
|             publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [ | ||||
|                 'get', | ||||
|                 'mutate', | ||||
|                 'observe', | ||||
|                 'areIdsEqual' | ||||
|             ]); | ||||
|  | ||||
|             publicAPI.objects.areIdsEqual.and.callFake(function (id1, id2) { | ||||
|                 return id1.namespace === id2.namespace && id1.key === id2.key; | ||||
|             }); | ||||
|  | ||||
|             publicAPI.composition = jasmine.createSpyObj('CompositionAPI', [ | ||||
|                 'checkPolicy' | ||||
|             ]); | ||||
|             publicAPI.composition.checkPolicy.and.returnValue(true); | ||||
|  | ||||
|             publicAPI.objects.eventEmitter = jasmine.createSpyObj('eventemitter', [ | ||||
|                 'on' | ||||
|                 'get' | ||||
|             ]); | ||||
|             publicAPI.objects.get.and.callFake(function (identifier) { | ||||
|                 return Promise.resolve({identifier: identifier}); | ||||
| @@ -68,14 +52,6 @@ define([ | ||||
|                         { | ||||
|                             namespace: 'test', | ||||
|                             key: 'a' | ||||
|                         }, | ||||
|                         { | ||||
|                             namespace: 'test', | ||||
|                             key: 'b' | ||||
|                         }, | ||||
|                         { | ||||
|                             namespace: 'test', | ||||
|                             key: 'c' | ||||
|                         } | ||||
|                     ] | ||||
|                 }; | ||||
| @@ -92,55 +68,55 @@ define([ | ||||
|                 composition.on('add', listener); | ||||
|  | ||||
|                 return composition.load().then(function () { | ||||
|                     expect(listener.calls.count()).toBe(3); | ||||
|                     expect(listener.calls.count()).toBe(1); | ||||
|                     expect(listener).toHaveBeenCalledWith({ | ||||
|                         identifier: {namespace: 'test', key: 'a'} | ||||
|                     }); | ||||
|                 }); | ||||
|             }); | ||||
|             describe('supports reordering of composition', function () { | ||||
|                 var listener; | ||||
|                 beforeEach(function () { | ||||
|                     listener = jasmine.createSpy('reorderListener'); | ||||
|                     composition.on('reorder', listener); | ||||
|  | ||||
|                     return composition.load(); | ||||
|                 }); | ||||
|                 it('', function () { | ||||
|                     composition.reorder(1, 0); | ||||
|                     let newComposition = | ||||
|                         publicAPI.objects.mutate.calls.mostRecent().args[2]; | ||||
|                     let reorderPlan = listener.calls.mostRecent().args[0][0]; | ||||
|  | ||||
|                     expect(reorderPlan.oldIndex).toBe(1); | ||||
|                     expect(reorderPlan.newIndex).toBe(0); | ||||
|                     expect(newComposition[0].key).toEqual('b'); | ||||
|                     expect(newComposition[1].key).toEqual('a'); | ||||
|                     expect(newComposition[2].key).toEqual('c'); | ||||
|                 }); | ||||
|                 it('', function () { | ||||
|                     composition.reorder(0, 2); | ||||
|                     let newComposition = | ||||
|                         publicAPI.objects.mutate.calls.mostRecent().args[2]; | ||||
|                     let reorderPlan = listener.calls.mostRecent().args[0][0]; | ||||
|  | ||||
|                     expect(reorderPlan.oldIndex).toBe(0); | ||||
|                     expect(reorderPlan.newIndex).toBe(2); | ||||
|                     expect(newComposition[0].key).toEqual('b'); | ||||
|                     expect(newComposition[1].key).toEqual('c'); | ||||
|                     expect(newComposition[2].key).toEqual('a'); | ||||
|                 }) | ||||
|             }); | ||||
|             it('supports adding an object to composition', function () { | ||||
|                 let addListener = jasmine.createSpy('addListener'); | ||||
|                 let mockChildObject = { | ||||
|                     identifier: {key: 'mock-key', namespace: ''} | ||||
|                 }; | ||||
|             // TODO: Implement add/removal in new default provider. | ||||
|             xit('synchronizes changes between instances', function () { | ||||
|                 var otherComposition = compositionAPI.get(domainObject); | ||||
|                 var addListener = jasmine.createSpy('addListener'); | ||||
|                 var removeListener = jasmine.createSpy('removeListener'); | ||||
|                 var otherAddListener = jasmine.createSpy('otherAddListener'); | ||||
|                 var otherRemoveListener = jasmine.createSpy('otherRemoveListener'); | ||||
|                 composition.on('add', addListener); | ||||
|                 composition.add(mockChildObject); | ||||
|                 composition.on('remove', removeListener); | ||||
|                 otherComposition.on('add', otherAddListener); | ||||
|                 otherComposition.on('remove', otherRemoveListener); | ||||
|  | ||||
|                 expect(domainObject.composition.length).toBe(4); | ||||
|                 expect(domainObject.composition[3]).toEqual(mockChildObject.identifier); | ||||
|                 return Promise.all([composition.load(), otherComposition.load()]) | ||||
|                     .then(function () { | ||||
|                         expect(addListener).toHaveBeenCalled(); | ||||
|                         expect(otherAddListener).toHaveBeenCalled(); | ||||
|                         expect(removeListener).not.toHaveBeenCalled(); | ||||
|                         expect(otherRemoveListener).not.toHaveBeenCalled(); | ||||
|  | ||||
|                         var object = addListener.calls.mostRecent().args[0]; | ||||
|                         composition.remove(object); | ||||
|                         expect(removeListener).toHaveBeenCalled(); | ||||
|                         expect(otherRemoveListener).toHaveBeenCalled(); | ||||
|  | ||||
|                         addListener.reset(); | ||||
|                         otherAddListener.reset(); | ||||
|                         composition.add(object); | ||||
|                         expect(addListener).toHaveBeenCalled(); | ||||
|                         expect(otherAddListener).toHaveBeenCalled(); | ||||
|  | ||||
|                         removeListener.reset(); | ||||
|                         otherRemoveListener.reset(); | ||||
|                         otherComposition.remove(object); | ||||
|                         expect(removeListener).toHaveBeenCalled(); | ||||
|                         expect(otherRemoveListener).toHaveBeenCalled(); | ||||
|  | ||||
|                         addListener.reset(); | ||||
|                         otherAddListener.reset(); | ||||
|                         otherComposition.add(object); | ||||
|                         expect(addListener).toHaveBeenCalled(); | ||||
|                         expect(otherAddListener).toHaveBeenCalled(); | ||||
|                     }); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
| @@ -163,9 +139,7 @@ define([ | ||||
|                                 key: 'thing' | ||||
|                             } | ||||
|                         ]); | ||||
|                     }, | ||||
|                     add: jasmine.createSpy('add'), | ||||
|                     remove: jasmine.createSpy('remove') | ||||
|                     } | ||||
|                 }; | ||||
|                 domainObject = { | ||||
|                     identifier: { | ||||
| @@ -195,25 +169,6 @@ define([ | ||||
|                     }); | ||||
|                 }); | ||||
|             }); | ||||
|             describe('Calling add or remove', function () { | ||||
|                 let mockChildObject; | ||||
|  | ||||
|                 beforeEach(function () { | ||||
|                     mockChildObject = { | ||||
|                         identifier: {key: 'mock-key', namespace: ''} | ||||
|                     }; | ||||
|                     composition.add(mockChildObject); | ||||
|                 }); | ||||
|  | ||||
|                 it('calls add on the provider', function () { | ||||
|                     expect(customProvider.add).toHaveBeenCalledWith(domainObject, mockChildObject.identifier); | ||||
|                 }); | ||||
|  | ||||
|                 it('calls remove on the provider', function () { | ||||
|                     composition.remove(mockChildObject); | ||||
|                     expect(customProvider.remove).toHaveBeenCalledWith(domainObject, mockChildObject.identifier); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe('dynamic custom composition', function () { | ||||
|   | ||||
| @@ -25,6 +25,7 @@ define([ | ||||
| ], function ( | ||||
|     _ | ||||
| ) { | ||||
|  | ||||
|     /** | ||||
|      * A CompositionCollection represents the list of domain objects contained | ||||
|      * by another domain object. It provides methods for loading this | ||||
| @@ -55,13 +56,13 @@ define([ | ||||
|         this.listeners = { | ||||
|             add: [], | ||||
|             remove: [], | ||||
|             load: [], | ||||
|             reorder: [] | ||||
|             load: [] | ||||
|         }; | ||||
|         this.onProviderAdd = this.onProviderAdd.bind(this); | ||||
|         this.onProviderRemove = this.onProviderRemove.bind(this); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Listen for changes to this composition.  Supports 'add', 'remove', and | ||||
|      * 'load' events. | ||||
| @@ -74,9 +75,7 @@ define([ | ||||
|         if (!this.listeners[event]) { | ||||
|             throw new Error('Event not supported by composition: ' + event); | ||||
|         } | ||||
|         if (!this.mutationListener) { | ||||
|             this._synchronize(); | ||||
|         } | ||||
|  | ||||
|         if (this.provider.on && this.provider.off) { | ||||
|             if (event === 'add') { | ||||
|                 this.provider.on( | ||||
| @@ -92,13 +91,6 @@ define([ | ||||
|                     this.onProviderRemove, | ||||
|                     this | ||||
|                 ); | ||||
|             } if (event === 'reorder') { | ||||
|                 this.provider.on( | ||||
|                     this.domainObject, | ||||
|                     'reorder', | ||||
|                     this.onProviderReorder, | ||||
|                     this | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -132,8 +124,6 @@ define([ | ||||
|  | ||||
|         this.listeners[event].splice(index, 1); | ||||
|         if (this.listeners[event].length === 0) { | ||||
|             this._destroy(); | ||||
|  | ||||
|             // Remove provider listener if this is the last callback to | ||||
|             // be removed. | ||||
|             if (this.provider.off && this.provider.on) { | ||||
| @@ -151,13 +141,6 @@ define([ | ||||
|                         this.onProviderRemove, | ||||
|                         this | ||||
|                     ); | ||||
|                 } else if (event === 'reorder') { | ||||
|                     this.provider.off( | ||||
|                         this.domainObject, | ||||
|                         'reorder', | ||||
|                         this.onProviderReorder, | ||||
|                         this | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -177,9 +160,6 @@ define([ | ||||
|      */ | ||||
|     CompositionCollection.prototype.add = function (child, skipMutate) { | ||||
|         if (!skipMutate) { | ||||
|             if (!this.publicAPI.composition.checkPolicy(this.domainObject, child)) { | ||||
|                 throw `Object of type ${child.type} cannot be added to object of type ${this.domainObject.type}`; | ||||
|             } | ||||
|             this.provider.add(this.domainObject, child.identifier); | ||||
|         } else { | ||||
|             this.emit('add', child); | ||||
| @@ -229,29 +209,6 @@ define([ | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Reorder the domain objects in this composition. | ||||
|      * | ||||
|      * A call to [load]{@link module:openmct.CompositionCollection#load} | ||||
|      * must have resolved before using this method. | ||||
|      * | ||||
|      * @param {number} oldIndex | ||||
|      * @param {number} newIndex | ||||
|      * @memberof module:openmct.CompositionCollection# | ||||
|      * @name remove | ||||
|      */ | ||||
|     CompositionCollection.prototype.reorder = function (oldIndex, newIndex, skipMutate) { | ||||
|         this.provider.reorder(this.domainObject, oldIndex, newIndex); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Handle reorder from provider. | ||||
|      * @private | ||||
|      */ | ||||
|     CompositionCollection.prototype.onProviderReorder = function (reorderMap) { | ||||
|         this.emit('reorder', reorderMap); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Handle adds from provider. | ||||
|      * @private | ||||
| @@ -271,29 +228,16 @@ define([ | ||||
|         this.remove(child, true); | ||||
|     }; | ||||
|  | ||||
|     CompositionCollection.prototype._synchronize = function () { | ||||
|         this.mutationListener = this.publicAPI.objects.observe(this.domainObject, '*', (newDomainObject) => { | ||||
|             this.domainObject = JSON.parse(JSON.stringify(newDomainObject)); | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     CompositionCollection.prototype._destroy = function () { | ||||
|         if (this.mutationListener) { | ||||
|             this.mutationListener(); | ||||
|             delete this.mutationListener; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Emit events. | ||||
|      * @private | ||||
|      */ | ||||
|     CompositionCollection.prototype.emit = function (event, ...payload) { | ||||
|     CompositionCollection.prototype.emit = function (event, payload) { | ||||
|         this.listeners[event].forEach(function (l) { | ||||
|             if (l.context) { | ||||
|                 l.callback.apply(l.context, payload); | ||||
|                 l.callback.call(l.context, payload); | ||||
|             } else { | ||||
|                 l.callback(...payload); | ||||
|                 l.callback(payload); | ||||
|             } | ||||
|         }); | ||||
|     }; | ||||
|   | ||||
| @@ -48,11 +48,24 @@ define([ | ||||
|         this.listeningTo = {}; | ||||
|         this.onMutation = this.onMutation.bind(this); | ||||
|  | ||||
|         this.cannotContainDuplicates = this.cannotContainDuplicates.bind(this); | ||||
|         this.cannotContainItself = this.cannotContainItself.bind(this); | ||||
|  | ||||
|         compositionAPI.addPolicy(this.cannotContainDuplicates); | ||||
|         compositionAPI.addPolicy(this.cannotContainItself); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @private | ||||
|      */ | ||||
|     DefaultCompositionProvider.prototype.cannotContainDuplicates = function (parent, child) { | ||||
|         return this.appliesTo(parent) && | ||||
|             parent.composition.findIndex((composeeId) => { | ||||
|                 return composeeId.namespace === child.identifier.namespace && | ||||
|                     composeeId.key === child.identifier.key; | ||||
|             }) === -1; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @private | ||||
|      */ | ||||
| @@ -113,7 +126,6 @@ define([ | ||||
|             objectListeners = this.listeningTo[keyString] = { | ||||
|                 add: [], | ||||
|                 remove: [], | ||||
|                 reorder: [], | ||||
|                 composition: [].slice.apply(domainObject.composition) | ||||
|             }; | ||||
|         } | ||||
| @@ -148,7 +160,7 @@ define([ | ||||
|         }); | ||||
|  | ||||
|         objectListeners[event].splice(index, 1); | ||||
|         if (!objectListeners.add.length && !objectListeners.remove.length && !objectListeners.reorder.length) { | ||||
|         if (!objectListeners.add.length && !objectListeners.remove.length) { | ||||
|             delete this.listeningTo[keyString]; | ||||
|         } | ||||
|     }; | ||||
| @@ -166,12 +178,8 @@ define([ | ||||
|      * @method remove | ||||
|      */ | ||||
|     DefaultCompositionProvider.prototype.remove = function (domainObject, childId) { | ||||
|         let composition = domainObject.composition.filter(function (child) { | ||||
|             return !(childId.namespace === child.namespace && | ||||
|                 childId.key === child.key); | ||||
|         }); | ||||
|  | ||||
|         this.publicAPI.objects.mutate(domainObject, 'composition', composition); | ||||
|         // TODO: this needs to be synchronized via mutation. | ||||
|         throw new Error('Default Provider does not implement removal.'); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
| @@ -186,66 +194,9 @@ define([ | ||||
|      * @memberof module:openmct.CompositionProvider# | ||||
|      * @method add | ||||
|      */ | ||||
|     DefaultCompositionProvider.prototype.add = function (parent, childId) { | ||||
|         if (!this.includes(parent, childId)) { | ||||
|             parent.composition.push(childId); | ||||
|             this.publicAPI.objects.mutate(parent, 'composition', parent.composition); | ||||
|         } | ||||
|     }; | ||||
|     /** | ||||
|      * @private | ||||
|      */ | ||||
|     DefaultCompositionProvider.prototype.includes = function (parent, childId) { | ||||
|         return parent.composition.findIndex(composee => | ||||
|             this.publicAPI.objects.areIdsEqual(composee, childId)) !== -1; | ||||
|     }; | ||||
|  | ||||
|     DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) { | ||||
|         let newComposition = domainObject.composition.slice(); | ||||
|         let removeId = oldIndex > newIndex ? oldIndex + 1 : oldIndex; | ||||
|         let insertPosition = oldIndex < newIndex ? newIndex + 1 : newIndex; | ||||
|         //Insert object in new position | ||||
|         newComposition.splice(insertPosition, 0, domainObject.composition[oldIndex]); | ||||
|         newComposition.splice(removeId, 1); | ||||
|  | ||||
|         let reorderPlan = [{ | ||||
|             oldIndex, | ||||
|             newIndex | ||||
|         }]; | ||||
|  | ||||
|         if (oldIndex > newIndex) { | ||||
|             for (let i = newIndex; i < oldIndex; i++) { | ||||
|                 reorderPlan.push({ | ||||
|                     oldIndex: i, | ||||
|                     newIndex: i + 1 | ||||
|                 }); | ||||
|             } | ||||
|         } else { | ||||
|             for (let i = oldIndex + 1; i <= newIndex; i++) { | ||||
|                 reorderPlan.push({ | ||||
|                     oldIndex: i, | ||||
|                     newIndex: i - 1 | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|         this.publicAPI.objects.mutate(domainObject, 'composition', newComposition); | ||||
|  | ||||
|         let id = objectUtils.makeKeyString(domainObject.identifier); | ||||
|         var listeners = this.listeningTo[id]; | ||||
|  | ||||
|         if (!listeners) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         listeners.reorder.forEach(notify); | ||||
|  | ||||
|         function notify(listener) { | ||||
|             if (listener.context) { | ||||
|                 listener.callback.call(listener.context, reorderPlan); | ||||
|             } else { | ||||
|                 listener.callback(reorderPlan); | ||||
|             } | ||||
|         } | ||||
|     DefaultCompositionProvider.prototype.add = function (domainObject, child) { | ||||
|         throw new Error('Default Provider does not implement adding.'); | ||||
|         // TODO: this needs to be synchronized via mutation | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -21,10 +21,8 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     './object-utils.js', | ||||
|     'lodash' | ||||
| ], function ( | ||||
|     utils, | ||||
|     _ | ||||
| ) { | ||||
|     var ANY_OBJECT_EVENT = "mutation"; | ||||
| @@ -43,9 +41,7 @@ define([ | ||||
|     } | ||||
|  | ||||
|     function qualifiedEventName(object, eventName) { | ||||
|         var keystring = utils.makeKeyString(object.identifier); | ||||
|  | ||||
|         return [keystring, eventName].join(':'); | ||||
|         return [object.identifier.key, eventName].join(':'); | ||||
|     } | ||||
|  | ||||
|     MutableObject.prototype.stopListening = function () { | ||||
|   | ||||
| @@ -226,20 +226,7 @@ define([ | ||||
|                     (identifier.namespace === identifiers[0].namespace && | ||||
|                         identifier.key === identifiers[0].key); | ||||
|             }); | ||||
|     }; | ||||
|  | ||||
|     ObjectAPI.prototype.getOriginalPath = function (identifier, path = []) { | ||||
|         return this.get(identifier).then((domainObject) => { | ||||
|             path.push(domainObject); | ||||
|             let location = domainObject.location; | ||||
|  | ||||
|             if (location) { | ||||
|                 return this.getOriginalPath(utils.parseKeyString(location), path); | ||||
|             } else { | ||||
|                 return path; | ||||
|             } | ||||
|         }); | ||||
|     }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Uniquely identifies a domain object. | ||||
|   | ||||
| @@ -5,8 +5,7 @@ import Vue from 'vue'; | ||||
| const cssClasses = { | ||||
|     large: 'l-overlay-large', | ||||
|     small: 'l-overlay-small', | ||||
|     fit: 'l-overlay-fit', | ||||
|     fullscreen: 'l-overlay-fullscreen' | ||||
|     fit: 'l-overlay-fit' | ||||
| }; | ||||
|  | ||||
| class Overlay extends EventEmitter { | ||||
|   | ||||
| @@ -27,16 +27,10 @@ | ||||
| <style lang="scss"> | ||||
|     @import "~styles/sass-base"; | ||||
|  | ||||
|     @mixin legacyMessage() { | ||||
|         flex: 0 1 auto; | ||||
|         font-family: symbolsfont; | ||||
|         font-size: $messageIconD; // Singleton message in a dialog | ||||
|         margin-right: $interiorMarginLg; | ||||
|     } | ||||
|  | ||||
|     .c-message { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         padding: $interiorMarginLg; | ||||
|  | ||||
|         > * + * { | ||||
|             margin-left: $interiorMarginLg; | ||||
| @@ -64,44 +58,7 @@ | ||||
|         &__title, | ||||
|         &__action-text { | ||||
|             font-size: 1.2em; // TEMP | ||||
|         } | ||||
|  | ||||
|         &--simple { | ||||
|             // Icon and text elements only | ||||
|             &:before { | ||||
|                 font-size: 30px !important; | ||||
|             } | ||||
|  | ||||
|             [class*='__text'] { | ||||
|                 font-size: 1.25em; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /************************** LEGACY */ | ||||
|         &.message-severity-info:before { | ||||
|             @include legacyMessage(); | ||||
|             content: $glyph-icon-info; | ||||
|             color: $colorInfo; | ||||
|         } | ||||
|  | ||||
|         &.message-severity-alert:before { | ||||
|             @include legacyMessage(); | ||||
|             content: $glyph-icon-alert-rect; | ||||
|             color: $colorWarningLo; | ||||
|         } | ||||
|  | ||||
|         &.message-severity-error:before { | ||||
|             @include legacyMessage(); | ||||
|             content: $glyph-icon-alert-triangle; | ||||
|             color: $colorWarningLo; | ||||
|         } | ||||
|  | ||||
|         // Messages in a list | ||||
|         .c-overlay__messages & { | ||||
|             padding: $interiorMarginLg; | ||||
|             &:before { | ||||
|                 font-size: $messageListIconD; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
|   | ||||
| @@ -56,45 +56,16 @@ | ||||
|         } | ||||
|  | ||||
|         &__close-button { | ||||
|             $p: $interiorMargin; | ||||
|             $p: $interiorMarginSm; | ||||
|             border-radius: 100% !important; | ||||
|             color: $overlayColorFg; | ||||
|             display: inline-block; | ||||
|             font-size: 1.25em; | ||||
|             position: absolute; | ||||
|             top: $p; right: $p; | ||||
|         } | ||||
|  | ||||
|         &__contents { | ||||
|             flex: 1 1 auto; | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|         } | ||||
|  | ||||
|         &__top-bar { | ||||
|             flex: 0 0 auto; | ||||
|             flex-direction: column; | ||||
|             display: flex; | ||||
|  | ||||
|             > * { | ||||
|                 flex: 0 0 auto; | ||||
|                 margin-bottom: $interiorMargin; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &__dialog-title { | ||||
|             @include ellipsize(); | ||||
|             font-size: 1.5em; | ||||
|             line-height: 120%; | ||||
|         } | ||||
|  | ||||
|         &__contents-main { | ||||
|             display: flex; | ||||
|             flex-direction: column; | ||||
|             flex: 1 1 auto; | ||||
|             height: 0; // Chrome 73 overflow bug fix | ||||
|             overflow: auto; | ||||
|             padding-right: $interiorMargin; // fend off scroll bar | ||||
|         } | ||||
|  | ||||
|         &__button-bar { | ||||
| @@ -118,29 +89,18 @@ | ||||
|         .c-overlay { | ||||
|             &__blocker { | ||||
|                 @include abs(); | ||||
|                 background: $colorOvrBlocker; | ||||
|                 background: rgba(black, 0.7); | ||||
|                 cursor: pointer; | ||||
|                 display: block; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Overlay types, styling for desktop. Appended to .l-overlay-wrapper element. | ||||
|         .l-overlay-large, | ||||
|         .l-overlay-small, | ||||
|         .l-overlay-fit { | ||||
|             .c-overlay__outer { | ||||
|             &__outer { | ||||
|                 border-radius: $overlayCr; | ||||
|                 box-shadow: rgba(black, 0.5) 0 2px 25px; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         .l-overlay-fullscreen { | ||||
|             // Used by About > Licenses display | ||||
|             .c-overlay__outer { | ||||
|                 @include overlaySizing($overlayOuterMarginFullscreen); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Overlay types, styling for desktop. Appended to .l-overlay-wrapper element. | ||||
|         .l-overlay-large { | ||||
|             // Default | ||||
|             .c-overlay__outer { | ||||
| @@ -154,7 +114,6 @@ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         .t-dialog-sm .l-overlay-small, // Legacy dialog support | ||||
|         .l-overlay-fit { | ||||
|             .c-overlay__outer { | ||||
|                 @include overlaySizing(auto); | ||||
|   | ||||
| @@ -280,11 +280,7 @@ define([ | ||||
|         if (!provider) { | ||||
|             return Promise.reject('No provider found'); | ||||
|         } | ||||
|         return provider.request.apply(provider, arguments).catch((rejected) => { | ||||
|             this.openmct.notifications.error('Error requesting telemetry data, see console for details'); | ||||
|             console.error(rejected); | ||||
|             return Promise.reject(rejected); | ||||
|         }); | ||||
|         return provider.request.apply(provider, arguments); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
| @@ -301,7 +297,7 @@ define([ | ||||
|      * @returns {Function} a function which may be called to terminate | ||||
|      *          the subscription | ||||
|      */ | ||||
|     TelemetryAPI.prototype.subscribe = function (domainObject, callback, options) { | ||||
|     TelemetryAPI.prototype.subscribe = function (domainObject, callback) { | ||||
|         var provider = this.findSubscriptionProvider(domainObject); | ||||
|  | ||||
|         if (!this.subscribeCache) { | ||||
| @@ -320,7 +316,7 @@ define([ | ||||
|                         subscriber.callbacks.forEach(function (cb) { | ||||
|                             cb(value); | ||||
|                         }); | ||||
|                     }, options); | ||||
|                     }); | ||||
|             } else { | ||||
|                 subscriber.unsubscribe = function () {}; | ||||
|             } | ||||
|   | ||||
| @@ -28,22 +28,14 @@ define([ | ||||
|     describe('Telemetry API', function () { | ||||
|         var openmct; | ||||
|         var telemetryAPI; | ||||
|         var mockTypeService; | ||||
|  | ||||
|         beforeEach(function () { | ||||
|             openmct = { | ||||
|                 time: jasmine.createSpyObj('timeAPI', [ | ||||
|                     'timeSystem', | ||||
|                     'bounds' | ||||
|                 ]), | ||||
|                 $injector: jasmine.createSpyObj('injector', [ | ||||
|                     'get' | ||||
|                 ]) | ||||
|             }; | ||||
|             mockTypeService = jasmine.createSpyObj('typeService', [ | ||||
|                 'getType' | ||||
|             ]); | ||||
|             openmct.$injector.get.and.returnValue(mockTypeService); | ||||
|             openmct.time.timeSystem.and.returnValue({key: 'system'}); | ||||
|             openmct.time.bounds.and.returnValue({start: 0, end: 1}); | ||||
|             telemetryAPI = new TelemetryAPI(openmct); | ||||
| @@ -304,233 +296,5 @@ define([ | ||||
|                 ); | ||||
|             }); | ||||
|         }); | ||||
|         describe('metadata', function () { | ||||
|             let mockMetadata = {}; | ||||
|             let mockObjectType = { | ||||
|                 typeDef: {} | ||||
|             }; | ||||
|             beforeEach(function () { | ||||
|                 telemetryAPI.addProvider({ | ||||
|                     key: 'mockMetadataProvider', | ||||
|                     supportsMetadata() { | ||||
|                         return true; | ||||
|                     }, | ||||
|                     getMetadata() { | ||||
|                         return mockMetadata; | ||||
|                     } | ||||
|                 }); | ||||
|                 mockTypeService.getType.and.returnValue(mockObjectType); | ||||
|             }) | ||||
|             it('respects explicit priority', function () { | ||||
|                 mockMetadata.values = [ | ||||
|                     { | ||||
|                         key: "name", | ||||
|                         name: "Name", | ||||
|                         hints: { | ||||
|                             priority: 2 | ||||
|                         } | ||||
|                          | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp", | ||||
|                         name: "Timestamp", | ||||
|                         hints: { | ||||
|                             priority: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "sin", | ||||
|                         name: "Sine", | ||||
|                         hints: { | ||||
|                             priority: 4 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "cos", | ||||
|                         name: "Cosine", | ||||
|                         hints: { | ||||
|                             priority: 3 | ||||
|                         } | ||||
|                     } | ||||
|                 ]; | ||||
|                 let metadata = telemetryAPI.getMetadata({}); | ||||
|                 let values = metadata.values(); | ||||
|  | ||||
|                 values.forEach((value, index) => { | ||||
|                     expect(value.hints.priority).toBe(index + 1); | ||||
|                 }); | ||||
|             }); | ||||
|             it('if no explicit priority, defaults to order defined', function () { | ||||
|                 mockMetadata.values = [ | ||||
|                     { | ||||
|                         key: "name", | ||||
|                         name: "Name" | ||||
|                          | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp", | ||||
|                         name: "Timestamp" | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "sin", | ||||
|                         name: "Sine" | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "cos", | ||||
|                         name: "Cosine" | ||||
|                     } | ||||
|                 ]; | ||||
|                 let metadata = telemetryAPI.getMetadata({}); | ||||
|                 let values = metadata.values(); | ||||
|  | ||||
|                 values.forEach((value, index) => { | ||||
|                     expect(value.key).toBe(mockMetadata.values[index].key); | ||||
|                 }); | ||||
|             }); | ||||
|             it('respects domain priority', function () { | ||||
|                 mockMetadata.values = [ | ||||
|                     { | ||||
|                         key: "name", | ||||
|                         name: "Name" | ||||
|                          | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-utc", | ||||
|                         name: "Timestamp UTC", | ||||
|                         hints: { | ||||
|                             domain: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-local", | ||||
|                         name: "Timestamp Local", | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "sin", | ||||
|                         name: "Sine", | ||||
|                         hints: { | ||||
|                             range: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "cos", | ||||
|                         name: "Cosine", | ||||
|                         hints: { | ||||
|                             range: 1 | ||||
|                         } | ||||
|                     } | ||||
|                 ]; | ||||
|                 let metadata = telemetryAPI.getMetadata({}); | ||||
|                 let values = metadata.valuesForHints(['domain']); | ||||
|  | ||||
|                 expect(values[0].key).toBe('timestamp-local'); | ||||
|                 expect(values[1].key).toBe('timestamp-utc'); | ||||
|             }); | ||||
|             it('respects range priority', function () { | ||||
|                 mockMetadata.values = [ | ||||
|                     { | ||||
|                         key: "name", | ||||
|                         name: "Name" | ||||
|                          | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-utc", | ||||
|                         name: "Timestamp UTC", | ||||
|                         hints: { | ||||
|                             domain: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-local", | ||||
|                         name: "Timestamp Local", | ||||
|                         hints: { | ||||
|                             domain: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "sin", | ||||
|                         name: "Sine", | ||||
|                         hints: { | ||||
|                             range: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "cos", | ||||
|                         name: "Cosine", | ||||
|                         hints: { | ||||
|                             range: 1 | ||||
|                         } | ||||
|                     } | ||||
|                 ]; | ||||
|                 let metadata = telemetryAPI.getMetadata({}); | ||||
|                 let values = metadata.valuesForHints(['range']); | ||||
|  | ||||
|                 expect(values[0].key).toBe('cos'); | ||||
|                 expect(values[1].key).toBe('sin'); | ||||
|             }); | ||||
|             it('respects priority and domain ordering', function () { | ||||
|                 mockMetadata.values = [ | ||||
|                     { | ||||
|                         key: "id", | ||||
|                         name: "ID", | ||||
|                         hints: { | ||||
|                             priority: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "name", | ||||
|                         name: "Name", | ||||
|                         hints: { | ||||
|                             priority: 1 | ||||
|                         } | ||||
|                          | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-utc", | ||||
|                         name: "Timestamp UTC", | ||||
|                         hints: { | ||||
|                             domain: 2, | ||||
|                             priority: 1 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-local", | ||||
|                         name: "Timestamp Local", | ||||
|                         hints: { | ||||
|                             domain: 1, | ||||
|                             priority: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "timestamp-pst", | ||||
|                         name: "Timestamp PST", | ||||
|                         hints: { | ||||
|                             domain: 3, | ||||
|                             priority: 2 | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "sin", | ||||
|                         name: "Sine" | ||||
|                     }, | ||||
|                     { | ||||
|                         key: "cos", | ||||
|                         name: "Cosine" | ||||
|                     } | ||||
|                 ]; | ||||
|                 let metadata = telemetryAPI.getMetadata({}); | ||||
|                 let values = metadata.valuesForHints(['priority', 'domain']); | ||||
|                 [ | ||||
|                     'timestamp-utc', | ||||
|                     'timestamp-local', | ||||
|                     'timestamp-pst' | ||||
|                 ].forEach((key, index) => { | ||||
|                     expect(values[index].key).toBe(key); | ||||
|                 }); | ||||
|             }); | ||||
|         }) | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -116,12 +116,12 @@ define([ | ||||
|             return hints.every(hasHint, metadata); | ||||
|         } | ||||
|         var matchingMetadata = this.valueMetadatas.filter(hasHints); | ||||
|         let iteratees = hints.map(hint => { | ||||
|             return (metadata) => { | ||||
|         var sortedMetadata = _.sortBy(matchingMetadata, function (metadata) { | ||||
|             return hints.map(function (hint) { | ||||
|                 return metadata.hints[hint]; | ||||
|             } | ||||
|             }); | ||||
|         }); | ||||
|         return _.sortByAll(matchingMetadata, ...iteratees); | ||||
|         return sortedMetadata; | ||||
|     }; | ||||
|  | ||||
|     TelemetryMetadataManager.prototype.getFilterableValues = function () { | ||||
|   | ||||
| @@ -35,9 +35,6 @@ define([ | ||||
|             canView: function (domainObject) { | ||||
|                 return domainObject.type === 'LadTableSet'; | ||||
|             }, | ||||
|             canEdit: function (domainObject) { | ||||
|                 return domainObject.type === 'LadTableSet'; | ||||
|             }, | ||||
|             view: function (domainObject) { | ||||
|                 let component; | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| define([ | ||||
|     './components/LADTable.vue', | ||||
|     './components/LadTable.vue', | ||||
|     'vue' | ||||
| ], function ( | ||||
|     LadTableComponent, | ||||
| @@ -35,9 +35,6 @@ define([ | ||||
|             canView: function (domainObject) { | ||||
|                 return domainObject.type === 'LadTable'; | ||||
|             }, | ||||
|             canEdit: function (domainObject) { | ||||
|                 return domainObject.type === 'LadTable'; | ||||
|             }, | ||||
|             view: function (domainObject) { | ||||
|                 let component; | ||||
|  | ||||
|   | ||||
| @@ -41,7 +41,7 @@ | ||||
|  | ||||
| <script> | ||||
| import lodash from 'lodash'; | ||||
| import LadRow from './LADRow.vue'; | ||||
| import LadRow from './LadRow.vue'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct', 'domainObject'], | ||||
| @@ -65,25 +65,17 @@ export default { | ||||
|             let index = _.findIndex(this.items, (item) => this.openmct.objects.makeKeyString(identifier) === item.key); | ||||
|  | ||||
|             this.items.splice(index, 1); | ||||
|         }, | ||||
|         reorder(reorderPlan) { | ||||
|             let oldItems = this.items.slice(); | ||||
|             reorderPlan.forEach((reorderEvent) => { | ||||
|                 this.$set(this.items, reorderEvent.newIndex, oldItems[reorderEvent.oldIndex]); | ||||
|             }); | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.composition = this.openmct.composition.get(this.domainObject); | ||||
|         this.composition.on('add', this.addItem); | ||||
|         this.composition.on('remove', this.removeItem); | ||||
|         this.composition.on('reorder', this.reorder); | ||||
|         this.composition.load(); | ||||
|     }, | ||||
|     destroyed() { | ||||
|         this.composition.off('add', this.addItem); | ||||
|         this.composition.off('remove', this.removeItem); | ||||
|         this.composition.off('reorder', this.reorder); | ||||
|     } | ||||
| } | ||||
| </script> | ||||
|   | ||||
| @@ -52,7 +52,7 @@ | ||||
|  | ||||
| <script> | ||||
|     import lodash from 'lodash'; | ||||
|     import LadRow from './LADRow.vue'; | ||||
|     import LadRow from './LadRow.vue'; | ||||
|  | ||||
|     export default { | ||||
|     inject: ['openmct', 'domainObject'], | ||||
| @@ -93,12 +93,6 @@ | ||||
|             this.primaryTelemetryObjects.splice(index,1); | ||||
|             primary = undefined; | ||||
|         }, | ||||
|         reorderPrimary(reorderPlan) { | ||||
|             let oldComposition = this.primaryTelemetryObjects.slice(); | ||||
|             reorderPlan.forEach(reorderEvent => { | ||||
|                 this.$set(this.primaryTelemetryObjects, reorderEvent.newIndex, oldComposition[reorderEvent.oldIndex]); | ||||
|             }); | ||||
|         }, | ||||
|         addSecondary(primary) { | ||||
|             return (domainObject) => { | ||||
|                 let secondary = {}; | ||||
| @@ -126,13 +120,11 @@ | ||||
|         this.composition = this.openmct.composition.get(this.domainObject); | ||||
|         this.composition.on('add', this.addPrimary); | ||||
|         this.composition.on('remove', this.removePrimary); | ||||
|         this.composition.on('reorder', this.reorderPrimary); | ||||
|         this.composition.load(); | ||||
|     }, | ||||
|     destroyed() { | ||||
|         this.composition.off('add', this.addPrimary); | ||||
|         this.composition.off('remove', this.removePrimary); | ||||
|         this.composition.off('reorder', this.reorderPrimary); | ||||
|         this.compositions.forEach(c => { | ||||
|             c.composition.off('add', c.addCallback); | ||||
|             c.composition.off('remove', c.removeCallback); | ||||
|   | ||||
| @@ -1,77 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, 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([ | ||||
|     './components/AlphanumericFormatView.vue', | ||||
|     'vue' | ||||
| ], function (AlphanumericFormatView, Vue) { | ||||
|  | ||||
|     function AlphanumericFormatViewProvider(openmct, options) { | ||||
|         function isTelemetryObject(selectionPath) { | ||||
|             let selectedObject = selectionPath[0].context.item; | ||||
|             let parentObject = selectionPath[1].context.item; | ||||
|             return parentObject &&  | ||||
|                 parentObject.type === 'layout' && | ||||
|                 selectedObject && | ||||
|                 openmct.telemetry.isTelemetryObject(selectedObject) && | ||||
|                 !options.showAsView.includes(selectedObject.type) | ||||
|         } | ||||
|  | ||||
|         return { | ||||
|             key: 'alphanumeric-format', | ||||
|             name: 'Alphanumeric Format', | ||||
|             canView: function (selection) { | ||||
|                 if (selection.length === 0 || selection[0].length === 1) { | ||||
|                     return false; | ||||
|                 } | ||||
|  | ||||
|                 return selection.every(isTelemetryObject); | ||||
|             }, | ||||
|             view: function (selection) { | ||||
|                 let component; | ||||
|                 return { | ||||
|                     show: function (element) { | ||||
|                         component = new Vue({ | ||||
|                             provide: { | ||||
|                                 openmct | ||||
|                             }, | ||||
|                             components: { | ||||
|                                 AlphanumericFormatView: AlphanumericFormatView.default | ||||
|                             }, | ||||
|                             template: '<alphanumeric-format-view></alphanumeric-format-view>', | ||||
|                             el: element | ||||
|                         }); | ||||
|                     }, | ||||
|                     destroy: function () { | ||||
|                         component.$destroy(); | ||||
|                         component = undefined; | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             priority: function () { | ||||
|                 return 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return AlphanumericFormatViewProvider; | ||||
| }); | ||||
| @@ -28,17 +28,11 @@ define([], function () { | ||||
|             key: "layout", | ||||
|             description: "A toolbar for objects inside a display layout.", | ||||
|             forSelection: function (selection) { | ||||
|                 if (!selection || selection.length === 0) { | ||||
|                     return false; | ||||
|                 } | ||||
|  | ||||
|                 let selectionPath = selection[0]; | ||||
|                 let selectedObject = selectionPath[0]; | ||||
|                 let selectedParent = selectionPath[1]; | ||||
|  | ||||
|                 // Apply the layout toolbar if the selected object is inside a layout, or the main layout is selected. | ||||
|                 return (selectedParent && selectedParent.context.item && selectedParent.context.item.type === 'layout') || | ||||
|                     (selectedObject.context.item && selectedObject.context.item.type === 'layout'); | ||||
|                 // Apply the layout toolbar if the edit mode is on, and the selected object | ||||
|                 // is inside a layout, or the main layout is selected. | ||||
|                 return (openmct.editor.isEditing() && selection && | ||||
|                     ((selection[1] && selection[1].context.item && selection[1].context.item.type === 'layout') || | ||||
|                         (selection[0].context.item && selection[0].context.item.type === 'layout'))); | ||||
|             }, | ||||
|             toolbar: function (selection) { | ||||
|                 const DIALOG_FORM = { | ||||
| @@ -79,72 +73,190 @@ define([], function () { | ||||
|                     return openmct.$injector.get('dialogService').getUserInput(form, {}); | ||||
|                 } | ||||
|  | ||||
|                 function getPath(selectionPath) { | ||||
|                     return `configuration.items[${selectionPath[0].context.index}]`; | ||||
|                 function getPath() { | ||||
|                     return `configuration.items[${selection[0].context.index}]`; | ||||
|                 } | ||||
|  | ||||
|                 function getAllTypes(selection) { | ||||
|                     return selection.filter(selectionPath => { | ||||
|                         let type = selectionPath[0].context.layoutItem.type; | ||||
|                         return type === 'text-view' || | ||||
|                             type === 'telemetry-view' || | ||||
|                             type === 'box-view' || | ||||
|                             type === 'image-view' || | ||||
|                             type === 'line-view' || | ||||
|                             type === 'subobject-view'; | ||||
|                 let selectedParent = selection[1] && selection[1].context.item, | ||||
|                     selectedObject = selection[0].context.item, | ||||
|                     layoutItem = selection[0].context.layoutItem, | ||||
|                     toolbar = []; | ||||
|  | ||||
|                 if (selectedObject && selectedObject.type === 'layout') { | ||||
|                     toolbar.push({ | ||||
|                         control: "menu", | ||||
|                         domainObject: selectedObject, | ||||
|                         method: function (option) { | ||||
|                             let name = option.name.toLowerCase(); | ||||
|                             let form = DIALOG_FORM[name]; | ||||
|                             if (form) { | ||||
|                                 getUserInput(form) | ||||
|                                     .then(element => selection[0].context.addElement(name, element)); | ||||
|                             } else { | ||||
|                                 selection[0].context.addElement(name); | ||||
|                             } | ||||
|                         }, | ||||
|                         key: "add", | ||||
|                         icon: "icon-plus", | ||||
|                         label: "Add", | ||||
|                         options: [ | ||||
|                             { | ||||
|                                 "name": "Box", | ||||
|                                 "class": "icon-box-round-corners" | ||||
|                             }, | ||||
|                             { | ||||
|                                 "name": "Line", | ||||
|                                 "class": "icon-line-horz" | ||||
|                             }, | ||||
|                             { | ||||
|                                 "name": "Text", | ||||
|                                 "class": "icon-font" | ||||
|                             }, | ||||
|                             { | ||||
|                                 "name": "Image", | ||||
|                                 "class": "icon-image" | ||||
|                             } | ||||
|                         ] | ||||
|                     }); | ||||
|                 } | ||||
|  | ||||
|                 function getAddButton(selection, selectionPath) { | ||||
|                     if (selection.length === 1) { | ||||
|                         selectionPath = selectionPath || selection[0]; | ||||
|                         return { | ||||
|                             control: "menu", | ||||
|                             domainObject: selectionPath[0].context.item, | ||||
|                             method: function (option) { | ||||
|                                 let name = option.name.toLowerCase(); | ||||
|                                 let form = DIALOG_FORM[name]; | ||||
|                                 if (form) { | ||||
|                                     getUserInput(form) | ||||
|                                         .then(element => selectionPath[0].context.addElement(name, element)); | ||||
|                                 } else { | ||||
|                                     selectionPath[0].context.addElement(name); | ||||
|                                 } | ||||
|                             }, | ||||
|                             key: "add", | ||||
|                             icon: "icon-plus", | ||||
|                             label: "Add", | ||||
|                             options: [ | ||||
|                                 { | ||||
|                                     "name": "Box", | ||||
|                                     "class": "icon-box-round-corners" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "name": "Line", | ||||
|                                     "class": "icon-line-horz" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "name": "Text", | ||||
|                                     "class": "icon-font" | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     "name": "Image", | ||||
|                                     "class": "icon-image" | ||||
|                                 } | ||||
|                             ] | ||||
|                         }; | ||||
|                     } | ||||
|                 if (!layoutItem) { | ||||
|                     return toolbar; | ||||
|                 } | ||||
|  | ||||
|                 function getToggleFrameButton(selectedParent, selection) { | ||||
|                     return { | ||||
|                 let separator = { | ||||
|                     control: "separator" | ||||
|                 }; | ||||
|                 let remove = { | ||||
|                     control: "button", | ||||
|                     domainObject: selectedParent, | ||||
|                     icon: "icon-trash", | ||||
|                     title: "Delete the selected object", | ||||
|                     method: function () { | ||||
|                         let removeItem = selection[1].context.removeItem; | ||||
|                         let prompt = openmct.overlays.dialog({ | ||||
|                             iconClass: 'alert', | ||||
|                             message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`, | ||||
|                             buttons: [ | ||||
|                                 { | ||||
|                                     label: 'Ok', | ||||
|                                     emphasis: 'true', | ||||
|                                     callback: function () { | ||||
|                                         removeItem(layoutItem, selection[0].context.index); | ||||
|                                         prompt.dismiss(); | ||||
|                                     } | ||||
|                                 }, | ||||
|                                 { | ||||
|                                     label: 'Cancel', | ||||
|                                     callback: function () { | ||||
|                                         prompt.dismiss(); | ||||
|                                     } | ||||
|                                 } | ||||
|                             ] | ||||
|                         }); | ||||
|                     } | ||||
|                 }; | ||||
|                 let stackOrder = { | ||||
|                     control: "menu", | ||||
|                     domainObject: selectedParent, | ||||
|                     icon: "icon-layers", | ||||
|                     title: "Move the selected object above or below other objects", | ||||
|                     options: [ | ||||
|                         { | ||||
|                             name: "Move to Top", | ||||
|                             value: "top", | ||||
|                             class: "icon-arrow-double-up" | ||||
|                         }, | ||||
|                         { | ||||
|                             name: "Move Up", | ||||
|                             value: "up", | ||||
|                             class: "icon-arrow-up" | ||||
|                         }, | ||||
|                         { | ||||
|                             name: "Move Down", | ||||
|                             value: "down", | ||||
|                             class: "icon-arrow-down" | ||||
|                         }, | ||||
|                         { | ||||
|                             name: "Move to Bottom", | ||||
|                             value: "bottom", | ||||
|                             class: "icon-arrow-double-down" | ||||
|                         } | ||||
|                     ], | ||||
|                     method: function (option) { | ||||
|                         selection[1].context.orderItem(option.value, selection[0].context.index); | ||||
|                     } | ||||
|                 }; | ||||
|                 let useGrid = { | ||||
|                     control: "toggle-button", | ||||
|                     domainObject: selectedParent, | ||||
|                     property: function () { | ||||
|                         return getPath() + ".useGrid"; | ||||
|                     }, | ||||
|                     options: [ | ||||
|                         { | ||||
|                             value: false, | ||||
|                             icon: "icon-grid-snap-to", | ||||
|                             title: "Grid snapping enabled" | ||||
|                         }, | ||||
|                         { | ||||
|                             value: true, | ||||
|                             icon: "icon-grid-snap-no", | ||||
|                             title: "Grid snapping disabled" | ||||
|                         } | ||||
|                     ] | ||||
|                 }; | ||||
|                 let x = { | ||||
|                     control: "input", | ||||
|                     type: "number", | ||||
|                     domainObject: selectedParent, | ||||
|                     property: function () { | ||||
|                         return getPath() + ".x"; | ||||
|                     }, | ||||
|                     label: "X:", | ||||
|                     title: "X position" | ||||
|                 }, | ||||
|                 y = { | ||||
|                     control: "input", | ||||
|                     type: "number", | ||||
|                     domainObject: selectedParent, | ||||
|                     property: function () { | ||||
|                         return getPath() + ".y"; | ||||
|                     }, | ||||
|                     label: "Y:", | ||||
|                     title: "Y position", | ||||
|                 }, | ||||
|                 width = { | ||||
|                     control: 'input', | ||||
|                     type: 'number', | ||||
|                     domainObject: selectedParent, | ||||
|                     property: function () { | ||||
|                         return getPath() + ".width"; | ||||
|                     }, | ||||
|                     label: 'W:', | ||||
|                     title: 'Resize object width' | ||||
|                 }, | ||||
|                 height = { | ||||
|                     control: 'input', | ||||
|                     type: 'number', | ||||
|                     domainObject: selectedParent, | ||||
|                     property: function () { | ||||
|                         return getPath() + ".height"; | ||||
|                     }, | ||||
|                     label: 'H:', | ||||
|                     title: 'Resize object height' | ||||
|                 }; | ||||
|  | ||||
|                 if (layoutItem.type === 'subobject-view') { | ||||
|                     if (toolbar.length > 0) { | ||||
|                         toolbar.push(separator); | ||||
|                     } | ||||
|  | ||||
|                     toolbar.push({ | ||||
|                         control: "toggle-button", | ||||
|                         domainObject: selectedParent, | ||||
|                         applicableSelectedItems: selection.filter(selectionPath =>  | ||||
|                             selectionPath[0].context.layoutItem.type === 'subobject-view' | ||||
|                         ), | ||||
|                         property: function (selectionPath) { | ||||
|                             return getPath(selectionPath) + ".hasFrame"; | ||||
|                         property: function () { | ||||
|                             return getPath() + ".hasFrame"; | ||||
|                         }, | ||||
|                         options: [ | ||||
|                             { | ||||
| @@ -158,186 +270,52 @@ define([], function () { | ||||
|                                 title: "Frame hidden" | ||||
|                             } | ||||
|                         ] | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 function getRemoveButton(selectedParent, selectionPath, selection) { | ||||
|                     return { | ||||
|                         control: "button", | ||||
|                         domainObject: selectedParent, | ||||
|                         icon: "icon-trash", | ||||
|                         title: "Delete the selected object", | ||||
|                         method: function () { | ||||
|                             let removeItem = selectionPath[1].context.removeItem; | ||||
|                             let prompt = openmct.overlays.dialog({ | ||||
|                                 iconClass: 'alert', | ||||
|                                 message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`, | ||||
|                                 buttons: [ | ||||
|                                     { | ||||
|                                         label: 'Ok', | ||||
|                                         emphasis: 'true', | ||||
|                                         callback: function () { | ||||
|                                             removeItem(getAllTypes(selection)); | ||||
|                                             prompt.dismiss(); | ||||
|                                         } | ||||
|                                     }, | ||||
|                                     { | ||||
|                                         label: 'Cancel', | ||||
|                                         callback: function () { | ||||
|                                             prompt.dismiss(); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 ] | ||||
|                             }); | ||||
|                         } | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 function getStackOrder(selectedParent, selectionPath) { | ||||
|                     return { | ||||
|                         control: "menu", | ||||
|                         domainObject: selectedParent, | ||||
|                         icon: "icon-layers", | ||||
|                         title: "Move the selected object above or below other objects", | ||||
|                         options: [ | ||||
|                             { | ||||
|                                 name: "Move to Top", | ||||
|                                 value: "top", | ||||
|                                 class: "icon-arrow-double-up" | ||||
|                             }, | ||||
|                             { | ||||
|                                 name: "Move Up", | ||||
|                                 value: "up", | ||||
|                                 class: "icon-arrow-up" | ||||
|                             }, | ||||
|                             { | ||||
|                                 name: "Move Down", | ||||
|                                 value: "down", | ||||
|                                 class: "icon-arrow-down" | ||||
|                             }, | ||||
|                             { | ||||
|                                 name: "Move to Bottom", | ||||
|                                 value: "bottom", | ||||
|                                 class: "icon-arrow-double-down" | ||||
|                             } | ||||
|                         ], | ||||
|                         method: function (option) { | ||||
|                             selectionPath[1].context.orderItem(option.value, getAllTypes(selection)); | ||||
|                         } | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 function getXInput(selectedParent, selection) { | ||||
|                     if (selection.length === 1) { | ||||
|                         return { | ||||
|                             control: "input", | ||||
|                             type: "number", | ||||
|                             domainObject: selectedParent, | ||||
|                             applicableSelectedItems: getAllTypes(selection), | ||||
|                             property: function (selectionPath) { | ||||
|                                 return getPath(selectionPath) + ".x"; | ||||
|                             }, | ||||
|                             label: "X:", | ||||
|                             title: "X position" | ||||
|                         }; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 function getYInput(selectedParent, selection) { | ||||
|                     if (selection.length === 1) { | ||||
|                         return { | ||||
|                             control: "input", | ||||
|                             type: "number", | ||||
|                             domainObject: selectedParent, | ||||
|                             applicableSelectedItems: getAllTypes(selection), | ||||
|                             property: function (selectionPath) { | ||||
|                                 return getPath(selectionPath) + ".y"; | ||||
|                             }, | ||||
|                             label: "Y:", | ||||
|                             title: "Y position", | ||||
|                         }; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 function getWidthInput(selectedParent, selection) { | ||||
|                     if (selection.length === 1) { | ||||
|                         return { | ||||
|                             control: 'input', | ||||
|                             type: 'number', | ||||
|                             domainObject: selectedParent, | ||||
|                             applicableSelectedItems: getAllTypes(selection), | ||||
|                             property: function (selectionPath) { | ||||
|                                 return getPath(selectionPath) + ".width"; | ||||
|                             }, | ||||
|                             label: 'W:', | ||||
|                             title: 'Resize object width' | ||||
|                         }; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 function getHeightInput(selectedParent, selection) { | ||||
|                     if (selection.length === 1) { | ||||
|                         return { | ||||
|                             control: 'input', | ||||
|                             type: 'number', | ||||
|                             domainObject: selectedParent, | ||||
|                             applicableSelectedItems: getAllTypes(selection), | ||||
|                             property: function (selectionPath) { | ||||
|                                 return getPath(selectionPath) + ".height"; | ||||
|                             }, | ||||
|                             label: 'H:', | ||||
|                             title: 'Resize object height' | ||||
|                         }; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 function getX2Input(selectedParent, selection) { | ||||
|                     if (selection.length === 1) { | ||||
|                         return { | ||||
|                             control: "input", | ||||
|                             type: "number", | ||||
|                             domainObject: selectedParent, | ||||
|                             applicableSelectedItems: selection.filter(selectionPath => { | ||||
|                                 return selectionPath[0].context.layoutItem.type === 'line-view'; | ||||
|                             }), | ||||
|                             property: function (selectionPath) { | ||||
|                                 return getPath(selectionPath) + ".x2"; | ||||
|                             }, | ||||
|                             label: "X2:", | ||||
|                             title: "X2 position" | ||||
|                         }; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 function getY2Input(selectedParent, selection) { | ||||
|                     if (selection.length === 1) { | ||||
|                         return { | ||||
|                             control: "input", | ||||
|                             type: "number", | ||||
|                             domainObject: selectedParent, | ||||
|                             applicableSelectedItems: selection.filter(selectionPath => { | ||||
|                                 return selectionPath[0].context.layoutItem.type === 'line-view'; | ||||
|                             }), | ||||
|                             property: function (selectionPath) { | ||||
|                                 return getPath(selectionPath) + ".y2"; | ||||
|                             }, | ||||
|                             label: "Y2:", | ||||
|                             title: "Y2 position", | ||||
|                         }; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 function getTextSizeMenu(selectedParent, selection) { | ||||
|                     }); | ||||
|                     toolbar.push(separator); | ||||
|                     toolbar.push(stackOrder); | ||||
|                     toolbar.push(x); | ||||
|                     toolbar.push(y); | ||||
|                     toolbar.push(width); | ||||
|                     toolbar.push(height); | ||||
|                     toolbar.push(useGrid); | ||||
|                     toolbar.push(separator); | ||||
|                     toolbar.push(remove); | ||||
|                 } else { | ||||
|                     const TEXT_SIZE = [8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 30, 36, 48, 72, 96, 128]; | ||||
|                     return { | ||||
|                     let fill = { | ||||
|                         control: "color-picker", | ||||
|                         domainObject: selectedParent, | ||||
|                         property: function () { | ||||
|                             return getPath() + ".fill"; | ||||
|                         }, | ||||
|                         icon: "icon-paint-bucket", | ||||
|                         title: "Set fill color" | ||||
|                     }, | ||||
|                     stroke = { | ||||
|                         control: "color-picker", | ||||
|                         domainObject: selectedParent, | ||||
|                         property: function () { | ||||
|                             return getPath() + ".stroke"; | ||||
|                         }, | ||||
|                         icon: "icon-line-horz", | ||||
|                         title: "Set border color" | ||||
|                     }, | ||||
|                     color = { | ||||
|                         control: "color-picker", | ||||
|                         domainObject: selectedParent, | ||||
|                         property: function () { | ||||
|                             return getPath() + ".color"; | ||||
|                         }, | ||||
|                         icon: "icon-font", | ||||
|                         mandatory: true, | ||||
|                         title: "Set text color", | ||||
|                         preventNone: true | ||||
|                     }, | ||||
|                     size = { | ||||
|                         control: "select-menu", | ||||
|                         domainObject: selectedParent, | ||||
|                         applicableSelectedItems: selection.filter(selectionPath => { | ||||
|                             let type = selectionPath[0].context.layoutItem.type; | ||||
|                             return type === 'text-view' || type === 'telemetry-view'; | ||||
|                         }), | ||||
|                         property: function (selectionPath) { | ||||
|                             return getPath(selectionPath) + ".size"; | ||||
|                         property: function () { | ||||
|                             return getPath() + ".size"; | ||||
|                         }, | ||||
|                         title: "Set text size", | ||||
|                         options: TEXT_SIZE.map(size => { | ||||
| @@ -346,128 +324,13 @@ define([], function () { | ||||
|                             }; | ||||
|                         }) | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 function getFillMenu(selectedParent, selection) { | ||||
|                     return { | ||||
|                         control: "color-picker", | ||||
|                         domainObject: selectedParent, | ||||
|                         applicableSelectedItems: selection.filter(selectionPath => { | ||||
|                             let type = selectionPath[0].context.layoutItem.type; | ||||
|                             return type === 'text-view' || | ||||
|                                 type === 'telemetry-view' || | ||||
|                                 type === 'box-view'; | ||||
|                         }), | ||||
|                         property: function (selectionPath) { | ||||
|                             return getPath(selectionPath) + ".fill"; | ||||
|                         }, | ||||
|                         icon: "icon-paint-bucket", | ||||
|                         title: "Set fill color" | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 function getStrokeMenu(selectedParent, selection) { | ||||
|                     return { | ||||
|                         control: "color-picker", | ||||
|                         domainObject: selectedParent, | ||||
|                         applicableSelectedItems: selection.filter(selectionPath => { | ||||
|                             let type = selectionPath[0].context.layoutItem.type; | ||||
|                             return type === 'text-view' || | ||||
|                                 type === 'telemetry-view' || | ||||
|                                 type === 'box-view' || | ||||
|                                 type === 'image-view' || | ||||
|                                 type === 'line-view'; | ||||
|                         }), | ||||
|                         property: function (selectionPath) { | ||||
|                             return getPath(selectionPath) + ".stroke"; | ||||
|                         }, | ||||
|                         icon: "icon-line-horz", | ||||
|                         title: "Set border color" | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 function getTextColorMenu(selectedParent, selection) { | ||||
|                     return { | ||||
|                         control: "color-picker", | ||||
|                         domainObject: selectedParent, | ||||
|                         applicableSelectedItems: selection.filter(selectionPath => { | ||||
|                             let type = selectionPath[0].context.layoutItem.type; | ||||
|                             return type === 'text-view' || type === 'telemetry-view'; | ||||
|                         }), | ||||
|                         property: function (selectionPath) { | ||||
|                             return getPath(selectionPath) + ".color"; | ||||
|                         }, | ||||
|                         icon: "icon-font", | ||||
|                         mandatory: true, | ||||
|                         title: "Set text color", | ||||
|                         preventNone: true | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 function getURLButton(selectedParent, selection) { | ||||
|                     return { | ||||
|                         control: "button", | ||||
|                         domainObject: selectedParent, | ||||
|                         applicableSelectedItems: selection.filter(selectionPath => { | ||||
|                             return selectionPath[0].context.layoutItem.type === 'image-view'; | ||||
|                         }), | ||||
|                         property: function (selectionPath) { | ||||
|                             return getPath(selectionPath); | ||||
|                         }, | ||||
|                         icon: "icon-image", | ||||
|                         title: "Edit image properties", | ||||
|                         dialog: DIALOG_FORM['image'] | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 function getTextButton(selectedParent, selection) { | ||||
|                         return { | ||||
|                             control: "button", | ||||
|                             domainObject: selectedParent, | ||||
|                             applicableSelectedItems: selection.filter(selectionPath => { | ||||
|                                 return selectionPath[0].context.layoutItem.type === 'text-view'; | ||||
|                             }), | ||||
|                             property: function (selectionPath) { | ||||
|                                 return getPath(selectionPath); | ||||
|                             }, | ||||
|                             icon: "icon-gear", | ||||
|                             title: "Edit text properties", | ||||
|                             dialog: DIALOG_FORM['text'] | ||||
|                         }; | ||||
|                 } | ||||
|  | ||||
|                 function getTelemetryValueMenu(selectionPath, selection) { | ||||
|                     if (selection.length === 1) { | ||||
|                         return { | ||||
|                             control: "select-menu", | ||||
|                             domainObject: selectionPath[1].context.item, | ||||
|                             applicableSelectedItems: selection.filter(selectionPath => { | ||||
|                                 return selectionPath[0].context.layoutItem.type === 'telemetry-view'; | ||||
|                             }), | ||||
|                             property: function (selectionPath) { | ||||
|                                 return getPath(selectionPath) + ".value"; | ||||
|                             }, | ||||
|                             title: "Set value", | ||||
|                             options: openmct.telemetry.getMetadata(selectionPath[0].context.item).values().map(value => { | ||||
|                                 return { | ||||
|                                     name: value.name, | ||||
|                                     value: value.key | ||||
|                                 } | ||||
|                             }) | ||||
|                         }; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 function getDisplayModeMenu(selectedParent, selection) { | ||||
|                     if (selection.length === 1) { | ||||
|                         return { | ||||
|                     if (layoutItem.type === 'telemetry-view') { | ||||
|                         let displayMode = { | ||||
|                             control: "select-menu", | ||||
|                             domainObject: selectedParent, | ||||
|                             applicableSelectedItems: selection.filter(selectionPath => { | ||||
|                                 return selectionPath[0].context.layoutItem.type === 'telemetry-view'; | ||||
|                             }), | ||||
|                             property: function (selectionPath) { | ||||
|                                 return getPath(selectionPath) + ".displayMode"; | ||||
|                             property: function () { | ||||
|                                 return getPath() + ".displayMode"; | ||||
|                             }, | ||||
|                             title: "Set display mode", | ||||
|                             options: [ | ||||
| @@ -484,196 +347,146 @@ define([], function () { | ||||
|                                     value: "value" | ||||
|                                 } | ||||
|                             ] | ||||
|                         }, | ||||
|                         value = { | ||||
|                             control: "select-menu", | ||||
|                             domainObject: selectedParent, | ||||
|                             property: function () { | ||||
|                                 return getPath() + ".value"; | ||||
|                             }, | ||||
|                             title: "Set value", | ||||
|                             options: openmct.telemetry.getMetadata(selectedObject).values().map(value => { | ||||
|                                 return { | ||||
|                                     name: value.name, | ||||
|                                     value: value.key | ||||
|                                 } | ||||
|                             }) | ||||
|                         }; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 function getSeparator() { | ||||
|                     return { | ||||
|                         control: "separator" | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 function isMainLayoutSelected(selectionPath) { | ||||
|                     let selectedObject = selectionPath[0].context.item; | ||||
|                     return selectedObject && selectedObject.type === 'layout' && | ||||
|                         !selectionPath[0].context.layoutItem; | ||||
|                 } | ||||
|  | ||||
|                 if (isMainLayoutSelected(selection[0])) { | ||||
|                     return [getAddButton(selection)]; | ||||
|                 } | ||||
|  | ||||
|                 let toolbar = { | ||||
|                     'add-menu': [], | ||||
|                     'toggle-frame': [], | ||||
|                     'display-mode': [], | ||||
|                     'telemetry-value': [], | ||||
|                     'style': [], | ||||
|                     'text-style': [], | ||||
|                     'position': [], | ||||
|                     'text': [], | ||||
|                     'url': [], | ||||
|                     'remove': [], | ||||
|                 }; | ||||
|  | ||||
|                 selection.forEach(selectionPath => { | ||||
|                     let selectedParent = selectionPath[1].context.item; | ||||
|                     let layoutItem = selectionPath[0].context.layoutItem; | ||||
|  | ||||
|                     if (layoutItem.type === 'subobject-view') { | ||||
|                         if (toolbar['add-menu'].length === 0 && selectionPath[0].context.item.type === 'layout') { | ||||
|                             toolbar['add-menu'] = [getAddButton(selection, selectionPath)]; | ||||
|                         } | ||||
|                         if (toolbar['toggle-frame'].length === 0) { | ||||
|                             toolbar['toggle-frame'] = [getToggleFrameButton(selectedParent, selection)]; | ||||
|                         } | ||||
|                         if (toolbar['position'].length === 0) { | ||||
|                             toolbar['position'] = [ | ||||
|                                 getStackOrder(selectedParent, selectionPath), | ||||
|                                 getXInput(selectedParent, selection), | ||||
|                                 getYInput(selectedParent, selection), | ||||
|                                 getHeightInput(selectedParent, selection), | ||||
|                                 getWidthInput(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['remove'].length === 0) { | ||||
|                             toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)]; | ||||
|                         } | ||||
|                     } else if (layoutItem.type === 'telemetry-view') { | ||||
|                         if (toolbar['display-mode'].length === 0) { | ||||
|                             toolbar['display-mode'] = [getDisplayModeMenu(selectedParent, selection)]; | ||||
|                         } | ||||
|                         if (toolbar['telemetry-value'].length === 0) { | ||||
|                             toolbar['telemetry-value'] = [getTelemetryValueMenu(selectionPath, selection)]; | ||||
|                         } | ||||
|                         if (toolbar['style'].length < 2) { | ||||
|                             toolbar['style'] = [ | ||||
|                                 getFillMenu(selectedParent, selection), | ||||
|                                 getStrokeMenu(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['text-style'].length === 0) { | ||||
|                             toolbar['text-style'] = [ | ||||
|                                 getTextColorMenu(selectedParent, selection), | ||||
|                                 getTextSizeMenu(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['position'].length === 0) { | ||||
|                             toolbar['position'] = [ | ||||
|                                 getStackOrder(selectedParent, selectionPath), | ||||
|                                 getXInput(selectedParent, selection), | ||||
|                                 getYInput(selectedParent, selection), | ||||
|                                 getHeightInput(selectedParent, selection), | ||||
|                                 getWidthInput(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['remove'].length === 0) { | ||||
|                             toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)]; | ||||
|                         } | ||||
|                         toolbar = [ | ||||
|                             displayMode, | ||||
|                             separator, | ||||
|                             value, | ||||
|                             separator, | ||||
|                             fill, | ||||
|                             stroke, | ||||
|                             color, | ||||
|                             separator, | ||||
|                             size, | ||||
|                             separator, | ||||
|                             stackOrder, | ||||
|                             x, | ||||
|                             y, | ||||
|                             height, | ||||
|                             width, | ||||
|                             useGrid, | ||||
|                             separator, | ||||
|                             remove | ||||
|                         ]; | ||||
|                     } else if (layoutItem.type === 'text-view') { | ||||
|                         if (toolbar['style'].length < 2) { | ||||
|                             toolbar['style'] = [ | ||||
|                                 getFillMenu(selectedParent, selection), | ||||
|                                 getStrokeMenu(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['text-style'].length === 0) { | ||||
|                             toolbar['text-style'] = [ | ||||
|                                 getTextColorMenu(selectedParent, selection), | ||||
|                                 getTextSizeMenu(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['position'].length === 0) { | ||||
|                             toolbar['position'] = [ | ||||
|                                 getStackOrder(selectedParent, selectionPath), | ||||
|                                 getXInput(selectedParent, selection), | ||||
|                                 getYInput(selectedParent, selection), | ||||
|                                 getHeightInput(selectedParent, selection), | ||||
|                                 getWidthInput(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['text'].length === 0) { | ||||
|                             toolbar['text'] = [getTextButton(selectedParent, selection)]; | ||||
|                         } | ||||
|                         if (toolbar['remove'].length === 0) { | ||||
|                             toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)]; | ||||
|                         } | ||||
|                         let text = { | ||||
|                             control: "button", | ||||
|                             domainObject: selectedParent, | ||||
|                             property: function () { | ||||
|                                 return getPath(); | ||||
|                             }, | ||||
|                             icon: "icon-gear", | ||||
|                             title: "Edit text properties", | ||||
|                             dialog: DIALOG_FORM['text'] | ||||
|                         }; | ||||
|                         toolbar = [ | ||||
|                             fill, | ||||
|                             stroke, | ||||
|                             separator, | ||||
|                             color, | ||||
|                             size, | ||||
|                             separator, | ||||
|                             stackOrder, | ||||
|                             x, | ||||
|                             y, | ||||
|                             height, | ||||
|                             width, | ||||
|                             useGrid, | ||||
|                             separator, | ||||
|                             text, | ||||
|                             separator, | ||||
|                             remove | ||||
|                         ]; | ||||
|                     } else if (layoutItem.type === 'box-view') { | ||||
|                         if (toolbar['style'].length < 2) { | ||||
|                             toolbar['style'] = [ | ||||
|                                 getFillMenu(selectedParent, selection), | ||||
|                                 getStrokeMenu(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['position'].length === 0) { | ||||
|                             toolbar['position'] = [ | ||||
|                                 getStackOrder(selectedParent, selectionPath), | ||||
|                                 getXInput(selectedParent, selection), | ||||
|                                 getYInput(selectedParent, selection), | ||||
|                                 getHeightInput(selectedParent, selection), | ||||
|                                 getWidthInput(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['remove'].length === 0) { | ||||
|                             toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)]; | ||||
|                         } | ||||
|                         toolbar = [ | ||||
|                             fill, | ||||
|                             stroke, | ||||
|                             separator, | ||||
|                             stackOrder, | ||||
|                             x, | ||||
|                             y, | ||||
|                             height, | ||||
|                             width, | ||||
|                             useGrid, | ||||
|                             separator, | ||||
|                             remove | ||||
|                         ]; | ||||
|                     } else if (layoutItem.type === 'image-view') { | ||||
|                         if (toolbar['style'].length === 0) { | ||||
|                             toolbar['style'] = [ | ||||
|                                 getStrokeMenu(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['position'].length === 0) { | ||||
|                             toolbar['position'] = [ | ||||
|                                 getStackOrder(selectedParent, selectionPath), | ||||
|                                 getXInput(selectedParent, selection), | ||||
|                                 getYInput(selectedParent, selection), | ||||
|                                 getHeightInput(selectedParent, selection), | ||||
|                                 getWidthInput(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['url'].length === 0) { | ||||
|                             toolbar['url'] = [getURLButton(selectedParent, selection)]; | ||||
|                         } | ||||
|                         if (toolbar['remove'].length === 0) { | ||||
|                             toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)]; | ||||
|                         } | ||||
|                         let url = { | ||||
|                             control: "button", | ||||
|                             domainObject: selectedParent, | ||||
|                             property: function () { | ||||
|                                 return getPath(); | ||||
|                             }, | ||||
|                             icon: "icon-image", | ||||
|                             title: "Edit image properties", | ||||
|                             dialog: DIALOG_FORM['image'] | ||||
|                         }; | ||||
|                         toolbar = [ | ||||
|                             stroke, | ||||
|                             separator, | ||||
|                             stackOrder, | ||||
|                             x, | ||||
|                             y, | ||||
|                             height, | ||||
|                             width, | ||||
|                             useGrid, | ||||
|                             separator, | ||||
|                             url, | ||||
|                             separator, | ||||
|                             remove | ||||
|                         ]; | ||||
|                     } else if (layoutItem.type === 'line-view') { | ||||
|                         if (toolbar['style'].length === 0) { | ||||
|                             toolbar['style'] = [ | ||||
|                                 getStrokeMenu(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['position'].length === 0) { | ||||
|                             toolbar['position'] = [ | ||||
|                                 getStackOrder(selectedParent, selectionPath), | ||||
|                                 getXInput(selectedParent, selection), | ||||
|                                 getYInput(selectedParent, selection), | ||||
|                                 getX2Input(selectedParent, selection), | ||||
|                                 getY2Input(selectedParent, selection) | ||||
|                             ]; | ||||
|                         } | ||||
|                         if (toolbar['remove'].length === 0) { | ||||
|                             toolbar['remove'] = [getRemoveButton(selectedParent, selectionPath, selection)]; | ||||
|                         } | ||||
|                         let x2 = { | ||||
|                             control: "input", | ||||
|                             type: "number", | ||||
|                             domainObject: selectedParent, | ||||
|                             property: function () { | ||||
|                                 return getPath() + ".x2"; | ||||
|                             }, | ||||
|                             label: "X2:", | ||||
|                             title: "X2 position" | ||||
|                         }, | ||||
|                         y2 = { | ||||
|                             control: "input", | ||||
|                             type: "number", | ||||
|                             domainObject: selectedParent, | ||||
|                             property: function () { | ||||
|                                 return getPath() + ".y2"; | ||||
|                             }, | ||||
|                             label: "Y2:", | ||||
|                             title: "Y2 position", | ||||
|                         }; | ||||
|                         toolbar = [ | ||||
|                             stroke, | ||||
|                             separator, | ||||
|                             stackOrder, | ||||
|                             x, | ||||
|                             y, | ||||
|                             x2, | ||||
|                             y2, | ||||
|                             useGrid, | ||||
|                             separator, | ||||
|                             remove | ||||
|                         ]; | ||||
|                     } | ||||
|                 }); | ||||
|                 } | ||||
|  | ||||
|                 let toolbarArray = Object.values(toolbar); | ||||
|                 return _.flatten(toolbarArray.reduce((accumulator, group, index) => { | ||||
|                     group = group.filter(control => control !== undefined); | ||||
|  | ||||
|                     if (group.length > 0) { | ||||
|                         accumulator.push(group); | ||||
|  | ||||
|                         if (index < toolbarArray.length - 1) { | ||||
|                             accumulator.push(getSeparator()); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     return accumulator; | ||||
|                 }, [])); | ||||
|                 return toolbar; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -95,7 +95,7 @@ define( | ||||
|          * @param {number[]} pixelDelta the offset from the | ||||
|          *        original position, in pixels | ||||
|          */ | ||||
|         LayoutDrag.prototype.getAdjustedPositionAndDimensions = function (pixelDelta) { | ||||
|         LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) { | ||||
|             var gridDelta = toGridDelta(this.gridSize, pixelDelta); | ||||
|             return { | ||||
|                 position: max(add( | ||||
| @@ -109,16 +109,6 @@ define( | ||||
|             }; | ||||
|         }; | ||||
|  | ||||
|         LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) { | ||||
|             var gridDelta = toGridDelta(this.gridSize, pixelDelta); | ||||
|             return { | ||||
|                 position: max(add( | ||||
|                     this.rawPosition.position, | ||||
|                     multiply(gridDelta, this.posFactor) | ||||
|                 ), [0, 0]) | ||||
|             }; | ||||
|         }; | ||||
|  | ||||
|         return LayoutDrag; | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -1,90 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, 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-properties" v-if="isEditing"> | ||||
|         <div class="c-properties__header">Alphanumeric Format</div> | ||||
|         <ul class="c-properties__section"> | ||||
|             <li class="c-properties__row"> | ||||
|                 <div class="c-properties__label" title="Printf formatting for the selected telemetry"> | ||||
|                     <label for="telemetryPrintfFormat">Format</label> | ||||
|                 </div> | ||||
|                 <div class="c-properties__value"> | ||||
|                     <input id="telemetryPrintfFormat" | ||||
|                         type="text" | ||||
|                         @change="formatTelemetry" | ||||
|                         :value="telemetryFormat" | ||||
|                         :placeholder="nonMixedFormat ? '' : 'Mixed'" | ||||
|                     > | ||||
|                 </div> | ||||
|             </li> | ||||
|         </ul> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|     export default { | ||||
|         inject: ['openmct'], | ||||
|         data() { | ||||
|             let selectionPath = this.openmct.selection.get()[0]; | ||||
|             return { | ||||
|                 isEditing: this.openmct.editor.isEditing(), | ||||
|                 telemetryFormat: undefined, | ||||
|                 nonMixedFormat: false | ||||
|             } | ||||
|         }, | ||||
|         methods: { | ||||
|             toggleEdit(isEditing) { | ||||
|                 this.isEditing = isEditing; | ||||
|             }, | ||||
|             formatTelemetry(event) { | ||||
|                 let newFormat = event.currentTarget.value; | ||||
|                 this.openmct.selection.get().forEach(selectionPath => { | ||||
|                     selectionPath[0].context.updateTelemetryFormat(newFormat);     | ||||
|                 }); | ||||
|                 this.telemetryFormat = newFormat; | ||||
|             }, | ||||
|             handleSelection(selection) { | ||||
|                 if (selection.length === 0 || selection[0].length < 2) { | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 let format = selection[0][0].context.layoutItem.format; | ||||
|                 this.nonMixedFormat = selection.every(selectionPath => { | ||||
|                     return selectionPath[0].context.layoutItem.format === format; | ||||
|                 }); | ||||
|  | ||||
|                 this.telemetryFormat = this.nonMixedFormat ? format : ''; | ||||
|             } | ||||
|         }, | ||||
|         mounted() { | ||||
|             this.openmct.editor.on('isEditing', this.toggleEdit); | ||||
|             this.openmct.selection.on('change', this.handleSelection); | ||||
|             this.handleSelection(this.openmct.selection.get()); | ||||
|         }, | ||||
|         destroyed() { | ||||
|             this.openmct.editor.off('isEditing', this.toggleEdit); | ||||
|             this.openmct.selection.off('change', this.handleSelection); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| </script> | ||||
| @@ -23,8 +23,7 @@ | ||||
| <template> | ||||
|     <layout-frame :item="item" | ||||
|                   :grid-size="gridSize" | ||||
|                   @move="(gridDelta) => $emit('move', gridDelta)" | ||||
|                   @endMove="() => $emit('endMove')"> | ||||
|                   @endDrag="(item, updates) => $emit('endDrag', item, updates)"> | ||||
|         <div class="c-box-view" | ||||
|              :style="style"> | ||||
|         </div> | ||||
| @@ -55,7 +54,8 @@ | ||||
|                 x: 1, | ||||
|                 y: 1, | ||||
|                 width: 10,  | ||||
|                 height: 5 | ||||
|                 height: 5, | ||||
|                 useGrid: true | ||||
|             }; | ||||
|         }, | ||||
|         inject: ['openmct'], | ||||
|   | ||||
| @@ -23,11 +23,8 @@ | ||||
| <template> | ||||
|     <div class="l-layout" | ||||
|          @dragover="handleDragOver" | ||||
|          @click.capture="bypassSelection" | ||||
|          @drop="handleDrop" | ||||
|          :class="{ | ||||
|             'is-multi-selected': selectedLayoutItems.length > 1 | ||||
|             }"> | ||||
|          @click="bypassSelection" | ||||
|          @drop="handleDrop"> | ||||
|         <!-- Background grid --> | ||||
|         <div class="l-layout__grid-holder c-grid"> | ||||
|             <div class="c-grid__x l-grid l-grid-x" | ||||
| @@ -42,39 +39,18 @@ | ||||
|                    :is="item.type" | ||||
|                    :item="item" | ||||
|                    :key="item.id" | ||||
|                    :gridSize="gridSize" | ||||
|                    :gridSize="item.useGrid ? gridSize : [1, 1]" | ||||
|                    :initSelect="initSelectIndex === index" | ||||
|                    :index="index" | ||||
|                    :multiSelect="selectedLayoutItems.length > 1" | ||||
|                    @move="move" | ||||
|                    @endMove="endMove" | ||||
|                    @endLineResize='endLineResize' | ||||
|                    @formatChanged='updateTelemetryFormat'> | ||||
|                    @endDrag="endDrag" | ||||
|         > | ||||
|         </component> | ||||
|         <edit-marquee v-if='showMarquee' | ||||
|                       :gridSize="gridSize" | ||||
|                       :selectedLayoutItems="selectedLayoutItems" | ||||
|                       @endResize="endResize"> | ||||
|         </edit-marquee> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <style lang="scss"> | ||||
|     @import "~styles/sass-base"; | ||||
|  | ||||
|     @mixin displayMarquee($c) { | ||||
|         > .c-frame-edit { | ||||
|             // All other frames | ||||
|             //@include test($c, 0.4); | ||||
|             display: block; | ||||
|         } | ||||
|         > .c-frame > .c-frame-edit { | ||||
|             // Line object frame | ||||
|             //@include test($c, 0.4); | ||||
|             display: block; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .l-layout { | ||||
|         @include abs(); | ||||
|         display: flex; | ||||
| @@ -94,7 +70,7 @@ | ||||
|         .l-shell__main-container { | ||||
|             &[s-selected], | ||||
|             &[s-selected-parent] { | ||||
|                 // Display grid and allow edit marquee to display in main layout holder when editing | ||||
|                 // Display grid in main layout holder when editing | ||||
|                 > .l-layout { | ||||
|                     background: $editUIGridColorBg; | ||||
|  | ||||
| @@ -108,7 +84,7 @@ | ||||
|         .l-layout__frame { | ||||
|             &[s-selected], | ||||
|             &[s-selected-parent] { | ||||
|                 // Display grid and allow edit marquee to display in nested layouts when editing | ||||
|                 // Display grid in nested layouts when editing | ||||
|                 > * > * > .l-layout { | ||||
|                     background: $editUIGridColorBg; | ||||
|                     box-shadow: inset $editUIGridColorFg 0 0 2px 1px; | ||||
| @@ -119,21 +95,10 @@ | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /*********************** EDIT MARQUEE CONTROL */ | ||||
|         *[s-selected-parent] { | ||||
|             > .l-layout { | ||||
|                 // When main shell layout is the parent | ||||
|                 @include displayMarquee(deeppink); | ||||
|             } | ||||
|             > * > * > * { | ||||
|                 // When a sub-layout is the parent | ||||
|                 @include displayMarquee(blue); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
|  | ||||
|  | ||||
| <script> | ||||
|     import uuid from 'uuid'; | ||||
|  | ||||
| @@ -143,7 +108,6 @@ | ||||
|     import TextView from './TextView.vue' | ||||
|     import LineView from './LineView.vue' | ||||
|     import ImageView from './ImageView.vue' | ||||
|     import EditMarquee from './EditMarquee.vue' | ||||
|  | ||||
|     const ITEM_TYPE_VIEW_MAP = { | ||||
|         'subobject-view': SubobjectView, | ||||
| @@ -159,10 +123,8 @@ | ||||
|         down: -1, | ||||
|         bottom: Number.NEGATIVE_INFINITY | ||||
|     }; | ||||
|     const DRAG_OBJECT_TRANSFER_PREFIX = 'openmct/domain-object/'; | ||||
|  | ||||
|     let components = ITEM_TYPE_VIEW_MAP; | ||||
|     components['edit-marquee'] = EditMarquee; | ||||
|     const DRAG_OBJECT_TRANSFER_PREFIX = 'openmct/domain-object/'; | ||||
|  | ||||
|     function getItemDefinition(itemType, ...options) { | ||||
|         let itemView = ITEM_TYPE_VIEW_MAP[itemType]; | ||||
| @@ -179,8 +141,7 @@ | ||||
|             let domainObject = JSON.parse(JSON.stringify(this.domainObject)); | ||||
|             return { | ||||
|                 internalDomainObject: domainObject, | ||||
|                 initSelectIndex: undefined, | ||||
|                 selection: [] | ||||
|                 initSelectIndex: undefined | ||||
|             }; | ||||
|         }, | ||||
|         computed: { | ||||
| @@ -189,145 +150,82 @@ | ||||
|             }, | ||||
|             layoutItems() { | ||||
|                 return this.internalDomainObject.configuration.items; | ||||
|             }, | ||||
|             selectedLayoutItems() { | ||||
|                 return this.layoutItems.filter(item => { | ||||
|                     return this.itemIsInCurrentSelection(item); | ||||
|                 }); | ||||
|             }, | ||||
|             showMarquee() { | ||||
|                 let selectionPath = this.selection[0]; | ||||
|                 let singleSelectedLine = this.selection.length === 1 && | ||||
|                     selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.type === 'line-view'; | ||||
|                 return selectionPath && selectionPath.length > 1 && !singleSelectedLine; | ||||
|             } | ||||
|         }, | ||||
|         inject: ['openmct', 'options'], | ||||
|         inject: ['openmct'], | ||||
|         props: ['domainObject'], | ||||
|         components: components, | ||||
|         components: ITEM_TYPE_VIEW_MAP, | ||||
|         methods: { | ||||
|             addElement(itemType, element) { | ||||
|                 this.addItem(itemType + '-view', element); | ||||
|             }, | ||||
|             setSelection(selection) { | ||||
|                 this.selection = selection; | ||||
|                 if (selection.length === 0) { | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 if (this.removeSelectionListener) { | ||||
|                     this.removeSelectionListener(); | ||||
|                 } | ||||
|  | ||||
|                 let itemIndex = selection[0].context.index; | ||||
|  | ||||
|                 if (itemIndex !== undefined) { | ||||
|                     this.attachSelectionListener(itemIndex); | ||||
|                 } | ||||
|             }, | ||||
|             itemIsInCurrentSelection(item) { | ||||
|                 return this.selection.some(selectionPath => | ||||
|                     selectionPath[0].context.layoutItem && selectionPath[0].context.layoutItem.id === item.id); | ||||
|             attachSelectionListener(index) { | ||||
|                 let path = `configuration.items[${index}].useGrid`; | ||||
|                 this.removeSelectionListener = this.openmct.objects.observe(this.internalDomainObject, path, function (value) { | ||||
|                     let item = this.layoutItems[index]; | ||||
|  | ||||
|                     if (value) { | ||||
|                         item.x = Math.round(item.x / this.gridSize[0]); | ||||
|                         item.y = Math.round(item.y / this.gridSize[1]); | ||||
|                         item.width = Math.round(item.width / this.gridSize[0]); | ||||
|                         item.height = Math.round(item.height / this.gridSize[1]); | ||||
|  | ||||
|                         if (item.x2) { | ||||
|                             item.x2 = Math.round(item.x2 / this.gridSize[0]); | ||||
|                         } | ||||
|                         if (item.y2) { | ||||
|                             item.y2 = Math.round(item.y2 / this.gridSize[1]); | ||||
|                         } | ||||
|                     } else { | ||||
|                         item.x = this.gridSize[0] * item.x; | ||||
|                         item.y = this.gridSize[1] * item.y; | ||||
|                         item.width = this.gridSize[0] * item.width; | ||||
|                         item.height = this.gridSize[1] * item.height; | ||||
|  | ||||
|                         if (item.x2) { | ||||
|                             item.x2 = this.gridSize[0] * item.x2; | ||||
|                         } | ||||
|                         if (item.y2) { | ||||
|                             item.y2 = this.gridSize[1] * item.y2; | ||||
|                         } | ||||
|                     } | ||||
|                     item.useGrid = value; | ||||
|                     this.mutate(`configuration.items[${index}]`, item); | ||||
|                 }.bind(this)); | ||||
|             }, | ||||
|             bypassSelection($event) { | ||||
|                 if (this.dragInProgress) { | ||||
|                     if ($event) { | ||||
|                         $event.stopImmediatePropagation(); | ||||
|                     } | ||||
|                     this.dragInProgress = false; | ||||
|                     return; | ||||
|                 } | ||||
|             }, | ||||
|             endLineResize(item, updates) { | ||||
|             endDrag(item, updates) { | ||||
|                 this.dragInProgress = true; | ||||
|                 setTimeout(function () { | ||||
|                     this.dragInProgress = false; | ||||
|                 }.bind(this), 0); | ||||
|  | ||||
|                 let index = this.layoutItems.indexOf(item); | ||||
|                 Object.assign(item, updates); | ||||
|                 this.mutate(`configuration.items[${index}]`, item); | ||||
|             }, | ||||
|             endResize(scaleWidth, scaleHeight, marqueeStart, marqueeOffset) { | ||||
|                 this.dragInProgress = true; | ||||
|                 this.layoutItems.forEach(item => { | ||||
|                     if (this.itemIsInCurrentSelection(item)) { | ||||
|                         let itemXInMarqueeSpace = item.x - marqueeStart.x; | ||||
|                         let itemXInMarqueeSpaceAfterScale = Math.round(itemXInMarqueeSpace * scaleWidth); | ||||
|                         item.x = itemXInMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x; | ||||
|  | ||||
|                         let itemYInMarqueeSpace = item.y - marqueeStart.y; | ||||
|                         let itemYInMarqueeSpaceAfterScale = Math.round(itemYInMarqueeSpace * scaleHeight); | ||||
|                         item.y = itemYInMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y; | ||||
|  | ||||
|                         if (item.x2) { | ||||
|                             let itemX2InMarqueeSpace = item.x2 - marqueeStart.x; | ||||
|                             let itemX2InMarqueeSpaceAfterScale = Math.round(itemX2InMarqueeSpace * scaleWidth); | ||||
|                             item.x2 = itemX2InMarqueeSpaceAfterScale + marqueeOffset.x + marqueeStart.x; | ||||
|                         } else { | ||||
|                             item.width = Math.round(item.width * scaleWidth); | ||||
|                         } | ||||
|  | ||||
|                         if (item.y2) { | ||||
|                             let itemY2InMarqueeSpace = item.y2 - marqueeStart.y; | ||||
|                             let itemY2InMarqueeSpaceAfterScale = Math.round(itemY2InMarqueeSpace * scaleHeight); | ||||
|                             item.y2 = itemY2InMarqueeSpaceAfterScale + marqueeOffset.y + marqueeStart.y; | ||||
|                         } else { | ||||
|                             item.height = Math.round(item.height * scaleHeight); | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|                 this.mutate("configuration.items", this.layoutItems); | ||||
|             }, | ||||
|             move(gridDelta) { | ||||
|                 this.dragInProgress = true; | ||||
|  | ||||
|                 if (!this.initialPositions) { | ||||
|                     this.initialPositions = {}; | ||||
|                     _.cloneDeep(this.selectedLayoutItems).forEach(selectedItem => { | ||||
|                         if (selectedItem.type === 'line-view') { | ||||
|                             this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y, selectedItem.x2, selectedItem.y2]; | ||||
|                             this.startingMinX2 = this.startingMinX2 !== undefined ? Math.min(this.startingMinX2, selectedItem.x2) : selectedItem.x2; | ||||
|                             this.startingMinY2 = this.startingMinY2 !== undefined ? Math.min(this.startingMinY2, selectedItem.y2) : selectedItem.y2; | ||||
|                         } else { | ||||
|                             this.initialPositions[selectedItem.id] = [selectedItem.x, selectedItem.y]; | ||||
|                         } | ||||
|  | ||||
|                         this.startingMinX = this.startingMinX !== undefined ? Math.min(this.startingMinX, selectedItem.x) : selectedItem.x; | ||||
|                         this.startingMinY = this.startingMinY !== undefined ? Math.min(this.startingMinY, selectedItem.y) : selectedItem.y; | ||||
|                     }); | ||||
|                 } | ||||
|  | ||||
|                 let layoutItems = this.layoutItems.map(item => { | ||||
|                     if (this.initialPositions[item.id]) { | ||||
|                         this.updateItemPosition(item, gridDelta); | ||||
|                     } | ||||
|                     return item; | ||||
|                 }); | ||||
|             }, | ||||
|             updateItemPosition(item, gridDelta) { | ||||
|                 let startingPosition = this.initialPositions[item.id]; | ||||
|                 let [startingX, startingY, startingX2, startingY2] = startingPosition; | ||||
|  | ||||
|                 if (this.startingMinX + gridDelta[0] >= 0) { | ||||
|                     if (item.x2 !== undefined) { | ||||
|                         if (this.startingMinX2 + gridDelta[0] >= 0) { | ||||
|                             item.x = startingX + gridDelta[0]; | ||||
|                         } | ||||
|                     } else { | ||||
|                         item.x = startingX + gridDelta[0]; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (this.startingMinY + gridDelta[1] >= 0) { | ||||
|                     if (item.y2 !== undefined) { | ||||
|                         if (this.startingMinY2 + gridDelta[1] >= 0) { | ||||
|                             item.y = startingY + gridDelta[1]; | ||||
|                         } | ||||
|                     } else { | ||||
|                         item.y = startingY + gridDelta[1]; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 if (item.x2 !== undefined && this.startingMinX2 + gridDelta[0] >= 0 && this.startingMinX + gridDelta[0] >= 0) { | ||||
|                     item.x2 = startingX2 + gridDelta[0]; | ||||
|                 } | ||||
|  | ||||
|                 if (item.y2 !== undefined && this.startingMinY2 + gridDelta[1] >= 0 && this.startingMinY + gridDelta[1] >= 0) { | ||||
|                     item.y2 = startingY2 + gridDelta[1]; | ||||
|                 } | ||||
|             }, | ||||
|             endMove() { | ||||
|                 this.mutate('configuration.items', this.layoutItems); | ||||
|                 this.initialPositions = undefined; | ||||
|                 this.startingMinX = undefined; | ||||
|                 this.startingMinY = undefined; | ||||
|                 this.startingMinX2 = undefined; | ||||
|                 this.startingMinY2 = undefined; | ||||
|             }, | ||||
|             mutate(path, value) { | ||||
|                 this.openmct.objects.mutate(this.internalDomainObject, path, value); | ||||
|             }, | ||||
| @@ -385,8 +283,9 @@ | ||||
|                 } | ||||
|             }, | ||||
|             isTelemetry(domainObject) { | ||||
|                 if (this.openmct.telemetry.isTelemetryObject(domainObject) &&  | ||||
|                     !this.options.showAsView.includes(domainObject.type)) { | ||||
|                 if (this.openmct.telemetry.isTelemetryObject(domainObject) | ||||
|                     && domainObject.type !== 'summary-widget' | ||||
|                     && domainObject.type !== 'example.imagery') { | ||||
|                     return true; | ||||
|                 } else { | ||||
|                     return false; | ||||
| @@ -415,15 +314,11 @@ | ||||
|                     this.objectViewMap[keyString] = true; | ||||
|                 } | ||||
|             }, | ||||
|             removeItem(selectedItems) { | ||||
|                 let indices = []; | ||||
|             removeItem(item, index) { | ||||
|                 this.initSelectIndex = -1; | ||||
|                 selectedItems.forEach(selectedItem => { | ||||
|                     indices.push(selectedItem[0].context.index); | ||||
|                     this.untrackItem(selectedItem[0].context.layoutItem); | ||||
|                 }); | ||||
|                 _.pullAt(this.layoutItems, indices); | ||||
|                 this.layoutItems.splice(index, 1); | ||||
|                 this.mutate("configuration.items", this.layoutItems); | ||||
|                 this.untrackItem(item); | ||||
|                 this.$el.click(); | ||||
|             }, | ||||
|             untrackItem(item) { | ||||
| @@ -489,80 +384,21 @@ | ||||
|                 this.mutate("configuration.items", layoutItems); | ||||
|                 this.$el.click(); | ||||
|             }, | ||||
|             orderItem(position, selectedItems) { | ||||
|             orderItem(position, index) { | ||||
|                 let delta = ORDERS[position]; | ||||
|                 let indices = []; | ||||
|                 let newIndex = -1; | ||||
|                 let items = []; | ||||
|                 let newIndex = Math.max(Math.min(index + delta, this.layoutItems.length - 1), 0); | ||||
|                 let item = this.layoutItems[index]; | ||||
|  | ||||
|                 Object.assign(items, this.layoutItems); | ||||
|                 this.selectedLayoutItems.forEach(selectedItem => { | ||||
|                     indices.push(this.layoutItems.indexOf(selectedItem)); | ||||
|                 }); | ||||
|                 indices.sort((a, b) => a - b); | ||||
|                 if (newIndex !== index) { | ||||
|                     this.layoutItems.splice(index, 1); | ||||
|                     this.layoutItems.splice(newIndex, 0, item); | ||||
|                     this.mutate('configuration.items', this.layoutItems); | ||||
|  | ||||
|                 if (position === 'top' || position === 'up') { | ||||
|                     indices.reverse(); | ||||
|                 } | ||||
|  | ||||
|                 if (position === 'top' || position === 'bottom') { | ||||
|                     this.moveToTopOrBottom(position, indices, items, delta); | ||||
|                 } else { | ||||
|                     this.moveUpOrDown(position, indices, items, delta); | ||||
|                 } | ||||
|  | ||||
|                 this.mutate('configuration.items', this.layoutItems); | ||||
|             }, | ||||
|             moveUpOrDown(position, indices, items, delta) { | ||||
|                 let previousItemIndex = -1; | ||||
|                 let newIndex = -1; | ||||
|  | ||||
|                 indices.forEach((itemIndex, index) => { | ||||
|                     let isAdjacentItemSelected = position === 'up' ? | ||||
|                         itemIndex + 1 === previousItemIndex : | ||||
|                         itemIndex - 1 === previousItemIndex; | ||||
|  | ||||
|                     if (index > 0 && isAdjacentItemSelected) { | ||||
|                         if (position === 'up') { | ||||
|                             newIndex -= 1; | ||||
|                         } else { | ||||
|                             newIndex += 1; | ||||
|                         } | ||||
|                     } else { | ||||
|                         newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0); | ||||
|                     if (this.removeSelectionListener) { | ||||
|                         this.removeSelectionListener(); | ||||
|                         this.attachSelectionListener(newIndex); | ||||
|                     } | ||||
|  | ||||
|                     previousItemIndex = itemIndex; | ||||
|                     this.updateItemOrder(newIndex, itemIndex, items); | ||||
|                 }); | ||||
|             }, | ||||
|             moveToTopOrBottom(position, indices, items, delta) { | ||||
|                 let newIndex = -1; | ||||
|  | ||||
|                 indices.forEach((itemIndex, index) => { | ||||
|                     if (index === 0) { | ||||
|                         newIndex = Math.max(Math.min(itemIndex + delta, this.layoutItems.length - 1), 0); | ||||
|                     } else { | ||||
|                         if (position === 'top') { | ||||
|                             newIndex -= 1; | ||||
|                         } else { | ||||
|                             newIndex += 1; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     this.updateItemOrder(newIndex, itemIndex, items); | ||||
|                 }); | ||||
|             }, | ||||
|             updateItemOrder(newIndex, itemIndex, items) { | ||||
|                 if (newIndex !== itemIndex) { | ||||
|                     this.layoutItems.splice(itemIndex, 1); | ||||
|                     this.layoutItems.splice(newIndex, 0, items[itemIndex]); | ||||
|                 } | ||||
|             }, | ||||
|             updateTelemetryFormat(item, format) { | ||||
|                 let index = _.findIndex(this.layoutItems, item); | ||||
|                 item.format = format; | ||||
|                 this.mutate(`configuration.items[${index}]`, item); | ||||
|             } | ||||
|         }, | ||||
|         mounted() { | ||||
| @@ -577,10 +413,14 @@ | ||||
|             this.composition.load(); | ||||
|         }, | ||||
|         destroyed: function () { | ||||
|             this.openmct.selection.off('change', this.setSelection); | ||||
|             this.openmct.off('change', this.setSelection); | ||||
|             this.composition.off('add', this.addChild); | ||||
|             this.composition.off('remove', this.removeChild); | ||||
|             this.unlisten(); | ||||
|  | ||||
|             if (this.removeSelectionListener) { | ||||
|                 this.removeSelectionListener(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|   | ||||
| @@ -1,233 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, 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> | ||||
|         <!-- Resize handles --> | ||||
|         <div class="c-frame-edit" :style="style"> | ||||
|             <div class="c-frame-edit__handle c-frame-edit__handle--nw" | ||||
|                  @mousedown="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)"></div> | ||||
|             <div class="c-frame-edit__handle c-frame-edit__handle--sw" | ||||
|                  @mousedown="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)"></div> | ||||
|         </div> | ||||
| </template> | ||||
|  | ||||
| <style lang="scss"> | ||||
|     @import "~styles/sass-base"; | ||||
|  | ||||
|     .c-frame-edit { | ||||
|         // In Layouts, this is the editing rect and handles | ||||
|         display: none; // Set to display: block in DisplayLayout.vue | ||||
|         pointer-events: none; | ||||
|         @include abs(); | ||||
|         border: $editMarqueeBorder; | ||||
|  | ||||
|         &__handle { | ||||
|             $d: 6px; | ||||
|             $o: floor($d * -0.5); | ||||
|             background: $editFrameColorHandleFg; | ||||
|             box-shadow: $editFrameColorHandleBg 0 0 0 2px; | ||||
|             pointer-events: all; | ||||
|             position: absolute; | ||||
|             width: $d; height: $d; | ||||
|             top: auto; right: auto; bottom: auto; left: auto; | ||||
|  | ||||
|             &:before { | ||||
|                 // Extended hit area | ||||
|                 @include abs(-10px); | ||||
|                 content: ''; | ||||
|                 display: block; | ||||
|                 z-index: 0; | ||||
|             } | ||||
|  | ||||
|             &:hover { | ||||
|                 background: $editUIColor; | ||||
|             } | ||||
|  | ||||
|             &--nwse { | ||||
|                 cursor: nwse-resize; | ||||
|             } | ||||
|  | ||||
|             &--nw { | ||||
|                 cursor: nw-resize; | ||||
|                 left: $o; top: $o; | ||||
|             } | ||||
|  | ||||
|             &--ne { | ||||
|                 cursor: ne-resize; | ||||
|                 right: $o; top: $o; | ||||
|             } | ||||
|  | ||||
|             &--se { | ||||
|                 cursor: se-resize; | ||||
|                 right: $o; bottom: $o; | ||||
|             } | ||||
|  | ||||
|             &--sw { | ||||
|                 cursor: sw-resize; | ||||
|                 left: $o; bottom: $o; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
|  | ||||
|  | ||||
| <script> | ||||
|     import LayoutDrag from './../LayoutDrag' | ||||
|  | ||||
|     export default { | ||||
|         inject: ['openmct'], | ||||
|         props: { | ||||
|             selectedLayoutItems: Array, | ||||
|             gridSize: Array | ||||
|         },         | ||||
|         data() { | ||||
|             return { | ||||
|                 dragPosition: undefined | ||||
|             } | ||||
|         }, | ||||
|         computed: { | ||||
|             style() { | ||||
|                 let x = Number.POSITIVE_INFINITY; | ||||
|                 let y = Number.POSITIVE_INFINITY; | ||||
|                 let width = Number.NEGATIVE_INFINITY; | ||||
|                 let height = Number.NEGATIVE_INFINITY; | ||||
|  | ||||
|                 this.selectedLayoutItems.forEach(item => { | ||||
|                     if (item.x2 !== undefined) { | ||||
|                         let lineWidth = Math.abs(item.x - item.x2); | ||||
|                         let lineMinX = Math.min(item.x, item.x2); | ||||
|                         x = Math.min(lineMinX, x); | ||||
|                         width = Math.max(lineWidth + lineMinX, width); | ||||
|                     } else { | ||||
|                         x = Math.min(item.x, x); | ||||
|                         width = Math.max(item.width + item.x, width); | ||||
|                     } | ||||
|  | ||||
|                     if (item.y2 !== undefined) { | ||||
|                         let lineHeight = Math.abs(item.y - item.y2); | ||||
|                         let lineMinY = Math.min(item.y, item.y2); | ||||
|                         y = Math.min(lineMinY, y); | ||||
|                         height = Math.max(lineHeight + lineMinY, height); | ||||
|                     } else { | ||||
|                         y = Math.min(item.y, y); | ||||
|                         height = Math.max(item.height + item.y, height); | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 if (this.dragPosition) { | ||||
|                     [x, y] = this.dragPosition.position; | ||||
|                     [width, height] = this.dragPosition.dimensions; | ||||
|                 } else { | ||||
|                     width = width - x; | ||||
|                     height = height - y; | ||||
|                 } | ||||
|  | ||||
|                 this.marqueePosition = { | ||||
|                     x: x, | ||||
|                     y: y, | ||||
|                     width: width, | ||||
|                     height: height | ||||
|                 } | ||||
|                 return this.getMarqueeStyle(x, y, width, height); | ||||
|             } | ||||
|         }, | ||||
|         methods: { | ||||
|             getMarqueeStyle(x, y, width, height) { | ||||
|                 return { | ||||
|                     left: (this.gridSize[0] * x) + 'px', | ||||
|                     top: (this.gridSize[1] * y) + 'px', | ||||
|                     width: (this.gridSize[0] * width) + 'px', | ||||
|                     height: (this.gridSize[1] * height) + 'px' | ||||
|                 }; | ||||
|             }, | ||||
|             updatePosition(event) { | ||||
|                 let currentPosition = [event.pageX, event.pageY]; | ||||
|                 this.initialPosition = this.initialPosition || currentPosition; | ||||
|                 this.delta = currentPosition.map(function (value, index) { | ||||
|                     return value - this.initialPosition[index]; | ||||
|                 }.bind(this)); | ||||
|             }, | ||||
|             startResize(posFactor, dimFactor, event) { | ||||
|                 document.body.addEventListener('mousemove', this.continueResize); | ||||
|                 document.body.addEventListener('mouseup', this.endResize); | ||||
|                 this.marqueeStartPosition = { | ||||
|                     position: [this.marqueePosition.x, this.marqueePosition.y], | ||||
|                     dimensions: [this.marqueePosition.width, this.marqueePosition.height] | ||||
|                 }; | ||||
|                 this.updatePosition(event); | ||||
|                 this.activeDrag = new LayoutDrag(this.marqueeStartPosition, posFactor, dimFactor, this.gridSize); | ||||
|                 event.preventDefault(); | ||||
|             }, | ||||
|             continueResize(event) { | ||||
|                 event.preventDefault(); | ||||
|                 this.updatePosition(event); | ||||
|                 this.dragPosition = this.activeDrag.getAdjustedPositionAndDimensions(this.delta); | ||||
|             }, | ||||
|             endResize(event) { | ||||
|                 document.body.removeEventListener('mousemove', this.continueResize); | ||||
|                 document.body.removeEventListener('mouseup', this.endResize); | ||||
|                 this.continueResize(event); | ||||
|  | ||||
|                 let marqueeStartWidth = this.marqueeStartPosition.dimensions[0]; | ||||
|                 let marqueeStartHeight = this.marqueeStartPosition.dimensions[1]; | ||||
|                 let marqueeStartX = this.marqueeStartPosition.position[0]; | ||||
|                 let marqueeStartY = this.marqueeStartPosition.position[1]; | ||||
|  | ||||
|                 let marqueeEndX = this.dragPosition.position[0]; | ||||
|                 let marqueeEndY = this.dragPosition.position[1]; | ||||
|                 let marqueeEndWidth = this.dragPosition.dimensions[0]; | ||||
|                 let marqueeEndHeight = this.dragPosition.dimensions[1]; | ||||
|  | ||||
|                 let scaleWidth =  marqueeEndWidth / marqueeStartWidth; | ||||
|                 let scaleHeight =  marqueeEndHeight / marqueeStartHeight; | ||||
|  | ||||
|                 let marqueeStart = { | ||||
|                     x: marqueeStartX, | ||||
|                     y: marqueeStartY, | ||||
|                     height: marqueeStartWidth, | ||||
|                     width: marqueeStartHeight | ||||
|                 }; | ||||
|                 let marqueeEnd = { | ||||
|                     x: marqueeEndX, | ||||
|                     y: marqueeEndY, | ||||
|                     width: marqueeEndWidth, | ||||
|                     height: marqueeEndHeight | ||||
|                 }; | ||||
|                 let marqueeOffset = { | ||||
|                     x: marqueeEnd.x - marqueeStart.x, | ||||
|                     y: marqueeEnd.y - marqueeStart.y | ||||
|                 }; | ||||
|  | ||||
|                 this.$emit('endResize', scaleWidth, scaleHeight, marqueeStart, marqueeOffset); | ||||
|                 this.dragPosition = undefined; | ||||
|                 this.initialPosition = undefined; | ||||
|                 this.marqueeStartPosition = undefined; | ||||
|                 this.delta = undefined; | ||||
|                 event.preventDefault(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
| @@ -23,8 +23,7 @@ | ||||
| <template> | ||||
|     <layout-frame :item="item" | ||||
|                   :grid-size="gridSize" | ||||
|                   @move="(gridDelta) => $emit('move', gridDelta)" | ||||
|                   @endMove="() => $emit('endMove')"> | ||||
|                   @endDrag="(item, updates) => $emit('endDrag', item, updates)"> | ||||
|         <div class="c-image-view" | ||||
|              :style="style"> | ||||
|         </div> | ||||
| @@ -57,7 +56,8 @@ | ||||
|                 y: 1, | ||||
|                 width: 10, | ||||
|                 height: 5, | ||||
|                 url: element.url | ||||
|                 url: element.url, | ||||
|                 useGrid: true | ||||
|             }; | ||||
|         }, | ||||
|         inject: ['openmct'], | ||||
|   | ||||
| @@ -24,14 +24,25 @@ | ||||
|     <div class="l-layout__frame c-frame" | ||||
|          :class="{ | ||||
|              'no-frame': !item.hasFrame, | ||||
|              'u-inspectable': inspectable | ||||
|              'u-inspectable': inspectable, | ||||
|              'is-resizing': isResizing | ||||
|          }" | ||||
|          :style="style"> | ||||
|  | ||||
|         <slot></slot> | ||||
|  | ||||
|         <div class="c-frame-edit__move" | ||||
|              @mousedown="startMove([1,1], [0,0], $event)"> | ||||
|         <!-- Drag handles --> | ||||
|         <div class="c-frame-edit"> | ||||
|             <div class="c-frame-edit__move" | ||||
|                  @mousedown="startDrag([1,1], [0,0], $event, 'move')"></div> | ||||
|             <div class="c-frame-edit__handle c-frame-edit__handle--nw" | ||||
|                  @mousedown="startDrag([1,1], [-1,-1], $event, 'resize')"></div> | ||||
|             <div class="c-frame-edit__handle c-frame-edit__handle--ne" | ||||
|                  @mousedown="startDrag([0,1], [1,-1], $event, 'resize')"></div> | ||||
|             <div class="c-frame-edit__handle c-frame-edit__handle--sw" | ||||
|                  @mousedown="startDrag([1,0], [-1,1], $event, 'resize')"></div> | ||||
|             <div class="c-frame-edit__handle c-frame-edit__handle--se" | ||||
|                  @mousedown="startDrag([0,0], [1,1], $event, 'resize')"></div> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
| @@ -39,7 +50,7 @@ | ||||
| <style lang="scss"> | ||||
|     @import "~styles/sass-base"; | ||||
|  | ||||
|     /******************* FRAME */ | ||||
|     /******************************* FRAME */ | ||||
|     .c-frame { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
| @@ -48,15 +59,124 @@ | ||||
|         > *:first-child { | ||||
|             flex: 1 1 auto; | ||||
|         } | ||||
|  | ||||
|         &:not(.no-frame) { | ||||
|             background: $colorBodyBg; | ||||
|             border: $browseFrameBorder; | ||||
|             padding: $interiorMargin; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     .c-frame-edit__move { | ||||
|     .c-frame-edit { | ||||
|         // In Layouts, this is the editing rect and handles | ||||
|         // In Fixed Position, this is a wrapper element | ||||
|         @include abs(); | ||||
|         display: none; | ||||
|  | ||||
|         &__move { | ||||
|             @include abs(); | ||||
|             cursor: move; | ||||
|         } | ||||
|  | ||||
|         &__handle { | ||||
|             $d: 6px; | ||||
|             $o: floor($d * -0.5); | ||||
|             background: $editFrameColorHandleFg; | ||||
|             box-shadow: $editFrameColorHandleBg 0 0 0 2px; | ||||
|             display: none; // Set to block via s-selected selector | ||||
|             position: absolute; | ||||
|             width: $d; height: $d; | ||||
|             top: auto; right: auto; bottom: auto; left: auto; | ||||
|  | ||||
|             &:before { | ||||
|                 // Extended hit area | ||||
|                 @include abs(-10px); | ||||
|                 content: ''; | ||||
|                 display: block; | ||||
|                 z-index: 0; | ||||
|             } | ||||
|  | ||||
|             &:hover { | ||||
|                 background: $editUIColor; | ||||
|             } | ||||
|  | ||||
|             &--nwse { | ||||
|                 cursor: nwse-resize; | ||||
|             } | ||||
|  | ||||
|             &--nw { | ||||
|                 cursor: nw-resize; | ||||
|                 left: $o; top: $o; | ||||
|             } | ||||
|  | ||||
|             &--ne { | ||||
|                 cursor: ne-resize; | ||||
|                 right: $o; top: $o; | ||||
|             } | ||||
|  | ||||
|             &--se { | ||||
|                 cursor: se-resize; | ||||
|                 right: $o; bottom: $o; | ||||
|             } | ||||
|  | ||||
|             &--sw { | ||||
|                 cursor: sw-resize; | ||||
|                 left: $o; bottom: $o; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .c-so-view.has-complex-content + .c-frame-edit { | ||||
|         // Target frames that hold domain objects that include header elements, as opposed to drawing and alpha objects | ||||
|         // Make the __move element a more affordable drag UI element | ||||
|         .c-frame-edit__move { | ||||
|             @include userSelectNone(); | ||||
|             background: $editFrameMovebarColorBg; | ||||
|             box-shadow: rgba(black, 0.2) 0 1px; | ||||
|             bottom: auto; | ||||
|             height: 0; // Height is set on hover on s-selected.c-frame | ||||
|             opacity: 0.8; | ||||
|             max-height: 100%; | ||||
|             overflow: hidden; | ||||
|             text-align: center; | ||||
|  | ||||
|             &:before { | ||||
|                 // Grippy | ||||
|                 $h: 4px; | ||||
|                 $tbOffset: ($editFrameMovebarH - $h) / 2; | ||||
|                 $lrOffset: 25%; | ||||
|                 @include grippy($editFrameMovebarColorFg); | ||||
|                 content: ''; | ||||
|                 display: block; | ||||
|                 position: absolute; | ||||
|                 top: $tbOffset; right: $lrOffset; bottom: $tbOffset; left: $lrOffset; | ||||
|             } | ||||
|  | ||||
|             &:hover { | ||||
|                 background: $editFrameHovMovebarColorBg; | ||||
|                 &:before { @include grippy($editFrameHovMovebarColorFg); } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .is-editing { | ||||
|         /******************* STYLES FOR C-FRAME WHILE EDITING */ | ||||
|         .c-frame { | ||||
|             $moveBarOutDelay: 500ms; | ||||
|             &.no-frame { | ||||
|                 border: $editFrameBorder; // Base border style for a frame element while editing. | ||||
|             } | ||||
|  | ||||
|             &-edit { | ||||
|                 display: contents; | ||||
|             } | ||||
|  | ||||
|             &-edit__move, | ||||
|             .c-so-view { | ||||
|                 transition: $transOut; | ||||
|                 transition-delay: $moveBarOutDelay; | ||||
|             } | ||||
|  | ||||
|             &:not([s-selected]) { | ||||
|                 &:hover { | ||||
|                     border: $editFrameBorderHov; | ||||
| @@ -68,110 +188,37 @@ | ||||
|                 border: $editFrameSelectedBorder; | ||||
|                 box-shadow: $editFrameSelectedShdw; | ||||
|  | ||||
|                 .c-frame-edit__move { | ||||
|                     cursor: move; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /******************* DEFAULT STYLES FOR -EDIT__MOVE */ | ||||
|         // All object types | ||||
|         .c-frame-edit__move { | ||||
|             @include abs(); | ||||
|             display: block; | ||||
|         } | ||||
|  | ||||
|         // Has-complex-content objects | ||||
|         .c-so-view.has-complex-content { | ||||
|             transition: $transOut; | ||||
|             transition-delay: $moveBarOutDelay; | ||||
|  | ||||
|             > .c-so-view__local-controls { | ||||
|                 transition: transform 250ms ease-in-out; | ||||
|                 transition-delay: $moveBarOutDelay; | ||||
|             } | ||||
|  | ||||
|             + .c-frame-edit__move { | ||||
|                 display: none; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         .l-layout { | ||||
|             /******************* 0 - 1 ITEM SELECTED */ | ||||
|             &:not(.is-multi-selected) { | ||||
|                 > .l-layout__frame[s-selected] { | ||||
|                     > .c-so-view.has-complex-content { | ||||
|                         > .c-so-view__local-controls { | ||||
|                             transition: transform $transOutTime ease-in-out; | ||||
|                             transition-delay: $moveBarOutDelay; | ||||
|                         } | ||||
|  | ||||
|                         + .c-frame-edit__move { | ||||
|                             transition: $transOut; | ||||
|                             transition-delay: $moveBarOutDelay; | ||||
|                             @include userSelectNone(); | ||||
|                             background: $editFrameMovebarColorBg; | ||||
|                             box-shadow: rgba(black, 0.2) 0 1px; | ||||
|                             bottom: auto; | ||||
|                             display: block; | ||||
|                             height: 0; // Height is set on hover below | ||||
|                             opacity: 0.8; | ||||
|                             max-height: 100%; | ||||
|                             overflow: hidden; | ||||
|                             text-align: center; | ||||
|  | ||||
|                             &:before { | ||||
|                                 // Grippy | ||||
|                                 $h: 4px; | ||||
|                                 $tbOffset: ($editFrameMovebarH - $h) / 2; | ||||
|                                 $lrOffset: 25%; | ||||
|                                 @include grippy($editFrameMovebarColorFg); | ||||
|                                 content: ''; | ||||
|                                 display: block; | ||||
|                                 position: absolute; | ||||
|                                 top: $tbOffset; | ||||
|                                 right: $lrOffset; | ||||
|                                 bottom: $tbOffset; | ||||
|                                 left: $lrOffset; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     &:hover { | ||||
|                         > .c-so-view.has-complex-content { | ||||
|                             transition: $transIn; | ||||
|                             transition-delay: 0s; | ||||
|                             padding-top: $editFrameMovebarH + $interiorMarginSm; | ||||
|  | ||||
|                             > .c-so-view__local-controls { | ||||
|                                 transform: translateY($editFrameMovebarH); | ||||
|                                 transition: transform $transInTime ease-in-out; | ||||
|                                 transition-delay: 0s; | ||||
|                             } | ||||
|  | ||||
|                             + .c-frame-edit__move { | ||||
|                                 transition: $transIn; | ||||
|                                 transition-delay: 0s; | ||||
|                                 height: $editFrameMovebarH; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             /******************* > 1 ITEMS SELECTED */ | ||||
|             &.is-multi-selected { | ||||
|                 .l-layout__frame[s-selected] { | ||||
|                     > .c-so-view.has-complex-content + .c-frame-edit__move { | ||||
|                 > .c-frame-edit { | ||||
|                     [class*='__handle'] { | ||||
|                         display: block; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         .l-layout__frame:not(.is-resizing) { | ||||
|             // Show and animate the __move bar for sub-object views with complex content | ||||
|             &:hover > .c-so-view.has-complex-content { | ||||
|                 // Move content down so the __move bar doesn't cover it. | ||||
|                 padding-top: $editFrameMovebarH; | ||||
|                 transition: $transIn; | ||||
|  | ||||
|                 &.c-so-view--no-frame { | ||||
|                     // Move content down with a bit more space | ||||
|                     padding-top: $editFrameMovebarH + $interiorMarginSm; | ||||
|                 } | ||||
|  | ||||
|                 // Show the move bar | ||||
|                 + .c-frame-edit .c-frame-edit__move { | ||||
|                     height: $editFrameMovebarH; | ||||
|                     transition: $transIn; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
|  | ||||
|  | ||||
| <script> | ||||
|     import LayoutDrag from './../LayoutDrag' | ||||
|  | ||||
| @@ -181,9 +228,21 @@ | ||||
|             item: Object, | ||||
|             gridSize: Array | ||||
|         }, | ||||
|         data() { | ||||
|             return { | ||||
|                 dragPosition: undefined, | ||||
|                 isResizing: undefined | ||||
|             } | ||||
|         }, | ||||
|         computed: { | ||||
|             style() { | ||||
|                 let {x, y, width, height} = this.item; | ||||
|  | ||||
|                 if (this.dragPosition) { | ||||
|                     [x, y] = this.dragPosition.position; | ||||
|                     [width, height] = this.dragPosition.dimensions; | ||||
|                 } | ||||
|  | ||||
|                 return { | ||||
|                     left: (this.gridSize[0] * x) + 'px', | ||||
|                     top: (this.gridSize[1] * y) + 'px', | ||||
| @@ -205,40 +264,36 @@ | ||||
|                     return value - this.initialPosition[index]; | ||||
|                 }.bind(this)); | ||||
|             }, | ||||
|             startMove(posFactor, dimFactor, event) { | ||||
|                 document.body.addEventListener('mousemove', this.continueMove); | ||||
|                 document.body.addEventListener('mouseup', this.endMove); | ||||
|             startDrag(posFactor, dimFactor, event, type) { | ||||
|                 document.body.addEventListener('mousemove', this.continueDrag); | ||||
|                 document.body.addEventListener('mouseup', this.endDrag); | ||||
|  | ||||
|                 this.dragPosition = { | ||||
|                     position: [this.item.x, this.item.y] | ||||
|                     position: [this.item.x, this.item.y], | ||||
|                     dimensions: [this.item.width, this.item.height] | ||||
|                 }; | ||||
|                 this.updatePosition(event); | ||||
|                 this.activeDrag = new LayoutDrag(this.dragPosition, posFactor, dimFactor, this.gridSize); | ||||
|                 this.isResizing = type === 'resize'; | ||||
|                 event.preventDefault(); | ||||
|             }, | ||||
|             continueMove(event) { | ||||
|             continueDrag(event) { | ||||
|                 event.preventDefault(); | ||||
|                 this.updatePosition(event); | ||||
|                 let newPosition = this.activeDrag.getAdjustedPosition(this.delta); | ||||
|  | ||||
|                 if (!_.isEqual(newPosition, this.dragPosition)) { | ||||
|                     this.dragPosition = newPosition; | ||||
|                     this.$emit('move', this.toGridDelta(this.delta)); | ||||
|                 } | ||||
|                 this.dragPosition = this.activeDrag.getAdjustedPosition(this.delta); | ||||
|             }, | ||||
|             endMove(event) { | ||||
|                 document.body.removeEventListener('mousemove', this.continueMove); | ||||
|                 document.body.removeEventListener('mouseup', this.endMove); | ||||
|                 this.continueMove(event); | ||||
|                 this.$emit('endMove'); | ||||
|             endDrag(event) { | ||||
|                 document.body.removeEventListener('mousemove', this.continueDrag); | ||||
|                 document.body.removeEventListener('mouseup', this.endDrag); | ||||
|                 this.continueDrag(event); | ||||
|                 let [x, y] = this.dragPosition.position; | ||||
|                 let [width, height] = this.dragPosition.dimensions; | ||||
|                 this.$emit('endDrag', this.item, {x, y, width, height}); | ||||
|                 this.dragPosition = undefined; | ||||
|                 this.initialPosition = undefined; | ||||
|                 this.delta = undefined; | ||||
|                 this.isResizing = undefined; | ||||
|                 event.preventDefault(); | ||||
|             }, | ||||
|             toGridDelta(pixelDelta) { | ||||
|                 return pixelDelta.map((v, i) => { | ||||
|                     return Math.round(v / this.gridSize[i]); | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -30,9 +30,9 @@ | ||||
|             </line> | ||||
|         </svg> | ||||
|  | ||||
|         <div class="c-frame-edit__move" | ||||
|              @mousedown="startDrag($event)"></div> | ||||
|         <div class="c-frame-edit" v-if="showFrameEdit"> | ||||
|         <div class="c-frame-edit"> | ||||
|             <div class="c-frame-edit__move" | ||||
|                  @mousedown="startDrag($event)"></div> | ||||
|             <div class="c-frame-edit__handle" | ||||
|                  :class="startHandleClass" | ||||
|                  @mousedown="startDrag($event, 'start')"></div> | ||||
| @@ -66,7 +66,8 @@ | ||||
|                 y: 10, | ||||
|                 x2: 10, | ||||
|                 y2: 5, | ||||
|                 stroke: '#717171' | ||||
|                 stroke: '#717171', | ||||
|                 useGrid: true | ||||
|             }; | ||||
|         }, | ||||
|         inject: ['openmct'], | ||||
| @@ -75,31 +76,24 @@ | ||||
|             gridSize: Array, | ||||
|             initSelect: Boolean, | ||||
|             index: Number, | ||||
|             multiSelect: Boolean | ||||
|         }, | ||||
|         data() { | ||||
|             return { | ||||
|                 dragPosition: undefined, | ||||
|                 dragging: undefined, | ||||
|                 selection: [] | ||||
|                 dragPosition: undefined | ||||
|             }; | ||||
|         }, | ||||
|         computed: { | ||||
|             showFrameEdit() { | ||||
|                 let layoutItem = this.selection.length > 0 && this.selection[0][0].context.layoutItem; | ||||
|                 return !this.multiSelect && layoutItem && layoutItem.id === this.item.id; | ||||
|             }, | ||||
|             position() { | ||||
|                 let {x, y, x2, y2} = this.item; | ||||
|                 if (this.dragging && this.dragPosition) { | ||||
|                 if (this.dragPosition) { | ||||
|                     ({x, y, x2, y2} = this.dragPosition); | ||||
|                 } | ||||
|                 return {x, y, x2, y2}; | ||||
|             }, | ||||
|             style() { | ||||
|                 let {x, y, x2, y2} = this.position; | ||||
|                 let width = Math.max(this.gridSize[0] * Math.abs(x - x2), 1); | ||||
|                 let height = Math.max(this.gridSize[1] * Math.abs(y - y2), 1); | ||||
|                 let width = this.gridSize[0] * Math.abs(x - x2); | ||||
|                 let height = this.gridSize[1] * Math.abs(y - y2); | ||||
|                 let left = this.gridSize[0] * Math.min(x, x2); | ||||
|                 let top = this.gridSize[1] * Math.min(y, y2); | ||||
|                 return { | ||||
| @@ -181,27 +175,13 @@ | ||||
|                 event.preventDefault(); | ||||
|                 let pxDeltaX = this.startPosition[0] - event.pageX; | ||||
|                 let pxDeltaY = this.startPosition[1] - event.pageY; | ||||
|                 let newPosition = this.calculateDragPosition(pxDeltaX, pxDeltaY); | ||||
|  | ||||
|                 if (!this.dragging) { | ||||
|                     if (!_.isEqual(newPosition, this.dragPosition)) { | ||||
|                         let gridDelta = [event.pageX - this.startPosition[0], event.pageY - this.startPosition[1]]; | ||||
|                         this.dragPosition = newPosition; | ||||
|                         this.$emit('move', this.toGridDelta(gridDelta)); | ||||
|                     } | ||||
|                 } else { | ||||
|                     this.dragPosition = newPosition; | ||||
|                 } | ||||
|                 this.dragPosition = this.calculateDragPosition(pxDeltaX, pxDeltaY); | ||||
|             }, | ||||
|             endDrag(event) { | ||||
|                 document.body.removeEventListener('mousemove', this.continueDrag); | ||||
|                 document.body.removeEventListener('mouseup', this.endDrag); | ||||
|                 let {x, y, x2, y2} = this.dragPosition; | ||||
|                 if (!this.dragging) { | ||||
|                     this.$emit('endMove'); | ||||
|                 } else { | ||||
|                     this.$emit('endLineResize', this.item, {x, y, x2, y2}); | ||||
|                 } | ||||
|                 this.$emit('endDrag', this.item, {x, y, x2, y2}); | ||||
|                 this.dragPosition = undefined; | ||||
|                 this.dragging = undefined; | ||||
|                 event.preventDefault(); | ||||
| @@ -211,7 +191,6 @@ | ||||
|                 let gridDeltaY = Math.round(pxDeltaY / this.gridSize[0]); // TODO: should this be gridSize[1]? | ||||
|                 let {x, y, x2, y2} = this.item; | ||||
|                 let dragPosition = {x, y, x2, y2}; | ||||
|  | ||||
|                 if (this.dragging === 'start') { | ||||
|                     dragPosition.x -= gridDeltaX; | ||||
|                     dragPosition.y -= gridDeltaY; | ||||
| @@ -226,14 +205,6 @@ | ||||
|                     dragPosition.y2 -= gridDeltaY; | ||||
|                 } | ||||
|                 return dragPosition; | ||||
|             }, | ||||
|             setSelection(selection) { | ||||
|                 this.selection = selection; | ||||
|             }, | ||||
|             toGridDelta(pixelDelta) { | ||||
|                 return pixelDelta.map((v, i) => { | ||||
|                     return Math.round(v / this.gridSize[i]); | ||||
|                 }); | ||||
|             } | ||||
|         }, | ||||
|         watch: { | ||||
| @@ -246,7 +217,6 @@ | ||||
|             } | ||||
|         }, | ||||
|         mounted() { | ||||
|             this.openmct.selection.on('change', this.setSelection); | ||||
|             this.context = { | ||||
|                 layoutItem: this.item, | ||||
|                 index: this.index | ||||
| @@ -258,7 +228,6 @@ | ||||
|             if (this.removeSelectable) { | ||||
|                 this.removeSelectable(); | ||||
|             } | ||||
|             this.openmct.selection.off('change', this.setSelection); | ||||
|         } | ||||
|     } | ||||
|  </script> | ||||
|   | ||||
| @@ -22,14 +22,11 @@ | ||||
| <template> | ||||
|     <layout-frame :item="item" | ||||
|                   :grid-size="gridSize" | ||||
|                   :title="domainObject && domainObject.name + ': ' + domainObject.type" | ||||
|                   @move="(gridDelta) => $emit('move', gridDelta)" | ||||
|                   @endMove="() => $emit('endMove')"> | ||||
|                   @endDrag="(item, updates) => $emit('endDrag', item, updates)"> | ||||
|         <object-frame v-if="domainObject" | ||||
|                       :domain-object="domainObject" | ||||
|                       :object-path="objectPath" | ||||
|                       :has-frame="item.hasFrame" | ||||
|                       :show-edit-view="false" | ||||
|                       ref="objectFrame"> | ||||
|         </object-frame> | ||||
|     </layout-frame> | ||||
| @@ -68,7 +65,8 @@ | ||||
|                 x: position[0], | ||||
|                 y: position[1], | ||||
|                 identifier: domainObject.identifier, | ||||
|                 hasFrame: hasFrameByDefault(domainObject.type) | ||||
|                 hasFrame: hasFrameByDefault(domainObject.type), | ||||
|                 useGrid: true | ||||
|             }; | ||||
|         }, | ||||
|         inject: ['openmct'], | ||||
|   | ||||
| @@ -23,14 +23,11 @@ | ||||
|  <template> | ||||
|      <layout-frame :item="item" | ||||
|                    :grid-size="gridSize" | ||||
|                    :title="domainObject && domainObject.name + ': ' + domainObject.type" | ||||
|                    :class="{'c-telemetry-view--unknown': domainObject.type.indexOf('unknown') !== -1}" | ||||
|                    @move="(gridDelta) => $emit('move', gridDelta)" | ||||
|                    @endMove="() => $emit('endMove')"> | ||||
|                    @endDrag="(item, updates) => $emit('endDrag', item, updates)"> | ||||
|         <div class="c-telemetry-view" | ||||
|              :style="styleObject" | ||||
|              v-if="domainObject"> | ||||
|             <div v-if="showLabel || domainObject.type.indexOf('unknown') !== -1" | ||||
|             <div v-if="showLabel" | ||||
|                   class="c-telemetry-view__label"> | ||||
|                 <div class="c-telemetry-view__label-text">{{ domainObject.name }}</div> | ||||
|             </div> | ||||
| @@ -52,21 +49,6 @@ | ||||
|         display: flex; | ||||
|         align-items: stretch; | ||||
|  | ||||
|         &--unknown { | ||||
|             .c-telemetry-view__label-text { | ||||
|                 @include isUnknown(); | ||||
|                 display: flex; | ||||
|                 align-items: center; | ||||
|                 &:before { | ||||
|                     content: $glyph-icon-object-unknown; | ||||
|                     font-family: symbolsfont; | ||||
|                     font-style: normal; | ||||
|                     display: inline-block; | ||||
|                     margin-right: $interiorMarginSm; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         > * { | ||||
|             // Label and value holders | ||||
|             flex: 1 1 auto; | ||||
| @@ -96,7 +78,6 @@ | ||||
|  | ||||
|  <script> | ||||
|     import LayoutFrame from './LayoutFrame.vue' | ||||
|     import printj from 'printj' | ||||
|  | ||||
|     const DEFAULT_TELEMETRY_DIMENSIONS = [10, 5], | ||||
|           DEFAULT_POSITION = [1, 1]; | ||||
| @@ -115,9 +96,10 @@ | ||||
|                 displayMode: 'all', | ||||
|                 value: metadata.getDefaultDisplayValue(), | ||||
|                 stroke: "transparent", | ||||
|                 fill: "transparent", | ||||
|                 fill: "", | ||||
|                 color: "", | ||||
|                 size: "13px" | ||||
|                 size: "13px", | ||||
|                 useGrid: true | ||||
|             }; | ||||
|         }, | ||||
|         inject: ['openmct'], | ||||
| @@ -161,10 +143,6 @@ | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 if (this.item.format) { | ||||
|                     return printj.sprintf(this.item.format, this.datum[this.valueMetadata.key]); | ||||
|                 } | ||||
|  | ||||
|                 return this.valueFormatter && this.valueFormatter.format(this.datum); | ||||
|             }, | ||||
|             telemetryClass() { | ||||
| @@ -190,9 +168,6 @@ | ||||
|                 } | ||||
|  | ||||
|                 this.context.index = newIndex; | ||||
|             }, | ||||
|             item(newItem) { | ||||
|                 this.context.layoutItem = newItem; | ||||
|             } | ||||
|         }, | ||||
|         methods: { | ||||
| @@ -201,8 +176,7 @@ | ||||
|                 let options = { | ||||
|                     start: bounds.start, | ||||
|                     end: bounds.end, | ||||
|                     size: 1, | ||||
|                     strategy: 'latest' | ||||
|                     size: 1 | ||||
|                 }; | ||||
|                 this.openmct.telemetry.request(this.domainObject, options) | ||||
|                     .then(data => { | ||||
| @@ -244,14 +218,10 @@ | ||||
|                 this.context = { | ||||
|                     item: domainObject, | ||||
|                     layoutItem: this.item, | ||||
|                     index: this.index, | ||||
|                     updateTelemetryFormat: this.updateTelemetryFormat | ||||
|                     index: this.index | ||||
|                 }; | ||||
|                 this.removeSelectable = this.openmct.selection.selectable( | ||||
|                     this.$el, this.context, this.initSelect); | ||||
|             }, | ||||
|             updateTelemetryFormat(format) { | ||||
|                 this.$emit('formatChanged', this.item, format); | ||||
|             } | ||||
|         }, | ||||
|         mounted() { | ||||
|   | ||||
| @@ -23,8 +23,7 @@ | ||||
|  <template> | ||||
|     <layout-frame :item="item" | ||||
|                   :grid-size="gridSize" | ||||
|                   @move="(gridDelta) => $emit('move', gridDelta)" | ||||
|                   @endMove="() => $emit('endMove')"> | ||||
|                   @endDrag="(item, updates) => $emit('endDrag', item, updates)"> | ||||
|         <div class="c-text-view" | ||||
|              :style="style"> | ||||
|             {{ item.text }} | ||||
| @@ -60,7 +59,8 @@ | ||||
|                 y: 1, | ||||
|                 width: 10, | ||||
|                 height: 5, | ||||
|                 text: element.text | ||||
|                 text: element.text, | ||||
|                 useGrid: true | ||||
|             }; | ||||
|         }, | ||||
|         inject: ['openmct'], | ||||
|   | ||||
| @@ -25,9 +25,8 @@ import Vue from 'vue' | ||||
| import objectUtils from '../../api/objects/object-utils.js' | ||||
| import DisplayLayoutType from './DisplayLayoutType.js' | ||||
| import DisplayLayoutToolbar from './DisplayLayoutToolbar.js' | ||||
| import AlphaNumericFormatViewProvider from './AlphaNumericFormatViewProvider.js' | ||||
|  | ||||
| export default function DisplayLayoutPlugin(options) { | ||||
| export default function () { | ||||
|     return function (openmct) { | ||||
|         openmct.objectViews.addProvider({ | ||||
|             key: 'layout.view', | ||||
| @@ -48,8 +47,7 @@ export default function DisplayLayoutPlugin(options) { | ||||
|                             template: '<layout ref="displayLayout" :domain-object="domainObject"></layout>', | ||||
|                             provide: { | ||||
|                                 openmct, | ||||
|                                 objectUtils, | ||||
|                                 options | ||||
|                                 objectUtils | ||||
|                             }, | ||||
|                             el: container, | ||||
|                             data () { | ||||
| @@ -62,7 +60,6 @@ export default function DisplayLayoutPlugin(options) { | ||||
|                     getSelectionContext() { | ||||
|                         return { | ||||
|                             item: domainObject, | ||||
|                             supportsMultiSelect: true, | ||||
|                             addElement: component && component.$refs.displayLayout.addElement, | ||||
|                             removeItem: component && component.$refs.displayLayout.removeItem, | ||||
|                             orderItem: component && component.$refs.displayLayout.orderItem | ||||
| @@ -78,8 +75,7 @@ export default function DisplayLayoutPlugin(options) { | ||||
|             } | ||||
|         }); | ||||
|         openmct.types.addType('layout', DisplayLayoutType()); | ||||
|         openmct.toolbars.addProvider(new DisplayLayoutToolbar(openmct, options)); | ||||
|         openmct.inspectorViews.addProvider(new AlphaNumericFormatViewProvider(openmct, options)); | ||||
|         openmct.toolbars.addProvider(new DisplayLayoutToolbar(openmct)); | ||||
|         openmct.composition.addPolicy((parent, child) => { | ||||
|             if (parent.type === 'layout' && child.type === 'folder') { | ||||
|                 return false; | ||||
| @@ -87,6 +83,5 @@ export default function DisplayLayoutPlugin(options) { | ||||
|                 return true; | ||||
|             } | ||||
|         }); | ||||
|         DisplayLayoutPlugin._installed = true; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,116 +0,0 @@ | ||||
| <template> | ||||
|     <div class="u-contents c-filter-settings"> | ||||
|         <li class="grid-row c-filter-settings__setting" | ||||
|             v-for="(filter, index) in filterField.filters" | ||||
|             :key="index"> | ||||
|             <div class="grid-cell label"> | ||||
|                 {{ filterField.name }} = | ||||
|             </div> | ||||
|             <div class="grid-cell value"> | ||||
|                 <!-- EDITING --> | ||||
|                 <!-- String input, editing --> | ||||
|                 <template v-if="!filter.possibleValues && isEditing"> | ||||
|                     <input class="c-input--flex" | ||||
|                            type="text" | ||||
|                            placeholder="Enter Value" | ||||
|                            :id="`${filter}filterControl`" | ||||
|                            :value="persistedValue(filter)" | ||||
|                            @change="updateFilterValue($event, filter)"> | ||||
|                 </template> | ||||
|  | ||||
|                 <!-- Checkbox list, editing --> | ||||
|                 <template v-if="filter.possibleValues && isEditing"> | ||||
|                     <div class="c-checkbox-list__row" | ||||
|                          v-for="value in filter.possibleValues" | ||||
|                          :key="value"> | ||||
|                         <input class="c-checkbox-list__input" | ||||
|                                type="checkbox" | ||||
|                                :id="`${value}filterControl`" | ||||
|                                @change="onUserSelect($event, filter.comparator, value)" | ||||
|                                :checked="isChecked(filter.comparator, value)"> | ||||
|                         <span class="c-checkbox-list__value"> | ||||
|                             {{ value }} | ||||
|                         </span> | ||||
|                     </div> | ||||
|                 </template> | ||||
|  | ||||
|                 <!-- BROWSING --> | ||||
|                 <!-- String input, NOT editing --> | ||||
|                 <template v-if="!filter.possibleValues && !isEditing"> | ||||
|                     {{ persistedValue(filter) }} | ||||
|                 </template> | ||||
|  | ||||
|                 <!-- Checkbox list, NOT editing --> | ||||
|                 <template v-if="filter.possibleValues && !isEditing"> | ||||
|                     <span  | ||||
|                         v-if="persistedFilters[filter.comparator]"> | ||||
|                         {{persistedFilters[filter.comparator].join(', ')}} | ||||
|                     </span> | ||||
|                 </template> | ||||
|             </div> | ||||
|         </li> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <style lang="scss"> | ||||
|     @import "~styles/sass-base"; | ||||
|  | ||||
|     .c-filter-settings { | ||||
|         &__setting { | ||||
|             .grid-cell.label { | ||||
|                 white-space: nowrap; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
|  | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|     inject: [ | ||||
|         'openmct' | ||||
|     ], | ||||
|     props: { | ||||
|         filterField: Object,  | ||||
|         persistedFilters: { | ||||
|             type: Object, | ||||
|             default: () => { | ||||
|                 return {} | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             expanded: false, | ||||
|             isEditing: this.openmct.editor.isEditing() | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         toggleIsEditing(isEditing) { | ||||
|             this.isEditing = isEditing; | ||||
|         }, | ||||
|         onUserSelect(event, comparator, value){ | ||||
|             this.$emit('onUserSelect', this.filterField.key, comparator, value, event.target.checked); | ||||
|         }, | ||||
|         isChecked(comparator, value) { | ||||
|             if (this.persistedFilters[comparator] && this.persistedFilters[comparator].includes(value)) { | ||||
|                 return true; | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         }, | ||||
|         persistedValue(comparator) { | ||||
|             return this.persistedFilters && this.persistedFilters[comparator]; | ||||
|         }, | ||||
|         updateFilterValue(event, comparator) { | ||||
|             this.$emit('onTextEnter', this.filterField.key, comparator, event.target.value); | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         this.openmct.editor.on('isEditing', this.toggleIsEditing); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         this.openmct.editor.off('isEditing', this.toggleIsEditing); | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @@ -1,94 +0,0 @@ | ||||
| <template> | ||||
|     <li> | ||||
|         <div class="c-tree__item menus-to-left" | ||||
|              @click="toggleExpanded"> | ||||
|             <span class="c-disclosure-triangle is-enabled flex-elem" | ||||
|               :class="{'c-disclosure-triangle--expanded': expanded}"></span> | ||||
|             <div class="c-tree__item__label"> | ||||
|                 <div class="c-object-label"> | ||||
|                     <div class="c-object-label__type-icon" | ||||
|                          :class="objectCssClass"> | ||||
|                     </div> | ||||
|                     <div class="c-object-label__name flex-elem grows">{{ filterObject.name }}</div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <ul class="grid-properties" v-if="expanded"> | ||||
|             <filter-field | ||||
|                     v-for="field in filterObject.valuesWithFilters" | ||||
|                     :key="field.key" | ||||
|                     :filterField="field" | ||||
|                     :persistedFilters="persistedFilters[field.key]" | ||||
|                     @onUserSelect="collectUserSelects" | ||||
|                     @onTextEnter="updateTextFilter"> | ||||
|             </filter-field> | ||||
|         </ul> | ||||
|     </li> | ||||
| </template> | ||||
|  | ||||
| <style lang="scss"> | ||||
|  | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
| import FilterField from './FilterField.vue'; | ||||
|  | ||||
| export default { | ||||
|     inject: ['openmct'], | ||||
|     components: { | ||||
|         FilterField | ||||
|     }, | ||||
|     props: { | ||||
|         filterObject: Object,  | ||||
|         persistedFilters: { | ||||
|             type: Object, | ||||
|             default: () => { | ||||
|                 return {}; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
|         return { | ||||
|             expanded: false, | ||||
|             objectCssClass: undefined, | ||||
|             updatedFilters: this.persistedFilters | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         toggleExpanded() { | ||||
|             this.expanded = !this.expanded; | ||||
|         }, | ||||
|         collectUserSelects(key, comparator, valueName, value) { | ||||
|             let filterValue = this.updatedFilters[key]; | ||||
|  | ||||
|             if (filterValue && filterValue[comparator]) { | ||||
|                 if (value === false) { | ||||
|                     filterValue[comparator] = filterValue[comparator].filter(v => v !== valueName); | ||||
|                 } else { | ||||
|                     filterValue[comparator].push(valueName); | ||||
|                 } | ||||
|             } else { | ||||
|                 if (!this.updatedFilters[key]) { | ||||
|                     this.$set(this.updatedFilters, key, {}); | ||||
|                 } | ||||
|                 this.$set(this.updatedFilters[key], comparator, [value ? valueName : undefined]); | ||||
|             } | ||||
|  | ||||
|             this.$emit('updateFilters', this.keyString, this.updatedFilters); | ||||
|         }, | ||||
|         updateTextFilter(key, comparator, value) { | ||||
|             if (!this.updatedFilters[key]) { | ||||
|                 this.$set(this.updatedFilters, key, {}); | ||||
|                 this.$set(this.updatedFilters[key], comparator, ''); | ||||
|             } | ||||
|             this.updatedFilters[key][comparator] = value; | ||||
|             this.$emit('updateFilters', this.keyString, this.updatedFilters); | ||||
|         } | ||||
|     }, | ||||
|     mounted() { | ||||
|         let type = this.openmct.types.get(this.filterObject.domainObject.type) || {}; | ||||
|         this.keyString = this.openmct.objects.makeKeyString(this.filterObject.domainObject.identifier); | ||||
|         this.objectCssClass = type.definition.cssClass; | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @@ -1,87 +0,0 @@ | ||||
| <template> | ||||
|     <ul class="tree c-tree c-properties__section" v-if="Object.keys(children).length"> | ||||
|         <h2 class="c-properties__header">Filters</h2> | ||||
|         <filter-object  | ||||
|             v-for="(child, key) in children" | ||||
|             :key="key" | ||||
|             :filterObject="child" | ||||
|             :persistedFilters="persistedFilters[key]" | ||||
|             @updateFilters="persistFilters"> | ||||
|         </filter-object> | ||||
|     </ul> | ||||
| </template> | ||||
|  | ||||
| <style lang="scss"> | ||||
|  | ||||
| </style> | ||||
|  | ||||
| <script> | ||||
| import FilterObject from './FilterObject.vue'; | ||||
|  | ||||
| export default { | ||||
|     components: { | ||||
|         FilterObject | ||||
|     }, | ||||
|     inject: [ | ||||
|         'openmct' | ||||
|     ], | ||||
|     data() { | ||||
|         let providedObject = this.openmct.selection.get()[0][0].context.item; | ||||
|         let persistedFilters = {}; | ||||
|  | ||||
|         if (providedObject.configuration && providedObject.configuration.filters) { | ||||
|             persistedFilters = providedObject.configuration.filters; | ||||
|         } | ||||
|  | ||||
|         return { | ||||
|             providedObject, | ||||
|             persistedFilters, | ||||
|             children: {} | ||||
|         } | ||||
|     }, | ||||
|     methods: { | ||||
|         addChildren(child) { | ||||
|             let keyString = this.openmct.objects.makeKeyString(child.identifier), | ||||
|                 metadata = this.openmct.telemetry.getMetadata(child), | ||||
|                 valuesWithFilters = metadata.valueMetadatas.filter((value) => value.filters), | ||||
|                 childObject = { | ||||
|                     name: child.name, | ||||
|                     domainObject: child, | ||||
|                     valuesWithFilters | ||||
|                 }; | ||||
|  | ||||
|             if (childObject.valuesWithFilters.length) { | ||||
|                 this.$set(this.children, keyString, childObject); | ||||
|             } else { | ||||
|                 return; | ||||
|             } | ||||
|         }, | ||||
|         removeChildren(identifier) { | ||||
|             let keyString = this.openmct.objects.makeKeyString(identifier); | ||||
|             this.$delete(this.children, keyString); | ||||
|             this.persistFilters(keyString); | ||||
|         }, | ||||
|         persistFilters(keyString, userSelects) { | ||||
|             this.persistedFilters[keyString] = userSelects; | ||||
|             this.openmct.objects.mutate(this.providedObject, 'configuration.filters', this.persistedFilters); | ||||
|         }, | ||||
|         updatePersistedFilters(filters) { | ||||
|             this.persistedFilters = filters; | ||||
|         } | ||||
|     }, | ||||
|     mounted(){ | ||||
|         this.composition = this.openmct.composition.get(this.providedObject); | ||||
|         this.composition.on('add', this.addChildren); | ||||
|         this.composition.on('remove', this.removeChildren); | ||||
|         this.composition.load(); | ||||
|         this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters); | ||||
|         this.unobserveAllMutation = this.openmct.objects.observe(this.providedObject, '*', (mutatedObject) => this.providedObject = mutatedObject); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|         this.composition.off('add', this.addChildren); | ||||
|         this.composition.off('remove', this.removeChildren); | ||||
|         this.unobserve(); | ||||
|         this.unobserveAllMutation(); | ||||
|     } | ||||
| } | ||||
| </script> | ||||
| @@ -1,72 +0,0 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT, Copyright (c) 2014-2018, 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([ | ||||
|     './components/FiltersView.vue', | ||||
|     'vue' | ||||
| ], function ( | ||||
|     FiltersView, | ||||
|     Vue | ||||
| ) { | ||||
|  | ||||
|     function FiltersInspectorViewProvider(openmct, supportedObjectTypesArray) { | ||||
|         return { | ||||
|             key: 'filters-inspector', | ||||
|             name: 'Filters Inspector View', | ||||
|             canView: function (selection) { | ||||
|                 if (selection.length === 0 || selection[0].length === 0) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 let object = selection[0][0].context.item; | ||||
|  | ||||
|                 return object && supportedObjectTypesArray.some(type => object.type === type); | ||||
|             }, | ||||
|             view: function (selection) { | ||||
|                 let component; | ||||
|                 return { | ||||
|                     show: function (element) { | ||||
|                         component = new Vue({ | ||||
|                             provide: { | ||||
|                                 openmct | ||||
|                             }, | ||||
|                             components: { | ||||
|                                 FiltersView: FiltersView.default | ||||
|                             }, | ||||
|                             template: '<filters-view></filters-view>', | ||||
|                             el: element | ||||
|                         }); | ||||
|                     }, | ||||
|                     destroy: function () { | ||||
|                         if (component) { | ||||
|                             component.$destroy(); | ||||
|                             component = undefined; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             priority: function () { | ||||
|                 return 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return FiltersInspectorViewProvider; | ||||
| }); | ||||
| @@ -106,6 +106,9 @@ | ||||
|     .c-fl { | ||||
|         @include abs(); | ||||
|         display: flex; | ||||
|         flex-direction: column; // TEMP: only needed to support temp-toolbar element | ||||
|  | ||||
|         > * + * {  margin-top: $interiorMargin; } | ||||
|  | ||||
|         .temp-toolbar { | ||||
|             flex: 0 0 auto; | ||||
| @@ -113,8 +116,7 @@ | ||||
|  | ||||
|         &__container-holder { | ||||
|             display: flex; | ||||
|             flex: 1 1 100%; // Must be 100% to work | ||||
|             overflow: auto; | ||||
|             flex: 1 1 100%; // Must needs to be 100% to work | ||||
|  | ||||
|             // Columns by default | ||||
|             flex-direction: row; | ||||
| @@ -290,6 +292,11 @@ | ||||
|             margin-bottom: $interiorMargin; | ||||
|         } | ||||
|  | ||||
|         &__object-view { | ||||
|             flex: 1 1 auto; | ||||
|             overflow: auto; | ||||
|         } | ||||
|  | ||||
|         &__size-indicator { | ||||
|             $size: 35px; | ||||
|  | ||||
| @@ -415,7 +422,6 @@ import Container from '../utils/container'; | ||||
| import Frame from '../utils/frame'; | ||||
| import ResizeHandle from  './resizeHandle.vue'; | ||||
| import DropHint from './dropHint.vue'; | ||||
| import RemoveAction from '../../remove/RemoveAction.js'; | ||||
|  | ||||
| const MIN_CONTAINER_SIZE = 5; | ||||
|  | ||||
| @@ -500,29 +506,10 @@ export default { | ||||
|             this.persist(); | ||||
|         }, | ||||
|         deleteContainer(containerId) { | ||||
|             let container = this.containers.filter(c => c.id === containerId)[0], | ||||
|                 containerIndex = this.containers.indexOf(container); | ||||
|  | ||||
|             /* | ||||
|                 remove associated domainObjects from composition | ||||
|             */ | ||||
|             container.frames.forEach(f => { | ||||
|                 this.removeFromComposition(f.domainObjectIdentifier); | ||||
|             }); | ||||
|  | ||||
|             let container = this.containers.filter(c => c.id === containerId)[0]; | ||||
|             let containerIndex = this.containers.indexOf(container); | ||||
|             this.containers.splice(containerIndex, 1); | ||||
|  | ||||
|             /* | ||||
|                 add a container when there are no containers in the FL, | ||||
|                 to prevent user from not being able to add a frame via | ||||
|                 drag and drop.  | ||||
|             */ | ||||
|             if (this.containers.length === 0) { | ||||
|                 this.containers.push(new Container(100)); | ||||
|             } | ||||
|  | ||||
|             sizeToFill(this.containers); | ||||
|             this.setSelectionToParent(); | ||||
|             this.persist(); | ||||
|         }, | ||||
|         moveFrame(toContainerIndex, toFrameIndex, frameId, fromContainerIndex) { | ||||
| @@ -556,23 +543,14 @@ export default { | ||||
|         deleteFrame(frameId) { | ||||
|             let container = this.containers | ||||
|                 .filter(c => c.frames.some(f => f.id === frameId))[0]; | ||||
|             let containerIndex = this.containers.indexOf(container); | ||||
|             let frame = container | ||||
|                 .frames | ||||
|                 .filter((f => f.id === frameId))[0]; | ||||
|  | ||||
|             this.removeFromComposition(frame.domainObjectIdentifier) | ||||
|                 .then(() => { | ||||
|                     sizeToFill(container.frames) | ||||
|                     this.setSelectionToParent(); | ||||
|                 }); | ||||
|         }, | ||||
|         removeFromComposition(identifier) { | ||||
|             return this.openmct.objects.get(identifier).then((childDomainObject) => { | ||||
|                 this.RemoveAction.removeFromComposition(this.domainObject, childDomainObject); | ||||
|             }); | ||||
|         }, | ||||
|         setSelectionToParent() { | ||||
|             this.$el.click(); | ||||
|             let frameIndex = container.frames.indexOf(frame); | ||||
|             container.frames.splice(frameIndex, 1); | ||||
|             sizeToFill(container.frames); | ||||
|             this.persist(containerIndex); | ||||
|         }, | ||||
|         allowContainerDrop(event, index) { | ||||
|             if (!event.dataTransfer.types.includes('containerid')) { | ||||
| @@ -642,7 +620,7 @@ export default { | ||||
|             } else { | ||||
|                 this.containers.splice(toIndex, 0, container); | ||||
|             } | ||||
|             this.persist(); | ||||
|             this.persist(index); | ||||
|         }, | ||||
|         removeChildObject(identifier) { | ||||
|             let removeIdentifier = this.openmct.objects.makeKeyString(identifier); | ||||
| @@ -663,8 +641,6 @@ export default { | ||||
|         this.composition.on('remove', this.removeChildObject); | ||||
|         this.composition.on('add', this.addFrame); | ||||
|  | ||||
|         this.RemoveAction = new RemoveAction(this.openmct); | ||||
|  | ||||
|         this.unobserve = this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|   | ||||
| @@ -36,7 +36,6 @@ | ||||
|                 :domain-object="domainObject" | ||||
|                 :object-path="objectPath" | ||||
|                 :has-frame="hasFrame" | ||||
|                 :show-edit-view="false" | ||||
|                 ref="objectFrame"> | ||||
|             </object-frame> | ||||
|  | ||||
| @@ -79,14 +78,12 @@ export default { | ||||
|         }, | ||||
|         setSelection() { | ||||
|             this.$nextTick(function () { | ||||
|                 if (this.$refs && this.$refs.objectFrame) { | ||||
|                     let childContext = this.$refs.objectFrame.getSelectionContext(); | ||||
|                     childContext.item = this.domainObject; | ||||
|                     childContext.type = 'frame'; | ||||
|                     childContext.frameId = this.frame.id; | ||||
|                     this.unsubscribeSelection = this.openmct.selection.selectable( | ||||
|                         this.$refs.frame, childContext, false); | ||||
|                 } | ||||
|                 let childContext = this.$refs.objectFrame.getSelectionContext(); | ||||
|                 childContext.item = this.domainObject; | ||||
|                 childContext.type = 'frame'; | ||||
|                 childContext.frameId = this.frame.id; | ||||
|                 this.unsubscribeSelection = this.openmct.selection.selectable( | ||||
|                     this.$refs.frame, childContext, false); | ||||
|             }); | ||||
|         }, | ||||
|         initDrag(event) { | ||||
|   | ||||
| @@ -79,12 +79,10 @@ export default { | ||||
|     mounted() { | ||||
|         document.addEventListener('dragstart', this.setDragging); | ||||
|         document.addEventListener('dragend', this.unsetDragging); | ||||
|         document.addEventListener('drop', this.unsetDragging); | ||||
|     }, | ||||
|     destroyed() { | ||||
|         document.removeEventListener('dragstart', this.setDragging); | ||||
|         document.removeEventListener('dragend', this.unsetDragging); | ||||
|         document.removeEventListener('drop', this.unsetDragging); | ||||
|     } | ||||
| } | ||||
| </script> | ||||
|   | ||||
| @@ -57,7 +57,7 @@ define([ | ||||
|                                 layoutObject: domainObject | ||||
|                             }, | ||||
|                             el: element, | ||||
|                             template: '<flexible-layout-component ref="flexibleLayout" :isEditing="isEditing"></flexible-layout-component>' | ||||
|                             template: '<flexible-layout-component ref="flexibleLayout"></flexible-layout-component>' | ||||
|                         }); | ||||
|                     }, | ||||
|                     getSelectionContext: function () { | ||||
|   | ||||
| @@ -27,22 +27,28 @@ function ToolbarProvider(openmct) { | ||||
|         key: "flex-layout", | ||||
|         description: "A toolbar for objects inside a Flexible Layout.", | ||||
|         forSelection: function (selection) { | ||||
|             let context = selection[0][0].context; | ||||
|             let context = selection[0].context; | ||||
|  | ||||
|             return (context && context.type && | ||||
|             return (openmct.editor.isEditing() && context && context.type && | ||||
|                 (context.type === 'flexible-layout' || context.type === 'container' || context.type === 'frame')); | ||||
|         }, | ||||
|         toolbar: function (selection) { | ||||
|  | ||||
|             let selectionPath = selection[0], | ||||
|                 primary = selectionPath[0], | ||||
|                 secondary = selectionPath[1], | ||||
|                 tertiary = selectionPath[2], | ||||
|             let primary = selection[0], | ||||
|                 secondary = selection[1], | ||||
|                 tertiary = selection[2], | ||||
|                 deleteFrame, | ||||
|                 toggleContainer, | ||||
|                 deleteContainer, | ||||
|                 addContainer, | ||||
|                 toggleFrame; | ||||
|                 toggleFrame, | ||||
|                 separator; | ||||
|  | ||||
|             separator = { | ||||
|                 control: "separator", | ||||
|                 domainObject: selection[0].context.item, | ||||
|                 key: "separator" | ||||
|             }; | ||||
|  | ||||
|             toggleContainer = { | ||||
|                 control: 'toggle-button', | ||||
| @@ -63,12 +69,6 @@ function ToolbarProvider(openmct) { | ||||
|                 ] | ||||
|             }; | ||||
|  | ||||
|             function getSeparator() { | ||||
|                 return { | ||||
|                     control: "separator" | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             if (primary.context.type === 'frame') { | ||||
|                 let frameId = primary.context.frameId; | ||||
|                 let layoutObject = tertiary.context.item; | ||||
| @@ -77,11 +77,11 @@ function ToolbarProvider(openmct) { | ||||
|                     .containers; | ||||
|                 let container = containers | ||||
|                     .filter(c => c.frames.some(f => f.id === frameId))[0]; | ||||
|                 let containerIndex = containers.indexOf(container); | ||||
|                 let frame = container && container | ||||
|                 let frame = container | ||||
|                     .frames | ||||
|                     .filter((f => f.id === frameId))[0]; | ||||
|                 let frameIndex = container && container.frames.indexOf(frame); | ||||
|                 let containerIndex = containers.indexOf(container); | ||||
|                 let frameIndex = container.frames.indexOf(frame); | ||||
|  | ||||
|                 deleteFrame = { | ||||
|                     control: "button", | ||||
| @@ -202,9 +202,9 @@ function ToolbarProvider(openmct) { | ||||
|             let toolbar = [ | ||||
|                 toggleContainer, | ||||
|                 addContainer, | ||||
|                 toggleFrame ? getSeparator() : undefined, | ||||
|                 toggleFrame ? separator: undefined, | ||||
|                 toggleFrame, | ||||
|                 deleteFrame || deleteContainer ? getSeparator() : undefined, | ||||
|                 deleteFrame || deleteContainer ? separator: undefined, | ||||
|                 deleteFrame, | ||||
|                 deleteContainer | ||||
|             ]; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <template> | ||||
|     <a class="l-grid-view__item c-grid-item" | ||||
|         :class="{ 'is-alias': item.isAlias === true, 'c-grid-item--unknown': item.type.cssClass === undefined || item.type.cssClass.indexOf('unknown') !== -1 }" | ||||
|         :class="{ 'is-alias': item.isAlias === true }" | ||||
|         :href="objectLink"> | ||||
|         <div class="c-grid-item__type-icon" | ||||
|              :class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'"> | ||||
| @@ -16,7 +16,7 @@ | ||||
|         </div> | ||||
|         <div class="c-grid-item__controls"> | ||||
|             <div class="icon-people" title='Shared'></div> | ||||
|             <button class="c-icon-button icon-info c-info-button" title='More Info'></button> | ||||
|             <button class="c-click-icon icon-info c-info-button" title='More Info'></button> | ||||
|             <div class="icon-pointer-right c-pointer-icon"></div> | ||||
|         </div> | ||||
|     </a> | ||||
| @@ -34,13 +34,10 @@ | ||||
|         padding: $interiorMarginLg; | ||||
|  | ||||
|         &__type-icon { | ||||
|             filter: $colorKeyFilter; | ||||
|             flex: 0 0 $gridItemMobile; | ||||
|             font-size: floor($gridItemMobile / 2); | ||||
|             margin-right: $interiorMarginLg; | ||||
|             &:before { | ||||
|                 filter: $colorKeyFilter; | ||||
|                 height: 100%; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &.is-alias { | ||||
| @@ -51,22 +48,6 @@ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &--unknown { | ||||
|             @include isUnknown(); | ||||
|             /*[class*='__'] { | ||||
|                 opacity: 0.7; | ||||
|             } | ||||
|              | ||||
|             [class*='__name'], | ||||
|             [class*='__metadata'] { | ||||
|                 font-style: italic; | ||||
|             }*/ | ||||
|  | ||||
|             [class*='__type-icon__glyph'] { | ||||
|                 filter: $filterItemUnknown; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &__details { | ||||
|             display: flex; | ||||
|             flex-flow: column nowrap; | ||||
| @@ -112,10 +93,11 @@ | ||||
|             transition: background $transOutMs ease-in-out; | ||||
|  | ||||
|             &:hover { | ||||
|                 filter: $filterItemHoverFg; | ||||
|                 //transition: $transIn; | ||||
|                 background: $colorItemBgHov; | ||||
|                 transition: $transIn; | ||||
|  | ||||
|                 .c-grid-item__type-icon { | ||||
|                     filter: $colorKeyFilterHov; | ||||
|                     transform: scale(1); | ||||
|                     transition: $transInBounce; | ||||
|                 } | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user