Compare commits
	
		
			3 Commits
		
	
	
		
			release/2.
			...
			open627_re
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | a515ccfb61 | ||
|   | 560897fe81 | ||
|   | e1d20e7275 | 
| @@ -80,7 +80,6 @@ define([ | ||||
|                         "$scope", | ||||
|                         "$route", | ||||
|                         "$location", | ||||
|                         "$q", | ||||
|                         "objectService", | ||||
|                         "navigationService", | ||||
|                         "urlService" | ||||
| @@ -202,6 +201,10 @@ define([ | ||||
|                 { | ||||
|                     "key": "inspector-region", | ||||
|                     "templateUrl": "templates/browse/inspector-region.html" | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "view-region", | ||||
|                     "templateUrl": "templates/view-region.html" | ||||
|                 } | ||||
|             ], | ||||
|             "services": [ | ||||
|   | ||||
| @@ -47,11 +47,6 @@ | ||||
|         <div class="holder l-flex-col flex-elem grows l-object-wrapper-inner"> | ||||
|             <!-- Toolbar and Save/Cancel buttons --> | ||||
|             <div class="l-edit-controls flex-elem l-flex-row flex-align-end"> | ||||
|                 <mct-toolbar name="mctToolbar" | ||||
|                              structure="toolbar.structure" | ||||
|                              ng-model="toolbar.state" | ||||
|                              class="flex-elem grows"> | ||||
|                 </mct-toolbar> | ||||
|                 <mct-representation key="'edit-action-buttons'" | ||||
|                                     mct-object="domainObject" | ||||
|                                     class='flex-elem conclude-editing'> | ||||
|   | ||||
| @@ -63,7 +63,7 @@ | ||||
|                     <mct-split-pane class='l-object-and-inspector contents abs' anchor='right'> | ||||
|                         <div class='split-pane-component t-object pane primary-pane left'> | ||||
|                             <mct-representation mct-object="navigatedObject" | ||||
|                                                 key="'browse-object'" | ||||
|                                                 key="'view-region'" | ||||
|                                                 class="abs holder holder-object"> | ||||
|                             </mct-representation> | ||||
|                         </div> | ||||
| @@ -87,4 +87,3 @@ | ||||
|     </div> | ||||
|     <mct-include key="'bottombar'"></mct-include> | ||||
| </div> | ||||
|  | ||||
|   | ||||
| @@ -19,14 +19,12 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <div content="jquery-wrapper" | ||||
|      class="abs holder-all edit-mode" | ||||
|      ng-controller="EditController as editMode" | ||||
|      mct-before-unload="editMode.getUnloadWarning()"> | ||||
| 
 | ||||
|     <mct-representation key="'edit-object'" mct-object="editMode.navigatedObject()"> | ||||
|     </mct-representation> | ||||
| 
 | ||||
|     <mct-include key="'bottombar'"></mct-include> | ||||
| 
 | ||||
| </div> | ||||
| <div ng-controller="RegionController as regionController"> | ||||
|     <div ng-repeat="part in regions.view.parts"> | ||||
|         <mct-representation | ||||
|                 key="part.content.key" | ||||
|                 mct-object="domainObject" | ||||
|                 ng-model="ngModel"> | ||||
|         </mct-representation> | ||||
|     </div> | ||||
| </div><!--/ PaneController --> | ||||
| @@ -27,10 +27,9 @@ | ||||
|  */ | ||||
| define( | ||||
|     [ | ||||
|         '../../../representation/src/gestures/GestureConstants', | ||||
|         '../../edit/src/objects/EditableDomainObject' | ||||
|         '../../../representation/src/gestures/GestureConstants' | ||||
|     ], | ||||
|     function (GestureConstants, EditableDomainObject) { | ||||
|     function (GestureConstants) { | ||||
|         "use strict"; | ||||
|  | ||||
|         var ROOT_ID = "ROOT", | ||||
| @@ -47,18 +46,11 @@ define( | ||||
|          * @memberof platform/commonUI/browse | ||||
|          * @constructor | ||||
|          */ | ||||
|         function BrowseController($scope, $route, $location, $q, objectService, navigationService, urlService) { | ||||
|         function BrowseController($scope, $route, $location, objectService, navigationService, urlService) { | ||||
|             var path = [ROOT_ID].concat( | ||||
|                 ($route.current.params.ids || DEFAULT_PATH).split("/") | ||||
|             ); | ||||
|  | ||||
|             function isDirty(){ | ||||
|                 var editorCapability = $scope.navigatedObject && | ||||
|                         $scope.navigatedObject.getCapability("editor"), | ||||
|                     hasChanges = editorCapability && editorCapability.dirty(); | ||||
|                 return hasChanges; | ||||
|             } | ||||
|  | ||||
|             function updateRoute(domainObject) { | ||||
|                 var priorRoute = $route.current, | ||||
|                     // Act as if params HADN'T changed to avoid page reload | ||||
| @@ -75,9 +67,7 @@ define( | ||||
|                 // urlService.urlForLocation used to adjust current | ||||
|                 // path to new, addressed, path based on | ||||
|                 // domainObject | ||||
|                 $location.path(urlService.urlForLocation("browse", | ||||
|                     domainObject.hasCapability('editor') ? | ||||
|                         domainObject.getOriginalObject() : domainObject)); | ||||
|                 $location.path(urlService.urlForLocation("browse", domainObject)); | ||||
|  | ||||
|             } | ||||
|  | ||||
| @@ -89,17 +79,15 @@ define( | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 if (isDirty() && !confirm(CONFIRM_MSG)) { | ||||
|                     $scope.treeModel.selectedObject = $scope.navigatedObject; | ||||
|                     navigationService.setNavigation($scope.navigatedObject); | ||||
|                 } else { | ||||
|                     if ($scope.navigatedObject && $scope.navigatedObject.hasCapability("editor")){ | ||||
|                         $scope.navigatedObject.getCapability("editor").cancel(); | ||||
|                     } | ||||
|                 if (navigationService.setNavigation(domainObject)) { | ||||
|                     $scope.navigatedObject = domainObject; | ||||
|                     $scope.treeModel.selectedObject = domainObject; | ||||
|                     navigationService.setNavigation(domainObject); | ||||
|                     updateRoute(domainObject); | ||||
|                 } else { | ||||
|                     //If navigation was unsuccessful (ie. blocked), reset | ||||
|                     // the selected object in the tree to the currently | ||||
|                     // navigated object | ||||
|                     $scope.treeModel.selectedObject = $scope.navigatedObject ; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -170,18 +158,13 @@ define( | ||||
|                 selectedObject: navigationService.getNavigation() | ||||
|             }; | ||||
|  | ||||
|             $scope.beforeUnloadWarning = function() { | ||||
|                 return isDirty() ? | ||||
|                     "Unsaved changes will be lost if you leave this page." : | ||||
|                     undefined; | ||||
|             }; | ||||
|  | ||||
|             // Listen for changes in navigation state. | ||||
|             navigationService.addListener(setNavigation); | ||||
|  | ||||
|             // Also listen for changes which come from the tree | ||||
|             // Also listen for changes which come from the tree. Changes in | ||||
|             // the tree will trigger a change in browse navigation state. | ||||
|             $scope.$watch("treeModel.selectedObject", setNavigation); | ||||
|              | ||||
|  | ||||
|             // Clean up when the scope is destroyed | ||||
|             $scope.$on("$destroy", function () { | ||||
|                 navigationService.removeListener(setNavigation); | ||||
| @@ -191,4 +174,3 @@ define( | ||||
|         return BrowseController; | ||||
|     } | ||||
| ); | ||||
|  | ||||
|   | ||||
| @@ -22,11 +22,8 @@ | ||||
| /*global define,Promise*/ | ||||
|  | ||||
| define( | ||||
|     [ | ||||
|         '../../../representation/src/gestures/GestureConstants', | ||||
|         '../../edit/src/objects/EditableDomainObject' | ||||
|     ], | ||||
|     function (GestureConstants, EditableDomainObject) { | ||||
|     [], | ||||
|     function () { | ||||
|         "use strict"; | ||||
|  | ||||
|         /** | ||||
| @@ -57,10 +54,9 @@ define( | ||||
|  | ||||
|             function updateQueryParam(viewKey) { | ||||
|                 var unlisten, | ||||
|                     priorRoute = $route.current, | ||||
|                     isEditMode = $scope.domainObject && $scope.domainObject.hasCapability('editor'); | ||||
|                     priorRoute = $route.current; | ||||
|  | ||||
|                 if (viewKey && !isEditMode) { | ||||
|                 if (viewKey) { | ||||
|                     $location.search('view', viewKey); | ||||
|                     unlisten = $scope.$on('$locationChangeSuccess', function () { | ||||
|                         // Checks path to make sure /browse/ is at front | ||||
| @@ -76,10 +72,6 @@ define( | ||||
|             $scope.$watch('domainObject', setViewForDomainObject); | ||||
|             $scope.$watch('representation.selected.key', updateQueryParam); | ||||
|  | ||||
|             $scope.cancelEditing = function() { | ||||
|                 navigationService.setNavigation($scope.domainObject.getDomainObject()); | ||||
|             }; | ||||
|  | ||||
|             $scope.doAction = function (action){ | ||||
|                 return $scope[action] && $scope[action](); | ||||
|             }; | ||||
| @@ -89,4 +81,3 @@ define( | ||||
|         return BrowseObjectController; | ||||
|     } | ||||
| ); | ||||
|  | ||||
|   | ||||
| @@ -23,9 +23,10 @@ | ||||
|  | ||||
| define( | ||||
|     [ | ||||
|         './InspectorRegion' | ||||
|         './InspectorRegion', | ||||
|         './ViewRegion' | ||||
|     ], | ||||
|     function (InspectorRegion) { | ||||
|     function (InspectorRegion, ViewRegion) { | ||||
|         "use strict"; | ||||
|  | ||||
|         /** | ||||
| @@ -53,6 +54,7 @@ define( | ||||
|             var regions = type.getDefinition().regions || {}; | ||||
|  | ||||
|             regions.inspector = regions.inspector || new InspectorRegion(); | ||||
|             regions.view = regions.view || new ViewRegion(); | ||||
|  | ||||
|             type.getDefinition().regions = regions; | ||||
|  | ||||
|   | ||||
							
								
								
									
										73
									
								
								platform/commonUI/browse/src/ViewRegion.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								platform/commonUI/browse/src/ViewRegion.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| /***************************************************************************** | ||||
|  * Open MCT Web, Copyright (c) 2014-2015, United States Government | ||||
|  * as represented by the Administrator of the National Aeronautics and Space | ||||
|  * Administration. All rights reserved. | ||||
|  * | ||||
|  * Open MCT Web 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 Web 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. | ||||
|  *****************************************************************************/ | ||||
| /*global define,window*/ | ||||
|  | ||||
| define( | ||||
|     [ | ||||
|         '../../regions/src/Region' | ||||
|     ], | ||||
|     function (Region) { | ||||
|         "use strict"; | ||||
|  | ||||
|         /** | ||||
|          * Defines the default View region. Captured in a class to | ||||
|          * allow for modular extension and customization of regions based on | ||||
|          * the typical case. | ||||
|          * @memberOf platform/commonUI/regions | ||||
|          * @constructor | ||||
|          */ | ||||
|         function ViewRegion() { | ||||
|             Region.call(this); | ||||
|  | ||||
|             this.buildRegion(); | ||||
|         } | ||||
|  | ||||
|         ViewRegion.prototype = Object.create(Region.prototype); | ||||
|         ViewRegion.prototype.constructor = Region; | ||||
|  | ||||
|         /** | ||||
|          * @private | ||||
|          */ | ||||
|         ViewRegion.prototype.buildRegion = function() { | ||||
|             var browseViewPart = { | ||||
|                     name: 'browse-view', | ||||
|                     title: 'Browse Object View', | ||||
|                     modes: ['browse'], | ||||
|                     content: { | ||||
|                         key: 'browse-object' | ||||
|                     } | ||||
|                 }, | ||||
|                 editViewPart = { | ||||
|                     name: 'edit-view', | ||||
|                     title: 'Edit Object View', | ||||
|                     modes: ['edit'], | ||||
|                     content: { | ||||
|                         key: 'edit-object' | ||||
|                     } | ||||
|                 }; | ||||
|             this.addPart(browseViewPart); | ||||
|             this.addPart(editViewPart); | ||||
|         }; | ||||
|  | ||||
|         return ViewRegion; | ||||
|     } | ||||
| ); | ||||
| @@ -37,7 +37,7 @@ define( | ||||
|          */ | ||||
|         function NavigationService() { | ||||
|             this.navigated = undefined; | ||||
|             this.callbacks = []; | ||||
|             this.callbacks = {}; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
| @@ -50,26 +50,48 @@ define( | ||||
|  | ||||
|         /** | ||||
|          * Set the current navigation state. This will invoke listeners. | ||||
|          * Changing the navigation state will be blocked if any of the | ||||
|          * 'before' navigation state change listeners return 'false'. | ||||
|          * @param {DomainObject} domainObject the domain object to navigate to | ||||
|          */ | ||||
|         NavigationService.prototype.setNavigation = function (value) { | ||||
|             var canNavigate = true; | ||||
|             if (this.navigated !== value) { | ||||
|                 this.navigated = value; | ||||
|                 this.callbacks.forEach(function (callback) { | ||||
|                     callback(value); | ||||
|                 }); | ||||
|                 canNavigate = (this.callbacks.before || []) | ||||
|                     .reduce(function (previous, callback) { | ||||
|                         //Check whether the callback returned a value of | ||||
|                         // 'false' indicating that navigation should not | ||||
|                         // continue. All other return values will allow | ||||
|                         // navigation to continue | ||||
|                         return (callback(value)!==false) && previous; | ||||
|                     }, true); | ||||
|                 if (canNavigate) { | ||||
|                     this.navigated = value; | ||||
|                     (this.callbacks.after || []).forEach(function (callback) { | ||||
|                         callback(value); | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|             return canNavigate; | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|          * Listen for changes in navigation. The passed callback will | ||||
|          * be invoked with the new domain object of navigation when | ||||
|          * this changes. | ||||
|          * this changes. Callbacks can be registered to listen to pre or | ||||
|          * post-navigation events. The event to listen to is specified using | ||||
|          * the event parameter. In the case of pre-navigation events | ||||
|          * returning a false value will prevent the navigation event from | ||||
|          * going ahead. | ||||
|          * @param {function} callback the callback to invoke when | ||||
|          *        navigation state changes | ||||
|          * @param {string} [event=after] the navigation event to listen to. | ||||
|          * One of 'before' or 'after'. | ||||
|          */ | ||||
|         NavigationService.prototype.addListener = function (callback) { | ||||
|             this.callbacks.push(callback); | ||||
|         NavigationService.prototype.addListener = function (callback, event) { | ||||
|             event = event || 'after'; | ||||
|             this.callbacks[event] = this.callbacks[event] || []; | ||||
|             this.callbacks[event].push(callback); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
| @@ -77,9 +99,12 @@ define( | ||||
|          * @param {function} callback the callback which should | ||||
|          *        no longer be invoked when navigation state | ||||
|          *        changes | ||||
|          * @param {string} [event=after] the navigation event that the | ||||
|          * callback is registered to. One of 'before' or 'after'. | ||||
|          */ | ||||
|         NavigationService.prototype.removeListener = function (callback) { | ||||
|             this.callbacks = this.callbacks.filter(function (cb) { | ||||
|         NavigationService.prototype.removeListener = function (callback, event) { | ||||
|             event = event || 'after'; | ||||
|             this.callbacks[event] = this.callbacks[event].filter(function (cb) { | ||||
|                 return cb !== callback; | ||||
|             }); | ||||
|         }; | ||||
|   | ||||
| @@ -29,8 +29,7 @@ define( | ||||
|     function (BrowseController) { | ||||
|         "use strict"; | ||||
|  | ||||
|         //TODO: Disabled for NEM Beta | ||||
|         xdescribe("The browse controller", function () { | ||||
|         describe("The browse controller", function () { | ||||
|             var mockScope, | ||||
|                 mockRoute, | ||||
|                 mockLocation, | ||||
| @@ -230,7 +229,10 @@ define( | ||||
|                 // prior to setting $route.current | ||||
|                 mockLocation.path.andReturn("/browse/"); | ||||
|  | ||||
|                 mockNavigationService.setNavigation.andReturn(true); | ||||
|  | ||||
|                 // Exercise the Angular workaround | ||||
|                 mockNavigationService.addListener.mostRecentCall.args[0](); | ||||
|                 mockScope.$on.mostRecentCall.args[1](); | ||||
|                 expect(mockUnlisten).toHaveBeenCalled(); | ||||
|  | ||||
| @@ -241,6 +243,32 @@ define( | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("after successful navigation event sets the selected tree " + | ||||
|                 "object", function () { | ||||
|                 mockScope.navigatedObject = mockDomainObject; | ||||
|                 mockNavigationService.setNavigation.andReturn(true); | ||||
|  | ||||
|                 //Simulate a change in selected tree object | ||||
|                 mockScope.treeModel = {selectedObject: mockDomainObject}; | ||||
|                 mockScope.$watch.mostRecentCall.args[1](mockNextObject); | ||||
|  | ||||
|                 expect(mockScope.treeModel.selectedObject).toBe(mockNextObject); | ||||
|                 expect(mockScope.treeModel.selectedObject).not.toBe(mockDomainObject); | ||||
|             }); | ||||
|  | ||||
|             it("after failed navigation event resets the selected tree" + | ||||
|                 " object", function () { | ||||
|                 mockScope.navigatedObject = mockDomainObject; | ||||
|                 mockNavigationService.setNavigation.andReturn(false); | ||||
|  | ||||
|                 //Simulate a change in selected tree object | ||||
|                 mockScope.treeModel = {selectedObject: mockDomainObject}; | ||||
|                 mockScope.$watch.mostRecentCall.args[1](mockNextObject); | ||||
|  | ||||
|                 expect(mockScope.treeModel.selectedObject).not.toBe(mockNextObject); | ||||
|                 expect(mockScope.treeModel.selectedObject).toBe(mockDomainObject); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
|   | ||||
| @@ -84,6 +84,24 @@ define( | ||||
|                 expect(callback).not.toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|             it("adds listeners to the 'after' state by default", function(){ | ||||
|                 expect(navigationService.callbacks.after).toBeUndefined(); | ||||
|                 navigationService.addListener(function(){}); | ||||
|                 expect(navigationService.callbacks.after).toBeDefined(); | ||||
|                 expect(navigationService.callbacks.after.length).toBe(1); | ||||
|             }); | ||||
|  | ||||
|             it("allows navigationService events to be prevented", function(){ | ||||
|                 var callback = jasmine.createSpy("callback"), | ||||
|                     navigationResult; | ||||
|                 callback.andReturn(false); | ||||
|                 navigationService.addListener(callback, "before"); | ||||
|                 navigationResult = navigationService.setNavigation({}); | ||||
|                 expect(callback).toHaveBeenCalled(); | ||||
|                 expect(navigationResult).toBe(false); | ||||
|  | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| ); | ||||
|   | ||||
| @@ -22,10 +22,10 @@ | ||||
| /*global define*/ | ||||
|  | ||||
| define([ | ||||
|     "./src/controllers/EditController", | ||||
|     "./src/controllers/EditActionController", | ||||
|     "./src/controllers/EditPanesController", | ||||
|     "./src/controllers/ElementsController", | ||||
|     "./src/controllers/EditObjectController", | ||||
|     "./src/directives/MCTBeforeUnload", | ||||
|     "./src/actions/LinkAction", | ||||
|     "./src/actions/EditAction", | ||||
| @@ -38,10 +38,10 @@ define([ | ||||
|     "./src/representers/EditToolbarRepresenter", | ||||
|     'legacyRegistry' | ||||
| ], function ( | ||||
|     EditController, | ||||
|     EditActionController, | ||||
|     EditPanesController, | ||||
|     ElementsController, | ||||
|     EditObjectController, | ||||
|     MCTBeforeUnload, | ||||
|     LinkAction, | ||||
|     EditAction, | ||||
| @@ -58,22 +58,7 @@ define([ | ||||
|  | ||||
|     legacyRegistry.register("platform/commonUI/edit", { | ||||
|         "extensions": { | ||||
|             "routes": [ | ||||
|                 { | ||||
|                     "when": "/edit", | ||||
|                     "templateUrl": "templates/edit.html" | ||||
|                 } | ||||
|             ], | ||||
|             "controllers": [ | ||||
|                 { | ||||
|                     "key": "EditController", | ||||
|                     "implementation": EditController, | ||||
|                     "depends": [ | ||||
|                         "$scope", | ||||
|                         "$q", | ||||
|                         "navigationService" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "EditActionController", | ||||
|                     "implementation": EditActionController, | ||||
| @@ -94,6 +79,14 @@ define([ | ||||
|                     "depends": [ | ||||
|                         "$scope" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     "key": "EditObjectController", | ||||
|                     "implementation": EditObjectController, | ||||
|                     "depends": [ | ||||
|                         "$scope", | ||||
|                         "$location" | ||||
|                     ] | ||||
|                 } | ||||
|             ], | ||||
|             "directives": [ | ||||
| @@ -101,7 +94,8 @@ define([ | ||||
|                     "key": "mctBeforeUnload", | ||||
|                     "implementation": MCTBeforeUnload, | ||||
|                     "depends": [ | ||||
|                         "$window" | ||||
|                         "$window", | ||||
|                         "navigationService" | ||||
|                     ] | ||||
|                 } | ||||
|             ], | ||||
| @@ -197,6 +191,9 @@ define([ | ||||
|                     "templateUrl": "templates/edit-object.html", | ||||
|                     "uses": [ | ||||
|                         "view" | ||||
|                     ], | ||||
|                     "gestures": [ | ||||
|                         "drop" | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|   | ||||
| @@ -19,50 +19,51 @@ | ||||
|  this source code distribution or the Licensing information page available | ||||
|  at runtime from the About dialog for additional information. | ||||
| --> | ||||
| <mct-representation key="'topbar-edit'" | ||||
|                     mct-object="domainObject" | ||||
|                     ng-model="representation"> | ||||
| </mct-representation> | ||||
| <div class="holder edit-area abs"> | ||||
|     <mct-split-pane class='contents abs' anchor='right'> | ||||
|         <div class='split-pane-component pane left edit-main'> | ||||
|             <mct-toolbar name="mctToolbar" | ||||
|                          structure="toolbar.structure" | ||||
|                          ng-model="toolbar.state"> | ||||
|             </mct-toolbar> | ||||
|             <mct-representation key="representation.selected.key" | ||||
|                                 toolbar="toolbar" | ||||
|                                 mct-object="representation.selected.key && domainObject" | ||||
|                                 class="holder abs object-holder work-area"> | ||||
| <div class="abs l-flex-col" ng-controller="EditObjectController as EditObjectController"> | ||||
|     <div mct-before-unload="EditObjectController.getUnloadWarning()" | ||||
|          class="holder flex-elem l-flex-row object-browse-bar "> | ||||
|         <div class="items-select left flex-elem l-flex-row grows"> | ||||
|             <mct-representation key="'back-arrow'" | ||||
|                                 mct-object="domainObject" | ||||
|                                 class="flex-elem l-back"></mct-representation> | ||||
|             <mct-representation key="'object-header'" | ||||
|                                 mct-object="domainObject" | ||||
|                                 class="l-flex-row flex-elem grows object-header"> | ||||
|             </mct-representation> | ||||
|         </div> | ||||
|         <mct-splitter></mct-splitter> | ||||
|         <div | ||||
|             class='split-pane-component pane right edit-objects menus-to-left' | ||||
|             ng-controller='EditPanesController as editPanes' | ||||
|             > | ||||
|             <mct-split-pane class='contents abs' anchor='bottom'> | ||||
|                 <div | ||||
|                     class="abs pane top accordion" | ||||
|                     ng-controller="ToggleController as toggle" | ||||
|                     > | ||||
|                     <mct-container key="accordion" label="Library"> | ||||
|                         <mct-representation key="'tree'" | ||||
|                                             mct-object="editPanes.getRoot()"> | ||||
|                         </mct-representation> | ||||
|                     </mct-container> | ||||
|                 </div> | ||||
|                 <mct-splitter></mct-splitter> | ||||
|                 <div | ||||
|                     class="abs pane bottom accordion" | ||||
|                     ng-controller="ToggleController as toggle" | ||||
|                     > | ||||
|                     <mct-container key="accordion" label="Elements"> | ||||
|                         <mct-representation key="'edit-elements'" mct-object="domainObject"> | ||||
|                         </mct-representation> | ||||
|                     </mct-container> | ||||
|                 </div> | ||||
|             </mct-split-pane> | ||||
|         <div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed"> | ||||
|             <mct-representation key="'switcher'" | ||||
|                                 mct-object="domainObject" | ||||
|                                 ng-model="representation"> | ||||
|             </mct-representation> | ||||
|             <!-- Temporarily, on mobile, the action buttons are hidden--> | ||||
|             <mct-representation key="'action-group'" | ||||
|                                 mct-object="domainObject" | ||||
|                                 parameters="{ category: 'view-control' }" | ||||
|                                 class="mobile-hide"> | ||||
|             </mct-representation> | ||||
|         </div> | ||||
|     </mct-split-pane> | ||||
|     </div> | ||||
|     <div class="holder l-flex-col flex-elem grows l-object-wrapper"> | ||||
|         <div class="holder l-flex-col flex-elem grows l-object-wrapper-inner"> | ||||
|             <!-- Toolbar and Save/Cancel buttons --> | ||||
|             <div class="l-edit-controls flex-elem l-flex-row flex-align-end"> | ||||
|                 <mct-toolbar name="mctToolbar" | ||||
|                              structure="toolbar.structure" | ||||
|                              ng-model="toolbar.state" | ||||
|                              class="flex-elem grows"> | ||||
|                 </mct-toolbar> | ||||
|                 <mct-representation key="'edit-action-buttons'" | ||||
|                                     mct-object="domainObject" | ||||
|                                     class='flex-elem conclude-editing'> | ||||
|                 </mct-representation> | ||||
|  | ||||
|             </div> | ||||
|             <mct-representation key="representation.selected.key" | ||||
|                                 mct-object="representation.selected.key && domainObject" | ||||
|                                 class="abs flex-elem grows object-holder-main scroll" | ||||
|                                 toolbar="toolbar"> | ||||
|             </mct-representation> | ||||
|         </div><!--/ l-object-wrapper-inner --> | ||||
|     </div> | ||||
| </div> | ||||
|   | ||||
| @@ -72,13 +72,26 @@ define( | ||||
|          * Enter edit mode. | ||||
|          */ | ||||
|         EditAction.prototype.perform = function () { | ||||
|             var editableObject; | ||||
|             var self = this; | ||||
|             if (!this.domainObject.hasCapability("editor")) { | ||||
|                 editableObject = new EditableDomainObject(this.domainObject, this.$q); | ||||
|                 editableObject.getCapability('status').set('editing', true); | ||||
|                 this.navigationService.setNavigation(editableObject); | ||||
|                 //TODO: This is only necessary because the drop gesture is | ||||
|                 // wrapping the object itself, need to refactor this later. | ||||
|                 // All responsibility for switching into edit mode should be | ||||
|                 // in the edit action, and not duplicated in the gesture | ||||
|                 this.domainObject = new EditableDomainObject(this.domainObject, this.$q); | ||||
|             } | ||||
|             //this.$location.path("/edit"); | ||||
|             this.navigationService.setNavigation(this.domainObject); | ||||
|             this.domainObject.getCapability('status').set('editing', true); | ||||
|  | ||||
|             //Register a listener to automatically cancel this edit action | ||||
|             //if the user navigates away from this object. | ||||
|             function cancelEditing(navigatedTo){ | ||||
|                 if (!navigatedTo || navigatedTo.getId() !== self.domainObject.getId()) { | ||||
|                     self.domainObject.getCapability('editor').cancel(); | ||||
|                     self.navigationService.removeListener(cancelEditing); | ||||
|                 } | ||||
|             } | ||||
|             this.navigationService.addListener(cancelEditing); | ||||
|         }; | ||||
|  | ||||
|         /** | ||||
|   | ||||
| @@ -26,41 +26,44 @@ | ||||
|  * @namespace platform/commonUI/edit | ||||
|  */ | ||||
| define( | ||||
|     ["../objects/EditableDomainObject"], | ||||
|     function (EditableDomainObject) { | ||||
|     [], | ||||
|     function () { | ||||
|         "use strict"; | ||||
| 
 | ||||
|         /** | ||||
|          * Controller which is responsible for populating the scope for | ||||
|          * Edit mode; introduces an editable version of the currently | ||||
|          * navigated domain object into the scope. | ||||
|          * Edit mode | ||||
|          * @memberof platform/commonUI/edit | ||||
|          * @constructor | ||||
|          */ | ||||
|         function EditController($scope, $q, navigationService) { | ||||
|             var self = this; | ||||
|         function EditObjectController($scope, $location) { | ||||
|             this.scope = $scope; | ||||
| 
 | ||||
|             function setNavigation(domainObject) { | ||||
|                 // Wrap the domain object such that all mutation is
 | ||||
|                 // confined to edit mode (until Save)
 | ||||
|                 self.navigatedDomainObject = | ||||
|                     domainObject && new EditableDomainObject(domainObject, $q); | ||||
|             var navigatedObject; | ||||
|             function setViewForDomainObject(domainObject) { | ||||
| 
 | ||||
|                 var locationViewKey = $location.search().view; | ||||
| 
 | ||||
|                 function selectViewIfMatching(view) { | ||||
|                     if (view.key === locationViewKey) { | ||||
|                         $scope.representation = $scope.representation || {}; | ||||
|                         $scope.representation.selected = view; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if (locationViewKey) { | ||||
|                     ((domainObject && domainObject.useCapability('view')) || []) | ||||
|                         .forEach(selectViewIfMatching); | ||||
|                 } | ||||
|                 navigatedObject = domainObject; | ||||
|             } | ||||
| 
 | ||||
|             setNavigation(navigationService.getNavigation()); | ||||
|             navigationService.addListener(setNavigation); | ||||
|             $scope.$on("$destroy", function () { | ||||
|                 navigationService.removeListener(setNavigation); | ||||
|             }); | ||||
|         } | ||||
|             $scope.$watch('domainObject', setViewForDomainObject); | ||||
| 
 | ||||
|         /** | ||||
|          * Get the domain object which is navigated-to. | ||||
|          * @returns {DomainObject} the domain object that is navigated-to | ||||
|          */ | ||||
|         EditController.prototype.navigatedObject = function () { | ||||
|             return this.navigatedDomainObject; | ||||
|         }; | ||||
|             $scope.doAction = function (action){ | ||||
|                 return $scope[action] && $scope[action](); | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Get the warning to show if the user attempts to navigate | ||||
| @@ -68,17 +71,20 @@ define( | ||||
|          * @returns {string} the warning to show, or undefined if | ||||
|          *          there are no unsaved changes | ||||
|          */ | ||||
|         EditController.prototype.getUnloadWarning = function () { | ||||
|             var navigatedObject = this.navigatedDomainObject, | ||||
|         EditObjectController.prototype.getUnloadWarning = function () { | ||||
|             var navigatedObject = this.scope.domainObject, | ||||
|                 editorCapability = navigatedObject && | ||||
|                     navigatedObject.getCapability("editor"), | ||||
|                 hasChanges = editorCapability && editorCapability.dirty(); | ||||
|                 statusCapability = navigatedObject && | ||||
|                     navigatedObject.getCapability("status"), | ||||
|                 hasChanges = statusCapability && statusCapability.get('editing') | ||||
|                     && editorCapability && editorCapability.dirty(); | ||||
| 
 | ||||
|             return hasChanges ? | ||||
|                 "Unsaved changes will be lost if you leave this page." : | ||||
|                 undefined; | ||||
|         }; | ||||
| 
 | ||||
|         return EditController; | ||||
|         return EditObjectController; | ||||
|     } | ||||
| ); | ||||
| @@ -35,7 +35,7 @@ define( | ||||
|          * @constructor | ||||
|          * @param $window the window | ||||
|          */ | ||||
|         function MCTBeforeUnload($window) { | ||||
|         function MCTBeforeUnload($window, navigationService) { | ||||
|             var unloads = [], | ||||
|                 oldBeforeUnload = $window.onbeforeunload; | ||||
|  | ||||
| @@ -55,8 +55,23 @@ define( | ||||
|                     return scope.$eval(attrs.mctBeforeUnload); | ||||
|                 } | ||||
|  | ||||
|                 function shouldAllowNavigation(){ | ||||
|                     // Get an unload message (if any) | ||||
|                     var warning = unload(); | ||||
|                     // Prompt the user if there's an unload message | ||||
|                     return !warning || $window.confirm(warning); | ||||
|                 } | ||||
|  | ||||
|                 // Show a dialog before allowing a location change | ||||
|                 function checkNavigationEvent(event) { | ||||
|                     // Return a false value to the navigationService to | ||||
|                     // indicate that the navigation event should be prevented | ||||
|                     return shouldAllowNavigation(); | ||||
|                 } | ||||
|  | ||||
|                 // Stop using this unload expression | ||||
|                 function removeUnload() { | ||||
|                     navigationService.removeListener(checkNavigationEvent, "before"); | ||||
|                     unloads = unloads.filter(function (callback) { | ||||
|                         return callback !== unload; | ||||
|                     }); | ||||
| @@ -67,11 +82,8 @@ define( | ||||
|  | ||||
|                 // Show a dialog before allowing a location change | ||||
|                 function checkLocationChange(event) { | ||||
|                     // Get an unload message (if any) | ||||
|                     var warning = unload(); | ||||
|                     // Prompt the user if there's an unload message | ||||
|                     if (warning && !$window.confirm(warning)) { | ||||
|                         // ...and prevent the route change if it was confirmed | ||||
|                     if (!shouldAllowNavigation()) { | ||||
|                         // Prevent the route change if it was confirmed | ||||
|                         event.preventDefault(); | ||||
|                     } | ||||
|                 } | ||||
| @@ -90,6 +102,8 @@ define( | ||||
|  | ||||
|                 // Also handle route changes | ||||
|                 scope.$on("$locationChangeStart", checkLocationChange); | ||||
|  | ||||
|                 navigationService.addListener(checkNavigationEvent, "before"); | ||||
|             } | ||||
|  | ||||
|             return { | ||||
|   | ||||
| @@ -22,89 +22,72 @@ | ||||
| /*global define,describe,it,expect,beforeEach,jasmine*/ | ||||
|  | ||||
| define( | ||||
|     ["../../src/controllers/EditController"], | ||||
|     function (EditController) { | ||||
|     ["../../src/controllers/EditObjectController"], | ||||
|     function (EditObjectController) { | ||||
|         "use strict"; | ||||
|  | ||||
|         describe("The Edit mode controller", function () { | ||||
|             var mockScope, | ||||
|                 mockQ, | ||||
|                 mockNavigationService, | ||||
|                 mockObject, | ||||
|                 mockType, | ||||
|                 mockLocation, | ||||
|                 mockStatusCapability, | ||||
|                 mockCapabilities, | ||||
|                 controller; | ||||
|  | ||||
|             // Utility function; look for a $watch on scope and fire it | ||||
|             function fireWatch(expr, value) { | ||||
|                 mockScope.$watch.calls.forEach(function (call) { | ||||
|                     if (call.args[0] === expr) { | ||||
|                         call.args[1](value); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 mockScope = jasmine.createSpyObj( | ||||
|                     "$scope", | ||||
|                     [ "$on" ] | ||||
|                 ); | ||||
|                 mockQ = jasmine.createSpyObj('$q', ['when', 'all']); | ||||
|                 mockNavigationService = jasmine.createSpyObj( | ||||
|                     "navigationService", | ||||
|                     [ "getNavigation", "addListener", "removeListener" ] | ||||
|                     [ "$on", "$watch" ] | ||||
|                 ); | ||||
|                 mockObject = jasmine.createSpyObj( | ||||
|                     "domainObject", | ||||
|                     [ "getId", "getModel", "getCapability", "hasCapability" ] | ||||
|                     [ "getId", "getModel", "getCapability", "hasCapability", "useCapability" ] | ||||
|                 ); | ||||
|                 mockType = jasmine.createSpyObj( | ||||
|                     "type", | ||||
|                     [ "hasFeature" ] | ||||
|                 ); | ||||
|                 mockStatusCapability = jasmine.createSpyObj('statusCapability', | ||||
|                     ["get"] | ||||
|                 ); | ||||
|  | ||||
|                 mockCapabilities = { | ||||
|                     "type" : mockType, | ||||
|                     "status": mockStatusCapability | ||||
|                 }; | ||||
|  | ||||
|                 mockLocation = jasmine.createSpyObj('$location', | ||||
|                     ["search"] | ||||
|                 ); | ||||
|                 mockLocation.search.andReturn({"view": "fixed"}); | ||||
|  | ||||
|                 mockNavigationService.getNavigation.andReturn(mockObject); | ||||
|                 mockObject.getId.andReturn("test"); | ||||
|                 mockObject.getModel.andReturn({ name: "Test object" }); | ||||
|                 mockObject.getCapability.andCallFake(function (key) { | ||||
|                     return key === 'type' && mockType; | ||||
|                     return mockCapabilities[key]; | ||||
|                 }); | ||||
|                 mockType.hasFeature.andReturn(true); | ||||
|  | ||||
|                 controller = new EditController( | ||||
|                 mockScope.domainObject = mockObject; | ||||
|  | ||||
|                 controller = new EditObjectController( | ||||
|                     mockScope, | ||||
|                     mockQ, | ||||
|                     mockNavigationService | ||||
|                     mockLocation | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("exposes the currently-navigated object", function () { | ||||
|                 expect(controller.navigatedObject()).toBeDefined(); | ||||
|                 expect(controller.navigatedObject().getId()).toEqual("test"); | ||||
|             }); | ||||
|  | ||||
|             it("adds an editor capability to the navigated object", function () { | ||||
|                 // Should provide an editor capability... | ||||
|                 expect(controller.navigatedObject().getCapability("editor")) | ||||
|                     .toBeDefined(); | ||||
|                 // Shouldn't have been the mock capability we provided | ||||
|                 expect(controller.navigatedObject().getCapability("editor")) | ||||
|                     .not.toEqual(mockType); | ||||
|             }); | ||||
|  | ||||
|             it("detaches its navigation listener when destroyed", function () { | ||||
|                 var navCallback = mockNavigationService | ||||
|                         .addListener.mostRecentCall.args[0]; | ||||
|  | ||||
|                 expect(mockScope.$on).toHaveBeenCalledWith( | ||||
|                     "$destroy", | ||||
|                     jasmine.any(Function) | ||||
|                 ); | ||||
|  | ||||
|                 // Verify precondition | ||||
|                 expect(mockNavigationService.removeListener) | ||||
|                     .not.toHaveBeenCalled(); | ||||
|  | ||||
|                 // Trigger destroy | ||||
|                 mockScope.$on.mostRecentCall.args[1](); | ||||
|  | ||||
|                 // Listener should have been removed | ||||
|                 expect(mockNavigationService.removeListener) | ||||
|                     .toHaveBeenCalledWith(navCallback); | ||||
|             }); | ||||
|  | ||||
|             it("exposes a warning message for unload", function () { | ||||
|                 var obj = controller.navigatedObject(), | ||||
|                 var obj = mockObject, | ||||
|                     mockEditor = jasmine.createSpyObj('editor', ['dirty']); | ||||
|  | ||||
|                 // Normally, should be undefined | ||||
| @@ -112,14 +95,32 @@ define( | ||||
|  | ||||
|                 // Override the object's editor capability, make it look | ||||
|                 // like there are unsaved changes. | ||||
|                 obj.getCapability = jasmine.createSpy(); | ||||
|                 obj.getCapability.andReturn(mockEditor); | ||||
|                 mockCapabilities.editor = mockEditor; | ||||
|                 mockEditor.dirty.andReturn(true); | ||||
|                 mockStatusCapability.get.andReturn(true); | ||||
|  | ||||
|                 // Should have some warning message here now | ||||
|                 expect(controller.getUnloadWarning()).toEqual(jasmine.any(String)); | ||||
|             }); | ||||
|  | ||||
|  | ||||
|             it("sets the active view from query parameters", function () { | ||||
|                 var testViews = [ | ||||
|                         { key: 'abc' }, | ||||
|                         { key: 'def', someKey: 'some value' }, | ||||
|                         { key: 'xyz' } | ||||
|                     ]; | ||||
|  | ||||
|                 mockObject.useCapability.andCallFake(function (c) { | ||||
|                     return (c === 'view') && testViews; | ||||
|                 }); | ||||
|                 mockLocation.search.andReturn({ view: 'def' }); | ||||
|  | ||||
|                 fireWatch('domainObject', mockObject); | ||||
|                 expect(mockScope.representation.selected) | ||||
|                     .toEqual(testViews[1]); | ||||
|             }); | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
|   | ||||
| @@ -31,6 +31,7 @@ define( | ||||
|                 mockScope, | ||||
|                 testAttrs, | ||||
|                 mockEvent, | ||||
|                 mockNavigationService, | ||||
|                 directive; | ||||
|  | ||||
|             function fireListener(eventType, value) { | ||||
| @@ -46,7 +47,8 @@ define( | ||||
|                 mockScope = jasmine.createSpyObj("$scope", ['$eval', '$on']); | ||||
|                 testAttrs = { mctBeforeUnload: "someExpression" }; | ||||
|                 mockEvent = jasmine.createSpyObj("event", ["preventDefault"]); | ||||
|                 directive = new MCTBeforeUnload(mockWindow); | ||||
|                 mockNavigationService = jasmine.createSpyObj("navigationService", ["addListener", "removeListener"]); | ||||
|                 directive = new MCTBeforeUnload(mockWindow, mockNavigationService); | ||||
|                 directive.link(mockScope, {}, testAttrs); | ||||
|             }); | ||||
|  | ||||
| @@ -65,6 +67,10 @@ define( | ||||
|                 ); | ||||
|             }); | ||||
|  | ||||
|             it("listens for navigation changes", function () { | ||||
|                 expect(mockNavigationService.addListener).toHaveBeenCalledWith(jasmine.any(Function), "before"); | ||||
|             }); | ||||
|  | ||||
|             it("listens for its scope's destroy event", function () { | ||||
|                 expect(mockScope.$on).toHaveBeenCalledWith( | ||||
|                     "$destroy", | ||||
| @@ -108,9 +114,10 @@ define( | ||||
|             it("cleans up listeners when destroyed", function () { | ||||
|                 fireListener("$destroy", mockEvent); | ||||
|                 expect(mockWindow.onbeforeunload).toBeUndefined(); | ||||
|                 expect(mockNavigationService.removeListener).toHaveBeenCalled(); | ||||
|             }); | ||||
|  | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| ); | ||||
|   | ||||
| @@ -33,6 +33,8 @@ define( | ||||
|                 testRepresentation, | ||||
|                 mockDomainObject, | ||||
|                 mockPersistence, | ||||
|                 mockStatusCapability, | ||||
|                 mockCapabilities, | ||||
|                 representer; | ||||
|  | ||||
|             function mockPromise(value) { | ||||
| @@ -57,11 +59,20 @@ define( | ||||
|                 ]); | ||||
|                 mockPersistence = | ||||
|                     jasmine.createSpyObj("persistence", ["persist"]); | ||||
|                 mockStatusCapability = | ||||
|                     jasmine.createSpyObj("statusCapability", ["get", "listen"]); | ||||
|  | ||||
|                 mockCapabilities = { | ||||
|                     "persistence": mockPersistence, | ||||
|                     "status": mockStatusCapability | ||||
|                 }; | ||||
|  | ||||
|                 mockDomainObject.getModel.andReturn({}); | ||||
|                 mockDomainObject.hasCapability.andReturn(true); | ||||
|                 mockDomainObject.useCapability.andReturn(true); | ||||
|                 mockDomainObject.getCapability.andReturn(mockPersistence); | ||||
|                 mockDomainObject.getCapability.andCallFake(function(capability){ | ||||
|                     return mockCapabilities[capability]; | ||||
|                 }); | ||||
|  | ||||
|                 representer = new EditRepresenter(mockQ, mockLog, mockScope); | ||||
|                 representer.represent(testRepresentation, mockDomainObject); | ||||
| @@ -101,4 +112,4 @@ define( | ||||
|  | ||||
|         }); | ||||
|     } | ||||
| ); | ||||
| ); | ||||
|   | ||||
| @@ -165,16 +165,14 @@ define( | ||||
|                     if (shouldCreateVirtualPanel(domainObject, selectedObject)){ | ||||
|                         editableDomainObject = createVirtualPanel(domainObject, selectedObject); | ||||
|                         if (editableDomainObject) { | ||||
|                             navigationService.setNavigation(editableDomainObject); | ||||
|                             editableDomainObject.getCapability('action').perform('edit'); | ||||
|                             broadcastDrop(id, event); | ||||
|                             editableDomainObject.getCapability('status').set('editing', true); | ||||
|                         } | ||||
|                     } else { | ||||
|                         $q.when(action && action.perform()).then(function (result) { | ||||
|                             //Don't go into edit mode for folders | ||||
|                             if (domainObjectType!=='folder') { | ||||
|                                 navigationService.setNavigation(editableDomainObject); | ||||
|                                 editableDomainObject.getCapability('status').set('editing', true); | ||||
|                                 editableDomainObject.getCapability('action').perform('edit'); | ||||
|                             } | ||||
|                             broadcastDrop(id, event); | ||||
|                         }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user