ngx-dynamic-entity 0.0.1 → 1.0.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.
- package/README.md +139 -0
- package/esm2022/lib/field-types/array-field.component.mjs +160 -0
- package/esm2022/lib/field-types/boolean-field.component.mjs +100 -0
- package/esm2022/lib/field-types/checkbox-field.component.mjs +49 -41
- package/esm2022/lib/field-types/currency-field.component.mjs +101 -0
- package/esm2022/lib/field-types/date-field.component.mjs +47 -39
- package/esm2022/lib/field-types/dropdown-field.component.mjs +145 -54
- package/esm2022/lib/field-types/email-field.component.mjs +101 -0
- package/esm2022/lib/field-types/entity-ref-field.component.mjs +134 -70
- package/esm2022/lib/field-types/file-field.component.mjs +156 -0
- package/esm2022/lib/field-types/group-field.component.mjs +89 -0
- package/esm2022/lib/field-types/image-field.component.mjs +168 -0
- package/esm2022/lib/field-types/month-year-field.component.mjs +151 -0
- package/esm2022/lib/field-types/multi-select-field.component.mjs +110 -56
- package/esm2022/lib/field-types/number-field.component.mjs +84 -41
- package/esm2022/lib/field-types/password-field.component.mjs +109 -0
- package/esm2022/lib/field-types/radio-field.component.mjs +152 -0
- package/esm2022/lib/field-types/text-field.component.mjs +82 -42
- package/esm2022/lib/field-types/textarea-field.component.mjs +52 -41
- package/esm2022/lib/form/dynamic-field/dynamic-field.component.mjs +44 -26
- package/esm2022/lib/form/dynamic-form.component.mjs +501 -67
- package/esm2022/lib/form/dynamic-record-form.component.mjs +469 -0
- package/esm2022/lib/providers/provide-field-types.mjs +72 -0
- package/esm2022/lib/providers/provide-ngx-dynamic-entity.mjs +3 -5
- package/esm2022/lib/services/cascade-data.service.mjs +129 -0
- package/esm2022/lib/services/config-source.service.mjs +67 -0
- package/esm2022/lib/services/entity-ref-cache.mjs +20 -0
- package/esm2022/lib/services/entity-ref-registry.service.mjs +3 -3
- package/esm2022/lib/services/entity-ref-selection.service.mjs +27 -0
- package/esm2022/lib/services/entity-reference.service.mjs +168 -0
- package/esm2022/lib/services/field-registry.service.mjs +35 -30
- package/esm2022/lib/services/file-upload.service.mjs +89 -0
- package/esm2022/lib/services/lookup-registry.service.mjs +246 -0
- package/esm2022/lib/services/rbac.service.mjs +5 -11
- package/esm2022/lib/services/rules-evaluation.service.mjs +27 -0
- package/esm2022/lib/services/validator-registry.service.mjs +24 -7
- package/esm2022/lib/tokens/injection-tokens.mjs +88 -7
- package/esm2022/public-api.mjs +14 -5
- package/fesm2022/ngx-dynamic-entity.mjs +3985 -876
- package/fesm2022/ngx-dynamic-entity.mjs.map +1 -1
- package/lib/field-types/array-field.component.d.ts +22 -0
- package/lib/field-types/boolean-field.component.d.ts +18 -0
- package/lib/field-types/checkbox-field.component.d.ts +3 -2
- package/lib/field-types/currency-field.component.d.ts +17 -0
- package/lib/field-types/date-field.component.d.ts +3 -2
- package/lib/field-types/dropdown-field.component.d.ts +24 -4
- package/lib/field-types/email-field.component.d.ts +15 -0
- package/lib/field-types/entity-ref-field.component.d.ts +24 -11
- package/lib/field-types/file-field.component.d.ts +25 -0
- package/lib/field-types/group-field.component.d.ts +17 -0
- package/lib/field-types/image-field.component.d.ts +30 -0
- package/lib/field-types/month-year-field.component.d.ts +28 -0
- package/lib/field-types/multi-select-field.component.d.ts +18 -4
- package/lib/field-types/number-field.component.d.ts +5 -2
- package/lib/field-types/password-field.component.d.ts +17 -0
- package/lib/field-types/radio-field.component.d.ts +33 -0
- package/lib/field-types/text-field.component.d.ts +5 -3
- package/lib/field-types/textarea-field.component.d.ts +4 -2
- package/lib/form/dynamic-field/dynamic-field.component.d.ts +20 -7
- package/lib/form/dynamic-form.component.d.ts +111 -19
- package/lib/form/dynamic-record-form.component.d.ts +169 -0
- package/lib/providers/provide-field-types.d.ts +24 -0
- package/lib/providers/provide-ngx-dynamic-entity.d.ts +12 -7
- package/lib/services/cascade-data.service.d.ts +45 -0
- package/lib/services/config-source.service.d.ts +14 -0
- package/lib/services/entity-ref-cache.d.ts +27 -0
- package/lib/services/entity-ref-registry.service.d.ts +4 -3
- package/lib/services/entity-ref-selection.service.d.ts +24 -0
- package/lib/services/entity-reference.service.d.ts +49 -0
- package/lib/services/field-registry.service.d.ts +17 -7
- package/lib/services/file-upload.service.d.ts +27 -0
- package/lib/services/lookup-registry.service.d.ts +94 -0
- package/lib/services/rbac.service.d.ts +4 -9
- package/lib/services/rules-evaluation.service.d.ts +17 -0
- package/lib/services/validator-registry.service.d.ts +5 -4
- package/lib/tokens/injection-tokens.d.ts +90 -7
- package/package.json +14 -3
- package/public-api.d.ts +11 -4
- package/esm2022/lib/services/config.service.mjs +0 -58
- package/esm2022/lib/services/version.service.mjs +0 -40
- package/esm2022/lib/table/dynamic-table.component.mjs +0 -160
- package/lib/services/config.service.d.ts +0 -25
- package/lib/services/version.service.d.ts +0 -26
- package/lib/table/dynamic-table.component.d.ts +0 -56
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, signal, computed, inject, 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
|
+
* DynamicRecordFormComponent — comprehensive tabbed record view & edit component.
|
|
12
|
+
* Supports cross-tab rule evaluation, profile header, summary drawer (showOnMinimize),
|
|
13
|
+
* interactive quick-jump links, and baseline modification tracking.
|
|
14
|
+
*/
|
|
15
|
+
export class DynamicRecordFormComponent {
|
|
16
|
+
config;
|
|
17
|
+
rules;
|
|
18
|
+
initialData;
|
|
19
|
+
userRoles = [];
|
|
20
|
+
language = 'en';
|
|
21
|
+
readonly = false;
|
|
22
|
+
loading = false;
|
|
23
|
+
error = null;
|
|
24
|
+
/** Whole record read-only, regardless of RBAC. Equivalent to the reference's `isReadOnly`. */
|
|
25
|
+
isReadOnly = false;
|
|
26
|
+
/** Specific field ids forced read-only while the rest of the record stays editable. */
|
|
27
|
+
readOnlyFields = [];
|
|
28
|
+
/**
|
|
29
|
+
* Open the record read-only, with a per-tab "Edit section" flow — one tab is edited and
|
|
30
|
+
* validated at a time. This is the framework's `EntityRecordComponent` model and the
|
|
31
|
+
* default here.
|
|
32
|
+
*
|
|
33
|
+
* Set false for a directly editable record with no view/edit distinction.
|
|
34
|
+
*/
|
|
35
|
+
viewMode = true;
|
|
36
|
+
formSubmit = new EventEmitter();
|
|
37
|
+
formChange = new EventEmitter();
|
|
38
|
+
formReset = new EventEmitter();
|
|
39
|
+
/** One tab section was saved. Carries the whole record, plus which tab was edited. */
|
|
40
|
+
sectionSave = new EventEmitter();
|
|
41
|
+
dynamicFormComp;
|
|
42
|
+
rulesEvaluation = inject(RulesEvaluationService);
|
|
43
|
+
currentData = signal({});
|
|
44
|
+
originalBaseline = signal({});
|
|
45
|
+
/**
|
|
46
|
+
* Info banners the user has dismissed, by field id.
|
|
47
|
+
*
|
|
48
|
+
* The reference's contract: an info banner persists until dismissed, **not** until the
|
|
49
|
+
* value changes. Dismissal is keyed by field so re-triggering a different field's rule
|
|
50
|
+
* still shows. Re-armed when a different record is loaded.
|
|
51
|
+
*/
|
|
52
|
+
dismissed = signal(new Set());
|
|
53
|
+
/**
|
|
54
|
+
* Info banners from the rules engine that are still showing.
|
|
55
|
+
*
|
|
56
|
+
* A getter, not a `computed`: the source is `dynamicFormComp`, a ViewChild. A computed
|
|
57
|
+
* evaluated before the view initialises would capture no dependency on the child's
|
|
58
|
+
* `ruleResult` signal and then never recompute. Read per change-detection pass instead.
|
|
59
|
+
*/
|
|
60
|
+
get infoBanners() {
|
|
61
|
+
const result = this.dynamicFormComp?.ruleResult();
|
|
62
|
+
const gone = this.dismissed();
|
|
63
|
+
return Object.entries(result?.infoBanners ?? {})
|
|
64
|
+
.filter(([fieldId]) => !gone.has(fieldId))
|
|
65
|
+
.map(([fieldId, message]) => ({ fieldId, message: String(message) }));
|
|
66
|
+
}
|
|
67
|
+
dismissInfoBanner(fieldId) {
|
|
68
|
+
this.dismissed.set(new Set(this.dismissed()).add(fieldId));
|
|
69
|
+
}
|
|
70
|
+
/** True when nothing in the record may be edited. */
|
|
71
|
+
get recordReadOnly() {
|
|
72
|
+
return this.readonly || this.isReadOnly;
|
|
73
|
+
}
|
|
74
|
+
// ─── Inline array-row editing ───────────────────────────────────────────────
|
|
75
|
+
/**
|
|
76
|
+
* A row of an `array` field is edited in a drawer, not inline in the tab panel.
|
|
77
|
+
*
|
|
78
|
+
* The drawer is rendered outside the tab container on purpose: the reference found that
|
|
79
|
+
* building a row's FormGroup inside the tab view races the tab's own initialisation, so
|
|
80
|
+
* the row form is kept detached and only pushed into the FormArray on save.
|
|
81
|
+
*/
|
|
82
|
+
inlineRowField = signal(null);
|
|
83
|
+
inlineRowIndex = signal(null);
|
|
84
|
+
inlineRowForm = null;
|
|
85
|
+
/** `array` fields on the tab currently shown, so the template can list their rows. */
|
|
86
|
+
get arrayFieldsForActiveTab() {
|
|
87
|
+
const tabId = this.activeTabId;
|
|
88
|
+
const tab = tabId ? findTab(this.config?.tabs, tabId) : null;
|
|
89
|
+
return (tab?.fields ?? []).filter(f => f.type === 'array');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Rows currently held by an `array` field, read from the record value.
|
|
93
|
+
*
|
|
94
|
+
* Deliberately not read off the child's FormArray: that control only exists once the
|
|
95
|
+
* child has initialised, so rendering from it changed value mid-pass. `currentData` is a
|
|
96
|
+
* signal seeded from `initialData` and kept current by the child's `formChange`.
|
|
97
|
+
*/
|
|
98
|
+
rowsOf(field) {
|
|
99
|
+
const tabId = this.activeTabId;
|
|
100
|
+
const tabData = tabId ? getTabData(tabId, this.currentData(), this.config) : null;
|
|
101
|
+
const rows = tabData?.[field.id];
|
|
102
|
+
return Array.isArray(rows) ? rows : [];
|
|
103
|
+
}
|
|
104
|
+
/** One row rendered as text, so the list is readable without opening the drawer. */
|
|
105
|
+
rowSummary(field, row) {
|
|
106
|
+
const value = row ?? {};
|
|
107
|
+
const parts = (field.children ?? [])
|
|
108
|
+
.map(child => value[child.id])
|
|
109
|
+
.filter(v => v !== null && v !== undefined && v !== '')
|
|
110
|
+
.map(v => (typeof v === 'object' ? JSON.stringify(v) : String(v)));
|
|
111
|
+
return parts.length ? parts.join(' · ') : '(empty)';
|
|
112
|
+
}
|
|
113
|
+
openAddRow(field) {
|
|
114
|
+
if (this.sectionReadOnly)
|
|
115
|
+
return;
|
|
116
|
+
this.inlineRowField.set(field);
|
|
117
|
+
this.inlineRowIndex.set(null);
|
|
118
|
+
this.inlineRowForm = this.dynamicFormComp?.createArrayRow(field);
|
|
119
|
+
}
|
|
120
|
+
openEditRow(field, index) {
|
|
121
|
+
if (this.sectionReadOnly)
|
|
122
|
+
return;
|
|
123
|
+
this.inlineRowField.set(field);
|
|
124
|
+
this.inlineRowIndex.set(index);
|
|
125
|
+
this.inlineRowForm = this.dynamicFormComp?.createArrayRow(field, this.rowsOf(field)[index]);
|
|
126
|
+
}
|
|
127
|
+
cancelRow() {
|
|
128
|
+
this.inlineRowField.set(null);
|
|
129
|
+
this.inlineRowIndex.set(null);
|
|
130
|
+
this.inlineRowForm = null;
|
|
131
|
+
}
|
|
132
|
+
/** Commit the drawer into the FormArray. Rules re-evaluate off the resulting value change. */
|
|
133
|
+
saveRow() {
|
|
134
|
+
const field = this.inlineRowField();
|
|
135
|
+
const form = this.dynamicFormComp;
|
|
136
|
+
if (!field || !form || !this.inlineRowForm)
|
|
137
|
+
return;
|
|
138
|
+
if (this.inlineRowForm.invalid) {
|
|
139
|
+
this.inlineRowForm.markAllAsTouched();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const array = form.getArrayControl(field.id, this.activeTabId ?? undefined);
|
|
143
|
+
if (!array)
|
|
144
|
+
return;
|
|
145
|
+
const index = this.inlineRowIndex();
|
|
146
|
+
if (index === null)
|
|
147
|
+
array.push(form.createArrayRow(field, this.inlineRowForm.value));
|
|
148
|
+
else
|
|
149
|
+
array.at(index).patchValue(this.inlineRowForm.value);
|
|
150
|
+
array.updateValueAndValidity();
|
|
151
|
+
this.currentData.set(form.extractRecord());
|
|
152
|
+
this.cancelRow();
|
|
153
|
+
}
|
|
154
|
+
deleteRow(field, index) {
|
|
155
|
+
if (this.sectionReadOnly)
|
|
156
|
+
return;
|
|
157
|
+
const array = this.dynamicFormComp?.getArrayControl(field.id, this.activeTabId ?? undefined);
|
|
158
|
+
array?.removeAt(index);
|
|
159
|
+
array?.updateValueAndValidity();
|
|
160
|
+
if (this.dynamicFormComp)
|
|
161
|
+
this.currentData.set(this.dynamicFormComp.extractRecord());
|
|
162
|
+
if (this.inlineRowIndex() === index)
|
|
163
|
+
this.cancelRow();
|
|
164
|
+
}
|
|
165
|
+
// ─── Per-tab section editing ────────────────────────────────────────────────
|
|
166
|
+
/**
|
|
167
|
+
* The tab currently open for editing, or null when the record is being viewed.
|
|
168
|
+
*
|
|
169
|
+
* The reference edits one tab section at a time rather than the whole record, and
|
|
170
|
+
* validates only that section on save — a required field on an untouched tab must not
|
|
171
|
+
* block saving the tab you are actually working on.
|
|
172
|
+
*/
|
|
173
|
+
editingTabId = signal(null);
|
|
174
|
+
/**
|
|
175
|
+
* The tab the inner form is showing.
|
|
176
|
+
*
|
|
177
|
+
* Tracked here rather than read off the ViewChild. The child resolves its first tab as
|
|
178
|
+
* part of its own initialisation, so reading it during this component's render changed
|
|
179
|
+
* value mid-pass — an ExpressionChangedAfterItHasBeenChecked error. Seeded from the
|
|
180
|
+
* config and kept in step by the child's `activeTabChange` output.
|
|
181
|
+
*/
|
|
182
|
+
activeTabIdSig = signal(null);
|
|
183
|
+
get activeTabId() {
|
|
184
|
+
return this.activeTabIdSig();
|
|
185
|
+
}
|
|
186
|
+
onActiveTabChange(tabId) {
|
|
187
|
+
if (this.activeTabIdSig() === tabId)
|
|
188
|
+
return;
|
|
189
|
+
this.activeTabIdSig.set(tabId);
|
|
190
|
+
// Editing does not follow the user to another tab.
|
|
191
|
+
this.editingTabId.set(null);
|
|
192
|
+
this.cancelRow();
|
|
193
|
+
}
|
|
194
|
+
/** First tab a user can see, used to seed the active tab before the child reports one. */
|
|
195
|
+
firstVisibleTabId() {
|
|
196
|
+
return this.config?.tabs?.find(t => t.visibility !== false)?.id ?? null;
|
|
197
|
+
}
|
|
198
|
+
/** Errors from the last save attempt, by field id. */
|
|
199
|
+
sectionErrors = signal({});
|
|
200
|
+
/** Template access to `Object.keys` for iterating the error map. */
|
|
201
|
+
Object = Object;
|
|
202
|
+
get isEditingActiveTab() {
|
|
203
|
+
// Without view mode there is no view/edit distinction — everything is always editable.
|
|
204
|
+
if (!this.viewMode)
|
|
205
|
+
return true;
|
|
206
|
+
return !!this.activeTabId && this.editingTabId() === this.activeTabId;
|
|
207
|
+
}
|
|
208
|
+
/** Fields render read-only unless their own tab is the one being edited. */
|
|
209
|
+
get sectionReadOnly() {
|
|
210
|
+
return this.recordReadOnly || !this.isEditingActiveTab;
|
|
211
|
+
}
|
|
212
|
+
editSection() {
|
|
213
|
+
if (!this.viewMode || this.recordReadOnly || !this.activeTabId)
|
|
214
|
+
return;
|
|
215
|
+
this.sectionErrors.set({});
|
|
216
|
+
this.editingTabId.set(this.activeTabId);
|
|
217
|
+
}
|
|
218
|
+
/** Discard this section's edits, restoring each field from the session baseline. */
|
|
219
|
+
cancelSection() {
|
|
220
|
+
const tabId = this.editingTabId();
|
|
221
|
+
const form = this.dynamicFormComp;
|
|
222
|
+
if (tabId && form) {
|
|
223
|
+
const tab = findTab(this.config?.tabs, tabId);
|
|
224
|
+
const baselineTab = (getTabData(tabId, this.originalBaseline(), this.config) ?? {});
|
|
225
|
+
for (const field of tab?.fields ?? []) {
|
|
226
|
+
const baselineValue = baselineTab[field.id];
|
|
227
|
+
// A FormArray cannot be restored with setValue — that demands a value shaped exactly
|
|
228
|
+
// like the current rows. Rebuild it from the baseline rows instead.
|
|
229
|
+
const array = form.getArrayControl(field.id, tabId);
|
|
230
|
+
if (array) {
|
|
231
|
+
array.clear();
|
|
232
|
+
for (const row of Array.isArray(baselineValue) ? baselineValue : []) {
|
|
233
|
+
array.push(form.createArrayRow(field, row));
|
|
234
|
+
}
|
|
235
|
+
array.updateValueAndValidity();
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
form.getControl(field.id, tabId)?.setValue(baselineValue ?? null);
|
|
239
|
+
}
|
|
240
|
+
this.currentData.set(form.extractRecord());
|
|
241
|
+
}
|
|
242
|
+
this.sectionErrors.set({});
|
|
243
|
+
this.editingTabId.set(null);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Validate this section only, then emit it. Validation is scoped two ways: Angular
|
|
247
|
+
* validity of the tab's own controls, and rules filtered to this tab — a rule targeting
|
|
248
|
+
* a hidden tab must not block the save (the reference's OV0-968 fix).
|
|
249
|
+
*/
|
|
250
|
+
saveSection() {
|
|
251
|
+
const tabId = this.editingTabId();
|
|
252
|
+
const form = this.dynamicFormComp;
|
|
253
|
+
if (!tabId || !form)
|
|
254
|
+
return;
|
|
255
|
+
const tab = findTab(this.config?.tabs, tabId);
|
|
256
|
+
const errors = {};
|
|
257
|
+
for (const field of tab?.fields ?? []) {
|
|
258
|
+
const ctrl = form.getControl(field.id, tabId);
|
|
259
|
+
if (ctrl?.invalid) {
|
|
260
|
+
ctrl.markAsTouched();
|
|
261
|
+
errors[field.id] = `${resolveLabel(field.label, this.language)} is invalid.`;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const scoped = this.rulesEvaluation.filterForTab(this.rules, tabId, this.config);
|
|
265
|
+
const ruleErrors = this.rulesEvaluation.evaluate(scoped, form.formValues(), this.originalBaseline())
|
|
266
|
+
.validationErrors;
|
|
267
|
+
Object.assign(errors, ruleErrors);
|
|
268
|
+
if (Object.keys(errors).length > 0) {
|
|
269
|
+
this.sectionErrors.set(errors);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
this.sectionErrors.set({});
|
|
273
|
+
this.editingTabId.set(null);
|
|
274
|
+
const record = form.extractRecord();
|
|
275
|
+
this.currentData.set(record);
|
|
276
|
+
this.sectionSave.emit({ tabId, record });
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Seed the session baseline from the loaded record so `VALUE_CHANGED` rules and the
|
|
280
|
+
* modification banner compare against the record as it was opened — not as it was
|
|
281
|
+
* after the first keystroke.
|
|
282
|
+
*/
|
|
283
|
+
ngOnChanges(changes) {
|
|
284
|
+
if (changes['initialData'] || changes['config']) {
|
|
285
|
+
this.originalBaseline.set({ ...(this.initialData ?? {}) });
|
|
286
|
+
this.currentData.set({ ...(this.initialData ?? {}) });
|
|
287
|
+
this.activeTabIdSig.set(this.firstVisibleTabId());
|
|
288
|
+
// A different record is a different set of banners — re-arm them.
|
|
289
|
+
this.dismissed.set(new Set());
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
get recordTitle() {
|
|
293
|
+
if (!this.config)
|
|
294
|
+
return 'Record';
|
|
295
|
+
return this.config.name ? resolveLabel(this.config.name, this.language) : this.config.entity;
|
|
296
|
+
}
|
|
297
|
+
get avatarLetter() {
|
|
298
|
+
return (this.recordTitle || 'R').charAt(0).toUpperCase();
|
|
299
|
+
}
|
|
300
|
+
// ─── Header fields (isProfileImage / isHeaderToggle) ────────────────────────
|
|
301
|
+
/** Every field in the config, at any depth. */
|
|
302
|
+
allFields() {
|
|
303
|
+
const out = [];
|
|
304
|
+
const walkFields = (fields) => {
|
|
305
|
+
for (const f of fields ?? []) {
|
|
306
|
+
out.push(f);
|
|
307
|
+
walkFields(f.children);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
const walkTabs = (tabs) => {
|
|
311
|
+
for (const t of tabs ?? []) {
|
|
312
|
+
walkFields(t.fields);
|
|
313
|
+
walkTabs(t.children);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
walkTabs(this.config?.tabs);
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
/** The field flagged `isProfileImage`, rendered as the record avatar. */
|
|
320
|
+
get headerProfileField() {
|
|
321
|
+
return this.allFields().find(f => f.isProfileImage) ?? null;
|
|
322
|
+
}
|
|
323
|
+
/** The field flagged `isHeaderToggle`, rendered as a status switch in the header. */
|
|
324
|
+
get headerToggleField() {
|
|
325
|
+
return this.allFields().find(f => f.isHeaderToggle) ?? null;
|
|
326
|
+
}
|
|
327
|
+
/** Avatar image URL, when the profile field holds a persisted FileRef. */
|
|
328
|
+
get profileImageUrl() {
|
|
329
|
+
const field = this.headerProfileField;
|
|
330
|
+
if (!field)
|
|
331
|
+
return null;
|
|
332
|
+
const value = this.fieldValue(field.id);
|
|
333
|
+
return value?.url ?? null;
|
|
334
|
+
}
|
|
335
|
+
get headerToggleValue() {
|
|
336
|
+
const field = this.headerToggleField;
|
|
337
|
+
return field ? this.fieldValue(field.id) === true : false;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Flip the header toggle. It writes straight to the control rather than going through
|
|
341
|
+
* section editing — the reference treats it as a record-level status switch, not part of
|
|
342
|
+
* any one tab's section.
|
|
343
|
+
*/
|
|
344
|
+
toggleHeaderStatus() {
|
|
345
|
+
const field = this.headerToggleField;
|
|
346
|
+
if (!field || this.recordReadOnly)
|
|
347
|
+
return;
|
|
348
|
+
const ctrl = this.controlFor(field.id);
|
|
349
|
+
if (!ctrl)
|
|
350
|
+
return;
|
|
351
|
+
ctrl.setValue(!this.headerToggleValue);
|
|
352
|
+
if (this.dynamicFormComp)
|
|
353
|
+
this.currentData.set(this.dynamicFormComp.extractRecord());
|
|
354
|
+
}
|
|
355
|
+
/** Locate a field's control wherever it lives, since header fields may be on any tab. */
|
|
356
|
+
controlFor(fieldId) {
|
|
357
|
+
return this.dynamicFormComp?.getControl(fieldId) ?? null;
|
|
358
|
+
}
|
|
359
|
+
/** A field's current value, read from the record so it is stable during render. */
|
|
360
|
+
fieldValue(fieldId) {
|
|
361
|
+
for (const tab of this.config?.tabs ?? []) {
|
|
362
|
+
const data = getTabData(tab.id, this.currentData(), this.config);
|
|
363
|
+
if (data && fieldId in data)
|
|
364
|
+
return data[fieldId];
|
|
365
|
+
}
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
isModified = computed(() => {
|
|
369
|
+
const orig = this.originalBaseline();
|
|
370
|
+
const curr = this.currentData();
|
|
371
|
+
if (!orig || !curr || Object.keys(orig).length === 0)
|
|
372
|
+
return false;
|
|
373
|
+
return JSON.stringify(orig) !== JSON.stringify(curr);
|
|
374
|
+
});
|
|
375
|
+
summaryFields = computed(() => {
|
|
376
|
+
const fields = [];
|
|
377
|
+
const walkTabs = (tabs = []) => {
|
|
378
|
+
for (const t of tabs) {
|
|
379
|
+
for (const f of t.fields ?? []) {
|
|
380
|
+
if (f.showOnMinimize)
|
|
381
|
+
fields.push(f);
|
|
382
|
+
}
|
|
383
|
+
if (t.children)
|
|
384
|
+
walkTabs(t.children);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
walkTabs(this.config?.tabs);
|
|
388
|
+
return fields;
|
|
389
|
+
});
|
|
390
|
+
onFormChange(data) {
|
|
391
|
+
// Baseline is seeded from `initialData` in ngOnChanges; only fill it here when the
|
|
392
|
+
// record was opened empty (create flow), so the first emitted shape becomes the baseline.
|
|
393
|
+
if (Object.keys(this.originalBaseline()).length === 0) {
|
|
394
|
+
this.originalBaseline.set({ ...data });
|
|
395
|
+
}
|
|
396
|
+
this.currentData.set(data);
|
|
397
|
+
this.formChange.emit(data);
|
|
398
|
+
}
|
|
399
|
+
onFormSubmit(data) {
|
|
400
|
+
this.formSubmit.emit(data);
|
|
401
|
+
}
|
|
402
|
+
onFormReset() {
|
|
403
|
+
this.formReset.emit();
|
|
404
|
+
}
|
|
405
|
+
formatFieldLabel(field) {
|
|
406
|
+
return resolveLabel(field.label, this.language);
|
|
407
|
+
}
|
|
408
|
+
/** Summary values render through the shared core formatter, not a local stringifier. */
|
|
409
|
+
formatFieldValue(field) {
|
|
410
|
+
return formatDisplayValue(field.type, field.options, this.currentData()[field.id], this.language);
|
|
411
|
+
}
|
|
412
|
+
jumpToField(fieldId) {
|
|
413
|
+
// Find parent tab for field
|
|
414
|
+
for (const tab of this.config?.tabs || []) {
|
|
415
|
+
const hasField = (tab.fields || []).some(f => f.id === fieldId);
|
|
416
|
+
if (hasField) {
|
|
417
|
+
this.dynamicFormComp?.setActiveTab(tab.id);
|
|
418
|
+
setTimeout(() => {
|
|
419
|
+
const el = document.getElementById(`field-container-${fieldId}`);
|
|
420
|
+
if (el) {
|
|
421
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
422
|
+
el.focus();
|
|
423
|
+
}
|
|
424
|
+
}, 50);
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicRecordFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
430
|
+
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\">\r\n <!-- Record Header -->\r\n <div class=\"ngx-record-editor__header\">\r\n <div class=\"ngx-record-editor__title-group\">\r\n @if (profileImageUrl) {\r\n <img\r\n class=\"ngx-record-editor__avatar\"\r\n data-testid=\"header-profile-image\"\r\n [src]=\"profileImageUrl\"\r\n [alt]=\"recordTitle\"\r\n />\r\n } @else {\r\n <div class=\"ngx-record-editor__avatar\" data-testid=\"header-avatar-initial\">\r\n {{ avatarLetter }}\r\n </div>\r\n }\r\n <div>\r\n <h2 class=\"ngx-record-editor__title\" data-testid=\"record-title\">\r\n {{ recordTitle }}\r\n </h2>\r\n <span class=\"ngx-record-editor__subtitle\">Entity: {{ config.entity }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Record-level status switch, outside section editing -->\r\n @if (headerToggleField; as toggleField) {\r\n <label class=\"ngx-record-editor__header-toggle\">\r\n <span>{{ formatFieldLabel(toggleField) }}</span>\r\n <input\r\n type=\"checkbox\"\r\n role=\"switch\"\r\n data-testid=\"header-toggle\"\r\n [checked]=\"headerToggleValue\"\r\n [disabled]=\"recordReadOnly\"\r\n [attr.aria-checked]=\"headerToggleValue\"\r\n (change)=\"toggleHeaderStatus()\"\r\n />\r\n <span class=\"ngx-record-editor__header-toggle-text\">{{ headerToggleValue ? 'Yes' : 'No' }}</span>\r\n </label>\r\n }\r\n </div>\r\n\r\n <!-- Dismissible info banners (rules `info` action) -->\r\n @for (banner of infoBanners; track banner.fieldId) {\r\n <div\r\n class=\"ngx-record-editor__banner ngx-record-editor__banner--info\"\r\n role=\"status\"\r\n [attr.data-testid]=\"'info-banner-' + banner.fieldId\"\r\n >\r\n <span>\u2139\uFE0F {{ banner.message }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"ngx-record-editor__banner-dismiss\"\r\n [attr.data-testid]=\"'dismiss-' + banner.fieldId\"\r\n [attr.aria-label]=\"'Dismiss: ' + banner.message\"\r\n (click)=\"dismissInfoBanner(banner.fieldId)\"\r\n >\u2715</button>\r\n </div>\r\n }\r\n\r\n <!-- Modification baseline banner -->\r\n @if (isModified()) {\r\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--warning\">\r\n \uD83D\uDD12 Record modified from session baseline \u2014 unsaved changes pending.\r\n </div>\r\n }\r\n\r\n <!-- Summary Panel (showOnMinimize fields with interactive jump) -->\r\n @if (summaryFields().length > 0) {\r\n <div class=\"ngx-record-editor__summary-panel\" data-testid=\"summary-panel\">\r\n @for (sf of summaryFields(); track sf.id) {\r\n <div\r\n class=\"ngx-record-editor__summary-item\"\r\n [attr.data-testid]=\"'summary-' + sf.id\"\r\n (click)=\"jumpToField(sf.id)\"\r\n [title]=\"'Click to jump to ' + formatFieldLabel(sf)\"\r\n >\r\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(sf) }}</span>\r\n <span class=\"ngx-record-editor__summary-val\">{{ formatFieldValue(sf) }}</span>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Section edit bar: one tab is edited at a time, and validated on its own -->\r\n @if (viewMode && !recordReadOnly) {\r\n <div class=\"ngx-record-editor__section-bar\">\r\n @if (isEditingActiveTab) {\r\n <span class=\"ngx-record-editor__section-hint\">Editing this section</span>\r\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-section\" (click)=\"cancelSection()\">\r\n Cancel\r\n </button>\r\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-section\" (click)=\"saveSection()\">\r\n Save section\r\n </button>\r\n } @else {\r\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"edit-section\" (click)=\"editSection()\">\r\n Edit section\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n @if (Object.keys(sectionErrors()).length > 0) {\r\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--error\" role=\"alert\" data-testid=\"section-errors\">\r\n @for (key of Object.keys(sectionErrors()); track key) {\r\n <div>\u26A0\uFE0F {{ sectionErrors()[key] }}</div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Array rows: listed here, edited in the drawer below -->\r\n @for (arrayField of arrayFieldsForActiveTab; track arrayField.id) {\r\n <div class=\"ngx-record-editor__rows\" [attr.data-testid]=\"'rows-' + arrayField.id\">\r\n <div class=\"ngx-record-editor__rows-head\">\r\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(arrayField) }}</span>\r\n @if (!sectionReadOnly) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-form__reset\"\r\n [attr.data-testid]=\"'add-row-' + arrayField.id\"\r\n (click)=\"openAddRow(arrayField)\"\r\n >+ Row</button>\r\n }\r\n </div>\r\n\r\n @for (row of rowsOf(arrayField); track $index) {\r\n <div class=\"ngx-record-editor__row\" [attr.data-testid]=\"'row-' + arrayField.id + '-' + $index\">\r\n <span class=\"ngx-record-editor__row-text\">{{ rowSummary(arrayField, row) }}</span>\r\n @if (!sectionReadOnly) {\r\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\r\n [attr.data-testid]=\"'edit-row-' + arrayField.id + '-' + $index\"\r\n (click)=\"openEditRow(arrayField, $index)\">Edit</button>\r\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\r\n [attr.data-testid]=\"'delete-row-' + arrayField.id + '-' + $index\"\r\n (click)=\"deleteRow(arrayField, $index)\">Delete</button>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"ngx-record-editor__row-empty\">No rows yet.</p>\r\n }\r\n </div>\r\n }\r\n\r\n <!--\r\n Row drawer. Deliberately outside the tab panel: building a row's FormGroup inside the\r\n tab view races the tab's own initialisation (the reference hit this with FormArray).\r\n -->\r\n @if (inlineRowField(); as rowField) {\r\n <div class=\"ngx-record-editor__drawer\" data-testid=\"row-drawer\" [formGroup]=\"inlineRowForm!\">\r\n <div class=\"ngx-record-editor__rows-head\">\r\n <strong>{{ inlineRowIndex() === null ? 'Add' : 'Edit' }} {{ formatFieldLabel(rowField) }} row</strong>\r\n </div>\r\n\r\n <div class=\"ngx-form__panel\">\r\n @for (child of rowField.children ?? []; track child.id) {\r\n <ngx-dynamic-field\r\n [field]=\"child\"\r\n [control]=\"inlineRowForm!.get(child.id)!\"\r\n [config]=\"config\"\r\n [language]=\"language\"\r\n [readonly]=\"false\"\r\n [userRoles]=\"userRoles\"\r\n [style.gridColumn]=\"'span 12'\"\r\n />\r\n }\r\n </div>\r\n\r\n <div class=\"ngx-record-editor__section-bar\">\r\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-row\" (click)=\"cancelRow()\">Cancel</button>\r\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-row\" (click)=\"saveRow()\">Save row</button>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Main Dynamic Form -->\r\n <ngx-dynamic-form\r\n [config]=\"config\"\r\n [rules]=\"rules\"\r\n [initialData]=\"initialData\"\r\n [originalData]=\"originalBaseline()\"\r\n [userRoles]=\"userRoles\"\r\n [language]=\"language\"\r\n [readonly]=\"sectionReadOnly\"\r\n [readOnlyFields]=\"readOnlyFields\"\r\n [showInfoBanners]=\"false\"\r\n [loading]=\"loading\"\r\n [error]=\"error\"\r\n (formChange)=\"onFormChange($event)\"\r\n (formSubmit)=\"onFormSubmit($event)\"\r\n (formReset)=\"onFormReset()\"\r\n (activeTabChange)=\"onActiveTabChange($event)\"\r\n />\r\n</div>\r\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"] }, { kind: "component", type: DynamicFieldComponent, selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "currentTabId", "language", "readonly", "userRoles"] }] });
|
|
431
|
+
}
|
|
432
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicRecordFormComponent, decorators: [{
|
|
433
|
+
type: Component,
|
|
434
|
+
args: [{ selector: 'ngx-dynamic-record-form', standalone: true, imports: [CommonModule, ReactiveFormsModule, DynamicFormComponent, DynamicFieldComponent], template: "<div class=\"ngx-record-editor\">\r\n <!-- Record Header -->\r\n <div class=\"ngx-record-editor__header\">\r\n <div class=\"ngx-record-editor__title-group\">\r\n @if (profileImageUrl) {\r\n <img\r\n class=\"ngx-record-editor__avatar\"\r\n data-testid=\"header-profile-image\"\r\n [src]=\"profileImageUrl\"\r\n [alt]=\"recordTitle\"\r\n />\r\n } @else {\r\n <div class=\"ngx-record-editor__avatar\" data-testid=\"header-avatar-initial\">\r\n {{ avatarLetter }}\r\n </div>\r\n }\r\n <div>\r\n <h2 class=\"ngx-record-editor__title\" data-testid=\"record-title\">\r\n {{ recordTitle }}\r\n </h2>\r\n <span class=\"ngx-record-editor__subtitle\">Entity: {{ config.entity }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Record-level status switch, outside section editing -->\r\n @if (headerToggleField; as toggleField) {\r\n <label class=\"ngx-record-editor__header-toggle\">\r\n <span>{{ formatFieldLabel(toggleField) }}</span>\r\n <input\r\n type=\"checkbox\"\r\n role=\"switch\"\r\n data-testid=\"header-toggle\"\r\n [checked]=\"headerToggleValue\"\r\n [disabled]=\"recordReadOnly\"\r\n [attr.aria-checked]=\"headerToggleValue\"\r\n (change)=\"toggleHeaderStatus()\"\r\n />\r\n <span class=\"ngx-record-editor__header-toggle-text\">{{ headerToggleValue ? 'Yes' : 'No' }}</span>\r\n </label>\r\n }\r\n </div>\r\n\r\n <!-- Dismissible info banners (rules `info` action) -->\r\n @for (banner of infoBanners; track banner.fieldId) {\r\n <div\r\n class=\"ngx-record-editor__banner ngx-record-editor__banner--info\"\r\n role=\"status\"\r\n [attr.data-testid]=\"'info-banner-' + banner.fieldId\"\r\n >\r\n <span>\u2139\uFE0F {{ banner.message }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"ngx-record-editor__banner-dismiss\"\r\n [attr.data-testid]=\"'dismiss-' + banner.fieldId\"\r\n [attr.aria-label]=\"'Dismiss: ' + banner.message\"\r\n (click)=\"dismissInfoBanner(banner.fieldId)\"\r\n >\u2715</button>\r\n </div>\r\n }\r\n\r\n <!-- Modification baseline banner -->\r\n @if (isModified()) {\r\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--warning\">\r\n \uD83D\uDD12 Record modified from session baseline \u2014 unsaved changes pending.\r\n </div>\r\n }\r\n\r\n <!-- Summary Panel (showOnMinimize fields with interactive jump) -->\r\n @if (summaryFields().length > 0) {\r\n <div class=\"ngx-record-editor__summary-panel\" data-testid=\"summary-panel\">\r\n @for (sf of summaryFields(); track sf.id) {\r\n <div\r\n class=\"ngx-record-editor__summary-item\"\r\n [attr.data-testid]=\"'summary-' + sf.id\"\r\n (click)=\"jumpToField(sf.id)\"\r\n [title]=\"'Click to jump to ' + formatFieldLabel(sf)\"\r\n >\r\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(sf) }}</span>\r\n <span class=\"ngx-record-editor__summary-val\">{{ formatFieldValue(sf) }}</span>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Section edit bar: one tab is edited at a time, and validated on its own -->\r\n @if (viewMode && !recordReadOnly) {\r\n <div class=\"ngx-record-editor__section-bar\">\r\n @if (isEditingActiveTab) {\r\n <span class=\"ngx-record-editor__section-hint\">Editing this section</span>\r\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-section\" (click)=\"cancelSection()\">\r\n Cancel\r\n </button>\r\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-section\" (click)=\"saveSection()\">\r\n Save section\r\n </button>\r\n } @else {\r\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"edit-section\" (click)=\"editSection()\">\r\n Edit section\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n @if (Object.keys(sectionErrors()).length > 0) {\r\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--error\" role=\"alert\" data-testid=\"section-errors\">\r\n @for (key of Object.keys(sectionErrors()); track key) {\r\n <div>\u26A0\uFE0F {{ sectionErrors()[key] }}</div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Array rows: listed here, edited in the drawer below -->\r\n @for (arrayField of arrayFieldsForActiveTab; track arrayField.id) {\r\n <div class=\"ngx-record-editor__rows\" [attr.data-testid]=\"'rows-' + arrayField.id\">\r\n <div class=\"ngx-record-editor__rows-head\">\r\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(arrayField) }}</span>\r\n @if (!sectionReadOnly) {\r\n <button\r\n type=\"button\"\r\n class=\"ngx-form__reset\"\r\n [attr.data-testid]=\"'add-row-' + arrayField.id\"\r\n (click)=\"openAddRow(arrayField)\"\r\n >+ Row</button>\r\n }\r\n </div>\r\n\r\n @for (row of rowsOf(arrayField); track $index) {\r\n <div class=\"ngx-record-editor__row\" [attr.data-testid]=\"'row-' + arrayField.id + '-' + $index\">\r\n <span class=\"ngx-record-editor__row-text\">{{ rowSummary(arrayField, row) }}</span>\r\n @if (!sectionReadOnly) {\r\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\r\n [attr.data-testid]=\"'edit-row-' + arrayField.id + '-' + $index\"\r\n (click)=\"openEditRow(arrayField, $index)\">Edit</button>\r\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\r\n [attr.data-testid]=\"'delete-row-' + arrayField.id + '-' + $index\"\r\n (click)=\"deleteRow(arrayField, $index)\">Delete</button>\r\n }\r\n </div>\r\n } @empty {\r\n <p class=\"ngx-record-editor__row-empty\">No rows yet.</p>\r\n }\r\n </div>\r\n }\r\n\r\n <!--\r\n Row drawer. Deliberately outside the tab panel: building a row's FormGroup inside the\r\n tab view races the tab's own initialisation (the reference hit this with FormArray).\r\n -->\r\n @if (inlineRowField(); as rowField) {\r\n <div class=\"ngx-record-editor__drawer\" data-testid=\"row-drawer\" [formGroup]=\"inlineRowForm!\">\r\n <div class=\"ngx-record-editor__rows-head\">\r\n <strong>{{ inlineRowIndex() === null ? 'Add' : 'Edit' }} {{ formatFieldLabel(rowField) }} row</strong>\r\n </div>\r\n\r\n <div class=\"ngx-form__panel\">\r\n @for (child of rowField.children ?? []; track child.id) {\r\n <ngx-dynamic-field\r\n [field]=\"child\"\r\n [control]=\"inlineRowForm!.get(child.id)!\"\r\n [config]=\"config\"\r\n [language]=\"language\"\r\n [readonly]=\"false\"\r\n [userRoles]=\"userRoles\"\r\n [style.gridColumn]=\"'span 12'\"\r\n />\r\n }\r\n </div>\r\n\r\n <div class=\"ngx-record-editor__section-bar\">\r\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-row\" (click)=\"cancelRow()\">Cancel</button>\r\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-row\" (click)=\"saveRow()\">Save row</button>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Main Dynamic Form -->\r\n <ngx-dynamic-form\r\n [config]=\"config\"\r\n [rules]=\"rules\"\r\n [initialData]=\"initialData\"\r\n [originalData]=\"originalBaseline()\"\r\n [userRoles]=\"userRoles\"\r\n [language]=\"language\"\r\n [readonly]=\"sectionReadOnly\"\r\n [readOnlyFields]=\"readOnlyFields\"\r\n [showInfoBanners]=\"false\"\r\n [loading]=\"loading\"\r\n [error]=\"error\"\r\n (formChange)=\"onFormChange($event)\"\r\n (formSubmit)=\"onFormSubmit($event)\"\r\n (formReset)=\"onFormReset()\"\r\n (activeTabChange)=\"onActiveTabChange($event)\"\r\n />\r\n</div>\r\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"] }]
|
|
435
|
+
}], propDecorators: { config: [{
|
|
436
|
+
type: Input
|
|
437
|
+
}], rules: [{
|
|
438
|
+
type: Input
|
|
439
|
+
}], initialData: [{
|
|
440
|
+
type: Input
|
|
441
|
+
}], userRoles: [{
|
|
442
|
+
type: Input
|
|
443
|
+
}], language: [{
|
|
444
|
+
type: Input
|
|
445
|
+
}], readonly: [{
|
|
446
|
+
type: Input
|
|
447
|
+
}], loading: [{
|
|
448
|
+
type: Input
|
|
449
|
+
}], error: [{
|
|
450
|
+
type: Input
|
|
451
|
+
}], isReadOnly: [{
|
|
452
|
+
type: Input
|
|
453
|
+
}], readOnlyFields: [{
|
|
454
|
+
type: Input
|
|
455
|
+
}], viewMode: [{
|
|
456
|
+
type: Input
|
|
457
|
+
}], formSubmit: [{
|
|
458
|
+
type: Output
|
|
459
|
+
}], formChange: [{
|
|
460
|
+
type: Output
|
|
461
|
+
}], formReset: [{
|
|
462
|
+
type: Output
|
|
463
|
+
}], sectionSave: [{
|
|
464
|
+
type: Output
|
|
465
|
+
}], dynamicFormComp: [{
|
|
466
|
+
type: ViewChild,
|
|
467
|
+
args: [DynamicFormComponent]
|
|
468
|
+
}] } });
|
|
469
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1yZWNvcmQtZm9ybS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2Zvcm0vZHluYW1pYy1yZWNvcmQtZm9ybS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL2Zvcm0vZHluYW1pYy1yZWNvcmQtZm9ybS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUNULEtBQUssRUFDTCxNQUFNLEVBQ04sWUFBWSxFQUdaLE1BQU0sRUFDTixRQUFRLEVBQ1IsTUFBTSxFQUNOLFNBQVMsR0FDVixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUE4QixtQkFBbUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRWpGLE9BQU8sRUFBRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzdGLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHNDQUFzQyxDQUFDOzs7QUFFOUU7Ozs7R0FJRztBQW9NSCxNQUFNLE9BQU8sMEJBQTBCO0lBQzVCLE1BQU0sQ0FBb0I7SUFDMUIsS0FBSyxDQUFjO0lBQ25CLFdBQVcsQ0FBdUI7SUFDbEMsU0FBUyxHQUFhLEVBQUUsQ0FBQztJQUN6QixRQUFRLEdBQVcsSUFBSSxDQUFDO0lBQ3hCLFFBQVEsR0FBWSxLQUFLLENBQUM7SUFDMUIsT0FBTyxHQUFZLEtBQUssQ0FBQztJQUN6QixLQUFLLEdBQWtCLElBQUksQ0FBQztJQUNyQyw4RkFBOEY7SUFDckYsVUFBVSxHQUFZLEtBQUssQ0FBQztJQUNy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UUFBUTtzQkFBaEIsS0FBSztnQkFDRyxPQUFPO3NCQUFmLEtBQUs7Z0JBQ0csS0FBSztzQkFBYixLQUFLO2dCQUVHLFVBQVU7c0JBQWxCLEtBQUs7Z0JBRUcsY0FBYztzQkFBdEIsS0FBSztnQkFRRyxRQUFRO3NCQUFoQixLQUFLO2dCQUVJLFVBQVU7c0JBQW5CLE1BQU07Z0JBQ0csVUFBVTtzQkFBbkIsTUFBTTtnQkFDRyxTQUFTO3NCQUFsQixNQUFNO2dCQUVHLFdBQVc7c0JBQXBCLE1BQU07Z0JBRTBCLGVBQWU7c0JBQS9DLFNBQVM7dUJBQUMsb0JBQW9CIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcclxuICBDb21wb25lbnQsXHJcbiAgSW5wdXQsXHJcbiAgT3V0cHV0LFxyXG4gIEV2ZW50RW1pdHRlcixcclxuICBPbkNoYW5nZXMsXHJcbiAgU2ltcGxlQ2hhbmdlcyxcclxuICBzaWduYWwsXHJcbiAgY29tcHV0ZWQsXHJcbiAgaW5qZWN0LFxyXG4gIFZpZXdDaGlsZCxcclxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcclxuaW1wb3J0IHsgQWJzdHJhY3RDb250cm9sLCBGb3JtR3JvdXAsIFJlYWN0aXZlRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XHJcbmltcG9ydCB0eXBlIHsgRW50aXR5Rm9ybUNvbmZpZywgRm9ybVJ1bGUsIE5lc3RlZEZpZWxkQ29uZmlnLCBOZXN0ZWRUYWJDb25maWcgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XHJcbmltcG9ydCB7IGZpbmRUYWIsIGZvcm1hdERpc3BsYXlWYWx1ZSwgZ2V0VGFiRGF0YSwgcmVzb2x2ZUxhYmVsIH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xyXG5pbXBvcnQgeyBEeW5hbWljRm9ybUNvbXBvbmVudCB9IGZyb20gJy4vZHluYW1pYy1mb3JtLmNvbXBvbmVudCc7XHJcbmltcG9ydCB7IER5bmFtaWNGaWVsZENvbXBvbmVudCB9IGZyb20gJy4vZHluYW1pYy1maWVsZC9keW5hbWljLWZpZWxkLmNvbXBvbmVudCc7XHJcbmltcG9ydCB7IFJ1bGVzRXZhbHVhdGlvblNlcnZpY2UgfSBmcm9tICcuLi9zZXJ2aWNlcy9ydWxlcy1ldmFsdWF0aW9uLnNlcnZpY2UnO1xyXG5cclxuLyoqXHJcbiAqIER5bmFtaWNSZWNvcmRGb3JtQ29tcG9uZW50IOKAlCBjb21wcmVoZW5zaXZlIHRhYmJlZCByZWNvcmQgdmlldyAmIGVkaXQgY29tcG9uZW50LlxyXG4gKiBTdXBwb3J0cyBjcm9zcy10YWIgcnVsZSBldmFsdWF0aW9uLCBwcm9maWxlIGhlYWRlciwgc3VtbWFyeSBkcmF3ZXIgKHNob3dPbk1pbmltaXplKSxcclxuICogaW50ZXJhY3RpdmUgcXVpY2stanVtcCBsaW5rcywgYW5kIGJhc2VsaW5lIG1vZGlmaWNhdGlvbiB0cmFja2luZy5cclxuICovXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAnbmd4LWR5bmFtaWMtcmVjb3JkLWZvcm0nLFxyXG4gIHN0YW5kYWxvbmU6IHRydWUsXHJcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZSwgUmVhY3RpdmVGb3Jtc01vZHVsZSwgRHluYW1pY0Zvcm1Db21wb25lbnQsIER5bmFtaWNGaWVsZENvbXBvbmVudF0sXHJcbiAgdGVtcGxhdGVVcmw6ICcuL2R5bmFtaWMtcmVjb3JkLWZvcm0uY29tcG9uZW50Lmh0bWwnLFxyXG4gIHN0eWxlczogW1xyXG4gICAgYFxyXG4gICAgICAubmd4LXJlY29yZC1lZGl0b3Ige1xyXG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XHJcbiAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcclxuICAgICAgICBnYXA6IDE2cHg7XHJcbiAgICAgICAgYmFja2dyb3VuZDogI2ZmZmZmZjtcclxuICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjZTVlN2ViO1xyXG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDEycHg7XHJcbiAgICAgICAgcGFkZGluZzogMjBweDtcclxuICAgICAgICBib3gtc2hhZG93OiAwIDRweCA2cHggLTFweCByZ2JhKDAsIDAsIDAsIDAuMDUpO1xyXG4gICAgICB9XHJcbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9faGVhZGVyIHtcclxuICAgICAgICBkaXNwbGF5OiBmbGV4O1xyXG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XHJcbiAgICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xyXG4gICAgICAgIHBhZGRpbmctYm90dG9tOiAxNnB4O1xyXG4gICAgICAgIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZjNmNGY2O1xyXG4gICAgICB9XHJcbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fdGl0bGUtZ3JvdXAge1xyXG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XHJcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcclxuICAgICAgICBnYXA6IDEycHg7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19hdmF0YXIge1xyXG4gICAgICAgIHdpZHRoOiA0OHB4O1xyXG4gICAgICAgIGhlaWdodDogNDhweDtcclxuICAgICAgICBib3JkZXItcmFkaXVzOiA1MCU7XHJcbiAgICAgICAgb2JqZWN0LWZpdDogY292ZXI7XHJcbiAgICAgICAgYmFja2dyb3VuZDogI2UwZTdmZjtcclxuICAgICAgICBjb2xvcjogIzRmNDZlNTtcclxuICAgICAgICBkaXNwbGF5OiBmbGV4O1xyXG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XHJcbiAgICAgICAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XHJcbiAgICAgICAgZm9udC13ZWlnaHQ6IDcwMDtcclxuICAgICAgICBmb250LXNpemU6IDE4cHg7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX190aXRsZSB7XHJcbiAgICAgICAgZm9udC1zaXplOiAyMHB4O1xyXG4gICAgICAgIGZvbnQtd2VpZ2h0OiA3MDA7XHJcbiAgICAgICAgY29sb3I6ICMxMTE4Mjc7XHJcbiAgICAgICAgbWFyZ2luOiAwO1xyXG4gICAgICB9XHJcbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fc3VidGl0bGUge1xyXG4gICAgICAgIGZvbnQtc2l6ZTogMTNweDtcclxuICAgICAgICBjb2xvcjogIzZiNzI4MDtcclxuICAgICAgfVxyXG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX2hlYWRlci10b2dnbGUge1xyXG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XHJcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcclxuICAgICAgICBnYXA6IDhweDtcclxuICAgICAgICBmb250LXNpemU6IDEzcHg7XHJcbiAgICAgICAgY29sb3I6ICMzNzQxNTE7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19oZWFkZXItdG9nZ2xlLXRleHQge1xyXG4gICAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXIge1xyXG4gICAgICAgIHBhZGRpbmc6IDEwcHggMTRweDtcclxuICAgICAgICBib3JkZXItcmFkaXVzOiA4cHg7XHJcbiAgICAgICAgZm9udC1zaXplOiAxM3B4O1xyXG4gICAgICAgIGZvbnQtd2VpZ2h0OiA1MDA7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXItLWluZm8ge1xyXG4gICAgICAgIGJhY2tncm91bmQ6ICNlZmY2ZmY7XHJcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2RiZWFmZTtcclxuICAgICAgICBjb2xvcjogIzFkNGVkODtcclxuICAgICAgICBkaXNwbGF5OiBmbGV4O1xyXG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XHJcbiAgICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xyXG4gICAgICAgIGdhcDogMTJweDtcclxuICAgICAgfVxyXG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lci1kaXNtaXNzIHtcclxuICAgICAgICBiYWNrZ3JvdW5kOiBub25lO1xyXG4gICAgICAgIGJvcmRlcjogbm9uZTtcclxuICAgICAgICBjdXJzb3I6IHBvaW50ZXI7XHJcbiAgICAgICAgY29sb3I6IGluaGVyaXQ7XHJcbiAgICAgICAgZm9udC1zaXplOiAxNHB4O1xyXG4gICAgICAgIGxpbmUtaGVpZ2h0OiAxO1xyXG4gICAgICAgIHBhZGRpbmc6IDJweCA2cHg7XHJcbiAgICAgICAgYm9yZGVyLXJhZGl1czogNHB4O1xyXG4gICAgICB9XHJcbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLWRpc21pc3M6aG92ZXIge1xyXG4gICAgICAgIGJhY2tncm91bmQ6IHJnYmEoMCwgMCwgMCwgMC4wNik7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXItLWVycm9yIHtcclxuICAgICAgICBiYWNrZ3JvdW5kOiAjZmVmMmYyO1xyXG4gICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNmZWNhY2E7XHJcbiAgICAgICAgY29sb3I6ICNiOTFjMWM7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zZWN0aW9uLWJhciB7XHJcbiAgICAgICAgZGlzcGxheTogZmxleDtcclxuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xyXG4gICAgICAgIGp1c3RpZnktY29udGVudDogZmxleC1lbmQ7XHJcbiAgICAgICAgZ2FwOiA4cHg7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zZWN0aW9uLWhpbnQge1xyXG4gICAgICAgIG1hcmdpbi1yaWdodDogYXV0bztcclxuICAgICAgICBmb250LXNpemU6IDEycHg7XHJcbiAgICAgICAgY29sb3I6ICM2YjcyODA7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXItLXdhcm5pbmcge1xyXG4gICAgICAgIGJhY2tncm91bmQ6ICNmZmZiZWI7XHJcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2ZlZjNjNztcclxuICAgICAgICBjb2xvcjogI2I0NTMwOTtcclxuICAgICAgfVxyXG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3Jvd3Mge1xyXG4gICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNmM2Y0ZjY7XHJcbiAgICAgICAgYm9yZGVyLXJhZGl1czogOHB4O1xyXG4gICAgICAgIHBhZGRpbmc6IDEycHggMTRweDtcclxuICAgICAgICBkaXNwbGF5OiBmbGV4O1xyXG4gICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XHJcbiAgICAgICAgZ2FwOiA2cHg7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19yb3dzLWhlYWQge1xyXG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XHJcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcclxuICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47XHJcbiAgICAgICAgZ2FwOiA4cHg7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19yb3cge1xyXG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XHJcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcclxuICAgICAgICBnYXA6IDhweDtcclxuICAgICAgICBwYWRkaW5nOiA2cHggOHB4O1xyXG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDZweDtcclxuICAgICAgICBiYWNrZ3JvdW5kOiAjZjlmYWZiO1xyXG4gICAgICB9XHJcbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fcm93LXRleHQge1xyXG4gICAgICAgIGZsZXg6IDE7XHJcbiAgICAgICAgZm9udC1zaXplOiAxM3B4O1xyXG4gICAgICAgIG92ZXJmbG93LXdyYXA6IGFueXdoZXJlO1xyXG4gICAgICB9XHJcbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fcm93LWJ0biB7XHJcbiAgICAgICAgYmFja2dyb3VuZDogbm9uZTtcclxuICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjZTVlN2ViO1xyXG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDZweDtcclxuICAgICAgICBjdXJzb3I6IHBvaW50ZXI7XHJcbiAgICAgICAgZm9udC1zaXplOiAxMnB4O1xyXG4gICAgICAgIHBhZGRpbmc6IDJweCA4cHg7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19yb3ctZW1wdHkge1xyXG4gICAgICAgIGZvbnQtc2l6ZTogMTJweDtcclxuICAgICAgICBjb2xvcjogIzZiNzI4MDtcclxuICAgICAgICBtYXJnaW46IDA7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19kcmF3ZXIge1xyXG4gICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNkYmVhZmU7XHJcbiAgICAgICAgYmFja2dyb3VuZDogI2Y4ZmFmYztcclxuICAgICAgICBib3JkZXItcmFkaXVzOiA4cHg7XHJcbiAgICAgICAgcGFkZGluZzogMTRweDtcclxuICAgICAgICBkaXNwbGF5OiBmbGV4O1xyXG4gICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XHJcbiAgICAgICAgZ2FwOiAxMnB4O1xyXG4gICAgICB9XHJcbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fc3VtbWFyeS1wYW5lbCB7XHJcbiAgICAgICAgYmFja2dyb3VuZDogI2Y5ZmFmYjtcclxuICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjZjNmNGY2O1xyXG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDhweDtcclxuICAgICAgICBwYWRkaW5nOiAxMnB4IDE2cHg7XHJcbiAgICAgICAgZGlzcGxheTogZ3JpZDtcclxuICAgICAgICBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IHJlcGVhdChhdXRvLWZpdCwgbWlubWF4KDE4MHB4LCAxZnIpKTtcclxuICAgICAgICBnYXA6IDEycHg7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zdW1tYXJ5LWl0ZW0ge1xyXG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XHJcbiAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcclxuICAgICAgICBjdXJzb3I6IHBvaW50ZXI7XHJcbiAgICAgICAgcGFkZGluZzogNnB4IDEwcHg7XHJcbiAgICAgICAgYm9yZGVyLXJhZGl1czogNnB4O1xyXG4gICAgICAgIHRyYW5zaXRpb246IGJhY2tncm91bmQgMC4xNXM7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zdW1tYXJ5LWl0ZW06aG92ZXIge1xyXG4gICAgICAgIGJhY2tncm91bmQ6ICNlZmY2ZmY7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zdW1tYXJ5LWxhYmVsIHtcclxuICAgICAgICBmb250LXNpemU6IDExcHg7XHJcbiAgICAgICAgdGV4dC10cmFuc2Zvcm06IHVwcGVyY2FzZTtcclxuICAgICAgICBsZXR0ZXItc3BhY2luZzogMC4wNWVtO1xyXG4gICAgICAgIGNvbG9yOiAjNmI3MjgwO1xyXG4gICAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7XHJcbiAgICAgIH1cclxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zdW1tYXJ5LXZhbCB7XHJcbiAgICAgICAgZm9udC1zaXplOiAxNHB4O1xyXG4gICAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7XHJcbiAgICAgICAgY29sb3I6ICMxZjI5Mzc7XHJcbiAgICAgIH1cclxuICAgIGAsXHJcbiAgXSxcclxufSlcclxuZXhwb3J0IGNsYXNzIER5bmFtaWNSZWNvcmRGb3JtQ29tcG9uZW50IGltcGxlbWVudHMgT25DaGFuZ2VzIHtcclxuICBASW5wdXQoKSBjb25maWchOiBFbnRpdHlGb3JtQ29uZmlnO1xyXG4gIEBJbnB1dCgpIHJ1bGVzPzogRm9ybVJ1bGVbXTtcclxuICBASW5wdXQoKSBpbml0aWFsRGF0YT86IFJlY29yZDxzdHJpbmcsIGFueT47XHJcbiAgQElucHV0KCkgdXNlclJvbGVzOiBzdHJpbmdbXSA9IFtdO1xyXG4gIEBJbnB1dCgpIGxhbmd1YWdlOiBzdHJpbmcgPSAnZW4nO1xyXG4gIEBJbnB1dCgpIHJlYWRvbmx5OiBib29sZWFuID0gZmFsc2U7XHJcbiAgQElucHV0KCkgbG9hZGluZzogYm9vbGVhbiA9IGZhbHNlO1xyXG4gIEBJbnB1dCgpIGVycm9yOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcclxuICAvKiogV2hvbGUgcmVjb3JkIHJlYWQtb25seSwgcmVnYXJkbGVzcyBvZiBSQkFDLiBFcXVpdmFsZW50IHRvIHRoZSByZWZlcmVuY2UncyBgaXNSZWFkT25seWAuICovXHJcbiAgQElucHV0KCkgaXNSZWFkT25seTogYm9vbGVhbiA9IGZhbHNlO1xyXG4gIC8qKiBTcGVjaWZpYyBmaWVsZCBpZHMgZm9yY2VkIHJlYWQtb25seSB3aGlsZSB0aGUgcmVzdCBvZiB0aGUgcmVjb3JkIHN0YXlzIGVkaXRhYmxlLiAqL1xyXG4gIEBJbnB1dCgpIHJlYWRPbmx5RmllbGRzOiBzdHJpbmdbXSA9IFtdO1xyXG4gIC8qKlxyXG4gICAqIE9wZW4gdGhlIHJlY29yZCByZWFkLW9ubHksIHdpdGggYSBwZXItdGFiIFwiRWRpdCBzZWN0aW9uXCIgZmxvdyDigJQgb25lIHRhYiBpcyBlZGl0ZWQgYW5kXHJcbiAgICogdmFsaWRhdGVkIGF0IGEgdGltZS4gVGhpcyBpcyB0aGUgZnJhbWV3b3JrJ3MgYEVudGl0eVJlY29yZENvbXBvbmVudGAgbW9kZWwgYW5kIHRoZVxyXG4gICAqIGRlZmF1bHQgaGVyZS5cclxuICAgKlxyXG4gICAqIFNldCBmYWxzZSBmb3IgYSBkaXJlY3RseSBlZGl0YWJsZSByZWNvcmQgd2l0aCBubyB2aWV3L2VkaXQgZGlzdGluY3Rpb24uXHJcbiAgICovXHJcbiAgQElucHV0KCkgdmlld01vZGU6IGJvb2xlYW4gPSB0cnVlO1xyXG5cclxuICBAT3V0cHV0KCkgZm9ybVN1Ym1pdCA9IG5ldyBFdmVudEVtaXR0ZXI8UmVjb3JkPHN0cmluZywgYW55Pj4oKTtcclxuICBAT3V0cHV0KCkgZm9ybUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8UmVjb3JkPHN0cmluZywgYW55Pj4oKTtcclxuICBAT3V0cHV0KCkgZm9ybVJlc2V0ID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xyXG4gIC8qKiBPbmUgdGFiIHNlY3Rpb24gd2FzIHNhdmVkLiBDYXJyaWVzIHRoZSB3aG9sZSByZWNvcmQsIHBsdXMgd2hpY2ggdGFiIHdhcyBlZGl0ZWQuICovXHJcbiAgQE91dHB1dCgpIHNlY3Rpb25TYXZlID0gbmV3IEV2ZW50RW1pdHRlcjx7IHRhYklkOiBzdHJpbmc7IHJlY29yZDogUmVjb3JkPHN0cmluZywgYW55PiB9PigpO1xyXG5cclxuICBAVmlld0NoaWxkKER5bmFtaWNGb3JtQ29tcG9uZW50KSBkeW5hbWljRm9ybUNvbXA/OiBEeW5hbWljRm9ybUNvbXBvbmVudDtcclxuXHJcbiAgcHJpdmF0ZSByZWFkb25seSBydWxlc0V2YWx1YXRpb24gPSBpbmplY3QoUnVsZXNFdmFsdWF0aW9uU2VydmljZSk7XHJcblxyXG4gIHJlYWRvbmx5IGN1cnJlbnREYXRhID0gc2lnbmFsPFJlY29yZDxzdHJpbmcsIGFueT4+KHt9KTtcclxuICByZWFkb25seSBvcmlnaW5hbEJhc2VsaW5lID0gc2lnbmFsPFJlY29yZDxzdHJpbmcsIGFueT4+KHt9KTtcclxuXHJcbiAgLyoqXHJcbiAgICogSW5mbyBiYW5uZXJzIHRoZSB1c2VyIGhhcyBkaXNtaXNzZWQsIGJ5IGZpZWxkIGlkLlxyXG4gICAqXHJcbiAgICogVGhlIHJlZmVyZW5jZSdzIGNvbnRyYWN0OiBhbiBpbmZvIGJhbm5lciBwZXJzaXN0cyB1bnRpbCBkaXNtaXNzZWQsICoqbm90KiogdW50aWwgdGhlXHJcbiAgICogdmFsdWUgY2hhbmdlcy4gRGlzbWlzc2FsIGlzIGtleWVkIGJ5IGZpZWxkIHNvIHJlLXRyaWdnZXJpbmcgYSBkaWZmZXJlbnQgZmllbGQncyBydWxlXHJcbiAgICogc3RpbGwgc2hvd3MuIFJlLWFybWVkIHdoZW4gYSBkaWZmZXJlbnQgcmVjb3JkIGlzIGxvYWRlZC5cclxuICAgKi9cclxuICBwcml2YXRlIHJlYWRvbmx5IGRpc21pc3NlZCA9IHNpZ25hbDxSZWFkb25seVNldDxzdHJpbmc+PihuZXcgU2V0PHN0cmluZz4oKSk7XHJcblxyXG4gIC8qKlxyXG4gICAqIEluZm8gYmFubmVycyBmcm9tIHRoZSBydWxlcyBlbmdpbmUgdGhhdCBhcmUgc3RpbGwgc2hvd2luZy5cclxuICAgKlxyXG4gICAqIEEgZ2V0dGVyLCBub3QgYSBgY29tcHV0ZWRgOiB0aGUgc291cmNlIGlzIGBkeW5hbWljRm9ybUNvbXBgLCBhIFZpZXdDaGlsZC4gQSBjb21wdXRlZFxyXG4gICAqIGV2YWx1YXRlZCBiZWZvcmUgdGhlIHZpZXcgaW5pdGlhbGlzZXMgd291bGQgY2FwdHVyZSBubyBkZXBlbmRlbmN5IG9uIHRoZSBjaGlsZCdzXHJcbiAgICogYHJ1bGVSZXN1bHRgIHNpZ25hbCBhbmQgdGhlbiBuZXZlciByZWNvbXB1dGUuIFJlYWQgcGVyIGNoYW5nZS1kZXRlY3Rpb24gcGFzcyBpbnN0ZWFkLlxyXG4gICAqL1xyXG4gIGdldCBpbmZvQmFubmVycygpOiB7IGZpZWxkSWQ6IHN0cmluZzsgbWVzc2FnZTogc3RyaW5nIH1bXSB7XHJcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLmR5bmFtaWNGb3JtQ29tcD8ucnVsZVJlc3VsdCgpO1xyXG4gICAgY29uc3QgZ29uZSA9IHRoaXMuZGlzbWlzc2VkKCk7XHJcbiAgICByZXR1cm4gT2JqZWN0LmVudHJpZXMocmVzdWx0Py5pbmZvQmFubmVycyA/PyB7fSlcclxuICAgICAgLmZpbHRlcigoW2ZpZWxkSWRdKSA9PiAhZ29uZS5oYXMoZmllbGRJZCkpXHJcbiAgICAgIC5tYXAoKFtmaWVsZElkLCBtZXNzYWdlXSkgPT4gKHsgZmllbGRJZCwgbWVzc2FnZTogU3RyaW5nKG1lc3NhZ2UpIH0pKTtcclxuICB9XHJcblxyXG4gIGRpc21pc3NJbmZvQmFubmVyKGZpZWxkSWQ6IHN0cmluZyk6IHZvaWQge1xyXG4gICAgdGhpcy5kaXNtaXNzZWQuc2V0KG5ldyBTZXQodGhpcy5kaXNtaXNzZWQoKSkuYWRkKGZpZWxkSWQpKTtcclxuICB9XHJcblxyXG4gIC8qKiBUcnVlIHdoZW4gbm90aGluZyBpbiB0aGUgcmVjb3JkIG1heSBiZSBlZGl0ZWQuICovXHJcbiAgZ2V0IHJlY29yZFJlYWRPbmx5KCk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIHRoaXMucmVhZG9ubHkgfHwgdGhpcy5pc1JlYWRPbmx5O1xyXG4gIH1cclxuXHJcbiAgLy8g4pSA4pSA4pSAIElubGluZSBhcnJheS1yb3cgZWRpdGluZyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcclxuXHJcbiAgLyoqXHJcbiAgICogQSByb3cgb2YgYW4gYGFycmF5YCBmaWVsZCBpcyBlZGl0ZWQgaW4gYSBkcmF3ZXIsIG5vdCBpbmxpbmUgaW4gdGhlIHRhYiBwYW5lbC5cclxuICAgKlxyXG4gICAqIFRoZSBkcmF3ZXIgaXMgcmVuZGVyZWQgb3V0c2lkZSB0aGUgdGFiIGNvbnRhaW5lciBvbiBwdXJwb3NlOiB0aGUgcmVmZXJlbmNlIGZvdW5kIHRoYXRcclxuICAgKiBidWlsZGluZyBhIHJvdydzIEZvcm1Hcm91cCBpbnNpZGUgdGhlIHRhYiB2aWV3IHJhY2VzIHRoZSB0YWIncyBvd24gaW5pdGlhbGlzYXRpb24sIHNvXHJcbiAgICogdGhlIHJvdyBmb3JtIGlzIGtlcHQgZGV0YWNoZWQgYW5kIG9ubHkgcHVzaGVkIGludG8gdGhlIEZvcm1BcnJheSBvbiBzYXZlLlxyXG4gICAqL1xyXG4gIHJlYWRvbmx5IGlubGluZVJvd0ZpZWxkID0gc2lnbmFsPE5lc3RlZEZpZWxkQ29uZmlnIHwgbnVsbD4obnVsbCk7XHJcbiAgcmVhZG9ubHkgaW5saW5lUm93SW5kZXggPSBzaWduYWw8bnVtYmVyIHwgbnVsbD4obnVsbCk7XHJcbiAgaW5saW5lUm93Rm9ybTogRm9ybUdyb3VwIHwgbnVsbCA9IG51bGw7XHJcblxyXG4gIC8qKiBgYXJyYXlgIGZpZWxkcyBvbiB0aGUgdGFiIGN1cnJlbnRseSBzaG93biwgc28gdGhlIHRlbXBsYXRlIGNhbiBsaXN0IHRoZWlyIHJvd3MuICovXHJcbiAgZ2V0IGFycmF5RmllbGRzRm9yQWN0aXZlVGFiKCk6IE5lc3RlZEZpZWxkQ29uZmlnW10ge1xyXG4gICAgY29uc3QgdGFiSWQgPSB0aGlzLmFjdGl2ZVRhYklkO1xyXG4gICAgY29uc3QgdGFiID0gdGFiSWQgPyBmaW5kVGFiKHRoaXMuY29uZmlnPy50YWJzLCB0YWJJZCkgOiBudWxsO1xyXG4gICAgcmV0dXJuICh0YWI/LmZpZWxkcyA/PyBbXSkuZmlsdGVyKGYgPT4gZi50eXBlID09PSAnYXJyYXknKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJvd3MgY3VycmVudGx5IGhlbGQgYnkgYW4gYGFycmF5YCBmaWVsZCwgcmVhZCBmcm9tIHRoZSByZWNvcmQgdmFsdWUuXHJcbiAgICpcclxuICAgKiBEZWxpYmVyYXRlbHkgbm90IHJlYWQgb2ZmIHRoZSBjaGlsZCdzIEZvcm1BcnJheTogdGhhdCBjb250cm9sIG9ubHkgZXhpc3RzIG9uY2UgdGhlXHJcbiAgICogY2hpbGQgaGFzIGluaXRpYWxpc2VkLCBzbyByZW5kZXJpbmcgZnJvbSBpdCBjaGFuZ2VkIHZhbHVlIG1pZC1wYXNzLiBgY3VycmVudERhdGFgIGlzIGFcclxuICAgKiBzaWduYWwgc2VlZGVkIGZyb20gYGluaXRpYWxEYXRhYCBhbmQga2VwdCBjdXJyZW50IGJ5IHRoZSBjaGlsZCdzIGBmb3JtQ2hhbmdlYC5cclxuICAgKi9cclxuICByb3dzT2YoZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSB7XHJcbiAgICBjb25zdCB0YWJJZCA9IHRoaXMuYWN0aXZlVGFiSWQ7XHJcbiAgICBjb25zdCB0YWJEYXRhID0gdGFiSWQgPyBnZXRUYWJEYXRhKHRhYklkLCB0aGlzLmN1cnJlbnREYXRhKCksIHRoaXMuY29uZmlnKSA6IG51bGw7XHJcbiAgICBjb25zdCByb3dzID0gKHRhYkRhdGEgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCBudWxsKT8uW2ZpZWxkLmlkXTtcclxuICAgIHJldHVybiBBcnJheS5pc0FycmF5KHJvd3MpID8gKHJvd3MgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSkgOiBbXTtcclxuICB9XHJcblxyXG4gIC8qKiBPbmUgcm93IHJlbmRlcmVkIGFzIHRleHQsIHNvIHRoZSBsaXN0IGlzIHJlYWRhYmxlIHdpdGhvdXQgb3BlbmluZyB0aGUgZHJhd2VyLiAqL1xyXG4gIHJvd1N1bW1hcnkoZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnLCByb3c6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogc3RyaW5nIHtcclxuICAgIGNvbnN0IHZhbHVlID0gcm93ID8/IHt9O1xyXG4gICAgY29uc3QgcGFydHMgPSAoZmllbGQuY2hpbGRyZW4gPz8gW10pXHJcbiAgICAgIC5tYXAoY2hpbGQgPT4gdmFsdWVbY2hpbGQuaWRdKVxyXG4gICAgICAuZmlsdGVyKHYgPT4gdiAhPT0gbnVsbCAmJiB2ICE9PSB1bmRlZmluZWQgJiYgdiAhPT0gJycpXHJcbiAgICAgIC5tYXAodiA9PiAodHlwZW9mIHYgPT09ICdvYmplY3QnID8gSlNPTi5zdHJpbmdpZnkodikgOiBTdHJpbmcodikpKTtcclxuICAgIHJldHVybiBwYXJ0cy5sZW5ndGggPyBwYXJ0cy5qb2luKCcgwrcgJykgOiAnKGVtcHR5KSc7XHJcbiAgfVxyXG5cclxuICBvcGVuQWRkUm93KGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IHZvaWQge1xyXG4gICAgaWYgKHRoaXMuc2VjdGlvblJlYWRPbmx5KSByZXR1cm47XHJcbiAgICB0aGlzLmlubGluZVJvd0ZpZWxkLnNldChmaWVsZCk7XHJcbiAgICB0aGlzLmlubGluZVJvd0luZGV4LnNldChudWxsKTtcclxuICAgIHRoaXMuaW5saW5lUm93Rm9ybSA9IHRoaXMuZHluYW1pY0Zvcm1Db21wPy5jcmVhdGVBcnJheVJvdyhmaWVsZCkgYXMgRm9ybUdyb3VwO1xyXG4gIH1cclxuXHJcbiAgb3BlbkVkaXRSb3coZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnLCBpbmRleDogbnVtYmVyKTogdm9pZCB7XHJcbiAgICBpZiAodGhpcy5zZWN0aW9uUmVhZE9ubHkpIHJldHVybjtcclxuICAgIHRoaXMuaW5saW5lUm93RmllbGQuc2V0KGZpZWxkKTtcclxuICAgIHRoaXMuaW5saW5lUm93SW5kZXguc2V0KGluZGV4KTtcclxuICAgIHRoaXMuaW5saW5lUm93Rm9ybSA9IHRoaXMuZHluYW1pY0Zvcm1Db21wPy5jcmVhdGVBcnJheVJvdyhmaWVsZCwgdGhpcy5yb3dzT2YoZmllbGQpW2luZGV4XSkgYXMgRm9ybUdyb3VwO1xyXG4gIH1cclxuXHJcbiAgY2FuY2VsUm93KCk6IHZvaWQge1xyXG4gICAgdGhpcy5pbmxpbmVSb3dGaWVsZC5zZXQobnVsbCk7XHJcbiAgICB0aGlzLmlubGluZVJvd0luZGV4LnNldChudWxsKTtcclxuICAgIHRoaXMuaW5saW5lUm93Rm9ybSA9IG51bGw7XHJcbiAgfVxyXG5cclxuICAvKiogQ29tbWl0IHRoZSBkcmF3ZXIgaW50byB0aGUgRm9ybUFycmF5LiBSdWxlcyByZS1ldmFsdWF0ZSBvZmYgdGhlIHJlc3VsdGluZyB2YWx1ZSBjaGFuZ2UuICovXHJcbiAgc2F2ZVJvdygpOiB2b2lkIHtcclxuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5pbmxpbmVSb3dGaWVsZCgpO1xyXG4gICAgY29uc3QgZm9ybSA9IHRoaXMuZHluYW1pY0Zvcm1Db21wO1xyXG4gICAgaWYgKCFmaWVsZCB8fCAhZm9ybSB8fCAhdGhpcy5pbmxpbmVSb3dGb3JtKSByZXR1cm47XHJcblxyXG4gICAgaWYgKHRoaXMuaW5saW5lUm93Rm9ybS5pbnZhbGlkKSB7XHJcbiAgICAgIHRoaXMuaW5saW5lUm93Rm9ybS5tYXJrQWxsQXNUb3VjaGVkKCk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBhcnJheSA9IGZvcm0uZ2V0QXJyYXlDb250cm9sKGZpZWxkLmlkLCB0aGlzLmFjdGl2ZVRhYklkID8/IHVuZGVmaW5lZCk7XHJcbiAgICBpZiAoIWFycmF5KSByZXR1cm47XHJcblxyXG4gICAgY29uc3QgaW5kZXggPSB0aGlzLmlubGluZVJvd0luZGV4KCk7XHJcbiAgICBpZiAoaW5kZXggPT09IG51bGwpIGFycmF5LnB1c2goZm9ybS5jcmVhdGVBcnJheVJvdyhmaWVsZCwgdGhpcy5pbmxpbmVSb3dGb3JtLnZhbHVlKSk7XHJcbiAgICBlbHNlIGFycmF5LmF0KGluZGV4KS5wYXRjaFZhbHVlKHRoaXMuaW5saW5lUm93Rm9ybS52YWx1ZSk7XHJcblxyXG4gICAgYXJyYXkudXBkYXRlVmFsdWVBbmRWYWxpZGl0eSgpO1xyXG4gICAgdGhpcy5jdXJyZW50RGF0YS5zZXQoZm9ybS5leHRyYWN0UmVjb3JkKCkpO1xyXG4gICAgdGhpcy5jYW5jZWxSb3coKTtcclxuICB9XHJcblxyXG4gIGRlbGV0ZVJvdyhmaWVsZDogTmVzdGVkRmllbGRDb25maWcsIGluZGV4OiBudW1iZXIpOiB2b2lkIHtcclxuICAgIGlmICh0aGlzLnNlY3Rpb25SZWFkT25seSkgcmV0dXJuO1xyXG4gICAgY29uc3QgYXJyYXkgPSB0aGlzLmR5bmFtaWNGb3JtQ29tcD8uZ2V0QXJyYXlDb250cm9sKGZpZWxkLmlkLCB0aGlzLmFjdGl2ZVRhYklkID8/IHVuZGVmaW5lZCk7XHJcbiAgICBhcnJheT8ucmVtb3ZlQXQoaW5kZXgpO1xyXG4gICAgYXJyYXk/LnVwZGF0ZVZhbHVlQW5kVmFsaWRpdHkoKTtcclxuICAgIGlmICh0aGlzLmR5bmFtaWNGb3JtQ29tcCkgdGhpcy5jdXJyZW50RGF0YS5zZXQodGhpcy5keW5hbWljRm9ybUNvbXAuZXh0cmFjdFJlY29yZCgpKTtcclxuICAgIGlmICh0aGlzLmlubGluZVJvd0luZGV4KCkgPT09IGluZGV4KSB0aGlzLmNhbmNlbFJvdygpO1xyXG4gIH1cclxuXHJcbiAgLy8g4pSA4pSA4pSAIFBlci10YWIgc2VjdGlvbiBlZGl0aW5nIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxyXG5cclxuICAvKipcclxuICAgKiBUaGUgdGFiIGN1cnJlbnRseSBvcGVuIGZvciBlZGl0aW5nLCBvciBudWxsIHdoZW4gdGhlIHJlY29yZCBpcyBiZWluZyB2aWV3ZWQuXHJcbiAgICpcclxuICAgKiBUaGUgcmVmZXJlbmNlIGVkaXRzIG9uZSB0YWIgc2VjdGlvbiBhdCBhIHRpbWUgcmF0aGVyIHRoYW4gdGhlIHdob2xlIHJlY29yZCwgYW5kXHJcbiAgICogdmFsaWRhdGVzIG9ubHkgdGhhdCBzZWN0aW9uIG9uIHNhdmUg4oCUIGEgcmVxdWlyZWQgZmllbGQgb24gYW4gdW50b3VjaGVkIHRhYiBtdXN0IG5vdFxyXG4gICAqIGJsb2NrIHNhdmluZyB0aGUgdGFiIHlvdSBhcmUgYWN0dWFsbHkgd29ya2luZyBvbi5cclxuICAgKi9cclxuICByZWFkb25seSBlZGl0aW5nVGFiSWQgPSBzaWduYWw8c3RyaW5nIHwgbnVsbD4obnVsbCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoZSB0YWIgdGhlIGlubmVyIGZvcm0gaXMgc2hvd2luZy5cclxuICAgKlxyXG4gICAqIFRyYWNrZWQgaGVyZSByYXRoZXIgdGhhbiByZWFkIG9mZiB0aGUgVmlld0NoaWxkLiBUaGUgY2hpbGQgcmVzb2x2ZXMgaXRzIGZpcnN0IHRhYiBhc1xyXG4gICAqIHBhcnQgb2YgaXRzIG93biBpbml0aWFsaXNhdGlvbiwgc28gcmVhZGluZyBpdCBkdXJpbmcgdGhpcyBjb21wb25lbnQncyByZW5kZXIgY2hhbmdlZFxyXG4gICAqIHZhbHVlIG1pZC1wYXNzIOKAlCBhbiBFeHByZXNzaW9uQ2hhbmdlZEFmdGVySXRIYXNCZWVuQ2hlY2tlZCBlcnJvci4gU2VlZGVkIGZyb20gdGhlXHJcbiAgICogY29uZmlnIGFuZCBrZXB0IGluIHN0ZXAgYnkgdGhlIGNoaWxkJ3MgYGFjdGl2ZVRhYkNoYW5nZWAgb3V0cHV0LlxyXG4gICAqL1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgYWN0aXZlVGFiSWRTaWcgPSBzaWduYWw8c3RyaW5nIHwgbnVsbD4obnVsbCk7XHJcblxyXG4gIGdldCBhY3RpdmVUYWJJZCgpOiBzdHJpbmcgfCBudWxsIHtcclxuICAgIHJldHVybiB0aGlzLmFjdGl2ZVRhYklkU2lnKCk7XHJcbiAgfVxyXG5cclxuICBvbkFjdGl2ZVRhYkNoYW5nZSh0YWJJZDogc3RyaW5nKTogdm9pZCB7XHJcbiAgICBpZiAodGhpcy5hY3RpdmVUYWJJZFNpZygpID09PSB0YWJJZCkgcmV0dXJuO1xyXG4gICAgdGhpcy5hY3RpdmVUYWJJZFNpZy5zZXQodGFiSWQpO1xyXG4gICAgLy8gRWRpdGluZyBkb2VzIG5vdCBmb2xsb3cgdGhlIHVzZXIgdG8gYW5vdGhlciB0YWIuXHJcbiAgICB0aGlzLmVkaXRpbmdUYWJJZC5zZXQobnVsbCk7XHJcbiAgICB0aGlzLmNhbmNlbFJvdygpO1xyXG4gIH1cclxuXHJcbiAgLyoqIEZpcnN0IHRhYiBhIHVzZXIgY2FuIHNlZSwgdXNlZCB0byBzZWVkIHRoZSBhY3RpdmUgdGFiIGJlZm9yZSB0aGUgY2hpbGQgcmVwb3J0cyBvbmUuICovXHJcbiAgcHJpdmF0ZSBmaXJzdFZpc2libGVUYWJJZCgpOiBzdHJpbmcgfCBudWxsIHtcclxuICAgIHJldHVybiB0aGlzLmNvbmZpZz8udGFicz8uZmluZCh0ID0+IHQudmlzaWJpbGl0eSAhPT0gZmFsc2UpPy5pZCA/PyBudWxsO1xyXG4gIH1cclxuXHJcbiAgLyoqIEVycm9ycyBmcm9tIHRoZSBsYXN0IHNhdmUgYXR0ZW1wdCwgYnkgZmllbGQgaWQuICovXHJcbiAgcmVhZG9ubHkgc2VjdGlvbkVycm9ycyA9IHNpZ25hbDxSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+Pih7fSk7XHJcblxyXG4gIC8qKiBUZW1wbGF0ZSBhY2Nlc3MgdG8gYE9iamVjdC5rZXlzYCBmb3IgaXRlcmF0aW5nIHRoZSBlcnJvciBtYXAuICovXHJcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IE9iamVjdCA9IE9iamVjdDtcclxuXHJcbiAgZ2V0IGlzRWRpdGluZ0FjdGl2ZVRhYigpOiBib29sZWFuIHtcclxuICAgIC8vIFdpdGhvdXQgdmlldyBtb2RlIHRoZXJlIGlzIG5vIHZpZXcvZWRpdCBkaXN0aW5jdGlvbiDigJQgZXZlcnl0aGluZyBpcyBhbHdheXMgZWRpdGFibGUuXHJcbiAgICBpZiAoIXRoaXMudmlld01vZGUpIHJldHVybiB0cnVlO1xyXG4gICAgcmV0dXJuICEhdGhpcy5hY3RpdmVUYWJJZCAmJiB0aGlzLmVkaXRpbmdUYWJJZCgpID09PSB0aGlzLmFjdGl2ZVRhYklkO1xyXG4gIH1cclxuXHJcbiAgLyoqIEZpZWxkcyByZW5kZXIgcmVhZC1vbmx5IHVubGVzcyB0aGVpciBvd24gdGFiIGlzIHRoZSBvbmUgYmVpbmcgZWRpdGVkLiAqL1xyXG4gIGdldCBzZWN0aW9uUmVhZE9ubHkoKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gdGhpcy5yZWNvcmRSZWFkT25seSB8fCAhdGhpcy5pc0VkaXRpbmdBY3RpdmVUYWI7XHJcbiAgfVxyXG5cclxuICBlZGl0U2VjdGlvbigpOiB2b2lkIHtcclxuICAgIGlmICghdGhpcy52aWV3TW9kZSB8fCB0aGlzLnJlY29yZFJlYWRPbmx5IHx8ICF0aGlzLmFjdGl2ZVRhYklkKSByZXR1cm47XHJcbiAgICB0aGlzLnNlY3Rpb25FcnJvcnMuc2V0KHt9KTtcclxuICAgIHRoaXMuZWRpdGluZ1RhYklkLnNldCh0aGlzLmFjdGl2ZVRhYklkKTtcclxuICB9XHJcblxyXG4gIC8qKiBEaXNjYXJkIHRoaXMgc2VjdGlvbidzIGVkaXRzLCByZXN0b3JpbmcgZWFjaCBmaWVsZCBmcm9tIHRoZSBzZXNzaW9uIGJhc2VsaW5lLiAqL1xyXG4gIGNhbmNlbFNlY3Rpb24oKTogdm9pZCB7XHJcbiAgICBjb25zdCB0YWJJZCA9IHRoaXMuZWRpdGluZ1RhYklkKCk7XHJcbiAgICBjb25zdCBmb3JtID0gdGhpcy5keW5hbWljRm9ybUNvbXA7XHJcbiAgICBpZiAodGFiSWQgJiYgZm9ybSkge1xyXG4gICAgICBjb25zdCB0YWIgPSBmaW5kVGFiKHRoaXMuY29uZmlnPy50YWJzLCB0YWJJZCk7XHJcbiAgICAgIGNvbnN0IGJhc2VsaW5lVGFiID0gKGdldFRhYkRhdGEodGFiSWQsIHRoaXMub3JpZ2luYWxCYXNlbGluZSgpLCB0aGlzLmNvbmZpZykgPz8ge30pIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xyXG4gICAgICBmb3IgKGNvbnN0IGZpZWxkIG9mIHRhYj8uZmllbGRzID8/IFtdKSB7XHJcbiAgICAgICAgY29uc3QgYmFzZWxpbmVWYWx1ZSA9IGJhc2VsaW5lVGFiW2ZpZWxkLmlkXTtcclxuXHJcbiAgICAgICAgLy8gQSBGb3JtQXJyYXkgY2Fubm90IGJlIHJlc3RvcmVkIHdpdGggc2V0VmFsdWUg4oCUIHRoYXQgZGVtYW5kcyBhIHZhbHVlIHNoYXBlZCBleGFjdGx5XHJcbiAgICAgICAgLy8gbGlrZSB0aGUgY3VycmVudCByb3dzLiBSZWJ1aWxkIGl0IGZyb20gdGhlIGJhc2VsaW5lIHJvd3MgaW5zdGVhZC5cclxuICAgICAgICBjb25zdCBhcnJheSA9IGZvcm0uZ2V0QXJyYXlDb250cm9sKGZpZWxkLmlkLCB0YWJJZCk7XHJcbiAgICAgICAgaWYgKGFycmF5KSB7XHJcbiAgICAgICAgICBhcnJheS5jbGVhcigpO1xyXG4gICAgICAgICAgZm9yIChjb25zdCByb3cgb2YgQXJyYXkuaXNBcnJheShiYXNlbGluZVZhbHVlKSA/IGJhc2VsaW5lVmFsdWUgOiBbXSkge1xyXG4gICAgICAgICAgICBhcnJheS5wdXNoKGZvcm0uY3JlYXRlQXJyYXlSb3coZmllbGQsIHJvdykpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgYXJyYXkudXBkYXRlVmFsdWVBbmRWYWxpZGl0eSgpO1xyXG4gICAgICAgICAgY29udGludWU7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBmb3JtLmdldENvbnRyb2woZmllbGQuaWQsIHRhYklkKT8uc2V0VmFsdWUoYmFzZWxpbmVWYWx1ZSA/PyBudWxsKTtcclxuICAgICAgfVxyXG4gICAgICB0aGlzLmN1cnJlbnREYXRhLnNldChmb3JtLmV4dHJhY3RSZWNvcmQoKSk7XHJcbiAgICB9XHJcbiAgICB0aGlzLnNlY3Rpb25FcnJvcnMuc2V0KHt9KTtcclxuICAgIHRoaXMuZWRpdGluZ1RhYklkLnNldChudWxsKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFZhbGlkYXRlIHRoaXMgc2VjdGlvbiBvbmx5LCB0aGVuIGVtaXQgaXQuIFZhbGlkYXRpb24gaXMgc2NvcGVkIHR3byB3YXlzOiBBbmd1bGFyXHJcbiAgICogdmFsaWRpdHkgb2YgdGhlIHRhYidzIG93biBjb250cm9scywgYW5kIHJ1bGVzIGZpbHRlcmVkIHRvIHRoaXMgdGFiIOKAlCBhIHJ1bGUgdGFyZ2V0aW5nXHJcbiAgICogYSBoaWRkZW4gdGFiIG11c3Qgbm90IGJsb2NrIHRoZSBzYXZlICh0aGUgcmVmZXJlbmNlJ3MgT1YwLTk2OCBmaXgpLlxyXG4gICAqL1xyXG4gIHNhdmVTZWN0aW9uKCk6IHZvaWQge1xyXG4gICAgY29uc3QgdGFiSWQgPSB0aGlzLmVkaXRpbmdUYWJJZCgpO1xyXG4gICAgY29uc3QgZm9ybSA9IHRoaXMuZHluYW1pY0Zvcm1Db21wO1xyXG4gICAgaWYgKCF0YWJJZCB8fCAhZm9ybSkgcmV0dXJuO1xyXG5cclxuICAgIGNvbnN0IHRhYiA9IGZpbmRUYWIodGhpcy5jb25maWc/LnRhYnMsIHRhYklkKTtcclxuICAgIGNvbnN0IGVycm9yczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xyXG5cclxuICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGFiPy5maWVsZHMgPz8gW10pIHtcclxuICAgICAgY29uc3QgY3RybCA9IGZvcm0uZ2V0Q29udHJvbChmaWVsZC5pZCwgdGFiSWQpO1xyXG4gICAgICBpZiAoY3RybD8uaW52YWxpZCkge1xyXG4gICAgICAgIGN0cmwubWFya0FzVG91Y2hlZCgpO1xyXG4gICAgICAgIGVycm9yc1tmaWVsZC5pZF0gPSBgJHtyZXNvbHZlTGFiZWwoZmllbGQubGFiZWwsIHRoaXMubGFuZ3VhZ2UpfSBpcyBpbnZhbGlkLmA7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBzY29wZWQgPSB0aGlzLnJ1bGVzRXZhbHVhdGlvbi5maWx0ZXJGb3JUYWIodGhpcy5ydWxlcywgdGFiSWQsIHRoaXMuY29uZmlnKTtcclxuICAgIGNvbnN0IHJ1bGVFcnJvcnMgPSB0aGlzLnJ1bGVzRXZhbHVhdGlvbi5ldmFsdWF0ZShzY29wZWQsIGZvcm0uZm9ybVZhbHVlcygpLCB0aGlzLm9yaWdpbmFsQmFzZWxpbmUoKSlcclxuICAgICAgLnZhbGlkYXRpb25FcnJvcnM7XHJcbiAgICBPYmplY3QuYXNzaWduKGVycm9ycywgcnVsZUVycm9ycyk7XHJcblxyXG4gICAgaWYgKE9iamVjdC5rZXlzKGVycm9ycykubGVuZ3RoID4gMCkge1xyXG4gICAgICB0aGlzLnNlY3Rpb25FcnJvcnMuc2V0KGVycm9ycyk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLnNlY3Rpb25FcnJvcnMuc2V0KHt9KTtcclxuICAgIHRoaXMuZWRpdGluZ1RhYklkLnNldChudWxsKTtcclxuXHJcbiAgICBjb25zdCByZWNvcmQgPSBmb3JtLmV4dHJhY3RSZWNvcmQoKTtcclxuICAgIHRoaXMuY3VycmVudERhdGEuc2V0KHJlY29yZCk7XHJcbiAgICB0aGlzLnNlY3Rpb25TYXZlLmVtaXQoeyB0YWJJZCwgcmVjb3JkIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2VlZCB0aGUgc2Vzc2lvbiBiYXNlbGluZSBmcm9tIHRoZSBsb2FkZWQgcmVjb3JkIHNvIGBWQUxVRV9DSEFOR0VEYCBydWxlcyBhbmQgdGhlXHJcbiAgICogbW9kaWZpY2F0aW9uIGJhbm5lciBjb21wYXJlIGFnYWluc3QgdGhlIHJlY29yZCBhcyBpdCB3YXMgb3BlbmVkIOKAlCBub3QgYXMgaXQgd2FzXHJcbiAgICogYWZ0ZXIgdGhlIGZpcnN0IGtleXN0cm9rZS5cclxuICAgKi9cclxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XHJcbiAgICBpZiAoY2hhbmdlc1snaW5pdGlhbERhdGEnXSB8fCBjaGFuZ2VzWydjb25maWcnXSkge1xyXG4gICAgICB0aGlzLm9yaWdpbmFsQmFzZWxpbmUuc2V0KHsgLi4uKHRoaXMuaW5pdGlhbERhdGEgPz8ge30pIH0pO1xyXG4gICAgICB0aGlzLmN1cnJlbnREYXRhLnNldCh7IC4uLih0aGlzLmluaXRpYWxEYXRhID8/IHt9KSB9KTtcclxuICAgICAgdGhpcy5hY3RpdmVUYWJJZFNpZy5zZXQodGhpcy5maXJzdFZpc2libGVUYWJJZCgpKTtcclxuICAgICAgLy8gQSBkaWZmZXJlbnQgcmVjb3JkIGlzIGEgZGlmZmVyZW50IHNldCBvZiBiYW5uZXJzIOKAlCByZS1hcm0gdGhlbS5cclxuICAgICAgdGhpcy5kaXNtaXNzZWQuc2V0KG5ldyBTZXQ8c3RyaW5nPigpKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIGdldCByZWNvcmRUaXRsZSgpOiBzdHJpbmcge1xyXG4gICAgaWYgKCF0aGlzLmNvbmZpZykgcmV0dXJuICdSZWNvcmQnO1xyXG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLm5hbWUgPyByZXNvbHZlTGFiZWwodGhpcy5jb25maWcubmFtZSwgdGhpcy5sYW5ndWFnZSkgOiB0aGlzLmNvbmZpZy5lbnRpdHk7XHJcbiAgfVxyXG5cclxuICBnZXQgYXZhdGFyTGV0dGVyKCk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gKHRoaXMucmVjb3JkVGl0bGUgfHwgJ1InKS5jaGFyQXQoMCkudG9VcHBlckNhc2UoKTtcclxuICB9XHJcblxyXG4gIC8vIOKUgOKUgOKUgCBIZWFkZXIgZmllbGRzIChpc1Byb2ZpbGVJbWFnZSAvIGlzSGVhZGVyVG9nZ2xlKSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcclxuXHJcbiAgLyoqIEV2ZXJ5IGZpZWxkIGluIHRoZSBjb25maWcsIGF0IGFueSBkZXB0aC4gKi9cclxuICBwcml2YXRlIGFsbEZpZWxkcygpOiBOZXN0ZWRGaWVsZENvbmZpZ1tdIHtcclxuICAgIGNvbnN0IG91dDogTmVzdGVkRmllbGRDb25maWdbXSA9IFtdO1xyXG4gICAgY29uc3Qgd2Fsa0ZpZWxkcyA9IChmaWVsZHM6IE5lc3RlZEZpZWxkQ29uZmlnW10gfCB1bmRlZmluZWQpID0+IHtcclxuICAgICAgZm9yIChjb25zdCBmIG9mIGZpZWxkcyA/PyBbXSkge1xyXG4gICAgICAgIG91dC5wdXNoKGYpO1xyXG4gICAgICAgIHdhbGtGaWVsZHMoZi5jaGlsZHJlbik7XHJcbiAgICAgIH1cclxuICAgIH07XHJcbiAgICBjb25zdCB3YWxrVGFicyA9ICh0YWJzOiBOZXN0ZWRUYWJDb25maWdbXSB8IHVuZGVmaW5lZCkgPT4ge1xyXG4gICAgICBmb3IgKGNvbnN0IHQgb2YgdGFicyA/PyBbXSkge1xyXG4gICAgICAgIHdhbGtGaWVsZHModC5maWVsZHMpO1xyXG4gICAgICAgIHdhbGtUYWJzKHQuY2hpbGRyZW4pO1xyXG4gICAgICB9XHJcbiAgICB9O1xyXG4gICAgd2Fsa1RhYnModGhpcy5jb25maWc/LnRhYnMpO1xyXG4gICAgcmV0dXJuIG91dDtcclxuICB9XHJcblxyXG4gIC8qKiBUaGUgZmllbGQgZmxhZ2dlZCBgaXNQcm9maWxlSW1hZ2VgLCByZW5kZXJlZCBhcyB0aGUgcmVjb3JkIGF2YXRhci4gKi9cclxuICBnZXQgaGVhZGVyUHJvZmlsZUZpZWxkKCk6IE5lc3RlZEZpZWxkQ29uZmlnIHwgbnVsbCB7XHJcbiAgICByZXR1cm4gdGhpcy5hbGxGaWVsZHMoKS5maW5kKGYgPT4gZi5pc1Byb2ZpbGVJbWFnZSkgPz8gbnVsbDtcclxuICB9XHJcblxyXG4gIC8qKiBUaGUgZmllbGQgZmxhZ2dlZCBgaXNIZWFkZXJUb2dnbGVgLCByZW5kZXJlZCBhcyBhIHN0YXR1cyBzd2l0Y2ggaW4gdGhlIGhlYWRlci4gKi9cclxuICBnZXQgaGVhZGVyVG9nZ2xlRmllbGQoKTogTmVzdGVkRmllbGRDb25maWcgfCBudWxsIHtcclxuICAgIHJldHVybiB0aGlzLmFsbEZpZWxkcygpLmZpbmQoZiA9PiBmLmlzSGVhZGVyVG9nZ2xlKSA/PyBudWxsO1xyXG4gIH1cclxuXHJcbiAgLyoqIEF2YXRhciBpbWFnZSBVUkwsIHdoZW4gdGhlIHByb2ZpbGUgZmllbGQgaG9sZHMgYSBwZXJzaXN0ZWQgRmlsZVJlZi4gKi9cclxuICBnZXQgcHJvZmlsZUltYWdlVXJsKCk6IHN0cmluZyB8IG51bGwge1xyXG4gICAgY29uc3QgZmllbGQgPSB0aGlzLmhlYWRlclByb2ZpbGVGaWVsZDtcclxuICAgIGlmICghZmllbGQpIHJldHVybiBudWxsO1xyXG4gICAgY29uc3QgdmFsdWUgPSB0aGlzLmZpZWxkVmFsdWUoZmllbGQuaWQpIGFzIHsgdXJsPzogc3RyaW5nIH0gfCBudWxsO1xyXG4gICAgcmV0dXJuIHZhbHVlPy51cmwgPz8gbnVsbDtcclxuICB9XHJcblxyXG4gIGdldCBoZWFkZXJUb2dnbGVWYWx1ZSgpOiBib29sZWFuIHtcclxuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5oZWFkZXJUb2dnbGVGaWVsZDtcclxuICAgIHJldHVybiBmaWVsZCA/IHRoaXMuZmllbGRWYWx1ZShmaWVsZC5pZCkgPT09IHRydWUgOiBmYWxzZTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEZsaXAgdGhlIGhlYWRlciB0b2dnbGUuIEl0IHdyaXRlcyBzdHJhaWdodCB0byB0aGUgY29udHJvbCByYXRoZXIgdGhhbiBnb2luZyB0aHJvdWdoXHJcbiAgICogc2VjdGlvbiBlZGl0aW5nIOKAlCB0aGUgcmVmZXJlbmNlIHRyZWF0cyBpdCBhcyBhIHJlY29yZC1sZXZlbCBzdGF0dXMgc3dpdGNoLCBub3QgcGFydCBvZlxyXG4gICAqIGFueSBvbmUgdGFiJ3Mgc2VjdGlvbi5cclxuICAgKi9cclxuICB0b2dnbGVIZWFkZXJTdGF0dXMoKTogdm9pZCB7XHJcbiAgICBjb25zdCBmaWVsZCA9IHRoaXMuaGVhZGVyVG9nZ2xlRmllbGQ7XHJcbiAgICBpZiAoIWZpZWxkIHx8IHRoaXMucmVjb3JkUmVhZE9ubHkpIHJldHVybjtcclxuICAgIGNvbnN0IGN0cmwgPSB0aGlzLmNvbnRyb2xGb3IoZmllbGQuaWQpO1xyXG4gICAgaWYgKCFjdHJsKSByZXR1cm47XHJcbiAgICBjdHJsLnNldFZhbHVlKCF0aGlzLmhlYWRlclRvZ2dsZVZhbHVlKTtcclxuICAgIGlmICh0aGlzLmR5bmFtaWNGb3JtQ29tcCkgdGhpcy5jdXJyZW50RGF0YS5zZXQodGhpcy5keW5hbWljRm9ybUNvbXAuZXh0cmFjdFJlY29yZCgpKTtcclxuICB9XHJcblxyXG4gIC8qKiBMb2NhdGUgYSBmaWVsZCdzIGNvbnRyb2wgd2hlcmV2ZXIgaXQgbGl2ZXMsIHNpbmNlIGhlYWRlciBmaWVsZHMgbWF5IGJlIG9uIGFueSB0YWIuICovXHJcbiAgcHJpdmF0ZSBjb250cm9sRm9yKGZpZWxkSWQ6IHN0cmluZyk6IEFic3RyYWN0Q29udHJvbCB8IG51bGwge1xyXG4gICAgcmV0dXJuIHRoaXMuZHluYW1pY0Zvcm1Db21wPy5nZXRDb250cm9sKGZpZWxkSWQpID8/IG51bGw7XHJcbiAgfVxyXG5cclxuICAvKiogQSBmaWVsZCdzIGN1cnJlbnQgdmFsdWUsIHJlYWQgZnJvbSB0aGUgcmVjb3JkIHNvIGl0IGlzIHN0YWJsZSBkdXJpbmcgcmVuZGVyLiAqL1xyXG4gIHByaXZhdGUgZmllbGRWYWx1ZShmaWVsZElkOiBzdHJpbmcpOiB1bmtub3duIHtcclxuICAgIGZvciAoY29uc3QgdGFiIG9mIHRoaXMuY29uZmlnPy50YWJzID8/IFtdKSB7XHJcbiAgICAgIGNvbnN0IGRhdGEgPSBnZXRUYWJEYXRhKHRhYi5pZCwgdGhpcy5jdXJyZW50RGF0YSgpLCB0aGlzLmNvbmZpZykgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCBudWxsO1xyXG4gICAgICBpZiAoZGF0YSAmJiBmaWVsZElkIGluIGRhdGEpIHJldHVybiBkYXRhW2ZpZWxkSWRdO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuICB9XHJcblxyXG4gIHJlYWRvbmx5IGlzTW9kaWZpZWQgPSBjb21wdXRlZCgoKSA9PiB7XHJcbiAgICBjb25zdCBvcmlnID0gdGhpcy5vcmlnaW5hbEJhc2VsaW5lKCk7XHJcbiAgICBjb25zdCBjdXJyID0gdGhpcy5jdXJyZW50RGF0YSgpO1xyXG4gICAgaWYgKCFvcmlnIHx8ICFjdXJyIHx8IE9iamVjdC5rZXlzKG9yaWcpLmxlbmd0aCA9PT0gMCkgcmV0dXJuIGZhbHNlO1xyXG4gICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KG9yaWcpICE9PSBKU09OLnN0cmluZ2lmeShjdXJyKTtcclxuICB9KTtcclxuXHJcbiAgcmVhZG9ubHkgc3VtbWFyeUZpZWxkcyA9IGNvbXB1dGVkPE5lc3RlZEZpZWxkQ29uZmlnW10+KCgpID0+IHtcclxuICAgIGNvbnN0IGZpZWxkczogTmVzdGVkRmllbGRDb25maWdbXSA9IFtdO1xyXG4gICAgY29uc3Qgd2Fsa1RhYnMgPSAodGFiczogTmVzdGVkVGFiQ29uZmlnW10gPSBbXSkgPT4ge1xyXG4gICAgICBmb3IgKGNvbnN0IHQgb2YgdGFicykge1xyXG4gICAgICAgIGZvciAoY29uc3QgZiBvZiB0LmZpZWxkcyA/PyBbXSkge1xyXG4gICAgICAgICAgaWYgKGYuc2hvd09uTWluaW1pemUpIGZpZWxkcy5wdXNoKGYpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAodC5jaGlsZHJlbikgd2Fsa1RhYnModC5jaGlsZHJlbik7XHJcbiAgICAgIH1cclxuICAgIH07XHJcbiAgICB3YWxrVGFicyh0aGlzLmNvbmZpZz8udGFicyk7XHJcbiAgICByZXR1cm4gZmllbGRzO1xyXG4gIH0pO1xyXG5cclxuICBvbkZvcm1DaGFuZ2UoZGF0YTogUmVjb3JkPHN0cmluZywgYW55Pik6IHZvaWQge1xyXG4gICAgLy8gQmFzZWxpbmUgaXMgc2VlZGVkIGZyb20gYGluaXRpYWxEYXRhYCBpbiBuZ09uQ2hhbmdlczsgb25seSBmaWxsIGl0IGhlcmUgd2hlbiB0aGVcclxuICAgIC8vIHJlY29yZCB3YXMgb3BlbmVkIGVtcHR5IChjcmVhdGUgZmxvdyksIHNvIHRoZSBmaXJzdCBlbWl0dGVkIHNoYXBlIGJlY29tZXMgdGhlIGJhc2VsaW5lLlxyXG4gICAgaWYgKE9iamVjdC5rZXlzKHRoaXMub3JpZ2luYWxCYXNlbGluZSgpKS5sZW5ndGggPT09IDApIHtcclxuICAgICAgdGhpcy5vcmlnaW5hbEJhc2VsaW5lLnNldCh7IC4uLmRhdGEgfSk7XHJcbiAgICB9XHJcbiAgICB0aGlzLmN1cnJlbnREYXRhLnNldChkYXRhKTtcclxuICAgIHRoaXMuZm9ybUNoYW5nZS5lbWl0KGRhdGEpO1xyXG4gIH1cclxuXHJcbiAgb25Gb3JtU3VibWl0KGRhdGE6IFJlY29yZDxzdHJpbmcsIGFueT4pOiB2b2lkIHtcclxuICAgIHRoaXMuZm9ybVN1Ym1pdC5lbWl0KGRhdGEpO1xyXG4gIH1cclxuXHJcbiAgb25Gb3JtUmVzZXQoKTogdm9pZCB7XHJcbiAgICB0aGlzLmZvcm1SZXNldC5lbWl0KCk7XHJcbiAgfVxyXG5cclxuICBmb3JtYXRGaWVsZExhYmVsKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gcmVzb2x2ZUxhYmVsKGZpZWxkLmxhYmVsLCB0aGlzLmxhbmd1YWdlKTtcclxuICB9XHJcblxyXG4gIC8qKiBTdW1tYXJ5IHZhbHVlcyByZW5kZXIgdGhyb3VnaCB0aGUgc2hhcmVkIGNvcmUgZm9ybWF0dGVyLCBub3QgYSBsb2NhbCBzdHJpbmdpZmllci4gKi9cclxuICBmb3JtYXRGaWVsZFZhbHVlKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gZm9ybWF0RGlzcGxheVZhbHVlKGZpZWxkLnR5cGUsIGZpZWxkLm9wdGlvbnMsIHRoaXMuY3VycmVudERhdGEoKVtmaWVsZC5pZF0sIHRoaXMubGFuZ3VhZ2UpO1xyXG4gIH1cclxuXHJcbiAganVtcFRvRmllbGQoZmllbGRJZDogc3RyaW5nKTogdm9pZCB7XHJcbiAgICAvLyBGaW5kIHBhcmVudCB0YWIgZm9yIGZpZWxkXHJcbiAgICBmb3IgKGNvbnN0IHRhYiBvZiB0aGlzLmNvbmZpZz8udGFicyB8fCBbXSkge1xyXG4gICAgICBjb25zdCBoYXNGaWVsZCA9ICh0YWIuZmllbGRzIHx8IFtdKS5zb21lKGYgPT4gZi5pZCA9PT0gZmllbGRJZCk7XHJcbiAgICAgIGlmIChoYXNGaWVsZCkge1xyXG4gICAgICAgIHRoaXMuZHluYW1pY0Zvcm1Db21wPy5zZXRBY3RpdmVUYWIodGFiLmlkKTtcclxuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgIGNvbnN0IGVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoYGZpZWxkLWNvbnRhaW5lci0ke2ZpZWxkSWR9YCk7XHJcbiAgICAgICAgICBpZiAoZWwpIHtcclxuICAgICAgICAgICAgZWwuc2Nyb2xsSW50b1ZpZXcoeyBiZWhhdmlvcjogJ3Ntb290aCcsIGJsb2NrOiAnY2VudGVyJyB9KTtcclxuICAgICAgICAgICAgZWwuZm9jdXMoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9LCA1MCk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcbn1cclxuIiwiPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yXCI+XHJcbiAgPCEtLSBSZWNvcmQgSGVhZGVyIC0tPlxyXG4gIDxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9faGVhZGVyXCI+XHJcbiAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3RpdGxlLWdyb3VwXCI+XHJcbiAgICAgIEBpZiAocHJvZmlsZUltYWdlVXJsKSB7XHJcbiAgICAgICAgPGltZ1xyXG4gICAgICAgICAgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fYXZhdGFyXCJcclxuICAgICAgICAgIGRhdGEtdGVzdGlkPVwiaGVhZGVyLXByb2ZpbGUtaW1hZ2VcIlxyXG4gICAgICAgICAgW3NyY109XCJwcm9maWxlSW1hZ2VVcmxcIlxyXG4gICAgICAgICAgW2FsdF09XCJyZWNvcmRUaXRsZVwiXHJcbiAgICAgICAgLz5cclxuICAgICAgfSBAZWxzZSB7XHJcbiAgICAgICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19hdmF0YXJcIiBkYXRhLXRlc3RpZD1cImhlYWRlci1hdmF0YXItaW5pdGlhbFwiPlxyXG4gICAgICAgICAge3sgYXZhdGFyTGV0dGVyIH19XHJcbiAgICAgICAgPC9kaXY+XHJcbiAgICAgIH1cclxuICAgICAgPGRpdj5cclxuICAgICAgICA8aDIgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fdGl0bGVcIiBkYXRhLXRlc3RpZD1cInJlY29yZC10aXRsZVwiPlxyXG4gICAgICAgICAge3sgcmVjb3JkVGl0bGUgfX1cclxuICAgICAgICA8L2gyPlxyXG4gICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3N1YnRpdGxlXCI+RW50aXR5OiB7eyBjb25maWcuZW50aXR5IH19PC9zcGFuPlxyXG4gICAgICA8L2Rpdj5cclxuICAgIDwvZGl2PlxyXG5cclxuICAgIDwhLS0gUmVjb3JkLWxldmVsIHN0YXR1cyBzd2l0Y2gsIG91dHNpZGUgc2VjdGlvbiBlZGl0aW5nIC0tPlxyXG4gICAgQGlmIChoZWFkZXJUb2dnbGVGaWVsZDsgYXMgdG9nZ2xlRmllbGQpIHtcclxuICAgICAgPGxhYmVsIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2hlYWRlci10b2dnbGVcIj5cclxuICAgICAgICA8c3Bhbj57eyBmb3JtYXRGaWVsZExhYmVsKHRvZ2dsZUZpZWxkKSB9fTwvc3Bhbj5cclxuICAgICAgICA8aW5wdXRcclxuICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXHJcbiAgICAgICAgICByb2xlPVwic3dpdGNoXCJcclxuICAgICAgICAgIGRhdGEtdGVzdGlkPVwiaGVhZGVyLXRvZ2dsZVwiXHJcbiAgICAgICAgICBbY2hlY2tlZF09XCJoZWFkZXJUb2dnbGVWYWx1ZVwiXHJcbiAgICAgICAgICBbZGlzYWJsZWRdPVwicmVjb3JkUmVhZE9ubHlcIlxyXG4gICAgICAgICAgW2F0dHIuYXJpYS1jaGVja2VkXT1cImhlYWRlclRvZ2dsZVZhbHVlXCJcclxuICAgICAgICAgIChjaGFuZ2UpPVwidG9nZ2xlSGVhZGVyU3RhdHVzKClcIlxyXG4gICAgICAgIC8+XHJcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9faGVhZGVyLXRvZ2dsZS10ZXh0XCI+e3sgaGVhZGVyVG9nZ2xlVmFsdWUgPyAnWWVzJyA6ICdObycgfX08L3NwYW4+XHJcbiAgICAgIDwvbGFiZWw+XHJcbiAgICB9XHJcbiAgPC9kaXY+XHJcblxyXG4gIDwhLS0gRGlzbWlzc2libGUgaW5mbyBiYW5uZXJzIChydWxlcyBgaW5mb2AgYWN0aW9uKSAtLT5cclxuICBAZm9yIChiYW5uZXIgb2YgaW5mb0Jhbm5lcnM7IHRyYWNrIGJhbm5lci5maWVsZElkKSB7XHJcbiAgICA8ZGl2XHJcbiAgICAgIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lciBuZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLS1pbmZvXCJcclxuICAgICAgcm9sZT1cInN0YXR1c1wiXHJcbiAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidpbmZvLWJhbm5lci0nICsgYmFubmVyLmZpZWxkSWRcIlxyXG4gICAgPlxyXG4gICAgICA8c3Bhbj7ihLnvuI8ge3sgYmFubmVyLm1lc3NhZ2UgfX08L3NwYW4+XHJcbiAgICAgIDxidXR0b25cclxuICAgICAgICB0eXBlPVwiYnV0dG9uXCJcclxuICAgICAgICBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXItZGlzbWlzc1wiXHJcbiAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2Rpc21pc3MtJyArIGJhbm5lci5maWVsZElkXCJcclxuICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidEaXNtaXNzOiAnICsgYmFubmVyLm1lc3NhZ2VcIlxyXG4gICAgICAgIChjbGljayk9XCJkaXNtaXNzSW5mb0Jhbm5lcihiYW5uZXIuZmllbGRJZClcIlxyXG4gICAgICA+4pyVPC9idXR0b24+XHJcbiAgICA8L2Rpdj5cclxuICB9XHJcblxyXG4gIDwhLS0gTW9kaWZpY2F0aW9uIGJhc2VsaW5lIGJhbm5lciAtLT5cclxuICBAaWYgKGlzTW9kaWZpZWQoKSkge1xyXG4gICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXIgbmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lci0td2FybmluZ1wiPlxyXG4gICAgICDwn5SSIFJlY29yZCBtb2RpZmllZCBmcm9tIHNlc3Npb24gYmFzZWxpbmUg4oCUIHVuc2F2ZWQgY2hhbmdlcyBwZW5kaW5nLlxyXG4gICAgPC9kaXY+XHJcbiAgfVxyXG5cclxuICA8IS0tIFN1bW1hcnkgUGFuZWwgKHNob3dPbk1pbmltaXplIGZpZWxkcyB3aXRoIGludGVyYWN0aXZlIGp1bXApIC0tPlxyXG4gIEBpZiAoc3VtbWFyeUZpZWxkcygpLmxlbmd0aCA+IDApIHtcclxuICAgIDxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fc3VtbWFyeS1wYW5lbFwiIGRhdGEtdGVzdGlkPVwic3VtbWFyeS1wYW5lbFwiPlxyXG4gICAgICBAZm9yIChzZiBvZiBzdW1tYXJ5RmllbGRzKCk7IHRyYWNrIHNmLmlkKSB7XHJcbiAgICAgICAgPGRpdlxyXG4gICAgICAgICAgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fc3VtbWFyeS1pdGVtXCJcclxuICAgICAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidzdW1tYXJ5LScgKyBzZi5pZFwiXHJcbiAgICAgICAgICAoY2xpY2spPVwianVtcFRvRmllbGQoc2YuaWQpXCJcclxuICAgICAgICAgIFt0aXRsZV09XCInQ2xpY2sgdG8ganVtcCB0byAnICsgZm9ybWF0RmllbGRMYWJlbChzZilcIlxyXG4gICAgICAgID5cclxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3N1bW1hcnktbGFiZWxcIj57eyBmb3JtYXRGaWVsZExhYmVsKHNmKSB9fTwvc3Bhbj5cclxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3N1bW1hcnktdmFsXCI+e3sgZm9ybWF0RmllbGRWYWx1ZShzZikgfX08L3NwYW4+XHJcbiAgICAgICAgPC9kaXY+XHJcbiAgICAgIH1cclxuICAgIDwvZGl2PlxyXG4gIH1cclxuXHJcbiAgPCEtLSBTZWN0aW9uIGVkaXQgYmFyOiBvbmUgdGFiIGlzIGVkaXRlZCBhdCBhIHRpbWUsIGFuZCB2YWxpZGF0ZWQgb24gaXRzIG93biAtLT5cclxuICBAaWYgKHZpZXdNb2RlICYmICFyZWNvcmRSZWFkT25seSkge1xyXG4gICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19zZWN0aW9uLWJhclwiPlxyXG4gICAgICBAaWYgKGlzRWRpdGluZ0FjdGl2ZVRhYikge1xyXG4gICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3NlY3Rpb24taGludFwiPkVkaXRpbmcgdGhpcyBzZWN0aW9uPC9zcGFuPlxyXG4gICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwibmd4LWZvcm1fX3Jlc2V0XCIgZGF0YS10ZXN0aWQ9XCJjYW5jZWwtc2VjdGlvblwiIChjbGljayk9XCJjYW5jZWxTZWN0aW9uKClcIj5cclxuICAgICAgICAgIENhbmNlbFxyXG4gICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwibmd4LWZvcm1fX3N1Ym1pdFwiIGRhdGEtdGVzdGlkPVwic2F2ZS1zZWN0aW9uXCIgKGNsaWNrKT1cInNhdmVTZWN0aW9uKClcIj5cclxuICAgICAgICAgIFNhdmUgc2VjdGlvblxyXG4gICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICB9IEBlbHNlIHtcclxuICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cIm5neC1mb3JtX19yZXNldFwiIGRhdGEtdGVzdGlkPVwiZWRpdC1zZWN0aW9uXCIgKGNsaWNrKT1cImVkaXRTZWN0aW9uKClcIj5cclxuICAgICAgICAgIEVkaXQgc2VjdGlvblxyXG4gICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICB9XHJcbiAgICA8L2Rpdj5cclxuICB9XHJcblxyXG4gIEBpZiAoT2JqZWN0LmtleXMoc2VjdGlvbkVycm9ycygpKS5sZW5ndGggPiAwKSB7XHJcbiAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lciBuZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLS1lcnJvclwiIHJvbGU9XCJhbGVydFwiIGRhdGEtdGVzdGlkPVwic2VjdGlvbi1lcnJvcnNcIj5cclxuICAgICAgQGZvciAoa2V5IG9mIE9iamVjdC5rZXlzKHNlY3Rpb25FcnJvcnMoKSk7IHRyYWNrIGtleSkge1xyXG4gICAgICAgIDxkaXY+4pqg77iPIHt7IHNlY3Rpb25FcnJvcnMoKVtrZXldIH19PC9kaXY+XHJcbiAgICAgIH1cclxuICAgIDwvZGl2PlxyXG4gIH1cclxuXHJcbiAgPCEtLSBBcnJheSByb3dzOiBsaXN0ZWQgaGVyZSwgZWRpdGVkIGluIHRoZSBkcmF3ZXIgYmVsb3cgLS0+XHJcbiAgQGZvciAoYXJyYXlGaWVsZCBvZiBhcnJheUZpZWxkc0ZvckFjdGl2ZVRhYjsgdHJhY2sgYXJyYXlGaWVsZC5pZCkge1xyXG4gICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19yb3dzXCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3Jvd3MtJyArIGFycmF5RmllbGQuaWRcIj5cclxuICAgICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19yb3dzLWhlYWRcIj5cclxuICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19zdW1tYXJ5LWxhYmVsXCI+e3sgZm9ybWF0RmllbGRMYWJlbChhcnJheUZpZWxkKSB9fTwvc3Bhbj5cclxuICAgICAgICBAaWYgKCFzZWN0aW9uUmVhZE9ubHkpIHtcclxuICAgICAgICAgIDxidXR0b25cclxuICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXHJcbiAgICAgICAgICAgIGNsYXNzPVwibmd4LWZvcm1fX3Jlc2V0XCJcclxuICAgICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2FkZC1yb3ctJyArIGFycmF5RmllbGQuaWRcIlxyXG4gICAgICAgICAgICAoY2xpY2spPVwib3BlbkFkZFJvdyhhcnJheUZpZWxkKVwiXHJcbiAgICAgICAgICA+KyBSb3c8L2J1dHRvbj5cclxuICAgICAgICB9XHJcbiAgICAgIDwvZGl2PlxyXG5cclxuICAgICAgQGZvciAocm93IG9mIHJvd3NPZihhcnJheUZpZWxkKTsgdHJhY2sgJGluZGV4KSB7XHJcbiAgICAgICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19yb3dcIiBbYXR0ci5kYXRhLXRlc3RpZF09XCIncm93LScgKyBhcnJheUZpZWxkLmlkICsgJy0nICsgJGluZGV4XCI+XHJcbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19yb3ctdGV4dFwiPnt7IHJvd1N1bW1hcnkoYXJyYXlGaWVsZCwgcm93KSB9fTwvc3Bhbj5cclxuICAgICAgICAgIEBpZiAoIXNlY3Rpb25SZWFkT25seSkge1xyXG4gICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19yb3ctYnRuXCJcclxuICAgICAgICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCInZWRpdC1yb3ctJyArIGFycmF5RmllbGQuaWQgKyAnLScgKyAkaW5kZXhcIlxyXG4gICAgICAgICAgICAgIChjbGljayk9XCJvcGVuRWRpdFJvdyhhcnJheUZpZWxkLCAkaW5kZXgpXCI+RWRpdDwvYnV0dG9uPlxyXG4gICAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19yb3ctYnRuXCJcclxuICAgICAgICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCInZGVsZXRlLXJvdy0nICsgYXJyYXlGaWVsZC5pZCArICctJyArICRpbmRleFwiXHJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cImRlbGV0ZVJvdyhhcnJheUZpZWxkLCAkaW5kZXgpXCI+RGVsZXRlPC9idXR0b24+XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgPC9kaXY+XHJcbiAgICAgIH0gQGVtcHR5IHtcclxuICAgICAgICA8cCBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19yb3ctZW1wdHlcIj5ObyByb3dzIHlldC48L3A+XHJcbiAgICAgIH1cclxuICAgIDwvZGl2PlxyXG4gIH1cclxuXHJcbiAgPCEtLVxyXG4gICAgUm93IGRyYXdlci4gRGVsaWJlcmF0ZWx5IG91dHNpZGUgdGhlIHRhYiBwYW5lbDogYnVpbGRpbmcgYSByb3cncyBGb3JtR3JvdXAgaW5zaWRlIHRoZVxyXG4gICAgdGFiIHZpZXcgcmFjZXMgdGhlIHRhYidzIG93biBpbml0aWFsaXNhdGlvbiAodGhlIHJlZmVyZW5jZSBoaXQgdGhpcyB3aXRoIEZvcm1BcnJheSkuXHJcbiAgLS0+XHJcbiAgQGlmIChpbmxpbmVSb3dGaWVsZCgpOyBhcyByb3dGaWVsZCkge1xyXG4gICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19kcmF3ZXJcIiBkYXRhLXRlc3RpZD1cInJvdy1kcmF3ZXJcIiBbZm9ybUdyb3VwXT1cImlubGluZVJvd0Zvcm0hXCI+XHJcbiAgICAgIDxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fcm93cy1oZWFkXCI+XHJcbiAgICAgICAgPHN0cm9uZz57eyBpbmxpbmVSb3dJbmRleCgpID09PSBudWxsID8gJ0FkZCcgOiAnRWRpdCcgfX0ge3sgZm9ybWF0RmllbGRMYWJlbChyb3dGaWVsZCkgfX0gcm93PC9zdHJvbmc+XHJcbiAgICAgIDwvZGl2PlxyXG5cclxuICAgICAgPGRpdiBjbGFzcz1cIm5neC1mb3JtX19wYW5lbFwiPlxyXG4gICAgICAgIEBmb3IgKGNoaWxkIG9mIHJvd0ZpZWxkLmNoaWxkcmVuID8/IFtdOyB0cmFjayBjaGlsZC5pZCkge1xyXG4gICAgICAgICAgPG5neC1keW5hbWljLWZpZWxkXHJcbiAgICAgICAgICAgIFtmaWVsZF09XCJjaGlsZFwiXHJcbiAgICAgICAgICAgIFtjb250cm9sXT1cImlubGluZVJvd0Zvcm0hLmdldChjaGlsZC5pZCkhXCJcclxuICAgICAgICAgICAgW2NvbmZpZ109XCJjb25maWdcIlxyXG4gICAgICAgICAgICBbbGFuZ3VhZ2VdPVwibGFuZ3VhZ2VcIlxyXG4gICAgICAgICAgICBbcmVhZG9ubHldPVwiZmFsc2VcIlxyXG4gICAgICAgICAgICBbdXNlclJvbGVzXT1cInVzZXJSb2xlc1wiXHJcbiAgICAgICAgICAgIFtzdHlsZS5ncmlkQ29sdW1uXT1cIidzcGFuIDEyJ1wiXHJcbiAgICAgICAgICAvPlxyXG4gICAgICAgIH1cclxuICAgICAgPC9kaXY+XHJcblxyXG4gICAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3NlY3Rpb24tYmFyXCI+XHJcbiAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJuZ3gtZm9ybV9fcmVzZXRcIiBkYXRhLXRlc3RpZD1cImNhbmNlbC1yb3dcIiAoY2xpY2spPVwiY2FuY2VsUm93KClcIj5DYW5jZWw8L2J1dHRvbj5cclxuICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cIm5neC1mb3JtX19zdWJtaXRcIiBkYXRhLXRlc3RpZD1cInNhdmUtcm93XCIgKGNsaWNrKT1cInNhdmVSb3coKVwiPlNhdmUgcm93PC9idXR0b24+XHJcbiAgICAgIDwvZGl2PlxyXG4gICAgPC9kaXY+XHJcbiAgfVxyXG5cclxuICA8IS0tIE1haW4gRHluYW1pYyBGb3JtIC0tPlxyXG4gIDxuZ3gtZHluYW1pYy1mb3JtXHJcbiAgICBbY29uZmlnXT1cImNvbmZpZ1wiXHJcbiAgICBbcnVsZXNdPVwicnVsZXNcIlxyXG4gICAgW2luaXRpYWxEYXRhXT1cImluaXRpYWxEYXRhXCJcclxuICAgIFtvcmlnaW5hbERhdGFdPVwib3JpZ2luYWxCYXNlbGluZSgpXCJcclxuICAgIFt1c2VyUm9sZXNdPVwidXNlclJvbGVzXCJcclxuICAgIFtsYW5ndWFnZV09XCJsYW5ndWFnZVwiXHJcbiAgICBbcmVhZG9ubHldPVwic2VjdGlvblJlYWRPbmx5XCJcclxuICAgIFtyZWFkT25seUZpZWxkc109XCJyZWFkT25seUZpZWxkc1wiXHJcbiAgICBbc2hvd0luZm9CYW5uZXJzXT1cImZhbHNlXCJcclxuICAgIFtsb2FkaW5nXT1cImxvYWRpbmdcIlxyXG4gICAgW2Vycm9yXT1cImVycm9yXCJcclxuICAgIChmb3JtQ2hhbmdlKT1cIm9uRm9ybUNoYW5nZSgkZXZlbnQpXCJcclxuICAgIChmb3JtU3VibWl0KT1cIm9uRm9ybVN1Ym1pdCgkZXZlbnQpXCJcclxuICAgIChmb3JtUmVzZXQpPVwib25Gb3JtUmVzZXQoKVwiXHJcbiAgICAoYWN0aXZlVGFiQ2hhbmdlKT1cIm9uQWN0aXZlVGFiQ2hhbmdlKCRldmVudClcIlxyXG4gIC8+XHJcbjwvZGl2PlxyXG4iXX0=
|