@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
431 lines • 71.5 kB
JavaScript
import { LiveAnnouncer } from '@angular/cdk/a11y';
import { DOWN_ARROW, ENTER, ESCAPE, LEFT_ARROW, RIGHT_ARROW, SPACE, UP_ARROW, } from '@angular/cdk/keycodes';
import { Directive, ElementRef, HostBinding, HostListener, Input, inject, } from '@angular/core';
import { Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { ActionDirection, DashboardService } from '../dashboard.service';
import { DashboardWidgetComponent } from '../widget/dashboard-widget.component';
import { DashboardGrabHandleService } from './grab-handle.service';
import * as i0 from "@angular/core";
export class DashboardGrabHandleDirective {
constructor() {
this.widget = inject(DashboardWidgetComponent);
this._dashboard = inject(DashboardService);
this._handle = inject(DashboardGrabHandleService);
this._elementRef = inject(ElementRef);
this._announcer = inject(LiveAnnouncer);
/** Specify whether or not this handle can be used to perform moving */
this.uxGrabAllowMove = true;
/** Specify whether or not this handle can be used to perform resizing */
this.uxGrabAllowResize = true;
/** The aria label for the grab handle */
this.uxGrabAriaLabel = this.getDefaultAriaLabel.bind(this);
/** Customize the announcement that is made whenever an item has successfully been moved or resized */
this.uxGrabChangeSuccessAnnouncement = this.getChangeSuccessAnnouncement.bind(this);
/** Customize the announcement that is made whenever an item enters 'grab' mode */
this.uxGrabStartAnnouncement = this.getStartAnnouncement.bind(this);
/** Customize the announcement thqt is made whenever an item cannot be moved */
this.uxGrabMoveFailAnnouncement = this.getMoveFailAnnouncement.bind(this);
/** Customize the announcement thqt is made whenever an item cannot be resized */
this.uxGrabResizeFailAnnouncement = this.getResizeFailAnnouncement.bind(this);
/** Customize the announcement made whenever the moving/resizing is commited */
this.uxGrabConfirmAnnouncement = this.getConfirmAnnouncement.bind(this);
/** Customize the announcement made whenever the moving/resizing is cancelled */
this.uxGrabCancelAnnouncement = this.getCancellationAnnouncement.bind(this);
/** We must programmatically control the focus of the drag handles */
this.tabIndex = -1;
/** Store the current dragging state */
this.isGrabbing = false;
/** Emit when the directive is destroyed to unsubscribe from all observables */
this._onDestroy = new Subject();
if (!this.widget) {
throw new Error('uxDashboardGrabHandle must be used within a dashboard widget');
}
this._handle.addHandle(this);
// subscribe to changes to the current grab state
this._dashboard.isGrabbing$
.pipe(takeUntil(this._onDestroy), map(_widget => _widget === this.widget))
.subscribe(isGrabbing => (this.isGrabbing = isGrabbing));
}
/** Set the initial aria label and subscribe to layout changes */
ngOnInit() {
if (!this.widget.name) {
console.warn(`Dashboard widget ${this.widget.id} must have a valid 'name' to use uxDashboardGrabHandle`);
}
// set the initial aria label
this.ariaLabel = this.getAnnouncement(this.uxGrabAriaLabel);
// update the aria label when layout changes occur
this._dashboard.layout$
.pipe(takeUntil(this._onDestroy))
.subscribe(() => (this.ariaLabel = this.getAnnouncement(this.uxGrabAriaLabel)));
}
/** Unsubscribe from all observables */
ngOnDestroy() {
this._handle.removeHandle(this);
this._onDestroy.next();
this._onDestroy.complete();
}
/** Begin drag mode and cache initial state */
enableDragMode() {
if (!this.isGrabbing) {
// cache the widgets so we can restore when escape is pressed
this._cache = this._lastMovement = this._dashboard.cacheWidgets();
// store the current widget being grabbed
this._dashboard.isGrabbing$.next(this.widget);
this._dashboard.onShiftStart(this.widget);
// announce the grab start
this._announcer.announce(this.getAnnouncement(this.uxGrabStartAnnouncement));
}
}
/** Finish drag mode and commit the current state */
disableDragMode() {
if (this.isGrabbing) {
this._dashboard.isGrabbing$.next(null);
this._lastMovement = null;
this._dashboard.onShiftEnd();
// announce the confirmation
this._announcer.announce(this.getAnnouncement(this.uxGrabConfirmAnnouncement));
}
}
/** Finish the drag mode and restore the original state */
cancelDragMode() {
if (this.isGrabbing) {
this._dashboard.onShiftEnd();
this._dashboard.restoreWidgets(false, this._cache, true);
this._dashboard.setDashboardHeight();
this._dashboard.layout$.next(this._dashboard.getLayoutData());
this._dashboard.isGrabbing$.next(null);
// announce the cancellation
this._announcer.announce(this.getAnnouncement(this.uxGrabCancelAnnouncement));
}
}
/** Toggle the drag mode state */
toggleDragMode() {
this.isGrabbing ? this.disableDragMode() : this.enableDragMode();
}
/** Set the tab index and optionally focus the DOM element */
focus(focusElement = true) {
this.tabIndex = 0;
if (focusElement) {
this._elementRef.nativeElement.focus();
}
}
/** Make this item non-tabbable */
blur() {
this.tabIndex = -1;
}
/** When the grab handle loses focus then exit 'grab' mode */
onBlur() {
this.disableDragMode();
}
/** Handle key events */
onKeydown(event, key, ctrlKey) {
switch (key) {
case ESCAPE:
this.cancelDragMode();
break;
case SPACE:
case ENTER:
this.toggleDragMode();
event.preventDefault();
event.stopPropagation();
break;
case UP_ARROW:
case RIGHT_ARROW:
case DOWN_ARROW:
case LEFT_ARROW:
if (this.isGrabbing) {
ctrlKey ? this.resizeWidget(event, key) : this.moveWidget(event, key);
}
else {
this.moveFocus(event, key);
}
}
}
/** Get an announcement from the inputs - they may be a string or a function so handle both */
getAnnouncement(announcement, ...args) {
return typeof announcement === 'function' ? announcement(this.widget, ...args) : announcement;
}
/** Move the widget in a given direction based on arrow keys */
moveWidget(event, key) {
// check if moving is allowed
if (!this.widget.isDraggable || !this.uxGrabAllowMove) {
return;
}
// attempt to perform the move
this._dashboard.onShift(this.widget, this.getDirectionFromKey(key));
// get the announcable diff
const changes = this.getLayoutDiff();
// if there were changes then announce them
if (changes.length > 0) {
this._announcer.announce(this.getAnnouncement(this.uxGrabChangeSuccessAnnouncement, changes));
}
else {
this._announcer.announce(this.getAnnouncement(this.uxGrabMoveFailAnnouncement, this.getDirectionFromKey(key)));
}
this._lastMovement = this._dashboard.cacheWidgets();
event.preventDefault();
event.stopPropagation();
}
/** Resize the widgets accordingly based on the arrow keys */
resizeWidget(event, key) {
// check if resizing is allowed
if (!this.widget.resizable || !this.uxGrabAllowResize) {
return;
}
this._dashboard.onResize(this.widget, this.getDirectionFromKey(key));
// get the announcable diff
const changes = this.getLayoutDiff();
// if there were changes then announce them
if (changes.length > 0) {
this._announcer.announce(this.getAnnouncement(this.uxGrabChangeSuccessAnnouncement, changes));
}
else {
this._announcer.announce(this.getAnnouncement(this.uxGrabResizeFailAnnouncement, this.getDirectionFromKey(key)));
}
this._lastMovement = this._dashboard.cacheWidgets();
event.preventDefault();
event.stopPropagation();
}
/** Shift focus between the variour grab handles */
moveFocus(event, key) {
switch (key) {
case UP_ARROW:
this._handle.setSiblingItemFocus(this.widget, ActionDirection.Top);
break;
case RIGHT_ARROW:
this._handle.setNextItemFocus(this);
break;
case DOWN_ARROW:
this._handle.setSiblingItemFocus(this.widget, ActionDirection.Bottom);
break;
case LEFT_ARROW:
this._handle.setPreviousItemFocus(this);
break;
}
event.preventDefault();
event.stopPropagation();
}
/** Convert an arrow key code into an ActionDirection enum */
getDirectionFromKey(key) {
switch (key) {
case UP_ARROW:
return ActionDirection.Top;
case RIGHT_ARROW:
return ActionDirection.Right;
case DOWN_ARROW:
return ActionDirection.Bottom;
case LEFT_ARROW:
return ActionDirection.Left;
}
}
/** Supply the default grab handle aria label based on the provided constraints */
getDefaultAriaLabel(widget) {
if (widget.resizable && this.uxGrabAllowResize && widget.isDraggable && this.uxGrabAllowMove) {
return `Press space to move and resize the ${widget.name} panel.`;
}
else if (widget.resizable && this.uxGrabAllowResize) {
return `Press space to resize the ${widget.name} panel.`;
}
else if (widget.isDraggable && this.uxGrabAllowMove) {
return `Press space to move the ${widget.name} panel.`;
}
}
/** Get the default announcement whenever a movement or resize was successful */
getChangeSuccessAnnouncement() {
return `${this.getDiffAnnouncements().join(' ')} Use the cursor keys to continue moving and resizing, enter to commit, or escape to cancel.`;
}
getDiffAnnouncements() {
// map the differences to strings
return this.getLayoutDiff().map(diff => {
const changes = [];
// Handle movement strings
if (diff.isMovedHorizontally && diff.isMovedVertically) {
changes.push(`moved to row ${diff.currentRow}, column ${diff.currentColumn}`);
}
else if (diff.isMovedDown) {
changes.push(`moved down to row ${diff.currentRow}, column ${diff.currentColumn}`);
}
else if (diff.isMovedUp) {
changes.push(`moved up to row ${diff.currentRow}, column ${diff.currentColumn}`);
}
else if (diff.isMovedLeft) {
changes.push(`moved left to row ${diff.currentRow}, column ${diff.currentColumn}`);
}
else if (diff.isMovedRight) {
changes.push(`moved right to row ${diff.currentRow}, column ${diff.currentColumn}`);
}
// handle resize strings
if (diff.isResized) {
changes.push(`resized to ${diff.currentColumnSpan} columns wide and ${diff.currentRowSpan} rows high`);
}
return `${diff.widget.name} panel is ${changes.join(' and ')}.`;
});
}
/** Get the default announcement whenever a movement is not possible due to dashboard boundaries */
getMoveFailAnnouncement(widget, direction) {
switch (direction) {
case ActionDirection.Top:
return `Cannot move the ${widget.name} panel up, because it is at the top edge of the dashboard`;
case ActionDirection.Bottom:
return `Cannot move the ${widget.name} panel down, because it is at the bottom edge of the dashboard`;
case ActionDirection.Right:
return `Cannot move the ${widget.name} panel right, because it is at the right edge of the dashboard`;
case ActionDirection.Left:
return `Cannot move the ${widget.name} panel left, because it is at the left edge of the dashboard`;
}
}
/** Get the default announcement whenever a resize is not possible due to either widget constraints of dashboard bounds */
getResizeFailAnnouncement(widget, direction) {
switch (direction) {
case ActionDirection.Top:
return `Cannot make the ${widget.name} panel shorter, because it is currently at its minimum height.`;
case ActionDirection.Bottom:
return `Cannot make the ${widget.name} panel taller, because it is currently at its maximum height.`;
case ActionDirection.Right:
return `Cannot make the ${widget.name} panel wider, because it is at the right edge of the dashboard.`;
case ActionDirection.Left:
return `Cannot make the ${widget.name} panel narrower, because it is currently at its minimum width.`;
}
}
/** Get the default announcement whenever we enter 'grab' mode */
getStartAnnouncement(widget) {
if (widget.isDraggable && widget.resizable && this.uxGrabAllowMove && this.uxGrabAllowResize) {
return `${widget.name} panel is currently on row ${widget.getRow()}, column ${widget.getColumn()} and is ${widget.getColumnSpan()} columns wide and ${widget.getRowSpan()} rows high. Use the cursor keys to move the widget and the cursor keys with the control modifier to resize the widget. Press enter to commit changes and press escape to cancel changes.`;
}
else if (widget.isDraggable && this.uxGrabAllowMove) {
return `${widget.name} panel is currently on row ${widget.getRow()}, column ${widget.getColumn()}. Use the cursor keys to move the widget. Press enter to commit changes and press escape to cancel changes.`;
}
else if (widget.resizable && this.uxGrabAllowResize) {
return `${widget.name} panel is currently on row ${widget.getRow()}, column ${widget.getColumn()} and is ${widget.getColumnSpan()} columns wide and ${widget.getRowSpan()} rows high. Use the cursor keys with the control modifier to resize the widget. Press enter to commit changes and press escape to cancel changes.`;
}
}
/** Get the default announcement whenever grab mode is exited after a movement or resize */
getConfirmAnnouncement(widget) {
if (widget.isDraggable && widget.resizable && this.uxGrabAllowMove && this.uxGrabAllowResize) {
return `Moving and resizing complete. ${this.getDiffAnnouncements().join(' ')} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
}
else if (widget.isDraggable && this.uxGrabAllowMove) {
return `Moving complete. ${this.getDiffAnnouncements().join(' ')} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
}
else if (widget.resizable && this.uxGrabAllowResize) {
return `Resizing complete. ${this.getDiffAnnouncements().join(' ')} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
}
}
/** Get the default announcement whenever grab mode is exited after being cancelled */
getCancellationAnnouncement(widget) {
if (widget.isDraggable && widget.resizable && this.uxGrabAllowMove && this.uxGrabAllowResize) {
return `Moving and resizing cancelled. ${this.getDashboardAriaLabel()} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
}
else if (widget.isDraggable && this.uxGrabAllowMove) {
return `Moving cancelled. ${this.getDashboardAriaLabel()} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
}
else if (widget.resizable && this.uxGrabAllowResize) {
return `Resizing cancelled. ${this.getDashboardAriaLabel()} ${this.getAnnouncement(this.uxGrabAriaLabel)}`;
}
}
/** Get a description of all dashboard widgets, their positions and sizes */
getDashboardAriaLabel() {
return `Dashboard with ${this._dashboard.options.columns} columns, containing ${this._dashboard.widgets.length} panels. ${this._dashboard.widgets.map(this.getWidgetAriaLabel).join(' ')}`;
}
/** Get a description of a given widget */
getWidgetAriaLabel(widget) {
return `${widget.name} panel in row ${widget.getRow()}, column ${widget.getColumn()}, is ${widget.getColumnSpan()} columns wide and ${widget.getRowSpan()} rows high.`;
}
/** Get an object describing all the changes that have been made to all widgets since the last change */
getLayoutDiff() {
// find all changes
const diffs = this._dashboard.getLayoutData().map(layout => {
// get the most recent cache
const cache = this._lastMovement || this._cache;
// get the actual widget
const widget = this._dashboard.widgets.find(_widget => _widget.id === layout.id);
// get previous position
const previousLayout = cache.find(_widget => _widget.id === layout.id);
// ensure they are all numbers
layout.row = Number(layout.row);
layout.rowSpan = Number(layout.rowSpan);
layout.col = Number(layout.col);
layout.colSpan = Number(layout.colSpan);
previousLayout.row = Number(previousLayout.row);
previousLayout.rowSpan = Number(previousLayout.rowSpan);
previousLayout.column = Number(previousLayout.column);
previousLayout.columnSpan = Number(previousLayout.columnSpan);
return {
widget,
currentRow: layout.row,
currentColumn: layout.col,
currentRowSpan: layout.rowSpan,
currentColumnSpan: layout.colSpan,
previousColumn: previousLayout.column,
previousRow: previousLayout.row,
previousColumnSpan: previousLayout.columnSpan,
previousRowSpan: previousLayout.rowSpan,
isMovedLeft: layout.col < previousLayout.column,
isMovedRight: layout.col > previousLayout.column,
isMovedUp: layout.row < previousLayout.row,
isMovedDown: layout.row > previousLayout.row,
isMovedHorizontally: layout.col !== previousLayout.column,
isMovedVertically: layout.row !== previousLayout.row,
isMoved: layout.col !== previousLayout.column || layout.row !== previousLayout.row,
isResized: previousLayout.columnSpan !== layout.colSpan || previousLayout.rowSpan !== layout.rowSpan,
};
});
// get the order the widgets appear visually
const order = this._handle.getHandlesInOrder().map(handle => handle.widget);
// only return items that have been repositioned or resized
return diffs
.filter(diff => diff.isMoved || diff.isResized)
.sort((diffOne, diffTwo) => {
// sort this so that the item that the user moved is first in the list, and the remainder are in their new order as seen in the dashboard
if (diffOne.widget === this.widget) {
return -1;
}
if (diffTwo.widget === this.widget) {
return 1;
}
// otherwise sort based on their visual order
return order.indexOf(diffOne.widget) < order.indexOf(diffTwo.widget) ? -1 : 1;
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: DashboardGrabHandleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.12", type: DashboardGrabHandleDirective, selector: "[uxDashboardGrabHandle]", inputs: { uxGrabAllowMove: "uxGrabAllowMove", uxGrabAllowResize: "uxGrabAllowResize", uxGrabAriaLabel: "uxGrabAriaLabel", uxGrabChangeSuccessAnnouncement: "uxGrabChangeSuccessAnnouncement", uxGrabStartAnnouncement: "uxGrabStartAnnouncement", uxGrabMoveFailAnnouncement: "uxGrabMoveFailAnnouncement", uxGrabResizeFailAnnouncement: "uxGrabResizeFailAnnouncement", uxGrabConfirmAnnouncement: "uxGrabConfirmAnnouncement", uxGrabCancelAnnouncement: "uxGrabCancelAnnouncement" }, host: { listeners: { "blur": "onBlur()", "keydown": "onKeydown($event,$event.which,$event.ctrlKey)" }, properties: { "attr.aria-label": "this.ariaLabel", "tabIndex": "this.tabIndex" } }, exportAs: ["ux-dashboard-grab-handle"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: DashboardGrabHandleDirective, decorators: [{
type: Directive,
args: [{
selector: '[uxDashboardGrabHandle]',
exportAs: 'ux-dashboard-grab-handle',
}]
}], ctorParameters: () => [], propDecorators: { uxGrabAllowMove: [{
type: Input
}], uxGrabAllowResize: [{
type: Input
}], uxGrabAriaLabel: [{
type: Input
}], uxGrabChangeSuccessAnnouncement: [{
type: Input
}], uxGrabStartAnnouncement: [{
type: Input
}], uxGrabMoveFailAnnouncement: [{
type: Input
}], uxGrabResizeFailAnnouncement: [{
type: Input
}], uxGrabConfirmAnnouncement: [{
type: Input
}], uxGrabCancelAnnouncement: [{
type: Input
}], ariaLabel: [{
type: HostBinding,
args: ['attr.aria-label']
}], tabIndex: [{
type: HostBinding,
args: ['tabIndex']
}], onBlur: [{
type: HostListener,
args: ['blur']
}], onKeydown: [{
type: HostListener,
args: ['keydown', ['$event', '$event.which', '$event.ctrlKey']]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JhYi1oYW5kbGUuZGlyZWN0aXZlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvZGFzaGJvYXJkL2dyYWItaGFuZGxlL2dyYWItaGFuZGxlLmRpcmVjdGl2ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDbEQsT0FBTyxFQUNMLFVBQVUsRUFDVixLQUFLLEVBQ0wsTUFBTSxFQUNOLFVBQVUsRUFDVixXQUFXLEVBQ1gsS0FBSyxFQUNMLFFBQVEsR0FDVCxNQUFNLHVCQUF1QixDQUFDO0FBQy9CLE9BQU8sRUFDTCxTQUFTLEVBQ1QsVUFBVSxFQUNWLFdBQVcsRUFDWCxZQUFZLEVBQ1osS0FBSyxFQUdMLE1BQU0sR0FDUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDaEQsT0FBTyxFQUFFLGVBQWUsRUFBa0IsZ0JBQWdCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN6RixPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNoRixPQUFPLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQzs7QUFNbkUsTUFBTSxPQUFPLDRCQUE0QjtJQXFFdkM7UUFwRVMsV0FBTSxHQUFHLE1BQU0sQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO1FBRWxDLGVBQVUsR0FBRyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUV0QyxZQUFPLEdBQUcsTUFBTSxDQUFDLDBCQUEwQixDQUFDLENBQUM7UUFFN0MsZ0JBQVcsR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7UUFFakMsZUFBVSxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUMsQ0FBQztR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LENBQUM7WUFDWCxDQUFDO1lBRUQsNkNBQTZDO1lBQzdDLE9BQU8sS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDaEYsQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDOytHQWxmVSw0QkFBNEI7bUdBQTVCLDRCQUE0Qjs7NEZBQTVCLDRCQUE0QjtrQkFKeEMsU0FBUzttQkFBQztvQkFDVCxRQUFRLEVBQUUseUJBQXlCO29CQUNuQyxRQUFRLEVBQUUsMEJBQTBCO2lCQUNyQzt3REFhVSxlQUFlO3NCQUF2QixLQUFLO2dCQUdHLGlCQUFpQjtzQkFBekIsS0FBSztnQkFHRyxlQUFlO3NCQUF2QixLQUFLO2dCQUlHLCtCQUErQjtzQkFBdkMsS0FBSztnQkFNRyx1QkFBdUI7c0JBQS9CLEtBQUs7Z0JBSUcsMEJBQTBCO3NCQUFsQyxLQUFLO2dCQU1HLDRCQUE0QjtzQkFBcEMsS0FBSztnQkFNRyx5QkFBeUI7c0JBQWpDLEtBQUs7Z0JBSUcsd0JBQXdCO3NCQUFoQyxLQUFLO2dCQUkwQixTQUFTO3NCQUF4QyxXQUFXO3VCQUFDLGlCQUFpQjtnQkFHTCxRQUFRO3NCQUFoQyxXQUFXO3VCQUFDLFVBQVU7Z0JBcUh2QixNQUFNO3NCQURMLFlBQVk7dUJBQUMsTUFBTTtnQkFPcEIsU0FBUztzQkFEUixZQUFZO3VCQUFDLFNBQVMsRUFBRSxDQUFDLFFBQVEsRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBMaXZlQW5ub3VuY2VyIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2ExMXknO1xuaW1wb3J0IHtcbiAgRE9XTl9BUlJPVyxcbiAgRU5URVIsXG4gIEVTQ0FQRSxcbiAgTEVGVF9BUlJPVyxcbiAgUklHSFRfQVJST1csXG4gIFNQQUNFLFxuICBVUF9BUlJPVyxcbn0gZnJvbSAnQGFuZ3VsYXIvY2RrL2tleWNvZGVzJztcbmltcG9ydCB7XG4gIERpcmVjdGl2ZSxcbiAgRWxlbWVudFJlZixcbiAgSG9zdEJpbmRpbmcsXG4gIEhvc3RMaXN0ZW5lcixcbiAgSW5wdXQsXG4gIE9uRGVzdHJveSxcbiAgT25Jbml0LFxuICBpbmplY3QsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgbWFwLCB0YWtlVW50aWwgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBBY3Rpb25EaXJlY3Rpb24sIERhc2hib2FyZENhY2hlLCBEYXNoYm9hcmRTZXJ2aWNlIH0gZnJvbSAnLi4vZGFzaGJvYXJkLnNlcnZpY2UnO1xuaW1wb3J0IHsgRGFzaGJvYXJkV2lkZ2V0Q29tcG9uZW50IH0gZnJvbSAnLi4vd2lkZ2V0L2Rhc2hib2FyZC13aWRnZXQuY29tcG9uZW50JztcbmltcG9ydCB7IERhc2hib2FyZEdyYWJIYW5kbGVTZXJ2aWNlIH0gZnJvbSAnLi9ncmFiLWhhbmRsZS5zZXJ2aWNlJztcblxuQERpcmVjdGl2ZSh7XG4gIHNlbGVjdG9yOiAnW3V4RGFzaGJvYXJkR3JhYkhhbmRsZV0nLFxuICBleHBvcnRBczogJ3V4LWRhc2hib2FyZC1ncmFiLWhhbmRsZScsXG59KVxuZXhwb3J0IGNsYXNzIERhc2hib2FyZEdyYWJIYW5kbGVEaXJlY3RpdmUgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gIHJlYWRvbmx5IHdpZGdldCA9IGluamVjdChEYXNoYm9hcmRXaWRnZXRDb21wb25lbnQpO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgX2Rhc2hib2FyZCA9IGluamVjdChEYXNoYm9hcmRTZXJ2aWNlKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IF9oYW5kbGUgPSBpbmplY3QoRGFzaGJvYXJkR3JhYkhhbmRsZVNlcnZpY2UpO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgX2VsZW1lbnRSZWYgPSBpbmplY3QoRWxlbWVudFJlZik7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBfYW5ub3VuY2VyID0gaW5qZWN0KExpdmVBbm5vdW5jZXIpO1xuXG4gIC8qKiBTcGVjaWZ5IHdoZXRoZXIgb3Igbm90IHRoaXMgaGFuZGxlIGNhbiBiZSB1c2VkIHRvIHBlcmZvcm0gbW92aW5nICovXG4gIEBJbnB1dCgpIHV4R3JhYkFsbG93TW92ZTogYm9vbGVhbiA9IHRydWU7XG5cbiAgLyoqIFNwZWNpZnkgd2hldGhlciBvciBub3QgdGhpcyBoYW5kbGUgY2FuIGJlIHVzZWQgdG8gcGVyZm9ybSByZXNpemluZyAqL1xuICBASW5wdXQoKSB1eEdyYWJBbGxvd1Jlc2l6ZTogYm9vbGVhbiA9IHRydWU7XG5cbiAgLyoqIFRoZSBhcmlhIGxhYmVsIGZvciB0aGUgZ3JhYiBoYW5kbGUgKi9cbiAgQElucHV0KCkgdXhHcmFiQXJpYUxhYmVsOiAod2lkZ2V0OiBEYXNoYm9hcmRXaWRnZXRDb21wb25lbnQpID0+IHN0cmluZyB8IHN0cmluZyA9XG4gICAgdGhpcy5nZXREZWZhdWx0QXJpYUxhYmVsLmJpbmQodGhpcyk7XG5cbiAgLyoqIEN1c3RvbWl6ZSB0aGUgYW5ub3VuY2VtZW50IHRoYXQgaXMgbWFkZSB3aGVuZXZlciBhbiBpdGVtIGhhcyBzdWNjZXNzZnVsbHkgYmVlbiBtb3ZlZCBvciByZXNpemVkICovXG4gIEBJbnB1dCgpIHV4R3JhYkNoYW5nZVN1Y2Nlc3NBbm5vdW5jZW1lbnQ6IChcbiAgICB3aWRnZXQ6IERhc2hib2FyZFdpZGdldENvbXBvbmVudCxcbiAgICBkaWZmZXJlbmNlczogRGFzaGJvYXJkTGF5b3V0RGlmZltdXG4gICkgPT4gc3RyaW5nIHwgc3RyaW5nID0gdGhpcy5nZXRDaGFuZ2VTdWNjZXNzQW5ub3VuY2VtZW50LmJpbmQodGhpcyk7XG5cbiAgLyoqIEN1c3RvbWl6ZSB0aGUgYW5ub3VuY2VtZW50IHRoYXQgaXMgbWFkZSB3aGVuZXZlciBhbiBpdGVtIGVudGVycyAnZ3JhYicgbW9kZSAqL1xuICBASW5wdXQoKSB1eEdyYWJTdGFydEFubm91bmNlbWVudDogKHdpZGdldDogRGFzaGJvYXJkV2lkZ2V0Q29tcG9uZW50KSA9PiBzdHJpbmcgfCBzdHJpbmcgPVxuICAgIHRoaXMuZ2V0U3RhcnRBbm5vdW5jZW1lbnQuYmluZCh0aGlzKTtcblxuICAvKiogQ3VzdG9taXplIHRoZSBhbm5vdW5jZW1lbnQgdGhxdCBpcyBtYWRlIHdoZW5ldmVyIGFuIGl0ZW0gY2Fubm90IGJlIG1vdmVkICovXG4gIEBJbnB1dCgpIHV4R3JhYk1vdmVGYWlsQW5ub3VuY2VtZW50OiAoXG4gICAgd2lkZ2V0OiBEYXNoYm9hcmRXaWRnZXRDb21wb25lbnQsXG4gICAgZGlyZWN0aW9uOiBBY3Rpb25EaXJlY3Rpb25cbiAgKSA9PiBzdHJpbmcgfCBzdHJpbmcgPSB0aGlzLmdldE1vdmVGYWlsQW5ub3VuY2VtZW50LmJpbmQodGhpcyk7XG5cbiAgLyoqIEN1c3RvbWl6ZSB0aGUgYW5ub3VuY2VtZW50IHRocXQgaXMgbWFkZSB3aGVuZXZlciBhbiBpdGVtIGNhbm5vdCBiZSByZXNpemVkICovXG4gIEBJbnB1dCgpIHV4R3JhYlJlc2l6ZUZhaWxBbm5vdW5jZW1lbnQ6IChcbiAgICB3aWRnZXQ6IERhc2hib2FyZFdpZGdldENvbXBvbmVudCxcbiAgICBkaXJlY3Rpb246IEFjdGlvbkRpcmVjdGlvblxuICApID0+IHN0cmluZyB8IHN0cmluZyA9IHRoaXMuZ2V0UmVzaXplRmFpbEFubm91bmNlbWVudC5iaW5kKHRoaXMpO1xuXG4gIC8qKiBDdXN0b21pemUgdGhlIGFubm91bmNlbWVudCBtYWRlIHdoZW5ldmVyIHRoZSBtb3ZpbmcvcmVzaXppbmcgaXMgY29tbWl0ZWQgKi9cbiAgQElucHV0KCkgdXhHcmFiQ29uZmlybUFubm91bmNlbWVudDogKHdpZGdldDogRGFzaGJvYXJkV2lkZ2V0Q29tcG9uZW50KSA9PiBzdHJpbmcgfCBzdHJpbmcgPVxuICAgIHRoaXMuZ2V0Q29uZmlybUFubm91bmNlbWVudC5iaW5kKHRoaXMpO1xuXG4gIC8qKiBDdXN0b21pemUgdGhlIGFubm91bmNlbWVudCBtYWRlIHdoZW5ldmVyIHRoZSBtb3ZpbmcvcmVzaXppbmcgaXMgY2FuY2VsbGVkICovXG4gIEBJbnB1dCgpIHV4R3JhYkNhbmNlbEFubm91bmNlbWVudDogKHdpZGdldDogRGFzaGJvYXJkV2lkZ2V0Q29tcG9uZW50KSA9PiBzdHJpbmcgfCBzdHJpbmcgPVxuICAgIHRoaXMuZ2V0Q2FuY2VsbGF0aW9uQW5ub3VuY2VtZW50LmJpbmQodGhpcyk7XG5cbiAgLyoqIEJpbmRpbmcgZm9yIHRoZSBncmFiIGhhbmRsZSBhcmlhIGxhYmVsICovXG4gIEBIb3N0QmluZGluZygnYXR0ci5hcmlhLWxhYmVsJykgYXJpYUxhYmVsOiBzdHJpbmc7XG5cbiAgLyoqIFdlIG11c3QgcHJvZ3JhbW1hdGljYWxseSBjb250cm9sIHRoZSBmb2N1cyBvZiB0aGUgZHJhZyBoYW5kbGVzICovXG4gIEBIb3N0QmluZGluZygndGFiSW5kZXgnKSB0YWJJbmRleDogbnVtYmVyID0gLTE7XG5cbiAgLyoqIFN0b3JlIHRoZSBjdXJyZW50IGRyYWdnaW5nIHN0YXRlICovXG4gIGlzR3JhYmJpbmc6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAvKiogU3RvcmUgdGhlIGN1cnJlbnQgbGF5b3V0IHdoZW4gd2UgZW50ZXIgJ2dyYWInIG1vZGUgKi9cbiAgcHJpdmF0ZSBfY2FjaGU6IERhc2hib2FyZENhY2hlW107XG5cbiAgLyoqIFN0b3JlIHRoZSBsYXlvdXQgYWZ0ZXIgdGhlIG1vc3QgcmVjZW50IHN1Y2Nlc3NmdWwgbW92ZSBvciByZXNpemUgKi9cbiAgcHJpdmF0ZSBfbGFzdE1vdmVtZW50OiBEYXNoYm9hcmRDYWNoZVtdO1xuXG4gIC8qKiBFbWl0IHdoZW4gdGhlIGRpcmVjdGl2ZSBpcyBkZXN0cm95ZWQgdG8gdW5zdWJzY3JpYmUgZnJvbSBhbGwgb2JzZXJ2YWJsZXMgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBfb25EZXN0cm95ID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICBpZiAoIXRoaXMud2lkZ2V0KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3V4RGFzaGJvYXJkR3JhYkhhbmRsZSBtdXN0IGJlIHVzZWQgd2l0aGluIGEgZGFzaGJvYXJkIHdpZGdldCcpO1xuICAgIH1cblxuICAgIHRoaXMuX2hhbmRsZS5hZGRIYW5kbGUodGhpcyk7XG5cbiAgICAvLyBzdWJzY3JpYmUgdG8gY2hhbmdlcyB0byB0aGUgY3VycmVudCBncmFiIHN0YXRlXG4gICAgdGhpcy5fZGFzaGJvYXJkLmlzR3JhYmJpbmckXG4gICAgICAucGlwZShcbiAgICAgICAgdGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSksXG4gICAgICAgIG1hcChfd2lkZ2V0ID0+IF93aWRnZXQgPT09IHRoaXMud2lkZ2V0KVxuICAgICAgKVxuICAgICAgLnN1YnNjcmliZShpc0dyYWJiaW5nID0+ICh0aGlzLmlzR3JhYmJpbmcgPSBpc0dyYWJiaW5nKSk7XG4gIH1cblxuICAvKiogU2V0IHRoZSBpbml0aWFsIGFyaWEgbGFiZWwgYW5kIHN1YnNjcmliZSB0byBsYXlvdXQgY2hhbmdlcyAqL1xuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMud2lkZ2V0Lm5hbWUpIHtcbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgYERhc2hib2FyZCB3aWRnZXQgJHt0aGlzLndpZGdldC5pZH0gbXVzdCBoYXZlIGEgdmFsaWQgJ25hbWUnIHRvIHVzZSB1eERhc2hib2FyZEdyYWJIYW5kbGVgXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIHNldCB0aGUgaW5pdGlhbCBhcmlhIGxhYmVsXG4gICAgdGhpcy5hcmlhTGFiZWwgPSB0aGlzLmdldEFubm91bmNlbWVudCh0aGlzLnV4R3JhYkFyaWFMYWJlbCk7XG5cbiAgICAvLyB1cGRhdGUgdGhlIGFyaWEgbGFiZWwgd2hlbiBsYXlvdXQgY2hhbmdlcyBvY2N1clxuICAgIHRoaXMuX2Rhc2hib2FyZC5sYXlvdXQkXG4gICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSlcbiAgICAgIC5zdWJzY3JpYmUoKCkgPT4gKHRoaXMuYXJpYUxhYmVsID0gdGhpcy5nZXRBbm5vdW5jZW1lbnQodGhpcy51eEdyYWJBcmlhTGFiZWwpKSk7XG4gIH1cblxuICAvKiogVW5zdWJzY3JpYmUgZnJvbSBhbGwgb2JzZXJ2YWJsZXMgKi9cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5faGFuZGxlLnJlbW92ZUhhbmRsZSh0aGlzKTtcbiAgICB0aGlzLl9vbkRlc3Ryb3kubmV4dCgpO1xuICAgIHRoaXMuX29uRGVzdHJveS5jb21wbGV0ZSgpO1xuICB9XG5cbiAgLyoqIEJlZ2luIGRyYWcgbW9kZSBhbmQgY2FjaGUgaW5pdGlhbCBzdGF0ZSAqL1xuICBlbmFibGVEcmFnTW9kZSgpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNHcmFiYmluZykge1xuICAgICAgLy8gY2FjaGUgdGhlIHdpZGdldHMgc28gd2UgY2FuIHJlc3RvcmUgd2hlbiBlc2NhcGUgaXMgcHJlc3NlZFxuICAgICAgdGhpcy5fY2FjaGUgPSB0aGlzLl9sYXN0TW92ZW1lbnQgPSB0aGlzLl9kYXNoYm9hcmQuY2FjaGVXaWRnZXRzKCk7XG5cbiAgICAgIC8vIHN0b3JlIHRoZSBjdXJyZW50IHdpZGdldCBiZWluZyBncmFiYmVkXG4gICAgICB0aGlzLl9kYXNoYm9hcmQuaXNHcmFiYmluZyQubmV4dCh0aGlzLndpZGdldCk7XG5cbiAgICAgIH