@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
431 lines • 58.7 kB
JavaScript
import { Component, ContentChild, ContentChildren, ElementRef, EventEmitter, Input, Output, QueryList, ViewChild, ViewChildren, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
import { BsDropdownDirective } from 'ngx-bootstrap/dropdown';
import { Subject } from 'rxjs';
import { ListItemComponent } from '../list-group';
import { SelectItemDirective } from './select-item.directive';
import { SelectKeyboardService } from './select-keyboard.service';
import { SelectedItemsDirective } from './selected-items.directive';
import * as i0 from "@angular/core";
import * as i1 from "./select-keyboard.service";
import * as i2 from "ngx-bootstrap/dropdown";
import * as i3 from "../common/icon.directive";
import * as i4 from "@angular/common";
import * as i5 from "../list-group/list-group.component";
import * as i6 from "../list-group/list-item.component";
import * as i7 from "../list-group/list-item-body.component";
import * as i8 from "../list-group/list-item-checkbox.component";
import * as i9 from "../forms/required-input-placeholder.directive";
import * as i10 from "../i18n/c8y-translate.pipe";
export class SelectComponent {
/**
* Items to be displayed in the select.
* Can be an array of strings or an array of objects with `label` and `value` properties.
*
* @example
* ```html
* <c8y-select [items]="[{ label: 'Item 1', value: 'item1' }, { label: 'Item 2', value: 'item2' }]"></c8y-select>
* ```
*
* @example
* ```html
* <c8y-select [items]="['Item 1', 'Item 2', 'Item 3']"></c8y-select>
* ```
*
* For more complex scenarios, you can use content-projection:
*
* @example
* ```html
* <c8y-select>
* <i [c8yIcon]="'rocket'" class="text-16" *c8ySelectItem="'rocket'; label: 'Rocket'"></i>
* <i [c8yIcon]="'car'" class="text-16" *c8ySelectItem="'car'; label: 'Car'"></i>
* </c8y-select>
* ```
*/
set items(value) {
this._items = value.map(item => {
if (typeof item === 'string') {
return { label: item, value: item };
}
return item;
});
}
/**
* The items to be displayed in the select.
*/
get items() {
return this._items;
}
/**
* The selected item.
*/
set selected(value) {
const ensuredArray = Array.isArray(value) ? value : [value];
const normalizedArray = ensuredArray.map(item => {
if (typeof item === 'string') {
return { label: item, value: item };
}
return item;
});
this._selected = normalizedArray.map(item => {
return this._items.find(i => i.value === item.value);
});
}
/**
* Returns the selected item.
*/
get selected() {
return this._selected;
}
/**
* A item which is preselected. It is used when a user types in the search input to give a visual typeahead feedback.
*/
get preselectedItem() {
return this._preselectedItem;
}
/**
* @ignore
* @param selectKeyboardService The service to handle keyboard navigation.
*/
constructor(selectKeyboardService) {
this.selectKeyboardService = selectKeyboardService;
/**
* Placeholder text to be displayed in the select.
*/
this.placeholder = 'Select item…';
/**
* The container to put the dropdown to. Defaults to body.
*/
this.container = 'body';
/**
* If set to true, the user can select multiple items.
*/
this.multi = false;
/**
* If enabled, an item can be selected with the space key.
*/
this.canSelectWithSpace = !this.multi;
/**
* If set to true, the select is disabled.
*/
this.disabled = false;
/**
* Defines, if the dropdown should close automatically after user interaction.
*/
this.autoClose = true;
/**
* Marks the select as required.
*/
this.required = false;
/**
* Allows the user to deselect an item.
*/
this.canDeselect = false;
/**
* The name used for this select.
*/
this.name = 'select';
/**
* The icon to be displayed in the select.
*/
this.icon = 'caret-down';
/**
* Emits if a item is selected.
*/
this.onSelect = new EventEmitter();
/**
* Emits if a item was deselected.
*/
this.onDeselect = new EventEmitter();
/**
* Emits when the select icon is clicked.
*/
this.onIconClick = new EventEmitter();
/**
* Indicates if the search input has focus.
*/
this.searchHasFocus = false;
/**
* The internal select element.
* @ignore
*/
this._selected = [];
/**
* The internal items element.
* @ignore
*/
this._items = [];
this.destroy$ = new Subject();
this.selectKeyboardService.options = {
emptyInput: true,
keyboardSearch: true,
spaceSelect: this.canSelectWithSpace
};
}
/**
* @ignore
*/
ngAfterContentInit() {
if (this.projectedSelectableItems.length > 0) {
this.projectedSelectableItems.forEach(item => {
this._items.push({
label: item.label,
value: item.value,
template: item.templateRef
});
});
}
if (!this.insideClick) {
this.insideClick = this.multi;
}
}
/**
* @ignore
*/
ngAfterViewInit() {
this.selectKeyboardService
.register$(this.searchControl.nativeElement, this.list, this.dropdown)
.subscribe(selectedIndex => {
if (selectedIndex > -1) {
this._preselectedItem = this._items[selectedIndex];
}
else {
this._preselectedItem = null;
}
});
}
/**
* @ignore
*/
ngOnChanges(changes) {
if (changes.canSelectWithSpace) {
this.selectKeyboardService.options = {
emptyInput: true,
keyboardSearch: true,
spaceSelect: changes.canSelectWithSpace.currentValue
};
}
}
/**
* @ignore
*/
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
this.selectKeyboardService.unregister();
}
/**
* Selects an item
* @param item The item to select.
*/
select(item) {
if (this.multi) {
const isSelected = this._selected.indexOf(item) > -1;
if (isSelected) {
this.deselect(item);
return;
}
this._selected.push(item);
this.emitChangeEvent();
this.onSelect.emit(item);
return;
}
this._selected = [item];
this._preselectedItem = item;
this.emitChangeEvent();
this.onSelect.emit(item);
}
/**
* Deselects an item.
* @param item The item to deselect.
*/
deselect(item) {
const index = this._selected.indexOf(item);
if (index > -1) {
this._selected.splice(index, 1);
this.emitChangeEvent();
this.onDeselect.emit(item);
this._preselectedItem = null;
}
}
/**
* Deselects all items
*/
deselectAll() {
if (this._selected.length > 0) {
this.onDeselect.emit();
this._selected = [];
this._preselectedItem = null;
this.searchControl.nativeElement.value = '';
this.close();
this.emitChangeEvent();
}
}
/**
* Closes the dropdown.
*/
close() {
this.dropdown.hide();
}
/**
* Opens the dropdown.
*/
open() {
this.dropdown.show();
}
/**
* @ignore
* @param value The value to write.
*/
writeValue(value) {
if (value) {
this.selected = value;
}
}
/**
* @ignore
* @param fn The function to register for onChange.
*/
registerOnChange(fn) {
this.onChange = fn;
}
/**
* @ignore
* @param fn The function to register for onTouched.
*/
registerOnTouched(fn) {
this.onTouched = fn;
}
/**
* @ignore
* @param isDisabled Should disable or not
*/
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
/**
* @ignore
*/
doBlur() {
this.searchHasFocus = false;
if (this.onTouched) {
this.onTouched();
}
}
/**
* @ignore
*/
doFocus() {
this.open();
queueMicrotask(() => {
this.searchHasFocus = true;
});
}
/**
* @ignore
*/
validate(control) {
if (this.required && (!control.value || control.value.length === 0)) {
return { required: true };
}
return null;
}
/**
* Triggered if the dropdown was shown.
* @ignore
*/
onShown() {
this.searchControl.nativeElement.focus();
}
/**
* Triggered if the dropdown was hidden.
* @ignore
*/
onHidden() {
this.searchControl.nativeElement.value = '';
this._preselectedItem = null;
}
emitChangeEvent() {
if (typeof this.onChange === 'function') {
this.onChange(this.multi ? this._selected : this._selected[0]);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectComponent, deps: [{ token: i1.SelectKeyboardService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: SelectComponent, selector: "c8y-select", inputs: { placeholder: "placeholder", items: "items", selected: "selected", container: "container", multi: "multi", canSelectWithSpace: "canSelectWithSpace", disabled: "disabled", autoClose: "autoClose", insideClick: "insideClick", required: "required", canDeselect: "canDeselect", name: "name", icon: "icon" }, outputs: { onSelect: "onSelect", onDeselect: "onDeselect", onIconClick: "onIconClick" }, host: { classAttribute: "c8y-select-v2" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => SelectComponent)
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => SelectComponent),
multi: true
},
SelectKeyboardService
], queries: [{ propertyName: "projectedSelectedItems", first: true, predicate: SelectedItemsDirective, descendants: true }, { propertyName: "projectedSelectableItems", predicate: SelectItemDirective }], viewQueries: [{ propertyName: "searchControl", first: true, predicate: ["searchControl"], descendants: true }, { propertyName: "dropdown", first: true, predicate: ["dropdown"], descendants: true }, { propertyName: "list", predicate: ListItemComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"c8y-search-dropdown dropdown fit-w\"\n placement=\"bottom left\"\n dropdown\n [container]=\"container\"\n #dropdown=\"bs-dropdown\"\n [autoClose]=\"autoClose\"\n [isDisabled]=\"disabled\"\n [insideClick]=\"insideClick\"\n (onShown)=\"onShown()\"\n (onHidden)=\"onHidden()\"\n dropdownToggle\n (click)=\"open()\"\n>\n <div\n class=\"input-group input-group-dropdown\"\n role=\"button\"\n >\n <div\n class=\"form-control text-truncate\"\n *ngIf=\"true\"\n [ngClass]=\"{\n 'm-r-80': canDeselect && selected.length > 0,\n 'm-r-40': !canDeselect || selected.length === 0,\n 'text-truncate': !multi,\n 'inner-scroll d-flex a-i-center': multi\n }\"\n >\n <!-- rendering of selected items (with content projection) -->\n <div\n class=\"selected-items\"\n *ngIf=\"projectedSelectedItems\"\n >\n <ng-container *ngFor=\"let selectedItem of selected\">\n <ng-container\n *ngTemplateOutlet=\"\n projectedSelectedItems.templateRef;\n context: { $implicit: selectedItem }\n \"\n ></ng-container>\n </ng-container>\n <i\n class=\"text-muted\"\n *ngIf=\"selected.length === 0 && !searchHasFocus && searchControl.value.length === 0\"\n >\n {{ placeholder | translate }}\n </i>\n </div>\n\n <!-- rendering of selected items (default) -->\n <div\n class=\"selected-items\"\n *ngIf=\"!projectedSelectedItems\"\n >\n <span *ngIf=\"!multi\">\n <span *ngIf=\"searchHasFocus && preselectedItem\">\n {{ preselectedItem.label | translate }}\n </span>\n <span *ngIf=\"!searchHasFocus && selected.length === 1\">\n {{ selected[0].label | translate }}\n </span>\n </span>\n <i\n class=\"text-muted\"\n *ngIf=\"selected.length === 0 && !preselectedItem && searchControl.value.length === 0\"\n >\n {{ placeholder | translate }}\n </i>\n <ng-container *ngIf=\"multi\">\n <span class=\"m-r-4\">{{ searchControl.value }}</span>\n <span\n class=\"tag tag--info chip\"\n *ngFor=\"let selectedItem of selected\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10 m-r-4\"\n title=\"{{ selectedItem.label | translate }}\"\n type=\"button\"\n (click)=\"$event.preventDefault(); $event.stopPropagation(); deselect(selectedItem)\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n {{ selectedItem.label | translate }}\n </span>\n </ng-container>\n </div>\n </div>\n \n <input\n class=\"form-control text-truncate\"\n type=\"text\"\n autocomplete=\"off\"\n #searchControl\n [ngClass]=\"{\n 'p-absolute': true,\n 'm-r-80': canDeselect && selected.length > 0,\n 'm-r-40': !canDeselect || selected.length === 0\n }\"\n [required]=\"required\"\n (blur)=\"doBlur()\"\n (focus)=\"doFocus()\"\n [name]=\"name\"\n [disabled]=\"disabled\"\n />\n\n <span class=\"input-group-btn\">\n <!-- this button is displayed only if we have something selected and are allowed to deselect -->\n <button\n class=\"btn btn-dot\"\n title=\"{{ 'Deselect' | translate }}\"\n type=\"button\"\n *ngIf=\"canDeselect && selected.length > 0\"\n [disabled]=\"disabled\"\n (click)=\"$event.preventDefault(); $event.stopPropagation(); deselectAll()\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n <button\n class=\"btn btn-dot\"\n title=\"{{ 'Search' | translate }}\"\n type=\"button\"\n [disabled]=\"disabled\"\n (click)=\"onIconClick.emit({ icon, $event })\"\n data-cy=\"select-button\"\n >\n <i\n class=\"text-primary\"\n [c8yIcon]=\"icon\"\n ></i>\n </button>\n </span>\n </div>\n\n <c8y-list-group\n class=\"dropdown-menu dropdown-menu--modal\"\n [style.width]=\"container === 'body' ? searchControl.parentNode.clientWidth + 'px' : undefined\"\n role=\"menu\"\n data-cy=\"select--dropdown-menu\"\n *dropdownMenu\n >\n <!-- rendering of items (default) -->\n <c8y-li\n style=\"cursor: pointer\"\n *ngFor=\"let item of items\"\n [selectable]=\"true\"\n [dense]=\"true\"\n [active]=\"!multi && item.value === selected[0]?.value\"\n (click)=\"select(item)\"\n >\n <span [attr.data-search-label]=\"item.label | translate\"></span>\n <c8y-li-checkbox\n *ngIf=\"multi\"\n [selected]=\"selected.indexOf(item) > -1\"\n (click)=\"$event.preventDefault();\"\n ></c8y-li-checkbox>\n <c8y-li-body\n *ngIf=\"!item.template\"\n >\n {{ item.label | translate }}\n </c8y-li-body>\n <ng-container\n *ngTemplateOutlet=\"item?.template\"\n ngProjectAs=\"c8y-li-body\"\n ></ng-container>\n </c8y-li>\n <ng-content select=\"div\"></ng-content>\n </c8y-list-group>\n</div>\n", dependencies: [{ kind: "directive", type: i2.BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: i2.BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: i2.BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "directive", type: i3.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i5.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i6.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i7.ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "component", type: i8.ListItemCheckboxComponent, selector: "c8y-list-item-checkbox, c8y-li-checkbox", inputs: ["selected", "indeterminate", "disabled", "displayAsSwitch"], outputs: ["onSelect"] }, { kind: "directive", type: i9.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "pipe", type: i10.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SelectComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-select', host: { class: 'c8y-select-v2' }, providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => SelectComponent)
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => SelectComponent),
multi: true
},
SelectKeyboardService
], template: "<div\n class=\"c8y-search-dropdown dropdown fit-w\"\n placement=\"bottom left\"\n dropdown\n [container]=\"container\"\n #dropdown=\"bs-dropdown\"\n [autoClose]=\"autoClose\"\n [isDisabled]=\"disabled\"\n [insideClick]=\"insideClick\"\n (onShown)=\"onShown()\"\n (onHidden)=\"onHidden()\"\n dropdownToggle\n (click)=\"open()\"\n>\n <div\n class=\"input-group input-group-dropdown\"\n role=\"button\"\n >\n <div\n class=\"form-control text-truncate\"\n *ngIf=\"true\"\n [ngClass]=\"{\n 'm-r-80': canDeselect && selected.length > 0,\n 'm-r-40': !canDeselect || selected.length === 0,\n 'text-truncate': !multi,\n 'inner-scroll d-flex a-i-center': multi\n }\"\n >\n <!-- rendering of selected items (with content projection) -->\n <div\n class=\"selected-items\"\n *ngIf=\"projectedSelectedItems\"\n >\n <ng-container *ngFor=\"let selectedItem of selected\">\n <ng-container\n *ngTemplateOutlet=\"\n projectedSelectedItems.templateRef;\n context: { $implicit: selectedItem }\n \"\n ></ng-container>\n </ng-container>\n <i\n class=\"text-muted\"\n *ngIf=\"selected.length === 0 && !searchHasFocus && searchControl.value.length === 0\"\n >\n {{ placeholder | translate }}\n </i>\n </div>\n\n <!-- rendering of selected items (default) -->\n <div\n class=\"selected-items\"\n *ngIf=\"!projectedSelectedItems\"\n >\n <span *ngIf=\"!multi\">\n <span *ngIf=\"searchHasFocus && preselectedItem\">\n {{ preselectedItem.label | translate }}\n </span>\n <span *ngIf=\"!searchHasFocus && selected.length === 1\">\n {{ selected[0].label | translate }}\n </span>\n </span>\n <i\n class=\"text-muted\"\n *ngIf=\"selected.length === 0 && !preselectedItem && searchControl.value.length === 0\"\n >\n {{ placeholder | translate }}\n </i>\n <ng-container *ngIf=\"multi\">\n <span class=\"m-r-4\">{{ searchControl.value }}</span>\n <span\n class=\"tag tag--info chip\"\n *ngFor=\"let selectedItem of selected\"\n >\n <button\n class=\"btn btn-xs btn-clean text-10 m-r-4\"\n title=\"{{ selectedItem.label | translate }}\"\n type=\"button\"\n (click)=\"$event.preventDefault(); $event.stopPropagation(); deselect(selectedItem)\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n {{ selectedItem.label | translate }}\n </span>\n </ng-container>\n </div>\n </div>\n \n <input\n class=\"form-control text-truncate\"\n type=\"text\"\n autocomplete=\"off\"\n #searchControl\n [ngClass]=\"{\n 'p-absolute': true,\n 'm-r-80': canDeselect && selected.length > 0,\n 'm-r-40': !canDeselect || selected.length === 0\n }\"\n [required]=\"required\"\n (blur)=\"doBlur()\"\n (focus)=\"doFocus()\"\n [name]=\"name\"\n [disabled]=\"disabled\"\n />\n\n <span class=\"input-group-btn\">\n <!-- this button is displayed only if we have something selected and are allowed to deselect -->\n <button\n class=\"btn btn-dot\"\n title=\"{{ 'Deselect' | translate }}\"\n type=\"button\"\n *ngIf=\"canDeselect && selected.length > 0\"\n [disabled]=\"disabled\"\n (click)=\"$event.preventDefault(); $event.stopPropagation(); deselectAll()\"\n >\n <i c8yIcon=\"times\"></i>\n </button>\n <button\n class=\"btn btn-dot\"\n title=\"{{ 'Search' | translate }}\"\n type=\"button\"\n [disabled]=\"disabled\"\n (click)=\"onIconClick.emit({ icon, $event })\"\n data-cy=\"select-button\"\n >\n <i\n class=\"text-primary\"\n [c8yIcon]=\"icon\"\n ></i>\n </button>\n </span>\n </div>\n\n <c8y-list-group\n class=\"dropdown-menu dropdown-menu--modal\"\n [style.width]=\"container === 'body' ? searchControl.parentNode.clientWidth + 'px' : undefined\"\n role=\"menu\"\n data-cy=\"select--dropdown-menu\"\n *dropdownMenu\n >\n <!-- rendering of items (default) -->\n <c8y-li\n style=\"cursor: pointer\"\n *ngFor=\"let item of items\"\n [selectable]=\"true\"\n [dense]=\"true\"\n [active]=\"!multi && item.value === selected[0]?.value\"\n (click)=\"select(item)\"\n >\n <span [attr.data-search-label]=\"item.label | translate\"></span>\n <c8y-li-checkbox\n *ngIf=\"multi\"\n [selected]=\"selected.indexOf(item) > -1\"\n (click)=\"$event.preventDefault();\"\n ></c8y-li-checkbox>\n <c8y-li-body\n *ngIf=\"!item.template\"\n >\n {{ item.label | translate }}\n </c8y-li-body>\n <ng-container\n *ngTemplateOutlet=\"item?.template\"\n ngProjectAs=\"c8y-li-body\"\n ></ng-container>\n </c8y-li>\n <ng-content select=\"div\"></ng-content>\n </c8y-list-group>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.SelectKeyboardService }], propDecorators: { placeholder: [{
type: Input
}], items: [{
type: Input
}], selected: [{
type: Input
}], container: [{
type: Input
}], multi: [{
type: Input
}], canSelectWithSpace: [{
type: Input
}], disabled: [{
type: Input
}], autoClose: [{
type: Input
}], insideClick: [{
type: Input
}], required: [{
type: Input
}], canDeselect: [{
type: Input
}], name: [{
type: Input
}], icon: [{
type: Input
}], onSelect: [{
type: Output
}], onDeselect: [{
type: Output
}], onIconClick: [{
type: Output
}], projectedSelectableItems: [{
type: ContentChildren,
args: [SelectItemDirective]
}], projectedSelectedItems: [{
type: ContentChild,
args: [SelectedItemsDirective]
}], searchControl: [{
type: ViewChild,
args: ['searchControl', { static: false }]
}], dropdown: [{
type: ViewChild,
args: ['dropdown', { static: false }]
}], list: [{
type: ViewChildren,
args: [ListItemComponent]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VsZWN0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2NvcmUvc2VsZWN0L3NlbGVjdC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9jb3JlL3NlbGVjdC9zZWxlY3QuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUdMLFNBQVMsRUFDVCxZQUFZLEVBQ1osZUFBZSxFQUNmLFVBQVUsRUFDVixZQUFZLEVBQ1osS0FBSyxFQUdMLE1BQU0sRUFDTixTQUFTLEVBRVQsU0FBUyxFQUNULFlBQVksRUFDWixVQUFVLEVBQ1gsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUdMLGFBQWEsRUFDYixpQkFBaUIsRUFHbEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUM3RCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNsRCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM5RCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUVsRSxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQzs7Ozs7Ozs7Ozs7O0FBb0JwRSxNQUFNLE9BQU8sZUFBZTtJQVExQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0F1Qkc7SUFDSCxJQUFhLEtBQUssQ0FBQyxLQUE2RDtRQUM5RSxJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDN0IsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDN0IsT0FBTyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxD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QixLQUFLO2dCQU1OLFFBQVE7c0JBRFAsS0FBSztnQkFPTixTQUFTO3NCQURSLEtBQUs7Z0JBUU4sV0FBVztzQkFEVixLQUFLO2dCQU9OLFFBQVE7c0JBRFAsS0FBSztnQkFPTixXQUFXO3NCQURWLEtBQUs7Z0JBT04sSUFBSTtzQkFESCxLQUFLO2dCQU9OLElBQUk7c0JBREgsS0FBSztnQkFPTixRQUFRO3NCQURQLE1BQU07Z0JBT1AsVUFBVTtzQkFEVCxNQUFNO2dCQU9QLFdBQVc7c0JBRFYsTUFBTTtnQkFZK0Isd0JBQXdCO3NCQUE3RCxlQUFlO3VCQUFDLG1CQUFtQjtnQkFNRSxzQkFBc0I7c0JBQTNELFlBQVk7dUJBQUMsc0JBQXNCO2dCQUVtQixhQUFhO3NCQUFuRSxTQUFTO3VCQUFDLGVBQWUsRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUU7Z0JBQ0ssUUFBUTtzQkFBekQsU0FBUzt1QkFBQyxVQUFVLEVBQUUsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFO2dCQUNDLElBQUk7c0JBQTVDLFlBQVk7dUJBQUMsaUJBQWlCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQWZ0ZXJDb250ZW50SW5pdCxcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ29tcG9uZW50LFxuICBDb250ZW50Q2hpbGQsXG4gIENvbnRlbnRDaGlsZHJlbixcbiAgRWxlbWVudFJlZixcbiAgRXZlbnRFbWl0dGVyLFxuICBJbnB1dCxcbiAgT25DaGFuZ2VzLFxuICBPbkRlc3Ryb3ksXG4gIE91dHB1dCxcbiAgUXVlcnlMaXN0LFxuICBTaW1wbGVDaGFuZ2VzLFxuICBWaWV3Q2hpbGQsXG4gIFZpZXdDaGlsZHJlbixcbiAgZm9yd2FyZFJlZlxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIEFic3RyYWN0Q29udHJvbCxcbiAgQ29udHJvbFZhbHVlQWNjZXNzb3IsXG4gIE5HX1ZBTElEQVRPUlMsXG4gIE5HX1ZBTFVFX0FDQ0VTU09SLFxuICBWYWxpZGF0aW9uRXJyb3JzLFxuICBWYWxpZGF0b3Jcbn0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgQnNEcm9wZG93bkRpcmVjdGl2ZSB9IGZyb20gJ25neC1ib290c3RyYXAvZHJvcGRvd24nO1xuaW1wb3J0IHsgU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgTGlzdEl0ZW1Db21wb25lbnQgfSBmcm9tICcuLi9saXN0LWdyb3VwJztcbmltcG9ydCB7IFNlbGVjdEl0ZW1EaXJlY3RpdmUgfSBmcm9tICcuL3NlbGVjdC1pdGVtLmRpcmVjdGl2ZSc7XG5pbXBvcnQgeyBTZWxlY3RLZXlib2FyZFNlcnZpY2UgfSBmcm9tICcuL3NlbGVjdC1rZXlib2FyZC5zZXJ2aWNlJztcbmltcG9ydCB7IFNlbGVjdGFibGVJdGVtLCBTZWxlY3RhYmxlSXRlbVRlbXBsYXRlIH0gZnJvbSAnLi9zZWxlY3QubW9kZWwnO1xuaW1wb3J0IHsgU2VsZWN0ZWRJdGVtc0RpcmVjdGl2ZSB9IGZyb20gJy4vc2VsZWN0ZWQtaXRlbXMuZGlyZWN0aXZlJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYzh5LXNlbGVjdCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9zZWxlY3QuY29tcG9uZW50Lmh0bWwnLFxuICBob3N0OiB7IGNsYXNzOiAnYzh5LXNlbGVjdC12MicgfSxcbiAgcHJvdmlkZXJzOiBbXG4gICAge1xuICAgICAgcHJvdmlkZTogTkdfVkFMVUVfQUNDRVNTT1IsXG4gICAgICBtdWx0aTogdHJ1ZSxcbiAgICAgIHVzZUV4aXN0aW5nOiBmb3J3YXJkUmVmKCgpID0+IFNlbGVjdENvbXBvbmVudClcbiAgICB9LFxuICAgIHtcbiAgICAgIHByb3ZpZGU6IE5HX1ZBTElEQVRPUlMsXG4gICAgICB1c2VFeGlzdGluZzogZm9yd2FyZFJlZigoKSA9PiBTZWxlY3RDb21wb25lbnQpLFxuICAgICAgbXVsdGk6IHRydWVcbiAgICB9LFxuICAgIFNlbGVjdEtleWJvYXJkU2VydmljZVxuICBdXG59KVxuZXhwb3J0IGNsYXNzIFNlbGVjdENvbXBvbmVudFxuICBpbXBsZW1lbnRzIEFmdGVyQ29udGVudEluaXQsIE9uQ2hhbmdlcywgT25EZXN0cm95LCBBZnRlclZpZXdJbml0LCBDb250cm9sVmFsdWVBY2Nlc3NvciwgVmFsaWRhdG9yXG57XG4gIC8qKlxuICAgKiBQbGFjZWhvbGRlciB0ZXh0IHRvIGJlIGRpc3BsYXllZCBpbiB0aGUgc2VsZWN0LlxuICAgKi9cbiAgQElucHV0KCkgcGxhY2Vob2xkZXIgPSAnU2VsZWN0IGl0ZW3igKYnO1xuXG4gIC8qKlxuICAgKiBJdGVtcyB0byBiZSBkaXNwbGF5ZWQgaW4gdGhlIHNlbGVjdC5cbiAgICogQ2FuIGJlIGFuIGFycmF5IG9mIHN0cmluZ3Mgb3IgYW4gYXJyYXkgb2Ygb2JqZWN0cyB3aXRoIGBsYWJlbGAgYW5kIGB2YWx1ZWAgcHJvcGVydGllcy5cbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgaHRtbFxuICAgKiA8Yzh5LXNlbGVjdCBbaXRlbXNdPVwiW3sgbGFiZWw6ICdJdGVtIDEnLCB2YWx1ZTogJ2l0ZW0xJyB9LCB7IGxhYmVsOiAnSXRlbSAyJywgdmFsdWU6ICdpdGVtMicgfV1cIj48L2M4eS1zZWxlY3Q+XG4gICAqIGBgYFxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGBodG1sXG4gICAqIDxjOHktc2VsZWN0IFtpdGVtc109XCJbJ0l0ZW0gMScsICdJdGVtIDInLCAnSXRlbSAzJ11cIj48L2M4eS1zZWxlY3Q+XG4gICAqIGBgYFxuICAgKlxuICAgKiBGb3IgbW9yZSBjb21wbGV4IHNjZW5hcmlvcywgeW91IGNhbiB1c2UgY29udGVudC1wcm9qZWN0aW9uOlxuICAgKlxuICAgKiBAZXhhbXBsZVxuICAgKiBgYGBodG1sXG4gICAqIDxjOHktc2VsZWN0PlxuICAgKiAgICA8aSBbYzh5SWNvbl09XCIncm9ja2V0J1wiIGNsYXNzPVwidGV4dC0xNlwiICpjOHlTZWxlY3RJdGVtPVwiJ3JvY2tldCc7IGxhYmVsOiAnUm9ja2V0J1wiPjwvaT5cbiAgICogICAgPGkgW2M4eUljb25dPVwiJ2NhcidcIiBjbGFzcz1cInRleHQtMTZcIiAqYzh5U2VsZWN0SXRlbT1cIidjYXInOyBsYWJlbDogJ0NhcidcIj48L2k+XG4gICAqIDwvYzh5LXNlbGVjdD5cbiAgICogYGBgXG4gICAqL1xuICBASW5wdXQoKSBzZXQgaXRlbXModmFsdWU6IHN0cmluZ1tdIHwgU2VsZWN0YWJsZUl0ZW1bXSB8IFNlbGVjdGFibGVJdGVtVGVtcGxhdGVbXSkge1xuICAgIHRoaXMuX2l0ZW1zID0gdmFsdWUubWFwKGl0ZW0gPT4ge1xuICAgICAgaWYgKHR5cGVvZiBpdGVtID09PSAnc3RyaW5nJykge1xuICAgICAgICByZXR1cm4geyBsYWJlbDogaXRlbSwgdmFsdWU6IGl0ZW0gfTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBpdGVtO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBpdGVtcyB0byBiZSBkaXNwbGF5ZWQgaW4gdGhlIHNlbGVjdC5cbiAgICovXG4gIGdldCBpdGVtcygpOiBTZWxlY3RhYmxlSXRlbVRlbXBsYXRlW10ge1xuICAgIHJldHVybiB0aGlzLl9pdGVtcztcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgc2VsZWN0ZWQgaXRlbS5cbiAgICovXG4gIEBJbnB1dCgpXG4gIHNldCBzZWxlY3RlZCh2YWx1ZTogc3RyaW5nIHwgU2VsZWN0YWJsZUl0ZW0gfCBBcnJheTxzdHJpbmcgfCBTZWxlY3RhYmxlSXRlbT4pIHtcbiAgICBjb25zdCBlbnN1cmVkQXJyYXk6IChzdHJpbmcgfCBTZWxlY3RhYmxlSXRlbSlbXSA9IEFycmF5LmlzQXJyYXkodmFsdWUpID8gdmFsdWUgOiBbdmFsdWVdO1xuICAgIGNvbnN0IG5vcm1hbGl6ZWRBcnJheSA9IGVuc3VyZWRBcnJheS5tYXAoaXRlbSA9PiB7XG4gICAgICBpZiAodHlwZW9mIGl0ZW0gPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIHJldHVybiB7IGxhYmVsOiBpdGVtLCB2YWx1ZTogaXRlbSB9O1xuICAgICAgfVxuICAgICAgcmV0dXJuIGl0ZW07XG4gICAgfSk7XG4gICAgdGhpcy5fc2VsZWN0ZWQgPSBub3JtYWxpemVkQXJyYXkubWFwKGl0ZW0gPT4ge1xuICAgICAgcmV0dXJuIHRoaXMuX2l0ZW1zLmZpbmQoaSA9PiBpLnZhbHVlID09PSBpdGVtLnZhbHVlKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBzZWxlY3RlZCBpdGVtLlxuICAgKi9cbiAgZ2V0IHNlbGVjdGVkKCk6IFNlbGVjdGFibGVJdGVtW10ge1xuICAgIHJldHVybiB0aGlzLl9zZWxlY3RlZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgY29udGFpbmVyIHRvIHB1dCB0aGUgZHJvcGRvd24gdG8uIERlZmF1bHRzIHRvIGJvZHkuXG4gICAqL1xuICBASW5wdXQoKVxuICBjb250YWluZXI6ICcnIHwgJ2JvZHknID0gJ2JvZHknO1xuXG4gIC8qKlxuICAgKiBJZiBzZXQgdG8gdHJ1ZSwgdGhlIHVzZXIgY2FuIHNlbGVjdCBtdWx0aXBsZSBpdGVtcy5cbiAgICovXG4gIEBJbnB1dCgpXG4gIG11bHRpID0gZmFsc2U7XG5cbiAgLyoqXG4gICAqIElmIGVuYWJsZWQsIGFuIGl0ZW0gY2FuIGJlIHNlbGVjdGVkIHdpdGggdGhlIHNwYWNlIGtleS5cbiAgICovXG4gIEBJbnB1dCgpIGNhblNlbGVjdFdpdGhTcGFjZSA9ICF0aGlzLm11bHRpO1xuXG4gIC8qKlxuICAgKiBJZiBzZXQgdG8gdHJ1ZSwgdGhlIHNlbGVjdCBpcyBkaXNhYmxlZC5cbiAgICovXG4gIEBJbnB1dCgpXG4gIGRpc2FibGVkID0gZmFsc2U7XG5cbiAgLyoqXG4gICAqIERlZmluZXMsIGlmIHRoZSBkcm9wZG93biBzaG91bGQgY2xvc2UgYXV0b21hdGljYWxseSBhZnRlciB1c2VyIGludGVyYWN0aW9uLlxuICAgKi9cbiAgQElucHV0KClcbiAgYXV0b0Nsb3NlID0gdHJ1ZTtcblxuICAvKipcbiAgICogRGVmaW5lcyBpZiB0aGUgZHJvcGRvd24gc2hvdWxkIHN0YXkgb3BlbiB3aGVuIHRoZSB1c2VyIGNsaWNrcyBpbnNpZGUgdGhlIHNlbGVjdC5cbiAgICogSWYgc2V0IHRvIHRydWUsIHRoZSBkcm9wZG93biB3aWxsIG9ubHkgY2xvc2Ugd2hlbiB0aGUgdXNlciBjbGlja3Mgb3V0c2lkZSB0aGUgc2VsZWN0LlxuICAgKi9cbiAgQElucHV0KClcbiAgaW5zaWRlQ2xpY2s6IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIE1hcmtzIHRoZSBzZWxlY3QgYXMgcmVxdWlyZWQuXG4gICAqL1xuICBASW5wdXQoKVxuICByZXF1aXJlZCA9IGZhbHNlO1xuXG4gIC8qKlxuICAgKiBBbGxvd3MgdGhlIHVzZXIgdG8gZGVzZWxlY3QgYW4gaXRlbS5cbiAgICovXG4gIEBJbnB1dCgpXG4gIGNhbkRlc2VsZWN0ID0gZmFsc2U7XG5cbiAgLyoqXG4gICAqIFRoZSBuYW1lIHVzZWQgZm9yIHRoaXMgc2VsZWN0LlxuICAgKi9cbiAgQElucHV0KClcbiAgbmFtZSA9ICdzZWxlY3QnO1xuXG4gIC8qKlxuICAgKiBUaGUgaWNvbiB0byBiZSBkaXNwbGF5ZWQgaW4gdGhlIHNlbGVjdC5cbiAgICovXG4gIEBJbnB1dCgpXG4gIGljb24gPSAnY2FyZXQtZG93bic7XG5cbiAgLyoqXG4gICAqIEVtaXRzIGlmIGEgaXRlbSBpcyBzZWxlY3RlZC5cbiAgICovXG4gIEBPdXRwdXQoKVxuICBvblNlbGVjdCA9IG5ldyBFdmVudEVtaXR0ZXI8U2VsZWN0YWJsZUl0ZW0+KCk7XG5cbiAgLyoqXG4gICAqIEVtaXRzIGlmIGEgaXRlbSB3YXMgZGVzZWxlY3RlZC5cbiAgICovXG4gIEBPdXRwdXQoKVxuICBvbkRlc2VsZWN0ID0gbmV3IEV2ZW50RW1pdHRlcjxTZWxlY3RhYmxlSXRlbT4oKTtcblxuICAvKipcbiAgICogRW1pdHMgd2hlbiB0aGUgc2VsZWN0IGljb24gaXMgY2xpY2tlZC5cbiAgICovXG4gIEBPdXRwdXQoKVxuICBvbkljb25DbGljayA9IG5ldyBFdmVudEVtaXR0ZXI8eyBpY29uOiBzdHJpbmc7ICRldmVudDogTW91c2VFdmVudCB9PigpO1xuXG4gIC8qKlxuICAgKiBJbmRpY2F0ZXMgaWYgdGhlIHNlYXJjaCBpbnB1dCBoYXMgZm9jdXMuXG4gICAqL1xuICBzZWFyY2hIYXNGb2N1cyA9IGZhbHNlO1xuXG4gIC8qKlxuICAgKiBUaGUgc2VsZWN0YWJsZSBpdGVtcyB3aGVuIGNvbnRlbnQgcHJvamVjdGlvbiBpcyB1c2VkLlxuICAgKiBAaWdub3JlXG4gICAqL1xuICBAQ29udGVudENoaWxkcmVuKFNlbGVjdEl0ZW1EaXJlY3RpdmUpIHByb2plY3RlZFNlbGVjdGFibGVJdGVtczogUXVlcnlMaXN0PFNlbGVjdEl0ZW1EaXJlY3RpdmU+O1xuXG4gIC8qKlxuICAgKiBUaGUgc2VsZWN0ZWQgaXRlbXMgd2hlbiBjb250ZW50IHByb2plY3Rpb24gaXMgdXNlZC5cbiAgICogQGlnbm9yZVxuICAgKi9cbiAgQENvbnRlbnRDaGlsZChTZWxlY3RlZEl0ZW1zRGlyZWN0aXZlKSBwcm9qZWN0ZWRTZWxlY3RlZEl0ZW1zOiBTZWxlY3RlZEl0ZW1zRGlyZWN0aXZlO1xuXG4gIEBWaWV3Q2hpbGQoJ3NlYXJjaENvbnRyb2wnLCB7IHN0YXRpYzogZmFsc2UgfSkgcHJpdmF0ZSBzZWFyY2hDb250cm9sOiBFbGVtZW50UmVmO1xuICBAVmlld0NoaWxkKCdkcm9wZG93bicsIHsgc3RhdGljOiBmYWxzZSB9KSBwcml2YXRlIGRyb3Bkb3duOiBCc0Ryb3Bkb3duRGlyZWN0aXZlO1xuICBAVmlld0NoaWxkcmVuKExpc3RJdGVtQ29tcG9uZW50KSBwcml2YXRlIGxpc3Q6IFF1ZXJ5TGlzdDxMaXN0SXRlbUNvbXBvbmVudD47XG5cbiAgLyoqXG4gICAqIEEgaXRlbSB3aGljaCBpcyBwcmVzZWxlY3RlZC4gSXQgaXMgdXNlZCB3aGVuIGEgdXNlciB0eXBlcyBpbiB0aGUgc2VhcmNoIGlucHV0IHRvIGdpdmUgYSB2aXN1YWwgdHlwZWFoZWFkIGZlZWRiYWNrLlxuICAgKi9cbiAgZ2V0IHByZXNlbGVjdGVkSXRlbSgpOiBTZWxlY3RhYmxlSXRlbSB7XG4gICAgcmV0dXJuIHRoaXMuX3ByZXNlbGVjdGVkSXRlbTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgaW50ZXJuYWwgc2VsZWN0IGVsZW1lbnQuXG4gICAqIEBpZ25vcmVcbiAgICovXG4gIHByaXZhdGUgX3NlbGVjdGVkOiBTZWxlY3RhYmxlSXRlbVtdID0gW107XG5cbiAgLyoqXG4gICAqIFRoZSBpbnRlcm5hbCBwcmUtc2VsZWN0IGVsZW1lbnQuIEl0IGlzIHVzZWQgd2hlbiBhIHVzZXIgdHlwZXMgaW4gdGhlIHNlYXJjaCBpbnB1dCB0byBnaXZlIGEgdmlzdWFsIHR5cGVhaGVhZCBmZWVkYmFjay5cbiAgICogQGlnbm9yZVxuICAgKi9cbiAgcHJpdmF0ZSBfcHJlc2VsZWN0ZWRJdGVtOiBTZWxlY3RhYmxlSXRlbTtcblxuICAvKipcbiAgICogVGhlIGludGVybmFsIGl0ZW1zIGVsZW1lbnQuXG4gICAqIEBpZ25vcmVcbiAgICovXG4gIHByaXZhdGUgX2l0ZW1zOiBTZWxlY3RhYmxlSXRlbVRlbXBsYXRlW10gPSBbXTtcblxuICBwcml2YXRlIGRlc3Ryb3kkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcbiAgcHJpdmF0ZSBvbkNoYW5nZTogKGl0ZW1zOiBTZWxlY3RhYmxlSXRlbSB8IFNlbGVjdGFibGVJdGVtW10pID0+IHZvaWQ7XG4gIHByaXZhdGUgb25Ub3VjaGVkOiAoKSA9PiB2b2lkO1xuXG4gIC8qKlxuICAgKiBAaWdub3JlXG4gICAqIEBwYXJhbSBzZWxlY3RLZXlib2FyZFNlcnZpY2UgVGhlIHNlcnZpY2UgdG8gaGFuZGxlIGtleWJvYXJkIG5hdmlnYXRpb24uXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHNlbGVjdEtleWJvYXJkU2VydmljZTogU2VsZWN0S2V5Ym9hcmRTZXJ2aWNlKSB7XG4gICAgdGhpcy5zZWxlY3RLZXlib2FyZFNlcnZpY2Uub3B0aW9ucyA9IHtcbiAgICAgIGVtcHR5SW5wdXQ6IHRydWUsXG4gICAgICBrZXlib2FyZFNlYXJjaDogdHJ1ZSxcbiAgICAgIHNwYWNlU2VsZWN0OiB0aGlzLmNhblNlbGVjdFdpdGhTcGFjZVxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQGlnbm9yZVxuICAgKi9cbiAgbmdBZnRlckNvbnRlbnRJbml0KCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnByb2plY3RlZFNlbGVjdGFibGVJdGVtcy5sZW5ndGggPiAwKSB7XG4gICAgICB0aGlzLnByb2plY3RlZFNlbGVjdGFibGVJdGVtcy5mb3JFYWNoKGl0ZW0gPT4ge1xuICAgICAgICB0aGlzLl9pdGVtcy5wdXNoKHtcbiAgICAgICAgICBsYWJlbDogaXRlbS5sYWJlbCxcbiAgICAgICAgICB2YWx1ZTogaXRlbS52YWx1ZSxcbiAgICAgICAgICB0ZW1wbGF0ZTogaXRlbS50ZW1wbGF0ZVJlZlxuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5pbnNpZGVDbGljaykge1xuICAgICAgdGhpcy5pbnNpZGVDbGljayA9IHRoaXMubXVsdGk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBpZ25vcmVcbiAgICovXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLnNlbGVjdEtleWJvYXJkU2VydmljZVxuICAgICAgLnJlZ2lzdGVyJCh0aGlzLnNlYXJjaENvbnRyb2wubmF0aXZlRWxlbWVudCwgdGhpcy5saXN0LCB0aGlzLmRyb3Bkb3duKVxuICAgICAgLnN1YnNjcmliZShzZWxlY3RlZEluZGV4ID0+IHtcbiAgICAgICAgaWYgKHNlbGVjdGVkSW5kZXggPiAtMSkge1xuICAgICAgICAgIHRoaXMuX3ByZXNlbGVjdGVkSXRlbSA9IHRoaXMuX2l0ZW1zW3NlbGVjdGVkSW5kZXhdO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRoaXMuX3ByZXNlbGVjdGVkSXRlbSA9IG51bGw7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEBpZ25vcmVcbiAgICovXG4gIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICBpZiAoY2hhbmdlcy5jYW5TZWxlY3RXaXRoU3BhY2UpIHtcbiAgICAgIHRoaXMuc2VsZWN0S2V5Ym9hcmRTZXJ2aWNlLm9wdGlvbnMgPSB7XG4gICAgICAgIGVtcHR5SW5wdXQ6IHRydWUsXG4gICAgICAgIGtleWJvYXJkU2VhcmNoOiB0cnVlLFxuICAgICAgICBzcGFjZVNlbGVjdDogY2hhbmdlcy5jYW5TZWxlY3RXaXRoU3BhY2UuY3VycmVudFZhbHVlXG4gICAgICB9O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAaWdub3JlXG4gICAqL1xuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLmRlc3Ryb3kkLm5leHQoKTtcbiAgICB0aGlzLmRlc3Ryb3kkLmNvbXBsZXRlKCk7XG4gICAgdGhpcy5zZWxlY3RLZXlib2FyZFNlcnZpY2UudW5yZWdpc3RlcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlbGVjdHMgYW4gaXRlbVxuICAgKiBAcGFyYW0gaXRlbSBUaGUgaXRlbSB0byBzZWxlY3QuXG4gICAqL1xuICBzZWxlY3QoaXRlbTogU2VsZWN0YWJsZUl0ZW0pOiB2b2lkIHtcbiAgICBpZiAodGhpcy5tdWx0aSkge1xuICAgICAgY29uc3QgaXNTZWxlY3RlZCA9IHRoaXMuX3NlbGVjdGVkLmluZGV4T2YoaXRlbSkgPiAtMTtcbiAgICAgIGlmIChpc1NlbGVjdGVkKSB7XG4gICAgICAgIHRoaXMuZGVzZWxlY3QoaXRlbSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMuX3NlbGVjdGVkLnB1c2goaXRlbSk7XG4gICAgICB0aGlzLmVtaXRDaGFuZ2VFdmVudCgpO1xuICAgICAgdGhpcy5vblNlbGVjdC5lbWl0KGl0ZW0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLl9zZWxlY3RlZCA9IFtpdGVtXTtcbiAgICB0aGlzLl9wcmVzZWxlY3RlZEl0ZW0gPSBpdGVtO1xuICAgIHRoaXMuZW1pdENoYW5nZUV2ZW50KCk7XG4gICAgdGhpcy5vblNlbGVjdC5lbWl0KGl0ZW0pO1xuICB9XG5cbiAgLyoqXG4gICAqIERlc2VsZWN0cyBhbiBpdGVtLlxuICAgKiBAcGFyYW0gaXRlbSBUaGUgaXRlbSB0byBkZXNlbGVjdC5cbiAgICovXG4gIGRlc2VsZWN0KGl0ZW06IFNlbGVjdGFibGVJdGVtKTogdm9pZCB7XG4gICAgY29uc3QgaW5kZXggPSB0aGlzLl9zZWxlY3RlZC5pbmRleE9mKGl0ZW0pO1xuICAgIGlmIChpbmRleCA+IC0xKSB7XG4gICAgICB0aGlzLl9zZWxlY3RlZC5zcGxpY2UoaW5kZXgsIDEpO1xuICAgICAgdGhpcy5lbWl0Q2hhbmdlRXZlbnQoKTtcbiAgICAgIHRoaXMub25EZXNlbGVjdC5lbWl0KGl0ZW0pO1xuICAgICAgdGhpcy5fcHJlc2VsZWN0ZWRJdGVtID0gbnVsbDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRGVzZWxlY3RzIGFsbCBpdGVtc1xuICAgKi9cbiAgZGVzZWxlY3RBbGwoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuX3NlbGVjdGVkLmxlbmd0aCA+IDApIHtcbiAgICAgIHRoaXMub25EZXNlbGVjdC5lbWl0KCk7XG4gICAgICB0aGlzLl9zZWxlY3RlZCA9IFtdO1xuICAgICAgdGhpcy5fcHJlc2VsZWN0ZWRJdGVtID0gbnVsbDtcbiAgICAgIHRoaXMuc2VhcmNoQ29udHJvbC5uYXRpdmVFbGVtZW50LnZhbHVlID0gJyc7XG4gICAgICB0aGlzLmNsb3NlKCk7XG4gICAgICB0aGlzLmVtaXRDaGFuZ2VFdmVudCgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDbG9zZXMgdGhlIGRyb3Bkb3duLlxuICAgKi9cbiAgY2xvc2UoKTogdm9pZCB7XG4gICAgdGhpcy5kcm9wZG93bi5oaWRlKCk7XG4gIH1cblxuICAvKipcbiAgICogT3BlbnMgdGhlIGRyb3Bkb3duLlxuICAgKi9cbiAgb3BlbigpOiB2b2lkIHtcbiAgICB0aGlzLmRyb3Bkb3duLnNob3coKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAaWdub3JlXG4gICAqIEBwYXJhbSB2YWx1ZSBUaGUgdmFsdWUgdG8gd3JpdGUuXG4gICAqL1xuICB3cml0ZVZhbHVlKHZhbHVlOiBTZWxlY3RhYmxlSXRlbSB8IFNlbGVjdGFibGVJdGVtW10pIHtcbiAgICBpZiAodmFsdWUpIHtcbiAgICAgIHRoaXMuc2VsZWN0ZWQgPSB2YWx1ZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQGlnbm9yZVxuICAgKiBAcGFyYW0gZm4gVGhlIGZ1bmN0aW9uIHRvIHJlZ2lzdGVyIGZvciBvbkNoYW5nZS5cbiAgICovXG4gIHJlZ2lzdGVyT25DaGFuZ2UoZm46IChpdGVtczogU2VsZWN0YWJsZUl0ZW0gfCBTZWxlY3RhYmxlSXRlbVtdKSA9PiB2b2lkKTogdm9pZCB7XG4gICAgdGhpcy5vbkNoYW5nZSA9IGZuO1xuICB9XG5cbiAgLyoqXG4gICAqIEBpZ25vcmVcbiAgICogQHBhcmFtIGZuIFRoZSBmdW5jdGlvbiB0byByZWdpc3RlciBmb3Igb25Ub3VjaGVkLlxuICAgKi9cbiAgcmVnaXN0ZXJPblRvdWNoZWQo