UNPKG

@ux-aspects/ux-aspects

Version:

Open source user interface framework for building modern, responsive, mobile big data applications

208 lines 46 kB
import { LiveAnnouncer } from '@angular/cdk/a11y'; import { Component, ContentChild, EventEmitter, inject, Input, Output, TemplateRef, ViewChild } from '@angular/core'; import { Subject } from 'rxjs'; import { filter } from 'rxjs/operators'; import { FacetClearButtonDirective } from './facet-clear-button/facet-clear-button.directive'; import { FacetDeselect } from './facet-events'; import { FacetService } from './facet.service'; import { CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop'; import * as i0 from "@angular/core"; import * as i1 from "../../directives/accessibility/focus-indicator/default-focus-indicator.directive"; import * as i2 from "../../directives/accessibility/focus-indicator/focus-indicator.directive"; import * as i3 from "@angular/common"; import * as i4 from "../icon/icon.component"; import * as i5 from "../tooltip/tooltip.directive"; import * as i6 from "@angular/cdk/drag-drop"; export class FacetContainerComponent { /** Allows a predefined set of Facets to be displayed. */ set facets(facets) { this.facetService.facets$.next(facets); } get facets() { return this.facetService.facets$.value; } constructor() { this.facetService = inject(FacetService); this._announcer = inject(LiveAnnouncer); /** Defines the text displayed at the top of the Facet Container. */ this.header = 'Selected'; /** Defines the text to display in the tooltip when hovering over the clear all button. */ this.clearTooltip = 'Clear All'; /** Defines the text to display when there are no selected facets. */ this.emptyText = 'No Items'; /** Determines if the facets can be reordered. */ this.facetsReorderable = false; /** Defines the aria-label for the clear all button. */ this.clearAriaLabel = 'Clear All'; /** Defines the aria-label for the deselect facet button.. */ this.deselectFacetAriaLabel = 'Deselect Facet'; /** If using two-way binding this array will update when the selected facets change. */ this.facetsChange = new EventEmitter(); /** * This will be triggered when a facet is selected, deselected or all facets are deselected. * The event will be an instance of either `FacetSelect`, `FacetDeselect` or `FacetDeselectAll` and * will contain the facet being selected or deselected in a `facet` property * (deselect all will not contain affected facets). */ this.events = new EventEmitter(); this.target = null; this.source = null; this.dragRef = null; this._onDestroy = new Subject(); this.facetService.facets$.subscribe(facets => this.facetsChange.next(facets)); this.facetService.events$.subscribe(event => this.triggerEvent(event)); // announce deselection this.facetService.events$.pipe(filter(event => event instanceof FacetDeselect)) .subscribe(event => this._announcer.announce(`Option ${event.facet.title} deselected.`, 'assertive')); } ngAfterViewInit() { const placeholderElement = this.placeholder.element.nativeElement; placeholderElement.style.display = 'none'; placeholderElement.parentNode.removeChild(placeholderElement); } ngOnDestroy() { this._onDestroy.next(); this._onDestroy.complete(); } onDropListDropped() { if (!this.target) { return; } const placeholderElement = this.placeholder.element.nativeElement; const placeholderParentElement = placeholderElement.parentElement; placeholderElement.style.display = 'none'; placeholderParentElement.removeChild(placeholderElement); placeholderParentElement.appendChild(placeholderElement); placeholderParentElement.insertBefore(this.source.element.nativeElement, placeholderParentElement.children[this.sourceIndex]); if (this.placeholder._dropListRef.isDragging()) { this.placeholder._dropListRef.exit(this.dragRef); } this.target = null; this.source = null; this.dragRef = null; if (this.sourceIndex !== this.targetIndex) { moveItemInArray(this.facets, this.sourceIndex, this.targetIndex); } } onDropListEntered({ item, container }) { if (container == this.placeholder) { return; } const placeholderElement = this.placeholder.element.nativeElement; const sourceElement = item.dropContainer.element.nativeElement; const dropElement = container.element.nativeElement; const dragIndex = Array.prototype.indexOf.call(dropElement.parentElement.children, this.source ? placeholderElement : sourceElement); const dropIndex = Array.prototype.indexOf.call(dropElement.parentElement.children, dropElement); if (!this.source) { this.sourceIndex = dragIndex; this.source = item.dropContainer; sourceElement.parentElement.removeChild(sourceElement); } this.targetIndex = dropIndex; this.target = container; this.dragRef = item._dragRef; placeholderElement.style.display = ''; dropElement.parentElement.insertBefore(placeholderElement, dropIndex > dragIndex ? dropElement.nextSibling : dropElement); this.placeholder._dropListRef.enter(item._dragRef, item.element.nativeElement.offsetLeft, item.element.nativeElement.offsetTop); } selectFacet(facet) { this.facetService.select(facet); } deselectFacet(facet, tag) { // find the index of the item in the selected array const idx = this.facets.findIndex(selectedFacet => facet === selectedFacet); // if match there was no match then finish if (idx === -1) { return; } // remove the last item this.facetService.deselect(facet); // announce the facet removal this._announcer.announce(`Option ${facet.title} deselected.`, 'assertive'); // focus another tag if there is one if (tag) { const sibling = tag.previousElementSibling || tag.nextElementSibling; // if there is a sibling then focus it if (sibling) { sibling.focus(); } } } deselectAllFacets() { // empty the selected array this.facetService.deselectAll(); // announce the facet removal this._announcer.announce('All options deselected.', 'assertive'); } trackBy(_index, facet) { return facet.id || facet.title; } shiftRight(facet, element) { // only move the item if reordering is allowed if (this.facetsReorderable === false) { return; } // perform the movement this.shiftFacet(facet, 1); // the item may become unfocused during the reorder so we should refocus it requestAnimationFrame(() => element.focus()); // announce the move this._announcer.announce(`Option ${facet.title} moved down.`); } shiftLeft(facet, element) { // only move the item if reordering is allowed if (this.facetsReorderable === false) { return; } // perform the movement this.shiftFacet(facet, -1); // the item may become unfocused during the reorder so we should refocus it requestAnimationFrame(() => element.focus()); // announce the move this._announcer.announce(`Option ${facet.title} moved up.`); } shiftFacet(facet, distance) { const index = this.facets.indexOf(facet); const target = index + distance; // Ensure the move is valid if (target < 0 || target === this.facets.length) { return; } // Perform the move this.facets.splice(index, 1); this.facets.splice(target, 0, facet); } triggerEvent(event) { this.events.next(event); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: FacetContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: FacetContainerComponent, selector: "ux-facet-container", inputs: { header: "header", clearTooltip: "clearTooltip", emptyText: "emptyText", facetsReorderable: "facetsReorderable", facets: "facets", clearAriaLabel: "clearAriaLabel", deselectFacetAriaLabel: "deselectFacetAriaLabel" }, outputs: { facetsChange: "facetsChange", events: "events" }, providers: [FacetService], queries: [{ propertyName: "clearButton", first: true, predicate: FacetClearButtonDirective, descendants: true, read: TemplateRef }], viewQueries: [{ propertyName: "placeholder", first: true, predicate: CdkDropList, descendants: true }], ngImport: i0, template: "<!-- Display Any Selected Facets -->\n<div class=\"facets-selected-container\">\n\n <!-- Display Title an Clear Button -->\n <div class=\"facets-selected-header-container\">\n\n <!-- Show The Selected Text -->\n <span class=\"facets-selected-header-label\">{{ header }}</span>\n\n <!-- Add a Clear Button -->\n @if ((facetService.facets$ | async).length > 0) {\n <ng-container [ngTemplateOutlet]=\"clearButton || clearButtonDefault\"></ng-container>\n }\n\n </div>\n\n <!-- Display Tags For Selected Items -->\n <div class=\"facets-selected-list\"\n cdkDropListGroup\n role=\"list\">\n\n <div\n cdkDropList\n (cdkDropListEntered)=\"onDropListEntered($event)\"\n (cdkDropListDropped)=\"onDropListDropped()\"\n ></div>\n\n <!-- Show Selected Tags -->\n @for (facet of (facetService.facets$ | async); track trackBy($index, facet)) {\n <div #tag\n class=\"facet-selected-tag\"\n role=\"listitem\"\n tabindex=\"0\"\n cdkDropList\n (cdkDropListEntered)=\"onDropListEntered($event)\"\n (cdkDropListDropped)=\"onDropListDropped()\"\n [attr.aria-label]=\"facet.title\"\n (mousedown)=\"tag.focus()\"\n (touchmove)=\"$event.preventDefault()\"\n (keydown.ArrowRight)=\"shiftRight(facet, tag)\"\n (keydown.ArrowLeft)=\"shiftLeft(facet, tag)\">\n @if (facetsReorderable) {\n <div class=\"facet-drag-item\" cdkDrag>\n <!-- Display Label -->\n <span class=\"facet-selected-tag-label\">{{ facet.title }}</span>\n <!-- Display Remove Icon -->\n <button type=\"button\"\n uxFocusIndicator\n class=\"facet-selected-remove-btn\"\n [attr.aria-label]=\"deselectFacetAriaLabel\"\n (click)=\"deselectFacet(facet, tag)\">\n <ux-icon name=\"close\"></ux-icon>\n </button>\n </div>\n } @else {\n <span class=\"facet-selected-tag-label\">{{ facet.title }}</span>\n <button type=\"button\"\n uxFocusIndicator\n class=\"facet-selected-remove-btn\"\n [attr.aria-label]=\"deselectFacetAriaLabel\"\n (click)=\"deselectFacet(facet, tag)\">\n <ux-icon name=\"close\"></ux-icon>\n </button>\n }\n </div>\n }\n\n </div>\n\n <!-- Show Message Here if No Facets Selected -->\n @if (emptyText && (facetService.facets$ | async).length === 0) {\n <p class=\"facets-selected-none-label\">{{ emptyText }}</p>\n }\n\n</div>\n\n<!-- Any Facet Elements Should be Added Here By User -->\n<div class=\"facets-region\">\n <ng-content></ng-content>\n</div>\n\n<ng-template #clearButtonDefault>\n <button type=\"button\"\n class=\"btn btn-link btn-icon button-secondary\"\n tabindex=\"0\"\n [attr.aria-label]=\"clearAriaLabel\"\n [uxTooltip]=\"clearTooltip\"\n placement=\"left\"\n (click)=\"deselectAllFacets()\">\n\n <svg class=\"facets-selected-clear-graphic\" focusable=\"false\" viewBox=\"0 0 19 12\" shape-rendering=\"geometricPrecision\">\n <rect class=\"light-grey\" x=\"0\" y=\"2\" width=\"7\" height=\"2\"></rect>\n <rect class=\"dark-grey\" x=\"0\" y=\"5\" width=\"9\" height=\"2\"></rect>\n <rect class=\"light-grey\" x=\"0\" y=\"8\" width=\"7\" height=\"2\"></rect>\n <path class=\"dark-grey\" d=\"M9,1 h1 l9,9 v1 h-1 l-9,-9 v-1 Z\"></path>\n <path class=\"dark-grey\" d=\"M9,11 v-1 l9,-9 h1 v1 l-9,9 h-1 Z\"></path>\n </svg>\n </button>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i1.DefaultFocusIndicatorDirective, selector: ".btn:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor]), a[href]:not([uxFocusIndicator]):not([uxMenuNavigationToggle]):not([uxMenuTriggerFor])" }, { kind: "directive", type: i2.FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i4.IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }, { kind: "directive", type: i5.TooltipDirective, selector: "[uxTooltip]", inputs: ["uxTooltip", "tooltipDisabled", "tooltipClass", "tooltipRole", "tooltipContext", "tooltipDelay", "isOpen", "placement", "fallbackPlacement", "alignment", "showTriggers", "hideTriggers"], outputs: ["shown", "hidden", "isOpenChange"], exportAs: ["ux-tooltip"] }, { kind: "directive", type: i6.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i6.CdkDropListGroup, selector: "[cdkDropListGroup]", inputs: ["cdkDropListGroupDisabled"], exportAs: ["cdkDropListGroup"] }, { kind: "directive", type: i6.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: FacetContainerComponent, decorators: [{ type: Component, args: [{ selector: 'ux-facet-container', providers: [FacetService], preserveWhitespaces: false, template: "<!-- Display Any Selected Facets -->\n<div class=\"facets-selected-container\">\n\n <!-- Display Title an Clear Button -->\n <div class=\"facets-selected-header-container\">\n\n <!-- Show The Selected Text -->\n <span class=\"facets-selected-header-label\">{{ header }}</span>\n\n <!-- Add a Clear Button -->\n @if ((facetService.facets$ | async).length > 0) {\n <ng-container [ngTemplateOutlet]=\"clearButton || clearButtonDefault\"></ng-container>\n }\n\n </div>\n\n <!-- Display Tags For Selected Items -->\n <div class=\"facets-selected-list\"\n cdkDropListGroup\n role=\"list\">\n\n <div\n cdkDropList\n (cdkDropListEntered)=\"onDropListEntered($event)\"\n (cdkDropListDropped)=\"onDropListDropped()\"\n ></div>\n\n <!-- Show Selected Tags -->\n @for (facet of (facetService.facets$ | async); track trackBy($index, facet)) {\n <div #tag\n class=\"facet-selected-tag\"\n role=\"listitem\"\n tabindex=\"0\"\n cdkDropList\n (cdkDropListEntered)=\"onDropListEntered($event)\"\n (cdkDropListDropped)=\"onDropListDropped()\"\n [attr.aria-label]=\"facet.title\"\n (mousedown)=\"tag.focus()\"\n (touchmove)=\"$event.preventDefault()\"\n (keydown.ArrowRight)=\"shiftRight(facet, tag)\"\n (keydown.ArrowLeft)=\"shiftLeft(facet, tag)\">\n @if (facetsReorderable) {\n <div class=\"facet-drag-item\" cdkDrag>\n <!-- Display Label -->\n <span class=\"facet-selected-tag-label\">{{ facet.title }}</span>\n <!-- Display Remove Icon -->\n <button type=\"button\"\n uxFocusIndicator\n class=\"facet-selected-remove-btn\"\n [attr.aria-label]=\"deselectFacetAriaLabel\"\n (click)=\"deselectFacet(facet, tag)\">\n <ux-icon name=\"close\"></ux-icon>\n </button>\n </div>\n } @else {\n <span class=\"facet-selected-tag-label\">{{ facet.title }}</span>\n <button type=\"button\"\n uxFocusIndicator\n class=\"facet-selected-remove-btn\"\n [attr.aria-label]=\"deselectFacetAriaLabel\"\n (click)=\"deselectFacet(facet, tag)\">\n <ux-icon name=\"close\"></ux-icon>\n </button>\n }\n </div>\n }\n\n </div>\n\n <!-- Show Message Here if No Facets Selected -->\n @if (emptyText && (facetService.facets$ | async).length === 0) {\n <p class=\"facets-selected-none-label\">{{ emptyText }}</p>\n }\n\n</div>\n\n<!-- Any Facet Elements Should be Added Here By User -->\n<div class=\"facets-region\">\n <ng-content></ng-content>\n</div>\n\n<ng-template #clearButtonDefault>\n <button type=\"button\"\n class=\"btn btn-link btn-icon button-secondary\"\n tabindex=\"0\"\n [attr.aria-label]=\"clearAriaLabel\"\n [uxTooltip]=\"clearTooltip\"\n placement=\"left\"\n (click)=\"deselectAllFacets()\">\n\n <svg class=\"facets-selected-clear-graphic\" focusable=\"false\" viewBox=\"0 0 19 12\" shape-rendering=\"geometricPrecision\">\n <rect class=\"light-grey\" x=\"0\" y=\"2\" width=\"7\" height=\"2\"></rect>\n <rect class=\"dark-grey\" x=\"0\" y=\"5\" width=\"9\" height=\"2\"></rect>\n <rect class=\"light-grey\" x=\"0\" y=\"8\" width=\"7\" height=\"2\"></rect>\n <path class=\"dark-grey\" d=\"M9,1 h1 l9,9 v1 h-1 l-9,-9 v-1 Z\"></path>\n <path class=\"dark-grey\" d=\"M9,11 v-1 l9,-9 h1 v1 l-9,9 h-1 Z\"></path>\n </svg>\n </button>\n</ng-template>\n" }] }], ctorParameters: () => [], propDecorators: { header: [{ type: Input }], clearTooltip: [{ type: Input }], emptyText: [{ type: Input }], facetsReorderable: [{ type: Input }], facets: [{ type: Input }], clearAriaLabel: [{ type: Input }], deselectFacetAriaLabel: [{ type: Input }], facetsChange: [{ type: Output }], events: [{ type: Output }], clearButton: [{ type: ContentChild, args: [FacetClearButtonDirective, { read: TemplateRef, static: false }] }], placeholder: [{ type: ViewChild, args: [CdkDropList] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjZXQtY29udGFpbmVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL2ZhY2V0cy9mYWNldC1jb250YWluZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvZmFjZXRzL2ZhY2V0LWNvbnRhaW5lci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDbEQsT0FBTyxFQUFpQixTQUFTLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFhLE1BQU0sRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQy9JLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDL0IsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXhDLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLG1EQUFtRCxDQUFDO0FBQzlGLE9BQU8sRUFBRSxhQUFhLEVBQWMsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFL0MsT0FBTyxFQUFnQixXQUFXLEVBQVcsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7Ozs7Ozs7O0FBUTdGLE1BQU0sT0FBTyx1QkFBdUI7SUFnQmhDLHlEQUF5RDtJQUN6RCxJQUFhLE1BQU0sQ0FBQyxNQUFlO1FBQy9CLElBQUksQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRUQsSUFBSSxNQUFNO1FBQ04sT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUM7SUFDM0MsQ0FBQztJQStCRDtRQXJEUyxpQkFBWSxH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Q0FBQyxDQUFDO0lBQzVCLENBQUM7K0dBMU9RLHVCQUF1QjttR0FBdkIsdUJBQXVCLDRVQUhyQixDQUFDLFlBQVksQ0FBQyxtRUE2Q1gseUJBQXlCLDJCQUFVLFdBQVcsMEVBRWpELFdBQVcsZ0RDN0QxQiw2K0dBbUdBOzs0RkRsRmEsdUJBQXVCO2tCQU5uQyxTQUFTOytCQUNJLG9CQUFvQixhQUVuQixDQUFDLFlBQVksQ0FBQyx1QkFDSixLQUFLO3dEQU9qQixNQUFNO3NCQUFkLEtBQUs7Z0JBR0csWUFBWTtzQkFBcEIsS0FBSztnQkFHRyxTQUFTO3NCQUFqQixLQUFLO2dCQUdHLGlCQUFpQjtzQkFBekIsS0FBSztnQkFHTyxNQUFNO3NCQUFsQixLQUFLO2dCQVNHLGNBQWM7c0JBQXRCLEtBQUs7Z0JBR0csc0JBQXNCO3NCQUE5QixLQUFLO2dCQUdJLFlBQVk7c0JBQXJCLE1BQU07Z0JBT0csTUFBTTtzQkFBZixNQUFNO2dCQUd3RSxXQUFXO3NCQUF6RixZQUFZO3VCQUFDLHlCQUF5QixFQUFFLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFO2dCQUVyRCxXQUFXO3NCQUFsQyxTQUFTO3VCQUFDLFdBQVciLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBMaXZlQW5ub3VuY2VyIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2ExMXknO1xuaW1wb3J0IHsgQWZ0ZXJWaWV3SW5pdCwgQ29tcG9uZW50LCBDb250ZW50Q2hpbGQsIEV2ZW50RW1pdHRlciwgaW5qZWN0LCBJbnB1dCwgT25EZXN0cm95LCBPdXRwdXQsIFRlbXBsYXRlUmVmLCBWaWV3Q2hpbGQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGZpbHRlciB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IFJlb3JkZXJFdmVudCB9IGZyb20gJy4uLy4uL2RpcmVjdGl2ZXMvcmVvcmRlcmFibGUvaW5kZXgnO1xuaW1wb3J0IHsgRmFjZXRDbGVhckJ1dHRvbkRpcmVjdGl2ZSB9IGZyb20gJy4vZmFjZXQtY2xlYXItYnV0dG9uL2ZhY2V0LWNsZWFyLWJ1dHRvbi5kaXJlY3RpdmUnO1xuaW1wb3J0IHsgRmFjZXREZXNlbGVjdCwgRmFjZXRFdmVudCB9IGZyb20gJy4vZmFjZXQtZXZlbnRzJztcbmltcG9ydCB7IEZhY2V0U2VydmljZSB9IGZyb20gJy4vZmFjZXQuc2VydmljZSc7XG5pbXBvcnQgeyBGYWNldCB9IGZyb20gJy4vbW9kZWxzL2ZhY2V0JztcbmltcG9ydCB7IENka0RyYWdFbnRlciwgQ2RrRHJvcExpc3QsIERyYWdSZWYsIG1vdmVJdGVtSW5BcnJheSB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9kcmFnLWRyb3AnO1xuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ3V4LWZhY2V0LWNvbnRhaW5lcicsXG4gICAgdGVtcGxhdGVVcmw6ICcuL2ZhY2V0LWNvbnRhaW5lci5jb21wb25lbnQuaHRtbCcsXG4gICAgcHJvdmlkZXJzOiBbRmFjZXRTZXJ2aWNlXSxcbiAgICBwcmVzZXJ2ZVdoaXRlc3BhY2VzOiBmYWxzZVxufSlcbmV4cG9ydCBjbGFzcyBGYWNldENvbnRhaW5lckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uRGVzdHJveSwgQWZ0ZXJWaWV3SW5pdCB7XG4gICAgcmVhZG9ubHkgZmFjZXRTZXJ2aWNlID0gaW5qZWN0KEZhY2V0U2VydmljZSk7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfYW5ub3VuY2VyID0gaW5qZWN0KExpdmVBbm5vdW5jZXIpO1xuXG4gICAgLyoqIERlZmluZXMgdGhlIHRleHQgZGlzcGxheWVkIGF0IHRoZSB0b3Agb2YgdGhlIEZhY2V0IENvbnRhaW5lci4gKi9cbiAgICBASW5wdXQoKSBoZWFkZXI6IHN0cmluZyA9ICdTZWxlY3RlZCc7XG5cbiAgICAvKiogRGVmaW5lcyB0aGUgdGV4dCB0byBkaXNwbGF5IGluIHRoZSB0b29sdGlwIHdoZW4gaG92ZXJpbmcgb3ZlciB0aGUgY2xlYXIgYWxsIGJ1dHRvbi4gKi9cbiAgICBASW5wdXQoKSBjbGVhclRvb2x0aXA6IHN0cmluZyA9ICdDbGVhciBBbGwnO1xuXG4gICAgLyoqIERlZmluZXMgdGhlIHRleHQgdG8gZGlzcGxheSB3aGVuIHRoZXJlIGFyZSBubyBzZWxlY3RlZCBmYWNldHMuICovXG4gICAgQElucHV0KCkgZW1wdHlUZXh0OiBzdHJpbmcgPSAnTm8gSXRlbXMnO1xuXG4gICAgLyoqIERldGVybWluZXMgaWYgdGhlIGZhY2V0cyBjYW4gYmUgcmVvcmRlcmVkLiAqL1xuICAgIEBJbnB1dCgpIGZhY2V0c1Jlb3JkZXJhYmxlOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKiogQWxsb3dzIGEgcHJlZGVmaW5lZCBzZXQgb2YgRmFjZXRzIHRvIGJlIGRpc3BsYXllZC4gKi9cbiAgICBASW5wdXQoKSBzZXQgZmFjZXRzKGZhY2V0czogRmFjZXRbXSkge1xuICAgICAgICB0aGlzLmZhY2V0U2VydmljZS5mYWNldHMkLm5leHQoZmFjZXRzKTtcbiAgICB9XG5cbiAgICBnZXQgZmFjZXRzKCk6IEZhY2V0W10ge1xuICAgICAgICByZXR1cm4gdGhpcy5mYWNldFNlcnZpY2UuZmFjZXRzJC52YWx1ZTtcbiAgICB9XG5cbiAgICAvKiogRGVmaW5lcyB0aGUgYXJpYS1sYWJlbCBmb3IgdGhlIGNsZWFyIGFsbCBidXR0b24uICovXG4gICAgQElucHV0KCkgY2xlYXJBcmlhTGFiZWw6IHN0cmluZyA9ICdDbGVhciBBbGwnO1xuXG4gICAgLyoqIERlZmluZXMgdGhlIGFyaWEtbGFiZWwgZm9yIHRoZSBkZXNlbGVjdCBmYWNldCBidXR0b24uLiAqL1xuICAgIEBJbnB1dCgpIGRlc2VsZWN0RmFjZXRBcmlhTGFiZWw6IHN0cmluZyA9ICdEZXNlbGVjdCBGYWNldCc7XG5cbiAgICAvKiogSWYgdXNpbmcgdHdvLXdheSBiaW5kaW5nIHRoaXMgYXJyYXkgd2lsbCB1cGRhdGUgd2hlbiB0aGUgc2VsZWN0ZWQgZmFjZXRzIGNoYW5nZS4gKi9cbiAgICBAT3V0cHV0KCkgZmFjZXRzQ2hhbmdlOiBFdmVudEVtaXR0ZXI8RmFjZXRbXT4gPSBuZXcgRXZlbnRFbWl0dGVyPEZhY2V0W10+KCk7XG5cbiAgICAvKipcbiAgICAgKiBUaGlzIHdpbGwgYmUgdHJpZ2dlcmVkIHdoZW4gYSBmYWNldCBpcyBzZWxlY3RlZCwgZGVzZWxlY3RlZCBvciBhbGwgZmFjZXRzIGFyZSBkZXNlbGVjdGVkLlxuICAgICAqIFRoZSBldmVudCB3aWxsIGJlIGFuIGluc3RhbmNlIG9mIGVpdGhlciBgRmFjZXRTZWxlY3RgLCBgRmFjZXREZXNlbGVjdGAgb3IgYEZhY2V0RGVzZWxlY3RBbGxgIGFuZFxuICAgICAqIHdpbGwgY29udGFpbiB0aGUgZmFjZXQgYmVpbmcgc2VsZWN0ZWQgb3IgZGVzZWxlY3RlZCBpbiBhIGBmYWNldGAgcHJvcGVydHlcbiAgICAgKiAoZGVzZWxlY3QgYWxsIHdpbGwgbm90IGNvbnRhaW4gYWZmZWN0ZWQgZmFjZXRzKS4gKi9cbiAgICBAT3V0cHV0KCkgZXZlbnRzOiBFdmVudEVtaXR0ZXI8RmFjZXRFdmVudD4gPSBuZXcgRXZlbnRFbWl0dGVyPEZhY2V0RXZlbnQ+KCk7XG5cbiAgICAvKiogQWxsb3cgYSBjdXN0b20gY2xlYXIgYnV0dG9uICovXG4gICAgQENvbnRlbnRDaGlsZChGYWNldENsZWFyQnV0dG9uRGlyZWN0aXZlLCB7IHJlYWQ6IFRlbXBsYXRlUmVmLCBzdGF0aWM6IGZhbHNlIH0pIGNsZWFyQnV0dG9uOiBUZW1wbGF0ZVJlZjxGYWNldENsZWFyQnV0dG9uRGlyZWN0aXZlPjtcblxuICAgIEBWaWV3Q2hpbGQoQ2RrRHJvcExpc3QpIHBsYWNlaG9sZGVyOiBDZGtEcm9wTGlzdDtcblxuICAgIHByaXZhdGUgdGFyZ2V0OiBDZGtEcm9wTGlzdCA9IG51bGw7XG4gICAgcHJpdmF0ZSB0YXJnZXRJbmRleDogbnVtYmVyO1xuICAgIHByaXZhdGUgc291cmNlOiBDZGtEcm9wTGlzdCA9IG51bGw7XG4gICAgcHJpdmF0ZSBzb3VyY2VJbmRleDogbnVtYmVyO1xuICAgIHByaXZhdGUgZHJhZ1JlZjogRHJhZ1JlZiA9IG51bGw7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9vbkRlc3Ryb3kgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuZmFjZXRTZXJ2aWNlLmZhY2V0cyQuc3Vic2NyaWJlKGZhY2V0cyA9PiB0aGlzLmZhY2V0c0NoYW5nZS5uZXh0KGZhY2V0cykpO1xuICAgICAgICB0aGlzLmZhY2V0U2VydmljZS5ldmVudHMkLnN1YnNjcmliZShldmVudCA9PiB0aGlzLnRyaWdnZXJFdmVudChldmVudCkpO1xuXG4gICAgICAgIC8vIGFubm91bmNlIGRlc2VsZWN0aW9uXG4gICAgICAgIHRoaXMuZmFjZXRTZXJ2aWNlLmV2ZW50cyQucGlwZShmaWx0ZXI8RmFjZXREZXNlbGVjdD4oZXZlbnQgPT4gZXZlbnQgaW5zdGFuY2VvZiBGYWNldERlc2VsZWN0KSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoZXZlbnQgPT4gdGhpcy5fYW5ub3VuY2VyLmFubm91bmNlKGBPcHRpb24gJHtldmVudC5mYWNldC50aXRsZX0gZGVzZWxlY3RlZC5gLCAnYXNzZXJ0aXZlJykpO1xuICAgIH1cblxuICAgIG5nQWZ0ZXJWaWV3SW5pdCgpIHtcbiAgICAgICAgY29uc3QgcGxhY2Vob2xkZXJFbGVtZW50ID0gdGhpcy5wbGFjZWhvbGRlci5lbGVtZW50Lm5hdGl2ZUVsZW1lbnQ7XG4gICAgICAgIHBsYWNlaG9sZGVyRWxlbWVudC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgICBwbGFjZWhvbGRlckVsZW1lbnQucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChwbGFjZWhvbGRlckVsZW1lbnQpO1xuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kubmV4dCgpO1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICBvbkRyb3BMaXN0RHJvcHBlZCgpIHtcbiAgICAgICAgaWYgKCF0aGlzLnRhcmdldCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHBsYWNlaG9sZGVyRWxlbWVudDogSFRNTEVsZW1lbnQgPSB0aGlzLnBsYWNlaG9sZGVyLmVsZW1lbnQubmF0aXZlRWxlbWVudDtcbiAgICAgICAgY29uc3QgcGxhY2Vob2xkZXJQYXJlbnRFbGVtZW50OiBIVE1MRWxlbWVudCA9IHBsYWNlaG9sZGVyRWxlbWVudC5wYXJlbnRFbGVtZW50O1xuICAgICAgICBwbGFjZWhvbGRlckVsZW1lbnQuc3R5bGUuZGlzcGxheSA9ICdub25lJztcblxuICAgICAgICBwbGFjZWhvbGRlclBhcmVudEVsZW1lbnQucmVtb3ZlQ2hpbGQocGxhY2Vob2xkZXJFbGVtZW50KTtcbiAgICAgICAgcGxhY2Vob2xkZXJQYXJlbnRFbGVtZW50LmFwcGVuZENoaWxkKHBsYWNlaG9sZGVyRWxlbWVudCk7XG4gICAgICAgIHBsYWNlaG9sZGVyUGFyZW50RWxlbWVudC5pbnNlcnRCZWZvcmUoXG4gICAgICAgICAgICB0aGlzLnNvdXJjZS5lbGVtZW50Lm5hdGl2ZUVsZW1lbnQsXG4gICAgICAgICAgICBwbGFjZWhvbGRlclBhcmVudEVsZW1lbnQuY2hpbGRyZW5bdGhpcy5zb3VyY2VJbmRleF1cbiAgICAgICAgKTtcblxuICAgICAgICBpZiAodGhpcy5wbGFjZWhvbGRlci5fZHJvcExpc3RSZWYuaXNEcmFnZ2luZygpKSB7XG4gICAgICAgICAgICB0aGlzLnBsYWNlaG9sZGVyLl9kcm9wTGlzdFJlZi5leGl0KHRoaXMuZHJhZ1JlZik7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnRhcmdldCA9IG51bGw7XG4gICAgICAgIHRoaXMuc291cmNlID0gbnVsbDtcbiAgICAgICAgdGhpcy5kcmFnUmVmID0gbnVsbDtcblxuICAgICAgICBpZiAodGhpcy5zb3VyY2VJbmRleCAhPT0gdGhpcy50YXJnZXRJbmRleCkge1xuICAgICAgICAgICAgbW92ZUl0ZW1JbkFycmF5KHRoaXMuZmFjZXRzLCB0aGlzLnNvdXJjZUluZGV4LCB0aGlzLnRhcmdldEluZGV4KTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uRHJvcExpc3RFbnRlcmVkKHsgaXRlbSwgY29udGFpbmVyIH06IENka0RyYWdFbnRlcikge1xuICAgICAgICBpZiAoY29udGFpbmVyID09IHRoaXMucGxhY2Vob2xkZXIpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBwbGFjZWhvbGRlckVsZW1lbnQ6IEhUTUxFbGVtZW50ID0gdGhpcy5wbGFjZWhvbGRlci5lbGVtZW50Lm5hdGl2ZUVsZW1lbnQ7XG4gICAgICAgIGNvbnN0IHNvdXJjZUVsZW1lbnQ6IEhUTUxFbGVtZW50ID0gaXRlbS5kcm9wQ29udGFpbmVyLmVsZW1lbnQubmF0aXZlRWxlbWVudDtcbiAgICAgICAgY29uc3QgZHJvcEVsZW1lbnQ6IEhUTUxFbGVtZW50ID0gY29udGFpbmVyLmVsZW1lbnQubmF0aXZlRWxlbWVudDtcbiAgICAgICAgXG4gICAgICAgIGNvbnN0IGRyYWdJbmRleDogbnVtYmVyID0gQXJyYXkucHJvdG90eXBlLmluZGV4T2YuY2FsbChcbiAgICAgICAgICAgIGRyb3BFbGVtZW50LnBhcmVudEVsZW1lbnQuY2hpbGRyZW4sXG4gICAgICAgICAgICB0aGlzLnNvdXJjZSA/IHBsYWNlaG9sZGVyRWxlbWVudCA6IHNvdXJjZUVsZW1lbnRcbiAgICAgICAgKTtcbiAgICAgICAgY29uc3QgZHJvcEluZGV4OiBudW1iZXIgPSBBcnJheS5wcm90b3R5cGUuaW5kZXhPZi5jYWxsKFxuICAgICAgICAgICAgZHJvcEVsZW1lbnQucGFyZW50RWxlbWVudC5jaGlsZHJlbixcbiAgICAgICAgICAgIGRyb3BFbGVtZW50XG4gICAgICAgICk7XG5cbiAgICAgICAgaWYgKCF0aGlzLnNvdXJjZSkge1xuICAgICAgICAgICAgdGhpcy5zb3VyY2VJbmRleCA9IGRyYWdJbmRleDtcbiAgICAgICAgICAgIHRoaXMuc291cmNlID0gaXRlbS5kcm9wQ29udGFpbmVyO1xuICAgICAgICAgICAgc291cmNlRWxlbWVudC5wYXJlbnRFbGVtZW50LnJlbW92ZUNoaWxkKHNvdXJjZUVsZW1lbnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy50YXJnZXRJbmRleCA9IGRyb3BJbmRleDtcbiAgICAgICAgdGhpcy50YXJnZXQgPSBjb250YWluZXI7XG4gICAgICAgIHRoaXMuZHJhZ1JlZiA9IGl0ZW0uX2RyYWdSZWY7XG4gICAgICAgIHBsYWNlaG9sZGVyRWxlbWVudC5zdHlsZS5kaXNwbGF5ID0gJyc7XG5cbiAgICAgICAgZHJvcEVsZW1lbnQucGFyZW50RWxlbWVudC5pbnNlcnRCZWZvcmUoXG4gICAgICAgICAgICBwbGFjZWhvbGRlckVsZW1lbnQsXG4gICAgICAgICAgICBkcm9wSW5kZXggPiBkcmFnSW5kZXggPyBkcm9wRWxlbWVudC5uZXh0U2libGluZyA6IGRyb3BFbGVtZW50XG4gICAgICAgICk7XG5cbiAgICAgICAgdGhpcy5wbGFjZWhvbGRlci5fZHJvcExpc3RSZWYuZW50ZXIoXG4gICAgICAgICAgICBpdGVtLl9kcmFnUmVmLFxuICAgICAgICAgICAgaXRlbS5lbGVtZW50Lm5hdGl2ZUVsZW1lbnQub2Zmc2V0TGVmdCxcbiAgICAgICAgICAgIGl0ZW0uZWxlbWVudC5uYXRpdmVFbGVtZW50Lm9mZnNldFRvcCxcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBzZWxlY3RGYWNldChmYWNldDogRmFjZXQpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5mYWNldFNlcnZpY2Uuc2VsZWN0KGZhY2V0KTtcbiAgICB9XG5cbiAgICBkZXNlbGVjdEZhY2V0KGZhY2V0OiBGYWNldCwgdGFnPzogSFRNTEVsZW1lbnQpOiB2b2lkIHtcblxuICAgICAgICAvLyBmaW5kIHRoZSBpbmRleCBvZiB0aGUgaXRlbSBpbiB0aGUgc2VsZWN0ZWQgYXJyYXlcbiAgICAgICAgY29uc3QgaWR4ID0gdGhpcy5mYWNldHMuZmluZEluZGV4KHNlbGVjdGVkRmFjZXQgPT4gZmFjZXQgPT09IHNlbGVjdGVkRmFjZXQpO1xuXG4gICAgICAgIC8vIGlmIG1hdGNoIHRoZXJlIHdhcyBubyBtYXRjaCB0aGVuIGZpbmlzaFxuICAgICAgICBpZiAoaWR4ID09PSAtMSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gcmVtb3ZlIHRoZSBsYXN0IGl0ZW1cbiAgICAgICAgdGhpcy5mYWNldFNlcnZpY2UuZGVzZWxlY3QoZmFjZXQpO1xuXG4gICAgICAgIC8vIGFubm91bmNlIHRoZSBmYWNldCByZW1vdmFsXG4gICAgICAgIHRoaXMuX2Fubm91bmNlci5hbm5vdW5jZShgT3B0aW9uICR7ZmFjZXQudGl0bGV9IGRlc2VsZWN0ZWQuYCwgJ2Fzc2VydGl2ZScpO1xuXG4gICAgICAgIC8vIGZvY3VzIGFub3RoZXIgdGFnIGlmIHRoZXJlIGlzIG9uZVxuICAgICAgICBpZiAodGFnKSB7XG4gICAgICAgICAgICBjb25zdCBzaWJsaW5nID0gdGFnLnByZXZpb3VzRWxlbWVudFNpYmxpbmcgfHwgdGFnLm5leHRFbGVtZW50U2libGluZztcblxuICAgICAgICAgICAgLy8gaWYgdGhlcmUgaXMgYSBzaWJsaW5nIHRoZW4gZm9jdXMgaXRcbiAgICAgICAgICAgIGlmIChzaWJsaW5nKSB7XG4gICAgICAgICAgICAgICAgKHNpYmxpbmcgYXMgSFRNTEVsZW1lbnQpLmZvY3VzKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBkZXNlbGVjdEFsbEZhY2V0cygpOiB2b2lkIHtcblxuICAgICAgICAvLyBlbXB0eSB0aGUgc2VsZWN0ZWQgYXJyYXlcbiAgICAgICAgdGhpcy5mYWNldFNlcnZpY2UuZGVzZWxlY3RBbGwoKTtcblxuICAgICAgICAvLyBhbm5vdW5jZSB0aGUgZmFjZXQgcmVtb3ZhbFxuICAgICAgICB0aGlzLl9hbm5vdW5jZXIuYW5ub3VuY2UoJ0FsbCBvcHRpb25zIGRlc2VsZWN0ZWQuJywgJ2Fzc2VydGl2ZScpO1xuICAgIH1cblxuICAgIHRyYWNrQnkoX2luZGV4OiBudW1iZXIsIGZhY2V0OiBGYWNldCk6IHN0cmluZyB8IG51bWJlciB7XG4gICAgICAgIHJldHVybiBmYWNldC5pZCB8fCBmYWNldC50aXRsZTtcbiAgICB9XG5cbiAgICBzaGlmdFJpZ2h0KGZhY2V0OiBGYWNldCwgZWxlbWVudDogSFRNTEVsZW1lbnQpOiB2b2lkIHtcbiAgICAgICAgLy8gb25seSBtb3ZlIHRoZSBpdGVtIGlmIHJlb3JkZXJpbmcgaXMgYWxsb3dlZFxuICAgICAgICBpZiAodGhpcy5mYWNldHNSZW9yZGVyYWJsZSA9PT0gZmFsc2UpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHBlcmZvcm0gdGhlIG1vdmVtZW50XG4gICAgICAgIHRoaXMuc2hpZnRGYWNldChmYWNldCwgMSk7XG5cbiAgICAgICAgLy8gdGhlIGl0ZW0gbWF5IGJlY29tZSB1bmZvY3VzZWQgZHVyaW5nIHRoZSByZW9yZGVyIHNvIHdlIHNob3VsZCByZWZvY3VzIGl0XG4gICAgICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiBlbGVtZW50LmZvY3VzKCkpO1xuXG4gICAgICAgIC8vIGFubm91bmNlIHRoZSBtb3ZlXG4gICAgICAgIHRoaXMuX2Fubm91bmNlci5hbm5vdW5jZShgT3B0aW9uICR7ZmFjZXQudGl0bGV9IG1vdmVkIGRvd24uYCk7XG4gICAgfVxuXG4gICAgc2hpZnRMZWZ0KGZhY2V0OiBGYWNldCwgZWxlbWVudDogSFRNTEVsZW1lbnQpOiB2b2lkIHtcbiAgICAgICAgLy8gb25seSBtb3ZlIHRoZSBpdGVtIGlmIHJlb3JkZXJpbmcgaXMgYWxsb3dlZFxuICAgICAgICBpZiAodGhpcy5mYWNldHNSZW9yZGVyYWJsZSA9PT0gZmFsc2UpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHBlcmZvcm0gdGhlIG1vdmVtZW50XG4gICAgICAgIHRoaXMuc2hpZnRGYWNldChmYWNldCwgLTEpO1xuXG4gICAgICAgIC8vIHRoZSBpdGVtIG1heSBiZWNvbWUgdW5mb2N1c2VkIGR1cmluZyB0aGUgcmVvcmRlciBzbyB3ZSBzaG91bGQgcmVmb2N1cyBpdFxuICAgICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4gZWxlbWVudC5mb2N1cygpKTtcblxuICAgICAgICAvLyBhbm5vdW5jZSB0aGUgbW92ZVxuICAgICAgICB0aGlzLl9hbm5vdW5jZXIuYW5ub3VuY2UoYE9wdGlvbiAke2ZhY2V0LnRpdGxlfSBtb3ZlZCB1cC5gKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNoaWZ0RmFjZXQoZmFjZXQ6IEZhY2V0LCBkaXN0YW5jZTogbnVtYmVyKSB7XG4gICAgICAgIGNvbnN0IGluZGV4ID0gdGhpcy5mYWNldHMuaW5kZXhPZihmYWNldCk7XG4gICAgICAgIGNvbnN0IHRhcmdldCA9IGluZGV4ICsgZGlzdGFuY2U7XG5cbiAgICAgICAgLy8gRW5zdXJlIHRoZSBtb3ZlIGlzIHZhbGlkXG4gICAgICAgIGlmICh0YXJnZXQgPCAwIHx8IHRhcmdldCA9PT0gdGhpcy5mYWNldHMubGVuZ3RoKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBQZXJmb3JtIHRoZSBtb3ZlXG4gICAgICAgIHRoaXMuZmFjZXRzLnNwbGljZShpbmRleCwgMSk7XG4gICAgICAgIHRoaXMuZmFjZXRzLnNwbGljZSh0YXJnZXQsIDAsIGZhY2V0KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHRyaWdnZXJFdmVudChldmVudDogRmFjZXRFdmVudCkge1xuICAgICAgICB0aGlzLmV2ZW50cy5uZXh0KGV2ZW50KTtcbiAgICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRmFjZXRSZW9yZGVyRXZlbnQgZXh0ZW5kcyBSZW9yZGVyRXZlbnQge1xuICAgIGluZGV4OiBudW1iZXI7XG59XG4iLCI8IS0tIERpc3BsYXkgQW55IFNlbGVjdGVkIEZhY2V0cyAtLT5cbjxkaXYgY2xhc3M9XCJmYWNldHMtc2VsZWN0ZWQtY29udGFpbmVyXCI+XG5cbiAgPCEtLSBEaXNwbGF5IFRpdGxlIGFuIENsZWFyIEJ1dHRvbiAtLT5cbiAgPGRpdiBjbGFzcz1cImZhY2V0cy1zZWxlY3RlZC1oZWFkZXItY29udGFpbmVyXCI+XG5cbiAgICA8IS0tIFNob3cgVGhlIFNlbGVjdGVkIFRleHQgLS0+XG4gICAgPHNwYW4gY2xhc3M9XCJmYWNldHMtc2VsZWN0ZWQtaGVhZGVyLWxhYmVsXCI+e3sgaGVhZGVyIH19PC9zcGFuPlxuXG4gICAgPCEtLSBBZGQgYSBDbGVhciBCdXR0b24gLS0+XG4gICAgQGlmICgoZmFjZXRTZXJ2aWNlLmZhY2V0cyQgfCBhc3luYykubGVuZ3RoID4gMCkge1xuICAgICAgPG5nLWNvbnRhaW5lciBbbmdUZW1wbGF0ZU91dGxldF09XCJjbGVhckJ1dHRvbiB8fCBjbGVhckJ1dHRvbkRlZmF1bHRcIj48L25nLWNvbnRhaW5lcj5cbiAgICB9XG5cbiAgPC9kaXY+XG5cbiAgPCEtLSBEaXNwbGF5IFRhZ3MgRm9yIFNlbGVjdGVkIEl0ZW1zIC0tPlxuICA8ZGl2IGNsYXNzPVwiZmFjZXRzLXNlbGVjdGVkLWxpc3RcIlxuICAgIGNka0Ryb3BMaXN0R3JvdXBcbiAgICByb2xlPVwibGlzdFwiPlxuXG4gICAgPGRpdlxuICAgICAgY2RrRHJvcExpc3RcbiAgICAgIChjZGtEcm9wTGlzdEVudGVyZWQpPVwib25Ecm9wTGlzdEVudGVyZWQoJGV2ZW50KVwiXG4gICAgICAoY2RrRHJvcExpc3REcm9wcGVkKT1cIm9uRHJvcExpc3REcm9wcGVkKClcIlxuICAgID48L2Rpdj5cblxuICAgIDwhLS0gU2hvdyBTZWxlY3RlZCBUYWdzIC0tPlxuICAgIEBmb3IgKGZhY2V0IG9mIChmYWNldFNlcnZpY2UuZmFjZXRzJCB8IGFzeW5jKTsgdHJhY2sgdHJhY2tCeSgkaW5kZXgsIGZhY2V0KSkge1xuICAgICAgPGRpdiAjdGFnXG4gICAgICAgIGNsYXNzPVwiZmFjZXQtc2VsZWN0ZWQtdGFnXCJcbiAgICAgICAgcm9sZT1cImxpc3RpdGVtXCJcbiAgICAgICAgdGFiaW5kZXg9XCIwXCJcbiAgICAgICAgY2RrRHJvcExpc3RcbiAgICAgICAgKGNka0Ryb3BMaXN0RW50ZXJlZCk9XCJvbkRyb3BMaXN0RW50ZXJlZCgkZXZlbnQpXCJcbiAgICAgICAgKGNka0Ryb3BMaXN0RHJvcHBlZCk9XCJvbkRyb3BMaXN0RHJvcHBlZCgpXCJcbiAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCJmYWNldC50aXRsZVwiXG4gICAgICAgIChtb3VzZWRvd24pPVwidGFnLmZvY3VzKClcIlxuICAgICAgICAodG91Y2htb3ZlKT1cIiRldmVudC5wcmV2ZW50RGVmYXVsdCgpXCJcbiAgICAgICAgKGtleWRvd24uQXJyb3dSaWdodCk9XCJzaGlmdFJpZ2h0KGZhY2V0LCB0YWcpXCJcbiAgICAgICAgKGtleWRvd24uQXJyb3dMZWZ0KT1cInNoaWZ0TGVmdChmYWNldCwgdGFnKVwiPlxuICAgICAgICBAaWYgKGZhY2V0c1Jlb3JkZXJhYmxlKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImZhY2V0LWRyYWctaXRlbVwiIGNka0RyYWc+XG4gICAgICAgICAgICA8IS0tIERpc3BsYXkgTGFiZWwgLS0+XG4gICAgICAgICAgICA8c3BhbiBjbGFzcz1cImZhY2V0LXNlbGVjdGVkLXRhZy1sYWJlbFwiPnt7IGZhY2V0LnRpdGxlIH19PC9zcGFuPlxuICAgICAgICAgICAgPCEtLSBEaXNwbGF5IFJlbW92ZSBJY29uIC0tPlxuICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgdXhGb2N1c0luZGljYXRvclxuICAgICAgICAgICAgICBjbGFzcz1cImZhY2V0LXNlbGVjdGVkLXJlbW92ZS1idG5cIlxuICAgICAgICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cImRlc2VsZWN0RmFjZXRBcmlhTGFiZWxcIlxuICAgICAgICAgICAgICAoY2xpY2spPVwiZGVzZWxlY3RGYWNldChmYWNldCwgdGFnKVwiPlxuICAgICAgICAgICAgICA8dXgtaWNvbiBuYW1lPVwiY2xvc2VcIj48L3V4LWljb24+XG4gICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJmYWNldC1zZWxlY3RlZC10YWctbGFiZWxcIj57eyBmYWNldC50aXRsZSB9fTwvc3Bhbj5cbiAgICAgICAgICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgdXhGb2N1c0luZGljYXRvclxuICAgICAgICAgICAgY2xhc3M9XCJmYWNldC1zZWxlY3RlZC1yZW1vdmUtYnRuXCJcbiAgICAgICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiZGVzZWxlY3RGYWNldEFyaWFMYWJlbFwiXG4gICAgICAgICAgICAoY2xpY2spPVwiZGVzZWxlY3RGYWNldChmYWNldCwgdGFnKVwiPlxuICAgICAgICAgICAgPHV4LWljb24gbmFtZT1cImNsb3NlXCI+PC91eC1pY29uPlxuICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cbiAgICB9XG5cbiAgPC9kaXY+XG5cbiAgPCEtLSBTaG93IE1lc3NhZ2UgSGVyZSBpZiBObyBGYWNldHMgU2VsZWN0ZWQgLS0+XG4gIEBpZiAoZW1wdHlUZXh0ICYmIChmYWNldFNlcnZpY2UuZmFjZXRzJCB8IGFzeW5jKS5sZW5ndGggPT09IDApIHtcbiAgICA8cCBjbGFzcz1cImZhY2V0cy1zZWxlY3RlZC1ub25lLWxhYmVsXCI+e3sgZW1wdHlUZXh0IH19PC9wPlxuICB9XG5cbjwvZGl2PlxuXG48IS0tIEFueSBGYWNldCBFbGVtZW50cyBTaG91bGQgYmUgQWRkZWQgSGVyZSBCeSBVc2VyIC0tPlxuPGRpdiBjbGFzcz1cImZhY2V0cy1yZWdpb25cIj5cbiAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuPC9kaXY+XG5cbjxuZy10ZW1wbGF0ZSAjY2xlYXJCdXR0b25EZWZhdWx0PlxuICA8YnV0dG9uIHR5cGU9XCJidXR0b25cIlxuICAgIGNsYXNzPVwiYnRuIGJ0bi1saW5rIGJ0bi1pY29uIGJ1dHRvbi1zZWNvbmRhcnlcIlxuICAgIHRhYmluZGV4PVwiMFwiXG4gICAgW2F0dHIuYXJpYS1sYWJlbF09XCJjbGVhckFyaWFMYWJlbFwiXG4gICAgW3V4VG9vbHRpcF09XCJjbGVhclRvb2x0aXBcIlxuICAgIHBsYWNlbWVudD1cImxlZnRcIlxuICAgIChjbGljayk9XCJkZXNlbGVjdEFsbEZhY2V0cygpXCI+XG5cbiAgICA8c3ZnIGNsYXNzPVwiZmFjZXRzLXNlbGVjdGVkLWNsZWFyLWdyYXBoaWNcIiBmb2N1c2FibGU9XCJmYWxzZVwiIHZpZXdCb3g9XCIwIDAgMTkgMTJcIiBzaGFwZS1yZW5kZXJpbmc9XCJnZW9tZXRyaWNQcmVjaXNpb25cIj5cbiAgICAgIDxyZWN0IGNsYXNzPVwibGlnaHQtZ3JleVwiIHg9XCIwXCIgeT1cIjJcIiB3aWR0aD1cIjdcIiBoZWlnaHQ9XCIyXCI+PC9yZWN0PlxuICAgICAgPHJlY3QgY2xhc3M9XCJkYXJrLWdyZXlcIiB4PVwiMFwiIHk9XCI1XCIgd2lkdGg9XCI5XCIgaGVpZ2h0PVwiMlwiPjwvcmVjdD5cbiAgICAgIDxyZWN0IGNsYXNzPVwibGlnaHQtZ3JleVwiIHg9XCIwXCIgeT1cIjhcIiB3aWR0aD1cIjdcIiBoZWlnaHQ9XCIyXCI+PC9yZWN0PlxuICAgICAgPHBhdGggY2xhc3M9XCJkYXJrLWdyZXlcIiBkPVwiTTksMSBoMSBsOSw5IHYxIGgtMSBsLTksLTkgdi0xIFpcIj48L3BhdGg+XG4gICAgICA8cGF0aCBjbGFzcz1cImRhcmstZ3JleVwiIGQ9XCJNOSwxMSB2LTEgbDksLTkgaDEgdjEgbC05LDkgaC0xIFpcIj48L3BhdGg+XG4gICAgPC9zdmc+XG4gIDwvYnV0dG9uPlxuPC9uZy10ZW1wbGF0ZT5cbiJdfQ==