UNPKG

govuk-angular

Version:

Angular components port of govuk-frontend nunjucks macros.

179 lines 39.1 kB
import { Component, ViewChild, Input, Output, EventEmitter, ChangeDetectionStrategy, } from '@angular/core'; import { of, fromEvent, merge } from 'rxjs'; import { filter, map, tap, distinctUntilChanged, debounceTime, delay, withLatestFrom, startWith, mapTo, } from 'rxjs/operators'; import * as i0 from "@angular/core"; import * as i1 from "../label/label.component"; import * as i2 from "../hint/hint.component"; import * as i3 from "../error/error.component"; import * as i4 from "../govuk-error-line.directive"; import * as i5 from "@angular/common"; import * as i6 from "@angular/forms"; export class FilteredSelectComponent { constructor(cdr, el) { this.cdr = cdr; this.el = el; /** Label = {text: "", classes: "", isPageHeading: false} */ this.label = { text: '', classes: '', isPageHeading: false }; /** Hint text */ this.hint = { text: '', classes: '' }; /** Error message to show */ this.errorMessage = { text: '', classes: '' }; // boolean to declare whether we grouped or not this.grouped = false; // styling input variables all with a preset default this.lines = 5; // the background colour needs to be hardcoded so that it isn't transperant this.backgroundColor = '#fff'; // likewise with border needs hard coding this.borderStyle = '1px solid #999'; // produce a single chosen option as output this.chosenOption = new EventEmitter(); } ngAfterViewInit() { // use the @ViewChild obatined ElementRef to get the controls this.selectBox = this.selectBoxElementRef.nativeElement; this.filterInput = this.filterInputElementRef.nativeElement; this.fakeInput = this.fakeInputElementRef.nativeElement; // the "typeahead" style observable triggered by keyup on the filterInput control this.changeFilterText = fromEvent(this.filterInput, 'keyup').pipe( // if the down arrow is hit then give the focus to the selectBox control tap((e) => e.key === 'ArrowDown' ? this.selectBox.focus() : null), // ignore enter key and down arrow key from here onwards // (enter key dealth with elsewhere) filter((e) => e.key !== 'Enter' && e.key !== 'ArrowDown'), // prevent to rapid a change debounceTime(100), // only fire if text is different distinctUntilChanged(), // the payloiad map(() => this.filterInput.value), // kick off with an empty string startWith('')); // when user focuses on the fakeInput control, give the focus to the filterInput this.fakeInputFocus = merge(fromEvent(this.fakeInput, 'focus'), fromEvent(this.fakeInput, 'keyup'), fromEvent(this.fakeInput, 'click')).pipe(mapTo(true), // needs the delay so that the dropdown div has appeared otherwise doesn't work tap(() => setTimeout(() => this.filterInput.focus(), 20))); // observable that returns true when selectBox gets focus and false when it loses it (blur) this.selectFocusOrBlur = merge(fromEvent(this.selectBox, 'blur').pipe(mapTo(false)), fromEvent(this.selectBox, 'focus').pipe(mapTo(true))).pipe(startWith(false)); // filter input blur event and the select component receives focus // this observable will fire every time the filterInput loses focus // true = selectBox got focus, false = it didn't this.filterInputBlurFocusSelect = fromEvent(this.filterInput, 'blur').pipe( // mapTo(true), // delay so that... delay(10), // ... the selectFocusOrBlur observable can be used // to decide if _the focus has left the filter but not gone to the select_ withLatestFrom(this.selectFocusOrBlur), // spit out the most recent returned boolean from this.selectFocusOrBlur map(([, y]) => y)); // observable that produces the filtered array of options // which is used to fill the box (via ngFor* and async in the template) this.filteredOptions = merge(this.changeFilterText, of('')).pipe(map((filterString) => this.options // filter them ignorning case .filter((x) => // look for the string in both the text and the group strings // use the JS || shorthand to replace undefined group field with "" (x.text.toLowerCase() + (x.group || '').toLowerCase()).indexOf(filterString.toLowerCase()) > -1) // make them all "unselected" .map((y) => { y.selected = false; return y; }) .sort((a, b) => ((a.group || '') + a.text).localeCompare((b.group || '') + b.text))), map((x) => { // if the array is non-zero... if (x[0]) { // make the first one the selected one by default x[0].selected = true; } return x; })); // observable that produces the filtered AND GROUPED array of options // which is used to fill the second alternative (grouped) box this.filteredGroupedOptions = this.filteredOptions.pipe(map((x) => // get the list of groups by mapping the group field of the options // and then deleting duplicates using Array.from(new Set(_original_array_)) Array.from(new Set(x.map((y) => y.group))) // for each one of these groups create a groupedOptions object .map((group) => ({ // with the group name groupName: group, // and an appropriately selected (filtered) group of options options: x.filter((y) => y.group === group), })))); // observable to fire when the user chooses an option this.optionChosen = merge( // by clicking an item the selectBox or hitting enter in // either the filterInput or the selectBox fromEvent(this.selectBox, 'keyup').pipe(filter((e) => e.key === 'Enter')), fromEvent(this.selectBox, 'click'), fromEvent(this.filterInput, 'keyup').pipe(filter((e) => e.key === 'Enter'), mapTo(true))).pipe( // transform the output into an option type variable map(() => ({ text: this.selectBox.options[this.selectBox.selectedIndex].text, id: this.selectBox.options[this.selectBox.selectedIndex].value, // group: this.selectBox.options[this.selectBox.selectedIndex].label, })), // emit the chosenOption for the parent HTML control to read tap((x) => this.chosenOption.emit(x))); // observable to keep the text in the fakeInput up to date this.chosenText = this.optionChosen.pipe(map((x) => x.text)); // observable that determines whether the dropdown + inner divs are visible or not // true = should be visible, false = should be hidden // used by the angular template as [hidden]="!(active | async)" this.active = merge( // this observable will fire every time the filterInput loses focus // true = selectBox got focus, false = it didn't this.filterInputBlurFocusSelect, // this input fires true if the fakeInput gets focus this.fakeInputFocus, // this observable fires false if the selectBox loses focus merge(fromEvent(this.selectBox, 'blur')).pipe(map(() => false)), // this observable fires if the user chooses an option this.optionChosen.pipe(mapTo(false)) // this observable fires if the fakeInput text changes for whatever reason // this.chosenText.pipe(map(() => false)) ); // because we are creating all these observables in ngAfterViewInit // (cant do them in ngOnInit because the @ViewChild doesnt work) // we need to run change detection manually once this.cdr.detectChanges(); } } FilteredSelectComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: FilteredSelectComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); FilteredSelectComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.0.2", type: FilteredSelectComponent, selector: "govuk-filtered-select", inputs: { id: "id", label: "label", classes: "classes", hint: "hint", errorMessage: "errorMessage", options: "options", grouped: "grouped", lines: "lines", backgroundColor: "backgroundColor", borderStyle: "borderStyle" }, outputs: { chosenOption: "chosenOption" }, viewQueries: [{ propertyName: "filterInputElementRef", first: true, predicate: ["filterInput"], descendants: true }, { propertyName: "selectBoxElementRef", first: true, predicate: ["selectBox"], descendants: true }, { propertyName: "fakeInputElementRef", first: true, predicate: ["fakeInput"], descendants: true }], ngImport: i0, template: "<div [govukErrorLine]=\"errorMessage\" class=\"govuk-form-group\">\n\n <govuk-label id=\"{{id}}\" [label]=\"label\"></govuk-label>\n <govuk-hint id=\"{{id}}\" [hint]=\"hint\"> </govuk-hint>\n <govuk-error id=\"{{id}}\" [errorMessage]=\"errorMessage\"></govuk-error>\n\n <div>\n <select #fakeInput class=\"govuk-select \" [ngClass]=\"{'govuk-select--error' : errorMessage.text}\"\n>\n <option value=\"{{ chosenText | async }}\" class=\"govuk-input\" >{{ chosenText | async }}</option>\n </select>\n </div>\n\n <div class=\"dropdown\">\n\n <div class=\"inner\" [hidden]=\"(active | async) === false\"\n [ngStyle]=\"{ 'background-color': backgroundColor, border: borderStyle }\">\n\n <input #filterInput type=\"text\" class=\"govuk-input\" />\n\n <!-- this select is used if we are not grouping by option groups -->\n <select *ngIf=\"!grouped\" size=\"{{ lines }}\" #selectBox >\n\n <option *ngFor=\"let option of filteredOptions | async; index as i\"\n [value]=\"option.id\"\n [selected]=\"option.selected\" >{{ option.text }}</option>\n\n\n </select>\n\n <!-- this select is used if we are grouping by option groups -->\n <select *ngIf=\"grouped\" size=\"{{ lines }}\" #selectBox>\n\n <optgroup *ngFor=\"\n let groupedOptions of filteredGroupedOptions | async;\n index as i \" label=\"{{ groupedOptions.groupName }}\">\n\n <option *ngFor=\"let option of groupedOptions.options; index as i\" [value]=\"option.id\"\n [selected]=\"option.selected\"\n class=\"govuk-body\"\n\n >{{ option.text }}</option>\n\n </optgroup>\n\n </select>\n\n </div>\n\n </div>\n</div>\n", styles: ["input{margin-bottom:.5rem;width:max-content}.dropdown{width:100%;position:relative}.inner{z-index:1;padding:.5rem;position:absolute;top:0;left:0;right:0;box-shadow:0 2px 2px 2px #ccc}select{margin-bottom:0;width:100%;z-index:-1;font-family:GDS Transport,arial,sans-serif;font-style:normal;font-stretch:normal;font-size:1.2rem}.component{margin-bottom:1rem}\n"], components: [{ type: i1.GovUKLabelComponent, selector: "govuk-label", inputs: ["id", "label"] }, { type: i2.GovUKHintComponent, selector: "govuk-hint", inputs: ["id", "hint"] }, { type: i3.GovUKErrorComponent, selector: "govuk-error", inputs: ["id", "errorMessage"] }], directives: [{ type: i4.GovErrorLineDirective, selector: "[govukErrorLine]", inputs: ["govukErrorLine"] }, { type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i6.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i6.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "async": i5.AsyncPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: FilteredSelectComponent, decorators: [{ type: Component, args: [{ selector: 'govuk-filtered-select', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [govukErrorLine]=\"errorMessage\" class=\"govuk-form-group\">\n\n <govuk-label id=\"{{id}}\" [label]=\"label\"></govuk-label>\n <govuk-hint id=\"{{id}}\" [hint]=\"hint\"> </govuk-hint>\n <govuk-error id=\"{{id}}\" [errorMessage]=\"errorMessage\"></govuk-error>\n\n <div>\n <select #fakeInput class=\"govuk-select \" [ngClass]=\"{'govuk-select--error' : errorMessage.text}\"\n>\n <option value=\"{{ chosenText | async }}\" class=\"govuk-input\" >{{ chosenText | async }}</option>\n </select>\n </div>\n\n <div class=\"dropdown\">\n\n <div class=\"inner\" [hidden]=\"(active | async) === false\"\n [ngStyle]=\"{ 'background-color': backgroundColor, border: borderStyle }\">\n\n <input #filterInput type=\"text\" class=\"govuk-input\" />\n\n <!-- this select is used if we are not grouping by option groups -->\n <select *ngIf=\"!grouped\" size=\"{{ lines }}\" #selectBox >\n\n <option *ngFor=\"let option of filteredOptions | async; index as i\"\n [value]=\"option.id\"\n [selected]=\"option.selected\" >{{ option.text }}</option>\n\n\n </select>\n\n <!-- this select is used if we are grouping by option groups -->\n <select *ngIf=\"grouped\" size=\"{{ lines }}\" #selectBox>\n\n <optgroup *ngFor=\"\n let groupedOptions of filteredGroupedOptions | async;\n index as i \" label=\"{{ groupedOptions.groupName }}\">\n\n <option *ngFor=\"let option of groupedOptions.options; index as i\" [value]=\"option.id\"\n [selected]=\"option.selected\"\n class=\"govuk-body\"\n\n >{{ option.text }}</option>\n\n </optgroup>\n\n </select>\n\n </div>\n\n </div>\n</div>\n", styles: ["input{margin-bottom:.5rem;width:max-content}.dropdown{width:100%;position:relative}.inner{z-index:1;padding:.5rem;position:absolute;top:0;left:0;right:0;box-shadow:0 2px 2px 2px #ccc}select{margin-bottom:0;width:100%;z-index:-1;font-family:GDS Transport,arial,sans-serif;font-style:normal;font-stretch:normal;font-size:1.2rem}.component{margin-bottom:1rem}\n"] }] }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }]; }, propDecorators: { id: [{ type: Input }], label: [{ type: Input }], classes: [{ type: Input }], hint: [{ type: Input }], errorMessage: [{ type: Input }], filterInputElementRef: [{ type: ViewChild, args: ['filterInput'] }], selectBoxElementRef: [{ type: ViewChild, args: ['selectBox'] }], fakeInputElementRef: [{ type: ViewChild, args: ['fakeInput'] }], options: [{ type: Input }], grouped: [{ type: Input }], lines: [{ type: Input }], backgroundColor: [{ type: Input }], borderStyle: [{ type: Input }], chosenOption: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsdGVyZWQtc2VsZWN0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2dvdnVrLWFuZ3VsYXIvc3JjL2xpYi9zZWxlY3QtbGlzdC0yL2ZpbHRlcmVkLXNlbGVjdC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9nb3Z1ay1hbmd1bGFyL3NyYy9saWIvc2VsZWN0LWxpc3QtMi9maWx0ZXJlZC1zZWxlY3QuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUNMLFNBQVMsRUFFVCxTQUFTLEVBRVQsS0FBSyxFQUNMLE1BQU0sRUFDTixZQUFZLEVBQ1osdUJBQXVCLEdBS3hCLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBYyxFQUFFLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN4RCxPQUFPLEVBQ0wsTUFBTSxFQUNOLEdBQUcsRUFDSCxHQUFHLEVBQ0gsb0JBQW9CLEVBQ3BCLFlBQVksRUFDWixLQUFLLEVBQ0wsY0FBYyxFQUNkLFNBQVMsRUFDVCxLQUFLLEdBQ04sTUFBTSxnQkFBZ0IsQ0FBQzs7Ozs7Ozs7QUFvQnhCLE1BQU0sT0FBTyx1QkFBdUI7SUE2RGxDLFlBQW9CLEdBQXNCLEVBQVMsRUFBYztRQUE3QyxRQUFHLEdBQUgsR0FBRyxDQUFtQjtRQUFTLE9BQUUsR0FBRixFQUFFLENBQVk7UUF6RGpFLDREQUE0RDtRQUNuRCxVQUFLLEdBQVUsRUFBQyxJQUFJLEVBQUUsRUFBRSxFQUFFLE9BQU8sRUFBRSxFQUFFLEVBQUUsYUFBYSxFQUFFLEtBQUssRUFBQyxDQUFDO1FBS3RFLGdCQUFnQjtRQUNQLFNBQUksR0FBUyxFQUFDLElBQUksRUFBRSxFQUFFLEVBQUUsT0FBTyxFQUFFLEVBQUUsRUFBQyxDQUFDO1FBRTlDLDRCQUE0QjtRQUNuQixpQkFBWSxHQUFpQixFQUFFLElBQUksRUFB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LENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQy9ELHNEQUFzRDtRQUN0RCxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDcEMsMEVBQTBFO1FBQzFFLHlDQUF5QztTQUMxQyxDQUFDO1FBRUYsbUVBQW1FO1FBQ25FLGdFQUFnRTtRQUNoRSxnREFBZ0Q7UUFDaEQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUMzQixDQUFDOztvSEE1TlUsdUJBQXVCO3dHQUF2Qix1QkFBdUIsa29CQzlDcEMsZ3REQW1EQTsyRkRMYSx1QkFBdUI7a0JBTm5DLFNBQVM7K0JBQ0UsdUJBQXVCLG1CQUdoQix1QkFBdUIsQ0FBQyxNQUFNO2lJQUl0QyxFQUFFO3NCQUFWLEtBQUs7Z0JBR0csS0FBSztzQkFBYixLQUFLO2dCQUdHLE9BQU87c0JBQWYsS0FBSztnQkFHRyxJQUFJO3NCQUFaLEtBQUs7Z0JBR0csWUFBWTtzQkFBcEIsS0FBSztnQkFNTixxQkFBcUI7c0JBRHBCLFNBQVM7dUJBQUMsYUFBYTtnQkFFQSxtQkFBbUI7c0JBQTFDLFNBQVM7dUJBQUMsV0FBVztnQkFDRSxtQkFBbUI7c0JBQTFDLFNBQVM7dUJBQUMsV0FBVztnQkFHYixPQUFPO3NCQUFmLEtBQUs7Z0JBR0csT0FBTztzQkFBZixLQUFLO2dCQUdHLEtBQUs7c0JBQWIsS0FBSztnQkFFRyxlQUFlO3NCQUF2QixLQUFLO2dCQUVHLFdBQVc7c0JBQW5CLEtBQUs7Z0JBR0ksWUFBWTtzQkFBckIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbIlxuaW1wb3J0IHtcbiAgQ29tcG9uZW50LFxuICBPbkluaXQsXG4gIFZpZXdDaGlsZCxcbiAgRWxlbWVudFJlZixcbiAgSW5wdXQsXG4gIE91dHB1dCxcbiAgRXZlbnRFbWl0dGVyLFxuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gIFNpbXBsZUNoYW5nZXMsXG4gIE9uQ2hhbmdlcyxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlLCBvZiwgZnJvbUV2ZW50LCBtZXJnZSB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHtcbiAgZmlsdGVyLFxuICBtYXAsXG4gIHRhcCxcbiAgZGlzdGluY3RVbnRpbENoYW5nZWQsXG4gIGRlYm91bmNlVGltZSxcbiAgZGVsYXksXG4gIHdpdGhMYXRlc3RGcm9tLFxuICBzdGFydFdpdGgsXG4gIG1hcFRvLFxufSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBFcnJvck1lc3NhZ2UgfSBmcm9tICcuLi9lcnJvci9lcnJvci1tZXNzYWdlLm1vZGVsJztcbmltcG9ydCB7IEhpbnQgfSBmcm9tICcuLi9oaW50L2hpbnQnO1xuaW1wb3J0IHsgTGFiZWwgfSBmcm9tICcuLi9sYWJlbC9sYWJlbCc7XG5cbmV4cG9ydCB0eXBlIE9wdGlvbiA9IHtcbiAgdGV4dDogc3RyaW5nO1xuICBpZDogc3RyaW5nO1xuICBncm91cD86IHN0cmluZztcbiAgc2VsZWN0ZWQ/OiBib29sZWFuO1xufTtcblxudHlwZSBHcm91cGVkT3B0aW9ucyA9IHsgZ3JvdXBOYW1lOiBzdHJpbmc7IG9wdGlvbnM6IE9wdGlvbltdIH07XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2dvdnVrLWZpbHRlcmVkLXNlbGVjdCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9maWx0ZXJlZC1zZWxlY3QuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9maWx0ZXJlZC1zZWxlY3QuY29tcG9uZW50LnNjc3MnXSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG59KVxuZXhwb3J0IGNsYXNzIEZpbHRlcmVkU2VsZWN0Q29tcG9uZW50IGltcGxlbWVudHMgQWZ0ZXJWaWV3SW5pdCB7XG5cbiAgQElucHV0KCkgaWQ6IHN0cmluZztcblxuICAvKiogTGFiZWwgPSB7dGV4dDogXCJcIiwgY2xhc3NlczogXCJcIiwgaXNQYWdlSGVhZGluZzogZmFsc2V9ICovXG4gIEBJbnB1dCgpIGxhYmVsOiBMYWJlbCA9IHt0ZXh0OiAnJywgY2xhc3NlczogJycsIGlzUGFnZUhlYWRpbmc6IGZhbHNlfTtcblxuICAvKiogQ2xhc3MgZXh0ZW50aW9uICdnb3Z1ay1pbnB1dC0td2lkdGgtMjAgKi9cbiAgQElucHV0KCkgY2xhc3Nlczogc3RyaW5nO1xuXG4gIC8qKiBIaW50IHRleHQgKi9cbiAgQElucHV0KCkgaGludDogSGludCA9IHt0ZXh0OiAnJywgY2xhc3NlczogJyd9O1xuXG4gIC8qKiBFcnJvciBtZXNzYWdlIHRvIHNob3cgKi9cbiAgQElucHV0KCkgZXJyb3JNZXNzYWdlOiBFcnJvck1lc3NhZ2UgPSB7IHRleHQ6ICcnLCBjbGFzc2VzOiAnJ307XG5cblxuXG4gIC8vIHVzZSB0aGUgYW5ndWxhciBkZWNvcmF0b3IgdG8gZ2V0IHRoZSBjb250cm9scyB3aXRoaW4gdGhlIHRlbXBsYXRlXG4gIEBWaWV3Q2hpbGQoJ2ZpbHRlcklucHV0JylcbiAgZmlsdGVySW5wdXRFbGVtZW50UmVmOiBFbGVtZW50UmVmO1xuICBAVmlld0NoaWxkKCdzZWxlY3RCb3gnKSBzZWxlY3RCb3hFbGVtZW50UmVmOiBFbGVtZW50UmVmO1xuICBAVmlld0NoaWxkKCdmYWtlSW5wdXQnKSBmYWtlSW5wdXRFbGVtZW50UmVmOiBFbGVtZW50UmVmO1xuXG4gIC8vIHJlY2lldmUgYW4gYXJyYXkgb2Ygb3B0aW9uc1xuICBASW5wdXQoKSBvcHRpb25zOiBPcHRpb25bXTtcblxuICAvLyBib29sZWFuIHRvIGRlY2xhcmUgd2hldGhlciB3ZSBncm91cGVkIG9yIG5vdFxuICBASW5wdXQoKSBncm91cGVkID0gZmFsc2U7XG5cbiAgLy8gc3R5bGluZyBpbnB1dCB2YXJpYWJsZXMgYWxsIHdpdGggYSBwcmVzZXQgZGVmYXVsdFxuICBASW5wdXQoKSBsaW5lcyA9IDU7XG4gIC8vIHRoZSBiYWNrZ3JvdW5kIGNvbG91ciBuZWVkcyB0byBiZSBoYXJkY29kZWQgc28gdGhhdCBpdCBpc24ndCB0cmFuc3BlcmFudFxuICBASW5wdXQoKSBiYWNrZ3JvdW5kQ29sb3IgPSAnI2ZmZic7XG4gIC8vIGxpa2V3aXNlIHdpdGggYm9yZGVyIG5lZWRzIGhhcmQgY29kaW5nXG4gIEBJbnB1dCgpIGJvcmRlclN0eWxlID0gJzFweCBzb2xpZCAjOTk5JztcblxuICAvLyBwcm9kdWNlIGEgc2luZ2xlIGNob3NlbiBvcHRpb24gYXMgb3V0cHV0XG4gIEBPdXRwdXQoKSBjaG9zZW5PcHRpb24gPSBuZXcgRXZlbnRFbWl0dGVyPE9wdGlvbj4oKTtcblxuICAvLyBvYnNlcnZhYmxlcyB1c2VkIGluIHRoZSBhbmd1bGFyIHRlbXBsYXRlXG4gIGZpbHRlcmVkT3B0aW9uczogT2JzZXJ2YWJsZTxPcHRpb25bXT47XG4gIGZpbHRlcmVkR3JvdXBlZE9wdGlvbnM6IE9ic2VydmFibGU8R3JvdXBlZE9wdGlvbnNbXT47XG4gIGFjdGl2ZTogT2JzZXJ2YWJsZTxib29sZWFuPjtcbiAgY2hvc2VuVGV4dDogT2JzZXJ2YWJsZTxzdHJpbmc+O1xuXG4gIC8vIHRoZSB0aHJlZSBjb250cm9scyB0aGF0IGFyZSBpbiB0aGUgdGVtcGxhdGVcbiAgLy8gYWxsIGZvdW5kIHVzaW5nIEBWaWV3Q2hpbGQgYWJvdmVcbiAgc2VsZWN0Qm94OiBIVE1MU2VsZWN0RWxlbWVudDtcbiAgZmlsdGVySW5wdXQ6IEhUTUxJbnB1dEVsZW1lbnQ7XG4gIGZha2VJbnB1dDogSFRNTElucHV0RWxlbWVudDtcblxuICAvLyB0aGVzZSBvYnNlcnZhYmxlcyB0YWtlIGNhcmUgb2YgdGhlIGNvbnRyb2xcbiAgLy8gcHJldGVuZGluZyB0byBiZSBqdXN0IGxpa2UgYSBub3JtYWwgSFRNTFxuICAvLyBjb250cm9sLi4uXG4gIGZha2VJbnB1dEZvY3VzOiBPYnNlcnZhYmxlPGJvb2xlYW4+O1xuICBjaGFuZ2VGaWx0ZXJUZXh0OiBPYnNlcnZhYmxlPHN0cmluZz47XG4gIGZpbHRlcklucHV0Qmx1ckZvY3VzU2VsZWN0OiBPYnNlcnZhYmxlPGJvb2xlYW4+O1xuICBzZWxlY3RGb2N1c09yQmx1cjogT2JzZXJ2YWJsZTxib29sZWFuPjtcbiAgb3B0aW9uQ2hvc2VuOiBPYnNlcnZhYmxlPE9wdGlvbj47XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBjZHI6IENoYW5nZURldGVjdG9yUmVmLCBwdWJsaWMgZWw6IEVsZW1lbnRSZWYpIHt9XG5cbiAgbmdBZnRlclZpZXdJbml0KCkge1xuICAgIC8vIHVzZSB0aGUgQFZpZXdDaGlsZCBvYmF0aW5lZCBFbGVtZW50UmVmIHRvIGdldCB0aGUgY29udHJvbHNcbiAgICB0aGlzLnNlbGVjdEJveCA9IHRoaXMuc2VsZWN0Qm94RWxlbWVudFJlZi5uYXRpdmVFbGVtZW50O1xuICAgIHRoaXMuZmlsdGVySW5wdXQgPSB0aGlzLmZpbHRlcklucHV0RWxlbWVudFJlZi5uYXRpdmVFbGVtZW50O1xuICAgIHRoaXMuZmFrZUlucHV0ID0gdGhpcy5mYWtlSW5wdXRFbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQ7XG5cbiAgICAvLyB0aGUgXCJ0eXBlYWhlYWRcIiBzdHlsZSBvYnNlcnZhYmxlIHRyaWdnZXJlZCBieSBrZXl1cCBvbiB0aGUgZmlsdGVySW5wdXQgY29udHJvbFxuICAgIHRoaXMuY2hhbmdlRmlsdGVyVGV4dCA9IGZyb21FdmVudCh0aGlzLmZpbHRlcklucHV0LCAna2V5dXAnKS5waXBlKFxuICAgICAgLy8gaWYgdGhlIGRvd24gYXJyb3cgaXMgaGl0IHRoZW4gZ2l2ZSB0aGUgZm9jdXMgdG8gdGhlIHNlbGVjdEJveCBjb250cm9sXG4gICAgICB0YXAoKGUpID0+XG4gICAgICAgIChlIGFzIEtleWJvYXJkRXZlbnQpLmtleSA9PT0gJ0Fycm93RG93bicgPyB0aGlzLnNlbGVjdEJveC5mb2N1cygpIDogbnVsbFxuICAgICAgKSxcbiAgICAgIC8vIGlnbm9yZSBlbnRlciBrZXkgYW5kIGRvd24gYXJyb3cga2V5IGZyb20gaGVyZSBvbndhcmRzXG4gICAgICAvLyAoZW50ZXIga2V5IGRlYWx0aCB3aXRoIGVsc2V3aGVyZSlcbiAgICAgIGZpbHRlcihcbiAgICAgICAgKGUpID0+XG4gICAgICAgICAgKGUgYXMgS2V5Ym9hcmRFdmVudCkua2V5ICE9PSAnRW50ZXInICYmXG4gICAgICAgICAgKGUgYXMgS2V5Ym9hcmRFdmVudCkua2V5ICE9PSAnQXJyb3dEb3duJ1xuICAgICAgKSxcbiAgICAgIC8vIHByZXZlbnQgdG8gcmFwaWQgYSBjaGFuZ2VcbiAgICAgIGRlYm91bmNlVGltZSgxMDApLFxuICAgICAgLy8gb25seSBmaXJlIGlmIHRleHQgaXMgZGlmZmVyZW50XG4gICAgICBkaXN0aW5jdFVudGlsQ2hhbmdlZCgpLFxuICAgICAgLy8gdGhlIHBheWxvaWFkXG4gICAgICBtYXAoKCkgPT4gdGhpcy5maWx0ZXJJbnB1dC52YWx1ZSksXG4gICAgICAvLyBraWNrIG9mZiB3aXRoIGFuIGVtcHR5IHN0cmluZ1xuICAgICAgc3RhcnRXaXRoKCcnKVxuICAgICk7XG5cbiAgICAvLyB3aGVuIHVzZXIgZm9jdXNlcyBvbiB0aGUgZmFrZUlucHV0IGNvbnRyb2wsIGdpdmUgdGhlIGZvY3VzIHRvIHRoZSBmaWx0ZXJJbnB1dFxuICAgIHRoaXMuZmFrZUlucHV0Rm9jdXMgPSBtZXJnZShcbiAgICAgIGZyb21FdmVudCh0aGlzLmZha2VJbnB1dCwgJ2ZvY3VzJyksXG4gICAgICBmcm9tRXZlbnQodGhpcy5mYWtlSW5wdXQsICdrZXl1cCcpLFxuICAgICAgZnJvbUV2ZW50KHRoaXMuZmFrZUlucHV0LCAnY2xpY2snKVxuICAgICkucGlwZShcbiAgICAgIG1hcFRvKHRydWUpLFxuICAgICAgLy8gbmVlZHMgdGhlIGRlbGF5IHNvIHRoYXQgdGhlIGRyb3Bkb3duIGRpdiBoYXMgYXBwZWFyZWQgb3RoZXJ3aXNlIGRvZXNuJ3Qgd29ya1xuICAgICAgdGFwKCgpID0+IHNldFRpbWVvdXQoKCkgPT4gdGhpcy5maWx0ZXJJbnB1dC5mb2N1cygpLCAyMCkpXG4gICAgKTtcblxuICAgIC8vIG9ic2VydmFibGUgdGhhdCByZXR1cm5zIHRydWUgd2hlbiBzZWxlY3RCb3ggZ2V0cyBmb2N1cyBhbmQgZmFsc2Ugd2hlbiBpdCBsb3NlcyBpdCAoYmx1cilcbiAgICB0aGlzLnNlbGVjdEZvY3VzT3JCbHVyID0gbWVyZ2UoXG4gICAgICBmcm9tRXZlbnQodGhpcy5zZWxlY3RCb3gsICdibHVyJykucGlwZShtYXBUbyhmYWxzZSkpLFxuICAgICAgZnJvbUV2ZW50KHRoaXMuc2VsZWN0Qm94LCAnZm9jdXMnKS5waXBlKG1hcFRvKHRydWUpKVxuICAgICkucGlwZShzdGFydFdpdGgoZmFsc2UpKTtcblxuICAgIC8vIGZpbHRlciBpbnB1dCBibHVyIGV2ZW50IGFuZCB0aGUgc2VsZWN0IGNvbXBvbmVudCByZWNlaXZlcyBmb2N1c1xuICAgIC8vIHRoaXMgb2JzZXJ2YWJsZSB3aWxsIGZpcmUgZXZlcnkgdGltZSB0aGUgZmlsdGVySW5wdXQgbG9zZXMgZm9jdXNcbiAgICAvLyB0cnVlID0gc2VsZWN0Qm94IGdvdCBmb2N1cywgZmFsc2UgPSBpdCBkaWRuJ3RcbiAgICB0aGlzLmZpbHRlcklucHV0Qmx1ckZvY3VzU2VsZWN0ID0gZnJvbUV2ZW50KHRoaXMuZmlsdGVySW5wdXQsICdibHVyJykucGlwZShcbiAgICAgIC8vIG1hcFRvKHRydWUpLFxuICAgICAgLy8gZGVsYXkgc28gdGhhdC4uLlxuICAgICAgZGVsYXkoMTApLFxuICAgICAgLy8gLi4uIHRoZSBzZWxlY3RGb2N1c09yQmx1ciBvYnNlcnZhYmxlIGNhbiBiZSB1c2VkXG4gICAgICAvLyB0byBkZWNpZGUgaWYgX3RoZSBmb2N1cyBoYXMgbGVmdCB0aGUgZmlsdGVyIGJ1dCBub3QgZ29uZSB0byB0aGUgc2VsZWN0X1xuICAgICAgd2l0aExhdGVzdEZyb20odGhpcy5zZWxlY3RGb2N1c09yQmx1ciksXG4gICAgICAvLyBzcGl0IG91dCB0aGUgbW9zdCByZWNlbnQgcmV0dXJuZWQgYm9vbGVhbiBmcm9tIHRoaXMuc2VsZWN0Rm9jdXNPckJsdXJcbiAgICAgIG1hcCgoWywgeV0pID0+IHkpXG4gICAgKTtcblxuICAgIC8vIG9ic2VydmFibGUgdGhhdCBwcm9kdWNlcyB0aGUgZmlsdGVyZWQgYXJyYXkgb2Ygb3B0aW9uc1xuICAgIC8vIHdoaWNoIGlzIHVzZWQgdG8gZmlsbCB0aGUgYm94ICh2aWEgbmdGb3IqIGFuZCBhc3luYyBpbiB0aGUgdGVtcGxhdGUpXG4gICAgdGhpcy5maWx0ZXJlZE9wdGlvbnMgPSBtZXJnZSh0aGlzLmNoYW5nZUZpbHRlclRleHQsIG9mKCcnKSkucGlwZShcbiAgICAgIG1hcCgoZmlsdGVyU3RyaW5nKSA9PlxuICAgICAgICB0aGlzLm9wdGlvbnNcbiAgICAgICAgICAvLyBmaWx0ZXIgdGhlbSBpZ25vcm5pbmcgY2FzZVxuICAgICAgICAgIC5maWx0ZXIoXG4gICAgICAgICAgICAoeCkgPT5cbiAgICAgICAgICAgICAgLy8gbG9vayBmb3IgdGhlIHN0cmluZyBpbiBib3RoIHRoZSB0ZXh0IGFuZCB0aGUgZ3JvdXAgc3RyaW5nc1xuICAgICAgICAgICAgICAvLyB1c2UgdGhlIEpTIHx8IHNob3J0aGFuZCB0byByZXBsYWNlIHVuZGVmaW5lZCBncm91cCBmaWVsZCB3aXRoIFwiXCJcbiAgICAgICAgICAgICAgKHgudGV4dC50b0xvd2VyQ2FzZSgpICsgKHguZ3JvdXAgfHwgJycpLnRvTG93ZXJDYXNlKCkpLmluZGV4T2YoXG4gICAgICAgICAgICAgICAgZmlsdGVyU3RyaW5nLnRvTG93ZXJDYXNlKClcbiAgICAgICAgICAgICAgKSA+IC0xXG4gICAgICAgICAgKVxuICAgICAgICAgIC8vIG1ha2UgdGhlbSBhbGwgXCJ1bnNlbGVjdGVkXCJcbiAgICAgICAgICAubWFwKCh5KSA9PiB7XG4gICAgICAgICAgICB5LnNlbGVjdGVkID0gZmFsc2U7XG4gICAgICAgICAgICByZXR1cm4geTtcbiAgICAgICAgICB9KVxuICAgICAgICAgIC5zb3J0KChhLCBiKSA9PlxuICAgICAgICAgICAgKChhLmdyb3VwIHx8ICcnKSArIGEudGV4dCkubG9jYWxlQ29tcGFyZSgoYi5ncm91cCB8fCAnJykgKyBiLnRleHQpXG4gICAgICAgICAgKVxuICAgICAgKSxcbiAgICAgIG1hcCgoeCkgPT4ge1xuICAgICAgICAvLyBpZiB0aGUgYXJyYXkgaXMgbm9uLXplcm8uLi5cbiAgICAgICAgaWYgKHhbMF0pIHtcbiAgICAgICAgICAvLyBtYWtlIHRoZSBmaXJzdCBvbmUgdGhlIHNlbGVjdGVkIG9uZSBieSBkZWZhdWx0XG4gICAgICAgICAgeFswXS5zZWxlY3RlZCA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHg7XG4gICAgICB9KVxuICAgICk7XG5cbiAgICAvLyBvYnNlcnZhYmxlIHRoYXQgcHJvZHVjZXMgdGhlIGZpbHRlcmVkIEFORCBHUk9VUEVEIGFycmF5IG9mIG9wdGlvbnNcbiAgICAvLyB3aGljaCBpcyB1c2VkIHRvIGZpbGwgdGhlIHNlY29uZCBhbHRlcm5hdGl2ZSAoZ3JvdXBlZCkgYm94XG4gICAgdGhpcy5maWx0ZXJlZEdyb3VwZWRPcHRpb25zID0gdGhpcy5maWx0ZXJlZE9wdGlvbnMucGlwZShcbiAgICAgIG1hcCgoeCkgPT5cbiAgICAgICAgLy8gZ2V0IHRoZSBsaXN0IG9mIGdyb3VwcyBieSBtYXBwaW5nIHRoZSBncm91cCBmaWVsZCBvZiB0aGUgb3B0aW9uc1xuICAgICAgICAvLyBhbmQgdGhlbiBkZWxldGluZyBkdXBsaWNhdGVzIHVzaW5nIEFycmF5LmZyb20obmV3IFNldChfb3JpZ2luYWxfYXJyYXlfKSlcbiAgICAgICAgQXJyYXkuZnJvbShuZXcgU2V0KHgubWFwKCh5KSA9PiB5Lmdyb3VwKSkpXG4gICAgICAgICAgLy8gZm9yIGVhY2ggb25lIG9mIHRoZXNlIGdyb3VwcyBjcmVhdGUgYSBncm91cGVkT3B0aW9ucyBvYmplY3RcbiAgICAgICAgICAubWFwKChncm91cCkgPT4gKHtcbiAgICAgICAgICAgIC8vIHdpdGggdGhlIGdyb3VwIG5hbWVcbiAgICAgICAgICAgIGdyb3VwTmFtZTogZ3JvdXAsXG4gICAgICAgICAgICAvLyBhbmQgYW4gYXBwcm9wcmlhdGVseSBzZWxlY3RlZCAoZmlsdGVyZWQpIGdyb3VwIG9mIG9wdGlvbnNcbiAgICAgICAgICAgIG9wdGlvbnM6IHguZmlsdGVyKCh5KSA9PiB5Lmdyb3VwID09PSBncm91cCksXG4gICAgICAgICAgfSkpXG4gICAgICApXG4gICAgKTtcblxuICAgIC8vIG9ic2VydmFibGUgdG8gZmlyZSB3aGVuIHRoZSB1c2VyIGNob29zZXMgYW4gb3B0aW9uXG4gICAgdGhpcy5vcHRpb25DaG9zZW4gPSBtZXJnZShcbiAgICAgIC8vIGJ5IGNsaWNraW5nIGFuIGl0ZW0gdGhlIHNlbGVjdEJveCBvciBoaXR0aW5nIGVudGVyIGluXG4gICAgICAvLyBlaXRoZXIgdGhlIGZpbHRlcklucHV0IG9yIHRoZSBzZWxlY3RCb3hcbiAgICAgIGZyb21FdmVudCh0aGlzLnNlbGVjdEJveCwgJ2tleXVwJykucGlwZShcbiAgICAgICAgZmlsdGVyKChlKSA9PiAoZSBhcyBLZXlib2FyZEV2ZW50KS5rZXkgPT09ICdFbnRlcicpXG4gICAgICApLFxuICAgICAgZnJvbUV2ZW50KHRoaXMuc2VsZWN0Qm94LCAnY2xpY2snKSxcbiAgICAgIGZyb21FdmVudCh0aGlzLmZpbHRlcklucHV0LCAna2V5dXAnKS5waXBlKFxuICAgICAgICBmaWx0ZXIoKGUpID0+IChlIGFzIEtleWJvYXJkRXZlbnQpLmtleSA9PT0gJ0VudGVyJyksXG4gICAgICAgIG1hcFRvKHRydWUpXG4gICAgICApXG4gICAgKS5waXBlKFxuICAgICAgLy8gdHJhbnNmb3JtIHRoZSBvdXRwdXQgaW50byBhbiBvcHRpb24gdHlwZSB2YXJpYWJsZVxuICAgICAgbWFwKCgpID0+ICh7XG4gICAgICAgIHRleHQ6IHRoaXMuc2VsZWN0Qm94Lm9wdGlvbnNbdGhpcy5zZWxlY3RCb3guc2VsZWN0ZWRJbmRleF0udGV4dCxcbiAgICAgICAgaWQ6IHRoaXMuc2VsZWN0Qm94Lm9wdGlvbnNbdGhpcy5zZWxlY3RCb3guc2VsZWN0ZWRJbmRleF0udmFsdWUsXG4gICAgICAgIC8vIGdyb3VwOiB0aGlzLnNlbGVjdEJveC5vcHRpb25zW3RoaXMuc2VsZWN0Qm94LnNlbGVjdGVkSW5kZXhdLmxhYmVsLFxuICAgICAgfSkpLFxuICAgICAgLy8gZW1pdCB0aGUgY2hvc2VuT3B0aW9uIGZvciB0aGUgcGFyZW50IEhUTUwgY29udHJvbCB0byByZWFkXG4gICAgICB0YXAoKHgpID0+IHRoaXMuY2hvc2VuT3B0aW9uLmVtaXQoeCkpXG4gICAgKTtcblxuICAgIC8vIG9ic2VydmFibGUgdG8ga2VlcCB0aGUgdGV4dCBpbiB0aGUgZmFrZUlucHV0IHVwIHRvIGRhdGVcbiAgICB0aGlzLmNob3NlblRleHQgPSB0aGlzLm9wdGlvbkNob3Nlbi5waXBlKG1hcCgoeCkgPT4geC50ZXh0KSk7XG5cbiAgICAvLyBvYnNlcnZhYmxlIHRoYXQgZGV0ZXJtaW5lcyB3aGV0aGVyIHRoZSBkcm9wZG93biArIGlubmVyIGRpdnMgYXJlIHZpc2libGUgb3Igbm90XG4gICAgLy8gdHJ1ZSA9IHNob3VsZCBiZSB2aXNpYmxlLCBmYWxzZSA9IHNob3VsZCBiZSBoaWRkZW5cbiAgICAvLyB1c2VkIGJ5IHRoZSBhbmd1bGFyIHRlbXBsYXRlIGFzIFtoaWRkZW5dPVwiIShhY3RpdmUgfCBhc3luYylcIlxuICAgIHRoaXMuYWN0aXZlID0gbWVyZ2UoXG4gICAgICAvLyB0aGlzIG9ic2VydmFibGUgd2lsbCBmaXJlIGV2ZXJ5IHRpbWUgdGhlIGZpbHRlcklucHV0IGxvc2VzIGZvY3VzXG4gICAgICAvLyB0cnVlID0gc2VsZWN0Qm94IGdvdCBmb2N1cywgZmFsc2UgPSBpdCBkaWRuJ3RcbiAgICAgIHRoaXMuZmlsdGVySW5wdXRCbHVyRm9jdXNTZWxlY3QsXG4gICAgICAvLyB0aGlzIGlucHV0IGZpcmVzIHRydWUgaWYgdGhlIGZha2VJbnB1dCBnZXRzIGZvY3VzXG4gICAgICB0aGlzLmZha2VJbnB1dEZvY3VzLFxuICAgICAgLy8gdGhpcyBvYnNlcnZhYmxlIGZpcmVzIGZhbHNlIGlmIHRoZSBzZWxlY3RCb3ggbG9zZXMgZm9jdXNcbiAgICAgIG1lcmdlKGZyb21FdmVudCh0aGlzLnNlbGVjdEJveCwgJ2JsdXInKSkucGlwZShtYXAoKCkgPT4gZmFsc2UpKSxcbiAgICAgIC8vIHRoaXMgb2JzZXJ2YWJsZSBmaXJlcyBpZiB0aGUgdXNlciBjaG9vc2VzIGFuIG9wdGlvblxuICAgICAgdGhpcy5vcHRpb25DaG9zZW4ucGlwZShtYXBUbyhmYWxzZSkpXG4gICAgICAvLyB0aGlzIG9ic2VydmFibGUgZmlyZXMgaWYgdGhlIGZha2VJbnB1dCB0ZXh0IGNoYW5nZXMgZm9yIHdoYXRldmVyIHJlYXNvblxuICAgICAgLy8gdGhpcy5jaG9zZW5UZXh0LnBpcGUobWFwKCgpID0+IGZhbHNlKSlcbiAgICApO1xuXG4gICAgLy8gYmVjYXVzZSB3ZSBhcmUgY3JlYXRpbmcgYWxsIHRoZXNlIG9ic2VydmFibGVzIGluIG5nQWZ0ZXJWaWV3SW5pdFxuICAgIC8vIChjYW50IGRvIHRoZW0gaW4gbmdPbkluaXQgYmVjYXVzZSB0aGUgQFZpZXdDaGlsZCBkb2VzbnQgd29yaylcbiAgICAvLyB3ZSBuZWVkIHRvIHJ1biBjaGFuZ2UgZGV0ZWN0aW9uIG1hbnVhbGx5IG9uY2VcbiAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG4gIH1cblxufVxuIiwiPGRpdiBbZ292dWtFcnJvckxpbmVdPVwiZXJyb3JNZXNzYWdlXCIgY2xhc3M9XCJnb3Z1ay1mb3JtLWdyb3VwXCI+XG5cbiAgPGdvdnVrLWxhYmVsIGlkPVwie3tpZH19XCIgW2xhYmVsXT1cImxhYmVsXCI+PC9nb3Z1ay1sYWJlbD5cbiAgPGdvdnVrLWhpbnQgIGlkPVwie3tpZH19XCIgW2hpbnRdPVwiaGludFwiPiA8L2dvdnVrLWhpbnQ+XG4gIDxnb3Z1ay1lcnJvciBpZD1cInt7aWR9fVwiIFtlcnJvck1lc3NhZ2VdPVwiZXJyb3JNZXNzYWdlXCI+PC9nb3Z1ay1lcnJvcj5cblxuICA8ZGl2PlxuICAgIDxzZWxlY3QgI2Zha2VJbnB1dCBjbGFzcz1cImdvdnVrLXNlbGVjdCBcIiBbbmdDbGFzc109XCJ7J2dvdnVrLXNlbGVjdC0tZXJyb3InIDogZXJyb3JNZXNzYWdlLnRleHR9XCJcbj5cbiAgICAgIDxvcHRpb24gdmFsdWU9XCJ7eyBjaG9zZW5UZXh0IHwgYXN5bmMgfX1cIiBjbGFzcz1cImdvdnVrLWlucHV0XCIgPnt7IGNob3NlblRleHQgfCBhc3luYyB9fTwvb3B0aW9uPlxuICAgIDwvc2VsZWN0PlxuICA8L2Rpdj5cblxuICA8ZGl2IGNsYXNzPVwiZHJvcGRvd25cIj5cblxuICAgIDxkaXYgY2xhc3M9XCJpbm5lclwiIFtoaWRkZW5dPVwiKGFjdGl2ZSB8IGFzeW5jKSA9PT0gZmFsc2VcIlxuICAgIFtuZ1N0eWxlXT1cInsgJ2JhY2tncm91bmQtY29sb3InOiBiYWNrZ3JvdW5kQ29sb3IsIGJvcmRlcjogYm9yZGVyU3R5bGUgfVwiPlxuXG4gICAgICA8aW5wdXQgI2ZpbHRlcklucHV0IHR5cGU9XCJ0ZXh0XCIgY2xhc3M9XCJnb3Z1ay1pbnB1dFwiIC8+XG5cbiAgICAgIDwhLS0gdGhpcyBzZWxlY3QgaXMgdXNlZCBpZiB3ZSBhcmUgbm90IGdyb3VwaW5nIGJ5IG9wdGlvbiBncm91cHMgLS0+XG4gICAgICA8c2VsZWN0ICpuZ0lmPVwiIWdyb3VwZWRcIiBzaXplPVwie3sgbGluZXMgfX1cIiAjc2VsZWN0Qm94ICA+XG5cbiAgICAgICAgPG9wdGlvbiAqbmdGb3I9XCJsZXQgb3B0aW9uIG9mIGZpbHRlcmVkT3B0aW9ucyB8IGFzeW5jOyBpbmRleCBhcyBpXCJcbiAgICAgICAgICAgICAgICBbdmFsdWVdPVwib3B0aW9uLmlkXCJcbiAgICAgICAgICAgICAgICBbc2VsZWN0ZWRdPVwib3B0aW9uLnNlbGVjdGVkXCIgICA+e3sgb3B0aW9uLnRleHQgfX08L29wdGlvbj5cblxuXG4gICAgICA8L3NlbGVjdD5cblxuICAgICAgPCEtLSB0aGlzIHNlbGVjdCBpcyB1c2VkIGlmIHdlIGFyZSBncm91cGluZyBieSBvcHRpb24gZ3JvdXBzIC0tPlxuICAgICAgPHNlbGVjdCAqbmdJZj1cImdyb3VwZWRcIiBzaXplPVwie3sgbGluZXMgfX1cIiAjc2VsZWN0Qm94PlxuXG4gICAgICAgIDxvcHRncm91cCAqbmdGb3I9XCJcbiAgICAgICAgICAgIGxldCBncm91cGVkT3B0aW9ucyBvZiBmaWx0ZXJlZEdyb3VwZWRPcHRpb25zIHwgYXN5bmM7XG4gICAgICAgICAgICBpbmRleCBhcyBpIFwiIGxhYmVsPVwie3sgZ3JvdXBlZE9wdGlvbnMuZ3JvdXBOYW1lIH19XCI+XG5cbiAgICAgICAgICA8b3B0aW9uICpuZ0Zvcj1cImxldCBvcHRpb24gb2YgZ3JvdXBlZE9wdGlvbnMub3B0aW9uczsgaW5kZXggYXMgaVwiIFt2YWx1ZV09XCJvcHRpb24uaWRcIlxuICAgICAgICAgICAgW3NlbGVjdGVkXT1cIm9wdGlvbi5zZWxlY3RlZFwiXG4gICAgICAgICAgICBjbGFzcz1cImdvdnVrLWJvZHlcIlxuXG4gICAgICAgICAgICA+e3sgb3B0aW9uLnRleHQgfX08L29wdGlvbj5cblxuICAgICAgICA8L29wdGdyb3VwPlxuXG4gICAgICA8L3NlbGVjdD5cblxuICAgIDwvZGl2PlxuXG4gIDwvZGl2PlxuPC9kaXY+XG4iXX0=