ngx-dynamic-entity 1.3.0 → 1.4.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 (91) hide show
  1. package/README.md +14 -0
  2. package/fesm2022/ngx-dynamic-entity.mjs +228 -150
  3. package/fesm2022/ngx-dynamic-entity.mjs.map +1 -1
  4. package/package.json +6 -7
  5. package/types/ngx-dynamic-entity.d.ts +1719 -0
  6. package/esm2022/lib/field-types/array-field.component.mjs +0 -168
  7. package/esm2022/lib/field-types/boolean-field.component.mjs +0 -101
  8. package/esm2022/lib/field-types/checkbox-field.component.mjs +0 -82
  9. package/esm2022/lib/field-types/currency-field.component.mjs +0 -102
  10. package/esm2022/lib/field-types/date-field.component.mjs +0 -91
  11. package/esm2022/lib/field-types/date-time-field.component.mjs +0 -138
  12. package/esm2022/lib/field-types/dropdown-field.component.mjs +0 -176
  13. package/esm2022/lib/field-types/email-field.component.mjs +0 -102
  14. package/esm2022/lib/field-types/entity-ref-field.component.mjs +0 -195
  15. package/esm2022/lib/field-types/field-component.contract.mjs +0 -2
  16. package/esm2022/lib/field-types/file-field.component.mjs +0 -157
  17. package/esm2022/lib/field-types/group-field.component.mjs +0 -89
  18. package/esm2022/lib/field-types/image-field.component.mjs +0 -169
  19. package/esm2022/lib/field-types/month-year-field.component.mjs +0 -152
  20. package/esm2022/lib/field-types/multi-select-field.component.mjs +0 -146
  21. package/esm2022/lib/field-types/number-field.component.mjs +0 -112
  22. package/esm2022/lib/field-types/password-field.component.mjs +0 -110
  23. package/esm2022/lib/field-types/radio-field.component.mjs +0 -153
  24. package/esm2022/lib/field-types/text-field.component.mjs +0 -121
  25. package/esm2022/lib/field-types/textarea-field.component.mjs +0 -85
  26. package/esm2022/lib/field-types/time-field.component.mjs +0 -96
  27. package/esm2022/lib/form/dynamic-field/dynamic-field.component.mjs +0 -152
  28. package/esm2022/lib/form/dynamic-form.component.mjs +0 -899
  29. package/esm2022/lib/form/dynamic-record-form.component.mjs +0 -531
  30. package/esm2022/lib/providers/provide-field-types.mjs +0 -75
  31. package/esm2022/lib/providers/provide-ngx-dynamic-entity.mjs +0 -27
  32. package/esm2022/lib/services/cascade-data.service.mjs +0 -129
  33. package/esm2022/lib/services/config-source.service.mjs +0 -67
  34. package/esm2022/lib/services/entity-ref-cache.mjs +0 -20
  35. package/esm2022/lib/services/entity-ref-registry.service.mjs +0 -31
  36. package/esm2022/lib/services/entity-ref-selection.service.mjs +0 -27
  37. package/esm2022/lib/services/entity-reference.service.mjs +0 -168
  38. package/esm2022/lib/services/field-registry.service.mjs +0 -55
  39. package/esm2022/lib/services/file-upload.service.mjs +0 -89
  40. package/esm2022/lib/services/hook-registry.service.mjs +0 -26
  41. package/esm2022/lib/services/lookup-registry.service.mjs +0 -246
  42. package/esm2022/lib/services/rbac.service.mjs +0 -46
  43. package/esm2022/lib/services/rules-evaluation.service.mjs +0 -27
  44. package/esm2022/lib/services/validation-messages.service.mjs +0 -56
  45. package/esm2022/lib/services/validator-registry.service.mjs +0 -99
  46. package/esm2022/lib/tokens/injection-tokens.mjs +0 -126
  47. package/esm2022/ngx-dynamic-entity.mjs +0 -5
  48. package/esm2022/public-api.mjs +0 -58
  49. package/index.d.ts +0 -5
  50. package/lib/field-types/array-field.component.d.ts +0 -28
  51. package/lib/field-types/boolean-field.component.d.ts +0 -18
  52. package/lib/field-types/checkbox-field.component.d.ts +0 -13
  53. package/lib/field-types/currency-field.component.d.ts +0 -17
  54. package/lib/field-types/date-field.component.d.ts +0 -15
  55. package/lib/field-types/date-time-field.component.d.ts +0 -30
  56. package/lib/field-types/dropdown-field.component.d.ts +0 -34
  57. package/lib/field-types/email-field.component.d.ts +0 -15
  58. package/lib/field-types/entity-ref-field.component.d.ts +0 -49
  59. package/lib/field-types/field-component.contract.d.ts +0 -49
  60. package/lib/field-types/file-field.component.d.ts +0 -25
  61. package/lib/field-types/group-field.component.d.ts +0 -17
  62. package/lib/field-types/image-field.component.d.ts +0 -30
  63. package/lib/field-types/month-year-field.component.d.ts +0 -28
  64. package/lib/field-types/multi-select-field.component.d.ts +0 -27
  65. package/lib/field-types/number-field.component.d.ts +0 -16
  66. package/lib/field-types/password-field.component.d.ts +0 -17
  67. package/lib/field-types/radio-field.component.d.ts +0 -33
  68. package/lib/field-types/text-field.component.d.ts +0 -23
  69. package/lib/field-types/textarea-field.component.d.ts +0 -14
  70. package/lib/field-types/time-field.component.d.ts +0 -27
  71. package/lib/form/dynamic-field/dynamic-field.component.d.ts +0 -78
  72. package/lib/form/dynamic-form.component.d.ts +0 -293
  73. package/lib/form/dynamic-record-form.component.d.ts +0 -199
  74. package/lib/providers/provide-field-types.d.ts +0 -24
  75. package/lib/providers/provide-ngx-dynamic-entity.d.ts +0 -56
  76. package/lib/services/cascade-data.service.d.ts +0 -45
  77. package/lib/services/config-source.service.d.ts +0 -14
  78. package/lib/services/entity-ref-cache.d.ts +0 -27
  79. package/lib/services/entity-ref-registry.service.d.ts +0 -21
  80. package/lib/services/entity-ref-selection.service.d.ts +0 -24
  81. package/lib/services/entity-reference.service.d.ts +0 -49
  82. package/lib/services/field-registry.service.d.ts +0 -32
  83. package/lib/services/file-upload.service.d.ts +0 -27
  84. package/lib/services/hook-registry.service.d.ts +0 -12
  85. package/lib/services/lookup-registry.service.d.ts +0 -94
  86. package/lib/services/rbac.service.d.ts +0 -27
  87. package/lib/services/rules-evaluation.service.d.ts +0 -17
  88. package/lib/services/validation-messages.service.d.ts +0 -43
  89. package/lib/services/validator-registry.service.d.ts +0 -32
  90. package/lib/tokens/injection-tokens.d.ts +0 -134
  91. package/public-api.d.ts +0 -42
