@sapui5/sap.fe.core 1.98.0 → 1.99.2

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 (231) hide show
  1. package/package.json +3 -2
  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 +3 -2
  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 -2399
  12. package/src/sap/fe/core/CommonUtils.ts +2078 -0
  13. package/src/sap/fe/core/ExtensionAPI.js +3 -2
  14. package/src/sap/fe/core/PageController.js +84 -125
  15. package/src/sap/fe/core/PageController.ts +101 -0
  16. package/src/sap/fe/core/RouterProxy.js +986 -823
  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 -164
  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 -1664
  25. package/src/sap/fe/core/TransactionHelper.ts +1706 -0
  26. package/src/sap/fe/core/actions/draft.js +561 -608
  27. package/src/sap/fe/core/actions/draft.ts +595 -0
  28. package/src/sap/fe/core/actions/messageHandling.js +545 -514
  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 -1215
  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 +170 -392
  38. package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.js +3 -2
  39. package/src/sap/fe/core/controllerextensions/InternalEditFlow.js +279 -11
  40. package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.js +8 -6
  41. package/src/sap/fe/core/controllerextensions/InternalRouting.js +52 -47
  42. package/src/sap/fe/core/controllerextensions/KPIManagement.js +2 -3
  43. package/src/sap/fe/core/controllerextensions/KPIManagement.ts +29 -26
  44. package/src/sap/fe/core/controllerextensions/MassEdit.js +101 -8
  45. package/src/sap/fe/core/controllerextensions/MessageHandler.js +17 -8
  46. package/src/sap/fe/core/controllerextensions/PageReady.js +2 -2
  47. package/src/sap/fe/core/controllerextensions/PageReady.ts +12 -8
  48. package/src/sap/fe/core/controllerextensions/Paginator.js +3 -2
  49. package/src/sap/fe/core/controllerextensions/Placeholder.js +3 -2
  50. package/src/sap/fe/core/controllerextensions/Routing.js +9 -4
  51. package/src/sap/fe/core/controllerextensions/RoutingListener.js +3 -2
  52. package/src/sap/fe/core/controllerextensions/Share.js +3 -2
  53. package/src/sap/fe/core/controllerextensions/SideEffects.js +3 -3
  54. package/src/sap/fe/core/controllerextensions/SideEffects.ts +18 -18
  55. package/src/sap/fe/core/controllerextensions/ViewState.js +3 -2
  56. package/src/sap/fe/core/controls/CommandExecution.js +3 -2
  57. package/src/sap/fe/core/controls/ConditionalWrapper.js +3 -2
  58. package/src/sap/fe/core/controls/CustomQuickViewPage.js +3 -2
  59. package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.js +3 -2
  60. package/src/sap/fe/core/controls/FieldWrapper.js +11 -22
  61. package/src/sap/fe/core/controls/FilterBar.js +3 -2
  62. package/src/sap/fe/core/controls/FormElementWrapper.js +3 -2
  63. package/src/sap/fe/core/controls/MultiValueParameterDelegate.js +3 -2
  64. package/src/sap/fe/core/controls/NonComputedVisibleKeyFieldsDialog.fragment.xml +1 -0
  65. package/src/sap/fe/core/controls/filterbar/FilterContainer.js +3 -2
  66. package/src/sap/fe/core/controls/filterbar/VisualFilter.js +5 -4
  67. package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.js +3 -2
  68. package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.js +3 -2
  69. package/src/sap/fe/core/controls/massEdit/MassEditHandlers.js +3 -2
  70. package/src/sap/fe/core/converters/ConverterContext.js +2 -2
  71. package/src/sap/fe/core/converters/ConverterContext.ts +4 -3
  72. package/src/sap/fe/core/converters/ManifestSettings.js +1 -1
  73. package/src/sap/fe/core/converters/ManifestSettings.ts +1 -0
  74. package/src/sap/fe/core/converters/ManifestWrapper.js +39 -26
  75. package/src/sap/fe/core/converters/ManifestWrapper.ts +9 -0
  76. package/src/sap/fe/core/converters/MetaModelConverter.js +11 -8
  77. package/src/sap/fe/core/converters/MetaModelConverter.ts +16 -16
  78. package/src/sap/fe/core/converters/TemplateConverter.js +1 -1
  79. package/src/sap/fe/core/converters/TemplateConverter.ts +2 -2
  80. package/src/sap/fe/core/converters/common/AnnotationConverter.js +17 -13
  81. package/src/sap/fe/core/converters/controls/Common/Action.js +2 -2
  82. package/src/sap/fe/core/converters/controls/Common/Action.ts +2 -2
  83. package/src/sap/fe/core/converters/controls/Common/Chart.js +5 -3
  84. package/src/sap/fe/core/converters/controls/Common/Chart.ts +11 -3
  85. package/src/sap/fe/core/converters/controls/Common/DataVisualization.js +3 -3
  86. package/src/sap/fe/core/converters/controls/Common/DataVisualization.ts +2 -2
  87. package/src/sap/fe/core/converters/controls/Common/Form.js +4 -2
  88. package/src/sap/fe/core/converters/controls/Common/Form.ts +16 -2
  89. package/src/sap/fe/core/converters/controls/Common/Table.js +250 -146
  90. package/src/sap/fe/core/converters/controls/Common/Table.ts +300 -167
  91. package/src/sap/fe/core/converters/controls/Common/table/StandardActions.js +115 -56
  92. package/src/sap/fe/core/converters/controls/Common/table/StandardActions.ts +120 -74
  93. package/src/sap/fe/core/converters/controls/ListReport/FilterBar.js +467 -32
  94. package/src/sap/fe/core/converters/controls/ListReport/FilterBar.ts +483 -54
  95. package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.js +6 -6
  96. package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.ts +9 -15
  97. package/src/sap/fe/core/converters/helpers/Aggregation.js +13 -5
  98. package/src/sap/fe/core/converters/helpers/Aggregation.ts +20 -5
  99. package/src/sap/fe/core/converters/helpers/IssueManager.js +4 -1
  100. package/src/sap/fe/core/converters/helpers/IssueManager.ts +3 -0
  101. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.js +4 -4
  102. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.ts +3 -3
  103. package/src/sap/fe/core/converters/templates/ListReportConverter.js +13 -6
  104. package/src/sap/fe/core/converters/templates/ListReportConverter.ts +26 -15
  105. package/src/sap/fe/core/designtime/AppComponent.designtime.js +3 -2
  106. package/src/sap/fe/core/formatters/CriticalityFormatter.js +1 -1
  107. package/src/sap/fe/core/formatters/CriticalityFormatter.ts +1 -1
  108. package/src/sap/fe/core/formatters/FPMFormatter.js +1 -1
  109. package/src/sap/fe/core/formatters/FPMFormatter.ts +4 -10
  110. package/src/sap/fe/core/formatters/KPIFormatter.js +1 -1
  111. package/src/sap/fe/core/formatters/KPIFormatter.ts +3 -1
  112. package/src/sap/fe/core/formatters/TableFormatter.js +39 -28
  113. package/src/sap/fe/core/formatters/TableFormatter.ts +43 -28
  114. package/src/sap/fe/core/formatters/ValueFormatter.js +1 -1
  115. package/src/sap/fe/core/formatters/ValueFormatter.ts +7 -6
  116. package/src/sap/fe/core/fpm/Component.js +3 -2
  117. package/src/sap/fe/core/helpers/AppStartupHelper.js +2 -2
  118. package/src/sap/fe/core/helpers/AppStartupHelper.ts +7 -4
  119. package/src/sap/fe/core/helpers/BindingExpression.js +314 -355
  120. package/src/sap/fe/core/helpers/BindingExpression.ts +317 -391
  121. package/src/sap/fe/core/helpers/ClassSupport.js +27 -15
  122. package/src/sap/fe/core/helpers/ClassSupport.ts +31 -20
  123. package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.js +63 -59
  124. package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.ts +56 -0
  125. package/src/sap/fe/core/helpers/EditState.js +81 -84
  126. package/src/sap/fe/core/helpers/EditState.ts +81 -0
  127. package/src/sap/fe/core/helpers/ExcelFormatHelper.js +62 -48
  128. package/src/sap/fe/core/helpers/ExcelFormatHelper.ts +49 -0
  129. package/src/sap/fe/core/helpers/FPMHelper.js +52 -56
  130. package/src/sap/fe/core/helpers/FPMHelper.ts +62 -0
  131. package/src/sap/fe/core/helpers/KeepAliveHelper.js +3 -4
  132. package/src/sap/fe/core/helpers/KeepAliveHelper.ts +9 -9
  133. package/src/sap/fe/core/helpers/MassEditHelper.js +27 -18
  134. package/src/sap/fe/core/helpers/ModelHelper.js +229 -225
  135. package/src/sap/fe/core/helpers/ModelHelper.ts +227 -0
  136. package/src/sap/fe/core/helpers/PasteHelper.js +210 -132
  137. package/src/sap/fe/core/helpers/PasteHelper.ts +196 -0
  138. package/src/sap/fe/core/helpers/SemanticDateOperators.js +332 -313
  139. package/src/sap/fe/core/helpers/SemanticDateOperators.ts +330 -0
  140. package/src/sap/fe/core/helpers/SemanticKeyHelper.js +66 -67
  141. package/src/sap/fe/core/helpers/SemanticKeyHelper.ts +73 -0
  142. package/src/sap/fe/core/helpers/StableIdHelper.js +4 -7
  143. package/src/sap/fe/core/helpers/StableIdHelper.ts +2 -6
  144. package/src/sap/fe/core/jsx-runtime/jsx.js +1 -1
  145. package/src/sap/fe/core/jsx-runtime/jsx.ts +1 -1
  146. package/src/sap/fe/core/library.js +4 -3
  147. package/src/sap/fe/core/library.support.js +3 -2
  148. package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.js +1 -1
  149. package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.ts +1 -1
  150. package/src/sap/fe/core/messagebundle.properties +23 -10
  151. package/src/sap/fe/core/messagebundle_ar.properties +9 -0
  152. package/src/sap/fe/core/messagebundle_bg.properties +9 -0
  153. package/src/sap/fe/core/messagebundle_ca.properties +9 -0
  154. package/src/sap/fe/core/messagebundle_cs.properties +10 -1
  155. package/src/sap/fe/core/messagebundle_cy.properties +9 -0
  156. package/src/sap/fe/core/messagebundle_da.properties +9 -0
  157. package/src/sap/fe/core/messagebundle_de.properties +9 -0
  158. package/src/sap/fe/core/messagebundle_el.properties +9 -0
  159. package/src/sap/fe/core/messagebundle_en.properties +9 -0
  160. package/src/sap/fe/core/messagebundle_en_GB.properties +9 -0
  161. package/src/sap/fe/core/messagebundle_en_US_sappsd.properties +9 -0
  162. package/src/sap/fe/core/messagebundle_en_US_saprigi.properties +9 -0
  163. package/src/sap/fe/core/messagebundle_en_US_saptrc.properties +9 -0
  164. package/src/sap/fe/core/messagebundle_es.properties +9 -0
  165. package/src/sap/fe/core/messagebundle_es_MX.properties +9 -0
  166. package/src/sap/fe/core/messagebundle_et.properties +9 -0
  167. package/src/sap/fe/core/messagebundle_fi.properties +9 -0
  168. package/src/sap/fe/core/messagebundle_fr.properties +13 -4
  169. package/src/sap/fe/core/messagebundle_fr_CA.properties +15 -6
  170. package/src/sap/fe/core/messagebundle_hi.properties +15 -6
  171. package/src/sap/fe/core/messagebundle_hr.properties +15 -6
  172. package/src/sap/fe/core/messagebundle_hu.properties +9 -0
  173. package/src/sap/fe/core/messagebundle_id.properties +10 -1
  174. package/src/sap/fe/core/messagebundle_it.properties +9 -0
  175. package/src/sap/fe/core/messagebundle_iw.properties +9 -0
  176. package/src/sap/fe/core/messagebundle_ja.properties +9 -0
  177. package/src/sap/fe/core/messagebundle_kk.properties +15 -6
  178. package/src/sap/fe/core/messagebundle_ko.properties +9 -0
  179. package/src/sap/fe/core/messagebundle_lt.properties +9 -0
  180. package/src/sap/fe/core/messagebundle_lv.properties +15 -6
  181. package/src/sap/fe/core/messagebundle_ms.properties +14 -5
  182. package/src/sap/fe/core/messagebundle_nl.properties +9 -0
  183. package/src/sap/fe/core/messagebundle_no.properties +9 -0
  184. package/src/sap/fe/core/messagebundle_pl.properties +9 -0
  185. package/src/sap/fe/core/messagebundle_pt.properties +9 -0
  186. package/src/sap/fe/core/messagebundle_pt_PT.properties +9 -0
  187. package/src/sap/fe/core/messagebundle_ro.properties +9 -0
  188. package/src/sap/fe/core/messagebundle_ru.properties +9 -0
  189. package/src/sap/fe/core/messagebundle_sh.properties +11 -2
  190. package/src/sap/fe/core/messagebundle_sk.properties +9 -0
  191. package/src/sap/fe/core/messagebundle_sl.properties +15 -6
  192. package/src/sap/fe/core/messagebundle_sv.properties +9 -0
  193. package/src/sap/fe/core/messagebundle_th.properties +9 -0
  194. package/src/sap/fe/core/messagebundle_tr.properties +9 -0
  195. package/src/sap/fe/core/messagebundle_uk.properties +9 -0
  196. package/src/sap/fe/core/messagebundle_vi.properties +9 -0
  197. package/src/sap/fe/core/messagebundle_zh_CN.properties +9 -0
  198. package/src/sap/fe/core/messagebundle_zh_TW.properties +15 -6
  199. package/src/sap/fe/core/services/AsyncComponentServiceFactory.js +1 -1
  200. package/src/sap/fe/core/services/AsyncComponentServiceFactory.ts +3 -1
  201. package/src/sap/fe/core/services/CacheHandlerServiceFactory.js +269 -202
  202. package/src/sap/fe/core/services/CacheHandlerServiceFactory.ts +212 -0
  203. package/src/sap/fe/core/services/EnvironmentServiceFactory.js +1 -1
  204. package/src/sap/fe/core/services/EnvironmentServiceFactory.ts +7 -6
  205. package/src/sap/fe/core/services/NavigationServiceFactory.js +406 -300
  206. package/src/sap/fe/core/services/NavigationServiceFactory.ts +316 -0
  207. package/src/sap/fe/core/services/ResourceModelServiceFactory.js +149 -81
  208. package/src/sap/fe/core/services/ResourceModelServiceFactory.ts +80 -0
  209. package/src/sap/fe/core/services/RoutingServiceFactory.js +987 -899
  210. package/src/sap/fe/core/services/RoutingServiceFactory.ts +898 -0
  211. package/src/sap/fe/core/services/ShellServicesFactory.js +1 -1
  212. package/src/sap/fe/core/services/ShellServicesFactory.ts +15 -10
  213. package/src/sap/fe/core/services/SideEffectsServiceFactory.js +35 -81
  214. package/src/sap/fe/core/services/SideEffectsServiceFactory.ts +46 -94
  215. package/src/sap/fe/core/services/TemplatedViewServiceFactory.js +461 -487
  216. package/src/sap/fe/core/services/TemplatedViewServiceFactory.ts +453 -0
  217. package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.js +10 -8
  218. package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.ts +8 -0
  219. package/src/sap/fe/core/support/AnnotationIssue.support.js +15 -3
  220. package/src/sap/fe/core/support/AnnotationIssue.support.ts +16 -2
  221. package/src/sap/fe/core/support/CollectionFacetUnsupportedLevel.support.js +2 -2
  222. package/src/sap/fe/core/support/CollectionFacetUnsupportedLevel.support.ts +1 -1
  223. package/src/sap/fe/core/templating/FieldControlHelper.js +8 -8
  224. package/src/sap/fe/core/templating/FieldControlHelper.ts +25 -7
  225. package/src/sap/fe/core/templating/FilterHelper.js +138 -72
  226. package/src/sap/fe/core/templating/FilterHelper.ts +139 -71
  227. package/src/sap/fe/core/templating/UIFormatters.js +31 -1
  228. package/src/sap/fe/core/templating/UIFormatters.ts +33 -2
  229. package/src/sap/fe/core/type/Email.js +1 -1
  230. package/src/sap/fe/core/type/Email.ts +4 -6
  231. package/ui5.yaml +0 -3
