@sapui5/sap.ushell 1.119.2 → 1.120.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) hide show
  1. package/package.json +1 -1
  2. package/src/main/js/sap/ushell/.library +3 -3
  3. package/src/main/js/sap/ushell/Container.js +272 -41
  4. package/src/main/js/sap/ushell/Fiori20Adapter.js +1 -1
  5. package/src/main/js/sap/ushell/Fiori20AdapterTest.js +3 -2
  6. package/src/main/js/sap/ushell/NWBCInterface.js +1 -1
  7. package/src/main/js/sap/ushell/SessionHandler.js +11 -6
  8. package/src/main/js/sap/ushell/TechnicalParameters.js +1 -1
  9. package/src/main/js/sap/ushell/URLTemplateProcessor.js +1 -1
  10. package/src/main/js/sap/ushell/Ui5NativeServiceFactory.js +1 -1
  11. package/src/main/js/sap/ushell/Ui5ServiceFactory.js +1 -1
  12. package/src/main/js/sap/ushell/User.js +26 -35
  13. package/src/main/js/sap/ushell/_ApplicationType/urlTemplateResolution.js +1 -1
  14. package/src/main/js/sap/ushell/_URLTemplateProcessor/DefinitionParameterSetBuilder.js +1 -1
  15. package/src/main/js/sap/ushell/_URLTemplateProcessor/DependencyGraph.js +1 -1
  16. package/src/main/js/sap/ushell/_URLTemplateProcessor/Functions.js +1 -1
  17. package/src/main/js/sap/ushell/_URLTemplateProcessor/Resolvers.js +1 -1
  18. package/src/main/js/sap/ushell/_URLTemplateProcessor/TemplateParameterParser.js +1 -1
  19. package/src/main/js/sap/ushell/_URLTemplateProcessor/utils.js +1 -1
  20. package/src/main/js/sap/ushell/adapters/cdm/ClientSideTargetResolutionAdapter.js +1 -1
  21. package/src/main/js/sap/ushell/adapters/cdm/CommonDataModelAdapter.js +15 -11
  22. package/src/main/js/sap/ushell/adapters/cdm/ConfigurationDefaultsAdapter.js +1 -1
  23. package/src/main/js/sap/ushell/adapters/cdm/ContainerAdapter.js +1 -1
  24. package/src/main/js/sap/ushell/adapters/cdm/LaunchPageAdapter.js +1 -1
  25. package/src/main/js/sap/ushell/adapters/cdm/PagesCommonDataModelAdapter.js +16 -11
  26. package/src/main/js/sap/ushell/adapters/cdm/PersonalizationAdapter.js +1 -1
  27. package/src/main/js/sap/ushell/adapters/cdm/PersonalizationV2Adapter.js +18 -0
  28. package/src/main/js/sap/ushell/adapters/cdm/Ui5ComponentLoaderAdapter.js +1 -1
  29. package/src/main/js/sap/ushell/adapters/cdm/_LaunchPage/modifyHome.js +1 -1
  30. package/src/main/js/sap/ushell/adapters/cdm/_LaunchPage/readCatalogs.js +1 -1
  31. package/src/main/js/sap/ushell/adapters/cdm/_LaunchPage/readHome.js +1 -1
  32. package/src/main/js/sap/ushell/adapters/cdm/_LaunchPage/uri.transform.js +1 -1
  33. package/src/main/js/sap/ushell/adapters/cdm/util/AppForInbound.js +1 -1
  34. package/src/main/js/sap/ushell/adapters/cdm/util/cdmSiteUtils.js +1 -1
  35. package/src/main/js/sap/ushell/adapters/cdm/v3/AdapterBase.js +1 -1
  36. package/src/main/js/sap/ushell/adapters/cdm/v3/LaunchPageAdapter.js +1 -1
  37. package/src/main/js/sap/ushell/adapters/cdm/v3/StaticGroupsAdapter.js +1 -1
  38. package/src/main/js/sap/ushell/adapters/cdm/v3/_LaunchPage/readApplications.js +1 -1
  39. package/src/main/js/sap/ushell/adapters/cdm/v3/_LaunchPage/readCatalogs.js +1 -1
  40. package/src/main/js/sap/ushell/adapters/cdm/v3/_LaunchPage/readHome.js +1 -1
  41. package/src/main/js/sap/ushell/adapters/cdm/v3/_LaunchPage/readPages.js +1 -1
  42. package/src/main/js/sap/ushell/adapters/cdm/v3/_LaunchPage/readUtils.js +1 -1
  43. package/src/main/js/sap/ushell/adapters/cdm/v3/_LaunchPage/readVisualizations.js +1 -1
  44. package/src/main/js/sap/ushell/adapters/cep/NavTargetResolutionAdapter.js +1 -1
  45. package/src/main/js/sap/ushell/adapters/cep/SearchCEPAdapter.js +1 -1
  46. package/src/main/js/sap/ushell/adapters/cflp/UserDefaultParameterPersistenceAdapter.js +1 -1
  47. package/src/main/js/sap/ushell/adapters/local/AdapterContainer.js +1 -1
  48. package/src/main/js/sap/ushell/adapters/local/AppStateAdapter.js +24 -18
  49. package/src/main/js/sap/ushell/adapters/local/ClientSideTargetResolutionAdapter.js +1 -1
  50. package/src/main/js/sap/ushell/adapters/local/ContainerAdapter.js +1 -1
  51. package/src/main/js/sap/ushell/adapters/local/LaunchPageAdapter.js +1 -1
  52. package/src/main/js/sap/ushell/adapters/local/MenuAdapter.js +4 -7
  53. package/src/main/js/sap/ushell/adapters/local/NavTargetResolutionAdapter.js +17 -22
  54. package/src/main/js/sap/ushell/adapters/local/PageBuildingAdapter.js +1 -1
  55. package/src/main/js/sap/ushell/adapters/local/PagePersistenceAdapter.js +1 -1
  56. package/src/main/js/sap/ushell/adapters/local/PersonalizationAdapter.js +1 -1
  57. package/src/main/js/sap/ushell/adapters/local/PersonalizationV2Adapter.js +19 -0
  58. package/src/main/js/sap/ushell/adapters/local/SearchAdapter.js +1 -1
  59. package/src/main/js/sap/ushell/adapters/local/SearchCEPAdapter.js +1 -1
  60. package/src/main/js/sap/ushell/adapters/local/SupportTicketAdapter.js +1 -1
  61. package/src/main/js/sap/ushell/adapters/local/Ui5ComponentLoaderAdapter.js +1 -1
  62. package/src/main/js/sap/ushell/adapters/local/UserDefaultParameterPersistenceAdapter.js +11 -7
  63. package/src/main/js/sap/ushell/adapters/local/UserInfoAdapter.js +8 -8
  64. package/src/main/js/sap/ushell/api/RTA.js +47 -0
  65. package/src/main/js/sap/ushell/appRuntime/neo/AppInfoService.js +154 -0
  66. package/src/main/js/sap/ushell/appRuntime/neo/AppRuntimeNeo.js +20 -0
  67. package/src/main/js/sap/ushell/appRuntime/neo/ProxyAppUtils.js +71 -0
  68. package/src/main/js/sap/ushell/appRuntime/ui5/AppRuntime.js +12 -5
  69. package/src/main/js/sap/ushell/appRuntime/ui5/AppRuntimeContext.js +2 -2
  70. package/src/main/js/sap/ushell/appRuntime/ui5/services/adapters/Ui5ComponentLoaderAdapter.js +1 -1
  71. package/src/main/js/sap/ushell/bootstrap/SchedulingAgent.js +2 -2
  72. package/src/main/js/sap/ushell/bootstrap/_SchedulingAgent/logger.js +1 -1
  73. package/src/main/js/sap/ushell/bootstrap/_SchedulingAgent/state.js +1 -1
  74. package/src/main/js/sap/ushell/bootstrap/cdm/cdm-def-dev.js +6 -3
  75. package/src/main/js/sap/ushell/bootstrap/common/common.configure.ui5datetimeformat.js +1 -1
  76. package/src/main/js/sap/ushell/bootstrap/common/common.configure.ushell.js +3 -0
  77. package/src/main/js/sap/ushell/bootstrap/common/common.load.launchpad.js +1 -1
  78. package/src/main/js/sap/ushell/bootstrap/common/common.util.js +34 -2
  79. package/src/main/js/sap/ushell/bootstrap/sandbox2.js +7 -2
  80. package/src/main/js/sap/ushell/components/ComponentKeysHandler.js +1 -1
  81. package/src/main/js/sap/ushell/components/HomepageManager.js +5 -5
  82. package/src/main/js/sap/ushell/components/SharedComponentUtils.js +7 -10
  83. package/src/main/js/sap/ushell/components/_HeaderManager/ShellHeader.controller.js +3 -3
  84. package/src/main/js/sap/ushell/components/_HomepageManager/DashboardLoadingManager.js +5 -0
  85. package/src/main/js/sap/ushell/components/_HomepageManager/PagingManager.js +5 -0
  86. package/src/main/js/sap/ushell/components/_HomepageManager/PersistentPageOperationAdapter.js +5 -0
  87. package/src/main/js/sap/ushell/components/_HomepageManager/TransientPageOperationAdapter.js +5 -0
  88. package/src/main/js/sap/ushell/components/appfinder/AppFinderView.js +1 -1
  89. package/src/main/js/sap/ushell/components/appfinder/CatalogView.js +1 -1
  90. package/src/main/js/sap/ushell/components/appfinder/EasyAccess.controller.js +3 -3
  91. package/src/main/js/sap/ushell/components/appfinder/EasyAccessView.js +0 -8
  92. package/src/main/js/sap/ushell/components/appfinder/HierarchyApps.controller.js +4 -4
  93. package/src/main/js/sap/ushell/components/appfinder/HierarchyFolders.controller.js +18 -16
  94. package/src/main/js/sap/ushell/components/appfinder/manifest.json +2 -2
  95. package/src/main/js/sap/ushell/components/applicationIntegration/AppLifeCycle.js +2 -2
  96. package/src/main/js/sap/ushell/components/applicationIntegration/application/Application.js +1 -1
  97. package/src/main/js/sap/ushell/components/applicationIntegration/application/BlueBoxHandler.js +1 -1
  98. package/src/main/js/sap/ushell/components/applicationIntegration/application/PostMessageAPI.js +2 -2
  99. package/src/main/js/sap/ushell/components/applicationIntegration/configuration/AppMeta.js +1 -1
  100. package/src/main/js/sap/ushell/components/applicationIntegration/elements/model.js +1 -1
  101. package/src/main/js/sap/ushell/components/applicationIntegration/relatedServices/RelatedServices.js +1 -1
  102. package/src/main/js/sap/ushell/components/applicationIntegration/relatedShellElements/RelatedShellElements.js +1 -1
  103. package/src/main/js/sap/ushell/components/cards/Card.controller.js +2 -2
  104. package/src/main/js/sap/ushell/components/container/ApplicationContainer.js +92 -71
  105. package/src/main/js/sap/ushell/components/contentFinder/Component.js +1 -1
  106. package/src/main/js/sap/ushell/components/contentFinder/controller/AppSearch.controller.js +1 -1
  107. package/src/main/js/sap/ushell/components/contentFinder/controller/ContentFinderDialog.controller.js +1 -1
  108. package/src/main/js/sap/ushell/components/contentFinder/controller/WidgetGallery.controller.js +1 -1
  109. package/src/main/js/sap/ushell/components/contentFinder/manifest.json +1 -1
  110. package/src/main/js/sap/ushell/components/contentFinder/model/formatter.js +4 -4
  111. package/src/main/js/sap/ushell/components/factsheet/Component.js +1 -1
  112. package/src/main/js/sap/ushell/components/homeApp/error/manifest.json +1 -1
  113. package/src/main/js/sap/ushell/components/homepage/ActionMode.js +1 -1
  114. package/src/main/js/sap/ushell/components/homepage/DashboardContent.view.js +1 -1
  115. package/src/main/js/sap/ushell/components/homepage/DashboardGroupsBox.js +1 -1
  116. package/src/main/js/sap/ushell/components/homepage/DashboardUIActions.js +1 -1
  117. package/src/main/js/sap/ushell/components/homepage/manifest.json +2 -2
  118. package/src/main/js/sap/ushell/components/pages/ActionMode.js +5 -5
  119. package/src/main/js/sap/ushell/components/pages/Component.js +1 -1
  120. package/src/main/js/sap/ushell/components/pages/MyHomeImport.js +1 -1
  121. package/src/main/js/sap/ushell/components/pages/StateManager.js +37 -24
  122. package/src/main/js/sap/ushell/components/pages/controller/ImportDialog.controller.js +1 -1
  123. package/src/main/js/sap/ushell/components/pages/controller/PageRuntime.controller.js +1 -1
  124. package/src/main/js/sap/ushell/components/pages/manifest.json +2 -2
  125. package/src/main/js/sap/ushell/components/runtimeSwitcher/controller/RuntimeSwitcher.controller.js +7 -4
  126. package/src/main/js/sap/ushell/components/runtimeSwitcher/manifest.json +1 -1
  127. package/src/main/js/sap/ushell/components/shell/MenuBar/controller/MenuBar.controller.js +6 -7
  128. package/src/main/js/sap/ushell/components/shell/MenuBar/manifest.json +2 -2
  129. package/src/main/js/sap/ushell/components/shell/NavigationBarMenu/controller/NavigationBarMenu.controller.js +39 -13
  130. package/src/main/js/sap/ushell/components/shell/NavigationBarMenu/manifest.json +3 -3
  131. package/src/main/js/sap/ushell/components/shell/Notifications/Component.js +5 -5
  132. package/src/main/js/sap/ushell/components/shell/Notifications/Notifications.controller.js +25 -45
  133. package/src/main/js/sap/ushell/components/shell/PostLoadingHeaderEnhancement/Component.js +2 -2
  134. package/src/main/js/sap/ushell/components/shell/ProductSwitch/Component.js +1 -1
  135. package/src/main/js/sap/ushell/components/shell/Search/ESearch.js +1 -1
  136. package/src/main/js/sap/ushell/components/shell/Search/manifest.json +2 -2
  137. package/src/main/js/sap/ushell/components/shell/SearchCEP/Component.js +2 -2
  138. package/src/main/js/sap/ushell/components/shell/SearchCEP/SearchCEP.controller.js +3 -3
  139. package/src/main/js/sap/ushell/components/shell/SearchCEP/SearchProviders/FrequentActivityProvider.js +1 -1
  140. package/src/main/js/sap/ushell/components/shell/SearchCEP/SearchProviders/RecentSearchProvider.js +1 -1
  141. package/src/main/js/sap/ushell/components/shell/SearchCEP/SearchProviders/SearchProvider.js +1 -1
  142. package/src/main/js/sap/ushell/components/shell/SearchCEP/SearchProviders/SearchServiceProvider.js +1 -1
  143. package/src/main/js/sap/ushell/components/shell/Settings/Component.js +4 -4
  144. package/src/main/js/sap/ushell/components/shell/Settings/ProfilingLoader.js +4 -0
  145. package/src/main/js/sap/ushell/components/shell/Settings/UserSettings.controller.js +4 -0
  146. package/src/main/js/sap/ushell/components/shell/Settings/UserSettings.view.xml +4 -3
  147. package/src/main/js/sap/ushell/components/shell/Settings/homepage/HomepageEntry.js +10 -13
  148. package/src/main/js/sap/ushell/components/shell/Settings/notifications/NotificationsSetting.controller.js +13 -22
  149. package/src/main/js/sap/ushell/components/shell/Settings/userAccount/UserAccountSelector.controller.js +2 -2
  150. package/src/main/js/sap/ushell/components/shell/Settings/userActivities/UserActivitiesSetting.controller.js +11 -14
  151. package/src/main/js/sap/ushell/components/shell/Settings/userDefaults/manifest.json +1 -1
  152. package/src/main/js/sap/ushell/components/shell/UserActionsMenu/Component.js +1 -1
  153. package/src/main/js/sap/ushell/components/shell/UserActionsMenu/UserActionsMenu.controller.js +2 -2
  154. package/src/main/js/sap/ushell/components/shell/UserImage/Component.js +2 -2
  155. package/src/main/js/sap/ushell/components/tiles/indicatorTileUtils/smartBusinessUtil.js +4 -6
  156. package/src/main/js/sap/ushell/components/visualizationOrganizer/Component.js +14 -18
  157. package/src/main/js/sap/ushell/components/workPageBuilder/Component.js +1 -1
  158. package/src/main/js/sap/ushell/components/workPageBuilder/controller/WorkPageBuilder.accessibility.js +1 -1
  159. package/src/main/js/sap/ushell/components/workPageBuilder/controller/WorkPageBuilder.controller.js +16 -12
  160. package/src/main/js/sap/ushell/components/workPageBuilder/controller/WorkPageHost.js +1 -1
  161. package/src/main/js/sap/ushell/components/workPageBuilder/controls/WorkPage.js +1 -1
  162. package/src/main/js/sap/ushell/components/workPageBuilder/controls/WorkPageButton.js +1 -1
  163. package/src/main/js/sap/ushell/components/workPageBuilder/controls/WorkPageCell.js +1 -1
  164. package/src/main/js/sap/ushell/components/workPageBuilder/controls/WorkPageColumn.js +1 -1
  165. package/src/main/js/sap/ushell/components/workPageBuilder/controls/WorkPageColumnResizer.js +1 -1
  166. package/src/main/js/sap/ushell/components/workPageBuilder/controls/WorkPageRow.js +1 -1
  167. package/src/main/js/sap/ushell/components/workPageBuilder/manifest.json +1 -1
  168. package/src/main/js/sap/ushell/components/workPageBuilder/view/WorkPageBuilder.view.xml +3 -3
  169. package/src/main/js/sap/ushell/components/workPageRuntime/Component.js +1 -1
  170. package/src/main/js/sap/ushell/components/workPageRuntime/controller/WorkPageRuntime.controller.js +1 -2
  171. package/src/main/js/sap/ushell/components/workPageRuntime/manifest.json +1 -1
  172. package/src/main/js/sap/ushell/components/workPageRuntime/services/NavigationResolver.js +1 -1
  173. package/src/main/js/sap/ushell/components/workPageRuntime/services/WorkPage.js +1 -1
  174. package/src/main/js/sap/ushell/designtime/ApplicationContainer.designtime.js +1 -1
  175. package/src/main/js/sap/ushell/library.js +1 -1
  176. package/src/main/js/sap/ushell/navigationMode.js +1 -1
  177. package/src/main/js/sap/ushell/performance/ShellAnalytics.js +1 -1
  178. package/src/main/js/sap/ushell/performance/StatisticalRecord.js +1 -1
  179. package/src/main/js/sap/ushell/plugins/appwarmup/Component.js +1 -1
  180. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/AccessKeysHandler.js +1 -1
  181. package/src/main/js/sap/ushell/renderer/History.js +84 -0
  182. package/src/main/js/sap/ushell/renderer/LogonFrameProvider.js +96 -0
  183. package/src/main/js/sap/ushell/renderer/Renderer.js +2851 -0
  184. package/src/main/js/sap/ushell/renderer/Shell.controller.js +3155 -0
  185. package/src/main/js/sap/ushell/{renderers/fiori2/ShellAsync.view.js → renderer/Shell.view.js} +29 -18
  186. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/ShellLayout.js +3 -3
  187. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/allMyApps/AllMyApps.controller.js +4 -4
  188. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/allMyApps/AllMyApps.view.xml +1 -1
  189. package/src/main/js/sap/ushell/renderer/allMyApps/AllMyAppsKeyboardHandler.js +108 -0
  190. package/src/main/js/sap/ushell/renderer/allMyApps/AllMyAppsManager.js +453 -0
  191. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources.properties +4 -0
  192. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/search/searchComponent/Component.js +3 -3
  193. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/search/searchComponent/controller/CEPSearchApp.controller.js +1 -1
  194. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/search/searchComponent/controller/SearchApp.controller.js +1 -1
  195. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/search/searchComponent/manifest.json +2 -2
  196. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/search/searchComponent/view/CEPSearchApp.view.js +2 -2
  197. package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/search/searchComponent/view/SearchApp.view.js +2 -2
  198. package/src/main/js/sap/ushell/renderers/fiori2/History.js +7 -68
  199. package/src/main/js/sap/ushell/renderers/fiori2/LogonFrameProvider.js +9 -79
  200. package/src/main/js/sap/ushell/renderers/fiori2/Renderer.js +101 -1882
  201. package/src/main/js/sap/ushell/renderers/fiori2/RendererExtensions.js +2 -3
  202. package/src/main/js/sap/ushell/renderers/fiori2/Shell.controller.js +8 -3130
  203. package/src/main/js/sap/ushell/renderers/fiori2/Shell.view.js +7 -7
  204. package/src/main/js/sap/ushell/renderers/fiori2/allMyApps/AllMyAppsKeyboardHandler.js +7 -92
  205. package/src/main/js/sap/ushell/renderers/fiori2/allMyApps/AllMyAppsManager.js +8 -438
  206. package/src/main/js/sap/ushell/resources.js +1 -1
  207. package/src/main/js/sap/ushell/services/AllMyApps.js +16 -7
  208. package/src/main/js/sap/ushell/services/AppConfiguration.js +12 -0
  209. package/src/main/js/sap/ushell/services/AppLifeCycle.js +11 -14
  210. package/src/main/js/sap/ushell/services/AppState.js +12 -8
  211. package/src/main/js/sap/ushell/services/Bookmark.js +15 -13
  212. package/src/main/js/sap/ushell/services/BookmarkV2.js +919 -0
  213. package/src/main/js/sap/ushell/services/ClientSideTargetResolution.js +229 -241
  214. package/src/main/js/sap/ushell/services/CommonDataModel.js +14 -4
  215. package/src/main/js/sap/ushell/services/Configuration.js +12 -11
  216. package/src/main/js/sap/ushell/services/ConfigurationDefaults.js +13 -10
  217. package/src/main/js/sap/ushell/services/Container.js +1 -1
  218. package/src/main/js/sap/ushell/services/ContentExtensionAdapterFactory.js +1 -1
  219. package/src/main/js/sap/ushell/services/CrossApplicationNavigation.js +8 -6
  220. package/src/main/js/sap/ushell/services/DarkModeSupport.js +16 -10
  221. package/src/main/js/sap/ushell/services/EndUserFeedback.js +1 -1
  222. package/src/main/js/sap/ushell/services/Extension/FloatingContainer.js +103 -0
  223. package/src/main/js/sap/ushell/services/Extension/Footer.js +61 -0
  224. package/src/main/js/sap/ushell/services/Extension/Header.js +193 -0
  225. package/src/main/js/sap/ushell/services/Extension/Item.js +195 -0
  226. package/src/main/js/sap/ushell/services/Extension/SidePane.js +155 -0
  227. package/src/main/js/sap/ushell/services/Extension/ToolArea.js +152 -0
  228. package/src/main/js/sap/ushell/services/Extension.js +381 -0
  229. package/src/main/js/sap/ushell/services/LaunchPage.js +1 -1
  230. package/src/main/js/sap/ushell/services/Menu.js +48 -59
  231. package/src/main/js/sap/ushell/services/Message.js +15 -14
  232. package/src/main/js/sap/ushell/services/MessageBroker.js +12 -12
  233. package/src/main/js/sap/ushell/services/NavTargetResolution.js +48 -70
  234. package/src/main/js/sap/ushell/services/Navigation/utils.js +565 -0
  235. package/src/main/js/sap/ushell/services/Navigation.js +152 -139
  236. package/src/main/js/sap/ushell/services/NavigationDataProvider.js +11 -8
  237. package/src/main/js/sap/ushell/services/Notifications.js +118 -1523
  238. package/src/main/js/sap/ushell/services/NotificationsV2.js +1872 -0
  239. package/src/main/js/sap/ushell/services/PageBuilding.js +11 -8
  240. package/src/main/js/sap/ushell/services/PagePersistence.js +11 -8
  241. package/src/main/js/sap/ushell/services/PageReferencing.js +11 -8
  242. package/src/main/js/sap/ushell/services/Pages.js +11 -15
  243. package/src/main/js/sap/ushell/services/Personalization.js +5 -3
  244. package/src/main/js/sap/ushell/services/PersonalizationV2/ContextContainer.js +410 -0
  245. package/src/main/js/sap/ushell/services/PersonalizationV2/Personalizer.js +109 -0
  246. package/src/main/js/sap/ushell/services/PersonalizationV2/TransientPersonalizer.js +57 -0
  247. package/src/main/js/sap/ushell/services/PersonalizationV2/Variant.js +180 -0
  248. package/src/main/js/sap/ushell/services/PersonalizationV2/VariantSet.js +296 -0
  249. package/src/main/js/sap/ushell/services/PersonalizationV2/VariantSetAdapter.js +125 -0
  250. package/src/main/js/sap/ushell/services/PersonalizationV2/WindowAdapter.js +71 -0
  251. package/src/main/js/sap/ushell/services/PersonalizationV2/WindowAdapterContainer.js +172 -0
  252. package/src/main/js/sap/ushell/services/PersonalizationV2/constants.js +23 -0
  253. package/src/main/js/sap/ushell/services/PersonalizationV2/constants.private.js +18 -0
  254. package/src/main/js/sap/ushell/services/PersonalizationV2/utils.js +233 -0
  255. package/src/main/js/sap/ushell/services/PersonalizationV2.js +641 -0
  256. package/src/main/js/sap/ushell/services/PluginManager.js +47 -55
  257. package/src/main/js/sap/ushell/services/ReferenceResolver.js +61 -54
  258. package/src/main/js/sap/ushell/services/Search.js +15 -2
  259. package/src/main/js/sap/ushell/services/SearchCEP.js +40 -44
  260. package/src/main/js/sap/ushell/services/SearchableContent.js +9 -9
  261. package/src/main/js/sap/ushell/services/ShellNavigation.js +14 -13
  262. package/src/main/js/sap/ushell/services/SmartNavigation.js +1 -1
  263. package/src/main/js/sap/ushell/services/SpaceContent.js +9 -10
  264. package/src/main/js/sap/ushell/services/SupportTicket.js +14 -14
  265. package/src/main/js/sap/ushell/services/UITracer.js +31 -35
  266. package/src/main/js/sap/ushell/services/URLParsing.js +9 -11
  267. package/src/main/js/sap/ushell/services/URLShortening.js +1 -1
  268. package/src/main/js/sap/ushell/services/URLTemplate.js +14 -13
  269. package/src/main/js/sap/ushell/services/Ui5ComponentLoader.js +29 -26
  270. package/src/main/js/sap/ushell/services/UsageAnalytics.js +6 -0
  271. package/src/main/js/sap/ushell/services/UserDefaultParameterPersistence.js +11 -7
  272. package/src/main/js/sap/ushell/services/UserDefaultParameters.js +20 -26
  273. package/src/main/js/sap/ushell/services/UserInfo.js +44 -23
  274. package/src/main/js/sap/ushell/services/UserRecents.js +13 -5
  275. package/src/main/js/sap/ushell/services/VisualizationDataProvider.js +12 -9
  276. package/src/main/js/sap/ushell/services/VisualizationInstantiation.js +10 -8
  277. package/src/main/js/sap/ushell/services/_ClientSideTargetResolution/Formatter.js +1 -1
  278. package/src/main/js/sap/ushell/services/_ClientSideTargetResolution/InboundIndex.js +1 -1
  279. package/src/main/js/sap/ushell/services/_ClientSideTargetResolution/InboundProvider.js +1 -1
  280. package/src/main/js/sap/ushell/services/_ClientSideTargetResolution/PrelaunchOperations.js +1 -1
  281. package/src/main/js/sap/ushell/services/_ClientSideTargetResolution/Search.js +1 -1
  282. package/src/main/js/sap/ushell/services/_ClientSideTargetResolution/StagedLogger.js +1 -1
  283. package/src/main/js/sap/ushell/services/_ClientSideTargetResolution/SystemContext.js +1 -1
  284. package/src/main/js/sap/ushell/services/_ClientSideTargetResolution/Utils.js +3 -4
  285. package/src/main/js/sap/ushell/services/_ClientSideTargetResolution/VirtualInbounds.js +4 -4
  286. package/src/main/js/sap/ushell/services/_ClientSideTargetResolution/XAppStateProcessing.js +1 -1
  287. package/src/main/js/sap/ushell/services/_CommonDataModel/SiteConverter.js +1 -1
  288. package/src/main/js/sap/ushell/services/_ContentExtensionAdapterFactory/ContentExtensionAdapterConfig.js +1 -1
  289. package/src/main/js/sap/ushell/services/_ContentExtensionAdapterFactory/FeaturedGroupConfig.js +1 -1
  290. package/src/main/js/sap/ushell/services/_CrossApplicationNavigation/utils.js +2 -1
  291. package/src/main/js/sap/ushell/services/_MessageBroker/MessageBrokerEngine.js +1 -1
  292. package/src/main/js/sap/ushell/services/_PageReferencing/PageReferencer.js +1 -1
  293. package/src/main/js/sap/ushell/services/_Personalization/ContextContainer.js +6 -0
  294. package/src/main/js/sap/ushell/services/_Personalization/PersonalizationContainer.js +5 -1
  295. package/src/main/js/sap/ushell/services/_Personalization/PersonalizationContainerVariant.js +13 -9
  296. package/src/main/js/sap/ushell/services/_Personalization/PersonalizationContainerVariantSet.js +17 -13
  297. package/src/main/js/sap/ushell/services/_Personalization/Personalizer.js +5 -0
  298. package/src/main/js/sap/ushell/services/_Personalization/TransientPersonalizer.js +5 -1
  299. package/src/main/js/sap/ushell/services/_Personalization/Variant.js +4 -0
  300. package/src/main/js/sap/ushell/services/_Personalization/VariantSet.js +17 -9
  301. package/src/main/js/sap/ushell/services/_Personalization/VariantSetAdapter.js +4 -6
  302. package/src/main/js/sap/ushell/services/_Personalization/WindowAdapter.js +5 -44
  303. package/src/main/js/sap/ushell/services/_Personalization/WindowAdapterContainer.js +5 -101
  304. package/src/main/js/sap/ushell/services/_Personalization/constants.js +3 -0
  305. package/src/main/js/sap/ushell/services/_Personalization/constants.private.js +3 -0
  306. package/src/main/js/sap/ushell/services/_Personalization/utils.js +3 -0
  307. package/src/main/js/sap/ushell/services/_PluginManager/Extensions.js +1 -1
  308. package/src/main/js/sap/ushell/services/_PluginManager/HeaderExtensions.js +1 -1
  309. package/src/main/js/sap/ushell/services/_PluginManager/MenuExtensions.js +1 -1
  310. package/src/main/js/sap/ushell/services/_Ui5ComponentLoader/utils.js +29 -5
  311. package/src/main/js/sap/ushell/services/_UserRecents/UserRecentsBase.js +19 -27
  312. package/src/main/js/sap/ushell/services/_VisualizationInstantiation/VizInstance.js +4 -0
  313. package/src/main/js/sap/ushell/services/_VisualizationInstantiation/VizInstanceAbap.js +3 -0
  314. package/src/main/js/sap/ushell/services/_VisualizationInstantiation/VizInstanceLink.js +1 -1
  315. package/src/main/js/sap/ushell/services/personalization/VariantSetAdapter.js +13 -15
  316. package/src/main/js/sap/ushell/support/plugins/flpConfig/FlpConfigurationPlugin.js +1 -1
  317. package/src/main/js/sap/ushell/themes/sap_horizon/ShellAppTitle.less +4 -4
  318. package/src/main/js/sap/ushell/themes/sap_horizon_dark/ShellAppTitle.less +4 -4
  319. package/src/main/js/sap/ushell/themes/sap_horizon_hcb/ShellAppTitle.less +2 -2
  320. package/src/main/js/sap/ushell/themes/sap_horizon_hcw/ShellAppTitle.less +2 -2
  321. package/src/main/js/sap/ushell/ui/AppContainer.js +2 -2
  322. package/src/main/js/sap/ushell/ui/ContentNodeSelector.js +2 -2
  323. package/src/main/js/sap/ushell/ui/ShellHeader.js +2 -2
  324. package/src/main/js/sap/ushell/ui/cards/RecentActivitiesExtension.js +1 -1
  325. package/src/main/js/sap/ushell/ui/footerbar/AddBookmarkButton.js +8 -12
  326. package/src/main/js/sap/ushell/ui/launchpad/ExtendedChangeDetection.js +1 -1
  327. package/src/main/js/sap/ushell/ui/launchpad/Page.js +1 -1
  328. package/src/main/js/sap/ushell/ui/launchpad/Section.js +21 -8
  329. package/src/main/js/sap/ushell/ui/launchpad/TileState.js +0 -1
  330. package/src/main/js/sap/ushell/ui/launchpad/section/CompactArea.js +1 -1
  331. package/src/main/js/sap/ushell/ui/shell/ShellAppTitle.js +1 -1
  332. package/src/main/js/sap/ushell/ui/shell/ShellHeadItem.js +1 -1
  333. package/src/main/js/sap/ushell/ui/shell/SysInfoBar.js +1 -1
  334. package/src/main/js/sap/ushell/ui/shell/ToolAreaItem.js +1 -1
  335. package/src/main/js/sap/ushell/ui/utils.js +1 -1
  336. package/src/main/js/sap/ushell/ui5service/ShellUIService.js +1 -1
  337. package/src/main/js/sap/ushell/ui5service/UserStatus.js +1 -1
  338. package/src/main/js/sap/ushell/ui5service/_ShellUIService/shelluiservice.class.factory.js +14 -26
  339. package/src/main/js/sap/ushell/utils/Deferred.js +88 -0
  340. package/src/main/js/sap/ushell/utils/UrlShortening.js +1 -1
  341. package/src/main/js/sap/ushell/utils/WindowUtils.js +8 -8
  342. package/src/main/js/sap/ushell/utils.js +31 -67
  343. package/src/test/js/sap/ushell/bootstrap/sandbox.js +7 -2
  344. package/ui5.yaml +8 -2
  345. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/HeadEndItemsOverflowPopover.fragment.xml +0 -0
  346. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_ar.properties +0 -0
  347. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_bg.properties +0 -0
  348. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_ca.properties +0 -0
  349. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_cs.properties +0 -0
  350. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_cy.properties +0 -0
  351. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_da.properties +0 -0
  352. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_de.properties +0 -0
  353. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_el.properties +0 -0
  354. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_en.properties +0 -0
  355. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_en_GB.properties +0 -0
  356. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_en_US_sappsd.properties +0 -0
  357. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_en_US_saprigi.properties +0 -0
  358. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_en_US_saptrc.properties +0 -0
  359. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_es.properties +0 -0
  360. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_es_MX.properties +0 -0
  361. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_et.properties +0 -0
  362. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_fi.properties +0 -0
  363. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_fr.properties +0 -0
  364. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_fr_CA.properties +0 -0
  365. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_hi.properties +0 -0
  366. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_hr.properties +0 -0
  367. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_hu.properties +0 -0
  368. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_id.properties +0 -0
  369. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_it.properties +0 -0
  370. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_iw.properties +0 -0
  371. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_ja.properties +0 -0
  372. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_kk.properties +0 -0
  373. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_ko.properties +0 -0
  374. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_lt.properties +0 -0
  375. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_lv.properties +0 -0
  376. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_ms.properties +0 -0
  377. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_nl.properties +0 -0
  378. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_no.properties +0 -0
  379. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_pl.properties +0 -0
  380. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_pt.properties +0 -0
  381. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_pt_PT.properties +0 -0
  382. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_ro.properties +0 -0
  383. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_ru.properties +0 -0
  384. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_sh.properties +0 -0
  385. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_sk.properties +0 -0
  386. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_sl.properties +0 -0
  387. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_sv.properties +0 -0
  388. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_th.properties +0 -0
  389. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_tr.properties +0 -0
  390. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_uk.properties +0 -0
  391. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_vi.properties +0 -0
  392. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_zh_CN.properties +0 -0
  393. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/resources/resources_zh_TW.properties +0 -0
  394. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/search/util.js +0 -0
  395. /package/src/main/js/sap/ushell/{renderers/fiori2 → renderer}/utils.js +0 -0
