Skip to content

Commit c4d47dd

Browse files
authored
Merge pull request nasa#1366 from nasa/fix-navigation-warnings-1360
Fix navigation warnings, tidy editing, navigation, browse logic.
2 parents b9601ff + 60d1b73 commit c4d47dd

File tree

29 files changed

+396
-894
lines changed

29 files changed

+396
-894
lines changed

docs/src/guide/index.md

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,20 +1338,6 @@ are supported:
13381338

13391339
Open MCT defines several Angular directives that are intended for use both
13401340
internally within the platform, and by plugins.
1341-
1342-
## Before Unload
1343-
1344-
The `mct-before-unload` directive is used to listen for (and prompt for user
1345-
confirmation) of navigation changes in the browser. This includes reloading,
1346-
following links out of Open MCT, or changing routes. It is used to hook into
1347-
both `onbeforeunload` event handling as well as route changes from within
1348-
Angular.
1349-
1350-
This directive is useable as an attribute. Its value should be an Angular
1351-
expression. When an action that would trigger an unload and/or route change
1352-
occurs, this Angular expression is evaluated. Its result should be a message to
1353-
display to the user to confirm their navigation change; if this expression
1354-
evaluates to a falsy value, no message will be displayed.
13551341

13561342
## Chart
13571343

platform/commonUI/browse/bundle.js

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ define([
4141
"text!./res/templates/items/items.html",
4242
"text!./res/templates/browse/object-properties.html",
4343
"text!./res/templates/browse/inspector-region.html",
44-
"text!./res/templates/view-object.html",
4544
'legacyRegistry'
4645
], function (
4746
BrowseController,
@@ -64,7 +63,6 @@ define([
6463
itemsTemplate,
6564
objectPropertiesTemplate,
6665
inspectorRegionTemplate,
67-
viewObjectTemplate,
6866
legacyRegistry
6967
) {
7068

@@ -141,10 +139,6 @@ define([
141139
}
142140
],
143141
"representations": [
144-
{
145-
"key": "view-object",
146-
"template": viewObjectTemplate
147-
},
148142
{
149143
"key": "browse-object",
150144
"template": browseObjectTemplate,
@@ -204,18 +198,18 @@ define([
204198
"services": [
205199
{
206200
"key": "navigationService",
207-
"implementation": NavigationService
201+
"implementation": NavigationService,
202+
"depends": [
203+
"$window"
204+
]
208205
}
209206
],
210207
"actions": [
211208
{
212209
"key": "navigate",
213210
"implementation": NavigateAction,
214211
"depends": [
215-
"navigationService",
216-
"$q",
217-
"policyService",
218-
"$window"
212+
"navigationService"
219213
]
220214
},
221215
{

platform/commonUI/browse/res/templates/browse.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
<mct-split-pane class='l-object-and-inspector contents abs' anchor='right'>
6464
<div class='split-pane-component t-object pane primary-pane left'>
6565
<mct-representation mct-object="navigatedObject"
66-
key="'view-object'"
66+
key="navigatedObject.getCapability('status').get('editing') ? 'edit-object' : 'browse-object'"
6767
class="abs holder holder-object">
6868
</mct-representation>
6969
<a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect flush-right"

platform/commonUI/browse/res/templates/view-object.html

Lines changed: 0 additions & 33 deletions
This file was deleted.

platform/commonUI/browse/src/BrowseController.js

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,16 @@ define(
4848
defaultPath
4949
) {
5050
var initialPath = ($route.current.params.ids || defaultPath).split("/");
51-
52-
var currentIds = $route.current.params.ids;
51+
var currentIds;
5352

5453
$scope.treeModel = {
55-
selectedObject: undefined
54+
selectedObject: undefined,
55+
onSelection: function (object) {
56+
navigationService.setNavigation(object, true);
57+
},
58+
allowSelection: function (object) {
59+
return navigationService.shouldNavigate();
60+
}
5661
};
5762

5863
function idsForObject(domainObject) {
@@ -103,7 +108,6 @@ define(
103108
function navigateToObject(desiredObject) {
104109
$scope.navigatedObject = desiredObject;
105110
$scope.treeModel.selectedObject = desiredObject;
106-
navigationService.setNavigation(desiredObject);
107111
currentIds = idsForObject(desiredObject);
108112
$route.current.pathParams.ids = currentIds;
109113
$location.path('/browse/' + currentIds);
@@ -114,10 +118,11 @@ define(
114118
.then(function (root) {
115119
return findViaComposition(root, path);
116120
})
117-
.then(navigateToObject);
121+
.then(function (object) {
122+
navigationService.setNavigation(object);
123+
});
118124
}
119125

120-
121126
getObject('ROOT')
122127
.then(function (root) {
123128
$scope.domainObject = root;
@@ -137,15 +142,6 @@ define(
137142
// Listen for changes in navigation state.
138143
navigationService.addListener(navigateDirectlyToModel);
139144

140-
// Also listen for changes which come from the tree. Changes in
141-
// the tree will trigger a change in browse navigation state.
142-
$scope.$watch("treeModel.selectedObject", function (newObject, oldObject) {
143-
if (oldObject !== newObject) {
144-
navigateDirectlyToModel(newObject);
145-
}
146-
});
147-
148-
149145
// Listen for route changes which are caused by browser events
150146
// (e.g. bookmarks to pages in OpenMCT) and prevent them. Instead,
151147
// navigate to the path ourselves, which results in it being

platform/commonUI/browse/src/navigation/NavigateAction.js

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,9 @@ define(
3333
* @constructor
3434
* @implements {Action}
3535
*/
36-
function NavigateAction(navigationService, $q, policyService, $window, context) {
36+
function NavigateAction(navigationService, context) {
3737
this.domainObject = context.domainObject;
38-
this.$q = $q;
3938
this.navigationService = navigationService;
40-
this.policyService = policyService;
41-
this.$window = $window;
4239
}
4340

4441
/**
@@ -47,36 +44,12 @@ define(
4744
* navigation has been updated
4845
*/
4946
NavigateAction.prototype.perform = function () {
50-
var self = this,
51-
navigateTo = this.domainObject,
52-
currentObject = self.navigationService.getNavigation();
53-
54-
function allow() {
55-
var navigationAllowed = true;
56-
self.policyService.allow("navigation", currentObject, navigateTo, function (message) {
57-
navigationAllowed = self.$window.confirm(message + "\r\n\r\n" +
58-
" Are you sure you want to continue?");
59-
});
60-
return navigationAllowed;
61-
}
62-
63-
function cancelIfEditing() {
64-
var editing = currentObject.hasCapability('editor') &&
65-
currentObject.getCapability('editor').isEditContextRoot();
66-
67-
return self.$q.when(editing && currentObject.getCapability("editor").finish());
68-
}
69-
70-
function navigate() {
71-
return self.navigationService.setNavigation(navigateTo);
72-
}
73-
74-
if (allow()) {
75-
return cancelIfEditing().then(navigate);
76-
} else {
77-
return this.$q.when(false);
47+
if (this.navigationService.shouldNavigate()) {
48+
this.navigationService.setNavigation(this.domainObject, true);
49+
return Promise.resolve({});
7850
}
7951

52+
return Promise.reject('Navigation Prevented by User');
8053
};
8154

8255
/**

platform/commonUI/browse/src/navigation/NavigationService.js

Lines changed: 117 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,40 +30,65 @@ define(
3030
/**
3131
* The navigation service maintains the application's current
3232
* navigation state, and allows listening for changes thereto.
33+
*
3334
* @memberof platform/commonUI/browse
3435
* @constructor
3536
*/
36-
function NavigationService() {
37+
function NavigationService($window) {
3738
this.navigated = undefined;
3839
this.callbacks = [];
40+
this.checks = [];
41+
this.$window = $window;
42+
43+
this.oldUnload = $window.onbeforeunload;
44+
$window.onbeforeunload = this.onBeforeUnload.bind(this);
3945
}
4046

4147
/**
4248
* Get the current navigation state.
49+
*
4350
* @returns {DomainObject} the object that is navigated-to
4451
*/
4552
NavigationService.prototype.getNavigation = function () {
4653
return this.navigated;
4754
};
4855

4956
/**
50-
* Set the current navigation state. This will invoke listeners.
57+
* Navigate to a specified object. If navigation checks exist and
58+
* return reasons to prevent navigation, it will prompt the user before
59+
* continuing. Trying to navigate to the currently navigated object will
60+
* do nothing.
61+
*
62+
* If a truthy value is passed for `force`, it will skip navigation
63+
* and will not prevent navigation to an already selected object.
64+
*
5165
* @param {DomainObject} domainObject the domain object to navigate to
66+
* @param {Boolean} force if true, force navigation to occur.
67+
* @returns {Boolean} true if navigation occured, otherwise false.
5268
*/
53-
NavigationService.prototype.setNavigation = function (value) {
54-
if (this.navigated !== value) {
55-
this.navigated = value;
56-
this.callbacks.forEach(function (callback) {
57-
callback(value);
58-
});
69+
NavigationService.prototype.setNavigation = function (domainObject, force) {
70+
if (force) {
71+
this.doNavigation(domainObject);
72+
return true;
73+
}
74+
if (this.navigated === domainObject) {
75+
return true;
76+
}
77+
78+
var doNotNavigate = this.shouldWarnBeforeNavigate();
79+
if (doNotNavigate && !this.$window.confirm(doNotNavigate)) {
80+
return false;
5981
}
82+
83+
this.doNavigation(domainObject);
6084
return true;
6185
};
6286

6387
/**
6488
* Listen for changes in navigation. The passed callback will
6589
* be invoked with the new domain object of navigation when
6690
* this changes.
91+
*
6792
* @param {function} callback the callback to invoke when
6893
* navigation state changes
6994
*/
@@ -73,6 +98,7 @@ define(
7398

7499
/**
75100
* Stop listening for changes in navigation state.
101+
*
76102
* @param {function} callback the callback which should
77103
* no longer be invoked when navigation state
78104
* changes
@@ -83,6 +109,89 @@ define(
83109
});
84110
};
85111

112+
/**
113+
* Check if navigation should proceed. May prompt a user for input
114+
* if any checkFns return messages. Returns true if the user wishes to
115+
* navigate, otherwise false. If using this prior to calling
116+
* `setNavigation`, you should call `setNavigation` with `force=true`
117+
* to prevent duplicate dialogs being displayed to the user.
118+
*
119+
* @returns {Boolean} true if the user wishes to navigate, otherwise false.
120+
*/
121+
NavigationService.prototype.shouldNavigate = function () {
122+
var doNotNavigate = this.shouldWarnBeforeNavigate();
123+
return !doNotNavigate || this.$window.confirm(doNotNavigate);
124+
};
125+
126+
/**
127+
* Register a check function to be called before any navigation occurs.
128+
* Check functions should return a human readable "message" if
129+
* there are any reasons to prevent navigation. Otherwise, they should
130+
* return falsy. Returns a function which can be called to remove the
131+
* check function.
132+
*
133+
* @param {Function} checkFn a function to call before navigation occurs.
134+
* @returns {Function} removeCheck call to remove check
135+
*/
136+
NavigationService.prototype.checkBeforeNavigation = function (checkFn) {
137+
this.checks.push(checkFn);
138+
return function removeCheck() {
139+
this.checks = this.checks.filter(function (fn) {
140+
return checkFn !== fn;
141+
});
142+
}.bind(this);
143+
};
144+
145+
/**
146+
* Private method to actually perform navigation.
147+
*
148+
* @private
149+
*/
150+
NavigationService.prototype.doNavigation = function (value) {
151+
this.navigated = value;
152+
this.callbacks.forEach(function (callback) {
153+
callback(value);
154+
});
155+
};
156+
157+
/**
158+
* Returns either a false value, or a string that should be displayed
159+
* to the user before navigation is allowed.
160+
*
161+
* @private
162+
*/
163+
NavigationService.prototype.shouldWarnBeforeNavigate = function () {
164+
var reasons = [];
165+
166+
this.checks.forEach(function (checkFn) {
167+
var reason = checkFn();
168+
if (reason) {
169+
reasons.push(reason);
170+
}
171+
});
172+
173+
if (reasons.length) {
174+
return reasons.join('\n');
175+
}
176+
return false;
177+
};
178+
179+
/**
180+
* Listener for window on before unload event-- will warn before
181+
* navigation is allowed.
182+
*
183+
* @private
184+
*/
185+
NavigationService.prototype.onBeforeUnload = function () {
186+
var shouldWarnBeforeNavigate = this.shouldWarnBeforeNavigate();
187+
if (shouldWarnBeforeNavigate) {
188+
return shouldWarnBeforeNavigate;
189+
}
190+
if (this.oldUnload) {
191+
return this.oldUnload.apply(undefined, [].slice.apply(arguments));
192+
}
193+
};
194+
86195
return NavigationService;
87196
}
88197
);

0 commit comments

Comments
 (0)