ngx-aside-ease
Version:
ngx-aside-ease is an Angular library that offers a lightweight, performant, and responsive aside panel. This library has multiple options and offers a good user experience.
339 lines • 53.1 kB
JavaScript
import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, ContentChildren, HostBinding, Input, ViewChild, } from '@angular/core';
import { Subject, debounceTime, fromEvent, takeUntil } from 'rxjs';
import { AsideItemDirective } from './item.directive';
import * as i0 from "@angular/core";
import * as i1 from "../internal-aside.service";
import * as i2 from "../aside.service";
import * as i3 from "@angular/router";
import * as i4 from "@angular/common";
export class AsideComponent {
constructor(element, internalAsideService, asideService, router, route, cd) {
this.element = element;
this.internalAsideService = internalAsideService;
this.asideService = asideService;
this.router = router;
this.route = route;
this.cd = cd;
this.minVisible = 30;
this.minWidth = 250;
this.width = 300;
this.maxWidth = '50';
this.responsiveBreakpoint = 800;
this.displayCollapsableIcon = true;
this.asideAnimationTiming = '0.3s ease-out';
this.markerAnimationTiming = '0.3s ease-out';
this.enableResize = true;
this.resizerColor = '#0095be';
this.enableMarker = true;
this.storePreference = true;
this.updateUrl = true;
this.paramUrlName = 'name';
this.userWidth = 0;
this.showCollapsableIcon = false;
this.asideIsOpen = true;
this.responsiveMode = false;
this.asideContentTop = 0;
this.destroy$ = new Subject();
this.asideFullWidthResponsive = false;
this.keepUserNavigationChoice = false;
this.reverse = false;
}
get native() {
return this.element.nativeElement;
}
get asideNative() {
return this.asideWrapper.nativeElement;
}
get resizerNative() {
return this.resizer.nativeElement;
}
get isResponsiveMode() {
return this.responsiveMode;
}
ngOnInit() {
this.internalAsideService.internalOnSelectionChange
.pipe(takeUntil(this.destroy$))
.subscribe((item) => {
const { element, animate } = item;
this.positionMarker(element, animate);
this.updateUrlTabs(element.innerText.toLowerCase());
this.addClassActiveToElement(element);
});
fromEvent(window, 'resize')
.pipe(takeUntil(this.destroy$), debounceTime(300))
.subscribe(() => {
this.applyResponsive();
});
}
/**
* Retrieve the previous stored preference or take the defined width.
* Initialise CSS variables.
*/
ngAfterViewInit() {
this.userWidth = this.width;
if (this.storePreference) {
this.userWidth =
parseFloat(localStorage.getItem('user-width') || '') || this.width;
}
this.updateMinWidthPercentDiff();
this.native.style.setProperty('--min-width', `${this.minWidth}px`);
this.native.style.setProperty('--min-width-visible', `${this.minVisible}px`);
this.native.style.setProperty('--width', `${this.userWidth}px`);
this.native.style.setProperty('--max-width', `${this.maxWidth}vw`);
this.applyResponsive(false);
this.activateParamsUrl();
this.selectDefaultItem();
if (this.enableResize)
this.resize();
}
/**
* Navigation by URL.
* Not through the Angular API for synchronous reason. Give priority to the user choice over the default active item.
* Apply 100 ms delay, a custom font can be not fully loaded.
*/
activateParamsUrl() {
if (!this.updateUrl)
return;
const currentUrl = window.location.href;
const url = new URL(currentUrl);
const params = new URLSearchParams(url.search);
const name = params.get(this.paramUrlName)?.toLowerCase().trim() || '';
const active = this.findTabToActivate(name);
if (active instanceof HTMLElement) {
this.timeoutID = window.setTimeout(() => {
this.internalAsideService.internalOnSelectionChange.next({
element: active,
animate: false,
});
}, 100);
this.asideService.onSelectionChange.next(active);
this.keepUserNavigationChoice = true;
}
}
/**
* Navigation by URL.
* Find the corresponding item and activate it.
*/
findTabToActivate(name) {
if (!name)
return;
const cleanedName = name.toLowerCase().trim();
for (const item of this.items) {
if (item.disable)
return null;
const text = item.native.innerText.toLowerCase();
if (cleanedName === text) {
return item.native;
}
}
return null;
}
updateUrlTabs(text) {
if (!this.updateUrl)
return;
const newUrl = {
...this.route.snapshot.queryParams,
[this.paramUrlName]: text,
};
this.router.navigate([], { relativeTo: this.route, queryParams: newUrl });
}
/**
* Set the default selected item.
* Apply an delay of 100 ms in case of loading a custom font.
* Give priority to user choice (URL) over the default item.
*/
selectDefaultItem() {
if (this.keepUserNavigationChoice)
return;
for (const item of this.items) {
if (item.defaultActive && !item.disable) {
this.timeoutID = window.setTimeout(() => {
this.internalAsideService.internalOnSelectionChange.next({
element: item.native,
animate: false,
});
}, 100);
this.asideService.onSelectionChange.next(item.native);
break;
}
}
}
/**
* Compute the min width in percent based in the provided value for the panel visibility reduction (non responsive mode).
*/
updateMinWidthPercentDiff() {
const minWidthPercentDiff = (this.minVisible / this.userWidth - 1) * 100;
this.native.style.setProperty('--min-width-percent-diff', `${minWidthPercentDiff}%`);
}
/**
* Responsive mode triggered on basis on the breakpoint set.
*/
applyResponsive(animate = true) {
this.responsiveMode = window.innerWidth < this.responsiveBreakpoint;
this.applyAnimations(animate);
if (this.responsiveMode) {
this.native.style.setProperty('--width', '0');
this.native.style.setProperty('--min-width-visible', '0');
}
else {
this.native.style.setProperty('--width', `${this.userWidth}px`);
this.native.style.setProperty('--min-width-visible', `${this.minVisible}px`);
this.asideFullWidthResponsive = false;
}
this.cd.markForCheck();
}
applyAnimations(animate = true) {
this.native.style.transition = animate
? `width ${this.asideAnimationTiming}`
: 'none';
this.asideNative.style.transition = animate
? this.asideAnimationTiming
: 'none';
}
/**
* Position the left marker correctly.
* Inverse it in reverse mode.
*/
positionMarker(element, animated = true) {
if (!this.enableMarker)
return;
this.asideContentTop =
this.asideContent.nativeElement.getBoundingClientRect().top;
const { top } = element.getBoundingClientRect();
const topMarker = top - this.asideContentTop;
const leftMarker = this.reverse ? '100' : '0';
this.asideMarker.nativeElement.style.height = element.clientHeight + 'px';
this.asideMarker.nativeElement.style.transition = animated
? `top ${this.markerAnimationTiming}`
: 'none';
this.asideMarker.nativeElement.style.top = `${topMarker}px`;
this.asideMarker.nativeElement.style.left = `${leftMarker}%`;
}
resize() {
this.resizerNative.style.setProperty('--resizer-color', this.resizerColor);
this.resizerNative.addEventListener('pointerdown', this.onPointerDown.bind(this));
}
/**
* Triggered on Mouse and Touch event.
* Prevent text selection, set the CSS variable for width adaptation, add other event listeners.
*/
onPointerDown(e) {
e.preventDefault();
this.asideNative.style.transition = 'none';
this.native.style.transition = 'none';
const onPointerMove = (e) => {
const pageX = this.reverse ? window.innerWidth - e.pageX : e.pageX;
if (pageX < this.minWidth)
return;
this.native.style.setProperty('--width', `${pageX}px`);
};
const onPointerUp = () => {
this.userWidth = parseFloat(this.native.style.getPropertyValue('--width'));
this.updateMinWidthPercentDiff();
if (this.storePreference) {
localStorage.setItem('user-width', `${this.userWidth}px`);
}
document.removeEventListener('pointermove', onPointerMove);
};
document.addEventListener('pointermove', onPointerMove);
document.addEventListener('pointerup', onPointerUp, {
once: true,
});
}
/**
* In responsive mode, display/hide panel totally.
*/
setAsideFullWidth() {
this.asideFullWidthResponsive = !this.asideFullWidthResponsive;
this.applyAnimations();
this.cd.markForCheck();
}
onCollapseBtnClick() {
this.asideIsOpen = !this.asideIsOpen;
this.applyAnimations();
this.cd.markForCheck();
}
/**
* Reverse the panel position.
* An overflow hidden has to be applied in reversed mode.
*/
reverseDisplay() {
this.reverse = true;
document.body.style.overflow = 'hidden';
}
onMouseEnter() {
this.showCollapsableIcon = true;
}
onMouseLeave() {
this.showCollapsableIcon = false;
}
/**
* Add active class to the element so the user can overload it with custom styles.
*/
addClassActiveToElement(element) {
const prev = this.asideContent.nativeElement.querySelector('.active');
prev?.classList.remove('active');
element.classList.add('active');
}
get notOpened() {
return !this.asideIsOpen;
}
ngOnDestroy() {
this.destroy$.next();
clearTimeout(this.timeoutID);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.3", ngImport: i0, type: AsideComponent, deps: [{ token: i0.ElementRef }, { token: i1.InternalAsideService }, { token: i2.AsideService }, { token: i3.Router }, { token: i3.ActivatedRoute }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.2.3", type: AsideComponent, isStandalone: true, selector: "ngx-aside", inputs: { minVisible: "minVisible", minWidth: "minWidth", width: "width", maxWidth: "maxWidth", responsiveBreakpoint: "responsiveBreakpoint", displayCollapsableIcon: "displayCollapsableIcon", asideAnimationTiming: "asideAnimationTiming", markerAnimationTiming: "markerAnimationTiming", enableResize: "enableResize", resizerColor: "resizerColor", enableMarker: "enableMarker", storePreference: "storePreference", updateUrl: "updateUrl", paramUrlName: "paramUrlName" }, host: { properties: { "class.not-open": "this.notOpened" } }, queries: [{ propertyName: "items", predicate: AsideItemDirective }], viewQueries: [{ propertyName: "asideWrapper", first: true, predicate: ["asideWrapper"], descendants: true }, { propertyName: "asideContent", first: true, predicate: ["asideContent"], descendants: true }, { propertyName: "resizer", first: true, predicate: ["resizer"], descendants: true }, { propertyName: "asideMarker", first: true, predicate: ["asideMarker"], descendants: true }], ngImport: i0, template: "<aside>\r\n <div\r\n class=\"ngx-aside-wrapper\"\r\n [ngClass]=\"{\r\n 'not-open': !asideIsOpen,\r\n 'responsive-mode': responsiveMode,\r\n 'full-width': asideFullWidthResponsive,\r\n 'reversed': reverse,\r\n }\"\r\n (mouseenter)=\"onMouseEnter()\"\r\n (mouseleave)=\"onMouseLeave()\"\r\n #asideWrapper\r\n >\r\n <div class=\"ngx-aside\">\r\n <ng-content select=\"[ngxTitle]\"></ng-content>\r\n\r\n <div class=\"ngx-aside-content\" #asideContent>\r\n <ng-content select=\"[ngxItem]\"></ng-content>\r\n @if (enableMarker) {\r\n <span class=\"ngx-aside-marker\" #asideMarker></span>\r\n }\r\n </div>\r\n <ng-content></ng-content>\r\n </div>\r\n\r\n @if (displayCollapsableIcon) {\r\n <span\r\n class=\"ngx-collapsable-icon\"\r\n [ngClass]=\"{\r\n show: showCollapsableIcon || !asideIsOpen,\r\n 'not-open': !asideIsOpen,\r\n 'responsive-mode': responsiveMode,\r\n 'reversed': reverse,\r\n }\"\r\n (click)=\"onCollapseBtnClick()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 320 512\"\r\n aria-label=\"arrow icon\"\r\n >\r\n <path\r\n d=\"M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z\"\r\n />\r\n </svg>\r\n </span>\r\n } @if (enableResize) {\r\n <div class=\"ngx-resizer-container\">\r\n <span\r\n class=\"ngx-resizer\"\r\n [ngClass]=\"{\r\n 'full-width': asideFullWidthResponsive,\r\n 'responsive-mode': responsiveMode,\r\n 'reversed': reverse,\r\n }\"\r\n #resizer\r\n ></span>\r\n </div>\r\n }\r\n </div>\r\n</aside>\r\n", styles: [":host{--min-width-percent-diff: 0;--min-width: 0;--min-width-visible: 0;--width: 0;--max-width: 0;width:var(--width)}:host.not-open{width:var(--min-width-visible)}.ngx-aside-wrapper{min-height:100vh;padding:1rem .7rem 0;position:relative;width:clamp(var(--min-width),var(--width),var(--max-width))}.ngx-aside-wrapper.not-open{transform:translate3d(var(--min-width-percent-diff),0,0);padding:1rem 0}.ngx-aside-wrapper.not-open.reversed{transform:translateZ(0)}.ngx-aside-wrapper.responsive-mode{transform:translate3d(-100%,0,0);position:absolute;width:0;overflow:hidden}.ngx-aside-wrapper.full-width{transform:translateZ(0);padding:1rem .7rem 0;width:100%;z-index:10}.ngx-aside-wrapper.responsive-mode.reversed{transform:translate3d(100%,0,0)}.ngx-aside-wrapper.full-width.reversed{transform:translate3d(-100%,0,0)}.ngx-aside{text-wrap:nowrap}.ngx-aside-content{position:relative;display:flex;flex-direction:column;gap:.5rem;margin-top:3rem}.ngx-aside-marker{position:absolute;top:-500px;left:0;display:block;width:2px;background:gold}.ngx-resizer{position:absolute;top:0;right:-10px;width:20px;height:100%;cursor:ew-resize;touch-action:none;--resizer-color: #0095be}.ngx-resizer.reversed{right:auto;left:-10px}.ngx-resizer:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;transform:scaleX(0);opacity:0;transition:.3s}.ngx-resizer.reversed:after{left:auto;right:0}.ngx-resizer:not(.full-width):hover:after{opacity:1;background:var(--resizer-color);transform:scaleX(.3)}.ngx-resizer.responsive-mode{display:none}span.ngx-collapsable-icon{position:absolute;top:40px;right:-12px;display:flex;padding:.5rem;background:#fff;box-shadow:0 0 7px 1px #616161;cursor:pointer;border-radius:50%;transform:rotate(180deg);transition:opacity .3s,right .3s;opacity:0;z-index:2}span.ngx-collapsable-icon.reversed{right:auto;left:-12px;transform:rotate(0)}span.ngx-collapsable-icon.responsive-mode{display:none}span.ngx-collapsable-icon svg{width:10px;height:10px}span.ngx-collapsable-icon.show{opacity:1}span.ngx-collapsable-icon.not-open{transform:rotate(0)}span.ngx-collapsable-icon.not-open.reversed{transform:rotate(180deg)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.3", ngImport: i0, type: AsideComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-aside', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<aside>\r\n <div\r\n class=\"ngx-aside-wrapper\"\r\n [ngClass]=\"{\r\n 'not-open': !asideIsOpen,\r\n 'responsive-mode': responsiveMode,\r\n 'full-width': asideFullWidthResponsive,\r\n 'reversed': reverse,\r\n }\"\r\n (mouseenter)=\"onMouseEnter()\"\r\n (mouseleave)=\"onMouseLeave()\"\r\n #asideWrapper\r\n >\r\n <div class=\"ngx-aside\">\r\n <ng-content select=\"[ngxTitle]\"></ng-content>\r\n\r\n <div class=\"ngx-aside-content\" #asideContent>\r\n <ng-content select=\"[ngxItem]\"></ng-content>\r\n @if (enableMarker) {\r\n <span class=\"ngx-aside-marker\" #asideMarker></span>\r\n }\r\n </div>\r\n <ng-content></ng-content>\r\n </div>\r\n\r\n @if (displayCollapsableIcon) {\r\n <span\r\n class=\"ngx-collapsable-icon\"\r\n [ngClass]=\"{\r\n show: showCollapsableIcon || !asideIsOpen,\r\n 'not-open': !asideIsOpen,\r\n 'responsive-mode': responsiveMode,\r\n 'reversed': reverse,\r\n }\"\r\n (click)=\"onCollapseBtnClick()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 320 512\"\r\n aria-label=\"arrow icon\"\r\n >\r\n <path\r\n d=\"M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z\"\r\n />\r\n </svg>\r\n </span>\r\n } @if (enableResize) {\r\n <div class=\"ngx-resizer-container\">\r\n <span\r\n class=\"ngx-resizer\"\r\n [ngClass]=\"{\r\n 'full-width': asideFullWidthResponsive,\r\n 'responsive-mode': responsiveMode,\r\n 'reversed': reverse,\r\n }\"\r\n #resizer\r\n ></span>\r\n </div>\r\n }\r\n </div>\r\n</aside>\r\n", styles: [":host{--min-width-percent-diff: 0;--min-width: 0;--min-width-visible: 0;--width: 0;--max-width: 0;width:var(--width)}:host.not-open{width:var(--min-width-visible)}.ngx-aside-wrapper{min-height:100vh;padding:1rem .7rem 0;position:relative;width:clamp(var(--min-width),var(--width),var(--max-width))}.ngx-aside-wrapper.not-open{transform:translate3d(var(--min-width-percent-diff),0,0);padding:1rem 0}.ngx-aside-wrapper.not-open.reversed{transform:translateZ(0)}.ngx-aside-wrapper.responsive-mode{transform:translate3d(-100%,0,0);position:absolute;width:0;overflow:hidden}.ngx-aside-wrapper.full-width{transform:translateZ(0);padding:1rem .7rem 0;width:100%;z-index:10}.ngx-aside-wrapper.responsive-mode.reversed{transform:translate3d(100%,0,0)}.ngx-aside-wrapper.full-width.reversed{transform:translate3d(-100%,0,0)}.ngx-aside{text-wrap:nowrap}.ngx-aside-content{position:relative;display:flex;flex-direction:column;gap:.5rem;margin-top:3rem}.ngx-aside-marker{position:absolute;top:-500px;left:0;display:block;width:2px;background:gold}.ngx-resizer{position:absolute;top:0;right:-10px;width:20px;height:100%;cursor:ew-resize;touch-action:none;--resizer-color: #0095be}.ngx-resizer.reversed{right:auto;left:-10px}.ngx-resizer:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:100%;transform:scaleX(0);opacity:0;transition:.3s}.ngx-resizer.reversed:after{left:auto;right:0}.ngx-resizer:not(.full-width):hover:after{opacity:1;background:var(--resizer-color);transform:scaleX(.3)}.ngx-resizer.responsive-mode{display:none}span.ngx-collapsable-icon{position:absolute;top:40px;right:-12px;display:flex;padding:.5rem;background:#fff;box-shadow:0 0 7px 1px #616161;cursor:pointer;border-radius:50%;transform:rotate(180deg);transition:opacity .3s,right .3s;opacity:0;z-index:2}span.ngx-collapsable-icon.reversed{right:auto;left:-12px;transform:rotate(0)}span.ngx-collapsable-icon.responsive-mode{display:none}span.ngx-collapsable-icon svg{width:10px;height:10px}span.ngx-collapsable-icon.show{opacity:1}span.ngx-collapsable-icon.not-open{transform:rotate(0)}span.ngx-collapsable-icon.not-open.reversed{transform:rotate(180deg)}\n"] }]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.InternalAsideService }, { type: i2.AsideService }, { type: i3.Router }, { type: i3.ActivatedRoute }, { type: i0.ChangeDetectorRef }], propDecorators: { minVisible: [{
type: Input
}], minWidth: [{
type: Input
}], width: [{
type: Input
}], maxWidth: [{
type: Input
}], responsiveBreakpoint: [{
type: Input
}], displayCollapsableIcon: [{
type: Input
}], asideAnimationTiming: [{
type: Input
}], markerAnimationTiming: [{
type: Input
}], enableResize: [{
type: Input
}], resizerColor: [{
type: Input
}], enableMarker: [{
type: Input
}], storePreference: [{
type: Input
}], updateUrl: [{
type: Input
}], paramUrlName: [{
type: Input
}], asideWrapper: [{
type: ViewChild,
args: ['asideWrapper']
}], asideContent: [{
type: ViewChild,
args: ['asideContent']
}], resizer: [{
type: ViewChild,
args: ['resizer']
}], asideMarker: [{
type: ViewChild,
args: ['asideMarker']
}], items: [{
type: ContentChildren,
args: [AsideItemDirective]
}], notOpened: [{
type: HostBinding,
args: ['class.not-open']
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNpZGUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWFzaWRlLWVhc2Uvc3JjL2xpYi9hc2lkZS9hc2lkZS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtYXNpZGUtZWFzZS9zcmMvbGliL2FzaWRlL2FzaWRlLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMvQyxPQUFPLEVBRUwsdUJBQXVCLEVBRXZCLFNBQVMsRUFDVCxlQUFlLEVBRWYsV0FBVyxFQUNYLEtBQUssRUFJTCxTQUFTLEdBQ1YsTUFBTSxlQUFlLENBQUM7QUFFdkIsT0FBTyxFQUFFLE9BQU8sRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNuRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQzs7Ozs7O0FBWXRELE1BQU0sT0FBTyxjQUFjO0lBa0N6QixZQUNVLE9BQW1DLEVBQ25DLG9CQUEwQyxFQUMxQyxZQUEwQixFQUMxQixNQUFjLEVBQ2QsS0FBcUIsRUFDckIsRUFBcUI7UUFMckIsWUFBTyxHQUFQLE9BQU8sQ0FBNEI7UUFDbkMseUJBQW9CLEdBQXBCLG9CQUFvQixDQUFzQjtRQUMxQyxpQkFBWSxHQUFaLFlBQVksQ0FBYztRQUMxQixXQUFNLEdBQU4sTUFBTSxDQUFRO1FBQ2QsVUFBSyxHQUFMLEtBQUssQ0FBZ0I7UUFDckIsT0FBRSxHQUFGLEVBQUUsQ0FBbUI7UUF2Q3RCLGVBQVUsR0FBRyxFQUFFLENBQUM7UUFDaEIsYUFBUSxHQUFHLEdBQUcsQ0FBQztRQUNmLFVBQUssR0FBRyxHQUFHLENBQUM7UUFDWixhQUFRLEdBQUcsSUFBSSxDQUFDO1FBQ2hCLHlCQUFvQixHQUFHLEdBQUcsQ0FBQztRQUMzQiwyQkFBc0IsR0FBRyxJQUFJLENBQUM7UUFDOUIseUJBQW9CLEdBQUcsZUFBZSxDQUFDO1FBQ3ZDLDBCQUFxQixHQUFHLGVBQWUsQ0FBQztRQUN4QyxpQkFBWSxHQUFHLElBQUksQ0FBQztRQUNwQixpQkFBWSxHQUFHLFNBQVMsQ0FBQztRQUN6QixpQkFBWSxHQUFHLElBQUksQ0FBQztRQUNwQixvQkFBZSxHQUFHLElBQUksQ0FBQztRQUN2QixjQUFTLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLGlCQUFZLEdBQUcsTUFBTSxDQUFDO1FBRS9CLGNBQVMsR0FBRyxDQUFDLENBQUM7UUFDZCx3QkFBbUIsR0FBRyxLQUFLLENBQUM7UUFDNUIsZ0JBQVcsR0FBRyxJQUFJLENBQUM7UUFDbkIsbUJBQWMsR0FBRyxLQUFLLENBQUM7UUFDdkIsb0JBQWUsR0FBRyxDQUFDLENBQUM7UUFDcEIsYUFBUSxHQUFHLElBQUksT0FBTyxFQUFRLENBQUM7UUFDL0IsNkJBQXdCLEdBQUcsS0FBSyxDQUFDO1FBQ2pDLDZCQUF3QixHQUFHLEtBQUssQ0FBQztRQUNqQyxZQUFPLEdBQUcsS0FBSyxDQUFDO0lBaUJiLENBQUM7SUFFSixJQUFJLE1BQU07UUFDUixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDO0lBQ3BDLENBQUM7SUFFRCxJQUFJLFdBQVc7UUFDYixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYSxDQUFDO0lBQ3pDLENBQUM7SUFFRCxJQUFJLGFBQWE7UUFDZixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDO0lBQ3BDLENBQUM7SUFFRCxJQUFJLGdCQUFnQjtRQUNsQixPQUFPLElBQUksQ0FBQyxjQUFjLENBQUM7SUFDN0IsQ0FBQztJQUVELFFBQVE7UUFDTixJQUFJLENBQUMsb0JBQW9CLENBQUMseUJBQXlCO2FBQ2hELElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO2FBQzlCLFNBQVMsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFO1lBQ2xCLE1BQU0sRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDO1lBQ2xDLElBQUksQ0FBQyxjQUFjLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQ3RDLElBQUksQ0FBQyxhQUFhLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDO1lBQ3BELElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUN4QyxDQUFDLENBQUMsQ0FBQztRQUVMLFNBQVMsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDO2FBQ3hCLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQzthQUNqRCxTQUFTLENBQUMsR0FBRyxFQUFFO1lBQ2QsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ3pCLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVEOzs7T0FHRztJQUNILGVBQWU7UUFDYixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7UUFDNUIsSUFBSSxJQUFJLENBQUMsZUFBZSxFQUFFO1lBQ3hCLElBQUksQ0FBQyxTQUFTO2dCQUNaLFVBQVUsQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUM7U0FDdEU7UUFFRCxJQUFJLENBQUMseUJBQXlCLEVBQUUsQ0FBQztRQUVqQyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsYUFBYSxFQUFFLEdBQUcsSUFBSSxDQUFDLFFBQVEsSUFBSSxDQUFDLENBQUM7UUFDbkUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUMzQixxQkFBcUIsRUFDckIsR0FBRyxJQUFJLENBQUMsVUFBVSxJQUFJLENBQ3ZCLENBQUM7UUFDRixJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsU0FBUyxFQUFFLEdBQUcsSUFBSSxDQUFDLFNBQVMsSUFBSSxDQUFDLENBQUM7UUFDaEUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLGFBQWEsRUFBRSxHQUFHLElBQUksQ0FBQyxRQUFRLElBQUksQ0FBQyxDQUFDO1FBRW5FLElBQUksQ0FBQyxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDNUIsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDekIsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFFekIsSUFBSSxJQUFJLENBQUMsWUFBWTtZQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILGlCQUFpQjtRQUNmLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUztZQUFFLE9BQU87UUFFNUIsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7UUFDeEMsTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDaEMsTUFBTSxNQUFNLEdBQUcsSUFBSSxlQUFlLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQy9DLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxFQUFFLFdBQVcsRUFBRSxDQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQztRQUN2RSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFNUMsSUFBSSxNQUFNLFlBQVksV0FBVyxFQUFFO1lBQ2pDLElBQUksQ0FBQyxTQUFTLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7Z0JBQ3RDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyx5QkFBeUIsQ0FBQyxJQUFJLENBQUM7b0JBQ3ZELE9BQU8sRUFBRSxNQUFNO29CQUNmLE9BQU8sRUFBRSxLQUFLO2lCQUNmLENBQUMsQ0FBQztZQUNMLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUVSLElBQUksQ0FBQyxZQUFZLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ2pELElBQUksQ0FBQyx3QkFBd0IsR0FBRyxJQUFJLENBQUM7U0FDdEM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsaUJBQWlCLENBQUMsSUFBWTtRQUM1QixJQUFJLENBQUMsSUFBSTtZQUFFLE9BQU87UUFDbEIsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDO1FBRTlDLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLEtBQUssRUFBRTtZQUM3QixJQUFJLElBQUksQ0FBQyxPQUFPO2dCQUFFLE9BQU8sSUFBSSxDQUFDO1lBRTlCLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ2pELElBQUksV0FBVyxLQUFLLElBQUksRUFBRTtnQkFDeEIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDO2FBQ3BCO1NBQ0Y7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCxhQUFhLENBQUMsSUFBWTtRQUN4QixJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVM7WUFBRSxPQUFPO1FBRTVCLE1BQU0sTUFBTSxHQUFHO1lBQ2IsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxXQUFXO1lBQ2xDLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxFQUFFLElBQUk7U0FDMUIsQ0FBQztRQUNGLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxFQUFFLFVBQVUsRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO0lBQzVFLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsaUJBQWlCO1FBQ2YsSUFBSSxJQUFJLENBQUMsd0JBQXdCO1lBQUUsT0FBTztRQUUxQyxLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUU7WUFDN0IsSUFBSSxJQUFJLENBQUMsYUFBYSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRTtnQkFDdkMsSUFBSSxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLEdBQUcsRUFBRTtvQkFDdEMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLHlCQUF5QixDQUFDLElBQUksQ0FBQzt3QkFDdkQsT0FBTyxFQUFFLElBQUksQ0FBQyxNQUFNO3dCQUNwQixPQUFPLEVBQUUsS0FBSztxQkFDZixDQUFDLENBQUM7Z0JBQ0wsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO2dCQUNSLElBQUksQ0FBQyxZQUFZLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDdEQsTUFBTTthQUNQO1NBQ0Y7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSCx5QkFBeUI7UUFDdkIsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUM7UUFFekUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUMzQiwwQkFBMEIsRUFDMUIsR0FBRyxtQkFBbUIsR0FBRyxDQUMxQixDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0gsZUFBZSxDQUFDLE9BQU8sR0FBRyxJQUFJO1FBQzVCLElBQUksQ0FBQyxjQUFjLEdBQUcsTUFBTSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsb0JBQW9CLENBQUM7UUFDcEUsSUFBSSxDQUFDLGVBQWUsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUU5QixJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUU7WUFDdkIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLFNBQVMsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUM5QyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMscUJBQXFCLEVBQUUsR0FBRyxDQUFDLENBQUM7U0FDM0Q7YUFBTTtZQUNMLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxTQUFTLEVBQUUsR0FBRyxJQUFJLENBQUMsU0FBUyxJQUFJLENBQUMsQ0FBQztZQUNoRSxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQzNCLHFCQUFxQixFQUNyQixHQUFHLElBQUksQ0FBQyxVQUFVLElBQUksQ0FDdkIsQ0FBQztZQUNGLElBQUksQ0FBQyx3QkFBd0IsR0FBRyxLQUFLLENBQUM7U0FDdkM7UUFFRCxJQUFJLENBQUMsRUFBRSxDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFFRCxlQUFlLENBQUMsT0FBTyxHQUFHLElBQUk7UUFDNUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsVUFBVSxHQUFHLE9BQU87WUFDcEMsQ0FBQyxDQUFDLFNBQVMsSUFBSSxDQUFDLG9CQUFvQixFQUFFO1lBQ3RDLENBQUMsQ0FBQyxNQUFNLENBQUM7UUFDWCxJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxVQUFVLEdBQUcsT0FBTztZQUN6QyxDQUFDLENBQUMsSUFBSSxDQUFDLG9CQUFvQjtZQUMzQixDQUFDLENBQUMsTUFBTSxDQUFDO0lBQ2IsQ0FBQztJQUVEOzs7T0FHRztJQUNILGNBQWMsQ0FBQyxPQUFvQixFQUFFLFFBQVEsR0FBRyxJQUFJO1FBQ2xELElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWTtZQUFFLE9BQU87UUFFL0IsSUFBSSxDQUFDLGVBQWU7WUFDbEIsSUFBSSxDQUFDLFlBQVksQ0FBQyxhQUFhLENBQUMscUJBQXFCLEVBQUUsQ0FBQyxHQUFHLENBQUM7UUFFOUQsTUFBTSxFQUFFLEdBQUcsRUFBRSxHQUFHLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBQ2hELE1BQU0sU0FBUyxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDO1FBQzdDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO1FBRTlDLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUM7UUFDMUUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLFVBQVUsR0FBRyxRQUFRO1lBQ3hELENBQUMsQ0FBQyxPQUFPLElBQUksQ0FBQyxxQkFBcUIsRUFBRTtZQUNyQyxDQUFDLENBQUMsTUFBTSxDQUFDO1FBQ1gsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLEdBQUcsR0FBRyxHQUFHLFNBQVMsSUFBSSxDQUFDO1FBQzVELElBQUksQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxJQUFJLEdBQUcsR0FBRyxVQUFVLEdBQUcsQ0FBQztJQUMvRCxDQUFDO0lBRUQsTUFBTTtRQUNKLElBQUksQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxpQkFBaUIsRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDM0UsSUFBSSxDQUFDLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FDakMsYUFBYSxFQUNiLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUM5QixDQUFDO0lBQ0osQ0FBQztJQUVEOzs7T0FHRztJQUNILGFBQWEsQ0FBQyxDQUFlO1FBQzNCLENBQUMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUNuQixJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxVQUFVLEdBQUcsTUFBTSxDQUFDO1FBQzNDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLFVBQVUsR0FBRyxNQUFNLENBQUM7UUFFdEMsTUFBTSxhQUFhLEdBQUcsQ0FBQyxDQUFlLEVBQUUsRUFBRTtZQUN4QyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7WUFDbkUsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDLFFBQVE7Z0JBQUUsT0FBTztZQUNsQyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsU0FBUyxFQUFFLEdBQUcsS0FBSyxJQUFJLENBQUMsQ0FBQztRQUN6RCxDQUFDLENBQUM7UUFFRixNQUFNLFdBQVcsR0FBRyxHQUFHLEVBQUU7WUFDdkIsSUFBSSxDQUFDLFNBQVMsR0FBRyxVQUFVLENBQ3pCLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxDQUM5QyxDQUFDO1lBQ0YsSUFBSSxDQUFDLHlCQUF5QixFQUFFLENBQUM7WUFFakMsSUFBSSxJQUFJLENBQUMsZUFBZSxFQUFFO2dCQUN4QixZQUFZLENBQUMsT0FBTyxDQUFDLFlBQVksRUFBRSxHQUFHLElBQUksQ0FBQyxTQUFTLElBQUksQ0FBQyxDQUFDO2FBQzNEO1lBQ0QsUUFBUSxDQUFDLG1CQUFtQixDQUFDLGFBQWEsRUFBRSxhQUFhLENBQUMsQ0FBQztRQUM3RCxDQUFDLENBQUM7UUFFRixRQUFRLENBQUMsZ0JBQWdCLENBQUMsYUFBYSxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBQ3hELFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxXQUFXLEVBQUUsV0FBVyxFQUFFO1lBQ2xELElBQUksRUFBRSxJQUFJO1NBQ1gsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0gsaUJBQWlCO1FBQ2YsSUFBSSxDQUFDLHdCQUF3QixHQUFHLENBQUMsSUFBSSxDQUFDLHdCQUF3QixDQUFDO1FBQy9ELElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMsRUFBRSxDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ3pCLENBQUM7SUFFRCxrQkFBa0I7UUFDaEIsSUFBSSxDQUFDLFdBQVcsR0FBRyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUM7UUFDckMsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxFQUFFLENBQUMsWUFBWSxFQUFFLENBQUM7SUFDekIsQ0FBQztJQUVEOzs7T0FHRztJQUNILGNBQWM7UUFDWixJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztRQUNwQixRQUFRLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO0lBQzFDLENBQUM7SUFFRCxZQUFZO1FBQ1YsSUFBSSxDQUFDLG1CQUFtQixHQUFHLElBQUksQ0FBQztJQUNsQyxDQUFDO0lBRUQsWUFBWTtRQUNWLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxLQUFLLENBQUM7SUFDbkMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsdUJBQXVCLENBQUMsT0FBb0I7UUFDMUMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxhQUFhLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3RFLElBQUksRUFBRSxTQUFTLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2pDLE9BQU8sQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ2xDLENBQUM7SUFFRCxJQUNJLFNBQVM7UUFDWCxPQUFPLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQztJQUMzQixDQUFDO0lBRUQsV0FBVztRQUNULElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDckIsWUFBWSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUMvQixDQUFDOzhHQWxWVSxjQUFjO2tHQUFkLGNBQWMsNm1CQStCUixrQkFBa0IsNFpDNURyQywyMERBNkRBLGlwRURyQ1ksWUFBWTs7MkZBS1gsY0FBYztrQkFSMUIsU0FBUzsrQkFDRSxXQUFXLGNBQ1QsSUFBSSxXQUNQLENBQUMsWUFBWSxDQUFDLG1CQUdOLHVCQUF1QixDQUFDLE1BQU07K05BR3RDLFVBQVU7c0JBQWxCLEtBQUs7Z0JBQ0csUUFBUTtzQkFBaEIsS0FBSztnQkFDRyxLQUFLO3NCQUFiLEtBQUs7Z0JBQ0csUUFBUTtzQkFBaEIsS0FBSztnQkFDRyxvQkFBb0I7c0JBQTVCLEtBQUs7Z0JBQ0csc0JBQXNCO3NCQUE5QixLQUFLO2dCQUNHLG9CQUFvQjtzQkFBNUIsS0FBSztnQkFDRyxxQkFBcUI7c0JBQTdCLEtBQUs7Z0JBQ0csWUFBWTtzQkFBcEIsS0FBSztnQkFDRyxZQUFZO3NCQUFwQixLQUFLO2dCQUNHLFlBQVk7c0JBQXBCLEtBQUs7Z0JBQ0csZUFBZTtzQkFBdkIsS0FBSztnQkFDRyxTQUFTO3NCQUFqQixLQUFLO2dCQUNHLFlBQVk7c0JBQXBCLEtBQUs7Z0JBYXFCLFlBQVk7c0JBQXRDLFNBQVM7dUJBQUMsY0FBYztnQkFDRSxZQUFZO3NCQUF0QyxTQUFTO3VCQUFDLGNBQWM7Z0JBQ0gsT0FBTztzQkFBNUIsU0FBUzt1QkFBQyxTQUFTO2dCQUNNLFdBQVc7c0JBQXBDLFNBQVM7dUJBQUMsYUFBYTtnQkFFeEIsS0FBSztzQkFESixlQUFlO3VCQUFDLGtCQUFrQjtnQkE0Uy9CLFNBQVM7c0JBRFosV0FBVzt1QkFBQyxnQkFBZ0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xyXG5pbXBvcnQge1xyXG4gIEFmdGVyVmlld0luaXQsXHJcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXHJcbiAgQ2hhbmdlRGV0ZWN0b3JSZWYsXHJcbiAgQ29tcG9uZW50LFxyXG4gIENvbnRlbnRDaGlsZHJlbixcclxuICBFbGVtZW50UmVmLFxyXG4gIEhvc3RCaW5kaW5nLFxyXG4gIElucHV0LFxyXG4gIE9uRGVzdHJveSxcclxuICBPbkluaXQsXHJcbiAgUXVlcnlMaXN0LFxyXG4gIFZpZXdDaGlsZCxcclxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgSW50ZXJuYWxBc2lkZVNlcnZpY2UgfSBmcm9tICcuLi9pbnRlcm5hbC1hc2lkZS5zZXJ2aWNlJztcclxuaW1wb3J0IHsgU3ViamVjdCwgZGVib3VuY2VUaW1lLCBmcm9tRXZlbnQsIHRha2VVbnRpbCB9IGZyb20gJ3J4anMnO1xyXG5pbXBvcnQgeyBBc2lkZUl0ZW1EaXJlY3RpdmUgfSBmcm9tICcuL2l0ZW0uZGlyZWN0aXZlJztcclxuaW1wb3J0IHsgQWN0aXZhdGVkUm91dGUsIFJvdXRlciB9IGZyb20gJ0Bhbmd1bGFyL3JvdXRlcic7XHJcbmltcG9ydCB7IEFzaWRlU2VydmljZSB9IGZyb20gJy4uL2FzaWRlLnNlcnZpY2UnO1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICduZ3gtYXNpZGUnLFxyXG4gIHN0YW5kYWxvbmU6IHRydWUsXHJcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZV0sXHJcbiAgdGVtcGxhdGVVcmw6ICdhc2lkZS5jb21wb25lbnQuaHRtbCcsXHJcbiAgc3R5bGVVcmxzOiBbJ2FzaWRlLmNvbXBvbmVudC5jc3MnXSxcclxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcclxufSlcclxuZXhwb3J0IGNsYXNzIEFzaWRlQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3kge1xyXG4gIEBJbnB1dCgpIG1pblZpc2libGUgPSAzMDtcclxuICBASW5wdXQoKSBtaW5XaWR0aCA9IDI1MDtcclxuICBASW5wdXQoKSB3aWR0aCA9IDMwMDtcclxuICBASW5wdXQoKSBtYXhXaWR0aCA9ICc1MCc7XHJcbiAgQElucHV0KCkgcmVzcG9uc2l2ZUJyZWFrcG9pbnQgPSA4MDA7XHJcbiAgQElucHV0KCkgZGlzcGxheUNvbGxhcHNhYmxlSWNvbiA9IHRydWU7XHJcbiAgQElucHV0KCkgYXNpZGVBbmltYXRpb25UaW1pbmcgPSAnMC4zcyBlYXNlLW91dCc7XHJcbiAgQElucHV0KCkgbWFya2VyQW5pbWF0aW9uVGltaW5nID0gJzAuM3MgZWFzZS1vdXQnO1xyXG4gIEBJbnB1dCgpIGVuYWJsZVJlc2l6ZSA9IHRydWU7XHJcbiAgQElucHV0KCkgcmVzaXplckNvbG9yID0gJyMwMDk1YmUnO1xyXG4gIEBJbnB1dCgpIGVuYWJsZU1hcmtlciA9IHRydWU7XHJcbiAgQElucHV0KCkgc3RvcmVQcmVmZXJlbmNlID0gdHJ1ZTtcclxuICBASW5wdXQoKSB1cGRhdGVVcmwgPSB0cnVlO1xyXG4gIEBJbnB1dCgpIHBhcmFtVXJsTmFtZSA9ICduYW1lJztcclxuXHJcbiAgdXNlcldpZHRoID0gMDtcclxuICBzaG93Q29sbGFwc2FibGVJY29uID0gZmFsc2U7XHJcbiAgYXNpZGVJc09wZW4gPSB0cnVlO1xyXG4gIHJlc3BvbnNpdmVNb2RlID0gZmFsc2U7XHJcbiAgYXNpZGVDb250ZW50VG9wID0gMDtcclxuICBkZXN0cm95JCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XHJcbiAgYXNpZGVGdWxsV2lkdGhSZXNwb25zaXZlID0gZmFsc2U7XHJcbiAga2VlcFVzZXJOYXZpZ2F0aW9uQ2hvaWNlID0gZmFsc2U7XHJcbiAgcmV2ZXJzZSA9IGZhbHNlO1xyXG4gIHRpbWVvdXRJRCE6IG51bWJlcjtcclxuXHJcbiAgQFZpZXdDaGlsZCgnYXNpZGVXcmFwcGVyJykgYXNpZGVXcmFwcGVyITogRWxlbWVudFJlZjxIVE1MRGl2RWxlbWVudD47XHJcbiAgQFZpZXdDaGlsZCgnYXNpZGVDb250ZW50JykgYXNpZGVDb250ZW50ITogRWxlbWVudFJlZjxIVE1MRGl2RWxlbWVudD47XHJcbiAgQFZpZXdDaGlsZCgncmVzaXplcicpIHJlc2l6ZXIhOiBFbGVtZW50UmVmPEhUTUxTcGFuRWxlbWVudD47XHJcbiAgQFZpZXdDaGlsZCgnYXNpZGVNYXJrZXInKSBhc2lkZU1hcmtlciE6IEVsZW1lbnRSZWY8SFRNTFNwYW5FbGVtZW50PjtcclxuICBAQ29udGVudENoaWxkcmVuKEFzaWRlSXRlbURpcmVjdGl2ZSlcclxuICBpdGVtcyE6IFF1ZXJ5TGlzdDxBc2lkZUl0ZW1EaXJlY3RpdmU+O1xyXG5cclxuICBjb25zdHJ1Y3RvcihcclxuICAgIHByaXZhdGUgZWxlbWVudDogRWxlbWVudFJlZjxIVE1MRGl2RWxlbWVudD4sXHJcbiAgICBwcml2YXRlIGludGVybmFsQXNpZGVTZXJ2aWNlOiBJbnRlcm5hbEFzaWRlU2VydmljZSxcclxuICAgIHByaXZhdGUgYXNpZGVTZXJ2aWNlOiBBc2lkZVNlcnZpY2UsXHJcbiAgICBwcml2YXRlIHJvdXRlcjogUm91dGVyLFxyXG4gICAgcHJpdmF0ZSByb3V0ZTogQWN0aXZhdGVkUm91dGUsXHJcbiAgICBwcml2YXRlIGNkOiBDaGFuZ2VEZXRlY3RvclJlZlxyXG4gICkge31cclxuXHJcbiAgZ2V0IG5hdGl2ZSgpIHtcclxuICAgIHJldHVybiB0aGlzLmVsZW1lbnQubmF0aXZlRWxlbWVudDtcclxuICB9XHJcblxyXG4gIGdldCBhc2lkZU5hdGl2ZSgpIHtcclxuICAgIHJldHVybiB0aGlzLmFzaWRlV3JhcHBlci5uYXRpdmVFbGVtZW50O1xyXG4gIH1cclxuXHJcbiAgZ2V0IHJlc2l6ZXJOYXRpdmUoKSB7XHJcbiAgICByZXR1cm4gdGhpcy5yZXNpemVyLm5hdGl2ZUVsZW1lbnQ7XHJcbiAgfVxyXG5cclxuICBnZXQgaXNSZXNwb25zaXZlTW9kZSgpIHtcclxuICAgIHJldHVybiB0aGlzLnJlc3BvbnNpdmVNb2RlO1xyXG4gIH1cclxuXHJcbiAgbmdPbkluaXQoKSB7XHJcbiAgICB0aGlzLmludGVybmFsQXNpZGVTZXJ2aWNlLmludGVybmFsT25TZWxlY3Rpb25DaGFuZ2VcclxuICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKVxyXG4gICAgICAuc3Vic2NyaWJlKChpdGVtKSA9PiB7XHJcbiAgICAgICAgY29uc3QgeyBlbGVtZW50LCBhbmltYXRlIH0gPSBpdGVtO1xyXG4gICAgICAgIHRoaXMucG9zaXRpb25NYXJrZXIoZWxlbWVudCwgYW5pbWF0ZSk7XHJcbiAgICAgICAgdGhpcy51cGRhdGVVcmxUYWJzKGVsZW1lbnQuaW5uZXJUZXh0LnRvTG93ZXJDYXNlKCkpO1xyXG4gICAgICAgIHRoaXMuYWRkQ2xhc3NBY3RpdmVUb0VsZW1lbnQoZWxlbWVudCk7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgIGZyb21FdmVudCh3aW5kb3csICdyZXNpemUnKVxyXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCksIGRlYm91bmNlVGltZSgzMDApKVxyXG4gICAgICAuc3Vic2NyaWJlKCgpID0+IHtcclxuICAgICAgICB0aGlzLmFwcGx5UmVzcG9uc2l2ZSgpO1xyXG4gICAgICB9KTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJldHJpZXZlIHRoZSBwcmV2aW91cyBzdG9yZWQgcHJlZmVyZW5jZSBvciB0YWtlIHRoZSBkZWZpbmVkIHdpZHRoLlxyXG4gICAqIEluaXRpYWxpc2UgQ1NTIHZhcmlhYmxlcy5cclxuICAgKi9cclxuICBuZ0FmdGVyVmlld0luaXQoKSB7XHJcbiAgICB0aGlzLnVzZXJXaWR0aCA9IHRoaXMud2lkdGg7XHJcbiAgICBpZiAodGhpcy5zdG9yZVByZWZlcmVuY2UpIHtcclxuICAgICAgdGhpcy51c2VyV2lkdGggPVxyXG4gICAgICAgIHBhcnNlRmxvYXQobG9jYWxTdG9yYWdlLmdldEl0ZW0oJ3VzZXItd2lkdGgnKSB8fCAnJykgfHwgdGhpcy53aWR0aDtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLnVwZGF0ZU1pbldpZHRoUGVyY2VudERpZmYoKTtcclxuXHJcbiAgICB0aGlzLm5hdGl2ZS5zdHlsZS5zZXRQcm9wZXJ0eSgnLS1taW4td2lkdGgnLCBgJHt0aGlzLm1pbldpZHRofXB4YCk7XHJcbiAgICB0aGlzLm5hdGl2ZS5zdHlsZS5zZXRQcm9wZXJ0eShcclxuICAgICAgJy0tbWluLXdpZHRoLXZpc2libGUnLFxyXG4gICAgICBgJHt0aGlzLm1pblZpc2libGV9cHhgXHJcbiAgICApO1xyXG4gICAgdGhpcy5uYXRpdmUuc3R5bGUuc2V0UHJvcGVydHkoJy0td2lkdGgnLCBgJHt0aGlzLnVzZXJXaWR0aH1weGApO1xyXG4gICAgdGhpcy5uYXRpdmUuc3R5bGUuc2V0UHJvcGVydHkoJy0tbWF4LXdpZHRoJywgYCR7dGhpcy5tYXhXaWR0aH12d2ApO1xyXG5cclxuICAgIHRoaXMuYXBwbHlSZXNwb25zaXZlKGZhbHNlKTtcclxuICAgIHRoaXMuYWN0aXZhdGVQYXJhbXNVcmwoKTtcclxuICAgIHRoaXMuc2VsZWN0RGVmYXVsdEl0ZW0oKTtcclxuXHJcbiAgICBpZiAodGhpcy5lbmFibGVSZXNpemUpIHRoaXMucmVzaXplKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBOYXZpZ2F0aW9uIGJ5IFVSTC5cclxuICAgKiBOb3QgdGhyb3VnaCB0aGUgQW5ndWxhciBBUEkgZm9yIHN5bmNocm9ub3VzIHJlYXNvbi4gR2l2ZSBwcmlvcml0eSB0byB0aGUgdXNlciBjaG9pY2Ugb3ZlciB0aGUgZGVmYXVsdCBhY3RpdmUgaXRlbS5cclxuICAgKiBBcHBseSAxMDAgbXMgZGVsYXksIGEgY3VzdG9tIGZvbnQgY2FuIGJlIG5vdCBmdWxseSBsb2FkZWQuXHJcbiAgICovXHJcbiAgYWN0aXZhdGVQYXJhbXNVcmwoKSB7XHJcbiAgICBpZiAoIXRoaXMudXBkYXRlVXJsKSByZXR1cm47XHJcblxyXG4gICAgY29uc3QgY3VycmVudFVybCA9IHdpbmRvdy5sb2NhdGlvbi5ocmVmO1xyXG4gICAgY29uc3QgdXJsID0gbmV3IFVSTChjdXJyZW50VXJsKTtcclxuICAgIGNvbnN0IHBhcmFtcyA9IG5ldyBVUkxTZWFyY2hQYXJhbXModXJsLnNlYXJjaCk7XHJcbiAgICBjb25zdCBuYW1lID0gcGFyYW1zLmdldCh0aGlzLnBhcmFtVXJsTmFtZSk/LnRvTG93ZXJDYXNlKCkudHJpbSgpIHx8ICcnO1xyXG4gICAgY29uc3QgYWN0aXZlID0gdGhpcy5maW5kVGFiVG9BY3RpdmF0ZShuYW1lKTtcclxuXHJcbiAgICBpZiAoYWN0aXZlIGluc3RhbmNlb2YgSFRNTEVsZW1lbnQpIHtcclxuICAgICAgdGhpcy50aW1lb3V0SUQgPSB3aW5kb3cuc2V0VGltZW91dCgoKSA9PiB7XHJcbiAgICAgICAgdGhpcy5pbnRlcm5hbEFzaWRlU2VydmljZS5pbnRlcm5hbE9uU2VsZWN0aW9uQ2hhbmdlLm5leHQoe1xyXG4gICAgICAgICAgZWxlbWVudDogYWN0aXZlLFxyXG4gICAgICAgICAgYW5pbWF0ZTogZmFsc2UsXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0sIDEwMCk7XHJcblxyXG4gICAgICB0aGlzLmFzaWRlU2VydmljZS5vblNlbGVjdGlvbkNoYW5nZS5uZXh0KGFjdGl2ZSk7XHJcbiAgICAgIHRoaXMua2VlcFVzZXJOYXZpZ2F0aW9uQ2hvaWNlID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIE5hdmlnYXRpb24gYnkgVVJMLlxyXG4gICAqIEZpbmQgdGhlIGNvcnJlc3BvbmRpbmcgaXRlbSBhbmQgYWN0aXZhdGUgaXQuXHJcbiAgICovXHJcbiAgZmluZFRhYlRvQWN0aXZhdGUobmFtZTogc3RyaW5nKSB7XHJcbiAgICBpZiAoIW5hbWUpIHJldHVybjtcclxuICAgIGNvbnN0IGNsZWFuZWROYW1lID0gbmFtZS50b0xvd2VyQ2FzZSgpLnRyaW0oKTtcclxuXHJcbiAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgdGhpcy5pdGVtcykge1xyXG4gICAgICBpZiAoaXRlbS5kaXNhYmxlKSByZXR1cm4gbnVsbDtcclxuXHJcbiAgICAgIGNvbnN0IHRleHQgPSBpdGVtLm5hdGl2ZS5pbm5lclRleHQudG9Mb3dlckNhc2UoKTtcclxuICAgICAgaWYgKGNsZWFuZWROYW1lID09PSB0ZXh0KSB7XHJcbiAgICAgICAgcmV0dXJuIGl0ZW0ubmF0aXZlO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICB1cGRhdGVVcmxUYWJzKHRleHQ6IHN0cmluZykge1xyXG4gICAgaWYgKCF0aGlzLnVwZGF0ZVVybCkgcmV0dXJuO1xyXG5cclxuICAgIGNvbnN0IG5ld1VybCA9IHtcclxuICAgICAgLi4udGhpcy5yb3V0ZS5zbmFwc2hvdC5xdWVyeVBhcmFtcyxcclxuICAgICAgW3RoaXMucGFyYW1VcmxOYW1lXTogdGV4dCxcclxuICAgIH07XHJcbiAgICB0aGlzLnJvdXRlci5uYXZpZ2F0ZShbXSwgeyByZWxhdGl2ZVRvOiB0aGlzLnJvdXRlLCBxdWVyeVBhcmFtczogbmV3VXJsIH0pO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2V0IHRoZSBkZWZhdWx0IHNlbGVjdGVkIGl0ZW0uXHJcbiAgICogQXBwbHkgYW4gZGVsYXkgb2YgMTAwIG1zIGluIGNhc2Ugb2YgbG9hZGluZyBhIGN1c3RvbSBmb250LlxyXG4gICAqIEdpdmUgcHJpb3JpdHkgdG8gdXNlciBjaG9pY2UgKFVSTCkgb3ZlciB0aGUgZGVmYXVsdCBpdGVtLlxyXG4gICAqL1xyXG4gIHNlbGVjdERlZmF1bHRJdGVtKCkge1xyXG4gICAgaWYgKHRoaXMua2VlcFVzZXJOYXZpZ2F0aW9uQ2hvaWNlKSByZXR1cm47XHJcblxyXG4gICAgZm9yIChjb25zdCBpdGVtIG9mIHRoaXMuaXRlbXMpIHtcclxuICAgICAgaWYgKGl0ZW0uZGVmYXVsdEFjdGl2ZSAmJiAhaXRlbS5kaXNhYmxlKSB7XHJcbiAgICAgICAgdGhpcy50aW1lb3V0SUQgPSB3aW5kb3cuc2V0VGltZW91dCgoKSA9PiB7XHJcbiAgICAgICAgICB0aGlzLmludGVybmFsQXNpZGVTZXJ2aWNlLmludGVybmFsT25TZWxlY3Rpb25DaGFuZ2UubmV4dCh7XHJcbiAgICAgICAgICAgIGVsZW1lbnQ6IGl0ZW0ubmF0aXZlLFxyXG4gICAgICAgICAgICBhbmltYXRlOiBmYWxzZSxcclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0sIDEwMCk7XHJcbiAgICAgICAgdGhpcy5hc2lkZVNlcnZpY2Uub25TZWxlY3Rpb25DaGFuZ2UubmV4dChpdGVtLm5hdGl2ZSk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIENvbXB1dGUgdGhlIG1pbiB3aWR0aCBpbiBwZXJjZW50IGJhc2VkIGluIHRoZSBwcm92aWRlZCB2YWx1ZSBmb3IgdGhlIHBhbmVsIHZpc2liaWxpdHkgcmVkdWN0aW9uIChub24gcmVzcG9uc2l2ZSBtb2RlKS5cclxuICAgKi9cclxuICB1cGRhdGVNaW5XaWR0aFBlcmNlbnREaWZmKCkge1xyXG4gICAgY29uc3QgbWluV2lkdGhQZXJjZW50RGlmZiA9ICh0aGlzLm1pblZpc2libGUgLyB0aGlzLnVzZXJXaWR0aCAtIDEpICogMTAwO1xyXG5cclxuICAgIHRoaXMubmF0aXZlLnN0eWxlLnNldFByb3BlcnR5KFxyXG4gICAgICAnLS1taW4td2lkdGgtcGVyY2VudC1kaWZmJyxcclxuICAgICAgYCR7bWluV2lkdGhQZXJjZW50RGlmZn0lYFxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFJlc3BvbnNpdmUgbW9kZSB0cmlnZ2VyZWQgb24gYmFzaXMgb24gdGhlIGJyZWFrcG9pbnQgc2V0LlxyXG4gICAqL1xyXG4gIGFwcGx5UmVzcG9uc2l2ZShhbmltYXRlID0gdHJ1ZSkge1xyXG4gICAgdGhpcy5yZXNwb25zaXZlTW9kZSA9IHdpbmRvdy5pbm5lcldpZHRoIDwgdGhpcy5yZXNwb25zaXZlQnJlYWtwb2ludDtcclxuICAgIHRoaXMuYXBwbHlBbmltYXRpb25zKGFuaW1hdGUpO1xyXG5cclxuICAgIGlmICh0aGlzLnJlc3BvbnNpdmVNb2RlKSB7XHJcbiAgICAgIHRoaXMubmF0aXZlLnN0eWxlLnNldFByb3BlcnR5KCctLXdpZHRoJywgJzAnKTtcclxuICAgICAgdGhpcy5uYXRpdmUuc3R5bGUuc2V0UHJvcGVydHkoJy0tbWluLXdpZHRoLXZpc2libGUnLCAnMCcpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgdGhpcy5uYXRpdmUuc3R5bGUuc2V0UHJvcGVydHkoJy0td2lkdGgnLCBgJHt0aGlzLnVzZXJXaWR0aH1weGApO1xyXG4gICAgICB0aGlzLm5hdGl2ZS5zdHlsZS5zZXRQcm9wZXJ0eShcclxuICAgICAgICAnLS1taW4td2lkdGgtdmlzaWJsZScsXHJcbiAgICAgICAgYCR7dGhpcy5taW5WaXNpYmxlfXB4YFxyXG4gICAgICApO1xyXG4gICAgICB0aGlzLmFzaWRlRnVsbFdpZHRoUmVzcG9uc2l2ZSA9IGZhbHNlO1xyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuY2QubWFya0ZvckNoZWNrKCk7XHJcbiAgfVxyXG5cclxuICBhcHBseUFuaW1hdGlvbnMoYW5pbWF0ZSA9IHRydWUpIHtcclxuICAgIHRoaXMubmF0aXZlLnN0eWxlLnRyYW5zaXRpb24gPSBhbmltYXRlXHJcbiAgICAgID8gYHdpZHRoICR7dGhpcy5hc2lkZUFuaW1hdGlvblRpbWluZ31gXHJcbiAgICAgIDogJ25vbmUnO1xyXG4gICAgdGhpcy5hc2lkZU5hdGl2ZS5zdHlsZS50cmFuc2l0aW9uID0gYW5pbWF0ZVxyXG4gICAgICA/IHRoaXMuYXNpZGVBbmltYXRpb25UaW1pbmdcclxuICAgICAgOiAnbm9uZSc7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBQb3NpdGlvbiB0aGUgbGVmdCBtYXJrZXIgY29ycmVjdGx5LlxyXG4gICAqIEludmVyc2UgaXQgaW4gcmV2ZXJzZSBtb2RlLlxyXG4gICAqL1xyXG4gIHBvc2l0aW9uTWFya2VyKGVsZW1lbnQ6IEhUTUxFbGVtZW50LCBhbmltYXRlZCA9IHRydWUpIHtcclxuICAgIGlmICghdGhpcy5lbmFibGVNYXJrZXIpIHJldHVybjtcclxuXHJcbiAgICB0aGlzLmFzaWRlQ29udGVudFRvcCA9XHJcbiAgICAgIHRoaXMuYXNpZGVDb250ZW50Lm5hdGl2ZUVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wO1xyXG5cclxuICAgIGNvbnN0IHsgdG9wIH0gPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgY29uc3QgdG9wTWFya2VyID0gdG9wIC0gdGhpcy5hc2lkZUNvbnRlbnRUb3A7XHJcbiAgICBjb25zdCBsZWZ0TWFya2VyID0gdGhpcy5yZXZlcnNlID8gJzEwMCcgOiAnMCc7XHJcblxyXG4gICAgdGhpcy5hc2lkZU1hcmtlci5uYXRpdmVFbGVtZW50LnN0eWxlLmhlaWdodCA9IGVsZW1lbnQuY2xpZW50SGVpZ2h0ICsgJ3B4JztcclxuICAgIHRoaXMuYXNpZGVNYXJrZXIubmF0aXZlRWxlbWVudC5zdHlsZS50cmFuc2l0aW9uID0gYW5pbWF0ZWRcclxuICAgICAgPyBgdG9wICR7dGhpcy5tYXJrZXJBbmltYXRpb25UaW1pbmd9YFxyXG4gICAgICA6ICdub25lJztcclxuICAgIHRoaXMuYXNpZGVNYXJrZXIubmF0aXZlRWxlbWVudC5zdHlsZS50b3AgPSBgJHt0b3BNYXJrZXJ9cHhgO1xyXG4gICAgdGhpcy5hc2lkZU1hcmtlci5uYXRpdmVFbGVtZW50LnN0eWxlLmxlZnQgPSBgJHtsZWZ0TWFya2VyfSVgO1xyXG4gIH1cclxuXHJcbiAgcmVzaXplKCkge1xyXG4gICAgdGhpcy5yZXNpemVyTmF0aXZlLnN0eWxlLnNldFByb3BlcnR5KCctLXJlc2l6ZXItY29sb3InLCB0aGlzLnJlc2l6ZXJDb2xvcik7XHJcbiAgICB0aGlzLnJlc2l6ZXJOYXRpdmUuYWRkRXZlbnRMaXN0ZW5lcihcclxuICAgICAgJ3BvaW50ZXJkb3duJyxcclxuICAgICAgdGhpcy5vblBvaW50ZXJEb3duLmJpbmQodGhpcylcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUcmlnZ2VyZWQgb24gTW91c2UgYW5kIFRvdWNoIGV2ZW50LlxyXG4gICAqIFByZXZlbnQgdGV4dCBzZWxlY3Rpb24sIHNldCB0aGUgQ1NTIHZhcmlhYmxlIGZvciB3aWR0aCBhZGFwdGF0aW9uLCBhZGQgb3RoZXIgZXZlbnQgbGlzdGVuZXJzLlxyXG4gICAqL1xyXG4gIG9uUG9pbnRlckRvd24oZTogUG9pbnRlckV2ZW50KSB7XHJcbiAgICBlLnByZXZlbnREZWZhdWx0KCk7XHJcbiAgICB0aGlzLmFzaWRlTmF0aXZlLnN0eWxlLnRyYW5zaXRpb24gPSAnbm9uZSc7XHJcbiAgICB0aGlzLm5hdGl2ZS5zdHlsZS50cmFuc2l0aW9uID0gJ25vbmUnO1xyXG5cclxuICAgIGNvbnN0IG9uUG9pbnRlck1vdmUgPSAoZTogUG9pbnRlckV2ZW50KSA9PiB7XHJcbiAgICAgIGNvbnN0IHBhZ2VYID0gdGhpcy5yZXZlcnNlID8gd2luZG93LmlubmVyV2lkdGggLSBlLnBhZ2VYIDogZS5wYWdlWDtcclxuICAgICAgaWYgKHBhZ2VYIDwgdGhpcy5taW5XaWR0aCkgcmV0dXJuO1xyXG4gICAgICB0aGlzLm5hdGl2ZS5zdHlsZS5zZXRQcm9wZXJ0eSgnLS13aWR0aCcsIGAke3BhZ2VYfXB4YCk7XHJcbiAgICB9O1xyXG5cclxuICAgIGNvbnN0IG9uUG9pbnRlclVwID0gKCkgPT4ge1xyXG4gICAgICB0aGlzLnVzZXJXaWR0aCA9IHBhcnNlRmxvYXQoXHJcbiAgICAgICAgdGhpcy5uYXRpdmUuc3R5bGUuZ2V0UHJvcGVydHlWYWx1ZSgnLS13aWR0aCcpXHJcbiAgICAgICk7XHJcbiAgICAgIHRoaXMudXBkYXRlTWluV2lkdGhQZXJjZW50RGlmZigpO1xyXG5cclxuICAgICAgaWYgKHRoaXMuc3RvcmVQcmVmZXJlbmNlKSB7XHJcbiAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oJ3VzZXItd2lkdGgnLCBgJHt0aGlzLnVzZXJXaWR0aH1weGApO1xyXG4gICAgICB9XHJcbiAgICAgIGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJtb3ZlJywgb25Qb2ludGVyTW92ZSk7XHJcbiAgICB9O1xyXG5cclxuICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ3BvaW50ZXJtb3ZlJywgb25Qb2ludGVyTW92ZSk7XHJcbiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdwb2ludGVydXAnLCBvblBvaW50ZXJVcCwge1xyXG4gICAgICBvbmNlOiB0cnVlLFxyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBJbiByZXNwb25zaXZlIG1vZGUsIGRpc3BsYXkvaGlkZSBwYW5lbCB0b3RhbGx5LlxyXG4gICAqL1xyXG4gIHNldEFzaWRlRnVsbFdpZHRoKCkge1xyXG4gICAgdGhpcy5hc2lkZUZ1bGxXaWR0aFJlc3BvbnNpdmUgPSAhdGhpcy5hc2lkZUZ1bGxXaWR0aFJlc3BvbnNpdmU7XHJcbiAgICB0aGlzLmFwcGx5QW5pbWF0aW9ucygpO1xyXG4gICAgdGhpcy5jZC5tYXJrRm9yQ2hlY2soKTtcclxuICB9XHJcblxyXG4gIG9uQ29sbGFwc2VCdG5DbGljaygpIHtcclxuICAgIHRoaXMuYXNpZGVJc09wZW4gPSAhdGhpcy5hc2lkZUlzT3BlbjtcclxuICAgIHRoaXMuYXBwbHlBbmltYXRpb25zKCk7XHJcbiAgICB0aGlzLmNkLm1hcmtGb3JDaGVjaygpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogUmV2ZXJzZSB0aGUgcGFuZWwgcG9zaXRpb24uXHJcbiAgICogQW4gb3ZlcmZsb3cgaGlkZGVuIGhhcyB0byBiZSBhcHBsaWVkIGluIHJldmVyc2VkIG1vZGUuXHJcbiAgICovXHJcbiAgcmV2ZXJzZURpc3BsYXkoKSB7XHJcbiAgICB0aGlzLnJldmVyc2UgPSB0cnVlO1xyXG4gICAgZG9jdW1lbnQuYm9keS5zdHlsZS5vdmVyZmxvdyA9ICdoaWRkZW4nO1xyXG4gIH1cclxuXHJcbiAgb25Nb3VzZUVudGVyKCkge1xyXG4gICAgdGhpcy5zaG93Q29sbGFwc2FibGVJY29uID0gdHJ1ZTtcclxuICB9XHJcblxyXG4gIG9uTW91c2VMZWF2ZSgpIHtcclxuICAgIHRoaXMuc2hvd0NvbGxhcHNhYmxlSWNvbiA9IGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogQWRkIGFjdGl2ZSBjbGFzcyB0byB0aGUgZWxlbWVudCBzbyB0aGUgdXNlciBjYW4gb3ZlcmxvYWQgaXQgd2l