@hugogo/angular-grid-layout-hugo
Version:
Grid Layout with draggable and resizable items for Angular
374 lines • 68 kB
JavaScript
import { ChangeDetectionStrategy, Component, ContentChildren, ElementRef, EventEmitter, Input, NgZone, Output, Renderer2, 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 { KtdGridService } from './grid.service';
import { getMutableClientRect } from './utils/client-rect';
import { ktdGetScrollTotalRelativeDifference$, ktdScrollIfNearElementClientRect$ } from './utils/scroll';
import { coerceBooleanProperty } from './coercion/boolean-property';
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 Object.assign({}, 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(gridItemId, 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.id, {
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] = Object.assign(Object.assign({}, 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) {
// 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
})));
}
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.decorators = [
{ type: Component, args: [{
selector: 'ktd-grid',
template: "<ng-content></ng-content>\n",
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: GRID_ITEM_GET_RENDER_DATA_TOKEN,
useFactory: ktdGridItemGetRenderDataFactoryFunc,
deps: [KtdGridComponent]
}
],
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{background-color:#8b0000;opacity:.6;position:absolute;transition:all .15s ease;transition-property:transform;z-index:0}"]
},] }
];
KtdGridComponent.ctorParameters = () => [
{ type: KtdGridService },
{ type: ElementRef },
{ type: Renderer2 },
{ type: NgZone }
];
KtdGridComponent.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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiLi4vLi4vLi4vcHJvamVjdHMvYW5ndWxhci1ncmlkLWxheW91dC1odWdvL3NyYy8iLCJzb3VyY2VzIjpbImxpYi9ncmlkLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ29DLHVCQUF1QixFQUFFLFNBQVMsRUFBRSxlQUFlLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUN4SCxNQUFNLEVBQWEsU0FBUyxFQUFpQixpQkFBaUIsRUFDNUUsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLG9CQUFvQixFQUFlLE1BQU0sNEJBQTRCLENBQUM7QUFDL0UsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDdkUsT0FBTyxFQUFFLGFBQWEsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBWSxFQUFFLEVBQWdCLE1BQU0sTUFBTSxDQUFDO0FBQzNGLE9BQU8sRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbEYsT0FBTyxFQUFFLG1CQUFtQixFQUFFLG1CQUFtQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDOUUsT0FBTyxFQUFFLE9BQU8sRUFBZSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3ZFLE9BQU8sRUFDSCwrQkFBK0IsRUFFbEMsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsaUJBQWlCLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUVqRyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDaEQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDM0QsT0FBTyxFQUFFLG9DQUFvQyxFQUFFLGlDQUFpQyxFQUFFLE1B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QUUsVUFBVTtnQkFDcEIsdUNBQW9DO2dCQUVwQyxhQUFhLEVBQUUsaUJBQWlCLENBQUMsSUFBSTtnQkFDckMsZUFBZSxFQUFFLHVCQUF1QixDQUFDLE1BQU07Z0JBQy9DLFNBQVMsRUFBRTtvQkFDUDt3QkFDSSxPQUFPLEVBQUUsK0JBQStCO3dCQUN4QyxVQUFVLEVBQUUsbUNBQW1DO3dCQUMvQyxJQUFJLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQztxQkFDM0I7aUJBQ0o7O2FBQ0o7OztZQXBGUSxjQUFjO1lBZnlFLFVBQVU7WUFDeEUsU0FBUztZQURzRixNQUFNOzs7eUJBc0dsSSxlQUFlLFNBQUMsb0JBQW9CLEVBQUUsRUFBQyxXQUFXLEVBQUUsSUFBSSxFQUFDOzRCQUd6RCxNQUFNOzBCQUdOLE1BQU07NEJBR04sTUFBTTt3QkFHTixNQUFNOzBCQUdOLE1BQU07K0JBTU4sS0FBSzttQ0FHTCxLQUFLOytCQVVMLEtBQUs7MEJBVUwsS0FBSzswQkFVTCxLQUFLO3dCQVlMLEtBQUs7bUJBVUwsS0FBSztxQkFVTCxLQUFLIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgICBBZnRlckNvbnRlbnRDaGVja2VkLCBBZnRlckNvbnRlbnRJbml0LCBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ29tcG9uZW50LCBDb250ZW50Q2hpbGRyZW4sIEVsZW1lbnRSZWYsIEV2ZW50RW1pdHRlciwgSW5wdXQsIE5nWm9uZSwgT25DaGFuZ2VzLFxuICAgIE9uRGVzdHJveSwgT3V0cHV0LCBRdWVyeUxpc3QsIFJlbmRlcmVyMiwgU2ltcGxlQ2hhbmdlcywgVmlld0VuY2Fwc3VsYXRpb25cbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBjb2VyY2VOdW1iZXJQcm9wZXJ0eSwgTnVtYmVySW5wdXQgfSBmcm9tICcuL2NvZXJjaW9uL251bWJlci1wcm9wZXJ0eSc7XG5pbXBvcnQgeyBLdGRHcmlkSXRlbUNvbXBvbmVudCB9IGZyb20gJy4vZ3JpZC1pdGVtL2dyaWQtaXRlbS5jb21wb25lbnQnO1xuaW1wb3J0IHsgY29tYmluZUxhdGVzdCwgbWVyZ2UsIE5FVkVSLCBPYnNlcnZhYmxlLCBPYnNlcnZlciwgb2YsIFN1YnNjcmlwdGlvbiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZXhoYXVzdE1hcCwgbWFwLCBzdGFydFdpdGgsIHN3aXRjaE1hcCwgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsga3RkR3JpZEl0ZW1EcmFnZ2luZywga3RkR3JpZEl0ZW1SZXNpemluZyB9IGZyb20gJy4vdXRpbHMvZ3JpZC51dGlscyc7XG5pbXBvcnQgeyBjb21wYWN0LCBDb21wYWN0VHlwZSB9IGZyb20gJy4vdXRpbHMvcmVhY3QtZ3JpZC1sYXlvdXQudXRpbHMnO1xuaW1wb3J0IHtcbiAgICBHUklEX0lURU1fR0VUX1JFTkRFUl9EQVRBX1RPS0VOLCBLdGREcmFnZ2luZ0RhdGEsIEt0ZEdyaWRDZmcsIEt0ZEdyaWRDb21wYWN0VHlwZSwgS3RkR3JpZEl0ZW1SZWN0LCBLdGRHcmlkSXRlbVJlbmRlckRhdGEsIEt0ZEdyaWRMYXlvdXQsXG4gICAgS3RkR3JpZExheW91dEl0ZW1cbn0gZnJvbSAnLi9ncmlkLmRlZmluaXRpb25zJztcbmltcG9ydCB7IGt0ZE1vdXNlT3JUb3VjaEVuZCwga3RkUG9pbnRlckNsaWVudFgsIGt0ZFBvaW50ZXJDbGllbnRZIH0gZnJvbSAnLi91dGlscy9wb2ludGVyLnV0aWxzJztcbmltcG9ydCB7IEt0ZERpY3Rpb25hcnkgfSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgeyBLdGRHcmlkU2VydmljZSB9IGZyb20gJy4vZ3JpZC5zZXJ2aWNlJztcbmltcG9ydCB7IGdldE11dGFibGVDbGllbnRSZWN0IH0gZnJvbSAnLi91dGlscy9jbGllbnQtcmVjdCc7XG5pbXBvcnQgeyBrdGRHZXRTY3JvbGxUb3RhbFJlbGF0aXZlRGlmZmVyZW5jZSQsIGt0ZFNjcm9sbElmTmVhckVsZW1lbnRDbGllbnRSZWN0JCB9IGZyb20gJy4vdXRpbHMvc2Nyb2xsJztcbmltcG9ydCB7IEJvb2xlYW5JbnB1dCwgY29lcmNlQm9vbGVhblByb3BlcnR5IH0gZnJvbSAnLi9jb2VyY2lvbi9ib29sZWFuLXByb3BlcnR5JztcblxuaW50ZXJmYWNlIEt0ZERyYWdSZXNpemVFdmVudCB7XG4gICAgbGF5b3V0OiBLdGRHcmlkTGF5b3V0O1xuICAgIGxheW91dEl0ZW06IEt0ZEdyaWRMYXlvdXRJdGVtO1xuICAgIGdyaWRJdGVtUmVmOiBLdGRHcmlkSXRlbUNvbXBvbmVudDtcbn1cblxuZXhwb3J0IHR5cGUgS3RkRHJhZ1N0YXJ0ID0gS3RkRHJhZ1Jlc2l6ZUV2ZW50O1xuZXhwb3J0IHR5cGUgS3RkUmVzaXplU3RhcnQgPSBLdGREcmFnUmVzaXplRXZlbnQ7XG5leHBvcnQgdHlwZSBLdGREcmFnRW5kID0gS3RkRHJhZ1Jlc2l6ZUV2ZW50O1xuZXhwb3J0IHR5cGUgS3RkUmVzaXplRW5kID0gS3RkRHJhZ1Jlc2l6ZUV2ZW50O1xuXG5mdW5jdGlvbiBnZXREcmFnUmVzaXplRXZlbnREYXRhKGdyaWRJdGVtOiBLdGRHcmlkSXRlbUNvbXBvbmVudCwgbGF5b3V0OiBLdGRHcmlkTGF5b3V0KTogS3RkRHJhZ1Jlc2l6ZUV2ZW50IHtcbiAgICByZXR1cm4ge1xuICAgICAgICBsYXlvdXQsXG4gICAgICAgIGxheW91dEl0ZW06IGxheW91dC5maW5kKChpdGVtKSA9PiBpdGVtLmlkID09PSBncmlkSXRlbS5pZCkhLFxuICAgICAgICBncmlkSXRlbVJlZjogZ3JpZEl0ZW1cbiAgICB9O1xufVxuXG5cbmZ1bmN0aW9uIGxheW91dFRvUmVuZGVySXRlbXMoY29uZmlnOiBLdGRHcmlkQ2ZnLCB3aWR0aDogbnVtYmVyLCBoZWlnaHQ6IG51bWJlcik6IEt0ZERpY3Rpb25hcnk8S3RkR3JpZEl0ZW1SZW5kZXJEYXRhPG51bWJlcj4+IHtcbiAgICBjb25zdCB7Y29scywgcm93SGVpZ2h0LCBsYXlvdXR9ID0gY29uZmlnO1xuXG4gICAgY29uc3QgcmVuZGVySXRlbXM6IEt0ZERpY3Rpb25hcnk8S3RkR3JpZEl0ZW1SZW5kZXJEYXRhPG51bWJlcj4+ID0ge307XG4gICAgZm9yIChjb25zdCBpdGVtIG9mIGxheW91dCkge1xuICAgICAgICByZW5kZXJJdGVtc1tpdGVtLmlkXSA9IHtcbiAgICAgICAgICAgIGlkOiBpdGVtLmlkLFxuICAgICAgICAgICAgdG9wOiBpdGVtLnkgPT09IDAgPyAwIDogaXRlbS55ICogcm93SGVpZ2h0LFxuICAgICAgICAgICAgbGVmdDogaXRlbS54ICogKHdpZHRoIC8gY29scyksXG4gICAgICAgICAgICB3aWR0aDogaXRlbS53ICogKHdpZHRoIC8gY29scyksXG4gICAgICAgICAgICBoZWlnaHQ6IGl0ZW0uaCAqIHJvd0hlaWdodFxuICAgICAgICB9O1xuICAgIH1cbiAgICByZXR1cm4gcmVuZGVySXRlbXM7XG59XG5cbmZ1bmN0aW9uIGdldEdyaWRIZWlnaHQobGF5b3V0OiBLdGRHcmlkTGF5b3V0LCByb3dIZWlnaHQ6IG51bWJlcik6IG51bWJlciB7XG4gICAgcmV0dXJuIGxheW91dC5yZWR1Y2UoKGFjYywgY3VyKSA9PiBNYXRoLm1heChhY2MsIChjdXIueSArIGN1ci5oKSAqIHJvd0hlaWdodCksIDApO1xufVxuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmVcbmV4cG9ydCBmdW5jdGlvbiBwYXJzZVJlbmRlckl0ZW1Ub1BpeGVscyhyZW5kZXJJdGVtOiBLdGRHcmlkSXRlbVJlbmRlckRhdGE8bnVtYmVyPik6IEt0ZEdyaWRJdGVtUmVuZGVyRGF0YTxzdHJpbmc+IHtcbiAgICByZXR1cm4ge1xuICAgICAgICBpZDogcmVuZGVySXRlbS5pZCxcbiAgICAgICAgdG9wOiBgJHtyZW5kZXJJdGVtLnRvcH1weGAsXG4gICAgICAgIGxlZnQ6IGAke3JlbmRlckl0ZW0ubGVmdH1weGAsXG4gICAgICAgIHdpZHRoOiBgJHtyZW5kZXJJdGVtLndpZHRofXB4YCxcbiAgICAgICAgaGVpZ2h0OiBgJHtyZW5kZXJJdGVtLmhlaWdodH1weGBcbiAgICB9O1xufVxuXG4vLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6a3RkLXByZWZpeC1jb2RlXG5leHBvcnQgZnVuY3Rpb24gX19ncmlkSXRlbUdldFJlbmRlckRhdGFGYWN0b3J5RnVuYyhncmlkQ21wOiBLdGRHcmlkQ29tcG9uZW50KSB7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm9ubHktYXJyb3ctZnVuY3Rpb25zXG4gICAgcmV0dXJuIGZ1bmN0aW9uKGlkOiBzdHJpbmcpIHtcbiAgICAgICAgcmV0dXJuIHBhcnNlUmVuZGVySXRlbVRvUGl4ZWxzKGdyaWRDbXAuZ2V0SXRlbVJlbmRlckRhdGEoaWQpKTtcbiAgICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24ga3RkR3JpZEl0ZW1HZXRSZW5kZXJEYXRhRmFjdG9yeUZ1bmMoZ3JpZENtcDogS3RkR3JpZENvbXBvbmVudCkge1xuICAgIC8vIFdvcmthcm91bmQgZXhwbGFpbmVkOiBodHRwczovL2dpdGh1Yi5jb20vbmctcGFja2Fnci9uZy1wYWNrYWdyL2lzc3Vlcy82OTYjaXNzdWVjb21tZW50LTM4NzExNDYxM1xuICAgIGNvbnN0IHJlc3VsdEZ1bmMgPSBfX2dyaWRJdGVtR2V0UmVuZGVyRGF0YUZhY3RvcnlGdW5jKGdyaWRDbXApO1xuICAgIHJldHVybiByZXN1bHRGdW5jO1xufVxuXG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiAna3RkLWdyaWQnLFxuICAgIHRlbXBsYXRlVXJsOiAnLi9ncmlkLmNvbXBvbmVudC5odG1sJyxcbiAgICBzdHlsZVVybHM6IFsnLi9ncmlkLmNvbXBvbmVudC5zY3NzJ10sXG4gICAgZW5jYXBzdWxhdGlvbjogVmlld0VuY2Fwc3VsYXRpb24uTm9uZSxcbiAgICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgICBwcm92aWRlcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgICAgcHJvdmlkZTogR1JJRF9JVEVNX0dFVF9SRU5ERVJfREFUQV9UT0tFTixcbiAgICAgICAgICAgIHVzZUZhY3Rvcnk6IGt0ZEdyaWRJdGVtR2V0UmVuZGVyRGF0YUZhY3RvcnlGdW5jLFxuICAgICAgICAgICAgZGVwczogW0t0ZEdyaWRDb21wb25lbnRdXG4gICAgICAgIH1cbiAgICBdXG59KVxuZXhwb3J0IGNsYXNzIEt0ZEdyaWRDb21wb25lbnQgaW1wbGVtZW50cyBPbkNoYW5nZXMsIEFmdGVyQ29udGVudEluaXQsIEFmdGVyQ29udGVudENoZWNrZWQsIE9uRGVzdHJveSB7XG4gICAgLyoqIFF1ZXJ5IGxpc3Qgb2YgZ3JpZCBpdGVtcyB0aGF0IGFyZSBiZWluZyByZW5kZXJlZC4gKi9cbiAgICBAQ29udGVudENoaWxkcmVuKEt0ZEdyaWRJdGVtQ29tcG9uZW50LCB7ZGVzY2VuZGFudHM6IHRydWV9KSBfZ3JpZEl0ZW1zOiBRdWVyeUxpc3Q8S3RkR3JpZEl0ZW1Db21wb25lbnQ+O1xuXG4gICAgLyoqIEVtaXRzIHdoZW4gbGF5b3V0IGNoYW5nZSAqL1xuICAgIEBPdXRwdXQoKSBsYXlvdXRVcGRhdGVkOiBFdmVudEVtaXR0ZXI8S3RkR3JpZExheW91dD4gPSBuZXcgRXZlbnRFbWl0dGVyPEt0ZEdyaWRMYXlvdXQ+KCk7XG5cbiAgICAvKiogRW1pdHMgd2hlbiBkcmFnIHN0YXJ0cyAqL1xuICAgIEBPdXRwdXQoKSBkcmFnU3RhcnRlZDogRXZlbnRFbWl0dGVyPEt0ZERyYWdTdGFydD4gPSBuZXcgRXZlbnRFbWl0dGVyPEt0ZERyYWdTdGFydD4oKTtcblxuICAgIC8qKiBFbWl0cyB3aGVuIHJlc2l6ZSBzdGFydHMgKi9cbiAgICBAT3V0cHV0KCkgcmVzaXplU3RhcnRlZDogRXZlbnRFbWl0dGVyPEt0ZFJlc2l6ZVN0YXJ0PiA9IG5ldyBFdmVudEVtaXR0ZXI8S3RkUmVzaXplU3RhcnQ+KCk7XG5cbiAgICAvKiogRW1pdHMgd2hlbiBkcmFnIGVuZHMgKi9cbiAgICBAT3V0cHV0KCkgZHJhZ0VuZGVkOiBFdmVudEVtaXR0ZXI8S3RkRHJhZ0VuZD4gPSBuZXcgRXZlbnRFbWl0dGVyPEt0ZERyYWdFbmQ+KCk7XG5cbiAgICAvKiogRW1pdHMgd2hlbiByZXNpemUgZW5kcyAqL1xuICAgIEBPdXRwdXQoKSByZXNpemVFbmRlZDogRXZlbnRFbWl0dGVyPEt0ZFJlc2l6ZUVuZD4gPSBuZXcgRXZlbnRFbWl0dGVyPEt0ZFJlc2l6ZUVuZD4oKTtcblxuICAgIC8qKlxuICAgICAqIFBhcmVudCBlbGVtZW50IHRoYXQgY29udGFpbnMgdGhlIHNjcm9sbC4gSWYgYW4gc3RyaW5nIGlzIHByb3ZpZGVkIGl0IHdvdWxkIHNlYXJjaCB0aGF0IGVsZW1lbnQgYnkgaWQgb24gdGhlIGRvbS5cbiAgICAgKiBJZiBubyBkYXRhIHByb3ZpZGVkIG9yIG51bGwgYXV0b3Njcm9sbCBpcyBub3QgcGVyZm9ybWVkLlxuICAgICAqL1xuICAgIEBJbnB1dCgpIHNjcm9sbGFibGVQYXJlbnQ6IEhUTUxFbGVtZW50IHwgRG9jdW1lbnQgfCBzdHJpbmcgfCBudWxsID0gbnVsbDtcblxuICAgIC8qKiBXaGV0aGVyIG9yIG5vdCB0byB1cGRhdGUgdGhlIGludGVybmFsIGxheW91dCB3aGVuIHNvbWUgZGVwZW5kZW50IHByb3BlcnR5IGNoYW5nZS4gKi9cbiAgICBASW5wdXQoKVxuICAgIGdldCBjb21wYWN0T25Qcm9wc0NoYW5nZSgpOiBib29sZWFuIHsgcmV0dXJuIHRoaXMuX2NvbXBhY3RPblByb3BzQ2hhbmdlOyB9XG5cbiAgICBzZXQgY29tcGFjdE9uUHJvcHNDaGFuZ2UodmFsdWU6IGJvb2xlYW4pIHtcbiAgICAgICAgdGhpcy5fY29tcGFjdE9uUHJvcHNDaGFuZ2UgPSBjb2VyY2VCb29sZWFuUHJvcGVydHkodmFsdWUpO1xuICAgIH1cblxuICAgIHByaXZhdGUgX2NvbXBhY3RPblByb3BzQ2hhbmdlOiBib29sZWFuID0gdHJ1ZTtcblxuICAgIC8qKiBJZiB0cnVlLCBncmlkIGl0ZW1zIHdvbid0IGNoYW5nZSBwb3NpdGlvbiB3aGVuIGJlaW5nIGRyYWdnZWQgb3Zlci4gSGFuZHkgd2hlbiB1c2luZyBubyBjb21wYWN0aW9uICovXG4gICAgQElucHV0KClcbiAgICBnZXQgcHJldmVudENvbGxpc2lvbigpOiBib29sZWFuIHsgcmV0dXJuIHRoaXMuX3ByZXZlbnRDb2xsaXNpb247IH1cblxuICAgIHNldCBwcmV2ZW50Q29sbGlzaW9uKHZhbHVlOiBib29sZWFuKSB7XG4gICAgICAgIHRoaXMuX3ByZXZlbnRDb2xsaXNpb24gPSBjb2VyY2VCb29sZWFuUHJvcGVydHkodmFsdWUpO1xuICAgIH1cblxuICAgIHByaXZhdGUgX3ByZXZlbnRDb2xsaXNpb246IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKiBOdW1iZXIgb2YgQ1NTIHBpeGVscyB0aGF0IHdvdWxkIGJlIHNjcm9sbGVkIG9uIGVhY2ggJ3RpY2snIHdoZW4gYXV0byBzY3JvbGwgaXMgcGVyZm9ybWVkLiAqL1xuICAgIEBJbnB1dCgpXG4gICAgZ2V0IHNjcm9sbFNwZWVkKCk6IG51bWJlciB7IHJldHVybiB0aGlzLl9zY3JvbGxTcGVlZDsgfVxuXG4gICAgc2V0IHNjcm9sbFNwZWVkKHZhbHVlOiBudW1iZXIpIHtcbiAgICAgICAgdGhpcy5fc2Nyb2xsU3BlZWQgPSBjb2VyY2VOdW1iZXJQcm9wZXJ0eSh2YWx1ZSwgMik7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfc2Nyb2xsU3BlZWQ6IG51bWJlciA9IDI7XG5cbiAgICAvKiogVHlwZSBvZiBjb21wYWN0aW9uIHRoYXQgd2lsbCBiZSBhcHBsaWVkIHRvIHRoZSBsYXlvdXQgKHZlcnRpY2FsLCBob3Jpem9udGFsIG9yIGZyZWUpLiBEZWZhdWx0cyB0byAndmVydGljYWwnICovXG4gICAgQElucHV0KClcbiAgICBnZXQgY29tcGFjdFR5cGUoKTogS3RkR3JpZENvbXBhY3RUeXBlIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NvbXBhY3RUeXBlO1xuICAgIH1cblxuICAgIHNldCBjb21wYWN0VHlwZSh2YWw6IEt0ZEdyaWRDb21wYWN0VHlwZSkge1xuICAgICAgICB0aGlzLl9jb21wYWN0VHlwZSA9IHZhbDtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9jb21wYWN0VHlwZTogS3RkR3JpZENvbXBhY3RUeXBlID0gJ3ZlcnRpY2FsJztcblxuICAgIC8qKiBSb3cgaGVpZ2h0IGluIGNzcyBwaXhlbHMgKi9cbiAgICBASW5wdXQoKVxuICAgIGdldCByb3dIZWlnaHQoKTogbnVtYmVyIHsgcmV0dXJuIHRoaXMuX3Jvd0hlaWdodDsgfVxuXG4gICAgc2V0IHJvd0hlaWdodCh2YWw6IG51bWJlcikge1xuICAgICAgICB0aGlzLl9yb3dIZWlnaHQgPSBNYXRoLm1heCgxLCBNYXRoLnJvdW5kKGNvZXJjZU51bWJlclByb3BlcnR5KHZhbCkpKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9yb3dIZWlnaHQ6IG51bWJlciA9IDEwMDtcblxuICAgIC8qKiBOdW1iZXIgb2YgY29sdW1ucyAgKi9cbiAgICBASW5wdXQoKVxuICAgIGdldCBjb2xzKCk6IG51bWJlciB7IHJldHVybiB0aGlzLl9jb2xzOyB9XG5cbiAgICBzZXQgY29scyh2YWw6IG51bWJlcikge1xuICAgICAgICB0aGlzLl9jb2xzID0gTWF0aC5tYXgoMSwgTWF0aC5yb3VuZChjb2VyY2VOdW1iZXJQcm9wZXJ0eSh2YWwpKSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfY29sczogbnVtYmVyID0gNjtcblxuICAgIC8qKiBMYXlvdXQgb2YgdGhlIGdyaWQuIEFycmF5IG9mIGFsbCB0aGUgZ3JpZCBpdGVtcyB3aXRoIGl0cyAnaWQnIGFuZCBwb3NpdGlvbiBvbiB0aGUgZ3JpZC4gKi9cbiAgICBASW5wdXQoKVxuICAgIGdldCBsYXlvdXQoKTogS3RkR3JpZExheW91dCB7IHJldHVybiB0aGlzLl9sYXlvdXQ7IH1cblxuICAgIHNldCBsYXlvdXQobGF5b3V0OiBLdGRHcmlkTGF5b3V0KSB7XG4gICAgICAgIC8qKlxuICAgICAgICAgKiBFbmhhbmNlbWVudDpcbiAgICAgICAgICogT25seSBzZXQgbGF5b3V0IGlmIGl0J3MgcmVmZXJlbmNlIGhhcyBjaGFuZ2VkIGFuZCB1c2UgYSBib29sZWFuIHRvIHRyYWNrIHdoZW5ldmVyIHJlY2FsY3VsYXRlIHRoZSBsYXlvdXQgb24gbmdPbkNoYW5nZXMuXG4gICAgICAgICAqXG4gICAgICAgICAqIFdoeTpcbiAgICAgICAgICogVGhlIG5vcm1hbCB1c2Ugb2YgdGhpcyBsaWIgaXMgaGF2aW5nIHRoZSB2YXJpYWJsZSBsYXlvdXQgaW4gdGhlIG91dGVyIGNvbXBvbmVudCBvciBpbiBhIHN0b3JlLCBhc3NpZ25pbmcgaXQgd2hlbmV2ZXIgaXQgY2hhbmdlcyBhbmRcbiAgICAgICAgICogYmluZGVkIGluIHRoZSBjb21wb25lbnQgd2l0aCBpdCdzIGlucHV0IFtsYXlvdXRdLiBJbiB0aGlzIHNjZW5hcmlvLCB3ZSB3b3VsZCBhbHdheXMgY2FsY3VsYXRlIG9uZSB1bm5lY2Vzc2FyeSBjaGFuZ2Ugb24gdGhlIGxheW91dCB3aGVuXG4gICAgICAgICAqIGl0IGlzIHJlLWJpbmRlZCBvbiB0aGUgaW5wdXQuXG4gICAgICAgICAqL1xuICAgICAgICB0aGlzLl9sYXlvdXQgPSBsYXlvdXQ7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfbGF5b3V0OiBLdGRHcmlkTGF5b3V0O1xuXG4gICAgZ2V0IGNvbmZpZygpOiBLdGRHcmlkQ2ZnIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGNvbHM6IHRoaXMuY29scyxcbiAgICAgICAgICAgIHJvd0hlaWdodDogdGhpcy5yb3dIZWlnaHQsXG4gICAgICAgICAgICBsYXlvdXQ6IHRoaXMubGF5b3V0LFxuICAgICAgICAgICAgcHJldmVudENvbGxpc2lvbjogdGhpcy5wcmV2ZW50Q29sbGlzaW9uLFxuICAgICAgICB9O1xuICAgIH1cblxuICAgIC8qKiBUb3RhbCBoZWlnaHQgb2YgdGhlIGdyaWQgKi9cbiAgICBwcml2YXRlIF9oZWlnaHQ6IG51bWJlcjtcbiAgICBwcml2YXRlIF9ncmlkSXRlbXNSZW5kZXJEYXRhOiBLdGREaWN0aW9uYXJ5PEt0ZEdyaWRJdGVtUmVuZGVyRGF0YTxudW1iZXI+PjtcbiAgICBwcml2YXRlIHN1YnNjcmlwdGlvbnM6IFN1YnNjcmlwdGlvbltdO1xuXG4gICAgY29uc3RydWN0b3IocHJpdmF0ZSBncmlkU2VydmljZTogS3RkR3JpZFNlcnZpY2UsXG4gICAgICAgICAgICAgICAgcHJpdmF0ZSBlbGVtZW50UmVmOiBFbGVtZW50UmVmLFxuICAgICAgICAgICAgICAgIHByaXZhdGUgcmVuZGVyZXI6IFJlbmRlcmVyMixcbiAgICAgICAgICAgICAgICBwcml2YXRlIG5nWm9uZTogTmdab25lKSB7XG5cbiAgICB9XG5cbiAgICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKSB7XG4gICAgICAgIGxldCBuZWVkc0NvbXBhY3RMYXlvdXQgPSBmYWxzZTtcbiAgICAgICAgbGV0IG5lZWRzUmVjYWxjdWxhdGVSZW5kZXJEYXRhID0gZmFsc2U7XG5cbiAgICAgICAgLy8gVE9ETzogRG9lcyBmaXN0IGNoYW5nZSBuZWVkIHRvIGJlIGNvbXBhY3RlZCBieSBkZWZhdWx0P1xuICAgICAgICAvLyBDb21wYWN0IGxheW91dCB3aGVuZXZlciBzb21lIGRlcGVuZGVudCBwcm9wIGNoYW5nZXMuXG4gICAgICAgIGlmIChjaGFuZ2VzLmNvbXBhY3RUeXBlIHx8IGNoYW5nZXMuY29scyB8fCBjaGFuZ2VzLmxheW91dCkge1xuICAgICAgICAgICAgbmVlZHNDb21wYWN0TGF5b3V0ID0gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENoZWNrIGlmIHdlZSBuZWVkIHRvIHJlY2FsY3VsYXRlIHJlbmRlcmluZyBkYXRhLlxuICAgICAgICBpZiAobmVlZHNDb21wYWN0TGF5b3V0IHx8IGNoYW5nZXMucm93SGVpZ2h0KSB7XG4gICAgICAgICAgICBuZWVkc1JlY2FsY3VsYXRlUmVuZGVyRGF0YSA9IHRydWU7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBPbmx5IGNvbXBhY3QgbGF5b3V0IGlmIGxpYiB1c2VyIGhhcyBwcm92aWRlZCBpdC4gTGliIHVzZXJzIHRoYXQgd2FudCB0byBzYXZlL3N0b3JlIGFsd2F5cyB0aGUgc2FtZSBsYXlvdXQgIGFzIGl0IGlzIHJlcHJlc2VudGV