@@ -0,0 +1,3155 @@
1
+ // Copyright (c) 2009-2023 SAP SE, All Rights Reserved
2
+
3
+ /**
4
+ * @fileOverview Shell Controller
5
+ */
6
+ sap.ui.define([
7
+ "./History",
8
+ "sap/base/Log",
9
+ "sap/base/util/extend",
10
+ "sap/base/util/isPlainObject",
11
+ "sap/m/InstanceManager",
12
+ "sap/ui/core/Component",
13
+ "sap/ui/core/Configuration",
14
+ "sap/ui/core/Core",
15
+ "sap/ui/core/library",
16
+ "sap/ui/core/mvc/Controller",
17
+ "sap/ui/core/routing/HashChanger",
18
+ "sap/ui/core/routing/History",
19
+ "sap/ui/Device",
20
+ "sap/ui/model/json/JSONModel",
21
+ "sap/ui/performance/trace/Interaction",
22
+ "sap/ui/performance/Measurement",
23
+ "sap/ui/thirdparty/hasher",
24
+ "sap/ui/thirdparty/jquery",
25
+ "sap/ui/util/Storage",
26
+ "sap/ushell/bootstrap/SchedulingAgent",
27
+ "sap/ushell/components/_HeaderManager/ControlManager",
28
+ "sap/ushell/components/applicationIntegration/AppLifeCycle",
29
+ "sap/ushell/components/applicationIntegration/relatedServices/RelatedServices",
30
+ "sap/ushell/components/HeaderManager",
31
+ "sap/ushell/Config",
32
+ "sap/ushell/EventHub",
33
+ "sap/ushell/library",
34
+ "sap/ushell/performance/FesrEnhancer",
35
+ "sap/ushell/renderer/LogonFrameProvider",
36
+ "sap/ushell/resources",
37
+ "sap/ushell/services/AppConfiguration",
38
+ "sap/ushell/UserActivityLog",
39
+ "sap/ushell/utils",
40
+ "sap/ushell/utils/WindowUtils",
41
+ "sap/ushell/renderer/ShellLayout",
42
+ "sap/ushell/renderer/utils",
43
+ "sap/base/util/ObjectPath"
44
+ ], function (
45
+ History,
46
+ Log,
47
+ extend,
48
+ isPlainObject,
49
+ InstanceManager,
50
+ Component,
51
+ Configuration,
52
+ Core,
53
+ library,
54
+ Controller,
55
+ HashChanger,
56
+ Ui5History,
57
+ Device,
58
+ JSONModel,
59
+ Interaction,
60
+ Measurement,
61
+ hasher,
62
+ jQuery,
63
+ Storage,
64
+ SchedulingAgent,
65
+ HeaderControlManager,
66
+ AppLifeCycleAI,
67
+ RelatedServices,
68
+ HeaderManager,
69
+ Config,
70
+ EventHub,
71
+ ushellLibrary,
72
+ FesrEnhancer,
73
+ LogonFrameProvider,
74
+ ushellResources,
75
+ AppConfiguration,
76
+ UserActivityLog,
77
+ ushellUtils,
78
+ WindowUtils,
79
+ ShellLayout,
80
+ RendererUtils,
81
+ ObjectPath
82
+ ) {
83
+ "use strict";
84
+
85
+ var ShellLayoutMapping = ShellLayout.LAYOUT_MAPPING;
86
+
87
+ // shortcut for sap.ui.core.routing.HistoryDirection
88
+ var Ui5HistoryDirection = library.routing.HistoryDirection;
89
+
90
+ // shortcut for sap.ushell.AppType
91
+ var AppType = ushellLibrary.AppType;
92
+
93
+ /* don't delay these cause they are needed for direct bookmarks */
94
+
95
+ // create global model and add some demo data
96
+ var closeAllDialogs = true;
97
+ var bPreviousPageDirty = false;
98
+ var oShellModel;
99
+ var ShellModel = AppLifeCycleAI.getElementsModel();
100
+ var oModel;
101
+ var oEPCMNavigationMode = {
102
+ embedded: 0,
103
+ newWindowThenEmbedded: 1,
104
+ newWindow: 1,
105
+ replace: 0
106
+ };
107
+ var oNavigationMode = {
108
+ embedded: "embedded",
109
+ newWindowThenEmbedded: "newWindowThenEmbedded",
110
+ newWindow: "newWindow",
111
+ replace: "replace"
112
+ };
113
+ var oConfig = {};
114
+
115
+ // track performance marks and enhance UI5's Frontend Sub Records with FLP specific information
116
+ // Note: This can not yet be done using the SchedulingAgent as plain modules cannot be loaded and
117
+ // making it a UI5 component would be overhead (performance).
118
+ FesrEnhancer.init();
119
+
120
+ const sModuleName = "Shell.controller";
121
+ const sLogFesrPrefix = "[FesrFlp]";
122
+
123
+ /**
124
+ * @name sap.ushell.renderer.Shell
125
+ * @extends sap.ui.core.mvc.Controller
126
+ * @public
127
+ */
128
+ return Controller.extend("sap.ushell.renderer.Shell", {
129
+ _aDoables: [],
130
+
131
+ addDoable: function (oDoable) {
132
+ this._aDoables.push(oDoable);
133
+ },
134
+
135
+ _aDanglingControls: [],
136
+
137
+ /**
138
+ * @param {sap.ui.core.Control} oControl The Control
139
+ * @see sap.ushell.renderer.Shell.controller#onExit
140
+ *
141
+ * @private
142
+ * @since 1.115.0
143
+ */
144
+ addDanglingControl: function (oControl) {
145
+ this._aDanglingControls.push(oControl);
146
+ },
147
+
148
+ _aDanglingBindings: [],
149
+
150
+ /**
151
+ * Adds a binding which is destroyed on Renderer destroy
152
+ * @param {sap.ui.model.Binding} oBinding The Binding
153
+ * @see sap.ushell.renderer.Shell.controller#onExit
154
+ *
155
+ * @private
156
+ * @since 1.115.0
157
+ */
158
+ addDanglingBinding: function (oBinding) {
159
+ this._aDanglingBindings.push(oBinding);
160
+ },
161
+
162
+ /**
163
+ * @param {sap.ui.model.Binding} oTargetBinding The Binding
164
+ * @see sap.ushell.renderer.Shell.controller#onExit
165
+ *
166
+ * @private
167
+ * @since 1.115.0
168
+ */
169
+ removeDanglingBinding: function (oTargetBinding) {
170
+ var iBindingIndex = this._aDanglingBindings.findIndex(function (oBinding) {
171
+ return oBinding === oTargetBinding;
172
+ });
173
+
174
+ if (iBindingIndex > -1) {
175
+ this._aDanglingBindings.splice(iBindingIndex, 1);
176
+ }
177
+ },
178
+
179
+ _aPendingInitializations: [],
180
+
181
+ /**
182
+ * Adds a promise to the list of initializations
183
+ * Only used in test environments!
184
+ * @param {Promise} oPromise A Promise
185
+ * @returns {Promise} Returns the provided promise to allow chaining
186
+ *
187
+ * @private
188
+ * @since 1.115.0
189
+ */
190
+ addPendingInitialization: function (oPromise) {
191
+ this._aPendingInitializations.push(oPromise);
192
+ return oPromise;
193
+ },
194
+
195
+ /**
196
+ * Only used in test environments!
197
+ * @returns {Promise} Resolves once all initializations have settled
198
+ *
199
+ * @private
200
+ * @since 1.115.0
201
+ */
202
+ awaitPendingInitializations: function () {
203
+ var iOldLength = this._aPendingInitializations.length;
204
+ return Promise.allSettled(this._aPendingInitializations)
205
+ .then(function () {
206
+ var iNewLength = this._aPendingInitializations.length;
207
+ if (iOldLength < iNewLength) {
208
+ return this.awaitPendingInitializations();
209
+ }
210
+ }.bind(this));
211
+ },
212
+
213
+ onComponentTargetDisplay: function (oEvent) {
214
+ var oParameters = oEvent.getParameters();
215
+ var oContainer = oParameters.control;
216
+ var oComponentContainer = oParameters.object;
217
+
218
+ if (this.bRestorePreviousHash) {
219
+ this.bRestorePreviousHash = false;
220
+ }
221
+
222
+ oContainer.navTo("centerViewPort", oComponentContainer.getId(), "show");
223
+ },
224
+
225
+ onInit: function () {
226
+ var oRouter = Component.getOwnerComponentFor(this.getView()).getRouter();
227
+
228
+ // TODO We still need to think about implementing a custom router to move the display handler from
229
+ // this file to the custom router. Maybe use oRouter.attachBypassed?
230
+ oRouter.getTarget("home").attachDisplay(this.onComponentTargetDisplay, this);
231
+ oRouter.getTarget("appfinder").attachDisplay(this.onComponentTargetDisplay, this);
232
+ oRouter.getTarget("pages").attachDisplay(this.onComponentTargetDisplay, this);
233
+ oRouter.getTarget("workpages").attachDisplay(this.onComponentTargetDisplay, this);
234
+ oRouter.getTarget("runtimeSwitcher").attachDisplay(this.onComponentTargetDisplay, this);
235
+ oRouter.getTarget("wzsearch").attachDisplay(this.onComponentTargetDisplay, this);
236
+
237
+ if (Config.last("/core/homeApp/enabled")) {
238
+ oRouter.getTarget("homeapp").attachDisplay(this.onComponentTargetDisplay, this);
239
+ }
240
+
241
+ /*
242
+ * Assign hash changer directly because otherwise a default one is used.
243
+ *
244
+ * Default hash changers for UI5 routers assume &/ is the separator for a nested component route.
245
+ *
246
+ * We must re-assign this otherwise an hash like `#Shell-appfinder&/userMenu' ends up in `#Shell-appfinder&/'
247
+ * in the URL - &/userMenu part is eaten by the default hash changer.
248
+ */
249
+ oRouter.oHashChanger = HashChanger.getInstance();
250
+ oRouter.initialize(true /*tell the router not to parse the current browser hash, and wait for ShellNavigation.init*/);
251
+
252
+ this.bEnableHashChange = true;
253
+ closeAllDialogs = true;
254
+ var oView = this.getView();
255
+ var oViewData = oView.getViewData() || {};
256
+ var mediaQ = window.matchMedia("(min-width: 600px)");
257
+ var oViewConfig = oViewData.config || {};
258
+
259
+ var oApplicationModel = AppLifeCycleAI.shellElements().model();
260
+ HeaderManager.init(oViewConfig, oApplicationModel);
261
+
262
+ oView.setModel(oViewData.helperModel, "helper");
263
+ oView.setModel(oViewData.shellModel);
264
+ // Add global model to view
265
+ this.initShellModel(oViewConfig, oApplicationModel);
266
+
267
+ var handleMedia = function (mq) {
268
+ Config.emit("/core/shell/model/isPhoneWidth", !mq.matches);
269
+ };
270
+ if (mediaQ.addListener) { // Assure that mediaMatch is supported(Not supported on IE9).
271
+ mediaQ.addListener(handleMedia);
272
+ handleMedia(mediaQ);
273
+ }
274
+
275
+ // Bind the translation model to this view
276
+ oView.setModel(ushellResources.i18nModel, "i18n");
277
+
278
+ Core.getEventBus().subscribe("externalSearch", this.externalSearchTriggered, this);
279
+ Core.getEventBus().subscribe("sap.ushell", "appOpened", this.onAppOpened, this);
280
+ Core.getEventBus().subscribe("ESHSearchFinished", this._logSearchActivity, this);
281
+ // handling of configuration should be done before the code block below otherwise the doHashChange is
282
+ // triggered before the personalization flag is disabled (URL may contain hash value which invokes navigation)
283
+ this._setConfigurationToModel();
284
+
285
+ // Doable objects are kept in a global array to enable their off-ing later on.
286
+ this._registerAndCreateEventHubDoables();
287
+
288
+ oShellModel.addHeaderEndItem(["userActionsMenuHeaderButton"], false, ["home", "app", "minimal", "standalone", "embedded", "embedded-home", "lean"], true);
289
+
290
+ // Actions to add to custom states
291
+ var aActions = [];
292
+
293
+ if (oViewConfig && !oViewConfig.moveContactSupportActionToShellHeader) {
294
+ aActions.push("ContactSupportBtn");
295
+ }
296
+
297
+ this.history = new History();
298
+ this.oViewPortContainer = Core.byId("viewPortContainer");
299
+ AppLifeCycleAI.init(oViewConfig.appState, this.oViewPortContainer, oViewConfig.rootIntent, oViewConfig.disableHomeAppCache, {
300
+ ownerComponent: this.getOwnerComponent()
301
+ }, aActions, oViewConfig.cacheConfiguration);
302
+
303
+ // init Shell Navigation
304
+ var initShellNavigation = function (oShellNavigation, oAppLifeCycleService) {
305
+ this.oShellNavigation = oShellNavigation;
306
+ this.oShellNavigation.registerPrivateFilters(oAppLifeCycleService);
307
+
308
+ // register the router in the ShellNavigation to let it skip the split of hash before firing the hashChange event
309
+ this.oShellNavigation.registerExtraRouter(oRouter);
310
+ this.oShellNavigation.registerNavigationFilter(this._handleEmptyHash.bind(this));
311
+ // must be after event registration (for synchronous nav target resolver calls)
312
+ this.oShellNavigation.init(this.doHashChange.bind(this));
313
+
314
+ this.oShellNavigation.registerNavigationFilter(this._disableSourceAppRouter.bind(this));
315
+
316
+ this.oShellNavigation.registerNavigationFilter(this.handleDataLoss.bind(this));
317
+
318
+ AppLifeCycleAI.registerHandleHashChange(this.oShellNavigation);
319
+ // enable the direct app start and tests to wait for the initialization
320
+ EventHub.emit("ShellNavigationInitialized", Date.now());
321
+ }.bind(this);
322
+
323
+ Promise.all([
324
+ sap.ushell.Container.getServiceAsync("URLParsing"),
325
+ sap.ushell.Container.getServiceAsync("ShellNavigation"),
326
+ sap.ushell.Container.getServiceAsync("AppLifeCycle")
327
+ ])
328
+ .then(function (aServices) {
329
+ this.oURLParsing = aServices[0];
330
+ var oShellNavigation = aServices[1];
331
+ this._oAppLifeCycleService = aServices[2];
332
+
333
+ initShellNavigation(oShellNavigation, this._oAppLifeCycleService);
334
+ }.bind(this));
335
+
336
+ sap.ushell.Container.setLogonFrameProvider(this._getLogonFrameProvider());
337
+
338
+ if (Device.system.desktop) {
339
+ sap.ui.require(["sap/ushell/renderer/AccessKeysHandler"], function (AccessKeysHandler) {
340
+ AccessKeysHandler.init(oModel);
341
+ });
342
+ }
343
+
344
+ window.onbeforeunload = function () {
345
+ if (sap.ushell.Container && sap.ushell.Container.getDirtyFlag()
346
+ // workaround: skip data-loss-warning in IE when app is NWBC-based (as there is a separate implementation)
347
+ && !(Device.browser.name === Device.browser.BROWSER.INTERNET_EXPLORER
348
+ && AppLifeCycleAI.getCurrentApplication().container.getApplicationType() === "NWBC")
349
+ ) {
350
+ if (!ushellResources.browserI18n) {
351
+ ushellResources.browserI18n = ushellResources.getTranslationModel(window.navigator.language).getResourceBundle();
352
+ }
353
+ return ushellResources.browserI18n.getText("dataLossExternalMessage");
354
+ }
355
+
356
+ return undefined;
357
+ };
358
+
359
+ if (Config.last("/core/shell/model/contentDensity")) {
360
+ // do not call _applyContentDensity,
361
+ // no promise that the component-preload is fully loaded and _applyContentDensity loads the root application.
362
+ // we only want to display the shell in its default state, once the root application will be loaded
363
+ // then the _applyContentDensity will be called with promise that component-preload loaded.
364
+ AppLifeCycleAI.getAppMeta()._applyContentDensityClass();
365
+ }
366
+
367
+ this.initShellUIService();
368
+
369
+ if (!oViewConfig.disableSignOut && (oViewConfig.sessionTimeoutTileStopRefreshIntervalInMinutes > 0 || oViewConfig.sessionTimeoutReminderInMinutes > 0)) {
370
+ this._createSessionHandler(oViewConfig);
371
+ }
372
+
373
+ if (Device.system.desktop) {
374
+ sap.ui.require(["sap/ushell/renderer/AccessKeysHandler"], function (AccessKeysHandler) {
375
+ oView.waitForShellLayout().then(function () {
376
+ var oNavigationBar = document.getElementById(ShellLayoutMapping.NavigationBar);
377
+ oNavigationBar.addEventListener("focusin", function () {
378
+ // focus not in the shell
379
+ AccessKeysHandler.bFocusOnShell = false;
380
+ AccessKeysHandler.bFocusPassedToExternalHandlerFirstTime = false;
381
+ });
382
+ oNavigationBar.addEventListener("focusout", function () {
383
+ // focus in the shell
384
+ AccessKeysHandler.bFocusOnShell = true;
385
+ AccessKeysHandler.bFocusPassedToExternalHandlerFirstTime = true;
386
+ });
387
+ var oRootView = document.getElementById(ShellLayoutMapping.RendererRootView);
388
+ oRootView.addEventListener("focusin", function () {
389
+ // focus not in the shell
390
+ AccessKeysHandler.bFocusOnShell = false;
391
+ AccessKeysHandler.bFocusPassedToExternalHandlerFirstTime = false;
392
+ });
393
+ oRootView.addEventListener("focusout", function () {
394
+ // focus in the shell
395
+ AccessKeysHandler.bFocusOnShell = true;
396
+ AccessKeysHandler.bFocusPassedToExternalHandlerFirstTime = true;
397
+ });
398
+ });
399
+ });
400
+ }
401
+ },
402
+
403
+ shellUpdateAggItem: function (sId, oContext) {
404
+ return Core.byId(oContext.getObject());
405
+ },
406
+
407
+ getViewPortContainer: function () {
408
+ return Core.byId("viewPortContainer");
409
+ },
410
+
411
+ /**
412
+ * Creates the EventHub event bindings and saves them.
413
+ */
414
+ _registerAndCreateEventHubDoables: function () {
415
+ [
416
+ EventHub.once("CenterViewPointContentRendered").do(this._loadCoreExt.bind(this)),
417
+ EventHub.once("PagesRuntimeRendered").do(this._loadCoreExt.bind(this)),
418
+ EventHub.once("AppRendered").do(this._loadCoreExtNonUI5.bind(this)),
419
+ EventHub.on("toggleContentDensity").do(this.toggleContentDensity.bind(this)),
420
+ EventHub.on("ShellFloatingContainerUndockOnResize").do(this._handleFloatingContainerUndockOnResize.bind(this)),
421
+ EventHub.on("ShellFloatingContainerDockedIsResized").do(this._emitFloatingContainerResizeEvent.bind(this)),
422
+ EventHub.on("LaunchpadCustomRouterRouteMatched").do(this._centerViewPort.bind(this)),
423
+
424
+ EventHub.once("CoreResourcesComplementLoaded").do(this._onCoreResourcesComplementLoaded.bind(this)),
425
+ EventHub.once("loadRendererExtensions").do(this._loadRendererExtensionPlugins.bind(this)),
426
+ EventHub.once("loadUsageAnalytics").do(this._loadUsageAnalytics.bind(this)),
427
+ EventHub.once("loadWarmupPlugins").do(this._loadWarmupPlugins.bind(this)),
428
+ EventHub.once("loadTrackingActivitiesSetting").do(this._loadTrackingActivitiesSetting.bind(this)),
429
+ EventHub.on("centerViewPort").do(this._centerViewPort.bind(this))
430
+ ].forEach(function (oDoable) {
431
+ this.addDoable(oDoable);
432
+ }.bind(this));
433
+ },
434
+
435
+ initShellModel: function (oConfigForModel, oApplicationModel) {
436
+ oShellModel = ShellModel;
437
+ oShellModel.init(oConfigForModel, oApplicationModel);
438
+ oModel = this.getView().getViewData().shellModel;
439
+ Config.emit("/core/shell/model/personalization", Config.last("/core/shell/enablePersonalization"));
440
+ },
441
+
442
+ initShellUIService: function () {
443
+ AppLifeCycleAI.initShellUIService({
444
+ fnOnBackNavigationChange: this.onBackNavigationChange.bind(this)
445
+ });
446
+
447
+ if (oConfig.enableOnlineStatus) {
448
+ sap.ui.require(["sap/ushell/ui5service/UserStatus"], function (UserStatus) {
449
+ this.oUserStatus = new UserStatus({
450
+ scopeObject: this.getOwnerComponent(),
451
+ scopeType: "component"
452
+ });
453
+ }.bind(this));
454
+ }
455
+ },
456
+
457
+ /*
458
+ * This method change the back navigation handler with custom logic in the shell header when the ShellUIService#setBackNavigation method is called.
459
+ *
460
+ * This method currently assumes that the application is displayed in the "minimal" state (no home button present).
461
+ */
462
+ onBackNavigationChange: function (oEvent) {
463
+ AppLifeCycleAI.setBackNavigationChanged(true);
464
+ var fnCallback = oEvent.getParameters().data;
465
+ var oCurrentStateModel = Config.last("/core/shell/model/currentState");
466
+
467
+ if (fnCallback) {
468
+ AppLifeCycleAI.service().setNavigateBack(fnCallback);
469
+
470
+ if (oCurrentStateModel.stateName === "minimal" || oCurrentStateModel.stateName === "standalone" || oCurrentStateModel.stateName === "embedded") {
471
+ sap.ushell.Container.getRenderer("fiori2").showHeaderItem("backBtn", true);
472
+ }
473
+ } else {
474
+ //if no callback is provided we set the default handler: history back
475
+ AppLifeCycleAI.service().resetNavigateBack();
476
+ }
477
+ },
478
+
479
+ toggleContentDensity: function (oData) {
480
+ var isCompact = oData.contentDensity === "compact";
481
+ AppLifeCycleAI.getAppMeta()._applyContentDensityByPriority(isCompact, true);
482
+ },
483
+
484
+ _handleEmptyHash: function (sHash) {
485
+ sHash = (typeof sHash === "string") ? sHash : "";
486
+ sHash = sHash.split("?")[0];
487
+ if (sHash.length === 0) {
488
+ var oViewData = this.getView() ? this.getView().getViewData() : {};
489
+ oConfig = oViewData.config || {};
490
+ // Migration support: we have to set rootIntent empty
491
+ // And continue navigation in order to check if empty hash is resolved locally
492
+ if (oConfig.migrationConfig) {
493
+ return this.oShellNavigation.NavigationFilterStatus.Continue;
494
+ }
495
+ if (oConfig.rootIntent) {
496
+ window.setTimeout(function () {
497
+ window.hasher.setHash(oConfig.rootIntent);
498
+ }, 0);
499
+ return this.oShellNavigation.NavigationFilterStatus.Abandon;
500
+ }
501
+ }
502
+ return this.oShellNavigation.NavigationFilterStatus.Continue;
503
+ },
504
+
505
+ _setConfigurationToModel: function () {
506
+ var oViewData = this.getView().getViewData();
507
+
508
+ if (oViewData) {
509
+ oConfig = oViewData.config || {};
510
+ }
511
+ if (oConfig) {
512
+ if (oConfig.states) {
513
+ ShellModel.extendStates(oConfig.states);
514
+ HeaderManager.extendStates(oConfig.states);
515
+ }
516
+
517
+ if (oConfig.enableSetTheme !== undefined) {
518
+ oModel.setProperty("/setTheme", oConfig.enableSetTheme);
519
+ }
520
+
521
+ // Compact Cozy mode
522
+ oModel.setProperty("/contentDensity", oConfig.enableContentDensity === undefined ? true : oConfig.enableContentDensity);
523
+
524
+ // Check if the configuration is passed by html of older version(1.28 and lower)
525
+ if (oConfig.migrationConfig !== undefined) {
526
+ oModel.setProperty("/migrationConfig", oConfig.migrationConfig);
527
+ }
528
+ // User default parameters settings
529
+ if (oConfig.enableUserDefaultParameters !== undefined) {
530
+ oModel.setProperty("/userDefaultParameters", oConfig.enableUserDefaultParameters);
531
+ }
532
+
533
+ if (oConfig.disableHomeAppCache !== undefined) {
534
+ oModel.setProperty("/disableHomeAppCache", oConfig.disableHomeAppCache);
535
+ }
536
+ // xRay enablement configuration
537
+ oModel.setProperty("/enableHelp", Config.last("/core/extension/enableHelp"));
538
+ oModel.setProperty("/searchAvailable", (oConfig.enableSearch !== false));
539
+ }
540
+ },
541
+
542
+ _loadTrackingActivitiesSetting: function (eventData) {
543
+ // Tracking activities
544
+ this._getPersData({
545
+ container: "flp.settings.FlpSettings",
546
+ item: "userActivitesTracking"
547
+ }).then(function (enableTrackingActivity) {
548
+ if (enableTrackingActivity === undefined) {
549
+ enableTrackingActivity = true;
550
+ }
551
+ oModel.setProperty("/enableTrackingActivity", enableTrackingActivity);
552
+ EventHub.emit("StepDone", eventData.stepName);
553
+ }).catch(function (error) {
554
+ EventHub.emit("StepFailed", eventData.stepName);
555
+ Log.error(
556
+ "Failed to load tracking activities state from the personalization", error,
557
+ "sap.ushell.components.flp.settings.FlpSettings");
558
+ });
559
+ },
560
+
561
+ _getPreviousPageDirty: function () {
562
+ return bPreviousPageDirty;
563
+ },
564
+
565
+ _setPreviousPageDirty: function (bState) {
566
+ bPreviousPageDirty = bState;
567
+ },
568
+
569
+ getModelConfiguration: function () {
570
+ var oViewData = this.getView().getViewData();
571
+ var oShellConfig;
572
+
573
+ if (oViewData) {
574
+ var oConfiguration = oViewData.config || {};
575
+ oShellConfig = extend({}, oConfiguration);
576
+ }
577
+ delete oShellConfig.applications;
578
+ return oShellConfig;
579
+ },
580
+
581
+ /**
582
+ * This method will be used by the Container service in order to create, show and destroy a Dialog control with an inner iframe.
583
+ * The iframe will be used for rare scenarios in which additional authentication is required.
584
+ * This is mainly related to SAML 2.0 flows. The api sequence will be managed by UI2 services.
585
+ *
586
+ * @returns {{create: function, show: function, destroy: function}} Logon Frame Provider interface
587
+ * @private
588
+ */
589
+ _getLogonFrameProvider: function () {
590
+ return LogonFrameProvider;
591
+ },
592
+
593
+ onExit: function () {
594
+ this._aDoables.forEach(function (oDoable) {
595
+ oDoable.off();
596
+ });
597
+ this._aDoables = [];
598
+
599
+ this._aDanglingControls.forEach(function (oControl) {
600
+ oControl.destroy();
601
+ });
602
+ this._aDanglingControls = [];
603
+
604
+ this._aDanglingBindings.forEach(function (oBinding) {
605
+ oBinding.destroy();
606
+ });
607
+ this._aDanglingBindings = [];
608
+
609
+ Core.getEventBus().unsubscribe("externalSearch", this.externalSearchTriggered, this);
610
+ Core.getEventBus().unsubscribe("ESHSearchFinished", this._logSearchActivity, this);
611
+ Core.getEventBus().unsubscribe("sap.ushell", "appOpened", this.onAppOpened, this);
612
+
613
+ // Some qUnits destroy the shell very early, check if oShellNavigation exists
614
+ if (this.oShellNavigation) {
615
+ this.oShellNavigation.hashChanger.destroy();
616
+ }
617
+
618
+ HeaderManager.destroy();
619
+ HeaderControlManager.destroy();
620
+
621
+ UserActivityLog.deactivate();
622
+ oShellModel.destroy();
623
+ AppLifeCycleAI.shellElements().clean();
624
+ oShellModel = undefined;
625
+ },
626
+
627
+ /**
628
+ * @returns {object} the current router of the current application component
629
+ */
630
+ _getCurrentAppRouter: function () {
631
+ var oAppLifeCycle = this._oAppLifeCycleService;
632
+ var oCurrentApplication = oAppLifeCycle && oAppLifeCycle.getCurrentApplication && oAppLifeCycle.getCurrentApplication();
633
+ var oComponentInstance = oCurrentApplication && oCurrentApplication.componentInstance;
634
+
635
+ if (oComponentInstance) {
636
+ return oComponentInstance.getRouter();
637
+ }
638
+ return null;
639
+ },
640
+
641
+ /**
642
+ * If the navigation is not an inner app navigation, this function stops the router of the old application.
643
+ *
644
+ * @param {string} newHash new url hash
645
+ * @param {string} oldHash old url hash
646
+ *
647
+ * @returns {string} ShellNavigation.NavigationFilterStatus
648
+ */
649
+ _disableSourceAppRouter: function (newHash, oldHash) {
650
+ if (!this.bEnableHashChange || this.bRestorePreviousHash) {
651
+ return this.oShellNavigation.NavigationFilterStatus.Continue;
652
+ }
653
+
654
+ var bAppSpecificChange = this.oShellNavigation.hashChanger.isInnerAppNavigation(newHash, oldHash);
655
+ if (!bAppSpecificChange) {
656
+ var oCurrentAppRouter = this._getCurrentAppRouter();
657
+
658
+ if (oCurrentAppRouter && oCurrentAppRouter.isInitialized()) {
659
+ oCurrentAppRouter.stop();
660
+ }
661
+ }
662
+
663
+ return this.oShellNavigation.NavigationFilterStatus.Continue;
664
+ },
665
+
666
+ /**
667
+ * Makes sure that the router is not stopped after a failed / aborted navigation.
668
+ * We ignore the current hash when re-initializing the router because we are handling cases that restore the old state
669
+ * (nothing should change application side when the router is resumed).
670
+ */
671
+ _resumeAppRouterIgnoringCurrentHash: function () {
672
+ var oAppRouter = this._getCurrentAppRouter();
673
+
674
+ if (oAppRouter) {
675
+ oAppRouter.initialize(true /* bIgnoreInitialHash */);
676
+ }
677
+ },
678
+
679
+ /**
680
+ * Navigation Filter function registered with ShellNavigation service.
681
+ * Triggered on each navigation.
682
+ * Aborts navigation if there are unsaved data inside app(getDirtyFlag returns true).
683
+ * For non-IE browsers the dirtyState=true gets handled asynchronously by undoing the navigation and redoing the navigation.
684
+ * In case of an explace navigation there is no data loss popup.
685
+ *
686
+ * @param {string} targetIntent The new intent to navigate to.
687
+ * @param {string} currentIntent The current intent before navigation was triggered.
688
+ * @returns {string|object} A navigation filter status object or string.
689
+ * @private
690
+ */
691
+ handleDataLoss: function (targetIntent, currentIntent) {
692
+ var oShellNavigationHashChanger = this.oShellNavigation.hashChanger;
693
+ var bReloadApplication = oShellNavigationHashChanger.getReloadApplication();
694
+
695
+ if (this.bReloadApplication !== null && this.bReloadApplication !== undefined) {
696
+ oShellNavigationHashChanger.setReloadApplication(this.bReloadApplication);
697
+ this.bReloadApplication = null;
698
+ }
699
+
700
+ // We are navigating from empty hash to rootIntent
701
+ if (currentIntent === "" || hasher.disableBlueBoxHashChangeTrigger === true) {
702
+ return this.oShellNavigation.NavigationFilterStatus.Continue;
703
+ }
704
+
705
+ if (!this.bEnableHashChange && !this.bRedoNavigation) {
706
+ this.bEnableHashChange = true;
707
+
708
+ return this.oShellNavigation.NavigationFilterStatus.Custom;
709
+ }
710
+
711
+ if (this.bRestorePreviousHash) {
712
+ return this.oShellNavigation.NavigationFilterStatus.Continue;
713
+ }
714
+
715
+ // User confirmed the data loss and now we do the hash change again
716
+ if (this.bRedoNavigation) {
717
+ this.bRedoNavigation = false;
718
+ this.bEnableHashChange = true;
719
+ this.bExplaceNavigation = false;
720
+ return this.oShellNavigation.NavigationFilterStatus.Continue;
721
+ }
722
+
723
+ // Do we do an ex-place navigation?
724
+ var oParsedHash = this.oURLParsing.parseShellHash(targetIntent);
725
+ var bBuiltInIntent = sap.ushell.Container.getRenderer()._isBuiltInIntent(oParsedHash);
726
+ var aParsedNavMode = oParsedHash.params["sap-ushell-navmode"];
727
+ var sNavMode = aParsedNavMode && aParsedNavMode[0];
728
+
729
+ // Built-in intents must never be opened ex-place, therefore only continue with non built-in Intents
730
+ if (!bBuiltInIntent && (this.bExplaceNavigation || sNavMode === "explace" || sNavMode === "frameless")) {
731
+ // Yes, just continue the navigation without a dirtyFlag popup
732
+ this.bExplaceNavigation = false;
733
+ return this.oShellNavigation.NavigationFilterStatus.Continue;
734
+ }
735
+
736
+ // In case the async dirtyState provider is NOT set we only have to check the sync dirtyState.
737
+ // TODO: Simplify (Remove) the shortcut once the OPA test iframe issue is solved.
738
+ // The OPA iframe handler overwrites some hasher methods which results in a confused ushell navigation flow.
739
+ var bIsDirty = sap.ushell.Container.getDirtyFlag();
740
+ var bIsAsyncDirtyStateProviderSet = sap.ushell.Container.isAsyncDirtyStateProviderSet();
741
+ if (!bIsAsyncDirtyStateProviderSet && !bIsDirty) {
742
+ bPreviousPageDirty = bIsDirty;
743
+ return this.oShellNavigation.NavigationFilterStatus.Continue;
744
+ }
745
+
746
+ /*
747
+ The dirtyState is either true or has to be retrieved async.
748
+ After the hash changes we evaluate the dirtyState asynchronously
749
+ and redo the navigation in case it is false. In case it is true we do additional logic
750
+ _restoreHashUsingAction returns the NavigationFilterStatus Custom.
751
+ */
752
+ var bIsBackNavigation = Ui5History.getInstance().getHistoryStateOffset() < 0;
753
+ Promise.all([
754
+ this._waitForHash(currentIntent),
755
+ sap.ushell.Container.getServiceAsync("NavTargetResolution"),
756
+ sap.ushell.Container.getDirtyFlagsAsync()
757
+ ])
758
+ .then(function (aResults) {
759
+ var oService = aResults[1];
760
+ var bIsDirty = aResults[2];
761
+
762
+ /*
763
+ Update the internal dirtyFlag to match the current dirtyFlag
764
+ This is needed in case of a hashChangeFailure. When we navigate to a broken app the
765
+ controller does not ask for the dirtyState therefore we need to be able to restore it
766
+ */
767
+ bPreviousPageDirty = bIsDirty;
768
+
769
+ if (!bIsDirty) {
770
+ this.bRedoNavigation = true;
771
+ this._restoreHashUsingAction(targetIntent, "redo");
772
+ return;
773
+ }
774
+
775
+ // Built-in intents can not be resolved, so we need to check
776
+ // first if the user is trying to navigate to one of them.
777
+ if (bBuiltInIntent) {
778
+ // We assume built-in intents are always in-place, so we need user input
779
+ if (this._handleDirtyStateUserConfirm()) {
780
+ this.bRedoNavigation = true;
781
+ this._restoreHashUsingAction(targetIntent, "redo");
782
+ }
783
+ } else {
784
+ // resolveHashFragment expects a full hash, this filter always gets the target without '#'
785
+ var sTargetHash = "#" + targetIntent;
786
+ oService.resolveHashFragment(sTargetHash)
787
+ .then(function (oResolvedHashFragment) {
788
+ var bIsExplaceNavigation = oResolvedHashFragment.targetNavigationMode === "explace" || oResolvedHashFragment.targetNavigationMode === "frameless";
789
+ var bIsBackNavigationAndNewWindow = bIsBackNavigation && oResolvedHashFragment.navigationMode === oNavigationMode.newWindowThenEmbedded;
790
+
791
+ if (bIsExplaceNavigation && (!bIsBackNavigationAndNewWindow)) {
792
+ this.bExplaceNavigation = true;
793
+ this.bRedoNavigation = true;
794
+ this._restoreHashUsingAction(targetIntent, "redo");
795
+ } else if (this._handleDirtyStateUserConfirm()) {
796
+ this.bRedoNavigation = true;
797
+ this.bReloadApplication = bReloadApplication;
798
+ this._restoreHashUsingAction(targetIntent, "redo");
799
+ }
800
+ }.bind(this))
801
+ .catch(function () {
802
+ // If the resolution failed, redoing the navigation will trigger the
803
+ // error pop-up.
804
+ this.bRedoNavigation = true;
805
+ this._restoreHashUsingAction(targetIntent, "redo");
806
+ }.bind(this));
807
+ }
808
+ }.bind(this));
809
+
810
+ // Returns this.oShellNavigation.NavigationFilterStatus.Custom
811
+ return this._restoreHashUsingAction(currentIntent, "undo");
812
+ },
813
+
814
+ /**
815
+ * Wait for the given hash to be loaded by the browser.
816
+ *
817
+ * @param {string} hash The hash to wait for.
818
+ * @returns {Promise<undefined>} A promise that is resolved once the given hash was found. It is not rejected.
819
+ * @since 1.86.0
820
+ * @private
821
+ */
822
+ _waitForHash: function (hash) {
823
+ var sHash = "#" + hash;
824
+
825
+ return new Promise(function (resolve) {
826
+ var fnWaitChange = function () {
827
+ var bChanged = sHash === decodeURIComponent(window.location.hash);
828
+
829
+ if (bChanged) {
830
+ window.removeEventListener("hashchange", fnWaitChange);
831
+ resolve();
832
+ }
833
+ };
834
+
835
+ window.addEventListener("hashchange", fnWaitChange);
836
+ });
837
+ },
838
+
839
+ /**
840
+ * Shows a browser-popup for the user to confirm that they want to discard their changes.
841
+ * @returns {boolean} True if the popup has been confirmed by the user, otherwise false.
842
+ *
843
+ * @private
844
+ * @since 1.86.0
845
+ */
846
+ _handleDirtyStateUserConfirm: function () {
847
+ Log.debug(`${sLogFesrPrefix} Interaction Start`, "_handleDirtyStateUserConfirm", sModuleName);
848
+ const fnResolveInteraction = Interaction.notifyAsyncStep();
849
+
850
+ // eslint-disable-next-line no-alert
851
+ if (window.confirm(ushellResources.i18n.getText("dataLossInternalMessage"))) {
852
+ sap.ushell.Container.setDirtyFlag(false);
853
+ if (RelatedServices.isBackNavigation() === true) {
854
+ RelatedServices.resetBackNavigationFlag();
855
+ }
856
+ Log.debug(`${sLogFesrPrefix} Interaction End`, "_handleDirtyStateUserConfirm", sModuleName);
857
+ fnResolveInteraction();
858
+
859
+ bPreviousPageDirty = true;
860
+ return true;
861
+ }
862
+ Log.debug(`${sLogFesrPrefix} Interaction End`, "_handleDirtyStateUserConfirm", sModuleName);
863
+ fnResolveInteraction();
864
+
865
+ return false;
866
+ },
867
+
868
+ /**
869
+ * Undoes or redoes navigation. Restores the previous or next hash and the navigation history that lead to it.
870
+ *
871
+ * @param {string} sHash The old hash that should be reset.
872
+ * @param {string} sAction Whether a 'redo' or 'undo' action is to be performed.
873
+ * @returns {object} The navigation filter status.
874
+ * @since 1.86.0
875
+ * @private
876
+ */
877
+ _restoreHashUsingAction: function (sHash, sAction) {
878
+ var bWasHistoryEntryReplaced = this.oShellNavigation.wasHistoryEntryReplaced();
879
+ var oRestoreStrategy = this._getRestoreHashStrategy(bWasHistoryEntryReplaced, sAction);
880
+
881
+ this._resumeAppRouterIgnoringCurrentHash();
882
+
883
+ return this._restoreHash(oRestoreStrategy, sHash);
884
+ },
885
+
886
+ /**
887
+ * Decides the strategy to use when restoring an old hash in an attempt to undo a forward or a backward navigation.
888
+ * This method is mostly used for data loss handling, after a user confirmed that they want to stay in the current application.
889
+ *
890
+ * @param {boolean} bNavToReplacedHash Whether the last navigation had replaced the history without adding a new entry.
891
+ * @param {string} sAction The action to be carried out.
892
+ * @returns {object} The strategy to use to restore the previous shell hash which is one of:
893
+ * "historyBack", "replaceHash", "historyForward" and the number of steps to restore the hash.
894
+ */
895
+ _getRestoreHashStrategy: function (bNavToReplacedHash, sAction) {
896
+ /**
897
+ * The bNavToReplacedHash needs to be checked first because the iHistoryStateOffset is undefined when:
898
+ * * hash is replaced
899
+ * * new hash is given
900
+ * * in browser where history pushState isn't fully supported
901
+ */
902
+ if (bNavToReplacedHash) {
903
+ return {
904
+ strategy: "replaceHash",
905
+ stepCount: 0
906
+ };
907
+ }
908
+
909
+ // This is a workaround:
910
+ // Notifying the UI5 history via the hashChanged event triggers unwanted navigation.
911
+ // But without the notify the offset gets out of sync with the current state and stacks up.
912
+ // We are now assuming that we only do (and undo) single navigation steps
913
+ var sStrategy;
914
+ var iHistoryStateOffset = Ui5History.getInstance().getHistoryStateOffset();
915
+ if (iHistoryStateOffset === undefined || iHistoryStateOffset >= 0) {
916
+ // Forward
917
+ iHistoryStateOffset = 1;
918
+ } else if (iHistoryStateOffset < 0) {
919
+ // Backward
920
+ iHistoryStateOffset = -1;
921
+ }
922
+
923
+ // If the history offset exists, we use the absolute value because later we use dedicated functions like _windowHistoryBack
924
+ // and _windowHistoryForward for back- and forwards navigation, respectively.
925
+ // Calling either of those function with a negative value would yield the exact opposite.
926
+ // TODO: Actually, we could refactor this to call history.go directly, so we don't have to transform it.
927
+
928
+ if (sAction === "undo") {
929
+ if (iHistoryStateOffset < 0) {
930
+ // Went backwards
931
+ sStrategy = "historyForward";
932
+ } else {
933
+ // Went forwards
934
+ sStrategy = "historyBack";
935
+ }
936
+
937
+ this.sLastUndoStrategy = sStrategy;
938
+ } else {
939
+ switch (this.sLastUndoStrategy) {
940
+ case "historyBack":
941
+ sStrategy = "historyForward";
942
+ break;
943
+ case "historyForward":
944
+ sStrategy = "historyBack";
945
+ break;
946
+ default:
947
+ sStrategy = "replaceHash";
948
+ }
949
+ }
950
+
951
+ return {
952
+ strategy: sStrategy,
953
+ stepCount: 1
954
+ };
955
+ },
956
+
957
+ /**
958
+ * Restores the previous or next hash using the given restore-strategy.
959
+ * If the restore strategy is 'replaceHash', the given hash is used.
960
+ *
961
+ * @param {object} oRestoreStrategy A restore strategy containing a strategy such as 'historyBack', 'historyForward' or 'replaceHash'.
962
+ * @param {string} sHash A hash without hash sign to be set if the 'replaceHash' strategy is to be used.
963
+ * @returns {object} The Custom navigation filter status.
964
+ * @throws If the given restore strategy is not recognized.
965
+ * @private
966
+ */
967
+ _restoreHash: function (oRestoreStrategy, sHash) {
968
+ var oNavigationFilterStatus = {
969
+ status: this.oShellNavigation.NavigationFilterStatus.Custom,
970
+ hash: ""
971
+ };
972
+ switch (oRestoreStrategy.strategy) {
973
+ case "historyBack":
974
+ this.bEnableHashChange = false;
975
+ this._windowHistoryBack(oRestoreStrategy.stepCount);
976
+ break;
977
+ case "historyForward":
978
+ this.bEnableHashChange = false;
979
+ this._windowHistoryForward(oRestoreStrategy.stepCount);
980
+ break;
981
+ case "replaceHash":
982
+ this.bEnableHashChange = false;
983
+ hasher.replaceHash(sHash);
984
+ break;
985
+ default:
986
+ throw new Error("Cannot execute unknown navigation strategy");
987
+ }
988
+
989
+ return oNavigationFilterStatus;
990
+ },
991
+
992
+ /**
993
+ * Checks whether an application is cold started.
994
+ * This method is scoped to checking the cold start conditions of applications only.
995
+ *
996
+ * A cold start state occurs whenever the user has previously opened the window.
997
+ *
998
+ * - page is refreshed
999
+ * - URL is pasted in a new window
1000
+ * - user opens the page and pastes a URL
1001
+ *
1002
+ * @returns {boolean} whether the application is in a cold start state
1003
+ */
1004
+ _isColdStart: function () {
1005
+ var oRenderer = sap.ushell.Container.getRenderer("fiori2");
1006
+ var bNoCoreViewNavigated = !oRenderer || !oRenderer.getCurrentCoreView();
1007
+ if (this.history.getHistoryLength() <= 1 && bNoCoreViewNavigated) {
1008
+ return true;
1009
+ }
1010
+ this._isColdStart = function () {
1011
+ return false;
1012
+ };
1013
+ return false;
1014
+ },
1015
+
1016
+ _setEnableHashChange: function (bValue) {
1017
+ this.bEnableHashChange = bValue;
1018
+ },
1019
+
1020
+ /**
1021
+ * Triggers the app-usage mechanism to log an openApp action.
1022
+ *
1023
+ * @param {object} oRecentActivity An object containing details of a recently opened app
1024
+ * @returns {Promise} A promise that is resolved once the action is logged
1025
+ * @private
1026
+ */
1027
+ _logRecentActivity: function (oRecentActivity) {
1028
+ // In a direct app start the logging happens before the user setting is loaded
1029
+ if (!this.oInitialEnableTrackingPromise) {
1030
+ // The initial value was overwritten by now
1031
+ if (Config.last("/core/shell/model/enableTrackingActivity") !== undefined) {
1032
+ this.oInitialEnableTrackingPromise = Promise.resolve();
1033
+ } else {
1034
+ this.oInitialEnableTrackingPromise = new Promise(function (resolve, reject) {
1035
+ Config.once("/core/shell/model/enableTrackingActivity").do(resolve);
1036
+ });
1037
+ }
1038
+ }
1039
+
1040
+ return this.oInitialEnableTrackingPromise.then(function () {
1041
+ if (Config.last("/core/shell/model/enableTrackingActivity")) {
1042
+ return new Promise(function (resolve, reject) {
1043
+ AppConfiguration.addActivity(oRecentActivity)
1044
+ .done(resolve)
1045
+ .fail(reject);
1046
+ });
1047
+ }
1048
+ Log.warning("Tracking is not enabled", null, "sap.ushell.renderer.Shell.controller");
1049
+ return undefined;
1050
+ });
1051
+ },
1052
+
1053
+ /**
1054
+ * Sets application container based on information in URL hash.
1055
+ *
1056
+ * This is a callback registered with NavService. It's triggered whenever the url (or the hash fragment in the url) changes.
1057
+ *
1058
+ * NOTE: when this method is called, the new URL is already in the address bar of the browser.
1059
+ * Therefore back navigation is used to restore the URL in case of wrong navigation or errors.
1060
+ *
1061
+ * @param {string} sShellHash shell hash
1062
+ * @param {string} sAppPart application part
1063
+ * @param {string} sOldShellHash previous shell hash
1064
+ * @param {string} sOldAppPart previous application part
1065
+ * @param {object} oParseError parse error
1066
+ * @returns {Promise} promise
1067
+ * @public
1068
+ */
1069
+ doHashChange: function (sShellHash, sAppPart, sOldShellHash, sOldAppPart, oParseError) {
1070
+ Log.debug(`${sLogFesrPrefix} Interaction Start`, "doHashChange", sModuleName);
1071
+ const fnResolveInteraction = Interaction.notifyAsyncStep();
1072
+
1073
+ //Performance Debug
1074
+ Measurement.start("FLP:ShellController.doHashChange", "doHashChange", "FLP");
1075
+ ushellUtils.setPerformanceMark("FLP-ShellController-doHashChange-begin");
1076
+ EventHub.emit("trackHashChange", sShellHash);
1077
+
1078
+ var oDashboard = Core.byId("sapUshellDashboardPage");
1079
+ if (oDashboard) {
1080
+ oDashboard.setBlocked(true);
1081
+ oDashboard.setBusy(true);
1082
+ }
1083
+
1084
+ return this._doHashChange(this, sShellHash, sAppPart, sOldShellHash, sOldAppPart, oParseError)
1085
+ .then(function () {
1086
+ Measurement.end("FLP:ShellController.doHashChange");
1087
+
1088
+ Log.debug(`${sLogFesrPrefix} Interaction End`, "doHashChange", sModuleName);
1089
+ fnResolveInteraction();
1090
+ }, function (vError) {
1091
+ Measurement.end("FLP:ShellController.doHashChange");
1092
+ EventHub.emit("doHashChangeError", Date.now());
1093
+ if (oDashboard) {
1094
+ oDashboard.setBlocked(false);
1095
+ oDashboard.setBusy(false);
1096
+ }
1097
+
1098
+ Log.debug(`${sLogFesrPrefix} Interaction End`, "doHashChange", sModuleName);
1099
+ fnResolveInteraction();
1100
+ });
1101
+ },
1102
+
1103
+ _doHashChange: function (oShellController, sShellHash, sAppPart, sOldShellHash, sOldAppPart, oParseError) {
1104
+ /*
1105
+ * reset here because the result of wasHistoryEntryReplaced is only useful in navigation filters
1106
+ * and might give inconsistent results after this point.
1107
+ */
1108
+ this._wasHistoryEntryReplaced = this.oShellNavigation.wasHistoryEntryReplaced();
1109
+ this.oShellNavigation.resetHistoryEntryReplaced();
1110
+ var oInMemoryApplicationInstance;
1111
+
1112
+ if (!this.bEnableHashChange) {
1113
+ this.bEnableHashChange = true;
1114
+ return jQuery.when();
1115
+ }
1116
+ //When the application is opened in the new tab, the ShellHashEvent should be fired in order to update UI5 hasher,
1117
+ //but the application should not be resolved again.
1118
+ if (this.bRestorePreviousHash) {
1119
+ this.bRestorePreviousHash = false;
1120
+ return jQuery.when();
1121
+ }
1122
+
1123
+ if (oParseError) {
1124
+ oShellController.hashChangeFailure(
1125
+ oShellController.history.getHistoryLength(),
1126
+ oParseError.message,
1127
+ null,
1128
+ "sap.ushell.renderer.Shell.controller",
1129
+ false
1130
+ );
1131
+ return jQuery.when();
1132
+ }
1133
+
1134
+ var oDeferred = new jQuery.Deferred();
1135
+
1136
+ // save current history length to handle errors (in case)
1137
+ var iOriginalHistoryLength = oShellController.history.getHistoryLength();
1138
+
1139
+ var sFixedShellHash = oShellController.fixShellHash(sShellHash);
1140
+ var sOldFixedShellHash = oShellController.fixShellHash(sOldShellHash);
1141
+
1142
+ // track hash change
1143
+ oShellController.history.hashChange(sFixedShellHash, sOldShellHash);
1144
+
1145
+ // we save the current-application before resolving the next navigation's fragment,
1146
+ // as in cases of navigation in a new window we need to set it back for the app-configuration to be consistent
1147
+ oShellController.currentAppBeforeNav = AppConfiguration.getCurrentApplication();
1148
+
1149
+ oShellController._resolveHashFragment(sFixedShellHash)
1150
+ .then(function (oResolvedHashFragment, oParsedShellHash) {
1151
+ // NOTE: AppConfiguration.setCurrentApplication was called with the currently resolved target.
1152
+
1153
+ var sIntent = oParsedShellHash ? oParsedShellHash.semanticObject + "-" + oParsedShellHash.action : "";
1154
+ var oConfig = oShellController._getConfig();
1155
+ var bComponentLoaded = !!(oResolvedHashFragment && oResolvedHashFragment.componentHandle);
1156
+ // for SAPUI5 apps, the application type is still "URL" due to backwards compatibility, but the
1157
+ // NavTargetResolution service already extracts the component name, so this can directly be used as indicator
1158
+ var sTargetUi5ComponentName = oResolvedHashFragment && oResolvedHashFragment.ui5ComponentName;
1159
+
1160
+ // calculate effective Navigation Mode with resolution result and current Application,
1161
+ // we will determine the next navigation mode.
1162
+ oResolvedHashFragment = oShellController._calculateNavigationMode(oResolvedHashFragment);
1163
+
1164
+ oShellController._handleEarlyNavigation(sFixedShellHash, sAppPart, oResolvedHashFragment).then(function (bProcessed) {
1165
+ if (bProcessed !== true) {
1166
+ // add application config to the application properties
1167
+ if (oConfig && oConfig.applications && oConfig.applications[sIntent]) {
1168
+ oResolvedHashFragment.applicationConfiguration = oConfig.applications[sIntent];
1169
+ }
1170
+
1171
+ oInMemoryApplicationInstance = AppLifeCycleAI.getInMemoryInstance(sIntent, sFixedShellHash, sAppPart, sOldShellHash);
1172
+
1173
+ if (oInMemoryApplicationInstance.isInstanceSupported) {
1174
+ oShellController._initiateApplication(oResolvedHashFragment, sFixedShellHash, oParsedShellHash, iOriginalHistoryLength, sAppPart, sOldFixedShellHash, sOldAppPart);
1175
+ oDeferred.resolve();
1176
+ return;
1177
+ }
1178
+ if (bComponentLoaded || !sTargetUi5ComponentName) {
1179
+ var oDeferredAppDestroy = new jQuery.Deferred();
1180
+ if ((oResolvedHashFragment.applicationType === "URL" || ushellUtils.isSAPLegacyApplicationType(oResolvedHashFragment.applicationType))
1181
+ && oInMemoryApplicationInstance.isInstanceSupported === false && oInMemoryApplicationInstance.appId) {
1182
+ AppLifeCycleAI.destroyApplication(
1183
+ oInMemoryApplicationInstance.appId, oInMemoryApplicationInstance.container, oDeferredAppDestroy
1184
+ );
1185
+ } else {
1186
+ oDeferredAppDestroy.resolve();
1187
+ }
1188
+ oDeferredAppDestroy.done(function () {
1189
+ oShellController._initiateApplication(
1190
+ oResolvedHashFragment, sFixedShellHash, oParsedShellHash,
1191
+ iOriginalHistoryLength, sAppPart, sOldFixedShellHash, sOldAppPart
1192
+ );
1193
+ oDeferred.resolve();
1194
+ });
1195
+ return;
1196
+ }
1197
+ AppLifeCycleAI.destroyApplication(oInMemoryApplicationInstance.appId, oInMemoryApplicationInstance.container);
1198
+ AppLifeCycleAI.removeApplication(sIntent);
1199
+ AppConfiguration.setApplicationInInitMode();
1200
+
1201
+ // normal application:
1202
+ // fire the _prior.newUI5ComponentInstantion event before creating the new component instance, so that
1203
+ // the ApplicationContainer can stop the router of the current app (avoid inner-app hash change notifications)
1204
+ // NOTE: this dependency to the ApplicationContainer is not nice, but we need a fast fix now; we should refactor
1205
+ // the ApplicationContainer code, because most of the logic has to be done by the shell controller;
1206
+ // maybe rather introduce a utility module
1207
+ Core.getEventBus().publish("ApplicationContainer", "_prior.newUI5ComponentInstantion",
1208
+ { name: sTargetUi5ComponentName }
1209
+ );
1210
+
1211
+ //Performance Debug
1212
+ Measurement.start("FLP:ShellController.UI5createComponent", "UI5 createComponent", "FLP");
1213
+ // load ui5 component via shell service; core-ext-light will be loaded as part of the asyncHints
1214
+
1215
+ sap.ushell.Container.getServiceAsync("Ui5ComponentLoader").then(function () {
1216
+ AppLifeCycleAI.createComponent(oResolvedHashFragment, oParsedShellHash).done(function (/*oResolutionResultWithComponentHandle*/) {
1217
+ // `oResolutionResultWithComponentHandle` is unused.
1218
+ // This is because oResolvedHashFragment contains the component handle already.
1219
+ // See the preceding note in AppLifeCycle.createComponent.
1220
+ Measurement.end("FLP:ShellController.UI5createComponent");
1221
+ oShellController._initiateApplication(oResolvedHashFragment, sFixedShellHash, oParsedShellHash, iOriginalHistoryLength, sAppPart, sOldFixedShellHash, sOldAppPart);
1222
+ }).fail(function (vError) {
1223
+ var sErrorReason = ushellResources.i18n.getText("cannot_load_ui5_component_details", [sFixedShellHash]);
1224
+ var sErrorReasonEnglish = "Failed to load UI5 component for navigation intent " + sFixedShellHash;
1225
+
1226
+ AppConfiguration.setCurrentApplication(oShellController.currentAppBeforeNav);
1227
+ oShellController.hashChangeFailure(
1228
+ iOriginalHistoryLength,
1229
+ {
1230
+ title: ushellResources.i18n.getText("error"),
1231
+ message: ushellResources.i18n.getText("failed_to_open_ui5_component"),
1232
+ technicalMessage: sErrorReasonEnglish
1233
+ }, {
1234
+ info: sErrorReason,
1235
+ technicalMessage: vError.message + "\n" + vError.stack
1236
+ },
1237
+ "sap.ushell.renderer.Shell.controller",
1238
+ false);
1239
+ });
1240
+ });
1241
+ }
1242
+ oDeferred.resolve();
1243
+ });
1244
+ }, function (sMsg) {
1245
+ var sErrorReason = ushellResources.i18n.getText("cannot_resolve_navigation_target_details", [sFixedShellHash]);
1246
+ var sErrorReasonEnglish = "Failed to resolve navigation target: " + sFixedShellHash
1247
+ + ". This is most likely caused by an incorrect SAP Fiori launchpad content configuration or by missing role assignment.";
1248
+
1249
+ oShellController.hashChangeFailure(
1250
+ iOriginalHistoryLength,
1251
+ {
1252
+ title: ushellResources.i18n.getText("error"),
1253
+ message: ushellResources.i18n.getText("failed_to_open_app_missing_configuration_or_role_assignment"),
1254
+ technicalMessage: sErrorReasonEnglish
1255
+ },
1256
+ {
1257
+ info: sErrorReason,
1258
+ fixedShellHash: sFixedShellHash,
1259
+ technicalMessage: sMsg
1260
+ },
1261
+ "sap.ushell.renderer.Shell.controller",
1262
+ false);
1263
+
1264
+ oDeferred.reject(sErrorReasonEnglish);
1265
+ });
1266
+
1267
+ return oDeferred.promise();
1268
+ },
1269
+
1270
+ /**
1271
+ * Attempts to navigate to the target application via a shortcut,
1272
+ * without executing unnecessary AppLifeCycle navigation logic (e.g.,
1273
+ * application component initialization).
1274
+ *
1275
+ * This might be necessary when the navigation should not be handled in
1276
+ * the current window context anymore, for example, when the
1277
+ * application should be opened in a new window, or NWBC takes over the
1278
+ * navigation task.
1279
+ *
1280
+ * @param {string} sFixedShellHash
1281
+ * The hash fragment including "#"
1282
+ * @param {string} sAppPart
1283
+ * The inner app route part of the intent
1284
+ * @param {object} oResolvedHashFragment
1285
+ * The resolved target intent
1286
+ *
1287
+ * @returns {undefined} Nothing
1288
+ * @private
1289
+ */
1290
+ _handleEarlyNavigation: function (sFixedShellHash, sAppPart, oResolvedHashFragment) {
1291
+ var that = this;
1292
+ var oDashboard;
1293
+
1294
+ return new Promise(function (fnResolve) {
1295
+ that._openAppViaNWBC(oResolvedHashFragment).then(function (bOpenedViaNWBC) {
1296
+ if (bOpenedViaNWBC === true) {
1297
+ oResolvedHashFragment.sFixedShellHash = sFixedShellHash;
1298
+ that.logOpenAppAction(oResolvedHashFragment, sAppPart);
1299
+ oDashboard = Core.byId("sapUshellDashboardPage");
1300
+ if (oDashboard) {
1301
+ oDashboard.setBlocked(false);
1302
+ oDashboard.setBusy(false);
1303
+ }
1304
+ fnResolve(true);
1305
+ return;
1306
+ }
1307
+
1308
+ var bNewWindowNavigation = oResolvedHashFragment
1309
+ && oResolvedHashFragment.navigationMode === oNavigationMode.newWindow;
1310
+ if (bNewWindowNavigation) {
1311
+ // add the app to application usage log
1312
+ oResolvedHashFragment.sFixedShellHash = sFixedShellHash;
1313
+ that.logOpenAppAction(oResolvedHashFragment, sAppPart);
1314
+ sap.ui.require(["sap/ushell/components/container/ApplicationContainer"], function (ApplicationContainer) {
1315
+ oResolvedHashFragment.url = ApplicationContainer.prototype._checkNwbcUrlAdjustment(undefined, oResolvedHashFragment.applicationType, oResolvedHashFragment.url, true);
1316
+ that._openAppInNewWindowAndRestore(oResolvedHashFragment);
1317
+ oDashboard = Core.byId("sapUshellDashboardPage");
1318
+ if (oDashboard) {
1319
+ oDashboard.setBlocked(false);
1320
+ oDashboard.setBusy(false);
1321
+ }
1322
+ fnResolve(true);
1323
+ });
1324
+ return;
1325
+ }
1326
+
1327
+ // Close dialogs only for inplace navigation
1328
+ if (InstanceManager && closeAllDialogs) {
1329
+ InstanceManager.closeAllDialogs();
1330
+ InstanceManager.closeAllPopovers();
1331
+ }
1332
+ closeAllDialogs = true;
1333
+
1334
+ // In case of empty hash, if there is a resolved target, set the flag to false, from now on the rootIntent
1335
+ // will be an empty hash. Otherwise, change hash to rootIntent to trigger normal resolution.
1336
+ if (Config.last("/core/shell/model/migrationConfig")) {
1337
+ oConfig.migrationConfig = false;
1338
+ that.getModel().setProperty("/migrationConfig", false);
1339
+
1340
+ if (oResolvedHashFragment && sFixedShellHash === "#") {
1341
+ oConfig.rootIntent = "";
1342
+ } else if (sFixedShellHash === "#") {
1343
+ window.setTimeout(function () {
1344
+ window.hasher.setHash(oConfig.rootIntent);
1345
+ }, 0);
1346
+ fnResolve(true);
1347
+ return;
1348
+ }
1349
+ }
1350
+
1351
+ fnResolve(false);
1352
+ return;
1353
+ });
1354
+ });
1355
+ },
1356
+
1357
+ _initiateApplication: function (oResolvedHashFragment, sFixedShellHash, oParsedShellHash, iOriginalHistoryLength, sAppPart, sOldFixedShellHash, sOldAppPart) {
1358
+ Measurement.start("FLP:ShellController._initiateApplication", "_initiateApplication", "FLP");
1359
+
1360
+ var oMetadata = AppConfiguration.getMetadata(oResolvedHashFragment);
1361
+ var bContactSupportEnabled = Config.last("/core/extension/SupportTicket");
1362
+ var bPreviousIsInitialNavigation;
1363
+
1364
+ // the "if" should protect against undefined, empty string and null
1365
+ if (oMetadata.title) {
1366
+ window.document.title = oMetadata.title;
1367
+ } else {
1368
+ // FIXME: Remove title so that users don't think it's a bug
1369
+ Log.debug("Shell controller._initiateApplication: the title of the window is not changed because most probably the application was resolved with undefined");
1370
+ }
1371
+ // the activation of user activity logging must be done after the app component is fully loaded
1372
+ // otherwise the module loading sequence causes race conditions on firefox
1373
+ if (bContactSupportEnabled) {
1374
+ window.setTimeout(function () {
1375
+ UserActivityLog.activate();
1376
+ }, 0);
1377
+ }
1378
+
1379
+ try {
1380
+ bPreviousIsInitialNavigation = this.oShellNavigation.isInitialNavigation();
1381
+ this.navigate(oParsedShellHash, sFixedShellHash, oMetadata, oResolvedHashFragment, sOldFixedShellHash);
1382
+ } catch (oExc) {
1383
+ if (oExc.stack) {
1384
+ Log.error("Application initialization (Intent: \n" + sFixedShellHash + "\n failed due to an Exception:\n" + oExc.stack);
1385
+ }
1386
+ this.oShellNavigation.setIsInitialNavigation(bPreviousIsInitialNavigation);
1387
+ this.hashChangeFailure(iOriginalHistoryLength, oExc.name, oExc.message, oMetadata ? oMetadata.title : "", false);
1388
+ }
1389
+
1390
+ Measurement.end("FLP:ShellController._initiateApplication");
1391
+ },
1392
+
1393
+ /**
1394
+ * Callback registered with NavService. Triggered on navigation requests
1395
+ *
1396
+ * @param {string} sShellHash the hash fragment to parse (must start with "#")
1397
+ * @returns {jQuery.Deferred.promise} a promise resolved with an object containing the resolved hash fragment (i.e., the result of
1398
+ * {@link sap.ushell.services.NavTargetResolution#resolveHashFragment}), the parsed shell hash obtained via
1399
+ * {@link sap.ushell.services.URLParsing#parseShellHash}, and a boolean value indicating whether application dependencies
1400
+ * <b>and</b> core-ext-light were loaded earlier. The promise is rejected with an error message in case errors occur.
1401
+ */
1402
+ _resolveHashFragment: function (sShellHash) {
1403
+ //Performance Debug
1404
+ Measurement.start("FLP:ShellController._resolveHashFragment", "_resolveHashFragment", "FLP");
1405
+ var oParsedShellHash = this.oURLParsing.parseShellHash(sShellHash);
1406
+ var oDeferred = new jQuery.Deferred();
1407
+ var oConfig = this._getConfig(); // for testing
1408
+
1409
+ // Check and use resolved hash fragment from direct start promise if it's there
1410
+ if (window["sap-ushell-async-libs-promise-directstart"]) {
1411
+ window["sap-ushell-async-libs-promise-directstart"]
1412
+ .then(function (oDirectstartPromiseResult) {
1413
+ oDeferred.resolve(
1414
+ oDirectstartPromiseResult.resolvedHashFragment,
1415
+ oParsedShellHash
1416
+ );
1417
+ delete window["sap-ushell-async-libs-promise-directstart"];
1418
+ },
1419
+ function (sMsg) {
1420
+ oDeferred.reject(sMsg);
1421
+ delete window["sap-ushell-async-libs-promise-directstart"];
1422
+ });
1423
+ return oDeferred.promise();
1424
+ }
1425
+
1426
+ // Perform target resolution as normal...
1427
+ sap.ushell.Container.getServiceAsync("NavTargetResolution").then(function (oNavTargetResolution) {
1428
+ oNavTargetResolution.resolveHashFragment(sShellHash)
1429
+ .done(function (oResolvedHashFragment) {
1430
+ //@FIXME: Should be also executed for directstart
1431
+ AppConfiguration.setCurrentApplication(oResolvedHashFragment);
1432
+ /*
1433
+ * Override navigation mode for root intent.
1434
+ * Shell home should be opened in embedded mode to allow a new window to be opened from GUI applications.
1435
+ */
1436
+ if (oParsedShellHash && (oParsedShellHash.semanticObject + "-" + oParsedShellHash.action) === oConfig.rootIntent) {
1437
+ oResolvedHashFragment.navigationMode = "embedded";
1438
+ }
1439
+ Measurement.end("FLP:ShellController._resolveHashFragment");
1440
+ ushellUtils.setPerformanceMark("FLP-ShellController-resolveHashFragment-end");
1441
+ oDeferred.resolve(oResolvedHashFragment, oParsedShellHash);
1442
+ })
1443
+ .fail(function (sMsg) {
1444
+ oDeferred.reject(sMsg);
1445
+ });
1446
+ });
1447
+ return oDeferred.promise();
1448
+ },
1449
+
1450
+ /**
1451
+ * Adjust Navigation mode based on current state of the Shell and application and the ResolveHashFragment bo be started
1452
+ *
1453
+ * This operation mutates oResolvedHashFragment
1454
+ *
1455
+ * {@link #navigate}.
1456
+ *
1457
+ * @param {object} oResolvedHashFragment the hash fragment resolved via {@link sap.ushell.services.NavTargetResolution#resolveHashFragment}
1458
+ * @returns {object} a new, potentially altered resolution result. Note that url and navigation mode may have been changed!
1459
+ * For navigation in new window, the URL is replaced with the current location hash.
1460
+ * NOTE: refactor this; we should not have these implicit changes of the navigation target
1461
+ * @private
1462
+ */
1463
+ _calculateNavigationMode: function (oResolvedHashFragment) {
1464
+ if (!oResolvedHashFragment) {
1465
+ return undefined; // happens in tests
1466
+ }
1467
+ var sNavigationMode = oResolvedHashFragment.navigationMode;
1468
+
1469
+ if (sNavigationMode === oNavigationMode.newWindowThenEmbedded) {
1470
+ // Implement newWindowThenEmbedded based on current state.
1471
+ if (this._isColdStart() || Ui5History.getInstance().getDirection() === Ui5HistoryDirection.Backwards) {
1472
+ /*
1473
+ * cold start
1474
+ * -> always open in place because the new window was opened by the user
1475
+ *
1476
+ * Ui5History.getInstance().getDirection()
1477
+ * -> URL has already been navigated to and it was the predecessor of the previous page
1478
+ */
1479
+ oResolvedHashFragment.navigationMode = oNavigationMode.embedded;
1480
+ } else {
1481
+ oResolvedHashFragment.navigationMode = oNavigationMode.newWindow;
1482
+ // if its a non-native navigation, we resolve the hash again in the new window
1483
+ // we set the full current location hash as URL for the new window as it is
1484
+ // for avoiding encoding issues and stripping off parameters or inner-app route
1485
+ // see internal BCP 1770274241
1486
+ if (!ushellUtils.isNativeWebGuiNavigation(oResolvedHashFragment)) {
1487
+ oResolvedHashFragment.url = this._getCurrentLocationHash();
1488
+ }
1489
+ }
1490
+ return oResolvedHashFragment;
1491
+ }
1492
+
1493
+ if (sNavigationMode === oNavigationMode.newWindow && this._isColdStart()) {
1494
+ // Workaround for URLs that start an FLP app which needs the shell.
1495
+ if (this._hasAppCapabilitiesNavigationMode(oResolvedHashFragment)
1496
+ && oResolvedHashFragment.appCapabilities.navigationMode === oNavigationMode.embedded) {
1497
+ oResolvedHashFragment.navigationMode = oNavigationMode.embedded;
1498
+ return oResolvedHashFragment;
1499
+ }
1500
+ // Replace the content of the current window if the user has already opened one.
1501
+ oResolvedHashFragment.navigationMode = oNavigationMode.replace;
1502
+ return oResolvedHashFragment;
1503
+ }
1504
+
1505
+ if (sNavigationMode === oNavigationMode.newWindow
1506
+ && this._hasAppCapabilitiesNavigationMode(oResolvedHashFragment)
1507
+ && oResolvedHashFragment.appCapabilities.navigationMode === oNavigationMode.embedded) {
1508
+ oResolvedHashFragment.url = this._getCurrentLocationHash();
1509
+ }
1510
+ return oResolvedHashFragment;
1511
+ },
1512
+
1513
+ // Workaround for URLs that start an FLP app which needs the shell.
1514
+ _hasAppCapabilitiesNavigationMode: function (oResolvedHashFragment) {
1515
+ return ushellUtils.isPlainObject(oResolvedHashFragment.appCapabilities) && oResolvedHashFragment.appCapabilities.hasOwnProperty("navigationMode");
1516
+ },
1517
+
1518
+ _usesNavigationRedirect: function (oComponentHandle) {
1519
+ if (!oComponentHandle) {
1520
+ return new jQuery.Deferred().reject().promise();
1521
+ }
1522
+ var that = this;
1523
+ var oComponent = oComponentHandle.getInstance({});
1524
+ if (oComponent && typeof oComponent.navigationRedirect === "function") {
1525
+ // oComponent refers to a trampoline application
1526
+ var oDeferred = new jQuery.Deferred();
1527
+ var oNavRedirectPromise = oComponent.navigationRedirect();
1528
+ if (oNavRedirectPromise && (typeof oNavRedirectPromise.then === "function")) {
1529
+ oNavRedirectPromise.then(function (sNextHash) {
1530
+ Log.warning("Performing navigation redirect to hash " + sNextHash);
1531
+ oComponent.destroy();
1532
+ that.history.pop();
1533
+ sap.ushell.Container.getServiceAsync("ShellNavigation").then(function (oShellNavigation) {
1534
+ oShellNavigation.toExternal({ target: { shellHash: sNextHash } }, undefined, false);
1535
+ oDeferred.resolve(true);
1536
+ });
1537
+ }, function () {
1538
+ oDeferred.reject();
1539
+ });
1540
+ return oDeferred.promise();
1541
+ }
1542
+ }
1543
+ return new jQuery.Deferred().reject().promise();
1544
+ },
1545
+
1546
+ /**
1547
+ * Performs navigation based on the given resolved hash fragment.
1548
+ *
1549
+ * @param {object} oParsedShellHash the parsed shell hash obtained via {@link sap.ushell.services.URLParsing} service
1550
+ * @param {string} sFixedShellHash the hash fragment to navigate to. It must start with "#" (i.e., fixed).
1551
+ * @param {object} oMetadata the metadata object obtained via {@link sap.ushell.services.AppConfiguration#parseShellHash}
1552
+ * @param {object} oResolvedHashFragment the hash fragment resolved via {@link sap.ushell.services.NavTargetResolution#resolveHashFragment}
1553
+ *
1554
+ * @returns {Promise} Navigation Promise
1555
+ */
1556
+ navigate: function (oParsedShellHash, sFixedShellHash, oMetadata, oResolvedHashFragment, sOldFixedShellHash) {
1557
+ //Performance Debug
1558
+ Measurement.start("FLP:ShellController.navigate", "navigate", "FLP");
1559
+ var sNavigationMode = (isPlainObject(oResolvedHashFragment) ? oResolvedHashFragment.navigationMode : null);
1560
+ var that = this;
1561
+
1562
+ /*
1563
+ * A null navigationMode is a no-op, it indicates no navigation should occur.
1564
+ * However, we need to restore the current hash to the previous one.
1565
+ * If cold start happened (history has only one entry), we go to the shell home.
1566
+ */
1567
+ if (sNavigationMode === null) {
1568
+ if (this._isColdStart()) {
1569
+ window.hasher.setHash("");
1570
+ return;
1571
+ }
1572
+
1573
+ this.bEnableHashChange = false;
1574
+ this.history.pop();
1575
+ this._windowHistoryBack(1);
1576
+ return Promise.resolve();
1577
+ }
1578
+
1579
+ oResolvedHashFragment = this._calculateNavigationMode(oResolvedHashFragment);
1580
+ sNavigationMode = (isPlainObject(oResolvedHashFragment) ? oResolvedHashFragment.navigationMode : null);
1581
+
1582
+ if (!sNavigationMode) {
1583
+ Log.error(
1584
+ "Mandatory member 'navigationMode' is missing in the resolution result! Temporarily set to 'embedded' for further processing...",
1585
+ "sap.ushell.renderer.Shell.controller"
1586
+ );
1587
+ sNavigationMode = "embedded";
1588
+ }
1589
+
1590
+ if (sNavigationMode === oNavigationMode.embedded) {
1591
+ if (!this._isColdStart() && !this._wasHistoryEntryReplaced) {
1592
+ this.oShellNavigation.setIsInitialNavigation(false);
1593
+ }
1594
+
1595
+ var oDeferred = this._usesNavigationRedirect(oResolvedHashFragment.componentHandle);
1596
+ var oNavigationPromise = new Promise(function (resolve, reject) {
1597
+ // When `oDeferred` succeeds, it implies the component references a trampoline application.
1598
+ // The trampoline application subsequently gets destroyed after it's used to enable the redirection.
1599
+ // The failure is being used here as a means for branching in the execution flow.
1600
+ oDeferred.then(null, function () {
1601
+ that._handleEmbeddedNavMode(sFixedShellHash, oParsedShellHash, oMetadata, oResolvedHashFragment, sOldFixedShellHash).then(function () {
1602
+
1603
+ //The event is used for FESR records
1604
+ //Normally FESR record is closed on the "AppRendered" event, but for some cases (stateful container, etc.)
1605
+ //the application container is not re-rendered and EmbeddedNavigationDone is additionally fired to close the record.
1606
+ EventHub.emit("CloseFesrRecord", { time: Date.now(), technicalName: oMetadata.technicalName });
1607
+ resolve();
1608
+ });
1609
+ });
1610
+ Measurement.end("FLP:ShellController.navigate");
1611
+ });
1612
+
1613
+ return oNavigationPromise;
1614
+ }
1615
+
1616
+ if (sNavigationMode === oNavigationMode.replace) {
1617
+ this.oShellNavigation.setIsInitialNavigation(false);
1618
+ // restore hash
1619
+ this.bEnableHashChange = false;
1620
+ this._changeWindowLocation(oResolvedHashFragment.url);
1621
+ return Promise.resolve();
1622
+ }
1623
+
1624
+ if (sNavigationMode === oNavigationMode.newWindow) {
1625
+ this._openAppInNewWindowAndRestore(oResolvedHashFragment);
1626
+ return Promise.resolve();
1627
+ }
1628
+
1629
+ // the navigation mode doesn't match any valid one.
1630
+ // In this case an error message is logged and previous hash is fetched
1631
+ this.hashChangeFailure(this.history.getHistoryLength(), "Navigation mode is not recognized", null, "sap.ushell.renderer.Shell.controller", false);
1632
+ return Promise.resolve();
1633
+ },
1634
+
1635
+ /**
1636
+ * Attempts opening the application via NWBC.
1637
+ *
1638
+ * @param {object} oResolvedHashFragment
1639
+ * The resolved hash fragment
1640
+ * @returns {Promise}
1641
+ * Whether NWBC managed to open the application (resolved with either true or false)
1642
+ *
1643
+ * @private
1644
+ */
1645
+ _openAppViaNWBC: function (oResolvedHashFragment) {
1646
+ var that = this;
1647
+
1648
+ return new Promise(function (fnResolve) {
1649
+ var bNwbcHandling;
1650
+
1651
+ if (oResolvedHashFragment && (ushellUtils.isNativeWebGuiNavigation(oResolvedHashFragment) || oResolvedHashFragment.nativeNWBCNavigation)) {
1652
+ bNwbcHandling = true;
1653
+ } else {
1654
+ bNwbcHandling = false;
1655
+ }
1656
+
1657
+ if (bNwbcHandling === false) {
1658
+ fnResolve(false);
1659
+ return;
1660
+ }
1661
+
1662
+ var oEPCM = ushellUtils.getPrivateEpcm();
1663
+ var iEPCMNavigationMode = oEPCMNavigationMode[oResolvedHashFragment.navigationMode];
1664
+ var sUrlWithSapUserAndShellParam;
1665
+ var iMode;
1666
+
1667
+ if (ushellUtils.hasNavigationModeCapability()) {
1668
+ iMode = iEPCMNavigationMode || oEPCMNavigationMode[oNavigationMode.embedded];
1669
+ }
1670
+
1671
+ var oPromiseParams = that._appendUserIdToUrl("sap-user", oResolvedHashFragment.url).then(async function (sUrlWithSapUser) {
1672
+ sUrlWithSapUserAndShellParam = await ushellUtils.appendSapShellParam(sUrlWithSapUser);
1673
+
1674
+ return new Promise(function (fnResolveParams) {
1675
+ try {
1676
+ var sAppType = oResolvedHashFragment.applicationType;
1677
+ var sFrameworkId = (oResolvedHashFragment.appCapabilities && oResolvedHashFragment.appCapabilities.appFrameworkId);
1678
+ var oFLPParams = {};
1679
+ var aInfoArray = [];
1680
+ var aKeysArray;
1681
+ var fnPreparePostBodyParams = function () {
1682
+ oFLPParams["sap-flp-url"] = sap.ushell.Container.getFLPUrl(true);
1683
+ oFLPParams["system-alias"] = oResolvedHashFragment.systemAlias;
1684
+ return [{
1685
+ name: "sap-flp-params",
1686
+ value: JSON.stringify(oFLPParams)
1687
+ }];
1688
+ };
1689
+ var getParamKeys = function getParamKeys (sUrl, aInfoArray) {
1690
+ var aAppStateKeysArray = [];
1691
+ var fnAddKey = function (sName, sDataName) {
1692
+ if (sUrl.indexOf(sName + "=") > 0) {
1693
+ var aParams = new RegExp("(?:" + sName + "=)([^&/]+)").exec(sUrl);
1694
+ if (aParams && aParams.length === 2) {
1695
+ aAppStateKeysArray.push([aParams[1]]);
1696
+ aInfoArray.push(sDataName);
1697
+ }
1698
+ }
1699
+ };
1700
+
1701
+ fnAddKey("sap-iapp-state", "sap-iapp-state-data");
1702
+ fnAddKey("sap-xapp-state", "sap-xapp-state-data");
1703
+ fnAddKey("sap-xapp-state-data", "sap-xapp-state-data");
1704
+ fnAddKey("sap-intent-param", "sap-intent-param-data");
1705
+ return aAppStateKeysArray;
1706
+ };
1707
+
1708
+ if (ushellUtils.isSAPLegacyApplicationType(sAppType, sFrameworkId)) {
1709
+ sap.ushell.Container.getServiceAsync("Navigation").then(function (oCrossAppNavService) {
1710
+ aKeysArray = getParamKeys(sUrlWithSapUserAndShellParam, aInfoArray);
1711
+ if (aKeysArray.length > 0) {
1712
+ oCrossAppNavService.getAppStateData(aKeysArray).then(function (aDataArray) {
1713
+ aInfoArray.forEach(function (item, index) {
1714
+ if (aDataArray[index][0]) {
1715
+ oFLPParams[item] = aDataArray[index][0];
1716
+ }
1717
+ });
1718
+ fnResolveParams(fnPreparePostBodyParams());
1719
+ }, function (sError) {
1720
+ fnResolveParams();
1721
+ });
1722
+ } else {
1723
+ fnResolveParams(fnPreparePostBodyParams());
1724
+ }
1725
+ });
1726
+ } else {
1727
+ fnResolveParams();
1728
+ }
1729
+ } catch (e) {
1730
+ //nothing to do, we will open the app without the post params as before
1731
+ fnResolveParams();
1732
+ }
1733
+ });
1734
+ });
1735
+
1736
+ oPromiseParams.then(
1737
+ //the promise can not reject as we have to try to open the app
1738
+ function (oPostBodyParams) {
1739
+ try {
1740
+ Core.getEventBus().publish("launchpad", "appOpening", oResolvedHashFragment);
1741
+ if (!oEPCM.doNavigate(sUrlWithSapUserAndShellParam, iMode, undefined, undefined, undefined, undefined, undefined, oPostBodyParams)) {
1742
+ fnResolve(false);
1743
+ return;
1744
+ }
1745
+ Core.getEventBus().publish("sap.ushell", "appOpened", oResolvedHashFragment);
1746
+
1747
+ that._restoreAfterNwbcNavigation(oResolvedHashFragment).then(function () {
1748
+ /*
1749
+ TODO: This is a workaround.
1750
+ We should rather update the resolvedHashFragment before the navigation happens. Other Components listening
1751
+ to the appOpened event might rely on the correct navigationMode / targetNavigationMode of the
1752
+ resolvedHashFragment.
1753
+ */
1754
+ // restore menu bar in case we are still on home
1755
+ if (that._oAppLifeCycleService.getCurrentApplication() && that._oAppLifeCycleService.getCurrentApplication().homePage) {
1756
+ that._setMenuVisible(true);
1757
+ }
1758
+
1759
+ fnResolve(true);
1760
+ });
1761
+ } catch (e) {
1762
+ if (e.stack) {
1763
+ Log.error("Application initialization failed due to an Exception:\n" + e.stack);
1764
+ }
1765
+ that.hashChangeFailure(that.history.getHistoryLength(), e.name, e.message, oResolvedHashFragment.text, false);
1766
+ fnResolve(false);
1767
+ }
1768
+ }
1769
+ );
1770
+ });
1771
+ },
1772
+
1773
+ /**
1774
+ *
1775
+ * If a GUI application that was handled by NWBC was opened via deep link, a new tab is opened, which started the GUI.
1776
+ * Therefore, the current tab must navigate to the home page.
1777
+ *
1778
+ * In all other cases (non-deep link), it is sufficient
1779
+ * to navigate one step back in the browser history to restore the current hash.
1780
+ *
1781
+ * @param {object} oResolvedHashFragment The resolved hash fragment.
1782
+ * @returns {Promise} A Promise resolving when the hash is restored.
1783
+ * @private
1784
+ */
1785
+ _restoreAfterNwbcNavigation: function (oResolvedHashFragment) {
1786
+ if (this.history.getHistoryLength() === 1 && !this._oAppLifeCycleService.getCurrentApplication()) {
1787
+ // Deep link case
1788
+ return sap.ushell.Container.getServiceAsync("Navigation").then(function (oNavigationService) {
1789
+ oNavigationService.navigate({ target: { shellHash: "#" }, writeHistory: false });
1790
+ });
1791
+ }
1792
+
1793
+ // Regular case
1794
+ this._restorePreviousHashAfterOpenNewWindow(oResolvedHashFragment);
1795
+ return Promise.resolve();
1796
+ },
1797
+
1798
+ /**
1799
+ * Appends the ID of the user to the given URL.
1800
+ * The ID of the user is retrieved via the UserInfo service, and appended blindly to the given URL.
1801
+ * This method tries to detect whether a previous parameter was already appended
1802
+ * and use the <code>?</code> or <code>&</code> separator for the parameter accordingly.
1803
+ *
1804
+ * @param {string} sParamName The name of the parameter that needs to be appended.
1805
+ * @param {string} sUrl A URL with or without the sap-user parameter.
1806
+ * @returns {Promise<string>} The URL with the user id parameter appended.
1807
+ * @private
1808
+ */
1809
+ _appendUserIdToUrl: function (sParamName, sUrl) {
1810
+ return sap.ushell.Container.getServiceAsync("UserInfo").then(function (oUserInfoService) {
1811
+ var sUserId = oUserInfoService.getUser().getId();
1812
+ var sSep = sUrl.indexOf("?") >= 0 ? "&" : "?";
1813
+ return sUrl + sSep + sParamName + "=" + sUserId;
1814
+ });
1815
+ },
1816
+
1817
+ /**
1818
+ * The method restore the previous hash when app is opened in the new tab
1819
+ * @param {*} oResolvedHashFragment resolved hash fragment
1820
+ */
1821
+ _restorePreviousHashAfterOpenNewWindow: function (oResolvedHashFragment) {
1822
+ this.history.pop();
1823
+ var oVarInstance = oResolvedHashFragment.componentHandle && oResolvedHashFragment.componentHandle.getInstance
1824
+ && oResolvedHashFragment.componentHandle.getInstance({});
1825
+ if (oVarInstance) {
1826
+ oVarInstance.destroy();
1827
+ }
1828
+ this._resumeAppRouterIgnoringCurrentHash();
1829
+ this.bRestorePreviousHash = true;
1830
+ this._windowHistoryBack(1);
1831
+
1832
+ // set back the current application to be the one before this navigation occurred as current application is opened in a new window
1833
+ AppConfiguration.setCurrentApplication(this.currentAppBeforeNav);
1834
+ EventHub.emit("openedAppInNewWindow", Date.now());
1835
+ },
1836
+
1837
+ _openAppInNewWindowAndRestore: function (oResolvedHashFragment) {
1838
+ Core.getEventBus().publish("launchpad", "appOpening", oResolvedHashFragment);
1839
+ this._openAppNewWindow(oResolvedHashFragment.url);
1840
+ Core.getEventBus().publish("sap.ushell", "appOpened", oResolvedHashFragment);
1841
+ this._restorePreviousHashAfterOpenNewWindow(oResolvedHashFragment);
1842
+ },
1843
+
1844
+ _handleEmbeddedNavMode: function (sFixedShellHash, oParsedShellHash, oMetadata, oResolvedHashFragment, sOldFixedShellHash) {
1845
+ // Performance Debug
1846
+ Measurement.start("FLP:ShellController._handleEmbeddedNavMode", "_handleEmbeddedNavMode", "FLP");
1847
+ var that = this,
1848
+ oConfig = this._getConfig();
1849
+
1850
+ this.resetShellUIServiceHandlers();
1851
+
1852
+ AppLifeCycleAI.getAppMeta().setAppIcons(oMetadata);
1853
+
1854
+ // obtain a unique id for the app (or the component)
1855
+ var sAppId = "-" + oParsedShellHash.semanticObject + "-" + oParsedShellHash.action;
1856
+
1857
+ var bIsNavToHome = sFixedShellHash === "#" ||
1858
+ (oConfig.rootIntent && ushellUtils.getBasicHash(oConfig.rootIntent) === oParsedShellHash.semanticObject + "-" + oParsedShellHash.action);
1859
+
1860
+ // Support migration from version 1.28 or lower in case local resolution for empty hash was used
1861
+ var sIntent = oParsedShellHash ? oParsedShellHash.semanticObject + "-" + oParsedShellHash.action : "";
1862
+
1863
+ AppLifeCycleAI.switchViewState(
1864
+ AppLifeCycleAI.shellElements().calculateElementsState(
1865
+ bIsNavToHome ? "home" : "app",
1866
+ oResolvedHashFragment.applicationType,
1867
+ oConfig.appState,
1868
+ oResolvedHashFragment.explicitNavMode
1869
+ ),
1870
+ undefined,
1871
+ sAppId
1872
+ );
1873
+
1874
+ if (bIsNavToHome) {
1875
+ AppLifeCycleAI.getShellUIService().setBackNavigation();
1876
+ }
1877
+
1878
+ // adding intent as this published application info is required for the contact-support scenario
1879
+ oResolvedHashFragment.sFixedShellHash = sFixedShellHash;
1880
+
1881
+ var oHandlerPromise = AppLifeCycleAI.handleControl(
1882
+ sIntent,
1883
+ sAppId,
1884
+ oParsedShellHash,
1885
+ oResolvedHashFragment,
1886
+ this.getWrappedApplicationWithMoreStrictnessInIntention.bind(this),
1887
+ sFixedShellHash,
1888
+ sOldFixedShellHash
1889
+ );
1890
+
1891
+ if (this.currentAppBeforeNav) {
1892
+ if (ushellUtils.isTRApplicationType(this.currentAppBeforeNav.applicationType, ObjectPath.get("appCapabilities.appFrameworkId", this.currentAppBeforeNav)) &&
1893
+ !ushellUtils.isTRApplicationType(oResolvedHashFragment.applicationType)) {
1894
+ sap.ui.require([
1895
+ "sap/ushell/components/applicationIntegration/application/BlueBoxHandler",
1896
+ "sap/ushell/components/applicationIntegration/application/WebGUIStatefulHandler"], function (BlueBoxHandler, WebGUIStatefulHandler) {
1897
+ var oPreviousStatefulContainer = BlueBoxHandler.getBlueBoxByUrl(that.currentAppBeforeNav.url);
1898
+ if (oPreviousStatefulContainer && oPreviousStatefulContainer.getStatefulType() === BlueBoxHandler.STATEFUL_TYPES.GUI_V1) {
1899
+ WebGUIStatefulHandler.guiStatefulCloseCurrentApp(oPreviousStatefulContainer);
1900
+ }
1901
+ });
1902
+ }
1903
+ }
1904
+
1905
+ Measurement.end("FLP:ShellController._handleEmbeddedNavMode");
1906
+
1907
+ return oHandlerPromise;
1908
+ },
1909
+
1910
+ _centerViewPort: function () {
1911
+ this.oViewPortContainer.switchState("Center");
1912
+ },
1913
+
1914
+ _isShellHomeIntent: function (sIntent) {
1915
+ return sIntent === "#" || sIntent === oConfig.rootIntent;
1916
+ },
1917
+
1918
+ // Please help improve the strictness of this method.
1919
+ getWrappedApplicationWithMoreStrictnessInIntention: function (sIntent, oMetadata, oShellHash, oResolvedNavigationTarget, sAppId, bFullWidth, sFixedShellHash) {
1920
+ var that = this;
1921
+
1922
+ if (Device.system.desktop) {
1923
+ sap.ui.require(["sap/ushell/renderer/AccessKeysHandler"], function (AccessKeysHandler) {
1924
+ var oShellAppTitle = Core.byId("shellAppTitle");
1925
+ if (oShellAppTitle && Config.last("/core/shell/model/currentState/stateName") === "app") {
1926
+ var oShellAppTitleDomRef = oShellAppTitle.getFocusDomRef();
1927
+ if (oShellAppTitleDomRef) {
1928
+ AccessKeysHandler.sendFocusBackToShell(oShellAppTitleDomRef.getAttribute("id"));
1929
+ }
1930
+ }
1931
+ });
1932
+ }
1933
+
1934
+ window.setTimeout(function () {
1935
+ window.setTimeout(function () {
1936
+ //Screen reader: "Loading Complete"
1937
+ that.readNavigationEnd();
1938
+ }, 600);
1939
+
1940
+ Core.getEventBus().publish("launchpad", "appOpening", oResolvedNavigationTarget);
1941
+ Log.info("app is being opened");
1942
+ }, 0);
1943
+ if (oConfig.applications) {
1944
+ oResolvedNavigationTarget.applicationConfiguration = oConfig.applications[sIntent];
1945
+ }
1946
+
1947
+ var oAppContainer = AppLifeCycleAI.getAppContainer(sAppId, oResolvedNavigationTarget, this._isColdStart(), oShellHash, sFixedShellHash);
1948
+ var bContainerFromCachePool = false;
1949
+ if (oAppContainer.getIsFetchedFromCache && oAppContainer.getIsFetchedFromCache()) {
1950
+ bContainerFromCachePool = true;
1951
+ }
1952
+
1953
+ if (!bContainerFromCachePool) {
1954
+ AppLifeCycleAI.publishNavigationStateEvents(oAppContainer, oResolvedNavigationTarget, this.onAppAfterRendering.bind(this, oResolvedNavigationTarget));
1955
+
1956
+ if (ushellUtils.isSAPLegacyApplicationType(oAppContainer.getApplicationType(), oAppContainer.getFrameworkId())) {
1957
+ oAppContainer.addStyleClass("sapUShellApplicationContainerShiftedIframe");
1958
+ }
1959
+
1960
+ if (!bFullWidth) {
1961
+ oAppContainer.addStyleClass("sapUShellApplicationContainerLimitedWidth");
1962
+ }
1963
+
1964
+ if (this._isFloatingContainerDocked() && window.matchMedia("(min-width: 106.4rem)").matches) {
1965
+ oAppContainer.addStyleClass("sapUShellDockingContainer");
1966
+ oAppContainer.removeStyleClass("sapUShellApplicationContainerLimitedWidth");
1967
+ } else if (this._isFloatingContainerDocked()) {
1968
+ oAppContainer.removeStyleClass("sapUShellApplicationContainerLimitedWidth");
1969
+ }
1970
+
1971
+ oAppContainer.toggleStyleClass("sapUshellDefaultBackground", !oMetadata.hideLightBackground);
1972
+
1973
+ AppLifeCycleAI.getAppMeta()._applyContentDensityByPriority();
1974
+
1975
+ // Add inner control for next request
1976
+ AppLifeCycleAI.addControl(oAppContainer);
1977
+ }
1978
+
1979
+ ushellUtils.setPerformanceMark("FLP - addAppContainer");
1980
+
1981
+ Measurement.end("FLP:ShellController.getWrappedApplication");
1982
+ return oAppContainer;
1983
+ },
1984
+
1985
+ // Set booleans to false which indicate whether shellUIService was called or not
1986
+ resetShellUIServiceHandlers: function () {
1987
+ AppLifeCycleAI.getAppMeta().resetShellUIServiceHandlers();
1988
+ AppLifeCycleAI.setBackNavigationChanged(false);
1989
+ },
1990
+
1991
+ onAppAfterRendering: function (oApplication) {
1992
+ var oShellUIService = AppLifeCycleAI.getShellUIService();
1993
+ // wrapped in setTimeout since "publish" is not async
1994
+ window.setTimeout(function () {
1995
+ Core.getEventBus().publish("sap.ushell", "appOpened", oApplication);
1996
+ Log.info("app was opened");
1997
+ }, 0);
1998
+
1999
+ // publish the event externally
2000
+ // TODO: cloned, frozen object!
2001
+ var oAppOpenedEventData = AppLifeCycleAI._publicEventDataFromResolutionResult(oApplication);
2002
+
2003
+ // Event is emitted internally (EventHub) _and_ externally (for compatibility reasons)
2004
+ EventHub.emit("AppRendered", oAppOpenedEventData, true);
2005
+ RendererUtils.publishExternalEvent("appOpened", oAppOpenedEventData);
2006
+ ushellUtils.setPerformanceMark("FLP.appOpened");
2007
+
2008
+ // Call setHierarchy, setTitle, setRelatedApps with default values in case handlers were not called yet
2009
+ if (oShellUIService) {
2010
+ AppLifeCycleAI.initAppMetaParams();
2011
+ }
2012
+ oShellModel.updateStateProperty("application/icon", AppLifeCycleAI.getAppMeta().getAppIcon(), true);
2013
+ },
2014
+
2015
+ /**
2016
+ * Adds a listener to the "appComponentLoaded" Event that is published by the "sap.ushell".
2017
+ * Once the "home app" Component is saved, the listener is removed, and this function will not do anything.
2018
+ */
2019
+ _saveHomePageComponent: function () {
2020
+ if (this.oHomeApp) {
2021
+ return;
2022
+ }
2023
+ var that = this;
2024
+ var sContainerNS = "sap.ushell";
2025
+ var fListener = function (oEvent, sChannel, oData) {
2026
+ that.oHomeApp = oData.component;
2027
+ Core.getEventBus().unsubscribe(sContainerNS, "appComponentLoaded", fListener);
2028
+ };
2029
+ Core.getEventBus().subscribe(sContainerNS, "appComponentLoaded", fListener);
2030
+ },
2031
+
2032
+ /**
2033
+ * Shows an error message and navigates to the previous page.
2034
+ *
2035
+ * @param {int} iHistoryLength the length of the history <b>before</b> the navigation occurred.
2036
+ * @param {string|object} vMessage the error message
2037
+ * @param {string|object} vDetails the detailed error message
2038
+ * @param {string} sComponent the component that generated the error message
2039
+ * @param {boolean} bEnableHashChange enable hash change
2040
+ */
2041
+ hashChangeFailure: function (iHistoryLength, vMessage, vDetails, sComponent, bEnableHashChange) {
2042
+ var oDashboard = Core.byId("sapUshellDashboardPage");
2043
+ if (oDashboard) {
2044
+ oDashboard.setBlocked(false);
2045
+ oDashboard.setBusy(false);
2046
+ }
2047
+
2048
+ if (ushellUtils.isPlainObject(vMessage)) {
2049
+ this.reportError(vMessage.technicalMessage, vDetails.technicalMessage, sComponent);
2050
+ sap.ushell.Container.getServiceAsync("Message").then(function (oMessage) {
2051
+ oMessage.show(
2052
+ oMessage.Type.ERROR,
2053
+ vMessage.message,
2054
+ {
2055
+ title: vMessage.title,
2056
+ details: vDetails
2057
+ }
2058
+ );
2059
+ });
2060
+ } else {
2061
+ this.reportError(vMessage, vDetails, sComponent);
2062
+ // use timeout to avoid "MessageService not initialized.: error
2063
+ this.delayedMessageError(ushellResources.i18n.getText("fail_to_start_app_try_later"));
2064
+ }
2065
+ closeAllDialogs = false;
2066
+
2067
+ this._resumeAppRouterIgnoringCurrentHash();
2068
+ if (iHistoryLength === 0) {
2069
+ // if started with an illegal shell hash (deep link), we just remove the hash
2070
+ window.hasher.setHash("");
2071
+ } else if (new URLSearchParams(window.location.search).get("bFallbackToShellHome")) {
2072
+ // The previous url is not valid navigation
2073
+ window.hasher.setHash("");
2074
+ } else {
2075
+ // navigate to the previous URL since in this state the hash that has failed to load is in the URL.
2076
+ this.bEnableHashChange = bEnableHashChange;
2077
+ sap.ushell.Container.setDirtyFlag(bPreviousPageDirty);
2078
+ this._windowHistoryBack(1);
2079
+ }
2080
+ },
2081
+
2082
+ reportError: function (sMessage, sDetails, sComponent) {
2083
+ Log.error(sMessage, sDetails, sComponent);
2084
+ },
2085
+
2086
+ delayedMessageError: function (sMsg) {
2087
+ window.setTimeout(function () {
2088
+ if (sap.ushell.Container !== undefined) {
2089
+ sap.ushell.Container.getServiceAsync("Message").then(function (oMessage) {
2090
+ oMessage.error(sMsg);
2091
+ });
2092
+ }
2093
+ }, 0);
2094
+ },
2095
+
2096
+ fixShellHash: function (sShellHash) {
2097
+ if (!sShellHash) {
2098
+ sShellHash = "#";
2099
+ } else if (sShellHash.charAt(0) !== "#") {
2100
+ sShellHash = "#" + sShellHash;
2101
+ }
2102
+ return sShellHash;
2103
+ },
2104
+
2105
+ _openAppNewWindow: function (sUrl) {
2106
+ var oNewWin = WindowUtils.openURL(sUrl);
2107
+ // Show a message when window.open returns null --> Popup Blocker
2108
+ // Exception: in WorkZone's mobile client, when external nav happens: Keep calm and carry on.
2109
+ if (!oNewWin && !this._checkWindowLocationSearch("workzone-mobile-app=true")) {
2110
+ var msg = ushellResources.i18n.getText("fail_to_start_app_popup_blocker", [window.location.hostname]);
2111
+ this.delayedMessageError(msg);
2112
+ }
2113
+ },
2114
+
2115
+ _checkWindowLocationSearch: function (sTerm) {
2116
+ return window.location.search.indexOf(sTerm) > -1;
2117
+ },
2118
+
2119
+ _windowHistoryBack: function (iSteps) {
2120
+ window.history.go(-1 * iSteps);
2121
+ },
2122
+
2123
+ _windowHistoryForward: function (iSteps) {
2124
+ window.history.go(iSteps);
2125
+ },
2126
+
2127
+ _changeWindowLocation: function (sUrl) {
2128
+ window.location.href = sUrl;
2129
+ },
2130
+
2131
+ _setMenuVisible: function (bVisible) {
2132
+ var navigationBar = document.getElementById(ShellLayoutMapping.NavigationBar);
2133
+ if (!navigationBar) {
2134
+ return;
2135
+ }
2136
+ bVisible = bVisible || (Config.last("/core/menu/enabled") && Config.last("/core/menu/visibleInAllStates")); // visibleInAllStates:true has priority
2137
+
2138
+ navigationBar.classList.toggle("sapUshellShellHidden", !bVisible);
2139
+ },
2140
+
2141
+ /**
2142
+ * Triggered by the EventBus "appOpened" event.
2143
+ * Performs logging for recent activities and application usage
2144
+ *
2145
+ * @param {string} sChannelId The channelId of the event
2146
+ * @param {string} sEventId The event id
2147
+ * @param {object} oResolvedHashFragment The resolved hash fragment object belonging to the event
2148
+ */
2149
+ onAppOpened: function (sChannelId, sEventId, oResolvedHashFragment) {
2150
+ if (oResolvedHashFragment.targetNavigationMode !== "explace" && oResolvedHashFragment.targetNavigationMode !== "frameless") {
2151
+ this._setMenuVisible(false);
2152
+ }
2153
+
2154
+ var sAppHash = this.oShellNavigation.hashChanger.getAppHash();
2155
+ var sAppPart = sAppHash ? "&/" + sAppHash : null;
2156
+
2157
+ this.logOpenAppAction(oResolvedHashFragment, sAppPart);
2158
+
2159
+ this._notifyUITracer(oResolvedHashFragment);
2160
+ },
2161
+
2162
+ externalSearchTriggered: function (sChannelId, sEventId, oData) {
2163
+ Config.emit("/core/shell/model/searchTerm", oData.searchTerm);
2164
+ oData.query = oData.searchTerm;
2165
+ },
2166
+
2167
+ onBeforeNavigate: function (oEvent) {
2168
+ var oToView = oEvent.getParameter("to");
2169
+ var oOwnerComponent = this.getOwnerComponent();
2170
+
2171
+ var bToDashboardView = oToView === oOwnerComponent.byId("Shell-home-component-container");
2172
+ var bToPagesView = oToView === oOwnerComponent.byId("pages-component-container");
2173
+ var bToHomeAppView = oToView === oOwnerComponent.byId("homeApp-component-container");
2174
+ var bToWorkpagesView = oToView === oOwnerComponent.byId("workPageRuntime-component-container");
2175
+ var bToSwitcherView = oToView === oOwnerComponent.byId("runtimeSwitcher-component-container");
2176
+ var bMenuVisible = bToDashboardView || bToPagesView || bToHomeAppView || bToWorkpagesView || bToSwitcherView;
2177
+
2178
+ AppLifeCycleAI.onBeforeNavigate(oEvent.getParameter("fromId"), oEvent.getParameter("from"), oEvent.getParameter("toId"), oEvent.getParameter("to"));
2179
+ this._setMenuVisible(bMenuVisible);
2180
+ },
2181
+
2182
+ onAfterNavigate: function (oEvent) {
2183
+ var sToId = oEvent.mParameters ? oEvent.mParameters.toId : undefined;
2184
+
2185
+ var oDashboard = Core.byId("sapUshellDashboardPage");
2186
+ if (oDashboard) {
2187
+ oDashboard.setBlocked(false);
2188
+ oDashboard.setBusy(false);
2189
+ }
2190
+
2191
+ AppLifeCycleAI.onAfterNavigate(oEvent.getParameter("fromId"), oEvent.getParameter("from"), sToId, oEvent.getParameter("to"));
2192
+ Core.getEventBus().publish("sap.ushell", "navigated", {});
2193
+ },
2194
+
2195
+ // 1 - remove appClosed hooks
2196
+ // 2 - logApplicationUsage to take resolved hash fragment
2197
+
2198
+ logOpenAppAction: function (oResolvedHashFragment, sAppPart) {
2199
+ var bEnableRecentActivity = Config.last("/core/shell/enableRecentActivity") && Config.last("/core/shell/enableRecentActivityLogging");
2200
+ if (!bEnableRecentActivity) {
2201
+ return;
2202
+ }
2203
+
2204
+ var oRecentEntry = {};
2205
+ var oMetadata = AppConfiguration.getMetadata(oResolvedHashFragment);
2206
+ var sFixedShellHash = oResolvedHashFragment.sFixedShellHash;
2207
+ var sUrl = sFixedShellHash;
2208
+ if (sAppPart) {
2209
+ sUrl += sAppPart; // some application use inner routes, for example search.
2210
+ }
2211
+
2212
+ oRecentEntry.title = oMetadata.title;
2213
+ oRecentEntry.appType = AppType.APP; // default app type the shell adds is 'Application'
2214
+ oRecentEntry.url = sUrl;
2215
+
2216
+ var oParsed = this.oURLParsing.parseShellHash(sFixedShellHash);
2217
+ if (oParsed) {
2218
+ /*
2219
+ * This is the key that determines whether an existing activity should be updated or added.
2220
+ *
2221
+ * In theory we could use the full hash without parameters here, however this causes the same application to be logged
2222
+ * multiple times with the same title, confusing the user.
2223
+ *
2224
+ * Therefore we choose to update a previous entry in case just the parameters change. This might cause a bit of
2225
+ * confusion in case another target mapping is opened, as the title of a previously logged entry would be updated
2226
+ * instead of having a new title added to the recent activities (same target mapping but different title).
2227
+ *
2228
+ * Perhaps this could be further fixed by hashing a target mapping on the client before returning the resolution
2229
+ * result, and using the hash as the id.
2230
+ */
2231
+ oRecentEntry.appId = "#" + this.oURLParsing.constructShellHash({
2232
+ semanticObject: oParsed.semanticObject,
2233
+ action: oParsed.action
2234
+ });
2235
+ } else {
2236
+ oRecentEntry.appId = sFixedShellHash;
2237
+ }
2238
+
2239
+ // The recent activity for searches is done in a different way, see this._logSearchActivity
2240
+ if (sFixedShellHash && sFixedShellHash.indexOf("#Action-search") === -1 && sFixedShellHash.indexOf("#Shell-startIntent") === -1) {
2241
+ window.setTimeout(function () {
2242
+ this._logRecentActivity(oRecentEntry);
2243
+ }.bind(this), 1500);
2244
+ }
2245
+ },
2246
+
2247
+ /**
2248
+ * Notifies the UITracer service about the navigation (app launch) by emitting
2249
+ * a corresponding event on the event hub.
2250
+ *
2251
+ * @param {object} oResolvedHashFragment - the result of the NavTargetResolution service
2252
+ */
2253
+ _notifyUITracer: function (oResolvedHashFragment) {
2254
+ try {
2255
+ var sApplicationId = oResolvedHashFragment.ui5ComponentName;
2256
+ if (oResolvedHashFragment.reservedParameters) {
2257
+ sApplicationId =
2258
+ oResolvedHashFragment.reservedParameters["sap-fiori-id"] ||
2259
+ oResolvedHashFragment.reservedParameters["sap-ui-app-id-hint"];
2260
+ }
2261
+ EventHub.emit("UITracer.trace", {
2262
+ reason: "NavigateApp",
2263
+ source: "Intent",
2264
+ data: {
2265
+ applicationId: sApplicationId || "",
2266
+ applicationType: oResolvedHashFragment.applicationType || "",
2267
+ hash: oResolvedHashFragment.sFixedShellHash || "",
2268
+ targetNavigationMode: oResolvedHashFragment.targetNavigationMode || "",
2269
+ providerId: oResolvedHashFragment.contentProviderId || "",
2270
+ targetUrl: oResolvedHashFragment.url || ""
2271
+ }
2272
+ });
2273
+ } catch (ex) {
2274
+ Log.warning("Could not emit UITracer.trace event", ex, "sap.ushell.renderer.Shell.controller");
2275
+ }
2276
+ },
2277
+
2278
+ // Special logic for Search.
2279
+ // The search activity must be logged even after a user makes different searches in a single #Action-search session.
2280
+ // Therefore, the logging should occur on the search event and not by the navigation to the search application.
2281
+ _logSearchActivity: function () {
2282
+ if (Config.last("/core/shell/enableRecentActivity") && Config.last("/core/shell/enableRecentActivityLogging")) {
2283
+ var sTitle = "";
2284
+ try {
2285
+ sTitle = Core.byId("searchFieldInShell-input").getModel().getLastSearchTerm();
2286
+ } catch (ex) {
2287
+ Log.error("Shell: last search term is not available to log a recent activity");
2288
+ }
2289
+ this._logRecentActivity({
2290
+ appId: "#Action-search",
2291
+ appType: AppType.SEARCH,
2292
+ title: sTitle,
2293
+ url: "#" + window.hasher.getHash()
2294
+ });
2295
+ }
2296
+ },
2297
+
2298
+ readNavigationEnd: function () {
2299
+ var oAccessibilityHelperLoadingComplete = document.getElementById("sapUshellLoadingAccessibilityHelper-loadingComplete");
2300
+
2301
+ if (oAccessibilityHelperLoadingComplete) {
2302
+ oAccessibilityHelperLoadingComplete.setAttribute("aria-live", "polite");
2303
+ oAccessibilityHelperLoadingComplete.innerHTML = ushellResources.i18n.getText("loadingComplete");
2304
+ window.setTimeout(function () {
2305
+ oAccessibilityHelperLoadingComplete.setAttribute("aria-live", "off");
2306
+ oAccessibilityHelperLoadingComplete.innerHTML = "";
2307
+ }, 0);
2308
+ }
2309
+ },
2310
+
2311
+ _loadCoreExtNonUI5: function (oAppTarget) {
2312
+ if (oAppTarget && oAppTarget.applicationType !== "SAPUI5") {
2313
+ this._loadCoreExt();
2314
+ }
2315
+ },
2316
+
2317
+ _loadUsageAnalytics: function (eventData) {
2318
+ sap.ushell.Container.getServiceAsync("UsageAnalytics").then(function (oUsageAnalytics) {
2319
+ oUsageAnalytics.init(
2320
+ ushellResources.i18n.getText("usageAnalytics"),
2321
+ ushellResources.i18n.getText("i_agree"),
2322
+ ushellResources.i18n.getText("i_disagree"),
2323
+ ushellResources.i18n.getText("remind_me_later")
2324
+ );
2325
+ EventHub.emit("StepDone", eventData.stepName);
2326
+ }, function () {
2327
+ EventHub.emit("StepFailed", eventData.stepName);
2328
+ });
2329
+ },
2330
+
2331
+ /**
2332
+ * RendererExtensions plugins are loaded after the core-ext modules.
2333
+ * core-ext is loaded, either in first application load flow in case app is not FLP or explicitly by the Renderer (in this file) after FLP is loaded.
2334
+ * In any case, after we load the plugins, we also publish the event that all Core resources are loaded
2335
+ */
2336
+ _onCoreResourcesComplementLoaded: function () {
2337
+ ushellUtils.setPerformanceMark("SchedulingAgent-StartOfFlow");
2338
+ // Create delayed controls in the view
2339
+ var oView = this.getView();
2340
+ if (oView) { // some qUnits do not create the view
2341
+ oView.createPostCoreExtControls();
2342
+ }
2343
+
2344
+ SchedulingAgent._initialize();
2345
+
2346
+ EventHub.emit("startScheduler");
2347
+ },
2348
+
2349
+ /*
2350
+ * After core-ext is loaded (see_onCoreResourcesComplementLoaded) the renderer extensions plugins can be loaded.
2351
+ * To enable the Scheduling Agent to direct this the loading is wrapped in this function.
2352
+ */
2353
+ _loadRendererExtensionPlugins: function (eventData) {
2354
+ var oUriParameters = new URLSearchParams(window.location.search);
2355
+ var bDelayPlugin = oUriParameters.get("sap-ushell-xx-pluginmode") === "delayed";
2356
+
2357
+ function createPlugins (oPluginManager) {
2358
+ // in addition we have to ensure the new EventHub Event is thrown
2359
+ function fnPublishPostLoadingEvents () {
2360
+ EventHub.emit("StepDone", eventData.stepName);
2361
+ }
2362
+
2363
+ // load the plugins and always publish post events
2364
+ function fnLoadPlugins () {
2365
+ oPluginManager
2366
+ .loadPlugins("RendererExtensions")
2367
+ .always(fnPublishPostLoadingEvents);
2368
+ }
2369
+
2370
+ if (bDelayPlugin) {
2371
+ // delay loading by 5 sec.
2372
+ window.setTimeout(fnLoadPlugins, 5000);
2373
+ } else {
2374
+ fnLoadPlugins();
2375
+ }
2376
+ }
2377
+ sap.ushell.Container.getServiceAsync("PluginManager").then(createPlugins.bind(this));
2378
+ },
2379
+
2380
+ // Triggers loading of the warmup plugins via Plugin Manager
2381
+ _loadWarmupPlugins: function (eventData) {
2382
+ sap.ushell.Container.getServiceAsync("PluginManager").then(function (oPluginManager) {
2383
+ oPluginManager.loadPlugins("AppWarmup").always(function () {
2384
+ Log.debug("WARMUP plugins loaded", null, "sap.ushell.renderer.Shell");
2385
+ EventHub.emit("StepDone", eventData.stepName);
2386
+ ushellUtils.setPerformanceMark("SchedulingAgent-EndOfFlow");
2387
+ ushellUtils.setPerformanceMeasure("SchedulingAgentTotalTime", "SchedulingAgent-StartOfFlow", "SchedulingAgent-EndOfFlow");
2388
+ });
2389
+ });
2390
+ },
2391
+
2392
+ // Triggers loading of CoreExt via EventHub
2393
+ _loadCoreExt: function () {
2394
+ Measurement.end("FLP:Container.InitLoading");
2395
+ // Trigger oEventHub.once("loadCoreResourcesComplement") in case homepage is first rendered.
2396
+ // Usually this is done with resolveHashFragment, but without passing from that path we should trigger it actively.
2397
+ sap.ushell.Container.getServiceAsync("Ui5ComponentLoader").then(function () {
2398
+ EventHub.emit("loadCoreResourcesComplement", Date.now());
2399
+ });
2400
+ },
2401
+
2402
+ getCurrentViewportState: function () {
2403
+ return Config.last("/core/shell/model/currentViewPortState");
2404
+ },
2405
+
2406
+ _activateFloatingUIActions: function (iWindowWidth) {
2407
+ if (iWindowWidth < 417) {
2408
+ this.oFloatingUIActions.disable();
2409
+ } else {
2410
+ this.oFloatingUIActions.enable();
2411
+ }
2412
+ },
2413
+
2414
+ setFloatingContainerDragSelector: function (sElementToCaptureSelector) {
2415
+ if (!this._oFloatingContainerPromise) {
2416
+ this._oFloatingContainerPromise = this.getView().createFloatingContainer();
2417
+ }
2418
+
2419
+ this._oFloatingContainerPromise.then(function () {
2420
+ var oElementToCapture = document.querySelector(sElementToCaptureSelector);
2421
+ if (oElementToCapture) {
2422
+ oElementToCapture.classList.add("sapUshellFloatingContainerSelector");
2423
+ } else {
2424
+ Log.error("setFloatingContainerDragSelector could not find the element", "sap.ushell.renderer.Shell.controller");
2425
+ }
2426
+
2427
+ // Fix for internal incident #1770519876 2017 -
2428
+ // Avoiding crash of CoPilot after deleting an instance and using a property (in UIAction) of the deleted one
2429
+ sap.ui.require(["sap/ushell/UIActions"], function (UIActions) {
2430
+ if (!this.oFloatingUIActions) {
2431
+ this.oFloatingUIActions = new UIActions({
2432
+ containerSelector: ".sapUiBody",
2433
+ wrapperSelector: ".sapUshellFloatingContainerWrapper",
2434
+ draggableSelector: ".sapUshellFloatingContainerWrapper", // the element that we drag
2435
+ rootSelector: ".sapUiBody",
2436
+ cloneClass: "sapUshellFloatingContainer-clone",
2437
+ dragCallback: this._handleFloatingContainerDrag.bind(this), // for hide the original item while dragging
2438
+ endCallback: this._handleFloatingContainerDrop.bind(this),
2439
+ moveTolerance: 3,
2440
+ onDragStartUIHandler: this._onFloatingContainerDragStart.bind(this),
2441
+ onDragEndUIHandler: this._onFloatingContainerDragEnd.bind(this),
2442
+ dragAndScrollCallback: this._onFloatingContainerDrag.bind(this),
2443
+ switchModeDelay: 1000,
2444
+ isLayoutEngine: false,
2445
+ isTouch: false, // that.isTouch,
2446
+ elementToCapture: sElementToCaptureSelector,
2447
+ defaultMouseMoveHandler: function () { },
2448
+ debug: Log.getLevel() >= Log.Level.DEBUG
2449
+ });
2450
+ } else {
2451
+ this.oFloatingUIActions.elementsToCapture = jQuery(sElementToCaptureSelector);
2452
+ }
2453
+
2454
+ this._activateFloatingUIActions(jQuery(window).width());
2455
+ var timer;
2456
+ addEventListener("resize", function () {
2457
+ clearTimeout(timer);
2458
+ timer = window.setTimeout(this._activateFloatingUIActions(jQuery(window).width()), 300);
2459
+ }.bind(this));
2460
+ }.bind(this));
2461
+ }.bind(this));
2462
+ },
2463
+
2464
+ /**
2465
+ * This function called once start to drag the co-pilot element
2466
+ * It checks whether it reach 64px(4rem) to the right/left in order to open the docking area
2467
+ * Also it checks whether to close the docking area
2468
+ * @param {object} oCfg configuration parameters
2469
+ * @private
2470
+ */
2471
+ _onFloatingContainerDrag: function (oCfg) {
2472
+ Measurement.start("FLP:Shell.controller._doDock", "dragging co-pilot element", "FLP");
2473
+
2474
+ if (!this._isFloatingContainerDockSupported()) {
2475
+ Measurement.end("FLP:Shell.controller._doDock");
2476
+ return;
2477
+ }
2478
+
2479
+ var iWindowWidth = jQuery(window).width();
2480
+ if (oCfg) {
2481
+ oCfg.docked = {};
2482
+ var oDockedProp = oCfg.docked;
2483
+ // cfg.moveX get FloatingContainer courser x position.
2484
+ // handle for opening the docking area for right and left
2485
+ // in case that docking area open - close it
2486
+ // in case canvas moved (because the docking ) close it
2487
+ if (oCfg.moveX >= iWindowWidth - 64) {
2488
+ oDockedProp.dockPos = "right";
2489
+ oDockedProp.setIsDockingAreaOpen = true;
2490
+ this._handleDockingPreview(oCfg);
2491
+ } else if (oCfg.moveX < 64) {
2492
+ oDockedProp.dockPos = "left";
2493
+ oDockedProp.setIsDockingAreaOpen = true;
2494
+ this._handleDockingPreview(oCfg);
2495
+ } else {
2496
+ // prevent flickering
2497
+ if (this._isDockingAreaOpen()) {
2498
+ this._handleDockingPreview(false);
2499
+ }
2500
+ if (this._isFloatingContainerDocked()) {
2501
+ this._handleDockingPreview(false);
2502
+ this._undockFloatingContainer();
2503
+ }
2504
+
2505
+ }
2506
+ }
2507
+ Measurement.end("FLP:Shell.controller._doDock");
2508
+ },
2509
+
2510
+ _isFloatingContainerDockSupported: function () {
2511
+ // open dock option only if config is enable and screen size is L (desktop + tablet landscape)
2512
+ var oDevice = Device.media.getCurrentRange(Device.media.RANGESETS.SAP_STANDARD);
2513
+ return oDevice.name === "Desktop";
2514
+ },
2515
+
2516
+ /**
2517
+ * This method handle the finish (after drop) for the docking
2518
+ * @param {object} oDockCfg properties object
2519
+ * @private
2520
+ */
2521
+ _finishDoDock: function (oDockCfg) {
2522
+ this._handleDockingPreview(false);
2523
+ this._saveFloatingContainerState("docked:" + oDockCfg.dockPos);
2524
+
2525
+ this._moveFloatingContainerIntoDock(oDockCfg);
2526
+ const oData = {
2527
+ isDocked: (this.getFloatingContainerState() || "").startsWith("docked"),
2528
+ isVisible: this.getFloatingContainerVisibility()
2529
+ };
2530
+ // New event for co-pilot is docked.
2531
+ Core.getEventBus().publish("launchpad", "shellFloatingContainerIsDocked", oData);
2532
+ },
2533
+
2534
+ _expandFloatingContainerToDock: function () {
2535
+ var oWrapperElement = document.getElementById(ShellLayoutMapping.FloatingContainer);
2536
+ oWrapperElement.style.height = "100%";
2537
+
2538
+ Core.byId("shell-floatingContainer").addStyleClass("sapUshellShellFloatingContainerFullHeight");
2539
+ },
2540
+
2541
+ _shrinkFloatingContainerToDragUI: function () {
2542
+ var oWrapperElement = document.getElementById(ShellLayoutMapping.FloatingContainer);
2543
+ oWrapperElement.classList.remove("sapUshellContainerDocked");
2544
+
2545
+ Core.byId("shell-floatingContainer").removeStyleClass("sapUshellShellFloatingContainerFullHeight");
2546
+ jQuery(".sapUshellShellFloatingContainerFullHeight").removeClass("sapUshellShellFloatingContainerFullHeight"); // remove it also for the clone
2547
+ },
2548
+
2549
+ _emitFloatingContainerResizeEvent: function () {
2550
+ var oViewPortContainer = Core.byId("viewPortContainer");
2551
+ if (oViewPortContainer) {
2552
+ oViewPortContainer._handleSizeChange();
2553
+ }
2554
+
2555
+ // handle appFinder size changed
2556
+ // timeOut waiting for resize event is finish
2557
+ window.setTimeout(function () {
2558
+ Core.getEventBus().publish("launchpad", "appFinderWithDocking");
2559
+ }, 300);
2560
+ },
2561
+
2562
+ _undockFloatingContainer: function () {
2563
+ if (this._isFloatingContainerDocked()) {
2564
+ this._saveFloatingContainerState("floating");
2565
+
2566
+ this._shrinkFloatingContainerToDragUI();
2567
+ this._removeFloatingContainerFromDock();
2568
+ this._startFloatingContainerAnimation(false);
2569
+ }
2570
+ },
2571
+
2572
+ _handleFloatingContainerUndockOnResize: function () {
2573
+ this._undockFloatingContainer();
2574
+ const oData = {
2575
+ isDocked: (this.getFloatingContainerState() || "").startsWith("docked"),
2576
+ isVisible: this.getFloatingContainerVisibility()
2577
+ };
2578
+ Core.getEventBus().publish("launchpad", "shellFloatingContainerIsUnDockedOnResize", oData);
2579
+ },
2580
+
2581
+ /**
2582
+ * This function happens when start to drag
2583
+ * In this case if we docked we need to remove animations and close canvas
2584
+ * @private
2585
+ */
2586
+ _onFloatingContainerDragStart: function () {
2587
+ Measurement.start("FLP:Shell.controller._onDragStartUI", "start drag", "FLP");
2588
+ this._undockFloatingContainer();
2589
+ const oData = {
2590
+ isDocked: (this.getFloatingContainerState() || "").startsWith("docked"),
2591
+ isVisible: this.getFloatingContainerVisibility()
2592
+ };
2593
+ // New event for co-pilot is unDock
2594
+ Core.getEventBus().publish("launchpad", "shellFloatingContainerIsUnDocked", oData);
2595
+ Measurement.end("FLP:Shell.controller._onDragStartUI");
2596
+ },
2597
+
2598
+ /**
2599
+ * This function handle the adding animations when dock/undock
2600
+ * @param {boolean} bIsDock set docked
2601
+ * @returns {Promise} Resolves once the animation is done
2602
+ *
2603
+ * @private
2604
+ */
2605
+ _startFloatingContainerAnimation: function (bIsDock) {
2606
+ return new Promise(function (resolve, reject) {
2607
+ this._clearFloatingContainerAnimation();
2608
+ var aElements = Array.from(document.getElementsByClassName("sapUshellFloatingContainerWrapper"));
2609
+ aElements.forEach(function (oElement) {
2610
+ if (bIsDock) {
2611
+ oElement.classList.add("sapUshellContainerDockedExtendCoPilot");
2612
+ this._emitFloatingContainerResizeEvent();
2613
+ } else {
2614
+ // todo minimize animation does not work properly
2615
+ //oElement.classList.add("sapUshellContainerDockedMinimizeCoPilot");
2616
+ return;
2617
+ }
2618
+
2619
+ ["webkitAnimationEnd", "onanimationend", "msAnimationEnd", "animationend"].forEach(function (sEvent) {
2620
+ oElement.addEventListener(sEvent, function () {
2621
+ this._clearFloatingContainerAnimation();
2622
+ resolve();
2623
+ }.bind(this), { once: true });
2624
+ }.bind(this));
2625
+ }.bind(this));
2626
+ }.bind(this));
2627
+ },
2628
+
2629
+ _clearFloatingContainerAnimation: function () {
2630
+ var aElements = Array.from(document.getElementsByClassName("sapUshellFloatingContainerWrapper"));
2631
+ aElements.forEach(function (oElement) {
2632
+ oElement.classList.remove("sapUshellContainerDockedExtendCoPilot");
2633
+ oElement.classList.remove("sapUshellContainerDockedMinimizeCoPilot");
2634
+ });
2635
+ },
2636
+
2637
+ /**
2638
+ * This function opens docking area for copilot
2639
+ * @param {object} oCfg configuration object
2640
+ * @private
2641
+ */
2642
+ _handleDockingPreview: function (oCfg) {
2643
+ var oDockProperties = oCfg ? oCfg.docked : false;
2644
+ var bIsDock = oDockProperties ? oDockProperties.setIsDockingAreaOpen : false;
2645
+ // check if need to open docking area and it doesn't exist already
2646
+ if (bIsDock && jQuery("#DockingAreaDiv").length === 0) {
2647
+ var bIsRTL = Configuration.getRTL();
2648
+ if ((oDockProperties.dockPos === "right" && oCfg.clone && !bIsRTL) || (oDockProperties.dockPos === "left" && oCfg.clone && bIsRTL)) {
2649
+ jQuery('<div id="DockingAreaDiv" class="sapUshellShellDisplayDockingAreaRight">').appendTo(oCfg.clone.parentElement);
2650
+ } else if ((oDockProperties.dockPos === "left" && oCfg.clone && !bIsRTL) || (oDockProperties.dockPos === "right" && oCfg.clone && bIsRTL)) {
2651
+ jQuery('<div id="DockingAreaDiv" class="sapUshellShellDisplayDockingAreaLeft">').appendTo(oCfg.clone.parentElement);
2652
+ }
2653
+ oCfg.clone.oDockedProp = {};
2654
+ oCfg.clone.oDockedProp.dockPos = oDockProperties.dockPos;
2655
+ // After drop the copilot - docking area should disappear
2656
+ } else if (!bIsDock) {
2657
+ window.setTimeout(function () {
2658
+ jQuery(".sapUshellShellDisplayDockingAreaRight").remove();
2659
+ jQuery(".sapUshellShellDisplayDockingAreaLeft").remove();
2660
+ }, 150);
2661
+ }
2662
+ },
2663
+
2664
+ /**
2665
+ * @returns {boolean} True if co-pilot is docked. Otherwise false.
2666
+ * @private
2667
+ */
2668
+ _isFloatingContainerDocked: function () {
2669
+ return document.getElementsByClassName("sapUshellContainerDocked").length !== 0;
2670
+ },
2671
+
2672
+ /**
2673
+ * This function return whether the docking area open or not
2674
+ * @returns {boolean} if the docker area is opened
2675
+ * @private
2676
+ */
2677
+ _isDockingAreaOpen: function () {
2678
+ return document.getElementsByClassName("sapUshellShellDisplayDockingAreaRight").length !== 0
2679
+ || document.getElementsByClassName("sapUshellShellDisplayDockingAreaLeft").length !== 0;
2680
+ },
2681
+
2682
+ /**
2683
+ * This function open the canvas so there will be place for the docking area
2684
+ * @param {object} oDockedProp dock properties object
2685
+ * @private
2686
+ */
2687
+ _moveFloatingContainerIntoDock: function (oDockedProp) {
2688
+ var bIsRTL = Configuration.getRTL();
2689
+ var oWrapper = document.getElementById(ShellLayoutMapping.FloatingContainer);
2690
+ oWrapper.classList.add("sapUshellContainerDocked");
2691
+
2692
+ var sPlaceholderId;
2693
+ if ((oDockedProp.dockPos === "left" && !bIsRTL) || oDockedProp.dockPos === "right" && bIsRTL) {
2694
+ sPlaceholderId = ShellLayoutMapping.FloatingContainerDockStart;
2695
+ } else {
2696
+ sPlaceholderId = ShellLayoutMapping.FloatingContainerDockEnd;
2697
+ }
2698
+
2699
+ // Flex Layout already takes care of start / end
2700
+ if (oDockedProp.dockPos === "left") {
2701
+ oWrapper.classList.add("sapUshellContainerDockedStart");
2702
+ oWrapper.classList.remove("sapUshellContainerDockedEnd");
2703
+ } else {
2704
+ oWrapper.classList.remove("sapUshellContainerDockedStart");
2705
+ oWrapper.classList.add("sapUshellContainerDockedEnd");
2706
+ }
2707
+
2708
+ var oPlaceHolder = document.getElementById(sPlaceholderId);
2709
+ oPlaceHolder.appendChild(oWrapper);
2710
+
2711
+ this._expandFloatingContainerToDock();
2712
+ this._emitFloatingContainerResizeEvent();
2713
+ },
2714
+
2715
+ _removeFloatingContainerFromDock: function () {
2716
+ var oWrapper = document.getElementById(ShellLayoutMapping.FloatingContainer);
2717
+ document.body.appendChild(oWrapper);
2718
+
2719
+ oWrapper.classList.remove("sapUshellContainerDocked");
2720
+ oWrapper.classList.remove("sapUshellContainerDockedStart");
2721
+ oWrapper.classList.remove("sapUshellContainerDockedEnd");
2722
+
2723
+ this._emitFloatingContainerResizeEvent();
2724
+ },
2725
+
2726
+ /**
2727
+ * Handle the height of the copilot + add animations for ir
2728
+ * @param {object} oEvent event object
2729
+ * @private
2730
+ */
2731
+ _onFloatingContainerDragEnd: function (oEvent) {
2732
+ // if movement X && Y is 0 its means there is no drag was made only click
2733
+ var iWindowWidth = jQuery(window).width();
2734
+
2735
+ if (this._isFloatingContainerDocked()) {
2736
+ if (oEvent && (oEvent.clientX >= iWindowWidth - 64 || oEvent.clientX < 64)) { // if less then 64 its just a click - no need to animate
2737
+ this._startFloatingContainerAnimation(false);
2738
+ }
2739
+ } else {
2740
+ // jQuery("#sapUshellFloatingContainerWrapper").removeClass("sapUshellContainerDockedMinimizeCoPilot sapUshellContainerDockedExtendCoPilot");
2741
+ }
2742
+ },
2743
+
2744
+ _handleFloatingContainerDrop: function (oEvent, floatingContainerWrapper, oDelta) {
2745
+ Measurement.start("FLP:Shell.controller._handleFloatingContainerDrop", "drop floating container", "FLP");
2746
+ var oFloatingContainer = floatingContainerWrapper.firstChild ? Core.byId(floatingContainerWrapper.firstChild.id) : undefined;
2747
+ var oStorage = new Storage(Storage.Type.local, "com.sap.ushell.adapters.local.FloatingContainer");
2748
+ var iWindowWidth = jQuery(window).width();
2749
+ var iWindowHeight = jQuery(window).height();
2750
+ var iPosLeft = oDelta.deltaX / iWindowWidth;
2751
+ var iPosTop = -oDelta.deltaY / iWindowHeight;
2752
+ var sOrigContainerVisibility = floatingContainerWrapper.style.visibility;
2753
+ var sOrigContainerDisplay = floatingContainerWrapper.style.display;
2754
+ var iContainerLeft = parseFloat(floatingContainerWrapper.style.left.replace("%", ""));
2755
+ var iContainerTop = parseFloat(floatingContainerWrapper.style.top.replace("%", ""));
2756
+
2757
+ if (typeof (iContainerLeft) === "number") {
2758
+ iPosLeft = iContainerLeft + 100 * oDelta.deltaX / iWindowWidth;
2759
+ }
2760
+
2761
+ if (typeof (iContainerTop) === "number") {
2762
+ iPosTop = iContainerTop + 100 * oDelta.deltaY / iWindowHeight;
2763
+ }
2764
+
2765
+ // when docking area is open - means the copilot should be on top of the screen
2766
+ if (this._isDockingAreaOpen()) {
2767
+ iPosTop = 0;
2768
+ }
2769
+
2770
+ floatingContainerWrapper.style.left = iPosLeft + "%";
2771
+ floatingContainerWrapper.style.top = iPosTop + "%";
2772
+ floatingContainerWrapper.style.position = "absolute";
2773
+ floatingContainerWrapper.style.display = "block";
2774
+ floatingContainerWrapper.visibility = sOrigContainerVisibility;
2775
+ floatingContainerWrapper.display = sOrigContainerDisplay;
2776
+
2777
+ oStorage.put("floatingContainerStyle", floatingContainerWrapper.style.cssText);
2778
+ // call resizeHandler to adjust the size and position of the floating container in case it was dropped out of the window size boundaries
2779
+ if (oFloatingContainer) {
2780
+ oFloatingContainer.handleDrop();
2781
+ // when docking area is open and the copilot drop inside - should handle it
2782
+ if (oDelta.clone.oDockedProp && this._isDockingAreaOpen()) {
2783
+ this._finishDoDock(oDelta.clone.oDockedProp);
2784
+ }
2785
+ }
2786
+ Measurement.end("FLP:Shell.controller.handleFloatingContainerDrop");
2787
+ },
2788
+
2789
+ // This function called after co-pilot start to be dragged
2790
+ _handleFloatingContainerDrag: function (evt, ui) {
2791
+ Measurement.start("FLP:Shell.controller._handleFloatingContainerUIStart", "starts dragging floating container", "FLP");
2792
+ var floatingContainer = ui;
2793
+ floatingContainer.style.display = "none";
2794
+ if (window.getSelection) {
2795
+ var selection = window.getSelection();
2796
+ // for IE
2797
+ try {
2798
+ selection.removeAllRanges();
2799
+ } catch (e) {
2800
+ // continue regardless of error
2801
+ }
2802
+ }
2803
+ Measurement.end("FLP:Shell.controller._handleFloatingContainerUIStart");
2804
+ },
2805
+
2806
+ // This function open local storage and return the docked state: docked or floating
2807
+ getFloatingContainerState: function () {
2808
+ var oStorage = new Storage(Storage.Type.local, "com.sap.ushell.adapters.local.CopilotLastState");
2809
+ var sLastState = "floating";
2810
+ if (oStorage !== null) {
2811
+ sLastState = oStorage.get("lastState");
2812
+ if (sLastState === null) {
2813
+ sLastState = "floating";
2814
+ }
2815
+ }
2816
+ return sLastState;
2817
+ },
2818
+
2819
+ _saveFloatingContainerState: function (sState) {
2820
+ var oStorage = new Storage(Storage.Type.local, "com.sap.ushell.adapters.local.CopilotLastState");
2821
+ oStorage.put("lastState", sState);
2822
+ },
2823
+
2824
+ setFloatingContainerVisibility: function (bVisible) {
2825
+ if (!this._oFloatingContainerPromise) {
2826
+ this._oFloatingContainerPromise = this.getView().createFloatingContainer();
2827
+ }
2828
+
2829
+ this.getView().getModel("helper").setProperty("/shell/floatingContainerVisible", bVisible);
2830
+
2831
+ return this._oFloatingContainerPromise.then(function () {
2832
+ var sLastState = this.getFloatingContainerState() || "";
2833
+
2834
+ if (sLastState === "floating") {
2835
+ // no initial handling required
2836
+ } else if (sLastState.indexOf("docked") !== -1) {
2837
+ if (bVisible) {
2838
+ if (this._isFloatingContainerDockSupported()) {
2839
+ var sDockPos = sLastState.indexOf("right") !== -1 ? "right" : "left";
2840
+ this._moveFloatingContainerIntoDock({ dockPos: sDockPos });
2841
+ this._startFloatingContainerAnimation(true);
2842
+
2843
+ } else {
2844
+ this._saveFloatingContainerState("floating");
2845
+ }
2846
+ } else {
2847
+ // case : undock from button
2848
+ this._startFloatingContainerAnimation(false);
2849
+ }
2850
+ this._emitFloatingContainerResizeEvent();
2851
+ }
2852
+ }.bind(this));
2853
+ },
2854
+
2855
+ getFloatingContainerVisibility: function () {
2856
+ return this.getView().getModel("helper").getProperty("/shell/floatingContainerVisible");
2857
+ },
2858
+
2859
+ setFloatingContainerContent: function (sPropertyString, aIds, bCurrentState, aStates) {
2860
+ oShellModel.setFloatingContainerContent(sPropertyString, aIds, bCurrentState, aStates);
2861
+ },
2862
+
2863
+ getRightFloatingContainerVisibility: function () {
2864
+ var oRightFloatingContainer = this.getView().getRightFloatingContainer();
2865
+ var bRightFloatingContainerVisible = oRightFloatingContainer && oRightFloatingContainer.getVisible();
2866
+
2867
+ return bRightFloatingContainerVisible;
2868
+ },
2869
+
2870
+ setHeaderTitle: function (sTitle) {
2871
+ HeaderManager.updateStates({
2872
+ propertyName: "title",
2873
+ value: sTitle || "",
2874
+ aStates: ["home", "app"],
2875
+ bCurrentState: false,
2876
+ bDoNotPropagate: false
2877
+ });
2878
+ },
2879
+
2880
+ setNavigationBar: function (oNavigationBar) {
2881
+ oNavigationBar.placeAt(ShellLayoutMapping.NavigationBar, "only");
2882
+ this.addDanglingControl(oNavigationBar);
2883
+ },
2884
+
2885
+ setShellShapes: function (oShellShapes) {
2886
+ if (oShellShapes.placeAt) {
2887
+ oShellShapes.placeAt(ShellLayoutMapping.ShellShapes, "only");
2888
+ this.addDanglingControl(oShellShapes);
2889
+ } else if (oShellShapes instanceof Node) {
2890
+ document.getElementById(ShellLayoutMapping.ShellShapes).appendChild(oShellShapes);
2891
+ }
2892
+ },
2893
+
2894
+ setFooter: function (oFooter) {
2895
+ var oView = this.getView();
2896
+ var oHelperModel = oView.getModel("helper");
2897
+ if (typeof oFooter !== "object" || !oFooter.getId) {
2898
+ throw new Error("oFooter value is invalid");
2899
+ }
2900
+
2901
+ var sOldFooterId = oHelperModel.getProperty("/shell/footerContent");
2902
+ if (sOldFooterId) {
2903
+ Log.warning("You can only set one footer. Replacing existing footer: " + sOldFooterId + ", with the new footer: " + oFooter.getId() + ".");
2904
+ }
2905
+
2906
+ oHelperModel.setProperty("/shell/footerContent", oFooter.getId());
2907
+ oFooter.placeAt(ShellLayoutMapping.Footer, "only");
2908
+ this.addDanglingControl(oFooter);
2909
+
2910
+ if (oFooter._applyContextClassFor) {
2911
+ oFooter._applyContextClassFor("footer");
2912
+ }
2913
+ },
2914
+
2915
+ getFooterId: function () {
2916
+ var oView = this.getView();
2917
+ var oHelperModel = oView.getModel("helper");
2918
+ var sFooterId = oHelperModel.getProperty("/shell/footerContent");
2919
+ return sFooterId;
2920
+ },
2921
+
2922
+ removeFooter: function () {
2923
+ var oView = this.getView();
2924
+ var oHelperModel = oView.getModel("helper");
2925
+ var sFooterId = oHelperModel.getProperty("/shell/footerContent");
2926
+
2927
+ if (sFooterId) {
2928
+ var oFooter = Core.byId(sFooterId);
2929
+ if (oFooter && oFooter.destroy) {
2930
+ oFooter.destroy();
2931
+ }
2932
+ }
2933
+
2934
+ oHelperModel.setProperty("/shell/footerContent", "");
2935
+ },
2936
+
2937
+ addUserPreferencesEntry: function (entryObject, bGrouped) {
2938
+ this._validateUserPrefEntryConfiguration(entryObject, bGrouped);
2939
+ this._updateUserPrefModel(entryObject, bGrouped);
2940
+ },
2941
+
2942
+ addUserProfilingEntry: function (entryObject) {
2943
+ this._validateUserPrefEntryConfiguration(entryObject);
2944
+ this._updateProfilingModel(entryObject);
2945
+ },
2946
+
2947
+ _validateUserPrefEntryConfiguration: function (entryObject, bGrouped) {
2948
+ if ((!entryObject) || (typeof entryObject !== "object")) {
2949
+ throw new Error("object oConfig was not provided");
2950
+ }
2951
+
2952
+ if (!entryObject.title) {
2953
+ throw new Error("title was not provided");
2954
+ }
2955
+
2956
+ if (!entryObject.value) {
2957
+ throw new Error("value was not provided");
2958
+ }
2959
+
2960
+ if (typeof entryObject.entryHelpID !== "undefined") {
2961
+ if (typeof entryObject.entryHelpID !== "string") {
2962
+ throw new Error("entryHelpID type is invalid");
2963
+ } else if (entryObject.entryHelpID === "") {
2964
+ throw new Error("entryHelpID should not be an empty string");
2965
+ }
2966
+ }
2967
+
2968
+ if (entryObject.title && typeof entryObject.title !== "string") {
2969
+ throw new Error("title type is invalid");
2970
+ }
2971
+
2972
+ if (typeof entryObject.value !== "function" && typeof entryObject.value !== "string" && typeof entryObject.value !== "number") {
2973
+ throw new Error("value type is invalid");
2974
+ }
2975
+
2976
+ [
2977
+ "onSave",
2978
+ "content",
2979
+ "onCancel"
2980
+ ].forEach(function (sPropertyName) {
2981
+ if (entryObject[sPropertyName] && typeof entryObject[sPropertyName] !== "function") {
2982
+ throw new Error(sPropertyName + " type is " + typeof entryObject[sPropertyName] + " but should be a function");
2983
+ }
2984
+ });
2985
+
2986
+ if (bGrouped) {
2987
+ [
2988
+ {
2989
+ name: "groupingId",
2990
+ type: "string"
2991
+ },
2992
+ {
2993
+ name: "groupingTabTitle",
2994
+ type: "string"
2995
+ },
2996
+ {
2997
+ name: "groupingTabHelpId",
2998
+ type: "string"
2999
+ }
3000
+ ].forEach(function (oProperty) {
3001
+ if (!entryObject[oProperty.name]) {
3002
+ throw new Error(oProperty.name + " is missing");
3003
+ } else if (typeof entryObject[oProperty.name] !== oProperty.type) {
3004
+ throw new Error(oProperty.name + " type is " + typeof entryObject[oProperty.name] + " but should be a " + oProperty.type);
3005
+ }
3006
+ });
3007
+ }
3008
+ },
3009
+
3010
+ _createSessionHandler: function (oSessionConfig) {
3011
+ var that = this;
3012
+ var iLazyCreationTime = 20000;
3013
+
3014
+ sap.ui.require(["sap/ushell/SessionHandler"], function (SessionHandler) {
3015
+ that.oSessionHandler = new SessionHandler(AppLifeCycleAI);
3016
+ //we need to immediately init the logout logic that is needed
3017
+ //for cFLP without any delay
3018
+ that.oSessionHandler.initLogout();
3019
+ window.setTimeout(function () {
3020
+ that.oSessionHandler.init({
3021
+ sessionTimeoutReminderInMinutes: oSessionConfig.sessionTimeoutReminderInMinutes,
3022
+ sessionTimeoutIntervalInMinutes: oSessionConfig.sessionTimeoutIntervalInMinutes,
3023
+ sessionTimeoutTileStopRefreshIntervalInMinutes: oSessionConfig.sessionTimeoutTileStopRefreshIntervalInMinutes,
3024
+ enableAutomaticSignout: oSessionConfig.enableAutomaticSignout
3025
+ });
3026
+ }, iLazyCreationTime);
3027
+ });
3028
+ },
3029
+
3030
+ _getSessionHandler: function () {
3031
+ return this.oSessionHandler;
3032
+ },
3033
+
3034
+ _navBack: function () {
3035
+ // set meAria as closed when navigating back
3036
+ this.setUserActionsMenuSelected(false);
3037
+ AppLifeCycleAI.service().navigateBack();
3038
+ },
3039
+
3040
+ _updateUserPrefModel: function (entryObject, bGrouped) {
3041
+ var oNewEntry = this._getModelEntryFromEntryObject(entryObject);
3042
+ var aEntries = Config.last("/core/userPreferences/entries") || [];
3043
+
3044
+ if (bGrouped) {
3045
+ oNewEntry.groupingEnablement = true;
3046
+ oNewEntry.groupingId = entryObject.groupingId;
3047
+ oNewEntry.groupingTabTitle = entryObject.groupingTabTitle;
3048
+ oNewEntry.groupingTabHelpId = entryObject.groupingTabHelpId;
3049
+ }
3050
+
3051
+ aEntries.push(oNewEntry);
3052
+ // Re-order the entries array to have the Home Page entry right after the Appearance entry (if both exist)
3053
+ aEntries = this._reorderUserPrefEntries(aEntries);
3054
+ Config.emit("/core/userPreferences/entries", aEntries);
3055
+ },
3056
+
3057
+ _updateProfilingModel: function (entryObject) {
3058
+ var oNewEntry = this._getModelEntryFromEntryObject(entryObject);
3059
+ var aProfilingEntries = Config.last("/core/userPreferences/profiling") || [];
3060
+
3061
+ aProfilingEntries.push(oNewEntry);
3062
+ Config.emit("/core/userPreferences/profiling", aProfilingEntries);
3063
+ },
3064
+
3065
+ _getModelEntryFromEntryObject: function (entryObject) {
3066
+ return {
3067
+ id: ushellUtils._getUid(),
3068
+ entryHelpID: entryObject.entryHelpID,
3069
+ title: entryObject.title,
3070
+ valueArgument: entryObject.value,
3071
+ valueResult: null,
3072
+ onSave: entryObject.onSave,
3073
+ onCancel: entryObject.onCancel,
3074
+ contentFunc: entryObject.content,
3075
+ contentResult: null,
3076
+ icon: entryObject.icon,
3077
+ provideEmptyWrapper: entryObject.provideEmptyWrapper
3078
+ };
3079
+ },
3080
+
3081
+ _reorderUserPrefEntries: function (aEntries) {
3082
+ var aNewEntries = [];
3083
+ var aOrderedIds = [
3084
+ "userAccountEntry",
3085
+ "themes",
3086
+ "homepageEntry",
3087
+ "spacesEntry",
3088
+ "userActivitiesEntry",
3089
+ "userProfiling",
3090
+ "language",
3091
+ "notificationsEntry",
3092
+ "userDefaultEntry"
3093
+ ];
3094
+ var mSpecialEntries = {};
3095
+
3096
+ for (var i = aEntries.length - 1; i >= 0; i--) {
3097
+ if (aOrderedIds.indexOf(aEntries[i].id) !== -1) {
3098
+ var oSpecialEntry = aEntries.splice(i, 1)[0];
3099
+ mSpecialEntries[oSpecialEntry.id] = oSpecialEntry;
3100
+ }
3101
+ }
3102
+
3103
+ aOrderedIds.forEach(function (sId) {
3104
+ var oEntry = mSpecialEntries[sId];
3105
+ if (oEntry) {
3106
+ aNewEntries.push(oEntry);
3107
+ }
3108
+ });
3109
+
3110
+ return aNewEntries.concat(aEntries);
3111
+ },
3112
+
3113
+ getModel: function () {
3114
+ return oModel;
3115
+ },
3116
+
3117
+ _getConfig: function () {
3118
+ return oConfig || {};
3119
+ },
3120
+
3121
+ _getPersData: function (oPersonalizationId) {
3122
+ var oComponent = Component.getOwnerComponentFor(this.getView());
3123
+ return sap.ushell.Container.getServiceAsync("PersonalizationV2").then(async function (oPersonalizationService) {
3124
+ var oScope = {
3125
+ keyCategory: oPersonalizationService.constants.keyCategory.FIXED_KEY,
3126
+ writeFrequency: oPersonalizationService.constants.writeFrequency.LOW,
3127
+ clientStorageAllowed: true
3128
+ };
3129
+ var oPersonalizer = await oPersonalizationService.getPersonalizer(oPersonalizationId, oScope, oComponent);
3130
+ return oPersonalizer.getPersData();
3131
+ });
3132
+ },
3133
+
3134
+ // encapsulate access to location so that we can stub it easily in tests
3135
+ _getCurrentLocationHash: function () {
3136
+ return window.location.hash;
3137
+ },
3138
+
3139
+ setUserActionsMenuSelected: function (bSelected) {
3140
+ EventHub.emit("showUserActionsMenu", bSelected);
3141
+ },
3142
+
3143
+ getUserActionsMenuSelected: function () {
3144
+ return Config.last("/core/shell/model/currentViewPortState") === "LeftCenter";
3145
+ },
3146
+
3147
+ setNotificationsSelected: function (bSelected) {
3148
+ EventHub.emit("showNotifications", bSelected);
3149
+ },
3150
+
3151
+ getNotificationsSelected: function () {
3152
+ return Config.last("/core/shell/model/currentViewPortState") === "RightCenter";
3153
+ }
3154
+ });
3155
+ });