UNPKG

gentics-ui-core

Version:

This is the common core framework for the Gentics CMS and Mesh UI, and other Angular applications.

259 lines 52 kB
import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, Output, QueryList, ViewChild, ViewChildren, ChangeDetectorRef } from '@angular/core'; import { RouterLinkWithHref } from '@angular/router'; import { BehaviorSubject, Subscription, timer } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { UserAgentRef } from '../modal/user-agent-ref'; import * as i0 from "@angular/core"; import * as i1 from "../modal/user-agent-ref"; import * as i2 from "@angular/common"; import * as i3 from "@angular/router"; import * as i4 from "../icon/icon.directive"; /** The width configured in the .ellipsis CSS class. */ const ELLIPSIS_WIDTH = 13; /** * A Breadcrumbs navigation component. * * ```html * <gtx-breadcrumbs></gtx-breadcrumbs> * ``` */ export class Breadcrumbs { constructor(changeDetector, elementRef, userAgent) { this.changeDetector = changeDetector; this.elementRef = elementRef; this.userAgent = userAgent; /** * Fires when a link is clicked */ this.linkClick = new EventEmitter(); /** * Fires when the expand button is clicked */ this.multilineExpandedChange = new EventEmitter(); this.isMultiline = false; this.isMultilineExpanded = false; this.isDisabled = false; this.isOverflowing = false; this.showArrow = false; this.subscriptions = new Subscription(); this.resizeEvents = new BehaviorSubject(null); this.preventClicksWhenDisabled = (ev) => { if (this.isDisabled) { let target = ev.target; if (target.tagName.toLowerCase() === 'a' && target.classList.contains('breadcrumb')) { ev.preventDefault(); ev.stopImmediatePropagation(); } } }; } /** * If true the first folder and all the folder names from the end of the breadcrumbs, which fit into one line are shown * and an ellipsis in between. */ get multiline() { return this.isMultiline; } set multiline(val) { this.isMultiline = val != undefined && val !== false; } /** * If true the breadcrumbs are always expanded */ get multilineExpanded() { return this.isMultilineExpanded; } set multilineExpanded(val) { this.isMultilineExpanded = val != undefined && val !== false; } /** * Controls whether the navigation is disabled. */ get disabled() { return this.isDisabled; } set disabled(val) { this.isDisabled = val != undefined && val !== false; } ngAfterViewInit() { let element = this.elementRef.nativeElement; if (element) { // Listen in the "capture" phase to prevent routerLinks when disabled element.firstElementChild.addEventListener('click', this.preventClicksWhenDisabled, true); element.style.setProperty('--collapsedColor', this.collapsedColor); } const timerSub = timer(500, 500) .subscribe(() => this.resizeEvents.next()); this.subscriptions.add(timerSub); this.setUpResizeSub(); this.preventDisabledRouterLinks(); this.routerLinkChildren.changes.subscribe(() => this.preventDisabledRouterLinks()); this.resizeEvents.next(null); } ngOnChanges(changes) { if (changes['links'] || changes['routerLinks']) { let allLinks = (this.links || []).concat(this.routerLinks || []); this.backLink = allLinks[allLinks.length - 2]; this.resizeEvents.next(null); } if (changes['multiline'] || changes['multilineExpanded']) { this.resizeEvents.next(null); } } ngOnDestroy() { let element = this.elementRef.nativeElement; element.firstElementChild.removeEventListener('click', this.preventClicksWhenDisabled, true); this.subscriptions.unsubscribe(); } onLinkClicked(link, event) { if (this.isDisabled) { event.preventDefault(); event.stopImmediatePropagation(); } else { this.linkClick.emit(link); } } toggleMultilineExpanded() { this.multilineExpanded = !this.multilineExpanded; this.multilineExpandedChange.emit(this.multilineExpanded); this.resizeEvents.next(null); this.changeDetector.markForCheck(); } setUpResizeSub() { let prevLinks; let prevRouterLinks; let prevIsExpanded; let prevNavWidth = -1; const resizeSub = this.resizeEvents .pipe(debounceTime(5)) .subscribe(() => { if (!this.lastPart || !this.navWrapper) { return; } // If neither the links, nor isMultilineExpanded, nor the navWrapper element's clientWidth has changed, we don't need to do anything. const currNavWidth = this.navWrapper.nativeElement.clientWidth; if (prevLinks === this.links && prevRouterLinks === this.routerLinks && prevIsExpanded === this.isMultilineExpanded && prevNavWidth === currNavWidth) { return; } prevLinks = this.links; prevRouterLinks = this.routerLinks; prevIsExpanded = this.isMultilineExpanded; prevNavWidth = currNavWidth; const elements = this.lastPart.nativeElement.querySelectorAll('a.breadcrumb'); if (elements.length > 0) { const firstOffsetBottom = elements[0].offsetTop + elements[0].offsetHeight; const lastOffsetBottom = elements[elements.length - 1].offsetTop + elements[elements.length - 1].offsetHeight; this.showArrow = firstOffsetBottom !== lastOffsetBottom; } else { this.showArrow = false; } this.shortenTexts(); this.changeDetector.markForCheck(); }); this.subscriptions.add(resizeSub); } shortenTexts() { const navWrapper = this.navWrapper.nativeElement; const lastPart = this.lastPart.nativeElement; const innerElements = lastPart.querySelectorAll('a.breadcrumb'); const defaultElements = this.getCuttableBreadcrumbsTexts(); this.isOverflowing = false; // Reset all elements to their default states. const offset = this.multilineExpanded ? 0 : 1; for (let i = 0; i < innerElements.length; i++) { const innerElement = innerElements[i]; innerElement.classList.remove('without'); innerElement.classList.remove('hidden'); innerElement.textContent = defaultElements[i + offset]; } if (this.multilineExpanded) { return; } for (let i = 0; i < innerElements.length; ++i) { const innerElement = innerElements[i]; while (lastPart.offsetLeft + lastPart.scrollWidth + ELLIPSIS_WIDTH > navWrapper.clientWidth) { this.isOverflowing = true; if (innerElement.textContent.length === 0) { innerElement.classList.add('hidden'); const nextInnerElement = innerElements[i + 1]; if (nextInnerElement) { nextInnerElement.classList.add('without'); } break; } else { innerElement.textContent = innerElement.textContent.substring(1); } } } } getCuttableBreadcrumbsTexts() { let defaultBreadcrumbs = []; if (this.links) { for (let i = 0; i < this.links.length; i++) { defaultBreadcrumbs.push(this.links[i].text); } } if (this.routerLinks) { for (let i = 0; i < this.routerLinks.length; i++) { defaultBreadcrumbs.push(this.routerLinks[i].text); } } return defaultBreadcrumbs; } onResize(event) { this.resizeEvents.next(null); } /** * Workaround/Hack for the native angular "RouterLink" having no way to disable navigation on click. */ preventDisabledRouterLinks() { const thisComponent = this; const createsCompileErrorIfRouterLinkAPIChanges = 'onClick'; for (const link of this.routerLinkChildren.filter(link => !link.hasOwnProperty('onClick'))) { const originalOnClick = link.onClick; link.onClick = function interceptedOnClick(...args) { if (thisComponent.isDisabled) { return true; } else { return originalOnClick.apply(this, args); } }; } } } /** @nocollapse */ Breadcrumbs.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Breadcrumbs, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i1.UserAgentRef }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ Breadcrumbs.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.8", type: Breadcrumbs, selector: "gtx-breadcrumbs", inputs: { links: "links", routerLinks: "routerLinks", collapsedColor: "collapsedColor", multiline: "multiline", multilineExpanded: "multilineExpanded", disabled: "disabled" }, outputs: { linkClick: "linkClick", multilineExpandedChange: "multilineExpandedChange" }, viewQueries: [{ propertyName: "navWrapper", first: true, predicate: ["navWrapper"], descendants: true }, { propertyName: "lastPart", first: true, predicate: ["lastPart"], descendants: true }, { propertyName: "routerLinkChildren", predicate: RouterLinkWithHref, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<nav [class.disabled]=\"isDisabled\" [class.multiline]=\"multiline\">\n <div #navWrapper class=\"nav-wrapper\" [class.is-overflowing]=\"isOverflowing\" [class.multilineExpanded]=\"multilineExpanded\" [class.multiline]=\"multiline\" (window:resize)=\"onResize($event)\">\n <div class=\"inner-wrapper\">\n <a class=\"back-button\" *ngIf=\"backLink && backLink.route\"\n (click)=\"onLinkClicked(backLink, $event)\"\n [routerLink]=\"backLink.route\"\n [title]=\"backLink.text\"></a>\n <a class=\"back-button\" *ngIf=\"backLink && !backLink.route\"\n (click)=\"onLinkClicked(backLink, $event)\"\n [attr.href]=\"isDisabled ? null : backLink?.href\"\n [title]=\"backLink.text\"></a>\n\n <div class=\"other-content\">\n <ng-content></ng-content>\n </div>\n\n <ng-template [ngIf]=\"links\">\n <a *ngIf=\"!multilineExpanded && links[0]\" class=\"breadcrumb\"\n [attr.href]=\"isDisabled ? null : links[0].href\"\n (click)=\"onLinkClicked(links[0], $event)\"\n [title]=\"links[0].tooltip || links[0].text\"\n >{{ links[0].text }}</a>\n <div class=\"ellipsis\" *ngIf=\"isOverflowing && !multilineExpanded\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n ...\n </div>\n <div #lastPart class=\"lastPart\">\n <ng-container *ngFor=\"let link of links; let i = index\">\n <a *ngIf=\"!multilineExpanded && i > 0\"\n class=\"breadcrumb last\"\n [attr.href]=\"isDisabled ? null : link?.href\"\n (click)=\"onLinkClicked(link, $event)\"\n [title]=\"link.tooltip || link.text\"\n >{{ link.text }}</a>\n <a *ngIf=\"multilineExpanded\"\n class=\"breadcrumb last\"\n [attr.href]=\"isDisabled ? null : link?.href\"\n (click)=\"onLinkClicked(link, $event)\"\n [title]=\"link.tooltip || link.text\"\n >{{ link.text }}\n </a>\n </ng-container>\n <span *ngIf=\"multiline && multilineExpanded && !isOverflowing && showArrow\" class=\"back_arrow\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n <icon>arrow_back</icon>\n </span>\n </div>\n </ng-template>\n\n <ng-template [ngIf]=\"routerLinks\">\n <a *ngIf=\"!multilineExpanded && routerLinks[0]\" class=\"breadcrumb\"\n [routerLink]=\"routerLinks[0].route\"\n (click)=\"onLinkClicked(routerLinks[0], $event)\"\n [title]=\"routerLinks[0].tooltip || routerLinks[0].text\"\n >{{ routerLinks[0].text }}</a>\n <div class=\"ellipsis\" *ngIf=\"isOverflowing && !multilineExpanded\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n ...\n </div>\n <div #lastPart class=\"lastPart\">\n <ng-container *ngFor=\"let routerLink of routerLinks; let i = index\">\n <a *ngIf=\"!multilineExpanded && i > 0\"\n class=\"breadcrumb last\"\n [routerLink]=\"routerLink.route\"\n (click)=\"onLinkClicked(routerLink, $event)\"\n [title]=\"routerLink.tooltip || routerLink.text\"\n >{{ routerLink.text }}</a>\n <a *ngIf=\"multilineExpanded\"\n class=\"breadcrumb last\"\n [routerLink]=\"routerLink.route\"\n (click)=\"onLinkClicked(routerLink, $event)\"\n [title]=\"routerLink.tooltip || routerLink.text\"\n >{{ routerLink.text }}\n </a>\n </ng-container>\n <span *ngIf=\"multiline && multilineExpanded && !isOverflowing && showArrow\" class=\"back_arrow\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n <icon>arrow_back</icon>\n </span>\n </div>\n </ng-template>\n </div>\n </div>\n</nav>\n", directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.RouterLinkWithHref, selector: "a[routerLink],area[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "preserveFragment", "skipLocationChange", "replaceUrl", "state", "relativeTo", "routerLink"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i4.Icon, selector: "icon" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: Breadcrumbs, decorators: [{ type: Component, args: [{ selector: 'gtx-breadcrumbs', changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav [class.disabled]=\"isDisabled\" [class.multiline]=\"multiline\">\n <div #navWrapper class=\"nav-wrapper\" [class.is-overflowing]=\"isOverflowing\" [class.multilineExpanded]=\"multilineExpanded\" [class.multiline]=\"multiline\" (window:resize)=\"onResize($event)\">\n <div class=\"inner-wrapper\">\n <a class=\"back-button\" *ngIf=\"backLink && backLink.route\"\n (click)=\"onLinkClicked(backLink, $event)\"\n [routerLink]=\"backLink.route\"\n [title]=\"backLink.text\"></a>\n <a class=\"back-button\" *ngIf=\"backLink && !backLink.route\"\n (click)=\"onLinkClicked(backLink, $event)\"\n [attr.href]=\"isDisabled ? null : backLink?.href\"\n [title]=\"backLink.text\"></a>\n\n <div class=\"other-content\">\n <ng-content></ng-content>\n </div>\n\n <ng-template [ngIf]=\"links\">\n <a *ngIf=\"!multilineExpanded && links[0]\" class=\"breadcrumb\"\n [attr.href]=\"isDisabled ? null : links[0].href\"\n (click)=\"onLinkClicked(links[0], $event)\"\n [title]=\"links[0].tooltip || links[0].text\"\n >{{ links[0].text }}</a>\n <div class=\"ellipsis\" *ngIf=\"isOverflowing && !multilineExpanded\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n ...\n </div>\n <div #lastPart class=\"lastPart\">\n <ng-container *ngFor=\"let link of links; let i = index\">\n <a *ngIf=\"!multilineExpanded && i > 0\"\n class=\"breadcrumb last\"\n [attr.href]=\"isDisabled ? null : link?.href\"\n (click)=\"onLinkClicked(link, $event)\"\n [title]=\"link.tooltip || link.text\"\n >{{ link.text }}</a>\n <a *ngIf=\"multilineExpanded\"\n class=\"breadcrumb last\"\n [attr.href]=\"isDisabled ? null : link?.href\"\n (click)=\"onLinkClicked(link, $event)\"\n [title]=\"link.tooltip || link.text\"\n >{{ link.text }}\n </a>\n </ng-container>\n <span *ngIf=\"multiline && multilineExpanded && !isOverflowing && showArrow\" class=\"back_arrow\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n <icon>arrow_back</icon>\n </span>\n </div>\n </ng-template>\n\n <ng-template [ngIf]=\"routerLinks\">\n <a *ngIf=\"!multilineExpanded && routerLinks[0]\" class=\"breadcrumb\"\n [routerLink]=\"routerLinks[0].route\"\n (click)=\"onLinkClicked(routerLinks[0], $event)\"\n [title]=\"routerLinks[0].tooltip || routerLinks[0].text\"\n >{{ routerLinks[0].text }}</a>\n <div class=\"ellipsis\" *ngIf=\"isOverflowing && !multilineExpanded\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n ...\n </div>\n <div #lastPart class=\"lastPart\">\n <ng-container *ngFor=\"let routerLink of routerLinks; let i = index\">\n <a *ngIf=\"!multilineExpanded && i > 0\"\n class=\"breadcrumb last\"\n [routerLink]=\"routerLink.route\"\n (click)=\"onLinkClicked(routerLink, $event)\"\n [title]=\"routerLink.tooltip || routerLink.text\"\n >{{ routerLink.text }}</a>\n <a *ngIf=\"multilineExpanded\"\n class=\"breadcrumb last\"\n [routerLink]=\"routerLink.route\"\n (click)=\"onLinkClicked(routerLink, $event)\"\n [title]=\"routerLink.tooltip || routerLink.text\"\n >{{ routerLink.text }}\n </a>\n </ng-container>\n <span *ngIf=\"multiline && multilineExpanded && !isOverflowing && showArrow\" class=\"back_arrow\" [class.multilineExpanded]=\"multilineExpanded\" (click)=\"toggleMultilineExpanded()\">\n <icon>arrow_back</icon>\n </span>\n </div>\n </ng-template>\n </div>\n </div>\n</nav>\n" }] }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i1.UserAgentRef }]; }, propDecorators: { links: [{ type: Input }], routerLinks: [{ type: Input }], collapsedColor: [{ type: Input }], multiline: [{ type: Input }], multilineExpanded: [{ type: Input }], disabled: [{ type: Input }], linkClick: [{ type: Output }], multilineExpandedChange: [{ type: Output }], routerLinkChildren: [{ type: ViewChildren, args: [RouterLinkWithHref] }], navWrapper: [{ type: ViewChild, args: ['navWrapper'] }], lastPart: [{ type: ViewChild, args: ['lastPart'] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJlYWRjcnVtYnMuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvYnJlYWRjcnVtYnMvYnJlYWRjcnVtYnMuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvYnJlYWRjcnVtYnMvYnJlYWRjcnVtYnMudHBsLmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNILHVCQUF1QixFQUN2QixTQUFTLEVBQ1QsVUFBVSxFQUNWLFlBQVksRUFDWixLQUFLLEVBR0wsTUFBTSxFQUNOLFNBQVMsRUFFVCxTQUFTLEVBQ1QsWUFBWSxFQUNaLGlCQUFpQixFQUVwQixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUMsa0JBQWtCLEVBQUMsTUFBTSxpQkFBaUIsQ0FBQztBQUNuRCxPQUFPLEVBQUMsZUFBZSxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQUMsTUFBTSxNQUFNLENBQUM7QUFDMUQsT0FBTyxFQUFDLFlBQVksRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBRTVDLE9BQU8sRUFBQyxZQUFZLEVBQUMsTUFBTSx5QkFBeUIsQ0FBQzs7Ozs7O0FBaUJyRCx1REFBdUQ7QUFDdkQsTUFBTSxjQUFjLEdBQUcsRUFBRSxDQUFDO0FBRTFCOzs7Ozs7R0FNRztBQU1ILE1BQU0sT0FBTyxXQUFXO0lBNkVwQixZQUFvQixjQUFpQyxFQUNqQyxVQUFzQixFQUN0QixTQUF1QjtRQUZ2QixtQkFBYyxHQUFkLGNBQWMsQ0FBbUI7UUFDakMsZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQUN0QixjQUFTLEdBQVQsU0FBUyxDQUFjO1FBL0IzQzs7V0FFRztRQUNPLGNBQVMsR0FBRyxJQUFJLFlBQVksRUFBMkMsQ0FBQztRQUVsRjs7V0FFRztRQUNPLDRCQUF1QixHQUFHLElBQUksWUFBWSxFQUFXLENBQUM7UUFFaEUsZ0JBQVcsR0FBWSxLQUFLLENBQUM7UUFDN0Is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SUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDO2lCQUM1QztZQUNMLENBQUMsQ0FBQztTQUNMO0lBQ0wsQ0FBQzs7MkhBOVBRLFdBQVc7K0dBQVgsV0FBVyx5aEJBa0VOLGtCQUFrQixxRUN0SHBDLGtwSkFnRkE7MkZENUJhLFdBQVc7a0JBTHZCLFNBQVM7K0JBQ0ksaUJBQWlCLG1CQUVWLHVCQUF1QixDQUFDLE1BQU07NEpBT3RDLEtBQUs7c0JBQWIsS0FBSztnQkFLRyxXQUFXO3NCQUFuQixLQUFLO2dCQUtHLGNBQWM7c0JBQXRCLEtBQUs7Z0JBTU8sU0FBUztzQkFBckIsS0FBSztnQkFVTyxpQkFBaUI7c0JBQTdCLEtBQUs7Z0JBVU8sUUFBUTtzQkFBcEIsS0FBSztnQkFVSSxTQUFTO3NCQUFsQixNQUFNO2dCQUtHLHVCQUF1QjtzQkFBaEMsTUFBTTtnQkFVMkIsa0JBQWtCO3NCQUFuRCxZQUFZO3VCQUFDLGtCQUFrQjtnQkFHaEMsVUFBVTtzQkFEVCxTQUFTO3VCQUFDLFlBQVk7Z0JBSXZCLFFBQVE7c0JBRFAsU0FBUzt1QkFBQyxVQUFVIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgICBDb21wb25lbnQsXG4gICAgRWxlbWVudFJlZixcbiAgICBFdmVudEVtaXR0ZXIsXG4gICAgSW5wdXQsXG4gICAgT25DaGFuZ2VzLFxuICAgIE9uRGVzdHJveSxcbiAgICBPdXRwdXQsXG4gICAgUXVlcnlMaXN0LFxuICAgIFNpbXBsZUNoYW5nZXMsXG4gICAgVmlld0NoaWxkLFxuICAgIFZpZXdDaGlsZHJlbixcbiAgICBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICBBZnRlclZpZXdJbml0XG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtSb3V0ZXJMaW5rV2l0aEhyZWZ9IGZyb20gJ0Bhbmd1bGFyL3JvdXRlcic7XG5pbXBvcnQge0JlaGF2aW9yU3ViamVjdCwgU3Vic2NyaXB0aW9uLCB0aW1lcn0gZnJvbSAncnhqcyc7XG5pbXBvcnQge2RlYm91bmNlVGltZX0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG5pbXBvcnQge1VzZXJBZ2VudFJlZn0gZnJvbSAnLi4vbW9kYWwvdXNlci1hZ2VudC1yZWYnO1xuXG5leHBvcnQgaW50ZXJmYWNlIElCcmVhZGNydW1iTGluayB7XG4gICAgaHJlZj86IHN0cmluZztcbiAgICByb3V0ZT86IGFueTtcbiAgICB0ZXh0OiBzdHJpbmc7XG4gICAgdG9vbHRpcD86IHN0cmluZztcbiAgICBba2V5OiBzdHJpbmddOiBhbnk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSUJyZWFkY3J1bWJSb3V0ZXJMaW5rIHtcbiAgICByb3V0ZTogYW55W107XG4gICAgdGV4dDogc3RyaW5nO1xuICAgIHRvb2x0aXA/OiBzdHJpbmc7XG4gICAgW2tleTogc3RyaW5nXTogYW55O1xufVxuXG4vKiogVGhlIHdpZHRoIGNvbmZpZ3VyZWQgaW4gdGhlIC5lbGxpcHNpcyBDU1MgY2xhc3MuICovXG5jb25zdCBFTExJUFNJU19XSURUSCA9IDEzO1xuXG4vKipcbiAqIEEgQnJlYWRjcnVtYnMgbmF2aWdhdGlvbiBjb21wb25lbnQuXG4gKlxuICogYGBgaHRtbFxuICogPGd0eC1icmVhZGNydW1icz48L2d0eC1icmVhZGNydW1icz5cbiAqIGBgYFxuICovXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ2d0eC1icmVhZGNydW1icycsXG4gICAgdGVtcGxhdGVVcmw6ICcuL2JyZWFkY3J1bWJzLnRwbC5odG1sJyxcbiAgICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaFxufSlcbmV4cG9ydCBjbGFzcyBCcmVhZGNydW1icyBpbXBsZW1lbnRzIE9uQ2hhbmdlcywgT25EZXN0cm95LCBBZnRlclZpZXdJbml0IHtcblxuICAgIC8qKlxuICAgICAqIEEgbGlzdCBvZiBsaW5rcyB0byBkaXNwbGF5XG4gICAgICovXG4gICAgQElucHV0KCkgbGlua3M6IElCcmVhZGNydW1iTGlua1tdO1xuXG4gICAgLyoqXG4gICAgICogQSBsaXN0IG9mIFJvdXRlckxpbmtzIHRvIGRpc3BsYXlcbiAgICAgKi9cbiAgICBASW5wdXQoKSByb3V0ZXJMaW5rczogSUJyZWFkY3J1bWJSb3V0ZXJMaW5rW107XG5cbiAgICAvKipcbiAgICAgKiBBIGNvbG9yIHRoYXQgaXMgdXNlZCBmb3IgY29sbGFwc2VkIHN0YXRlIGJhY2tncm91bmQuXG4gICAgICovXG4gICAgQElucHV0KCkgY29sbGFwc2VkQ29sb3I6IHN0cmluZztcblxuICAgIC8qKlxuICAgICAqIElmIHRydWUgdGhlIGZpcnN0IGZvbGRlciBhbmQgYWxsIHRoZSBmb2xkZXIgbmFtZXMgZnJvbSB0aGUgZW5kIG9mIHRoZSBicmVhZGNydW1icywgd2hpY2ggZml0IGludG8gb25lIGxpbmUgYXJlIHNob3duXG4gICAgICogYW5kIGFuIGVsbGlwc2lzIGluIGJldHdlZW4uXG4gICAgICovXG4gICAgQElucHV0KCkgZ2V0IG11bHRpbGluZSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuaXNNdWx0aWxpbmU7XG4gICAgfVxuICAgIHNldCBtdWx0aWxpbmUodmFsOiBib29sZWFuKSB7XG4gICAgICAgIHRoaXMuaXNNdWx0aWxpbmUgPSB2YWwgIT0gdW5kZWZpbmVkICYmIHZhbCAhPT0gZmFsc2U7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSWYgdHJ1ZSB0aGUgYnJlYWRjcnVtYnMgYXJlIGFsd2F5cyBleHBhbmRlZFxuICAgICAqL1xuICAgIEBJbnB1dCgpIGdldCBtdWx0aWxpbmVFeHBhbmRlZCgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuaXNNdWx0aWxpbmVFeHBhbmRlZDtcbiAgICB9XG4gICAgc2V0IG11bHRpbGluZUV4cGFuZGVkKHZhbDogYm9vbGVhbikge1xuICAgICAgICB0aGlzLmlzTXVsdGlsaW5lRXhwYW5kZWQgPSB2YWwgIT0gdW5kZWZpbmVkICYmIHZhbCAhPT0gZmFsc2U7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ29udHJvbHMgd2hldGhlciB0aGUgbmF2aWdhdGlvbiBpcyBkaXNhYmxlZC5cbiAgICAgKi9cbiAgICBASW5wdXQoKSBnZXQgZGlzYWJsZWQoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0aGlzLmlzRGlzYWJsZWQ7XG4gICAgfVxuICAgIHNldCBkaXNhYmxlZCh2YWw6IGJvb2xlYW4pIHtcbiAgICAgICAgdGhpcy5pc0Rpc2FibGVkID0gdmFsICE9IHVuZGVmaW5lZCAmJiB2YWwgIT09IGZhbHNlO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZpcmVzIHdoZW4gYSBsaW5rIGlzIGNsaWNrZWRcbiAgICAgKi9cbiAgICBAT3V0cHV0KCkgbGlua0NsaWNrID0gbmV3IEV2ZW50RW1pdHRlcjxJQnJlYWRjcnVtYkxpbmsgfCBJQnJlYWRjcnVtYlJvdXRlckxpbms+KCk7XG5cbiAgICAvKipcbiAgICAgKiBGaXJlcyB3aGVuIHRoZSBleHBhbmQgYnV0dG9uIGlzIGNsaWNrZWRcbiAgICAgKi9cbiAgICBAT3V0cHV0KCkgbXVsdGlsaW5lRXhwYW5kZWRDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPGJvb2xlYW4+KCk7XG5cbiAgICBpc011bHRpbGluZTogYm9vbGVhbiA9IGZhbHNlO1xuICAgIGlzTXVsdGlsaW5lRXhwYW5kZWQ6IGJvb2xlYW4gPSBmYWxzZTtcbiAgICBpc0Rpc2FibGVkOiBib29sZWFuID0gZmFsc2U7XG4gICAgaXNPdmVyZmxvd2luZzogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgc2hvd0Fycm93OiBib29sZWFuID0gZmFsc2U7XG5cbiAgICBiYWNrTGluazogSUJyZWFkY3J1bWJMaW5rIHwgSUJyZWFkY3J1bWJSb3V0ZXJMaW5rO1xuICAgIEBWaWV3Q2hpbGRyZW4oUm91dGVyTGlua1dpdGhIcmVmKSByb3V0ZXJMaW5rQ2hpbGRyZW46IFF1ZXJ5TGlzdDxSb3V0ZXJMaW5rV2l0aEhyZWY+O1xuXG4gICAgQFZpZXdDaGlsZCgnbmF2V3JhcHBlcicpXG4gICAgbmF2V3JhcHBlcjogRWxlbWVudFJlZjtcblxuICAgIEBWaWV3Q2hpbGQoJ2xhc3RQYXJ0JylcbiAgICBsYXN0UGFydDogRWxlbWVudFJlZjtcblxuICAgIHByaXZhdGUgc3Vic2NyaXB0aW9ucyA9IG5ldyBTdWJzY3JpcHRpb24oKTtcbiAgICBwcml2YXRlIHJlc2l6ZUV2ZW50cyA9IG5ldyBCZWhhdmlvclN1YmplY3Q8dm9pZD4obnVsbCk7XG5cbiAgICBjb25zdHJ1Y3Rvcihwcml2YXRlIGNoYW5nZURldGVjdG9yOiBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICAgICAgICAgICAgICBwcml2YXRlIGVsZW1lbnRSZWY6IEVsZW1lbnRSZWYsXG4gICAgICAgICAgICAgICAgcHJpdmF0ZSB1c2VyQWdlbnQ6IFVzZXJBZ2VudFJlZikgeyB9XG5cbiAgICBuZ0FmdGVyVmlld0luaXQoKTogdm9pZCB7XG4gICAgICAgIGxldCBlbGVtZW50OiBIVE1MRWxlbWVudCA9IHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50O1xuICAgICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICAgICAgLy8gTGlzdGVuIGluIHRoZSBcImNhcHR1cmVcIiBwaGFzZSB0byBwcmV2ZW50IHJvdXRlckxpbmtzIHdoZW4gZGlzYWJsZWRcbiAgICAgICAgICAgIGVsZW1lbnQuZmlyc3RFbGVtZW50Q2hpbGQuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCB0aGlzLnByZXZlbnRDbGlja3NXaGVuRGlzYWJsZWQsIHRydWUpO1xuICAgICAgICAgICAgZWxlbWVudC5zdHlsZS5zZXRQcm9wZXJ0eSgnLS1jb2xsYXBzZWRDb2xvcicsIHRoaXMuY29sbGFwc2VkQ29sb3IpO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgdGltZXJTdWIgPSB0aW1lcig1MDAsIDUwMClcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKCkgPT4gdGhpcy5yZXNpemVFdmVudHMubmV4dCgpKTtcbiAgICAgICAgdGhpcy5zdWJzY3JpcHRpb25zLmFkZCh0aW1lclN1Yik7XG4gICAgICAgIHRoaXMuc2V0VXBSZXNpemVTdWIoKTtcblxuICAgICAgICB0aGlzLnByZXZlbnREaXNhYmxlZFJvdXRlckxpbmtzKCk7XG4gICAgICAgIHRoaXMucm91dGVyTGlua0NoaWxkcmVuLmNoYW5nZXMuc3Vic2NyaWJlKCgpID0+IHRoaXMucHJldmVudERpc2FibGVkUm91dGVyTGlua3MoKSk7XG4gICAgICAgIHRoaXMucmVzaXplRXZlbnRzLm5leHQobnVsbCk7XG4gICAgfVxuXG4gICAgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgICAgICBpZiAoY2hhbmdlc1snbGlua3MnXSB8fCBjaGFuZ2VzWydyb3V0ZXJMaW5rcyddKSB7XG4gICAgICAgICAgICBsZXQgYWxsTGlua3MgPSAodGhpcy5saW5rcyB8fCBbXSkuY29uY2F0KHRoaXMucm91dGVyTGlua3MgfHwgW10pO1xuICAgICAgICAgICAgdGhpcy5iYWNrTGluayA9IGFsbExpbmtzW2FsbExpbmtzLmxlbmd0aCAtIDJdO1xuICAgICAgICAgICAgdGhpcy5yZXNpemVFdmVudHMubmV4dChudWxsKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoY2hhbmdlc1snbXVsdGlsaW5lJ10gfHwgY2hhbmdlc1snbXVsdGlsaW5lRXhwYW5kZWQnXSkge1xuICAgICAgICAgICAgdGhpcy5yZXNpemVFdmVudHMubmV4dChudWxsKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICBsZXQgZWxlbWVudDogSFRNTEVsZW1lbnQgPSB0aGlzLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudDtcbiAgICAgICAgZWxlbWVudC5maXJzdEVsZW1lbnRDaGlsZC5yZW1vdmVFdmVudExpc3RlbmVyKCdjbGljaycsIHRoaXMucHJldmVudENsaWNrc1doZW5EaXNhYmxlZCwgdHJ1ZSk7XG4gICAgICAgIHRoaXMuc3Vic2NyaXB0aW9ucy51bnN1YnNjcmliZSgpO1xuICAgIH1cblxuICAgIG9uTGlua0NsaWNrZWQobGluazogSUJyZWFkY3J1bWJMaW5rIHwgSUJyZWFkY3J1bWJSb3V0ZXJMaW5rLCBldmVudDogRXZlbnQpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMuaXNEaXNhYmxlZCkge1xuICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgIGV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5saW5rQ2xpY2suZW1pdChsaW5rKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHRvZ2dsZU11bHRpbGluZUV4cGFuZGVkKCk6IHZvaWQge1xuICAgICAgICB0aGlzLm11bHRpbGluZUV4cGFuZGVkID0gIXRoaXMubXVsdGlsaW5lRXhwYW5kZWQ7XG4gICAgICAgIHRoaXMubXVsdGlsaW5lRXhwYW5kZWRDaGFuZ2UuZW1pdCh0aGlzLm11bHRpbGluZUV4cGFuZGVkKTtcbiAgICAgICAgdGhpcy5yZXNpemVFdmVudHMubmV4dChudWxsKTtcbiAgICAgICAgdGhpcy5jaGFuZ2VEZXRlY3Rvci5tYXJrRm9yQ2hlY2soKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNldFVwUmVzaXplU3ViKCkge1xuICAgICAgICBsZXQgcHJldkxpbmtzOiBJQnJlYWRjcnVtYkxpbmtbXTtcbiAgICAgICAgbGV0IHByZXZSb3V0ZXJMaW5rczogSUJyZWFkY3J1bWJSb3V0ZXJMaW5rW107XG4gICAgICAgIGxldCBwcmV2SXNFeHBhbmRlZDogYm9vbGVhbjtcbiAgICAgICAgbGV0IHByZXZOYXZXaWR0aCA9IC0xO1xuXG4gICAgICAgIGNvbnN0IHJlc2l6ZVN1YiA9IHRoaXMucmVzaXplRXZlbnRzXG4gICAgICAgICAgICAucGlwZShkZWJvdW5jZVRpbWUoNSkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoIXRoaXMubGFzdFBhcnQgfHwgIXRoaXMubmF2V3JhcHBlcikge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIElmIG5laXRoZXIgdGhlIGxpbmtzLCBub3IgaXNNdWx0aWxpbmVFeHBhbmRlZCwgbm9yIHRoZSBuYXZXcmFwcGVyIGVsZW1lbnQncyBjbGllbnRXaWR0aCBoYXMgY2hhbmdlZCwgd2UgZG9uJ3QgbmVlZCB0byBkbyBhbnl0aGluZy5cbiAgICAgICAgICAgICAgICBjb25zdCBjdXJyTmF2V2lkdGggPSB0aGlzLm5hdldyYXBwZXIubmF0aXZlRWxlbWVudC5jbGllbnRXaWR0aDtcbiAgICAgICAgICAgICAgICBpZiAocHJldkxpbmtzID09PSB0aGlzLmxpbmtzICYmIHByZXZSb3V0ZXJMaW5rcyA9PT0gdGhpcy5yb3V0ZXJMaW5rcyAmJiBwcmV2SXNFeHBhbmRlZCA9PT0gdGhpcy5pc011bHRpbGluZUV4cGFuZGVkICYmIHByZXZOYXZXaWR0aCA9PT0gY3Vyck5hdldpZHRoKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcHJldkxpbmtzID0gdGhpcy5saW5rcztcbiAgICAgICAgICAgICAgICBwcmV2Um91dGVyTGlua3MgPSB0aGlzLnJvdXRlckxpbmtzO1xuICAgICAgICAgICAgICAgIHByZXZJc0V4cGFuZGVkID0gdGhpcy5pc011bHRpbGluZUV4cGFuZGVkO1xuICAgICAgICAgICAgICAgIHByZXZOYXZXaWR0aCA9IGN1cnJOYXZXaWR0aDtcblxuICAgICAgICAgICAgICAgIGNvbnN0IGVsZW1lbnRzID0gdGhpcy5sYXN0UGFydC5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJ2EuYnJlYWRjcnVtYicpO1xuICAgICAgICAgICAgICAgIGlmIChlbGVtZW50cy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGZpcnN0T2Zmc2V0Qm90dG9tID0gZWxlbWVudHNbMF0ub2Zmc2V0VG9wICsgZWxlbWVudHNbMF0ub2Zmc2V0SGVpZ2h0O1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBsYXN0T2Zmc2V0Qm90dG9tID0gZWxlbWVudHNbZWxlbWVudHMubGVuZ3RoIC0gMV0ub2Zmc2V0VG9wICsgZWxlbWVudHNbZWxlbWVudHMubGVuZ3RoIC0gMV0ub2Zmc2V0SGVpZ2h0O1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnNob3dBcnJvdyA9IGZpcnN0T2Zmc2V0Qm90dG9tICE9PSBsYXN0T2Zmc2V0Qm90dG9tO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2hvd0Fycm93ID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRoaXMuc2hvcnRlblRleHRzKCk7XG4gICAgICAgICAgICAgICAgdGhpcy5jaGFuZ2VEZXRlY3Rvci5tYXJrRm9yQ2hlY2soKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMuc3Vic2NyaXB0aW9ucy5hZGQocmVzaXplU3ViKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNob3J0ZW5UZXh0cygpIHtcbiAgICAgICAgY29uc3QgbmF2V3JhcHBlciA9IHRoaXMubmF2V3JhcHBlci5uYXRpdmVFbGVtZW50IGFzIEhUTUxFbGVtZW50O1xuICAgICAgICBjb25zdCBsYXN0UGFydCA9IHRoaXMubGFzdFBhcnQubmF0aXZlRWxlbWVudCBhcyBIVE1MRWxlbWVudDtcbiAgICAgICAgY29uc3QgaW5uZXJFbGVtZW50cyA9IGxhc3RQYXJ0LnF1ZXJ5U2VsZWN0b3JBbGwoJ2EuYnJlYWRjcnVtYicpO1xuICAgICAgICBjb25zdCBkZWZhdWx0RWxlbWVudHMgPSB0aGlzLmdldEN1dHRhYmxlQnJlYWRjcnVtYnNUZXh0cygpO1xuXG4gICAgICAgIHRoaXMuaXNPdmVyZmxvd2luZyA9IGZhbHNlO1xuXG4gICAgICAgIC8vIFJlc2V0IGFsbCBlbGVtZW50cyB0byB0aGVpciBkZWZhdWx0IHN0YXRlcy5cbiAgICAgICAgY29uc3Qgb2Zmc2V0ID0gdGhpcy5tdWx0aWxpbmVFeHBhbmRlZCA/IDAgOiAxO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGlubmVyRWxlbWVudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGNvbnN0IGlubmVyRWxlbWVudCA9IGlubmVyRWxlbWVudHNbaV07XG4gICAgICAgICAgICBpbm5lckVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgnd2l0aG91dCcpO1xuICAgICAgICAgICAgaW5uZXJFbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpO1xuICAgICAgICAgICAgaW5uZXJFbGVtZW50LnRleHRDb250ZW50ID0gZGVmYXVsdEVsZW1lbnRzW2kgKyBvZmZzZXRdO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMubXVsdGlsaW5lRXhwYW5kZWQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW5uZXJFbGVtZW50cy5sZW5ndGg7ICsraSkge1xuICAgICAgICAgICAgY29uc3QgaW5uZXJFbGVtZW50ID0gaW5uZXJFbGVtZW50c1tpXTtcbiAgICAgICAgICAgIHdoaWxlIChsYXN0UGFydC5vZmZzZXRMZWZ0ICsgbGFzdFBhcnQuc2Nyb2xsV2lkdGggKyBFTExJUFNJU19XSURUSCA+IG5hdldyYXBwZXIuY2xpZW50V2lkdGgpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmlzT3ZlcmZsb3dpbmcgPSB0cnVlO1xuICAgICAgICAgICAgICAgIGlmIChpbm5lckVsZW1lbnQudGV4dENvbnRlbnQubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGlubmVyRWxlbWVudC5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgbmV4dElubmVyRWxlbWVudCA9IGlubmVyRWxlbWVudHNbaSArIDFdO1xuICAgICAgICAgICAgICAgICAgICBpZiAobmV4dElubmVyRWxlbWVudCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgbmV4dElubmVyRWxlbWVudC5jbGFzc0xpc3QuYWRkKCd3aXRob3V0Jyk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgaW5uZXJFbGVtZW50LnRleHRDb250ZW50ID0gaW5uZXJFbGVtZW50LnRleHRDb250ZW50LnN1YnN0cmluZygxKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGdldEN1dHRhYmxlQnJlYWRjcnVtYnNUZXh0cygpOiBzdHJpbmdbXSB7XG4gICAgICAgIGxldCBkZWZhdWx0QnJlYWRjcnVtYnM6IHN0cmluZ1tdID0gW107XG4gICAgICAgIGlmICh0aGlzLmxpbmtzKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMubGlua3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBkZWZhdWx0QnJlYWRjcnVtYnMucHVzaCh0aGlzLmxpbmtzW2ldLnRleHQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnJvdXRlckxpbmtzKSB7XG4gICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMucm91dGVyTGlua3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBkZWZhdWx0QnJlYWRjcnVtYnMucHVzaCh0aGlzLnJvdXRlckxpbmtzW2ldLnRleHQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBkZWZhdWx0QnJlYWRjcnVtYnM7XG4gICAgfVxuXG4gICAgb25SZXNpemUoZXZlbnQ6IGFueSk6IHZvaWQge1xuICAgICAgICB0aGlzLnJlc2l6ZUV2ZW50cy5uZXh0KG51bGwpO1xuICAgIH1cblxuICAgIHByaXZhdGUgcHJldmVudENsaWNrc1doZW5EaXNhYmxlZCA9IChldjogRXZlbnQpOiB2b2lkID0+IHtcbiAgICAgICAgaWYgKHRoaXMuaXNEaXNhYmxlZCkge1xuICAgICAgICAgICAgbGV0IHRhcmdldCA9IGV2LnRhcmdldCBhcyBIVE1MRWxlbWVudDtcbiAgICAgICAgICAgIGlmICh0YXJnZXQudGFnTmFtZS50b0xvd2VyQ2FzZSgpID09PSAnYScgJiYgdGFyZ2V0LmNsYXNzTGlzdC5jb250YWlucygnYnJlYWRjcnVtYicpKSB7XG4gICAgICAgICAgICAgICAgZXYucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICBldi5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFdvcmthcm91bmQvSGFjayBmb3IgdGhlIG5hdGl2ZSBhbmd1bGFyIFwiUm91dGVyTGlua1wiIGhhdmluZyBubyB3YXkgdG8gZGlzYWJsZSBuYXZpZ2F0aW9uIG9uIGNsaWNrLlxuICAgICAqL1xuICAgIHByaXZhdGUgcHJldmVudERpc2FibGVkUm91dGVyTGlua3MoKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IHRoaXNDb21wb25lbnQgPSB0aGlzO1xuICAgICAgICBjb25zdCBjcmVhdGVzQ29tcGlsZUVycm9ySWZSb3V0ZXJMaW5rQVBJQ2hhbmdlczoga2V5b2YgUm91dGVyTGlua1dpdGhIcmVmID0gJ29uQ2xpY2snO1xuXG4gICAgICAgIGZvciAoY29uc3QgbGluayBvZiB0aGlzLnJvdXRlckxpbmtDaGlsZHJlbi5maWx0ZXIobGluayA9PiAhbGluay5oYXNPd25Qcm9wZXJ0eSgnb25DbGljaycpKSkge1xuICAgICAgICAgICAgY29uc3Qgb3JpZ2luYWxPbkNsaWNrID0gbGluay5vbkNsaWNrO1xuICAgICAgICAgICAgbGluay5vbkNsaWNrID0gZnVuY3Rpb24gaW50ZXJjZXB0ZWRPbkNsaWNrKC4uLmFyZ3M6IGFueVtdKTogYm9vbGVhbiB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXNDb21wb25lbnQuaXNEaXNhYmxlZCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gb3JpZ2luYWxPbkNsaWNrLmFwcGx5KHRoaXMsIGFyZ3MpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICB9XG59XG4iLCI8bmF2IFtjbGFzcy5kaXNhYmxlZF09XCJpc0Rpc2FibGVkXCIgW2NsYXNzLm11bHRpbGluZV09XCJtdWx0aWxpbmVcIj5cbiAgICA8ZGl2ICNuYXZXcmFwcGVyIGNsYXNzPVwibmF2LXdyYXBwZXJcIiBbY2xhc3MuaXMtb3ZlcmZsb3dpbmddPVwiaXNPdmVyZmxvd2luZ1wiIFtjbGFzcy5tdWx0aWxpbmVFeHBhbmRlZF09XCJtdWx0aWxpbmVFeHBhbmRlZFwiIFtjbGFzcy5tdWx0aWxpbmVdPVwibXVsdGlsaW5lXCIgKHdpbmRvdzpyZXNpemUpPVwib25SZXNpemUoJGV2ZW50KVwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiaW5uZXItd3JhcHBlclwiPlxuICAgICAgICAgICAgPGEgY2xhc3M9XCJiYWNrLWJ1dHRvblwiICpuZ0lmPVwiYmFja0xpbmsgJiYgYmFja0xpbmsucm91dGVcIlxuICAgICAgICAgICAgICAgIChjbGljayk9XCJvbkxpbmtDbGlja2VkKGJhY2tMaW5rLCAkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICBbcm91dGVyTGlua109XCJiYWNrTGluay5yb3V0ZVwiXG4gICAgICAgICAgICAgICAgW3RpdGxlXT1cImJhY2tMaW5rLnRleHRcIj48L2E+XG4gICAgICAgICAgICA8YSBjbGFzcz1cImJhY2stYnV0dG9uXCIgKm5nSWY9XCJiYWNrTGluayAmJiAhYmFja0xpbmsucm91dGVcIlxuICAgICAgICAgICAgICAgIChjbGljayk9XCJvbkxpbmtDbGlja2VkKGJhY2tMaW5rLCAkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICBbYXR0ci5ocmVmXT1cImlzRGlzYWJsZWQgPyBudWxsIDogYmFja0xpbms/LmhyZWZcIlxuICAgICAgICAgICAgICAgIFt0aXRsZV09XCJiYWNrTGluay50ZXh0XCI+PC9hPlxuXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwib3RoZXItY29udGVudFwiPlxuICAgICAgICAgICAgICAgIDxuZy1jb250ZW50PjwvbmctY29udGVudD5cbiAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICA8bmctdGVtcGxhdGUgW25nSWZdPVwibGlua3NcIj5cbiAgICAgICAgICAgICAgICA8YSAqbmdJZj1cIiFtdWx0aWxpbmVFeHBhbmRlZCAmJiBsaW5rc1swXVwiIGNsYXNzPVwiYnJlYWRjcnVtYlwiXG4gICAgICAgICAgICAgICAgICAgIFthdHRyLmhyZWZdPVwiaXNEaXNhYmxlZCA/IG51bGwgOiBsaW5rc1swXS5ocmVmXCJcbiAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uTGlua0NsaWNrZWQobGlua3NbMF0sICRldmVudClcIlxuICAgICAgICAgICAgICAgICAgICBbdGl0bGVdPVwibGlua3NbMF0udG9vbHRpcCB8fCBsaW5rc1swXS50ZXh0XCJcbiAgICAgICAgICAgICAgICA+e3sgbGlua3NbMF0udGV4dCB9fTwvYT5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZWxsaXBzaXNcIiAqbmdJZj1cImlzT3ZlcmZsb3dpbmcgJiYgIW11bHRpbGluZUV4cGFuZGVkXCIgW2NsYXNzLm11bHRpbGluZUV4cGFuZGVkXT1cIm11bHRpbGluZUV4cGFuZGVkXCIgKGNsaWNrKT1cInRvZ2dsZU11bHRpbGluZUV4cGFuZGVkKClcIj5cbiAgICAgICAgICAgICAgICAgICAgLi4uXG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgPGRpdiAjbGFzdFBhcnQgY2xhc3M9XCJsYXN0UGFydFwiPlxuICAgICAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyICpuZ0Zvcj1cImxldCBsaW5rIG9mIGxpbmtzOyBsZXQgaSA9IGluZGV4XCI+XG4gICAgICAgICAgICAgICAgICAgICAgICA8YSAqbmdJZj1cIiFtdWx0aWxpbmVFeHBhbmRlZCAmJiBpID4gMFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJicmVhZGNydW1iIGxhc3RcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFthdHRyLmhyZWZdPVwiaXNEaXNhYmxlZCA/IG51bGwgOiBsaW5rPy5ocmVmXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwib25MaW5rQ2xpY2tlZChsaW5rLCAkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBbdGl0bGVdPVwibGluay50b29sdGlwIHx8IGxpbmsudGV4dFwiXG4gICAgICAgICAgICAgICAgICAgICAgICA+e3sgbGluay50ZXh0IH19PC9hPlxuICAgICAgICAgICAgICAgICAgICAgICAgPGEgKm5nSWY9XCJtdWx0aWxpbmVFeHBhbmRlZFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJicmVhZGNydW1iIGxhc3RcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFthdHRyLmhyZWZdPVwiaXNEaXNhYmxlZCA/IG51bGwgOiBsaW5rPy5ocmVmXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwib25MaW5rQ2xpY2tlZChsaW5rLCAkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBbdGl0bGVdPVwibGluay50b29sdGlwIHx8IGxpbmsudGV4dFwiXG4gICAgICAgICAgICAgICAgICAgICAgICA+e3sgbGluay50ZXh0IH19XG4gICAgICAgICAgICAgICAgICAgICAgIDwvYT5cbiAgICAgICAgICAgICAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICAgICAgICAgICAgICAgIDxzcGFuICpuZ0lmPVwibXVsdGlsaW5lICYmIG11bHRpbGluZUV4cGFuZGVkICYmICFpc092ZXJmbG93aW5nICYmIHNob3dBcnJvd1wiIGNsYXNzPVwiYmFja19hcnJvd1wiIFtjbGFzcy5tdWx0aWxpbmVFeHBhbmRlZF09XCJtdWx0aWxpbmVFeHBhbmRlZFwiIChjbGljayk9XCJ0b2dnbGVNdWx0aWxpbmVFeHBhbmRlZCgpXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICA8aWNvbj5hcnJvd19iYWNrPC9pY29uPlxuICAgICAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L25nLXRlbXBsYXRlPlxuXG4gICAgICAgICAgICA8bmctdGVtcGxhdGUgW25nSWZdPVwicm91dGVyTGlua3NcIj5cbiAgICAgICAgICAgICAgICA8YSAqbmdJZj1cIiFtdWx0aWxpbmVFeHBhbmRlZCAmJiByb3V0ZXJMaW5rc1swXVwiIGNsYXNzPVwiYnJlYWRjcnVtYlwiXG4gICAgICAgICAgICAgICAgICAgIFtyb3V0ZXJMaW5rXT1cInJvdXRlckxpbmtzWzBdLnJvdXRlXCJcbiAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uTGlua0NsaWNrZWQocm91dGVyTGlua3NbMF0sICRldmVudClcIlxuICAgICAgICAgICAgICAgICAgICBbdGl0bGVdPVwicm91dGVyTGlua3NbMF0udG9vbHRpcCB8fCByb3V0ZXJMaW5rc1swXS50ZXh0XCJcbiAgICAgICAgICAgICAgICA+e3sgcm91dGVyTGlua3NbMF0udGV4dCB9fTwvYT5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZWxsaXBzaXNcIiAqbmdJZj1cImlzT3ZlcmZsb3dpbmcgJiYgIW11bHRpbGluZUV4cGFuZGVkXCIgW2NsYXNzLm11bHRpbGluZUV4