@angular-generic-table/core 5.0.0-rc.2 → 5.0.0-rc.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2020/angular-generic-table-core.mjs +5 -0
- package/esm2020/lib/core.component.mjs +560 -0
- package/esm2020/lib/core.module.mjs +47 -0
- package/esm2020/lib/core.service.mjs +14 -0
- package/esm2020/lib/gt-delta/gt-delta.component.mjs +126 -0
- package/esm2020/lib/models/gt-pagination.mjs +2 -0
- package/esm2020/lib/models/table-column.interface.mjs +2 -0
- package/esm2020/lib/models/table-config.interface.mjs +2 -0
- package/esm2020/lib/models/table-events.interface.mjs +2 -0
- package/esm2020/lib/models/table-info.interface.mjs +2 -0
- package/esm2020/lib/models/table-meta.interface.mjs +2 -0
- package/esm2020/lib/models/table-row.interface.mjs +2 -0
- package/esm2020/lib/models/table-sort.interface.mjs +2 -0
- package/esm2020/lib/pagination/pagination.component.mjs +157 -0
- package/esm2020/lib/pagination/pagination.module.mjs +17 -0
- package/esm2020/lib/pipes/capital-case.pipe.mjs +18 -0
- package/esm2020/lib/pipes/dash-case.pipe.mjs +18 -0
- package/esm2020/lib/pipes/dynamic.pipe.mjs +26 -0
- package/esm2020/lib/pipes/highlight.pipe.mjs +49 -0
- package/esm2020/lib/pipes/row-selection.pipe.mjs +17 -0
- package/esm2020/lib/pipes/sort-class.pipe.mjs +40 -0
- package/esm2020/lib/utilities/utilities.mjs +186 -0
- package/esm2020/public-api.mjs +24 -0
- package/fesm2015/angular-generic-table-core.mjs +1253 -0
- package/fesm2015/angular-generic-table-core.mjs.map +1 -0
- package/fesm2020/angular-generic-table-core.mjs +1240 -0
- package/fesm2020/angular-generic-table-core.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/core.component.d.ts +123 -18
- package/lib/core.module.d.ts +13 -0
- package/lib/core.service.d.ts +3 -0
- package/lib/gt-delta/gt-delta.component.d.ts +32 -0
- package/lib/models/gt-pagination.d.ts +18 -0
- package/lib/models/table-column.interface.d.ts +15 -3
- package/lib/models/table-config.interface.d.ts +44 -6
- package/lib/models/table-events.interface.d.ts +27 -0
- package/lib/models/table-info.interface.d.ts +3 -1
- package/lib/models/table-meta.interface.d.ts +8 -0
- package/lib/models/table-row.interface.d.ts +1 -1
- package/lib/models/table-sort.interface.d.ts +6 -4
- package/lib/pagination/pagination.component.d.ts +62 -0
- package/lib/pagination/pagination.module.d.ts +8 -0
- package/lib/pipes/capital-case.pipe.d.ts +7 -0
- package/lib/pipes/dash-case.pipe.d.ts +3 -0
- package/lib/pipes/dynamic.pipe.d.ts +9 -0
- package/lib/pipes/highlight.pipe.d.ts +3 -0
- package/lib/pipes/row-selection.pipe.d.ts +8 -0
- package/lib/pipes/sort-class.pipe.d.ts +5 -5
- package/lib/utilities/utilities.d.ts +32 -0
- package/package.json +22 -10
- package/public-api.d.ts +12 -0
- package/scss/index.scss +319 -0
- package/angular-generic-table-core.d.ts +0 -7
- package/angular-generic-table-core.metadata.json +0 -1
- package/bundles/angular-generic-table-core.umd.js +0 -594
- package/bundles/angular-generic-table-core.umd.js.map +0 -1
- package/bundles/angular-generic-table-core.umd.min.js +0 -2
- package/bundles/angular-generic-table-core.umd.min.js.map +0 -1
- package/esm2015/angular-generic-table-core.js +0 -8
- package/esm2015/lib/core.component.js +0 -107
- package/esm2015/lib/core.module.js +0 -16
- package/esm2015/lib/core.service.js +0 -13
- package/esm2015/lib/enums/order.enum.js +0 -6
- package/esm2015/lib/models/table-column.interface.js +0 -2
- package/esm2015/lib/models/table-config.interface.js +0 -2
- package/esm2015/lib/models/table-info.interface.js +0 -2
- package/esm2015/lib/models/table-row.interface.js +0 -2
- package/esm2015/lib/models/table-sort.interface.js +0 -2
- package/esm2015/lib/pipes/dash-case.pipe.js +0 -13
- package/esm2015/lib/pipes/highlight.pipe.js +0 -44
- package/esm2015/lib/pipes/sort-class.pipe.js +0 -12
- package/esm2015/lib/utilities/utilities.js +0 -25
- package/esm2015/public-api.js +0 -12
- package/fesm2015/angular-generic-table-core.js +0 -234
- package/fesm2015/angular-generic-table-core.js.map +0 -1
- package/lib/enums/order.enum.d.ts +0 -4
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, } from '@angular/core';
|
|
2
|
+
import { BehaviorSubject, combineLatest, fromEvent, isObservable, of, ReplaySubject, Subject, } from 'rxjs';
|
|
3
|
+
import { AsyncPipe, KeyValuePipe, NgClass, NgForOf, NgIf, NgTemplateOutlet, SlicePipe, } from '@angular/common';
|
|
4
|
+
import { distinctUntilChanged, filter, map, pluck, shareReplay, startWith, switchMap, take, takeUntil, tap, withLatestFrom, } from 'rxjs/operators';
|
|
5
|
+
import { calculate, chunk, search, sortOnMultipleKeys, } from './utilities/utilities';
|
|
6
|
+
import { CapitalCasePipe } from './pipes/capital-case.pipe';
|
|
7
|
+
import { SortClassPipe } from './pipes/sort-class.pipe';
|
|
8
|
+
import { DashCasePipe } from './pipes/dash-case.pipe';
|
|
9
|
+
import { DynamicPipe } from './pipes/dynamic.pipe';
|
|
10
|
+
import { HighlightPipe } from './pipes/highlight.pipe';
|
|
11
|
+
import { RowSelectionPipe } from './pipes/row-selection.pipe';
|
|
12
|
+
import * as i0 from "@angular/core";
|
|
13
|
+
export class CoreComponent {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.unsubscribe$ = new Subject();
|
|
16
|
+
this._navigationKeys = [
|
|
17
|
+
'ArrowDown',
|
|
18
|
+
'ArrowUp',
|
|
19
|
+
'ArrowLeft',
|
|
20
|
+
'ArrowRight',
|
|
21
|
+
'Home',
|
|
22
|
+
'End',
|
|
23
|
+
];
|
|
24
|
+
this._selectKeys = ['Enter', ' '];
|
|
25
|
+
this._customClasses = {
|
|
26
|
+
selectedRow: 'gt-selected',
|
|
27
|
+
activeRow: 'gt-active',
|
|
28
|
+
};
|
|
29
|
+
/** selection
|
|
30
|
+
* @description An object that contains the currently selected row(s) in the table. It's passed to the selection function to determine which rows should be selected.
|
|
31
|
+
* @type {any}
|
|
32
|
+
*/
|
|
33
|
+
this.selection = {};
|
|
34
|
+
/** generateRowId
|
|
35
|
+
* @description Whether or not to generate a unique id for each row in the table. Defaults to `true`.
|
|
36
|
+
* @type {boolean}
|
|
37
|
+
*/
|
|
38
|
+
this.generateRowId = true;
|
|
39
|
+
this.rowClick = new EventEmitter();
|
|
40
|
+
this.rowSelect = new EventEmitter();
|
|
41
|
+
this.sortOrderChange = new EventEmitter();
|
|
42
|
+
this._rowActive$ = new ReplaySubject(1);
|
|
43
|
+
this.rowActive = new EventEmitter();
|
|
44
|
+
this.columnSort = new EventEmitter();
|
|
45
|
+
/** page change event - emitted when current page/index changes for pagination */
|
|
46
|
+
this.pageChange = new EventEmitter();
|
|
47
|
+
this.rowActive$ = this._rowActive$.asObservable().pipe(distinctUntilChanged((p, q) => {
|
|
48
|
+
if (this.rowIdKey && p.row && q.row) {
|
|
49
|
+
return p.row[this.rowIdKey] === q.row[this.rowIdKey];
|
|
50
|
+
}
|
|
51
|
+
else if (this.generateRowId && p.row && q.row) {
|
|
52
|
+
return p.row._id === q.row._id;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return p.index === q.index;
|
|
56
|
+
}
|
|
57
|
+
}), tap((event) => (this.activeRowIndex = event.index)), tap((event) => this.rowActive.emit(event)), shareReplay(1));
|
|
58
|
+
this.activeRowIndex = null;
|
|
59
|
+
this._loading$ = new ReplaySubject(1);
|
|
60
|
+
this._sortOrder$ = new BehaviorSubject([]);
|
|
61
|
+
this._searchBy$ = new ReplaySubject(1);
|
|
62
|
+
this.searchBy$ = this._searchBy$.pipe(startWith(''), map((value) => (isObservable(value) ? value : of(value))), switchMap((obs) => obs), shareReplay(1));
|
|
63
|
+
this._pagingInfo$ = new BehaviorSubject({
|
|
64
|
+
pageCurrent: null,
|
|
65
|
+
pageNext: null,
|
|
66
|
+
pagePrevious: null,
|
|
67
|
+
pageSize: null,
|
|
68
|
+
numberOfRecords: null,
|
|
69
|
+
//recordsAfterFilter: null,
|
|
70
|
+
//recordsAfterSearch: null,
|
|
71
|
+
//recordsAll: null,
|
|
72
|
+
});
|
|
73
|
+
// tslint:disable-next-line:variable-name
|
|
74
|
+
this._tableConfig$ = new BehaviorSubject({});
|
|
75
|
+
this.tableConfig$ = this._tableConfig$.pipe(map((value) => (isObservable(value) ? value : of(value))), switchMap((obs) => obs), tap((config) => (this._tableConfig = config)), shareReplay(1));
|
|
76
|
+
this._data$ = new ReplaySubject(1);
|
|
77
|
+
this.data$ = this._data$.pipe(map((value) => (isObservable(value) ? value : of(value))), switchMap((obs) => combineLatest([obs])), withLatestFrom(this.tableConfig$), map(([[data], config]) => {
|
|
78
|
+
// if columns or rows contains config for mapTo...
|
|
79
|
+
if ((config.columns &&
|
|
80
|
+
!!Object.values(config.columns).find((column) => !!column.mapTo)) ||
|
|
81
|
+
(config.rows &&
|
|
82
|
+
!!Object.values(config.rows).find((column) => !!column.mapTo))) {
|
|
83
|
+
// ...map data to new keys on row...
|
|
84
|
+
const newData = [];
|
|
85
|
+
for (let i = 0; i < data.length; i++) {
|
|
86
|
+
const row = data[i];
|
|
87
|
+
const newKeys = Object.entries(config.columns || config.rows || [])
|
|
88
|
+
.filter(([key, value]) => !!value.mapTo) // add keys for columns with mapTo config...
|
|
89
|
+
.reduce((previousValue, [key, value]) => ({
|
|
90
|
+
...previousValue,
|
|
91
|
+
[key]: this.nestedValue(row, value.mapTo.path, value.mapTo?.missingValue),
|
|
92
|
+
}), {});
|
|
93
|
+
newData[i] = { ...row, ...newKeys };
|
|
94
|
+
}
|
|
95
|
+
data = newData;
|
|
96
|
+
}
|
|
97
|
+
if (this.generateRowId && !this.rowIdKey && data.length > 0) {
|
|
98
|
+
const dataWithId = [];
|
|
99
|
+
for (let i = 0; i < data.length; i++) {
|
|
100
|
+
dataWithId[i] = { ...data[i], _id: i };
|
|
101
|
+
}
|
|
102
|
+
data = dataWithId;
|
|
103
|
+
}
|
|
104
|
+
return { data, config };
|
|
105
|
+
}), switchMap((obs) => combineLatest([of(obs), this.sortOrder$, this.searchBy$])), map(([table, sortBy, searchBy]) => {
|
|
106
|
+
// create a new array reference and sort new array (prevent mutating existing state)
|
|
107
|
+
table.data = [...table.data];
|
|
108
|
+
return !sortBy?.length || table.config?.disableTableSort
|
|
109
|
+
? searchBy && !this.tableInfo?.lazyLoaded
|
|
110
|
+
? search(searchBy, false, table.data, table.config)
|
|
111
|
+
: table.data
|
|
112
|
+
: searchBy && !this.tableInfo?.lazyLoaded
|
|
113
|
+
? search(searchBy, false, table.data, table.config)?.sort(sortOnMultipleKeys(sortBy))
|
|
114
|
+
: table.data?.sort(sortOnMultipleKeys(sortBy));
|
|
115
|
+
}), shareReplay(1));
|
|
116
|
+
this.calculations$ = combineLatest([this.data$, this.tableConfig$]).pipe(map(([data, config]) => calculate(data, config)), shareReplay(1));
|
|
117
|
+
this.table$ = combineLatest([
|
|
118
|
+
this.data$,
|
|
119
|
+
this.tableConfig$,
|
|
120
|
+
this._pagingInfo$,
|
|
121
|
+
]).pipe(map(([sorted, config, pagingInfo]) => {
|
|
122
|
+
if (pagingInfo.pageCurrent !== null &&
|
|
123
|
+
pagingInfo.numberOfRecords !== null &&
|
|
124
|
+
pagingInfo.pageSize !== null) {
|
|
125
|
+
return {
|
|
126
|
+
data: [sorted],
|
|
127
|
+
config,
|
|
128
|
+
info: {
|
|
129
|
+
lazyLoaded: true,
|
|
130
|
+
numberOfRecords: pagingInfo.numberOfRecords,
|
|
131
|
+
pageSize: pagingInfo.pageSize,
|
|
132
|
+
pageTotal: pagingInfo.pageTotal ??
|
|
133
|
+
Math.ceil(pagingInfo.numberOfRecords / pagingInfo.pageSize),
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// if pagination is disabled...
|
|
138
|
+
if (!config.pagination || config.pagination.length === 0) {
|
|
139
|
+
// ...return unaltered array
|
|
140
|
+
return {
|
|
141
|
+
data: [sorted],
|
|
142
|
+
config,
|
|
143
|
+
info: { numberOfRecords: sorted.length, pageTotal: 1 },
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// return record set
|
|
147
|
+
return {
|
|
148
|
+
data: chunk(sorted, +(config.pagination.length || 0)),
|
|
149
|
+
config,
|
|
150
|
+
info: {
|
|
151
|
+
numberOfRecords: sorted.length,
|
|
152
|
+
pageSize: +(config.pagination.length || 0),
|
|
153
|
+
pageTotal: Math.ceil(sorted.length / +(config.pagination.length || 0)),
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}), tap((meta) => this._tableInfo$.next(meta.info)), shareReplay(1));
|
|
157
|
+
this._tableInfo$ = new BehaviorSubject(undefined);
|
|
158
|
+
this._currentPaginationIndex$ = new BehaviorSubject(0);
|
|
159
|
+
this.currentPaginationIndex$ = combineLatest([
|
|
160
|
+
this._currentPaginationIndex$,
|
|
161
|
+
this.table$,
|
|
162
|
+
]).pipe(map(([page, table]) => {
|
|
163
|
+
// determine last page
|
|
164
|
+
const lastPage = Math.ceil(table.info.numberOfRecords /
|
|
165
|
+
(table.info.pageSize ??
|
|
166
|
+
(table.config?.pagination?.length || table.info.numberOfRecords))) - 1;
|
|
167
|
+
// determine min/max position
|
|
168
|
+
return +page < 0 ? 0 : +page > lastPage ? lastPage : +page;
|
|
169
|
+
}), distinctUntilChanged(), tap((index) => this.pageChange.emit({ index })), shareReplay(1));
|
|
170
|
+
this.colspan$ = this.tableConfig$.pipe(switchMap((config) => config.columns
|
|
171
|
+
? of(Object.values(config.columns || config.rows || {}).filter((value) => value.hidden !== true).length)
|
|
172
|
+
: this.data$.pipe(map((data) => data.length + 1))), shareReplay(1));
|
|
173
|
+
this.footerColspan$ = this.tableConfig$.pipe(map((config) => {
|
|
174
|
+
let colspan = 0;
|
|
175
|
+
Object.values(config?.footer?.columns || {}).forEach((calculations) => {
|
|
176
|
+
if (Object.values(calculations).filter((value) => value !== false)
|
|
177
|
+
.length >= 0) {
|
|
178
|
+
colspan += 1;
|
|
179
|
+
}
|
|
180
|
+
}, {});
|
|
181
|
+
return colspan;
|
|
182
|
+
}), shareReplay(1));
|
|
183
|
+
this.columnOrder = (a, b) => {
|
|
184
|
+
return (a.value.order || 0) - (b.value.order || 0);
|
|
185
|
+
};
|
|
186
|
+
this._unsubscribeFromKeyboardEvents$ = new Subject();
|
|
187
|
+
this._keyboardArrowEvent$ = fromEvent(document, 'keydown').pipe(filter((event) => [...this._navigationKeys, ...this._selectKeys].indexOf(event.key) > -1));
|
|
188
|
+
}
|
|
189
|
+
get navigationKeys() {
|
|
190
|
+
return this._navigationKeys;
|
|
191
|
+
}
|
|
192
|
+
/** navigationKeys
|
|
193
|
+
* @description An array of keyboard keys that will trigger navigation and active row, currently only supports arrow keys, home and end (omit key name from array to disable it)
|
|
194
|
+
* @type {string[]}
|
|
195
|
+
* @default ['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End']
|
|
196
|
+
*/
|
|
197
|
+
set navigationKeys(value) {
|
|
198
|
+
this._navigationKeys = value;
|
|
199
|
+
}
|
|
200
|
+
get selectKeys() {
|
|
201
|
+
return this._selectKeys;
|
|
202
|
+
}
|
|
203
|
+
/** selectKeys
|
|
204
|
+
* @description An array of keyboard keys that will trigger row selection (omit key name from array to disable it)
|
|
205
|
+
* @type {string[]}
|
|
206
|
+
* @default ['Enter', ' ']
|
|
207
|
+
*/
|
|
208
|
+
set selectKeys(value) {
|
|
209
|
+
this._selectKeys = value;
|
|
210
|
+
}
|
|
211
|
+
get sortOrder$() {
|
|
212
|
+
return this._sortOrder$.asObservable();
|
|
213
|
+
}
|
|
214
|
+
set loading(isLoading) {
|
|
215
|
+
this._loading$.next(isLoading);
|
|
216
|
+
}
|
|
217
|
+
set paginationIndex(pageIndex) {
|
|
218
|
+
this._currentPaginationIndex$.next(pageIndex);
|
|
219
|
+
}
|
|
220
|
+
get paginationIndex() {
|
|
221
|
+
return this._currentPaginationIndex$.getValue();
|
|
222
|
+
}
|
|
223
|
+
set pagingInfo(value) {
|
|
224
|
+
if (value) {
|
|
225
|
+
this._pagingInfo$.next(value);
|
|
226
|
+
if (value.pageCurrent !== this._currentPaginationIndex$.getValue() + 1 &&
|
|
227
|
+
value.pageCurrent !== null) {
|
|
228
|
+
this.paginationIndex = value.pageCurrent - 1;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** customClasses
|
|
233
|
+
* @description An object that contains custom classes for various elements in the table.
|
|
234
|
+
* @type {object} - { selectedRow: string, activeRow: string } - default classes are 'gt-selected' and 'gt-active'
|
|
235
|
+
*/
|
|
236
|
+
set customClasses(classes) {
|
|
237
|
+
this._customClasses = { ...this._customClasses, ...classes };
|
|
238
|
+
}
|
|
239
|
+
get customClasses() {
|
|
240
|
+
return this._customClasses;
|
|
241
|
+
}
|
|
242
|
+
/** isRowSelectedFn
|
|
243
|
+
* @description Function to determine if row is selected or not.
|
|
244
|
+
* @type {fn} A function that receives a row object and optional state for current selection that can be used to determine if row should be marked as selected or not. */
|
|
245
|
+
set isRowSelectedFn(fn) {
|
|
246
|
+
this._isRowSelectedFn = fn;
|
|
247
|
+
}
|
|
248
|
+
get isRowSelectedFn() {
|
|
249
|
+
return this._isRowSelectedFn;
|
|
250
|
+
}
|
|
251
|
+
/** trackRowByFn
|
|
252
|
+
* @description A function that returns a unique identifier for each row in the table to optimize rendering when data is added or removed.
|
|
253
|
+
* @type fn - TrackByFunction to retrieve unique id based on index and/or row. Defaults to using `row[this.rowIdKey]`.
|
|
254
|
+
*/
|
|
255
|
+
set trackRowByFn(fn) {
|
|
256
|
+
this._trackRowByFn = fn;
|
|
257
|
+
}
|
|
258
|
+
get trackRowByFn() {
|
|
259
|
+
return this._trackRowByFn;
|
|
260
|
+
}
|
|
261
|
+
_trackRowByFn(index, row) {
|
|
262
|
+
return this.rowIdKey ? row[this.rowIdKey] : row?._id;
|
|
263
|
+
}
|
|
264
|
+
set search(string) {
|
|
265
|
+
this._searchBy$.next(string);
|
|
266
|
+
}
|
|
267
|
+
set config(config) {
|
|
268
|
+
this._tableConfig$.next(config);
|
|
269
|
+
}
|
|
270
|
+
set data(data) {
|
|
271
|
+
if (data) {
|
|
272
|
+
this._data$.next(data);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
set sortOrder(sortConfig) {
|
|
276
|
+
if (JSON.stringify(sortConfig) !== JSON.stringify(this._sortOrder$.value)) {
|
|
277
|
+
this.sortOrderChange.emit(sortConfig);
|
|
278
|
+
this._sortOrder$.next(sortConfig);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
_rowClick(row, index, event) {
|
|
282
|
+
this.rowClick.emit({ row, index, event });
|
|
283
|
+
}
|
|
284
|
+
_rowActive(row, index, event) {
|
|
285
|
+
this.rowSelect.emit({ row, index, event });
|
|
286
|
+
}
|
|
287
|
+
activateRow(arg, event) {
|
|
288
|
+
if (typeof arg === 'number') {
|
|
289
|
+
this.table$
|
|
290
|
+
.pipe(pluck('data'), map((data) => data[this.paginationIndex][arg]), take(1), takeUntil(this.unsubscribe$))
|
|
291
|
+
.subscribe((row) => this._activateRow(row, arg, event));
|
|
292
|
+
}
|
|
293
|
+
else if (typeof arg === 'string') {
|
|
294
|
+
// TODO: implement hover by id
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
this._activateRow(null, null);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
_activateRow(row, index, event) {
|
|
301
|
+
this._rowActive$.next({ row, index, event });
|
|
302
|
+
}
|
|
303
|
+
get loading$() {
|
|
304
|
+
return this._loading$.pipe(startWith(false), map((value) => (isObservable(value) ? value : of(value))), switchMap((obs) => obs), shareReplay(1));
|
|
305
|
+
}
|
|
306
|
+
/** tableInfo$ - returns observable for table info
|
|
307
|
+
* @return Observable<TableInfo> */
|
|
308
|
+
get tableInfo$() {
|
|
309
|
+
return this._tableInfo$.asObservable().pipe(filter((info) => !!info), shareReplay(1));
|
|
310
|
+
}
|
|
311
|
+
/** tableInfo - returns the current table info
|
|
312
|
+
* @return TableInfo */
|
|
313
|
+
get tableInfo() {
|
|
314
|
+
return this._tableInfo$.getValue();
|
|
315
|
+
}
|
|
316
|
+
/** sortByKey - Sort by key in table row
|
|
317
|
+
* @param key - key to sort by
|
|
318
|
+
* @param { MouseEvent } [$event] - Mouse event triggering sort, if shift key is pressed sort key will be added to already present sort keys
|
|
319
|
+
*/
|
|
320
|
+
sortByKey(key, $event) {
|
|
321
|
+
const shiftKey = $event?.shiftKey === true;
|
|
322
|
+
const currentOrder = this._sortOrder$.value;
|
|
323
|
+
let sortOrder = 'asc';
|
|
324
|
+
let newOrder = [];
|
|
325
|
+
// if shift key is pressed while sorting...
|
|
326
|
+
if (shiftKey) {
|
|
327
|
+
// ...check if key is already sorted
|
|
328
|
+
const existingSortPosition = currentOrder.findIndex((value) => value.key === key);
|
|
329
|
+
if (existingSortPosition === -1) {
|
|
330
|
+
// ...if key is not sorted, add it to the end of the sort order
|
|
331
|
+
newOrder = [...currentOrder, { key, order: 'asc' }];
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
// ...if key is already sorted, toggle sort order
|
|
335
|
+
sortOrder = currentOrder[existingSortPosition].order;
|
|
336
|
+
const newSortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
|
|
337
|
+
newOrder = [...currentOrder];
|
|
338
|
+
newOrder[existingSortPosition] = {
|
|
339
|
+
...newOrder[existingSortPosition],
|
|
340
|
+
order: newSortOrder,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
// ...else if shift key is not pressed...
|
|
346
|
+
if (currentOrder.length > 0) {
|
|
347
|
+
// ...check if key is already sorted
|
|
348
|
+
const existingSortPosition = currentOrder.findIndex((value) => value.key === key);
|
|
349
|
+
// ...if key is already sorted, toggle sort order
|
|
350
|
+
if (existingSortPosition === -1) {
|
|
351
|
+
newOrder = [{ key, order: 'asc' }];
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
sortOrder = currentOrder[existingSortPosition].order;
|
|
355
|
+
const newSortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
|
|
356
|
+
newOrder = [{ key, order: newSortOrder }];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
// ...if key is not sorted set sort order for key to ascending
|
|
361
|
+
newOrder = [{ key, order: sortOrder }];
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// create sort event
|
|
365
|
+
const sortEvent = {
|
|
366
|
+
key,
|
|
367
|
+
order: sortOrder,
|
|
368
|
+
currentSortOrder: newOrder,
|
|
369
|
+
addSortKey: shiftKey,
|
|
370
|
+
};
|
|
371
|
+
// if event is passed to sort function...
|
|
372
|
+
if ($event) {
|
|
373
|
+
// ...emit it as well
|
|
374
|
+
sortEvent.event = $event;
|
|
375
|
+
}
|
|
376
|
+
// emit sort event
|
|
377
|
+
this.columnSort.emit(sortEvent);
|
|
378
|
+
// if table is not lazy loaded (sorting is then handled server-side)...
|
|
379
|
+
if (!this.tableInfo?.lazyLoaded) {
|
|
380
|
+
// ...update sort order
|
|
381
|
+
this.sortOrder = newOrder;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
nestedValue(object, mapTo, missingValue = null) {
|
|
385
|
+
const levels = mapTo.split('.');
|
|
386
|
+
return levels.reduce((previousValue, currentValue, index) => previousValue[currentValue] ||
|
|
387
|
+
(index === levels.length - 1 ? missingValue : {}), object);
|
|
388
|
+
}
|
|
389
|
+
listenToKeyboardEvents() {
|
|
390
|
+
if (!this._tableConfig?.activateRowOnKeyboardNavigation) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
this._unsubscribeFromKeyboardEvents$.next(true);
|
|
394
|
+
this._keyboardArrowEvent$
|
|
395
|
+
.pipe(withLatestFrom(this.data$, this.currentPaginationIndex$, this.tableInfo$), takeUntil(this._unsubscribeFromKeyboardEvents$), takeUntil(this.unsubscribe$))
|
|
396
|
+
.subscribe(([event, rows, currentPage, tableInfo]) => {
|
|
397
|
+
const selectEvent = this._selectKeys.includes(event.key);
|
|
398
|
+
if (selectEvent && this.activeRowIndex !== null) {
|
|
399
|
+
const rowIndex = this.activeRowIndex + currentPage * (tableInfo?.pageSize ?? 0);
|
|
400
|
+
this._rowActive(rows[rowIndex], rowIndex, event);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const navigationEvent = this._navigationKeys.includes(event.key);
|
|
404
|
+
if (navigationEvent) {
|
|
405
|
+
this._handleNavigationEvent(event, rows, currentPage, tableInfo);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
unsubscribeFromKeyboardEvents(tableRef) {
|
|
410
|
+
if (!this._tableConfig?.activateRowOnKeyboardNavigation) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
// only unsubscribe if table is not focused
|
|
414
|
+
if (tableRef !== document.activeElement) {
|
|
415
|
+
if (this._tableConfig?.activateRowOnHover) {
|
|
416
|
+
// unset active row
|
|
417
|
+
this.activateRow(null);
|
|
418
|
+
}
|
|
419
|
+
this._unsubscribeFromKeyboardEvents$.next(true);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
_handleNavigationEvent(event, rows, currentPage, tableInfo) {
|
|
423
|
+
const hasPagination = (tableInfo?.pageTotal || 0) > 1 && tableInfo;
|
|
424
|
+
const lastRowIndex = rows.length - 1;
|
|
425
|
+
let newIndex = this.activeRowIndex;
|
|
426
|
+
let indexModifier = 0;
|
|
427
|
+
if (event.key === 'Home') {
|
|
428
|
+
this.paginationIndex = 0;
|
|
429
|
+
this.activateRow(0, event);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (event.key === 'End') {
|
|
433
|
+
const indexOfLastRecord = hasPagination
|
|
434
|
+
? rows.length - (tableInfo.pageTotal - 1) * tableInfo.pageSize - 1
|
|
435
|
+
: lastRowIndex;
|
|
436
|
+
if (tableInfo?.pageTotal) {
|
|
437
|
+
this.paginationIndex = tableInfo.pageTotal - 1;
|
|
438
|
+
}
|
|
439
|
+
this.activateRow(indexOfLastRecord, event);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (event.key === 'ArrowDown') {
|
|
443
|
+
indexModifier = 1;
|
|
444
|
+
}
|
|
445
|
+
else if (event.key === 'ArrowUp') {
|
|
446
|
+
indexModifier = -1;
|
|
447
|
+
}
|
|
448
|
+
if (newIndex === null) {
|
|
449
|
+
newIndex = 0;
|
|
450
|
+
}
|
|
451
|
+
else if (newIndex + indexModifier >= 0 &&
|
|
452
|
+
newIndex + indexModifier <= lastRowIndex) {
|
|
453
|
+
newIndex = newIndex + indexModifier;
|
|
454
|
+
}
|
|
455
|
+
if (hasPagination && tableInfo?.pageSize) {
|
|
456
|
+
const isNotLastPage = currentPage + 1 < tableInfo.pageTotal;
|
|
457
|
+
const recordsOnLastPage = rows.length - (tableInfo.pageTotal - 1) * tableInfo.pageSize - 1;
|
|
458
|
+
const maxIndex = isNotLastPage
|
|
459
|
+
? tableInfo?.pageSize - 1
|
|
460
|
+
: recordsOnLastPage;
|
|
461
|
+
if (event.key === 'ArrowLeft' && currentPage > 0) {
|
|
462
|
+
this.paginationIndex = currentPage - 1;
|
|
463
|
+
this.activateRow(newIndex, event);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
else if (event.key === 'ArrowRight' && isNotLastPage) {
|
|
467
|
+
if (currentPage + 1 === tableInfo.pageTotal - 1 &&
|
|
468
|
+
newIndex > recordsOnLastPage) {
|
|
469
|
+
this.activateRow(recordsOnLastPage, event);
|
|
470
|
+
}
|
|
471
|
+
this.paginationIndex = currentPage + 1;
|
|
472
|
+
this.activateRow(newIndex, event);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (currentPage > 0 &&
|
|
476
|
+
indexModifier < 0 &&
|
|
477
|
+
newIndex + indexModifier <= lastRowIndex &&
|
|
478
|
+
(this.activeRowIndex || 0) + indexModifier < 0) {
|
|
479
|
+
// set last row of previous page as active
|
|
480
|
+
this.activateRow(tableInfo?.pageSize - 1, event);
|
|
481
|
+
this.paginationIndex = currentPage - 1;
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
const pageIndex = newIndex % tableInfo?.pageSize;
|
|
485
|
+
if (newIndex > maxIndex && currentPage + 1 < tableInfo.pageTotal) {
|
|
486
|
+
this.paginationIndex = currentPage + 1;
|
|
487
|
+
}
|
|
488
|
+
this.activateRow(pageIndex > maxIndex ? maxIndex : pageIndex, event);
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
this.activateRow(newIndex, event);
|
|
492
|
+
}
|
|
493
|
+
ngOnDestroy() {
|
|
494
|
+
this.unsubscribe$.next(true);
|
|
495
|
+
this.unsubscribe$.complete();
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
CoreComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: CoreComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
499
|
+
CoreComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.1.1", type: CoreComponent, isStandalone: true, selector: "angular-generic-table", inputs: { navigationKeys: "navigationKeys", selectKeys: "selectKeys", loading: "loading", paginationIndex: "paginationIndex", pagingInfo: "pagingInfo", customClasses: "customClasses", isRowSelectedFn: "isRowSelectedFn", selection: "selection", rowIdKey: "rowIdKey", generateRowId: "generateRowId", trackRowByFn: "trackRowByFn", search: "search", config: "config", data: "data", sortOrder: "sortOrder" }, outputs: { rowClick: "rowClick", rowSelect: "rowSelect", sortOrderChange: "sortOrderChange", rowActive: "rowActive", columnSort: "columnSort", pageChange: "pageChange" }, ngImport: i0, template: "<table\n [ngClass]=\"(tableConfig$ | async)?.class || 'table'\"\n [class.table-mobile]=\"(tableConfig$ | async)?.mobileLayout\"\n [class.table-horizontal]=\"(tableConfig$ | async)?.rows\"\n [class.table-loading]=\"loading$ | async\"\n [class.gt-sticky-row-header]=\"\n (tableConfig$ | async)?.stickyHeaders?.row && (tableConfig$ | async)?.rows\n \"\n [class.gt-sticky-column-header]=\"\n (tableConfig$ | async)?.stickyHeaders?.column\n \"\n [attr.aria-busy]=\"(loading$ | async) === true ? true : null\"\n [tabindex]=\"(tableConfig$ | async)?.activateRowOnKeyboardNavigation ? 0 : -1\"\n #tableRef\n (focus)=\"listenToKeyboardEvents()\"\n (focusout)=\"unsubscribeFromKeyboardEvents(tableRef)\"\n (mouseenter)=\"listenToKeyboardEvents()\"\n (mouseleave)=\"unsubscribeFromKeyboardEvents(tableRef)\"\n>\n <thead>\n <tr\n *ngIf=\"{\n config: (tableConfig$ | async)!,\n isLoading: loading$ | async\n } as table\"\n >\n <ng-container\n *ngFor=\"let column of table.config?.columns | keyvalue: columnOrder\"\n >\n <th\n *ngIf=\"!column.value?.hidden\"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [class.disabled]=\"table.isLoading\"\n [attr.aria-sort]=\"sortOrder$ | async | sortClass: column.key:'aria'\"\n [class.gt-sortable]=\"true\"\n scope=\"col\"\n >\n <button\n *ngIf=\"column.value?.sortable\"\n [attr.data-sort-order]=\"\n sortOrder$ | async | sortClass: column.key:'order'\n \"\n class=\"gt-sort\"\n (click)=\"\n table.isLoading ||\n !column.value.sortable ||\n sortByKey(column.key, $event)\n \"\n >\n <span *ngIf=\"column.value?.header !== false\">{{\n column.value.header || column.key | capitalCase\n }}</span>\n </button>\n <span\n *ngIf=\"!column.value?.sortable && column.value?.header !== false\"\n >{{ column.value.header || column.key | capitalCase }}</span\n >\n </th>\n </ng-container>\n <ng-container\n *ngIf=\"\n ((table?.config?.rows | keyvalue: columnOrder) || [])[0] as headerRow\n \"\n >\n <th\n class=\"row-header\"\n [attr.aria-sort]=\"\n sortOrder$ | async | sortClass: headerRow.key:'aria'\n \"\n ngClass=\"{{ headerRow.value.sortable ? 'sort ' : '' }} {{\n sortOrder$ | async | sortClass: headerRow.key\n }} {{ (headerRow.key | dashCase) + '-column' }}\"\n (click)=\"\n table.isLoading ||\n !headerRow.value.sortable ||\n sortByKey(headerRow.key, $event)\n \"\n scope=\"col\"\n >\n <ng-container *ngIf=\"headerRow?.value?.header !== false\">{{\n headerRow?.value?.header || headerRow.key | capitalCase\n }}</ng-container>\n </th>\n <th\n *ngFor=\"let column of ((table$ | async)?.data || [])[0]\"\n ngClass=\"{{ headerRow.value.class }}\"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.rows || {})[headerRow.key].templateRef\n ? templateRef\n : (table.config.rows || {})[headerRow.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: column,\n column: headerRow,\n transform: (table.config.rows || {})[headerRow.key].transform,\n templateRef: (table.config.rows || {})[headerRow.key].templateRef,\n index: 0\n }\"\n >\n </ng-container>\n </th>\n </ng-container>\n </tr>\n </thead>\n <tbody *ngIf=\"loading$ | async; else tableContent\">\n <tr>\n <td class=\"p-0\" [colSpan]=\"colspan$ | async\">\n <ng-content select=\".table-loading\"></ng-content>\n </td>\n </tr>\n </tbody>\n <tfoot *ngIf=\"(table$ | async)! as table\">\n <ng-container *ngIf=\"table.data.length > 0 && !(loading$ | async)\">\n <ng-container *ngIf=\"(calculations$ | async)! as calculations\">\n <tr\n *ngFor=\"let calculation of calculations.calculations; let i = index\"\n >\n <ng-container\n *ngIf=\"{\n showHeader: (colspan$ | async) !== (footerColspan$ | async)\n } as footerRow\"\n >\n <th\n *ngIf=\"footerRow.showHeader\"\n [colSpan]=\"\n ((colspan$ | async) || 0) - ((footerColspan$ | async) || 0)\n \"\n scope=\"row\"\n >\n <ng-container\n *ngIf=\"table.config?.footer?.headers?.[calculation] as showHeader\"\n >{{showHeader === true ? (calculation | capitalCase): table.config.footer?.headers?.[calculation]}}\n </ng-container>\n </th>\n <ng-container\n *ngFor=\"\n let column of table.config?.columns | keyvalue: columnOrder\n \"\n >\n <td\n *ngIf=\"\n !column.value?.hidden && calculations.calculated[column.key]\n \"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [attr.data-header]=\"\n !footerRow.showHeader && table.config.footer?.headers?.[calculation]\n ? table.config.footer?.headers?.[calculation] === true ? (calculation | capitalCase) : table.config.footer?.headers?.[calculation]\n : null\n \"\n [attr.data-label]=\"\n table.config.mobileLayout && column.value.mobileHeader\n ? column.value.mobileHeader !== true\n ? column.value.mobileHeader\n : (column.value.header || column.key | capitalCase)\n : null\n \"\n [class.gt-no-content]=\"\n !calculations.calculated[column.key][calculation]\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.columns || {})[column.key].templateRef\n ? templateRef\n : (table.config.columns || {})[column.key].transform\n ? transformFooter\n : rawFooter\n \"\n [ngTemplateOutletContext]=\"{\n value: calculations.calculated[column.key][calculation],\n row: calculations.calculated[column.key],\n column: calculation,\n templateRef: (table.config.columns || {})[column.key]\n .templateRef,\n transform: (table.config.columns || {})[column.key]\n .transform\n }\"\n ></ng-container>\n </td>\n </ng-container>\n </ng-container>\n </tr>\n </ng-container>\n </ng-container>\n </tfoot>\n</table>\n<ng-template #tableContent>\n <ng-container *ngIf=\"(table$ | async)! as table\">\n <tbody *ngIf=\"(table!.data![0] || table!.data!).length > 0; else noData\">\n <ng-container *ngIf=\"table.config.columns\">\n <tr\n *ngFor=\"\n let row of table!.data![\n table.info.lazyLoaded ? 0 : (currentPaginationIndex$ | async) || 0\n ];\n trackBy: trackRowByFn;\n let i = index\n \"\n [attr.id]=\"'tableRow_' + i\"\n (click)=\"table?.config?.rowClick && _rowClick(row, i, $event)\"\n (mouseenter)=\"\n table?.config?.activateRowOnHover && _activateRow(row, i, $event)\n \"\n (mouseleave)=\"\n table?.config?.activateRowOnHover &&\n _activateRow(null, null, $event)\n \"\n [ngClass]=\"[\n !!isRowSelectedFn\n ? (row\n | rowSelection\n : selection\n : isRowSelectedFn\n : customClasses.selectedRow)\n : '',\n (rowActive$ | async)?.index === i ? customClasses.activeRow : ''\n ]\"\n >\n <ng-container\n *ngFor=\"let column of table.config?.columns | keyvalue: columnOrder\"\n >\n <td\n *ngIf=\"!column.value?.hidden\"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [attr.data-label]=\"\n table.config.mobileLayout && column.value.mobileHeader\n ? column.value.mobileHeader !== true\n ? column.value.mobileHeader\n : (column.value.header || column.key | capitalCase)\n : null\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (searchBy$ | async) &&\n !(table.config.columns || {})[column.key].templateRef\n ? highlighted\n : (table.config.columns || {})[column.key].templateRef\n ? templateRef\n : (table.config.columns || {})[column.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: row,\n column: column,\n search: (searchBy$ | async),\n transform: (table.config.columns || {})[column.key].transform,\n templateRef: (table.config.columns || {})[column.key]\n .templateRef,\n index: i,\n data: table.data[\n table.info.lazyLoaded\n ? 0\n : (currentPaginationIndex$ | async) || 0\n ]\n }\"\n ></ng-container>\n </td>\n </ng-container>\n </tr>\n </ng-container>\n <ng-container *ngIf=\"table.config.rows\">\n <ng-container\n *ngFor=\"\n let row of table?.config?.rows | keyvalue: columnOrder | slice: 1;\n let i = index\n \"\n >\n <tr\n *ngIf=\"!row.value?.hidden\"\n [attr.id]=\"'tableRow_' + i\"\n ngClass=\"{{ (row.key | dashCase) + '-row' }}\"\n (click)=\"table?.config?.rowClick && _rowClick(row, i, $event)\"\n (mouseenter)=\"\n table?.config?.activateRowOnHover && _activateRow(row, i, $event)\n \"\n (mouseleave)=\"\n table?.config?.activateRowOnHover &&\n _activateRow(null, null, $event)\n \"\n [ngClass]=\"[\n !!isRowSelectedFn\n ? (row\n | rowSelection\n : selection\n : isRowSelectedFn\n : customClasses.selectedRow)\n : '',\n (rowActive$ | async)?.index === i ? customClasses.activeRow : ''\n ]\"\n >\n <th class=\"row-header\" scope=\"row\">\n {{ row.value.header || row.key | capitalCase }}\n </th>\n <td\n *ngFor=\"let column of (table?.data || [])[0]; let y = index\"\n ngClass=\"{{ row.value.class }}\"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.rows || {})[row.key].templateRef\n ? templateRef\n : (table.config.rows || {})[row.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: column,\n column: row,\n transform: (table.config.rows || {})[row.key].transform,\n templateRef: (table.config.rows || {})[row.key].templateRef,\n index: table.config.rows ? y : i,\n data: table.data[\n table.info.lazyLoaded\n ? 0\n : (currentPaginationIndex$ | async) || 0\n ]\n }\"\n >\n </ng-container>\n </td>\n </tr>\n </ng-container>\n </ng-container>\n </tbody>\n </ng-container>\n</ng-template>\n<ng-template #noData>\n <tbody>\n <tr>\n <td class=\"p-0\" [colSpan]=\"colspan$ | async\">\n <ng-content select=\".table-no-data\"></ng-content>\n </td>\n </tr>\n </tbody>\n</ng-template>\n<ng-template\n #highlighted\n let-row=\"row\"\n let-column=\"column\"\n let-search=\"search\"\n let-transform=\"transform\"\n>\n <div\n *ngIf=\"!transform\"\n [innerHTML]=\"row[column.key] | highlight: search\"\n ></div>\n <div\n *ngIf=\"transform\"\n [innerHTML]=\"\n row[column.key]\n | dynamicPipe: transform.pipe:transform?.args\n | highlight: search\n \"\n ></div>\n</ng-template>\n<ng-template #rawData let-row=\"row\" let-column=\"column\">\n {{ row[column.key] }}\n</ng-template>\n<ng-template\n #transformData\n let-row=\"row\"\n let-column=\"column\"\n let-transform=\"transform\"\n let-data=\"data\"\n>\n {{ row[column.key] | dynamicPipe: transform.pipe:transform?.args }}\n</ng-template>\n<ng-template #transformFooter let-value=\"value\" let-transform=\"transform\">\n {{\n (value | dynamicPipe: transform.pipe:transform?.args) ||\n (tableConfig$ | async)?.footer?.emptyContent\n }}\n</ng-template>\n<ng-template #rawFooter let-value=\"value\">\n {{ value || (tableConfig$ | async)?.footer?.emptyContent }}\n</ng-template>\n<ng-template\n #templateRef\n let-row=\"row\"\n let-column=\"column\"\n let-index=\"index\"\n let-templateRef=\"templateRef\"\n let-data=\"data\"\n>\n <ng-container\n [ngTemplateOutlet]=\"templateRef\"\n [ngTemplateOutletContext]=\"{\n row: row,\n col: column,\n index: index,\n data: data\n }\"\n ></ng-container>\n</ng-template>\n", dependencies: [{ kind: "pipe", type: CapitalCasePipe, name: "capitalCase" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: SortClassPipe, name: "sortClass" }, { kind: "pipe", type: DashCasePipe, name: "dashCase" }, { kind: "pipe", type: RowSelectionPipe, name: "rowSelection" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: SlicePipe, name: "slice" }, { kind: "pipe", type: DynamicPipe, name: "dynamicPipe" }, { kind: "pipe", type: HighlightPipe, name: "highlight" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
500
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.1.1", ngImport: i0, type: CoreComponent, decorators: [{
|
|
501
|
+
type: Component,
|
|
502
|
+
args: [{ selector: 'angular-generic-table', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
503
|
+
CapitalCasePipe,
|
|
504
|
+
KeyValuePipe,
|
|
505
|
+
SortClassPipe,
|
|
506
|
+
DashCasePipe,
|
|
507
|
+
RowSelectionPipe,
|
|
508
|
+
AsyncPipe,
|
|
509
|
+
NgTemplateOutlet,
|
|
510
|
+
SlicePipe,
|
|
511
|
+
DynamicPipe,
|
|
512
|
+
HighlightPipe,
|
|
513
|
+
NgClass,
|
|
514
|
+
NgIf,
|
|
515
|
+
NgForOf,
|
|
516
|
+
], template: "<table\n [ngClass]=\"(tableConfig$ | async)?.class || 'table'\"\n [class.table-mobile]=\"(tableConfig$ | async)?.mobileLayout\"\n [class.table-horizontal]=\"(tableConfig$ | async)?.rows\"\n [class.table-loading]=\"loading$ | async\"\n [class.gt-sticky-row-header]=\"\n (tableConfig$ | async)?.stickyHeaders?.row && (tableConfig$ | async)?.rows\n \"\n [class.gt-sticky-column-header]=\"\n (tableConfig$ | async)?.stickyHeaders?.column\n \"\n [attr.aria-busy]=\"(loading$ | async) === true ? true : null\"\n [tabindex]=\"(tableConfig$ | async)?.activateRowOnKeyboardNavigation ? 0 : -1\"\n #tableRef\n (focus)=\"listenToKeyboardEvents()\"\n (focusout)=\"unsubscribeFromKeyboardEvents(tableRef)\"\n (mouseenter)=\"listenToKeyboardEvents()\"\n (mouseleave)=\"unsubscribeFromKeyboardEvents(tableRef)\"\n>\n <thead>\n <tr\n *ngIf=\"{\n config: (tableConfig$ | async)!,\n isLoading: loading$ | async\n } as table\"\n >\n <ng-container\n *ngFor=\"let column of table.config?.columns | keyvalue: columnOrder\"\n >\n <th\n *ngIf=\"!column.value?.hidden\"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [class.disabled]=\"table.isLoading\"\n [attr.aria-sort]=\"sortOrder$ | async | sortClass: column.key:'aria'\"\n [class.gt-sortable]=\"true\"\n scope=\"col\"\n >\n <button\n *ngIf=\"column.value?.sortable\"\n [attr.data-sort-order]=\"\n sortOrder$ | async | sortClass: column.key:'order'\n \"\n class=\"gt-sort\"\n (click)=\"\n table.isLoading ||\n !column.value.sortable ||\n sortByKey(column.key, $event)\n \"\n >\n <span *ngIf=\"column.value?.header !== false\">{{\n column.value.header || column.key | capitalCase\n }}</span>\n </button>\n <span\n *ngIf=\"!column.value?.sortable && column.value?.header !== false\"\n >{{ column.value.header || column.key | capitalCase }}</span\n >\n </th>\n </ng-container>\n <ng-container\n *ngIf=\"\n ((table?.config?.rows | keyvalue: columnOrder) || [])[0] as headerRow\n \"\n >\n <th\n class=\"row-header\"\n [attr.aria-sort]=\"\n sortOrder$ | async | sortClass: headerRow.key:'aria'\n \"\n ngClass=\"{{ headerRow.value.sortable ? 'sort ' : '' }} {{\n sortOrder$ | async | sortClass: headerRow.key\n }} {{ (headerRow.key | dashCase) + '-column' }}\"\n (click)=\"\n table.isLoading ||\n !headerRow.value.sortable ||\n sortByKey(headerRow.key, $event)\n \"\n scope=\"col\"\n >\n <ng-container *ngIf=\"headerRow?.value?.header !== false\">{{\n headerRow?.value?.header || headerRow.key | capitalCase\n }}</ng-container>\n </th>\n <th\n *ngFor=\"let column of ((table$ | async)?.data || [])[0]\"\n ngClass=\"{{ headerRow.value.class }}\"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.rows || {})[headerRow.key].templateRef\n ? templateRef\n : (table.config.rows || {})[headerRow.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: column,\n column: headerRow,\n transform: (table.config.rows || {})[headerRow.key].transform,\n templateRef: (table.config.rows || {})[headerRow.key].templateRef,\n index: 0\n }\"\n >\n </ng-container>\n </th>\n </ng-container>\n </tr>\n </thead>\n <tbody *ngIf=\"loading$ | async; else tableContent\">\n <tr>\n <td class=\"p-0\" [colSpan]=\"colspan$ | async\">\n <ng-content select=\".table-loading\"></ng-content>\n </td>\n </tr>\n </tbody>\n <tfoot *ngIf=\"(table$ | async)! as table\">\n <ng-container *ngIf=\"table.data.length > 0 && !(loading$ | async)\">\n <ng-container *ngIf=\"(calculations$ | async)! as calculations\">\n <tr\n *ngFor=\"let calculation of calculations.calculations; let i = index\"\n >\n <ng-container\n *ngIf=\"{\n showHeader: (colspan$ | async) !== (footerColspan$ | async)\n } as footerRow\"\n >\n <th\n *ngIf=\"footerRow.showHeader\"\n [colSpan]=\"\n ((colspan$ | async) || 0) - ((footerColspan$ | async) || 0)\n \"\n scope=\"row\"\n >\n <ng-container\n *ngIf=\"table.config?.footer?.headers?.[calculation] as showHeader\"\n >{{showHeader === true ? (calculation | capitalCase): table.config.footer?.headers?.[calculation]}}\n </ng-container>\n </th>\n <ng-container\n *ngFor=\"\n let column of table.config?.columns | keyvalue: columnOrder\n \"\n >\n <td\n *ngIf=\"\n !column.value?.hidden && calculations.calculated[column.key]\n \"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [attr.data-header]=\"\n !footerRow.showHeader && table.config.footer?.headers?.[calculation]\n ? table.config.footer?.headers?.[calculation] === true ? (calculation | capitalCase) : table.config.footer?.headers?.[calculation]\n : null\n \"\n [attr.data-label]=\"\n table.config.mobileLayout && column.value.mobileHeader\n ? column.value.mobileHeader !== true\n ? column.value.mobileHeader\n : (column.value.header || column.key | capitalCase)\n : null\n \"\n [class.gt-no-content]=\"\n !calculations.calculated[column.key][calculation]\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.columns || {})[column.key].templateRef\n ? templateRef\n : (table.config.columns || {})[column.key].transform\n ? transformFooter\n : rawFooter\n \"\n [ngTemplateOutletContext]=\"{\n value: calculations.calculated[column.key][calculation],\n row: calculations.calculated[column.key],\n column: calculation,\n templateRef: (table.config.columns || {})[column.key]\n .templateRef,\n transform: (table.config.columns || {})[column.key]\n .transform\n }\"\n ></ng-container>\n </td>\n </ng-container>\n </ng-container>\n </tr>\n </ng-container>\n </ng-container>\n </tfoot>\n</table>\n<ng-template #tableContent>\n <ng-container *ngIf=\"(table$ | async)! as table\">\n <tbody *ngIf=\"(table!.data![0] || table!.data!).length > 0; else noData\">\n <ng-container *ngIf=\"table.config.columns\">\n <tr\n *ngFor=\"\n let row of table!.data![\n table.info.lazyLoaded ? 0 : (currentPaginationIndex$ | async) || 0\n ];\n trackBy: trackRowByFn;\n let i = index\n \"\n [attr.id]=\"'tableRow_' + i\"\n (click)=\"table?.config?.rowClick && _rowClick(row, i, $event)\"\n (mouseenter)=\"\n table?.config?.activateRowOnHover && _activateRow(row, i, $event)\n \"\n (mouseleave)=\"\n table?.config?.activateRowOnHover &&\n _activateRow(null, null, $event)\n \"\n [ngClass]=\"[\n !!isRowSelectedFn\n ? (row\n | rowSelection\n : selection\n : isRowSelectedFn\n : customClasses.selectedRow)\n : '',\n (rowActive$ | async)?.index === i ? customClasses.activeRow : ''\n ]\"\n >\n <ng-container\n *ngFor=\"let column of table.config?.columns | keyvalue: columnOrder\"\n >\n <td\n *ngIf=\"!column.value?.hidden\"\n ngClass=\"{{ (column.key | dashCase) + '-column' }} {{\n column.value.class\n }}\"\n [attr.data-label]=\"\n table.config.mobileLayout && column.value.mobileHeader\n ? column.value.mobileHeader !== true\n ? column.value.mobileHeader\n : (column.value.header || column.key | capitalCase)\n : null\n \"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (searchBy$ | async) &&\n !(table.config.columns || {})[column.key].templateRef\n ? highlighted\n : (table.config.columns || {})[column.key].templateRef\n ? templateRef\n : (table.config.columns || {})[column.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: row,\n column: column,\n search: (searchBy$ | async),\n transform: (table.config.columns || {})[column.key].transform,\n templateRef: (table.config.columns || {})[column.key]\n .templateRef,\n index: i,\n data: table.data[\n table.info.lazyLoaded\n ? 0\n : (currentPaginationIndex$ | async) || 0\n ]\n }\"\n ></ng-container>\n </td>\n </ng-container>\n </tr>\n </ng-container>\n <ng-container *ngIf=\"table.config.rows\">\n <ng-container\n *ngFor=\"\n let row of table?.config?.rows | keyvalue: columnOrder | slice: 1;\n let i = index\n \"\n >\n <tr\n *ngIf=\"!row.value?.hidden\"\n [attr.id]=\"'tableRow_' + i\"\n ngClass=\"{{ (row.key | dashCase) + '-row' }}\"\n (click)=\"table?.config?.rowClick && _rowClick(row, i, $event)\"\n (mouseenter)=\"\n table?.config?.activateRowOnHover && _activateRow(row, i, $event)\n \"\n (mouseleave)=\"\n table?.config?.activateRowOnHover &&\n _activateRow(null, null, $event)\n \"\n [ngClass]=\"[\n !!isRowSelectedFn\n ? (row\n | rowSelection\n : selection\n : isRowSelectedFn\n : customClasses.selectedRow)\n : '',\n (rowActive$ | async)?.index === i ? customClasses.activeRow : ''\n ]\"\n >\n <th class=\"row-header\" scope=\"row\">\n {{ row.value.header || row.key | capitalCase }}\n </th>\n <td\n *ngFor=\"let column of (table?.data || [])[0]; let y = index\"\n ngClass=\"{{ row.value.class }}\"\n >\n <ng-container\n [ngTemplateOutlet]=\"\n (table.config.rows || {})[row.key].templateRef\n ? templateRef\n : (table.config.rows || {})[row.key].transform\n ? transformData\n : rawData\n \"\n [ngTemplateOutletContext]=\"{\n row: column,\n column: row,\n transform: (table.config.rows || {})[row.key].transform,\n templateRef: (table.config.rows || {})[row.key].templateRef,\n index: table.config.rows ? y : i,\n data: table.data[\n table.info.lazyLoaded\n ? 0\n : (currentPaginationIndex$ | async) || 0\n ]\n }\"\n >\n </ng-container>\n </td>\n </tr>\n </ng-container>\n </ng-container>\n </tbody>\n </ng-container>\n</ng-template>\n<ng-template #noData>\n <tbody>\n <tr>\n <td class=\"p-0\" [colSpan]=\"colspan$ | async\">\n <ng-content select=\".table-no-data\"></ng-content>\n </td>\n </tr>\n </tbody>\n</ng-template>\n<ng-template\n #highlighted\n let-row=\"row\"\n let-column=\"column\"\n let-search=\"search\"\n let-transform=\"transform\"\n>\n <div\n *ngIf=\"!transform\"\n [innerHTML]=\"row[column.key] | highlight: search\"\n ></div>\n <div\n *ngIf=\"transform\"\n [innerHTML]=\"\n row[column.key]\n | dynamicPipe: transform.pipe:transform?.args\n | highlight: search\n \"\n ></div>\n</ng-template>\n<ng-template #rawData let-row=\"row\" let-column=\"column\">\n {{ row[column.key] }}\n</ng-template>\n<ng-template\n #transformData\n let-row=\"row\"\n let-column=\"column\"\n let-transform=\"transform\"\n let-data=\"data\"\n>\n {{ row[column.key] | dynamicPipe: transform.pipe:transform?.args }}\n</ng-template>\n<ng-template #transformFooter let-value=\"value\" let-transform=\"transform\">\n {{\n (value | dynamicPipe: transform.pipe:transform?.args) ||\n (tableConfig$ | async)?.footer?.emptyContent\n }}\n</ng-template>\n<ng-template #rawFooter let-value=\"value\">\n {{ value || (tableConfig$ | async)?.footer?.emptyContent }}\n</ng-template>\n<ng-template\n #templateRef\n let-row=\"row\"\n let-column=\"column\"\n let-index=\"index\"\n let-templateRef=\"templateRef\"\n let-data=\"data\"\n>\n <ng-container\n [ngTemplateOutlet]=\"templateRef\"\n [ngTemplateOutletContext]=\"{\n row: row,\n col: column,\n index: index,\n data: data\n }\"\n ></ng-container>\n</ng-template>\n" }]
|
|
517
|
+
}], propDecorators: { navigationKeys: [{
|
|
518
|
+
type: Input
|
|
519
|
+
}], selectKeys: [{
|
|
520
|
+
type: Input
|
|
521
|
+
}], loading: [{
|
|
522
|
+
type: Input
|
|
523
|
+
}], paginationIndex: [{
|
|
524
|
+
type: Input
|
|
525
|
+
}], pagingInfo: [{
|
|
526
|
+
type: Input
|
|
527
|
+
}], customClasses: [{
|
|
528
|
+
type: Input
|
|
529
|
+
}], isRowSelectedFn: [{
|
|
530
|
+
type: Input
|
|
531
|
+
}], selection: [{
|
|
532
|
+
type: Input
|
|
533
|
+
}], rowIdKey: [{
|
|
534
|
+
type: Input
|
|
535
|
+
}], generateRowId: [{
|
|
536
|
+
type: Input
|
|
537
|
+
}], trackRowByFn: [{
|
|
538
|
+
type: Input
|
|
539
|
+
}], search: [{
|
|
540
|
+
type: Input
|
|
541
|
+
}], config: [{
|
|
542
|
+
type: Input
|
|
543
|
+
}], data: [{
|
|
544
|
+
type: Input
|
|
545
|
+
}], sortOrder: [{
|
|
546
|
+
type: Input
|
|
547
|
+
}], rowClick: [{
|
|
548
|
+
type: Output
|
|
549
|
+
}], rowSelect: [{
|
|
550
|
+
type: Output
|
|
551
|
+
}], sortOrderChange: [{
|
|
552
|
+
type: Output
|
|
553
|
+
}], rowActive: [{
|
|
554
|
+
type: Output
|
|
555
|
+
}], columnSort: [{
|
|
556
|
+
type: Output
|
|
557
|
+
}], pageChange: [{
|
|
558
|
+
type: Output
|
|
559
|
+
}] } });
|
|
560
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29yZS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9jb3JlL3NyYy9saWIvY29yZS5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9wcm9qZWN0cy9jb3JlL3NyYy9saWIvY29yZS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLFNBQVMsRUFDVCxZQUFZLEVBQ1osS0FBSyxFQUVMLE1BQU0sR0FFUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQ0wsZUFBZSxFQUNmLGFBQWEsRUFDYixTQUFTLEVBQ1QsWUFBWSxFQUVaLEVBQUUsRUFDRixhQUFhLEVBQ2IsT0FBTyxHQUNSLE1BQU0sTUFBTSxDQUFDO0FBRWQsT0FBTyxFQUNMLFNBQVMsRUFFVCxZQUFZLEVBQ1osT0FBTyxFQUNQLE9BQU8sRUFDUCxJQUFJLEVBQ0osZ0JBQWdCLEVBQ2hCLFNBQVMsR0FDVixNQUFNLGlCQUFpQixDQUFDO0FBQ3pCLE9BQU8sRUFFTCxvQkFBb0IsRUFDcEIsTUFBTSxFQUNOLEdBQUcsRUFDSCxLQUFLLEVBQ0wsV0FBVyxFQUNYLFNBQVMsRUFDVCxTQUFTLEVBQ1QsSUFBSSxFQUNKLFNBQVMsRUFDVCxHQUFHLEVBQ0gsY0FBYyxHQUNmLE1BQU0sZ0JBQWdCLENBQUM7QUFFeEIsT0FBTyxFQUNMLFNBQVMsRUFDVCxLQUFLLEVBQ0wsTUFBTSxFQUNOLGtCQUFrQixHQUNuQixNQUFNLHVCQUF1QixDQUFDO0FBVS9CLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDeEQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3RELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNuRCxPQUFPLEVBQUUs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SztnQkFnQk8sYUFBYTtzQkFBekIsS0FBSztnQkFnQk8sZUFBZTtzQkFBM0IsS0FBSztnQkFnQkcsU0FBUztzQkFBakIsS0FBSztnQkFNRyxRQUFRO3NCQUFoQixLQUFLO2dCQU1HLGFBQWE7c0JBQXJCLEtBQUs7Z0JBTU8sWUFBWTtzQkFBeEIsS0FBSztnQkFlRixNQUFNO3NCQURULEtBQUs7Z0JBTUYsTUFBTTtzQkFEVCxLQUFLO2dCQVFGLElBQUk7c0JBRFAsS0FBSztnQkFPTyxTQUFTO3NCQUFyQixLQUFLO2dCQU9JLFFBQVE7c0JBQWpCLE1BQU07Z0JBQ0csU0FBUztzQkFBbEIsTUFBTTtnQkFDRyxlQUFlO3NCQUF4QixNQUFNO2dCQVdHLFNBQVM7c0JBQWxCLE1BQU07Z0JBQ0csVUFBVTtzQkFBbkIsTUFBTTtnQkFFRyxVQUFVO3NCQUFuQixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENvbXBvbmVudCxcbiAgRXZlbnRFbWl0dGVyLFxuICBJbnB1dCxcbiAgT25EZXN0cm95LFxuICBPdXRwdXQsXG4gIFRyYWNrQnlGdW5jdGlvbixcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICBCZWhhdmlvclN1YmplY3QsXG4gIGNvbWJpbmVMYXRlc3QsXG4gIGZyb21FdmVudCxcbiAgaXNPYnNlcnZhYmxlLFxuICBPYnNlcnZhYmxlLFxuICBvZixcbiAgUmVwbGF5U3ViamVjdCxcbiAgU3ViamVjdCxcbn0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBUYWJsZUNvbmZpZyB9IGZyb20gJy4vbW9kZWxzL3RhYmxlLWNvbmZpZy5pbnRlcmZhY2UnO1xuaW1wb3J0IHtcbiAgQXN5bmNQaXBlLFxuICBLZXlWYWx1ZSxcbiAgS2V5VmFsdWVQaXBlLFxuICBOZ0NsYXNzLFxuICBOZ0Zvck9mLFxuICBOZ0lmLFxuICBOZ1RlbXBsYXRlT3V0bGV0LFxuICBTbGljZVBpcGUsXG59IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQge1xuICBkZWJvdW5jZVRpbWUsXG4gIGRpc3RpbmN0VW50aWxDaGFuZ2VkLFxuICBmaWx0ZXIsXG4gIG1hcCxcbiAgcGx1Y2ssXG4gIHNoYXJlUmVwbGF5LFxuICBzdGFydFdpdGgsXG4gIHN3aXRjaE1hcCxcbiAgdGFrZSxcbiAgdGFrZVVudGlsLFxuICB0YXAsXG4gIHdpdGhMYXRlc3RGcm9tLFxufSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBUYWJsZUNvbHVtbiB9IGZyb20gJy4vbW9kZWxzL3RhYmxlLWNvbHVtbi5pbnRlcmZhY2UnO1xuaW1wb3J0IHtcbiAgY2FsY3VsYXRlLFxuICBjaHVuayxcbiAgc2VhcmNoLFxuICBzb3J0T25NdWx0aXBsZUtleXMsXG59IGZyb20gJy4vdXRpbGl0aWVzL3V0aWxpdGllcyc7XG5pbXBvcnQgeyBUYWJsZVJvdyB9IGZyb20gJy4vbW9kZWxzL3RhYmxlLXJvdy5pbnRlcmZhY2UnO1xuaW1wb3J0IHsgR3RPcmRlciwgR3RTb3J0T3JkZXIgfSBmcm9tICcuL21vZGVscy90YWJsZS1zb3J0LmludGVyZmFjZSc7XG5pbXBvcnQge1xuICBHdFBhZ2VDaGFuZ2VFdmVudCxcbiAgR3RSb3dTZWxlY3RFdmVudCxcbiAgR3RSb3dDbGlja0V2ZW50LFxuICBHdFJvd0FjdGl2ZUV2ZW50LFxuICBHdFNvcnRFdmVudCxcbn0gZnJvbSAnLi9tb2RlbHMvdGFibGUtZXZlbnRzLmludGVyZmFjZSc7XG5pbXBvcnQgeyBDYXBpdGFsQ2FzZVBpcGUgfSBmcm9tICcuL3BpcGVzL2NhcGl0YWwtY2FzZS5waXBlJztcbmltcG9ydCB7IFNvcnRDbGFzc1BpcGUgfSBmcm9tICcuL3BpcGVzL3NvcnQtY2xhc3MucGlwZSc7XG5pbXBvcnQgeyBEYXNoQ2FzZVBpcGUgfSBmcm9tICcuL3BpcGVzL2Rhc2gtY2FzZS5waXBlJztcbmltcG9ydCB7IER5bmFtaWNQaXBlIH0gZnJvbSAnLi9waXBlcy9keW5hbWljLnBpcGUnO1xuaW1wb3J0IHsgSGlnaGxpZ2h0UGlwZSB9IGZyb20gJy4vcGlwZXMvaGlnaGxpZ2h0LnBpcGUnO1xuaW1wb3J0IHsgUm93U2VsZWN0aW9uUGlwZSB9IGZyb20gJy4vcGlwZXMvcm93LXNlbGVjdGlvbi5waXBlJztcbmltcG9ydCB7IEd0UGFnaW5hdGlvbkluZm8gfSBmcm9tICcuL21vZGVscy9ndC1wYWdpbmF0aW9uJztcbmltcG9ydCB7IFRhYmxlSW5mbyB9IGZyb20gJy4vbW9kZWxzL3RhYmxlLWluZm8uaW50ZXJmYWNlJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYW5ndWxhci1nZW5lcmljLXRhYmxlJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2NvcmUuY29tcG9uZW50Lmh0bWwnLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIENhcGl0YWxDYXNlUGlwZSxcbiAgICBLZXlWYWx1ZVBpcGUsXG4gICAgU29ydENsYXNzUGlwZSxcbiAgICBEYXNoQ2FzZVBpcGUsXG4gICAgUm93U2VsZWN0aW9uUGlwZSxcbiAgICBBc3luY1BpcGUsXG4gICAgTmdUZW1wbGF0ZU91dGxldCxcbiAgICBTbGljZVBpcGUsXG4gICAgRHluYW1pY1BpcGUsXG4gICAgSGlnaGxpZ2h0UGlwZSxcbiAgICBOZ0NsYXNzLFxuICAgIE5nSWYsXG4gICAgTmdGb3JPZixcbiAgXSxcbn0pXG5leHBvcnQgY2xhc3MgQ29yZUNvbXBvbmVudCBpbXBsZW1lbnRzIE9uRGVzdHJveSB7XG4gIHVuc3Vic2NyaWJlJCA9IG5ldyBTdWJqZWN0KCk7XG4gIGdldCBuYXZpZ2F0aW9uS2V5cygpOiB0eXBlb2YgdGhpcy5fbmF2aWdhdGlvbktleXMge1xuICAgIHJldHVybiB0aGlzLl9uYXZpZ2F0aW9uS2V5cztcbiAgfVxuXG4gIC8qKiBuYXZpZ2F0aW9uS2V5c1xuICAgKiBAZGVzY3JpcHRpb24gQW4gYXJyYXkgb2Yga2V5Ym9hcmQga2V5cyB0aGF0IHdpbGwgdHJpZ2dlciBuYXZpZ2F0aW9uIGFuZCBhY3RpdmUgcm93LCBjdXJyZW50bHkgb25seSBzdXBwb3J0cyBhcnJvdyBrZXlzLCBob21lIGFuZCBlbmQgKG9taXQga2V5IG5hbWUgZnJvbSBhcnJheSB0byBkaXNhYmxlIGl0KVxuICAgKiBAdHlwZSB7c3RyaW5nW119XG4gICAqIEBkZWZhdWx0IFsnQXJyb3dEb3duJywgJ0Fycm93VXAnLCAnQXJyb3dMZWZ0JywgJ0Fycm93UmlnaHQnLCAnSG9tZScsICdFbmQnXVxuICAgKi9cbiAgQElucHV0KCkgc2V0IG5hdmlnYXRpb25LZXlzKHZhbHVlOiB0eXBlb2YgdGhpcy5fbmF2aWdhdGlvbktleXMpIHtcbiAgICB0aGlzLl9uYXZpZ2F0aW9uS2V5cyA9IHZhbHVlO1xuICB9XG5cbiAgcHJpdmF0ZSBfbmF2aWdhdGlvbktleXMgPSBbXG4gICAgJ0Fycm93RG93bicsXG4gICAgJ0Fycm93VXAnLFxuICAgICdBcnJvd0xlZnQnLFxuICAgICdBcnJvd1JpZ2h0JyxcbiAgICAnSG9tZScsXG4gICAgJ0VuZCcsXG4gIF07XG4gIGdldCBzZWxlY3RLZXlzKCk6IHN0cmluZ1tdIHtcbiAgICByZXR1cm4gdGhpcy5fc2VsZWN0S2V5cztcbiAgfVxuXG4gIC8qKiBzZWxlY3RLZXlzXG4gICAqIEBkZXNjcmlwdGlvbiBBbiBhcnJheSBvZiBrZXlib2FyZCBrZXlzIHRoYXQgd2lsbCB0cmlnZ2VyIHJvdyBzZWxlY3Rpb24gKG9taXQga2V5IG5hbWUgZnJvbSBhcnJheSB0byBkaXNhYmxlIGl0KVxuICAgKiBAdHlwZSB7c3RyaW5nW119XG4gICAqIEBkZWZhdWx0IFsnRW50ZXInLCAnICddXG4gICAqL1xuICBASW5wdXQoKSBzZXQgc2VsZWN0S2V5cyh2YWx1ZTogc3RyaW5nW10pIHtcbiAgICB0aGlzLl9zZWxlY3RLZXlzID0gdmFsdWU7XG4gIH1cblxuICBwcml2YXRlIF9zZWxlY3RLZXlzID0gWydFbnRlcicsICcgJ107XG4gIGdldCBzb3J0T3JkZXIkKCk6IE9ic2VydmFibGU8R3RTb3J0T3JkZXI+IHtcbiAgICByZXR1cm4gdGhpcy5fc29ydE9yZGVyJC5hc09ic2VydmFibGUoKTtcbiAgfVxuXG4gIEBJbnB1dCgpIHNldCBsb2FkaW5nKGlzTG9hZGluZzogT2JzZXJ2YWJsZTxib29sZWFuPiB8IGJvb2xlYW4pIHtcbiAgICB0aGlzLl9sb2FkaW5nJC5uZXh0KGlzTG9hZGluZyk7XG4gIH1cbiAgQElucHV0KClcbiAgc2V0IHBhZ2luYXRpb25JbmRleChwYWdlSW5kZXg6IG51bWJlcikge1xuICAgIHRoaXMuX2N1cnJlbnRQYWdpbmF0aW9uSW5kZXgkLm5leHQocGFnZUluZGV4KTtcbiAgfVxuXG4gIGdldCBwYWdpbmF0aW9uSW5kZXgoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5fY3VycmVudFBhZ2luYXRpb25JbmRleCQuZ2V0VmFsdWUoKTtcbiAgfVxuXG4gIEBJbnB1dCgpIHNldCBwYWdpbmdJbmZvKHZhbHVlOiBHdFBhZ2luYXRpb25JbmZvIHwgbnVsbCkge1xuICAgIGlmICh2YWx1ZSkge1xuICAgICAgdGhpcy5fcGFnaW5nSW5mbyQubmV4dCh2YWx1ZSk7XG4gICAgICBpZiAoXG4gICAgICAgIHZhbHVlLnBhZ2VDdXJyZW50ICE9PSB0aGlzLl9jdXJyZW50UGFnaW5hdGlvbkluZGV4JC5nZXRWYWx1ZSgpICsgMSAmJlxuICAgICAgICB2YWx1ZS5wYWdlQ3VycmVudCAhPT0gbnVsbFxuICAgICAgKSB7XG4gICAgICAgIHRoaXMucGFnaW5hdGlvbkluZGV4ID0gdmFsdWUucGFnZUN1cnJlbnQgLSAxO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBjdXN0b21DbGFzc2VzXG4gICAqIEBkZXNjcmlwdGlvbiBBbiBvYmplY3QgdGhhdCBjb250YWlucyBjdXN0b20gY2xhc3NlcyBmb3IgdmFyaW91cyBlbGVtZW50cyBpbiB0aGUgdGFibGUuXG4gICAqIEB0eXBlIHtvYmplY3R9IC0geyBzZWxlY3RlZFJvdzogc3RyaW5nLCBhY3RpdmVSb3c6IHN0cmluZyB9IC0gZGVmYXVsdCBjbGFzc2VzIGFyZSAnZ3Qtc2VsZWN0ZWQnIGFuZCAnZ3QtYWN0aXZlJ1xuICAgKi9cbiAgQElucHV0KCkgc2V0IGN1c3RvbUNsYXNzZXMoY2xhc3NlczogUGFydGlhbDx0eXBlb2YgdGhpcy5fY3VzdG9tQ2xhc3Nlcz4pIHtcbiAgICB0aGlzLl9jdXN0b21DbGFzc2VzID0geyAuLi50aGlzLl9jdXN0b21DbGFzc2VzLCAuLi5jbGFzc2VzIH07XG4gIH1cblxuICBnZXQgY3VzdG9tQ2xhc3NlcygpOiB0eXBlb2YgdGhpcy5fY3VzdG9tQ2xhc3NlcyB7XG4gICAgcmV0dXJuIHRoaXMuX2N1c3RvbUNsYXNzZXM7XG4gIH1cblxuICBwcml2YXRlIF9jdXN0b21DbGFzc2VzID0ge1xuICAgIHNlbGVjdGVkUm93OiAnZ3Qtc2VsZWN0ZWQnLFxuICAgIGFjdGl2ZVJvdzogJ2d0LWFjdGl2ZScsXG4gIH07XG5cbiAgLyoqIGlzUm93U2VsZWN0ZWRGblxuICAgKiBAZGVzY3JpcHRpb24gRnVuY3Rpb24gdG8gZGV0ZXJtaW5lIGlmIHJvdyBpcyBzZWxlY3RlZCBvciBub3QuXG4gICAqIEB0eXBlIHtmbn0gQSBmdW5jdGlvbiB0aGF0IHJlY2VpdmVzIGEgcm93IG9iamVjdCBhbmQgb3B0aW9uYWwgc3RhdGUgZm9yIGN1cnJlbnQgc2VsZWN0aW9uIHRoYXQgY2FuIGJlIHVzZWQgdG8gZGV0ZXJtaW5lIGlmIHJvdyBzaG91bGQgYmUgbWFya2VkIGFzIHNlbGVjdGVkIG9yIG5vdC4gKi9cbiAgQElucHV0KCkgc2V0IGlzUm93U2VsZWN0ZWRGbihcbiAgICBmbjogKHJvdzogVGFibGVSb3cgfCBhbnksIHNlbGVjdGlvbj86IGFueSkgPT4gYm9vbGVhblxuICApIHtcbiAgICB0aGlzLl9pc1Jvd1NlbGVjdGVkRm4gPSBmbjtcbiAgfVxuXG4gIGdldCBpc1Jvd1NlbGVjdGVkRm4oKTogYW55IHtcbiAgICByZXR1cm4gdGhpcy5faXNSb3dTZWxlY3RlZEZuO1xuICB9XG5cbiAgcHJpdmF0ZSBfaXNSb3dTZWxlY3RlZEZuOiBhbnk7XG5cbiAgLyoqIHNlbGVjdGlvblxuICAgKiBAZGVzY3JpcHRpb24gQW4gb2JqZWN0IHRoYXQgY29udGFpbnMgdGhlIGN1cnJlbnRseSBzZWxlY3RlZCByb3cocykgaW4gdGhlIHRhYmxlLiBJdCdzIHBhc3NlZCB0byB0aGUgc2VsZWN0aW9uIGZ1bmN0aW9uIHRvIGRldGVybWluZSB3aGljaCByb3dzIHNob3VsZCBiZSBzZWxlY3RlZC5cbiAgICogQHR5cGUge2FueX1cbiAgICovXG4gIEBJbnB1dCgpIHNlbGVjdGlvbjogYW55ID0ge307XG5cbiAgLyoqIHJvd0lkS2V5XG4gICAqIEBkZXNjcmlwdGlvbiByb3cga2V5IHRvIHVzZSBhcyB1bmlxdWUgaWQgZm9yIHRhYmxlIHJvdy4gSWYgcGFzc2VkLCB0YWJsZSB3b24ndCBnZW5lcmF0ZSB1bmlxdWUgaWRzIGZvciBlYWNoIHJvdyBidXQgaW5zdGVhZCB1c2Uga2V5IHRvIHJldHJpZXZlIHVuaXF1ZSBpZCBmcm9tIHJvdy5cbiAgICogQHR5cGUge3N0cmluZ31cbiAgICovXG4gIEBJbnB1dCgpIHJvd0lkS2V5OiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgLyoqIGdlbmVyYXRlUm93SWRcbiAgICogQGRlc2NyaXB0aW9uIFdoZXRoZXIgb3Igbm90IHRvIGdlbmVyYXRlIGEgdW5pcXVlIGlkIGZvciBlYWNoIHJvdyBpbiB0aGUgdGFibGUuIERlZmF1bHRzIHRvIGB0cnVlYC5cbiAgICogQHR5cGUge2Jvb2xlYW59XG4gICAqL1xuICBASW5wdXQoKSBnZW5lcmF0ZVJvd0lkOiBib29sZWFuID0gdHJ1ZTtcblxuICAvKiogdHJhY2tSb3dCeUZuXG4gICAqIEBkZXNjcmlwdGlvbiBBIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhIHVuaXF1ZSBpZGVudGlmaWVyIGZvciBlYWNoIHJvdyBpbiB0aGUgdGFibGUgdG8gb3B0aW1pemUgcmVuZGVyaW5nIHdoZW4gZGF0YSBpcyBhZGRlZCBvciByZW1vdmVkLlxuICAgKiBAdHlwZSBmbiAtIFRyYWNrQnlGdW5jdGlvbiB0byByZXRyaWV2ZSB1bmlxdWUgaWQgYmFzZWQgb24gaW5kZXggYW5kL29yIHJvdy4gRGVmYXVsdHMgdG8gdXNpbmcgYHJvd1t0aGlzLnJvd0lkS2V5XWAuXG4gICAqL1xuICBASW5wdXQoKSBzZXQgdHJhY2tSb3dCeUZuKGZuOiBUcmFja0J5RnVuY3Rpb248VGFibGVSb3c+KSB7XG4gICAgdGhpcy5fdHJhY2tSb3dCeUZuID0gZm47XG4gIH1cbiAgZ2V0IHRyYWNrUm93QnlGbigpOiBUcmFja0J5RnVuY3Rpb248VGFibGVSb3c+IHtcbiAgICByZXR1cm4gdGhpcy5fdHJhY2tSb3dCeUZuO1xuICB9XG5cbiAgcHJpdmF0ZSBfdHJhY2tSb3dCeUZuKFxuICAgIGluZGV4OiBudW1iZXIsXG4gICAgcm93OiBUYWJsZVJvd1xuICApOiBUcmFja0J5RnVuY3Rpb248VGFibGVSb3c+IHtcbiAgICByZXR1cm4gdGhpcy5yb3dJZEtleSA/IHJvd1t0aGlzLnJvd0lkS2V5XSA6IHJvdz8uX2lkO1xuICB9XG5cbiAgQElucHV0KClcbiAgc2V0IHNlYXJjaChzdHJpbmc6IE9ic2VydmFibGU8c3RyaW5nIHwgbnVsbD4gfCBzdHJpbmcgfCBudWxsKSB7XG4gICAgdGhpcy5fc2VhcmNoQnkkLm5leHQoc3RyaW5nKTtcbiAgfVxuXG4gIEBJbnB1dCgpXG4gIHNldCBjb25maWcoY29uZmlnOiBPYnNlcnZhYmxlPFRhYmxlQ29uZmlnPGFueT4+IHwgVGFibGVDb25maWc8YW55Pikge1xuICAgIHRoaXMuX3RhYmxlQ29uZmlnJC5uZXh0KGNvbmZpZyk7XG4gIH1cblxuICBwcml2YXRlIF90YWJsZUNvbmZpZzogVGFibGVDb25maWc8YW55PiB8IHVuZGVmaW5lZDtcblxuICBASW5wdXQoKVxuICBzZXQgZGF0YShkYXRhOiBPYnNlcnZhYmxlPEFycmF5PFRhYmxlUm93Pj4gfCBBcnJheTxUYWJsZVJvdz4gfCBudWxsKSB7XG4gICAgaWYgKGRhdGEpIHtcbiAgICAgIHRoaXMuX2RhdGEkLm5leHQoZGF0YSk7XG4gICAgfVxuICB9XG5cbiAgQElucHV0KCkgc2V0IHNvcnRPcmRlcihzb3J0Q29uZmlnOiBHdFNvcnRPcmRlcjxUYWJsZVJvdz4gfCBhbnkpIHtcbiAgICBpZiAoSlNPTi5zdHJpbmdpZnkoc29ydENvbmZpZykgIT09IEpTT04uc3RyaW5naWZ5KHRoaXMuX3NvcnRPcmRlciQudmFsdWUpKSB7XG4gICAgICB0aGlzLnNvcnRPcmRlckNoYW5nZS5lbWl0KHNvcnRDb25maWcpO1xuICAgICAgdGhpcy5fc29ydE9yZGVyJC5uZXh0KHNvcnRDb25maWcpO1xuICAgIH1cbiAgfVxuXG4gIEBPdXRwdXQoKSByb3dDbGljayA9IG5ldyBFdmVudEVtaXR0ZXI8R3RSb3dDbGlja0V2ZW50PigpO1xuICBAT3V0cHV0KCkgcm93U2VsZWN0ID0gbmV3IEV2ZW50RW1pdHRlcjxHdFJvd1NlbGVjdEV2ZW50PigpO1xuICBAT3V0cHV0KCkgc29ydE9yZGVyQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxHdFNvcnRPcmRlcjxUYWJsZVJvdz4+KCk7XG5cbiAgX3Jvd0NsaWNrKHJvdzogVGFibGVSb3csIGluZGV4OiBudW1iZXIsIGV2ZW50OiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgdGhpcy5yb3dDbGljay5lbWl0KHsgcm93LCBpbmRleCwgZXZlbnQgfSk7XG4gIH1cblxuICBfcm93QWN0aXZlKHJvdzogVGFibGVSb3csIGluZGV4OiBudW1iZXIsIGV2ZW50OiBLZXlib2FyZEV2ZW50KTogdm9pZCB7XG4gICAgdGhpcy5yb3dTZWxlY3QuZW1pdCh7IHJvdywgaW5kZXgsIGV2ZW50IH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBfcm93QWN0aXZlJCA9IG5ldyBSZXBsYXlTdWJqZWN0PEd0Um93QWN0aXZlRXZlbnQ+KDEpO1xuICBAT3V0cHV0KCkgcm93QWN0aXZlID0gbmV3IEV2ZW50RW1pdHRlcjxHdFJvd0FjdGl2ZUV2ZW50PigpO1xuICBAT3V0cHV0KCkgY29sdW1uU29ydCA9IG5ldyBFdmVudEVtaXR0ZXI8R3RTb3J0RXZlbnQ+KCk7XG4gIC8qKiBwYWdlIGNoYW5nZSBldmVudCAtIGVtaXR0ZWQgd2hlbiBjdXJyZW50IHBhZ2UvaW5kZXggY2hhbmdlcyBmb3IgcGFnaW5hdGlvbiAqL1xuICBAT3V0cHV0KCkgcGFnZUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8R3RQYWdlQ2hhbmdlRXZlbnQ+KCk7XG4gIHJvd0FjdGl2ZSQgPSB0aGlzLl9yb3dBY3RpdmUkLmFzT2JzZXJ2YWJsZSgpLnBpcGUoXG4gICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKHAsIHEpID0+IHtcbiAgICAgIGlmICh0aGlzLnJvd0lkS2V5ICYmIHAucm93ICYmIHEucm93KSB7XG4gICAgICAgIHJldHVybiBwLnJvd1t0aGlzLnJvd0lkS2V5XSA9PT0gcS5yb3dbdGhpcy5yb3dJZEtleV07XG4gICAgICB9IGVsc2UgaWYgKHRoaXMuZ2VuZXJhdGVSb3dJZCAmJiBwLnJvdyAmJiBxLnJvdykge1xuICAgICAgICByZXR1cm4gcC5yb3cuX2lkID09PSBxLnJvdy5faWQ7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gcC5pbmRleCA9PT0gcS5pbmRleDtcbiAgICAgIH1cbiAgICB9KSxcbiAgICB0YXAoKGV2ZW50KSA9PiAodGhpcy5hY3RpdmVSb3dJbmRleCA9IGV2ZW50LmluZGV4KSksXG4gICAgdGFwKChldmVudCkgPT4gdGhpcy5yb3dBY3RpdmUuZW1pdChldmVudCkpLFxuICAgIHNoYXJlUmVwbGF5KDEpXG4gICk7XG5cbiAgYWN0aXZlUm93SW5kZXg6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICBhY3RpdmF0ZVJvdyhpZDogc3RyaW5nLCBldmVudD86IE1vdXNlRXZlbnQgfCBLZXlib2FyZEV2ZW50KTogdm9pZDtcbiAgYWN0aXZhdGVSb3coaW5kZXg6IG51bWJlciwgZXZlbnQ/OiBNb3VzZUV2ZW50IHwgS2V5Ym9hcmRFdmVudCk6IHZvaWQ7XG4gIGFjdGl2YXRlUm93KG5vbmU6IG51bGwsIGV2ZW50PzogTW91c2VFdmVudCB8IEtleWJvYXJkRXZlbnQpOiB2b2lkO1xuICBhY3RpdmF0ZVJvdyhcbiAgICBhcmc6IHN0cmluZyB8IG51bWJlciB8IG51bGwsXG4gICAgZXZlbnQ/OiBNb3VzZUV2ZW50IHwgS2V5Ym9hcmRFdmVudFxuICApOiB2b2lkIHtcbiAgICBpZiAodHlwZW9mIGFyZyA9PT0gJ251bWJlcicpIHtcbiAgICAgIHRoaXMudGFibGUkXG4gICAgICAgIC5waXBlKFxuICAgICAgICAgIHBsdWNrKCdkYXRhJyksXG4gICAgICAgICAgbWFwKChkYXRhKSA9PiBkYXRhW3RoaXMucGFnaW5hdGlvbkluZGV4XVthcmddKSxcbiAgICAgICAgICB0YWtlKDEpLFxuICAgICAgICAgIHRha2VVbnRpbCh0aGlzLnVuc3Vic2NyaWJlJClcbiAgICAgICAgKVxuICAgICAgICAuc3Vic2NyaWJlKChyb3cpID0+IHRoaXMuX2FjdGl2YXRlUm93KHJvdywgYXJnLCBldmVudCkpO1xuICAgIH0gZWxzZSBpZiAodHlwZW9mIGFyZyA9PT0gJ3N0cmluZycpIHtcbiAgICAgIC8vIFRPRE86IGltcGxlbWVudCBob3ZlciBieSBpZFxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLl9hY3RpdmF0ZVJvdyhudWxsLCBudWxsKTtcbiAgICB9XG4gIH1cbiAgcHJvdGVjdGVkIF9hY3RpdmF0ZVJvdyhcbiAgICByb3c6IFRhYmxlUm93IHwgbnVsbCxcbiAgICBpbmRleDogbnVtYmVyIHwgbnVsbCxcbiAgICBldmVudD86IE1vdXNlRXZlbnQgfCBLZXlib2FyZEV2ZW50XG4gICk6IHZvaWQge1xuICAgIHRoaXMuX3Jvd0FjdGl2ZSQubmV4dCh7IHJvdywgaW5kZXgsIGV2ZW50IH0pO1xuICB9XG5cbiAgZ2V0IGxvYWRpbmckKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuICAgIHJldHVybiB0aGlzLl9sb2FkaW5nJC5waXBlKFxuICAgICAgc3RhcnRXaXRoKGZhbHNlKSxcbiAgICAgIG1hcCgodmFsdWUpID0+IChpc09ic2VydmFibGUodmFsdWUpID8gdmFsdWUgOiBvZih2YWx1ZSkpKSxcbiAgICAgIHN3aXRjaE1hcCgob2JzKSA9PiBvYnMpLFxuICAgICAgc2hhcmVSZXBsYXkoMSlcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBfbG9hZGluZyQ6IFJlcGxheVN1YmplY3Q8T2JzZXJ2YWJsZTxib29sZWFuPiB8IGJvb2xlYW4+ID1cbiAgICBuZXcgUmVwbGF5U3ViamVjdCgxKTtcbiAgcHJpdmF0ZSBfc29ydE9yZGVyJDogQmVoYXZpb3JTdWJqZWN0PEd0U29ydE9yZGVyPiA9XG4gICAgbmV3IEJlaGF2aW9yU3ViamVjdDxHdFNvcnRPcmRlcj4oW10pO1xuICBwcml2YXRlIF9zZWFyY2hCeSQ6IFJlcGxheVN1YmplY3Q8T2JzZXJ2YWJsZTxzdHJpbmcgfCBudWxsPiB8IHN0cmluZyB8IG51bGw+ID1cbiAgICBuZXcgUmVwbGF5U3ViamVjdCgxKTtcbiAgc2VhcmNoQnkkOiBPYnNlcnZhYmxlPHN0cmluZyB8IG51bGw+ID0gdGhpcy5fc2VhcmNoQnkkLnBpcGUoXG4gICAgc3RhcnRXaXRoKCcnKSxcbiAgICBtYXAoKHZhbHVlKSA9PiAoaXNPYnNlcnZhYmxlKHZhbHVlKSA/IHZhbHVlIDogb2YodmFsdWUpKSksXG4gICAgc3dpdGNoTWFwKChvYnMpID0+IG9icyksXG4gICAgc2hhcmVSZXBsYXkoMSlcbiAgKTtcblxuICBwcml2YXRlIF9wYWdpbmdJbmZvJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8R3RQYWdpbmF0aW9uSW5mbz4oe1xuICAgIHBhZ2VDdXJyZW50OiBudWxsLFxuICAgIHBhZ2VOZXh0OiBudWxsLFxuICAgIHBhZ2VQcmV2aW91czogbnVsbCxcbiAgICBwYWdlU2l6ZTogbnVsbCxcbiAgICBudW1iZXJPZlJlY29yZHM6IG51bGwsXG4gICAgLy9yZWNvcmRzQWZ0ZXJGaWx0ZXI6IG51bGwsXG4gICAgLy9yZWNvcmRzQWZ0ZXJTZWFyY2g6IG51bGwsXG4gICAgLy9yZWNvcmRzQWxsOiBudWxsLFxuICB9KTtcblxuICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6dmFyaWFibGUtbmFtZVxuICBwcml2YXRlIF90YWJsZUNvbmZpZyQ6IEJlaGF2aW9yU3ViamVjdDxcbiAgICBUYWJsZUNvbmZpZzxhbnk+IHwgT2JzZXJ2YWJsZTxUYWJsZUNvbmZpZzxhbnk+PlxuICA+ID0gbmV3IEJlaGF2aW9yU3ViamVjdCh7fSk7XG4gIHRhYmxlQ29uZmlnJCA9IHRoaXMuX3RhYmxlQ29uZmlnJC5waXBlKFxuICAgIG1hcCgodmFsdWUpID0+IChpc09ic2VydmFibGUodmFsdWUpID8gdmFsdWUgOiBvZih2YWx1ZSkpKSxcbiAgICBzd2l0Y2hNYXAoKG9icykgPT4gb2JzKSxcbiAgICB0YXAoKGNvbmZpZykgPT4gKHRoaXMuX3RhYmxlQ29uZmlnID0gY29uZmlnKSksXG4gICAgc2hhcmVSZXBsYXkoMSlcbiAgKTtcblxuICBwcml2YXRlIF9kYXRhJDogUmVwbGF5U3ViamVjdDxBcnJheTxUYWJsZVJvdz4gfCBPYnNlcnZhYmxlPEFycmF5PFRhYmxlUm93Pj4+ID1cbiAgICBuZXcgUmVwbGF5U3ViamVjdCgxKTtcbiAgZGF0YSQ6IE9ic2VydmFibGU8QXJyYXk8VGFibGVSb3c+PiA9IHRoaXMuX2RhdGEkLnBpcGUoXG4gICAgbWFwKCh2YWx1ZSkgPT4gKGlzT2JzZXJ2YWJsZSh2YWx1ZSkgPyB2YWx1ZSA6IG9mKHZhbHVlKSkpLFxuICAgIHN3aXRjaE1hcCgob2JzKSA9PiBjb21iaW5lTGF0ZXN0KFtvYnNdKSksXG4gICAgd2l0aExhdGVzdEZyb20odGhpcy50YWJsZUNvbmZpZyQpLFxuICAgIG1hcCgoW1tkYXRhXSwgY29uZmlnXSkgPT4ge1xuICAgICAgLy8gaWYgY29sdW1ucyBvciByb3dzIGNvbnRhaW5zIGNvbmZpZyBmb3IgbWFwVG8uLi5cbiAgICAgIGlmIChcbiAgICAgICAgKGNvbmZpZy5jb2x1bW5zICYmXG4gICAgICAgICAgISFPYmplY3QudmFsdWVzKGNvbmZpZy5jb2x1bW5zKS5maW5kKChjb2x1bW4pID0+ICEhY29sdW1uLm1hcFRvKSkgfHxcbiAgICAgICAgKGNvbmZpZy5yb3dzICYmXG4gICAgICAgICAgISFPYmplY3QudmFsdWVzKGNvbmZpZy5yb3dzKS5maW5kKChjb2x1bW4pID0+ICEhY29sdW1uLm1hcFRvKSlcbiAgICAgICkge1xuICAgICAgICAvLyAuLi5tYXAgZGF0YSB0byBuZXcga2V5cyBvbiByb3cuLi5cbiAgICAgICAgY29uc3QgbmV3RGF0YTogVGFibGVSb3dbXSA9IFtdO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGRhdGEubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICBjb25zdCByb3cgPSBkYXRhW2ldO1xuICAgICAgICAgIGNvbnN0IG5ld0tleXMgPSBPYmplY3QuZW50cmllcyhjb25maWcuY29sdW1ucyB8fCBjb25maWcucm93cyB8fCBbXSlcbiAgICAgICAgICAgIC5maWx0ZXIoKFtrZXksIHZhbHVlXSkgPT4gISF2YWx1ZS5tYXBUbykgLy8gYWRkIGtleXMgZm9yIGNvbHVtbnMgd2l0aCBtYXBUbyBjb25maWcuLi5cbiAgICAgICAgICAgIC5yZWR1Y2UoXG4gICAgICAgICAgICAgIChwcmV2aW91c1ZhbHVlLCBba2V5LCB2YWx1ZV0pID0+ICh7XG4gICAgICAgICAgICAgICAgLi4ucHJldmlvdXNWYWx1ZSxcbiAgICAgICAgICAgICAgICBba2V5XTogdGhpcy5uZXN0ZWRWYWx1ZShcbiAgICAgICAgICAgICAgICAgIHJvdyxcbiAgICAgICAgICAgICAgICAgIHZhbHVlLm1hcFRvIS5wYXRoLFxuICAgICAgICAgICAgICAgICAgdmFsdWUubWFwVG8/Lm1pc3NpbmdWYWx1ZVxuICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgICB7fVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICBuZXdEYXRhW2ldID0geyAuLi5yb3csIC4uLm5ld0tleXMgfTtcbiAgICAgICAgfVxuICAgICAgICBkYXRhID0gbmV3RGF0YTtcbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLmdlbmVyYXRlUm93SWQgJiYgIXRoaXMucm93SWRLZXkgJiYgZGF0YS5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IGRhdGFXaXRoSWQgPSBbXTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBkYXRhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgZGF0YVdpdGhJZFtpXSA9IHsgLi4uZGF0YVtpXSwgX2lkOiBpIH07XG4gICAgICAgIH1cbiAgICAgICAgZGF0YSA9IGRhdGFXaXRoSWQ7XG4gICAgICB9XG4gICAgICByZXR1cm4geyBkYXRhLCBjb25maWcgfTtcbiAgICB9KSxcbiAgICBzd2l0Y2hNYXAoKG9icykgPT5cbiAgICAgIGNvbWJpbmVMYXRlc3QoW29mKG9icyksIHRoaXMuc29ydE9yZGVyJCwgdGhpcy5zZWFyY2hCeSRdKVxuICAgICksXG4gICAgbWFwKChbdGFibGUsIHNvcnRCeSwgc2VhcmNoQnldKSA9PiB7XG4gICAgICAvLyBjcmVhdGUgYSBuZXcgYXJyYXkgcmVmZXJlbmNlIGFuZCBzb3J0IG5ldyBhcnJheSAocHJldmVudCBtdXRhdGluZyBleGlzdGluZyBzdGF0ZSlcbiAgICAgIHRhYmxlLmRhdGEgPSBbLi4udGFibGUuZGF0YV07XG4gICAgICByZXR1cm4gIXNvcnRCeT8ubGVuZ3RoIHx8IHRhYmxlLmNvbmZpZz8uZGlzYWJsZVRhYmxlU29ydFxuICAgICAgICA/IHNlYXJjaEJ5ICYmICF0aGlzLnRhYmxlSW5mbz8ubGF6eUxvYWRlZFxuICAgICAgICAgID8gc2VhcmNoKHNlYXJjaEJ5LCBmYWxzZSwgdGFibGUuZGF0YSwgdGFibGUuY29uZmlnKVxuICAgICAgICAgIDogdGFibGUuZGF0YVxuICAgICAgICA6IHNlYXJjaEJ5ICYmICF0aGlzLnRhYmxlSW5mbz8ubGF6eUxvYWRlZFxuICAgICAgICA/IHNlYXJjaChzZWFyY2hCeSwgZmFsc2UsIHRhYmxlLmRhdGEsIHRhYmxlLmNvbmZpZyk/LnNvcnQoXG4gICAgICAgICAgICBzb3J0T25NdWx0aXBsZUtleXMoc29ydEJ5KVxuICAgICAgICAgIClcbiAgICAgICAgOiB0YWJsZS5kYXRhPy5zb3J0KHNvcnRPbk11bHRpcGxlS2V5cyhzb3J0QnkpKTtcbiAgICB9KSxcbiAgICBzaGFyZVJlcGxheSgxKVxuICApO1xuXG4gIGNhbGN1bGF0aW9ucyQgPSBjb21iaW5lTGF0ZXN0KFt0aGlzLmRhdGEkLCB0aGlzLnRhYmxlQ29uZmlnJF0pLnBpcGUoXG4gICAgbWFwKChbZGF0YSwgY29uZmlnXSkgPT4gY2FsY3VsYXRlKGRhdGEsIGNvbmZpZykpLFxuICAgIHNoYXJlUmVwbGF5KDEpXG4gICk7XG5cbiAgdGFibGUkID0gY29tYmluZUxhdGVzdChbXG4gICAgdGhpcy5kYXRhJCxcbiAgICB0aGlzLnRhYmxlQ29uZmlnJCxcbiAgICB0aGlzLl9wYWdpbmdJbmZvJCxcbiAgXSkucGlwZShcbiAgICBtYXAoKFtzb3J0ZWQsIGNvbmZpZywgcGFnaW5nSW5mb10pID0+IHtcbiAgICAgIGlmIChcbiAgICAgICAgcGFnaW5nSW5mby5wYWdlQ3VycmVudCAhPT0gbnVsbCAmJlxuICAgICAgICBwYWdpbmdJbmZvLm51bWJlck9mUmVjb3JkcyAhPT0gbnVsbCAmJlxuICAgICAgICBwYWdpbmdJbmZvLnBhZ2VTaXplICE9PSBudWxsXG4gICAgICApIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBkYXRhOiBbc29ydGVkXSxcbiAgICAgICAgICBjb25maWcsXG4gICAgICAgICAgaW5mbzogPFRhYmxlSW5mbz57XG4gICAgICAgICAgICBsYXp5TG9hZGVkOiB0cnVlLFxuICAgICAgICAgICAgbnVtYmVyT2ZSZWNvcmRzOiBwYWdpbmdJbmZvLm51bWJlck9mUmVjb3JkcyxcbiAgICAgICAgICAgIHBhZ2VTaXplOiBwYWdpbmdJbmZvLnBhZ2VTaXplLFxuICAgICAgICAgICAgcGFnZVRvdGFsOlxuICAgICAgICAgICAgICBwYWdpbmdJbmZvLnBhZ2VUb3RhbCA/P1xuICAgICAgICAgICAgICBNYXRoLmNlaWwocGFnaW5nSW5mby5udW1iZXJPZlJlY29yZHMgLyBwYWdpbmdJbmZvLnBhZ2VTaXplKSxcbiAgICAgICAgICB9LFxuICAgICAgICB9O1xuICAgICAgfVxuICAgICAgLy8gaWYgcGFnaW5hdGlvbiBpcyBkaXNhYmxlZC4uLlxuICAgICAgaWYgKCFjb25maWcucGFnaW5hdGlvbiB8fCBjb25maWcucGFnaW5hdGlvbi5sZW5ndGggPT09IDApIHtcbiAgICAgICAgLy8gLi4ucmV0dXJuIHVuYWx0ZXJlZCBhcnJheVxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGRhdGE6IFtzb3J0ZWRdLFxuICAgICAgICAgIGNvbmZpZyxcbiAgICAgICAgICBpbmZvOiA8VGFibGVJbmZvPnsgbnVtYmVyT2ZSZWNvcmRzOiBzb3J0ZWQubGVuZ3RoLCBwYWdlVG90YWw6IDEgfSxcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICAgIC8vIHJldHVybiByZWNvcmQgc2V0XG4gICAgICByZXR1cm4ge1xuICAgICAgICBkYXRhOiBjaHVuayhzb3J0ZWQsICsoY29uZmlnLnBhZ2luYXRpb24ubGVuZ3RoIHx8IDApKSxcbiAgICAgICAgY29uZmlnLFxuICAgICAgICBpbmZvOiA8VGFibGVJbmZvPntcbiAgICAgICAgICBudW1iZXJPZlJlY29yZHM6IHNvcnRlZC5sZW5ndGgsXG4gICAgICAgICAgcGFnZVNpemU6ICsoY29uZmlnLnBhZ2luYXRpb24ubGVuZ3RoIHx8IDApLFxuICAgICAgICAgIHBhZ2VUb3RhbDogTWF0aC5jZWlsKFxuICAgICAgICAgICAgc29ydGVkLmxlbmd0aCAvICsoY29uZmlnLnBhZ2luYXRpb24ubGVuZ3RoIHx8IDApXG4gICAgICAgICAgKSxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSksXG4gICAgdGFwKChtZXRhKSA9PiB0aGlzLl90YWJsZUluZm8kLm5leHQobWV0YS5pbmZvKSksXG4gICAgc2hhcmVSZXBsYXkoMSlcbiAgKTtcblxuICAvKiogdGFibGVJbmZvJCAtIHJldHVybnMgb2JzZXJ2YWJsZSBmb3IgdGFibGUgaW5mb1xuICAgKiBAcmV0dXJuIE9ic2VydmFibGU8VGFibGVJbmZvPiAqL1xuICBnZXQgdGFibGVJbmZvJCgpOiBPYnNlcnZhYmxlPFRhYmxlSW5mbyB8IHVuZGVmaW5lZD4ge1xuICAgIHJldHVybiB0aGlzLl90YWJsZUluZm8kLmFzT2JzZXJ2YWJsZSgpLnBpcGUoXG4gICAgICBmaWx0ZXIoKGluZm8pID0+ICEhaW5mbyksXG4gICAgICBzaGFyZVJlcGxheSgxKVxuICAgICk7XG4gIH1cblxuICAvKiogdGFibGVJbmZvIC0gcmV0dXJucyB0aGUgY3VycmVudCB0YWJsZSBpbmZvXG4gICAqIEByZXR1cm4gVGFibGVJbmZvICovXG4gIGdldCB0YWJsZUluZm8oKTogVGFibGVJbmZvIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5fdGFibGVJbmZvJC5nZXRWYWx1ZSgpO1xuICB9XG5cbiAgcHJpdmF0ZSBfdGFibGVJbmZvJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8VGFibGVJbmZvIHwgdW5kZWZpbmVkPih1bmRlZmluZWQpO1xuXG4gIHByaXZhdGUgX2N1cnJlbnRQYWdpbmF0aW9uSW5kZXgkID0gbmV3IEJlaGF2aW9yU3ViamVjdCgwKTtcbiAgY3VycmVudFBhZ2luYXRpb25JbmRleCQgPSBjb21iaW5lTGF0ZXN0KFtcbiAgICB0aGlzLl9jdXJyZW50UGFnaW5hdGlvbkluZGV4JCxcbiAgICB0aGlzLnRhYmxlJCxcbiAgXSkucGlwZShcbiAgICBtYXAoKFtwYWdlLCB0YWJsZV0pID0+IHtcbiAgICAgIC8vIGRldGVybWluZSBsYXN0IHBhZ2VcbiAgICAgIGNvbnN0IGxhc3RQYWdlID1cbiAgICAgICAgTWF0aC5jZWlsKFxuICAgICAgICAgIHRhYmxlLmluZm8ubnVtYmVyT2ZSZWNvcmRzIC9cbiAgICAgICAgICAgICh0YWJsZS5pbmZvLnBhZ2VTaXplID8/XG4gICAgICAgICAgICAgICh0YWJsZS5jb25maWc/LnBhZ2luYXRpb24/Lmxlbmd0aCB8fCB0YWJsZS5pbmZvLm51bWJlck9mUmVjb3JkcykpXG4gICAgICAgICkgLSAxO1xuICAgICAgLy8gZGV0ZXJtaW5lIG1pbi9tYXggcG9zaXRpb25cbiAgICAgIHJldHVybiArcGFnZSA8IDAgPyAwIDogK3BhZ2UgPiBsYXN0UGFnZSA/IGxhc3RQYWdlIDogK3BhZ2U7XG4gICAgfSksXG4gICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKSxcbiAgICB0YXAoKGluZGV4KSA9PiB0aGlzLnBhZ2VDaGFuZ2UuZW1pdCh7IGluZGV4IH0pKSxcbiAgICBzaGFyZVJlcGxheSgxKVxuICApO1xuXG4gIGNvbHNwYW4kID0gdGhpcy50YWJsZUNvbmZpZyQucGlwZShcbiAgICBzd2l0Y2hNYXAoKGNvbmZpZykgPT5cbiAgICAgIGNvbmZpZy5jb2x1bW5zXG4gICAgICAgID8gb2YoXG4gICAgICAgICAgICBPYmplY3QudmFsdWVzKGNvbmZpZy5jb2x1bW5zIHx8IGNvbmZpZy5yb3dzIHx8IHt9KS5maWx0ZXIoXG4gICAgICAgICAgICAgICh2YWx1ZSkgPT4gdmFsdWUuaGlkZGVuICE9PSB0cnVlXG4gICAgICAgICAgICApLmxlbmd0aFxuICAgICAgICAgIClcbiAgICAgICAgOiB0aGlzLmRhdGEkLnBpcGUobWFwKChkYXRhKSA9PiBkYXRhLmxlbmd0aCArIDEpKVxuICAgICksXG4gICAgc2hhcmVSZXBsYXkoMSlcbiAgKTtcblxuICBmb290ZXJDb2xzcGFuJCA9IHRoaXMudGFibGVDb25maWckLnBpcGUoXG4gICAgbWFwKChjb25maWcpID0+IHtcbiAgICAgIGxldCBjb2xzcGFuID0gMDtcbiAgICAgIE9iamVjdC52YWx1ZXMoY29uZmlnPy5mb290ZXI/LmNvbHVtbnMgfHwge30pLmZvckVhY2goKGNhbGN1bGF0aW9ucykgPT4ge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgT2JqZWN0LnZhbHVlcyhjYWxjdWxhdGlvbnMpLmZpbHRlcigodmFsdWUpID0+IHZhbHVlICE9PSBmYWxzZSlcbiAgICAgICAgICAgIC5sZW5ndGggPj0gMFxuICAgICAgICApIHtcbiAgICAgICAgICBjb2xzcGFuICs9IDE7XG4gICAgICAgIH1cbiAgICAgIH0sIHt9KTtcbiAgICAgIHJldHVybiBjb2xzcGFuO1xuICAgIH0pLFxuICAgIHNoYXJlUmVwbGF5KDEpXG4gICk7XG5cbiAgLyoqIHNvcnRCeUtleSAtIFNvcnQgYnkga2V5IGluIHRhYmxlIHJvd1xuICAgKiBAcGFyYW0ga2V5IC0ga2V5IHRvIHNvcnQgYnlcbiAgICogQHBhcmFtIHsgTW91c2VFdmVudCB9IFskZXZlbnRdIC0gTW91c2UgZXZlbnQgdHJpZ2dlcmluZyBzb3J0LCBpZiBzaGlmdCBrZXkgaXMgcHJlc3NlZCBzb3J0IGtleSB3aWxsIGJlIGFkZGVkIHRvIGFscmVhZHkgcHJlc2VudCBzb3J0IGtleXNcbiAgICovXG4gIHNvcnRCeUtleShrZXk6IGtleW9mIFRhYmxlUm93LCAkZXZlbnQ/OiBNb3VzZUV2ZW50KTogdm9pZCB7XG4gICAgY29uc3Qgc2hpZnRLZXkgPSAkZXZlbnQ/LnNoaWZ0S2V5ID09PSB0cnVlO1xuICAgIGNvbnN0IGN1cnJlbnRPcmRlciA9IHRoaXMuX3NvcnRPcmRlciQudmFsdWU7XG4gICAgbGV0IHNvcnRPcmRlcjogR3RPcmRlciA9ICdhc2MnO1xuICAgIGxldCBuZXdPcmRlcjogR3RTb3J0T3JkZXIgPSBbXTtcbiAgICAvLyBpZiBzaGlmdCBrZXkgaXMgcHJlc3NlZCB3aGlsZSBzb3J0aW5nLi4uXG4gICAgaWYgKHNoaWZ0S2V5KSB7XG4gICAgICAvLyAuLi5jaGVjayBpZiBrZXkgaXMgYWxyZWFkeSBzb3J0ZWRcbiAgICAgIGNvbnN0IGV4aXN0aW5nU29ydFBvc2l0aW9uID0gY3VycmVudE9yZGVyLmZpbmRJbmRleChcbiAgICAgICAgKHZhbHVlKSA9PiB2YWx1ZS5rZXkgPT09IGtleVxuICAgICAgKTtcbiAgICAgIGlmIChleGlzdGluZ1NvcnRQb3NpdGlvbiA9PT0gLTEpIHtcbiAgICAgICAgLy8gLi4uaWYga2V5IGlzIG5vdCBzb3J0ZWQsIGFkZCBpdCB0byB0aGUgZW5kIG9mIHRoZSBzb3J0IG9yZGVyXG4gICAgICAgIG5ld09yZGVyID0gWy4uLmN1cnJlbnRPcmRlciwgeyBrZXksIG9yZGVyOiAnYXNjJyB9XTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIC4uLmlmIGtleSBpcyBhbHJlYWR5IHNvcnRlZCwgdG9nZ2xlIHNvcnQgb3JkZXJcbiAgICAgICAgc29ydE9yZGVyID0gY3VycmVudE9yZGVyW2V4aXN0aW5nU29ydFBvc2l0aW9uXS5vcmRlcjtcbiAgICAgICAgY29uc3QgbmV3U29ydE9yZGVyID0gc29ydE9yZGVyID09PSAnYXNjJyA/ICdkZXNjJyA6ICdhc2MnO1xuICAgICAgICBuZXdPcmRlciA9IFsuLi5jdXJyZW50T3JkZXJdO1xuICAgICAgICBuZXdPcmRlcltleGlzdGluZ1NvcnRQb3NpdGlvbl0gPSB7XG4gICAgICAgICAgLi4ubmV3T3JkZXJbZXhpc3RpbmdTb3J0UG9zaXRpb25dLFxuICAgICAgICAgIG9yZGVyOiBuZXdTb3J0T3JkZXIsXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIC4uLmVsc2UgaWYgc2hpZnQga2V5IGlzIG5vdCBwcmVzc2VkLi4uXG4gICAgICBpZiAoY3VycmVudE9yZGVyLmxlbmd0aCA+IDApIHtcbiAgICAgICAgLy8gLi4uY2hlY2sgaWYga2V5IGlzIGFscmVhZHkgc29ydGVkXG4gICAgICAgIGNvbnN0IGV4aXN0aW5nU29ydFBvc2l0aW9uID0gY3VycmVudE9yZGVyLmZpbmRJbmRleChcbiAgICAgICAgICAodmFsdWUpID0+IHZhbHVlLmtleSA9PT0ga2V5XG4gICAgICAgICk7XG4gICAgICAgIC8vIC4uLmlmIGtleSBpcyBhbHJlYWR5IHNvcnRlZCwgdG9nZ2xlIHNvcnQgb3JkZXJcbiAgICAgICAgaWYgKGV4aXN0aW5nU29ydFBvc2l0aW9uID09PSAtMSkge1xuICAgICAgICAgIG5ld09yZGVyID0gW3sga2V5LCBvcmRlcjogJ2FzYycgfV07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc29ydE9yZGVyID0gY3VycmVudE9yZGVyW2V4aXN0aW5nU29ydFBvc2l0aW9uXS5vcmRlcjtcbiAgICAgICAgICBjb25zdCBuZXdTb3J0T3JkZXIgPSBzb3J0T3JkZXIgPT09ICdhc2MnID8gJ2Rlc2MnIDogJ2FzYyc7XG4gICAgICAgICAgbmV3T3JkZXIgPSBbeyBrZXksIG9yZGVyOiBuZXdTb3J0T3JkZXIgfV07XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIC4uLmlmIGtleSBpcyBub3Qgc29ydGVkIHNldCBzb3J0IG9yZGVyIGZvciBrZXkgdG8gYXNjZW5kaW5nXG4gICAgICAgIG5ld09yZGVyID0gW3sga2V5LCBvcmRlcjogc29ydE9yZGVyIH1dO1xuICAgICAgfVxuICAgIH1cbiAgICAvLyBjcmVhdGUgc29ydCBldmVudFxuICAgIGNvbnN0IHNvcnRFdmVudDogR3RTb3J0RXZlbnQgPSB7XG4gICAgICBrZXksXG4gICAgICBvcmRlcjogc29ydE9yZGVyLFxuICAgICAgY3VycmVudFNvcnRPcmRlcjogbmV3T3JkZXIsXG4gICAgICBhZGRTb3J0S2V5OiBzaGlmdEtleSxcbiAgICB9O1xuXG4gICAgLy8gaWYgZXZlbnQgaXMgcGFzc2VkIHRvIHNvcnQgZnVuY3Rpb24uLi5cbiAgICBpZiAoJGV2ZW50KSB7XG4gICAgICAvLyAuLi5lbWl0IGl0IGFzIHdlbGxcbiAgICAgIHNvcnRFdmVudC5ldmVudCA9ICRldmVudDtcbiAgICB9XG4gICAgLy8gZW1pdCBzb3J0IGV2ZW50XG4gICAgdGhpcy5jb2x1bW5Tb3J0LmVtaXQoc29ydEV2ZW50KTtcblxuICAgIC8vIGlmIHRhYmxlIGlzIG5vdCBsYXp5IGxvYWRlZCAoc29ydGluZyBpcyB0aGVuIGhhbmRsZWQgc2VydmVyLXNpZGUpLi4uXG4gICAgaWYgKCF0aGlzLnRhYmxlSW5mbz8ubGF6eUxvYWRlZCkge1xuICAgICAgLy8gLi4udXBkYXRlIHNvcnQgb3JkZXJcbiAgICAgIHRoaXMuc29ydE9yZGVyID0gbmV3T3JkZXI7XG4gICAgfVxuICB9XG5cbiAgY29sdW1uT3JkZXIgPSAoXG4gICAgYTogS2V5VmFsdWU8c3RyaW5nLCBUYWJsZUNvbHVtbj4sXG4gICAgYjogS2V5VmFsdWU8c3RyaW5nLCBUYWJsZUNvbHVtbj5cbiAgKTogbnVtYmVyID0+IHtcbiAgICByZXR1cm4gKGEudmFsdWUub3JkZXIgfHwgMCkgLSAoYi52YWx1ZS5vcmRlciB8fCAwKTtcbiAgfTtcblxuICBuZXN0ZWRWYWx1ZShcbiAgICBvYmplY3Q6IGFueSxcbiAgICBtYXBUbzogc3RyaW5nLFxuICAgIG1pc3NpbmdWYWx1ZTogc3RyaW5nIHwgbnVtYmVyIHwgbnVsbCA9IG51bGxcbiAgKTogdW5rbm93biB7XG4gICAgY29uc3QgbGV2ZWxzID0gbWFwVG8uc3BsaXQoJy4nKTtcbiAgICByZXR1cm4gbGV2ZWxzLnJlZHVjZShcbiAgICAgIChwcmV2aW91c1ZhbHVlLCBjdXJyZW50VmFsdWUsIGluZGV4KSA9PlxuICAgICAgICBwcmV2aW91c1ZhbHVlW2N1cnJlbnRWYWx1ZV0gfHxcbiAgICAgICAgKGluZGV4ID09PSBsZXZlbHMubGVuZ3RoIC0gMSA/IG1pc3NpbmdWYWx1ZSA6IHt9KSxcbiAgICAgIG9iamVjdFxuICAgICk7XG4gIH1cblxuICBwcml2YXRlIF91bnN1YnNjcmliZUZyb21LZXlib2FyZEV2ZW50cyQgPSBuZXcgU3ViamVjdCgpO1xuICBwcml2YXRlIF9rZXlib2FyZEFycm93RXZlbnQkID0gZnJvbUV2ZW50PEtleWJvYXJkRXZlbnQ+KFxuICAgIGRvY3VtZW50LFxuICAgICdrZXlkb3duJ1xuICApLnBpcGUoXG4gICAgZmlsdGVyKFxuICAgICAgKGV2ZW50KSA9PlxuICAgICAgICBbLi4udGhpcy5fbmF2aWdhdGlvbktleXMsIC4uLnRoaXMuX3NlbGVjdEtleXNdLmluZGV4T2YoZXZlbnQua2V5KSA+IC0xXG4gICAgKVxuICApO1xuXG4gIHByb3RlY3RlZCBsaXN0ZW5Ub0tleWJvYXJkRXZlbnRzKCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5fdGFibGVDb25maWc/LmFjdGl2YXRlUm93T25LZXlib2FyZE5hdmlnYXRpb24pIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLl91bnN1YnNjcmliZUZyb21LZXlib2FyZEV2ZW50cyQubmV4dCh0cnVlKTtcbiAgICB0aGlzLl9rZXlib2FyZEFycm93RXZlbnQkXG4gICAgICAucGlwZShcbiAgICAgICAgd2l0aExhdGVzdEZyb20oXG4gICAgICAgICAgdGhpcy5kYXRhJCxcbiAgICAgICAgICB0aGlzLmN1cnJlbnRQYWdpbmF0aW9uSW5kZXgkLFxuICAgICAgICAgIHRoaXMudGFibGVJbmZvJFxuICAgICAgICApLFxuICAgICAgICB0YWtlVW50aWwodGhpcy5fdW5zdWJzY3JpYmVGcm9tS2V5Ym9hcmRFdmVudHMkKSxcbiAgICAgICAgdGFrZVVudGlsKHRoaXMudW5zdWJzY3JpYmUkKVxuICAgICAgKVxuICAgICAgLnN1YnNjcmliZSgoW2V2ZW50LCByb3dzLCBjdXJyZW50UGFnZSwgdGFibGVJbmZvXSkgPT4ge1xuICAgICAgICBjb25zdCBzZWxlY3RFdmVudCA9IHRoaXMuX3NlbGVjdEtleXMuaW5jbHVkZXMoZXZlbnQua2V5KTtcbiAgICAgICAgaWYgKHNlbGVjdEV2ZW50ICYmIHRoaXMuYWN0aXZlUm93SW5kZXggIT09IG51bGwpIHtcbiAgICAgICAgICBjb25zdCByb3dJbmRleCA9XG4gICAgICAgICAgICB0aGlzLmFjdGl2ZVJvd0luZGV4ICsgY3VycmVudFBhZ2UgKiAodGFibGVJbmZvPy5wYWdlU2l6ZSA/PyAwKTtcbiAgICAgICAgICB0aGlzLl9yb3dBY3RpdmUocm93c1tyb3dJbmRleF0sIHJvd0luZGV4LCBldmVudCk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgbmF2aWdhdGlvbkV2ZW50ID0gdGhpcy5fbmF2aWdhdGlvbktleXMuaW5jbHVkZXMoZXZlbnQua2V5KTtcbiAgICAgICAgaWYgKG5hdmlnYXRpb25FdmVudCkge1xuICAgICAgICAgIHRoaXMuX2hhbmRsZU5hdmlnYXRpb25FdmVudChldmVudCwgcm93cywgY3VycmVudFBhZ2UsIHRhYmxlSW5mbyk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICB9XG4gIHVuc3Vic2NyaWJlRnJvbUtleWJvYXJkRXZlbnRzKHRhYmxlUmVmOiBIVE1MVGFibGVFbGVtZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLl90YWJsZUNvbmZpZz8uYWN0aXZhdGVSb3dPbktleWJvYXJkTmF2aWdhdGlvbikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBvbmx5IHVuc3Vic2NyaWJlIGlmIHRhYmxlIGlzIG5vdCBmb2N1c2VkXG4gICAgaWYgKHRhYmxlUmVmICE9PSBkb2N1bWVudC5hY3RpdmVFbGVtZW50KSB7XG4gICAgICBpZiAodGhpcy5fdGFibGVDb25maWc/LmFjdGl2YXRlUm93T25Ib3Zlcikge1xuICAgICAgICAvLyB1bnNldCBhY3RpdmUgcm93XG4gICAgICAgIHRoaXMuYWN0aXZhdGVSb3cobnVsbCk7XG4gICAgICB9XG4gICAgICB0aGlzLl91bnN1YnNjcmliZUZyb21LZXlib2FyZEV2ZW50cyQubmV4dCh0cnVlKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIF9oYW5kbGVOYXZpZ2F0aW9uRXZlbnQoXG4gICAgZXZlbnQ6IEtleWJvYXJkRXZlbnQsXG4gICAgcm93czogYW55W10sXG4gICAgY3VycmVudFBhZ2U6IG51bWJlcixcbiAgICB0YWJsZUluZm86IGFueVxuICApOiB2b2lkIHtcbiAgICBjb25zdCBoYXNQYWdpbmF0aW9uID0gKHRhYmxlSW5mbz8ucGFnZVRvdGFsIHx8IDApID4gMSAmJiB0YWJsZUluZm87XG4gICAgY29uc3QgbGFzdFJvd0luZGV4ID0gcm93cy5sZW5ndGggLSAxO1xuICAgIGxldCBuZXdJbmRleCA9IHRoaXMuYWN0aXZlUm93SW5kZXg7XG4gICAgbGV0IGluZGV4TW9kaWZpZXIgPSAwO1xuXG4gICAgaWYgKGV2ZW50LmtleSA9PT0gJ0hvbWUnKSB7XG4gICAgICB0aGlzLnBhZ2luYXRpb25JbmRleCA9IDA7XG4gICAgICB0aGlzLmFjdGl2YXRlUm93KDAsIGV2ZW50KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoZXZlbnQua2V5ID09PSAnRW5kJykge1xuICAgICAgY29uc3QgaW5kZXhPZkxhc3RSZWNvcmQgPSBoYXNQYWdpbmF0aW9uXG4gICAgICAgID8gcm93cy5sZW5ndGggLSAodGFibGVJbmZvLnBhZ2VUb3RhbCAtIDEpICogdGFibGVJbmZvLnBhZ2VTaXplIC0gMVxuICAgICAgICA6IGxhc3RSb3dJbmRleDtcbiAgICAgIGlmICh0YWJsZUluZm8/LnBhZ2VUb3RhbCkge1xuICAgICAgICB0aGlzLnBhZ2luYXRpb25JbmRleCA9IHRhYmxlSW5mby5wYWdlVG90YWwgLSAxO1xuICAgICAgfVxuICAgICAgdGhpcy5hY3RpdmF0ZVJvdyhpbmRleE9mTGFzdFJlY29yZCwgZXZlbnQpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChldmVudC5rZXkgPT09ICdBcnJvd0Rvd24nKSB7XG4gICAgICBpbmRleE1vZGlmaWVyID0gMTtcbiAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ0Fycm93VXAnKSB7XG4gICAgICBpbmRleE1vZGlmaWVyID0gLTE7XG4gICAgfVxuXG4gICAgaWYgKG5ld0luZGV4ID09PSBudWxsKSB7XG4gICAgICBuZXdJbmRleCA9IDA7XG4gICAgfSBlbHNlIGlmIChcbiAgICAgIG5ld0luZGV4ICsgaW5kZXhNb2RpZmllciA+PSAwICYmXG4gICAgICBuZXdJbmRleCArIGluZGV4TW9kaWZpZXIgPD0gbGFzdFJvd0luZGV4XG4gICAgKSB7XG4gICAgICBuZXdJbmRleCA9IG5ld0luZGV4ICsgaW5kZXhNb2RpZmllcjtcbiAgICB9XG5cbiAgICBpZiAoaGFzUGFnaW5hdGlvbiAmJiB0YWJsZUluZm8/LnBhZ2VTaXplKSB7XG4gICAgICBjb25zdCBpc05vdExhc3RQYWdlID0gY3VycmVudFBhZ2UgKyAxIDwgdGFibGVJbmZvLnBhZ2VUb3RhbDtcbiAgICAgIGNvbnN0IHJlY29yZHNPbkxhc3RQYWdlID1cbiAgICAgICAgcm93cy5sZW5ndGggLSAodGFibGVJbmZvLnBhZ2VUb3RhbCAtIDEpICogdGFibGVJbmZvLnBhZ2VTaXplIC0gMTtcbiAgICAgIGNvbnN0IG1heEluZGV4ID0gaXNOb3RMYXN0UGFnZVxuICAgICAgICA/IHRhYmxlSW5mbz8ucGFnZVNpemUgLSAxXG4gICAgICAgIDogcmVjb3Jkc09uTGFzdFBhZ2U7XG5cbiAgICAgIGlmIChldmVudC5rZXkgPT09ICdBcnJvd0xlZnQnICYmIGN1cnJlbnRQYWdlID4gMCkge1xuICAgICAgICB0aGlzLnBhZ2luYXRpb25JbmRleCA9IGN1cnJlbnRQYWdlIC0gMTtcbiAgICAgICAgdGhpcy5hY3RpdmF0ZVJvdyhuZXdJbmRleCwgZXZlbnQpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ0Fycm93UmlnaHQnICYmIGlzTm90TGFzdFBhZ2UpIHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgIGN1cnJlbnRQYWdlICsgMSA9PT0gdGFibGVJbmZvLnBhZ2VUb3RhbCAtIDEgJiZcbiAgICAgICAgICBuZXdJbmRleCA+IHJlY29yZHNPbkxhc3RQYWdlXG4gICAgICAgICkge1xuICAgICAgICAgIHRoaXMuYWN0aXZhdGVSb3cocmVjb3Jkc09uTGFzdFBhZ2UsIGV2ZW50KTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnBhZ2luYXRpb25JbmRleCA9IGN1cnJlbnRQYWdlICsgMTtcbiAgICAgICAgdGhpcy5hY3RpdmF0ZVJvdyhuZXdJbmRleCwgZXZlbnQpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGlmIChcbiAgICAgICAgY3VycmVudFBhZ2UgPiAwICYmXG4gICAgICAgIGluZGV4TW9kaWZpZXIgPCAwICYmXG4gICAgICAgIG5ld0luZGV4ICsgaW5kZXhNb2RpZmllciA8PSBsYXN0Um93SW5kZXggJiZcbiAgICAgICAgKHRoaXMuYWN0aXZlUm93SW5kZXggfHwgMCkgKyBpbmRleE1vZGlmaWVyIDwgMFxuICAgICAgKSB7XG4gICAgICAgIC8vIHNldCBsYXN0IHJvdyBvZiBwcmV2aW91cyBwYWdlIGFzIGFjdGl2ZVxuICAgICAgICB0aGlzLmFjdGl2YXRlUm93KHRhYmxlSW5mbz8ucGFnZVNpemUgLSAxLCBldmVudCk7XG4gICAgICAgIHRoaXMucGFnaW5hdGlvbkluZGV4ID0gY3VycmVudFBhZ2UgLSAxO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHBhZ2VJbmRleCA9IG5ld0luZGV4ICUgdGFibGVJbmZvPy5wYWdlU2l6ZTtcblxuICAgICAgaWYgKG5ld0luZGV4ID4gbWF4SW5kZXggJiYgY3VycmVudFBhZ2UgKyAxIDwgdGFibGVJbmZvLnBhZ2VUb3RhbCkge1xuICAgICAgICB0aGlzLnBhZ2luYXRpb25JbmRleCA9IGN1cnJlbnRQYWdlICsgMTtcbiAgICAgIH1cbiAgICAgIHRoaXMuYWN0aXZhdGVSb3cocGFnZUluZGV4ID4gbWF4SW5kZXggPyBtYXhJbmRleCA6IHBhZ2VJbmRleCwgZXZlbnQpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuYWN0aXZhdGVSb3cobmV3SW5kZXgsIGV2ZW50KTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCkge1xuICAgIHRoaXMudW5zdWJzY3JpYmUkLm5leHQodHJ1ZSk7XG4gICAgdGhpcy51bnN1YnNjcmliZSQuY29tcGxldGUoKTtcbiAgfVxufVxuIiwiPHRhYmxlXG4gIFtuZ0NsYXNzXT1cIih0YWJsZUNvbmZpZyQgfCBhc3luYyk/LmNsYXNzIHx8ICd0YWJsZSdcIlxuICBbY2xhc3MudGFibGUtbW9iaWxlXT1cIih0YWJsZUNvbmZpZyQgfCBhc3luYyk/Lm1vYmlsZUxheW91dFwiXG4gIFtjbGFzcy50YWJsZS1ob3Jpem9udGFsXT1cIih0YWJsZUNvbmZpZyQgfCBhc3luYyk/LnJvd3NcIlxuICBbY2xhc3MudGFibGUtbG9hZGluZ109XCJsb2FkaW5nJCB8IGFzeW5jXCJcbiAgW2NsYXNzLmd0LXN0aWNreS1yb3ctaGVhZGVyXT1cIlxuICAgICh0YWJsZUNvbmZpZyQgfCBhc3luYyk/LnN0aWNreUhlYWRlcnM/LnJvdyAmJiAodGFibGVDb25maWckIHwgYXN5bmMpPy5yb3dzXG4gIFwiXG4gIFtjbGFzcy5ndC1zdGlja3ktY29sdW1uLWhlYWRlcl09XCJcbiAgICAodGFibGVDb25maWckIHwgYXN5bmMpPy5zdGlja3lIZWFkZXJzPy5jb2x1bW5cbiAgXCJcbiAgW2F0dHIuYXJpYS1idXN5XT1cIihsb2FkaW5nJCB8IGFzeW5jKSA9PT0gdHJ1ZSA/IHRydWUgOiBudWxsXCJcbiAgW3RhYmluZGV4XT1cIih0YWJsZUNvbmZpZyQgfCBhc3luYyk/LmFjdGl2YXRlUm93T25LZXlib2FyZE5hdmlnYXRpb24gPyAwIDogLTFcIlxuICAjdGFibGVSZWZcbiAgKGZvY3VzKT1cImxpc3RlblRvS2V5Ym9hcmRFdmVudHMoKVwiXG4gIChmb2N1c291dCk9XCJ1bnN1YnNjcmliZUZyb21LZXlib2FyZEV2ZW50cyh0YWJsZVJlZilcIlxuICAobW91c2VlbnRlcik9XCJsaXN0ZW5Ub0tleWJvYXJkRXZlbnRzKClcIlxuICAobW91c2VsZWF2ZSk9XCJ1bnN1YnNjcmliZUZyb21LZXlib2FyZEV2ZW50cyh0YWJsZVJlZilcIlxuPlxuICA8dGhlYWQ+XG4gICAgPHRyXG4gICAgICAqbmdJZj1cIntcbiAgICAgICAgY29uZmlnOiAodGFibGVDb25maWckIHwgYXN5bmMpISxcbiAgICAgICAgaXNMb2FkaW5nOiBsb2FkaW5nJCB8IGFzeW5jXG4gICAgICB9IGFzIHRhYmxlXCJcbiAgICA+XG4gICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICpuZ0Zvcj1cImxldCBjb2x1bW4gb2YgdGFibGUuY29uZmlnPy5jb2x1bW5zIHwga2V5dmFsdWU6IGNvbHVtbk9yZGVyXCJcbiAgICAgID5cbiAgICAgICAgPHRoXG4gICAgICAgICAgKm5nSWY9XCIhY29sdW1uLnZhbHVlPy5oaWRkZW5cIlxuICAgICAgICAgIG5nQ2xhc3M9XCJ7eyAoY29sdW1uLmtleSB8IGRhc2hDYXNlKSArICctY29sdW1uJyB9fSB7e1xuICAgICAgICAgICAgY29sdW1uLnZhbHVlLmNsYXNzXG4gICAgICAgICAgfX1cIlxuICAgICAgICAgIFtjbGFzcy5kaXNhYmxlZF09XCJ0YWJsZS5pc0xvYWRpbmdcIlxuICAgICAgICAgIFthdHRyLmFyaWEtc29ydF09XCJzb3J0T3JkZXIkIHwgYXN5bmMgfCBzb3J0Q2xhc3M6IGNvbHVtbi5rZXk6J2FyaWEnXCJcbiAgICAgICAgICBbY2xhc3MuZ3Qtc29ydGFibGVdPVwidHJ1ZVwiXG4gICAgICAgICAgc2NvcGU9XCJjb2xcIlxuICAgICAgICA+XG4gICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgKm5nSWY9XCJjb2x1bW4udmFsdWU/LnNvcnRhYmxlXCJcbiAgICAgICAgICAgIFthdHRyLmRhdGEtc29ydC1vcmRlcl09XCJcbiAgICAgICAgICAgICAgc29ydE9yZGVyJCB8IGFzeW5jIHwgc29ydENsYXNzOiBjb2x1bW4ua2V5OidvcmRlcidcbiAgICAgICAgICAgIFwiXG4gICAgICAgICAgICBjbGFzcz1cImd0LXNvcnRcIlxuICAgICAgICAgICAgKGNsaWNrKT1cIlxuICAgICAgICAgICAgICB0YWJsZS5pc0xvYWRpbmcgfHxcbiAgICAgICAgICAgICAgICAhY29sdW1uLnZhbHVlLnNvcnRhYmxlIHx8XG4gICAgICAgICAgICAgICAgc29ydEJ5S2V5KGNvbHVtbi5rZXksICRldmVudClcbiAgICAgICAgICAgIFwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAgPHNwYW4gKm5nSWY9XCJjb2x1bW4udmFsdWU/LmhlYWRlciAhPT0gZmFsc2VcIj57e1xuICAgICAgICAgICAgICBjb2x1bW4udmFsdWUuaGVhZGVyIHx8IGNvbHVtbi5rZXkgfCBjYXBpdGFsQ2FzZVxuICAgICAgICAgICAgfX08L3NwYW4+XG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgPHNwYW5cbiAgICAgICAgICAgICpuZ0lmPVwiIWNvbHVtbi52YWx1ZT8uc29ydGFibGUgJiYgY29sdW1uLnZhbHVlPy5oZWFkZXIgIT09IGZhbHNlXCJcbiAgICAgICAgICAgID57eyBjb2x1bW4udmFsdWUuaGVhZGVyIHx8IGNvbHVtbi5rZXkgfCBjYXBpdGFsQ2FzZSB9fTwvc3BhblxuICAgICAgICAgID5cbiAgICAgICAgPC90aD5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAqbmdJZj1cIlxuICAgICAgICAgICgodGFibGU/LmNvbmZpZz8ucm93cyB8IGtleXZhbHVlOiBjb2x1bW5PcmRlcikgfHwgW10pWzBdIGFzIGhlYWRlclJvd1xuICAgICAgICBcIlxuICAgICAgPlxuICAgICAgICA8dGhcbiAgICAgICAgICBjbGFzcz1cInJvdy1oZWFkZXJcIlxuICAgICAgICAgIFthdHRyLmFyaWEtc29ydF09XCJcbiAgICAgICAgICAgIHNvcnRPcmRlciQgfCBhc3luYyB8IHNvcnRDbGFzczogaGVhZGVyUm93LmtleTonYXJpYSdcbiAgICAgICAgICBcIlxuICAgICAgICAgIG5nQ2xhc3M9XCJ7eyBoZWFkZXJSb3cudmFsdWUuc29ydGFibGUgPyAnc29ydCAnIDogJycgfX0ge3tcbiAgICAgICAgICAgIHNvcnRPcmRlciQgfCBhc3luYyB8IHNvcnRDbGFzczogaGVhZGVyUm93LmtleVxuICAgICAgICAgIH19IHt7IChoZWFkZXJSb3cua2V5IHwgZGFzaENhc2UpICsgJy1jb2x1bW4nIH19XCJcbiAgICAgICAgICAoY2xpY2spPVwiXG4gICAgICAgICAgICB0YWJsZS5pc0xvYWRpbmcgfHxcbiAgICAgICAgICAgICAgIWhlYWRlclJvdy52YWx1ZS5zb3J0YWJsZSB8fFxuICAgICAgICAgICAgICBzb3J0QnlLZXkoaGVhZGVyUm93LmtleSwgJGV2ZW50KVxuICAgICAgICAgIFwiXG4gICAgICAgICAgc2NvcGU9XCJjb2xcIlxuICAgICAgICA+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImhlYWRlclJvdz8udmFsdWU/LmhlYWRlciAhPT0gZmFsc2VcIj57e1xuICAgICAgICAgICAgaGVhZGVyUm93Py52YWx1ZT8uaGVhZGVyIHx8IGhlYWRlclJvdy5rZXkgfCBjYXBpdGFsQ2FzZVxuICAgICAgICAgIH19PC9uZy1jb250YWluZXI+XG4gICAgICAgIDwvdGg+XG4gICAgICAgIDx0aFxuICAgICAgICAgICpuZ0Zvcj1cImxldCBjb2x1bW4gb2YgKCh0YWJsZSQgfCBhc3luYyk/LmRhdGEgfHwgW10pWzBdXCJcbiAgICAgICAgICBuZ0NsYXNzPVwie3sgaGVhZGVyUm93LnZhbHVlLmNsYXNzIH19XCJcbiAgICAgICAgPlxuICAgICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICAgIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIlxuICAgICAgICAgICAgICAodGFibGUuY29uZmlnLnJvd3MgfHwge30pW2hlYWRlclJvdy5rZXldLnRlbXBsYXRlUmVmXG4gICAgICAgICAgICAgICAgPyB0ZW1wbGF0ZVJlZlxuICAgICAgICAgICAgICAgIDogKHRhYmxlLmNvbmZpZy5yb3dzIHx8IHt9KVtoZWFkZXJSb3cua2V5XS50cmFuc2Zvcm1cbiAgICAgICAgICAgICAgICA/IHRyYW5zZm9ybURhdGFcbiAgICAgICAgICAgICAgICA6IHJhd0RhdGFcbiAgICAgICAgICAgIFwiXG4gICAgICAgICAgICBbbmdUZW1wbGF0ZU91dGxldENvbnRleHRdPVwie1xuICAgICAgICAgICAgICByb3c6IGNvbHVtbixcbiAgICAgICAgICAgICAgY29sdW1uOiBoZWFkZXJSb3csXG4gICAgICAgICAgICAgIHRyYW5zZm9ybTogKHRhYmxlLmNvbmZpZy5yb3dzIHx8IHt9KVtoZWFkZXJSb3cua2V5XS50cmFuc2Zvcm0sXG4gICAgICAgICAgICAgIHRlbXBsYXRlUmVmOiAodGFibGUuY29uZmlnLnJvd3MgfHwge30pW2hlYWRlclJvdy5rZXldLnRlbXBsYXRlUmVmLFxuICAgICAgICAgICAgICBpbmRleDogMFxuICAgICAgICAgICAgfVwiXG4gICAgICAgICAgPlxuICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICA8L3RoPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC90cj5cbiAgPC90aGVhZD5cbiAgPHRib2R5ICpuZ0lmPVwibG9hZGluZyQgfCBhc3luYzsgZWxzZSB0YWJsZUNvbnRlbnRcIj5cbiAgICA8dHI+XG4gICAgICA8dGQgY2xhc3M9XCJwLTBcIiBbY29sU3Bhbl09XCJjb2xzcGFuJCB8IGFzeW5jXCI+XG4gICAgICAgIDxuZy1jb250ZW50IHNlbGVjdD1cIi50YWJsZS1sb2FkaW5nXCI+PC9uZy1jb250ZW50PlxuICAgICAgPC90ZD5cbiAgICA8L3RyPlxuICA8L3Rib2R5PlxuICA8dGZvb3QgKm5nSWY9XCIodGFibGUkIHwgYXN5bmMpISBhcyB0YWJsZVwiPlxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJ0YWJsZS5kYXRhLmxlbmd0aCA+IDAgJiYgIShsb2FkaW5nJCB8IGFzeW5jKVwiPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIihjYWxjdWxhdGlvbnMkIHwgYXN5bmMpISBhcyBjYWxjdWxhdGlvbnNcIj5cbiAgICAgICAgPHRyXG4gICAgICAgICAgKm5nRm9yPVwibGV0IGNhbGN1bGF0aW9uIG9mIGNhbGN1bGF0aW9ucy5jYWxjdWxhdGlvbnM7IGxldCBpID0gaW5kZXhcIlxuICAgICAgICA+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgKm5nSWY9XCJ7XG4gICAgICAgICAgICAgIHNob3dIZWFkZXI6IChjb2xzcGFuJCB8IGFzeW5jKSAhPT0gKGZvb3RlckNvbHNwYW4kIHwgYXN5bmMpXG4gICAgICAgICAgICB9IGFzIGZvb3RlclJvd1wiXG4gICAgICAgICAgPlxuICAgICAgICAgICAgPHRoXG4gICAgICAgICAgICAgICpuZ0lmPVwiZm9vdGVyUm93LnNob3dIZWFkZXJcIlxuICAgICAgICAgICAgICBbY29sU3Bhbl09XCJcbiAgICAgICAgICAgICAgICAoKGNvbHNwYW4kIHwgYXN5bmMpIHx8IDApIC0gKChmb290ZXJDb2xzcGFuJCB8IGFzeW5jKSB8fCAwKVxuICAgICAgICAgICAgICBcIlxuICAgICAgICAgICAgICBzY29wZT1cInJvd1wiXG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICAgICAgICAqbmdJZj1cInRhYmxlLmNvbmZpZz8uZm9vdGVyPy5oZWFkZXJzPy5bY2FsY3VsYXRpb25dIGFzIHNob3dIZWFkZXJcIlxuICAgICAgICAgICAgICAgID57e3Nob3dIZWFkZXIgPT09IHRydWUgPyAoY2FsY3VsYXRpb24gfCBjYXBpdGFsQ2FzZSk6IHRhYmxlLmNvbmZpZy5mb290ZXI/LmhlYWRlcnM/LltjYWxjdWxhdGlvbl19fVxuICAgICAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICAgIDwvdGg+XG4gICAgICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICAgICpuZ0Zvcj1cIlxuICAgICAgICAgICAgICAgIGxldCBjb2x1bW4gb2YgdGFibGUuY29uZmlnPy5jb2x1bW5zIHwga2V5dmFsdWU6IGNvbHVtbk9yZGVyXG4gICAgICAgICAgICAgIFwiXG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgIDx0ZFxuICAgICAgICAgICAgICAgICpuZ0lmPVwiXG4gICAgICAgICAgICAgICAgICAhY29sdW1uLnZhbHVlPy5oaWRkZW4gJiYgY2FsY3VsYXRpb25zLmNhbGN1bGF0ZWRbY29sdW1uLmtleV1cbiAgICAgICAgICAgICAgICBcIlxuICAgICAgICAgICAgICAgIG5nQ2xhc3M9XCJ7eyAoY29sdW1uLmtleSB8IGRhc2hDYXNlKSArICctY29sdW1uJyB9fSB7e1xuICAgICAgICAgICAgICAgICAgY29sdW1uLnZhbHVlLmNsYXNzXG4gICAgICAgICAgICAgICAgfX1cIlxuICAgICAgICAgICAgICAgIFthdHRyLmRhdGEtaGVhZGVyXT1cIlxuICAgICAgICAgICAgICAgICAgIWZvb3RlclJvdy5zaG93SGVhZGVyICYmIHRhYmxlLmNvbmZpZy5mb290ZXI/LmhlYWRlcnM/LltjYWxjdWxhdGlvbl1cbiAgICAgICAgICAgICAgICAgICAgPyB0YWJsZS5jb25maWcuZm9vdGVyPy5oZWFkZXJzPy5bY2FsY3VsYXRpb25dID09PSB0cnVlID8gKGNhbGN1bGF0aW9uIHwgY2FwaXRhbENhc2UpIDogdGFibGUuY29uZmlnLmZvb3Rlcj8uaGVhZGVycz8uW2NhbGN1bGF0aW9uXVxuICAgICAgICAgICAgICAgICAgICA6IG51bGxcbiAgICAgICAgICAgICAgICBcIlxuICAgICAgICAgICAgICAgIFthdHRyLmRhdGEtbGFiZWxdPVwiXG4gICAgICAgICAgICAgICAgICB0YWJsZS5jb25maWcubW9iaWxlTGF5b3V0ICYmIGNvbHVtbi52YWx1ZS5tb2JpbGVIZWFkZXJcbiAgICAgICAgICAgICAgICAgICAgPyBjb2x1bW4udmFsdWUubW9iaWxlSGVhZGVyICE9PSB0cnVlXG4gICAgICAgICAgICAgICAgICAgICAgPyBjb2x1bW4udmFsdWUubW9iaWxlSGVhZGVyXG4gICAgICAgICAgICAgICAgICAgICAgOiAoY29sdW1uLnZhbHVlLmhlYWRlciB8fCBjb2x1bW4ua2V5IHwgY2FwaXRhbENhc2UpXG4gICAgICAgICAgICAgICAgICAgIDogbnVsbFxuICAgICAgICAgICAgICAgIFwiXG4gICAgICAgICAgICAgICAgW2NsYXNzLmd0LW5vLWNvbnRlbnRdPVwiXG4gICAgICAgICAgICAgICAgICAhY2FsY3VsYXRpb25zLmNhbGN1bGF0ZWRbY29sdW1uLmtleV1bY2FsY3VsYXRpb25dXG4gICAgICAgICAgICAgICAgXCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICAgICAgICAgIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIlxuICAgICAgICAgICAgICAgICAgICAodGFibGUuY29uZmlnLmNvbHVtbnMgfHwge30pW2NvbHVtbi5rZXldLnRlbXBsYXRlUmVmXG4gICAgICAgICAgICAgICAgICAgICAgPyB0ZW1wbGF0ZVJlZlxuICAgICAgICAgICAgICAgICAgICAgIDogKHRhYmxlLmNvbmZpZy5jb2x1bW5zIHx8IHt9KVtjb2x1bW4ua2V5XS50cmFuc2Zvcm1cbiAgICAgICAgICAgICAgICAgICAgICA/IHRyYW5zZm9ybUZvb3RlclxuICAgICAgICAgICAgICAgICAgICAgIDogcmF3Rm9vdGVyXG4gICAgICAgICAgICAgICAgICBcIlxuICAgICAgICAgICAgICAgICAgW25nVGVtcGxhdGVPdXRsZXRDb250ZXh0XT1cIntcbiAgICAgICAgICAgICAgICAgICAgdmFsdWU6IGNhbGN1bGF0aW9ucy5jYWxjdWxhdGVkW2NvbHVtbi5rZXldW2NhbGN1bGF0aW9uXSxcbiAgICAgICAgICAgICAgICAgICAgcm93OiBjYWxjdWxhdGlvbnMuY2FsY3VsYXRlZFtjb2x1bW4ua2V5XSxcbiAgICAgICAgICAgICAgICAgICAgY29sdW1uOiBjYWxjdWxhdGlvbixcbiAgICAgICAgICAgICAgICAgICAgdGVtcGxhdGVSZWY6ICh0YWJsZS5jb25maWcuY29sdW1ucyB8fCB7fSlbY29sdW1uLmtleV1cbiAgICAgICAgICAgICAgICAgICAgICAudGVtcGxhdGVSZWYsXG4gICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogKHRhYmxlLmNvbmZpZy5jb2x1bW5zIHx8IHt9KVtjb2x1bW4ua2V5XVxuICAgICAgICAgICAgICAgICAgICAgIC50cmFuc2Zvcm1cbiAgICAgICAgICAgICAgICAgIH1cIlxuICAgICAgICAgICAgICAgID48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICAgICAgPC90ZD5cbiAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICA8L3RyPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC9uZy1jb250YWluZXI+XG4gIDwvdGZvb3Q+XG48L3RhYmxlPlxuPG5nLXRlbXBsYXRlICN0YWJsZUNvbnRlbnQ+XG4gIDxuZy1jb250YWluZXIgKm5nSWY9XCIodGFibGUkIHwgYXN5bmMpISBhcyB0YWJsZVwiPlxuICAgIDx0Ym9keSAqbmdJZj1cIih0YWJsZSEuZGF0YSFbMF0gfHwgdGFibGUhLmRhdGEhKS5sZW5ndGggPiAwOyBlbHNlIG5vRGF0YVwiPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cInRhYmxlLmNvbmZpZy5jb2x1bW5zXCI+XG4gICAgICAgIDx0clxuICAgICAgICAgICpuZ0Zvcj1cIlxuICAgICAgICAgICAgbGV0IHJvdyBvZiB0YWJsZSEuZGF0YSFbXG4gICAgICAgICAgICAgIHRhYmxlLmluZm8ubGF6eUxvYWRlZCA/IDAgOiAoY3VycmVudFBhZ2luYXRpb25JbmRleCQgfCBhc3luYykgfHwgMFxuICAgICAgICAgICAgXTtcbiAgICAgICAgICAgIHRyYWNrQnk6IHRyYWNrUm93QnlGbjtcbiAgICAgICAgICAgIGxldCBpID0gaW5kZXhcbiAgICAgICAgICBcIlxuICAgICAgICAgIFthdHRyLmlkXT1cIid0YWJsZVJvd18nICsgaVwiXG4gICAgICAgICAgKGNsaWNrKT1cInRhYmxlPy5jb25maWc/LnJvd0NsaWNrICYmIF9yb3dDbGljayhyb3csIGksICRldmVudClcIlxuICAgICAgICAgIChtb3VzZWVudGVyKT1cIlxuICAgICAgICAgICAgdGFibGU/LmNvbmZpZz8uYWN0aXZhdGVSb3dPbkhvdmVyICYmIF9hY3RpdmF0ZVJvdyhyb3csIGksICRldmVudClcbiAgICAgICAgICBcIlxuICAgICAgICAgIChtb3VzZWxlYXZlKT1cIlxuICAgICAgICAgICAgdGFibGU/LmNvbmZpZz8uYWN0aXZhdGVSb3dPbkhvdmVyICYmXG4gICAgICAgICAgICAgIF9hY3RpdmF0ZVJvdyhudWxsLCBudWxsLCAkZXZlbnQpXG4gICAgICAgICAgXCJcbiAgICAgICAgICBbbmdDbGFzc109XCJbXG4gICAgICAgICAgICAhIWlzUm93U2VsZWN0ZWRGblxuICAgICAgICAgICAgICA/IChyb3dcbiAgICAgICAgICAgICAgICB8IHJvd1NlbGVjdGlvblxuICAgICAgICAgICAgICAgICAgOiBzZWxlY3Rpb25cbiAgICAgICAgICAgICAgICAgIDogaXNSb3dTZWxlY3RlZEZuXG4gICAgICAgICAgICAgICAgICA6IGN1c3RvbUNsYXNzZXMuc2VsZWN0ZWRSb3cpXG4gICAgICAgICAgICAgIDogJycsXG4gICAgICAgICAgICAocm93QWN0aXZlJCB8IGFzeW5jKT8uaW5kZXggPT09IGkgPyBjdXN0b21DbGFzc2VzLmFjdGl2ZVJvdyA6ICcnXG4gICAgICAgICAgXVwiXG4gICAgICAgID5cbiAgICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICAqbmdGb3I9XCJsZXQgY29sdW1uIG9mIHRhYmxlLmNvbmZpZz8uY29sdW1ucyB8IGtleXZhbHVlOiBjb2x1bW5PcmRlclwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAgPHRkXG4gICAgICAgICAgICAgICpuZ0lmPVwiIWNvbHVtbi52YWx1ZT8uaGlkZGVuXCJcbiAgICAgICAgICAgICAgbmdDbGFzcz1cInt7IChjb2x1bW4ua2V5IHwgZGFzaENhc2UpICsgJy1jb2x1bW4nIH19IHt7XG4gICAgICAgICAgICAgICAgY29sdW1uLnZhbHVlLmNsYXNzXG4gICAgICAgICAgICAgIH19XCJcbiAgICAgICAgICAgICAgW2F0dHIuZGF0YS1sYWJlbF09XCJcbiAgICAgICAgICAgICAgICB0YWJsZS5jb25maWcubW9iaWxlTGF5b3V0ICYmIGNvbHVtbi52YWx1ZS5tb2JpbGVIZWFkZXJcbiAgICAgICAgICAgICAgICAgID8gY29sdW1uLnZhbHVlLm1vYmlsZUhlYWRlciAhPT0gdHJ1ZVxuICAgICAgICAgICAgICAgICAgICA/IGNvbHVtbi52YWx1ZS5tb2JpbGVIZWFkZXJcbiAgICAgICAgICAgICAgICAgICAgOiAoY29sdW1uLnZhbHVlLmhlYWRlciB8fCBjb2x1bW4ua2V5IHwgY2FwaXRhbENhc2UpXG4gICAgICAgICAgICAgICAgICA6IG51bGxcbiAgICAgICAgICAgICAgXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgICAgIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIlxuICAgICAgICAgICAgICAgICAgKHNlYXJjaEJ5JCB8IGFzeW5jKSAmJlxuICAgICAgICAgICAgICAgICAgISh0YWJsZS5jb25maWcuY29sdW1ucyB8fCB7fSlbY29sdW1uLmtleV0udGVtcGxhdGVSZWZcbiAgICAgICAgICAgICAgICAgICAgPyBoaWdobGlnaHRlZFxuICAgICAgICAgICAgICAgICAgICA6ICh0YWJsZS5jb25maWcuY29sdW1ucyB8fCB7fSlbY29sdW1uLmtleV0udGVtcGxhdGVSZWZcbiAgICAgICAgICAgICAgICAgICAgPyB0ZW1wbGF0ZVJlZlxuICAgICAgICAgICAgICAgICAgICA6ICh0YWJsZS5jb25maWcuY29sdW1ucyB8fCB7fSlbY29sdW1uLmtleV0udHJhbnNmb3JtXG4gICAgICAgICAgICAgICAgICAgID8gdHJhbnNmb3JtRGF0YVxuICAgICAgICAgICAgICAgICAgICA6IHJhd0RhdGFcbiAgICAgICAgICAgICAgICBcIlxuICAgICAgICAgICAgICAgIFtuZ1RlbXBsYXRlT3V0bGV0Q29udGV4dF09XCJ7XG4gICAgICAgICAgICAgICAgICByb3c6IHJvdyxcbiAgICAgICAgICAgICAgICAgIGNvbHVtbjogY29sdW1uLFxuICAgICAgICAgICAgICAgICAgc2VhcmNoOiAoc2VhcmNoQnkkIHwgYXN5bmMpLFxuICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtOiAodGFibGUuY29uZmlnLmNvbHVtbnMgfHwge30pW2NvbHVtbi5rZXldLnRyYW5zZm9ybSxcbiAgICAgICAgICAgICAgICAgIHRlbXBsYXRlUmVmOiAodGFibGUuY29uZmlnLmNvbHVtbnMgfHwge30pW2NvbHVtbi5rZXldXG4gICAgICAgICAgICAgICAgICAgIC50ZW1wbGF0ZVJlZixcbiAgICAgICAgICAgICAgICAgIGluZGV4OiBpLFxuICAgICAgICAgICAgICAgICAgZGF0YTogdGFibGUuZGF0YVtcbiAgICAgICAgICAgICAgICAgICAgdGFibGUuaW5mby5sYXp5TG9hZGVkXG4gICAgICAgICAgICAgICAgICAgICAgPyAwXG4gICAgICAgICAgICAgICAgICAgICAgOiAoY3VycmVudFBhZ2luYXRpb25JbmRleCQgfCBhc3luYykgfHwgMFxuICAgICAgICAgICAgICAgICAgXVxuICAgICAgICAgICAgICAgIH1cIlxuICAgICAgICAgICAgICA+PC9uZy1jb250YWluZXI+XG4gICAgICAgICAgICA8L3RkPlxuICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICA8L3RyPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwidGFibGUuY29uZmlnLnJvd3NcIj5cbiAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICpuZ0Zvcj1cIlxuICAgICAgICAgICAgbGV0IHJvdyBvZiB0YWJsZT8uY29uZmlnPy5yb3dzIHwga2V5dmFsdWU6IGNvbHVtbk9yZGVyIHwgc2xpY2U6IDE7XG4gICAgICAgICAgICBsZXQgaSA9IGluZGV4XG4gICAgICAgICAgXCJcbiAgICAgICAgPlxuICAgICAgICAgIDx0clxuICAgICAgICAgICAgKm5nSWY9XCIhcm93LnZhbHVlPy5oaWRkZW5cIlxuICAgICAgICAgICAgW2F0dHIuaWRdPVwiJ3RhYmxlUm93XycgKyBpXCJcbiAgICAgICAgICAgIG5nQ2xhc3M9XCJ7eyAocm93LmtleSB8IGRhc2hDYXNlKSArICctcm93JyB9fVwiXG4gICAgICAgICAgICAoY2xpY2spPVwidGFibGU/LmNvbmZpZz8ucm93Q2xpY2sgJiYgX3Jvd0NsaWNrKHJvdywgaSwgJGV2ZW50KVwiXG4gICAgICAgICAgICAobW91c2VlbnRlcik9XCJcbiAgICAgICAgICAgICAgdGFibGU/LmNvbmZpZz8uYWN0aXZhdGVSb3dPbkhvdmVyICYmIF9hY3RpdmF0ZVJvdyhyb3csIGksICRldmVudClcbiAgICAgICAgICAgIFwiXG4gICAgICAgICAgICAobW91c2VsZWF2ZSk9XCJcbiAgICAgICAgICAgICAgdGFibGU/LmNvbmZpZz8uYWN0aXZhdGVSb3dPbkhvdmVyICYmXG4gICAgICAgICAgICAgICAgX2FjdGl2YXRlUm93KG51bGwsIG51bGwsICRldmVudClcbiAgICAgICAgICAgIFwiXG4gICAgICAgICAgICBbbmdDbGFzc109XCJbXG4gICAgICAgICAgICAgICEhaXNSb3dTZWxlY3RlZEZuXG4gICAgICAgICAgICAgICAgPyAocm93XG4gICAgICAgICAgICAgICAgICB8IHJvd1NlbGVjdGlvblxuICAgICAgICAgICAgICAgICAgICA6IHNlbGVjdGlvblxuICAgICAgICAgICAgICAgICAgICA6IGlzUm93U2VsZWN0ZWRGblxuICAgICAgICAgICAgICAgICAgICA6IGN1c3RvbUNsYXNzZXMuc2VsZWN0ZWRSb3cpXG4gICAgICAgICAgICAgICAgOiAnJyxcbiAgICAgICAgICAgICAgKHJvd0FjdGl2ZSQgfCBhc3luYyk/LmluZGV4ID09PSBpID8gY3VzdG9tQ2xhc3Nlcy5hY3RpdmVSb3cgOiAnJ1xuICAgICAgICAgICAgXVwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAgPHRoIGNsYXNzPVwicm93LWhlYWRlclwiIHNjb3BlPVwicm93XCI+XG4gICAgICAgICAgICAgIHt7IHJvdy52YWx1ZS5oZWFkZXIgfHwgcm93LmtleSB8IGNhcGl0YWxDYXNlIH19XG4gICAgICAgICAgICA8L3RoPlxuICAgICAgICAgICAgPHRkXG4gICAgICAgICAgICAgICpuZ0Zvcj1cImxldCBjb2x1bW4gb2YgKHRhYmxlPy5kYXRhIHx8IFtdKVswXTsgbGV0IHkgPSBpbmRleFwiXG4gICAgICAgICAgICAgIG5nQ2xhc3M9XCJ7eyByb3cudmFsdWUuY2xhc3MgfX1cIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICAgICAgW25nVGVtcGxhdGVPdXRsZXRdPVwiXG4gICAgICAgICAgICAgICAgICAodGFibGUuY29uZmlnLnJvd3MgfHwge30pW3Jvdy5rZXldLnRlbXBsYXRlUmVmXG4gICAgICAgICAgICAgICAgICAgID8gdGVtcGxhdGVSZWZcbiAgICAgICAgICAgICAgICAgICAgOiAodGFibGUuY29uZmlnLnJvd3MgfHwge30pW3Jvdy5rZXldLnRyYW5zZm9ybVxuICAgICAgICAgICAgICAgICAgICA/IHRyYW5zZm9ybURhdGFcbiAgICAgICAgICAgICAgICAgICAgOiByYXdEYXRhXG4gICAgICAgICAgICAgICAgXCJcbiAgICAgICAgICAgICAgICBbbmdUZW1wbGF0ZU91dGxldENvbnRleHRdPVwie1xuICAgICAgICAgICAgICAgICAgcm93OiBjb2x1bW4sXG4gICAgICAgICAgICAgICAgICBjb2x1bW46IHJvdyxcbiAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogKHRhYmxlLmNvbmZpZy5yb3dzIHx8IHt9KVtyb3cua2V5XS50cmFuc2Zvcm0sXG4gICAgICAgICAgICAgICAgICB0ZW1wbGF0ZVJlZjogKHRhYmxlLmNvbmZpZy5yb3dzIHx8IHt9KVtyb3cua2V5XS50ZW1wbGF0ZVJlZixcbiAgICAgICAgICAgICAgICAgIGluZGV4OiB0YWJsZS5jb25maWcucm93cyA/IHkgOiBpLFxuICAgICAgICAgICAgICAgICAgZGF0YTogdGFibGUuZGF0YVtcbiAgICAgICAgICAgICAgICAgICAgdGFibGUuaW5mby5sYXp5TG9hZGVkXG4gICAgICAgICAgICAgICAgICAgICAgPyAwXG4gICAgICAgICAgICAgICAgICAgICAgOiAoY3VycmVudFBhZ2luYXRpb25JbmRleCQgfCBhc3luYykgfHwgMFxuICAgICAgICAgICAgICAgICAgXVxuICAgICAgICAgICAgICAgIH1cIlxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICAgICAgPC90ZD5cbiAgICAgICAgICA8L3RyPlxuICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvdGJvZHk+XG4gIDwvbmctY29udGFpbmVyPlxuPC9uZy10ZW1wbGF0ZT5cbjxuZy10ZW1wbGF0ZSAjbm9EYXRhPlxuICA8dGJvZHk+XG4gICAgPHRyPlxuICAgICAgPHRkIGNsYXNzPVwicC0wXCIgW2NvbFNwYW5dPVwiY29sc3BhbiQgfCBhc3luY1wiPlxuICAgICAgICA8bmctY29udGVudCBzZWxlY3Q9XCIudGFibGUtbm8tZGF0YVwiPjwvbmctY29udGVudD5cbiAgICAgIDwvdGQ+XG4gICAgPC90cj5cbiAgPC90Ym9keT5cbjwvbmctdGVtcGxhdGU+XG48bmctdGVtcGxhdGVcbiAgI2hpZ2hsaWdodGVkXG4gIGxldC1yb3c9XCJyb3dcIlxuICBsZXQtY29sdW1uPVwiY29sdW1uXCJcbiAgbGV0LXNlYXJjaD1cInNlYXJjaFwiXG4gIGxldC10cmFuc2Zvcm09XCJ0cmFuc2Zvcm1cIlxuPlxuICA8ZGl2XG4gICAgKm5nSWY9XCIhdHJhbnNmb3JtXCJcbiAgICBbaW5uZXJIVE1MXT1cInJvd1tjb2x1bW4ua2V5XSB8IGhpZ2hsaWdodDogc2VhcmNoXCJcbiAgPjwvZGl2PlxuICA8ZGl2XG4gICAgKm5nSWY9XCJ0cmFuc2Zvcm1cIlxuICAgIFtpbm5lckhUTUxdPVwiXG4gICAgICByb3dbY29sdW1uLmtleV1cbiAgICAgICAgfCBkeW5hbWljUGlwZTogdHJhbnNmb3JtLnBpcGU6dHJhbnNmb3JtPy5hcmdzXG4gICAgICAgIHwgaGlnaGxpZ2h0OiBzZWFyY2hcbiAgICBcIlxuICA+PC9kaXY+XG48L25nLXRlbXBsYXRlPlxuPG5nLXRlbXBsYXRlICNyYXdEYXRhIGxldC1yb3c9XCJyb3dcIiBsZXQtY29sdW1uPVwiY29sdW1uXCI+XG4gIHt7IHJvd1tjb2x1bW4ua2V5XSB9fVxuPC9uZy10ZW1wbGF0ZT5cbjxuZy10ZW1wbGF0ZVxuICAjdHJhbnNmb3JtRGF0YVxuICBsZXQtcm93PVwicm93XCJcbiAgbGV0LWNvbHVtbj1cImNvbHVtblwiXG4gIGxldC10cmFuc2Zvcm09XCJ0cmFuc2Zvcm1cIlxuICBsZXQtZGF0YT1cImRhdGFcIlxuPlxuICB7eyByb3dbY29sdW1uLmtleV0gfCBkeW5hbWljUGlwZTogdHJhbnNmb3JtLnBpcGU6dHJhbnNmb3JtPy5hcmdzIH19XG48L25nLXRlbXBsYXRlPlxuPG5nLXRlbXBsYXRlICN0cmFuc2Zvcm1Gb290ZXIgbGV0LXZhbHVlPVwidmFsdWVcIiBsZXQtdHJhbnNmb3JtPVwidHJhbnNmb3JtXCI+XG4gIHt7XG4gICAgKHZhbHVlIHwgZHluYW1pY1BpcGU6IHRyYW5zZm9ybS5waXBlOnRyYW5zZm9ybT8uYXJncykgfHxcbiAgICAgICh0YWJsZUNvbmZpZyQgfCBhc3luYyk/LmZvb3Rlcj8uZW1wdHlDb250ZW50XG4gIH19XG48L25nLXRlbXBsYXRlPlxuPG5nLXRlbXBsYXRlICNyYXdGb290ZXIgbGV0LXZhbHVlPVwidmFsdWVcIj5cbiAge3sgdmFsdWUgfHwgKHRhYmxlQ29uZmlnJCB8IGFzeW5jKT8uZm9vdGVyPy5lbXB0eUNvbnRlbnQgfX1cbjwvbmctdGVtcGxhdGU+XG48bmctdGVtcGxhdGVcbiAgI3RlbXBsYXRlUmVmXG4gIGxldC1yb3c9XCJyb3dcIlxuICBsZXQtY29sdW1uPVwiY29sdW1uXCJcbiAgbGV0LWluZGV4PVwiaW5kZXhcIlxuICBsZXQtdGVtcGxhdGVSZWY9XCJ0ZW1wbGF0ZVJlZlwiXG4gIGxldC1kYXRhPVwiZGF0YVwiXG4+XG4gIDxuZy1jb250YWluZXJcbiAgICBbbmdUZW1wbGF0ZU91dGxldF09XCJ0ZW1wbGF0ZVJlZlwiXG4gICAgW25nVGVtcGxhdGVPdXRsZXRDb250ZXh0XT1cIntcbiAgICAgIHJvdzogcm93LFxuICAgICAgY29sOiBjb2x1bW4sXG4gICAgICBpbmRleDogaW5kZXgsXG4gICAgICBkYXRhOiBkYXRhXG4gICAgfVwiXG4gID48L25nLWNvbnRhaW5lcj5cbjwvbmctdGVtcGxhdGU+XG4iXX0=
|