UNPKG

@spartacus/storefront

Version:

Spartacus Storefront is a package that you can include in your application, which allows you to add default storefront features.

236 lines 39.4 kB
import { ChangeDetectionStrategy, Component, Input, Optional, } from '@angular/core'; import { PageType, } from '@spartacus/core'; import { of } from 'rxjs'; import { filter, map, switchMap, tap } from 'rxjs/operators'; import { ICON_TYPE } from '../../../cms-components/misc/icon/index'; import * as i0 from "@angular/core"; import * as i1 from "./search-box-component.service"; import * as i2 from "../../../cms-structure/page/model/cms-component-data"; import * as i3 from "@spartacus/core"; import * as i4 from "../../misc/icon/icon.component"; import * as i5 from "../../../shared/components/media/media.component"; import * as i6 from "@angular/common"; import * as i7 from "@angular/router"; import * as i8 from "./highlight.pipe"; const DEFAULT_SEARCH_BOX_CONFIG = { minCharactersBeforeRequest: 1, displayProducts: true, displaySuggestions: true, maxProducts: 5, maxSuggestions: 5, displayProductImages: true, }; export class SearchBoxComponent { constructor(searchBoxComponentService, componentData, winRef, routingService) { var _a; this.searchBoxComponentService = searchBoxComponentService; this.componentData = componentData; this.winRef = winRef; this.routingService = routingService; this.iconTypes = ICON_TYPE; /** * In some occasions we need to ignore the close event, * for example when we click inside the search result section. */ this.ignoreCloseEvent = false; this.chosenWord = ''; /** * Returns the SearchBox configuration. The configuration is driven by multiple * layers: default configuration, (optional) backend configuration and (optional) * input configuration. */ this.config$ = (((_a = this.componentData) === null || _a === void 0 ? void 0 : _a.data$) || of({})).pipe(map((config) => { const isBool = (obj, prop) => (obj === null || obj === void 0 ? void 0 : obj[prop]) !== 'false' && (obj === null || obj === void 0 ? void 0 : obj[prop]) !== false; return Object.assign(Object.assign(Object.assign(Object.assign({}, DEFAULT_SEARCH_BOX_CONFIG), config), { displayProducts: isBool(config, 'displayProducts'), displayProductImages: isBool(config, 'displayProductImages'), displaySuggestions: isBool(config, 'displaySuggestions') }), this.config); }), tap((config) => (this.config = config))); this.results$ = this.config$.pipe(switchMap((config) => this.searchBoxComponentService.getResults(config))); } /** * Sets the search box input field */ set queryText(value) { if (value) { this.search(value); } } ngOnInit() { this.subscription = this.routingService .getRouterState() .pipe(filter((data) => !data.nextState)) .subscribe((data) => { var _a, _b; if (!(((_a = data.state.context) === null || _a === void 0 ? void 0 : _a.id) === 'search' && ((_b = data.state.context) === null || _b === void 0 ? void 0 : _b.type) === PageType.CONTENT_PAGE)) this.chosenWord = ''; }); } /** * Closes the searchBox and opens the search result page. */ search(query) { this.searchBoxComponentService.search(query, this.config); // force the searchBox to open this.open(); } /** * Opens the type-ahead searchBox */ open() { this.searchBoxComponentService.toggleBodyClass('searchbox-is-active', true); } /** * Dispatch UI events for Suggestion selected * * @param eventData the data for the event */ dispatchSuggestionEvent(eventData) { this.searchBoxComponentService.dispatchSuggestionSelectedEvent(eventData); } /** * Dispatch UI events for Product selected * * @param eventData the data for the event */ dispatchProductEvent(eventData) { this.searchBoxComponentService.dispatchProductSelectedEvent(eventData); } /** * Closes the type-ahead searchBox. */ close(event, force) { // Use timeout to detect changes setTimeout(() => { if ((!this.ignoreCloseEvent && !this.isSearchBoxFocused()) || force) { this.blurSearchBox(event); } }); } blurSearchBox(event) { this.searchBoxComponentService.toggleBodyClass('searchbox-is-active', false); if (event && event.target) { event.target.blur(); } } // Check if focus is on searchbox or result list elements isSearchBoxFocused() { return (this.getResultElements().includes(this.getFocusedElement()) || this.winRef.document.querySelector('input[aria-label="Search"]') === this.getFocusedElement()); } /** * Especially in mobile we do not want the search icon * to focus the input again when it's already open. * */ avoidReopen(event) { if (this.searchBoxComponentService.hasBodyClass('searchbox-is-active')) { this.close(event); event.preventDefault(); } } // Return result list as HTMLElement array getResultElements() { return Array.from(this.winRef.document.querySelectorAll('.products > li a, .suggestions > li a')); } // Return focused element as HTMLElement getFocusedElement() { return this.winRef.document.activeElement; } updateChosenWord(chosenWord) { this.chosenWord = chosenWord; } getFocusedIndex() { return this.getResultElements().indexOf(this.getFocusedElement()); } // Focus on previous item in results list focusPreviousChild(event) { event.preventDefault(); // Negate normal keyscroll const [results, focusedIndex] = [ this.getResultElements(), this.getFocusedIndex(), ]; // Focus on last index moving to first if (results.length) { if (focusedIndex < 1) { results[results.length - 1].focus(); } else { results[focusedIndex - 1].focus(); } } } // Focus on next item in results list focusNextChild(event) { this.open(); event.preventDefault(); // Negate normal keyscroll const [results, focusedIndex] = [ this.getResultElements(), this.getFocusedIndex(), ]; // Focus on first index moving to last if (results.length) { if (focusedIndex >= results.length - 1) { results[0].focus(); } else { results[focusedIndex + 1].focus(); } } } /** * Opens the PLP with the given query. * * TODO: if there's a single product match, we could open the PDP. */ launchSearchResult(event, query) { if (!query || query.trim().length === 0) { return; } this.close(event); this.searchBoxComponentService.launchSearchPage(query); } /** * Disables closing the search result list. */ disableClose() { this.ignoreCloseEvent = true; } preventDefault(ev) { ev.preventDefault(); } /** * Clears the search box input field */ clear(el) { this.disableClose(); el.value = ''; this.searchBoxComponentService.clearResults(); // Use Timeout to run after blur event to prevent the searchbox from closing on mobile setTimeout(() => { // Retain focus on input lost by clicking on icon el.focus(); this.ignoreCloseEvent = false; }); } ngOnDestroy() { var _a; (_a = this.subscription) === null || _a === void 0 ? void 0 : _a.unsubscribe(); } } SearchBoxComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.0.5", ngImport: i0, type: SearchBoxComponent, deps: [{ token: i1.SearchBoxComponentService }, { token: i2.CmsComponentData, optional: true }, { token: i3.WindowRef }, { token: i3.RoutingService }], target: i0.ɵɵFactoryTarget.Component }); SearchBoxComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.0.5", type: SearchBoxComponent, selector: "cx-searchbox", inputs: { config: "config", queryText: "queryText" }, ngImport: i0, template: "<label class=\"searchbox\" [class.dirty]=\"!!searchInput.value\">\n <input\n #searchInput\n [placeholder]=\"'searchBox.placeholder' | cxTranslate\"\n autocomplete=\"off\"\n aria-describedby=\"initialDescription\"\n aria-controls=\"results\"\n [attr.aria-label]=\"'common.search' | cxTranslate\"\n (focus)=\"open()\"\n (click)=\"open()\"\n (input)=\"search(searchInput.value)\"\n (blur)=\"close($event)\"\n (keydown.escape)=\"close($event)\"\n (keydown.enter)=\"\n close($event, true);\n launchSearchResult($event, searchInput.value);\n updateChosenWord(searchInput.value)\n \"\n (keydown.arrowup)=\"focusPreviousChild($event)\"\n (keydown.arrowdown)=\"focusNextChild($event)\"\n value=\"{{ chosenWord }}\"\n />\n\n <button\n [attr.aria-label]=\"'common.reset' | cxTranslate\"\n (mousedown)=\"clear(searchInput)\"\n (keydown.enter)=\"clear(searchInput)\"\n class=\"reset\"\n >\n <cx-icon [type]=\"iconTypes.RESET\"></cx-icon>\n </button>\n\n <div role=\"presentation\" class=\"search-icon\">\n <cx-icon [type]=\"iconTypes.SEARCH\"></cx-icon>\n </div>\n\n <button\n [attr.aria-label]=\"'common.search' | cxTranslate\"\n class=\"search\"\n (click)=\"open()\"\n >\n <cx-icon [type]=\"iconTypes.SEARCH\"></cx-icon>\n </button>\n</label>\n\n<div\n *ngIf=\"results$ | async as result\"\n class=\"results\"\n id=\"results\"\n (click)=\"close($event, true)\"\n role=\"listbox\"\n>\n <div\n *ngIf=\"result.message\"\n class=\"message\"\n [innerHTML]=\"result.message\"\n ></div>\n\n <ul\n class=\"suggestions\"\n attr.aria-label=\"{{ 'searchBox.ariaLabelSuggestions' | cxTranslate }}\"\n tabindex=\"0\"\n >\n <li *ngFor=\"let suggestion of result.suggestions\">\n <a\n [innerHTML]=\"suggestion | cxHighlight: searchInput.value\"\n [routerLink]=\"\n {\n cxRoute: 'search',\n params: { query: suggestion }\n } | cxUrl\n \"\n (keydown.arrowup)=\"focusPreviousChild($event)\"\n (keydown.arrowdown)=\"focusNextChild($event)\"\n (keydown.enter)=\"close($event, true)\"\n (keydown.escape)=\"close($event, true)\"\n (blur)=\"close($event)\"\n (mousedown)=\"preventDefault($event)\"\n (click)=\"\n dispatchSuggestionEvent({\n freeText: searchInput.value,\n selectedSuggestion: suggestion,\n searchSuggestions: result.suggestions\n });\n updateChosenWord(suggestion)\n \"\n >\n </a>\n </li>\n </ul>\n\n <ul\n class=\"products\"\n *ngIf=\"result.products\"\n attr.aria-label=\"{{ 'searchBox.ariaLabelProducts' | cxTranslate }}\"\n tabindex=\"0\"\n >\n <li *ngFor=\"let product of result.products\">\n <a\n [routerLink]=\"\n {\n cxRoute: 'product',\n params: product\n } | cxUrl\n \"\n [class.has-media]=\"config.displayProductImages\"\n (keydown.arrowup)=\"focusPreviousChild($event)\"\n (keydown.arrowdown)=\"focusNextChild($event)\"\n (keydown.enter)=\"close($event, true)\"\n (keydown.escape)=\"close($event, true)\"\n (blur)=\"close($event)\"\n (mousedown)=\"preventDefault($event)\"\n (click)=\"\n dispatchProductEvent({\n freeText: searchInput.value,\n productCode: product.code\n })\n \"\n >\n <cx-media\n *ngIf=\"config.displayProductImages\"\n [container]=\"product.images?.PRIMARY\"\n format=\"thumbnail\"\n role=\"presentation\"\n ></cx-media>\n <div class=\"name\" [innerHTML]=\"product.nameHtml\"></div>\n <span class=\"price\">{{ product.price?.formattedValue }}</span>\n </a>\n </li>\n </ul>\n <span id=\"initialDescription\" class=\"cx-visually-hidden\">\n {{ 'searchBox.initialDescription' | cxTranslate }}\n </span>\n <div\n *ngIf=\"result.suggestions?.length || result.products?.length\"\n aria-live=\"assertive\"\n class=\"cx-visually-hidden\"\n >\n {{\n 'searchBox.suggestionsResult'\n | cxTranslate: { count: result.suggestions?.length }\n }}\n {{\n 'searchBox.productsResult'\n | cxTranslate: { count: result.products?.length }\n }}\n {{ 'searchBox.initialDescription' | cxTranslate }}\n </div>\n</div>\n", components: [{ type: i4.IconComponent, selector: "cx-icon,[cxIcon]", inputs: ["cxIcon", "type"] }, { type: i5.MediaComponent, selector: "cx-media", inputs: ["container", "format", "alt", "role", "loading"], outputs: ["loaded"] }], directives: [{ type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i6.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i7.RouterLinkWithHref, selector: "a[routerLink],area[routerLink]", inputs: ["routerLink", "target", "queryParams", "fragment", "queryParamsHandling", "preserveFragment", "skipLocationChange", "replaceUrl", "state", "relativeTo"] }], pipes: { "cxTranslate": i3.TranslatePipe, "async": i6.AsyncPipe, "cxHighlight": i8.HighlightPipe, "cxUrl": i3.UrlPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.0.5", ngImport: i0, type: SearchBoxComponent, decorators: [{ type: Component, args: [{ selector: 'cx-searchbox', templateUrl: './search-box.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }] }], ctorParameters: function () { return [{ type: i1.SearchBoxComponentService }, { type: i2.CmsComponentData, decorators: [{ type: Optional }] }, { type: i3.WindowRef }, { type: i3.RoutingService }]; }, propDecorators: { config: [{ type: Input }], queryText: [{ type: Input, args: ['queryText'] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VhcmNoLWJveC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9zdG9yZWZyb250bGliL2Ntcy1jb21wb25lbnRzL25hdmlnYXRpb24vc2VhcmNoLWJveC9zZWFyY2gtYm94LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3N0b3JlZnJvbnRsaWIvY21zLWNvbXBvbmVudHMvbmF2aWdhdGlvbi9zZWFyY2gtYm94L3NlYXJjaC1ib3guY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLHVCQUF1QixFQUN2QixTQUFTLEVBQ1QsS0FBSyxFQUdMLFFBQVEsR0FDVCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBRUwsUUFBUSxHQUdULE1BQU0saUJBQWlCLENBQUM7QUFDekIsT0FBTyxFQUFjLEVBQUUsRUFBZ0IsTUFBTSxNQUFNLENBQUM7QUFDcEQsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQzs7Ozs7Ozs7OztBQVNwRSxNQUFNLHlCQUF5QixHQUFvQjtJQUNqRCwwQkFBMEIsRUFBRSxDQUFDO0lBQzdCLGVBQWUsRUFBRSxJQUFJO0lBQ3JCLGtCQUFrQixFQUFFLElBQUk7SUFDeEIsV0FBVyxFQUFFLENBQUM7SUFDZCxjQUFjLEVBQUUsQ0FBQztJQUNqQixvQkFBb0IsRUFBRSxJQUFJO0NBQzNCLENBQUM7QUFPRixNQUFNLE9BQU8sa0JBQWtCO0lBdUI3QixZQUNZLHlCQUFvRCxFQUVwRCxhQUFzRCxFQUN0RCxNQUFpQixFQUNqQixjQUE4Qjs7UUFKOUIsOEJBQXlCLEdBQXpCLHlCQUF5QixDQUEyQjtRQUVwRCxrQkFBYSxHQUFiLGFBQWEsQ0FBeUM7UUFDdEQs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UUFDdEYsVUFBVSxDQUFDLEdBQUcsRUFBRTtZQUNkLGlEQUFpRDtZQUNqRCxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDWCxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsS0FBSyxDQUFDO1FBQ2hDLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELFdBQVc7O1FBQ1QsTUFBQSxJQUFJLENBQUMsWUFBWSwwQ0FBRSxXQUFXLEVBQUUsQ0FBQztJQUNuQyxDQUFDOzsrR0EzUFUsa0JBQWtCO21HQUFsQixrQkFBa0IsMEdDdkMvQixrMklBcUpBOzJGRDlHYSxrQkFBa0I7a0JBTDlCLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLGNBQWM7b0JBQ3hCLFdBQVcsRUFBRSw2QkFBNkI7b0JBQzFDLGVBQWUsRUFBRSx1QkFBdUIsQ0FBQyxNQUFNO2lCQUNoRDs7MEJBMEJJLFFBQVE7aUdBeEJGLE1BQU07c0JBQWQsS0FBSztnQkFNRixTQUFTO3NCQURaLEtBQUs7dUJBQUMsV0FBVyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICBDb21wb25lbnQsXG4gIElucHV0LFxuICBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgT3B0aW9uYWwsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcbiAgQ21zU2VhcmNoQm94Q29tcG9uZW50LFxuICBQYWdlVHlwZSxcbiAgUm91dGluZ1NlcnZpY2UsXG4gIFdpbmRvd1JlZixcbn0gZnJvbSAnQHNwYXJ0YWN1cy9jb3JlJztcbmltcG9ydCB7IE9ic2VydmFibGUsIG9mLCBTdWJzY3JpcHRpb24gfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGZpbHRlciwgbWFwLCBzd2l0Y2hNYXAsIHRhcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IElDT05fVFlQRSB9IGZyb20gJy4uLy4uLy4uL2Ntcy1jb21wb25lbnRzL21pc2MvaWNvbi9pbmRleCc7XG5pbXBvcnQgeyBDbXNDb21wb25lbnREYXRhIH0gZnJvbSAnLi4vLi4vLi4vY21zLXN0cnVjdHVyZS9wYWdlL21vZGVsL2Ntcy1jb21wb25lbnQtZGF0YSc7XG5pbXBvcnQgeyBTZWFyY2hCb3hDb21wb25lbnRTZXJ2aWNlIH0gZnJvbSAnLi9zZWFyY2gtYm94LWNvbXBvbmVudC5zZXJ2aWNlJztcbmltcG9ydCB7XG4gIFNlYXJjaEJveFByb2R1Y3RTZWxlY3RlZEV2ZW50LFxuICBTZWFyY2hCb3hTdWdnZXN0aW9uU2VsZWN0ZWRFdmVudCxcbn0gZnJvbSAnLi9zZWFyY2gtYm94LmV2ZW50cyc7XG5pbXBvcnQgeyBTZWFyY2hCb3hDb25maWcsIFNlYXJjaFJlc3VsdHMgfSBmcm9tICcuL3NlYXJjaC1ib3gubW9kZWwnO1xuXG5jb25zdCBERUZBVUxUX1NFQVJDSF9CT1hfQ09ORklHOiBTZWFyY2hCb3hDb25maWcgPSB7XG4gIG1pbkNoYXJhY3RlcnNCZWZvcmVSZXF1ZXN0OiAxLFxuICBkaXNwbGF5UHJvZHVjdHM6IHRydWUsXG4gIGRpc3BsYXlTdWdnZXN0aW9uczogdHJ1ZSxcbiAgbWF4UHJvZHVjdHM6IDUsXG4gIG1heFN1Z2dlc3Rpb25zOiA1LFxuICBkaXNwbGF5UHJvZHVjdEltYWdlczogdHJ1ZSxcbn07XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2N4LXNlYXJjaGJveCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9zZWFyY2gtYm94LmNvbXBvbmVudC5odG1sJyxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG59KVxuZXhwb3J0IGNsYXNzIFNlYXJjaEJveENvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95IHtcbiAgQElucHV0KCkgY29uZmlnOiBTZWFyY2hCb3hDb25maWc7XG5cbiAgLyoqXG4gICAqIFNldHMgdGhlIHNlYXJjaCBib3ggaW5wdXQgZmllbGRcbiAgICovXG4gIEBJbnB1dCgncXVlcnlUZXh0JylcbiAgc2V0IHF1ZXJ5VGV4dCh2YWx1ZTogc3RyaW5nKSB7XG4gICAgaWYgKHZhbHVlKSB7XG4gICAgICB0aGlzLnNlYXJjaCh2YWx1ZSk7XG4gICAgfVxuICB9XG5cbiAgaWNvblR5cGVzID0gSUNPTl9UWVBFO1xuXG4gIC8qKlxuICAgKiBJbiBzb21lIG9jY2FzaW9ucyB3ZSBuZWVkIHRvIGlnbm9yZSB0aGUgY2xvc2UgZXZlbnQsXG4gICAqIGZvciBleGFtcGxlIHdoZW4gd2UgY2xpY2sgaW5zaWRlIHRoZSBzZWFyY2ggcmVzdWx0IHNlY3Rpb24uXG4gICAqL1xuICBwcml2YXRlIGlnbm9yZUNsb3NlRXZlbnQgPSBmYWxzZTtcbiAgY2hvc2VuV29yZCA9ICcnO1xuICBwdWJsaWMgc3Vic2NyaXB0aW9uOiBTdWJzY3JpcHRpb247XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJvdGVjdGVkIHNlYXJjaEJveENvbXBvbmVudFNlcnZpY2U6IFNlYXJjaEJveENvbXBvbmVudFNlcnZpY2UsXG4gICAgQE9wdGlvbmFsKClcbiAgICBwcm90ZWN0ZWQgY29tcG9uZW50RGF0YTogQ21zQ29tcG9uZW50RGF0YTxDbXNTZWFyY2hCb3hDb21wb25lbnQ+LFxuICAgIHByb3RlY3RlZCB3aW5SZWY6IFdpbmRvd1JlZixcbiAgICBwcm90ZWN0ZWQgcm91dGluZ1NlcnZpY2U6IFJvdXRpbmdTZXJ2aWNlXG4gICkge31cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgU2VhcmNoQm94IGNvbmZpZ3VyYXRpb24uIFRoZSBjb25maWd1cmF0aW9uIGlzIGRyaXZlbiBieSBtdWx0aXBsZVxuICAgKiBsYXllcnM6IGRlZmF1bHQgY29uZmlndXJhdGlvbiwgKG9wdGlvbmFsKSBiYWNrZW5kIGNvbmZpZ3VyYXRpb24gYW5kIChvcHRpb25hbClcbiAgICogaW5wdXQgY29uZmlndXJhdGlvbi5cbiAgICovXG4gIHByb3RlY3RlZCBjb25maWckOiBPYnNlcnZhYmxlPFNlYXJjaEJveENvbmZpZz4gPSAoXG4gICAgdGhpcy5jb21wb25lbnREYXRhPy5kYXRhJCB8fCBvZih7fSBhcyBhbnkpXG4gICkucGlwZShcbiAgICBtYXAoKGNvbmZpZykgPT4ge1xuICAgICAgY29uc3QgaXNCb29sID0gKG9iajogU2VhcmNoQm94Q29uZmlnLCBwcm9wOiBzdHJpbmcpOiBib29sZWFuID0+XG4gICAgICAgIG9iaj8uW3Byb3BdICE9PSAnZmFsc2UnICYmIG9iaj8uW3Byb3BdICE9PSBmYWxzZTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4uREVGQVVMVF9TRUFSQ0hfQk9YX0NPTkZJRyxcbiAgICAgICAgLi4uY29uZmlnLFxuICAgICAgICBkaXNwbGF5UHJvZHVjdHM6IGlzQm9vbChjb25maWcsICdkaXNwbGF5UHJvZHVjdHMnKSxcbiAgICAgICAgZGlzcGxheVByb2R1Y3RJbWFnZXM6IGlzQm9vbChjb25maWcsICdkaXNwbGF5UHJvZHVjdEltYWdlcycpLFxuICAgICAgICBkaXNwbGF5U3VnZ2VzdGlvbnM6IGlzQm9vbChjb25maWcsICdkaXNwbGF5U3VnZ2VzdGlvbnMnKSxcbiAgICAgICAgLy8gd2UncmUgbWVyZ2luZyB0aGUgKG9wdGlvbmFsKSBpbnB1dCBvZiB0aGlzIGNvbXBvbmVudCwgYnV0IHdyaXRlIHRoZSBtZXJnZWRcbiAgICAgICAgLy8gcmVzdWx0IGJhY2sgdG8gdGhlIGlucHV0IHByb3BlcnR5LCBhcyB0aGUgdmlldyBsb2dpYyBkZXBlbmRzIG9uIGl0LlxuICAgICAgICAuLi50aGlzLmNvbmZpZyxcbiAgICAgIH07XG4gICAgfSksXG4gICAgdGFwKChjb25maWcpID0+ICh0aGlzLmNvbmZpZyA9IGNvbmZpZykpXG4gICk7XG5cbiAgcmVzdWx0cyQ6IE9ic2VydmFibGU8U2VhcmNoUmVzdWx0cz4gPSB0aGlzLmNvbmZpZyQucGlwZShcbiAgICBzd2l0Y2hNYXAoKGNvbmZpZykgPT4gdGhpcy5zZWFyY2hCb3hDb21wb25lbnRTZXJ2aWNlLmdldFJlc3VsdHMoY29uZmlnKSlcbiAgKTtcblxuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLnN1YnNjcmlwdGlvbiA9IHRoaXMucm91dGluZ1NlcnZpY2VcbiAgICAgIC5nZXRSb3V0ZXJTdGF0ZSgpXG4gICAgICAucGlwZShmaWx0ZXIoKGRhdGEpID0+ICFkYXRhLm5leHRTdGF0ZSkpXG4gICAgICAuc3Vic2NyaWJlKChkYXRhKSA9PiB7XG4gICAgICAgIGlmIChcbiAgICAgICAgICAhKFxuICAgICAgICAgICAgZGF0YS5zdGF0ZS5jb250ZXh0Py5pZCA9PT0gJ3NlYXJjaCcgJiZcbiAgICAgICAgICAgIGRhdGEuc3RhdGUuY29udGV4dD8udHlwZSA9PT0gUGFnZVR5cGUuQ09OVEVOVF9QQUdFXG4gICAgICAgICAgKVxuICAgICAgICApXG4gICAgICAgICAgdGhpcy5jaG9zZW5Xb3JkID0gJyc7XG4gICAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbG9zZXMgdGhlIHNlYXJjaEJveCBhbmQgb3BlbnMgdGhlIHNlYXJjaCByZXN1bHQgcGFnZS5cbiAgICovXG4gIHNlYXJjaChxdWVyeTogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5zZWFyY2hCb3hDb21wb25lbnRTZXJ2aWNlLnNlYXJjaChxdWVyeSwgdGhpcy5jb25maWcpO1xuICAgIC8vIGZvcmNlIHRoZSBzZWFyY2hCb3ggdG8gb3BlblxuICAgIHRoaXMub3BlbigpO1xuICB9XG5cbiAgLyoqXG4gICAqIE9wZW5zIHRoZSB0eXBlLWFoZWFkIHNlYXJjaEJveFxuICAgKi9cbiAgb3BlbigpOiB2b2lkIHtcbiAgICB0aGlzLnNlYXJjaEJveENvbXBvbmVudFNlcnZpY2UudG9nZ2xlQm9keUNsYXNzKCdzZWFyY2hib3gtaXMtYWN0aXZlJywgdHJ1ZSk7XG4gIH1cblxuICAvKipcbiAgICogRGlzcGF0Y2ggVUkgZXZlbnRzIGZvciBTdWdnZXN0aW9uIHNlbGVjdGVkXG4gICAqXG4gICAqIEBwYXJhbSBldmVudERhdGEgdGhlIGRhdGEgZm9yIHRoZSBldmVudFxuICAgKi9cbiAgZGlzcGF0Y2hTdWdnZXN0aW9uRXZlbnQoZXZlbnREYXRhOiBTZWFyY2hCb3hTdWdnZXN0aW9uU2VsZWN0ZWRFdmVudCk6IHZvaWQge1xuICAgIHRoaXMuc2VhcmNoQm94Q29tcG9uZW50U2VydmljZS5kaXNwYXRjaFN1Z2dlc3Rpb25TZWxlY3RlZEV2ZW50KGV2ZW50RGF0YSk7XG4gIH1cblxuICAvKipcbiAgICogRGlzcGF0Y2ggVUkgZXZlbnRzIGZvciBQcm9kdWN0IHNlbGVjdGVkXG4gICAqXG4gICAqIEBwYXJhbSBldmVudERhdGEgdGhlIGRhdGEgZm9yIHRoZSBldmVudFxuICAgKi9cbiAgZGlzcGF0Y2hQcm9kdWN0RXZlbnQoZXZlbnREYXRhOiBTZWFyY2hCb3hQcm9kdWN0U2VsZWN0ZWRFdmVudCk6IHZvaWQge1xuICAgIHRoaXMuc2VhcmNoQm94Q29tcG9uZW50U2VydmljZS5kaXNwYXRjaFByb2R1Y3RTZWxlY3RlZEV2ZW50KGV2ZW50RGF0YSk7XG4gIH1cblxuICAvKipcbiAgICogQ2xvc2VzIHRoZSB0eXBlLWFoZWFkIHNlYXJjaEJveC5cbiAgICovXG4gIGNsb3NlKGV2ZW50OiBVSUV2ZW50LCBmb3JjZT86IGJvb2xlYW4pOiB2b2lkIHtcbiAgICAvLyBVc2UgdGltZW91dCB0byBkZXRlY3QgY2hhbmdlc1xuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgaWYgKCghdGhpcy5pZ25vcmVDbG9zZUV2ZW50ICYmICF0aGlzLmlzU2VhcmNoQm94Rm9jdXNlZCgpKSB8fCBmb3JjZSkge1xuICAgICAgICB0aGlzLmJsdXJTZWFyY2hCb3goZXZlbnQpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIGJsdXJTZWFyY2hCb3goZXZlbnQ6IFVJRXZlbnQpOiB2b2lkIHtcbiAgICB0aGlzLnNlYXJjaEJveENvbXBvbmVudFNlcnZpY2UudG9nZ2xlQm9keUNsYXNzKFxuICAgICAgJ3NlYXJjaGJveC1pcy1hY3RpdmUnLFxuICAgICAgZmFsc2VcbiAgICApO1xuICAgIGlmIChldmVudCAmJiBldmVudC50YXJnZXQpIHtcbiAgICAgICg8SFRNTEVsZW1lbnQ+ZXZlbnQudGFyZ2V0KS5ibHVyKCk7XG4gICAgfVxuICB9XG5cbiAgLy8gQ2hlY2sgaWYgZm9jdXMgaXMgb24gc2VhcmNoYm94IG9yIHJlc3VsdCBsaXN0IGVsZW1lbnRzXG4gIHByaXZhdGUgaXNTZWFyY2hCb3hGb2N1c2VkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICB0aGlzLmdldFJlc3VsdEVsZW1lbnRzKCkuaW5jbHVkZXModGhpcy5nZXRGb2N1c2VkRWxlbWVudCgpKSB8fFxuICAgICAgdGhpcy53aW5SZWYuZG9jdW1lbnQucXVlcnlTZWxlY3RvcignaW5wdXRbYXJpYS1sYWJlbD1cIlNlYXJjaFwiXScpID09PVxuICAgICAgICB0aGlzLmdldEZvY3VzZWRFbGVtZW50KClcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEVzcGVjaWFsbHkgaW4gbW9iaWxlIHdlIGRvIG5vdCB3YW50IHRoZSBzZWFyY2ggaWNvblxuICAgKiB0byBmb2N1cyB0aGUgaW5wdXQgYWdhaW4gd2hlbiBpdCdzIGFscmVhZHkgb3Blbi5cbiAgICogKi9cbiAgYXZvaWRSZW9wZW4oZXZlbnQ6IFVJRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5zZWFyY2hCb3hDb21wb25lbnRTZXJ2aWNlLmhhc0JvZHlDbGFzcygnc2VhcmNoYm94LWlzLWFjdGl2ZScpKSB7XG4gICAgICB0aGlzLmNsb3NlKGV2ZW50KTtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgfVxuICB9XG5cbiAgLy8gUmV0dXJuIHJlc3VsdCBsaXN0IGFzIEhUTUxFbGVtZW50IGFycmF5XG4gIHByaXZhdGUgZ2V0UmVzdWx0RWxlbWVudHMoKTogSFRNTEVsZW1lbnRbXSB7XG4gICAgcmV0dXJuIEFycmF5LmZyb20oXG4gICAgICB0aGlzLndpblJlZi5kb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKFxuICAgICAgICAnLnByb2R1Y3RzID4gbGkgYSwgLnN1Z2dlc3Rpb25zID4gbGkgYSdcbiAgICAgIClcbiAgICApO1xuICB9XG5cbiAgLy8gUmV0dXJuIGZvY3VzZWQgZWxlbWVudCBhcyBIVE1MRWxlbWVudFxuICBwcml2YXRlIGdldEZvY3VzZWRFbGVtZW50KCk6IEhUTUxFbGVtZW50IHtcbiAgICByZXR1cm4gPEhUTUxFbGVtZW50PnRoaXMud2luUmVmLmRvY3VtZW50LmFjdGl2ZUVsZW1lbnQ7XG4gIH1cblxuICB1cGRhdGVDaG9zZW5Xb3JkKGNob3NlbldvcmQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuY2hvc2VuV29yZCA9IGNob3NlbldvcmQ7XG4gIH1cblxuICBwcml2YXRlIGdldEZvY3VzZWRJbmRleCgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLmdldFJlc3VsdEVsZW1lbnRzKCkuaW5kZXhPZih0aGlzLmdldEZvY3VzZWRFbGVtZW50KCkpO1xuICB9XG5cbiAgLy8gRm9jdXMgb24gcHJldmlvdXMgaXRlbSBpbiByZXN1bHRzIGxpc3RcbiAgZm9jdXNQcmV2aW91c0NoaWxkKGV2ZW50OiBVSUV2ZW50KSB7XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTsgLy8gTmVnYXRlIG5vcm1hbCBrZXlzY3JvbGxcbiAgICBjb25zdCBbcmVzdWx0cywgZm9jdXNlZEluZGV4XSA9IFtcbiAgICAgIHRoaXMuZ2V0UmVzdWx0RWxlbWVudHMoKSxcbiAgICAgIHRoaXMuZ2V0Rm9jdXNlZEluZGV4KCksXG4gICAgXTtcbiAgICAvLyBGb2N1cyBvbiBsYXN0IGluZGV4IG1vdmluZyB0byBmaXJzdFxuICAgIGlmIChyZXN1bHRzLmxlbmd0aCkge1xuICAgICAgaWYgKGZvY3VzZWRJbmRleCA8IDEpIHtcbiAgICAgICAgcmVzdWx0c1tyZXN1bHRzLmxlbmd0aCAtIDFdLmZvY3VzKCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXN1bHRzW2ZvY3VzZWRJbmRleCAtIDFdLmZvY3VzKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8gRm9jdXMgb24gbmV4dCBpdGVtIGluIHJlc3VsdHMgbGlzdFxuICBmb2N1c05leHRDaGlsZChldmVudDogVUlFdmVudCkge1xuICAgIHRoaXMub3BlbigpO1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7IC8vIE5lZ2F0ZSBub3JtYWwga2V5c2Nyb2xsXG4gICAgY29uc3QgW3Jlc3VsdHMsIGZvY3VzZWRJbmRleF0gPSBbXG4gICAgICB0aGlzLmdldFJlc3VsdEVsZW1lbnRzKCksXG4gICAgICB0aGlzLmdldEZvY3VzZWRJbmRleCgpLFxuICAgIF07XG4gICAgLy8gRm9jdXMgb24gZmlyc3QgaW5kZXggbW92aW5nIHRvIGxhc3RcbiAgICBpZiAocmVzdWx0cy5sZW5ndGgpIHtcbiAgICAgIGlmIChmb2N1c2VkSW5kZXggPj0gcmVzdWx0cy5sZW5ndGggLSAxKSB7XG4gICAgICAgIHJlc3VsdHNbMF0uZm9jdXMoKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdHNbZm9jdXNlZEluZGV4ICsgMV0uZm9jdXMoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogT3BlbnMgdGhlIFBMUCB3aXRoIHRoZSBnaXZlbiBxdWVyeS5cbiAgICpcbiAgICogVE9ETzogaWYgdGhlcmUncyBhIHNpbmdsZSBwcm9kdWN0IG1hdGNoLCB3ZSBjb3VsZCBvcGVuIHRoZSBQRFAuXG4gICAqL1xuICBsYXVuY2hTZWFyY2hSZXN1bHQoZXZlbnQ6IFVJRXZlbnQsIHF1ZXJ5OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBpZiAoIXF1ZXJ5IHx8IHF1ZXJ5LnRyaW0oKS5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5jbG9zZShldmVudCk7XG4gICAgdGhpcy5zZWFyY2hCb3hDb21wb25lbnRTZXJ2aWNlLmxhdW5jaFNlYXJjaFBhZ2UocXVlcnkpO1xuICB9XG5cbiAgLyoqXG4gICAqIERpc2FibGVzIGNsb3NpbmcgdGhlIHNlYXJjaCByZXN1bHQgbGlzdC5cbiAgICovXG4gIGRpc2FibGVDbG9zZSgpOiB2b2lkIHtcbiAgICB0aGlzLmlnbm9yZUNsb3NlRXZlbnQgPSB0cnVlO1xuICB9XG5cbiAgcHJldmVudERlZmF1bHQoZXY6IFVJRXZlbnQpOiB2b2lkIHtcbiAgICBldi5wcmV2ZW50RGVmYXVsdCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFycyB0aGUgc2VhcmNoIGJveCBpbnB1dCBmaWVsZFxuICAgKi9cbiAgY2xlYXIoZWw6IEhUTUxJbnB1dEVsZW1lbnQpOiB2b2lkIHtcbiAgICB0aGlzLmRpc2FibGVDbG9zZSgpO1xuICAgIGVsLnZhbHVlID0gJyc7XG4gICAgdGhpcy5zZWFyY2hCb3hDb21wb25lbnRTZXJ2aWNlLmNsZWFyUmVzdWx0cygpO1xuXG4gICAgLy8gVXNlIFRpbWVvdXQgdG8gcnVuIGFmdGVyIGJsdXIgZXZlbnQgdG8gcHJldmVudCB0aGUgc2VhcmNoYm94IGZyb20gY2xvc2luZyBvbiBtb2JpbGVcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIC8vIFJldGFpbiBmb2N1cyBvbiBpbnB1dCBsb3N0IGJ5IGNsaWNraW5nIG9uIGljb25cbiAgICAgIGVsLmZvY3VzKCk7XG4gICAgICB0aGlzLmlnbm9yZUNsb3NlRXZlbnQgPSBmYWxzZTtcbiAgICB9KTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuc3Vic2NyaXB0aW9uPy51bnN1YnNjcmliZSgpO1xuICB9XG59XG4iLCI8bGFiZWwgY2xhc3M9XCJzZWFyY2hib3hcIiBbY2xhc3MuZGlydHldPVwiISFzZWFyY2hJbnB1dC52YWx1ZVwiPlxuICA8aW5wdXRcbiAgICAjc2VhcmNoSW5wdXRcbiAgICBbcGxhY2Vob2xkZXJdPVwiJ3NlYXJjaEJveC5wbGFjZWhvbGRlcicgfCBjeFRyYW5zbGF0ZVwiXG4gICAgYXV0b2NvbXBsZXRlPVwib2ZmXCJcbiAgICBhcmlhLWRlc2NyaWJlZGJ5PVwiaW5pdGlhbERlc2NyaXB0aW9uXCJcbiAgICBhcmlhLWNvbnRyb2xzPVwicmVzdWx0c1wiXG4gICAgW2F0dHIuYXJpYS1sYWJlbF09XCInY29tbW9uLnNlYXJjaCcgfCBjeFRyYW5zbGF0ZVwiXG4gICAgKGZvY3VzKT1cIm9wZW4oKVwiXG4gICAgKGNsaWNrKT1cIm9wZW4oKVwiXG4gICAgKGlucHV0KT1cInNlYXJjaChzZWFyY2hJbnB1dC52YWx1ZSlcIlxuICAgIChibHVyKT1cImNsb3NlKCRldmVudClcIlxuICAgIChrZXlkb3duLmVzY2FwZSk9XCJjbG9zZSgkZXZlbnQpXCJcbiAgICAoa2V5ZG93bi5lbnRlcik9XCJcbiAgICAgIGNsb3NlKCRldmVudCwgdHJ1ZSk7XG4gICAgICBsYXVuY2hTZWFyY2hSZXN1bHQoJGV2ZW50LCBzZWFyY2hJbnB1dC52YWx1ZSk7XG4gICAgICB1cGRhdGVDaG9zZW5Xb3JkKHNlYXJjaElucHV0LnZhbHVlKVxuICAgIFwiXG4gICAgKGtleWRvd24uYXJyb3d1cCk9XCJmb2N1c1ByZXZpb3VzQ2hpbGQoJGV2ZW50KVwiXG4gICAgKGtleWRvd24uYXJyb3dkb3duKT1cImZvY3VzTmV4dENoaWxkKCRldmVudClcIlxuICAgIHZhbHVlPVwie3sgY2hvc2VuV29yZCB9fVwiXG4gIC8+XG5cbiAgPGJ1dHRvblxuICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ2NvbW1vbi5yZXNldCcgfCBjeFRyYW5zbGF0ZVwiXG4gICAgKG1vdXNlZG93bik9XCJjbGVhcihzZWFyY2hJbnB1dClcIlxuICAgIChrZXlkb3duLmVudGVyKT1cImNsZWFyKHNlYXJjaElucHV0KVwiXG4gICAgY2xhc3M9XCJyZXNldFwiXG4gID5cbiAgICA8Y3gtaWNvbiBbdHlwZV09XCJpY29uVHlwZXMuUkVTRVRcIj48L2N4LWljb24+XG4gIDwvYnV0dG9uPlxuXG4gIDxkaXYgcm9sZT1cInByZXNlbnRhdGlvblwiIGNsYXNzPVwic2VhcmNoLWljb25cIj5cbiAgICA8Y3gtaWNvbiBbdHlwZV09XCJpY29uVHlwZXMuU0VBUkNIXCI+PC9jeC1pY29uPlxuICA8L2Rpdj5cblxuICA8YnV0dG9uXG4gICAgW2F0dHIuYXJpYS1sYWJlbF09XCInY29tbW9uLnNlYXJjaCcgfCBjeFRyYW5zbGF0ZVwiXG4gICAgY2xhc3M9XCJzZWFyY2hcIlxuICAgIChjbGljayk9XCJvcGVuKClcIlxuICA+XG4gICAgPGN4LWljb24gW3R5cGVdPVwiaWNvblR5cGVzLlNFQVJDSFwiPjwvY3gtaWNvbj5cbiAgPC9idXR0b24+XG48L2xhYmVsPlxuXG48ZGl2XG4gICpuZ0lmPVwicmVzdWx0cyQgfCBhc3luYyBhcyByZXN1bHRcIlxuICBjbGFzcz1cInJlc3VsdHNcIlxuICBpZD1cInJlc3VsdHNcIlxuICAoY2xpY2spPVwiY2xvc2UoJGV2ZW50LCB0cnVlKVwiXG4gIHJvbGU9XCJsaXN0Ym94XCJcbj5cbiAgPGRpdlxuICAgICpuZ0lmPVwicmVzdWx0Lm1lc3NhZ2VcIlxuICAgIGNsYXNzPVwibWVzc2FnZVwiXG4gICAgW2lubmVySFRNTF09XCJyZXN1bHQubWVzc2FnZVwiXG4gID48L2Rpdj5cblxuICA8dWxcbiAgICBjbGFzcz1cInN1Z2dlc3Rpb25zXCJcbiAgICBhdHRyLmFyaWEtbGFiZWw9XCJ7eyAnc2VhcmNoQm94LmFyaWFMYWJlbFN1Z2dlc3Rpb25zJyB8IGN4VHJhbnNsYXRlIH19XCJcbiAgICB0YWJpbmRleD1cIjBcIlxuICA+XG4gICAgPGxpICpuZ0Zvcj1cImxldCBzdWdnZXN0aW9uIG9mIHJlc3VsdC5zdWdnZXN0aW9uc1wiPlxuICAgICAgPGFcbiAgICAgICAgW2lubmVySFRNTF09XCJzdWdnZXN0aW9uIHwgY3hIaWdobGlnaHQ6IHNlYXJjaElucHV0LnZhbHVlXCJcbiAgICAgICAgW3JvdXRlckxpbmtdPVwiXG4gICAgICAgICAge1xuICAgICAgICAgICAgY3hSb3V0ZTogJ3NlYXJjaCcsXG4gICAgICAgICAgICBwYXJhbXM6IHsgcXVlcnk6IHN1Z2dlc3Rpb24gfVxuICAgICAgICAgIH0gfCBjeFVybFxuICAgICAgICBcIlxuICAgICAgICAoa2V5ZG93bi5hcnJvd3VwKT1cImZvY3VzUHJldmlvdXNDaGlsZCgkZXZlbnQpXCJcbiAgICAgICAgKGtleWRvd24uYXJyb3dkb3duKT1cImZvY3VzTmV4dENoaWxkKCRldmVudClcIlxuICAgICAgICAoa2V5ZG93bi5lbnRlcik9XCJjbG9zZSgkZXZlbnQsIHRydWUpXCJcbiAgICAgICAgKGtleWRvd24uZXNjYXBlKT1cImNsb3NlKCRldmVudCwgdHJ1ZSlcIlxuICAgICAgICAoYmx1cik9XCJjbG9zZSgkZXZlbnQpXCJcbiAgICAgICAgKG1vdXNlZG93bik9XCJwcmV2ZW50RGVmYXVsdCgkZXZlbnQpXCJcbiAgICAgICAgKGNsaWNrKT1cIlxuICAgICAgICAgIGRpc3BhdGNoU3VnZ2VzdGlvbkV2ZW50KHtcbiAgICAgICAgICAgIGZyZWVUZXh0OiBzZWFyY2hJbnB1dC52YWx1ZSxcbiAgICAgICAgICAgIHNlbGVjdGVkU3VnZ2VzdGlvbjogc3VnZ2VzdGlvbixcbiAgICAgICAgICAgIHNlYXJjaFN1Z2dlc3Rpb25zOiByZXN1bHQuc3VnZ2VzdGlvbnNcbiAgICAgICAgICB9KTtcbiAgICAgICAgICB1cGRhdGVDaG9zZW5Xb3JkKHN1Z2dlc3Rpb24pXG4gICAgICAgIFwiXG4gICAgICA+XG4gICAgICA8L2E+XG4gICAgPC9saT5cbiAgPC91bD5cblxuICA8dWxcbiAgICBjbGFzcz1cInByb2R1Y3RzXCJcbiAgICAqbmdJZj1cInJlc3VsdC5wcm9kdWN0c1wiXG4gICAgYXR0ci5hcmlhLWxhYmVsPVwie3sgJ3NlYXJjaEJveC5hcmlhTGFiZWxQcm9kdWN0cycgfCBjeFRyYW5zbGF0ZSB9fVwiXG4gICAgdGFiaW5kZXg9XCIwXCJcbiAgPlxuICAgIDxsaSAqbmdGb3I9XCJsZXQgcHJvZHVjdCBvZiByZXN1bHQucHJvZHVjdHNcIj5cbiAgICAgIDxhXG4gICAgICAgIFtyb3V0ZXJMaW5rXT1cIlxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGN4Um91dGU6ICdwcm9kdWN0JyxcbiAgICAgICAgICAgIHBhcmFtczogcHJvZHVjdFxuICAgICAgICAgIH0gfCBjeFVybFxuICAgICAgICBcIlxuICAgICAgICBbY2xhc3MuaGFzLW1lZGlhXT1cImNvbmZpZy5kaXNwbGF5UHJvZHVjdEltYWdlc1wiXG4gICAgICAgIChrZXlkb3duLmFycm93dXApPVwiZm9jdXNQcmV2aW91c0NoaWxkKCRldmVudClcIlxuICAgICAgICAoa2V5ZG93bi5hcnJvd2Rvd24pPVwiZm9jdXNOZXh0Q2hpbGQoJGV2ZW50KVwiXG4gICAgICAgIChrZXlkb3duLmVudGVyKT1cImNsb3NlKCRldmVudCwgdHJ1ZSlcIlxuICAgICAgICAoa2V5ZG93bi5lc2NhcGUpPVwiY2xvc2UoJGV2ZW50LCB0cnVlKVwiXG4gICAgICAgIChibHVyKT1cImNsb3NlKCRldmVudClcIlxuICAgICAgICAobW91c2Vkb3duKT1cInByZXZlbnREZWZhdWx0KCRldmVudClcIlxuICAgICAgICAoY2xpY2spPVwiXG4gICAgICAgICAgZGlzcGF0Y2hQcm9kdWN0RXZlbnQoe1xuICAgICAgICAgICAgZnJlZVRleHQ6IHNlYXJjaElucHV0LnZhbHVlLFxuICAgICAgICAgICAgcHJvZHVjdENvZGU6IHByb2R1Y3QuY29kZVxuICAgICAgICAgIH0pXG4gICAgICAgIFwiXG4gICAgICA+XG4gICAgICAgIDxjeC1tZWRpYVxuICAgICAgICAgICpuZ0lmPVwiY29uZmlnLmRpc3BsYXlQcm9kdWN0SW1hZ2VzXCJcbiAgICAgICAgICBbY29udGFpbmVyXT1cInByb2R1Y3QuaW1hZ2VzPy5QUklNQVJZXCJcbiAgICAgICAgICBmb3JtYXQ9XCJ0aHVtYm5haWxcIlxuICAgICAgICAgIHJvbGU9XCJwcmVzZW50YXRpb25cIlxuICAgICAgICA+PC9jeC1tZWRpYT5cbiAgICAgICAgPGRpdiBjbGFzcz1cIm5hbWVcIiBbaW5uZXJIVE1MXT1cInByb2R1Y3QubmFtZUh0bWxcIj48L2Rpdj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJwcmljZVwiPnt7IHByb2R1Y3QucHJpY2U/LmZvcm1hdHRlZFZhbHVlIH19PC9zcGFuPlxuICAgICAgPC9hPlxuICAgIDwvbGk+XG4gIDwvdWw+XG4gIDxzcGFuIGlkPVwiaW5pdGlhbERlc2NyaXB0aW9uXCIgY2xhc3M9XCJjeC12aXN1YWxseS1oaWRkZW5cIj5cbiAgICB7eyAnc2VhcmNoQm94LmluaXRpYWxEZXNjcmlwdGlvbicgfCBjeFRyYW5zbGF0ZSB9fVxuICA8L3NwYW4+XG4gIDxkaXZcbiAgICAqbmdJZj1cInJlc3VsdC5zdWdnZXN0aW9ucz8ubGVuZ3RoIHx8IHJlc3VsdC5wcm9kdWN0cz8ubGVuZ3RoXCJcbiAgICBhcmlhLWxpdmU9XCJhc3NlcnRpdmVcIlxuICAgIGNsYXNzPVwiY3gtdmlzdWFsbHktaGlkZGVuXCJcbiAgPlxuICAgIHt7XG4gICAgICAnc2VhcmNoQm94LnN1Z2dlc3Rpb25zUmVzdWx0J1xuICAgICAgICB8IGN4VHJhbnNsYXRlOiB7IGNvdW50OiByZXN1bHQuc3VnZ2VzdGlvbnM/Lmxlbmd0aCB9XG4gICAgfX1cbiAgICB7e1xuICAgICAgJ3NlYXJjaEJveC5wcm9kdWN0c1Jlc3VsdCdcbiAgICAgICAgfCBjeFRyYW5zbGF0ZTogeyBjb3VudDogcmVzdWx0LnByb2R1Y3RzPy5sZW5ndGggfVxuICAgIH19XG4gICAge3sgJ3NlYXJjaEJveC5pbml0aWFsRGVzY3JpcHRpb24nIHwgY3hUcmFuc2xhdGUgfX1cbiAgPC9kaXY+XG48L2Rpdj5cbiJdfQ==