@sapui5/sap.fe.core 1.98.0 → 1.99.2

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