UNPKG

@tangential/admin-console

Version:
134 lines 29 kB
import { LiveAnnouncer } from '@angular/cdk/a11y'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ViewChild, ViewEncapsulation } from '@angular/core'; import { AuthRole, AdminService } from '@tangential/authorization-service'; import { NameGenerator } from '@tangential/core'; import { MatSort } from '@angular/material/sort'; import { SelectionModel } from '@angular/cdk/collections'; import { MatTableDataSource } from '@angular/material/table'; import { AdminConsoleParentPage } from '../_parent/admin-console-parent.page'; import * as i0 from "@angular/core"; import * as i1 from "../_parent/admin-console-parent.page"; import * as i2 from "@tangential/authorization-service"; import * as i3 from "@angular/cdk/a11y"; import * as i4 from "@angular/material/checkbox"; import * as i5 from "@tangential/components"; import * as i6 from "@angular/material/table"; import * as i7 from "@angular/material/sort"; import * as i8 from "@angular/common"; export class RoleManagerPage { constructor(parent, adminService, _liveAnnouncer, changeDetectorRef) { this.parent = parent; this.adminService = adminService; this._liveAnnouncer = _liveAnnouncer; this.changeDetectorRef = changeDetectorRef; this.displayedColumns = ['select', '$key', 'description', 'createdMils', 'editedMils']; this.dataSource = new MatTableDataSource(); this.selection = new SelectionModel(true, []); this.rows = []; this.selected = []; } ngOnInit() { this.parent.auth$.subscribe({ next: (v) => { this.rows = v.settings.roles; this.dataSource = new MatTableDataSource(v.settings.roles); this.dataSource.sort = this.sort; this.changeDetectorRef.markForCheck(); } }); } get nextItemIndex() { let idx = 1; if (this.rows && this.rows.length) { idx = (this.rows[this.rows.length - 1].orderIndex + 1); } return idx; } grantPermission(role, permission) { this.adminService.grantPermissionOnRole(role.$key, permission.$key).catch((reason) => { console.error('RoleManagerComponent', 'could not grant permission', reason); }); } revokePermission(role, permission) { this.adminService.revokePermissionOnRole(role.$key, permission.$key).catch((reason) => { console.error('RoleManagerComponent', 'could not revoke permission', reason); }); } onAddItemAction() { const role = AuthRole.from({ $key: NameGenerator.generate(), orderIndex: this.nextItemIndex }); this.adminService.addRole(role).catch((reason) => { console.error('RoleManagerComponent', 'error adding role', reason); throw new Error(reason); }); } onRemove(key) { this.adminService.removeRole(key).catch((reason) => { console.error('RoleManagerComponent', 'error removing role', reason); throw new Error(reason); }); } onRemoveSelectedAction(keys) { keys.forEach((key) => { this.adminService.removeRole(key).catch((reason) => { console.error('RoleManagerComponent', 'error removing role', reason); throw new Error(reason); }); }); } onItemChange(role) { this.adminService.updateRole(role).catch((reason) => { console.log('RoleManagerComponent', 'error updating role', reason); throw new Error(reason); }); } /** * Borrowed directly from Angular Material examples: https://material.angular.io/components/table/overview */ /** Whether the number of selected elements matches the total number of rows. */ isAllSelected() { const numSelected = this.selection.selected.length; const numRows = this.dataSource.data.length; return numSelected === numRows; } /** Selects all rows if they are not all selected; otherwise clear selection. */ toggleAllRows() { if (this.isAllSelected()) { this.selection.clear(); return; } this.selection.select(...this.dataSource.data); } /** The label for the checkbox on the passed row */ checkboxLabel(row) { if (!row) { return `${this.isAllSelected() ? 'deselect' : 'select'} all`; } return `${this.selection.isSelected(row) ? 'deselect' : 'select'} ${row.description}`; } /** Announce the change in sort state for assistive technology. */ announceSortChange(sortState) { // This example uses English messages. If your application supports // multiple language, you would internationalize these strings. // Furthermore, you can customize the message to add additional // details about the values being sorted. if (sortState.direction) { this._liveAnnouncer.announce(`Sorted ${sortState.direction}ending`); } else { this._liveAnnouncer.announce('Sorting cleared'); } } } RoleManagerPage.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: RoleManagerPage, deps: [{ token: i1.AdminConsoleParentPage }, { token: i2.AdminService }, { token: i3.LiveAnnouncer }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); RoleManagerPage.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.10", type: RoleManagerPage, selector: "tanj-role-manager-page", viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0, template: "<tanj-page-body>\n <table mat-table matSort (matSortChange)=\"announceSortChange($event)\" [dataSource]=\"dataSource\" class=\"mat-elevation-z8 tng-user-mgr-table\">\n\n <!-- Checkbox Column -->\n <ng-container matColumnDef=\"select\">\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\">\n </mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" >\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? selection.toggle(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\">\n </mat-checkbox>\n </td>\n </ng-container>\n\n <!-- UID Column -->\n <ng-container matColumnDef=\"$key\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Permission ID\"> Permission ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.$key}} </td>\n </ng-container>\n\n <!-- User name Column -->\n <ng-container matColumnDef=\"description\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Description\"> Description </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.description}} </td>\n </ng-container>\n\n <!-- User Email Column -->\n <ng-container matColumnDef=\"createdMils\" >\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Created Date\"> Created </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.createdMils | date:'yyyy-MM~dd, HH:mm:ss'}} </td>\n </ng-container>\n\n <!-- Last Sign in Column -->\n <ng-container matColumnDef=\"editedMils\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Last Modified\"> Last Modified </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.editedMils | date:'yyyy-MM~dd, HH:mm:ss' }} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"selection.toggle(row)\"></tr>\n </table>\n\n</tanj-page-body>\n", dependencies: [{ kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "component", type: i5.PageBodyComponent, selector: "tanj-page-body", inputs: ["suppressHeaderShim", "flex", "layout", "layoutAlign"] }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i7.MatSort, selector: "[matSort]", inputs: ["matSortDisabled", "matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i7.MatSortHeader, selector: "[mat-sort-header]", inputs: ["disabled", "mat-sort-header", "arrowPosition", "start", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "pipe", type: i8.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: RoleManagerPage, decorators: [{ type: Component, args: [{ selector: 'tanj-role-manager-page', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<tanj-page-body>\n <table mat-table matSort (matSortChange)=\"announceSortChange($event)\" [dataSource]=\"dataSource\" class=\"mat-elevation-z8 tng-user-mgr-table\">\n\n <!-- Checkbox Column -->\n <ng-container matColumnDef=\"select\">\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\">\n </mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" >\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? selection.toggle(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\">\n </mat-checkbox>\n </td>\n </ng-container>\n\n <!-- UID Column -->\n <ng-container matColumnDef=\"$key\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Permission ID\"> Permission ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.$key}} </td>\n </ng-container>\n\n <!-- User name Column -->\n <ng-container matColumnDef=\"description\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Description\"> Description </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.description}} </td>\n </ng-container>\n\n <!-- User Email Column -->\n <ng-container matColumnDef=\"createdMils\" >\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Created Date\"> Created </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.createdMils | date:'yyyy-MM~dd, HH:mm:ss'}} </td>\n </ng-container>\n\n <!-- Last Sign in Column -->\n <ng-container matColumnDef=\"editedMils\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Last Modified\"> Last Modified </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.editedMils | date:'yyyy-MM~dd, HH:mm:ss' }} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"selection.toggle(row)\"></tr>\n </table>\n\n</tanj-page-body>\n" }] }], ctorParameters: function () { return [{ type: i1.AdminConsoleParentPage }, { type: i2.AdminService }, { type: i3.LiveAnnouncer }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { sort: [{ type: ViewChild, args: [MatSort] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm9sZS1tYW5hZ2VyLnBhZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy90YW5nZW50aWFsL2FkbWluLWNvbnNvbGUvc3JjL2xpYi9wYWdlcy9yb2xlcy9yb2xlLW1hbmFnZXIucGFnZS50cyIsIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RhbmdlbnRpYWwvYWRtaW4tY29uc29sZS9zcmMvbGliL3BhZ2VzL3JvbGVzL3JvbGUtbWFuYWdlci5wYWdlLmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFDLGFBQWEsRUFBQyxNQUFNLG1CQUFtQixDQUFBO0FBQy9DLE9BQU8sRUFBQyx1QkFBdUIsRUFBRSxpQkFBaUIsRUFBRSxTQUFTLEVBQVUsU0FBUyxFQUFFLGlCQUFpQixFQUFDLE1BQU0sZUFBZSxDQUFBO0FBQ3pILE9BQU8sRUFBaUIsUUFBUSxFQUFFLFlBQVksRUFBQyxNQUFNLG1DQUFtQyxDQUFBO0FBQ3hGLE9BQU8sRUFBQyxhQUFhLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQTtBQUU5QyxPQUFPLEVBQUMsT0FBTyxFQUFPLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxFQUFDLGNBQWMsRUFBQyxNQUFNLDBCQUEwQixDQUFDO0FBQ3hELE9BQU8sRUFBQyxrQkFBa0IsRUFBQyxNQUFNLHlCQUF5QixDQUFDO0FBQzNELE9BQU8sRUFBQyxzQkFBc0IsRUFBQyxNQUFNLHNDQUFzQyxDQUFBOzs7Ozs7Ozs7O0FBUzNFLE1BQU0sT0FBTyxlQUFlO0lBUzFCLFlBQW9CLE1BQThCLEVBQzlCLFlBQXlCLEVBQ3pCLGNBQTZCLEVBQzdCLGlCQUFvQztRQUhwQyxXQUFNLEdBQU4sTUFBTSxDQUF3QjtRQUM5QixpQkFBWSxHQUFaLFlBQVksQ0FBYTtRQUN6QixtQkFBYyxHQUFkLGNBQWMsQ0FBZTtRQUM3QixzQkFBaUIsR0FBakIsaUJBQWlCLENBQW1CO1FBWHhELHFC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WyJpbXBvcnQge0xpdmVBbm5vdW5jZXJ9IGZyb20gJ0Bhbmd1bGFyL2Nkay9hMTF5J1xuaW1wb3J0IHtDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ2hhbmdlRGV0ZWN0b3JSZWYsIENvbXBvbmVudCwgT25Jbml0LCBWaWV3Q2hpbGQsIFZpZXdFbmNhcHN1bGF0aW9ufSBmcm9tICdAYW5ndWxhci9jb3JlJ1xuaW1wb3J0IHtBdXRoUGVybWlzc2lvbiwgQXV0aFJvbGUsIEFkbWluU2VydmljZX0gZnJvbSAnQHRhbmdlbnRpYWwvYXV0aG9yaXphdGlvbi1zZXJ2aWNlJ1xuaW1wb3J0IHtOYW1lR2VuZXJhdG9yfSBmcm9tICdAdGFuZ2VudGlhbC9jb3JlJ1xuXG5pbXBvcnQge01hdFNvcnQsIFNvcnR9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3NvcnQnO1xuaW1wb3J0IHtTZWxlY3Rpb25Nb2RlbH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2NvbGxlY3Rpb25zJztcbmltcG9ydCB7TWF0VGFibGVEYXRhU291cmNlfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC90YWJsZSc7XG5pbXBvcnQge0FkbWluQ29uc29sZVBhcmVudFBhZ2V9IGZyb20gJy4uL19wYXJlbnQvYWRtaW4tY29uc29sZS1wYXJlbnQucGFnZSdcblxuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICAgICAgICAndGFuai1yb2xlLW1hbmFnZXItcGFnZScsXG4gIHRlbXBsYXRlVXJsOiAgICAgJy4vcm9sZS1tYW5hZ2VyLnBhZ2UuaHRtbCcsXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBlbmNhcHN1bGF0aW9uOiAgIFZpZXdFbmNhcHN1bGF0aW9uLk5vbmVcbn0pXG5leHBvcnQgY2xhc3MgUm9sZU1hbmFnZXJQYWdlIGltcGxlbWVudHMgT25Jbml0IHtcbiAgZGlzcGxheWVkQ29sdW1uczogc3RyaW5nW10gPSBbJ3NlbGVjdCcsICcka2V5JywgJ2Rlc2NyaXB0aW9uJywgJ2NyZWF0ZWRNaWxzJywgJ2VkaXRlZE1pbHMnXTtcbiAgZGF0YVNvdXJjZSA9IG5ldyBNYXRUYWJsZURhdGFTb3VyY2U8QXV0aFJvbGU+KCk7XG4gIHNlbGVjdGlvbiA9IG5ldyBTZWxlY3Rpb25Nb2RlbDxBdXRoUm9sZT4odHJ1ZSwgW10pO1xuXG4gIHJvd3M6IEF1dGhSb2xlW10gPSBbXTtcbiAgc2VsZWN0ZWQ6IGFueVtdID0gW107XG5cblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHBhcmVudDogQWRtaW5Db25zb2xlUGFyZW50UGFnZSxcbiAgICAgICAgICAgICAgcHJpdmF0ZSBhZG1pblNlcnZpY2U6QWRtaW5TZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIF9saXZlQW5ub3VuY2VyOiBMaXZlQW5ub3VuY2VyLFxuICAgICAgICAgICAgICBwcml2YXRlIGNoYW5nZURldGVjdG9yUmVmOiBDaGFuZ2VEZXRlY3RvclJlZikge1xuICB9XG5cbiAgQFZpZXdDaGlsZChNYXRTb3J0KSBzb3J0OiBNYXRTb3J0O1xuXG4gIG5nT25Jbml0KCkge1xuICAgIHRoaXMucGFyZW50LmF1dGgkLnN1YnNjcmliZSh7XG4gICAgICBuZXh0OiAodikgPT4ge1xuICAgICAgICB0aGlzLnJvd3MgPSB2LnNldHRpbmdzLnJvbGVzXG4gICAgICAgIHRoaXMuZGF0YVNvdXJjZSA9IG5ldyBNYXRUYWJsZURhdGFTb3VyY2U8QXV0aFJvbGU+KHYuc2V0dGluZ3Mucm9sZXMpO1xuICAgICAgICB0aGlzLmRhdGFTb3VyY2Uuc29ydCA9IHRoaXMuc29ydDtcbiAgICAgICAgdGhpcy5jaGFuZ2VEZXRlY3RvclJlZi5tYXJrRm9yQ2hlY2soKVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICBnZXQgbmV4dEl0ZW1JbmRleCgpOiBudW1iZXIge1xuICAgIGxldCBpZHggPSAxXG4gICAgaWYgKHRoaXMucm93cyAmJiB0aGlzLnJvd3MubGVuZ3RoKSB7XG4gICAgICBpZHggPSAodGhpcy5yb3dzW3RoaXMucm93cy5sZW5ndGggLSAxXS5vcmRlckluZGV4ICsgMSlcbiAgICB9XG4gICAgcmV0dXJuIGlkeFxuICB9XG5cbiAgZ3JhbnRQZXJtaXNzaW9uKHJvbGU6IEF1dGhSb2xlLCBwZXJtaXNzaW9uOiBBdXRoUGVybWlzc2lvbikge1xuICAgIHRoaXMuYWRtaW5TZXJ2aWNlLmdyYW50UGVybWlzc2lvbk9uUm9sZShyb2xlLiRrZXksIHBlcm1pc3Npb24uJGtleSkuY2F0Y2goKHJlYXNvbikgPT4ge1xuICAgICAgY29uc29sZS5lcnJvcignUm9sZU1hbmFnZXJDb21wb25lbnQnLCAnY291bGQgbm90IGdyYW50IHBlcm1pc3Npb24nLCByZWFzb24pXG4gICAgfSlcbiAgfVxuXG4gIHJldm9rZVBlcm1pc3Npb24ocm9sZTogQXV0aFJvbGUsIHBlcm1pc3Npb246IEF1dGhQZXJtaXNzaW9uKSB7XG4gICAgdGhpcy5hZG1pblNlcnZpY2UucmV2b2tlUGVybWlzc2lvbk9uUm9sZShyb2xlLiRrZXksIHBlcm1pc3Npb24uJGtleSkuY2F0Y2goKHJlYXNvbikgPT4ge1xuICAgICAgY29uc29sZS5lcnJvcignUm9sZU1hbmFnZXJDb21wb25lbnQnLCAnY291bGQgbm90IHJldm9rZSBwZXJtaXNzaW9uJywgcmVhc29uKVxuICAgIH0pXG4gIH1cblxuICBvbkFkZEl0ZW1BY3Rpb24oKSB7XG4gICAgY29uc3Qgcm9sZSA9IEF1dGhSb2xlLmZyb20oe1xuICAgICAgJGtleTogICAgICAgTmFtZUdlbmVyYXRvci5nZW5lcmF0ZSgpLFxuICAgICAgb3JkZXJJbmRleDogdGhpcy5uZXh0SXRlbUluZGV4XG4gICAgfSlcbiAgICB0aGlzLmFkbWluU2VydmljZS5hZGRSb2xlKHJvbGUpLmNhdGNoKChyZWFzb24pID0+IHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ1JvbGVNYW5hZ2VyQ29tcG9uZW50JywgJ2Vycm9yIGFkZGluZyByb2xlJywgcmVhc29uKVxuICAgICAgdGhyb3cgbmV3IEVycm9yKHJlYXNvbilcbiAgICB9KVxuICB9XG5cbiAgb25SZW1vdmUoa2V5OiBzdHJpbmcpIHtcbiAgICB0aGlzLmFkbWluU2VydmljZS5yZW1vdmVSb2xlKGtleSkuY2F0Y2goKHJlYXNvbikgPT4ge1xuICAgICAgY29uc29sZS5lcnJvcignUm9sZU1hbmFnZXJDb21wb25lbnQnLCAnZXJyb3IgcmVtb3Zpbmcgcm9sZScsIHJlYXNvbilcbiAgICAgIHRocm93IG5ldyBFcnJvcihyZWFzb24pXG4gICAgfSlcbiAgfVxuXG4gIG9uUmVtb3ZlU2VsZWN0ZWRBY3Rpb24oa2V5czogc3RyaW5nW10pIHtcbiAgICBrZXlzLmZvckVhY2goKGtleSkgPT4ge1xuICAgICAgdGhpcy5hZG1pblNlcnZpY2UucmVtb3ZlUm9sZShrZXkpLmNhdGNoKChyZWFzb24pID0+IHtcbiAgICAgICAgY29uc29sZS5lcnJvcignUm9sZU1hbmFnZXJDb21wb25lbnQnLCAnZXJyb3IgcmVtb3Zpbmcgcm9sZScsIHJlYXNvbilcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKHJlYXNvbilcbiAgICAgIH0pXG4gICAgfSlcbiAgfVxuXG4gIG9uSXRlbUNoYW5nZShyb2xlOiBBdXRoUm9sZSkge1xuICAgIHRoaXMuYWRtaW5TZXJ2aWNlLnVwZGF0ZVJvbGUocm9sZSkuY2F0Y2goKHJlYXNvbikgPT4ge1xuICAgICAgY29uc29sZS5sb2coJ1JvbGVNYW5hZ2VyQ29tcG9uZW50JywgJ2Vycm9yIHVwZGF0aW5nIHJvbGUnLCByZWFzb24pXG4gICAgICB0aHJvdyBuZXcgRXJyb3IocmVhc29uKVxuICAgIH0pXG5cbiAgfVxuXG4gIC8qKlxuICAgKiBCb3Jyb3dlZCBkaXJlY3RseSBmcm9tIEFuZ3VsYXIgTWF0ZXJpYWwgZXhhbXBsZXM6IGh0dHBzOi8vbWF0ZXJpYWwuYW5ndWxhci5pby9jb21wb25lbnRzL3RhYmxlL292ZXJ2aWV3XG4gICAqL1xuICAvKiogV2hldGhlciB0aGUgbnVtYmVyIG9mIHNlbGVjdGVkIGVsZW1lbnRzIG1hdGNoZXMgdGhlIHRvdGFsIG51bWJlciBvZiByb3dzLiAqL1xuICBpc0FsbFNlbGVjdGVkKCkge1xuICAgIGNvbnN0IG51bVNlbGVjdGVkID0gdGhpcy5zZWxlY3Rpb24uc2VsZWN0ZWQubGVuZ3RoO1xuICAgIGNvbnN0IG51bVJvd3MgPSB0aGlzLmRhdGFTb3VyY2UuZGF0YS5sZW5ndGg7XG4gICAgcmV0dXJuIG51bVNlbGVjdGVkID09PSBudW1Sb3dzO1xuICB9XG5cbiAgLyoqIFNlbGVjdHMgYWxsIHJvd3MgaWYgdGhleSBhcmUgbm90IGFsbCBzZWxlY3RlZDsgb3RoZXJ3aXNlIGNsZWFyIHNlbGVjdGlvbi4gKi9cbiAgdG9nZ2xlQWxsUm93cygpIHtcbiAgICBpZiAodGhpcy5pc0FsbFNlbGVjdGVkKCkpIHtcbiAgICAgIHRoaXMuc2VsZWN0aW9uLmNsZWFyKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuc2VsZWN0aW9uLnNlbGVjdCguLi50aGlzLmRhdGFTb3VyY2UuZGF0YSk7XG4gIH1cblxuICAvKiogVGhlIGxhYmVsIGZvciB0aGUgY2hlY2tib3ggb24gdGhlIHBhc3NlZCByb3cgKi9cbiAgY2hlY2tib3hMYWJlbChyb3c/OiBBdXRoUm9sZSk6IHN0cmluZyB7XG4gICAgaWYgKCFyb3cpIHtcbiAgICAgIHJldHVybiBgJHt0aGlzLmlzQWxsU2VsZWN0ZWQoKSA/ICdkZXNlbGVjdCcgOiAnc2VsZWN0J30gYWxsYDtcbiAgICB9XG4gICAgcmV0dXJuIGAke3RoaXMuc2VsZWN0aW9uLmlzU2VsZWN0ZWQocm93KSA/ICdkZXNlbGVjdCcgOiAnc2VsZWN0J30gJHtyb3cuZGVzY3JpcHRpb259YDtcbiAgfVxuXG4gIC8qKiBBbm5vdW5jZSB0aGUgY2hhbmdlIGluIHNvcnQgc3RhdGUgZm9yIGFzc2lzdGl2ZSB0ZWNobm9sb2d5LiAqL1xuICBhbm5vdW5jZVNvcnRDaGFuZ2Uoc29ydFN0YXRlOiBTb3J0KSB7XG4gICAgLy8gVGhpcyBleGFtcGxlIHVzZXMgRW5nbGlzaCBtZXNzYWdlcy4gSWYgeW91ciBhcHBsaWNhdGlvbiBzdXBwb3J0c1xuICAgIC8vIG11bHRpcGxlIGxhbmd1YWdlLCB5b3Ugd291bGQgaW50ZXJuYXRpb25hbGl6ZSB0aGVzZSBzdHJpbmdzLlxuICAgIC8vIEZ1cnRoZXJtb3JlLCB5b3UgY2FuIGN1c3RvbWl6ZSB0aGUgbWVzc2FnZSB0byBhZGQgYWRkaXRpb25hbFxuICAgIC8vIGRldGFpbHMgYWJvdXQgdGhlIHZhbHVlcyBiZWluZyBzb3J0ZWQuXG4gICAgaWYgKHNvcnRTdGF0ZS5kaXJlY3Rpb24pIHtcbiAgICAgIHRoaXMuX2xpdmVBbm5vdW5jZXIuYW5ub3VuY2UoYFNvcnRlZCAke3NvcnRTdGF0ZS5kaXJlY3Rpb259ZW5kaW5nYCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuX2xpdmVBbm5vdW5jZXIuYW5ub3VuY2UoJ1NvcnRpbmcgY2xlYXJlZCcpO1xuICAgIH1cbiAgfVxuXG59XG4iLCI8dGFuai1wYWdlLWJvZHk+XG4gIDx0YWJsZSBtYXQtdGFibGUgbWF0U29ydCAobWF0U29ydENoYW5nZSk9XCJhbm5vdW5jZVNvcnRDaGFuZ2UoJGV2ZW50KVwiICBbZGF0YVNvdXJjZV09XCJkYXRhU291cmNlXCIgY2xhc3M9XCJtYXQtZWxldmF0aW9uLXo4IHRuZy11c2VyLW1nci10YWJsZVwiPlxuXG4gICAgPCEtLSBDaGVja2JveCBDb2x1bW4gLS0+XG4gICAgPG5nLWNvbnRhaW5lciBtYXRDb2x1bW5EZWY9XCJzZWxlY3RcIj5cbiAgICAgIDx0aCBtYXQtaGVhZGVyLWNlbGwgKm1hdEhlYWRlckNlbGxEZWY+XG4gICAgICAgIDxtYXQtY2hlY2tib3ggKGNoYW5nZSk9XCIkZXZlbnQgPyB0b2dnbGVBbGxSb3dzKCkgOiBudWxsXCJcbiAgICAgICAgICAgICAgICAgICAgICBbY2hlY2tlZF09XCJzZWxlY3Rpb24uaGFzVmFsdWUoKSAmJiBpc0FsbFNlbGVjdGVkKClcIlxuICAgICAgICAgICAgICAgICAgICAgIFtpbmRldGVybWluYXRlXT1cInNlbGVjdGlvbi5oYXNWYWx1ZSgpICYmICFpc0FsbFNlbGVjdGVkKClcIlxuICAgICAgICAgICAgICAgICAgICAgIFthcmlhLWxhYmVsXT1cImNoZWNrYm94TGFiZWwoKVwiPlxuICAgICAgICA8L21hdC1jaGVja2JveD5cbiAgICAgIDwvdGg+XG4gICAgICA8dGQgbWF0LWNlbGwgKm1hdENlbGxEZWY9XCJsZXQgcm93XCIgPlxuICAgICAgICA8bWF0LWNoZWNrYm94IChjbGljayk9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgICAgICAgICAgICAgICAgIChjaGFuZ2UpPVwiJGV2ZW50ID8gc2VsZWN0aW9uLnRvZ2dsZShyb3cpIDogbnVsbFwiXG4gICAgICAgICAgICAgICAgICAgICAgW2NoZWNrZWRdPVwic2VsZWN0aW9uLmlzU2VsZWN0ZWQocm93KVwiXG4gICAgICAgICAgICAgICAgICAgICAgW2FyaWEtbGFiZWxdPVwiY2hlY2tib3hMYWJlbChyb3cpXCI+XG4gICAgICAgIDwvbWF0LWNoZWNrYm94PlxuICAgICAgPC90ZD5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDwhLS0gVUlEIENvbHVtbiAtLT5cbiAgICA8bmctY29udGFpbmVyIG1hdENvbHVtbkRlZj1cIiRrZXlcIj5cbiAgICAgIDx0aCBtYXQtaGVhZGVyLWNlbGwgKm1hdEhlYWRlckNlbGxEZWYgbWF0LXNvcnQtaGVhZGVyIHNvcnRBY3Rpb25EZXNjcmlwdGlvbj1cIlNvcnQgYnkgUGVybWlzc2lvbiBJRFwiPiBQZXJtaXNzaW9uIElEIDwvdGg+XG4gICAgICA8dGQgbWF0LWNlbGwgKm1hdENlbGxEZWY9XCJsZXQgZWxlbWVudFwiPiB7e2VsZW1lbnQuJGtleX19IDwvdGQ+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8IS0tIFVzZXIgbmFtZSBDb2x1bW4gLS0+XG4gICAgPG5nLWNvbnRhaW5lciBtYXRDb2x1bW5EZWY9XCJkZXNjcmlwdGlvblwiPlxuICAgICAgPHRoIG1hdC1oZWFkZXItY2VsbCAqbWF0SGVhZGVyQ2VsbERlZiBtYXQtc29ydC1oZWFkZXIgc29ydEFjdGlvbkRlc2NyaXB0aW9uPVwiU29ydCBieSBEZXNjcmlwdGlvblwiPiAgRGVzY3JpcHRpb24gPC90aD5cbiAgICAgIDx0ZCBtYXQtY2VsbCAqbWF0Q2VsbERlZj1cImxldCBlbGVtZW50XCI+IHt7ZWxlbWVudC5kZXNjcmlwdGlvbn19IDwvdGQ+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8IS0tIFVzZXIgRW1haWwgQ29sdW1uIC0tPlxuICAgIDxuZy1jb250YWluZXIgbWF0Q29sdW1uRGVmPVwiY3JlYXRlZE1pbHNcIiA+XG4gICAgICA8dGggbWF0LWhlYWRlci1jZWxsICptYXRIZWFkZXJDZWxsRGVmIG1hdC1zb3J0LWhlYWRlciBzb3J0QWN0aW9uRGVzY3JpcHRpb249XCJTb3J0IGJ5IENyZWF0ZWQgRGF0ZVwiPiBDcmVhdGVkIDwvdGg+XG4gICAgICA8dGQgbWF0LWNlbGwgKm1hdENlbGxEZWY9XCJsZXQgZWxlbWVudFwiPiB7e2VsZW1lbnQuY3JlYXRlZE1pbHMgIHwgZGF0ZToneXl5eS1NTX5kZCwgSEg6bW06c3MnfX0gPC90ZD5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDwhLS0gTGFzdCBTaWduIGluIENvbHVtbiAtLT5cbiAgICA8bmctY29udGFpbmVyIG1hdENvbHVtbkRlZj1cImVkaXRlZE1pbHNcIj5cbiAgICAgIDx0aCBtYXQtaGVhZGVyLWNlbGwgKm1hdEhlYWRlckNlbGxEZWYgbWF0LXNvcnQtaGVhZGVyIHNvcnRBY3Rpb25EZXNjcmlwdGlvbj1cIlNvcnQgYnkgTGFzdCBNb2RpZmllZFwiPiBMYXN0IE1vZGlmaWVkIDwvdGg+XG4gICAgICA8dGQgbWF0LWNlbGwgKm1hdENlbGxEZWY9XCJsZXQgZWxlbWVudFwiPiB7e2VsZW1lbnQuZWRpdGVkTWlscyB8IGRhdGU6J3l5eXktTU1+ZGQsIEhIOm1tOnNzJyB9fSA8L3RkPlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgPHRyIG1hdC1oZWFkZXItcm93ICptYXRIZWFkZXJSb3dEZWY9XCJkaXNwbGF5ZWRDb2x1bW5zXCI+PC90cj5cbiAgICA8dHIgbWF0LXJvdyAqbWF0Um93RGVmPVwibGV0IHJvdzsgY29sdW1uczogZGlzcGxheWVkQ29sdW1ucztcIiAoY2xpY2spPVwic2VsZWN0aW9uLnRvZ2dsZShyb3cpXCI+PC90cj5cbiAgPC90YWJsZT5cblxuPC90YW5qLXBhZ2UtYm9keT5cbiJdfQ==