navigation-stack 0.5.3 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +144 -282
- package/karma.conf.cjs +1 -1
- package/lib/cjs/NavigationStack.js +138 -49
- package/lib/cjs/data-storage/DataStorage.js +7 -6
- package/lib/cjs/environment/InMemoryEnvironment.js +6 -0
- package/lib/cjs/{session/ServerSideRenderSession.js → environment/ServerSideRenderEnvironment.js} +5 -6
- package/lib/cjs/environment/WebBrowserEnvironment.js +6 -0
- package/lib/cjs/environment/log/InMemoryLog.js +23 -0
- package/lib/cjs/environment/log/WebBrowserLog.js +22 -0
- package/lib/cjs/{session → environment}/navigation/InMemoryNavigation.js +16 -5
- package/lib/cjs/{session → environment}/navigation/ServerSideNavigation.js +16 -7
- package/lib/cjs/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
- package/lib/cjs/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +2 -2
- package/lib/cjs/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
- package/lib/cjs/getLocationBaseFromLocation.js +14 -0
- package/lib/cjs/getLocationUrl.js +3 -5
- package/lib/cjs/index.js +10 -16
- package/lib/cjs/navigationBlockers.js +34 -32
- package/lib/cjs/navigationBlockersEvaluation.js +150 -0
- package/lib/cjs/parseInputLocation.js +2 -2
- package/lib/cjs/parseQueryFromSearch.js +3 -6
- package/lib/cjs/parseQueryString.js +77 -0
- package/lib/cjs/scroll-position/ScrollPositionAutoSaver.js +7 -6
- package/lib/cjs/scroll-position/ScrollPositionRestoration.js +31 -27
- package/lib/cjs/scroll-position/ScrollPositionSaver.js +6 -4
- package/lib/cjs/session/Session.js +61 -26
- package/lib/cjs/session/subscription/Subscription.js +36 -18
- package/lib/cjs/stringifyQuery.js +66 -0
- package/lib/cjs/stringifyQueryAsSearch.js +14 -0
- package/lib/esm/NavigationStack.js +138 -49
- package/lib/esm/data-storage/DataStorage.js +7 -6
- package/lib/esm/environment/InMemoryEnvironment.js +6 -0
- package/lib/esm/environment/ServerSideRenderEnvironment.js +10 -0
- package/lib/esm/environment/WebBrowserEnvironment.js +6 -0
- package/lib/esm/environment/log/InMemoryLog.js +17 -0
- package/lib/esm/environment/log/WebBrowserLog.js +16 -0
- package/lib/esm/{session → environment}/navigation/InMemoryNavigation.js +16 -5
- package/lib/esm/{session → environment}/navigation/ServerSideNavigation.js +16 -7
- package/lib/esm/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
- package/lib/esm/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +1 -1
- package/lib/esm/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
- package/lib/esm/getLocationBaseFromLocation.js +9 -0
- package/lib/esm/getLocationUrl.js +2 -5
- package/lib/esm/index.js +5 -8
- package/lib/esm/navigationBlockers.js +34 -32
- package/lib/esm/navigationBlockersEvaluation.js +145 -0
- package/lib/esm/parseInputLocation.js +2 -2
- package/lib/esm/parseQueryFromSearch.js +2 -6
- package/lib/esm/parseQueryString.js +72 -0
- package/lib/esm/scroll-position/ScrollPositionAutoSaver.js +7 -6
- package/lib/esm/scroll-position/ScrollPositionRestoration.js +31 -27
- package/lib/esm/scroll-position/ScrollPositionSaver.js +6 -4
- package/lib/esm/session/Session.js +61 -26
- package/lib/esm/session/subscription/Subscription.js +36 -18
- package/lib/esm/stringifyQuery.js +61 -0
- package/lib/esm/stringifyQueryAsSearch.js +8 -0
- package/lib/index.d.ts +180 -34
- package/package.json +4 -7
- package/src/NavigationStack.js +166 -56
- package/src/data-storage/DataStorage.js +9 -6
- package/src/environment/InMemoryEnvironment.js +6 -0
- package/src/environment/ServerSideRenderEnvironment.js +10 -0
- package/src/environment/WebBrowserEnvironment.js +6 -0
- package/src/environment/log/InMemoryLog.js +20 -0
- package/src/environment/log/WebBrowserLog.js +18 -0
- package/src/{session → environment}/navigation/InMemoryNavigation.js +16 -5
- package/src/{session → environment}/navigation/ServerSideNavigation.js +16 -7
- package/src/{session → environment}/navigation/WebBrowserNavigation.js +48 -8
- package/src/{session/navigation/error/ServerSideNavigationError.js → environment/navigation/error/ServerSideRedirectError.js} +1 -1
- package/src/environment/scroll-position/WebBrowserScrollPosition.js +15 -0
- package/src/getLocationBaseFromLocation.js +7 -0
- package/src/getLocationUrl.js +2 -5
- package/src/index.js +10 -13
- package/src/navigationBlockers.js +55 -34
- package/src/navigationBlockersEvaluation.js +161 -0
- package/src/parseInputLocation.js +2 -2
- package/src/parseQueryFromSearch.js +2 -6
- package/src/parseQueryString.js +81 -0
- package/src/scroll-position/ScrollPositionAutoSaver.js +10 -6
- package/src/scroll-position/ScrollPositionRestoration.js +36 -30
- package/src/scroll-position/ScrollPositionSaver.js +6 -4
- package/src/scroll-position/index.js +1 -1
- package/src/session/Session.js +68 -24
- package/src/session/subscription/Subscription.js +36 -11
- package/src/stringifyQuery.js +71 -0
- package/src/stringifyQueryAsSearch.js +9 -0
- package/test/NavigationStack.addBasePath.test.js +50 -0
- package/test/{redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.test.js → NavigationStack.blockNonProgrammaticNavigationIfRequired.test.js} +51 -63
- package/test/{redux/middleware/createProgrammaticNavigationBlockerMiddleware.test.js → NavigationStack.blockProgrammaticNavigationIfRequired.test.js} +98 -78
- package/test/NavigationStack.general.test.js +68 -0
- package/test/NavigationStack.parseInputLocation.test.js +52 -0
- package/test/NavigationStack.removeBasePath.test.js +69 -0
- package/test/NavigationStack.test.js +97 -29
- package/test/data-storage/LocationDataStorage.test.js +3 -2
- package/test/index.js +7 -31
- package/test/index.test.js +4 -5
- package/test/parseQueryFromSearch.test.js +19 -0
- package/test/parseQueryString.test.js +18 -0
- package/test/scroll-position/ScrollPositionRestoration.test.js +34 -13
- package/test/scroll-position/createApp.js +8 -8
- package/test/scroll-position/withScrollableContainerAtIndexPageWithDisabledAutomaticScrollPositionRestoration.js +4 -4
- package/test/session/{InMemorySession.test.js → Session.InMemoryEnvironment.test.js} +10 -9
- package/test/session/{ServerSession.test.js → Session.ServerSideRenderEnvironment.test.js} +5 -4
- package/test/session/{WebBrowserSession.test.js → Session.WebBrowserEnvironment.test.js} +63 -13
- package/test/shouldWarn.js +44 -0
- package/test/stringifyQuery.test.js +65 -0
- package/types/index.d.ts +180 -34
- package/types/tsconfig.json +0 -1
- package/data-storage/package.json +0 -7
- package/lib/cjs/createSearchFromQuery.js +0 -13
- package/lib/cjs/debug.js +0 -12
- package/lib/cjs/redux/ActionTypes.js +0 -14
- package/lib/cjs/redux/ActionTypesInternal.js +0 -8
- package/lib/cjs/redux/Actions.js +0 -28
- package/lib/cjs/redux/createMiddlewares.js +0 -60
- package/lib/cjs/redux/index.js +0 -13
- package/lib/cjs/redux/internalLocationReducer.js +0 -14
- package/lib/cjs/redux/locationReducer.js +0 -13
- package/lib/cjs/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -32
- package/lib/cjs/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -113
- package/lib/cjs/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -94
- package/lib/cjs/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -30
- package/lib/cjs/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -73
- package/lib/cjs/redux/middleware/navigationOperationMiddleware.js +0 -40
- package/lib/cjs/redux/middleware/parseInputLocationMiddleware.js +0 -29
- package/lib/cjs/redux/middleware/updateLocationMiddleware.js +0 -34
- package/lib/cjs/session/InMemorySession.js +0 -22
- package/lib/cjs/session/WebBrowserSession.js +0 -20
- package/lib/data-storage/index.d.ts +0 -35
- package/lib/esm/createSearchFromQuery.js +0 -8
- package/lib/esm/debug.js +0 -7
- package/lib/esm/redux/ActionTypes.js +0 -9
- package/lib/esm/redux/ActionTypesInternal.js +0 -3
- package/lib/esm/redux/Actions.js +0 -22
- package/lib/esm/redux/createMiddlewares.js +0 -54
- package/lib/esm/redux/index.js +0 -4
- package/lib/esm/redux/internalLocationReducer.js +0 -8
- package/lib/esm/redux/locationReducer.js +0 -7
- package/lib/esm/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -27
- package/lib/esm/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -108
- package/lib/esm/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -88
- package/lib/esm/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -25
- package/lib/esm/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -68
- package/lib/esm/redux/middleware/navigationOperationMiddleware.js +0 -35
- package/lib/esm/redux/middleware/parseInputLocationMiddleware.js +0 -24
- package/lib/esm/redux/middleware/updateLocationMiddleware.js +0 -28
- package/lib/esm/session/InMemorySession.js +0 -15
- package/lib/esm/session/ServerSideRenderSession.js +0 -11
- package/lib/esm/session/WebBrowserSession.js +0 -13
- package/lib/redux/index.d.ts +0 -90
- package/lib/scroll-position/index.d.ts +0 -107
- package/redux/package.json +0 -7
- package/scroll-position/package.json +0 -7
- package/src/createSearchFromQuery.js +0 -9
- package/src/debug.js +0 -8
- package/src/redux/ActionTypes.js +0 -9
- package/src/redux/ActionTypesInternal.js +0 -3
- package/src/redux/Actions.js +0 -27
- package/src/redux/createMiddlewares.js +0 -65
- package/src/redux/index.js +0 -4
- package/src/redux/internalLocationReducer.js +0 -9
- package/src/redux/locationReducer.js +0 -8
- package/src/redux/middleware/createAddInputLocationBasePathMiddleware.js +0 -27
- package/src/redux/middleware/createNonProgrammaticNavigationBlockerMiddleware.js +0 -119
- package/src/redux/middleware/createProgrammaticNavigationBlockerMiddleware.js +0 -94
- package/src/redux/middleware/createRemoveOutputLocationBasePathMiddleware.js +0 -26
- package/src/redux/middleware/createUpdateInternalLocationMiddleware.js +0 -72
- package/src/redux/middleware/navigationOperationMiddleware.js +0 -34
- package/src/redux/middleware/parseInputLocationMiddleware.js +0 -23
- package/src/redux/middleware/updateLocationMiddleware.js +0 -28
- package/src/session/InMemorySession.js +0 -13
- package/src/session/ServerSideRenderSession.js +0 -9
- package/src/session/WebBrowserSession.js +0 -13
- package/test/middlewareTestUtil.js +0 -31
- package/test/redux/Action.test.js +0 -73
- package/test/redux/ActionTypes.test.js +0 -13
- package/test/redux/createMiddlewares.test.js +0 -96
- package/test/redux/index.test.js +0 -10
- package/test/redux/locationReducer.test.js +0 -39
- package/test/redux/middleware/createAddInputLocationBasePathMiddleware.test.js +0 -40
- package/test/redux/middleware/createRemoveOutputLocationBasePathMiddleware.test.js +0 -51
- package/test/redux/middleware/navigationOperationMiddleware.test.js +0 -78
- package/test/redux/middleware/parseInputLocationMiddleware.test.js +0 -62
- package/test/testUtil.js +0 -3
- package/types/data-storage/index.d.ts +0 -35
- package/types/redux/index.d.ts +0 -90
- package/types/scroll-position/index.d.ts +0 -107
- /package/lib/cjs/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
- /package/lib/cjs/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
- /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
- /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
- /package/lib/cjs/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
- /package/lib/cjs/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
- /package/lib/cjs/{session → environment}/navigation/operation/operations.js +0 -0
- /package/lib/esm/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
- /package/lib/esm/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
- /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
- /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
- /package/lib/esm/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
- /package/lib/esm/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
- /package/lib/esm/{session → environment}/navigation/operation/operations.js +0 -0
- /package/src/{session → environment}/lifecycle/InMemorySessionLifecycle.js +0 -0
- /package/src/{session → environment}/lifecycle/WebBrowserSessionLifecycle.js +0 -0
- /package/src/{session → environment}/lifecycle/page-lifecycle/PageLifecycle.js +0 -0
- /package/src/{session → environment}/lifecycle/page-lifecycle/PageLifecycleInstance.js +0 -0
- /package/src/{session → environment}/lifecycle/page-lifecycle/supportsConstructableEventTarget.js +0 -0
- /package/src/{session → environment}/navigation/error/NavigationOutOfBoundsError.js +0 -0
- /package/src/{session → environment}/navigation/operation/operations.js +0 -0
- /package/test/{parseInputLocationMiddleware.test.js → parseInputLocation.test.js} +0 -0
package/lib/cjs/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
|
-
exports.removeBasePath = exports.parseLocationUrl = exports.parseInputLocation = exports.getLocationUrl = exports.addNavigationBlocker = exports.addBasePath = exports.
|
|
4
|
+
exports.removeBasePath = exports.parseLocationUrl = exports.parseInputLocation = exports.getLocationUrl = exports.addNavigationBlocker = exports.addBasePath = exports.WebBrowserEnvironment = exports.ServerSideRenderEnvironment = exports.ServerSideRedirectError = exports.NavigationStack = exports.NavigationOutOfBoundsError = exports.InMemoryEnvironment = void 0;
|
|
5
5
|
var _basePath = require("./basePath");
|
|
6
6
|
exports.addBasePath = _basePath.addBasePath;
|
|
7
7
|
exports.removeBasePath = _basePath.removeBasePath;
|
|
@@ -15,20 +15,14 @@ var _parseInputLocation = _interopRequireDefault(require("./parseInputLocation")
|
|
|
15
15
|
exports.parseInputLocation = _parseInputLocation.default;
|
|
16
16
|
var _NavigationStack = _interopRequireDefault(require("./NavigationStack"));
|
|
17
17
|
exports.NavigationStack = _NavigationStack.default;
|
|
18
|
-
var
|
|
19
|
-
exports.
|
|
20
|
-
var
|
|
21
|
-
exports.
|
|
22
|
-
var
|
|
23
|
-
exports.
|
|
24
|
-
var
|
|
25
|
-
exports.
|
|
26
|
-
var
|
|
27
|
-
exports.WebBrowserSession = _WebBrowserSession.default;
|
|
28
|
-
var _ServerSideRenderSession = _interopRequireDefault(require("./session/ServerSideRenderSession"));
|
|
29
|
-
exports.ServerSideRenderSession = _ServerSideRenderSession.default;
|
|
30
|
-
var _ServerSideNavigationError = _interopRequireDefault(require("./session/navigation/error/ServerSideNavigationError"));
|
|
31
|
-
exports.ServerSideNavigationError = _ServerSideNavigationError.default;
|
|
32
|
-
var _NavigationOutOfBoundsError = _interopRequireDefault(require("./session/navigation/error/NavigationOutOfBoundsError"));
|
|
18
|
+
var _InMemoryEnvironment = _interopRequireDefault(require("./environment/InMemoryEnvironment"));
|
|
19
|
+
exports.InMemoryEnvironment = _InMemoryEnvironment.default;
|
|
20
|
+
var _WebBrowserEnvironment = _interopRequireDefault(require("./environment/WebBrowserEnvironment"));
|
|
21
|
+
exports.WebBrowserEnvironment = _WebBrowserEnvironment.default;
|
|
22
|
+
var _ServerSideRenderEnvironment = _interopRequireDefault(require("./environment/ServerSideRenderEnvironment"));
|
|
23
|
+
exports.ServerSideRenderEnvironment = _ServerSideRenderEnvironment.default;
|
|
24
|
+
var _ServerSideRedirectError = _interopRequireDefault(require("./environment/navigation/error/ServerSideRedirectError"));
|
|
25
|
+
exports.ServerSideRedirectError = _ServerSideRedirectError.default;
|
|
26
|
+
var _NavigationOutOfBoundsError = _interopRequireDefault(require("./environment/navigation/error/NavigationOutOfBoundsError"));
|
|
33
27
|
exports.NavigationOutOfBoundsError = _NavigationOutOfBoundsError.default;
|
|
34
28
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -5,58 +5,55 @@ exports.addNavigationBlocker = addNavigationBlocker;
|
|
|
5
5
|
exports.getNavigationBlockers = getNavigationBlockers;
|
|
6
6
|
exports.removeAllNavigationBlockers = removeAllNavigationBlockers;
|
|
7
7
|
exports.runNavigationBlockers = runNavigationBlockers;
|
|
8
|
-
var _debug = _interopRequireDefault(require("./debug"));
|
|
9
8
|
var _isPromise = _interopRequireDefault(require("./isPromise"));
|
|
10
9
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
10
|
/* eslint-disable no-underscore-dangle */
|
|
12
11
|
|
|
13
|
-
function getNavigationBlockers(
|
|
14
|
-
return
|
|
12
|
+
function getNavigationBlockers(container) {
|
|
13
|
+
return container._navigationBlockersList || [];
|
|
15
14
|
}
|
|
16
|
-
function addNavigationBlockerToTheList(blocker,
|
|
17
|
-
if (!
|
|
18
|
-
|
|
15
|
+
function addNavigationBlockerToTheList(blocker, container) {
|
|
16
|
+
if (!container._navigationBlockersList) {
|
|
17
|
+
container._navigationBlockersList = [];
|
|
19
18
|
}
|
|
20
|
-
|
|
19
|
+
container._navigationBlockersList.push(blocker);
|
|
21
20
|
}
|
|
22
|
-
function removeNavigationBlockerFromTheList(blocker,
|
|
23
|
-
if (
|
|
24
|
-
|
|
21
|
+
function removeNavigationBlockerFromTheList(blocker, container) {
|
|
22
|
+
if (container._navigationBlockersList) {
|
|
23
|
+
container._navigationBlockersList = container._navigationBlockersList.filter(_ => _ !== blocker);
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
26
|
function removeAllNavigationBlockers(session) {
|
|
28
|
-
|
|
27
|
+
// `navigationBlockers` are stored in `session`.
|
|
28
|
+
const container = session;
|
|
29
|
+
if (getNavigationBlockers(container).some(blocker => blocker.beforeTermination)) {
|
|
29
30
|
if (!session._removeTerminationBlocker) {
|
|
30
31
|
throw new Error('`_removeTerminationBlocker` property not found in the `session`');
|
|
31
32
|
}
|
|
32
33
|
session._removeTerminationBlocker();
|
|
33
34
|
session._removeTerminationBlocker = undefined;
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
+
container._navigationBlockersList = [];
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
// Runs the `blocker` while ignoring any errors that might be thrown by it.
|
|
39
40
|
function runNavigationBlocker({
|
|
40
41
|
blocker
|
|
41
|
-
}, location) {
|
|
42
|
+
}, location, environment) {
|
|
42
43
|
let result;
|
|
43
44
|
try {
|
|
44
45
|
result = blocker(location);
|
|
45
46
|
} catch (error) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// eslint-disable-next-line no-console
|
|
49
|
-
console.error(error);
|
|
47
|
+
environment.log.warn(`Ignoring navigation blocker \`${blocker.name}\` that failed with \`${error}\`.`);
|
|
48
|
+
environment.log.error(error);
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
// If the blocker returned a `Promise`, await for that `Promise`
|
|
53
52
|
// and then return the result.
|
|
54
53
|
if ((0, _isPromise.default)(result)) {
|
|
55
54
|
return result.catch(error => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// eslint-disable-next-line no-console
|
|
59
|
-
console.error(error);
|
|
55
|
+
environment.log.warn(`Ignoring navigation blocker \`${blocker.name}\` that failed with \`${error}\`.`);
|
|
56
|
+
environment.log.error(error);
|
|
60
57
|
});
|
|
61
58
|
}
|
|
62
59
|
// The blocker didn't return a `Promise`.
|
|
@@ -67,28 +64,28 @@ function runNavigationBlocker({
|
|
|
67
64
|
// Runs all blockers in order.
|
|
68
65
|
// If any blocker returns `true`, it stops and returns the result.
|
|
69
66
|
// If there's no such blocker, returns `undefined`.
|
|
70
|
-
function runNavigationBlockers(navigationBlockers, toLocation) {
|
|
67
|
+
function runNavigationBlockers(navigationBlockers, toLocation, environment) {
|
|
71
68
|
if (navigationBlockers.length === 0) {
|
|
72
69
|
return undefined;
|
|
73
70
|
}
|
|
74
71
|
|
|
75
72
|
// Call the first blocker in the list.
|
|
76
|
-
const result = runNavigationBlocker(navigationBlockers[0], toLocation);
|
|
73
|
+
const result = runNavigationBlocker(navigationBlockers[0], toLocation, environment);
|
|
77
74
|
const next = () => {
|
|
78
75
|
// Proceed to the next blocker.
|
|
79
|
-
return runNavigationBlockers(navigationBlockers.slice(1), toLocation);
|
|
76
|
+
return runNavigationBlockers(navigationBlockers.slice(1), toLocation, environment);
|
|
80
77
|
};
|
|
81
78
|
if ((0, _isPromise.default)(result)) {
|
|
82
79
|
return result.then(resultValue => {
|
|
83
80
|
if (resultValue) {
|
|
84
|
-
|
|
81
|
+
environment.log.debug('Navigation blocked', toLocation.pathname);
|
|
85
82
|
return resultValue;
|
|
86
83
|
}
|
|
87
84
|
return next();
|
|
88
85
|
});
|
|
89
86
|
}
|
|
90
87
|
if (result) {
|
|
91
|
-
|
|
88
|
+
environment.log.debug('Navigation blocked', toLocation.pathname);
|
|
92
89
|
return result;
|
|
93
90
|
}
|
|
94
91
|
return next();
|
|
@@ -96,7 +93,9 @@ function runNavigationBlockers(navigationBlockers, toLocation) {
|
|
|
96
93
|
|
|
97
94
|
/* istanbul ignore next: not testable with Karma */
|
|
98
95
|
function terminationBlocker(session) {
|
|
99
|
-
|
|
96
|
+
// `navigationBlockers` are stored in `session`.
|
|
97
|
+
const container = session;
|
|
98
|
+
const result = runNavigationBlockers(getNavigationBlockers(container), null, session.environment);
|
|
100
99
|
|
|
101
100
|
// If no blocker returned anything, so don't prevent the navigation.
|
|
102
101
|
if (!result) {
|
|
@@ -115,6 +114,9 @@ function terminationBlocker(session) {
|
|
|
115
114
|
return true;
|
|
116
115
|
}
|
|
117
116
|
function addNavigationBlocker(session, blocker) {
|
|
117
|
+
// `navigationBlockers` are stored in `session`.
|
|
118
|
+
const container = session;
|
|
119
|
+
|
|
118
120
|
// All navigation blockers also run on `beforeTermination` event.
|
|
119
121
|
// If required, this could be a parameter of this function.
|
|
120
122
|
// The rationale could be that adding a `beforeunload` listener
|
|
@@ -131,11 +133,11 @@ function addNavigationBlocker(session, blocker) {
|
|
|
131
133
|
// and this is bad for performance."
|
|
132
134
|
//
|
|
133
135
|
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
|
|
134
|
-
if (beforeTermination && !getNavigationBlockers(
|
|
136
|
+
if (beforeTermination && !getNavigationBlockers(container).some(navigationBlocker => navigationBlocker.beforeTermination)) {
|
|
135
137
|
if (session._removeTerminationBlocker) {
|
|
136
138
|
throw new Error('Unexpected `_removeTerminationBlocker` property found in the `session`');
|
|
137
139
|
}
|
|
138
|
-
session._removeTerminationBlocker = session.lifecycle.addTerminationBlocker(() => {
|
|
140
|
+
session._removeTerminationBlocker = session.environment.lifecycle.addTerminationBlocker(() => {
|
|
139
141
|
return terminationBlocker(session);
|
|
140
142
|
});
|
|
141
143
|
}
|
|
@@ -143,12 +145,12 @@ function addNavigationBlocker(session, blocker) {
|
|
|
143
145
|
blocker,
|
|
144
146
|
beforeTermination
|
|
145
147
|
};
|
|
146
|
-
addNavigationBlockerToTheList(newNavigationBlocker,
|
|
148
|
+
addNavigationBlockerToTheList(newNavigationBlocker, container);
|
|
147
149
|
return () => {
|
|
148
|
-
removeNavigationBlockerFromTheList(newNavigationBlocker,
|
|
150
|
+
removeNavigationBlockerFromTheList(newNavigationBlocker, container);
|
|
149
151
|
|
|
150
152
|
// If it was the last "beforeTermination" blocker, remove navigation blocker.
|
|
151
|
-
if (beforeTermination && !getNavigationBlockers(
|
|
153
|
+
if (beforeTermination && !getNavigationBlockers(container).some(navigationBlocker => navigationBlocker.beforeTermination)) {
|
|
152
154
|
if (!session._removeTerminationBlocker) {
|
|
153
155
|
throw new Error('`_removeTerminationBlocker` property not found in the `session`');
|
|
154
156
|
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.blockNonProgrammaticNavigationIfRequired = blockNonProgrammaticNavigationIfRequired;
|
|
5
|
+
exports.blockProgrammaticNavigationIfRequired = blockProgrammaticNavigationIfRequired;
|
|
6
|
+
var _getLocationBaseFromLocation = _interopRequireDefault(require("./getLocationBaseFromLocation"));
|
|
7
|
+
var _getLocationFromInternalLocation = _interopRequireDefault(require("./getLocationFromInternalLocation"));
|
|
8
|
+
var _isPromise = _interopRequireDefault(require("./isPromise"));
|
|
9
|
+
var _navigationBlockers = require("./navigationBlockers");
|
|
10
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
// Creates "navigation blockers evaluation" status object.
|
|
12
|
+
// It tracks the "cancelled" status of the evaluation:
|
|
13
|
+
// when next navigation happens, the previous one is no longer relevant
|
|
14
|
+
// so the evaluation of navigation blockers for it can be cancelled.
|
|
15
|
+
function createNavigationBlockersEvaluationStatus(container) {
|
|
16
|
+
/* eslint-disable no-underscore-dangle */
|
|
17
|
+
if (container._navigationBlockersEvaluationStatus) {
|
|
18
|
+
container._navigationBlockersEvaluationStatus.cancelled = true;
|
|
19
|
+
}
|
|
20
|
+
container._navigationBlockersEvaluationStatus = {
|
|
21
|
+
cancelled: false
|
|
22
|
+
};
|
|
23
|
+
return container._navigationBlockersEvaluationStatus;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Prevents or allows navigation that was initiated by the application code
|
|
27
|
+
// by making a `.push()` or `.replace()` method call.
|
|
28
|
+
//
|
|
29
|
+
// It doesn't handle `.shift()` method calls because it doesn't yet know
|
|
30
|
+
// the `location` that it's gonna `shift` to. Instead, it waits for the web browser
|
|
31
|
+
// to "shift" to that `location` and then reads the `location` from the address bar
|
|
32
|
+
// and, if such "shift" should've been blocked, it "rewinds" the address bar back
|
|
33
|
+
// to the previous location. This part is handled by another function.
|
|
34
|
+
//
|
|
35
|
+
// Such type of "shifting" and then rewinding the "shift" doesn't really matter to the application code at all.
|
|
36
|
+
// From the application code's point of view, all web browser's address bar doesn't matter and even doesn't exist.
|
|
37
|
+
// All that exists from the application code's point of view is the `location` object in the `NavigationStack`'s state.
|
|
38
|
+
// Until the `location` object in the `NavigationStack`'s state is updated, the "old" page is still rendered.
|
|
39
|
+
// The appliation is only concerned with the updates of the `location` object in the `NavigationStack`'s state
|
|
40
|
+
// and completely ignores any updates to the URL in the web browser's address bar.
|
|
41
|
+
//
|
|
42
|
+
function blockProgrammaticNavigationIfRequired(toLocationBase,
|
|
43
|
+
// `location` of type `LocationBase`
|
|
44
|
+
session) {
|
|
45
|
+
// `resultValue` variable name works around a stupid javascript error:
|
|
46
|
+
// "Cannot redeclare block-scoped variable 'result'".
|
|
47
|
+
const result = (0, _navigationBlockers.runNavigationBlockers)((0, _navigationBlockers.getNavigationBlockers)(session),
|
|
48
|
+
// Here `payload.location` is `LocationBase`.
|
|
49
|
+
toLocationBase, session.environment);
|
|
50
|
+
if ((0, _isPromise.default)(result)) {
|
|
51
|
+
const evaluationStatus = createNavigationBlockersEvaluationStatus(session);
|
|
52
|
+
// eslint-disable-next-line consistent-return
|
|
53
|
+
return result.then(promiseResult => {
|
|
54
|
+
if (evaluationStatus.cancelled) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return promiseResult;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Runs navigation blockers on internal location update and "undoes" the location update
|
|
64
|
+
// if it should've been blocked.
|
|
65
|
+
//
|
|
66
|
+
// One could ask: Why the hassle of running navigation blockers on internal location update
|
|
67
|
+
// and then rewinding back if the location change should've been blocked?
|
|
68
|
+
// Why not just run navigation blockers on `.push()`/`.replace()`/`.shift()`?
|
|
69
|
+
//
|
|
70
|
+
// The reason why it runs on internal location update here is because
|
|
71
|
+
// aside from programmatic `.shift()` that can be initiated from the application code,
|
|
72
|
+
// there's non-programmatic "shift" navigation when the user manually clicks "Back" or "Forward" button
|
|
73
|
+
// in a web browser. And even if a "shift" navigation is initiated programmatically in the application code,
|
|
74
|
+
// it still doesn't know yet what the new location is gonna be cause it only knows the numeric `delta`.
|
|
75
|
+
// Such cases could only be handled by reacting to internal location updates which,
|
|
76
|
+
// in case of "Back"/"Forward", only happen after the URL in the browser's address bar has changed.
|
|
77
|
+
//
|
|
78
|
+
// There's no real drawback in reacting to an internal location update "post factum" because
|
|
79
|
+
// from the application code's point of view, web browser's address bar doesn't matter and even doesn't exist.
|
|
80
|
+
// All that exists from the application code's point of view is the `navigationStack.current` location
|
|
81
|
+
// returned from the `NavigationStack`. Until that location is updated, the "old" page is still rendered.
|
|
82
|
+
// The appliation is only concerned with the updates of the internal `location` object in the `NavigationStack``
|
|
83
|
+
// and completely ignores any updates to the URL in the web browser's address bar.
|
|
84
|
+
//
|
|
85
|
+
// So here, the code attempts to prevent or allow navigation that has already happened
|
|
86
|
+
// in the web browser's address bar but hasn't yet happened in the `NavigationStack`'s state.
|
|
87
|
+
// For example, it could be a user clicking a "Back"/"Forward" button in a web browser.
|
|
88
|
+
// If such navigation should've been blocked, it will simply not update the `location` object
|
|
89
|
+
// in the `NavigationStack`'s state, and it will also "rewind" the change of the URL in the web browser's
|
|
90
|
+
// address bar so that it's consistent with the `location` in the `NavigationStack`'s state.
|
|
91
|
+
//
|
|
92
|
+
// Returns either a `boolean` value or a `Promise` that resolves to a `boolean` value:
|
|
93
|
+
// * `false` when navigation should not have been blocked and therefore was not "rewinded".
|
|
94
|
+
// * `true` when navigation should have been blocked and therefore was "rewinded".
|
|
95
|
+
// * `true` when it "rewinded" the navigation "just in case" and then started evaluating async blockers,
|
|
96
|
+
// but while doing that, next navigation already happened so this one is no longer relevant.
|
|
97
|
+
//
|
|
98
|
+
function blockNonProgrammaticNavigationIfRequired(toLocationInternal,
|
|
99
|
+
// `location` of type `LocationInternal`
|
|
100
|
+
session, doAndIgnoreLocationUpdates) {
|
|
101
|
+
// If there're no navigation blockers to run, don't do anything.
|
|
102
|
+
if ((0, _navigationBlockers.getNavigationBlockers)(session).length === 0) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// If it was the initial page load or a redirect,
|
|
107
|
+
// it's not really a navigation that could be rolled back.
|
|
108
|
+
if (toLocationInternal.delta === 0) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const result = (0, _navigationBlockers.runNavigationBlockers)((0, _navigationBlockers.getNavigationBlockers)(session), (0, _getLocationBaseFromLocation.default)((0, _getLocationFromInternalLocation.default)(toLocationInternal)), session.environment);
|
|
112
|
+
|
|
113
|
+
// If some navigation blocker returned a `Promise`.
|
|
114
|
+
if ((0, _isPromise.default)(result)) {
|
|
115
|
+
const evaluationStatus = createNavigationBlockersEvaluationStatus(session);
|
|
116
|
+
|
|
117
|
+
// While location blockers are running, rewind to the previous location.
|
|
118
|
+
doAndIgnoreLocationUpdates(() => {
|
|
119
|
+
session.shift(-toLocationInternal.delta);
|
|
120
|
+
});
|
|
121
|
+
return result.then(promiseResult => {
|
|
122
|
+
if (evaluationStatus.cancelled) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
if (promiseResult) {
|
|
126
|
+
// Navigation blocked.
|
|
127
|
+
// Already rewound to a previous location.
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
// Navigation not blocked.
|
|
131
|
+
// Rewind back to the new location.
|
|
132
|
+
doAndIgnoreLocationUpdates(() => {
|
|
133
|
+
session.shift(toLocationInternal.delta);
|
|
134
|
+
});
|
|
135
|
+
// Update the location.
|
|
136
|
+
return false;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Navigation blockers did not return a `Promise`.
|
|
141
|
+
if (result) {
|
|
142
|
+
// Prevent the navigation: rewind to the previous location.
|
|
143
|
+
doAndIgnoreLocationUpdates(() => {
|
|
144
|
+
session.shift(-toLocationInternal.delta);
|
|
145
|
+
});
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
// Update the location.
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
4
|
exports.default = parseInputLocation;
|
|
5
|
-
var _createSearchFromQuery = _interopRequireDefault(require("./createSearchFromQuery"));
|
|
6
5
|
var _parseLocationUrl = _interopRequireDefault(require("./parseLocationUrl"));
|
|
7
6
|
var _parseQueryFromSearch = _interopRequireDefault(require("./parseQueryFromSearch"));
|
|
7
|
+
var _stringifyQueryAsSearch = _interopRequireDefault(require("./stringifyQueryAsSearch"));
|
|
8
8
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
9
|
function stringifyQueryParameterValue(value) {
|
|
10
10
|
if (value === null || value === undefined) {
|
|
@@ -45,7 +45,7 @@ function parseInputLocation(location) {
|
|
|
45
45
|
// if `query` is present but `search` is not.
|
|
46
46
|
if (location.query && !location.search) {
|
|
47
47
|
location = Object.assign({}, location, {
|
|
48
|
-
search: (0,
|
|
48
|
+
search: (0, _stringifyQueryAsSearch.default)(location.query)
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
4
|
exports.default = parseQueryFromSearch;
|
|
5
|
-
var
|
|
5
|
+
var _parseQueryString = _interopRequireDefault(require("./parseQueryString"));
|
|
6
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
7
|
function parseQueryFromSearch(search) {
|
|
7
8
|
if (search.length > '?'.length) {
|
|
8
|
-
|
|
9
|
-
return (0, _queryString.parse)(search.slice(1));
|
|
10
|
-
} catch (error) {
|
|
11
|
-
// Ignore any query parsing errors.
|
|
12
|
-
}
|
|
9
|
+
return (0, _parseQueryString.default)(search.slice('?'.length));
|
|
13
10
|
}
|
|
14
11
|
return {};
|
|
15
12
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.default = parseQueryString;
|
|
5
|
+
function splitAtFirstOccurence(string, separator) {
|
|
6
|
+
const separatorIndex = string.indexOf(separator);
|
|
7
|
+
if (separatorIndex === -1) {
|
|
8
|
+
return [string, ''];
|
|
9
|
+
}
|
|
10
|
+
return [string.slice(0, separatorIndex), string.slice(separatorIndex + separator.length)];
|
|
11
|
+
}
|
|
12
|
+
function decode(value) {
|
|
13
|
+
// There's a convention that a space character could be encoded
|
|
14
|
+
// either as "%20" or as "+". Both of them are valid.
|
|
15
|
+
// The "+" character is unusally preferred because it results in a more
|
|
16
|
+
// human-readable URL.
|
|
17
|
+
//
|
|
18
|
+
// https://dev.to/lico/understanding-how-spaces-are-encoded-20-with-encodeuri-vs-with-url-2d6c
|
|
19
|
+
// https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding
|
|
20
|
+
//
|
|
21
|
+
// Those "+" characters don't get transformed to spaces by `decodeURIComponent()` function.
|
|
22
|
+
// This means that they should be transformed to spaces manually.
|
|
23
|
+
//
|
|
24
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#decoding_query_parameters_from_a_url
|
|
25
|
+
//
|
|
26
|
+
value = value.replaceAll('+', ' ');
|
|
27
|
+
|
|
28
|
+
// `decodeURIComponent()` could throw an error of class `URIError`.
|
|
29
|
+
//
|
|
30
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError
|
|
31
|
+
//
|
|
32
|
+
// Example: "URIError: malformed URI sequence".
|
|
33
|
+
try {
|
|
34
|
+
return decodeURIComponent(value);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
// eslint-disable-next-line no-console
|
|
37
|
+
console.error(error);
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function parseQueryString(queryString) {
|
|
42
|
+
// Create an object with no prototype
|
|
43
|
+
const query = Object.create(null);
|
|
44
|
+
|
|
45
|
+
// query parameter parsing is described in the specification:
|
|
46
|
+
// https://url.spec.whatwg.org/#urlencoded-parsing
|
|
47
|
+
for (const keyValuePair of queryString.split('&')) {
|
|
48
|
+
if (!keyValuePair) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
let [key, value] = splitAtFirstOccurence(keyValuePair, '=');
|
|
52
|
+
|
|
53
|
+
// If `key` is empty, the specification considers this a valid case with `key: null`.
|
|
54
|
+
// But, there seems to be no practical use for a query parameter with `key: null`.
|
|
55
|
+
// So just skip it.
|
|
56
|
+
if (!key) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
key = decode(key);
|
|
60
|
+
|
|
61
|
+
// According to the specification, missing `=` should be treated as `value: null`.
|
|
62
|
+
if (value === '') {
|
|
63
|
+
value = null;
|
|
64
|
+
} else {
|
|
65
|
+
value = decode(value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// The handling of duplicate URL query parameters is not explicitly defined by a single,
|
|
69
|
+
// universally enforced specification. Hence, we just assume such query parameters invalid
|
|
70
|
+
// and only include the first occurrence of the query parameter in the query string.
|
|
71
|
+
if (query[key] === undefined) {
|
|
72
|
+
query[key] = value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return query;
|
|
76
|
+
}
|
|
77
|
+
module.exports = exports.default;
|
|
@@ -4,17 +4,18 @@ exports.__esModule = true;
|
|
|
4
4
|
exports.default = void 0;
|
|
5
5
|
var _constants = require("./constants");
|
|
6
6
|
var _scheduleNextTick = _interopRequireDefault(require("./scheduleNextTick"));
|
|
7
|
-
var _debug = _interopRequireDefault(require("../debug"));
|
|
8
7
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
8
|
/* eslint-disable no-underscore-dangle */
|
|
10
9
|
|
|
11
10
|
class ScrollPositionAutoSaver {
|
|
12
11
|
constructor({
|
|
12
|
+
log,
|
|
13
13
|
scrollPosition,
|
|
14
14
|
scrollPositionSaver,
|
|
15
15
|
getScrollableContainers,
|
|
16
16
|
shouldSaveScrollPosition
|
|
17
17
|
}) {
|
|
18
|
+
this._log = log;
|
|
18
19
|
this._scrollPosition = scrollPosition;
|
|
19
20
|
this._scrollPositionSaver = scrollPositionSaver;
|
|
20
21
|
this._shouldSaveScrollPosition = shouldSaveScrollPosition;
|
|
@@ -66,7 +67,7 @@ class ScrollPositionAutoSaver {
|
|
|
66
67
|
cancelSavePageScrollPosition(hasRun) {
|
|
67
68
|
if (this._cancelSavePageScrollPosition) {
|
|
68
69
|
if (!hasRun) {
|
|
69
|
-
|
|
70
|
+
this._log.debug('cancel delayed save scroll position', _constants.PAGE_SCROLLABLE_CONTAINER_KEY);
|
|
70
71
|
}
|
|
71
72
|
this._cancelSavePageScrollPosition();
|
|
72
73
|
this._cancelSavePageScrollPosition = null;
|
|
@@ -76,7 +77,7 @@ class ScrollPositionAutoSaver {
|
|
|
76
77
|
const scrollableContainerEntry = this._getScrollableContainers()[scrollableContainerKey];
|
|
77
78
|
if (scrollableContainerEntry.cancelSaveScrollPosition) {
|
|
78
79
|
if (!hasRun) {
|
|
79
|
-
|
|
80
|
+
this._log.debug('cancel delayed save scroll position', scrollableContainerKey);
|
|
80
81
|
}
|
|
81
82
|
scrollableContainerEntry.cancelSaveScrollPosition();
|
|
82
83
|
scrollableContainerEntry.cancelSaveScrollPosition = null;
|
|
@@ -108,9 +109,9 @@ class ScrollPositionAutoSaver {
|
|
|
108
109
|
// because there might be too many in a given short period of time
|
|
109
110
|
// which could affect the performance of the application.
|
|
110
111
|
if (!scrollableContainerEntry.cancelSaveScrollPosition) {
|
|
111
|
-
|
|
112
|
+
this._log.debug('scroll detected', scrollableContainerKey);
|
|
112
113
|
scrollableContainerEntry.cancelSaveScrollPosition = (0, _scheduleNextTick.default)(() => {
|
|
113
|
-
|
|
114
|
+
this._log.debug('auto-save scroll position after scroll', scrollableContainerKey);
|
|
114
115
|
this._scrollPositionSaver.saveScrollableContainerScrollPosition(scrollableContainerKey, scrollableContainerEntry.scrollableContainer);
|
|
115
116
|
});
|
|
116
117
|
}
|
|
@@ -119,7 +120,7 @@ class ScrollPositionAutoSaver {
|
|
|
119
120
|
addPageScrollListener() {
|
|
120
121
|
// Set up scroll listener on the page.
|
|
121
122
|
this._removePageScrollListener = this._scrollPosition.addPageScrollListener(() => {
|
|
122
|
-
|
|
123
|
+
this._log.debug('scroll detected', _constants.PAGE_SCROLLABLE_CONTAINER_KEY);
|
|
123
124
|
|
|
124
125
|
// This flag is not used in real life and is only used in tests (for some reason).
|
|
125
126
|
if (!this._shouldSaveScrollPosition()) {
|