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