UNPKG

angular-grid-layout-ngx13

Version:

Grid Layout with draggable and resizable items for Angular

392 lines 71.4 kB
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