@sapui5/sap.fe.core 1.97.0 → 1.99.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.
Files changed (249) hide show
  1. package/package.json +5 -4
  2. package/src/sap/fe/core/.library +1 -1
  3. package/src/sap/fe/core/AnnotationHelper.js +377 -405
  4. package/src/sap/fe/core/AnnotationHelper.ts +385 -0
  5. package/src/sap/fe/core/AppComponent.js +6 -3
  6. package/src/sap/fe/core/AppStateHandler.js +229 -181
  7. package/src/sap/fe/core/AppStateHandler.ts +171 -0
  8. package/src/sap/fe/core/BaseController.js +3 -2
  9. package/src/sap/fe/core/BusyLocker.js +105 -121
  10. package/src/sap/fe/core/BusyLocker.ts +98 -0
  11. package/src/sap/fe/core/CommonUtils.js +2073 -2379
  12. package/src/sap/fe/core/CommonUtils.ts +2078 -0
  13. package/src/sap/fe/core/ExtensionAPI.js +16 -7
  14. package/src/sap/fe/core/PageController.js +84 -119
  15. package/src/sap/fe/core/PageController.ts +101 -0
  16. package/src/sap/fe/core/RouterProxy.js +986 -807
  17. package/src/sap/fe/core/RouterProxy.ts +838 -0
  18. package/src/sap/fe/core/Synchronization.js +51 -35
  19. package/src/sap/fe/core/Synchronization.ts +29 -0
  20. package/src/sap/fe/core/TemplateComponent.js +173 -155
  21. package/src/sap/fe/core/TemplateComponent.ts +166 -0
  22. package/src/sap/fe/core/TemplateModel.js +97 -54
  23. package/src/sap/fe/core/TemplateModel.ts +64 -0
  24. package/src/sap/fe/core/TransactionHelper.js +1576 -1579
  25. package/src/sap/fe/core/TransactionHelper.ts +1706 -0
  26. package/src/sap/fe/core/actions/draft.js +560 -581
  27. package/src/sap/fe/core/actions/draft.ts +594 -0
  28. package/src/sap/fe/core/actions/messageHandling.js +545 -511
  29. package/src/sap/fe/core/actions/messageHandling.ts +532 -0
  30. package/src/sap/fe/core/actions/nonDraft.js +17 -19
  31. package/src/sap/fe/core/actions/nonDraft.ts +12 -0
  32. package/src/sap/fe/core/actions/operations.js +1074 -1211
  33. package/src/sap/fe/core/actions/operations.ts +1162 -0
  34. package/src/sap/fe/core/actions/sticky.js +102 -105
  35. package/src/sap/fe/core/actions/sticky.ts +102 -0
  36. package/src/sap/fe/core/controllerextensions/ControllerExtensionMetadata.js +3 -2
  37. package/src/sap/fe/core/controllerextensions/EditFlow.js +229 -260
  38. package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.js +3 -2
  39. package/src/sap/fe/core/controllerextensions/InternalEditFlow.js +288 -13
  40. package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.js +27 -23
  41. package/src/sap/fe/core/controllerextensions/InternalRouting.js +82 -46
  42. package/src/sap/fe/core/controllerextensions/KPIManagement.js +113 -25
  43. package/src/sap/fe/core/controllerextensions/KPIManagement.ts +135 -44
  44. package/src/sap/fe/core/controllerextensions/MassEdit.js +172 -0
  45. package/src/sap/fe/core/controllerextensions/MessageHandler.js +22 -9
  46. package/src/sap/fe/core/controllerextensions/PageReady.js +3 -3
  47. package/src/sap/fe/core/controllerextensions/PageReady.ts +12 -8
  48. package/src/sap/fe/core/controllerextensions/Paginator.js +37 -9
  49. package/src/sap/fe/core/controllerextensions/Placeholder.js +8 -4
  50. package/src/sap/fe/core/controllerextensions/Routing.js +25 -5
  51. package/src/sap/fe/core/controllerextensions/RoutingListener.js +3 -2
  52. package/src/sap/fe/core/controllerextensions/Share.js +22 -8
  53. package/src/sap/fe/core/controllerextensions/SideEffects.js +9 -9
  54. package/src/sap/fe/core/controllerextensions/SideEffects.ts +22 -21
  55. package/src/sap/fe/core/controllerextensions/ViewState.js +3 -2
  56. package/src/sap/fe/core/controls/ActionParameterDialog.fragment.xml +2 -1
  57. package/src/sap/fe/core/controls/CommandExecution.js +3 -2
  58. package/src/sap/fe/core/controls/ConditionalWrapper.js +3 -2
  59. package/src/sap/fe/core/controls/CustomQuickViewPage.js +3 -2
  60. package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.js +9 -5
  61. package/src/sap/fe/core/controls/FieldWrapper.js +18 -23
  62. package/src/sap/fe/core/controls/FilterBar.js +3 -2
  63. package/src/sap/fe/core/controls/FormElementWrapper.js +3 -7
  64. package/src/sap/fe/core/controls/MultiValueParameterDelegate.js +3 -2
  65. package/src/sap/fe/core/controls/NonComputedVisibleKeyFieldsDialog.fragment.xml +2 -1
  66. package/src/sap/fe/core/controls/filterbar/FilterContainer.js +3 -2
  67. package/src/sap/fe/core/controls/filterbar/VisualFilter.js +5 -4
  68. package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.js +3 -2
  69. package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.js +3 -2
  70. package/src/sap/fe/core/controls/massEdit/MassEditDialog.fragment.xml +106 -0
  71. package/src/sap/fe/core/controls/massEdit/MassEditHandlers.js +79 -0
  72. package/src/sap/fe/core/converters/ConverterContext.js +14 -2
  73. package/src/sap/fe/core/converters/ConverterContext.ts +14 -3
  74. package/src/sap/fe/core/converters/ManifestSettings.js +1 -1
  75. package/src/sap/fe/core/converters/ManifestSettings.ts +4 -0
  76. package/src/sap/fe/core/converters/ManifestWrapper.js +56 -32
  77. package/src/sap/fe/core/converters/ManifestWrapper.ts +24 -6
  78. package/src/sap/fe/core/converters/MetaModelConverter.js +67 -6
  79. package/src/sap/fe/core/converters/MetaModelConverter.ts +70 -14
  80. package/src/sap/fe/core/converters/TemplateConverter.js +1 -1
  81. package/src/sap/fe/core/converters/TemplateConverter.ts +2 -2
  82. package/src/sap/fe/core/converters/annotations/DataField.js +3 -3
  83. package/src/sap/fe/core/converters/annotations/DataField.ts +1 -1
  84. package/src/sap/fe/core/converters/common/AnnotationConverter.js +33 -21
  85. package/src/sap/fe/core/converters/controls/Common/Action.js +11 -2
  86. package/src/sap/fe/core/converters/controls/Common/Action.ts +8 -2
  87. package/src/sap/fe/core/converters/controls/Common/Chart.js +5 -3
  88. package/src/sap/fe/core/converters/controls/Common/Chart.ts +11 -3
  89. package/src/sap/fe/core/converters/controls/Common/DataVisualization.js +3 -3
  90. package/src/sap/fe/core/converters/controls/Common/DataVisualization.ts +2 -2
  91. package/src/sap/fe/core/converters/controls/Common/Form.js +20 -10
  92. package/src/sap/fe/core/converters/controls/Common/Form.ts +24 -4
  93. package/src/sap/fe/core/converters/controls/Common/KPI.js +69 -4
  94. package/src/sap/fe/core/converters/controls/Common/KPI.ts +72 -0
  95. package/src/sap/fe/core/converters/controls/Common/Table.js +390 -398
  96. package/src/sap/fe/core/converters/controls/Common/Table.ts +497 -484
  97. package/src/sap/fe/core/converters/controls/Common/table/StandardActions.js +527 -0
  98. package/src/sap/fe/core/converters/controls/Common/table/StandardActions.ts +655 -0
  99. package/src/sap/fe/core/converters/controls/ListReport/FilterBar.js +489 -37
  100. package/src/sap/fe/core/converters/controls/ListReport/FilterBar.ts +506 -58
  101. package/src/sap/fe/core/converters/controls/ListReport/VisualFilters.js +2 -2
  102. package/src/sap/fe/core/converters/controls/ListReport/VisualFilters.ts +1 -1
  103. package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.js +17 -9
  104. package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.ts +24 -9
  105. package/src/sap/fe/core/converters/helpers/Aggregation.js +18 -3
  106. package/src/sap/fe/core/converters/helpers/Aggregation.ts +28 -5
  107. package/src/sap/fe/core/converters/helpers/IssueManager.js +7 -1
  108. package/src/sap/fe/core/converters/helpers/IssueManager.ts +6 -0
  109. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.js +4 -4
  110. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.ts +3 -3
  111. package/src/sap/fe/core/converters/templates/ListReportConverter.js +13 -6
  112. package/src/sap/fe/core/converters/templates/ListReportConverter.ts +26 -15
  113. package/src/sap/fe/core/designtime/AppComponent.designtime.js +11 -2
  114. package/src/sap/fe/core/formatters/CriticalityFormatter.js +1 -1
  115. package/src/sap/fe/core/formatters/CriticalityFormatter.ts +1 -1
  116. package/src/sap/fe/core/formatters/FPMFormatter.js +1 -1
  117. package/src/sap/fe/core/formatters/FPMFormatter.ts +4 -10
  118. package/src/sap/fe/core/formatters/KPIFormatter.js +1 -1
  119. package/src/sap/fe/core/formatters/KPIFormatter.ts +3 -1
  120. package/src/sap/fe/core/formatters/TableFormatter.js +39 -28
  121. package/src/sap/fe/core/formatters/TableFormatter.ts +43 -28
  122. package/src/sap/fe/core/formatters/ValueFormatter.js +30 -5
  123. package/src/sap/fe/core/formatters/ValueFormatter.ts +30 -7
  124. package/src/sap/fe/core/fpm/Component.js +3 -2
  125. package/src/sap/fe/core/helpers/AppStartupHelper.js +359 -0
  126. package/src/sap/fe/core/helpers/AppStartupHelper.ts +391 -0
  127. package/src/sap/fe/core/helpers/BindingExpression.js +484 -437
  128. package/src/sap/fe/core/helpers/BindingExpression.ts +480 -463
  129. package/src/sap/fe/core/helpers/ClassSupport.js +27 -15
  130. package/src/sap/fe/core/helpers/ClassSupport.ts +31 -20
  131. package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.js +63 -59
  132. package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.ts +56 -0
  133. package/src/sap/fe/core/helpers/EditState.js +81 -84
  134. package/src/sap/fe/core/helpers/EditState.ts +81 -0
  135. package/src/sap/fe/core/helpers/ExcelFormatHelper.js +62 -48
  136. package/src/sap/fe/core/helpers/ExcelFormatHelper.ts +49 -0
  137. package/src/sap/fe/core/helpers/FPMHelper.js +52 -56
  138. package/src/sap/fe/core/helpers/FPMHelper.ts +62 -0
  139. package/src/sap/fe/core/helpers/KeepAliveHelper.js +4 -5
  140. package/src/sap/fe/core/helpers/KeepAliveHelper.ts +10 -10
  141. package/src/sap/fe/core/helpers/MassEditHelper.js +687 -0
  142. package/src/sap/fe/core/helpers/ModelHelper.js +229 -225
  143. package/src/sap/fe/core/helpers/ModelHelper.ts +227 -0
  144. package/src/sap/fe/core/helpers/PasteHelper.js +210 -132
  145. package/src/sap/fe/core/helpers/PasteHelper.ts +196 -0
  146. package/src/sap/fe/core/helpers/SemanticDateOperators.js +332 -313
  147. package/src/sap/fe/core/helpers/SemanticDateOperators.ts +330 -0
  148. package/src/sap/fe/core/helpers/SemanticKeyHelper.js +66 -67
  149. package/src/sap/fe/core/helpers/SemanticKeyHelper.ts +73 -0
  150. package/src/sap/fe/core/helpers/StableIdHelper.js +4 -7
  151. package/src/sap/fe/core/helpers/StableIdHelper.ts +2 -6
  152. package/src/sap/fe/core/jsx-runtime/jsx.js +1 -1
  153. package/src/sap/fe/core/jsx-runtime/jsx.ts +1 -1
  154. package/src/sap/fe/core/library.js +30 -3
  155. package/src/sap/fe/core/library.support.js +12 -5
  156. package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.js +62 -0
  157. package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.ts +66 -0
  158. package/src/sap/fe/core/messagebundle.properties +61 -13
  159. package/src/sap/fe/core/messagebundle_ar.properties +33 -2
  160. package/src/sap/fe/core/messagebundle_bg.properties +33 -2
  161. package/src/sap/fe/core/messagebundle_ca.properties +33 -2
  162. package/src/sap/fe/core/messagebundle_cs.properties +34 -3
  163. package/src/sap/fe/core/messagebundle_cy.properties +33 -2
  164. package/src/sap/fe/core/messagebundle_da.properties +33 -2
  165. package/src/sap/fe/core/messagebundle_de.properties +33 -2
  166. package/src/sap/fe/core/messagebundle_el.properties +33 -2
  167. package/src/sap/fe/core/messagebundle_en.properties +32 -1
  168. package/src/sap/fe/core/messagebundle_en_GB.properties +32 -1
  169. package/src/sap/fe/core/messagebundle_en_US_sappsd.properties +36 -1
  170. package/src/sap/fe/core/messagebundle_en_US_saprigi.properties +33 -2
  171. package/src/sap/fe/core/messagebundle_en_US_saptrc.properties +33 -2
  172. package/src/sap/fe/core/messagebundle_es.properties +33 -2
  173. package/src/sap/fe/core/messagebundle_es_MX.properties +33 -2
  174. package/src/sap/fe/core/messagebundle_et.properties +33 -2
  175. package/src/sap/fe/core/messagebundle_fi.properties +34 -3
  176. package/src/sap/fe/core/messagebundle_fr.properties +38 -7
  177. package/src/sap/fe/core/messagebundle_fr_CA.properties +33 -2
  178. package/src/sap/fe/core/messagebundle_hi.properties +33 -2
  179. package/src/sap/fe/core/messagebundle_hr.properties +33 -2
  180. package/src/sap/fe/core/messagebundle_hu.properties +34 -3
  181. package/src/sap/fe/core/messagebundle_id.properties +36 -5
  182. package/src/sap/fe/core/messagebundle_it.properties +33 -2
  183. package/src/sap/fe/core/messagebundle_iw.properties +33 -2
  184. package/src/sap/fe/core/messagebundle_ja.properties +33 -2
  185. package/src/sap/fe/core/messagebundle_kk.properties +33 -2
  186. package/src/sap/fe/core/messagebundle_ko.properties +33 -2
  187. package/src/sap/fe/core/messagebundle_lt.properties +33 -2
  188. package/src/sap/fe/core/messagebundle_lv.properties +33 -2
  189. package/src/sap/fe/core/messagebundle_ms.properties +33 -2
  190. package/src/sap/fe/core/messagebundle_nl.properties +33 -2
  191. package/src/sap/fe/core/messagebundle_no.properties +33 -2
  192. package/src/sap/fe/core/messagebundle_pl.properties +33 -2
  193. package/src/sap/fe/core/messagebundle_pt.properties +34 -3
  194. package/src/sap/fe/core/messagebundle_pt_PT.properties +33 -2
  195. package/src/sap/fe/core/messagebundle_ro.properties +33 -2
  196. package/src/sap/fe/core/messagebundle_ru.properties +33 -2
  197. package/src/sap/fe/core/messagebundle_sh.properties +33 -2
  198. package/src/sap/fe/core/messagebundle_sk.properties +33 -2
  199. package/src/sap/fe/core/messagebundle_sl.properties +33 -2
  200. package/src/sap/fe/core/messagebundle_sv.properties +34 -3
  201. package/src/sap/fe/core/messagebundle_th.properties +33 -2
  202. package/src/sap/fe/core/messagebundle_tr.properties +33 -2
  203. package/src/sap/fe/core/messagebundle_uk.properties +33 -2
  204. package/src/sap/fe/core/messagebundle_vi.properties +33 -2
  205. package/src/sap/fe/core/messagebundle_zh_CN.properties +33 -2
  206. package/src/sap/fe/core/messagebundle_zh_TW.properties +33 -2
  207. package/src/sap/fe/core/services/AsyncComponentServiceFactory.js +2 -2
  208. package/src/sap/fe/core/services/AsyncComponentServiceFactory.ts +3 -1
  209. package/src/sap/fe/core/services/CacheHandlerServiceFactory.js +269 -202
  210. package/src/sap/fe/core/services/CacheHandlerServiceFactory.ts +212 -0
  211. package/src/sap/fe/core/services/EnvironmentServiceFactory.js +4 -3
  212. package/src/sap/fe/core/services/EnvironmentServiceFactory.ts +9 -5
  213. package/src/sap/fe/core/services/NavigationServiceFactory.js +406 -300
  214. package/src/sap/fe/core/services/NavigationServiceFactory.ts +316 -0
  215. package/src/sap/fe/core/services/ResourceModelServiceFactory.js +149 -81
  216. package/src/sap/fe/core/services/ResourceModelServiceFactory.ts +80 -0
  217. package/src/sap/fe/core/services/RoutingServiceFactory.js +987 -1152
  218. package/src/sap/fe/core/services/RoutingServiceFactory.ts +898 -0
  219. package/src/sap/fe/core/services/ShellServicesFactory.js +31 -2
  220. package/src/sap/fe/core/services/ShellServicesFactory.ts +45 -11
  221. package/src/sap/fe/core/services/SideEffectsServiceFactory.js +42 -85
  222. package/src/sap/fe/core/services/SideEffectsServiceFactory.ts +57 -100
  223. package/src/sap/fe/core/services/TemplatedViewServiceFactory.js +461 -478
  224. package/src/sap/fe/core/services/TemplatedViewServiceFactory.ts +453 -0
  225. package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.js +10 -8
  226. package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.ts +8 -0
  227. package/src/sap/fe/core/support/AnnotationIssue.support.js +15 -3
  228. package/src/sap/fe/core/support/AnnotationIssue.support.ts +16 -2
  229. package/src/sap/fe/core/support/CollectionFacetUnsupportedLevel.support.js +2 -2
  230. package/src/sap/fe/core/support/CollectionFacetUnsupportedLevel.support.ts +1 -1
  231. package/src/sap/fe/core/support/InvalidAnnotationColumnKey.support.js +38 -0
  232. package/src/sap/fe/core/support/InvalidAnnotationColumnKey.support.ts +18 -0
  233. package/src/sap/fe/core/templating/DataModelPathHelper.js +10 -48
  234. package/src/sap/fe/core/templating/DataModelPathHelper.ts +14 -46
  235. package/src/sap/fe/core/templating/DisplayModeFormatter.js +114 -0
  236. package/src/sap/fe/core/templating/DisplayModeFormatter.ts +86 -0
  237. package/src/sap/fe/core/templating/FieldControlHelper.js +8 -8
  238. package/src/sap/fe/core/templating/FieldControlHelper.ts +25 -7
  239. package/src/sap/fe/core/templating/FilterHelper.js +139 -70
  240. package/src/sap/fe/core/templating/FilterHelper.ts +140 -70
  241. package/src/sap/fe/core/templating/PropertyHelper.js +2 -2
  242. package/src/sap/fe/core/templating/PropertyHelper.ts +1 -1
  243. package/src/sap/fe/core/templating/UIFormatters.js +45 -110
  244. package/src/sap/fe/core/templating/UIFormatters.ts +39 -75
  245. package/src/sap/fe/core/type/Email.js +1 -1
  246. package/src/sap/fe/core/type/Email.ts +4 -6
  247. package/ui5.yaml +0 -3
  248. package/src/sap/fe/core/controls/filterbar.d.js +0 -8
  249. package/src/sap/fe/core/controls/filterbar.d.ts +0 -0
@@ -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;