@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
JavaScript
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==