angular-grid-layout-ngx13
Version:
Grid Layout with draggable and resizable items for Angular
392 lines • 71.4 kB
JavaScript
import { ChangeDetectionStrategy, Component, ContentChildren, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
import { coerceNumberProperty } from './coercion/number-property';
import { KtdGridItemComponent } from './grid-item/grid-item.component';
import { combineLatest, merge, NEVER, Observable, of } from 'rxjs';
import { exhaustMap, map, startWith, switchMap, takeUntil } from 'rxjs/operators';
import { ktdGridItemDragging, ktdGridItemResizing } from './utils/grid.utils';
import { compact } from './utils/react-grid-layout.utils';
import { GRID_ITEM_GET_RENDER_DATA_TOKEN } from './grid.definitions';
import { ktdMouseOrTouchEnd, ktdPointerClientX, ktdPointerClientY } from './utils/pointer.utils';
import { getMutableClientRect } from './utils/client-rect';
import { ktdGetScrollTotalRelativeDifference$, ktdScrollIfNearElementClientRect$ } from './utils/scroll';
import { coerceBooleanProperty } from './coercion/boolean-property';
import * as i0 from "@angular/core";
import * as i1 from "./grid.service";
function getDragResizeEventData(gridItem, layout) {
return {
layout,
layoutItem: layout.find((item) => item.id === gridItem.id),
gridItemRef: gridItem
};
}
function layoutToRenderItems(config, width, height) {
const { cols, rowHeight, layout } = config;
const renderItems = {};
for (const item of layout) {
renderItems[item.id] = {
id: item.id,
top: item.y === 0 ? 0 : item.y * rowHeight,
left: item.x * (width / cols),
width: item.w * (width / cols),
height: item.h * rowHeight
};
}
return renderItems;
}
function getGridHeight(layout, rowHeight) {
return layout.reduce((acc, cur) => Math.max(acc, (cur.y + cur.h) * rowHeight), 0);
}
// tslint:disable-next-line
export function parseRenderItemToPixels(renderItem) {
return {
id: renderItem.id,
top: `${renderItem.top}px`,
left: `${renderItem.left}px`,
width: `${renderItem.width}px`,
height: `${renderItem.height}px`
};
}
// tslint:disable-next-line:ktd-prefix-code
export function __gridItemGetRenderDataFactoryFunc(gridCmp) {
// tslint:disable-next-line:only-arrow-functions
return function (id) {
return parseRenderItemToPixels(gridCmp.getItemRenderData(id));
};
}
export function ktdGridItemGetRenderDataFactoryFunc(gridCmp) {
// Workaround explained: https://github.com/ng-packagr/ng-packagr/issues/696#issuecomment-387114613
const resultFunc = __gridItemGetRenderDataFactoryFunc(gridCmp);
return resultFunc;
}
export class KtdGridComponent {
constructor(gridService, elementRef, renderer, ngZone) {
this.gridService = gridService;
this.elementRef = elementRef;
this.renderer = renderer;
this.ngZone = ngZone;
/** Emits when layout change */
this.layoutUpdated = new EventEmitter();
/** Emits when drag starts */
this.dragStarted = new EventEmitter();
/** Emits when resize starts */
this.resizeStarted = new EventEmitter();
/** Emits when drag ends */
this.dragEnded = new EventEmitter();
/** Emits when resize ends */
this.resizeEnded = new EventEmitter();
/**
* Parent element that contains the scroll. If an string is provided it would search that element by id on the dom.
* If no data provided or null autoscroll is not performed.
*/
this.scrollableParent = null;
this._compactOnPropsChange = true;
this._preventCollision = false;
this._scrollSpeed = 2;
this._compactType = 'vertical';
this._rowHeight = 100;
this._cols = 6;
}
/** Whether or not to update the internal layout when some dependent property change. */
get compactOnPropsChange() { return this._compactOnPropsChange; }
set compactOnPropsChange(value) {
this._compactOnPropsChange = coerceBooleanProperty(value);
}
/** If true, grid items won't change position when being dragged over. Handy when using no compaction */
get preventCollision() { return this._preventCollision; }
set preventCollision(value) {
this._preventCollision = coerceBooleanProperty(value);
}
/** Number of CSS pixels that would be scrolled on each 'tick' when auto scroll is performed. */
get scrollSpeed() { return this._scrollSpeed; }
set scrollSpeed(value) {
this._scrollSpeed = coerceNumberProperty(value, 2);
}
/** Type of compaction that will be applied to the layout (vertical, horizontal or free). Defaults to 'vertical' */
get compactType() {
return this._compactType;
}
set compactType(val) {
this._compactType = val;
}
/** Row height in css pixels */
get rowHeight() { return this._rowHeight; }
set rowHeight(val) {
this._rowHeight = Math.max(1, Math.round(coerceNumberProperty(val)));
}
/** Number of columns */
get cols() { return this._cols; }
set cols(val) {
this._cols = Math.max(1, Math.round(coerceNumberProperty(val)));
}
/** Layout of the grid. Array of all the grid items with its 'id' and position on the grid. */
get layout() { return this._layout; }
set layout(layout) {
/**
* Enhancement:
* Only set layout if it's reference has changed and use a boolean to track whenever recalculate the layout on ngOnChanges.
*
* Why:
* The normal use of this lib is having the variable layout in the outer component or in a store, assigning it whenever it changes and
* binded in the component with it's input [layout]. In this scenario, we would always calculate one unnecessary change on the layout when
* it is re-binded on the input.
*/
this._layout = layout;
}
get config() {
return {
cols: this.cols,
rowHeight: this.rowHeight,
layout: this.layout,
preventCollision: this.preventCollision,
};
}
ngOnChanges(changes) {
let needsCompactLayout = false;
let needsRecalculateRenderData = false;
// TODO: Does fist change need to be compacted by default?
// Compact layout whenever some dependent prop changes.
if (changes.compactType || changes.cols || changes.layout) {
needsCompactLayout = true;
}
// Check if wee need to recalculate rendering data.
if (needsCompactLayout || changes.rowHeight) {
needsRecalculateRenderData = true;
}
// Only compact layout if lib user has provided it. Lib users that want to save/store always the same layout as it is represented (compacted)
// can use KtdCompactGrid utility and pre-compact the layout. This is the recommended behaviour for always having a the same layout on this component
// and the ones that uses it.
if (needsCompactLayout && this.compactOnPropsChange) {
this.compactLayout();
}
if (needsRecalculateRenderData) {
this.calculateRenderData();
}
}
ngAfterContentInit() {
this.initSubscriptions();
}
ngAfterContentChecked() {
this.render();
}
resize() {
this.calculateRenderData();
this.render();
}
ngOnDestroy() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
compactLayout() {
this.layout = compact(this.layout, this.compactType, this.cols);
}
getItemsRenderData() {
return { ...this._gridItemsRenderData };
}
getItemRenderData(itemId) {
return this._gridItemsRenderData[itemId];
}
calculateRenderData() {
const clientRect = this.elementRef.nativeElement.getBoundingClientRect();
this._gridItemsRenderData = layoutToRenderItems(this.config, clientRect.width, clientRect.height);
this._height = getGridHeight(this.layout, this.rowHeight);
}
render() {
this.renderer.setStyle(this.elementRef.nativeElement, 'height', `${this._height}px`);
this.updateGridItemsStyles();
}
updateGridItemsStyles() {
this._gridItems.forEach(item => {
const gridItemRenderData = this._gridItemsRenderData[item.id];
if (gridItemRenderData == null) {
console.error(`Couldn\'t find the specified grid item for the id: ${item.id}`);
}
else {
item.setStyles(parseRenderItemToPixels(gridItemRenderData));
}
});
}
initSubscriptions() {
this.subscriptions = [
this._gridItems.changes.pipe(startWith(this._gridItems), switchMap((gridItems) => {
return merge(...gridItems.map((gridItem) => gridItem.dragStart$.pipe(map((event) => ({ event, gridItem, type: 'drag' })))), ...gridItems.map((gridItem) => gridItem.resizeStart$.pipe(map((event) => ({ event, gridItem, type: 'resize' }))))).pipe(exhaustMap(({ event, gridItem, type }) => {
// Emit drag or resize start events. Ensure that is start event is inside the zone.
this.ngZone.run(() => (type === 'drag' ? this.dragStarted : this.resizeStarted).emit(getDragResizeEventData(gridItem, this.layout)));
// Get the correct newStateFunc depending on if we are dragging or resizing
const calcNewStateFunc = type === 'drag' ? ktdGridItemDragging : ktdGridItemResizing;
// Perform drag sequence
return this.performDragSequence$(gridItem, event, (gridItemId, config, compactionType, draggingData) => calcNewStateFunc(gridItem, config, compactionType, draggingData)).pipe(map((layout) => ({ layout, gridItem, type })));
}));
})).subscribe(({ layout, gridItem, type }) => {
this.layout = layout;
// Calculate new rendering data given the new layout.
this.calculateRenderData();
// Emit drag or resize end events.
(type === 'drag' ? this.dragEnded : this.resizeEnded).emit(getDragResizeEventData(gridItem, layout));
// Notify that the layout has been updated.
this.layoutUpdated.emit(layout);
})
];
}
/**
* Perform a general grid drag action, from start to end. A general grid drag action basically includes creating the placeholder element and adding
* some class animations. calcNewStateFunc needs to be provided in order to calculate the new state of the layout.
* @param gridItem that is been dragged
* @param pointerDownEvent event (mousedown or touchdown) where the user initiated the drag
* @param calcNewStateFunc function that return the new layout state and the drag element position
*/
performDragSequence$(gridItem, pointerDownEvent, calcNewStateFunc) {
return new Observable((observer) => {
// Retrieve grid (parent) and gridItem (draggedElem) client rects.
const gridElemClientRect = getMutableClientRect(this.elementRef.nativeElement);
const dragElemClientRect = getMutableClientRect(gridItem.elementRef.nativeElement);
const scrollableParent = typeof this.scrollableParent === 'string' ? document.getElementById(this.scrollableParent) : this.scrollableParent;
this.renderer.addClass(gridItem.elementRef.nativeElement, 'no-transitions');
this.renderer.addClass(gridItem.elementRef.nativeElement, 'ktd-grid-item-dragging');
// Create placeholder element. This element would represent the position where the dragged/resized element would be if the action ends
const placeholderElement = this.renderer.createElement('div');
placeholderElement.style.width = `${dragElemClientRect.width}px`;
placeholderElement.style.height = `${dragElemClientRect.height}px`;
placeholderElement.style.transform = `translateX(${dragElemClientRect.left - gridElemClientRect.left}px) translateY(${dragElemClientRect.top - gridElemClientRect.top}px)`;
this.renderer.addClass(placeholderElement, 'ktd-grid-item-placeholder');
this.renderer.appendChild(this.elementRef.nativeElement, placeholderElement);
let newLayout;
// TODO (enhancement): consider move this 'side effect' observable inside the main drag loop.
// - Pros are that we would not repeat subscriptions and takeUntil would shut down observables at the same time.
// - Cons are that moving this functionality as a side effect inside the main drag loop would be confusing.
const scrollSubscription = this.ngZone.runOutsideAngular(() => (!scrollableParent ? NEVER : this.gridService.mouseOrTouchMove$(document).pipe(map((event) => ({
pointerX: ktdPointerClientX(event),
pointerY: ktdPointerClientY(event)
})), ktdScrollIfNearElementClientRect$(scrollableParent, { scrollStep: this.scrollSpeed }))).pipe(takeUntil(ktdMouseOrTouchEnd(document))).subscribe());
/**
* Main subscription, it listens for 'pointer move' and 'scroll' events and recalculates the layout on each emission
*/
const subscription = this.ngZone.runOutsideAngular(() => merge(combineLatest([
this.gridService.mouseOrTouchMove$(document),
...(!scrollableParent ? [of({ top: 0, left: 0 })] : [
ktdGetScrollTotalRelativeDifference$(scrollableParent).pipe(startWith({ top: 0, left: 0 }) // Force first emission to allow CombineLatest to emit even no scroll event has occurred
)
])
])).pipe(takeUntil(ktdMouseOrTouchEnd(document))).subscribe(([pointerDragEvent, scrollDifference]) => {
pointerDragEvent.preventDefault();
/**
* Set the new layout to be the layout in which the calcNewStateFunc would be executed.
* NOTE: using the mutated layout is the way to go by 'react-grid-layout' utils. If we don't use the previous layout,
* some utilities from 'react-grid-layout' would not work as expected.
*/
const currentLayout = newLayout || this.layout;
const { layout, draggedItemPos } = calcNewStateFunc(gridItem, {
layout: currentLayout,
rowHeight: this.rowHeight,
cols: this.cols,
preventCollision: this.preventCollision
}, this.compactType, {
pointerDownEvent,
pointerDragEvent,
gridElemClientRect,
dragElemClientRect,
scrollDifference
});
newLayout = layout;
this._height = getGridHeight(newLayout, this.rowHeight);
this._gridItemsRenderData = layoutToRenderItems({
cols: this.cols,
rowHeight: this.rowHeight,
layout: newLayout,
preventCollision: this.preventCollision,
}, gridElemClientRect.width, gridElemClientRect.height);
const placeholderStyles = parseRenderItemToPixels(this._gridItemsRenderData[gridItem.id]);
// Put the real final position to the placeholder element
placeholderElement.style.width = placeholderStyles.width;
placeholderElement.style.height = placeholderStyles.height;
placeholderElement.style.transform = `translateX(${placeholderStyles.left}) translateY(${placeholderStyles.top})`;
// modify the position of the dragged item to be the once we want (for example the mouse position or whatever)
this._gridItemsRenderData[gridItem.id] = {
...draggedItemPos,
id: this._gridItemsRenderData[gridItem.id].id
};
this.render();
}, (error) => observer.error(error), () => {
this.ngZone.run(() => {
// Remove drag classes
this.renderer.removeClass(gridItem.elementRef.nativeElement, 'no-transitions');
this.renderer.removeClass(gridItem.elementRef.nativeElement, 'ktd-grid-item-dragging');
// Remove placeholder element from the dom
// NOTE: If we don't put the removeChild inside the zone it would not work... This may be a bug from angular or maybe is the intended behaviour, although strange.
// It should work since AFAIK this action should not be done in a CD cycle.
this.renderer.removeChild(this.elementRef.nativeElement, placeholderElement);
if (newLayout) {
// TODO: newLayout should already be pruned. If not, it should have type Layout, not KtdGridLayout as it is now.
// Prune react-grid-layout compact extra properties.
observer.next(newLayout.map(item => ({
id: item.id,
x: item.x,
y: item.y,
w: item.w,
h: item.h,
minW: item.minW,
minH: item.minH,
maxW: item.maxW,
maxH: item.maxH,
})));
}
else {
// TODO: Need we really to emit if there is no layout change but drag started and ended?
observer.next(this.layout);
}
observer.complete();
});
}));
return () => {
scrollSubscription.unsubscribe();
subscription.unsubscribe();
};
});
}
}
KtdGridComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: KtdGridComponent, deps: [{ token: i1.KtdGridService }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
KtdGridComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: KtdGridComponent, selector: "ktd-grid", inputs: { scrollableParent: "scrollableParent", compactOnPropsChange: "compactOnPropsChange", preventCollision: "preventCollision", scrollSpeed: "scrollSpeed", compactType: "compactType", rowHeight: "rowHeight", cols: "cols", layout: "layout" }, outputs: { layoutUpdated: "layoutUpdated", dragStarted: "dragStarted", resizeStarted: "resizeStarted", dragEnded: "dragEnded", resizeEnded: "resizeEnded" }, providers: [
{
provide: GRID_ITEM_GET_RENDER_DATA_TOKEN,
useFactory: ktdGridItemGetRenderDataFactoryFunc,
deps: [KtdGridComponent]
}
], queries: [{ propertyName: "_gridItems", predicate: KtdGridItemComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-content></ng-content>\n", styles: ["ktd-grid{display:block;position:relative;width:100%}ktd-grid ktd-grid-item.ktd-grid-item-dragging{z-index:1000}ktd-grid ktd-grid-item.no-transitions{transition:none!important}ktd-grid .ktd-grid-item-placeholder{position:absolute;background-color:#8b0000;opacity:.6;z-index:0;transition-property:transform;transition:all .15s ease}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: KtdGridComponent, decorators: [{
type: Component,
args: [{ selector: 'ktd-grid', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
{
provide: GRID_ITEM_GET_RENDER_DATA_TOKEN,
useFactory: ktdGridItemGetRenderDataFactoryFunc,
deps: [KtdGridComponent]
}
], template: "<ng-content></ng-content>\n", styles: ["ktd-grid{display:block;position:relative;width:100%}ktd-grid ktd-grid-item.ktd-grid-item-dragging{z-index:1000}ktd-grid ktd-grid-item.no-transitions{transition:none!important}ktd-grid .ktd-grid-item-placeholder{position:absolute;background-color:#8b0000;opacity:.6;z-index:0;transition-property:transform;transition:all .15s ease}\n"] }]
}], ctorParameters: function () { return [{ type: i1.KtdGridService }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }]; }, propDecorators: { _gridItems: [{
type: ContentChildren,
args: [KtdGridItemComponent, { descendants: true }]
}], layoutUpdated: [{
type: Output
}], dragStarted: [{
type: Output
}], resizeStarted: [{
type: Output
}], dragEnded: [{
type: Output
}], resizeEnded: [{
type: Output
}], scrollableParent: [{
type: Input
}], compactOnPropsChange: [{
type: Input
}], preventCollision: [{
type: Input
}], scrollSpeed: [{
type: Input
}], compactType: [{
type: Input
}], rowHeight: [{
type: Input
}], cols: [{
type: Input
}], layout: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9hbmd1bGFyLWdyaWQtbGF5b3V0L3NyYy9saWIvZ3JpZC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9wcm9qZWN0cy9hbmd1bGFyLWdyaWQtbGF5b3V0L3NyYy9saWIvZ3JpZC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ29DLHVCQUF1QixFQUFFLFNBQVMsRUFBRSxlQUFlLEVBQWMsWUFBWSxFQUFFLEtBQUssRUFDaEgsTUFBTSxFQUF1QyxpQkFBaUIsRUFDNUUsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLG9CQUFvQixFQUFlLE1BQU0sNEJBQTRCLENBQUM7QUFDL0UsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDdkUsT0FBTyxFQUFFLGFBQWEsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBWSxFQUFFLEVBQWdCLE1BQU0sTUFBTSxDQUFDO0FBQzNGLE9BQU8sRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbEYsT0FBTyxFQUFFLG1CQUFtQixFQUFFLG1CQUFtQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDOUUsT0FBTyxFQUFFLE9BQU8sRUFBZSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3ZFLE9BQU8sRUFDSCwrQkFBK0IsRUFFbEMsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsaUJBQWlCLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdqRyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsb0NBQW9DLEVBQUUsaUNBQWlDLEVBQUUsTUFBTSxn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Q2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgQ29udGVudENoaWxkcmVuLCBFbGVtZW50UmVmLCBFdmVudEVtaXR0ZXIsIElucHV0LCBOZ1pvbmUsIE9uQ2hhbmdlcyxcbiAgICBPbkRlc3Ryb3ksIE91dHB1dCwgUXVlcnlMaXN0LCBSZW5kZXJlcjIsIFNpbXBsZUNoYW5nZXMsIFZpZXdFbmNhcHN1bGF0aW9uXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgY29lcmNlTnVtYmVyUHJvcGVydHksIE51bWJlcklucHV0IH0gZnJvbSAnLi9jb2VyY2lvbi9udW1iZXItcHJvcGVydHknO1xuaW1wb3J0IHsgS3RkR3JpZEl0ZW1Db21wb25lbnQgfSBmcm9tICcuL2dyaWQtaXRlbS9ncmlkLWl0ZW0uY29tcG9uZW50JztcbmltcG9ydCB7IGNvbWJpbmVMYXRlc3QsIG1lcmdlLCBORVZFUiwgT2JzZXJ2YWJsZSwgT2JzZXJ2ZXIsIG9mLCBTdWJzY3JpcHRpb24gfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGV4aGF1c3RNYXAsIG1hcCwgc3RhcnRXaXRoLCBzd2l0Y2hNYXAsIHRha2VVbnRpbCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IGt0ZEdyaWRJdGVtRHJhZ2dpbmcsIGt0ZEdyaWRJdGVtUmVzaXppbmcgfSBmcm9tICcuL3V0aWxzL2dyaWQudXRpbHMnO1xuaW1wb3J0IHsgY29tcGFjdCwgQ29tcGFjdFR5cGUgfSBmcm9tICcuL3V0aWxzL3JlYWN0LWdyaWQtbGF5b3V0LnV0aWxzJztcbmltcG9ydCB7XG4gICAgR1JJRF9JVEVNX0dFVF9SRU5ERVJfREFUQV9UT0tFTiwgS3RkRHJhZ2dpbmdEYXRhLCBLdGRHcmlkQ2ZnLCBLdGRHcmlkQ29tcGFjdFR5cGUsIEt0ZEdyaWRJdGVtUmVjdCwgS3RkR3JpZEl0ZW1SZW5kZXJEYXRhLCBLdGRHcmlkTGF5b3V0LFxuICAgIEt0ZEdyaWRMYXlvdXRJdGVtXG59IGZyb20gJy4vZ3JpZC5kZWZpbml0aW9ucyc7XG5pbXBvcnQgeyBrdGRNb3VzZU9yVG91Y2hFbmQsIGt0ZFBvaW50ZXJDbGllbnRYLCBrdGRQb2ludGVyQ2xpZW50WSB9IGZyb20gJy4vdXRpbHMvcG9pbnRlci51dGlscyc7XG5pbXBvcnQgeyBLdGREaWN0aW9uYXJ5IH0gZnJvbSAnLi4vdHlwZXMnO1xuaW1wb3J0IHsgS3RkR3JpZFNlcnZpY2UgfSBmcm9tICcuL2dyaWQuc2VydmljZSc7XG5pbXBvcnQgeyBnZXRNdXRhYmxlQ2xpZW50UmVjdCB9IGZyb20gJy4vdXRpbHMvY2xpZW50LXJlY3QnO1xuaW1wb3J0IHsga3RkR2V0U2Nyb2xsVG90YWxSZWxhdGl2ZURpZmZlcmVuY2UkLCBrdGRTY3JvbGxJZk5lYXJFbGVtZW50Q2xpZW50UmVjdCQgfSBmcm9tICcuL3V0aWxzL3Njcm9sbCc7XG5pbXBvcnQgeyBCb29sZWFuSW5wdXQsIGNvZXJjZUJvb2xlYW5Qcm9wZXJ0eSB9IGZyb20gJy4vY29lcmNpb24vYm9vbGVhbi1wcm9wZXJ0eSc7XG5cbmludGVyZmFjZSBLdGREcmFnUmVzaXplRXZlbnQge1xuICAgIGxheW91dDogS3RkR3JpZExheW91dDtcbiAgICBsYXlvdXRJdGVtOiBLdGRHcmlkTGF5b3V0SXRlbTtcbiAgICBncmlkSXRlbVJlZjogS3RkR3JpZEl0ZW1Db21wb25lbnQ7XG59XG5cbmV4cG9ydCB0eXBlIEt0ZERyYWdTdGFydCA9IEt0ZERyYWdSZXNpemVFdmVudDtcbmV4cG9ydCB0eXBlIEt0ZFJlc2l6ZVN0YXJ0ID0gS3RkRHJhZ1Jlc2l6ZUV2ZW50O1xuZXhwb3J0IHR5cGUgS3RkRHJhZ0VuZCA9IEt0ZERyYWdSZXNpemVFdmVudDtcbmV4cG9ydCB0eXBlIEt0ZFJlc2l6ZUVuZCA9IEt0ZERyYWdSZXNpemVFdmVudDtcblxuZnVuY3Rpb24gZ2V0RHJhZ1Jlc2l6ZUV2ZW50RGF0YShncmlkSXRlbTogS3RkR3JpZEl0ZW1Db21wb25lbnQsIGxheW91dDogS3RkR3JpZExheW91dCk6IEt0ZERyYWdSZXNpemVFdmVudCB7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgbGF5b3V0LFxuICAgICAgICBsYXlvdXRJdGVtOiBsYXlvdXQuZmluZCgoaXRlbSkgPT4gaXRlbS5pZCA9PT0gZ3JpZEl0ZW0uaWQpISxcbiAgICAgICAgZ3JpZEl0ZW1SZWY6IGdyaWRJdGVtXG4gICAgfTtcbn1cblxuXG5mdW5jdGlvbiBsYXlvdXRUb1JlbmRlckl0ZW1zKGNvbmZpZzogS3RkR3JpZENmZywgd2lkdGg6IG51bWJlciwgaGVpZ2h0OiBudW1iZXIpOiBLdGREaWN0aW9uYXJ5PEt0ZEdyaWRJdGVtUmVuZGVyRGF0YTxudW1iZXI+PiB7XG4gICAgY29uc3Qge2NvbHMsIHJvd0hlaWdodCwgbGF5b3V0fSA9IGNvbmZpZztcblxuICAgIGNvbnN0IHJlbmRlckl0ZW1zOiBLdGREaWN0aW9uYXJ5PEt0ZEdyaWRJdGVtUmVuZGVyRGF0YTxudW1iZXI+PiA9IHt9O1xuICAgIGZvciAoY29uc3QgaXRlbSBvZiBsYXlvdXQpIHtcbiAgICAgICAgcmVuZGVySXRlbXNbaXRlbS5pZF0gPSB7XG4gICAgICAgICAgICBpZDogaXRlbS5pZCxcbiAgICAgICAgICAgIHRvcDogaXRlbS55ID09PSAwID8gMCA6IGl0ZW0ueSAqIHJvd0hlaWdodCxcbiAgICAgICAgICAgIGxlZnQ6IGl0ZW0ueCAqICh3aWR0aCAvIGNvbHMpLFxuICAgICAgICAgICAgd2lkdGg6IGl0ZW0udyAqICh3aWR0aCAvIGNvbHMpLFxuICAgICAgICAgICAgaGVpZ2h0OiBpdGVtLmggKiByb3dIZWlnaHRcbiAgICAgICAgfTtcbiAgICB9XG4gICAgcmV0dXJuIHJlbmRlckl0ZW1zO1xufVxuXG5mdW5jdGlvbiBnZXRHcmlkSGVpZ2h0KGxheW91dDogS3RkR3JpZExheW91dCwgcm93SGVpZ2h0OiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiBsYXlvdXQucmVkdWNlKChhY2MsIGN1cikgPT4gTWF0aC5tYXgoYWNjLCAoY3VyLnkgKyBjdXIuaCkgKiByb3dIZWlnaHQpLCAwKTtcbn1cblxuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VSZW5kZXJJdGVtVG9QaXhlbHMocmVuZGVySXRlbTogS3RkR3JpZEl0ZW1SZW5kZXJEYXRhPG51bWJlcj4pOiBLdGRHcmlkSXRlbVJlbmRlckRhdGE8c3RyaW5nPiB7XG4gICAgcmV0dXJuIHtcbiAgICAgICAgaWQ6IHJlbmRlckl0ZW0uaWQsXG4gICAgICAgIHRvcDogYCR7cmVuZGVySXRlbS50b3B9cHhgLFxuICAgICAgICBsZWZ0OiBgJHtyZW5kZXJJdGVtLmxlZnR9cHhgLFxuICAgICAgICB3aWR0aDogYCR7cmVuZGVySXRlbS53aWR0aH1weGAsXG4gICAgICAgIGhlaWdodDogYCR7cmVuZGVySXRlbS5oZWlnaHR9cHhgXG4gICAgfTtcbn1cblxuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOmt0ZC1wcmVmaXgtY29kZVxuZXhwb3J0IGZ1bmN0aW9uIF9fZ3JpZEl0ZW1HZXRSZW5kZXJEYXRhRmFjdG9yeUZ1bmMoZ3JpZENtcDogS3RkR3JpZENvbXBvbmVudCkge1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpvbmx5LWFycm93LWZ1bmN0aW9uc1xuICAgIHJldHVybiBmdW5jdGlvbihpZDogc3RyaW5nKSB7XG4gICAgICAgIHJldHVybiBwYXJzZVJlbmRlckl0ZW1Ub1BpeGVscyhncmlkQ21wLmdldEl0ZW1SZW5kZXJEYXRhKGlkKSk7XG4gICAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGt0ZEdyaWRJdGVtR2V0UmVuZGVyRGF0YUZhY3RvcnlGdW5jKGdyaWRDbXA6IEt0ZEdyaWRDb21wb25lbnQpIHtcbiAgICAvLyBXb3JrYXJvdW5kIGV4cGxhaW5lZDogaHR0cHM6Ly9naXRodWIuY29tL25nLXBhY2thZ3IvbmctcGFja2Fnci9pc3N1ZXMvNjk2I2lzc3VlY29tbWVudC0zODcxMTQ2MTNcbiAgICBjb25zdCByZXN1bHRGdW5jID0gX19ncmlkSXRlbUdldFJlbmRlckRhdGFGYWN0b3J5RnVuYyhncmlkQ21wKTtcbiAgICByZXR1cm4gcmVzdWx0RnVuYztcbn1cblxuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ2t0ZC1ncmlkJyxcbiAgICB0ZW1wbGF0ZVVybDogJy4vZ3JpZC5jb21wb25lbnQuaHRtbCcsXG4gICAgc3R5bGVVcmxzOiBbJy4vZ3JpZC5jb21wb25lbnQuc2NzcyddLFxuICAgIGVuY2Fwc3VsYXRpb246IFZpZXdFbmNhcHN1bGF0aW9uLk5vbmUsXG4gICAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG4gICAgcHJvdmlkZXJzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICAgIHByb3ZpZGU6IEdSSURfSVRFTV9HRVRfUkVOREVSX0RBVEFfVE9LRU4sXG4gICAgICAgICAgICB1c2VGYWN0b3J5OiBrdGRHcmlkSXRlbUdldFJlbmRlckRhdGFGYWN0b3J5RnVuYyxcbiAgICAgICAgICAgIGRlcHM6IFtLdGRHcmlkQ29tcG9uZW50XVxuICAgICAgICB9XG4gICAgXVxufSlcbmV4cG9ydCBjbGFzcyBLdGRHcmlkQ29tcG9uZW50IGltcGxlbWVudHMgT25DaGFuZ2VzLCBBZnRlckNvbnRlbnRJbml0LCBBZnRlckNvbnRlbnRDaGVja2VkLCBPbkRlc3Ryb3kge1xuICAgIC8qKiBRdWVyeSBsaXN0IG9mIGdyaWQgaXRlbXMgdGhhdCBhcmUgYmVpbmcgcmVuZGVyZWQuICovXG4gICAgQENvbnRlbnRDaGlsZHJlbihLdGRHcmlkSXRlbUNvbXBvbmVudCwge2Rlc2NlbmRhbnRzOiB0cnVlfSkgX2dyaWRJdGVtczogUXVlcnlMaXN0PEt0ZEdyaWRJdGVtQ29tcG9uZW50PjtcblxuICAgIC8qKiBFbWl0cyB3aGVuIGxheW91dCBjaGFuZ2UgKi9cbiAgICBAT3V0cHV0KCkgbGF5b3V0VXBkYXRlZDogRXZlbnRFbWl0dGVyPEt0ZEdyaWRMYXlvdXQ+ID0gbmV3IEV2ZW50RW1pdHRlcjxLdGRHcmlkTGF5b3V0PigpO1xuXG4gICAgLyoqIEVtaXRzIHdoZW4gZHJhZyBzdGFydHMgKi9cbiAgICBAT3V0cHV0KCkgZHJhZ1N0YXJ0ZWQ6IEV2ZW50RW1pdHRlcjxLdGREcmFnU3RhcnQ+ID0gbmV3IEV2ZW50RW1pdHRlcjxLdGREcmFnU3RhcnQ+KCk7XG5cbiAgICAvKiogRW1pdHMgd2hlbiByZXNpemUgc3RhcnRzICovXG4gICAgQE91dHB1dCgpIHJlc2l6ZVN0YXJ0ZWQ6IEV2ZW50RW1pdHRlcjxLdGRSZXNpemVTdGFydD4gPSBuZXcgRXZlbnRFbWl0dGVyPEt0ZFJlc2l6ZVN0YXJ0PigpO1xuXG4gICAgLyoqIEVtaXRzIHdoZW4gZHJhZyBlbmRzICovXG4gICAgQE91dHB1dCgpIGRyYWdFbmRlZDogRXZlbnRFbWl0dGVyPEt0ZERyYWdFbmQ+ID0gbmV3IEV2ZW50RW1pdHRlcjxLdGREcmFnRW5kPigpO1xuXG4gICAgLyoqIEVtaXRzIHdoZW4gcmVzaXplIGVuZHMgKi9cbiAgICBAT3V0cHV0KCkgcmVzaXplRW5kZWQ6IEV2ZW50RW1pdHRlcjxLdGRSZXNpemVFbmQ+ID0gbmV3IEV2ZW50RW1pdHRlcjxLdGRSZXNpemVFbmQ+KCk7XG5cbiAgICAvKipcbiAgICAgKiBQYXJlbnQgZWxlbWVudCB0aGF0IGNvbnRhaW5zIHRoZSBzY3JvbGwuIElmIGFuIHN0cmluZyBpcyBwcm92aWRlZCBpdCB3b3VsZCBzZWFyY2ggdGhhdCBlbGVtZW50IGJ5IGlkIG9uIHRoZSBkb20uXG4gICAgICogSWYgbm8gZGF0YSBwcm92aWRlZCBvciBudWxsIGF1dG9zY3JvbGwgaXMgbm90IHBlcmZvcm1lZC5cbiAgICAgKi9cbiAgICBASW5wdXQoKSBzY3JvbGxhYmxlUGFyZW50OiBIVE1MRWxlbWVudCB8IERvY3VtZW50IHwgc3RyaW5nIHwgbnVsbCA9IG51bGw7XG5cbiAgICAvKiogV2hldGhlciBvciBub3QgdG8gdXBkYXRlIHRoZSBpbnRlcm5hbCBsYXlvdXQgd2hlbiBzb21lIGRlcGVuZGVudCBwcm9wZXJ0eSBjaGFuZ2UuICovXG4gICAgQElucHV0KClcbiAgICBnZXQgY29tcGFjdE9uUHJvcHNDaGFuZ2UoKTogYm9vbGVhbiB7IHJldHVybiB0aGlzLl9jb21wYWN0T25Qcm9wc0NoYW5nZTsgfVxuXG4gICAgc2V0IGNvbXBhY3RPblByb3BzQ2hhbmdlKHZhbHVlOiBib29sZWFuKSB7XG4gICAgICAgIHRoaXMuX2NvbXBhY3RPblByb3BzQ2hhbmdlID0gY29lcmNlQm9vbGVhblByb3BlcnR5KHZhbHVlKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9jb21wYWN0T25Qcm9wc0NoYW5nZTogYm9vbGVhbiA9IHRydWU7XG5cbiAgICAvKiogSWYgdHJ1ZSwgZ3JpZCBpdGVtcyB3b24ndCBjaGFuZ2UgcG9zaXRpb24gd2hlbiBiZWluZyBkcmFnZ2VkIG92ZXIuIEhhbmR5IHdoZW4gdXNpbmcgbm8gY29tcGFjdGlvbiAqL1xuICAgIEBJbnB1dCgpXG4gICAgZ2V0IHByZXZlbnRDb2xsaXNpb24oKTogYm9vbGVhbiB7IHJldHVybiB0aGlzLl9wcmV2ZW50Q29sbGlzaW9uOyB9XG5cbiAgICBzZXQgcHJldmVudENvbGxpc2lvbih2YWx1ZTogYm9vbGVhbikge1xuICAgICAgICB0aGlzLl9wcmV2ZW50Q29sbGlzaW9uID0gY29lcmNlQm9vbGVhblByb3BlcnR5KHZhbHVlKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9wcmV2ZW50Q29sbGlzaW9uOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKiogTnVtYmVyIG9mIENTUyBwaXhlbHMgdGhhdCB3b3VsZCBiZSBzY3JvbGxlZCBvbiBlYWNoICd0aWNrJyB3aGVuIGF1dG8gc2Nyb2xsIGlzIHBlcmZvcm1lZC4gKi9cbiAgICBASW5wdXQoKVxuICAgIGdldCBzY3JvbGxTcGVlZCgpOiBudW1iZXIgeyByZXR1cm4gdGhpcy5fc2Nyb2xsU3BlZWQ7IH1cblxuICAgIHNldCBzY3JvbGxTcGVlZCh2YWx1ZTogbnVtYmVyKSB7XG4gICAgICAgIHRoaXMuX3Njcm9sbFNwZWVkID0gY29lcmNlTnVtYmVyUHJvcGVydHkodmFsdWUsIDIpO1xuICAgIH1cblxuICAgIHByaXZhdGUgX3Njcm9sbFNwZWVkOiBudW1iZXIgPSAyO1xuXG4gICAgLyoqIFR5cGUgb2YgY29tcGFjdGlvbiB0aGF0IHdpbGwgYmUgYXBwbGllZCB0byB0aGUgbGF5b3V0ICh2ZXJ0aWNhbCwgaG9yaXpvbnRhbCBvciBmcmVlKS4gRGVmYXVsdHMgdG8gJ3ZlcnRpY2FsJyAqL1xuICAgIEBJbnB1dCgpXG4gICAgZ2V0IGNvbXBhY3RUeXBlKCk6IEt0ZEdyaWRDb21wYWN0VHlwZSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jb21wYWN0VHlwZTtcbiAgICB9XG5cbiAgICBzZXQgY29tcGFjdFR5cGUodmFsOiBLdGRHcmlkQ29tcGFjdFR5cGUpIHtcbiAgICAgICAgdGhpcy5fY29tcGFjdFR5cGUgPSB2YWw7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfY29tcGFjdFR5cGU6IEt0ZEdyaWRDb21wYWN0VHlwZSA9ICd2ZXJ0aWNhbCc7XG5cbiAgICAvKiogUm93IGhlaWdodCBpbiBjc3MgcGl4ZWxzICovXG4gICAgQElucHV0KClcbiAgICBnZXQgcm93SGVpZ2h0KCk6IG51bWJlciB7IHJldHVybiB0aGlzLl9yb3dIZWlnaHQ7IH1cblxuICAgIHNldCByb3dIZWlnaHQodmFsOiBudW1iZXIpIHtcbiAgICAgICAgdGhpcy5fcm93SGVpZ2h0ID0gTWF0aC5tYXgoMSwgTWF0aC5yb3VuZChjb2VyY2VOdW1iZXJQcm9wZXJ0eSh2YWwpKSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfcm93SGVpZ2h0OiBudW1iZXIgPSAxMDA7XG5cbiAgICAvKiogTnVtYmVyIG9mIGNvbHVtbnMgICovXG4gICAgQElucHV0KClcbiAgICBnZXQgY29scygpOiBudW1iZXIgeyByZXR1cm4gdGhpcy5fY29sczsgfVxuXG4gICAgc2V0IGNvbHModmFsOiBudW1iZXIpIHtcbiAgICAgICAgdGhpcy5fY29scyA9IE1hdGgubWF4KDEsIE1hdGgucm91bmQoY29lcmNlTnVtYmVyUHJvcGVydHkodmFsKSkpO1xuICAgIH1cblxuICAgIHByaXZhdGUgX2NvbHM6IG51bWJlciA9IDY7XG5cbiAgICAvKiogTGF5b3V0IG9mIHRoZSBncmlkLiBBcnJheSBvZiBhbGwgdGhlIGdyaWQgaXRlbXMgd2l0aCB