@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
191 lines • 39.2 kB
JavaScript
import { Component, Input, HostBinding, Renderer2, ElementRef, ViewChild } from '@angular/core';
import { debounceTime, map, take, takeUntil } from 'rxjs/operators';
import { fromEventPattern, fromEvent, merge, BehaviorSubject, Subject } from 'rxjs';
import { ActivatedRoute, Router } from '@angular/router';
import { HeaderService } from '../header/header.service';
import { TabsService } from './tabs.service';
import * as i0 from "@angular/core";
import * as i1 from "@angular/router";
import * as i2 from "../header/header.service";
import * as i3 from "./tabs.service";
import * as i4 from "../common/icon.directive";
import * as i5 from "@angular/common";
import * as i6 from "../common/component-outlet.directive";
import * as i7 from "ngx-bootstrap/tooltip";
import * as i8 from "../i18n/c8y-translate.pipe";
export class TabsOutletComponent {
get isNavigatorOpen() {
return this.navigatorOpen;
}
get hasTabs() {
let visibleTabs = [];
if (this.tabs) {
visibleTabs = this.tabs.filter(tab => tab.hide !== true);
}
return visibleTabs.length > 1 || visibleTabs.find(tab => tab.showAlways);
}
get isHorizontal() {
return this.orientation === 'horizontal';
}
get isVertical() {
return this.orientation !== 'horizontal';
}
constructor(router, renderer, header, tabsService, activatedRoute) {
this.router = router;
this.renderer = renderer;
this.header = header;
this.tabsService = tabsService;
this.activatedRoute = activatedRoute;
this.tabs = [];
this.orientation = 'vertical';
this.navigatorOpen = false;
this.openFirstTab = true;
/**
* Identifies if the current view has a header element.
*/
this.hasHeader = false;
this.showLeft = false;
this.showRight = false;
this.extras = {
queryParamsHandling: 'preserve',
relativeTo: this.activatedRoute
};
this.destroy$ = new Subject();
this.tabsChanges$ = new BehaviorSubject([]);
}
ngOnInit() {
if (this.outletName && this.tabs.length === 0) {
const tabsSubscription$ = this.tabsService.items$.pipe(map(tabs => tabs.filter(tab => tab.tabsOutlet === this.outletName)), takeUntil(this.destroy$));
tabsSubscription$.subscribe(tabs => {
this.tabs = tabs;
});
tabsSubscription$.pipe(take(1)).subscribe(tabs => {
if (this.openFirstTab && tabs.length) {
this.navigateTo(tabs[0].path);
}
});
}
else if (this.openFirstTab && this.tabs.length) {
const matchingTab = this.tabs.find(tab => this.isActive(tab));
// only navigate to first tab if no tab is active
if (!matchingTab) {
this.navigateTo(this.tabs[0].path);
}
}
}
ngOnChanges(changes) {
if (changes.tabs) {
this.tabs = this.tabs.filter(tab => tab.tabsOutlet === this.outletName);
this.tabsChanges$.next(this.tabs);
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
ngAfterContentInit() {
if (this.orientation === 'horizontal') {
this.scrollSubscribe();
}
}
trackByPath(index, tab) {
return tab.path ? tab.path : index;
}
isActive(tab) {
if (tab && tab.path) {
const tree = this.router.createUrlTree(typeof tab.path === 'string' ? [tab.path] : tab.path);
return this.router.isActive(tree, {
paths: 'subset',
queryParams: 'subset',
fragment: 'ignored',
matrixParams: 'ignored'
});
}
else {
return false;
}
}
navigateTo(path) {
this.header.toggle();
if (typeof path === 'string') {
this.router.navigate([path], this.extras);
return;
}
this.router.navigate(path, this.extras);
}
scroll(direction = 'right') {
const { widthScroll, width, scrollLeft } = this.getDimensions();
const multiplier = direction === 'left' ? -1 : 1;
let targetScroll = scrollLeft + width * multiplier;
if (targetScroll < 0) {
targetScroll = 0;
}
if (targetScroll > widthScroll - width) {
targetScroll = widthScroll - width;
}
this.container.nativeElement.scrollLeft = targetScroll;
}
scrollSubscribe() {
let unlisten;
merge(this.tabsChanges$, fromEvent(this.container.nativeElement, 'scroll'), fromEventPattern((handler) => {
unlisten = this.renderer.listen('window', 'resize', handler);
}, () => unlisten()))
.pipe(debounceTime(250), takeUntil(this.destroy$))
.subscribe(() => this.updateScroll());
}
updateScroll() {
const { widthScroll, width, scrollLeft } = this.getDimensions();
this.showLeft = !!scrollLeft;
this.showRight = widthScroll > width + scrollLeft;
}
getDimensions() {
const elTabs = this.tabsContainer.nativeElement;
const el = this.container.nativeElement;
const widthScroll = elTabs.scrollWidth;
const width = el.clientWidth;
const scrollLeft = el.scrollLeft;
return { widthScroll, width, scrollLeft };
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabsOutletComponent, deps: [{ token: i1.Router }, { token: i0.Renderer2 }, { token: i2.HeaderService }, { token: i3.TabsService }, { token: i1.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: TabsOutletComponent, selector: "c8y-tabs-outlet,c8y-ui-tabs", inputs: { tabs: "tabs", orientation: "orientation", navigatorOpen: "navigatorOpen", outletName: "outletName", context: "context", openFirstTab: "openFirstTab", hasHeader: "hasHeader" }, host: { properties: { "class.has-header": "this.hasHeader", "class.navigator-open": "this.isNavigatorOpen", "class.page-tabs": "this.hasTabs", "class.page-tabs-horizontal": "this.isHorizontal", "class.page-tabs-vertical": "this.isVertical" } }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, read: ElementRef, static: true }, { propertyName: "tabsContainer", first: true, predicate: ["tabsContainer"], descendants: true, read: ElementRef }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"tabContainer\"\n [ngClass]=\"{ 'hidden-xs': !outletName }\"\n #container\n>\n <button\n class=\"tabs-slider tabs-slider-left\"\n title=\"{{ 'Scroll to the left' | translate }}\"\n type=\"button\"\n #left\n *ngIf=\"showLeft || showRight\"\n [disabled]=\"!showLeft\"\n (click)=\"scroll('left')\"\n >\n <i [c8yIcon]=\"'chevron-left'\"></i>\n </button>\n <button\n class=\"tabs-slider tabs-slider-right\"\n title=\"{{ 'Scroll to the right' | translate }}\"\n type=\"button\"\n #right\n *ngIf=\"showRight || showLeft\"\n [disabled]=\"!showRight\"\n (click)=\"scroll('right')\"\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n <ng-container *ngIf=\"!outletName\">\n <div\n class=\"nav nav-tabs nav-tabsc8y\"\n role=\"list\"\n #tabsContainer\n >\n <ng-container *ngFor=\"let tab of tabs\">\n <div\n role=\"listitem\"\n [ngClass]=\"{ active: rla?.isActive }\"\n *ngIf=\"!tab.component && !tab.hide\"\n >\n <a\n [routerLink]=\"tab.path\"\n routerLinkActive\n [queryParamsHandling]=\"'preserve'\"\n #rla=\"routerLinkActive\"\n [ngClass]=\"{ 'p-r-24': !tab.template && tab?.badge }\"\n (click)=\"tab.onSelect?.emit()\"\n >\n <i\n [c8yIcon]=\"tab?.icon\"\n *ngIf=\"!tab.template && tab?.icon\"\n ></i>\n <span\n title=\"{{ tab?.label | translate }}\"\n *ngIf=\"!tab.template && !tab.component\"\n >\n {{ tab?.label | translate }}\n </span>\n <span\n class=\"dashboard-template-marker\"\n tooltip=\"{{ tab?.tooltipText | translate }}\"\n placement=\"bottom\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n [delay]=\"300\"\n *ngIf=\"!tab.template && tab?.badge\"\n ></span>\n </a>\n <ng-container *ngTemplateOutlet=\"tab.template; injector: tab.injector\"></ng-container>\n </div>\n <ng-container *ngComponentOutlet=\"tab.component; injector: tab.injector\"></ng-container>\n </ng-container>\n </div>\n </ng-container>\n\n <div\n class=\"component-tabs nav nav-tabs nav-tabsc8y\"\n *ngIf=\"outletName\"\n #tabsContainer\n >\n <ng-container *ngFor=\"let tab of tabs; trackBy: trackByPath\">\n <div\n [ngClass]=\"{ active: tab.isActive }\"\n [routerLink]=\"tab.path\"\n [routerLinkActive]=\"tab.path ? 'active' : ''\"\n [queryParamsHandling]=\"'preserve'\"\n >\n <a\n class=\"pointer\"\n title=\"{{ tab?.label | translate }}\"\n (click)=\"tab.onSelect?.emit()\"\n >\n <i [c8yIcon]=\"tab?.icon\"></i>\n <span>\n {{ tab?.label | translate }}\n </span>\n </a>\n </div>\n </ng-container>\n </div>\n</div>\n\n<div\n class=\"visible-xs mobile-tabs\"\n *ngIf=\"!outletName\"\n>\n <ul class=\"list-unstyled d-flex m-b-0\">\n <li class=\"c8y-select-wrapper flex-grow\">\n <select\n (change)=\"navigateTo($event.target.value)\"\n #select\n >\n <ng-container *ngFor=\"let tab of tabs\">\n <option\n *ngIf=\"!tab.component\"\n [value]=\"tab.path\"\n [selected]=\"isActive(tab)\"\n >\n {{ tab?.label | translate }}\n </option>\n </ng-container>\n </select>\n </li>\n <ng-container *ngFor=\"let tab of tabs\">\n <ng-container *ngIf=\"tab.component\">\n <ng-container\n *c8yComponentOutlet=\"tab.component; environmentInjector: tab.injector\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </ul>\n</div>\n", dependencies: [{ kind: "directive", type: i4.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i6.C8yComponentOutlet, selector: "[c8yComponentOutlet]", inputs: ["c8yComponentOutlet", "c8yComponentOutletInjector", "c8yComponentOutletEnvironmentInjector"] }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: i7.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: i8.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: TabsOutletComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-tabs-outlet,c8y-ui-tabs', template: "<div\n class=\"tabContainer\"\n [ngClass]=\"{ 'hidden-xs': !outletName }\"\n #container\n>\n <button\n class=\"tabs-slider tabs-slider-left\"\n title=\"{{ 'Scroll to the left' | translate }}\"\n type=\"button\"\n #left\n *ngIf=\"showLeft || showRight\"\n [disabled]=\"!showLeft\"\n (click)=\"scroll('left')\"\n >\n <i [c8yIcon]=\"'chevron-left'\"></i>\n </button>\n <button\n class=\"tabs-slider tabs-slider-right\"\n title=\"{{ 'Scroll to the right' | translate }}\"\n type=\"button\"\n #right\n *ngIf=\"showRight || showLeft\"\n [disabled]=\"!showRight\"\n (click)=\"scroll('right')\"\n >\n <i [c8yIcon]=\"'chevron-right'\"></i>\n </button>\n <ng-container *ngIf=\"!outletName\">\n <div\n class=\"nav nav-tabs nav-tabsc8y\"\n role=\"list\"\n #tabsContainer\n >\n <ng-container *ngFor=\"let tab of tabs\">\n <div\n role=\"listitem\"\n [ngClass]=\"{ active: rla?.isActive }\"\n *ngIf=\"!tab.component && !tab.hide\"\n >\n <a\n [routerLink]=\"tab.path\"\n routerLinkActive\n [queryParamsHandling]=\"'preserve'\"\n #rla=\"routerLinkActive\"\n [ngClass]=\"{ 'p-r-24': !tab.template && tab?.badge }\"\n (click)=\"tab.onSelect?.emit()\"\n >\n <i\n [c8yIcon]=\"tab?.icon\"\n *ngIf=\"!tab.template && tab?.icon\"\n ></i>\n <span\n title=\"{{ tab?.label | translate }}\"\n *ngIf=\"!tab.template && !tab.component\"\n >\n {{ tab?.label | translate }}\n </span>\n <span\n class=\"dashboard-template-marker\"\n tooltip=\"{{ tab?.tooltipText | translate }}\"\n placement=\"bottom\"\n container=\"body\"\n [adaptivePosition]=\"false\"\n [delay]=\"300\"\n *ngIf=\"!tab.template && tab?.badge\"\n ></span>\n </a>\n <ng-container *ngTemplateOutlet=\"tab.template; injector: tab.injector\"></ng-container>\n </div>\n <ng-container *ngComponentOutlet=\"tab.component; injector: tab.injector\"></ng-container>\n </ng-container>\n </div>\n </ng-container>\n\n <div\n class=\"component-tabs nav nav-tabs nav-tabsc8y\"\n *ngIf=\"outletName\"\n #tabsContainer\n >\n <ng-container *ngFor=\"let tab of tabs; trackBy: trackByPath\">\n <div\n [ngClass]=\"{ active: tab.isActive }\"\n [routerLink]=\"tab.path\"\n [routerLinkActive]=\"tab.path ? 'active' : ''\"\n [queryParamsHandling]=\"'preserve'\"\n >\n <a\n class=\"pointer\"\n title=\"{{ tab?.label | translate }}\"\n (click)=\"tab.onSelect?.emit()\"\n >\n <i [c8yIcon]=\"tab?.icon\"></i>\n <span>\n {{ tab?.label | translate }}\n </span>\n </a>\n </div>\n </ng-container>\n </div>\n</div>\n\n<div\n class=\"visible-xs mobile-tabs\"\n *ngIf=\"!outletName\"\n>\n <ul class=\"list-unstyled d-flex m-b-0\">\n <li class=\"c8y-select-wrapper flex-grow\">\n <select\n (change)=\"navigateTo($event.target.value)\"\n #select\n >\n <ng-container *ngFor=\"let tab of tabs\">\n <option\n *ngIf=\"!tab.component\"\n [value]=\"tab.path\"\n [selected]=\"isActive(tab)\"\n >\n {{ tab?.label | translate }}\n </option>\n </ng-container>\n </select>\n </li>\n <ng-container *ngFor=\"let tab of tabs\">\n <ng-container *ngIf=\"tab.component\">\n <ng-container\n *c8yComponentOutlet=\"tab.component; environmentInjector: tab.injector\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </ul>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Router }, { type: i0.Renderer2 }, { type: i2.HeaderService }, { type: i3.TabsService }, { type: i1.ActivatedRoute }], propDecorators: { tabs: [{
type: Input
}], orientation: [{
type: Input
}], navigatorOpen: [{
type: Input
}], outletName: [{
type: Input
}], context: [{
type: Input
}], openFirstTab: [{
type: Input
}], hasHeader: [{
type: Input
}, {
type: HostBinding,
args: ['class.has-header']
}], isNavigatorOpen: [{
type: HostBinding,
args: ['class.navigator-open']
}], hasTabs: [{
type: HostBinding,
args: ['class.page-tabs']
}], isHorizontal: [{
type: HostBinding,
args: ['class.page-tabs-horizontal']
}], isVertical: [{
type: HostBinding,
args: ['class.page-tabs-vertical']
}], container: [{
type: ViewChild,
args: ['container', { read: ElementRef, static: true }]
}], tabsContainer: [{
type: ViewChild,
args: ['tabsContainer', { read: ElementRef, static: false }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFicy1vdXRsZXQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vY29yZS90YWJzL3RhYnMtb3V0bGV0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uL2NvcmUvdGFicy90YWJzLW91dGxldC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsU0FBUyxFQUNULEtBQUssRUFDTCxXQUFXLEVBQ1gsU0FBUyxFQUNULFVBQVUsRUFDVixTQUFTLEVBS1YsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLGVBQWUsRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDcEYsT0FBTyxFQUFFLGNBQWMsRUFBb0IsTUFBTSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFM0UsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQzs7Ozs7Ozs7OztBQU03QyxNQUFNLE9BQU8sbUJBQW1CO0lBa0I5QixJQUF5QyxlQUFlO1FBQ3RELE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQztJQUM1QixDQUFDO0lBQ0QsSUFBb0MsT0FBTztRQUN6QyxJQUFJLFdBQVcsR0FBRyxFQUFFLENBQUM7UUFDckIsSUFBSSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDZCxXQUFXLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxLQUFLLElBQUksQ0FBQyxDQUFDO1FBQzNELENBQUM7UUFDRCxPQUFPLFdBQVcsQ0FBQyxNQUFNLEdBQUcs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QWpLVSxtQkFBbUI7bUdBQW5CLG1CQUFtQixxa0JBa0NFLFVBQVUsdUhBQ04sVUFBVSxrREMxRGhELHMwSEFtSUE7OzRGRDVHYSxtQkFBbUI7a0JBSi9CLFNBQVM7K0JBQ0UsNkJBQTZCO3NMQUk5QixJQUFJO3NCQUFaLEtBQUs7Z0JBQ0csV0FBVztzQkFBbkIsS0FBSztnQkFDRyxhQUFhO3NCQUFyQixLQUFLO2dCQUNHLFVBQVU7c0JBQWxCLEtBQUs7Z0JBQ0csT0FBTztzQkFBZixLQUFLO2dCQUNHLFlBQVk7c0JBQXBCLEtBQUs7Z0JBSW9DLFNBQVM7c0JBQWxELEtBQUs7O3NCQUFJLFdBQVc7dUJBQUMsa0JBQWtCO2dCQVFDLGVBQWU7c0JBQXZELFdBQVc7dUJBQUMsc0JBQXNCO2dCQUdDLE9BQU87c0JBQTFDLFdBQVc7dUJBQUMsaUJBQWlCO2dCQU9pQixZQUFZO3NCQUExRCxXQUFXO3VCQUFDLDRCQUE0QjtnQkFHSSxVQUFVO3NCQUF0RCxXQUFXO3VCQUFDLDBCQUEwQjtnQkFHNkIsU0FBUztzQkFBNUUsU0FBUzt1QkFBQyxXQUFXLEVBQUUsRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUU7Z0JBRWxELGFBQWE7c0JBRHBCLFNBQVM7dUJBQUMsZUFBZSxFQUFFLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ29tcG9uZW50LFxuICBJbnB1dCxcbiAgSG9zdEJpbmRpbmcsXG4gIFJlbmRlcmVyMixcbiAgRWxlbWVudFJlZixcbiAgVmlld0NoaWxkLFxuICBPbkRlc3Ryb3ksXG4gIEFmdGVyQ29udGVudEluaXQsXG4gIE9uQ2hhbmdlcyxcbiAgU2ltcGxlQ2hhbmdlc1xufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IGRlYm91bmNlVGltZSwgbWFwLCB0YWtlLCB0YWtlVW50aWwgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBmcm9tRXZlbnRQYXR0ZXJuLCBmcm9tRXZlbnQsIG1lcmdlLCBCZWhhdmlvclN1YmplY3QsIFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEFjdGl2YXRlZFJvdXRlLCBOYXZpZ2F0aW9uRXh0cmFzLCBSb3V0ZXIgfSBmcm9tICdAYW5ndWxhci9yb3V0ZXInO1xuaW1wb3J0IHsgVGFiIH0gZnJvbSAnLi90YWIubW9kZWwnO1xuaW1wb3J0IHsgSGVhZGVyU2VydmljZSB9IGZyb20gJy4uL2hlYWRlci9oZWFkZXIuc2VydmljZSc7XG5pbXBvcnQgeyBUYWJzU2VydmljZSB9IGZyb20gJy4vdGFicy5zZXJ2aWNlJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYzh5LXRhYnMtb3V0bGV0LGM4eS11aS10YWJzJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3RhYnMtb3V0bGV0LmNvbXBvbmVudC5odG1sJ1xufSlcbmV4cG9ydCBjbGFzcyBUYWJzT3V0bGV0Q29tcG9uZW50IGltcGxlbWVudHMgT25EZXN0cm95LCBBZnRlckNvbnRlbnRJbml0LCBPbkNoYW5nZXMge1xuICBASW5wdXQoKSB0YWJzOiBUYWJbXSA9IFtdO1xuICBASW5wdXQoKSBvcmllbnRhdGlvbjogJ3ZlcnRpY2FsJyB8ICdob3Jpem9udGFsJyA9ICd2ZXJ0aWNhbCc7XG4gIEBJbnB1dCgpIG5hdmlnYXRvck9wZW4gPSBmYWxzZTtcbiAgQElucHV0KCkgb3V0bGV0TmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBASW5wdXQoKSBjb250ZXh0OiBhbnk7XG4gIEBJbnB1dCgpIG9wZW5GaXJzdFRhYiA9IHRydWU7XG4gIC8qKlxuICAgKiBJZGVudGlmaWVzIGlmIHRoZSBjdXJyZW50IHZpZXcgaGFzIGEgaGVhZGVyIGVsZW1lbnQuXG4gICAqL1xuICBASW5wdXQoKSBASG9zdEJpbmRpbmcoJ2NsYXNzLmhhcy1oZWFkZXInKSBoYXNIZWFkZXIgPSBmYWxzZTtcblxuICBzaG93TGVmdCA9IGZhbHNlO1xuICBzaG93UmlnaHQgPSBmYWxzZTtcbiAgZXh0cmFzOiBOYXZpZ2F0aW9uRXh0cmFzID0ge1xuICAgIHF1ZXJ5UGFyYW1zSGFuZGxpbmc6ICdwcmVzZXJ2ZScsXG4gICAgcmVsYXRpdmVUbzogdGhpcy5hY3RpdmF0ZWRSb3V0ZVxuICB9O1xuICBASG9zdEJpbmRpbmcoJ2NsYXNzLm5hdmlnYXRvci1vcGVuJykgZ2V0IGlzTmF2aWdhdG9yT3BlbigpIHtcbiAgICByZXR1cm4gdGhpcy5uYXZpZ2F0b3JPcGVuO1xuICB9XG4gIEBIb3N0QmluZGluZygnY2xhc3MucGFnZS10YWJzJykgZ2V0IGhhc1RhYnMoKSB7XG4gICAgbGV0IHZpc2libGVUYWJzID0gW107XG4gICAgaWYgKHRoaXMudGFicykge1xuICAgICAgdmlzaWJsZVRhYnMgPSB0aGlzLnRhYnMuZmlsdGVyKHRhYiA9PiB0YWIuaGlkZSAhPT0gdHJ1ZSk7XG4gICAgfVxuICAgIHJldHVybiB2aXNpYmxlVGFicy5sZW5ndGggPiAxIHx8IHZpc2libGVUYWJzLmZpbmQodGFiID0+IHRhYi5zaG93QWx3YXlzKTtcbiAgfVxuICBASG9zdEJpbmRpbmcoJ2NsYXNzLnBhZ2UtdGFicy1ob3Jpem9udGFsJykgZ2V0IGlzSG9yaXpvbnRhbCgpIHtcbiAgICByZXR1cm4gdGhpcy5vcmllbnRhdGlvbiA9PT0gJ2hvcml6b250YWwnO1xuICB9XG4gIEBIb3N0QmluZGluZygnY2xhc3MucGFnZS10YWJzLXZlcnRpY2FsJykgZ2V0IGlzVmVydGljYWwoKSB7XG4gICAgcmV0dXJuIHRoaXMub3JpZW50YXRpb24gIT09ICdob3Jpem9udGFsJztcbiAgfVxuICBAVmlld0NoaWxkKCdjb250YWluZXInLCB7IHJlYWQ6IEVsZW1lbnRSZWYsIHN0YXRpYzogdHJ1ZSB9KSBwcml2YXRlIGNvbnRhaW5lcjogRWxlbWVudFJlZjtcbiAgQFZpZXdDaGlsZCgndGFic0NvbnRhaW5lcicsIHsgcmVhZDogRWxlbWVudFJlZiwgc3RhdGljOiBmYWxzZSB9KVxuICBwcml2YXRlIHRhYnNDb250YWluZXI6IEVsZW1lbnRSZWY7XG4gIHByaXZhdGUgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICBwcml2YXRlIHRhYnNDaGFuZ2VzJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8VGFiW10+KFtdKTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJvdXRlcjogUm91dGVyLFxuICAgIHByaXZhdGUgcmVuZGVyZXI6IFJlbmRlcmVyMixcbiAgICBwcml2YXRlIGhlYWRlcjogSGVhZGVyU2VydmljZSxcbiAgICBwcml2YXRlIHRhYnNTZXJ2aWNlOiBUYWJzU2VydmljZSxcbiAgICBwcml2YXRlIGFjdGl2YXRlZFJvdXRlOiBBY3RpdmF0ZWRSb3V0ZVxuICApIHt9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMub3V0bGV0TmFtZSAmJiB0aGlzLnRhYnMubGVuZ3RoID09PSAwKSB7XG4gICAgICBjb25zdCB0YWJzU3Vic2NyaXB0aW9uJCA9IHRoaXMudGFic1NlcnZpY2UuaXRlbXMkLnBpcGUoXG4gICAgICAgIG1hcCh0YWJzID0+IHRhYnMuZmlsdGVyKHRhYiA9PiB0YWIudGFic091dGxldCA9PT0gdGhpcy5vdXRsZXROYW1lKSksXG4gICAgICAgIHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKVxuICAgICAgKTtcblxuICAgICAgdGFic1N1YnNjcmlwdGlvbiQuc3Vic2NyaWJlKHRhYnMgPT4ge1xuICAgICAgICB0aGlzLnRhYnMgPSB0YWJzO1xuICAgICAgfSk7XG5cbiAgICAgIHRhYnNTdWJzY3JpcHRpb24kLnBpcGUodGFrZSgxKSkuc3Vic2NyaWJlKHRhYnMgPT4ge1xuICAgICAgICBpZiAodGhpcy5vcGVuRmlyc3RUYWIgJiYgdGFicy5sZW5ndGgpIHtcbiAgICAgICAgICB0aGlzLm5hdmlnYXRlVG8odGFic1swXS5wYXRoKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSBlbHNlIGlmICh0aGlzLm9wZW5GaXJzdFRhYiAmJiB0aGlzLnRhYnMubGVuZ3RoKSB7XG4gICAgICBjb25zdCBtYXRjaGluZ1RhYiA9IHRoaXMudGFicy5maW5kKHRhYiA9PiB0aGlzLmlzQWN0aXZlKHRhYikpO1xuICAgICAgLy8gb25seSBuYXZpZ2F0ZSB0byBmaXJzdCB0YWIgaWYgbm8gdGFiIGlzIGFjdGl2ZVxuICAgICAgaWYgKCFtYXRjaGluZ1RhYikge1xuICAgICAgICB0aGlzLm5hdmlnYXRlVG8odGhpcy50YWJzWzBdLnBhdGgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICBpZiAoY2hhbmdlcy50YWJzKSB7XG4gICAgICB0aGlzLnRhYnMgPSB0aGlzLnRhYnMuZmlsdGVyKHRhYiA9PiB0YWIudGFic091dGxldCA9PT0gdGhpcy5vdXRsZXROYW1lKTtcbiAgICAgIHRoaXMudGFic0NoYW5nZXMkLm5leHQodGhpcy50YWJzKTtcbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLmRlc3Ryb3kkLm5leHQoKTtcbiAgICB0aGlzLmRlc3Ryb3kkLmNvbXBsZXRlKCk7XG4gIH1cblxuICBuZ0FmdGVyQ29udGVudEluaXQoKSB7XG4gICAgaWYgKHRoaXMub3JpZW50YXRpb24gPT09ICdob3Jpem9udGFsJykge1xuICAgICAgdGhpcy5zY3JvbGxTdWJzY3JpYmUoKTtcbiAgICB9XG4gIH1cblxuICB0cmFja0J5UGF0aChpbmRleCwgdGFiOiBUYWIpIHtcbiAgICByZXR1cm4gdGFiLnBhdGggPyB0YWIucGF0aCA6IGluZGV4O1xuICB9XG5cbiAgaXNBY3RpdmUodGFiOiBUYWIpIHtcbiAgICBpZiAodGFiICYmIHRhYi5wYXRoKSB7XG4gICAgICBjb25zdCB0cmVlID0gdGhpcy5yb3V0ZXIuY3JlYXRlVXJsVHJlZSh0eXBlb2YgdGFiLnBhdGggPT09ICdzdHJpbmcnID8gW3RhYi5wYXRoXSA6IHRhYi5wYXRoKTtcbiAgICAgIHJldHVybiB0aGlzLnJvdXRlci5pc0FjdGl2ZSh0cmVlLCB7XG4gICAgICAgIHBhdGhzOiAnc3Vic2V0JyxcbiAgICAgICAgcXVlcnlQYXJhbXM6ICdzdWJzZXQnLFxuICAgICAgICBmcmFnbWVudDogJ2lnbm9yZWQnLFxuICAgICAgICBtYXRyaXhQYXJhbXM6ICdpZ25vcmVkJ1xuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBuYXZpZ2F0ZVRvKHBhdGg6IHN0cmluZyB8IGFueVtdKSB7XG4gICAgdGhpcy5oZWFkZXIudG9nZ2xlKCk7XG4gICAgaWYgKHR5cGVvZiBwYXRoID09PSAnc3RyaW5nJykge1xuICAgICAgdGhpcy5yb3V0ZXIubmF2aWdhdGUoW3BhdGhdLCB0aGlzLmV4dHJhcyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMucm91dGVyLm5hdmlnYXRlKHBhdGgsIHRoaXMuZXh0cmFzKTtcbiAgfVxuXG4gIHNjcm9sbChkaXJlY3Rpb246ICdyaWdodCcgfCAnbGVmdCcgPSAncmlnaHQnKSB7XG4gICAgY29uc3QgeyB3aWR0aFNjcm9sbCwgd2lkdGgsIHNjcm9sbExlZnQgfSA9IHRoaXMuZ2V0RGltZW5zaW9ucygpO1xuICAgIGNvbnN0IG11bHRpcGxpZXIgPSBkaXJlY3Rpb24gPT09ICdsZWZ0JyA/IC0xIDogMTtcblxuICAgIGxldCB0YXJnZXRTY3JvbGwgPSBzY3JvbGxMZWZ0ICsgd2lkdGggKiBtdWx0aXBsaWVyO1xuICAgIGlmICh0YXJnZXRTY3JvbGwgPCAwKSB7XG4gICAgICB0YXJnZXRTY3JvbGwgPSAwO1xuICAgIH1cbiAgICBpZiAodGFyZ2V0U2Nyb2xsID4gd2lkdGhTY3JvbGwgLSB3aWR0aCkge1xuICAgICAgdGFyZ2V0U2Nyb2xsID0gd2lkdGhTY3JvbGwgLSB3aWR0aDtcbiAgICB9XG4gICAgdGhpcy5jb250YWluZXIubmF0aXZlRWxlbWVudC5zY3JvbGxMZWZ0ID0gdGFyZ2V0U2Nyb2xsO1xuICB9XG5cbiAgcHJpdmF0ZSBzY3JvbGxTdWJzY3JpYmUoKSB7XG4gICAgbGV0IHVubGlzdGVuO1xuICAgIG1lcmdlKFxuICAgICAgdGhpcy50YWJzQ2hhbmdlcyQsXG4gICAgICBmcm9tRXZlbnQodGhpcy5jb250YWluZXIubmF0aXZlRWxlbWVudCwgJ3Njcm9sbCcpLFxuICAgICAgZnJvbUV2ZW50UGF0dGVybihcbiAgICAgICAgKGhhbmRsZXI6IGFueSkgPT4ge1xuICAgICAgICAgIHVubGlzdGVuID0gdGhpcy5yZW5kZXJlci5saXN0ZW4oJ3dpbmRvdycsICdyZXNpemUnLCBoYW5kbGVyKTtcbiAgICAgICAgfSxcbiAgICAgICAgKCkgPT4gdW5saXN0ZW4oKVxuICAgICAgKVxuICAgIClcbiAgICAgIC5waXBlKGRlYm91bmNlVGltZSgyNTApLCB0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAuc3Vic2NyaWJlKCgpID0+IHRoaXMudXBkYXRlU2Nyb2xsKCkpO1xuICB9XG5cbiAgcHJpdmF0ZSB1cGRhdGVTY3JvbGwoKSB7XG4gICAgY29uc3QgeyB3aWR0aFNjcm9sbCwgd2lkdGgsIHNjcm9sbExlZnQgfSA9IHRoaXMuZ2V0RGltZW5zaW9ucygpO1xuICAgIHRoaXMuc2hvd0xlZnQgPSAhIXNjcm9sbExlZnQ7XG4gICAgdGhpcy5zaG93UmlnaHQgPSB3aWR0aFNjcm9sbCA+IHdpZHRoICsgc2Nyb2xsTGVmdDtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0RGltZW5zaW9ucygpIHtcbiAgICBjb25zdCBlbFRhYnMgPSB0aGlzLnRhYnNDb250YWluZXIubmF0aXZlRWxlbWVudDtcbiAgICBjb25zdCBlbCA9IHRoaXMuY29udGFpbmVyLm5hdGl2ZUVsZW1lbnQ7XG4gICAgY29uc3Qgd2lkdGhTY3JvbGwgPSBlbFRhYnMuc2Nyb2xsV2lkdGg7XG4gICAgY29uc3Qgd2lkdGggPSBlbC5jbGllbnRXaWR0aDtcbiAgICBjb25zdCBzY3JvbGxMZWZ0ID0gZWwuc2Nyb2xsTGVmdDtcbiAgICByZXR1cm4geyB3aWR0aFNjcm9sbCwgd2lkdGgsIHNjcm9sbExlZnQgfTtcbiAgfVxufVxuIiwiPGRpdlxuICBjbGFzcz1cInRhYkNvbnRhaW5lclwiXG4gIFtuZ0NsYXNzXT1cInsgJ2hpZGRlbi14cyc6ICFvdXRsZXROYW1lIH1cIlxuICAjY29udGFpbmVyXG4+XG4gIDxidXR0b25cbiAgICBjbGFzcz1cInRhYnMtc2xpZGVyIHRhYnMtc2xpZGVyLWxlZnRcIlxuICAgIHRpdGxlPVwie3sgJ1Njcm9sbCB0byB0aGUgbGVmdCcgfCB0cmFuc2xhdGUgfX1cIlxuICAgIHR5cGU9XCJidXR0b25cIlxuICAgICNsZWZ0XG4gICAgKm5nSWY9XCJzaG93TGVmdCB8fCBzaG93UmlnaHRcIlxuICAgIFtkaXNhYmxlZF09XCIhc2hvd0xlZnRcIlxuICAgIChjbGljayk9XCJzY3JvbGwoJ2xlZnQnKVwiXG4gID5cbiAgICA8aSBbYzh5SWNvbl09XCInY2hldnJvbi1sZWZ0J1wiPjwvaT5cbiAgPC9idXR0b24+XG4gIDxidXR0b25cbiAgICBjbGFzcz1cInRhYnMtc2xpZGVyIHRhYnMtc2xpZGVyLXJpZ2h0XCJcbiAgICB0aXRsZT1cInt7ICdTY3JvbGwgdG8gdGhlIHJpZ2h0JyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgdHlwZT1cImJ1dHRvblwiXG4gICAgI3JpZ2h0XG4gICAgKm5nSWY9XCJzaG93UmlnaHQgfHwgc2hvd0xlZnRcIlxuICAgIFtkaXNhYmxlZF09XCIhc2hvd1JpZ2h0XCJcbiAgICAoY2xpY2spPVwic2Nyb2xsKCdyaWdodCcpXCJcbiAgPlxuICAgIDxpIFtjOHlJY29uXT1cIidjaGV2cm9uLXJpZ2h0J1wiPjwvaT5cbiAgPC9idXR0b24+XG4gIDxuZy1jb250YWluZXIgKm5nSWY9XCIhb3V0bGV0TmFtZVwiPlxuICAgIDxkaXZcbiAgICAgIGNsYXNzPVwibmF2IG5hdi10YWJzIG5hdi10YWJzYzh5XCJcbiAgICAgIHJvbGU9XCJsaXN0XCJcbiAgICAgICN0YWJzQ29udGFpbmVyXG4gICAgPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdGb3I9XCJsZXQgdGFiIG9mIHRhYnNcIj5cbiAgICAgICAgPGRpdlxuICAgICAgICAgIHJvbGU9XCJsaXN0aXRlbVwiXG4gICAgICAgICAgW25nQ2xhc3NdPVwieyBhY3RpdmU6IHJsYT8uaXNBY3RpdmUgfVwiXG4gICAgICAgICAgKm5nSWY9XCIhdGFiLmNvbXBvbmVudCAmJiAhdGFiLmhpZGVcIlxuICAgICAgICA+XG4gICAgICAgICAgPGFcbiAgICAgICAgICAgIFtyb3V0ZXJMaW5rXT1cInRhYi5wYXRoXCJcbiAgICAgICAgICAgIHJvdXRlckxpbmtBY3RpdmVcbiAgICAgICAgICAgIFtxdWVyeVBhcmFtc0hhbmRsaW5nXT1cIidwcmVzZXJ2ZSdcIlxuICAgICAgICAgICAgI3JsYT1cInJvdXRlckxpbmtBY3RpdmVcIlxuICAgICAgICAgICAgW25nQ2xhc3NdPVwieyAncC1yLTI0JzogIXRhYi50ZW1wbGF0ZSAmJiB0YWI/LmJhZGdlIH1cIlxuICAgICAgICAgICAgKGNsaWNrKT1cInRhYi5vblNlbGVjdD8uZW1pdCgpXCJcbiAgICAgICAgICA+XG4gICAgICAgICAgICA8aVxuICAgICAgICAgICAgICBbYzh5SWNvbl09XCJ0YWI/Lmljb25cIlxuICAgICAgICAgICAgICAqbmdJZj1cIiF0YWIudGVtcGxhdGUgJiYgdGFiPy5pY29uXCJcbiAgICAgICAgICAgID48L2k+XG4gICAgICAgICAgICA8c3BhblxuICAgICAgICAgICAgICB0aXRsZT1cInt7IHRhYj8ubGFiZWwgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgICAgICAqbmdJZj1cIiF0YWIudGVtcGxhdGUgJiYgIXRhYi5jb21wb25lbnRcIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICB7eyB0YWI/LmxhYmVsIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICA8c3BhblxuICAgICAgICAgICAgICBjbGFzcz1cImRhc2hib2FyZC10ZW1wbGF0ZS1tYXJrZXJcIlxuICAgICAgICAgICAgICB0b29sdGlwPVwie3sgdGFiPy50b29sdGlwVGV4dCB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgICAgIHBsYWNlbWVudD1cImJvdHRvbVwiXG4gICAgICAgICAgICAgIGNvbnRhaW5lcj1cImJvZHlcIlxuICAgICAgICAgICAgICBbYWRhcHRpdmVQb3NpdGlvbl09XCJmYWxzZVwiXG4gICAgICAgICAgICAgIFtkZWxheV09XCIzMDBcIlxuICAgICAgICAgICAgICAqbmdJZj1cIiF0YWIudGVtcGxhdGUgJiYgdGFiPy5iYWRnZVwiXG4gICAgICAgICAgICA+PC9zcGFuPlxuICAgICAgICAgIDwvYT5cbiAgICAgICAgICA8bmctY29udGFpbmVyICpuZ1RlbXBsYXRlT3V0bGV0PVwidGFiLnRlbXBsYXRlOyBpbmplY3RvcjogdGFiLmluamVjdG9yXCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8bmctY29udGFpbmVyICpuZ0NvbXBvbmVudE91dGxldD1cInRhYi5jb21wb25lbnQ7IGluamVjdG9yOiB0YWIuaW5qZWN0b3JcIj48L25nLWNvbnRhaW5lcj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvZGl2PlxuICA8L25nLWNvbnRhaW5lcj5cblxuICA8ZGl2XG4gICAgY2xhc3M9XCJjb21wb25lbnQtdGFicyBuYXYgbmF2LXRhYnMgbmF2LXRhYnNjOHlcIlxuICAgICpuZ0lmPVwib3V0bGV0TmFtZVwiXG4gICAgI3RhYnNDb250YWluZXJcbiAgPlxuICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IHRhYiBvZiB0YWJzOyB0cmFja0J5OiB0cmFja0J5UGF0aFwiPlxuICAgICAgPGRpdlxuICAgICAgICBbbmdDbGFzc109XCJ7IGFjdGl2ZTogdGFiLmlzQWN0aXZlIH1cIlxuICAgICAgICBbcm91dGVyTGlua109XCJ0YWIucGF0aFwiXG4gICAgICAgIFtyb3V0ZXJMaW5rQWN0aXZlXT1cInRhYi5wYXRoID8gJ2FjdGl2ZScgOiAnJ1wiXG4gICAgICAgIFtxdWVyeVBhcmFtc0hhbmRsaW5nXT1cIidwcmVzZXJ2ZSdcIlxuICAgICAgPlxuICAgICAgICA8YVxuICAgICAgICAgIGNsYXNzPVwicG9pbnRlclwiXG4gICAgICAgICAgdGl0bGU9XCJ7eyB0YWI/LmxhYmVsIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgICAoY2xpY2spPVwidGFiLm9uU2VsZWN0Py5lbWl0KClcIlxuICAgICAgICA+XG4gICAgICAgICAgPGkgW2M4eUljb25dPVwidGFiPy5pY29uXCI+PC9pPlxuICAgICAgICAgIDxzcGFuPlxuICAgICAgICAgICAge3sgdGFiPy5sYWJlbCB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgPC9hPlxuICAgICAgPC9kaXY+XG4gICAgPC9uZy1jb250YWluZXI+XG4gIDwvZGl2PlxuPC9kaXY+XG5cbjxkaXZcbiAgY2xhc3M9XCJ2aXNpYmxlLXhzIG1vYmlsZS10YWJzXCJcbiAgKm5nSWY9XCIhb3V0bGV0TmFtZVwiXG4+XG4gIDx1bCBjbGFzcz1cImxpc3QtdW5zdHlsZWQgZC1mbGV4IG0tYi0wXCI+XG4gICAgPGxpIGNsYXNzPVwiYzh5LXNlbGVjdC13cmFwcGVyIGZsZXgtZ3Jvd1wiPlxuICAgICAgPHNlbGVjdFxuICAgICAgICAoY2hhbmdlKT1cIm5hdmlnYXRlVG8oJGV2ZW50LnRhcmdldC52YWx1ZSlcIlxuICAgICAgICAjc2VsZWN0XG4gICAgICA+XG4gICAgICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IHRhYiBvZiB0YWJzXCI+XG4gICAgICAgICAgPG9wdGlvblxuICAgICAgICAgICAgKm5nSWY9XCIhdGFiLmNvbXBvbmVudFwiXG4gICAgICAgICAgICBbdmFsdWVdPVwidGFiLnBhdGhcIlxuICAgICAgICAgICAgW3NlbGVjdGVkXT1cImlzQWN0aXZlKHRhYilcIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIHt7IHRhYj8ubGFiZWwgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICA8L29wdGlvbj5cbiAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICA8L3NlbGVjdD5cbiAgICA8L2xpPlxuICAgIDxuZy1jb250YWluZXIgKm5nRm9yPVwibGV0IHRhYiBvZiB0YWJzXCI+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwidGFiLmNvbXBvbmVudFwiPlxuICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgKmM4eUNvbXBvbmVudE91dGxldD1cInRhYi5jb21wb25lbnQ7IGVudmlyb25tZW50SW5qZWN0b3I6IHRhYi5pbmplY3RvclwiXG4gICAgICAgID48L25nLWNvbnRhaW5lcj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvbmctY29udGFpbmVyPlxuICA8L3VsPlxuPC9kaXY+XG4iXX0=