@codehint-ng/tree-table
Version:
A simple component to display tree-hierarchically data in table.
146 lines • 28.7 kB
JavaScript
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Helper } from './helpers/items.helper';
import { SortHelper } from './helpers/sort.helper';
import { of } from 'rxjs';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common";
import * as i2 from "./shared/resizable-table-column.directive";
import * as i3 from "./shared/number-to-array.pipe";
export class CngTreeTableComponent {
constructor() {
this.toggleExpandItem = new EventEmitter();
this.rowClick = new EventEmitter();
this.tableClass = '';
this.treeTableItemShells = [];
this.otherItems = []; // to avoid unexpected infinite looping, if parent ids errored with never-ending cycle.
}
insertLevelItemsFor(index, parentShell, level) {
const { levelItems, otherItems } = Helper.selectLevelItems(this.otherItems, parentShell.item);
if (levelItems.length < 1) {
return;
}
const levelItems2 = this.sortFunction ? Helper.sortItems(levelItems, this.sortFunction) : levelItems;
const newLevelShells = Helper.itemsToShells(levelItems2, level, parentShell);
this.treeTableItemShells = Helper.arrayInsertItems(this.treeTableItemShells, index, newLevelShells);
this.otherItems = [...otherItems];
newLevelShells.forEach((shell) => {
shell.haveChildren = this.otherItems.some(i => i.parentId === shell.item.id);
});
}
toggleExpand(event, shell) {
event.preventDefault();
event.stopPropagation();
shell.item.isExpanded = !shell.item.isExpanded;
this.toggleExpandItem.emit(shell.item);
if (shell.item.isExpanded) {
const shellIndex = this.treeTableItemShells.findIndex(i => i.item.id === shell.item.id);
this.insertLevelItemsFor(shellIndex + 1, shell, shell.level + 1);
}
this.updateVisibility();
}
localRowClick(shell) {
this.rowClick.emit(shell.item);
}
isSelected$(shell) {
const data = shell.item.data;
if (data && this.isSelectedFunc$) {
return this.isSelectedFunc$(data);
}
return of(false);
}
updateVisibility() {
this.treeTableItemShells.forEach(sh => {
sh.isVisible = Helper.allParentsExpanded(sh);
});
}
// external functions
clearItems() {
this.treeTableItemShells = [];
this.otherItems = [];
}
appendItems(items) {
this.otherItems = [...this.otherItems, ...items];
const root = { item: { id: null } };
this.insertLevelItemsFor(0, root, 0);
this.treeTableItemShells.forEach(shell => {
if (shell.item.isExpanded) {
const shellIndex = this.treeTableItemShells.findIndex(i => i.item.id === shell.item.id);
this.insertLevelItemsFor(shellIndex + 1, shell, shell.level + 1);
}
else {
shell.haveChildren = this.otherItems.some(i => i.parentId === shell.item.id);
}
});
// fix have children for already added items, but reseted above adding code
this.treeTableItemShells.forEach(shell => {
if (!shell.haveChildren) {
shell.haveChildren = this.isExistsChildrenFor(shell.item.id);
}
});
this.updateVisibility();
}
isExistsChildrenFor(itemId) {
return this.treeTableItemShells
.some(i => {
if (!i) {
return false;
}
if (!i.parent) {
return false;
}
if (!i.parent.item) {
return false;
}
return i.parent.item.id === itemId;
});
}
getChildren(id) {
const fromShells = this.treeTableItemShells
.map(i => i.item)
.filter(i => i.parentId === id);
const fromItems = this.otherItems.filter(i => i.parentId === id);
return [...fromShells, ...fromItems];
}
removeItem(id) {
this.treeTableItemShells = Helper.removeAllShellsChildren([...this.treeTableItemShells], id);
const shell = this.treeTableItemShells.find(i => i.item.id === id);
if (shell && shell.parent) {
const parentId = shell.parent.item.id;
const isExistChildShell = this.treeTableItemShells.some(i => i.item.parentId === parentId);
const isExistChild = this.otherItems.some(i => i.parentId === parentId);
shell.parent.haveChildren = isExistChildShell || isExistChild;
}
this.treeTableItemShells = this.treeTableItemShells.filter(i => i.item.id !== id);
this.otherItems = Helper.removeAllChildren([...this.otherItems], id);
this.otherItems = this.otherItems
.filter(i => i.id !== id);
}
sort() {
if (!this.sortFunction) {
console.warn('cng-tree-table: there is not sortFunction');
return;
}
this.treeTableItemShells = SortHelper.getSorted(this.treeTableItemShells, this.sortFunction);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: CngTreeTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.3", type: CngTreeTableComponent, selector: "cng-tree-table", inputs: { columns: "columns", sortFunction: "sortFunction", isSelectedFunc$: "isSelectedFunc$", conditionalCellClassFunc: "conditionalCellClassFunc", tableClass: "tableClass" }, outputs: { toggleExpandItem: "toggleExpandItem", rowClick: "rowClick" }, ngImport: i0, template: "<table [ngClass]=\"tableClass\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let column of columns; let last=last;\"\r\n [appResizableTableColumn]=\"!last\"\r\n class=\"tree-item-header\"\r\n [ngClass]=\"[column.thClass || '']\">\r\n {{column.titleTemplate ? '' : column.title}}\r\n <ng-container *ngTemplateOutlet=\"column.titleTemplate; context: { $implicit: column.title }\">\r\n </ng-container>\r\n </th>\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n <tr [ngClass]=\"{'selected': (isSelected$(shell) | async)}\"\r\n *ngFor=\"let shell of treeTableItemShells\"\r\n [hidden]=\"!shell.isVisible\" (click)=\"localRowClick(shell)\">\r\n\r\n <td [className]=\"'tree-item ' + (conditionalCellClassFunc ? conditionalCellClassFunc(shell.item, column) : '')\"\r\n *ngFor=\"let column of columns; let i = index;\">\r\n <ng-container *ngIf=\"i === 0\">\r\n <div class=\"level-indent\" *ngFor=\"let foo of shell.level | appNumberToArray\"></div>\r\n <div class=\"tree-item-arrow\"\r\n [ngClass]=\"{'expanded': shell.item.isExpanded, 'vis-hidden': !shell.haveChildren}\"\r\n (click)=\"toggleExpand($event, shell)\"\r\n ><div class=\"arrow\"></div>\r\n </div>\r\n </ng-container>\r\n <ng-container *ngTemplateOutlet=\"column.template; context: { $implicit: shell.item }\">\r\n </ng-container>\r\n </td>\r\n </tr>\r\n </tbody>\r\n\r\n</table>\r\n", styles: ["table{table-layout:fixed}table td,table th{overflow:hidden;white-space:nowrap;-moz-text-overflow:ellipsis;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}table tbody tr td .level-indent{display:inline-block;width:22px}table tbody tr td .tree-item-arrow{cursor:pointer;display:inline-block;width:14px;height:12px}table tbody tr td .tree-item-arrow .arrow{display:inline-block;border:solid darkslateblue;border-width:0 3px 3px 0;padding:3px;transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}table tbody tr td .tree-item-arrow.expanded .arrow{transform:rotate(45deg);-webkit-transform:rotate(45deg)}table tbody tr td .tree-item-arrow.vis-hidden{visibility:hidden}table{border-collapse:collapse}table,th,td{padding:4px 8px;border:1px solid darkgray}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.ResizableTableColumnDirective, selector: "[appResizableTableColumn]", inputs: ["appResizableTableColumn"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3.NumberToArrayPipe, name: "appNumberToArray" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: CngTreeTableComponent, decorators: [{
type: Component,
args: [{ selector: 'cng-tree-table', template: "<table [ngClass]=\"tableClass\">\r\n <thead>\r\n <tr>\r\n <th *ngFor=\"let column of columns; let last=last;\"\r\n [appResizableTableColumn]=\"!last\"\r\n class=\"tree-item-header\"\r\n [ngClass]=\"[column.thClass || '']\">\r\n {{column.titleTemplate ? '' : column.title}}\r\n <ng-container *ngTemplateOutlet=\"column.titleTemplate; context: { $implicit: column.title }\">\r\n </ng-container>\r\n </th>\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n <tr [ngClass]=\"{'selected': (isSelected$(shell) | async)}\"\r\n *ngFor=\"let shell of treeTableItemShells\"\r\n [hidden]=\"!shell.isVisible\" (click)=\"localRowClick(shell)\">\r\n\r\n <td [className]=\"'tree-item ' + (conditionalCellClassFunc ? conditionalCellClassFunc(shell.item, column) : '')\"\r\n *ngFor=\"let column of columns; let i = index;\">\r\n <ng-container *ngIf=\"i === 0\">\r\n <div class=\"level-indent\" *ngFor=\"let foo of shell.level | appNumberToArray\"></div>\r\n <div class=\"tree-item-arrow\"\r\n [ngClass]=\"{'expanded': shell.item.isExpanded, 'vis-hidden': !shell.haveChildren}\"\r\n (click)=\"toggleExpand($event, shell)\"\r\n ><div class=\"arrow\"></div>\r\n </div>\r\n </ng-container>\r\n <ng-container *ngTemplateOutlet=\"column.template; context: { $implicit: shell.item }\">\r\n </ng-container>\r\n </td>\r\n </tr>\r\n </tbody>\r\n\r\n</table>\r\n", styles: ["table{table-layout:fixed}table td,table th{overflow:hidden;white-space:nowrap;-moz-text-overflow:ellipsis;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis}table tbody tr td .level-indent{display:inline-block;width:22px}table tbody tr td .tree-item-arrow{cursor:pointer;display:inline-block;width:14px;height:12px}table tbody tr td .tree-item-arrow .arrow{display:inline-block;border:solid darkslateblue;border-width:0 3px 3px 0;padding:3px;transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}table tbody tr td .tree-item-arrow.expanded .arrow{transform:rotate(45deg);-webkit-transform:rotate(45deg)}table tbody tr td .tree-item-arrow.vis-hidden{visibility:hidden}table{border-collapse:collapse}table,th,td{padding:4px 8px;border:1px solid darkgray}\n"] }]
}], ctorParameters: () => [], propDecorators: { columns: [{
type: Input
}], sortFunction: [{
type: Input
}], isSelectedFunc$: [{
type: Input
}], conditionalCellClassFunc: [{
type: Input
}], toggleExpandItem: [{
type: Output
}], rowClick: [{
type: Output
}], tableClass: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJlZS10YWJsZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy90cmVlLXRhYmxlL3NyYy9saWIvdHJlZS10YWJsZS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9wcm9qZWN0cy90cmVlLXRhYmxlL3NyYy9saWIvdHJlZS10YWJsZS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUMsU0FBUyxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQVUsTUFBTSxFQUFjLE1BQU0sZUFBZSxDQUFDO0FBQzFGLE9BQU8sRUFBQyxNQUFNLEVBQUMsTUFBTSx3QkFBd0IsQ0FBQztBQUc5QyxPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFDakQsT0FBTyxFQUFhLEVBQUUsRUFBQyxNQUFNLE1BQU0sQ0FBQzs7Ozs7QUFPcEMsTUFBTSxPQUFPLHFCQUFxQjtJQVloQztRQVBVLHFCQUFnQixHQUFHLElBQUksWUFBWSxFQUFvQixDQUFDO1FBQ3hELGFBQVEsR0FBRyxJQUFJLFlBQVksRUFBb0IsQ0FBQztRQUNqRCxlQUFVLEdBQUcsRUFBRSxDQUFDO1FBRXpCLHdCQUFtQixHQUE0QixFQUFFLENBQUM7UUFDbEQsZUFBVSxHQUF1QixFQUFFLENBQUMsQ0FBQyx1RkFBdUY7SUFHNUgsQ0FBQztJQUVELG1CQUFtQixDQUFDLEtBQWEsRUFBRSxXQUFrQyxFQUFFLEtBQWE7UUFDbEYsTUFBTSxFQUFDLFVBQVUsRUFBRSxVQUFVLEVBQUMsR0FBRyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDNUYsSUFBSSxVQUFVLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQUMsT0FBTztRQUFDLENBQUM7UUFFdEMsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxD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QUNULENBQUM7UUFFRCxJQUFJLENBQUMsbUJBQW1CLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQy9GLENBQUM7OEdBdklVLHFCQUFxQjtrR0FBckIscUJBQXFCLGlUQ1psQyxnOENBb0NBOzsyRkR4QmEscUJBQXFCO2tCQUxqQyxTQUFTOytCQUNFLGdCQUFnQjt3REFLakIsT0FBTztzQkFBZixLQUFLO2dCQUNHLFlBQVk7c0JBQXBCLEtBQUs7Z0JBQ0csZUFBZTtzQkFBdkIsS0FBSztnQkFDRyx3QkFBd0I7c0JBQWhDLEtBQUs7Z0JBQ0ksZ0JBQWdCO3NCQUF6QixNQUFNO2dCQUNHLFFBQVE7c0JBQWpCLE1BQU07Z0JBQ0UsVUFBVTtzQkFBbEIsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7Q29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIElucHV0LCBPbkluaXQsIE91dHB1dCwgVGVtcGxhdGVSZWZ9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtIZWxwZXJ9IGZyb20gJy4vaGVscGVycy9pdGVtcy5oZWxwZXInO1xuaW1wb3J0IHtUcmVlVGFibGVJdGVtU2hlbGx9IGZyb20gJy4vaGVscGVycy9tb2RlbHMnO1xuaW1wb3J0IHtDb25kaXRpb25hbENlbGxDbGFzc0Z1bmMsIElzU2VsZWN0ZWRGdW5jJCwgU29ydEZ1bmN0aW9uLCBUcmVlVGFibGVDb2x1bW4sIFRyZWVUYWJsZUl0ZW19IGZyb20gJy4vbW9kZWxzJztcbmltcG9ydCB7U29ydEhlbHBlcn0gZnJvbSAnLi9oZWxwZXJzL3NvcnQuaGVscGVyJztcbmltcG9ydCB7T2JzZXJ2YWJsZSwgb2Z9IGZyb20gJ3J4anMnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdjbmctdHJlZS10YWJsZScsXG4gIHRlbXBsYXRlVXJsOiAnLi90cmVlLXRhYmxlLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vdHJlZS10YWJsZS5jb21wb25lbnQuc2NzcyddXG59KVxuZXhwb3J0IGNsYXNzIENuZ1RyZWVUYWJsZUNvbXBvbmVudDxUPiB7XG4gIEBJbnB1dCgpIGNvbHVtbnM6IFRyZWVUYWJsZUNvbHVtbltdO1xuICBASW5wdXQoKSBzb3J0RnVuY3Rpb246IFNvcnRGdW5jdGlvbjxUPjtcbiAgQElucHV0KCkgaXNTZWxlY3RlZEZ1bmMkOiBJc1NlbGVjdGVkRnVuYyQ8VD47XG4gIEBJbnB1dCgpIGNvbmRpdGlvbmFsQ2VsbENsYXNzRnVuYzogQ29uZGl0aW9uYWxDZWxsQ2xhc3NGdW5jPFQ+O1xuICBAT3V0cHV0KCkgdG9nZ2xlRXhwYW5kSXRlbSA9IG5ldyBFdmVudEVtaXR0ZXI8VHJlZVRhYmxlSXRlbTxUPj4oKTtcbiAgQE91dHB1dCgpIHJvd0NsaWNrID0gbmV3IEV2ZW50RW1pdHRlcjxUcmVlVGFibGVJdGVtPFQ+PigpO1xuICBASW5wdXQoKSB0YWJsZUNsYXNzID0gJyc7XG5cbiAgdHJlZVRhYmxlSXRlbVNoZWxsczogVHJlZVRhYmxlSXRlbVNoZWxsPFQ+W10gPSBbXTtcbiAgb3RoZXJJdGVtczogVHJlZVRhYmxlSXRlbTxUPltdID0gW107IC8vIHRvIGF2b2lkIHVuZXhwZWN0ZWQgaW5maW5pdGUgbG9vcGluZywgaWYgcGFyZW50IGlkcyBlcnJvcmVkIHdpdGggbmV2ZXItZW5kaW5nIGN5Y2xlLlxuXG4gIGNvbnN0cnVjdG9yKCkge1xuICB9XG5cbiAgaW5zZXJ0TGV2ZWxJdGVtc0ZvcihpbmRleDogbnVtYmVyLCBwYXJlbnRTaGVsbDogVHJlZVRhYmxlSXRlbVNoZWxsPFQ+LCBsZXZlbDogbnVtYmVyKSB7XG4gICAgY29uc3Qge2xldmVsSXRlbXMsIG90aGVySXRlbXN9ID0gSGVscGVyLnNlbGVjdExldmVsSXRlbXModGhpcy5vdGhlckl0ZW1zLCBwYXJlbnRTaGVsbC5pdGVtKTtcbiAgICBpZiAobGV2ZWxJdGVtcy5sZW5ndGggPCAxKSB7IHJldHVybjsgfVxuXG4gICAgY29uc3QgbGV2ZWxJdGVtczIgPSB0aGlzLnNvcnRGdW5jdGlvbiA/IEhlbHBlci5zb3J0SXRlbXMobGV2ZWxJdGVtcywgdGhpcy5zb3J0RnVuY3Rpb24pIDogbGV2ZWxJdGVtcztcblxuICAgIGNvbnN0IG5ld0xldmVsU2hlbGxzID0gSGVscGVyLml0ZW1zVG9TaGVsbHMobGV2ZWxJdGVtczIsIGxldmVsLCBwYXJlbnRTaGVsbCk7XG4gICAgdGhpcy50cmVlVGFibGVJdGVtU2hlbGxzID0gSGVscGVyLmFycmF5SW5zZXJ0SXRlbXModGhpcy50cmVlVGFibGVJdGVtU2hlbGxzLCBpbmRleCwgbmV3TGV2ZWxTaGVsbHMpO1xuXG4gICAgdGhpcy5vdGhlckl0ZW1zID0gWy4uLm90aGVySXRlbXNdO1xuICAgIG5ld0xldmVsU2hlbGxzLmZvckVhY2goKHNoZWxsKSA9PiB7XG4gICAgICBzaGVsbC5oYXZlQ2hpbGRyZW4gPSB0aGlzLm90aGVySXRlbXMuc29tZShpID0+IGkucGFyZW50SWQgPT09IHNoZWxsLml0ZW0uaWQpO1xuICAgIH0pO1xuICB9XG5cbiAgdG9nZ2xlRXhwYW5kKGV2ZW50LCBzaGVsbDogVHJlZVRhYmxlSXRlbVNoZWxsPFQ+KSB7XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcblxuICAgIHNoZWxsLml0ZW0uaXNFeHBhbmRlZCA9ICFzaGVsbC5pdGVtLmlzRXhwYW5kZWQ7XG4gICAgdGhpcy50b2dnbGVFeHBhbmRJdGVtLmVtaXQoc2hlbGwuaXRlbSk7XG5cbiAgICBpZiAoc2hlbGwuaXRlbS5pc0V4cGFuZGVkKSB7XG4gICAgICBjb25zdCBzaGVsbEluZGV4ID0gdGhpcy50cmVlVGFibGVJdGVtU2hlbGxzLmZpbmRJbmRleChpID0+IGkuaXRlbS5pZCA9PT0gc2hlbGwuaXRlbS5pZCk7XG4gICAgICB0aGlzLmluc2VydExldmVsSXRlbXNGb3Ioc2hlbGxJbmRleCArIDEsIHNoZWxsLCBzaGVsbC5sZXZlbCArIDEpO1xuICAgIH1cblxuICAgIHRoaXMudXBkYXRlVmlzaWJpbGl0eSgpO1xuICB9XG5cbiAgbG9jYWxSb3dDbGljayhzaGVsbDogVHJlZVRhYmxlSXRlbVNoZWxsPFQ+KSB7XG4gICAgdGhpcy5yb3dDbGljay5lbWl0KHNoZWxsLml0ZW0pO1xuICB9XG5cbiAgaXNTZWxlY3RlZCQoc2hlbGw6IFRyZWVUYWJsZUl0ZW1TaGVsbDxUPik6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IGRhdGEgPSBzaGVsbC5pdGVtLmRhdGE7XG4gICAgaWYgKGRhdGEgJiYgdGhpcy5pc1NlbGVjdGVkRnVuYyQpIHtcbiAgICAgIHJldHVybiB0aGlzLmlzU2VsZWN0ZWRGdW5jJChkYXRhKTtcbiAgICB9XG4gICAgcmV0dXJuIG9mKGZhbHNlKTtcbiAgfVxuXG4gIHByaXZhdGUgdXBkYXRlVmlzaWJpbGl0eSgpIHtcbiAgICB0aGlzLnRyZWVUYWJsZUl0ZW1TaGVsbHMuZm9yRWFjaChzaCA9PiB7XG4gICAgICBzaC5pc1Zpc2libGUgPSBIZWxwZXIuYWxsUGFyZW50c0V4cGFuZGVkKHNoKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8vIGV4dGVybmFsIGZ1bmN0aW9uc1xuICBjbGVhckl0ZW1zKCkge1xuICAgIHRoaXMudHJlZVRhYmxlSXRlbVNoZWxscyA9IFtdO1xuICAgIHRoaXMub3RoZXJJdGVtcyA9IFtdO1xuICB9XG5cbiAgYXBwZW5kSXRlbXMoaXRlbXM6IFRyZWVUYWJsZUl0ZW08VD5bXSkge1xuICAgIHRoaXMub3RoZXJJdGVtcyA9IFsuLi50aGlzLm90aGVySXRlbXMsIC4uLml0ZW1zXTtcblxuICAgIGNvbnN0IHJvb3Q6IFRyZWVUYWJsZUl0ZW1TaGVsbDxUPiAgPSB7IGl0ZW06IHtpZDogbnVsbH0gfSBhcyBUcmVlVGFibGVJdGVtU2hlbGw8VD47XG4gICAgdGhpcy5pbnNlcnRMZXZlbEl0ZW1zRm9yKDAsIHJvb3QsIDApO1xuICAgIHRoaXMudHJlZVRhYmxlSXRlbVNoZWxscy5mb3JFYWNoKHNoZWxsID0+IHtcbiAgICAgIGlmIChzaGVsbC5pdGVtLmlzRXhwYW5kZWQpIHtcbiAgICAgICAgY29uc3Qgc2hlbGxJbmRleCA9IHRoaXMudHJlZVRhYmxlSXRlbVNoZWxscy5maW5kSW5kZXgoaSA9PiBpLml0ZW0uaWQgPT09IHNoZWxsLml0ZW0uaWQpO1xuICAgICAgICB0aGlzLmluc2VydExldmVsSXRlbXNGb3Ioc2hlbGxJbmRleCArIDEsIHNoZWxsLCBzaGVsbC5sZXZlbCArIDEpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc2hlbGwuaGF2ZUNoaWxkcmVuID0gdGhpcy5vdGhlckl0ZW1zLnNvbWUoaSA9PiBpLnBhcmVudElkID09PSBzaGVsbC5pdGVtLmlkKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIC8vIGZpeCBoYXZlIGNoaWxkcmVuIGZvciBhbHJlYWR5IGFkZGVkIGl0ZW1zLCBidXQgcmVzZXRlZCBhYm92ZSBhZGRpbmcgY29kZVxuICAgIHRoaXMudHJlZVRhYmxlSXRlbVNoZWxscy5mb3JFYWNoKHNoZWxsID0+IHtcbiAgICAgIGlmICghc2hlbGwuaGF2ZUNoaWxkcmVuKSB7XG4gICAgICAgIHNoZWxsLmhhdmVDaGlsZHJlbiA9IHRoaXMuaXNFeGlzdHNDaGlsZHJlbkZvcihzaGVsbC5pdGVtLmlkKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHRoaXMudXBkYXRlVmlzaWJpbGl0eSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBpc0V4aXN0c0NoaWxkcmVuRm9yKGl0ZW1JZDogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMudHJlZVRhYmxlSXRlbVNoZWxsc1xuICAgICAgLnNvbWUoaSA9PiB7XG4gICAgICAgIGlmICghaSkgeyByZXR1cm4gZmFsc2U7IH1cbiAgICAgICAgaWYgKCFpLnBhcmVudCkgeyByZXR1cm4gZmFsc2U7IH1cbiAgICAgICAgaWYgKCFpLnBhcmVudC5pdGVtKSB7IHJldHVybiBmYWxzZTsgfVxuICAgICAgICByZXR1cm4gaS5wYXJlbnQuaXRlbS5pZCA9PT0gaXRlbUlkO1xuICAgICAgfSk7XG4gIH1cblxuICBnZXRDaGlsZHJlbihpZDogc3RyaW5nKTogVHJlZVRhYmxlSXRlbTxUPltdIHtcbiAgICBjb25zdCBmcm9tU2hlbGxzID0gdGhpcy50cmVlVGFibGVJdGVtU2hlbGxzXG4gICAgICAubWFwKGkgPT4gaS5pdGVtKVxuICAgICAgLmZpbHRlcihpID0+IGkucGFyZW50SWQgPT09IGlkKTtcbiAgICBjb25zdCBmcm9tSXRlbXMgPSB0aGlzLm90aGVySXRlbXMuZmlsdGVyKGkgPT4gaS5wYXJlbnRJZCA9PT0gaWQpO1xuICAgIHJldHVybiBbLi4uZnJvbVNoZWxscywgLi4uZnJvbUl0ZW1zXTtcbiAgfVxuXG4gIHJlbW92ZUl0ZW0oaWQ6IHN0cmluZykge1xuICAgIHRoaXMudHJlZVRhYmxlSXRlbVNoZWxscyA9IEhlbHBlci5yZW1vdmVBbGxTaGVsbHNDaGlsZHJlbihbLi4udGhpcy50cmVlVGFibGVJdGVtU2hlbGxzXSwgaWQpO1xuICAgIGNvbnN0IHNoZWxsID0gdGhpcy50cmVlVGFibGVJdGVtU2hlbGxzLmZpbmQoaSA9PiBpLml0ZW0uaWQgPT09IGlkKTtcbiAgICBpZiAoc2hlbGwgJiYgc2hlbGwucGFyZW50KSB7XG4gICAgICBjb25zdCBwYXJlbnRJZCA9IHNoZWxsLnBhcmVudC5pdGVtLmlkO1xuICAgICAgY29uc3QgaXNFeGlzdENoaWxkU2hlbGwgPSB0aGlzLnRyZWVUYWJsZUl0ZW1TaGVsbHMuc29tZShpID0+IGkuaXRlbS5wYXJlbnRJZCA9PT0gcGFyZW50SWQpO1xuICAgICAgY29uc3QgaXNFeGlzdENoaWxkID0gdGhpcy5vdGhlckl0ZW1zLnNvbWUoaSA9PiBpLnBhcmVudElkID09PSBwYXJlbnRJZCk7XG4gICAgICBzaGVsbC5wYXJlbnQuaGF2ZUNoaWxkcmVuID0gaXNFeGlzdENoaWxkU2hlbGwgfHwgaXNFeGlzdENoaWxkO1xuICAgIH1cblxuICAgIHRoaXMudHJlZVRhYmxlSXRlbVNoZWxscyA9IHRoaXMudHJlZVRhYmxlSXRlbVNoZWxscy5maWx0ZXIoaSA9PiBpLml0ZW0uaWQgIT09IGlkKTtcblxuICAgIHRoaXMub3RoZXJJdGVtcyA9IEhlbHBlci5yZW1vdmVBbGxDaGlsZHJlbihbLi4udGhpcy5vdGhlckl0ZW1zXSwgaWQpO1xuICAgIHRoaXMub3RoZXJJdGVtcyA9IHRoaXMub3RoZXJJdGVtc1xuICAgICAgLmZpbHRlcihpID0+IGkuaWQgIT09IGlkKTtcbiAgfVxuXG4gIHNvcnQoKSB7XG4gICAgaWYgKCF0aGlzLnNvcnRGdW5jdGlvbikge1xuICAgICAgY29uc29sZS53YXJuKCdjbmctdHJlZS10YWJsZTogdGhlcmUgaXMgbm90IHNvcnRGdW5jdGlvbicpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMudHJlZVRhYmxlSXRlbVNoZWxscyA9IFNvcnRIZWxwZXIuZ2V0U29ydGVkKHRoaXMudHJlZVRhYmxlSXRlbVNoZWxscywgdGhpcy5zb3J0RnVuY3Rpb24pO1xuICB9XG59XG4iLCI8dGFibGUgW25nQ2xhc3NdPVwidGFibGVDbGFzc1wiPlxyXG4gIDx0aGVhZD5cclxuICA8dHI+XHJcbiAgICA8dGggKm5nRm9yPVwibGV0IGNvbHVtbiBvZiBjb2x1bW5zOyBsZXQgbGFzdD1sYXN0O1wiXHJcbiAgICAgICAgW2FwcFJlc2l6YWJsZVRhYmxlQ29sdW1uXT1cIiFsYXN0XCJcclxuICAgICAgICBjbGFzcz1cInRyZWUtaXRlbS1oZWFkZXJcIlxyXG4gICAgICAgIFtuZ0NsYXNzXT1cIltjb2x1bW4udGhDbGFzcyB8fCAnJ11cIj5cclxuICAgICAge3tjb2x1bW4udGl0bGVUZW1wbGF0ZSA/ICcnIDogY29sdW1uLnRpdGxlfX1cclxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cImNvbHVtbi50aXRsZVRlbXBsYXRlOyBjb250ZXh0OiB7ICRpbXBsaWNpdDogY29sdW1uLnRpdGxlIH1cIj5cclxuICAgICAgPC9uZy1jb250YWluZXI+XHJcbiAgICA8L3RoPlxyXG4gIDwvdHI+XHJcbiAgPC90aGVhZD5cclxuXHJcbiAgPHRib2R5PlxyXG4gIDx0ciBbbmdDbGFzc109XCJ7J3NlbGVjdGVkJzogKGlzU2VsZWN0ZWQkKHNoZWxsKSB8IGFzeW5jKX1cIlxyXG4gICAgICAqbmdGb3I9XCJsZXQgc2hlbGwgb2YgdHJlZVRhYmxlSXRlbVNoZWxsc1wiXHJcbiAgICAgIFtoaWRkZW5dPVwiIXNoZWxsLmlzVmlzaWJsZVwiIChjbGljayk9XCJsb2NhbFJvd0NsaWNrKHNoZWxsKVwiPlxyXG5cclxuICAgIDx0ZCBbY2xhc3NOYW1lXT1cIid0cmVlLWl0ZW0gJyArIChjb25kaXRpb25hbENlbGxDbGFzc0Z1bmMgPyBjb25kaXRpb25hbENlbGxDbGFzc0Z1bmMoc2hlbGwuaXRlbSwgY29sdW1uKSA6ICcnKVwiXHJcbiAgICAgICAgKm5nRm9yPVwibGV0IGNvbHVtbiBvZiBjb2x1bW5zOyBsZXQgaSA9IGluZGV4O1wiPlxyXG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiaSA9PT0gMFwiPlxyXG4gICAgICAgIDxkaXYgY2xhc3M9XCJsZXZlbC1pbmRlbnRcIiAqbmdGb3I9XCJsZXQgZm9vIG9mIHNoZWxsLmxldmVsIHwgYXBwTnVtYmVyVG9BcnJheVwiPjwvZGl2PlxyXG4gICAgICAgIDxkaXYgY2xhc3M9XCJ0cmVlLWl0ZW0tYXJyb3dcIlxyXG4gICAgICAgICAgICAgW25nQ2xhc3NdPVwieydleHBhbmRlZCc6IHNoZWxsLml0ZW0uaXNFeHBhbmRlZCwgJ3Zpcy1oaWRkZW4nOiAhc2hlbGwuaGF2ZUNoaWxkcmVufVwiXHJcbiAgICAgICAgICAgICAoY2xpY2spPVwidG9nZ2xlRXhwYW5kKCRldmVudCwgc2hlbGwpXCJcclxuICAgICAgICA+PGRpdiBjbGFzcz1cImFycm93XCI+PC9kaXY+XHJcbiAgICAgICAgPC9kaXY+XHJcbiAgICAgIDwvbmctY29udGFpbmVyPlxyXG4gICAgICA8bmctY29udGFpbmVyICpuZ1RlbXBsYXRlT3V0bGV0PVwiY29sdW1uLnRlbXBsYXRlOyBjb250ZXh0OiB7ICRpbXBsaWNpdDogc2hlbGwuaXRlbSB9XCI+XHJcbiAgICAgIDwvbmctY29udGFpbmVyPlxyXG4gICAgPC90ZD5cclxuICA8L3RyPlxyXG4gIDwvdGJvZHk+XHJcblxyXG48L3RhYmxlPlxyXG4iXX0=