[Navigation] remove mct-before-unload
Remove mct-before-unload, and move the functionality to the navigation service. The navigation service considers "unload" to be a navigation event and prompts in much the same way as it would before any other navigation event. https://github.com/nasa/openmct/issues/1360
This commit is contained in:
@@ -1338,20 +1338,6 @@ are supported:
|
|||||||
|
|
||||||
Open MCT defines several Angular directives that are intended for use both
|
Open MCT defines several Angular directives that are intended for use both
|
||||||
internally within the platform, and by plugins.
|
internally within the platform, and by plugins.
|
||||||
|
|
||||||
## Before Unload
|
|
||||||
|
|
||||||
The `mct-before-unload` directive is used to listen for (and prompt for user
|
|
||||||
confirmation) of navigation changes in the browser. This includes reloading,
|
|
||||||
following links out of Open MCT, or changing routes. It is used to hook into
|
|
||||||
both `onbeforeunload` event handling as well as route changes from within
|
|
||||||
Angular.
|
|
||||||
|
|
||||||
This directive is useable as an attribute. Its value should be an Angular
|
|
||||||
expression. When an action that would trigger an unload and/or route change
|
|
||||||
occurs, this Angular expression is evaluated. Its result should be a message to
|
|
||||||
display to the user to confirm their navigation change; if this expression
|
|
||||||
evaluates to a falsy value, no message will be displayed.
|
|
||||||
|
|
||||||
## Chart
|
## Chart
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ define(
|
|||||||
this.callbacks = [];
|
this.callbacks = [];
|
||||||
this.checks = [];
|
this.checks = [];
|
||||||
this.$window = $window;
|
this.$window = $window;
|
||||||
|
|
||||||
|
this.oldUnload = $window.onbeforeunload;
|
||||||
|
$window.onbeforeunload = this.onBeforeUnload.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -173,6 +176,22 @@ define(
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for window on before unload event-- will warn before
|
||||||
|
* navigation is allowed.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
NavigationService.prototype.onBeforeUnload = function () {
|
||||||
|
var shouldWarnBeforeNavigate = this.shouldWarnBeforeNavigate();
|
||||||
|
if (shouldWarnBeforeNavigate) {
|
||||||
|
return shouldWarnBeforeNavigate;
|
||||||
|
}
|
||||||
|
if (this.oldUnload) {
|
||||||
|
return this.oldUnload.apply(undefined, [].slice.apply(arguments));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return NavigationService;
|
return NavigationService;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,25 +2,6 @@ Contains sources and resources associated with Edit mode.
|
|||||||
|
|
||||||
# Extensions
|
# Extensions
|
||||||
|
|
||||||
## Directives
|
|
||||||
|
|
||||||
This bundle introduces the `mct-before-unload` directive, primarily for
|
|
||||||
internal use (to prompt the user to confirm navigation away from unsaved
|
|
||||||
changes in Edit mode.)
|
|
||||||
|
|
||||||
The `mct-before-unload` directive is used as an attribute whose value is
|
|
||||||
an Angular expression that is evaluated when navigation changes (either
|
|
||||||
via browser-level changes, such as the refresh button, or changes to
|
|
||||||
the Angular route, which happens when hitting the back button in Edit
|
|
||||||
mode.) The result of this evaluation, when truthy, is shown in a browser
|
|
||||||
dialog to allow the user to confirm navigation. When falsy, no prompt is
|
|
||||||
shown, allowing these dialogs to be shown conditionally. (For instance, in
|
|
||||||
Edit mode, prompts are only shown if user-initiated changes have
|
|
||||||
occurred.)
|
|
||||||
|
|
||||||
This directive may be attached to any element; its behavior will be enforced
|
|
||||||
so long as that element remains within the DOM.
|
|
||||||
|
|
||||||
# Toolbars
|
# Toolbars
|
||||||
|
|
||||||
Views may specify the contents of a toolbar through a `toolbar`
|
Views may specify the contents of a toolbar through a `toolbar`
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ define([
|
|||||||
"./src/controllers/EditPanesController",
|
"./src/controllers/EditPanesController",
|
||||||
"./src/controllers/ElementsController",
|
"./src/controllers/ElementsController",
|
||||||
"./src/controllers/EditObjectController",
|
"./src/controllers/EditObjectController",
|
||||||
"./src/directives/MCTBeforeUnload",
|
|
||||||
"./src/actions/EditAndComposeAction",
|
"./src/actions/EditAndComposeAction",
|
||||||
"./src/actions/EditAction",
|
"./src/actions/EditAction",
|
||||||
"./src/actions/PropertiesAction",
|
"./src/actions/PropertiesAction",
|
||||||
@@ -65,7 +64,6 @@ define([
|
|||||||
EditPanesController,
|
EditPanesController,
|
||||||
ElementsController,
|
ElementsController,
|
||||||
EditObjectController,
|
EditObjectController,
|
||||||
MCTBeforeUnload,
|
|
||||||
EditAndComposeAction,
|
EditAndComposeAction,
|
||||||
EditAction,
|
EditAction,
|
||||||
PropertiesAction,
|
PropertiesAction,
|
||||||
@@ -152,15 +150,6 @@ define([
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"directives": [
|
|
||||||
{
|
|
||||||
"key": "mctBeforeUnload",
|
|
||||||
"implementation": MCTBeforeUnload,
|
|
||||||
"depends": [
|
|
||||||
"$window"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"key": "compose",
|
"key": "compose",
|
||||||
|
|||||||
@@ -20,8 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="abs l-flex-col" ng-controller="EditObjectController as EditObjectController">
|
<div class="abs l-flex-col" ng-controller="EditObjectController as EditObjectController">
|
||||||
<div mct-before-unload="EditObjectController.getUnloadWarning()"
|
<div class="holder flex-elem l-flex-row object-browse-bar ">
|
||||||
class="holder flex-elem l-flex-row object-browse-bar ">
|
|
||||||
<div class="items-select left flex-elem l-flex-row grows">
|
<div class="items-select left flex-elem l-flex-row grows">
|
||||||
<mct-representation key="'back-arrow'"
|
<mct-representation key="'back-arrow'"
|
||||||
mct-object="domainObject"
|
mct-object="domainObject"
|
||||||
|
|||||||
@@ -64,24 +64,6 @@ define(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the warning to show if the user attempts to navigate
|
|
||||||
* away from Edit mode while unsaved changes are present.
|
|
||||||
* @returns {string} the warning to show, or undefined if
|
|
||||||
* there are no unsaved changes
|
|
||||||
*/
|
|
||||||
EditObjectController.prototype.getUnloadWarning = function () {
|
|
||||||
var navigatedObject = this.scope.domainObject,
|
|
||||||
policyMessage;
|
|
||||||
|
|
||||||
this.policyService.allow("navigation", navigatedObject, undefined, function (message) {
|
|
||||||
policyMessage = message;
|
|
||||||
});
|
|
||||||
|
|
||||||
return policyMessage;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
return EditObjectController;
|
return EditObjectController;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
[],
|
|
||||||
function () {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the `mct-before-unload` directive. The expression bound
|
|
||||||
* to this attribute will be evaluated during page navigation events
|
|
||||||
* and, if it returns a truthy value, will be used to populate a
|
|
||||||
* prompt to the user to confirm this navigation.
|
|
||||||
* @memberof platform/commonUI/edit
|
|
||||||
* @constructor
|
|
||||||
* @param $window the window
|
|
||||||
*/
|
|
||||||
function MCTBeforeUnload($window) {
|
|
||||||
var unloads = [],
|
|
||||||
oldBeforeUnload = $window.onbeforeunload;
|
|
||||||
|
|
||||||
// Run all unload functions, returning the first returns truthily.
|
|
||||||
function checkUnloads() {
|
|
||||||
var result;
|
|
||||||
unloads.forEach(function (unload) {
|
|
||||||
result = result || unload();
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link function for an mct-before-unload directive usage
|
|
||||||
function link(scope, element, attrs) {
|
|
||||||
// Invoke the
|
|
||||||
function unload() {
|
|
||||||
return scope.$eval(attrs.mctBeforeUnload);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop using this unload expression
|
|
||||||
function removeUnload() {
|
|
||||||
unloads = unloads.filter(function (callback) {
|
|
||||||
return callback !== unload;
|
|
||||||
});
|
|
||||||
if (unloads.length === 0) {
|
|
||||||
$window.onbeforeunload = oldBeforeUnload;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is the first active instance of this directive,
|
|
||||||
// register as the window's beforeunload handler
|
|
||||||
if (unloads.length === 0) {
|
|
||||||
$window.onbeforeunload = checkUnloads;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include this instance of the directive's unload function
|
|
||||||
unloads.push(unload);
|
|
||||||
|
|
||||||
// Remove it when the scope is destroyed
|
|
||||||
scope.$on("$destroy", removeUnload);
|
|
||||||
|
|
||||||
// Also handle route changes
|
|
||||||
scope.$on("$locationChangeStart", checkLocationChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
// Applicable as an attribute
|
|
||||||
restrict: "A",
|
|
||||||
// Link with the provided function
|
|
||||||
link: link
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return MCTBeforeUnload;
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2016, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
define(
|
|
||||||
["../../src/directives/MCTBeforeUnload"],
|
|
||||||
function (MCTBeforeUnload) {
|
|
||||||
|
|
||||||
describe("The mct-before-unload directive", function () {
|
|
||||||
var mockWindow,
|
|
||||||
mockScope,
|
|
||||||
testAttrs,
|
|
||||||
mockEvent,
|
|
||||||
directive;
|
|
||||||
|
|
||||||
function fireListener(eventType, value) {
|
|
||||||
mockScope.$on.calls.forEach(function (call) {
|
|
||||||
if (call.args[0] === eventType) {
|
|
||||||
call.args[1](value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mockWindow = jasmine.createSpyObj("$window", ['confirm']);
|
|
||||||
mockScope = jasmine.createSpyObj("$scope", ['$eval', '$on']);
|
|
||||||
testAttrs = { mctBeforeUnload: "someExpression" };
|
|
||||||
mockEvent = jasmine.createSpyObj("event", ["preventDefault"]);
|
|
||||||
directive = new MCTBeforeUnload(mockWindow);
|
|
||||||
directive.link(mockScope, {}, testAttrs);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("can be used only as an attribute", function () {
|
|
||||||
expect(directive.restrict).toEqual('A');
|
|
||||||
});
|
|
||||||
|
|
||||||
it("listens for beforeunload", function () {
|
|
||||||
expect(mockWindow.onbeforeunload).toEqual(jasmine.any(Function));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("listens for route changes", function () {
|
|
||||||
expect(mockScope.$on).toHaveBeenCalledWith(
|
|
||||||
"$locationChangeStart",
|
|
||||||
jasmine.any(Function)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("listens for its scope's destroy event", function () {
|
|
||||||
expect(mockScope.$on).toHaveBeenCalledWith(
|
|
||||||
"$destroy",
|
|
||||||
jasmine.any(Function)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("uses result of evaluated expression as a warning", function () {
|
|
||||||
mockScope.$eval.andReturn(undefined);
|
|
||||||
expect(mockWindow.onbeforeunload(mockEvent)).toBeUndefined();
|
|
||||||
mockScope.$eval.andReturn("some message");
|
|
||||||
expect(mockWindow.onbeforeunload(mockEvent)).toEqual("some message");
|
|
||||||
// Verify that the right expression was evaluated
|
|
||||||
expect(mockScope.$eval).toHaveBeenCalledWith(testAttrs.mctBeforeUnload);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("confirms route changes", function () {
|
|
||||||
// First, try with no unsaved changes;
|
|
||||||
// should not confirm or preventDefault
|
|
||||||
mockScope.$eval.andReturn(undefined);
|
|
||||||
fireListener("$locationChangeStart", mockEvent);
|
|
||||||
expect(mockWindow.confirm).not.toHaveBeenCalled();
|
|
||||||
expect(mockEvent.preventDefault).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
// Next, try with unsaved changes that the user confirms;
|
|
||||||
// should prompt, but not preventDefault
|
|
||||||
mockScope.$eval.andReturn("some message");
|
|
||||||
mockWindow.confirm.andReturn(true);
|
|
||||||
fireListener("$locationChangeStart", mockEvent);
|
|
||||||
expect(mockWindow.confirm).toHaveBeenCalledWith("some message");
|
|
||||||
expect(mockEvent.preventDefault).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
// Finally, act as if the user said no to this dialog;
|
|
||||||
// this should preventDefault on the location change.
|
|
||||||
mockWindow.confirm.andReturn(false);
|
|
||||||
fireListener("$locationChangeStart", mockEvent);
|
|
||||||
expect(mockWindow.confirm).toHaveBeenCalledWith("some message");
|
|
||||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("cleans up listeners when destroyed", function () {
|
|
||||||
fireListener("$destroy", mockEvent);
|
|
||||||
expect(mockWindow.onbeforeunload).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
Reference in New Issue
Block a user