@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
73 lines • 23 kB
JavaScript
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { BehaviorSubject, combineLatest, defer, from } from 'rxjs';
import { debounceTime, map, shareReplay, startWith } from 'rxjs/operators';
import { gettext } from '@c8y/ngx-components';
import { clone } from 'lodash-es';
import * as i0 from "@angular/core";
import * as i1 from "@c8y/ngx-components";
import * as i2 from "@angular/common";
import * as i3 from "@angular/forms";
import * as i4 from "./icon-name.pipe";
const allIconCategory = gettext('All`icons-category`');
export class IconSelectorComponent {
constructor() {
this.iconCategoriesToExclude = [];
this.showIconClass = true;
this.onSelect = new EventEmitter();
this.searchTerm$ = new BehaviorSubject('');
this.selectedIconCategory$ = new BehaviorSubject(allIconCategory);
this.icons$ = defer(() => from(this.loadIconDefinitions())).pipe(map(icons => icons.filter(tmp => !this.iconCategoriesToExclude.includes(tmp.label))), shareReplay({ refCount: true, bufferSize: 1 }));
this.filteredIcons$ = combineLatest([
this.icons$,
this.searchTerm$.pipe(debounceTime(500), startWith(this.searchTerm$.value)),
this.selectedIconCategory$
]).pipe(map(([icons, searchTerm, category]) => this.filterIconsByCategoryAndSearchTerm(icons, category, searchTerm)));
this.availableIconCategories$ = this.icons$.pipe(map(icons => [allIconCategory, ...icons.map(tmp => tmp.label)]));
}
async loadIconDefinitions() {
const { allIcons } = await import('@c8y/ngx-components/icon-selector/icons');
return allIcons;
}
filterIconsByCategoryAndSearchTerm(iconCategories, selectedCategory, searchTerm) {
if (selectedCategory !== allIconCategory) {
iconCategories = iconCategories.filter(category => category.label === selectedCategory);
}
if (searchTerm) {
const lowerCaseSearchTerm = searchTerm.toLowerCase();
const matchingCategories = new Array();
for (const category of iconCategories) {
const matchingIcons = category.icons.filter(iconClasses => iconClasses.some(iconClass => iconClass.includes(lowerCaseSearchTerm)));
if (matchingIcons.length) {
matchingCategories.push({ ...clone(category), icons: matchingIcons });
}
}
return matchingCategories;
}
return iconCategories;
}
onSearchChange(searchTerm) {
this.searchTerm$.next(searchTerm);
}
onCategoryFilterChanged(categoryChange) {
this.selectedIconCategory$.next(categoryChange);
}
onIconClicked(icon) {
this.selectedIcon = icon[0];
this.onSelect.emit(icon[0]);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: IconSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: IconSelectorComponent, selector: "c8y-icon-selector", inputs: { iconCategoriesToExclude: "iconCategoriesToExclude", showIconClass: "showIconClass", selectedIcon: "selectedIcon" }, outputs: { onSelect: "onSelect" }, ngImport: i0, template: "<div class=\"p-l-24 p-r-24 p-t-8 p-b-8 separator-bottom\">\n <div class=\"row d-flex-sm\">\n <div class=\"col-sm-6 m-b-8\">\n <div class=\"input-group-search input-group\" style=\"width: auto\">\n <input\n type=\"search\"\n class=\"form-control\"\n id=\"filter-icons\"\n [ngModel]=\"searchTerm$ | async\"\n (ngModelChange)=\"onSearchChange($event)\"\n placeholder=\"{{ 'Type to filter icons\u2026' | translate }}\"\n />\n <ng-template #searchIcon>\n <span class=\"input-group-addon\">\n <i c8yIcon=\"search\"></i>\n </span>\n </ng-template>\n <span\n class=\"input-group-addon pointer\"\n *ngIf=\"searchTerm$ | async; else searchIcon\"\n (click)=\"onSearchChange('')\"\n >\n <i c8yIcon=\"times\"></i>\n </span>\n </div>\n </div>\n <div class=\"col-sm-6 m-b-8 text-right\">\n <div class=\"d-inline-flex a-i-center text-left\">\n <label class=\"m-b-0 m-r-8 flex-no-shrink\" translate>Filter by type</label>\n <div class=\"c8y-select-wrapper\">\n <select\n id=\"exampleSelect\"\n class=\"form-control\"\n [ngModel]=\"selectedIconCategory$ | async\"\n (ngModelChange)=\"onCategoryFilterChanged($event)\"\n >\n <option *ngFor=\"let category of availableIconCategories$ | async\" [ngValue]=\"category\">\n {{ category | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n </div>\n </div>\n</div>\n<div class=\"modal-inner-scroll\">\n <div class=\"modal-body\" style=\"height: calc(100vh - 293px)\">\n <div class=\"dtm-icon-grid\">\n <div *ngFor=\"let iconDefinition of filteredIcons$ | async\" class=\"d-contents\">\n <div class=\"legend form-block center grid__col--fullspan\">\n {{ iconDefinition.label | translate }}\n </div>\n\n <div class=\"d-contents\" *ngFor=\"let icon of iconDefinition.icons\">\n <div\n class=\"dtm-icon-grid__item\"\n [ngClass]=\"{\n 'dtm-icon-grid__item--selected': selectedIcon && icon[0] === selectedIcon\n }\"\n >\n <button (click)=\"onIconClicked(icon)\" class=\"dtm-icon-grid__btn\" [title]=\"icon[0] | iconName\">\n <i [c8yIcon]=\"icon[0]\" class=\"d-block icon-40\"></i>\n <small *ngIf=\"showIconClass\" class=\"text-break-word\">{{ icon[0] | iconName }}</small>\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.IconNamePipe, name: "iconName" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: IconSelectorComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-icon-selector', template: "<div class=\"p-l-24 p-r-24 p-t-8 p-b-8 separator-bottom\">\n <div class=\"row d-flex-sm\">\n <div class=\"col-sm-6 m-b-8\">\n <div class=\"input-group-search input-group\" style=\"width: auto\">\n <input\n type=\"search\"\n class=\"form-control\"\n id=\"filter-icons\"\n [ngModel]=\"searchTerm$ | async\"\n (ngModelChange)=\"onSearchChange($event)\"\n placeholder=\"{{ 'Type to filter icons\u2026' | translate }}\"\n />\n <ng-template #searchIcon>\n <span class=\"input-group-addon\">\n <i c8yIcon=\"search\"></i>\n </span>\n </ng-template>\n <span\n class=\"input-group-addon pointer\"\n *ngIf=\"searchTerm$ | async; else searchIcon\"\n (click)=\"onSearchChange('')\"\n >\n <i c8yIcon=\"times\"></i>\n </span>\n </div>\n </div>\n <div class=\"col-sm-6 m-b-8 text-right\">\n <div class=\"d-inline-flex a-i-center text-left\">\n <label class=\"m-b-0 m-r-8 flex-no-shrink\" translate>Filter by type</label>\n <div class=\"c8y-select-wrapper\">\n <select\n id=\"exampleSelect\"\n class=\"form-control\"\n [ngModel]=\"selectedIconCategory$ | async\"\n (ngModelChange)=\"onCategoryFilterChanged($event)\"\n >\n <option *ngFor=\"let category of availableIconCategories$ | async\" [ngValue]=\"category\">\n {{ category | translate }}\n </option>\n </select>\n <span></span>\n </div>\n </div>\n </div>\n </div>\n</div>\n<div class=\"modal-inner-scroll\">\n <div class=\"modal-body\" style=\"height: calc(100vh - 293px)\">\n <div class=\"dtm-icon-grid\">\n <div *ngFor=\"let iconDefinition of filteredIcons$ | async\" class=\"d-contents\">\n <div class=\"legend form-block center grid__col--fullspan\">\n {{ iconDefinition.label | translate }}\n </div>\n\n <div class=\"d-contents\" *ngFor=\"let icon of iconDefinition.icons\">\n <div\n class=\"dtm-icon-grid__item\"\n [ngClass]=\"{\n 'dtm-icon-grid__item--selected': selectedIcon && icon[0] === selectedIcon\n }\"\n >\n <button (click)=\"onIconClicked(icon)\" class=\"dtm-icon-grid__btn\" [title]=\"icon[0] | iconName\">\n <i [c8yIcon]=\"icon[0]\" class=\"d-block icon-40\"></i>\n <small *ngIf=\"showIconClass\" class=\"text-break-word\">{{ icon[0] | iconName }}</small>\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [], propDecorators: { iconCategoriesToExclude: [{
type: Input
}], showIconClass: [{
type: Input
}], onSelect: [{
type: Output
}], selectedIcon: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWNvbi1zZWxlY3Rvci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9pY29uLXNlbGVjdG9yL2ljb24tc2VsZWN0b3IuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vaWNvbi1zZWxlY3Rvci9pY29uLXNlbGVjdG9yLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdkUsT0FBTyxFQUFFLGVBQWUsRUFBYyxhQUFhLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMvRSxPQUFPLEVBQUUsWUFBWSxFQUFFLEdBQUcsRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDM0UsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxXQUFXLENBQUM7Ozs7OztBQUlsQyxNQUFNLGVBQWUsR0FBRyxPQUFPLENBQUMscUJBQXFCLENBQUMsQ0FBQztBQU92RCxNQUFNLE9BQU8scUJBQXFCO0lBWWhDO1FBWFMsNEJBQXVCLEdBQWEsRUFBRSxDQUFDO1FBQ3ZDLGtCQUFhLEdBQUcsSUFBSSxDQUFDO1FBQ3BCLGFBQVEsR0FBRyxJQUFJLFlBQVksRUFBVSxDQUFDO1FBS2hELGdCQUFXLEdBQUcsSUFBSSxlQUFlLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDdEMsMEJBQXFCLEdBQUcsSUFBSSxlQUFlLENBQVMsZUFBZSxDQUFDLENBQUM7UUFJbkUsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzlELEdBQUcsQ0FBQyxLQUFLLENBQUMs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LENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDNUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDOUIsQ0FBQzsrR0F6RVUscUJBQXFCO21HQUFyQixxQkFBcUIsME5DZmxDLHVvRkF1RUE7OzRGRHhEYSxxQkFBcUI7a0JBTGpDLFNBQVM7K0JBQ0UsbUJBQW1CO3dEQUtwQix1QkFBdUI7c0JBQS9CLEtBQUs7Z0JBQ0csYUFBYTtzQkFBckIsS0FBSztnQkFDSSxRQUFRO3NCQUFqQixNQUFNO2dCQUNFLFlBQVk7c0JBQXBCLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIEV2ZW50RW1pdHRlciwgSW5wdXQsIE91dHB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBPYnNlcnZhYmxlLCBjb21iaW5lTGF0ZXN0LCBkZWZlciwgZnJvbSB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZGVib3VuY2VUaW1lLCBtYXAsIHNoYXJlUmVwbGF5LCBzdGFydFdpdGggfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBnZXR0ZXh0IH0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG5pbXBvcnQgeyBjbG9uZSB9IGZyb20gJ2xvZGFzaC1lcyc7XG5pbXBvcnQgeyBEZWZhdWx0SWNvbkRlZmluaXRpb24gfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzL2ljb24tc2VsZWN0b3IvbW9kZWwnO1xuaW1wb3J0IHsgU3VwcG9ydGVkSWNvbnNTdWdnZXN0aW9ucyB9IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMvaWNvbi1zZWxlY3Rvci9pY29ucyc7XG5cbmNvbnN0IGFsbEljb25DYXRlZ29yeSA9IGdldHRleHQoJ0FsbGBpY29ucy1jYXRlZ29yeWAnKTtcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYzh5LWljb24tc2VsZWN0b3InLFxuICB0ZW1wbGF0ZVVybDogJy4vaWNvbi1zZWxlY3Rvci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogW11cbn0pXG5leHBvcnQgY2xhc3MgSWNvblNlbGVjdG9yQ29tcG9uZW50IHtcbiAgQElucHV0KCkgaWNvbkNhdGVnb3JpZXNUb0V4Y2x1ZGU6IHN0cmluZ1tdID0gW107XG4gIEBJbnB1dCgpIHNob3dJY29uQ2xhc3MgPSB0cnVlO1xuICBAT3V0cHV0KCkgb25TZWxlY3QgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcbiAgQElucHV0KCkgc2VsZWN0ZWRJY29uOiBTdXBwb3J0ZWRJY29uc1N1Z2dlc3Rpb25zO1xuXG4gIGljb25zJDogT2JzZXJ2YWJsZTxEZWZhdWx0SWNvbkRlZmluaXRpb25bXT47XG4gIGZpbHRlcmVkSWNvbnMkOiBPYnNlcnZhYmxlPERlZmF1bHRJY29uRGVmaW5pdGlvbltdPjtcbiAgc2VhcmNoVGVybSQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0KCcnKTtcbiAgc2VsZWN0ZWRJY29uQ2F0ZWdvcnkkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxzdHJpbmc+KGFsbEljb25DYXRlZ29yeSk7XG4gIGF2YWlsYWJsZUljb25DYXRlZ29yaWVzJDogT2JzZXJ2YWJsZTxzdHJpbmdbXT47XG5cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy5pY29ucyQgPSBkZWZlcigoKSA9PiBmcm9tKHRoaXMubG9hZEljb25EZWZpbml0aW9ucygpKSkucGlwZShcbiAgICAgIG1hcChpY29ucyA9PiBpY29ucy5maWx0ZXIodG1wID0+ICF0aGlzLmljb25DYXRlZ29yaWVzVG9FeGNsdWRlLmluY2x1ZGVzKHRtcC5sYWJlbCkpKSxcbiAgICAgIHNoYXJlUmVwbGF5KHsgcmVmQ291bnQ6IHRydWUsIGJ1ZmZlclNpemU6IDEgfSlcbiAgICApO1xuICAgIHRoaXMuZmlsdGVyZWRJY29ucyQgPSBjb21iaW5lTGF0ZXN0KFtcbiAgICAgIHRoaXMuaWNvbnMkLFxuICAgICAgdGhpcy5zZWFyY2hUZXJtJC5waXBlKGRlYm91bmNlVGltZSg1MDApLCBzdGFydFdpdGgodGhpcy5zZWFyY2hUZXJtJC52YWx1ZSkpLFxuICAgICAgdGhpcy5zZWxlY3RlZEljb25DYXRlZ29yeSRcbiAgICBdKS5waXBlKFxuICAgICAgbWFwKChbaWNvbnMsIHNlYXJjaFRlcm0sIGNhdGVnb3J5XSkgPT5cbiAgICAgICAgdGhpcy5maWx0ZXJJY29uc0J5Q2F0ZWdvcnlBbmRTZWFyY2hUZXJtKGljb25zLCBjYXRlZ29yeSwgc2VhcmNoVGVybSlcbiAgICAgIClcbiAgICApO1xuICAgIHRoaXMuYXZhaWxhYmxlSWNvbkNhdGVnb3JpZXMkID0gdGhpcy5pY29ucyQucGlwZShcbiAgICAgIG1hcChpY29ucyA9PiBbYWxsSWNvbkNhdGVnb3J5LCAuLi5pY29ucy5tYXAodG1wID0+IHRtcC5sYWJlbCldKVxuICAgICk7XG4gIH1cblxuICBhc3luYyBsb2FkSWNvbkRlZmluaXRpb25zKCk6IFByb21pc2U8RGVmYXVsdEljb25EZWZpbml0aW9uW10+IHtcbiAgICBjb25zdCB7IGFsbEljb25zIH0gPSBhd2FpdCBpbXBvcnQoJ0BjOHkvbmd4LWNvbXBvbmVudHMvaWNvbi1zZWxlY3Rvci9pY29ucycpO1xuICAgIHJldHVybiBhbGxJY29ucztcbiAgfVxuXG4gIGZpbHRlckljb25zQnlDYXRlZ29yeUFuZFNlYXJjaFRlcm0oXG4gICAgaWNvbkNhdGVnb3JpZXM6IERlZmF1bHRJY29uRGVmaW5pdGlvbltdLFxuICAgIHNlbGVjdGVkQ2F0ZWdvcnk6IHN0cmluZyxcbiAgICBzZWFyY2hUZXJtOiBzdHJpbmdcbiAgKTogRGVmYXVsdEljb25EZWZpbml0aW9uW10ge1xuICAgIGlmIChzZWxlY3RlZENhdGVnb3J5ICE9PSBhbGxJY29uQ2F0ZWdvcnkpIHtcbiAgICAgIGljb25DYXRlZ29yaWVzID0gaWNvbkNhdGVnb3JpZXMuZmlsdGVyKGNhdGVnb3J5ID0+IGNhdGVnb3J5LmxhYmVsID09PSBzZWxlY3RlZENhdGVnb3J5KTtcbiAgICB9XG5cbiAgICBpZiAoc2VhcmNoVGVybSkge1xuICAgICAgY29uc3QgbG93ZXJDYXNlU2VhcmNoVGVybSA9IHNlYXJjaFRlcm0udG9Mb3dlckNhc2UoKTtcbiAgICAgIGNvbnN0IG1hdGNoaW5nQ2F0ZWdvcmllcyA9IG5ldyBBcnJheTxEZWZhdWx0SWNvbkRlZmluaXRpb24+KCk7XG4gICAgICBmb3IgKGNvbnN0IGNhdGVnb3J5IG9mIGljb25DYXRlZ29yaWVzKSB7XG4gICAgICAgIGNvbnN0IG1hdGNoaW5nSWNvbnMgPSBjYXRlZ29yeS5pY29ucy5maWx0ZXIoaWNvbkNsYXNzZXMgPT5cbiAgICAgICAgICBpY29uQ2xhc3Nlcy5zb21lKGljb25DbGFzcyA9PiBpY29uQ2xhc3MuaW5jbHVkZXMobG93ZXJDYXNlU2VhcmNoVGVybSkpXG4gICAgICAgICk7XG4gICAgICAgIGlmIChtYXRjaGluZ0ljb25zLmxlbmd0aCkge1xuICAgICAgICAgIG1hdGNoaW5nQ2F0ZWdvcmllcy5wdXNoKHsgLi4uY2xvbmUoY2F0ZWdvcnkpLCBpY29uczogbWF0Y2hpbmdJY29ucyB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIG1hdGNoaW5nQ2F0ZWdvcmllcztcbiAgICB9XG5cbiAgICByZXR1cm4gaWNvbkNhdGVnb3JpZXM7XG4gIH1cblxuICBvblNlYXJjaENoYW5nZShzZWFyY2hUZXJtOiBzdHJpbmcpIHtcbiAgICB0aGlzLnNlYXJjaFRlcm0kLm5leHQoc2VhcmNoVGVybSk7XG4gIH1cblxuICBvbkNhdGVnb3J5RmlsdGVyQ2hhbmdlZChjYXRlZ29yeUNoYW5nZTogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5zZWxlY3RlZEljb25DYXRlZ29yeSQubmV4dChjYXRlZ29yeUNoYW5nZSk7XG4gIH1cblxuICBvbkljb25DbGlja2VkKGljb246IFJlYWRvbmx5QXJyYXk8c3RyaW5nPik6IHZvaWQge1xuICAgIHRoaXMuc2VsZWN0ZWRJY29uID0gaWNvblswXTtcbiAgICB0aGlzLm9uU2VsZWN0LmVtaXQoaWNvblswXSk7XG4gIH1cbn1cbiIsIjxkaXYgY2xhc3M9XCJwLWwtMjQgcC1yLTI0IHAtdC04IHAtYi04IHNlcGFyYXRvci1ib3R0b21cIj5cbiAgPGRpdiBjbGFzcz1cInJvdyBkLWZsZXgtc21cIj5cbiAgICA8ZGl2IGNsYXNzPVwiY29sLXNtLTYgbS1iLThcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJpbnB1dC1ncm91cC1zZWFyY2ggaW5wdXQtZ3JvdXBcIiBzdHlsZT1cIndpZHRoOiBhdXRvXCI+XG4gICAgICAgIDxpbnB1dFxuICAgICAgICAgIHR5cGU9XCJzZWFyY2hcIlxuICAgICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sXCJcbiAgICAgICAgICBpZD1cImZpbHRlci1pY29uc1wiXG4gICAgICAgICAgW25nTW9kZWxdPVwic2VhcmNoVGVybSQgfCBhc3luY1wiXG4gICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwib25TZWFyY2hDaGFuZ2UoJGV2ZW50KVwiXG4gICAgICAgICAgcGxhY2Vob2xkZXI9XCJ7eyAnVHlwZSB0byBmaWx0ZXIgaWNvbnPigKYnIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgLz5cbiAgICAgICAgPG5nLXRlbXBsYXRlICNzZWFyY2hJY29uPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwiaW5wdXQtZ3JvdXAtYWRkb25cIj5cbiAgICAgICAgICAgIDxpIGM4eUljb249XCJzZWFyY2hcIj48L2k+XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICA8L25nLXRlbXBsYXRlPlxuICAgICAgICA8c3BhblxuICAgICAgICAgIGNsYXNzPVwiaW5wdXQtZ3JvdXAtYWRkb24gcG9pbnRlclwiXG4gICAgICAgICAgKm5nSWY9XCJzZWFyY2hUZXJtJCB8IGFzeW5jOyBlbHNlIHNlYXJjaEljb25cIlxuICAgICAgICAgIChjbGljayk9XCJvblNlYXJjaENoYW5nZSgnJylcIlxuICAgICAgICA+XG4gICAgICAgICAgPGkgYzh5SWNvbj1cInRpbWVzXCI+PC9pPlxuICAgICAgICA8L3NwYW4+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwiY29sLXNtLTYgbS1iLTggdGV4dC1yaWdodFwiPlxuICAgICAgPGRpdiBjbGFzcz1cImQtaW5saW5lLWZsZXggYS1pLWNlbnRlciB0ZXh0LWxlZnRcIj5cbiAgICAgICAgPGxhYmVsIGNsYXNzPVwibS1iLTAgbS1yLTggZmxleC1uby1zaHJpbmtcIiB0cmFuc2xhdGU+RmlsdGVyIGJ5IHR5cGU8L2xhYmVsPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiYzh5LXNlbGVjdC13cmFwcGVyXCI+XG4gICAgICAgICAgPHNlbGVjdFxuICAgICAgICAgICAgaWQ9XCJleGFtcGxlU2VsZWN0XCJcbiAgICAgICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sXCJcbiAgICAgICAgICAgIFtuZ01vZGVsXT1cInNlbGVjdGVkSWNvbkNhdGVnb3J5JCB8IGFzeW5jXCJcbiAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cIm9uQ2F0ZWdvcnlGaWx0ZXJDaGFuZ2VkKCRldmVudClcIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIDxvcHRpb24gKm5nRm9yPVwibGV0IGNhdGVnb3J5IG9mIGF2YWlsYWJsZUljb25DYXRlZ29yaWVzJCB8IGFzeW5jXCIgW25nVmFsdWVdPVwiY2F0ZWdvcnlcIj5cbiAgICAgICAgICAgICAge3sgY2F0ZWdvcnkgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICAgIDwvb3B0aW9uPlxuICAgICAgICAgIDwvc2VsZWN0PlxuICAgICAgICAgIDxzcGFuPjwvc3Bhbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG48L2Rpdj5cbjxkaXYgY2xhc3M9XCJtb2RhbC1pbm5lci1zY3JvbGxcIj5cbiAgPGRpdiBjbGFzcz1cIm1vZGFsLWJvZHlcIiBzdHlsZT1cImhlaWdodDogY2FsYygxMDB2aCAtIDI5M3B4KVwiPlxuICAgIDxkaXYgY2xhc3M9XCJkdG0taWNvbi1ncmlkXCI+XG4gICAgICA8ZGl2ICpuZ0Zvcj1cImxldCBpY29uRGVmaW5pdGlvbiBvZiBmaWx0ZXJlZEljb25zJCB8IGFzeW5jXCIgY2xhc3M9XCJkLWNvbnRlbnRzXCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJsZWdlbmQgZm9ybS1ibG9jayBjZW50ZXIgZ3JpZF9fY29sLS1mdWxsc3BhblwiPlxuICAgICAgICAgIHt7IGljb25EZWZpbml0aW9uLmxhYmVsIHwgdHJhbnNsYXRlIH19XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxkaXYgY2xhc3M9XCJkLWNvbnRlbnRzXCIgKm5nRm9yPVwibGV0IGljb24gb2YgaWNvbkRlZmluaXRpb24uaWNvbnNcIj5cbiAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICBjbGFzcz1cImR0bS1pY29uLWdyaWRfX2l0ZW1cIlxuICAgICAgICAgICAgW25nQ2xhc3NdPVwie1xuICAgICAgICAgICAgICAnZHRtLWljb24tZ3JpZF9faXRlbS0tc2VsZWN0ZWQnOiBzZWxlY3RlZEljb24gJiYgaWNvblswXSA9PT0gc2VsZWN0ZWRJY29uXG4gICAgICAgICAgICB9XCJcbiAgICAgICAgICA+XG4gICAgICAgICAgICA8YnV0dG9uIChjbGljayk9XCJvbkljb25DbGlja2VkKGljb24pXCIgY2xhc3M9XCJkdG0taWNvbi1ncmlkX19idG5cIiBbdGl0bGVdPVwiaWNvblswXSB8IGljb25OYW1lXCI+XG4gICAgICAgICAgICAgIDxpIFtjOHlJY29uXT1cImljb25bMF1cIiBjbGFzcz1cImQtYmxvY2sgaWNvbi00MFwiPjwvaT5cbiAgICAgICAgICAgICAgPHNtYWxsICpuZ0lmPVwic2hvd0ljb25DbGFzc1wiIGNsYXNzPVwidGV4dC1icmVhay13b3JkXCI+e3sgaWNvblswXSB8IGljb25OYW1lIH19PC9zbWFsbD5cbiAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICA8L2Rpdj5cbjwvZGl2PlxuIl19