UNPKG

dfx-bootstrap-table

Version:

Angular table CDK implementation for Bootstrap with filtering, sorting and pagination.

172 lines 38.2 kB
/** * @license * Original work Copyright Google LLC All Rights Reserved. * Modified work Copyright DatePoll-Systems * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { AriaDescriber, FocusMonitor } from '@angular/cdk/a11y'; import { ENTER, SPACE } from '@angular/cdk/keycodes'; import { ANIMATION_MODULE_TYPE, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, ViewEncapsulation, booleanAttribute, inject, signal, } from '@angular/core'; import { merge } from 'rxjs'; import { NGB_SORT_DEFAULT_OPTIONS, NgbSort } from './sort'; import { getSortHeaderNotContainedWithinSortError } from './sort-errors'; import * as i0 from "@angular/core"; /** * Applies sorting behavior (click to change sort) and styles to an element, including an * arrow to display the current sort direction. * * Must be provided with an id and contained within a parent NgbSort directive. * * If used on header cells in a CdkTable, it will automatically default its id from its containing * column definition. */ export class NgbSortHeader { /** * Description applied to NgbSortHeader's button element with aria-describedby. This text should * describe the action that will occur when the user clicks the sort header. */ get sortActionDescription() { return this._sortActionDescription; } set sortActionDescription(value) { this._updateSortActionDescription(value); } constructor() { this._sort = inject(NgbSort, { optional: true }); this._columnDef = inject('NGB_SORT_HEADER_COLUMN_DEF', { optional: true, }); this._changeDetectorRef = inject(ChangeDetectorRef); this._focusMonitor = inject(FocusMonitor); this._elementRef = inject(ElementRef); this._ariaDescriber = inject(AriaDescriber, { optional: true }); this._animationModule = inject(ANIMATION_MODULE_TYPE, { optional: true }); /** * Indicates which state was just cleared from the sort header. * Will be reset on the next interaction. Used for coordinating animations. */ this._recentlyCleared = signal(null, ...(ngDevMode ? [{ debugName: "_recentlyCleared" }] : [])); /** Sets the position of the arrow that displays when sorted. */ this.arrowPosition = 'after'; /** whether the sort header is disabled. */ this.disabled = false; // Default the action description to "Sort" because it's better than nothing. // Without a description, the button's label comes from the sort header text content, // which doesn't give any indication that it performs a sorting operation. this._sortActionDescription = 'Sort'; const defaultOptions = inject(NGB_SORT_DEFAULT_OPTIONS, { optional: true, }); if (!this._sort && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw getSortHeaderNotContainedWithinSortError(); } if (defaultOptions?.arrowPosition) { this.arrowPosition = defaultOptions?.arrowPosition; } } ngOnInit() { if (!this.id && this._columnDef) { this.id = this._columnDef.name; } this._sort.register(this); this._renderChanges = merge(this._sort._stateChanges, this._sort.sortChange).subscribe(() => this._changeDetectorRef.markForCheck()); this._sortButton = this._elementRef.nativeElement.querySelector('.ngb-sort-header-container'); this._updateSortActionDescription(this._sortActionDescription); } ngAfterViewInit() { // We use the focus monitor because we also want to style // things differently based on the focus origin. this._focusMonitor.monitor(this._elementRef, true).subscribe(() => this._recentlyCleared.set(null)); } ngOnDestroy() { this._focusMonitor.stopMonitoring(this._elementRef); this._sort.deregister(this); this._renderChanges?.unsubscribe(); if (this._sortButton) { this._ariaDescriber?.removeDescription(this._sortButton, this._sortActionDescription); } } /** Triggers the sort on this sort header and removes the indicator hint. */ _toggleOnInteraction() { if (!this._isDisabled()) { const wasSorted = this._isSorted(); const prevDirection = this._sort.direction; this._sort.sort(this); this._recentlyCleared.set(wasSorted && !this._isSorted() ? prevDirection : null); } } _handleKeydown(event) { if (event.keyCode === SPACE || event.keyCode === ENTER) { event.preventDefault(); this._toggleOnInteraction(); } } /** Whether this NgbSortHeader is currently sorted in either ascending or descending order. */ _isSorted() { return this._sort.active == this.id && (this._sort.direction === 'asc' || this._sort.direction === 'desc'); } _isDisabled() { return this._sort.disabled || this.disabled; } /** * Gets the aria-sort attribute that should be applied to this sort header. If this header * is not sorted, returns null so that the attribute is removed from the host element. Aria spec * says that the aria-sort property should only be present on one header at a time, so removing * ensures this is true. */ _getAriaSortAttribute() { if (!this._isSorted()) { return 'none'; } return this._sort.direction == 'asc' ? 'ascending' : 'descending'; } /** Whether the arrow inside the sort header should be rendered. */ _renderArrow() { return !this._isDisabled() || this._isSorted(); } _updateSortActionDescription(newDescription) { // We use AriaDescriber for the sort button instead of setting an `aria-label` because some // screen readers (notably VoiceOver) will read both the column header *and* the button's label // for every *cell* in the table, creating a lot of unnecessary noise. // If _sortButton is undefined, the component hasn't been initialized yet so there's // nothing to update in the DOM. if (this._sortButton) { // removeDescription will no-op if there is no existing message. // TODO(jelbourn): remove optional chaining when AriaDescriber is required. this._ariaDescriber?.removeDescription(this._sortButton, this._sortActionDescription); this._ariaDescriber?.describe(this._sortButton, newDescription); } this._sortActionDescription = newDescription; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: NgbSortHeader, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: NgbSortHeader, isStandalone: true, selector: "[ngb-sort-header]", inputs: { id: ["ngb-sort-header", "id"], arrowPosition: "arrowPosition", start: "start", disabled: ["disabled", "disabled", booleanAttribute], sortActionDescription: "sortActionDescription", disableClear: ["disableClear", "disableClear", booleanAttribute] }, host: { listeners: { "click": "_toggleOnInteraction()", "keydown": "_handleKeydown($event)", "mouseleave": "_recentlyCleared.set(null)" }, properties: { "attr.aria-sort": "_getAriaSortAttribute()", "class.ngb-sort-header-disabled": "_isDisabled()" }, classAttribute: "ngb-sort-header" }, exportAs: ["ngbSortHeader"], ngImport: i0, template: "<!--\n We set the `tabindex` on an element inside the table header, rather than the header itself,\n because of a bug in NVDA where having a `tabindex` on a `th` breaks keyboard navigation in the\n table (see https://github.com/nvaccess/nvda/issues/7718). This allows for the header to both\n be focusable, and have screen readers read out its `aria-sort` state. We prefer this approach\n over having a button with an `aria-label` inside the header, because the button's `aria-label`\n will be read out as the user is navigating the table's cell (see #13012).\n\n The approach is based off of: https://dequeuniversity.com/library/aria/tables/sf-sortable-grid\n-->\n<div\n class=\"ngb-sort-header-container ngb-focus-indicator\"\n [class.ngb-sort-header-sorted]=\"_isSorted()\"\n [class.ngb-sort-header-position-before]=\"arrowPosition === 'before'\"\n [class.ngb-sort-header-descending]=\"this._sort.direction === 'desc'\"\n [class.ngb-sort-header-ascending]=\"this._sort.direction === 'asc'\"\n [class.ngb-sort-header-recently-cleared-ascending]=\"_recentlyCleared() === 'asc'\"\n [class.ngb-sort-header-recently-cleared-descending]=\"_recentlyCleared() === 'desc'\"\n [class.ngb-sort-header-animations-disabled]=\"_animationModule === 'NoopAnimations'\"\n [attr.tabindex]=\"_isDisabled() ? null : 0\"\n [attr.role]=\"_isDisabled() ? null : 'button'\">\n <!--\n TODO(crisbeto): this div isn't strictly necessary, but we have to keep it due to a large\n number of screenshot diff failures. It should be removed eventually. Note that the difference\n isn't visible with a shorter header, but once it breaks up into multiple lines, this element\n causes it to be center-aligned, whereas removing it will keep the text to the left.\n -->\n <div class=\"ngb-sort-header-content\">\n <ng-content></ng-content>\n </div>\n\n <!-- Disable animations while a current animation is running -->\n @if (_renderArrow()) {\n <div class=\"ngb-sort-header-arrow\">\n <svg viewBox=\"0 -960 960 960\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M440-240v-368L296-464l-56-56 240-240 240 240-56 56-144-144v368h-80Z\" />\n </svg>\n </div>\n }\n</div>\n", styles: [".ngb-sort-header-container{display:flex;cursor:pointer;align-items:center;letter-spacing:normal;outline:0}[ngb-sort-header].cdk-keyboard-focused .ngb-sort-header-container,[ngb-sort-header].cdk-program-focused .ngb-sort-header-container{border-bottom:solid 1px currentColor}.ngb-sort-header-disabled .ngb-sort-header-container{cursor:default}.ngb-sort-header-content{text-align:center;display:flex;align-items:center}.ngb-sort-header-position-before{flex-direction:row-reverse}@keyframes ngb-sort-header-recently-cleared-ascending{0%{transform:translateY(0);opacity:1}to{transform:translateY(-25%);opacity:0}}@keyframes ngb-sort-header-recently-cleared-descending{0%{transform:translateY(0) rotate(180deg);opacity:1}to{transform:translateY(25%) rotate(180deg);opacity:0}}.ngb-sort-header-arrow{height:12px;width:12px;position:relative;transition:transform 225ms cubic-bezier(.4,0,.2,1),opacity 225ms cubic-bezier(.4,0,.2,1);opacity:0;overflow:visible}.ngb-sort-header:hover .ngb-sort-header-arrow{opacity:.54}.ngb-sort-header .ngb-sort-header-sorted .ngb-sort-header-arrow{opacity:1}.ngb-sort-header-descending .ngb-sort-header-arrow{transform:rotate(180deg)}.ngb-sort-header-recently-cleared-ascending .ngb-sort-header-arrow{transform:translateY(-25%)}.ngb-sort-header-recently-cleared-ascending .ngb-sort-header-arrow{transition:none;animation:_ngb-sort-header-recently-cleared-ascending 225ms cubic-bezier(.4,0,.2,1) forwards}.ngb-sort-header-recently-cleared-descending .ngb-sort-header-arrow{transition:none;animation:_ngb-sort-header-recently-cleared-descending 225ms cubic-bezier(.4,0,.2,1) forwards}.ngb-sort-header-animations-disabled .ngb-sort-header-arrow{transition-duration:0ms;animation-duration:0ms}.ngb-sort-header-arrow svg{width:24px;height:24px;fill:currentColor;position:absolute;top:50%;left:50%;margin:-12px 0 0 -12px;transform:translateZ(0)}.ngb-sort-header-arrow,[dir=rtl] .ngb-sort-header-position-before .ngb-sort-header-arrow{margin:0 0 0 6px}.ngb-sort-header-position-before .ngb-sort-header-arrow,[dir=rtl] .ngb-sort-header-arrow{margin:0 6px 0 0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: NgbSortHeader, decorators: [{ type: Component, args: [{ selector: '[ngb-sort-header]', exportAs: 'ngbSortHeader', host: { class: 'ngb-sort-header', '(click)': '_toggleOnInteraction()', '(keydown)': '_handleKeydown($event)', '(mouseleave)': '_recentlyCleared.set(null)', '[attr.aria-sort]': '_getAriaSortAttribute()', '[class.ngb-sort-header-disabled]': '_isDisabled()', }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n We set the `tabindex` on an element inside the table header, rather than the header itself,\n because of a bug in NVDA where having a `tabindex` on a `th` breaks keyboard navigation in the\n table (see https://github.com/nvaccess/nvda/issues/7718). This allows for the header to both\n be focusable, and have screen readers read out its `aria-sort` state. We prefer this approach\n over having a button with an `aria-label` inside the header, because the button's `aria-label`\n will be read out as the user is navigating the table's cell (see #13012).\n\n The approach is based off of: https://dequeuniversity.com/library/aria/tables/sf-sortable-grid\n-->\n<div\n class=\"ngb-sort-header-container ngb-focus-indicator\"\n [class.ngb-sort-header-sorted]=\"_isSorted()\"\n [class.ngb-sort-header-position-before]=\"arrowPosition === 'before'\"\n [class.ngb-sort-header-descending]=\"this._sort.direction === 'desc'\"\n [class.ngb-sort-header-ascending]=\"this._sort.direction === 'asc'\"\n [class.ngb-sort-header-recently-cleared-ascending]=\"_recentlyCleared() === 'asc'\"\n [class.ngb-sort-header-recently-cleared-descending]=\"_recentlyCleared() === 'desc'\"\n [class.ngb-sort-header-animations-disabled]=\"_animationModule === 'NoopAnimations'\"\n [attr.tabindex]=\"_isDisabled() ? null : 0\"\n [attr.role]=\"_isDisabled() ? null : 'button'\">\n <!--\n TODO(crisbeto): this div isn't strictly necessary, but we have to keep it due to a large\n number of screenshot diff failures. It should be removed eventually. Note that the difference\n isn't visible with a shorter header, but once it breaks up into multiple lines, this element\n causes it to be center-aligned, whereas removing it will keep the text to the left.\n -->\n <div class=\"ngb-sort-header-content\">\n <ng-content></ng-content>\n </div>\n\n <!-- Disable animations while a current animation is running -->\n @if (_renderArrow()) {\n <div class=\"ngb-sort-header-arrow\">\n <svg viewBox=\"0 -960 960 960\" focusable=\"false\" aria-hidden=\"true\">\n <path d=\"M440-240v-368L296-464l-56-56 240-240 240 240-56 56-144-144v368h-80Z\" />\n </svg>\n </div>\n }\n</div>\n", styles: [".ngb-sort-header-container{display:flex;cursor:pointer;align-items:center;letter-spacing:normal;outline:0}[ngb-sort-header].cdk-keyboard-focused .ngb-sort-header-container,[ngb-sort-header].cdk-program-focused .ngb-sort-header-container{border-bottom:solid 1px currentColor}.ngb-sort-header-disabled .ngb-sort-header-container{cursor:default}.ngb-sort-header-content{text-align:center;display:flex;align-items:center}.ngb-sort-header-position-before{flex-direction:row-reverse}@keyframes ngb-sort-header-recently-cleared-ascending{0%{transform:translateY(0);opacity:1}to{transform:translateY(-25%);opacity:0}}@keyframes ngb-sort-header-recently-cleared-descending{0%{transform:translateY(0) rotate(180deg);opacity:1}to{transform:translateY(25%) rotate(180deg);opacity:0}}.ngb-sort-header-arrow{height:12px;width:12px;position:relative;transition:transform 225ms cubic-bezier(.4,0,.2,1),opacity 225ms cubic-bezier(.4,0,.2,1);opacity:0;overflow:visible}.ngb-sort-header:hover .ngb-sort-header-arrow{opacity:.54}.ngb-sort-header .ngb-sort-header-sorted .ngb-sort-header-arrow{opacity:1}.ngb-sort-header-descending .ngb-sort-header-arrow{transform:rotate(180deg)}.ngb-sort-header-recently-cleared-ascending .ngb-sort-header-arrow{transform:translateY(-25%)}.ngb-sort-header-recently-cleared-ascending .ngb-sort-header-arrow{transition:none;animation:_ngb-sort-header-recently-cleared-ascending 225ms cubic-bezier(.4,0,.2,1) forwards}.ngb-sort-header-recently-cleared-descending .ngb-sort-header-arrow{transition:none;animation:_ngb-sort-header-recently-cleared-descending 225ms cubic-bezier(.4,0,.2,1) forwards}.ngb-sort-header-animations-disabled .ngb-sort-header-arrow{transition-duration:0ms;animation-duration:0ms}.ngb-sort-header-arrow svg{width:24px;height:24px;fill:currentColor;position:absolute;top:50%;left:50%;margin:-12px 0 0 -12px;transform:translateZ(0)}.ngb-sort-header-arrow,[dir=rtl] .ngb-sort-header-position-before .ngb-sort-header-arrow{margin:0 0 0 6px}.ngb-sort-header-position-before .ngb-sort-header-arrow,[dir=rtl] .ngb-sort-header-arrow{margin:0 6px 0 0}\n"] }] }], ctorParameters: () => [], propDecorators: { id: [{ type: Input, args: ['ngb-sort-header'] }], arrowPosition: [{ type: Input }], start: [{ type: Input }], disabled: [{ type: Input, args: [{ transform: booleanAttribute }] }], sortActionDescription: [{ type: Input }], disableClear: [{ type: Input, args: [{ transform: booleanAttribute }] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic29ydC1oZWFkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9saWJzL2RmeC1ib290c3RyYXAtdGFibGUvc3JjL2xpYi9zb3J0L3NvcnQtaGVhZGVyLnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy9kZngtYm9vdHN0cmFwLXRhYmxlL3NyYy9saWIvc29ydC9zb3J0LWhlYWRlci5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O0dBT0c7QUFDSCxPQUFPLEVBQUUsYUFBYSxFQUFFLFlBQVksRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDckQsT0FBTyxFQUNMLHFCQUFxQixFQUVyQix1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxVQUFVLEVBQ1YsS0FBSyxFQUdMLGlCQUFpQixFQUNqQixnQkFBZ0IsRUFDaEIsTUFBTSxFQUNOLE1BQU0sR0FDUCxNQUFNLGVBQWUsQ0FBQztBQUV2QixPQUFPLEVBQWdCLEtBQUssRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUUzQyxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsT0FBTyxFQUErRCxNQUFNLFFBQVEsQ0FBQztBQUV4SCxPQUFPLEVBQUUsd0NBQXdDLEVBQUUsTUFBTSxlQUFlLENBQUM7O0FBT3pFOzs7Ozs7OztHQVFHO0FBaUJILE1BQU0sT0FBTyxhQUFhO0lBd0N4Qjs7O09BR0c7SUFDSCxJQUNJLHFCQUFxQjtRQUN2QixPQUFPLElBQUksQ0FBQyxzQkFBc0IsQ0FBQztJQUNyQyxDQUFDO0lBQ0QsSUFBSSxxQkFBcUIsQ0FBQyxLQUFhO1FBQ3JDLElBQUksQ0FBQyw0QkFBNEIsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBYUQ7UUE5REEsVUFBSyxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxF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O3dCQUM3QyxrQ0FBa0MsRUFBRSxlQUFlO3FCQUNwRCxpQkFDYyxpQkFBaUIsQ0FBQyxJQUFJLG1CQUNwQix1QkFBdUIsQ0FBQyxNQUFNO3dEQThCckIsRUFBRTtzQkFBM0IsS0FBSzt1QkFBQyxpQkFBaUI7Z0JBR2YsYUFBYTtzQkFBckIsS0FBSztnQkFHRyxLQUFLO3NCQUFiLEtBQUs7Z0JBSU4sUUFBUTtzQkFEUCxLQUFLO3VCQUFDLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFO2dCQVFsQyxxQkFBcUI7c0JBRHhCLEtBQUs7Z0JBY04sWUFBWTtzQkFEWCxLQUFLO3VCQUFDLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogT3JpZ2luYWwgd29yayBDb3B5cmlnaHQgR29vZ2xlIExMQyBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICogTW9kaWZpZWQgd29yayBDb3B5cmlnaHQgRGF0ZVBvbGwtU3lzdGVtc1xuICpcbiAqIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGFuIE1JVC1zdHlsZSBsaWNlbnNlIHRoYXQgY2FuIGJlXG4gKiBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGF0IGh0dHBzOi8vYW5ndWxhci5pby9saWNlbnNlXG4gKi9cbmltcG9ydCB7IEFyaWFEZXNjcmliZXIsIEZvY3VzTW9uaXRvciB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9hMTF5JztcbmltcG9ydCB7IEVOVEVSLCBTUEFDRSB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9rZXljb2Rlcyc7XG5pbXBvcnQge1xuICBBTklNQVRJT05fTU9EVUxFX1RZUEUsXG4gIEFmdGVyVmlld0luaXQsXG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICBDaGFuZ2VEZXRlY3RvclJlZixcbiAgQ29tcG9uZW50LFxuICBFbGVtZW50UmVmLFxuICBJbnB1dCxcbiAgT25EZXN0cm95LFxuICBPbkluaXQsXG4gIFZpZXdFbmNhcHN1bGF0aW9uLFxuICBib29sZWFuQXR0cmlidXRlLFxuICBpbmplY3QsXG4gIHNpZ25hbCxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7IFN1YnNjcmlwdGlvbiwgbWVyZ2UgfSBmcm9tICdyeGpzJztcblxuaW1wb3J0IHsgTkdCX1NPUlRfREVGQVVMVF9PUFRJT05TLCBOZ2JTb3J0LCBOZ2JTb3J0RGVmYXVsdE9wdGlvbnMsIE5nYlNvcnRhYmxlLCBTb3J0SGVhZGVyQXJyb3dQb3NpdGlvbiB9IGZyb20gJy4vc29ydCc7XG5pbXBvcnQgeyBTb3J0RGlyZWN0aW9uIH0gZnJvbSAnLi9zb3J0LWRpcmVjdGlvbic7XG5pbXBvcnQgeyBnZXRTb3J0SGVhZGVyTm90Q29udGFpbmVkV2l0aGluU29ydEVycm9yIH0gZnJvbSAnLi9zb3J0LWVycm9ycyc7XG5cbi8qKiBDb2x1bW4gZGVmaW5pdGlvbiBhc3NvY2lhdGVkIHdpdGggYSBgTmdiU29ydEhlYWRlcmAuICovXG5pbnRlcmZhY2UgTmdiU29ydEhlYWRlckNvbHVtbkRlZiB7XG4gIG5hbWU6IHN0cmluZztcbn1cblxuLyoqXG4gKiBBcHBsaWVzIHNvcnRpbmcgYmVoYXZpb3IgKGNsaWNrIHRvIGNoYW5nZSBzb3J0KSBhbmQgc3R5bGVzIHRvIGFuIGVsZW1lbnQsIGluY2x1ZGluZyBhblxuICogYXJyb3cgdG8gZGlzcGxheSB0aGUgY3VycmVudCBzb3J0IGRpcmVjdGlvbi5cbiAqXG4gKiBNdXN0IGJlIHByb3ZpZGVkIHdpdGggYW4gaWQgYW5kIGNvbnRhaW5lZCB3aXRoaW4gYSBwYXJlbnQgTmdiU29ydCBkaXJlY3RpdmUuXG4gKlxuICogSWYgdXNlZCBvbiBoZWFkZXIgY2VsbHMgaW4gYSBDZGtUYWJsZSwgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IGRlZmF1bHQgaXRzIGlkIGZyb20gaXRzIGNvbnRhaW5pbmdcbiAqIGNvbHVtbiBkZWZpbml0aW9uLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdbbmdiLXNvcnQtaGVhZGVyXScsXG4gIGV4cG9ydEFzOiAnbmdiU29ydEhlYWRlcicsXG4gIHRlbXBsYXRlVXJsOiAnc29ydC1oZWFkZXIuaHRtbCcsXG4gIHN0eWxlVXJsczogWydzb3J0LWhlYWRlci5zY3NzJ10sXG4gIGhvc3Q6IHtcbiAgICBjbGFzczogJ25nYi1zb3J0LWhlYWRlcicsXG4gICAgJyhjbGljayknOiAnX3RvZ2dsZU9uSW50ZXJhY3Rpb24oKScsXG4gICAgJyhrZXlkb3duKSc6ICdfaGFuZGxlS2V5ZG93bigkZXZlbnQpJyxcbiAgICAnKG1vdXNlbGVhdmUpJzogJ19yZWNlbnRseUNsZWFyZWQuc2V0KG51bGwpJyxcbiAgICAnW2F0dHIuYXJpYS1zb3J0XSc6ICdfZ2V0QXJpYVNvcnRBdHRyaWJ1dGUoKScsXG4gICAgJ1tjbGFzcy5uZ2Itc29ydC1oZWFkZXItZGlzYWJsZWRdJzogJ19pc0Rpc2FibGVkKCknLFxuICB9LFxuICBlbmNhcHN1bGF0aW9uOiBWaWV3RW5jYXBzdWxhdGlvbi5Ob25lLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgTmdiU29ydEhlYWRlciBpbXBsZW1lbnRzIE5nYlNvcnRhYmxlLCBPbkRlc3Ryb3ksIE9uSW5pdCwgQWZ0ZXJWaWV3SW5pdCB7XG4gIF9zb3J0ID0gaW5qZWN0KE5nYlNvcnQsIHsgb3B0aW9uYWw6IHRydWUgfSkhO1xuICBfY29sdW1uRGVmID0gaW5qZWN0PE5nYlNvcnRIZWFkZXJDb2x1bW5EZWY+KCdOR0JfU09SVF9IRUFERVJfQ09MVU1OX0RFRicgYXMgYW55LCB7XG4gICAgb3B0aW9uYWw6IHRydWUsXG4gIH0pO1xuICBwcml2YXRlIF9jaGFuZ2VEZXRlY3RvclJlZiA9IGluamVjdChDaGFuZ2VEZXRlY3RvclJlZik7XG4gIHByaXZhdGUgX2ZvY3VzTW9uaXRvciA9IGluamVjdChGb2N1c01vbml0b3IpO1xuICBwcml2YXRlIF9lbGVtZW50UmVmID0gaW5qZWN0PEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+PihFbGVtZW50UmVmKTtcbiAgcHJpdmF0ZSBfYXJpYURlc2NyaWJlciA9IGluamVjdChBcmlhRGVzY3JpYmVyLCB7IG9wdGlvbmFsOiB0cnVlIH0pO1xuICBwcml2YXRlIF9yZW5kZXJDaGFuZ2VzOiBTdWJzY3JpcHRpb24gfCB1bmRlZmluZWQ7XG4gIHByb3RlY3RlZCBfYW5pbWF0aW9uTW9kdWxlID0gaW5qZWN0KEFOSU1BVElPTl9NT0RVTEVfVFlQRSwgeyBvcHRpb25hbDogdHJ1ZSB9KTtcblxuICAvKipcbiAgICogSW5kaWNhdGVzIHdoaWNoIHN0YXRlIHdhcyBqdXN0IGNsZWFyZWQgZnJvbSB0aGUgc29ydCBoZWFkZXIuXG4gICAqIFdpbGwgYmUgcmVzZXQgb24gdGhlIG5leHQgaW50ZXJhY3Rpb24uIFVzZWQgZm9yIGNvb3JkaW5hdGluZyBhbmltYXRpb25zLlxuICAgKi9cbiAgcHJvdGVjdGVkIF9yZWNlbnRseUNsZWFyZWQgPSBzaWduYWw8U29ydERpcmVjdGlvbiB8IG51bGw+KG51bGwpO1xuXG4gIC8qKlxuICAgKiBUaGUgZWxlbWVudCB3aXRoIHJvbGU9XCJidXR0b25cIiBpbnNpZGUgdGhpcyBjb21wb25lbnQncyB2aWV3LiBXZSBuZWVkIHRoaXNcbiAgICogaW4gb3JkZXIgdG8gYXBwbHkgYSBkZXNjcmlwdGlvbiB3aXRoIEFyaWFEZXNjcmliZXIuXG4gICAqL1xuICBwcml2YXRlIF9zb3J0QnV0dG9uITogSFRNTEVsZW1lbnQ7XG5cbiAgLyoqXG4gICAqIElEIG9mIHRoaXMgc29ydCBoZWFkZXIuIElmIHVzZWQgd2l0aGluIHRoZSBjb250ZXh0IG9mIGEgQ2RrQ29sdW1uRGVmLCB0aGlzIHdpbGwgZGVmYXVsdCB0b1xuICAgKiB0aGUgY29sdW1uJ3MgbmFtZS5cbiAgICovXG4gIEBJbnB1dCgnbmdiLXNvcnQtaGVhZGVyJykgaWQhOiBzdHJpbmc7XG5cbiAgLyoqIFNldHMgdGhlIHBvc2l0aW9uIG9mIHRoZSBhcnJvdyB0aGF0IGRpc3BsYXlzIHdoZW4gc29ydGVkLiAqL1xuICBASW5wdXQoKSBhcnJvd1Bvc2l0aW9uOiBTb3J0SGVhZGVyQXJyb3dQb3NpdGlvbiA9ICdhZnRlcic7XG5cbiAgLyoqIE92ZXJyaWRlcyB0aGUgc29ydCBzdGFydCB2YWx1ZSBvZiB0aGUgY29udGFpbmluZyBOZ2JTb3J0IGZvciB0aGlzIE5nYlNvcnRhYmxlLiAqL1xuICBASW5wdXQoKSBzdGFydCE6IFNvcnREaXJlY3Rpb247XG5cbiAgLyoqIHdoZXRoZXIgdGhlIHNvcnQgaGVhZGVyIGlzIGRpc2FibGVkLiAqL1xuICBASW5wdXQoeyB0cmFuc2Zvcm06IGJvb2xlYW5BdHRyaWJ1dGUgfSlcbiAgZGlzYWJsZWQgPSBmYWxzZTtcblxuICAvKipcbiAgICogRGVzY3JpcHRpb24gYXBwbGllZCB0byBOZ2JTb3J0SGVhZGVyJ3MgYnV0dG9uIGVsZW1lbnQgd2l0aCBhcmlhLWRlc2NyaWJlZGJ5LiBUaGlzIHRleHQgc2hvdWxkXG4gICAqIGRlc2NyaWJlIHRoZSBhY3Rpb24gdGhhdCB3aWxsIG9jY3VyIHdoZW4gdGhlIHVzZXIgY2xpY2tzIHRoZSBzb3J0IGhlYWRlci5cbiAgICovXG4gIEBJbnB1dCgpXG4gIGdldCBzb3J0QWN0aW9uRGVzY3JpcHRpb24oKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5fc29ydEFjdGlvbkRlc2NyaXB0aW9uO1xuICB9XG4gIHNldCBzb3J0QWN0aW9uRGVzY3JpcHRpb24odmFsdWU6IHN0cmluZykge1xuICAgIHRoaXMuX3VwZGF0ZVNvcnRBY3Rpb25EZXNjcmlwdGlvbih2YWx1ZSk7XG4gIH1cbiAgLy8gRGVmYXVsdCB0aGUgYWN0aW9uIGRlc2NyaXB0aW9uIHRvIFwiU29ydFwiIGJlY2F1c2UgaXQncyBiZXR0ZXIgdGhhbiBub3RoaW5nLlxuICAvLyBXaXRob3V0IGEgZGVzY3JpcHRpb24sIHRoZSBidXR0b24ncyBsYWJlbCBjb21lcyBmcm9tIHRoZSBzb3J0IGhlYWRlciB0ZXh0IGNvbnRlbnQsXG4gIC8vIHdoaWNoIGRvZXNuJ3QgZ2l2ZSBhbnkgaW5kaWNhdGlvbiB0aGF0IGl0IHBlcmZvcm1zIGEgc29ydGluZyBvcGVyYXRpb24uXG4gIHByaXZhdGUgX3NvcnRBY3Rpb25EZXNjcmlwdGlvbiA9ICdTb3J0JztcblxuICAvKiogT3ZlcnJpZGVzIHRoZSBkaXNhYmxlIGNsZWFyIHZhbHVlIG9mIHRoZSBjb250YWluaW5nIE5nYlNvcnQgZm9yIHRoaXMgTmdiU29ydGFibGUuICovXG4gIEBJbnB1dCh7IHRyYW5zZm9ybTogYm9vbGVhbkF0dHJpYnV0ZSB9KVxuICBkaXNhYmxlQ2xlYXIhOiBib29sZWFuO1xuXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAYW5ndWxhci1lc2xpbnQvcHJlZmVyLWluamVjdFxuICBjb25zdHJ1Y3RvciguLi5hcmdzOiB1bmtub3duW10pO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIGNvbnN0IGRlZmF1bHRPcHRpb25zID0gaW5qZWN0PE5nYlNvcnREZWZhdWx0T3B0aW9ucz4oTkdCX1NPUlRfREVGQVVMVF9PUFRJT05TLCB7XG4gICAgICBvcHRpb25hbDogdHJ1ZSxcbiAgICB9KTtcblxuICAgIGlmICghdGhpcy5fc29ydCAmJiAodHlwZW9mIG5nRGV2TW9kZSA9PT0gJ3VuZGVmaW5lZCcgfHwgbmdEZXZNb2RlKSkge1xuICAgICAgdGhyb3cgZ2V0U29ydEhlYWRlck5vdENvbnRhaW5lZFdpdGhpblNvcnRFcnJvcigpO1xuICAgIH1cblxuICAgIGlmIChkZWZhdWx0T3B0aW9ucz8uYXJyb3dQb3NpdGlvbikge1xuICAgICAgdGhpcy5hcnJvd1Bvc2l0aW9uID0gZGVmYXVsdE9wdGlvbnM/LmFycm93UG9zaXRpb247XG4gICAgfVxuICB9XG5cbiAgbmdPbkluaXQoKSB7XG4gICAgaWYgKCF0aGlzLmlkICYmIHRoaXMuX2NvbHVtbkRlZikge1xuICAgICAgdGhpcy5pZCA9IHRoaXMuX2NvbHVtbkRlZi5uYW1lO1xuICAgIH1cblxuICAgIHRoaXMuX3NvcnQucmVnaXN0ZXIodGhpcyk7XG4gICAgdGhpcy5fcmVuZGVyQ2hhbmdlcyA9IG1lcmdlKHRoaXMuX3NvcnQuX3N0YXRlQ2hhbmdlcywgdGhpcy5fc29ydC5zb3J0Q2hhbmdlKS5zdWJzY3JpYmUoKCkgPT4gdGhpcy5fY2hhbmdlRGV0ZWN0b3JSZWYubWFya0ZvckNoZWNrKCkpO1xuICAgIHRoaXMuX3NvcnRCdXR0b24gPSB0aGlzLl9lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvcignLm5nYi1zb3J0LWhlYWRlci1jb250YWluZXInKSE7XG4gICAgdGhpcy5fdXBkYXRlU29ydEFjdGlvbkRlc2NyaXB0aW9uKHRoaXMuX3NvcnRBY3Rpb25EZXNjcmlwdGlvbik7XG4gIH1cblxuICBuZ0FmdGVyVmlld0luaXQoKSB7XG4gICAgLy8gV2UgdXNlIHRoZSBmb2N1cyBtb25pdG9yIGJlY2F1c2Ugd2UgYWxzbyB3YW50IHRvIHN0eWxlXG4gICAgLy8gdGhpbmdzIGRpZmZlcmVudGx5IGJhc2VkIG9uIHRoZSBmb2N1cyBvcmlnaW4uXG4gICAgdGhpcy5fZm9jdXNNb25pdG9yLm1vbml0b3IodGhpcy5fZWxlbWVudFJlZiwgdHJ1ZSkuc3Vic2NyaWJlKCgpID0+IHRoaXMuX3JlY2VudGx5Q2xlYXJlZC5zZXQobnVsbCkpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgdGhpcy5fZm9jdXNNb25pdG9yLnN0b3BNb25pdG9yaW5nKHRoaXMuX2VsZW1lbnRSZWYpO1xuICAgIHRoaXMuX3NvcnQuZGVyZWdpc3Rlcih0aGlzKTtcbiAgICB0aGlzLl9yZW5kZXJDaGFuZ2VzPy51bnN1YnNjcmliZSgpO1xuXG4gICAgaWYgKHRoaXMuX3NvcnRCdXR0b24pIHtcbiAgICAgIHRoaXMuX2FyaWFEZXNjcmliZXI/LnJlbW92ZURlc2NyaXB0aW9uKHRoaXMuX3NvcnRCdXR0b24sIHRoaXMuX3NvcnRBY3Rpb25EZXNjcmlwdGlvbik7XG4gICAgfVxuICB9XG5cbiAgLyoqIFRyaWdnZXJzIHRoZSBzb3J0IG9uIHRoaXMgc29ydCBoZWFkZXIgYW5kIHJlbW92ZXMgdGhlIGluZGljYXRvciBoaW50LiAqL1xuICBfdG9nZ2xlT25JbnRlcmFjdGlvbigpIHtcbiAgICBpZiAoIXRoaXMuX2lzRGlzYWJsZWQoKSkge1xuICAgICAgY29uc3Qgd2FzU29ydGVkID0gdGhpcy5faXNTb3J0ZWQoKTtcbiAgICAgIGNvbnN0IHByZXZEaXJlY3Rpb24gPSB0aGlzLl9zb3J0LmRpcmVjdGlvbjtcbiAgICAgIHRoaXMuX3NvcnQuc29ydCh0aGlzKTtcbiAgICAgIHRoaXMuX3JlY2VudGx5Q2xlYXJlZC5zZXQod2FzU29ydGVkICYmICF0aGlzLl9pc1NvcnRlZCgpID8gcHJldkRpcmVjdGlvbiA6IG51bGwpO1xuICAgIH1cbiAgfVxuXG4gIF9oYW5kbGVLZXlkb3duKGV2ZW50OiBLZXlib2FyZEV2ZW50KSB7XG4gICAgaWYgKGV2ZW50LmtleUNvZGUgPT09IFNQQUNFIHx8IGV2ZW50LmtleUNvZGUgPT09IEVOVEVSKSB7XG4gICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgdGhpcy5fdG9nZ2xlT25JbnRlcmFjdGlvbigpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBXaGV0aGVyIHRoaXMgTmdiU29ydEhlYWRlciBpcyBjdXJyZW50bHkgc29ydGVkIGluIGVpdGhlciBhc2NlbmRpbmcgb3IgZGVzY2VuZGluZyBvcmRlci4gKi9cbiAgX2lzU29ydGVkKCkge1xuICAgIHJldHVybiB0aGlzLl9zb3J0LmFjdGl2ZSA9PSB0aGlzLmlkICYmICh0aGlzLl9zb3J0LmRpcmVjdGlvbiA9PT0gJ2FzYycgfHwgdGhpcy5fc29ydC5kaXJlY3Rpb24gPT09ICdkZXNjJyk7XG4gIH1cblxuICBfaXNEaXNhYmxlZCgpIHtcbiAgICByZXR1cm4gdGhpcy5fc29ydC5kaXNhYmxlZCB8fCB0aGlzLmRpc2FibGVkO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgdGhlIGFyaWEtc29ydCBhdHRyaWJ1dGUgdGhhdCBzaG91bGQgYmUgYXBwbGllZCB0byB0aGlzIHNvcnQgaGVhZGVyLiBJZiB0aGlzIGhlYWRlclxuICAgKiBpcyBub3Qgc29ydGVkLCByZXR1cm5zIG51bGwgc28gdGhhdCB0aGUgYXR0cmlidXRlIGlzIHJlbW92ZWQgZnJvbSB0aGUgaG9zdCBlbGVtZW50LiBBcmlhIHNwZWNcbiAgICogc2F5cyB0aGF0IHRoZSBhcmlhLXNvcnQgcHJvcGVydHkgc2hvdWxkIG9ubHkgYmUgcHJlc2VudCBvbiBvbmUgaGVhZGVyIGF0IGEgdGltZSwgc28gcmVtb3ZpbmdcbiAgICogZW5zdXJlcyB0aGlzIGlzIHRydWUuXG4gICAqL1xuICBfZ2V0QXJpYVNvcnRBdHRyaWJ1dGUoKSB7XG4gICAgaWYgKCF0aGlzLl9pc1NvcnRlZCgpKSB7XG4gICAgICByZXR1cm4gJ25vbmUnO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLl9zb3J0LmRpcmVjdGlvbiA9PSAnYXNjJyA/ICdhc2NlbmRpbmcnIDogJ2Rlc2NlbmRpbmcnO1xuICB9XG5cbiAgLyoqIFdoZXRoZXIgdGhlIGFycm93IGluc2lkZSB0aGUgc29ydCBoZWFkZXIgc2hvdWxkIGJlIHJlbmRlcmVkLiAqL1xuICBfcmVuZGVyQXJyb3coKSB7XG4gICAgcmV0dXJuICF0aGlzLl9pc0Rpc2FibGVkKCkgfHwgdGhpcy5faXNTb3J0ZWQoKTtcbiAgfVxuXG4gIHByaXZhdGUgX3VwZGF0ZVNvcnRBY3Rpb25EZXNjcmlwdGlvbihuZXdEZXNjcmlwdGlvbjogc3RyaW5nKSB7XG4gICAgLy8gV2UgdXNlIEFyaWFEZXNjcmliZXIgZm9yIHRoZSBzb3J0IGJ1dHRvbiBpbnN0ZWFkIG9mIHNldHRpbmcgYW4gYGFyaWEtbGFiZWxgIGJlY2F1c2Ugc29tZVxuICAgIC8vIHNjcmVlbiByZWFkZXJzIChub3RhYmx5IFZvaWNlT3Zlcikgd2lsbCByZWFkIGJvdGggdGhlIGNvbHVtbiBoZWFkZXIgKmFuZCogdGhlIGJ1dHRvbidzIGxhYmVsXG4gICAgLy8gZm9yIGV2ZXJ5ICpjZWxsKiBpbiB0aGUgdGFibGUsIGNyZWF0aW5nIGEgbG90IG9mIHVubmVjZXNzYXJ5IG5vaXNlLlxuXG4gICAgLy8gSWYgX3NvcnRCdXR0b24gaXMgdW5kZWZpbmVkLCB0aGUgY29tcG9uZW50IGhhc24ndCBiZWVuIGluaXRpYWxpemVkIHlldCBzbyB0aGVyZSdzXG4gICAgLy8gbm90aGluZyB0byB1cGRhdGUgaW4gdGhlIERPTS5cbiAgICBpZiAodGhpcy5fc29ydEJ1dHRvbikge1xuICAgICAgLy8gcmVtb3ZlRGVzY3JpcHRpb24gd2lsbCBuby1vcCBpZiB0aGVyZSBpcyBubyBleGlzdGluZyBtZXNzYWdlLlxuICAgICAgLy8gVE9ETyhqZWxib3Vybik6IHJlbW92ZSBvcHRpb25hbCBjaGFpbmluZyB3aGVuIEFyaWFEZXNjcmliZXIgaXMgcmVxdWlyZWQuXG4gICAgICB0aGlzLl9hcmlhRGVzY3JpYmVyPy5yZW1vdmVEZXNjcmlwdGlvbih0aGlzLl9zb3J0QnV0dG9uLCB0aGlzLl9zb3J0QWN0aW9uRGVzY3JpcHRpb24pO1xuICAgICAgdGhpcy5fYXJpYURlc2NyaWJlcj8uZGVzY3JpYmUodGhpcy5fc29ydEJ1dHRvbiwgbmV3RGVzY3JpcHRpb24pO1xuICAgIH1cblxuICAgIHRoaXMuX3NvcnRBY3Rpb25EZXNjcmlwdGlvbiA9IG5ld0Rlc2NyaXB0aW9uO1xuICB9XG59XG4iLCI8IS0tXG4gIFdlIHNldCB0aGUgYHRhYmluZGV4YCBvbiBhbiBlbGVtZW50IGluc2lkZSB0aGUgdGFibGUgaGVhZGVyLCByYXRoZXIgdGhhbiB0aGUgaGVhZGVyIGl0c2VsZixcbiAgYmVjYXVzZSBvZiBhIGJ1ZyBpbiBOVkRBIHdoZXJlIGhhdmluZyBhIGB0YWJpbmRleGAgb24gYSBgdGhgIGJyZWFrcyBrZXlib2FyZCBuYXZpZ2F0aW9uIGluIHRoZVxuICB0YWJsZSAoc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9udmFjY2Vzcy9udmRhL2lzc3Vlcy83NzE4KS4gVGhpcyBhbGxvd3MgZm9yIHRoZSBoZWFkZXIgdG8gYm90aFxuICBiZSBmb2N1c2FibGUsIGFuZCBoYXZlIHNjcmVlbiByZWFkZXJzIHJlYWQgb3V0IGl0cyBgYXJpYS1zb3J0YCBzdGF0ZS4gV2UgcHJlZmVyIHRoaXMgYXBwcm9hY2hcbiAgb3ZlciBoYXZpbmcgYSBidXR0b24gd2l0aCBhbiBgYXJpYS1sYWJlbGAgaW5zaWRlIHRoZSBoZWFkZXIsIGJlY2F1c2UgdGhlIGJ1dHRvbidzIGBhcmlhLWxhYmVsYFxuICB3aWxsIGJlIHJlYWQgb3V0IGFzIHRoZSB1c2VyIGlzIG5hdmlnYXRpbmcgdGhlIHRhYmxlJ3MgY2VsbCAoc2VlICMxMzAxMikuXG5cbiAgVGhlIGFwcHJvYWNoIGlzIGJhc2VkIG9mZiBvZjogaHR0cHM6Ly9kZXF1ZXVuaXZlcnNpdHkuY29tL2xpYnJhcnkvYXJpYS90YWJsZXMvc2Ytc29ydGFibGUtZ3JpZFxuLS0+XG48ZGl2XG4gIGNsYXNzPVwibmdiLXNvcnQtaGVhZGVyLWNvbnRhaW5lciBuZ2ItZm9jdXMtaW5kaWNhdG9yXCJcbiAgW2NsYXNzLm5nYi1zb3J0LWhlYWRlci1zb3J0ZWRdPVwiX2lzU29ydGVkKClcIlxuICBbY2xhc3MubmdiLXNvcnQtaGVhZGVyLXBvc2l0aW9uLWJlZm9yZV09XCJhcnJvd1Bvc2l0aW9uID09PSAnYmVmb3JlJ1wiXG4gIFtjbGFzcy5uZ2Itc29ydC1oZWFkZXItZGVzY2VuZGluZ109XCJ0aGlzLl9zb3J0LmRpcmVjdGlvbiA9PT0gJ2Rlc2MnXCJcbiAgW2NsYXNzLm5nYi1zb3J0LWhlYWRlci1hc2NlbmRpbmddPVwidGhpcy5fc29ydC5kaXJlY3Rpb24gPT09ICdhc2MnXCJcbiAgW2NsYXNzLm5nYi1zb3J0LWhlYWRlci1yZWNlbnRseS1jbGVhcmVkLWFzY2VuZGluZ109XCJfcmVjZW50bHlDbGVhcmVkKCkgPT09ICdhc2MnXCJcbiAgW2NsYXNzLm5nYi1zb3J0LWhlYWRlci1yZWNlbnRseS1jbGVhcmVkLWRlc2NlbmRpbmddPVwiX3JlY2VudGx5Q2xlYXJlZCgpID09PSAnZGVzYydcIlxuICBbY2xhc3MubmdiLXNvcnQtaGVhZGVyLWFuaW1hdGlvbnMtZGlzYWJsZWRdPVwiX2FuaW1hdGlvbk1vZHVsZSA9PT0gJ05vb3BBbmltYXRpb25zJ1wiXG4gIFthdHRyLnRhYmluZGV4XT1cIl9pc0Rpc2FibGVkKCkgPyBudWxsIDogMFwiXG4gIFthdHRyLnJvbGVdPVwiX2lzRGlzYWJsZWQoKSA/IG51bGwgOiAnYnV0dG9uJ1wiPlxuICA8IS0tXG4gICAgVE9ETyhjcmlzYmV0byk6IHRoaXMgZGl2IGlzbid0IHN0cmljdGx5IG5lY2Vzc2FyeSwgYnV0IHdlIGhhdmUgdG8ga2VlcCBpdCBkdWUgdG8gYSBsYXJnZVxuICAgIG51bWJlciBvZiBzY3JlZW5zaG90IGRpZmYgZmFpbHVyZXMuIEl0IHNob3VsZCBiZSByZW1vdmVkIGV2ZW50dWFsbHkuIE5vdGUgdGhhdCB0aGUgZGlmZmVyZW5jZVxuICAgIGlzbid0IHZpc2libGUgd2l0aCBhIHNob3J0ZXIgaGVhZGVyLCBidXQgb25jZSBpdCBicmVha3MgdXAgaW50byBtdWx0aXBsZSBsaW5lcywgdGhpcyBlbGVtZW50XG4gICAgY2F1c2VzIGl0IHRvIGJlIGNlbnRlci1hbGlnbmVkLCB3aGVyZWFzIHJlbW92aW5nIGl0IHdpbGwga2VlcCB0aGUgdGV4dCB0byB0aGUgbGVmdC5cbiAgLS0+XG4gIDxkaXYgY2xhc3M9XCJuZ2Itc29ydC1oZWFkZXItY29udGVudFwiPlxuICAgIDxuZy1jb250ZW50PjwvbmctY29udGVudD5cbiAgPC9kaXY+XG5cbiAgPCEtLSBEaXNhYmxlIGFuaW1hdGlvbnMgd2hpbGUgYSBjdXJyZW50IGFuaW1hdGlvbiBpcyBydW5uaW5nIC0tPlxuICBAaWYgKF9yZW5kZXJBcnJvdygpKSB7XG4gIDxkaXYgY2xhc3M9XCJuZ2Itc29ydC1oZWFkZXItYXJyb3dcIj5cbiAgICA8c3ZnIHZpZXdCb3g9XCIwIC05NjAgOTYwIDk2MFwiIGZvY3VzYWJsZT1cImZhbHNlXCIgYXJpYS1oaWRkZW49XCJ0cnVlXCI+XG4gICAgICA8cGF0aCBkPVwiTTQ0MC0yNDB2LTM2OEwyOTYtNDY0bC01Ni01NiAyNDAtMjQwIDI0MCAyNDAtNTYgNTYtMTQ0LTE0NHYzNjhoLTgwWlwiIC8+XG4gICAgPC9zdmc+XG4gIDwvZGl2PlxuICB9XG48L2Rpdj5cbiJdfQ==