ngx-dynamic-entity 0.0.1 → 1.1.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 +129 -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 +25 -17
- package/esm2022/lib/field-types/currency-field.component.mjs +101 -0
- package/esm2022/lib/field-types/date-field.component.mjs +21 -13
- package/esm2022/lib/field-types/dropdown-field.component.mjs +120 -29
- package/esm2022/lib/field-types/email-field.component.mjs +101 -0
- package/esm2022/lib/field-types/entity-ref-field.component.mjs +96 -32
- 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 +79 -25
- package/esm2022/lib/field-types/number-field.component.mjs +60 -17
- 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 +58 -18
- package/esm2022/lib/field-types/textarea-field.component.mjs +26 -15
- package/esm2022/lib/form/dynamic-field/dynamic-field.component.mjs +44 -26
- package/esm2022/lib/form/dynamic-form.component.mjs +676 -69
- package/esm2022/lib/form/dynamic-record-form.component.mjs +494 -0
- package/esm2022/lib/providers/provide-field-types.mjs +72 -0
- package/esm2022/lib/providers/provide-ngx-dynamic-entity.mjs +4 -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 +35 -7
- package/esm2022/lib/tokens/injection-tokens.mjs +104 -7
- package/esm2022/public-api.mjs +31 -6
- package/fesm2022/ngx-dynamic-entity.mjs +4094 -759
- 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 +203 -24
- package/lib/form/dynamic-record-form.component.d.ts +180 -0
- package/lib/providers/provide-field-types.d.ts +24 -0
- package/lib/providers/provide-ngx-dynamic-entity.d.ts +19 -8
- 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 +113 -8
- package/package.json +18 -8
- package/public-api.d.ts +21 -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,494 @@
|
|
|
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
|
+
* Distinguishes "no tab holds this field" from "the field holds undefined", so the tab walk
|
|
12
|
+
* can keep searching in the first case and stop in the second.
|
|
13
|
+
*/
|
|
14
|
+
const FIELD_NOT_FOUND = Symbol('field-not-found');
|
|
15
|
+
/**
|
|
16
|
+
* DynamicRecordFormComponent — comprehensive tabbed record view & edit component.
|
|
17
|
+
* Supports cross-tab rule evaluation, profile header, summary drawer (showOnMinimize),
|
|
18
|
+
* interactive quick-jump links, and baseline modification tracking.
|
|
19
|
+
*/
|
|
20
|
+
export class DynamicRecordFormComponent {
|
|
21
|
+
config;
|
|
22
|
+
rules;
|
|
23
|
+
initialData;
|
|
24
|
+
userRoles = [];
|
|
25
|
+
language = 'en';
|
|
26
|
+
readonly = false;
|
|
27
|
+
loading = false;
|
|
28
|
+
error = null;
|
|
29
|
+
/** Whole record read-only, regardless of RBAC. Equivalent to the reference's `isReadOnly`. */
|
|
30
|
+
isReadOnly = false;
|
|
31
|
+
/** Specific field ids forced read-only while the rest of the record stays editable. */
|
|
32
|
+
readOnlyFields = [];
|
|
33
|
+
/**
|
|
34
|
+
* Open the record read-only, with a per-tab "Edit section" flow — one tab is edited and
|
|
35
|
+
* validated at a time. This is the framework's `EntityRecordComponent` model and the
|
|
36
|
+
* default here.
|
|
37
|
+
*
|
|
38
|
+
* Set false for a directly editable record with no view/edit distinction.
|
|
39
|
+
*/
|
|
40
|
+
viewMode = true;
|
|
41
|
+
formSubmit = new EventEmitter();
|
|
42
|
+
formChange = new EventEmitter();
|
|
43
|
+
formReset = new EventEmitter();
|
|
44
|
+
/** One tab section was saved. Carries the whole record, plus which tab was edited. */
|
|
45
|
+
sectionSave = new EventEmitter();
|
|
46
|
+
dynamicFormComp;
|
|
47
|
+
rulesEvaluation = inject(RulesEvaluationService);
|
|
48
|
+
currentData = signal({});
|
|
49
|
+
originalBaseline = signal({});
|
|
50
|
+
/**
|
|
51
|
+
* Info banners the user has dismissed, by field id.
|
|
52
|
+
*
|
|
53
|
+
* The reference's contract: an info banner persists until dismissed, **not** until the
|
|
54
|
+
* value changes. Dismissal is keyed by field so re-triggering a different field's rule
|
|
55
|
+
* still shows. Re-armed when a different record is loaded.
|
|
56
|
+
*/
|
|
57
|
+
dismissed = signal(new Set());
|
|
58
|
+
/**
|
|
59
|
+
* Info banners from the rules engine that are still showing.
|
|
60
|
+
*
|
|
61
|
+
* A getter, not a `computed`: the source is `dynamicFormComp`, a ViewChild. A computed
|
|
62
|
+
* evaluated before the view initialises would capture no dependency on the child's
|
|
63
|
+
* `ruleResult` signal and then never recompute. Read per change-detection pass instead.
|
|
64
|
+
*/
|
|
65
|
+
get infoBanners() {
|
|
66
|
+
const result = this.dynamicFormComp?.ruleResult();
|
|
67
|
+
const gone = this.dismissed();
|
|
68
|
+
return Object.entries(result?.infoBanners ?? {})
|
|
69
|
+
.filter(([fieldId]) => !gone.has(fieldId))
|
|
70
|
+
.map(([fieldId, message]) => ({ fieldId, message: String(message) }));
|
|
71
|
+
}
|
|
72
|
+
dismissInfoBanner(fieldId) {
|
|
73
|
+
this.dismissed.set(new Set(this.dismissed()).add(fieldId));
|
|
74
|
+
}
|
|
75
|
+
/** True when nothing in the record may be edited. */
|
|
76
|
+
get recordReadOnly() {
|
|
77
|
+
return this.readonly || this.isReadOnly;
|
|
78
|
+
}
|
|
79
|
+
// ─── Inline array-row editing ───────────────────────────────────────────────
|
|
80
|
+
/**
|
|
81
|
+
* A row of an `array` field is edited in a drawer, not inline in the tab panel.
|
|
82
|
+
*
|
|
83
|
+
* The drawer is rendered outside the tab container on purpose: the reference found that
|
|
84
|
+
* building a row's FormGroup inside the tab view races the tab's own initialisation, so
|
|
85
|
+
* the row form is kept detached and only pushed into the FormArray on save.
|
|
86
|
+
*/
|
|
87
|
+
inlineRowField = signal(null);
|
|
88
|
+
inlineRowIndex = signal(null);
|
|
89
|
+
inlineRowForm = null;
|
|
90
|
+
/** `array` fields on the tab currently shown, so the template can list their rows. */
|
|
91
|
+
get arrayFieldsForActiveTab() {
|
|
92
|
+
const tabId = this.activeTabId;
|
|
93
|
+
const tab = tabId ? findTab(this.config?.tabs, tabId) : null;
|
|
94
|
+
return (tab?.fields ?? []).filter(f => f.type === 'array');
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Rows currently held by an `array` field, read from the record value.
|
|
98
|
+
*
|
|
99
|
+
* Deliberately not read off the child's FormArray: that control only exists once the
|
|
100
|
+
* child has initialised, so rendering from it changed value mid-pass. `currentData` is a
|
|
101
|
+
* signal seeded from `initialData` and kept current by the child's `formChange`.
|
|
102
|
+
*/
|
|
103
|
+
rowsOf(field) {
|
|
104
|
+
const tabId = this.activeTabId;
|
|
105
|
+
const tabData = tabId ? getTabData(tabId, this.currentData(), this.config) : null;
|
|
106
|
+
const rows = tabData?.[field.id];
|
|
107
|
+
return Array.isArray(rows) ? rows : [];
|
|
108
|
+
}
|
|
109
|
+
/** One row rendered as text, so the list is readable without opening the drawer. */
|
|
110
|
+
rowSummary(field, row) {
|
|
111
|
+
const value = row ?? {};
|
|
112
|
+
const parts = (field.children ?? [])
|
|
113
|
+
.map(child => value[child.id])
|
|
114
|
+
.filter(v => v !== null && v !== undefined && v !== '')
|
|
115
|
+
.map(v => (typeof v === 'object' ? JSON.stringify(v) : String(v)));
|
|
116
|
+
return parts.length ? parts.join(' · ') : '(empty)';
|
|
117
|
+
}
|
|
118
|
+
openAddRow(field) {
|
|
119
|
+
if (this.sectionReadOnly)
|
|
120
|
+
return;
|
|
121
|
+
this.inlineRowField.set(field);
|
|
122
|
+
this.inlineRowIndex.set(null);
|
|
123
|
+
this.inlineRowForm = this.dynamicFormComp?.createArrayRow(field);
|
|
124
|
+
}
|
|
125
|
+
openEditRow(field, index) {
|
|
126
|
+
if (this.sectionReadOnly)
|
|
127
|
+
return;
|
|
128
|
+
this.inlineRowField.set(field);
|
|
129
|
+
this.inlineRowIndex.set(index);
|
|
130
|
+
this.inlineRowForm = this.dynamicFormComp?.createArrayRow(field, this.rowsOf(field)[index]);
|
|
131
|
+
}
|
|
132
|
+
cancelRow() {
|
|
133
|
+
this.inlineRowField.set(null);
|
|
134
|
+
this.inlineRowIndex.set(null);
|
|
135
|
+
this.inlineRowForm = null;
|
|
136
|
+
}
|
|
137
|
+
/** Commit the drawer into the FormArray. Rules re-evaluate off the resulting value change. */
|
|
138
|
+
saveRow() {
|
|
139
|
+
const field = this.inlineRowField();
|
|
140
|
+
const form = this.dynamicFormComp;
|
|
141
|
+
if (!field || !form || !this.inlineRowForm)
|
|
142
|
+
return;
|
|
143
|
+
if (this.inlineRowForm.invalid) {
|
|
144
|
+
this.inlineRowForm.markAllAsTouched();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const array = form.getArrayControl(field.id, this.activeTabId ?? undefined);
|
|
148
|
+
if (!array)
|
|
149
|
+
return;
|
|
150
|
+
const index = this.inlineRowIndex();
|
|
151
|
+
if (index === null)
|
|
152
|
+
array.push(form.createArrayRow(field, this.inlineRowForm.value));
|
|
153
|
+
else
|
|
154
|
+
array.at(index).patchValue(this.inlineRowForm.value);
|
|
155
|
+
array.updateValueAndValidity();
|
|
156
|
+
this.currentData.set(form.extractRecord());
|
|
157
|
+
this.cancelRow();
|
|
158
|
+
}
|
|
159
|
+
deleteRow(field, index) {
|
|
160
|
+
if (this.sectionReadOnly)
|
|
161
|
+
return;
|
|
162
|
+
const array = this.dynamicFormComp?.getArrayControl(field.id, this.activeTabId ?? undefined);
|
|
163
|
+
array?.removeAt(index);
|
|
164
|
+
array?.updateValueAndValidity();
|
|
165
|
+
if (this.dynamicFormComp)
|
|
166
|
+
this.currentData.set(this.dynamicFormComp.extractRecord());
|
|
167
|
+
if (this.inlineRowIndex() === index)
|
|
168
|
+
this.cancelRow();
|
|
169
|
+
}
|
|
170
|
+
// ─── Per-tab section editing ────────────────────────────────────────────────
|
|
171
|
+
/**
|
|
172
|
+
* The tab currently open for editing, or null when the record is being viewed.
|
|
173
|
+
*
|
|
174
|
+
* The reference edits one tab section at a time rather than the whole record, and
|
|
175
|
+
* validates only that section on save — a required field on an untouched tab must not
|
|
176
|
+
* block saving the tab you are actually working on.
|
|
177
|
+
*/
|
|
178
|
+
editingTabId = signal(null);
|
|
179
|
+
/**
|
|
180
|
+
* The tab the inner form is showing.
|
|
181
|
+
*
|
|
182
|
+
* Tracked here rather than read off the ViewChild. The child resolves its first tab as
|
|
183
|
+
* part of its own initialisation, so reading it during this component's render changed
|
|
184
|
+
* value mid-pass — an ExpressionChangedAfterItHasBeenChecked error. Seeded from the
|
|
185
|
+
* config and kept in step by the child's `activeTabChange` output.
|
|
186
|
+
*/
|
|
187
|
+
activeTabIdSig = signal(null);
|
|
188
|
+
get activeTabId() {
|
|
189
|
+
return this.activeTabIdSig();
|
|
190
|
+
}
|
|
191
|
+
onActiveTabChange(tabId) {
|
|
192
|
+
if (this.activeTabIdSig() === tabId)
|
|
193
|
+
return;
|
|
194
|
+
this.activeTabIdSig.set(tabId);
|
|
195
|
+
// Editing does not follow the user to another tab.
|
|
196
|
+
this.editingTabId.set(null);
|
|
197
|
+
this.cancelRow();
|
|
198
|
+
}
|
|
199
|
+
/** First tab a user can see, used to seed the active tab before the child reports one. */
|
|
200
|
+
firstVisibleTabId() {
|
|
201
|
+
return this.config?.tabs?.find(t => t.visibility !== false)?.id ?? null;
|
|
202
|
+
}
|
|
203
|
+
/** Errors from the last save attempt, by field id. */
|
|
204
|
+
sectionErrors = signal({});
|
|
205
|
+
/** Template access to `Object.keys` for iterating the error map. */
|
|
206
|
+
Object = Object;
|
|
207
|
+
get isEditingActiveTab() {
|
|
208
|
+
// Without view mode there is no view/edit distinction — everything is always editable.
|
|
209
|
+
if (!this.viewMode)
|
|
210
|
+
return true;
|
|
211
|
+
return !!this.activeTabId && this.editingTabId() === this.activeTabId;
|
|
212
|
+
}
|
|
213
|
+
/** Fields render read-only unless their own tab is the one being edited. */
|
|
214
|
+
get sectionReadOnly() {
|
|
215
|
+
return this.recordReadOnly || !this.isEditingActiveTab;
|
|
216
|
+
}
|
|
217
|
+
editSection() {
|
|
218
|
+
if (!this.viewMode || this.recordReadOnly || !this.activeTabId)
|
|
219
|
+
return;
|
|
220
|
+
this.sectionErrors.set({});
|
|
221
|
+
this.editingTabId.set(this.activeTabId);
|
|
222
|
+
}
|
|
223
|
+
/** Discard this section's edits, restoring each field from the session baseline. */
|
|
224
|
+
cancelSection() {
|
|
225
|
+
const tabId = this.editingTabId();
|
|
226
|
+
const form = this.dynamicFormComp;
|
|
227
|
+
if (tabId && form) {
|
|
228
|
+
const tab = findTab(this.config?.tabs, tabId);
|
|
229
|
+
const baselineTab = (getTabData(tabId, this.originalBaseline(), this.config) ?? {});
|
|
230
|
+
for (const field of tab?.fields ?? []) {
|
|
231
|
+
const baselineValue = baselineTab[field.id];
|
|
232
|
+
// A FormArray cannot be restored with setValue — that demands a value shaped exactly
|
|
233
|
+
// like the current rows. Rebuild it from the baseline rows instead.
|
|
234
|
+
const array = form.getArrayControl(field.id, tabId);
|
|
235
|
+
if (array) {
|
|
236
|
+
array.clear();
|
|
237
|
+
for (const row of Array.isArray(baselineValue) ? baselineValue : []) {
|
|
238
|
+
array.push(form.createArrayRow(field, row));
|
|
239
|
+
}
|
|
240
|
+
array.updateValueAndValidity();
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
form.getControl(field.id, tabId)?.setValue(baselineValue ?? null);
|
|
244
|
+
}
|
|
245
|
+
this.currentData.set(form.extractRecord());
|
|
246
|
+
}
|
|
247
|
+
this.sectionErrors.set({});
|
|
248
|
+
this.editingTabId.set(null);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Validate this section only, then emit it. Validation is scoped two ways: Angular
|
|
252
|
+
* validity of the tab's own controls, and rules filtered to this tab — a rule targeting
|
|
253
|
+
* a hidden tab must not block the save (the reference's OV0-968 fix).
|
|
254
|
+
*/
|
|
255
|
+
saveSection() {
|
|
256
|
+
const tabId = this.editingTabId();
|
|
257
|
+
const form = this.dynamicFormComp;
|
|
258
|
+
if (!tabId || !form)
|
|
259
|
+
return;
|
|
260
|
+
const tab = findTab(this.config?.tabs, tabId);
|
|
261
|
+
const errors = {};
|
|
262
|
+
for (const field of tab?.fields ?? []) {
|
|
263
|
+
const ctrl = form.getControl(field.id, tabId);
|
|
264
|
+
if (ctrl?.invalid) {
|
|
265
|
+
ctrl.markAsTouched();
|
|
266
|
+
errors[field.id] = `${resolveLabel(field.label, this.language)} is invalid.`;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const scoped = this.rulesEvaluation.filterForTab(this.rules, tabId, this.config);
|
|
270
|
+
const ruleErrors = this.rulesEvaluation.evaluate(scoped, form.formValues(), this.originalBaseline())
|
|
271
|
+
.validationErrors;
|
|
272
|
+
Object.assign(errors, ruleErrors);
|
|
273
|
+
if (Object.keys(errors).length > 0) {
|
|
274
|
+
this.sectionErrors.set(errors);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
this.sectionErrors.set({});
|
|
278
|
+
this.editingTabId.set(null);
|
|
279
|
+
const record = form.extractRecord();
|
|
280
|
+
this.currentData.set(record);
|
|
281
|
+
this.sectionSave.emit({ tabId, record });
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Seed the session baseline from the loaded record so `VALUE_CHANGED` rules and the
|
|
285
|
+
* modification banner compare against the record as it was opened — not as it was
|
|
286
|
+
* after the first keystroke.
|
|
287
|
+
*/
|
|
288
|
+
ngOnChanges(changes) {
|
|
289
|
+
if (changes['initialData'] || changes['config']) {
|
|
290
|
+
this.originalBaseline.set({ ...(this.initialData ?? {}) });
|
|
291
|
+
this.currentData.set({ ...(this.initialData ?? {}) });
|
|
292
|
+
this.activeTabIdSig.set(this.firstVisibleTabId());
|
|
293
|
+
// A different record is a different set of banners — re-arm them.
|
|
294
|
+
this.dismissed.set(new Set());
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
get recordTitle() {
|
|
298
|
+
if (!this.config)
|
|
299
|
+
return 'Record';
|
|
300
|
+
return this.config.name ? resolveLabel(this.config.name, this.language) : this.config.entity;
|
|
301
|
+
}
|
|
302
|
+
get avatarLetter() {
|
|
303
|
+
return (this.recordTitle || 'R').charAt(0).toUpperCase();
|
|
304
|
+
}
|
|
305
|
+
// ─── Header fields (isProfileImage / isHeaderToggle) ────────────────────────
|
|
306
|
+
/** Every field in the config, at any depth. */
|
|
307
|
+
allFields() {
|
|
308
|
+
const out = [];
|
|
309
|
+
const walkFields = (fields) => {
|
|
310
|
+
for (const f of fields ?? []) {
|
|
311
|
+
out.push(f);
|
|
312
|
+
walkFields(f.children);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
const walkTabs = (tabs) => {
|
|
316
|
+
for (const t of tabs ?? []) {
|
|
317
|
+
walkFields(t.fields);
|
|
318
|
+
walkTabs(t.children);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
walkTabs(this.config?.tabs);
|
|
322
|
+
return out;
|
|
323
|
+
}
|
|
324
|
+
/** The field flagged `isProfileImage`, rendered as the record avatar. */
|
|
325
|
+
get headerProfileField() {
|
|
326
|
+
return this.allFields().find(f => f.isProfileImage) ?? null;
|
|
327
|
+
}
|
|
328
|
+
/** The field flagged `isHeaderToggle`, rendered as a status switch in the header. */
|
|
329
|
+
get headerToggleField() {
|
|
330
|
+
return this.allFields().find(f => f.isHeaderToggle) ?? null;
|
|
331
|
+
}
|
|
332
|
+
/** Avatar image URL, when the profile field holds a persisted FileRef. */
|
|
333
|
+
get profileImageUrl() {
|
|
334
|
+
const field = this.headerProfileField;
|
|
335
|
+
if (!field)
|
|
336
|
+
return null;
|
|
337
|
+
const value = this.fieldValue(field.id);
|
|
338
|
+
return value?.url ?? null;
|
|
339
|
+
}
|
|
340
|
+
get headerToggleValue() {
|
|
341
|
+
const field = this.headerToggleField;
|
|
342
|
+
return field ? this.fieldValue(field.id) === true : false;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Flip the header toggle. It writes straight to the control rather than going through
|
|
346
|
+
* section editing — the reference treats it as a record-level status switch, not part of
|
|
347
|
+
* any one tab's section.
|
|
348
|
+
*/
|
|
349
|
+
toggleHeaderStatus() {
|
|
350
|
+
const field = this.headerToggleField;
|
|
351
|
+
if (!field || this.recordReadOnly)
|
|
352
|
+
return;
|
|
353
|
+
const ctrl = this.controlFor(field.id);
|
|
354
|
+
if (!ctrl)
|
|
355
|
+
return;
|
|
356
|
+
ctrl.setValue(!this.headerToggleValue);
|
|
357
|
+
if (this.dynamicFormComp)
|
|
358
|
+
this.currentData.set(this.dynamicFormComp.extractRecord());
|
|
359
|
+
}
|
|
360
|
+
/** Locate a field's control wherever it lives, since header fields may be on any tab. */
|
|
361
|
+
controlFor(fieldId) {
|
|
362
|
+
return this.dynamicFormComp?.getControl(fieldId) ?? null;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* A field's current value, read from the record so it is stable during render.
|
|
366
|
+
*
|
|
367
|
+
* Resolved through `getTabData` so it honours exactly the nesting contract the form
|
|
368
|
+
* patches with — `{ tabId: { fieldId } }`, or the record root when the tab sets
|
|
369
|
+
* `flatData`. Reading the record flat instead is not a shortcut: it makes the summary
|
|
370
|
+
* display values the form never loaded, which reads as "the data is there" while every
|
|
371
|
+
* control behind it is empty.
|
|
372
|
+
*
|
|
373
|
+
* Walks sub-tabs as well: a summary, header, or profile field may sit on any tab at any
|
|
374
|
+
* depth.
|
|
375
|
+
*/
|
|
376
|
+
fieldValue(fieldId) {
|
|
377
|
+
const visit = (tabs = []) => {
|
|
378
|
+
for (const tab of tabs) {
|
|
379
|
+
const data = getTabData(tab.id, this.currentData(), this.config);
|
|
380
|
+
if (data && fieldId in data)
|
|
381
|
+
return data[fieldId];
|
|
382
|
+
if (tab.children) {
|
|
383
|
+
const found = visit(tab.children);
|
|
384
|
+
if (found !== FIELD_NOT_FOUND)
|
|
385
|
+
return found;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return FIELD_NOT_FOUND;
|
|
389
|
+
};
|
|
390
|
+
const result = visit(this.config?.tabs);
|
|
391
|
+
return result === FIELD_NOT_FOUND ? undefined : result;
|
|
392
|
+
}
|
|
393
|
+
isModified = computed(() => {
|
|
394
|
+
const orig = this.originalBaseline();
|
|
395
|
+
const curr = this.currentData();
|
|
396
|
+
if (!orig || !curr || Object.keys(orig).length === 0)
|
|
397
|
+
return false;
|
|
398
|
+
return JSON.stringify(orig) !== JSON.stringify(curr);
|
|
399
|
+
});
|
|
400
|
+
summaryFields = computed(() => {
|
|
401
|
+
const fields = [];
|
|
402
|
+
const walkTabs = (tabs = []) => {
|
|
403
|
+
for (const t of tabs) {
|
|
404
|
+
for (const f of t.fields ?? []) {
|
|
405
|
+
if (f.showOnMinimize)
|
|
406
|
+
fields.push(f);
|
|
407
|
+
}
|
|
408
|
+
if (t.children)
|
|
409
|
+
walkTabs(t.children);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
walkTabs(this.config?.tabs);
|
|
413
|
+
return fields;
|
|
414
|
+
});
|
|
415
|
+
onFormChange(data) {
|
|
416
|
+
// Baseline is seeded from `initialData` in ngOnChanges; only fill it here when the
|
|
417
|
+
// record was opened empty (create flow), so the first emitted shape becomes the baseline.
|
|
418
|
+
if (Object.keys(this.originalBaseline()).length === 0) {
|
|
419
|
+
this.originalBaseline.set({ ...data });
|
|
420
|
+
}
|
|
421
|
+
this.currentData.set(data);
|
|
422
|
+
this.formChange.emit(data);
|
|
423
|
+
}
|
|
424
|
+
onFormSubmit(data) {
|
|
425
|
+
this.formSubmit.emit(data);
|
|
426
|
+
}
|
|
427
|
+
onFormReset() {
|
|
428
|
+
this.formReset.emit();
|
|
429
|
+
}
|
|
430
|
+
formatFieldLabel(field) {
|
|
431
|
+
return resolveLabel(field.label, this.language);
|
|
432
|
+
}
|
|
433
|
+
/** Summary values render through the shared core formatter, not a local stringifier. */
|
|
434
|
+
formatFieldValue(field) {
|
|
435
|
+
return formatDisplayValue(field.type, field.options, this.fieldValue(field.id), this.language);
|
|
436
|
+
}
|
|
437
|
+
jumpToField(fieldId) {
|
|
438
|
+
// Find parent tab for field
|
|
439
|
+
for (const tab of this.config?.tabs || []) {
|
|
440
|
+
const hasField = (tab.fields || []).some(f => f.id === fieldId);
|
|
441
|
+
if (hasField) {
|
|
442
|
+
this.dynamicFormComp?.setActiveTab(tab.id);
|
|
443
|
+
setTimeout(() => {
|
|
444
|
+
const el = document.getElementById(`field-container-${fieldId}`);
|
|
445
|
+
if (el) {
|
|
446
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
447
|
+
el.focus();
|
|
448
|
+
}
|
|
449
|
+
}, 50);
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicRecordFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
455
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: DynamicRecordFormComponent, isStandalone: true, selector: "ngx-dynamic-record-form", inputs: { config: "config", rules: "rules", initialData: "initialData", userRoles: "userRoles", language: "language", readonly: "readonly", loading: "loading", error: "error", isReadOnly: "isReadOnly", readOnlyFields: "readOnlyFields", viewMode: "viewMode" }, outputs: { formSubmit: "formSubmit", formChange: "formChange", formReset: "formReset", sectionSave: "sectionSave" }, viewQueries: [{ propertyName: "dynamicFormComp", first: true, predicate: DynamicFormComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"ngx-record-editor\">\n <!-- Record Header -->\n <div class=\"ngx-record-editor__header\">\n <div class=\"ngx-record-editor__title-group\">\n @if (profileImageUrl) {\n <img\n class=\"ngx-record-editor__avatar\"\n data-testid=\"header-profile-image\"\n [src]=\"profileImageUrl\"\n [alt]=\"recordTitle\"\n />\n } @else {\n <div class=\"ngx-record-editor__avatar\" data-testid=\"header-avatar-initial\">\n {{ avatarLetter }}\n </div>\n }\n <div>\n <h2 class=\"ngx-record-editor__title\" data-testid=\"record-title\">\n {{ recordTitle }}\n </h2>\n <span class=\"ngx-record-editor__subtitle\">Entity: {{ config.entity }}</span>\n </div>\n </div>\n\n <!-- Record-level status switch, outside section editing -->\n @if (headerToggleField; as toggleField) {\n <label class=\"ngx-record-editor__header-toggle\">\n <span>{{ formatFieldLabel(toggleField) }}</span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n data-testid=\"header-toggle\"\n [checked]=\"headerToggleValue\"\n [disabled]=\"recordReadOnly\"\n [attr.aria-checked]=\"headerToggleValue\"\n (change)=\"toggleHeaderStatus()\"\n />\n <span class=\"ngx-record-editor__header-toggle-text\">{{ headerToggleValue ? 'Yes' : 'No' }}</span>\n </label>\n }\n </div>\n\n <!-- Dismissible info banners (rules `info` action) -->\n @for (banner of infoBanners; track banner.fieldId) {\n <div\n class=\"ngx-record-editor__banner ngx-record-editor__banner--info\"\n role=\"status\"\n [attr.data-testid]=\"'info-banner-' + banner.fieldId\"\n >\n <span>\u2139\uFE0F {{ banner.message }}</span>\n <button\n type=\"button\"\n class=\"ngx-record-editor__banner-dismiss\"\n [attr.data-testid]=\"'dismiss-' + banner.fieldId\"\n [attr.aria-label]=\"'Dismiss: ' + banner.message\"\n (click)=\"dismissInfoBanner(banner.fieldId)\"\n >\u2715</button>\n </div>\n }\n\n <!-- Modification baseline banner -->\n @if (isModified()) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--warning\">\n \uD83D\uDD12 Record modified from session baseline \u2014 unsaved changes pending.\n </div>\n }\n\n <!-- Summary Panel (showOnMinimize fields with interactive jump) -->\n @if (summaryFields().length > 0) {\n <div class=\"ngx-record-editor__summary-panel\" data-testid=\"summary-panel\">\n @for (sf of summaryFields(); track sf.id) {\n <div\n class=\"ngx-record-editor__summary-item\"\n [attr.data-testid]=\"'summary-' + sf.id\"\n (click)=\"jumpToField(sf.id)\"\n [title]=\"'Click to jump to ' + formatFieldLabel(sf)\"\n >\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(sf) }}</span>\n <span class=\"ngx-record-editor__summary-val\">{{ formatFieldValue(sf) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Section edit bar: one tab is edited at a time, and validated on its own -->\n @if (viewMode && !recordReadOnly) {\n <div class=\"ngx-record-editor__section-bar\">\n @if (isEditingActiveTab) {\n <span class=\"ngx-record-editor__section-hint\">Editing this section</span>\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-section\" (click)=\"cancelSection()\">\n Cancel\n </button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-section\" (click)=\"saveSection()\">\n Save section\n </button>\n } @else {\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"edit-section\" (click)=\"editSection()\">\n Edit section\n </button>\n }\n </div>\n }\n\n @if (Object.keys(sectionErrors()).length > 0) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--error\" role=\"alert\" data-testid=\"section-errors\">\n @for (key of Object.keys(sectionErrors()); track key) {\n <div>\u26A0\uFE0F {{ sectionErrors()[key] }}</div>\n }\n </div>\n }\n\n <!-- Array rows: listed here, edited in the drawer below -->\n @for (arrayField of arrayFieldsForActiveTab; track arrayField.id) {\n <div class=\"ngx-record-editor__rows\" [attr.data-testid]=\"'rows-' + arrayField.id\">\n <div class=\"ngx-record-editor__rows-head\">\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(arrayField) }}</span>\n @if (!sectionReadOnly) {\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n [attr.data-testid]=\"'add-row-' + arrayField.id\"\n (click)=\"openAddRow(arrayField)\"\n >+ Row</button>\n }\n </div>\n\n @for (row of rowsOf(arrayField); track $index) {\n <div class=\"ngx-record-editor__row\" [attr.data-testid]=\"'row-' + arrayField.id + '-' + $index\">\n <span class=\"ngx-record-editor__row-text\">{{ rowSummary(arrayField, row) }}</span>\n @if (!sectionReadOnly) {\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'edit-row-' + arrayField.id + '-' + $index\"\n (click)=\"openEditRow(arrayField, $index)\">Edit</button>\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'delete-row-' + arrayField.id + '-' + $index\"\n (click)=\"deleteRow(arrayField, $index)\">Delete</button>\n }\n </div>\n } @empty {\n <p class=\"ngx-record-editor__row-empty\">No rows yet.</p>\n }\n </div>\n }\n\n <!--\n Row drawer. Deliberately outside the tab panel: building a row's FormGroup inside the\n tab view races the tab's own initialisation (the reference hit this with FormArray).\n -->\n @if (inlineRowField(); as rowField) {\n <div class=\"ngx-record-editor__drawer\" data-testid=\"row-drawer\" [formGroup]=\"inlineRowForm!\">\n <div class=\"ngx-record-editor__rows-head\">\n <strong>{{ inlineRowIndex() === null ? 'Add' : 'Edit' }} {{ formatFieldLabel(rowField) }} row</strong>\n </div>\n\n <div class=\"ngx-form__panel\">\n @for (child of rowField.children ?? []; track child.id) {\n <ngx-dynamic-field\n [field]=\"child\"\n [control]=\"inlineRowForm!.get(child.id)!\"\n [config]=\"config\"\n [language]=\"language\"\n [readonly]=\"false\"\n [userRoles]=\"userRoles\"\n [style.gridColumn]=\"'span 12'\"\n />\n }\n </div>\n\n <div class=\"ngx-record-editor__section-bar\">\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-row\" (click)=\"cancelRow()\">Cancel</button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-row\" (click)=\"saveRow()\">Save row</button>\n </div>\n </div>\n }\n\n <!-- Main Dynamic Form -->\n <ngx-dynamic-form\n [config]=\"config\"\n [rules]=\"rules\"\n [initialData]=\"initialData\"\n [originalData]=\"originalBaseline()\"\n [userRoles]=\"userRoles\"\n [language]=\"language\"\n [readonly]=\"sectionReadOnly\"\n [readOnlyFields]=\"readOnlyFields\"\n [showInfoBanners]=\"false\"\n [loading]=\"loading\"\n [error]=\"error\"\n (formChange)=\"onFormChange($event)\"\n (formSubmit)=\"onFormSubmit($event)\"\n (formReset)=\"onFormReset()\"\n (activeTabChange)=\"onActiveTabChange($event)\"\n />\n</div>\n", styles: [".ngx-record-editor{display:flex;flex-direction:column;gap:16px;background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:20px;box-shadow:0 4px 6px -1px #0000000d}.ngx-record-editor__header{display:flex;align-items:center;justify-content:space-between;padding-bottom:16px;border-bottom:1px solid #f3f4f6}.ngx-record-editor__title-group{display:flex;align-items:center;gap:12px}.ngx-record-editor__avatar{width:48px;height:48px;border-radius:50%;object-fit:cover;background:#e0e7ff;color:#4f46e5;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px}.ngx-record-editor__title{font-size:20px;font-weight:700;color:#111827;margin:0}.ngx-record-editor__subtitle{font-size:13px;color:#6b7280}.ngx-record-editor__header-toggle{display:flex;align-items:center;gap:8px;font-size:13px;color:#374151}.ngx-record-editor__header-toggle-text{font-weight:600}.ngx-record-editor__banner{padding:10px 14px;border-radius:8px;font-size:13px;font-weight:500}.ngx-record-editor__banner--info{background:#eff6ff;border:1px solid #dbeafe;color:#1d4ed8;display:flex;align-items:center;justify-content:space-between;gap:12px}.ngx-record-editor__banner-dismiss{background:none;border:none;cursor:pointer;color:inherit;font-size:14px;line-height:1;padding:2px 6px;border-radius:4px}.ngx-record-editor__banner-dismiss:hover{background:#0000000f}.ngx-record-editor__banner--error{background:#fef2f2;border:1px solid #fecaca;color:#b91c1c}.ngx-record-editor__section-bar{display:flex;align-items:center;justify-content:flex-end;gap:8px}.ngx-record-editor__section-hint{margin-right:auto;font-size:12px;color:#6b7280}.ngx-record-editor__banner--warning{background:#fffbeb;border:1px solid #fef3c7;color:#b45309}.ngx-record-editor__rows{border:1px solid #f3f4f6;border-radius:8px;padding:12px 14px;display:flex;flex-direction:column;gap:6px}.ngx-record-editor__rows-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.ngx-record-editor__row{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;background:#f9fafb}.ngx-record-editor__row-text{flex:1;font-size:13px;overflow-wrap:anywhere}.ngx-record-editor__row-btn{background:none;border:1px solid #e5e7eb;border-radius:6px;cursor:pointer;font-size:12px;padding:2px 8px}.ngx-record-editor__row-empty{font-size:12px;color:#6b7280;margin:0}.ngx-record-editor__drawer{border:1px solid #dbeafe;background:#f8fafc;border-radius:8px;padding:14px;display:flex;flex-direction:column;gap:12px}.ngx-record-editor__summary-panel{background:#f9fafb;border:1px solid #f3f4f6;border-radius:8px;padding:12px 16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}.ngx-record-editor__summary-item{display:flex;flex-direction:column;cursor:pointer;padding:6px 10px;border-radius:6px;transition:background .15s}.ngx-record-editor__summary-item:hover{background:#eff6ff}.ngx-record-editor__summary-label{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:#6b7280;font-weight:600}.ngx-record-editor__summary-val{font-size:14px;font-weight:600;color:#1f2937}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: DynamicFormComponent, selector: "ngx-dynamic-form", inputs: ["config", "rules", "initialData", "originalData", "userRoles", "language", "readonly", "readOnlyFields", "showInfoBanners", "loading", "error", "changeDebounceMs", "preview"], outputs: ["formSubmit", "formChange", "formReset", "activeTabChange"] }, { kind: "component", type: DynamicFieldComponent, selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "currentTabId", "language", "readonly", "userRoles"] }] });
|
|
456
|
+
}
|
|
457
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DynamicRecordFormComponent, decorators: [{
|
|
458
|
+
type: Component,
|
|
459
|
+
args: [{ selector: 'ngx-dynamic-record-form', standalone: true, imports: [CommonModule, ReactiveFormsModule, DynamicFormComponent, DynamicFieldComponent], template: "<div class=\"ngx-record-editor\">\n <!-- Record Header -->\n <div class=\"ngx-record-editor__header\">\n <div class=\"ngx-record-editor__title-group\">\n @if (profileImageUrl) {\n <img\n class=\"ngx-record-editor__avatar\"\n data-testid=\"header-profile-image\"\n [src]=\"profileImageUrl\"\n [alt]=\"recordTitle\"\n />\n } @else {\n <div class=\"ngx-record-editor__avatar\" data-testid=\"header-avatar-initial\">\n {{ avatarLetter }}\n </div>\n }\n <div>\n <h2 class=\"ngx-record-editor__title\" data-testid=\"record-title\">\n {{ recordTitle }}\n </h2>\n <span class=\"ngx-record-editor__subtitle\">Entity: {{ config.entity }}</span>\n </div>\n </div>\n\n <!-- Record-level status switch, outside section editing -->\n @if (headerToggleField; as toggleField) {\n <label class=\"ngx-record-editor__header-toggle\">\n <span>{{ formatFieldLabel(toggleField) }}</span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n data-testid=\"header-toggle\"\n [checked]=\"headerToggleValue\"\n [disabled]=\"recordReadOnly\"\n [attr.aria-checked]=\"headerToggleValue\"\n (change)=\"toggleHeaderStatus()\"\n />\n <span class=\"ngx-record-editor__header-toggle-text\">{{ headerToggleValue ? 'Yes' : 'No' }}</span>\n </label>\n }\n </div>\n\n <!-- Dismissible info banners (rules `info` action) -->\n @for (banner of infoBanners; track banner.fieldId) {\n <div\n class=\"ngx-record-editor__banner ngx-record-editor__banner--info\"\n role=\"status\"\n [attr.data-testid]=\"'info-banner-' + banner.fieldId\"\n >\n <span>\u2139\uFE0F {{ banner.message }}</span>\n <button\n type=\"button\"\n class=\"ngx-record-editor__banner-dismiss\"\n [attr.data-testid]=\"'dismiss-' + banner.fieldId\"\n [attr.aria-label]=\"'Dismiss: ' + banner.message\"\n (click)=\"dismissInfoBanner(banner.fieldId)\"\n >\u2715</button>\n </div>\n }\n\n <!-- Modification baseline banner -->\n @if (isModified()) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--warning\">\n \uD83D\uDD12 Record modified from session baseline \u2014 unsaved changes pending.\n </div>\n }\n\n <!-- Summary Panel (showOnMinimize fields with interactive jump) -->\n @if (summaryFields().length > 0) {\n <div class=\"ngx-record-editor__summary-panel\" data-testid=\"summary-panel\">\n @for (sf of summaryFields(); track sf.id) {\n <div\n class=\"ngx-record-editor__summary-item\"\n [attr.data-testid]=\"'summary-' + sf.id\"\n (click)=\"jumpToField(sf.id)\"\n [title]=\"'Click to jump to ' + formatFieldLabel(sf)\"\n >\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(sf) }}</span>\n <span class=\"ngx-record-editor__summary-val\">{{ formatFieldValue(sf) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Section edit bar: one tab is edited at a time, and validated on its own -->\n @if (viewMode && !recordReadOnly) {\n <div class=\"ngx-record-editor__section-bar\">\n @if (isEditingActiveTab) {\n <span class=\"ngx-record-editor__section-hint\">Editing this section</span>\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-section\" (click)=\"cancelSection()\">\n Cancel\n </button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-section\" (click)=\"saveSection()\">\n Save section\n </button>\n } @else {\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"edit-section\" (click)=\"editSection()\">\n Edit section\n </button>\n }\n </div>\n }\n\n @if (Object.keys(sectionErrors()).length > 0) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--error\" role=\"alert\" data-testid=\"section-errors\">\n @for (key of Object.keys(sectionErrors()); track key) {\n <div>\u26A0\uFE0F {{ sectionErrors()[key] }}</div>\n }\n </div>\n }\n\n <!-- Array rows: listed here, edited in the drawer below -->\n @for (arrayField of arrayFieldsForActiveTab; track arrayField.id) {\n <div class=\"ngx-record-editor__rows\" [attr.data-testid]=\"'rows-' + arrayField.id\">\n <div class=\"ngx-record-editor__rows-head\">\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(arrayField) }}</span>\n @if (!sectionReadOnly) {\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n [attr.data-testid]=\"'add-row-' + arrayField.id\"\n (click)=\"openAddRow(arrayField)\"\n >+ Row</button>\n }\n </div>\n\n @for (row of rowsOf(arrayField); track $index) {\n <div class=\"ngx-record-editor__row\" [attr.data-testid]=\"'row-' + arrayField.id + '-' + $index\">\n <span class=\"ngx-record-editor__row-text\">{{ rowSummary(arrayField, row) }}</span>\n @if (!sectionReadOnly) {\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'edit-row-' + arrayField.id + '-' + $index\"\n (click)=\"openEditRow(arrayField, $index)\">Edit</button>\n <button type=\"button\" class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'delete-row-' + arrayField.id + '-' + $index\"\n (click)=\"deleteRow(arrayField, $index)\">Delete</button>\n }\n </div>\n } @empty {\n <p class=\"ngx-record-editor__row-empty\">No rows yet.</p>\n }\n </div>\n }\n\n <!--\n Row drawer. Deliberately outside the tab panel: building a row's FormGroup inside the\n tab view races the tab's own initialisation (the reference hit this with FormArray).\n -->\n @if (inlineRowField(); as rowField) {\n <div class=\"ngx-record-editor__drawer\" data-testid=\"row-drawer\" [formGroup]=\"inlineRowForm!\">\n <div class=\"ngx-record-editor__rows-head\">\n <strong>{{ inlineRowIndex() === null ? 'Add' : 'Edit' }} {{ formatFieldLabel(rowField) }} row</strong>\n </div>\n\n <div class=\"ngx-form__panel\">\n @for (child of rowField.children ?? []; track child.id) {\n <ngx-dynamic-field\n [field]=\"child\"\n [control]=\"inlineRowForm!.get(child.id)!\"\n [config]=\"config\"\n [language]=\"language\"\n [readonly]=\"false\"\n [userRoles]=\"userRoles\"\n [style.gridColumn]=\"'span 12'\"\n />\n }\n </div>\n\n <div class=\"ngx-record-editor__section-bar\">\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-row\" (click)=\"cancelRow()\">Cancel</button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-row\" (click)=\"saveRow()\">Save row</button>\n </div>\n </div>\n }\n\n <!-- Main Dynamic Form -->\n <ngx-dynamic-form\n [config]=\"config\"\n [rules]=\"rules\"\n [initialData]=\"initialData\"\n [originalData]=\"originalBaseline()\"\n [userRoles]=\"userRoles\"\n [language]=\"language\"\n [readonly]=\"sectionReadOnly\"\n [readOnlyFields]=\"readOnlyFields\"\n [showInfoBanners]=\"false\"\n [loading]=\"loading\"\n [error]=\"error\"\n (formChange)=\"onFormChange($event)\"\n (formSubmit)=\"onFormSubmit($event)\"\n (formReset)=\"onFormReset()\"\n (activeTabChange)=\"onActiveTabChange($event)\"\n />\n</div>\n", styles: [".ngx-record-editor{display:flex;flex-direction:column;gap:16px;background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:20px;box-shadow:0 4px 6px -1px #0000000d}.ngx-record-editor__header{display:flex;align-items:center;justify-content:space-between;padding-bottom:16px;border-bottom:1px solid #f3f4f6}.ngx-record-editor__title-group{display:flex;align-items:center;gap:12px}.ngx-record-editor__avatar{width:48px;height:48px;border-radius:50%;object-fit:cover;background:#e0e7ff;color:#4f46e5;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px}.ngx-record-editor__title{font-size:20px;font-weight:700;color:#111827;margin:0}.ngx-record-editor__subtitle{font-size:13px;color:#6b7280}.ngx-record-editor__header-toggle{display:flex;align-items:center;gap:8px;font-size:13px;color:#374151}.ngx-record-editor__header-toggle-text{font-weight:600}.ngx-record-editor__banner{padding:10px 14px;border-radius:8px;font-size:13px;font-weight:500}.ngx-record-editor__banner--info{background:#eff6ff;border:1px solid #dbeafe;color:#1d4ed8;display:flex;align-items:center;justify-content:space-between;gap:12px}.ngx-record-editor__banner-dismiss{background:none;border:none;cursor:pointer;color:inherit;font-size:14px;line-height:1;padding:2px 6px;border-radius:4px}.ngx-record-editor__banner-dismiss:hover{background:#0000000f}.ngx-record-editor__banner--error{background:#fef2f2;border:1px solid #fecaca;color:#b91c1c}.ngx-record-editor__section-bar{display:flex;align-items:center;justify-content:flex-end;gap:8px}.ngx-record-editor__section-hint{margin-right:auto;font-size:12px;color:#6b7280}.ngx-record-editor__banner--warning{background:#fffbeb;border:1px solid #fef3c7;color:#b45309}.ngx-record-editor__rows{border:1px solid #f3f4f6;border-radius:8px;padding:12px 14px;display:flex;flex-direction:column;gap:6px}.ngx-record-editor__rows-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.ngx-record-editor__row{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;background:#f9fafb}.ngx-record-editor__row-text{flex:1;font-size:13px;overflow-wrap:anywhere}.ngx-record-editor__row-btn{background:none;border:1px solid #e5e7eb;border-radius:6px;cursor:pointer;font-size:12px;padding:2px 8px}.ngx-record-editor__row-empty{font-size:12px;color:#6b7280;margin:0}.ngx-record-editor__drawer{border:1px solid #dbeafe;background:#f8fafc;border-radius:8px;padding:14px;display:flex;flex-direction:column;gap:12px}.ngx-record-editor__summary-panel{background:#f9fafb;border:1px solid #f3f4f6;border-radius:8px;padding:12px 16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}.ngx-record-editor__summary-item{display:flex;flex-direction:column;cursor:pointer;padding:6px 10px;border-radius:6px;transition:background .15s}.ngx-record-editor__summary-item:hover{background:#eff6ff}.ngx-record-editor__summary-label{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:#6b7280;font-weight:600}.ngx-record-editor__summary-val{font-size:14px;font-weight:600;color:#1f2937}\n"] }]
|
|
460
|
+
}], propDecorators: { config: [{
|
|
461
|
+
type: Input
|
|
462
|
+
}], rules: [{
|
|
463
|
+
type: Input
|
|
464
|
+
}], initialData: [{
|
|
465
|
+
type: Input
|
|
466
|
+
}], userRoles: [{
|
|
467
|
+
type: Input
|
|
468
|
+
}], language: [{
|
|
469
|
+
type: Input
|
|
470
|
+
}], readonly: [{
|
|
471
|
+
type: Input
|
|
472
|
+
}], loading: [{
|
|
473
|
+
type: Input
|
|
474
|
+
}], error: [{
|
|
475
|
+
type: Input
|
|
476
|
+
}], isReadOnly: [{
|
|
477
|
+
type: Input
|
|
478
|
+
}], readOnlyFields: [{
|
|
479
|
+
type: Input
|
|
480
|
+
}], viewMode: [{
|
|
481
|
+
type: Input
|
|
482
|
+
}], formSubmit: [{
|
|
483
|
+
type: Output
|
|
484
|
+
}], formChange: [{
|
|
485
|
+
type: Output
|
|
486
|
+
}], formReset: [{
|
|
487
|
+
type: Output
|
|
488
|
+
}], sectionSave: [{
|
|
489
|
+
type: Output
|
|
490
|
+
}], dynamicFormComp: [{
|
|
491
|
+
type: ViewChild,
|
|
492
|
+
args: [DynamicFormComponent]
|
|
493
|
+
}] } });
|
|
494
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1yZWNvcmQtZm9ybS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2Zvcm0vZHluYW1pYy1yZWNvcmQtZm9ybS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9zcmMvbGliL2Zvcm0vZHluYW1pYy1yZWNvcmQtZm9ybS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUNULEtBQUssRUFDTCxNQUFNLEVBQ04sWUFBWSxFQUdaLE1BQU0sRUFDTixRQUFRLEVBQ1IsTUFBTSxFQUNOLFNBQVMsR0FDVixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUE4QixtQkFBbUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRWpGLE9BQU8sRUFBRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzdGLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLHNDQUFzQyxDQUFDOzs7QUFFOUU7OztHQUdHO0FBQ0gsTUFBTSxlQUFlLEdBQUcsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUM7QUFFbEQ7Ozs7R0FJRztBQW9NSCxNQUFNLE9BQU8sMEJBQTBCO0lBQzVCLE1BQU0sQ0FBb0I7SUFDMUIsS0FBSyxDQUFjO0lBQ25CLFdBQVcsQ0FBdUI7SUFDbEMsU0FBUyxHQUFhLEVBQUUsQ0FBQztJQUN6QixRQUFRLEdBQVcsSUFBSSxDQUFDO0lBQ3hCLFFBQVEsR0FBWSxLQUFLLENBQUM7SUFDMUIsT0FBTyxHQUFZLEtBQUssQ0FBQztJQUN6QixLQUFL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QUFDO2dCQUNQLE1BQU07WUFDUixDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7d0dBMWRVLDBCQUEwQjs0RkFBMUIsMEJBQTBCLDZmQTRCMUIsb0JBQW9CLHFFQzlQakMsdWhQQWtNQSwwa0dEaEtZLFlBQVksOEJBQUUsbUJBQW1CLDRVQUFFLG9CQUFvQiw2VEFBRSxxQkFBcUI7OzRGQWdNN0UsMEJBQTBCO2tCQW5NdEMsU0FBUzsrQkFDRSx5QkFBeUIsY0FDdkIsSUFBSSxXQUNQLENBQUMsWUFBWSxFQUFFLG1CQUFtQixFQUFFLG9CQUFvQixFQUFFLHFCQUFxQixDQUFDOzhCQWlNaEYsTUFBTTtzQkFBZCxLQUFLO2dCQUNHLEtBQUs7c0JBQWIsS0FBSztnQkFDRyxXQUFXO3NCQUFuQixLQUFLO2dCQUNHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBQ0csUUFBUTtzQkFBaEIsS0FBSztnQkFDRyxRQUFRO3NCQUFoQixLQUFLO2dCQUNHLE9BQU87c0JBQWYsS0FBSztnQkFDRyxLQUFLO3NCQUFiLEtBQUs7Z0JBRUcsVUFBVTtzQkFBbEIsS0FBSztnQkFFRyxjQUFjO3NCQUF0QixLQUFLO2dCQVFHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBRUksVUFBVTtzQkFBbkIsTUFBTTtnQkFDRyxVQUFVO3NCQUFuQixNQUFNO2dCQUNHLFNBQVM7c0JBQWxCLE1BQU07Z0JBRUcsV0FBVztzQkFBcEIsTUFBTTtnQkFFMEIsZUFBZTtzQkFBL0MsU0FBUzt1QkFBQyxvQkFBb0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDb21wb25lbnQsXG4gIElucHV0LFxuICBPdXRwdXQsXG4gIEV2ZW50RW1pdHRlcixcbiAgT25DaGFuZ2VzLFxuICBTaW1wbGVDaGFuZ2VzLFxuICBzaWduYWwsXG4gIGNvbXB1dGVkLFxuICBpbmplY3QsXG4gIFZpZXdDaGlsZCxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQWJzdHJhY3RDb250cm9sLCBGb3JtR3JvdXAsIFJlYWN0aXZlRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgdHlwZSB7IEVudGl0eUZvcm1Db25maWcsIEZvcm1SdWxlLCBOZXN0ZWRGaWVsZENvbmZpZywgTmVzdGVkVGFiQ29uZmlnIH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xuaW1wb3J0IHsgZmluZFRhYiwgZm9ybWF0RGlzcGxheVZhbHVlLCBnZXRUYWJEYXRhLCByZXNvbHZlTGFiZWwgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XG5pbXBvcnQgeyBEeW5hbWljRm9ybUNvbXBvbmVudCB9IGZyb20gJy4vZHluYW1pYy1mb3JtLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBEeW5hbWljRmllbGRDb21wb25lbnQgfSBmcm9tICcuL2R5bmFtaWMtZmllbGQvZHluYW1pYy1maWVsZC5jb21wb25lbnQnO1xuaW1wb3J0IHsgUnVsZXNFdmFsdWF0aW9uU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL3J1bGVzLWV2YWx1YXRpb24uc2VydmljZSc7XG5cbi8qKlxuICogRGlzdGluZ3Vpc2hlcyBcIm5vIHRhYiBob2xkcyB0aGlzIGZpZWxkXCIgZnJvbSBcInRoZSBmaWVsZCBob2xkcyB1bmRlZmluZWRcIiwgc28gdGhlIHRhYiB3YWxrXG4gKiBjYW4ga2VlcCBzZWFyY2hpbmcgaW4gdGhlIGZpcnN0IGNhc2UgYW5kIHN0b3AgaW4gdGhlIHNlY29uZC5cbiAqL1xuY29uc3QgRklFTERfTk9UX0ZPVU5EID0gU3ltYm9sKCdmaWVsZC1ub3QtZm91bmQnKTtcblxuLyoqXG4gKiBEeW5hbWljUmVjb3JkRm9ybUNvbXBvbmVudCDigJQgY29tcHJlaGVuc2l2ZSB0YWJiZWQgcmVjb3JkIHZpZXcgJiBlZGl0IGNvbXBvbmVudC5cbiAqIFN1cHBvcnRzIGNyb3NzLXRhYiBydWxlIGV2YWx1YXRpb24sIHByb2ZpbGUgaGVhZGVyLCBzdW1tYXJ5IGRyYXdlciAoc2hvd09uTWluaW1pemUpLFxuICogaW50ZXJhY3RpdmUgcXVpY2stanVtcCBsaW5rcywgYW5kIGJhc2VsaW5lIG1vZGlmaWNhdGlvbiB0cmFja2luZy5cbiAqL1xuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbmd4LWR5bmFtaWMtcmVjb3JkLWZvcm0nLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlLCBSZWFjdGl2ZUZvcm1zTW9kdWxlLCBEeW5hbWljRm9ybUNvbXBvbmVudCwgRHluYW1pY0ZpZWxkQ29tcG9uZW50XSxcbiAgdGVtcGxhdGVVcmw6ICcuL2R5bmFtaWMtcmVjb3JkLWZvcm0uY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZXM6IFtcbiAgICBgXG4gICAgICAubmd4LXJlY29yZC1lZGl0b3Ige1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICAgICAgICBnYXA6IDE2cHg7XG4gICAgICAgIGJhY2tncm91bmQ6ICNmZmZmZmY7XG4gICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNlNWU3ZWI7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDEycHg7XG4gICAgICAgIHBhZGRpbmc6IDIwcHg7XG4gICAgICAgIGJveC1zaGFkb3c6IDAgNHB4IDZweCAtMXB4IHJnYmEoMCwgMCwgMCwgMC4wNSk7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX2hlYWRlciB7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjtcbiAgICAgICAgcGFkZGluZy1ib3R0b206IDE2cHg7XG4gICAgICAgIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZjNmNGY2O1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX190aXRsZS1ncm91cCB7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICAgIGdhcDogMTJweDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fYXZhdGFyIHtcbiAgICAgICAgd2lkdGg6IDQ4cHg7XG4gICAgICAgIGhlaWdodDogNDhweDtcbiAgICAgICAgYm9yZGVyLXJhZGl1czogNTAlO1xuICAgICAgICBvYmplY3QtZml0OiBjb3ZlcjtcbiAgICAgICAgYmFja2dyb3VuZDogI2UwZTdmZjtcbiAgICAgICAgY29sb3I6ICM0ZjQ2ZTU7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICAgIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICAgICAgICBmb250LXdlaWdodDogNzAwO1xuICAgICAgICBmb250LXNpemU6IDE4cHg7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3RpdGxlIHtcbiAgICAgICAgZm9udC1zaXplOiAyMHB4O1xuICAgICAgICBmb250LXdlaWdodDogNzAwO1xuICAgICAgICBjb2xvcjogIzExMTgyNztcbiAgICAgICAgbWFyZ2luOiAwO1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zdWJ0aXRsZSB7XG4gICAgICAgIGZvbnQtc2l6ZTogMTNweDtcbiAgICAgICAgY29sb3I6ICM2YjcyODA7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX2hlYWRlci10b2dnbGUge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xuICAgICAgICBnYXA6IDhweDtcbiAgICAgICAgZm9udC1zaXplOiAxM3B4O1xuICAgICAgICBjb2xvcjogIzM3NDE1MTtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9faGVhZGVyLXRvZ2dsZS10ZXh0IHtcbiAgICAgICAgZm9udC13ZWlnaHQ6IDYwMDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyIHtcbiAgICAgICAgcGFkZGluZzogMTBweCAxNHB4O1xuICAgICAgICBib3JkZXItcmFkaXVzOiA4cHg7XG4gICAgICAgIGZvbnQtc2l6ZTogMTNweDtcbiAgICAgICAgZm9udC13ZWlnaHQ6IDUwMDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLS1pbmZvIHtcbiAgICAgICAgYmFja2dyb3VuZDogI2VmZjZmZjtcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2RiZWFmZTtcbiAgICAgICAgY29sb3I6ICMxZDRlZDg7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gICAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjtcbiAgICAgICAgZ2FwOiAxMnB4O1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXItZGlzbWlzcyB7XG4gICAgICAgIGJhY2tncm91bmQ6IG5vbmU7XG4gICAgICAgIGJvcmRlcjogbm9uZTtcbiAgICAgICAgY3Vyc29yOiBwb2ludGVyO1xuICAgICAgICBjb2xvcjogaW5oZXJpdDtcbiAgICAgICAgZm9udC1zaXplOiAxNHB4O1xuICAgICAgICBsaW5lLWhlaWdodDogMTtcbiAgICAgICAgcGFkZGluZzogMnB4IDZweDtcbiAgICAgICAgYm9yZGVyLXJhZGl1czogNHB4O1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXItZGlzbWlzczpob3ZlciB7XG4gICAgICAgIGJhY2tncm91bmQ6IHJnYmEoMCwgMCwgMCwgMC4wNik7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lci0tZXJyb3Ige1xuICAgICAgICBiYWNrZ3JvdW5kOiAjZmVmMmYyO1xuICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjZmVjYWNhO1xuICAgICAgICBjb2xvcjogI2I5MWMxYztcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fc2VjdGlvbi1iYXIge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xuICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IGZsZXgtZW5kO1xuICAgICAgICBnYXA6IDhweDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fc2VjdGlvbi1oaW50IHtcbiAgICAgICAgbWFyZ2luLXJpZ2h0OiBhdXRvO1xuICAgICAgICBmb250LXNpemU6IDEycHg7XG4gICAgICAgIGNvbG9yOiAjNmI3MjgwO1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXItLXdhcm5pbmcge1xuICAgICAgICBiYWNrZ3JvdW5kOiAjZmZmYmViO1xuICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjZmVmM2M3O1xuICAgICAgICBjb2xvcjogI2I0NTMwOTtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fcm93cyB7XG4gICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNmM2Y0ZjY7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDhweDtcbiAgICAgICAgcGFkZGluZzogMTJweCAxNHB4O1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICAgICAgICBnYXA6IDZweDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fcm93cy1oZWFkIHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xuICAgICAgICBnYXA6IDhweDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fcm93IHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICAgICAgZ2FwOiA4cHg7XG4gICAgICAgIHBhZGRpbmc6IDZweCA4cHg7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDZweDtcbiAgICAgICAgYmFja2dyb3VuZDogI2Y5ZmFmYjtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fcm93LXRleHQge1xuICAgICAgICBmbGV4OiAxO1xuICAgICAgICBmb250LXNpemU6IDEzcHg7XG4gICAgICAgIG92ZXJmbG93LXdyYXA6IGFueXdoZXJlO1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19yb3ctYnRuIHtcbiAgICAgICAgYmFja2dyb3VuZDogbm9uZTtcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2U1ZTdlYjtcbiAgICAgICAgYm9yZGVyLXJhZGl1czogNnB4O1xuICAgICAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgICAgIGZvbnQtc2l6ZTogMTJweDtcbiAgICAgICAgcGFkZGluZzogMnB4IDhweDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fcm93LWVtcHR5IHtcbiAgICAgICAgZm9udC1zaXplOiAxMnB4O1xuICAgICAgICBjb2xvcjogIzZiNzI4MDtcbiAgICAgICAgbWFyZ2luOiAwO1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19kcmF3ZXIge1xuICAgICAgICBib3JkZXI6IDFweCBzb2xpZCAjZGJlYWZlO1xuICAgICAgICBiYWNrZ3JvdW5kOiAjZjhmYWZjO1xuICAgICAgICBib3JkZXItcmFkaXVzOiA4cHg7XG4gICAgICAgIHBhZGRpbmc6IDE0cHg7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gICAgICAgIGdhcDogMTJweDtcbiAgICAgIH1cbiAgICAgIC5uZ3gtcmVjb3JkLWVkaXRvcl9fc3VtbWFyeS1wYW5lbCB7XG4gICAgICAgIGJhY2tncm91bmQ6ICNmOWZhZmI7XG4gICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNmM2Y0ZjY7XG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDhweDtcbiAgICAgICAgcGFkZGluZzogMTJweCAxNnB4O1xuICAgICAgICBkaXNwbGF5OiBncmlkO1xuICAgICAgICBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IHJlcGVhdChhdXRvLWZpdCwgbWlubWF4KDE4MHB4LCAxZnIpKTtcbiAgICAgICAgZ2FwOiAxMnB4O1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zdW1tYXJ5LWl0ZW0ge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICAgICAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgICAgIHBhZGRpbmc6IDZweCAxMHB4O1xuICAgICAgICBib3JkZXItcmFkaXVzOiA2cHg7XG4gICAgICAgIHRyYW5zaXRpb246IGJhY2tncm91bmQgMC4xNXM7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3N1bW1hcnktaXRlbTpob3ZlciB7XG4gICAgICAgIGJhY2tncm91bmQ6ICNlZmY2ZmY7XG4gICAgICB9XG4gICAgICAubmd4LXJlY29yZC1lZGl0b3JfX3N1bW1hcnktbGFiZWwge1xuICAgICAgICBmb250LXNpemU6IDExcHg7XG4gICAgICAgIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7XG4gICAgICAgIGxldHRlci1zcGFjaW5nOiAwLjA1ZW07XG4gICAgICAgIGNvbG9yOiAjNmI3MjgwO1xuICAgICAgICBmb250LXdlaWdodDogNjAwO1xuICAgICAgfVxuICAgICAgLm5neC1yZWNvcmQtZWRpdG9yX19zdW1tYXJ5LXZhbCB7XG4gICAgICAgIGZvbnQtc2l6ZTogMTRweDtcbiAgICAgICAgZm9udC13ZWlnaHQ6IDYwMDtcbiAgICAgICAgY29sb3I6ICMxZjI5Mzc7XG4gICAgICB9XG4gICAgYCxcbiAgXSxcbn0pXG5leHBvcnQgY2xhc3MgRHluYW1pY1JlY29yZEZvcm1Db21wb25lbnQgaW1wbGVtZW50cyBPbkNoYW5nZXMge1xuICBASW5wdXQoKSBjb25maWchOiBFbnRpdHlGb3JtQ29uZmlnO1xuICBASW5wdXQoKSBydWxlcz86IEZvcm1SdWxlW107XG4gIEBJbnB1dCgpIGluaXRpYWxEYXRhPzogUmVjb3JkPHN0cmluZywgYW55PjtcbiAgQElucHV0KCkgdXNlclJvbGVzOiBzdHJpbmdbXSA9IFtdO1xuICBASW5wdXQoKSBsYW5ndWFnZTogc3RyaW5nID0gJ2VuJztcbiAgQElucHV0KCkgcmVhZG9ubHk6IGJvb2xlYW4gPSBmYWxzZTtcbiAgQElucHV0KCkgbG9hZGluZzogYm9vbGVhbiA9IGZhbHNlO1xuICBASW5wdXQoKSBlcnJvcjogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIC8qKiBXaG9sZSByZWNvcmQgcmVhZC1vbmx5LCByZWdhcmRsZXNzIG9mIFJCQUMuIEVxdWl2YWxlbnQgdG8gdGhlIHJlZmVyZW5jZSdzIGBpc1JlYWRPbmx5YC4gKi9cbiAgQElucHV0KCkgaXNSZWFkT25seTogYm9vbGVhbiA9IGZhbHNlO1xuICAvKiogU3BlY2lmaWMgZmllbGQgaWRzIGZvcmNlZCByZWFkLW9ubHkgd2hpbGUgdGhlIHJlc3Qgb2YgdGhlIHJlY29yZCBzdGF5cyBlZGl0YWJsZS4gKi9cbiAgQElucHV0KCkgcmVhZE9ubHlGaWVsZHM6IHN0cmluZ1tdID0gW107XG4gIC8qKlxuICAgKiBPcGVuIHRoZSByZWNvcmQgcmVhZC1vbmx5LCB3aXRoIGEgcGVyLXRhYiBcIkVkaXQgc2VjdGlvblwiIGZsb3cg4oCUIG9uZSB0YWIgaXMgZWRpdGVkIGFuZFxuICAgKiB2YWxpZGF0ZWQgYXQgYSB0aW1lLiBUaGlzIGlzIHRoZSBmcmFtZXdvcmsncyBgRW50aXR5UmVjb3JkQ29tcG9uZW50YCBtb2RlbCBhbmQgdGhlXG4gICAqIGRlZmF1bHQgaGVyZS5cbiAgICpcbiAgICogU2V0IGZhbHNlIGZvciBhIGRpcmVjdGx5IGVkaXRhYmxlIHJlY29yZCB3aXRoIG5vIHZpZXcvZWRpdCBkaXN0aW5jdGlvbi5cbiAgICovXG4gIEBJbnB1dCgpIHZpZXdNb2RlOiBib29sZWFuID0gdHJ1ZTtcblxuICBAT3V0cHV0KCkgZm9ybVN1Ym1pdCA9IG5ldyBFdmVudEVtaXR0ZXI8UmVjb3JkPHN0cmluZywgYW55Pj4oKTtcbiAgQE91dHB1dCgpIGZvcm1DaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPFJlY29yZDxzdHJpbmcsIGFueT4+KCk7XG4gIEBPdXRwdXQoKSBmb3JtUmVzZXQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG4gIC8qKiBPbmUgdGFiIHNlY3Rpb24gd2FzIHNhdmVkLiBDYXJyaWVzIHRoZSB3aG9sZSByZWNvcmQsIHBsdXMgd2hpY2ggdGFiIHdhcyBlZGl0ZWQuICovXG4gIEBPdXRwdXQoKSBzZWN0aW9uU2F2ZSA9IG5ldyBFdmVudEVtaXR0ZXI8eyB0YWJJZDogc3RyaW5nOyByZWNvcmQ6IFJlY29yZDxzdHJpbmcsIGFueT4gfT4oKTtcblxuICBAVmlld0NoaWxkKER5bmFtaWNGb3JtQ29tcG9uZW50KSBkeW5hbWljRm9ybUNvbXA/OiBEeW5hbWljRm9ybUNvbXBvbmVudDtcblxuICBwcml2YXRlIHJlYWRvbmx5IHJ1bGVzRXZhbHVhdGlvbiA9IGluamVjdChSdWxlc0V2YWx1YXRpb25TZXJ2aWNlKTtcblxuICByZWFkb25seSBjdXJyZW50RGF0YSA9IHNpZ25hbDxSZWNvcmQ8c3RyaW5nLCBhbnk+Pih7fSk7XG4gIHJlYWRvbmx5IG9yaWdpbmFsQmFzZWxpbmUgPSBzaWduYWw8UmVjb3JkPHN0cmluZywgYW55Pj4oe30pO1xuXG4gIC8qKlxuICAgKiBJbmZvIGJhbm5lcnMgdGhlIHVzZXIgaGFzIGRpc21pc3NlZCwgYnkgZmllbGQgaWQuXG4gICAqXG4gICAqIFRoZSByZWZlcmVuY2UncyBjb250cmFjdDogYW4gaW5mbyBiYW5uZXIgcGVyc2lzdHMgdW50aWwgZGlzbWlzc2VkLCAqKm5vdCoqIHVudGlsIHRoZVxuICAgKiB2YWx1ZSBjaGFuZ2VzLiBEaXNtaXNzYWwgaXMga2V5ZWQgYnkgZmllbGQgc28gcmUtdHJpZ2dlcmluZyBhIGRpZmZlcmVudCBmaWVsZCdzIHJ1bGVcbiAgICogc3RpbGwgc2hvd3MuIFJlLWFybWVkIHdoZW4gYSBkaWZmZXJlbnQgcmVjb3JkIGlzIGxvYWRlZC5cbiAgICovXG4gIHByaXZhdGUgcmVhZG9ubHkgZGlzbWlzc2VkID0gc2lnbmFsPFJlYWRvbmx5U2V0PHN0cmluZz4+KG5ldyBTZXQ8c3RyaW5nPigpKTtcblxuICAvKipcbiAgICogSW5mbyBiYW5uZXJzIGZyb20gdGhlIHJ1bGVzIGVuZ2luZSB0aGF0IGFyZSBzdGlsbCBzaG93aW5nLlxuICAgKlxuICAgKiBBIGdldHRlciwgbm90IGEgYGNvbXB1dGVkYDogdGhlIHNvdXJjZSBpcyBgZHluYW1pY0Zvcm1Db21wYCwgYSBWaWV3Q2hpbGQuIEEgY29tcHV0ZWRcbiAgICogZXZhbHVhdGVkIGJlZm9yZSB0aGUgdmlldyBpbml0aWFsaXNlcyB3b3VsZCBjYXB0dXJlIG5vIGRlcGVuZGVuY3kgb24gdGhlIGNoaWxkJ3NcbiAgICogYHJ1bGVSZXN1bHRgIHNpZ25hbCBhbmQgdGhlbiBuZXZlciByZWNvbXB1dGUuIFJlYWQgcGVyIGNoYW5nZS1kZXRlY3Rpb24gcGFzcyBpbnN0ZWFkLlxuICAgKi9cbiAgZ2V0IGluZm9CYW5uZXJzKCk6IHsgZmllbGRJZDogc3RyaW5nOyBtZXNzYWdlOiBzdHJpbmcgfVtdIHtcbiAgICBjb25zdCByZXN1bHQgPSB0aGlzLmR5bmFtaWNGb3JtQ29tcD8ucnVsZVJlc3VsdCgpO1xuICAgIGNvbnN0IGdvbmUgPSB0aGlzLmRpc21pc3NlZCgpO1xuICAgIHJldHVybiBPYmplY3QuZW50cmllcyhyZXN1bHQ/LmluZm9CYW5uZXJzID8/IHt9KVxuICAgICAgLmZpbHRlcigoW2ZpZWxkSWRdKSA9PiAhZ29uZS5oYXMoZmllbGRJZCkpXG4gICAgICAubWFwKChbZmllbGRJZCwgbWVzc2FnZV0pID0+ICh7IGZpZWxkSWQsIG1lc3NhZ2U6IFN0cmluZyhtZXNzYWdlKSB9KSk7XG4gIH1cblxuICBkaXNtaXNzSW5mb0Jhbm5lcihmaWVsZElkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmRpc21pc3NlZC5zZXQobmV3IFNldCh0aGlzLmRpc21pc3NlZCgpKS5hZGQoZmllbGRJZCkpO1xuICB9XG5cbiAgLyoqIFRydWUgd2hlbiBub3RoaW5nIGluIHRoZSByZWNvcmQgbWF5IGJlIGVkaXRlZC4gKi9cbiAgZ2V0IHJlY29yZFJlYWRPbmx5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnJlYWRvbmx5IHx8IHRoaXMuaXNSZWFkT25seTtcbiAgfVxuXG4gIC8vIOKUgOKUgOKUgCBJbmxpbmUgYXJyYXktcm93IGVkaXRpbmcg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqXG4gICAqIEEgcm93IG9mIGFuIGBhcnJheWAgZmllbGQgaXMgZWRpdGVkIGluIGEgZHJhd2VyLCBub3QgaW5saW5lIGluIHRoZSB0YWIgcGFuZWwuXG4gICAqXG4gICAqIFRoZSBkcmF3ZXIgaXMgcmVuZGVyZWQgb3V0c2lkZSB0aGUgdGFiIGNvbnRhaW5lciBvbiBwdXJwb3NlOiB0aGUgcmVmZXJlbmNlIGZvdW5kIHRoYXRcbiAgICogYnVpbGRpbmcgYSByb3cncyBGb3JtR3JvdXAgaW5zaWRlIHRoZSB0YWIgdmlldyByYWNlcyB0aGUgdGFiJ3Mgb3duIGluaXRpYWxpc2F0aW9uLCBzb1xuICAgKiB0aGUgcm93IGZvcm0gaXMga2VwdCBkZXRhY2hlZCBhbmQgb25seSBwdXNoZWQgaW50byB0aGUgRm9ybUFycmF5IG9uIHNhdmUuXG4gICAqL1xuICByZWFkb25seSBpbmxpbmVSb3dGaWVsZCA9IHNpZ25hbDxOZXN0ZWRGaWVsZENvbmZpZyB8IG51bGw+KG51bGwpO1xuICByZWFkb25seSBpbmxpbmVSb3dJbmRleCA9IHNpZ25hbDxudW1iZXIgfCBudWxsPihudWxsKTtcbiAgaW5saW5lUm93Rm9ybTogRm9ybUdyb3VwIHwgbnVsbCA9IG51bGw7XG5cbiAgLyoqIGBhcnJheWAgZmllbGRzIG9uIHRoZSB0YWIgY3VycmVudGx5IHNob3duLCBzbyB0aGUgdGVtcGxhdGUgY2FuIGxpc3QgdGhlaXIgcm93cy4gKi9cbiAgZ2V0IGFycmF5RmllbGRzRm9yQWN0aXZlVGFiKCk6IE5lc3RlZEZpZWxkQ29uZmlnW10ge1xuICAgIGNvbnN0IHRhYklkID0gdGhpcy5hY3RpdmVUYWJJZDtcbiAgICBjb25zdCB0YWIgPSB0YWJJZCA/IGZpbmRUYWIodGhpcy5jb25maWc/LnRhYnMsIHRhYklkKSA6IG51bGw7XG4gICAgcmV0dXJuICh0YWI/LmZpZWxkcyA/PyBbXSkuZmlsdGVyKGYgPT4gZi50eXBlID09PSAnYXJyYXknKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSb3dzIGN1cnJlbnRseSBoZWxkIGJ5IGFuIGBhcnJheWAgZmllbGQsIHJlYWQgZnJvbSB0aGUgcmVjb3JkIHZhbHVlLlxuICAgKlxuICAgKiBEZWxpYmVyYXRlbHkgbm90IHJlYWQgb2ZmIHRoZSBjaGlsZCdzIEZvcm1BcnJheTogdGhhdCBjb250cm9sIG9ubHkgZXhpc3RzIG9uY2UgdGhlXG4gICAqIGNoaWxkIGhhcyBpbml0aWFsaXNlZCwgc28gcmVuZGVyaW5nIGZyb20gaXQgY2hhbmdlZCB2YWx1ZSBtaWQtcGFzcy4gYGN1cnJlbnREYXRhYCBpcyBhXG4gICAqIHNpZ25hbCBzZWVkZWQgZnJvbSBgaW5pdGlhbERhdGFgIGFuZCBrZXB0IGN1cnJlbnQgYnkgdGhlIGNoaWxkJ3MgYGZvcm1DaGFuZ2VgLlxuICAgKi9cbiAgcm93c09mKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+W10ge1xuICAgIGNvbnN0IHRhYklkID0gdGhpcy5hY3RpdmVUYWJJZDtcbiAgICBjb25zdCB0YWJEYXRhID0gdGFiSWQgPyBnZXRUYWJEYXRhKHRhYklkLCB0aGlzLmN1cnJlbnREYXRhKCksIHRoaXMuY29uZmlnKSA6IG51bGw7XG4gICAgY29uc3Qgcm93cyA9ICh0YWJEYXRhIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgbnVsbCk/LltmaWVsZC5pZF07XG4gICAgcmV0dXJuIEFycmF5LmlzQXJyYXkocm93cykgPyAocm93cyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPltdKSA6IFtdO1xuICB9XG5cbiAgLyoqIE9uZSByb3cgcmVuZGVyZWQgYXMgdGV4dCwgc28gdGhlIGxpc3QgaXMgcmVhZGFibGUgd2l0aG91dCBvcGVuaW5nIHRoZSBkcmF3ZXIuICovXG4gIHJvd1N1bW1hcnkoZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnLCByb3c6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KTogc3RyaW5nIHtcbiAgICBjb25zdCB2YWx1ZSA9IHJvdyA/PyB7fTtcbiAgICBjb25zdCBwYXJ0cyA9IChmaWVsZC5jaGlsZHJlbiA/PyBbXSlcbiAgICAgIC5tYXAoY2hpbGQgPT4gdmFsdWVbY2hpbGQuaWRdKVxuICAgICAgLmZpbHRlcih2ID0+IHYgIT09IG51bGwgJiYgdiAhPT0gdW5kZWZpbmVkICYmIHYgIT09ICcnKVxuICAgICAgLm1hcCh2ID0+ICh0eXBlb2YgdiA9PT0gJ29iamVjdCcgPyBKU09OLnN0cmluZ2lmeSh2KSA6IFN0cmluZyh2KSkpO1xuICAgIHJldHVybiBwYXJ0cy5sZW5ndGggPyBwYXJ0cy5qb2luKCcgwrcgJykgOiAnKGVtcHR5KSc7XG4gIH1cblxuICBvcGVuQWRkUm93KGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNlY3Rpb25SZWFkT25seSkgcmV0dXJuO1xuICAgIHRoaXMuaW5saW5lUm93RmllbGQuc2V0KGZpZWxkKTtcbiAgICB0aGlzLmlubGluZVJvd0luZGV4LnNldChudWxsKTtcbiAgICB0aGlzLmlubGluZVJvd0Zvcm0gPSB0aGlzLmR5bmFtaWNGb3JtQ29tcD8uY3JlYXRlQXJyYXlSb3coZmllbGQpIGFzIEZvcm1Hcm91cDtcbiAgfVxuXG4gIG9wZW5FZGl0Um93KGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZywgaW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgIGlmICh0aGlzLnNlY3Rpb25SZWFkT25seSkgcmV0dXJuO1xuICAgIHRoaXMuaW5saW5lUm93RmllbGQuc2V0KGZpZWxkKTtcbiAgICB0aGlzLmlubGluZVJvd0luZGV4LnNldChpbmRleCk7XG4gICAgdGhpcy5pbmxpbmVSb3dGb3JtID0gdGhpcy5keW5hbWljRm9ybUNvbXA/LmNyZWF0ZUFycmF5Um93KGZpZWxkLCB0aGlzLnJvd3NPZihmaWVsZClbaW5kZXhdKSBhcyBGb3JtR3JvdXA7XG4gIH1cblxuICBjYW5jZWxSb3coKTogdm9pZCB7XG4gICAgdGhpcy5pbmxpbmVSb3dGaWVsZC5zZXQobnVsbCk7XG4gICAgdGhpcy5pbmxpbmVSb3dJbmRleC5zZXQobnVsbCk7XG4gICAgdGhpcy5pbmxpbmVSb3dGb3JtID0gbnVsbDtcbiAgfVxuXG4gIC8qKiBDb21taXQgdGhlIGRyYXdlciBpbnRvIHRoZSBGb3JtQXJyYXkuIFJ1bGVzIHJlLWV2YWx1YXRlIG9mZiB0aGUgcmVzdWx0aW5nIHZhbHVlIGNoYW5nZS4gKi9cbiAgc2F2ZVJvdygpOiB2b2lkIHtcbiAgICBjb25zdCBmaWVsZCA9IHRoaXMuaW5saW5lUm93RmllbGQoKTtcbiAgICBjb25zdCBmb3JtID0gdGhpcy5keW5hbWljRm9ybUNvbXA7XG4gICAgaWYgKCFmaWVsZCB8fCAhZm9ybSB8fCAhdGhpcy5pbmxpbmVSb3dGb3JtKSByZXR1cm47XG5cbiAgICBpZiAodGhpcy5pbmxpbmVSb3dGb3JtLmludmFsaWQpIHtcbiAgICAgIHRoaXMuaW5saW5lUm93Rm9ybS5tYXJrQWxsQXNUb3VjaGVkKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgYXJyYXkgPSBmb3JtLmdldEFycmF5Q29udHJvbChmaWVsZC5pZCwgdGhpcy5hY3RpdmVUYWJJZCA/PyB1bmRlZmluZWQpO1xuICAgIGlmICghYXJyYXkpIHJldHVybjtcblxuICAgIGNvbnN0IGluZGV4ID0gdGhpcy5pbmxpbmVSb3dJbmRleCgpO1xuICAgIGlmIChpbmRleCA9PT0gbnVsbCkgYXJyYXkucHVzaChmb3JtLmNyZWF0ZUFycmF5Um93KGZpZWxkLCB0aGlzLmlubGluZVJvd0Zvcm0udmFsdWUpKTtcbiAgICBlbHNlIGFycmF5LmF0KGluZGV4KS5wYXRjaFZhbHVlKHRoaXMuaW5saW5lUm93Rm9ybS52YWx1ZSk7XG5cbiAgICBhcnJheS51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KCk7XG4gICAgdGhpcy5jdXJyZW50RGF0YS5zZXQoZm9ybS5leHRyYWN0UmVjb3JkKCkpO1xuICAgIHRoaXMuY2FuY2VsUm93KCk7XG4gIH1cblxuICBkZWxldGVSb3coZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnLCBpbmRleDogbnVtYmVyKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuc2VjdGlvblJlYWRPbmx5KSByZXR1cm47XG4gICAgY29uc3QgYXJyYXkgPSB0aGlzLmR5bmFtaWNGb3JtQ29tcD8uZ2V0QXJyYXlDb250cm9sKGZpZWxkLmlkLCB0aGlzLmFjdGl2ZVRhYklkID8/IHVuZGVmaW5lZCk7XG4gICAgYXJyYXk/LnJlbW92ZUF0KGluZGV4KTtcbiAgICBhcnJheT8udXBkYXRlVmFsdWVBbmRWYWxpZGl0eSgpO1xuICAgIGlmICh0aGlzLmR5bmFtaWNGb3JtQ29tcCkgdGhpcy5jdXJyZW50RGF0YS5zZXQodGhpcy5keW5hbWljRm9ybUNvbXAuZXh0cmFjdFJlY29yZCgpKTtcbiAgICBpZiAodGhpcy5pbmxpbmVSb3dJbmRleCgpID09PSBpbmRleCkgdGhpcy5jYW5jZWxSb3coKTtcbiAgfVxuXG4gIC8vIOKUgOKUgOKUgCBQZXItdGFiIHNlY3Rpb24gZWRpdGluZyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKipcbiAgICogVGhlIHRhYiBjdXJyZW50bHkgb3BlbiBmb3IgZWRpdGluZywgb3IgbnVsbCB3aGVuIHRoZSByZWNvcmQgaXMgYmVpbmcgdmlld2VkLlxuICAgKlxuICAgKiBUaGUgcmVmZXJlbmNlIGVkaXRzIG9uZSB0YWIgc2VjdGlvbiBhdCBhIHRpbWUgcmF0aGVyIHRoYW4gdGhlIHdob2xlIHJlY29yZCwgYW5kXG4gICAqIHZhbGlkYXRlcyBvbmx5IHRoYXQgc2VjdGlvbiBvbiBzYXZlIOKAlCBhIHJlcXVpcmVkIGZpZWxkIG9uIGFuIHVudG91Y2hlZCB0YWIgbXVzdCBub3RcbiAgICogYmxvY2sgc2F2aW5nIHRoZSB0YWIgeW91IGFyZSBhY3R1YWxseSB3b3JraW5nIG9uLlxuICAgKi9cbiAgcmVhZG9ubHkgZWRpdGluZ1RhYklkID0gc2lnbmFsPHN0cmluZyB8IG51bGw+KG51bGwpO1xuXG4gIC8qKlxuICAgKiBUaGUgdGFiIHRoZSBpbm5lciBmb3JtIGlzIHNob3dpbmcuXG4gICAqXG4gICAqIFRyYWNrZWQgaGVyZSByYXRoZXIgdGhhbiByZWFkIG9mZiB0aGUgVmlld0NoaWxkLiBUaGUgY2hpbGQgcmVzb2x2ZXMgaXRzIGZpcnN0IHRhYiBhc1xuICAgKiBwYXJ0IG9mIGl0cyBvd24gaW5pdGlhbGlzYXRpb24sIHNvIHJlYWRpbmcgaXQgZHVyaW5nIHRoaXMgY29tcG9uZW50J3MgcmVuZGVyIGNoYW5nZWRcbiAgICogdmFsdWUgbWlkLXBhc3Mg4oCUIGFuIEV4cHJlc3Npb25DaGFuZ2VkQWZ0ZXJJdEhhc0JlZW5DaGVja2VkIGVycm9yLiBTZWVkZWQgZnJvbSB0aGVcbiAgICogY29uZmlnIGFuZCBrZXB0IGluIHN0ZXAgYnkgdGhlIGNoaWxkJ3MgYGFjdGl2ZVRhYkNoYW5nZWAgb3V0cHV0LlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBhY3RpdmVUYWJJZFNpZyA9IHNpZ25hbDxzdHJpbmcgfCBudWxsPihudWxsKTtcblxuICBnZXQgYWN0aXZlVGFiSWQoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuYWN0aXZlVGFiSWRTaWcoKTtcbiAgfVxuXG4gIG9uQWN0aXZlVGFiQ2hhbmdlKHRhYklkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5hY3RpdmVUYWJJZFNpZygpID09PSB0YWJJZCkgcmV0dXJuO1xuICAgIHRoaXMuYWN0aXZlVGFiSWRTaWcuc2V0KHRhYklkKTtcbiAgICAvLyBFZGl0aW5nIGRvZXMgbm90IGZvbGxvdyB0aGUgdXNlciB0byBhbm90aGVyIHRhYi5cbiAgICB0aGlzLmVkaXRpbmdUYWJJZC5zZXQobnVsbCk7XG4gICAgdGhpcy5jYW5jZWxSb3coKTtcbiAgfVxuXG4gIC8qKiBGaXJzdCB0YWIgYSB1c2VyIGNhbiBzZWUsIHVzZWQgdG8gc2VlZCB0aGUgYWN0aXZlIHRhYiBiZWZvcmUgdGhlIGNoaWxkIHJlcG9ydHMgb25lLiAqL1xuICBwcml2YXRlIGZpcnN0VmlzaWJsZVRhYklkKCk6IHN0cmluZyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmNvbmZpZz8udGFicz8uZmluZCh0ID0+IHQudmlzaWJpbGl0eSAhPT0gZmFsc2UpPy5pZCA/PyBudWxsO1xuICB9XG5cbiAgLyoqIEVycm9ycyBmcm9tIHRoZSBsYXN0IHNhdmUgYXR0ZW1wdCwgYnkgZmllbGQgaWQuICovXG4gIHJlYWRvbmx5IHNlY3Rpb25FcnJvcnMgPSBzaWduYWw8UmVjb3JkPHN0cmluZywgc3RyaW5nPj4oe30pO1xuXG4gIC8qKiBUZW1wbGF0ZSBhY2Nlc3MgdG8gYE9iamVjdC5rZXlzYCBmb3IgaXRlcmF0aW5nIHRoZSBlcnJvciBtYXAuICovXG4gIHByb3RlY3RlZCByZWFkb25seSBPYmplY3QgPSBPYmplY3Q7XG5cbiAgZ2V0IGlzRWRpdGluZ0FjdGl2ZVRhYigpOiBib29sZWFuIHtcbiAgICAvLyBXaXRob3V0IHZpZXcgbW9kZSB0aGVyZSBpcyBubyB2aWV3L2VkaXQgZGlzdGluY3Rpb24g4oCUIGV2ZXJ5dGhpbmcgaXMgYWx3YXlzIGVkaXRhYmxlLlxuICAgIGlmICghdGhpcy52aWV3TW9kZSkgcmV0dXJuIHRydWU7XG4gICAgcmV0dXJuICEhdGhpcy5hY3RpdmVUYWJJZCAmJiB0aGlzLmVkaXRpbmdUYWJJZCgpID09PSB0aGlzLmFjdGl2ZVRhYklkO1xuICB9XG5cbiAgLyoqIEZpZWxkcyByZW5kZXIgcmVhZC1vbmx5IHVubGVzcyB0aGVpciBvd24gdGFiIGlzIHRoZSBvbmUgYmVpbmcgZWRpdGVkLiAqL1xuICBnZXQgc2VjdGlvblJlYWRPbmx5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnJlY29yZFJlYWRPbmx5IHx8ICF0aGlzLmlzRWRpdGluZ0FjdGl2ZVRhYjtcbiAgfVxuXG4gIGVkaXRTZWN0aW9uKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy52aWV3TW9kZSB8fCB0aGlzLnJlY29yZFJlYWRPbmx5IHx8ICF0aGlzLmFjdGl2ZVRhYklkKSByZXR1cm47XG4gICAgdGhpcy5zZWN0aW9uRXJyb3JzLnNldCh7fSk7XG4gICAgdGhpcy5lZGl0aW5nVGFiSWQuc2V0KHRoaXMuYWN0aXZlVGFiSWQpO1xuICB9XG5cbiAgLyoqIERpc2NhcmQgdGhpcyBzZWN0aW9uJ3MgZWRpdHMsIHJlc3RvcmluZyBlYWNoIGZpZWxkIGZyb20gdGhlIHNlc3Npb24gYmFzZWxpbmUuICovXG4gIGNhbmNlbFNlY3Rpb24oKTogdm9pZCB7XG4gICAgY29uc3QgdGFiSWQgPSB0aGlzLmVkaXRpbmdUYWJJZCgpO1xuICAgIGNvbnN0IGZvcm0gPSB0aGlzLmR5bmFtaWNGb3JtQ29tcDtcbiAgICBpZiAodGFiSWQgJiYgZm9ybSkge1xuICAgICAgY29uc3QgdGFiID0gZmluZFRhYih0aGlzLmNvbmZpZz8udGFicywgdGFiSWQpO1xuICAgICAgY29uc3QgYmFzZWxpbmVUYWIgPSAoZ2V0VGFiRGF0YSh0YWJJZCwgdGhpcy5vcmlnaW5hbEJhc2VsaW5lKCksIHRoaXMuY29uZmlnKSA/PyB7fSkgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgICBmb3IgKGNvbnN0IGZpZWxkIG9mIHRhYj8uZmllbGRzID8/IFtdKSB7XG4gICAgICAgIGNvbnN0IGJhc2VsaW5lVmFsdWUgPSBiYXNlbGluZVRhYltmaWVsZC5pZF07XG5cbiAgICAgICAgLy8gQSBGb3JtQXJyYXkgY2Fubm90IGJlIHJlc3RvcmVkIHdpdGggc2V0VmFsdWUg4oCUIHRoYXQgZGVtYW5kcyBhIHZhbHVlIHNoYXBlZCBleGFjdGx5XG4gICAgICAgIC8vIGxpa2UgdGhlIGN1cnJlbnQgcm93cy4gUmVidWlsZCBpdCBmcm9tIHRoZSBiYXNlbGluZSByb3dzIGluc3RlYWQuXG4gICAgICAgIGNvbnN0IGFycmF5ID0gZm9ybS5nZXRBcnJheUNvbnRyb2woZmllbGQuaWQsIHRhYklkKTtcbiAgICAgICAgaWYgKGFycmF5KSB7XG4gICAgICAgICAgYXJyYXkuY2xlYXIoKTtcbiAgICAgICAgICBmb3IgKGNvbnN0IHJvdyBvZiBBcnJheS5pc0FycmF5KGJhc2VsaW5lVmFsdWUpID8gYmFzZWxpbmVWYWx1ZSA6IFtdKSB7XG4gICAgICAgICAgICBhcnJheS5wdXNoKGZvcm0uY3JlYXRlQXJyYXlSb3coZmllbGQsIHJvdykpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBhcnJheS51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KCk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICBmb3JtLmdldENvbnRyb2woZmllbGQuaWQsIHRhYklkKT8uc2V0VmFsdWUoYmFzZWxpbmVWYWx1ZSA/PyBudWxsKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuY3VycmVudERhdGEuc2V0KGZvcm0uZXh0cmFjdFJlY29yZCgpKTtcbiAgICB9XG4gICAgdGhpcy5zZWN0aW9uRXJyb3JzLnNldCh7fSk7XG4gICAgdGhpcy5lZGl0aW5nVGFiSWQuc2V0KG51bGwpO1xuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIHRoaXMgc2VjdGlvbiBvbmx5LCB0aGVuIGVtaXQgaXQuIFZhbGlkYXRpb24gaXMgc2NvcGVkIHR3byB3YXlzOiBBbmd1bGFyXG4gICAqIHZhbGlkaXR5IG9mIHRoZSB0YWIncyBvd24gY29udHJvbHMsIGFuZCBydWxlcyBmaWx0ZXJlZCB0byB0aGlzIHRhYiDigJQgYSBydWxlIHRhcmdldGluZ1xuICAgKiBhIGhpZGRlbiB0YWIgbXVzdCBub3QgYmxvY2sgdGhlIHNhdmUgKHRoZSByZWZlcmVuY2UncyBPVjAtOTY4IGZpeCkuXG4gICAqL1xuICBzYXZlU2VjdGlvbigpOiB2b2lkIHtcbiAgICBjb25zdCB0YWJJZCA9IHRoaXMuZWRpdGluZ1RhYklkKCk7XG4gICAgY29uc3QgZm9ybSA9IHRoaXMuZHluYW1pY0Zvcm1Db21wO1xuICAgIGlmICghdGFiSWQgfHwgIWZvcm0pIHJldHVybjtcblxuICAgIGNvbnN0IHRhYiA9IGZpbmRUYWIodGhpcy5jb25maWc/LnRhYnMsIHRhYklkKTtcbiAgICBjb25zdCBlcnJvcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblxuICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGFiPy5maWVsZHMgPz8gW10pIHtcbiAgICAgIGNvbnN0IGN0cmwgPSBmb3JtLmdldENvbnRyb2woZmllbGQuaWQsIHRhYklkKTtcbiAgICAgIGlmIChjdHJsPy5pbnZhbGlkKSB7XG4gICAgICAgIGN0cmwubWFya0FzVG91Y2hlZCgpO1xuICAgICAgICBlcnJvcnNbZmllbGQuaWRdID0gYCR7cmVzb2x2ZUxhYmVsKGZpZWxkLmxhYmVsLCB0aGlzLmxhbmd1YWdlKX0gaXMgaW52YWxpZC5gO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHNjb3BlZCA9IHRoaXMucnVsZXNFdmFsdWF0aW9uLmZpbHRlckZvclRhYih0aGlzLnJ1bGVzLCB0YWJJZCwgdGhpcy5jb25maWcpO1xuICAgIGNvbnN0IHJ1bGVFcnJvcnMgPSB0aGlzLnJ1bGVzRXZhbHVhdGlvbi5ldmFsdWF0ZShzY29wZWQsIGZvcm0uZm9ybVZhbHVlcygpLCB0aGlzLm9yaWdpbmFsQmFzZWxpbmUoKSlcbiAgICAgIC52YWxpZGF0aW9uRXJyb3JzO1xuICAgIE9iamVjdC5hc3NpZ24oZXJyb3JzLCBydWxlRXJyb3JzKTtcblxuICAgIGlmIChPYmplY3Qua2V5cyhlcnJvcnMpLmxlbmd0aCA+IDApIHtcbiAgICAgIHRoaXMuc2VjdGlvbkVycm9ycy5zZXQoZXJyb3JzKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnNlY3Rpb25FcnJvcnMuc2V0KHt9KTtcbiAgICB0aGlzLmVkaXRpbmdUYWJJZC5zZXQobnVsbCk7XG5cbiAgICBjb25zdCByZWNvcmQgPSBmb3JtLmV4dHJhY3RSZWNvcmQoKTtcbiAgICB0aGlzLmN1cnJlbnREYXRhLnNldChyZWNvcmQpO1xuICAgIHRoaXMuc2VjdGlvblNhdmUuZW1pdCh7IHRhYklkLCByZWNvcmQgfSk7XG4gIH1cblxuICAvKipcbiAgICogU2VlZCB0aGUgc2Vzc2lvbiBiYXNlbGluZSBmcm9tIHRoZSBsb2FkZWQgcmVjb3JkIHNvIGBWQUxVRV9DSEFOR0VEYCBydWxlcyBhbmQgdGhlXG4gICAqIG1vZGlmaWNhdGlvbiBiYW5uZXIgY29tcGFyZSBhZ2FpbnN0IHRoZSByZWNvcmQgYXMgaXQgd2FzIG9wZW5lZCDigJQgbm90IGFzIGl0IHdhc1xuICAgKiBhZnRlciB0aGUgZmlyc3Qga2V5c3Ryb2tlLlxuICAgKi9cbiAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgIGlmIChjaGFuZ2VzWydpbml0aWFsRGF0YSddIHx8IGNoYW5nZXNbJ2NvbmZpZyddKSB7XG4gICAgICB0aGlzLm9yaWdpbmFsQmFzZWxpbmUuc2V0KHsgLi4uKHRoaXMuaW5pdGlhbERhdGEgPz8ge30pIH0pO1xuICAgICAgdGhpcy5jdXJyZW50RGF0YS5zZXQoeyAuLi4odGhpcy5pbml0aWFsRGF0YSA/PyB7fSkgfSk7XG4gICAgICB0aGlzLmFjdGl2ZVRhYklkU2lnLnNldCh0aGlzLmZpcnN0VmlzaWJsZVRhYklkKCkpO1xuICAgICAgLy8gQSBkaWZmZXJlbnQgcmVjb3JkIGlzIGEgZGlmZmVyZW50IHNldCBvZiBiYW5uZXJzIOKAlCByZS1hcm0gdGhlbS5cbiAgICAgIHRoaXMuZGlzbWlzc2VkLnNldChuZXcgU2V0PHN0cmluZz4oKSk7XG4gICAgfVxuICB9XG5cbiAgZ2V0IHJlY29yZFRpdGxlKCk6IHN0cmluZyB7XG4gICAgaWYgKCF0aGlzLmNvbmZpZykgcmV0dXJuICdSZWNvcmQnO1xuICAgIHJldHVybiB0aGlzLmNvbmZpZy5uYW1lID8gcmVzb2x2ZUxhYmVsKHRoaXMuY29uZmlnLm5hbWUsIHRoaXMubGFuZ3VhZ2UpIDogdGhpcy5jb25maWcuZW50aXR5O1xuICB9XG5cbiAgZ2V0IGF2YXRhckxldHRlcigpOiBzdHJpbmcge1xuICAgIHJldHVybiAodGhpcy5yZWNvcmRUaXRsZSB8fCAnUicpLmNoYXJBdCgwKS50b1VwcGVyQ2FzZSgpO1xuICB9XG5cbiAgLy8g4pSA4pSA4pSAIEhlYWRlciBmaWVsZHMgKGlzUHJvZmlsZUltYWdlIC8gaXNIZWFkZXJUb2dnbGUpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBFdmVyeSBmaWVsZCBpbiB0aGUgY29uZmlnLCBhdCBhbnkgZGVwdGguICovXG4gIHByaXZhdGUgYWxsRmllbGRzKCk6IE5lc3RlZEZpZWxkQ29uZmlnW10ge1xuICAgIGNvbnN0IG91dDogTmVzdGVkRmllbGRDb25maWdbXSA9IFtdO1xuICAgIGNvbnN0IHdhbGtGaWVsZHMgPSAoZmllbGRzOiBOZXN0ZWRGaWVsZENvbmZpZ1tdIHwgdW5kZWZpbmVkKSA9PiB7XG4gICAgICBmb3IgKGNvbnN0IGYgb2YgZmllbGRzID8/IFtdKSB7XG4gICAgICAgIG91dC5wdXNoKGYpO1xuICAgICAgICB3YWxrRmllbGRzKGYuY2hpbGRyZW4pO1xuICAgICAgfVxuICAgIH07XG4gICAgY29uc3Qgd2Fsa1RhYnMgPSAodGFiczogTmVzdGVkVGFiQ29uZmlnW10gfCB1bmRlZmluZWQpID0+IHtcbiAgICAgIGZvciAoY29uc3QgdCBvZiB0YWJzID8/IFtdKSB7XG4gICAgICAgIHdhbGtGaWVsZHModC5maWVsZHMpO1xuICAgICAgICB3YWxrVGFicyh0LmNoaWxkcmVuKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHdhbGtUYWJzKHRoaXMuY29uZmlnPy50YWJzKTtcbiAgICByZXR1cm4gb3V0O1xuICB9XG5cbiAgLyoqIFRoZSBmaWVsZCBmbGFnZ2VkIGBpc1Byb2ZpbGVJbWFnZWAsIHJlbmRlcmVkIGFzIHRoZSByZWNvcmQgYXZhdGFyLiAqL1xuICBnZXQgaGVhZGVyUHJvZmlsZUZpZWxkKCk6IE5lc3RlZEZpZWxkQ29uZmlnIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuYWxsRmllbGRzKCkuZmluZChmID0+IGYuaXNQcm9maWxlSW1hZ2UpID8/IG51bGw7XG4gIH1cblxuICAvKiogVGhlIGZpZWxkIGZsYWdnZWQgYGlzSGVhZGVyVG9nZ2xlYCwgcmVuZGVyZWQgYXMgYSBzdGF0dXMgc3dpdGNoIGluIHRoZSBoZWFkZXIuICovXG4gIGdldCBoZWFkZXJUb2dnbGVGaWVsZCgpOiBOZXN0ZWRGaWVsZENvbmZpZyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmFsbEZpZWxkcygpLmZpbmQoZiA9PiBmLmlzSGVhZGVyVG9nZ2xlKSA/PyBudWxsO1xuICB9XG5cbiAgLyoqIEF2YXRhciBpbWFnZSBVUkwsIHdoZW4gdGhlIHByb2ZpbGUgZmllbGQgaG9sZHMgYSBwZXJzaXN0ZWQgRmlsZVJlZi4gKi9cbiAgZ2V0IHByb2ZpbGVJbWFnZVVybCgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICBjb25zdCBmaWVsZCA9IHRoaXMuaGVhZGVyUHJvZmlsZUZpZWxkO1xuICAgIGlmICghZmllbGQpIHJldHVybiBudWxsO1xuICAgIGNvbnN0IHZhbHVlID0gdGhpcy5maWVsZFZhbHVlKGZpZWxkLmlkKSBhcyB7IHVybD86IHN0cmluZyB9IHwgbnVsbDtcbiAgICByZXR1cm4gdmFsdWU/LnVybCA/PyBudWxsO1xuICB9XG5cbiAgZ2V0IGhlYWRlclRvZ2dsZVZhbHVlKCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5oZWFkZXJUb2dnbGVGaWVsZDtcbiAgICByZXR1cm4gZmllbGQgPyB0aGlzLmZpZWxkVmFsdWUoZmllbGQuaWQpID09PSB0cnVlIDogZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogRmxpcCB0aGUgaGVhZGVyIHRvZ2dsZS4gSXQgd3JpdGVzIHN0cmFpZ2h0IHRvIHRoZSBjb250cm9sIHJhdGhlciB0aGFuIGdvaW5nIHRocm91Z2hcbiAgICogc2VjdGlvbiBlZGl0aW5nIOKAlCB0aGUgcmVmZXJlbmNlIHRyZWF0cyBpdCBhcyBhIHJlY29yZC1sZXZlbCBzdGF0dXMgc3dpdGNoLCBub3QgcGFydCBvZlxuICAgKiBhbnkgb25lIHRhYidzIHNlY3Rpb24uXG4gICAqL1xuICB0b2dnbGVIZWFkZXJTdGF0dXMoKTogdm9pZCB7XG4gICAgY29uc3QgZmllbGQgPSB0aGlzLmhlYWRlclRvZ2dsZUZpZWxkO1xuICAgIGlmICghZmllbGQgfHwgdGhpcy5yZWNvcmRSZWFkT25seSkgcmV0dXJuO1xuICAgIGNvbnN0IGN0cmwgPSB0aGlzLmNvbnRyb2xGb3IoZmllbGQuaWQpO1xuICAgIGlmICghY3RybCkgcmV0dXJuO1xuICAgIGN0cmwuc2V0VmFsdWUoIXRoaXMuaGVhZGVyVG9nZ2xlVmFsdWUpO1xuICAgIGlmICh0aGlzLmR5bmFtaWNGb3JtQ29tcCkgdGhpcy5jdXJyZW50RGF0YS5zZXQodGhpcy5keW5hbWljRm9ybUNvbXAuZXh0cmFjdFJlY29yZCgpKTtcbiAgfVxuXG4gIC8qKiBMb2NhdGUgYSBmaWVsZCdzIGNvbnRyb2wgd2hlcmV2ZXIgaXQgbGl2ZXMsIHNpbmNlIGhlYWRlciBmaWVsZHMgbWF5IGJlIG9uIGFueSB0YWIuICovXG4gIHByaXZhdGUgY29udHJvbEZvcihmaWVsZElkOiBzdHJpbmcpOiBBYnN0cmFjdENvbnRyb2wgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5keW5hbWljRm9ybUNvbXA/LmdldENvbnRyb2woZmllbGRJZCkgPz8gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBBIGZpZWxkJ3MgY3VycmVudCB2YWx1ZSwgcmVhZCBmcm9tIHRoZSByZWNvcmQgc28gaXQgaXMgc3RhYmxlIGR1cmluZyByZW5kZXIuXG4gICAqXG4gICAqIFJlc29sdmVkIHRocm91Z2ggYGdldFRhYkRhdGFgIHNvIGl0IGhvbm91cnMgZXhhY3RseSB0aGUgbmVzdGluZyBjb250cmFjdCB0aGUgZm9ybVxuICAgKiBwYXRjaGVzIHdpdGgg4oCUIGB7IHRhYklkOiB7IGZpZWxkSWQgfSB9YCwgb3IgdGhlIHJlY29yZCByb290IHdoZW4gdGhlIHRhYiBzZXRzXG4gICAqIGBmbGF0RGF0YWAuIFJlYWRpbmcgdGhlIHJlY29yZCBmbGF0IGluc3RlYWQgaXMgbm90IGEgc2hvcnRjdXQ6IGl0IG1ha2VzIHRoZSBzdW1tYXJ5XG4gICAqIGRpc3BsYXkgdmFsdWVzIHRoZSBmb3JtIG5ldmVyIGxvYWRlZCwgd2hpY2ggcmVhZHMgYXMgXCJ0aGUgZGF0YSBpcyB0aGVyZVwiIHdoaWxlIGV2ZXJ5XG4gICAqIGNvbnRyb2wgYmVoaW5kIGl0IGlzIGVtcHR5LlxuICAgKlxuICAgKiBXYWxrcyBzdWItdGFicyBhcyB3ZWxsOiBhIHN1bW1hcnksIGhlYWRlciwgb3IgcHJvZmlsZSBmaWVsZCBtYXkgc2l0IG9uIGFueSB0YWIgYXQgYW55XG4gICAqIGRlcHRoLlxuICAgKi9cbiAgcHJpdmF0ZSBmaWVsZFZhbHVlKGZpZWxkSWQ6IHN0cmluZyk6IHVua25vd24ge1xuICAgIGNvbnN0IHZpc2l0ID0gKHRhYnM6IE5lc3RlZFRhYkNvbmZpZ1tdID0gW10pOiB1bmtub3duID0+IHtcbiAgICAgIGZvciAoY29uc3QgdGFiIG9mIHRhYnMpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGdldFRhYkRhdGEodGFiLmlkLCB0aGlzLmN1cnJlbnREYXRhKCksIHRoaXMuY29uZmlnKSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IG51bGw7XG4gICAgICAgIGlmIChkYXRhICYmIGZpZWxkSWQgaW4gZGF0YSkgcmV0dXJuIGRhdGFbZmllbGRJZF07XG4gICAgICAgIGlmICh0YWIuY2hpbGRyZW4pIHtcbiAgICAgICAgICBjb25zdCBmb3VuZCA9IHZpc2l0KHRhYi5jaGlsZHJlbik7XG4gICAgICAgICAgaWYgKGZvdW5kICE9PSBGSUVMRF9OT1RfRk9VTkQpIHJldHVybiBmb3VuZDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIEZJRUxEX05PVF9GT1VORDtcbiAgICB9O1xuXG4gICAgY29uc3QgcmVzdWx0ID0gdmlzaXQodGhpcy5jb25maWc/LnRhYnMpO1xuICAgIHJldHVybiByZXN1bHQgPT09IEZJRUxEX05PVF9GT1VORCA/IHVuZGVmaW5lZCA6IHJlc3VsdDtcbiAgfVxuXG4gIHJlYWRvbmx5IGlzTW9kaWZpZWQgPSBjb21wdXRlZCgoKSA9PiB7XG4gICAgY29uc3Qgb3JpZyA9IHRoaXMub3JpZ2luYWxCYXNlbGluZSgpO1xuICAgIGNvbnN0IGN1cnIgPSB0aGlzLmN1cnJlbnREYXRhKCk7XG4gICAgaWYgKCFvcmlnIHx8ICFjdXJyIHx8IE9iamVjdC5rZXlzKG9yaWcpLmxlbmd0aCA9PT0gMCkgcmV0dXJuIGZhbHNlO1xuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShvcmlnKSAhPT0gSlNPTi5zdHJpbmdpZnkoY3Vycik7XG4gIH0pO1xuXG4gIHJlYWRvbmx5IHN1bW1hcnlGaWVsZHMgPSBjb21wdXRlZDxOZXN0ZWRGaWVsZENvbmZpZ1tdPigoKSA9PiB7XG4gICAgY29uc3QgZmllbGRzOiBOZXN0ZWRGaWVsZENvbmZpZ1tdID0gW107XG4gICAgY29uc3Qgd2Fsa1RhYnMgPSAodGFiczogTmVzdGVkVGFiQ29uZmlnW10gPSBbXSkgPT4ge1xuICAgICAgZm9yIChjb25zdCB0IG9mIHRhYnMpIHtcbiAgICAgICAgZm9yIChjb25zdCBmIG9mIHQuZmllbGRzID8/IFtdKSB7XG4gICAgICAgICAgaWYgKGYuc2hvd09uTWluaW1pemUpIGZpZWxkcy5wdXNoKGYpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0LmNoaWxkcmVuKSB3YWxrVGFicyh0LmNoaWxkcmVuKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHdhbGtUYWJzKHRoaXMuY29uZmlnPy50YWJzKTtcbiAgICByZXR1cm4gZmllbGRzO1xuICB9KTtcblxuICBvbkZvcm1DaGFuZ2UoZGF0YTogUmVjb3JkPHN0cmluZywgYW55Pik6IHZvaWQge1xuICAgIC8vIEJhc2VsaW5lIGlzIHNlZWRlZCBmcm9tIGBpbml0aWFsRGF0YWAgaW4gbmdPbkNoYW5nZXM7IG9ubHkgZmlsbCBpdCBoZXJlIHdoZW4gdGhlXG4gICAgLy8gcmVjb3JkIHdhcyBvcGVuZWQgZW1wdHkgKGNyZWF0ZSBmbG93KSwgc28gdGhlIGZpcnN0IGVtaXR0ZWQgc2hhcGUgYmVjb21lcyB0aGUgYmFzZWxpbmUuXG4gICAgaWYgKE9iamVjdC5rZXlzKHRoaXMub3JpZ2luYWxCYXNlbGluZSgpKS5sZW5ndGggPT09IDApIHtcbiAgICAgIHRoaXMub3JpZ2luYWxCYXNlbGluZS5zZXQoeyAuLi5kYXRhIH0pO1xuICAgIH1cbiAgICB0aGlzLmN1cnJlbnREYXRhLnNldChkYXRhKTtcbiAgICB0aGlzLmZvcm1DaGFuZ2UuZW1pdChkYXRhKTtcbiAgfVxuXG4gIG9uRm9ybVN1Ym1pdChkYXRhOiBSZWNvcmQ8c3RyaW5nLCBhbnk+KTogdm9pZCB7XG4gICAgdGhpcy5mb3JtU3VibWl0LmVtaXQoZGF0YSk7XG4gIH1cblxuICBvbkZvcm1SZXNldCgpOiB2b2lkIHtcbiAgICB0aGlzLmZvcm1SZXNldC5lbWl0KCk7XG4gIH1cblxuICBmb3JtYXRGaWVsZExhYmVsKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHJlc29sdmVMYWJlbChmaWVsZC5sYWJlbCwgdGhpcy5sYW5ndWFnZSk7XG4gIH1cblxuICAvKiogU3VtbWFyeSB2YWx1ZXMgcmVuZGVyIHRocm91Z2ggdGhlIHNoYXJlZCBjb3JlIGZvcm1hdHRlciwgbm90IGEgbG9jYWwgc3RyaW5naWZpZXIuICovXG4gIGZvcm1hdEZpZWxkVmFsdWUoZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnKTogc3RyaW5nIHtcbiAgICByZXR1cm4gZm9ybWF0RGlzcGxheVZhbHVlKGZpZWxkLnR5cGUsIGZpZWxkLm9wdGlvbnMsIHRoaXMuZmllbGRWYWx1ZShmaWVsZC5pZCksIHRoaXMubGFuZ3VhZ2UpO1xuICB9XG5cbiAganVtcFRvRmllbGQoZmllbGRJZDogc3RyaW5nKTogdm9pZCB7XG4gICAgLy8gRmluZCBwYXJlbnQgdGFiIGZvciBmaWVsZFxuICAgIGZvciAoY29uc3QgdGFiIG9mIHRoaXMuY29uZmlnPy50YWJzIHx8IFtdKSB7XG4gICAgICBjb25zdCBoYXNGaWVsZCA9ICh0YWIuZmllbGRzIHx8IFtdKS5zb21lKGYgPT4gZi5pZCA9PT0gZmllbGRJZCk7XG4gICAgICBpZiAoaGFzRmllbGQpIHtcbiAgICAgICAgdGhpcy5keW5hbWljRm9ybUNvbXA/LnNldEFjdGl2ZVRhYih0YWIuaWQpO1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICBjb25zdCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGBmaWVsZC1jb250YWluZXItJHtmaWVsZElkfWApO1xuICAgICAgICAgIGlmIChlbCkge1xuICAgICAgICAgICAgZWwuc2Nyb2xsSW50b1ZpZXcoeyBiZWhhdmlvcjogJ3Ntb290aCcsIGJsb2NrOiAnY2VudGVyJyB9KTtcbiAgICAgICAgICAgIGVsLmZvY3VzKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9LCA1MCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuIiwiPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yXCI+XG4gIDwhLS0gUmVjb3JkIEhlYWRlciAtLT5cbiAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19oZWFkZXJcIj5cbiAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3RpdGxlLWdyb3VwXCI+XG4gICAgICBAaWYgKHByb2ZpbGVJbWFnZVVybCkge1xuICAgICAgICA8aW1nXG4gICAgICAgICAgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fYXZhdGFyXCJcbiAgICAgICAgICBkYXRhLXRlc3RpZD1cImhlYWRlci1wcm9maWxlLWltYWdlXCJcbiAgICAgICAgICBbc3JjXT1cInByb2ZpbGVJbWFnZVVybFwiXG4gICAgICAgICAgW2FsdF09XCJyZWNvcmRUaXRsZVwiXG4gICAgICAgIC8+XG4gICAgICB9IEBlbHNlIHtcbiAgICAgICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19hdmF0YXJcIiBkYXRhLXRlc3RpZD1cImhlYWRlci1hdmF0YXItaW5pdGlhbFwiPlxuICAgICAgICAgIHt7IGF2YXRhckxldHRlciB9fVxuICAgICAgICA8L2Rpdj5cbiAgICAgIH1cbiAgICAgIDxkaXY+XG4gICAgICAgIDxoMiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX190aXRsZVwiIGRhdGEtdGVzdGlkPVwicmVjb3JkLXRpdGxlXCI+XG4gICAgICAgICAge3sgcmVjb3JkVGl0bGUgfX1cbiAgICAgICAgPC9oMj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fc3VidGl0bGVcIj5FbnRpdHk6IHt7IGNvbmZpZy5lbnRpdHkgfX08L3NwYW4+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDwhLS0gUmVjb3JkLWxldmVsIHN0YXR1cyBzd2l0Y2gsIG91dHNpZGUgc2VjdGlvbiBlZGl0aW5nIC0tPlxuICAgIEBpZiAoaGVhZGVyVG9nZ2xlRmllbGQ7IGFzIHRvZ2dsZUZpZWxkKSB7XG4gICAgICA8bGFiZWwgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9faGVhZGVyLXRvZ2dsZVwiPlxuICAgICAgICA8c3Bhbj57eyBmb3JtYXRGaWVsZExhYmVsKHRvZ2dsZUZpZWxkKSB9fTwvc3Bhbj5cbiAgICAgICAgPGlucHV0XG4gICAgICAgICAgdHlwZT1cImNoZWNrYm94XCJcbiAgICAgICAgICByb2xlPVwic3dpdGNoXCJcbiAgICAgICAgICBkYXRhLXRlc3RpZD1cImhlYWRlci10b2dnbGVcIlxuICAgICAgICAgIFtjaGVja2VkXT1cImhlYWRlclRvZ2dsZVZhbHVlXCJcbiAgICAgICAgICBbZGlzYWJsZWRdPVwicmVjb3JkUmVhZE9ubHlcIlxuICAgICAgICAgIFthdHRyLmFyaWEtY2hlY2tlZF09XCJoZWFkZXJUb2dnbGVWYWx1ZVwiXG4gICAgICAgICAgKGNoYW5nZSk9XCJ0b2dnbGVIZWFkZXJTdGF0dXMoKVwiXG4gICAgICAgIC8+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2hlYWRlci10b2dnbGUtdGV4dFwiPnt7IGhlYWRlclRvZ2dsZVZhbHVlID8gJ1llcycgOiAnTm8nIH19PC9zcGFuPlxuICAgICAgPC9sYWJlbD5cbiAgICB9XG4gIDwvZGl2PlxuXG4gIDwhLS0gRGlzbWlzc2libGUgaW5mbyBiYW5uZXJzIChydWxlcyBgaW5mb2AgYWN0aW9uKSAtLT5cbiAgQGZvciAoYmFubmVyIG9mIGluZm9CYW5uZXJzOyB0cmFjayBiYW5uZXIuZmllbGRJZCkge1xuICAgIDxkaXZcbiAgICAgIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lciBuZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLS1pbmZvXCJcbiAgICAgIHJvbGU9XCJzdGF0dXNcIlxuICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2luZm8tYmFubmVyLScgKyBiYW5uZXIuZmllbGRJZFwiXG4gICAgPlxuICAgICAgPHNwYW4+4oS577iPIHt7IGJhbm5lci5tZXNzYWdlIH19PC9zcGFuPlxuICAgICAgPGJ1dHRvblxuICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLWRpc21pc3NcIlxuICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCInZGlzbWlzcy0nICsgYmFubmVyLmZpZWxkSWRcIlxuICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidEaXNtaXNzOiAnICsgYmFubmVyLm1lc3NhZ2VcIlxuICAgICAgICAoY2xpY2spPVwiZGlzbWlzc0luZm9CYW5uZXIoYmFubmVyLmZpZWxkSWQpXCJcbiAgICAgID7inJU8L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgfVxuXG4gIDwhLS0gTW9kaWZpY2F0aW9uIGJhc2VsaW5lIGJhbm5lciAtLT5cbiAgQGlmIChpc01vZGlmaWVkKCkpIHtcbiAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2Jhbm5lciBuZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyLS13YXJuaW5nXCI+XG4gICAgICDwn5SSIFJlY29yZCBtb2RpZmllZCBmcm9tIHNlc3Npb24gYmFzZWxpbmUg4oCUIHVuc2F2ZWQgY2hhbmdlcyBwZW5kaW5nLlxuICAgIDwvZGl2PlxuICB9XG5cbiAgPCEtLSBTdW1tYXJ5IFBhbmVsIChzaG93T25NaW5pbWl6ZSBmaWVsZHMgd2l0aCBpbnRlcmFjdGl2ZSBqdW1wKSAtLT5cbiAgQGlmIChzdW1tYXJ5RmllbGRzKCkubGVuZ3RoID4gMCkge1xuICAgIDxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fc3VtbWFyeS1wYW5lbFwiIGRhdGEtdGVzdGlkPVwic3VtbWFyeS1wYW5lbFwiPlxuICAgICAgQGZvciAoc2Ygb2Ygc3VtbWFyeUZpZWxkcygpOyB0cmFjayBzZi5pZCkge1xuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fc3VtbWFyeS1pdGVtXCJcbiAgICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCInc3VtbWFyeS0nICsgc2YuaWRcIlxuICAgICAgICAgIChjbGljayk9XCJqdW1wVG9GaWVsZChzZi5pZClcIlxuICAgICAgICAgIFt0aXRsZV09XCInQ2xpY2sgdG8ganVtcCB0byAnICsgZm9ybWF0RmllbGRMYWJlbChzZilcIlxuICAgICAgICA+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fc3VtbWFyeS1sYWJlbFwiPnt7IGZvcm1hdEZpZWxkTGFiZWwoc2YpIH19PC9zcGFuPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3N1bW1hcnktdmFsXCI+e3sgZm9ybWF0RmllbGRWYWx1ZShzZikgfX08L3NwYW4+XG4gICAgICAgIDwvZGl2PlxuICAgICAgfVxuICAgIDwvZGl2PlxuICB9XG5cbiAgPCEtLSBTZWN0aW9uIGVkaXQgYmFyOiBvbmUgdGFiIGlzIGVkaXRlZCBhdCBhIHRpbWUsIGFuZCB2YWxpZGF0ZWQgb24gaXRzIG93biAtLT5cbiAgQGlmICh2aWV3TW9kZSAmJiAhcmVjb3JkUmVhZE9ubHkpIHtcbiAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3NlY3Rpb24tYmFyXCI+XG4gICAgICBAaWYgKGlzRWRpdGluZ0FjdGl2ZVRhYikge1xuICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19zZWN0aW9uLWhpbnRcIj5FZGl0aW5nIHRoaXMgc2VjdGlvbjwvc3Bhbj5cbiAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJuZ3gtZm9ybV9fcmVzZXRcIiBkYXRhLXRlc3RpZD1cImNhbmNlbC1zZWN0aW9uXCIgKGNsaWNrKT1cImNhbmNlbFNlY3Rpb24oKVwiPlxuICAgICAgICAgIENhbmNlbFxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJuZ3gtZm9ybV9fc3VibWl0XCIgZGF0YS10ZXN0aWQ9XCJzYXZlLXNlY3Rpb25cIiAoY2xpY2spPVwic2F2ZVNlY3Rpb24oKVwiPlxuICAgICAgICAgIFNhdmUgc2VjdGlvblxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIH0gQGVsc2Uge1xuICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIiBjbGFzcz1cIm5neC1mb3JtX19yZXNldFwiIGRhdGEtdGVzdGlkPVwiZWRpdC1zZWN0aW9uXCIgKGNsaWNrKT1cImVkaXRTZWN0aW9uKClcIj5cbiAgICAgICAgICBFZGl0IHNlY3Rpb25cbiAgICAgICAgPC9idXR0b24+XG4gICAgICB9XG4gICAgPC9kaXY+XG4gIH1cblxuICBAaWYgKE9iamVjdC5rZXlzKHNlY3Rpb25FcnJvcnMoKSkubGVuZ3RoID4gMCkge1xuICAgIDxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fYmFubmVyIG5neC1yZWNvcmQtZWRpdG9yX19iYW5uZXItLWVycm9yXCIgcm9sZT1cImFsZXJ0XCIgZGF0YS10ZXN0aWQ9XCJzZWN0aW9uLWVycm9yc1wiPlxuICAgICAgQGZvciAoa2V5IG9mIE9iamVjdC5rZXlzKHNlY3Rpb25FcnJvcnMoKSk7IHRyYWNrIGtleSkge1xuICAgICAgICA8ZGl2PuKaoO+4jyB7eyBzZWN0aW9uRXJyb3JzKClba2V5XSB9fTwvZGl2PlxuICAgICAgfVxuICAgIDwvZGl2PlxuICB9XG5cbiAgPCEtLSBBcnJheSByb3dzOiBsaXN0ZWQgaGVyZSwgZWRpdGVkIGluIHRoZSBkcmF3ZXIgYmVsb3cgLS0+XG4gIEBmb3IgKGFycmF5RmllbGQgb2YgYXJyYXlGaWVsZHNGb3JBY3RpdmVUYWI7IHRyYWNrIGFycmF5RmllbGQuaWQpIHtcbiAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3Jvd3NcIiBbYXR0ci5kYXRhLXRlc3RpZF09XCIncm93cy0nICsgYXJyYXlGaWVsZC5pZFwiPlxuICAgICAgPGRpdiBjbGFzcz1cIm5neC1yZWNvcmQtZWRpdG9yX19yb3dzLWhlYWRcIj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fc3VtbWFyeS1sYWJlbFwiPnt7IGZvcm1hdEZpZWxkTGFiZWwoYXJyYXlGaWVsZCkgfX08L3NwYW4+XG4gICAgICAgIEBpZiAoIXNlY3Rpb25SZWFkT25seSkge1xuICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgY2xhc3M9XCJuZ3gtZm9ybV9fcmVzZXRcIlxuICAgICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2FkZC1yb3ctJyArIGFycmF5RmllbGQuaWRcIlxuICAgICAgICAgICAgKGNsaWNrKT1cIm9wZW5BZGRSb3coYXJyYXlGaWVsZClcIlxuICAgICAgICAgID4rIFJvdzwvYnV0dG9uPlxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cblxuICAgICAgQGZvciAocm93IG9mIHJvd3NPZihhcnJheUZpZWxkKTsgdHJhY2sgJGluZGV4KSB7XG4gICAgICAgIDxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fcm93XCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3Jvdy0nICsgYXJyYXlGaWVsZC5pZCArICctJyArICRpbmRleFwiPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3Jvdy10ZXh0XCI+e3sgcm93U3VtbWFyeShhcnJheUZpZWxkLCByb3cpIH19PC9zcGFuPlxuICAgICAgICAgIEBpZiAoIXNlY3Rpb25SZWFkT25seSkge1xuICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fcm93LWJ0blwiXG4gICAgICAgICAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidlZGl0LXJvdy0nICsgYXJyYXlGaWVsZC5pZCArICctJyArICRpbmRleFwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJvcGVuRWRpdFJvdyhhcnJheUZpZWxkLCAkaW5kZXgpXCI+RWRpdDwvYnV0dG9uPlxuICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fcm93LWJ0blwiXG4gICAgICAgICAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidkZWxldGUtcm93LScgKyBhcnJheUZpZWxkLmlkICsgJy0nICsgJGluZGV4XCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cImRlbGV0ZVJvdyhhcnJheUZpZWxkLCAkaW5kZXgpXCI+RGVsZXRlPC9idXR0b24+XG4gICAgICAgICAgfVxuICAgICAgICA8L2Rpdj5cbiAgICAgIH0gQGVtcHR5IHtcbiAgICAgICAgPHAgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fcm93LWVtcHR5XCI+Tm8gcm93cyB5ZXQuPC9wPlxuICAgICAgfVxuICAgIDwvZGl2PlxuICB9XG5cbiAgPCEtLVxuICAgIFJvdyBkcmF3ZXIuIERlbGliZXJhdGVseSBvdXRzaWRlIHRoZSB0YWIgcGFuZWw6IGJ1aWxkaW5nIGEgcm93J3MgRm9ybUdyb3VwIGluc2lkZSB0aGVcbiAgICB0YWIgdmlldyByYWNlcyB0aGUgdGFiJ3Mgb3duIGluaXRpYWxpc2F0aW9uICh0aGUgcmVmZXJlbmNlIGhpdCB0aGlzIHdpdGggRm9ybUFycmF5KS5cbiAgLS0+XG4gIEBpZiAoaW5saW5lUm93RmllbGQoKTsgYXMgcm93RmllbGQpIHtcbiAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX2RyYXdlclwiIGRhdGEtdGVzdGlkPVwicm93LWRyYXdlclwiIFtmb3JtR3JvdXBdPVwiaW5saW5lUm93Rm9ybSFcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJuZ3gtcmVjb3JkLWVkaXRvcl9fcm93cy1oZWFkXCI+XG4gICAgICAgIDxzdHJvbmc+e3sgaW5saW5lUm93SW5kZXgoKSA9PT0gbnVsbCA/ICdBZGQnIDogJ0VkaXQnIH19IHt7IGZvcm1hdEZpZWxkTGFiZWwocm93RmllbGQpIH19IHJvdzwvc3Ryb25nPlxuICAgICAgPC9kaXY+XG5cbiAgICAgIDxkaXYgY2xhc3M9XCJuZ3gtZm9ybV9fcGFuZWxcIj5cbiAgICAgICAgQGZvciAoY2hpbGQgb2Ygcm93RmllbGQuY2hpbGRyZW4gPz8gW107IHRyYWNrIGNoaWxkLmlkKSB7XG4gICAgICAgICAgPG5neC1keW5hbWljLWZpZWxkXG4gICAgICAgICAgICBbZmllbGRdPVwiY2hpbGRcIlxuICAgICAgICAgICAgW2NvbnRyb2xdPVwiaW5saW5lUm93Rm9ybSEuZ2V0KGNoaWxkLmlkKSFcIlxuICAgICAgICAgICAgW2NvbmZpZ109XCJjb25maWdcIlxuICAgICAgICAgICAgW2xhbmd1YWdlXT1cImxhbmd1YWdlXCJcbiAgICAgICAgICAgIFtyZWFkb25seV09XCJmYWxzZVwiXG4gICAgICAgICAgICBbdXNlclJvbGVzXT1cInVzZXJSb2xlc1wiXG4gICAgICAgICAgICBbc3R5bGUuZ3JpZENvbHVtbl09XCInc3BhbiAxMidcIlxuICAgICAgICAgIC8+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8ZGl2IGNsYXNzPVwibmd4LXJlY29yZC1lZGl0b3JfX3NlY3Rpb24tYmFyXCI+XG4gICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwibmd4LWZvcm1fX3Jlc2V0XCIgZGF0YS10ZXN0aWQ9XCJjYW5jZWwtcm93XCIgKGNsaWNrKT1cImNhbmNlbFJvdygpXCI+Q2FuY2VsPC9idXR0b24+XG4gICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIGNsYXNzPVwibmd4LWZvcm1fX3N1Ym1pdFwiIGRhdGEtdGVzdGlkPVwic2F2ZS1yb3dcIiAoY2xpY2spPVwic2F2ZVJvdygpXCI+U2F2ZSByb3c8L2J1dHRvbj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICB9XG5cbiAgPCEtLSBNYWluIER5bmFtaWMgRm9ybSAtLT5cbiAgPG5neC1keW5hbWljLWZvcm1cbiAgICBbY29uZmlnXT1cImNvbmZpZ1wiXG4gICAgW3J1bGVzXT1cInJ1bGVzXCJcbiAgICBbaW5pdGlhbERhdGFdPVwiaW5pdGlhbERhdGFcIlxuICAgIFtvcmlnaW5hbERhdGFdPVwib3JpZ2luYWxCYXNlbGluZSgpXCJcbiAgICBbdXNlclJvbGVzXT1cInVzZXJSb2xlc1wiXG4gICAgW2xhbmd1YWdlXT1cImxhbmd1YWdlXCJcbiAgICBbcmVhZG9ubHldPVwic2VjdGlvblJlYWRPbmx5XCJcbiAgICBbcmVhZE9ubHlGaWVsZHNdPVwicmVhZE9ubHlGaWVsZHNcIlxuICAgIFtzaG93SW5mb0Jhbm5lcnNdPVwiZmFsc2VcIlxuICAgIFtsb2FkaW5nXT1cImxvYWRpbmdcIlxuICAgIFtlcnJvcl09XCJlcnJvclwiXG4gICAgKGZvcm1DaGFuZ2UpPVwib25Gb3JtQ2hhbmdlKCRldmVudClcIlxuICAgIChmb3JtU3VibWl0KT1cIm9uRm9ybVN1Ym1pdCgkZXZlbnQpXCJcbiAgICAoZm9ybVJlc2V0KT1cIm9uRm9ybVJlc2V0KClcIlxuICAgIChhY3RpdmVUYWJDaGFuZ2UpPVwib25BY3RpdmVUYWJDaGFuZ2UoJGV2ZW50KVwiXG4gIC8+XG48L2Rpdj5cbiJdfQ==
|