UNPKG

@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

129 lines 36.4 kB
// © 2022 SolarWinds Worldwide, LLC. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Inject, Input, Optional, ViewEncapsulation, } from "@angular/core"; import _isNil from "lodash/isNil"; import { EventBus } from "@nova-ui/bits"; import { mapDataToFormatterProperties } from "../../functions/map-data-to-formatter-properties"; import { INTERACTION } from "../../services/types"; import { DATA_SOURCE, PIZZAGNA_EVENT_BUS, WellKnownDataSourceFeatures, } from "../../types"; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; import * as i2 from "@nova-ui/bits"; import * as i3 from "@angular/cdk/portal"; import * as i4 from "../../pizzagna/directives/component-portal/component-portal.directive"; export class KpiComponent { static { this.lateLoadKey = "KpiComponent"; } get interactive() { return (((this.configuration?.interactive || this.dataSource?.features?.getFeatureConfig(WellKnownDataSourceFeatures.Interactivity)?.enabled) && !_isNil(this.widgetData?.value)) || false); } constructor(changeDetector, dataSource, eventBus) { this.changeDetector = changeDetector; this.dataSource = dataSource; this.eventBus = eventBus; this.busy = false; this.elementClass = ""; this.defaultColor = "var(--nui-color-bg-secondary)"; } onInteraction() { if (!this.interactive) { return; } this.eventBus .getStream(INTERACTION) .next({ payload: { data: this.widgetData } }); } getScaleBroker(id) { if (this.syncValuesBroker) { return this.syncValuesBroker.find((b) => b.id === id); } } ngOnChanges(changes) { if (changes.configuration) { const formattersConfiguration = changes.configuration.currentValue.formatters; if (formattersConfiguration) { this.formattersProperties = this.getFormatterProperties(formattersConfiguration); } } if (changes.widgetData) { if (this.configuration?.formatters) { this.formattersProperties = this.getFormatterProperties(this.configuration.formatters); } } } get showEmpty() { if (typeof this.widgetData?.value === "boolean") { return false; } if (Array.isArray(this.widgetData?.value) && this.widgetData?.value.length === 0) { return true; } return !this.widgetData?.value && this.widgetData?.value !== 0; } /** * Iterates over formatters and maps their properties from the data * * @param formattersConfiguration */ getFormatterProperties(formattersConfiguration) { const formatterKeys = Object.keys(formattersConfiguration); const formattersProperties = formatterKeys.reduce((acc, key) => { const formatterCfg = formattersConfiguration[key]?.formatter; if (formatterCfg) { acc[key] = { data: mapDataToFormatterProperties(formatterCfg, this.widgetData), }; } return acc; }, {}); return formattersProperties; } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: KpiComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: DATA_SOURCE, optional: true }, { token: PIZZAGNA_EVENT_BUS }], target: i0.ɵɵFactoryTarget.Component }); } /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: KpiComponent, selector: "nui-kpi", inputs: { widgetData: "widgetData", backgroundColor: "backgroundColor", syncValuesBroker: "syncValuesBroker", configuration: "configuration", busy: "busy" }, host: { properties: { "class": "this.elementClass" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"w-100 h-100\" nui-busy [busy]=\"busy\">\n <ng-container *ngIf=\"widgetData?.link\">\n <a class=\"nui-kpi-indicator\" [href]=\"widgetData.link\">\n <ng-container [ngTemplateOutlet]=\"kpiContent\"></ng-container>\n </a>\n </ng-container>\n\n <ng-container *ngIf=\"!widgetData?.link\">\n <div class=\"nui-kpi-indicator\" (click)=\"onInteraction()\">\n <ng-container [ngTemplateOutlet]=\"kpiContent\"></ng-container>\n </div>\n </ng-container>\n</div>\n\n<ng-template #kpiContent>\n <div class=\"nui-kpi-indicator__background\"\n [class.nui-kpi-indicator--interactive]=\"interactive\"\n [ngStyle]=\"{'background-color': backgroundColor || widgetData?.backgroundColor}\">\n </div>\n <div class=\"nui-kpi-indicator__text\"\n [ngStyle]=\"{'color': widgetData?.textColor || backgroundColor || widgetData?.backgroundColor || defaultColor}\">\n <div class=\"nui-kpi-indicator__zoom-container\">\n <div class=\"nui-kpi-indicator__description nui-kpi-indicator__zoom-container__row\"\n [title]=\"widgetData?.label\">\n <span nuiZoomContent\n [useZoom]=\"false\"\n [minScale]=\"0.5\"\n [scaleIN$]=\"getScaleBroker('label')?.out$\"\n [scaleOUT$]=\"getScaleBroker('label')?.in$\"\n >{{widgetData?.label}}</span>\n </div>\n\n <ng-container *ngIf=\"configuration?.formatters?.Value; else kpiValueRaw\"\n nuiComponentPortal\n #componentPortal=\"nuiComponentPortal\"\n componentId=\"KpiValueFormatter\"\n [componentType]=\"configuration?.formatters?.Value?.formatter?.componentType\"\n [properties]=\"formattersProperties.Value\">\n <div class=\"nui-kpi-indicator__value nui-kpi-indicator__zoom-container__row\">\n <span nuiZoomContent\n [ngClass]=\"configuration?.formatters?.Value?.formatter?.componentType === 'IconFormatterComponent' ? 'without-filter' : ''\"\n [useZoom]=\"false\"\n [margin]=\"widgetData?.margin ?? 2\"\n [scaleIN$]=\"getScaleBroker('value')?.out$\"\n [scaleOUT$]=\"getScaleBroker('value')?.in$\">\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"></ng-template>\n </span>\n </div>\n </ng-container>\n\n <div class=\"nui-kpi-indicator__units nui-kpi-indicator__zoom-container__row\"\n *ngIf=\"widgetData?.units\"\n [title]=\"widgetData?.units\">\n <span nuiZoomContent\n [useZoom]=\"false\"\n [scaleIN$]=\"getScaleBroker('units')?.out$\"\n [scaleOUT$]=\"getScaleBroker('units')?.in$\"\n [minScale]=\"0.5\">{{widgetData?.units}}</span>\n </div>\n </div>\n </div>\n</ng-template>\n\n<ng-template #kpiValueRaw>\n <div class=\"nui-kpi-indicator__value nui-kpi-indicator__zoom-container__row\"\n [style.font-size]=\"widgetData?.fontSize\"\n [title]=\"widgetData?.value\">\n <span nuiZoomContent\n [useZoom]=\"false\"\n [margin]=\"widgetData?.margin ?? 2\"\n [scaleIN$]=\"getScaleBroker('value')?.out$\"\n [scaleOUT$]=\"getScaleBroker('value')?.in$\">\n {{widgetData?.value}}</span>\n </div>\n</ng-template>\n", styles: [".nui-kpi-indicator{position:relative;display:block;height:100%;width:100%}.nui-kpi-indicator__background{height:100%;width:100%;background-color:var(--nui-color-bg-secondary,#fafafa);color:var(--nui-color-text-default,#111)}.nui-kpi-indicator__text{position:absolute;width:100%;height:100%;top:0;pointer-events:none}.nui-kpi-indicator__text *>span{filter:invert(1) grayscale(1) contrast(100)}.nui-kpi-indicator__value{font-size:54px;font-weight:600;margin:auto 0;min-width:100%;white-space:nowrap;display:flex;align-items:center;justify-content:center;height:100%;min-height:5px;max-height:200px}.nui-kpi-indicator__value>span.without-filter{filter:unset}.nui-kpi-indicator__units{font-size:16px;font-weight:600;min-width:0;margin-bottom:auto;display:flex;align-items:center;justify-content:center;height:100%;min-height:10px;max-height:45px}.nui-kpi-indicator__units>span{white-space:nowrap;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}.nui-kpi-indicator__description{font-size:16px;font-weight:600;min-width:0;margin-top:auto;display:flex;align-items:center;justify-content:center;height:100%;min-height:10px;max-height:45px}.nui-kpi-indicator__description>span{white-space:nowrap;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}.nui-kpi-indicator__zoom-container{display:grid;grid-template-columns:1fr;width:100%;height:100%}.nui-kpi-indicator__zoom-container:has(.nui-kpi-indicator__zoom-container__row:nth-child(3)){grid-template-rows:30% 40% 30%}.nui-kpi-indicator__zoom-container:not(:has(.nui-kpi-indicator__zoom-container__row:nth-child(3))){grid-template-rows:30% 70%}.nui-kpi-indicator--interactive{cursor:pointer}.nui-kpi-indicator--interactive:hover{filter:brightness(90%)}.is-empty{display:grid;place-content:center;height:100%}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i2.BusyComponent, selector: "[nui-busy]", inputs: ["busy", "disableTabNavigation"] }, { kind: "directive", type: i3.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "directive", type: i4.ComponentPortalDirective, selector: "[nuiComponentPortal]", inputs: ["componentId", "componentType", "properties", "providers", "outputs"], outputs: ["output"], exportAs: ["nuiComponentPortal"] }, { kind: "directive", type: i2.ZoomContentDirective, selector: "[nuiZoomContent]", inputs: ["zoomRatio", "minZoomDifference", "maxZoom", "minZoom", "useZoom", "margin", "minScale", "maxScale", "scaleIN$", "scaleOUT$"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: KpiComponent, decorators: [{ type: Component, args: [{ selector: "nui-kpi", encapsulation: ViewEncapsulation.Emulated, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"w-100 h-100\" nui-busy [busy]=\"busy\">\n <ng-container *ngIf=\"widgetData?.link\">\n <a class=\"nui-kpi-indicator\" [href]=\"widgetData.link\">\n <ng-container [ngTemplateOutlet]=\"kpiContent\"></ng-container>\n </a>\n </ng-container>\n\n <ng-container *ngIf=\"!widgetData?.link\">\n <div class=\"nui-kpi-indicator\" (click)=\"onInteraction()\">\n <ng-container [ngTemplateOutlet]=\"kpiContent\"></ng-container>\n </div>\n </ng-container>\n</div>\n\n<ng-template #kpiContent>\n <div class=\"nui-kpi-indicator__background\"\n [class.nui-kpi-indicator--interactive]=\"interactive\"\n [ngStyle]=\"{'background-color': backgroundColor || widgetData?.backgroundColor}\">\n </div>\n <div class=\"nui-kpi-indicator__text\"\n [ngStyle]=\"{'color': widgetData?.textColor || backgroundColor || widgetData?.backgroundColor || defaultColor}\">\n <div class=\"nui-kpi-indicator__zoom-container\">\n <div class=\"nui-kpi-indicator__description nui-kpi-indicator__zoom-container__row\"\n [title]=\"widgetData?.label\">\n <span nuiZoomContent\n [useZoom]=\"false\"\n [minScale]=\"0.5\"\n [scaleIN$]=\"getScaleBroker('label')?.out$\"\n [scaleOUT$]=\"getScaleBroker('label')?.in$\"\n >{{widgetData?.label}}</span>\n </div>\n\n <ng-container *ngIf=\"configuration?.formatters?.Value; else kpiValueRaw\"\n nuiComponentPortal\n #componentPortal=\"nuiComponentPortal\"\n componentId=\"KpiValueFormatter\"\n [componentType]=\"configuration?.formatters?.Value?.formatter?.componentType\"\n [properties]=\"formattersProperties.Value\">\n <div class=\"nui-kpi-indicator__value nui-kpi-indicator__zoom-container__row\">\n <span nuiZoomContent\n [ngClass]=\"configuration?.formatters?.Value?.formatter?.componentType === 'IconFormatterComponent' ? 'without-filter' : ''\"\n [useZoom]=\"false\"\n [margin]=\"widgetData?.margin ?? 2\"\n [scaleIN$]=\"getScaleBroker('value')?.out$\"\n [scaleOUT$]=\"getScaleBroker('value')?.in$\">\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"></ng-template>\n </span>\n </div>\n </ng-container>\n\n <div class=\"nui-kpi-indicator__units nui-kpi-indicator__zoom-container__row\"\n *ngIf=\"widgetData?.units\"\n [title]=\"widgetData?.units\">\n <span nuiZoomContent\n [useZoom]=\"false\"\n [scaleIN$]=\"getScaleBroker('units')?.out$\"\n [scaleOUT$]=\"getScaleBroker('units')?.in$\"\n [minScale]=\"0.5\">{{widgetData?.units}}</span>\n </div>\n </div>\n </div>\n</ng-template>\n\n<ng-template #kpiValueRaw>\n <div class=\"nui-kpi-indicator__value nui-kpi-indicator__zoom-container__row\"\n [style.font-size]=\"widgetData?.fontSize\"\n [title]=\"widgetData?.value\">\n <span nuiZoomContent\n [useZoom]=\"false\"\n [margin]=\"widgetData?.margin ?? 2\"\n [scaleIN$]=\"getScaleBroker('value')?.out$\"\n [scaleOUT$]=\"getScaleBroker('value')?.in$\">\n {{widgetData?.value}}</span>\n </div>\n</ng-template>\n", styles: [".nui-kpi-indicator{position:relative;display:block;height:100%;width:100%}.nui-kpi-indicator__background{height:100%;width:100%;background-color:var(--nui-color-bg-secondary,#fafafa);color:var(--nui-color-text-default,#111)}.nui-kpi-indicator__text{position:absolute;width:100%;height:100%;top:0;pointer-events:none}.nui-kpi-indicator__text *>span{filter:invert(1) grayscale(1) contrast(100)}.nui-kpi-indicator__value{font-size:54px;font-weight:600;margin:auto 0;min-width:100%;white-space:nowrap;display:flex;align-items:center;justify-content:center;height:100%;min-height:5px;max-height:200px}.nui-kpi-indicator__value>span.without-filter{filter:unset}.nui-kpi-indicator__units{font-size:16px;font-weight:600;min-width:0;margin-bottom:auto;display:flex;align-items:center;justify-content:center;height:100%;min-height:10px;max-height:45px}.nui-kpi-indicator__units>span{white-space:nowrap;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}.nui-kpi-indicator__description{font-size:16px;font-weight:600;min-width:0;margin-top:auto;display:flex;align-items:center;justify-content:center;height:100%;min-height:10px;max-height:45px}.nui-kpi-indicator__description>span{white-space:nowrap;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}.nui-kpi-indicator__zoom-container{display:grid;grid-template-columns:1fr;width:100%;height:100%}.nui-kpi-indicator__zoom-container:has(.nui-kpi-indicator__zoom-container__row:nth-child(3)){grid-template-rows:30% 40% 30%}.nui-kpi-indicator__zoom-container:not(:has(.nui-kpi-indicator__zoom-container__row:nth-child(3))){grid-template-rows:30% 70%}.nui-kpi-indicator--interactive{cursor:pointer}.nui-kpi-indicator--interactive:hover{filter:brightness(90%)}.is-empty{display:grid;place-content:center;height:100%}\n"] }] }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DATA_SOURCE] }] }, { type: i2.EventBus, decorators: [{ type: Inject, args: [PIZZAGNA_EVENT_BUS] }] }], propDecorators: { widgetData: [{ type: Input }], backgroundColor: [{ type: Input }], syncValuesBroker: [{ type: Input }], configuration: [{ type: Input }], busy: [{ type: Input }], elementClass: [{ type: HostBinding, args: ["class"] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia3BpLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9rcGktd2lkZ2V0L2twaS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMva3BpLXdpZGdldC9rcGkuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEseURBQXlEO0FBQ3pELEVBQUU7QUFDRiwrRUFBK0U7QUFDL0UsNEVBQTRFO0FBQzVFLDhFQUE4RTtBQUM5RSwrRUFBK0U7QUFDL0UsOEVBQThFO0FBQzlFLDREQUE0RDtBQUM1RCxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLHVEQUF1RDtBQUN2RCxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLDRFQUE0RTtBQUM1RSwrRUFBK0U7QUFDL0UsMEVBQTBFO0FBQzFFLGlGQUFpRjtBQUNqRiw2RUFBNkU7QUFDN0UsaUJBQWlCO0FBRWpCLE9BQU8sRUFDSCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxXQUFXLEVBQ1gsTUFBTSxFQUNOLEtBQUssRUFFTCxRQUFRLEVBRVIsaUJBQWlCLEdBQ3BCLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sTUFBTSxNQUFNLGNBQWMsQ0FBQztBQUVsQyxPQUFPLEVBQUUsUUFBUSxFQUF1QixNQUFNLGVBQWUsQ0FBQztBQVE5RCxPQUFPLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSxrREFBa0QsQ0FBQztBQUNoRyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDbkQsT0FBTyxFQUNILFdBQVcsRUFFWCxrQkFBa0IsRUFDbEIsMkJBQTJCLEdBQzlCLE1BQU0sYUFBYSxDQUFDOzs7Ozs7QUFVckIsTUFBTSxPQUFPLFlBQVk7YUFDUCxnQkFBVyxHQUFHLGNBQWMsQUFBakIsQ0FBa0I7SUF1QjNDLElBQVcsV0FBVztRQUNsQixPQUFPLENBQ0gsQ0FB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QUFHO29CQUNQLElBQUksRUFBRSw0QkFBNEIsQ0FDOUIsWUFBWSxFQUNaLElBQUksQ0FBQyxVQUFVLENBQ2xCO2lCQUNKLENBQUM7YUFDTDtZQUNELE9BQU8sR0FBRyxDQUFDO1FBQ2YsQ0FBQyxFQUNELEVBQUUsQ0FDTCxDQUFDO1FBRUYsT0FBTyxvQkFBb0IsQ0FBQztJQUNoQyxDQUFDO2tJQXZIUSxZQUFZLG1EQXFDRyxXQUFXLDZCQUN2QixrQkFBa0I7c0hBdENyQixZQUFZLDBSQzNEekIsZzBIQTZFQTs7NEZEbEJhLFlBQVk7a0JBUHhCLFNBQVM7K0JBQ0ksU0FBUyxpQkFHSixpQkFBaUIsQ0FBQyxRQUFRLG1CQUN4Qix1QkFBdUIsQ0FBQyxNQUFNOzswQkF1QzFDLFFBQVE7OzBCQUFJLE1BQU07MkJBQUMsV0FBVzs7MEJBQzlCLE1BQU07MkJBQUMsa0JBQWtCO3lDQWxDdkIsVUFBVTtzQkFEaEIsS0FBSztnQkFJQyxlQUFlO3NCQURyQixLQUFLO2dCQUlDLGdCQUFnQjtzQkFEdEIsS0FBSztnQkFJQyxhQUFhO3NCQURuQixLQUFLO2dCQUlDLElBQUk7c0JBRFYsS0FBSztnQkFJQyxZQUFZO3NCQURsQixXQUFXO3VCQUFDLE9BQU8iLCJzb3VyY2VzQ29udGVudCI6WyIvLyDCqSAyMDIyIFNvbGFyV2luZHMgV29ybGR3aWRlLCBMTEMuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4vL1xuLy8gUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nIGEgY29weVxuLy8gIG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlIFwiU29mdHdhcmVcIiksIHRvXG4vLyAgZGVhbCBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGVcbi8vICByaWdodHMgdG8gdXNlLCBjb3B5LCBtb2RpZnksIG1lcmdlLCBwdWJsaXNoLCBkaXN0cmlidXRlLCBzdWJsaWNlbnNlLCBhbmQvb3Jcbi8vICBzZWxsIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpc1xuLy8gIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6XG4vL1xuLy8gVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW5cbi8vICBhbGwgY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS5cbi8vXG4vLyBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTIE9SXG4vLyAgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFksXG4vLyAgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFXG4vLyAgQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwgREFNQUdFUyBPUiBPVEhFUlxuLy8gIExJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQgT1IgT1RIRVJXSVNFLCBBUklTSU5HIEZST00sXG4vLyAgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTlxuLy8gIFRIRSBTT0ZUV0FSRS5cblxuaW1wb3J0IHtcbiAgICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgICBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICBDb21wb25lbnQsXG4gICAgSG9zdEJpbmRpbmcsXG4gICAgSW5qZWN0LFxuICAgIElucHV0LFxuICAgIE9uQ2hhbmdlcyxcbiAgICBPcHRpb25hbCxcbiAgICBTaW1wbGVDaGFuZ2VzLFxuICAgIFZpZXdFbmNhcHN1bGF0aW9uLFxufSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuaW1wb3J0IF9pc05pbCBmcm9tIFwibG9kYXNoL2lzTmlsXCI7XG5cbmltcG9ydCB7IEV2ZW50QnVzLCBJRGF0YVNvdXJjZSwgSUV2ZW50IH0gZnJvbSBcIkBub3ZhLXVpL2JpdHNcIjtcblxuaW1wb3J0IHtcbiAgICBJS3BpQ29uZmlndXJhdGlvbixcbiAgICBJS3BpRGF0YSxcbiAgICBJS3BpRm9ybWF0dGVyUHJvcGVydGllcyxcbiAgICBJS3BpRm9ybWF0dGVyc0NvbmZpZ3VyYXRpb24sXG59IGZyb20gXCIuL3R5cGVzXCI7XG5pbXBvcnQgeyBtYXBEYXRhVG9Gb3JtYXR0ZXJQcm9wZXJ0aWVzIH0gZnJvbSBcIi4uLy4uL2Z1bmN0aW9ucy9tYXAtZGF0YS10by1mb3JtYXR0ZXItcHJvcGVydGllc1wiO1xuaW1wb3J0IHsgSU5URVJBQ1RJT04gfSBmcm9tIFwiLi4vLi4vc2VydmljZXMvdHlwZXNcIjtcbmltcG9ydCB7XG4gICAgREFUQV9TT1VSQ0UsXG4gICAgSUhhc0NoYW5nZURldGVjdG9yLFxuICAgIFBJWlpBR05BX0VWRU5UX0JVUyxcbiAgICBXZWxsS25vd25EYXRhU291cmNlRmVhdHVyZXMsXG59IGZyb20gXCIuLi8uLi90eXBlc1wiO1xuaW1wb3J0IHsgSUJyb2tlciB9IGZyb20gXCIuLi9wcm92aWRlcnMvdHlwZXNcIjtcblxuQENvbXBvbmVudCh7XG4gICAgc2VsZWN0b3I6IFwibnVpLWtwaVwiLFxuICAgIHRlbXBsYXRlVXJsOiBcIi4va3BpLmNvbXBvbmVudC5odG1sXCIsXG4gICAgc3R5bGVVcmxzOiBbXCIuL2twaS5jb21wb25lbnQubGVzc1wiXSxcbiAgICBlbmNhcHN1bGF0aW9uOiBWaWV3RW5jYXBzdWxhdGlvbi5FbXVsYXRlZCxcbiAgICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgS3BpQ29tcG9uZW50IGltcGxlbWVudHMgSUhhc0NoYW5nZURldGVjdG9yLCBPbkNoYW5nZXMge1xuICAgIHB1YmxpYyBzdGF0aWMgbGF0ZUxvYWRLZXkgPSBcIktwaUNvbXBvbmVudFwiO1xuXG4gICAgQElucHV0KClcbiAgICBwdWJsaWMgd2lkZ2V0RGF0YTogSUtwaURhdGE7XG5cbiAgICBASW5wdXQoKVxuICAgIHB1YmxpYyBiYWNrZ3JvdW5kQ29sb3I6IHN0cmluZztcblxuICAgIEBJbnB1dCgpXG4gICAgcHVibGljIHN5bmNWYWx1ZXNCcm9rZXI6IElCcm9rZXJbXTtcblxuICAgIEBJbnB1dCgpXG4gICAgcHVibGljIGNvbmZpZ3VyYXRpb246IElLcGlDb25maWd1cmF0aW9uO1xuXG4gICAgQElucHV0KClcbiAgICBwdWJsaWMgYnVzeSA9IGZhbHNlO1xuXG4gICAgQEhvc3RCaW5kaW5nKFwiY2xhc3NcIilcbiAgICBwdWJsaWMgZWxlbWVudENsYXNzID0gXCJcIjtcblxuICAgIHB1YmxpYyBmb3JtYXR0ZXJzUHJvcGVydGllczogSUtwaUZvcm1hdHRlclByb3BlcnRpZXM7XG4gICAgcHVibGljIGRlZmF1bHRDb2xvcjogc3RyaW5nID0gXCJ2YXIoLS1udWktY29sb3ItYmctc2Vjb25kYXJ5KVwiO1xuXG4gICAgcHVibGljIGdldCBpbnRlcmFjdGl2ZSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgICgodGhpcy5jb25maWd1cmF0aW9uPy5pbnRlcmFjdGl2ZSB8fFxuICAgICAgICAgICAgICAgIHRoaXMuZGF0YVNvdXJjZT8uZmVhdHVyZXM/LmdldEZlYXR1cmVDb25maWcoXG4gICAgICAgICAgICAgICAgICAgIFdlbGxLbm93bkRhdGFTb3VyY2VGZWF0dXJlcy5JbnRlcmFjdGl2aXR5XG4gICAgICAgICAgICAgICAgKT8uZW5hYmxlZCkgJiZcbiAgICAgICAgICAgICAgICAhX2lzTmlsKHRoaXMud2lkZ2V0RGF0YT8udmFsdWUpKSB8fFxuICAgICAgICAgICAgZmFsc2VcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHVibGljIGNoYW5nZURldGVjdG9yOiBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICAgICAgQE9wdGlvbmFsKCkgQEluamVjdChEQVRBX1NPVVJDRSkgcHVibGljIGRhdGFTb3VyY2U6IElEYXRhU291cmNlLFxuICAgICAgICBASW5qZWN0KFBJWlpBR05BX0VWRU5UX0JVUykgcHVibGljIGV2ZW50QnVzOiBFdmVudEJ1czxJRXZlbnQ+XG4gICAgKSB7fVxuXG4gICAgcHVibGljIG9uSW50ZXJhY3Rpb24oKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5pbnRlcmFjdGl2ZSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZXZlbnRCdXNcbiAgICAgICAgICAgIC5nZXRTdHJlYW0oSU5URVJBQ1RJT04pXG4gICAgICAgICAgICAubmV4dCh7IHBheWxvYWQ6IHsgZGF0YTogdGhpcy53aWRnZXREYXRhIH0gfSk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFNjYWxlQnJva2VyKGlkOiBzdHJpbmcpOiBJQnJva2VyIHwgdW5kZWZpbmVkIHtcbiAgICAgICAgaWYgKHRoaXMuc3luY1ZhbHVlc0Jyb2tlcikge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuc3luY1ZhbHVlc0Jyb2tlci5maW5kKChiKSA9PiBiLmlkID09PSBpZCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgICAgICBpZiAoY2hhbmdlcy5jb25maWd1cmF0aW9uKSB7XG4gICAgICAgICAgICBjb25zdCBmb3JtYXR0ZXJzQ29uZmlndXJhdGlvbjogSUtwaUZvcm1hdHRlcnNDb25maWd1cmF0aW9uID1cbiAgICAgICAgICAgICAgICBjaGFuZ2VzLmNvbmZpZ3VyYXRpb24uY3VycmVudFZhbHVlLmZvcm1hdHRlcnM7XG5cbiAgICAgICAgICAgIGlmIChmb3JtYXR0ZXJzQ29uZmlndXJhdGlvbikge1xuICAgICAgICAgICAgICAgIHRoaXMuZm9ybWF0dGVyc1Byb3BlcnRpZXMgPSB0aGlzLmdldEZvcm1hdHRlclByb3BlcnRpZXMoXG4gICAgICAgICAgICAgICAgICAgIGZvcm1hdHRlcnNDb25maWd1cmF0aW9uXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjaGFuZ2VzLndpZGdldERhdGEpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmNvbmZpZ3VyYXRpb24/LmZvcm1hdHRlcnMpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmZvcm1hdHRlcnNQcm9wZXJ0aWVzID0gdGhpcy5nZXRGb3JtYXR0ZXJQcm9wZXJ0aWVzKFxuICAgICAgICAgICAgICAgICAgICB0aGlzLmNvbmZpZ3VyYXRpb24uZm9ybWF0dGVyc1xuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0IHNob3dFbXB0eSgpOiBib29sZWFuIHtcbiAgICAgICAgaWYgKHR5cGVvZiB0aGlzLndpZGdldERhdGE/LnZhbHVlID09PSBcImJvb2xlYW5cIikge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgQXJyYXkuaXNBcnJheSh0aGlzLndpZGdldERhdGE/LnZhbHVlKSAmJlxuICAgICAgICAgICAgdGhpcy53aWRnZXREYXRhPy52YWx1ZS5sZW5ndGggPT09IDBcbiAgICAgICAgKSB7XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiAhdGhpcy53aWRnZXREYXRhPy52YWx1ZSAmJiB0aGlzLndpZGdldERhdGE/LnZhbHVlICE9PSAwO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEl0ZXJhdGVzIG92ZXIgZm9ybWF0dGVycyBhbmQgbWFwcyB0aGVpciBwcm9wZXJ0aWVzIGZyb20gdGhlIGRhdGFcbiAgICAgKlxuICAgICAqIEBwYXJhbSBmb3JtYXR0ZXJzQ29uZmlndXJhdGlvblxuICAgICAqL1xuICAgIHByaXZhdGUgZ2V0Rm9ybWF0dGVyUHJvcGVydGllcyhcbiAgICAgICAgZm9ybWF0dGVyc0NvbmZpZ3VyYXRpb246IElLcGlGb3JtYXR0ZXJzQ29uZmlndXJhdGlvblxuICAgICkge1xuICAgICAgICBjb25zdCBmb3JtYXR0ZXJLZXlzID0gT2JqZWN0LmtleXMoZm9ybWF0dGVyc0NvbmZpZ3VyYXRpb24pO1xuXG4gICAgICAgIGNvbnN0IGZvcm1hdHRlcnNQcm9wZXJ0aWVzID0gZm9ybWF0dGVyS2V5cy5yZWR1Y2UoXG4gICAgICAgICAgICAoYWNjOiBJS3BpRm9ybWF0dGVyUHJvcGVydGllcywga2V5OiBzdHJpbmcpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBmb3JtYXR0ZXJDZmcgPSBmb3JtYXR0ZXJzQ29uZmlndXJhdGlvbltrZXldPy5mb3JtYXR0ZXI7XG4gICAgICAgICAgICAgICAgaWYgKGZvcm1hdHRlckNmZykge1xuICAgICAgICAgICAgICAgICAgICBhY2Nba2V5XSA9IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IG1hcERhdGFUb0Zvcm1hdHRlclByb3BlcnRpZXMoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0dGVyQ2ZnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMud2lkZ2V0RGF0YVxuICAgICAgICAgICAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7fVxuICAgICAgICApO1xuXG4gICAgICAgIHJldHVybiBmb3JtYXR0ZXJzUHJvcGVydGllcztcbiAgICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwidy0xMDAgaC0xMDBcIiBudWktYnVzeSBbYnVzeV09XCJidXN5XCI+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIndpZGdldERhdGE/LmxpbmtcIj5cbiAgICAgICAgPGEgY2xhc3M9XCJudWkta3BpLWluZGljYXRvclwiIFtocmVmXT1cIndpZGdldERhdGEubGlua1wiPlxuICAgICAgICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJrcGlDb250ZW50XCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgIDwvYT5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCIhd2lkZ2V0RGF0YT8ubGlua1wiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwibnVpLWtwaS1pbmRpY2F0b3JcIiAoY2xpY2spPVwib25JbnRlcmFjdGlvbigpXCI+XG4gICAgICAgICAgICA8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cImtwaUNvbnRlbnRcIj48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgPC9kaXY+XG4gICAgPC9uZy1jb250YWluZXI+XG48L2Rpdj5cblxuPG5nLXRlbXBsYXRlICNrcGlDb250ZW50PlxuICAgIDxkaXYgY2xhc3M9XCJudWkta3BpLWluZGljYXRvcl9fYmFja2dyb3VuZFwiXG4gICAgICAgICBbY2xhc3MubnVpLWtwaS1pbmRpY2F0b3ItLWludGVyYWN0aXZlXT1cImludGVyYWN0aXZlXCJcbiAgICAgICAgIFtuZ1N0eWxlXT1cInsnYmFja2dyb3VuZC1jb2xvcic6IGJhY2tncm91bmRDb2xvciB8fCB3aWRnZXREYXRhPy5iYWNrZ3JvdW5kQ29sb3J9XCI+XG4gICAgPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cIm51aS1rcGktaW5kaWNhdG9yX190ZXh0XCJcbiAgICAgICAgIFtuZ1N0eWxlXT1cInsnY29sb3InOiB3aWRnZXREYXRhPy50ZXh0Q29sb3IgfHwgYmFja2dyb3VuZENvbG9yIHx8ICB3aWRnZXREYXRhPy5iYWNrZ3JvdW5kQ29sb3IgfHwgZGVmYXVsdENvbG9yfVwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwibnVpLWtwaS1pbmRpY2F0b3JfX3pvb20tY29udGFpbmVyXCI+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibnVpLWtwaS1pbmRpY2F0b3JfX2Rlc2NyaXB0aW9uIG51aS1rcGktaW5kaWNhdG9yX196b29tLWNvbnRhaW5lcl9fcm93XCJcbiAgICAgICAgICAgICAgICAgW3RpdGxlXT1cIndpZGdldERhdGE/LmxhYmVsXCI+XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIG51aVpvb21Db250ZW50XG4gICAgICAgICAgICAgICAgICAgICAgICAgIFt1c2Vab29tXT1cImZhbHNlXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgW21pblNjYWxlXT1cIjAuNVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgIFtzY2FsZUlOJF09XCJnZXRTY2FsZUJyb2tlcignbGFiZWwnKT8ub3V0JFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgIFtzY2FsZU9VVCRdPVwiZ2V0U2NhbGVCcm9rZXIoJ2xhYmVsJyk/LmluJFwiXG4gICAgICAgICAgICAgICAgICAgID57e3dpZGdldERhdGE/LmxhYmVsfX08L3NwYW4+XG4gICAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImNvbmZpZ3VyYXRpb24/LmZvcm1hdHRlcnM/LlZhbHVlOyBlbHNlIGtwaVZhbHVlUmF3XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgbnVpQ29tcG9uZW50UG9ydGFsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICNjb21wb25lbnRQb3J0YWw9XCJudWlDb21wb25lbnRQb3J0YWxcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wb25lbnRJZD1cIktwaVZhbHVlRm9ybWF0dGVyXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgW2NvbXBvbmVudFR5cGVdPVwiY29uZmlndXJhdGlvbj8uZm9ybWF0dGVycz8uVmFsdWU/LmZvcm1hdHRlcj8uY29tcG9uZW50VHlwZVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgIFtwcm9wZXJ0aWVzXT1cImZvcm1hdHRlcnNQcm9wZXJ0aWVzLlZhbHVlXCI+XG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm51aS1rcGktaW5kaWNhdG9yX192YWx1ZSBudWkta3BpLWluZGljYXRvcl9fem9vbS1jb250YWluZXJfX3Jvd1wiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIG51aVpvb21Db250ZW50XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW25nQ2xhc3NdPVwiY29uZmlndXJhdGlvbj8uZm9ybWF0dGVycz8uVmFsdWU/LmZvcm1hdHRlcj8uY29tcG9uZW50VHlwZSA9PT0gJ0ljb25Gb3JtYXR0ZXJDb21wb25lbnQnID8gJ3dpdGhvdXQtZmlsdGVyJyA6ICcnXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbdXNlWm9vbV09XCJmYWxzZVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW21hcmdpbl09XCJ3aWRnZXREYXRhPy5tYXJnaW4gPz8gMlwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW3NjYWxlSU4kXT1cImdldFNjYWxlQnJva2VyKCd2YWx1ZScpPy5vdXQkXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbc2NhbGVPVVQkXT1cImdldFNjYWxlQnJva2VyKCd2YWx1ZScpPy5pbiRcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG5nLXRlbXBsYXRlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbY2RrUG9ydGFsT3V0bGV0XT1cImNvbXBvbmVudFBvcnRhbC5wb3J0YWxcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGF0dGFjaGVkKT1cImNvbXBvbmVudFBvcnRhbC5hdHRhY2hlZCgkZXZlbnQpXCI+PC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm51aS1rcGktaW5kaWNhdG9yX191bml0cyBudWkta3BpLWluZGljYXRvcl9fem9vbS1jb250YWluZXJfX3Jvd1wiXG4gICAgICAgICAgICAgICAgICpuZ0lmPVwid2lkZ2V0RGF0YT8udW5pdHNcIlxuICAgICAgICAgICAgICAgICBbdGl0bGVdPVwid2lkZ2V0RGF0YT8udW5pdHNcIj5cbiAgICAgICAgICAgICAgICAgICAgPHNwYW4gbnVpWm9vbUNvbnRlbnRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgW3VzZVpvb21dPVwiZmFsc2VcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICBbc2NhbGVJTiRdPVwiZ2V0U2NhbGVCcm9rZXIoJ3VuaXRzJyk/Lm91dCRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICBbc2NhbGVPVVQkXT1cImdldFNjYWxlQnJva2VyKCd1bml0cycpPy5pbiRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICBbbWluU2NhbGVdPVwiMC41XCI+e3t3aWRnZXREYXRhPy51bml0c319PC9zcGFuPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuPC9uZy10ZW1wbGF0ZT5cblxuPG5nLXRlbXBsYXRlICNrcGlWYWx1ZVJhdz5cbiAgICA8ZGl2IGNsYXNzPVwibnVpLWtwaS1pbmRpY2F0b3JfX3ZhbHVlIG51aS1rcGktaW5kaWNhdG9yX196b29tLWNvbnRhaW5lcl9fcm93XCJcbiAgICAgICAgIFtzdHlsZS5mb250LXNpemVdPVwid2lkZ2V0RGF0YT8uZm9udFNpemVcIlxuICAgICAgICAgW3RpdGxlXT1cIndpZGdldERhdGE/LnZhbHVlXCI+XG4gICA8c3BhbiBudWlab29tQ29udGVudFxuICAgICAgICAgW3VzZVpvb21dPVwiZmFsc2VcIlxuICAgICAgICAgW21hcmdpbl09XCJ3aWRnZXREYXRhPy5tYXJnaW4gPz8gMlwiXG4gICAgICAgICBbc2NhbGVJTiRdPVwiZ2V0U2NhbGVCcm9rZXIoJ3ZhbHVlJyk/Lm91dCRcIlxuICAgICAgICAgW3NjYWxlT1VUJF09XCJnZXRTY2FsZUJyb2tlcigndmFsdWUnKT8uaW4kXCI+XG4gICAgICAge3t3aWRnZXREYXRhPy52YWx1ZX19PC9zcGFuPlxuICAgIDwvZGl2PlxuPC9uZy10ZW1wbGF0ZT5cbiJdfQ==