@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
210 lines • 52.9 kB
JavaScript
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Severity, SEVERITY_LABELS } from '@c8y/client';
import { AlertService } from '@c8y/ngx-components';
import { BsDropdownDirective } from 'ngx-bootstrap/dropdown';
import { BehaviorSubject, Subject, combineLatest, from, forkJoin, EMPTY } from 'rxjs';
import { catchError, filter, finalize, map, startWith, takeUntil } from 'rxjs/operators';
import { AlarmsViewService } from './alarms-view.service';
import { DEFAULT_ALARM_COUNTS, DEFAULT_SEVERITY_VALUES } from './alarms.model';
import { ActivatedRoute, Router } from '@angular/router';
import * as i0 from "@angular/core";
import * as i1 from "@angular/forms";
import * as i2 from "./alarms-view.service";
import * as i3 from "@c8y/ngx-components";
import * as i4 from "@angular/router";
import * as i5 from "@angular/cdk/a11y";
import * as i6 from "ngx-bootstrap/dropdown";
import * as i7 from "@angular/common";
import * as i8 from "@ngx-translate/core";
import * as i9 from "./alarm-severity-to-icon.pipe";
import * as i10 from "./alarm-severities-to-title.pipe";
export class AlarmsFilterComponent {
constructor(formBuilder, alarmsViewService, alertService, router, activatedRoute) {
this.formBuilder = formBuilder;
this.alarmsViewService = alarmsViewService;
this.alertService = alertService;
this.router = router;
this.activatedRoute = activatedRoute;
this.severitiesList = Object.keys(SEVERITY_LABELS);
/**
* EventEmitter to notify when filters have been applied.
* Emits a `AlarmListFormFilters` object representing the filter criteria applied by the user.
*/
this.onFilterApplied = new EventEmitter();
this.formGroup = this.formBuilder.group(DEFAULT_SEVERITY_VALUES);
this.chips = [];
this.showCleared = false;
this.alarmCounts = DEFAULT_ALARM_COUNTS;
this.SEVERITY_LABELS = SEVERITY_LABELS;
this.isNoneCheckboxSelected$ = new BehaviorSubject(false);
this.severitiesTouched$ = new BehaviorSubject(false);
this.currentFormGroupValues = this.formGroup.value;
this.destroy$ = new Subject();
}
ngOnInit() {
this.activatedRoute.queryParams.pipe(takeUntil(this.destroy$)).subscribe(params => {
this.showCleared = params.showCleared === 'true';
this.formGroup.setValue({
[Severity.CRITICAL]: params[Severity.CRITICAL] === 'true',
[Severity.MAJOR]: params[Severity.MAJOR] === 'true',
[Severity.MINOR]: params[Severity.MINOR] === 'true',
[Severity.WARNING]: params[Severity.WARNING] === 'true'
});
if (params.lastUpdatedFrom) {
this.selectedDates = [new Date(params.lastUpdatedFrom), new Date(params.createdTo)];
}
this.applyFilters(true, false);
});
this.trackCheckboxStateWithFormChanges();
this.currentShowClearedValue = this.showCleared;
this.updateChipsAndDefaultValues();
}
ngAfterViewInit() {
this.filtersDropdown.isOpenChange
.pipe(takeUntil(this.destroy$), filter(Boolean))
.subscribe(() => this.updateAlarmsCount());
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
allChanged(selected) {
this.formGroup.patchValue({
[Severity.CRITICAL]: selected,
[Severity.MAJOR]: selected,
[Severity.MINOR]: selected,
[Severity.WARNING]: selected
});
}
applyFilters(emit = true, navigate = true) {
this.updateChipsAndDefaultValues();
const combinedFormEvent = {
showCleared: this.showCleared,
severityOptions: this.formGroup.value,
selectedDates: this.selectedDates
};
if (emit) {
this.onFilterApplied.emit(combinedFormEvent);
}
this.currentFormGroupValues = this.formGroup.value;
this.currentShowClearedValue = this.showCleared;
if (navigate) {
this.router.navigate([], {
queryParams: {
showCleared: combinedFormEvent.showCleared,
...combinedFormEvent.severityOptions,
lastUpdatedFrom: combinedFormEvent.selectedDates?.[0]?.toISOString(),
createdTo: combinedFormEvent.selectedDates?.[1]?.toISOString()
},
queryParamsHandling: 'merge'
});
}
}
deselectChip(chip) {
this.formGroup.patchValue({
...this.formGroup.value,
[chip]: false
});
this.applyFilters(false);
this.closeDropdown();
}
closeDropdown() {
if (this.filtersDropdown.isOpen) {
this.filtersDropdown.isOpen = false;
}
}
resetForm() {
this.formGroup.reset(this.currentFormGroupValues);
this.severitiesTouched$.next(false);
this.showCleared = this.currentShowClearedValue;
}
markSeveritiesAsTouched() {
this.severitiesTouched$.next(true);
}
/**
* Asynchronously fetches and updates the count of alarms for each severity level.
*
* @param showCleared - Whether to include cleared alarms in the count.
* Defaults to the current value of `this.currentShowClearedValue`.
* @returns A Promise that resolves when all alarm counts have been fetched,
* or rejects with an error if the operation fails.
* @throws Will throw an error if any of the alarm count fetching promises reject.
*/
async updateAlarmsCount(showCleared = this.currentShowClearedValue) {
this.countLoading = true;
const alarmFilter = this.contextSourceId
? {
source: this.contextSourceId,
withSourceAssets: true,
withSourceDevices: true
}
: null;
const observables = this.severitiesList.reduce((acc, severity) => {
acc[severity] = from(this.alarmsViewService.getAlarmsCountBySeverity(severity, showCleared, alarmFilter));
return acc;
}, {
CRITICAL: null,
MAJOR: null,
MINOR: null,
WARNING: null
});
forkJoin(observables)
.pipe(catchError(err => {
this.alarmCounts = {};
this.alertService.addServerFailure(err);
return EMPTY;
}), finalize(() => (this.countLoading = false)))
.subscribe((alarmCounts) => (this.alarmCounts = alarmCounts));
}
createFormValueWithChangesStream() {
return this.formGroup.valueChanges.pipe(startWith(this.formGroup.value));
}
trackCheckboxStateWithFormChanges() {
const formValue$ = this.createFormValueWithChangesStream();
this.isEachCheckboxSelected$ = this.createAllSelectedStream(formValue$);
this.isIndeterminate$ = this.createIndeterminateStream(formValue$);
this.trackAllCheckboxesDisabled(formValue$);
this.shouldDisableApplyButton$ = this.createDisableApplyButtonStream();
}
createAllSelectedStream(formValue$) {
return formValue$.pipe(map(severities => Object.values(severities).every(Boolean)));
}
createIndeterminateStream(formValue$) {
return formValue$.pipe(map(severities => Object.values(severities).some(Boolean) && !Object.values(severities).every(Boolean)));
}
trackAllCheckboxesDisabled(formValue$) {
formValue$.pipe(takeUntil(this.destroy$)).subscribe(severities => {
const areAllDisabled = Object.values(severities).every(value => !value);
this.isNoneCheckboxSelected$.next(areAllDisabled);
});
}
createDisableApplyButtonStream() {
return combineLatest([this.isNoneCheckboxSelected$, this.severitiesTouched$]).pipe(map(([allCheckboxesAreDisabled, severitiesTouched]) => allCheckboxesAreDisabled || !severitiesTouched));
}
updateChipsAndDefaultValues() {
const severityFilter = this.formGroup;
const seveerityValues = severityFilter.value;
this.chips = Object.keys(seveerityValues).filter(key => seveerityValues[key]);
const allChipsRemoved = this.chips.length === 0;
if (allChipsRemoved) {
const defaultValues = DEFAULT_SEVERITY_VALUES;
severityFilter.setValue(defaultValues);
this.chips = Object.keys(defaultValues);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmsFilterComponent, deps: [{ token: i1.FormBuilder }, { token: i2.AlarmsViewService }, { token: i3.AlertService }, { token: i4.Router }, { token: i4.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AlarmsFilterComponent, selector: "c8y-alarms-filter", inputs: { contextSourceId: "contextSourceId" }, outputs: { onFilterApplied: "onFilterApplied" }, viewQueries: [{ propertyName: "filtersDropdown", first: true, predicate: ["filtersDropdown"], descendants: true }], ngImport: i0, template: "<form\n class=\"d-flex a-i-center\"\n [formGroup]=\"formGroup\"\n>\n <div\n class=\"dropdown\"\n title=\"{{ 'Filter by severity' | translate }}\"\n dropdown\n #filtersDropdown=\"bs-dropdown\"\n [cdkTrapFocus]=\"filtersDropdown.isOpen\"\n [insideClick]=\"true\"\n >\n <div class=\"input-group fit-w\">\n <div class=\"form-control d-flex a-i-center inner-scroll\" style=\"min-width: 104px; padding-top: 0; padding-bottom: 0;\">\n <ng-container *ngIf=\"chips.length !== severitiesList.length; else allAlarms\">\n <span\n class=\"tag tag--info chip\"\n *ngFor=\"let chip of chips\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n (click)=\"deselectChip(chip); $event.stopPropagation()\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n <i\n class=\"status stroked-icon icon-12\"\n [c8yIcon]=\"chip | AlarmSeverityToIcon\"\n [ngClass]=\"chip | lowercase\"\n ></i>\n {{ SEVERITY_LABELS[chip] | translate }}\n </span>\n </ng-container>\n <ng-template #allAlarms>\n <span class=\"text-truncate\" title=\"{{ 'All severities' | translate }}\">\n {{ 'All severities' | translate }}\n </span>\n </ng-template>\n </div>\n <div class=\"input-group-btn input-group-btn--last\">\n <button\n class=\"btn-default btn btn--caret\"\n title=\"{{ chips | AlarmSeveritiesToTitle }}\"\n data-cy=\"c8y-alarm-filter\"\n dropdownToggle\n (click)=\"resetForm()\"\n >\n <i class=\"caret\"></i>\n </button>\n </div>\n </div>\n <ul\n class=\"dropdown-menu dropdown-menu-action-bar\"\n *dropdownMenu\n >\n <li class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center sticky-top separator-bottom\">\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n [title]=\"'All' | translate\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarm-filter--all\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"allChanged($event)\"\n [ngModel]=\"isEachCheckboxSelected$ | async\"\n [indeterminate]=\"isIndeterminate$ | async\"\n (click)=\"markSeveritiesAsTouched()\"\n />\n <span></span>\n <i\n class=\"status stroked-icon m-l-8 icon-20\"\n [c8yIcon]=\"'bell'\"\n ></i>\n <span class=\"m-l-8\">{{ 'All' | translate }}</span>\n </label>\n </li>\n <li\n class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center\"\n *ngFor=\"let severity of severitiesList\"\n >\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n [title]=\"SEVERITY_LABELS[severity] | translate\"\n >\n <input\n type=\"checkbox\"\n [attr.data-cy]=\"'c8y-alarm-filter--' + severity\"\n [formControlName]=\"severity\"\n [value]=\"severity\"\n (click)=\"markSeveritiesAsTouched()\"\n />\n <span></span>\n <i\n class=\"status stroked-icon m-l-8 icon-20\"\n [c8yIcon]=\"severity | AlarmSeverityToIcon\"\n [ngClass]=\"severity | lowercase\"\n ></i>\n <span class=\"m-l-8\">{{ SEVERITY_LABELS[severity] | translate }}</span>\n </label>\n <!-- badge -->\n <div\n class=\"badge badge-info m-l-auto\"\n *ngIf=\"alarmCounts[severity] || alarmCounts[severity] === 0\"\n >\n <i\n class=\"icon-spin\"\n [c8yIcon]=\"'circle-o-notch'\"\n *ngIf=\"countLoading\"\n ></i>\n <span\n *ngIf=\"!countLoading\"\n [attr.data-cy]=\"severity + '-badge'\"\n >\n {{ alarmCounts[severity] < 99 ? alarmCounts[severity] : '99+' }}\n </span>\n </div>\n </li>\n <li class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center separator-top\">\n <label\n class=\"c8y-switch\"\n [attr.aria-label]=\"'Show cleared alarms' | translate\"\n [attr.data-cy]=\"'c8y-alarms-filter--cleared'\"\n >\n <input\n type=\"checkbox\"\n #showClearedCheckbox\n [ngModelOptions]=\"{ standalone: true }\"\n [(ngModel)]=\"showCleared\"\n (click)=\"markSeveritiesAsTouched(); updateAlarmsCount(showClearedCheckbox.checked)\"\n />\n <span></span>\n <span\n class=\"text-truncate\"\n title=\"{{ 'Show cleared alarms' | translate }}\"\n >\n {{ 'Show cleared alarms' | translate }}\n </span>\n </label>\n </li>\n\n <div class=\"p-16 d-flex sticky-bottom separator-top\">\n <button\n [attr.data-cy]=\"'c8y-alarms-filter--apply'\"\n class=\"btn btn-primary btn-sm flex-grow\"\n title=\"{{ 'Apply' | translate }}\"\n type=\"button\"\n (click)=\"applyFilters(false); closeDropdown()\"\n [disabled]=\"shouldDisableApplyButton$ | async\"\n >\n {{ 'Apply' | translate }}\n </button>\n </div>\n </ul>\n </div>\n</form>\n", dependencies: [{ kind: "directive", type: i5.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i6.BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: i6.BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: i6.BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i7.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i7.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: i7.AsyncPipe, name: "async" }, { kind: "pipe", type: i7.LowerCasePipe, name: "lowercase" }, { kind: "pipe", type: i8.TranslatePipe, name: "translate" }, { kind: "pipe", type: i9.AlarmSeverityToIconPipe, name: "AlarmSeverityToIcon" }, { kind: "pipe", type: i10.AlarmSeveritiesToTitlePipe, name: "AlarmSeveritiesToTitle" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmsFilterComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-alarms-filter', template: "<form\n class=\"d-flex a-i-center\"\n [formGroup]=\"formGroup\"\n>\n <div\n class=\"dropdown\"\n title=\"{{ 'Filter by severity' | translate }}\"\n dropdown\n #filtersDropdown=\"bs-dropdown\"\n [cdkTrapFocus]=\"filtersDropdown.isOpen\"\n [insideClick]=\"true\"\n >\n <div class=\"input-group fit-w\">\n <div class=\"form-control d-flex a-i-center inner-scroll\" style=\"min-width: 104px; padding-top: 0; padding-bottom: 0;\">\n <ng-container *ngIf=\"chips.length !== severitiesList.length; else allAlarms\">\n <span\n class=\"tag tag--info chip\"\n *ngFor=\"let chip of chips\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10\"\n title=\"{{ 'Remove' | translate }}\"\n type=\"button\"\n (click)=\"deselectChip(chip); $event.stopPropagation()\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n <i\n class=\"status stroked-icon icon-12\"\n [c8yIcon]=\"chip | AlarmSeverityToIcon\"\n [ngClass]=\"chip | lowercase\"\n ></i>\n {{ SEVERITY_LABELS[chip] | translate }}\n </span>\n </ng-container>\n <ng-template #allAlarms>\n <span class=\"text-truncate\" title=\"{{ 'All severities' | translate }}\">\n {{ 'All severities' | translate }}\n </span>\n </ng-template>\n </div>\n <div class=\"input-group-btn input-group-btn--last\">\n <button\n class=\"btn-default btn btn--caret\"\n title=\"{{ chips | AlarmSeveritiesToTitle }}\"\n data-cy=\"c8y-alarm-filter\"\n dropdownToggle\n (click)=\"resetForm()\"\n >\n <i class=\"caret\"></i>\n </button>\n </div>\n </div>\n <ul\n class=\"dropdown-menu dropdown-menu-action-bar\"\n *dropdownMenu\n >\n <li class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center sticky-top separator-bottom\">\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n [title]=\"'All' | translate\"\n >\n <input\n type=\"checkbox\"\n data-cy=\"c8y-alarm-filter--all\"\n [ngModelOptions]=\"{ standalone: true }\"\n (ngModelChange)=\"allChanged($event)\"\n [ngModel]=\"isEachCheckboxSelected$ | async\"\n [indeterminate]=\"isIndeterminate$ | async\"\n (click)=\"markSeveritiesAsTouched()\"\n />\n <span></span>\n <i\n class=\"status stroked-icon m-l-8 icon-20\"\n [c8yIcon]=\"'bell'\"\n ></i>\n <span class=\"m-l-8\">{{ 'All' | translate }}</span>\n </label>\n </li>\n <li\n class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center\"\n *ngFor=\"let severity of severitiesList\"\n >\n <label\n class=\"c8y-checkbox d-flex a-i-center\"\n [title]=\"SEVERITY_LABELS[severity] | translate\"\n >\n <input\n type=\"checkbox\"\n [attr.data-cy]=\"'c8y-alarm-filter--' + severity\"\n [formControlName]=\"severity\"\n [value]=\"severity\"\n (click)=\"markSeveritiesAsTouched()\"\n />\n <span></span>\n <i\n class=\"status stroked-icon m-l-8 icon-20\"\n [c8yIcon]=\"severity | AlarmSeverityToIcon\"\n [ngClass]=\"severity | lowercase\"\n ></i>\n <span class=\"m-l-8\">{{ SEVERITY_LABELS[severity] | translate }}</span>\n </label>\n <!-- badge -->\n <div\n class=\"badge badge-info m-l-auto\"\n *ngIf=\"alarmCounts[severity] || alarmCounts[severity] === 0\"\n >\n <i\n class=\"icon-spin\"\n [c8yIcon]=\"'circle-o-notch'\"\n *ngIf=\"countLoading\"\n ></i>\n <span\n *ngIf=\"!countLoading\"\n [attr.data-cy]=\"severity + '-badge'\"\n >\n {{ alarmCounts[severity] < 99 ? alarmCounts[severity] : '99+' }}\n </span>\n </div>\n </li>\n <li class=\"p-l-16 p-r-16 p-t-4 p-b-4 d-flex a-i-center separator-top\">\n <label\n class=\"c8y-switch\"\n [attr.aria-label]=\"'Show cleared alarms' | translate\"\n [attr.data-cy]=\"'c8y-alarms-filter--cleared'\"\n >\n <input\n type=\"checkbox\"\n #showClearedCheckbox\n [ngModelOptions]=\"{ standalone: true }\"\n [(ngModel)]=\"showCleared\"\n (click)=\"markSeveritiesAsTouched(); updateAlarmsCount(showClearedCheckbox.checked)\"\n />\n <span></span>\n <span\n class=\"text-truncate\"\n title=\"{{ 'Show cleared alarms' | translate }}\"\n >\n {{ 'Show cleared alarms' | translate }}\n </span>\n </label>\n </li>\n\n <div class=\"p-16 d-flex sticky-bottom separator-top\">\n <button\n [attr.data-cy]=\"'c8y-alarms-filter--apply'\"\n class=\"btn btn-primary btn-sm flex-grow\"\n title=\"{{ 'Apply' | translate }}\"\n type=\"button\"\n (click)=\"applyFilters(false); closeDropdown()\"\n [disabled]=\"shouldDisableApplyButton$ | async\"\n >\n {{ 'Apply' | translate }}\n </button>\n </div>\n </ul>\n </div>\n</form>\n" }]
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2.AlarmsViewService }, { type: i3.AlertService }, { type: i4.Router }, { type: i4.ActivatedRoute }], propDecorators: { contextSourceId: [{
type: Input
}], onFilterApplied: [{
type: Output
}], filtersDropdown: [{
type: ViewChild,
args: ['filtersDropdown']
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxhcm1zLWZpbHRlci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9hbGFybXMvYWxhcm1zLWZpbHRlci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi9hbGFybXMvYWxhcm1zLWZpbHRlci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUwsU0FBUyxFQUNULFlBQVksRUFDWixLQUFLLEVBR0wsTUFBTSxFQUNOLFNBQVMsRUFDVixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0MsT0FBTyxFQUVMLFFBQVEsRUFFUixlQUFlLEVBRWhCLE1BQU0sYUFBYSxDQUFDO0FBQ3JCLE9BQU8sRUFBRSxZQUFZLEVBQW1CLE1BQU0scUJBQXFCLENBQUM7QUFDcEUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDN0QsT0FBTyxFQUFFLGVBQWUsRUFBYyxPQUFPLEVBQUUsYUFBYSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2xHLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3pGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzFELE9BQU8sRUFFTCxvQkFBb0IsRUFDcEIsdUJBQXVCLEVBRXhCLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEIsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQzs7Ozs7Ozs7Ozs7O0FBT3pELE1BQU0sT0FBTyxxQkFBcUI7SUFnQ2hDLFlBQ1UsV0FBd0IsRUFDeEIsaUJBQW9DLEVBQ3BDLFlBQTBCLEVBQzFCLE1BQWMsRUFDZCxjQUE4QjtRQUo5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UCxlQUFlO3NCQURkLFNBQVM7dUJBQUMsaUJBQWlCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ29tcG9uZW50LFxuICBFdmVudEVtaXR0ZXIsXG4gIElucHV0LFxuICBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgT3V0cHV0LFxuICBWaWV3Q2hpbGRcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBGb3JtQnVpbGRlciB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB7XG4gIEFsYXJtUXVlcnlGaWx0ZXIsXG4gIFNldmVyaXR5LFxuICBTZXZlcml0eUZpbHRlcixcbiAgU0VWRVJJVFlfTEFCRUxTLFxuICBTZXZlcml0eVR5cGVcbn0gZnJvbSAnQGM4eS9jbGllbnQnO1xuaW1wb3J0IHsgQWxlcnRTZXJ2aWNlLCBEYXRlVGltZUNvbnRleHQgfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzJztcbmltcG9ydCB7IEJzRHJvcGRvd25EaXJlY3RpdmUgfSBmcm9tICduZ3gtYm9vdHN0cmFwL2Ryb3Bkb3duJztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgT2JzZXJ2YWJsZSwgU3ViamVjdCwgY29tYmluZUxhdGVzdCwgZnJvbSwgZm9ya0pvaW4sIEVNUFRZIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBjYXRjaEVycm9yLCBmaWx0ZXIsIGZpbmFsaXplLCBtYXAsIHN0YXJ0V2l0aCwgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgQWxhcm1zVmlld1NlcnZpY2UgfSBmcm9tICcuL2FsYXJtcy12aWV3LnNlcnZpY2UnO1xuaW1wb3J0IHtcbiAgQWxhcm1Db3VudCxcbiAgREVGQVVMVF9BTEFSTV9DT1VOVFMsXG4gIERFRkFVTFRfU0VWRVJJVFlfVkFMVUVTLFxuICBBbGFybUxpc3RGb3JtRmlsdGVyc1xufSBmcm9tICcuL2FsYXJtcy5tb2RlbCc7XG5pbXBvcnQgeyBBY3RpdmF0ZWRSb3V0ZSwgUm91dGVyIH0gZnJvbSAnQGFuZ3VsYXIvcm91dGVyJztcbmltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2M4eS1hbGFybXMtZmlsdGVyJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2FsYXJtcy1maWx0ZXIuY29tcG9uZW50Lmh0bWwnXG59KVxuZXhwb3J0IGNsYXNzIEFsYXJtc0ZpbHRlckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgQWZ0ZXJWaWV3SW5pdCwgT25EZXN0cm95IHtcbiAgcmVhZG9ubHkgc2V2ZXJpdGllc0xpc3QgPSBPYmplY3Qua2V5cyhTRVZFUklUWV9MQUJFTFMpIGFzIFNldmVyaXR5VHlwZVtdO1xuXG4gIEBJbnB1dCgpXG4gIGNvbnRleHRTb3VyY2VJZDogbnVtYmVyIHwgc3RyaW5nIHwgbnVsbDtcblxuICAvKipcbiAgICogRXZlbnRFbWl0dGVyIHRvIG5vdGlmeSB3aGVuIGZpbHRlcnMgaGF2ZSBiZWVuIGFwcGxpZWQuXG4gICAqIEVtaXRzIGEgYEFsYXJtTGlzdEZvcm1GaWx0ZXJzYCBvYmplY3QgcmVwcmVzZW50aW5nIHRoZSBmaWx0ZXIgY3JpdGVyaWEgYXBwbGllZCBieSB0aGUgdXNlci5cbiAgICovXG4gIEBPdXRwdXQoKVxuICBvbkZpbHRlckFwcGxpZWQgPSBuZXcgRXZlbnRFbWl0dGVyPEFsYXJtTGlzdEZvcm1GaWx0ZXJzPigpO1xuXG4gIEBWaWV3Q2hpbGQoJ2ZpbHRlcnNEcm9wZG93bicpXG4gIGZpbHRlcnNEcm9wZG93bjogQnNEcm9wZG93bkRpcmVjdGl2ZTtcblxuICBmb3JtR3JvdXAgPSB0aGlzLmZvcm1CdWlsZGVyLmdyb3VwKERFRkFVTFRfU0VWRVJJVFlfVkFMVUVTKTtcbiAgY2hpcHM6IFNldmVyaXR5VHlwZVtdID0gW107XG4gIGlzSW5kZXRlcm1pbmF0ZSQ6IE9ic2VydmFibGU8Ym9vbGVhbj47XG4gIGNvdW50TG9hZGluZzogYm9vbGVhbjtcbiAgc2hvd0NsZWFyZWQgPSBmYWxzZTtcbiAgYWxhcm1Db3VudHM6IEFsYXJtQ291bnQgPSBERUZBVUxUX0FMQVJNX0NPVU5UUztcbiAgc2VsZWN0ZWREYXRlczogRGF0ZVRpbWVDb250ZXh0O1xuICBzaG91bGREaXNhYmxlQXBwbHlCdXR0b24kOiBPYnNlcnZhYmxlPGJvb2xlYW4+O1xuICBpc0VhY2hDaGVja2JveFNlbGVjdGVkJDogT2JzZXJ2YWJsZTxib29sZWFuPjtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IFNFVkVSSVRZX0xBQkVMUyA9IFNFVkVSSVRZX0xBQkVMUztcbiAgcHJpdmF0ZSBpc05vbmVDaGVja2JveFNlbGVjdGVkJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuICBwcml2YXRlIHNldmVyaXRpZXNUb3VjaGVkJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuICBwcml2YXRlIGN1cnJlbnRGb3JtR3JvdXBWYWx1ZXMgPSB0aGlzLmZvcm1Hcm91cC52YWx1ZTtcbiAgcHJpdmF0ZSBjdXJyZW50U2hvd0NsZWFyZWRWYWx1ZTogYm9vbGVhbjtcbiAgcHJpdmF0ZSBkZXN0cm95JDogU3ViamVjdDx2b2lkPiA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBmb3JtQnVpbGRlcjogRm9ybUJ1aWxkZXIsXG4gICAgcHJpdmF0ZSBhbGFybXNWaWV3U2VydmljZTogQWxhcm1zVmlld1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBhbGVydFNlcnZpY2U6IEFsZXJ0U2VydmljZSxcbiAgICBwcml2YXRlIHJvdXRlcjogUm91dGVyLFxuICAgIHByaXZhdGUgYWN0aXZhdGVkUm91dGU6IEFjdGl2YXRlZFJvdXRlXG4gICkge31cblxuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLmFjdGl2YXRlZFJvdXRlLnF1ZXJ5UGFyYW1zLnBpcGUodGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKS5zdWJzY3JpYmUocGFyYW1zID0+IHtcbiAgICAgIHRoaXMuc2hvd0NsZWFyZWQgPSBwYXJhbXMuc2hvd0NsZWFyZWQgPT09ICd0cnVlJztcbiAgICAgIHRoaXMuZm9ybUdyb3VwLnNldFZhbHVlKHtcbiAgICAgICAgW1NldmVyaXR5LkNSSVRJQ0FMXTogcGFyYW1zW1NldmVyaXR5LkNSSVRJQ0FMXSA9PT0gJ3RydWUnLFxuICAgICAgICBbU2V2ZXJpdHkuTUFKT1JdOiBwYXJhbXNbU2V2ZXJpdHkuTUFKT1JdID09PSAndHJ1ZScsXG4gICAgICAgIFtTZXZlcml0eS5NSU5PUl06IHBhcmFtc1tTZXZlcml0eS5NSU5PUl0gPT09ICd0cnVlJyxcbiAgICAgICAgW1NldmVyaXR5LldBUk5JTkddOiBwYXJhbXNbU2V2ZXJpdHkuV0FSTklOR10gPT09ICd0cnVlJ1xuICAgICAgfSk7XG4gICAgICBpZiAocGFyYW1zLmxhc3RVcGRhdGVkRnJvbSkge1xuICAgICAgICB0aGlzLnNlbGVjdGVkRGF0ZXMgPSBbbmV3IERhdGUocGFyYW1zLmxhc3RVcGRhdGVkRnJvbSksIG5ldyBEYXRlKHBhcmFtcy5jcmVhdGVkVG8pXTtcbiAgICAgIH1cbiAgICAgIHRoaXMuYXBwbHlGaWx0ZXJzKHRydWUsIGZhbHNlKTtcbiAgICB9KTtcbiAgICB0aGlzLnRyYWNrQ2hlY2tib3hTdGF0ZVdpdGhGb3JtQ2hhbmdlcygpO1xuICAgIHRoaXMuY3VycmVudFNob3dDbGVhcmVkVmFsdWUgPSB0aGlzLnNob3dDbGVhcmVkO1xuICAgIHRoaXMudXBkYXRlQ2hpcHNBbmREZWZhdWx0VmFsdWVzKCk7XG4gIH1cblxuICBuZ0FmdGVyVmlld0luaXQoKTogdm9pZCB7XG4gICAgdGhpcy5maWx0ZXJzRHJvcGRvd24uaXNPcGVuQ2hhbmdlXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCksIGZpbHRlcihCb29sZWFuKSlcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4gdGhpcy51cGRhdGVBbGFybXNDb3VudCgpKTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuZGVzdHJveSQubmV4dCgpO1xuICAgIHRoaXMuZGVzdHJveSQuY29tcGxldGUoKTtcbiAgfVxuXG4gIGFsbENoYW5nZWQoc2VsZWN0ZWQ6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLmZvcm1Hcm91cC5wYXRjaFZhbHVlKHtcbiAgICAgIFtTZXZlcml0eS5DUklUSUNBTF06IHNlbGVjdGVkLFxuICAgICAgW1NldmVyaXR5Lk1BSk9SXTogc2VsZWN0ZWQsXG4gICAgICBbU2V2ZXJpdHkuTUlOT1JdOiBzZWxlY3RlZCxcbiAgICAgIFtTZXZlcml0eS5XQVJOSU5HXTogc2VsZWN0ZWRcbiAgICB9IGFzIFNldmVyaXR5RmlsdGVyKTtcbiAgfVxuXG4gIGFwcGx5RmlsdGVycyhlbWl0ID0gdHJ1ZSwgbmF2aWdhdGUgPSB0cnVlKTogdm9pZCB7XG4gICAgdGhpcy51cGRhdGVDaGlwc0FuZERlZmF1bHRWYWx1ZXMoKTtcbiAgICBjb25zdCBjb21iaW5lZEZvcm1FdmVudDogQWxhcm1MaXN0Rm9ybUZpbHRlcnMgPSB7XG4gICAgICBzaG93Q2xlYXJlZDogdGhpcy5zaG93Q2xlYXJlZCxcbiAgICAgIHNldmVyaXR5T3B0aW9uczogdGhpcy5mb3JtR3JvdXAudmFsdWUsXG4gICAgICBzZWxlY3RlZERhdGVzOiB0aGlzLnNlbGVjdGVkRGF0ZXNcbiAgICB9O1xuICAgIGlmIChlbWl0KSB7XG4gICAgICB0aGlzLm9uRmlsdGVyQXBwbGllZC5lbWl0KGNvbWJpbmVkRm9ybUV2ZW50KTtcbiAgICB9XG4gICAgdGhpcy5jdXJyZW50Rm9ybUdyb3VwVmFsdWVzID0gdGhpcy5mb3JtR3JvdXAudmFsdWU7XG4gICAgdGhpcy5jdXJyZW50U2hvd0NsZWFyZWRWYWx1ZSA9IHRoaXMuc2hvd0NsZWFyZWQ7XG5cbiAgICBpZiAobmF2aWdhdGUpIHtcbiAgICAgIHRoaXMucm91dGVyLm5hdmlnYXRlKFtdLCB7XG4gICAgICAgIHF1ZXJ5UGFyYW1zOiB7XG4gICAgICAgICAgc2hvd0NsZWFyZWQ6IGNvbWJpbmVkRm9ybUV2ZW50LnNob3dDbGVhcmVkLFxuICAgICAgICAgIC4uLmNvbWJpbmVkRm9ybUV2ZW50LnNldmVyaXR5T3B0aW9ucyxcbiAgICAgICAgICBsYXN0VXBkYXRlZEZyb206IGNvbWJpbmVkRm9ybUV2ZW50LnNlbGVjdGVkRGF0ZXM/LlswXT8udG9JU09TdHJpbmcoKSxcbiAgICAgICAgICBjcmVhdGVkVG86IGNvbWJpbmVkRm9ybUV2ZW50LnNlbGVjdGVkRGF0ZXM/LlsxXT8udG9JU09TdHJpbmcoKVxuICAgICAgICB9LFxuICAgICAgICBxdWVyeVBhcmFtc0hhbmRsaW5nOiAnbWVyZ2UnXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBkZXNlbGVjdENoaXAoY2hpcDogU2V2ZXJpdHlUeXBlKTogdm9pZCB7XG4gICAgdGhpcy5mb3JtR3JvdXAucGF0Y2hWYWx1ZSh7XG4gICAgICAuLi50aGlzLmZvcm1Hcm91cC52YWx1ZSxcbiAgICAgIFtjaGlwXTogZmFsc2VcbiAgICB9KTtcbiAgICB0aGlzLmFwcGx5RmlsdGVycyhmYWxzZSk7XG4gICAgdGhpcy5jbG9zZURyb3Bkb3duKCk7XG4gIH1cblxuICBjbG9zZURyb3Bkb3duKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmZpbHRlcnNEcm9wZG93bi5pc09wZW4pIHtcbiAgICAgIHRoaXMuZmlsdGVyc0Ryb3Bkb3duLmlzT3BlbiA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIHJlc2V0Rm9ybSgpOiB2b2lkIHtcbiAgICB0aGlzLmZvcm1Hcm91cC5yZXNldCh0aGlzLmN1cnJlbnRGb3JtR3JvdXBWYWx1ZXMpO1xuICAgIHRoaXMuc2V2ZXJpdGllc1RvdWNoZWQkLm5leHQoZmFsc2UpO1xuICAgIHRoaXMuc2hvd0NsZWFyZWQgPSB0aGlzLmN1cnJlbnRTaG93Q2xlYXJlZFZhbHVlO1xuICB9XG5cbiAgbWFya1NldmVyaXRpZXNBc1RvdWNoZWQoKTogdm9pZCB7XG4gICAgdGhpcy5zZXZlcml0aWVzVG91Y2hlZCQubmV4dCh0cnVlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBc3luY2hyb25vdXNseSBmZXRjaGVzIGFuZCB1cGRhdGVzIHRoZSBjb3VudCBvZiBhbGFybXMgZm9yIGVhY2ggc2V2ZXJpdHkgbGV2ZWwuXG4gICAqXG4gICAqIEBwYXJhbSBzaG93Q2xlYXJlZCAtIFdoZXRoZXIgdG8gaW5jbHVkZSBjbGVhcmVkIGFsYXJtcyBpbiB0aGUgY291bnQuXG4gICAqIERlZmF1bHRzIHRvIHRoZSBjdXJyZW50IHZhbHVlIG9mIGB0aGlzLmN1cnJlbnRTaG93Q2xlYXJlZFZhbHVlYC5cbiAgICogQHJldHVybnMgQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiBhbGwgYWxhcm0gY291bnRzIGhhdmUgYmVlbiBmZXRjaGVkLFxuICAgKiBvciByZWplY3RzIHdpdGggYW4gZXJyb3IgaWYgdGhlIG9wZXJhdGlvbiBmYWlscy5cbiAgICogQHRocm93cyBXaWxsIHRocm93IGFuIGVycm9yIGlmIGFueSBvZiB0aGUgYWxhcm0gY291bnQgZmV0Y2hpbmcgcHJvbWlzZXMgcmVqZWN0LlxuICAgKi9cbiAgYXN5bmMgdXBkYXRlQWxhcm1zQ291bnQoc2hvd0NsZWFyZWQgPSB0aGlzLmN1cnJlbnRTaG93Q2xlYXJlZFZhbHVlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5jb3VudExvYWRpbmcgPSB0cnVlO1xuICAgIGNvbnN0IGFsYXJtRmlsdGVyOiBBbGFybVF1ZXJ5RmlsdGVyIHwgbnVsbCA9IHRoaXMuY29udGV4dFNvdXJjZUlkXG4gICAgICA/IHtcbiAgICAgICAgICBzb3VyY2U6IHRoaXMuY29udGV4dFNvdXJjZUlkLFxuICAgICAgICAgIHdpdGhTb3VyY2VBc3NldHM6IHRydWUsXG4gICAgICAgICAgd2l0aFNvdXJjZURldmljZXM6IHRydWVcbiAgICAgICAgfVxuICAgICAgOiBudWxsO1xuICAgIGNvbnN0IG9ic2VydmFibGVzOiBSZWNvcmQ8U2V2ZXJpdHlUeXBlLCBPYnNlcnZhYmxlPG51bWJlcj4+ID0gdGhpcy5zZXZlcml0aWVzTGlzdC5yZWR1Y2UoXG4gICAgICAoYWNjLCBzZXZlcml0eSkgPT4ge1xuICAgICAgICBhY2Nbc2V2ZXJpdHldID0gZnJvbShcbiAgICAgICAgICB0aGlzLmFsYXJtc1ZpZXdTZXJ2aWNlLmdldEFsYXJtc0NvdW50QnlTZXZlcml0eShzZXZlcml0eSwgc2hvd0NsZWFyZWQsIGFsYXJtRmlsdGVyKVxuICAgICAgICApO1xuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgQ1JJVElDQUw6IG51bGwsXG4gICAgICAgIE1BSk9SOiBudWxsLFxuICAgICAgICBNSU5PUjogbnVsbCxcbiAgICAgICAgV0FSTklORzogbnVsbFxuICAgICAgfVxuICAgICk7XG4gICAgZm9ya0pvaW4ob2JzZXJ2YWJsZXMpXG4gICAgICAucGlwZShcbiAgICAgICAgY2F0Y2hFcnJvcihlcnIgPT4ge1xuICAgICAgICAgIHRoaXMuYWxhcm1Db3VudHMgPSB7fSBhcyBBbGFybUNvdW50O1xuICAgICAgICAgIHRoaXMuYWxlcnRTZXJ2aWNlLmFkZFNlcnZlckZhaWx1cmUoZXJyKTtcbiAgICAgICAgICByZXR1cm4gRU1QVFk7XG4gICAgICAgIH0pLFxuICAgICAgICBmaW5hbGl6ZSgoKSA9PiAodGhpcy5jb3VudExvYWRpbmcgPSBmYWxzZSkpXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKChhbGFybUNvdW50czogQWxhcm1Db3VudCkgPT4gKHRoaXMuYWxhcm1Db3VudHMgPSBhbGFybUNvdW50cykpO1xuICB9XG5cbiAgcHJpdmF0ZSBjcmVhdGVGb3JtVmFsdWVXaXRoQ2hhbmdlc1N0cmVhbSgpOiBPYnNlcnZhYmxlPFNldmVyaXR5RmlsdGVyPiB7XG4gICAgcmV0dXJuIHRoaXMuZm9ybUdyb3VwLnZhbHVlQ2hhbmdlcy5waXBlKHN0YXJ0V2l0aCh0aGlzLmZvcm1Hcm91cC52YWx1ZSkpO1xuICB9XG5cbiAgcHJpdmF0ZSB0cmFja0NoZWNrYm94U3RhdGVXaXRoRm9ybUNoYW5nZXMoKTogdm9pZCB7XG4gICAgY29uc3QgZm9ybVZhbHVlJCA9IHRoaXMuY3JlYXRlRm9ybVZhbHVlV2l0aENoYW5nZXNTdHJlYW0oKTtcbiAgICB0aGlzLmlzRWFjaENoZWNrYm94U2VsZWN0ZWQkID0gdGhpcy5jcmVhdGVBbGxTZWxlY3RlZFN0cmVhbShmb3JtVmFsdWUkKTtcbiAgICB0aGlzLmlzSW5kZXRlcm1pbmF0ZSQgPSB0aGlzLmNyZWF0ZUluZGV0ZXJtaW5hdGVTdHJlYW0oZm9ybVZhbHVlJCk7XG4gICAgdGhpcy50cmFja0FsbENoZWNrYm94ZXNEaXNhYmxlZChmb3JtVmFsdWUkKTtcbiAgICB0aGlzLnNob3VsZERpc2FibGVBcHBseUJ1dHRvbiQgPSB0aGlzLmNyZWF0ZURpc2FibGVBcHBseUJ1dHRvblN0cmVhbSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBjcmVhdGVBbGxTZWxlY3RlZFN0cmVhbShmb3JtVmFsdWUkOiBPYnNlcnZhYmxlPFNldmVyaXR5RmlsdGVyPik6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuICAgIHJldHVybiBmb3JtVmFsdWUkLnBpcGUobWFwKHNldmVyaXRpZXMgPT4gT2JqZWN0LnZhbHVlcyhzZXZlcml0aWVzKS5ldmVyeShCb29sZWFuKSkpO1xuICB9XG5cbiAgcHJpdmF0ZSBjcmVhdGVJbmRldGVybWluYXRlU3RyZWFtKGZvcm1WYWx1ZSQ6IE9ic2VydmFibGU8U2V2ZXJpdHlGaWx0ZXI+KTogT2JzZXJ2YWJsZTxib29sZWFuPiB7XG4gICAgcmV0dXJuIGZvcm1WYWx1ZSQucGlwZShcbiAgICAgIG1hcChcbiAgICAgICAgc2V2ZXJpdGllcyA9PlxuICAgICAgICAgIE9iamVjdC52YWx1ZXMoc2V2ZXJpdGllcykuc29tZShCb29sZWFuKSAmJiAhT2JqZWN0LnZhbHVlcyhzZXZlcml0aWVzKS5ldmVyeShCb29sZWFuKVxuICAgICAgKVxuICAgICk7XG4gIH1cblxuICBwcml2YXRlIHRyYWNrQWxsQ2hlY2tib3hlc0Rpc2FibGVkKGZvcm1WYWx1ZSQ6IE9ic2VydmFibGU8U2V2ZXJpdHlGaWx0ZXI+KTogdm9pZCB7XG4gICAgZm9ybVZhbHVlJC5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSkuc3Vic2NyaWJlKHNldmVyaXRpZXMgPT4ge1xuICAgICAgY29uc3QgYXJlQWxsRGlzYWJsZWQgPSBPYmplY3QudmFsdWVzKHNldmVyaXRpZXMpLmV2ZXJ5KHZhbHVlID0+ICF2YWx1ZSk7XG4gICAgICB0aGlzLmlzTm9uZUNoZWNrYm94U2VsZWN0ZWQkLm5leHQoYXJlQWxsRGlzYWJsZWQpO1xuICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBjcmVhdGVEaXNhYmxlQXBwbHlCdXR0b25TdHJlYW0oKTogT2JzZXJ2YWJsZTxib29sZWFuPiB7XG4gICAgcmV0dXJuIGNvbWJpbmVMYXRlc3QoW3RoaXMuaXNOb25lQ2hlY2tib3hTZWxlY3RlZCQsIHRoaXMuc2V2ZXJpdGllc1RvdWNoZWQkXSkucGlwZShcbiAgICAgIG1hcChcbiAgICAgICAgKFthbGxDaGVja2JveGVzQXJlRGlzYWJsZWQsIHNldmVyaXRpZXNUb3VjaGVkXSkgPT5cbiAgICAgICAgICBhbGxDaGVja2JveGVzQXJlRGlzYWJsZWQgfHwgIXNldmVyaXRpZXNUb3VjaGVkXG4gICAgICApXG4gICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgdXBkYXRlQ2hpcHNBbmREZWZhdWx0VmFsdWVzKCk6IHZvaWQge1xuICAgIGNvbnN0IHNldmVyaXR5RmlsdGVyID0gdGhpcy5mb3JtR3JvdXA7XG4gICAgY29uc3Qgc2V2ZWVyaXR5VmFsdWVzID0gc2V2ZXJpdHlGaWx0ZXIudmFsdWU7XG5cbiAgICB0aGlzLmNoaXBzID0gT2JqZWN0LmtleXMoc2V2ZWVyaXR5VmFsdWVzKS5maWx0ZXIoa2V5ID0+IHNldmVlcml0eVZhbHVlc1trZXldKSBhcyBTZXZlcml0eVR5cGVbXTtcbiAgICBjb25zdCBhbGxDaGlwc1JlbW92ZWQgPSB0aGlzLmNoaXBzLmxlbmd0aCA9PT0gMDtcbiAgICBpZiAoYWxsQ2hpcHNSZW1vdmVkKSB7XG4gICAgICBjb25zdCBkZWZhdWx0VmFsdWVzID0gREVGQVVMVF9TRVZFUklUWV9WQUxVRVM7XG4gICAgICBzZXZlcml0eUZpbHRlci5zZXRWYWx1ZShkZWZhdWx0VmFsdWVzKTtcbiAgICAgIHRoaXMuY2hpcHMgPSBPYmplY3Qua2V5cyhkZWZhdWx0VmFsdWVzKSBhcyBTZXZlcml0eVR5cGVbXTtcbiAgICB9XG4gIH1cbn1cbiIsIjxmb3JtXG4gIGNsYXNzPVwiZC1mbGV4IGEtaS1jZW50ZXJcIlxuICBbZm9ybUdyb3VwXT1cImZvcm1Hcm91cFwiXG4+XG4gIDxkaXZcbiAgICBjbGFzcz1cImRyb3Bkb3duXCJcbiAgICB0aXRsZT1cInt7ICdGaWx0ZXIgYnkgc2V2ZXJpdHknIHwgdHJhbnNsYXRlIH19XCJcbiAgICBkcm9wZG93blxuICAgICNmaWx0ZXJzRHJvcGRvd249XCJicy1kcm9wZG93blwiXG4gICAgW2Nka1RyYXBGb2N1c109XCJmaWx0ZXJzRHJvcGRvd24uaXNPcGVuXCJcbiAgICBbaW5zaWRlQ2xpY2tdPVwidHJ1ZVwiXG4gID5cbiAgICA8ZGl2IGNsYXNzPVwiaW5wdXQtZ3JvdXAgZml0LXdcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJmb3JtLWNvbnRyb2wgZC1mbGV4IGEtaS1jZW50ZXIgaW5uZXItc2Nyb2xsXCIgc3R5bGU9XCJtaW4td2lkdGg6IDEwNHB4OyBwYWRkaW5nLXRvcDogMDsgcGFkZGluZy1ib3R0b206IDA7XCI+XG4gICAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJjaGlwcy5sZW5ndGggIT09IHNldmVyaXRpZXNMaXN0Lmxlbmd0aDsgZWxzZSBhbGxBbGFybXNcIj5cbiAgICAgICAgICA8c3BhblxuICAgICAgICAgICAgY2xhc3M9XCJ0YWcgdGFnLS1pbmZvIGNoaXBcIlxuICAgICAgICAgICAgKm5nRm9yPVwibGV0IGNoaXAgb2YgY2hpcHNcIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgY2xhc3M9XCJidG4gYnRuLXhzIGJ0bi1jbGVhbiB0ZXh0LTEwXCJcbiAgICAgICAgICAgICAgdGl0bGU9XCJ7eyAnUmVtb3ZlJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAoY2xpY2spPVwiZGVzZWxlY3RDaGlwKGNoaXApOyAkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICA8aSBjOHlJY29uPVwidGltZXNcIj48L2k+XG4gICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgIDxpXG4gICAgICAgICAgICAgIGNsYXNzPVwic3RhdHVzIHN0cm9rZWQtaWNvbiBpY29uLTEyXCJcbiAgICAgICAgICAgICAgW2M4eUljb25dPVwiY2hpcCB8IEFsYXJtU2V2ZXJpdHlUb0ljb25cIlxuICAgICAgICAgICAgICBbbmdDbGFzc109XCJjaGlwIHwgbG93ZXJjYXNlXCJcbiAgICAgICAgICAgID48L2k+XG4gICAgICAgICAgICB7eyBTRVZFUklUWV9MQUJFTFNbY2hpcF0gfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICA8L3NwYW4+XG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICA8bmctdGVtcGxhdGUgI2FsbEFsYXJtcz5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cInRleHQtdHJ1bmNhdGVcIiB0aXRsZT1cInt7ICdBbGwgc2V2ZXJpdGllcycgfCB0cmFuc2xhdGUgfX1cIj5cbiAgICAgICAgICAgIHt7ICdBbGwgc2V2ZXJpdGllcycgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICA8L3NwYW4+XG4gICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxkaXYgY2xhc3M9XCJpbnB1dC1ncm91cC1idG4gaW5wdXQtZ3JvdXAtYnRuLS1sYXN0XCI+XG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICBjbGFzcz1cImJ0bi1kZWZhdWx0IGJ0biBidG4tLWNhcmV0XCJcbiAgICAgICAgICB0aXRsZT1cInt7IGNoaXBzIHwgQWxhcm1TZXZlcml0aWVzVG9UaXRsZSB9fVwiXG4gICAgICAgICAgZGF0YS1jeT1cImM4eS1hbGFybS1maWx0ZXJcIlxuICAgICAgICAgIGRyb3Bkb3duVG9nZ2xlXG4gICAgICAgICAgKGNsaWNrKT1cInJlc2V0Rm9ybSgpXCJcbiAgICAgICAgPlxuICAgICAgICAgIDxpIGNsYXNzPVwiY2FyZXRcIj48L2k+XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG4gICAgPHVsXG4gICAgICBjbGFzcz1cImRyb3Bkb3duLW1lbnUgZHJvcGRvd24tbWVudS1hY3Rpb24tYmFyXCJcbiAgICAgICpkcm9wZG93bk1lbnVcbiAgICA+XG4gICAgICA8bGkgY2xhc3M9XCJwLWwtMTYgcC1yLTE2IHAtdC00IHAtYi00IGQtZmxleCBhLWktY2VudGVyIHN0aWNreS10b3Agc2VwYXJhdG9yLWJvdHRvbVwiPlxuICAgICAgICA8bGFiZWxcbiAgICAgICAgICBjbGFzcz1cImM4eS1jaGVja2JveCBkLWZsZXggYS1pLWNlbnRlclwiXG4gICAgICAgICAgW3RpdGxlXT1cIidBbGwnIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgPlxuICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgdHlwZT1cImNoZWNrYm94XCJcbiAgICAgICAgICAgIGRhdGEtY3k9XCJjOHktYWxhcm0tZmlsdGVyLS1hbGxcIlxuICAgICAgICAgICAgW25nTW9kZWxPcHRpb25zXT1cInsgc3RhbmRhbG9uZTogdHJ1ZSB9XCJcbiAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cImFsbENoYW5nZWQoJGV2ZW50KVwiXG4gICAgICAgICAgICBbbmdNb2RlbF09XCJpc0VhY2hDaGVja2JveFNlbGVjdGVkJCB8IGFzeW5jXCJcbiAgICAgICAgICAgIFtpbmRldGVybWluYXRlXT1cImlzSW5kZXRlcm1pbmF0ZSQgfCBhc3luY1wiXG4gICAgICAgICAgICAoY2xpY2spPVwibWFya1NldmVyaXRpZXNBc1RvdWNoZWQoKVwiXG4gICAgICAgICAgLz5cbiAgICAgICAgICA8c3Bhbj48L3NwYW4+XG4gICAgICAgICAgPGlcbiAgICAgICAgICAgIGNsYXNzPVwic3RhdHVzIHN0cm9rZWQtaWNvbiBtLWwtOCBpY29uLTIwXCJcbiAgICAgICAgICAgIFtjOHlJY29uXT1cIidiZWxsJ1wiXG4gICAgICAgICAgPjwvaT5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm0tbC04XCI+e3sgJ0FsbCcgfCB0cmFuc2xhdGUgfX08L3NwYW4+XG4gICAgICAgIDwvbGFiZWw+XG4gICAgICA8L2xpPlxuICAgICAgPGxpXG4gICAgICAgIGNsYXNzPVwicC1sLTE2IHAtci0xNiBwLXQtNCBwLWItNCBkLWZsZXggYS1pLWNlbnRlclwiXG4gICAgICAgICpuZ0Zvcj1cImxldCBzZXZlcml0eSBvZiBzZXZlcml0aWVzTGlzdFwiXG4gICAgICA+XG4gICAgICAgIDxsYWJlbFxuICAgICAgICAgIGNsYXNzPVwiYzh5LWNoZWNrYm94IGQtZmxleCBhLWktY2VudGVyXCJcbiAgICAgICAgICBbdGl0bGVdPVwiU0VWRVJJVFlfTEFCRUxTW3NldmVyaXR5XSB8IHRyYW5zbGF0ZVwiXG4gICAgICAgID5cbiAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgICBbYXR0ci5kYXRhLWN5XT1cIidjOHktYWxhcm0tZmlsdGVyLS0nICsgc2V2ZXJpdHlcIlxuICAgICAgICAgICAgW2Zvcm1Db250cm9sTmFtZV09XCJzZXZlcml0eVwiXG4gICAgICAgICAgICBbdmFsdWVdPVwic2V2ZXJpdHlcIlxuICAgICAgICAgICAgKGNsaWNrKT1cIm1hcmtTZXZlcml0aWVzQXNUb3VjaGVkKClcIlxuICAgICAgICAgIC8+XG4gICAgICAgICAgPHNwYW4+PC9