@@ -0,0 +1,898 @@
1
+ import Event from "sap/ui/base/Event";
2
+ import Service from "sap/ui/core/service/Service";
3
+ import ServiceFactory from "sap/ui/core/service/ServiceFactory";
4
+ import BindingParser from "sap/ui/base/BindingParser";
5
+ import SemanticKeyHelper from "sap/fe/core/helpers/SemanticKeyHelper";
6
+ import Log from "sap/base/Log";
7
+ import EventProvider from "sap/ui/base/EventProvider";
8
+ import BusyLocker from "sap/fe/core/BusyLocker";
9
+ import ModelHelper from "sap/fe/core/helpers/ModelHelper";
10
+ import EditState from "sap/fe/core/helpers/EditState";
11
+ import messageHandling from "sap/fe/core/actions/messageHandling";
12
+ import { Placeholder } from "sap/fe/core/controllerextensions";
13
+ import AppStartupHelper from "sap/fe/core/helpers/AppStartupHelper";
14
+ import ODataUtils from "sap/ui/model/odata/v4/ODataUtils";
15
+ import { ServiceContext } from "sap/ui/core/service";
16
+ import { AppComponent } from "sap/fe/core";
17
+ import ODataModel from "sap/ui/model/odata/v4/ODataModel";
18
+ import ODataMetaModel from "sap/ui/model/odata/v4/ODataMetaModel";
19
+ import Router from "sap/ui/core/routing/Router";
20
+ import RouterProxy from "sap/fe/core/RouterProxy";
21
+ import Context from "sap/ui/model/odata/v4/Context";
22
+ type RoutingServiceSettings = {};
23
+ const RoutingServiceEventing = EventProvider.extend("sap.fe.core.services.RoutingServiceEventing", {
24
+ metadata: {
25
+ events: {
26
+ "routeMatched": {},
27
+ "afterRouteMatched": {}
28
+ }
29
+ }
30
+ });
31
+ class RoutingService extends Service<RoutingServiceSettings> {
32
+ oAppComponent!: AppComponent;
33
+ oModel!: ODataModel;
34
+ oMetaModel!: ODataMetaModel;
35
+ oRouter!: Router;
36
+ oRouterProxy!: RouterProxy;
37
+ eventProvider!: EventProvider;
38
+ initPromise!: Promise<any>;
39
+ outbounds: any;
40
+ _mTargets: any;
41
+ _mRoutes: any;
42
+ oLastSemanticMapping: any;
43
+ bExitOnNavigateBackToRoot?: boolean;
44
+ sCurrentRouteName?: string;
45
+ sCurrentRoutePattern?: string;
46
+ aCurrentViews?: any[];
47
+ navigationInfoQueue: any[] = [];
48
+ sContextPath!: string;
49
+ _fnOnRouteMatched!: Function;
50
+ init() {
51
+ const oContext = this.getContext();
52
+ if (oContext.scopeType === "component") {
53
+ this.oAppComponent = oContext.scopeObject;
54
+ this.oModel = this.oAppComponent.getModel();
55
+ this.oMetaModel = this.oModel.getMetaModel();
56
+ this.oRouter = this.oAppComponent.getRouter();
57
+ this.oRouterProxy = this.oAppComponent.getRouterProxy();
58
+ this.eventProvider = new (RoutingServiceEventing as any)();
59
+
60
+ const oRoutingConfig = this.oAppComponent.getManifestEntry("/sap.ui5/routing");
61
+ const oRootViewConfig = this.oAppComponent.getManifestEntry("/sap.ui5/rootView");
62
+ this._parseRoutingConfiguration(oRoutingConfig, oRootViewConfig);
63
+
64
+ const oAppConfig = this.oAppComponent.getManifestEntry("/sap.app");
65
+ this.outbounds = oAppConfig && oAppConfig.crossNavigation && oAppConfig.crossNavigation.outbounds;
66
+ }
67
+
68
+ this.initPromise = Promise.resolve(this);
69
+ }
70
+ beforeExit() {
71
+ this.oRouter.detachRouteMatched(this._fnOnRouteMatched, this);
72
+ this.eventProvider.fireEvent("routeMatched", {});
73
+ }
74
+ exit() {
75
+ this.eventProvider.destroy();
76
+ }
77
+
78
+ /**
79
+ * Parse a manifest routing configuration for internal usage.
80
+ *
81
+ * @param {object} oRoutingConfig The routing configuration from the manifest
82
+ * @param {object} oRootViewConfig The root view configuration from the manifest
83
+ * @private
84
+ */
85
+ _parseRoutingConfiguration(oRoutingConfig: any, oRootViewConfig: any) {
86
+ const isFCL = oRootViewConfig && oRootViewConfig.viewName === "sap.fe.templates.RootContainer.view.Fcl";
87
+
88
+ // Information of targets
89
+ this._mTargets = {};
90
+ Object.keys(oRoutingConfig.targets).forEach((sTargetName: string) => {
91
+ this._mTargets[sTargetName] = Object.assign({ targetName: sTargetName }, oRoutingConfig.targets[sTargetName]);
92
+
93
+ // View level for FCL cases is calculated from the target pattern
94
+ if (this._mTargets[sTargetName].contextPattern !== undefined) {
95
+ this._mTargets[sTargetName].viewLevel = this._getViewLevelFromPattern(this._mTargets[sTargetName].contextPattern, 0);
96
+ }
97
+ });
98
+
99
+ // Information of routes
100
+ this._mRoutes = {};
101
+ for (const sRouteKey in oRoutingConfig.routes) {
102
+ const oRouteManifestInfo = oRoutingConfig.routes[sRouteKey],
103
+ aRouteTargets = Array.isArray(oRouteManifestInfo.target) ? oRouteManifestInfo.target : [oRouteManifestInfo.target],
104
+ sRouteName = Array.isArray(oRoutingConfig.routes) ? oRouteManifestInfo.name : sRouteKey,
105
+ sRoutePattern = oRouteManifestInfo.pattern;
106
+
107
+ // Check route pattern: all patterns need to end with ':?query:', that we use for parameters
108
+ if (sRoutePattern.length < 8 || sRoutePattern.indexOf(":?query:") !== sRoutePattern.length - 8) {
109
+ Log.warning("Pattern for route " + sRouteName + " doesn't end with ':?query:' : " + sRoutePattern);
110
+ }
111
+ const iRouteLevel = this._getViewLevelFromPattern(sRoutePattern, 0);
112
+ this._mRoutes[sRouteName] = {
113
+ name: sRouteName,
114
+ pattern: sRoutePattern,
115
+ targets: aRouteTargets,
116
+ routeLevel: iRouteLevel
117
+ };
118
+
119
+ // Add the parent targets in the list of targets for the route
120
+ for (let i = 0; i < aRouteTargets.length; i++) {
121
+ const sParentTargetName = this._mTargets[aRouteTargets[i]].parent;
122
+ if (sParentTargetName) {
123
+ aRouteTargets.push(sParentTargetName);
124
+ }
125
+ }
126
+
127
+ if (!isFCL) {
128
+ // View level for non-FCL cases is calculated from the route pattern
129
+ if (this._mTargets[aRouteTargets[0]].viewLevel === undefined || this._mTargets[aRouteTargets[0]].viewLevel < iRouteLevel) {
130
+ // There are cases when different routes point to the same target. We take the
131
+ // largest viewLevel in that case.
132
+ this._mTargets[aRouteTargets[0]].viewLevel = iRouteLevel;
133
+ }
134
+
135
+ // FCL level for non-FCL cases is equal to -1
136
+ this._mTargets[aRouteTargets[0]].FCLLevel = -1;
137
+ } else if (aRouteTargets.length === 1 && this._mTargets[aRouteTargets[0]].controlAggregation !== "beginColumnPages") {
138
+ // We're in the case where there's only 1 target for the route, and it's not in the first column
139
+ // --> this is a fullscreen column after all columns in the FCL have been used
140
+ this._mTargets[aRouteTargets[0]].FCLLevel = 3;
141
+ } else {
142
+ // Other FCL cases
143
+ aRouteTargets.forEach((sTargetName: any) => {
144
+ switch (this._mTargets[sTargetName].controlAggregation) {
145
+ case "beginColumnPages":
146
+ this._mTargets[sTargetName].FCLLevel = 0;
147
+ break;
148
+
149
+ case "midColumnPages":
150
+ this._mTargets[sTargetName].FCLLevel = 1;
151
+ break;
152
+
153
+ default:
154
+ this._mTargets[sTargetName].FCLLevel = 2;
155
+ }
156
+ });
157
+ }
158
+ }
159
+
160
+ // Propagate viewLevel, contextPattern, FCLLevel and controlAggregation to parent targets
161
+ Object.keys(this._mTargets).forEach((sTargetName: string) => {
162
+ while (this._mTargets[sTargetName].parent) {
163
+ const sParentTargetName = this._mTargets[sTargetName].parent;
164
+ this._mTargets[sParentTargetName].viewLevel =
165
+ this._mTargets[sParentTargetName].viewLevel || this._mTargets[sTargetName].viewLevel;
166
+ this._mTargets[sParentTargetName].contextPattern =
167
+ this._mTargets[sParentTargetName].contextPattern || this._mTargets[sTargetName].contextPattern;
168
+ this._mTargets[sParentTargetName].FCLLevel =
169
+ this._mTargets[sParentTargetName].FCLLevel || this._mTargets[sTargetName].FCLLevel;
170
+ this._mTargets[sParentTargetName].controlAggregation =
171
+ this._mTargets[sParentTargetName].controlAggregation || this._mTargets[sTargetName].controlAggregation;
172
+ sTargetName = sParentTargetName;
173
+ }
174
+ });
175
+
176
+ // Determine the root entity for the app
177
+ const aLevel0RouteNames = [];
178
+ const aLevel1RouteNames = [];
179
+ let sDefaultRouteName;
180
+
181
+ for (const sName in this._mRoutes) {
182
+ const iLevel = this._mRoutes[sName].routeLevel;
183
+ if (iLevel === 0) {
184
+ aLevel0RouteNames.push(sName);
185
+ } else if (iLevel === 1) {
186
+ aLevel1RouteNames.push(sName);
187
+ }
188
+ }
189
+
190
+ if (aLevel0RouteNames.length === 1) {
191
+ sDefaultRouteName = aLevel0RouteNames[0];
192
+ } else if (aLevel1RouteNames.length === 1) {
193
+ sDefaultRouteName = aLevel1RouteNames[0];
194
+ }
195
+
196
+ if (sDefaultRouteName) {
197
+ const sDefaultTargetName = this._mRoutes[sDefaultRouteName].targets.slice(-1)[0];
198
+ this.sContextPath = "";
199
+ if (this._mTargets[sDefaultTargetName].options && this._mTargets[sDefaultTargetName].options.settings) {
200
+ const oSettings = this._mTargets[sDefaultTargetName].options.settings;
201
+ this.sContextPath = oSettings.contextPath || "/" + oSettings.entitySet;
202
+ }
203
+ if (!this.sContextPath) {
204
+ Log.warning(
205
+ "Cannot determine default contextPath: contextPath or entitySet missing in default target: " + sDefaultTargetName
206
+ );
207
+ }
208
+ } else {
209
+ Log.warning("Cannot determine default contextPath: no default route found.");
210
+ }
211
+
212
+ // We need to establish the correct path to the different pages, including the navigation properties
213
+ Object.keys(this._mTargets)
214
+ .map((sTargetKey: string) => {
215
+ return this._mTargets[sTargetKey];
216
+ })
217
+ .sort((a: any, b: any) => {
218
+ return a.viewLevel < b.viewLevel ? -1 : 1;
219
+ })
220
+ .forEach((target: any) => {
221
+ // After sorting the targets per level we can then go through their navigation object and update the paths accordingly.
222
+ if (target.options) {
223
+ const settings = target.options.settings;
224
+ const sContextPath = settings.contextPath || (settings.entitySet ? "/" + settings.entitySet : "");
225
+ if (!settings.fullContextPath && sContextPath) {
226
+ settings.fullContextPath = sContextPath + "/";
227
+ }
228
+ Object.keys(settings.navigation || {}).forEach((sNavName: string) => {
229
+ // Check if it's a navigation property
230
+ const targetRoute = this._mRoutes[settings.navigation[sNavName].detail.route];
231
+ if (targetRoute && targetRoute.targets) {
232
+ targetRoute.targets.forEach((sTargetName: any) => {
233
+ if (
234
+ this._mTargets[sTargetName].options &&
235
+ this._mTargets[sTargetName].options.settings &&
236
+ !this._mTargets[sTargetName].options.settings.fullContextPath
237
+ ) {
238
+ if (target.viewLevel === 0) {
239
+ this._mTargets[sTargetName].options.settings.fullContextPath = "/" + sNavName + "/";
240
+ } else {
241
+ this._mTargets[sTargetName].options.settings.fullContextPath =
242
+ settings.fullContextPath + sNavName + "/";
243
+ }
244
+ }
245
+ });
246
+ }
247
+ });
248
+ }
249
+ });
250
+ }
251
+
252
+ /**
253
+ * Calculates a view level from a pattern by counting the number of segments.
254
+ *
255
+ * @param {string} sPattern The pattern
256
+ * @param {number} viewLevel The current level of view
257
+ * @returns {number} The level
258
+ */
259
+ _getViewLevelFromPattern(sPattern: string, viewLevel: number): number {
260
+ sPattern = sPattern.replace(":?query:", "");
261
+ const regex = new RegExp("/[^/]*$");
262
+ if (sPattern && sPattern[0] !== "/" && sPattern[0] !== "?") {
263
+ sPattern = "/" + sPattern;
264
+ }
265
+ if (sPattern.length) {
266
+ sPattern = sPattern.replace(regex, "");
267
+ if (this.oRouter.match(sPattern) || sPattern === "") {
268
+ return this._getViewLevelFromPattern(sPattern, ++viewLevel);
269
+ } else {
270
+ return this._getViewLevelFromPattern(sPattern, viewLevel);
271
+ }
272
+ } else {
273
+ return viewLevel;
274
+ }
275
+ }
276
+
277
+ _getRouteInformation(sRouteName: any) {
278
+ return this._mRoutes[sRouteName];
279
+ }
280
+
281
+ _getTargetInformation(sTargetName: any) {
282
+ return this._mTargets[sTargetName];
283
+ }
284
+
285
+ _getComponentId(sOwnerId: any, sComponentId: any) {
286
+ if (sComponentId.indexOf(sOwnerId + "---") === 0) {
287
+ return sComponentId.substr(sOwnerId.length + 3);
288
+ }
289
+ return sComponentId;
290
+ }
291
+
292
+ /**
293
+ * Get target information for a given component.
294
+ *
295
+ * @param {object} oComponentInstance Instance of the component
296
+ * @returns {object} The configuration for the target
297
+ */
298
+ getTargetInformationFor(oComponentInstance: any) {
299
+ const sTargetComponentId = this._getComponentId(oComponentInstance._sOwnerId, oComponentInstance.getId());
300
+ let sCorrectTargetName = null;
301
+ Object.keys(this._mTargets).forEach((sTargetName: string) => {
302
+ if (this._mTargets[sTargetName].id === sTargetComponentId || this._mTargets[sTargetName].viewId === sTargetComponentId) {
303
+ sCorrectTargetName = sTargetName;
304
+ }
305
+ });
306
+ return this._getTargetInformation(sCorrectTargetName);
307
+ }
308
+
309
+ getLastSemanticMapping() {
310
+ return this.oLastSemanticMapping;
311
+ }
312
+
313
+ setLastSemanticMapping(oMapping: any) {
314
+ this.oLastSemanticMapping = oMapping;
315
+ }
316
+
317
+ navigateTo(oContext: any, sRouteName: any, mParameterMapping: any, bPreserveHistory: any) {
318
+ let sTargetURLPromise, bIsStickyMode: boolean;
319
+ if (oContext.getModel() && oContext.getModel().getMetaModel) {
320
+ bIsStickyMode = ModelHelper.isStickySessionSupported(oContext.getModel().getMetaModel());
321
+ }
322
+ if (!mParameterMapping) {
323
+ // if there is no parameter mapping define this mean we rely entirely on the binding context path
324
+ sTargetURLPromise = Promise.resolve(SemanticKeyHelper.getSemanticPath(oContext));
325
+ } else {
326
+ sTargetURLPromise = this.prepareParameters(mParameterMapping, sRouteName, oContext).then((mParameters: any) => {
327
+ return this.oRouter.getURL(sRouteName, mParameters);
328
+ });
329
+ }
330
+ return sTargetURLPromise.then((sTargetURL: any) => {
331
+ this.oRouterProxy.navToHash(sTargetURL, bPreserveHistory, false, false, !bIsStickyMode);
332
+ });
333
+ }
334
+
335
+ /**
336
+ * Method to return a map of routing target parameters where the binding syntax is resolved to the current model.
337
+ *
338
+ * @param {object} mParameters Parameters map in the format [k: string] : ComplexBindingSyntax
339
+ * @param {string} sTargetRoute Name of the target route
340
+ * @param {object} oContext The instance of the binding context
341
+ * @returns {Promise} A promise which resolves to the routing target parameters
342
+ */
343
+ prepareParameters(mParameters: any, sTargetRoute: string, oContext: Context) {
344
+ let oParametersPromise;
345
+ try {
346
+ const sContextPath = oContext.getPath();
347
+ const oMetaModel: ODataMetaModel = oContext.getModel().getMetaModel() as ODataMetaModel;
348
+ const aContextPathParts = sContextPath.split("/");
349
+ const aAllResolvedParameterPromises = Object.keys(mParameters).map((sParameterKey: any) => {
350
+ const sParameterMappingExpression = mParameters[sParameterKey];
351
+ // We assume the defined parameters will be compatible with a binding expression
352
+ const oParsedExpression = BindingParser.complexParser(sParameterMappingExpression);
353
+ const aParts = oParsedExpression.parts || [oParsedExpression];
354
+ const aResolvedParameterPromises = aParts.map(function(oPathPart: any) {
355
+ const aRelativeParts = oPathPart.path.split("../");
356
+ // We go up the current context path as many times as necessary
357
+ const aLocalParts = aContextPathParts.slice(0, aContextPathParts.length - aRelativeParts.length + 1);
358
+ aLocalParts.push(aRelativeParts[aRelativeParts.length - 1]);
359
+
360
+ const sPropertyPath = aLocalParts.join("/");
361
+ const oMetaContext = (oMetaModel as any).getMetaContext(sPropertyPath);
362
+ return oContext.requestProperty(sPropertyPath).then(function(oValue: any) {
363
+ const oPropertyInfo = oMetaContext.getObject();
364
+ const sEdmType = oPropertyInfo.$Type;
365
+ return ODataUtils.formatLiteral(oValue, sEdmType);
366
+ });
367
+ });
368
+
369
+ return Promise.all(aResolvedParameterPromises).then((aResolvedParameters: any) => {
370
+ const value = oParsedExpression.formatter
371
+ ? oParsedExpression.formatter.apply(this, aResolvedParameters)
372
+ : aResolvedParameters.join("");
373
+ return { key: sParameterKey, value: value };
374
+ });
375
+ });
376
+
377
+ oParametersPromise = Promise.all(aAllResolvedParameterPromises).then(function(
378
+ aAllResolvedParameters: { key: any; value: any }[]
379
+ ) {
380
+ const oParameters: any = {};
381
+ aAllResolvedParameters.forEach(function(oResolvedParameter: { key: any; value: any }) {
382
+ oParameters[oResolvedParameter.key] = oResolvedParameter.value;
383
+ });
384
+ return oParameters;
385
+ });
386
+ } catch (oError) {
387
+ Log.error("Could not parse the parameters for the navigation to route " + sTargetRoute);
388
+ oParametersPromise = Promise.resolve(undefined);
389
+ }
390
+ return oParametersPromise;
391
+ }
392
+
393
+ _fireRouteMatchEvents(mParameters: any) {
394
+ this.eventProvider.fireEvent("routeMatched", mParameters);
395
+ this.eventProvider.fireEvent("afterRouteMatched", mParameters);
396
+
397
+ EditState.cleanProcessedEditState(); // Reset UI state when all bindings have been refreshed
398
+ }
399
+
400
+ /**
401
+ * Navigates to a context.
402
+ *
403
+ * @param {sap.ui.model.odata.v4.Context} oContext The Context to be navigated to
404
+ * @param {object} [mParameters] Optional, map containing the following attributes:
405
+ * @param {boolean} [mParameters.checkNoHashChange] Navigate to the context without changing the URL
406
+ * @param {Promise} [mParameters.asyncContext] The context is created async, navigate to (...) and
407
+ * wait for Promise to be resolved and then navigate into the context
408
+ * @param {boolean} [mParameters.bDeferredContext] The context shall be created deferred at the target page
409
+ * @param {boolean} [mParameters.editable] The target page shall be immediately in the edit mode to avoid flickering
410
+ * @param {boolean} [mParameters.bPersistOPScroll] The bPersistOPScroll will be used for scrolling to first tab
411
+ * @param {number} [mParameters.updateFCLLevel] `+1` if we add a column in FCL, `-1` to remove a column, 0 to stay on the same column
412
+ * @param {boolean} [mParameters.noPreservationCache] Do navigation without taking into account the preserved cache mechanism
413
+ * @param {boolean} [mParameters.bRecreateContext] Force re-creation of the context instead of using the one passed as parameter
414
+ * @param {boolean} [mParameters.bForceFocus] Forces focus selection after navigation
415
+ * @param {object} [oViewData] View data
416
+ * @param {object} [oCurrentTargetInfo] The target information from which the navigation is triggered
417
+ * @returns {Promise} Promise which is resolved once the navigation is triggered
418
+ *
419
+ * @ui5-restricted
420
+ * @final
421
+ */
422
+ navigateToContext(
423
+ oContext: any,
424
+ mParameters:
425
+ | {
426
+ checkNoHashChange?: boolean;
427
+ asyncContext?: Promise<any>;
428
+ bDeferredContext?: boolean;
429
+ editable?: boolean;
430
+ transient?: boolean;
431
+ bPersistOPScroll?: boolean;
432
+ updateFCLLevel?: number;
433
+ noPreservationCache?: boolean;
434
+ bRecreateContext?: boolean;
435
+ bForceFocus?: boolean;
436
+ targetPath?: string;
437
+ showPlaceholder?: boolean;
438
+ bDraftNavigation?: boolean;
439
+ }
440
+ | undefined,
441
+ oViewData: any | undefined,
442
+ oCurrentTargetInfo: any | undefined
443
+ ) {
444
+ let sTargetRoute: string = "",
445
+ oRouteParametersPromise,
446
+ bIsStickyMode: boolean = false;
447
+
448
+ if (oContext.getModel() && oContext.getModel().getMetaModel) {
449
+ bIsStickyMode = ModelHelper.isStickySessionSupported(oContext.getModel().getMetaModel());
450
+ }
451
+ // Manage parameter mapping
452
+ if (mParameters && mParameters.targetPath && oViewData && oViewData.navigation) {
453
+ const oRouteDetail = oViewData.navigation[mParameters.targetPath].detail;
454
+ sTargetRoute = oRouteDetail.route;
455
+
456
+ if (oRouteDetail.parameters) {
457
+ oRouteParametersPromise = this.prepareParameters(oRouteDetail.parameters, sTargetRoute, oContext);
458
+ }
459
+ }
460
+
461
+ let sTargetPath = this._getPathFromContext(oContext, mParameters);
462
+ // If the path is empty, we're supposed to navigate to the first page of the app
463
+ // Check if we need to exit from the app instead
464
+ if (sTargetPath.length === 0 && this.bExitOnNavigateBackToRoot) {
465
+ return this.oRouterProxy.exitFromApp();
466
+ }
467
+
468
+ // If the context is deferred or async, we add (...) to the path
469
+ if (mParameters?.asyncContext || mParameters?.bDeferredContext) {
470
+ sTargetPath += "(...)";
471
+ }
472
+
473
+ // Add layout parameter if needed
474
+ const sLayout = this._calculateLayout(sTargetPath, mParameters);
475
+ if (sLayout) {
476
+ sTargetPath += "?layout=" + sLayout;
477
+ }
478
+
479
+ // Navigation parameters for later usage
480
+ const oNavigationInfo = {
481
+ oAsyncContext: mParameters?.asyncContext,
482
+ bDeferredContext: mParameters?.bDeferredContext,
483
+ bTargetEditable: mParameters?.editable,
484
+ bPersistOPScroll: mParameters?.bPersistOPScroll,
485
+ useContext: mParameters?.updateFCLLevel === -1 || mParameters?.bRecreateContext ? undefined : oContext,
486
+ bDraftNavigation: mParameters?.bDraftNavigation,
487
+ bShowPlaceholder: mParameters?.showPlaceholder !== undefined ? mParameters?.showPlaceholder : true
488
+ };
489
+
490
+ if (mParameters?.checkNoHashChange) {
491
+ // Check if the new hash is different from the current one
492
+ const sCurrentHashNoAppState = this.oRouterProxy.getHash().replace(/[&?]{1}sap-iapp-state=[A-Z0-9]+/, "");
493
+ if (sTargetPath === sCurrentHashNoAppState) {
494
+ // The hash doesn't change, but we fire the routeMatch event to trigger page refresh / binding
495
+ const mEventParameters: any = this.oRouter.getRouteInfoByHash(this.oRouterProxy.getHash());
496
+ if (mEventParameters) {
497
+ mEventParameters.navigationInfo = oNavigationInfo;
498
+ mEventParameters.routeInformation = this._getRouteInformation(this.sCurrentRouteName);
499
+ mEventParameters.routePattern = this.sCurrentRoutePattern;
500
+ mEventParameters.views = this.aCurrentViews;
501
+ }
502
+
503
+ this.oRouterProxy.setFocusForced(!!mParameters.bForceFocus);
504
+
505
+ this._fireRouteMatchEvents(mEventParameters);
506
+
507
+ return Promise.resolve();
508
+ }
509
+ }
510
+
511
+ if (mParameters?.transient && mParameters.editable == true && sTargetPath.indexOf("(...)") === -1) {
512
+ if (sTargetPath.indexOf("?") > -1) {
513
+ sTargetPath += "&i-action=create";
514
+ } else {
515
+ sTargetPath += "?i-action=create";
516
+ }
517
+ }
518
+
519
+ // Clear unbound messages upon navigating from LR to OP
520
+ // This is to ensure stale error messages from LR are not shown to the user after navigation to OP.
521
+ if (oCurrentTargetInfo && oCurrentTargetInfo.name === "sap.fe.templates.ListReport") {
522
+ const oRouteInfo = this.oRouter.getRouteInfoByHash(sTargetPath);
523
+ if (oRouteInfo) {
524
+ const oRoute = this._getRouteInformation(oRouteInfo.name);
525
+ if (oRoute && oRoute.targets && oRoute.targets.length > 0) {
526
+ const sLastTargetName = oRoute.targets[oRoute.targets.length - 1];
527
+ const oTarget = this._getTargetInformation(sLastTargetName);
528
+ if (oTarget && oTarget.name === "sap.fe.templates.ObjectPage") {
529
+ messageHandling.removeUnboundTransitionMessages();
530
+ }
531
+ }
532
+ }
533
+ }
534
+
535
+ // Add the navigation parameters in the queue
536
+ this.navigationInfoQueue.push(oNavigationInfo);
537
+
538
+ if (sTargetRoute && oRouteParametersPromise) {
539
+ return oRouteParametersPromise.then((oRouteParameters: any) => {
540
+ oRouteParameters.bIsStickyMode = bIsStickyMode;
541
+ this.oRouter.navTo(sTargetRoute, oRouteParameters);
542
+ return Promise.resolve();
543
+ });
544
+ }
545
+
546
+ return this.oRouterProxy
547
+ .navToHash(sTargetPath, false, mParameters?.noPreservationCache, mParameters?.bForceFocus, !bIsStickyMode)
548
+ .then((bNavigated: any) => {
549
+ if (!bNavigated) {
550
+ // The navigation did not happen --> remove the navigation parameters from the queue as they shouldn't be used
551
+ this.navigationInfoQueue.pop();
552
+ }
553
+ });
554
+ }
555
+
556
+ /**
557
+ * Navigates to a route.
558
+ *
559
+ * @function
560
+ * @name sap.fe.core.controllerextensions.Routing#navigateToRoute
561
+ * @memberof sap.fe.core.controllerextensions.Routing
562
+ * @static
563
+ * @param {string} [sTargetRouteName] Name of the target route
564
+ * @param {object} [oRouteParameters] Parameters to be used with route to create the target hash
565
+ * @returns {Promise} Promise that is resolved when the navigation is finalized
566
+ *
567
+ * @ui5-restricted
568
+ * @final
569
+ */
570
+ navigateToRoute(sTargetRouteName: string, oRouteParameters: any | undefined) {
571
+ const sTargetURL = this.oRouter.getURL(sTargetRouteName, oRouteParameters);
572
+ return this.oRouterProxy.navToHash(sTargetURL, undefined, undefined, undefined, !oRouteParameters.bIsStickyMode);
573
+ }
574
+
575
+ /**
576
+ * Checks if one of the current views on the screen is bound to a given context.
577
+ *
578
+ * @param {sap.ui.model.odata.v4.Context} oContext The context
579
+ * @returns {boolean} `true` or `false` if the current state is impacted or not
580
+ */
581
+ isCurrentStateImpactedBy(oContext: any) {
582
+ const sPath = oContext.getPath();
583
+
584
+ // First, check with the technical path. We have to try it, because for level > 1, we
585
+ // uses technical keys even if Semantic keys are enabled
586
+ if (this.oRouterProxy.isCurrentStateImpactedBy(sPath)) {
587
+ return true;
588
+ } else if (/^[^\(\)]+\([^\(\)]+\)$/.test(sPath)) {
589
+ // If the current path can be semantic (i.e. is like xxx(yyy))
590
+ // check with the semantic path if we can find it
591
+ let sSemanticPath;
592
+ if (this.oLastSemanticMapping && this.oLastSemanticMapping.technicalPath === sPath) {
593
+ // We have already resolved this semantic path
594
+ sSemanticPath = this.oLastSemanticMapping.semanticPath;
595
+ } else {
596
+ sSemanticPath = SemanticKeyHelper.getSemanticPath(oContext);
597
+ }
598
+
599
+ return sSemanticPath != sPath ? this.oRouterProxy.isCurrentStateImpactedBy(sSemanticPath) : false;
600
+ } else {
601
+ return false;
602
+ }
603
+ }
604
+
605
+ _findPathToNavigate(sPath: any): string {
606
+ const regex = new RegExp("/[^/]*$");
607
+ sPath = sPath.replace(regex, "");
608
+ if (this.oRouter.match(sPath) || sPath === "") {
609
+ return sPath;
610
+ } else {
611
+ return this._findPathToNavigate(sPath);
612
+ }
613
+ }
614
+
615
+ _checkContextForSemanticPath(oContext: any) {
616
+ const sPath = oContext.getPath();
617
+
618
+ // First, check if this is a level-1 object (path = /aaa(bbb))
619
+ if (!/^\/[^\(]+\([^\)]+\)$/.test(sPath)) {
620
+ return false;
621
+ }
622
+
623
+ // Then check if the entity has semantic keys
624
+ const oMetaModel = oContext.getModel().getMetaModel(),
625
+ sEntitySetName = oMetaModel.getMetaContext(oContext.getPath()).getObject("@sapui.name");
626
+ if (!SemanticKeyHelper.getSemanticKeys(oMetaModel, sEntitySetName)) {
627
+ return false;
628
+ }
629
+
630
+ // Then check the entity is draft-enabled
631
+ return ModelHelper.isDraftSupported(oMetaModel, sPath);
632
+ }
633
+
634
+ _getPathFromContext(oContext: any, mParameters: any) {
635
+ let sPath;
636
+
637
+ if (oContext.isA("sap.ui.model.odata.v4.ODataListBinding") && oContext.isRelative()) {
638
+ sPath = oContext.getHeaderContext().getPath();
639
+ } else {
640
+ sPath = oContext.getPath();
641
+ }
642
+
643
+ if (mParameters.updateFCLLevel === -1) {
644
+ // When navigating back from a context, we need to remove the last component of the path
645
+ sPath = this._findPathToNavigate(sPath);
646
+
647
+ // Check if we're navigating back to a semantic path that was previously resolved
648
+ if (this.oLastSemanticMapping && this.oLastSemanticMapping.technicalPath === sPath) {
649
+ sPath = this.oLastSemanticMapping.semanticPath;
650
+ }
651
+ } else if (this._checkContextForSemanticPath(oContext)) {
652
+ // We check if we have to use a semantic path
653
+ const sSemanticPath = SemanticKeyHelper.getSemanticPath(oContext, true);
654
+
655
+ if (!sSemanticPath) {
656
+ // We were not able to build the semantic path --> Use the technical path and
657
+ // clear the previous mapping, otherwise the old mapping is used in EditFlow#updateDocument
658
+ // and it leads to unwanted page reloads
659
+ this.setLastSemanticMapping(undefined);
660
+ } else if (sSemanticPath !== sPath) {
661
+ // Store the mapping technical <-> semantic path to avoid recalculating it later
662
+ // and use the semantic path instead of the technical one
663
+ this.setLastSemanticMapping({ technicalPath: sPath, semanticPath: sSemanticPath });
664
+ sPath = sSemanticPath;
665
+ }
666
+ }
667
+
668
+ // remove extra '/' at the beginning of path
669
+ if (sPath[0] === "/") {
670
+ sPath = sPath.substring(1);
671
+ }
672
+
673
+ return sPath;
674
+ }
675
+
676
+ _calculateLayout(sPath: any, mParameters: any) {
677
+ let FCLLevel = mParameters.FCLLevel;
678
+ if (mParameters.updateFCLLevel) {
679
+ FCLLevel += mParameters.updateFCLLevel;
680
+ if (FCLLevel < 0) {
681
+ FCLLevel = 0;
682
+ }
683
+ }
684
+
685
+ return this.oAppComponent.getRootViewController().calculateLayout(FCLLevel, sPath, mParameters.sLayout);
686
+ }
687
+
688
+ /**
689
+ * Event handler before a route is matched.
690
+ * Displays a busy indicator.
691
+ *
692
+ */
693
+ _beforeRouteMatched(/*oEvent: Event*/) {
694
+ const bPlaceholderEnabled = new Placeholder().isPlaceholderEnabled();
695
+ if (!bPlaceholderEnabled) {
696
+ const oRootView = this.oAppComponent.getRootControl();
697
+ BusyLocker.lock(oRootView);
698
+ }
699
+ }
700
+
701
+ /**
702
+ * Event handler when a route is matched.
703
+ * Hides the busy indicator and fires its own 'routematched' event.
704
+ *
705
+ * @param {object} oEvent The event
706
+ */
707
+ _onRouteMatched(oEvent: Event) {
708
+ const oAppStateHandler = this.oAppComponent.getAppStateHandler(),
709
+ oRootView = this.oAppComponent.getRootControl();
710
+ const bPlaceholderEnabled = new Placeholder().isPlaceholderEnabled();
711
+ if (BusyLocker.isLocked(oRootView) && !bPlaceholderEnabled) {
712
+ BusyLocker.unlock(oRootView);
713
+ }
714
+ const mParameters: any = oEvent.getParameters();
715
+ if (this.navigationInfoQueue.length) {
716
+ mParameters.navigationInfo = this.navigationInfoQueue[0];
717
+ this.navigationInfoQueue = this.navigationInfoQueue.slice(1);
718
+ } else {
719
+ mParameters.navigationInfo = {};
720
+ }
721
+ if (oAppStateHandler.checkIfRouteChangedByIApp()) {
722
+ mParameters.navigationInfo.bReasonIsIappState = true;
723
+ oAppStateHandler.resetRouteChangedByIApp();
724
+ }
725
+
726
+ this.sCurrentRouteName = oEvent.getParameter("name");
727
+ this.sCurrentRoutePattern = mParameters.config.pattern;
728
+ this.aCurrentViews = oEvent.getParameter("views");
729
+
730
+ mParameters.routeInformation = this._getRouteInformation(this.sCurrentRouteName);
731
+ mParameters.routePattern = this.sCurrentRoutePattern;
732
+
733
+ this._fireRouteMatchEvents(mParameters);
734
+
735
+ // Check if current hash has been set by the routerProxy.navToHash function
736
+ // If not, rebuild history properly (both in the browser and the RouterProxy)
737
+ if (!history.state || history.state.feLevel === undefined) {
738
+ this.oRouterProxy
739
+ .restoreHistory()
740
+ .then(() => {
741
+ this.oRouterProxy.resolveRouteMatch();
742
+ })
743
+ .catch(function(oError: any) {
744
+ Log.error("Error while restoring history", oError);
745
+ });
746
+ } else {
747
+ this.oRouterProxy.resolveRouteMatch();
748
+ }
749
+ }
750
+
751
+ attachRouteMatched(oData: any, fnFunction: any, oListener: any) {
752
+ this.eventProvider.attachEvent("routeMatched", oData, fnFunction, oListener);
753
+ }
754
+ detachRouteMatched(fnFunction: any, oListener: any) {
755
+ this.eventProvider.detachEvent("routeMatched", fnFunction, oListener);
756
+ }
757
+ attachAfterRouteMatched(oData: any, fnFunction: any, oListener: any) {
758
+ this.eventProvider.attachEvent("afterRouteMatched", oData, fnFunction, oListener);
759
+ }
760
+ detachAfterRouteMatched(fnFunction: any, oListener: any) {
761
+ this.eventProvider.detachEvent("afterRouteMatched", fnFunction, oListener);
762
+ }
763
+
764
+ getRouteFromHash(oRouter: any, oAppComponent: any) {
765
+ const sHash = oRouter.getHashChanger().hash;
766
+ const oRouteInfo = oRouter.getRouteInfoByHash(sHash);
767
+ return oAppComponent
768
+ .getMetadata()
769
+ .getManifestEntry("/sap.ui5/routing/routes")
770
+ .filter(function(oRoute: any) {
771
+ return oRoute.name === oRouteInfo.name;
772
+ })[0];
773
+ }
774
+ getTargetsFromRoute(oRoute: any) {
775
+ const oTarget = oRoute.target;
776
+ if (typeof oTarget === "string") {
777
+ return [this._mTargets[oTarget]];
778
+ } else {
779
+ const aTarget: any[] = [];
780
+ oTarget.forEach((sTarget: any) => {
781
+ aTarget.push(this._mTargets[sTarget]);
782
+ });
783
+ return aTarget;
784
+ }
785
+ }
786
+
787
+ initializeRouting() {
788
+ // Attach router handlers
789
+ this._fnOnRouteMatched = this._onRouteMatched.bind(this);
790
+ this.oRouter.attachRouteMatched(this._fnOnRouteMatched, this);
791
+ const bPlaceholderEnabled = new Placeholder().isPlaceholderEnabled();
792
+ if (!bPlaceholderEnabled) {
793
+ this.oRouter.attachBeforeRouteMatched(this._beforeRouteMatched.bind(this));
794
+ }
795
+ // Reset internal state
796
+ this.navigationInfoQueue = [];
797
+ EditState.resetEditState();
798
+ this.bExitOnNavigateBackToRoot = !this.oRouter.match("");
799
+
800
+ const bIsIappState =
801
+ this.oRouter
802
+ .getHashChanger()
803
+ .getHash()
804
+ .indexOf("sap-iapp-state") !== -1;
805
+ const initPromise = this.oAppComponent
806
+ .getStartupParameters()
807
+ .then((oStartupParameters: any) => {
808
+ const bHasStartUpParameters = oStartupParameters !== undefined && Object.keys(oStartupParameters).length !== 0;
809
+ const sHash = this.oRouter.getHashChanger().getHash();
810
+ // Manage startup parameters (in case of no iapp-state)
811
+ if (!bIsIappState && bHasStartUpParameters && !sHash) {
812
+ if (oStartupParameters.preferredMode && oStartupParameters.preferredMode[0].indexOf("create") !== -1) {
813
+ // Create mode
814
+ return this._manageCreateStartup(oStartupParameters);
815
+ } else {
816
+ // Deep link
817
+ return this._manageDeepLinkStartup(oStartupParameters);
818
+ }
819
+ }
820
+ })
821
+ .catch(function(oError: any) {
822
+ Log.error("Error during routing initialization", oError);
823
+ });
824
+
825
+ return initPromise;
826
+ }
827
+
828
+ getDefaultCreateHash(oStartupParameters: any) {
829
+ return AppStartupHelper.getDefaultCreateHash(oStartupParameters, this.getContextPath(), this.oRouter);
830
+ }
831
+
832
+ _manageCreateStartup(oStartupParameters: any) {
833
+ return AppStartupHelper.getCreateStartupHash(oStartupParameters, this.getContextPath(), this.oRouter, this.oMetaModel).then(
834
+ (sNewHash: any) => {
835
+ if (sNewHash) {
836
+ (this.oRouter.getHashChanger().replaceHash as any)(sNewHash);
837
+ this.bExitOnNavigateBackToRoot = true;
838
+ }
839
+ }
840
+ );
841
+ }
842
+
843
+ _manageDeepLinkStartup(oStartupParameters: any) {
844
+ return AppStartupHelper.getDeepLinkStartupHash(
845
+ this.oAppComponent.getManifest()["sap.ui5"].routing,
846
+ oStartupParameters,
847
+ this.oModel
848
+ ).then((oDeepLink: any) => {
849
+ let sHash;
850
+ if (oDeepLink.context) {
851
+ const sTechnicalPath = oDeepLink.context.getPath(),
852
+ sSemanticPath = SemanticKeyHelper.getSemanticPath(oDeepLink.context);
853
+
854
+ if (sSemanticPath !== sTechnicalPath) {
855
+ // Store the mapping technical <-> semantic path to avoid recalculating it later
856
+ // and use the semantic path instead of the technical one
857
+ this.setLastSemanticMapping({ technicalPath: sTechnicalPath, semanticPath: sSemanticPath });
858
+ }
859
+
860
+ sHash = sSemanticPath.substring(1); // To remove the leading '/'
861
+ } else if (oDeepLink.hash) {
862
+ sHash = oDeepLink.hash;
863
+ }
864
+
865
+ if (sHash) {
866
+ //Replace the hash with newly created hash
867
+ (this.oRouter.getHashChanger().replaceHash as any)(sHash);
868
+ }
869
+ });
870
+ }
871
+
872
+ getOutbounds() {
873
+ return this.outbounds;
874
+ }
875
+
876
+ /**
877
+ * Gets the name of the Draft root entity set or the sticky-enabled entity set.
878
+ *
879
+ * @returns {string} The name of the root EntitySet
880
+ *
881
+ * @ui5-restricted
882
+ */
883
+ getContextPath() {
884
+ return this.sContextPath;
885
+ }
886
+ getInterface(): any {
887
+ return this;
888
+ }
889
+ }
890
+
891
+ class RoutingServiceFactory extends ServiceFactory<RoutingServiceSettings> {
892
+ createInstance(oServiceContext: ServiceContext<RoutingServiceSettings>) {
893
+ const oRoutingService = new RoutingService(oServiceContext);
894
+ return oRoutingService.initPromise;
895
+ }
896
+ }
897
+
898
+ export default RoutingServiceFactory;