@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
186 lines • 35.7 kB
JavaScript
import { ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { CountdownIntervalComponent, gettext, IntervalBasedReload, WidgetGlobalAutoRefreshService } from '@c8y/ngx-components';
import { AlarmsViewService } from '@c8y/ngx-components/alarms';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject } from 'rxjs';
import { tap } from 'rxjs/operators';
import { GLOBAL_INTERVAL_OPTION } from './alarm-list-widget.model';
import * as i0 from "@angular/core";
import * as i1 from "@c8y/ngx-components/alarms";
import * as i2 from "@ngx-translate/core";
import * as i3 from "@c8y/ngx-components";
import * as i4 from "@angular/common";
import * as i5 from "ngx-bootstrap/tooltip";
export class AlarmWidgetAlarmsReloadComponent extends IntervalBasedReload {
constructor(alarmsViewService, cdRef, translateService, widgetGlobalAutoRefreshService) {
super();
this.alarmsViewService = alarmsViewService;
this.cdRef = cdRef;
this.translateService = translateService;
this.widgetGlobalAutoRefreshService = widgetGlobalAutoRefreshService;
this.REALTIME_UPDATE_ALARMS_MESSAGE = this.alarmsViewService.REALTIME_UPDATE_ALARMS_MESSAGE;
/**
* @inheritdoc
*/
this.onCountdownEnded = new EventEmitter();
/**
* Emitted to indicate that the widgets realtime button state has changed.
*/
this.onRealTimeToggleChanged = new EventEmitter();
/**
* Indicates whether the countdown has been cleared already.
*/
this.isNewAlarmMessageCleared = true;
/**
* @inheritdoc
*/
this.manuallyDisabledCountdown = false;
}
ngOnInit() {
if (!this.isIntervalRefresh) {
this.isRealtimeToggleOn = this.config.isAutoRefreshEnabled;
this.realtimeIconTitle = this.getRealtimeIconTitle();
}
else {
this.isIntervalRefreshToggleOn =
!this.isRefreshDisabled && (this.config.isAutoRefreshEnabled || !!this.refreshInterval);
this.updateCountdownButtonTooltipText();
}
}
ngAfterViewInit() {
if (this.isIntervalRefresh && this.refreshInterval) {
this.startCountdown();
}
if (this.globalAutoRefreshEnabled) {
this.widgetGlobalAutoRefreshService.countdownActions.countdownEnded$
.pipe(tap(() => this.onCountdownEnded.emit()))
.subscribe();
}
}
ngOnChanges(changes) {
const { isDisabled, config } = changes;
if (config) {
this.globalAutoRefreshEnabled = this.config.refreshOption === GLOBAL_INTERVAL_OPTION;
}
if (isDisabled) {
this.isIntervalRefreshToggleOn =
!this.isRefreshDisabled && (this.config.isAutoRefreshEnabled || !!this.refreshInterval);
this.updateCountdownButtonTooltipText(gettext('Disabled'));
}
if (!this.isIntervalRefresh ||
this.manuallyDisabledCountdown ||
!this.config.isAutoRefreshEnabled) {
return;
}
this.handleScrolling();
}
ngOnDestroy() {
if (this.countdownSubscription) {
this.countdownSubscription.unsubscribe();
}
}
/**
* @inheritdoc
*/
countdownEnded() {
/**
* @inheritdoc
*/
this.autoRefreshList();
}
reload() {
if (this.isIntervalRefresh) {
this.autoRefreshList();
}
else {
this.onCountdownEnded.emit();
this.isNewAlarmMessageCleared = true;
}
}
/**
* Toggles the real-time state of the component.
*
* This method switches the `isRealtimeToggleOn` state between true and false.
* When the state is toggled, it emits the current state through `onRealTimeToggleChanged` event.
* It also updates the `realtimeIconTitle` based on the new state.
*/
toggleRealtimeState() {
this.isRealtimeToggleOn = !this.isRealtimeToggleOn;
this.onRealTimeToggleChanged.emit(this.isRealtimeToggleOn);
this.realtimeIconTitle = this.getRealtimeIconTitle();
}
/**
* Stops the countdown and triggers a refresh action.
* This function is responsible for halting the countdown interval component's operation.
* After stopping the countdown, it emits an `onCountdownEnded` event.
* This event is used to inform external components that the countdown has ended,
* typically prompting them to reload or refresh their data.
*/
autoRefreshList() {
if (this.isIntervalRefreshToggleOn && this.config.isAutoRefreshEnabled) {
this.countdownIntervalComponent.stop();
}
this.onCountdownEnded.emit();
}
/**
* Sets the tooltip message for a new realtime alarm indicator.
* @returns Tooltip message.
*/
getRealtimeIconTitle() {
return this.isRealtimeToggleOn
? this.translateService.instant(gettext('Realtime active'))
: this.translateService.instant(gettext('Realtime inactive'));
}
/**
* Enables and starts the countdown timer.
*
* This method makes the countdown visible (`hideCountdown` is set to false) and then
* starts the countdown process. It ensures the countdown timer is updated immediately
* by triggering change detection with `cdRef.detectChanges()` before starting the countdown.
* This method encapsulates the logic required to initiate the countdown timer.
*/
enableCountdown() {
this.hideCountdown = false;
// Prevents the countdown from getting stuck on an initial value.
this.cdRef.detectChanges();
this.startCountdown();
}
/**
* @inheritdoc
*/
updateCountdownButtonTooltipText(customText) {
if (customText) {
this.toggleCountdownButtonTooltipText = customText;
return;
}
this.toggleCountdownButtonTooltipText = this.isIntervalRefreshToggleOn
? this.translateService.instant(gettext('Disable auto refresh'))
: this.translateService.instant(gettext('Enable auto refresh'));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmWidgetAlarmsReloadComponent, deps: [{ token: i1.AlarmsViewService }, { token: i0.ChangeDetectorRef }, { token: i2.TranslateService }, { token: i3.WidgetGlobalAutoRefreshService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AlarmWidgetAlarmsReloadComponent, selector: "c8y-alarm-widget-alarms-reload", inputs: { isIntervalRefresh: "isIntervalRefresh", refreshInterval: "refreshInterval", config: "config", isLoading: "isLoading", isScrolling: "isScrolling", isRefreshDisabled: "isRefreshDisabled" }, outputs: { onCountdownEnded: "onCountdownEnded", onRealTimeToggleChanged: "onRealTimeToggleChanged" }, viewQueries: [{ propertyName: "countdownIntervalComponent", first: true, predicate: CountdownIntervalComponent, descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div\n class=\"m-l-auto d-flex p-t-4 p-b-4\"\n *ngIf=\"!globalAutoRefreshEnabled\"\n>\n <div class=\"m-l-auto d-flex a-i-center\">\n <label\n class=\"m-b-0 m-r-8 text-label-small text-truncate flex-no-shrink\"\n title=\" {{ 'Auto refresh' | translate }}\"\n *ngIf=\"isIntervalRefresh && refreshInterval && config.isAutoRefreshEnabled\"\n >\n {{ 'Auto refresh' | translate }}\n </label>\n <div class=\"input-group\">\n <label\n class=\"toggle-countdown\"\n [class.toggle-countdown-disabled]=\"isRefreshDisabled\"\n [attr.aria-label]=\"toggleCountdownButtonTooltipText\"\n [tooltip]=\"toggleCountdownButtonTooltipText\"\n placement=\"bottom\"\n *ngIf=\"isIntervalRefresh && refreshInterval && config.isAutoRefreshEnabled\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarms-widget--interval-toggle-button\"\n (click)=\"onToggleCountdownButtonState($event)\"\n />\n <c8y-countdown-interval\n *ngIf=\"isIntervalRefreshToggleOn\"\n [countdownInterval]=\"refreshInterval\"\n (countdownEnded)=\"countdownEnded()\"\n ></c8y-countdown-interval>\n <i\n c8yIcon=\"pause\"\n *ngIf=\"!isIntervalRefreshToggleOn\"\n ></i>\n </label>\n\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n [attr.aria-label]=\"'Refresh' | translate\"\n [tooltip]=\"\n !isNewAlarmMessageCleared\n ? (REALTIME_UPDATE_ALARMS_MESSAGE | translate)\n : ('Refresh' | translate)\n \"\n placement=\"bottom\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n [disabled]=\"isRefreshDisabled || (isLoading | async)\"\n (click)=\"reload()\"\n data-cy=\"c8y-alarms-widget--reload-button\"\n >\n <span\n class=\"tag tag--info m-r-8\"\n *ngIf=\"!isNewAlarmMessageCleared\"\n >\n {{ 'New alarms' | translate }}\n </span>\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading | async }\"\n ></i>\n </button>\n </div>\n <div\n class=\"input-group-btn input-group-btn--last\"\n *ngIf=\"!isIntervalRefresh\"\n >\n <button\n class=\"c8y-realtime btn btn-default\"\n [attr.aria-label]=\"realtimeIconTitle\"\n [tooltip]=\"realtimeIconTitle\"\n placement=\"bottom\"\n type=\"button\"\n [container]=\"'body'\"\n (click)=\"toggleRealtimeState()\"\n [disabled]=\"isRefreshDisabled\"\n >\n <span\n class=\"c8y-pulse m-0\"\n [ngClass]=\"{\n active: isRealtimeToggleOn,\n inactive: !isRealtimeToggleOn\n }\"\n ></span>\n </button>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.CountdownIntervalComponent, selector: "c8y-countdown-interval", inputs: ["countdownInterval"], outputs: ["countdownEnded"] }, { kind: "directive", type: i5.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmWidgetAlarmsReloadComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarm-widget-alarms-reload', template: "<div\n class=\"m-l-auto d-flex p-t-4 p-b-4\"\n *ngIf=\"!globalAutoRefreshEnabled\"\n>\n <div class=\"m-l-auto d-flex a-i-center\">\n <label\n class=\"m-b-0 m-r-8 text-label-small text-truncate flex-no-shrink\"\n title=\" {{ 'Auto refresh' | translate }}\"\n *ngIf=\"isIntervalRefresh && refreshInterval && config.isAutoRefreshEnabled\"\n >\n {{ 'Auto refresh' | translate }}\n </label>\n <div class=\"input-group\">\n <label\n class=\"toggle-countdown\"\n [class.toggle-countdown-disabled]=\"isRefreshDisabled\"\n [attr.aria-label]=\"toggleCountdownButtonTooltipText\"\n [tooltip]=\"toggleCountdownButtonTooltipText\"\n placement=\"bottom\"\n *ngIf=\"isIntervalRefresh && refreshInterval && config.isAutoRefreshEnabled\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarms-widget--interval-toggle-button\"\n (click)=\"onToggleCountdownButtonState($event)\"\n />\n <c8y-countdown-interval\n *ngIf=\"isIntervalRefreshToggleOn\"\n [countdownInterval]=\"refreshInterval\"\n (countdownEnded)=\"countdownEnded()\"\n ></c8y-countdown-interval>\n <i\n c8yIcon=\"pause\"\n *ngIf=\"!isIntervalRefreshToggleOn\"\n ></i>\n </label>\n\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n [attr.aria-label]=\"'Refresh' | translate\"\n [tooltip]=\"\n !isNewAlarmMessageCleared\n ? (REALTIME_UPDATE_ALARMS_MESSAGE | translate)\n : ('Refresh' | translate)\n \"\n placement=\"bottom\"\n type=\"button\"\n [adaptivePosition]=\"false\"\n [container]=\"'body'\"\n [delay]=\"500\"\n [disabled]=\"isRefreshDisabled || (isLoading | async)\"\n (click)=\"reload()\"\n data-cy=\"c8y-alarms-widget--reload-button\"\n >\n <span\n class=\"tag tag--info m-r-8\"\n *ngIf=\"!isNewAlarmMessageCleared\"\n >\n {{ 'New alarms' | translate }}\n </span>\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': isLoading | async }\"\n ></i>\n </button>\n </div>\n <div\n class=\"input-group-btn input-group-btn--last\"\n *ngIf=\"!isIntervalRefresh\"\n >\n <button\n class=\"c8y-realtime btn btn-default\"\n [attr.aria-label]=\"realtimeIconTitle\"\n [tooltip]=\"realtimeIconTitle\"\n placement=\"bottom\"\n type=\"button\"\n [container]=\"'body'\"\n (click)=\"toggleRealtimeState()\"\n [disabled]=\"isRefreshDisabled\"\n >\n <span\n class=\"c8y-pulse m-0\"\n [ngClass]=\"{\n active: isRealtimeToggleOn,\n inactive: !isRealtimeToggleOn\n }\"\n ></span>\n </button>\n </div>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.AlarmsViewService }, { type: i0.ChangeDetectorRef }, { type: i2.TranslateService }, { type: i3.WidgetGlobalAutoRefreshService }], propDecorators: { isIntervalRefresh: [{
type: Input
}], refreshInterval: [{
type: Input
}], config: [{
type: Input
}], isLoading: [{
type: Input
}], isScrolling: [{
type: Input
}], isRefreshDisabled: [{
type: Input
}], onCountdownEnded: [{
type: Output
}], onRealTimeToggleChanged: [{
type: Output
}], countdownIntervalComponent: [{
type: ViewChild,
args: [CountdownIntervalComponent, { static: false }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxhcm0td2lkZ2V0LWFsYXJtcy1yZWxvYWQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vd2lkZ2V0cy9pbXBsZW1lbnRhdGlvbnMvYWxhcm1zL2FsYXJtLXdpZGdldC1hbGFybXMtcmVsb2FkLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3dpZGdldHMvaW1wbGVtZW50YXRpb25zL2FsYXJtcy9hbGFybS13aWRnZXQtYWxhcm1zLXJlbG9hZC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUwsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxZQUFZLEVBQ1osS0FBSyxFQUlMLE1BQU0sRUFFTixTQUFTLEVBQ1YsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUNMLDBCQUEwQixFQUMxQixPQUFPLEVBQ1AsbUJBQW1CLEVBQ25CLDhCQUE4QixFQUMvQixNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQy9ELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxlQUFlLEVBQWdCLE1BQU0sTUFBTSxDQUFDO0FBQ3JELE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNyQyxPQUFPLEVBQXlCLHNCQUFzQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7Ozs7Ozs7QUFNMUYsTUFBTSxPQUFPLGdDQUNYLFNBQVEsbUJBQW1CO0lBd0UzQixZQUNVLGlCQUFvQyxFQUNwQyxLQUF3QixFQUN4QixnQkFBa0MsRUFDbEMsOEJBQThEO1FBRXRFLEtBQUssRUFBRSxDQUFDO1FBTEEsc0JBQWlCLEdBQWpCLGlCQUFpQixDQUFtQjtRQUNwQyxVQUFLLEdBQUwsS0FBSyxDQUFtQjtRQUN4QixxQkFBZ0IsR0FBaEIsZ0JBQWdCLENBQWtCO1FBQ2xD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O09BRUc7SUFDTyxnQ0FBZ0MsQ0FBQyxVQUFtQjtRQUM1RCxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLGdDQUFnQyxHQUFHLFVBQVUsQ0FBQztZQUNuRCxPQUFPO1FBQ1QsQ0FBQztRQUNELElBQUksQ0FBQyxnQ0FBZ0MsR0FBRyxJQUFJLENBQUMseUJBQXlCO1lBQ3BFLENBQUMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1lBQ2hFLENBQUMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLENBQUM7SUFDcEUsQ0FBQzsrR0F2TlUsZ0NBQWdDO21HQUFoQyxnQ0FBZ0MsK2FBdUNoQywwQkFBMEIsNEZDcEV2QywrbEdBZ0dBOzs0RkRuRWEsZ0NBQWdDO2tCQUo1QyxTQUFTOytCQUNFLGdDQUFnQztrTUFZakMsaUJBQWlCO3NCQUF6QixLQUFLO2dCQUlHLGVBQWU7c0JBQXZCLEtBQUs7Z0JBSUcsTUFBTTtzQkFBZCxLQUFLO2dCQUlHLFNBQVM7c0JBQWpCLEtBQUs7Z0JBSUcsV0FBVztzQkFBbkIsS0FBSztnQkFJRyxpQkFBaUI7c0JBQXpCLEtBQUs7Z0JBSUksZ0JBQWdCO3NCQUF6QixNQUFNO2dCQUlHLHVCQUF1QjtzQkFBaEMsTUFBTTtnQkFHUCwwQkFBMEI7c0JBRHpCLFNBQVM7dUJBQUMsMEJBQTBCLEVBQUUsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gIENvbXBvbmVudCxcbiAgRXZlbnRFbWl0dGVyLFxuICBJbnB1dCxcbiAgT25DaGFuZ2VzLFxuICBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgT3V0cHV0LFxuICBTaW1wbGVDaGFuZ2VzLFxuICBWaWV3Q2hpbGRcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICBDb3VudGRvd25JbnRlcnZhbENvbXBvbmVudCxcbiAgZ2V0dGV4dCxcbiAgSW50ZXJ2YWxCYXNlZFJlbG9hZCxcbiAgV2lkZ2V0R2xvYmFsQXV0b1JlZnJlc2hTZXJ2aWNlXG59IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMnO1xuaW1wb3J0IHsgQWxhcm1zVmlld1NlcnZpY2UgfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzL2FsYXJtcyc7XG5pbXBvcnQgeyBUcmFuc2xhdGVTZXJ2aWNlIH0gZnJvbSAnQG5neC10cmFuc2xhdGUvY29yZSc7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIFN1YnNjcmlwdGlvbiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgdGFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgQWxhcm1MaXN0V2lkZ2V0Q29uZmlnLCBHTE9CQUxfSU5URVJWQUxfT1BUSU9OIH0gZnJvbSAnLi9hbGFybS1saXN0LXdpZGdldC5tb2RlbCc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2M4eS1hbGFybS13aWRnZXQtYWxhcm1zLXJlbG9hZCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9hbGFybS13aWRnZXQtYWxhcm1zLXJlbG9hZC5jb21wb25lbnQuaHRtbCdcbn0pXG5leHBvcnQgY2xhc3MgQWxhcm1XaWRnZXRBbGFybXNSZWxvYWRDb21wb25lbnRcbiAgZXh0ZW5kcyBJbnRlcnZhbEJhc2VkUmVsb2FkXG4gIGltcGxlbWVudHMgT25Jbml0LCBBZnRlclZpZXdJbml0LCBPbkNoYW5nZXMsIE9uRGVzdHJveVxue1xuICByZWFkb25seSBSRUFMVElNRV9VUERBVEVfQUxBUk1TX01FU1NBR0UgPSB0aGlzLmFsYXJtc1ZpZXdTZXJ2aWNlLlJFQUxUSU1FX1VQREFURV9BTEFSTVNfTUVTU0FHRTtcblxuICAvKipcbiAgICogU3RhdGUgb2YgdGhpcyBib29sZWFuIGRlcGVuZHMgb24gYW4gQXBwbGljYXRpb24gT3B0aW9ucyBcImFsYXJtc1JlZnJlc2hUeXBlXCIgc2V0dGluZy5cbiAgICovXG4gIEBJbnB1dCgpIGlzSW50ZXJ2YWxSZWZyZXNoOiBib29sZWFuO1xuICAvKipcbiAgICogQGluaGVyaXRkb2NcbiAgICovXG4gIEBJbnB1dCgpIHJlZnJlc2hJbnRlcnZhbDogbnVtYmVyO1xuICAvKipcbiAgICogQWxhcm0gd2lkZ2V0cyBjb25maWcuXG4gICAqL1xuICBASW5wdXQoKSBjb25maWc6IEFsYXJtTGlzdFdpZGdldENvbmZpZztcbiAgLyoqXG4gICAqIEBpbmhlcml0ZG9jXG4gICAqL1xuICBASW5wdXQoKSBpc0xvYWRpbmc6IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPjtcbiAgLyoqXG4gICAqIEluZGljYXRlcyB3aGV0aGVyIHRoZSBhbGFybSBsaXN0IGlzIGJlaW5nIHNjcm9sbGVkIG9yIG5vdC5cbiAgICovXG4gIEBJbnB1dCgpIGlzU2Nyb2xsaW5nOiBib29sZWFuO1xuICAvKipcbiAgICogQGluaGVyaXRkb2NcbiAgICovXG4gIEBJbnB1dCgpIGlzUmVmcmVzaERpc2FibGVkOiBib29sZWFuO1xuICAvKipcbiAgICogQGluaGVyaXRkb2NcbiAgICovXG4gIEBPdXRwdXQoKSBvbkNvdW50ZG93bkVuZGVkID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuICAvKipcbiAgICogRW1pdHRlZCB0byBpbmRpY2F0ZSB0aGF0IHRoZSB3aWRnZXRzIHJlYWx0aW1lIGJ1dHRvbiBzdGF0ZSBoYXMgY2hhbmdlZC5cbiAgICovXG4gIEBPdXRwdXQoKSBvblJlYWxUaW1lVG9nZ2xlQ2hhbmdlZDogRXZlbnRFbWl0dGVyPGJvb2xlYW4+ID0gbmV3IEV2ZW50RW1pdHRlcjxib29sZWFuPigpO1xuXG4gIEBWaWV3Q2hpbGQoQ291bnRkb3duSW50ZXJ2YWxDb21wb25lbnQsIHsgc3RhdGljOiBmYWxzZSB9KVxuICBjb3VudGRvd25JbnRlcnZhbENvbXBvbmVudDogQ291bnRkb3duSW50ZXJ2YWxDb21wb25lbnQ7XG4gIC8qKlxuICAgKiBAaW5oZXJpdGRvY1xuICAgKi9cbiAgcHJvdGVjdGVkIGhpZGVDb3VudGRvd246IGJvb2xlYW47XG4gIC8qKlxuICAgKiBUb29sdGlwIGZvciBhIG5ldyByZWFsdGltZSBhbGFybSBpbmRpY2F0b3IuXG4gICAqL1xuICByZWFsdGltZUljb25UaXRsZTogc3RyaW5nO1xuICAvKipcbiAgICogSW5kaWNhdGVzIHdoZXRoZXIgdGhlIGNvdW50ZG93biBoYXMgYmVlbiBjbGVhcmVkIGFscmVhZHkuXG4gICAqL1xuICBpc05ld0FsYXJtTWVzc2FnZUNsZWFyZWQgPSB0cnVlO1xuICAvKipcbiAgICogQGluaGVyaXRkb2NcbiAgICovXG4gIGlzSW50ZXJ2YWxSZWZyZXNoVG9nZ2xlT246IGJvb2xlYW47XG4gIC8qKlxuICAgKiBJbmRpY2F0ZXMgdGhlIGN1cnJlbnQgc3RhdGUgb2YgYW4gcmVhbHRpbWUgdG9nZ2xlIGJ1dHRvbi5cbiAgICovXG4gIGlzUmVhbHRpbWVUb2dnbGVPbjogYm9vbGVhbjtcbiAgLyoqXG4gICAqIEhvbGRzIHRoZSBzdWJzY3JpcHRpb24gdG8gYSBjb3VudGRvd24gb2JzZXJ2YWJsZS5cbiAgICovXG4gIGNvdW50ZG93blN1YnNjcmlwdGlvbjogU3Vic2NyaXB0aW9uO1xuICAvKipcbiAgICogQGluaGVyaXRkb2NcbiAgICovXG4gIHByb3RlY3RlZCBtYW51YWxseURpc2FibGVkQ291bnRkb3duID0gZmFsc2U7XG5cbiAgdG9nZ2xlQ291bnRkb3duQnV0dG9uVG9vbHRpcFRleHQ6IHN0cmluZztcbiAgZ2xvYmFsQXV0b1JlZnJlc2hFbmFibGVkOiBib29sZWFuO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgYWxhcm1zVmlld1NlcnZpY2U6IEFsYXJtc1ZpZXdTZXJ2aWNlLFxuICAgIHByaXZhdGUgY2RSZWY6IENoYW5nZURldGVjdG9yUmVmLFxuICAgIHByaXZhdGUgdHJhbnNsYXRlU2VydmljZTogVHJhbnNsYXRlU2VydmljZSxcbiAgICBwcml2YXRlIHdpZGdldEdsb2JhbEF1dG9SZWZyZXNoU2VydmljZTogV2lkZ2V0R2xvYmFsQXV0b1JlZnJlc2hTZXJ2aWNlXG4gICkge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBuZ09uSW5pdCgpIHtcbiAgICBpZiAoIXRoaXMuaXNJbnRlcnZhbFJlZnJlc2gpIHtcbiAgICAgIHRoaXMuaXNSZWFsdGltZVRvZ2dsZU9uID0gdGhpcy5jb25maWcuaXNBdXRvUmVmcmVzaEVuYWJsZWQ7XG4gICAgICB0aGlzLnJlYWx0aW1lSWNvblRpdGxlID0gdGhpcy5nZXRSZWFsdGltZUljb25UaXRsZSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmlzSW50ZXJ2YWxSZWZyZXNoVG9nZ2xlT24gPVxuICAgICAgICAhdGhpcy5pc1JlZnJlc2hEaXNhYmxlZCAmJiAodGhpcy5jb25maWcuaXNBdXRvUmVmcmVzaEVuYWJsZWQgfHwgISF0aGlzLnJlZnJlc2hJbnRlcnZhbCk7XG4gICAgICB0aGlzLnVwZGF0ZUNvdW50ZG93bkJ1dHRvblRvb2x0aXBUZXh0KCk7XG4gICAgfVxuICB9XG5cbiAgbmdBZnRlclZpZXdJbml0KCkge1xuICAgIGlmICh0aGlzLmlzSW50ZXJ2YWxSZWZyZXNoICYmIHRoaXMucmVmcmVzaEludGVydmFsKSB7XG4gICAgICB0aGlzLnN0YXJ0Q291bnRkb3duKCk7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuZ2xvYmFsQXV0b1JlZnJlc2hFbmFibGVkKSB7XG4gICAgICB0aGlzLndpZGdldEdsb2JhbEF1dG9SZWZyZXNoU2VydmljZS5jb3VudGRvd25BY3Rpb25zLmNvdW50ZG93bkVuZGVkJFxuICAgICAgICAucGlwZSh0YXAoKCkgPT4gdGhpcy5vbkNvdW50ZG93bkVuZGVkLmVtaXQoKSkpXG4gICAgICAgIC5zdWJzY3JpYmUoKTtcbiAgICB9XG4gIH1cblxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKTogdm9pZCB7XG4gICAgY29uc3QgeyBpc0Rpc2FibGVkLCBjb25maWcgfSA9IGNoYW5nZXM7XG4gICAgaWYgKGNvbmZpZykge1xuICAgICAgdGhpcy5nbG9iYWxBdXRvUmVmcmVzaEVuYWJsZWQgPSB0aGlzLmNvbmZpZy5yZWZyZXNoT3B0aW9uID09PSBHTE9CQUxfSU5URVJWQUxfT1BUSU9OO1xuICAgIH1cbiAgICBpZiAoaXNEaXNhYmxlZCkge1xuICAgICAgdGhpcy5pc0ludGVydmFsUmVmcmVzaFRvZ2dsZU9uID1cbiAgICAgICAgIXRoaXMuaXNSZWZyZXNoRGlzYWJsZWQgJiYgKHRoaXMuY29uZmlnLmlzQXV0b1JlZnJlc2hFbmFibGVkIHx8ICEhdGhpcy5yZWZyZXNoSW50ZXJ2YWwpO1xuICAgICAgdGhpcy51cGRhdGVDb3VudGRvd25CdXR0b25Ub29sdGlwVGV4dChnZXR0ZXh0KCdEaXNhYmxlZCcpKTtcbiAgICB9XG5cbiAgICBpZiAoXG4gICAgICAhdGhpcy5pc0ludGVydmFsUmVmcmVzaCB8fFxuICAgICAgdGhpcy5tYW51YWxseURpc2FibGVkQ291bnRkb3duIHx8XG4gICAgICAhdGhpcy5jb25maWcuaXNBdXRvUmVmcmVzaEVuYWJsZWRcbiAgICApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLmhhbmRsZVNjcm9sbGluZygpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgaWYgKHRoaXMuY291bnRkb3duU3Vic2NyaXB0aW9uKSB7XG4gICAgICB0aGlzLmNvdW50ZG93blN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAaW5oZXJpdGRvY1xuICAgKi9cbiAgY291bnRkb3duRW5kZWQoKTogdm9pZCB7XG4gICAgLyoqXG4gICAgICogQGluaGVyaXRkb2NcbiAgICAgKi9cbiAgICB0aGlzLmF1dG9SZWZyZXNoTGlzdCgpO1xuICB9XG5cbiAgcmVsb2FkKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmlzSW50ZXJ2YWxSZWZyZXNoKSB7XG4gICAgICB0aGlzLmF1dG9SZWZyZXNoTGlzdCgpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLm9uQ291bnRkb3duRW5kZWQuZW1pdCgpO1xuICAgICAgdGhpcy5pc05ld0FsYXJtTWVzc2FnZUNsZWFyZWQgPSB0cnVlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUb2dnbGVzIHRoZSByZWFsLXRpbWUgc3RhdGUgb2YgdGhlIGNvbXBvbmVudC5cbiAgICpcbiAgICogVGhpcyBtZXRob2Qgc3dpdGNoZXMgdGhlIGBpc1JlYWx0aW1lVG9nZ2xlT25gIHN0YXRlIGJldHdlZW4gdHJ1ZSBhbmQgZmFsc2UuXG4gICAqIFdoZW4gdGhlIHN0YXRlIGlzIHRvZ2dsZWQsIGl0IGVtaXRzIHRoZSBjdXJyZW50IHN0YXRlIHRocm91Z2ggYG9uUmVhbFRpbWVUb2dnbGVDaGFuZ2VkYCBldmVudC5cbiAgICogSXQgYWxzbyB1cGRhdGVzIHRoZSBgcmVhbHRpbWVJY29uVGl0bGVgIGJhc2VkIG9uIHRoZSBuZXcgc3RhdGUuXG4gICAqL1xuICB0b2dnbGVSZWFsdGltZVN0YXRlKCk6IHZvaWQge1xuICAgIHRoaXMuaXNSZWFsdGltZVRvZ2dsZU9uID0gIXRoaXMuaXNSZWFsdGltZVRvZ2dsZU9uO1xuICAgIHRoaXMub25SZWFsVGltZVRvZ2dsZUNoYW5nZWQuZW1pdCh0aGlzLmlzUmVhbHRpbWVUb2dnbGVPbik7XG4gICAgdGhpcy5yZWFsdGltZUljb25UaXRsZSA9IHRoaXMuZ2V0UmVhbHRpbWVJY29uVGl0bGUoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wcyB0aGUgY291bnRkb3duIGFuZCB0cmlnZ2VycyBhIHJlZnJlc2ggYWN0aW9uLlxuICAgKiBUaGlzIGZ1bmN0aW9uIGlzIHJlc3BvbnNpYmxlIGZvciBoYWx0aW5nIHRoZSBjb3VudGRvd24gaW50ZXJ2YWwgY29tcG9uZW50J3Mgb3BlcmF0aW9uLlxuICAgKiBBZnRlciBzdG9wcGluZyB0aGUgY291bnRkb3duLCBpdCBlbWl0cyBhbiBgb25Db3VudGRvd25FbmRlZGAgZXZlbnQuXG4gICAqIFRoaXMgZXZlbnQgaXMgdXNlZCB0byBpbmZvcm0gZXh0ZXJuYWwgY29tcG9uZW50cyB0aGF0IHRoZSBjb3VudGRvd24gaGFzIGVuZGVkLFxuICAgKiB0eXBpY2FsbHkgcHJvbXB0aW5nIHRoZW0gdG8gcmVsb2FkIG9yIHJlZnJlc2ggdGhlaXIgZGF0YS5cbiAgICovXG4gIGF1dG9SZWZyZXNoTGlzdCgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5pc0ludGVydmFsUmVmcmVzaFRvZ2dsZU9uICYmIHRoaXMuY29uZmlnLmlzQXV0b1JlZnJlc2hFbmFibGVkKSB7XG4gICAgICB0aGlzLmNvdW50ZG93bkludGVydmFsQ29tcG9uZW50LnN0b3AoKTtcbiAgICB9XG4gICAgdGhpcy5vbkNvdW50ZG93bkVuZGVkLmVtaXQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXRzIHRoZSB0b29sdGlwIG1lc3NhZ2UgZm9yIGEgbmV3IHJlYWx0aW1lIGFsYXJtIGluZGljYXRvci5cbiAgICogQHJldHVybnMgVG9vbHRpcCBtZXNzYWdlLlxuICAgKi9cbiAgZ2V0UmVhbHRpbWVJY29uVGl0bGUoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5pc1JlYWx0aW1lVG9nZ2xlT25cbiAgICAgID8gdGhpcy50cmFuc2xhdGVTZXJ2aWNlLmluc3RhbnQoZ2V0dGV4dCgnUmVhbHRpbWUgYWN0aXZlJykpXG4gICAgICA6IHRoaXMudHJhbnNsYXRlU2VydmljZS5pbnN0YW50KGdldHRleHQoJ1JlYWx0aW1lIGluYWN0aXZlJykpO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuYWJsZXMgYW5kIHN0YXJ0cyB0aGUgY291bnRkb3duIHRpbWVyLlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBtYWtlcyB0aGUgY291bnRkb3duIHZpc2libGUgKGBoaWRlQ291bnRkb3duYCBpcyBzZXQgdG8gZmFsc2UpIGFuZCB0aGVuXG4gICAqIHN0YXJ0cyB0aGUgY291bnRkb3duIHByb2Nlc3MuIEl0IGVuc3VyZXMgdGhlIGNvdW50ZG93biB0aW1lciBpcyB1cGRhdGVkIGltbWVkaWF0ZWx5XG4gICAqIGJ5IHRyaWdnZXJpbmcgY2hhbmdlIGRldGVjdGlvbiB3aXRoIGBjZFJlZi5kZXRlY3RDaGFuZ2VzKClgIGJlZm9yZSBzdGFydGluZyB0aGUgY291bnRkb3duLlxuICAgKiBUaGlzIG1ldGhvZCBlbmNhcHN1bGF0ZXMgdGhlIGxvZ2ljIHJlcXVpcmVkIHRvIGluaXRpYXRlIHRoZSBjb3VudGRvd24gdGltZXIuXG4gICAqL1xuICBlbmFibGVDb3VudGRvd24oKTogdm9pZCB7XG4gICAgdGhpcy5oaWRlQ291bnRkb3duID0gZmFsc2U7XG4gICAgLy8gUHJldmVudHMgdGhlIGNvdW50ZG93biBmcm9tIGdldHRpbmcgc3R1Y2sgb24gYW4gaW5pdGlhbCB2YWx1ZS5cbiAgICB0aGlzLmNkUmVmLmRldGVjdENoYW5nZXMoKTtcbiAgICB0aGlzLnN0YXJ0Q291bnRkb3duKCk7XG4gIH1cblxuICAvKipcbiAgICogQGluaGVyaXRkb2NcbiAgICovXG4gIHByb3RlY3RlZCB1cGRhdGVDb3VudGRvd25CdXR0b25Ub29sdGlwVGV4dChjdXN0b21UZXh0Pzogc3RyaW5nKTogdm9pZCB7XG4gICAgaWYgKGN1c3RvbVRleHQpIHtcbiAgICAgIHRoaXMudG9nZ2xlQ291bnRkb3duQnV0dG9uVG9vbHRpcFRleHQgPSBjdXN0b21UZXh0O1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLnRvZ2dsZUNvdW50ZG93bkJ1dHRvblRvb2x0aXBUZXh0ID0gdGhpcy5pc0ludGVydmFsUmVmcmVzaFRvZ2dsZU9uXG4gICAgICA/IHRoaXMudHJhbnNsYXRlU2VydmljZS5pbnN0YW50KGdldHRleHQoJ0Rpc2FibGUgYXV0byByZWZyZXNoJykpXG4gICAgICA6IHRoaXMudHJhbnNsYXRlU2VydmljZS5pbnN0YW50KGdldHRleHQoJ0VuYWJsZSBhdXRvIHJlZnJlc2gnKSk7XG4gIH1cbn1cbiIsIjxkaXZcbiAgY2xhc3M9XCJtLWwtYXV0byBkLWZsZXggcC10LTQgcC1iLTRcIlxuICAqbmdJZj1cIiFnbG9iYWxBdXRvUmVmcmVzaEVuYWJsZWRcIlxuPlxuICA8ZGl2IGNsYXNzPVwibS1sLWF1dG8gZC1mbGV4IGEtaS1jZW50ZXJcIj5cbiAgICA8bGFiZWxcbiAgICAgIGNsYXNzPVwibS1iLTAgbS1yLTggdGV4dC1sYWJlbC1zbWFsbCB0ZXh0LXRydW5jYXRlIGZsZXgtbm8tc2hyaW5rXCJcbiAgICAgIHRpdGxlPVwiIHt7ICdBdXRvIHJlZnJlc2gnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICpuZ0lmPVwiaXNJbnRlcnZhbFJlZnJlc2ggJiYgcmVmcmVzaEludGVydmFsICYmIGNvbmZpZy5pc0F1dG9SZWZyZXNoRW5hYmxlZFwiXG4gICAgPlxuICAgICAge3sgJ0F1dG8gcmVmcmVzaCcgfCB0cmFuc2xhdGUgfX1cbiAgICA8L2xhYmVsPlxuICAgIDxkaXYgY2xhc3M9XCJpbnB1dC1ncm91cFwiPlxuICAgICAgPGxhYmVsXG4gICAgICAgIGNsYXNzPVwidG9nZ2xlLWNvdW50ZG93blwiXG4gICAgICAgIFtjbGFzcy50b2dnbGUtY291bnRkb3duLWRpc2FibGVkXT1cImlzUmVmcmVzaERpc2FibGVkXCJcbiAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCJ0b2dnbGVDb3VudGRvd25CdXR0b25Ub29sdGlwVGV4dFwiXG4gICAgICAgIFt0b29sdGlwXT1cInRvZ2dsZUNvdW50ZG93bkJ1dHRvblRvb2x0aXBUZXh0XCJcbiAgICAgICAgcGxhY2VtZW50PVwiYm90dG9tXCJcbiAgICAgICAgKm5nSWY9XCJpc0ludGVydmFsUmVmcmVzaCAmJiByZWZyZXNoSW50ZXJ2YWwgJiYgY29uZmlnLmlzQXV0b1JlZnJlc2hFbmFibGVkXCJcbiAgICAgICAgW2FkYXB0aXZlUG9zaXRpb25dPVwiZmFsc2VcIlxuICAgICAgICBbY29udGFpbmVyXT1cIidib2R5J1wiXG4gICAgICAgIFtkZWxheV09XCI1MDBcIlxuICAgICAgPlxuICAgICAgICA8aW5wdXRcbiAgICAgICAgICB0eXBlPVwiY2hlY2tib3hcIlxuICAgICAgICAgIGRhdGEtY3k9XCJjOHktYWxhcm1zLXdpZGdldC0taW50ZXJ2YWwtdG9nZ2xlLWJ1dHRvblwiXG4gICAgICAgICAgKGNsaWNrKT1cIm9uVG9nZ2xlQ291bnRkb3duQnV0dG9uU3RhdGUoJGV2ZW50KVwiXG4gICAgICAgIC8+XG4gICAgICAgIDxjOHktY291bnRkb3duLWludGVydmFsXG4gICAgICAgICAgKm5nSWY9XCJpc0ludGVydmFsUmVmcmVzaFRvZ2dsZU9uXCJcbiAgICAgICAgICBbY291bnRkb3duSW50ZXJ2YWxdPVwicmVmcmVzaEludGVydmFsXCJcbiAgICAgICAgICAoY291bnRkb3duRW5kZWQpPVwiY291bnRkb3duRW5kZWQoKVwiXG4gICAgICAgID48L2M4eS1jb3VudGRvd24taW50ZXJ2YWw+XG4gICAgICAgIDxpXG4gICAgICAgICAgYzh5SWNvbj1cInBhdXNlXCJcbiAgICAgICAgICAqbmdJZj1cIiFpc0ludGVydmFsUmVmcmVzaFRvZ2dsZU9uXCJcbiAgICAgICAgPjwvaT5cbiAgICAgIDwvbGFiZWw+XG5cbiAgICAgIDxkaXYgY2xhc3M9XCJpbnB1dC1ncm91cC1idG5cIj5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgIGNsYXNzPVwiYnRuIGJ0bi1kZWZhdWx0XCJcbiAgICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidSZWZyZXNoJyB8IHRyYW5zbGF0ZVwiXG4gICAgICAgICAgW3Rvb2x0aXBdPVwiXG4gICAgICAgICAgICAhaXNOZXdBbGFybU1lc3NhZ2VDbGVhcmVkXG4gICAgICAgICAgICAgID8gKFJFQUxUSU1FX1VQREFURV9BTEFSTVNfTUVTU0FHRSB8IHRyYW5zbGF0ZSlcbiAgICAgICAgICAgICAgOiAoJ1JlZnJlc2gnIHwgdHJhbnNsYXRlKVxuICAgICAgICAgIFwiXG4gICAgICAgICAgcGxhY2VtZW50PVwiYm90dG9tXCJcbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICBbYWRhcHRpdmVQb3NpdGlvbl09XCJmYWxzZVwiXG4gICAgICAgICAgW2NvbnRhaW5lcl09XCInYm9keSdcIlxuICAgICAgICAgIFtkZWxheV09XCI1MDBcIlxuICAgICAgICAgIFtkaXNhYmxlZF09XCJpc1JlZnJlc2hEaXNhYmxlZCB8fCAoaXNMb2FkaW5nIHwgYXN5bmMpXCJcbiAgICAgICAgICAoY2xpY2spPVwicmVsb2FkKClcIlxuICAgICAgICAgIGRhdGEtY3k9XCJjOHktYWxhcm1zLXdpZGdldC0tcmVsb2FkLWJ1dHRvblwiXG4gICAgICAgID5cbiAgICAgICAgICA8c3BhblxuICAgICAgICAgICAgY2xhc3M9XCJ0YWcgdGFnLS1pbmZvIG0tci04XCJcbiAgICAgICAgICAgICpuZ0lmPVwiIWlzTmV3QWxhcm1NZXNzYWdlQ2xlYXJlZFwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAge3sgJ05ldyBhbGFybXMnIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgIDxpXG4gICAgICAgICAgICBjOHlJY29uPVwicmVmcmVzaFwiXG4gICAgICAgICAgICBbbmdDbGFzc109XCJ7ICdpY29uLXNwaW4nOiBpc0xvYWRpbmcgfCBhc3luYyB9XCJcbiAgICAgICAgICA+PC9pPlxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIDwvZGl2PlxuICAgICAgPGRpdlxuICAgICAgICBjbGFzcz1cImlucHV0LWdyb3VwLWJ0biBpbnB1dC1ncm91cC1idG4tLWxhc3RcIlxuICAgICAgICAqbmdJZj1cIiFpc0ludGVydmFsUmVmcmVzaFwiXG4gICAgICA+XG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICBjbGFzcz1cImM4eS1yZWFsdGltZSBidG4gYnRuLWRlZmF1bHRcIlxuICAgICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwicmVhbHRpbWVJY29uVGl0bGVcIlxuICAgICAgICAgIFt0b29sdGlwXT1cInJlYWx0aW1lSWNvblRpdGxlXCJcbiAgICAgICAgICBwbGFjZW1lbnQ9XCJib3R0b21cIlxuICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgIFtjb250YWluZXJdPVwiJ2JvZHknXCJcbiAgICAgICAgICAoY2xpY2spPVwidG9nZ2xlUmVhbHRpbWVTdGF0ZSgpXCJcbiAgICAgICAgICBbZGlzYWJsZWRdPVwiaXNSZWZyZXNoRGlzYWJsZWRcIlxuICAgICAgICA+XG4gICAgICAgICAgPHNwYW5cbiAgICAgICAgICAgIGNsYXNzPVwiYzh5LXB1bHNlIG0tMFwiXG4gICAgICAgICAgICBbbmdDbGFzc109XCJ7XG4gICAgICAgICAgICAgIGFjdGl2ZTogaXNSZWFsdGltZVRvZ2dsZU9uLFxuICAgICAgICAgICAgICBpbmFjdGl2ZTogIWlzUmVhbHRpbWVUb2dnbGVPblxuICAgICAgICAgICAgfVwiXG4gICAgICAgICAgPjwvc3Bhbj5cbiAgICAgICAgPC9idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG48L2Rpdj5cbiJdfQ==