ngx-dynamic-entity 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +14 -0
  2. package/fesm2022/ngx-dynamic-entity.mjs +255 -157
  3. package/fesm2022/ngx-dynamic-entity.mjs.map +1 -1
  4. package/package.json +6 -7
  5. package/types/ngx-dynamic-entity.d.ts +1730 -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,531 +0,0 @@
1
- import { ChangeDetectionStrategy, Component, Input, Output, EventEmitter, signal, computed, inject, afterNextRender, ElementRef, Injector, ViewChild } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { ReactiveFormsModule } from '@angular/forms';
4
- import { findTab, formatDisplayValue, getTabData, resolveLabel } from '@dynamic-entity/core';
5
- import { DynamicFormComponent } from './dynamic-form.component';
6
- import { DynamicFieldComponent } from './dynamic-field/dynamic-field.component';
7
- import { RulesEvaluationService } from '../services/rules-evaluation.service';
8
- import * as i0 from "@angular/core";
9
- import * as i1 from "@angular/forms";
10
- /**
11
- * Distinguishes "no tab holds this field" from "the field holds undefined", so the tab walk
12
- * can keep searching in the first case and stop in the second.
13
- */
14
- const FIELD_NOT_FOUND = Symbol('field-not-found');
15
- /**
16
- * DynamicRecordFormComponent — comprehensive tabbed record view & edit component.
17
- * Supports cross-tab rule evaluation, profile header, summary drawer (showOnMinimize),
18
- * interactive quick-jump links, and baseline modification tracking.
19
- */
20
- export class DynamicRecordFormComponent {
21
- config;
22
- rules;
23
- initialData;
24
- userRoles = [];
25
- language = 'en';
26
- readonly = false;
27
- loading = false;
28
- error = null;
29
- /** Whole record read-only, regardless of RBAC. Equivalent to the reference's `isReadOnly`. */
30
- isReadOnly = false;
31
- /** Specific field ids forced read-only while the rest of the record stays editable. */
32
- readOnlyFields = [];
33
- /**
34
- * Open the record read-only, with a per-tab "Edit section" flow — one tab is edited and
35
- * validated at a time. This is the framework's `EntityRecordComponent` model and the
36
- * default here.
37
- *
38
- * Set false for a directly editable record with no view/edit distinction.
39
- */
40
- viewMode = true;
41
- formSubmit = new EventEmitter();
42
- formChange = new EventEmitter();
43
- formReset = new EventEmitter();
44
- /** One tab section was saved. Carries the whole record, plus which tab was edited. */
45
- sectionSave = new EventEmitter();
46
- dynamicFormComp;
47
- rulesEvaluation = inject(RulesEvaluationService);
48
- host = inject(ElementRef);
49
- injector = inject(Injector);
50
- currentData = signal({});
51
- originalBaseline = signal({});
52
- /**
53
- * Info banners the user has dismissed, by field id.
54
- *
55
- * The reference's contract: an info banner persists until dismissed, **not** until the
56
- * value changes. Dismissal is keyed by field so re-triggering a different field's rule
57
- * still shows. Re-armed when a different record is loaded.
58
- */
59
- dismissed = signal(new Set());
60
- /**
61
- * Info banners from the rules engine that are still showing.
62
- *
63
- * A getter, not a `computed`: the source is `dynamicFormComp`, a ViewChild. A computed
64
- * evaluated before the view initialises would capture no dependency on the child's
65
- * `ruleResult` signal and then never recompute. Read per change-detection pass instead.
66
- */
67
- get infoBanners() {
68
- const result = this.dynamicFormComp?.ruleResult();
69
- const gone = this.dismissed();
70
- return Object.entries(result?.infoBanners ?? {})
71
- .filter(([fieldId]) => !gone.has(fieldId))
72
- .map(([fieldId, message]) => ({ fieldId, message: String(message) }));
73
- }
74
- dismissInfoBanner(fieldId) {
75
- this.dismissed.set(new Set(this.dismissed()).add(fieldId));
76
- }
77
- /** True when nothing in the record may be edited. */
78
- get recordReadOnly() {
79
- return this.readonly || this.isReadOnly;
80
- }
81
- // ─── Inline array-row editing ───────────────────────────────────────────────
82
- /**
83
- * A row of an `array` field is edited in a drawer, not inline in the tab panel.
84
- *
85
- * The drawer is rendered outside the tab container on purpose: the reference found that
86
- * building a row's FormGroup inside the tab view races the tab's own initialisation, so
87
- * the row form is kept detached and only pushed into the FormArray on save.
88
- */
89
- inlineRowField = signal(null);
90
- inlineRowIndex = signal(null);
91
- inlineRowForm = null;
92
- /** `array` fields on the tab currently shown, so the template can list their rows. */
93
- get arrayFieldsForActiveTab() {
94
- const tabId = this.activeTabId;
95
- const tab = tabId ? findTab(this.config?.tabs, tabId) : null;
96
- return (tab?.fields ?? []).filter(f => f.type === 'array');
97
- }
98
- /**
99
- * Rows currently held by an `array` field, read from the record value.
100
- *
101
- * Deliberately not read off the child's FormArray: that control only exists once the
102
- * child has initialised, so rendering from it changed value mid-pass. `currentData` is a
103
- * signal seeded from `initialData` and kept current by the child's `formChange`.
104
- */
105
- rowsOf(field) {
106
- const tabId = this.activeTabId;
107
- const tabData = tabId ? getTabData(tabId, this.currentData(), this.config) : null;
108
- const rows = tabData?.[field.id];
109
- return Array.isArray(rows) ? rows : [];
110
- }
111
- /** One row rendered as text, so the list is readable without opening the drawer. */
112
- rowSummary(field, row) {
113
- const value = row ?? {};
114
- const parts = (field.children ?? [])
115
- .map(child => value[child.id])
116
- .filter(v => v !== null && v !== undefined && v !== '')
117
- .map(v => (typeof v === 'object' ? JSON.stringify(v) : String(v)));
118
- return parts.length ? parts.join(' · ') : '(empty)';
119
- }
120
- openAddRow(field) {
121
- if (this.sectionReadOnly)
122
- return;
123
- this.inlineRowField.set(field);
124
- this.inlineRowIndex.set(null);
125
- this.inlineRowForm = this.dynamicFormComp?.createArrayRow(field);
126
- }
127
- openEditRow(field, index) {
128
- if (this.sectionReadOnly)
129
- return;
130
- this.inlineRowField.set(field);
131
- this.inlineRowIndex.set(index);
132
- this.inlineRowForm = this.dynamicFormComp?.createArrayRow(field, this.rowsOf(field)[index]);
133
- }
134
- cancelRow() {
135
- this.inlineRowField.set(null);
136
- this.inlineRowIndex.set(null);
137
- this.inlineRowForm = null;
138
- }
139
- /** Commit the drawer into the FormArray. Rules re-evaluate off the resulting value change. */
140
- saveRow() {
141
- const field = this.inlineRowField();
142
- const form = this.dynamicFormComp;
143
- if (!field || !form || !this.inlineRowForm)
144
- return;
145
- if (this.inlineRowForm.invalid) {
146
- this.inlineRowForm.markAllAsTouched();
147
- return;
148
- }
149
- const array = form.getArrayControl(field.id, this.activeTabId ?? undefined);
150
- if (!array)
151
- return;
152
- const index = this.inlineRowIndex();
153
- if (index === null)
154
- array.push(form.createArrayRow(field, this.inlineRowForm.value));
155
- else
156
- array.at(index).patchValue(this.inlineRowForm.value);
157
- array.updateValueAndValidity();
158
- this.currentData.set(form.extractRecord());
159
- this.cancelRow();
160
- }
161
- deleteRow(field, index) {
162
- if (this.sectionReadOnly)
163
- return;
164
- const array = this.dynamicFormComp?.getArrayControl(field.id, this.activeTabId ?? undefined);
165
- array?.removeAt(index);
166
- array?.updateValueAndValidity();
167
- if (this.dynamicFormComp)
168
- this.currentData.set(this.dynamicFormComp.extractRecord());
169
- if (this.inlineRowIndex() === index)
170
- this.cancelRow();
171
- }
172
- // ─── Per-tab section editing ────────────────────────────────────────────────
173
- /**
174
- * The tab currently open for editing, or null when the record is being viewed.
175
- *
176
- * The reference edits one tab section at a time rather than the whole record, and
177
- * validates only that section on save — a required field on an untouched tab must not
178
- * block saving the tab you are actually working on.
179
- */
180
- editingTabId = signal(null);
181
- /**
182
- * The tab the inner form is showing.
183
- *
184
- * Tracked here rather than read off the ViewChild. The child resolves its first tab as
185
- * part of its own initialisation, so reading it during this component's render changed
186
- * value mid-pass — an ExpressionChangedAfterItHasBeenChecked error. Seeded from the
187
- * config and kept in step by the child's `activeTabChange` output.
188
- */
189
- activeTabIdSig = signal(null);
190
- get activeTabId() {
191
- return this.activeTabIdSig();
192
- }
193
- onActiveTabChange(tabId) {
194
- if (this.activeTabIdSig() === tabId)
195
- return;
196
- this.activeTabIdSig.set(tabId);
197
- // Editing does not follow the user to another tab.
198
- this.editingTabId.set(null);
199
- this.cancelRow();
200
- }
201
- /** First tab a user can see, used to seed the active tab before the child reports one. */
202
- firstVisibleTabId() {
203
- return this.config?.tabs?.find(t => t.visibility !== false)?.id ?? null;
204
- }
205
- /** Errors from the last save attempt, by field id. */
206
- sectionErrors = signal({});
207
- /** Template access to `Object.keys` for iterating the error map. */
208
- Object = Object;
209
- get isEditingActiveTab() {
210
- // Without view mode there is no view/edit distinction — everything is always editable.
211
- if (!this.viewMode)
212
- return true;
213
- return !!this.activeTabId && this.editingTabId() === this.activeTabId;
214
- }
215
- /** Fields render read-only unless their own tab is the one being edited. */
216
- get sectionReadOnly() {
217
- return this.recordReadOnly || !this.isEditingActiveTab;
218
- }
219
- editSection() {
220
- if (!this.viewMode || this.recordReadOnly || !this.activeTabId)
221
- return;
222
- this.sectionErrors.set({});
223
- this.editingTabId.set(this.activeTabId);
224
- }
225
- /** Discard this section's edits, restoring each field from the session baseline. */
226
- cancelSection() {
227
- const tabId = this.editingTabId();
228
- const form = this.dynamicFormComp;
229
- if (tabId && form) {
230
- const tab = findTab(this.config?.tabs, tabId);
231
- const baselineTab = (getTabData(tabId, this.originalBaseline(), this.config) ?? {});
232
- for (const field of tab?.fields ?? []) {
233
- const baselineValue = baselineTab[field.id];
234
- // A FormArray cannot be restored with setValue — that demands a value shaped exactly
235
- // like the current rows. Rebuild it from the baseline rows instead.
236
- const array = form.getArrayControl(field.id, tabId);
237
- if (array) {
238
- array.clear();
239
- for (const row of Array.isArray(baselineValue) ? baselineValue : []) {
240
- array.push(form.createArrayRow(field, row));
241
- }
242
- array.updateValueAndValidity();
243
- continue;
244
- }
245
- form.getControl(field.id, tabId)?.setValue(baselineValue ?? null);
246
- }
247
- this.currentData.set(form.extractRecord());
248
- }
249
- this.sectionErrors.set({});
250
- this.editingTabId.set(null);
251
- }
252
- /**
253
- * Validate this section only, then emit it. Validation is scoped two ways: Angular
254
- * validity of the tab's own controls, and rules filtered to this tab — a rule targeting
255
- * a hidden tab must not block the save (the reference's OV0-968 fix).
256
- */
257
- saveSection() {
258
- const tabId = this.editingTabId();
259
- const form = this.dynamicFormComp;
260
- if (!tabId || !form)
261
- return;
262
- const tab = findTab(this.config?.tabs, tabId);
263
- const errors = {};
264
- for (const field of tab?.fields ?? []) {
265
- const ctrl = form.getControl(field.id, tabId);
266
- if (ctrl?.invalid) {
267
- ctrl.markAsTouched();
268
- errors[field.id] = `${resolveLabel(field.label, this.language)} is invalid.`;
269
- }
270
- }
271
- const scoped = this.rulesEvaluation.filterForTab(this.rules, tabId, this.config);
272
- const ruleErrors = this.rulesEvaluation.evaluate(scoped, form.formValues(), this.originalBaseline())
273
- .validationErrors;
274
- Object.assign(errors, ruleErrors);
275
- if (Object.keys(errors).length > 0) {
276
- this.sectionErrors.set(errors);
277
- return;
278
- }
279
- this.sectionErrors.set({});
280
- this.editingTabId.set(null);
281
- const record = form.extractRecord();
282
- this.currentData.set(record);
283
- this.sectionSave.emit({ tabId, record });
284
- }
285
- /**
286
- * Seed the session baseline from the loaded record so `VALUE_CHANGED` rules and the
287
- * modification banner compare against the record as it was opened — not as it was
288
- * after the first keystroke.
289
- */
290
- ngOnChanges(changes) {
291
- if (changes['initialData'] || changes['config']) {
292
- this.originalBaseline.set({ ...(this.initialData ?? {}) });
293
- this.currentData.set({ ...(this.initialData ?? {}) });
294
- this.activeTabIdSig.set(this.firstVisibleTabId());
295
- // A different record is a different set of banners — re-arm them.
296
- this.dismissed.set(new Set());
297
- }
298
- }
299
- get recordTitle() {
300
- if (!this.config)
301
- return 'Record';
302
- return this.config.name ? resolveLabel(this.config.name, this.language) : this.config.entity;
303
- }
304
- get avatarLetter() {
305
- return (this.recordTitle || 'R').charAt(0).toUpperCase();
306
- }
307
- // ─── Header fields (isProfileImage / isHeaderToggle) ────────────────────────
308
- /** Every field in the config, at any depth. */
309
- allFields() {
310
- const out = [];
311
- const walkFields = (fields) => {
312
- for (const f of fields ?? []) {
313
- out.push(f);
314
- walkFields(f.children);
315
- }
316
- };
317
- const walkTabs = (tabs) => {
318
- for (const t of tabs ?? []) {
319
- walkFields(t.fields);
320
- walkTabs(t.children);
321
- }
322
- };
323
- walkTabs(this.config?.tabs);
324
- return out;
325
- }
326
- /** The field flagged `isProfileImage`, rendered as the record avatar. */
327
- get headerProfileField() {
328
- return this.allFields().find(f => f.isProfileImage) ?? null;
329
- }
330
- /** The field flagged `isHeaderToggle`, rendered as a status switch in the header. */
331
- get headerToggleField() {
332
- return this.allFields().find(f => f.isHeaderToggle) ?? null;
333
- }
334
- /** Avatar image URL, when the profile field holds a persisted FileRef. */
335
- get profileImageUrl() {
336
- const field = this.headerProfileField;
337
- if (!field)
338
- return null;
339
- const value = this.fieldValue(field.id);
340
- return value?.url ?? null;
341
- }
342
- get headerToggleValue() {
343
- const field = this.headerToggleField;
344
- return field ? this.fieldValue(field.id) === true : false;
345
- }
346
- /**
347
- * Flip the header toggle. It writes straight to the control rather than going through
348
- * section editing — the reference treats it as a record-level status switch, not part of
349
- * any one tab's section.
350
- */
351
- toggleHeaderStatus() {
352
- const field = this.headerToggleField;
353
- if (!field || this.recordReadOnly)
354
- return;
355
- const ctrl = this.controlFor(field.id);
356
- if (!ctrl)
357
- return;
358
- ctrl.setValue(!this.headerToggleValue);
359
- if (this.dynamicFormComp)
360
- this.currentData.set(this.dynamicFormComp.extractRecord());
361
- }
362
- /** Locate a field's control wherever it lives, since header fields may be on any tab. */
363
- controlFor(fieldId) {
364
- return this.dynamicFormComp?.getControl(fieldId) ?? null;
365
- }
366
- /**
367
- * A field's current value, read from the record so it is stable during render.
368
- *
369
- * Resolved through `getTabData` so it honours exactly the nesting contract the form
370
- * patches with — `{ tabId: { fieldId } }`, or the record root when the tab sets
371
- * `flatData`. Reading the record flat instead is not a shortcut: it makes the summary
372
- * display values the form never loaded, which reads as "the data is there" while every
373
- * control behind it is empty.
374
- *
375
- * Walks sub-tabs as well: a summary, header, or profile field may sit on any tab at any
376
- * depth.
377
- */
378
- fieldValue(fieldId) {
379
- const visit = (tabs = []) => {
380
- for (const tab of tabs) {
381
- const data = getTabData(tab.id, this.currentData(), this.config);
382
- if (data && fieldId in data)
383
- return data[fieldId];
384
- if (tab.children) {
385
- const found = visit(tab.children);
386
- if (found !== FIELD_NOT_FOUND)
387
- return found;
388
- }
389
- }
390
- return FIELD_NOT_FOUND;
391
- };
392
- const result = visit(this.config?.tabs);
393
- return result === FIELD_NOT_FOUND ? undefined : result;
394
- }
395
- isModified = computed(() => {
396
- const orig = this.originalBaseline();
397
- const curr = this.currentData();
398
- if (!orig || !curr || Object.keys(orig).length === 0)
399
- return false;
400
- return JSON.stringify(orig) !== JSON.stringify(curr);
401
- });
402
- summaryFields = computed(() => {
403
- const fields = [];
404
- const walkTabs = (tabs = []) => {
405
- for (const t of tabs) {
406
- for (const f of t.fields ?? []) {
407
- if (f.showOnMinimize)
408
- fields.push(f);
409
- }
410
- if (t.children)
411
- walkTabs(t.children);
412
- }
413
- };
414
- walkTabs(this.config?.tabs);
415
- return fields;
416
- });
417
- onFormChange(data) {
418
- // Baseline is seeded from `initialData` in ngOnChanges; only fill it here when the
419
- // record was opened empty (create flow), so the first emitted shape becomes the baseline.
420
- if (Object.keys(this.originalBaseline()).length === 0) {
421
- this.originalBaseline.set({ ...data });
422
- }
423
- this.currentData.set(data);
424
- this.formChange.emit(data);
425
- }
426
- onFormSubmit(data) {
427
- this.formSubmit.emit(data);
428
- }
429
- onFormReset() {
430
- this.formReset.emit();
431
- }
432
- formatFieldLabel(field) {
433
- return resolveLabel(field.label, this.language);
434
- }
435
- /** Summary values render through the shared core formatter, not a local stringifier. */
436
- formatFieldValue(field) {
437
- return formatDisplayValue(field.type, field.options, this.fieldValue(field.id), this.language);
438
- }
439
- /**
440
- * Finds which tab — and, where the field lives one level down, which sub-tab — owns a
441
- * field. The walk used to check top-level `fields` only, so every field in a sub-tab was
442
- * simply not found and the jump did nothing at all.
443
- */
444
- locateField(fieldId) {
445
- for (const tab of this.config?.tabs || []) {
446
- if ((tab.fields || []).some(f => f.id === fieldId))
447
- return { tabId: tab.id };
448
- for (const child of tab.children || []) {
449
- if ((child.fields || []).some(f => f.id === fieldId)) {
450
- return { tabId: tab.id, subTabId: child.id };
451
- }
452
- }
453
- }
454
- return null;
455
- }
456
- /**
457
- * Switches to the tab holding `fieldId`, then scrolls it into view and moves focus to it.
458
- *
459
- * The wait for the new tab to render used to be a 50 ms `setTimeout`, which was wrong three
460
- * ways: `document` is undefined on a server render, 50 ms is a guess that a large tab can
461
- * outrun, and nothing cancelled the timer if the component went away first.
462
- * `afterNextRender` fixes all three — it never runs on the server, it runs when the panel
463
- * has actually rendered rather than when a guess expires, and it is tied to the injector so
464
- * destroying the component cancels it. The query is scoped to this component's own element,
465
- * so the library no longer reaches for the global `document` at all.
466
- */
467
- jumpToField(fieldId) {
468
- const location = this.locateField(fieldId);
469
- if (!location)
470
- return;
471
- this.dynamicFormComp?.setActiveTab(location.tabId);
472
- // `setActiveTab` resets to a tab's first child, so the sub-tab has to be selected after
473
- // it, not before. Without this the walk found sub-tab fields and then rendered the wrong
474
- // panel, so the element the callback below looks for never existed.
475
- if (location.subTabId)
476
- this.dynamicFormComp?.setActiveSubTab(location.subTabId);
477
- afterNextRender(() => {
478
- // The field id comes from config, so it never goes into a selector string: no
479
- // escaping to get wrong, and no need for `CSS.escape`, which jsdom and older
480
- // browsers do not provide and whose absence throws silently inside a render hook.
481
- const wanted = `field-container-${fieldId}`;
482
- const el = Array.from(this.host.nativeElement.querySelectorAll('[id^="field-container-"]')).find(slot => slot.id === wanted);
483
- if (!el)
484
- return;
485
- el.scrollIntoView({ behavior: 'smooth', block: 'center' });
486
- // The slot carries tabindex="-1" so this actually moves focus. Without it the jump
487
- // scrolled the field into view and left focus behind on the link.
488
- el.focus();
489
- }, { injector: this.injector });
490
- }
491
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicRecordFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
492
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: DynamicRecordFormComponent, isStandalone: true, selector: "ngx-dynamic-record-form", inputs: { config: "config", rules: "rules", initialData: "initialData", userRoles: "userRoles", language: "language", readonly: "readonly", loading: "loading", error: "error", isReadOnly: "isReadOnly", readOnlyFields: "readOnlyFields", viewMode: "viewMode" }, outputs: { formSubmit: "formSubmit", formChange: "formChange", formReset: "formReset", sectionSave: "sectionSave" }, viewQueries: [{ propertyName: "dynamicFormComp", first: true, predicate: DynamicFormComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"ngx-record-editor\">\n <!-- Record Header -->\n <div class=\"ngx-record-editor__header\">\n <div class=\"ngx-record-editor__title-group\">\n @if (profileImageUrl) {\n <img\n class=\"ngx-record-editor__avatar\"\n data-testid=\"header-profile-image\"\n [src]=\"profileImageUrl\"\n [alt]=\"recordTitle\"\n />\n } @else {\n <div class=\"ngx-record-editor__avatar\" data-testid=\"header-avatar-initial\">\n {{ avatarLetter }}\n </div>\n }\n <div>\n <h2 class=\"ngx-record-editor__title\" data-testid=\"record-title\">\n {{ recordTitle }}\n </h2>\n <span class=\"ngx-record-editor__subtitle\">Entity: {{ config.entity }}</span>\n </div>\n </div>\n\n <!-- Record-level status switch, outside section editing -->\n @if (headerToggleField; as toggleField) {\n <label class=\"ngx-record-editor__header-toggle\">\n <span>{{ formatFieldLabel(toggleField) }}</span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n data-testid=\"header-toggle\"\n [checked]=\"headerToggleValue\"\n [disabled]=\"recordReadOnly\"\n [attr.aria-checked]=\"headerToggleValue\"\n (change)=\"toggleHeaderStatus()\"\n />\n <span class=\"ngx-record-editor__header-toggle-text\">{{ headerToggleValue ? 'Yes' : 'No' }}</span>\n </label>\n }\n </div>\n\n <!-- Dismissible info banners (rules `info` action) -->\n @for (banner of infoBanners; track banner.fieldId) {\n <div\n class=\"ngx-record-editor__banner ngx-record-editor__banner--info\"\n role=\"status\"\n [attr.data-testid]=\"'info-banner-' + banner.fieldId\"\n >\n <span>\u2139\uFE0F {{ banner.message }}</span>\n <button\n type=\"button\"\n class=\"ngx-record-editor__banner-dismiss\"\n [attr.data-testid]=\"'dismiss-' + banner.fieldId\"\n [attr.aria-label]=\"'Dismiss: ' + banner.message\"\n (click)=\"dismissInfoBanner(banner.fieldId)\"\n >\u2715</button>\n </div>\n }\n\n <!-- Modification baseline banner -->\n @if (isModified()) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--warning\">\n \uD83D\uDD12 Record modified from session baseline \u2014 unsaved changes pending.\n </div>\n }\n\n <!-- Summary Panel (showOnMinimize fields with interactive jump) -->\n @if (summaryFields().length > 0) {\n <div class=\"ngx-record-editor__summary-panel\" data-testid=\"summary-panel\">\n @for (sf of summaryFields(); track sf.id) {\n <div\n class=\"ngx-record-editor__summary-item\"\n [attr.data-testid]=\"'summary-' + sf.id\"\n (click)=\"jumpToField(sf.id)\"\n [title]=\"'Click to jump to ' + formatFieldLabel(sf)\"\n >\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(sf) }}</span>\n <span class=\"ngx-record-editor__summary-val\">{{ formatFieldValue(sf) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Section edit bar: one tab is edited at a time, and validated on its own -->\n @if (viewMode && !recordReadOnly) {\n <div class=\"ngx-record-editor__section-bar\">\n @if (isEditingActiveTab) {\n <span class=\"ngx-record-editor__section-hint\">Editing this section</span>\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-section\" (click)=\"cancelSection()\">\n Cancel\n </button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-section\" (click)=\"saveSection()\">\n Save section\n </button>\n } @else {\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"edit-section\" (click)=\"editSection()\">\n Edit section\n </button>\n }\n </div>\n }\n\n @if (Object.keys(sectionErrors()).length > 0) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--error\" role=\"alert\" data-testid=\"section-errors\">\n @for (key of Object.keys(sectionErrors()); track key) {\n <div>\u26A0\uFE0F {{ sectionErrors()[key] }}</div>\n }\n </div>\n }\n\n <!-- Array rows: listed here, edited in the drawer below -->\n @for (arrayField of arrayFieldsForActiveTab; track arrayField.id) {\n <div class=\"ngx-record-editor__rows\" [attr.data-testid]=\"'rows-' + arrayField.id\">\n <div class=\"ngx-record-editor__rows-head\">\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(arrayField) }}</span>\n @if (!sectionReadOnly) {\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n [attr.data-testid]=\"'add-row-' + arrayField.id\"\n (click)=\"openAddRow(arrayField)\"\n >+ Row</button>\n }\n </div>\n\n @for (row of rowsOf(arrayField); track $index) {\n <div class=\"ngx-record-editor__row\" [attr.data-testid]=\"'row-' + arrayField.id + '-' + $index\">\n <span class=\"ngx-record-editor__row-text\">{{ rowSummary(arrayField, row) }}</span>\n @if (!sectionReadOnly) {\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'edit-row-' + arrayField.id + '-' + $index\"\n (click)=\"openEditRow(arrayField, $index)\">Edit</button>\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'delete-row-' + arrayField.id + '-' + $index\"\n (click)=\"deleteRow(arrayField, $index)\">Delete</button>\n }\n </div>\n } @empty {\n <p class=\"ngx-record-editor__row-empty\">No rows yet.</p>\n }\n </div>\n }\n\n <!--\n Row drawer. Deliberately outside the tab panel: building a row's FormGroup inside the\n tab view races the tab's own initialisation (the reference hit this with FormArray).\n -->\n @if (inlineRowField(); as rowField) {\n <div class=\"ngx-record-editor__drawer\" data-testid=\"row-drawer\" [formGroup]=\"inlineRowForm!\">\n <div class=\"ngx-record-editor__rows-head\">\n <strong>{{ inlineRowIndex() === null ? 'Add' : 'Edit' }} {{ formatFieldLabel(rowField) }} row</strong>\n </div>\n\n <div class=\"ngx-form__panel\">\n @for (child of rowField.children ?? []; track child.id) {\n <ngx-dynamic-field\n [field]=\"child\"\n [control]=\"inlineRowForm!.get(child.id)!\"\n [config]=\"config\"\n [language]=\"language\"\n [readonly]=\"false\"\n [userRoles]=\"userRoles\"\n [style.gridColumn]=\"'span 12'\"\n />\n }\n </div>\n\n <div class=\"ngx-record-editor__section-bar\">\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-row\" (click)=\"cancelRow()\">Cancel</button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-row\" (click)=\"saveRow()\">Save row</button>\n </div>\n </div>\n }\n\n <!-- Main Dynamic Form -->\n <ngx-dynamic-form\n [config]=\"config\"\n [rules]=\"rules\"\n [initialData]=\"initialData\"\n [originalData]=\"originalBaseline()\"\n [userRoles]=\"userRoles\"\n [language]=\"language\"\n [readonly]=\"sectionReadOnly\"\n [readOnlyFields]=\"readOnlyFields\"\n [showInfoBanners]=\"false\"\n [loading]=\"loading\"\n [error]=\"error\"\n (formChange)=\"onFormChange($event)\"\n (formSubmit)=\"onFormSubmit($event)\"\n (formReset)=\"onFormReset()\"\n (activeTabChange)=\"onActiveTabChange($event)\"\n />\n</div>\n", styles: [".ngx-record-editor{display:flex;flex-direction:column;gap:16px;background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:20px;box-shadow:0 4px 6px -1px #0000000d}.ngx-record-editor__header{display:flex;align-items:center;justify-content:space-between;padding-bottom:16px;border-bottom:1px solid #f3f4f6}.ngx-record-editor__title-group{display:flex;align-items:center;gap:12px}.ngx-record-editor__avatar{width:48px;height:48px;border-radius:50%;object-fit:cover;background:#e0e7ff;color:#4f46e5;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px}.ngx-record-editor__title{font-size:20px;font-weight:700;color:#111827;margin:0}.ngx-record-editor__subtitle{font-size:13px;color:#6b7280}.ngx-record-editor__header-toggle{display:flex;align-items:center;gap:8px;font-size:13px;color:#374151}.ngx-record-editor__header-toggle-text{font-weight:600}.ngx-record-editor__banner{padding:10px 14px;border-radius:8px;font-size:13px;font-weight:500}.ngx-record-editor__banner--info{background:#eff6ff;border:1px solid #dbeafe;color:#1d4ed8;display:flex;align-items:center;justify-content:space-between;gap:12px}.ngx-record-editor__banner-dismiss{background:none;border:none;cursor:pointer;color:inherit;font-size:14px;line-height:1;padding:2px 6px;border-radius:4px}.ngx-record-editor__banner-dismiss:hover{background:#0000000f}.ngx-record-editor__banner--error{background:#fef2f2;border:1px solid #fecaca;color:#b91c1c}.ngx-record-editor__section-bar{display:flex;align-items:center;justify-content:flex-end;gap:8px}.ngx-record-editor__section-hint{margin-right:auto;font-size:12px;color:#6b7280}.ngx-record-editor__banner--warning{background:#fffbeb;border:1px solid #fef3c7;color:#b45309}.ngx-record-editor__rows{border:1px solid #f3f4f6;border-radius:8px;padding:12px 14px;display:flex;flex-direction:column;gap:6px}.ngx-record-editor__rows-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.ngx-record-editor__row{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;background:#f9fafb}.ngx-record-editor__row-text{flex:1;font-size:13px;overflow-wrap:anywhere}.ngx-record-editor__row-btn{background:none;border:1px solid #e5e7eb;border-radius:6px;cursor:pointer;font-size:12px;padding:2px 8px}.ngx-record-editor__row-empty{font-size:12px;color:#6b7280;margin:0}.ngx-record-editor__drawer{border:1px solid #dbeafe;background:#f8fafc;border-radius:8px;padding:14px;display:flex;flex-direction:column;gap:12px}.ngx-record-editor__summary-panel{background:#f9fafb;border:1px solid #f3f4f6;border-radius:8px;padding:12px 16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}.ngx-record-editor__summary-item{display:flex;flex-direction:column;cursor:pointer;padding:6px 10px;border-radius:6px;transition:background .15s}.ngx-record-editor__summary-item:hover{background:#eff6ff}.ngx-record-editor__summary-label{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:#6b7280;font-weight:600}.ngx-record-editor__summary-val{font-size:14px;font-weight:600;color:#1f2937}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { 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: DynamicFormComponent, selector: "ngx-dynamic-form", inputs: ["config", "rules", "initialData", "originalData", "userRoles", "language", "readonly", "readOnlyFields", "showInfoBanners", "loading", "error", "changeDebounceMs", "preview"], outputs: ["formSubmit", "formChange", "formReset", "activeTabChange", "saveRejected"] }, { kind: "component", type: DynamicFieldComponent, selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "currentTabId", "language", "readonly", "userRoles"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
493
- }
494
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicRecordFormComponent, decorators: [{
495
- type: Component,
496
- args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ngx-dynamic-record-form', standalone: true, imports: [CommonModule, ReactiveFormsModule, DynamicFormComponent, DynamicFieldComponent], template: "<div class=\"ngx-record-editor\">\n <!-- Record Header -->\n <div class=\"ngx-record-editor__header\">\n <div class=\"ngx-record-editor__title-group\">\n @if (profileImageUrl) {\n <img\n class=\"ngx-record-editor__avatar\"\n data-testid=\"header-profile-image\"\n [src]=\"profileImageUrl\"\n [alt]=\"recordTitle\"\n />\n } @else {\n <div class=\"ngx-record-editor__avatar\" data-testid=\"header-avatar-initial\">\n {{ avatarLetter }}\n </div>\n }\n <div>\n <h2 class=\"ngx-record-editor__title\" data-testid=\"record-title\">\n {{ recordTitle }}\n </h2>\n <span class=\"ngx-record-editor__subtitle\">Entity: {{ config.entity }}</span>\n </div>\n </div>\n\n <!-- Record-level status switch, outside section editing -->\n @if (headerToggleField; as toggleField) {\n <label class=\"ngx-record-editor__header-toggle\">\n <span>{{ formatFieldLabel(toggleField) }}</span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n data-testid=\"header-toggle\"\n [checked]=\"headerToggleValue\"\n [disabled]=\"recordReadOnly\"\n [attr.aria-checked]=\"headerToggleValue\"\n (change)=\"toggleHeaderStatus()\"\n />\n <span class=\"ngx-record-editor__header-toggle-text\">{{ headerToggleValue ? 'Yes' : 'No' }}</span>\n </label>\n }\n </div>\n\n <!-- Dismissible info banners (rules `info` action) -->\n @for (banner of infoBanners; track banner.fieldId) {\n <div\n class=\"ngx-record-editor__banner ngx-record-editor__banner--info\"\n role=\"status\"\n [attr.data-testid]=\"'info-banner-' + banner.fieldId\"\n >\n <span>\u2139\uFE0F {{ banner.message }}</span>\n <button\n type=\"button\"\n class=\"ngx-record-editor__banner-dismiss\"\n [attr.data-testid]=\"'dismiss-' + banner.fieldId\"\n [attr.aria-label]=\"'Dismiss: ' + banner.message\"\n (click)=\"dismissInfoBanner(banner.fieldId)\"\n >\u2715</button>\n </div>\n }\n\n <!-- Modification baseline banner -->\n @if (isModified()) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--warning\">\n \uD83D\uDD12 Record modified from session baseline \u2014 unsaved changes pending.\n </div>\n }\n\n <!-- Summary Panel (showOnMinimize fields with interactive jump) -->\n @if (summaryFields().length > 0) {\n <div class=\"ngx-record-editor__summary-panel\" data-testid=\"summary-panel\">\n @for (sf of summaryFields(); track sf.id) {\n <div\n class=\"ngx-record-editor__summary-item\"\n [attr.data-testid]=\"'summary-' + sf.id\"\n (click)=\"jumpToField(sf.id)\"\n [title]=\"'Click to jump to ' + formatFieldLabel(sf)\"\n >\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(sf) }}</span>\n <span class=\"ngx-record-editor__summary-val\">{{ formatFieldValue(sf) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Section edit bar: one tab is edited at a time, and validated on its own -->\n @if (viewMode && !recordReadOnly) {\n <div class=\"ngx-record-editor__section-bar\">\n @if (isEditingActiveTab) {\n <span class=\"ngx-record-editor__section-hint\">Editing this section</span>\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-section\" (click)=\"cancelSection()\">\n Cancel\n </button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-section\" (click)=\"saveSection()\">\n Save section\n </button>\n } @else {\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"edit-section\" (click)=\"editSection()\">\n Edit section\n </button>\n }\n </div>\n }\n\n @if (Object.keys(sectionErrors()).length > 0) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--error\" role=\"alert\" data-testid=\"section-errors\">\n @for (key of Object.keys(sectionErrors()); track key) {\n <div>\u26A0\uFE0F {{ sectionErrors()[key] }}</div>\n }\n </div>\n }\n\n <!-- Array rows: listed here, edited in the drawer below -->\n @for (arrayField of arrayFieldsForActiveTab; track arrayField.id) {\n <div class=\"ngx-record-editor__rows\" [attr.data-testid]=\"'rows-' + arrayField.id\">\n <div class=\"ngx-record-editor__rows-head\">\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(arrayField) }}</span>\n @if (!sectionReadOnly) {\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n [attr.data-testid]=\"'add-row-' + arrayField.id\"\n (click)=\"openAddRow(arrayField)\"\n >+ Row</button>\n }\n </div>\n\n @for (row of rowsOf(arrayField); track $index) {\n <div class=\"ngx-record-editor__row\" [attr.data-testid]=\"'row-' + arrayField.id + '-' + $index\">\n <span class=\"ngx-record-editor__row-text\">{{ rowSummary(arrayField, row) }}</span>\n @if (!sectionReadOnly) {\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'edit-row-' + arrayField.id + '-' + $index\"\n (click)=\"openEditRow(arrayField, $index)\">Edit</button>\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'delete-row-' + arrayField.id + '-' + $index\"\n (click)=\"deleteRow(arrayField, $index)\">Delete</button>\n }\n </div>\n } @empty {\n <p class=\"ngx-record-editor__row-empty\">No rows yet.</p>\n }\n </div>\n }\n\n <!--\n Row drawer. Deliberately outside the tab panel: building a row's FormGroup inside the\n tab view races the tab's own initialisation (the reference hit this with FormArray).\n -->\n @if (inlineRowField(); as rowField) {\n <div class=\"ngx-record-editor__drawer\" data-testid=\"row-drawer\" [formGroup]=\"inlineRowForm!\">\n <div class=\"ngx-record-editor__rows-head\">\n <strong>{{ inlineRowIndex() === null ? 'Add' : 'Edit' }} {{ formatFieldLabel(rowField) }} row</strong>\n </div>\n\n <div class=\"ngx-form__panel\">\n @for (child of rowField.children ?? []; track child.id) {\n <ngx-dynamic-field\n [field]=\"child\"\n [control]=\"inlineRowForm!.get(child.id)!\"\n [config]=\"config\"\n [language]=\"language\"\n [readonly]=\"false\"\n [userRoles]=\"userRoles\"\n [style.gridColumn]=\"'span 12'\"\n />\n }\n </div>\n\n <div class=\"ngx-record-editor__section-bar\">\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-row\" (click)=\"cancelRow()\">Cancel</button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-row\" (click)=\"saveRow()\">Save row</button>\n </div>\n </div>\n }\n\n <!-- Main Dynamic Form -->\n <ngx-dynamic-form\n [config]=\"config\"\n [rules]=\"rules\"\n [initialData]=\"initialData\"\n [originalData]=\"originalBaseline()\"\n [userRoles]=\"userRoles\"\n [language]=\"language\"\n [readonly]=\"sectionReadOnly\"\n [readOnlyFields]=\"readOnlyFields\"\n [showInfoBanners]=\"false\"\n [loading]=\"loading\"\n [error]=\"error\"\n (formChange)=\"onFormChange($event)\"\n (formSubmit)=\"onFormSubmit($event)\"\n (formReset)=\"onFormReset()\"\n (activeTabChange)=\"onActiveTabChange($event)\"\n />\n</div>\n", styles: [".ngx-record-editor{display:flex;flex-direction:column;gap:16px;background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:20px;box-shadow:0 4px 6px -1px #0000000d}.ngx-record-editor__header{display:flex;align-items:center;justify-content:space-between;padding-bottom:16px;border-bottom:1px solid #f3f4f6}.ngx-record-editor__title-group{display:flex;align-items:center;gap:12px}.ngx-record-editor__avatar{width:48px;height:48px;border-radius:50%;object-fit:cover;background:#e0e7ff;color:#4f46e5;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px}.ngx-record-editor__title{font-size:20px;font-weight:700;color:#111827;margin:0}.ngx-record-editor__subtitle{font-size:13px;color:#6b7280}.ngx-record-editor__header-toggle{display:flex;align-items:center;gap:8px;font-size:13px;color:#374151}.ngx-record-editor__header-toggle-text{font-weight:600}.ngx-record-editor__banner{padding:10px 14px;border-radius:8px;font-size:13px;font-weight:500}.ngx-record-editor__banner--info{background:#eff6ff;border:1px solid #dbeafe;color:#1d4ed8;display:flex;align-items:center;justify-content:space-between;gap:12px}.ngx-record-editor__banner-dismiss{background:none;border:none;cursor:pointer;color:inherit;font-size:14px;line-height:1;padding:2px 6px;border-radius:4px}.ngx-record-editor__banner-dismiss:hover{background:#0000000f}.ngx-record-editor__banner--error{background:#fef2f2;border:1px solid #fecaca;color:#b91c1c}.ngx-record-editor__section-bar{display:flex;align-items:center;justify-content:flex-end;gap:8px}.ngx-record-editor__section-hint{margin-right:auto;font-size:12px;color:#6b7280}.ngx-record-editor__banner--warning{background:#fffbeb;border:1px solid #fef3c7;color:#b45309}.ngx-record-editor__rows{border:1px solid #f3f4f6;border-radius:8px;padding:12px 14px;display:flex;flex-direction:column;gap:6px}.ngx-record-editor__rows-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.ngx-record-editor__row{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;background:#f9fafb}.ngx-record-editor__row-text{flex:1;font-size:13px;overflow-wrap:anywhere}.ngx-record-editor__row-btn{background:none;border:1px solid #e5e7eb;border-radius:6px;cursor:pointer;font-size:12px;padding:2px 8px}.ngx-record-editor__row-empty{font-size:12px;color:#6b7280;margin:0}.ngx-record-editor__drawer{border:1px solid #dbeafe;background:#f8fafc;border-radius:8px;padding:14px;display:flex;flex-direction:column;gap:12px}.ngx-record-editor__summary-panel{background:#f9fafb;border:1px solid #f3f4f6;border-radius:8px;padding:12px 16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}.ngx-record-editor__summary-item{display:flex;flex-direction:column;cursor:pointer;padding:6px 10px;border-radius:6px;transition:background .15s}.ngx-record-editor__summary-item:hover{background:#eff6ff}.ngx-record-editor__summary-label{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:#6b7280;font-weight:600}.ngx-record-editor__summary-val{font-size:14px;font-weight:600;color:#1f2937}\n"] }]
497
- }], propDecorators: { config: [{
498
- type: Input
499
- }], rules: [{
500
- type: Input
501
- }], initialData: [{
502
- type: Input
503
- }], userRoles: [{
504
- type: Input
505
- }], language: [{
506
- type: Input
507
- }], readonly: [{
508
- type: Input
509
- }], loading: [{
510
- type: Input
511
- }], error: [{
512
- type: Input
513
- }], isReadOnly: [{
514
- type: Input
515
- }], readOnlyFields: [{
516
- type: Input
517
- }], viewMode: [{
518
- type: Input
519
- }], formSubmit: [{
520
- type: Output
521
- }], formChange: [{
522
- type: Output
523
- }], formReset: [{
524
- type: Output
525
- }], sectionSave: [{
526
- type: Output
527
- }], dynamicFormComp: [{
528
- type: ViewChild,
529
- args: [DynamicFormComponent]
530
- }] } });
531
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1yZWNvcmQtZm9ybS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2Zvcm0vZHluYW1pYy1yZWNvcmQtZm9ybS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL2Zvcm0vZHluYW1pYy1yZWNvcmQtZm9ybS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUE0QixNQUFNLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxlQUFlLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdE0sT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBOEIsbUJBQW1CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixPQUFPLEVBQUUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUM3RixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUNoRixPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQzs7O0FBRTlFOzs7R0FHRztBQUNILE1BQU0sZUFBZSxHQUFHLE1BQU0sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0FBRWxEOzs7O0dBSUc7QUFxTUgsTUFBTSxPQUFPLDBCQUEwQjtJQUM1QixNQUFNLENBQW9CO0lBQzFCLEtBQUssQ0FBYztJQUNuQixXQUFXLENBQXVCO0lBQ2xDLFNBQVMsR0FBYSxFQUFFLENBQUM7SUFDekIsUUFBUSxHQUFXLElBQUksQ0FBQztJQUN4QixRQUFRLEdBQVksS0FB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RUFDRCxFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQzVCLENBQUM7SUFDSixDQUFDO3dHQW5nQlUsMEJBQTBCOzRGQUExQiwwQkFBMEIsNmZBNEIxQixvQkFBb0IscUVDcFBqQyx1aFBBa01BLDBrR0QxS1ksWUFBWSw4QkFBRSxtQkFBbUIsNFVBQUUsb0JBQW9CLDZVQUFFLHFCQUFxQjs7NEZBZ003RSwwQkFBMEI7a0JBcE10QyxTQUFTO3NDQUNTLHVCQUF1QixDQUFDLE1BQU0sWUFDckMseUJBQXlCLGNBQ3ZCLElBQUksV0FDUCxDQUFDLFlBQVksRUFBRSxtQkFBbUIsRUFBRSxvQkFBb0IsRUFBRSxxQkFBcUIsQ0FBQzs4QkFpTWhGLE1BQU07c0JBQWQsS0FBSztnQkFDRyxLQUFLO3NCQUFiLEtBQUs7Z0JBQ0csV0FBVztzQkFBbkIsS0FBSztnQkFDRyxTQUFTO3NCQUFqQixLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csUUFBUTtzQkFBaEIsS0FBSztnQkFDRyxPQUFPO3NCQUFmLEtBQUs7Z0JBQ0csS0FBSztzQkFBYixLQUFLO2dCQUVHLFVBQVU7c0JBQWxCLEtBQUs7Z0JBRUcsY0FBYztzQkFBdEIsS0FBSztnQkFRRyxRQUFRO3NCQUFoQixLQUFLO2dCQUVJLFVBQVU7c0JBQW5CLE1BQU07Z0JBQ0csVUFBVTtzQkFBbkIsTUFBTTtnQkFDRyxTQUFTO3NCQUFsQixNQUFNO2dCQUVHLFdBQVc7c0JBQXBCLE1BQU07Z0JBRTBCLGVBQWU7c0JBQS9DLFNBQVM7dUJBQUMsb0JBQW9CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgSW5wdXQsIE91dHB1dCwgRXZlbnRFbWl0dGVyLCBPbkNoYW5nZXMsIFNpbXBsZUNoYW5nZXMsIHNpZ25hbCwgY29tcHV0ZWQsIGluamVjdCwgYWZ0ZXJOZXh0UmVuZGVyLCBFbGVtZW50UmVmLCBJbmplY3RvciwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQWJzdHJhY3RDb250cm9sLCBGb3JtR3JvdXAsIFJlYWN0aXZlRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgdHlwZSB7IEVudGl0eUZvcm1Db25maWcsIEZvcm1SdWxlLCBOZXN0ZWRGaWVsZENvbmZpZywgTmVzdGVkVGFiQ29uZmlnIH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xuaW1wb3J0IHsgZmluZFRhYiwgZm9ybWF0RGlzcGxheVZhbHVlLCBnZXRUYWJEYXRhLCByZXNvbHZlTGFiZWwgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XG5pbXBvcnQgeyBEeW5hbWljRm9ybUNvbXBvbmVudCB9IGZyb20gJy4vZHluYW1pYy1mb3JtLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBEeW5hbWljRmllbGRDb21wb25lbnQgfSBmcm9tICcuL2R5bmFtaWMtZmllbGQvZHluYW1pYy1maWVsZC5jb21wb25lbnQnO1xuaW1wb3J0IHsgUnVsZXNFdmFsdWF0aW9uU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL3J1bGVzLWV2YWx1YXRpb24uc2VydmljZSc7XG5cbi8qKlxuICogRGlzdGluZ3Vpc2hlcyBcIm5vIHRhYiBob2xkcyB0aGlzIGZpZWxkXCIgZnJvbSBcInRoZSBmaWVsZCBob2xkcyB1bmRlZmluZWRcIiwgc28gdGhlIHRhYiB3YWxrXG4gKiBjYW4ga2VlcCBzZWFyY2hpbmcgaW4gdGhlIGZpcnN0IGNhc2UgYW5kIHN0b3AgaW4gdGhlIHNlY29uZC5cbiAqL1xuY29uc3QgRklFTERfTk9UX0ZPVU5EID0gU3ltYm9sKCdmaWVsZC1ub3QtZm91bmQnKTtcblxuLyoqXG4gKiBEeW5hbWljUmVjb3JkRm9ybUNvbXBvbmVudCDigJQgY29tcHJlaGVuc2l2ZSB0YWJiZWQgcmVjb3JkIHZpZXcgJiBlZGl0IGNvbXBvbmVudC5cbiAqIFN1cHBvcnRzIGNyb3NzLXRhYiBydWxlIGV2YWx1YXRpb24sIHByb2ZpbGUgaGVhZGVyLCBzdW1tYXJ5IGRyYXdlciAoc2hvd09uTWluaW1pemUpLFxuICogaW50ZXJhY3RpdmUgcXVpY2stanVtcCBsaW5rcywgYW5kIGJhc2VsaW5lIG1vZGlmaWNhdGlvbiB0cmFja2luZy5cbiAqL1xuQENvbXBvbmVudCh7XG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBzZWxlY3RvcjogJ25neC1keW5hbWljLXJlY29yZC1mb3JtJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZSwgUmVhY3RpdmVGb3Jtc01vZHVsZSwgRHluYW1pY0Zvcm1Db21wb25lbnQsIER5bmFtaWNGaWVsZENvbXBvbmVudF0sXG4gIHRlbXBsYXRlVXJsOiAnLi9keW5hbWljLXJlY29yZC1mb3JtLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVzOiBbXG4gICAgYFxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yIHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgICAgICAgZ2FwOiAxNnB4O1xuICAgICAgICBiYWNrZ3JvdW5kOiAjZmZmZmZmO1xuICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjZTVlN2ViO1xuICAgICAgICBib3JkZXItcmFkaXVzOiAxMnB4O1xuICAgICAgICBwYWRkaW5nOiAyMHB4O1xuICAgICAgICBib3gtc2hhZG93OiAwIDRweCA2cHggLTFweCByZ2JhKDAsIDAsIDAsIDAuMDUpO1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19oZWFkZXIge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xuICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47XG4gICAgICAgIHBhZGRpbmctYm90dG9tOiAxNnB4O1xuICAgICAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2YzZjRmNjtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fdGl0bGUtZ3JvdXAge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xuICAgICAgICBnYXA6IDEycHg7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX2F2YXRhciB7XG4gICAgICAgIHdpZHRoOiA0OHB4O1xuICAgICAgICBoZWlnaHQ6IDQ4cHg7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDUwJTtcbiAgICAgICAgb2JqZWN0LWZpdDogY292ZXI7XG4gICAgICAgIGJhY2tncm91bmQ6ICNlMGU3ZmY7XG4gICAgICAgIGNvbG9yOiAjNGY0NmU1O1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xuICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjtcbiAgICAgICAgZm9udC13ZWlnaHQ6IDcwMDtcbiAgICAgICAgZm9udC1zaXplOiAxOHB4O1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX190aXRsZSB7XG4gICAgICAgIGZvbnQtc2l6ZTogMjBweDtcbiAgICAgICAgZm9udC13ZWlnaHQ6IDcwMDtcbiAgICAgICAgY29sb3I6ICMxMTE4Mjc7XG4gICAgICAgIG1hcmdpbjogMDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fc3VidGl0bGUge1xuICAgICAgICBmb250LXNpemU6IDEzcHg7XG4gICAgICAgIGNvbG9yOiAjNmI3MjgwO1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19oZWFkZXItdG9nZ2xlIHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICAgICAgZ2FwOiA4cHg7XG4gICAgICAgIGZvbnQtc2l6ZTogMTNweDtcbiAgICAgICAgY29sb3I6ICMzNzQxNTE7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX2hlYWRlci10b2dnbGUtdGV4dCB7XG4gICAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lciB7XG4gICAgICAgIHBhZGRpbmc6IDEwcHggMTRweDtcbiAgICAgICAgYm9yZGVyLXJhZGl1czogOHB4O1xuICAgICAgICBmb250LXNpemU6IDEzcHg7XG4gICAgICAgIGZvbnQtd2VpZ2h0OiA1MDA7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lci0taW5mbyB7XG4gICAgICAgIGJhY2tncm91bmQ6ICNlZmY2ZmY7XG4gICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNkYmVhZmU7XG4gICAgICAgIGNvbG9yOiAjMWQ0ZWQ4O1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xuICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47XG4gICAgICAgIGdhcDogMTJweDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLWRpc21pc3Mge1xuICAgICAgICBiYWNrZ3JvdW5kOiBub25lO1xuICAgICAgICBib3JkZXI6IG5vbmU7XG4gICAgICAgIGN1cnNvcjogcG9pbnRlcjtcbiAgICAgICAgY29sb3I6IGluaGVyaXQ7XG4gICAgICAgIGZvbnQtc2l6ZTogMTRweDtcbiAgICAgICAgbGluZS1oZWlnaHQ6IDE7XG4gICAgICAgIHBhZGRpbmc6IDJweCA2cHg7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLWRpc21pc3M6aG92ZXIge1xuICAgICAgICBiYWNrZ3JvdW5kOiByZ2JhKDAsIDAsIDAsIDAuMDYpO1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXItLWVycm9yIHtcbiAgICAgICAgYmFja2dyb3VuZDogI2ZlZjJmMjtcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2ZlY2FjYTtcbiAgICAgICAgY29sb3I6ICNiOTFjMWM7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3NlY3Rpb24tYmFyIHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICAgICAganVzdGlmeS1jb250ZW50OiBmbGV4LWVuZDtcbiAgICAgICAgZ2FwOiA4cHg7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3NlY3Rpb24taGludCB7XG4gICAgICAgIG1hcmdpbi1yaWdodDogYXV0bztcbiAgICAgICAgZm9udC1zaXplOiAxMnB4O1xuICAgICAgICBjb2xvcjogIzZiNzI4MDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLS13YXJuaW5nIHtcbiAgICAgICAgYmFja2dyb3VuZDogI2ZmZmJlYjtcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2ZlZjNjNztcbiAgICAgICAgY29sb3I6ICNiNDUzMDk7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3Jvd3Mge1xuICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjZjNmNGY2O1xuICAgICAgICBib3JkZXItcmFkaXVzOiA4cHg7XG4gICAgICAgIHBhZGRpbmc6IDEycHggMTRweDtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgICAgICAgZ2FwOiA2cHg7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3Jvd3MtaGVhZCB7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjtcbiAgICAgICAgZ2FwOiA4cHg7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3JvdyB7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICAgIGdhcDogOHB4O1xuICAgICAgICBwYWRkaW5nOiA2cHggOHB4O1xuICAgICAgICBib3JkZXItcmFkaXVzOiA2cHg7XG4gICAgICAgIGJhY2tncm91bmQ6ICNmOWZhZmI7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3Jvdy10ZXh0IHtcbiAgICAgICAgZmxleDogMTtcbiAgICAgICAgZm9udC1zaXplOiAxM3B4O1xuICAgICAgICBvdmVyZmxvdy13cmFwOiBhbnl3aGVyZTtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fcm93LWJ0biB7XG4gICAgICAgIGJhY2tncm91bmQ6IG5vbmU7XG4gICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNlNWU3ZWI7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDZweDtcbiAgICAgICAgY3Vyc29yOiBwb2ludGVyO1xuICAgICAgICBmb250LXNpemU6IDEycHg7XG4gICAgICAgIHBhZGRpbmc6IDJweCA4cHg7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3Jvdy1lbXB0eSB7XG4gICAgICAgIGZvbnQtc2l6ZTogMTJweDtcbiAgICAgICAgY29sb3I6ICM2YjcyODA7XG4gICAgICAgIG1hcmdpbjogMDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fZHJhd2VyIHtcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2RiZWFmZTtcbiAgICAgICAgYmFja2dyb3VuZDogI2Y4ZmFmYztcbiAgICAgICAgYm9yZGVyLXJhZGl1czogOHB4O1xuICAgICAgICBwYWRkaW5nOiAxNHB4O1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICAgICAgICBnYXA6IDEycHg7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3N1bW1hcnktcGFuZWwge1xuICAgICAgICBiYWNrZ3JvdW5kOiAjZjlmYWZiO1xuICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjZjNmNGY2O1xuICAgICAgICBib3JkZXItcmFkaXVzOiA4cHg7XG4gICAgICAgIHBhZGRpbmc6IDEycHggMTZweDtcbiAgICAgICAgZGlzcGxheTogZ3JpZDtcbiAgICAgICAgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiByZXBlYXQoYXV0by1maXQsIG1pbm1heCgxODBweCwgMWZyKSk7XG4gICAgICAgIGdhcDogMTJweDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fc3VtbWFyeS1pdGVtIHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgICAgICAgY3Vyc29yOiBwb2ludGVyO1xuICAgICAgICBwYWRkaW5nOiA2cHggMTBweDtcbiAgICAgICAgYm9yZGVyLXJhZGl1czogNnB4O1xuICAgICAgICB0cmFuc2l0aW9uOiBiYWNrZ3JvdW5kIDAuMTVzO1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zdW1tYXJ5LWl0ZW06aG92ZXIge1xuICAgICAgICBiYWNrZ3JvdW5kOiAjZWZmNmZmO1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zdW1tYXJ5LWxhYmVsIHtcbiAgICAgICAgZm9udC1zaXplOiAxMXB4O1xuICAgICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICAgICAgICBsZXR0ZXItc3BhY2luZzogMC4wNWVtO1xuICAgICAgICBjb2xvcjogIzZiNzI4MDtcbiAgICAgICAgZm9udC13ZWlnaHQ6IDYwMDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fc3VtbWFyeS12YWwge1xuICAgICAgICBmb250LXNpemU6IDE0cHg7XG4gICAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7XG4gICAgICAgIGNvbG9yOiAjMWYyOTM3O1xuICAgICAgfVxuICAgIGAsXG4gIF0sXG59KVxuZXhwb3J0IGNsYXNzIER5bmFtaWNSZWNvcmRGb3JtQ29tcG9uZW50IGltcGxlbWVudHMgT25DaGFuZ2VzIHtcbiAgQElucHV0KCkgY29uZmlnITogRW50aXR5Rm9ybUNvbmZpZztcbiAgQElucHV0KCkgcnVsZXM/OiBGb3JtUnVsZVtdO1xuICBASW5wdXQoKSBpbml0aWFsRGF0YT86IFJlY29yZDxzdHJpbmcsIGFueT47XG4gIEBJbnB1dCgpIHVzZXJSb2xlczogc3RyaW5nW10gPSBbXTtcbiAgQElucHV0KCkgbGFuZ3VhZ2U6IHN0cmluZyA9ICdlbic7XG4gIEBJbnB1dCgpIHJlYWRvbmx5OiBib29sZWFuID0gZmFsc2U7XG4gIEBJbnB1dCgpIGxvYWRpbmc6IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgZXJyb3I6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAvKiogV2hvbGUgcmVjb3JkIHJlYWQtb25seSwgcmVnYXJkbGVzcyBvZiBSQkFDLiBFcXVpdmFsZW50IHRvIHRoZSByZWZlcmVuY2UncyBgaXNSZWFkT25seWAuICovXG4gIEBJbnB1dCgpIGlzUmVhZE9ubHk6IGJvb2xlYW4gPSBmYWxzZTtcbiAgLyoqIFNwZWNpZmljIGZpZWxkIGlkcyBmb3JjZWQgcmVhZC1vbmx5IHdoaWxlIHRoZSByZXN0IG9mIHRoZSByZWNvcmQgc3RheXMgZWRpdGFibGUuICovXG4gIEBJbnB1dCgpIHJlYWRPbmx5RmllbGRzOiBzdHJpbmdbXSA9IFtdO1xuICAvKipcbiAgICogT3BlbiB0aGUgcmVjb3JkIHJlYWQtb25seSwgd2l0aCBhIHBlci10YWIgXCJFZGl0IHNlY3Rpb25cIiBmbG93IOKAlCBvbmUgdGFiIGlzIGVkaXRlZCBhbmRcbiAgICogdmFsaWRhdGVkIGF0IGEgdGltZS4gVGhpcyBpcyB0aGUgZnJhbWV3b3JrJ3MgYEVudGl0eVJlY29yZENvbXBvbmVudGAgbW9kZWwgYW5kIHRoZVxuICAgKiBkZWZhdWx0IGhlcmUuXG4gICAqXG4gICAqIFNldCBmYWxzZSBmb3IgYSBkaXJlY3RseSBlZGl0YWJsZSByZWNvcmQgd2l0aCBubyB2aWV3L2VkaXQgZGlzdGluY3Rpb24uXG4gICAqL1xuICBASW5wdXQoKSB2aWV3TW9kZTogYm9vbGVhbiA9IHRydWU7XG5cbiAgQE91dHB1dCgpIGZvcm1TdWJtaXQgPSBuZXcgRXZlbnRFbWl0dGVyPFJlY29yZDxzdHJpbmcsIGFueT4+KCk7XG4gIEBPdXRwdXQoKSBmb3JtQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxSZWNvcmQ8c3RyaW5nLCBhbnk+PigpO1xuICBAT3V0cHV0KCkgZm9ybVJlc2V0ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKiogT25lIHRhYiBzZWN0aW9uIHdhcyBzYXZlZC4gQ2FycmllcyB0aGUgd2hvbGUgcmVjb3JkLCBwbHVzIHdoaWNoIHRhYiB3YXMgZWRpdGVkLiAqL1xuICBAT3V0cHV0KCkgc2VjdGlvblNhdmUgPSBuZXcgRXZlbnRFbWl0dGVyPHsgdGFiSWQ6IHN0cmluZzsgcmVjb3JkOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IH0+KCk7XG5cbiAgQFZpZXdDaGlsZChEeW5hbWljRm9ybUNvbXBvbmVudCkgZHluYW1pY0Zvcm1Db21wPzogRHluYW1pY0Zvcm1Db21wb25lbnQ7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBydWxlc0V2YWx1YXRpb24gPSBpbmplY3QoUnVsZXNFdmFsdWF0aW9uU2VydmljZSk7XG4gIHByaXZhdGUgcmVhZG9ubHkgaG9zdDogRWxlbWVudFJlZjxIVE1MRWxlbWVudD4gPSBpbmplY3QoRWxlbWVudFJlZik7XG4gIHByaXZhdGUgcmVhZG9ubHkgaW5qZWN0b3IgPSBpbmplY3QoSW5qZWN0b3IpO1xuXG4gIHJlYWRvbmx5IGN1cnJlbnREYXRhID0gc2lnbmFsPFJlY29yZDxzdHJpbmcsIGFueT4+KHt9KTtcbiAgcmVhZG9ubHkgb3JpZ2luYWxCYXNlbGluZSA9IHNpZ25hbDxSZWNvcmQ8c3RyaW5nLCBhbnk+Pih7fSk7XG5cbiAgLyoqXG4gICAqIEluZm8gYmFubmVycyB0aGUgdXNlciBoYXMgZGlzbWlzc2VkLCBieSBmaWVsZCBpZC5cbiAgICpcbiAgICogVGhlIHJlZmVyZW5jZSdzIGNvbnRyYWN0OiBhbiBpbmZvIGJhbm5lciBwZXJzaXN0cyB1bnRpbCBkaXNtaXNzZWQsICoqbm90KiogdW50aWwgdGhlXG4gICAqIHZhbHVlIGNoYW5nZXMuIERpc21pc3NhbCBpcyBrZXllZCBieSBmaWVsZCBzbyByZS10cmlnZ2VyaW5nIGEgZGlmZmVyZW50IGZpZWxkJ3MgcnVsZVxuICAgKiBzdGlsbCBzaG93cy4gUmUtYXJtZWQgd2hlbiBhIGRpZmZlcmVudCByZWNvcmQgaXMgbG9hZGVkLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBkaXNtaXNzZWQgPSBzaWduYWw8UmVhZG9ubHlTZXQ8c3RyaW5nPj4obmV3IFNldDxzdHJpbmc+KCkpO1xuXG4gIC8qKlxuICAgKiBJbmZvIGJhbm5lcnMgZnJvbSB0aGUgcnVsZXMgZW5naW5lIHRoYXQgYXJlIHN0aWxsIHNob3dpbmcuXG4gICAqXG4gICAqIEEgZ2V0dGVyLCBub3QgYSBgY29tcHV0ZWRgOiB0aGUgc291cmNlIGlzIGBkeW5hbWljRm9ybUNvbXBgLCBhIFZpZXdDaGlsZC4gQSBjb21wdXRlZFxuICAgKiBldmFsdWF0ZWQgYmVmb3JlIHRoZSB2aWV3IGluaXRpYWxpc2VzIHdvdWxkIGNhcHR1cmUgbm8gZGVwZW5kZW5jeSBvbiB0aGUgY2hpbGQnc1xuICAgKiBgcnVsZVJlc3VsdGAgc2lnbmFsIGFuZCB0aGVuIG5ldmVyIHJlY29tcHV0ZS4gUmVhZCBwZXIgY2hhbmdlLWRldGVjdGlvbiBwYXNzIGluc3RlYWQuXG4gICAqL1xuICBnZXQgaW5mb0Jhbm5lcnMoKTogeyBmaWVsZElkOiBzdHJpbmc7IG1lc3NhZ2U6IHN0cmluZyB9W10ge1xuICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuZHluYW1pY0Zvcm1Db21wPy5ydWxlUmVzdWx0KCk7XG4gICAgY29uc3QgZ29uZSA9IHRoaXMuZGlzbWlzc2VkKCk7XG4gICAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKHJlc3VsdD8uaW5mb0Jhbm5lcnMgPz8ge30pXG4gICAgICAuZmlsdGVyKChbZmllbGRJZF0pID0+ICFnb25lLmhhcyhmaWVsZElkKSlcbiAgICAgIC5tYXAoKFtmaWVsZElkLCBtZXNzYWdlXSkgPT4gKHsgZmllbGRJZCwgbWVzc2FnZTogU3RyaW5nKG1lc3NhZ2UpIH0pKTtcbiAgfVxuXG4gIGRpc21pc3NJbmZvQmFubmVyKGZpZWxkSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuZGlzbWlzc2VkLnNldChuZXcgU2V0KHRoaXMuZGlzbWlzc2VkKCkpLmFkZChmaWVsZElkKSk7XG4gIH1cblxuICAvKiogVHJ1ZSB3aGVuIG5vdGhpbmcgaW4gdGhlIHJlY29yZCBtYXkgYmUgZWRpdGVkLiAqL1xuICBnZXQgcmVjb3JkUmVhZE9ubHkoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMucmVhZG9ubHkgfHwgdGhpcy5pc1JlYWRPbmx5O1xuICB9XG5cbiAgLy8g4pSA4pSA4pSAIElubGluZSBhcnJheS1yb3cgZWRpdGluZyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKipcbiAgICogQSByb3cgb2YgYW4gYGFycmF5YCBmaWVsZCBpcyBlZGl0ZWQgaW4gYSBkcmF3ZXIsIG5vdCBpbmxpbmUgaW4gdGhlIHRhYiBwYW5lbC5cbiAgICpcbiAgICogVGhlIGRyYXdlciBpcyByZW5kZXJlZCBvdXRzaWRlIHRoZSB0YWIgY29udGFpbmVyIG9uIHB1cnBvc2U6IHRoZSByZWZlcmVuY2UgZm91bmQgdGhhdFxuICAgKiBidWlsZGluZyBhIHJvdydzIEZvcm1Hcm91cCBpbnNpZGUgdGhlIHRhYiB2aWV3IHJhY2VzIHRoZSB0YWIncyBvd24gaW5pdGlhbGlzYXRpb24sIHNvXG4gICAqIHRoZSByb3cgZm9ybSBpcyBrZXB0IGRldGFjaGVkIGFuZCBvbmx5IHB1c2hlZCBpbnRvIHRoZSBGb3JtQXJyYXkgb24gc2F2ZS5cbiAgICovXG4gIHJlYWRvbmx5IGlubGluZVJvd0ZpZWxkID0gc2lnbmFsPE5lc3RlZEZpZWxkQ29uZmlnIHwgbnVsbD4obnVsbCk7XG4gIHJlYWRvbmx5IGlubGluZVJvd0luZGV4ID0gc2lnbmFsPG51bWJlciB8IG51bGw+KG51bGwpO1xuICBpbmxpbmVSb3dGb3JtOiBGb3JtR3JvdXAgfCBudWxsID0gbnVsbDtcblxuICAvKiogYGFycmF5YCBmaWVsZHMgb24gdGhlIHRhYiBjdXJyZW50bHkgc2hvd24sIHNvIHRoZSB0ZW1wbGF0ZSBjYW4gbGlzdCB0aGVpciByb3dzLiAqL1xuICBnZXQgYXJyYXlGaWVsZHNGb3JBY3RpdmVUYWIoKTogTmVzdGVkRmllbGRDb25maWdbXSB7XG4gICAgY29uc3QgdGFiSWQgPSB0aGlzLmFjdGl2ZVRhYklkO1xuICAgIGNvbnN0IHRhYiA9IHRhYklkID8gZmluZFRhYih0aGlzLmNvbmZpZz8udGFicywgdGFiSWQpIDogbnVsbDtcbiAgICByZXR1cm4gKHRhYj8uZmllbGRzID8/IFtdKS5maWx0ZXIoZiA9PiBmLnR5cGUgPT09ICdhcnJheScpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJvd3MgY3VycmVudGx5IGhlbGQgYnkgYW4gYGFycmF5YCBmaWVsZCwgcmVhZCBmcm9tIHRoZSByZWNvcmQgdmFsdWUuXG4gICAqXG4gICAqIERlbGliZXJhdGVseSBub3QgcmVhZCBvZmYgdGhlIGNoaWxkJ3MgRm9ybUFycmF5OiB0aGF0IGNvbnRyb2wgb25seSBleGlzdHMgb25jZSB0aGVcbiAgICogY2hpbGQgaGFzIGluaXRpYWxpc2VkLCBzbyByZW5kZXJpbmcgZnJvbSBpdCBjaGFuZ2VkIHZhbHVlIG1pZC1wYXNzLiBgY3VycmVudERhdGFgIGlzIGFcbiAgICogc2lnbmFsIHNlZWRlZCBmcm9tIGBpbml0aWFsRGF0YWAgYW5kIGtlcHQgY3VycmVudCBieSB0aGUgY2hpbGQncyBgZm9ybUNoYW5nZWAuXG4gICAqL1xuICByb3dzT2YoZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSB7XG4gICAgY29uc3QgdGFiSWQgPSB0aGlzLmFjdGl2ZVRhYklkO1xuICAgIGNvbnN0IHRhYkRhdGEgPSB0YWJJZCA/IGdldFRhYkRhdGEodGFiSWQsIHRoaXMuY3VycmVudERhdGEoKSwgdGhpcy5jb25maWcpIDogbnVsbDtcbiAgICBjb25zdCByb3dzID0gKHRhYkRhdGEgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCBudWxsKT8uW2ZpZWxkLmlkXTtcbiAgICByZXR1cm4gQXJyYXkuaXNBcnJheShyb3dzKSA/IChyb3dzIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+W10pIDogW107XG4gIH1cblxuICAvKiogT25lIHJvdyByZW5kZXJlZCBhcyB0ZXh0LCBzbyB0aGUgbGlzdCBpcyByZWFkYWJsZSB3aXRob3V0IG9wZW5pbmcgdGhlIGRyYXdlci4gKi9cbiAgcm93U3VtbWFyeShmaWVsZDogTmVzdGVkRmllbGRDb25maWcsIHJvdzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBzdHJpbmcge1xuICAgIGNvbnN0IHZhbHVlID0gcm93ID8/IHt9O1xuICAgIGNvbnN0IHBhcnRzID0gKGZpZWxkLmNoaWxkcmVuID8/IFtdKVxuICAgICAgLm1hcChjaGlsZCA9PiB2YWx1ZVtjaGlsZC5pZF0pXG4gICAgICAuZmlsdGVyKHYgPT4gdiAhPT0gbnVsbCAmJiB2ICE9PSB1bmRlZmluZWQgJiYgdiAhPT0gJycpXG4gICAgICAubWFwKHYgPT4gKHR5cGVvZiB2ID09PSAnb2JqZWN0JyA/IEpTT04uc3RyaW5naWZ5KHYpIDogU3RyaW5nKHYpKSk7XG4gICAgcmV0dXJuIHBhcnRzLmxlbmd0aCA/IHBhcnRzLmpvaW4oJyDCtyAnKSA6ICcoZW1wdHkpJztcbiAgfVxuXG4gIG9wZW5BZGRSb3coZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuc2VjdGlvblJlYWRPbmx5KSByZXR1cm47XG4gICAgdGhpcy5pbmxpbmVSb3dGaWVsZC5zZXQoZmllbGQpO1xuICAgIHRoaXMuaW5saW5lUm93SW5kZXguc2V0KG51bGwpO1xuICAgIHRoaXMuaW5saW5lUm93Rm9ybSA9IHRoaXMuZHluYW1pY0Zvcm1Db21wPy5jcmVhdGVBcnJheVJvdyhmaWVsZCkgYXMgRm9ybUdyb3VwO1xuICB9XG5cbiAgb3BlbkVkaXRSb3coZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnLCBpbmRleDogbnVtYmVyKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuc2VjdGlvblJlYWRPbmx5KSByZXR1cm47XG4gICAgdGhpcy5pbmxpbmVSb3dGaWVsZC5zZXQoZmllbGQpO1xuICAgIHRoaXMuaW5saW5lUm93SW5kZXguc2V0KGluZGV4KTtcbiAgICB0aGlzLmlubGluZVJvd0Zvcm0gPSB0aGlzLmR5bmFtaWNGb3JtQ29tcD8uY3JlYXRlQXJyYXlSb3coZmllbGQsIHRoaXMucm93c09mKGZpZWxkKVtpbmRleF0pIGFzIEZvcm1Hcm91cDtcbiAgfVxuXG4gIGNhbmNlbFJvdygpOiB2b2lkIHtcbiAgICB0aGlzLmlubGluZVJvd0ZpZWxkLnNldChudWxsKTtcbiAgICB0aGlzLmlubGluZVJvd0luZGV4LnNldChudWxsKTtcbiAgICB0aGlzLmlubGluZVJvd0Zvcm0gPSBudWxsO1xuICB9XG5cbiAgLyoqIENvbW1pdCB0aGUgZHJhd2VyIGludG8gdGhlIEZvcm1BcnJheS4gUnVsZXMgcmUtZXZhbHVhdGUgb2ZmIHRoZSByZXN1bHRpbmcgdmFsdWUgY2hhbmdlLiAqL1xuICBzYXZlUm93KCk6IHZvaWQge1xuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5pbmxpbmVSb3dGaWVsZCgpO1xuICAgIGNvbnN0IGZvcm0gPSB0aGlzLmR5bmFtaWNGb3JtQ29tcDtcbiAgICBpZiAoIWZpZWxkIHx8ICFmb3JtIHx8ICF0aGlzLmlubGluZVJvd0Zvcm0pIHJldHVybjtcblxuICAgIGlmICh0aGlzLmlubGluZVJvd0Zvcm0uaW52YWxpZCkge1xuICAgICAgdGhpcy5pbmxpbmVSb3dGb3JtLm1hcmtBbGxBc1RvdWNoZWQoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBhcnJheSA9IGZvcm0uZ2V0QXJyYXlDb250cm9sKGZpZWxkLmlkLCB0aGlzLmFjdGl2ZVRhYklkID8/IHVuZGVmaW5lZCk7XG4gICAgaWYgKCFhcnJheSkgcmV0dXJuO1xuXG4gICAgY29uc3QgaW5kZXggPSB0aGlzLmlubGluZVJvd0luZGV4KCk7XG4gICAgaWYgKGluZGV4ID09PSBudWxsKSBhcnJheS5wdXNoKGZvcm0uY3JlYXRlQXJyYXlSb3coZmllbGQsIHRoaXMuaW5saW5lUm93Rm9ybS52YWx1ZSkpO1xuICAgIGVsc2UgYXJyYXkuYXQoaW5kZXgpLnBhdGNoVmFsdWUodGhpcy5pbmxpbmVSb3dGb3JtLnZhbHVlKTtcblxuICAgIGFycmF5LnVwZGF0ZVZhbHVlQW5kVmFsaWRpdHkoKTtcbiAgICB0aGlzLmN1cnJlbnREYXRhLnNldChmb3JtLmV4dHJhY3RSZWNvcmQoKSk7XG4gICAgdGhpcy5jYW5jZWxSb3coKTtcbiAgfVxuXG4gIGRlbGV0ZVJvdyhmaWVsZDogTmVzdGVkRmllbGRDb25maWcsIGluZGV4OiBudW1iZXIpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5zZWN0aW9uUmVhZE9ubHkpIHJldHVybjtcbiAgICBjb25zdCBhcnJheSA9IHRoaXMuZHluYW1pY0Zvcm1Db21wPy5nZXRBcnJheUNvbnRyb2woZmllbGQuaWQsIHRoaXMuYWN0aXZlVGFiSWQgPz8gdW5kZWZpbmVkKTtcbiAgICBhcnJheT8ucmVtb3ZlQXQoaW5kZXgpO1xuICAgIGFycmF5Py51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KCk7XG4gICAgaWYgKHRoaXMuZHluYW1pY0Zvcm1Db21wKSB0aGlzLmN1cnJlbnREYXRhLnNldCh0aGlzLmR5bmFtaWNGb3JtQ29tcC5leHRyYWN0UmVjb3JkKCkpO1xuICAgIGlmICh0aGlzLmlubGluZVJvd0luZGV4KCkgPT09IGluZGV4KSB0aGlzLmNhbmNlbFJvdygpO1xuICB9XG5cbiAgLy8g4pSA4pSA4pSAIFBlci10YWIgc2VjdGlvbiBlZGl0aW5nIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKlxuICAgKiBUaGUgdGFiIGN1cnJlbnRseSBvcGVuIGZvciBlZGl0aW5nLCBvciBudWxsIHdoZW4gdGhlIHJlY29yZCBpcyBiZWluZyB2aWV3ZWQuXG4gICAqXG4gICAqIFRoZSByZWZlcmVuY2UgZWRpdHMgb25lIHRhYiBzZWN0aW9uIGF0IGEgdGltZSByYXRoZXIgdGhhbiB0aGUgd2hvbGUgcmVjb3JkLCBhbmRcbiAgICogdmFsaWRhdGVzIG9ubHkgdGhhdCBzZWN0aW9uIG9uIHNhdmUg4oCUIGEgcmVxdWlyZWQgZmllbGQgb24gYW4gdW50b3VjaGVkIHRhYiBtdXN0IG5vdFxuICAgKiBibG9jayBzYXZpbmcgdGhlIHRhYiB5b3UgYXJlIGFjdHVhbGx5IHdvcmtpbmcgb24uXG4gICAqL1xuICByZWFkb25seSBlZGl0aW5nVGFiSWQgPSBzaWduYWw8c3RyaW5nIHwgbnVsbD4obnVsbCk7XG5cbiAgLyoqXG4gICAqIFRoZSB0YWIgdGhlIGlubmVyIGZvcm0gaXMgc2hvd2luZy5cbiAgICpcbiAgICogVHJhY2tlZCBoZXJlIHJhdGhlciB0aGFuIHJlYWQgb2ZmIHRoZSBWaWV3Q2hpbGQuIFRoZSBjaGlsZCByZXNvbHZlcyBpdHMgZmlyc3QgdGFiIGFzXG4gICAqIHBhcnQgb2YgaXRzIG93biBpbml0aWFsaXNhdGlvbiwgc28gcmVhZGluZyBpdCBkdXJpbmcgdGhpcyBjb21wb25lbnQncyByZW5kZXIgY2hhbmdlZFxuICAgKiB2YWx1ZSBtaWQtcGFzcyDigJQgYW4gRXhwcmVzc2lvbkNoYW5nZWRBZnRlckl0SGFzQmVlbkNoZWNrZWQgZXJyb3IuIFNlZWRlZCBmcm9tIHRoZVxuICAgKiBjb25maWcgYW5kIGtlcHQgaW4gc3RlcCBieSB0aGUgY2hpbGQncyBgYWN0aXZlVGFiQ2hhbmdlYCBvdXRwdXQuXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGFjdGl2ZVRhYklkU2lnID0gc2lnbmFsPHN0cmluZyB8IG51bGw+KG51bGwpO1xuXG4gIGdldCBhY3RpdmVUYWJJZCgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5hY3RpdmVUYWJJZFNpZygpO1xuICB9XG5cbiAgb25BY3RpdmVUYWJDaGFuZ2UodGFiSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh0aGlzLmFjdGl2ZVRhYklkU2lnKCkgPT09IHRhYklkKSByZXR1cm47XG4gICAgdGhpcy5hY3RpdmVUYWJJZFNpZy5zZXQodGFiSWQpO1xuICAgIC8vIEVkaXRpbmcgZG9lcyBub3QgZm9sbG93IHRoZSB1c2VyIHRvIGFub3RoZXIgdGFiLlxuICAgIHRoaXMuZWRpdGluZ1RhYklkLnNldChudWxsKTtcbiAgICB0aGlzLmNhbmNlbFJvdygpO1xuICB9XG5cbiAgLyoqIEZpcnN0IHRhYiBhIHVzZXIgY2FuIHNlZSwgdXNlZCB0byBzZWVkIHRoZSBhY3RpdmUgdGFiIGJlZm9yZSB0aGUgY2hpbGQgcmVwb3J0cyBvbmUuICovXG4gIHByaXZhdGUgZmlyc3RWaXNpYmxlVGFiSWQoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnPy50YWJzPy5maW5kKHQgPT4gdC52aXNpYmlsaXR5ICE9PSBmYWxzZSk/LmlkID8/IG51bGw7XG4gIH1cblxuICAvKiogRXJyb3JzIGZyb20gdGhlIGxhc3Qgc2F2ZSBhdHRlbXB0LCBieSBmaWVsZCBpZC4gKi9cbiAgcmVhZG9ubHkgc2VjdGlvbkVycm9ycyA9IHNpZ25hbDxSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+Pih7fSk7XG5cbiAgLyoqIFRlbXBsYXRlIGFjY2VzcyB0byBgT2JqZWN0LmtleXNgIGZvciBpdGVyYXRpbmcgdGhlIGVycm9yIG1hcC4gKi9cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IE9iamVjdCA9IE9iamVjdDtcblxuICBnZXQgaXNFZGl0aW5nQWN0aXZlVGFiKCk6IGJvb2xlYW4ge1xuICAgIC8vIFdpdGhvdXQgdmlldyBtb2RlIHRoZXJlIGlzIG5vIHZpZXcvZWRpdCBkaXN0aW5jdGlvbiDigJQgZXZlcnl0aGluZyBpcyBhbHdheXMgZWRpdGFibGUuXG4gICAgaWYgKCF0aGlzLnZpZXdNb2RlKSByZXR1cm4gdHJ1ZTtcbiAgICByZXR1cm4gISF0aGlzLmFjdGl2ZVRhYklkICYmIHRoaXMuZWRpdGluZ1RhYklkKCkgPT09IHRoaXMuYWN0aXZlVGFiSWQ7XG4gIH1cblxuICAvKiogRmllbGRzIHJlbmRlciByZWFkLW9ubHkgdW5sZXNzIHRoZWlyIG93biB0YWIgaXMgdGhlIG9uZSBiZWluZyBlZGl0ZWQuICovXG4gIGdldCBzZWN0aW9uUmVhZE9ubHkoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMucmVjb3JkUmVhZE9ubHkgfHwgIXRoaXMuaXNFZGl0aW5nQWN0aXZlVGFiO1xuICB9XG5cbiAgZWRpdFNlY3Rpb24oKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnZpZXdNb2RlIHx8IHRoaXMucmVjb3JkUmVhZE9ubHkgfHwgIXRoaXMuYWN0aXZlVGFiSWQpIHJldHVybjtcbiAgICB0aGlzLnNlY3Rpb25FcnJvcnMuc2V0KHt9KTtcbiAgICB0aGlzLmVkaXRpbmdUYWJJZC5zZXQodGhpcy5hY3RpdmVUYWJJZCk7XG4gIH1cblxuICAvKiogRGlzY2FyZCB0aGlzIHNlY3Rpb24ncyBlZGl0cywgcmVzdG9yaW5nIGVhY2ggZmllbGQgZnJvbSB0aGUgc2Vzc2lvbiBiYXNlbGluZS4gKi9cbiAgY2FuY2VsU2VjdGlvbigpOiB2b2lkIHtcbiAgICBjb25zdCB0YWJJZCA9IHRoaXMuZWRpdGluZ1RhYklkKCk7XG4gICAgY29uc3QgZm9ybSA9IHRoaXMuZHluYW1pY0Zvcm1Db21wO1xuICAgIGlmICh0YWJJZCAmJiBmb3JtKSB7XG4gICAgICBjb25zdCB0YWIgPSBmaW5kVGFiKHRoaXMuY29uZmlnPy50YWJzLCB0YWJJZCk7XG4gICAgICBjb25zdCBiYXNlbGluZVRhYiA9IChnZXRUYWJEYXRhKHRhYklkLCB0aGlzLm9yaWdpbmFsQmFzZWxpbmUoKSwgdGhpcy5jb25maWcpID8/IHt9KSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGFiPy5maWVsZHMgPz8gW10pIHtcbiAgICAgICAgY29uc3QgYmFzZWxpbmVWYWx1ZSA9IGJhc2VsaW5lVGFiW2ZpZWxkLmlkXTtcblxuICAgICAgICAvLyBBIEZvcm1BcnJheSBjYW5ub3QgYmUgcmVzdG9yZWQgd2l0aCBzZXRWYWx1ZSDigJQgdGhhdCBkZW1hbmRzIGEgdmFsdWUgc2hhcGVkIGV4YWN0bHlcbiAgICAgICAgLy8gbGlrZSB0aGUgY3VycmVudCByb3dzLiBSZWJ1aWxkIGl0IGZyb20gdGhlIGJhc2VsaW5lIHJvd3MgaW5zdGVhZC5cbiAgICAgICAgY29uc3QgYXJyYXkgPSBmb3JtLmdldEFycmF5Q29udHJvbChmaWVsZC5pZCwgdGFiSWQpO1xuICAgICAgICBpZiAoYXJyYXkpIHtcbiAgICAgICAgICBhcnJheS5jbGVhcigpO1xuICAgICAgICAgIGZvciAoY29uc3Qgcm93IG9mIEFycmF5LmlzQXJyYXkoYmFzZWxpbmVWYWx1ZSkgPyBiYXNlbGluZVZhbHVlIDogW10pIHtcbiAgICAgICAgICAgIGFycmF5LnB1c2goZm9ybS5jcmVhdGVBcnJheVJvdyhmaWVsZCwgcm93KSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGFycmF5LnVwZGF0ZVZhbHVlQW5kVmFsaWRpdHkoKTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZvcm0uZ2V0Q29udHJvbChmaWVsZC5pZCwgdGFiSWQpPy5zZXRWYWx1ZShiYXNlbGluZVZhbHVlID8/IG51bGwpO1xuICAgICAgfVxuICAgICAgdGhpcy5jdXJyZW50RGF0YS5zZXQoZm9ybS5leHRyYWN0UmVjb3JkKCkpO1xuICAgIH1cbiAgICB0aGlzLnNlY3Rpb25FcnJvcnMuc2V0KHt9KTtcbiAgICB0aGlzLmVkaXRpbmdUYWJJZC5zZXQobnVsbCk7XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGUgdGhpcyBzZWN0aW9uIG9ubHksIHRoZW4gZW1pdCBpdC4gVmFsaWRhdGlvbiBpcyBzY29wZWQgdHdvIHdheXM6IEFuZ3VsYXJcbiAgICogdmFsaWRpdHkgb2YgdGhlIHRhYidzIG93biBjb250cm9scywgYW5kIHJ1bGVzIGZpbHRlcmVkIHRvIHRoaXMgdGFiIOKAlCBhIHJ1bGUgdGFyZ2V0aW5nXG4gICAqIGEgaGlkZGVuIHRhYiBtdXN0IG5vdCBibG9jayB0aGUgc2F2ZSAodGhlIHJlZmVyZW5jZSdzIE9WMC05NjggZml4KS5cbiAgICovXG4gIHNhdmVTZWN0aW9uKCk6IHZvaWQge1xuICAgIGNvbnN0IHRhYklkID0gdGhpcy5lZGl0aW5nVGFiSWQoKTtcbiAgICBjb25zdCBmb3JtID0gdGhpcy5keW5hbWljRm9ybUNvbXA7XG4gICAgaWYgKCF0YWJJZCB8fCAhZm9ybSkgcmV0dXJuO1xuXG4gICAgY29uc3QgdGFiID0gZmluZFRhYih0aGlzLmNvbmZpZz8udGFicywgdGFiSWQpO1xuICAgIGNvbnN0IGVycm9yczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuXG4gICAgZm9yIChjb25zdCBmaWVsZCBvZiB0YWI/LmZpZWxkcyA/PyBbXSkge1xuICAgICAgY29uc3QgY3RybCA9IGZvcm0uZ2V0Q29udHJvbChmaWVsZC5pZCwgdGFiSWQpO1xuICAgICAgaWYgKGN0cmw/LmludmFsaWQpIHtcbiAgICAgICAgY3RybC5tYXJrQXNUb3VjaGVkKCk7XG4gICAgICAgIGVycm9yc1tmaWVsZC5pZF0gPSBgJHtyZXNvbHZlTGFiZWwoZmllbGQubGFiZWwsIHRoaXMubGFuZ3VhZ2UpfSBpcyBpbnZhbGlkLmA7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3Qgc2NvcGVkID0gdGhpcy5ydWxlc0V2YWx1YXRpb24uZmlsdGVyRm9yVGFiKHRoaXMucnVsZXMsIHRhYklkLCB0aGlzLmNvbmZpZyk7XG4gICAgY29uc3QgcnVsZUVycm9ycyA9IHRoaXMucnVsZXNFdmFsdWF0aW9uLmV2YWx1YXRlKHNjb3BlZCwgZm9ybS5mb3JtVmFsdWVzKCksIHRoaXMub3JpZ2luYWxCYXNlbGluZSgpKVxuICAgICAgLnZhbGlkYXRpb25FcnJvcnM7XG4gICAgT2JqZWN0LmFzc2lnbihlcnJvcnMsIHJ1bGVFcnJvcnMpO1xuXG4gICAgaWYgKE9iamVjdC5rZXlzKGVycm9ycykubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy5zZWN0aW9uRXJyb3JzLnNldChlcnJvcnMpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuc2VjdGlvbkVycm9ycy5zZXQoe30pO1xuICAgIHRoaXMuZWRpdGluZ1RhYklkLnNldChudWxsKTtcblxuICAgIGNvbnN0IHJlY29yZCA9IGZvcm0uZXh0cmFjdFJlY29yZCgpO1xuICAgIHRoaXMuY3VycmVudERhdGEuc2V0KHJlY29yZCk7XG4gICAgdGhpcy5zZWN0aW9uU2F2ZS5lbWl0KHsgdGFiSWQsIHJlY29yZCB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZWVkIHRoZSBzZXNzaW9uIGJhc2VsaW5lIGZyb20gdGhlIGxvYWRlZCByZWNvcmQgc28gYFZBTFVFX0NIQU5HRURgIHJ1bGVzIGFuZCB0aGVcbiAgICogbW9kaWZpY2F0aW9uIGJhbm5lciBjb21wYXJlIGFnYWluc3QgdGhlIHJlY29yZCBhcyBpdCB3YXMgb3BlbmVkIOKAlCBub3QgYXMgaXQgd2FzXG4gICAqIGFmdGVyIHRoZSBmaXJzdCBrZXlzdHJva2UuXG4gICAqL1xuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgaWYgKGNoYW5nZXNbJ2luaXRpYWxEYXRhJ10gfHwgY2hhbmdlc1snY29uZmlnJ10pIHtcbiAgICAgIHRoaXMub3JpZ2luYWxCYXNlbGluZS5zZXQoeyAuLi4odGhpcy5pbml0aWFsRGF0YSA/PyB7fSkgfSk7XG4gICAgICB0aGlzLmN1cnJlbnREYXRhLnNldCh7IC4uLih0aGlzLmluaXRpYWxEYXRhID8/IHt9KSB9KTtcbiAgICAgIHRoaXMuYWN0aXZlVGFiSWRTaWcuc2V0KHRoaXMuZmlyc3RWaXNpYmxlVGFiSWQoKSk7XG4gICAgICAvLyBBIGRpZmZlcmVudCByZWNvcmQgaXMgYSBkaWZmZXJlbnQgc2V0IG9mIGJhbm5lcnMg4oCUIHJlLWFybSB0aGVtLlxuICAgICAgdGhpcy5kaXNtaXNzZWQuc2V0KG5ldyBTZXQ8c3RyaW5nPigpKTtcbiAgICB9XG4gIH1cblxuICBnZXQgcmVjb3JkVGl0bGUoKTogc3RyaW5nIHtcbiAgICBpZiAoIXRoaXMuY29uZmlnKSByZXR1cm4gJ1JlY29yZCc7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLm5hbWUgPyByZXNvbHZlTGFiZWwodGhpcy5jb25maWcubmFtZSwgdGhpcy5sYW5ndWFnZSkgOiB0aGlzLmNvbmZpZy5lbnRpdHk7XG4gIH1cblxuICBnZXQgYXZhdGFyTGV0dGVyKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuICh0aGlzLnJlY29yZFRpdGxlIHx8ICdSJykuY2hhckF0KDApLnRvVXBwZXJDYXNlKCk7XG4gIH1cblxuICAvLyDilIDilIDilIAgSGVhZGVyIGZpZWxkcyAoaXNQcm9maWxlSW1hZ2UgLyBpc0hlYWRlclRvZ2dsZSkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqIEV2ZXJ5IGZpZWxkIGluIHRoZSBjb25maWcsIGF0IGFueSBkZXB0aC4gKi9cbiAgcHJpdmF0ZSBhbGxGaWVsZHMoKTogTmVzdGVkRmllbGRDb25maWdbXSB7XG4gICAgY29uc3Qgb3V0OiBOZXN0ZWRGaWVsZENvbmZpZ1tdID0gW107XG4gICAgY29uc3Qgd2Fsa0ZpZWxkcyA9IChmaWVsZHM6IE5lc3RlZEZpZWxkQ29uZmlnW10gfCB1bmRlZmluZWQpID0+IHtcbiAgICAgIGZvciAoY29uc3QgZiBvZiBmaWVsZHMgPz8gW10pIHtcbiAgICAgICAgb3V0LnB1c2goZik7XG4gICAgICAgIHdhbGtGaWVsZHMoZi5jaGlsZHJlbik7XG4gICAgICB9XG4gICAgfTtcbiAgICBjb25zdCB3YWxrVGFicyA9ICh0YWJzOiBOZXN0ZWRUYWJDb25maWdbXSB8IHVuZGVmaW5lZCkgPT4ge1xuICAgICAgZm9yIChjb25zdCB0IG9mIHRhYnMgPz8gW10pIHtcbiAgICAgICAgd2Fsa0ZpZWxkcyh0LmZpZWxkcyk7XG4gICAgICAgIHdhbGtUYWJzKHQuY2hpbGRyZW4pO1xuICAgICAgfVxuICAgIH07XG4gICAgd2Fsa1RhYnModGhpcy5jb25maWc/LnRhYnMpO1xuICAgIHJldHVybiBvdXQ7XG4gIH1cblxuICAvKiogVGhlIGZpZWxkIGZsYWdnZWQgYGlzUHJvZmlsZUltYWdlYCwgcmVuZGVyZWQgYXMgdGhlIHJlY29yZCBhdmF0YXIuICovXG4gIGdldCBoZWFkZXJQcm9maWxlRmllbGQoKTogTmVzdGVkRmllbGRDb25maWcgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5hbGxGaWVsZHMoKS5maW5kKGYgPT4gZi5pc1Byb2ZpbGVJbWFnZSkgPz8gbnVsbDtcbiAgfVxuXG4gIC8qKiBUaGUgZmllbGQgZmxhZ2dlZCBgaXNIZWFkZXJUb2dnbGVgLCByZW5kZXJlZCBhcyBhIHN0YXR1cyBzd2l0Y2ggaW4gdGhlIGhlYWRlci4gKi9cbiAgZ2V0IGhlYWRlclRvZ2dsZUZpZWxkKCk6IE5lc3RlZEZpZWxkQ29uZmlnIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuYWxsRmllbGRzKCkuZmluZChmID0+IGYuaXNIZWFkZXJUb2dnbGUpID8/IG51bGw7XG4gIH1cblxuICAvKiogQXZhdGFyIGltYWdlIFVSTCwgd2hlbiB0aGUgcHJvZmlsZSBmaWVsZCBob2xkcyBhIHBlcnNpc3RlZCBGaWxlUmVmLiAqL1xuICBnZXQgcHJvZmlsZUltYWdlVXJsKCk6IHN0cmluZyB8IG51bGwge1xuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5oZWFkZXJQcm9maWxlRmllbGQ7XG4gICAgaWYgKCFmaWVsZCkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgdmFsdWUgPSB0aGlzLmZpZWxkVmFsdWUoZmllbGQuaWQpIGFzIHsgdXJsPzogc3RyaW5nIH0gfCBudWxsO1xuICAgIHJldHVybiB2YWx1ZT8udXJsID8/IG51bGw7XG4gIH1cblxuICBnZXQgaGVhZGVyVG9nZ2xlVmFsdWUoKTogYm9vbGVhbiB7XG4gICAgY29uc3QgZmllbGQgPSB0aGlzLmhlYWRlclRvZ2dsZUZpZWxkO1xuICAgIHJldHVybiBmaWVsZCA/IHRoaXMuZmllbGRWYWx1ZShmaWVsZC5pZCkgPT09IHRydWUgOiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGbGlwIHRoZSBoZWFkZXIgdG9nZ2xlLiBJdCB3cml0ZXMgc3RyYWlnaHQgdG8gdGhlIGNvbnRyb2wgcmF0aGVyIHRoYW4gZ29pbmcgdGhyb3VnaFxuICAgKiBzZWN0aW9uIGVkaXRpbmcg4oCUIHRoZSByZWZlcmVuY2UgdHJlYXRzIGl0IGFzIGEgcmVjb3JkLWxldmVsIHN0YXR1cyBzd2l0Y2gsIG5vdCBwYXJ0IG9mXG4gICAqIGFueSBvbmUgdGFiJ3Mgc2VjdGlvbi5cbiAgICovXG4gIHRvZ2dsZUhlYWRlclN0YXR1cygpOiB2b2lkIHtcbiAgICBjb25zdCBmaWVsZCA9IHRoaXMuaGVhZGVyVG9nZ2xlRmllbGQ7XG4gICAgaWYgKCFmaWVsZCB8fCB0aGlzLnJlY29yZFJlYWRPbmx5KSByZXR1cm47XG4gICAgY29uc3QgY3RybCA9IHRoaXMuY29udHJvbEZvcihmaWVsZC5pZCk7XG4gICAgaWYgKCFjdHJsKSByZXR1cm47XG4gICAgY3RybC5zZXRWYWx1ZSghdGhpcy5oZWFkZXJUb2dnbGVWYWx1ZSk7XG4gICAgaWYgKHRoaXMuZHluYW1pY0Zvcm1Db21wKSB0aGlzLmN1cnJlbnREYXRhLnNldCh0aGlzLmR5bmFtaWNGb3JtQ29tcC5leHRyYWN0UmVjb3JkKCkpO1xuICB9XG5cbiAgLyoqIExvY2F0ZSBhIGZpZWxkJ3MgY29udHJvbCB3aGVyZXZlciBpdCBsaXZlcywgc2luY2UgaGVhZGVyIGZpZWxkcyBtYXkgYmUgb24gYW55IHRhYi4gKi9cbiAgcHJpdmF0ZSBjb250cm9sRm9yKGZpZWxkSWQ6IHN0cmluZyk6IEFic3RyYWN0Q29udHJvbCB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmR5bmFtaWNGb3JtQ29tcD8uZ2V0Q29udHJvbChmaWVsZElkKSA/PyBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIEEgZmllbGQncyBjdXJyZW50IHZhbHVlLCByZWFkIGZyb20gdGhlIHJlY29yZCBzbyBpdCBpcyBzdGFibGUgZHVyaW5nIHJlbmRlci5cbiAgICpcbiAgICogUmVzb2x2ZWQgdGhyb3VnaCBgZ2V0VGFiRGF0YWAgc28gaXQgaG9ub3VycyBleGFjdGx5IHRoZSBuZXN0aW5nIGNvbnRyYWN0IHRoZSBmb3JtXG4gICAqIHBhdGNoZXMgd2l0aCDigJQgYHsgdGFiSWQ6IHsgZmllbGRJZCB9IH1gLCBvciB0aGUgcmVjb3JkIHJvb3Qgd2hlbiB0aGUgdGFiIHNldHNcbiAgICogYGZsYXREYXRhYC4gUmVhZGluZyB0aGUgcmVjb3JkIGZsYXQgaW5zdGVhZCBpcyBub3QgYSBzaG9ydGN1dDogaXQgbWFrZXMgdGhlIHN1bW1hcnlcbiAgICogZGlzcGxheSB2YWx1ZXMgdGhlIGZvcm0gbmV2ZXIgbG9hZGVkLCB3aGljaCByZWFkcyBhcyBcInRoZSBkYXRhIGlzIHRoZXJlXCIgd2hpbGUgZXZlcnlcbiAgICogY29udHJvbCBiZWhpbmQgaXQgaXMgZW1wdHkuXG4gICAqXG4gICAqIFdhbGtzIHN1Yi10YWJzIGFzIHdlbGw6IGEgc3VtbWFyeSwgaGVhZGVyLCBvciBwcm9maWxlIGZpZWxkIG1heSBzaXQgb24gYW55IHRhYiBhdCBhbnlcbiAgICogZGVwdGguXG4gICAqL1xuICBwcml2YXRlIGZpZWxkVmFsdWUoZmllbGRJZDogc3RyaW5nKTogdW5rbm93biB7XG4gICAgY29uc3QgdmlzaXQgPSAodGFiczogTmVzdGVkVGFiQ29uZmlnW10gPSBbXSk6IHVua25vd24gPT4ge1xuICAgICAgZm9yIChjb25zdCB0YWIgb2YgdGFicykge1xuICAgICAgICBjb25zdCBkYXRhID0gZ2V0VGFiRGF0YSh0YWIuaWQsIHRoaXMuY3VycmVudERhdGEoKSwgdGhpcy5jb25maWcpIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgbnVsbDtcbiAgICAgICAgaWYgKGRhdGEgJiYgZmllbGRJZCBpbiBkYXRhKSByZXR1cm4gZGF0YVtmaWVsZElkXTtcbiAgICAgICAgaWYgKHRhYi5jaGlsZHJlbikge1xuICAgICAgICAgIGNvbnN0IGZvdW5kID0gdmlzaXQodGFiLmNoaWxkcmVuKTtcbiAgICAgICAgICBpZiAoZm91bmQgIT09IEZJRUxEX05PVF9GT1VORCkgcmV0dXJuIGZvdW5kO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gRklFTERfTk9UX0ZPVU5EO1xuICAgIH07XG5cbiAgICBjb25zdCByZXN1bHQgPSB2aXNpdCh0aGlzLmNvbmZpZz8udGFicyk7XG4gICAgcmV0dXJuIHJlc3VsdCA9PT0gRklFTERfTk9UX0ZPVU5EID8gdW5kZWZpbmVkIDogcmVzdWx0O1xuICB9XG5cbiAgcmVhZG9ubHkgaXNNb2RpZmllZCA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBjb25zdCBvcmlnID0gdGhpcy5vcmlnaW5hbEJhc2VsaW5lKCk7XG4gICAgY29uc3QgY3VyciA9IHRoaXMuY3VycmVudERhdGEoKTtcbiAgICBpZiAoIW9yaWcgfHwgIWN1cnIgfHwgT2JqZWN0LmtleXMob3JpZykubGVuZ3RoID09PSAwKSByZXR1cm4gZmFsc2U7XG4gICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KG9yaWcpICE9PSBKU09OLnN0cmluZ2lmeShjdXJyKTtcbiAgfSk7XG5cbiAgcmVhZG9ubHkgc3VtbWFyeUZpZWxkcyA9IGNvbXB1dGVkPE5lc3RlZEZpZWxkQ29uZmlnW10+KCgpID0+IHtcbiAgICBjb25zdCBmaWVsZHM6IE5lc3RlZEZpZWxkQ29uZmlnW10gPSBbXTtcbiAgICBjb25zdCB3YWxrVGFicyA9ICh0YWJzOiBOZXN0ZWRUYWJDb25maWdbXSA9IFtdKSA9PiB7XG4gICAgICBmb3IgKGNvbnN0IHQgb2YgdGFicykge1xuICAgICAgICBmb3IgKGNvbnN0IGYgb2YgdC5maWVsZHMgPz8gW10pIHtcbiAgICAgICAgICBpZiAoZi5zaG93T25NaW5pbWl6ZSkgZmllbGRzLnB1c2goZik7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHQuY2hpbGRyZW4pIHdhbGtUYWJzKHQuY2hpbGRyZW4pO1xuICAgICAgfVxuICAgIH07XG4gICAgd2Fsa1RhYnModGhpcy5jb25maWc/LnRhYnMpO1xuICAgIHJldHVybiBmaWVsZHM7XG4gIH0pO1xuXG4gIG9uRm9ybUNoYW5nZShkYXRhOiBSZWNvcmQ8c3RyaW5nLCBhbnk+KTogdm9pZCB7XG4gICAgLy8gQmFzZWxpbmUgaXMgc2VlZGVkIGZyb20gYGluaXRpYWxEYXRhYCBpbiBuZ09uQ2hhbmdlczsgb25seSBmaWxsIGl0IGhlcmUgd2hlbiB0aGVcbiAgICAvLyByZWNvcmQgd2FzIG9wZW5lZCBlbXB0eSAoY3JlYXRlIGZsb3cpLCBzbyB0aGUgZmlyc3QgZW1pdHRlZCBzaGFwZSBiZWNvbWVzIHRoZSBiYXNlbGluZS5cbiAgICBpZiAoT2JqZWN0LmtleXModGhpcy5vcmlnaW5hbEJhc2VsaW5lKCkpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhpcy5vcmlnaW5hbEJhc2VsaW5lLnNldCh7IC4uLmRhdGEgfSk7XG4gICAgfVxuICAgIHRoaXMuY3VycmVudERhdGEuc2V0KGRhdGEpO1xuICAgIHRoaXMuZm9ybUNoYW5nZS5lbWl0KGRhdGEpO1xuICB9XG5cbiAgb25Gb3JtU3VibWl0KGRhdGE6IFJlY29yZDxzdHJpbmcsIGFueT4pOiB2b2lkIHtcbiAgICB0aGlzLmZvcm1TdWJtaXQuZW1pdChkYXRhKTtcbiAgfVxuXG4gIG9uRm9ybVJlc2V0KCk6IHZvaWQge1xuICAgIHRoaXMuZm9ybVJlc2V0LmVtaXQoKTtcbiAgfVxuXG4gIGZvcm1hdEZpZWxkTGFiZWwoZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnKTogc3RyaW5nIHtcbiAgICByZXR1cm4gcmVzb2x2ZUxhYmVsKGZpZWxkLmxhYmVsLCB0aGlzLmxhbmd1YWdlKTtcbiAgfVxuXG4gIC8qKiBTdW1tYXJ5IHZhbHVlcyByZW5kZXIgdGhyb3VnaCB0aGUgc2hhcmVkIGNvcmUgZm9ybWF0dGVyLCBub3QgYSBsb2NhbCBzdHJpbmdpZmllci4gKi9cbiAgZm9ybWF0RmllbGRWYWx1ZShmaWVsZDogTmVzdGVkRmllbGRDb25maWcpOiBzdHJpbmcge1xuICAgIHJldHVybiBmb3JtYXREaXNwbGF5VmFsdWUoZmllbGQudHlwZSwgZmllbGQub3B0aW9ucywgdGhpcy5maWVsZFZhbHVlKGZpZWxkLmlkKSwgdGhpcy5sYW5ndWFnZSk7XG4gIH1cblxuICAvKipcbiAgICogRmluZHMgd2hpY2ggdGFiIOKAlCBhbmQsIHdoZXJlIHRoZSBmaWVsZCBsaXZlcyBvbmUgbGV2ZWwgZG93biwgd2hpY2ggc3ViLXRhYiDigJQgb3ducyBhXG4gICAqIGZpZWxkLiBUaGUgd2FsayB1c2VkIHRvIGNoZWNrIHRvcC1sZXZlbCBgZmllbGRzYCBvbmx5LCBzbyBldmVyeSBmaWVsZCBpbiBhIHN1Yi10YWIgd2FzXG4gICAqIHNpbXBseSBub3QgZm91bmQgYW5kIHRoZSBqdW1wIGRpZCBub3RoaW5nIGF0IGFsbC5cbiAgICovXG4gIHByaXZhdGUgbG9jYXRlRmllbGQoZmllbGRJZDogc3RyaW5nKTogeyB0YWJJZDogc3RyaW5nOyBzdWJUYWJJZD86IHN0cmluZyB9IHwgbnVsbCB7XG4gICAgZm9yIChjb25zdCB0YWIgb2YgdGhpcy5jb25maWc/LnRhYnMgfHwgW10pIHtcbiAgICAgIGlmICgodGFiLmZpZWxkcyB8fCBbXSkuc29tZShmID0+IGYuaWQgPT09IGZpZWxkSWQpKSByZXR1cm4geyB0YWJJZDogdGFiLmlkIH07XG4gICAgICBmb3IgKGNvbnN0IGNoaWxkIG9mIHRhYi5jaGlsZHJlbiB8fCBbXSkge1xuICAgICAgICBpZiAoKGNoaWxkLmZpZWxkcyB8fCBbXSkuc29tZShmID0+IGYuaWQgPT09IGZpZWxkSWQpKSB7XG4gICAgICAgICAgcmV0dXJuIHsgdGFiSWQ6IHRhYi5pZCwgc3ViVGFiSWQ6IGNoaWxkLmlkIH07XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvKipcbiAgICogU3dpdGNoZXMgdG8gdGhlIHRhYiBob2xkaW5nIGBmaWVsZElkYCwgdGhlbiBzY3JvbGxzIGl0IGludG8gdmlldyBhbmQgbW92ZXMgZm9jdXMgdG8gaXQuXG4gICAqXG4gICAqIFRoZSB3YWl0IGZvciB0aGUgbmV3IHRhYiB0byByZW5kZXIgdXNlZCB0byBiZSBhIDUwIG1zIGBzZXRUaW1lb3V0YCwgd2hpY2ggd2FzIHdyb25nIHRocmVlXG4gICAqIHdheXM6IGBkb2N1bWVudGAgaXMgdW5kZWZpbmVkIG9uIGEgc2VydmVyIHJlbmRlciwgNTAgbXMgaXMgYSBndWVzcyB0aGF0IGEgbGFyZ2UgdGFiIGNhblxuICAgKiBvdXRydW4sIGFuZCBub3RoaW5nIGNhbmNlbGxlZCB0aGUgdGltZXIgaWYgdGhlIGNvbXBvbmVudCB3ZW50IGF3YXkgZmlyc3QuXG4gICAqIGBhZnRlck5leHRSZW5kZXJgIGZpeGVzIGFsbCB0aHJlZSDigJQgaXQgbmV2ZXIgcnVucyBvbiB0aGUgc2VydmVyLCBpdCBydW5zIHdoZW4gdGhlIHBhbmVsXG4gICAqIGhhcyBhY3R1YWxseSByZW5kZXJlZCByYXRoZXIgdGhhbiB3aGVuIGEgZ3Vlc3MgZXhwaXJlcywgYW5kIGl0IGlzIHRpZWQgdG8gdGhlIGluamVjdG9yIHNvXG4gICAqIGRlc3Ryb3lpbmcgdGhlIGNvbXBvbmVudCBjYW5jZWxzIGl0LiBUaGUgcXVlcnkgaXMgc2NvcGVkIHRvIHRoaXMgY29tcG9uZW50J3Mgb3duIGVsZW1lbnQsXG4gICAqIHNvIHRoZSBsaWJyYXJ5IG5vIGxvbmdlciByZWFjaGVzIGZvciB0aGUgZ2xvYmFsIGBkb2N1bWVudGAgYXQgYWxsLlxuICAgKi9cbiAganVtcFRvRmllbGQoZmllbGRJZDogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgbG9jYXRpb24gPSB0aGlzLmxvY2F0ZUZpZWxkKGZpZWxkSWQpO1xuICAgIGlmICghbG9jYXRpb24pIHJldHVybjtcblxuICAgIHRoaXMuZHluYW1pY0Zvcm1Db21wPy5zZXRBY3RpdmVUYWIobG9jYXRpb24udGFiSWQpO1xuICAgIC8vIGBzZXRBY3RpdmVUYWJgIHJlc2V0cyB0byBhIHRhYidzIGZpcnN0IGNoaWxkLCBzbyB0aGUgc3ViLXRhYiBoYXMgdG8gYmUgc2VsZWN0ZWQgYWZ0ZXJcbiAgICAvLyBpdCwgbm90IGJlZm9yZS4gV2l0aG91dCB0aGlzIHRoZSB3YWxrIGZvdW5kIHN1Yi10YWIgZmllbGRzIGFuZCB0aGVuIHJlbmRlcmVkIHRoZSB3cm9uZ1xuICAgIC8vIHBhbmVsLCBzbyB0aGUgZWxlbWVudCB0aGUgY2FsbGJhY2sgYmVsb3cgbG9va3MgZm9yIG5ldmVyIGV4aXN0ZWQuXG4gICAgaWYgKGxvY2F0aW9uLnN1YlRhYklkKSB0aGlzLmR5bmFtaWNGb3JtQ29tcD8uc2V0QWN0aXZlU3ViVGFiKGxvY2F0aW9uLnN1YlRhYklkKTtcblxuICAgIGFmdGVyTmV4dFJlbmRlcihcbiAgICAgICgpID0+IHtcbiAgICAgICAgLy8gVGhlIGZpZWxkIGlkIGNvbWVzIGZyb20gY29uZmlnLCBzbyBpdCBuZXZlciBnb2VzIGludG8gYSBzZWxlY3RvciBzdHJpbmc6IG5vXG4gICAgICAgIC8vIGVzY2FwaW5nIHRvIGdldCB3cm9uZywgYW5kIG5vIG5lZWQgZm9yIGBDU1MuZXNjYXBlYCwgd2hpY2gganNkb20gYW5kIG9sZGVyXG4gICAgICAgIC8vIGJyb3dzZXJzIGRvIG5vdCBwcm92aWRlIGFuZCB3aG9zZSBhYnNlbmNlIHRocm93cyBzaWxlbnRseSBpbnNpZGUgYSByZW5kZXIgaG9vay5cbiAgICAgICAgY29uc3Qgd2FudGVkID0gYGZpZWxkLWNvbnRhaW5lci0ke2ZpZWxkSWR9YDtcbiAgICAgICAgY29uc3QgZWwgPSBBcnJheS5mcm9tKFxuICAgICAgICAgIHRoaXMuaG9zdC5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGw8SFRNTEVsZW1lbnQ+KCdbaWRePVwiZmllbGQtY29udGFpbmVyLVwiXScpLFxuICAgICAgICApLmZpbmQoc2xvdCA9PiBzbG90LmlkID09PSB3YW50ZWQpO1xuICAgICAgICBpZiAoIWVsKSByZXR1cm47XG4gICAgICAgIGVsLnNjcm9sbEludG9WaWV3KHsgYmVoYXZpb3I6ICdzbW9vdGgnLCBibG9jazogJ2NlbnRlcicgfSk7XG4gICAgICAgIC8vIFRoZSBzbG90IGNhcnJpZXMgdGFiaW5kZXg9XCItMVwiIHNvIHRoaXMgYWN0dWFsbHkgbW92ZXMgZm9jdXMuIFdpdGhvdXQgaXQgdGhlIGp1bXBcbiAgICAgICAgLy8gc2Nyb2xsZWQgdGhlIGZpZWxkIGludG8gdmlldyBhbmQgbGVmdCBmb2N1cyBiZWhpbmQgb24gdGhlIGxpbmsuXG4gICAgICAgIGVsLmZvY3VzKCk7XG4gICAgICB9LFxuICAgICAgeyBpbmplY3RvcjogdGhpcy5pbmplY3RvciB9LFxuICAgICk7XG4gIH1cbn1cbiIsIjxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvclwiPlxuICA8IS0tIFJlY29yZCBIZWFkZXIgLS0+XG4gIDxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9faGVhZGVyXCI+XG4gICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX190aXRsZS1ncm91cFwiPlxuICAgICAgQGlmIChwcm9maWxlSW1hZ2VVcmwpIHtcbiAgICAgICAgPGltZ1xuICAgICAgICAgIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2F2YXRhclwiXG4gICAgICAgICAgZGF0YS10ZXN0aWQ9XCJoZWFkZXItcHJvZmlsZS1pbWFnZVwiXG4gICAgICAgICAgW3NyY109XCJwcm9maWxlSW1hZ2VVcmxcIlxuICAgICAgICAgIFthbHRdPVwicmVjb3JkVGl0bGVcIlxuICAgICAgICAvPlxuICAgICAgfSBAZWxzZSB7XG4gICAgICAgIDxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fYXZhdGFyXCIgZGF0YS10ZXN0aWQ9XCJoZWFkZXItYXZhdGFyLWluaXRpYWxcIj5cbiAgICAgICAgICB7eyBhdmF0YXJMZXR0ZXIgfX1cbiAgICAgICAgPC9kaXY+XG4gICAgICB9XG4gICAgICA8ZGl2PlxuICAgICAgICA8aDIgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fdGl0bGVcIiBkYXRhLXRlc3RpZD1cInJlY29yZC10aXRsZVwiPlxuICAgICAgICAgIHt7IHJlY29yZFRpdGxlIH19XG4gICAgICAgIDwvaDI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3N1YnRpdGxlXCI+RW50aXR5OiB7eyBjb25maWcuZW50aXR5IH19PC9zcGFuPlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG5cbiAgICA8IS0tIFJlY29yZC1sZXZlbCBzdGF0dXMgc3dpdGNoLCBvdXRzaWRlIHNlY3Rpb24gZWRpdGluZyAtLT5cbiAgICBAaWYgKGhlYWRlclRvZ2dsZUZpZWxkOyBhcyB0b2dnbGVGaWVsZCkge1xuICAgICAgPGxhYmVsIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2hlYWRlci10b2dnbGVcIj5cbiAgICAgICAgPHNwYW4+e3sgZm9ybWF0RmllbGRMYWJlbCh0b2dnbGVGaWVsZCkgfX08L3NwYW4+XG4gICAgICAgIDxpbnB1dFxuICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgcm9sZT1cInN3aXRjaFwiXG4gICAgICAgICAgZGF0YS10ZXN0aWQ9XCJoZWFkZXItdG9nZ2xlXCJcbiAgICAgICAgICBbY2hlY2tlZF09XCJoZWFkZXJUb2dnbGVWYWx1ZVwiXG4gICAgICAgICAgW2Rpc2FibGVkXT1cInJlY29yZFJlYWRPbmx5XCJcbiAgICAgICAgICBbYXR0ci5hcmlhLWNoZWNrZWRdPVwiaGVhZGVyVG9nZ2xlVmFsdWVcIlxuICAgICAgICAgIChjaGFuZ2UpPVwidG9nZ2xlSGVhZGVyU3RhdHVzKClcIlxuICAgICAgICAvPlxuICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19oZWFkZXItdG9nZ2xlLXRleHRcIj57eyBoZWFkZXJUb2dnbGVWYWx1ZSA/ICdZZXMnIDogJ05vJyB9fTwvc3Bhbj5cbiAgICAgIDwvbGFiZWw+XG4gICAgfVxuICA8L2Rpdj5cblxuICA8IS0tIERpc21pc3NpYmxlIGluZm8gYmFubmVycyAocnVsZXMgYGluZm9gIGFjdGlvbikgLS0+XG4gIEBmb3IgKGJhbm5lciBvZiBpbmZvQmFubmVyczsgdHJhY2sgYmFubmVyLmZpZWxkSWQpIHtcbiAgICA8ZGl2XG4gICAgICBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXIgbmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lci0taW5mb1wiXG4gICAgICByb2xlPVwic3RhdHVzXCJcbiAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidpbmZvLWJhbm5lci0nICsgYmFubmVyLmZpZWxkSWRcIlxuICAgID5cbiAgICAgIDxzcGFuPuKEue+4jyB7eyBiYW5uZXIubWVzc2FnZSB9fTwvc3Bhbj5cbiAgICAgIDxidXR0b25cbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lci1kaXNtaXNzXCJcbiAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2Rpc21pc3MtJyArIGJhbm5lci5maWVsZElkXCJcbiAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCInRGlzbWlzczogJyArIGJhbm5lci5tZXNzYWdlXCJcbiAgICAgICAgKGNsaWNrKT1cImRpc21pc3NJbmZvQmFubmVyKGJhbm5lci5maWVsZElkKVwiXG4gICAgICA+4pyVPC9idXR0b24+XG4gICAgPC9kaXY+XG4gIH1cblxuICA8IS0tIE1vZGlmaWNhdGlvbiBiYXNlbGluZSBiYW5uZXIgLS0+XG4gIEBpZiAoaXNNb2RpZmllZCgpKSB7XG4gICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXIgbmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lci0td2FybmluZ1wiPlxuICAgICAg8J+UkiBSZWNvcmQgbW9kaWZpZWQgZnJvbSBzZXNzaW9uIGJhc2VsaW5lIOKAlCB1bnNhdmVkIGNoYW5nZXMgcGVuZGluZy5cbiAgICA8L2Rpdj5cbiAgfVxuXG4gIDwhLS0gU3VtbWFyeSBQYW5lbCAoc2hvd09uTWluaW1pemUgZmllbGRzIHdpdGggaW50ZXJhY3RpdmUganVtcCkgLS0+XG4gIEBpZiAoc3VtbWFyeUZpZWxkcygpLmxlbmd0aCA+IDApIHtcbiAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3N1bW1hcnktcGFuZWxcIiBkYXRhLXRlc3RpZD1cInN1bW1hcnktcGFuZWxcIj5cbiAgICAgIEBmb3IgKHNmIG9mIHN1bW1hcnlGaWVsZHMoKTsgdHJhY2sgc2YuaWQpIHtcbiAgICAgICAgPGRpdlxuICAgICAgICAgIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3N1bW1hcnktaXRlbVwiXG4gICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3N1bW1hcnktJyArIHNmLmlkXCJcbiAgICAgICAgICAoY2xpY2spPVwianVtcFRvRmllbGQoc2YuaWQpXCJcbiAgICAgICAgICBbdGl0bGVdPVwiJ0NsaWNrIHRvIGp1bXAgdG8gJyArIGZvcm1hdEZpZWxkTGFiZWwoc2YpXCJcbiAgICAgICAgPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3N1bW1hcnktbGFiZWxcIj57eyBmb3JtYXRGaWVsZExhYmVsKHNmKSB9fTwvc3Bhbj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19zdW1tYXJ5LXZhbFwiPnt7IGZvcm1hdEZpZWxkVmFsdWUoc2YpIH19PC9zcGFuPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIH1cbiAgICA8L2Rpdj5cbiAgfVxuXG4gIDwhLS0gU2VjdGlvbiBlZGl0IGJhcjogb25lIHRhYiBpcyBlZGl0ZWQgYXQgYSB0aW1lLCBhbmQgdmFsaWRhdGVkIG9uIGl0cyBvd24gLS0+XG4gIEBpZiAodmlld01vZGUgJiYgIXJlY29yZFJlYWRPbmx5KSB7XG4gICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19zZWN0aW9uLWJhclwiPlxuICAgICAgQGlmIChpc0VkaXRpbmdBY3RpdmVUYWIpIHtcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fc2VjdGlvbi1oaW50XCI+RWRpdGluZyB0aGlzIHNlY3Rpb248L3NwYW4+XG4gICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwibmd4LWZvcm1fX3Jlc2V0XCIgZGF0YS10ZXN0aWQ9XCJjYW5jZWwtc2VjdGlvblwiIChjbGljayk9XCJjYW5jZWxTZWN0aW9uKClcIj5cbiAgICAgICAgICBDYW5jZWxcbiAgICAgICAgPC9idXR0b24+XG4gICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwibmd4LWZvcm1fX3N1Ym1pdFwiIGRhdGEtdGVzdGlkPVwic2F2ZS1zZWN0aW9uXCIgKGNsaWNrKT1cInNhdmVTZWN0aW9uKClcIj5cbiAgICAgICAgICBTYXZlIHNlY3Rpb25cbiAgICAgICAgPC9idXR0b24+XG4gICAgICB9IEBlbHNlIHtcbiAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJuZ3gtZm9ybV9fcmVzZXRcIiBkYXRhLXRlc3RpZD1cImVkaXQtc2VjdGlvblwiIChjbGljayk9XCJlZGl0U2VjdGlvbigpXCI+XG4gICAgICAgICAgRWRpdCBzZWN0aW9uXG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgfVxuICAgIDwvZGl2PlxuICB9XG5cbiAgQGlmIChPYmplY3Qua2V5cyhzZWN0aW9uRXJyb3JzKCkpLmxlbmd0aCA+IDApIHtcbiAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lciBuZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLS1lcnJvclwiIHJvbGU9XCJhbGVydFwiIGRhdGEtdGVzdGlkPVwic2VjdGlvbi1lcnJvcnNcIj5cbiAgICAgIEBmb3IgKGtleSBvZiBPYmplY3Qua2V5cyhzZWN0aW9uRXJyb3JzKCkpOyB0cmFjayBrZXkpIHtcbiAgICAgICAgPGRpdj7imqDvuI8ge3sgc2VjdGlvbkVycm9ycygpW2tleV0gfX08L2Rpdj5cbiAgICAgIH1cbiAgICA8L2Rpdj5cbiAgfVxuXG4gIDwhLS0gQXJyYXkgcm93czogbGlzdGVkIGhlcmUsIGVkaXRlZCBpbiB0aGUgZHJhd2VyIGJlbG93IC0tPlxuICBAZm9yIChhcnJheUZpZWxkIG9mIGFycmF5RmllbGRzRm9yQWN0aXZlVGFiOyB0cmFjayBhcnJheUZpZWxkLmlkKSB7XG4gICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19yb3dzXCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3Jvd3MtJyArIGFycmF5RmllbGQuaWRcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fcm93cy1oZWFkXCI+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3N1bW1hcnktbGFiZWxcIj57eyBmb3JtYXRGaWVsZExhYmVsKGFycmF5RmllbGQpIH19PC9zcGFuPlxuICAgICAgICBAaWYgKCFzZWN0aW9uUmVhZE9ubHkpIHtcbiAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgIGNsYXNzPVwibmd4LWZvcm1fX3Jlc2V0XCJcbiAgICAgICAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidhZGQtcm93LScgKyBhcnJheUZpZWxkLmlkXCJcbiAgICAgICAgICAgIChjbGljayk9XCJvcGVuQWRkUm93KGFycmF5RmllbGQpXCJcbiAgICAgICAgICA+KyBSb3c8L2J1dHRvbj5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG5cbiAgICAgIEBmb3IgKHJvdyBvZiByb3dzT2YoYXJyYXlGaWVsZCk7IHRyYWNrICRpbmRleCkge1xuICAgICAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3Jvd1wiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidyb3ctJyArIGFycmF5RmllbGQuaWQgKyAnLScgKyAkaW5kZXhcIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19yb3ctdGV4dFwiPnt7IHJvd1N1bW1hcnkoYXJyYXlGaWVsZCwgcm93KSB9fTwvc3Bhbj5cbiAgICAgICAgICBAaWYgKCFzZWN0aW9uUmVhZE9ubHkpIHtcbiAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3Jvdy1idG5cIlxuICAgICAgICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCInZWRpdC1yb3ctJyArIGFycmF5RmllbGQuaWQgKyAnLScgKyAkaW5kZXhcIlxuICAgICAgICAgICAgICAoY2xpY2spPVwib3BlbkVkaXRSb3coYXJyYXlGaWVsZCwgJGluZGV4KVwiPkVkaXQ8L2J1dHRvbj5cbiAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3Jvdy1idG5cIlxuICAgICAgICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCInZGVsZXRlLXJvdy0nICsgYXJyYXlGaWVsZC5pZCArICctJyArICRpbmRleFwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJkZWxldGVSb3coYXJyYXlGaWVsZCwgJGluZGV4KVwiPkRlbGV0ZTwvYnV0dG9uPlxuICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICB9IEBlbXB0eSB7XG4gICAgICAgIDxwIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3Jvdy1lbXB0eVwiPk5vIHJvd3MgeWV0LjwvcD5cbiAgICAgIH1cbiAgICA8L2Rpdj5cbiAgfVxuXG4gIDwhLS1cbiAgICBSb3cgZHJhd2VyLiBEZWxpYmVyYXRlbHkgb3V0c2lkZSB0aGUgdGFiIHBhbmVsOiBidWlsZGluZyBhIHJvdydzIEZvcm1Hcm91cCBpbnNpZGUgdGhlXG4gICAgdGFiIHZpZXcgcmFjZXMgdGhlIHRhYidzIG93biBpbml0aWFsaXNhdGlvbiAodGhlIHJlZmVyZW5jZSBoaXQgdGhpcyB3aXRoIEZvcm1BcnJheSkuXG4gIC0tPlxuICBAaWYgKGlubGluZVJvd0ZpZWxkKCk7IGFzIHJvd0ZpZWxkKSB7XG4gICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19kcmF3ZXJcIiBkYXRhLXRlc3RpZD1cInJvdy1kcmF3ZXJcIiBbZm9ybUdyb3VwXT1cImlubGluZVJvd0Zvcm0hXCI+XG4gICAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3Jvd3MtaGVhZFwiPlxuICAgICAgICA8c3Ryb25nPnt7IGlubGluZVJvd0luZGV4KCkgPT09IG51bGwgPyAnQWRkJyA6ICdFZGl0JyB9fSB7eyBmb3JtYXRGaWVsZExhYmVsKHJvd0ZpZWxkKSB9fSByb3c8L3N0cm9uZz5cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8ZGl2IGNsYXNzPVwibmd4LWZvcm1fX3BhbmVsXCI+XG4gICAgICAgIEBmb3IgKGNoaWxkIG9mIHJvd0ZpZWxkLmNoaWxkcmVuID8/IFtdOyB0cmFjayBjaGlsZC5pZCkge1xuICAgICAgICAgIDxuZ3gtZHluYW1pYy1maWVsZFxuICAgICAgICAgICAgW2ZpZWxkXT1cImNoaWxkXCJcbiAgICAgICAgICAgIFtjb250cm9sXT1cImlubGluZVJvd0Zvcm0hLmdldChjaGlsZC5pZCkhXCJcbiAgICAgICAgICAgIFtjb25maWddPVwiY29uZmlnXCJcbiAgICAgICAgICAgIFtsYW5ndWFnZV09XCJsYW5ndWFnZVwiXG4gICAgICAgICAgICBbcmVhZG9ubHldPVwiZmFsc2VcIlxuICAgICAgICAgICAgW3VzZXJSb2xlc109XCJ1c2VyUm9sZXNcIlxuICAgICAgICAgICAgW3N0eWxlLmdyaWRDb2x1bW5dPVwiJ3NwYW4gMTInXCJcbiAgICAgICAgICAvPlxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cblxuICAgICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19zZWN0aW9uLWJhclwiPlxuICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cIm5neC1mb3JtX19yZXNldFwiIGRhdGEtdGVzdGlkPVwiY2FuY2VsLXJvd1wiIChjbGljayk9XCJjYW5jZWxSb3coKVwiPkNhbmNlbDwvYnV0dG9uPlxuICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cIm5neC1mb3JtX19zdWJtaXRcIiBkYXRhLXRlc3RpZD1cInNhdmUtcm93XCIgKGNsaWNrKT1cInNhdmVSb3coKVwiPlNhdmUgcm93PC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgfVxuXG4gIDwhLS0gTWFpbiBEeW5hbWljIEZvcm0gLS0+XG4gIDxuZ3gtZHluYW1pYy1mb3JtXG4gICAgW2NvbmZpZ109XCJjb25maWdcIlxuICAgIFtydWxlc109XCJydWxlc1wiXG4gICAgW2luaXRpYWxEYXRhXT1cImluaXRpYWxEYXRhXCJcbiAgICBbb3JpZ2luYWxEYXRhXT1cIm9yaWdpbmFsQmFzZWxpbmUoKVwiXG4gICAgW3VzZXJSb2xlc109XCJ1c2VyUm9sZXNcIlxuICAgIFtsYW5ndWFnZV09XCJsYW5ndWFnZVwiXG4gICAgW3JlYWRvbmx5XT1cInNlY3Rpb25SZWFkT25seVwiXG4gICAgW3JlYWRPbmx5RmllbGRzXT1cInJlYWRPbmx5RmllbGRzXCJcbiAgICBbc2hvd0luZm9CYW5uZXJzXT1cImZhbHNlXCJcbiAgICBbbG9hZGluZ109XCJsb2FkaW5nXCJcbiAgICBbZXJyb3JdPVwiZXJyb3JcIlxuICAgIChmb3JtQ2hhbmdlKT1cIm9uRm9ybUNoYW5nZSgkZXZlbnQpXCJcbiAgICAoZm9ybVN1Ym1pdCk9XCJvbkZvcm1TdWJtaXQoJGV2ZW50KVwiXG4gICAgKGZvcm1SZXNldCk9XCJvbkZvcm1SZXNldCgpXCJcbiAgICAoYWN0aXZlVGFiQ2hhbmdlKT1cIm9uQWN0aXZlVGFiQ2hhbmdlKCRldmVudClcIlxuICAvPlxuPC9kaXY+XG4iXX0=