UNPKG

dfx-bootstrap-table

Version:

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

172 lines 38.1 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); /** 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.0.2", ngImport: i0, type: NgbSortHeader, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.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.0.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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d0JBQzdDLGtDQUFrQyxFQUFFLGVBQWU7cUJBQ3BELGlCQUNjLGlCQUFpQixDQUFDLElBQUksbUJBQ3BCLHVCQUF1QixDQUFDLE1BQU07d0RBOEJyQixFQUFFO3NCQUEzQixLQUFLO3VCQUFDLGlCQUFpQjtnQkFHZixhQUFhO3NCQUFyQixLQUFLO2dCQUdHLEtBQUs7c0JBQWIsS0FBSztnQkFJTixRQUFRO3NCQURQLEtBQUs7dUJBQUMsRUFBRSxTQUFTLEVBQUUsZ0JBQWdCLEVBQUU7Z0JBUWxDLHFCQUFxQjtzQkFEeEIsS0FBSztnQkFjTixZQUFZO3NCQURYLEtBQUs7dUJBQUMsRUFBRSxTQUFTLEVBQUUsZ0JBQWdCLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBPcmlnaW5hbCB3b3JrIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKiBNb2RpZmllZCB3b3JrIENvcHlyaWdodCBEYXRlUG9sbC1TeXN0ZW1zXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuaW1wb3J0IHsgQXJpYURlc2NyaWJlciwgRm9jdXNNb25pdG9yIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2ExMXknO1xuaW1wb3J0IHsgRU5URVIsIFNQQUNFIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2tleWNvZGVzJztcbmltcG9ydCB7XG4gIEFOSU1BVElPTl9NT0RVTEVfVFlQRSxcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENoYW5nZURldGVjdG9yUmVmLFxuICBDb21wb25lbnQsXG4gIEVsZW1lbnRSZWYsXG4gIElucHV0LFxuICBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgVmlld0VuY2Fwc3VsYXRpb24sXG4gIGJvb2xlYW5BdHRyaWJ1dGUsXG4gIGluamVjdCxcbiAgc2lnbmFsLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHsgU3Vic2NyaXB0aW9uLCBtZXJnZSB9IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQgeyBOR0JfU09SVF9ERUZBVUxUX09QVElPTlMsIE5nYlNvcnQsIE5nYlNvcnREZWZhdWx0T3B0aW9ucywgTmdiU29ydGFibGUsIFNvcnRIZWFkZXJBcnJvd1Bvc2l0aW9uIH0gZnJvbSAnLi9zb3J0JztcbmltcG9ydCB7IFNvcnREaXJlY3Rpb24gfSBmcm9tICcuL3NvcnQtZGlyZWN0aW9uJztcbmltcG9ydCB7IGdldFNvcnRIZWFkZXJOb3RDb250YWluZWRXaXRoaW5Tb3J0RXJyb3IgfSBmcm9tICcuL3NvcnQtZXJyb3JzJztcblxuLyoqIENvbHVtbiBkZWZpbml0aW9uIGFzc29jaWF0ZWQgd2l0aCBhIGBOZ2JTb3J0SGVhZGVyYC4gKi9cbmludGVyZmFjZSBOZ2JTb3J0SGVhZGVyQ29sdW1uRGVmIHtcbiAgbmFtZTogc3RyaW5nO1xufVxuXG4vKipcbiAqIEFwcGxpZXMgc29ydGluZyBiZWhhdmlvciAoY2xpY2sgdG8gY2hhbmdlIHNvcnQpIGFuZCBzdHlsZXMgdG8gYW4gZWxlbWVudCwgaW5jbHVkaW5nIGFuXG4gKiBhcnJvdyB0byBkaXNwbGF5IHRoZSBjdXJyZW50IHNvcnQgZGlyZWN0aW9uLlxuICpcbiAqIE11c3QgYmUgcHJvdmlkZWQgd2l0aCBhbiBpZCBhbmQgY29udGFpbmVkIHdpdGhpbiBhIHBhcmVudCBOZ2JTb3J0IGRpcmVjdGl2ZS5cbiAqXG4gKiBJZiB1c2VkIG9uIGhlYWRlciBjZWxscyBpbiBhIENka1RhYmxlLCBpdCB3aWxsIGF1dG9tYXRpY2FsbHkgZGVmYXVsdCBpdHMgaWQgZnJvbSBpdHMgY29udGFpbmluZ1xuICogY29sdW1uIGRlZmluaXRpb24uXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ1tuZ2Itc29ydC1oZWFkZXJdJyxcbiAgZXhwb3J0QXM6ICduZ2JTb3J0SGVhZGVyJyxcbiAgdGVtcGxhdGVVcmw6ICdzb3J0LWhlYWRlci5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJ3NvcnQtaGVhZGVyLnNjc3MnXSxcbiAgaG9zdDoge1xuICAgIGNsYXNzOiAnbmdiLXNvcnQtaGVhZGVyJyxcbiAgICAnKGNsaWNrKSc6ICdfdG9nZ2xlT25JbnRlcmFjdGlvbigpJyxcbiAgICAnKGtleWRvd24pJzogJ19oYW5kbGVLZXlkb3duKCRldmVudCknLFxuICAgICcobW91c2VsZWF2ZSknOiAnX3JlY2VudGx5Q2xlYXJlZC5zZXQobnVsbCknLFxuICAgICdbYXR0ci5hcmlhLXNvcnRdJzogJ19nZXRBcmlhU29ydEF0dHJpYnV0ZSgpJyxcbiAgICAnW2NsYXNzLm5nYi1zb3J0LWhlYWRlci1kaXNhYmxlZF0nOiAnX2lzRGlzYWJsZWQoKScsXG4gIH0sXG4gIGVuY2Fwc3VsYXRpb246IFZpZXdFbmNhcHN1bGF0aW9uLk5vbmUsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBOZ2JTb3J0SGVhZGVyIGltcGxlbWVudHMgTmdiU29ydGFibGUsIE9uRGVzdHJveSwgT25Jbml0LCBBZnRlclZpZXdJbml0IHtcbiAgX3NvcnQgPSBpbmplY3QoTmdiU29ydCwgeyBvcHRpb25hbDogdHJ1ZSB9KSE7XG4gIF9jb2x1bW5EZWYgPSBpbmplY3Q8TmdiU29ydEhlYWRlckNvbHVtbkRlZj4oJ05HQl9TT1JUX0hFQURFUl9DT0xVTU5fREVGJyBhcyBhbnksIHtcbiAgICBvcHRpb25hbDogdHJ1ZSxcbiAgfSk7XG4gIHByaXZhdGUgX2NoYW5nZURldGVjdG9yUmVmID0gaW5qZWN0KENoYW5nZURldGVjdG9yUmVmKTtcbiAgcHJpdmF0ZSBfZm9jdXNNb25pdG9yID0gaW5qZWN0KEZvY3VzTW9uaXRvcik7XG4gIHByaXZhdGUgX2VsZW1lbnRSZWYgPSBpbmplY3Q8RWxlbWVudFJlZjxIVE1MRWxlbWVudD4+KEVsZW1lbnRSZWYpO1xuICBwcml2YXRlIF9hcmlhRGVzY3JpYmVyID0gaW5qZWN0KEFyaWFEZXNjcmliZXIsIHsgb3B0aW9uYWw6IHRydWUgfSk7XG4gIHByaXZhdGUgX3JlbmRlckNoYW5nZXM6IFN1YnNjcmlwdGlvbiB8IHVuZGVmaW5lZDtcbiAgcHJvdGVjdGVkIF9hbmltYXRpb25Nb2R1bGUgPSBpbmplY3QoQU5JTUFUSU9OX01PRFVMRV9UWVBFLCB7IG9wdGlvbmFsOiB0cnVlIH0pO1xuXG4gIC8qKlxuICAgKiBJbmRpY2F0ZXMgd2hpY2ggc3RhdGUgd2FzIGp1c3QgY2xlYXJlZCBmcm9tIHRoZSBzb3J0IGhlYWRlci5cbiAgICogV2lsbCBiZSByZXNldCBvbiB0aGUgbmV4dCBpbnRlcmFjdGlvbi4gVXNlZCBmb3IgY29vcmRpbmF0aW5nIGFuaW1hdGlvbnMuXG4gICAqL1xuICBwcm90ZWN0ZWQgX3JlY2VudGx5Q2xlYXJlZCA9IHNpZ25hbDxTb3J0RGlyZWN0aW9uIHwgbnVsbD4obnVsbCk7XG5cbiAgLyoqXG4gICAqIFRoZSBlbGVtZW50IHdpdGggcm9sZT1cImJ1dHRvblwiIGluc2lkZSB0aGlzIGNvbXBvbmVudCdzIHZpZXcuIFdlIG5lZWQgdGhpc1xuICAgKiBpbiBvcmRlciB0byBhcHBseSBhIGRlc2NyaXB0aW9uIHdpdGggQXJpYURlc2NyaWJlci5cbiAgICovXG4gIHByaXZhdGUgX3NvcnRCdXR0b24hOiBIVE1MRWxlbWVudDtcblxuICAvKipcbiAgICogSUQgb2YgdGhpcyBzb3J0IGhlYWRlci4gSWYgdXNlZCB3aXRoaW4gdGhlIGNvbnRleHQgb2YgYSBDZGtDb2x1bW5EZWYsIHRoaXMgd2lsbCBkZWZhdWx0IHRvXG4gICAqIHRoZSBjb2x1bW4ncyBuYW1lLlxuICAgKi9cbiAgQElucHV0KCduZ2Itc29ydC1oZWFkZXInKSBpZCE6IHN0cmluZztcblxuICAvKiogU2V0cyB0aGUgcG9zaXRpb24gb2YgdGhlIGFycm93IHRoYXQgZGlzcGxheXMgd2hlbiBzb3J0ZWQuICovXG4gIEBJbnB1dCgpIGFycm93UG9zaXRpb246IFNvcnRIZWFkZXJBcnJvd1Bvc2l0aW9uID0gJ2FmdGVyJztcblxuICAvKiogT3ZlcnJpZGVzIHRoZSBzb3J0IHN0YXJ0IHZhbHVlIG9mIHRoZSBjb250YWluaW5nIE5nYlNvcnQgZm9yIHRoaXMgTmdiU29ydGFibGUuICovXG4gIEBJbnB1dCgpIHN0YXJ0ITogU29ydERpcmVjdGlvbjtcblxuICAvKiogd2hldGhlciB0aGUgc29ydCBoZWFkZXIgaXMgZGlzYWJsZWQuICovXG4gIEBJbnB1dCh7IHRyYW5zZm9ybTogYm9vbGVhbkF0dHJpYnV0ZSB9KVxuICBkaXNhYmxlZCA9IGZhbHNlO1xuXG4gIC8qKlxuICAgKiBEZXNjcmlwdGlvbiBhcHBsaWVkIHRvIE5nYlNvcnRIZWFkZXIncyBidXR0b24gZWxlbWVudCB3aXRoIGFyaWEtZGVzY3JpYmVkYnkuIFRoaXMgdGV4dCBzaG91bGRcbiAgICogZGVzY3JpYmUgdGhlIGFjdGlvbiB0aGF0IHdpbGwgb2NjdXIgd2hlbiB0aGUgdXNlciBjbGlja3MgdGhlIHNvcnQgaGVhZGVyLlxuICAgKi9cbiAgQElucHV0KClcbiAgZ2V0IHNvcnRBY3Rpb25EZXNjcmlwdGlvbigpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9zb3J0QWN0aW9uRGVzY3JpcHRpb247XG4gIH1cbiAgc2V0IHNvcnRBY3Rpb25EZXNjcmlwdGlvbih2YWx1ZTogc3RyaW5nKSB7XG4gICAgdGhpcy5fdXBkYXRlU29ydEFjdGlvbkRlc2NyaXB0aW9uKHZhbHVlKTtcbiAgfVxuICAvLyBEZWZhdWx0IHRoZSBhY3Rpb24gZGVzY3JpcHRpb24gdG8gXCJTb3J0XCIgYmVjYXVzZSBpdCdzIGJldHRlciB0aGFuIG5vdGhpbmcuXG4gIC8vIFdpdGhvdXQgYSBkZXNjcmlwdGlvbiwgdGhlIGJ1dHRvbidzIGxhYmVsIGNvbWVzIGZyb20gdGhlIHNvcnQgaGVhZGVyIHRleHQgY29udGVudCxcbiAgLy8gd2hpY2ggZG9lc24ndCBnaXZlIGFueSBpbmRpY2F0aW9uIHRoYXQgaXQgcGVyZm9ybXMgYSBzb3J0aW5nIG9wZXJhdGlvbi5cbiAgcHJpdmF0ZSBfc29ydEFjdGlvbkRlc2NyaXB0aW9uID0gJ1NvcnQnO1xuXG4gIC8qKiBPdmVycmlkZXMgdGhlIGRpc2FibGUgY2xlYXIgdmFsdWUgb2YgdGhlIGNvbnRhaW5pbmcgTmdiU29ydCBmb3IgdGhpcyBOZ2JTb3J0YWJsZS4gKi9cbiAgQElucHV0KHsgdHJhbnNmb3JtOiBib29sZWFuQXR0cmlidXRlIH0pXG4gIGRpc2FibGVDbGVhciE6IGJvb2xlYW47XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEBhbmd1bGFyLWVzbGludC9wcmVmZXItaW5qZWN0XG4gIGNvbnN0cnVjdG9yKC4uLmFyZ3M6IHVua25vd25bXSk7XG5cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSBpbmplY3Q8TmdiU29ydERlZmF1bHRPcHRpb25zPihOR0JfU09SVF9ERUZBVUxUX09QVElPTlMsIHtcbiAgICAgIG9wdGlvbmFsOiB0cnVlLFxuICAgIH0pO1xuXG4gICAgaWYgKCF0aGlzLl9zb3J0ICYmICh0eXBlb2YgbmdEZXZNb2RlID09PSAndW5kZWZpbmVkJyB8fCBuZ0Rldk1vZGUpKSB7XG4gICAgICB0aHJvdyBnZXRTb3J0SGVhZGVyTm90Q29udGFpbmVkV2l0aGluU29ydEVycm9yKCk7XG4gICAgfVxuXG4gICAgaWYgKGRlZmF1bHRPcHRpb25zPy5hcnJvd1Bvc2l0aW9uKSB7XG4gICAgICB0aGlzLmFycm93UG9zaXRpb24gPSBkZWZhdWx0T3B0aW9ucz8uYXJyb3dQb3NpdGlvbjtcbiAgICB9XG4gIH1cblxuICBuZ09uSW5pdCgpIHtcbiAgICBpZiAoIXRoaXMuaWQgJiYgdGhpcy5fY29sdW1uRGVmKSB7XG4gICAgICB0aGlzLmlkID0gdGhpcy5fY29sdW1uRGVmLm5hbWU7XG4gICAgfVxuXG4gICAgdGhpcy5fc29ydC5yZWdpc3Rlcih0aGlzKTtcbiAgICB0aGlzLl9yZW5kZXJDaGFuZ2VzID0gbWVyZ2UodGhpcy5fc29ydC5fc3RhdGVDaGFuZ2VzLCB0aGlzLl9zb3J0LnNvcnRDaGFuZ2UpLnN1YnNjcmliZSgoKSA9PiB0aGlzLl9jaGFuZ2VEZXRlY3RvclJlZi5tYXJrRm9yQ2hlY2soKSk7XG4gICAgdGhpcy5fc29ydEJ1dHRvbiA9IHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5xdWVyeVNlbGVjdG9yKCcubmdiLXNvcnQtaGVhZGVyLWNvbnRhaW5lcicpITtcbiAgICB0aGlzLl91cGRhdGVTb3J0QWN0aW9uRGVzY3JpcHRpb24odGhpcy5fc29ydEFjdGlvbkRlc2NyaXB0aW9uKTtcbiAgfVxuXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpIHtcbiAgICAvLyBXZSB1c2UgdGhlIGZvY3VzIG1vbml0b3IgYmVjYXVzZSB3ZSBhbHNvIHdhbnQgdG8gc3R5bGVcbiAgICAvLyB0aGluZ3MgZGlmZmVyZW50bHkgYmFzZWQgb24gdGhlIGZvY3VzIG9yaWdpbi5cbiAgICB0aGlzLl9mb2N1c01vbml0b3IubW9uaXRvcih0aGlzLl9lbGVtZW50UmVmLCB0cnVlKS5zdWJzY3JpYmUoKCkgPT4gdGhpcy5fcmVjZW50bHlDbGVhcmVkLnNldChudWxsKSk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICB0aGlzLl9mb2N1c01vbml0b3Iuc3RvcE1vbml0b3JpbmcodGhpcy5fZWxlbWVudFJlZik7XG4gICAgdGhpcy5fc29ydC5kZXJlZ2lzdGVyKHRoaXMpO1xuICAgIHRoaXMuX3JlbmRlckNoYW5nZXM/LnVuc3Vic2NyaWJlKCk7XG5cbiAgICBpZiAodGhpcy5fc29ydEJ1dHRvbikge1xuICAgICAgdGhpcy5fYXJpYURlc2NyaWJlcj8ucmVtb3ZlRGVzY3JpcHRpb24odGhpcy5fc29ydEJ1dHRvbiwgdGhpcy5fc29ydEFjdGlvbkRlc2NyaXB0aW9uKTtcbiAgICB9XG4gIH1cblxuICAvKiogVHJpZ2dlcnMgdGhlIHNvcnQgb24gdGhpcyBzb3J0IGhlYWRlciBhbmQgcmVtb3ZlcyB0aGUgaW5kaWNhdG9yIGhpbnQuICovXG4gIF90b2dnbGVPbkludGVyYWN0aW9uKCkge1xuICAgIGlmICghdGhpcy5faXNEaXNhYmxlZCgpKSB7XG4gICAgICBjb25zdCB3YXNTb3J0ZWQgPSB0aGlzLl9pc1NvcnRlZCgpO1xuICAgICAgY29uc3QgcHJldkRpcmVjdGlvbiA9IHRoaXMuX3NvcnQuZGlyZWN0aW9uO1xuICAgICAgdGhpcy5fc29ydC5zb3J0KHRoaXMpO1xuICAgICAgdGhpcy5fcmVjZW50bHlDbGVhcmVkLnNldCh3YXNTb3J0ZWQgJiYgIXRoaXMuX2lzU29ydGVkKCkgPyBwcmV2RGlyZWN0aW9uIDogbnVsbCk7XG4gICAgfVxuICB9XG5cbiAgX2hhbmRsZUtleWRvd24oZXZlbnQ6IEtleWJvYXJkRXZlbnQpIHtcbiAgICBpZiAoZXZlbnQua2V5Q29kZSA9PT0gU1BBQ0UgfHwgZXZlbnQua2V5Q29kZSA9PT0gRU5URVIpIHtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICB0aGlzLl90b2dnbGVPbkludGVyYWN0aW9uKCk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFdoZXRoZXIgdGhpcyBOZ2JTb3J0SGVhZGVyIGlzIGN1cnJlbnRseSBzb3J0ZWQgaW4gZWl0aGVyIGFzY2VuZGluZyBvciBkZXNjZW5kaW5nIG9yZGVyLiAqL1xuICBfaXNTb3J0ZWQoKSB7XG4gICAgcmV0dXJuIHRoaXMuX3NvcnQuYWN0aXZlID09IHRoaXMuaWQgJiYgKHRoaXMuX3NvcnQuZGlyZWN0aW9uID09PSAnYXNjJyB8fCB0aGlzLl9zb3J0LmRpcmVjdGlvbiA9PT0gJ2Rlc2MnKTtcbiAgfVxuXG4gIF9pc0Rpc2FibGVkKCkge1xuICAgIHJldHVybiB0aGlzLl9zb3J0LmRpc2FibGVkIHx8IHRoaXMuZGlzYWJsZWQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0cyB0aGUgYXJpYS1zb3J0IGF0dHJpYnV0ZSB0aGF0IHNob3VsZCBiZSBhcHBsaWVkIHRvIHRoaXMgc29ydCBoZWFkZXIuIElmIHRoaXMgaGVhZGVyXG4gICAqIGlzIG5vdCBzb3J0ZWQsIHJldHVybnMgbnVsbCBzbyB0aGF0IHRoZSBhdHRyaWJ1dGUgaXMgcmVtb3ZlZCBmcm9tIHRoZSBob3N0IGVsZW1lbnQuIEFyaWEgc3BlY1xuICAgKiBzYXlzIHRoYXQgdGhlIGFyaWEtc29ydCBwcm9wZXJ0eSBzaG91bGQgb25seSBiZSBwcmVzZW50IG9uIG9uZSBoZWFkZXIgYXQgYSB0aW1lLCBzbyByZW1vdmluZ1xuICAgKiBlbnN1cmVzIHRoaXMgaXMgdHJ1ZS5cbiAgICovXG4gIF9nZXRBcmlhU29ydEF0dHJpYnV0ZSgpIHtcbiAgICBpZiAoIXRoaXMuX2lzU29ydGVkKCkpIHtcbiAgICAgIHJldHVybiAnbm9uZSc7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuX3NvcnQuZGlyZWN0aW9uID09ICdhc2MnID8gJ2FzY2VuZGluZycgOiAnZGVzY2VuZGluZyc7XG4gIH1cblxuICAvKiogV2hldGhlciB0aGUgYXJyb3cgaW5zaWRlIHRoZSBzb3J0IGhlYWRlciBzaG91bGQgYmUgcmVuZGVyZWQuICovXG4gIF9yZW5kZXJBcnJvdygpIHtcbiAgICByZXR1cm4gIXRoaXMuX2lzRGlzYWJsZWQoKSB8fCB0aGlzLl9pc1NvcnRlZCgpO1xuICB9XG5cbiAgcHJpdmF0ZSBfdXBkYXRlU29ydEFjdGlvbkRlc2NyaXB0aW9uKG5ld0Rlc2NyaXB0aW9uOiBzdHJpbmcpIHtcbiAgICAvLyBXZSB1c2UgQXJpYURlc2NyaWJlciBmb3IgdGhlIHNvcnQgYnV0dG9uIGluc3RlYWQgb2Ygc2V0dGluZyBhbiBgYXJpYS1sYWJlbGAgYmVjYXVzZSBzb21lXG4gICAgLy8gc2NyZWVuIHJlYWRlcnMgKG5vdGFibHkgVm9pY2VPdmVyKSB3aWxsIHJlYWQgYm90aCB0aGUgY29sdW1uIGhlYWRlciAqYW5kKiB0aGUgYnV0dG9uJ3MgbGFiZWxcbiAgICAvLyBmb3IgZXZlcnkgKmNlbGwqIGluIHRoZSB0YWJsZSwgY3JlYXRpbmcgYSBsb3Qgb2YgdW5uZWNlc3Nhcnkgbm9pc2UuXG5cbiAgICAvLyBJZiBfc29ydEJ1dHRvbiBpcyB1bmRlZmluZWQsIHRoZSBjb21wb25lbnQgaGFzbid0IGJlZW4gaW5pdGlhbGl6ZWQgeWV0IHNvIHRoZXJlJ3NcbiAgICAvLyBub3RoaW5nIHRvIHVwZGF0ZSBpbiB0aGUgRE9NLlxuICAgIGlmICh0aGlzLl9zb3J0QnV0dG9uKSB7XG4gICAgICAvLyByZW1vdmVEZXNjcmlwdGlvbiB3aWxsIG5vLW9wIGlmIHRoZXJlIGlzIG5vIGV4aXN0aW5nIG1lc3NhZ2UuXG4gICAgICAvLyBUT0RPKGplbGJvdXJuKTogcmVtb3ZlIG9wdGlvbmFsIGNoYWluaW5nIHdoZW4gQXJpYURlc2NyaWJlciBpcyByZXF1aXJlZC5cbiAgICAgIHRoaXMuX2FyaWFEZXNjcmliZXI/LnJlbW92ZURlc2NyaXB0aW9uKHRoaXMuX3NvcnRCdXR0b24sIHRoaXMuX3NvcnRBY3Rpb25EZXNjcmlwdGlvbik7XG4gICAgICB0aGlzLl9hcmlhRGVzY3JpYmVyPy5kZXNjcmliZSh0aGlzLl9zb3J0QnV0dG9uLCBuZXdEZXNjcmlwdGlvbik7XG4gICAgfVxuXG4gICAgdGhpcy5fc29ydEFjdGlvbkRlc2NyaXB0aW9uID0gbmV3RGVzY3JpcHRpb247XG4gIH1cbn1cbiIsIjwhLS1cbiAgV2Ugc2V0IHRoZSBgdGFiaW5kZXhgIG9uIGFuIGVsZW1lbnQgaW5zaWRlIHRoZSB0YWJsZSBoZWFkZXIsIHJhdGhlciB0aGFuIHRoZSBoZWFkZXIgaXRzZWxmLFxuICBiZWNhdXNlIG9mIGEgYnVnIGluIE5WREEgd2hlcmUgaGF2aW5nIGEgYHRhYmluZGV4YCBvbiBhIGB0aGAgYnJlYWtzIGtleWJvYXJkIG5hdmlnYXRpb24gaW4gdGhlXG4gIHRhYmxlIChzZWUgaHR0cHM6Ly9naXRodWIuY29tL252YWNjZXNzL252ZGEvaXNzdWVzLzc3MTgpLiBUaGlzIGFsbG93cyBmb3IgdGhlIGhlYWRlciB0byBib3RoXG4gIGJlIGZvY3VzYWJsZSwgYW5kIGhhdmUgc2NyZWVuIHJlYWRlcnMgcmVhZCBvdXQgaXRzIGBhcmlhLXNvcnRgIHN0YXRlLiBXZSBwcmVmZXIgdGhpcyBhcHByb2FjaFxuICBvdmVyIGhhdmluZyBhIGJ1dHRvbiB3aXRoIGFuIGBhcmlhLWxhYmVsYCBpbnNpZGUgdGhlIGhlYWRlciwgYmVjYXVzZSB0aGUgYnV0dG9uJ3MgYGFyaWEtbGFiZWxgXG4gIHdpbGwgYmUgcmVhZCBvdXQgYXMgdGhlIHVzZXIgaXMgbmF2aWdhdGluZyB0aGUgdGFibGUncyBjZWxsIChzZWUgIzEzMDEyKS5cblxuICBUaGUgYXBwcm9hY2ggaXMgYmFzZWQgb2ZmIG9mOiBodHRwczovL2RlcXVldW5pdmVyc2l0eS5jb20vbGlicmFyeS9hcmlhL3RhYmxlcy9zZi1zb3J0YWJsZS1ncmlkXG4tLT5cbjxkaXZcbiAgY2xhc3M9XCJuZ2Itc29ydC1oZWFkZXItY29udGFpbmVyIG5nYi1mb2N1cy1pbmRpY2F0b3JcIlxuICBbY2xhc3MubmdiLXNvcnQtaGVhZGVyLXNvcnRlZF09XCJfaXNTb3J0ZWQoKVwiXG4gIFtjbGFzcy5uZ2Itc29ydC1oZWFkZXItcG9zaXRpb24tYmVmb3JlXT1cImFycm93UG9zaXRpb24gPT09ICdiZWZvcmUnXCJcbiAgW2NsYXNzLm5nYi1zb3J0LWhlYWRlci1kZXNjZW5kaW5nXT1cInRoaXMuX3NvcnQuZGlyZWN0aW9uID09PSAnZGVzYydcIlxuICBbY2xhc3MubmdiLXNvcnQtaGVhZGVyLWFzY2VuZGluZ109XCJ0aGlzLl9zb3J0LmRpcmVjdGlvbiA9PT0gJ2FzYydcIlxuICBbY2xhc3MubmdiLXNvcnQtaGVhZGVyLXJlY2VudGx5LWNsZWFyZWQtYXNjZW5kaW5nXT1cIl9yZWNlbnRseUNsZWFyZWQoKSA9PT0gJ2FzYydcIlxuICBbY2xhc3MubmdiLXNvcnQtaGVhZGVyLXJlY2VudGx5LWNsZWFyZWQtZGVzY2VuZGluZ109XCJfcmVjZW50bHlDbGVhcmVkKCkgPT09ICdkZXNjJ1wiXG4gIFtjbGFzcy5uZ2Itc29ydC1oZWFkZXItYW5pbWF0aW9ucy1kaXNhYmxlZF09XCJfYW5pbWF0aW9uTW9kdWxlID09PSAnTm9vcEFuaW1hdGlvbnMnXCJcbiAgW2F0dHIudGFiaW5kZXhdPVwiX2lzRGlzYWJsZWQoKSA/IG51bGwgOiAwXCJcbiAgW2F0dHIucm9sZV09XCJfaXNEaXNhYmxlZCgpID8gbnVsbCA6ICdidXR0b24nXCI+XG4gIDwhLS1cbiAgICBUT0RPKGNyaXNiZXRvKTogdGhpcyBkaXYgaXNuJ3Qgc3RyaWN0bHkgbmVjZXNzYXJ5LCBidXQgd2UgaGF2ZSB0byBrZWVwIGl0IGR1ZSB0byBhIGxhcmdlXG4gICAgbnVtYmVyIG9mIHNjcmVlbnNob3QgZGlmZiBmYWlsdXJlcy4gSXQgc2hvdWxkIGJlIHJlbW92ZWQgZXZlbnR1YWxseS4gTm90ZSB0aGF0IHRoZSBkaWZmZXJlbmNlXG4gICAgaXNuJ3QgdmlzaWJsZSB3aXRoIGEgc2hvcnRlciBoZWFkZXIsIGJ1dCBvbmNlIGl0IGJyZWFrcyB1cCBpbnRvIG11bHRpcGxlIGxpbmVzLCB0aGlzIGVsZW1lbnRcbiAgICBjYXVzZXMgaXQgdG8gYmUgY2VudGVyLWFsaWduZWQsIHdoZXJlYXMgcmVtb3ZpbmcgaXQgd2lsbCBrZWVwIHRoZSB0ZXh0IHRvIHRoZSBsZWZ0LlxuICAtLT5cbiAgPGRpdiBjbGFzcz1cIm5nYi1zb3J0LWhlYWRlci1jb250ZW50XCI+XG4gICAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuICA8L2Rpdj5cblxuICA8IS0tIERpc2FibGUgYW5pbWF0aW9ucyB3aGlsZSBhIGN1cnJlbnQgYW5pbWF0aW9uIGlzIHJ1bm5pbmcgLS0+XG4gIEBpZiAoX3JlbmRlckFycm93KCkpIHtcbiAgPGRpdiBjbGFzcz1cIm5nYi1zb3J0LWhlYWRlci1hcnJvd1wiPlxuICAgIDxzdmcgdmlld0JveD1cIjAgLTk2MCA5NjAgOTYwXCIgZm9jdXNhYmxlPVwiZmFsc2VcIiBhcmlhLWhpZGRlbj1cInRydWVcIj5cbiAgICAgIDxwYXRoIGQ9XCJNNDQwLTI0MHYtMzY4TDI5Ni00NjRsLTU2LTU2IDI0MC0yNDAgMjQwIDI0MC01NiA1Ni0xNDQtMTQ0djM2OGgtODBaXCIgLz5cbiAgICA8L3N2Zz5cbiAgPC9kaXY+XG4gIH1cbjwvZGl2PlxuIl19