@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
270 lines • 46.9 kB
JavaScript
import { ScrollDispatcher } from '@angular/cdk/scrolling';
import { Directive, ElementRef, EventEmitter, inject, Input, NgZone, Output, Renderer2 } from '@angular/core';
import { fromEvent, Subject } from 'rxjs';
import { filter, first, takeUntil } from 'rxjs/operators';
import { DragService } from './drag.service';
import * as i0 from "@angular/core";
export class DragDirective {
constructor() {
this._elementRef = inject(ElementRef);
this._ngZone = inject(NgZone);
this._renderer = inject(Renderer2);
this._scrollDispatcher = inject(ScrollDispatcher);
this._drag = inject(DragService);
/** Detemine if we should show a clone when dragging */
this.clone = false;
/** Allow the dragging to be enabled/disabled */
this.draggable = true;
/** Emit an event when dragging starts */
this.onDragStart = new EventEmitter();
/** Emit an event when the mouse moves while dragging */
this.onDrag = new EventEmitter();
/** Emit an event when the document scrolls while dragging */
this.onDragScroll = new EventEmitter();
/** Emit an event when the dragging finishes */
this.onDragEnd = new EventEmitter();
/** Emit when the user drops an item in a drop area */
this.onDrop = new EventEmitter();
/** Emit when the user drags over a drop area */
this.onDropEnter = new EventEmitter();
/** Emit when the user drags out of a drop area */
this.onDropLeave = new EventEmitter();
/** Store the dragging state */
this._isDragging = false;
/** The offset in pixels currently being scrolled while dragging */
this._scrollOffset = { x: 0, y: 0 };
/** Create an observable from the mouse down event */
this._mousedown$ = fromEvent(this._elementRef.nativeElement, 'mousedown');
/** Create an observable from the mouse move event */
this._mousemove$ = fromEvent(document, 'mousemove');
/** Create an observable from the mouse up event */
this._mouseup$ = fromEvent(document, 'mouseup');
/** Use an observable to unsubscribe from all subscriptions */
this._onDestroy = new Subject();
// ensure all mouse down events on the object are captured
this._mousedown$.pipe(filter(() => this.draggable), takeUntil(this._onDestroy))
.subscribe(this.dragStart.bind(this));
// emit the outputs when drag events occur
this._drag.onDragStart.pipe(filter(() => this._isDragging), takeUntil(this._onDestroy))
.subscribe((dragEvent) => this.onDragStart.emit(dragEvent.event));
this._drag.onDrag.pipe(filter(() => this._isDragging), takeUntil(this._onDestroy))
.subscribe((dragEvent) => this.onDrag.emit(dragEvent.event));
this._drag.onDragEnd.pipe(filter(event => this._isDragging || (this.model && this.model === event.data)), takeUntil(this._onDestroy))
.subscribe(() => this.onDragEnd.emit());
this._drag.onDrop.pipe(filter(() => this._isDragging), takeUntil(this._onDestroy))
.subscribe((event) => this.onDrop.emit(event));
this._drag.onDropEnter.pipe(filter(() => this._isDragging), takeUntil(this._onDestroy))
.subscribe(() => this.onDropEnter.emit());
this._drag.onDropLeave.pipe(filter(() => this._isDragging), takeUntil(this._onDestroy))
.subscribe(() => this.onDropLeave.emit());
}
/** Emit events and create clone when drag starts */
dragStart(event) {
event.preventDefault();
// start listening for scroll events on the nearest scrollable ancestor element
this.createScrollEventListener();
if (this.clone) {
// clone the node
this.cloneNode(event);
}
// apply a class to the element being dragged
this._renderer.addClass(this._elementRef.nativeElement, 'ux-drag-dragging');
// store the dragging state
this._isDragging = true;
// emit the drag start event
this._ngZone.run(() => this._drag.onDragStart.next({ event, group: this.group, data: this.model }));
this._mousemove$.pipe(takeUntil(this._mouseup$), takeUntil(this._onDestroy))
.subscribe(this.dragMove.bind(this));
// When dragging stops emit the drag end
this._mouseup$.pipe(first()).subscribe(this.dragEnd.bind(this));
}
/** Emit event and update clone position when dragging moves */
dragMove(event) {
event.preventDefault();
// scroll the viewport if needed
this.updateScrolling(event);
if (this._clone) {
this.updateNodePosition(event);
}
// emit the drag start event
this._ngZone.run(() => this._drag.onDrag.next({ event, group: this.group, data: this.model }));
}
/** Emit event and destroy clone when dragging ends */
dragEnd() {
// if the drag ended outside of the viewport, stop the scrolling interval
this.stopScrolling();
this.removeScrollEventListener();
// if there was a clone, remove it
if (this._clone) {
this._renderer.removeChild(document.body, this._clone);
this._clone = null;
}
// remove the dragging class
this._renderer.removeClass(this._elementRef.nativeElement, 'ux-drag-dragging');
// emit the on drag end output
this._ngZone.run(() => this._drag.onDragEnd.next({ group: this.group, data: this.model }));
// store the dragging state
this._isDragging = false;
}
/** Emit the onDragScroll event */
scroll() {
const offsetX = this._scrollParent.scrollLeft - this._scrollPosition.left;
const offsetY = this._scrollParent.scrollTop - this._scrollPosition.top;
this.onDragScroll.emit({ offsetX, offsetY });
this._scrollPosition = { top: this._scrollParent.scrollTop, left: this._scrollParent.scrollLeft };
}
/** Create an exact clone of an element */
cloneNode(event) {
// duplicate the node
this._clone = this._elementRef.nativeElement.cloneNode(true);
// store the position within the draggable element
const { top, left, width } = this._elementRef.nativeElement.getBoundingClientRect();
this._offset = { x: event.clientX - left, y: event.clientY - top };
// inline all styles so it looks identical regardless of its position in the DOM
this.inlineStyles(this._elementRef.nativeElement, this._clone);
// IE doesn't always calculate the correct width value using getComputedStyles... use bounding client value instead
this._renderer.setStyle(this._clone, 'width', width + 'px');
// ensure we can easily position the node an it is above all other elements
this._renderer.setAttribute(this._clone, 'aria-hidden', 'true');
this._renderer.setStyle(this._clone, 'position', 'absolute');
this._renderer.setStyle(this._clone, 'z-index', '99999');
// apply a class to allow custom styling
this._renderer.addClass(this._clone, 'ux-drag-dragging-clone');
// insert the cloned element
this._renderer.appendChild(document.body, this._clone);
// set the cloned element initial position
this.updateNodePosition(event);
}
/** Position the clone relative to the mouse */
updateNodePosition(event) {
this._renderer.setStyle(this._clone, 'left', (event.pageX - this._offset.x) + 'px');
this._renderer.setStyle(this._clone, 'top', (event.pageY - this._offset.y) + 'px');
}
/** Inline all styles to ensure styling is consistent regardless of its position in the dom */
inlineStyles(source, target) {
// get all the computed styles from the source element
const styles = getComputedStyle(source);
// inline every specified style
for (let idx = 0; idx < styles.length; idx++) {
const style = styles.item(idx);
if (style !== undefined) {
this._renderer.setStyle(target, styles[idx], styles[style]);
}
}
// ensure we dont capture any move events
this._renderer.setStyle(target, 'pointer-events', 'none');
// do the same for all the child elements
for (let idx = 0; idx < source.children.length; idx++) {
this.inlineStyles(source.children[idx], target.children[idx]);
}
}
/** Unsubscribe from all subscriptions */
ngOnDestroy() {
this.stopScrolling();
this.removeScrollEventListener();
this._onDestroy.next();
this._onDestroy.complete();
}
updateScrolling(event) {
this._scrollOffset = this.getScrollOffsets(this._scrollParent, event);
if (this._scrollOffset.x === 0 && this._scrollOffset.y === 0) {
this.stopScrolling();
}
else {
this.startScrolling(this._scrollParent);
}
}
getScrollParent() {
// Get the nearest ancestor element with the cdkScrollable directive applied
const containers = this._scrollDispatcher.getAncestorScrollContainers(this._elementRef);
if (containers.length > 0) {
return containers[containers.length - 1].getElementRef().nativeElement;
}
return document.documentElement;
}
createScrollEventListener() {
// get the nearest scrollable ancestor
this._scrollParent = this.getScrollParent();
// save the current scroll position to allow calculation of the scroll delta
this._scrollPosition = { top: this._scrollParent.scrollTop, left: this._scrollParent.scrollLeft };
// start listening for scroll events
const target = this._scrollParent === document.documentElement ? 'document' : this._scrollParent;
this._scrollListener = this._renderer.listen(target, 'scroll', this.scroll.bind(this));
}
removeScrollEventListener() {
// remove the scroll event listener
if (this._scrollListener) {
this._scrollListener();
this._scrollListener = null;
}
}
getScrollOffsets(scrollElement, event) {
let scrollX = 0;
let scrollY = 0;
// scroll by at least this much so that it still scrolls if the pointer is exactly at the edge of the scroll element
const minScroll = 5;
const isRoot = (scrollElement === document.documentElement);
const bounds = scrollElement.getBoundingClientRect();
const pointerOffsetX = isRoot ? event.clientX : event.clientX - bounds.x;
const pointerOffsetY = isRoot ? event.clientY : event.clientY - bounds.y;
if (pointerOffsetX <= 0 && scrollElement.scrollLeft > 0) {
scrollX = Math.min(pointerOffsetX, -minScroll);
}
else if (pointerOffsetX >= scrollElement.clientWidth && (scrollElement.scrollLeft + scrollElement.clientWidth) < scrollElement.scrollWidth) {
scrollX = Math.max(pointerOffsetX - scrollElement.clientWidth, minScroll);
}
if (pointerOffsetY <= 0 && scrollElement.scrollTop > 0) {
scrollY = Math.min(pointerOffsetY, -minScroll);
}
else if (pointerOffsetY >= scrollElement.clientHeight && (scrollElement.scrollTop + scrollElement.clientHeight) < scrollElement.scrollHeight) {
scrollY = Math.max(pointerOffsetY - scrollElement.clientHeight, minScroll);
}
return { x: scrollX, y: scrollY };
}
startScrolling(scrollElement) {
if (!this._scrollIntervalHandle) {
this._scrollIntervalHandle = window.setInterval(() => this.performScroll(scrollElement), 100);
}
}
stopScrolling() {
if (this._scrollIntervalHandle) {
clearInterval(this._scrollIntervalHandle);
this._scrollIntervalHandle = 0;
}
}
performScroll(scrollElement) {
scrollElement.scrollLeft += this._scrollOffset.x;
scrollElement.scrollTop += this._scrollOffset.y;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: DragDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.12", type: DragDirective, selector: "[uxDrag]", inputs: { clone: "clone", group: "group", model: "model", draggable: "draggable" }, outputs: { onDragStart: "onDragStart", onDrag: "onDrag", onDragScroll: "onDragScroll", onDragEnd: "onDragEnd", onDrop: "onDrop", onDropEnter: "onDropEnter", onDropLeave: "onDropLeave" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: DragDirective, decorators: [{
type: Directive,
args: [{
selector: '[uxDrag]'
}]
}], ctorParameters: () => [], propDecorators: { clone: [{
type: Input
}], group: [{
type: Input
}], model: [{
type: Input
}], draggable: [{
type: Input
}], onDragStart: [{
type: Output
}], onDrag: [{
type: Output
}], onDragScroll: [{
type: Output
}], onDragEnd: [{
type: Output
}], onDrop: [{
type: Output
}], onDropEnter: [{
type: Output
}], onDropLeave: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJhZy5kaXJlY3RpdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvZGlyZWN0aXZlcy9kcmFnL2RyYWcuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQzFELE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBYSxNQUFNLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3pILE9BQU8sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzFELE9BQU8sRUFBRSxXQUFXLEVBQWUsTUFBTSxnQkFBZ0IsQ0FBQzs7QUFVMUQsTUFBTSxPQUFPLGFBQWE7SUFnRnRCO1FBL0VpQixnQkFBVyxHQUFHLE1BQU0sQ0FBc0IsVUFBVSxDQUFDLENBQUM7UUFFdEQsWUFBTyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUV6QixjQUFTLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRTlCLHNCQUFpQixHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBRTdDLFVBQUssR0FBRyxNQUFNLENBQWlCLFdBQVcsQ0FBQyxDQUFDO1FBRTdELHVEQUF1RDtRQUM5QyxVQUFLLEdBQVksS0FBSyxDQUFDO1FBUWhDLGdEQUFnRDtRQUN2QyxjQUFTLEdBQVksSUFBSSxDQUFDO1FBRW5DLHlDQUF5QztRQUMvQixnQkFBVyxHQUFHLElBQUksWUFBWSxFQUFjLENBQUM7UUFFdkQsd0RBQXdEO1FBQzlDLFdBQU0sR0FBRyxJQUFJLFlBQVksRUFBYyxDQUFDO1FBRWxELDZEQUE2RDtRQUNuRCxpQkFBWSxHQUFHLElBQUksWUFBWSxFQUFtQixDQUFDO1FBRTdELCtDQUErQztRQUNyQyxjQUFTLEdBQUcsSUFBSSxZQUFZLEVBQVEsQ0FBQztRQUUvQyxzREFBc0Q7UUFDNUMsV0FBTSxHQUFHLElBQUksWUFBWSxFQUFLLENBQUM7UUFFekMsZ0RBQWdEO1FBQ3RDLGdCQUFXLEdBQUcsSUFBSSxZQUFZLEVBQVEsQ0FBQztRQUVqRCxrREFBa0Q7UUFDeEMsZ0JBQVcsR0FBRyxJQUFJLFlBQVksRUFBUSxDQUFDO1FBS2pELCtCQUErQjtRQUN2QixnQkFBVyxHQUFZLEtBQUssQ0FBQztRQWNyQyxtRUFBbUU7UUFDM0Qsa0JBQWEsR0FBNkIsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUtqRSxxREFBcUQ7UUFDcEMsZ0JBQVcsR0FBRyxTQUFTLENBQWEsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFFbEcscURBQXFEO1FBQ3BDLGdCQUFXLEdBQUcsU0FBUyxDQUFhLFFBQVEsRUFBRSxXQUFXLENBQUMsQ0FBQztRQUU1RSxtREFBbUQ7UUFDbEMsY0FBUyxHQUFHLFNBQVMsQ0FBYSxRQUFRLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFFeEUsOERBQThEO1FBQ3BELGVBQVUsR0FBRyxJQUFJLE9BQU8sRUFBUSxDQUFDO1FBSXZDLDBEQUEwRDtRQUMxRCxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7YUFDMUUsU0FBUyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFFMUMsMENBQTBDO1FBQzFDLElBQUksQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7YUFDbEYsU0FBUyxDQUFDLENBQUMsU0FBeUIsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFFdEYsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQzthQUM3RSxTQUFTLENBQUMsQ0FBQyxTQUF5QixFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUVqRixJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLFdBQVcsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLEtBQUssS0FBSyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxTQUFTLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO2FBQ2hJLFNBQVMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFFNUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQzthQUM3RSxTQUFTLENBQUMsQ0FBQyxLQUFRLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFFdEQsSUFBSSxD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QUFXO3NCQUFwQixNQUFNO2dCQUdHLE1BQU07c0JBQWYsTUFBTTtnQkFHRyxZQUFZO3NCQUFyQixNQUFNO2dCQUdHLFNBQVM7c0JBQWxCLE1BQU07Z0JBR0csTUFBTTtzQkFBZixNQUFNO2dCQUdHLFdBQVc7c0JBQXBCLE1BQU07Z0JBR0csV0FBVztzQkFBcEIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFNjcm9sbERpc3BhdGNoZXIgfSBmcm9tICdAYW5ndWxhci9jZGsvc2Nyb2xsaW5nJztcbmltcG9ydCB7IERpcmVjdGl2ZSwgRWxlbWVudFJlZiwgRXZlbnRFbWl0dGVyLCBpbmplY3QsIElucHV0LCBOZ1pvbmUsIE9uRGVzdHJveSwgT3V0cHV0LCBSZW5kZXJlcjIgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IGZyb21FdmVudCwgU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZmlsdGVyLCBmaXJzdCwgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgRHJhZ1NlcnZpY2UsIFV4RHJhZ0V2ZW50IH0gZnJvbSAnLi9kcmFnLnNlcnZpY2UnO1xuXG5leHBvcnQgaW50ZXJmYWNlIERyYWdTY3JvbGxFdmVudCB7XG4gICAgb2Zmc2V0WDogbnVtYmVyO1xuICAgIG9mZnNldFk6IG51bWJlcjtcbn1cblxuQERpcmVjdGl2ZSh7XG4gICAgc2VsZWN0b3I6ICdbdXhEcmFnXSdcbn0pXG5leHBvcnQgY2xhc3MgRHJhZ0RpcmVjdGl2ZTxUID0gdW5rbm93bj4gaW1wbGVtZW50cyBPbkRlc3Ryb3kge1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2VsZW1lbnRSZWYgPSBpbmplY3Q8RWxlbWVudFJlZjxFbGVtZW50Pj4oRWxlbWVudFJlZik7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9uZ1pvbmUgPSBpbmplY3QoTmdab25lKTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX3JlbmRlcmVyID0gaW5qZWN0KFJlbmRlcmVyMik7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9zY3JvbGxEaXNwYXRjaGVyID0gaW5qZWN0KFNjcm9sbERpc3BhdGNoZXIpO1xuXG4gICAgcHJpdmF0ZSByZWFkb25seSBfZHJhZyA9IGluamVjdDxEcmFnU2VydmljZTxUPj4oRHJhZ1NlcnZpY2UpO1xuXG4gICAgLyoqIERldGVtaW5lIGlmIHdlIHNob3VsZCBzaG93IGEgY2xvbmUgd2hlbiBkcmFnZ2luZyAqL1xuICAgIEBJbnB1dCgpIGNsb25lOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKiogRGVmaW5lIHRoZSBncm91cCB0aGUgZHJhZyBldmVudCBiZWxvbmdzIHRvICovXG4gICAgQElucHV0KCkgZ3JvdXA6IHN0cmluZztcblxuICAgIC8qKiBBc3NvY2lhdGUgc29tZSBkYXRhIHdpdGggdGhlIGRyYWcgZXZlbnQgKi9cbiAgICBASW5wdXQoKSBtb2RlbDogVDtcblxuICAgIC8qKiBBbGxvdyB0aGUgZHJhZ2dpbmcgdG8gYmUgZW5hYmxlZC9kaXNhYmxlZCAqL1xuICAgIEBJbnB1dCgpIGRyYWdnYWJsZTogYm9vbGVhbiA9IHRydWU7XG5cbiAgICAvKiogRW1pdCBhbiBldmVudCB3aGVuIGRyYWdnaW5nIHN0YXJ0cyAqL1xuICAgIEBPdXRwdXQoKSBvbkRyYWdTdGFydCA9IG5ldyBFdmVudEVtaXR0ZXI8TW91c2VFdmVudD4oKTtcblxuICAgIC8qKiBFbWl0IGFuIGV2ZW50IHdoZW4gdGhlIG1vdXNlIG1vdmVzIHdoaWxlIGRyYWdnaW5nICovXG4gICAgQE91dHB1dCgpIG9uRHJhZyA9IG5ldyBFdmVudEVtaXR0ZXI8TW91c2VFdmVudD4oKTtcblxuICAgIC8qKiBFbWl0IGFuIGV2ZW50IHdoZW4gdGhlIGRvY3VtZW50IHNjcm9sbHMgd2hpbGUgZHJhZ2dpbmcgKi9cbiAgICBAT3V0cHV0KCkgb25EcmFnU2Nyb2xsID0gbmV3IEV2ZW50RW1pdHRlcjxEcmFnU2Nyb2xsRXZlbnQ+KCk7XG5cbiAgICAvKiogRW1pdCBhbiBldmVudCB3aGVuIHRoZSBkcmFnZ2luZyBmaW5pc2hlcyAqL1xuICAgIEBPdXRwdXQoKSBvbkRyYWdFbmQgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG5cbiAgICAvKiogRW1pdCB3aGVuIHRoZSB1c2VyIGRyb3BzIGFuIGl0ZW0gaW4gYSBkcm9wIGFyZWEgKi9cbiAgICBAT3V0cHV0KCkgb25Ecm9wID0gbmV3IEV2ZW50RW1pdHRlcjxUPigpO1xuXG4gICAgLyoqIEVtaXQgd2hlbiB0aGUgdXNlciBkcmFncyBvdmVyIGEgZHJvcCBhcmVhICovXG4gICAgQE91dHB1dCgpIG9uRHJvcEVudGVyID0gbmV3IEV2ZW50RW1pdHRlcjx2b2lkPigpO1xuXG4gICAgLyoqIEVtaXQgd2hlbiB0aGUgdXNlciBkcmFncyBvdXQgb2YgYSBkcm9wIGFyZWEgKi9cbiAgICBAT3V0cHV0KCkgb25Ecm9wTGVhdmUgPSBuZXcgRXZlbnRFbWl0dGVyPHZvaWQ+KCk7XG5cbiAgICAvKiogU3RvcmUgdGhlIGVsZW1lbnQgd2UgaGF2ZSBjbG9uZWQgKi9cbiAgICBwcml2YXRlIF9jbG9uZTogRWxlbWVudDtcblxuICAgIC8qKiBTdG9yZSB0aGUgZHJhZ2dpbmcgc3RhdGUgKi9cbiAgICBwcml2YXRlIF9pc0RyYWdnaW5nOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKiogU3RvcmUgdGhlIG1vdXNlIG9mZnNldCBmb3IgdGhlIGNsb25lZCBlbGVtZW50IHBvc2l0aW9uICovXG4gICAgcHJpdmF0ZSBfb2Zmc2V0OiB7IHg6IG51bWJlciwgeTogbnVtYmVyIH07XG5cbiAgICAvKiogVGhlIG5lYXJlc3Qgc2Nyb2xsaW5nIGFuY2VzdG9yLCBwb3B1bGF0ZWQgd2hpbGUgZHJhZ2dpbmcgKi9cbiAgICBwcml2YXRlIF9zY3JvbGxQYXJlbnQ6IEVsZW1lbnQ7XG5cbiAgICAvKiogVGhlIGN1cnJlbnQgc2Nyb2xsIHBvc2l0aW9uIG9mIF9zY3JvbGxQYXJlbnQgKi9cbiAgICBwcml2YXRlIF9zY3JvbGxQb3NpdGlvbjogeyB0b3A6IG51bWJlciwgbGVmdDogbnVtYmVyIH07XG5cbiAgICAvKiogVGhlIHNjcm9sbCBldmVudCBsaXN0ZW5lciBoYW5kbGUgKi9cbiAgICBwcml2YXRlIF9zY3JvbGxMaXN0ZW5lcjogKCkgPT4gdm9pZDtcblxuICAgIC8qKiBUaGUgb2Zmc2V0IGluIHBpeGVscyBjdXJyZW50bHkgYmVpbmcgc2Nyb2xsZWQgd2hpbGUgZHJhZ2dpbmcgKi9cbiAgICBwcml2YXRlIF9zY3JvbGxPZmZzZXQ6IHsgeDogbnVtYmVyLCB5OiBudW1iZXIgfSA9IHsgeDogMCwgeTogMCB9O1xuXG4gICAgLyoqIFRoZSBjdXJyZW50IGBzZXRJbnRlcnZhbGAgaGFuZGxlIGZvciBwZXJpb2RpYyBzY3JvbGxpbmcgKi9cbiAgICBwcml2YXRlIF9zY3JvbGxJbnRlcnZhbEhhbmRsZTogbnVtYmVyO1xuXG4gICAgLyoqIENyZWF0ZSBhbiBvYnNlcnZhYmxlIGZyb20gdGhlIG1vdXNlIGRvd24gZXZlbnQgKi9cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9tb3VzZWRvd24kID0gZnJvbUV2ZW50PE1vdXNlRXZlbnQ+KHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudCwgJ21vdXNlZG93bicpO1xuXG4gICAgLyoqIENyZWF0ZSBhbiBvYnNlcnZhYmxlIGZyb20gdGhlIG1vdXNlIG1vdmUgZXZlbnQgKi9cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9tb3VzZW1vdmUkID0gZnJvbUV2ZW50PE1vdXNlRXZlbnQ+KGRvY3VtZW50LCAnbW91c2Vtb3ZlJyk7XG5cbiAgICAvKiogQ3JlYXRlIGFuIG9ic2VydmFibGUgZnJvbSB0aGUgbW91c2UgdXAgZXZlbnQgKi9cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9tb3VzZXVwJCA9IGZyb21FdmVudDxNb3VzZUV2ZW50Pihkb2N1bWVudCwgJ21vdXNldXAnKTtcblxuICAgIC8qKiBVc2UgYW4gb2JzZXJ2YWJsZSB0byB1bnN1YnNjcmliZSBmcm9tIGFsbCBzdWJzY3JpcHRpb25zICovXG4gICAgcHJvdGVjdGVkIF9vbkRlc3Ryb3kgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gICAgY29uc3RydWN0b3IoKSB7XG5cbiAgICAgICAgLy8gZW5zdXJlIGFsbCBtb3VzZSBkb3duIGV2ZW50cyBvbiB0aGUgb2JqZWN0IGFyZSBjYXB0dXJlZFxuICAgICAgICB0aGlzLl9tb3VzZWRvd24kLnBpcGUoZmlsdGVyKCgpID0+IHRoaXMuZHJhZ2dhYmxlKSwgdGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKHRoaXMuZHJhZ1N0YXJ0LmJpbmQodGhpcykpO1xuXG4gICAgICAgIC8vIGVtaXQgdGhlIG91dHB1dHMgd2hlbiBkcmFnIGV2ZW50cyBvY2N1clxuICAgICAgICB0aGlzLl9kcmFnLm9uRHJhZ1N0YXJ0LnBpcGUoZmlsdGVyKCgpID0+IHRoaXMuX2lzRHJhZ2dpbmcpLCB0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKGRyYWdFdmVudDogVXhEcmFnRXZlbnQ8VD4pID0+IHRoaXMub25EcmFnU3RhcnQuZW1pdChkcmFnRXZlbnQuZXZlbnQpKTtcblxuICAgICAgICB0aGlzLl9kcmFnLm9uRHJhZy5waXBlKGZpbHRlcigoKSA9PiB0aGlzLl9pc0RyYWdnaW5nKSwgdGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKChkcmFnRXZlbnQ6IFV4RHJhZ0V2ZW50PFQ+KSA9PiB0aGlzLm9uRHJhZy5lbWl0KGRyYWdFdmVudC5ldmVudCkpO1xuXG4gICAgICAgIHRoaXMuX2RyYWcub25EcmFnRW5kLnBpcGUoZmlsdGVyKGV2ZW50ID0+IHRoaXMuX2lzRHJhZ2dpbmcgfHwgKHRoaXMubW9kZWwgJiYgdGhpcy5tb2RlbCA9PT0gZXZlbnQuZGF0YSkpLCB0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKCkgPT4gdGhpcy5vbkRyYWdFbmQuZW1pdCgpKTtcblxuICAgICAgICB0aGlzLl9kcmFnLm9uRHJvcC5waXBlKGZpbHRlcigoKSA9PiB0aGlzLl9pc0RyYWdnaW5nKSwgdGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKChldmVudDogVCkgPT4gdGhpcy5vbkRyb3AuZW1pdChldmVudCkpO1xuXG4gICAgICAgIHRoaXMuX2RyYWcub25Ecm9wRW50ZXIucGlwZShmaWx0ZXIoKCkgPT4gdGhpcy5faXNEcmFnZ2luZyksIHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLm9uRHJvcEVudGVyLmVtaXQoKSk7XG5cbiAgICAgICAgdGhpcy5fZHJhZy5vbkRyb3BMZWF2ZS5waXBlKGZpbHRlcigoKSA9PiB0aGlzLl9pc0RyYWdnaW5nKSwgdGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHRoaXMub25Ecm9wTGVhdmUuZW1pdCgpKTtcbiAgICB9XG5cbiAgICAvKiogRW1pdCBldmVudHMgYW5kIGNyZWF0ZSBjbG9uZSB3aGVuIGRyYWcgc3RhcnRzICovXG4gICAgZHJhZ1N0YXJ0KGV2ZW50OiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cbiAgICAgICAgLy8gc3RhcnQgbGlzdGVuaW5nIGZvciBzY3JvbGwgZXZlbnRzIG9uIHRoZSBuZWFyZXN0IHNjcm9sbGFibGUgYW5jZXN0b3IgZWxlbWVudFxuICAgICAgICB0aGlzLmNyZWF0ZVNjcm9sbEV2ZW50TGlzdGVuZXIoKTtcblxuICAgICAgICBpZiAodGhpcy5jbG9uZSkge1xuICAgICAgICAgICAgLy8gY2xvbmUgdGhlIG5vZGVcbiAgICAgICAgICAgIHRoaXMuY2xvbmVOb2RlKGV2ZW50KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGFwcGx5IGEgY2xhc3MgdG8gdGhlIGVsZW1lbnQgYmVpbmcgZHJhZ2dlZFxuICAgICAgICB0aGlzLl9yZW5kZXJlci5hZGRDbGFzcyh0aGlzLl9lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQsICd1eC1kcmFnLWRyYWdnaW5nJyk7XG5cbiAgICAgICAgLy8gc3RvcmUgdGhlIGRyYWdnaW5nIHN0YXRlXG4gICAgICAgIHRoaXMuX2lzRHJhZ2dpbmcgPSB0cnVlO1xuXG4gICAgICAgIC8vIGVtaXQgdGhlIGRyYWcgc3RhcnQgZXZlbnRcbiAgICAgICAgdGhpcy5fbmdab25lLnJ1bigoKSA9PiB0aGlzLl9kcmFnLm9uRHJhZ1N0YXJ0Lm5leHQoeyBldmVudCwgZ3JvdXA6IHRoaXMuZ3JvdXAsIGRhdGE6IHRoaXMubW9kZWwgfSkpO1xuXG4gICAgICAgIHRoaXMuX21vdXNlbW92ZSQucGlwZSh0YWtlVW50aWwodGhpcy5fbW91c2V1cCQpLCB0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUodGhpcy5kcmFnTW92ZS5iaW5kKHRoaXMpKTtcblxuICAgICAgICAvLyBXaGVuIGRyYWdnaW5nIHN0b3BzIGVtaXQgdGhlIGRyYWcgZW5kXG4gICAgICAgIHRoaXMuX21vdXNldXAkLnBpcGUoZmlyc3QoKSkuc3Vic2NyaWJlKHRoaXMuZHJhZ0VuZC5iaW5kKHRoaXMpKTtcbiAgICB9XG5cbiAgICAvKiogRW1pdCBldmVudCBhbmQgdXBkYXRlIGNsb25lIHBvc2l0aW9uIHdoZW4gZHJhZ2dpbmcgbW92ZXMgKi9cbiAgICBkcmFnTW92ZShldmVudDogTW91c2VFdmVudCk6IHZvaWQge1xuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXG4gICAgICAgIC8vIHNjcm9sbCB0aGUgdmlld3BvcnQgaWYgbmVlZGVkXG4gICAgICAgIHRoaXMudXBkYXRlU2Nyb2xsaW5nKGV2ZW50KTtcblxuICAgICAgICBpZiAodGhpcy5fY2xvbmUpIHtcbiAgICAgICAgICAgIHRoaXMudXBkYXRlTm9kZVBvc2l0aW9uKGV2ZW50KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGVtaXQgdGhlIGRyYWcgc3RhcnQgZXZlbnRcbiAgICAgICAgdGhpcy5fbmdab25lLnJ1bigoKSA9PiB0aGlzLl9kcmFnLm9uRHJhZy5uZXh0KHsgZXZlbnQsIGdyb3VwOiB0aGlzLmdyb3VwLCBkYXRhOiB0aGlzLm1vZGVsIH0pKTtcbiAgICB9XG5cbiAgICAvKiogRW1pdCBldmVudCBhbmQgZGVzdHJveSBjbG9uZSB3aGVuIGRyYWdnaW5nIGVuZHMgKi9cbiAgICBkcmFnRW5kKCk6IHZvaWQge1xuXG4gICAgICAgIC8vIGlmIHRoZSBkcmFnIGVuZGVkIG91dHNpZGUgb2YgdGhlIHZpZXdwb3J0LCBzdG9wIHRoZSBzY3JvbGxpbmcgaW50ZXJ2YWxcbiAgICAgICAgdGhpcy5zdG9wU2Nyb2xsaW5nKCk7XG5cbiAgICAgICAgdGhpcy5yZW1vdmVTY3JvbGxFdmVudExpc3RlbmVyKCk7XG5cbiAgICAgICAgLy8gaWYgdGhlcmUgd2FzIGEgY2xvbmUsIHJlbW92ZSBpdFxuICAgICAgICBpZiAodGhpcy5fY2xvbmUpIHtcbiAgICAgICAgICAgIHRoaXMuX3JlbmRlcmVyLnJlbW92ZUNoaWxkKGRvY3VtZW50LmJvZHksIHRoaXMuX2Nsb25lKTtcbiAgICAgICAgICAgIHRoaXMuX2Nsb25lID0gbnVsbDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHJlbW92ZSB0aGUgZHJhZ2dpbmcgY2xhc3NcbiAgICAgICAgdGhpcy5fcmVuZGVyZXIucmVtb3ZlQ2xhc3ModGhpcy5fZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50LCAndXgtZHJhZy1kcmFnZ2luZycpO1xuXG4gICAgICAgIC8vIGVtaXQgdGhlIG9uIGRyYWcgZW5kIG91dHB1dFxuICAgICAgICB0aGlzLl9uZ1pvbmUucnVuKCgpID0+IHRoaXMuX2RyYWcub25EcmFnRW5kLm5leHQoeyBncm91cDogdGhpcy5ncm91cCwgZGF0YTogdGhpcy5tb2RlbCB9KSk7XG5cbiAgICAgICAgLy8gc3RvcmUgdGhlIGRyYWdnaW5nIHN0YXRlXG4gICAgICAgIHRoaXMuX2lzRHJhZ2dpbmcgPSBmYWxzZTtcbiAgICB9XG5cbiAgICAvKiogRW1pdCB0aGUgb25EcmFnU2Nyb2xsIGV2ZW50ICovXG4gICAgc2Nyb2xsKCk6IHZvaWQge1xuICAgICAgICBjb25zdCBvZmZzZXRYID0gdGhpcy5fc2Nyb2xsUGFyZW50LnNjcm9sbExlZnQgLSB0aGlzLl9zY3JvbGxQb3NpdGlvbi5sZWZ0O1xuICAgICAgICBjb25zdCBvZmZzZXRZID0gdGhpcy5fc2Nyb2xsUGFyZW50LnNjcm9sbFRvcCAtIHRoaXMuX3Njcm9sbFBvc2l0aW9uLnRvcDtcblxuICAgICAgICB0aGlzLm9uRHJhZ1Njcm9sbC5lbWl0KHsgb2Zmc2V0WCwgb2Zmc2V0WSB9KTtcblxuICAgICAgICB0aGlzLl9zY3JvbGxQb3NpdGlvbiA9IHsgdG9wOiB0aGlzLl9zY3JvbGxQYXJlbnQuc2Nyb2xsVG9wLCBsZWZ0OiB0aGlzLl9zY3JvbGxQYXJlbnQuc2Nyb2xsTGVmdCB9O1xuICAgIH1cblxuICAgIC8qKiBDcmVhdGUgYW4gZXhhY3QgY2xvbmUgb2YgYW4gZWxlbWVudCAqL1xuICAgIGNsb25lTm9kZShldmVudDogTW91c2VFdmVudCk6IHZvaWQge1xuXG4gICAgICAgIC8vIGR1cGxpY2F0ZSB0aGUgbm9kZVxuICAgICAgICB0aGlzLl9jbG9uZSA9IHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5jbG9uZU5vZGUodHJ1ZSkgYXMgRWxlbWVudDtcblxuICAgICAgICAvLyBzdG9yZSB0aGUgcG9zaXRpb24gd2l0aGluIHRoZSBkcmFnZ2FibGUgZWxlbWVudFxuICAgICAgICBjb25zdCB7IHRvcCwgbGVmdCwgd2lkdGggfSA9IHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgdGhpcy5fb2Zmc2V0ID0geyB4OiBldmVudC5jbGllbnRYIC0gbGVmdCwgeTogZXZlbnQuY2xpZW50WSAtIHRvcCB9O1xuXG4gICAgICAgIC8vIGlubGluZSBhbGwgc3R5bGVzIHNvIGl0IGxvb2tzIGlkZW50aWNhbCByZWdhcmRsZXNzIG9mIGl0cyBwb3NpdGlvbiBpbiB0aGUgRE9NXG4gICAgICAgIHRoaXMuaW5saW5lU3R5bGVzKHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudCwgdGhpcy5fY2xvbmUpO1xuXG4gICAgICAgIC8vIElFIGRvZXNuJ3QgYWx3YXlzIGNhbGN1bGF0ZSB0aGUgY29ycmVjdCB3aWR0aCB2YWx1ZSB1c2luZyBnZXRDb21wdXRlZFN0eWxlcy4uLiB1c2UgYm91bmRpbmcgY2xpZW50IHZhbHVlIGluc3RlYWRcbiAgICAgICAgdGhpcy5fcmVuZGVyZXIuc2V0U3R5bGUodGhpcy5fY2xvbmUsICd3aWR0aCcsIHdpZHRoICsgJ3B4Jyk7XG5cbiAgICAgICAgLy8gZW5zdXJlIHdlIGNhbiBlYXNpbHkgcG9zaXRpb24gdGhlIG5vZGUgYW4gaXQgaXMgYWJvdmUgYWxsIG90aGVyIGVsZW1lbnRzXG4gICAgICAgIHRoaXMuX3JlbmRlcmVyLnNldEF0dHJpYnV0ZSh0aGlzLl9jbG9uZSwgJ2FyaWEtaGlkZGVuJywgJ3RydWUnKTtcbiAgICAgICAgdGhpcy5fcmVuZGVyZXIuc2V0U3R5bGUodGhpcy5fY2xvbmUsICdwb3NpdGlvbicsICdhYnNvbHV0ZScpO1xuICAgICAgICB0aGlzLl9yZW5kZXJlci5zZXRTdHlsZSh0aGlzLl9jbG9uZSwgJ3otaW5kZXgnLCAnOTk5OTknKTtcblxuICAgICAgICAvLyBhcHBseSBhIGNsYXNzIHRvIGFsbG93IGN1c3RvbSBzdHlsaW5nXG4gICAgICAgIHRoaXMuX3JlbmRlcmVyLmFkZENsYXNzKHRoaXMuX2Nsb25lLCAndXgtZHJhZy1kcmFnZ2luZy1jbG9uZScpO1xuXG4gICAgICAgIC8vIGluc2VydCB0aGUgY2xvbmVkIGVsZW1lbnRcbiAgICAgICAgdGhpcy5fcmVuZGVyZXIuYXBwZW5kQ2hpbGQoZG9jdW1lbnQuYm9keSwgdGhpcy5fY2xvbmUpO1xuXG4gICAgICAgIC8vIHNldCB0aGUgY2xvbmVkIGVsZW1lbnQgaW5pdGlhbCBwb3NpdGlvblxuICAgICAgICB0aGlzLnVwZGF0ZU5vZGVQb3NpdGlvbihldmVudCk7XG4gICAgfVxuXG4gICAgLyoqIFBvc2l0aW9uIHRoZSBjbG9uZSByZWxhdGl2ZSB0byB0aGUgbW91c2UgKi9cbiAgICB1cGRhdGVOb2RlUG9zaXRpb24oZXZlbnQ6IE1vdXNlRXZlbnQpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5fcmVuZGVyZXIuc2V0U3R5bGUodGhpcy5fY2xvbmUsICdsZWZ0JywgKGV2ZW50LnBhZ2VYIC0gdGhpcy5fb2Zmc2V0LngpICsgJ3B4Jyk7XG4gICAgICAgIHRoaXMuX3JlbmRlcmVyLnNldFN0eWxlKHRoaXMuX2Nsb25lLCAndG9wJywgKGV2ZW50LnBhZ2VZIC0gdGhpcy5fb2Zmc2V0LnkpICsgJ3B4Jyk7XG4gICAgfVxuXG4gICAgLyoqIElubGluZSBhbGwgc3R5bGVzIHRvIGVuc3VyZSBzdHlsaW5nIGlzIGNvbnNpc3RlbnQgcmVnYXJkbGVzcyBvZiBpdHMgcG9zaXRpb24gaW4gdGhlIGRvbSAqL1xuICAgIGlubGluZVN0eWxlcyhzb3VyY2U6IEVsZW1lbnQsIHRhcmdldDogRWxlbWVudCk6IHZvaWQge1xuICAgICAgICAvLyBnZXQgYWxsIHRoZSBjb21wdXRlZCBzdHlsZXMgZnJvbSB0aGUgc291cmNlIGVsZW1lbnRcbiAgICAgICAgY29uc3Qgc3R5bGVzID0gZ2V0Q29tcHV0ZWRTdHlsZShzb3VyY2UpO1xuXG4gICAgICAgIC8vIGlubGluZSBldmVyeSBzcGVjaWZpZWQgc3R5bGVcbiAgICAgICAgZm9yIChsZXQgaWR4ID0gMDsgaWR4IDwgc3R5bGVzLmxlbmd0aDsgaWR4KyspIHtcbiAgICAgICAgICAgIGNvbnN0IHN0eWxlID0gc3R5bGVzLml0ZW0oaWR4KTtcblxuICAgICAgICAgICAgaWYgKHN0eWxlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLl9yZW5kZXJlci5zZXRTdHlsZSh0YXJnZXQsIHN0eWxlc1tpZHhdLCBzdHlsZXNbc3R5bGVdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGVuc3VyZSB3ZSBkb250IGNhcHR1cmUgYW55IG1vdmUgZXZlbnRzXG4gICAgICAgIHRoaXMuX3JlbmRlcmVyLnNldFN0eWxlKHRhcmdldCwgJ3BvaW50ZXItZXZlbnRzJywgJ25vbmUnKTtcblxuICAgICAgICAvLyBkbyB0aGUgc2FtZSBmb3IgYWxsIHRoZSBjaGlsZCBlbGVtZW50c1xuICAgICAgICBmb3IgKGxldCBpZHggPSAwOyBpZHggPCBzb3VyY2UuY2hpbGRyZW4ubGVuZ3RoOyBpZHgrKykge1xuICAgICAgICAgICAgdGhpcy5pbmxpbmVTdHlsZXMoc291cmNlLmNoaWxkcmVuW2lkeF0sIHRhcmdldC5jaGlsZHJlbltpZHhdKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKiBVbnN1YnNjcmliZSBmcm9tIGFsbCBzdWJzY3JpcHRpb25zICovXG4gICAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuc3RvcFNjcm9sbGluZygpO1xuICAgICAgICB0aGlzLnJlbW92ZVNjcm9sbEV2ZW50TGlzdGVuZXIoKTtcbiAgICAgICAgdGhpcy5fb25EZXN0cm95Lm5leHQoKTtcbiAgICAgICAgdGhpcy5fb25EZXN0cm95LmNvbXBsZXRlKCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSB1cGRhdGVTY3JvbGxpbmcoZXZlbnQ6IE1vdXNlRXZlbnQpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5fc2Nyb2xsT2Zmc2V0ID0gdGhpcy5nZXRTY3JvbGxPZmZzZXRzKHRoaXMuX3Njcm9sbFBhcmVudCwgZXZlbnQpO1xuXG4gICAgICAgIGlmICh0aGlzLl9zY3JvbGxPZmZzZXQueCA9PT0gMCAmJiB0aGlzLl9zY3JvbGxPZmZzZXQueSA9PT0gMCkge1xuICAgICAgICAgICAgdGhpcy5zdG9wU2Nyb2xsaW5nKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnN0YXJ0U2Nyb2xsaW5nKHRoaXMuX3Njcm9sbFBhcmVudCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGdldFNjcm9sbFBhcmVudCgpOiBFbGVtZW50IHtcbiAgICAgICAgLy8gR2V0IHRoZSBuZWFyZXN0IGFuY2VzdG9yIGVsZW1lbnQgd2l0aCB0aGUgY2RrU2Nyb2xsYWJsZSBkaXJlY3RpdmUgYXBwbGllZFxuICAgICAgICBjb25zdCBjb250YWluZXJzID0gdGhpcy5fc2Nyb2xsRGlzcGF0Y2hlci5nZXRBbmNlc3RvclNjcm9sbENvbnRhaW5lcnModGhpcy5fZWxlbWVudFJlZik7XG4gICAgICAgIGlmIChjb250YWluZXJzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHJldHVybiBjb250YWluZXJzW2NvbnRhaW5lcnMubGVuZ3RoIC0gMV0uZ2V0RWxlbWVudFJlZigpLm5hdGl2ZUVsZW1lbnQ7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50O1xuICAgIH1cblxuICAgIHByaXZhdGUgY3JlYXRlU2Nyb2xsRXZlbnRMaXN0ZW5lcigpOiB2b2lkIHtcbiAgICAgICAgLy8gZ2V0IHRoZSBuZWFyZXN0IHNjcm9sbGFibGUgYW5jZXN0b3JcbiAgICAgICAgdGhpcy5fc2Nyb2xsUGFyZW50ID0gdGhpcy5nZXRTY3JvbGxQYXJlbnQoKTtcblxuICAgICAgICAvLyBzYXZlIHRoZSBjdXJyZW50IHNjcm9sbCBwb3NpdGlvbiB0byBhbGxvdyBjYWxjdWxhdGlvbiBvZiB0aGUgc2Nyb2xsIGRlbHRhXG4gICAgICAgIHRoaXMuX3Njcm9sbFBvc2l0aW9uID0geyB0b3A6IHRoaXMuX3Njcm9sbFBhcmVudC5zY3JvbGxUb3AsIGxlZnQ6IHRoaXMuX3Njcm9sbFBhcmVudC5zY3JvbGxMZWZ0IH07XG5cbiAgICAgICAgLy8gc3RhcnQgbGlzdGVuaW5nIGZvciBzY3JvbGwgZXZlbnRzXG4gICAgICAgIGNvbnN0IHRhcmdldCA9IHRoaXMuX3Njcm9sbFBhcmVudCA9PT0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50ID8gJ2RvY3VtZW50JyA6IHRoaXMuX3Njcm9sbFBhcmVudDtcbiAgICAgICAgdGhpcy5fc2Nyb2xsTGlzdGVuZXIgPSB0aGlzLl9yZW5kZXJlci5saXN0ZW4odGFyZ2V0LCAnc2Nyb2xsJywgdGhpcy5zY3JvbGwuYmluZCh0aGlzKSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSByZW1vdmVTY3JvbGxFdmVudExpc3RlbmVyKCk6IHZvaWQge1xuICAgICAgICAvLyByZW1vdmUgdGhlIHNjcm9sbCBldmVudCBsaXN0ZW5lclxuICAgICAgICBpZiAodGhpcy5fc2Nyb2xsTGlzdGVuZXIpIHtcbiAgICAgICAgICAgIHRoaXMuX3Njcm9sbExpc3RlbmVyKCk7XG4gICAgICAgICAgICB0aGlzLl9zY3JvbGxMaXN0ZW5lciA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGdldFNjcm9sbE9mZnNldHMoc2Nyb2xsRWxlbWVudDogRWxlbWVudCwgZXZlbnQ6IE1vdXNlRXZlbnQpOiB7IHg6IG51bWJlciwgeTogbnVtYmVyIH0ge1xuICAgICAgICBsZXQgc2Nyb2xsWCA9IDA7XG4gICAgICAgIGxldCBzY3JvbGxZID0gMDtcblxuICAgICAgICAvLyBzY3JvbGwgYnkgYXQgbGVhc3QgdGhpcyBtdWNoIHNvIHRoYXQgaXQgc3RpbGwgc2Nyb2xscyBpZiB0aGUgcG9pbnRlciBpcyBleGFjdGx5IGF0IHRoZSBlZGdlIG9mIHRoZSBzY3JvbGwgZWxlbWVudFxuICAgICAgICBjb25zdCBtaW5TY3JvbGwgPSA1O1xuXG4gICAgICAgIGNvbnN0IGlzUm9vdCA9IChzY3JvbGxFbGVtZW50ID09PSBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQpO1xuXG4gICAgICAgIGNvbnN0IGJvdW5kcyA9IHNjcm9sbEVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAgIGNvbnN0IHBvaW50ZXJPZmZzZXRYID0gaXNSb290ID8gZXZlbnQuY2xpZW50WCA6IGV2ZW50LmNsaWVudFggLSBib3VuZHMueDtcbiAgICAgICAgY29uc3QgcG9pbnRlck9mZnNldFkgPSBpc1Jvb3QgPyBldmVudC5jbGllbnRZIDogZXZlbnQuY2xpZW50WSAtIGJvdW5kcy55O1xuXG4gICAgICAgIGlmIChwb2ludGVyT2Zmc2V0WCA8PSAwICYmIHNjcm9sbEVsZW1lbnQuc2Nyb2xsTGVmdCA+IDApIHtcbiAgICAgICAgICAgIHNjcm9sbFggPSBNYXRoLm1pbihwb2ludGVyT2Zmc2V0WCwgLW1pblNjcm9sbCk7XG4gICAgICAgIH0gZWxzZSBpZiAocG9pbnRlck9mZnNldFggPj0gc2Nyb2xsRWxlbWVudC5jbGllbnRXaWR0aCAmJiAoc2Nyb2xsRWxlbWVudC5zY3JvbGxMZWZ0ICsgc2Nyb2xsRWxlbWVudC5jbGllbnRXaWR0aCkgPCBzY3JvbGxFbGVtZW50LnNjcm9sbFdpZHRoKSB7XG4gICAgICAgICAgICBzY3JvbGxYID0gTWF0aC5tYXgocG9pbnRlck9mZnNldFggLSBzY3JvbGxFbGVtZW50LmNsaWVudFdpZHRoLCBtaW5TY3JvbGwpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHBvaW50ZXJPZmZzZXRZIDw9IDAgJiYgc2Nyb2xsRWxlbWVudC5zY3JvbGxUb3AgPiAwKSB7XG4gICAgICAgICAgICBzY3JvbGxZID0gTWF0aC5taW4ocG9pbnRlck9mZnNldFksIC1taW5TY3JvbGwpO1xuICAgICAgICB9IGVsc2UgaWYgKHBvaW50ZXJPZmZzZXRZID49IHNjcm9sbEVsZW1lbnQuY2xpZW50SGVpZ2h0ICYmIChzY3JvbGxFbGVtZW50LnNjcm9sbFRvcCArIHNjcm9sbEVsZW1lbnQuY2xpZW50SGVpZ2h0KSA8IHNjcm9sbEVsZW1lbnQuc2Nyb2xsSGVpZ2h0KSB7XG4gICAgICAgICAgICBzY3JvbGxZID0gTWF0aC5tYXgocG9pbnRlck9mZnNldFkgLSBzY3JvbGxFbGVtZW50LmNsaWVudEhlaWdodCwgbWluU2Nyb2xsKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB7IHg6IHNjcm9sbFgsIHk6IHNjcm9sbFkgfTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHN0YXJ0U2Nyb2xsaW5nKHNjcm9sbEVsZW1lbnQ6IEVsZW1lbnQpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLl9zY3JvbGxJbnRlcnZhbEhhbmRsZSkge1xuICAgICAgICAgICAgdGhpcy5fc2Nyb2xsSW50ZXJ2YWxIYW5kbGUgPSB3aW5kb3cuc2V0SW50ZXJ2YWwoKCkgPT4gdGhpcy5wZXJmb3JtU2Nyb2xsKHNjcm9sbEVsZW1lbnQpLCAxMDApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzdG9wU2Nyb2xsaW5nKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5fc2Nyb2xsSW50ZXJ2YWxIYW5kbGUpIHtcbiAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5fc2Nyb2xsSW50ZXJ2YWxIYW5kbGUpO1xuICAgICAgICAgICAgdGhpcy5fc2Nyb2xsSW50ZXJ2YWxIYW5kbGUgPSAwO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBwZXJmb3JtU2Nyb2xsKHNjcm9sbEVsZW1lbnQ6IEVsZW1lbnQpOiB2b2lkIHtcbiAgICAgICAgc2Nyb2xsRWxlbWVudC5zY3JvbGxMZWZ0ICs9IHRoaXMuX3Njcm9sbE9mZnNldC54O1xuICAgICAgICBzY3JvbGxFbGVtZW50LnNjcm9sbFRvcCArPSB0aGlzLl9zY3JvbGxPZmZzZXQueTtcbiAgICB9XG59XG4iXX0=