@gnggln/ng-ui-system 1.0.0-alpha.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/esm2022/gnggln-ng-ui-system.mjs +5 -0
- package/esm2022/lib/components/accordion/accordion.component.mjs +353 -0
- package/esm2022/lib/components/accordion/accordion.types.mjs +6 -0
- package/esm2022/lib/components/accordion/index.mjs +2 -0
- package/esm2022/lib/components/base-layout/base-layout.component.mjs +218 -0
- package/esm2022/lib/components/base-layout/base-layout.types.mjs +6 -0
- package/esm2022/lib/components/base-layout/index.mjs +14 -0
- package/esm2022/lib/components/button/button-area.component.mjs +196 -0
- package/esm2022/lib/components/button/button.component.mjs +164 -0
- package/esm2022/lib/components/button/button.types.mjs +6 -0
- package/esm2022/lib/components/button/index.mjs +16 -0
- package/esm2022/lib/components/crud-table/crud-table.component.mjs +789 -0
- package/esm2022/lib/components/crud-table/crud-table.types.mjs +6 -0
- package/esm2022/lib/components/crud-table/index.mjs +16 -0
- package/esm2022/lib/components/form-builder/adapters/it-date-adapter.mjs +82 -0
- package/esm2022/lib/components/form-builder/directives/currency-input.directive.mjs +184 -0
- package/esm2022/lib/components/form-builder/form-builder.component.mjs +824 -0
- package/esm2022/lib/components/form-builder/form-wizard.component.mjs +510 -0
- package/esm2022/lib/components/form-builder/index.mjs +19 -0
- package/esm2022/lib/components/form-builder/services/form-condition.service.mjs +132 -0
- package/esm2022/lib/components/form-builder/services/form-validation.service.mjs +381 -0
- package/esm2022/lib/components/form-builder/services/location.service.mjs +140 -0
- package/esm2022/lib/components/form-builder/services/wizard-sync.service.mjs +84 -0
- package/esm2022/lib/components/form-builder/sub-components/error-summary/form-error-summary.component.mjs +161 -0
- package/esm2022/lib/components/form-builder/sub-components/file-input/file-input.component.mjs +310 -0
- package/esm2022/lib/components/form-builder/sub-components/specifica-territoriale/specifica-territoriale.component.mjs +648 -0
- package/esm2022/lib/components/form-builder/sub-components/table-territoriale/table-territoriale.component.mjs +432 -0
- package/esm2022/lib/components/form-builder/types/condition.types.mjs +6 -0
- package/esm2022/lib/components/form-builder/types/field.types.mjs +6 -0
- package/esm2022/lib/components/form-builder/types/index.mjs +2 -0
- package/esm2022/lib/components/form-builder/types/schema.types.mjs +6 -0
- package/esm2022/lib/components/form-builder/types/territoriale.types.mjs +6 -0
- package/esm2022/lib/components/form-builder/types/validation.types.mjs +6 -0
- package/esm2022/lib/components/form-builder-editor/form-builder-editor.component.mjs +730 -0
- package/esm2022/lib/components/form-builder-editor/form-builder-editor.service.mjs +56 -0
- package/esm2022/lib/components/form-builder-editor/index.mjs +21 -0
- package/esm2022/lib/components/form-builder-editor/services/editor-persistence.service.mjs +190 -0
- package/esm2022/lib/components/form-builder-editor/services/editor-state.service.mjs +324 -0
- package/esm2022/lib/components/form-builder-editor/services/field-factory.service.mjs +188 -0
- package/esm2022/lib/components/form-builder-editor/sub-components/condition-editor/condition-editor.component.mjs +667 -0
- package/esm2022/lib/components/form-builder-editor/sub-components/editor-toolbar/editor-toolbar.component.mjs +317 -0
- package/esm2022/lib/components/form-builder-editor/sub-components/field-config-panel/field-config-panel.component.mjs +611 -0
- package/esm2022/lib/components/form-builder-editor/sub-components/field-palette/field-palette.component.mjs +267 -0
- package/esm2022/lib/components/form-builder-editor/sub-components/form-values-panel/form-values-panel.component.mjs +276 -0
- package/esm2022/lib/components/form-builder-editor/sub-components/options-editor/options-editor.component.mjs +323 -0
- package/esm2022/lib/components/form-builder-editor/sub-components/preview-container/preview-container.component.mjs +238 -0
- package/esm2022/lib/components/form-builder-editor/sub-components/section-editor/section-editor.component.mjs +472 -0
- package/esm2022/lib/components/form-builder-editor/sub-components/validation-editor/validation-editor.component.mjs +473 -0
- package/esm2022/lib/components/form-builder-editor/types/editor.types.mjs +6 -0
- package/esm2022/lib/components/layout-builder/index.mjs +18 -0
- package/esm2022/lib/components/layout-builder/layout-builder.component.mjs +1730 -0
- package/esm2022/lib/components/layout-builder/layout-builder.types.mjs +9 -0
- package/esm2022/lib/components/layout-builder/layout.service.mjs +239 -0
- package/esm2022/lib/components/modal/confirm-dialog.component.mjs +151 -0
- package/esm2022/lib/components/modal/index.mjs +4 -0
- package/esm2022/lib/components/modal/modal.component.mjs +139 -0
- package/esm2022/lib/components/modal/modal.service.mjs +194 -0
- package/esm2022/lib/components/modal/modal.types.mjs +6 -0
- package/esm2022/lib/components/page-header/breadcrumb.service.mjs +242 -0
- package/esm2022/lib/components/page-header/index.mjs +20 -0
- package/esm2022/lib/components/page-header/page-header.component.mjs +243 -0
- package/esm2022/lib/components/page-header/page-header.types.mjs +21 -0
- package/esm2022/lib/components/table/index.mjs +2 -0
- package/esm2022/lib/components/table/paginated-table.component.mjs +407 -0
- package/esm2022/lib/components/table/table.types.mjs +6 -0
- package/esm2022/lib/core/types/index.mjs +6 -0
- package/esm2022/lib/core/utils/index.mjs +53 -0
- package/esm2022/lib/sources/location-data.opt.json +8942 -0
- package/esm2022/lib/sources/nazioni.opt.json +215 -0
- package/esm2022/public-api.mjs +34 -0
- package/fesm2022/gnggln-ng-ui-system.mjs +55752 -0
- package/fesm2022/gnggln-ng-ui-system.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/components/accordion/accordion.component.d.ts +118 -0
- package/lib/components/accordion/accordion.types.d.ts +62 -0
- package/lib/components/accordion/index.d.ts +2 -0
- package/lib/components/base-layout/base-layout.component.d.ts +83 -0
- package/lib/components/base-layout/base-layout.types.d.ts +26 -0
- package/lib/components/base-layout/index.d.ts +13 -0
- package/lib/components/button/button-area.component.d.ts +88 -0
- package/lib/components/button/button.component.d.ts +55 -0
- package/lib/components/button/button.types.d.ts +70 -0
- package/lib/components/button/index.d.ts +15 -0
- package/lib/components/crud-table/crud-table.component.d.ts +143 -0
- package/lib/components/crud-table/crud-table.types.d.ts +207 -0
- package/lib/components/crud-table/index.d.ts +15 -0
- package/lib/components/form-builder/adapters/it-date-adapter.d.ts +32 -0
- package/lib/components/form-builder/directives/currency-input.directive.d.ts +48 -0
- package/lib/components/form-builder/form-builder.component.d.ts +183 -0
- package/lib/components/form-builder/form-wizard.component.d.ts +87 -0
- package/lib/components/form-builder/index.d.ts +13 -0
- package/lib/components/form-builder/services/form-condition.service.d.ts +46 -0
- package/lib/components/form-builder/services/form-validation.service.d.ts +63 -0
- package/lib/components/form-builder/services/location.service.d.ts +83 -0
- package/lib/components/form-builder/services/wizard-sync.service.d.ts +63 -0
- package/lib/components/form-builder/sub-components/error-summary/form-error-summary.component.d.ts +28 -0
- package/lib/components/form-builder/sub-components/file-input/file-input.component.d.ts +41 -0
- package/lib/components/form-builder/sub-components/specifica-territoriale/specifica-territoriale.component.d.ts +145 -0
- package/lib/components/form-builder/sub-components/table-territoriale/table-territoriale.component.d.ts +108 -0
- package/lib/components/form-builder/types/condition.types.d.ts +51 -0
- package/lib/components/form-builder/types/field.types.d.ts +288 -0
- package/lib/components/form-builder/types/index.d.ts +5 -0
- package/lib/components/form-builder/types/schema.types.d.ts +227 -0
- package/lib/components/form-builder/types/territoriale.types.d.ts +170 -0
- package/lib/components/form-builder/types/validation.types.d.ts +174 -0
- package/lib/components/form-builder-editor/form-builder-editor.component.d.ts +117 -0
- package/lib/components/form-builder-editor/form-builder-editor.service.d.ts +38 -0
- package/lib/components/form-builder-editor/index.d.ts +15 -0
- package/lib/components/form-builder-editor/services/editor-persistence.service.d.ts +42 -0
- package/lib/components/form-builder-editor/services/editor-state.service.d.ts +66 -0
- package/lib/components/form-builder-editor/services/field-factory.service.d.ts +28 -0
- package/lib/components/form-builder-editor/sub-components/condition-editor/condition-editor.component.d.ts +139 -0
- package/lib/components/form-builder-editor/sub-components/editor-toolbar/editor-toolbar.component.d.ts +43 -0
- package/lib/components/form-builder-editor/sub-components/field-config-panel/field-config-panel.component.d.ts +83 -0
- package/lib/components/form-builder-editor/sub-components/field-palette/field-palette.component.d.ts +40 -0
- package/lib/components/form-builder-editor/sub-components/form-values-panel/form-values-panel.component.d.ts +51 -0
- package/lib/components/form-builder-editor/sub-components/options-editor/options-editor.component.d.ts +63 -0
- package/lib/components/form-builder-editor/sub-components/preview-container/preview-container.component.d.ts +68 -0
- package/lib/components/form-builder-editor/sub-components/section-editor/section-editor.component.d.ts +82 -0
- package/lib/components/form-builder-editor/sub-components/validation-editor/validation-editor.component.d.ts +112 -0
- package/lib/components/form-builder-editor/types/editor.types.d.ts +124 -0
- package/lib/components/layout-builder/index.d.ts +16 -0
- package/lib/components/layout-builder/layout-builder.component.d.ts +85 -0
- package/lib/components/layout-builder/layout-builder.types.d.ts +436 -0
- package/lib/components/layout-builder/layout.service.d.ts +100 -0
- package/lib/components/modal/confirm-dialog.component.d.ts +46 -0
- package/lib/components/modal/index.d.ts +4 -0
- package/lib/components/modal/modal.component.d.ts +44 -0
- package/lib/components/modal/modal.service.d.ts +93 -0
- package/lib/components/modal/modal.types.d.ts +110 -0
- package/lib/components/page-header/breadcrumb.service.d.ts +96 -0
- package/lib/components/page-header/index.d.ts +16 -0
- package/lib/components/page-header/page-header.component.d.ts +59 -0
- package/lib/components/page-header/page-header.types.d.ts +96 -0
- package/lib/components/table/index.d.ts +2 -0
- package/lib/components/table/paginated-table.component.d.ts +85 -0
- package/lib/components/table/table.types.d.ts +81 -0
- package/lib/core/types/index.d.ts +57 -0
- package/lib/core/utils/index.d.ts +29 -0
- package/package.json +44 -0
- package/public-api.d.ts +22 -0
|
@@ -0,0 +1,824 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, ChangeDetectorRef, ViewEncapsulation, inject, ElementRef, } from '@angular/core';
|
|
2
|
+
import { FormGroup, FormControl, ReactiveFormsModule, } from '@angular/forms';
|
|
3
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
4
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
5
|
+
import { MatInputModule } from '@angular/material/input';
|
|
6
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
7
|
+
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
8
|
+
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
9
|
+
import { MatRadioModule } from '@angular/material/radio';
|
|
10
|
+
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
11
|
+
import { MatNativeDateModule, ErrorStateMatcher, DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE, } from '@angular/material/core';
|
|
12
|
+
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
|
13
|
+
import { MatChipsModule } from '@angular/material/chips';
|
|
14
|
+
import { MatExpansionModule } from '@angular/material/expansion';
|
|
15
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
16
|
+
import { LucideAngularModule } from 'lucide-angular';
|
|
17
|
+
import { Subject } from 'rxjs';
|
|
18
|
+
import { debounceTime, takeUntil } from 'rxjs/operators';
|
|
19
|
+
import { UiButtonAreaComponent } from '../../components/button/button-area.component';
|
|
20
|
+
import { UiFormConditionService } from './services/form-condition.service';
|
|
21
|
+
import { UiFormValidationService } from './services/form-validation.service';
|
|
22
|
+
import { UiFormErrorSummaryComponent } from './sub-components/error-summary/form-error-summary.component';
|
|
23
|
+
import { UiFileInputComponent } from './sub-components/file-input/file-input.component';
|
|
24
|
+
import { UiSpecificaTerritorialeComponent } from './sub-components/specifica-territoriale/specifica-territoriale.component';
|
|
25
|
+
import { UiTableTerritorialeComponent } from './sub-components/table-territoriale/table-territoriale.component';
|
|
26
|
+
import { UiCurrencyInputDirective } from './directives/currency-input.directive';
|
|
27
|
+
import { UiItalianDateAdapter, UI_IT_DATE_FORMATS } from './adapters/it-date-adapter';
|
|
28
|
+
import * as i0 from "@angular/core";
|
|
29
|
+
import * as i1 from "@angular/forms";
|
|
30
|
+
import * as i2 from "@angular/material/form-field";
|
|
31
|
+
import * as i3 from "@angular/material/input";
|
|
32
|
+
import * as i4 from "@angular/material/select";
|
|
33
|
+
import * as i5 from "@angular/material/core";
|
|
34
|
+
import * as i6 from "@angular/material/checkbox";
|
|
35
|
+
import * as i7 from "@angular/material/slide-toggle";
|
|
36
|
+
import * as i8 from "@angular/material/radio";
|
|
37
|
+
import * as i9 from "@angular/material/datepicker";
|
|
38
|
+
import * as i10 from "@angular/material/autocomplete";
|
|
39
|
+
import * as i11 from "@angular/material/chips";
|
|
40
|
+
import * as i12 from "@angular/material/expansion";
|
|
41
|
+
import * as i13 from "@angular/material/tooltip";
|
|
42
|
+
import * as i14 from "lucide-angular";
|
|
43
|
+
/**
|
|
44
|
+
* ErrorStateMatcher custom per il form builder.
|
|
45
|
+
* Mostra lo stato di errore quando il campo e invalido E (touched OPPURE dirty).
|
|
46
|
+
* Sostituisce il matcher di default di Angular Material.
|
|
47
|
+
*/
|
|
48
|
+
export class UiFormErrorStateMatcher {
|
|
49
|
+
isErrorState(control, form) {
|
|
50
|
+
return !!(control && control.invalid && (control.touched || control.dirty));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Form builder data-driven e schema-based.
|
|
55
|
+
*
|
|
56
|
+
* Genera un form completo a partire da uno schema dichiarativo (`UiFormSchema`).
|
|
57
|
+
* Supporta 16 tipi di campo, validazione condizionale, cross-field validation,
|
|
58
|
+
* date con offset, opzioni dipendenti/dinamiche, sezioni collapsibili,
|
|
59
|
+
* error summary con scroll-to-field e button override.
|
|
60
|
+
*
|
|
61
|
+
* @selector ui-form-builder
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```html
|
|
65
|
+
* <ui-form-builder
|
|
66
|
+
* [schema]="formSchema"
|
|
67
|
+
* [initialData]="userData"
|
|
68
|
+
* (formSubmit)="onSubmit($event)"
|
|
69
|
+
* (valueChange)="onChange($event)"
|
|
70
|
+
* />
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export class UiFormBuilderComponent {
|
|
74
|
+
constructor() {
|
|
75
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
76
|
+
this.elRef = inject(ElementRef);
|
|
77
|
+
this.conditionService = inject(UiFormConditionService);
|
|
78
|
+
this.validationService = inject(UiFormValidationService);
|
|
79
|
+
this.destroy$ = new Subject();
|
|
80
|
+
/** ID schema corrente -- usato per evitare rebuild superflui. */
|
|
81
|
+
this.currentSchemaId = null;
|
|
82
|
+
/** Dati iniziali per la prevalorizzazione. */
|
|
83
|
+
this.initialData = {};
|
|
84
|
+
/** Modalita sola lettura globale. */
|
|
85
|
+
this.readonly = false;
|
|
86
|
+
/** Stato disabilitato globale. */
|
|
87
|
+
this.disabled = false;
|
|
88
|
+
/** Chiave del pulsante in stato di caricamento. */
|
|
89
|
+
this.loadingFor = null;
|
|
90
|
+
// ─── Output ─────────────────────────────────────────────────────
|
|
91
|
+
this.valueChange = new EventEmitter();
|
|
92
|
+
this.validationChange = new EventEmitter();
|
|
93
|
+
this.formSubmit = new EventEmitter();
|
|
94
|
+
this.formReset = new EventEmitter();
|
|
95
|
+
this.customEvent = new EventEmitter();
|
|
96
|
+
/** Mappa campo -> opzioni filtrate (per autocomplete e dependent). */
|
|
97
|
+
this.filteredOptions = {};
|
|
98
|
+
/** Campi prevalorizzati (mostrano errori subito). */
|
|
99
|
+
this.prevalorizedFields = new Set();
|
|
100
|
+
/** Mappa campo -> regole di validazione (per lookup rapido). */
|
|
101
|
+
this.validationRulesMap = {};
|
|
102
|
+
/** Mappa dipendenze: campoTarget -> campi dipendenti. */
|
|
103
|
+
this.fieldDependencies = new Map();
|
|
104
|
+
/** Flag per evitare emissioni durante l'inizializzazione. */
|
|
105
|
+
this.isInitializing = true;
|
|
106
|
+
}
|
|
107
|
+
// ─── Getter calcolati ───────────────────────────────────────────
|
|
108
|
+
get allFields() {
|
|
109
|
+
return this.schema?.sections?.flatMap((s) => s.fields) || [];
|
|
110
|
+
}
|
|
111
|
+
get formErrors() {
|
|
112
|
+
return this.getDetailedFormErrors();
|
|
113
|
+
}
|
|
114
|
+
get formButtons() {
|
|
115
|
+
if (this.buttonsOverride)
|
|
116
|
+
return this.buttonsOverride;
|
|
117
|
+
if (!this.schema?.config?.showButtons && this.schema?.config?.showButtons !== undefined)
|
|
118
|
+
return [];
|
|
119
|
+
return [
|
|
120
|
+
{ label: this.schema?.config?.buttonLabels?.reset || 'Reset', variant: 'outline', action: () => this.onReset() },
|
|
121
|
+
{
|
|
122
|
+
label: this.schema?.config?.buttonLabels?.submit || 'Invia',
|
|
123
|
+
variant: 'primary',
|
|
124
|
+
action: () => this.onSubmit(),
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
// ─── Lifecycle ──────────────────────────────────────────────────
|
|
129
|
+
//
|
|
130
|
+
// ORDINE LIFECYCLE ANGULAR:
|
|
131
|
+
// 1. constructor
|
|
132
|
+
// 2. ngOnChanges (prima chiamata con valori iniziali)
|
|
133
|
+
// 3. ngOnInit
|
|
134
|
+
// 4. ngAfterViewInit
|
|
135
|
+
// 5. ngOnChanges (chiamate successive per cambio input)
|
|
136
|
+
//
|
|
137
|
+
// PER IL WIZARD:
|
|
138
|
+
// Il wizard crea un nuovo schema (con id diverso) ad ogni cambio step.
|
|
139
|
+
// Lo schema viene cachato nel wizard per evitare rebuild infiniti.
|
|
140
|
+
// ngOnChanges rileva il cambio di schema.id e ricostruisce il form.
|
|
141
|
+
ngOnInit() {
|
|
142
|
+
// [DEBUG] Verifica che lo schema sia disponibile al momento di ngOnInit
|
|
143
|
+
console.debug('[UiFormBuilder] ngOnInit - schema:', this.schema?.id, '| formGroup definito:', !!this.formGroup);
|
|
144
|
+
if (!this.schema) {
|
|
145
|
+
console.warn('[UiFormBuilder] ngOnInit - schema non presente, il form non verra costruito ora');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.initFormFromSchema();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Rileva cambiamenti sugli @Input.
|
|
152
|
+
* Ricostruisce il form quando cambia lo schema (id diverso).
|
|
153
|
+
* Cruciale per il wizard che cambia schema ad ogni step.
|
|
154
|
+
*/
|
|
155
|
+
ngOnChanges(changes) {
|
|
156
|
+
// [DEBUG] Log di tutti i cambiamenti degli Input
|
|
157
|
+
const changedInputs = Object.keys(changes)
|
|
158
|
+
.map((k) => `${k}(firstChange=${changes[k].firstChange})`)
|
|
159
|
+
.join(', ');
|
|
160
|
+
console.debug('[UiFormBuilder] ngOnChanges -', changedInputs);
|
|
161
|
+
if (changes['schema'] && !changes['schema'].firstChange) {
|
|
162
|
+
const newSchema = changes['schema'].currentValue;
|
|
163
|
+
console.debug('[UiFormBuilder] ngOnChanges - nuovo schema:', newSchema?.id, '| corrente:', this.currentSchemaId);
|
|
164
|
+
if (newSchema && newSchema.id !== this.currentSchemaId) {
|
|
165
|
+
console.debug('[UiFormBuilder] ngOnChanges - REBUILD: id diverso, ricostruisco il form');
|
|
166
|
+
this.destroyForm();
|
|
167
|
+
this.initFormFromSchema();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Aggiorna lo stato disabled/readonly globalmente
|
|
171
|
+
if (changes['disabled'] && !changes['disabled'].firstChange && this.formGroup) {
|
|
172
|
+
this.allFields.forEach((field) => {
|
|
173
|
+
const control = this.formGroup.get(field.key);
|
|
174
|
+
if (!control)
|
|
175
|
+
return;
|
|
176
|
+
if (this.disabled && control.enabled) {
|
|
177
|
+
control.disable({ emitEvent: false });
|
|
178
|
+
}
|
|
179
|
+
else if (!this.disabled && control.disabled && !field.disabled) {
|
|
180
|
+
control.enable({ emitEvent: false });
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
ngAfterViewInit() {
|
|
186
|
+
// [DEBUG] Verifica stato dopo la prima renderizzazione della vista
|
|
187
|
+
console.debug('[UiFormBuilder] ngAfterViewInit - formGroup definito:', !!this.formGroup, '| campi:', this.formGroup ? Object.keys(this.formGroup.controls).length : 0);
|
|
188
|
+
}
|
|
189
|
+
ngOnDestroy() {
|
|
190
|
+
console.debug('[UiFormBuilder] ngOnDestroy - schema:', this.currentSchemaId);
|
|
191
|
+
this.destroy$.next();
|
|
192
|
+
this.destroy$.complete();
|
|
193
|
+
this.valueChangeSub?.unsubscribe();
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Inizializza il form dallo schema corrente.
|
|
197
|
+
* Estratto per poter essere richiamato sia da ngOnInit che da ngOnChanges.
|
|
198
|
+
*/
|
|
199
|
+
initFormFromSchema() {
|
|
200
|
+
if (!this.schema) {
|
|
201
|
+
console.warn('[UiFormBuilder] initFormFromSchema - schema nullo, skip');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
console.debug('[UiFormBuilder] initFormFromSchema - costruisco form per:', this.schema.id, '| sezioni:', this.schema.sections?.length, '| campi totali:', this.schema.sections?.reduce((sum, s) => sum + s.fields.length, 0));
|
|
205
|
+
this.currentSchemaId = this.schema.id;
|
|
206
|
+
this.buildForm();
|
|
207
|
+
this.isInitializing = false;
|
|
208
|
+
console.debug('[UiFormBuilder] initFormFromSchema - form costruito. Controls:', Object.keys(this.formGroup.controls));
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Pulisce lo stato del form prima di un rebuild.
|
|
212
|
+
*/
|
|
213
|
+
destroyForm() {
|
|
214
|
+
console.debug('[UiFormBuilder] destroyForm - pulizia stato per schema:', this.currentSchemaId);
|
|
215
|
+
this.valueChangeSub?.unsubscribe();
|
|
216
|
+
this.valueChangeSub = undefined;
|
|
217
|
+
this.filteredOptions = {};
|
|
218
|
+
this.prevalorizedFields.clear();
|
|
219
|
+
this.validationRulesMap = {};
|
|
220
|
+
this.fieldDependencies.clear();
|
|
221
|
+
this.isInitializing = true;
|
|
222
|
+
this.currentSchemaId = null;
|
|
223
|
+
}
|
|
224
|
+
// ─── Costruzione form ───────────────────────────────────────────
|
|
225
|
+
buildForm() {
|
|
226
|
+
const controls = {};
|
|
227
|
+
for (const field of this.allFields) {
|
|
228
|
+
const initialValue = this.initialData?.[field.key] ?? field.defaultValue ?? null;
|
|
229
|
+
const isDisabled = field.disabled || this.disabled;
|
|
230
|
+
// Crea il FormControl
|
|
231
|
+
const control = new FormControl({ value: initialValue, disabled: isDisabled });
|
|
232
|
+
// Registra le regole di validazione
|
|
233
|
+
if (field.validation?.length) {
|
|
234
|
+
this.validationRulesMap[field.key] = field.validation;
|
|
235
|
+
}
|
|
236
|
+
// Traccia campi prevalorizzati
|
|
237
|
+
if (initialValue != null && initialValue !== '' && initialValue !== false) {
|
|
238
|
+
this.prevalorizedFields.add(field.key);
|
|
239
|
+
}
|
|
240
|
+
// Inizializza opzioni filtrate per select/multiselect
|
|
241
|
+
if (field.options && !this.isObservable(field.options)) {
|
|
242
|
+
this.filteredOptions[field.key] = [...field.options];
|
|
243
|
+
}
|
|
244
|
+
controls[field.key] = control;
|
|
245
|
+
}
|
|
246
|
+
this.formGroup = new FormGroup(controls);
|
|
247
|
+
// Applica validatori iniziali
|
|
248
|
+
this.applyAllValidators();
|
|
249
|
+
// Registra dipendenze
|
|
250
|
+
this.buildDependencyMap();
|
|
251
|
+
// Sottoscrivi ai cambiamenti di valore
|
|
252
|
+
this.valueChangeSub = this.formGroup.valueChanges.pipe(debounceTime(50), takeUntil(this.destroy$)).subscribe(() => {
|
|
253
|
+
this.evaluateAllConditions();
|
|
254
|
+
this.updateDependentOptions();
|
|
255
|
+
if (!this.isInitializing) {
|
|
256
|
+
this.valueChange.emit(this.getFormValue());
|
|
257
|
+
this.validationChange.emit(this.getValidationState());
|
|
258
|
+
this.cdr.markForCheck();
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
// Valutazione condizioni iniziale
|
|
262
|
+
this.evaluateAllConditions();
|
|
263
|
+
}
|
|
264
|
+
// ─── Validazione ────────────────────────────────────────────────
|
|
265
|
+
applyAllValidators() {
|
|
266
|
+
for (const field of this.allFields) {
|
|
267
|
+
if (field.validation?.length) {
|
|
268
|
+
const formData = this.formGroup.getRawValue();
|
|
269
|
+
this.validationService.updateFieldValidators(this.formGroup.get(field.key), field.validation, formData);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// ─── Condizioni ─────────────────────────────────────────────────
|
|
274
|
+
evaluateAllConditions() {
|
|
275
|
+
const formData = this.formGroup.getRawValue();
|
|
276
|
+
for (const field of this.allFields) {
|
|
277
|
+
// Visibilita
|
|
278
|
+
if (field.conditions?.length) {
|
|
279
|
+
const visible = this.conditionService.evaluateConditions(field.conditions, formData);
|
|
280
|
+
const control = this.formGroup.get(field.key);
|
|
281
|
+
if (control) {
|
|
282
|
+
if (!visible && control.enabled) {
|
|
283
|
+
control.disable({ emitEvent: false });
|
|
284
|
+
if (!field.freezeValueOnDisable) {
|
|
285
|
+
control.setValue(field.defaultValue ?? null, { emitEvent: false });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else if (visible && control.disabled && !field.disabled && !this.disabled) {
|
|
289
|
+
control.enable({ emitEvent: false });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Disabilita condizionale
|
|
294
|
+
if (field.disableConditions?.length && !field.conditions?.length) {
|
|
295
|
+
const shouldDisable = this.conditionService.evaluateConditions(field.disableConditions, formData);
|
|
296
|
+
const control = this.formGroup.get(field.key);
|
|
297
|
+
if (control) {
|
|
298
|
+
if (shouldDisable && control.enabled) {
|
|
299
|
+
control.disable({ emitEvent: false });
|
|
300
|
+
if (!field.freezeValueOnDisable) {
|
|
301
|
+
control.setValue(field.defaultValue ?? null, { emitEvent: false });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
else if (!shouldDisable && control.disabled && !field.disabled && !this.disabled) {
|
|
305
|
+
control.enable({ emitEvent: false });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Aggiorna validatori condizionali
|
|
310
|
+
if (field.validation?.some((r) => r.conditions?.length)) {
|
|
311
|
+
this.validationService.updateFieldValidators(this.formGroup.get(field.key), field.validation, formData);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// ─── Dipendenze opzioni ─────────────────────────────────────────
|
|
316
|
+
buildDependencyMap() {
|
|
317
|
+
for (const field of this.allFields) {
|
|
318
|
+
if (field.dependentOptions?.dependsOn) {
|
|
319
|
+
const depKey = field.dependentOptions.dependsOn;
|
|
320
|
+
if (!this.fieldDependencies.has(depKey)) {
|
|
321
|
+
this.fieldDependencies.set(depKey, new Set());
|
|
322
|
+
}
|
|
323
|
+
this.fieldDependencies.get(depKey).add(field.key);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
updateDependentOptions() {
|
|
328
|
+
const formData = this.formGroup.getRawValue();
|
|
329
|
+
for (const field of this.allFields) {
|
|
330
|
+
// Dynamic options
|
|
331
|
+
if (field.dynamicOptions) {
|
|
332
|
+
if (field.dynamicOptions.type === 'year-range') {
|
|
333
|
+
const min = field.dynamicOptions.minYear || new Date().getFullYear() - 10;
|
|
334
|
+
const max = field.dynamicOptions.maxYear || new Date().getFullYear();
|
|
335
|
+
const opts = [];
|
|
336
|
+
for (let y = max; y >= min; y--) {
|
|
337
|
+
opts.push({ value: y, label: String(y) });
|
|
338
|
+
}
|
|
339
|
+
this.filteredOptions[field.key] = opts;
|
|
340
|
+
}
|
|
341
|
+
else if (field.dynamicOptions.generator) {
|
|
342
|
+
this.filteredOptions[field.key] = field.dynamicOptions.generator(formData);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Dependent options
|
|
346
|
+
if (field.dependentOptions && field.options && !this.isObservable(field.options)) {
|
|
347
|
+
const dep = field.dependentOptions;
|
|
348
|
+
const depValue = formData[dep.dependsOn];
|
|
349
|
+
const allOptions = field.options;
|
|
350
|
+
if (depValue == null || depValue === '') {
|
|
351
|
+
this.filteredOptions[field.key] = [...allOptions];
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
this.filteredOptions[field.key] = allOptions.filter((opt) => {
|
|
355
|
+
if (dep.filterType === 'custom' && dep.filterFn) {
|
|
356
|
+
return dep.filterFn(opt, depValue, formData);
|
|
357
|
+
}
|
|
358
|
+
const optVal = Number(opt.value);
|
|
359
|
+
const depVal = Number(depValue);
|
|
360
|
+
if (isNaN(optVal) || isNaN(depVal))
|
|
361
|
+
return true;
|
|
362
|
+
switch (dep.filterType) {
|
|
363
|
+
case 'greater_than':
|
|
364
|
+
return optVal > depVal;
|
|
365
|
+
case 'greater_equal':
|
|
366
|
+
return optVal >= depVal;
|
|
367
|
+
case 'less_than':
|
|
368
|
+
return optVal < depVal;
|
|
369
|
+
case 'less_equal':
|
|
370
|
+
return optVal <= depVal;
|
|
371
|
+
case 'equals':
|
|
372
|
+
return optVal === depVal;
|
|
373
|
+
case 'not_equals':
|
|
374
|
+
return optVal !== depVal;
|
|
375
|
+
default:
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// ─── API pubblica ───────────────────────────────────────────────
|
|
383
|
+
/** Verifica se un campo e visibile. */
|
|
384
|
+
isFieldVisible(fieldKey) {
|
|
385
|
+
const field = this.allFields.find((f) => f.key === fieldKey);
|
|
386
|
+
if (!field?.conditions?.length)
|
|
387
|
+
return true;
|
|
388
|
+
return this.conditionService.evaluateConditions(field.conditions, this.formGroup.getRawValue());
|
|
389
|
+
}
|
|
390
|
+
/** Verifica se un campo e obbligatorio (include validazione condizionale). */
|
|
391
|
+
isFieldRequired(fieldKey) {
|
|
392
|
+
const field = this.allFields.find((f) => f.key === fieldKey);
|
|
393
|
+
if (field?.required)
|
|
394
|
+
return true;
|
|
395
|
+
const rules = field?.validation || [];
|
|
396
|
+
const formData = this.formGroup.getRawValue();
|
|
397
|
+
return rules.some((r) => {
|
|
398
|
+
if (r.type !== 'required')
|
|
399
|
+
return false;
|
|
400
|
+
if (!r.conditions?.length)
|
|
401
|
+
return true;
|
|
402
|
+
return this.conditionService.evaluateConditions(r.conditions, formData);
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
/** Verifica se una sezione e visibile. */
|
|
406
|
+
isSectionVisible(sectionId) {
|
|
407
|
+
const section = this.schema.sections.find((s) => s.id === sectionId);
|
|
408
|
+
if (!section?.conditions?.length)
|
|
409
|
+
return true;
|
|
410
|
+
return this.conditionService.evaluateConditions(section.conditions, this.formGroup.getRawValue());
|
|
411
|
+
}
|
|
412
|
+
/** Verifica se mostrare errori per un campo. */
|
|
413
|
+
shouldShowFieldErrors(fieldKey) {
|
|
414
|
+
const control = this.formGroup.get(fieldKey);
|
|
415
|
+
if (!control)
|
|
416
|
+
return false;
|
|
417
|
+
return control.invalid && (control.dirty || control.touched || this.prevalorizedFields.has(fieldKey));
|
|
418
|
+
}
|
|
419
|
+
/** Errori di un campo come array di messaggi. */
|
|
420
|
+
getFieldErrors(fieldKey) {
|
|
421
|
+
const control = this.formGroup.get(fieldKey);
|
|
422
|
+
if (!control?.errors)
|
|
423
|
+
return [];
|
|
424
|
+
const field = this.allFields.find((f) => f.key === fieldKey);
|
|
425
|
+
const rules = field?.validation || [];
|
|
426
|
+
const messages = [];
|
|
427
|
+
for (const [errorKey] of Object.entries(control.errors)) {
|
|
428
|
+
// Angular usa chiavi lowercase (es. "minlength"), lo schema usa camelCase (es. "minLength").
|
|
429
|
+
// Normalizziamo per trovare la corrispondenza corretta.
|
|
430
|
+
const rule = rules.find((r) => this.normalizeValidationKey(r.type) === errorKey || (r.type === 'crossField' && errorKey === 'crossField'));
|
|
431
|
+
if (rule?.message) {
|
|
432
|
+
messages.push(rule.message);
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
messages.push(this.getDefaultErrorMessage(errorKey, control.errors[errorKey]));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return messages;
|
|
439
|
+
}
|
|
440
|
+
/** Valore completo del form (include campi disabilitati). */
|
|
441
|
+
getFormValue() {
|
|
442
|
+
return this.formGroup.getRawValue();
|
|
443
|
+
}
|
|
444
|
+
/** Il form e valido. */
|
|
445
|
+
isFormValid() {
|
|
446
|
+
return this.formGroup.valid;
|
|
447
|
+
}
|
|
448
|
+
/** Stato di validazione completo. */
|
|
449
|
+
getValidationState() {
|
|
450
|
+
const errors = {};
|
|
451
|
+
const dirty = [];
|
|
452
|
+
const touched = [];
|
|
453
|
+
for (const field of this.allFields) {
|
|
454
|
+
const control = this.formGroup.get(field.key);
|
|
455
|
+
if (!control)
|
|
456
|
+
continue;
|
|
457
|
+
if (control.dirty)
|
|
458
|
+
dirty.push(field.key);
|
|
459
|
+
if (control.touched)
|
|
460
|
+
touched.push(field.key);
|
|
461
|
+
const fieldErrors = this.getFieldErrors(field.key);
|
|
462
|
+
if (fieldErrors.length)
|
|
463
|
+
errors[field.key] = fieldErrors;
|
|
464
|
+
}
|
|
465
|
+
return { valid: this.formGroup.valid, errors, dirty, touched };
|
|
466
|
+
}
|
|
467
|
+
/** Errori dettagliati per l'error summary. */
|
|
468
|
+
getDetailedFormErrors() {
|
|
469
|
+
const details = [];
|
|
470
|
+
for (const field of this.allFields) {
|
|
471
|
+
if (field.type === 'flag' || field.type === 'divider')
|
|
472
|
+
continue;
|
|
473
|
+
if (!this.isFieldVisible(field.key))
|
|
474
|
+
continue;
|
|
475
|
+
const control = this.formGroup.get(field.key);
|
|
476
|
+
if (!control?.errors)
|
|
477
|
+
continue;
|
|
478
|
+
const errors = this.getFieldErrors(field.key);
|
|
479
|
+
if (errors.length) {
|
|
480
|
+
details.push({ fieldKey: field.key, fieldLabel: field.label, errors });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return details;
|
|
484
|
+
}
|
|
485
|
+
/** Conta errori totali. */
|
|
486
|
+
getFormErrorsCount() {
|
|
487
|
+
return this.getDetailedFormErrors().reduce((sum, d) => sum + d.errors.length, 0);
|
|
488
|
+
}
|
|
489
|
+
/** Controlla se ci sono errori. */
|
|
490
|
+
hasFormErrors() {
|
|
491
|
+
return this.getFormErrorsCount() > 0;
|
|
492
|
+
}
|
|
493
|
+
/** Controllo per un campo. */
|
|
494
|
+
getFieldControl(key) {
|
|
495
|
+
return this.formGroup.get(key) ?? null;
|
|
496
|
+
}
|
|
497
|
+
/** Aggiorna il valore di un campo programmaticamente. */
|
|
498
|
+
updateFieldValue(key, value) {
|
|
499
|
+
const control = this.formGroup.get(key);
|
|
500
|
+
if (control) {
|
|
501
|
+
control.setValue(value);
|
|
502
|
+
control.markAsDirty();
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/** Forza la validazione di tutti i campi. */
|
|
506
|
+
validateAllFields() {
|
|
507
|
+
Object.keys(this.formGroup.controls).forEach((key) => {
|
|
508
|
+
const control = this.formGroup.get(key);
|
|
509
|
+
control?.markAsTouched();
|
|
510
|
+
control?.markAsDirty();
|
|
511
|
+
control?.updateValueAndValidity();
|
|
512
|
+
});
|
|
513
|
+
this.cdr.markForCheck();
|
|
514
|
+
}
|
|
515
|
+
/** Scrolla al campo e lo evidenzia. */
|
|
516
|
+
scrollToField(fieldKey) {
|
|
517
|
+
// Forza lo stato dirty e touched per mostrare immediatamente gli errori
|
|
518
|
+
const control = this.formGroup.get(fieldKey);
|
|
519
|
+
if (control) {
|
|
520
|
+
control.markAsTouched();
|
|
521
|
+
control.markAsDirty();
|
|
522
|
+
control.updateValueAndValidity();
|
|
523
|
+
}
|
|
524
|
+
// Delay per lasciare che Angular Material aggiorni il DOM
|
|
525
|
+
setTimeout(() => {
|
|
526
|
+
// Cerca l'elemento tramite diversi selettori in ordine di priorita
|
|
527
|
+
const selectors = [`[data-field-key="${fieldKey}"]`, `[formControlName="${fieldKey}"]`];
|
|
528
|
+
let targetElement = null;
|
|
529
|
+
for (const sel of selectors) {
|
|
530
|
+
targetElement = this.elRef.nativeElement.querySelector(sel);
|
|
531
|
+
if (targetElement)
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
if (!targetElement)
|
|
535
|
+
return;
|
|
536
|
+
// Se trovato il formControlName, risali al wrapper
|
|
537
|
+
if (targetElement.hasAttribute('formControlName')) {
|
|
538
|
+
const wrapper = targetElement.closest('.ui-form-builder__field-wrapper, mat-form-field');
|
|
539
|
+
if (wrapper)
|
|
540
|
+
targetElement = wrapper;
|
|
541
|
+
}
|
|
542
|
+
targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
543
|
+
// Aggiungi classe highlight e rimuovila dopo l'animazione
|
|
544
|
+
targetElement.classList.add('ui-form-field--highlight');
|
|
545
|
+
setTimeout(() => targetElement.classList.remove('ui-form-field--highlight'), 2000);
|
|
546
|
+
}, 100);
|
|
547
|
+
}
|
|
548
|
+
/** Submit del form. */
|
|
549
|
+
onSubmit() {
|
|
550
|
+
this.validateAllFields();
|
|
551
|
+
if (this.formGroup.valid) {
|
|
552
|
+
this.formSubmit.emit(this.getFormValue());
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
/** Reset del form. */
|
|
556
|
+
onReset() {
|
|
557
|
+
// Resetta a valori iniziali o default
|
|
558
|
+
for (const field of this.allFields) {
|
|
559
|
+
const value = this.initialData?.[field.key] ?? field.defaultValue ?? null;
|
|
560
|
+
const control = this.formGroup.get(field.key);
|
|
561
|
+
control?.setValue(value, { emitEvent: false });
|
|
562
|
+
control?.markAsPristine();
|
|
563
|
+
control?.markAsUntouched();
|
|
564
|
+
}
|
|
565
|
+
this.formGroup.updateValueAndValidity();
|
|
566
|
+
this.formReset.emit();
|
|
567
|
+
this.cdr.markForCheck();
|
|
568
|
+
}
|
|
569
|
+
// ─── Template helpers ───────────────────────────────────────────
|
|
570
|
+
/** Opzioni per un campo (filtrando per autocomplete query). */
|
|
571
|
+
getFieldOptions(field) {
|
|
572
|
+
return this.filteredOptions[field.key] || [];
|
|
573
|
+
}
|
|
574
|
+
/** Filtra opzioni per autocomplete. */
|
|
575
|
+
filterOptions(field, query) {
|
|
576
|
+
if (!query) {
|
|
577
|
+
if (field.options && !this.isObservable(field.options)) {
|
|
578
|
+
this.filteredOptions[field.key] = [...field.options];
|
|
579
|
+
}
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const lowerQuery = query.toLowerCase();
|
|
583
|
+
if (field.asyncOptions) {
|
|
584
|
+
field.asyncOptions(query).then((opts) => {
|
|
585
|
+
this.filteredOptions[field.key] = opts;
|
|
586
|
+
this.cdr.markForCheck();
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
else if (field.options && !this.isObservable(field.options)) {
|
|
590
|
+
this.filteredOptions[field.key] = field.options.filter((o) => o.label.toLowerCase().includes(lowerQuery));
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
/** Display fn per autocomplete. */
|
|
594
|
+
displayFn(options) {
|
|
595
|
+
return (value) => {
|
|
596
|
+
const opt = options.find((o) => o.value === value);
|
|
597
|
+
return opt?.label || '';
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
/** Seleziona tutte le opzioni per multiselect. */
|
|
601
|
+
selectAll(field) {
|
|
602
|
+
const options = this.getFieldOptions(field);
|
|
603
|
+
const allValues = options.filter((o) => !o.disabled).map((o) => o.value);
|
|
604
|
+
this.formGroup.get(field.key)?.setValue(allValues);
|
|
605
|
+
}
|
|
606
|
+
/** Aggiunge un chip al freemultiselect (da token: Enter o virgola). */
|
|
607
|
+
addFreeChip(field, event) {
|
|
608
|
+
const value = (event.value || '').trim();
|
|
609
|
+
if (!value)
|
|
610
|
+
return;
|
|
611
|
+
const current = this.formGroup.get(field.key)?.value || [];
|
|
612
|
+
if (!current.includes(value)) {
|
|
613
|
+
this.formGroup.get(field.key)?.setValue([...current, value]);
|
|
614
|
+
}
|
|
615
|
+
event.chipInput?.clear();
|
|
616
|
+
}
|
|
617
|
+
/** Aggiunge un chip al freemultiselect sull'evento blur dell'input. */
|
|
618
|
+
addFreeChipOnBlur(field, event) {
|
|
619
|
+
const input = event.target;
|
|
620
|
+
const value = (input.value || '').trim();
|
|
621
|
+
if (!value)
|
|
622
|
+
return;
|
|
623
|
+
const current = this.formGroup.get(field.key)?.value || [];
|
|
624
|
+
if (!current.includes(value)) {
|
|
625
|
+
this.formGroup.get(field.key)?.setValue([...current, value]);
|
|
626
|
+
}
|
|
627
|
+
input.value = '';
|
|
628
|
+
}
|
|
629
|
+
/** Rimuove un chip dal freemultiselect. */
|
|
630
|
+
removeFreeChip(field, chipValue) {
|
|
631
|
+
const current = this.formGroup.get(field.key)?.value || [];
|
|
632
|
+
const protected_ = field.metadata?.['cantDeleteList'] || [];
|
|
633
|
+
if (protected_.includes(chipValue))
|
|
634
|
+
return;
|
|
635
|
+
this.formGroup.get(field.key)?.setValue(current.filter((v) => v !== chipValue));
|
|
636
|
+
}
|
|
637
|
+
/** Verifica se un chip e protetto dalla cancellazione. */
|
|
638
|
+
isChipProtected(field, chipValue) {
|
|
639
|
+
return (field.metadata?.['cantDeleteList'] || []).includes(chipValue);
|
|
640
|
+
}
|
|
641
|
+
/** Rimuove chip da multiselect. */
|
|
642
|
+
removeMultiselectChip(field, chipValue) {
|
|
643
|
+
const current = this.formGroup.get(field.key)?.value || [];
|
|
644
|
+
this.formGroup.get(field.key)?.setValue(current.filter((v) => v !== chipValue));
|
|
645
|
+
}
|
|
646
|
+
/** Trova label di un'opzione dal valore. */
|
|
647
|
+
getOptionLabel(field, value) {
|
|
648
|
+
const options = this.getFieldOptions(field);
|
|
649
|
+
return options.find((o) => o.value === value)?.label || String(value);
|
|
650
|
+
}
|
|
651
|
+
/** Tipo di input nativo per il campo. */
|
|
652
|
+
getNativeInputType(field) {
|
|
653
|
+
switch (field.type) {
|
|
654
|
+
case 'password':
|
|
655
|
+
return 'password';
|
|
656
|
+
case 'email':
|
|
657
|
+
return 'email';
|
|
658
|
+
default:
|
|
659
|
+
return 'text';
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Genera le classi CSS per il wrapper di un campo,
|
|
664
|
+
* combinando eventuali cssClasses dallo schema con le classi
|
|
665
|
+
* responsive per il sistema a griglia base-12.
|
|
666
|
+
*
|
|
667
|
+
* Mobile-first: tutti i campi sono span-12 di default (100%).
|
|
668
|
+
* Le classi `ui-col-{breakpoint}-{n}` attivano la larghezza
|
|
669
|
+
* configurata solo dal breakpoint indicato in su.
|
|
670
|
+
*
|
|
671
|
+
* Breakpoint supportati: sm (≥576px), md (≥768px), lg (≥1024px), xl (≥1280px).
|
|
672
|
+
*/
|
|
673
|
+
getFieldWrapperClasses(field) {
|
|
674
|
+
const classes = [];
|
|
675
|
+
if (field.cssClasses?.length) {
|
|
676
|
+
classes.push(...field.cssClasses);
|
|
677
|
+
}
|
|
678
|
+
// layout.columns → classe per il breakpoint md (default desktop)
|
|
679
|
+
if (field.layout?.columns) {
|
|
680
|
+
classes.push(`ui-col-md-${field.layout.columns}`);
|
|
681
|
+
}
|
|
682
|
+
// layout.breakpoints → classi per breakpoint specifici
|
|
683
|
+
if (field.layout?.breakpoints) {
|
|
684
|
+
for (const [bp, cols] of Object.entries(field.layout.breakpoints)) {
|
|
685
|
+
classes.push(`ui-col-${bp}-${cols}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return classes.join(' ');
|
|
689
|
+
}
|
|
690
|
+
/** Character count per textarea/text con maxLength. */
|
|
691
|
+
getCharCount(fieldKey) {
|
|
692
|
+
const field = this.allFields.find((f) => f.key === fieldKey);
|
|
693
|
+
const maxRule = field?.validation?.find((r) => r.type === 'maxLength');
|
|
694
|
+
if (!maxRule?.value)
|
|
695
|
+
return null;
|
|
696
|
+
const control = this.formGroup.get(fieldKey);
|
|
697
|
+
return {
|
|
698
|
+
current: (control?.value || '').length,
|
|
699
|
+
max: Number(maxRule.value),
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
// ─── Utilita private ────────────────────────────────────────────
|
|
703
|
+
isObservable(value) {
|
|
704
|
+
return value && typeof value.subscribe === 'function';
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Normalizza la chiave del tipo di validazione dallo schema (camelCase)
|
|
708
|
+
* alla chiave di errore usata da Angular (lowercase).
|
|
709
|
+
* Es: "minLength" -> "minlength", "maxLength" -> "maxlength".
|
|
710
|
+
*/
|
|
711
|
+
normalizeValidationKey(schemaType) {
|
|
712
|
+
const keyMap = {
|
|
713
|
+
minLength: 'minlength',
|
|
714
|
+
maxLength: 'maxlength',
|
|
715
|
+
};
|
|
716
|
+
return keyMap[schemaType] || schemaType;
|
|
717
|
+
}
|
|
718
|
+
getDefaultErrorMessage(errorKey, errorValue) {
|
|
719
|
+
// Se il valore dell'errore e gia un messaggio descrittivo (stringa),
|
|
720
|
+
// lo restituisce direttamente. Questo supporta i validatori personalizzati
|
|
721
|
+
// (es. location, location-table) che forniscono messaggi espliciti come
|
|
722
|
+
// valore dell'errore (es. { nazioneRequired: 'Seleziona una nazione' }).
|
|
723
|
+
if (typeof errorValue === 'string') {
|
|
724
|
+
return errorValue;
|
|
725
|
+
}
|
|
726
|
+
switch (errorKey) {
|
|
727
|
+
case 'required':
|
|
728
|
+
return 'Campo obbligatorio';
|
|
729
|
+
case 'email':
|
|
730
|
+
return 'Formato email non valido';
|
|
731
|
+
case 'min':
|
|
732
|
+
return `Valore minimo: ${errorValue.min}`;
|
|
733
|
+
case 'max':
|
|
734
|
+
return `Valore massimo: ${errorValue.max}`;
|
|
735
|
+
case 'minlength':
|
|
736
|
+
return `Lunghezza minima: ${errorValue.requiredLength} caratteri`;
|
|
737
|
+
case 'maxlength':
|
|
738
|
+
return `Lunghezza massima: ${errorValue.requiredLength} caratteri`;
|
|
739
|
+
case 'pattern':
|
|
740
|
+
return 'Formato non valido';
|
|
741
|
+
case 'crossField':
|
|
742
|
+
return 'Valore non coerente con il campo correlato';
|
|
743
|
+
case 'date-min':
|
|
744
|
+
return errorValue?.message || 'Data troppo remota';
|
|
745
|
+
case 'date-max':
|
|
746
|
+
return errorValue?.message || 'Data troppo avanzata';
|
|
747
|
+
case 'fileSize':
|
|
748
|
+
return `Dimensione massima: ${errorValue?.maxSizeFormatted}`;
|
|
749
|
+
case 'fileType':
|
|
750
|
+
return 'Formato file non accettato';
|
|
751
|
+
case 'fileCount':
|
|
752
|
+
return `Massimo ${errorValue?.maxCount} file`;
|
|
753
|
+
default:
|
|
754
|
+
return 'Valore non valido';
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiFormBuilderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
758
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: UiFormBuilderComponent, isStandalone: true, selector: "ui-form-builder", inputs: { schema: "schema", initialData: "initialData", readonly: "readonly", disabled: "disabled", buttonsOverride: "buttonsOverride", loadingFor: "loadingFor" }, outputs: { valueChange: "valueChange", validationChange: "validationChange", formSubmit: "formSubmit", formReset: "formReset", customEvent: "customEvent" }, host: { classAttribute: "ui-form-builder-host" }, providers: [
|
|
759
|
+
{ provide: ErrorStateMatcher, useClass: UiFormErrorStateMatcher },
|
|
760
|
+
// Locale italiano per il datepicker Material
|
|
761
|
+
{ provide: MAT_DATE_LOCALE, useValue: 'it-IT' },
|
|
762
|
+
// Adapter personalizzato per parsing DD/MM/YYYY
|
|
763
|
+
{ provide: DateAdapter, useClass: UiItalianDateAdapter },
|
|
764
|
+
// Formati data italiani (DD/MM/YYYY)
|
|
765
|
+
{ provide: MAT_DATE_FORMATS, useValue: UI_IT_DATE_FORMATS },
|
|
766
|
+
], usesOnChanges: true, ngImport: i0, template: "<!--\r\n ============================================================\r\n UI FORM BUILDER - TEMPLATE PRINCIPALE\r\n ============================================================\r\n ARCHITETTURA:\r\n Ogni <ng-template> che contiene direttive [formControlName]\r\n DEVE wrappare il proprio contenuto in un <div [formGroup]>.\r\n Questo perche Angular risolve l'injector di formControlName\r\n dal CONTESTO DI DICHIARAZIONE del template, non dal punto\r\n di inserimento (ngTemplateOutlet).\r\n\r\n Senza questo wrapper, formControlName non trova il\r\n FormGroupDirective e lancia NG01050.\r\n\r\n Ref: versione tailored (ang-ms-webapp) usa lo stesso pattern.\r\n ============================================================\r\n-->\r\n\r\n@if (schema && formGroup) {\r\n <div class=\"ui-form-builder\" [formGroup]=\"formGroup\" [class]=\"schema.config?.cssClasses?.join(' ')\">\r\n\r\n <!-- DEBUG: Verifica che formGroup sia inizializzato -->\r\n <!-- [formGroup] e sul div padre; tutti i formControlName INLINE funzionano -->\r\n\r\n <!-- ==================== HEADER ==================== -->\r\n @if (schema.title || schema.description) {\r\n <div class=\"ui-form-builder__header\">\r\n @if (schema.title) {\r\n <h2 class=\"ui-form-builder__title\">{{ schema.title }}</h2>\r\n }\r\n @if (schema.description) {\r\n <p class=\"ui-form-builder__description\">{{ schema.description }}</p>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- ==================== SEZIONI ==================== -->\r\n <div class=\"ui-form-builder__sections\">\r\n @for (section of schema.sections; track section.id) {\r\n @if (isSectionVisible(section.id)) {\r\n\r\n <!-- Sezione collassabile -->\r\n @if (section.collapsible) {\r\n <mat-expansion-panel\r\n class=\"ui-form-builder__section ui-form-builder__section--collapsible\"\r\n [class]=\"section.cssClasses?.join(' ')\"\r\n [expanded]=\"!section.collapsed\"\r\n >\r\n <mat-expansion-panel-header>\r\n @if (section.title) {\r\n <mat-panel-title>{{ section.title }}</mat-panel-title>\r\n }\r\n @if (section.description) {\r\n <mat-panel-description>{{ section.description }}</mat-panel-description>\r\n }\r\n </mat-expansion-panel-header>\r\n\r\n <div class=\"ui-form-builder__section-content\" [class.ui-form-builder__grid]=\"schema.config?.layout === 'grid' || !schema.config?.layout\">\r\n @for (field of section.fields; track field.key) {\r\n @if (isFieldVisible(field.key) && field.type !== 'flag') {\r\n <div\r\n class=\"ui-form-builder__field-wrapper\"\r\n [class]=\"getFieldWrapperClasses(field)\"\r\n [style.order]=\"field.layout?.order || null\"\r\n [attr.data-field-key]=\"field.key\"\r\n >\r\n <!-- OUTLET: il template fieldTpl ha il proprio [formGroup] wrapper -->\r\n <ng-container\r\n *ngTemplateOutlet=\"fieldTpl; context: { $implicit: field }\"\r\n />\r\n </div>\r\n }\r\n }\r\n </div>\r\n </mat-expansion-panel>\r\n } @else {\r\n <!-- Sezione non collassabile -->\r\n <div class=\"ui-form-builder__section\" [class]=\"section.cssClasses?.join(' ')\">\r\n @if (section.title || section.description) {\r\n <div class=\"ui-form-builder__section-header\">\r\n @if (section.title) {\r\n <h3 class=\"ui-form-builder__section-title\">{{ section.title }}</h3>\r\n }\r\n @if (section.description) {\r\n <p class=\"ui-form-builder__section-description\">{{ section.description }}</p>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"ui-form-builder__section-content\" [class.ui-form-builder__grid]=\"schema.config?.layout === 'grid' || !schema.config?.layout\">\r\n @for (field of section.fields; track field.key) {\r\n @if (isFieldVisible(field.key) && field.type !== 'flag') {\r\n <div\r\n class=\"ui-form-builder__field-wrapper\"\r\n [class]=\"getFieldWrapperClasses(field)\"\r\n [style.order]=\"field.layout?.order || null\"\r\n [attr.data-field-key]=\"field.key\"\r\n >\r\n <!-- OUTLET: il template fieldTpl ha il proprio [formGroup] wrapper -->\r\n <ng-container\r\n *ngTemplateOutlet=\"fieldTpl; context: { $implicit: field }\"\r\n />\r\n </div>\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n\r\n <!-- ==================== FOOTER ==================== -->\r\n @if (!schema.config?.hideFooter) {\r\n <div class=\"ui-form-builder__footer\">\r\n <ui-form-error-summary\r\n [errors]=\"formErrors\"\r\n [totalErrorCount]=\"getFormErrorsCount()\"\r\n (fieldClick)=\"scrollToField($event)\"\r\n />\r\n <ui-button-area\r\n [buttons]=\"formButtons\"\r\n align=\"end\"\r\n [loadingIds]=\"loadingFor\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n}\r\n\r\n<!-- ============================================================ -->\r\n<!-- FIELD TEMPLATE -->\r\n<!-- ============================================================ -->\r\n<!--\r\n CRITICO: Questo template e dichiarato FUORI dal <div [formGroup]>.\r\n Angular risolve l'injector dal contesto di DICHIARAZIONE.\r\n Quindi DOBBIAMO wrappare il contenuto in un <div [formGroup]>\r\n per fornire un FormGroupDirective ai formControlName figli.\r\n Senza questo wrapper -> errore NG01050.\r\n-->\r\n<ng-template #fieldTpl let-field>\r\n @if (formGroup) {\r\n <div [formGroup]=\"formGroup\" class=\"ui-fb-field-ctx\">\r\n @switch (field.type) {\r\n\r\n <!-- TEXT / EMAIL / PASSWORD -->\r\n @case ('text') { <ng-container *ngTemplateOutlet=\"textFieldTpl; context: { $implicit: field }\" /> }\r\n @case ('email') { <ng-container *ngTemplateOutlet=\"textFieldTpl; context: { $implicit: field }\" /> }\r\n @case ('password') { <ng-container *ngTemplateOutlet=\"textFieldTpl; context: { $implicit: field }\" /> }\r\n\r\n <!-- NUMBER -->\r\n @case ('number') {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n @if (field.metadata?.type === 'currency') {\r\n <input matInput type=\"text\" [formControlName]=\"field.key\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n uiCurrencyInput\r\n [currencySymbol]=\"field.metadata?.currency || 'EUR'\"\r\n [decimalPlaces]=\"field.metadata?.decimals ?? 2\"\r\n />\r\n } @else {\r\n <input matInput type=\"number\" [formControlName]=\"field.key\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n />\r\n }\r\n @if (field.iconTooltip) {\r\n <button matSuffix mat-icon-button type=\"button\"\r\n [matTooltip]=\"field.iconTooltip.text\" [attr.aria-label]=\"field.iconTooltip.text\">\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"18\" />\r\n </button>\r\n }\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- TEXTAREA -->\r\n @case ('textarea') {\r\n <div class=\"ui-form-builder__textarea-wrapper\">\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <textarea matInput [formControlName]=\"field.key\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n rows=\"4\"\r\n ></textarea>\r\n @if (field.iconTooltip) {\r\n <button matSuffix mat-icon-button type=\"button\"\r\n [matTooltip]=\"field.iconTooltip.text\" [attr.aria-label]=\"field.iconTooltip.text\">\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"18\" />\r\n </button>\r\n }\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n @if (getCharCount(field.key); as cc) {\r\n <span class=\"ui-form-builder__char-count\"\r\n [class.ui-form-builder__char-count--warn]=\"cc.current > cc.max * 0.9\"\r\n [class.ui-form-builder__char-count--error]=\"cc.current > cc.max\">\r\n {{ cc.current }} / {{ cc.max }}\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- SELECT -->\r\n @case ('select') {\r\n @if (field.searchable) {\r\n <!-- Autocomplete select -->\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input matInput [formControlName]=\"field.key\"\r\n [matAutocomplete]=\"auto\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n (input)=\"filterOptions(field, $any($event.target).value)\"\r\n />\r\n <mat-autocomplete #auto=\"matAutocomplete\" [displayWith]=\"displayFn(getFieldOptions(field))\">\r\n @for (opt of getFieldOptions(field); track opt.value) {\r\n <mat-option [value]=\"opt.value\" [disabled]=\"opt.disabled\">\r\n @if (opt.icon) {\r\n <lucide-icon [name]=\"opt.icon\" [size]=\"16\" class=\"ui-form-builder__option-icon\" />\r\n }\r\n {{ opt.label }}\r\n </mat-option>\r\n }\r\n </mat-autocomplete>\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n } @else {\r\n <!-- Standard select -->\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <mat-select [formControlName]=\"field.key\" [placeholder]=\"field.placeholder || ''\">\r\n @if (!field.hideEmptyOption) {\r\n <mat-option [value]=\"null\">--</mat-option>\r\n }\r\n @for (opt of getFieldOptions(field); track opt.value) {\r\n <mat-option [value]=\"opt.value\" [disabled]=\"opt.disabled\">\r\n @if (opt.icon) {\r\n <lucide-icon [name]=\"opt.icon\" [size]=\"16\" class=\"ui-form-builder__option-icon\" />\r\n }\r\n {{ opt.label }}\r\n @if (opt.tooltip) {\r\n <lucide-icon name=\"info\" [size]=\"14\"\r\n [matTooltip]=\"opt.tooltip\" class=\"ui-form-builder__option-info\" />\r\n }\r\n </mat-option>\r\n }\r\n </mat-select>\r\n @if (field.iconTooltip) {\r\n <button matSuffix mat-icon-button type=\"button\"\r\n [matTooltip]=\"field.iconTooltip.text\" [attr.aria-label]=\"field.iconTooltip.text\">\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"18\" />\r\n </button>\r\n }\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n }\r\n }\r\n\r\n <!-- MULTISELECT -->\r\n @case ('multiselect') {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <mat-select [formControlName]=\"field.key\" multiple [placeholder]=\"field.placeholder || ''\">\r\n @if (field.allowSelectAll) {\r\n <mat-option (click)=\"selectAll(field)\" (keydown.enter)=\"selectAll(field)\" (keydown.space)=\"selectAll(field)\">\r\n <em>Seleziona tutto</em>\r\n </mat-option>\r\n }\r\n @for (opt of getFieldOptions(field); track opt.value) {\r\n <mat-option [value]=\"opt.value\" [disabled]=\"opt.disabled\">\r\n {{ opt.label }}\r\n </mat-option>\r\n }\r\n </mat-select>\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n <!-- Chips preview sotto il select -->\r\n @if (formGroup.get(field.key)?.value?.length) {\r\n <div class=\"ui-form-builder__chips-preview\">\r\n @for (val of formGroup.get(field.key)!.value; track val) {\r\n <span class=\"ui-form-builder__chip\">\r\n {{ getOptionLabel(field, val) }}\r\n <button type=\"button\" (click)=\"removeMultiselectChip(field, val)\"\r\n class=\"ui-form-builder__chip-remove\" aria-label=\"Rimuovi\">\r\n <lucide-icon name=\"x\" [size]=\"12\" />\r\n </button>\r\n </span>\r\n }\r\n </div>\r\n }\r\n }\r\n\r\n <!-- FREEMULTISELECT -->\r\n @case ('freemultiselect') {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width ui-form-builder__free-multi-field\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <mat-chip-grid #chipGrid [formControlName]=\"field.key\">\r\n @for (chip of formGroup.get(field.key)?.value || []; track chip) {\r\n <mat-chip-row [removable]=\"!isChipProtected(field, chip)\" (removed)=\"removeFreeChip(field, chip)\">\r\n {{ chip }}\r\n @if (!isChipProtected(field, chip)) {\r\n <button matChipRemove aria-label=\"Rimuovi\">\r\n <lucide-icon name=\"x\" [size]=\"14\" />\r\n </button>\r\n }\r\n </mat-chip-row>\r\n }\r\n </mat-chip-grid>\r\n <input matInput\r\n [matChipInputFor]=\"chipGrid\"\r\n [placeholder]=\"field.placeholder || 'Aggiungi...'\"\r\n [readonly]=\"field.readonly || readonly\"\r\n (matChipInputTokenEnd)=\"addFreeChip(field, $event)\"\r\n (blur)=\"addFreeChipOnBlur(field, $event)\"\r\n />\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- CHECKBOX -->\r\n @case ('checkbox') {\r\n @if (field.appearance?.style === 'switch') {\r\n <mat-slide-toggle\r\n [formControlName]=\"field.key\"\r\n [color]=\"field.appearance?.color || 'primary'\"\r\n >\r\n {{ field.label }}\r\n </mat-slide-toggle>\r\n } @else {\r\n <mat-checkbox\r\n [formControlName]=\"field.key\"\r\n [color]=\"field.appearance?.color || 'primary'\"\r\n >\r\n {{ field.label }}\r\n @if (field.iconTooltip) {\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"14\"\r\n [matTooltip]=\"field.iconTooltip.text\" class=\"ui-form-builder__inline-tooltip\" />\r\n }\r\n </mat-checkbox>\r\n }\r\n @if (shouldShowFieldErrors(field.key)) {\r\n <div class=\"ui-form-builder__field-error\">\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <span>{{ err }}</span>\r\n }\r\n </div>\r\n }\r\n }\r\n\r\n <!-- SWITCH -->\r\n @case ('switch') {\r\n <mat-slide-toggle\r\n [formControlName]=\"field.key\"\r\n [color]=\"field.appearance?.color || 'primary'\"\r\n >\r\n {{ field.label }}\r\n @if (field.iconTooltip) {\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"14\"\r\n [matTooltip]=\"field.iconTooltip.text\" class=\"ui-form-builder__inline-tooltip\" />\r\n }\r\n </mat-slide-toggle>\r\n }\r\n\r\n <!-- RADIO -->\r\n @case ('radio') {\r\n <div class=\"ui-form-builder__radio-wrapper\">\r\n <label class=\"ui-form-builder__field-label\">\r\n {{ field.label }}\r\n @if (isFieldRequired(field.key)) { <span class=\"ui-form-builder__required\">*</span> }\r\n </label>\r\n <mat-radio-group [formControlName]=\"field.key\" [color]=\"field.appearance?.color || 'primary'\">\r\n @for (opt of getFieldOptions(field); track opt.value) {\r\n <mat-radio-button [value]=\"opt.value\" [disabled]=\"opt.disabled\">\r\n @if (opt.icon) {\r\n <lucide-icon [name]=\"opt.icon\" [size]=\"16\" />\r\n }\r\n {{ opt.label }}\r\n </mat-radio-button>\r\n }\r\n </mat-radio-group>\r\n @if (shouldShowFieldErrors(field.key)) {\r\n <div class=\"ui-form-builder__field-error\">\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <span>{{ err }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- DATE -->\r\n @case ('date') {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input matInput [matDatepicker]=\"datepicker\" [formControlName]=\"field.key\"\r\n [placeholder]=\"field.placeholder || 'GG/MM/AAAA'\"\r\n [readonly]=\"field.readonly || readonly\"\r\n />\r\n <mat-datepicker-toggle matSuffix [for]=\"datepicker\" />\r\n <mat-datepicker #datepicker\r\n [startView]=\"field.customConfig?.config?.['monthView'] ? 'year' : 'month'\"\r\n />\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- DATETIME -->\r\n @case ('datetime') {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input matInput type=\"datetime-local\" [formControlName]=\"field.key\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n />\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- FILE -->\r\n @case ('file') {\r\n <div class=\"ui-form-builder__file-wrapper\">\r\n <label class=\"ui-form-builder__field-label\">\r\n {{ field.label }}\r\n @if (isFieldRequired(field.key)) { <span class=\"ui-form-builder__required\">*</span> }\r\n </label>\r\n <ui-file-input\r\n [formControlName]=\"field.key\"\r\n [config]=\"field.fileConfig\"\r\n />\r\n @if (shouldShowFieldErrors(field.key)) {\r\n <div class=\"ui-form-builder__field-error\">\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <span>{{ err }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- DIVIDER -->\r\n @case ('divider') {\r\n <div class=\"ui-form-builder__divider\">\r\n @if (field.label) {\r\n <span class=\"ui-form-builder__divider-label\">{{ field.label }}</span>\r\n }\r\n <hr class=\"ui-form-builder__divider-line\" />\r\n </div>\r\n }\r\n\r\n <!-- LOCATION (selezione territorio) -->\r\n @case ('location') {\r\n <div class=\"ui-form-builder__location-wrapper\">\r\n @if (field.label) {\r\n <label class=\"ui-form-builder__field-label\">\r\n {{ field.label }}\r\n @if (isFieldRequired(field.key)) { <span class=\"ui-form-builder__required\">*</span> }\r\n </label>\r\n }\r\n <ui-specifica-territoriale\r\n [formControlName]=\"field.key\"\r\n [config]=\"field.customConfig?.config || {}\"\r\n [disabled]=\"readonly || field.disabled || disabled\"\r\n />\r\n @if (shouldShowFieldErrors(field.key)) {\r\n <div class=\"ui-form-builder__field-error\">\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <span>{{ err }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- LOCATION-TABLE (tabella CRUD di location) -->\r\n @case ('location-table') {\r\n <div class=\"ui-form-builder__location-table-wrapper\">\r\n <ui-table-territoriale\r\n [formControlName]=\"field.key\"\r\n [config]=\"field.customConfig?.config || {}\"\r\n [disabled]=\"readonly || field.disabled || disabled\"\r\n />\r\n @if (shouldShowFieldErrors(field.key)) {\r\n <div class=\"ui-form-builder__field-error\">\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <span>{{ err }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- CUSTOM -->\r\n @case ('custom') {\r\n <div class=\"ui-form-builder__custom-wrapper\" [attr.data-component]=\"field.customConfig?.component\">\r\n @if (field.label) {\r\n <label class=\"ui-form-builder__field-label\">{{ field.label }}</label>\r\n }\r\n <!-- I componenti custom devono essere proiettati dall'applicazione host -->\r\n <div class=\"ui-form-builder__custom-placeholder\">\r\n <span>Componente custom: {{ field.customConfig?.component }}</span>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n</ng-template>\r\n\r\n<!-- ============================================================ -->\r\n<!-- TEXT / EMAIL / PASSWORD template -->\r\n<!-- ============================================================ -->\r\n<!--\r\n CRITICO: Anche questo template necessita del wrapper [formGroup].\r\n Viene invocato da fieldTpl che gia ha il wrapper, ma siccome\r\n questo e un SECONDO ng-template, Angular crea un NUOVO contesto\r\n di dichiarazione. Il wrapper di fieldTpl non e sufficiente.\r\n-->\r\n<ng-template #textFieldTpl let-field>\r\n @if (formGroup) {\r\n <div [formGroup]=\"formGroup\" class=\"ui-fb-text-ctx\">\r\n @if (field.searchable && field.type === 'text') {\r\n <!-- Autocomplete text -->\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input matInput [formControlName]=\"field.key\"\r\n [type]=\"getNativeInputType(field)\"\r\n [matAutocomplete]=\"textAuto\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n (input)=\"filterOptions(field, $any($event.target).value)\"\r\n />\r\n <mat-autocomplete #textAuto=\"matAutocomplete\">\r\n @for (opt of getFieldOptions(field); track opt.value) {\r\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\r\n }\r\n </mat-autocomplete>\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n } @else {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input matInput [formControlName]=\"field.key\"\r\n [type]=\"getNativeInputType(field)\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n />\r\n @if (field.iconTooltip) {\r\n <button matSuffix mat-icon-button type=\"button\"\r\n [matTooltip]=\"field.iconTooltip.text\" [attr.aria-label]=\"field.iconTooltip.text\">\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"18\" />\r\n </button>\r\n }\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n @if (field.type === 'text' && getCharCount(field.key); as cc) {\r\n <span class=\"ui-form-builder__char-count\"\r\n [class.ui-form-builder__char-count--warn]=\"cc.current > cc.max * 0.9\"\r\n [class.ui-form-builder__char-count--error]=\"cc.current > cc.max\">\r\n {{ cc.current }} / {{ cc.max }}\r\n </span>\r\n }\r\n }\r\n </div>\r\n }\r\n</ng-template>\r\n", styles: [".ui-form-builder{display:flex;flex-direction:column;gap:var(--ui-spacing-6);font-family:var(--ui-font-family)}.ui-form-builder__header{margin-bottom:var(--ui-spacing-2)}.ui-form-builder__title{font-size:var(--ui-font-size-xl);font-weight:var(--ui-font-weight-semibold);color:var(--ui-color-text);margin:0 0 var(--ui-spacing-1)}.ui-form-builder__description{font-size:var(--ui-font-size-sm);color:var(--ui-color-text-secondary);margin:0}.ui-form-builder__sections{display:flex;flex-direction:column;gap:var(--ui-spacing-6)}.ui-form-builder__section--collapsible .mat-expansion-panel-body{padding:var(--ui-spacing-4) var(--ui-spacing-3) var(--ui-spacing-3)}.ui-form-builder__section-header{margin-bottom:var(--ui-spacing-4);padding-bottom:var(--ui-spacing-2);border-bottom:1px solid var(--ui-color-border)}.ui-form-builder__section-title{font-size:var(--ui-font-size-lg);font-weight:var(--ui-font-weight-semibold);color:var(--ui-color-text);margin:0 0 var(--ui-spacing-1)}.ui-form-builder__section-description{font-size:var(--ui-font-size-sm);color:var(--ui-color-text-secondary);margin:0}.ui-form-builder__grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--ui-spacing-4) var(--ui-spacing-4);align-items:start}.ui-form-builder__field-wrapper{grid-column:span 12;min-width:0}@media (min-width: 768px){.ui-form-builder__field-wrapper{grid-column:span 6}}@media (min-width: 576px){.ui-form-builder__field-wrapper.ui-col-sm-1{grid-column:span 1}.ui-form-builder__field-wrapper.ui-col-sm-2{grid-column:span 2}.ui-form-builder__field-wrapper.ui-col-sm-3{grid-column:span 3}.ui-form-builder__field-wrapper.ui-col-sm-4{grid-column:span 4}.ui-form-builder__field-wrapper.ui-col-sm-5{grid-column:span 5}.ui-form-builder__field-wrapper.ui-col-sm-6{grid-column:span 6}.ui-form-builder__field-wrapper.ui-col-sm-7{grid-column:span 7}.ui-form-builder__field-wrapper.ui-col-sm-8{grid-column:span 8}.ui-form-builder__field-wrapper.ui-col-sm-9{grid-column:span 9}.ui-form-builder__field-wrapper.ui-col-sm-10{grid-column:span 10}.ui-form-builder__field-wrapper.ui-col-sm-11{grid-column:span 11}.ui-form-builder__field-wrapper.ui-col-sm-12{grid-column:span 12}}@media (min-width: 768px){.ui-form-builder__field-wrapper.ui-col-md-1{grid-column:span 1}.ui-form-builder__field-wrapper.ui-col-md-2{grid-column:span 2}.ui-form-builder__field-wrapper.ui-col-md-3{grid-column:span 3}.ui-form-builder__field-wrapper.ui-col-md-4{grid-column:span 4}.ui-form-builder__field-wrapper.ui-col-md-5{grid-column:span 5}.ui-form-builder__field-wrapper.ui-col-md-6{grid-column:span 6}.ui-form-builder__field-wrapper.ui-col-md-7{grid-column:span 7}.ui-form-builder__field-wrapper.ui-col-md-8{grid-column:span 8}.ui-form-builder__field-wrapper.ui-col-md-9{grid-column:span 9}.ui-form-builder__field-wrapper.ui-col-md-10{grid-column:span 10}.ui-form-builder__field-wrapper.ui-col-md-11{grid-column:span 11}.ui-form-builder__field-wrapper.ui-col-md-12{grid-column:span 12}}@media (min-width: 1024px){.ui-form-builder__field-wrapper.ui-col-lg-1{grid-column:span 1}.ui-form-builder__field-wrapper.ui-col-lg-2{grid-column:span 2}.ui-form-builder__field-wrapper.ui-col-lg-3{grid-column:span 3}.ui-form-builder__field-wrapper.ui-col-lg-4{grid-column:span 4}.ui-form-builder__field-wrapper.ui-col-lg-5{grid-column:span 5}.ui-form-builder__field-wrapper.ui-col-lg-6{grid-column:span 6}.ui-form-builder__field-wrapper.ui-col-lg-7{grid-column:span 7}.ui-form-builder__field-wrapper.ui-col-lg-8{grid-column:span 8}.ui-form-builder__field-wrapper.ui-col-lg-9{grid-column:span 9}.ui-form-builder__field-wrapper.ui-col-lg-10{grid-column:span 10}.ui-form-builder__field-wrapper.ui-col-lg-11{grid-column:span 11}.ui-form-builder__field-wrapper.ui-col-lg-12{grid-column:span 12}}@media (min-width: 1280px){.ui-form-builder__field-wrapper.ui-col-xl-1{grid-column:span 1}.ui-form-builder__field-wrapper.ui-col-xl-2{grid-column:span 2}.ui-form-builder__field-wrapper.ui-col-xl-3{grid-column:span 3}.ui-form-builder__field-wrapper.ui-col-xl-4{grid-column:span 4}.ui-form-builder__field-wrapper.ui-col-xl-5{grid-column:span 5}.ui-form-builder__field-wrapper.ui-col-xl-6{grid-column:span 6}.ui-form-builder__field-wrapper.ui-col-xl-7{grid-column:span 7}.ui-form-builder__field-wrapper.ui-col-xl-8{grid-column:span 8}.ui-form-builder__field-wrapper.ui-col-xl-9{grid-column:span 9}.ui-form-builder__field-wrapper.ui-col-xl-10{grid-column:span 10}.ui-form-builder__field-wrapper.ui-col-xl-11{grid-column:span 11}.ui-form-builder__field-wrapper.ui-col-xl-12{grid-column:span 12}}.ui-form-builder__full-width{width:100%}.ui-fb-field-ctx,.ui-fb-text-ctx{display:contents}.ui-form-builder__textarea-wrapper{position:relative}.ui-form-builder__char-count{display:block;text-align:right;font-size:var(--ui-font-size-xs);color:var(--ui-color-text-muted);margin-top:calc(-1 * var(--ui-spacing-3))}.ui-form-builder__char-count--warn{color:var(--ui-color-warn, #d97706)}.ui-form-builder__char-count--error{color:var(--ui-color-error, #dc2626);font-weight:var(--ui-font-weight-semibold)}.ui-form-builder__field-label{display:block;font-size:var(--ui-font-size-sm);font-weight:var(--ui-font-weight-medium);color:var(--ui-color-text);margin-bottom:var(--ui-spacing-4)}.ui-form-builder__required{color:var(--ui-color-error, #dc2626);margin-left:2px}.ui-form-builder__field-error{margin-top:var(--ui-spacing-1);font-size:var(--ui-font-size-xs);color:var(--ui-color-error, #dc2626);display:flex;flex-direction:column;gap:2px}.ui-form-builder__radio-wrapper mat-radio-group{display:flex;flex-wrap:wrap;gap:var(--ui-spacing-3)}.ui-form-builder__chips-preview{display:flex;flex-wrap:wrap;gap:var(--ui-spacing-1);margin-top:var(--ui-spacing-1)}.ui-form-builder__chip{display:inline-flex;align-items:center;gap:4px;padding:2px var(--ui-spacing-2);border-radius:var(--ui-radius-full);background:var(--ui-color-primary-subtle, rgba(37, 99, 235, .08));color:var(--ui-color-primary);font-size:var(--ui-font-size-xs)}.ui-form-builder__chip-remove{appearance:none;border:none;background:transparent;cursor:pointer;padding:0;display:flex;color:inherit;opacity:.7}.ui-form-builder__chip-remove:hover{opacity:1}.ui-form-builder__option-icon{margin-right:var(--ui-spacing-2);vertical-align:middle}.ui-form-builder__option-info{margin-left:auto;color:var(--ui-color-text-muted)}.ui-form-builder__inline-tooltip{margin-left:var(--ui-spacing-1);color:var(--ui-color-text-muted);cursor:help}.ui-form-builder__divider{grid-column:1/-1!important;display:flex;align-items:center;gap:var(--ui-spacing-3);padding:var(--ui-spacing-2) 0}.ui-form-builder__divider-label{font-size:var(--ui-font-size-sm);font-weight:var(--ui-font-weight-medium);color:var(--ui-color-text-secondary);white-space:nowrap}.ui-form-builder__divider-line{flex:1;border:none;border-top:1px solid var(--ui-color-border);margin:0}.ui-form-builder__custom-placeholder{display:flex;align-items:center;gap:var(--ui-spacing-2);padding:var(--ui-spacing-4);border:1px dashed var(--ui-color-border);border-radius:var(--ui-radius-md);color:var(--ui-color-text-muted);font-size:var(--ui-font-size-sm)}.ui-form-builder__footer{display:flex;align-items:center;justify-content:space-between;gap:var(--ui-spacing-4);padding-top:var(--ui-spacing-4);border-top:1px solid var(--ui-color-border)}@media (max-width: 767.98px){.ui-form-builder__footer{flex-direction:column;align-items:stretch}}.ui-form-field--highlight{animation:ui-field-highlight-pulse 1s ease-in-out 2;border-radius:var(--ui-radius-md)}@keyframes ui-field-highlight-pulse{0%{box-shadow:0 0 #dc262666;padding:10px}50%{box-shadow:0 0 0 6px #dc262626;padding:10px}to{box-shadow:0 0 #dc262600;padding:10px}}.ui-form-builder mat-error{font-size:var(--ui-font-size-xs, 12px);display:flex;flex-direction:column;transform:translateY(-5px)}.ui-form-builder .mat-mdc-form-field.mat-form-field-invalid .mdc-notched-outline__leading,.ui-form-builder .mat-mdc-form-field.mat-form-field-invalid .mdc-notched-outline__notch,.ui-form-builder .mat-mdc-form-field.mat-form-field-invalid .mdc-notched-outline__trailing{border-color:var(--ui-color-error, #dc2626)!important}.ui-form-builder .mat-mdc-form-field.mat-form-field-invalid .mat-mdc-floating-label{color:var(--ui-color-error, #dc2626)}.ui-form-builder__section--collapsible.mat-expansion-panel{box-shadow:none;border:1px solid var(--ui-color-border);border-radius:var(--ui-radius-lg)!important}.ui-form-builder__free-multi-field mat-chip-grid{display:flex;flex-wrap:wrap;gap:var(--ui-spacing-1)}.ui-form-builder__free-multi-field input[matInput]{flex:1;min-width:80px}.ui-form-builder mat-datepicker-toggle{display:inline-flex;align-items:center}.mdc-notched-outline__notch{border-left:none!important}.mat-expansion-panel{box-shadow:none!important}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { 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: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i6.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i7.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i8.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i8.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i9.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i9.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i9.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatNativeDateModule }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i10.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i10.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i11.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i11.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i11.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i11.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i12.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i12.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i12.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i12.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i13.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i14.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: UiButtonAreaComponent, selector: "ui-button-area", inputs: ["buttons", "align", "ariaLabel", "gap", "stackOnMobile", "disableWhileLoading", "loadingIds"] }, { kind: "component", type: UiFormErrorSummaryComponent, selector: "ui-form-error-summary", inputs: ["errors", "totalErrorCount"], outputs: ["fieldClick"] }, { kind: "component", type: UiFileInputComponent, selector: "ui-file-input", inputs: ["config"], outputs: ["fileRemoved", "validationError"] }, { kind: "component", type: UiSpecificaTerritorialeComponent, selector: "ui-specifica-territoriale", inputs: ["config", "disabled"] }, { kind: "component", type: UiTableTerritorialeComponent, selector: "ui-table-territoriale", inputs: ["config", "disabled"] }, { kind: "directive", type: UiCurrencyInputDirective, selector: "[uiCurrencyInput]", inputs: ["currencySymbol", "decimalPlaces"] }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
767
|
+
}
|
|
768
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UiFormBuilderComponent, decorators: [{
|
|
769
|
+
type: Component,
|
|
770
|
+
args: [{ selector: 'ui-form-builder', standalone: true, imports: [
|
|
771
|
+
ReactiveFormsModule,
|
|
772
|
+
NgTemplateOutlet,
|
|
773
|
+
MatFormFieldModule,
|
|
774
|
+
MatInputModule,
|
|
775
|
+
MatSelectModule,
|
|
776
|
+
MatCheckboxModule,
|
|
777
|
+
MatSlideToggleModule,
|
|
778
|
+
MatRadioModule,
|
|
779
|
+
MatDatepickerModule,
|
|
780
|
+
MatNativeDateModule,
|
|
781
|
+
MatAutocompleteModule,
|
|
782
|
+
MatChipsModule,
|
|
783
|
+
MatExpansionModule,
|
|
784
|
+
MatTooltipModule,
|
|
785
|
+
LucideAngularModule,
|
|
786
|
+
UiButtonAreaComponent,
|
|
787
|
+
UiFormErrorSummaryComponent,
|
|
788
|
+
UiFileInputComponent,
|
|
789
|
+
UiSpecificaTerritorialeComponent,
|
|
790
|
+
UiTableTerritorialeComponent,
|
|
791
|
+
UiCurrencyInputDirective,
|
|
792
|
+
], providers: [
|
|
793
|
+
{ provide: ErrorStateMatcher, useClass: UiFormErrorStateMatcher },
|
|
794
|
+
// Locale italiano per il datepicker Material
|
|
795
|
+
{ provide: MAT_DATE_LOCALE, useValue: 'it-IT' },
|
|
796
|
+
// Adapter personalizzato per parsing DD/MM/YYYY
|
|
797
|
+
{ provide: DateAdapter, useClass: UiItalianDateAdapter },
|
|
798
|
+
// Formati data italiani (DD/MM/YYYY)
|
|
799
|
+
{ provide: MAT_DATE_FORMATS, useValue: UI_IT_DATE_FORMATS },
|
|
800
|
+
], encapsulation: ViewEncapsulation.None, host: { class: 'ui-form-builder-host' }, template: "<!--\r\n ============================================================\r\n UI FORM BUILDER - TEMPLATE PRINCIPALE\r\n ============================================================\r\n ARCHITETTURA:\r\n Ogni <ng-template> che contiene direttive [formControlName]\r\n DEVE wrappare il proprio contenuto in un <div [formGroup]>.\r\n Questo perche Angular risolve l'injector di formControlName\r\n dal CONTESTO DI DICHIARAZIONE del template, non dal punto\r\n di inserimento (ngTemplateOutlet).\r\n\r\n Senza questo wrapper, formControlName non trova il\r\n FormGroupDirective e lancia NG01050.\r\n\r\n Ref: versione tailored (ang-ms-webapp) usa lo stesso pattern.\r\n ============================================================\r\n-->\r\n\r\n@if (schema && formGroup) {\r\n <div class=\"ui-form-builder\" [formGroup]=\"formGroup\" [class]=\"schema.config?.cssClasses?.join(' ')\">\r\n\r\n <!-- DEBUG: Verifica che formGroup sia inizializzato -->\r\n <!-- [formGroup] e sul div padre; tutti i formControlName INLINE funzionano -->\r\n\r\n <!-- ==================== HEADER ==================== -->\r\n @if (schema.title || schema.description) {\r\n <div class=\"ui-form-builder__header\">\r\n @if (schema.title) {\r\n <h2 class=\"ui-form-builder__title\">{{ schema.title }}</h2>\r\n }\r\n @if (schema.description) {\r\n <p class=\"ui-form-builder__description\">{{ schema.description }}</p>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- ==================== SEZIONI ==================== -->\r\n <div class=\"ui-form-builder__sections\">\r\n @for (section of schema.sections; track section.id) {\r\n @if (isSectionVisible(section.id)) {\r\n\r\n <!-- Sezione collassabile -->\r\n @if (section.collapsible) {\r\n <mat-expansion-panel\r\n class=\"ui-form-builder__section ui-form-builder__section--collapsible\"\r\n [class]=\"section.cssClasses?.join(' ')\"\r\n [expanded]=\"!section.collapsed\"\r\n >\r\n <mat-expansion-panel-header>\r\n @if (section.title) {\r\n <mat-panel-title>{{ section.title }}</mat-panel-title>\r\n }\r\n @if (section.description) {\r\n <mat-panel-description>{{ section.description }}</mat-panel-description>\r\n }\r\n </mat-expansion-panel-header>\r\n\r\n <div class=\"ui-form-builder__section-content\" [class.ui-form-builder__grid]=\"schema.config?.layout === 'grid' || !schema.config?.layout\">\r\n @for (field of section.fields; track field.key) {\r\n @if (isFieldVisible(field.key) && field.type !== 'flag') {\r\n <div\r\n class=\"ui-form-builder__field-wrapper\"\r\n [class]=\"getFieldWrapperClasses(field)\"\r\n [style.order]=\"field.layout?.order || null\"\r\n [attr.data-field-key]=\"field.key\"\r\n >\r\n <!-- OUTLET: il template fieldTpl ha il proprio [formGroup] wrapper -->\r\n <ng-container\r\n *ngTemplateOutlet=\"fieldTpl; context: { $implicit: field }\"\r\n />\r\n </div>\r\n }\r\n }\r\n </div>\r\n </mat-expansion-panel>\r\n } @else {\r\n <!-- Sezione non collassabile -->\r\n <div class=\"ui-form-builder__section\" [class]=\"section.cssClasses?.join(' ')\">\r\n @if (section.title || section.description) {\r\n <div class=\"ui-form-builder__section-header\">\r\n @if (section.title) {\r\n <h3 class=\"ui-form-builder__section-title\">{{ section.title }}</h3>\r\n }\r\n @if (section.description) {\r\n <p class=\"ui-form-builder__section-description\">{{ section.description }}</p>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"ui-form-builder__section-content\" [class.ui-form-builder__grid]=\"schema.config?.layout === 'grid' || !schema.config?.layout\">\r\n @for (field of section.fields; track field.key) {\r\n @if (isFieldVisible(field.key) && field.type !== 'flag') {\r\n <div\r\n class=\"ui-form-builder__field-wrapper\"\r\n [class]=\"getFieldWrapperClasses(field)\"\r\n [style.order]=\"field.layout?.order || null\"\r\n [attr.data-field-key]=\"field.key\"\r\n >\r\n <!-- OUTLET: il template fieldTpl ha il proprio [formGroup] wrapper -->\r\n <ng-container\r\n *ngTemplateOutlet=\"fieldTpl; context: { $implicit: field }\"\r\n />\r\n </div>\r\n }\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n }\r\n </div>\r\n\r\n <!-- ==================== FOOTER ==================== -->\r\n @if (!schema.config?.hideFooter) {\r\n <div class=\"ui-form-builder__footer\">\r\n <ui-form-error-summary\r\n [errors]=\"formErrors\"\r\n [totalErrorCount]=\"getFormErrorsCount()\"\r\n (fieldClick)=\"scrollToField($event)\"\r\n />\r\n <ui-button-area\r\n [buttons]=\"formButtons\"\r\n align=\"end\"\r\n [loadingIds]=\"loadingFor\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n}\r\n\r\n<!-- ============================================================ -->\r\n<!-- FIELD TEMPLATE -->\r\n<!-- ============================================================ -->\r\n<!--\r\n CRITICO: Questo template e dichiarato FUORI dal <div [formGroup]>.\r\n Angular risolve l'injector dal contesto di DICHIARAZIONE.\r\n Quindi DOBBIAMO wrappare il contenuto in un <div [formGroup]>\r\n per fornire un FormGroupDirective ai formControlName figli.\r\n Senza questo wrapper -> errore NG01050.\r\n-->\r\n<ng-template #fieldTpl let-field>\r\n @if (formGroup) {\r\n <div [formGroup]=\"formGroup\" class=\"ui-fb-field-ctx\">\r\n @switch (field.type) {\r\n\r\n <!-- TEXT / EMAIL / PASSWORD -->\r\n @case ('text') { <ng-container *ngTemplateOutlet=\"textFieldTpl; context: { $implicit: field }\" /> }\r\n @case ('email') { <ng-container *ngTemplateOutlet=\"textFieldTpl; context: { $implicit: field }\" /> }\r\n @case ('password') { <ng-container *ngTemplateOutlet=\"textFieldTpl; context: { $implicit: field }\" /> }\r\n\r\n <!-- NUMBER -->\r\n @case ('number') {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n @if (field.metadata?.type === 'currency') {\r\n <input matInput type=\"text\" [formControlName]=\"field.key\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n uiCurrencyInput\r\n [currencySymbol]=\"field.metadata?.currency || 'EUR'\"\r\n [decimalPlaces]=\"field.metadata?.decimals ?? 2\"\r\n />\r\n } @else {\r\n <input matInput type=\"number\" [formControlName]=\"field.key\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n />\r\n }\r\n @if (field.iconTooltip) {\r\n <button matSuffix mat-icon-button type=\"button\"\r\n [matTooltip]=\"field.iconTooltip.text\" [attr.aria-label]=\"field.iconTooltip.text\">\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"18\" />\r\n </button>\r\n }\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- TEXTAREA -->\r\n @case ('textarea') {\r\n <div class=\"ui-form-builder__textarea-wrapper\">\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <textarea matInput [formControlName]=\"field.key\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n rows=\"4\"\r\n ></textarea>\r\n @if (field.iconTooltip) {\r\n <button matSuffix mat-icon-button type=\"button\"\r\n [matTooltip]=\"field.iconTooltip.text\" [attr.aria-label]=\"field.iconTooltip.text\">\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"18\" />\r\n </button>\r\n }\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n @if (getCharCount(field.key); as cc) {\r\n <span class=\"ui-form-builder__char-count\"\r\n [class.ui-form-builder__char-count--warn]=\"cc.current > cc.max * 0.9\"\r\n [class.ui-form-builder__char-count--error]=\"cc.current > cc.max\">\r\n {{ cc.current }} / {{ cc.max }}\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- SELECT -->\r\n @case ('select') {\r\n @if (field.searchable) {\r\n <!-- Autocomplete select -->\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input matInput [formControlName]=\"field.key\"\r\n [matAutocomplete]=\"auto\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n (input)=\"filterOptions(field, $any($event.target).value)\"\r\n />\r\n <mat-autocomplete #auto=\"matAutocomplete\" [displayWith]=\"displayFn(getFieldOptions(field))\">\r\n @for (opt of getFieldOptions(field); track opt.value) {\r\n <mat-option [value]=\"opt.value\" [disabled]=\"opt.disabled\">\r\n @if (opt.icon) {\r\n <lucide-icon [name]=\"opt.icon\" [size]=\"16\" class=\"ui-form-builder__option-icon\" />\r\n }\r\n {{ opt.label }}\r\n </mat-option>\r\n }\r\n </mat-autocomplete>\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n } @else {\r\n <!-- Standard select -->\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <mat-select [formControlName]=\"field.key\" [placeholder]=\"field.placeholder || ''\">\r\n @if (!field.hideEmptyOption) {\r\n <mat-option [value]=\"null\">--</mat-option>\r\n }\r\n @for (opt of getFieldOptions(field); track opt.value) {\r\n <mat-option [value]=\"opt.value\" [disabled]=\"opt.disabled\">\r\n @if (opt.icon) {\r\n <lucide-icon [name]=\"opt.icon\" [size]=\"16\" class=\"ui-form-builder__option-icon\" />\r\n }\r\n {{ opt.label }}\r\n @if (opt.tooltip) {\r\n <lucide-icon name=\"info\" [size]=\"14\"\r\n [matTooltip]=\"opt.tooltip\" class=\"ui-form-builder__option-info\" />\r\n }\r\n </mat-option>\r\n }\r\n </mat-select>\r\n @if (field.iconTooltip) {\r\n <button matSuffix mat-icon-button type=\"button\"\r\n [matTooltip]=\"field.iconTooltip.text\" [attr.aria-label]=\"field.iconTooltip.text\">\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"18\" />\r\n </button>\r\n }\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n }\r\n }\r\n\r\n <!-- MULTISELECT -->\r\n @case ('multiselect') {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <mat-select [formControlName]=\"field.key\" multiple [placeholder]=\"field.placeholder || ''\">\r\n @if (field.allowSelectAll) {\r\n <mat-option (click)=\"selectAll(field)\" (keydown.enter)=\"selectAll(field)\" (keydown.space)=\"selectAll(field)\">\r\n <em>Seleziona tutto</em>\r\n </mat-option>\r\n }\r\n @for (opt of getFieldOptions(field); track opt.value) {\r\n <mat-option [value]=\"opt.value\" [disabled]=\"opt.disabled\">\r\n {{ opt.label }}\r\n </mat-option>\r\n }\r\n </mat-select>\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n <!-- Chips preview sotto il select -->\r\n @if (formGroup.get(field.key)?.value?.length) {\r\n <div class=\"ui-form-builder__chips-preview\">\r\n @for (val of formGroup.get(field.key)!.value; track val) {\r\n <span class=\"ui-form-builder__chip\">\r\n {{ getOptionLabel(field, val) }}\r\n <button type=\"button\" (click)=\"removeMultiselectChip(field, val)\"\r\n class=\"ui-form-builder__chip-remove\" aria-label=\"Rimuovi\">\r\n <lucide-icon name=\"x\" [size]=\"12\" />\r\n </button>\r\n </span>\r\n }\r\n </div>\r\n }\r\n }\r\n\r\n <!-- FREEMULTISELECT -->\r\n @case ('freemultiselect') {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width ui-form-builder__free-multi-field\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <mat-chip-grid #chipGrid [formControlName]=\"field.key\">\r\n @for (chip of formGroup.get(field.key)?.value || []; track chip) {\r\n <mat-chip-row [removable]=\"!isChipProtected(field, chip)\" (removed)=\"removeFreeChip(field, chip)\">\r\n {{ chip }}\r\n @if (!isChipProtected(field, chip)) {\r\n <button matChipRemove aria-label=\"Rimuovi\">\r\n <lucide-icon name=\"x\" [size]=\"14\" />\r\n </button>\r\n }\r\n </mat-chip-row>\r\n }\r\n </mat-chip-grid>\r\n <input matInput\r\n [matChipInputFor]=\"chipGrid\"\r\n [placeholder]=\"field.placeholder || 'Aggiungi...'\"\r\n [readonly]=\"field.readonly || readonly\"\r\n (matChipInputTokenEnd)=\"addFreeChip(field, $event)\"\r\n (blur)=\"addFreeChipOnBlur(field, $event)\"\r\n />\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- CHECKBOX -->\r\n @case ('checkbox') {\r\n @if (field.appearance?.style === 'switch') {\r\n <mat-slide-toggle\r\n [formControlName]=\"field.key\"\r\n [color]=\"field.appearance?.color || 'primary'\"\r\n >\r\n {{ field.label }}\r\n </mat-slide-toggle>\r\n } @else {\r\n <mat-checkbox\r\n [formControlName]=\"field.key\"\r\n [color]=\"field.appearance?.color || 'primary'\"\r\n >\r\n {{ field.label }}\r\n @if (field.iconTooltip) {\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"14\"\r\n [matTooltip]=\"field.iconTooltip.text\" class=\"ui-form-builder__inline-tooltip\" />\r\n }\r\n </mat-checkbox>\r\n }\r\n @if (shouldShowFieldErrors(field.key)) {\r\n <div class=\"ui-form-builder__field-error\">\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <span>{{ err }}</span>\r\n }\r\n </div>\r\n }\r\n }\r\n\r\n <!-- SWITCH -->\r\n @case ('switch') {\r\n <mat-slide-toggle\r\n [formControlName]=\"field.key\"\r\n [color]=\"field.appearance?.color || 'primary'\"\r\n >\r\n {{ field.label }}\r\n @if (field.iconTooltip) {\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"14\"\r\n [matTooltip]=\"field.iconTooltip.text\" class=\"ui-form-builder__inline-tooltip\" />\r\n }\r\n </mat-slide-toggle>\r\n }\r\n\r\n <!-- RADIO -->\r\n @case ('radio') {\r\n <div class=\"ui-form-builder__radio-wrapper\">\r\n <label class=\"ui-form-builder__field-label\">\r\n {{ field.label }}\r\n @if (isFieldRequired(field.key)) { <span class=\"ui-form-builder__required\">*</span> }\r\n </label>\r\n <mat-radio-group [formControlName]=\"field.key\" [color]=\"field.appearance?.color || 'primary'\">\r\n @for (opt of getFieldOptions(field); track opt.value) {\r\n <mat-radio-button [value]=\"opt.value\" [disabled]=\"opt.disabled\">\r\n @if (opt.icon) {\r\n <lucide-icon [name]=\"opt.icon\" [size]=\"16\" />\r\n }\r\n {{ opt.label }}\r\n </mat-radio-button>\r\n }\r\n </mat-radio-group>\r\n @if (shouldShowFieldErrors(field.key)) {\r\n <div class=\"ui-form-builder__field-error\">\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <span>{{ err }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- DATE -->\r\n @case ('date') {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input matInput [matDatepicker]=\"datepicker\" [formControlName]=\"field.key\"\r\n [placeholder]=\"field.placeholder || 'GG/MM/AAAA'\"\r\n [readonly]=\"field.readonly || readonly\"\r\n />\r\n <mat-datepicker-toggle matSuffix [for]=\"datepicker\" />\r\n <mat-datepicker #datepicker\r\n [startView]=\"field.customConfig?.config?.['monthView'] ? 'year' : 'month'\"\r\n />\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- DATETIME -->\r\n @case ('datetime') {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input matInput type=\"datetime-local\" [formControlName]=\"field.key\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n />\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- FILE -->\r\n @case ('file') {\r\n <div class=\"ui-form-builder__file-wrapper\">\r\n <label class=\"ui-form-builder__field-label\">\r\n {{ field.label }}\r\n @if (isFieldRequired(field.key)) { <span class=\"ui-form-builder__required\">*</span> }\r\n </label>\r\n <ui-file-input\r\n [formControlName]=\"field.key\"\r\n [config]=\"field.fileConfig\"\r\n />\r\n @if (shouldShowFieldErrors(field.key)) {\r\n <div class=\"ui-form-builder__field-error\">\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <span>{{ err }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- DIVIDER -->\r\n @case ('divider') {\r\n <div class=\"ui-form-builder__divider\">\r\n @if (field.label) {\r\n <span class=\"ui-form-builder__divider-label\">{{ field.label }}</span>\r\n }\r\n <hr class=\"ui-form-builder__divider-line\" />\r\n </div>\r\n }\r\n\r\n <!-- LOCATION (selezione territorio) -->\r\n @case ('location') {\r\n <div class=\"ui-form-builder__location-wrapper\">\r\n @if (field.label) {\r\n <label class=\"ui-form-builder__field-label\">\r\n {{ field.label }}\r\n @if (isFieldRequired(field.key)) { <span class=\"ui-form-builder__required\">*</span> }\r\n </label>\r\n }\r\n <ui-specifica-territoriale\r\n [formControlName]=\"field.key\"\r\n [config]=\"field.customConfig?.config || {}\"\r\n [disabled]=\"readonly || field.disabled || disabled\"\r\n />\r\n @if (shouldShowFieldErrors(field.key)) {\r\n <div class=\"ui-form-builder__field-error\">\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <span>{{ err }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- LOCATION-TABLE (tabella CRUD di location) -->\r\n @case ('location-table') {\r\n <div class=\"ui-form-builder__location-table-wrapper\">\r\n <ui-table-territoriale\r\n [formControlName]=\"field.key\"\r\n [config]=\"field.customConfig?.config || {}\"\r\n [disabled]=\"readonly || field.disabled || disabled\"\r\n />\r\n @if (shouldShowFieldErrors(field.key)) {\r\n <div class=\"ui-form-builder__field-error\">\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <span>{{ err }}</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- CUSTOM -->\r\n @case ('custom') {\r\n <div class=\"ui-form-builder__custom-wrapper\" [attr.data-component]=\"field.customConfig?.component\">\r\n @if (field.label) {\r\n <label class=\"ui-form-builder__field-label\">{{ field.label }}</label>\r\n }\r\n <!-- I componenti custom devono essere proiettati dall'applicazione host -->\r\n <div class=\"ui-form-builder__custom-placeholder\">\r\n <span>Componente custom: {{ field.customConfig?.component }}</span>\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n</ng-template>\r\n\r\n<!-- ============================================================ -->\r\n<!-- TEXT / EMAIL / PASSWORD template -->\r\n<!-- ============================================================ -->\r\n<!--\r\n CRITICO: Anche questo template necessita del wrapper [formGroup].\r\n Viene invocato da fieldTpl che gia ha il wrapper, ma siccome\r\n questo e un SECONDO ng-template, Angular crea un NUOVO contesto\r\n di dichiarazione. Il wrapper di fieldTpl non e sufficiente.\r\n-->\r\n<ng-template #textFieldTpl let-field>\r\n @if (formGroup) {\r\n <div [formGroup]=\"formGroup\" class=\"ui-fb-text-ctx\">\r\n @if (field.searchable && field.type === 'text') {\r\n <!-- Autocomplete text -->\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input matInput [formControlName]=\"field.key\"\r\n [type]=\"getNativeInputType(field)\"\r\n [matAutocomplete]=\"textAuto\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n (input)=\"filterOptions(field, $any($event.target).value)\"\r\n />\r\n <mat-autocomplete #textAuto=\"matAutocomplete\">\r\n @for (opt of getFieldOptions(field); track opt.value) {\r\n <mat-option [value]=\"opt.value\">{{ opt.label }}</mat-option>\r\n }\r\n </mat-autocomplete>\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n } @else {\r\n <mat-form-field appearance=\"outline\" class=\"ui-form-builder__full-width\">\r\n <mat-label>{{ field.label }}</mat-label>\r\n <input matInput [formControlName]=\"field.key\"\r\n [type]=\"getNativeInputType(field)\"\r\n [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly || readonly\"\r\n />\r\n @if (field.iconTooltip) {\r\n <button matSuffix mat-icon-button type=\"button\"\r\n [matTooltip]=\"field.iconTooltip.text\" [attr.aria-label]=\"field.iconTooltip.text\">\r\n <lucide-icon [name]=\"field.iconTooltip.icon || 'info'\" [size]=\"18\" />\r\n </button>\r\n }\r\n <mat-error>\r\n @for (err of getFieldErrors(field.key); track $index) {\r\n <div>{{ err }}</div>\r\n }\r\n </mat-error>\r\n </mat-form-field>\r\n @if (field.type === 'text' && getCharCount(field.key); as cc) {\r\n <span class=\"ui-form-builder__char-count\"\r\n [class.ui-form-builder__char-count--warn]=\"cc.current > cc.max * 0.9\"\r\n [class.ui-form-builder__char-count--error]=\"cc.current > cc.max\">\r\n {{ cc.current }} / {{ cc.max }}\r\n </span>\r\n }\r\n }\r\n </div>\r\n }\r\n</ng-template>\r\n", styles: [".ui-form-builder{display:flex;flex-direction:column;gap:var(--ui-spacing-6);font-family:var(--ui-font-family)}.ui-form-builder__header{margin-bottom:var(--ui-spacing-2)}.ui-form-builder__title{font-size:var(--ui-font-size-xl);font-weight:var(--ui-font-weight-semibold);color:var(--ui-color-text);margin:0 0 var(--ui-spacing-1)}.ui-form-builder__description{font-size:var(--ui-font-size-sm);color:var(--ui-color-text-secondary);margin:0}.ui-form-builder__sections{display:flex;flex-direction:column;gap:var(--ui-spacing-6)}.ui-form-builder__section--collapsible .mat-expansion-panel-body{padding:var(--ui-spacing-4) var(--ui-spacing-3) var(--ui-spacing-3)}.ui-form-builder__section-header{margin-bottom:var(--ui-spacing-4);padding-bottom:var(--ui-spacing-2);border-bottom:1px solid var(--ui-color-border)}.ui-form-builder__section-title{font-size:var(--ui-font-size-lg);font-weight:var(--ui-font-weight-semibold);color:var(--ui-color-text);margin:0 0 var(--ui-spacing-1)}.ui-form-builder__section-description{font-size:var(--ui-font-size-sm);color:var(--ui-color-text-secondary);margin:0}.ui-form-builder__grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--ui-spacing-4) var(--ui-spacing-4);align-items:start}.ui-form-builder__field-wrapper{grid-column:span 12;min-width:0}@media (min-width: 768px){.ui-form-builder__field-wrapper{grid-column:span 6}}@media (min-width: 576px){.ui-form-builder__field-wrapper.ui-col-sm-1{grid-column:span 1}.ui-form-builder__field-wrapper.ui-col-sm-2{grid-column:span 2}.ui-form-builder__field-wrapper.ui-col-sm-3{grid-column:span 3}.ui-form-builder__field-wrapper.ui-col-sm-4{grid-column:span 4}.ui-form-builder__field-wrapper.ui-col-sm-5{grid-column:span 5}.ui-form-builder__field-wrapper.ui-col-sm-6{grid-column:span 6}.ui-form-builder__field-wrapper.ui-col-sm-7{grid-column:span 7}.ui-form-builder__field-wrapper.ui-col-sm-8{grid-column:span 8}.ui-form-builder__field-wrapper.ui-col-sm-9{grid-column:span 9}.ui-form-builder__field-wrapper.ui-col-sm-10{grid-column:span 10}.ui-form-builder__field-wrapper.ui-col-sm-11{grid-column:span 11}.ui-form-builder__field-wrapper.ui-col-sm-12{grid-column:span 12}}@media (min-width: 768px){.ui-form-builder__field-wrapper.ui-col-md-1{grid-column:span 1}.ui-form-builder__field-wrapper.ui-col-md-2{grid-column:span 2}.ui-form-builder__field-wrapper.ui-col-md-3{grid-column:span 3}.ui-form-builder__field-wrapper.ui-col-md-4{grid-column:span 4}.ui-form-builder__field-wrapper.ui-col-md-5{grid-column:span 5}.ui-form-builder__field-wrapper.ui-col-md-6{grid-column:span 6}.ui-form-builder__field-wrapper.ui-col-md-7{grid-column:span 7}.ui-form-builder__field-wrapper.ui-col-md-8{grid-column:span 8}.ui-form-builder__field-wrapper.ui-col-md-9{grid-column:span 9}.ui-form-builder__field-wrapper.ui-col-md-10{grid-column:span 10}.ui-form-builder__field-wrapper.ui-col-md-11{grid-column:span 11}.ui-form-builder__field-wrapper.ui-col-md-12{grid-column:span 12}}@media (min-width: 1024px){.ui-form-builder__field-wrapper.ui-col-lg-1{grid-column:span 1}.ui-form-builder__field-wrapper.ui-col-lg-2{grid-column:span 2}.ui-form-builder__field-wrapper.ui-col-lg-3{grid-column:span 3}.ui-form-builder__field-wrapper.ui-col-lg-4{grid-column:span 4}.ui-form-builder__field-wrapper.ui-col-lg-5{grid-column:span 5}.ui-form-builder__field-wrapper.ui-col-lg-6{grid-column:span 6}.ui-form-builder__field-wrapper.ui-col-lg-7{grid-column:span 7}.ui-form-builder__field-wrapper.ui-col-lg-8{grid-column:span 8}.ui-form-builder__field-wrapper.ui-col-lg-9{grid-column:span 9}.ui-form-builder__field-wrapper.ui-col-lg-10{grid-column:span 10}.ui-form-builder__field-wrapper.ui-col-lg-11{grid-column:span 11}.ui-form-builder__field-wrapper.ui-col-lg-12{grid-column:span 12}}@media (min-width: 1280px){.ui-form-builder__field-wrapper.ui-col-xl-1{grid-column:span 1}.ui-form-builder__field-wrapper.ui-col-xl-2{grid-column:span 2}.ui-form-builder__field-wrapper.ui-col-xl-3{grid-column:span 3}.ui-form-builder__field-wrapper.ui-col-xl-4{grid-column:span 4}.ui-form-builder__field-wrapper.ui-col-xl-5{grid-column:span 5}.ui-form-builder__field-wrapper.ui-col-xl-6{grid-column:span 6}.ui-form-builder__field-wrapper.ui-col-xl-7{grid-column:span 7}.ui-form-builder__field-wrapper.ui-col-xl-8{grid-column:span 8}.ui-form-builder__field-wrapper.ui-col-xl-9{grid-column:span 9}.ui-form-builder__field-wrapper.ui-col-xl-10{grid-column:span 10}.ui-form-builder__field-wrapper.ui-col-xl-11{grid-column:span 11}.ui-form-builder__field-wrapper.ui-col-xl-12{grid-column:span 12}}.ui-form-builder__full-width{width:100%}.ui-fb-field-ctx,.ui-fb-text-ctx{display:contents}.ui-form-builder__textarea-wrapper{position:relative}.ui-form-builder__char-count{display:block;text-align:right;font-size:var(--ui-font-size-xs);color:var(--ui-color-text-muted);margin-top:calc(-1 * var(--ui-spacing-3))}.ui-form-builder__char-count--warn{color:var(--ui-color-warn, #d97706)}.ui-form-builder__char-count--error{color:var(--ui-color-error, #dc2626);font-weight:var(--ui-font-weight-semibold)}.ui-form-builder__field-label{display:block;font-size:var(--ui-font-size-sm);font-weight:var(--ui-font-weight-medium);color:var(--ui-color-text);margin-bottom:var(--ui-spacing-4)}.ui-form-builder__required{color:var(--ui-color-error, #dc2626);margin-left:2px}.ui-form-builder__field-error{margin-top:var(--ui-spacing-1);font-size:var(--ui-font-size-xs);color:var(--ui-color-error, #dc2626);display:flex;flex-direction:column;gap:2px}.ui-form-builder__radio-wrapper mat-radio-group{display:flex;flex-wrap:wrap;gap:var(--ui-spacing-3)}.ui-form-builder__chips-preview{display:flex;flex-wrap:wrap;gap:var(--ui-spacing-1);margin-top:var(--ui-spacing-1)}.ui-form-builder__chip{display:inline-flex;align-items:center;gap:4px;padding:2px var(--ui-spacing-2);border-radius:var(--ui-radius-full);background:var(--ui-color-primary-subtle, rgba(37, 99, 235, .08));color:var(--ui-color-primary);font-size:var(--ui-font-size-xs)}.ui-form-builder__chip-remove{appearance:none;border:none;background:transparent;cursor:pointer;padding:0;display:flex;color:inherit;opacity:.7}.ui-form-builder__chip-remove:hover{opacity:1}.ui-form-builder__option-icon{margin-right:var(--ui-spacing-2);vertical-align:middle}.ui-form-builder__option-info{margin-left:auto;color:var(--ui-color-text-muted)}.ui-form-builder__inline-tooltip{margin-left:var(--ui-spacing-1);color:var(--ui-color-text-muted);cursor:help}.ui-form-builder__divider{grid-column:1/-1!important;display:flex;align-items:center;gap:var(--ui-spacing-3);padding:var(--ui-spacing-2) 0}.ui-form-builder__divider-label{font-size:var(--ui-font-size-sm);font-weight:var(--ui-font-weight-medium);color:var(--ui-color-text-secondary);white-space:nowrap}.ui-form-builder__divider-line{flex:1;border:none;border-top:1px solid var(--ui-color-border);margin:0}.ui-form-builder__custom-placeholder{display:flex;align-items:center;gap:var(--ui-spacing-2);padding:var(--ui-spacing-4);border:1px dashed var(--ui-color-border);border-radius:var(--ui-radius-md);color:var(--ui-color-text-muted);font-size:var(--ui-font-size-sm)}.ui-form-builder__footer{display:flex;align-items:center;justify-content:space-between;gap:var(--ui-spacing-4);padding-top:var(--ui-spacing-4);border-top:1px solid var(--ui-color-border)}@media (max-width: 767.98px){.ui-form-builder__footer{flex-direction:column;align-items:stretch}}.ui-form-field--highlight{animation:ui-field-highlight-pulse 1s ease-in-out 2;border-radius:var(--ui-radius-md)}@keyframes ui-field-highlight-pulse{0%{box-shadow:0 0 #dc262666;padding:10px}50%{box-shadow:0 0 0 6px #dc262626;padding:10px}to{box-shadow:0 0 #dc262600;padding:10px}}.ui-form-builder mat-error{font-size:var(--ui-font-size-xs, 12px);display:flex;flex-direction:column;transform:translateY(-5px)}.ui-form-builder .mat-mdc-form-field.mat-form-field-invalid .mdc-notched-outline__leading,.ui-form-builder .mat-mdc-form-field.mat-form-field-invalid .mdc-notched-outline__notch,.ui-form-builder .mat-mdc-form-field.mat-form-field-invalid .mdc-notched-outline__trailing{border-color:var(--ui-color-error, #dc2626)!important}.ui-form-builder .mat-mdc-form-field.mat-form-field-invalid .mat-mdc-floating-label{color:var(--ui-color-error, #dc2626)}.ui-form-builder__section--collapsible.mat-expansion-panel{box-shadow:none;border:1px solid var(--ui-color-border);border-radius:var(--ui-radius-lg)!important}.ui-form-builder__free-multi-field mat-chip-grid{display:flex;flex-wrap:wrap;gap:var(--ui-spacing-1)}.ui-form-builder__free-multi-field input[matInput]{flex:1;min-width:80px}.ui-form-builder mat-datepicker-toggle{display:inline-flex;align-items:center}.mdc-notched-outline__notch{border-left:none!important}.mat-expansion-panel{box-shadow:none!important}\n"] }]
|
|
801
|
+
}], propDecorators: { schema: [{
|
|
802
|
+
type: Input
|
|
803
|
+
}], initialData: [{
|
|
804
|
+
type: Input
|
|
805
|
+
}], readonly: [{
|
|
806
|
+
type: Input
|
|
807
|
+
}], disabled: [{
|
|
808
|
+
type: Input
|
|
809
|
+
}], buttonsOverride: [{
|
|
810
|
+
type: Input
|
|
811
|
+
}], loadingFor: [{
|
|
812
|
+
type: Input
|
|
813
|
+
}], valueChange: [{
|
|
814
|
+
type: Output
|
|
815
|
+
}], validationChange: [{
|
|
816
|
+
type: Output
|
|
817
|
+
}], formSubmit: [{
|
|
818
|
+
type: Output
|
|
819
|
+
}], formReset: [{
|
|
820
|
+
type: Output
|
|
821
|
+
}], customEvent: [{
|
|
822
|
+
type: Output
|
|
823
|
+
}] } });
|
|
824
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9ybS1idWlsZGVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL25nLXVpLXN5c3RlbS9zcmMvbGliL2NvbXBvbmVudHMvZm9ybS1idWlsZGVyL2Zvcm0tYnVpbGRlci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9uZy11aS1zeXN0ZW0vc3JjL2xpYi9jb21wb25lbnRzL2Zvcm0tYnVpbGRlci9mb3JtLWJ1aWxkZXIuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLFNBQVMsRUFDVCxLQUFLLEVBQ0wsTUFBTSxFQUNOLFlBQVksRUFNWixpQkFBaUIsRUFDakIsaUJBQWlCLEVBQ2pCLE1BQU0sRUFDTixVQUFVLEdBQ1gsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUNMLFNBQVMsRUFDVCxXQUFXLEVBR1gsbUJBQW1CLEdBR3BCLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEIsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDbkQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDbEUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3pELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUMvRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN0RSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDekQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDbkUsT0FBTyxFQUNMLG1CQUFtQixFQUNuQixpQkFBaUIsRUFDakIsV0FBVyxFQUNYLGdCQUFnQixFQUNo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QUFlLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRTt3QkFDL0MsZ0RBQWdEO3dCQUNoRCxFQUFFLE9BQU8sRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLG9CQUFvQixFQUFFO3dCQUN4RCxxQ0FBcUM7d0JBQ3JDLEVBQUUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLFFBQVEsRUFBRSxrQkFBa0IsRUFBRTtxQkFDNUQsaUJBQ2MsaUJBQWlCLENBQUMsSUFBSSxRQUMvQixFQUFFLEtBQUssRUFBRSxzQkFBc0IsRUFBRTs4QkFtQjlCLE1BQU07c0JBQWQsS0FBSztnQkFHRyxXQUFXO3NCQUFuQixLQUFLO2dCQUdHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBR0csUUFBUTtzQkFBaEIsS0FBSztnQkFHRyxlQUFlO3NCQUF2QixLQUFLO2dCQUdHLFVBQVU7c0JBQWxCLEtBQUs7Z0JBSUksV0FBVztzQkFBcEIsTUFBTTtnQkFDRyxnQkFBZ0I7c0JBQXpCLE1BQU07Z0JBQ0csVUFBVTtzQkFBbkIsTUFBTTtnQkFDRyxTQUFTO3NCQUFsQixNQUFNO2dCQUNHLFdBQVc7c0JBQXBCLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xyXG4gIENvbXBvbmVudCxcclxuICBJbnB1dCxcclxuICBPdXRwdXQsXHJcbiAgRXZlbnRFbWl0dGVyLFxyXG4gIE9uSW5pdCxcclxuICBPbkNoYW5nZXMsXHJcbiAgT25EZXN0cm95LFxyXG4gIFNpbXBsZUNoYW5nZXMsXHJcbiAgQWZ0ZXJWaWV3SW5pdCxcclxuICBDaGFuZ2VEZXRlY3RvclJlZixcclxuICBWaWV3RW5jYXBzdWxhdGlvbixcclxuICBpbmplY3QsXHJcbiAgRWxlbWVudFJlZixcclxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHtcclxuICBGb3JtR3JvdXAsXHJcbiAgRm9ybUNvbnRyb2wsXHJcbiAgRm9ybUdyb3VwRGlyZWN0aXZlLFxyXG4gIE5nRm9ybSxcclxuICBSZWFjdGl2ZUZvcm1zTW9kdWxlLFxyXG4gIFZhbGlkYXRvcnMsXHJcbiAgQWJzdHJhY3RDb250cm9sLFxyXG59IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcclxuaW1wb3J0IHsgTmdUZW1wbGF0ZU91dGxldCB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XHJcbmltcG9ydCB7IE1hdEZvcm1GaWVsZE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2Zvcm0tZmllbGQnO1xyXG5pbXBvcnQgeyBNYXRJbnB1dE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2lucHV0JztcclxuaW1wb3J0IHsgTWF0U2VsZWN0TW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvc2VsZWN0JztcclxuaW1wb3J0IHsgTWF0Q2hlY2tib3hNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9jaGVja2JveCc7XHJcbmltcG9ydCB7IE1hdFNsaWRlVG9nZ2xlTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvc2xpZGUtdG9nZ2xlJztcclxuaW1wb3J0IHsgTWF0UmFkaW9Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9yYWRpbyc7XHJcbmltcG9ydCB7IE1hdERhdGVwaWNrZXJNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9kYXRlcGlja2VyJztcclxuaW1wb3J0IHtcclxuICBNYXROYXRpdmVEYXRlTW9kdWxlLFxyXG4gIEVycm9yU3RhdGVNYXRjaGVyLFxyXG4gIERhdGVBZGFwdGVyLFxyXG4gIE1BVF9EQVRFX0ZPUk1BVFMsXHJcbiAgTUFUX0RBVEVfTE9DQUxFLFxyXG59IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2NvcmUnO1xyXG5pbXBvcnQgeyBNYXRBdXRvY29tcGxldGVNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9hdXRvY29tcGxldGUnO1xyXG5pbXBvcnQgeyBNYXRDaGlwc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2NoaXBzJztcclxuaW1wb3J0IHsgTWF0RXhwYW5zaW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvZXhwYW5zaW9uJztcclxuaW1wb3J0IHsgTWF0VG9vbHRpcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnO1xyXG5pbXBvcnQgeyBMdWNpZGVBbmd1bGFyTW9kdWxlIH0gZnJvbSAnbHVjaWRlLWFuZ3VsYXInO1xyXG5pbXBvcnQgeyBTdWJqZWN0LCBTdWJzY3JpcHRpb24sIE9ic2VydmFibGUgfSBmcm9tICdyeGpzJztcclxuaW1wb3J0IHsgZGVib3VuY2VUaW1lLCB0YWtlVW50aWwgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XHJcbmltcG9ydCB7IFVpQnV0dG9uQ29tcG9uZW50LCBVaUJ1dHRvbkRlc2NyaXB0b3IgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL2J1dHRvbi9pbmRleCc7XHJcbmltcG9ydCB7IFVpQnV0dG9uQXJlYUNvbXBvbmVudCB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvYnV0dG9uL2J1dHRvbi1hcmVhLmNvbXBvbmVudCc7XHJcblxyXG5pbXBvcnQgeyBVaUZvcm1TY2hlbWEsIFVpRm9ybVNlY3Rpb24sIFVpRm9ybURhdGEsIFVpRm9ybUN1c3RvbUV2ZW50LCBVaUZvcm1CdWlsZGVyQ29uZmlnIH0gZnJvbSAnLi90eXBlcy9zY2hlbWEudHlwZXMnO1xyXG5pbXBvcnQgeyBVaUZvcm1GaWVsZERlc2NyaXB0b3IsIFVpRmllbGRPcHRpb24sIFVpRmllbGRUeXBlIH0gZnJvbSAnLi90eXBlcy9maWVsZC50eXBlcyc7XHJcbmltcG9ydCB7IFVpRm9ybVZhbGlkYXRpb25TdGF0ZSwgVWlGb3JtRXJyb3JEZXRhaWwsIFVpVmFsaWRhdGlvblJ1bGUgfSBmcm9tICcuL3R5cGVzL3ZhbGlkYXRpb24udHlwZXMnO1xyXG5pbXBvcnQgeyBVaUZpZWxkQ29uZGl0aW9uIH0gZnJvbSAnLi90eXBlcy9jb25kaXRpb24udHlwZXMnO1xyXG5pbXBvcnQgeyBVaUZvcm1Db25kaXRpb25TZXJ2aWNlIH0gZnJvbSAnLi9zZXJ2aWNlcy9mb3JtLWNvbmRpdGlvbi5zZXJ2aWNlJztcclxuaW1wb3J0IHsgVWlGb3JtVmFsaWRhdGlvblNlcnZpY2UgfSBmcm9tICcuL3NlcnZpY2VzL2Zvcm0tdmFsaWRhdGlvbi5zZXJ2aWNlJztcclxuaW1wb3J0IHsgVWlGb3JtRXJyb3JTdW1tYXJ5Q29tcG9uZW50IH0gZnJvbSAnLi9zdWItY29tcG9uZW50cy9lcnJvci1zdW1tYXJ5L2Zvcm0tZXJyb3Itc3VtbWFyeS5jb21wb25lbnQnO1xyXG5pbXBvcnQgeyBVaUZpbGVJbnB1dENvbXBvbmVudCB9IGZyb20gJy4vc3ViLWNvbXBvbmVudHMvZmlsZS1pbnB1dC9maWxlLWlucHV0LmNvbXBvbmVudCc7XHJcbmltcG9ydCB7IFVpU3BlY2lmaWNhVGVycml0b3JpYWxlQ29tcG9uZW50IH0gZnJvbSAnLi9zdWItY29tcG9uZW50cy9zcGVjaWZpY2EtdGVycml0b3JpYWxlL3NwZWNpZmljYS10ZXJyaXRvcmlhbGUuY29tcG9uZW50JztcclxuaW1wb3J0IHsgVWlUYWJsZVRlcnJpdG9yaWFsZUNvbXBvbmVudCB9IGZyb20gJy4vc3ViLWNvbXBvbmVudHMvdGFibGUtdGVycml0b3JpYWxlL3RhYmxlLXRlcnJpdG9yaWFsZS5jb21wb25lbnQnO1xyXG5pbXBvcnQgeyBVaUN1cnJlbmN5SW5wdXREaXJlY3RpdmUgfSBmcm9tICcuL2RpcmVjdGl2ZXMvY3VycmVuY3ktaW5wdXQuZGlyZWN0aXZlJztcclxuaW1wb3J0IHsgVWlJdGFsaWFuRGF0ZUFkYXB0ZXIsIFVJX0lUX0RBVEVfRk9STUFUUyB9IGZyb20gJy4vYWRhcHRlcnMvaXQtZGF0ZS1hZGFwdGVyJztcclxuXHJcbi8qKlxyXG4gKiBFcnJvclN0YXRlTWF0Y2hlciBjdXN0b20gcGVyIGlsIGZvcm0gYnVpbGRlci5cclxuICogTW9zdHJhIGxvIHN0YXRvIGRpIGVycm9yZSBxdWFuZG8gaWwgY2FtcG8gZSBpbnZhbGlkbyBFICh0b3VjaGVkIE9QUFVSRSBkaXJ0eSkuXHJcbiAqIFNvc3RpdHVpc2NlIGlsIG1hdGNoZXIgZGkgZGVmYXVsdCBkaSBBbmd1bGFyIE1hdGVyaWFsLlxyXG4gKi9cclxuZXhwb3J0IGNsYXNzIFVpRm9ybUVycm9yU3RhdGVNYXRjaGVyIGltcGxlbWVudHMgRXJyb3JTdGF0ZU1hdGNoZXIge1xyXG4gIGlzRXJyb3JTdGF0ZShjb250cm9sOiBGb3JtQ29udHJvbCB8IG51bGwsIGZvcm06IEZvcm1Hcm91cERpcmVjdGl2ZSB8IE5nRm9ybSB8IG51bGwpOiBib29sZWFuIHtcclxuICAgIHJldHVybiAhIShjb250cm9sICYmIGNvbnRyb2wuaW52YWxpZCAmJiAoY29udHJvbC50b3VjaGVkIHx8IGNvbnRyb2wuZGlydHkpKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGb3JtIGJ1aWxkZXIgZGF0YS1kcml2ZW4gZSBzY2hlbWEtYmFzZWQuXHJcbiAqXHJcbiAqIEdlbmVyYSB1biBmb3JtIGNvbXBsZXRvIGEgcGFydGlyZSBkYSB1bm8gc2NoZW1hIGRpY2hpYXJhdGl2byAoYFVpRm9ybVNjaGVtYWApLlxyXG4gKiBTdXBwb3J0YSAxNiB0aXBpIGRpIGNhbXBvLCB2YWxpZGF6aW9uZSBjb25kaXppb25hbGUsIGNyb3NzLWZpZWxkIHZhbGlkYXRpb24sXHJcbiAqIGRhdGUgY29uIG9mZnNldCwgb3B6aW9uaSBkaXBlbmRlbnRpL2RpbmFtaWNoZSwgc2V6aW9uaSBjb2xsYXBzaWJpbGksXHJcbiAqIGVycm9yIHN1bW1hcnkgY29uIHNjcm9sbC10by1maWVsZCBlIGJ1dHRvbiBvdmVycmlkZS5cclxuICpcclxuICogQHNlbGVjdG9yIHVpLWZvcm0tYnVpbGRlclxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKiBgYGBodG1sXHJcbiAqIDx1aS1mb3JtLWJ1aWxkZXJcclxuICogICBbc2NoZW1hXT1cImZvcm1TY2hlbWFcIlxyXG4gKiAgIFtpbml0aWFsRGF0YV09XCJ1c2VyRGF0YVwiXHJcbiAqICAgKGZvcm1TdWJtaXQpPVwib25TdWJtaXQoJGV2ZW50KVwiXHJcbiAqICAgKHZhbHVlQ2hhbmdlKT1cIm9uQ2hhbmdlKCRldmVudClcIlxyXG4gKiAvPlxyXG4gKiBgYGBcclxuICovXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAndWktZm9ybS1idWlsZGVyJyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtcclxuICAgIFJlYWN0aXZlRm9ybXNNb2R1bGUsXHJcbiAgICBOZ1RlbXBsYXRlT3V0bGV0LFxyXG4gICAgTWF0Rm9ybUZpZWxkTW9kdWxlLFxyXG4gICAgTWF0SW5wdXRNb2R1bGUsXHJcbiAgICBNYXRTZWxlY3RNb2R1bGUsXHJcbiAgICBNYXRDaGVja2JveE1vZHVsZSxcclxuICAgIE1hdFNsaWRlVG9nZ2xlTW9kdWxlLFxyXG4gICAgTWF0UmFkaW9Nb2R1bGUsXHJcbiAgICBNYXREYXRlcGlja2VyTW9kdWxlLFxyXG4gICAgTWF0TmF0aXZlRGF0ZU1vZHVsZSxcclxuICAgIE1hdEF1dG9jb21wbGV0ZU1vZHVsZSxcclxuICAgIE1hdENoaXBzTW9kdWxlLFxyXG4gICAgTWF0RXhwYW5zaW9uTW9kdWxlLFxyXG4gICAgTWF0VG9vbHRpcE1vZHVsZSxcclxuICAgIEx1Y2lkZUFuZ3VsYXJNb2R1bGUsXHJcbiAgICBVaUJ1dHRvbkFyZWFDb21wb25lbnQsXHJcbiAgICBVaUZvcm1FcnJvclN1bW1hcnlDb21wb25lbnQsXHJcbiAgICBVaUZpbGVJbnB1dENvbXBvbmVudCxcclxuICAgIFVpU3BlY2lmaWNhVGVycml0b3JpYWxlQ29tcG9uZW50LFxyXG4gICAgVWlUYWJsZVRlcnJpdG9yaWFsZUNvbXBvbmVudCxcclxuICAgIFVpQ3VycmVuY3lJbnB1dERpcmVjdGl2ZSxcclxuICBdLFxyXG4gIHByb3ZpZGVyczogW1xyXG4gICAgeyBwcm92aWRlOiBFcnJvclN0YXRlTWF0Y2hlciwgdXNlQ2xhc3M6IFVpRm9ybUVycm9yU3RhdGVNYXRjaGVyIH0sXHJcbiAgICAvLyBMb2NhbGUgaXRhbGlhbm8gcGVyIGlsIGRhdGVwaWNrZXIgTWF0ZXJpYWxcclxuICAgIHsgcHJvdmlkZTogTUFUX0RBVEVfTE9DQUxFLCB1c2VWYWx1ZTogJ2l0LUlUJyB9LFxyXG4gICAgLy8gQWRhcHRlciBwZXJzb25hbGl6emF0byBwZXIgcGFyc2luZyBERC9NTS9ZWVlZXHJcbiAgICB7IHByb3ZpZGU6IERhdGVBZGFwdGVyLCB1c2VDbGFzczogVWlJdGFsaWFuRGF0ZUFkYXB0ZXIgfSxcclxuICAgIC8vIEZvcm1hdGkgZGF0YSBpdGFsaWFuaSAoREQvTU0vWVlZWSlcclxuICAgIHsgcHJvdmlkZTogTUFUX0RBVEVfRk9STUFUUywgdXNlVmFsdWU6IFVJX0lUX0RBVEVfRk9STUFUUyB9LFxyXG4gIF0sXHJcbiAgZW5jYXBzdWxhdGlvbjogVmlld0VuY2Fwc3VsYXRpb24uTm9uZSxcclxuICBob3N0OiB7IGNsYXNzOiAndWktZm9ybS1idWlsZGVyLWhvc3QnIH0sXHJcbiAgdGVtcGxhdGVVcmw6ICcuL2Zvcm0tYnVpbGRlci5jb21wb25lbnQuaHRtbCcsXHJcbiAgc3R5bGVVcmw6ICcuL2Zvcm0tYnVpbGRlci5jb21wb25lbnQuc2NzcycsXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBVaUZvcm1CdWlsZGVyQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSwgQWZ0ZXJWaWV3SW5pdCB7XHJcbiAgcHJpdmF0ZSByZWFkb25seSBjZHIgPSBpbmplY3QoQ2hhbmdlRGV0ZWN0b3JSZWYpO1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgZWxSZWYgPSBpbmplY3QoRWxlbWVudFJlZik7XHJcbiAgcHJpdmF0ZSByZWFkb25seSBjb25kaXRpb25TZXJ2aWNlID0gaW5qZWN0KFVpRm9ybUNvbmRpdGlvblNlcnZpY2UpO1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgdmFsaWRhdGlvblNlcnZpY2UgPSBpbmplY3QoVWlGb3JtVmFsaWRhdGlvblNlcnZpY2UpO1xyXG5cclxuICBwcml2YXRlIHJlYWRvbmx5IGRlc3Ryb3kkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcclxuICBwcml2YXRlIHZhbHVlQ2hhbmdlU3ViPzogU3Vic2NyaXB0aW9uO1xyXG5cclxuICAvKiogSUQgc2NoZW1hIGNvcnJlbnRlIC0tIHVzYXRvIHBlciBldml0YXJlIHJlYnVpbGQgc3VwZXJmbHVpLiAqL1xyXG4gIHByaXZhdGUgY3VycmVudFNjaGVtYUlkOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcclxuXHJcbiAgLy8g4pSA4pSA4pSAIElucHV0IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxyXG5cclxuICAvKiogU2NoZW1hIGRlbCBmb3JtLiAqL1xyXG4gIEBJbnB1dCgpIHNjaGVtYSE6IFVpRm9ybVNjaGVtYTtcclxuXHJcbiAgLyoqIERhdGkgaW5pemlhbGkgcGVyIGxhIHByZXZhbG9yaXp6YXppb25lLiAqL1xyXG4gIEBJbnB1dCgpIGluaXRpYWxEYXRhOiBVaUZvcm1EYXRhID0ge307XHJcblxyXG4gIC8qKiBNb2RhbGl0YSBzb2xhIGxldHR1cmEgZ2xvYmFsZS4gKi9cclxuICBASW5wdXQoKSByZWFkb25seSA9IGZhbHNlO1xyXG5cclxuICAvKiogU3RhdG8gZGlzYWJpbGl0YXRvIGdsb2JhbGUuICovXHJcbiAgQElucHV0KCkgZGlzYWJsZWQgPSBmYWxzZTtcclxuXHJcbiAgLyoqIE92ZXJyaWRlIGRlaSBwdWxzYW50aSBkaSBkZWZhdWx0LiAqL1xyXG4gIEBJbnB1dCgpIGJ1dHRvbnNPdmVycmlkZT86IFVpQnV0dG9uRGVzY3JpcHRvcltdO1xyXG5cclxuICAvKiogQ2hpYXZlIGRlbCBwdWxzYW50ZSBpbiBzdGF0byBkaSBjYXJpY2FtZW50by4gKi9cclxuICBASW5wdXQoKSBsb2FkaW5nRm9yOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcclxuXHJcbiAgLy8g4pSA4pSA4pSAIE91dHB1dCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcclxuXHJcbiAgQE91dHB1dCgpIHZhbHVlQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxVaUZvcm1EYXRhPigpO1xyXG4gIEBPdXRwdXQoKSB2YWxpZGF0aW9uQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxVaUZvcm1WYWxpZGF0aW9uU3RhdGU+KCk7XHJcbiAgQE91dHB1dCgpIGZvcm1TdWJtaXQgPSBuZXcgRXZlbnRFbWl0dGVyPFVpRm9ybURhdGE+KCk7XHJcbiAgQE91dHB1dCgpIGZvcm1SZXNldCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcclxuICBAT3V0cHV0KCkgY3VzdG9tRXZlbnQgPSBuZXcgRXZlbnRFbWl0dGVyPFVpRm9ybUN1c3RvbUV2ZW50PigpO1xyXG5cclxuICAvLyDilIDilIDilIAgU3RhdG8gaW50ZXJubyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcclxuXHJcbiAgZm9ybUdyb3VwITogRm9ybUdyb3VwO1xyXG5cclxuICAvKiogTWFwcGEgY2FtcG8gLT4gb3B6aW9uaSBmaWx0cmF0ZSAocGVyIGF1dG9jb21wbGV0ZSBlIGRlcGVuZGVudCkuICovXHJcbiAgZmlsdGVyZWRPcHRpb25zOiBSZWNvcmQ8c3RyaW5nLCBVaUZpZWxkT3B0aW9uW10+ID0ge307XHJcblxyXG4gIC8qKiBDYW1waSBwcmV2YWxvcml6emF0aSAobW9zdHJhbm8gZXJyb3JpIHN1Yml0bykuICovXHJcbiAgcHJldmFsb3JpemVkRmllbGRzID0gbmV3IFNldDxzdHJpbmc+KCk7XHJcblxyXG4gIC8qKiBNYXBwYSBjYW1wbyAtPiByZWdvbGUgZGkgdmFsaWRhemlvbmUgKHBlciBsb29rdXAgcmFwaWRvKS4gKi9cclxuICBwcml2YXRlIHZhbGlkYXRpb25SdWxlc01hcDogUmVjb3JkPHN0cmluZywgVWlWYWxpZGF0aW9uUnVsZVtdPiA9IHt9O1xyXG5cclxuICAvKiogTWFwcGEgZGlwZW5kZW56ZTogY2FtcG9UYXJnZXQgLT4gY2FtcGkgZGlwZW5kZW50aS4gKi9cclxuICBwcml2YXRlIGZpZWxkRGVwZW5kZW5jaWVzID0gbmV3IE1hcDxzdHJpbmcsIFNldDxzdHJpbmc+PigpO1xyXG5cclxuICAvKiogRmxhZyBwZXIgZXZpdGFyZSBlbWlzc2lvbmkgZHVyYW50ZSBsJ2luaXppYWxpenphemlvbmUuICovXHJcbiAgcHJpdmF0ZSBpc0luaXRpYWxpemluZyA9IHRydWU7XHJcblxyXG4gIC8vIOKUgOKUgOKUgCBHZXR0ZXIgY2FsY29sYXRpIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxyXG5cclxuICBnZXQgYWxsRmllbGRzKCk6IFVpRm9ybUZpZWxkRGVzY3JpcHRvcltdIHtcclxuICAgIHJldHVybiB0aGlzLnNjaGVtYT8uc2VjdGlvbnM/LmZsYXRNYXAoKHMpID0+IHMuZmllbGRzKSB8fCBbXTtcclxuICB9XHJcblxyXG4gIGdldCBmb3JtRXJyb3JzKCk6IFVpRm9ybUVycm9yRGV0YWlsW10ge1xyXG4gICAgcmV0dXJuIHRoaXMuZ2V0RGV0YWlsZWRGb3JtRXJyb3JzKCk7XHJcbiAgfVxyXG5cclxuICBnZXQgZm9ybUJ1dHRvbnMoKTogVWlCdXR0b25EZXNjcmlwdG9yW10ge1xyXG4gICAgaWYgKHRoaXMuYnV0dG9uc092ZXJyaWRlKSByZXR1cm4gdGhpcy5idXR0b25zT3ZlcnJpZGU7XHJcbiAgICBpZiAoIXRoaXMuc2NoZW1hPy5jb25maWc/LnNob3dCdXR0b25zICYmIHRoaXMuc2NoZW1hPy5jb25maWc/LnNob3dCdXR0b25zICE9PSB1bmRlZmluZWQpIHJldHVybiBbXTtcclxuICAgIHJldHVybiBbXHJcbiAgICAgIHsgbGFiZWw6IHRoaXMuc2NoZW1hPy5jb25maWc/LmJ1dHRvbkxhYmVscz8ucmVzZXQgfHwgJ1Jlc2V0JywgdmFyaWFudDogJ291dGxpbmUnLCBhY3Rpb246ICgpID0+IHRoaXMub25SZXNldCgpIH0sXHJcbiAgICAgIHtcclxuICAgICAgICBsYWJlbDogdGhpcy5zY2hlbWE/LmNvbmZpZz8uYnV0dG9uTGFiZWxzPy5zdWJtaXQgfHwgJ0ludmlhJyxcclxuICAgICAgICB2YXJpYW50OiAncHJpbWFyeScsXHJcbiAgICAgICAgYWN0aW9uOiAoKSA9PiB0aGlzLm9uU3VibWl0KCksXHJcbiAgICAgIH0sXHJcbiAgICBdO1xyXG4gIH1cclxuXHJcbiAgLy8g4pSA4pSA4pSAIExpZmVjeWNsZSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcclxuICAvL1xyXG4gIC8vIE9SRElORSBMSUZFQ1lDTEUgQU5HVUxBUjpcclxuICAvLyAxLiBjb25zdHJ1Y3RvclxyXG4gIC8vIDIuIG5nT25DaGFuZ2VzIChwcmltYSBjaGlhbWF0YSBjb24gdmFsb3JpIGluaXppYWxpKVxyXG4gIC8vIDMuIG5nT25Jbml0XHJcbiAgLy8gNC4gbmdBZnRlclZpZXdJbml0XHJcbiAgLy8gNS4gbmdPbkNoYW5nZXMgKGNoaWFtYXRlIHN1Y2Nlc3NpdmUgcGVyIGNhbWJpbyBpbnB1dClcclxuICAvL1xyXG4gIC8vIFBFUiBJTCBXSVpBUkQ6XHJcbiAgLy8gSWwgd2l6YXJkIGNyZWEgdW4gbnVvdm8gc2NoZW1hIChjb24gaWQgZGl2ZXJzbykgYWQgb2duaSBjYW1iaW8gc3RlcC5cclxuICAvLyBMbyBzY2hlbWEgdmllbmUgY2FjaGF0byBuZWwgd2l6YXJkIHBlciBldml0YXJlIHJlYnVpbGQgaW5maW5pdGkuXHJcbiAgLy8gbmdPbkNoYW5nZXMgcmlsZXZhIGlsIGNhbWJpbyBkaSBzY2hlbWEuaWQgZSByaWNvc3RydWlzY2UgaWwgZm9ybS5cclxuXHJcbiAgbmdPbkluaXQoKTogdm9pZCB7XHJcbiAgICAvLyBbREVCVUddIFZlcmlmaWNhIGNoZSBsbyBzY2hlbWEgc2lhIGRpc3BvbmliaWxlIGFsIG1vbWVudG8gZGkgbmdPbkluaXRcclxuICAgIGNvbnNvbGUuZGVidWcoJ1tVaUZvcm1CdWlsZGVyXSBuZ09uSW5pdCAtIHNjaGVtYTonLCB0aGlzLnNjaGVtYT8uaWQsICd8IGZvcm1Hcm91cCBkZWZpbml0bzonLCAhIXRoaXMuZm9ybUdyb3VwKTtcclxuICAgIGlmICghdGhpcy5zY2hlbWEpIHtcclxuICAgICAgY29uc29sZS53YXJuKCdbVWlGb3JtQnVpbGRlcl0gbmdPbkluaXQgLSBzY2hlbWEgbm9uIHByZXNlbnRlLCBpbCBmb3JtIG5vbiB2ZXJyYSBjb3N0cnVpdG8gb3JhJyk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIHRoaXMuaW5pdEZvcm1Gcm9tU2NoZW1hKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSaWxldmEgY2FtYmlhbWVudGkgc3VnbGkgQElucHV0LlxyXG4gICAqIFJpY29zdHJ1aXNjZSBpbCBmb3JtIHF1YW5kbyBjYW1iaWEgbG8gc2NoZW1hIChpZCBkaXZlcnNvKS5cclxuICAgKiBDcnVjaWFsZSBwZXIgaWwgd2l6YXJkIGNoZSBjYW1iaWEgc2NoZW1hIGFkIG9nbmkgc3RlcC5cclxuICAgKi9cclxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XHJcbiAgICAvLyBbREVCVUddIExvZyBkaSB0dXR0aSBpIGNhbWJpYW1lbnRpIGRlZ2xpIElucHV0XHJcbiAgICBjb25zdCBjaGFuZ2VkSW5wdXRzID0gT2JqZWN0LmtleXMoY2hhbmdlcylcclxuICAgICAgLm1hcCgoaykgPT4gYCR7a30oZmlyc3RDaGFuZ2U9JHtjaGFuZ2VzW2tdLmZpcnN0Q2hhbmdlfSlgKVxyXG4gICAgICAuam9pbignLCAnKTtcclxuICAgIGNvbnNvbGUuZGVidWcoJ1tVaUZvcm1CdWlsZGVyXSBuZ09uQ2hhbmdlcyAtJywgY2hhbmdlZElucHV0cyk7XHJcblxyXG4gICAgaWYgKGNoYW5nZXNbJ3NjaGVtYSddICYmICFjaGFuZ2VzWydzY2hlbWEnXS5maXJzdENoYW5nZSkge1xyXG4gICAgICBjb25zdCBuZXdTY2hlbWEgPSBjaGFuZ2VzWydzY2hlbWEnXS5jdXJyZW50VmFsdWUgYXMgVWlGb3JtU2NoZW1hO1xyXG4gICAgICBjb25zb2xlLmRlYnVnKCdbVWlGb3JtQnVpbGRlcl0gbmdPbkNoYW5nZXMgLSBudW92byBzY2hlbWE6JywgbmV3U2NoZW1hPy5pZCwgJ3wgY29ycmVudGU6JywgdGhpcy5jdXJyZW50U2NoZW1hSWQpO1xyXG4gICAgICBpZiAobmV3U2NoZW1hICYmIG5ld1NjaGVtYS5pZCAhPT0gdGhpcy5jdXJyZW50U2NoZW1hSWQpIHtcclxuICAgICAgICBjb25zb2xlLmRlYnVnKCdbVWlGb3JtQnVpbGRlcl0gbmdPbkNoYW5nZXMgLSBSRUJVSUxEOiBpZCBkaXZlcnNvLCByaWNvc3RydWlzY28gaWwgZm9ybScpO1xyXG4gICAgICAgIHRoaXMuZGVzdHJveUZvcm0oKTtcclxuICAgICAgICB0aGlzLmluaXRGb3JtRnJvbVNjaGVtYSgpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQWdnaW9ybmEgbG8gc3RhdG8gZGlzYWJsZWQvcmVhZG9ubHkgZ2xvYmFsbWVudGVcclxuICAgIGlmIChjaGFuZ2VzWydkaXNhYmxlZCddICYmICFjaGFuZ2VzWydkaXNhYmxlZCddLmZpcnN0Q2hhbmdlICYmIHRoaXMuZm9ybUdyb3VwKSB7XHJcbiAgICAgIHRoaXMuYWxsRmllbGRzLmZvckVhY2goKGZpZWxkKSA9PiB7XHJcbiAgICAgICAgY29uc3QgY29udHJvbCA9IHRoaXMuZm9ybUdyb3VwLmdldChmaWVsZC5rZXkpO1xyXG4gICAgICAgIGlmICghY29udHJvbCkgcmV0dXJuO1xyXG4gICAgICAgIGlmICh0aGlzLmRpc2FibGVkICYmIGNvbnRyb2wuZW5hYmxlZCkge1xyXG4gICAgICAgICAgY29udHJvbC5kaXNhYmxlKHsgZW1pdEV2ZW50OiBmYWxzZSB9KTtcclxuICAgICAgICB9IGVsc2UgaWYgKCF0aGlzLmRpc2FibGVkICYmIGNvbnRyb2wuZGlzYWJsZWQgJiYgIWZpZWxkLmRpc2FibGVkKSB7XHJcbiAgICAgICAgICBjb250cm9sLmVuYWJsZSh7IGVtaXRFdmVudDogZmFsc2UgfSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpOiB2b2lkIHtcclxuICAgIC8vIFtERUJVR10gVmVyaWZpY2Egc3RhdG8gZG9wbyBsYSBwcmltYSByZW5kZXJpenphemlvbmUgZGVsbGEgdmlzdGFcclxuICAgIGNvbnNvbGUuZGVidWcoXHJcbiAgICAgICdbVWlGb3JtQnVpbGRlcl0gbmdBZnRlclZpZXdJbml0IC0gZm9ybUdyb3VwIGRlZmluaXRvOicsXHJcbiAgICAgICEhdGhpcy5mb3JtR3JvdXAsXHJcbiAgICAgICd8IGNhbXBpOicsXHJcbiAgICAgIHRoaXMuZm9ybUdyb3VwID8gT2JqZWN0LmtleXModGhpcy5mb3JtR3JvdXAuY29udHJvbHMpLmxlbmd0aCA6IDAsXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XHJcbiAgICBjb25zb2xlLmRlYnVnKCdbVWlGb3JtQnVpbGRlcl0gbmdPbkRlc3Ryb3kgLSBzY2hlbWE6JywgdGhpcy5jdXJyZW50U2NoZW1hSWQpO1xyXG4gICAgdGhpcy5kZXN0cm95JC5uZXh0KCk7XHJcbiAgICB0aGlzLmRlc3Ryb3kkLmNvbXBsZXRlKCk7XHJcbiAgICB0aGlzLnZhbHVlQ2hhbmdlU3ViPy51bnN1YnNjcmliZSgpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogSW5pemlhbGl6emEgaWwgZm9ybSBkYWxsbyBzY2hlbWEgY29ycmVudGUuXHJcbiAgICogRXN0cmF0dG8gcGVyIHBvdGVyIGVzc2VyZSByaWNoaWFtYXRvIHNpYSBkYSBuZ09uSW5pdCBjaGUgZGEgbmdPbkNoYW5nZXMuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBpbml0Rm9ybUZyb21TY2hlbWEoKTogdm9pZCB7XHJcbiAgICBpZiAoIXRoaXMuc2NoZW1hKSB7XHJcbiAgICAgIGNvbnNvbGUud2FybignW1VpRm9ybUJ1aWxkZXJdIGluaXRGb3JtRnJvbVNjaGVtYSAtIHNjaGVtYSBudWxsbywgc2tpcCcpO1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgICBjb25zb2xlLmRlYnVnKFxyXG4gICAgICAnW1VpRm9ybUJ1aWxkZXJdIGluaXRGb3JtRnJvbVNjaGVtYSAtIGNvc3RydWlzY28gZm9ybSBwZXI6JyxcclxuICAgICAgdGhpcy5zY2hlbWEuaWQsXHJcbiAgICAgICd8IHNlemlvbmk6JyxcclxuICAgICAgdGhpcy5zY2hlbWEuc2VjdGlvbnM/Lmxlbmd0aCxcclxuICAgICAgJ3wgY2FtcGkgdG90YWxpOicsXHJcbiAgICAgIHRoaXMuc2NoZW1hLnNlY3Rpb25zPy5yZWR1Y2UoKHN1bSwgcykgPT4gc3VtICsgcy5maWVsZHMubGVuZ3RoLCAwKSxcclxuICAgICk7XHJcbiAgICB0aGlzLmN1cnJlbnRTY2hlbWFJZCA9IHRoaXMuc2NoZW1hLmlkO1xyXG4gICAgdGhpcy5idWlsZEZvcm0oKTtcclxuICAgIHRoaXMuaXNJbml0aWFsaXppbmcgPSBmYWxzZTtcclxuICAgIGNvbnNvbGUuZGVidWcoXHJcbiAgICAgICdbVWlGb3JtQnVpbGRlcl0gaW5pdEZvcm1Gcm9tU2NoZW1hIC0gZm9ybSBjb3N0cnVpdG8uIENvbnRyb2xzOicsXHJcbiAgICAgIE9iamVjdC5rZXlzKHRoaXMuZm9ybUdyb3VwLmNvbnRyb2xzKSxcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBQdWxpc2NlIGxvIHN0YXRvIGRlbCBmb3JtIHByaW1hIGRpIHVuIHJlYnVpbGQuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBkZXN0cm95Rm9ybSgpOiB2b2lkIHtcclxuICAgIGNvbnNvbGUuZGVidWcoJ1tVaUZvcm1CdWlsZGVyXSBkZXN0cm95Rm9ybSAtIHB1bGl6aWEgc3RhdG8gcGVyIHNjaGVtYTonLCB0aGlzLmN1cnJlbnRTY2hlbWFJZCk7XHJcbiAgICB0aGlzLnZhbHVlQ2hhbmdlU3ViPy51bnN1YnNjcmliZSgpO1xyXG4gICAgdGhpcy52YWx1ZUNoYW5nZVN1YiA9IHVuZGVmaW5lZDtcclxuICAgIHRoaXMuZmlsdGVyZWRPcHRpb25zID0ge307XHJcbiAgICB0aGlzLnByZXZhbG9yaXplZEZpZWxkcy5jbGVhcigpO1xyXG4gICAgdGhpcy52YWxpZGF0aW9uUnVsZXNNYXAgPSB7fTtcclxuICAgIHRoaXMuZmllbGREZXBlbmRlbmNpZXMuY2xlYXIoKTtcclxuICAgIHRoaXMuaXNJbml0aWFsaXppbmcgPSB0cnVlO1xyXG4gICAgdGhpcy5jdXJyZW50U2NoZW1hSWQgPSBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8g4pSA4pSA4pSAIENvc3RydXppb25lIGZvcm0g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXHJcblxyXG4gIHByaXZhdGUgYnVpbGRGb3JtKCk6IHZvaWQge1xyXG4gICAgY29uc3QgY29udHJvbHM6IFJlY29yZDxzdHJpbmcsIEZvcm1Db250cm9sPiA9IHt9O1xyXG5cclxuICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGhpcy5hbGxGaWVsZHMpIHtcclxuICAgICAgY29uc3QgaW5pdGlhbFZhbHVlID0gdGhpcy5pbml0aWFsRGF0YT8uW2ZpZWxkLmtleV0gPz8gZmllbGQuZGVmYXVsdFZhbHVlID8/IG51bGw7XHJcbiAgICAgIGNvbnN0IGlzRGlzYWJsZWQgPSBmaWVsZC5kaXNhYmxlZCB8fCB0aGlzLmRpc2FibGVkO1xyXG5cclxuICAgICAgLy8gQ3JlYSBpbCBGb3JtQ29udHJvbFxyXG4gICAgICBjb25zdCBjb250cm9sID0gbmV3IEZvcm1Db250cm9sKHsgdmFsdWU6IGluaXRpYWxWYWx1ZSwgZGlzYWJsZWQ6IGlzRGlzYWJsZWQgfSk7XHJcblxyXG4gICAgICAvLyBSZWdpc3RyYSBsZSByZWdvbGUgZGkgdmFsaWRhemlvbmVcclxuICAgICAgaWYgKGZpZWxkLnZhbGlkYXRpb24/Lmxlbmd0aCkge1xyXG4gICAgICAgIHRoaXMudmFsaWRhdGlvblJ1bGVzTWFwW2ZpZWxkLmtleV0gPSBmaWVsZC52YWxpZGF0aW9uO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBUcmFjY2lhIGNhbXBpIHByZXZhbG9yaXp6YXRpXHJcbiAgICAgIGlmIChpbml0aWFsVmFsdWUgIT0gbnVsbCAmJiBpbml0aWFsVmFsdWUgIT09ICcnICYmIGluaXRpYWxWYWx1ZSAhPT0gZmFsc2UpIHtcclxuICAgICAgICB0aGlzLnByZXZhbG9yaXplZEZpZWxkcy5hZGQoZmllbGQua2V5KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSW5pemlhbGl6emEgb3B6aW9uaSBmaWx0cmF0ZSBwZXIgc2VsZWN0L211bHRpc2VsZWN0XHJcbiAgICAgIGlmIChmaWVsZC5vcHRpb25zICYmICF0aGlzLmlzT2JzZXJ2YWJsZShmaWVsZC5vcHRpb25zKSkge1xyXG4gICAgICAgIHRoaXMuZmlsdGVyZWRPcHRpb25zW2ZpZWxkLmtleV0gPSBbLi4uKGZpZWxkLm9wdGlvbnMgYXMgVWlGaWVsZE9wdGlvbltdKV07XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGNvbnRyb2xzW2ZpZWxkLmtleV0gPSBjb250cm9sO1xyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuZm9ybUdyb3VwID0gbmV3IEZvcm1Hcm91cChjb250cm9scyk7XHJcblxyXG4gICAgLy8gQXBwbGljYSB2YWxpZGF0b3JpIGluaXppYWxpXHJcbiAgICB0aGlzLmFwcGx5QWxsVmFsaWRhdG9ycygpO1xyXG5cclxuICAgIC8vIFJlZ2lzdHJhIGRpcGVuZGVuemVcclxuICAgIHRoaXMuYnVpbGREZXBlbmRlbmN5TWFwKCk7XHJcblxyXG4gICAgLy8gU290dG9zY3JpdmkgYWkgY2FtYmlhbWVudGkgZGkgdmFsb3JlXHJcbiAgICB0aGlzLnZhbHVlQ2hhbmdlU3ViID0gdGhpcy5mb3JtR3JvdXAudmFsdWVDaGFuZ2VzLnBpcGUoZGVib3VuY2VUaW1lKDUwKSwgdGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKS5zdWJzY3JpYmUoKCkgPT4ge1xyXG4gICAgICB0aGlzLmV2YWx1YXRlQWxsQ29uZGl0aW9ucygpO1xyXG4gICAgICB0aGlzLnVwZGF0ZURlcGVuZGVudE9wdGlvbnMoKTtcclxuICAgICAgaWYgKCF0aGlzLmlzSW5pdGlhbGl6aW5nKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZUNoYW5nZS5lbWl0KHRoaXMuZ2V0Rm9ybVZhbHVlKCkpO1xyXG4gICAgICAgIHRoaXMudmFsaWRhdGlvbkNoYW5nZS5lbWl0KHRoaXMuZ2V0VmFsaWRhdGlvblN0YXRlKCkpO1xyXG4gICAgICAgIHRoaXMuY2RyLm1hcmtGb3JDaGVjaygpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBWYWx1dGF6aW9uZSBjb25kaXppb25pIGluaXppYWxlXHJcbiAgICB0aGlzLmV2YWx1YXRlQWxsQ29uZGl0aW9ucygpO1xyXG4gIH1cclxuXHJcbiAgLy8g4pSA4pSA4pSAIFZhbGlkYXppb25lIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxyXG5cclxuICBwcml2YXRlIGFwcGx5QWxsVmFsaWRhdG9ycygpOiB2b2lkIHtcclxuICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGhpcy5hbGxGaWVsZHMpIHtcclxuICAgICAgaWYgKGZpZWxkLnZhbGlkYXRpb24/Lmxlbmd0aCkge1xyXG4gICAgICAgIGNvbnN0IGZvcm1EYXRhID0gdGhpcy5mb3JtR3JvdXAuZ2V0UmF3VmFsdWUoKTtcclxuICAgICAgICB0aGlzLnZhbGlkYXRpb25TZXJ2aWNlLnVwZGF0ZUZpZWxkVmFsaWRhdG9ycyh0aGlzLmZvcm1Hcm91cC5nZXQoZmllbGQua2V5KSEsIGZpZWxkLnZhbGlkYXRpb24sIGZvcm1EYXRhKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8g4pSA4pSA4pSAIENvbmRpemlvbmkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXHJcblxyXG4gIHByaXZhdGUgZXZhbHVhdGVBbGxDb25kaXRpb25zKCk6IHZvaWQge1xyXG4gICAgY29uc3QgZm9ybURhdGEgPSB0aGlzLmZvcm1Hcm91cC5nZXRSYXdWYWx1ZSgpO1xyXG5cclxuICAgIGZvciAoY29uc3QgZmllbGQgb2YgdGhpcy5hbGxGaWVsZHMpIHtcclxuICAgICAgLy8gVmlzaWJpbGl0YVxyXG4gICAgICBpZiAoZmllbGQuY29uZGl0aW9ucz8ubGVuZ3RoKSB7XHJcbiAgICAgICAgY29uc3QgdmlzaWJsZSA9IHRoaXMuY29uZGl0aW9uU2VydmljZS5ldmFsdWF0ZUNvbmRpdGlvbnMoZmllbGQuY29uZGl0aW9ucywgZm9ybURhdGEpO1xyXG4gICAgICAgIGNvbnN0IGNvbnRyb2wgPSB0aGlzLmZvcm1Hcm91cC5nZXQoZmllbGQua2V5KTtcclxuICAgICAgICBpZiAoY29udHJvbCkge1xyXG4gICAgICAgICAgaWYgKCF2aXNpYmxlICYmIGNvbnRyb2wuZW5hYmxlZCkge1xyXG4gICAgICAgICAgICBjb250cm9sLmRpc2FibGUoeyBlbWl0RXZlbnQ6IGZhbHNlIH0pO1xyXG4gICAgICAgICAgICBpZiAoIWZpZWxkLmZyZWV6ZVZhbHVlT25EaXNhYmxlKSB7XHJcbiAgICAgICAgICAgICAgY29udHJvbC5zZXRWYWx1ZShmaWVsZC5kZWZhdWx0VmFsdWUgPz8gbnVsbCwgeyBlbWl0RXZlbnQ6IGZhbHNlIH0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9IGVsc2UgaWYgKHZpc2libGUgJiYgY29udHJvbC5kaXNhYmxlZCAmJiAhZmllbGQuZGlzYWJsZWQgJiYgIXRoaXMuZGlzYWJsZWQpIHtcclxuICAgICAgICAgICAgY29udHJvbC5lbmFibGUoeyBlbWl0RXZlbnQ6IGZhbHNlIH0pO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRGlzYWJpbGl0YSBjb25kaXppb25hbGVcclxuICAgICAgaWYgKGZpZWxkLmRpc2FibGVDb25kaXRpb25zPy5sZW5ndGggJiYgIWZpZWxkLmNvbmRpdGlvbnM/Lmxlbmd0aCkge1xyXG4gICAgICAgIGNvbnN0IHNob3VsZERpc2FibGUgPSB0aGlzLmNvbmRpdGlvblNlcnZpY2UuZXZhbHVhdGVDb25kaXRpb25zKGZpZWxkLmRpc2FibGVDb25kaXRpb25zLCBmb3JtRGF0YSk7XHJcbiAgICAgICAgY29uc3QgY29udHJvbCA9IHRoaXMuZm9ybUdyb3VwLmdldChmaWVsZC5rZXkpO1xyXG4gICAgICAgIGlmIChjb250cm9sKSB7XHJcbiAgICAgICAgICBpZiAoc2hvdWxkRGlzYWJsZSAmJiBjb250cm9sLmVuYWJsZWQpIHtcclxuICAgICAgICAgICAgY29udHJvbC5kaXNhYmxlKHsgZW1pdEV2ZW50OiBmYWxzZSB9KTtcclxuICAgICAgICAgICAgaWYgKCFmaWVsZC5mcmVlemVWYWx1ZU9uRGlzYWJsZSkge1xyXG4gICAgICAgICAgICAgIGNvbnRyb2wuc2V0VmFsdWUoZmllbGQuZGVmYXVsdFZhbHVlID8/IG51bGwsIHsgZW1pdEV2ZW50OiBmYWxzZSB9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSBlbHNlIGlmICghc2hvdWxkRGlzYWJsZSAmJiBjb250cm9sLmRpc2FibGVkICYmICFmaWVsZC5kaXNhYmxlZCAmJiAhdGhpcy5kaXNhYmxlZCkge1xyXG4gICAgICAgICAgICBjb250cm9sLmVuYWJsZSh7IGVtaXRFdmVudDogZmFsc2UgfSk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBBZ2dpb3JuYSB2YWxpZGF0b3JpIGNvbmRpemlvbmFsaVxyXG4gICAgICBpZiAoZmllbGQudmFsaWRhdGlvbj8uc29tZSgocikgPT4gci5jb25kaXRpb25zPy5sZW5ndGgpKSB7XHJcbiAgICAgICAgdGhpcy52YWxpZGF0aW9uU2VydmljZS51cGRhdGVGaWVsZFZhbGlkYXRvcnModGhpcy5mb3JtR3JvdXAuZ2V0KGZpZWxkLmtleSkhLCBmaWVsZC52YWxpZGF0aW9uLCBmb3JtRGF0YSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIOKUgOKUgOKUgCBEaXBlbmRlbnplIG9wemlvbmkg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXHJcblxyXG4gIHByaXZhdGUgYnVpbGREZXBlbmRlbmN5TWFwKCk6IHZvaWQge1xyXG4gICAgZm9yIChjb25zdCBmaWVsZCBvZiB0aGlzLmFsbEZpZWxkcykge1xyXG4gICAgICBpZiAoZmllbGQuZGVwZW5kZW50T3B0aW9ucz8uZGVwZW5kc09uKSB7XHJcbiAgICAgICAgY29uc3QgZGVwS2V5ID0gZmllbGQuZGVwZW5kZW50T3B0aW9ucy5kZXBlbmRzT247XHJcbiAgICAgICAgaWYgKCF0aGlzLmZpZWxkRGVwZW5kZW5jaWVzLmhhcyhkZXBLZXkpKSB7XHJcbiAgICAgICAgICB0aGlzLmZpZWxkRGVwZW5kZW5jaWVzLnNldChkZXBLZXksIG5ldyBTZXQoKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRoaXMuZmllbGREZXBlbmRlbmNpZXMuZ2V0KGRlcEtleSkhLmFkZChmaWVsZC5rZXkpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIHVwZGF0ZURlcGVuZGVudE9wdGlvbnMoKTogdm9pZCB7XHJcbiAgICBjb25zdCBmb3JtRGF0YSA9IHRoaXMuZm9ybUdyb3VwLmdldFJhd1ZhbHVlKCk7XHJcblxyXG4gICAgZm9yIChjb25zdCBmaWVsZCBvZiB0aGlzLmFsbEZpZWxkcykge1xyXG4gICAgICAvLyBEeW5hbWljIG9wdGlvbnNcclxuICAgICAgaWYgKGZpZWxkLmR5bmFtaWNPcHRpb25zKSB7XHJcbiAgICAgICAgaWYgKGZpZWxkLmR5bmFtaWNPcHRpb25zLnR5cGUgPT09ICd5ZWFyLXJhbmdlJykge1xyXG4gICAgICAgICAgY29uc3QgbWluID0gZmllbGQuZHluYW1pY09wdGlvbnMubWluWWVhciB8fCBuZXcgRGF0ZSgpLmdldEZ1bGxZZWFyKCkgLSAxMDtcclxuICAgICAgICAgIGNvbnN0IG1heCA9IGZpZWxkLmR5bmFtaWNPcHRpb25zLm1heFllYXIgfHwgbmV3IERhdGUoKS5nZXRGdWxsWWVhcigpO1xyXG4gICAgICAgICAgY29uc3Qgb3B0czogVWlGaWVsZE9wdGlvbltdID0gW107XHJcbiAgICAgICAgICBmb3IgKGxldCB5ID0gbWF4OyB5ID49IG1pbjsgeS0tKSB7XHJcbiAgICAgICAgICAgIG9wdHMucHVzaCh7IHZhbHVlOiB5LCBsYWJlbDogU3RyaW5nKHkpIH0pO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgdGhpcy5maWx0ZXJlZE9wdGlvbnNbZmllbGQua2V5XSA9IG9wdHM7XHJcbiAgICAgICAgfSBlbHNlIGlmIChmaWVsZC5keW5hbWljT3B0aW9ucy5nZW5lcmF0b3IpIHtcclxuICAgICAgICAgIHRoaXMuZmlsdGVyZWRPcHRpb25zW2ZpZWxkLmtleV0gPSBmaWVsZC5keW5hbWljT3B0aW9ucy5nZW5lcmF0b3IoZm9ybURhdGEpO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRGVwZW5kZW50IG9wdGlvbnNcclxuICAgICAgaWYgKGZpZWxkLmRlcGVuZGVudE9wdGlvbnMgJiYgZmllbGQub3B0aW9ucyAmJiAhdGhpcy5pc09ic2VydmFibGUoZmllbGQub3B0aW9ucykpIHtcclxuICAgICAgICBjb25zdCBkZXAgPSBmaWVsZC5kZXBlbmRlbnRPcHRpb25zO1xyXG4gICAgICAgIGNvbnN0IGRlcFZhbHVlID0gZm9ybURhdGFbZGVwLmRlcGVuZHNPbl07XHJcbiAgICAgICAgY29uc3QgYWxsT3B0aW9ucyA9IGZpZWxkLm9wdGlvbnMgYXMgVWlGaWVsZE9wdGlvbltdO1xyXG5cclxuICAgICAgICBpZiAoZGVwVmFsdWUgPT0gbnVsbCB8fCBkZXBWYWx1ZSA9PT0gJycpIHtcclxuICAgICAgICAgIHRoaXMuZmlsdGVyZWRPcHRpb25zW2ZpZWxkLmtleV0gPSBbLi4uYWxsT3B0aW9uc107XHJcbiAgICAgICAgICBjb250aW51ZTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRoaXMuZmlsdGVyZWRPcHRpb25zW2ZpZWxkLmtleV0gPSBhbGxPcHRpb25zLmZpbHRlcigob3B0KSA9PiB7XHJcbiAgICAgICAgICBpZiAoZGVwLmZpbHRlclR5cGUgPT09ICdjdXN0b20nICYmIGRlcC5maWx0ZXJGbikge1xyXG4gICAgICAgICAgICByZXR1cm4gZGVwLmZpbHRlckZuKG9wdCwgZGVwVmFsdWUsIGZvcm1EYXRhKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIGNvbnN0IG9wdFZhbCA9IE51bWJlcihvcHQudmFsdWUpO1xyXG4gICAgICAgICAgY29uc3QgZGVwVmFsID0gTnVtYmVyKGRlcFZhbHVlKTtcclxuICAgICAgICAgIGlmIChpc05hTihvcHRWYWwpIHx8IGlzTmFOKGRlcFZhbCkpIHJldHVybiB0cnVlO1xyXG5cclxuICAgICAgICAgIHN3aXRjaCAoZGVwLmZpbHRlclR5cGUpIHtcclxuICAgICAgICAgICAgY2FzZSAnZ3JlYXRlcl90aGFuJzpcclxuICAgICAgICAgICAgICByZXR1cm4gb3B0VmFsID4gZGVwVmFsO1xyXG4gICAgICAgICAgICBjYXNlICdncmVhdGVyX2VxdWFsJzpcclxuICAgICAgICAgICAgICByZXR1cm4gb3B0VmFsID49IGRlcFZhbDtcclxuICAgICAgICAgICAgY2FzZSAnbGVzc190aGFuJzpcclxuICAgICAgICAgICAgICByZXR1cm4gb3B0VmFsIDwgZGVwVmFsO1xyXG4gICAgICAgICAgICBjYXNlICdsZXNzX2VxdWFsJzpcclxuICAgICAgICAgICAgICByZXR1cm4gb3B0VmFsIDw9IGRlcFZhbDtcclxuICAgICAgICAgICAgY2FzZSAnZXF1YWxzJzpcclxuICAgICAgICAgICAgICByZXR1cm4gb3B0VmFsID09PSBkZXBWYWw7XHJcbiAgICAgICAgICAgIGNhc2UgJ25vdF9lcXVhbHMnOlxyXG4gICAgICAgICAgICAgIHJldHVybiBvcHRWYWwgIT09IGRlcFZhbDtcclxuICAgICAgICAgICAgZGVmYXVsdDpcclxuICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8g4pSA4pSA4pSAIEFQSSBwdWJibGljYSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcclxuXHJcbiAgLyoqIFZlcmlmaWNhIHNlIHVuIGNhbXBvIGUgdmlzaWJpbGUuICovXHJcbiAgaXNGaWVsZFZpc2libGUoZmllbGRLZXk6IHN0cmluZyk6IGJvb2xlYW4ge1xyXG4gICAgY29uc3QgZmllbGQgPSB0aGlzLmFsbEZpZWxkcy5maW5kKChmKSA9PiBmLmtleSA9PT0gZmllbGRLZXkpO1xyXG4gICAgaWYgKCFmaWVsZD8uY29uZGl0aW9ucz8ubGVuZ3RoKSByZXR1cm4gdHJ1ZTtcclxuICAgIHJldHVybiB0aGlzLmNvbmRpdGlvblNlcnZpY2UuZXZhbHVhdGVDb25kaXRpb25zKGZpZWxkLmNvbmRpdGlvbnMsIHRoaXMuZm9ybUdyb3VwLmdldFJhd1ZhbHVlKCkpO1xyXG4gIH1cclxuXHJcbiAgLyoqIFZlcmlmaWNhIHNlIHVuIGNhbXBvIGUgb2JibGlnYXRvcmlvIChpbmNsdWRlIHZhbGlkYXppb25lIGNvbmRpemlvbmFsZSkuICovXHJcbiAgaXNGaWVsZFJlcXVpcmVkKGZpZWxkS2V5OiBzdHJpbmcpOiBib29sZWFuIHtcclxuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5hbGxGaWVsZHMuZmluZCgoZikgPT4gZi5rZXkgPT09IGZpZWxkS2V5KTtcclxuICAgIGlmIChmaWVsZD8ucmVxdWlyZWQpIHJldHVybiB0cnVlO1xyXG5cclxuICAgIGNvbnN0IHJ1bGVzID0gZmllbGQ/LnZhbGlkYXRpb24gfHwgW107XHJcbiAgICBjb25zdCBmb3JtRGF0YSA9IHRoaXMuZm9ybUdyb3VwLmdldFJhd1ZhbHVlKCk7XHJcbiAgICByZXR1cm4gcnVsZXMuc29tZSgocikgPT4ge1xyXG4gICAgICBpZiAoci50eXBlICE9PSAncmVxdWlyZWQnKSByZXR1cm4gZmFsc2U7XHJcbiAgICAgIGlmICghci5jb25kaXRpb25zPy5sZW5ndGgpIHJldHVybiB0cnVlO1xyXG4gICAgICByZXR1cm4gdGhpcy5jb25kaXRpb25TZXJ2aWNlLmV2YWx1YXRlQ29uZGl0aW9ucyhyLmNvbmRpdGlvbnMsIGZvcm1EYXRhKTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqIFZlcmlmaWNhIHNlIHVuYSBzZXppb25lIGUgdmlzaWJpbGUuICovXHJcbiAgaXNTZWN0aW9uVmlzaWJsZShzZWN0aW9uSWQ6IHN0cmluZyk6IGJvb2xlYW4ge1xyXG4gICAgY29uc3Qgc2VjdGlvbiA9IHRoaXMuc2NoZW1hLnNlY3Rpb25zLmZpbmQoKHMpID0+IHMuaWQgPT09IHNlY3Rpb25JZCk7XHJcbiAgICBpZiAoIXNlY3Rpb24/LmNvbmRpdGlvbnM/Lmxlbmd0aCkgcmV0dXJuIHRydWU7XHJcbiAgICByZXR1cm4gdGhpcy5jb25kaXRpb25TZXJ2aWNlLmV2YWx1YXRlQ29uZGl0aW9ucyhzZWN0aW9uLmNvbmRpdGlvbnMsIHRoaXMuZm9ybUdyb3VwLmdldFJhd1ZhbHVlKCkpO1xyXG4gIH1cclxuXHJcbiAgLyoqIFZlcmlmaWNhIHNlIG1vc3RyYXJlIGVycm9yaSBwZXIgdW4gY2FtcG8uICovXHJcbiAgc2hvdWxkU2hvd0ZpZWxkRXJyb3JzKGZpZWxkS2V5OiBzdHJpbmcpOiBib29sZWFuIHtcclxuICAgIGNvbnN0IGNvbnRyb2wgPSB0aGlzLmZvcm1Hcm91cC5nZXQoZmllbGRLZXkpO1xyXG4gICAgaWYgKCFjb250cm9sKSByZXR1cm4gZmFsc2U7XHJcbiAgICByZXR1cm4gY29udHJvbC5pbnZhbGlkICYmIChjb250cm9sLmRpcnR5IHx8IGNvbnRyb2wudG91Y2hlZCB8fCB0aGlzLnByZXZhbG9yaXplZEZpZWxkcy5oYXMoZmllbGRLZXkpKTtcclxuICB9XHJcblxyXG4gIC8qKiBFcnJvcmkgZGkgdW4gY2FtcG8gY29tZSBhcnJheSBkaSBtZXNzYWdnaS4gKi9cclxuICBnZXRGaWVsZEVycm9ycyhmaWVsZEtleTogc3RyaW5nKTogc3RyaW5nW10ge1xyXG4gICAgY29uc3QgY29udHJvbCA9IHRoaXMuZm9ybUdyb3VwLmdldChmaWVsZEtleSk7XHJcbiAgICBpZiAoIWNvbnRyb2w/LmVycm9ycykgcmV0dXJuIFtdO1xyXG5cclxuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5hbGxGaWVsZHMuZmluZCgoZikgPT4gZi5rZXkgPT09IGZpZWxkS2V5KTtcclxuICAgIGNvbnN0IHJ1bGVzID0gZmllbGQ/LnZhbGlkYXRpb24gfHwgW107XHJcbiAgICBjb25zdCBtZXNzYWdlczogc3RyaW5nW10gPSBbXTtcclxuXHJcbiAgICBmb3IgKGNvbnN0IFtlcnJvcktleV0gb2YgT2JqZWN0LmVudHJpZXMoY29udHJvbC5lcnJvcnMpKSB7XHJcbiAgICAgIC8vIEFuZ3VsYXIgdXNhIGNoaWF2aSBsb3dlcmNhc2UgKGVzLiBcIm1pbmxlbmd0aFwiKSwgbG8gc2NoZW1hIHVzYSBjYW1lbENhc2UgKGVzLiBcIm1pbkxlbmd0aFwiKS5cclxuICAgICAgLy8gTm9ybWFsaXp6aWFtbyBwZXIgdHJvdmFyZSBsYSBjb3JyaXNwb25kZW56YSBjb3JyZXR0YS5cclxuICAgICAgY29uc3QgcnVsZSA9IHJ1bGVzLmZpbmQoXHJcbiAgICAgICAgKHIpID0+XHJcbiAgICAgICAgICB0aGlzLm5vcm1hbGl6ZVZhbGlkYXRpb25LZXkoci50eXBlKSA9PT0gZXJyb3JLZXkgfHwgKHIudHlwZSA9PT0gJ2Nyb3NzRmllbGQnICYmIGVycm9yS2V5ID09PSAnY3Jvc3NGaWVsZCcpLFxyXG4gICAgICApO1xyXG4gICAgICBpZiAocnVsZT8ubWVzc2FnZSkge1xyXG4gICAgICAgIG1lc3NhZ2VzLnB1c2gocnVsZS5tZXNzYWdlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBtZXNzYWdlcy5wdXNoKHRoaXMuZ2V0RGVmYXVsdEVycm9yTWVzc2FnZShlcnJvcktleSwgY29udHJvbC5lcnJvcnMhW2Vycm9yS2V5XSkpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICByZXR1cm4gbWVzc2FnZXM7XHJcbiAgfVxyXG5cclxuICAvKiogVmFsb3JlIGNvbXBsZXRvIGRlbCBmb3JtIChpbmNsdWRlIGNhbXBpIGRpc2FiaWxpdGF0aSkuICovXHJcbiAgZ2V0Rm9ybVZhbHVlKCk6IFVpRm9ybURhdGEge1xyXG4gICAgcmV0dXJuIHRoaXMuZm9ybUdyb3VwLmdldFJhd1ZhbHVlKCk7XHJcbiAgfVxyXG5cclxuICAvKiogSWwgZm9ybSBlIHZhbGlkby4gKi9cclxuICBpc0Zvcm1WYWxpZCgpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLmZvcm1Hcm91cC52YWxpZDtcclxuICB9XHJcblxyXG4gIC8qKiBTdGF0byBkaSB2YWxpZGF6aW9uZSBjb21wbGV0by4gKi9cclxuICBnZXRWYWxpZGF0aW9uU3RhdGUoKTogVWlGb3JtVmFsaWRhdGlvblN0YXRlIHtcclxuICAgIGNvbnN0IGVycm9yczogUmVjb3JkPHN0cmluZywgc3RyaW5nW10+ID0ge307XHJcbiAgICBjb25zdCBkaXJ0eTogc3RyaW5nW10gPSBbXTtcclxuICAgIGNvbnN0IHRvdWNoZWQ6IHN0cmluZ1tdID0gW107XHJcblxyXG4gICAgZm9yIChjb25zdCBmaWVsZCBvZiB0aGlzLmFsbEZpZWxkcykge1xyXG4gICAgICBjb25zdCBjb250cm9sID0gdGhpcy5mb3JtR3JvdXAuZ2V0KGZpZWxkLmtleSk7XHJcbiAgICAgIGlmICghY29udHJvbCkgY29udGludWU7XHJcbiAgICAgIGlmIChjb250cm9sLmRpcnR5KSBkaXJ0eS5wdXNoKGZpZWxkLmtleSk7XHJcbiAgICAgIGlmIChjb250cm9sLnRvdWNoZWQpIHRvdWNoZWQucHVzaChmaWVsZC5rZXkpO1xyXG4gICAgICBjb25zdCBmaWVsZEVycm9ycyA9IHRoaXMuZ2V0RmllbGRFcnJvcnMoZmllbGQua2V5KTtcclxuICAgICAgaWYgKGZpZWxkRXJyb3JzLmxlbmd0aCkgZXJyb3JzW2ZpZWxkLmtleV0gPSBmaWVsZEVycm9ycztcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4geyB2YWxpZDogdGhpcy5mb3JtR3JvdXAudmFsaWQsIGVycm9ycywgZGlydHksIHRvdWNoZWQgfTtcclxuICB9XHJcblxyXG4gIC8qKiBFcnJvcmkgZGV0dGFnbGlhdGkgcGVyIGwnZXJyb3Igc3VtbWFyeS4gKi9cclxuICBnZXREZXRhaWxlZEZvcm1FcnJvcnMoKTogVWlGb3JtRXJyb3JEZXRhaWxbXSB7XHJcbiAgICBjb25zdCBkZXRhaWxzOiBVaUZvcm1FcnJvckRldGFpbFtdID0gW107XHJcbiAgICBmb3IgKGNvbnN0IGZpZWxkIG9mIHRoaXMuYWxsRmllbGRzKSB7XHJcbiAgICAgIGlmIChmaWVsZC50eXBlID09PSAnZmxhZycgfHwgZmllbGQudHlwZSA9PT0gJ2RpdmlkZXInKSBjb250aW51ZTtcclxuICAgICAgaWYgKCF0aGlzLmlzRmllbGRWaXNpYmxlKGZpZWxkLmtleSkpIGNvbnRpbnVlO1xyXG5cclxuICAgICAgY29uc3QgY29udHJvbCA9IHRoaXMuZm9ybUdyb3VwLmdldChmaWVsZC5rZXkpO1xyXG4gICAgICBpZiAoIWNvbnRyb2w/LmVycm9ycykgY29udGludWU7XHJcblxyXG4gICAgICBjb25zdCBlcnJvcnMgPSB0aGlzLmdldEZpZWxkRXJyb3JzKGZpZWxkLmtleSk7XHJcbiAgICAgIGlmIChlcnJvcnMubGVuZ3RoKSB7XHJcbiAgICAgICAgZGV0YWlscy5wdXNoKHsgZmllbGRLZXk6IGZpZWxkLmtleSwgZmllbGRMYWJlbDogZmllbGQubGFiZWwsIGVycm9ycyB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgcmV0dXJuIGRldGFpbHM7XHJcbiAgfVxyXG5cclxuICAvKiogQ29udGEgZXJyb3JpIHRvdGFsaS4gKi9cclxuICBnZXRGb3JtRXJyb3JzQ291bnQoKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLmdldERldGFpbGVkRm9ybUVycm9ycygpLnJlZHVjZSgoc3VtLCBkKSA9PiBzdW0gKyBkLmVycm9ycy5sZW5ndGgsIDApO1xyXG4gIH1cclxuXHJcbiAgLyoqIENvbnRyb2xsYSBzZSBjaSBzb25vIGVycm9yaS4gKi9cclxuICBoYXNGb3JtRXJyb3JzKCk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIHRoaXMuZ2V0Rm9ybUVycm9yc0NvdW50KCkgPiAwO1xyXG4gIH1cclxuXHJcbiAgLyoqIENvbnRyb2xsbyBwZXIgdW4gY2FtcG8uICovXHJcbiAgZ2V0RmllbGRDb250cm9sKGtleTogc3RyaW5nKTogRm9ybUNvbnRyb2wgfCBudWxsIHtcclxuICAgIHJldHVybiAodGhpcy5mb3JtR3JvdXAuZ2V0KGtleSkgYXMgRm9ybUNvbnRyb2wpID8/IG51bGw7XHJcbiAgfVxyXG5cclxuICAvKiogQWdnaW9ybmEgaWwgdmFsb3JlIGRpIHVuIGNhbXBvIHByb2dyYW1tYXRpY2FtZW50ZS4gKi9cclxuICB1cGRhdGVGaWVsZFZhbHVlKGtleTogc3RyaW5nLCB2YWx1ZTogYW55KTogdm9pZCB7XHJcbiAgICBjb25zdCBjb250cm9sID0gdGhpcy5mb3JtR3JvdXAuZ2V0KGtleSk7XHJcbiAgICBpZiAoY29udHJvbCkge1xyXG4gICAgICBjb250cm9sLnNldFZhbHVlKHZhbHVlKTtcclxuICAgICAgY29udHJvbC5tYXJrQXNEaXJ0eSgpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqIEZvcnphIGxhIHZhbGlkYXppb25lIGRpIHR1dHRpIGkgY2FtcGkuICovXHJcbiAgdmFsaWRhdGVBbGxGaWVsZHMoKTogdm9pZCB7XHJcbiAgICBPYmplY3Qua2V5cyh0aGlzLmZvcm1Hcm91cC5jb250cm9scykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAgIGNvbnN0IGNvbnRyb2wgPSB0aGlzLmZvcm1Hcm91cC5nZXQoa2V5KTtcclxuICAgICAgY29udHJvbD8ubWFya0FzVG91Y2hlZCgpO1xyXG4gICAgICBjb250cm9sPy5tYXJrQXNEaXJ0eSgpO1xyXG4gICAgICBjb250cm9sPy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KCk7XHJcbiAgICB9KTtcclxuICAgIHRoaXMuY2RyLm1hcmtGb3JDaGVjaygpO1xyXG4gIH1cclxuXHJcbiAgLyoqIFNjcm9sbGEgYWwgY2FtcG8gZSBsbyBldmlkZW56aWEuICovXHJcbiAgc2Nyb2xsVG9GaWVsZChmaWVsZEtleTogc3RyaW5nKTogdm9pZCB7XHJcbiAgICAvLyBGb3J6YSBsbyBzdGF0byBkaXJ0eSBlIHRvdWNoZWQgcGVyIG1vc3RyYXJlIGltbWVkaWF0YW1lbnRlIGdsaSBlcnJvcmlcclxuICAgIGNvbnN0IGNvbnRyb2wgPSB0aGlzLmZvcm1Hcm91cC5nZXQoZmllbGRLZXkpO1xyXG4gICAgaWYgKGNvbnRyb2wpIHtcclxuICAgICAgY29udHJvbC5tYXJrQXNUb3VjaGVkKCk7XHJcbiAgICAgIGNvbnRyb2wubWFya0FzRGlydHkoKTtcclxuICAgICAgY29udHJvbC51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVsYXkgcGVyIGxhc2NpYXJlIGNoZSBBbmd1bGFyIE1hdGVyaWFsIGFnZ2lvcm5pIGlsIERPTVxyXG4gICAgc2V0VGltZW91dCgoKSA9PiB7XHJcbiAgICAgIC8vIENlcmNhIGwnZWxlbWVudG8gdHJhbWl0ZSBkaXZlcnNpIHNlbGV0dG9yaSBpbiBvcmRpbmUgZGkgcHJpb3JpdGFcclxuICAgICAgY29uc3Qgc2VsZWN0b3JzID0gW2BbZGF0YS1maWVsZC1rZXk9XCIke2ZpZWxkS2V5fVwiXWAsIGBbZm9ybUNvbnRyb2xOYW1lPVwiJHtmaWVsZEtleX1cIl1gXTtcclxuXHJcbiAgICAgIGxldCB0YXJnZXRFbGVtZW50OiBIVE1MRWxlbWVudCB8IG51bGwgPSBudWxsO1xyXG4gICAgICBmb3IgKGNvbnN0IHNlbCBvZiBzZWxlY3RvcnMpIHtcclxuICAgICAgICB0YXJnZXRFbGVtZW50ID0gdGhpcy5lbFJlZi5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3Ioc2VsKTtcclxuICAgICAgICBpZiAodGFyZ2V0RWxlbWVudCkgYnJlYWs7XHJcbiAgICAgIH1cclxuICAgICAgaWYgKCF0YXJnZXRFbGVtZW50KSByZXR1cm47XHJcblxyXG4gICAgICAvLyBTZSB0cm92YXRvIGlsIGZvcm1Db250cm9sTmFtZSwgcmlzYWxpIGFsIHdyYXBwZXJcclxuICAgICAgaWYgKHRhcmdldEVsZW1lbnQuaGFzQXR0cmlidXRlKCdmb3JtQ29udHJvbE5hbWUnKSkge1xyXG4gICAgICAgIGNvbnN0IHdyYXBwZXIgPSB0YXJnZXRFbGVtZW50LmNsb3Nlc3QoJy51aS1mb3JtLWJ1aWxkZXJfX2ZpZWxkLXdyYXBwZXIsIG1hdC1mb3JtLWZpZWxkJyk7XHJcbiAgICAgICAgaWYgKHdyYXBwZXIpIHRhcmdldEVsZW1lbnQgPSB3cmFwcGVyIGFzIEhUTUxFbGVtZW50O1xyXG4gICAgICB9XHJcblxyXG4gICAgICB0YXJnZXRFbGVtZW50LnNjcm9sbEludG9WaWV3KHsgYmVoYXZpb3I6ICdzbW9vdGgnLCBibG9jazogJ2NlbnRlcicgfSk7XHJcblxyXG4gICAgICAvLyBBZ2dpdW5naSBjbGFzc2UgaGlnaGxpZ2h0IGUgcmltdW92aWxhIGRvcG8gbCdhbmltYXppb25lXHJcbiAgICAgIHRhcmdldEVsZW1lbnQuY2xhc3NMaXN0LmFkZCgndWktZm9ybS1maWVsZC0taGlnaGxpZ2h0Jyk7XHJcbiAgICAgIHNldFRpbWVvdXQoKCkgPT4gdGFyZ2V0RWxlbWVudCEuY2xhc3NMaXN0LnJlbW92ZSgndWktZm9ybS1maWVsZC0taGlnaGxpZ2h0JyksIDIwMDApO1xyXG4gICAgfSwgMTAwKTtcclxuICB9XHJcblxyXG4gIC8qKiBTdWJtaXQgZGVsIGZvcm0uICovXHJcbiAgb25TdWJtaXQoKTogdm9pZCB7XHJcbiAgICB0aGlzLnZhbGlkYXRlQWxsRmllbGRzKCk7XHJcbiAgICBpZiAodGhpcy5mb3JtR3JvdXAudmFsaWQpIHtcclxuICAgICAgdGhpcy5mb3JtU3VibWl0LmVtaXQodGhpcy5nZXRGb3JtVmFsdWUoKSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKiogUmVzZXQgZGVsIGZvcm0uICovXHJcbiAgb25SZXNldCgpOiB2b2lkIHtcclxuICAgIC8vIFJlc2V0dGEgYSB2YWxvcmkgaW5pemlhbGkgbyBkZWZhdWx0XHJcbiAgICBmb3IgKGNvbnN0IGZpZWxkIG9mIHRoaXMuYWxsRmllbGRzKSB7XHJcbiAgICAgIGNvbnN0IHZhbHVlID0gdGhpcy5pbml0aWFsRGF0YT8uW2ZpZWxkLmtleV0gPz8gZmllbGQuZGVmYXVsdFZhbHVlID8/IG51bGw7XHJcbiAgICAgIGNvbnN0IGNvbnRyb2wgPSB0aGlzLmZvcm1Hcm91cC5nZXQoZmllbGQua2V5KTtcclxuICAgICAgY29udHJvbD8uc2V0VmFsdWUodmFsdWUsIHsgZW1pdEV2ZW50OiBmYWxzZSB9KTtcclxuICAgICAgY29udHJvbD8ubWFya0FzUHJpc3RpbmUoKTtcclxuICAgICAgY29udHJvbD8ubWFya0FzVW50b3VjaGVkKCk7XHJcbiAgICB9XHJcbiAgICB0aGlzLmZvcm1Hcm91cC51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KCk7XHJcbiAgICB0aGlzLmZvcm1SZXNldC5lbWl0KCk7XHJcbiAgICB0aGlzLmNkci5tYXJrRm9yQ2hlY2soKTtcclxuICB9XHJcblxyXG4gIC8vIOKUgOKUgOKUgCBUZW1wbGF0ZSBoZWxwZXJzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxyXG5cclxuICAvKiogT3B6aW9uaSBwZXIgdW4gY2FtcG8gKGZpbHRyYW5kbyBwZXIgYXV0b2NvbXBsZXRlIHF1ZXJ5KS4gKi9cclxuICBnZXRGaWVsZE9wdGlvbnMoZmllbGQ6IFVpRm9ybUZpZWxkRGVzY3JpcHRvcik6IFVpRmllbGRPcHRpb25bXSB7XHJcbiAgICByZXR1cm4gdGhpcy5maWx0ZXJlZE9wdGlvbnNbZmllbGQua2V5XSB8fCBbXTtcclxuICB9XHJcblxyXG4gIC8qKiBGaWx0cmEgb3B6aW9uaSBwZXIgYXV0b2NvbXBsZXRlLiAqL1xyXG4gIGZpbHRlck9wdGlvbnMoZmllbGQ6IFVpRm9ybUZpZWxkRGVzY3JpcHRvciwgcXVlcnk6IHN0cmluZyk6IHZvaWQge1xyXG4gICAgaWYgKCFxdWVyeSkge1xyXG4gICAgICBpZiAoZmllbGQub3B0aW9ucyAmJiAhdGhpcy5pc09ic2VydmFibGUoZmllbGQub3B0aW9ucykpIHtcclxuICAgICAgICB0aGlzLmZpbHRlcmVkT3B0aW9uc1tmaWVsZC5rZXldID0gWy4uLihmaWVsZC5vcHRpb25zIGFzIFVpRmllbGRPcHRpb25bXSldO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBsb3dlclF1ZXJ5ID0gcXVlcnkudG9Mb3dlckNhc2UoKTtcclxuXHJcbiAgICBpZiAoZmllbGQuYXN5bmNPcHRpb25zKSB7XHJcbiAgICAgIGZpZWxkLmFzeW5jT3B0aW9ucyhxdWVyeSkudGhlbigob3B0cykgPT4ge1xyXG4gICAgICAgIHRoaXMuZmlsdGVyZWRPcHRpb25zW2ZpZWxkLmtleV0gPSBvcHRzO1xyXG4gICAgICAgIHRoaXMuY2RyLm1hcmtGb3JDaGVjaygpO1xyXG4gICAgICB9KTtcclxuICAgIH0gZWxzZSBpZiAoZmllbGQub3B0aW9ucyAmJiAhdGhpcy5pc09ic2VydmFibGUoZmllbGQub3B0aW9ucykpIHtcclxuICAgICAgdGhpcy5maWx0ZXJlZE9wdGlvbnNbZmllbGQua2V5XSA9IChmaWVsZC5vcHRpb25zIGFzIFVpRmllbGRPcHRpb25bXSkuZmlsdGVyKChvKSA9PlxyXG4gICAgICAgIG8ubGFiZWwudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhsb3dlclF1ZXJ5KSxcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKiBEaXNwbGF5IGZuIHBlciBhdXRvY29tcGxldGUuICovXHJcbiAgZGlzcGxheUZuKG9wdGlvbnM6IFVpRmllbGRPcHRpb25bXSk6ICh2YWx1ZTogYW55KSA9PiBzdHJpbmcge1xyXG4gICAgcmV0dXJuICh2YWx1ZTogYW55KSA9PiB7XHJcbiAgICAgIGNvbnN0IG9wdCA9IG9wdGlvbnMuZmluZCgobykgPT4gby52YWx1ZSA9PT0gdmFsdWUpO1xyXG4gICAgICByZXR1cm4gb3B0Py5sYWJlbCB8fCAnJztcclxuICAgIH07XHJcbiAgfVxyXG5cclxuICAvKiogU2VsZXppb25hIHR1dHRlIGxlIG9wemlvbmkgcGVyIG11bHRpc2VsZWN0LiAqL1xyXG4gIHNlbGVjdEFsbChmaWVsZDogVWlGb3JtRmllbGREZXNjcmlwdG9yKTogdm9pZCB7XHJcbiAgICBjb25zdCBvcHRpb25zID0gdGhpcy5nZXRGaWVsZE9wdGlvbnMoZmllbGQpO1xyXG4gICAgY29uc3QgYWxsVmFsdWVzID0gb3B0aW9ucy5maWx0ZXIoKG8pID0+ICFvLmRpc2FibGVkKS5tYXAoKG8pID0+IG8udmFsdWUpO1xyXG4gICAgdGhpcy5mb3JtR3JvdXAuZ2V0KGZpZWxkLmtleSk/LnNldFZhbHVlKGFsbFZhbHVlcyk7XHJcbiAgfVxyXG5cclxuICAvKiogQWdnaXVuZ2UgdW4gY2hpcCBhbCBmcmVlbXVsdGlzZWxlY3QgKGRhIHRva2VuOiBFbnRlciBvIHZpcmdvbGEpLiAqL1xyXG4gIGFkZEZyZWVDaGlwKGZpZWxkOiBVaUZvcm1GaWVsZERlc2NyaXB0b3IsIGV2ZW50OiBhbnkpOiB2b2lkIHtcclxuICAgIGNvbnN0IHZhbHVlID0gKGV2ZW50LnZhbHVlIHx8ICcnKS50cmltKCk7XHJcbiAgICBpZiAoIXZhbHVlKSByZXR1cm47XHJcblxyXG4gICAgY29uc3QgY3VycmVudCA9IHRoaXMuZm9ybUdyb3VwLmdldChmaWVsZC5rZXkpPy52YWx1ZSB8fCBbXTtcclxuICAgIGlmICghY3VycmVudC5pbmNsdWRlcyh2YWx1ZSkpIHtcclxuICAgICAgdGhpcy5mb3JtR3JvdXAuZ2V0KGZpZWxkLmtleSk/LnNldFZhbHVlKFsuLi5jdXJyZW50LCB2YWx1ZV0pO1xyXG4gICAgfVxyXG4gICAgZXZlbnQuY2hpcElucHV0Py5jbGVhcigpO1xyXG4gIH1cclxuXHJcbiAgLyoqIEFnZ2l1bmdlIHVuIGNoaXAgYWwgZnJlZW11bHRpc2VsZWN0IHN1bGwnZXZlbnRvIGJsdXIgZGVsbCdpbnB1dC4gKi9cclxuICBhZGRGcmVlQ2hpcE9uQmx1cihmaWVsZDogVWlGb3JtRmllbGREZXNjcmlwdG9yLCBldmVudDogRm9jdXNFdmVudCk6IHZvaWQge1xyXG4gICAgY29uc3QgaW5wdXQgPSBldmVudC50YXJnZXQgYXMgSFRNTElucHV0RWxlbWVudDtcclxuICAgIGNvbnN0IHZhbHVlID0gKGlucHV0LnZhbHVlIHx8ICcnKS50cmltKCk7XHJcbiAgICBpZiAoIXZhbHVlKSByZXR1cm47XHJcblxyXG4gICAgY29uc3QgY3VycmVudCA9IHRoaXMuZm9ybUdyb3VwLmdldChmaWVsZC5rZXkpPy52YWx1ZSB8fCBbXTtcclxuICAgIGlmICghY3VycmVudC5pbmNsdWRlcyh2YWx1ZSkpIHtcclxuICAgICAgdGhpcy5mb3JtR3JvdXAuZ2V0KGZpZWxkLmtleSk/LnNldFZhbHVlKFsuLi5jdXJyZW50LCB2YWx1ZV0pO1xyXG4gICAgfVxyXG4gICAgaW5wdXQudmFsdWUgPSAnJztcclxuICB9XHJcblxyXG4gIC8qKiBSaW11b3ZlIHVuIGNoaXAgZGFsIGZyZWVtdWx0aXNlbGVjdC4gKi9cclxuICByZW1vdmVGcmVlQ2hpcChmaWVsZDogVWlGb3JtRmllbGREZXNjcmlwdG9yLCBjaGlwVmFsdWU6IHN0cmluZyk6IHZvaWQge1xyXG4gICAgY29uc3QgY3VycmVudCA9IHRoaXMuZm9ybUdyb3VwLmdldChmaWVsZC5rZXkpPy52YWx1ZSB8fCBbXTtcclxuICAgIGNvbnN0IHByb3RlY3RlZF8gPSBmaWVsZC5tZXRhZGF0YT8uWydjYW50RGVsZXRlTGlzdCddIHx8IFtdO1xyXG4gICAgaWYgKHByb3RlY3RlZF8uaW5jbHVkZXMoY2hpcFZhbHVlKSkgcmV0dXJuO1xyXG4gICAgdGhpcy5mb3JtR3JvdXAuZ2V0KGZpZWxkLmtleSk/LnNldFZhbHVlKGN1cnJlbnQuZmlsdGVyKCh2OiBzdHJpbmcpID0+IHYgIT09IGNoaXBWYWx1ZSkpO1xyXG4gIH1cclxuXHJcbiAgLyoqIFZlcmlmaWNhIHNlIHVuIGNoaXAgZSBwcm90ZXR0byBkYWxsYSBjYW5jZWxsYXppb25lLiAqL1xyXG4gIGlzQ2hpcFByb3RlY3RlZChmaWVsZDogVWlGb3JtRmllbGREZXNjcmlwdG9yLCBjaGlwVmFsdWU6IHN0cmluZyk6IGJvb2xlYW4ge1xyXG4gICAgcmV0dXJuIChmaWVsZC5tZXRhZGF0YT8uWydjYW50RGVsZXRlTGlzdCddIHx8IFtdKS5pbmNsdWRlcyhjaGlwVmFsdWUpO1xyXG4gIH1cclxuXHJcbiAgLyoqIFJpbXVvdmUgY2hpcCBkYSBtdWx0aXNlbGVjdC4gKi9cclxuICByZW1vdmVNdWx0aXNlbGVjdENoaXAoZmllbGQ6IFVpRm9ybUZpZWxkRGVzY3JpcHRvciwgY2hpcFZhbHVlOiBhbnkpOiB2b2lkIHtcclxuICAgIGNvbnN0IGN1cnJlbnQgPSB0aGlzLmZvcm1Hcm91cC5nZXQoZmllbGQua2V5KT8udmFsdWUgfHwgW107XHJcbiAgICB0aGlzLmZvcm1Hcm91cC5nZXQoZmllbGQua2V5KT8uc2V0VmFsdWUoY3VycmVudC5maWx0ZXIoKHY6IGFueSkgPT4gdiAhPT0gY2hpcFZhbHVlKSk7XHJcbiAgfVxyXG5cclxuICAvKiogVHJvdmEgbGFiZWwgZGkgdW4nb3B6aW9uZSBkYWwgdmFsb3JlLiAqL1xyXG4gIGdldE9wdGlvbkxhYmVsKGZpZWxkOiBVaUZvcm1GaWVsZERlc2NyaXB0b3IsIHZhbHVlOiBhbnkpOiBzdHJpbmcge1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHRoaXMuZ2V0RmllbGRPcHRpb25zKGZpZWxkKTtcclxuICAgIHJldHVybiBvcHRpb25zLmZpbmQoKG8pID0+IG8udmFsdWUgPT09IHZhbHVlKT8ubGFiZWwgfHwgU3RyaW5nKHZhbHVlKTtcclxuICB9XHJcblxyXG4gIC8qKiBUaXBvIGRpIGlucHV0IG5hdGl2byBwZXIgaWwgY2FtcG8uICovXHJcbiAgZ2V0TmF0aXZlSW5wdXRUeXBlKGZpZWxkOiBVaUZvcm1GaWVsZERlc2NyaXB0b3IpOiBzdHJpbmcge1xyXG4gICAgc3dpdGNoIChmaWVsZC50eXBlKSB7XHJcbiAgICAgIGNhc2UgJ3Bhc3N3b3JkJzpcclxuICAgICAgICByZXR1cm4gJ3Bhc3N3b3JkJztcclxuICAgICAgY2FzZSAnZW1haWwnOlxyXG4gICAgICAgIHJldHVybiAnZW1haWwnO1xyXG4gICAgICBkZWZhdWx0OlxyXG4gICAgICAgIHJldHVybiAndGV4dCc7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBHZW5lcmEgbGUgY2xhc3NpIENTUyBwZXIgaWwgd3JhcHBlciBkaSB1biBjYW1wbyxcclxuICAgKiBjb21iaW5hbmRvIGV2ZW50dWFsaSBjc3NDbGFzc2VzIGRhbGxvIHNjaGVtYSBjb24gbGUgY2xhc3NpXHJcbiAgICogcmVzcG9uc2l2ZSBwZXIgaWwgc2lzdGVtYSBhIGdyaWdsaWEgYmFzZS0xMi5cclxuICAgKlxyXG4gICAqIE1vYmlsZS1maXJzdDogdHV0dGkgaSBjYW1waSBzb25vIHNwYW4tMTIgZGkgZGVmYXVsdCAoMTAwJSkuXHJcbiAgICogTGUgY2xhc3NpIGB1aS1jb2wte2JyZWFrcG9pbnR9LXtufWAgYXR0aXZhbm8gbGEgbGFyZ2hlenphXHJcbiAgICogY29uZmlndXJhdGEgc29sbyBkYWwgYnJlYWtwb2ludCBpbmRpY2F0byBpbiBzdS5cclxuICAgKlxyXG4gICAqIEJyZWFrcG9pbnQgc3VwcG9ydGF0aTogc20gKOKJpTU3NnB4KSwgbWQgKOKJpTc2OHB4KSwgbGcgKOKJpTEwMjRweCksIHhsICjiiaUxMjgwcHgpLlxyXG4gICAqL1xyXG4gIGdldEZpZWxkV3JhcHBlckNsYXNzZXMoZmllbGQ6IFVpRm9ybUZpZWxkRGVzY3JpcHRvcik6IHN0cmluZyB7XHJcbiAgICBjb25zdCBjbGFzc2VzOiBzdHJpbmdbXSA9IFtdO1xyXG5cclxuICAgIGlmIChmaWVsZC5jc3NDbGFzc2VzPy5sZW5ndGgpIHtcclxuICAgICAgY2xhc3Nlcy5wdXNoKC4uLmZpZWxkLmNzc0NsYXNzZXMpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIGxheW91dC5jb2x1bW5zIOKGkiBjbGFzc2UgcGVyIGlsIGJyZWFrcG9pbnQgbWQgKGRlZmF1bHQgZGVza3RvcClcclxuICAgIGlmIChmaWVsZC5sYXlvdXQ/LmNvbHVtbnMpIHtcclxuICAgICAgY2xhc3Nlcy5wdXNoKGB1aS1jb2wtbWQtJHtmaWVsZC5sYXlvdXQuY29sdW1uc31gKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBsYXlvdXQuYnJlYWtwb2ludHMg4oaSIGNsYXNzaSBwZXIgYnJlYWtwb2ludCBzcGVjaWZpY2lcclxuICAgIGlmIChmaWVsZC5sYXlvdXQ/LmJyZWFrcG9pbnRzKSB7XHJcbiAgICAgIGZvciAoY29uc3QgW2JwLCBjb2xzXSBvZiBPYmplY3QuZW50cmllcyhmaWVsZC5sYXlvdXQuYnJlYWtwb2ludHMpKSB7XHJcbiAgICAgICAgY2xhc3Nlcy5wdXNoKGB1aS1jb2wtJHticH0tJHtjb2xzfWApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIGNsYXNzZXMuam9pbignICcpO1xyXG4gIH1cclxuXHJcbiAgLyoqIENoYXJhY3RlciBjb3VudCBwZXIgdGV4dGFyZWEvdGV4dCBjb24gbWF4TGVuZ3RoLiAqL1xyXG4gIGdldENoYXJDb3VudChmaWVsZEtleTogc3RyaW5nKTogeyBjdXJyZW50OiBudW1iZXI7IG1heDogbnVtYmVyIH0gfCBudWxsIHtcclxuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5hbGxGaWVsZHMuZmluZCgoZikgPT4gZi5rZXkgPT09IGZpZWxkS2V5KTtcclxuICAgIGNvbnN0IG1heFJ1bGUgPSBmaWVsZD8udmFsaWRhdGlvbj8uZmluZCgocikgPT4gci50eXBlID09PSAnbWF4TGVuZ3RoJyk7XHJcbiAgICBpZiAoIW1heFJ1bGU/LnZhbHVlKSByZXR1cm4gbnVsbDtcclxuXHJcbiAgICBjb25zdCBjb250cm9sID0gdGhpcy5mb3JtR3JvdXAuZ2V0KGZpZWxkS2V5KTtcclxuICAgIHJldHVybiB7XHJcbiAgICAgIGN1cnJlbnQ6IChjb250cm9sPy52YWx1ZSB8fCAnJykubGVuZ3RoLFxyXG4gICAgICBtYXg6IE51bWJlcihtYXhSdWxlLnZhbHVlKSxcclxuICAgIH07XHJcbiAgfVxyXG5cclxuICAvLyDilIDilIDilIAgVXRpbGl0YSBwcml2YXRlIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxyXG5cclxuICBwcml2YXRlIGlzT2JzZXJ2YWJsZSh2YWx1ZTogYW55KTogdmFsdWUgaXMgT2JzZXJ2YWJsZTxhbnk+IHtcclxuICAgIHJldHVybiB2YWx1ZSAmJiB0eXBlb2YgdmFsdWUuc3Vic2NyaWJlID09PSAnZnVuY3Rpb24nO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogTm9ybWFsaXp6YSBsYSBjaGlhdmUgZGVsIHRpcG8gZGkgdmFsaWRhemlvbmUgZGFsbG8gc2NoZW1hIChjYW1lbENhc2UpXHJcbiAgICogYWxsYSBjaGlhdmUgZGkgZXJyb3JlIHVzYXRhIGRhIEFuZ3VsYXIgKGxvd2VyY2FzZSkuXHJcbiAgICogRXM6IFwibWluTGVuZ3RoXCIgLT4gXCJtaW5sZW5ndGhcIiwgXCJtYXhMZW5ndGhcIiAtPiBcIm1heGxlbmd0aFwiLlxyXG4gICAqL1xyXG4gIHByaXZhdGUgbm9ybWFsaXplVmFsaWRhdGlvbktleShzY2hlbWFUeXBlOiBzdHJpbmcpOiBzdHJpbmcge1xyXG4gICAgY29uc3Qga2V5TWFwOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xyXG4gICAgICBtaW5MZW5ndGg6ICdtaW5sZW5ndGgnLFxyXG4gICAgICBtYXhMZW5ndGg6ICdtYXhsZW5ndGgnLFxyXG4gICAgfTtcclxuICAgIHJldHVybiBrZXlNYXBbc2NoZW1hVHlwZV0gfHwgc2NoZW1hVHlwZTtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgZ2V0RGVmYXVsdEVycm9yTWVzc2FnZShlcnJvcktleTogc3RyaW5nLCBlcnJvclZhbHVlOiBhbnkpOiBzdHJpbmcge1xyXG4gICAgLy8gU2UgaWwgdmFsb3JlIGRlbGwnZXJyb3JlIGUgZ2lhIHVuIG1lc3NhZ2dpbyBkZXNjcml0dGl2byAoc3RyaW5nYSksXHJcbiAgICAvLyBsbyByZXN0aXR1aXNjZSBkaXJldHRhbWVudGUuIFF1ZXN0byBzdXBwb3J0YSBpIHZhbGlkYXRvcmkgcGVyc29uYWxpenphdGlcclxuICAgIC8vIChlcy4gbG9jYXRpb24sIGxvY2F0aW9uLXRhYmxlKSBjaGUgZm9ybmlzY29ubyBtZXNzYWdnaSBlc3BsaWNpdGkgY29tZVxyXG4gICAgLy8gdmFsb3JlIGRlbGwnZXJyb3JlIChlcy4geyBuYXppb25lUmVxdWlyZWQ6ICdTZWxlemlvbmEgdW5hIG5hemlvbmUnIH0pLlxyXG4gICAgaWYgKHR5cGVvZiBlcnJvclZhbHVlID09PSAnc3RyaW5nJykge1xyXG4gICAgICByZXR1cm4gZXJyb3JWYWx1ZTtcclxuICAgIH1cclxuXHJcbiAgICBzd2l0Y2ggKGVycm9yS2V5KSB7XHJcbiAgICAgIGNhc2UgJ3JlcXVpcmVkJzpcclxuICAgICAgICByZXR1cm4gJ0NhbXBvIG9iYmxpZ2F0b3Jpbyc7XHJcbiAgICAgIGNhc2UgJ2VtYWlsJzpcclxuICAgICAgICByZXR1cm4gJ0Zvcm1hdG8gZW1haWwgbm9uIHZhbGlkbyc7XHJcbiAgICAgIGNhc2UgJ21pbic6XHJcbiAgICAgICAgcmV0dXJuIGBWYWxvcmUgbWluaW1vOiAke2Vycm9yVmFsdWUubWlufWA7XHJcbiAgICAgIGNhc2UgJ21heCc6XHJcbiAgICAgICAgcmV0dXJuIGBWYWxvcmUgbWFzc2ltbzogJHtlcnJvclZhbHVlLm1heH1gO1xyXG4gICAgICBjYXNlICdtaW5sZW5ndGgnOlxyXG4gICAgICAgIHJldHVybiBgTHVuZ2hlenphIG1pbmltYTogJHtlcnJvclZhbHVlLnJlcXVpcmVkTGVuZ3RofSBjYXJhdHRlcmlgO1xyXG4gICAgICBjYXNlICdtYXhsZW5ndGgnOlxyXG4gICAgICAgIHJldHVybiBgTHVuZ2hlenphIG1hc3NpbWE6ICR7ZXJyb3JWYWx1ZS5yZXF1aXJlZExlbmd0aH0gY2FyYXR0ZXJpYDtcclxuICAgICAgY2FzZSAncGF0dGVybic6XHJcbiAgICAgICAgcmV0dXJuICdGb3JtYXRvIG5vbiB2YWxpZG8nO1xyXG4gICAgICBjYXNlICdjcm9zc0ZpZWxkJzpcclxuICAgICAgICByZXR1cm4gJ1ZhbG9yZSBub24gY29lcmVudGUgY29uIGlsIGNhbXBvIGNvcnJlbGF0byc7XHJcbiAgICAgIGNhc2UgJ2RhdGUtbWluJzpcclxuICAgICAgICByZXR1cm4gZXJyb3JWYWx1ZT8ubWVzc2FnZSB8fCAnRGF0YSB0cm9wcG8gcmVtb3RhJztcclxuICAgICAgY2FzZSAnZGF0ZS1tYXgnOlxyXG4gICAgICAgIHJldHVybiBlcnJvclZhbHVlPy5tZXNzYWdlIHx8ICdEYXRhIHRyb3BwbyBhdmFuemF0YSc7XHJcbiAgICAgIGNhc2UgJ2ZpbGVTaXplJzpcclxuICAgICAgICByZXR1cm4gYERpbWVuc2lvbmUgbWFzc2ltYTogJHtlcnJvclZhbHVlPy5tYXhTaXplRm9ybWF0dGVkfWA7XHJcbiAgICAgIGNhc2UgJ2ZpbGVUeXBlJzpcclxuICAgICAgICByZXR1cm4gJ0Zvcm1hdG8gZmlsZSBub24gYWNjZXR0YXRvJztcclxuICAgICAgY2FzZSAnZmlsZUNvdW50JzpcclxuICAgICAgICByZXR1cm4gYE1hc3NpbW8gJHtlcnJvclZhbHVlPy5tYXhDb3VudH0gZmlsZWA7XHJcbiAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgcmV0dXJuICdWYWxvcmUgbm9uIHZhbGlkbyc7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcbiIsIjwhLS1cclxuICA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cclxuICBVSSBGT1JNIEJVSUxERVIgLSBURU1QTEFURSBQUklOQ0lQQUxFXHJcbiAgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XHJcbiAgQVJDSElURVRUVVJBOlxyXG4gIE9nbmkgPG5nLXRlbXBsYXRlPiBjaGUgY29udGllbmUgZGlyZXR0aXZlIFtmb3JtQ29udHJvbE5hbWVdXHJcbiAgREVWRSB3cmFwcGFyZSBpbCBwcm9wcmlvIGNvbnRlbnV0byBpbiB1biA8ZGl2IFtmb3JtR3JvdXBdPi5cclxuICBRdWVzdG8gcGVyY2hlIEFuZ3VsYXIgcmlzb2x2ZSBsJ2luamVjdG9yIGRpIGZvcm1Db250cm9sTmFtZVxyXG4gIGRhbCBDT05URVNUTyBESSBESUNISUFSQVpJT05FIGRlbCB0ZW1wbGF0ZSwgbm9uIGRhbCBwdW50b1xyXG4gIGRpIGluc2VyaW1lbnRvIChuZ1RlbXBsYXRlT3V0bGV0KS5cclxuXHJcbiAgU2VuemEgcXVlc3RvIHdyYXBwZXIsIGZvcm1Db250cm9sTmFtZSBub24gdHJvdmEgaWxcclxuICBGb3JtR3JvdXBEaXJlY3RpdmUgZSBsYW5jaWEgTkcwMTA1MC5cclxuXHJcbiAgUmVmOiB2ZXJzaW9uZSB0YWlsb3JlZCAoYW5nLW1zLXdlYmFwcCkgdXNhIGxvIHN0ZXNzbyBwYXR0ZXJuLlxyXG4gID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxyXG4tLT5cclxuXHJcbkBpZiAoc2NoZW1hICYmIGZvcm1Hcm91cCkge1xyXG4gIDxkaXYgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJcIiBbZm9ybUdyb3VwXT1cImZvcm1Hcm91cFwiIFtjbGFzc109XCJzY2hlbWEuY29uZmlnPy5jc3NDbGFzc2VzPy5qb2luKCcgJylcIj5cclxuXHJcbiAgICA8IS0tIERFQlVHOiBWZXJpZmljYSBjaGUgZm9ybUdyb3VwIHNpYSBpbml6aWFsaXp6YXRvIC0tPlxyXG4gICAgPCEtLSBbZm9ybUdyb3VwXSBlIHN1bCBkaXYgcGFkcmU7IHR1dHRpIGkgZm9ybUNvbnRyb2xOYW1lIElOTElORSBmdW56aW9uYW5vIC0tPlxyXG5cclxuICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0gSEVBREVSID09PT09PT09PT09PT09PT09PT09IC0tPlxyXG4gICAgQGlmIChzY2hlbWEudGl0bGUgfHwgc2NoZW1hLmRlc2NyaXB0aW9uKSB7XHJcbiAgICAgIDxkaXYgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2hlYWRlclwiPlxyXG4gICAgICAgIEBpZiAoc2NoZW1hLnRpdGxlKSB7XHJcbiAgICAgICAgICA8aDIgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX3RpdGxlXCI+e3sgc2NoZW1hLnRpdGxlIH19PC9oMj5cclxuICAgICAgICB9XHJcbiAgICAgICAgQGlmIChzY2hlbWEuZGVzY3JpcHRpb24pIHtcclxuICAgICAgICAgIDxwIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19kZXNjcmlwdGlvblwiPnt7IHNjaGVtYS5kZXNjcmlwdGlvbiB9fTwvcD5cclxuICAgICAgICB9XHJcbiAgICAgIDwvZGl2PlxyXG4gICAgfVxyXG5cclxuICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0gU0VaSU9OSSA9PT09PT09PT09PT09PT09PT09PSAtLT5cclxuICAgIDxkaXYgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX3NlY3Rpb25zXCI+XHJcbiAgICAgIEBmb3IgKHNlY3Rpb24gb2Ygc2NoZW1hLnNlY3Rpb25zOyB0cmFjayBzZWN0aW9uLmlkKSB7XHJcbiAgICAgICAgQGlmIChpc1NlY3Rpb25WaXNpYmxlKHNlY3Rpb24uaWQpKSB7XHJcblxyXG4gICAgICAgICAgPCEtLSBTZXppb25lIGNvbGxhc3NhYmlsZSAtLT5cclxuICAgICAgICAgIEBpZiAoc2VjdGlvbi5jb2xsYXBzaWJsZSkge1xyXG4gICAgICAgICAgICA8bWF0LWV4cGFuc2lvbi1wYW5lbFxyXG4gICAgICAgICAgICAgIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19zZWN0aW9uIHVpLWZvcm0tYnVpbGRlcl9fc2VjdGlvbi0tY29sbGFwc2libGVcIlxyXG4gICAgICAgICAgICAgIFtjbGFzc109XCJzZWN0aW9uLmNzc0NsYXNzZXM/LmpvaW4oJyAnKVwiXHJcbiAgICAgICAgICAgICAgW2V4cGFuZGVkXT1cIiFzZWN0aW9uLmNvbGxhcHNlZFwiXHJcbiAgICAgICAgICAgID5cclxuICAgICAgICAgICAgICA8bWF0LWV4cGFuc2lvbi1wYW5lbC1oZWFkZXI+XHJcbiAgICAgICAgICAgICAgICBAaWYgKHNlY3Rpb24udGl0bGUpIHtcclxuICAgICAgICAgICAgICAgICAgPG1hdC1wYW5lbC10aXRsZT57eyBzZWN0aW9uLnRpdGxlIH19PC9tYXQtcGFuZWwtdGl0bGU+XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBAaWYgKHNlY3Rpb24uZGVzY3JpcHRpb24pIHtcclxuICAgICAgICAgICAgICAgICAgPG1hdC1wYW5lbC1kZXNjcmlwdGlvbj57eyBzZWN0aW9uLmRlc2NyaXB0aW9uIH19PC9tYXQtcGFuZWwtZGVzY3JpcHRpb24+XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgPC9tYXQtZXhwYW5zaW9uLXBhbmVsLWhlYWRlcj5cclxuXHJcbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fc2VjdGlvbi1jb250ZW50XCIgW2NsYXNzLnVpLWZvcm0tYnVpbGRlcl9fZ3JpZF09XCJzY2hlbWEuY29uZmlnPy5sYXlvdXQgPT09ICdncmlkJyB8fCAhc2NoZW1hLmNvbmZpZz8ubGF5b3V0XCI+XHJcbiAgICAgICAgICAgICAgICBAZm9yIChmaWVsZCBvZiBzZWN0aW9uLmZpZWxkczsgdHJhY2sgZmllbGQua2V5KSB7XHJcbiAgICAgICAgICAgICAgICAgIEBpZiAoaXNGaWVsZFZpc2libGUoZmllbGQua2V5KSAmJiBmaWVsZC50eXBlICE9PSAnZmxhZycpIHtcclxuICAgICAgICAgICAgICAgICAgICA8ZGl2XHJcbiAgICAgICAgICAgICAgICAgICAgICBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZmllbGQtd3JhcHBlclwiXHJcbiAgICAgICAgICAgICAgICAgICAgICBbY2xhc3NdPVwiZ2V0RmllbGRXcmFwcGVyQ2xhc3NlcyhmaWVsZClcIlxyXG4gICAgICAgICAgICAgICAgICAgICAgW3N0eWxlLm9yZGVyXT1cImZpZWxkLmxheW91dD8ub3JkZXIgfHwgbnVsbFwiXHJcbiAgICAgICAgICAgICAgICAgICAgICBbYXR0ci5kYXRhLWZpZWxkLWtleV09XCJmaWVsZC5rZXlcIlxyXG4gICAgICAgICAgICAgICAgICAgID5cclxuICAgICAgICAgICAgICAgICAgICAgIDwhLS0gT1VUTEVUOiBpbCB0ZW1wbGF0ZSBmaWVsZFRwbCBoYSBpbCBwcm9wcmlvIFtmb3JtR3JvdXBdIHdyYXBwZXIgLS0+XHJcbiAgICAgICAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICpuZ1RlbXBsYXRlT3V0bGV0PVwiZmllbGRUcGw7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBmaWVsZCB9XCJcclxuICAgICAgICAgICAgICAgICAgICAgIC8+XHJcbiAgICAgICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgPC9tYXQtZXhwYW5zaW9uLXBhbmVsPlxyXG4gICAgICAgICAgfSBAZWxzZSB7XHJcbiAgICAgICAgICAgIDwhLS0gU2V6aW9uZSBub24gY29sbGFzc2FiaWxlIC0tPlxyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19zZWN0aW9uXCIgW2NsYXNzXT1cInNlY3Rpb24uY3NzQ2xhc3Nlcz8uam9pbignICcpXCI+XHJcbiAgICAgICAgICAgICAgQGlmIChzZWN0aW9uLnRpdGxlIHx8IHNlY3Rpb24uZGVzY3JpcHRpb24pIHtcclxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX3NlY3Rpb24taGVhZGVyXCI+XHJcbiAgICAgICAgICAgICAgICAgIEBpZiAoc2VjdGlvbi50aXRsZSkge1xyXG4gICAgICAgICAgICAgICAgICAgIDxoMyBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fc2VjdGlvbi10aXRsZVwiPnt7IHNlY3Rpb24udGl0bGUgfX08L2gzPlxyXG4gICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgIEBpZiAoc2VjdGlvbi5kZXNjcmlwdGlvbikge1xyXG4gICAgICAgICAgICAgICAgICAgIDxwIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19zZWN0aW9uLWRlc2NyaXB0aW9uXCI+e3sgc2VjdGlvbi5kZXNjcmlwdGlvbiB9fTwvcD5cclxuICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19zZWN0aW9uLWNvbnRlbnRcIiBbY2xhc3MudWktZm9ybS1idWlsZGVyX19ncmlkXT1cInNjaGVtYS5jb25maWc/LmxheW91dCA9PT0gJ2dyaWQnIHx8ICFzY2hlbWEuY29uZmlnPy5sYXlvdXRcIj5cclxuICAgICAgICAgICAgICAgIEBmb3IgKGZpZWxkIG9mIHNlY3Rpb24uZmllbGRzOyB0cmFjayBmaWVsZC5rZXkpIHtcclxuICAgICAgICAgICAgICAgICAgQGlmIChpc0ZpZWxkVmlzaWJsZShmaWVsZC5rZXkpICYmIGZpZWxkLnR5cGUgIT09ICdmbGFnJykge1xyXG4gICAgICAgICAgICAgICAgICAgIDxkaXZcclxuICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19maWVsZC13cmFwcGVyXCJcclxuICAgICAgICAgICAgICAgICAgICAgIFtjbGFzc109XCJnZXRGaWVsZFdyYXBwZXJDbGFzc2VzKGZpZWxkKVwiXHJcbiAgICAgICAgICAgICAgICAgICAgICBbc3R5bGUub3JkZXJdPVwiZmllbGQubGF5b3V0Py5vcmRlciB8fCBudWxsXCJcclxuICAgICAgICAgICAgICAgICAgICAgIFthdHRyLmRhdGEtZmllbGQta2V5XT1cImZpZWxkLmtleVwiXHJcbiAgICAgICAgICAgICAgICAgICAgPlxyXG4gICAgICAgICAgICAgICAgICAgICAgPCEtLSBPVVRMRVQ6IGlsIHRlbXBsYXRlIGZpZWxkVHBsIGhhIGlsIHByb3ByaW8gW2Zvcm1Hcm91cF0gd3JhcHBlciAtLT5cclxuICAgICAgICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXJcclxuICAgICAgICAgICAgICAgICAgICAgICAgKm5nVGVtcGxhdGVPdXRsZXQ9XCJmaWVsZFRwbDsgY29udGV4dDogeyAkaW1wbGljaXQ6IGZpZWxkIH1cIlxyXG4gICAgICAgICAgICAgICAgICAgICAgLz5cclxuICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIDwvZGl2PlxyXG5cclxuICAgIDwhLS0gPT09PT09PT09PT09PT09PT09PT0gRk9PVEVSID09PT09PT09PT09PT09PT09PT09IC0tPlxyXG4gICAgQGlmICghc2NoZW1hLmNvbmZpZz8uaGlkZUZvb3Rlcikge1xyXG4gICAgICA8ZGl2IGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19mb290ZXJcIj5cclxuICAgICAgICA8dWktZm9ybS1lcnJvci1zdW1tYXJ5XHJcbiAgICAgICAgICBbZXJyb3JzXT1cImZvcm1FcnJvcnNcIlxyXG4gICAgICAgICAgW3RvdGFsRXJyb3JDb3VudF09XCJnZXRGb3JtRXJyb3JzQ291bnQoKVwiXHJcbiAgICAgICAgICAoZmllbGRDbGljayk9XCJzY3JvbGxUb0ZpZWxkKCRldmVudClcIlxyXG4gICAgICAgIC8+XHJcbiAgICAgICAgPHVpLWJ1dHRvbi1hcmVhXHJcbiAgICAgICAgICBbYnV0dG9uc109XCJmb3JtQnV0dG9uc1wiXHJcbiAgICAgICAgICBhbGlnbj1cImVuZFwiXHJcbiAgICAgICAgICBbbG9hZGluZ0lkc109XCJsb2FkaW5nRm9yXCJcclxuICAgICAgICAvPlxyXG4gICAgICA8L2Rpdj5cclxuICAgIH1cclxuICA8L2Rpdj5cclxufVxyXG5cclxuPCEtLSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gLS0+XHJcbjwhLS0gRklFTEQgVEVNUExBVEUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT5cclxuPCEtLSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gLS0+XHJcbjwhLS1cclxuICBDUklUSUNPOiBRdWVzdG8gdGVtcGxhdGUgZSBkaWNoaWFyYXRvIEZVT1JJIGRhbCA8ZGl2IFtmb3JtR3JvdXBdPi5cclxuICBBbmd1bGFyIHJpc29sdmUgbCdpbmplY3RvciBkYWwgY29udGVzdG8gZGkgRElDSElBUkFaSU9ORS5cclxuICBRdWluZGkgRE9CQklBTU8gd3JhcHBhcmUgaWwgY29udGVudXRvIGluIHVuIDxkaXYgW2Zvcm1Hcm91cF0+XHJcbiAgcGVyIGZvcm5pcmUgdW4gRm9ybUdyb3VwRGlyZWN0aXZlIGFpIGZvcm1Db250cm9sTmFtZSBmaWdsaS5cclxuICBTZW56YSBxdWVzdG8gd3JhcHBlciAtPiBlcnJvcmUgTkcwMTA1MC5cclxuLS0+XHJcbjxuZy10ZW1wbGF0ZSAjZmllbGRUcGwgbGV0LWZpZWxkPlxyXG4gIEBpZiAoZm9ybUdyb3VwKSB7XHJcbiAgICA8ZGl2IFtmb3JtR3JvdXBdPVwiZm9ybUdyb3VwXCIgY2xhc3M9XCJ1aS1mYi1maWVsZC1jdHhcIj5cclxuICAgICAgQHN3aXRjaCAoZmllbGQudHlwZSkge1xyXG5cclxuICAgICAgICA8IS0tIFRFWFQgLyBFTUFJTCAvIFBBU1NXT1JEIC0tPlxyXG4gICAgICAgIEBjYXNlICgndGV4dCcpIHsgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cInRleHRGaWVsZFRwbDsgY29udGV4dDogeyAkaW1wbGljaXQ6IGZpZWxkIH1cIiAvPiB9XHJcbiAgICAgICAgQGNhc2UgKCdlbWFpbCcpIHsgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cInRleHRGaWVsZFRwbDsgY29udGV4dDogeyAkaW1wbGljaXQ6IGZpZWxkIH1cIiAvPiB9XHJcbiAgICAgICAgQGNhc2UgKCdwYXNzd29yZCcpIHsgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cInRleHRGaWVsZFRwbDsgY29udGV4dDogeyAkaW1wbGljaXQ6IGZpZWxkIH1cIiAvPiB9XHJcblxyXG4gICAgICAgIDwhLS0gTlVNQkVSIC0tPlxyXG4gICAgICAgIEBjYXNlICgnbnVtYmVyJykge1xyXG4gICAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2Z1bGwtd2lkdGhcIj5cclxuICAgICAgICAgICAgPG1hdC1sYWJlbD57eyBmaWVsZC5sYWJlbCB9fTwvbWF0LWxhYmVsPlxyXG4gICAgICAgICAgICBAaWYgKGZpZWxkLm1ldGFkYXRhPy50eXBlID09PSAnY3VycmVuY3knKSB7XHJcbiAgICAgICAgICAgICAgPGlucHV0IG1hdElucHV0IHR5cGU9XCJ0ZXh0XCIgW2Zvcm1Db250cm9sTmFtZV09XCJmaWVsZC5rZXlcIlxyXG4gICAgICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnXCJcclxuICAgICAgICAgICAgICAgIFtyZWFkb25seV09XCJmaWVsZC5yZWFkb25seSB8fCByZWFkb25seVwiXHJcbiAgICAgICAgICAgICAgICB1aUN1cnJlbmN5SW5wdXRcclxuICAgICAgICAgICAgICAgIFtjdXJyZW5jeVN5bWJvbF09XCJmaWVsZC5tZXRhZGF0YT8uY3VycmVuY3kgfHwgJ0VVUidcIlxyXG4gICAgICAgICAgICAgICAgW2RlY2ltYWxQbGFjZXNdPVwiZmllbGQubWV0YWRhdGE/LmRlY2ltYWxzID8/IDJcIlxyXG4gICAgICAgICAgICAgIC8+XHJcbiAgICAgICAgICAgIH0gQGVsc2Uge1xyXG4gICAgICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCB0eXBlPVwibnVtYmVyXCIgW2Zvcm1Db250cm9sTmFtZV09XCJmaWVsZC5rZXlcIlxyXG4gICAgICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnXCJcclxuICAgICAgICAgICAgICAgIFtyZWFkb25seV09XCJmaWVsZC5yZWFkb25seSB8fCByZWFkb25seVwiXHJcbiAgICAgICAgICAgICAgLz5cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBAaWYgKGZpZWxkLmljb25Ub29sdGlwKSB7XHJcbiAgICAgICAgICAgICAgPGJ1dHRvbiBtYXRTdWZmaXggbWF0LWljb24tYnV0dG9uIHR5cGU9XCJidXR0b25cIlxyXG4gICAgICAgICAgICAgICAgW21hdFRvb2x0aXBdPVwiZmllbGQuaWNvblRvb2x0aXAudGV4dFwiIFthdHRyLmFyaWEtbGFiZWxdPVwiZmllbGQuaWNvblRvb2x0aXAudGV4dFwiPlxyXG4gICAgICAgICAgICAgICAgPGx1Y2lkZS1pY29uIFtuYW1lXT1cImZpZWxkLmljb25Ub29sdGlwLmljb24gfHwgJ2luZm8nXCIgW3NpemVdPVwiMThcIiAvPlxyXG4gICAgICAgICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIDxtYXQtZXJyb3I+XHJcbiAgICAgICAgICAgICAgQGZvciAoZXJyIG9mIGdldEZpZWxkRXJyb3JzKGZpZWxkLmtleSk7IHRyYWNrICRpbmRleCkge1xyXG4gICAgICAgICAgICAgICAgPGRpdj57eyBlcnIgfX08L2Rpdj5cclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIDwvbWF0LWVycm9yPlxyXG4gICAgICAgICAgPC9tYXQtZm9ybS1maWVsZD5cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIDwhLS0gVEVYVEFSRUEgLS0+XHJcbiAgICAgICAgQGNhc2UgKCd0ZXh0YXJlYScpIHtcclxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX3RleHRhcmVhLXdyYXBwZXJcIj5cclxuICAgICAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2Z1bGwtd2lkdGhcIj5cclxuICAgICAgICAgICAgICA8bWF0LWxhYmVsPnt7IGZpZWxkLmxhYmVsIH19PC9tYXQtbGFiZWw+XHJcbiAgICAgICAgICAgICAgPHRleHRhcmVhIG1hdElucHV0IFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGQua2V5XCJcclxuICAgICAgICAgICAgICAgIFtwbGFjZWhvbGRlcl09XCJmaWVsZC5wbGFjZWhvbGRlciB8fCAnJ1wiXHJcbiAgICAgICAgICAgICAgICBbcmVhZG9ubHldPVwiZmllbGQucmVhZG9ubHkgfHwgcmVhZG9ubHlcIlxyXG4gICAgICAgICAgICAgICAgcm93cz1cIjRcIlxyXG4gICAgICAgICAgICAgID48L3RleHRhcmVhPlxyXG4gICAgICAgICAgICAgIEBpZiAoZmllbGQuaWNvblRvb2x0aXApIHtcclxuICAgICAgICAgICAgICAgIDxidXR0b24gbWF0U3VmZml4IG1hdC1pY29uLWJ1dHRvbiB0eXBlPVwiYnV0dG9uXCJcclxuICAgICAgICAgICAgICAgICAgW21hdFRvb2x0aXBdPVwiZmllbGQuaWNvblRvb2x0aXAudGV4dFwiIFthdHRyLmFyaWEtbGFiZWxdPVwiZmllbGQuaWNvblRvb2x0aXAudGV4dFwiPlxyXG4gICAgICAgICAgICAgICAgICA8bHVjaWRlLWljb24gW25hbWVdPVwiZmllbGQuaWNvblRvb2x0aXAuaWNvbiB8fCAnaW5mbydcIiBbc2l6ZV09XCIxOFwiIC8+XHJcbiAgICAgICAgICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgPG1hdC1lcnJvcj5cclxuICAgICAgICAgICAgICAgIEBmb3IgKGVyciBvZiBnZXRGaWVsZEVycm9ycyhmaWVsZC5rZXkpOyB0cmFjayAkaW5kZXgpIHtcclxuICAgICAgICAgICAgICAgICAgPGRpdj57eyBlcnIgfX08L2Rpdj5cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICA8L21hdC1lcnJvcj5cclxuICAgICAgICAgICAgPC9tYXQtZm9ybS1maWVsZD5cclxuICAgICAgICAgICAgQGlmIChnZXRDaGFyQ291bnQoZmllbGQua2V5KTsgYXMgY2MpIHtcclxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fY2hhci1jb3VudFwiXHJcbiAgICAgICAgICAgICAgICBbY2xhc3MudWktZm9ybS1idWlsZGVyX19jaGFyLWNvdW50LS13YXJuXT1cImNjLmN1cnJlbnQgPiBjYy5tYXggKiAwLjlcIlxyXG4gICAgICAgICAgICAgICAgW2NsYXNzLnVpLWZvcm0tYnVpbGRlcl9fY2hhci1jb3VudC0tZXJyb3JdPVwiY2MuY3VycmVudCA+IGNjLm1heFwiPlxyXG4gICAgICAgICAgICAgICAge3sgY2MuY3VycmVudCB9fSAvIHt7IGNjLm1heCB9fVxyXG4gICAgICAgICAgICAgIDwvc3Bhbj5cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICA8IS0tIFNFTEVDVCAtLT5cclxuICAgICAgICBAY2FzZSAoJ3NlbGVjdCcpIHtcclxuICAgICAgICAgIEBpZiAoZmllbGQuc2VhcmNoYWJsZSkge1xyXG4gICAgICAgICAgICA8IS0tIEF1dG9jb21wbGV0ZSBzZWxlY3QgLS0+XHJcbiAgICAgICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19mdWxsLXdpZHRoXCI+XHJcbiAgICAgICAgICAgICAgPG1hdC1sYWJlbD57eyBmaWVsZC5sYWJlbCB9fTwvbWF0LWxhYmVsPlxyXG4gICAgICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCBbZm9ybUNvbnRyb2xOYW1lXT1cImZpZWxkLmtleVwiXHJcbiAgICAgICAgICAgICAgICBbbWF0QXV0b2NvbXBsZXRlXT1cImF1dG9cIlxyXG4gICAgICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnXCJcclxuICAgICAgICAgICAgICAgIFtyZWFkb25seV09XCJmaWVsZC5yZWFkb25seSB8fCByZWFkb25seVwiXHJcbiAgICAgICAgICAgICAgICAoaW5wdXQpPVwiZmlsdGVyT3B0aW9ucyhmaWVsZCwgJGFueSgkZXZlbnQudGFyZ2V0KS52YWx1ZSlcIlxyXG4gICAgICAgICAgICAgIC8+XHJcbiAgICAgICAgICAgICAgPG1hdC1hdXRvY29tcGxldGUgI2F1dG89XCJtYXRBdXRvY29tcGxldGVcIiBbZGlzcGxheVdpdGhdPVwiZGlzcGxheUZuKGdldEZpZWxkT3B0aW9ucyhmaWVsZCkpXCI+XHJcbiAgICAgICAgICAgICAgICBAZm9yIChvcHQgb2YgZ2V0RmllbGRPcHRpb25zKGZpZWxkKTsgdHJhY2sgb3B0LnZhbHVlKSB7XHJcbiAgICAgICAgICAgICAgICAgIDxtYXQtb3B0aW9uIFt2YWx1ZV09XCJvcHQudmFsdWVcIiBbZGlzYWJsZWRdPVwib3B0LmRpc2FibGVkXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgQGlmIChvcHQuaWNvbikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgPGx1Y2lkZS1pY29uIFtuYW1lXT1cIm9wdC5pY29uXCIgW3NpemVdPVwiMTZcIiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fb3B0aW9uLWljb25cIiAvPlxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB7eyBvcHQubGFiZWwgfX1cclxuICAgICAgICAgICAgICAgICAgPC9tYXQtb3B0aW9uPlxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIDwvbWF0LWF1dG9jb21wbGV0ZT5cclxuICAgICAgICAgICAgICA8bWF0LWVycm9yPlxyXG4gICAgICAgICAgICAgICAgQGZvciAoZXJyIG9mIGdldEZpZWxkRXJyb3JzKGZpZWxkLmtleSk7IHRyYWNrICRpbmRleCkge1xyXG4gICAgICAgICAgICAgICAgICA8ZGl2Pnt7IGVyciB9fTwvZGl2PlxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIDwvbWF0LWVycm9yPlxyXG4gICAgICAgICAgICA8L21hdC1mb3JtLWZpZWxkPlxyXG4gICAgICAgICAgfSBAZWxzZSB7XHJcbiAgICAgICAgICAgIDwhLS0gU3RhbmRhcmQgc2VsZWN0IC0tPlxyXG4gICAgICAgICAgICA8bWF0LWZvcm0tZmllbGQgYXBwZWFyYW5jZT1cIm91dGxpbmVcIiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZnVsbC13aWR0aFwiPlxyXG4gICAgICAgICAgICAgIDxtYXQtbGFiZWw+e3sgZmllbGQubGFiZWwgfX08L21hdC1sYWJlbD5cclxuICAgICAgICAgICAgICA8bWF0LXNlbGVjdCBbZm9ybUNvbnRyb2xOYW1lXT1cImZpZWxkLmtleVwiIFtwbGFjZWhvbGRlcl09XCJmaWVsZC5wbGFjZWhvbGRlciB8fCAnJ1wiPlxyXG4gICAgICAgICAgICAgICAgQGlmICghZmllbGQuaGlkZUVtcHR5T3B0aW9uKSB7XHJcbiAgICAgICAgICAgICAgICAgIDxtYXQtb3B0aW9uIFt2YWx1ZV09XCJudWxsXCI+LS08L21hdC1vcHRpb24+XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBAZm9yIChvcHQgb2YgZ2V0RmllbGRPcHRpb25zKGZpZWxkKTsgdHJhY2sgb3B0LnZhbHVlKSB7XHJcbiAgICAgICAgICAgICAgICAgIDxtYXQtb3B0aW9uIFt2YWx1ZV09XCJvcHQudmFsdWVcIiBbZGlzYWJsZWRdPVwib3B0LmRpc2FibGVkXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgQGlmIChvcHQuaWNvbikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgPGx1Y2lkZS1pY29uIFtuYW1lXT1cIm9wdC5pY29uXCIgW3NpemVdPVwiMTZcIiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fb3B0aW9uLWljb25cIiAvPlxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB7eyBvcHQubGFiZWwgfX1cclxuICAgICAgICAgICAgICAgICAgICBAaWYgKG9wdC50b29sdGlwKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICA8bHVjaWRlLWljb24gbmFtZT1cImluZm9cIiBbc2l6ZV09XCIxNFwiXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIFttYXRUb29sdGlwXT1cIm9wdC50b29sdGlwXCIgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX29wdGlvbi1pbmZvXCIgLz5cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgIDwvbWF0LW9wdGlvbj5cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICA8L21hdC1zZWxlY3Q+XHJcbiAgICAgICAgICAgICAgQGlmIChmaWVsZC5pY29uVG9vbHRpcCkge1xyXG4gICAgICAgICAgICAgICAgPGJ1dHRvbiBtYXRTdWZmaXggbWF0LWljb24tYnV0dG9uIHR5cGU9XCJidXR0b25cIlxyXG4gICAgICAgICAgICAgICAgICBbbWF0VG9vbHRpcF09XCJmaWVsZC5pY29uVG9vbHRpcC50ZXh0XCIgW2F0dHIuYXJpYS1sYWJlbF09XCJmaWVsZC5pY29uVG9vbHRpcC50ZXh0XCI+XHJcbiAgICAgICAgICAgICAgICAgIDxsdWNpZGUtaWNvbiBbbmFtZV09XCJmaWVsZC5pY29uVG9vbHRpcC5pY29uIHx8ICdpbmZvJ1wiIFtzaXplXT1cIjE4XCIgLz5cclxuICAgICAgICAgICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICA8bWF0LWVycm9yPlxyXG4gICAgICAgICAgICAgICAgQGZvciAoZXJyIG9mIGdldEZpZWxkRXJyb3JzKGZpZWxkLmtleSk7IHRyYWNrICRpbmRleCkge1xyXG4gICAgICAgICAgICAgICAgICA8ZGl2Pnt7IGVyciB9fTwvZGl2PlxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIDwvbWF0LWVycm9yPlxyXG4gICAgICAgICAgICA8L21hdC1mb3JtLWZpZWxkPlxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgPCEtLSBNVUxUSVNFTEVDVCAtLT5cclxuICAgICAgICBAY2FzZSAoJ211bHRpc2VsZWN0Jykge1xyXG4gICAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2Z1bGwtd2lkdGhcIj5cclxuICAgICAgICAgICAgPG1hdC1sYWJlbD57eyBmaWVsZC5sYWJlbCB9fTwvbWF0LWxhYmVsPlxyXG4gICAgICAgICAgICA8bWF0LXNlbGVjdCBbZm9ybUNvbnRyb2xOYW1lXT1cImZpZWxkLmtleVwiIG11bHRpcGxlIFtwbGFjZWhvbGRlcl09XCJmaWVsZC5wbGFjZWhvbGRlciB8fCAnJ1wiPlxyXG4gICAgICAgICAgICAgIEBpZiAoZmllbGQuYWxsb3dTZWxlY3RBbGwpIHtcclxuICAgICAgICAgICAgICAgIDxtYXQtb3B0aW9uIChjbGljayk9XCJzZWxlY3RBbGwoZmllbGQpXCIgKGtleWRvd24uZW50ZXIpPVwic2VsZWN0QWxsKGZpZWxkKVwiIChrZXlkb3duLnNwYWNlKT1cInNlbGVjdEFsbChmaWVsZClcIj5cclxuICAgICAgICAgICAgICAgICAgPGVtPlNlbGV6aW9uYSB0dXR0bzwvZW0+XHJcbiAgICAgICAgICAgICAgICA8L21hdC1vcHRpb24+XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIEBmb3IgKG9wdCBvZiBnZXRGaWVsZE9wdGlvbnMoZmllbGQpOyB0cmFjayBvcHQudmFsdWUpIHtcclxuICAgICAgICAgICAgICAgIDxtYXQtb3B0aW9uIFt2YWx1ZV09XCJvcHQudmFsdWVcIiBbZGlzYWJsZWRdPVwib3B0LmRpc2FibGVkXCI+XHJcbiAgICAgICAgICAgICAgICAgIHt7IG9wdC5sYWJlbCB9fVxyXG4gICAgICAgICAgICAgICAgPC9tYXQtb3B0aW9uPlxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgPC9tYXQtc2VsZWN0PlxyXG4gICAgICAgICAgICA8bWF0LWVycm9yPlxyXG4gICAgICAgICAgICAgIEBmb3IgKGVyciBvZiBnZXRGaWVsZEVycm9ycyhmaWVsZC5rZXkpOyB0cmFjayAkaW5kZXgpIHtcclxuICAgICAgICAgICAgICAgIDxkaXY+e3sgZXJyIH19PC9kaXY+XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA8L21hdC1lcnJvcj5cclxuICAgICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XHJcbiAgICAgICAgICA8IS0tIENoaXBzIHByZXZpZXcgc290dG8gaWwgc2VsZWN0IC0tPlxyXG4gICAgICAgICAgQGlmIChmb3JtR3JvdXAuZ2V0KGZpZWxkLmtleSk/LnZhbHVlPy5sZW5ndGgpIHtcclxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fY2hpcHMtcHJldmlld1wiPlxyXG4gICAgICAgICAgICAgIEBmb3IgKHZhbCBvZiBmb3JtR3JvdXAuZ2V0KGZpZWxkLmtleSkhLnZhbHVlOyB0cmFjayB2YWwpIHtcclxuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19jaGlwXCI+XHJcbiAgICAgICAgICAgICAgICAgIHt7IGdldE9wdGlvbkxhYmVsKGZpZWxkLCB2YWwpIH19XHJcbiAgICAgICAgICAgICAgICAgIDxidXR0b24gdHlwZT1cImJ1dHRvblwiIChjbGljayk9XCJyZW1vdmVNdWx0aXNlbGVjdENoaXAoZmllbGQsIHZhbClcIlxyXG4gICAgICAgICAgICAgICAgICAgIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19jaGlwLXJlbW92ZVwiIGFyaWEtbGFiZWw9XCJSaW11b3ZpXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPGx1Y2lkZS1pY29uIG5hbWU9XCJ4XCIgW3NpemVdPVwiMTJcIiAvPlxyXG4gICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgICAgICAgICAgIDwvc3Bhbj5cclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgPCEtLSBGUkVFTVVMVElTRUxFQ1QgLS0+XHJcbiAgICAgICAgQGNhc2UgKCdmcmVlbXVsdGlzZWxlY3QnKSB7XHJcbiAgICAgICAgICA8bWF0LWZvcm0tZmllbGQgYXBwZWFyYW5jZT1cIm91dGxpbmVcIiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZnVsbC13aWR0aCB1aS1mb3JtLWJ1aWxkZXJfX2ZyZWUtbXVsdGktZmllbGRcIj5cclxuICAgICAgICAgICAgPG1hdC1sYWJlbD57eyBmaWVsZC5sYWJlbCB9fTwvbWF0LWxhYmVsPlxyXG4gICAgICAgICAgICA8bWF0LWNoaXAtZ3JpZCAjY2hpcEdyaWQgW2Zvcm1Db250cm9sTmFtZV09XCJmaWVsZC5rZXlcIj5cclxuICAgICAgICAgICAgICBAZm9yIChjaGlwIG9mIGZvcm1Hcm91cC5nZXQoZmllbGQua2V5KT8udmFsdWUgfHwgW107IHRyYWNrIGNoaXApIHtcclxuICAgICAgICAgICAgICAgIDxtYXQtY2hpcC1yb3cgW3JlbW92YWJsZV09XCIhaXNDaGlwUHJvdGVjdGVkKGZpZWxkLCBjaGlwKVwiIChyZW1vdmVkKT1cInJlbW92ZUZyZWVDaGlwKGZpZWxkLCBjaGlwKVwiPlxyXG4gICAgICAgICAgICAgICAgICB7eyBjaGlwIH19XHJcbiAgICAgICAgICAgICAgICAgIEBpZiAoIWlzQ2hpcFByb3RlY3RlZChmaWVsZCwgY2hpcCkpIHtcclxuICAgICAgICAgICAgICAgICAgICA8YnV0dG9uIG1hdENoaXBSZW1vdmUgYXJpYS1sYWJlbD1cIlJpbXVvdmlcIj5cclxuICAgICAgICAgICAgICAgICAgICAgIDxsdWNpZGUtaWNvbiBuYW1lPVwieFwiIFtzaXplXT1cIjE0XCIgLz5cclxuICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgPC9tYXQtY2hpcC1yb3c+XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA8L21hdC1jaGlwLWdyaWQ+XHJcbiAgICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dFxyXG4gICAgICAgICAgICAgIFttYXRDaGlwSW5wdXRGb3JdPVwiY2hpcEdyaWRcIlxyXG4gICAgICAgICAgICAgIFtwbGFjZWhvbGRlcl09XCJmaWVsZC5wbGFjZWhvbGRlciB8fCAnQWdnaXVuZ2kuLi4nXCJcclxuICAgICAgICAgICAgICBbcmVhZG9ubHldPVwiZmllbGQucmVhZG9ubHkgfHwgcmVhZG9ubHlcIlxyXG4gICAgICAgICAgICAgIChtYXRDaGlwSW5wdXRUb2tlbkVuZCk9XCJhZGRGcmVlQ2hpcChmaWVsZCwgJGV2ZW50KVwiXHJcbiAgICAgICAgICAgICAgKGJsdXIpPVwiYWRkRnJlZUNoaXBPbkJsdXIoZmllbGQsICRldmVudClcIlxyXG4gICAgICAgICAgICAvPlxyXG4gICAgICAgICAgICA8bWF0LWVycm9yPlxyXG4gICAgICAgICAgICAgIEBmb3IgKGVyciBvZiBnZXRGaWVsZEVycm9ycyhmaWVsZC5rZXkpOyB0cmFjayAkaW5kZXgpIHtcclxuICAgICAgICAgICAgICAgIDxkaXY+e3sgZXJyIH19PC9kaXY+XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA8L21hdC1lcnJvcj5cclxuICAgICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICA8IS0tIENIRUNLQk9YIC0tPlxyXG4gICAgICAgIEBjYXNlICgnY2hlY2tib3gnKSB7XHJcbiAgICAgICAgICBAaWYgKGZpZWxkLmFwcGVhcmFuY2U/LnN0eWxlID09PSAnc3dpdGNoJykge1xyXG4gICAgICAgICAgICA8bWF0LXNsaWRlLXRvZ2dsZVxyXG4gICAgICAgICAgICAgIFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGQua2V5XCJcclxuICAgICAgICAgICAgICBbY29sb3JdPVwiZmllbGQuYXBwZWFyYW5jZT8uY29sb3IgfHwgJ3ByaW1hcnknXCJcclxuICAgICAgICAgICAgPlxyXG4gICAgICAgICAgICAgIHt7IGZpZWxkLmxhYmVsIH19XHJcbiAgICAgICAgICAgIDwvbWF0LXNsaWRlLXRvZ2dsZT5cclxuICAgICAgICAgIH0gQGVsc2Uge1xyXG4gICAgICAgICAgICA8bWF0LWNoZWNrYm94XHJcbiAgICAgICAgICAgICAgW2Zvcm1Db250cm9sTmFtZV09XCJmaWVsZC5rZXlcIlxyXG4gICAgICAgICAgICAgIFtjb2xvcl09XCJmaWVsZC5hcHBlYXJhbmNlPy5jb2xvciB8fCAncHJpbWFyeSdcIlxyXG4gICAgICAgICAgICA+XHJcbiAgICAgICAgICAgICAge3sgZmllbGQubGFiZWwgfX1cclxuICAgICAgICAgICAgICBAaWYgKGZpZWxkLmljb25Ub29sdGlwKSB7XHJcbiAgICAgICAgICAgICAgICA8bHVjaWRlLWljb24gW25hbWVdPVwiZmllbGQuaWNvblRvb2x0aXAuaWNvbiB8fCAnaW5mbydcIiBbc2l6ZV09XCIxNFwiXHJcbiAgICAgICAgICAgICAgICAgIFttYXRUb29sdGlwXT1cImZpZWxkLmljb25Ub29sdGlwLnRleHRcIiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9faW5saW5lLXRvb2x0aXBcIiAvPlxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgPC9tYXQtY2hlY2tib3g+XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICBAaWYgKHNob3VsZFNob3dGaWVsZEVycm9ycyhmaWVsZC5rZXkpKSB7XHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2ZpZWxkLWVycm9yXCI+XHJcbiAgICAgICAgICAgICAgQGZvciAoZXJyIG9mIGdldEZpZWxkRXJyb3JzKGZpZWxkLmtleSk7IHRyYWNrICRpbmRleCkge1xyXG4gICAgICAgICAgICAgICAgPHNwYW4+e3sgZXJyIH19PC9zcGFuPlxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICA8IS0tIFNXSVRDSCAtLT5cclxuICAgICAgICBAY2FzZSAoJ3N3aXRjaCcpIHtcclxuICAgICAgICAgIDxtYXQtc2xpZGUtdG9nZ2xlXHJcbiAgICAgICAgICAgIFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGQua2V5XCJcclxuICAgICAgICAgICAgW2NvbG9yXT1cImZpZWxkLmFwcGVhcmFuY2U/LmNvbG9yIHx8ICdwcmltYXJ5J1wiXHJcbiAgICAgICAgICA+XHJcbiAgICAgICAgICAgIHt7IGZpZWxkLmxhYmVsIH19XHJcbiAgICAgICAgICAgIEBpZiAoZmllbGQuaWNvblRvb2x0aXApIHtcclxuICAgICAgICAgICAgICA8bHVjaWRlLWljb24gW25hbWVdPVwiZmllbGQuaWNvblRvb2x0aXAuaWNvbiB8fCAnaW5mbydcIiBbc2l6ZV09XCIxNFwiXHJcbiAgICAgICAgICAgICAgICBbbWF0VG9vbHRpcF09XCJmaWVsZC5pY29uVG9vbHRpcC50ZXh0XCIgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2lubGluZS10b29sdGlwXCIgLz5cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgPC9tYXQtc2xpZGUtdG9nZ2xlPlxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgPCEtLSBSQURJTyAtLT5cclxuICAgICAgICBAY2FzZSAoJ3JhZGlvJykge1xyXG4gICAgICAgICAgPGRpdiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fcmFkaW8td3JhcHBlclwiPlxyXG4gICAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2ZpZWxkLWxhYmVsXCI+XHJcbiAgICAgICAgICAgICAge3sgZmllbGQubGFiZWwgfX1cclxuICAgICAgICAgICAgICBAaWYgKGlzRmllbGRSZXF1aXJlZChmaWVsZC5rZXkpKSB7IDxzcGFuIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19yZXF1aXJlZFwiPio8L3NwYW4+IH1cclxuICAgICAgICAgICAgPC9sYWJlbD5cclxuICAgICAgICAgICAgPG1hdC1yYWRpby1ncm91cCBbZm9ybUNvbnRyb2xOYW1lXT1cImZpZWxkLmtleVwiIFtjb2xvcl09XCJmaWVsZC5hcHBlYXJhbmNlPy5jb2xvciB8fCAncHJpbWFyeSdcIj5cclxuICAgICAgICAgICAgICBAZm9yIChvcHQgb2YgZ2V0RmllbGRPcHRpb25zKGZpZWxkKTsgdHJhY2sgb3B0LnZhbHVlKSB7XHJcbiAgICAgICAgICAgICAgICA8bWF0LXJhZGlvLWJ1dHRvbiBbdmFsdWVdPVwib3B0LnZhbHVlXCIgW2Rpc2FibGVkXT1cIm9wdC5kaXNhYmxlZFwiPlxyXG4gICAgICAgICAgICAgICAgICBAaWYgKG9wdC5pY29uKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgPGx1Y2lkZS1pY29uIFtuYW1lXT1cIm9wdC5pY29uXCIgW3NpemVdPVwiMTZcIiAvPlxyXG4gICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgIHt7IG9wdC5sYWJlbCB9fVxyXG4gICAgICAgICAgICAgICAgPC9tYXQtcmFkaW8tYnV0dG9uPlxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgPC9tYXQtcmFkaW8tZ3JvdXA+XHJcbiAgICAgICAgICAgIEBpZiAoc2hvdWxkU2hvd0ZpZWxkRXJyb3JzKGZpZWxkLmtleSkpIHtcclxuICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19maWVsZC1lcnJvclwiPlxyXG4gICAgICAgICAgICAgICAgQGZvciAoZXJyIG9mIGdldEZpZWxkRXJyb3JzKGZpZWxkLmtleSk7IHRyYWNrICRpbmRleCkge1xyXG4gICAgICAgICAgICAgICAgICA8c3Bhbj57eyBlcnIgfX08L3NwYW4+XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgPCEtLSBEQVRFIC0tPlxyXG4gICAgICAgIEBjYXNlICgnZGF0ZScpIHtcclxuICAgICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19mdWxsLXdpZHRoXCI+XHJcbiAgICAgICAgICAgIDxtYXQtbGFiZWw+e3sgZmllbGQubGFiZWwgfX08L21hdC1sYWJlbD5cclxuICAgICAgICAgICAgPGlucHV0IG1hdElucHV0IFttYXREYXRlcGlja2VyXT1cImRhdGVwaWNrZXJcIiBbZm9ybUNvbnRyb2xOYW1lXT1cImZpZWxkLmtleVwiXHJcbiAgICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICdHRy9NTS9BQUFBJ1wiXHJcbiAgICAgICAgICAgICAgW3JlYWRvbmx5XT1cImZpZWxkLnJlYWRvbmx5IHx8IHJlYWRvbmx5XCJcclxuICAgICAgICAgICAgLz5cclxuICAgICAgICAgICAgPG1hdC1kYXRlcGlja2VyLXRvZ2dsZSBtYXRTdWZmaXggW2Zvcl09XCJkYXRlcGlja2VyXCIgLz5cclxuICAgICAgICAgICAgPG1hdC1kYXRlcGlja2VyICNkYXRlcGlja2VyXHJcbiAgICAgICAgICAgICAgW3N0YXJ0Vmlld109XCJmaWVsZC5jdXN0b21Db25maWc/LmNvbmZpZz8uWydtb250aFZpZXcnXSA/ICd5ZWFyJyA6ICdtb250aCdcIlxyXG4gICAgICAgICAgICAvPlxyXG4gICAgICAgICAgICA8bWF0LWVycm9yPlxyXG4gICAgICAgICAgICAgIEBmb3IgKGVyciBvZiBnZXRGaWVsZEVycm9ycyhmaWVsZC5rZXkpOyB0cmFjayAkaW5kZXgpIHtcclxuICAgICAgICAgICAgICAgIDxkaXY+e3sgZXJyIH19PC9kaXY+XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA8L21hdC1lcnJvcj5cclxuICAgICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICA8IS0tIERBVEVUSU1FIC0tPlxyXG4gICAgICAgIEBjYXNlICgnZGF0ZXRpbWUnKSB7XHJcbiAgICAgICAgICA8bWF0LWZvcm0tZmllbGQgYXBwZWFyYW5jZT1cIm91dGxpbmVcIiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZnVsbC13aWR0aFwiPlxyXG4gICAgICAgICAgICA8bWF0LWxhYmVsPnt7IGZpZWxkLmxhYmVsIH19PC9tYXQtbGFiZWw+XHJcbiAgICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCB0eXBlPVwiZGF0ZXRpbWUtbG9jYWxcIiBbZm9ybUNvbnRyb2xOYW1lXT1cImZpZWxkLmtleVwiXHJcbiAgICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnXCJcclxuICAgICAgICAgICAgICBbcmVhZG9ubHldPVwiZmllbGQucmVhZG9ubHkgfHwgcmVhZG9ubHlcIlxyXG4gICAgICAgICAgICAvPlxyXG4gICAgICAgICAgICA8bWF0LWVycm9yPlxyXG4gICAgICAgICAgICAgIEBmb3IgKGVyciBvZiBnZXRGaWVsZEVycm9ycyhmaWVsZC5rZXkpOyB0cmFjayAkaW5kZXgpIHtcclxuICAgICAgICAgICAgICAgIDxkaXY+e3sgZXJyIH19PC9kaXY+XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA8L21hdC1lcnJvcj5cclxuICAgICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICA8IS0tIEZJTEUgLS0+XHJcbiAgICAgICAgQGNhc2UgKCdmaWxlJykge1xyXG4gICAgICAgICAgPGRpdiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZmlsZS13cmFwcGVyXCI+XHJcbiAgICAgICAgICAgIDxsYWJlbCBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZmllbGQtbGFiZWxcIj5cclxuICAgICAgICAgICAgICB7eyBmaWVsZC5sYWJlbCB9fVxyXG4gICAgICAgICAgICAgIEBpZiAoaXNGaWVsZFJlcXVpcmVkKGZpZWxkLmtleSkpIHsgPHNwYW4gY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX3JlcXVpcmVkXCI+Kjwvc3Bhbj4gfVxyXG4gICAgICAgICAgICA8L2xhYmVsPlxyXG4gICAgICAgICAgICA8dWktZmlsZS1pbnB1dFxyXG4gICAgICAgICAgICAgIFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGQua2V5XCJcclxuICAgICAgICAgICAgICBbY29uZmlnXT1cImZpZWxkLmZpbGVDb25maWdcIlxyXG4gICAgICAgICAgICAvPlxyXG4gICAgICAgICAgICBAaWYgKHNob3VsZFNob3dGaWVsZEVycm9ycyhmaWVsZC5rZXkpKSB7XHJcbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZmllbGQtZXJyb3JcIj5cclxuICAgICAgICAgICAgICAgIEBmb3IgKGVyciBvZiBnZXRGaWVsZEVycm9ycyhmaWVsZC5rZXkpOyB0cmFjayAkaW5kZXgpIHtcclxuICAgICAgICAgICAgICAgICAgPHNwYW4+e3sgZXJyIH19PC9zcGFuPlxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIDwhLS0gRElWSURFUiAtLT5cclxuICAgICAgICBAY2FzZSAoJ2RpdmlkZXInKSB7XHJcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19kaXZpZGVyXCI+XHJcbiAgICAgICAgICAgIEBpZiAoZmllbGQubGFiZWwpIHtcclxuICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZGl2aWRlci1sYWJlbFwiPnt7IGZpZWxkLmxhYmVsIH19PC9zcGFuPlxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIDxociBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZGl2aWRlci1saW5lXCIgLz5cclxuICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgPCEtLSBMT0NBVElPTiAoc2VsZXppb25lIHRlcnJpdG9yaW8pIC0tPlxyXG4gICAgICAgIEBjYXNlICgnbG9jYXRpb24nKSB7XHJcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19sb2NhdGlvbi13cmFwcGVyXCI+XHJcbiAgICAgICAgICAgIEBpZiAoZmllbGQubGFiZWwpIHtcclxuICAgICAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2ZpZWxkLWxhYmVsXCI+XHJcbiAgICAgICAgICAgICAgICB7eyBmaWVsZC5sYWJlbCB9fVxyXG4gICAgICAgICAgICAgICAgQGlmIChpc0ZpZWxkUmVxdWlyZWQoZmllbGQua2V5KSkgeyA8c3BhbiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fcmVxdWlyZWRcIj4qPC9zcGFuPiB9XHJcbiAgICAgICAgICAgICAgPC9sYWJlbD5cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA8dWktc3BlY2lmaWNhLXRlcnJpdG9yaWFsZVxyXG4gICAgICAgICAgICAgIFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGQua2V5XCJcclxuICAgICAgICAgICAgICBbY29uZmlnXT1cImZpZWxkLmN1c3RvbUNvbmZpZz8uY29uZmlnIHx8IHt9XCJcclxuICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwicmVhZG9ubHkgfHwgZmllbGQuZGlzYWJsZWQgfHwgZGlzYWJsZWRcIlxyXG4gICAgICAgICAgICAvPlxyXG4gICAgICAgICAgICBAaWYgKHNob3VsZFNob3dGaWVsZEVycm9ycyhmaWVsZC5rZXkpKSB7XHJcbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZmllbGQtZXJyb3JcIj5cclxuICAgICAgICAgICAgICAgIEBmb3IgKGVyciBvZiBnZXRGaWVsZEVycm9ycyhmaWVsZC5rZXkpOyB0cmFjayAkaW5kZXgpIHtcclxuICAgICAgICAgICAgICAgICAgPHNwYW4+e3sgZXJyIH19PC9zcGFuPlxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIDwhLS0gTE9DQVRJT04tVEFCTEUgKHRhYmVsbGEgQ1JVRCBkaSBsb2NhdGlvbikgLS0+XHJcbiAgICAgICAgQGNhc2UgKCdsb2NhdGlvbi10YWJsZScpIHtcclxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2xvY2F0aW9uLXRhYmxlLXdyYXBwZXJcIj5cclxuICAgICAgICAgICAgPHVpLXRhYmxlLXRlcnJpdG9yaWFsZVxyXG4gICAgICAgICAgICAgIFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGQua2V5XCJcclxuICAgICAgICAgICAgICBbY29uZmlnXT1cImZpZWxkLmN1c3RvbUNvbmZpZz8uY29uZmlnIHx8IHt9XCJcclxuICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwicmVhZG9ubHkgfHwgZmllbGQuZGlzYWJsZWQgfHwgZGlzYWJsZWRcIlxyXG4gICAgICAgICAgICAvPlxyXG4gICAgICAgICAgICBAaWYgKHNob3VsZFNob3dGaWVsZEVycm9ycyhmaWVsZC5rZXkpKSB7XHJcbiAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZmllbGQtZXJyb3JcIj5cclxuICAgICAgICAgICAgICAgIEBmb3IgKGVyciBvZiBnZXRGaWVsZEVycm9ycyhmaWVsZC5rZXkpOyB0cmFjayAkaW5kZXgpIHtcclxuICAgICAgICAgICAgICAgICAgPHNwYW4+e3sgZXJyIH19PC9zcGFuPlxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIDwhLS0gQ1VTVE9NIC0tPlxyXG4gICAgICAgIEBjYXNlICgnY3VzdG9tJykge1xyXG4gICAgICAgICAgPGRpdiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fY3VzdG9tLXdyYXBwZXJcIiBbYXR0ci5kYXRhLWNvbXBvbmVudF09XCJmaWVsZC5jdXN0b21Db25maWc/LmNvbXBvbmVudFwiPlxyXG4gICAgICAgICAgICBAaWYgKGZpZWxkLmxhYmVsKSB7XHJcbiAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19maWVsZC1sYWJlbFwiPnt7IGZpZWxkLmxhYmVsIH19PC9sYWJlbD5cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA8IS0tIEkgY29tcG9uZW50aSBjdXN0b20gZGV2b25vIGVzc2VyZSBwcm9pZXR0YXRpIGRhbGwnYXBwbGljYXppb25lIGhvc3QgLS0+XHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2N1c3RvbS1wbGFjZWhvbGRlclwiPlxyXG4gICAgICAgICAgICAgIDxzcGFuPkNvbXBvbmVudGUgY3VzdG9tOiB7eyBmaWVsZC5jdXN0b21Db25maWc/LmNvbXBvbmVudCB9fTwvc3Bhbj5cclxuICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIDwvZGl2PlxyXG4gIH1cclxuPC9uZy10ZW1wbGF0ZT5cclxuXHJcbjwhLS0gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IC0tPlxyXG48IS0tIFRFWFQgLyBFTUFJTCAvIFBBU1NXT1JEIHRlbXBsYXRlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPlxyXG48IS0tID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAtLT5cclxuPCEtLVxyXG4gIENSSVRJQ086IEFuY2hlIHF1ZXN0byB0ZW1wbGF0ZSBuZWNlc3NpdGEgZGVsIHdyYXBwZXIgW2Zvcm1Hcm91cF0uXHJcbiAgVmllbmUgaW52b2NhdG8gZGEgZmllbGRUcGwgY2hlIGdpYSBoYSBpbCB3cmFwcGVyLCBtYSBzaWNjb21lXHJcbiAgcXVlc3RvIGUgdW4gU0VDT05ETyBuZy10ZW1wbGF0ZSwgQW5ndWxhciBjcmVhIHVuIE5VT1ZPIGNvbnRlc3RvXHJcbiAgZGkgZGljaGlhcmF6aW9uZS4gSWwgd3JhcHBlciBkaSBmaWVsZFRwbCBub24gZSBzdWZmaWNpZW50ZS5cclxuLS0+XHJcbjxuZy10ZW1wbGF0ZSAjdGV4dEZpZWxkVHBsIGxldC1maWVsZD5cclxuICBAaWYgKGZvcm1Hcm91cCkge1xyXG4gICAgPGRpdiBbZm9ybUdyb3VwXT1cImZvcm1Hcm91cFwiIGNsYXNzPVwidWktZmItdGV4dC1jdHhcIj5cclxuICAgICAgQGlmIChmaWVsZC5zZWFyY2hhYmxlICYmIGZpZWxkLnR5cGUgPT09ICd0ZXh0Jykge1xyXG4gICAgICAgIDwhLS0gQXV0b2NvbXBsZXRlIHRleHQgLS0+XHJcbiAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgY2xhc3M9XCJ1aS1mb3JtLWJ1aWxkZXJfX2Z1bGwtd2lkdGhcIj5cclxuICAgICAgICAgIDxtYXQtbGFiZWw+e3sgZmllbGQubGFiZWwgfX08L21hdC1sYWJlbD5cclxuICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCBbZm9ybUNvbnRyb2xOYW1lXT1cImZpZWxkLmtleVwiXHJcbiAgICAgICAgICAgIFt0eXBlXT1cImdldE5hdGl2ZUlucHV0VHlwZShmaWVsZClcIlxyXG4gICAgICAgICAgICBbbWF0QXV0b2NvbXBsZXRlXT1cInRleHRBdXRvXCJcclxuICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnXCJcclxuICAgICAgICAgICAgW3JlYWRvbmx5XT1cImZpZWxkLnJlYWRvbmx5IHx8IHJlYWRvbmx5XCJcclxuICAgICAgICAgICAgKGlucHV0KT1cImZpbHRlck9wdGlvbnMoZmllbGQsICRhbnkoJGV2ZW50LnRhcmdldCkudmFsdWUpXCJcclxuICAgICAgICAgIC8+XHJcbiAgICAgICAgICA8bWF0LWF1dG9jb21wbGV0ZSAjdGV4dEF1dG89XCJtYXRBdXRvY29tcGxldGVcIj5cclxuICAgICAgICAgICAgQGZvciAob3B0IG9mIGdldEZpZWxkT3B0aW9ucyhmaWVsZCk7IHRyYWNrIG9wdC52YWx1ZSkge1xyXG4gICAgICAgICAgICAgIDxtYXQtb3B0aW9uIFt2YWx1ZV09XCJvcHQudmFsdWVcIj57eyBvcHQubGFiZWwgfX08L21hdC1vcHRpb24+XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIDwvbWF0LWF1dG9jb21wbGV0ZT5cclxuICAgICAgICAgIDxtYXQtZXJyb3I+XHJcbiAgICAgICAgICAgIEBmb3IgKGVyciBvZiBnZXRGaWVsZEVycm9ycyhmaWVsZC5rZXkpOyB0cmFjayAkaW5kZXgpIHtcclxuICAgICAgICAgICAgICA8ZGl2Pnt7IGVyciB9fTwvZGl2PlxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICA8L21hdC1lcnJvcj5cclxuICAgICAgICA8L21hdC1mb3JtLWZpZWxkPlxyXG4gICAgICB9IEBlbHNlIHtcclxuICAgICAgICA8bWF0LWZvcm0tZmllbGQgYXBwZWFyYW5jZT1cIm91dGxpbmVcIiBjbGFzcz1cInVpLWZvcm0tYnVpbGRlcl9fZnVsbC13aWR0aFwiPlxyXG4gICAgICAgICAgPG1hdC1sYWJlbD57eyBmaWVsZC5sYWJlbCB9fTwvbWF0LWxhYmVsPlxyXG4gICAgICAgICAgPGlucHV0IG1hdElucHV0IFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGQua2V5XCJcclxuICAgICAgICAgICAgW3R5cGVdPVwiZ2V0TmF0aXZlSW5wdXRUeXBlKGZpZWxkKVwiXHJcbiAgICAgICAgICAgIFtwbGFjZWhvbGRlcl09XCJmaWVsZC5wbGFjZWhvbGRlciB8fCAnJ1wiXHJcbiAgICAgICAgICAgIFtyZWFkb25seV09XCJmaWVsZC5yZWFkb25seSB8fCByZWFkb25seVwiXHJcbiAgICAgICAgICAvPlxyXG4gICAgICAgICAgQGlmIChmaWVsZC5pY29uVG9vbHRpcCkge1xyXG4gICAgICAgICAgICA8YnV0dG9uIG1hdFN1ZmZpeCBtYXQtaWNvbi1idXR0b24gdHlwZT1cImJ1dHRvblwiXHJcbiAgICAgICAgICAgICAgW21hdFRvb2x0aXBdPVwiZmllbGQuaWNvblRvb2x0aXAudGV4dFwiIFthdHRyLmFyaWEtbGFiZWxdPVwiZmllbGQuaWNvblRvb2x0aXAudGV4dFwiPlxyXG4gICAgICAgICAgICAgIDxsdWNpZGUtaWNvbiBbbmFtZV09XCJmaWVsZC5pY29uVG9vbHRpcC5pY29uIHx8ICdpbmZvJ1wiIFtzaXplXT1cIjE4XCIgLz5cclxuICAgICAgICAgICAgPC9idXR0b24+XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICA8bWF0LWVycm9yPlxyXG4gICAgICAgICAgICBAZm9yIChlcnIgb2YgZ2V0RmllbGRFcnJvcnMoZmllbGQua2V5KTsgdHJhY2sgJGluZGV4KSB7XHJcbiAgICAgICAgICAgICAgPGRpdj57eyBlcnIgfX08L2Rpdj5cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgPC9tYXQtZXJyb3I+XHJcbiAgICAgICAgPC9tYXQtZm9ybS1maWVsZD5cclxuICAgICAgICBAaWYgKGZpZWxkLnR5cGUgPT09ICd0ZXh0JyAmJiBnZXRDaGFyQ291bnQoZmllbGQua2V5KTsgYXMgY2MpIHtcclxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwidWktZm9ybS1idWlsZGVyX19jaGFyLWNvdW50XCJcclxuICAgICAgICAgICAgW2NsYXNzLnVpLWZvcm0tYnVpbGRlcl9fY2hhci1jb3VudC0td2Fybl09XCJjYy5jdXJyZW50ID4gY2MubWF4ICogMC45XCJcclxuICAgICAgICAgICAgW2NsYXNzLnVpLWZvcm0tYnVpbGRlcl9fY2hhci1jb3VudC0tZXJyb3JdPVwiY2MuY3VycmVudCA+IGNjLm1heFwiPlxyXG4gICAgICAgICAgICB7eyBjYy5jdXJyZW50IH19IC8ge3sgY2MubWF4IH19XHJcbiAgICAgICAgICA8L3NwYW4+XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICA8L2Rpdj5cclxuICB9XHJcbjwvbmctdGVtcGxhdGU+XHJcbiJdfQ==
|