@nova-ui/dashboards
Version:
Nova Dashboards is a framework designed to provide feature developers with a common solution for presenting data coming from various sources within a single view, as well as a set of predefined widget visualizations that are 100% configuration-driven and
148 lines • 36.4 kB
JavaScript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewEncapsulation, } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
// eslint-disable-next-line import/no-deprecated
import { startWith, takeUntil } from "rxjs/operators";
import { LoggerService } from "@nova-ui/bits";
import { BaseLayout } from "../../../components/layouts/base-layout";
import { PizzagnaService } from "../../../pizzagna/services/pizzagna.service";
import * as i0 from "@angular/core";
import * as i1 from "../../../pizzagna/services/pizzagna.service";
import * as i2 from "@nova-ui/bits";
import * as i3 from "@angular/forms";
import * as i4 from "@angular/common";
import * as i5 from "@angular/cdk/drag-drop";
import * as i6 from "@angular/cdk/portal";
import * as i7 from "../../../pizzagna/directives/component-portal/component-portal.directive";
import * as i8 from "../drop-area/drop-area.component";
import * as i9 from "../../pipe/add-data.pipe";
import * as i10 from "../../pipe/widget-configurator-section-header.pipe";
/** @ignore */
export class ItemsDynamicComponent extends BaseLayout {
static { this.lateLoadKey = "ItemsDynamicComponent"; }
constructor(changeDetector, pizzagnaService, logger, formBuilder) {
super(changeDetector, pizzagnaService, logger);
this.formBuilder = formBuilder;
this.items = [];
this.moveButtons = true;
this.formReady = new EventEmitter();
this.itemsChange = new EventEmitter();
this.headerMap = new Map();
}
getItemForNode(node) {
return this.items.find((item) => item.id === node.id);
}
getNodes() {
return this.nodes;
}
ngOnInit() {
this.form = this.formBuilder.array([], Validators.required);
this.formReady.emit(this.form);
}
onEvent(componentId, event) {
// TODO: refactor
const item = this.items.find((i) => i.id === componentId);
const index = this.items.findIndex((i) => i.id === componentId);
if (event.id === "formReady" && item) {
this.onFormReady(item, event.payload, index);
}
if (event.id === "formDestroy") {
this.onFormDestroy(event.payload);
}
}
onFormReady(item, form, index) {
const childForm = this.formBuilder.group({
id: [item.id],
componentType: [item.componentType],
properties: form,
});
this.form.setControl(index, childForm);
setTimeout(() => {
const label = form.get(`${item.id}/description`)?.get("label");
label?.valueChanges
// eslint-disable-next-line import/no-deprecated
.pipe(startWith(label.value), takeUntil(this.destroyed$))
.subscribe((value) => {
this.headerMap.set(item.id, value || "");
this.changeDetector.markForCheck();
});
});
}
onFormDestroy(form) {
// using setTimeout to allow the change detection cycle to finish before updating the form
setTimeout(() => {
// remove form
let i = 0;
for (; i < this.form.length; i++) {
if (this.form.at(i).get("properties") === form) {
break;
}
}
this.form.removeAt(i);
// triggers form change
this.form.patchValue(this.form.value, { emitEvent: true });
});
}
trackBy(index, item) {
return item.id;
}
removeItem(item, index) {
if (!item) {
throw new Error("Unable to remove undefined item from pizzagna");
}
// remove item configuration - this will cause the onFormDestroy to be called for related form after component disappears from the view
this.itemsChange.emit(this.items.filter((it) => it !== item));
this.items.splice(index, 1);
this.nodes.splice(index, 1);
this.pizzagnaService.removeComponents(item.id);
}
moveItem(index, toIndex) {
const items = [...this.items];
const item = items.splice(index, 1);
items.splice(toIndex, 0, ...item);
const nodes = [...this.nodes];
const node = nodes.splice(index, 1);
nodes.splice(toIndex, 0, ...node);
this.nodes = [...nodes];
this.moveFormValues(index, toIndex);
this.itemsChange.emit(items);
// triggers form change - setTimeout because we need to wait first for items to update
// console.log(this.form.value);
setTimeout(() => {
this.form.setValue(this.form.value, { emitEvent: true });
});
}
drop(event) {
this.moveItem(event.previousIndex, event.currentIndex);
}
cdkDragStarted(event) {
this.height = event.source.element.nativeElement.offsetHeight;
}
moveFormValues(index, toIndex) {
const oldValue = this.form.at(index);
this.form.removeAt(index);
this.form.insert(toIndex, oldValue);
}
ngOnDestroy() {
// Ensures that any base class observables are unsubscribed.
super.ngOnDestroy();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ItemsDynamicComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.PizzagnaService }, { token: i2.LoggerService }, { token: i3.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ItemsDynamicComponent, selector: "nui-items-dynamic", inputs: { items: "items", moveButtons: "moveButtons", nodes: "nodes", headerPrefix: "headerPrefix" }, outputs: { formReady: "formReady", itemsChange: "itemsChange" }, usesInheritance: true, ngImport: i0, template: "<div\n class=\"nui-items-dynamic\"\n cdkDropList\n (cdkDropListDropped)=\"drop($event)\"\n [formGroup]=\"form\"\n>\n <div\n cdkDrag\n class=\"nui-items-dynamic__node-item\"\n (cdkDragStarted)=\"cdkDragStarted($event)\"\n *ngFor=\"let node of nodeConfigs; let i = index; trackBy: trackBy\"\n >\n <nui-drop-area\n class=\"nui-items-dynamic__drop-area\"\n *cdkDragPlaceholder\n [style.height.px]=\"height\"\n ></nui-drop-area>\n\n <ng-template #headerText>\n <ng-container *ngIf=\"moveButtons\">\n <div class=\"nui-items-dynamic__drag-handler\" cdkDragHandle>\n <nui-icon icon=\"drag\"></nui-icon>\n </div>\n </ng-container>\n <span\n [title]=\"\n headerMap.get(node.id)\n | nuiWidgetConfiguratorSectionHeader : i : headerPrefix\n \"\n class=\"nui-items-dynamic__header-text\"\n >\n {{\n headerMap.get(node.id)\n | nuiWidgetConfiguratorSectionHeader : i : headerPrefix\n }}\n </span>\n </ng-template>\n\n <ng-template #headerButtons>\n <div class=\"d-flex\">\n <!-- TODO: remove \"kpi\" prefix in v11 - NUI-5833 -->\n <button\n id=\"kpi-widget-remove-btn-{{ i }}\"\n nui-button\n type=\"button\"\n icon=\"delete\"\n displayStyle=\"action\"\n (click)=\"removeItem(getItemForNode(node), i)\"\n ></button>\n </div>\n </ng-template>\n\n <ng-container\n nuiComponentPortal\n #componentPortal=\"nuiComponentPortal\"\n [componentId]=\"node.id\"\n [componentType]=\"node.componentType\"\n [providers]=\"node.providers\"\n [properties]=\"\n node.properties\n | nuiAddData : headerText : 'headerTextTemplate'\n | nuiAddData : headerButtons : 'headerButtonsTemplate'\n \"\n [outputs]=\"['formReady', 'formDestroy']\"\n (output)=\"onEvent(node.id, $event)\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n >\n </ng-template>\n </ng-container>\n </div>\n</div>\n", styles: [".nui-items-dynamic__header-text{white-space:nowrap;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}.nui-items-dynamic__drag-handler{cursor:move;display:flex}.nui-items-dynamic__node-item{background-color:var(--nui-color-bg-light,#fff);margin:15px}.nui-items-dynamic__node-item .widget-configurator-section{margin:0}.nui-items-dynamic__drop-area{margin:15px}.kpi-indicators-configuration__color-preview{width:16px;height:16px;margin-right:15px;border-radius:3px}.cdk-drag-preview{z-index:2000!important;opacity:.9;box-shadow:0 0 5px 0 var(--nui-shadow-color, rgba(17, 17, 17, .3))}\n"], dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i5.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: i5.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "directive", type: i6.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "directive", type: i7.ComponentPortalDirective, selector: "[nuiComponentPortal]", inputs: ["componentId", "componentType", "properties", "providers", "outputs"], outputs: ["output"], exportAs: ["nuiComponentPortal"] }, { kind: "component", type: i2.IconComponent, selector: "nui-icon", inputs: ["iconColor", "brushType", "iconHoverColor", "iconSize", "cssClass", "fillContainer", "status", "childStatus", "icon", "counter"] }, { kind: "component", type: i2.ButtonComponent, selector: "[nui-button]", inputs: ["displayStyle", "icon", "iconColor", "iconRight", "isBusy", "isEmpty", "ariaLabel", "isRepeat", "size"] }, { kind: "component", type: i8.DropAreaComponent, selector: "nui-drop-area" }, { kind: "pipe", type: i9.AddDataPipe, name: "nuiAddData" }, { kind: "pipe", type: i10.WidgetConfiguratorSectionHeaderPipe, name: "nuiWidgetConfiguratorSectionHeader" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ItemsDynamicComponent, decorators: [{
type: Component,
args: [{ selector: "nui-items-dynamic", changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div\n class=\"nui-items-dynamic\"\n cdkDropList\n (cdkDropListDropped)=\"drop($event)\"\n [formGroup]=\"form\"\n>\n <div\n cdkDrag\n class=\"nui-items-dynamic__node-item\"\n (cdkDragStarted)=\"cdkDragStarted($event)\"\n *ngFor=\"let node of nodeConfigs; let i = index; trackBy: trackBy\"\n >\n <nui-drop-area\n class=\"nui-items-dynamic__drop-area\"\n *cdkDragPlaceholder\n [style.height.px]=\"height\"\n ></nui-drop-area>\n\n <ng-template #headerText>\n <ng-container *ngIf=\"moveButtons\">\n <div class=\"nui-items-dynamic__drag-handler\" cdkDragHandle>\n <nui-icon icon=\"drag\"></nui-icon>\n </div>\n </ng-container>\n <span\n [title]=\"\n headerMap.get(node.id)\n | nuiWidgetConfiguratorSectionHeader : i : headerPrefix\n \"\n class=\"nui-items-dynamic__header-text\"\n >\n {{\n headerMap.get(node.id)\n | nuiWidgetConfiguratorSectionHeader : i : headerPrefix\n }}\n </span>\n </ng-template>\n\n <ng-template #headerButtons>\n <div class=\"d-flex\">\n <!-- TODO: remove \"kpi\" prefix in v11 - NUI-5833 -->\n <button\n id=\"kpi-widget-remove-btn-{{ i }}\"\n nui-button\n type=\"button\"\n icon=\"delete\"\n displayStyle=\"action\"\n (click)=\"removeItem(getItemForNode(node), i)\"\n ></button>\n </div>\n </ng-template>\n\n <ng-container\n nuiComponentPortal\n #componentPortal=\"nuiComponentPortal\"\n [componentId]=\"node.id\"\n [componentType]=\"node.componentType\"\n [providers]=\"node.providers\"\n [properties]=\"\n node.properties\n | nuiAddData : headerText : 'headerTextTemplate'\n | nuiAddData : headerButtons : 'headerButtonsTemplate'\n \"\n [outputs]=\"['formReady', 'formDestroy']\"\n (output)=\"onEvent(node.id, $event)\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n >\n </ng-template>\n </ng-container>\n </div>\n</div>\n", styles: [".nui-items-dynamic__header-text{white-space:nowrap;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}.nui-items-dynamic__drag-handler{cursor:move;display:flex}.nui-items-dynamic__node-item{background-color:var(--nui-color-bg-light,#fff);margin:15px}.nui-items-dynamic__node-item .widget-configurator-section{margin:0}.nui-items-dynamic__drop-area{margin:15px}.kpi-indicators-configuration__color-preview{width:16px;height:16px;margin-right:15px;border-radius:3px}.cdk-drag-preview{z-index:2000!important;opacity:.9;box-shadow:0 0 5px 0 var(--nui-shadow-color, rgba(17, 17, 17, .3))}\n"] }]
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.PizzagnaService }, { type: i2.LoggerService }, { type: i3.FormBuilder }], propDecorators: { items: [{
type: Input
}], moveButtons: [{
type: Input
}], nodes: [{
type: Input
}], headerPrefix: [{
type: Input
}], formReady: [{
type: Output
}], itemsChange: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXRlbXMtZHluYW1pYy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL2l0ZW1zLWR5bmFtaWMvaXRlbXMtZHluYW1pYy5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL2l0ZW1zLWR5bmFtaWMvaXRlbXMtZHluYW1pYy5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFxQkEsT0FBTyxFQUNILHVCQUF1QixFQUN2QixpQkFBaUIsRUFDakIsU0FBUyxFQUNULFlBQVksRUFDWixLQUFLLEVBR0wsTUFBTSxFQUNOLGlCQUFpQixHQUNwQixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQWEsV0FBVyxFQUFhLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQy9FLGdEQUFnRDtBQUNoRCxPQUFPLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXRELE9BQU8sRUFBVSxhQUFhLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFdEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQzs7Ozs7Ozs7Ozs7O0FBUTlFLGNBQWM7QUFRZCxNQUFNLE9BQU8scUJBQ1QsU0FBUSxVQUFVO2FBR1gsZ0JBQVcsR0FBRyx1QkFBdUIsQUFBMUIsQ0FBMkI7SUFhN0MsWUFDSSxjQUFpQyxFQUNqQyxlQUFnQyxFQUNoQyxNQUFxQixFQUNiLFdBQXdCO1FBRWhDLEtBQUssQ0FBQyxjQUFjLEVBQUUsZUFBZSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBRnZDLGdCQUFXLEdBQVgsV0FBVyxDQUFhO1FBZjNCLFVBQUssR0FBeUIsRUFBRSxDQUFDO1FBQ2pDLGdCQUFX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QU0sR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsWUFBWSxDQUFDO0lBQ2xFLENBQUM7SUFFTyxjQUFjLENBQUMsS0FBYSxFQUFFLE9BQWU7UUFDakQsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDckMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDMUIsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQ3hDLENBQUM7SUFFTSxXQUFXO1FBQ2QsNERBQTREO1FBQzVELEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUN4QixDQUFDO2tJQXpKUSxxQkFBcUI7c0hBQXJCLHFCQUFxQix1UEN2RGxDLDhqRkEwRUE7OzRGRG5CYSxxQkFBcUI7a0JBUGpDLFNBQVM7K0JBQ0ksbUJBQW1CLG1CQUdaLHVCQUF1QixDQUFDLE1BQU0saUJBQ2hDLGlCQUFpQixDQUFDLElBQUk7MEtBUTVCLEtBQUs7c0JBQWIsS0FBSztnQkFDRyxXQUFXO3NCQUFuQixLQUFLO2dCQUNHLEtBQUs7c0JBQWIsS0FBSztnQkFDRyxZQUFZO3NCQUFwQixLQUFLO2dCQUNJLFNBQVM7c0JBQWxCLE1BQU07Z0JBQ0csV0FBVztzQkFBcEIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbIi8vIMKpIDIwMjIgU29sYXJXaW5kcyBXb3JsZHdpZGUsIExMQy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4vLyAgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG9cbi8vICBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZVxuLy8gIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vclxuLy8gIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzXG4vLyAgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpblxuLy8gIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1Jcbi8vICBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbi8vICBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbi8vICBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSXG4vLyAgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbi8vICBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4vLyAgVEhFIFNPRlRXQVJFLlxuXG5pbXBvcnQgeyBDZGtEcmFnRHJvcCwgQ2RrRHJhZ1N0YXJ0IH0gZnJvbSBcIkBhbmd1bGFyL2Nkay9kcmFnLWRyb3BcIjtcbmltcG9ydCB7XG4gICAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gICAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgQ29tcG9uZW50LFxuICAgIEV2ZW50RW1pdHRlcixcbiAgICBJbnB1dCxcbiAgICBPbkRlc3Ryb3ksXG4gICAgT25Jbml0LFxuICAgIE91dHB1dCxcbiAgICBWaWV3RW5jYXBzdWxhdGlvbixcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7IEZvcm1BcnJheSwgRm9ybUJ1aWxkZXIsIEZvcm1Hcm91cCwgVmFsaWRhdG9ycyB9IGZyb20gXCJAYW5ndWxhci9mb3Jtc1wiO1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1kZXByZWNhdGVkXG5pbXBvcnQgeyBzdGFydFdpdGgsIHRha2VVbnRpbCB9IGZyb20gXCJyeGpzL29wZXJhdG9yc1wiO1xuXG5pbXBvcnQgeyBJRXZlbnQsIExvZ2dlclNlcnZpY2UgfSBmcm9tIFwiQG5vdmEtdWkvYml0c1wiO1xuXG5pbXBvcnQgeyBCYXNlTGF5b3V0IH0gZnJvbSBcIi4uLy4uLy4uL2NvbXBvbmVudHMvbGF5b3V0cy9iYXNlLWxheW91dFwiO1xuaW1wb3J0IHsgUGl6emFnbmFTZXJ2aWNlIH0gZnJvbSBcIi4uLy4uLy4uL3BpenphZ25hL3NlcnZpY2VzL3BpenphZ25hLnNlcnZpY2VcIjtcbmltcG9ydCB7XG4gICAgSUNvbXBvbmVudENvbmZpZ3VyYXRpb24sXG4gICAgSUhhc0NoYW5nZURldGVjdG9yLFxuICAgIElIYXNGb3JtLFxufSBmcm9tIFwiLi4vLi4vLi4vdHlwZXNcIjtcbmltcG9ydCB7IElJdGVtQ29uZmlndXJhdGlvbiB9IGZyb20gXCIuLi90eXBlc1wiO1xuXG4vKiogQGlnbm9yZSAqL1xuQENvbXBvbmVudCh7XG4gICAgc2VsZWN0b3I6IFwibnVpLWl0ZW1zLWR5bmFtaWNcIixcbiAgICB0ZW1wbGF0ZVVybDogXCIuL2l0ZW1zLWR5bmFtaWMuY29tcG9uZW50Lmh0bWxcIixcbiAgICBzdHlsZVVybHM6IFtcIml0ZW1zLWR5bmFtaWMuY29tcG9uZW50Lmxlc3NcIl0sXG4gICAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG4gICAgZW5jYXBzdWxhdGlvbjogVmlld0VuY2Fwc3VsYXRpb24uTm9uZSxcbn0pXG5leHBvcnQgY2xhc3MgSXRlbXNEeW5hbWljQ29tcG9uZW50XG4gICAgZXh0ZW5kcyBCYXNlTGF5b3V0XG4gICAgaW1wbGVtZW50cyBJSGFzQ2hhbmdlRGV0ZWN0b3IsIElIYXNGb3JtPEZvcm1BcnJheT4sIE9uSW5pdCwgT25EZXN0cm95XG57XG4gICAgc3RhdGljIGxhdGVMb2FkS2V5ID0gXCJJdGVtc0R5bmFtaWNDb21wb25lbnRcIjtcblxuICAgIEBJbnB1dCgpIGl0ZW1zOiBJSXRlbUNvbmZpZ3VyYXRpb25bXSA9IFtdO1xuICAgIEBJbnB1dCgpIG1vdmVCdXR0b25zID0gdHJ1ZTtcbiAgICBASW5wdXQoKSBub2Rlczogc3RyaW5nW107XG4gICAgQElucHV0KCkgaGVhZGVyUHJlZml4OiBzdHJpbmc7XG4gICAgQE91dHB1dCgpIGZvcm1SZWFkeSA9IG5ldyBFdmVudEVtaXR0ZXI8Rm9ybUFycmF5PigpO1xuICAgIEBPdXRwdXQoKSBpdGVtc0NoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcblxuICAgIHB1YmxpYyBmb3JtOiBGb3JtQXJyYXk7XG4gICAgcHVibGljIGhlaWdodDogbnVtYmVyO1xuICAgIHB1YmxpYyBoZWFkZXJNYXA6IE1hcDxzdHJpbmcsIHN0cmluZz4gPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIGNoYW5nZURldGVjdG9yOiBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICAgICAgcGl6emFnbmFTZXJ2aWNlOiBQaXp6YWduYVNlcnZpY2UsXG4gICAgICAgIGxvZ2dlcjogTG9nZ2VyU2VydmljZSxcbiAgICAgICAgcHJpdmF0ZSBmb3JtQnVpbGRlcjogRm9ybUJ1aWxkZXJcbiAgICApIHtcbiAgICAgICAgc3VwZXIoY2hhbmdlRGV0ZWN0b3IsIHBpenphZ25hU2VydmljZSwgbG9nZ2VyKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0SXRlbUZvck5vZGUoXG4gICAgICAgIG5vZGU6IElDb21wb25lbnRDb25maWd1cmF0aW9uXG4gICAgKTogSUl0ZW1Db25maWd1cmF0aW9uIHwgdW5kZWZpbmVkIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuaXRlbXMuZmluZCgoaXRlbSkgPT4gaXRlbS5pZCA9PT0gbm9kZS5pZCk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldE5vZGVzKCk6IHN0cmluZ1tdIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubm9kZXM7XG4gICAgfVxuXG4gICAgcHVibGljIG5nT25Jbml0KCk6IHZvaWQge1xuICAgICAgICB0aGlzLmZvcm0gPSB0aGlzLmZvcm1CdWlsZGVyLmFycmF5KFtdLCBWYWxpZGF0b3JzLnJlcXVpcmVkKTtcbiAgICAgICAgdGhpcy5mb3JtUmVhZHkuZW1pdCh0aGlzLmZvcm0pO1xuICAgIH1cblxuICAgIHB1YmxpYyBvbkV2ZW50KGNvbXBvbmVudElkOiBzdHJpbmcsIGV2ZW50OiBJRXZlbnQpOiB2b2lkIHtcbiAgICAgICAgLy8gVE9ETzogcmVmYWN0b3JcbiAgICAgICAgY29uc3QgaXRlbSA9IHRoaXMuaXRlbXMuZmluZCgoaSkgPT4gaS5pZCA9PT0gY29tcG9uZW50SWQpO1xuICAgICAgICBjb25zdCBpbmRleCA9IHRoaXMuaXRlbXMuZmluZEluZGV4KChpKSA9PiBpLmlkID09PSBjb21wb25lbnRJZCk7XG5cbiAgICAgICAgaWYgKGV2ZW50LmlkID09PSBcImZvcm1SZWFkeVwiICYmIGl0ZW0pIHtcbiAgICAgICAgICAgIHRoaXMub25Gb3JtUmVhZHkoaXRlbSwgZXZlbnQucGF5bG9hZCwgaW5kZXgpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGV2ZW50LmlkID09PSBcImZvcm1EZXN0cm95XCIpIHtcbiAgICAgICAgICAgIHRoaXMub25Gb3JtRGVzdHJveShldmVudC5wYXlsb2FkKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBvbkZvcm1SZWFkeShcbiAgICAgICAgaXRlbTogSUl0ZW1Db25maWd1cmF0aW9uLFxuICAgICAgICBmb3JtOiBGb3JtR3JvdXAsXG4gICAgICAgIGluZGV4OiBudW1iZXJcbiAgICApOiB2b2lkIHtcbiAgICAgICAgY29uc3QgY2hpbGRGb3JtID0gdGhpcy5mb3JtQnVpbGRlci5ncm91cCh7XG4gICAgICAgICAgICBpZDogW2l0ZW0uaWRdLFxuICAgICAgICAgICAgY29tcG9uZW50VHlwZTogW2l0ZW0uY29tcG9uZW50VHlwZV0sXG4gICAgICAgICAgICBwcm9wZXJ0aWVzOiBmb3JtLFxuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmZvcm0uc2V0Q29udHJvbChpbmRleCwgY2hpbGRGb3JtKTtcblxuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGxhYmVsID0gZm9ybS5nZXQoYCR7aXRlbS5pZH0vZGVzY3JpcHRpb25gKT8uZ2V0KFwibGFiZWxcIik7XG4gICAgICAgICAgICBsYWJlbD8udmFsdWVDaGFuZ2VzXG4gICAgICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1kZXByZWNhdGVkXG4gICAgICAgICAgICAgICAgLnBpcGUoc3RhcnRXaXRoKGxhYmVsLnZhbHVlKSwgdGFrZVVudGlsKHRoaXMuZGVzdHJveWVkJCkpXG4gICAgICAgICAgICAgICAgLnN1YnNjcmliZSgodmFsdWU6IHN0cmluZykgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmhlYWRlck1hcC5zZXQoaXRlbS5pZCwgdmFsdWUgfHwgXCJcIik7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY2hhbmdlRGV0ZWN0b3IubWFya0ZvckNoZWNrKCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHB1YmxpYyBvbkZvcm1EZXN0cm95KGZvcm06IEZvcm1Hcm91cCk6IHZvaWQge1xuICAgICAgICAvLyB1c2luZyBzZXRUaW1lb3V0IHRvIGFsbG93IHRoZSBjaGFuZ2UgZGV0ZWN0aW9uIGN5Y2xlIHRvIGZpbmlzaCBiZWZvcmUgdXBkYXRpbmcgdGhlIGZvcm1cbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAvLyByZW1vdmUgZm9ybVxuICAgICAgICAgICAgbGV0IGkgPSAwO1xuICAgICAgICAgICAgZm9yICg7IGkgPCB0aGlzLmZvcm0ubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5mb3JtLmF0KGkpLmdldChcInByb3BlcnRpZXNcIikgPT09IGZvcm0pIHtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0aGlzLmZvcm0ucmVtb3ZlQXQoaSk7XG4gICAgICAgICAgICAvLyB0cmlnZ2VycyBmb3JtIGNoYW5nZVxuICAgICAgICAgICAgdGhpcy5mb3JtLnBhdGNoVmFsdWUodGhpcy5mb3JtLnZhbHVlLCB7IGVtaXRFdmVudDogdHJ1ZSB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHVibGljIHRyYWNrQnkoaW5kZXg6IG51bWJlciwgaXRlbTogSUl0ZW1Db25maWd1cmF0aW9uKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIGl0ZW0uaWQ7XG4gICAgfVxuXG4gICAgcHVibGljIHJlbW92ZUl0ZW0oXG4gICAgICAgIGl0ZW06IElJdGVtQ29uZmlndXJhdGlvbiB8IHVuZGVmaW5lZCxcbiAgICAgICAgaW5kZXg6IG51bWJlclxuICAgICk6IHZvaWQge1xuICAgICAgICBpZiAoIWl0ZW0pIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIlVuYWJsZSB0byByZW1vdmUgdW5kZWZpbmVkIGl0ZW0gZnJvbSBwaXp6YWduYVwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHJlbW92ZSBpdGVtIGNvbmZpZ3VyYXRpb24gLSB0aGlzIHdpbGwgY2F1c2UgdGhlIG9uRm9ybURlc3Ryb3kgdG8gYmUgY2FsbGVkIGZvciByZWxhdGVkIGZvcm0gYWZ0ZXIgY29tcG9uZW50IGRpc2FwcGVhcnMgZnJvbSB0aGUgdmlld1xuICAgICAgICB0aGlzLml0ZW1zQ2hhbmdlLmVtaXQodGhpcy5pdGVtcy5maWx0ZXIoKGl0KSA9PiBpdCAhPT0gaXRlbSkpO1xuICAgICAgICB0aGlzLml0ZW1zLnNwbGljZShpbmRleCwgMSk7XG5cbiAgICAgICAgdGhpcy5ub2Rlcy5zcGxpY2UoaW5kZXgsIDEpO1xuICAgICAgICB0aGlzLnBpenphZ25hU2VydmljZS5yZW1vdmVDb21wb25lbnRzKGl0ZW0uaWQpO1xuICAgIH1cblxuICAgIHB1YmxpYyBtb3ZlSXRlbShpbmRleDogbnVtYmVyLCB0b0luZGV4OiBudW1iZXIpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgaXRlbXMgPSBbLi4udGhpcy5pdGVtc107XG4gICAgICAgIGNvbnN0IGl0ZW0gPSBpdGVtcy5zcGxpY2UoaW5kZXgsIDEpO1xuICAgICAgICBpdGVtcy5zcGxpY2UodG9JbmRleCwgMCwgLi4uaXRlbSk7XG5cbiAgICAgICAgY29uc3Qgbm9kZXMgPSBbLi4udGhpcy5ub2Rlc107XG4gICAgICAgIGNvbnN0IG5vZGUgPSBub2Rlcy5zcGxpY2UoaW5kZXgsIDEpO1xuICAgICAgICBub2Rlcy5zcGxpY2UodG9JbmRleCwgMCwgLi4ubm9kZSk7XG4gICAgICAgIHRoaXMubm9kZXMgPSBbLi4ubm9kZXNdO1xuXG4gICAgICAgIHRoaXMubW92ZUZvcm1WYWx1ZXMoaW5kZXgsIHRvSW5kZXgpO1xuICAgICAgICB0aGlzLml0ZW1zQ2hhbmdlLmVtaXQoaXRlbXMpO1xuICAgICAgICAvLyB0cmlnZ2VycyBmb3JtIGNoYW5nZSAtIHNldFRpbWVvdXQgYmVjYXVzZSB3ZSBuZWVkIHRvIHdhaXQgZmlyc3QgZm9yIGl0ZW1zIHRvIHVwZGF0ZVxuICAgICAgICAvLyBjb25zb2xlLmxvZyh0aGlzLmZvcm0udmFsdWUpO1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuZm9ybS5zZXRWYWx1ZSh0aGlzLmZvcm0udmFsdWUsIHsgZW1pdEV2ZW50OiB0cnVlIH0pO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZHJvcChldmVudDogQ2RrRHJhZ0Ryb3A8c3RyaW5nW10+KTogdm9pZCB7XG4gICAgICAgIHRoaXMubW92ZUl0ZW0oZXZlbnQucHJldmlvdXNJbmRleCwgZXZlbnQuY3VycmVudEluZGV4KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgY2RrRHJhZ1N0YXJ0ZWQoZXZlbnQ6IENka0RyYWdTdGFydCk6IHZvaWQge1xuICAgICAgICB0aGlzLmhlaWdodCA9IGV2ZW50LnNvdXJjZS5lbGVtZW50Lm5hdGl2ZUVsZW1lbnQub2Zmc2V0SGVpZ2h0O1xuICAgIH1cblxuICAgIHByaXZhdGUgbW92ZUZvcm1WYWx1ZXMoaW5kZXg6IG51bWJlciwgdG9JbmRleDogbnVtYmVyKSB7XG4gICAgICAgIGNvbnN0IG9sZFZhbHVlID0gdGhpcy5mb3JtLmF0KGluZGV4KTtcbiAgICAgICAgdGhpcy5mb3JtLnJlbW92ZUF0KGluZGV4KTtcbiAgICAgICAgdGhpcy5mb3JtLmluc2VydCh0b0luZGV4LCBvbGRWYWx1ZSk7XG4gICAgfVxuXG4gICAgcHVibGljIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICAvLyBFbnN1cmVzIHRoYXQgYW55IGJhc2UgY2xhc3Mgb2JzZXJ2YWJsZXMgYXJlIHVuc3Vic2NyaWJlZC5cbiAgICAgICAgc3VwZXIubmdPbkRlc3Ryb3koKTtcbiAgICB9XG59XG4iLCI8ZGl2XG4gICAgY2xhc3M9XCJudWktaXRlbXMtZHluYW1pY1wiXG4gICAgY2RrRHJvcExpc3RcbiAgICAoY2RrRHJvcExpc3REcm9wcGVkKT1cImRyb3AoJGV2ZW50KVwiXG4gICAgW2Zvcm1Hcm91cF09XCJmb3JtXCJcbj5cbiAgICA8ZGl2XG4gICAgICAgIGNka0RyYWdcbiAgICAgICAgY2xhc3M9XCJudWktaXRlbXMtZHluYW1pY19fbm9kZS1pdGVtXCJcbiAgICAgICAgKGNka0RyYWdTdGFydGVkKT1cImNka0RyYWdTdGFydGVkKCRldmVudClcIlxuICAgICAgICAqbmdGb3I9XCJsZXQgbm9kZSBvZiBub2RlQ29uZmlnczsgbGV0IGkgPSBpbmRleDsgdHJhY2tCeTogdHJhY2tCeVwiXG4gICAgPlxuICAgICAgICA8bnVpLWRyb3AtYXJlYVxuICAgICAgICAgICAgY2xhc3M9XCJudWktaXRlbXMtZHluYW1pY19fZHJvcC1hcmVhXCJcbiAgICAgICAgICAgICpjZGtEcmFnUGxhY2Vob2xkZXJcbiAgICAgICAgICAgIFtzdHlsZS5oZWlnaHQucHhdPVwiaGVpZ2h0XCJcbiAgICAgICAgPjwvbnVpLWRyb3AtYXJlYT5cblxuICAgICAgICA8bmctdGVtcGxhdGUgI2hlYWRlclRleHQ+XG4gICAgICAgICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwibW92ZUJ1dHRvbnNcIj5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibnVpLWl0ZW1zLWR5bmFtaWNfX2RyYWctaGFuZGxlclwiIGNka0RyYWdIYW5kbGU+XG4gICAgICAgICAgICAgICAgICAgIDxudWktaWNvbiBpY29uPVwiZHJhZ1wiPjwvbnVpLWljb24+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICAgIDxzcGFuXG4gICAgICAgICAgICAgICAgW3RpdGxlXT1cIlxuICAgICAgICAgICAgICAgICAgICBoZWFkZXJNYXAuZ2V0KG5vZGUuaWQpXG4gICAgICAgICAgICAgICAgICAgICAgICB8IG51aVdpZGdldENvbmZpZ3VyYXRvclNlY3Rpb25IZWFkZXIgOiBpIDogaGVhZGVyUHJlZml4XG4gICAgICAgICAgICAgICAgXCJcbiAgICAgICAgICAgICAgICBjbGFzcz1cIm51aS1pdGVtcy1keW5hbWljX19oZWFkZXItdGV4dFwiXG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAge3tcbiAgICAgICAgICAgICAgICAgICAgaGVhZGVyTWFwLmdldChub2RlLmlkKVxuICAgICAgICAgICAgICAgICAgICAgICAgfCBudWlXaWRnZXRDb25maWd1cmF0b3JTZWN0aW9uSGVhZGVyIDogaSA6IGhlYWRlclByZWZpeFxuICAgICAgICAgICAgICAgIH19XG4gICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgIDwvbmctdGVtcGxhdGU+XG5cbiAgICAgICAgPG5nLXRlbXBsYXRlICNoZWFkZXJCdXR0b25zPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImQtZmxleFwiPlxuICAgICAgICAgICAgICAgIDwhLS0gVE9ETzogcmVtb3ZlIFwia3BpXCIgcHJlZml4IGluIHYxMSAtIE5VSS01ODMzIC0tPlxuICAgICAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgICAgICAgaWQ9XCJrcGktd2lkZ2V0LXJlbW92ZS1idG4te3sgaSB9fVwiXG4gICAgICAgICAgICAgICAgICAgIG51aS1idXR0b25cbiAgICAgICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgICAgIGljb249XCJkZWxldGVcIlxuICAgICAgICAgICAgICAgICAgICBkaXNwbGF5U3R5bGU9XCJhY3Rpb25cIlxuICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwicmVtb3ZlSXRlbShnZXRJdGVtRm9yTm9kZShub2RlKSwgaSlcIlxuICAgICAgICAgICAgICAgID48L2J1dHRvbj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L25nLXRlbXBsYXRlPlxuXG4gICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICAgIG51aUNvbXBvbmVudFBvcnRhbFxuICAgICAgICAgICAgI2NvbXBvbmVudFBvcnRhbD1cIm51aUNvbXBvbmVudFBvcnRhbFwiXG4gICAgICAgICAgICBbY29tcG9uZW50SWRdPVwibm9kZS5pZFwiXG4gICAgICAgICAgICBbY29tcG9uZW50VHlwZV09XCJub2RlLmNvbXBvbmVudFR5cGVcIlxuICAgICAgICAgICAgW3Byb3ZpZGVyc109XCJub2RlLnByb3ZpZGVyc1wiXG4gICAgICAgICAgICBbcHJvcGVydGllc109XCJcbiAgICAgICAgICAgICAgICBub2RlLnByb3BlcnRpZXNcbiAgICAgICAgICAgICAgICAgICAgfCBudWlBZGREYXRhIDogaGVhZGVyVGV4dCA6ICdoZWFkZXJUZXh0VGVtcGxhdGUnXG4gICAgICAgICAgICAgICAgICAgIHwgbnVpQWRkRGF0YSA6IGhlYWRlckJ1dHRvbnMgOiAnaGVhZGVyQnV0dG9uc1RlbXBsYXRlJ1xuICAgICAgICAgICAgXCJcbiAgICAgICAgICAgIFtvdXRwdXRzXT1cIlsnZm9ybVJlYWR5JywgJ2Zvcm1EZXN0cm95J11cIlxuICAgICAgICAgICAgKG91dHB1dCk9XCJvbkV2ZW50KG5vZGUuaWQsICRldmVudClcIlxuICAgICAgICA+XG4gICAgICAgICAgICA8bmctdGVtcGxhdGVcbiAgICAgICAgICAgICAgICBbY2RrUG9ydGFsT3V0bGV0XT1cImNvbXBvbmVudFBvcnRhbC5wb3J0YWxcIlxuICAgICAgICAgICAgICAgIChhdHRhY2hlZCk9XCJjb21wb25lbnRQb3J0YWwuYXR0YWNoZWQoJGV2ZW50KVwiXG4gICAgICAgICAgICA+XG4gICAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8L2Rpdj5cbjwvZGl2PlxuIl19