@qbs-origin/origin-form 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/README.md +24 -0
  2. package/esm2022/lib/action-step-handler.mjs +163 -0
  3. package/esm2022/lib/auth-client.service.mjs +69 -0
  4. package/esm2022/lib/enums/label.keys.mjs +721 -0
  5. package/esm2022/lib/form-css.helper.mjs +367 -0
  6. package/esm2022/lib/formly/baseFormlyControlComponent.mjs +52 -0
  7. package/esm2022/lib/formly/baseFormlyStepComponent.mjs +59 -0
  8. package/esm2022/lib/formly/custom-section-separator.component.mjs +32 -0
  9. package/esm2022/lib/formly/form-section-separator.component.mjs +36 -0
  10. package/esm2022/lib/formly/formly-action.mjs +56 -0
  11. package/esm2022/lib/formly/formly-checkbox/formly-checkbox.component.mjs +52 -0
  12. package/esm2022/lib/formly/formly-dictionary-dropdown-tree/formly-dictionary-dropdown-tree.component.mjs +261 -0
  13. package/esm2022/lib/formly/formly-download-documents/formly-download-documents.component.mjs +126 -0
  14. package/esm2022/lib/formly/formly-enrol-card/formly-enrol-card.component.mjs +120 -0
  15. package/esm2022/lib/formly/formly-field-stepper/formly-field-stepper.component.mjs +762 -0
  16. package/esm2022/lib/formly/formly-generate-documents/formly-generate-documents.component.mjs +57 -0
  17. package/esm2022/lib/formly/formly-identification.component.mjs +84 -0
  18. package/esm2022/lib/formly/formly-open-banking/formly-open-banking.component.mjs +590 -0
  19. package/esm2022/lib/formly/formly-paragraph/formly-paragraph.component.mjs +35 -0
  20. package/esm2022/lib/formly/formly-radio/formly-radio-component.mjs +49 -0
  21. package/esm2022/lib/formly/formly-row-fille.mjs +12 -0
  22. package/esm2022/lib/formly/formly-scan-id/formly-scan-id.component.mjs +284 -0
  23. package/esm2022/lib/formly/formly-sign/formly-sign.component.mjs +173 -0
  24. package/esm2022/lib/formly/formly-upload-documents/formly-upload-documents.component.mjs +198 -0
  25. package/esm2022/lib/formly/formly-validate-contact-info/formly-validate-contact-info.component.mjs +124 -0
  26. package/esm2022/lib/formly/formly-view-documents/formly-view-documents.component.mjs +245 -0
  27. package/esm2022/lib/formly/formly-view-offers/formly-view-offers.component.mjs +160 -0
  28. package/esm2022/lib/model-population.helper.mjs +265 -0
  29. package/esm2022/lib/models/application-type.model.mjs +12 -0
  30. package/esm2022/lib/models/application.model.mjs +30 -0
  31. package/esm2022/lib/models/auth/users.model.mjs +2 -0
  32. package/esm2022/lib/models/dictionary.model.mjs +20 -0
  33. package/esm2022/lib/models/flux.model.mjs +105 -0
  34. package/esm2022/lib/models/forms.model.mjs +572 -0
  35. package/esm2022/lib/models/label-info.model.mjs +2 -0
  36. package/esm2022/lib/models/label.model.mjs +2 -0
  37. package/esm2022/lib/models/language.model.mjs +3 -0
  38. package/esm2022/lib/models/list.model.mjs +2 -0
  39. package/esm2022/lib/models/partner.model.mjs +3 -0
  40. package/esm2022/lib/models/treeview.model.mjs +15 -0
  41. package/esm2022/lib/origin-form-auth.service.mjs +40 -0
  42. package/esm2022/lib/origin-form-config.model.mjs +2 -0
  43. package/esm2022/lib/origin-form-token.interceptor.mjs +35 -0
  44. package/esm2022/lib/origin-form.component.mjs +2391 -0
  45. package/esm2022/lib/origin-form.module.mjs +479 -0
  46. package/esm2022/lib/origin-form.service.mjs +14 -0
  47. package/esm2022/lib/others/check-list.database.mjs +55 -0
  48. package/esm2022/lib/others/config-service.mjs +42 -0
  49. package/esm2022/lib/others/dictionary-label-info.mjs +3 -0
  50. package/esm2022/lib/others/environment-type.mjs +21 -0
  51. package/esm2022/lib/others/external-link.directive.mjs +49 -0
  52. package/esm2022/lib/others/flux-helper.mjs +1397 -0
  53. package/esm2022/lib/others/picker.component.mjs +119 -0
  54. package/esm2022/lib/others/translation.pipe.mjs +21 -0
  55. package/esm2022/lib/others/translations-helper.mjs +258 -0
  56. package/esm2022/lib/others/utils.mjs +272 -0
  57. package/esm2022/lib/services/applicationData.service.mjs +145 -0
  58. package/esm2022/lib/services/auth-http.service.mjs +80 -0
  59. package/esm2022/lib/services/dialog.service.mjs +56 -0
  60. package/esm2022/lib/services/dictionary.service.mjs +198 -0
  61. package/esm2022/lib/services/forms.service.mjs +47 -0
  62. package/esm2022/lib/services/labels.service.mjs +29 -0
  63. package/esm2022/lib/services/language.service.mjs +24 -0
  64. package/esm2022/lib/services/open-banking.service.mjs +194 -0
  65. package/esm2022/lib/services/origin-form-signalr-handler.service.mjs +107 -0
  66. package/esm2022/lib/services/origin-form-signalr.service.mjs +105 -0
  67. package/esm2022/lib/services/otp.service.mjs +28 -0
  68. package/esm2022/lib/services/proxy.service.mjs +79 -0
  69. package/esm2022/lib/services/scroll-to-error.service.mjs +369 -0
  70. package/esm2022/lib/services/translation.service.mjs +27 -0
  71. package/esm2022/lib/shared-components/confirmation.component.mjs +34 -0
  72. package/esm2022/lib/shared-components/dictionaries-tree.component.mjs +301 -0
  73. package/esm2022/lib/shared-components/grid.component.mjs +241 -0
  74. package/esm2022/lib/shared-components/treeview/treeview.component.mjs +224 -0
  75. package/esm2022/lib/theme-css.mjs +2254 -0
  76. package/esm2022/lib/theme-injector.service.mjs +26 -0
  77. package/esm2022/public-api.mjs +4 -0
  78. package/esm2022/qbs-origin-origin-form.mjs +5 -0
  79. package/fesm2022/qbs-origin-origin-form.mjs +15215 -0
  80. package/fesm2022/qbs-origin-origin-form.mjs.map +1 -0
  81. package/index.d.ts +5 -0
  82. package/lib/action-step-handler.d.ts +49 -0
  83. package/lib/auth-client.service.d.ts +17 -0
  84. package/lib/enums/label.keys.d.ts +720 -0
  85. package/lib/form-css.helper.d.ts +28 -0
  86. package/lib/formly/baseFormlyControlComponent.d.ts +25 -0
  87. package/lib/formly/baseFormlyStepComponent.d.ts +29 -0
  88. package/lib/formly/custom-section-separator.component.d.ts +6 -0
  89. package/lib/formly/form-section-separator.component.d.ts +10 -0
  90. package/lib/formly/formly-action.d.ts +13 -0
  91. package/lib/formly/formly-checkbox/formly-checkbox.component.d.ts +15 -0
  92. package/lib/formly/formly-dictionary-dropdown-tree/formly-dictionary-dropdown-tree.component.d.ts +45 -0
  93. package/lib/formly/formly-download-documents/formly-download-documents.component.d.ts +22 -0
  94. package/lib/formly/formly-enrol-card/formly-enrol-card.component.d.ts +114 -0
  95. package/lib/formly/formly-field-stepper/formly-field-stepper.component.d.ts +79 -0
  96. package/lib/formly/formly-generate-documents/formly-generate-documents.component.d.ts +17 -0
  97. package/lib/formly/formly-identification.component.d.ts +19 -0
  98. package/lib/formly/formly-open-banking/formly-open-banking.component.d.ts +119 -0
  99. package/lib/formly/formly-paragraph/formly-paragraph.component.d.ts +10 -0
  100. package/lib/formly/formly-radio/formly-radio-component.d.ts +15 -0
  101. package/lib/formly/formly-row-fille.d.ts +6 -0
  102. package/lib/formly/formly-scan-id/formly-scan-id.component.d.ts +41 -0
  103. package/lib/formly/formly-sign/formly-sign.component.d.ts +36 -0
  104. package/lib/formly/formly-upload-documents/formly-upload-documents.component.d.ts +25 -0
  105. package/lib/formly/formly-validate-contact-info/formly-validate-contact-info.component.d.ts +79 -0
  106. package/lib/formly/formly-view-documents/formly-view-documents.component.d.ts +33 -0
  107. package/lib/formly/formly-view-offers/formly-view-offers.component.d.ts +23 -0
  108. package/lib/model-population.helper.d.ts +8 -0
  109. package/lib/models/application-type.model.d.ts +27 -0
  110. package/lib/models/application.model.d.ts +107 -0
  111. package/lib/models/auth/users.model.d.ts +20 -0
  112. package/lib/models/dictionary.model.d.ts +77 -0
  113. package/lib/models/flux.model.d.ts +101 -0
  114. package/lib/models/forms.model.d.ts +504 -0
  115. package/lib/models/label-info.model.d.ts +10 -0
  116. package/lib/models/label.model.d.ts +4 -0
  117. package/lib/models/language.model.d.ts +5 -0
  118. package/lib/models/list.model.d.ts +8 -0
  119. package/lib/models/partner.model.d.ts +12 -0
  120. package/lib/models/treeview.model.d.ts +17 -0
  121. package/lib/origin-form-auth.service.d.ts +15 -0
  122. package/lib/origin-form-config.model.d.ts +12 -0
  123. package/lib/origin-form-token.interceptor.d.ts +12 -0
  124. package/lib/origin-form.component.d.ts +231 -0
  125. package/lib/origin-form.module.d.ts +84 -0
  126. package/lib/origin-form.service.d.ts +6 -0
  127. package/lib/others/check-list.database.d.ts +16 -0
  128. package/lib/others/config-service.d.ts +22 -0
  129. package/lib/others/dictionary-label-info.d.ts +6 -0
  130. package/lib/others/environment-type.d.ts +8 -0
  131. package/lib/others/external-link.directive.d.ts +12 -0
  132. package/lib/others/flux-helper.d.ts +115 -0
  133. package/lib/others/picker.component.d.ts +36 -0
  134. package/lib/others/translation.pipe.d.ts +10 -0
  135. package/lib/others/translations-helper.d.ts +31 -0
  136. package/lib/others/utils.d.ts +37 -0
  137. package/lib/services/applicationData.service.d.ts +35 -0
  138. package/lib/services/auth-http.service.d.ts +21 -0
  139. package/lib/services/dialog.service.d.ts +20 -0
  140. package/lib/services/dictionary.service.d.ts +89 -0
  141. package/lib/services/forms.service.d.ts +17 -0
  142. package/lib/services/labels.service.d.ts +13 -0
  143. package/lib/services/language.service.d.ts +14 -0
  144. package/lib/services/open-banking.service.d.ts +137 -0
  145. package/lib/services/origin-form-signalr-handler.service.d.ts +29 -0
  146. package/lib/services/origin-form-signalr.service.d.ts +24 -0
  147. package/lib/services/otp.service.d.ts +22 -0
  148. package/lib/services/proxy.service.d.ts +29 -0
  149. package/lib/services/scroll-to-error.service.d.ts +54 -0
  150. package/lib/services/translation.service.d.ts +10 -0
  151. package/lib/shared-components/confirmation.component.d.ts +77 -0
  152. package/lib/shared-components/dictionaries-tree.component.d.ts +51 -0
  153. package/lib/shared-components/grid.component.d.ts +138 -0
  154. package/lib/shared-components/treeview/treeview.component.d.ts +121 -0
  155. package/lib/theme-css.d.ts +2 -0
  156. package/lib/theme-injector.service.d.ts +8 -0
  157. package/package.json +42 -0
  158. package/public-api.d.ts +3 -0
  159. package/schematics-compiled/collection.json +10 -0
  160. package/schematics-compiled/ng-add/index.d.ts +2 -0
  161. package/schematics-compiled/ng-add/index.js +67 -0
  162. package/schematics-compiled/ng-add/index.js.map +1 -0
  163. package/schematics-compiled/ng-add/schema.json +8 -0
  164. package/src/lib/assets/fonts/Figtree-Bold.ttf +0 -0
  165. package/src/lib/assets/fonts/Figtree-Light.ttf +0 -0
  166. package/src/lib/assets/fonts/Figtree-Regular.ttf +0 -0
  167. package/src/lib/assets/fonts/Sora-ExtraBold.ttf +0 -0
  168. package/src/lib/assets/fonts/Sora-Light.ttf +0 -0
  169. package/src/lib/assets/fonts/Sora-Regular.ttf +0 -0
  170. package/src/lib/assets/fonts/ttrounds-bold-webfont.woff +0 -0
  171. package/src/lib/assets/fonts/ttrounds-bold-webfont.woff2 +0 -0
  172. package/src/lib/assets/fonts/ttrounds-regular-webfont.woff +0 -0
  173. package/src/lib/assets/fonts/ttrounds-regular-webfont.woff2 +0 -0
  174. package/src/lib/assets/fonts/ttrounds-thin-webfont.woff +0 -0
  175. package/src/lib/assets/fonts/ttrounds-thin-webfont.woff2 +0 -0
  176. package/src/lib/assets/images/flag/icon-flag-de.svg +10 -0
  177. package/src/lib/assets/images/flag/icon-flag-en.svg +1 -0
  178. package/src/lib/assets/images/flag/icon-flag-es.svg +11 -0
  179. package/src/lib/assets/images/flag/icon-flag-fr.svg +1 -0
  180. package/src/lib/assets/images/flag/icon-flag-ro.svg +11 -0
  181. package/src/lib/assets/images/flag/origin-form/new-id-card.png +0 -0
  182. package/src/lib/assets/images/flag/origin-form/old-id-card.png +0 -0
