@sapui5/sap.fe.core 1.98.0 → 1.99.0
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/package.json +3 -2
- package/src/sap/fe/core/.library +1 -1
- package/src/sap/fe/core/AnnotationHelper.js +377 -405
- package/src/sap/fe/core/AnnotationHelper.ts +385 -0
- package/src/sap/fe/core/AppComponent.js +3 -2
- package/src/sap/fe/core/AppStateHandler.js +229 -181
- package/src/sap/fe/core/AppStateHandler.ts +171 -0
- package/src/sap/fe/core/BaseController.js +3 -2
- package/src/sap/fe/core/BusyLocker.js +105 -121
- package/src/sap/fe/core/BusyLocker.ts +98 -0
- package/src/sap/fe/core/CommonUtils.js +2073 -2399
- package/src/sap/fe/core/CommonUtils.ts +2078 -0
- package/src/sap/fe/core/ExtensionAPI.js +3 -2
- package/src/sap/fe/core/PageController.js +84 -125
- package/src/sap/fe/core/PageController.ts +101 -0
- package/src/sap/fe/core/RouterProxy.js +986 -823
- package/src/sap/fe/core/RouterProxy.ts +838 -0
- package/src/sap/fe/core/Synchronization.js +51 -35
- package/src/sap/fe/core/Synchronization.ts +29 -0
- package/src/sap/fe/core/TemplateComponent.js +173 -164
- package/src/sap/fe/core/TemplateComponent.ts +166 -0
- package/src/sap/fe/core/TemplateModel.js +97 -54
- package/src/sap/fe/core/TemplateModel.ts +63 -0
- package/src/sap/fe/core/TransactionHelper.js +1576 -1664
- package/src/sap/fe/core/TransactionHelper.ts +1706 -0
- package/src/sap/fe/core/actions/draft.js +559 -608
- package/src/sap/fe/core/actions/draft.ts +593 -0
- package/src/sap/fe/core/actions/messageHandling.js +545 -514
- package/src/sap/fe/core/actions/messageHandling.ts +532 -0
- package/src/sap/fe/core/actions/nonDraft.js +17 -19
- package/src/sap/fe/core/actions/nonDraft.ts +12 -0
- package/src/sap/fe/core/actions/operations.js +1074 -1215
- package/src/sap/fe/core/actions/operations.ts +1162 -0
- package/src/sap/fe/core/actions/sticky.js +102 -105
- package/src/sap/fe/core/actions/sticky.ts +102 -0
- package/src/sap/fe/core/controllerextensions/ControllerExtensionMetadata.js +3 -2
- package/src/sap/fe/core/controllerextensions/EditFlow.js +54 -211
- package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.js +3 -2
- package/src/sap/fe/core/controllerextensions/InternalEditFlow.js +279 -11
- package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.js +8 -6
- package/src/sap/fe/core/controllerextensions/InternalRouting.js +47 -45
- package/src/sap/fe/core/controllerextensions/KPIManagement.js +2 -3
- package/src/sap/fe/core/controllerextensions/KPIManagement.ts +29 -26
- package/src/sap/fe/core/controllerextensions/MassEdit.js +101 -8
- package/src/sap/fe/core/controllerextensions/MessageHandler.js +17 -8
- package/src/sap/fe/core/controllerextensions/PageReady.js +2 -2
- package/src/sap/fe/core/controllerextensions/PageReady.ts +12 -8
- package/src/sap/fe/core/controllerextensions/Paginator.js +3 -2
- package/src/sap/fe/core/controllerextensions/Placeholder.js +3 -2
- package/src/sap/fe/core/controllerextensions/Routing.js +9 -4
- package/src/sap/fe/core/controllerextensions/RoutingListener.js +3 -2
- package/src/sap/fe/core/controllerextensions/Share.js +3 -2
- package/src/sap/fe/core/controllerextensions/SideEffects.js +3 -3
- package/src/sap/fe/core/controllerextensions/SideEffects.ts +18 -18
- package/src/sap/fe/core/controllerextensions/ViewState.js +3 -2
- package/src/sap/fe/core/controls/CommandExecution.js +3 -2
- package/src/sap/fe/core/controls/ConditionalWrapper.js +3 -2
- package/src/sap/fe/core/controls/CustomQuickViewPage.js +3 -2
- package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.js +3 -2
- package/src/sap/fe/core/controls/FieldWrapper.js +11 -22
- package/src/sap/fe/core/controls/FilterBar.js +3 -2
- package/src/sap/fe/core/controls/FormElementWrapper.js +3 -2
- package/src/sap/fe/core/controls/MultiValueParameterDelegate.js +3 -2
- package/src/sap/fe/core/controls/NonComputedVisibleKeyFieldsDialog.fragment.xml +1 -0
- package/src/sap/fe/core/controls/filterbar/FilterContainer.js +3 -2
- package/src/sap/fe/core/controls/filterbar/VisualFilter.js +5 -4
- package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.js +3 -2
- package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.js +3 -2
- package/src/sap/fe/core/controls/massEdit/MassEditHandlers.js +3 -2
- package/src/sap/fe/core/converters/ConverterContext.js +2 -2
- package/src/sap/fe/core/converters/ConverterContext.ts +4 -3
- package/src/sap/fe/core/converters/ManifestSettings.js +1 -1
- package/src/sap/fe/core/converters/ManifestSettings.ts +1 -0
- package/src/sap/fe/core/converters/ManifestWrapper.js +39 -26
- package/src/sap/fe/core/converters/ManifestWrapper.ts +9 -0
- package/src/sap/fe/core/converters/MetaModelConverter.js +11 -8
- package/src/sap/fe/core/converters/MetaModelConverter.ts +16 -16
- package/src/sap/fe/core/converters/TemplateConverter.js +1 -1
- package/src/sap/fe/core/converters/TemplateConverter.ts +2 -2
- package/src/sap/fe/core/converters/common/AnnotationConverter.js +17 -13
- package/src/sap/fe/core/converters/controls/Common/Action.js +2 -2
- package/src/sap/fe/core/converters/controls/Common/Action.ts +2 -2
- package/src/sap/fe/core/converters/controls/Common/Chart.js +5 -3
- package/src/sap/fe/core/converters/controls/Common/Chart.ts +11 -3
- package/src/sap/fe/core/converters/controls/Common/DataVisualization.js +3 -3
- package/src/sap/fe/core/converters/controls/Common/DataVisualization.ts +2 -2
- package/src/sap/fe/core/converters/controls/Common/Form.js +4 -2
- package/src/sap/fe/core/converters/controls/Common/Form.ts +16 -2
- package/src/sap/fe/core/converters/controls/Common/Table.js +250 -146
- package/src/sap/fe/core/converters/controls/Common/Table.ts +300 -167
- package/src/sap/fe/core/converters/controls/Common/table/StandardActions.js +115 -56
- package/src/sap/fe/core/converters/controls/Common/table/StandardActions.ts +120 -74
- package/src/sap/fe/core/converters/controls/ListReport/FilterBar.js +467 -32
- package/src/sap/fe/core/converters/controls/ListReport/FilterBar.ts +483 -54
- package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.js +6 -6
- package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.ts +9 -15
- package/src/sap/fe/core/converters/helpers/Aggregation.js +13 -5
- package/src/sap/fe/core/converters/helpers/Aggregation.ts +20 -5
- package/src/sap/fe/core/converters/helpers/IssueManager.js +4 -1
- package/src/sap/fe/core/converters/helpers/IssueManager.ts +3 -0
- package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.js +4 -4
- package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.ts +3 -3
- package/src/sap/fe/core/converters/templates/ListReportConverter.js +13 -6
- package/src/sap/fe/core/converters/templates/ListReportConverter.ts +26 -15
- package/src/sap/fe/core/designtime/AppComponent.designtime.js +3 -2
- package/src/sap/fe/core/formatters/CriticalityFormatter.js +1 -1
- package/src/sap/fe/core/formatters/CriticalityFormatter.ts +1 -1
- package/src/sap/fe/core/formatters/FPMFormatter.js +1 -1
- package/src/sap/fe/core/formatters/FPMFormatter.ts +4 -10
- package/src/sap/fe/core/formatters/KPIFormatter.js +1 -1
- package/src/sap/fe/core/formatters/KPIFormatter.ts +3 -1
- package/src/sap/fe/core/formatters/TableFormatter.js +39 -28
- package/src/sap/fe/core/formatters/TableFormatter.ts +43 -28
- package/src/sap/fe/core/formatters/ValueFormatter.js +1 -1
- package/src/sap/fe/core/formatters/ValueFormatter.ts +7 -6
- package/src/sap/fe/core/fpm/Component.js +3 -2
- package/src/sap/fe/core/helpers/AppStartupHelper.js +2 -2
- package/src/sap/fe/core/helpers/AppStartupHelper.ts +7 -4
- package/src/sap/fe/core/helpers/BindingExpression.js +314 -355
- package/src/sap/fe/core/helpers/BindingExpression.ts +317 -391
- package/src/sap/fe/core/helpers/ClassSupport.js +27 -15
- package/src/sap/fe/core/helpers/ClassSupport.ts +31 -20
- package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.js +63 -59
- package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.ts +56 -0
- package/src/sap/fe/core/helpers/EditState.js +81 -84
- package/src/sap/fe/core/helpers/EditState.ts +81 -0
- package/src/sap/fe/core/helpers/ExcelFormatHelper.js +62 -48
- package/src/sap/fe/core/helpers/ExcelFormatHelper.ts +49 -0
- package/src/sap/fe/core/helpers/FPMHelper.js +52 -56
- package/src/sap/fe/core/helpers/FPMHelper.ts +62 -0
- package/src/sap/fe/core/helpers/KeepAliveHelper.js +3 -4
- package/src/sap/fe/core/helpers/KeepAliveHelper.ts +9 -9
- package/src/sap/fe/core/helpers/MassEditHelper.js +27 -18
- package/src/sap/fe/core/helpers/ModelHelper.js +229 -225
- package/src/sap/fe/core/helpers/ModelHelper.ts +227 -0
- package/src/sap/fe/core/helpers/PasteHelper.js +210 -132
- package/src/sap/fe/core/helpers/PasteHelper.ts +196 -0
- package/src/sap/fe/core/helpers/SemanticDateOperators.js +332 -313
- package/src/sap/fe/core/helpers/SemanticDateOperators.ts +330 -0
- package/src/sap/fe/core/helpers/SemanticKeyHelper.js +66 -67
- package/src/sap/fe/core/helpers/SemanticKeyHelper.ts +73 -0
- package/src/sap/fe/core/helpers/StableIdHelper.js +4 -7
- package/src/sap/fe/core/helpers/StableIdHelper.ts +2 -6
- package/src/sap/fe/core/jsx-runtime/jsx.js +1 -1
- package/src/sap/fe/core/jsx-runtime/jsx.ts +1 -1
- package/src/sap/fe/core/library.js +4 -3
- package/src/sap/fe/core/library.support.js +3 -2
- package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.js +1 -1
- package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.ts +1 -1
- package/src/sap/fe/core/messagebundle.properties +23 -10
- package/src/sap/fe/core/messagebundle_ar.properties +9 -0
- package/src/sap/fe/core/messagebundle_bg.properties +9 -0
- package/src/sap/fe/core/messagebundle_ca.properties +9 -0
- package/src/sap/fe/core/messagebundle_cs.properties +10 -1
- package/src/sap/fe/core/messagebundle_cy.properties +9 -0
- package/src/sap/fe/core/messagebundle_da.properties +9 -0
- package/src/sap/fe/core/messagebundle_de.properties +9 -0
- package/src/sap/fe/core/messagebundle_el.properties +9 -0
- package/src/sap/fe/core/messagebundle_en.properties +9 -0
- package/src/sap/fe/core/messagebundle_en_GB.properties +9 -0
- package/src/sap/fe/core/messagebundle_en_US_sappsd.properties +9 -0
- package/src/sap/fe/core/messagebundle_en_US_saprigi.properties +9 -0
- package/src/sap/fe/core/messagebundle_en_US_saptrc.properties +9 -0
- package/src/sap/fe/core/messagebundle_es.properties +9 -0
- package/src/sap/fe/core/messagebundle_es_MX.properties +9 -0
- package/src/sap/fe/core/messagebundle_et.properties +9 -0
- package/src/sap/fe/core/messagebundle_fi.properties +9 -0
- package/src/sap/fe/core/messagebundle_fr.properties +13 -4
- package/src/sap/fe/core/messagebundle_fr_CA.properties +15 -6
- package/src/sap/fe/core/messagebundle_hi.properties +15 -6
- package/src/sap/fe/core/messagebundle_hr.properties +15 -6
- package/src/sap/fe/core/messagebundle_hu.properties +9 -0
- package/src/sap/fe/core/messagebundle_id.properties +10 -1
- package/src/sap/fe/core/messagebundle_it.properties +9 -0
- package/src/sap/fe/core/messagebundle_iw.properties +9 -0
- package/src/sap/fe/core/messagebundle_ja.properties +9 -0
- package/src/sap/fe/core/messagebundle_kk.properties +15 -6
- package/src/sap/fe/core/messagebundle_ko.properties +9 -0
- package/src/sap/fe/core/messagebundle_lt.properties +9 -0
- package/src/sap/fe/core/messagebundle_lv.properties +15 -6
- package/src/sap/fe/core/messagebundle_ms.properties +14 -5
- package/src/sap/fe/core/messagebundle_nl.properties +9 -0
- package/src/sap/fe/core/messagebundle_no.properties +9 -0
- package/src/sap/fe/core/messagebundle_pl.properties +9 -0
- package/src/sap/fe/core/messagebundle_pt.properties +9 -0
- package/src/sap/fe/core/messagebundle_pt_PT.properties +9 -0
- package/src/sap/fe/core/messagebundle_ro.properties +9 -0
- package/src/sap/fe/core/messagebundle_ru.properties +9 -0
- package/src/sap/fe/core/messagebundle_sh.properties +11 -2
- package/src/sap/fe/core/messagebundle_sk.properties +9 -0
- package/src/sap/fe/core/messagebundle_sl.properties +15 -6
- package/src/sap/fe/core/messagebundle_sv.properties +9 -0
- package/src/sap/fe/core/messagebundle_th.properties +9 -0
- package/src/sap/fe/core/messagebundle_tr.properties +9 -0
- package/src/sap/fe/core/messagebundle_uk.properties +9 -0
- package/src/sap/fe/core/messagebundle_vi.properties +9 -0
- package/src/sap/fe/core/messagebundle_zh_CN.properties +9 -0
- package/src/sap/fe/core/messagebundle_zh_TW.properties +15 -6
- package/src/sap/fe/core/services/AsyncComponentServiceFactory.js +1 -1
- package/src/sap/fe/core/services/AsyncComponentServiceFactory.ts +3 -1
- package/src/sap/fe/core/services/CacheHandlerServiceFactory.js +269 -202
- package/src/sap/fe/core/services/CacheHandlerServiceFactory.ts +212 -0
- package/src/sap/fe/core/services/EnvironmentServiceFactory.js +1 -1
- package/src/sap/fe/core/services/EnvironmentServiceFactory.ts +7 -6
- package/src/sap/fe/core/services/NavigationServiceFactory.js +406 -300
- package/src/sap/fe/core/services/NavigationServiceFactory.ts +316 -0
- package/src/sap/fe/core/services/ResourceModelServiceFactory.js +149 -81
- package/src/sap/fe/core/services/ResourceModelServiceFactory.ts +80 -0
- package/src/sap/fe/core/services/RoutingServiceFactory.js +987 -899
- package/src/sap/fe/core/services/RoutingServiceFactory.ts +898 -0
- package/src/sap/fe/core/services/ShellServicesFactory.js +1 -1
- package/src/sap/fe/core/services/ShellServicesFactory.ts +15 -10
- package/src/sap/fe/core/services/SideEffectsServiceFactory.js +35 -81
- package/src/sap/fe/core/services/SideEffectsServiceFactory.ts +46 -94
- package/src/sap/fe/core/services/TemplatedViewServiceFactory.js +461 -487
- package/src/sap/fe/core/services/TemplatedViewServiceFactory.ts +453 -0
- package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.js +10 -8
- package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.ts +8 -0
- package/src/sap/fe/core/support/AnnotationIssue.support.js +15 -3
- package/src/sap/fe/core/support/AnnotationIssue.support.ts +16 -2
- package/src/sap/fe/core/support/CollectionFacetUnsupportedLevel.support.js +2 -2
- package/src/sap/fe/core/support/CollectionFacetUnsupportedLevel.support.ts +1 -1
- package/src/sap/fe/core/templating/FieldControlHelper.js +8 -8
- package/src/sap/fe/core/templating/FieldControlHelper.ts +25 -7
- package/src/sap/fe/core/templating/FilterHelper.js +138 -72
- package/src/sap/fe/core/templating/FilterHelper.ts +139 -71
- package/src/sap/fe/core/templating/UIFormatters.js +31 -1
- package/src/sap/fe/core/templating/UIFormatters.ts +33 -2
- package/src/sap/fe/core/type/Email.js +1 -1
- package/src/sap/fe/core/type/Email.ts +4 -6
- package/ui5.yaml +0 -3
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
import Log from "sap/base/Log";
|
|
2
|
+
import BaseObject from "sap/ui/base/Object";
|
|
3
|
+
import Synchronization from "sap/fe/core/Synchronization";
|
|
4
|
+
import URI from "sap/ui/thirdparty/URI";
|
|
5
|
+
import { IShellServices } from "sap/fe/core/services/ShellServicesFactory";
|
|
6
|
+
import Router from "sap/ui/core/routing/Router";
|
|
7
|
+
import { UI5Class } from "sap/fe/core/helpers/ClassSupport";
|
|
8
|
+
import ResourceBundle from "sap/base/i18n/ResourceBundle";
|
|
9
|
+
|
|
10
|
+
const enumState = {
|
|
11
|
+
EQUAL: 0,
|
|
12
|
+
COMPATIBLE: 1,
|
|
13
|
+
ANCESTOR: 2,
|
|
14
|
+
DIFFERENT: 3
|
|
15
|
+
};
|
|
16
|
+
const enumURLParams = {
|
|
17
|
+
LAYOUTPARAM: "layout",
|
|
18
|
+
IAPPSTATEPARAM: "sap-iapp-state"
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Creates a HashGuard object.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} sGuardHash The hash used for the guard
|
|
25
|
+
* @returns {object} The created hash guard
|
|
26
|
+
*/
|
|
27
|
+
function createGuardFromHash(sGuardHash: string) {
|
|
28
|
+
return {
|
|
29
|
+
_guardHash: sGuardHash.replace(/\?[^\?]*$/, ""), // Remove query part
|
|
30
|
+
check: function(sHash: any) {
|
|
31
|
+
return sHash.indexOf(this._guardHash) === 0;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns the iAppState part from a hash (or null if not found).
|
|
37
|
+
*
|
|
38
|
+
* @param {string} sHash The hash
|
|
39
|
+
* @returns {string} The iAppState part of the hash
|
|
40
|
+
*/
|
|
41
|
+
function findAppStateInHash(sHash: string): string | null {
|
|
42
|
+
const aAppState = sHash.match(new RegExp("\\?.*" + enumURLParams.IAPPSTATEPARAM + "=([^&]*)"));
|
|
43
|
+
return aAppState && aAppState.length > 1 ? aAppState[1] : null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Returns a hash without its iAppState part.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} sHash The hash
|
|
49
|
+
* @returns {string} The hash without the iAppState
|
|
50
|
+
*/
|
|
51
|
+
function removeAppStateInHash(sHash: string) {
|
|
52
|
+
return sHash.replace(new RegExp("[&?]*" + enumURLParams.IAPPSTATEPARAM + "=[^&]*"), "");
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Adds an iAppState inside a hash (or replaces an existing one).
|
|
56
|
+
*
|
|
57
|
+
* @param {*} sHash The hash
|
|
58
|
+
* @param {*} sAppStateKey The iAppState to add
|
|
59
|
+
* @returns {string} The hash with the app state
|
|
60
|
+
*/
|
|
61
|
+
function setAppStateInHash(sHash: any, sAppStateKey: any) {
|
|
62
|
+
let sNewHash;
|
|
63
|
+
|
|
64
|
+
if (sHash.indexOf(enumURLParams.IAPPSTATEPARAM) >= 0) {
|
|
65
|
+
// If there's already an iAppState parameter in the hash, replace it
|
|
66
|
+
sNewHash = sHash.replace(new RegExp(enumURLParams.IAPPSTATEPARAM + "=[^&]*"), enumURLParams.IAPPSTATEPARAM + "=" + sAppStateKey);
|
|
67
|
+
} else {
|
|
68
|
+
// Add the iAppState parameter in the hash
|
|
69
|
+
if (sHash.indexOf("?") < 0) {
|
|
70
|
+
sNewHash = sHash + "?";
|
|
71
|
+
} else {
|
|
72
|
+
sNewHash = sHash + "&";
|
|
73
|
+
}
|
|
74
|
+
sNewHash += enumURLParams.IAPPSTATEPARAM + "=" + sAppStateKey;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return sNewHash;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@UI5Class("sap.fe.core.RouterProxy")
|
|
81
|
+
class RouterProxy extends BaseObject {
|
|
82
|
+
bIsRebuildHistoryRunning = false;
|
|
83
|
+
bIsComputingTitleHierachy = false;
|
|
84
|
+
bIsGuardCrossAllowed = false;
|
|
85
|
+
sIAppStateKey: string | null = null;
|
|
86
|
+
_oShellServices!: IShellServices;
|
|
87
|
+
fclEnabled!: boolean;
|
|
88
|
+
_fnBlockingNavFilter!: Function;
|
|
89
|
+
_fnHashGuard!: Function;
|
|
90
|
+
_bDisableOnHashChange!: boolean;
|
|
91
|
+
_bIgnoreRestore!: boolean;
|
|
92
|
+
_bCleanedRestore!: boolean;
|
|
93
|
+
_bForceFocus!: boolean;
|
|
94
|
+
_oRouter!: Router;
|
|
95
|
+
_oManagedHistory!: any[];
|
|
96
|
+
_oNavigationGuard: any;
|
|
97
|
+
oResourceBundle?: ResourceBundle;
|
|
98
|
+
_oRouteMatchSynchronization?: Synchronization;
|
|
99
|
+
_bActivateRouteMatchSynchro: boolean = false;
|
|
100
|
+
_bApplyRestore: boolean = false;
|
|
101
|
+
_bDelayedRebuild: boolean = false;
|
|
102
|
+
|
|
103
|
+
init(oAppComponent: any, isfclEnabled: boolean) {
|
|
104
|
+
// Save the name of the app (including startup parameters) for rebuilding full hashes later
|
|
105
|
+
oAppComponent
|
|
106
|
+
.getService("shellServices")
|
|
107
|
+
.then(() => {
|
|
108
|
+
this._oShellServices = oAppComponent.getShellServices();
|
|
109
|
+
|
|
110
|
+
this.initRaw(oAppComponent.getRouter());
|
|
111
|
+
// We want to wait until the initial routeMatched is done before doing any navigation
|
|
112
|
+
this.waitForRouteMatchBeforeNavigation();
|
|
113
|
+
|
|
114
|
+
// Set feLevel=0 for the first Application page in the history
|
|
115
|
+
history.replaceState(
|
|
116
|
+
Object.assign(
|
|
117
|
+
{
|
|
118
|
+
feLevel: 0
|
|
119
|
+
},
|
|
120
|
+
history.state
|
|
121
|
+
),
|
|
122
|
+
"",
|
|
123
|
+
window.location as any
|
|
124
|
+
);
|
|
125
|
+
this.fclEnabled = isfclEnabled;
|
|
126
|
+
|
|
127
|
+
this._fnBlockingNavFilter = this._blockingNavigationFilter.bind(this);
|
|
128
|
+
this._oShellServices.registerNavigationFilter(this._fnBlockingNavFilter);
|
|
129
|
+
})
|
|
130
|
+
.catch(function(oError: any) {
|
|
131
|
+
Log.error("Cannot retrieve the shell services", oError);
|
|
132
|
+
});
|
|
133
|
+
this._fnHashGuard = this.hashGuard.bind(this);
|
|
134
|
+
window.addEventListener("popstate", this._fnHashGuard as any);
|
|
135
|
+
this._bDisableOnHashChange = false;
|
|
136
|
+
this._bIgnoreRestore = false;
|
|
137
|
+
this._bCleanedRestore = false;
|
|
138
|
+
this._bForceFocus = true; // Trigger the focus mechanism for the first view displayed by the app
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
destroy() {
|
|
142
|
+
if (this._oShellServices) {
|
|
143
|
+
this._oShellServices.unregisterNavigationFilter(this._fnBlockingNavFilter);
|
|
144
|
+
}
|
|
145
|
+
window.removeEventListener("popstate", this._fnHashGuard as any);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Raw initialization (for unit tests).
|
|
150
|
+
*
|
|
151
|
+
* @param {sap.ui.core.routing.Router} oRouter The router used by this proxy
|
|
152
|
+
*/
|
|
153
|
+
initRaw(oRouter: Router) {
|
|
154
|
+
this._oRouter = oRouter;
|
|
155
|
+
this._oManagedHistory = [];
|
|
156
|
+
this._oNavigationGuard = null;
|
|
157
|
+
|
|
158
|
+
const sCurrentAppHash = this.getHash();
|
|
159
|
+
this._oManagedHistory.push(this._extractStateFromHash(sCurrentAppHash));
|
|
160
|
+
|
|
161
|
+
// Set the iAppState if the initial hash contains one
|
|
162
|
+
this.sIAppStateKey = findAppStateInHash(sCurrentAppHash);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
getHash() {
|
|
166
|
+
return this._oRouter.getHashChanger().getHash();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
isFocusForced() {
|
|
170
|
+
return this._bForceFocus;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
setFocusForced(bForced: boolean) {
|
|
174
|
+
this._bForceFocus = bForced;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Resets the internal variable sIAppStateKey.
|
|
179
|
+
*
|
|
180
|
+
* @function
|
|
181
|
+
* @name sap.fe.core.RouterProxy#removeIAppStateKey
|
|
182
|
+
*
|
|
183
|
+
* @ui5-restricted
|
|
184
|
+
*/
|
|
185
|
+
removeIAppStateKey() {
|
|
186
|
+
this.sIAppStateKey = null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Navigates to a specific hash.
|
|
191
|
+
*
|
|
192
|
+
* @function
|
|
193
|
+
* @name sap.fe.core.RouterProxy#navToHash
|
|
194
|
+
* @memberof sap.fe.core.RouterProxy
|
|
195
|
+
* @static
|
|
196
|
+
* @param {string} sHash Hash to be navigated to
|
|
197
|
+
* @param {boolean} bPreserveHistory If set to true, non-ancestor entries in history will be retained
|
|
198
|
+
* @param {boolean} bDisablePreservationCache If set to true, cache preservation mechanism is disabled for the current navigation
|
|
199
|
+
* @param {boolean} bForceFocus If set to true, the logic to set the focus once the navigation is finalized will be triggered (onPageReady)
|
|
200
|
+
* @param {boolean} bPreserveShellBackNavigationHandler If not set to false, the back navigation is set to undefined
|
|
201
|
+
* @returns {Promise} Promise (resolved when the navigation is finalized) that returns 'true' if a navigation took place, 'false' if the navigation didn't happen
|
|
202
|
+
* @ui5-restricted
|
|
203
|
+
*/
|
|
204
|
+
navToHash(
|
|
205
|
+
sHash: string | undefined,
|
|
206
|
+
bPreserveHistory?: boolean,
|
|
207
|
+
bDisablePreservationCache?: boolean,
|
|
208
|
+
bForceFocus?: boolean,
|
|
209
|
+
bPreserveShellBackNavigationHandler?: boolean
|
|
210
|
+
) {
|
|
211
|
+
if (bPreserveShellBackNavigationHandler !== false) {
|
|
212
|
+
this._oShellServices.setBackNavigation();
|
|
213
|
+
}
|
|
214
|
+
if (this._oRouteMatchSynchronization) {
|
|
215
|
+
return this._oRouteMatchSynchronization.waitFor().then(() => {
|
|
216
|
+
this._oRouteMatchSynchronization = undefined;
|
|
217
|
+
return this._internalNavToHash(sHash, bPreserveHistory, bDisablePreservationCache, bForceFocus);
|
|
218
|
+
});
|
|
219
|
+
} else {
|
|
220
|
+
if (this._bActivateRouteMatchSynchro) {
|
|
221
|
+
this.waitForRouteMatchBeforeNavigation();
|
|
222
|
+
}
|
|
223
|
+
return this._internalNavToHash(sHash, bPreserveHistory, bDisablePreservationCache, bForceFocus);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
_internalNavToHash(sHash: any, bPreserveHistory: any, bDisablePreservationCache: any, bForceFocus?: boolean) {
|
|
228
|
+
// Add the app state in the hash if needed
|
|
229
|
+
if (this.fclEnabled && this.sIAppStateKey && !findAppStateInHash(sHash)) {
|
|
230
|
+
sHash = setAppStateInHash(sHash, this.sIAppStateKey);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (!this.checkHashWithGuard(sHash)) {
|
|
234
|
+
if (!this.oResourceBundle) {
|
|
235
|
+
this.oResourceBundle = sap.ui.getCore().getLibraryResourceBundle("sap.fe.core") as ResourceBundle;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// We have to use a confirm here for UI consistency reasons, as with some scenarios
|
|
239
|
+
// in the EditFlow we rely on a UI5 mechanism that displays a confirm dialog.
|
|
240
|
+
// eslint-disable-next-line no-alert
|
|
241
|
+
if (!confirm(this.oResourceBundle.getText("C_ROUTER_PROXY_SAPFE_EXIT_NOTSAVED_MESSAGE"))) {
|
|
242
|
+
// The user clicked on Cancel --> cancel navigation
|
|
243
|
+
return Promise.resolve(false);
|
|
244
|
+
}
|
|
245
|
+
this.bIsGuardCrossAllowed = true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// In case the navigation will cause a new view to be displayed, we force the focus
|
|
249
|
+
// I.e. if the keys for the hash we're navigating to is a superset of the current hash keys.
|
|
250
|
+
const oNewState = this._extractStateFromHash(sHash);
|
|
251
|
+
if (!this._bForceFocus) {
|
|
252
|
+
// If the focus was already forced, keep it
|
|
253
|
+
const aCurrentHashKeys = this._extractKeysFromHash(this.getHash());
|
|
254
|
+
this._bForceFocus =
|
|
255
|
+
bForceFocus ||
|
|
256
|
+
(aCurrentHashKeys.length < oNewState.keys.length &&
|
|
257
|
+
aCurrentHashKeys.every(function(key: any, index: any) {
|
|
258
|
+
return key === oNewState.keys[index];
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const oHistoryAction = this._pushNewState(oNewState, false, bPreserveHistory, bDisablePreservationCache);
|
|
263
|
+
|
|
264
|
+
return this._rebuildBrowserHistory(oHistoryAction, false);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Clears browser history if entries have been added without using the RouterProxy.
|
|
269
|
+
* Updates the internal history accordingly.
|
|
270
|
+
*
|
|
271
|
+
* @returns {Promise} Promise that is resolved once the history is rebuilt
|
|
272
|
+
*/
|
|
273
|
+
restoreHistory() {
|
|
274
|
+
if (this._bApplyRestore) {
|
|
275
|
+
this._bApplyRestore = false;
|
|
276
|
+
let sTargetHash = this.getHash();
|
|
277
|
+
sTargetHash = sTargetHash.replace(/(\?|&)restoreHistory=true/, "");
|
|
278
|
+
const oNewState = this._extractStateFromHash(sTargetHash);
|
|
279
|
+
|
|
280
|
+
const oHistoryAction = this._pushNewState(oNewState, true, false, true);
|
|
281
|
+
|
|
282
|
+
return this._rebuildBrowserHistory(oHistoryAction, true);
|
|
283
|
+
} else {
|
|
284
|
+
return Promise.resolve();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Navigates back in the history.
|
|
290
|
+
*
|
|
291
|
+
* @returns {Promise} Promise that is resolved when the navigation is finalized
|
|
292
|
+
*/
|
|
293
|
+
navBack() {
|
|
294
|
+
const sCurrentHash = this.getHash();
|
|
295
|
+
let sPreviousHash;
|
|
296
|
+
|
|
297
|
+
// Look for the current hash in the managed history
|
|
298
|
+
for (let i = this._oManagedHistory.length - 1; i > 0; i--) {
|
|
299
|
+
if (this._oManagedHistory[i].hash === sCurrentHash) {
|
|
300
|
+
sPreviousHash = this._oManagedHistory[i - 1].hash;
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (sPreviousHash) {
|
|
306
|
+
return this.navToHash(sPreviousHash);
|
|
307
|
+
} else {
|
|
308
|
+
// We couldn't find a previous hash in history
|
|
309
|
+
// This can happen when navigating from a transient hash in a create app, and
|
|
310
|
+
// in that case history.back would go back to the FLP
|
|
311
|
+
window.history.back();
|
|
312
|
+
return Promise.resolve();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Navigates to a route with parameters.
|
|
318
|
+
* @param {string} sRouteName The route name to be navigated to
|
|
319
|
+
* @param {object} oParameters Parameters for the navigation
|
|
320
|
+
* @returns {Promise} Promise that is resolved when the navigation is finalized
|
|
321
|
+
* @ui5-restricted
|
|
322
|
+
*/
|
|
323
|
+
navTo(sRouteName: string, oParameters: any) {
|
|
324
|
+
const sHash = this._oRouter.getURL(sRouteName, oParameters);
|
|
325
|
+
return this.navToHash(sHash, false, oParameters.noPreservationCache, false, !oParameters.bIsStickyMode);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Exits the current app by navigating back
|
|
330
|
+
* to the previous app (if any) or the FLP.
|
|
331
|
+
*
|
|
332
|
+
* @returns {Promise} Promise that is resolved when we exit the app
|
|
333
|
+
*/
|
|
334
|
+
exitFromApp() {
|
|
335
|
+
return this._oShellServices.backToPreviousApp();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Checks whether a given hash can have an impact on the current state
|
|
340
|
+
* i.e. if the hash is equal, compatible or an ancestor of the current state.
|
|
341
|
+
*
|
|
342
|
+
* @param {*} sHash `true` if there is an impact
|
|
343
|
+
* @returns {boolean} If there is an impact
|
|
344
|
+
*/
|
|
345
|
+
isCurrentStateImpactedBy(sHash: any) {
|
|
346
|
+
if (sHash[0] === "/") {
|
|
347
|
+
sHash = sHash.substring(1);
|
|
348
|
+
}
|
|
349
|
+
const oLocalGuard = createGuardFromHash(sHash);
|
|
350
|
+
return oLocalGuard.check(this.getHash());
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Checks if a navigation is currently being processed.
|
|
355
|
+
*
|
|
356
|
+
* @returns {boolean} `false` if a navigation has been triggered in the RouterProxy and is not yet finalized
|
|
357
|
+
*/
|
|
358
|
+
isNavigationFinalized() {
|
|
359
|
+
return !this.bIsRebuildHistoryRunning && !this._bDelayedRebuild;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Sets the last state as a guard.
|
|
364
|
+
* Each future navigation will be checked against this guard, and a confirmation dialog will
|
|
365
|
+
* be displayed before the navigation crosses the guard (i.e. goes to an ancestor of the guard).
|
|
366
|
+
*/
|
|
367
|
+
setNavigationGuard() {
|
|
368
|
+
this._oNavigationGuard = createGuardFromHash(this.getHash());
|
|
369
|
+
this.bIsGuardCrossAllowed = false;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Disables the navigation guard.
|
|
374
|
+
*/
|
|
375
|
+
discardNavigationGuard() {
|
|
376
|
+
this._oNavigationGuard = null;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Checks for the availability of the navigation guard.
|
|
381
|
+
*
|
|
382
|
+
* @returns {boolean} `true` if navigating guard is available
|
|
383
|
+
*/
|
|
384
|
+
hasNavigationGuard() {
|
|
385
|
+
return this._oNavigationGuard !== null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Tests a hash against the navigation guard.
|
|
390
|
+
*
|
|
391
|
+
* @param {string} sHash The hash to be tested
|
|
392
|
+
* @returns {boolean} `true` if navigating to the hash doesn't cross the guard
|
|
393
|
+
*/
|
|
394
|
+
checkHashWithGuard(sHash: string) {
|
|
395
|
+
return this._oNavigationGuard === null || this._oNavigationGuard.check(sHash);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Checks if the user allowed the navigation guard to be crossed.
|
|
400
|
+
*
|
|
401
|
+
* @returns {boolean} `true` if crossing the guard has been allowed by the user
|
|
402
|
+
*/
|
|
403
|
+
isGuardCrossAllowedByUser() {
|
|
404
|
+
return this.bIsGuardCrossAllowed;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Activates the synchronization for routeMatchedEvent.
|
|
409
|
+
* The next NavToHash call will create a Synchronization object that will be resolved
|
|
410
|
+
* by the corresponding onRouteMatched event, preventing another NavToHash to happen in parallel.
|
|
411
|
+
*/
|
|
412
|
+
activateRouteMatchSynchronization() {
|
|
413
|
+
this._bActivateRouteMatchSynchro = true;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Resolve the routeMatch synchronization object, unlocking potential pending NavToHash calls.
|
|
418
|
+
*/
|
|
419
|
+
resolveRouteMatch() {
|
|
420
|
+
if (this._oRouteMatchSynchronization) {
|
|
421
|
+
this._oRouteMatchSynchronization.resolve();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Makes sure no navigation can happen before a routeMatch happened.
|
|
427
|
+
*/
|
|
428
|
+
waitForRouteMatchBeforeNavigation() {
|
|
429
|
+
this._oRouteMatchSynchronization = new Synchronization();
|
|
430
|
+
this._bActivateRouteMatchSynchro = false;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
_extractKeysFromHash(sHash: any) {
|
|
434
|
+
if (sHash === undefined) {
|
|
435
|
+
sHash = "";
|
|
436
|
+
}
|
|
437
|
+
const sHashNoParams = sHash.split("?")[0]; // remove params
|
|
438
|
+
const aTokens = sHashNoParams.split("/");
|
|
439
|
+
const aKeys: any[] = [];
|
|
440
|
+
|
|
441
|
+
aTokens.forEach(function(sToken: any) {
|
|
442
|
+
const regexKey = /[^\(\)]+\([^\(\)]+\)/; // abc(def)
|
|
443
|
+
if (regexKey.test(sToken)) {
|
|
444
|
+
aKeys.push(sToken.split("(")[0]);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
return aKeys;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Builds a state from a hash.
|
|
453
|
+
*
|
|
454
|
+
* @param {string} sHash The hash to be used as entry
|
|
455
|
+
* @returns {object} The state
|
|
456
|
+
*
|
|
457
|
+
* @ui5-restricted
|
|
458
|
+
*/
|
|
459
|
+
_extractStateFromHash(sHash: string) {
|
|
460
|
+
if (sHash === undefined) {
|
|
461
|
+
sHash = "";
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const oState: any = {
|
|
465
|
+
keys: this._extractKeysFromHash(sHash)
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// Retrieve layout (if any)
|
|
469
|
+
const aLayout = sHash.match(new RegExp("\\?.*" + enumURLParams.LAYOUTPARAM + "=([^&]*)"));
|
|
470
|
+
oState.sLayout = aLayout && aLayout.length > 1 ? aLayout[1] : null;
|
|
471
|
+
if (oState.sLayout === "MidColumnFullScreen") {
|
|
472
|
+
oState.screenMode = 1;
|
|
473
|
+
} else if (oState.sLayout === "EndColumnFullScreen") {
|
|
474
|
+
oState.screenMode = 2;
|
|
475
|
+
} else {
|
|
476
|
+
oState.screenMode = 0;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
oState.hash = sHash;
|
|
480
|
+
return oState;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Adds a new state into the internal history structure.
|
|
485
|
+
* Makes sure this new state is added after an ancestor.
|
|
486
|
+
* Also sets the iAppState key in the whole history.
|
|
487
|
+
*
|
|
488
|
+
* @memberof sap.fe.core.RouterProxy
|
|
489
|
+
* @param {object} oNewState The new state to be added
|
|
490
|
+
* @param {boolean} bRebuildOnly `true` if we're rebuilding the history after a shell menu navigation
|
|
491
|
+
* @param {boolean} bPreserveHistory If set to true, non-ancestor entries in history will be retained
|
|
492
|
+
* @param {boolean} bDisableHistoryPreservation Disable the mechanism to retained marked entries in cache
|
|
493
|
+
* @returns {object} The new state
|
|
494
|
+
* @ui5-restricted
|
|
495
|
+
* @final
|
|
496
|
+
*/
|
|
497
|
+
_pushNewState(oNewState: any, bRebuildOnly: boolean, bPreserveHistory: boolean, bDisableHistoryPreservation: boolean) {
|
|
498
|
+
const sCurrentHash = this.getHash();
|
|
499
|
+
let lastIndex = this._oManagedHistory.length - 1;
|
|
500
|
+
let iPopCount = bRebuildOnly ? 1 : 0;
|
|
501
|
+
|
|
502
|
+
// 1. Do some cleanup in the managed history : in case the user has navigated back in the browser history, we need to remove
|
|
503
|
+
// the states ahead in history and make sure the top state corresponds to the current page
|
|
504
|
+
// We don't do that when restoring the history, as the current state has been added on top of the browser history
|
|
505
|
+
// and is not reflected in the managed history
|
|
506
|
+
if (!bRebuildOnly) {
|
|
507
|
+
while (lastIndex >= 0 && this._oManagedHistory[lastIndex].hash !== sCurrentHash) {
|
|
508
|
+
this._oManagedHistory.pop();
|
|
509
|
+
lastIndex--;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (this._oManagedHistory.length === 0) {
|
|
513
|
+
// We couldn't find the current location in the history. This can happen if a browser reload
|
|
514
|
+
// happened, causing a reinitialization of the managed history.
|
|
515
|
+
// In that case, we use the current location as the new starting point in the managed history
|
|
516
|
+
this._oManagedHistory.push(this._extractStateFromHash(sCurrentHash));
|
|
517
|
+
history.replaceState(Object.assign({ feLevel: 0 }, history.state), "");
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// 2. Mark the top state as preserved if required
|
|
522
|
+
if (bPreserveHistory && !bDisableHistoryPreservation) {
|
|
523
|
+
this._oManagedHistory[this._oManagedHistory.length - 1].preserved = true;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// 3. Then pop all states until we find an ancestor of the new state, or we find a state that need to be preserved
|
|
527
|
+
let oLastRemovedItem;
|
|
528
|
+
while (this._oManagedHistory.length > 0) {
|
|
529
|
+
const oTopState = this._oManagedHistory[this._oManagedHistory.length - 1];
|
|
530
|
+
if (
|
|
531
|
+
(bDisableHistoryPreservation || !oTopState.preserved) &&
|
|
532
|
+
this._compareCacheStates(oTopState, oNewState) !== enumState.ANCESTOR
|
|
533
|
+
) {
|
|
534
|
+
// The top state is not an ancestor of oNewState and is not preserved --> we can pop it
|
|
535
|
+
oLastRemovedItem = this._oManagedHistory.pop();
|
|
536
|
+
iPopCount++;
|
|
537
|
+
} else if (oTopState.preserved && removeAppStateInHash(oTopState.hash) === removeAppStateInHash(oNewState.hash)) {
|
|
538
|
+
// We try to add a state that is already in cache (due to preserved flag) but with a different iapp-state
|
|
539
|
+
// --> we should delete the previous entry (it will be later replaced by the new one) and stop popping
|
|
540
|
+
oLastRemovedItem = this._oManagedHistory.pop();
|
|
541
|
+
iPopCount++;
|
|
542
|
+
oNewState.preserved = true;
|
|
543
|
+
break;
|
|
544
|
+
} else {
|
|
545
|
+
break; // Ancestor or preserved state found --> we stop popping out states
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// 4. iAppState management
|
|
550
|
+
this.sIAppStateKey = findAppStateInHash(oNewState.hash);
|
|
551
|
+
if (this.fclEnabled && this.sIAppStateKey) {
|
|
552
|
+
// In case of FCL, the new app state needs to be applied to the whole history
|
|
553
|
+
this._oManagedHistory.forEach((oManagedState: any) => {
|
|
554
|
+
oManagedState.hash = setAppStateInHash(oManagedState.hash, this.sIAppStateKey);
|
|
555
|
+
});
|
|
556
|
+
} else if (!this.fclEnabled && oLastRemovedItem) {
|
|
557
|
+
const sPreviousIAppStateKey = findAppStateInHash(oLastRemovedItem.hash);
|
|
558
|
+
const oComparisonStateResult = this._compareCacheStates(oLastRemovedItem, oNewState);
|
|
559
|
+
// if current state doesn't contain a i-appstate and this state should replace a state containing a iAppState
|
|
560
|
+
// then the previous iAppState is preserved
|
|
561
|
+
if (
|
|
562
|
+
!this.sIAppStateKey &&
|
|
563
|
+
sPreviousIAppStateKey &&
|
|
564
|
+
(oComparisonStateResult === enumState.EQUAL || oComparisonStateResult === enumState.COMPATIBLE)
|
|
565
|
+
) {
|
|
566
|
+
oNewState.hash = setAppStateInHash(oNewState.hash, sPreviousIAppStateKey);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// 5. Now we can push the state at the top of the internal history
|
|
571
|
+
const bHasSameHash = oLastRemovedItem && oNewState.hash === oLastRemovedItem.hash;
|
|
572
|
+
if (this._oManagedHistory.length === 0 || this._oManagedHistory[this._oManagedHistory.length - 1].hash !== oNewState.hash) {
|
|
573
|
+
this._oManagedHistory.push(oNewState);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// 6. Determine which actions to do on the history
|
|
577
|
+
if (iPopCount === 0) {
|
|
578
|
+
// No state was popped --> append
|
|
579
|
+
return { type: "append" };
|
|
580
|
+
} else if (iPopCount === 1) {
|
|
581
|
+
// Only 1 state was popped --> replace current hash unless hash is the same (then nothing to do)
|
|
582
|
+
return bHasSameHash ? { type: "none" } : { type: "replace" };
|
|
583
|
+
} else {
|
|
584
|
+
// More than 1 state was popped --> go bakc in history and replace hash if necessary
|
|
585
|
+
return bHasSameHash ? { type: "back", steps: iPopCount - 1 } : { type: "back-replace", steps: iPopCount - 1 };
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
_blockingNavigationFilter() {
|
|
590
|
+
return this._bDisableOnHashChange ? "Custom" : "Continue";
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Disable the routing by calling the router stop method.
|
|
595
|
+
*
|
|
596
|
+
* @function
|
|
597
|
+
* @memberof sap.fe.core.RouterProxy
|
|
598
|
+
*
|
|
599
|
+
* @ui5-restricted
|
|
600
|
+
* @final
|
|
601
|
+
*/
|
|
602
|
+
_disableEventOnHashChange() {
|
|
603
|
+
this._bDisableOnHashChange = true;
|
|
604
|
+
this._oRouter.stop();
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Enable the routing by calling the router initialize method.
|
|
609
|
+
*
|
|
610
|
+
* @function
|
|
611
|
+
* @name sap.fe.core.RouterProxy#_enableEventOnHashChange
|
|
612
|
+
* @memberof sap.fe.core.RouterProxy
|
|
613
|
+
* @param {boolean} [bIgnoreCurrentHash] Ignore the last hash event triggered before the router has initialized
|
|
614
|
+
*
|
|
615
|
+
* @ui5-restricted
|
|
616
|
+
* @final
|
|
617
|
+
*/
|
|
618
|
+
_enableEventOnHashChange(bIgnoreCurrentHash: boolean | undefined) {
|
|
619
|
+
this._bDisableOnHashChange = false;
|
|
620
|
+
this._oRouter.initialize(bIgnoreCurrentHash);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Synchronizes the browser history with the internal history of the routerProxy, and triggers a navigation if needed.
|
|
625
|
+
*
|
|
626
|
+
* @memberof sap.fe.core.RouterProxy
|
|
627
|
+
* @param {object} oHistoryAction Specifies the navigation action to be performed
|
|
628
|
+
* @param {boolean} bRebuildOnly `true` if internal history is currently being rebuilt
|
|
629
|
+
* @returns {Promise} Promise (resolved when the navigation is finalized) that returns 'true' if a navigation took place, 'false' if the navigation didn't happen
|
|
630
|
+
* @ui5-restricted
|
|
631
|
+
* @final
|
|
632
|
+
*/
|
|
633
|
+
_rebuildBrowserHistory(oHistoryAction: any, bRebuildOnly: boolean) {
|
|
634
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
635
|
+
const that = this;
|
|
636
|
+
return new Promise(resolve => {
|
|
637
|
+
this.bIsRebuildHistoryRunning = true;
|
|
638
|
+
const oTargetState = this._oManagedHistory[this._oManagedHistory.length - 1],
|
|
639
|
+
newLevel = this._oManagedHistory.length - 1;
|
|
640
|
+
|
|
641
|
+
function replaceAsync() {
|
|
642
|
+
if (!bRebuildOnly) {
|
|
643
|
+
that._enableEventOnHashChange(true);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
(that._oRouter.getHashChanger().replaceHash as any)(oTargetState.hash);
|
|
647
|
+
history.replaceState(Object.assign({ feLevel: newLevel }, history.state), "");
|
|
648
|
+
|
|
649
|
+
if (bRebuildOnly) {
|
|
650
|
+
setTimeout(function() {
|
|
651
|
+
// Timeout to let 'hashchange' event be processed before by the HashChanger, so that
|
|
652
|
+
// onRouteMatched notification isn't raised
|
|
653
|
+
that._enableEventOnHashChange(true);
|
|
654
|
+
}, 0);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
that.bIsRebuildHistoryRunning = false;
|
|
658
|
+
resolve(true); // a navigation occurred
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Async callbacks when navigating back, in order to let all notifications and events get processed
|
|
662
|
+
function backReplaceAsync() {
|
|
663
|
+
window.removeEventListener("popstate", backReplaceAsync);
|
|
664
|
+
setTimeout(function() {
|
|
665
|
+
// Timeout to let 'hashchange' event be processed before by the HashChanger
|
|
666
|
+
replaceAsync();
|
|
667
|
+
}, 0);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function backAsync() {
|
|
671
|
+
window.removeEventListener("popstate", backAsync);
|
|
672
|
+
that.bIsRebuildHistoryRunning = false;
|
|
673
|
+
resolve(true); // a navigation occurred
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
that._bIgnoreRestore = true;
|
|
677
|
+
|
|
678
|
+
switch (oHistoryAction.type) {
|
|
679
|
+
case "replace":
|
|
680
|
+
(that._oRouter.getHashChanger().replaceHash as any)(oTargetState.hash);
|
|
681
|
+
history.replaceState(Object.assign({ feLevel: newLevel }, history.state), "");
|
|
682
|
+
that.bIsRebuildHistoryRunning = false;
|
|
683
|
+
resolve(true); // a navigation occurred
|
|
684
|
+
break;
|
|
685
|
+
|
|
686
|
+
case "append":
|
|
687
|
+
that._oRouter.getHashChanger().setHash(oTargetState.hash);
|
|
688
|
+
history.replaceState(Object.assign({ feLevel: newLevel }, history.state), "");
|
|
689
|
+
that.bIsRebuildHistoryRunning = false;
|
|
690
|
+
resolve(true); // a navigation occurred
|
|
691
|
+
break;
|
|
692
|
+
|
|
693
|
+
case "back":
|
|
694
|
+
window.addEventListener("popstate", backAsync);
|
|
695
|
+
history.go(-oHistoryAction.steps);
|
|
696
|
+
break;
|
|
697
|
+
|
|
698
|
+
case "back-replace":
|
|
699
|
+
this._disableEventOnHashChange();
|
|
700
|
+
window.addEventListener("popstate", backReplaceAsync);
|
|
701
|
+
history.go(-oHistoryAction.steps);
|
|
702
|
+
break;
|
|
703
|
+
|
|
704
|
+
default:
|
|
705
|
+
// No navigation
|
|
706
|
+
this.bIsRebuildHistoryRunning = false;
|
|
707
|
+
resolve(false); // no navigation --> resolve to false
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
getLastHistoryEntry() {
|
|
713
|
+
return this._oManagedHistory[this._oManagedHistory.length - 1];
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
hashGuard() {
|
|
717
|
+
if (this._bCleanedRestore) {
|
|
718
|
+
this._bCleanedRestore = false;
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
const sHash = window.location.hash;
|
|
722
|
+
if (sHash.indexOf("restoreHistory=true") !== -1) {
|
|
723
|
+
this._bApplyRestore = true;
|
|
724
|
+
} else if (!this.bIsRebuildHistoryRunning) {
|
|
725
|
+
const aHashSplit = sHash.split("&/");
|
|
726
|
+
const sAppHash = aHashSplit[1] ? aHashSplit[1] : "";
|
|
727
|
+
if (this.checkHashWithGuard(sAppHash)) {
|
|
728
|
+
this._bDelayedRebuild = true;
|
|
729
|
+
const oNewState = this._extractStateFromHash(sAppHash);
|
|
730
|
+
this._pushNewState(oNewState, false, false, true);
|
|
731
|
+
|
|
732
|
+
setTimeout(() => {
|
|
733
|
+
this._bDelayedRebuild = false;
|
|
734
|
+
}, 0);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Compares 2 states.
|
|
741
|
+
*
|
|
742
|
+
* @param {object} oState1
|
|
743
|
+
* @param {object} oState2
|
|
744
|
+
* @returns {number} The result of the comparison:
|
|
745
|
+
* - enumState.EQUAL if oState1 and oState2 are equal
|
|
746
|
+
* - enumState.COMPATIBLE if oState1 and oState2 are compatible
|
|
747
|
+
* - enumState.ANCESTOR if oState1 is an ancestor of oState2
|
|
748
|
+
* - enumState.DIFFERENT if the 2 states are different
|
|
749
|
+
*/
|
|
750
|
+
|
|
751
|
+
_compareCacheStates(oState1: any, oState2: any) {
|
|
752
|
+
// First compare object keys
|
|
753
|
+
if (oState1.keys.length > oState2.keys.length) {
|
|
754
|
+
return enumState.DIFFERENT;
|
|
755
|
+
}
|
|
756
|
+
let equal = true;
|
|
757
|
+
let index;
|
|
758
|
+
for (index = 0; equal && index < oState1.keys.length; index++) {
|
|
759
|
+
if (oState1.keys[index] !== oState2.keys[index]) {
|
|
760
|
+
equal = false;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
if (!equal) {
|
|
764
|
+
// Some objects keys are different
|
|
765
|
+
return enumState.DIFFERENT;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// All keys from oState1 are in oState2 --> check if ancestor
|
|
769
|
+
if (oState1.keys.length < oState2.keys.length || oState1.screenMode < oState2.screenMode) {
|
|
770
|
+
return enumState.ANCESTOR;
|
|
771
|
+
}
|
|
772
|
+
if (oState1.screenMode > oState2.screenMode) {
|
|
773
|
+
return enumState.DIFFERENT; // Not sure this case can happen...
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// At this stage, the 2 states have the same object keys (in the same order) and same screenmode
|
|
777
|
+
// They can be either compatible or equal
|
|
778
|
+
return oState1.sLayout === oState2.sLayout ? enumState.EQUAL : enumState.COMPATIBLE;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Checks if back exits the present guard set.
|
|
783
|
+
*
|
|
784
|
+
* @param {string} sPresentHash The current hash. Only used for unit tests.
|
|
785
|
+
* @returns {boolean} `true` if back exits there is a guard exit on back
|
|
786
|
+
*/
|
|
787
|
+
checkIfBackIsOutOfGuard(sPresentHash: string) {
|
|
788
|
+
let sPrevHash;
|
|
789
|
+
|
|
790
|
+
if (sPresentHash === undefined) {
|
|
791
|
+
// We use window.location.hash instead of HashChanger.getInstance().getHash() because the latter
|
|
792
|
+
// replaces characters in the URL (e.g. %24 replaced by $) and it causes issues when comparing
|
|
793
|
+
// with the URLs in the managed history
|
|
794
|
+
const oSplitHash = this._oShellServices.splitHash(window.location.hash) as any;
|
|
795
|
+
if (oSplitHash && oSplitHash.appSpecificRoute) {
|
|
796
|
+
sPresentHash = oSplitHash.appSpecificRoute;
|
|
797
|
+
if (sPresentHash.indexOf("&/") === 0) {
|
|
798
|
+
sPresentHash = sPresentHash.substring(2);
|
|
799
|
+
}
|
|
800
|
+
} else {
|
|
801
|
+
sPresentHash = window.location.hash.substring(1); // To remove the '#'
|
|
802
|
+
if (sPresentHash[0] === "/") {
|
|
803
|
+
sPresentHash = sPresentHash.substring(1);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
sPresentHash = URI.decode(sPresentHash);
|
|
808
|
+
if (this._oNavigationGuard) {
|
|
809
|
+
for (let i = this._oManagedHistory.length - 1; i > 0; i--) {
|
|
810
|
+
if (this._oManagedHistory[i].hash === sPresentHash) {
|
|
811
|
+
sPrevHash = this._oManagedHistory[i - 1].hash;
|
|
812
|
+
break;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
return !sPrevHash || !this.checkHashWithGuard(sPrevHash);
|
|
817
|
+
}
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Checks if the last 2 entries in the history share the same context.
|
|
823
|
+
*
|
|
824
|
+
* @returns {boolean} `true` if they share the same context.
|
|
825
|
+
*/
|
|
826
|
+
checkIfBackHasSameContext() {
|
|
827
|
+
if (this._oManagedHistory.length < 2) {
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
const oCurrentState = this._oManagedHistory[this._oManagedHistory.length - 1];
|
|
832
|
+
const oPreviousState = this._oManagedHistory[this._oManagedHistory.length - 2];
|
|
833
|
+
|
|
834
|
+
return oCurrentState.hash.split("?")[0] === oPreviousState.hash.split("?")[0];
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
export default RouterProxy;
|