@@ -1,899 +0,0 @@
1
- import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, Output, EventEmitter, signal, computed, inject, isDevMode, ViewChild, ViewChildren, HostListener, } from '@angular/core';
2
- import { NgComponentOutlet } from '@angular/common';
3
- import { FormArray, FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
4
- import { debounceTime } from 'rxjs/operators';
5
- import { applyAutoPatch, applyPatchOnTrue, migrateRecord, evaluateFieldVisibility, findTab, getTabData, getTabPath, getValueByPath, normalizeArrayStructures, normalizeConfigOptions, resolveLabel, setTabData, setValueByPath, } from '@dynamic-entity/core';
6
- import { DynamicFieldComponent } from './dynamic-field/dynamic-field.component';
7
- import { COMMON_MODULES_REGISTRY, RECORD_MIGRATIONS } from '../tokens/injection-tokens';
8
- import { ValidatorRegistryService } from '../services/validator-registry.service';
9
- import { HookRegistryService } from '../services/hook-registry.service';
10
- import { RbacService } from '../services/rbac.service';
11
- import { RulesEvaluationService } from '../services/rules-evaluation.service';
12
- import { EntityRefSelectionService } from '../services/entity-ref-selection.service';
13
- import * as i0 from "@angular/core";
14
- import * as i1 from "@angular/forms";
15
- /**
16
- * DynamicFormComponent — the main form component.
17
- * Renders a reactive form from EntityFormConfig with tab support, responsive 12-col grid,
18
- * rules evaluation, conditional visibility, autoPatch/patchOnTrue, criticalField locking,
19
- * keyboard shortcuts (Ctrl+S, Esc), and RBAC-gated submission.
20
- */
21
- export class DynamicFormComponent {
22
- // ─── Inputs ───────────────────────────────────────────────────────────────
23
- /**
24
- * The schema to render.
25
- *
26
- * An accessor pair rather than a plain field: `ngOnChanges` normalises the option shape,
27
- * and that result is kept here instead of being written back over the input. Reassigning
28
- * an `@Input` works, but it mutates what the parent handed us — and a parent using OnPush
29
- * or signals would never observe the replacement anyway.
30
- *
31
- * Reads of `this.config` get the normalised copy once it exists, so every internal caller
32
- * is unaffected.
33
- */
34
- set config(value) {
35
- this.rawConfig = value;
36
- this.normalizedConfig = undefined; // re-normalised in ngOnChanges
37
- }
38
- get config() {
39
- return this.normalizedConfig ?? this.rawConfig;
40
- }
41
- rawConfig;
42
- normalizedConfig;
43
- rules;
44
- initialData;
45
- /** Session-original values for `VALUE_CHANGED` rules. Captured from the first build when omitted. */
46
- originalData;
47
- userRoles = [];
48
- language = 'en';
49
- readonly = false;
50
- /** Field ids forced read-only while the rest of the form stays editable. */
51
- readOnlyFields = [];
52
- /**
53
- * Render rule `info` banners inline. The record editor sets this false and renders its
54
- * own dismissible versions, so the two do not double up.
55
- */
56
- showInfoBanners = true;
57
- loading = false;
58
- error = null;
59
- /**
60
- * Milliseconds to debounce `formChange`. The reference debounces at 300ms so a consumer
61
- * re-rendering on every value change is not driven per keystroke. Zero emits
62
- * synchronously, which the record editor relies on for its own bookkeeping.
63
- */
64
- changeDebounceMs = 0;
65
- /**
66
- * Preview mode: seed one empty row per `array` field so the structure is visible, then
67
- * disable the form. Used by the builder's live preview.
68
- */
69
- preview = false;
70
- // ─── Outputs ──────────────────────────────────────────────────────────────
71
- formSubmit = new EventEmitter();
72
- formChange = new EventEmitter();
73
- formReset = new EventEmitter();
74
- /** The visible tab changed. Lets a host track it without reaching into this component. */
75
- activeTabChange = new EventEmitter();
76
- /**
77
- * Emitted when a `beforeSave` hook aborted the save — by returning false or throwing.
78
- * `formSubmit` does not fire in that case.
79
- */
80
- saveRejected = new EventEmitter();
81
- // ─── Services ─────────────────────────────────────────────────────────────
82
- cdr = inject(ChangeDetectorRef);
83
- fb = inject(FormBuilder);
84
- validatorRegistry = inject(ValidatorRegistryService);
85
- hookRegistry = inject(HookRegistryService);
86
- rbacService = inject(RbacService);
87
- rulesEvaluation = inject(RulesEvaluationService);
88
- entityRefSelection = inject(EntityRefSelectionService);
89
- commonModulesRegistry = inject(COMMON_MODULES_REGISTRY, { optional: true });
90
- migrations = inject(RECORD_MIGRATIONS, { optional: true }) ?? [];
91
- Object = Object;
92
- // ─── Signals (local reactive state) ───────────────────────────────────────
93
- activeTab = signal('');
94
- activeSubTab = signal('');
95
- isSaving = signal(false);
96
- formValues = signal({});
97
- /** Baseline captured at first build, used when `originalData` is not supplied. */
98
- sessionBaseline = signal({});
99
- /** Critical fields the user has explicitly unlocked for editing. */
100
- unlockedFields = signal(new Set());
101
- /** The active tab's field panel, focused after a tab switch. */
102
- formPanel;
103
- /** The hosted field components, so an external control-state change can reach them. */
104
- fieldHosts;
105
- // ─── Form ─────────────────────────────────────────────────────────────────
106
- form;
107
- valueSub;
108
- statusSub;
109
- selectionSub;
110
- /** Previous values, for detecting `patchOnTrue` false→true transitions. */
111
- previousValues = {};
112
- // ─── Keyboard Shortcuts ───────────────────────────────────────────────────
113
- handleKeyboardEvent(event) {
114
- if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {
115
- event.preventDefault();
116
- if (!this.readonly && this.canSubmit && this.form?.valid) {
117
- this.submit();
118
- }
119
- }
120
- }
121
- // ─── Computed ─────────────────────────────────────────────────────────────
122
- ruleResult = computed(() => this.rulesEvaluation.evaluate(this.rules, this.formValues(), this.baseline()));
123
- /**
124
- * Critical fields whose value differs from the session baseline.
125
- * These drive the deferred `VALUE_CHANGED` banner — a critical edit is announced once,
126
- * against the value the record had when the session started, not per keystroke.
127
- */
128
- changedCriticalFields = computed(() => {
129
- const baseline = this.baseline();
130
- if (!baseline || Object.keys(baseline).length === 0)
131
- return [];
132
- const values = this.formValues();
133
- return this.allFields().filter(f => f.criticalField && !this.sameValue(values[f.id], baseline[f.id]));
134
- });
135
- get tabs() {
136
- return this.config?.tabs || [];
137
- }
138
- get visibleTabs() {
139
- const hidden = this.ruleResult().hiddenTabs;
140
- return this.tabs.filter(tab => tab.visibility !== false && !hidden.includes(tab.id));
141
- }
142
- get activeTabConfig() {
143
- const tabId = this.activeTab();
144
- if (!tabId)
145
- return this.visibleTabs[0] ?? null;
146
- return findTab(this.tabs, tabId);
147
- }
148
- get visibleSubTabs() {
149
- const active = this.activeTabConfig;
150
- if (!active?.children || active.children.length === 0)
151
- return [];
152
- const hidden = this.ruleResult().hiddenTabs;
153
- return active.children.filter(tab => tab.visibility !== false && !hidden.includes(tab.id));
154
- }
155
- get activeSubTabConfig() {
156
- const subTabs = this.visibleSubTabs;
157
- if (!subTabs.length)
158
- return null;
159
- const subId = this.activeSubTab();
160
- return subTabs.find(s => s.id === subId) ?? subTabs[0];
161
- }
162
- get activeTabModuleComponent() {
163
- const active = this.activeSubTabConfig ?? this.activeTabConfig;
164
- if (!active?.moduleName || !this.commonModulesRegistry)
165
- return null;
166
- const entry = this.commonModulesRegistry.find(m => m.id === active.moduleName || m.component === active.moduleName);
167
- return entry ? entry.component : null;
168
- }
169
- get fieldsForActiveTab() {
170
- const active = this.activeSubTabConfig ?? this.activeTabConfig;
171
- if (active?.moduleName)
172
- return [];
173
- const rawFields = active ? (active.fields || []) : ((this.config?.tabs || []).flatMap(t => t.fields || []));
174
- const currentValues = this.formValues();
175
- const hiddenFields = this.ruleResult().hiddenFields;
176
- return rawFields.filter(field => evaluateFieldVisibility(field, currentValues) && !hiddenFields.includes(field.id));
177
- }
178
- /**
179
- * Entity-level permissions for the current roles.
180
- *
181
- * Cached rather than recomputed: this is read from the template several times per change
182
- * detection pass, and a fresh object each time is both wasted allocation and a new
183
- * identity for anything comparing by reference. Invalidated in `ngOnChanges` when
184
- * `config` or `userRoles` change, which are its only inputs.
185
- */
186
- get permissions() {
187
- return (this.permissionsCache ??= this.rbacService.getPermissions(this.config, this.userRoles));
188
- }
189
- permissionsCache = null;
190
- /**
191
- * Whether the current roles may see this record at all.
192
- *
193
- * `permissions.view` used to be computed and thrown away — a user whose roles failed it
194
- * still received the complete form with every value in the DOM. It is honoured now.
195
- *
196
- * As with masking, this is presentational: it stops the browser rendering data, it does
197
- * not stop the data reaching the browser. Authorize on the server.
198
- */
199
- get canView() {
200
- return this.permissions.canView;
201
- }
202
- /**
203
- * Whether the current roles may delete this record.
204
- *
205
- * The library ships no delete affordance, so this is surfaced for the consumer to gate
206
- * their own — exposed rather than dropped, because a permission the schema declares
207
- * should be answerable.
208
- */
209
- get canDelete() {
210
- return this.permissions.canDelete;
211
- }
212
- get canSubmit() {
213
- return this.canView && this.permissions.canEdit && !this.readonly;
214
- }
215
- /**
216
- * Errors raised by `validation` rules, keyed by the target they point at.
217
- *
218
- * A rule can declare the record invalid independently of Angular's validators, so form
219
- * validity alone is not the whole picture. `DynamicRecordFormComponent.saveSection()`
220
- * has always honoured these; `submit()` did not, which meant the same rule blocked one
221
- * save path and merely painted a banner on the other — and anything wiring
222
- * `(formSubmit)` to persistence wrote records the rules engine had already rejected.
223
- */
224
- get ruleValidationErrors() {
225
- return this.ruleResult().validationErrors;
226
- }
227
- /** True when a `validation` rule is currently blocking submission. */
228
- get hasRuleErrors() {
229
- return Object.keys(this.ruleValidationErrors).length > 0;
230
- }
231
- /**
232
- * True while an async validator is still running.
233
- *
234
- * Angular reports a control with an outstanding async check as `pending`, and `invalid` is
235
- * false until it settles — so without this a form could be submitted in the gap before a
236
- * uniqueness check came back.
237
- */
238
- get isValidating() {
239
- return this.form.pending;
240
- }
241
- /** Single source of truth for both the submit guard and the button's disabled state. */
242
- get submitBlocked() {
243
- return this.form.invalid || this.form.pending || this.hasRuleErrors;
244
- }
245
- ngOnInit() {
246
- this.selectionSub = this.entityRefSelection.selection$.subscribe(({ fieldId, option }) => {
247
- this.runAutoPatch(fieldId, option?.record);
248
- });
249
- }
250
- ngOnChanges(changes) {
251
- if (changes['config'] || changes['userRoles']) {
252
- this.permissionsCache = null;
253
- }
254
- if (changes['config'] && this.rawConfig) {
255
- // Configs arrive as plain JSON from storage or an API, where TypeScript cannot enforce
256
- // the option shape. Normalise here, at the library boundary, so everything downstream
257
- // can rely on an option being a LocalizedText. Returns the same object when it already
258
- // is, so a well-formed config costs nothing.
259
- // Kept in a field of our own rather than written back to the @Input. Reassigning an
260
- // input works, but it surprises a parent that holds the same object — and a parent
261
- // using OnPush or signals would not see the replacement anyway.
262
- this.normalizedConfig = normalizeConfigOptions(this.config);
263
- }
264
- if (changes['config'] || changes['initialData']) {
265
- this.buildForm();
266
- if (this.visibleTabs.length > 0 && !this.activeTab()) {
267
- this.setActiveTab(this.visibleTabs[0].id);
268
- }
269
- }
270
- }
271
- ngOnDestroy() {
272
- this.valueSub?.unsubscribe();
273
- this.statusSub?.unsubscribe();
274
- this.selectionSub?.unsubscribe();
275
- }
276
- setActiveTab(tabId) {
277
- const changed = this.activeTab() !== tabId;
278
- if (changed)
279
- this.activeTabChange.emit(tabId);
280
- this.activeTab.set(tabId);
281
- if (changed)
282
- this.focusActivePanel();
283
- const parent = findTab(this.tabs, tabId);
284
- if (parent?.children?.length) {
285
- this.activeSubTab.set(parent.children[0].id);
286
- }
287
- else {
288
- this.activeSubTab.set('');
289
- }
290
- }
291
- setActiveSubTab(subTabId) {
292
- const changed = this.activeSubTab() !== subTabId;
293
- this.activeSubTab.set(subTabId);
294
- if (changed)
295
- this.focusActivePanel();
296
- }
297
- getFieldSpan(field) {
298
- const span = field.colSpan ?? 12;
299
- return `span ${Math.min(12, Math.max(1, span))}`;
300
- }
301
- // ─── criticalField locking ────────────────────────────────────────────────
302
- /** A critical field stays read-only until the user deliberately unlocks it. */
303
- isFieldLocked(field) {
304
- return !!field.criticalField && !this.unlockedFields().has(field.id);
305
- }
306
- /**
307
- * Whether this field renders read-only, from any of the four reasons: the whole form,
308
- * the field's own flag, a caller-supplied `readOnlyFields` entry, or a criticalField lock.
309
- */
310
- isFieldReadonly(field) {
311
- return (this.readonly ||
312
- !!field.readonly ||
313
- this.readOnlyFields.includes(field.id) ||
314
- this.isFieldLocked(field));
315
- }
316
- toggleFieldLock(field) {
317
- if (!field.criticalField || this.readonly)
318
- return;
319
- const next = new Set(this.unlockedFields());
320
- if (next.has(field.id))
321
- next.delete(field.id);
322
- else
323
- next.add(field.id);
324
- this.unlockedFields.set(next);
325
- }
326
- resolveFieldLabel(field) {
327
- return resolveLabel(field.label, this.language);
328
- }
329
- // ─── Form construction ────────────────────────────────────────────────────
330
- buildForm() {
331
- if (!this.config)
332
- return;
333
- const group = {};
334
- const buildTabControls = (tabs, parentGroup) => {
335
- for (const tab of tabs) {
336
- if (tab.flatData) {
337
- for (const field of tab.fields || []) {
338
- this.buildFieldControl(field, parentGroup);
339
- }
340
- if (tab.children)
341
- buildTabControls(tab.children, parentGroup);
342
- }
343
- else {
344
- const tabGroup = {};
345
- for (const field of tab.fields || []) {
346
- this.buildFieldControl(field, tabGroup);
347
- }
348
- if (tab.children)
349
- buildTabControls(tab.children, tabGroup);
350
- parentGroup[tab.id] = this.fb.group(tabGroup);
351
- }
352
- }
353
- };
354
- buildTabControls(this.config.tabs || [], group);
355
- this.form = this.fb.group(group);
356
- if (this.initialData) {
357
- this.patchForm(this.upgradeRecord(this.initialData));
358
- }
359
- const initialFlattened = this.flattenFormValues();
360
- this.formValues.set(initialFlattened);
361
- this.previousValues = { ...initialFlattened };
362
- this.sessionBaseline.set({ ...initialFlattened });
363
- this.unlockedFields.set(new Set());
364
- this.syncHiddenFieldState(initialFlattened);
365
- if (this.preview) {
366
- // Show what an array field looks like rather than an empty slot, then freeze.
367
- for (const field of this.allFields()) {
368
- if (field.type !== 'array')
369
- continue;
370
- const array = this.getArrayControl(field.id);
371
- if (array && array.length === 0)
372
- array.push(this.buildArrayRow(field, undefined));
373
- }
374
- this.form.disable({ emitEvent: false });
375
- }
376
- // Rebuilding must not stack subscriptions on successive config/data changes.
377
- this.valueSub?.unsubscribe();
378
- const changes$ = this.changeDebounceMs > 0
379
- ? this.form.valueChanges.pipe(debounceTime(this.changeDebounceMs))
380
- : this.form.valueChanges;
381
- // Validity is not value: an async validator settling flips `pending` and then `invalid`
382
- // with no value change and no template event, so under OnPush the Save button would
383
- // never re-enable. submitBlocked reads form.pending and form.invalid, so this is what
384
- // keeps it honest.
385
- this.statusSub?.unsubscribe();
386
- this.statusSub = this.form.statusChanges.subscribe(() => this.cdr.markForCheck());
387
- this.valueSub = changes$.subscribe(() => {
388
- const flattened = this.flattenFormValues();
389
- this.formValues.set(flattened);
390
- this.runPatchOnTrue(flattened);
391
- this.syncHiddenFieldState(flattened);
392
- this.previousValues = { ...flattened };
393
- this.formChange.emit(this.extractRecord());
394
- });
395
- }
396
- buildFieldControl(field, group) {
397
- if (field.type === 'group') {
398
- const subGroup = {};
399
- for (const child of field.children || []) {
400
- this.buildFieldControl(child, subGroup);
401
- }
402
- group[field.id] = this.fb.group(subGroup);
403
- }
404
- else if (field.type === 'array') {
405
- group[field.id] = this.fb.array([]);
406
- }
407
- else {
408
- const validators = this.validatorRegistry.resolveFromConfig(field.validators);
409
- const asyncValidators = this.validatorRegistry.resolveAsyncFromConfig(field.validators);
410
- group[field.id] = this.fb.control({ value: field.defaultValue ?? null, disabled: field.disabled ?? false }, validators, asyncValidators);
411
- }
412
- }
413
- /** One row of an `array` field: a FormGroup when the field declares columns, else a bare control. */
414
- /**
415
- * The `FormArray` behind an `array` field, for callers that manage rows themselves —
416
- * the record editor's inline row drawer.
417
- */
418
- getArrayControl(fieldId, tabId) {
419
- const ctrl = this.getControl(fieldId, tabId);
420
- return ctrl instanceof FormArray ? ctrl : null;
421
- }
422
- /**
423
- * Build one detached row for an `array` field, with the field's own validators applied.
424
- * Exposed so a row can be edited outside the form and pushed on save.
425
- */
426
- createArrayRow(field, value) {
427
- return this.buildArrayRow(field, value);
428
- }
429
- buildArrayRow(field, item) {
430
- if (!field?.children?.length)
431
- return this.fb.control(item);
432
- const rowGroup = {};
433
- for (const child of field.children) {
434
- this.buildFieldControl(child, rowGroup);
435
- }
436
- const group = this.fb.group(rowGroup);
437
- if (item && typeof item === 'object')
438
- group.patchValue(item);
439
- return group;
440
- }
441
- /**
442
- * Mark every control touched, then re-check the field components.
443
- *
444
- * They are OnPush and `touched` is flipped from outside their own template, so the second
445
- * half is what makes the error messages actually appear.
446
- */
447
- markAllTouched() {
448
- this.form.markAllAsTouched();
449
- this.fieldHosts?.forEach(host => host.refresh());
450
- }
451
- /**
452
- * Bring a record up to the config's `version` before it reaches the form.
453
- *
454
- * Done here because this is the one place a record enters the renderer, so a consumer
455
- * cannot forget it. Migration is a no-op unless the consumer registered steps *and* the
456
- * record is behind — an unstamped record is deliberately left alone (see `migrateRecord`).
457
- *
458
- * A missing or broken step throws from core. That is not swallowed: rendering a
459
- * half-understood record would write it back in a shape matching neither schema.
460
- */
461
- upgradeRecord(data) {
462
- if (!this.migrations.length || !this.config)
463
- return data;
464
- const result = migrateRecord(data, this.config, this.migrations);
465
- if (result.applied.length && isDevMode()) {
466
- console.info(`[ngx-dynamic-entity] Migrated a "${this.config.entity}" record from config version ` +
467
- `${result.from} to ${result.to} (steps: ${result.applied.join(', ')}).`);
468
- }
469
- return result.record;
470
- }
471
- patchForm(data) {
472
- if (!data || !this.form)
473
- return;
474
- const fieldsById = new Map(this.allFields().map(f => [f.id, f]));
475
- const patchedFieldIds = new Set();
476
- const walkTabs = (tabs) => {
477
- for (const tab of tabs) {
478
- const tabData = getTabData(tab.id, data, this.config);
479
- for (const field of tab.fields || []) {
480
- let val = tabData && typeof tabData === 'object' ? tabData[field.id] : undefined;
481
- if (field.refererField) {
482
- const refVal = getValueByPath(data, field.refererField);
483
- if (refVal !== undefined)
484
- val = refVal;
485
- }
486
- if (val === undefined)
487
- continue;
488
- const ctrl = this.getControl(field.id, tab.id);
489
- if (!ctrl)
490
- continue;
491
- if (ctrl instanceof FormArray && Array.isArray(val)) {
492
- ctrl.clear();
493
- for (const item of val) {
494
- ctrl.push(this.buildArrayRow(fieldsById.get(field.id), item));
495
- }
496
- }
497
- else {
498
- ctrl.patchValue(val, { emitEvent: false });
499
- }
500
- patchedFieldIds.add(field.id);
501
- }
502
- if (tab.children)
503
- walkTabs(tab.children);
504
- }
505
- };
506
- walkTabs(this.config.tabs || []);
507
- this.warnUnconsumedInitialData(data, fieldsById, patchedFieldIds);
508
- }
509
- /**
510
- * A record is nested by tab id (`{ tabId: { fieldId: value } }`) unless the tab sets
511
- * `flatData: true`. Handing a flat record to a nested tab finds nothing, so the fields
512
- * stay empty — with no error and no clue. That silent miss is the most expensive way to
513
- * lose an hour with this library, so name it in dev builds.
514
- *
515
- * Only top-level keys that match a known field id are reported: anything else is assumed
516
- * to be the consumer's own record metadata (ids, timestamps, `_configVersion`) and is not
517
- * our business.
518
- */
519
- warnUnconsumedInitialData(data, fieldsById, patchedFieldIds) {
520
- if (!isDevMode() || !this.config)
521
- return;
522
- const tabIds = new Set();
523
- const collectTabIds = (tabs) => {
524
- for (const tab of tabs) {
525
- tabIds.add(tab.id);
526
- if (tab.children)
527
- collectTabIds(tab.children);
528
- }
529
- };
530
- collectTabIds(this.config.tabs || []);
531
- const unconsumed = Object.keys(data).filter(key => !tabIds.has(key) && // a tab id at the root is the nested container, not a stray field
532
- data[key] !== undefined &&
533
- fieldsById.has(key) &&
534
- !patchedFieldIds.has(key) &&
535
- !this.warnedUnconsumedKeys.has(key));
536
- if (!unconsumed.length)
537
- return;
538
- for (const key of unconsumed)
539
- this.warnedUnconsumedKeys.add(key);
540
- const shown = unconsumed.slice(0, 5).join(', ');
541
- const more = unconsumed.length > 5 ? ` (+${unconsumed.length - 5} more)` : '';
542
- const plural = unconsumed.length === 1;
543
- console.warn(`[ngx-dynamic-entity] initialData has top-level ${plural ? 'key' : 'keys'} matching ` +
544
- `${plural ? 'a field that was' : 'fields that were'} not populated: ${shown}${more}. ` +
545
- `A record is nested by tab id ({ tabId: { fieldId: value } }) unless the tab sets ` +
546
- `flatData: true. Either nest the value under its tab id, or set flatData: true on ` +
547
- `the tab holding ${plural ? 'that field' : 'those fields'}.`);
548
- }
549
- /** Warn once per key per form instance — patchForm re-runs on every initialData change. */
550
- warnedUnconsumedKeys = new Set();
551
- /** Assemble full nested record from per-tab FormGroups, respecting flatData, refererField & arrays. */
552
- extractRecord() {
553
- if (!this.form || !this.config)
554
- return {};
555
- let record = {};
556
- const walkTabs = (tabs) => {
557
- for (const tab of tabs) {
558
- const fieldValBag = {};
559
- for (const field of tab.fields || []) {
560
- const ctrl = this.getControl(field.id, tab.id);
561
- if (ctrl) {
562
- fieldValBag[field.id] = ctrl.value;
563
- }
564
- }
565
- setTabData(record, tab.id, fieldValBag, this.config);
566
- for (const field of tab.fields || []) {
567
- if (field.refererField) {
568
- const ctrl = this.getControl(field.id, tab.id);
569
- if (ctrl) {
570
- setValueByPath(record, field.refererField, ctrl.value);
571
- }
572
- }
573
- }
574
- if (tab.children)
575
- walkTabs(tab.children);
576
- }
577
- };
578
- walkTabs(this.config.tabs || []);
579
- record = normalizeArrayStructures(record, this.config);
580
- return record;
581
- }
582
- flattenFormValues() {
583
- const out = {};
584
- for (const field of this.allFields()) {
585
- const ctrl = this.getControl(field.id);
586
- if (ctrl)
587
- out[field.id] = ctrl.value;
588
- }
589
- return out;
590
- }
591
- getTabGroup(tabId) {
592
- const path = getTabPath(this.config?.tabs, tabId);
593
- if (!path || path.length === 0)
594
- return this.form;
595
- let curr = this.form;
596
- for (const p of path) {
597
- if (!curr || !(curr instanceof FormGroup))
598
- return null;
599
- curr = curr.get(p);
600
- }
601
- return curr instanceof FormGroup ? curr : null;
602
- }
603
- // ─── autoPatch / patchOnTrue ──────────────────────────────────────────────
604
- /**
605
- * `autoPatch` — copy mapped fields out of the record the user just selected in an
606
- * entity-ref field into the configured target tab's controls.
607
- */
608
- runAutoPatch(fieldId, record) {
609
- if (!record)
610
- return;
611
- const field = this.allFields().find(f => f.id === fieldId);
612
- const autoPatch = field?.autoPatch;
613
- if (!autoPatch)
614
- return;
615
- const patch = applyAutoPatch(autoPatch, record);
616
- for (const [targetId, value] of Object.entries(patch)) {
617
- const control = this.resolveTargetControl(autoPatch, targetId);
618
- control?.patchValue(value);
619
- }
620
- // Readonly targets interpolate `control.value` under OnPush. `detectChanges` on the
621
- // hosted component flushes that write immediately — `markForCheck` waits for a later
622
- // tick that a Playwright selection never schedules.
623
- this.fieldHosts?.forEach(host => host.refresh());
624
- }
625
- /**
626
- * `patchOnTrue` — when a boolean/checkbox field transitions to `true`,
627
- * copy `from` → `to` within the current record.
628
- */
629
- runPatchOnTrue(values) {
630
- for (const field of this.allFields()) {
631
- const mappings = field.patchOnTrue;
632
- if (!mappings?.length)
633
- continue;
634
- const wasTrue = this.previousValues[field.id] === true;
635
- const isTrue = values[field.id] === true;
636
- if (!isTrue || wasTrue)
637
- continue;
638
- const patch = applyPatchOnTrue(mappings, values);
639
- for (const [targetId, value] of Object.entries(patch)) {
640
- this.getControl(targetId)?.patchValue(value, { emitEvent: false });
641
- }
642
- }
643
- }
644
- /** Prefer a control declared on the configured target tab; fall back to a top-level control. */
645
- resolveTargetControl(autoPatch, targetId) {
646
- const tab = findTab(this.tabs, autoPatch.targetTab);
647
- const inTargetTab = (tab?.fields ?? []).some(f => f.id === targetId);
648
- if (tab && !inTargetTab)
649
- return null;
650
- return this.getControl(targetId, autoPatch.targetTab);
651
- }
652
- // ─── Helpers ──────────────────────────────────────────────────────────────
653
- /**
654
- * The fields the render filter operates on — a tab's own fields, at every tab depth.
655
- * Deliberately not `allFields()`: `group` and `array` children are shown or hidden with
656
- * their parent, and toggling a child's state independently would fight the parent's.
657
- */
658
- tabFields() {
659
- const out = [];
660
- const walk = (tabs) => {
661
- for (const tab of tabs ?? []) {
662
- out.push(...(tab.fields ?? []));
663
- walk(tab.children);
664
- }
665
- };
666
- walk(this.config?.tabs);
667
- return out;
668
- }
669
- /**
670
- * Keep a hidden field's control out of form validity.
671
- *
672
- * A field hidden by a rule or a `showWhen` condition is filtered out of the render, but
673
- * its validators stay attached — so a hidden *required* field holds `form.invalid` true
674
- * forever, permanently disabling Save with nothing on screen to explain why.
675
- *
676
- * Disabling is the fix rather than stripping validators: Angular excludes disabled
677
- * controls from validity, and the field's own validators survive intact for when it
678
- * comes back. Values are untouched — `flattenFormValues` and `extractRecord` both read
679
- * `control.value`, which a disabled control still carries, so rule evaluation and the
680
- * emitted record see exactly what they saw before. That also keeps this from feeding
681
- * back on itself: hiding a field cannot change the values the rules are evaluated over.
682
- *
683
- * The visibility predicate is the same one `fieldsForActiveTab` filters with, so what is
684
- * rendered and what counts toward validity cannot drift apart.
685
- */
686
- syncHiddenFieldState(values) {
687
- if (this.preview)
688
- return; // preview freezes the whole form on purpose
689
- const hiddenByRule = new Set(this.ruleResult().hiddenFields);
690
- for (const field of this.tabFields()) {
691
- const ctrl = this.getControl(field.id);
692
- if (!ctrl)
693
- continue;
694
- const hidden = hiddenByRule.has(field.id) || !evaluateFieldVisibility(field, values);
695
- if (hidden) {
696
- if (ctrl.enabled)
697
- ctrl.disable({ emitEvent: false });
698
- }
699
- else if (ctrl.disabled && !field.disabled) {
700
- ctrl.enable({ emitEvent: false });
701
- }
702
- }
703
- }
704
- /** Every field in the config, including `group`/`array` children. */
705
- allFields() {
706
- const out = [];
707
- const walkFields = (fields) => {
708
- for (const f of fields ?? []) {
709
- out.push(f);
710
- if (f.children?.length)
711
- walkFields(f.children);
712
- }
713
- };
714
- const walkTabs = (tabs) => {
715
- for (const t of tabs ?? []) {
716
- walkFields(t.fields);
717
- walkTabs(t.children);
718
- }
719
- };
720
- walkTabs(this.config?.tabs);
721
- return out;
722
- }
723
- /** An empty `originalData` counts as "not supplied" — fall back to the captured baseline. */
724
- baseline() {
725
- const supplied = this.originalData;
726
- if (supplied && Object.keys(supplied).length > 0)
727
- return supplied;
728
- return this.sessionBaseline();
729
- }
730
- sameValue(a, b) {
731
- if (a === b)
732
- return true;
733
- const aEmpty = a === null || a === undefined || a === '';
734
- const bEmpty = b === null || b === undefined || b === '';
735
- if (aEmpty && bEmpty)
736
- return true;
737
- if (typeof a === 'object' || typeof b === 'object') {
738
- return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
739
- }
740
- return false;
741
- }
742
- getControl(fieldId, currentTabId) {
743
- if (!this.form)
744
- return null;
745
- if (currentTabId) {
746
- const tabGrp = this.getTabGroup(currentTabId);
747
- const ctrl = tabGrp?.get(fieldId);
748
- if (ctrl)
749
- return ctrl;
750
- }
751
- const rootCtrl = this.form.get(fieldId);
752
- if (rootCtrl)
753
- return rootCtrl;
754
- const findInGroup = (group) => {
755
- for (const key of Object.keys(group.controls)) {
756
- const c = group.controls[key];
757
- if (key === fieldId)
758
- return c;
759
- if (c instanceof FormGroup) {
760
- const found = findInGroup(c);
761
- if (found)
762
- return found;
763
- }
764
- }
765
- return null;
766
- };
767
- return findInGroup(this.form);
768
- }
769
- /**
770
- * Move focus into the newly shown panel.
771
- *
772
- * Activating a tab replaces everything below it while focus stays on the tab button, so a
773
- * screen-reader or keyboard user is given no indication that the content changed and must
774
- * tab back through the strip to reach it. Deferred a frame because the panel's contents
775
- * are rendered by the change detection this call is part of.
776
- */
777
- focusActivePanel() {
778
- if (typeof requestAnimationFrame !== 'function')
779
- return;
780
- requestAnimationFrame(() => this.formPanel?.nativeElement?.focus?.());
781
- }
782
- /** Accessible name for the field panel — it is the tabpanel for the active tab. */
783
- get activeTabLabel() {
784
- const tab = this.activeSubTabConfig ?? this.activeTabConfig;
785
- return tab ? this.resolveTabLabel(tab) : '';
786
- }
787
- /**
788
- * Why a referenced field is flagged.
789
- *
790
- * `hasDrift` was written by the builder and read by nothing at runtime, so a field whose
791
- * source definition had changed since it was linked looked entirely normal to the person
792
- * filling the form in.
793
- */
794
- driftHint(field) {
795
- const source = field.referencedEntityKey
796
- ? `"${field.referencedEntityKey}"`
797
- : 'its source entity';
798
- return `This field is linked to ${source} and its definition there has changed since it was linked.`;
799
- }
800
- resolveTabLabel(tab) {
801
- return resolveLabel(tab.label, this.language);
802
- }
803
- async submit() {
804
- if (!this.canSubmit || this.submitBlocked) {
805
- this.markAllTouched();
806
- return;
807
- }
808
- const rawData = this.extractRecord();
809
- let processedData = rawData;
810
- this.isSaving.set(true);
811
- try {
812
- // beforeSave can now abort the save: returning false, or throwing, stops it. Previously
813
- // its return value simply replaced the payload and the submit proceeded regardless, so
814
- // a hook that wanted to veto — a server-side check, a confirmation — had no way to.
815
- const hookKey = `${this.config?.entity}:beforeSave`;
816
- if (this.hookRegistry.has(hookKey)) {
817
- const result = await this.hookRegistry.run(hookKey, processedData);
818
- if (result === false) {
819
- this.saveRejected.emit({ reason: 'beforeSave returned false' });
820
- return;
821
- }
822
- // Anything else is the payload to submit; `undefined` means "unchanged".
823
- if (result !== undefined && result !== true)
824
- processedData = result;
825
- }
826
- this.formSubmit.emit(processedData);
827
- }
828
- catch (err) {
829
- // A hook that throws aborts the save and reports why, rather than the error escaping
830
- // into an unhandled rejection with the form left looking as though it saved.
831
- this.saveRejected.emit({ reason: err instanceof Error ? err.message : String(err), error: err });
832
- }
833
- finally {
834
- this.isSaving.set(false);
835
- }
836
- }
837
- reset() {
838
- this.form.reset();
839
- if (this.initialData)
840
- this.patchForm(this.initialData);
841
- const values = this.flattenFormValues();
842
- this.formValues.set(values);
843
- this.previousValues = { ...values };
844
- this.unlockedFields.set(new Set());
845
- this.formReset.emit();
846
- }
847
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
848
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: DynamicFormComponent, isStandalone: true, selector: "ngx-dynamic-form", inputs: { config: "config", rules: "rules", initialData: "initialData", originalData: "originalData", userRoles: "userRoles", language: "language", readonly: "readonly", readOnlyFields: "readOnlyFields", showInfoBanners: "showInfoBanners", loading: "loading", error: "error", changeDebounceMs: "changeDebounceMs", preview: "preview" }, outputs: { formSubmit: "formSubmit", formChange: "formChange", formReset: "formReset", activeTabChange: "activeTabChange", saveRejected: "saveRejected" }, host: { listeners: { "window:keydown": "handleKeyboardEvent($event)" } }, providers: [EntityRefSelectionService], viewQueries: [{ propertyName: "formPanel", first: true, predicate: ["formPanel"], descendants: true }, { propertyName: "fieldHosts", predicate: DynamicFieldComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (canView) {\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\" class=\"ngx-form\" novalidate>\n\n <!-- Error message banner -->\n @if (error) {\n <div class=\"ngx-form__error-banner\" role=\"alert\" data-testid=\"form-error\">{{ error }}</div>\n }\n\n <!-- Rules Info Banners -->\n @for (infoKey of showInfoBanners ? Object.keys(ruleResult().infoBanners) : []; track infoKey) {\n <div class=\"ngx-form__info-banner\" role=\"status\" [attr.data-testid]=\"'info-banner-' + infoKey\">\n \u2139\uFE0F {{ ruleResult().infoBanners[infoKey] }}\n </div>\n }\n\n <!-- Rules Validation Errors & Warnings -->\n @for (errKey of Object.keys(ruleResult().validationErrors); track errKey) {\n <div class=\"ngx-form__error-banner\" role=\"alert\" [attr.data-testid]=\"'rule-error-' + errKey\">\n \u26A0\uFE0F {{ ruleResult().validationErrors[errKey] }}\n </div>\n }\n @for (warnKey of Object.keys(ruleResult().validationWarnings); track warnKey) {\n <div class=\"ngx-form__warning-banner\" role=\"status\" [attr.data-testid]=\"'rule-warning-' + warnKey\">\n \u26A1 {{ ruleResult().validationWarnings[warnKey] }}\n </div>\n }\n\n <!-- Deferred VALUE_CHANGED notice for criticalField edits (vs session baseline) -->\n @if (changedCriticalFields().length > 0) {\n <div class=\"ngx-form__warning-banner\" role=\"status\" data-testid=\"critical-change-banner\">\n \uD83D\uDD12 Critical field changed:\n @for (field of changedCriticalFields(); track field.id) {\n <span>{{ resolveFieldLabel(field) }}{{ $last ? '' : ', ' }}</span>\n }\n \u2014 this differs from the value at the start of this session.\n </div>\n }\n\n <!-- Primary Tab bar \u2014 only if visible tabs defined -->\n @if (visibleTabs.length > 0) {\n <div class=\"ngx-form__tabs\" role=\"tablist\" data-testid=\"tab-strip\">\n @for (tab of visibleTabs; track tab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab\"\n [attr.data-testid]=\"'tab-' + tab.id\"\n [class.ngx-form__tab--active]=\"activeTab() === tab.id\"\n (click)=\"setActiveTab(tab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n >\n {{ resolveTabLabel(tab) }}\n </button>\n }\n </div>\n }\n\n <!-- Sub-tab bar \u2014 only if active tab has sub-tabs -->\n @if (visibleSubTabs.length > 0) {\n <div class=\"ngx-form__tabs ngx-form__tabs--sub\" role=\"tablist\" data-testid=\"subtab-strip\" style=\"margin-top: -12px; margin-bottom: 12px; font-size: 13px;\">\n @for (subTab of visibleSubTabs; track subTab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab ngx-form__tab--sub\"\n [attr.data-testid]=\"'subtab-' + subTab.id\"\n [class.ngx-form__tab--active]=\"(activeSubTabConfig?.id) === subTab.id\"\n (click)=\"setActiveSubTab(subTab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"(activeSubTabConfig?.id) === subTab.id\"\n >\n {{ resolveTabLabel(subTab) }}\n </button>\n }\n </div>\n }\n\n <!--\n One tabpanel per tab, wrapping both branches. Focus moves here on a tab switch, and a\n module tab is still a tab \u2014 putting the panel semantics on only the fields branch left\n a module tab with nowhere for focus to land.\n -->\n <div\n role=\"tabpanel\"\n tabindex=\"-1\"\n #formPanel\n class=\"ngx-form__tabpanel\"\n [attr.aria-label]=\"activeTabLabel\"\n >\n @if (activeTabModuleComponent) {\n <div class=\"ngx-form__module-panel\" data-testid=\"module-panel\">\n <ng-container *ngComponentOutlet=\"activeTabModuleComponent; inputs: (activeSubTabConfig || activeTabConfig)?.moduleInputs\"></ng-container>\n </div>\n } @else {\n <!-- Fields for active tab (Responsive 12-column grid) -->\n <div class=\"ngx-form__panel\" data-testid=\"form-panel\">\n @for (field of fieldsForActiveTab; track field.id) {\n <div\n class=\"ngx-form__field-slot\"\n [style.gridColumn]=\"getFieldSpan(field)\"\n [attr.id]=\"'field-container-' + field.id\"\n tabindex=\"-1\"\n >\n <ngx-dynamic-field\n [field]=\"field\"\n [control]=\"getControl(field.id, (activeSubTabConfig || activeTabConfig)?.id)!\"\n [config]=\"config\"\n [currentTabId]=\"(activeSubTabConfig || activeTabConfig)?.id\"\n [language]=\"language\"\n [readonly]=\"isFieldReadonly(field)\"\n [userRoles]=\"userRoles\"\n />\n @if (field.hasDrift) {\n <!--\n Drift was previously visible only in the builder, so a referenced field whose\n source had changed looked completely normal to the person filling the form in.\n role=\"status\" rather than \"alert\": it is information, not an error.\n -->\n <span\n class=\"ngx-field__drift\"\n role=\"status\"\n [attr.data-testid]=\"'field-' + field.id + '-drift'\"\n [title]=\"driftHint(field)\"\n >\u26A0 {{ driftHint(field) }}</span>\n }\n @if (field.criticalField && !readonly) {\n <button\n type=\"button\"\n class=\"ngx-form__lock\"\n [attr.data-testid]=\"'lock-' + field.id\"\n [attr.aria-pressed]=\"!isFieldLocked(field)\"\n [attr.aria-label]=\"\n (isFieldLocked(field) ? 'Unlock ' : 'Lock ') + resolveFieldLabel(field)\n \"\n [title]=\"\n isFieldLocked(field)\n ? 'Critical field \u2014 click to unlock for editing'\n : 'Critical field \u2014 click to lock'\n \"\n (click)=\"toggleFieldLock(field)\"\n >\n {{ isFieldLocked(field) ? '\uD83D\uDD12' : '\uD83D\uDD13' }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Submit button \u2014 gated by RBAC and isSaving lock -->\n @if (!readonly && permissions.canEdit) {\n <div class=\"ngx-form__actions\" data-testid=\"form-actions\">\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n data-testid=\"form-reset\"\n (click)=\"reset()\"\n [disabled]=\"isSaving()\"\n >\n Reset\n </button>\n <button\n type=\"submit\"\n class=\"ngx-form__submit\"\n data-testid=\"form-submit\"\n [disabled]=\"submitBlocked\"\n title=\"Shortcut: Ctrl+S\"\n >\n @if (isSaving() || loading) {\n Saving\u2026\n } @else {\n Save\n }\n </button>\n </div>\n }\n\n </form>\n} @else {\n <!-- permissions.view denied: render nothing of the record.\n Presentational only \u2014 the server must not send data this user may not see. -->\n <div class=\"ngx-form__denied\" role=\"status\" data-testid=\"form-access-denied\">\n You do not have permission to view this record.\n </div>\n}\n", styles: [".ngx-form__panel{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:16px 20px;align-items:start}.ngx-form__field-slot{display:flex;align-items:flex-start;gap:6px;min-width:0}.ngx-form__field-slot ngx-dynamic-field{flex:1;min-width:0}.ngx-form__lock{flex:0 0 auto;margin-top:22px;background:none;border:1px solid transparent;border-radius:6px;cursor:pointer;font-size:14px;line-height:1;padding:4px 6px}.ngx-form__lock:hover{border-color:#d1d5db;background:#f9fafb}@media (max-width: 768px){.ngx-form__panel{grid-template-columns:1fr}.ngx-form__field-slot{grid-column:span 12!important}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: DynamicFieldComponent, selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "currentTabId", "language", "readonly", "userRoles"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
849
- }
850
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicFormComponent, decorators: [{
851
- type: Component,
852
- args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ngx-dynamic-form', standalone: true, imports: [ReactiveFormsModule, DynamicFieldComponent, NgComponentOutlet], providers: [EntityRefSelectionService], template: "@if (canView) {\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\" class=\"ngx-form\" novalidate>\n\n <!-- Error message banner -->\n @if (error) {\n <div class=\"ngx-form__error-banner\" role=\"alert\" data-testid=\"form-error\">{{ error }}</div>\n }\n\n <!-- Rules Info Banners -->\n @for (infoKey of showInfoBanners ? Object.keys(ruleResult().infoBanners) : []; track infoKey) {\n <div class=\"ngx-form__info-banner\" role=\"status\" [attr.data-testid]=\"'info-banner-' + infoKey\">\n \u2139\uFE0F {{ ruleResult().infoBanners[infoKey] }}\n </div>\n }\n\n <!-- Rules Validation Errors & Warnings -->\n @for (errKey of Object.keys(ruleResult().validationErrors); track errKey) {\n <div class=\"ngx-form__error-banner\" role=\"alert\" [attr.data-testid]=\"'rule-error-' + errKey\">\n \u26A0\uFE0F {{ ruleResult().validationErrors[errKey] }}\n </div>\n }\n @for (warnKey of Object.keys(ruleResult().validationWarnings); track warnKey) {\n <div class=\"ngx-form__warning-banner\" role=\"status\" [attr.data-testid]=\"'rule-warning-' + warnKey\">\n \u26A1 {{ ruleResult().validationWarnings[warnKey] }}\n </div>\n }\n\n <!-- Deferred VALUE_CHANGED notice for criticalField edits (vs session baseline) -->\n @if (changedCriticalFields().length > 0) {\n <div class=\"ngx-form__warning-banner\" role=\"status\" data-testid=\"critical-change-banner\">\n \uD83D\uDD12 Critical field changed:\n @for (field of changedCriticalFields(); track field.id) {\n <span>{{ resolveFieldLabel(field) }}{{ $last ? '' : ', ' }}</span>\n }\n \u2014 this differs from the value at the start of this session.\n </div>\n }\n\n <!-- Primary Tab bar \u2014 only if visible tabs defined -->\n @if (visibleTabs.length > 0) {\n <div class=\"ngx-form__tabs\" role=\"tablist\" data-testid=\"tab-strip\">\n @for (tab of visibleTabs; track tab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab\"\n [attr.data-testid]=\"'tab-' + tab.id\"\n [class.ngx-form__tab--active]=\"activeTab() === tab.id\"\n (click)=\"setActiveTab(tab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n >\n {{ resolveTabLabel(tab) }}\n </button>\n }\n </div>\n }\n\n <!-- Sub-tab bar \u2014 only if active tab has sub-tabs -->\n @if (visibleSubTabs.length > 0) {\n <div class=\"ngx-form__tabs ngx-form__tabs--sub\" role=\"tablist\" data-testid=\"subtab-strip\" style=\"margin-top: -12px; margin-bottom: 12px; font-size: 13px;\">\n @for (subTab of visibleSubTabs; track subTab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab ngx-form__tab--sub\"\n [attr.data-testid]=\"'subtab-' + subTab.id\"\n [class.ngx-form__tab--active]=\"(activeSubTabConfig?.id) === subTab.id\"\n (click)=\"setActiveSubTab(subTab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"(activeSubTabConfig?.id) === subTab.id\"\n >\n {{ resolveTabLabel(subTab) }}\n </button>\n }\n </div>\n }\n\n <!--\n One tabpanel per tab, wrapping both branches. Focus moves here on a tab switch, and a\n module tab is still a tab \u2014 putting the panel semantics on only the fields branch left\n a module tab with nowhere for focus to land.\n -->\n <div\n role=\"tabpanel\"\n tabindex=\"-1\"\n #formPanel\n class=\"ngx-form__tabpanel\"\n [attr.aria-label]=\"activeTabLabel\"\n >\n @if (activeTabModuleComponent) {\n <div class=\"ngx-form__module-panel\" data-testid=\"module-panel\">\n <ng-container *ngComponentOutlet=\"activeTabModuleComponent; inputs: (activeSubTabConfig || activeTabConfig)?.moduleInputs\"></ng-container>\n </div>\n } @else {\n <!-- Fields for active tab (Responsive 12-column grid) -->\n <div class=\"ngx-form__panel\" data-testid=\"form-panel\">\n @for (field of fieldsForActiveTab; track field.id) {\n <div\n class=\"ngx-form__field-slot\"\n [style.gridColumn]=\"getFieldSpan(field)\"\n [attr.id]=\"'field-container-' + field.id\"\n tabindex=\"-1\"\n >\n <ngx-dynamic-field\n [field]=\"field\"\n [control]=\"getControl(field.id, (activeSubTabConfig || activeTabConfig)?.id)!\"\n [config]=\"config\"\n [currentTabId]=\"(activeSubTabConfig || activeTabConfig)?.id\"\n [language]=\"language\"\n [readonly]=\"isFieldReadonly(field)\"\n [userRoles]=\"userRoles\"\n />\n @if (field.hasDrift) {\n <!--\n Drift was previously visible only in the builder, so a referenced field whose\n source had changed looked completely normal to the person filling the form in.\n role=\"status\" rather than \"alert\": it is information, not an error.\n -->\n <span\n class=\"ngx-field__drift\"\n role=\"status\"\n [attr.data-testid]=\"'field-' + field.id + '-drift'\"\n [title]=\"driftHint(field)\"\n >\u26A0 {{ driftHint(field) }}</span>\n }\n @if (field.criticalField && !readonly) {\n <button\n type=\"button\"\n class=\"ngx-form__lock\"\n [attr.data-testid]=\"'lock-' + field.id\"\n [attr.aria-pressed]=\"!isFieldLocked(field)\"\n [attr.aria-label]=\"\n (isFieldLocked(field) ? 'Unlock ' : 'Lock ') + resolveFieldLabel(field)\n \"\n [title]=\"\n isFieldLocked(field)\n ? 'Critical field \u2014 click to unlock for editing'\n : 'Critical field \u2014 click to lock'\n \"\n (click)=\"toggleFieldLock(field)\"\n >\n {{ isFieldLocked(field) ? '\uD83D\uDD12' : '\uD83D\uDD13' }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Submit button \u2014 gated by RBAC and isSaving lock -->\n @if (!readonly && permissions.canEdit) {\n <div class=\"ngx-form__actions\" data-testid=\"form-actions\">\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n data-testid=\"form-reset\"\n (click)=\"reset()\"\n [disabled]=\"isSaving()\"\n >\n Reset\n </button>\n <button\n type=\"submit\"\n class=\"ngx-form__submit\"\n data-testid=\"form-submit\"\n [disabled]=\"submitBlocked\"\n title=\"Shortcut: Ctrl+S\"\n >\n @if (isSaving() || loading) {\n Saving\u2026\n } @else {\n Save\n }\n </button>\n </div>\n }\n\n </form>\n} @else {\n <!-- permissions.view denied: render nothing of the record.\n Presentational only \u2014 the server must not send data this user may not see. -->\n <div class=\"ngx-form__denied\" role=\"status\" data-testid=\"form-access-denied\">\n You do not have permission to view this record.\n </div>\n}\n", styles: [".ngx-form__panel{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:16px 20px;align-items:start}.ngx-form__field-slot{display:flex;align-items:flex-start;gap:6px;min-width:0}.ngx-form__field-slot ngx-dynamic-field{flex:1;min-width:0}.ngx-form__lock{flex:0 0 auto;margin-top:22px;background:none;border:1px solid transparent;border-radius:6px;cursor:pointer;font-size:14px;line-height:1;padding:4px 6px}.ngx-form__lock:hover{border-color:#d1d5db;background:#f9fafb}@media (max-width: 768px){.ngx-form__panel{grid-template-columns:1fr}.ngx-form__field-slot{grid-column:span 12!important}}\n"] }]
853
- }], propDecorators: { config: [{
854
- type: Input
855
- }], rules: [{
856
- type: Input
857
- }], initialData: [{
858
- type: Input
859
- }], originalData: [{
860
- type: Input
861
- }], userRoles: [{
862
- type: Input
863
- }], language: [{
864
- type: Input
865
- }], readonly: [{
866
- type: Input
867
- }], readOnlyFields: [{
868
- type: Input
869
- }], showInfoBanners: [{
870
- type: Input
871
- }], loading: [{
872
- type: Input
873
- }], error: [{
874
- type: Input
875
- }], changeDebounceMs: [{
876
- type: Input
877
- }], preview: [{
878
- type: Input
879
- }], formSubmit: [{
880
- type: Output
881
- }], formChange: [{
882
- type: Output
883
- }], formReset: [{
884
- type: Output
885
- }], activeTabChange: [{
886
- type: Output
887
- }], saveRejected: [{
888
- type: Output
889
- }], formPanel: [{
890
- type: ViewChild,
891
- args: ['formPanel']
892
- }], fieldHosts: [{
893
- type: ViewChildren,
894
- args: [DynamicFieldComponent]
895
- }], handleKeyboardEvent: [{
896
- type: HostListener,
897
- args: ['window:keydown', ['$event']]
898
- }] } });
899
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1mb3JtLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvZm9ybS9keW5hbWljLWZvcm0uY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vc3JjL2xpYi9mb3JtL2R5bmFtaWMtZm9ybS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLGlCQUFpQixFQUNqQixTQUFTLEVBQ1QsS0FBSyxFQUNMLE1BQU0sRUFDTixZQUFZLEVBS1osTUFBTSxFQUNOLFFBQVEsRUFDUixNQUFNLEVBQ04sU0FBUyxFQUVULFNBQVMsRUFDVCxZQUFZLEVBRVosWUFBWSxHQUNiLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3BELE9BQU8sRUFBbUIsU0FBUyxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV6RyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFROUMsT0FBTyxFQUNMLGNBQWMsRUFDZCxnQkFBZ0IsRUFDaEIsYUFBYSxFQUNiLHVCQUF1QixFQUN2QixPQUFPLEVBQ1AsVUFBVSxFQUNWLFVBQVUsRUFDVixjQUFjLEVBQ2Qsd0JBQXdCLEVBQ3hCLHNCQUFzQixFQUN0QixZQUFZLEVBQ1osVUFBVSxFQUNWLGNBQWMsR0FDZixNQUFNLHNCQUFzQixDQUFDO0FBQzlCLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3hGLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLHdDQUF3QyxDQUFDO0FBQ2xGLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLG1DQUFtQyxD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UTtzQkFBaEIsS0FBSztnQkFDRyxRQUFRO3NCQUFoQixLQUFLO2dCQUVHLGNBQWM7c0JBQXRCLEtBQUs7Z0JBS0csZUFBZTtzQkFBdkIsS0FBSztnQkFDRyxPQUFPO3NCQUFmLEtBQUs7Z0JBQ0csS0FBSztzQkFBYixLQUFLO2dCQU1HLGdCQUFnQjtzQkFBeEIsS0FBSztnQkFLRyxPQUFPO3NCQUFmLEtBQUs7Z0JBR0ksVUFBVTtzQkFBbkIsTUFBTTtnQkFDRyxVQUFVO3NCQUFuQixNQUFNO2dCQUNHLFNBQVM7c0JBQWxCLE1BQU07Z0JBRUcsZUFBZTtzQkFBeEIsTUFBTTtnQkFLRyxZQUFZO3NCQUFyQixNQUFNO2dCQTBCeUIsU0FBUztzQkFBeEMsU0FBUzt1QkFBQyxXQUFXO2dCQUd1QixVQUFVO3NCQUF0RCxZQUFZO3VCQUFDLHFCQUFxQjtnQkFhbkMsbUJBQW1CO3NCQURsQixZQUFZO3VCQUFDLGdCQUFnQixFQUFFLENBQUMsUUFBUSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENoYW5nZURldGVjdG9yUmVmLFxuICBDb21wb25lbnQsXG4gIElucHV0LFxuICBPdXRwdXQsXG4gIEV2ZW50RW1pdHRlcixcbiAgT25DaGFuZ2VzLFxuICBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgU2ltcGxlQ2hhbmdlcyxcbiAgc2lnbmFsLFxuICBjb21wdXRlZCxcbiAgaW5qZWN0LFxuICBpc0Rldk1vZGUsXG4gIEVsZW1lbnRSZWYsXG4gIFZpZXdDaGlsZCxcbiAgVmlld0NoaWxkcmVuLFxuICBRdWVyeUxpc3QsXG4gIEhvc3RMaXN0ZW5lcixcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBOZ0NvbXBvbmVudE91dGxldCB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBBYnN0cmFjdENvbnRyb2wsIEZvcm1BcnJheSwgRm9ybUJ1aWxkZXIsIEZvcm1Hcm91cCwgUmVhY3RpdmVGb3Jtc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB7IFN1YnNjcmlwdGlvbiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZGVib3VuY2VUaW1lIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHR5cGUge1xuICBBdXRvUGF0Y2hDb25maWcsXG4gIEVudGl0eUZvcm1Db25maWcsXG4gIEZvcm1SdWxlLFxuICBOZXN0ZWRGaWVsZENvbmZpZyxcbiAgTmVzdGVkVGFiQ29uZmlnLFxufSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XG5pbXBvcnQge1xuICBhcHBseUF1dG9QYXRjaCxcbiAgYXBwbHlQYXRjaE9uVHJ1ZSxcbiAgbWlncmF0ZVJlY29yZCxcbiAgZXZhbHVhdGVGaWVsZFZpc2liaWxpdHksXG4gIGZpbmRUYWIsXG4gIGdldFRhYkRhdGEsXG4gIGdldFRhYlBhdGgsXG4gIGdldFZhbHVlQnlQYXRoLFxuICBub3JtYWxpemVBcnJheVN0cnVjdHVyZXMsXG4gIG5vcm1hbGl6ZUNvbmZpZ09wdGlvbnMsXG4gIHJlc29sdmVMYWJlbCxcbiAgc2V0VGFiRGF0YSxcbiAgc2V0VmFsdWVCeVBhdGgsXG59IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcbmltcG9ydCB7IER5bmFtaWNGaWVsZENvbXBvbmVudCB9IGZyb20gJy4vZHluYW1pYy1maWVsZC9keW5hbWljLWZpZWxkLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBDT01NT05fTU9EVUxFU19SRUdJU1RSWSwgUkVDT1JEX01JR1JBVElPTlMgfSBmcm9tICcuLi90b2tlbnMvaW5qZWN0aW9uLXRva2Vucyc7XG5pbXBvcnQgeyBWYWxpZGF0b3JSZWdpc3RyeVNlcnZpY2UgfSBmcm9tICcuLi9zZXJ2aWNlcy92YWxpZGF0b3ItcmVnaXN0cnkuc2VydmljZSc7XG5pbXBvcnQgeyBIb29rUmVnaXN0cnlTZXJ2aWNlIH0gZnJvbSAnLi4vc2VydmljZXMvaG9vay1yZWdpc3RyeS5zZXJ2aWNlJztcbmltcG9ydCB7IFJiYWNTZXJ2aWNlIH0gZnJvbSAnLi4vc2VydmljZXMvcmJhYy5zZXJ2aWNlJztcbmltcG9ydCB7IFJ1bGVzRXZhbHVhdGlvblNlcnZpY2UgfSBmcm9tICcuLi9zZXJ2aWNlcy9ydWxlcy1ldmFsdWF0aW9uLnNlcnZpY2UnO1xuaW1wb3J0IHsgRW50aXR5UmVmU2VsZWN0aW9uU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL2VudGl0eS1yZWYtc2VsZWN0aW9uLnNlcnZpY2UnO1xuXG4vKipcbiAqIER5bmFtaWNGb3JtQ29tcG9uZW50IOKAlCB0aGUgbWFpbiBmb3JtIGNvbXBvbmVudC5cbiAqIFJlbmRlcnMgYSByZWFjdGl2ZSBmb3JtIGZyb20gRW50aXR5Rm9ybUNvbmZpZyB3aXRoIHRhYiBzdXBwb3J0LCByZXNwb25zaXZlIDEyLWNvbCBncmlkLFxuICogcnVsZXMgZXZhbHVhdGlvbiwgY29uZGl0aW9uYWwgdmlzaWJpbGl0eSwgYXV0b1BhdGNoL3BhdGNoT25UcnVlLCBjcml0aWNhbEZpZWxkIGxvY2tpbmcsXG4gKiBrZXlib2FyZCBzaG9ydGN1dHMgKEN0cmwrUywgRXNjKSwgYW5kIFJCQUMtZ2F0ZWQgc3VibWlzc2lvbi5cbiAqL1xuQENvbXBvbmVudCh7XG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBzZWxlY3RvcjogJ25neC1keW5hbWljLWZvcm0nLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbUmVhY3RpdmVGb3Jtc01vZHVsZSwgRHluYW1pY0ZpZWxkQ29tcG9uZW50LCBOZ0NvbXBvbmVudE91dGxldF0sXG4gIC8vIFNjb3BlZCBwZXIgZm9ybSBpbnN0YW5jZTogZW50aXR5LXJlZiBzZWxlY3Rpb25zIG11c3Qgbm90IGxlYWsgYmV0d2VlbiBjb25jdXJyZW50IGZvcm1zLlxuICBwcm92aWRlcnM6IFtFbnRpdHlSZWZTZWxlY3Rpb25TZXJ2aWNlXSxcbiAgdGVtcGxhdGVVcmw6ICcuL2R5bmFtaWMtZm9ybS5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlczogW1xuICAgIGBcbiAgICAgIC5uZ3gtZm9ybV9fcGFuZWwge1xuICAgICAgICBkaXNwbGF5OiBncmlkO1xuICAgICAgICBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IHJlcGVhdCgxMiwgbWlubWF4KDAsIDFmcikpO1xuICAgICAgICBnYXA6IDE2cHggMjBweDtcbiAgICAgICAgYWxpZ24taXRlbXM6IHN0YXJ0O1xuICAgICAgfVxuICAgICAgLm5neC1mb3JtX19maWVsZC1zbG90IHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgYWxpZ24taXRlbXM6IGZsZXgtc3RhcnQ7XG4gICAgICAgIGdhcDogNnB4O1xuICAgICAgICBtaW4td2lkdGg6IDA7XG4gICAgICB9XG4gICAgICAubmd4LWZvcm1fX2ZpZWxkLXNsb3Qgbmd4LWR5bmFtaWMtZmllbGQge1xuICAgICAgICBmbGV4OiAxO1xuICAgICAgICBtaW4td2lkdGg6IDA7XG4gICAgICB9XG4gICAgICAubmd4LWZvcm1fX2xvY2sge1xuICAgICAgICBmbGV4OiAwIDAgYXV0bztcbiAgICAgICAgbWFyZ2luLXRvcDogMjJweDtcbiAgICAgICAgYmFja2dyb3VuZDogbm9uZTtcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgdHJhbnNwYXJlbnQ7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDZweDtcbiAgICAgICAgY3Vyc29yOiBwb2ludGVyO1xuICAgICAgICBmb250LXNpemU6IDE0cHg7XG4gICAgICAgIGxpbmUtaGVpZ2h0OiAxO1xuICAgICAgICBwYWRkaW5nOiA0cHggNnB4O1xuICAgICAgfVxuICAgICAgLm5neC1mb3JtX19sb2NrOmhvdmVyIHtcbiAgICAgICAgYm9yZGVyLWNvbG9yOiAjZDFkNWRiO1xuICAgICAgICBiYWNrZ3JvdW5kOiAjZjlmYWZiO1xuICAgICAgfVxuICAgICAgQG1lZGlhIChtYXgtd2lkdGg6IDc2OHB4KSB7XG4gICAgICAgIC5uZ3gtZm9ybV9fcGFuZWwge1xuICAgICAgICAgIGdyaWQtdGVtcGxhdGUtY29sdW1uczogMWZyO1xuICAgICAgICB9XG4gICAgICAgIC5uZ3gtZm9ybV9fZmllbGQtc2xvdCB7XG4gICAgICAgICAgZ3JpZC1jb2x1bW46IHNwYW4gMTIgIWltcG9ydGFudDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIGAsXG4gIF0sXG59KVxuZXhwb3J0IGNsYXNzIER5bmFtaWNGb3JtQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSB7XG4gIC8vIOKUgOKUgOKUgCBJbnB1dHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8qKlxuICAgKiBUaGUgc2NoZW1hIHRvIHJlbmRlci5cbiAgICpcbiAgICogQW4gYWNjZXNzb3IgcGFpciByYXRoZXIgdGhhbiBhIHBsYWluIGZpZWxkOiBgbmdPbkNoYW5nZXNgIG5vcm1hbGlzZXMgdGhlIG9wdGlvbiBzaGFwZSxcbiAgICogYW5kIHRoYXQgcmVzdWx0IGlzIGtlcHQgaGVyZSBpbnN0ZWFkIG9mIGJlaW5nIHdyaXR0ZW4gYmFjayBvdmVyIHRoZSBpbnB1dC4gUmVhc3NpZ25pbmdcbiAgICogYW4gYEBJbnB1dGAgd29ya3MsIGJ1dCBpdCBtdXRhdGVzIHdoYXQgdGhlIHBhcmVudCBoYW5kZWQgdXMg4oCUIGFuZCBhIHBhcmVudCB1c2luZyBPblB1c2hcbiAgICogb3Igc2lnbmFscyB3b3VsZCBuZXZlciBvYnNlcnZlIHRoZSByZXBsYWNlbWVudCBhbnl3YXkuXG4gICAqXG4gICAqIFJlYWRzIG9mIGB0aGlzLmNvbmZpZ2AgZ2V0IHRoZSBub3JtYWxpc2VkIGNvcHkgb25jZSBpdCBleGlzdHMsIHNvIGV2ZXJ5IGludGVybmFsIGNhbGxlclxuICAgKiBpcyB1bmFmZmVjdGVkLlxuICAgKi9cbiAgQElucHV0KClcbiAgc2V0IGNvbmZpZyh2YWx1ZTogRW50aXR5Rm9ybUNvbmZpZykge1xuICAgIHRoaXMucmF3Q29uZmlnID0gdmFsdWU7XG4gICAgdGhpcy5ub3JtYWxpemVkQ29uZmlnID0gdW5kZWZpbmVkOyAvLyByZS1ub3JtYWxpc2VkIGluIG5nT25DaGFuZ2VzXG4gIH1cbiAgZ2V0IGNvbmZpZygpOiBFbnRpdHlGb3JtQ29uZmlnIHtcbiAgICByZXR1cm4gdGhpcy5ub3JtYWxpemVkQ29uZmlnID8/IHRoaXMucmF3Q29uZmlnO1xuICB9XG4gIHByaXZhdGUgcmF3Q29uZmlnITogRW50aXR5Rm9ybUNvbmZpZztcbiAgcHJpdmF0ZSBub3JtYWxpemVkQ29uZmlnPzogRW50aXR5Rm9ybUNvbmZpZztcbiAgQElucHV0KCkgcnVsZXM/OiBGb3JtUnVsZVtdO1xuICBASW5wdXQoKSBpbml0aWFsRGF0YT86IFJlY29yZDxzdHJpbmcsIGFueT47XG4gIC8qKiBTZXNzaW9uLW9yaWdpbmFsIHZhbHVlcyBmb3IgYFZBTFVFX0NIQU5HRURgIHJ1bGVzLiBDYXB0dXJlZCBmcm9tIHRoZSBmaXJzdCBidWlsZCB3aGVuIG9taXR0ZWQuICovXG4gIEBJbnB1dCgpIG9yaWdpbmFsRGF0YT86IFJlY29yZDxzdHJpbmcsIGFueT47XG4gIEBJbnB1dCgpIHVzZXJSb2xlczogc3RyaW5nW10gPSBbXTtcbiAgQElucHV0KCkgbGFuZ3VhZ2U6IHN0cmluZyA9ICdlbic7XG4gIEBJbnB1dCgpIHJlYWRvbmx5OiBib29sZWFuID0gZmFsc2U7XG4gIC8qKiBGaWVsZCBpZHMgZm9yY2VkIHJlYWQtb25seSB3aGlsZSB0aGUgcmVzdCBvZiB0aGUgZm9ybSBzdGF5cyBlZGl0YWJsZS4gKi9cbiAgQElucHV0KCkgcmVhZE9ubHlGaWVsZHM6IHN0cmluZ1tdID0gW107XG4gIC8qKlxuICAgKiBSZW5kZXIgcnVsZSBgaW5mb2AgYmFubmVycyBpbmxpbmUuIFRoZSByZWNvcmQgZWRpdG9yIHNldHMgdGhpcyBmYWxzZSBhbmQgcmVuZGVycyBpdHNcbiAgICogb3duIGRpc21pc3NpYmxlIHZlcnNpb25zLCBzbyB0aGUgdHdvIGRvIG5vdCBkb3VibGUgdXAuXG4gICAqL1xuICBASW5wdXQoKSBzaG93SW5mb0Jhbm5lcnM6IGJvb2xlYW4gPSB0cnVlO1xuICBASW5wdXQoKSBsb2FkaW5nOiBib29sZWFuID0gZmFsc2U7XG4gIEBJbnB1dCgpIGVycm9yOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgLyoqXG4gICAqIE1pbGxpc2Vjb25kcyB0byBkZWJvdW5jZSBgZm9ybUNoYW5nZWAuIFRoZSByZWZlcmVuY2UgZGVib3VuY2VzIGF0IDMwMG1zIHNvIGEgY29uc3VtZXJcbiAgICogcmUtcmVuZGVyaW5nIG9uIGV2ZXJ5IHZhbHVlIGNoYW5nZSBpcyBub3QgZHJpdmVuIHBlciBrZXlzdHJva2UuIFplcm8gZW1pdHNcbiAgICogc3luY2hyb25vdXNseSwgd2hpY2ggdGhlIHJlY29yZCBlZGl0b3IgcmVsaWVzIG9uIGZvciBpdHMgb3duIGJvb2trZWVwaW5nLlxuICAgKi9cbiAgQElucHV0KCkgY2hhbmdlRGVib3VuY2VNczogbnVtYmVyID0gMDtcbiAgLyoqXG4gICAqIFByZXZpZXcgbW9kZTogc2VlZCBvbmUgZW1wdHkgcm93IHBlciBgYXJyYXlgIGZpZWxkIHNvIHRoZSBzdHJ1Y3R1cmUgaXMgdmlzaWJsZSwgdGhlblxuICAgKiBkaXNhYmxlIHRoZSBmb3JtLiBVc2VkIGJ5IHRoZSBidWlsZGVyJ3MgbGl2ZSBwcmV2aWV3LlxuICAgKi9cbiAgQElucHV0KCkgcHJldmlldzogYm9vbGVhbiA9IGZhbHNlO1xuXG4gIC8vIOKUgOKUgOKUgCBPdXRwdXRzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICBAT3V0cHV0KCkgZm9ybVN1Ym1pdCA9IG5ldyBFdmVudEVtaXR0ZXI8UmVjb3JkPHN0cmluZywgYW55Pj4oKTtcbiAgQE91dHB1dCgpIGZvcm1DaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPFJlY29yZDxzdHJpbmcsIGFueT4+KCk7XG4gIEBPdXRwdXQoKSBmb3JtUmVzZXQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBUaGUgdmlzaWJsZSB0YWIgY2hhbmdlZC4gTGV0cyBhIGhvc3QgdHJhY2sgaXQgd2l0aG91dCByZWFjaGluZyBpbnRvIHRoaXMgY29tcG9uZW50LiAqL1xuICBAT3V0cHV0KCkgYWN0aXZlVGFiQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG4gIC8qKlxuICAgKiBFbWl0dGVkIHdoZW4gYSBgYmVmb3JlU2F2ZWAgaG9vayBhYm9ydGVkIHRoZSBzYXZlIOKAlCBieSByZXR1cm5pbmcgZmFsc2Ugb3IgdGhyb3dpbmcuXG4gICAqIGBmb3JtU3VibWl0YCBkb2VzIG5vdCBmaXJlIGluIHRoYXQgY2FzZS5cbiAgICovXG4gIEBPdXRwdXQoKSBzYXZlUmVqZWN0ZWQgPSBuZXcgRXZlbnRFbWl0dGVyPHsgcmVhc29uOiBzdHJpbmc7IGVycm9yPzogdW5rbm93biB9PigpO1xuXG4gIC8vIOKUgOKUgOKUgCBTZXJ2aWNlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgcHJpdmF0ZSByZWFkb25seSBjZHIgPSBpbmplY3QoQ2hhbmdlRGV0ZWN0b3JSZWYpO1xuICBwcml2YXRlIHJlYWRvbmx5IGZiID0gaW5qZWN0KEZvcm1CdWlsZGVyKTtcbiAgcHJpdmF0ZSByZWFkb25seSB2YWxpZGF0b3JSZWdpc3RyeSA9IGluamVjdChWYWxpZGF0b3JSZWdpc3RyeVNlcnZpY2UpO1xuICBwcml2YXRlIHJlYWRvbmx5IGhvb2tSZWdpc3RyeSA9IGluamVjdChIb29rUmVnaXN0cnlTZXJ2aWNlKTtcbiAgcHJpdmF0ZSByZWFkb25seSByYmFjU2VydmljZSA9IGluamVjdChSYmFjU2VydmljZSk7XG4gIHByaXZhdGUgcmVhZG9ubHkgcnVsZXNFdmFsdWF0aW9uID0gaW5qZWN0KFJ1bGVzRXZhbHVhdGlvblNlcnZpY2UpO1xuICBwcml2YXRlIHJlYWRvbmx5IGVudGl0eVJlZlNlbGVjdGlvbiA9IGluamVjdChFbnRpdHlSZWZTZWxlY3Rpb25TZXJ2aWNlKTtcbiAgcHJpdmF0ZSByZWFkb25seSBjb21tb25Nb2R1bGVzUmVnaXN0cnkgPSBpbmplY3QoQ09NTU9OX01PRFVMRVNfUkVHSVNUUlksIHsgb3B0aW9uYWw6IHRydWUgfSk7XG4gIHByaXZhdGUgcmVhZG9ubHkgbWlncmF0aW9ucyA9IGluamVjdChSRUNPUkRfTUlHUkFUSU9OUywgeyBvcHRpb25hbDogdHJ1ZSB9KSA/PyBbXTtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgT2JqZWN0ID0gT2JqZWN0O1xuXG4gIC8vIOKUgOKUgOKUgCBTaWduYWxzIChsb2NhbCByZWFjdGl2ZSBzdGF0ZSkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIHJlYWRvbmx5IGFjdGl2ZVRhYiA9IHNpZ25hbDxzdHJpbmc+KCcnKTtcbiAgcmVhZG9ubHkgYWN0aXZlU3ViVGFiID0gc2lnbmFsPHN0cmluZz4oJycpO1xuICByZWFkb25seSBpc1NhdmluZyA9IHNpZ25hbChmYWxzZSk7XG4gIHJlYWRvbmx5IGZvcm1WYWx1ZXMgPSBzaWduYWw8UmVjb3JkPHN0cmluZywgYW55Pj4oe30pO1xuICAvKiogQmFzZWxpbmUgY2FwdHVyZWQgYXQgZmlyc3QgYnVpbGQsIHVzZWQgd2hlbiBgb3JpZ2luYWxEYXRhYCBpcyBub3Qgc3VwcGxpZWQuICovXG4gIHJlYWRvbmx5IHNlc3Npb25CYXNlbGluZSA9IHNpZ25hbDxSZWNvcmQ8c3RyaW5nLCBhbnk+Pih7fSk7XG4gIC8qKiBDcml0aWNhbCBmaWVsZHMgdGhlIHVzZXIgaGFzIGV4cGxpY2l0bHkgdW5sb2NrZWQgZm9yIGVkaXRpbmcuICovXG4gIHJlYWRvbmx5IHVubG9ja2VkRmllbGRzID0gc2lnbmFsPFJlYWRvbmx5U2V0PHN0cmluZz4+KG5ldyBTZXQ8c3RyaW5nPigpKTtcblxuICAvKiogVGhlIGFjdGl2ZSB0YWIncyBmaWVsZCBwYW5lbCwgZm9jdXNlZCBhZnRlciBhIHRhYiBzd2l0Y2guICovXG4gIEBWaWV3Q2hpbGQoJ2Zvcm1QYW5lbCcpIHByaXZhdGUgZm9ybVBhbmVsPzogRWxlbWVudFJlZjxIVE1MRWxlbWVudD47XG5cbiAgLyoqIFRoZSBob3N0ZWQgZmllbGQgY29tcG9uZW50cywgc28gYW4gZXh0ZXJuYWwgY29udHJvbC1zdGF0ZSBjaGFuZ2UgY2FuIHJlYWNoIHRoZW0uICovXG4gIEBWaWV3Q2hpbGRyZW4oRHluYW1pY0ZpZWxkQ29tcG9uZW50KSBwcml2YXRlIGZpZWxkSG9zdHM/OiBRdWVyeUxpc3Q8RHluYW1pY0ZpZWxkQ29tcG9uZW50PjtcblxuICAvLyDilIDilIDilIAgRm9ybSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbiAgZm9ybSE6IEZvcm1Hcm91cDtcblxuICBwcml2YXRlIHZhbHVlU3ViPzogU3Vic2NyaXB0aW9uO1xuICBwcml2YXRlIHN0YXR1c1N1Yj86IFN1YnNjcmlwdGlvbjtcbiAgcHJpdmF0ZSBzZWxlY3Rpb25TdWI/OiBTdWJzY3JpcHRpb247XG4gIC8qKiBQcmV2aW91cyB2YWx1ZXMsIGZvciBkZXRlY3RpbmcgYHBhdGNoT25UcnVlYCBmYWxzZeKGknRydWUgdHJhbnNpdGlvbnMuICovXG4gIHByaXZhdGUgcHJldmlvdXNWYWx1ZXM6IFJlY29yZDxzdHJpbmcsIGFueT4gPSB7fTtcblxuICAvLyDilIDilIDilIAgS2V5Ym9hcmQgU2hvcnRjdXRzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICBASG9zdExpc3RlbmVyKCd3aW5kb3c6a2V5ZG93bicsIFsnJGV2ZW50J10pXG4gIGhhbmRsZUtleWJvYXJkRXZlbnQoZXZlbnQ6IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoKGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSkgJiYgZXZlbnQua2V5LnRvTG93ZXJDYXNlKCkgPT09ICdzJykge1xuICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgIGlmICghdGhpcy5yZWFkb25seSAmJiB0aGlzLmNhblN1Ym1pdCAmJiB0aGlzLmZvcm0/LnZhbGlkKSB7XG4gICAgICAgIHRoaXMuc3VibWl0KCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8g4pSA4pSA4pSAIENvbXB1dGVkIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICByZWFkb25seSBydWxlUmVzdWx0ID0gY29tcHV0ZWQoKCkgPT5cbiAgICB0aGlzLnJ1bGVzRXZhbHVhdGlvbi5ldmFsdWF0ZSh0aGlzLnJ1bGVzLCB0aGlzLmZvcm1WYWx1ZXMoKSwgdGhpcy5iYXNlbGluZSgpKSxcbiAgKTtcblxuICAvKipcbiAgICogQ3JpdGljYWwgZmllbGRzIHdob3NlIHZhbHVlIGRpZmZlcnMgZnJvbSB0aGUgc2Vzc2lvbiBiYXNlbGluZS5cbiAgICogVGhlc2UgZHJpdmUgdGhlIGRlZmVycmVkIGBWQUxVRV9DSEFOR0VEYCBiYW5uZXIg4oCUIGEgY3JpdGljYWwgZWRpdCBpcyBhbm5vdW5jZWQgb25jZSxcbiAgICogYWdhaW5zdCB0aGUgdmFsdWUgdGhlIHJlY29yZCBoYWQgd2hlbiB0aGUgc2Vzc2lvbiBzdGFydGVkLCBub3QgcGVyIGtleXN0cm9rZS5cbiAgICovXG4gIHJlYWRvbmx5IGNoYW5nZWRDcml0aWNhbEZpZWxkcyA9IGNvbXB1dGVkPE5lc3RlZEZpZWxkQ29uZmlnW10+KCgpID0+IHtcbiAgICBjb25zdCBiYXNlbGluZSA9IHRoaXMuYmFzZWxpbmUoKTtcbiAgICBpZiAoIWJhc2VsaW5lIHx8IE9iamVjdC5rZXlzKGJhc2VsaW5lKS5sZW5ndGggPT09IDApIHJldHVybiBbXTtcbiAgICBjb25zdCB2YWx1ZXMgPSB0aGlzLmZvcm1WYWx1ZXMoKTtcbiAgICByZXR1cm4gdGhpcy5hbGxGaWVsZHMoKS5maWx0ZXIoXG4gICAgICBmID0+IGYuY3JpdGljYWxGaWVsZCAmJiAhdGhpcy5zYW1lVmFsdWUodmFsdWVzW2YuaWRdLCBiYXNlbGluZVtmLmlkXSksXG4gICAgKTtcbiAgfSk7XG5cbiAgZ2V0IHRhYnMoKTogTmVzdGVkVGFiQ29uZmlnW10ge1xuICAgIHJldHVybiB0aGlzLmNvbmZpZz8udGFicyB8fCBbXTtcbiAgfVxuXG4gIGdldCB2aXNpYmxlVGFicygpOiBOZXN0ZWRUYWJDb25maWdbXSB7XG4gICAgY29uc3QgaGlkZGVuID0gdGhpcy5ydWxlUmVzdWx0KCkuaGlkZGVuVGFicztcbiAgICByZXR1cm4gdGhpcy50YWJzLmZpbHRlcih0YWIgPT4gdGFiLnZpc2liaWxpdHkgIT09IGZhbHNlICYmICFoaWRkZW4uaW5jbHVkZXModGFiLmlkKSk7XG4gIH1cblxuICBnZXQgYWN0aXZlVGFiQ29uZmlnKCk6IE5lc3RlZFRhYkNvbmZpZyB8IG51bGwge1xuICAgIGNvbnN0IHRhYklkID0gdGhpcy5hY3RpdmVUYWIoKTtcbiAgICBpZiAoIXRhYklkKSByZXR1cm4gdGhpcy52aXNpYmxlVGFic1swXSA/PyBudWxsO1xuICAgIHJldHVybiBmaW5kVGFiKHRoaXMudGFicywgdGFiSWQpO1xuICB9XG5cbiAgZ2V0IHZpc2libGVTdWJUYWJzKCk6IE5lc3RlZFRhYkNvbmZpZ1tdIHtcbiAgICBjb25zdCBhY3RpdmUgPSB0aGlzLmFjdGl2ZVRhYkNvbmZpZztcbiAgICBpZiAoIWFjdGl2ZT8uY2hpbGRyZW4gfHwgYWN0aXZlLmNoaWxkcmVuLmxlbmd0aCA9PT0gMCkgcmV0dXJuIFtdO1xuICAgIGNvbnN0IGhpZGRlbiA9IHRoaXMucnVsZVJlc3VsdCgpLmhpZGRlblRhYnM7XG4gICAgcmV0dXJuIGFjdGl2ZS5jaGlsZHJlbi5maWx0ZXIodGFiID0+IHRhYi52aXNpYmlsaXR5ICE9PSBmYWxzZSAmJiAhaGlkZGVuLmluY2x1ZGVzKHRhYi5pZCkpO1xuICB9XG5cbiAgZ2V0IGFjdGl2ZVN1YlRhYkNvbmZpZygpOiBOZXN0ZWRUYWJDb25maWcgfCBudWxsIHtcbiAgICBjb25zdCBzdWJUYWJzID0gdGhpcy52aXNpYmxlU3ViVGFicztcbiAgICBpZiAoIXN1YlRhYnMubGVuZ3RoKSByZXR1cm4gbnVsbDtcbiAgICBjb25zdCBzdWJJZCA9IHRoaXMuYWN0aXZlU3ViVGFiKCk7XG4gICAgcmV0dXJuIHN1YlRhYnMuZmluZChzID0+IHMuaWQgPT09IHN1YklkKSA/PyBzdWJUYWJzWzBdO1xuICB9XG5cbiAgZ2V0IGFjdGl2ZVRhYk1vZHVsZUNvbXBvbmVudCgpOiBhbnkgfCBudWxsIHtcbiAgICBjb25zdCBhY3RpdmUgPSB0aGlzLmFjdGl2ZVN1YlRhYkNvbmZpZyA/PyB0aGlzLmFjdGl2ZVRhYkNvbmZpZztcbiAgICBpZiAoIWFjdGl2ZT8ubW9kdWxlTmFtZSB8fCAhdGhpcy5jb21tb25Nb2R1bGVzUmVnaXN0cnkpIHJldHVybiBudWxsO1xuICAgIGNvbnN0IGVudHJ5ID0gdGhpcy5jb21tb25Nb2R1bGVzUmVnaXN0cnkuZmluZChcbiAgICAgIG0gPT4gbS5pZCA9PT0gYWN0aXZlLm1vZHVsZU5hbWUgfHwgbS5jb21wb25lbnQgPT09IGFjdGl2ZS5tb2R1bGVOYW1lLFxuICAgICk7XG4gICAgcmV0dXJuIGVudHJ5ID8gZW50cnkuY29tcG9uZW50IDogbnVsbDtcbiAgfVxuXG4gIGdldCBmaWVsZHNGb3JBY3RpdmVUYWIoKTogTmVzdGVkRmllbGRDb25maWdbXSB7XG4gICAgY29uc3QgYWN0aXZlID0gdGhpcy5hY3RpdmVTdWJUYWJDb25maWcgPz8gdGhpcy5hY3RpdmVUYWJDb25maWc7XG4gICAgaWYgKGFjdGl2ZT8ubW9kdWxlTmFtZSkgcmV0dXJuIFtdO1xuICAgIGNvbnN0IHJhd0ZpZWxkcyA9IGFjdGl2ZSA/IChhY3RpdmUuZmllbGRzIHx8IFtdKSA6ICgodGhpcy5jb25maWc/LnRhYnMgfHwgW10pLmZsYXRNYXAodCA9PiB0LmZpZWxkcyB8fCBbXSkpO1xuICAgIGNvbnN0IGN1cnJlbnRWYWx1ZXMgPSB0aGlzLmZvcm1WYWx1ZXMoKTtcbiAgICBjb25zdCBoaWRkZW5GaWVsZHMgPSB0aGlzLnJ1bGVSZXN1bHQoKS5oaWRkZW5GaWVsZHM7XG5cbiAgICByZXR1cm4gcmF3RmllbGRzLmZpbHRlcihcbiAgICAgIGZpZWxkID0+IGV2YWx1YXRlRmllbGRWaXNpYmlsaXR5KGZpZWxkLCBjdXJyZW50VmFsdWVzKSAmJiAhaGlkZGVuRmllbGRzLmluY2x1ZGVzKGZpZWxkLmlkKSxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEVudGl0eS1sZXZlbCBwZXJtaXNzaW9ucyBmb3IgdGhlIGN1cnJlbnQgcm9sZXMuXG4gICAqXG4gICAqIENhY2hlZCByYXRoZXIgdGhhbiByZWNvbXB1dGVkOiB0aGlzIGlzIHJlYWQgZnJvbSB0aGUgdGVtcGxhdGUgc2V2ZXJhbCB0aW1lcyBwZXIgY2hhbmdlXG4gICAqIGRldGVjdGlvbiBwYXNzLCBhbmQgYSBmcmVzaCBvYmplY3QgZWFjaCB0aW1lIGlzIGJvdGggd2FzdGVkIGFsbG9jYXRpb24gYW5kIGEgbmV3XG4gICAqIGlkZW50aXR5IGZvciBhbnl0aGluZyBjb21wYXJpbmcgYnkgcmVmZXJlbmNlLiBJbnZhbGlkYXRlZCBpbiBgbmdPbkNoYW5nZXNgIHdoZW5cbiAgICogYGNvbmZpZ2Agb3IgYHVzZXJSb2xlc2AgY2hhbmdlLCB3aGljaCBhcmUgaXRzIG9ubHkgaW5wdXRzLlxuICAgKi9cbiAgZ2V0IHBlcm1pc3Npb25zKCk6IFJldHVyblR5cGU8UmJhY1NlcnZpY2VbJ2dldFBlcm1pc3Npb25zJ10+IHtcbiAgICByZXR1cm4gKHRoaXMucGVybWlzc2lvbnNDYWNoZSA/Pz0gdGhpcy5yYmFjU2VydmljZS5nZXRQZXJtaXNzaW9ucyh0aGlzLmNvbmZpZywgdGhpcy51c2VyUm9sZXMpKTtcbiAgfVxuXG4gIHByaXZhdGUgcGVybWlzc2lvbnNDYWNoZTogUmV0dXJuVHlwZTxSYmFjU2VydmljZVsnZ2V0UGVybWlzc2lvbnMnXT4gfCBudWxsID0gbnVsbDtcblxuICAvKipcbiAgICogV2hldGhlciB0aGUgY3VycmVudCByb2xlcyBtYXkgc2VlIHRoaXMgcmVjb3JkIGF0IGFsbC5cbiAgICpcbiAgICogYHBlcm1pc3Npb25zLnZpZXdgIHVzZWQgdG8gYmUgY29tcHV0ZWQgYW5kIHRocm93biBhd2F5IOKAlCBhIHVzZXIgd2hvc2Ugcm9sZXMgZmFpbGVkIGl0XG4gICAqIHN0aWxsIHJlY2VpdmVkIHRoZSBjb21wbGV0ZSBmb3JtIHdpdGggZXZlcnkgdmFsdWUgaW4gdGhlIERPTS4gSXQgaXMgaG9ub3VyZWQgbm93LlxuICAgKlxuICAgKiBBcyB3aXRoIG1hc2tpbmcsIHRoaXMgaXMgcHJlc2VudGF0aW9uYWw6IGl0IHN0b3BzIHRoZSBicm93c2VyIHJlbmRlcmluZyBkYXRhLCBpdCBkb2VzXG4gICAqIG5vdCBzdG9wIHRoZSBkYXRhIHJlYWNoaW5nIHRoZSBicm93c2VyLiBBdXRob3JpemUgb24gdGhlIHNlcnZlci5cbiAgICovXG4gIGdldCBjYW5WaWV3KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnBlcm1pc3Npb25zLmNhblZpZXc7XG4gIH1cblxuICAvKipcbiAgICogV2hldGhlciB0aGUgY3VycmVudCByb2xlcyBtYXkgZGVsZXRlIHRoaXMgcmVjb3JkLlxuICAgKlxuICAgKiBUaGUgbGlicmFyeSBzaGlwcyBubyBkZWxldGUgYWZmb3JkYW5jZSwgc28gdGhpcyBpcyBzdXJmYWNlZCBmb3IgdGhlIGNvbnN1bWVyIHRvIGdhdGVcbiAgICogdGhlaXIgb3duIOKAlCBleHBvc2VkIHJhdGhlciB0aGFuIGRyb3BwZWQsIGJlY2F1c2UgYSBwZXJtaXNzaW9uIHRoZSBzY2hlbWEgZGVjbGFyZXNcbiAgICogc2hvdWxkIGJlIGFuc3dlcmFibGUuXG4gICAqL1xuICBnZXQgY2FuRGVsZXRlKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnBlcm1pc3Npb25zLmNhbkRlbGV0ZTtcbiAgfVxuXG4gIGdldCBjYW5TdWJtaXQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuY2FuVmlldyAmJiB0aGlzLnBlcm1pc3Npb25zLmNhbkVkaXQgJiYgIXRoaXMucmVhZG9ubHk7XG4gIH1cblxuICAvKipcbiAgICogRXJyb3JzIHJhaXNlZCBieSBgdmFsaWRhdGlvbmAgcnVsZXMsIGtleWVkIGJ5IHRoZSB0YXJnZXQgdGhleSBwb2ludCBhdC5cbiAgICpcbiAgICogQSBydWxlIGNhbiBkZWNsYXJlIHRoZSByZWNvcmQgaW52YWxpZCBpbmRlcGVuZGVudGx5IG9mIEFuZ3VsYXIncyB2YWxpZGF0b3JzLCBzbyBmb3JtXG4gICAqIHZhbGlkaXR5IGFsb25lIGlzIG5vdCB0aGUgd2hvbGUgcGljdHVyZS4gYER5bmFtaWNSZWNvcmRGb3JtQ29tcG9uZW50LnNhdmVTZWN0aW9uKClgXG4gICAqIGhhcyBhbHdheXMgaG9ub3VyZWQgdGhlc2U7IGBzdWJtaXQoKWAgZGlkIG5vdCwgd2hpY2ggbWVhbnQgdGhlIHNhbWUgcnVsZSBibG9ja2VkIG9uZVxuICAgKiBzYXZlIHBhdGggYW5kIG1lcmVseSBwYWludGVkIGEgYmFubmVyIG9uIHRoZSBvdGhlciDigJQgYW5kIGFueXRoaW5nIHdpcmluZ1xuICAgKiBgKGZvcm1TdWJtaXQpYCB0byBwZXJzaXN0ZW5jZSB3cm90ZSByZWNvcmRzIHRoZSBydWxlcyBlbmdpbmUgaGFkIGFscmVhZHkgcmVqZWN0ZWQuXG4gICAqL1xuICBnZXQgcnVsZVZhbGlkYXRpb25FcnJvcnMoKTogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB7XG4gICAgcmV0dXJuIHRoaXMucnVsZVJlc3VsdCgpLnZhbGlkYXRpb25FcnJvcnM7XG4gIH1cblxuICAvKiogVHJ1ZSB3aGVuIGEgYHZhbGlkYXRpb25gIHJ1bGUgaXMgY3VycmVudGx5IGJsb2NraW5nIHN1Ym1pc3Npb24uICovXG4gIGdldCBoYXNSdWxlRXJyb3JzKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBPYmplY3Qua2V5cyh0aGlzLnJ1bGVWYWxpZGF0aW9uRXJyb3JzKS5sZW5ndGggPiAwO1xuICB9XG5cbiAgLyoqXG4gICAqIFRydWUgd2hpbGUgYW4gYXN5bmMgdmFsaWRhdG9yIGlzIHN0aWxsIHJ1bm5pbmcuXG4gICAqXG4gICAqIEFuZ3VsYXIgcmVwb3J0cyBhIGNvbnRyb2wgd2l0aCBhbiBvdXRzdGFuZGluZyBhc3luYyBjaGVjayBhcyBgcGVuZGluZ2AsIGFuZCBgaW52YWxpZGAgaXNcbiAgICogZmFsc2UgdW50aWwgaXQgc2V0dGxlcyDigJQgc28gd2l0aG91dCB0aGlzIGEgZm9ybSBjb3VsZCBiZSBzdWJtaXR0ZWQgaW4gdGhlIGdhcCBiZWZvcmUgYVxuICAgKiB1bmlxdWVuZXNzIGNoZWNrIGNhbWUgYmFjay5cbiAgICovXG4gIGdldCBpc1ZhbGlkYXRpbmcoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuZm9ybS5wZW5kaW5nO1xuICB9XG5cbiAgLyoqIFNpbmdsZSBzb3VyY2Ugb2YgdHJ1dGggZm9yIGJvdGggdGhlIHN1Ym1pdCBndWFyZCBhbmQgdGhlIGJ1dHRvbidzIGRpc2FibGVkIHN0YXRlLiAqL1xuICBnZXQgc3VibWl0QmxvY2tlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5mb3JtLmludmFsaWQgfHwgdGhpcy5mb3JtLnBlbmRpbmcgfHwgdGhpcy5oYXNSdWxlRXJyb3JzO1xuICB9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgdGhpcy5zZWxlY3Rpb25TdWIgPSB0aGlzLmVudGl0eVJlZlNlbGVjdGlvbi5zZWxlY3Rpb24kLnN1YnNjcmliZSgoeyBmaWVsZElkLCBvcHRpb24gfSkgPT4ge1xuICAgICAgdGhpcy5ydW5BdXRvUGF0Y2goZmllbGRJZCwgb3B0aW9uPy5yZWNvcmQpO1xuICAgIH0pO1xuICB9XG5cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgIGlmIChjaGFuZ2VzWydjb25maWcnXSB8fCBjaGFuZ2VzWyd1c2VyUm9sZXMnXSkge1xuICAgICAgdGhpcy5wZXJtaXNzaW9uc0NhY2hlID0gbnVsbDtcbiAgICB9XG4gICAgaWYgKGNoYW5nZXNbJ2NvbmZpZyddICYmIHRoaXMucmF3Q29uZmlnKSB7XG4gICAgICAvLyBDb25maWdzIGFycml2ZSBhcyBwbGFpbiBKU09OIGZyb20gc3RvcmFnZSBvciBhbiBBUEksIHdoZXJlIFR5cGVTY3JpcHQgY2Fubm90IGVuZm9yY2VcbiAgICAgIC8vIHRoZSBvcHRpb24gc2hhcGUuIE5vcm1hbGlzZSBoZXJlLCBhdCB0aGUgbGlicmFyeSBib3VuZGFyeSwgc28gZXZlcnl0aGluZyBkb3duc3RyZWFtXG4gICAgICAvLyBjYW4gcmVseSBvbiBhbiBvcHRpb24gYmVpbmcgYSBMb2NhbGl6ZWRUZXh0LiBSZXR1cm5zIHRoZSBzYW1lIG9iamVjdCB3aGVuIGl0IGFscmVhZHlcbiAgICAgIC8vIGlzLCBzbyBhIHdlbGwtZm9ybWVkIGNvbmZpZyBjb3N0cyBub3RoaW5nLlxuICAgICAgLy8gS2VwdCBpbiBhIGZpZWxkIG9mIG91ciBvd24gcmF0aGVyIHRoYW4gd3JpdHRlbiBiYWNrIHRvIHRoZSBASW5wdXQuIFJlYXNzaWduaW5nIGFuXG4gICAgICAvLyBpbnB1dCB3b3JrcywgYnV0IGl0IHN1cnByaXNlcyBhIHBhcmVudCB0aGF0IGhvbGRzIHRoZSBzYW1lIG9iamVjdCDigJQgYW5kIGEgcGFyZW50XG4gICAgICAvLyB1c2luZyBPblB1c2ggb3Igc2lnbmFscyB3b3VsZCBub3Qgc2VlIHRoZSByZXBsYWNlbWVudCBhbnl3YXkuXG4gICAgICB0aGlzLm5vcm1hbGl6ZWRDb25maWcgPSBub3JtYWxpemVDb25maWdPcHRpb25zKHRoaXMuY29uZmlnKTtcbiAgICB9XG4gICAgaWYgKGNoYW5nZXNbJ2NvbmZpZyddIHx8IGNoYW5nZXNbJ2luaXRpYWxEYXRhJ10pIHtcbiAgICAgIHRoaXMuYnVpbGRGb3JtKCk7XG4gICAgICBpZiAodGhpcy52aXNpYmxlVGFicy5sZW5ndGggPiAwICYmICF0aGlzLmFjdGl2ZVRhYigpKSB7XG4gICAgICAgIHRoaXMuc2V0QWN0aXZlVGFiKHRoaXMudmlzaWJsZVRhYnNbMF0uaWQpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMudmFsdWVTdWI/LnVuc3Vic2NyaWJlKCk7XG4gICAgdGhpcy5zdGF0dXNTdWI/LnVuc3Vic2NyaWJlKCk7XG4gICAgdGhpcy5zZWxlY3Rpb25TdWI/LnVuc3Vic2NyaWJlKCk7XG4gIH1cblxuICBzZXRBY3RpdmVUYWIodGFiSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IGNoYW5nZWQgPSB0aGlzLmFjdGl2ZVRhYigpICE9PSB0YWJJZDtcbiAgICBpZiAoY2hhbmdlZCkgdGhpcy5hY3RpdmVUYWJDaGFuZ2UuZW1pdCh0YWJJZCk7XG4gICAgdGhpcy5hY3RpdmVUYWIuc2V0KHRhYklkKTtcbiAgICBpZiAoY2hhbmdlZCkgdGhpcy5mb2N1c0FjdGl2ZVBhbmVsKCk7XG4gICAgY29uc3QgcGFyZW50ID0gZmluZFRhYih0aGlzLnRhYnMsIHRhYklkKTtcbiAgICBpZiAocGFyZW50Py5jaGlsZHJlbj8ubGVuZ3RoKSB7XG4gICAgICB0aGlzLmFjdGl2ZVN1YlRhYi5zZXQocGFyZW50LmNoaWxkcmVuWzBdLmlkKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5hY3RpdmVTdWJUYWIuc2V0KCcnKTtcbiAgICB9XG4gIH1cblxuICBzZXRBY3RpdmVTdWJUYWIoc3ViVGFiSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IGNoYW5nZWQgPSB0aGlzLmFjdGl2ZVN1YlRhYigpICE9PSBzdWJUYWJJZDtcbiAgICB0aGlzLmFjdGl2ZVN1YlRhYi5zZXQoc3ViVGFiSWQpO1xuICAgIGlmIChjaGFuZ2VkKSB0aGlzLmZvY3VzQWN0aXZlUGFuZWwoKTtcbiAgfVxuXG4gIGdldEZpZWxkU3BhbihmaWVsZDogTmVzdGVkRmllbGRDb25maWcpOiBzdHJpbmcge1xuICAgIGNvbnN0IHNwYW4gPSBmaWVsZC5jb2xTcGFuID8/IDEyO1xuICAgIHJldHVybiBgc3BhbiAke01hdGgubWluKDEyLCBNYXRoLm1heCgxLCBzcGFuKSl9YDtcbiAgfVxuXG4gIC8vIOKUgOKUgOKUgCBjcml0aWNhbEZpZWxkIGxvY2tpbmcg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqIEEgY3JpdGljYWwgZmllbGQgc3RheXMgcmVhZC1vbmx5IHVudGlsIHRoZSB1c2VyIGRlbGliZXJhdGVseSB1bmxvY2tzIGl0LiAqL1xuICBpc0ZpZWxkTG9ja2VkKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAhIWZpZWxkLmNyaXRpY2FsRmllbGQgJiYgIXRoaXMudW5sb2NrZWRGaWVsZHMoKS5oYXMoZmllbGQuaWQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhpcyBmaWVsZCByZW5kZXJzIHJlYWQtb25seSwgZnJvbSBhbnkgb2YgdGhlIGZvdXIgcmVhc29uczogdGhlIHdob2xlIGZvcm0sXG4gICAqIHRoZSBmaWVsZCdzIG93biBmbGFnLCBhIGNhbGxlci1zdXBwbGllZCBgcmVhZE9ubHlGaWVsZHNgIGVudHJ5LCBvciBhIGNyaXRpY2FsRmllbGQgbG9jay5cbiAgICovXG4gIGlzRmllbGRSZWFkb25seShmaWVsZDogTmVzdGVkRmllbGRDb25maWcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdGhpcy5yZWFkb25seSB8fFxuICAgICAgISFmaWVsZC5yZWFkb25seSB8fFxuICAgICAgdGhpcy5yZWFkT25seUZpZWxkcy5pbmNsdWRlcyhmaWVsZC5pZCkgfHxcbiAgICAgIHRoaXMuaXNGaWVsZExvY2tlZChmaWVsZClcbiAgICApO1xuICB9XG5cbiAgdG9nZ2xlRmllbGRMb2NrKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IHZvaWQge1xuICAgIGlmICghZmllbGQuY3JpdGljYWxGaWVsZCB8fCB0aGlzLnJlYWRvbmx5KSByZXR1cm47XG4gICAgY29uc3QgbmV4dCA9IG5ldyBTZXQodGhpcy51bmxvY2tlZEZpZWxkcygpKTtcbiAgICBpZiAobmV4dC5oYXMoZmllbGQuaWQpKSBuZXh0LmRlbGV0ZShmaWVsZC5pZCk7XG4gICAgZWxzZSBuZXh0LmFkZChmaWVsZC5pZCk7XG4gICAgdGhpcy51bmxvY2tlZEZpZWxkcy5zZXQobmV4dCk7XG4gIH1cblxuICByZXNvbHZlRmllbGRMYWJlbChmaWVsZDogTmVzdGVkRmllbGRDb25maWcpOiBzdHJpbmcge1xuICAgIHJldHVybiByZXNvbHZlTGFiZWwoZmllbGQubGFiZWwsIHRoaXMubGFuZ3VhZ2UpO1xuICB9XG5cbiAgLy8g4pSA4pSA4pSAIEZvcm0gY29uc3RydWN0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIHByaXZhdGUgYnVpbGRGb3JtKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5jb25maWcpIHJldHVybjtcbiAgICBjb25zdCBncm91cDogUmVjb3JkPHN0cmluZywgQWJzdHJhY3RDb250cm9sPiA9IHt9O1xuXG4gICAgY29uc3QgYnVpbGRUYWJDb250cm9scyA9ICh0YWJzOiBOZXN0ZWRUYWJDb25maWdbXSwgcGFyZW50R3JvdXA6IFJlY29yZDxzdHJpbmcsIEFic3RyYWN0Q29udHJvbD4pID0+IHtcbiAgICAgIGZvciAoY29uc3QgdGFiIG9mIHRhYnMpIHtcbiAgICAgICAgaWYgKHRhYi5mbGF0RGF0YSkge1xuICAgICAgICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGFiLmZpZWxkcyB8fCBbXSkge1xuICAgICAgICAgICAgdGhpcy5idWlsZEZpZWxkQ29udHJvbChmaWVsZCwgcGFyZW50R3JvdXApO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAodGFiLmNoaWxkcmVuKSBidWlsZFRhYkNvbnRyb2xzKHRhYi5jaGlsZHJlbiwgcGFyZW50R3JvdXApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbnN0IHRhYkdyb3VwOiBSZWNvcmQ8c3RyaW5nLCBBYnN0cmFjdENvbnRyb2w+ID0ge307XG4gICAgICAgICAgZm9yIChjb25zdCBmaWVsZCBvZiB0YWIuZmllbGRzIHx8IFtdKSB7XG4gICAgICAgICAgICB0aGlzLmJ1aWxkRmllbGRDb250cm9sKGZpZWxkLCB0YWJHcm91cCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICh0YWIuY2hpbGRyZW4pIGJ1aWxkVGFiQ29udHJvbHModGFiLmNoaWxkcmVuLCB0YWJHcm91cCk7XG4gICAgICAgICAgcGFyZW50R3JvdXBbdGFiLmlkXSA9IHRoaXMuZmIuZ3JvdXAodGFiR3JvdXApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfTtcblxuICAgIGJ1aWxkVGFiQ29udHJvbHModGhpcy5jb25maWcudGFicyB8fCBbXSwgZ3JvdXApO1xuICAgIHRoaXMuZm9ybSA9IHRoaXMuZmIuZ3JvdXAoZ3JvdXApO1xuXG4gICAgaWYgKHRoaXMuaW5pdGlhbERhdGEpIHtcbiAgICAgIHRoaXMucGF0Y2hGb3JtKHRoaXMudXBncmFkZVJlY29yZCh0aGlzLmluaXRpYWxEYXRhKSk7XG4gICAgfVxuXG4gICAgY29uc3QgaW5pdGlhbEZsYXR0ZW5lZCA9IHRoaXMuZmxhdHRlbkZvcm1WYWx1ZXMoKTtcbiAgICB0aGlzLmZvcm1WYWx1ZXMuc2V0KGluaXRpYWxGbGF0dGVuZWQpO1xuICAgIHRoaXMucHJldmlvdXNWYWx1ZXMgPSB7IC4uLmluaXRpYWxGbGF0dGVuZWQgfTtcbiAgICB0aGlzLnNlc3Npb25CYXNlbGluZS5zZXQoeyAuLi5pbml0aWFsRmxhdHRlbmVkIH0pO1xuICAgIHRoaXMudW5sb2NrZWRGaWVsZHMuc2V0KG5ldyBTZXQ8c3RyaW5nPigpKTtcbiAgICB0aGlzLnN5bmNIaWRkZW5GaWVsZFN0YXRlKGluaXRpYWxGbGF0dGVuZWQpO1xuXG4gICAgaWYgKHRoaXMucHJldmlldykge1xuICAgICAgLy8gU2hvdyB3aGF0IGFuIGFycmF5IGZpZWxkIGxvb2tzIGxpa2UgcmF0aGVyIHRoYW4gYW4gZW1wdHkgc2xvdCwgdGhlbiBmcmVlemUuXG4gICAgICBmb3IgKGNvbnN0IGZpZWxkIG9mIHRoaXMuYWxsRmllbGRzKCkpIHtcbiAgICAgICAgaWYgKGZpZWxkLnR5cGUgIT09ICdhcnJheScpIGNvbnRpbnVlO1xuICAgICAgICBjb25zdCBhcnJheSA9IHRoaXMuZ2V0QXJyYXlDb250cm9sKGZpZWxkLmlkKTtcbiAgICAgICAgaWYgKGFycmF5ICYmIGFycmF5Lmxlbmd0aCA9PT0gMCkgYXJyYXkucHVzaCh0aGlzLmJ1aWxkQXJyYXlSb3coZmllbGQsIHVuZGVmaW5lZCkpO1xuICAgICAgfVxuICAgICAgdGhpcy5mb3JtLmRpc2FibGUoeyBlbWl0RXZlbnQ6IGZhbHNlIH0pO1xuICAgIH1cblxuICAgIC8vIFJlYnVpbGRpbmcgbXVzdCBub3Qgc3RhY2sgc3Vic2NyaXB0aW9ucyBvbiBzdWNjZXNzaXZlIGNvbmZpZy9kYXRhIGNoYW5nZXMuXG4gICAgdGhpcy52YWx1ZVN1Yj8udW5zdWJzY3JpYmUoKTtcbiAgICBjb25zdCBjaGFuZ2VzJCA9IHRoaXMuY2hhbmdlRGVib3VuY2VNcyA+IDBcbiAgICAgID8gdGhpcy5mb3JtLnZhbHVlQ2hhbmdlcy5waXBlKGRlYm91bmNlVGltZSh0aGlzLmNoYW5nZURlYm91bmNlTXMpKVxuICAgICAgOiB0aGlzLmZvcm0udmFsdWVDaGFuZ2VzO1xuICAgIC8vIFZhbGlkaXR5IGlzIG5vdCB2YWx1ZTogYW4gYXN5bmMgdmFsaWRhdG9yIHNldHRsaW5nIGZsaXBzIGBwZW5kaW5nYCBhbmQgdGhlbiBgaW52YWxpZGBcbiAgICAvLyB3aXRoIG5vIHZhbHVlIGNoYW5nZSBhbmQgbm8gdGVtcGxhdGUgZXZlbnQsIHNvIHVuZGVyIE9uUHVzaCB0aGUgU2F2ZSBidXR0b24gd291bGRcbiAgICAvLyBuZXZlciByZS1lbmFibGUuIHN1Ym1pdEJsb2NrZWQgcmVhZHMgZm9ybS5wZW5kaW5nIGFuZCBmb3JtLmludmFsaWQsIHNvIHRoaXMgaXMgd2hhdFxuICAgIC8vIGtlZXBzIGl0IGhvbmVzdC5cbiAgICB0aGlzLnN0YXR1c1N1Yj8udW5zdWJzY3JpYmUoKTtcbiAgICB0aGlzLnN0YXR1c1N1YiA9IHRoaXMuZm9ybS5zdGF0dXNDaGFuZ2VzLnN1YnNjcmliZSgoKSA9PiB0aGlzLmNkci5tYXJrRm9yQ2hlY2soKSk7XG5cbiAgICB0aGlzLnZhbHVlU3ViID0gY2hhbmdlcyQuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgIGNvbnN0IGZsYXR0ZW5lZCA9IHRoaXMuZmxhdHRlbkZvcm1WYWx1ZXMoKTtcbiAgICAgIHRoaXMuZm9ybVZhbHVlcy5zZXQoZmxhdHRlbmVkKTtcbiAgICAgIHRoaXMucnVuUGF0Y2hPblRydWUoZmxhdHRlbmVkKTtcbiAgICAgIHRoaXMuc3luY0hpZGRlbkZpZWxkU3RhdGUoZmxhdHRlbmVkKTtcbiAgICAgIHRoaXMucHJldmlvdXNWYWx1ZXMgPSB7IC4uLmZsYXR0ZW5lZCB9O1xuICAgICAgdGhpcy5mb3JtQ2hhbmdlLmVtaXQodGhpcy5leHRyYWN0UmVjb3JkKCkpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBidWlsZEZpZWxkQ29udHJvbChmaWVsZDogTmVzdGVkRmllbGRDb25maWcsIGdyb3VwOiBSZWNvcmQ8c3RyaW5nLCBBYnN0cmFjdENvbnRyb2w+KTogdm9pZCB7XG4gICAgaWYgKGZpZWxkLnR5cGUgPT09ICdncm91cCcpIHtcbiAgICAgIGNvbnN0IHN1Ykdyb3VwOiBSZWNvcmQ8c3RyaW5nLCBBYnN0cmFjdENvbnRyb2w+ID0ge307XG4gICAgICBmb3IgKGNvbnN0IGNoaWxkIG9mIGZpZWxkLmNoaWxkcmVuIHx8IFtdKSB7XG4gICAgICAgIHRoaXMuYnVpbGRGaWVsZENvbnRyb2woY2hpbGQsIHN1Ykdyb3VwKTtcbiAgICAgIH1cbiAgICAgIGdyb3VwW2ZpZWxkLmlkXSA9IHRoaXMuZmIuZ3JvdXAoc3ViR3JvdXApO1xuICAgIH0gZWxzZSBpZiAoZmllbGQudHlwZSA9PT0gJ2FycmF5Jykge1xuICAgICAgZ3JvdXBbZmllbGQuaWRdID0gdGhpcy5mYi5hcnJheShbXSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHZhbGlkYXRvcnMgPSB0aGlzLnZhbGlkYXRvclJlZ2lzdHJ5LnJlc29sdmVGcm9tQ29uZmlnKGZpZWxkLnZhbGlkYXRvcnMpO1xuICAgICAgY29uc3QgYXN5bmNWYWxpZGF0b3JzID0gdGhpcy52YWxpZGF0b3JSZWdpc3RyeS5yZXNvbHZlQXN5bmNGcm9tQ29uZmlnKGZpZWxkLnZhbGlkYXRvcnMpO1xuICAgICAgZ3JvdXBbZmllbGQuaWRdID0gdGhpcy5mYi5jb250cm9sKFxuICAgICAgICB7IHZhbHVlOiBmaWVsZC5kZWZhdWx0VmFsdWUgPz8gbnVsbCwgZGlzYWJsZWQ6IGZpZWxkLmRpc2FibGVkID8/IGZhbHNlIH0sXG4gICAgICAgIHZhbGlkYXRvcnMsXG4gICAgICAgIGFzeW5jVmFsaWRhdG9ycyxcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLyoqIE9uZSByb3cgb2YgYW4gYGFycmF5YCBmaWVsZDogYSBGb3JtR3JvdXAgd2hlbiB0aGUgZmllbGQgZGVjbGFyZXMgY29sdW1ucywgZWxzZSBhIGJhcmUgY29udHJvbC4gKi9cbiAgLyoqXG4gICAqIFRoZSBgRm9ybUFycmF5YCBiZWhpbmQgYW4gYGFycmF5YCBmaWVsZCwgZm9yIGNhbGxlcnMgdGhhdCBtYW5hZ2Ugcm93cyB0aGVtc2VsdmVzIOKAlFxuICAgKiB0aGUgcmVjb3JkIGVkaXRvcidzIGlubGluZSByb3cgZHJhd2VyLlxuICAgKi9cbiAgZ2V0QXJyYXlDb250cm9sKGZpZWxkSWQ6IHN0cmluZywgdGFiSWQ/OiBzdHJpbmcpOiBGb3JtQXJyYXkgfCBudWxsIHtcbiAgICBjb25zdCBjdHJsID0gdGhpcy5nZXRDb250cm9sKGZpZWxkSWQsIHRhYklkKTtcbiAgICByZXR1cm4gY3RybCBpbnN0YW5jZW9mIEZvcm1BcnJheSA/IGN0cmwgOiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkIG9uZSBkZXRhY2hlZCByb3cgZm9yIGFuIGBhcnJheWAgZmllbGQsIHdpdGggdGhlIGZpZWxkJ3Mgb3duIHZhbGlkYXRvcnMgYXBwbGllZC5cbiAgICogRXhwb3NlZCBzbyBhIHJvdyBjYW4gYmUgZWRpdGVkIG91dHNpZGUgdGhlIGZvcm0gYW5kIHB1c2hlZCBvbiBzYXZlLlxuICAgKi9cbiAgY3JlYXRlQXJyYXlSb3coZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnLCB2YWx1ZT86IHVua25vd24pOiBBYnN0cmFjdENvbnRyb2wge1xuICAgIHJldHVybiB0aGlzLmJ1aWxkQXJyYXlSb3coZmllbGQsIHZhbHVlKTtcbiAgfVxuXG4gIHByaXZhdGUgYnVpbGRBcnJheVJvdyhmaWVsZDogTmVzdGVkRmllbGRDb25maWcgfCB1bmRlZmluZWQsIGl0ZW06IHVua25vd24pOiBBYnN0cmFjdENvbnRyb2wge1xuICAgIGlmICghZmllbGQ/LmNoaWxkcmVuPy5sZW5ndGgpIHJldHVybiB0aGlzLmZiLmNvbnRyb2woaXRlbSk7XG5cbiAgICBjb25zdCByb3dHcm91cDogUmVjb3JkPHN0cmluZywgQWJzdHJhY3RDb250cm9sPiA9IHt9O1xuICAgIGZvciAoY29uc3QgY2hpbGQgb2YgZmllbGQuY2hpbGRyZW4pIHtcbiAgICAgIHRoaXMuYnVpbGRGaWVsZENvbnRyb2woY2hpbGQsIHJvd0dyb3VwKTtcbiAgICB9XG4gICAgY29uc3QgZ3JvdXAgPSB0aGlzLmZiLmdyb3VwKHJvd0dyb3VwKTtcbiAgICBpZiAoaXRlbSAmJiB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcpIGdyb3VwLnBhdGNoVmFsdWUoaXRlbSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik7XG4gICAgcmV0dXJuIGdyb3VwO1xuICB9XG5cbiAgLyoqXG4gICAqIE1hcmsgZXZlcnkgY29udHJvbCB0b3VjaGVkLCB0aGVuIHJlLWNoZWNrIHRoZSBmaWVsZCBjb21wb25lbnRzLlxuICAgKlxuICAgKiBUaGV5IGFyZSBPblB1c2ggYW5kIGB0b3VjaGVkYCBpcyBmbGlwcGVkIGZyb20gb3V0c2lkZSB0aGVpciBvd24gdGVtcGxhdGUsIHNvIHRoZSBzZWNvbmRcbiAgICogaGFsZiBpcyB3aGF0IG1ha2VzIHRoZSBlcnJvciBtZXNzYWdlcyBhY3R1YWxseSBhcHBlYXIuXG4gICAqL1xuICBwcml2YXRlIG1hcmtBbGxUb3VjaGVkKCk6IHZvaWQge1xuICAgIHRoaXMuZm9ybS5tYXJrQWxsQXNUb3VjaGVkKCk7XG4gICAgdGhpcy5maWVsZEhvc3RzPy5mb3JFYWNoKGhvc3QgPT4gaG9zdC5yZWZyZXNoKCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEJyaW5nIGEgcmVjb3JkIHVwIHRvIHRoZSBjb25maWcncyBgdmVyc2lvbmAgYmVmb3JlIGl0IHJlYWNoZXMgdGhlIGZvcm0uXG4gICAqXG4gICAqIERvbmUgaGVyZSBiZWNhdXNlIHRoaXMgaXMgdGhlIG9uZSBwbGFjZSBhIHJlY29yZCBlbnRlcnMgdGhlIHJlbmRlcmVyLCBzbyBhIGNvbnN1bWVyXG4gICAqIGNhbm5vdCBmb3JnZXQgaXQuIE1pZ3JhdGlvbiBpcyBhIG5vLW9wIHVubGVzcyB0aGUgY29uc3VtZXIgcmVnaXN0ZXJlZCBzdGVwcyAqYW5kKiB0aGVcbiAgICogcmVjb3JkIGlzIGJlaGluZCDigJQgYW4gdW5zdGFtcGVkIHJlY29yZCBpcyBkZWxpYmVyYXRlbHkgbGVmdCBhbG9uZSAoc2VlIGBtaWdyYXRlUmVjb3JkYCkuXG4gICAqXG4gICAqIEEgbWlzc2luZyBvciBicm9rZW4gc3RlcCB0aHJvd3MgZnJvbSBjb3JlLiBUaGF0IGlzIG5vdCBzd2FsbG93ZWQ6IHJlbmRlcmluZyBhXG4gICAqIGhhbGYtdW5kZXJzdG9vZCByZWNvcmQgd291bGQgd3JpdGUgaXQgYmFjayBpbiBhIHNoYXBlIG1hdGNoaW5nIG5laXRoZXIgc2NoZW1hLlxuICAgKi9cbiAgcHJpdmF0ZSB1cGdyYWRlUmVjb3JkKGRhdGE6IFJlY29yZDxzdHJpbmcsIGFueT4pOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IHtcbiAgICBpZiAoIXRoaXMubWlncmF0aW9ucy5sZW5ndGggfHwgIXRoaXMuY29uZmlnKSByZXR1cm4gZGF0YTtcblxuICAgIGNvbnN0IHJlc3VsdCA9IG1pZ3JhdGVSZWNvcmQoZGF0YSwgdGhpcy5jb25maWcsIHRoaXMubWlncmF0aW9ucyk7XG4gICAgaWYgKHJlc3VsdC5hcHBsaWVkLmxlbmd0aCAmJiBpc0Rldk1vZGUoKSkge1xuICAgICAgY29uc29sZS5pbmZvKFxuICAgICAgICBgW25neC1keW5hbWljLWVudGl0eV0gTWlncmF0ZWQgYSBcIiR7dGhpcy5jb25maWcuZW50aXR5fVwiIHJlY29yZCBmcm9tIGNvbmZpZyB2ZXJzaW9uIGAgK1xuICAgICAgICAgIGAke3Jlc3VsdC5mcm9tfSB0byAke3Jlc3VsdC50b30gKHN0ZXBzOiAke3Jlc3VsdC5hcHBsaWVkLmpvaW4oJywgJyl9KS5gLFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdC5yZWNvcmQ7XG4gIH1cblxuICBwcml2YXRlIHBhdGNoRm9ybShkYXRhOiBSZWNvcmQ8c3RyaW5nLCBhbnk+KTogdm9pZCB7XG4gICAgaWYgKCFkYXRhIHx8ICF0aGlzLmZvcm0pIHJldHVybjtcbiAgICBjb25zdCBmaWVsZHNCeUlkID0gbmV3IE1hcCh0aGlzLmFsbEZpZWxkcygpLm1hcChmID0+IFtmLmlkLCBmXSkpO1xuICAgIGNvbnN0IHBhdGNoZWRGaWVsZElkcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuXG4gICAgY29uc3Qgd2Fsa1RhYnMgPSAodGFiczogTmVzdGVkVGFiQ29uZmlnW10pID0+IHtcbiAgICAgIGZvciAoY29uc3QgdGFiIG9mIHRhYnMpIHtcbiAgICAgICAgY29uc3QgdGFiRGF0YSA9IGdldFRhYkRhdGEodGFiLmlkLCBkYXRhLCB0aGlzLmNvbmZpZyk7XG4gICAgICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGFiLmZpZWxkcyB8fCBbXSkge1xuICAgICAgICAgIGxldCB2YWwgPSB0YWJEYXRhICYmIHR5cGVvZiB0YWJEYXRhID09PSAnb2JqZWN0JyA/IHRhYkRhdGFbZmllbGQuaWRdIDogdW5kZWZpbmVkO1xuICAgICAgICAgIGlmIChmaWVsZC5yZWZlcmVyRmllbGQpIHtcbiAgICAgICAgICAgIGNvbnN0IHJlZlZhbCA9IGdldFZhbHVlQnlQYXRoKGRhdGEsIGZpZWxkLnJlZmVyZXJGaWVsZCk7XG4gICAgICAgICAgICBpZiAocmVmVmFsICE9PSB1bmRlZmluZWQpIHZhbCA9IHJlZlZhbDtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHZhbCA9PT0gdW5kZWZpbmVkKSBjb250aW51ZTtcblxuICAgICAgICAgIGNvbnN0IGN0cmwgPSB0aGlzLmdldENvbnRyb2woZmllbGQuaWQsIHRhYi5pZCk7XG4gICAgICAgICAgaWYgKCFjdHJsKSBjb250aW51ZTtcblxuICAgICAgICAgIGlmIChjdHJsIGluc3RhbmNlb2YgRm9ybUFycmF5ICYmIEFycmF5LmlzQXJyYXkodmFsKSkge1xuICAgICAgICAgICAgY3RybC5jbGVhcigpO1xuICAgICAgICAgICAgZm9yIChjb25zdCBpdGVtIG9mIHZhbCkge1xuICAgICAgICAgICAgICBjdHJsLnB1c2godGhpcy5idWlsZEFycmF5Um93KGZpZWxkc0J5SWQuZ2V0KGZpZWxkLmlkKSwgaXRlbSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjdHJsLnBhdGNoVmFsdWUodmFsLCB7IGVtaXRFdmVudDogZmFsc2UgfSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHBhdGNoZWRGaWVsZElkcy5hZGQoZmllbGQuaWQpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0YWIuY2hpbGRyZW4pIHdhbGtUYWJzKHRhYi5jaGlsZHJlbik7XG4gICAgICB9XG4gICAgfTtcblxuICAgIHdhbGtUYWJzKHRoaXMuY29uZmlnLnRhYnMgfHwgW10pO1xuICAgIHRoaXMud2FyblVuY29uc3VtZWRJbml0aWFsRGF0YShkYXRhLCBmaWVsZHNCeUlkLCBwYXRjaGVkRmllbGRJZHMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEEgcmVjb3JkIGlzIG5lc3RlZCBieSB0YWIgaWQgKGB7IHRhYklkOiB7IGZpZWxkSWQ6IHZhbHVlIH0gfWApIHVubGVzcyB0aGUgdGFiIHNldHNcbiAgICogYGZsYXREYXRhOiB0cnVlYC4gSGFuZGluZyBhIGZsYXQgcmVjb3JkIHRvIGEgbmVzdGVkIHRhYiBmaW5kcyBub3RoaW5nLCBzbyB0aGUgZmllbGRzXG4gICAqIHN0YXkgZW1wdHkg4oCUIHdpdGggbm8gZXJyb3IgYW5kIG5vIGNsdWUuIFRoYXQgc2lsZW50IG1pc3MgaXMgdGhlIG1vc3QgZXhwZW5zaXZlIHdheSB0b1xuICAgKiBsb3NlIGFuIGhvdXIgd2l0aCB0aGlzIGxpYnJhcnksIHNvIG5hbWUgaXQgaW4gZGV2IGJ1aWxkcy5cbiAgICpcbiAgICogT25seSB0b3AtbGV2ZWwga2V5cyB0aGF0IG1hdGNoIGEga25vd24gZmllbGQgaWQgYXJlIHJlcG9ydGVkOiBhbnl0aGluZyBlbHNlIGlzIGFzc3VtZWRcbiAgICogdG8gYmUgdGhlIGNvbnN1bWVyJ3Mgb3duIHJlY29yZCBtZXRhZGF0YSAoaWRzLCB0aW1lc3RhbXBzLCBgX2NvbmZpZ1ZlcnNpb25gKSBhbmQgaXMgbm90XG4gICAqIG91ciBidXNpbmVzcy5cbiAgICovXG4gIHByaXZhdGUgd2FyblVuY29uc3VtZWRJbml0aWFsRGF0YShcbiAgICBkYXRhOiBSZWNvcmQ8c3RyaW5nLCBhbnk+LFxuICAgIGZpZWxkc0J5SWQ6IE1hcDxzdHJpbmcsIE5lc3RlZEZpZWxkQ29uZmlnPixcbiAgICBwYXRjaGVkRmllbGRJZHM6IFNldDxzdHJpbmc+LFxuICApOiB2b2lkIHtcbiAgICBpZiAoIWlzRGV2TW9kZSgpIHx8ICF0aGlzLmNvbmZpZykgcmV0dXJuO1xuXG4gICAgY29uc3QgdGFiSWRzID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gICAgY29uc3QgY29sbGVjdFRhYklkcyA9ICh0YWJzOiBOZXN0ZWRUYWJDb25maWdbXSkgPT4ge1xuICAgICAgZm9yIChjb25zdCB0YWIgb2YgdGFicykge1xuICAgICAgICB0YWJJZHMuYWRkKHRhYi5pZCk7XG4gICAgICAgIGlmICh0YWIuY2hpbGRyZW4pIGNvbGxlY3RUYWJJZHModGFiLmNoaWxkcmVuKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIGNvbGxlY3RUYWJJZHModGhpcy5jb25maWcudGFicyB8fCBbXSk7XG5cbiAgICBjb25zdCB1bmNvbnN1bWVkID0gT2JqZWN0LmtleXMoZGF0YSkuZmlsdGVyKFxuICAgICAga2V5ID0+XG4gICAgICAgICF0YWJJZHMuaGFzKGtleSkgJiYgLy8gYSB0YWIgaWQgYXQgdGhlIHJvb3QgaXMgdGhlIG5lc3RlZCBjb250YWluZXIsIG5vdCBhIHN0cmF5IGZpZWxkXG4gICAgICAgIGRhdGFba2V5XSAhPT0gdW5kZWZpbmVkICYmXG4gICAgICAgIGZpZWxkc0J5SWQuaGFzKGtleSkgJiZcbiAgICAgICAgIXBhdGNoZWRGaWVsZElkcy5oYXMoa2V5KSAmJlxuICAgICAgICAhdGhpcy53YXJuZWRVbmNvbnN1bWVkS2V5cy5oYXMoa2V5KSxcbiAgICApO1xuICAgIGlmICghdW5jb25zdW1lZC5sZW5ndGgpIHJldHVybjtcblxuICAgIGZvciAoY29uc3Qga2V5IG9mIHVuY29uc3VtZWQpIHRoaXMud2FybmVkVW5jb25zdW1lZEtleXMuYWRkKGtleSk7XG5cbiAgICBjb25zdCBzaG93biA9IHVuY29uc3VtZWQuc2xpY2UoMCwgNSkuam9pbignLCAnKTtcbiAgICBjb25zdCBtb3JlID0gdW5jb25zdW1lZC5sZW5ndGggPiA1ID8gYCAoKyR7dW5jb25zdW1lZC5sZW5ndGggLSA1fSBtb3JlKWAgOiAnJztcbiAgICBjb25zdCBwbHVyYWwgPSB1bmNvbnN1bWVkLmxlbmd0aCA9PT0gMTtcbiAgICBjb25zb2xlLndhcm4oXG4gICAgICBgW25neC1keW5hbWljLWVudGl0eV0gaW5pdGlhbERhdGEgaGFzIHRvcC1sZXZlbCAke3BsdXJhbCA/ICdrZXknIDogJ2tleXMnfSBtYXRjaGluZyBgICtcbiAgICAgICAgYCR7cGx1cmFsID8gJ2EgZmllbGQgdGhhdCB3YXMnIDogJ2ZpZWxkcyB0aGF0IHdlcmUnfSBub3QgcG9wdWxhdGVkOiAke3Nob3dufSR7bW9yZX0uIGAgK1xuICAgICAgICBgQSByZWNvcmQgaXMgbmVzdGVkIGJ5IHRhYiBpZCAoeyB0YWJJZDogeyBmaWVsZElkOiB2YWx1ZSB9IH0pIHVubGVzcyB0aGUgdGFiIHNldHMgYCArXG4gICAgICAgIGBmbGF0RGF0YTogdHJ1ZS4gRWl0aGVyIG5lc3QgdGhlIHZhbHVlIHVuZGVyIGl0cyB0YWIgaWQsIG9yIHNldCBmbGF0RGF0YTogdHJ1ZSBvbiBgICtcbiAgICAgICAgYHRoZSB0YWIgaG9sZGluZyAke3BsdXJhbCA/ICd0aGF0IGZpZWxkJyA6ICd0aG9zZSBmaWVsZHMnfS5gLFxuICAgICk7XG4gIH1cblxuICAvKiogV2FybiBvbmNlIHBlciBrZXkgcGVyIGZvcm0gaW5zdGFuY2Ug4oCUIHBhdGNoRm9ybSByZS1ydW5zIG9uIGV2ZXJ5IGluaXRpYWxEYXRhIGNoYW5nZS4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSB3YXJuZWRVbmNvbnN1bWVkS2V5cyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuXG4gIC8qKiBBc3NlbWJsZSBmdWxsIG5lc3RlZCByZWNvcmQgZnJvbSBwZXItdGFiIEZvcm1Hcm91cHMsIHJlc3BlY3RpbmcgZmxhdERhdGEsIHJlZmVyZXJGaWVsZCAmIGFycmF5cy4gKi9cbiAgZXh0cmFjdFJlY29yZCgpOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IHtcbiAgICBpZiAoIXRoaXMuZm9ybSB8fCAhdGhpcy5jb25maWcpIHJldHVybiB7fTtcbiAgICBsZXQgcmVjb3JkOiBSZWNvcmQ8c3RyaW5nLCBhbnk+ID0ge307XG5cbiAgICBjb25zdCB3YWxrVGFicyA9ICh0YWJzOiBOZXN0ZWRUYWJDb25maWdbXSkgPT4ge1xuICAgICAgZm9yIChjb25zdCB0YWIgb2YgdGFicykge1xuICAgICAgICBjb25zdCBmaWVsZFZhbEJhZzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcbiAgICAgICAgZm9yIChjb25zdCBmaWVsZCBvZiB0YWIuZmllbGRzIHx8IFtdKSB7XG4gICAgICAgICAgY29uc3QgY3RybCA9IHRoaXMuZ2V0Q29udHJvbChmaWVsZC5pZCwgdGFiLmlkKTtcbiAgICAgICAgICBpZiAoY3RybCkge1xuICAgICAgICAgICAgZmllbGRWYWxCYWdbZmllbGQuaWRdID0gY3RybC52YWx1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBzZXRUYWJEYXRhKHJlY29yZCwgdGFiLmlkLCBmaWVsZFZhbEJhZywgdGhpcy5jb25maWcpO1xuXG4gICAgICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGFiLmZpZWxkcyB8fCBbXSkge1xuICAgICAgICAgIGlmIChmaWVsZC5yZWZlcmVyRmllbGQpIHtcbiAgICAgICAgICAgIGNvbnN0IGN0cmwgPSB0aGlzLmdldENvbnRyb2woZmllbGQuaWQsIHRhYi5pZCk7XG4gICAgICAgICAgICBpZiAoY3RybCkge1xuICAgICAgICAgICAgICBzZXRWYWx1ZUJ5UGF0aChyZWNvcmQsIGZpZWxkLnJlZmVyZXJGaWVsZCwgY3RybC52YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRhYi5jaGlsZHJlbikgd2Fsa1RhYnModGFiLmNoaWxkcmVuKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgd2Fsa1RhYnModGhpcy5jb25maWcudGFicyB8fCBbXSk7XG4gICAgcmVjb3JkID0gbm9ybWFsaXplQXJyYXlTdHJ1Y3R1cmVzKHJlY29yZCwgdGhpcy5jb25maWcpO1xuICAgIHJldHVybiByZWNvcmQ7XG4gIH1cblxuICBwcml2YXRlIGZsYXR0ZW5Gb3JtVmFsdWVzKCk6IFJlY29yZDxzdHJpbmcsIGFueT4ge1xuICAgIGNvbnN0IG91dDogUmVjb3JkPHN0cmluZywgYW55PiA9IHt9O1xuICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGhpcy5hbGxGaWVsZHMoKSkge1xuICAgICAgY29uc3QgY3RybCA9IHRoaXMuZ2V0Q29udHJvbChmaWVsZC5pZCk7XG4gICAgICBpZiAoY3RybCkgb3V0W2ZpZWxkLmlkXSA9IGN0cmwudmFsdWU7XG4gICAgfVxuICAgIHJldHVybiBvdXQ7XG4gIH1cblxuICBwcml2YXRlIGdldFRhYkdyb3VwKHRhYklkOiBzdHJpbmcpOiBGb3JtR3JvdXAgfCBudWxsIHtcbiAgICBjb25zdCBwYXRoID0gZ2V0VGFiUGF0aCh0aGlzLmNvbmZpZz8udGFicywgdGFiSWQpO1xuICAgIGlmICghcGF0aCB8fCBwYXRoLmxlbmd0aCA9PT0gMCkgcmV0dXJuIHRoaXMuZm9ybTtcbiAgICBsZXQgY3VycjogQWJzdHJhY3RDb250cm9sIHwgbnVsbCA9IHRoaXMuZm9ybTtcbiAgICBmb3IgKGNvbnN0IHAgb2YgcGF0aCkge1xuICAgICAgaWYgKCFjdXJyIHx8ICEoY3VyciBpbnN0YW5jZW9mIEZvcm1Hcm91cCkpIHJldHVybiBudWxsO1xuICAgICAgY3VyciA9IGN1cnIuZ2V0KHApO1xuICAgIH1cbiAgICByZXR1cm4gY3VyciBpbnN0YW5jZW9mIEZvcm1Hcm91cCA/IGN1cnIgOiBudWxsO1xuICB9XG5cbiAgLy8g4pSA4pSA4pSAIGF1dG9QYXRjaCAvIHBhdGNoT25UcnVlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKlxuICAgKiBgYXV0b1BhdGNoYCDigJQgY29weSBtYXBwZWQgZmllbGRzIG91dCBvZiB0aGUgcmVjb3JkIHRoZSB1c2VyIGp1c3Qgc2VsZWN0ZWQgaW4gYW5cbiAgICogZW50aXR5LXJlZiBmaWVsZCBpbnRvIHRoZSBjb25maWd1cmVkIHRhcmdldCB0YWIncyBjb250cm9scy5cbiAgICovXG4gIHByaXZhdGUgcnVuQXV0b1BhdGNoKGZpZWxkSWQ6IHN0cmluZywgcmVjb3JkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCk6IHZvaWQge1xuICAgIGlmICghcmVjb3JkKSByZXR1cm47XG4gICAgY29uc3QgZmllbGQgPSB0aGlzLmFsbEZpZWxkcygpLmZpbmQoZiA9PiBmLmlkID09PSBmaWVsZElkKTtcbiAgICBjb25zdCBhdXRvUGF0Y2ggPSBmaWVsZD8uYXV0b1BhdGNoO1xuICAgIGlmICghYXV0b1BhdGNoKSByZXR1cm47XG5cbiAgICBjb25zdCBwYXRjaCA9IGFwcGx5QXV0b1BhdGNoKGF1dG9QYXRjaCwgcmVjb3JkKTtcbiAgICBmb3IgKGNvbnN0IFt0YXJnZXRJZCwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHBhdGNoKSkge1xuICAgICAgY29uc3QgY29udHJvbCA9IHRoaXMucmVzb2x2ZVRhcmdldENvbnRyb2woYXV0b1BhdGNoLCB0YXJnZXRJZCk7XG4gICAgICBjb250cm9sPy5wYXRjaFZhbHVlKHZhbHVlKTtcbiAgICB9XG4gICAgLy8gUmVhZG9ubHkgdGFyZ2V0cyBpbnRlcnBvbGF0ZSBgY29udHJvbC52YWx1ZWAgdW5kZXIgT25QdXNoLiBgZGV0ZWN0Q2hhbmdlc2Agb24gdGhlXG4gICAgLy8gaG9zdGVkIGNvbXBvbmVudCBmbHVzaGVzIHRoYXQgd3JpdGUgaW1tZWRpYXRlbHkg4oCUIGBtYXJrRm9yQ2hlY2tgIHdhaXRzIGZvciBhIGxhdGVyXG4gICAgLy8gdGljayB0aGF0IGEgUGxheXdyaWdodCBzZWxlY3Rpb24gbmV2ZXIgc2NoZWR1bGVzLlxuICAgIHRoaXMuZmllbGRIb3N0cz8uZm9yRWFjaChob3N0ID0+IGhvc3QucmVmcmVzaCgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBgcGF0Y2hPblRydWVgIOKAlCB3aGVuIGEgYm9vbGVhbi9jaGVja2JveCBmaWVsZCB0cmFuc2l0aW9ucyB0byBgdHJ1ZWAsXG4gICAqIGNvcHkgYGZyb21gIOKGkiBgdG9gIHdpdGhpbiB0aGUgY3VycmVudCByZWNvcmQuXG4gICAqL1xuICBwcml2YXRlIHJ1blBhdGNoT25UcnVlKHZhbHVlczogUmVjb3JkPHN0cmluZywgYW55Pik6IHZvaWQge1xuICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGhpcy5hbGxGaWVsZHMoKSkge1xuICAgICAgY29uc3QgbWFwcGluZ3MgPSBmaWVsZC5wYXRjaE9uVHJ1ZTtcbiAgICAgIGlmICghbWFwcGluZ3M/Lmxlbmd0aCkgY29udGludWU7XG5cbiAgICAgIGNvbnN0IHdhc1RydWUgPSB0aGlzLnByZXZpb3VzVmFsdWVzW2ZpZWxkLmlkXSA9PT0gdHJ1ZTtcbiAgICAgIGNvbnN0IGlzVHJ1ZSA9IHZhbHVlc1tmaWVsZC5pZF0gPT09IHRydWU7XG4gICAgICBpZiAoIWlzVHJ1ZSB8fCB3YXNUcnVlKSBjb250aW51ZTtcblxuICAgICAgY29uc3QgcGF0Y2ggPSBhcHBseVBhdGNoT25UcnVlKG1hcHBpbmdzLCB2YWx1ZXMpO1xuICAgICAgZm9yIChjb25zdCBbdGFyZ2V0SWQsIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwYXRjaCkpIHtcbiAgICAgICAgdGhpcy5nZXRDb250cm9sKHRhcmdldElkKT8ucGF0Y2hWYWx1ZSh2YWx1ZSwgeyBlbWl0RXZlbnQ6IGZhbHNlIH0pO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBQcmVmZXIgYSBjb250cm9sIGRlY2xhcmVkIG9uIHRoZSBjb25maWd1cmVkIHRhcmdldCB0YWI7IGZhbGwgYmFjayB0byBhIHRvcC1sZXZlbCBjb250cm9sLiAqL1xuICBwcml2YXRlIHJlc29sdmVUYXJnZXRDb250cm9sKGF1dG9QYXRjaDogQXV0b1BhdGNoQ29uZmlnLCB0YXJnZXRJZDogc3RyaW5nKTogQWJzdHJhY3RDb250cm9sIHwgbnVsbCB7XG4gICAgY29uc3QgdGFiID0gZmluZFRhYih0aGlzLnRhYnMsIGF1dG9QYXRjaC50YXJnZXRUYWIpO1xuICAgIGNvbnN0IGluVGFyZ2V0VGFiID0gKHRhYj8uZmllbGRzID8/IFtdKS5zb21lKGYgPT4gZi5pZCA9PT0gdGFyZ2V0SWQpO1xuICAgIGlmICh0YWIgJiYgIWluVGFyZ2V0VGFiKSByZXR1cm4gbnVsbDtcbiAgICByZXR1cm4gdGhpcy5nZXRDb250cm9sKHRhcmdldElkLCBhdXRvUGF0Y2gudGFyZ2V0VGFiKTtcbiAgfVxuXG4gIC8vIOKUgOKUgOKUgCBIZWxwZXJzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKlxuICAgKiBUaGUgZmllbGRzIHRoZSByZW5kZXIgZmlsdGVyIG9wZXJhdGVzIG9uIOKAlCBhIHRhYidzIG93biBmaWVsZHMsIGF0IGV2ZXJ5IHRhYiBkZXB0aC5cbiAgICogRGVsaWJlcmF0ZWx5IG5vdCBgYWxsRmllbGRzKClgOiBgZ3JvdXBgIGFuZCBgYXJyYXlgIGNoaWxkcmVuIGFyZSBzaG93biBvciBoaWRkZW4gd2l0aFxuICAgKiB0aGVpciBwYXJlbnQsIGFuZCB0b2dnbGluZyBhIGNoaWxkJ3Mgc3RhdGUgaW5kZXBlbmRlbnRseSB3b3VsZCBmaWdodCB0aGUgcGFyZW50J3MuXG4gICAqL1xuICBwcml2YXRlIHRhYkZpZWxkcygpOiBOZXN0ZWRGaWVsZENvbmZpZ1tdIHtcbiAgICBjb25zdCBvdXQ6IE5lc3RlZEZpZWxkQ29uZmlnW10gPSBbXTtcbiAgICBjb25zdCB3YWxrID0gKHRhYnM6IE5lc3RlZFRhYkNvbmZpZ1tdIHwgdW5kZWZpbmVkKSA9PiB7XG4gICAgICBmb3IgKGNvbnN0IHRhYiBvZiB0YWJzID8/IFtdKSB7XG4gICAgICAgIG91dC5wdXNoKC4uLih0YWIuZmllbGRzID8/IFtdKSk7XG4gICAgICAgIHdhbGsodGFiLmNoaWxkcmVuKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHdhbGsodGhpcy5jb25maWc/LnRhYnMpO1xuICAgIHJldHVybiBvdXQ7XG4gIH1cblxuICAvKipcbiAgICogS2VlcCBhIGhpZGRlbiBmaWVsZCdzIGNvbnRyb2wgb3V0IG9mIGZvcm0gdmFsaWRpdHkuXG4gICAqXG4gICAqIEEgZmllbGQgaGlkZGVuIGJ5IGEgcnVsZSBvciBhIGBzaG93V2hlbmAgY29uZGl0aW9uIGlzIGZpbHRlcmVkIG91dCBvZiB0aGUgcmVuZGVyLCBidXRcbiAgICogaXRzIHZhbGlkYXRvcnMgc3RheSBhdHRhY2hlZCDigJQgc28gYSBoaWRkZW4gKnJlcXVpcmVkKiBmaWVsZCBob2xkcyBgZm9ybS5pbnZhbGlkYCB0cnVlXG4gICAqIGZvcmV2ZXIsIHBlcm1hbmVudGx5IGRpc2FibGluZyBTYXZlIHdpdGggbm90aGluZyBvbiBzY3JlZW4gdG8gZXhwbGFpbiB3aHkuXG4gICAqXG4gICAqIERpc2FibGluZyBpcyB0aGUgZml4IHJhdGhlciB0aGFuIHN0cmlwcGluZyB2YWxpZGF0b3JzOiBBbmd1bGFyIGV4Y2x1ZGVzIGRpc2FibGVkXG4gICAqIGNvbnRyb2xzIGZyb20gdmFsaWRpdHksIGFuZCB0aGUgZmllbGQncyBvd24gdmFsaWRhdG9ycyBzdXJ2aXZlIGludGFjdCBmb3Igd2hlbiBpdFxuICAgKiBjb21lcyBiYWNrLiBWYWx1ZXMgYXJlIHVudG91Y2hlZCDigJQgYGZsYXR0ZW5Gb3JtVmFsdWVzYCBhbmQgYGV4dHJhY3RSZWNvcmRgIGJvdGggcmVhZFxuICAgKiBgY29udHJvbC52YWx1ZWAsIHdoaWNoIGEgZGlzYWJsZWQgY29udHJvbCBzdGlsbCBjYXJyaWVzLCBzbyBydWxlIGV2YWx1YXRpb24gYW5kIHRoZVxuICAgKiBlbWl0dGVkIHJlY29yZCBzZWUgZXhhY3RseSB3aGF0IHRoZXkgc2F3IGJlZm9yZS4gVGhhdCBhbHNvIGtlZXBzIHRoaXMgZnJvbSBmZWVkaW5nXG4gICAqIGJhY2sgb24gaXRzZWxmOiBoaWRpbmcgYSBmaWVsZCBjYW5ub3QgY2hhbmdlIHRoZSB2YWx1ZXMgdGhlIHJ1bGVzIGFyZSBldmFsdWF0ZWQgb3Zlci5cbiAgICpcbiAgICogVGhlIHZpc2liaWxpdHkgcHJlZGljYXRlIGlzIHRoZSBzYW1lIG9uZSBgZmllbGRzRm9yQWN0aXZlVGFiYCBmaWx0ZXJzIHdpdGgsIHNvIHdoYXQgaXNcbiAgICogcmVuZGVyZWQgYW5kIHdoYXQgY291bnRzIHRvd2FyZCB2YWxpZGl0eSBjYW5ub3QgZHJpZnQgYXBhcnQuXG4gICAqL1xuICBwcml2YXRlIHN5bmNIaWRkZW5GaWVsZFN0YXRlKHZhbHVlczogUmVjb3JkPHN0cmluZywgYW55Pik6IHZvaWQge1xuICAgIGlmICh0aGlzLnByZXZpZXcpIHJldHVybjsgLy8gcHJldmlldyBmcmVlemVzIHRoZSB3aG9sZSBmb3JtIG9uIHB1cnBvc2VcblxuICAgIGNvbnN0IGhpZGRlbkJ5UnVsZSA9IG5ldyBTZXQodGhpcy5ydWxlUmVzdWx0KCkuaGlkZGVuRmllbGRzKTtcblxuICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGhpcy50YWJGaWVsZHMoKSkge1xuICAgICAgY29uc3QgY3RybCA9IHRoaXMuZ2V0Q29udHJvbChmaWVsZC5pZCk7XG4gICAgICBpZiAoIWN0cmwpIGNvbnRpbnVlO1xuXG4gICAgICBjb25zdCBoaWRkZW4gPSBoaWRkZW5CeVJ1bGUuaGFzKGZpZWxkLmlkKSB8fCAhZXZhbHVhdGVGaWVsZFZpc2liaWxpdHkoZmllbGQsIHZhbHVlcyk7XG5cbiAgICAgIGlmIChoaWRkZW4pIHtcbiAgICAgICAgaWYgKGN0cmwuZW5hYmxlZCkgY3RybC5kaXNhYmxlKHsgZW1pdEV2ZW50OiBmYWxzZSB9KTtcbiAgICAgIH0gZWxzZSBpZiAoY3RybC5kaXNhYmxlZCAmJiAhZmllbGQuZGlzYWJsZWQpIHtcbiAgICAgICAgY3RybC5lbmFibGUoeyBlbWl0RXZlbnQ6IGZhbHNlIH0pO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBFdmVyeSBmaWVsZCBpbiB0aGUgY29uZmlnLCBpbmNsdWRpbmcgYGdyb3VwYC9gYXJyYXlgIGNoaWxkcmVuLiAqL1xuICBwcml2YXRlIGFsbEZpZWxkcygpOiBOZXN0ZWRGaWVsZENvbmZpZ1tdIHtcbiAgICBjb25zdCBvdXQ6IE5lc3RlZEZpZWxkQ29uZmlnW10gPSBbXTtcbiAgICBjb25zdCB3YWxrRmllbGRzID0gKGZpZWxkczogTmVzdGVkRmllbGRDb25maWdbXSB8IHVuZGVmaW5lZCkgPT4ge1xuICAgICAgZm9yIChjb25zdCBmIG9mIGZpZWxkcyA/PyBbXSkge1xuICAgICAgICBvdXQucHVzaChmKTtcbiAgICAgICAgaWYgKGYuY2hpbGRyZW4/Lmxlbmd0aCkgd2Fsa0ZpZWxkcyhmLmNoaWxkcmVuKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIGNvbnN0IHdhbGtUYWJzID0gKHRhYnM6IE5lc3RlZFRhYkNvbmZpZ1tdIHwgdW5kZWZpbmVkKSA9PiB7XG4gICAgICBmb3IgKGNvbnN0IHQgb2YgdGFicyA/PyBbXSkge1xuICAgICAgICB3YWxrRmllbGRzKHQuZmllbGRzKTtcbiAgICAgICAgd2Fsa1RhYnModC5jaGlsZHJlbik7XG4gICAgICB9XG4gICAgfTtcbiAgICB3YWxrVGFicyh0aGlzLmNvbmZpZz8udGFicyk7XG4gICAgcmV0dXJuIG91dDtcbiAgfVxuXG4gIC8qKiBBbiBlbXB0eSBgb3JpZ2luYWxEYXRhYCBjb3VudHMgYXMgXCJub3Qgc3VwcGxpZWRcIiDigJQgZmFsbCBiYWNrIHRvIHRoZSBjYXB0dXJlZCBiYXNlbGluZS4gKi9cbiAgcHJpdmF0ZSBiYXNlbGluZSgpOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IHtcbiAgICBjb25zdCBzdXBwbGllZCA9IHRoaXMub3JpZ2luYWxEYXRhO1xuICAgIGlmIChzdXBwbGllZCAmJiBPYmplY3Qua2V5cyhzdXBwbGllZCkubGVuZ3RoID4gMCkgcmV0dXJuIHN1cHBsaWVkO1xuICAgIHJldHVybiB0aGlzLnNlc3Npb25CYXNlbGluZSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBzYW1lVmFsdWUoYTogdW5rbm93biwgYjogdW5rbm93bik6IGJvb2xlYW4ge1xuICAgIGlmIChhID09PSBiKSByZXR1cm4gdHJ1ZTtcbiAgICBjb25zdCBhRW1wdHkgPSBhID09PSBudWxsIHx8IGEgPT09IHVuZGVmaW5lZCB8fCBhID09PSAnJztcbiAgICBjb25zdCBiRW1wdHkgPSBiID09PSBudWxsIHx8IGIgPT09IHVuZGVmaW5lZCB8fCBiID09PSAnJztcbiAgICBpZiAoYUVtcHR5ICYmIGJFbXB0eSkgcmV0dXJuIHRydWU7XG4gICAgaWYgKHR5cGVvZiBhID09PSAnb2JqZWN0JyB8fCB0eXBlb2YgYiA9PT0gJ29iamVjdCcpIHtcbiAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShhID8/IG51bGwpID09PSBKU09OLnN0cmluZ2lmeShiID8/IG51bGwpO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBnZXRDb250cm9sKGZpZWxkSWQ6IHN0cmluZywgY3VycmVudFRhYklkPzogc3RyaW5nKTogQWJzdHJhY3RDb250cm9sIHwgbnVsbCB7XG4gICAgaWYgKCF0aGlzLmZvcm0pIHJldHVybiBudWxsO1xuXG4gICAgaWYgKGN1cnJlbnRUYWJJZCkge1xuICAgICAgY29uc3QgdGFiR3JwID0gdGhpcy5nZXRUYWJHcm91cChjdXJyZW50VGFiSWQpO1xuICAgICAgY29uc3QgY3RybCA9IHRhYkdycD8uZ2V0KGZpZWxkSWQpO1xuICAgICAgaWYgKGN0cmwpIHJldHVybiBjdHJsO1xuICAgIH1cblxuICAgIGNvbnN0IHJvb3RDdHJsID0gdGhpcy5mb3JtLmdldChmaWVsZElkKTtcbiAgICBpZiAocm9vdEN0cmwpIHJldHVybiByb290Q3RybDtcblxuICAgIGNvbnN0IGZpbmRJbkdyb3VwID0gKGdyb3VwOiBGb3JtR3JvdXApOiBBYnN0cmFjdENvbnRyb2wgfCBudWxsID0+IHtcbiAgICAgIGZvciAoY29uc3Qga2V5IG9mIE9iamVjdC5rZXlzKGdyb3VwLmNvbnRyb2xzKSkge1xuICAgICAgICBjb25zdCBjID0gZ3JvdXAuY29udHJvbHNba2V5XTtcbiAgICAgICAgaWYgKGtleSA9PT0gZmllbGRJZCkgcmV0dXJuIGM7XG4gICAgICAgIGlmIChjIGluc3RhbmNlb2YgRm9ybUdyb3VwKSB7XG4gICAgICAgICAgY29uc3QgZm91bmQgPSBmaW5kSW5Hcm91cChjKTtcbiAgICAgICAgICBpZiAoZm91bmQpIHJldHVybiBmb3VuZDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfTtcblxuICAgIHJldHVybiBmaW5kSW5Hcm91cCh0aGlzLmZvcm0pO1xuICB9XG5cbiAgLyoqXG4gICAqIE1vdmUgZm9jdXMgaW50byB0aGUgbmV3bHkgc2hvd24gcGFuZWwuXG4gICAqXG4gICAqIEFjdGl2YXRpbmcgYSB0YWIgcmVwbGFjZXMgZXZlcnl0aGluZyBiZWxvdyBpdCB3aGlsZSBmb2N1cyBzdGF5cyBvbiB0aGUgdGFiIGJ1dHRvbiwgc28gYVxuICAgKiBzY3JlZW4tcmVhZGVyIG9yIGtleWJvYXJkIHVzZXIgaXMgZ2l2ZW4gbm8gaW5kaWNhdGlvbiB0aGF0IHRoZSBjb250ZW50IGNoYW5nZWQgYW5kIG11c3RcbiAgICogdGFiIGJhY2sgdGhyb3VnaCB0aGUgc3RyaXAgdG8gcmVhY2ggaXQuIERlZmVycmVkIGEgZnJhbWUgYmVjYXVzZSB0aGUgcGFuZWwncyBjb250ZW50c1xuICAgKiBhcmUgcmVuZGVyZWQgYnkgdGhlIGNoYW5nZSBkZXRlY3Rpb24gdGhpcyBjYWxsIGlzIHBhcnQgb2YuXG4gICAqL1xuICBwcml2YXRlIGZvY3VzQWN0aXZlUGFuZWwoKTogdm9pZCB7XG4gICAgaWYgKHR5cGVvZiByZXF1ZXN0QW5pbWF0aW9uRnJhbWUgIT09ICdmdW5jdGlvbicpIHJldHVybjtcbiAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gdGhpcy5mb3JtUGFuZWw/Lm5hdGl2ZUVsZW1lbnQ/LmZvY3VzPy4oKSk7XG4gIH1cblxuICAvKiogQWNjZXNzaWJsZSBuYW1lIGZvciB0aGUgZmllbGQgcGFuZWwg4oCUIGl0IGlzIHRoZSB0YWJwYW5lbCBmb3IgdGhlIGFjdGl2ZSB0YWIuICovXG4gIGdldCBhY3RpdmVUYWJMYWJlbCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IHRhYiA9IHRoaXMuYWN0aXZlU3ViVGFiQ29uZmlnID8/IHRoaXMuYWN0aXZlVGFiQ29uZmlnO1xuICAgIHJldHVybiB0YWIgPyB0aGlzLnJlc29sdmVUYWJMYWJlbCh0YWIpIDogJyc7XG4gIH1cblxuICAvKipcbiAgICogV2h5IGEgcmVmZXJlbmNlZCBmaWVsZCBpcyBmbGFnZ2VkLlxuICAgKlxuICAgKiBgaGFzRHJpZnRgIHdhcyB3cml0dGVuIGJ5IHRoZSBidWlsZGVyIGFuZCByZWFkIGJ5IG5vdGhpbmcgYXQgcnVudGltZSwgc28gYSBmaWVsZCB3aG9zZVxuICAgKiBzb3VyY2UgZGVmaW5pdGlvbiBoYWQgY2hhbmdlZCBzaW5jZSBpdCB3YXMgbGlua2VkIGxvb2tlZCBlbnRpcmVseSBub3JtYWwgdG8gdGhlIHBlcnNvblxuICAgKiBmaWxsaW5nIHRoZSBmb3JtIGluLlxuICAgKi9cbiAgZHJpZnRIaW50KGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IHN0cmluZyB7XG4gICAgY29uc3Qgc291cmNlID0gZmllbGQucmVmZXJlbmNlZEVudGl0eUtleVxuICAgICAgPyBgXCIke2ZpZWxkLnJlZmVyZW5jZWRFbnRpdHlLZXl9XCJgXG4gICAgICA6ICdpdHMgc291cmNlIGVudGl0eSc7XG4gICAgcmV0dXJuIGBUaGlzIGZpZWxkIGlzIGxpbmtlZCB0byAke3NvdXJjZX0gYW5kIGl0cyBkZWZpbml0aW9uIHRoZXJlIGhhcyBjaGFuZ2VkIHNpbmNlIGl0IHdhcyBsaW5rZWQuYDtcbiAgfVxuXG4gIHJlc29sdmVUYWJMYWJlbCh0YWI6IE5lc3RlZFRhYkNvbmZpZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHJlc29sdmVMYWJlbCh0YWIubGFiZWwsIHRoaXMubGFuZ3VhZ2UpO1xuICB9XG5cbiAgYXN5bmMgc3VibWl0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghdGhpcy5jYW5TdWJtaXQgfHwgdGhpcy5zdWJtaXRCbG9ja2VkKSB7XG4gICAgICB0aGlzLm1hcmtBbGxUb3VjaGVkKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgcmF3RGF0YSA9IHRoaXMuZXh0cmFjdFJlY29yZCgpO1xuICAgIGxldCBwcm9jZXNzZWREYXRhID0gcmF3RGF0YTtcblxuICAgIHRoaXMuaXNTYXZpbmcuc2V0KHRydWUpO1xuICAgIHRyeSB7XG4gICAgICAvLyBiZWZvcmVTYXZlIGNhbiBub3cgYWJvcnQgdGhlIHNhdmU6IHJldHVybmluZyBmYWxzZSwgb3IgdGhyb3dpbmcsIHN0b3BzIGl0LiBQcmV2aW91c2x5XG4gICAgICAvLyBpdHMgcmV0dXJuIHZhbHVlIHNpbXBseSByZXBsYWNlZCB0aGUgcGF5bG9hZCBhbmQgdGhlIHN1Ym1pdCBwcm9jZWVkZWQgcmVnYXJkbGVzcywgc29cbiAgICAgIC8vIGEgaG9vayB0aGF0IHdhbnRlZCB0byB2ZXRvIOKAlCBhIHNlcnZlci1zaWRlIGNoZWNrLCBhIGNvbmZpcm1hdGlvbiDigJQgaGFkIG5vIHdheSB0by5cbiAgICAgIGNvbnN0IGhvb2tLZXkgPSBgJHt0aGlzLmNvbmZpZz8uZW50aXR5fTpiZWZvcmVTYXZlYDtcbiAgICAgIGlmICh0aGlzLmhvb2tSZWdpc3RyeS5oYXMoaG9va0tleSkpIHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5ob29rUmVnaXN0cnkucnVuKGhvb2tLZXksIHByb2Nlc3NlZERhdGEpO1xuICAgICAgICBpZiAocmVzdWx0ID09PSBmYWxzZSkge1xuICAgICAgICAgIHRoaXMuc2F2ZVJlamVjdGVkLmVtaXQoeyByZWFzb246ICdiZWZvcmVTYXZlIHJldHVybmVkIGZhbHNlJyB9KTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gQW55dGhpbmcgZWxzZSBpcyB0aGUgcGF5bG9hZCB0byBzdWJtaXQ7IGB1bmRlZmluZWRgIG1lYW5zIFwidW5jaGFuZ2VkXCIuXG4gICAgICAgIGlmIChyZXN1bHQgIT09IHVuZGVmaW5lZCAmJiByZXN1bHQgIT09IHRydWUpIHByb2Nlc3NlZERhdGEgPSByZXN1bHQ7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuZm9ybVN1Ym1pdC5lbWl0KHByb2Nlc3NlZERhdGEpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgLy8gQSBob29rIHRoYXQgdGhyb3dzIGFib3J0cyB0aGUgc2F2ZSBhbmQgcmVwb3J0cyB3aHksIHJhdGhlciB0aGFuIHRoZSBlcnJvciBlc2NhcGluZ1xuICAgICAgLy8gaW50byBhbiB1bmhhbmRsZWQgcmVqZWN0aW9uIHdpdGggdGhlIGZvcm0gbGVmdCBsb29raW5nIGFzIHRob3VnaCBpdCBzYXZlZC5cbiAgICAgIHRoaXMuc2F2ZVJlamVjdGVkLmVtaXQoeyByZWFzb246IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSwgZXJyb3I6IGVyciB9KTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgdGhpcy5pc1NhdmluZy5zZXQoZmFsc2UpO1xuICAgIH1cbiAgfVxuXG4gIHJlc2V0KCk6IHZvaWQge1xuICAgIHRoaXMuZm9ybS5yZXNldCgpO1xuICAgIGlmICh0aGlzLmluaXRpYWxEYXRhKSB0aGlzLnBhdGNoRm9ybSh0aGlzLmluaXRpYWxEYXRhKTtcbiAgICBjb25zdCB2YWx1ZXMgPSB0aGlzLmZsYXR0ZW5Gb3JtVmFsdWVzKCk7XG4gICAgdGhpcy5mb3JtVmFsdWVzLnNldCh2YWx1ZXMpO1xuICAgIHRoaXMucHJldmlvdXNWYWx1ZXMgPSB7IC4uLnZhbHVlcyB9O1xuICAgIHRoaXMudW5sb2NrZWRGaWVsZHMuc2V0KG5ldyBTZXQ8c3RyaW5nPigpKTtcbiAgICB0aGlzLmZvcm1SZXNldC5lbWl0KCk7XG4gIH1cbn1cbiIsIkBpZiAoY2FuVmlldykge1xuICA8Zm9ybSBbZm9ybUdyb3VwXT1cImZvcm1cIiAobmdTdWJtaXQpPVwic3VibWl0KClcIiBjbGFzcz1cIm5neC1mb3JtXCIgbm92YWxpZGF0ZT5cblxuICAgIDwhLS0gRXJyb3IgbWVzc2FnZSBiYW5uZXIgLS0+XG4gICAgQGlmIChlcnJvcikge1xuICAgICAgPGRpdiBjbGFzcz1cIm5neC1mb3JtX19lcnJvci1iYW5uZXJcIiByb2xlPVwiYWxlcnRcIiBkYXRhLXRlc3RpZD1cImZvcm0tZXJyb3JcIj57eyBlcnJvciB9fTwvZGl2PlxuICAgIH1cblxuICAgIDwhLS0gUnVsZXMgSW5mbyBCYW5uZXJzIC0tPlxuICAgIEBmb3IgKGluZm9LZXkgb2Ygc2hvd0luZm9CYW5uZXJzID8gT2JqZWN0LmtleXMocnVsZVJlc3VsdCgpLmluZm9CYW5uZXJzKSA6IFtdOyB0cmFjayBpbmZvS2V5KSB7XG4gICAgICA8ZGl2IGNsYXNzPVwibmd4LWZvcm1fX2luZm8tYmFubmVyXCIgcm9sZT1cInN0YXR1c1wiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidpbmZvLWJhbm5lci0nICsgaW5mb0tleVwiPlxuICAgICAgICDihLnvuI8ge3sgcnVsZVJlc3VsdCgpLmluZm9CYW5uZXJzW2luZm9LZXldIH19XG4gICAgICA8L2Rpdj5cbiAgICB9XG5cbiAgICA8IS0tIFJ1bGVzIFZhbGlkYXRpb24gRXJyb3JzICYgV2FybmluZ3MgLS0+XG4gICAgQGZvciAoZXJyS2V5IG9mIE9iamVjdC5rZXlzKHJ1bGVSZXN1bHQoKS52YWxpZGF0aW9uRXJyb3JzKTsgdHJhY2sgZXJyS2V5KSB7XG4gICAgICA8ZGl2IGNsYXNzPVwibmd4LWZvcm1fX2Vycm9yLWJhbm5lclwiIHJvbGU9XCJhbGVydFwiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidydWxlLWVycm9yLScgKyBlcnJLZXlcIj5cbiAgICAgICAg4pqg77iPIHt7IHJ1bGVSZXN1bHQoKS52YWxpZGF0aW9uRXJyb3JzW2VycktleV0gfX1cbiAgICAgIDwvZGl2PlxuICAgIH1cbiAgICBAZm9yICh3YXJuS2V5IG9mIE9iamVjdC5rZXlzKHJ1bGVSZXN1bHQoKS52YWxpZGF0aW9uV2FybmluZ3MpOyB0cmFjayB3YXJuS2V5KSB7XG4gICAgICA8ZGl2IGNsYXNzPVwibmd4LWZvcm1fX3dhcm5pbmctYmFubmVyXCIgcm9sZT1cInN0YXR1c1wiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidydWxlLXdhcm5pbmctJyArIHdhcm5LZXlcIj5cbiAgICAgICAg4pqhIHt7IHJ1bGVSZXN1bHQoKS52YWxpZGF0aW9uV2FybmluZ3Nbd2FybktleV0gfX1cbiAgICAgIDwvZGl2PlxuICAgIH1cblxuICAgIDwhLS0gRGVmZXJyZWQgVkFMVUVfQ0hBTkdFRCBub3RpY2UgZm9yIGNyaXRpY2FsRmllbGQgZWRpdHMgKHZzIHNlc3Npb24gYmFzZWxpbmUpIC0tPlxuICAgIEBpZiAoY2hhbmdlZENyaXRpY2FsRmllbGRzKCkubGVuZ3RoID4gMCkge1xuICAgICAgPGRpdiBjbGFzcz1cIm5neC1mb3JtX193YXJuaW5nLWJhbm5lclwiIHJvbGU9XCJzdGF0dXNcIiBkYXRhLXRlc3RpZD1cImNyaXRpY2FsLWNoYW5nZS1iYW5uZXJcIj5cbiAgICAgICAg8J+UkiBDcml0aWNhbCBmaWVsZCBjaGFuZ2VkOlxuICAgICAgICBAZm9yIChmaWVsZCBvZiBjaGFuZ2VkQ3JpdGljYWxGaWVsZHMoKTsgdHJhY2sgZmllbGQuaWQpIHtcbiAgICAgICAgICA8c3Bhbj57eyByZXNvbHZlRmllbGRMYWJlbChmaWVsZCkgfX17eyAkbGFzdCA/ICcnIDogJywgJyB9fTwvc3Bhbj5cbiAgICAgICAgfVxuICAgICAgICDigJQgdGhpcyBkaWZmZXJzIGZyb20gdGhlIHZhbHVlIGF0IHRoZSBzdGFydCBvZiB0aGlzIHNlc3Npb24uXG4gICAgICA8L2Rpdj5cbiAgICB9XG5cbiAgICA8IS0tIFByaW1hcnkgVGFiIGJhciDigJQgb25seSBpZiB2aXNpYmxlIHRhYnMgZGVmaW5lZCAtLT5cbiAgICBAaWYgKHZpc2libGVUYWJzLmxlbmd0aCA+IDApIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJuZ3gtZm9ybV9fdGFic1wiIHJvbGU9XCJ0YWJsaXN0XCIgZGF0YS10ZXN0aWQ9XCJ0YWItc3RyaXBcIj5cbiAgICAgICAgQGZvciAodGFiIG9mIHZpc2libGVUYWJzOyB0cmFjayB0YWIuaWQpIHtcbiAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgIGNsYXNzPVwibmd4LWZvcm1fX3RhYlwiXG4gICAgICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCIndGFiLScgKyB0YWIuaWRcIlxuICAgICAgICAgICAgW2NsYXNzLm5neC1mb3JtX190YWItLWFjdGl2ZV09XCJhY3RpdmVUYWIoKSA9PT0gdGFiLmlkXCJcbiAgICAgICAgICAgIChjbGljayk9XCJzZXRBY3RpdmVUYWIodGFiLmlkKVwiXG4gICAgICAgICAgICByb2xlPVwidGFiXCJcbiAgICAgICAgICAgIFthdHRyLmFyaWEtc2VsZWN0ZWRdPVwiYWN0aXZlVGFiKCkgPT09IHRhYi5pZFwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAge3sgcmVzb2x2ZVRhYkxhYmVsKHRhYikgfX1cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG4gICAgfVxuXG4gICAgPCEtLSBTdWItdGFiIGJhciDigJQgb25seSBpZiBhY3RpdmUgdGFiIGhhcyBzdWItdGFicyAtLT5cbiAgICBAaWYgKHZpc2libGVTdWJUYWJzLmxlbmd0aCA+IDApIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJuZ3gtZm9ybV9fdGFicyBuZ3gtZm9ybV9fdGFicy0tc3ViXCIgcm9sZT1cInRhYmxpc3RcIiBkYXRhLXRlc3RpZD1cInN1YnRhYi1zdHJpcFwiIHN0eWxlPVwibWFyZ2luLXRvcDogLTEycHg7IG1hcmdpbi1ib3R0b206IDEycHg7IGZvbnQtc2l6ZTogMTNweDtcIj5cbiAgICAgICAgQGZvciAoc3ViVGFiIG9mIHZpc2libGVTdWJUYWJzOyB0cmFjayBzdWJUYWIuaWQpIHtcbiAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgIGNsYXNzPVwibmd4LWZvcm1fX3RhYiBuZ3gtZm9ybV9fdGFiLS1zdWJcIlxuICAgICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3N1YnRhYi0nICsgc3ViVGFiLmlkXCJcbiAgICAgICAgICAgIFtjbGFzcy5uZ3gtZm9ybV9fdGFiLS1hY3RpdmVdPVwiKGFjdGl2ZVN1YlRhYkNvbmZpZz8uaWQpID09PSBzdWJUYWIuaWRcIlxuICAgICAgICAgICAgKGNsaWNrKT1cInNldEFjdGl2ZVN1YlRhYihzdWJUYWIuaWQpXCJcbiAgICAgICAgICAgIHJvbGU9XCJ0YWJcIlxuICAgICAgICAgICAgW2F0dHIuYXJpYS1zZWxlY3RlZF09XCIoYWN0aXZlU3ViVGFiQ29uZmlnPy5pZCkgPT09IHN1YlRhYi5pZFwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAge3sgcmVzb2x2ZVRhYkxhYmVsKHN1YlRhYikgfX1cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG4gICAgfVxuXG4gICAgPCEtLVxuICAgICAgT25lIHRhYnBhbmVsIHBlciB0YWIsIHdyYXBwaW5nIGJvdGggYnJhbmNoZXMuIEZvY3VzIG1vdmVzIGhlcmUgb24gYSB0YWIgc3dpdGNoLCBhbmQgYVxuICAgICAgbW9kdWxlIHRhYiBpcyBzdGlsbCBhIHRhYiDigJQgcHV0dGluZyB0aGUgcGFuZWwgc2VtYW50aWNzIG9uIG9ubHkgdGhlIGZpZWxkcyBicmFuY2ggbGVmdFxuICAgICAgYSBtb2R1bGUgdGFiIHdpdGggbm93aGVyZSBmb3IgZm9jdXMgdG8gbGFuZC5cbiAgICAtLT5cbiAgICA8ZGl2XG4gICAgICByb2xlPVwidGFicGFuZWxcIlxuICAgICAgdGFiaW5kZXg9XCItMVwiXG4gICAgICAjZm9ybVBhbmVsXG4gICAgICBjbGFzcz1cIm5neC1mb3JtX190YWJwYW5lbFwiXG4gICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cImFjdGl2ZVRhYkxhYmVsXCJcbiAgICA+XG4gICAgQGlmIChhY3RpdmVUYWJNb2R1bGVDb21wb25lbnQpIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJuZ3gtZm9ybV9fbW9kdWxlLXBhbmVsXCIgZGF0YS10ZXN0aWQ9XCJtb2R1bGUtcGFuZWxcIj5cbiAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdDb21wb25lbnRPdXRsZXQ9XCJhY3RpdmVUYWJNb2R1bGVDb21wb25lbnQ7IGlucHV0czogKGFjdGl2ZVN1YlRhYkNvbmZpZyB8fCBhY3RpdmVUYWJDb25maWcpPy5tb2R1bGVJbnB1dHNcIj48L25nLWNvbnRhaW5lcj5cbiAgICAgIDwvZGl2PlxuICAgIH0gQGVsc2Uge1xuICAgICAgPCEtLSBGaWVsZHMgZm9yIGFjdGl2ZSB0YWIgKFJlc3BvbnNpdmUgMTItY29sdW1uIGdyaWQpIC0tPlxuICAgICAgPGRpdiBjbGFzcz1cIm5neC1mb3JtX19wYW5lbFwiIGRhdGEtdGVzdGlkPVwiZm9ybS1wYW5lbFwiPlxuICAgICAgICBAZm9yIChmaWVsZCBvZiBmaWVsZHNGb3JBY3RpdmVUYWI7IHRyYWNrIGZpZWxkLmlkKSB7XG4gICAgICAgICAgPGRpdlxuICAgICAgICAgICAgY2xhc3M9XCJuZ3gtZm9ybV9fZmllbGQtc2xvdFwiXG4gICAgICAgICAgICBbc3R5bGUuZ3JpZENvbHVtbl09XCJnZXRGaWVsZFNwYW4oZmllbGQpXCJcbiAgICAgICAgICAgIFthdHRyLmlkXT1cIidmaWVsZC1jb250YWluZXItJyArIGZpZWxkLmlkXCJcbiAgICAgICAgICAgIHRhYmluZGV4PVwiLTFcIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIDxuZ3gtZHluYW1pYy1maWVsZFxuICAgICAgICAgICAgICBbZmllbGRdPVwiZmllbGRcIlxuICAgICAgICAgICAgICBbY29udHJvbF09XCJnZXRDb250cm9sKGZpZWxkLmlkLCAoYWN0aXZlU3ViVGFiQ29uZmlnIHx8IGFjdGl2ZVRhYkNvbmZpZyk/LmlkKSFcIlxuICAgICAgICAgICAgICBbY29uZmlnXT1cImNvbmZpZ1wiXG4gICAgICAgICAgICAgIFtjdXJyZW50VGFiSWRdPVwiKGFjdGl2ZVN1YlRhYkNvbmZpZyB8fCBhY3RpdmVUYWJDb25maWcpPy5pZFwiXG4gICAgICAgICAgICAgIFtsYW5ndWFnZV09XCJsYW5ndWFnZVwiXG4gICAgICAgICAgICAgIFtyZWFkb25seV09XCJpc0ZpZWxkUmVhZG9ubHkoZmllbGQpXCJcbiAgICAgICAgICAgICAgW3VzZXJSb2xlc109XCJ1c2VyUm9sZXNcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICAgIEBpZiAoZmllbGQuaGFzRHJpZnQpIHtcbiAgICAgICAgICAgICAgPCEtLVxuICAgICAgICAgICAgICAgIERyaWZ0IHdhcyBwcmV2aW91c2x5IHZpc2libGUgb25seSBpbiB0aGUgYnVpbGRlciwgc28gYSByZWZlcmVuY2VkIGZpZWxkIHdob3NlXG4gICAgICAgICAgICAgICAgc291cmNlIGhhZCBjaGFuZ2VkIGxvb2tlZCBjb21wbGV0ZWx5IG5vcm1hbCB0byB0aGUgcGVyc29uIGZpbGxpbmcgdGhlIGZvcm0gaW4uXG4gICAgICAgICAgICAgICAgcm9sZT1cInN0YXR1c1wiIHJhdGhlciB0aGFuIFwiYWxlcnRcIjogaXQgaXMgaW5mb3JtYXRpb24sIG5vdCBhbiBlcnJvci5cbiAgICAgICAgICAgICAgLS0+XG4gICAgICAgICAgICAgIDxzcGFuXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJuZ3gtZmllbGRfX2RyaWZ0XCJcbiAgICAgICAgICAgICAgICByb2xlPVwic3RhdHVzXCJcbiAgICAgICAgICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCInZmllbGQtJyArIGZpZWxkLmlkICsgJy1kcmlmdCdcIlxuICAgICAgICAgICAgICAgIFt0aXRsZV09XCJkcmlmdEhpbnQoZmllbGQpXCJcbiAgICAgICAgICAgICAgPuKaoCB7eyBkcmlmdEhpbnQoZmllbGQpIH19PC9zcGFuPlxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgQGlmIChmaWVsZC5jcml0aWNhbEZpZWxkICYmICFyZWFkb25seSkge1xuICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJuZ3gtZm9ybV9fbG9ja1wiXG4gICAgICAgICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2xvY2stJyArIGZpZWxkLmlkXCJcbiAgICAgICAgICAgICAgICBbYXR0ci5hcmlhLXByZXNzZWRdPVwiIWlzRmllbGRMb2NrZWQoZmllbGQpXCJcbiAgICAgICAgICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIlxuICAgICAgICAgICAgICAgICAgKGlzRmllbGRMb2NrZWQoZmllbGQpID8gJ1VubG9jayAnIDogJ0xvY2sgJykgKyByZXNvbHZlRmllbGRMYWJlbChmaWVsZClcbiAgICAgICAgICAgICAgICBcIlxuICAgICAgICAgICAgICAgIFt0aXRsZV09XCJcbiAgICAgICAgICAgICAgICAgIGlzRmllbGRMb2NrZWQoZmllbGQpXG4gICAgICAgICAgICAgICAgICAgID8gJ0NyaXRpY2FsIGZpZWxkIOKAlCBjbGljayB0byB1bmxvY2sgZm9yIGVkaXRpbmcnXG4gICAgICAgICAgICAgICAgICAgIDogJ0NyaXRpY2FsIGZpZWxkIOKAlCBjbGljayB0byBsb2NrJ1xuICAgICAgICAgICAgICAgIFwiXG4gICAgICAgICAgICAgICAgKGNsaWNrKT1cInRvZ2dsZUZpZWxkTG9jayhmaWVsZClcIlxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAge3sgaXNGaWVsZExvY2tlZChmaWVsZCkgPyAn8J+UkicgOiAn8J+UkycgfX1cbiAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICB9XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuICAgIH1cbiAgICA8L2Rpdj5cblxuICAgIDwhLS0gU3VibWl0IGJ1dHRvbiDigJQgZ2F0ZWQgYnkgUkJBQyBhbmQgaXNTYXZpbmcgbG9jayAtLT5cbiAgICBAaWYgKCFyZWFkb25seSAmJiBwZXJtaXNzaW9ucy5jYW5FZGl0KSB7XG4gICAgICA8ZGl2IGNsYXNzPVwibmd4LWZvcm1fX2FjdGlvbnNcIiBkYXRhLXRlc3RpZD1cImZvcm0tYWN0aW9uc1wiPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgY2xhc3M9XCJuZ3gtZm9ybV9fcmVzZXRcIlxuICAgICAgICAgIGRhdGEtdGVzdGlkPVwiZm9ybS1yZXNldFwiXG4gICAgICAgICAgKGNsaWNrKT1cInJlc2V0KClcIlxuICAgICAgICAgIFtkaXNhYmxlZF09XCJpc1NhdmluZygpXCJcbiAgICAgICAgPlxuICAgICAgICAgIFJlc2V0XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgdHlwZT1cInN1Ym1pdFwiXG4gICAgICAgICAgY2xhc3M9XCJuZ3gtZm9ybV9fc3VibWl0XCJcbiAgICAgICAgICBkYXRhLXRlc3RpZD1cImZvcm0tc3VibWl0XCJcbiAgICAgICAgICBbZGlzYWJsZWRdPVwic3VibWl0QmxvY2tlZFwiXG4gICAgICAgICAgdGl0bGU9XCJTaG9ydGN1dDogQ3RybCtTXCJcbiAgICAgICAgPlxuICAgICAgICAgIEBpZiAoaXNTYXZpbmcoKSB8fCBsb2FkaW5nKSB7XG4gICAgICAgICAgICBTYXZpbmfigKZcbiAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgIFNhdmVcbiAgICAgICAgICB9XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgPC9kaXY+XG4gICAgfVxuXG4gIDwvZm9ybT5cbn0gQGVsc2Uge1xuICA8IS0tIHBlcm1pc3Npb25zLnZpZXcgZGVuaWVkOiByZW5kZXIgbm90aGluZyBvZiB0aGUgcmVjb3JkLlxuICAgICAgIFByZXNlbnRhdGlvbmFsIG9ubHkg4oCUIHRoZSBzZXJ2ZXIgbXVzdCBub3Qgc2VuZCBkYXRhIHRoaXMgdXNlciBtYXkgbm90IHNlZS4gLS0+XG4gIDxkaXYgY2xhc3M9XCJuZ3gtZm9ybV9fZGVuaWVkXCIgcm9sZT1cInN0YXR1c1wiIGRhdGEtdGVzdGlkPVwiZm9ybS1hY2Nlc3MtZGVuaWVkXCI+XG4gICAgWW91IGRvIG5vdCBoYXZlIHBlcm1pc3Npb24gdG8gdmlldyB0aGlzIHJlY29yZC5cbiAgPC9kaXY+XG59XG4iXX0=