ngx-dynamic-entity-builder 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/esm2022/lib/builder-store.service.mjs +923 -0
- package/esm2022/lib/clone.mjs +14 -0
- package/esm2022/lib/components/connection-source-config.component.mjs +121 -0
- package/esm2022/lib/components/entity-builder-canvas.component.mjs +83 -0
- package/esm2022/lib/components/entity-builder-tree-node.component.mjs +173 -0
- package/esm2022/lib/components/entity-reference-config.component.mjs +405 -0
- package/esm2022/lib/components/field-inspector.component.mjs +139 -0
- package/esm2022/lib/components/field-palette.component.mjs +57 -0
- package/esm2022/lib/components/field-rules-list.component.mjs +224 -0
- package/esm2022/lib/components/referenced-field-config.component.mjs +228 -0
- package/esm2022/lib/components/rule-form.component.mjs +201 -0
- package/esm2022/lib/components/tab-manager.component.mjs +341 -0
- package/esm2022/lib/entity-builder.component.mjs +153 -0
- package/esm2022/lib/field-catalog.mjs +6 -0
- package/esm2022/ngx-dynamic-entity-builder.mjs +5 -0
- package/esm2022/public-api.mjs +17 -0
- package/fesm2022/ngx-dynamic-entity-builder.mjs +2910 -0
- package/fesm2022/ngx-dynamic-entity-builder.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/builder-store.service.d.ts +165 -0
- package/lib/clone.d.ts +9 -0
- package/lib/components/connection-source-config.component.d.ts +16 -0
- package/lib/components/entity-builder-canvas.component.d.ts +9 -0
- package/lib/components/entity-builder-tree-node.component.d.ts +17 -0
- package/lib/components/entity-reference-config.component.d.ts +29 -0
- package/lib/components/field-inspector.component.d.ts +33 -0
- package/lib/components/field-palette.component.d.ts +13 -0
- package/lib/components/field-rules-list.component.d.ts +19 -0
- package/lib/components/referenced-field-config.component.d.ts +18 -0
- package/lib/components/rule-form.component.d.ts +16 -0
- package/lib/components/tab-manager.component.d.ts +19 -0
- package/lib/entity-builder.component.d.ts +42 -0
- package/lib/field-catalog.d.ts +5 -0
- package/package.json +42 -0
- package/public-api.d.ts +13 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Component, computed, inject } from '@angular/core';
|
|
2
|
+
import { FormsModule } from '@angular/forms';
|
|
3
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
4
|
+
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
5
|
+
import { MatDividerModule } from '@angular/material/divider';
|
|
6
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
7
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
8
|
+
import { MatInputModule } from '@angular/material/input';
|
|
9
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
10
|
+
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
11
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
12
|
+
import { resolveLabel, resolveOptionLabel } from '@dynamic-entity/core';
|
|
13
|
+
import { BuilderStore } from '../builder-store.service';
|
|
14
|
+
import { getFieldTypeMeta } from '../field-catalog';
|
|
15
|
+
import { EntityReferenceConfigComponent } from './entity-reference-config.component';
|
|
16
|
+
import { FieldRulesListComponent } from './field-rules-list.component';
|
|
17
|
+
import { ReferencedFieldConfigComponent } from './referenced-field-config.component';
|
|
18
|
+
import * as i0 from "@angular/core";
|
|
19
|
+
import * as i1 from "@angular/forms";
|
|
20
|
+
import * as i2 from "@angular/material/button";
|
|
21
|
+
import * as i3 from "@angular/material/checkbox";
|
|
22
|
+
import * as i4 from "@angular/material/divider";
|
|
23
|
+
import * as i5 from "@angular/material/form-field";
|
|
24
|
+
import * as i6 from "@angular/material/icon";
|
|
25
|
+
import * as i7 from "@angular/material/input";
|
|
26
|
+
import * as i8 from "@angular/material/select";
|
|
27
|
+
import * as i9 from "@angular/material/core";
|
|
28
|
+
import * as i10 from "@angular/material/slide-toggle";
|
|
29
|
+
import * as i11 from "@angular/material/tooltip";
|
|
30
|
+
/**
|
|
31
|
+
* FieldInspectorComponent — edits every property of the currently selected field.
|
|
32
|
+
*/
|
|
33
|
+
export class FieldInspectorComponent {
|
|
34
|
+
store = inject(BuilderStore);
|
|
35
|
+
field = this.store.selectedField;
|
|
36
|
+
meta = computed(() => {
|
|
37
|
+
const f = this.field();
|
|
38
|
+
return f ? getFieldTypeMeta(f.type) : undefined;
|
|
39
|
+
});
|
|
40
|
+
lang() {
|
|
41
|
+
return this.store.activeLanguage();
|
|
42
|
+
}
|
|
43
|
+
toNum(value) {
|
|
44
|
+
if (value === '' || value === null || value === undefined)
|
|
45
|
+
return null;
|
|
46
|
+
const n = Number(value);
|
|
47
|
+
return Number.isNaN(n) ? null : n;
|
|
48
|
+
}
|
|
49
|
+
labelValue(field) {
|
|
50
|
+
return resolveLabel(field.label, this.lang());
|
|
51
|
+
}
|
|
52
|
+
placeholderValue(field) {
|
|
53
|
+
return resolveLabel(field.placeholder, this.lang());
|
|
54
|
+
}
|
|
55
|
+
optionLabel(option) {
|
|
56
|
+
return resolveOptionLabel(option, this.lang());
|
|
57
|
+
}
|
|
58
|
+
// ─── showWhen editing ───────────────────────────────────────────────────────
|
|
59
|
+
showWhenEntries(field) {
|
|
60
|
+
return Object.entries(field.showWhen ?? {}).map(([key, value]) => ({
|
|
61
|
+
key,
|
|
62
|
+
display: this.stringifyShowWhen(value),
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
addShowWhen(field) {
|
|
66
|
+
const next = { ...(field.showWhen ?? {}) };
|
|
67
|
+
let key = 'field';
|
|
68
|
+
let n = 1;
|
|
69
|
+
while (key in next)
|
|
70
|
+
key = `field_${++n}`;
|
|
71
|
+
next[key] = true;
|
|
72
|
+
this.store.setShowWhen(field.id, next);
|
|
73
|
+
}
|
|
74
|
+
renameShowWhen(field, oldKey, newKey) {
|
|
75
|
+
const trimmed = newKey.trim();
|
|
76
|
+
if (!trimmed || trimmed === oldKey)
|
|
77
|
+
return;
|
|
78
|
+
const current = field.showWhen ?? {};
|
|
79
|
+
if (trimmed in current)
|
|
80
|
+
return;
|
|
81
|
+
const next = {};
|
|
82
|
+
// Rebuild in order so the row does not jump while the user is typing.
|
|
83
|
+
for (const [k, v] of Object.entries(current))
|
|
84
|
+
next[k === oldKey ? trimmed : k] = v;
|
|
85
|
+
this.store.setShowWhen(field.id, next);
|
|
86
|
+
}
|
|
87
|
+
setShowWhenValue(field, key, raw) {
|
|
88
|
+
this.store.setShowWhen(field.id, { ...(field.showWhen ?? {}), [key]: this.parseShowWhen(raw) });
|
|
89
|
+
}
|
|
90
|
+
removeShowWhen(field, key) {
|
|
91
|
+
const next = { ...(field.showWhen ?? {}) };
|
|
92
|
+
delete next[key];
|
|
93
|
+
this.store.setShowWhen(field.id, next);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* `showWhen` compares with `!==`, so the authored type has to survive the round trip:
|
|
97
|
+
* "true"/"false" become booleans and numeric text becomes a number.
|
|
98
|
+
*/
|
|
99
|
+
parseShowWhen(raw) {
|
|
100
|
+
const trimmed = raw.trim();
|
|
101
|
+
if (trimmed === 'true')
|
|
102
|
+
return true;
|
|
103
|
+
if (trimmed === 'false')
|
|
104
|
+
return false;
|
|
105
|
+
if (trimmed === 'null')
|
|
106
|
+
return null;
|
|
107
|
+
if (trimmed !== '' && !Number.isNaN(Number(trimmed)))
|
|
108
|
+
return Number(trimmed);
|
|
109
|
+
return raw;
|
|
110
|
+
}
|
|
111
|
+
stringifyShowWhen(value) {
|
|
112
|
+
if (value === null)
|
|
113
|
+
return 'null';
|
|
114
|
+
if (typeof value === 'object')
|
|
115
|
+
return JSON.stringify(value);
|
|
116
|
+
return String(value);
|
|
117
|
+
}
|
|
118
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
119
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FieldInspectorComponent, isStandalone: true, selector: "ngx-field-inspector", ngImport: i0, template: "@if (field(); as f) {\r\n <div class=\"deb-inspector\">\r\n <!-- Identity -->\r\n <div class=\"deb-row\">\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\r\n <mat-label>Field id</mat-label>\r\n <input matInput data-testid=\"field-id\" [value]=\"f.id\" disabled />\r\n @if (store.hasManualId(f.id)) {\r\n <mat-hint>The stored data key for this saved field \u2014 fixed.</mat-hint>\r\n } @else {\r\n <mat-hint>Derived from the label. Used as the data key.</mat-hint>\r\n }\r\n </mat-form-field>\r\n <span class=\"deb-chip\">{{ meta()?.label || f.type }}</span>\r\n </div>\r\n\r\n <!-- Localised text (active language) -->\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>Label ({{ lang() }})</mat-label>\r\n <input\r\n matInput\r\n [ngModel]=\"labelValue(f)\"\r\n (ngModelChange)=\"store.setFieldLabel(f.id, lang(), $event)\"\r\n />\r\n </mat-form-field>\r\n\r\n @if (meta()?.supportsPlaceholder) {\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>Placeholder ({{ lang() }})</mat-label>\r\n <input\r\n matInput\r\n [ngModel]=\"placeholderValue(f)\"\r\n (ngModelChange)=\"store.setFieldPlaceholder(f.id, lang(), $event)\"\r\n />\r\n </mat-form-field>\r\n }\r\n\r\n @if (meta()?.supportsDefaultValue) {\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>Default value</mat-label>\r\n <input\r\n matInput\r\n [ngModel]=\"f.defaultValue ?? ''\"\r\n (ngModelChange)=\"store.updateField(f.id, { defaultValue: $event })\"\r\n />\r\n </mat-form-field>\r\n }\r\n\r\n <!-- Entity reference, cascade, and autoPatch -->\r\n @if (meta()?.isEntityRef) {\r\n <mat-divider></mat-divider>\r\n <ngx-entity-reference-config />\r\n }\r\n\r\n <mat-divider></mat-divider>\r\n\r\n <!-- Validators -->\r\n @if (meta(); as m) {\r\n @if (m.flagValidators.length || m.paramValidators.length) {\r\n <span class=\"deb-section-title\">Validation</span>\r\n <div class=\"deb-toggles\">\r\n @for (v of m.flagValidators; track v) {\r\n <mat-checkbox\r\n [ngModel]=\"store.hasFlagValidator(f, v)\"\r\n (ngModelChange)=\"store.toggleFlagValidator(f.id, v, $event)\"\r\n >{{ v }}</mat-checkbox>\r\n }\r\n </div>\r\n @for (v of m.paramValidators; track v) {\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>{{ v }}</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [ngModel]=\"store.getParamValidator(f, v)\"\r\n (ngModelChange)=\"store.setParamValidator(f.id, v, toNum($event))\"\r\n />\r\n </mat-form-field>\r\n }\r\n }\r\n }\r\n\r\n <!-- Options (dropdown / radio / multiSelect) -->\r\n @if (meta()?.hasOptions) {\r\n <mat-divider></mat-divider>\r\n <span class=\"deb-section-title\">Data source</span>\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>Options come from</mat-label>\r\n <mat-select\r\n data-testid=\"data-source\"\r\n [ngModel]=\"store.fieldDataSource(f)\"\r\n (ngModelChange)=\"store.setFieldDataSource(f.id, $event)\"\r\n >\r\n <mat-option value=\"none\">Nothing yet</mat-option>\r\n <mat-option value=\"manual\">Options authored here</mat-option>\r\n <mat-option value=\"lookup\">A named list</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- Named lookup list -->\r\n @if (meta()?.hasOptions && store.fieldDataSource(f) === 'lookup') {\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>List name</mat-label>\r\n <input\r\n matInput\r\n data-testid=\"list-name\"\r\n [ngModel]=\"f.listName ?? ''\"\r\n (ngModelChange)=\"store.setListName(f.id, $event)\"\r\n />\r\n </mat-form-field>\r\n <p class=\"deb-hint\">\r\n The consuming app resolves this name through its lookup registry. Options are not\r\n authored here.\r\n </p>\r\n }\r\n\r\n <!-- Inline options -->\r\n @if (meta()?.hasOptions && store.fieldDataSource(f) !== 'lookup') {\r\n <div class=\"deb-row\" style=\"justify-content: space-between;\">\r\n <span class=\"deb-section-title\">Options</span>\r\n <button mat-stroked-button type=\"button\" (click)=\"store.addOption(f.id)\">\r\n <mat-icon>add</mat-icon> Option\r\n </button>\r\n </div>\r\n @for (opt of f.options ?? []; track $index) {\r\n <div class=\"deb-option-row\" data-testid=\"option-row\">\r\n <!-- One input: the displayed text is the stored value. -->\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>Option ({{ lang() }})</mat-label>\r\n <input\r\n matInput\r\n [attr.data-testid]=\"'option-' + $index\"\r\n [ngModel]=\"optionLabel(opt)\"\r\n (ngModelChange)=\"store.setOptionLabel(f.id, $index, lang(), $event)\"\r\n />\r\n </mat-form-field>\r\n <button mat-icon-button type=\"button\" color=\"warn\"\r\n (click)=\"store.removeOption(f.id, $index)\" matTooltip=\"Remove option\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n @if (!(f.options ?? []).length) {\r\n <p class=\"deb-hint\">No options yet \u2014 add at least one.</p>\r\n }\r\n }\r\n\r\n <mat-divider></mat-divider>\r\n\r\n <!-- Flags -->\r\n <span class=\"deb-section-title\">Display</span>\r\n <div class=\"deb-toggles\">\r\n <mat-slide-toggle\r\n [ngModel]=\"f.visibility ?? true\"\r\n (ngModelChange)=\"store.updateField(f.id, { visibility: $event })\"\r\n >Visible</mat-slide-toggle>\r\n <mat-slide-toggle\r\n [ngModel]=\"f.table?.visible ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { table: { visible: $event } })\"\r\n >Table column</mat-slide-toggle>\r\n <mat-slide-toggle\r\n [ngModel]=\"f.readonly ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { readonly: $event })\"\r\n >Read-only</mat-slide-toggle>\r\n <mat-slide-toggle\r\n [ngModel]=\"f.disabled ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { disabled: $event })\"\r\n >Disabled</mat-slide-toggle>\r\n <mat-slide-toggle\r\n [ngModel]=\"f.maskData ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { maskData: $event })\"\r\n >Mask data (RBAC)</mat-slide-toggle>\r\n <mat-slide-toggle\r\n data-testid=\"toggle-critical\"\r\n [ngModel]=\"f.criticalField ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { criticalField: $event })\"\r\n matTooltip=\"Renders locked; editing it announces a change against the session baseline.\"\r\n >Critical field</mat-slide-toggle>\r\n <mat-slide-toggle\r\n [ngModel]=\"f.showOnMinimize ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { showOnMinimize: $event })\"\r\n matTooltip=\"Shown in the record editor's summary panel.\"\r\n >Show in summary</mat-slide-toggle>\r\n </div>\r\n\r\n <mat-divider></mat-divider>\r\n\r\n <!-- Conditional visibility (static showWhen) -->\r\n <div class=\"deb-row deb-row--split\">\r\n <span class=\"deb-section-title\">Show when</span>\r\n <button mat-stroked-button type=\"button\" data-testid=\"add-show-when\" (click)=\"addShowWhen(f)\">\r\n <mat-icon>add</mat-icon> Condition\r\n </button>\r\n </div>\r\n @for (entry of showWhenEntries(f); track entry.key) {\r\n <div class=\"deb-option-row\" data-testid=\"option-row\">\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\r\n <mat-label>Field id</mat-label>\r\n <input matInput [ngModel]=\"entry.key\" (ngModelChange)=\"renameShowWhen(f, entry.key, $event)\" />\r\n </mat-form-field>\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\r\n <mat-label>Equals</mat-label>\r\n <input matInput [ngModel]=\"entry.display\" (ngModelChange)=\"setShowWhenValue(f, entry.key, $event)\" />\r\n </mat-form-field>\r\n <button mat-icon-button type=\"button\" color=\"warn\" matTooltip=\"Remove condition\"\r\n (click)=\"removeShowWhen(f, entry.key)\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n @if (!showWhenEntries(f).length) {\r\n <p class=\"deb-hint\">Always visible. Add a condition to show this field only for certain values.</p>\r\n }\r\n\r\n <!-- patchOnTrue (boolean/checkbox only) -->\r\n @if (f.type === 'boolean' || f.type === 'checkbox') {\r\n <mat-divider></mat-divider>\r\n <div class=\"deb-row deb-row--split\">\r\n <span class=\"deb-section-title\">Copy on true</span>\r\n <button mat-stroked-button type=\"button\" data-testid=\"add-patch-on-true\"\r\n (click)=\"store.addPatchOnTrueMapping(f.id)\">\r\n <mat-icon>add</mat-icon> Mapping\r\n </button>\r\n </div>\r\n @for (mapping of f.patchOnTrue ?? []; track $index) {\r\n <div class=\"deb-option-row\" data-testid=\"option-row\">\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\r\n <mat-label>From field</mat-label>\r\n <input matInput [ngModel]=\"mapping.from\"\r\n (ngModelChange)=\"store.updatePatchOnTrueMapping(f.id, $index, { from: $event })\" />\r\n </mat-form-field>\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\r\n <mat-label>To field</mat-label>\r\n <input matInput [ngModel]=\"mapping.to\"\r\n (ngModelChange)=\"store.updatePatchOnTrueMapping(f.id, $index, { to: $event })\" />\r\n </mat-form-field>\r\n <button mat-icon-button type=\"button\" color=\"warn\" matTooltip=\"Remove mapping\"\r\n (click)=\"store.removePatchOnTrueMapping(f.id, $index)\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n @if (!(f.patchOnTrue ?? []).length) {\r\n <p class=\"deb-hint\">When this flips to true, copy one field's value into another.</p>\r\n }\r\n }\r\n\r\n <mat-divider></mat-divider>\r\n\r\n <!-- Rules attached to this field -->\r\n <ngx-field-rules-list />\r\n\r\n <mat-divider></mat-divider>\r\n\r\n <!-- Cross-entity field reference & drift management (Phase 8) -->\r\n <ngx-referenced-field-config />\r\n </div>\r\n} @else {\r\n <div class=\"deb-inspector__empty\">\r\n <mat-icon>touch_app</mat-icon>\r\n <p>Select a field to edit its properties.</p>\r\n </div>\r\n}\r\n", styles: [".deb-inspector{display:flex;flex-direction:column;gap:12px}.deb-inspector__empty{color:var(--deb-muted, #6b7280);text-align:center;padding:24px 8px}.deb-full{width:100%}.deb-row{display:flex;gap:8px;align-items:center}.deb-row>*{flex:1}.deb-row--split{justify-content:space-between}.deb-row--split>*{flex:0 0 auto}.deb-toggles{display:grid;grid-template-columns:1fr 1fr;gap:8px 12px}.deb-option-row{display:flex;gap:6px;align-items:center}.deb-chip{display:inline-block;padding:2px 8px;border-radius:12px;background:var(--deb-accent-soft, #eef2ff);color:var(--deb-accent, #6366f1);font-size:12px;font-weight:600}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i4.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.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: i8.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: i9.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i10.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: EntityReferenceConfigComponent, selector: "ngx-entity-reference-config" }, { kind: "component", type: FieldRulesListComponent, selector: "ngx-field-rules-list" }, { kind: "component", type: ReferencedFieldConfigComponent, selector: "ngx-referenced-field-config" }] });
|
|
120
|
+
}
|
|
121
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldInspectorComponent, decorators: [{
|
|
122
|
+
type: Component,
|
|
123
|
+
args: [{ selector: 'ngx-field-inspector', standalone: true, imports: [
|
|
124
|
+
FormsModule,
|
|
125
|
+
MatButtonModule,
|
|
126
|
+
MatCheckboxModule,
|
|
127
|
+
MatDividerModule,
|
|
128
|
+
MatFormFieldModule,
|
|
129
|
+
MatIconModule,
|
|
130
|
+
MatInputModule,
|
|
131
|
+
MatSelectModule,
|
|
132
|
+
MatSlideToggleModule,
|
|
133
|
+
MatTooltipModule,
|
|
134
|
+
EntityReferenceConfigComponent,
|
|
135
|
+
FieldRulesListComponent,
|
|
136
|
+
ReferencedFieldConfigComponent,
|
|
137
|
+
], template: "@if (field(); as f) {\r\n <div class=\"deb-inspector\">\r\n <!-- Identity -->\r\n <div class=\"deb-row\">\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\r\n <mat-label>Field id</mat-label>\r\n <input matInput data-testid=\"field-id\" [value]=\"f.id\" disabled />\r\n @if (store.hasManualId(f.id)) {\r\n <mat-hint>The stored data key for this saved field \u2014 fixed.</mat-hint>\r\n } @else {\r\n <mat-hint>Derived from the label. Used as the data key.</mat-hint>\r\n }\r\n </mat-form-field>\r\n <span class=\"deb-chip\">{{ meta()?.label || f.type }}</span>\r\n </div>\r\n\r\n <!-- Localised text (active language) -->\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>Label ({{ lang() }})</mat-label>\r\n <input\r\n matInput\r\n [ngModel]=\"labelValue(f)\"\r\n (ngModelChange)=\"store.setFieldLabel(f.id, lang(), $event)\"\r\n />\r\n </mat-form-field>\r\n\r\n @if (meta()?.supportsPlaceholder) {\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>Placeholder ({{ lang() }})</mat-label>\r\n <input\r\n matInput\r\n [ngModel]=\"placeholderValue(f)\"\r\n (ngModelChange)=\"store.setFieldPlaceholder(f.id, lang(), $event)\"\r\n />\r\n </mat-form-field>\r\n }\r\n\r\n @if (meta()?.supportsDefaultValue) {\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>Default value</mat-label>\r\n <input\r\n matInput\r\n [ngModel]=\"f.defaultValue ?? ''\"\r\n (ngModelChange)=\"store.updateField(f.id, { defaultValue: $event })\"\r\n />\r\n </mat-form-field>\r\n }\r\n\r\n <!-- Entity reference, cascade, and autoPatch -->\r\n @if (meta()?.isEntityRef) {\r\n <mat-divider></mat-divider>\r\n <ngx-entity-reference-config />\r\n }\r\n\r\n <mat-divider></mat-divider>\r\n\r\n <!-- Validators -->\r\n @if (meta(); as m) {\r\n @if (m.flagValidators.length || m.paramValidators.length) {\r\n <span class=\"deb-section-title\">Validation</span>\r\n <div class=\"deb-toggles\">\r\n @for (v of m.flagValidators; track v) {\r\n <mat-checkbox\r\n [ngModel]=\"store.hasFlagValidator(f, v)\"\r\n (ngModelChange)=\"store.toggleFlagValidator(f.id, v, $event)\"\r\n >{{ v }}</mat-checkbox>\r\n }\r\n </div>\r\n @for (v of m.paramValidators; track v) {\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>{{ v }}</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [ngModel]=\"store.getParamValidator(f, v)\"\r\n (ngModelChange)=\"store.setParamValidator(f.id, v, toNum($event))\"\r\n />\r\n </mat-form-field>\r\n }\r\n }\r\n }\r\n\r\n <!-- Options (dropdown / radio / multiSelect) -->\r\n @if (meta()?.hasOptions) {\r\n <mat-divider></mat-divider>\r\n <span class=\"deb-section-title\">Data source</span>\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>Options come from</mat-label>\r\n <mat-select\r\n data-testid=\"data-source\"\r\n [ngModel]=\"store.fieldDataSource(f)\"\r\n (ngModelChange)=\"store.setFieldDataSource(f.id, $event)\"\r\n >\r\n <mat-option value=\"none\">Nothing yet</mat-option>\r\n <mat-option value=\"manual\">Options authored here</mat-option>\r\n <mat-option value=\"lookup\">A named list</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- Named lookup list -->\r\n @if (meta()?.hasOptions && store.fieldDataSource(f) === 'lookup') {\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>List name</mat-label>\r\n <input\r\n matInput\r\n data-testid=\"list-name\"\r\n [ngModel]=\"f.listName ?? ''\"\r\n (ngModelChange)=\"store.setListName(f.id, $event)\"\r\n />\r\n </mat-form-field>\r\n <p class=\"deb-hint\">\r\n The consuming app resolves this name through its lookup registry. Options are not\r\n authored here.\r\n </p>\r\n }\r\n\r\n <!-- Inline options -->\r\n @if (meta()?.hasOptions && store.fieldDataSource(f) !== 'lookup') {\r\n <div class=\"deb-row\" style=\"justify-content: space-between;\">\r\n <span class=\"deb-section-title\">Options</span>\r\n <button mat-stroked-button type=\"button\" (click)=\"store.addOption(f.id)\">\r\n <mat-icon>add</mat-icon> Option\r\n </button>\r\n </div>\r\n @for (opt of f.options ?? []; track $index) {\r\n <div class=\"deb-option-row\" data-testid=\"option-row\">\r\n <!-- One input: the displayed text is the stored value. -->\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\" class=\"deb-full\">\r\n <mat-label>Option ({{ lang() }})</mat-label>\r\n <input\r\n matInput\r\n [attr.data-testid]=\"'option-' + $index\"\r\n [ngModel]=\"optionLabel(opt)\"\r\n (ngModelChange)=\"store.setOptionLabel(f.id, $index, lang(), $event)\"\r\n />\r\n </mat-form-field>\r\n <button mat-icon-button type=\"button\" color=\"warn\"\r\n (click)=\"store.removeOption(f.id, $index)\" matTooltip=\"Remove option\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n @if (!(f.options ?? []).length) {\r\n <p class=\"deb-hint\">No options yet \u2014 add at least one.</p>\r\n }\r\n }\r\n\r\n <mat-divider></mat-divider>\r\n\r\n <!-- Flags -->\r\n <span class=\"deb-section-title\">Display</span>\r\n <div class=\"deb-toggles\">\r\n <mat-slide-toggle\r\n [ngModel]=\"f.visibility ?? true\"\r\n (ngModelChange)=\"store.updateField(f.id, { visibility: $event })\"\r\n >Visible</mat-slide-toggle>\r\n <mat-slide-toggle\r\n [ngModel]=\"f.table?.visible ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { table: { visible: $event } })\"\r\n >Table column</mat-slide-toggle>\r\n <mat-slide-toggle\r\n [ngModel]=\"f.readonly ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { readonly: $event })\"\r\n >Read-only</mat-slide-toggle>\r\n <mat-slide-toggle\r\n [ngModel]=\"f.disabled ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { disabled: $event })\"\r\n >Disabled</mat-slide-toggle>\r\n <mat-slide-toggle\r\n [ngModel]=\"f.maskData ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { maskData: $event })\"\r\n >Mask data (RBAC)</mat-slide-toggle>\r\n <mat-slide-toggle\r\n data-testid=\"toggle-critical\"\r\n [ngModel]=\"f.criticalField ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { criticalField: $event })\"\r\n matTooltip=\"Renders locked; editing it announces a change against the session baseline.\"\r\n >Critical field</mat-slide-toggle>\r\n <mat-slide-toggle\r\n [ngModel]=\"f.showOnMinimize ?? false\"\r\n (ngModelChange)=\"store.updateField(f.id, { showOnMinimize: $event })\"\r\n matTooltip=\"Shown in the record editor's summary panel.\"\r\n >Show in summary</mat-slide-toggle>\r\n </div>\r\n\r\n <mat-divider></mat-divider>\r\n\r\n <!-- Conditional visibility (static showWhen) -->\r\n <div class=\"deb-row deb-row--split\">\r\n <span class=\"deb-section-title\">Show when</span>\r\n <button mat-stroked-button type=\"button\" data-testid=\"add-show-when\" (click)=\"addShowWhen(f)\">\r\n <mat-icon>add</mat-icon> Condition\r\n </button>\r\n </div>\r\n @for (entry of showWhenEntries(f); track entry.key) {\r\n <div class=\"deb-option-row\" data-testid=\"option-row\">\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\r\n <mat-label>Field id</mat-label>\r\n <input matInput [ngModel]=\"entry.key\" (ngModelChange)=\"renameShowWhen(f, entry.key, $event)\" />\r\n </mat-form-field>\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\r\n <mat-label>Equals</mat-label>\r\n <input matInput [ngModel]=\"entry.display\" (ngModelChange)=\"setShowWhenValue(f, entry.key, $event)\" />\r\n </mat-form-field>\r\n <button mat-icon-button type=\"button\" color=\"warn\" matTooltip=\"Remove condition\"\r\n (click)=\"removeShowWhen(f, entry.key)\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n @if (!showWhenEntries(f).length) {\r\n <p class=\"deb-hint\">Always visible. Add a condition to show this field only for certain values.</p>\r\n }\r\n\r\n <!-- patchOnTrue (boolean/checkbox only) -->\r\n @if (f.type === 'boolean' || f.type === 'checkbox') {\r\n <mat-divider></mat-divider>\r\n <div class=\"deb-row deb-row--split\">\r\n <span class=\"deb-section-title\">Copy on true</span>\r\n <button mat-stroked-button type=\"button\" data-testid=\"add-patch-on-true\"\r\n (click)=\"store.addPatchOnTrueMapping(f.id)\">\r\n <mat-icon>add</mat-icon> Mapping\r\n </button>\r\n </div>\r\n @for (mapping of f.patchOnTrue ?? []; track $index) {\r\n <div class=\"deb-option-row\" data-testid=\"option-row\">\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\r\n <mat-label>From field</mat-label>\r\n <input matInput [ngModel]=\"mapping.from\"\r\n (ngModelChange)=\"store.updatePatchOnTrueMapping(f.id, $index, { from: $event })\" />\r\n </mat-form-field>\r\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\r\n <mat-label>To field</mat-label>\r\n <input matInput [ngModel]=\"mapping.to\"\r\n (ngModelChange)=\"store.updatePatchOnTrueMapping(f.id, $index, { to: $event })\" />\r\n </mat-form-field>\r\n <button mat-icon-button type=\"button\" color=\"warn\" matTooltip=\"Remove mapping\"\r\n (click)=\"store.removePatchOnTrueMapping(f.id, $index)\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n @if (!(f.patchOnTrue ?? []).length) {\r\n <p class=\"deb-hint\">When this flips to true, copy one field's value into another.</p>\r\n }\r\n }\r\n\r\n <mat-divider></mat-divider>\r\n\r\n <!-- Rules attached to this field -->\r\n <ngx-field-rules-list />\r\n\r\n <mat-divider></mat-divider>\r\n\r\n <!-- Cross-entity field reference & drift management (Phase 8) -->\r\n <ngx-referenced-field-config />\r\n </div>\r\n} @else {\r\n <div class=\"deb-inspector__empty\">\r\n <mat-icon>touch_app</mat-icon>\r\n <p>Select a field to edit its properties.</p>\r\n </div>\r\n}\r\n", styles: [".deb-inspector{display:flex;flex-direction:column;gap:12px}.deb-inspector__empty{color:var(--deb-muted, #6b7280);text-align:center;padding:24px 8px}.deb-full{width:100%}.deb-row{display:flex;gap:8px;align-items:center}.deb-row>*{flex:1}.deb-row--split{justify-content:space-between}.deb-row--split>*{flex:0 0 auto}.deb-toggles{display:grid;grid-template-columns:1fr 1fr;gap:8px 12px}.deb-option-row{display:flex;gap:6px;align-items:center}.deb-chip{display:inline-block;padding:2px 8px;border-radius:12px;background:var(--deb-accent-soft, #eef2ff);color:var(--deb-accent, #6366f1);font-size:12px;font-weight:600}\n"] }]
|
|
138
|
+
}] });
|
|
139
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmllbGQtaW5zcGVjdG9yLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9maWVsZC1pbnNwZWN0b3IuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL2ZpZWxkLWluc3BlY3Rvci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDNUQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUMvRCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3pELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN0RSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUU3RCxPQUFPLEVBQXdCLFlBQVksRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzlGLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQXNCLE1BQU0sa0JBQWtCLENBQUM7QUFDeEUsT0FBTyxFQUFFLDhCQUE4QixFQUFF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OEJBQThCO3FCQUMvQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgY29tcHV0ZWQsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBGb3Jtc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcclxuaW1wb3J0IHsgTWF0QnV0dG9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvYnV0dG9uJztcclxuaW1wb3J0IHsgTWF0Q2hlY2tib3hNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9jaGVja2JveCc7XHJcbmltcG9ydCB7IE1hdERpdmlkZXJNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9kaXZpZGVyJztcclxuaW1wb3J0IHsgTWF0Rm9ybUZpZWxkTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvZm9ybS1maWVsZCc7XHJcbmltcG9ydCB7IE1hdEljb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9pY29uJztcclxuaW1wb3J0IHsgTWF0SW5wdXRNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9pbnB1dCc7XHJcbmltcG9ydCB7IE1hdFNlbGVjdE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3NlbGVjdCc7XHJcbmltcG9ydCB7IE1hdFNsaWRlVG9nZ2xlTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvc2xpZGUtdG9nZ2xlJztcclxuaW1wb3J0IHsgTWF0VG9vbHRpcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnO1xyXG5pbXBvcnQgdHlwZSB7IERyb3Bkb3duT3B0aW9uLCBOZXN0ZWRGaWVsZENvbmZpZyB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcclxuaW1wb3J0IHsgZ2V0T3B0aW9uU3RvcmVkVmFsdWUsIHJlc29sdmVMYWJlbCwgcmVzb2x2ZU9wdGlvbkxhYmVsIH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xyXG5pbXBvcnQgeyBCdWlsZGVyU3RvcmUgfSBmcm9tICcuLi9idWlsZGVyLXN0b3JlLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBnZXRGaWVsZFR5cGVNZXRhLCB0eXBlIEZpZWxkVHlwZU1ldGEgfSBmcm9tICcuLi9maWVsZC1jYXRhbG9nJztcclxuaW1wb3J0IHsgRW50aXR5UmVmZXJlbmNlQ29uZmlnQ29tcG9uZW50IH0gZnJvbSAnLi9lbnRpdHktcmVmZXJlbmNlLWNvbmZpZy5jb21wb25lbnQnO1xyXG5pbXBvcnQgeyBGaWVsZFJ1bGVzTGlzdENvbXBvbmVudCB9IGZyb20gJy4vZmllbGQtcnVsZXMtbGlzdC5jb21wb25lbnQnO1xyXG5pbXBvcnQgeyBSZWZlcmVuY2VkRmllbGRDb25maWdDb21wb25lbnQgfSBmcm9tICcuL3JlZmVyZW5jZWQtZmllbGQtY29uZmlnLmNvbXBvbmVudCc7XHJcblxyXG4vKipcclxuICogRmllbGRJbnNwZWN0b3JDb21wb25lbnQg4oCUIGVkaXRzIGV2ZXJ5IHByb3BlcnR5IG9mIHRoZSBjdXJyZW50bHkgc2VsZWN0ZWQgZmllbGQuXHJcbiAqL1xyXG5AQ29tcG9uZW50KHtcclxuICBzZWxlY3RvcjogJ25neC1maWVsZC1pbnNwZWN0b3InLFxyXG4gIHN0YW5kYWxvbmU6IHRydWUsXHJcbiAgaW1wb3J0czogW1xyXG4gICAgRm9ybXNNb2R1bGUsXHJcbiAgICBNYXRCdXR0b25Nb2R1bGUsXHJcbiAgICBNYXRDaGVja2JveE1vZHVsZSxcclxuICAgIE1hdERpdmlkZXJNb2R1bGUsXHJcbiAgICBNYXRGb3JtRmllbGRNb2R1bGUsXHJcbiAgICBNYXRJY29uTW9kdWxlLFxyXG4gICAgTWF0SW5wdXRNb2R1bGUsXHJcbiAgICBNYXRTZWxlY3RNb2R1bGUsXHJcbiAgICBNYXRTbGlkZVRvZ2dsZU1vZHVsZSxcclxuICAgIE1hdFRvb2x0aXBNb2R1bGUsXHJcbiAgICBFbnRpdHlSZWZlcmVuY2VDb25maWdDb21wb25lbnQsXHJcbiAgICBGaWVsZFJ1bGVzTGlzdENvbXBvbmVudCxcclxuICAgIFJlZmVyZW5jZWRGaWVsZENvbmZpZ0NvbXBvbmVudCxcclxuICBdLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9maWVsZC1pbnNwZWN0b3IuY29tcG9uZW50Lmh0bWwnLFxyXG4gIHN0eWxlczogW1xyXG4gICAgYFxyXG4gICAgICAuZGViLWluc3BlY3RvciB7XHJcbiAgICAgICAgZGlzcGxheTogZmxleDtcclxuICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xyXG4gICAgICAgIGdhcDogMTJweDtcclxuICAgICAgfVxyXG4gICAgICAuZGViLWluc3BlY3Rvcl9fZW1wdHkge1xyXG4gICAgICAgIGNvbG9yOiB2YXIoLS1kZWItbXV0ZWQsICM2YjcyODApO1xyXG4gICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjtcclxuICAgICAgICBwYWRkaW5nOiAyNHB4IDhweDtcclxuICAgICAgfVxyXG4gICAgICAuZGViLWZ1bGwge1xyXG4gICAgICAgIHdpZHRoOiAxMDAlO1xyXG4gICAgICB9XHJcbiAgICAgIC5kZWItcm93IHtcclxuICAgICAgICBkaXNwbGF5OiBmbGV4O1xyXG4gICAgICAgIGdhcDogOHB4O1xyXG4gICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7XHJcbiAgICAgIH1cclxuICAgICAgLmRlYi1yb3cgPiAqIHtcclxuICAgICAgICBmbGV4OiAxO1xyXG4gICAgICB9XHJcbiAgICAgIC5kZWItcm93LS1zcGxpdCB7XHJcbiAgICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xyXG4gICAgICB9XHJcbiAgICAgIC5kZWItcm93LS1zcGxpdCA+ICoge1xyXG4gICAgICAgIGZsZXg6IDAgMCBhdXRvO1xyXG4gICAgICB9XHJcbiAgICAgIC5kZWItdG9nZ2xlcyB7XHJcbiAgICAgICAgZGlzcGxheTogZ3JpZDtcclxuICAgICAgICBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IDFmciAxZnI7XHJcbiAgICAgICAgZ2FwOiA4cHggMTJweDtcclxuICAgICAgfVxyXG4gICAgICAuZGViLW9wdGlvbi1yb3cge1xyXG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XHJcbiAgICAgICAgZ2FwOiA2cHg7XHJcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcclxuICAgICAgfVxyXG4gICAgICAuZGViLWNoaXAge1xyXG4gICAgICAgIGRpc3BsYXk6IGlubGluZS1ibG9jaztcclxuICAgICAgICBwYWRkaW5nOiAycHggOHB4O1xyXG4gICAgICAgIGJvcmRlci1yYWRpdXM6IDEycHg7XHJcbiAgICAgICAgYmFja2dyb3VuZDogdmFyKC0tZGViLWFjY2VudC1zb2Z0LCAjZWVmMmZmKTtcclxuICAgICAgICBjb2xvcjogdmFyKC0tZGViLWFjY2VudCwgIzYzNjZmMSk7XHJcbiAgICAgICAgZm9udC1zaXplOiAxMnB4O1xyXG4gICAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7XHJcbiAgICAgIH1cclxuICAgIGAsXHJcbiAgXSxcclxufSlcclxuZXhwb3J0IGNsYXNzIEZpZWxkSW5zcGVjdG9yQ29tcG9uZW50IHtcclxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgc3RvcmUgPSBpbmplY3QoQnVpbGRlclN0b3JlKTtcclxuXHJcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGZpZWxkID0gdGhpcy5zdG9yZS5zZWxlY3RlZEZpZWxkO1xyXG4gIHByb3RlY3RlZCByZWFkb25seSBtZXRhID0gY29tcHV0ZWQ8RmllbGRUeXBlTWV0YSB8IHVuZGVmaW5lZD4oKCkgPT4ge1xyXG4gICAgY29uc3QgZiA9IHRoaXMuZmllbGQoKTtcclxuICAgIHJldHVybiBmID8gZ2V0RmllbGRUeXBlTWV0YShmLnR5cGUpIDogdW5kZWZpbmVkO1xyXG4gIH0pO1xyXG5cclxuICBwcm90ZWN0ZWQgbGFuZygpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIHRoaXMuc3RvcmUuYWN0aXZlTGFuZ3VhZ2UoKTtcclxuICB9XHJcblxyXG5cclxuICBwcm90ZWN0ZWQgdG9OdW0odmFsdWU6IHVua25vd24pOiBudW1iZXIgfCBudWxsIHtcclxuICAgIGlmICh2YWx1ZSA9PT0gJycgfHwgdmFsdWUgPT09IG51bGwgfHwgdmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuIG51bGw7XHJcbiAgICBjb25zdCBuID0gTnVtYmVyKHZhbHVlKTtcclxuICAgIHJldHVybiBOdW1iZXIuaXNOYU4obikgPyBudWxsIDogbjtcclxuICB9XHJcblxyXG4gIHByb3RlY3RlZCBsYWJlbFZhbHVlKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gcmVzb2x2ZUxhYmVsKGZpZWxkLmxhYmVsLCB0aGlzLmxhbmcoKSk7XHJcbiAgfVxyXG5cclxuICBwcm90ZWN0ZWQgcGxhY2Vob2xkZXJWYWx1ZShmaWVsZDogTmVzdGVkRmllbGRDb25maWcpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIHJlc29sdmVMYWJlbChmaWVsZC5wbGFjZWhvbGRlciwgdGhpcy5sYW5nKCkpO1xyXG4gIH1cclxuXHJcbiAgcHJvdGVjdGVkIG9wdGlvbkxhYmVsKG9wdGlvbjogRHJvcGRvd25PcHRpb24pOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIHJlc29sdmVPcHRpb25MYWJlbChvcHRpb24sIHRoaXMubGFuZygpKTtcclxuICB9XHJcblxyXG4gIC8vIOKUgOKUgOKUgCBzaG93V2hlbiBlZGl0aW5nIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxyXG5cclxuICBwcm90ZWN0ZWQgc2hvd1doZW5FbnRyaWVzKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZyk6IHsga2V5OiBzdHJpbmc7IGRpc3BsYXk6IHN0cmluZyB9W10ge1xyXG4gICAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKGZpZWxkLnNob3dXaGVuID8/IHt9KS5tYXAoKFtrZXksIHZhbHVlXSkgPT4gKHtcclxuICAgICAga2V5LFxyXG4gICAgICBkaXNwbGF5OiB0aGlzLnN0cmluZ2lmeVNob3dXaGVuKHZhbHVlKSxcclxuICAgIH0pKTtcclxuICB9XHJcblxyXG4gIHByb3RlY3RlZCBhZGRTaG93V2hlbihmaWVsZDogTmVzdGVkRmllbGRDb25maWcpOiB2b2lkIHtcclxuICAgIGNvbnN0IG5leHQgPSB7IC4uLihmaWVsZC5zaG93V2hlbiA/PyB7fSkgfTtcclxuICAgIGxldCBrZXkgPSAnZmllbGQnO1xyXG4gICAgbGV0IG4gPSAxO1xyXG4gICAgd2hpbGUgKGtleSBpbiBuZXh0KSBrZXkgPSBgZmllbGRfJHsrK259YDtcclxuICAgIG5leHRba2V5XSA9IHRydWU7XHJcbiAgICB0aGlzLnN0b3JlLnNldFNob3dXaGVuKGZpZWxkLmlkLCBuZXh0KTtcclxuICB9XHJcblxyXG4gIHByb3RlY3RlZCByZW5hbWVTaG93V2hlbihmaWVsZDogTmVzdGVkRmllbGRDb25maWcsIG9sZEtleTogc3RyaW5nLCBuZXdLZXk6IHN0cmluZyk6IHZvaWQge1xyXG4gICAgY29uc3QgdHJpbW1lZCA9IG5ld0tleS50cmltKCk7XHJcbiAgICBpZiAoIXRyaW1tZWQgfHwgdHJpbW1lZCA9PT0gb2xkS2V5KSByZXR1cm47XHJcbiAgICBjb25zdCBjdXJyZW50ID0gZmllbGQuc2hvd1doZW4gPz8ge307XHJcbiAgICBpZiAodHJpbW1lZCBpbiBjdXJyZW50KSByZXR1cm47XHJcbiAgICBjb25zdCBuZXh0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xyXG4gICAgLy8gUmVidWlsZCBpbiBvcmRlciBzbyB0aGUgcm93IGRvZXMgbm90IGp1bXAgd2hpbGUgdGhlIHVzZXIgaXMgdHlwaW5nLlxyXG4gICAgZm9yIChjb25zdCBbaywgdl0gb2YgT2JqZWN0LmVudHJpZXMoY3VycmVudCkpIG5leHRbayA9PT0gb2xkS2V5ID8gdHJpbW1lZCA6IGtdID0gdjtcclxuICAgIHRoaXMuc3RvcmUuc2V0U2hvd1doZW4oZmllbGQuaWQsIG5leHQpO1xyXG4gIH1cclxuXHJcbiAgcHJvdGVjdGVkIHNldFNob3dXaGVuVmFsdWUoZmllbGQ6IE5lc3RlZEZpZWxkQ29uZmlnLCBrZXk6IHN0cmluZywgcmF3OiBzdHJpbmcpOiB2b2lkIHtcclxuICAgIHRoaXMuc3RvcmUuc2V0U2hvd1doZW4oZmllbGQuaWQsIHsgLi4uKGZpZWxkLnNob3dXaGVuID8/IHt9KSwgW2tleV06IHRoaXMucGFyc2VTaG93V2hlbihyYXcpIH0pO1xyXG4gIH1cclxuXHJcbiAgcHJvdGVjdGVkIHJlbW92ZVNob3dXaGVuKGZpZWxkOiBOZXN0ZWRGaWVsZENvbmZpZywga2V5OiBzdHJpbmcpOiB2b2lkIHtcclxuICAgIGNvbnN0IG5leHQgPSB7IC4uLihmaWVsZC5zaG93V2hlbiA/PyB7fSkgfTtcclxuICAgIGRlbGV0ZSBuZXh0W2tleV07XHJcbiAgICB0aGlzLnN0b3JlLnNldFNob3dXaGVuKGZpZWxkLmlkLCBuZXh0KTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIGBzaG93V2hlbmAgY29tcGFyZXMgd2l0aCBgIT09YCwgc28gdGhlIGF1dGhvcmVkIHR5cGUgaGFzIHRvIHN1cnZpdmUgdGhlIHJvdW5kIHRyaXA6XHJcbiAgICogXCJ0cnVlXCIvXCJmYWxzZVwiIGJlY29tZSBib29sZWFucyBhbmQgbnVtZXJpYyB0ZXh0IGJlY29tZXMgYSBudW1iZXIuXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBwYXJzZVNob3dXaGVuKHJhdzogc3RyaW5nKTogdW5rbm93biB7XHJcbiAgICBjb25zdCB0cmltbWVkID0gcmF3LnRyaW0oKTtcclxuICAgIGlmICh0cmltbWVkID09PSAndHJ1ZScpIHJldHVybiB0cnVlO1xyXG4gICAgaWYgKHRyaW1tZWQgPT09ICdmYWxzZScpIHJldHVybiBmYWxzZTtcclxuICAgIGlmICh0cmltbWVkID09PSAnbnVsbCcpIHJldHVybiBudWxsO1xyXG4gICAgaWYgKHRyaW1tZWQgIT09ICcnICYmICFOdW1iZXIuaXNOYU4oTnVtYmVyKHRyaW1tZWQpKSkgcmV0dXJuIE51bWJlcih0cmltbWVkKTtcclxuICAgIHJldHVybiByYXc7XHJcbiAgfVxyXG5cclxuICBwcml2YXRlIHN0cmluZ2lmeVNob3dXaGVuKHZhbHVlOiB1bmtub3duKTogc3RyaW5nIHtcclxuICAgIGlmICh2YWx1ZSA9PT0gbnVsbCkgcmV0dXJuICdudWxsJztcclxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnKSByZXR1cm4gSlNPTi5zdHJpbmdpZnkodmFsdWUpO1xyXG4gICAgcmV0dXJuIFN0cmluZyh2YWx1ZSk7XHJcbiAgfVxyXG59XHJcbiIsIkBpZiAoZmllbGQoKTsgYXMgZikge1xyXG4gIDxkaXYgY2xhc3M9XCJkZWItaW5zcGVjdG9yXCI+XHJcbiAgICA8IS0tIElkZW50aXR5IC0tPlxyXG4gICAgPGRpdiBjbGFzcz1cImRlYi1yb3dcIj5cclxuICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3Vic2NyaXB0U2l6aW5nPVwiZHluYW1pY1wiPlxyXG4gICAgICAgIDxtYXQtbGFiZWw+RmllbGQgaWQ8L21hdC1sYWJlbD5cclxuICAgICAgICA8aW5wdXQgbWF0SW5wdXQgZGF0YS10ZXN0aWQ9XCJmaWVsZC1pZFwiIFt2YWx1ZV09XCJmLmlkXCIgZGlzYWJsZWQgLz5cclxuICAgICAgICBAaWYgKHN0b3JlLmhhc01hbnVhbElkKGYuaWQpKSB7XHJcbiAgICAgICAgICA8bWF0LWhpbnQ+VGhlIHN0b3JlZCBkYXRhIGtleSBmb3IgdGhpcyBzYXZlZCBmaWVsZCDigJQgZml4ZWQuPC9tYXQtaGludD5cclxuICAgICAgICB9IEBlbHNlIHtcclxuICAgICAgICAgIDxtYXQtaGludD5EZXJpdmVkIGZyb20gdGhlIGxhYmVsLiBVc2VkIGFzIHRoZSBkYXRhIGtleS48L21hdC1oaW50PlxyXG4gICAgICAgIH1cclxuICAgICAgPC9tYXQtZm9ybS1maWVsZD5cclxuICAgICAgPHNwYW4gY2xhc3M9XCJkZWItY2hpcFwiPnt7IG1ldGEoKT8ubGFiZWwgfHwgZi50eXBlIH19PC9zcGFuPlxyXG4gICAgPC9kaXY+XHJcblxyXG4gICAgPCEtLSBMb2NhbGlzZWQgdGV4dCAoYWN0aXZlIGxhbmd1YWdlKSAtLT5cclxuICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIiBjbGFzcz1cImRlYi1mdWxsXCI+XHJcbiAgICAgIDxtYXQtbGFiZWw+TGFiZWwgKHt7IGxhbmcoKSB9fSk8L21hdC1sYWJlbD5cclxuICAgICAgPGlucHV0XHJcbiAgICAgICAgbWF0SW5wdXRcclxuICAgICAgICBbbmdNb2RlbF09XCJsYWJlbFZhbHVlKGYpXCJcclxuICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS5zZXRGaWVsZExhYmVsKGYuaWQsIGxhbmcoKSwgJGV2ZW50KVwiXHJcbiAgICAgIC8+XHJcbiAgICA8L21hdC1mb3JtLWZpZWxkPlxyXG5cclxuICAgIEBpZiAobWV0YSgpPy5zdXBwb3J0c1BsYWNlaG9sZGVyKSB7XHJcbiAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIiBjbGFzcz1cImRlYi1mdWxsXCI+XHJcbiAgICAgICAgPG1hdC1sYWJlbD5QbGFjZWhvbGRlciAoe3sgbGFuZygpIH19KTwvbWF0LWxhYmVsPlxyXG4gICAgICAgIDxpbnB1dFxyXG4gICAgICAgICAgbWF0SW5wdXRcclxuICAgICAgICAgIFtuZ01vZGVsXT1cInBsYWNlaG9sZGVyVmFsdWUoZilcIlxyXG4gICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwic3RvcmUuc2V0RmllbGRQbGFjZWhvbGRlcihmLmlkLCBsYW5nKCksICRldmVudClcIlxyXG4gICAgICAgIC8+XHJcbiAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XHJcbiAgICB9XHJcblxyXG4gICAgQGlmIChtZXRhKCk/LnN1cHBvcnRzRGVmYXVsdFZhbHVlKSB7XHJcbiAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIiBjbGFzcz1cImRlYi1mdWxsXCI+XHJcbiAgICAgICAgPG1hdC1sYWJlbD5EZWZhdWx0IHZhbHVlPC9tYXQtbGFiZWw+XHJcbiAgICAgICAgPGlucHV0XHJcbiAgICAgICAgICBtYXRJbnB1dFxyXG4gICAgICAgICAgW25nTW9kZWxdPVwiZi5kZWZhdWx0VmFsdWUgPz8gJydcIlxyXG4gICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwic3RvcmUudXBkYXRlRmllbGQoZi5pZCwgeyBkZWZhdWx0VmFsdWU6ICRldmVudCB9KVwiXHJcbiAgICAgICAgLz5cclxuICAgICAgPC9tYXQtZm9ybS1maWVsZD5cclxuICAgIH1cclxuXHJcbiAgICA8IS0tIEVudGl0eSByZWZlcmVuY2UsIGNhc2NhZGUsIGFuZCBhdXRvUGF0Y2ggLS0+XHJcbiAgICBAaWYgKG1ldGEoKT8uaXNFbnRpdHlSZWYpIHtcclxuICAgICAgPG1hdC1kaXZpZGVyPjwvbWF0LWRpdmlkZXI+XHJcbiAgICAgIDxuZ3gtZW50aXR5LXJlZmVyZW5jZS1jb25maWcgLz5cclxuICAgIH1cclxuXHJcbiAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cclxuXHJcbiAgICA8IS0tIFZhbGlkYXRvcnMgLS0+XHJcbiAgICBAaWYgKG1ldGEoKTsgYXMgbSkge1xyXG4gICAgICBAaWYgKG0uZmxhZ1ZhbGlkYXRvcnMubGVuZ3RoIHx8IG0ucGFyYW1WYWxpZGF0b3JzLmxlbmd0aCkge1xyXG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLXNlY3Rpb24tdGl0bGVcIj5WYWxpZGF0aW9uPC9zcGFuPlxyXG4gICAgICAgIDxkaXYgY2xhc3M9XCJkZWItdG9nZ2xlc1wiPlxyXG4gICAgICAgICAgQGZvciAodiBvZiBtLmZsYWdWYWxpZGF0b3JzOyB0cmFjayB2KSB7XHJcbiAgICAgICAgICAgIDxtYXQtY2hlY2tib3hcclxuICAgICAgICAgICAgICBbbmdNb2RlbF09XCJzdG9yZS5oYXNGbGFnVmFsaWRhdG9yKGYsIHYpXCJcclxuICAgICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS50b2dnbGVGbGFnVmFsaWRhdG9yKGYuaWQsIHYsICRldmVudClcIlxyXG4gICAgICAgICAgICA+e3sgdiB9fTwvbWF0LWNoZWNrYm94PlxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIDwvZGl2PlxyXG4gICAgICAgIEBmb3IgKHYgb2YgbS5wYXJhbVZhbGlkYXRvcnM7IHRyYWNrIHYpIHtcclxuICAgICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIiBjbGFzcz1cImRlYi1mdWxsXCI+XHJcbiAgICAgICAgICAgIDxtYXQtbGFiZWw+e3sgdiB9fTwvbWF0LWxhYmVsPlxyXG4gICAgICAgICAgICA8aW5wdXRcclxuICAgICAgICAgICAgICBtYXRJbnB1dFxyXG4gICAgICAgICAgICAgIHR5cGU9XCJudW1iZXJcIlxyXG4gICAgICAgICAgICAgIFtuZ01vZGVsXT1cInN0b3JlLmdldFBhcmFtVmFsaWRhdG9yKGYsIHYpXCJcclxuICAgICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS5zZXRQYXJhbVZhbGlkYXRvcihmLmlkLCB2LCB0b051bSgkZXZlbnQpKVwiXHJcbiAgICAgICAgICAgIC8+XHJcbiAgICAgICAgICA8L21hdC1mb3JtLWZpZWxkPlxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIDwhLS0gT3B0aW9ucyAoZHJvcGRvd24gLyByYWRpbyAvIG11bHRpU2VsZWN0KSAtLT5cclxuICAgIEBpZiAobWV0YSgpPy5oYXNPcHRpb25zKSB7XHJcbiAgICAgIDxtYXQtZGl2aWRlcj48L21hdC1kaXZpZGVyPlxyXG4gICAgICA8c3BhbiBjbGFzcz1cImRlYi1zZWN0aW9uLXRpdGxlXCI+RGF0YSBzb3VyY2U8L3NwYW4+XHJcbiAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIiBjbGFzcz1cImRlYi1mdWxsXCI+XHJcbiAgICAgICAgPG1hdC1sYWJlbD5PcHRpb25zIGNvbWUgZnJvbTwvbWF0LWxhYmVsPlxyXG4gICAgICAgIDxtYXQtc2VsZWN0XHJcbiAgICAgICAgICBkYXRhLXRlc3RpZD1cImRhdGEtc291cmNlXCJcclxuICAgICAgICAgIFtuZ01vZGVsXT1cInN0b3JlLmZpZWxkRGF0YVNvdXJjZShmKVwiXHJcbiAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS5zZXRGaWVsZERhdGFTb3VyY2UoZi5pZCwgJGV2ZW50KVwiXHJcbiAgICAgICAgPlxyXG4gICAgICAgICAgPG1hdC1vcHRpb24gdmFsdWU9XCJub25lXCI+Tm90aGluZyB5ZXQ8L21hdC1vcHRpb24+XHJcbiAgICAgICAgICA8bWF0LW9wdGlvbiB2YWx1ZT1cIm1hbnVhbFwiPk9wdGlvbnMgYXV0aG9yZWQgaGVyZTwvbWF0LW9wdGlvbj5cclxuICAgICAgICAgIDxtYXQtb3B0aW9uIHZhbHVlPVwibG9va3VwXCI+QSBuYW1lZCBsaXN0PC9tYXQtb3B0aW9uPlxyXG4gICAgICAgIDwvbWF0LXNlbGVjdD5cclxuICAgICAgPC9tYXQtZm9ybS1maWVsZD5cclxuICAgIH1cclxuXHJcbiAgICA8IS0tIE5hbWVkIGxvb2t1cCBsaXN0IC0tPlxyXG4gICAgQGlmIChtZXRhKCk/Lmhhc09wdGlvbnMgJiYgc3RvcmUuZmllbGREYXRhU291cmNlKGYpID09PSAnbG9va3VwJykge1xyXG4gICAgICA8bWF0LWZvcm0tZmllbGQgYXBwZWFyYW5jZT1cIm91dGxpbmVcIiBzdWJzY3JpcHRTaXppbmc9XCJkeW5hbWljXCIgY2xhc3M9XCJkZWItZnVsbFwiPlxyXG4gICAgICAgIDxtYXQtbGFiZWw+TGlzdCBuYW1lPC9tYXQtbGFiZWw+XHJcbiAgICAgICAgPGlucHV0XHJcbiAgICAgICAgICBtYXRJbnB1dFxyXG4gICAgICAgICAgZGF0YS10ZXN0aWQ9XCJsaXN0LW5hbWVcIlxyXG4gICAgICAgICAgW25nTW9kZWxdPVwiZi5saXN0TmFtZSA/PyAnJ1wiXHJcbiAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS5zZXRMaXN0TmFtZShmLmlkLCAkZXZlbnQpXCJcclxuICAgICAgICAvPlxyXG4gICAgICA8L21hdC1mb3JtLWZpZWxkPlxyXG4gICAgICA8cCBjbGFzcz1cImRlYi1oaW50XCI+XHJcbiAgICAgICAgVGhlIGNvbnN1bWluZyBhcHAgcmVzb2x2ZXMgdGhpcyBuYW1lIHRocm91Z2ggaXRzIGxvb2t1cCByZWdpc3RyeS4gT3B0aW9ucyBhcmUgbm90XHJcbiAgICAgICAgYXV0aG9yZWQgaGVyZS5cclxuICAgICAgPC9wPlxyXG4gICAgfVxyXG5cclxuICAgIDwhLS0gSW5saW5lIG9wdGlvbnMgLS0+XHJcbiAgICBAaWYgKG1ldGEoKT8uaGFzT3B0aW9ucyAmJiBzdG9yZS5maWVsZERhdGFTb3VyY2UoZikgIT09ICdsb29rdXAnKSB7XHJcbiAgICAgIDxkaXYgY2xhc3M9XCJkZWItcm93XCIgc3R5bGU9XCJqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47XCI+XHJcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJkZWItc2VjdGlvbi10aXRsZVwiPk9wdGlvbnM8L3NwYW4+XHJcbiAgICAgICAgPGJ1dHRvbiBtYXQtc3Ryb2tlZC1idXR0b24gdHlwZT1cImJ1dHRvblwiIChjbGljayk9XCJzdG9yZS5hZGRPcHRpb24oZi5pZClcIj5cclxuICAgICAgICAgIDxtYXQtaWNvbj5hZGQ8L21hdC1pY29uPiBPcHRpb25cclxuICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgPC9kaXY+XHJcbiAgICAgIEBmb3IgKG9wdCBvZiBmLm9wdGlvbnMgPz8gW107IHRyYWNrICRpbmRleCkge1xyXG4gICAgICAgIDxkaXYgY2xhc3M9XCJkZWItb3B0aW9uLXJvd1wiIGRhdGEtdGVzdGlkPVwib3B0aW9uLXJvd1wiPlxyXG4gICAgICAgICAgPCEtLSBPbmUgaW5wdXQ6IHRoZSBkaXNwbGF5ZWQgdGV4dCBpcyB0aGUgc3RvcmVkIHZhbHVlLiAtLT5cclxuICAgICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIiBjbGFzcz1cImRlYi1mdWxsXCI+XHJcbiAgICAgICAgICAgIDxtYXQtbGFiZWw+T3B0aW9uICh7eyBsYW5nKCkgfX0pPC9tYXQtbGFiZWw+XHJcbiAgICAgICAgICAgIDxpbnB1dFxyXG4gICAgICAgICAgICAgIG1hdElucHV0XHJcbiAgICAgICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ29wdGlvbi0nICsgJGluZGV4XCJcclxuICAgICAgICAgICAgICBbbmdNb2RlbF09XCJvcHRpb25MYWJlbChvcHQpXCJcclxuICAgICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS5zZXRPcHRpb25MYWJlbChmLmlkLCAkaW5kZXgsIGxhbmcoKSwgJGV2ZW50KVwiXHJcbiAgICAgICAgICAgIC8+XHJcbiAgICAgICAgICA8L21hdC1mb3JtLWZpZWxkPlxyXG4gICAgICAgICAgPGJ1dHRvbiBtYXQtaWNvbi1idXR0b24gdHlwZT1cImJ1dHRvblwiIGNvbG9yPVwid2FyblwiXHJcbiAgICAgICAgICAgIChjbGljayk9XCJzdG9yZS5yZW1vdmVPcHRpb24oZi5pZCwgJGluZGV4KVwiIG1hdFRvb2x0aXA9XCJSZW1vdmUgb3B0aW9uXCI+XHJcbiAgICAgICAgICAgIDxtYXQtaWNvbj5kZWxldGU8L21hdC1pY29uPlxyXG4gICAgICAgICAgPC9idXR0b24+XHJcbiAgICAgICAgPC9kaXY+XHJcbiAgICAgIH1cclxuICAgICAgQGlmICghKGYub3B0aW9ucyA/PyBbXSkubGVuZ3RoKSB7XHJcbiAgICAgICAgPHAgY2xhc3M9XCJkZWItaGludFwiPk5vIG9wdGlvbnMgeWV0IOKAlCBhZGQgYXQgbGVhc3Qgb25lLjwvcD5cclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIDxtYXQtZGl2aWRlcj48L21hdC1kaXZpZGVyPlxyXG5cclxuICAgIDwhLS0gRmxhZ3MgLS0+XHJcbiAgICA8c3BhbiBjbGFzcz1cImRlYi1zZWN0aW9uLXRpdGxlXCI+RGlzcGxheTwvc3Bhbj5cclxuICAgIDxkaXYgY2xhc3M9XCJkZWItdG9nZ2xlc1wiPlxyXG4gICAgICA8bWF0LXNsaWRlLXRvZ2dsZVxyXG4gICAgICAgIFtuZ01vZGVsXT1cImYudmlzaWJpbGl0eSA/PyB0cnVlXCJcclxuICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS51cGRhdGVGaWVsZChmLmlkLCB7IHZpc2liaWxpdHk6ICRldmVudCB9KVwiXHJcbiAgICAgID5WaXNpYmxlPC9tYXQtc2xpZGUtdG9nZ2xlPlxyXG4gICAgICA8bWF0LXNsaWRlLXRvZ2dsZVxyXG4gICAgICAgIFtuZ01vZGVsXT1cImYudGFibGU/LnZpc2libGUgPz8gZmFsc2VcIlxyXG4gICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnVwZGF0ZUZpZWxkKGYuaWQsIHsgdGFibGU6IHsgdmlzaWJsZTogJGV2ZW50IH0gfSlcIlxyXG4gICAgICA+VGFibGUgY29sdW1uPC9tYXQtc2xpZGUtdG9nZ2xlPlxyXG4gICAgICA8bWF0LXNsaWRlLXRvZ2dsZVxyXG4gICAgICAgIFtuZ01vZGVsXT1cImYucmVhZG9ubHkgPz8gZmFsc2VcIlxyXG4gICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnVwZGF0ZUZpZWxkKGYuaWQsIHsgcmVhZG9ubHk6ICRldmVudCB9KVwiXHJcbiAgICAgID5SZWFkLW9ubHk8L21hdC1zbGlkZS10b2dnbGU+XHJcbiAgICAgIDxtYXQtc2xpZGUtdG9nZ2xlXHJcbiAgICAgICAgW25nTW9kZWxdPVwiZi5kaXNhYmxlZCA/PyBmYWxzZVwiXHJcbiAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwic3RvcmUudXBkYXRlRmllbGQoZi5pZCwgeyBkaXNhYmxlZDogJGV2ZW50IH0pXCJcclxuICAgICAgPkRpc2FibGVkPC9tYXQtc2xpZGUtdG9nZ2xlPlxyXG4gICAgICA8bWF0LXNsaWRlLXRvZ2dsZVxyXG4gICAgICAgIFtuZ01vZGVsXT1cImYubWFza0RhdGEgPz8gZmFsc2VcIlxyXG4gICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnVwZGF0ZUZpZWxkKGYuaWQsIHsgbWFza0RhdGE6ICRldmVudCB9KVwiXHJcbiAgICAgID5NYXNrIGRhdGEgKFJCQUMpPC9tYXQtc2xpZGUtdG9nZ2xlPlxyXG4gICAgICA8bWF0LXNsaWRlLXRvZ2dsZVxyXG4gICAgICAgIGRhdGEtdGVzdGlkPVwidG9nZ2xlLWNyaXRpY2FsXCJcclxuICAgICAgICBbbmdNb2RlbF09XCJmLmNyaXRpY2FsRmllbGQgPz8gZmFsc2VcIlxyXG4gICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnVwZGF0ZUZpZWxkKGYuaWQsIHsgY3JpdGljYWxGaWVsZDogJGV2ZW50IH0pXCJcclxuICAgICAgICBtYXRUb29sdGlwPVwiUmVuZGVycyBsb2NrZWQ7IGVkaXRpbmcgaXQgYW5ub3VuY2VzIGEgY2hhbmdlIGFnYWluc3QgdGhlIHNlc3Npb24gYmFzZWxpbmUuXCJcclxuICAgICAgPkNyaXRpY2FsIGZpZWxkPC9tYXQtc2xpZGUtdG9nZ2xlPlxyXG4gICAgICA8bWF0LXNsaWRlLXRvZ2dsZVxyXG4gICAgICAgIFtuZ01vZGVsXT1cImYuc2hvd09uTWluaW1pemUgPz8gZmFsc2VcIlxyXG4gICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInN0b3JlLnVwZGF0ZUZpZWxkKGYuaWQsIHsgc2hvd09uTWluaW1pemU6ICRldmVudCB9KVwiXHJcbiAgICAgICAgbWF0VG9vbHRpcD1cIlNob3duIGluIHRoZSByZWNvcmQgZWRpdG9yJ3Mgc3VtbWFyeSBwYW5lbC5cIlxyXG4gICAgICA+U2hvdyBpbiBzdW1tYXJ5PC9tYXQtc2xpZGUtdG9nZ2xlPlxyXG4gICAgPC9kaXY+XHJcblxyXG4gICAgPG1hdC1kaXZpZGVyPjwvbWF0LWRpdmlkZXI+XHJcblxyXG4gICAgPCEtLSBDb25kaXRpb25hbCB2aXNpYmlsaXR5IChzdGF0aWMgc2hvd1doZW4pIC0tPlxyXG4gICAgPGRpdiBjbGFzcz1cImRlYi1yb3cgZGViLXJvdy0tc3BsaXRcIj5cclxuICAgICAgPHNwYW4gY2xhc3M9XCJkZWItc2VjdGlvbi10aXRsZVwiPlNob3cgd2hlbjwvc3Bhbj5cclxuICAgICAgPGJ1dHRvbiBtYXQtc3Ryb2tlZC1idXR0b24gdHlwZT1cImJ1dHRvblwiIGRhdGEtdGVzdGlkPVwiYWRkLXNob3ctd2hlblwiIChjbGljayk9XCJhZGRTaG93V2hlbihmKVwiPlxyXG4gICAgICAgIDxtYXQtaWNvbj5hZGQ8L21hdC1pY29uPiBDb25kaXRpb25cclxuICAgICAgPC9idXR0b24+XHJcbiAgICA8L2Rpdj5cclxuICAgIEBmb3IgKGVudHJ5IG9mIHNob3dXaGVuRW50cmllcyhmKTsgdHJhY2sgZW50cnkua2V5KSB7XHJcbiAgICAgIDxkaXYgY2xhc3M9XCJkZWItb3B0aW9uLXJvd1wiIGRhdGEtdGVzdGlkPVwib3B0aW9uLXJvd1wiPlxyXG4gICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIj5cclxuICAgICAgICAgIDxtYXQtbGFiZWw+RmllbGQgaWQ8L21hdC1sYWJlbD5cclxuICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCBbbmdNb2RlbF09XCJlbnRyeS5rZXlcIiAobmdNb2RlbENoYW5nZSk9XCJyZW5hbWVTaG93V2hlbihmLCBlbnRyeS5rZXksICRldmVudClcIiAvPlxyXG4gICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XHJcbiAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3Vic2NyaXB0U2l6aW5nPVwiZHluYW1pY1wiPlxyXG4gICAgICAgICAgPG1hdC1sYWJlbD5FcXVhbHM8L21hdC1sYWJlbD5cclxuICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCBbbmdNb2RlbF09XCJlbnRyeS5kaXNwbGF5XCIgKG5nTW9kZWxDaGFuZ2UpPVwic2V0U2hvd1doZW5WYWx1ZShmLCBlbnRyeS5rZXksICRldmVudClcIiAvPlxyXG4gICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XHJcbiAgICAgICAgPGJ1dHRvbiBtYXQtaWNvbi1idXR0b24gdHlwZT1cImJ1dHRvblwiIGNvbG9yPVwid2FyblwiIG1hdFRvb2x0aXA9XCJSZW1vdmUgY29uZGl0aW9uXCJcclxuICAgICAgICAgIChjbGljayk9XCJyZW1vdmVTaG93V2hlbihmLCBlbnRyeS5rZXkpXCI+XHJcbiAgICAgICAgICA8bWF0LWljb24+ZGVsZXRlPC9tYXQtaWNvbj5cclxuICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgPC9kaXY+XHJcbiAgICB9XHJcbiAgICBAaWYgKCFzaG93V2hlbkVudHJpZXMoZikubGVuZ3RoKSB7XHJcbiAgICAgIDxwIGNsYXNzPVwiZGViLWhpbnRcIj5BbHdheXMgdmlzaWJsZS4gQWRkIGEgY29uZGl0aW9uIHRvIHNob3cgdGhpcyBmaWVsZCBvbmx5IGZvciBjZXJ0YWluIHZhbHVlcy48L3A+XHJcbiAgICB9XHJcblxyXG4gICAgPCEtLSBwYXRjaE9uVHJ1ZSAoYm9vbGVhbi9jaGVja2JveCBvbmx5KSAtLT5cclxuICAgIEBpZiAoZi50eXBlID09PSAnYm9vbGVhbicgfHwgZi50eXBlID09PSAnY2hlY2tib3gnKSB7XHJcbiAgICAgIDxtYXQtZGl2aWRlcj48L21hdC1kaXZpZGVyPlxyXG4gICAgICA8ZGl2IGNsYXNzPVwiZGViLXJvdyBkZWItcm93LS1zcGxpdFwiPlxyXG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLXNlY3Rpb24tdGl0bGVcIj5Db3B5IG9uIHRydWU8L3NwYW4+XHJcbiAgICAgICAgPGJ1dHRvbiBtYXQtc3Ryb2tlZC1idXR0b24gdHlwZT1cImJ1dHRvblwiIGRhdGEtdGVzdGlkPVwiYWRkLXBhdGNoLW9uLXRydWVcIlxyXG4gICAgICAgICAgKGNsaWNrKT1cInN0b3JlLmFkZFBhdGNoT25UcnVlTWFwcGluZyhmLmlkKVwiPlxyXG4gICAgICAgICAgPG1hdC1pY29uPmFkZDwvbWF0LWljb24+IE1hcHBpbmdcclxuICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgPC9kaXY+XHJcbiAgICAgIEBmb3IgKG1hcHBpbmcgb2YgZi5wYXRjaE9uVHJ1ZSA/PyBbXTsgdHJhY2sgJGluZGV4KSB7XHJcbiAgICAgICAgPGRpdiBjbGFzcz1cImRlYi1vcHRpb24tcm93XCIgZGF0YS10ZXN0aWQ9XCJvcHRpb24tcm93XCI+XHJcbiAgICAgICAgICA8bWF0LWZvcm0tZmllbGQgYXBwZWFyYW5jZT1cIm91dGxpbmVcIiBzdWJzY3JpcHRTaXppbmc9XCJkeW5hbWljXCI+XHJcbiAgICAgICAgICAgIDxtYXQtbGFiZWw+RnJvbSBmaWVsZDwvbWF0LWxhYmVsPlxyXG4gICAgICAgICAgICA8aW5wdXQgbWF0SW5wdXQgW25nTW9kZWxdPVwibWFwcGluZy5mcm9tXCJcclxuICAgICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS51cGRhdGVQYXRjaE9uVHJ1ZU1hcHBpbmcoZi5pZCwgJGluZGV4LCB7IGZyb206ICRldmVudCB9KVwiIC8+XHJcbiAgICAgICAgICA8L21hdC1mb3JtLWZpZWxkPlxyXG4gICAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3Vic2NyaXB0U2l6aW5nPVwiZHluYW1pY1wiPlxyXG4gICAgICAgICAgICA8bWF0LWxhYmVsPlRvIGZpZWxkPC9tYXQtbGFiZWw+XHJcbiAgICAgICAgICAgIDxpbnB1dCBtYXRJbnB1dCBbbmdNb2RlbF09XCJtYXBwaW5nLnRvXCJcclxuICAgICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzdG9yZS51cGRhdGVQYXRjaE9uVHJ1ZU1hcHBpbmcoZi5pZCwgJGluZGV4LCB7IHRvOiAkZXZlbnQgfSlcIiAvPlxyXG4gICAgICAgICAgPC9tYXQtZm9ybS1maWVsZD5cclxuICAgICAgICAgIDxidXR0b24gbWF0LWljb24tYnV0dG9uIHR5cGU9XCJidXR0b25cIiBjb2xvcj1cIndhcm5cIiBtYXRUb29sdGlwPVwiUmVtb3ZlIG1hcHBpbmdcIlxyXG4gICAgICAgICAgICAoY2xpY2spPVwic3RvcmUucmVtb3ZlUGF0Y2hPblRydWVNYXBwaW5nKGYuaWQsICRpbmRleClcIj5cclxuICAgICAgICAgICAgPG1hdC1pY29uPmRlbGV0ZTwvbWF0LWljb24+XHJcbiAgICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgICA8L2Rpdj5cclxuICAgICAgfVxyXG4gICAgICBAaWYgKCEoZi5wYXRjaE9uVHJ1ZSA/PyBbXSkubGVuZ3RoKSB7XHJcbiAgICAgICAgPHAgY2xhc3M9XCJkZWItaGludFwiPldoZW4gdGhpcyBmbGlwcyB0byB0cnVlLCBjb3B5IG9uZSBmaWVsZCdzIHZhbHVlIGludG8gYW5vdGhlci48L3A+XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cclxuXHJcbiAgICA8IS0tIFJ1bGVzIGF0dGFjaGVkIHRvIHRoaXMgZmllbGQgLS0+XHJcbiAgICA8bmd4LWZpZWxkLXJ1bGVzLWxpc3QgLz5cclxuXHJcbiAgICA8bWF0LWRpdmlkZXI+PC9tYXQtZGl2aWRlcj5cclxuXHJcbiAgICA8IS0tIENyb3NzLWVudGl0eSBmaWVsZCByZWZlcmVuY2UgJiBkcmlmdCBtYW5hZ2VtZW50IChQaGFzZSA4KSAtLT5cclxuICAgIDxuZ3gtcmVmZXJlbmNlZC1maWVsZC1jb25maWcgLz5cclxuICA8L2Rpdj5cclxufSBAZWxzZSB7XHJcbiAgPGRpdiBjbGFzcz1cImRlYi1pbnNwZWN0b3JfX2VtcHR5XCI+XHJcbiAgICA8bWF0LWljb24+dG91Y2hfYXBwPC9tYXQtaWNvbj5cclxuICAgIDxwPlNlbGVjdCBhIGZpZWxkIHRvIGVkaXQgaXRzIHByb3BlcnRpZXMuPC9wPlxyXG4gIDwvZGl2PlxyXG59XHJcbiJdfQ==
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Component, EventEmitter, Output } from '@angular/core';
|
|
2
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
3
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
4
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
5
|
+
import { FIELD_TYPE_CATALOG } from '../field-catalog';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
import * as i1 from "@angular/material/button";
|
|
8
|
+
import * as i2 from "@angular/material/icon";
|
|
9
|
+
import * as i3 from "@angular/material/tooltip";
|
|
10
|
+
/**
|
|
11
|
+
* FieldPaletteComponent — the list of buildable field types (Angular Material).
|
|
12
|
+
*/
|
|
13
|
+
export class FieldPaletteComponent {
|
|
14
|
+
catalog = FIELD_TYPE_CATALOG;
|
|
15
|
+
pick = new EventEmitter();
|
|
16
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldPaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FieldPaletteComponent, isStandalone: true, selector: "ngx-field-palette", outputs: { pick: "pick" }, ngImport: i0, template: `
|
|
18
|
+
<div class="deb-palette">
|
|
19
|
+
@for (meta of catalog; track meta.type) {
|
|
20
|
+
<button
|
|
21
|
+
mat-stroked-button
|
|
22
|
+
type="button"
|
|
23
|
+
class="deb-palette__item"
|
|
24
|
+
[attr.data-testid]="'palette-' + meta.type"
|
|
25
|
+
[matTooltip]="meta.description"
|
|
26
|
+
(click)="pick.emit(meta.type)"
|
|
27
|
+
>
|
|
28
|
+
<mat-icon>{{ meta.icon }}</mat-icon>
|
|
29
|
+
<span>{{ meta.label }}</span>
|
|
30
|
+
</button>
|
|
31
|
+
}
|
|
32
|
+
</div>
|
|
33
|
+
`, isInline: true, styles: [".deb-palette{display:grid;grid-template-columns:1fr 1fr;gap:8px}.deb-palette__item{justify-content:flex-start;overflow:hidden;border-radius:8px!important;border:1px solid var(--border, #e2e8f0)!important;padding:8px 12px!important;font-family:var(--font-heading, inherit)!important;font-weight:600!important;font-size:13px!important;transition:all .18s cubic-bezier(.16,1,.3,1)!important;background:#fff!important}.deb-palette__item:hover{border-color:#6366f1!important;background:#f5f3ff!important;color:#4f46e5!important;transform:translateY(-1px);box-shadow:0 4px 12px #6366f126!important}.deb-palette__item mat-icon{color:#6366f1;margin-right:6px;font-size:18px;width:18px;height:18px}.deb-palette__item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
|
|
34
|
+
}
|
|
35
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldPaletteComponent, decorators: [{
|
|
36
|
+
type: Component,
|
|
37
|
+
args: [{ selector: 'ngx-field-palette', standalone: true, imports: [MatButtonModule, MatIconModule, MatTooltipModule], template: `
|
|
38
|
+
<div class="deb-palette">
|
|
39
|
+
@for (meta of catalog; track meta.type) {
|
|
40
|
+
<button
|
|
41
|
+
mat-stroked-button
|
|
42
|
+
type="button"
|
|
43
|
+
class="deb-palette__item"
|
|
44
|
+
[attr.data-testid]="'palette-' + meta.type"
|
|
45
|
+
[matTooltip]="meta.description"
|
|
46
|
+
(click)="pick.emit(meta.type)"
|
|
47
|
+
>
|
|
48
|
+
<mat-icon>{{ meta.icon }}</mat-icon>
|
|
49
|
+
<span>{{ meta.label }}</span>
|
|
50
|
+
</button>
|
|
51
|
+
}
|
|
52
|
+
</div>
|
|
53
|
+
`, styles: [".deb-palette{display:grid;grid-template-columns:1fr 1fr;gap:8px}.deb-palette__item{justify-content:flex-start;overflow:hidden;border-radius:8px!important;border:1px solid var(--border, #e2e8f0)!important;padding:8px 12px!important;font-family:var(--font-heading, inherit)!important;font-weight:600!important;font-size:13px!important;transition:all .18s cubic-bezier(.16,1,.3,1)!important;background:#fff!important}.deb-palette__item:hover{border-color:#6366f1!important;background:#f5f3ff!important;color:#4f46e5!important;transform:translateY(-1px);box-shadow:0 4px 12px #6366f126!important}.deb-palette__item mat-icon{color:#6366f1;margin-right:6px;font-size:18px;width:18px;height:18px}.deb-palette__item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n"] }]
|
|
54
|
+
}], propDecorators: { pick: [{
|
|
55
|
+
type: Output
|
|
56
|
+
}] } });
|
|
57
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmllbGQtcGFsZXR0ZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvZmllbGQtcGFsZXR0ZS5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2hFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFN0QsT0FBTyxFQUFFLGtCQUFrQixFQUFzQixNQUFNLGtCQUFrQixDQUFDOzs7OztBQUUxRTs7R0FFRztBQStESCxNQUFNLE9BQU8scUJBQXFCO0lBQ3ZCLE9BQU8sR0FBNkIsa0JBQWtCLENBQUM7SUFFdEQsSUFBSSxHQUFHLElBQUksWUFBWSxFQUFpQixDQUFDO3dHQUh4QyxxQkFBcUI7NEZBQXJCLHFCQUFxQix3R0ExRHRCOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JULDgwQkFqQlMsZUFBZSwyTkFBRSxhQUFhLG1MQUFFLGdCQUFnQjs7NEZBMkQvQyxxQkFBcUI7a0JBOURqQyxTQUFTOytCQUNFLG1CQUFtQixjQUNqQixJQUFJLFdBQ1AsQ0FBQyxlQUFlLEVBQUUsYUFBYSxFQUFFLGdCQUFnQixDQUFDLFlBQ2pEOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JUOzhCQTZDUyxJQUFJO3NCQUFiLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIEV2ZW50RW1pdHRlciwgT3V0cHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IE1hdEJ1dHRvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2J1dHRvbic7XHJcbmltcG9ydCB7IE1hdEljb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9pY29uJztcclxuaW1wb3J0IHsgTWF0VG9vbHRpcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3Rvb2x0aXAnO1xyXG5pbXBvcnQgdHlwZSB7IFJpY2hGaWVsZFR5cGUgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XHJcbmltcG9ydCB7IEZJRUxEX1RZUEVfQ0FUQUxPRywgdHlwZSBGaWVsZFR5cGVNZXRhIH0gZnJvbSAnLi4vZmllbGQtY2F0YWxvZyc7XHJcblxyXG4vKipcclxuICogRmllbGRQYWxldHRlQ29tcG9uZW50IOKAlCB0aGUgbGlzdCBvZiBidWlsZGFibGUgZmllbGQgdHlwZXMgKEFuZ3VsYXIgTWF0ZXJpYWwpLlxyXG4gKi9cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICduZ3gtZmllbGQtcGFsZXR0ZScsXHJcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcclxuICBpbXBvcnRzOiBbTWF0QnV0dG9uTW9kdWxlLCBNYXRJY29uTW9kdWxlLCBNYXRUb29sdGlwTW9kdWxlXSxcclxuICB0ZW1wbGF0ZTogYFxyXG4gICAgPGRpdiBjbGFzcz1cImRlYi1wYWxldHRlXCI+XHJcbiAgICAgIEBmb3IgKG1ldGEgb2YgY2F0YWxvZzsgdHJhY2sgbWV0YS50eXBlKSB7XHJcbiAgICAgICAgPGJ1dHRvblxyXG4gICAgICAgICAgbWF0LXN0cm9rZWQtYnV0dG9uXHJcbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcclxuICAgICAgICAgIGNsYXNzPVwiZGViLXBhbGV0dGVfX2l0ZW1cIlxyXG4gICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3BhbGV0dGUtJyArIG1ldGEudHlwZVwiXHJcbiAgICAgICAgICBbbWF0VG9vbHRpcF09XCJtZXRhLmRlc2NyaXB0aW9uXCJcclxuICAgICAgICAgIChjbGljayk9XCJwaWNrLmVtaXQobWV0YS50eXBlKVwiXHJcbiAgICAgICAgPlxyXG4gICAgICAgICAgPG1hdC1pY29uPnt7IG1ldGEuaWNvbiB9fTwvbWF0LWljb24+XHJcbiAgICAgICAgICA8c3Bhbj57eyBtZXRhLmxhYmVsIH19PC9zcGFuPlxyXG4gICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICB9XHJcbiAgICA8L2Rpdj5cclxuICBgLFxyXG4gIHN0eWxlczogW1xyXG4gICAgYFxyXG4gICAgICAuZGViLXBhbGV0dGUge1xyXG4gICAgICAgIGRpc3BsYXk6IGdyaWQ7XHJcbiAgICAgICAgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiAxZnIgMWZyO1xyXG4gICAgICAgIGdhcDogOHB4O1xyXG4gICAgICB9XHJcbiAgICAgIC5kZWItcGFsZXR0ZV9faXRlbSB7XHJcbiAgICAgICAganVzdGlmeS1jb250ZW50OiBmbGV4LXN0YXJ0O1xyXG4gICAgICAgIG92ZXJmbG93OiBoaWRkZW47XHJcbiAgICAgICAgYm9yZGVyLXJhZGl1czogOHB4ICFpbXBvcnRhbnQ7XHJcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgdmFyKC0tYm9yZGVyLCAjZTJlOGYwKSAhaW1wb3J0YW50O1xyXG4gICAgICAgIHBhZGRpbmc6IDhweCAxMnB4ICFpbXBvcnRhbnQ7XHJcbiAgICAgICAgZm9udC1mYW1pbHk6IHZhcigtLWZvbnQtaGVhZGluZywgaW5oZXJpdCkgIWltcG9ydGFudDtcclxuICAgICAgICBmb250LXdlaWdodDogNjAwICFpbXBvcnRhbnQ7XHJcbiAgICAgICAgZm9udC1zaXplOiAxM3B4ICFpbXBvcnRhbnQ7XHJcbiAgICAgICAgdHJhbnNpdGlvbjogYWxsIDAuMThzIGN1YmljLWJlemllcigwLjE2LCAxLCAwLjMsIDEpICFpbXBvcnRhbnQ7XHJcbiAgICAgICAgYmFja2dyb3VuZDogI2ZmZmZmZiAhaW1wb3J0YW50O1xyXG4gICAgICB9XHJcbiAgICAgIC5kZWItcGFsZXR0ZV9faXRlbTpob3ZlciB7XHJcbiAgICAgICAgYm9yZGVyLWNvbG9yOiAjNjM2NmYxICFpbXBvcnRhbnQ7XHJcbiAgICAgICAgYmFja2dyb3VuZDogI2Y1ZjNmZiAhaW1wb3J0YW50O1xyXG4gICAgICAgIGNvbG9yOiAjNGY0NmU1ICFpbXBvcnRhbnQ7XHJcbiAgICAgICAgdHJhbnNmb3JtOiB0cmFuc2xhdGVZKC0xcHgpO1xyXG4gICAgICAgIGJveC1zaGFkb3c6IDAgNHB4IDEycHggcmdiYSg5OSwgMTAyLCAyNDEsIDAuMTUpICFpbXBvcnRhbnQ7XHJcbiAgICAgIH1cclxuICAgICAgLmRlYi1wYWxldHRlX19pdGVtIG1hdC1pY29uIHtcclxuICAgICAgICBjb2xvcjogIzYzNjZmMTtcclxuICAgICAgICBtYXJnaW4tcmlnaHQ6IDZweDtcclxuICAgICAgICBmb250LXNpemU6IDE4cHg7XHJcbiAgICAgICAgd2lkdGg6IDE4cHg7XHJcbiAgICAgICAgaGVpZ2h0OiAxOHB4O1xyXG4gICAgICB9XHJcbiAgICAgIC5kZWItcGFsZXR0ZV9faXRlbSBzcGFuIHtcclxuICAgICAgICBvdmVyZmxvdzogaGlkZGVuO1xyXG4gICAgICAgIHRleHQtb3ZlcmZsb3c6IGVsbGlwc2lzO1xyXG4gICAgICAgIHdoaXRlLXNwYWNlOiBub3dyYXA7XHJcbiAgICAgIH1cclxuICAgIGAsXHJcbiAgXSxcclxufSlcclxuZXhwb3J0IGNsYXNzIEZpZWxkUGFsZXR0ZUNvbXBvbmVudCB7XHJcbiAgcmVhZG9ubHkgY2F0YWxvZzogcmVhZG9ubHkgRmllbGRUeXBlTWV0YVtdID0gRklFTERfVFlQRV9DQVRBTE9HO1xyXG5cclxuICBAT3V0cHV0KCkgcGljayA9IG5ldyBFdmVudEVtaXR0ZXI8UmljaEZpZWxkVHlwZT4oKTtcclxufVxyXG4iXX0=
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { Component, inject, signal } from '@angular/core';
|
|
2
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
3
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
4
|
+
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
5
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
6
|
+
import { BuilderStore } from '../builder-store.service';
|
|
7
|
+
import { deepClone } from '../clone';
|
|
8
|
+
import { RuleFormComponent } from './rule-form.component';
|
|
9
|
+
import * as i0 from "@angular/core";
|
|
10
|
+
import * as i1 from "@angular/material/button";
|
|
11
|
+
import * as i2 from "@angular/material/icon";
|
|
12
|
+
import * as i3 from "@angular/material/slide-toggle";
|
|
13
|
+
import * as i4 from "@angular/material/tooltip";
|
|
14
|
+
/**
|
|
15
|
+
* FieldRulesListComponent — the rules attached to the selected field: view, reorder,
|
|
16
|
+
* enable/disable, edit, delete. `RuleFormComponent` authors one rule; this owns the set.
|
|
17
|
+
*/
|
|
18
|
+
export class FieldRulesListComponent {
|
|
19
|
+
store = inject(BuilderStore);
|
|
20
|
+
/** The rule currently open in the editor — a new draft or a copy of an existing rule. */
|
|
21
|
+
editing = signal(null);
|
|
22
|
+
startCreate() {
|
|
23
|
+
const fieldId = this.store.selectedFieldId();
|
|
24
|
+
if (!fieldId)
|
|
25
|
+
return;
|
|
26
|
+
this.editing.set({
|
|
27
|
+
formConfigId: this.store.config().entity || 'form-1',
|
|
28
|
+
fieldId,
|
|
29
|
+
conditions: [{ operator: 'EQUAL', compareType: 'value', value: '' }],
|
|
30
|
+
action: { type: 'visibility', value: false },
|
|
31
|
+
targets: [{ id: fieldId, type: 'field' }],
|
|
32
|
+
enabled: true,
|
|
33
|
+
priority: this.store.rules().length + 1,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
startEdit(rule) {
|
|
37
|
+
this.editing.set(deepClone(rule));
|
|
38
|
+
}
|
|
39
|
+
commit(rule) {
|
|
40
|
+
if (rule.id)
|
|
41
|
+
this.store.updateRule(rule.id, rule);
|
|
42
|
+
else
|
|
43
|
+
this.store.addRule(rule);
|
|
44
|
+
this.editing.set(null);
|
|
45
|
+
}
|
|
46
|
+
/** One-line human summary: `status EQUAL "archived" → hide`. */
|
|
47
|
+
summarize(rule) {
|
|
48
|
+
const cond = rule.conditions
|
|
49
|
+
.map(c => {
|
|
50
|
+
const rhs = c.compareType === 'field' ? c.compareToField : JSON.stringify(c.value ?? '');
|
|
51
|
+
return `${c.operator} ${rhs ?? ''}`.trim();
|
|
52
|
+
})
|
|
53
|
+
.join(' AND ');
|
|
54
|
+
const action = rule.action.type === 'visibility'
|
|
55
|
+
? rule.action.value === false || rule.action.value === 'false'
|
|
56
|
+
? 'hide'
|
|
57
|
+
: 'show'
|
|
58
|
+
: `${rule.action.type}: ${rule.action.value}`;
|
|
59
|
+
return `${rule.fieldId} ${cond} → ${action}`;
|
|
60
|
+
}
|
|
61
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldRulesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
62
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: FieldRulesListComponent, isStandalone: true, selector: "ngx-field-rules-list", ngImport: i0, template: `
|
|
63
|
+
<div class="deb-rules">
|
|
64
|
+
<div class="deb-rules__head">
|
|
65
|
+
<span class="deb-section-title">Rules</span>
|
|
66
|
+
<button
|
|
67
|
+
mat-stroked-button
|
|
68
|
+
type="button"
|
|
69
|
+
data-testid="add-rule"
|
|
70
|
+
[disabled]="!store.selectedFieldId()"
|
|
71
|
+
(click)="startCreate()"
|
|
72
|
+
>
|
|
73
|
+
<mat-icon>add</mat-icon> Rule
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
@if (editing(); as draft) {
|
|
78
|
+
<ngx-rule-form [rule]="draft" (save)="commit($event)" (cancel)="editing.set(null)" />
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@if (store.rulesForSelectedField().length === 0 && !editing()) {
|
|
82
|
+
<p class="deb-hint" data-testid="rules-empty">No rules on this field.</p>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@for (rule of store.rulesForSelectedField(); track rule.id) {
|
|
86
|
+
<div class="deb-rule-item" [class.deb-rule-item--off]="!rule.enabled" data-testid="rule-item">
|
|
87
|
+
<div class="deb-rule-item__body">
|
|
88
|
+
<span class="deb-rule-item__summary">{{ summarize(rule) }}</span>
|
|
89
|
+
<span class="deb-rule-item__meta">
|
|
90
|
+
priority {{ rule.priority }} · {{ rule.targets.length }} target(s)
|
|
91
|
+
</span>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="deb-rule-item__actions">
|
|
94
|
+
<mat-slide-toggle
|
|
95
|
+
[checked]="rule.enabled"
|
|
96
|
+
matTooltip="Enable / disable"
|
|
97
|
+
(change)="store.toggleRule(rule.id!, $event.checked)"
|
|
98
|
+
/>
|
|
99
|
+
<button
|
|
100
|
+
mat-icon-button
|
|
101
|
+
type="button"
|
|
102
|
+
matTooltip="Move up"
|
|
103
|
+
[attr.data-testid]="'rule-up-' + rule.id"
|
|
104
|
+
(click)="store.moveRule(rule.id!, -1)"
|
|
105
|
+
>
|
|
106
|
+
<mat-icon>arrow_upward</mat-icon>
|
|
107
|
+
</button>
|
|
108
|
+
<button
|
|
109
|
+
mat-icon-button
|
|
110
|
+
type="button"
|
|
111
|
+
matTooltip="Move down"
|
|
112
|
+
[attr.data-testid]="'rule-down-' + rule.id"
|
|
113
|
+
(click)="store.moveRule(rule.id!, 1)"
|
|
114
|
+
>
|
|
115
|
+
<mat-icon>arrow_downward</mat-icon>
|
|
116
|
+
</button>
|
|
117
|
+
<button
|
|
118
|
+
mat-icon-button
|
|
119
|
+
type="button"
|
|
120
|
+
matTooltip="Edit rule"
|
|
121
|
+
[attr.data-testid]="'rule-edit-' + rule.id"
|
|
122
|
+
(click)="startEdit(rule)"
|
|
123
|
+
>
|
|
124
|
+
<mat-icon>edit</mat-icon>
|
|
125
|
+
</button>
|
|
126
|
+
<button
|
|
127
|
+
mat-icon-button
|
|
128
|
+
type="button"
|
|
129
|
+
color="warn"
|
|
130
|
+
matTooltip="Delete rule"
|
|
131
|
+
[attr.data-testid]="'rule-delete-' + rule.id"
|
|
132
|
+
(click)="store.removeRule(rule.id!)"
|
|
133
|
+
>
|
|
134
|
+
<mat-icon>delete</mat-icon>
|
|
135
|
+
</button>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
}
|
|
139
|
+
</div>
|
|
140
|
+
`, isInline: true, styles: [".deb-rules{display:flex;flex-direction:column;gap:8px}.deb-rules__head{display:flex;align-items:center;justify-content:space-between}.deb-rule-item{display:flex;align-items:center;justify-content:space-between;gap:8px;border:1px solid #e5e7eb;border-radius:8px;padding:8px 10px}.deb-rule-item--off{opacity:.55}.deb-rule-item__body{display:flex;flex-direction:column;min-width:0}.deb-rule-item__summary{font-size:13px;font-weight:600;overflow-wrap:anywhere}.deb-rule-item__meta{font-size:11px;color:#6b7280}.deb-rule-item__actions{display:flex;align-items:center;flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i3.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: RuleFormComponent, selector: "ngx-rule-form", inputs: ["rule"], outputs: ["save", "cancel"] }] });
|
|
141
|
+
}
|
|
142
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FieldRulesListComponent, decorators: [{
|
|
143
|
+
type: Component,
|
|
144
|
+
args: [{ selector: 'ngx-field-rules-list', standalone: true, imports: [MatButtonModule, MatIconModule, MatSlideToggleModule, MatTooltipModule, RuleFormComponent], template: `
|
|
145
|
+
<div class="deb-rules">
|
|
146
|
+
<div class="deb-rules__head">
|
|
147
|
+
<span class="deb-section-title">Rules</span>
|
|
148
|
+
<button
|
|
149
|
+
mat-stroked-button
|
|
150
|
+
type="button"
|
|
151
|
+
data-testid="add-rule"
|
|
152
|
+
[disabled]="!store.selectedFieldId()"
|
|
153
|
+
(click)="startCreate()"
|
|
154
|
+
>
|
|
155
|
+
<mat-icon>add</mat-icon> Rule
|
|
156
|
+
</button>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
@if (editing(); as draft) {
|
|
160
|
+
<ngx-rule-form [rule]="draft" (save)="commit($event)" (cancel)="editing.set(null)" />
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@if (store.rulesForSelectedField().length === 0 && !editing()) {
|
|
164
|
+
<p class="deb-hint" data-testid="rules-empty">No rules on this field.</p>
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@for (rule of store.rulesForSelectedField(); track rule.id) {
|
|
168
|
+
<div class="deb-rule-item" [class.deb-rule-item--off]="!rule.enabled" data-testid="rule-item">
|
|
169
|
+
<div class="deb-rule-item__body">
|
|
170
|
+
<span class="deb-rule-item__summary">{{ summarize(rule) }}</span>
|
|
171
|
+
<span class="deb-rule-item__meta">
|
|
172
|
+
priority {{ rule.priority }} · {{ rule.targets.length }} target(s)
|
|
173
|
+
</span>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="deb-rule-item__actions">
|
|
176
|
+
<mat-slide-toggle
|
|
177
|
+
[checked]="rule.enabled"
|
|
178
|
+
matTooltip="Enable / disable"
|
|
179
|
+
(change)="store.toggleRule(rule.id!, $event.checked)"
|
|
180
|
+
/>
|
|
181
|
+
<button
|
|
182
|
+
mat-icon-button
|
|
183
|
+
type="button"
|
|
184
|
+
matTooltip="Move up"
|
|
185
|
+
[attr.data-testid]="'rule-up-' + rule.id"
|
|
186
|
+
(click)="store.moveRule(rule.id!, -1)"
|
|
187
|
+
>
|
|
188
|
+
<mat-icon>arrow_upward</mat-icon>
|
|
189
|
+
</button>
|
|
190
|
+
<button
|
|
191
|
+
mat-icon-button
|
|
192
|
+
type="button"
|
|
193
|
+
matTooltip="Move down"
|
|
194
|
+
[attr.data-testid]="'rule-down-' + rule.id"
|
|
195
|
+
(click)="store.moveRule(rule.id!, 1)"
|
|
196
|
+
>
|
|
197
|
+
<mat-icon>arrow_downward</mat-icon>
|
|
198
|
+
</button>
|
|
199
|
+
<button
|
|
200
|
+
mat-icon-button
|
|
201
|
+
type="button"
|
|
202
|
+
matTooltip="Edit rule"
|
|
203
|
+
[attr.data-testid]="'rule-edit-' + rule.id"
|
|
204
|
+
(click)="startEdit(rule)"
|
|
205
|
+
>
|
|
206
|
+
<mat-icon>edit</mat-icon>
|
|
207
|
+
</button>
|
|
208
|
+
<button
|
|
209
|
+
mat-icon-button
|
|
210
|
+
type="button"
|
|
211
|
+
color="warn"
|
|
212
|
+
matTooltip="Delete rule"
|
|
213
|
+
[attr.data-testid]="'rule-delete-' + rule.id"
|
|
214
|
+
(click)="store.removeRule(rule.id!)"
|
|
215
|
+
>
|
|
216
|
+
<mat-icon>delete</mat-icon>
|
|
217
|
+
</button>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
}
|
|
221
|
+
</div>
|
|
222
|
+
`, styles: [".deb-rules{display:flex;flex-direction:column;gap:8px}.deb-rules__head{display:flex;align-items:center;justify-content:space-between}.deb-rule-item{display:flex;align-items:center;justify-content:space-between;gap:8px;border:1px solid #e5e7eb;border-radius:8px;padding:8px 10px}.deb-rule-item--off{opacity:.55}.deb-rule-item__body{display:flex;flex-direction:column;min-width:0}.deb-rule-item__summary{font-size:13px;font-weight:600;overflow-wrap:anywhere}.deb-rule-item__meta{font-size:11px;color:#6b7280}.deb-rule-item__actions{display:flex;align-items:center;flex:0 0 auto}\n"] }]
|
|
223
|
+
}] });
|
|
224
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmllbGQtcnVsZXMtbGlzdC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvZmllbGQtcnVsZXMtbGlzdC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzFELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDdEUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFN0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDckMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7Ozs7OztBQUUxRDs7O0dBR0c7QUFrSUgsTUFBTSxPQUFPLHVCQUF1QjtJQUNmLEtBQUssR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7SUFFaEQseUZBQXlGO0lBQ3RFLE9BQU8sR0FBRyxNQUFNLENBQWtCLElBQUksQ0FBQyxDQUFDO0lBRWpELFdBQVc7UUFDbkIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxlQUFlLEVBQUUsQ0FBQztRQUM3QyxJQUFJLENBQUMsT0FBTztZQUFFLE9BQU87UUFDckIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUM7WUFDZixZQUFZLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxNQUFNLElBQUksUUFBUTtZQUNwRCxPQUFPO1lBQ1AsVUFBVSxFQUFFLENBQUMsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRSxDQUFDO1lBQ3BFLE1BQU0sRUFBRSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRTtZQUM1QyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFDO1lBQ3pDLE9BQU8sRUFBRSxJQUFJO1lBQ2IsUUFBUSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUMsTUFBTSxHQUFHLENBQUM7U0FDeEMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVTLFNBQVMsQ0FBQyxJQUFjO1FBQ2hDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFFUyxNQUFNLENBQUMsSUFBYztRQUM3QixJQUFJLElBQUksQ0FBQyxFQUFFO1lBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsRUFBRSxJQUFJLENBQUMsQ0FBQzs7WUFDN0MsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDOUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDekIsQ0FBQztJQUVELGdFQUFnRTtJQUN0RCxTQUFTLENBQUMsSUFBYztRQUNoQyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsVUFBVTthQUN6QixHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUU7WUFDUCxNQUFNLEdBQUcsR0FBRyxDQUFDLENBQUMsV0FBVyxLQUFLLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQ3pGLE9BQU8sR0FBRyxDQUFDLENBQUMsUUFBUSxJQUFJLEdBQUcsSUFBSSxFQUFFLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUM3QyxDQUFDLENBQUM7YUFDRCxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFakIsTUFBTSxNQUFNLEdBQ1YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssWUFBWTtZQUMvQixDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEtBQUssS0FBSyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxLQUFLLE9BQU87Z0JBQzVELENBQUMsQ0FBQyxNQUFNO2dCQUNSLENBQUMsQ0FBQyxNQUFNO1lBQ1YsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUVsRCxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLE1BQU0sTUFBTSxFQUFFLENBQUM7SUFDL0MsQ0FBQzt3R0EvQ1UsdUJBQXVCOzRGQUF2Qix1QkFBdUIsZ0ZBN0h4Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBOEVULDJvQkEvRVMsZUFBZSx3VUFBRSxhQUFhLG1MQUFFLG9CQUFvQixrV0FBRSxnQkFBZ0IsNlRBQUUsaUJBQWlCOzs0RkE4SHhGLHVCQUF1QjtrQkFqSW5DLFNBQVM7K0JBQ0Usc0JBQXNCLGNBQ3BCLElBQUksV0FDUCxDQUFDLGVBQWUsRUFBRSxhQUFhLEVBQUUsb0JBQW9CLEVBQUUsZ0JBQWdCLEVBQUUsaUJBQWlCLENBQUMsWUFDMUY7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQThFVCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgaW5qZWN0LCBzaWduYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgTWF0QnV0dG9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvYnV0dG9uJztcclxuaW1wb3J0IHsgTWF0SWNvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2ljb24nO1xyXG5pbXBvcnQgeyBNYXRTbGlkZVRvZ2dsZU1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3NsaWRlLXRvZ2dsZSc7XHJcbmltcG9ydCB7IE1hdFRvb2x0aXBNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC90b29sdGlwJztcclxuaW1wb3J0IHR5cGUgeyBGb3JtUnVsZSB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcclxuaW1wb3J0IHsgQnVpbGRlclN0b3JlIH0gZnJvbSAnLi4vYnVpbGRlci1zdG9yZS5zZXJ2aWNlJztcclxuaW1wb3J0IHsgZGVlcENsb25lIH0gZnJvbSAnLi4vY2xvbmUnO1xyXG5pbXBvcnQgeyBSdWxlRm9ybUNvbXBvbmVudCB9IGZyb20gJy4vcnVsZS1mb3JtLmNvbXBvbmVudCc7XHJcblxyXG4vKipcclxuICogRmllbGRSdWxlc0xpc3RDb21wb25lbnQg4oCUIHRoZSBydWxlcyBhdHRhY2hlZCB0byB0aGUgc2VsZWN0ZWQgZmllbGQ6IHZpZXcsIHJlb3JkZXIsXHJcbiAqIGVuYWJsZS9kaXNhYmxlLCBlZGl0LCBkZWxldGUuIGBSdWxlRm9ybUNvbXBvbmVudGAgYXV0aG9ycyBvbmUgcnVsZTsgdGhpcyBvd25zIHRoZSBzZXQuXHJcbiAqL1xyXG5AQ29tcG9uZW50KHtcclxuICBzZWxlY3RvcjogJ25neC1maWVsZC1ydWxlcy1saXN0JyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtNYXRCdXR0b25Nb2R1bGUsIE1hdEljb25Nb2R1bGUsIE1hdFNsaWRlVG9nZ2xlTW9kdWxlLCBNYXRUb29sdGlwTW9kdWxlLCBSdWxlRm9ybUNvbXBvbmVudF0sXHJcbiAgdGVtcGxhdGU6IGBcclxuICAgIDxkaXYgY2xhc3M9XCJkZWItcnVsZXNcIj5cclxuICAgICAgPGRpdiBjbGFzcz1cImRlYi1ydWxlc19faGVhZFwiPlxyXG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLXNlY3Rpb24tdGl0bGVcIj5SdWxlczwvc3Bhbj5cclxuICAgICAgICA8YnV0dG9uXHJcbiAgICAgICAgICBtYXQtc3Ryb2tlZC1idXR0b25cclxuICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxyXG4gICAgICAgICAgZGF0YS10ZXN0aWQ9XCJhZGQtcnVsZVwiXHJcbiAgICAgICAgICBbZGlzYWJsZWRdPVwiIXN0b3JlLnNlbGVjdGVkRmllbGRJZCgpXCJcclxuICAgICAgICAgIChjbGljayk9XCJzdGFydENyZWF0ZSgpXCJcclxuICAgICAgICA+XHJcbiAgICAgICAgICA8bWF0LWljb24+YWRkPC9tYXQtaWNvbj4gUnVsZVxyXG4gICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICA8L2Rpdj5cclxuXHJcbiAgICAgIEBpZiAoZWRpdGluZygpOyBhcyBkcmFmdCkge1xyXG4gICAgICAgIDxuZ3gtcnVsZS1mb3JtIFtydWxlXT1cImRyYWZ0XCIgKHNhdmUpPVwiY29tbWl0KCRldmVudClcIiAoY2FuY2VsKT1cImVkaXRpbmcuc2V0KG51bGwpXCIgLz5cclxuICAgICAgfVxyXG5cclxuICAgICAgQGlmIChzdG9yZS5ydWxlc0ZvclNlbGVjdGVkRmllbGQoKS5sZW5ndGggPT09IDAgJiYgIWVkaXRpbmcoKSkge1xyXG4gICAgICAgIDxwIGNsYXNzPVwiZGViLWhpbnRcIiBkYXRhLXRlc3RpZD1cInJ1bGVzLWVtcHR5XCI+Tm8gcnVsZXMgb24gdGhpcyBmaWVsZC48L3A+XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIEBmb3IgKHJ1bGUgb2Ygc3RvcmUucnVsZXNGb3JTZWxlY3RlZEZpZWxkKCk7IHRyYWNrIHJ1bGUuaWQpIHtcclxuICAgICAgICA8ZGl2IGNsYXNzPVwiZGViLXJ1bGUtaXRlbVwiIFtjbGFzcy5kZWItcnVsZS1pdGVtLS1vZmZdPVwiIXJ1bGUuZW5hYmxlZFwiIGRhdGEtdGVzdGlkPVwicnVsZS1pdGVtXCI+XHJcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiZGViLXJ1bGUtaXRlbV9fYm9keVwiPlxyXG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImRlYi1ydWxlLWl0ZW1fX3N1bW1hcnlcIj57eyBzdW1tYXJpemUocnVsZSkgfX08L3NwYW4+XHJcbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLXJ1bGUtaXRlbV9fbWV0YVwiPlxyXG4gICAgICAgICAgICAgIHByaW9yaXR5IHt7IHJ1bGUucHJpb3JpdHkgfX0gwrcge3sgcnVsZS50YXJnZXRzLmxlbmd0aCB9fSB0YXJnZXQocylcclxuICAgICAgICAgICAgPC9zcGFuPlxyXG4gICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiZGViLXJ1bGUtaXRlbV9fYWN0aW9uc1wiPlxyXG4gICAgICAgICAgICA8bWF0LXNsaWRlLXRvZ2dsZVxyXG4gICAgICAgICAgICAgIFtjaGVja2VkXT1cInJ1bGUuZW5hYmxlZFwiXHJcbiAgICAgICAgICAgICAgbWF0VG9vbHRpcD1cIkVuYWJsZSAvIGRpc2FibGVcIlxyXG4gICAgICAgICAgICAgIChjaGFuZ2UpPVwic3RvcmUudG9nZ2xlUnVsZShydWxlLmlkISwgJGV2ZW50LmNoZWNrZWQpXCJcclxuICAgICAgICAgICAgLz5cclxuICAgICAgICAgICAgPGJ1dHRvblxyXG4gICAgICAgICAgICAgIG1hdC1pY29uLWJ1dHRvblxyXG4gICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxyXG4gICAgICAgICAgICAgIG1hdFRvb2x0aXA9XCJNb3ZlIHVwXCJcclxuICAgICAgICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCIncnVsZS11cC0nICsgcnVsZS5pZFwiXHJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cInN0b3JlLm1vdmVSdWxlKHJ1bGUuaWQhLCAtMSlcIlxyXG4gICAgICAgICAgICA+XHJcbiAgICAgICAgICAgICAgPG1hdC1pY29uPmFycm93X3Vwd2FyZDwvbWF0LWljb24+XHJcbiAgICAgICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICAgICAgICA8YnV0dG9uXHJcbiAgICAgICAgICAgICAgbWF0LWljb24tYnV0dG9uXHJcbiAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXHJcbiAgICAgICAgICAgICAgbWF0VG9vbHRpcD1cIk1vdmUgZG93blwiXHJcbiAgICAgICAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3J1bGUtZG93bi0nICsgcnVsZS5pZFwiXHJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cInN0b3JlLm1vdmVSdWxlKHJ1bGUuaWQhLCAxKVwiXHJcbiAgICAgICAgICAgID5cclxuICAgICAgICAgICAgICA8bWF0LWljb24+YXJyb3dfZG93bndhcmQ8L21hdC1pY29uPlxyXG4gICAgICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgICAgICAgPGJ1dHRvblxyXG4gICAgICAgICAgICAgIG1hdC1pY29uLWJ1dHRvblxyXG4gICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxyXG4gICAgICAgICAgICAgIG1hdFRvb2x0aXA9XCJFZGl0IHJ1bGVcIlxyXG4gICAgICAgICAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidydWxlLWVkaXQtJyArIHJ1bGUuaWRcIlxyXG4gICAgICAgICAgICAgIChjbGljayk9XCJzdGFydEVkaXQocnVsZSlcIlxyXG4gICAgICAgICAgICA+XHJcbiAgICAgICAgICAgICAgPG1hdC1pY29uPmVkaXQ8L21hdC1pY29uPlxyXG4gICAgICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgICAgICAgPGJ1dHRvblxyXG4gICAgICAgICAgICAgIG1hdC1pY29uLWJ1dHRvblxyXG4gICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxyXG4gICAgICAgICAgICAgIGNvbG9yPVwid2FyblwiXHJcbiAgICAgICAgICAgICAgbWF0VG9vbHRpcD1cIkRlbGV0ZSBydWxlXCJcclxuICAgICAgICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCIncnVsZS1kZWxldGUtJyArIHJ1bGUuaWRcIlxyXG4gICAgICAgICAgICAgIChjbGljayk9XCJzdG9yZS5yZW1vdmVSdWxlKHJ1bGUuaWQhKVwiXHJcbiAgICAgICAgICAgID5cclxuICAgICAgICAgICAgICA8bWF0LWljb24+ZGVsZXRlPC9tYXQtaWNvbj5cclxuICAgICAgICAgICAgPC9idXR0b24+XHJcbiAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICA8L2Rpdj5cclxuICAgICAgfVxyXG4gICAgPC9kaXY+XHJcbiAgYCxcclxuICBzdHlsZXM6IFtcclxuICAgIGBcclxuICAgICAgLmRlYi1ydWxlcyB7XHJcbiAgICAgICAgZGlzcGxheTogZmxleDtcclxuICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xyXG4gICAgICAgIGdhcDogOHB4O1xyXG4gICAgICB9XHJcbiAgICAgIC5kZWItcnVsZXNfX2hlYWQge1xyXG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XHJcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcclxuICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47XHJcbiAgICAgIH1cclxuICAgICAgLmRlYi1ydWxlLWl0ZW0ge1xyXG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XHJcbiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcclxuICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47XHJcbiAgICAgICAgZ2FwOiA4cHg7XHJcbiAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2U1ZTdlYjtcclxuICAgICAgICBib3JkZXItcmFkaXVzOiA4cHg7XHJcbiAgICAgICAgcGFkZGluZzogOHB4IDEwcHg7XHJcbiAgICAgIH1cclxuICAgICAgLmRlYi1ydWxlLWl0ZW0tLW9mZiB7XHJcbiAgICAgICAgb3BhY2l0eTogMC41NTtcclxuICAgICAgfVxyXG4gICAgICAuZGViLXJ1bGUtaXRlbV9fYm9keSB7XHJcbiAgICAgICAgZGlzcGxheTogZmxleDtcclxuICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xyXG4gICAgICAgIG1pbi13aWR0aDogMDtcclxuICAgICAgfVxyXG4gICAgICAuZGViLXJ1bGUtaXRlbV9fc3VtbWFyeSB7XHJcbiAgICAgICAgZm9udC1zaXplOiAxM3B4O1xyXG4gICAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7XHJcbiAgICAgICAgb3ZlcmZsb3ctd3JhcDogYW55d2hlcmU7XHJcbiAgICAgIH1cclxuICAgICAgLmRlYi1ydWxlLWl0ZW1fX21ldGEge1xyXG4gICAgICAgIGZvbnQtc2l6ZTogMTFweDtcclxuICAgICAgICBjb2xvcjogIzZiNzI4MDtcclxuICAgICAgfVxyXG4gICAgICAuZGViLXJ1bGUtaXRlbV9fYWN0aW9ucyB7XHJcbiAgICAgICAgZGlzcGxheTogZmxleDtcclxuICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyO1xyXG4gICAgICAgIGZsZXg6IDAgMCBhdXRvO1xyXG4gICAgICB9XHJcbiAgICBgLFxyXG4gIF0sXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBGaWVsZFJ1bGVzTGlzdENvbXBvbmVudCB7XHJcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHN0b3JlID0gaW5qZWN0KEJ1aWxkZXJTdG9yZSk7XHJcblxyXG4gIC8qKiBUaGUgcnVsZSBjdXJyZW50bHkgb3BlbiBpbiB0aGUgZWRpdG9yIOKAlCBhIG5ldyBkcmFmdCBvciBhIGNvcHkgb2YgYW4gZXhpc3RpbmcgcnVsZS4gKi9cclxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZWRpdGluZyA9IHNpZ25hbDxGb3JtUnVsZSB8IG51bGw+KG51bGwpO1xyXG5cclxuICBwcm90ZWN0ZWQgc3RhcnRDcmVhdGUoKTogdm9pZCB7XHJcbiAgICBjb25zdCBmaWVsZElkID0gdGhpcy5zdG9yZS5zZWxlY3RlZEZpZWxkSWQoKTtcclxuICAgIGlmICghZmllbGRJZCkgcmV0dXJuO1xyXG4gICAgdGhpcy5lZGl0aW5nLnNldCh7XHJcbiAgICAgIGZvcm1Db25maWdJZDogdGhpcy5zdG9yZS5jb25maWcoKS5lbnRpdHkgfHwgJ2Zvcm0tMScsXHJcbiAgICAgIGZpZWxkSWQsXHJcbiAgICAgIGNvbmRpdGlvbnM6IFt7IG9wZXJhdG9yOiAnRVFVQUwnLCBjb21wYXJlVHlwZTogJ3ZhbHVlJywgdmFsdWU6ICcnIH1dLFxyXG4gICAgICBhY3Rpb246IHsgdHlwZTogJ3Zpc2liaWxpdHknLCB2YWx1ZTogZmFsc2UgfSxcclxuICAgICAgdGFyZ2V0czogW3sgaWQ6IGZpZWxkSWQsIHR5cGU6ICdmaWVsZCcgfV0sXHJcbiAgICAgIGVuYWJsZWQ6IHRydWUsXHJcbiAgICAgIHByaW9yaXR5OiB0aGlzLnN0b3JlLnJ1bGVzKCkubGVuZ3RoICsgMSxcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgcHJvdGVjdGVkIHN0YXJ0RWRpdChydWxlOiBGb3JtUnVsZSk6IHZvaWQge1xyXG4gICAgdGhpcy5lZGl0aW5nLnNldChkZWVwQ2xvbmUocnVsZSkpO1xyXG4gIH1cclxuXHJcbiAgcHJvdGVjdGVkIGNvbW1pdChydWxlOiBGb3JtUnVsZSk6IHZvaWQge1xyXG4gICAgaWYgKHJ1bGUuaWQpIHRoaXMuc3RvcmUudXBkYXRlUnVsZShydWxlLmlkLCBydWxlKTtcclxuICAgIGVsc2UgdGhpcy5zdG9yZS5hZGRSdWxlKHJ1bGUpO1xyXG4gICAgdGhpcy5lZGl0aW5nLnNldChudWxsKTtcclxuICB9XHJcblxyXG4gIC8qKiBPbmUtbGluZSBodW1hbiBzdW1tYXJ5OiBgc3RhdHVzIEVRVUFMIFwiYXJjaGl2ZWRcIiDihpIgaGlkZWAuICovXHJcbiAgcHJvdGVjdGVkIHN1bW1hcml6ZShydWxlOiBGb3JtUnVsZSk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBjb25kID0gcnVsZS5jb25kaXRpb25zXHJcbiAgICAgIC5tYXAoYyA9PiB7XHJcbiAgICAgICAgY29uc3QgcmhzID0gYy5jb21wYXJlVHlwZSA9PT0gJ2ZpZWxkJyA/IGMuY29tcGFyZVRvRmllbGQgOiBKU09OLnN0cmluZ2lmeShjLnZhbHVlID8/ICcnKTtcclxuICAgICAgICByZXR1cm4gYCR7Yy5vcGVyYXRvcn0gJHtyaHMgPz8gJyd9YC50cmltKCk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5qb2luKCcgQU5EICcpO1xyXG5cclxuICAgIGNvbnN0IGFjdGlvbiA9XHJcbiAgICAgIHJ1bGUuYWN0aW9uLnR5cGUgPT09ICd2aXNpYmlsaXR5J1xyXG4gICAgICAgID8gcnVsZS5hY3Rpb24udmFsdWUgPT09IGZhbHNlIHx8IHJ1bGUuYWN0aW9uLnZhbHVlID09PSAnZmFsc2UnXHJcbiAgICAgICAgICA/ICdoaWRlJ1xyXG4gICAgICAgICAgOiAnc2hvdydcclxuICAgICAgICA6IGAke3J1bGUuYWN0aW9uLnR5cGV9OiAke3J1bGUuYWN0aW9uLnZhbHVlfWA7XHJcblxyXG4gICAgcmV0dXJuIGAke3J1bGUuZmllbGRJZH0gJHtjb25kfSDihpIgJHthY3Rpb259YDtcclxuICB9XHJcbn1cclxuIl19
|