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

202 lines 51.6 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 { animate, state, style, transition, trigger, } from "@angular/animations"; import { ChangeDetectorRef, Component, ElementRef, HostBinding, Inject, Input, Optional, ViewChild, } from "@angular/core"; import { Subject } from "rxjs"; import { takeUntil } from "rxjs/operators"; import { EventBus } from "@nova-ui/bits"; import { PizzagnaService } from "../../../pizzagna/services/pizzagna.service"; import { DASHBOARD_EDIT_MODE, REFRESH, WIDGET_EDIT, WIDGET_REMOVE, } from "../../../services/types"; import { WidgetToDashboardEventProxyService } from "../../../services/widget-to-dashboard-event-proxy.service"; import { HEADER_LINK_PROVIDER, PIZZAGNA_EVENT_BUS, PizzagnaLayer, } from "../../../types"; import * as i0 from "@angular/core"; import * as i1 from "../../../pizzagna/services/pizzagna.service"; import * as i2 from "../../../services/widget-to-dashboard-event-proxy.service"; import * as i3 from "@angular/common"; import * as i4 from "@nova-ui/bits"; export class WidgetHeaderComponent { static { this.lateLoadKey = "WidgetHeaderComponent"; } get hostClass() { return true; } get state() { return this.collapsed && this.collapsible ? "collapsed" : "expanded"; } constructor(eventBus, pizzagnaService, changeDetector, eventProxy, linkProvider) { this.eventBus = eventBus; this.pizzagnaService = pizzagnaService; this.changeDetector = changeDetector; this.eventProxy = eventProxy; this.linkProvider = linkProvider; this.editMode = true; /** * Boolean which shows or hides the edit button */ this.editable = true; /** * Boolean which shows or hides the remove widget button */ this.removable = true; /** * Boolean which adds ability to collapse widget header */ this.collapsible = false; this.reloadable = true; this.hideMenu = false; /** * Boolean which tells what state the widget header is in if collapsible */ this.collapsed = false; this.onDestroy$ = new Subject(); this.linkTooltip = $localize `Explore this data`; } ngOnInit() { if (this.collapsible) { this.pizzagnaService.setProperty({ componentId: this.componentId, pizzagnaKey: PizzagnaLayer.Data, propertyPath: ["isCollapsed"], }, true); } if (this.eventProxy) { this.eventProxy.addUpstream(WIDGET_EDIT); this.eventProxy.addUpstream(WIDGET_REMOVE); this.eventProxy.addDownstream(DASHBOARD_EDIT_MODE); } // subscribing to dashboard event to set 'edit mode' this.eventBus .getStream(DASHBOARD_EDIT_MODE) .pipe(takeUntil(this.onDestroy$)) .subscribe((event) => { this.pizzagnaService.setProperty({ componentId: this.componentId, pizzagnaKey: PizzagnaLayer.Data, propertyPath: ["collapsed"], }, !event.payload); this.pizzagnaService.setProperty({ componentId: this.componentId, pizzagnaKey: PizzagnaLayer.Data, propertyPath: ["editMode"], }, !!event.payload); }); } ngAfterViewInit() { // we can't set values in ngAfterViewInit directly - it causes ExpressionChangedAfterViewChecked error, so setTimeout has to be used setTimeout(() => { this.withCustomElement = this.widgetHeaderCustomElement.nativeElement.childNodes .length !== 0; }); } ngOnDestroy() { this.onDestroy$.next(); this.onDestroy$.complete(); } removeWidget() { this.eventBus.getStream(WIDGET_REMOVE).next({}); } toggleCollapsed() { this.pizzagnaService.setProperty({ componentId: this.componentId, pizzagnaKey: PizzagnaLayer.Data, propertyPath: ["collapsed"], }, !this.collapsed); } onEditWidget() { this.eventBus.getStream(WIDGET_EDIT).next({}); } onReloadData() { if (!this.reloadable) { throw new Error("The widget is not reloadable, so it can't be reloaded manually."); } this.eventBus.getStream(REFRESH).next({}); } prepareLink($event) { const target = $event.target; if (target && this.linkProvider) { const link = this.linkProvider.getLink(this.url); if (link) { target.setAttribute("href", link); } } return true; } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetHeaderComponent, deps: [{ token: PIZZAGNA_EVENT_BUS }, { token: i1.PizzagnaService }, { token: i0.ChangeDetectorRef }, { token: i2.WidgetToDashboardEventProxyService, optional: true }, { token: HEADER_LINK_PROVIDER, optional: true }], target: i0.ɵɵFactoryTarget.Component }); } /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: WidgetHeaderComponent, selector: "nui-widget-header", inputs: { componentId: "componentId", editMode: "editMode", editable: "editable", removable: "removable", collapsible: "collapsible", reloadable: "reloadable", title: "title", subtitle: "subtitle", hideMenu: "hideMenu", url: "url", collapsed: "collapsed" }, host: { properties: { "class.nui-widget-header": "this.hostClass" } }, viewQueries: [{ propertyName: "widgetHeaderCustomElement", first: true, predicate: ["widgetHeaderCustomElement"], descendants: true }], ngImport: i0, template: "<div class=\"nui-widget__header\">\n <div\n class=\"nui-widget__header__container\"\n [class.nui-widget__header__container--collapsible]=\"collapsible\"\n [class.nui-widget__header__container--collapsed]=\"\n collapsible && collapsed\n \"\n [class.nui-widget__header__container--expanded]=\"\n collapsible && !collapsed\n \"\n [@expandedState]=\"state\"\n >\n <div\n *ngIf=\"collapsed && collapsible\"\n class=\"collapse-container\"\n (click)=\"toggleCollapsed()\"\n >\n <span class=\"collapse-container__line\"></span>\n </div>\n <div class=\"nui-widget__header__content\">\n <div\n *ngIf=\"editMode\"\n class=\"nui-widget__header__content-drag-icon nui-dashboard__widget--drag-handle\"\n >\n <nui-icon icon=\"drag\" iconColor=\"gray\"></nui-icon>\n </div>\n <div\n #widgetHeaderCustomElement\n class=\"nui-widget__header__content-custom-element\"\n >\n <ng-content></ng-content>\n </div>\n <div\n class=\"nui-widget__header__content-title-container nui-dashboard__widget--drag-handle\"\n [class.nui-widget__header__content-title-with-additional-content]=\"\n withCustomElement || editMode\n \"\n >\n <!-- text only header with no url defined -->\n <div\n class=\"nui-text-widget nui-widget__header__content-title\"\n [title]=\"title\"\n *ngIf=\"!url || editMode\"\n >\n {{ title }}\n </div>\n\n <!-- link version of the header -->\n <div *ngIf=\"url && !editMode\">\n <a\n [href]=\"url\"\n (click)=\"prepareLink($event)\"\n (mouseenter)=\"prepareLink($event)\"\n (mousedown)=\"\n $event.stopPropagation(); $event.preventDefault()\n \"\n class=\"nui-text-widget nui-widget__header__content-title\"\n [nuiTooltip]=\"linkTooltip\"\n >{{ title }}</a\n >\n </div>\n\n <div\n *ngIf=\"subtitle\"\n class=\"nui-text-hint nui-widget__header__content-help-hint\"\n [title]=\"subtitle\"\n >\n {{ subtitle }}\n </div>\n </div>\n <div class=\"nui-widget__header__content-actions\">\n <a\n *ngIf=\"url && !editMode\"\n nui-button\n displayStyle=\"action\"\n [href]=\"url\"\n (click)=\"prepareLink($event)\"\n (mouseenter)=\"prepareLink($event)\"\n icon=\"external-link\"\n [nuiTooltip]=\"linkTooltip\"\n (mousedown)=\"\n $event.stopPropagation(); $event.preventDefault()\n \"\n >\n </a>\n <button\n *ngIf=\"editable && editMode\"\n class=\"nui-widget-header__action-edit\"\n nui-button\n type=\"button\"\n displayStyle=\"action\"\n icon=\"edit\"\n nuiTooltip=\"Edit widget\"\n i18n-nuiTooltip\n (click)=\"onEditWidget()\"\n ></button>\n <button\n *ngIf=\"collapsible && !editMode\"\n class=\"nui-widget-header__action-toggle-collapsed\"\n nui-button\n type=\"button\"\n displayStyle=\"action\"\n nuiTooltip=\"Collapse header\"\n icon=\"double-caret-up\"\n (click)=\"toggleCollapsed()\"\n ></button>\n <nui-menu\n *ngIf=\"!hideMenu && !editMode\"\n class=\"nui-widget__header__content-actions__menu\"\n icon=\"menu\"\n displayStyle=\"action\"\n [appendToBody]=\"true\"\n >\n <nui-menu-action\n *ngIf=\"reloadable\"\n (actionDone)=\"onReloadData()\"\n >\n <nui-icon icon=\"reload\"></nui-icon>\n <span i18n>Reload data</span>\n </nui-menu-action>\n </nui-menu>\n <!--TODO: After NUI-3431 should be changed to regular menu instead of popup-->\n <nui-popup\n class=\"nui-widget-header__action-remove\"\n [appendToBody]=\"true\"\n *ngIf=\"removable && editMode\"\n >\n <span nuiTooltip=\"Remove widget\" i18n-nuiTooltip>\n <button\n nui-button\n nuiPopupToggle\n type=\"button\"\n icon=\"remove\"\n iconColor=\"red\"\n displayStyle=\"action\"\n i18n\n ></button>\n </span>\n <div popupAreaContent class=\"nui-menu\">\n <nui-menu-action\n type=\"destructive\"\n (actionDone)=\"removeWidget()\"\n i18n\n >Remove Widget</nui-menu-action\n >\n </div>\n </nui-popup>\n </div>\n </div>\n </div>\n <div *ngIf=\"collapsible\" class=\"collapse-spacer\"></div>\n</div>\n", styles: [".nui-widget__header{display:flex;position:relative}.nui-widget__header .collapse-spacer{margin-bottom:15px}.nui-widget__header__container{z-index:500;width:100%}.nui-widget__header__container--collapsible{width:100%}.nui-widget__header__container--collapsed .nui-widget__header__content{opacity:0;visibility:hidden}.nui-widget__header__container .collapse-container{width:100%;display:flex;justify-content:center;cursor:pointer}.nui-widget__header__container .collapse-container__line{width:16px;height:1px;background:var(--nui-color-line-active,#0079aa);margin:5px 0;cursor:pointer}.nui-widget__header__container .nui-widget__header__content{align-items:flex-start;display:flex;opacity:1}.nui-widget__header__container .nui-widget__header__content-drag-icon{align-items:center;cursor:move;display:flex;height:30px;justify-content:center;margin:5px;width:30px}.nui-widget__header__container .nui-widget__header__content-title{display:block;max-width:100%;white-space:nowrap;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}.nui-widget__header__container .nui-widget__header__content-title-container{flex:1;min-width:0;padding:10px 0 5px 10px}.nui-widget__header__container .nui-widget__header__content-title-with-additional-content{padding-left:0}.nui-widget__header__container .nui-widget__header__content-help-hint{max-width:100%;white-space:nowrap;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}.nui-widget__header__container .nui-widget__header__content-actions{display:flex;padding:5px 10px 0 5px}\n"], dependencies: [{ kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4.ButtonComponent, selector: "[nui-button]", inputs: ["displayStyle", "icon", "iconColor", "iconRight", "isBusy", "isEmpty", "ariaLabel", "isRepeat", "size"] }, { kind: "component", type: i4.IconComponent, selector: "nui-icon", inputs: ["iconColor", "brushType", "iconHoverColor", "iconSize", "cssClass", "fillContainer", "status", "childStatus", "icon", "counter"] }, { kind: "component", type: i4.MenuActionComponent, selector: "nui-menu-action", inputs: ["icon", "type"] }, { kind: "component", type: i4.MenuComponent, selector: "nui-menu", inputs: ["widthOfPopup", "contextClass", "appendToBody", "ariaLabel", "title", "size", "displayStyle", "isDisabled", "icon", "itemsSource"], outputs: ["blurred"] }, { kind: "directive", type: i4.PopupToggleDirective, selector: "[nuiPopupToggle]", inputs: ["isDisabled", "disabled"], outputs: ["toggle"] }, { kind: "component", type: i4.PopupComponent, selector: "nui-popup", inputs: ["width", "overlayConfig", "contextClass", "directionTop", "directionRight", "manualOpenControl", "appendToBody", "baseElementSelector", "isHostToggleRef", "ariaLabel", "isOpen", "visible"], outputs: ["opened"] }, { kind: "directive", type: i4.TooltipDirective, selector: "[nuiTooltip]", inputs: ["tooltipPlacement", "nuiTooltipDisabled", "nuiTooltipEllipsis", "nuiTooltip"], exportAs: ["nuiTooltip"] }], animations: [ trigger("expandedState", [ state("expanded", style({ height: "*" })), state("collapsed", style({ height: "11px" })), transition("expanded <=> collapsed", [ animate("350ms ease-in-out"), ]), ]), ] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetHeaderComponent, decorators: [{ type: Component, args: [{ selector: "nui-widget-header", animations: [ trigger("expandedState", [ state("expanded", style({ height: "*" })), state("collapsed", style({ height: "11px" })), transition("expanded <=> collapsed", [ animate("350ms ease-in-out"), ]), ]), ], template: "<div class=\"nui-widget__header\">\n <div\n class=\"nui-widget__header__container\"\n [class.nui-widget__header__container--collapsible]=\"collapsible\"\n [class.nui-widget__header__container--collapsed]=\"\n collapsible && collapsed\n \"\n [class.nui-widget__header__container--expanded]=\"\n collapsible && !collapsed\n \"\n [@expandedState]=\"state\"\n >\n <div\n *ngIf=\"collapsed && collapsible\"\n class=\"collapse-container\"\n (click)=\"toggleCollapsed()\"\n >\n <span class=\"collapse-container__line\"></span>\n </div>\n <div class=\"nui-widget__header__content\">\n <div\n *ngIf=\"editMode\"\n class=\"nui-widget__header__content-drag-icon nui-dashboard__widget--drag-handle\"\n >\n <nui-icon icon=\"drag\" iconColor=\"gray\"></nui-icon>\n </div>\n <div\n #widgetHeaderCustomElement\n class=\"nui-widget__header__content-custom-element\"\n >\n <ng-content></ng-content>\n </div>\n <div\n class=\"nui-widget__header__content-title-container nui-dashboard__widget--drag-handle\"\n [class.nui-widget__header__content-title-with-additional-content]=\"\n withCustomElement || editMode\n \"\n >\n <!-- text only header with no url defined -->\n <div\n class=\"nui-text-widget nui-widget__header__content-title\"\n [title]=\"title\"\n *ngIf=\"!url || editMode\"\n >\n {{ title }}\n </div>\n\n <!-- link version of the header -->\n <div *ngIf=\"url && !editMode\">\n <a\n [href]=\"url\"\n (click)=\"prepareLink($event)\"\n (mouseenter)=\"prepareLink($event)\"\n (mousedown)=\"\n $event.stopPropagation(); $event.preventDefault()\n \"\n class=\"nui-text-widget nui-widget__header__content-title\"\n [nuiTooltip]=\"linkTooltip\"\n >{{ title }}</a\n >\n </div>\n\n <div\n *ngIf=\"subtitle\"\n class=\"nui-text-hint nui-widget__header__content-help-hint\"\n [title]=\"subtitle\"\n >\n {{ subtitle }}\n </div>\n </div>\n <div class=\"nui-widget__header__content-actions\">\n <a\n *ngIf=\"url && !editMode\"\n nui-button\n displayStyle=\"action\"\n [href]=\"url\"\n (click)=\"prepareLink($event)\"\n (mouseenter)=\"prepareLink($event)\"\n icon=\"external-link\"\n [nuiTooltip]=\"linkTooltip\"\n (mousedown)=\"\n $event.stopPropagation(); $event.preventDefault()\n \"\n >\n </a>\n <button\n *ngIf=\"editable && editMode\"\n class=\"nui-widget-header__action-edit\"\n nui-button\n type=\"button\"\n displayStyle=\"action\"\n icon=\"edit\"\n nuiTooltip=\"Edit widget\"\n i18n-nuiTooltip\n (click)=\"onEditWidget()\"\n ></button>\n <button\n *ngIf=\"collapsible && !editMode\"\n class=\"nui-widget-header__action-toggle-collapsed\"\n nui-button\n type=\"button\"\n displayStyle=\"action\"\n nuiTooltip=\"Collapse header\"\n icon=\"double-caret-up\"\n (click)=\"toggleCollapsed()\"\n ></button>\n <nui-menu\n *ngIf=\"!hideMenu && !editMode\"\n class=\"nui-widget__header__content-actions__menu\"\n icon=\"menu\"\n displayStyle=\"action\"\n [appendToBody]=\"true\"\n >\n <nui-menu-action\n *ngIf=\"reloadable\"\n (actionDone)=\"onReloadData()\"\n >\n <nui-icon icon=\"reload\"></nui-icon>\n <span i18n>Reload data</span>\n </nui-menu-action>\n </nui-menu>\n <!--TODO: After NUI-3431 should be changed to regular menu instead of popup-->\n <nui-popup\n class=\"nui-widget-header__action-remove\"\n [appendToBody]=\"true\"\n *ngIf=\"removable && editMode\"\n >\n <span nuiTooltip=\"Remove widget\" i18n-nuiTooltip>\n <button\n nui-button\n nuiPopupToggle\n type=\"button\"\n icon=\"remove\"\n iconColor=\"red\"\n displayStyle=\"action\"\n i18n\n ></button>\n </span>\n <div popupAreaContent class=\"nui-menu\">\n <nui-menu-action\n type=\"destructive\"\n (actionDone)=\"removeWidget()\"\n i18n\n >Remove Widget</nui-menu-action\n >\n </div>\n </nui-popup>\n </div>\n </div>\n </div>\n <div *ngIf=\"collapsible\" class=\"collapse-spacer\"></div>\n</div>\n", styles: [".nui-widget__header{display:flex;position:relative}.nui-widget__header .collapse-spacer{margin-bottom:15px}.nui-widget__header__container{z-index:500;width:100%}.nui-widget__header__container--collapsible{width:100%}.nui-widget__header__container--collapsed .nui-widget__header__content{opacity:0;visibility:hidden}.nui-widget__header__container .collapse-container{width:100%;display:flex;justify-content:center;cursor:pointer}.nui-widget__header__container .collapse-container__line{width:16px;height:1px;background:var(--nui-color-line-active,#0079aa);margin:5px 0;cursor:pointer}.nui-widget__header__container .nui-widget__header__content{align-items:flex-start;display:flex;opacity:1}.nui-widget__header__container .nui-widget__header__content-drag-icon{align-items:center;cursor:move;display:flex;height:30px;justify-content:center;margin:5px;width:30px}.nui-widget__header__container .nui-widget__header__content-title{display:block;max-width:100%;white-space:nowrap;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}.nui-widget__header__container .nui-widget__header__content-title-container{flex:1;min-width:0;padding:10px 0 5px 10px}.nui-widget__header__container .nui-widget__header__content-title-with-additional-content{padding-left:0}.nui-widget__header__container .nui-widget__header__content-help-hint{max-width:100%;white-space:nowrap;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}.nui-widget__header__container .nui-widget__header__content-actions{display:flex;padding:5px 10px 0 5px}\n"] }] }], ctorParameters: () => [{ type: i4.EventBus, decorators: [{ type: Inject, args: [PIZZAGNA_EVENT_BUS] }] }, { type: i1.PizzagnaService }, { type: i0.ChangeDetectorRef }, { type: i2.WidgetToDashboardEventProxyService, decorators: [{ type: Optional }] }, { type: undefined, decorators: [{ type: Inject, args: [HEADER_LINK_PROVIDER] }, { type: Optional }] }], propDecorators: { componentId: [{ type: Input }], editMode: [{ type: Input }], editable: [{ type: Input }], removable: [{ type: Input }], collapsible: [{ type: Input }], reloadable: [{ type: Input }], title: [{ type: Input }], subtitle: [{ type: Input }], hideMenu: [{ type: Input }], url: [{ type: Input }], collapsed: [{ type: Input }], hostClass: [{ type: HostBinding, args: ["class.nui-widget-header"] }], widgetHeaderCustomElement: [{ type: ViewChild, args: ["widgetHeaderCustomElement"] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2lkZ2V0LWhlYWRlci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvd2lkZ2V0L3dpZGdldC1oZWFkZXIvd2lkZ2V0LWhlYWRlci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvd2lkZ2V0L3dpZGdldC1oZWFkZXIvd2lkZ2V0LWhlYWRlci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx5REFBeUQ7QUFDekQsRUFBRTtBQUNGLCtFQUErRTtBQUMvRSw0RUFBNEU7QUFDNUUsOEVBQThFO0FBQzlFLCtFQUErRTtBQUMvRSw4RUFBOEU7QUFDOUUsNERBQTREO0FBQzVELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsdURBQXVEO0FBQ3ZELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsNEVBQTRFO0FBQzVFLCtFQUErRTtBQUMvRSwwRUFBMEU7QUFDMUUsaUZBQWlGO0FBQ2pGLDZFQUE2RTtBQUM3RSxpQkFBaUI7QUFFakIsT0FBTyxFQUNILE9BQU8sRUFDUCxLQUFLLEVBQ0wsS0FBSyxFQUNMLFVBQVUsRUFDVixPQUFPLEdBQ1YsTUFBTSxxQkFBcUIsQ0FBQztBQUM3QixPQUFPLEVBRUgsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxVQUFVLEVBQ1YsV0FBVyxFQUNYLE1BQU0sRUFDTixLQUFLLEVBR0wsUUFBUSxFQUNSLFNBQVMsR0FDWixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUUzQyxPQUFPLEVBQUUsUUFBUSxFQUFVLE1BQU0sZUFBZSxDQUFDO0FBR2pELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSw2Q0FBNkMsQ0FBQztBQUM5RSxPQUFPLEVBQ0gsbUJBQW1C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Q0wsQ0FBQztxQkFDTDs7MEJBOENJLE1BQU07MkJBQUMsa0JBQWtCOzswQkFHekIsUUFBUTs7MEJBQ1IsTUFBTTsyQkFBQyxvQkFBb0I7OzBCQUMzQixRQUFRO3lDQTlDRyxXQUFXO3NCQUExQixLQUFLO2dCQUNVLFFBQVE7c0JBQXZCLEtBQUs7Z0JBSVUsUUFBUTtzQkFBdkIsS0FBSztnQkFJVSxTQUFTO3NCQUF4QixLQUFLO2dCQUlVLFdBQVc7c0JBQTFCLEtBQUs7Z0JBQ1UsVUFBVTtzQkFBekIsS0FBSztnQkFDVSxLQUFLO3NCQUFwQixLQUFLO2dCQUNVLFFBQVE7c0JBQXZCLEtBQUs7Z0JBQ1UsUUFBUTtzQkFBdkIsS0FBSztnQkFDVSxHQUFHO3NCQUFsQixLQUFLO2dCQUlVLFNBQVM7c0JBQXhCLEtBQUs7Z0JBRXNDLFNBQVM7c0JBQXBELFdBQVc7dUJBQUMseUJBQXlCO2dCQUsvQix5QkFBeUI7c0JBRC9CLFNBQVM7dUJBQUMsMkJBQTJCIiwic291cmNlc0NvbnRlbnQiOlsiLy8gwqkgMjAyMiBTb2xhcldpbmRzIFdvcmxkd2lkZSwgTExDLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuLy9cbi8vIFBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHlcbi8vICBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZSBcIlNvZnR3YXJlXCIpLCB0b1xuLy8gIGRlYWwgaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlXG4vLyAgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwgZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yXG4vLyAgc2VsbCBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXNcbi8vICBmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOlxuLy9cbi8vIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluXG4vLyAgYWxsIGNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuXG4vL1xuLy8gVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUlxuLy8gIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZLFxuLy8gIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRVxuLy8gIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVJcbi8vICBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLFxuLy8gIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU5cbi8vICBUSEUgU09GVFdBUkUuXG5cbmltcG9ydCB7XG4gICAgYW5pbWF0ZSxcbiAgICBzdGF0ZSxcbiAgICBzdHlsZSxcbiAgICB0cmFuc2l0aW9uLFxuICAgIHRyaWdnZXIsXG59IGZyb20gXCJAYW5ndWxhci9hbmltYXRpb25zXCI7XG5pbXBvcnQge1xuICAgIEFmdGVyVmlld0luaXQsXG4gICAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgQ29tcG9uZW50LFxuICAgIEVsZW1lbnRSZWYsXG4gICAgSG9zdEJpbmRpbmcsXG4gICAgSW5qZWN0LFxuICAgIElucHV0LFxuICAgIE9uRGVzdHJveSxcbiAgICBPbkluaXQsXG4gICAgT3B0aW9uYWwsXG4gICAgVmlld0NoaWxkLFxufSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuaW1wb3J0IHsgU3ViamVjdCB9IGZyb20gXCJyeGpzXCI7XG5pbXBvcnQgeyB0YWtlVW50aWwgfSBmcm9tIFwicnhqcy9vcGVyYXRvcnNcIjtcblxuaW1wb3J0IHsgRXZlbnRCdXMsIElFdmVudCB9IGZyb20gXCJAbm92YS11aS9iaXRzXCI7XG5cbmltcG9ydCB7IElIZWFkZXJMaW5rUHJvdmlkZXIgfSBmcm9tIFwiLi90eXBlc1wiO1xuaW1wb3J0IHsgUGl6emFnbmFTZXJ2aWNlIH0gZnJvbSBcIi4uLy4uLy4uL3BpenphZ25hL3NlcnZpY2VzL3BpenphZ25hLnNlcnZpY2VcIjtcbmltcG9ydCB7XG4gICAgREFTSEJPQVJEX0VESVRfTU9ERSxcbiAgICBSRUZSRVNILFxuICAgIFdJREdFVF9FRElULFxuICAgIFdJREdFVF9SRU1PVkUsXG59IGZyb20gXCIuLi8uLi8uLi9zZXJ2aWNlcy90eXBlc1wiO1xuaW1wb3J0IHsgV2lkZ2V0VG9EYXNoYm9hcmRFdmVudFByb3h5U2VydmljZSB9IGZyb20gXCIuLi8uLi8uLi9zZXJ2aWNlcy93aWRnZXQtdG8tZGFzaGJvYXJkLWV2ZW50LXByb3h5LnNlcnZpY2VcIjtcbmltcG9ydCB7XG4gICAgSEVBREVSX0xJTktfUFJPVklERVIsXG4gICAgUElaWkFHTkFfRVZFTlRfQlVTLFxuICAgIFBpenphZ25hTGF5ZXIsXG59IGZyb20gXCIuLi8uLi8uLi90eXBlc1wiO1xuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogXCJudWktd2lkZ2V0LWhlYWRlclwiLFxuICAgIHRlbXBsYXRlVXJsOiBcIi4vd2lkZ2V0LWhlYWRlci5jb21wb25lbnQuaHRtbFwiLFxuICAgIHN0eWxlVXJsczogW1wiLi93aWRnZXQtaGVhZGVyLmNvbXBvbmVudC5sZXNzXCJdLFxuICAgIGFuaW1hdGlvbnM6IFtcbiAgICAgICAgdHJpZ2dlcihcImV4cGFuZGVkU3RhdGVcIiwgW1xuICAgICAgICAgICAgc3RhdGUoXCJleHBhbmRlZFwiLCBzdHlsZSh7IGhlaWdodDogXCIqXCIgfSkpLFxuICAgICAgICAgICAgc3RhdGUoXCJjb2xsYXBzZWRcIiwgc3R5bGUoeyBoZWlnaHQ6IFwiMTFweFwiIH0pKSxcbiAgICAgICAgICAgIHRyYW5zaXRpb24oXCJleHBhbmRlZCA8PT4gY29sbGFwc2VkXCIsIFtcbiAgICAgICAgICAgICAgICBhbmltYXRlKFwiMzUwbXMgZWFzZS1pbi1vdXRcIiksXG4gICAgICAgICAgICBdKSxcbiAgICAgICAgXSksXG4gICAgXSxcbn0pXG5leHBvcnQgY2xhc3MgV2lkZ2V0SGVhZGVyQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3ksIEFmdGVyVmlld0luaXQge1xuICAgIHN0YXRpYyBsYXRlTG9hZEtleSA9IFwiV2lkZ2V0SGVhZGVyQ29tcG9uZW50XCI7XG5cbiAgICBASW5wdXQoKSBwdWJsaWMgY29tcG9uZW50SWQ6IHN0cmluZztcbiAgICBASW5wdXQoKSBwdWJsaWMgZWRpdE1vZGUgPSB0cnVlO1xuICAgIC8qKlxuICAgICAqIEJvb2xlYW4gd2hpY2ggc2hvd3Mgb3IgaGlkZXMgdGhlIGVkaXQgYnV0dG9uXG4gICAgICovXG4gICAgQElucHV0KCkgcHVibGljIGVkaXRhYmxlOiBib29sZWFuID0gdHJ1ZTtcbiAgICAvKipcbiAgICAgKiBCb29sZWFuIHdoaWNoIHNob3dzIG9yIGhpZGVzIHRoZSByZW1vdmUgd2lkZ2V0IGJ1dHRvblxuICAgICAqL1xuICAgIEBJbnB1dCgpIHB1YmxpYyByZW1vdmFibGU6IGJvb2xlYW4gPSB0cnVlO1xuICAgIC8qKlxuICAgICAqIEJvb2xlYW4gd2hpY2ggYWRkcyBhYmlsaXR5IHRvIGNvbGxhcHNlIHdpZGdldCBoZWFkZXJcbiAgICAgKi9cbiAgICBASW5wdXQoKSBwdWJsaWMgY29sbGFwc2libGUgPSBmYWxzZTtcbiAgICBASW5wdXQoKSBwdWJsaWMgcmVsb2FkYWJsZSA9IHRydWU7XG4gICAgQElucHV0KCkgcHVibGljIHRpdGxlOiBzdHJpbmc7XG4gICAgQElucHV0KCkgcHVibGljIHN1YnRpdGxlOiBzdHJpbmc7XG4gICAgQElucHV0KCkgcHVibGljIGhpZGVNZW51OiBib29sZWFuID0gZmFsc2U7XG4gICAgQElucHV0KCkgcHVibGljIHVybDogc3RyaW5nO1xuICAgIC8qKlxuICAgICAqIEJvb2xlYW4gd2hpY2ggdGVsbHMgd2hhdCBzdGF0ZSB0aGUgd2lkZ2V0IGhlYWRlciBpcyBpbiBpZiBjb2xsYXBzaWJsZVxuICAgICAqL1xuICAgIEBJbnB1dCgpIHB1YmxpYyBjb2xsYXBzZWQ6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIEBIb3N0QmluZGluZyhcImNsYXNzLm51aS13aWRnZXQtaGVhZGVyXCIpIGdldCBob3N0Q2xhc3MoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIEBWaWV3Q2hpbGQoXCJ3aWRnZXRIZWFkZXJDdXN0b21FbGVtZW50XCIpXG4gICAgcHVibGljIHdpZGdldEhlYWRlckN1c3RvbUVsZW1lbnQ6IEVsZW1lbnRSZWY7XG5cbiAgICBwdWJsaWMgd2l0aEN1c3RvbUVsZW1lbnQ6IGJvb2xlYW47XG4gICAgcHJpdmF0ZSBvbkRlc3Ryb3kkOiBTdWJqZWN0PHZvaWQ+ID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICAgIHB1YmxpYyBnZXQgc3RhdGUoKTogXCJleHBhbmRlZFwiIHwgXCJjb2xsYXBzZWRcIiB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbGxhcHNlZCAmJiB0aGlzLmNvbGxhcHNpYmxlID8gXCJjb2xsYXBzZWRcIiA6IFwiZXhwYW5kZWRcIjtcbiAgICB9XG5cbiAgICBwdWJsaWMgbGlua1Rvb2x0aXAgPSAkbG9jYWxpemVgRXhwbG9yZSB0aGlzIGRhdGFgO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIEBJbmplY3QoUElaWkFHTkFfRVZFTlRfQlVTKSBwcml2YXRlIGV2ZW50QnVzOiBFdmVudEJ1czxJRXZlbnQ+LFxuICAgICAgICBwdWJsaWMgcGl6emFnbmFTZXJ2aWNlOiBQaXp6YWduYVNlcnZpY2UsXG4gICAgICAgIHB1YmxpYyBjaGFuZ2VEZXRlY3RvcjogQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgICAgIEBPcHRpb25hbCgpIHByaXZhdGUgZXZlbnRQcm94eTogV2lkZ2V0VG9EYXNoYm9hcmRFdmVudFByb3h5U2VydmljZSxcbiAgICAgICAgQEluamVjdChIRUFERVJfTElOS19QUk9WSURFUilcbiAgICAgICAgQE9wdGlvbmFsKClcbiAgICAgICAgcHJpdmF0ZSBsaW5rUHJvdmlkZXI6IElIZWFkZXJMaW5rUHJvdmlkZXJcbiAgICApIHt9XG5cbiAgICBwdWJsaWMgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLmNvbGxhcHNpYmxlKSB7XG4gICAgICAgICAgICB0aGlzLnBpenphZ25hU2VydmljZS5zZXRQcm9wZXJ0eShcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudElkOiB0aGlzLmNvbXBvbmVudElkLFxuICAgICAgICAgICAgICAgICAgICBwaXp6YWduYUtleTogUGl6emFnbmFMYXllci5EYXRhLFxuICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0eVBhdGg6IFtcImlzQ29sbGFwc2VkXCJdLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgdHJ1ZVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLmV2ZW50UHJveHkpIHtcbiAgICAgICAgICAgIHRoaXMuZXZlbnRQcm94eS5hZGRVcHN0cmVhbShXSURHRVRfRURJVCk7XG4gICAgICAgICAgICB0aGlzLmV2ZW50UHJveHkuYWRkVXBzdHJlYW0oV0lER0VUX1JFTU9WRSk7XG4gICAgICAgICAgICB0aGlzLmV2ZW50UHJveHkuYWRkRG93bnN0cmVhbShEQVNIQk9BUkRfRURJVF9NT0RFKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHN1YnNjcmliaW5nIHRvIGRhc2hib2FyZCBldmVudCB0byBzZXQgJ2VkaXQgbW9kZSdcbiAgICAgICAgdGhpcy5ldmVudEJ1c1xuICAgICAgICAgICAgLmdldFN0cmVhbShEQVNIQk9BUkRfRURJVF9NT0RFKVxuICAgICAgICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMub25EZXN0cm95JCkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKChldmVudCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMucGl6emFnbmFTZXJ2aWNlLnNldFByb3BlcnR5KFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb21wb25lbnRJZDogdGhpcy5jb21wb25lbnRJZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHBpenphZ25hS2V5OiBQaXp6YWduYUxheWVyLkRhdGEsXG4gICAgICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0eVBhdGg6IFtcImNvbGxhcHNlZFwiXSxcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgIWV2ZW50LnBheWxvYWRcbiAgICAgICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAgICAgdGhpcy5waXp6YWduYVNlcnZpY2Uuc2V0UHJvcGVydHkoXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbXBvbmVudElkOiB0aGlzLmNvbXBvbmVudElkLFxuICAgICAgICAgICAgICAgICAgICAgICAgcGl6emFnbmFLZXk6IFBpenphZ25hTGF5ZXIuRGF0YSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHByb3BlcnR5UGF0aDogW1wiZWRpdE1vZGVcIl0sXG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICEhZXZlbnQucGF5bG9hZFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuICAgICAgICAvLyB3ZSBjYW4ndCBzZXQgdmFsdWVzIGluIG5nQWZ0ZXJWaWV3SW5pdCBkaXJlY3RseSAtIGl0IGNhdXNlcyBFeHByZXNzaW9uQ2hhbmdlZEFmdGVyVmlld0NoZWNrZWQgZXJyb3IsIHNvIHNldFRpbWVvdXQgaGFzIHRvIGJlIHVzZWRcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLndpdGhDdXN0b21FbGVtZW50ID1cbiAgICAgICAgICAgICAgICB0aGlzLndpZGdldEhlYWRlckN1c3RvbUVsZW1lbnQubmF0aXZlRWxlbWVudC5jaGlsZE5vZGVzXG4gICAgICAgICAgICAgICAgICAgIC5sZW5ndGggIT09IDA7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHB1YmxpYyBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5vbkRlc3Ryb3kkLm5leHQoKTtcbiAgICAgICAgdGhpcy5vbkRlc3Ryb3kkLmNvbXBsZXRlKCk7XG4gICAgfVxuXG4gICAgcHVibGljIHJlbW92ZVdpZGdldCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5nZXRTdHJlYW0oV0lER0VUX1JFTU9WRSkubmV4dCh7fSk7XG4gICAgfVxuXG4gICAgcHVibGljIHRvZ2dsZUNvbGxhcHNlZCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5waXp6YWduYVNlcnZpY2Uuc2V0UHJvcGVydHkoXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgY29tcG9uZW50SWQ6IHRoaXMuY29tcG9uZW50SWQsXG4gICAgICAgICAgICAgICAgcGl6emFnbmFLZXk6IFBpenphZ25hTGF5ZXIuRGF0YSxcbiAgICAgICAgICAgICAgICBwcm9wZXJ0eVBhdGg6IFtcImNvbGxhcHNlZFwiXSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAhdGhpcy5jb2xsYXBzZWRcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgb25FZGl0V2lkZ2V0KCk6IHZvaWQge1xuICAgICAgICB0aGlzLmV2ZW50QnVzLmdldFN0cmVhbShXSURHRVRfRURJVCkubmV4dCh7fSk7XG4gICAgfVxuXG4gICAgcHVibGljIG9uUmVsb2FkRGF0YSgpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLnJlbG9hZGFibGUpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICBcIlRoZSB3aWRnZXQgaXMgbm90IHJlbG9hZGFibGUsIHNvIGl0IGNhbid0IGJlIHJlbG9hZGVkIG1hbnVhbGx5LlwiXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZXZlbnRCdXMuZ2V0U3RyZWFtKFJFRlJFU0gpLm5leHQoe30pO1xuICAgIH1cblxuICAgIHB1YmxpYyBwcmVwYXJlTGluaygkZXZlbnQ6IE1vdXNlRXZlbnQpOiBib29sZWFuIHtcbiAgICAgICAgY29uc3QgdGFyZ2V0OiBIVE1MRWxlbWVudCA9ICRldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7XG4gICAgICAgIGlmICh0YXJnZXQgJiYgdGhpcy5saW5rUHJvdmlkZXIpIHtcbiAgICAgICAgICAgIGNvbnN0IGxpbmsgPSB0aGlzLmxpbmtQcm92aWRlci5nZXRMaW5rKHRoaXMudXJsKTtcbiAgICAgICAgICAgIGlmIChsaW5rKSB7XG4gICAgICAgICAgICAgICAgdGFyZ2V0LnNldEF0dHJpYnV0ZShcImhyZWZcIiwgbGluayk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxufVxuIiwiPGRpdiBjbGFzcz1cIm51aS13aWRnZXRfX2hlYWRlclwiPlxuICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJudWktd2lkZ2V0X19oZWFkZXJfX2NvbnRhaW5lclwiXG4gICAgICAgIFtjbGFzcy5udWktd2lkZ2V0X19oZWFkZXJfX2NvbnRhaW5lci0tY29sbGFwc2libGVdPVwiY29sbGFwc2libGVcIlxuICAgICAgICBbY2xhc3MubnVpLXdpZGdldF9faGVhZGVyX19jb250YWluZXItLWNvbGxhcHNlZF09XCJcbiAgICAgICAgICAgIGNvbGxhcHNpYmxlICYmIGNvbGxhcHNlZFxuICAgICAgICBcIlxuICAgICAgICBbY2xhc3MubnVpLXdpZGdldF9faGVhZGVyX19jb250YWluZXItLWV4cGFuZGVkXT1cIlxuICAgICAgICAgICAgY29sbGFwc2libGUgJiYgIWNvbGxhcHNlZFxuICAgICAgICBcIlxuICAgICAgICBbQGV4cGFuZGVkU3RhdGVdPVwic3RhdGVcIlxuICAgID5cbiAgICAgICAgPGRpdlxuICAgICAgICAgICAgKm5nSWY9XCJjb2xsYXBzZWQgJiYgY29sbGFwc2libGVcIlxuICAgICAgICAgICAgY2xhc3M9XCJjb2xsYXBzZS1jb250YWluZXJcIlxuICAgICAgICAgICAgKGNsaWNrKT1cInRvZ2dsZUNvbGxhcHNlZCgpXCJcbiAgICAgICAgPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJjb2xsYXBzZS1jb250YWluZXJfX2xpbmVcIj48L3NwYW4+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8ZGl2IGNsYXNzPVwibnVpLXdpZGdldF9faGVhZGVyX19jb250ZW50XCI+XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgICAgKm5nSWY9XCJlZGl0TW9kZVwiXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJudWktd2lkZ2V0X19oZWFkZXJfX2NvbnRlbnQtZHJhZy1pY29uIG51aS1kYXNoYm9hcmRfX3dpZGdldC0tZHJhZy1oYW5kbGVcIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxudWktaWNvbiBpY29uPVwiZHJhZ1wiIGljb25Db2xvcj1cImdyYXlcIj48L251aS1pY29uPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgICAgI3dpZGdldEhlYWRlckN1c3RvbUVsZW1lbnRcbiAgICAgICAgICAgICAgICBjbGFzcz1cIm51aS13aWRnZXRfX2hlYWRlcl9fY29udGVudC1jdXN0b20tZWxlbWVudFwiXG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgICAgY2xhc3M9XCJudWktd2lkZ2V0X19oZWFkZXJfX2NvbnRlbnQtdGl0bGUtY29udGFpbmVyIG51aS1kYXNoYm9hcmRfX3dpZGdldC0tZHJhZy1oYW5kbGVcIlxuICAgICAgICAgICAgICAgIFtjbGFzcy5udWktd2lkZ2V0X19oZWFkZXJfX2NvbnRlbnQtdGl0bGUtd2l0aC1hZGRpdGlvbmFsLWNvbnRlbnRdPVwiXG4gICAgICAgICAgICAgICAgICAgIHdpdGhDdXN0b21FbGVtZW50IHx8IGVkaXRNb2RlXG4gICAgICAgICAgICAgICAgXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICA8IS0tIHRleHQgb25seSBoZWFkZXIgd2l0aCBubyB1cmwgZGVmaW5lZCAtLT5cbiAgICAgICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgICAgICAgIGNsYXNzPVwibnVpLXRleHQtd2lkZ2V0IG51aS13aWRnZXRfX2hlYWRlcl9fY29udGVudC10aXRsZVwiXG4gICAgICAgICAgICAgICAgICAgIFt0aXRsZV09XCJ0aXRsZVwiXG4gICAgICAgICAgICAgICAgICAgICpuZ0lmPVwiIXVybCB8fCBlZGl0TW9kZVwiXG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICB7eyB0aXRsZSB9fVxuICAgICAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICAgICAgPCEtLSBsaW5rIHZlcnNpb24gb2YgdGhlIGhlYWRlciAtLT5cbiAgICAgICAgICAgICAgICA8ZGl2ICpuZ0lmPVwidXJsICYmICFlZGl0TW9kZVwiPlxuICAgICAgICAgICAgICAgICAgICA8YVxuICAgICAgICAgICAgICAgICAgICAgICAgW2hyZWZdPVwidXJsXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIChjbGljayk9XCJwcmVwYXJlTGluaygkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIChtb3VzZWVudGVyKT1cInByZXBhcmVMaW5rKCRldmVudClcIlxuICAgICAgICAgICAgICAgICAgICAgICAgKG1vdXNlZG93bik9XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7ICRldmVudC5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgICAgICAgICAgICAgICAgICBcIlxuICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJudWktdGV4dC13aWRnZXQgbnVpLXdpZGdldF9faGVhZGVyX19jb250ZW50LXRpdGxlXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIFtudWlUb29sdGlwXT1cImxpbmtUb29sdGlwXCJcbiAgICAgICAgICAgICAgICAgICAgICAgID57eyB0aXRsZSB9fTwvYVxuICAgICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgICAgICAgICpuZ0lmPVwic3VidGl0bGVcIlxuICAgICAgICAgICAgICAgICAgICBjbGFzcz1cIm51aS10ZXh0LWhpbnQgbnVpLXdpZGdldF9faGVhZGVyX19jb250ZW50LWhlbHAtaGludFwiXG4gICAgICAgICAgICAgICAgICAgIFt0aXRsZV09XCJzdWJ0aXRsZVwiXG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICB7eyBzdWJ0aXRsZSB9fVxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibnVpLXdpZGdldF9faGVhZGVyX19jb250ZW50LWFjdGlvbnNcIj5cbiAgICAgICAgICAgICAgICA8YVxuICAgICAgICAgICAgICAgICAgICAqbmdJZj1cInVybCAmJiAhZWRpdE1vZGVcIlxuICAgICAgICAgICAgICAgICAgICBudWktYnV0dG9uXG4gICAgICAgICAgICAgICAgICAgIGRpc3BsYXlTdHlsZT1cImFjdGlvblwiXG4gICAgICAgICAgICAgICAgICAgIFtocmVmXT1cInVybFwiXG4gICAgICAgICAgICAgICAgICAgIChjbGljayk9XCJwcmVwYXJlTGluaygkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICAgICAgKG1vdXNlZW50ZXIpPVwicHJlcGFyZUxpbmsoJGV2ZW50KVwiXG4gICAgICAgICAgICAgICAgICAgIGljb249XCJleHRlcm5hbC1saW5rXCJcbiAgICAgICAgICAgICAgICAgICAgW251aVRvb2x0aXBdPVwibGlua1Rvb2x0aXBcIlxuICAgICAgICAgICAgICAgICAgICAobW91c2Vkb3duKT1cIlxuICAgICAgICAgICAgICAgICAgICAgICAgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpOyAkZXZlbnQucHJldmVudERlZmF1bHQoKVxuICAgICAgICAgICAgICAgICAgICBcIlxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICA8L2E+XG4gICAgICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICAgICAgICAqbmdJZj1cImVkaXRhYmxlICYmIGVkaXRNb2RlXCJcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJudWktd2lkZ2V0LWhlYWRlcl9fYWN0aW9uLWVkaXRcIlxuICAgICAgICAgICAgICAgICAgICBudWktYnV0dG9uXG4gICAgICAgICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgICAgICBkaXNwbGF5U3R5bGU9XCJhY3Rpb25cIlxuICAgICAgICAgICAgICAgICAgICBpY29uPVwiZWRpdFwiXG4gICAgICAgICAgICAgICAgICAgIG51aVRvb2x0aXA9XCJFZGl0IHdpZGdldFwiXG4gICAgICAgICAgICAgICAgICAgIGkxOG4tbnVpVG9vbHRpcFxuICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwib25FZGl0V2lkZ2V0KClcIlxuICAgICAgICAgICAgICAgID48L2J1dHRvbj5cbiAgICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgICAgICpuZ0lmPVwiY29sbGFwc2libGUgJiYgIWVkaXRNb2RlXCJcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJudWktd2lkZ2V0LWhlYWRlcl9fYWN0aW9uLXRvZ2dsZS1jb2xsYXBzZWRcIlxuICAgICAgICAgICAgICAgICAgICBudWktYnV0dG9uXG4gICAgICAgICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgICAgICBkaXNwbGF5U3R5bGU9XCJhY3Rpb25cIlxuICAgICAgICAgICAgICAgICAgICBudWlUb29sdGlwPVwiQ29sbGFwc2UgaGVhZGVyXCJcbiAgICAgICAgICAgICAgICAgICAgaWNvbj1cImRvdWJsZS1jYXJldC11cFwiXG4gICAgICAgICAgICAgICAgICAgIChjbGljayk9XCJ0b2dnbGVDb2xsYXBzZWQoKVwiXG4gICAgICAgICAgICAgICAgPjwvYnV0dG9uPlxuICAgICAgICAgICAgICAgIDxudWktbWVudVxuICAgICAgICAgICAgICAgICAgICAqbmdJZj1cIiFoaWRlTWVudSAmJiAhZWRpdE1vZGVcIlxuICAgICAgICAgICAgICAgICAgICBjbGFzcz1cIm51aS13aWRnZXRfX2hlYWRlcl9fY29udGVudC1hY3Rpb25zX19tZW51XCJcbiAgICAgICAgICAgICAgICAgICAgaWNvbj1cIm1lbnVcIlxuICAgICAgICAgICAgICAgICAgICBkaXNwbGF5U3R5bGU9XCJhY3Rpb25cIlxuICAgICAgICAgICAgICAgICAgICBbYXBwZW5kVG9Cb2R5XT1cInRydWVcIlxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgPG51aS1tZW51LWFjdGlvblxuICAgICAgICAgICAgICAgICAgICAgICAgKm5nSWY9XCJyZWxvYWRhYmxlXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIChhY3Rpb25Eb25lKT1cIm9uUmVsb2FkRGF0YSgpXCJcbiAgICAgICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICAgICAgPG51aS1pY29uIGljb249XCJyZWxvYWRcIj48L251aS1pY29uPlxuICAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gaTE4bj5SZWxvYWQgZGF0YTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPC9udWktbWVudS1hY3Rpb24+XG4gICAgICAgICAgICAgICAgPC9udWktbWVudT5cbiAgICAgICAgICAgICAgICA8IS0tVE9ETzogQWZ0ZXIgTlVJLTM0MzEgc2hvdWxkIGJlIGNoYW5nZWQgdG8gcmVndWxhciBtZW51IGluc3RlYWQgb2YgcG9wdXAtLT5cbiAgICAgICAgICAgICAgICA8bnVpLXBvc