@@ -0,0 +1,590 @@
1
+ import { Component, } from '@angular/core';
2
+ import { FormControl } from '@angular/forms';
3
+ import { BehaviorSubject, interval } from 'rxjs';
4
+ import { switchMap, takeWhile, finalize } from 'rxjs/operators';
5
+ import { BaseFormlyStepComponent } from '../baseFormlyStepComponent';
6
+ import * as i0 from "@angular/core";
7
+ import * as i1 from "../../services/open-banking.service";
8
+ import * as i2 from "../../services/dialog.service";
9
+ import * as i3 from "@angular/platform-browser";
10
+ import * as i4 from "@angular/common";
11
+ import * as i5 from "@angular/material/button";
12
+ import * as i6 from "@angular/material/icon";
13
+ import * as i7 from "@angular/material/progress-spinner";
14
+ var OpenBankingState;
15
+ (function (OpenBankingState) {
16
+ OpenBankingState["Initial"] = "initial";
17
+ OpenBankingState["BankSelection"] = "bankSelection";
18
+ OpenBankingState["Consent"] = "consent";
19
+ OpenBankingState["Processing"] = "processing";
20
+ OpenBankingState["Error"] = "error";
21
+ OpenBankingState["Complete"] = "complete";
22
+ })(OpenBankingState || (OpenBankingState = {}));
23
+ export class FormlyOpenBankingComponent extends BaseFormlyStepComponent {
24
+ constructor(openBankingService, dialog, sanitizer, cdr) {
25
+ super(cdr);
26
+ this.openBankingService = openBankingService;
27
+ this.dialog = dialog;
28
+ this.sanitizer = sanitizer;
29
+ this.config = {};
30
+ this.currentState = new BehaviorSubject(OpenBankingState.Initial);
31
+ this.banks = [];
32
+ this.selectedBank = '';
33
+ this.authorizationUrl = null;
34
+ this.consentId = '';
35
+ this._errorMessage = '';
36
+ this._isConsentError = false;
37
+ this._consentErrorStatus = '';
38
+ this.isLoading = false;
39
+ this.isLoadingSubject = new BehaviorSubject(false);
40
+ this.accounts = [];
41
+ this.transactions = [];
42
+ this.selectedAccounts = [];
43
+ this.transactionPages = new Map();
44
+ }
45
+ async ngOnInit() {
46
+ await super.ngOnInit();
47
+ if (this.stepData?.config) {
48
+ this.config = this.stepData.config;
49
+ }
50
+ // If translations are in stepData.translations, merge them into config
51
+ if (this.stepData?.translations) {
52
+ this.mergeTranslationsIntoConfig();
53
+ }
54
+ }
55
+ mergeTranslationsIntoConfig() {
56
+ // Map translation names to config properties
57
+ const translationMap = {
58
+ 'initialMessageTranslations': 'initialMessageTranslations',
59
+ 'waitingMessageTranslations': 'waitingMessageTranslations',
60
+ 'consentErrorMessageTranslations': 'consentErrorMessageTranslations',
61
+ 'completionMessageTranslations': 'completionMessageTranslations',
62
+ 'retryButtonLabelTranslations': 'retryButtonLabelTranslations',
63
+ 'nextButtonTranslations': 'nextButtonTranslations',
64
+ 'continueButtonTranslations': 'continueButtonTranslations',
65
+ 'selectBankTranslations': 'selectBankTranslations',
66
+ 'consentTitleTranslations': 'consentTitleTranslations',
67
+ 'consentDescriptionTranslations': 'consentDescriptionTranslations',
68
+ 'waitingForAuthorizationTranslations': 'waitingForAuthorizationTranslations',
69
+ 'popupWindowMessageTranslations': 'popupWindowMessageTranslations',
70
+ 'popupBlockerMessageTranslations': 'popupBlockerMessageTranslations',
71
+ 'accountsRetrievedTranslations': 'accountsRetrievedTranslations',
72
+ 'transactionsRetrievedTranslations': 'transactionsRetrievedTranslations',
73
+ 'accountsFoundTranslations': 'accountsFoundTranslations',
74
+ 'transactionsSummaryTranslations': 'transactionsSummaryTranslations'
75
+ };
76
+ this.stepData.translations?.forEach((translation) => {
77
+ const configKey = translationMap[translation.name];
78
+ if (configKey && translation.values && translation.values.length > 0) {
79
+ // Use the translation values from stepData if config doesn't have all languages
80
+ this.config[configKey] = translation.values;
81
+ }
82
+ });
83
+ }
84
+ ngAfterViewInit() {
85
+ // Only set initial state if we haven't already set a state
86
+ if (!this.currentState.value) {
87
+ this.currentState.next(OpenBankingState.Initial);
88
+ }
89
+ }
90
+ onPageSelected() {
91
+ // Only reset to initial if we haven't started the flow yet
92
+ // This prevents reset when language changes
93
+ if (!this.currentState.value) {
94
+ this.currentState.next(OpenBankingState.Initial);
95
+ }
96
+ // Update language if it changed
97
+ this.cdr.detectChanges();
98
+ }
99
+ determineNextStep() {
100
+ // Get account information from form if configured
101
+ const accountNumber = this.getFieldValue(this.config.accountNumberCollected);
102
+ // If we should extract bank from IBAN and have an IBAN
103
+ if (this.config.extractBankFromIBAN && accountNumber && this.isIBAN(accountNumber)) {
104
+ // Load banks first, then match IBAN to bank
105
+ this.loadBanksAndMatchIBAN(accountNumber);
106
+ return;
107
+ }
108
+ // If we should show all banks
109
+ if (this.config.showAllBanks) {
110
+ this.loadBanks();
111
+ }
112
+ else {
113
+ // Go directly to consent without bank selection
114
+ this.initiateConsent();
115
+ }
116
+ }
117
+ getFieldValue(fieldId) {
118
+ if (!fieldId)
119
+ return '';
120
+ return this.form.get(fieldId)?.value || '';
121
+ }
122
+ isIBAN(value) {
123
+ // Basic IBAN validation for Romanian IBANs
124
+ return /^RO\d{2}[A-Z]{4}\d{16}$/.test(value.replace(/\s/g, ''));
125
+ }
126
+ extractBankFromIBAN(iban) {
127
+ // Extract bank code from Romanian IBAN (positions 5-8)
128
+ const cleanIban = iban.replace(/\s/g, '');
129
+ if (cleanIban.startsWith('RO') && cleanIban.length >= 8) {
130
+ return cleanIban.substring(4, 8);
131
+ }
132
+ return '';
133
+ }
134
+ loadBanksAndMatchIBAN(iban) {
135
+ this.setLoadingState(true);
136
+ const countryCode = 'RO'; // Default to Romania
137
+ this.openBankingService.getBanks(countryCode).subscribe({
138
+ next: (banks) => {
139
+ this.banks = banks.filter(b => b.isActive);
140
+ // Extract bank code from IBAN
141
+ const ibanBankCode = this.extractBankFromIBAN(iban);
142
+ if (ibanBankCode) {
143
+ // Try to match against bank codes
144
+ const matchedBank = this.banks.find(bank => bank.bankCode.toUpperCase() === ibanBankCode.toUpperCase());
145
+ if (matchedBank) {
146
+ // Bank found - select it and go directly to consent
147
+ this.selectedBank = matchedBank.bankCode;
148
+ this.selectedBankObject = matchedBank;
149
+ this.setLoadingState(false);
150
+ this.initiateConsent();
151
+ return;
152
+ }
153
+ }
154
+ // If no match found, show bank selection
155
+ this.currentState.next(OpenBankingState.BankSelection);
156
+ this.setLoadingState(false);
157
+ },
158
+ error: (error) => {
159
+ this.handleError(error);
160
+ this.setLoadingState(false);
161
+ },
162
+ });
163
+ }
164
+ loadBanks() {
165
+ this.setLoadingState(true);
166
+ const countryCode = 'RO'; // Default to Romania
167
+ this.openBankingService.getBanks(countryCode).subscribe({
168
+ next: (banks) => {
169
+ this.banks = banks.filter(b => b.isActive);
170
+ this.currentState.next(OpenBankingState.BankSelection);
171
+ this.setLoadingState(false);
172
+ },
173
+ error: (error) => {
174
+ this.handleError(error);
175
+ this.setLoadingState(false);
176
+ },
177
+ });
178
+ }
179
+ selectBank(bank) {
180
+ this.selectedBank = bank.bankCode;
181
+ this.selectedBankObject = bank;
182
+ }
183
+ proceedToNextStep() {
184
+ if (this.currentState.value === OpenBankingState.Initial) {
185
+ // User clicked Next on initial screen - determine what to show next
186
+ this.determineNextStep();
187
+ }
188
+ else if (this.currentState.value === OpenBankingState.BankSelection) {
189
+ if (this.selectedBank) {
190
+ this.initiateConsent();
191
+ }
192
+ }
193
+ }
194
+ initiateConsent() {
195
+ this.setLoadingState(true);
196
+ // Get user information
197
+ const email = this.getFieldValue(this.config.emailCollected) || 'user@example.com';
198
+ // Generate a unique PSU ID for this session
199
+ const psuId = this.appDataId || 'user-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
200
+ const consentRequest = {
201
+ bankCode: this.selectedBank || 'DEFAULT',
202
+ psuEmail: email,
203
+ psuIntermediaryId: psuId,
204
+ periodOfValidity: 90,
205
+ tcAccepted: this.config.tcAcceptedByDefault !== false
206
+ };
207
+ this.openBankingService.initializeConsent(consentRequest).subscribe({
208
+ next: (response) => {
209
+ this.consentId = response.consentId;
210
+ // Check if bank requires redirect
211
+ if (this.selectedBankObject?.requiresRedirect !== false) {
212
+ // Try iframe first, but most banks will block it
213
+ if (this.shouldUseIframe()) {
214
+ this.authorizationUrl = this.sanitizer.bypassSecurityTrustResourceUrl(response.authorizationUrl);
215
+ this.currentState.next(OpenBankingState.Consent);
216
+ // Set a flag to detect if iframe fails
217
+ this.checkIframeLoad(response.authorizationUrl);
218
+ }
219
+ else {
220
+ // Open in new window/tab
221
+ this.openConsentInNewWindow(response.authorizationUrl);
222
+ }
223
+ }
224
+ this.setLoadingState(false);
225
+ // Start polling for consent status
226
+ this.startConsentPolling();
227
+ },
228
+ error: (error) => {
229
+ this.handleError(error);
230
+ this.setLoadingState(false);
231
+ },
232
+ });
233
+ }
234
+ shouldUseIframe() {
235
+ // Most banks don't allow iframes due to X-Frame-Options
236
+ // So default to false (use popup window instead)
237
+ return false;
238
+ }
239
+ openConsentInNewWindow(authorizationUrl) {
240
+ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
241
+ let consentWindow;
242
+ if (isMobile) {
243
+ // On mobile, open in same tab (full redirect) or new tab
244
+ // Using _blank will open a new tab and allow user to return
245
+ consentWindow = window.open(authorizationUrl, '_blank');
246
+ }
247
+ else {
248
+ // On desktop, open in a popup window
249
+ const width = 800;
250
+ const height = 600;
251
+ const left = (window.screen.width - width) / 2;
252
+ const top = (window.screen.height - height) / 2;
253
+ consentWindow = window.open(authorizationUrl, 'bankConsent', `width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no,scrollbars=yes,resizable=yes`);
254
+ }
255
+ if (consentWindow) {
256
+ // Show a message that consent is in progress
257
+ this.currentState.next(OpenBankingState.Consent);
258
+ // Check if window is closed periodically (works better on desktop)
259
+ if (!isMobile) {
260
+ const checkWindowClosed = setInterval(() => {
261
+ if (consentWindow && consentWindow.closed) {
262
+ clearInterval(checkWindowClosed);
263
+ // Window was closed, the polling will detect the consent status
264
+ }
265
+ }, 1000);
266
+ }
267
+ // On mobile, we rely entirely on polling since window.closed doesn't work reliably
268
+ }
269
+ else {
270
+ // Popup was blocked
271
+ this._isConsentError = false;
272
+ this._errorMessage = 'Please allow popups for this site to complete bank authorization. Then click Retry to try again.';
273
+ this.currentState.next(OpenBankingState.Error);
274
+ }
275
+ }
276
+ checkIframeLoad(authorizationUrl) {
277
+ // If iframe fails to load (X-Frame-Options), fall back to popup
278
+ setTimeout(() => {
279
+ // Check if we're still in consent state (iframe might have failed)
280
+ if (this.currentState.value === OpenBankingState.Consent && this.authorizationUrl) {
281
+ // Clear iframe and open in new window
282
+ console.warn('Iframe might be blocked, opening in new window');
283
+ this.authorizationUrl = null;
284
+ this.openConsentInNewWindow(authorizationUrl);
285
+ }
286
+ }, 2000);
287
+ }
288
+ startConsentPolling() {
289
+ // Poll every 5 seconds for consent status (user needs time to complete bank authorization)
290
+ this.pollSubscription = interval(10000)
291
+ .pipe(switchMap(() => this.openBankingService.getConsentStatus(this.selectedBank || 'DEFAULT', this.consentId)), takeWhile((status) => status.status === 'received', true), finalize(() => this.pollSubscription?.unsubscribe()))
292
+ .subscribe({
293
+ next: (status) => {
294
+ if (status.status === 'valid') {
295
+ this.onConsentApproved();
296
+ }
297
+ else if (status.status === 'rejected' ||
298
+ status.status === 'expired' ||
299
+ status.status === 'terminated' ||
300
+ status.status === 'revokedByPsu') {
301
+ this.onConsentRejected(status);
302
+ }
303
+ },
304
+ error: (error) => {
305
+ this.handleError(error);
306
+ },
307
+ });
308
+ }
309
+ onConsentApproved() {
310
+ this.currentState.next(OpenBankingState.Processing);
311
+ this.fetchAccountData();
312
+ }
313
+ onConsentRejected(status) {
314
+ this._isConsentError = true;
315
+ this._consentErrorStatus = status.status;
316
+ this._errorMessage = ''; // Clear to use translated message
317
+ this.currentState.next(OpenBankingState.Error);
318
+ }
319
+ fetchAccountData() {
320
+ this.setLoadingState(true);
321
+ // First, fetch accounts
322
+ this.openBankingService.getAccounts(this.consentId, this.selectedBank || 'DEFAULT').subscribe({
323
+ next: (accounts) => {
324
+ this.accounts = accounts;
325
+ // Select all accounts for transaction fetching
326
+ this.selectedAccounts = accounts.map(a => a.accountId);
327
+ // Fetch transactions for each account
328
+ this.fetchTransactionsForAccounts();
329
+ },
330
+ error: (error) => {
331
+ this.handleError(error);
332
+ this.setLoadingState(false);
333
+ },
334
+ });
335
+ }
336
+ fetchTransactionsForAccounts() {
337
+ if (this.selectedAccounts.length === 0) {
338
+ this.completeProcess();
339
+ return;
340
+ }
341
+ const transactionRequests = this.selectedAccounts.map(accountId => {
342
+ // Calculate date range based on bank-specific limits
343
+ const bankCode = this.selectedBank || 'DEFAULT';
344
+ const maxDays = this.openBankingService.getMaxTransactionPeriod(bankCode);
345
+ const toDate = new Date();
346
+ const fromDate = new Date();
347
+ // If unlimited (-1) or very long period, use 365 days as practical limit
348
+ // Otherwise use the bank's specific limit
349
+ const daysToFetch = maxDays === -1 ? 365 : Math.min(maxDays, 365);
350
+ fromDate.setDate(fromDate.getDate() - daysToFetch);
351
+ return this.openBankingService.getTransactions({
352
+ consentId: this.consentId,
353
+ bankCode: bankCode,
354
+ accountId: accountId,
355
+ fromDate: fromDate.toISOString(),
356
+ toDate: toDate.toISOString(),
357
+ page: 1,
358
+ pageSize: 50
359
+ });
360
+ });
361
+ // Execute all transaction requests
362
+ Promise.all(transactionRequests.map(req => req.toPromise()))
363
+ .then(results => {
364
+ // Store transaction pages
365
+ results.forEach((page, index) => {
366
+ if (page) {
367
+ this.transactionPages.set(this.selectedAccounts[index], page);
368
+ // Flatten all transactions into single array
369
+ this.transactions.push(...page.items);
370
+ }
371
+ });
372
+ this.completeProcess();
373
+ })
374
+ .catch(error => {
375
+ console.error('Error fetching transactions:', error);
376
+ // Even if transactions fail, we can continue with accounts
377
+ this.completeProcess();
378
+ });
379
+ }
380
+ completeProcess() {
381
+ // Store data in form model
382
+ this.storeAccountData();
383
+ this.currentState.next(OpenBankingState.Complete);
384
+ this.setLoadingState(false);
385
+ // Auto-proceed to next step after a short delay
386
+ setTimeout(() => {
387
+ this.onEvent('success');
388
+ }, 2000);
389
+ }
390
+ storeAccountData() {
391
+ // Store the fetched data in the form model for use in later steps
392
+ const group = this.form;
393
+ // Add accounts and transactions to form
394
+ group.addControl('openBankingAccounts', new FormControl(this.accounts));
395
+ group.addControl('openBankingTransactions', new FormControl(this.transactions));
396
+ group.addControl('openBankingConsentId', new FormControl(this.consentId));
397
+ group.addControl('openBankingBankCode', new FormControl(this.selectedBank));
398
+ // Store account summary
399
+ const accountSummary = {
400
+ totalAccounts: this.accounts.length,
401
+ totalBalance: this.accounts.reduce((sum, acc) => sum + acc.balance, 0),
402
+ currencies: [...new Set(this.accounts.map(a => a.currency))],
403
+ transactionCount: this.transactions.length,
404
+ oldestTransaction: this.transactions.length > 0
405
+ ? this.transactions.reduce((oldest, t) => new Date(t.bookingDate || t.transactionDate || '') < new Date(oldest.bookingDate || oldest.transactionDate || '') ? t : oldest).bookingDate
406
+ : null,
407
+ newestTransaction: this.transactions.length > 0
408
+ ? this.transactions.reduce((newest, t) => new Date(t.bookingDate || t.transactionDate || '') > new Date(newest.bookingDate || newest.transactionDate || '') ? t : newest).bookingDate
409
+ : null
410
+ };
411
+ group.addControl('openBankingAccountSummary', new FormControl(accountSummary));
412
+ // Populate the step's controls for SaveAppData
413
+ this.populateStepControls();
414
+ }
415
+ populateStepControls() {
416
+ // Find and populate the controls in the step sections
417
+ if (!this.stepData?.sections)
418
+ return;
419
+ // Get email from config mapping or use empty string
420
+ const email = this.getFieldValue(this.config.emailCollected) || '';
421
+ // Get the first account as the primary account
422
+ const primaryAccount = this.accounts.length > 0 ? this.accounts[0] : null;
423
+ // Calculate total balance
424
+ const totalBalance = this.accounts.reduce((sum, acc) => sum + acc.balance, 0);
425
+ // Calculate transaction period in months
426
+ const months = this.getTransactionPeriodMonths();
427
+ // Map of control identifiers to values
428
+ const controlValues = {
429
+ 'OpenBankingConsentStatus': true, // Consent was successfully validated
430
+ 'OpenBankingEmail': email,
431
+ 'OpenBankingAccountNumber': primaryAccount?.iban || '',
432
+ 'OpenBankingAccountHolder': primaryAccount?.accountHolderName || '',
433
+ 'OpenBankingBankCode': this.selectedBank || '',
434
+ 'OpenBankingConsentId': this.consentId,
435
+ 'OpenBankingAccountsCount': this.accounts.length,
436
+ 'OpenBankingAccountsData': JSON.stringify(this.accounts),
437
+ 'OpenBankingTotalBalance': totalBalance,
438
+ 'OpenBankingTransactionsCount': this.transactions.length,
439
+ 'OpenBankingTransactionsData': JSON.stringify(this.transactions),
440
+ 'OpenBankingTransactionsPeriod': months
441
+ };
442
+ // Add all control values to the form
443
+ const group = this.form;
444
+ Object.keys(controlValues).forEach(controlId => {
445
+ if (!group.contains(controlId)) {
446
+ group.addControl(controlId, new FormControl(controlValues[controlId]));
447
+ }
448
+ else {
449
+ group.get(controlId)?.setValue(controlValues[controlId]);
450
+ }
451
+ });
452
+ // The values are now stored in the form model and will be saved with SaveAppData
453
+ // When the form is submitted, these values will be associated with the step's controls
454
+ }
455
+ retry() {
456
+ this._errorMessage = '';
457
+ this._isConsentError = false;
458
+ this._consentErrorStatus = '';
459
+ this.accounts = [];
460
+ this.transactions = [];
461
+ this.selectedAccounts = [];
462
+ this.transactionPages.clear();
463
+ this.consentId = '';
464
+ this.selectedBank = '';
465
+ this.selectedBankObject = undefined;
466
+ this.banks = [];
467
+ this.authorizationUrl = null;
468
+ // Reset to initial state - user needs to click Next again
469
+ this.currentState.next(OpenBankingState.Initial);
470
+ }
471
+ handleError(error) {
472
+ console.error('OpenBanking error:', error);
473
+ this._isConsentError = false;
474
+ this._consentErrorStatus = '';
475
+ this._errorMessage = error.error?.message || error.message || 'An error occurred. Please try again.';
476
+ this.currentState.next(OpenBankingState.Error);
477
+ }
478
+ getTranslatedMessage(translations, defaultMessage = '') {
479
+ if (!translations || translations.length === 0)
480
+ return defaultMessage;
481
+ const langIso = this.props?.['langIso'] || this.langIso || 'en';
482
+ const translation = translations.find(t => t.languageIso === langIso);
483
+ return translation?.value || translations[0]?.value || defaultMessage;
484
+ }
485
+ get initialMessage() {
486
+ return this.getTranslatedMessage(this.config.initialMessageTranslations, 'To continue, we need to verify your bank account information. Click Next to proceed.');
487
+ }
488
+ get waitingMessage() {
489
+ return this.getTranslatedMessage(this.config.waitingMessageTranslations, 'Please wait while we retrieve your account information...');
490
+ }
491
+ get completionMessage() {
492
+ return this.getTranslatedMessage(this.config.completionMessageTranslations, 'Account information successfully retrieved.');
493
+ }
494
+ get retryButtonLabel() {
495
+ return this.getTranslatedMessage(this.config.retryButtonLabelTranslations, 'Retry');
496
+ }
497
+ get errorMessage() {
498
+ // If it's a consent error, use the translated consent error message
499
+ if (this._isConsentError) {
500
+ const message = this.getTranslatedMessage(this.config.consentErrorMessageTranslations, 'You must provide consent to continue. Please try again.');
501
+ return this._consentErrorStatus ? `${message} (Status: ${this._consentErrorStatus})` : message;
502
+ }
503
+ // Otherwise return the stored error message (from API or default)
504
+ return this._errorMessage || 'An error occurred. Please try again.';
505
+ }
506
+ // Button translations
507
+ get nextButtonLabel() {
508
+ return this.getTranslatedMessage(this.config.nextButtonTranslations, 'Next');
509
+ }
510
+ get continueButtonLabel() {
511
+ return this.getTranslatedMessage(this.config.continueButtonTranslations, 'Continue');
512
+ }
513
+ // UI label translations
514
+ get selectBankTitle() {
515
+ return this.getTranslatedMessage(this.config.selectBankTranslations, 'Select your bank');
516
+ }
517
+ get consentTitle() {
518
+ return this.getTranslatedMessage(this.config.consentTitleTranslations, 'Bank Authorization');
519
+ }
520
+ get consentDescription() {
521
+ return this.getTranslatedMessage(this.config.consentDescriptionTranslations, 'Please complete the authorization process with your bank');
522
+ }
523
+ get waitingForAuthorizationText() {
524
+ return this.getTranslatedMessage(this.config.waitingForAuthorizationTranslations, 'Waiting for authorization...');
525
+ }
526
+ get popupWindowMessage() {
527
+ return this.getTranslatedMessage(this.config.popupWindowMessageTranslations, 'A new window has opened for bank authorization. Please complete the process there.');
528
+ }
529
+ get popupBlockerMessage() {
530
+ return this.getTranslatedMessage(this.config.popupBlockerMessageTranslations, 'If the window didn\'t open, please check your popup blocker settings.');
531
+ }
532
+ get accountsRetrievedLabel() {
533
+ return this.getTranslatedMessage(this.config.accountsRetrievedTranslations, 'Accounts retrieved');
534
+ }
535
+ get transactionsRetrievedLabel() {
536
+ return this.getTranslatedMessage(this.config.transactionsRetrievedTranslations, 'Transactions retrieved');
537
+ }
538
+ get accountsSummaryText() {
539
+ const count = this.accounts.length;
540
+ const template = this.getTranslatedMessage(this.config.accountsFoundTranslations, '{0} account(s) found');
541
+ return template.replace('{0}', count.toString());
542
+ }
543
+ get transactionsSummaryText() {
544
+ const count = this.transactions.length;
545
+ const months = this.getTransactionPeriodMonths();
546
+ const template = this.getTranslatedMessage(this.config.transactionsSummaryTranslations, '{0} transaction(s) retrieved for the last {1} month(s)');
547
+ return template.replace('{0}', count.toString()).replace('{1}', months.toString());
548
+ }
549
+ getTransactionPeriodMonths() {
550
+ if (!this.selectedBank)
551
+ return 3;
552
+ const maxDays = this.openBankingService.getMaxTransactionPeriod(this.selectedBank);
553
+ const daysToFetch = maxDays === -1 ? 365 : Math.min(maxDays, 365);
554
+ return Math.round(daysToFetch / 30);
555
+ }
556
+ get isInitialState() {
557
+ return this.currentState.value === OpenBankingState.Initial;
558
+ }
559
+ get isBankSelectionState() {
560
+ return this.currentState.value === OpenBankingState.BankSelection;
561
+ }
562
+ get isConsentState() {
563
+ return this.currentState.value === OpenBankingState.Consent;
564
+ }
565
+ get isProcessingState() {
566
+ return this.currentState.value === OpenBankingState.Processing;
567
+ }
568
+ get isErrorState() {
569
+ return this.currentState.value === OpenBankingState.Error;
570
+ }
571
+ get isCompleteState() {
572
+ return this.currentState.value === OpenBankingState.Complete;
573
+ }
574
+ setLoadingState(loading) {
575
+ this.isLoading = loading;
576
+ this.isLoadingSubject.next(loading);
577
+ this.cdr.detectChanges();
578
+ }
579
+ ngOnDestroy() {
580
+ this.pollSubscription?.unsubscribe();
581
+ this.isLoadingSubject.complete();
582
+ }
583
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FormlyOpenBankingComponent, deps: [{ token: i1.OpenBankingService }, { token: i2.DialogService }, { token: i3.DomSanitizer }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
584
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: FormlyOpenBankingComponent, selector: "app-formly-open-banking", usesInheritance: true, ngImport: i0, template: "<div class=\"open-banking-container\">\n <!-- Initial State -->\n <div *ngIf=\"isInitialState\" class=\"state-initial\">\n <div class=\"message-container\">\n <p class=\"message\">{{ initialMessage }}</p>\n <button mat-raised-button color=\"primary\" (click)=\"proceedToNextStep()\" [disabled]=\"isLoadingSubject | async\">\n {{ nextButtonLabel }}\n </button>\n </div>\n </div>\n\n <!-- Bank Selection State -->\n <div *ngIf=\"isBankSelectionState\" class=\"state-bank-selection\">\n <h3 class=\"section-title\">{{ selectBankTitle }}</h3>\n\n <!-- Bank Grid -->\n <div class=\"banks-grid\" *ngIf=\"banks.length > 0\">\n <div\n *ngFor=\"let bank of banks\"\n class=\"bank-card\"\n [class.selected]=\"selectedBank === bank.bankCode\"\n (click)=\"selectBank(bank)\">\n <div class=\"bank-logo\" *ngIf=\"bank.logoUrl\">\n <img [src]=\"bank.logoUrl\" [alt]=\"bank.displayName\">\n </div>\n <div class=\"bank-info\">\n <h4>{{ bank.displayName }}</h4>\n <span class=\"bank-code\">{{ bank.bankCode }}</span>\n </div>\n <mat-icon class=\"check-icon\" *ngIf=\"selectedBank === bank.bankCode\">check_circle</mat-icon>\n </div>\n </div>\n\n <button\n mat-raised-button\n color=\"primary\"\n (click)=\"proceedToNextStep()\"\n [disabled]=\"!selectedBank || (isLoadingSubject | async)\"\n class=\"m-t-16\">\n {{ continueButtonLabel }}\n </button>\n </div>\n\n <!-- Consent State -->\n <div *ngIf=\"isConsentState\" class=\"state-consent\">\n <div class=\"consent-header\">\n <h3>{{ consentTitle }}</h3>\n <p>{{ consentDescription }}</p>\n </div>\n\n <!-- Show iframe only if we have an authorization URL -->\n <div class=\"consent-container\" *ngIf=\"authorizationUrl\">\n <iframe\n [src]=\"authorizationUrl\"\n class=\"consent-iframe\"\n frameborder=\"0\"\n allowfullscreen\n sandbox=\"allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-top-navigation\">\n </iframe>\n </div>\n\n <!-- Show waiting message when consent is in popup or iframe failed -->\n <div class=\"consent-waiting\" *ngIf=\"!authorizationUrl\">\n <mat-spinner diameter=\"40\"></mat-spinner>\n <p>{{ waitingForAuthorizationText }}</p>\n <p class=\"consent-popup-hint\">\n <mat-icon>open_in_new</mat-icon>\n <span>{{ popupWindowMessage }}</span>\n </p>\n <p class=\"consent-popup-note\">\n {{ popupBlockerMessage }}\n </p>\n </div>\n </div>\n\n <!-- Processing State -->\n <div *ngIf=\"isProcessingState\" class=\"state-processing\">\n <div class=\"loading-container\">\n <mat-spinner diameter=\"50\"></mat-spinner>\n <p class=\"loading-message\">{{ waitingMessage }}</p>\n <div class=\"progress-details\">\n <p *ngIf=\"accounts.length > 0\">\n {{ accountsRetrievedLabel }}: {{ accounts.length }}\n </p>\n <p *ngIf=\"transactions.length > 0\">\n {{ transactionsRetrievedLabel }}: {{ transactions.length }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Error State -->\n <div *ngIf=\"isErrorState\" class=\"state-error\">\n <div class=\"error-container\">\n <mat-icon color=\"warn\" class=\"error-icon\">error_outline</mat-icon>\n <p class=\"error-message\">{{ errorMessage }}</p>\n <button mat-raised-button color=\"primary\" (click)=\"retry()\">\n {{ retryButtonLabel }}\n </button>\n </div>\n </div>\n\n <!-- Complete State -->\n <div *ngIf=\"isCompleteState\" class=\"state-complete\">\n <div class=\"success-container\">\n <mat-icon color=\"primary\" class=\"success-icon\">check_circle</mat-icon>\n <p class=\"success-message\">{{ completionMessage }}</p>\n\n <!-- Display summary of accounts found -->\n <div class=\"accounts-summary\" *ngIf=\"accounts.length > 0\">\n <h4>{{ accountsSummaryText }}</h4>\n <div class=\"accounts-list\">\n <div class=\"account-card\" *ngFor=\"let account of accounts\">\n <div class=\"account-header\">\n <mat-icon class=\"account-icon\">account_balance</mat-icon>\n <div class=\"account-info\">\n <div class=\"account-name\">{{ account.name || 'Account' }}</div>\n <div class=\"account-iban\">{{ account.iban }}</div>\n </div>\n </div>\n <div class=\"account-details\">\n <div class=\"account-balance\">\n <span class=\"balance-amount\">{{ account.balance | currency:account.currency:'symbol':'1.2-2' }}</span>\n </div>\n <div class=\"account-holder\">{{ account.accountHolderName }}</div>\n </div>\n </div>\n </div>\n\n <div class=\"transaction-summary\" *ngIf=\"transactions.length > 0\">\n <p>{{ transactionsSummaryText }}</p>\n </div>\n </div>\n </div>\n </div>\n</div>", styles: [".open-banking-container{padding:20px;min-height:400px}.open-banking-container .section-title{margin-bottom:20px;font-size:1.25rem;font-weight:500;color:#000000de}.open-banking-container .message-container,.open-banking-container .loading-container,.open-banking-container .error-container,.open-banking-container .success-container{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;min-height:200px}.open-banking-container .message,.open-banking-container .loading-message,.open-banking-container .error-message,.open-banking-container .success-message{font-size:1rem;margin:20px 0;max-width:600px;line-height:1.5}.open-banking-container .error-message{color:#f44336}.open-banking-container .success-message{color:#4caf50}.open-banking-container .error-icon,.open-banking-container .success-icon{font-size:48px;width:48px;height:48px}.open-banking-container .state-bank-selection .banks-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;margin-bottom:24px}.open-banking-container .state-bank-selection .bank-card{border:2px solid #e0e0e0;border-radius:8px;padding:16px;cursor:pointer;transition:all .3s ease;position:relative;background:#fff;min-height:120px;display:flex;flex-direction:column;align-items:center;justify-content:center}.open-banking-container .state-bank-selection .bank-card:hover{border-color:#3f51b5;box-shadow:0 4px 8px #0000001a;transform:translateY(-2px)}.open-banking-container .state-bank-selection .bank-card.selected{border-color:#3f51b5;background:#f5f7ff}.open-banking-container .state-bank-selection .bank-card .bank-logo{width:80px;height:40px;margin-bottom:8px;display:flex;align-items:center;justify-content:center}.open-banking-container .state-bank-selection .bank-card .bank-logo img{max-width:100%;max-height:100%;object-fit:contain}.open-banking-container .state-bank-selection .bank-card .bank-info{text-align:center}.open-banking-container .state-bank-selection .bank-card .bank-info h4{margin:0;font-size:.9rem;font-weight:500;color:#333}.open-banking-container .state-bank-selection .bank-card .bank-info .bank-code{font-size:.75rem;color:#666;margin-top:4px;display:block}.open-banking-container .state-bank-selection .bank-card .check-icon{position:absolute;top:8px;right:8px;color:#3f51b5;font-size:20px}.open-banking-container .state-bank-selection .bank-dropdown{margin:20px 0}.open-banking-container .state-bank-selection .bank-selector{width:100%;max-width:400px}.open-banking-container .state-consent .consent-header{text-align:center;margin-bottom:20px}.open-banking-container .state-consent .consent-header h3{font-size:1.25rem;font-weight:500;margin-bottom:8px}.open-banking-container .state-consent .consent-header p{color:#666;font-size:.95rem}.open-banking-container .state-consent .consent-container{width:100%;height:600px;border:1px solid #e0e0e0;border-radius:4px;overflow:hidden;background:#fff}.open-banking-container .state-consent .consent-iframe{width:100%;height:100%}.open-banking-container .state-consent .consent-waiting{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px}.open-banking-container .state-consent .consent-waiting p{margin-top:16px;color:#666}.open-banking-container .state-consent .consent-waiting .consent-popup-hint{display:flex;align-items:center;gap:8px;margin-top:24px;font-size:1.1rem;color:#333}.open-banking-container .state-consent .consent-waiting .consent-popup-hint mat-icon{color:#3f51b5}.open-banking-container .state-consent .consent-waiting .consent-popup-note{margin-top:12px;font-size:.9rem;color:#999;font-style:italic}.open-banking-container .progress-details{margin-top:20px}.open-banking-container .progress-details p{font-size:.9rem;color:#666;margin:4px 0}.open-banking-container .accounts-summary{margin-top:30px;width:100%;max-width:800px}.open-banking-container .accounts-summary h4{font-size:1.15rem;font-weight:500;margin-bottom:20px;color:#000000de}.open-banking-container .accounts-summary .accounts-list{display:flex;flex-direction:column;gap:16px}.open-banking-container .accounts-summary .account-card{border:1px solid #e0e0e0;border-radius:8px;padding:16px;background:#fff;box-shadow:0 1px 3px #0000001a;transition:box-shadow .2s ease}.open-banking-container .accounts-summary .account-card:hover{box-shadow:0 2px 6px #00000026}.open-banking-container .accounts-summary .account-header{display:flex;align-items:flex-start;gap:12px;margin-bottom:12px}.open-banking-container .accounts-summary .account-icon{color:#3f51b5;margin-top:2px}.open-banking-container .accounts-summary .account-info{flex:1}.open-banking-container .accounts-summary .account-name{font-weight:500;font-size:1rem;color:#333;margin-bottom:4px}.open-banking-container .accounts-summary .account-iban{font-size:.85rem;color:#666;font-family:Courier New,monospace;letter-spacing:.5px}.open-banking-container .accounts-summary .account-details{display:flex;justify-content:space-between;align-items:center;padding-left:36px}.open-banking-container .accounts-summary .account-balance{display:flex;flex-direction:column}.open-banking-container .accounts-summary .balance-amount{font-size:1.25rem;font-weight:600;color:#4caf50}.open-banking-container .accounts-summary .account-holder{font-size:.9rem;color:#666;text-align:right}.open-banking-container .accounts-summary .transaction-summary{margin-top:16px;padding:12px;background:#e8f5e9;border-radius:4px}.open-banking-container .accounts-summary .transaction-summary p{margin:0;color:#2e7d32;font-size:.95rem}.open-banking-container ::ng-deep .mat-spinner{margin:0 auto}.open-banking-container ::ng-deep .mat-progress-spinner circle,.open-banking-container ::ng-deep .mat-spinner circle{stroke:#3f51b5}@media (max-width: 768px){.open-banking-container{padding:10px}.open-banking-container .state-bank-selection .banks-grid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:12px}.open-banking-container .state-bank-selection .bank-card{min-height:100px;padding:12px}.open-banking-container .state-bank-selection .bank-card .bank-logo{width:60px;height:30px}.open-banking-container .consent-container{height:500px}.open-banking-container .accounts-summary{max-width:100%}}@media (max-width: 480px){.open-banking-container .state-bank-selection .banks-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i7.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.CurrencyPipe, name: "currency" }] }); }
585
+ }
586
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FormlyOpenBankingComponent, decorators: [{
587
+ type: Component,
588
+ args: [{ selector: 'app-formly-open-banking', template: "<div class=\"open-banking-container\">\n <!-- Initial State -->\n <div *ngIf=\"isInitialState\" class=\"state-initial\">\n <div class=\"message-container\">\n <p class=\"message\">{{ initialMessage }}</p>\n <button mat-raised-button color=\"primary\" (click)=\"proceedToNextStep()\" [disabled]=\"isLoadingSubject | async\">\n {{ nextButtonLabel }}\n </button>\n </div>\n </div>\n\n <!-- Bank Selection State -->\n <div *ngIf=\"isBankSelectionState\" class=\"state-bank-selection\">\n <h3 class=\"section-title\">{{ selectBankTitle }}</h3>\n\n <!-- Bank Grid -->\n <div class=\"banks-grid\" *ngIf=\"banks.length > 0\">\n <div\n *ngFor=\"let bank of banks\"\n class=\"bank-card\"\n [class.selected]=\"selectedBank === bank.bankCode\"\n (click)=\"selectBank(bank)\">\n <div class=\"bank-logo\" *ngIf=\"bank.logoUrl\">\n <img [src]=\"bank.logoUrl\" [alt]=\"bank.displayName\">\n </div>\n <div class=\"bank-info\">\n <h4>{{ bank.displayName }}</h4>\n <span class=\"bank-code\">{{ bank.bankCode }}</span>\n </div>\n <mat-icon class=\"check-icon\" *ngIf=\"selectedBank === bank.bankCode\">check_circle</mat-icon>\n </div>\n </div>\n\n <button\n mat-raised-button\n color=\"primary\"\n (click)=\"proceedToNextStep()\"\n [disabled]=\"!selectedBank || (isLoadingSubject | async)\"\n class=\"m-t-16\">\n {{ continueButtonLabel }}\n </button>\n </div>\n\n <!-- Consent State -->\n <div *ngIf=\"isConsentState\" class=\"state-consent\">\n <div class=\"consent-header\">\n <h3>{{ consentTitle }}</h3>\n <p>{{ consentDescription }}</p>\n </div>\n\n <!-- Show iframe only if we have an authorization URL -->\n <div class=\"consent-container\" *ngIf=\"authorizationUrl\">\n <iframe\n [src]=\"authorizationUrl\"\n class=\"consent-iframe\"\n frameborder=\"0\"\n allowfullscreen\n sandbox=\"allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-top-navigation\">\n </iframe>\n </div>\n\n <!-- Show waiting message when consent is in popup or iframe failed -->\n <div class=\"consent-waiting\" *ngIf=\"!authorizationUrl\">\n <mat-spinner diameter=\"40\"></mat-spinner>\n <p>{{ waitingForAuthorizationText }}</p>\n <p class=\"consent-popup-hint\">\n <mat-icon>open_in_new</mat-icon>\n <span>{{ popupWindowMessage }}</span>\n </p>\n <p class=\"consent-popup-note\">\n {{ popupBlockerMessage }}\n </p>\n </div>\n </div>\n\n <!-- Processing State -->\n <div *ngIf=\"isProcessingState\" class=\"state-processing\">\n <div class=\"loading-container\">\n <mat-spinner diameter=\"50\"></mat-spinner>\n <p class=\"loading-message\">{{ waitingMessage }}</p>\n <div class=\"progress-details\">\n <p *ngIf=\"accounts.length > 0\">\n {{ accountsRetrievedLabel }}: {{ accounts.length }}\n </p>\n <p *ngIf=\"transactions.length > 0\">\n {{ transactionsRetrievedLabel }}: {{ transactions.length }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Error State -->\n <div *ngIf=\"isErrorState\" class=\"state-error\">\n <div class=\"error-container\">\n <mat-icon color=\"warn\" class=\"error-icon\">error_outline</mat-icon>\n <p class=\"error-message\">{{ errorMessage }}</p>\n <button mat-raised-button color=\"primary\" (click)=\"retry()\">\n {{ retryButtonLabel }}\n </button>\n </div>\n </div>\n\n <!-- Complete State -->\n <div *ngIf=\"isCompleteState\" class=\"state-complete\">\n <div class=\"success-container\">\n <mat-icon color=\"primary\" class=\"success-icon\">check_circle</mat-icon>\n <p class=\"success-message\">{{ completionMessage }}</p>\n\n <!-- Display summary of accounts found -->\n <div class=\"accounts-summary\" *ngIf=\"accounts.length > 0\">\n <h4>{{ accountsSummaryText }}</h4>\n <div class=\"accounts-list\">\n <div class=\"account-card\" *ngFor=\"let account of accounts\">\n <div class=\"account-header\">\n <mat-icon class=\"account-icon\">account_balance</mat-icon>\n <div class=\"account-info\">\n <div class=\"account-name\">{{ account.name || 'Account' }}</div>\n <div class=\"account-iban\">{{ account.iban }}</div>\n </div>\n </div>\n <div class=\"account-details\">\n <div class=\"account-balance\">\n <span class=\"balance-amount\">{{ account.balance | currency:account.currency:'symbol':'1.2-2' }}</span>\n </div>\n <div class=\"account-holder\">{{ account.accountHolderName }}</div>\n </div>\n </div>\n </div>\n\n <div class=\"transaction-summary\" *ngIf=\"transactions.length > 0\">\n <p>{{ transactionsSummaryText }}</p>\n </div>\n </div>\n </div>\n </div>\n</div>", styles: [".open-banking-container{padding:20px;min-height:400px}.open-banking-container .section-title{margin-bottom:20px;font-size:1.25rem;font-weight:500;color:#000000de}.open-banking-container .message-container,.open-banking-container .loading-container,.open-banking-container .error-container,.open-banking-container .success-container{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;min-height:200px}.open-banking-container .message,.open-banking-container .loading-message,.open-banking-container .error-message,.open-banking-container .success-message{font-size:1rem;margin:20px 0;max-width:600px;line-height:1.5}.open-banking-container .error-message{color:#f44336}.open-banking-container .success-message{color:#4caf50}.open-banking-container .error-icon,.open-banking-container .success-icon{font-size:48px;width:48px;height:48px}.open-banking-container .state-bank-selection .banks-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;margin-bottom:24px}.open-banking-container .state-bank-selection .bank-card{border:2px solid #e0e0e0;border-radius:8px;padding:16px;cursor:pointer;transition:all .3s ease;position:relative;background:#fff;min-height:120px;display:flex;flex-direction:column;align-items:center;justify-content:center}.open-banking-container .state-bank-selection .bank-card:hover{border-color:#3f51b5;box-shadow:0 4px 8px #0000001a;transform:translateY(-2px)}.open-banking-container .state-bank-selection .bank-card.selected{border-color:#3f51b5;background:#f5f7ff}.open-banking-container .state-bank-selection .bank-card .bank-logo{width:80px;height:40px;margin-bottom:8px;display:flex;align-items:center;justify-content:center}.open-banking-container .state-bank-selection .bank-card .bank-logo img{max-width:100%;max-height:100%;object-fit:contain}.open-banking-container .state-bank-selection .bank-card .bank-info{text-align:center}.open-banking-container .state-bank-selection .bank-card .bank-info h4{margin:0;font-size:.9rem;font-weight:500;color:#333}.open-banking-container .state-bank-selection .bank-card .bank-info .bank-code{font-size:.75rem;color:#666;margin-top:4px;display:block}.open-banking-container .state-bank-selection .bank-card .check-icon{position:absolute;top:8px;right:8px;color:#3f51b5;font-size:20px}.open-banking-container .state-bank-selection .bank-dropdown{margin:20px 0}.open-banking-container .state-bank-selection .bank-selector{width:100%;max-width:400px}.open-banking-container .state-consent .consent-header{text-align:center;margin-bottom:20px}.open-banking-container .state-consent .consent-header h3{font-size:1.25rem;font-weight:500;margin-bottom:8px}.open-banking-container .state-consent .consent-header p{color:#666;font-size:.95rem}.open-banking-container .state-consent .consent-container{width:100%;height:600px;border:1px solid #e0e0e0;border-radius:4px;overflow:hidden;background:#fff}.open-banking-container .state-consent .consent-iframe{width:100%;height:100%}.open-banking-container .state-consent .consent-waiting{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:300px}.open-banking-container .state-consent .consent-waiting p{margin-top:16px;color:#666}.open-banking-container .state-consent .consent-waiting .consent-popup-hint{display:flex;align-items:center;gap:8px;margin-top:24px;font-size:1.1rem;color:#333}.open-banking-container .state-consent .consent-waiting .consent-popup-hint mat-icon{color:#3f51b5}.open-banking-container .state-consent .consent-waiting .consent-popup-note{margin-top:12px;font-size:.9rem;color:#999;font-style:italic}.open-banking-container .progress-details{margin-top:20px}.open-banking-container .progress-details p{font-size:.9rem;color:#666;margin:4px 0}.open-banking-container .accounts-summary{margin-top:30px;width:100%;max-width:800px}.open-banking-container .accounts-summary h4{font-size:1.15rem;font-weight:500;margin-bottom:20px;color:#000000de}.open-banking-container .accounts-summary .accounts-list{display:flex;flex-direction:column;gap:16px}.open-banking-container .accounts-summary .account-card{border:1px solid #e0e0e0;border-radius:8px;padding:16px;background:#fff;box-shadow:0 1px 3px #0000001a;transition:box-shadow .2s ease}.open-banking-container .accounts-summary .account-card:hover{box-shadow:0 2px 6px #00000026}.open-banking-container .accounts-summary .account-header{display:flex;align-items:flex-start;gap:12px;margin-bottom:12px}.open-banking-container .accounts-summary .account-icon{color:#3f51b5;margin-top:2px}.open-banking-container .accounts-summary .account-info{flex:1}.open-banking-container .accounts-summary .account-name{font-weight:500;font-size:1rem;color:#333;margin-bottom:4px}.open-banking-container .accounts-summary .account-iban{font-size:.85rem;color:#666;font-family:Courier New,monospace;letter-spacing:.5px}.open-banking-container .accounts-summary .account-details{display:flex;justify-content:space-between;align-items:center;padding-left:36px}.open-banking-container .accounts-summary .account-balance{display:flex;flex-direction:column}.open-banking-container .accounts-summary .balance-amount{font-size:1.25rem;font-weight:600;color:#4caf50}.open-banking-container .accounts-summary .account-holder{font-size:.9rem;color:#666;text-align:right}.open-banking-container .accounts-summary .transaction-summary{margin-top:16px;padding:12px;background:#e8f5e9;border-radius:4px}.open-banking-container .accounts-summary .transaction-summary p{margin:0;color:#2e7d32;font-size:.95rem}.open-banking-container ::ng-deep .mat-spinner{margin:0 auto}.open-banking-container ::ng-deep .mat-progress-spinner circle,.open-banking-container ::ng-deep .mat-spinner circle{stroke:#3f51b5}@media (max-width: 768px){.open-banking-container{padding:10px}.open-banking-container .state-bank-selection .banks-grid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:12px}.open-banking-container .state-bank-selection .bank-card{min-height:100px;padding:12px}.open-banking-container .state-bank-selection .bank-card .bank-logo{width:60px;height:30px}.open-banking-container .consent-container{height:500px}.open-banking-container .accounts-summary{max-width:100%}}@media (max-width: 480px){.open-banking-container .state-bank-selection .banks-grid{grid-template-columns:1fr}}\n"] }]
589
+ }], ctorParameters: () => [{ type: i1.OpenBankingService }, { type: i2.DialogService }, { type: i3.DomSanitizer }, { type: i0.ChangeDetectorRef }] });
590
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9ybWx5LW9wZW4tYmFua2luZy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9vcmlnaW4tZm9ybS9zcmMvbGliL2Zvcm1seS9mb3JtbHktb3Blbi1iYW5raW5nL2Zvcm1seS1vcGVuLWJhbmtpbmcuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvb3JpZ2luLWZvcm0vc3JjL2xpYi9mb3JtbHkvZm9ybWx5LW9wZW4tYmFua2luZy9mb3JtbHktb3Blbi1iYW5raW5nLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCxTQUFTLEdBS1YsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFdBQVcsRUFBYSxNQUFNLGdCQUFnQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxlQUFlLEVBQUUsUUFBUSxFQUFnQixNQUFNLE1BQU0sQ0FBQztBQUMvRCxPQUFPLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNoRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQzs7Ozs7Ozs7O0FBb0NyRSxJQUFLLGdCQU9KO0FBUEQsV0FBSyxnQkFBZ0I7SUFDbkIsdUNBQW1CLENBQUE7SUFDbkIsbURBQStCLENBQUE7SUFDL0IsdUNBQW1CLENBQUE7SUFDbkIsNkNBQXlCLENBQUE7SUFDekIsbUNBQWUsQ0FBQTtJQUNmLHlDQUFxQixDQUFBO0FBQ3ZCLENBQUMsRUFQSSxnQkFBZ0IsS0FBaEIsZ0JBQWdCLFFBT3BCO0FBT0QsTUFBTSxPQUFPLDBCQUNYLFNBQVEsdUJBQXVCO0lBcUIvQixZQUNVLGtCQUFzQyxFQUN0QyxNQUFxQixFQUNyQixTQUF1QixFQUMvQixHQUFzQjtRQUV0QixLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFMSCx1QkFBa0IsR0FBbEIsa0JB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QUNwRSxDQUFDO0lBRUQsSUFBSSxjQUFjO1FBQ2hCLE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQyxLQUFLLEtBQUssZ0JBQWdCLENBQUMsT0FBTyxDQUFDO0lBQzlELENBQUM7SUFFRCxJQUFJLGlCQUFpQjtRQUNuQixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxLQUFLLGdCQUFnQixDQUFDLFVBQVUsQ0FBQztJQUNqRSxDQUFDO0lBRUQsSUFBSSxZQUFZO1FBQ2QsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDLEtBQUssS0FBSyxnQkFBZ0IsQ0FBQyxLQUFLLENBQUM7SUFDNUQsQ0FBQztJQUVELElBQUksZUFBZTtRQUNqQixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxLQUFLLGdCQUFnQixDQUFDLFFBQVEsQ0FBQztJQUMvRCxDQUFDO0lBRU8sZUFBZSxDQUFDLE9BQWdCO1FBQ3RDLElBQUksQ0FBQyxTQUFTLEdBQUcsT0FBTyxDQUFDO1FBQ3pCLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDcEMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUMzQixDQUFDO0lBRUQsV0FBVztRQUNULElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxXQUFXLEVBQUUsQ0FBQztRQUNyQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLENBQUM7SUFDbkMsQ0FBQzsrR0Evc0JVLDBCQUEwQjttR0FBMUIsMEJBQTBCLHNGQzVEdkMsdStKQXVJTTs7NEZEM0VPLDBCQUEwQjtrQkFMdEMsU0FBUzsrQkFDRSx5QkFBeUIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDb21wb25lbnQsXG4gIE9uSW5pdCxcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gIE9uRGVzdHJveSxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBGb3JtQ29udHJvbCwgRm9ybUdyb3VwIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBpbnRlcnZhbCwgU3Vic2NyaXB0aW9uIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBzd2l0Y2hNYXAsIHRha2VXaGlsZSwgZmluYWxpemUgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBCYXNlRm9ybWx5U3RlcENvbXBvbmVudCB9IGZyb20gJy4uL2Jhc2VGb3JtbHlTdGVwQ29tcG9uZW50JztcblxuaW1wb3J0IHsgRG9tU2FuaXRpemVyLCBTYWZlUmVzb3VyY2VVcmwgfSBmcm9tICdAYW5ndWxhci9wbGF0Zm9ybS1icm93c2VyJztcbmltcG9ydCB7IEh0dHBFcnJvclJlc3BvbnNlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgRGlhbG9nU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2RpYWxvZy5zZXJ2aWNlJztcbmltcG9ydCB7IEJhbmssIEFjY291bnQsIFRyYW5zYWN0aW9uLCBUcmFuc2FjdGlvblBhZ2UsIE9wZW5CYW5raW5nU2VydmljZSwgQ29uc2VudEluaXRSZXF1ZXN0LCBDb25zZW50U3RhdHVzIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvb3Blbi1iYW5raW5nLnNlcnZpY2UnO1xuXG5pbnRlcmZhY2UgT3BlbkJhbmtpbmdDb25maWcge1xuICBhY2NvdW50TnVtYmVyQ29sbGVjdGVkPzogc3RyaW5nO1xuICBhY2NvdW50SG9sZGVyQ29sbGVjdGVkPzogc3RyaW5nO1xuICBlbWFpbENvbGxlY3RlZD86IHN0cmluZztcbiAgc2hvd0FsbEJhbmtzPzogYm9vbGVhbjtcbiAgZXh0cmFjdEJhbmtGcm9tSUJBTj86IGJvb2xlYW47XG4gIHRjQWNjZXB0ZWRCeURlZmF1bHQ/OiBib29sZWFuO1xuICAvLyBNZXNzYWdlIHRyYW5zbGF0aW9uc1xuICBpbml0aWFsTWVzc2FnZVRyYW5zbGF0aW9ucz86IGFueVtdO1xuICB3YWl0aW5nTWVzc2FnZVRyYW5zbGF0aW9ucz86IGFueVtdO1xuICBjb25zZW50RXJyb3JNZXNzYWdlVHJhbnNsYXRpb25zPzogYW55W107XG4gIGNvbXBsZXRpb25NZXNzYWdlVHJhbnNsYXRpb25zPzogYW55W107XG4gIHJldHJ5QnV0dG9uTGFiZWxUcmFuc2xhdGlvbnM/OiBhbnlbXTtcbiAgLy8gQnV0dG9uIHRyYW5zbGF0aW9uc1xuICBuZXh0QnV0dG9uVHJhbnNsYXRpb25zPzogYW55W107XG4gIGNvbnRpbnVlQnV0dG9uVHJhbnNsYXRpb25zPzogYW55W107XG4gIC8vIFVJIGxhYmVscyB0cmFuc2xhdGlvbnNcbiAgc2VsZWN0QmFua1RyYW5zbGF0aW9ucz86IGFueVtdO1xuICBjb25zZW50VGl0bGVUcmFuc2xhdGlvbnM/OiBhbnlbXTtcbiAgY29uc2VudERlc2NyaXB0aW9uVHJhbnNsYXRpb25zPzogYW55W107XG4gIHdhaXRpbmdGb3JBdXRob3JpemF0aW9uVHJhbnNsYXRpb25zPzogYW55W107XG4gIHBvcHVwV2luZG93TWVzc2FnZVRyYW5zbGF0aW9ucz86IGFueVtdO1xuICBwb3B1cEJsb2NrZXJNZXNzYWdlVHJhbnNsYXRpb25zPzogYW55W107XG4gIGFjY291bnRzUmV0cmlldmVkVHJhbnNsYXRpb25zPzogYW55W107XG4gIHRyYW5zYWN0aW9uc1JldHJpZXZlZFRyYW5zbGF0aW9ucz86IGFueVtdO1xuICBhY2NvdW50c0ZvdW5kVHJhbnNsYXRpb25zPzogYW55W107XG4gIHRyYW5zYWN0aW9uc1N1bW1hcnlUcmFuc2xhdGlvbnM/OiBhbnlbXTtcbn1cblxuZW51bSBPcGVuQmFua2luZ1N0YXRlIHtcbiAgSW5pdGlhbCA9ICdpbml0aWFsJyxcbiAgQmFua1NlbGVjdGlvbiA9ICdiYW5rU2VsZWN0aW9uJyxcbiAgQ29uc2VudCA9ICdjb25zZW50JyxcbiAgUHJvY2Vzc2luZyA9ICdwcm9jZXNzaW5nJyxcbiAgRXJyb3IgPSAnZXJyb3InLFxuICBDb21wbGV0ZSA9ICdjb21wbGV0ZScsXG59XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2FwcC1mb3JtbHktb3Blbi1iYW5raW5nJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2Zvcm1seS1vcGVuLWJhbmtpbmcuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9mb3JtbHktb3Blbi1iYW5raW5nLmNvbXBvbmVudC5zY3NzJ10sXG59KVxuZXhwb3J0IGNsYXNzIEZvcm1seU9wZW5CYW5raW5nQ29tcG9uZW50XG4gIGV4dGVuZHMgQmFzZUZvcm1seVN0ZXBDb21wb25lbnRcbiAgaW1wbGVtZW50cyBPbkluaXQsIEFmdGVyVmlld0luaXQsIE9uRGVzdHJveVxue1xuICBjb25maWc6IE9wZW5CYW5raW5nQ29uZmlnID0ge307XG4gIGN1cnJlbnRTdGF0ZSA9IG5ldyBCZWhhdmlvclN1YmplY3Q8T3BlbkJhbmtpbmdTdGF0ZT4oT3BlbkJhbmtpbmdTdGF0ZS5Jbml0aWFsKTtcbiAgYmFua3M6IEJhbmtbXSA9IFtdO1xuICBzZWxlY3RlZEJhbms6IHN0cmluZyA9ICcnO1xuICBzZWxlY3RlZEJhbmtPYmplY3Q/OiBCYW5rO1xuICBhdXRob3JpemF0aW9uVXJsOiBTYWZlUmVzb3VyY2VVcmwgfCBudWxsID0gbnVsbDtcbiAgY29uc2VudElkOiBzdHJpbmcgPSAnJztcbiAgcHJpdmF0ZSBfZXJyb3JNZXNzYWdlOiBzdHJpbmcgPSAnJztcbiAgcHJpdmF0ZSBfaXNDb25zZW50RXJyb3I6IGJvb2xlYW4gPSBmYWxzZTtcbiAgcHJpdmF0ZSBfY29uc2VudEVycm9yU3RhdHVzOiBzdHJpbmcgPSAnJztcbiAgb3ZlcnJpZGUgaXNMb2FkaW5nOiBib29sZWFuID0gZmFsc2U7XG4gIGlzTG9hZGluZ1N1YmplY3QgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcbiAgYWNjb3VudHM6IEFjY291bnRbXSA9IFtdO1xuICB0cmFuc2FjdGlvbnM6IFRyYW5zYWN0aW9uW10gPSBbXTtcbiAgcG9sbFN1YnNjcmlwdGlvbj86IFN1YnNjcmlwdGlvbjtcbiAgc2VsZWN0ZWRBY2NvdW50czogc3RyaW5nW10gPSBbXTtcbiAgdHJhbnNhY3Rpb25QYWdlczogTWFwPHN0cmluZywgVHJhbnNhY3Rpb25QYWdlPiA9IG5ldyBNYXAoKTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIG9wZW5CYW5raW5nU2VydmljZTogT3BlbkJhbmtpbmdTZXJ2aWNlLFxuICAgIHByaXZhdGUgZGlhbG9nOiBEaWFsb2dTZXJ2aWNlLFxuICAgIHByaXZhdGUgc2FuaXRpemVyOiBEb21TYW5pdGl6ZXIsXG4gICAgY2RyOiBDaGFuZ2VEZXRlY3RvclJlZlxuICApIHtcbiAgICBzdXBlcihjZHIpO1xuICB9XG5cbiAgb3ZlcnJpZGUgYXN5bmMgbmdPbkluaXQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgc3VwZXIubmdPbkluaXQoKTtcbiAgICBpZiAodGhpcy5zdGVwRGF0YT8uY29uZmlnKSB7XG4gICAgICB0aGlzLmNvbmZpZyA9IHRoaXMuc3RlcERhdGEuY29uZmlnIGFzIE9wZW5CYW5raW5nQ29uZmlnO1xuICAgIH1cbiAgICAvLyBJZiB0cmFuc2xhdGlvbnMgYXJlIGluIHN0ZXBEYXRhLnRyYW5zbGF0aW9ucywgbWVyZ2UgdGhlbSBpbnRvIGNvbmZpZ1xuICAgIGlmICh0aGlzLnN0ZXBEYXRhPy50cmFuc2xhdGlvbnMpIHtcbiAgICAgIHRoaXMubWVyZ2VUcmFuc2xhdGlvbnNJbnRvQ29uZmlnKCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBtZXJnZVRyYW5zbGF0aW9uc0ludG9Db25maWcoKTogdm9pZCB7XG4gICAgLy8gTWFwIHRyYW5zbGF0aW9uIG5hbWVzIHRvIGNvbmZpZyBwcm9wZXJ0aWVzXG4gICAgY29uc3QgdHJhbnNsYXRpb25NYXA6IHsgW2tleTogc3RyaW5nXToga2V5b2YgT3BlbkJhbmtpbmdDb25maWcgfSA9IHtcbiAgICAgICdpbml0aWFsTWVzc2FnZVRyYW5zbGF0aW9ucyc6ICdpbml0aWFsTWVzc2FnZVRyYW5zbGF0aW9ucycsXG4gICAgICAnd2FpdGluZ01lc3NhZ2VUcmFuc2xhdGlvbnMnOiAnd2FpdGluZ01lc3NhZ2VUcmFuc2xhdGlvbnMnLFxuICAgICAgJ2NvbnNlbnRFcnJvck1lc3NhZ2VUcmFuc2xhdGlvbnMnOiAnY29uc2VudEVycm9yTWVzc2FnZVRyYW5zbGF0aW9ucycsXG4gICAgICAnY29tcGxldGlvbk1lc3NhZ2VUcmFuc2xhdGlvbnMnOiAnY29tcGxldGlvbk1lc3NhZ2VUcmFuc2xhdGlvbnMnLFxuICAgICAgJ3JldHJ5QnV0dG9uTGFiZWxUcmFuc2xhdGlvbnMnOiAncmV0cnlCdXR0b25MYWJlbFRyYW5zbGF0aW9ucycsXG4gICAgICAnbmV4dEJ1dHRvblRyYW5zbGF0aW9ucyc6ICduZXh0QnV0dG9uVHJhbnNsYXRpb25zJyxcbiAgICAgICdjb250aW51ZUJ1dHRvblRyYW5zbGF0aW9ucyc6ICdjb250aW51ZUJ1dHRvblRyYW5zbGF0aW9ucycsXG4gICAgICAnc2VsZWN0QmFua1RyYW5zbGF0aW9ucyc6ICdzZWxlY3RCYW5rVHJhbnNsYXRpb25zJyxcbiAgICAgICdjb25zZW50VGl0bGVUcmFuc2xhdGlvbnMnOiAnY29uc2VudFRpdGxlVHJhbnNsYXRpb25zJyxcbiAgICAgICdjb25zZW50RGVzY3JpcHRpb25UcmFuc2xhdGlvbnMnOiAnY29uc2VudERlc2NyaXB0aW9uVHJhbnNsYXRpb25zJyxcbiAgICAgICd3YWl0aW5nRm9yQXV0aG9yaXphdGlvblRyYW5zbGF0aW9ucyc6ICd3YWl0aW5nRm9yQXV0aG9yaXphdGlvblRyYW5zbGF0aW9ucycsXG4gICAgICAncG9wdXBXaW5kb3dNZXNzYWdlVHJhbnNsYXRpb25zJzogJ3BvcHVwV2luZG93TWVzc2FnZVRyYW5zbGF0aW9ucycsXG4gICAgICAncG9wdXBCbG9ja2VyTWVzc2FnZVRyYW5zbGF0aW9ucyc6ICdwb3B1cEJsb2NrZXJNZXNzYWdlVHJhbnNsYXRpb25zJyxcbiAgICAgICdhY2NvdW50c1JldHJpZXZlZFRyYW5zbGF0aW9ucyc6ICdhY2NvdW50c1JldHJpZXZlZFRyYW5zbGF0aW9ucycsXG4gICAgICAndHJhbnNhY3Rpb25zUmV0cmlldmVkVHJhbnNsYXRpb25zJzogJ3RyYW5zYWN0aW9uc1JldHJpZXZlZFRyYW5zbGF0aW9ucycsXG4gICAgICAnYWNjb3VudHNGb3VuZFRyYW5zbGF0aW9ucyc6ICdhY2NvdW50c0ZvdW5kVHJhbnNsYXRpb25zJyxcbiAgICAgICd0cmFuc2FjdGlvbnNTdW1tYXJ5VHJhbnNsYXRpb25zJzogJ3RyYW5zYWN0aW9uc1N1bW1hcnlUcmFuc2xhdGlvbnMnXG4gICAgfTtcblxuICAgIHRoaXMuc3RlcERhdGEudHJhbnNsYXRpb25zPy5mb3JFYWNoKCh0cmFuc2xhdGlvbjogeyBuYW1lOiBzdHJpbmcgfCBudW1iZXI7IHZhbHVlczogc3RyaW5nIHwgYW55W107IH0pID0+IHtcbiAgICAgIGNvbnN0IGNvbmZpZ0tleSA9IHRyYW5zbGF0aW9uTWFwW3RyYW5zbGF0aW9uLm5hbWVdO1xuICAgICAgaWYgKGNvbmZpZ0tleSAmJiB0cmFuc2xhdGlvbi52YWx1ZXMgJiYgdHJhbnNsYXRpb24udmFsdWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgLy8gVXNlIHRoZSB0cmFuc2xhdGlvbiB2YWx1ZXMgZnJvbSBzdGVwRGF0YSBpZiBjb25maWcgZG9lc24ndCBoYXZlIGFsbCBsYW5ndWFnZXNcbiAgICAgICAgKHRoaXMuY29uZmlnIGFzIGFueSlbY29uZmlnS2V5XSA9IHRyYW5zbGF0aW9uLnZhbHVlcztcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpOiB2b2lkIHtcbiAgICAvLyBPbmx5IHNldCBpbml0aWFsIHN0YXRlIGlmIHdlIGhhdmVuJ3QgYWxyZWFkeSBzZXQgYSBzdGF0ZVxuICAgIGlmICghdGhpcy5jdXJyZW50U3RhdGUudmFsdWUpIHtcbiAgICAgIHRoaXMuY3VycmVudFN0YXRlLm5leHQoT3BlbkJhbmtpbmdTdGF0ZS5Jbml0aWFsKTtcbiAgICB9XG4gIH1cblxuICBvdmVycmlkZSBvblBhZ2VTZWxlY3RlZCgpIHtcbiAgICAvLyBPbmx5IHJlc2V0IHRvIGluaXRpYWwgaWYgd2UgaGF2ZW4ndCBzdGFydGVkIHRoZSBmbG93IHlldFxuICAgIC8vIFRoaXMgcHJldmVudHMgcmVzZXQgd2hlbiBsYW5ndWFnZSBjaGFuZ2VzXG4gICAgaWYgKCF0aGlzLmN1cnJlbnRTdGF0ZS52YWx1ZSkge1xuICAgICAgdGhpcy5jdXJyZW50U3RhdGUubmV4dChPcGVuQmFua2luZ1N0YXRlLkluaXRpYWwpO1xuICAgIH1cbiAgICAvLyBVcGRhdGUgbGFuZ3VhZ2UgaWYgaXQgY2hhbmdlZFxuICAgIHRoaXMuY2RyLmRldGVjdENoYW5nZXMoKTtcbiAgfVxuXG4gIHByaXZhdGUgZGV0ZXJtaW5lTmV4dFN0ZXAoKTogdm9pZCB7XG4gICAgLy8gR2V0IGFjY291bnQgaW5mb3JtYXRpb24gZnJvbSBmb3JtIGlmIGNvbmZpZ3VyZWRcbiAgICBjb25zdCBhY2NvdW50TnVtYmVyID0gdGhpcy5nZXRGaWVsZFZhbHVlKHRoaXMuY29uZmlnLmFjY291bnROdW1iZXJDb2xsZWN0ZWQpO1xuXG4gICAgLy8gSWYgd2Ugc2hvdWxkIGV4dHJhY3QgYmFuayBmcm9tIElCQU4gYW5kIGhhdmUgYW4gSUJBTlxuICAgIGlmICh0aGlzLmNvbmZpZy5leHRyYWN0QmFua0Zyb21JQkFOICYmIGFjY291bnROdW1iZXIgJiYgdGhpcy5pc0lCQU4oYWNjb3VudE51bWJlcikpIHtcbiAgICAgIC8vIExvYWQgYmFua3MgZmlyc3QsIHRoZW4gbWF0Y2ggSUJBTiB0byBiYW5rXG4gICAgICB0aGlzLmxvYWRCYW5rc0FuZE1hdGNoSUJBTihhY2NvdW50TnVtYmVyKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBJZiB3ZSBzaG91bGQgc2hvdyBhbGwgYmFua3NcbiAgICBpZiAodGhpcy5jb25maWcuc2hvd0FsbEJhbmtzKSB7XG4gICAgICB0aGlzLmxvYWRCYW5rcygpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBHbyBkaXJlY3RseSB0byBjb25zZW50IHdpdGhvdXQgYmFuayBzZWxlY3Rpb25cbiAgICAgIHRoaXMuaW5pdGlhdGVDb25zZW50KCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBnZXRGaWVsZFZhbHVlKGZpZWxkSWQ6IHN0cmluZyB8IHVuZGVmaW5lZCk6IHN0cmluZyB7XG4gICAgaWYgKCFmaWVsZElkKSByZXR1cm4gJyc7XG4gICAgcmV0dXJuIHRoaXMuZm9ybS5nZXQoZmllbGRJZCk/LnZhbHVlIHx8ICcnO1xuICB9XG5cbiAgcHJpdmF0ZSBpc0lCQU4odmFsdWU6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIC8vIEJhc2ljIElCQU4gdmFsaWRhdGlvbiBmb3IgUm9tYW5pYW4gSUJBTnNcbiAgICByZXR1cm4gL15ST1xcZHsyfVtBLVpdezR9XFxkezE2fSQvLnRlc3QodmFsdWUucmVwbGFjZSgvXFxzL2csICcnKSk7XG4gIH1cblxuICBwcml2YXRlIGV4dHJhY3RCYW5rRnJvbUlCQU4oaWJhbjogc3RyaW5nKTogc3RyaW5nIHtcbiAgICAvLyBFeHRyYWN0IGJhbmsgY29kZSBmcm9tIFJvbWFuaWFuIElCQU4gKHBvc2l0aW9ucyA1LTgpXG4gICAgY29uc3QgY2xlYW5JYmFuID0gaWJhbi5yZXBsYWNlKC9cXHMvZywgJycpO1xuICAgIGlmIChjbGVhbkliYW4uc3RhcnRzV2l0aCgnUk8nKSAmJiBjbGVhbkliYW4ubGVuZ3RoID49IDgpIHtcbiAgICAgIHJldHVybiBjbGVhbkliYW4uc3Vic3RyaW5nKDQsIDgpO1xuICAgIH1cbiAgICByZXR1cm4gJyc7XG4gIH1cblxuICBwcml2YXRlIGxvYWRCYW5rc0FuZE1hdGNoSUJBTihpYmFuOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnNldExvYWRpbmdTdGF0ZSh0cnVlKTtcbiAgICBjb25zdCBjb3VudHJ5Q29kZSA9ICdSTyc7IC8vIERlZmF1bHQgdG8gUm9tYW5pYVxuXG4gICAgdGhpcy5vcGVuQmFua2luZ1NlcnZpY2UuZ2V0QmFua3MoY291bnRyeUNvZGUpLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAoYmFua3MpID0+IHtcbiAgICAgICAgdGhpcy5iYW5rcyA9IGJhbmtzLmZpbHRlcihiID0+IGIuaXNBY3RpdmUpO1xuXG4gICAgICAgIC8vIEV4dHJhY3QgYmFuayBjb2RlIGZyb20gSUJBTlxuICAgICAgICBjb25zdCBpYmFuQmFua0NvZGUgPSB0aGlzLmV4dHJhY3RCYW5rRnJvbUlCQU4oaWJhbik7XG5cbiAgICAgICAgaWYgKGliYW5CYW5rQ29kZSkge1xuICAgICAgICAgIC8vIFRyeSB0byBtYXRjaCBhZ2FpbnN0IGJhbmsgY29kZXNcbiAgICAgICAgICBjb25zdCBtYXRjaGVkQmFuayA9IHRoaXMuYmFua3MuZmluZChiYW5rID0+XG4gICAgICAgICAgICBiYW5rLmJhbmtDb2RlLnRvVXBwZXJDYXNlKCkgPT09IGliYW5CYW5rQ29kZS50b1VwcGVyQ2FzZSgpXG4gICAgICAgICAgKTtcblxuICAgICAgICAgIGlmIChtYXRjaGVkQmFuaykge1xuICAgICAgICAgICAgLy8gQmFuayBmb3VuZCAtIHNlbGVjdCBpdCBhbmQgZ28gZGlyZWN0bHkgdG8gY29uc2VudFxuICAgICAgICAgICAgdGhpcy5zZWxlY3RlZEJhbmsgPSBtYXRjaGVkQmFuay5iYW5rQ29kZTtcbiAgICAgICAgICAgIHRoaXMuc2VsZWN0ZWRCYW5rT2JqZWN0ID0gbWF0Y2hlZEJhbms7XG4gICAgICAgICAgICB0aGlzLnNldExvYWRpbmdTdGF0ZShmYWxzZSk7XG4gICAgICAgICAgICB0aGlzLmluaXRpYXRlQ29uc2VudCgpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIElmIG5vIG1hdGNoIGZvdW5kLCBzaG93IGJhbmsgc2VsZWN0aW9uXG4gICAgICAgIHRoaXMuY3VycmVudFN0YXRlLm5leHQoT3BlbkJhbmtpbmdTdGF0ZS5CYW5rU2VsZWN0aW9uKTtcbiAgICAgICAgdGhpcy5zZXRMb2FkaW5nU3RhdGUoZmFsc2UpO1xuICAgICAgfSxcbiAgICAgIGVycm9yOiAoZXJyb3IpID0+IHtcbiAgICAgICAgdGhpcy5oYW5kbGVFcnJvcihlcnJvcik7XG4gICAgICAgIHRoaXMuc2V0TG9hZGluZ1N0YXRlKGZhbHNlKTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGxvYWRCYW5rcygpOiB2b2lkIHtcbiAgICB0aGlzLnNldExvYWRpbmdTdGF0ZSh0cnVlKTtcbiAgICBjb25zdCBjb3VudHJ5Q29kZSA9ICdSTyc7IC8vIERlZmF1bHQgdG8gUm9tYW5pYVxuXG4gICAgdGhpcy5vcGVuQmFua2luZ1NlcnZpY2UuZ2V0QmFua3MoY291bnRyeUNvZGUpLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAoYmFua3MpID0+IHtcbiAgICAgICAgdGhpcy5iYW5rcyA9IGJhbmtzLmZpbHRlcihiID0+IGIuaXNBY3RpdmUpO1xuICAgICAgICB0aGlzLmN1cnJlbnRTdGF0ZS5uZXh0KE9wZW5CYW5raW5nU3RhdGUuQmFua1NlbGVjdGlvbik7XG4gICAgICAgIHRoaXMuc2V0TG9hZGluZ1N0YXRlKGZhbHNlKTtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogKGVycm9yKSA9PiB7XG4gICAgICAgIHRoaXMuaGFuZGxlRXJyb3IoZXJyb3IpO1xuICAgICAgICB0aGlzLnNldExvYWRpbmdTdGF0ZShmYWxzZSk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgc2VsZWN0QmFuayhiYW5rOiBCYW5rKTogdm9pZCB7XG4gICAgdGhpcy5zZWxlY3RlZEJhbmsgPSBiYW5rLmJhbmtDb2RlO1xuICAgIHRoaXMuc2VsZWN0ZWRCYW5rT2JqZWN0ID0gYmFuaztcbiAgfVxuXG4gIHByb2NlZWRUb05leHRTdGVwKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmN1cnJlbnRTdGF0ZS52YWx1ZSA9PT0gT3BlbkJhbmtpbmdTdGF0ZS5Jbml0aWFsKSB7XG4gICAgICAvLyBVc2VyIGNsaWNrZWQgTmV4dCBvbiBpbml0aWFsIHNjcmVlbiAtIGRldGVybWluZSB3aGF0IHRvIHNob3cgbmV4dFxuICAgICAgdGhpcy5kZXRlcm1pbmVOZXh0U3RlcCgpO1xuICAgIH0gZWxzZSBpZiAodGhpcy5jdXJyZW50U3RhdGUudmFsdWUgPT09IE9wZW5CYW5raW5nU3RhdGUuQmFua1NlbGVjdGlvbikge1xuICAgICAgaWYgKHRoaXMuc2VsZWN0ZWRCYW5rKSB7XG4gICAgICAgIHRoaXMuaW5pdGlhdGVDb25zZW50KCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBpbml0aWF0ZUNvbnNlbnQoKTogdm9pZCB7XG4gICAgdGhpcy5zZXRMb2FkaW5nU3RhdGUodHJ1ZSk7XG5cbiAgICAvLyBHZXQgdXNlciBpbmZvcm1hdGlvblxuICAgIGNvbnN0IGVtYWlsID0gdGhpcy5nZXRGaWVsZFZhbHVlKHRoaXMuY29uZmlnLmVtYWlsQ29sbGVjdGVkKSB8fCAndXNlckBleGFtcGxlLmNvbSc7XG4gICAgLy8gR2VuZXJhdGUgYSB1bmlxdWUgUFNVIElEIGZvciB0aGlzIHNlc3Npb25cbiAgICBjb25zdCBwc3VJZCA9IHRoaXMuYXBwRGF0YUlkIHx8ICd1c2VyLScgKyBEYXRlLm5vdygpICsgJy0nICsgTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyKDIsIDkpO1xuXG4gICAgY29uc3QgY29uc2VudFJlcXVlc3Q6IENvbnNlbnRJbml0UmVxdWVzdCA9IHtcbiAgICAgIGJhbmtDb2RlOiB0aGlzLnNlbGVjdGVkQmFuayB8fCAnREVGQVVMVCcsXG4gICAgICBwc3VFbWFpbDogZW1haWwsXG4gICAgICBwc3VJbnRlcm1lZGlhcnlJZDogcHN1SWQsXG4gICAgICBwZXJpb2RPZlZhbGlkaXR5OiA5MCxcbiAgICAgIHRjQWNjZXB0ZWQ6IHRoaXMuY29uZmlnLnRjQWNjZXB0ZWRCeURlZmF1bHQgIT09IGZhbHNlXG4gICAgfTtcblxuICAgIHRoaXMub3BlbkJhbmtpbmdTZXJ2aWNlLmluaXRpYWxpemVDb25zZW50KGNvbnNlbnRSZXF1ZXN0KS5zdWJzY3JpYmUoe1xuICAgICAgbmV4dDogKHJlc3BvbnNlKSA9PiB7XG4gICAgICAgIHRoaXMuY29uc2VudElkID0gcmVzcG9uc2UuY29uc2VudElkO1xuXG4gICAgICAgIC8vIENoZWNrIGlmIGJhbmsgcmVxdWlyZXMgcmVkaXJlY3RcbiAgICAgICAgaWYgKHRoaXMuc2VsZWN0ZWRCYW5rT2JqZWN0Py5yZXF1aXJlc1JlZGlyZWN0ICE9PSBmYWxzZSkge1xuICAgICAgICAgIC8vIFRyeSBpZnJhbWUgZmlyc3QsIGJ1dCBtb3N0IGJhbmtzIHdpbGwgYmxvY2sgaXRcbiAgICAgICAgICBpZiAodGhpcy5zaG91bGRVc2VJZnJhbWUoKSkge1xuICAgICAgICAgICAgdGhpcy5hdXRob3JpemF0aW9uVXJsID0gdGhpcy5zYW5pdGl6ZXIuYnlwYXNzU2VjdXJpdHlUcnVzdFJlc291cmNlVXJsKHJlc3BvbnNlLmF1dGhvcml6YXRpb25VcmwpO1xuICAgICAgICAgICAgdGhpcy5jdXJyZW50U3RhdGUubmV4dChPcGVuQmFua2luZ1N0YXRlLkNvbnNlbnQpO1xuICAgICAgICAgICAgLy8gU2V0IGEgZmxhZyB0byBkZXRlY3QgaWYgaWZyYW1lIGZhaWxzXG4gICAgICAgICAgICB0aGlzLmNoZWNrSWZyYW1lTG9hZChyZXNwb25zZS5hdXRob3JpemF0aW9uVXJsKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gT3BlbiBpbiBuZXcgd2luZG93L3RhYlxuICAgICAgICAgICAgdGhpcy5vcGVuQ29uc2VudEluTmV3V2luZG93KHJlc3BvbnNlLmF1dGhvcml6YXRpb25VcmwpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc2V0TG9hZGluZ1N0YXRlKGZhbHNlKTtcblxuICAgICAgICAvLyBTdGFydCBwb2xsaW5nIGZvciBjb25zZW50IHN0YXR1c1xuICAgICAgICB0aGlzLnN0YXJ0Q29uc2VudFBvbGxpbmcoKTtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogKGVycm9yKSA9PiB7XG4gICAgICAgIHRoaXMuaGFuZGxlRXJyb3IoZXJyb3IpO1xuICAgICAgICB0aGlzLnNldExvYWRpbmdTdGF0ZShmYWxzZSk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBzaG91bGRVc2VJZnJhbWUoKTogYm9vbGVhbiB7XG4gICAgLy8gTW9zdCBiYW5rcyBkb24ndCBhbGxvdyBpZnJhbWVzIGR1ZSB0byBYLUZyYW1lLU9wdGlvbnNcbiAgICAvLyBTbyBkZWZhdWx0IHRvIGZhbHNlICh1c2UgcG9wdXAgd2luZG93IGluc3RlYWQpXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgcHJpdmF0ZSBvcGVuQ29uc2VudEluTmV3V2luZG93KGF1dGhvcml6YXRpb25Vcmw6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IGlzTW9iaWxlID0gL0FuZHJvaWR8d2ViT1N8aVBob25lfGlQYWR8aVBvZHxCbGFja0JlcnJ5fElFTW9iaWxlfE9wZXJhIE1pbmkvaS50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpO1xuXG4gICAgbGV0IGNvbnNlbnRXaW5kb3c6IFdpbmRvdyB8IG51bGw7XG5cbiAgICBpZiAoaXNNb2JpbGUpIHtcbiAgICAgIC8vIE9uIG1vYmlsZSwgb3BlbiBpbiBzYW1lIHRhYiAoZnVsbCByZWRpcmVjdCkgb3IgbmV3IHRhYlxuICAgICAgLy8gVXNpbmcgX2JsYW5rIHdpbGwgb3BlbiBhIG5ldyB0YWIgYW5kIGFsbG93IHVzZXIgdG8gcmV0dXJuXG4gICAgICBjb25zZW50V2luZG93ID0gd2luZG93Lm9wZW4oYXV0aG9yaXphdGlvblVybCwgJ19ibGFuaycpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBPbiBkZXNrdG9wLCBvcGVuIGluIGEgcG9wdXAgd2luZG93XG4gICAgICBjb25zdCB3aWR0aCA9IDgwMDtcbiAgICAgIGNvbnN0IGhlaWdodCA9IDYwMDtcbiAgICAgIGNvbnN0IGxlZnQgPSAod2luZG93LnNjcmVlbi53aWR0aCAtIHdpZHRoKSAvIDI7XG4gICAgICBjb25zdCB0b3AgPSAod2luZG93LnNjcmVlbi5oZWlnaHQgLSBoZWlnaHQpIC8gMjtcblxuICAgICAgY29uc2VudFdpbmRvdyA9IHdpbmRvdy5vcGVuKFxuICAgICAgICBhdXRob3JpemF0aW9uVXJsLFxuICAgICAgICAnYmFua0NvbnNlbnQnLFxuICAgICAgICBgd2lkdGg9JHt3aWR0aH0saGVpZ2h0PSR7aGVpZ2h0fSxsZWZ0PSR7bGVmdH0sdG9wPSR7dG9wfSx0b29sYmFyPW5vLG1lbnViYXI9bm8sc2Nyb2xsYmFycz15ZXMscmVzaXphYmxlPXllc2BcbiAgICAgICk7XG4gICAgfVxuXG4gICAgaWYgKGNvbnNlbnRXaW5kb3cpIHtcbiAgICAgIC8vIFNob3cgYSBtZXNzYWdlIHRoYXQgY29uc2VudCBpcyBpbiBwcm9ncmVzc1xuICAgICAgdGhpcy5jdXJyZW50U3RhdGUubmV4dChPcGVuQmFua2luZ1N0YXRlLkNvbnNlbnQpO1xuXG4gICAgICAvLyBDaGVjayBpZiB3aW5kb3cgaXMgY2xvc2VkIHBlcmlvZGljYWxseSAod29ya3MgYmV0dGVyIG9uIGRlc2t0b3ApXG4gICAgICBpZiAoIWlzTW9iaWxlKSB7XG4gICAgICAgIGNvbnN0IGNoZWNrV2luZG93Q2xvc2VkID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgICAgIGlmIChjb25zZW50V2luZG93ICYmIGNvbnNlbnRXaW5kb3cuY2xvc2VkKSB7XG4gICAgICAgICAgICBjbGVhckludGVydmFsKGNoZWNrV2luZG93Q2xvc2VkKTtcbiAgICAgICAgICAgIC8vIFdpbmRvdyB3YXMgY2xvc2VkLCB0aGUgcG9sbGluZyB3aWxsIGRldGVjdCB0aGUgY29uc2VudCBzdGF0dXNcbiAgICAgICAgICB9XG4gICAgICAgIH0sIDEwMDApO1xuICAgICAgfVxuICAgICAgLy8gT24gbW9iaWxlLCB3ZSByZWx5IGVudGlyZWx5IG9uIHBvbGxpbmcgc2luY2Ugd2luZG93LmNsb3NlZCBkb2Vzbid0IHdvcmsgcmVsaWFibHlcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gUG9wdXAgd2FzIGJsb2NrZWRcbiAgICAgIHRoaXMuX2lzQ29uc2VudEVycm9yID0gZmFsc2U7XG4gICAgICB0aGlzLl9lcnJvck1lc3NhZ2UgPSAnUGxlYXNlIGFsbG93IHBvcHVwcyBmb3IgdGhpcyBzaXRlIHRvIGNvbXBsZXRlIGJhbmsgYXV0aG9yaXphdGlvbi4gVGhlbiBjbGljayBSZXRyeSB0byB0cnkgYWdhaW4uJztcbiAgICAgIHRoaXMuY3VycmVudFN0YXRlLm5leHQoT3BlbkJhbmtpbmdTdGF0ZS5FcnJvcik7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBjaGVja0lmcmFtZUxvYWQoYXV0aG9yaXphdGlvblVybDogc3RyaW5nKTogdm9pZCB7XG4gICAgLy8gSWYgaWZyYW1lIGZhaWxzIHRvIGxvYWQgKFgtRnJhbWUtT3B0aW9ucyksIGZhbGwgYmFjayB0byBwb3B1cFxuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgLy8gQ2hlY2sgaWYgd2UncmUgc3RpbGwgaW4gY29uc2VudCBzdGF0ZSAoaWZyYW1lIG1pZ2h0IGhhdmUgZmFpbGVkKVxuICAgICAgaWYgKHRoaXMuY3VycmVudFN0YXRlLnZhbHVlID09PSBPcGVuQmFua2luZ1N0YXRlLkNvbnNlbnQgJiYgdGhpcy5hdXRob3JpemF0aW9uVXJsKSB7XG4gICAgICAgIC8vIENsZWFyIGlmcmFtZSBhbmQgb3BlbiBpbiBuZXcgd2luZG93XG4gICAgICAgIGNvbnNvbGUud2FybignSWZyYW1lIG1pZ2h0IGJlIGJsb2NrZWQsIG9wZW5pbmcgaW4gbmV3IHdpbmRvdycpO1xuICAgICAgICB0aGlzLmF1dGhvcml6YXRpb25VcmwgPSBudWxsO1xuICAgICAgICB0aGlzLm9wZW5Db25zZW50SW5OZXdXaW5kb3coYXV0aG9yaXphdGlvblVybCk7XG4gICAgICB9XG4gICAgfSwgMjAwMCk7XG4gIH1cblxuICBwcml2YXRlIHN0YXJ0Q29uc2VudFBvbGxpbmcoKTogdm9pZCB7XG4gICAgLy8gUG9sbCBldmVyeSA1IHNlY29uZHMgZm9yIGNvbnNlbnQgc3RhdHVzICh1c2VyIG5lZWRzIHRpbWUgdG8gY29tcGxldGUgYmFuayBhdXRob3JpemF0aW9uKVxuICAgIHRoaXMucG9sbFN1YnNjcmlwdGlvbiA9IGludGVydmFsKDEwMDAwKVxuICAgICAgLnBpcGUoXG4gICAgICAgIHN3aXRjaE1hcCgoKSA9PiB0aGlzLm9wZW5CYW5raW5nU2VydmljZS5nZXRDb25zZW50U3RhdHVzKHRoaXMuc2VsZWN0ZWRCYW5rIHx8ICdERUZBVUxUJywgdGhpcy5jb25zZW50SWQpKSxcbiAgICAgICAgdGFrZVdoaWxlKChzdGF0dXMpID0+IHN0YXR1cy5zdGF0dXMgPT09ICdyZWNlaXZlZCcsIHRydWUpLFxuICAgICAgICBmaW5hbGl6ZSgoKSA9PiB0aGlzLnBvbGxTdWJzY3JpcHRpb24/LnVuc3Vic2NyaWJlKCkpXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKHtcbiAgICAgICAgbmV4dDogKHN0YXR1czogQ29uc2VudFN0YXR1cykgPT4ge1xuICAgICAgICAgIGlmIChzdGF0dXMuc3RhdHVzID09PSAndmFsaWQnKSB7XG4gICAgICAgICAgICB0aGlzLm9uQ29uc2VudEFwcHJvdmVkKCk7XG4gICAgICAgICAgfSBlbHNlIGlmIChzdGF0dXMuc3RhdHVzID09PSAncmVqZWN0ZWQnIHx8XG4gICAgICAgICAgICAgICAgICAgICBzdGF0dXMuc3RhdHVzID09PSAnZXhwaXJlZCcgfHxcbiAgICAgICAgICAgICAgICAgICAgIHN0YXR1cy5zdGF0dXMgPT09ICd0ZXJtaW5hdGVkJyB8fFxuICAgICAgICAgICAgICAgICAgICAgc3RhdHVzLnN0YXR1cyA9PT0gJ3Jldm9rZWRCeVBzdScpIHtcbiAgICAgICAgICAgIHRoaXMub25Db25zZW50UmVqZWN0ZWQoc3RhdHVzKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIGVycm9yOiAoZXJyb3IpID0+IHtcbiAgICAgICAgICB0aGlzLmhhbmRsZUVycm9yKGVycm9yKTtcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBvbkNvbnNlbnRBcHByb3ZlZCgpOiB2b2lkIHtcbiAgICB0aGlzLmN1cnJlbnRTdGF0ZS5uZXh0KE9wZW5CYW5raW5nU3RhdGUuUHJvY2Vzc2luZyk7XG4gICAgdGhpcy5mZXRjaEFjY291bnREYXRhKCk7XG4gIH1cblxuICBwcml2YXRlIG9uQ29uc2VudFJlamVjdGVkKHN0YXR1czogQ29uc2VudFN0YXR1cyk6IHZvaWQge1xuICAgIHRoaXMuX2lzQ29uc2VudEVycm9yID0gdHJ1ZTtcbiAgICB0aGlzLl9jb25zZW50RXJyb3JTdGF0dXMgPSBzdGF0dXMuc3RhdHVzO1xuICAgIHRoaXMuX2Vycm9yTWVzc2FnZSA9ICcnOyAvLyBDbGVhciB0byB1c2UgdHJhbnNsYXRlZCBtZXNzYWdlXG4gICAgdGhpcy5jdXJyZW50U3RhdGUubmV4dChPcGVuQmFua2luZ1N0YXRlLkVycm9yKTtcbiAgfVxuXG4gIHByaXZhdGUgZmV0Y2hBY2NvdW50RGF0YSgpOiB2b2lkIHtcbiAgICB0aGlzLnNldExvYWRpbmdTdGF0ZSh0cnVlKTtcblxuICAgIC8vIEZpcnN0LCBmZXRjaCBhY2NvdW50c1xuICAgIHRoaXMub3BlbkJhbmtpbmdTZXJ2aWNlLmdldEFjY291bnRzKHRoaXMuY29uc2VudElkLCB0aGlzLnNlbGVjdGVkQmFuayB8fCAnREVGQVVMVCcpLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAoYWNjb3VudHMpID0+IHtcbiAgICAgICAgdGhpcy5hY2NvdW50cyA9IGFjY291bnRzO1xuXG4gICAgICAgIC8vIFNlbGVjdCBhbGwgYWNjb3VudHMgZm9yIHRyYW5zYWN0aW9uIGZldGNoaW5nXG4gICAgICAgIHRoaXMuc2VsZWN0ZWRBY2NvdW50cyA9IGFjY291bnRzLm1hcChhID0+IGEuYWNjb3VudElkKTtcblxuICAgICAgICAvLyBGZXRjaCB0cmFuc2FjdGlvbnMgZm9yIGVhY2ggYWNjb3VudFxuICAgICAgICB0aGlzLmZldGNoVHJhbnNhY3Rpb25zRm9yQWNjb3VudHMoKTtcbiAgICAgIH0sXG4gICAgICBlcnJvcjogKGVycm9yKSA9PiB7XG4gICAgICAgIHRoaXMuaGFuZGxlRXJyb3IoZXJyb3IpO1xuICAgICAgICB0aGlzLnNldExvYWRpbmdTdGF0ZShmYWxzZSk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBmZXRjaFRyYW5zYWN0aW9uc0ZvckFjY291bnRzKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNlbGVjdGVkQWNjb3VudHMubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aGlzLmNvbXBsZXRlUHJvY2VzcygpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHRyYW5zYWN0aW9uUmVxdWVzdHMgPSB0aGlzLnNlbGVjdGVkQWNjb3VudHMubWFwKGFjY291bnRJZCA9PiB7XG4gICAgICAvLyBDYWxjdWxhdGUgZGF0ZSByYW5nZSBiYXNlZCBvbiBiYW5rLXNwZWNpZmljIGxpbWl0c1xuICAgICAgY29uc3QgYmFua0NvZGUgPSB0aGlzLnNlbGVjdGVkQmFuayB8fCAnREVGQVVMVCc7XG4gICAgICBjb25zdCBtYXhEYXlzID0gdGhpcy5vcGVuQmFua2luZ1NlcnZpY2UuZ2V0TWF4VHJhbnNhY3Rpb25QZXJpb2QoYmFua0NvZGUpO1xuXG4gICAgICBjb25zdCB0b0RhdGUgPSBuZXcgRGF0ZSgpO1xuICAgICAgY29uc3QgZnJvbURhdGUgPSBuZXcgRGF0ZSgpO1xuXG4gICAgICAvLyBJZiB1bmxpbWl0ZWQgKC0xKSBvciB2ZXJ5IGxvbmcgcGVyaW9kLCB1c2UgMzY1IGRheXMgYXMgcHJhY3RpY2FsIGxpbWl0XG4gICAgICAvLyBPdGhlcndpc2UgdXNlIHRoZSBiYW5rJ3Mgc3BlY2lmaWMgbGltaXRcbiAgICAgIGNvbnN0IGRheXNUb0ZldGNoID0gbWF4RGF5cyA9PT0gLTEgPyAzNjUgOiBNYXRoLm1pbihtYXhEYXlzLCAzNjUpO1xuICAgICAgZnJvbURhdGUuc2V0RGF0ZShmcm9tRGF0ZS5nZXREYXRlKCkgLSBkYXlzVG9GZXRjaCk7XG5cbiAgICAgIHJldHVybiB0aGlzLm9wZW5CYW5raW5nU2VydmljZS5nZXRUcmFuc2FjdGlvbnMoe1xuICAgICAgICBjb25zZW50SWQ6IHRoaXMuY29uc2VudElkLFxuICAgICAgICBiYW5rQ29kZTogYmFua0NvZGUsXG4gICAgICAgIGFjY291bnRJZDogYWNjb3VudElkLFxuICAgICAgICBmcm9tRGF0ZTogZnJvbURhdGUudG9JU09TdHJpbmcoKSxcbiAgICAgICAgdG9EYXRlOiB0b0RhdGUudG9JU09TdHJpbmcoKSxcbiAgICAgICAgcGFnZTogMSxcbiAgICAgICAgcGFnZVNpemU6IDUwXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIC8vIEV4ZWN1dGUgYWxsIHRyYW5zYWN0aW9uIHJlcXVlc3RzXG4gICAgUHJvbWlzZS5hbGwodHJhbnNhY3Rpb25SZXF1ZXN0cy5tYXAocmVxID0+IHJlcS50b1Byb21pc2UoKSkpXG4gICAgICAudGhlbihyZXN1bHRzID0+IHtcbiAgICAgICAgLy8gU3RvcmUgdHJhbnNhY3Rpb24gcGFnZXNcbiAgICAgICAgcmVzdWx0cy5mb3JFYWNoKChwYWdlLCBpbmRleCkgPT4ge1xuICAgICAgICAgIGlmIChwYWdlKSB7XG4gICAgICAgICAgICB0aGlzLnRyYW5zYWN0aW9uUGFnZXMuc2V0KHRoaXMuc2VsZWN0ZWRBY2NvdW50c1tpbmRleF0sIHBhZ2UpO1xuICAgICAgICAgICAgLy8gRmxhdHRlbiBhbGwgdHJhbnNhY3Rpb25zIGludG8gc2luZ2xlIGFycmF5XG4gICAgICAgICAgICB0aGlzLnRyYW5zYWN0aW9ucy5wdXNoKC4uLnBhZ2UuaXRlbXMpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5jb21wbGV0ZVByb2Nlc3MoKTtcbiAgICAgIH0pXG4gICAgICAuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBmZXRjaGluZyB0cmFuc2FjdGlvbnM6JywgZXJyb3IpO1xuICAgICAgICAvLyBFdmVuIGlmIHRyYW5zYWN0aW9ucyBmYWlsLCB3ZSBjYW4gY29udGludWUgd2l0aCBhY2NvdW50c1xuICAgICAgICB0aGlzLmNvbXBsZXRlUHJvY2VzcygpO1xuICAgICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGNvbXBsZXRlUHJvY2VzcygpOiB2b2lkIHtcbiAgICAvLyBTdG9yZSBkYXRhIGluIGZvcm0gbW9kZWxcbiAgICB0aGlzLnN0b3JlQWNjb3VudERhdGEoKTtcblxuICAgIHRoaXMuY3VycmVudFN0YXRlLm5leHQoT3BlbkJhbmtpbmdTdGF0ZS5Db21wbGV0ZSk7XG4gICAgdGhpcy5zZXRMb2FkaW5nU3RhdGUoZmFsc2UpO1xuXG4gICAgLy8gQXV0by1wcm9jZWVkIHRvIG5leHQgc3RlcCBhZnRlciBhIHNob3J0IGRlbGF5XG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB0aGlzLm9uRXZlbnQoJ3N1Y2Nlc3MnKTtcbiAgICB9LCAyMDAwKTtcbiAgfVxuXG4gIHByaXZhdGUgc3RvcmVBY2NvdW50RGF0YSgpOiB2b2lkIHtcbiAgICAvLyBTdG9yZSB0aGUgZmV0Y2hlZCBkYXRhIGluIHRoZSBmb3JtIG1vZGVsIGZvciB1c2UgaW4gbGF0ZXIgc3RlcHNcbiAgICBjb25zdCBncm91cCA9IHRoaXMuZm9ybSBhcyBGb3JtR3JvdXA7XG5cbiAgICAvLyBBZGQgYWNjb3VudHMgYW5kIHRyYW5zYWN0aW9ucyB0byBmb3JtXG4gICAgZ3JvdXAuYWRkQ29udHJvbCgnb3BlbkJhbmtpbmdBY2NvdW50cycsIG5ldyBGb3JtQ29udHJvbCh0aGlzLmFjY291bnRzKSk7XG4gICAgZ3JvdXAuYWRkQ29udHJvbCgnb3BlbkJhbmtpbmdUcmFuc2FjdGlvbnMnLCBuZXcgRm9ybUNvbnRyb2wodGhpcy50cmFuc2FjdGlvbnMpKTtcbiAgICBncm91cC5hZGRDb250cm9sKCdvcGVuQmFua2luZ0NvbnNlbnRJZCcsIG5ldyBGb3JtQ29udHJvbCh0aGlzLmNvbnNlbnRJZCkpO1xuICAgIGdyb3VwLmFkZENvbnRyb2woJ29wZW5CYW5raW5nQmFua0NvZGUnLCBuZXcgRm9ybUNvbnRyb2wodGhpcy5zZWxlY3RlZEJhbmspKTtcblxuICAgIC8vIFN0b3JlIGFjY291bnQgc3VtbWFyeVxuICAgIGNvbnN0IGFjY291bnRTdW1tYXJ5ID0ge1xuICAgICAgdG90YWxBY2NvdW50czogdGhpcy5hY2NvdW50cy5sZW5ndGgsXG4gICAgICB0b3RhbEJhbGFuY2U6IHRoaXMuYWNjb3VudHMucmVkdWNlKChzdW0sIGFjYykgPT4gc3VtICsgYWNjLmJhbGFuY2UsIDApLFxuICAgICAgY3VycmVuY2llczogWy4uLm5ldyBTZXQodGhpcy5hY2NvdW50cy5tYXAoYSA9PiBhLmN1cnJlbmN5KSldLFxuICAgICAgdHJhbnNhY3Rpb25Db3VudDogdGhpcy50cmFuc2FjdGlvbnMubGVuZ3RoLFxuICAgICAgb2xkZXN0VHJhbnNhY3Rpb246IHRoaXMudHJhbnNhY3Rpb25zLmxlbmd0aCA+IDBcbiAgICAgICAgPyB0aGlzLnRyYW5zYWN0aW9ucy5yZWR1Y2UoKG9sZGVzdCwgdCkgPT5cbiAgICAgICAgICAgIG5ldyBEYXRlKHQuYm9va2luZ0RhdGUgfHwgdC50cmFuc2FjdGlvbkRhdGUgfHwgJycpIDwgbmV3IERhdGUob2xkZXN0LmJvb2tpbmdEYXRlIHx8IG9sZGVzdC50cmFuc2FjdGlvbkRhdGUgfHwgJycpID8gdCA6IG9sZGVzdFxuICAgICAgICAgICkuYm9va2luZ0RhdGVcbiAgICAgICAgOiBudWxsLFxuICAgICAgbmV3ZXN0VHJhbnNhY3Rpb246IHRoaXMudHJhbnNhY3Rpb25zLmxlbmd0aCA+IDBcbiAgICAgICAgPyB0aGlzLnRyYW5zYWN0aW9ucy5yZWR1Y2UoKG5ld2VzdCwgdCkgPT5cbiAgICAgICAgICAgIG5ldyBEYXRlKHQuYm9va2luZ0RhdGUgfHwgdC50cmFuc2FjdGlvbkRhdGUgfHwgJycpID4gbmV3IERhdGUobmV3ZXN0LmJvb2tpbmdEYXRlIHx8IG5ld2VzdC50cmFuc2FjdGlvbkRhdGUgfHwgJycpID8gdCA6IG5ld2VzdFxuICAgICAgICAgICkuYm9va2luZ0RhdGVcbiAgICAgICAgOiBudWxsXG4gICAgfTtcblxuICAgIGdyb3VwLmFkZENvbnRyb2woJ29wZW5CYW5raW5nQWNjb3VudFN1bW1hcnknLCBuZXcgRm9ybUNvbnRyb2woYWNjb3VudFN1bW1hcnkpKTtcblxuICAgIC8vIFBvcHVsYXRlIHRoZSBzdGVwJ3MgY29udHJvbHMgZm9yIFNhdmVBcHBEYXRhXG4gICAgdGhpcy5wb3B1bGF0ZVN0ZXBDb250cm9scygpO1xuICB9XG5cbiAgcHJpdmF0ZSBwb3B1bGF0ZVN0ZXBDb250cm9scygpOiB2b2lkIHtcbiAgICAvLyBGaW5kIGFuZCBwb3B1bGF0ZSB0aGUgY29udHJvbHMgaW4gdGhlIHN0ZXAgc2VjdGlvbnNcbiAgICBpZiAoIXRoaXMuc3RlcERhdGE/LnNlY3Rpb25zKSByZXR1cm47XG5cbiAgICAvLyBHZXQgZW1haWwgZnJvbSBjb25maWcgbWFwcGluZyBvciB1c2UgZW1wdHkgc3RyaW5nXG4gICAgY29uc3QgZW1haWwgPSB0aGlzLmdldEZpZWxkVmFsdWUodGhpcy5jb25maWcuZW1haWxDb2xsZWN0ZWQpIHx8ICcnO1xuXG4gICAgLy8gR2V0IHRoZSBmaXJzdCBhY2NvdW50IGFzIHRoZSBwcmltYXJ5IGFjY291bnRcbiAgICBjb25zdCBwcmltYXJ5QWNjb3VudCA9IHRoaXMuYWNjb3VudHMubGVuZ3RoID4gMCA/IHRoaXMuYWNjb3VudHNbMF0gOiBudWxsO1xuXG4gICAgLy8gQ2FsY3VsYXRlIHRvdGFsIGJhbGFuY2VcbiAgICBjb25zdCB0b3RhbEJhbGFuY2UgPSB0aGlzLmFjY291bnRzLnJlZHVjZSgoc3VtLCBhY2MpID0+IHN1bSArIGFjYy5iYWxhbmNlLCAwKTtcblxuICAgIC8vIENhbGN1bGF0ZSB0cmFuc2FjdGlvbiBwZXJpb2QgaW4gbW9udGhzXG4gICAgY29uc3QgbW9udGhzID0gdGhpcy5nZXRUcmFuc2FjdGlvblBlcmlvZE1vbnRocygpO1xuXG4gICAgLy8gTWFwIG9mIGNvbnRyb2wgaWRlbnRpZmllcnMgdG8gdmFsdWVzXG4gICAgY29uc3QgY29udHJvbFZhbHVlczogeyBba2V5OiBzdHJpbmddOiBhbnkgfSA9IHtcbiAgICAgICdPcGVuQmFua2luZ0NvbnNlbnRTdGF0dXMnOiB0cnVlLCAvLyBDb25zZW50IHdhcyBzdWNjZXNzZnVsbHkgdmFsaWRhdGVkXG4gICAgICAnT3BlbkJhbmtpbmdFbWFpbCc6IGVtYWlsLFxuICAgICAgJ09wZW5CYW5raW5nQWNjb3VudE51bWJlcic6IHByaW1hcnlBY2NvdW50Py5pYmFuIHx8ICcnLFxuICAgICAgJ09wZW5CYW5raW5nQWNjb3VudEhvbGRlcic6IHByaW1hcnlBY2NvdW50Py5hY2NvdW50SG9sZGVyTmFtZSB8fCAnJyxcbiAgICAgICdPcGVuQmFua2luZ0JhbmtDb2RlJzogdGhpcy5zZWxlY3RlZEJhbmsgfHwgJycsXG4gICAgICAnT3BlbkJhbmtpbmdDb25zZW50SWQnOiB0aGlzLmNvbnNlbnRJZCxcbiAgICAgICdPcGVuQmFua2luZ0FjY291bnRzQ291bnQnOiB0aGlzLmFjY291bnRzLmxlbmd0aCxcbiAgICAgICdPcGVuQmFua2luZ0FjY291bnRzRGF0YSc6IEpTT04uc3RyaW5naWZ5KHRoaXMuYWNjb3VudHMpLFxuICAgICAgJ09wZW5CYW5raW5nVG90YWxCYWxhbmNlJzogdG90YWxCYWxhbmNlLFxuICAgICAgJ09wZW5CYW5raW5nVHJhbnNhY3Rpb25zQ291bnQnOiB0aGlzLnRyYW5zYWN0aW9ucy5sZW5ndGgsXG4gICAgICAnT3BlbkJhbmtpbmdUcmFuc2FjdGlvbnNEYXRhJzogSlNPTi5zdHJpbmdpZnkodGhpcy50cmFuc2FjdGlvbnMpLFxuICAgICAgJ09wZW5CYW5raW5nVHJhbnNhY3Rpb25zUGVyaW9kJzogbW9udGhzXG4gICAgfTtcblxuICAgIC8vIEFkZCBhbGwgY29udHJvbCB2YWx1ZXMgdG8gdGhlIGZvcm1cbiAgICBjb25zdCBncm91cCA9IHRoaXMuZm9ybSBhcyBGb3JtR3JvdXA7XG4gICAgT2JqZWN0LmtleXMoY29udHJvbFZhbHVlcykuZm9yRWFjaChjb250cm9sSWQgPT4ge1xuICAgICAgaWYgKCFncm91cC5jb250YWlucyhjb250cm9sSWQpKSB7XG4gICAgICAgIGdyb3VwLmFkZENvbnRyb2woY29udHJvbElkLCBuZXcgRm9ybUNvbnRyb2woY29udHJvbFZhbHVlc1tjb250cm9sSWRdKSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBncm91cC5nZXQoY29udHJvbElkKT8uc2V0VmFsdWUoY29udHJvbFZhbHVlc1tjb250cm9sSWRdKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIC8vIFRoZSB2YWx1ZXMgYXJlIG5vdyBzdG9yZWQgaW4gdGhlIGZvcm0gbW9kZWwgYW5kIHdpbGwgYmUgc2F2ZWQgd2l0aCBTYXZlQXBwRGF0YVxuICAgIC8vIFdoZW4gdGhlIGZvcm0gaXMgc3VibWl0dGVkLCB0aGVzZSB2YWx1ZXMgd2lsbCBiZSBhc3NvY2lhdGVkIHdpdGggdGhlIHN0ZXAncyBjb250cm9sc1xuICB9XG5cbiAgcmV0cnkoKTogdm9pZCB7XG4gICAgdGhpcy5fZXJyb3JNZXNzYWdlID0gJyc7XG4gICAgdGhpcy5faXNDb25zZW50RXJyb3IgPSBmYWxzZTtcbiAgICB0aGlzLl9jb25zZW50RXJyb3JTdGF0dXMgPSAnJztcbiAgICB0aGlzLmFjY291bnRzID0gW107XG4gICAgdGhpcy50cmFuc2FjdGlvbnMgPSBbXTtcbiAgICB0aGlzLnNlbGVjdGVkQWNjb3VudHMgPSBbXTtcbiAgICB0aGlzLnRyYW5zYWN0aW9uUGFnZXMuY2xlYXIoKTtcbiAgICB0aGlzLmNvbnNlbnRJZCA9ICcnO1xuICAgIHRoaXMuc2VsZWN0ZWRCYW5rID0gJyc7XG4gICAgdGhpcy5zZWxlY3RlZEJhbmtPYmplY3QgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5iYW5rcyA9IFtdO1xuICAgIHRoaXMuYXV0aG9yaXphdGlvblVybCA9IG51bGw7XG5cbiAgICAvLyBSZXNldCB0byBpbml0aWFsIHN0YXRlIC0gdXNlciBuZWVkcyB0byBjbGljayBOZXh0IGFnYWluXG4gICAgdGhpcy5jdXJyZW50U3RhdGUubmV4dChPcGVuQmFua2luZ1N0YXRlLkluaXRpYWwpO1xuICB9XG5cbiAgcHJpdmF0ZSBoYW5kbGVFcnJvcihlcnJvcjogSHR0cEVycm9yUmVzcG9uc2UpOiB2b2lkIHtcbiAgICBjb25zb2xlLmVycm9yKCdPcGVuQmFua2luZyBlcnJvcjonLCBlcnJvcik7XG4gICAgdGhpcy5faXNDb25zZW50RXJyb3IgPSBmYWxzZTtcbiAgICB0aGlzLl9jb25zZW50RXJyb3JTdGF0dXMgPSAnJztcbiAgICB0aGlzLl9lcnJvck1lc3NhZ2UgPSBlcnJvci5lcnJvcj8ubWVzc2FnZSB8fCBlcnJvci5tZXNzYWdlIHx8ICdBbiBlcnJvciBvY2N1cnJlZC4gUGxlYXNlIHRyeSBhZ2Fpbi4nO1xuICAgIHRoaXMuY3VycmVudFN0YXRlLm5leHQoT3BlbkJhbmtpbmdTdGF0ZS5FcnJvcik7XG4gIH1cblxuICBwcml2YXRlIGdldFRyYW5zbGF0ZWRNZXNzYWdlKHRyYW5zbGF0aW9uczogYW55W10gfCB1bmRlZmluZWQsIGRlZmF1bHRNZXNzYWdlOiBzdHJpbmcgPSAnJyk6IHN0cmluZyB7XG4gICAgaWYgKCF0cmFuc2xhdGlvbnMgfHwgdHJhbnNsYXRpb25zLmxlbmd0aCA9PT0gMCkgcmV0dXJuIGRlZmF1bHRNZXNzYWdlO1xuXG4gICAgY29uc3QgbGFuZ0lzbyA9IHRoaXMucHJvcHM/LlsnbGFuZ0lzbyddIHx8IHRoaXMubGFuZ0lzbyB8fCAnZW4nO1xuICAgIGNvbnN0IHRyYW5zbGF0aW9uID0gdHJhbnNsYXRpb25zLmZpbmQodCA9PiB0Lmxhbmd1YWdlSXNvID09PSBsYW5nSXNvKTtcbiAgICByZXR1cm4gdHJhbnNsYXRpb24/LnZhbHVlIHx8IHRyYW5zbGF0aW9uc1swXT8udmFsdWUgfHwgZGVmYXVsdE1lc3NhZ2U7XG4gIH1cblxuICBnZXQgaW5pdGlhbE1lc3NhZ2UoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5nZXRUcmFuc2xhdGVkTWVzc2FnZShcbiAgICAgIHRoaXMuY29uZmlnLmluaXRpYWxNZXNzYWdlVHJhbnNsYXRpb25zLFxuICAgICAgJ1RvIGNvbnRpbnVlLCB3ZSBuZWVkIHRvIHZlcmlmeSB5b3VyIGJhbmsgYWNjb3VudCBpbmZvcm1hdGlvbi4gQ2xpY2sgTmV4dCB0byBwcm9jZWVkLidcbiAgICApO1xuICB9XG5cbiAgZ2V0IHdhaXRpbmdNZXNzYWdlKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VHJhbnNsYXRlZE1lc3NhZ2UoXG4gICAgICB0aGlzLmNvbmZpZy53YWl0aW5nTWVzc2FnZVRyYW5zbGF0aW9ucyxcbiAgICAgICdQbGVhc2Ugd2FpdCB3aGlsZSB3ZSByZXRyaWV2ZSB5b3VyIGFjY291bnQgaW5mb3JtYXRpb24uLi4nXG4gICAgKTtcbiAgfVxuXG4gIGdldCBjb21wbGV0aW9uTWVzc2FnZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmdldFRyYW5zbGF0ZWRNZXNzYWdlKFxuICAgICAgdGhpcy5jb25maWcuY29tcGxldGlvbk1lc3NhZ2VUcmFuc2xhdGlvbnMsXG4gICAgICAnQWNjb3VudCBpbmZvcm1hdGlvbiBzdWNjZXNzZnVsbHkgcmV0cmlldmVkLidcbiAgICApO1xuICB9XG5cbiAgZ2V0IHJldHJ5QnV0dG9uTGFiZWwoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5nZXRUcmFuc2xhdGVkTWVzc2FnZShcbiAgICAgIHRoaXMuY29uZmlnLnJldHJ5QnV0dG9uTGFiZWxUcmFuc2xhdGlvbnMsXG4gICAgICAnUmV0cnknXG4gICAgKTtcbiAgfVxuXG4gIGdldCBlcnJvck1lc3NhZ2UoKTogc3RyaW5nIHtcbiAgICAvLyBJZiBpdCdzIGEgY29uc2VudCBlcnJvciwgdXNlIHRoZSB0cmFuc2xhdGVkIGNvbnNlbnQgZXJyb3IgbWVzc2FnZVxuICAgIGlmICh0aGlzLl9pc0NvbnNlbnRFcnJvcikge1xuICAgICAgY29uc3QgbWVzc2FnZSA9IHRoaXMuZ2V0VHJhbnNsYXRlZE1lc3NhZ2UoXG4gICAgICAgIHRoaXMuY29uZmlnLmNvbnNlbnRFcnJvck1lc3NhZ2VUcmFuc2xhdGlvbnMsXG4gICAgICAgICdZb3UgbXVzdCBwcm92aWRlIGNvbnNlbnQgdG8gY29udGludWUuIFBsZWFzZSB0cnkgYWdhaW4uJ1xuICAgICAgKTtcbiAgICAgIHJldHVybiB0aGlzLl9jb25zZW50RXJyb3JTdGF0dXMgPyBgJHttZXNzYWdlfSAoU3RhdHVzOiAke3RoaXMuX2NvbnNlbnRFcnJvclN0YXR1c30pYCA6IG1lc3NhZ2U7XG4gICAgfVxuICAgIC8vIE90aGVyd2lzZSByZXR1cm4gdGhlIHN0b3JlZCBlcnJvciBtZXNzYWdlIChmcm9tIEFQSSBvciBkZWZhdWx0KVxuICAgIHJldHVybiB0aGlzLl9lcnJvck1lc3NhZ2UgfHwgJ0FuIGVycm9yIG9jY3VycmVkLiBQbGVhc2UgdHJ5IGFnYWluLic7XG4gIH1cblxuICAvLyBCdXR0b24gdHJhbnNsYXRpb25zXG4gIGdldCBuZXh0QnV0dG9uTGFiZWwoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5nZXRUcmFuc2xhdGVkTWVzc2FnZShcbiAgICAgIHRoaXMuY29uZmlnLm5leHRCdXR0b25UcmFuc2xhdGlvbnMsXG4gICAgICAnTmV4dCdcbiAgICApO1xuICB9XG5cbiAgZ2V0IGNvbnRpbnVlQnV0dG9uTGFiZWwoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5nZXRUcmFuc2xhdGVkTWVzc2FnZShcbiAgICAgIHRoaXMuY29uZmlnLmNvbnRpbnVlQnV0dG9uVHJhbnNsYXRpb25zLFxuICAgICAgJ0NvbnRpbnVlJ1xuICAgICk7XG4gIH1cblxuICAvLyBVSSBsYWJlbCB0cmFuc2xhdGlvbnNcbiAgZ2V0IHNlbGVjdEJhbmtUaXRsZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmdldFRyYW5zbGF0ZWRNZXNzYWdlKFxuICAgICAgdGhpcy5jb25maWcuc2VsZWN0QmFua1RyYW5zbGF0aW9ucyxcbiAgICAgICdTZWxlY3QgeW91ciBiYW5rJ1xuICAgICk7XG4gIH1cblxuICBnZXQgY29uc2VudFRpdGxlKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VHJhbnNsYXRlZE1lc3NhZ2UoXG4gICAgICB0aGlzLmNvbmZpZy5jb25zZW50VGl0bGVUcmFuc2xhdGlvbnMsXG4gICAgICAnQmFuayBBdXRob3JpemF0aW9uJ1xuICAgICk7XG4gIH1cblxuICBnZXQgY29uc2VudERlc2NyaXB0aW9uKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0VHJhbnNsYXRlZE1lc3NhZ2UoXG4gICAgICB0aGlzLmNvbmZpZy5jb25zZW50RGVzY3JpcHRpb25UcmFuc2xhdGlvbnMsXG4gICAgICAnUGxlYXNlIGNvbXBsZXRlIHRoZSBhdXRob3JpemF0aW9uIHByb2Nlc3Mgd2l0aCB5b3VyIGJhbmsnXG4gICAgKTtcbiAgfVxuXG4gIGdldCB3YWl0aW5nRm9yQXV0aG9yaXphdGlvblRleHQoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5nZXRUcmFuc2xhdGVkTWVzc2FnZShcbiAgICAgIHRoaXMuY29uZmlnLndhaXRpbmdGb3JBdXRob3JpemF0aW9uVHJhbnNsYXRpb25zLFxuICAgICAgJ1dhaXRpbmcgZm9yIGF1dGhvcml6YXRpb24uLi4nXG4gICAgKTtcbiAgfVxuXG4gIGdldCBwb3B1cFdpbmRvd01lc3NhZ2UoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5nZXRUcmFuc2xhdGVkTWVzc2FnZShcbiAgICAgIHRoaXMuY29uZmlnLnBvcHVwV2luZG93TWVzc2FnZVRyYW5zbGF0aW9ucyxcbiAgICAgICdBIG5ldyB3aW5kb3cgaGFzIG9wZW5lZCBmb3IgYmFuayBhdXRob3JpemF0aW9uLiBQbGVhc2UgY29tcGxldGUgdGhlIHByb2Nlc3MgdGhlcmUuJ1xuICAgICk7XG4gIH1cblxuICBnZXQgcG9wdXBCbG9ja2VyTWVzc2FnZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmdldFRyYW5zbGF0ZWRNZXNzYWdlKFxuICAgICAgdGhpcy5jb25maWcucG9wdXBCbG9ja2VyTWVzc2FnZVRyYW5zbGF0aW9ucyxcbiAgICAgICdJZiB0aGUgd2luZG93IGRpZG5cXCd0IG9wZW4sIHBsZWFzZSBjaGVjayB5b3VyIHBvcHVwIGJsb2NrZXIgc2V0dGluZ3MuJ1xuICAgICk7XG4gIH1cblxuICBnZXQgYWNjb3VudHNSZXRyaWV2ZWRMYWJlbCgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLmdldFRyYW5zbGF0ZWRNZXNzYWdlKFxuICAgICAgdGhpcy5jb25maWcuYWNjb3VudHNSZXRyaWV2ZWRUcmFuc2xhdGlvbnMsXG4gICAgICAnQWNjb3VudHMgcmV0cmlldmVkJ1xuICAgICk7XG4gIH1cblxuICBnZXQgdHJhbnNhY3Rpb25zUmV0cmlldmVkTGFiZWwoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5nZXRUcmFuc2xhdGVkTWVzc2FnZShcbiAgICAgIHRoaXMuY29uZmlnLnRyYW5zYWN0aW9uc1JldHJpZXZlZFRyYW5zbGF0aW9ucyxcbiAgICAgICdUcmFuc2FjdGlvbnMgcmV0cmlldmVkJ1xuICAgICk7XG4gIH1cblxuICBnZXQgYWNjb3VudHNTdW1tYXJ5VGV4dCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IGNvdW50ID0gdGhpcy5hY2NvdW50cy5sZW5ndGg7XG4gICAgY29uc3QgdGVtcGxhdGUgPSB0aGlzLmdldFRyYW5zbGF0ZWRNZXNzYWdlKFxuICAgICAgdGhpcy5jb25maWcuYWNjb3VudHNGb3VuZFRyYW5zbGF0aW9ucyxcbiAgICAgICd7MH0gYWNjb3VudChzKSBmb3VuZCdcbiAgICApO1xuICAgIHJldHVybiB0ZW1wbGF0ZS5yZXBsYWNlKCd7MH0nLCBjb3VudC50b1N0cmluZygpKTtcbiAgfVxuXG4gIGdldCB0cmFuc2FjdGlvbnNTdW1tYXJ5VGV4dCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IGNvdW50ID0gdGhpcy50cmFuc2FjdGlvbnMubGVuZ3RoO1xuICAgIGNvbnN0IG1vbnRocyA9IHRoaXMuZ2V0VHJhbnNhY3Rpb25QZXJpb2RNb250aHMoKTtcbiAgICBjb25zdCB0ZW1wbGF0ZSA9IHRoaXMuZ2V0VHJhbnNsYXRlZE1lc3NhZ2UoXG4gICAgICB0aGlzLmNvbmZpZy50cmFuc2FjdGlvbnNTdW1tYXJ5VHJhbnNsYXRpb25zLFxuICAgICAgJ3swfSB0cmFuc2FjdGlvbihzKSByZXRyaWV2ZWQgZm9yIHRoZSBsYXN0IHsxfSBtb250aChzKSdcbiAgICApO1xuICAgIHJldHVybiB0ZW1wbGF0ZS5yZXBsYWNlKCd7MH0nLCBjb3VudC50b1N0cmluZygpKS5yZXBsYWNlKCd7MX0nLCBtb250aHMudG9TdHJpbmcoKSk7XG4gIH1cblxuICBwcml2YXRlIGdldFRyYW5zYWN0aW9uUGVyaW9kTW9udGhzKCk6IG51bWJlciB7XG4gICAgaWYgKCF0aGlzLnNlbGVjdGVkQmFuaykgcmV0dXJuIDM7XG5cbiAgICBjb25zdCBtYXhEYXlzID0gdGhpcy5vcGVuQmFua2luZ1NlcnZpY2UuZ2V0TWF4VHJhbnNhY3Rpb25QZXJpb2QodGhpcy5zZWxlY3RlZEJhbmspO1xuICAgIGNvbnN0IGRheXNUb0ZldGNoID0gbWF4RGF5cyA9PT0gLTEgPyAzNjUgOiBNYXRoLm1pbihtYXhEYXlzLCAzNjUpO1xuICAgIHJldHVybiBNYXRoLnJvdW5kKGRheXNUb0ZldGNoIC8gMzApO1xuICB9XG5cbiAgZ2V0IGlzSW5pdGlhbFN0YXRlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmN1cnJlbnRTdGF0ZS52YWx1ZSA9PT0gT3BlbkJhbmtpbmdTdGF0ZS5Jbml0aWFsO1xuICB9XG5cbiAgZ2V0IGlzQmFua1NlbGVjdGlvblN0YXRlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmN1cnJlbnRTdGF0ZS52YWx1ZSA9PT0gT3BlbkJhbmtpbmdTdGF0ZS5CYW5rU2VsZWN0aW9uO1xuICB9XG5cbiAgZ2V0IGlzQ29uc2VudFN0YXRlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmN1cnJlbnRTdGF0ZS52YWx1ZSA9PT0gT3BlbkJhbmtpbmdTdGF0ZS5Db25zZW50O1xuICB9XG5cbiAgZ2V0IGlzUHJvY2Vzc2luZ1N0YXRlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmN1cnJlbnRTdGF0ZS52YWx1ZSA9PT0gT3BlbkJhbmtpbmdTdGF0ZS5Qcm9jZXNzaW5nO1xuICB9XG5cbiAgZ2V0IGlzRXJyb3JTdGF0ZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5jdXJyZW50U3RhdGUudmFsdWUgPT09IE9wZW5CYW5raW5nU3RhdGUuRXJyb3I7XG4gIH1cblxuICBnZXQgaXNDb21wbGV0ZVN0YXRlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmN1cnJlbnRTdGF0ZS52YWx1ZSA9PT0gT3BlbkJhbmtpbmdTdGF0ZS5Db21wbGV0ZTtcbiAgfVxuXG4gIHByaXZhdGUgc2V0TG9hZGluZ1N0YXRlKGxvYWRpbmc6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLmlzTG9hZGluZyA9IGxvYWRpbmc7XG4gICAgdGhpcy5pc0xvYWRpbmdTdWJqZWN0Lm5leHQobG9hZGluZyk7XG4gICAgdGhpcy5jZHIuZGV0ZWN0Q2hhbmdlcygpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5wb2xsU3Vic2NyaXB0aW9uPy51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuaXNMb2FkaW5nU3ViamVjdC5jb21wbGV0ZSgpO1xuICB9XG59IiwiPGRpdiBjbGFzcz1cIm9wZW4tYmFua2luZy1jb250YWluZXJcIj5cbiAgPCEtLSBJbml0aWFsIFN0YXRlIC0tPlxuICA8ZGl2ICpuZ0lmPVwiaXNJbml0aWFsU3RhdGVcIiBjbGFzcz1cInN0YXRlLWluaXRpYWxcIj5cbiAgICA8ZGl2IGNsYXNzPVwibWVzc2FnZS1jb250YWluZXJcIj5cbiAgICAgIDxwIGNsYXNzPVwibWVzc2FnZVwiPnt7IGluaXRpYWxNZXNzYWdlIH19PC9wPlxuICAgICAgPGJ1dHRvbiBtYXQtcmFpc2VkLWJ1dHRvbiBjb2xvcj1cInByaW1hcnlcIiAoY2xpY2spPVwicHJvY2VlZFRvTmV4dFN0ZXAoKVwiIFtkaXNhYmxlZF09XCJpc0xvYWRpbmdTdWJqZWN0IHwgYXN5bmNcIj5cbiAgICAgICAge3sgbmV4dEJ1dHRvbkxhYmVsIH19XG4gICAgICA8L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPCEtLSBCYW5rIFNlbGVjdGlvbiBTdGF0ZSAtLT5cbiAgPGRpdiAqbmdJZj1cImlzQmFua1NlbGVjdGlvblN0YXRlXCIgY2xhc3M9XCJzdGF0ZS1iYW5rLXNlbGVjdGlvblwiPlxuICAgIDxoMyBjbGFzcz1cInNlY3Rpb24tdGl0bGVcIj57eyBzZWxlY3RCYW5rVGl0bGUgfX08L2gzPlxuXG4gICAgPCEtLSBCYW5rIEdyaWQgLS0+XG4gICAgPGRpdiBjbGFzcz1cImJhbmtzLWdyaWRcIiAqbmdJZj1cImJhbmtzLmxlbmd0aCA+IDBcIj5cbiAgICAgIDxkaXZcbiAgICAgICAgKm5nRm9yPVwibGV0IGJhbmsgb2YgYmFua3NcIlxuICAgICAgICBjbGFzcz1cImJhbmstY2FyZFwiXG4gICAgICAgIFtjbGFzcy5zZWxlY3RlZF09XCJzZWxlY3RlZEJhbmsgPT09IGJhbmsuYmFua0NvZGVcIlxuICAgICAgICAoY2xpY2spPVwic2VsZWN0QmFuayhiYW5rKVwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiYmFuay1sb2dvXCIgKm5nSWY9XCJiYW5rLmxvZ29VcmxcIj5cbiAgICAgICAgICA8aW1nIFtzcmNdPVwiYmFuay5sb2dvVXJsXCIgW2FsdF09XCJiYW5rLmRpc3BsYXlOYW1lXCI+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8ZGl2IGNsYXNzPVwiYmFuay1pbmZvXCI+XG4gICAgICAgICAgPGg0Pnt7IGJhbmsuZGlzcGxheU5hbWUgfX08L2g0PlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwiYmFuay1jb2RlXCI+e3sgYmFuay5iYW5rQ29kZSB9fTwvc3Bhbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxtYXQtaWNvbiBjbGFzcz1cImNoZWNrLWljb25cIiAqbmdJZj1cInNlbGVjdGVkQmFuayA9PT0gYmFuay5iYW5rQ29kZVwiPmNoZWNrX2NpcmNsZTwvbWF0LWljb24+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDxidXR0b25cbiAgICAgIG1hdC1yYWlzZWQtYnV0dG9uXG4gICAgICBjb2xvcj1cInByaW1hcnlcIlxuICAgICAgKGNsaWNrKT1cInByb2NlZWRUb05leHRTdGVwKClcIlxuICAgICAgW2Rpc2FibGVkXT1cIiFzZWxlY3RlZEJhbmsgfHwgKGlzTG9hZGluZ1N1YmplY3QgfCBhc3luYylcIlxuICAgICAgY2xhc3M9XCJtLXQtMTZcIj5cbiAgICAgIHt7IGNvbnRpbnVlQnV0dG9uTGFiZWwgfX1cbiAgICA8L2J1dHRvbj5cbiAgPC9kaXY+XG5cbiAgPCEtLSBDb25zZW50IFN0YXRlIC0tPlxuICA8ZGl2ICpuZ0lmPVwiaXNDb25zZW50U3RhdGVcIiBjbGFzcz1cInN0YXRlLWNvbnNlbnRcIj5cbiAgICA8ZGl2IGNsYXNzPVwiY29uc2VudC1oZWFkZXJcIj5cbiAgICAgIDxoMz57eyBjb25zZW50VGl0bGUgfX08L2gzPlxuICAgICAgPHA+e3sgY29uc2VudERlc2NyaXB0aW9uIH19PC9wPlxuICAgIDwvZGl2PlxuXG4gICAgPCEtLSBTaG93IGlmcmFtZSBvbmx5IGlmIHdlIGhhdmUgYW4gYXV0aG9yaXphdGlvbiBVUkwgLS0+XG4gICAgPGRpdiBjbGFzcz1cImNvbnNlbnQtY29udGFpbmVyXCIgKm5nSWY9XCJhdXRob3JpemF0aW9uVXJsXCI+XG4gICAgICA8aWZyYW1lXG4gICAgICAgIFtzcmNdPVwiYXV0aG9yaXphdGlvblVybFwiXG4gICAgICAgIGNsYXNzPVwiY29uc2VudC1pZnJhbWVcIlxuICAgICAgICBmcmFtZWJvcmRlcj1cIjBcIlxuICAgICAgICBhbGxvd2Z1bGxzY3JlZW5cbiAgICAgICAgc2FuZGJveD1cImFsbG93LWZvcm1zIGFsbG93LXNjcmlwdHMgYWxsb3ctc2FtZS1vcmlnaW4gYWxsb3ctcG9wdXBzIGFsbG93LXBvcHVwcy10by1lc2NhcGUtc2FuZGJveCBhbGxvdy10b3AtbmF2aWdhdGlvblwiPlxuICAgICAgPC9pZnJhbWU+XG4gICAgPC9kaXY+XG5cbiAgICA8IS0tIFNob3cgd2FpdGluZyBtZXNzYWdlIHdoZW4gY29uc2VudCBpcyBpbiBwb3B1cCBvciBpZnJhbWUgZmFpbGVkIC0tPlxuICAgIDxkaXYgY2xhc3M9XCJjb25zZW50LXdhaXRpbmdcIiAqbmdJZj1cIiFhdXRob3JpemF0aW9uVXJsXCI+XG4gICAgICA8bWF0LXNwaW5uZXIgZGlhbWV0ZXI9XCI0MFwiPjwvbWF0LXNwaW5uZXI+XG4gICAgICA8cD57eyB3YWl0aW5nRm9yQXV0aG9yaXphdGlvblRleHQgfX08L3A+XG4gICAgICA8cCBjbGFzcz1cImNvbnNlbnQtcG9wdXAtaGludFwiPlxuICAgICAgICA8bWF0LWljb24+b3Blbl9pbl9uZXc8L21hdC1pY29uPlxuICAgICAgICA8c3Bhbj57eyBwb3B1cFdpbmRvd01lc3NhZ2UgfX08L3NwYW4+XG4gICAgICA8L3A+XG4gICAgICA8cCBjbGFzcz1cImNvbnNlbnQtcG9wdXAtbm90ZVwiPlxuICAgICAgICB7eyBwb3B1cEJsb2NrZXJNZXNzYWdlIH19XG4gICAgICA8L3A+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuXG4gIDwhLS0gUHJvY2Vzc2luZyBTdGF0ZSAtLT5cbiAgPGRpdiAqbmdJZj1cImlzUHJvY2Vzc2luZ1N0YXRlXCIgY2xhc3M9XCJzdGF0ZS1wcm9jZXNzaW5nXCI+XG4gICAgPGRpdiBjbGFzcz1cImxvYWRpbmctY29udGFpbmVyXCI+XG4gICAgICA8bWF0LXNwaW5uZXIgZGlhbWV0ZXI9XCI1MFwiPjwvbWF0LXNwaW5uZXI+XG4gICAgICA8cCBjbGFzcz1cImxvYWRpbmctbWVzc2FnZVwiPnt7IHdhaXRpbmdNZXNzYWdlIH19PC9wPlxuICAgICAgPGRpdiBjbGFzcz1cInByb2dyZXNzLWRldGFpbHNcIj5cbiAgICAgICAgPHAgKm5nSWY9XCJhY2NvdW50cy5sZW5ndGggPiAwXCI+XG4gICAgICAgICAge3sgYWNjb3VudHNSZXRyaWV2ZWRMYWJlbCB9fToge3sgYWNjb3VudHMubGVuZ3RoIH19XG4gICAgICAgIDwvcD5cbiAgICAgICAgPHAgKm5nSWY9XCJ0cmFuc2FjdGlvbnMubGVuZ3RoID4gMFwiPlxuICAgICAgICAgIHt7IHRyYW5zYWN0aW9uc1JldHJpZXZlZExhYmVsIH19OiB7eyB0cmFuc2FjdGlvbnMubGVuZ3RoIH19XG4gICAgICAgIDwvcD5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cblxuICA8IS0tIEVycm9yIFN0YXRlIC0tPlxuICA8ZGl2ICpuZ0lmPVwiaXNFcnJvclN0YXRlXCIgY2xhc3M9XCJzdGF0ZS1lcnJvclwiPlxuICAgIDxkaXYgY2xhc3M9XCJlcnJvci1jb250YWluZXJcIj5cbiAgICAgIDxtYXQtaWNvbiBjb2xvcj1cIndhcm5cIiBjbGFzcz1cImVycm9yLWljb25cIj5lcnJvcl9vdXRsaW5lPC9tYXQtaWNvbj5cbiAgICAgIDxwIGNsYXNzPVwiZXJyb3ItbWVzc2FnZVwiPnt7IGVycm9yTWVzc2FnZSB9fTwvcD5cbiAgICAgIDxidXR0b24gbWF0LXJhaXNlZC1idXR0b24gY29sb3I9XCJwcmltYXJ5XCIgKGNsaWNrKT1cInJldHJ5KClcIj5cbiAgICAgICAge3sgcmV0cnlCdXR0b25MYWJlbCB9fVxuICAgICAgPC9idXR0b24+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuXG4gIDwhLS0gQ29tcGxldGUgU3RhdGUgLS0+XG4gIDxkaXYgKm5nSWY9XCJpc0NvbXBsZXRlU3RhdGVcIiBjbGFzcz1cInN0YXRlLWNvbXBsZXRlXCI+XG4gICAgPGRpdiBjbGFzcz1cInN1Y2Nlc3MtY29udGFpbmVyXCI+XG4gICAgICA8bWF0LWljb24gY29sb3I9XCJwcmltYXJ5XCIgY2xhc3M9XCJzdWNjZXNzLWljb25cIj5jaGVja19jaXJjbGU8L21hdC1pY29uPlxuICAgICAgPHAgY2xhc3M9XCJzdWNjZXNzLW1lc3NhZ2VcIj57eyBjb21wbGV0aW9uTWVzc2FnZSB9fTwvcD5cblxuICAgICAgPCEtLSBEaXNwbGF5IHN1bW1hcnkgb2YgYWNjb3VudHMgZm91bmQgLS0+XG4gICAgICA8ZGl2IGNsYXNzPVwiYWNjb3VudHMtc3VtbWFyeVwiICpuZ0lmPVwiYWNjb3VudHMubGVuZ3RoID4gMFwiPlxuICAgICAgICA8aDQ+e3sgYWNjb3VudHNTdW1tYXJ5VGV4dCB9fTwvaDQ+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJhY2NvdW50cy1saXN0XCI+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImFjY291bnQtY2FyZFwiICpuZ0Zvcj1cImxldCBhY2NvdW50IG9mIGFjY291bnRzXCI+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwiYWNjb3VudC1oZWFkZXJcIj5cbiAgICAgICAgICAgICAgPG1hdC1pY29uIGNsYXNzPVwiYWNjb3VudC1pY29uXCI+YWNjb3VudF9iYWxhbmNlPC9tYXQtaWNvbj5cbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImFjY291bnQtaW5mb1wiPlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJhY2NvdW50LW5hbWVcIj57eyBhY2NvdW50Lm5hbWUgfHwgJ0FjY291bnQnIH19PC9kaXY+XG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImFjY291bnQtaWJhblwiPnt7IGFjY291bnQuaWJhbiB9fTwvZGl2PlxuICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImFjY291bnQtZGV0YWlsc1wiPlxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiYWNjb3VudC1iYWxhbmNlXCI+XG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJiYWxhbmNlLWFtb3VudFwiPnt7IGFjY291bnQuYmFsYW5jZSB8IGN1cnJlbmN5OmFjY291bnQuY3VycmVuY3k6J3N5bWJvbCc6JzEuMi0yJyB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJhY2NvdW50LWhvbGRlclwiPnt7IGFjY291bnQuYWNjb3VudEhvbGRlck5hbWUgfX08L2Rpdj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwidHJhbnNhY3Rpb24tc3VtbWFyeVwiICpuZ0lmPVwidHJhbnNhY3Rpb25zLmxlbmd0aCA+IDBcIj5cbiAgICAgICAgICA8cD57eyB0cmFuc2FjdGlvbnNTdW1tYXJ5VGV4dCB9fTwvcD5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG48L2Rpdj4iXX0=