@cqa-lib/cqa-ui 1.1.525 → 1.1.526
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/lib/assets/images/image-assets.constants.mjs +3 -1
- package/esm2020/lib/compare-runs/compare-runs.component.mjs +1 -1
- package/esm2020/lib/execution-screen/db-query-execution-item/db-query-execution-item.component.mjs +1 -1
- package/esm2020/lib/execution-screen/db-verification-step/db-verification-step.component.mjs +1 -1
- package/esm2020/lib/iterations-loop/iterations-loop.component.mjs +1 -1
- package/esm2020/lib/segment-control/segment-control.component.mjs +6 -3
- package/esm2020/lib/simulator/simulator.component.mjs +1 -1
- package/esm2020/lib/step-builder/step-builder-document-generation-template-step/step-builder-document-generation-template-step.component.mjs +1 -1
- package/esm2020/lib/table/dynamic-table/dynamic-table.component.mjs +148 -4
- package/esm2020/lib/templates/modular-table-template/dialogs/delete-folder-dialog.component.mjs +181 -0
- package/esm2020/lib/templates/modular-table-template/dialogs/move-to-folder-dialog.component.mjs +264 -0
- package/esm2020/lib/templates/modular-table-template/dialogs/new-folder-dialog.component.mjs +352 -0
- package/esm2020/lib/templates/modular-table-template/directives/folder-drag.directive.mjs +45 -0
- package/esm2020/lib/templates/modular-table-template/directives/folder-drop.directive.mjs +95 -0
- package/esm2020/lib/templates/modular-table-template/directives/row-drag.directive.mjs +44 -0
- package/esm2020/lib/templates/modular-table-template/folder-sidebar/folder-sidebar.component.mjs +479 -0
- package/esm2020/lib/templates/modular-table-template/modular-table-template.component.mjs +1475 -0
- package/esm2020/lib/templates/modular-table-template/modular-table-template.models.mjs +79 -0
- package/esm2020/lib/templates/table-template.component.mjs +88 -12
- package/esm2020/lib/test-case-details/api-edit-step/api-edit-step.component.mjs +1 -1
- package/esm2020/lib/ui-kit.module.mjs +41 -1
- package/esm2020/public-api.mjs +10 -1
- package/fesm2015/cqa-lib-cqa-ui.mjs +3408 -178
- package/fesm2015/cqa-lib-cqa-ui.mjs.map +1 -1
- package/fesm2020/cqa-lib-cqa-ui.mjs +3388 -176
- package/fesm2020/cqa-lib-cqa-ui.mjs.map +1 -1
- package/lib/assets/images/image-assets.constants.d.ts +1 -0
- package/lib/segment-control/segment-control.component.d.ts +2 -1
- package/lib/table/dynamic-table/dynamic-table.component.d.ts +43 -1
- package/lib/templates/modular-table-template/dialogs/delete-folder-dialog.component.d.ts +34 -0
- package/lib/templates/modular-table-template/dialogs/move-to-folder-dialog.component.d.ts +57 -0
- package/lib/templates/modular-table-template/dialogs/new-folder-dialog.component.d.ts +79 -0
- package/lib/templates/modular-table-template/directives/folder-drag.directive.d.ts +10 -0
- package/lib/templates/modular-table-template/directives/folder-drop.directive.d.ts +22 -0
- package/lib/templates/modular-table-template/directives/row-drag.directive.d.ts +10 -0
- package/lib/templates/modular-table-template/folder-sidebar/folder-sidebar.component.d.ts +149 -0
- package/lib/templates/modular-table-template/modular-table-template.component.d.ts +453 -0
- package/lib/templates/modular-table-template/modular-table-template.models.d.ts +150 -0
- package/lib/templates/table-template.component.d.ts +40 -2
- package/lib/ui-kit.module.d.ts +153 -145
- package/package.json +1 -1
- package/public-api.d.ts +9 -0
- package/src/lib/assets/images/EmptyFolderState.png +0 -0
- package/src/lib/assets/images/image-assets.constants.ts +3 -0
- package/styles.css +1 -1
package/esm2020/lib/templates/modular-table-template/folder-sidebar/folder-sidebar.component.mjs
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, EventEmitter, HostListener, Input, Output, } from '@angular/core';
|
|
2
|
+
import { DEFAULT_MODULAR_LABELS, } from '../modular-table-template.models';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "@angular/material/icon";
|
|
5
|
+
import * as i2 from "../../../search-bar/search-bar.component";
|
|
6
|
+
import * as i3 from "@angular/common";
|
|
7
|
+
import * as i4 from "../directives/folder-drop.directive";
|
|
8
|
+
import * as i5 from "../directives/folder-drag.directive";
|
|
9
|
+
import * as i6 from "@angular/forms";
|
|
10
|
+
export class FolderSidebarComponent {
|
|
11
|
+
constructor(cdr) {
|
|
12
|
+
this.cdr = cdr;
|
|
13
|
+
this.folders = [];
|
|
14
|
+
this.selectedFolderId = null;
|
|
15
|
+
this.expandedFolderIds = [];
|
|
16
|
+
this.unorganisedCount = 0;
|
|
17
|
+
this.allowCreate = true;
|
|
18
|
+
this.allowRename = true;
|
|
19
|
+
this.allowDelete = true;
|
|
20
|
+
this.allowMove = true;
|
|
21
|
+
this.allowDuplicate = true;
|
|
22
|
+
this.allowDrop = true;
|
|
23
|
+
this.showCounts = true;
|
|
24
|
+
this.collapsed = false;
|
|
25
|
+
this.labels = { ...DEFAULT_MODULAR_LABELS };
|
|
26
|
+
this.folderSelected = new EventEmitter();
|
|
27
|
+
this.folderExpansionToggled = new EventEmitter();
|
|
28
|
+
/** Emitted after the host completes folder creation (e.g., inline rename flow). Reserved for future use. */
|
|
29
|
+
this.folderCreated = new EventEmitter();
|
|
30
|
+
/** Emitted when the user clicks the "+" header button. Host should open a creation modal. */
|
|
31
|
+
this.folderCreateRequested = new EventEmitter();
|
|
32
|
+
/** Carries the full renamed folder node + parent id so hosts can issue complete update requests. */
|
|
33
|
+
this.folderRenamed = new EventEmitter();
|
|
34
|
+
this.folderDeleted = new EventEmitter();
|
|
35
|
+
/** User picked "Move folder" from the context menu. Host opens the folder picker. */
|
|
36
|
+
this.folderMoveRequested = new EventEmitter();
|
|
37
|
+
/** User picked "Duplicate folder" from the context menu. Host clones the subtree (tests referenced, not copied). */
|
|
38
|
+
this.folderDuplicateRequested = new EventEmitter();
|
|
39
|
+
this.testsDropped = new EventEmitter();
|
|
40
|
+
/** Fires when a folder row is dropped onto another folder (or the Unorganised row). */
|
|
41
|
+
this.folderDropped = new EventEmitter();
|
|
42
|
+
this.collapsedChange = new EventEmitter();
|
|
43
|
+
this.searchValue = '';
|
|
44
|
+
this.renamingId = null;
|
|
45
|
+
this.renameDraft = '';
|
|
46
|
+
/** Id of the folder whose context menu is open, or null when closed. */
|
|
47
|
+
this.contextMenuFolderId = null;
|
|
48
|
+
/** Viewport-anchored position (client coordinates) for the floating menu. */
|
|
49
|
+
this.contextMenuPosition = { x: 0, y: 0 };
|
|
50
|
+
/** Pending auto-expand timers while a folder drag is hovered over collapsed rows. */
|
|
51
|
+
this.dragExpandTimers = new Map();
|
|
52
|
+
this.DRAG_EXPAND_DELAY_MS = 600;
|
|
53
|
+
/** Id of the folder currently being dragged; used to skip cycle targets and source-hover expansion. */
|
|
54
|
+
this.activeFolderDragId = null;
|
|
55
|
+
this.trackByRow = (_, row) => row.node.id;
|
|
56
|
+
}
|
|
57
|
+
get expandedSet() {
|
|
58
|
+
return new Set(this.expandedFolderIds || []);
|
|
59
|
+
}
|
|
60
|
+
/** Flattened, depth-aware list of rows to render in order. Ancestors of matches are kept. */
|
|
61
|
+
get rows() {
|
|
62
|
+
const search = (this.searchValue || '').trim().toLowerCase();
|
|
63
|
+
const expanded = this.expandedSet;
|
|
64
|
+
const keepSet = new Set();
|
|
65
|
+
if (search) {
|
|
66
|
+
const mark = (nodes, ancestors) => {
|
|
67
|
+
for (const n of nodes || []) {
|
|
68
|
+
const matched = (n.name || '').toLowerCase().includes(search);
|
|
69
|
+
const childAncestors = [...ancestors, n.id];
|
|
70
|
+
if (matched) {
|
|
71
|
+
for (const a of ancestors)
|
|
72
|
+
keepSet.add(a);
|
|
73
|
+
keepSet.add(n.id);
|
|
74
|
+
}
|
|
75
|
+
if (n.children?.length)
|
|
76
|
+
mark(n.children, childAncestors);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
mark(this.folders, []);
|
|
80
|
+
}
|
|
81
|
+
const out = [];
|
|
82
|
+
const walk = (nodes, depth, parentVisible) => {
|
|
83
|
+
for (const n of nodes || []) {
|
|
84
|
+
const searchVisible = !search || keepSet.has(n.id);
|
|
85
|
+
const visible = parentVisible && searchVisible;
|
|
86
|
+
const hasChildren = !!(n.children && n.children.length);
|
|
87
|
+
out.push({ node: n, depth, visible, hasChildren });
|
|
88
|
+
const expandedBySearch = !!search && keepSet.has(n.id);
|
|
89
|
+
const isNodeExpanded = expanded.has(n.id) || expandedBySearch;
|
|
90
|
+
if (hasChildren && isNodeExpanded) {
|
|
91
|
+
walk(n.children, depth + 1, visible);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
walk(this.folders, 0, true);
|
|
96
|
+
return out.filter(r => r.visible);
|
|
97
|
+
}
|
|
98
|
+
isExpanded(id) {
|
|
99
|
+
return this.expandedSet.has(id);
|
|
100
|
+
}
|
|
101
|
+
isSelected(id) {
|
|
102
|
+
if (id == null && this.selectedFolderId == null)
|
|
103
|
+
return true;
|
|
104
|
+
return id != null && this.selectedFolderId != null && id === this.selectedFolderId;
|
|
105
|
+
}
|
|
106
|
+
onToggle(n, event) {
|
|
107
|
+
event.stopPropagation();
|
|
108
|
+
const expanded = !this.isExpanded(n.id);
|
|
109
|
+
this.folderExpansionToggled.emit({ id: n.id, expanded });
|
|
110
|
+
}
|
|
111
|
+
onSelect(n) {
|
|
112
|
+
if (this.renamingId != null)
|
|
113
|
+
return;
|
|
114
|
+
this.folderSelected.emit(n == null ? null : n.id);
|
|
115
|
+
}
|
|
116
|
+
onSelectUnorganised() {
|
|
117
|
+
// Sentinel: hosts that keep an "Unorganised" bucket can treat a null selection
|
|
118
|
+
// as "show unorganised". If there is already a root (null) selection pattern,
|
|
119
|
+
// this matches it. Host decides semantics.
|
|
120
|
+
this.folderSelected.emit(null);
|
|
121
|
+
}
|
|
122
|
+
beginRename(n, event) {
|
|
123
|
+
if (!this.allowRename)
|
|
124
|
+
return;
|
|
125
|
+
event.stopPropagation();
|
|
126
|
+
this.renamingId = n.id;
|
|
127
|
+
this.renameDraft = n.name;
|
|
128
|
+
this.cdr.markForCheck();
|
|
129
|
+
this.focusRenameInput(n.id);
|
|
130
|
+
}
|
|
131
|
+
commitRename(n) {
|
|
132
|
+
const name = (this.renameDraft || '').trim();
|
|
133
|
+
if (name && name !== n.name) {
|
|
134
|
+
// Emit the full renamed node (with the new name) + parent context so hosts can
|
|
135
|
+
// call `PUT /test_case_folders/{id}` without re-resolving from their cached tree.
|
|
136
|
+
const folder = { ...n, name };
|
|
137
|
+
this.folderRenamed.emit({
|
|
138
|
+
folder,
|
|
139
|
+
parentId: this.parentIdOf(n.id),
|
|
140
|
+
previousName: n.name,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
this.cancelRename();
|
|
144
|
+
}
|
|
145
|
+
cancelRename() {
|
|
146
|
+
this.renamingId = null;
|
|
147
|
+
this.renameDraft = '';
|
|
148
|
+
this.cdr.markForCheck();
|
|
149
|
+
}
|
|
150
|
+
onRenameKey(event, n) {
|
|
151
|
+
// Stop bubbling so the row's keydown handler (which treats Space/Enter/Arrows as
|
|
152
|
+
// selection / tree navigation) can't swallow characters typed into the rename input.
|
|
153
|
+
event.stopPropagation();
|
|
154
|
+
if (event.key === 'Enter') {
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
this.commitRename(n);
|
|
157
|
+
}
|
|
158
|
+
else if (event.key === 'Escape') {
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
this.cancelRename();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
deleteFolder(n, event) {
|
|
164
|
+
if (!this.allowDelete)
|
|
165
|
+
return;
|
|
166
|
+
event.stopPropagation();
|
|
167
|
+
this.folderDeleted.emit({ id: n.id });
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Opens the folder context menu anchored at the given client coordinates.
|
|
171
|
+
* Called from both (contextmenu) on the row and (click) on the ellipsis button.
|
|
172
|
+
* Menu visibility is clamped in the template via CSS transforms so it never leaves the viewport.
|
|
173
|
+
*/
|
|
174
|
+
openContextMenu(n, event) {
|
|
175
|
+
event.preventDefault();
|
|
176
|
+
event.stopPropagation();
|
|
177
|
+
if (!this.hasAnyContextAction)
|
|
178
|
+
return;
|
|
179
|
+
this.contextMenuFolderId = n.id;
|
|
180
|
+
this.contextMenuPosition = { x: event.clientX, y: event.clientY };
|
|
181
|
+
this.cdr.markForCheck();
|
|
182
|
+
}
|
|
183
|
+
closeContextMenu() {
|
|
184
|
+
if (this.contextMenuFolderId == null)
|
|
185
|
+
return;
|
|
186
|
+
this.contextMenuFolderId = null;
|
|
187
|
+
this.cdr.markForCheck();
|
|
188
|
+
}
|
|
189
|
+
/** True when at least one menu entry is enabled — otherwise the trigger is suppressed entirely. */
|
|
190
|
+
get hasAnyContextAction() {
|
|
191
|
+
return this.allowCreate || this.allowRename || this.allowMove || this.allowDuplicate || this.allowDelete;
|
|
192
|
+
}
|
|
193
|
+
onContextCreateSubfolder(n) {
|
|
194
|
+
this.closeContextMenu();
|
|
195
|
+
if (!this.allowCreate)
|
|
196
|
+
return;
|
|
197
|
+
this.folderCreateRequested.emit({ parentId: n.id });
|
|
198
|
+
}
|
|
199
|
+
onContextRename(n) {
|
|
200
|
+
this.closeContextMenu();
|
|
201
|
+
if (!this.allowRename)
|
|
202
|
+
return;
|
|
203
|
+
this.renamingId = n.id;
|
|
204
|
+
this.renameDraft = n.name;
|
|
205
|
+
this.cdr.markForCheck();
|
|
206
|
+
this.focusRenameInput(n.id);
|
|
207
|
+
}
|
|
208
|
+
onContextMove(n) {
|
|
209
|
+
this.closeContextMenu();
|
|
210
|
+
if (!this.allowMove)
|
|
211
|
+
return;
|
|
212
|
+
this.folderMoveRequested.emit({ id: n.id });
|
|
213
|
+
}
|
|
214
|
+
onContextDuplicate(n) {
|
|
215
|
+
this.closeContextMenu();
|
|
216
|
+
if (!this.allowDuplicate)
|
|
217
|
+
return;
|
|
218
|
+
this.folderDuplicateRequested.emit({ id: n.id });
|
|
219
|
+
}
|
|
220
|
+
onContextDelete(n) {
|
|
221
|
+
this.closeContextMenu();
|
|
222
|
+
if (!this.allowDelete)
|
|
223
|
+
return;
|
|
224
|
+
this.folderDeleted.emit({ id: n.id });
|
|
225
|
+
}
|
|
226
|
+
onDocumentClick() {
|
|
227
|
+
this.closeContextMenu();
|
|
228
|
+
}
|
|
229
|
+
onEscape() {
|
|
230
|
+
this.closeContextMenu();
|
|
231
|
+
}
|
|
232
|
+
/** Template helper: resolve a folder node by id from the input tree. */
|
|
233
|
+
folderById(id) {
|
|
234
|
+
if (id == null)
|
|
235
|
+
return null;
|
|
236
|
+
const find = (nodes) => {
|
|
237
|
+
for (const n of nodes || []) {
|
|
238
|
+
if (n.id === id)
|
|
239
|
+
return n;
|
|
240
|
+
if (n.children?.length) {
|
|
241
|
+
const hit = find(n.children);
|
|
242
|
+
if (hit)
|
|
243
|
+
return hit;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return null;
|
|
247
|
+
};
|
|
248
|
+
return find(this.folders);
|
|
249
|
+
}
|
|
250
|
+
/** Returns the parent id of `id`, or null when `id` is a root folder / not found. */
|
|
251
|
+
parentIdOf(id) {
|
|
252
|
+
let result = null;
|
|
253
|
+
const walk = (nodes, parentId) => {
|
|
254
|
+
for (const n of nodes || []) {
|
|
255
|
+
if (n.id === id) {
|
|
256
|
+
result = parentId;
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
if (n.children?.length && walk(n.children, n.id))
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
return false;
|
|
263
|
+
};
|
|
264
|
+
walk(this.folders, null);
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
267
|
+
requestCreate(parentId = null) {
|
|
268
|
+
if (!this.allowCreate)
|
|
269
|
+
return;
|
|
270
|
+
this.folderCreateRequested.emit({ parentId });
|
|
271
|
+
}
|
|
272
|
+
togglePanel() {
|
|
273
|
+
this.collapsed = !this.collapsed;
|
|
274
|
+
this.collapsedChange.emit(this.collapsed);
|
|
275
|
+
}
|
|
276
|
+
rowDropped(targetId, event) {
|
|
277
|
+
// Directive already emits with the bound folder id, but normalise in case callers
|
|
278
|
+
// wire raw events.
|
|
279
|
+
this.testsDropped.emit({ testIds: event.testIds, targetFolderId: targetId });
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Drop target received a folder drag. Validate against self/descendant cycles and
|
|
283
|
+
* emit `folderDropped` if the move is legal. Called from the directive's
|
|
284
|
+
* `(folderDropped)` output on every row and on the Unorganised row.
|
|
285
|
+
*/
|
|
286
|
+
onFolderRowDropped(targetId, event) {
|
|
287
|
+
this.clearAllExpandTimers();
|
|
288
|
+
this.activeFolderDragId = null;
|
|
289
|
+
const sourceId = event.folderId;
|
|
290
|
+
if (targetId != null && targetId === sourceId)
|
|
291
|
+
return;
|
|
292
|
+
if (targetId != null && this.isDescendantOf(sourceId, targetId))
|
|
293
|
+
return;
|
|
294
|
+
this.folderDropped.emit({ id: sourceId, newParentId: targetId });
|
|
295
|
+
}
|
|
296
|
+
/** Remember the dragged folder so we can block self-hover expansion and cycle drops. */
|
|
297
|
+
onFolderDragStart(n) {
|
|
298
|
+
this.activeFolderDragId = n.id;
|
|
299
|
+
}
|
|
300
|
+
onFolderDragEnd() {
|
|
301
|
+
this.activeFolderDragId = null;
|
|
302
|
+
this.clearAllExpandTimers();
|
|
303
|
+
}
|
|
304
|
+
/** Schedule an auto-expand for a collapsed folder that has children during drag-hover. */
|
|
305
|
+
onFolderRowDragOver(row) {
|
|
306
|
+
const id = row.node.id;
|
|
307
|
+
if (!row.hasChildren)
|
|
308
|
+
return;
|
|
309
|
+
if (this.isExpanded(id))
|
|
310
|
+
return;
|
|
311
|
+
if (this.activeFolderDragId === id)
|
|
312
|
+
return; // don't expand the source
|
|
313
|
+
if (this.dragExpandTimers.has(id))
|
|
314
|
+
return;
|
|
315
|
+
const timer = setTimeout(() => {
|
|
316
|
+
this.dragExpandTimers.delete(id);
|
|
317
|
+
if (!this.isExpanded(id)) {
|
|
318
|
+
this.folderExpansionToggled.emit({ id, expanded: true });
|
|
319
|
+
}
|
|
320
|
+
}, this.DRAG_EXPAND_DELAY_MS);
|
|
321
|
+
this.dragExpandTimers.set(id, timer);
|
|
322
|
+
}
|
|
323
|
+
onFolderRowDragLeave(row) {
|
|
324
|
+
const id = row.node.id;
|
|
325
|
+
const timer = this.dragExpandTimers.get(id);
|
|
326
|
+
if (timer != null) {
|
|
327
|
+
clearTimeout(timer);
|
|
328
|
+
this.dragExpandTimers.delete(id);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
onDocumentDragEnd() {
|
|
332
|
+
this.onFolderDragEnd();
|
|
333
|
+
}
|
|
334
|
+
/** True when `candidateId` is `sourceId` itself or any descendant of it. */
|
|
335
|
+
isDescendantOf(sourceId, candidateId) {
|
|
336
|
+
const source = this.folderById(sourceId);
|
|
337
|
+
if (!source)
|
|
338
|
+
return false;
|
|
339
|
+
const stack = [source];
|
|
340
|
+
while (stack.length) {
|
|
341
|
+
const n = stack.pop();
|
|
342
|
+
if (n.id === candidateId)
|
|
343
|
+
return true;
|
|
344
|
+
if (n.children?.length)
|
|
345
|
+
stack.push(...n.children);
|
|
346
|
+
}
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
clearAllExpandTimers() {
|
|
350
|
+
this.dragExpandTimers.forEach(t => clearTimeout(t));
|
|
351
|
+
this.dragExpandTimers.clear();
|
|
352
|
+
}
|
|
353
|
+
ngOnDestroy() {
|
|
354
|
+
this.clearAllExpandTimers();
|
|
355
|
+
}
|
|
356
|
+
onRowKeydown(event, row, index) {
|
|
357
|
+
const rows = this.rows;
|
|
358
|
+
switch (event.key) {
|
|
359
|
+
case 'ArrowDown': {
|
|
360
|
+
event.preventDefault();
|
|
361
|
+
const next = rows[index + 1];
|
|
362
|
+
if (next)
|
|
363
|
+
this.focusRowElement(next.node.id);
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
case 'ArrowUp': {
|
|
367
|
+
event.preventDefault();
|
|
368
|
+
const prev = rows[index - 1];
|
|
369
|
+
if (prev)
|
|
370
|
+
this.focusRowElement(prev.node.id);
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case 'ArrowRight': {
|
|
374
|
+
if (row.hasChildren && !this.isExpanded(row.node.id)) {
|
|
375
|
+
event.preventDefault();
|
|
376
|
+
this.folderExpansionToggled.emit({ id: row.node.id, expanded: true });
|
|
377
|
+
}
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
case 'ArrowLeft': {
|
|
381
|
+
if (row.hasChildren && this.isExpanded(row.node.id)) {
|
|
382
|
+
event.preventDefault();
|
|
383
|
+
this.folderExpansionToggled.emit({ id: row.node.id, expanded: false });
|
|
384
|
+
}
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case 'Enter':
|
|
388
|
+
case ' ': {
|
|
389
|
+
event.preventDefault();
|
|
390
|
+
this.onSelect(row.node);
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
focusRowElement(id) {
|
|
396
|
+
queueMicrotask(() => {
|
|
397
|
+
const el = document.querySelector(`[data-folder-row-id="${id}"]`);
|
|
398
|
+
el?.focus();
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Focuses the inline rename input for the given folder. Uses `setTimeout` (not
|
|
403
|
+
* microtask) because the input is gated by `*ngIf="renamingId === ..."` and
|
|
404
|
+
* must survive the next OnPush change-detection cycle before it's in the DOM.
|
|
405
|
+
*/
|
|
406
|
+
focusRenameInput(id) {
|
|
407
|
+
setTimeout(() => {
|
|
408
|
+
const el = document.querySelector(`[data-folder-rename-input="${id}"]`);
|
|
409
|
+
if (!el)
|
|
410
|
+
return;
|
|
411
|
+
el.focus();
|
|
412
|
+
el.select();
|
|
413
|
+
}, 0);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
FolderSidebarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: FolderSidebarComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
417
|
+
FolderSidebarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: FolderSidebarComponent, selector: "cqa-folder-sidebar", inputs: { folders: "folders", selectedFolderId: "selectedFolderId", expandedFolderIds: "expandedFolderIds", unorganisedCount: "unorganisedCount", allowCreate: "allowCreate", allowRename: "allowRename", allowDelete: "allowDelete", allowMove: "allowMove", allowDuplicate: "allowDuplicate", allowDrop: "allowDrop", showCounts: "showCounts", collapsed: "collapsed", labels: "labels" }, outputs: { folderSelected: "folderSelected", folderExpansionToggled: "folderExpansionToggled", folderCreated: "folderCreated", folderCreateRequested: "folderCreateRequested", folderRenamed: "folderRenamed", folderDeleted: "folderDeleted", folderMoveRequested: "folderMoveRequested", folderDuplicateRequested: "folderDuplicateRequested", testsDropped: "testsDropped", folderDropped: "folderDropped", collapsedChange: "collapsedChange" }, host: { listeners: { "document:click": "onDocumentClick()", "document:keydown.escape": "onEscape()", "document:dragend": "onDocumentDragEnd()" }, classAttribute: "cqa-ui-root" }, ngImport: i0, template: "<!-- Reusable folder icon. Render via <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: node.color }\"></ng-container>. -->\n<ng-template #folderIcon let-color=\"color\">\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center cqa-w-4 cqa-h-4 cqa-shrink-0\">\n <svg width=\"13\" height=\"12\" viewBox=\"0 0 13 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12.1916 9.85824C12.1916 10.1677 12.0687 10.4644 11.8499 10.6832C11.6311 10.902 11.3343 11.0249 11.0249 11.0249H1.69157C1.38215 11.0249 1.0854 10.902 0.866611 10.6832C0.647819 10.4644 0.524902 10.1677 0.524902 9.85824V1.69157C0.524902 1.38215 0.647819 1.0854 0.866611 0.866611C1.0854 0.647819 1.38215 0.524902 1.69157 0.524902H4.60824L5.7749 2.2749H11.0249C11.3343 2.2749 11.6311 2.39782 11.8499 2.61661C12.0687 2.8354 12.1916 3.13215 12.1916 3.44157V9.85824Z\" [attr.stroke]=\"color || '#99999E'\" stroke-width=\"1.05\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n</ng-template>\n\n<aside\n class=\"cqa-flex cqa-flex-col cqa-bg-white cqa-border cqa-border-neutral-200 cqa-rounded-lg cqa-h-full cqa-min-h-0\"\n [class.cqa-w-[240px]]=\"!collapsed\"\n [class.cqa-w-[48px]]=\"collapsed\"\n style=\"transition: width 150ms ease;\"\n>\n <!-- Header -->\n <div class=\"cqa-flex cqa-items-center cqa-justify-between cqa-px-3 cqa-py-3\">\n <span *ngIf=\"!collapsed\" class=\"cqa-text-sm cqa-font-semibold cqa-text-neutral-900\">\n {{ labels.folders }}\n </span>\n <div class=\"cqa-flex cqa-items-center cqa-gap-1\" [class.cqa-w-full]=\"collapsed\" [class.cqa-justify-center]=\"collapsed\">\n <button\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"togglePanel()\"\n [attr.aria-label]=\"collapsed ? 'Expand folders panel' : 'Collapse folders panel'\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">\n {{ collapsed ? 'chevron_right' : 'keyboard_double_arrow_left' }}\n </mat-icon>\n </button>\n <button\n *ngIf=\"!collapsed && allowCreate\"\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"requestCreate(null)\"\n [attr.aria-label]=\"labels.newFolder\"\n [title]=\"labels.newFolder\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container *ngIf=\"!collapsed\">\n <!-- Search -->\n <div class=\"cqa-px-3 cqa-pb-2\">\n <cqa-search-bar\n size=\"sm\"\n [fullWidth]=\"true\"\n [value]=\"searchValue\"\n [placeholder]=\"labels.searchFoldersPlaceholder\"\n [showClear]=\"true\"\n (valueChange)=\"searchValue = $event\"\n (cleared)=\"searchValue = ''\"\n ></cqa-search-bar>\n </div>\n\n <!-- Tree (folders + Unorganised tab) -->\n <div role=\"tree\" class=\"cqa-flex-1 cqa-overflow-y-auto cqa-py-1\">\n <ng-container *ngFor=\"let row of rows; let i = index; trackBy: trackByRow\">\n <div\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-expanded]=\"row.hasChildren ? isExpanded(row.node.id) : null\"\n [attr.aria-selected]=\"isSelected(row.node.id)\"\n [attr.data-folder-row-id]=\"row.node.id\"\n [cqaFolderDrop]=\"row.node.id\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(row.node.id, $event)\"\n (folderDropped)=\"onFolderRowDropped(row.node.id, $event)\"\n [cqaFolderDrag]=\"row.node.id\"\n [dragEnabled]=\"allowDrop\"\n (dragstart)=\"onFolderDragStart(row.node)\"\n (dragend)=\"onFolderDragEnd()\"\n (dragover)=\"onFolderRowDragOver(row)\"\n (dragleave)=\"onFolderRowDragLeave(row)\"\n (click)=\"onSelect(row.node)\"\n (contextmenu)=\"openContextMenu(row.node, $event)\"\n (keydown)=\"onRowKeydown($event, row, i)\"\n class=\"cqa-group cqa-flex cqa-items-center cqa-gap-1 cqa-pr-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-neutral-50 focus:cqa-outline-none focus:cqa-bg-neutral-100 cqa-relative\"\n [class.cqa-bg-indigo-50]=\"isSelected(row.node.id) || contextMenuFolderId === row.node.id\"\n [style.paddingLeft.px]=\"8 + row.depth * 16\"\n [attr.title]=\"row.node.totalCount != null ? (row.node.totalCount + ' total including subfolders') : null\"\n >\n <span\n *ngIf=\"isSelected(row.node.id)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <button\n type=\"button\"\n class=\"cqa-p-0 cqa-text-neutral-400\"\n [style.visibility]=\"row.hasChildren ? 'visible' : 'hidden'\"\n (click)=\"onToggle(row.node, $event)\"\n [attr.aria-label]=\"isExpanded(row.node.id) ? 'Collapse' : 'Expand'\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">\n {{ isExpanded(row.node.id) ? 'expand_more' : 'chevron_right' }}\n </mat-icon>\n </button>\n <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: row.node.color }\"></ng-container>\n\n <ng-container *ngIf=\"renamingId === row.node.id; else nameTpl\">\n <input\n type=\"text\"\n size=\"1\"\n [attr.data-folder-rename-input]=\"row.node.id\"\n class=\"cqa-flex-1 cqa-min-w-0 cqa-w-full cqa-px-2 cqa-py-0.5 cqa-rounded cqa-border cqa-border-neutral-300 cqa-bg-white cqa-text-sm cqa-shadow-sm focus:cqa-outline-none focus:cqa-border-indigo-400 focus:cqa-ring-1 focus:cqa-ring-indigo-200\"\n [(ngModel)]=\"renameDraft\"\n (keydown)=\"onRenameKey($event, row.node)\"\n (keypress)=\"$event.stopPropagation()\"\n (keyup)=\"$event.stopPropagation()\"\n (blur)=\"commitRename(row.node)\"\n (click)=\"$event.stopPropagation()\"\n />\n </ng-container>\n <ng-template #nameTpl>\n <span\n class=\"cqa-flex-1 cqa-text-sm cqa-text-neutral-800 cqa-truncate\"\n (dblclick)=\"beginRename(row.node, $event)\"\n >{{ row.node.name }}</span>\n </ng-template>\n\n <!-- Count shown at rest; hidden on row hover when delete is allowed -->\n <span\n *ngIf=\"showCounts && row.node.count != null\"\n class=\"cqa-text-xs cqa-text-neutral-400 cqa-tabular-nums cqa-ml-1\"\n [class.group-hover:cqa-hidden]=\"allowDelete\"\n >{{ row.node.count }}</span>\n\n <button\n *ngIf=\"hasAnyContextAction\"\n type=\"button\"\n class=\"cqa-p-0.5 cqa-rounded hover:cqa-bg-neutral-200 cqa-text-neutral-500\"\n [class.cqa-hidden]=\"contextMenuFolderId !== row.node.id\"\n [class.group-hover:cqa-inline-flex]=\"true\"\n [class.cqa-inline-flex]=\"contextMenuFolderId === row.node.id\"\n (click)=\"openContextMenu(row.node, $event)\"\n [attr.aria-label]=\"'Open actions for ' + row.node.name\"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"contextMenuFolderId === row.node.id\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">more_horiz</mat-icon>\n </button>\n </div>\n </ng-container>\n\n <!-- Divider between folder tree and Unorganised tab -->\n <div\n aria-hidden=\"true\"\n class=\"cqa-mx-3\"\n style=\"height: 1px; background-color: #E5E7EB; margin-top: 6px; margin-bottom: 6px;\"\n ></div>\n\n <!-- Unorganised \u2014 flows with the tree, behaves like a tab item -->\n <div\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-selected]=\"isSelected(null)\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-px-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-neutral-50 focus:cqa-outline-none focus:cqa-bg-neutral-100 cqa-relative\"\n [class.cqa-bg-indigo-50]=\"isSelected(null)\"\n [cqaFolderDrop]=\"null\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(null, $event)\"\n (folderDropped)=\"onFolderRowDropped(null, $event)\"\n (click)=\"onSelectUnorganised()\"\n >\n <span\n *ngIf=\"isSelected(null)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <mat-icon class=\"cqa-text-neutral-500\" style=\"font-size:16px;width:16px;height:16px\">inbox</mat-icon>\n <span class=\"cqa-flex-1 cqa-text-sm cqa-text-neutral-700\">{{ labels.unorganised }}</span>\n <span *ngIf=\"showCounts\" class=\"cqa-text-xs cqa-text-neutral-400 cqa-tabular-nums\">{{ unorganisedCount }}</span>\n </div>\n </div>\n\n <!-- Folder context menu (right-click / ellipsis). Anchored to viewport coords. -->\n <div\n *ngIf=\"contextMenuFolderId !== null\"\n role=\"menu\"\n class=\"cqa-fixed cqa-z-50 cqa-min-w-[180px] cqa-bg-white cqa-border cqa-border-neutral-200 cqa-rounded-md cqa-shadow-lg cqa-py-1\"\n [style.left.px]=\"contextMenuPosition.x\"\n [style.top.px]=\"contextMenuPosition.y\"\n (click)=\"$event.stopPropagation()\"\n (contextmenu)=\"$event.preventDefault(); $event.stopPropagation()\"\n >\n <ng-container *ngIf=\"folderById(contextMenuFolderId) as menuNode\">\n <button\n *ngIf=\"allowCreate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-neutral-100 cqa-text-left\"\n (click)=\"onContextCreateSubfolder(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">create_new_folder</mat-icon>\n <span>{{ labels.folderMenuCreateSubfolder }}</span>\n </button>\n <button\n *ngIf=\"allowRename\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-neutral-100 cqa-text-left\"\n (click)=\"onContextRename(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">edit</mat-icon>\n <span>{{ labels.folderMenuRename }}</span>\n </button>\n <button\n *ngIf=\"allowMove\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-neutral-100 cqa-text-left\"\n (click)=\"onContextMove(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">drive_file_move</mat-icon>\n <span>{{ labels.folderMenuMove }}</span>\n </button>\n <button\n *ngIf=\"allowDuplicate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-neutral-100 cqa-text-left\"\n (click)=\"onContextDuplicate(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">content_copy</mat-icon>\n <span>{{ labels.folderMenuDuplicate }}</span>\n </button>\n <div *ngIf=\"allowDelete && (allowCreate || allowRename || allowMove || allowDuplicate)\" class=\"cqa-h-px cqa-bg-neutral-200 cqa-my-1\"></div>\n <button\n *ngIf=\"allowDelete\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-red-600 hover:cqa-bg-red-50 cqa-text-left\"\n (click)=\"onContextDelete(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">delete_outline</mat-icon>\n <span>{{ labels.folderMenuDelete }}</span>\n </button>\n </ng-container>\n </div>\n\n </ng-container>\n</aside>\n", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: i2.SearchBarComponent, selector: "cqa-search-bar", inputs: ["placeholder", "value", "disabled", "showClear", "ariaLabel", "autoFocus", "size", "fullWidth"], outputs: ["valueChange", "search", "cleared"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i4.FolderDropDirective, selector: "[cqaFolderDrop]", inputs: ["cqaFolderDrop", "dropEnabled"], outputs: ["testsDropped", "folderDropped"] }, { type: i5.FolderDragDirective, selector: "[cqaFolderDrag]", inputs: ["cqaFolderDrag", "dragEnabled"] }, { type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
418
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: FolderSidebarComponent, decorators: [{
|
|
419
|
+
type: Component,
|
|
420
|
+
args: [{ selector: 'cqa-folder-sidebar', host: { class: 'cqa-ui-root' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Reusable folder icon. Render via <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: node.color }\"></ng-container>. -->\n<ng-template #folderIcon let-color=\"color\">\n <span class=\"cqa-inline-flex cqa-items-center cqa-justify-center cqa-w-4 cqa-h-4 cqa-shrink-0\">\n <svg width=\"13\" height=\"12\" viewBox=\"0 0 13 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12.1916 9.85824C12.1916 10.1677 12.0687 10.4644 11.8499 10.6832C11.6311 10.902 11.3343 11.0249 11.0249 11.0249H1.69157C1.38215 11.0249 1.0854 10.902 0.866611 10.6832C0.647819 10.4644 0.524902 10.1677 0.524902 9.85824V1.69157C0.524902 1.38215 0.647819 1.0854 0.866611 0.866611C1.0854 0.647819 1.38215 0.524902 1.69157 0.524902H4.60824L5.7749 2.2749H11.0249C11.3343 2.2749 11.6311 2.39782 11.8499 2.61661C12.0687 2.8354 12.1916 3.13215 12.1916 3.44157V9.85824Z\" [attr.stroke]=\"color || '#99999E'\" stroke-width=\"1.05\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </span>\n</ng-template>\n\n<aside\n class=\"cqa-flex cqa-flex-col cqa-bg-white cqa-border cqa-border-neutral-200 cqa-rounded-lg cqa-h-full cqa-min-h-0\"\n [class.cqa-w-[240px]]=\"!collapsed\"\n [class.cqa-w-[48px]]=\"collapsed\"\n style=\"transition: width 150ms ease;\"\n>\n <!-- Header -->\n <div class=\"cqa-flex cqa-items-center cqa-justify-between cqa-px-3 cqa-py-3\">\n <span *ngIf=\"!collapsed\" class=\"cqa-text-sm cqa-font-semibold cqa-text-neutral-900\">\n {{ labels.folders }}\n </span>\n <div class=\"cqa-flex cqa-items-center cqa-gap-1\" [class.cqa-w-full]=\"collapsed\" [class.cqa-justify-center]=\"collapsed\">\n <button\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"togglePanel()\"\n [attr.aria-label]=\"collapsed ? 'Expand folders panel' : 'Collapse folders panel'\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">\n {{ collapsed ? 'chevron_right' : 'keyboard_double_arrow_left' }}\n </mat-icon>\n </button>\n <button\n *ngIf=\"!collapsed && allowCreate\"\n type=\"button\"\n class=\"cqa-p-1 cqa-rounded hover:cqa-bg-neutral-100 cqa-text-neutral-500\"\n (click)=\"requestCreate(null)\"\n [attr.aria-label]=\"labels.newFolder\"\n [title]=\"labels.newFolder\"\n >\n <mat-icon style=\"font-size:18px;width:18px;height:18px\">add</mat-icon>\n </button>\n </div>\n </div>\n\n <ng-container *ngIf=\"!collapsed\">\n <!-- Search -->\n <div class=\"cqa-px-3 cqa-pb-2\">\n <cqa-search-bar\n size=\"sm\"\n [fullWidth]=\"true\"\n [value]=\"searchValue\"\n [placeholder]=\"labels.searchFoldersPlaceholder\"\n [showClear]=\"true\"\n (valueChange)=\"searchValue = $event\"\n (cleared)=\"searchValue = ''\"\n ></cqa-search-bar>\n </div>\n\n <!-- Tree (folders + Unorganised tab) -->\n <div role=\"tree\" class=\"cqa-flex-1 cqa-overflow-y-auto cqa-py-1\">\n <ng-container *ngFor=\"let row of rows; let i = index; trackBy: trackByRow\">\n <div\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-expanded]=\"row.hasChildren ? isExpanded(row.node.id) : null\"\n [attr.aria-selected]=\"isSelected(row.node.id)\"\n [attr.data-folder-row-id]=\"row.node.id\"\n [cqaFolderDrop]=\"row.node.id\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(row.node.id, $event)\"\n (folderDropped)=\"onFolderRowDropped(row.node.id, $event)\"\n [cqaFolderDrag]=\"row.node.id\"\n [dragEnabled]=\"allowDrop\"\n (dragstart)=\"onFolderDragStart(row.node)\"\n (dragend)=\"onFolderDragEnd()\"\n (dragover)=\"onFolderRowDragOver(row)\"\n (dragleave)=\"onFolderRowDragLeave(row)\"\n (click)=\"onSelect(row.node)\"\n (contextmenu)=\"openContextMenu(row.node, $event)\"\n (keydown)=\"onRowKeydown($event, row, i)\"\n class=\"cqa-group cqa-flex cqa-items-center cqa-gap-1 cqa-pr-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-neutral-50 focus:cqa-outline-none focus:cqa-bg-neutral-100 cqa-relative\"\n [class.cqa-bg-indigo-50]=\"isSelected(row.node.id) || contextMenuFolderId === row.node.id\"\n [style.paddingLeft.px]=\"8 + row.depth * 16\"\n [attr.title]=\"row.node.totalCount != null ? (row.node.totalCount + ' total including subfolders') : null\"\n >\n <span\n *ngIf=\"isSelected(row.node.id)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <button\n type=\"button\"\n class=\"cqa-p-0 cqa-text-neutral-400\"\n [style.visibility]=\"row.hasChildren ? 'visible' : 'hidden'\"\n (click)=\"onToggle(row.node, $event)\"\n [attr.aria-label]=\"isExpanded(row.node.id) ? 'Collapse' : 'Expand'\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">\n {{ isExpanded(row.node.id) ? 'expand_more' : 'chevron_right' }}\n </mat-icon>\n </button>\n <ng-container *ngTemplateOutlet=\"folderIcon; context: { color: row.node.color }\"></ng-container>\n\n <ng-container *ngIf=\"renamingId === row.node.id; else nameTpl\">\n <input\n type=\"text\"\n size=\"1\"\n [attr.data-folder-rename-input]=\"row.node.id\"\n class=\"cqa-flex-1 cqa-min-w-0 cqa-w-full cqa-px-2 cqa-py-0.5 cqa-rounded cqa-border cqa-border-neutral-300 cqa-bg-white cqa-text-sm cqa-shadow-sm focus:cqa-outline-none focus:cqa-border-indigo-400 focus:cqa-ring-1 focus:cqa-ring-indigo-200\"\n [(ngModel)]=\"renameDraft\"\n (keydown)=\"onRenameKey($event, row.node)\"\n (keypress)=\"$event.stopPropagation()\"\n (keyup)=\"$event.stopPropagation()\"\n (blur)=\"commitRename(row.node)\"\n (click)=\"$event.stopPropagation()\"\n />\n </ng-container>\n <ng-template #nameTpl>\n <span\n class=\"cqa-flex-1 cqa-text-sm cqa-text-neutral-800 cqa-truncate\"\n (dblclick)=\"beginRename(row.node, $event)\"\n >{{ row.node.name }}</span>\n </ng-template>\n\n <!-- Count shown at rest; hidden on row hover when delete is allowed -->\n <span\n *ngIf=\"showCounts && row.node.count != null\"\n class=\"cqa-text-xs cqa-text-neutral-400 cqa-tabular-nums cqa-ml-1\"\n [class.group-hover:cqa-hidden]=\"allowDelete\"\n >{{ row.node.count }}</span>\n\n <button\n *ngIf=\"hasAnyContextAction\"\n type=\"button\"\n class=\"cqa-p-0.5 cqa-rounded hover:cqa-bg-neutral-200 cqa-text-neutral-500\"\n [class.cqa-hidden]=\"contextMenuFolderId !== row.node.id\"\n [class.group-hover:cqa-inline-flex]=\"true\"\n [class.cqa-inline-flex]=\"contextMenuFolderId === row.node.id\"\n (click)=\"openContextMenu(row.node, $event)\"\n [attr.aria-label]=\"'Open actions for ' + row.node.name\"\n [attr.aria-haspopup]=\"'menu'\"\n [attr.aria-expanded]=\"contextMenuFolderId === row.node.id\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">more_horiz</mat-icon>\n </button>\n </div>\n </ng-container>\n\n <!-- Divider between folder tree and Unorganised tab -->\n <div\n aria-hidden=\"true\"\n class=\"cqa-mx-3\"\n style=\"height: 1px; background-color: #E5E7EB; margin-top: 6px; margin-bottom: 6px;\"\n ></div>\n\n <!-- Unorganised \u2014 flows with the tree, behaves like a tab item -->\n <div\n role=\"treeitem\"\n tabindex=\"0\"\n [attr.aria-selected]=\"isSelected(null)\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-px-3 cqa-py-1.5 cqa-cursor-pointer hover:cqa-bg-neutral-50 focus:cqa-outline-none focus:cqa-bg-neutral-100 cqa-relative\"\n [class.cqa-bg-indigo-50]=\"isSelected(null)\"\n [cqaFolderDrop]=\"null\"\n [dropEnabled]=\"allowDrop\"\n (testsDropped)=\"rowDropped(null, $event)\"\n (folderDropped)=\"onFolderRowDropped(null, $event)\"\n (click)=\"onSelectUnorganised()\"\n >\n <span\n *ngIf=\"isSelected(null)\"\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-bottom-0 cqa-w-[3px] cqa-bg-indigo-600\"\n ></span>\n <mat-icon class=\"cqa-text-neutral-500\" style=\"font-size:16px;width:16px;height:16px\">inbox</mat-icon>\n <span class=\"cqa-flex-1 cqa-text-sm cqa-text-neutral-700\">{{ labels.unorganised }}</span>\n <span *ngIf=\"showCounts\" class=\"cqa-text-xs cqa-text-neutral-400 cqa-tabular-nums\">{{ unorganisedCount }}</span>\n </div>\n </div>\n\n <!-- Folder context menu (right-click / ellipsis). Anchored to viewport coords. -->\n <div\n *ngIf=\"contextMenuFolderId !== null\"\n role=\"menu\"\n class=\"cqa-fixed cqa-z-50 cqa-min-w-[180px] cqa-bg-white cqa-border cqa-border-neutral-200 cqa-rounded-md cqa-shadow-lg cqa-py-1\"\n [style.left.px]=\"contextMenuPosition.x\"\n [style.top.px]=\"contextMenuPosition.y\"\n (click)=\"$event.stopPropagation()\"\n (contextmenu)=\"$event.preventDefault(); $event.stopPropagation()\"\n >\n <ng-container *ngIf=\"folderById(contextMenuFolderId) as menuNode\">\n <button\n *ngIf=\"allowCreate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-neutral-100 cqa-text-left\"\n (click)=\"onContextCreateSubfolder(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">create_new_folder</mat-icon>\n <span>{{ labels.folderMenuCreateSubfolder }}</span>\n </button>\n <button\n *ngIf=\"allowRename\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-neutral-100 cqa-text-left\"\n (click)=\"onContextRename(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">edit</mat-icon>\n <span>{{ labels.folderMenuRename }}</span>\n </button>\n <button\n *ngIf=\"allowMove\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-neutral-100 cqa-text-left\"\n (click)=\"onContextMove(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">drive_file_move</mat-icon>\n <span>{{ labels.folderMenuMove }}</span>\n </button>\n <button\n *ngIf=\"allowDuplicate\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-neutral-800 hover:cqa-bg-neutral-100 cqa-text-left\"\n (click)=\"onContextDuplicate(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">content_copy</mat-icon>\n <span>{{ labels.folderMenuDuplicate }}</span>\n </button>\n <div *ngIf=\"allowDelete && (allowCreate || allowRename || allowMove || allowDuplicate)\" class=\"cqa-h-px cqa-bg-neutral-200 cqa-my-1\"></div>\n <button\n *ngIf=\"allowDelete\"\n type=\"button\"\n role=\"menuitem\"\n class=\"cqa-flex cqa-items-center cqa-gap-2 cqa-w-full cqa-px-3 cqa-py-1.5 cqa-text-sm cqa-text-red-600 hover:cqa-bg-red-50 cqa-text-left\"\n (click)=\"onContextDelete(menuNode)\"\n >\n <mat-icon style=\"font-size:16px;width:16px;height:16px\">delete_outline</mat-icon>\n <span>{{ labels.folderMenuDelete }}</span>\n </button>\n </ng-container>\n </div>\n\n </ng-container>\n</aside>\n", styles: [] }]
|
|
421
|
+
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { folders: [{
|
|
422
|
+
type: Input
|
|
423
|
+
}], selectedFolderId: [{
|
|
424
|
+
type: Input
|
|
425
|
+
}], expandedFolderIds: [{
|
|
426
|
+
type: Input
|
|
427
|
+
}], unorganisedCount: [{
|
|
428
|
+
type: Input
|
|
429
|
+
}], allowCreate: [{
|
|
430
|
+
type: Input
|
|
431
|
+
}], allowRename: [{
|
|
432
|
+
type: Input
|
|
433
|
+
}], allowDelete: [{
|
|
434
|
+
type: Input
|
|
435
|
+
}], allowMove: [{
|
|
436
|
+
type: Input
|
|
437
|
+
}], allowDuplicate: [{
|
|
438
|
+
type: Input
|
|
439
|
+
}], allowDrop: [{
|
|
440
|
+
type: Input
|
|
441
|
+
}], showCounts: [{
|
|
442
|
+
type: Input
|
|
443
|
+
}], collapsed: [{
|
|
444
|
+
type: Input
|
|
445
|
+
}], labels: [{
|
|
446
|
+
type: Input
|
|
447
|
+
}], folderSelected: [{
|
|
448
|
+
type: Output
|
|
449
|
+
}], folderExpansionToggled: [{
|
|
450
|
+
type: Output
|
|
451
|
+
}], folderCreated: [{
|
|
452
|
+
type: Output
|
|
453
|
+
}], folderCreateRequested: [{
|
|
454
|
+
type: Output
|
|
455
|
+
}], folderRenamed: [{
|
|
456
|
+
type: Output
|
|
457
|
+
}], folderDeleted: [{
|
|
458
|
+
type: Output
|
|
459
|
+
}], folderMoveRequested: [{
|
|
460
|
+
type: Output
|
|
461
|
+
}], folderDuplicateRequested: [{
|
|
462
|
+
type: Output
|
|
463
|
+
}], testsDropped: [{
|
|
464
|
+
type: Output
|
|
465
|
+
}], folderDropped: [{
|
|
466
|
+
type: Output
|
|
467
|
+
}], collapsedChange: [{
|
|
468
|
+
type: Output
|
|
469
|
+
}], onDocumentClick: [{
|
|
470
|
+
type: HostListener,
|
|
471
|
+
args: ['document:click']
|
|
472
|
+
}], onEscape: [{
|
|
473
|
+
type: HostListener,
|
|
474
|
+
args: ['document:keydown.escape']
|
|
475
|
+
}], onDocumentDragEnd: [{
|
|
476
|
+
type: HostListener,
|
|
477
|
+
args: ['document:dragend']
|
|
478
|
+
}] } });
|
|
479
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9sZGVyLXNpZGViYXIuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi90ZW1wbGF0ZXMvbW9kdWxhci10YWJsZS10ZW1wbGF0ZS9mb2xkZXItc2lkZWJhci9mb2xkZXItc2lkZWJhci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL3RlbXBsYXRlcy9tb2R1bGFyLXRhYmxlLXRlbXBsYXRlL2ZvbGRlci1zaWRlYmFyL2ZvbGRlci1zaWRlYmFyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCx1QkFBdUIsRUFFdkIsU0FBUyxFQUNULFlBQVksRUFDWixZQUFZLEVBQ1osS0FBSyxFQUVMLE1BQU0sR0FDUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQ0wsc0JBQXNCLEdBSXZCLE1BQU0sa0NBQWtDLENBQUM7Ozs7Ozs7O0FBZ0IxQyxNQUFNLE9BQU8sc0JBQXNCO0lBZ0RqQyxZQUFvQixHQUFzQjtRQUF0QixRQUFHLEdBQUgsR0FBRyxDQUFtQjtRQS9DakMsWUFBTyxHQUFpQixFQUFFLENBQUM7UUFDM0IscUJBQWdCLEdBQWtCLElBQUksQ0FBQztRQUN2QyxzQkFBaUIsR0FBYSxFQUFFLENBQUM7UUFDakMscUJBQWdCLEdBQVcsQ0FBQyxDQUFDO1FBQzdCLGdCQUFXLEdBQVksSUFBSSxDQUFDO1FBQzVCLGdCQUFXLEdBQVksSUFBSSxDQUFDO1FBQzVCLGdCQUFXLEdBQVksSUFBSSxDQUFDO1FBQzVCLGNBQVMsR0FBWSxJQUFJLENBQUM7UUFDMUIsbUJBQWMsR0FBWSxJQUFJLENBQUM7UUFDL0IsY0FBUyxHQUFZLElBQUksQ0FBQztRQUMxQixlQUFVLEdBQVksSUFBSSxDQUFDO1FBQzNCLGNBQVMsR0FBWSxLQUFLLENBQUM7UUFDM0IsV0FBTSxHQUFrQixFQUFFLEdBQUcsc0JBQXNCLEVBQUUsQ0FB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QUFhO3NCQUF0QixNQUFNO2dCQUVHLG1CQUFtQjtzQkFBNUIsTUFBTTtnQkFFRyx3QkFBd0I7c0JBQWpDLE1BQU07Z0JBQ0csWUFBWTtzQkFBckIsTUFBTTtnQkFFRyxhQUFhO3NCQUF0QixNQUFNO2dCQUNHLGVBQWU7c0JBQXhCLE1BQU07Z0JBb01QLGVBQWU7c0JBRGQsWUFBWTt1QkFBQyxnQkFBZ0I7Z0JBTTlCLFFBQVE7c0JBRFAsWUFBWTt1QkFBQyx5QkFBeUI7Z0JBd0d2QyxpQkFBaUI7c0JBRGhCLFlBQVk7dUJBQUMsa0JBQWtCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gIENoYW5nZURldGVjdG9yUmVmLFxuICBDb21wb25lbnQsXG4gIEV2ZW50RW1pdHRlcixcbiAgSG9zdExpc3RlbmVyLFxuICBJbnB1dCxcbiAgT25EZXN0cm95LFxuICBPdXRwdXQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcbiAgREVGQVVMVF9NT0RVTEFSX0xBQkVMUyxcbiAgRm9sZGVyTm9kZSxcbiAgRm9sZGVyUmVuYW1lZFBheWxvYWQsXG4gIE1vZHVsYXJMYWJlbHMsXG59IGZyb20gJy4uL21vZHVsYXItdGFibGUtdGVtcGxhdGUubW9kZWxzJztcblxuaW50ZXJmYWNlIFJlbmRlck5vZGUge1xuICBub2RlOiBGb2xkZXJOb2RlO1xuICBkZXB0aDogbnVtYmVyO1xuICB2aXNpYmxlOiBib29sZWFuO1xuICBoYXNDaGlsZHJlbjogYm9vbGVhbjtcbn1cblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnY3FhLWZvbGRlci1zaWRlYmFyJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2ZvbGRlci1zaWRlYmFyLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbXSxcbiAgaG9zdDogeyBjbGFzczogJ2NxYS11aS1yb290JyB9LFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgRm9sZGVyU2lkZWJhckNvbXBvbmVudCBpbXBsZW1lbnRzIE9uRGVzdHJveSB7XG4gIEBJbnB1dCgpIGZvbGRlcnM6IEZvbGRlck5vZGVbXSA9IFtdO1xuICBASW5wdXQoKSBzZWxlY3RlZEZvbGRlcklkOiBudW1iZXIgfCBudWxsID0gbnVsbDtcbiAgQElucHV0KCkgZXhwYW5kZWRGb2xkZXJJZHM6IG51bWJlcltdID0gW107XG4gIEBJbnB1dCgpIHVub3JnYW5pc2VkQ291bnQ6IG51bWJlciA9IDA7XG4gIEBJbnB1dCgpIGFsbG93Q3JlYXRlOiBib29sZWFuID0gdHJ1ZTtcbiAgQElucHV0KCkgYWxsb3dSZW5hbWU6IGJvb2xlYW4gPSB0cnVlO1xuICBASW5wdXQoKSBhbGxvd0RlbGV0ZTogYm9vbGVhbiA9IHRydWU7XG4gIEBJbnB1dCgpIGFsbG93TW92ZTogYm9vbGVhbiA9IHRydWU7XG4gIEBJbnB1dCgpIGFsbG93RHVwbGljYXRlOiBib29sZWFuID0gdHJ1ZTtcbiAgQElucHV0KCkgYWxsb3dEcm9wOiBib29sZWFuID0gdHJ1ZTtcbiAgQElucHV0KCkgc2hvd0NvdW50czogYm9vbGVhbiA9IHRydWU7XG4gIEBJbnB1dCgpIGNvbGxhcHNlZDogYm9vbGVhbiA9IGZhbHNlO1xuICBASW5wdXQoKSBsYWJlbHM6IE1vZHVsYXJMYWJlbHMgPSB7IC4uLkRFRkFVTFRfTU9EVUxBUl9MQUJFTFMgfTtcblxuICBAT3V0cHV0KCkgZm9sZGVyU2VsZWN0ZWQgPSBuZXcgRXZlbnRFbWl0dGVyPG51bWJlciB8IG51bGw+KCk7XG4gIEBPdXRwdXQoKSBmb2xkZXJFeHBhbnNpb25Ub2dnbGVkID0gbmV3IEV2ZW50RW1pdHRlcjx7IGlkOiBudW1iZXI7IGV4cGFuZGVkOiBib29sZWFuIH0+KCk7XG4gIC8qKiBFbWl0dGVkIGFmdGVyIHRoZSBob3N0IGNvbXBsZXRlcyBmb2xkZXIgY3JlYXRpb24gKGUuZy4sIGlubGluZSByZW5hbWUgZmxvdykuIFJlc2VydmVkIGZvciBmdXR1cmUgdXNlLiAqL1xuICBAT3V0cHV0KCkgZm9sZGVyQ3JlYXRlZCA9IG5ldyBFdmVudEVtaXR0ZXI8eyBwYXJlbnRJZDogbnVtYmVyIHwgbnVsbDsgbmFtZTogc3RyaW5nIH0+KCk7XG4gIC8qKiBFbWl0dGVkIHdoZW4gdGhlIHVzZXIgY2xpY2tzIHRoZSBcIitcIiBoZWFkZXIgYnV0dG9uLiBIb3N0IHNob3VsZCBvcGVuIGEgY3JlYXRpb24gbW9kYWwuICovXG4gIEBPdXRwdXQoKSBmb2xkZXJDcmVhdGVSZXF1ZXN0ZWQgPSBuZXcgRXZlbnRFbWl0dGVyPHsgcGFyZW50SWQ6IG51bWJlciB8IG51bGwgfT4oKTtcbiAgLyoqIENhcnJpZXMgdGhlIGZ1bGwgcmVuYW1lZCBmb2xkZXIgbm9kZSArIHBhcmVudCBpZCBzbyBob3N0cyBjYW4gaXNzdWUgY29tcGxldGUgdXBkYXRlIHJlcXVlc3RzLiAqL1xuICBAT3V0cHV0KCkgZm9sZGVyUmVuYW1lZCA9IG5ldyBFdmVudEVtaXR0ZXI8Rm9sZGVyUmVuYW1lZFBheWxvYWQ+KCk7XG4gIEBPdXRwdXQoKSBmb2xkZXJEZWxldGVkID0gbmV3IEV2ZW50RW1pdHRlcjx7IGlkOiBudW1iZXIgfT4oKTtcbiAgLyoqIFVzZXIgcGlja2VkIFwiTW92ZSBmb2xkZXJcIiBmcm9tIHRoZSBjb250ZXh0IG1lbnUuIEhvc3Qgb3BlbnMgdGhlIGZvbGRlciBwaWNrZXIuICovXG4gIEBPdXRwdXQoKSBmb2xkZXJNb3ZlUmVxdWVzdGVkID0gbmV3IEV2ZW50RW1pdHRlcjx7IGlkOiBudW1iZXIgfT4oKTtcbiAgLyoqIFVzZXIgcGlja2VkIFwiRHVwbGljYXRlIGZvbGRlclwiIGZyb20gdGhlIGNvbnRleHQgbWVudS4gSG9zdCBjbG9uZXMgdGhlIHN1YnRyZWUgKHRlc3RzIHJlZmVyZW5jZWQsIG5vdCBjb3BpZWQpLiAqL1xuICBAT3V0cHV0KCkgZm9sZGVyRHVwbGljYXRlUmVxdWVzdGVkID0gbmV3IEV2ZW50RW1pdHRlcjx7IGlkOiBudW1iZXIgfT4oKTtcbiAgQE91dHB1dCgpIHRlc3RzRHJvcHBlZCA9IG5ldyBFdmVudEVtaXR0ZXI8eyB0ZXN0SWRzOiBBcnJheTxzdHJpbmcgfCBudW1iZXI+OyB0YXJnZXRGb2xkZXJJZDogbnVtYmVyIHwgbnVsbCB9PigpO1xuICAvKiogRmlyZXMgd2hlbiBhIGZvbGRlciByb3cgaXMgZHJvcHBlZCBvbnRvIGFub3RoZXIgZm9sZGVyIChvciB0aGUgVW5vcmdhbmlzZWQgcm93KS4gKi9cbiAgQE91dHB1dCgpIGZvbGRlckRyb3BwZWQgPSBuZXcgRXZlbnRFbWl0dGVyPHsgaWQ6IG51bWJlcjsgbmV3UGFyZW50SWQ6IG51bWJlciB8IG51bGwgfT4oKTtcbiAgQE91dHB1dCgpIGNvbGxhcHNlZENoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8Ym9vbGVhbj4oKTtcblxuICBzZWFyY2hWYWx1ZTogc3RyaW5nID0gJyc7XG4gIHJlbmFtaW5nSWQ6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICByZW5hbWVEcmFmdDogc3RyaW5nID0gJyc7XG5cbiAgLyoqIElkIG9mIHRoZSBmb2xkZXIgd2hvc2UgY29udGV4dCBtZW51IGlzIG9wZW4sIG9yIG51bGwgd2hlbiBjbG9zZWQuICovXG4gIGNvbnRleHRNZW51Rm9sZGVySWQ6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICAvKiogVmlld3BvcnQtYW5jaG9yZWQgcG9zaXRpb24gKGNsaWVudCBjb29yZGluYXRlcykgZm9yIHRoZSBmbG9hdGluZyBtZW51LiAqL1xuICBjb250ZXh0TWVudVBvc2l0aW9uOiB7IHg6IG51bWJlcjsgeTogbnVtYmVyIH0gPSB7IHg6IDAsIHk6IDAgfTtcblxuICAvKiogUGVuZGluZyBhdXRvLWV4cGFuZCB0aW1lcnMgd2hpbGUgYSBmb2xkZXIgZHJhZyBpcyBob3ZlcmVkIG92ZXIgY29sbGFwc2VkIHJvd3MuICovXG4gIHByaXZhdGUgZHJhZ0V4cGFuZFRpbWVycyA9IG5ldyBNYXA8bnVtYmVyLCBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0Pj4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSBEUkFHX0VYUEFORF9ERUxBWV9NUyA9IDYwMDtcbiAgLyoqIElkIG9mIHRoZSBmb2xkZXIgY3VycmVudGx5IGJlaW5nIGRyYWdnZWQ7IHVzZWQgdG8gc2tpcCBjeWNsZSB0YXJnZXRzIGFuZCBzb3VyY2UtaG92ZXIgZXhwYW5zaW9uLiAqL1xuICBwcml2YXRlIGFjdGl2ZUZvbGRlckRyYWdJZDogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBjZHI6IENoYW5nZURldGVjdG9yUmVmKSB7fVxuXG4gIGdldCBleHBhbmRlZFNldCgpOiBTZXQ8bnVtYmVyPiB7XG4gICAgcmV0dXJuIG5ldyBTZXQodGhpcy5leHBhbmRlZEZvbGRlcklkcyB8fCBbXSk7XG4gIH1cblxuICAvKiogRmxhdHRlbmVkLCBkZXB0aC1hd2FyZSBsaXN0IG9mIHJvd3MgdG8gcmVuZGVyIGluIG9yZGVyLiBBbmNlc3RvcnMgb2YgbWF0Y2hlcyBhcmUga2VwdC4gKi9cbiAgZ2V0IHJvd3MoKTogUmVuZGVyTm9kZVtdIHtcbiAgICBjb25zdCBzZWFyY2ggPSAodGhpcy5zZWFyY2hWYWx1ZSB8fCAnJykudHJpbSgpLnRvTG93ZXJDYXNlKCk7XG4gICAgY29uc3QgZXhwYW5kZWQgPSB0aGlzLmV4cGFuZGVkU2V0O1xuICAgIGNvbnN0IGtlZXBTZXQgPSBuZXcgU2V0PG51bWJlcj4oKTtcbiAgICBpZiAoc2VhcmNoKSB7XG4gICAgICBjb25zdCBtYXJrID0gKG5vZGVzOiBGb2xkZXJOb2RlW10sIGFuY2VzdG9yczogbnVtYmVyW10pOiB2b2lkID0+IHtcbiAgICAgICAgZm9yIChjb25zdCBuIG9mIG5vZGVzIHx8IFtdKSB7XG4gICAgICAgICAgY29uc3QgbWF0Y2hlZCA9IChuLm5hbWUgfHwgJycpLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoc2VhcmNoKTtcbiAgICAgICAgICBjb25zdCBjaGlsZEFuY2VzdG9ycyA9IFsuLi5hbmNlc3RvcnMsIG4uaWRdO1xuICAgICAgICAgIGlmIChtYXRjaGVkKSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGEgb2YgYW5jZXN0b3JzKSBrZWVwU2V0LmFkZChhKTtcbiAgICAgICAgICAgIGtlZXBTZXQuYWRkKG4uaWQpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAobi5jaGlsZHJlbj8ubGVuZ3RoKSBtYXJrKG4uY2hpbGRyZW4sIGNoaWxkQW5jZXN0b3JzKTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIG1hcmsodGhpcy5mb2xkZXJzLCBbXSk7XG4gICAgfVxuXG4gICAgY29uc3Qgb3V0OiBSZW5kZXJOb2RlW10gPSBbXTtcbiAgICBjb25zdCB3YWxrID0gKG5vZGVzOiBGb2xkZXJOb2RlW10sIGRlcHRoOiBudW1iZXIsIHBhcmVudFZpc2libGU6IGJvb2xlYW4pID0+IHtcbiAgICAgIGZvciAoY29uc3QgbiBvZiBub2RlcyB8fCBbXSkge1xuICAgICAgICBjb25zdCBzZWFyY2hWaXNpYmxlID0gIXNlYXJjaCB8fCBrZWVwU2V0LmhhcyhuLmlkKTtcbiAgICAgICAgY29uc3QgdmlzaWJsZSA9IHBhcmVudFZpc2libGUgJiYgc2VhcmNoVmlzaWJsZTtcbiAgICAgICAgY29uc3QgaGFzQ2hpbGRyZW4gPSAhIShuLmNoaWxkcmVuICYmIG4uY2hpbGRyZW4ubGVuZ3RoKTtcbiAgICAgICAgb3V0LnB1c2goeyBub2RlOiBuLCBkZXB0aCwgdmlzaWJsZSwgaGFzQ2hpbGRyZW4gfSk7XG4gICAgICAgIGNvbnN0IGV4cGFuZGVkQnlTZWFyY2ggPSAhIXNlYXJjaCAmJiBrZWVwU2V0LmhhcyhuLmlkKTtcbiAgICAgICAgY29uc3QgaXNOb2RlRXhwYW5kZWQgPSBleHBhbmRlZC5oYXMobi5pZCkgfHwgZXhwYW5kZWRCeVNlYXJjaDtcbiAgICAgICAgaWYgKGhhc0NoaWxkcmVuICYmIGlzTm9kZUV4cGFuZGVkKSB7XG4gICAgICAgICAgd2FsayhuLmNoaWxkcmVuISwgZGVwdGggKyAxLCB2aXNpYmxlKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH07XG4gICAgd2Fsayh0aGlzLmZvbGRlcnMsIDAsIHRydWUpO1xuICAgIHJldHVybiBvdXQuZmlsdGVyKHIgPT4gci52aXNpYmxlKTtcbiAgfVxuXG4gIGlzRXhwYW5kZWQoaWQ6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmV4cGFuZGVkU2V0LmhhcyhpZCk7XG4gIH1cblxuICBpc1NlbGVjdGVkKGlkOiBudW1iZXIgfCBudWxsKTogYm9vbGVhbiB7XG4gICAgaWYgKGlkID09IG51bGwgJiYgdGhpcy5zZWxlY3RlZEZvbGRlcklkID09IG51bGwpIHJldHVybiB0cnVlO1xuICAgIHJldHVybiBpZCAhPSBudWxsICYmIHRoaXMuc2VsZWN0ZWRGb2xkZXJJZCAhPSBudWxsICYmIGlkID09PSB0aGlzLnNlbGVjdGVkRm9sZGVySWQ7XG4gIH1cblxuICBvblRvZ2dsZShuOiBGb2xkZXJOb2RlLCBldmVudDogRXZlbnQpOiB2b2lkIHtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICBjb25zdCBleHBhbmRlZCA9ICF0aGlzLmlzRXhwYW5kZWQobi5pZCk7XG4gICAgdGhpcy5mb2xkZXJFeHBhbnNpb25Ub2dnbGVkLmVtaXQoeyBpZDogbi5pZCwgZXhwYW5kZWQgfSk7XG4gIH1cblxuICBvblNlbGVjdChuOiBGb2xkZXJOb2RlIHwgbnVsbCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnJlbmFtaW5nSWQgIT0gbnVsbCkgcmV0dXJuO1xuICAgIHRoaXMuZm9sZGVyU2VsZWN0ZWQuZW1pdChuID09IG51bGwgPyBudWxsIDogbi5pZCk7XG4gIH1cblxuICBvblNlbGVjdFVub3JnYW5pc2VkKCk6IHZvaWQge1xuICAgIC8vIFNlbnRpbmVsOiBob3N0cyB0aGF0IGtlZXAgYW4gXCJVbm9yZ2FuaXNlZFwiIGJ1Y2tldCBjYW4gdHJlYXQgYSBudWxsIHNlbGVjdGlvblxuICAgIC8vIGFzIFwic2hvdyB1bm9yZ2FuaXNlZFwiLiBJZiB0aGVyZSBpcyBhbHJlYWR5IGEgcm9vdCAobnVsbCkgc2VsZWN0aW9uIHBhdHRlcm4sXG4gICAgLy8gdGhpcyBtYXRjaGVzIGl0LiBIb3N0IGRlY2lkZXMgc2VtYW50aWNzLlxuICAgIHRoaXMuZm9sZGVyU2VsZWN0ZWQuZW1pdChudWxsKTtcbiAgfVxuXG4gIGJlZ2luUmVuYW1lKG46IEZvbGRlck5vZGUsIGV2ZW50OiBFdmVudCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5hbGxvd1JlbmFtZSkgcmV0dXJuO1xuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIHRoaXMucmVuYW1pbmdJZCA9IG4uaWQ7XG4gICAgdGhpcy5yZW5hbWVEcmFmdCA9IG4ubmFtZTtcbiAgICB0aGlzLmNkci5tYXJrRm9yQ2hlY2soKTtcbiAgICB0aGlzLmZvY3VzUmVuYW1lSW5wdXQobi5pZCk7XG4gIH1cblxuICBjb21taXRSZW5hbWUobjogRm9sZGVyTm9kZSk6IHZvaWQge1xuICAgIGNvbnN0IG5hbWUgPSAodGhpcy5yZW5hbWVEcmFmdCB8fCAnJykudHJpbSgpO1xuICAgIGlmIChuYW1lICYmIG5hbWUgIT09IG4ubmFtZSkge1xuICAgICAgLy8gRW1pdCB0aGUgZnVsbCByZW5hbWVkIG5vZGUgKHdpdGggdGhlIG5ldyBuYW1lKSArIHBhcmVudCBjb250ZXh0IHNvIGhvc3RzIGNhblxuICAgICAgLy8gY2FsbCBgUFVUIC90ZXN0X2Nhc2VfZm9sZGVycy97aWR9YCB3aXRob3V0IHJlLXJlc29sdmluZyBmcm9tIHRoZWlyIGNhY2hlZCB0cmVlLlxuICAgICAgY29uc3QgZm9sZGVyOiBGb2xkZXJOb2RlID0geyAuLi5uLCBuYW1lIH07XG4gICAgICB0aGlzLmZvbGRlclJlbmFtZWQuZW1pdCh7XG4gICAgICAgIGZvbGRlcixcbiAgICAgICAgcGFyZW50SWQ6IHRoaXMucGFyZW50SWRPZihuLmlkKSxcbiAgICAgICAgcHJldmlvdXNOYW1lOiBuLm5hbWUsXG4gICAgICB9KTtcbiAgICB9XG4gICAgdGhpcy5jYW5jZWxSZW5hbWUoKTtcbiAgfVxuXG4gIGNhbmNlbFJlbmFtZSgpOiB2b2lkIHtcbiAgICB0aGlzLnJlbmFtaW5nSWQgPSBudWxsO1xuICAgIHRoaXMucmVuYW1lRHJhZnQgPSAnJztcbiAgICB0aGlzLmNkci5tYXJrRm9yQ2hlY2soKTtcbiAgfVxuXG4gIG9uUmVuYW1lS2V5KGV2ZW50OiBLZXlib2FyZEV2ZW50LCBuOiBGb2xkZXJOb2RlKTogdm9pZCB7XG4gICAgLy8gU3RvcCBidWJibGluZyBzbyB0aGUgcm93J3Mga2V5ZG93biBoYW5kbGVyICh3aGljaCB0cmVhdHMgU3BhY2UvRW50ZXIvQXJyb3dzIGFzXG4gICAgLy8gc2VsZWN0aW9uIC8gdHJlZSBuYXZpZ2F0aW9uKSBjYW4ndCBzd2FsbG93IGNoYXJhY3RlcnMgdHlwZWQgaW50byB0aGUgcmVuYW1lIGlucHV0LlxuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIGlmIChldmVudC5rZXkgPT09ICdFbnRlcicpIHtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICB0aGlzLmNvbW1pdFJlbmFtZShuKTtcbiAgICB9IGVsc2UgaWYgKGV2ZW50LmtleSA9PT0gJ0VzY2FwZScpIHtcbiAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICB0aGlzLmNhbmNlbFJlbmFtZSgpO1xuICAgIH1cbiAgfVxuXG4gIGRlbGV0ZUZvbGRlcihuOiBGb2xkZXJOb2RlLCBldmVudDogRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuYWxsb3dEZWxldGUpIHJldHVybjtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICB0aGlzLmZvbGRlckRlbGV0ZWQuZW1pdCh7IGlkOiBuLmlkIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIE9wZW5zIHRoZSBmb2xkZXIgY29udGV4dCBtZW51IGFuY2hvcmVkIGF0IHRoZSBnaXZlbiBjbGllbnQgY29vcmRpbmF0ZXMuXG4gICAqIENhbGxlZCBmcm9tIGJvdGggKGNvbnRleHRtZW51KSBvbiB0aGUgcm93IGFuZCAoY2xpY2spIG9uIHRoZSBlbGxpcHNpcyBidXR0b24uXG4gICAqIE1lbnUgdmlzaWJpbGl0eSBpcyBjbGFtcGVkIGluIHRoZSB0ZW1wbGF0ZSB2aWEgQ1NTIHRyYW5zZm9ybXMgc28gaXQgbmV2ZXIgbGVhdmVzIHRoZSB2aWV3cG9ydC5cbiAgICovXG4gIG9wZW5Db250ZXh0TWVudShuOiBGb2xkZXJOb2RlLCBldmVudDogTW91c2VFdmVudCk6IHZvaWQge1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgaWYgKCF0aGlzLmhhc0FueUNvbnRleHRBY3Rpb24pIHJldHVybjtcbiAgICB0aGlzLmNvbnRleHRNZW51Rm9sZGVySWQgPSBuLmlkO1xuICAgIHRoaXMuY29udGV4dE1lbnVQb3NpdGlvbiA9IHsgeDogZXZlbnQuY2xpZW50WCwgeTogZXZlbnQuY2xpZW50WSB9O1xuICAgIHRoaXMuY2RyLm1hcmtGb3JDaGVjaygpO1xuICB9XG5cbiAgY2xvc2VDb250ZXh0TWVudSgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5jb250ZXh0TWVudUZvbGRlcklkID09IG51bGwpIHJldHVybjtcbiAgICB0aGlzLmNvbnRleHRNZW51Rm9sZGVySWQgPSBudWxsO1xuICAgIHRoaXMuY2RyLm1hcmtGb3JDaGVjaygpO1xuICB9XG5cbiAgLyoqIFRydWUgd2hlbiBhdCBsZWFzdCBvbmUgbWVudSBlbnRyeSBpcyBlbmFibGVkIOKAlCBvdGhlcndpc2UgdGhlIHRyaWdnZXIgaXMgc3VwcHJlc3NlZCBlbnRpcmVseS4gKi9cbiAgZ2V0IGhhc0FueUNvbnRleHRBY3Rpb24oKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuYWxsb3dDcmVhdGUgfHwgdGhpcy5hbGxvd1JlbmFtZSB8fCB0aGlzLmFsbG93TW92ZSB8fCB0aGlzLmFsbG93RHVwbGljYXRlIHx8IHRoaXMuYWxsb3dEZWxldGU7XG4gIH1cblxuICBvbkNvbnRleHRDcmVhdGVTdWJmb2xkZXIobjogRm9sZGVyTm9kZSk6IHZvaWQge1xuICAgIHRoaXMuY2xvc2VDb250ZXh0TWVudSgpO1xuICAgIGlmICghdGhpcy5hbGxvd0NyZWF0ZSkgcmV0dXJuO1xuICAgIHRoaXMuZm9sZGVyQ3JlYXRlUmVxdWVzdGVkLmVtaXQoeyBwYXJlbnRJZDogbi5pZCB9KTtcbiAgfVxuXG4gIG9uQ29udGV4dFJlbmFtZShuOiBGb2xkZXJOb2RlKTogdm9pZCB7XG4gICAgdGhpcy5jbG9zZUNvbnRleHRNZW51KCk7XG4gICAgaWYgKCF0aGlzLmFsbG93UmVuYW1lKSByZXR1cm47XG4gICAgdGhpcy5yZW5hbWluZ0lkID0gbi5pZDtcbiAgICB0aGlzLnJlbmFtZURyYWZ0ID0gbi5uYW1lO1xuICAgIHRoaXMuY2RyLm1hcmtGb3JDaGVjaygpO1xuICAgIHRoaXMuZm9jdXNSZW5hbWVJbnB1dChuLmlkKTtcbiAgfVxuXG4gIG9uQ29udGV4dE1vdmUobjogRm9sZGVyTm9kZSk6IHZvaWQge1xuICAgIHRoaXMuY2xvc2VDb250ZXh0TWVudSgpO1xuICAgIGlmICghdGhpcy5hbGxvd01vdmUpIHJldHVybjtcbiAgICB0aGlzLmZvbGRlck1vdmVSZXF1ZXN0ZWQuZW1pdCh7IGlkOiBuLmlkIH0pO1xuICB9XG5cbiAgb25Db250ZXh0RHVwbGljYXRlKG46IEZvbGRlck5vZGUpOiB2b2lkIHtcbiAgICB0aGlzLmNsb3NlQ29udGV4dE1lbnUoKTtcbiAgICBpZiAoIXRoaXMuYWxsb3dEdXBsaWNhdGUpIHJldHVybjtcbiAgICB0aGlzLmZvbGRlckR1cGxpY2F0ZVJlcXVlc3RlZC5lbWl0KHsgaWQ6IG4uaWQgfSk7XG4gIH1cblxuICBvbkNvbnRleHREZWxldGUobjogRm9sZGVyTm9kZSk6IHZvaWQge1xuICAgIHRoaXMuY2xvc2VDb250ZXh0TWVudSgpO1xuICAgIGlmICghdGhpcy5hbGxvd0RlbGV0ZSkgcmV0dXJuO1xuICAgIHRoaXMuZm9sZGVyRGVsZXRlZC5lbWl0KHsgaWQ6IG4uaWQgfSk7XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdkb2N1bWVudDpjbGljaycpXG4gIG9uRG9jdW1lbnRDbGljaygpOiB2b2lkIHtcbiAgICB0aGlzLmNsb3NlQ29udGV4dE1lbnUoKTtcbiAgfVxuXG4gIEBIb3N0TGlzdGVuZXIoJ2RvY3VtZW50OmtleWRvd24uZXNjYXBlJylcbiAgb25Fc2NhcGUoKTogdm9pZCB7XG4gICAgdGhpcy5jbG9zZUNvbnRleHRNZW51KCk7XG4gIH1cblxuICAvKiogVGVtcGxhdGUgaGVscGVyOiByZXNvbHZlIGEgZm9sZGVyIG5vZGUgYnkgaWQgZnJvbSB0aGUgaW5wdXQgdHJlZS4gKi9cbiAgZm9sZGVyQnlJZChpZDogbnVtYmVyIHwgbnVsbCk6IEZvbGRlck5vZGUgfCBudWxsIHtcbiAgICBpZiAoaWQgPT0gbnVsbCkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgZmluZCA9IChub2RlczogRm9sZGVyTm9kZVtdKTogRm9sZGVyTm9kZSB8IG51bGwgPT4ge1xuICAgICAgZm9yIChjb25zdCBuIG9mIG5vZGVzIHx8IFtdKSB7XG4gICAgICAgIGlmIChuLmlkID09PSBpZCkgcmV0dXJuIG47XG4gICAgICAgIGlmIChuLmNoaWxkcmVuPy5sZW5ndGgpIHtcbiAgICAgICAgICBjb25zdCBoaXQgPSBmaW5kKG4uY2hpbGRyZW4pO1xuICAgICAgICAgIGlmIChoaXQpIHJldHVybiBoaXQ7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH07XG4gICAgcmV0dXJuIGZpbmQodGhpcy5mb2xkZXJzKTtcbiAgfVxuXG4gIC8qKiBSZXR1cm5zIHRoZSBwYXJlbnQgaWQgb2YgYGlkYCwgb3IgbnVsbCB3aGVuIGBpZGAgaXMgYSByb290IGZvbGRlciAvIG5vdCBmb3VuZC4gKi9cbiAgcHJpdmF0ZSBwYXJlbnRJZE9mKGlkOiBudW1iZXIpOiBudW1iZXIgfCBudWxsIHtcbiAgICBsZXQgcmVzdWx0OiBudW1iZXIgfCBudWxsID0gbnVsbDtcbiAgICBjb25zdCB3YWxrID0gKG5vZGVzOiBGb2xkZXJOb2RlW10sIHBhcmVudElkOiBudW1iZXIgfCBudWxsKTogYm9vbGVhbiA9PiB7XG4gICAgICBmb3IgKGNvbnN0IG4gb2Ygbm9kZXMgfHwgW10pIHtcbiAgICAgICAgaWYgKG4uaWQgPT09IGlkKSB7XG4gICAgICAgICAgcmVzdWx0ID0gcGFyZW50SWQ7XG4gICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG4uY2hpbGRyZW4/Lmxlbmd0aCAmJiB3YWxrKG4uY2hpbGRyZW4sIG4uaWQpKSByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9O1xuICAgIHdhbGsodGhpcy5mb2xkZXJzLCBudWxsKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcmVxdWVzdENyZWF0ZShwYXJlbnRJZDogbnVtYmVyIHwgbnVsbCA9IG51bGwpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuYWxsb3dDcmVhdGUpIHJldHVybjtcbiAgICB0aGlzLmZvbGRlckNyZWF0ZVJlcXVlc3RlZC5lbWl0KHsgcGFyZW50SWQgfSk7XG4gIH1cblxuICB0b2dnbGVQYW5lbCgpOiB2b2lkIHtcbiAgICB0aGlzLmNvbGxhcHNlZCA9ICF0aGlzLmNvbGxhcHNlZDtcbiAgICB0aGlzLmNvbGxhcHNlZENoYW5nZS5lbWl0KHRoaXMuY29sbGFwc2VkKTtcbiAgfVxuXG4gIHJvd0Ryb3BwZWQodGFyZ2V0SWQ6IG51bWJlciB8IG51bGwsIGV2ZW50OiB7IHRlc3RJZHM6IEFycmF5PHN0cmluZyB8IG51bWJlcj47IHRhcmdldEZvbGRlcklkOiBudW1iZXIgfCBudWxsIH0pOiB2b2lkIHtcbiAgICAvLyBEaXJlY3RpdmUgYWxyZWFkeSBlbWl0cyB3aXRoIHRoZSBib3VuZCBmb2xkZXIgaWQsIGJ1dCBub3JtYWxpc2UgaW4gY2FzZSBjYWxsZXJzXG4gICAgLy8gd2lyZSByYXcgZXZlbnRzLlxuICAgIHRoaXMudGVzdHNEcm9wcGVkLmVtaXQoeyB0ZXN0SWRzOiBldmVudC50ZXN0SWRzLCB0YXJnZXRGb2xkZXJJZDogdGFyZ2V0SWQgfSk7XG4gIH1cblxuICAvKipcbiAgICogRHJvcCB0YXJnZXQgcmVjZWl2ZWQgYSBmb2xkZXIgZHJhZy4gVmFsaWRhdGUgYWdhaW5zdCBzZWxmL2Rlc2NlbmRhbnQgY3ljbGVzIGFuZFxuICAgKiBlbWl0IGBmb2xkZXJEcm9wcGVkYCBpZiB0aGUgbW92ZSBpcyBsZWdhbC4gQ2FsbGVkIGZyb20gdGhlIGRpcmVjdGl2ZSdzXG4gICAqIGAoZm9sZGVyRHJvcHBlZClgIG91dHB1dCBvbiBldmVyeSByb3cgYW5kIG9uIHRoZSBVbm9yZ2FuaXNlZCByb3cuXG4gICAqL1xuICBvbkZvbGRlclJvd0Ryb3BwZWQodGFyZ2V0SWQ6IG51bWJlciB8IG51bGwsIGV2ZW50OiB7IGZvbGRlcklkOiBudW1iZXI7IHRhcmdldEZvbGRlcklkOiBudW1iZXIgfCBudWxsIH0pOiB2b2lkIHtcbiAgICB0aGlzLmNsZWFyQWxsRXhwYW5kVGltZXJzKCk7XG4gICAgdGhpcy5hY3RpdmVGb2xkZXJEcmFnSWQgPSBudWxsO1xuICAgIGNvbnN0IHNvdXJjZUlkID0gZXZlbnQuZm9sZGVySWQ7XG4gICAgaWYgKHRhcmdldElkICE9IG51bGwgJiYgdGFyZ2V0SWQgPT09IHNvdXJjZUlkKSByZXR1cm47XG4gICAgaWYgKHRhcmdldElkICE9IG51bGwgJiYgdGhpcy5pc0Rlc2NlbmRhbnRPZihzb3VyY2VJZCwgdGFyZ2V0SWQpKSByZXR1cm47XG4gICAgdGhpcy5mb2xkZXJEcm9wcGVkLmVtaXQoeyBpZDogc291cmNlSWQsIG5ld1BhcmVudElkOiB0YXJnZXRJZCB9KTtcbiAgfVxuXG4gIC8qKiBSZW1lbWJlciB0aGUgZHJhZ2dlZCBmb2xkZXIgc28gd2UgY2FuIGJsb2NrIHNlbGYtaG92ZXIgZXhwYW5zaW9uIGFuZCBjeWNsZSBkcm9wcy4gKi9cbiAgb25Gb2xkZXJEcmFnU3RhcnQobjogRm9sZGVyTm9kZSk6IHZvaWQge1xuICAgIHRoaXMuYWN0aXZlRm9sZGVyRHJhZ0lkID0gbi5pZDtcbiAgfVxuXG4gIG9uRm9sZGVyRHJhZ0VuZCgpOiB2b2lkIHtcbiAgICB0aGlzLmFjdGl2ZUZvbGRlckRyYWdJZCA9IG51bGw7XG4gICAgdGhpcy5jbGVhckFsbEV4cGFuZFRpbWVycygpO1xuICB9XG5cbiAgLyoqIFNjaGVkdWxlIGFuIGF1dG8tZXhwYW5kIGZvciBhIGNvbGxhcHNlZCBmb2xkZXIgdGhhdCBoYXMgY2hpbGRyZW4gZHVyaW5nIGRyYWctaG92ZXIuICovXG4gIG9uRm9sZGVyUm93RHJhZ092ZXIocm93OiBSZW5kZXJOb2RlKTogdm9pZCB7XG4gICAgY29uc3QgaWQgPSByb3cubm9kZS5pZDtcbiAgICBpZiAoIXJvdy5oYXNDaGlsZHJlbikgcmV0dXJuO1xuICAgIGlmICh0aGlzLmlzRXhwYW5kZWQoaWQpKSByZXR1cm47XG4gICAgaWYgKHRoaXMuYWN0aXZlRm9sZGVyRHJhZ0lkID09PSBpZCkgcmV0dXJuOyAvLyBkb24ndCBleHBhbmQgdGhlIHNvdXJjZVxuICAgIGlmICh0aGlzLmRyYWdFeHBhbmRUaW1lcnMuaGFzKGlkKSkgcmV0dXJuO1xuICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB0aGlzLmRyYWdFeHBhbmRUaW1lcnMuZGVsZXRlKGlkKTtcbiAgICAgIGlmICghdGhpcy5pc0V4cGFuZGVkKGlkKSkge1xuICAgICAgICB0aGlzLmZvbGRlckV4cGFuc2lvblRvZ2dsZWQuZW1pdCh7IGlkLCBleHBhbmRlZDogdHJ1ZSB9KTtcbiAgICAgIH1cbiAgICB9LCB0aGlzLkRSQUdfRVhQQU5EX0RFTEFZX01TKTtcbiAgICB0aGlzLmRyYWdFeHBhbmRUaW1lcnMuc2V0KGlkLCB0aW1lcik7XG4gIH1cblxuICBvbkZvbGRlclJvd0RyYWdMZWF2ZShyb3c6IFJlbmRlck5vZGUpOiB2b2lkIHtcbiAgICBjb25zdCBpZCA9IHJvdy5ub2RlLmlkO1xuICAgIGNvbnN0IHRpbWVyID0gdGhpcy5kcmFnRXhwYW5kVGltZXJzLmdldChpZCk7XG4gICAgaWYgKHRpbWVyICE9IG51bGwpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gICAgICB0aGlzLmRyYWdFeHBhbmRUaW1lcnMuZGVsZXRlKGlkKTtcbiAgICB9XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdkb2N1bWVudDpkcmFnZW5kJylcbiAgb25Eb2N1bWVudERyYWdFbmQoKTogdm9pZCB7XG4gICAgdGhpcy5vbkZvbGRlckRyYWdFbmQoKTtcbiAgfVxuXG4gIC8qKiBUcnVlIHdoZW4gYGNhbmRpZGF0ZUlkYCBpcyBgc291cmNlSWRgIGl0c2VsZiBvciBhbnkgZGVzY2VuZGFudCBvZiBpdC4gKi9cbiAgcHJpdmF0ZSBpc0Rlc2NlbmRhbnRPZihzb3VyY2VJZDogbnVtYmVyLCBjYW5kaWRhdGVJZDogbnVtYmVyKTogYm9vbGVhbiB7XG4gICAgY29uc3Qgc291cmNlID0gdGhpcy5mb2xkZXJCeUlkKHNvdXJjZUlkKTtcbiAgICBpZiAoIXNvdXJjZSkgcmV0dXJuIGZhbHNlO1xuICAgIGNvbnN0IHN0YWNrOiBGb2xkZXJOb2RlW10gPSBbc291cmNlXTtcbiAgICB3aGlsZSAoc3RhY2subGVuZ3RoKSB7XG4gICAgICBjb25zdCBuID0gc3RhY2sucG9wKCkhO1xuICAgICAgaWYgKG4uaWQgPT09IGNhbmRpZGF0ZUlkKSByZXR1cm4gdHJ1ZTtcbiAgICAgIGlmIChuLmNoaWxkcmVuPy5sZW5ndGgpIHN0YWNrLnB1c2goLi4ubi5jaGlsZHJlbik7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHByaXZhdGUgY2xlYXJBbGxFeHBhbmRUaW1lcnMoKTogdm9pZCB7XG4gICAgdGhpcy5kcmFnRXhwYW5kVGltZXJzLmZvckVhY2godCA9PiBjbGVhclRpbWVvdXQodCkpO1xuICAgIHRoaXMuZHJhZ0V4cGFuZFRpbWVycy5jbGVhcigpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5jbGVhckFsbEV4cGFuZFRpbWVycygpO1xuICB9XG5cbiAgdHJhY2tCeVJvdyA9IChfOiBudW1iZXIsIHJvdzogUmVuZGVyTm9kZSkgPT4gcm93Lm5vZGUuaWQ7XG5cbiAgb25Sb3dLZXlkb3duKGV2ZW50OiBLZXlib2FyZEV2ZW50LCByb3c6IFJlbmRlck5vZGUsIGluZGV4OiBudW1iZXIpOiB2b2lkIHtcbiAgICBjb25zdCByb3dzID0gdGhpcy5yb3dzO1xuICAgIHN3aXRjaCAoZXZlbnQua2V5KSB7XG4gICAgICBjYXNlICdBcnJvd0Rvd24nOiB7XG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgIGNvbnN0IG5leHQgPSByb3dzW2luZGV4ICsgMV07XG4gICAgICAgIGlmIChuZXh0KSB0aGlzLmZvY3VzUm93RWxlbWVudChuZXh0Lm5vZGUuaWQpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGNhc2UgJ0Fycm93VXAnOiB7XG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgIGNvbnN0IHByZXYgPSByb3dzW2luZGV4IC0gMV07XG4gICAgICAgIGlmIChwcmV2KSB0aGlzLmZvY3VzUm93RWxlbWVudChwcmV2Lm5vZGUuaWQpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGNhc2UgJ0Fycm93UmlnaHQnOiB7XG4gICAgICAgIGlmIChyb3cuaGFzQ2hpbGRyZW4gJiYgIXRoaXMuaXNFeHBhbmRlZChyb3cubm9kZS5pZCkpIHtcbiAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgIHRoaXMuZm9sZGVyRXhwYW5zaW9uVG9nZ2xlZC5lbWl0KHsgaWQ6IHJvdy5ub2RlLmlkLCBleHBhbmRlZDogdHJ1ZSB9KTtcbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIGNhc2UgJ0Fycm93TGVmdCc6IHtcbiAgICAgICAgaWYgKHJvdy5oYXNDaGlsZHJlbiAmJiB0aGlzLmlzRXhwYW5kZWQocm93Lm5vZGUuaWQpKSB7XG4gICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICB0aGlzLmZvbGRlckV4cGFuc2lvblRvZ2dsZWQuZW1pdCh7IGlkOiByb3cubm9kZS5pZCwgZXhwYW5kZWQ6IGZhbHNlIH0pO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgY2FzZSAnRW50ZXInOlxuICAgICAgY2FzZSAnICc6IHtcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgdGhpcy5vblNlbGVjdChyb3cubm9kZSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZm9jdXNSb3dFbGVtZW50KGlkOiBudW1iZXIpOiB2b2lkIHtcbiAgICBxdWV1ZU1pY3JvdGFzaygoKSA9PiB7XG4gICAgICBjb25zdCBlbCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3I8SFRNTEVsZW1lbnQ+KGBbZGF0YS1mb2xkZXItcm93LWlkPVwiJHtpZH1cIl1gKTtcbiAgICAgIGVsPy5mb2N1cygpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEZvY3VzZXMgdGhlIGlubGluZSByZW5hbWUgaW5wdXQgZm9yIHRoZSBnaXZlbiBmb2xkZXIuIFVzZXMgYHNldFRpbWVvdXRgIChub3RcbiAgICogbWljcm90YXNrKSBiZWNhdXNlIHRoZSBpbnB1dCBpcyBnYXRlZCBieSBgKm5nSWY9XCJyZW5hbWluZ0lkID09PSAuLi5cImAgYW5kXG4gICAqIG11c3Qgc3Vydml2ZSB0aGUgbmV4dCBPblB1c2ggY2hhbmdlLWRldGVjdGlvbiBjeWNsZSBiZWZvcmUgaXQncyBpbiB0aGUgRE9NLlxuICAgKi9cbiAgcHJpdmF0ZSBmb2N1c1JlbmFtZUlucHV0KGlkOiBudW1iZXIpOiB2b2lkIHtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGNvbnN0IGVsID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcjxIVE1MSW5wdXRFbGVtZW50PihgW2RhdGEtZm9sZGVyLXJlbmFtZS1pbnB1dD1cIiR7aWR9XCJdYCk7XG4gICAgICBpZiAoIWVsKSByZXR1cm47XG4gICAgICBlbC5mb2N1cygpO1xuICAgICAgZWwuc2VsZWN0KCk7XG4gICAgfSwgMCk7XG4gIH1cbn1cbiIsIjwhLS0gUmV1c2FibGUgZm9sZGVyIGljb24uIFJlbmRlciB2aWEgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cImZvbGRlckljb247IGNvbnRleHQ6IHsgY29sb3I6IG5vZGUuY29sb3IgfVwiPjwvbmctY29udGFpbmVyPi4gLS0+XG48bmctdGVtcGxhdGUgI2ZvbGRlckljb24gbGV0LWNvbG9yPVwiY29sb3JcIj5cbiAgPHNwYW4gY2xhc3M9XCJjcWEtaW5saW5lLWZsZXggY3FhLWl0ZW1zLWNlbnRlciBjcWEtanVzdGlmeS1jZW50ZXIgY3FhLXctNCBjcWEtaC00IGNxYS1zaHJpbmstMFwiPlxuICAgIDxzdmcgd2lkdGg9XCIxM1wiIGhlaWdodD1cIjEyXCIgdmlld0JveD1cIjAgMCAxMyAxMlwiIGZpbGw9XCJub25lXCIgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiIGFyaWEtaGlkZGVuPVwidHJ1ZVwiPlxuICAgICAgPHBhdGggZD1cIk0xMi4xOTE2IDkuODU4MjRDMTIuMTkxNiAxMC4xNjc3IDEyLjA2ODcgMTAuNDY0NCAxMS44NDk5IDEwLjY4MzJDMTEuNjMxMSAxMC45MDIgMTEuMzM0MyAxMS4wMjQ5IDExLjAyNDkgMTEuMDI0OUgxLjY5MTU3QzEuMzgyMTUgMTEuMDI0OSAxLjA4NTQgMTAuOTAyIDAuODY2NjExIDEwLjY4MzJDMC42NDc4MTkgMTAuNDY0NCAwLjUyNDkwMiAxMC4xNjc3IDAuNTI0OTAyIDkuODU4MjRWMS42OTE1N0MwLjUyNDkwMiAxLjM4MjE1IDAuNjQ3ODE5IDEuMDg1NCAwLjg2NjYxMSAwLjg2NjYxMUMxLjA4NTQgMC42NDc4MTkgMS4zODIxNSAwLjUyNDkwMiAxLjY5MTU3IDAuNTI0OTAySDQuNjA4MjRMNS43NzQ5IDIuMjc0OUgxMS4wMjQ5QzExLjMzNDMgMi4yNzQ5IDExLjYzMTEgMi4zOTc4MiAxMS44NDk5IDIuNjE2NjFDMTIuMDY4NyAyLjgzNTQgMTIuMTkxNiAzLjEzMjE1IDEyLjE5MTYgMy40NDE1N1Y5Ljg1ODI0WlwiIFthdHRyLnN0cm9rZV09XCJjb2xvciB8fCAnIzk5OTk5RSdcIiBzdHJva2Utd2lkdGg9XCIxLjA1XCIgc3Ryb2tlLWxpbmVjYXA9XCJyb3VuZFwiIHN0cm9rZS1saW5lam9pbj1cInJvdW5kXCIvPlxuICAgIDwvc3ZnPlxuICA8L3NwYW4+XG48L25nLXRlbXBsYXRlPlxuXG48YXNpZGVcbiAgY2xhc3M9XCJjcWEtZmxleCBjcWEtZmxleC1jb2wgY3FhLWJnLXdoaXRlIGNxYS1ib3JkZXIgY3FhLWJvcmRlci1uZXV0cmFsLTIwMCBjcWEtcm91bmRlZC1sZyBjcWEtaC1mdWxsIGNxYS1taW4taC0wXCJcbiAgW2NsYXNzLmNxYS13LVsyNDBweF1dPVwiIWNvbGxhcHNlZFwiXG4gIFtjbGFzcy5jcWEtdy1bNDhweF1dPVwiY29sbGFwc2VkXCJcbiAgc3R5bGU9XCJ0cmFuc2l0aW9uOiB3aWR0aCAxNTBtcyBlYXNlO1wiXG4+XG4gIDwhLS0gSGVhZGVyIC0tPlxuICA8ZGl2IGNsYXNzPVwiY3FhLWZsZXggY3FhLWl0ZW1zLWNlbnRlciBjcWEtanVzdGlmeS1iZXR3ZWVuIGNxYS1weC0zIGNxYS1weS0zXCI+XG4gICAgPHNwYW4gKm5nSWY9XCIhY29sbGFwc2VkXCIgY2xhc3M9XCJjcWEtdGV4dC1zbSBjcWEtZm9udC1zZW1pYm9sZCBjcWEtdGV4dC1uZXV0cmFsLTkwMFwiPlxuICAgICAge3sgbGFiZWxzLmZvbGRlcnMgfX1cbiAgICA8L3NwYW4+XG4gICAgPGRpdiBjbGFzcz1cImNxYS1mbGV4IGNxYS1pdGVtcy1jZW50ZXIgY3FhLWdhcC0xXCIgW2NsYXNzLmNxYS13LWZ1bGxdPVwiY29sbGFwc2VkXCIgW2NsYXNzLmNxYS1qdXN0aWZ5LWNlbnRlcl09XCJjb2xsYXBzZWRcIj5cbiAgICAgIDxidXR0b25cbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwiY3FhLXAtMSBjcWEtcm91bmRlZCBob3ZlcjpjcWEtYmctbmV1dHJhbC0xMDAgY3FhLXRleHQtbmV1dHJhbC01MDBcIlxuICAgICAgICAoY2xpY2spPVwidG9nZ2xlUGFuZWwoKVwiXG4gICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiY29sbGFwc2VkID8gJ0V4cGFuZCBmb2xkZXJzIHBhbmVsJyA6ICdDb2xsYXBzZSBmb2xkZXJzIHBhbmVsJ1wiXG4gICAgICA+XG4gICAgICAgIDxtYXQtaWNvbiBzdHlsZT1cImZvbnQtc2l6ZToxOHB4O3dpZHRoOjE4cHg7aGVpZ2h0OjE4cHhcIj5cbiAgICAgICAgICB7eyBjb2xsYXBzZWQgPyAnY2hldnJvbl9yaWdodCcgOiAna2V5Ym9hcmRfZG91YmxlX2Fycm93X2xlZnQnIH19XG4gICAgICAgIDwvbWF0LWljb24+XG4gICAgICA8L2J1dHRvbj5cbiAgICAgIDxidXR0b25cbiAgICAgICAgKm5nSWY9XCIhY29sbGFwc2VkICYmIGFsbG93Q3JlYXRlXCJcbiAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgIGNsYXNzPVwiY3FhLXAtMSBjcWEtcm91bmRlZCBob3ZlcjpjcWEtYmctbmV1dHJhbC0xMDAgY3FhLXRleHQtbmV1dHJhbC01MDBcIlxuICAgICAgICAoY2xpY2spPVwicmVxdWVzdENyZWF0ZShudWxsKVwiXG4gICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwibGFiZWxzLm5ld0ZvbGRlclwiXG4gICAgICAgIFt0aXRsZV09XCJsYWJlbHMubmV3Rm9sZGVyXCJcbiAgICAgID5cbiAgICAgICAgPG1hdC1pY29uIHN0eWxlPVwiZm9udC1zaXplOjE4cHg7d2lkdGg6MThweDtoZWlnaHQ6MThweFwiPmFkZDwvbWF0LWljb24+XG4gICAgICA8L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIiFjb2xsYXBzZWRcIj5cbiAgICA8IS0tIFNlYXJjaCAtLT5cbiAgICA8ZGl2IGNsYXNzPVwiY3FhLXB4LTMgY3FhLXBiLTJcIj5cbiAgICAgIDxjcWEtc2VhcmNoLWJhclxuICAgICAgICBzaXplPVwic21cIlxuICAgICAgICBbZnVsbFdpZHRoXT1cInRydWVcIlxuICAgICAgICBbdmFsdWVdPVwic2VhcmNoVmFsdWVcIlxuICAgICAgICBbcGxhY2Vob2xkZXJdPVwibGFiZWxzLnNlYXJjaEZvbGRlcnNQbGFjZWhvbGRlclwiXG4gICAgICAgIFtzaG93Q2xlYXJdPVwidHJ1ZVwiXG4gICAgICAgICh2YWx1ZUNoYW5nZSk9XCJzZWFyY2hWYWx1ZSA9ICRldmVudFwiXG4gICAgICAgIChjbGVhcmVkKT1cInNlYXJjaFZhbHVlID0gJydcIlxuICAgICAgPjwvY3FhLXNlYXJjaC1iYXI+XG4gICAgPC9kaXY+XG5cbiAgICA8IS0tIFRyZWUgKGZvbGRlcnMgKyBVbm9yZ2FuaXNlZCB0YWIpIC0tPlxuICAgIDxkaXYgcm9sZT1cInRyZWVcIiBjbGFzcz1cImNxYS1mbGV4LTEgY3FhLW92ZXJmbG93LXktYXV0byBjcWEtcHktMVwiPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdGb3I9XCJsZXQgcm93IG9mIHJvd3M7IGxldCBpID0gaW5kZXg7IHRyYWNrQnk6IHRyYWNrQnlSb3dcIj5cbiAgICAgICAgPGRpdlxuICAgICAgICAgIHJvbGU9XCJ0cmVlaXRlbVwiXG4gICAgICAgICAgdGFiaW5kZXg9XCIwXCJcbiAgICAgICAgICBbYXR0ci5hcmlhLWV4cGFuZGVkXT1cInJvdy5oYXNDaGlsZHJlbiA/IGlzRXhwYW5kZWQocm93Lm5vZGUuaWQpIDogbnVsbFwiXG4gICAgICAgICAgW2F0dHIuYXJpYS1zZWxlY3RlZF09XCJpc1NlbGVjdGVkKHJvdy5ub2RlLmlkKVwiXG4gICAgICAgICAgW2F0dHIuZGF0YS1mb2xkZXItcm93LWlkXT1cInJvdy5ub2RlLmlkXCJcbiAgICAgICAgICBbY3FhRm9sZGVyRHJvcF09XCJyb3cubm9kZS5pZFwiXG4gICAgICAgICAgW2Ryb3BFbmFibGVkXT1cImFsbG93RHJvcFwiXG4gICAgICAgICAgKHRlc3RzRHJvcHBlZCk9XCJyb3dEcm9wcGVkKHJvdy5ub2RlLmlkLCAkZXZlbnQpXCJcbiAgICAgICAgICAoZm9sZGVyRHJvcHBlZCk9XCJvbkZvbGRlclJvd0Ryb3BwZWQocm93Lm5vZGUuaWQsICRldmVudClcIlxuICAgICAgICAgIFtjcWFGb2xkZXJEcmFnXT1cInJvdy5ub2RlLmlkXCJcbiAgICAgICAgICBbZHJhZ0VuYWJsZWRdPVwiYWxsb3dEcm9wXCJcbiAgICAgICAgICAoZHJhZ3N0YXJ0KT1cIm9uRm9sZGVyRHJhZ1N0YXJ0KHJvdy5ub2RlKVwiXG4gICAgICAgICAgKGRyYWdlbmQpPVwib25Gb2xkZXJEcmFnRW5kKClcIlxuICAgICAgICAgIChkcmFnb3Zlcik9XCJvbkZvbGRlclJvd0RyYWdPdmVyKHJvdylcIlxuICAgICAgICAgIChkcmFnbGVhdmUpPVwib25Gb2xkZXJSb3dEcmFnTGVhdmUocm93KVwiXG4gICAgICAgICAgKGNsaWNrKT1cIm9uU2VsZWN0KHJvdy5ub2RlKVwiXG4gICAgICAgICAgKGNvbnRleHRtZW51KT1cIm9wZW5Db250ZXh0TWVudShyb3cubm9kZSwgJGV2ZW50KVwiXG4gICAgICAgICAgKGtleWRvd24pPVwib25Sb3dLZXlkb3duKCRldmVudCwgcm93LCBpKVwiXG4gICAgICAgICAgY2xhc3M9XCJjcWEtZ3JvdXAgY3FhLWZsZXggY3FhLWl0ZW1zLWNlbnRlciBjcWEtZ2FwLTEgY3FhLXByLTMgY3FhLXB5LTEuNSBjcWEtY3Vyc29yLXBvaW50ZXIgaG92ZXI6Y3FhLWJnLW5ldXRyYWwtNTAgZm9jdXM6Y3FhLW91dGxpbmUtbm9uZSBmb2N1czpjcWEtYmctbmV1dHJhbC0xMDAgY3FhLXJlbGF0aXZlXCJcbiAgICAgICAgICBbY2xhc3MuY3FhLWJnLWluZGlnby01MF09XCJpc1NlbGVjdGVkKHJvdy5ub2RlLmlkKSB8fCBjb250ZXh0TWVudUZvbGRlcklkID09PSByb3cubm9kZS5pZFwiXG4gICAgICAgICAgW3N0eWxlLnBhZGRpbmdMZWZ0LnB4XT1cIjggKyByb3cuZGVwdGggKiAxNlwiXG4gICAgICAgICAgW2F0dHIudGl0bGVdPVwicm93Lm5vZGUudG90YWxDb3VudCAhPSBudWxsID8gKHJvdy5ub2RlLnRvdGFsQ291bnQgKyAnIHRvdGFsIGluY2x1ZGluZyBzdWJmb2xkZXJzJykgOiBudWxsXCJcbiAgICAgICAgPlxuICAgICAgICAgIDxzcGFuXG4gICAgICAgICAgICAqbmdJZj1cImlzU2VsZWN0ZWQocm93Lm5vZGUuaWQpXCJcbiAgICAgICAgICAgIGNsYXNzPVwiY3FhLWFic29sdXRlIGNxYS1sZWZ0LTAgY3FhLXRvcC0wIGNxYS1ib3R0b20tMCBjcWEtdy1bM3B4XSBjcWEtYmctaW5kaWdvLTYwMFwiXG4gICAgICAgICAgPjwvc3Bhbj5cbiAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgIGNsYXNzPVwiY3FhLXAtMCBjcWEtdGV4dC1uZXV0cmFsLTQwMFwiXG4gICAgICAgICAgICBbc3R5bGUudmlzaWJpbGl0eV09XCJyb3cuaGFzQ2hpbGRyZW4gPyAndmlzaWJsZScgOiAnaGlkZGVuJ1wiXG4gICAgICAgICAgICAoY2xpY2spPVwib25Ub2dnbGUocm93Lm5vZGUsICRldmVudClcIlxuICAgICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCJpc0V4cGFuZGVkKHJvdy5ub2RlLmlkKSA/ICdDb2xsYXBzZScgOiAnRXhwYW5kJ1wiXG4gICAgICAgICAgPlxuICAgICAgICAgICAgPG1hdC1pY29uIHN0eWxlPVwiZm9udC1zaXplOjE2cHg7d2lkdGg6MTZweDtoZWlnaHQ6MTZweFwiPlxuICAgICAgICAgICAgICB7eyBpc0V4cGFuZGVkKHJvdy5ub2RlLmlkKSA/ICdleHBhbmRfbW9yZScgOiAnY2hldnJvbl9yaWdodCcgfX1cbiAgICAgICAgICAgIDwvbWF0LWljb24+XG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cImZvbGRlckljb247IGNvbnRleHQ6IHsgY29sb3I6IHJvdy5ub2RlLmNvbG9yIH1cIj48L25nLWNvbnRhaW5lcj5cblxuICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJyZW5hbWluZ0lkID09PSByb3cubm9kZS5pZDsgZWxzZSBuYW1lVHBsXCI+XG4gICAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgICAgdHlwZT1cInRleHRcIlxuICAgICAgICAgICAgICBzaXplPVwiMVwiXG4gICAgICAgICAgICAgIFthdHRyLmRhdGEtZm9sZGVyLXJlbmFtZS1pbnB1dF09XCJyb3cubm9kZS5pZFwiXG4gICAgICAgICAgICAgIGNsYXNzPVwiY3FhLWZsZXgtMSBjcWEtbWluLXctMCBjcWEtdy1mdWxsIGNxYS1weC0yIGNxYS1weS0wLjUgY3FhLXJvdW5kZWQgY3FhLWJvcmRlciBjcWEtYm9yZGVyLW5ldXRyYWwtMzAwIGNxYS1iZy13aGl0ZSBjcWEtdGV4dC1zbSBjcWEtc2hhZG93LXNtIGZvY3VzOmNxYS1vdXRsaW5lLW5vbmUgZm9jdXM6Y3FhLWJvcmRlci1pbmRpZ28tNDAwIGZvY3VzOmNxYS1yaW5nLTEgZm9jdXM6Y3FhLXJpbmctaW5kaWdvLTIwMFwiXG4gICAgICAgICAgICAgIFsobmdNb2RlbCldPVwicmVuYW1lRHJhZnRcIlxuICAgICAgICAgICAgICAoa2V5ZG93bik9XCJvblJlbmFtZUtleSgkZXZlbnQsIHJvdy5ub2RlKVwiXG4gICAgICAgICAgICAgIChrZXlwcmVzcyk9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgICAgICAgICAoa2V5dXApPVwiJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgICAgICAgICAgKGJsdXIpPVwiY29tbWl0UmVuYW1lKHJvdy5ub2RlKVwiXG4gICAgICAgICAgICAgIChjbGljayk9XCIkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICA8bmctdGVtcGxhdGUgI25hbWVUcGw+XG4gICAgICAgICAgICA8c3BhblxuICAgICAgICAgICAgICBjbGFzcz1cImNxYS1mbGV4LTEgY3FhLXRleHQtc20gY3FhLXRleHQtbmV1dHJhbC04MDAgY3FhLXRydW5jYXRlXCJcbiAgICAgICAgICAgICAgKGRibGNsaWNrKT1cImJlZ2luUmVuYW1lKHJvdy5ub2RlLCAkZXZlbnQpXCJcbiAgICAgICAgICAgID57eyByb3cubm9kZS5uYW1lIH19PC9zcGFuPlxuICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG5cbiAgICAgICAgICA8IS0tIENvdW50IHNob3duIGF0IHJlc3Q7IGhpZGRlbiBvbiByb3cgaG92ZXIgd2hlbiBkZWxldGUgaXMgYWxsb3dlZCAtLT5cbiAgICAgICAgICA8c3BhblxuICAgICAgICAgICAgKm5nSWY9XCJzaG93Q291bnRzICYmIHJvdy5ub2RlLmNvdW50ICE9IG51bGxcIlxuICAgICAgICAgICAgY2xhc3M9XCJjcWEtdGV4dC14cyBjcWEtdGV4dC1uZXV0cmFsLTQwMCBjcWEtdGFidWxhci1udW1zIGNxYS1tbC0xXCJcbiAgICAgICAgICAgIFtjbGFzcy5ncm91cC1ob3ZlcjpjcWEtaGlkZGVuXT1cImFsbG93RGVsZXRlXCJcbiAgICAgICAgICA+e3sgcm93Lm5vZGUuY291bnQgfX08L3NwYW4+XG5cbiAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAqbmdJZj1cImhhc0FueUNvbnRleHRBY3Rpb25cIlxuICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICBjbGFzcz1cImNxYS1wLTAuNSBjcWEtcm91bmRlZCBob3ZlcjpjcWEtYmctbmV1dHJhbC0yMDAgY3FhLXRleHQtbmV1dHJhbC01MDBcIlxuICAgICAgICAgICAgW2NsYXNzLmNxYS1oaWRkZW5dPVwiY29udGV4dE1lbnVGb2xkZXJJZCAhPT0gcm93Lm5vZGUuaWRcIlxuICAgICAgICAgICAgW2NsYXNzLmdyb3VwLWhvdmVyOmNxYS1pbmxpbmUtZmxleF09XCJ0cnVlXCJcbiAgICAgICAgICAgIFtjbGFzcy5jcWEtaW5saW5lLWZsZXhdPVwiY29udGV4dE1lbnVGb2xkZXJJZCA9PT0gcm93Lm5vZGUuaWRcIlxuICAgICAgICAgICAgKGNsaWNrKT1cIm9wZW5Db250ZXh0TWVudShyb3cubm9kZSwgJGV2ZW50KVwiXG4gICAgICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidPcGVuIGFjdGlvbnMgZm9yICcgKyByb3cubm9kZS5uYW1lXCJcbiAgICAgICAgICAgIFthdHRyLmFyaWEtaGFzcG9wdXBdPVwiJ21lbnUnXCJcbiAgICAgICAgICAgIFthdHRyLmFyaWEtZXhwYW5kZWRdPVwiY29udGV4dE1lbnVGb2xkZXJJZCA9PT0gcm93Lm5vZGUuaWRcIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIDxtYXQtaWNvbiBzdHlsZT1cImZvbnQtc2l6ZToxNnB4O3dpZHRoOjE2cHg7aGVpZ2h0OjE2cHhcIj5tb3JlX2hvcml6PC9tYXQtaWNvbj5cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgPCEtLSBEaXZpZGVyIGJldHdlZW4gZm9sZGVyIHRyZWUgYW5kIFVub3JnYW5pc2VkIHRhYiAtLT5cbiAgICAgIDxkaXZcbiAgICAgICAgYXJpYS1oaWRkZW49XCJ0cnVlXCJcbiAgICAgICAgY2xhc3M9XCJjcWEtbXgtM1wiXG4gICAgICAgIHN0eWxlPVwiaGVpZ2h0OiAxcHg7IGJhY2tncm91bmQtY29sb3I6ICNFNUU3RUI7IG1hcmdpbi10b3A6IDZweDsgbWFyZ2luLWJvdHRvbTogNnB4O1wiXG4gICAgICA+PC9kaXY+XG5cbiAgICAgIDwhLS0gVW5vcmdhbmlzZWQg4oCUIGZsb3dzIHdpdGggdGhlIHRyZWUsIGJlaGF2ZXMgbGlrZSBhIHRhYiBpdGVtIC0tPlxuICAgICAgPGRpdlxuICAgICAgICByb2xlPVwidHJlZWl0ZW1cIlxuICAgICAgICB0YWJpbmRleD1cIjBcIlxuICAgICAgICBbYXR0ci5hcmlhLXNlbGVjdGVkXT1cImlzU2VsZWN0ZWQobnVsbClcIlxuICAgICAgICBjbGFzcz1cImNxYS1mbGV4IGNxYS1pdGVtcy1jZW50ZXIgY3FhLWdhcC0yIGNxYS1weC0zIGNxYS1weS0xLjUgY3FhLWN1cnNvci1wb2ludGVyIGhvdmVyOmNxYS1iZy1uZXV0cmFsLTUwIGZvY3VzOmNxYS1vdXRsaW5lLW5vbmUgZm9jdXM6Y3FhLWJnLW5ldXRyYWwtMTAwIGNxYS1yZWxhdGl2ZVwiXG4gICAgICAgIFtjbGFzcy5jcWEtYmctaW5kaWdvLTUwXT1cImlzU2VsZWN0ZWQobnVsbClcIlxuICAgICAgICBbY3FhRm9sZGVyRHJvcF09XCJudWxsXCJcbiAgICAgICAgW2Ryb3BFbmFibGVkXT1cImFsbG93RHJvcFwiXG4gICAgICAgICh0ZXN0c0Ryb3BwZWQpPVwicm93RHJvcHBlZChudWxsLCAkZXZlbnQpXCJcbiAgICAgICAgKGZvbGRlckRyb3BwZWQpPVwib25Gb2xkZXJSb3dEcm9wcGVkKG51bGwsICRldmVudClcIlxuICAgICAgICAoY2xpY2spPVwib25TZWxlY3RVbm9yZ2FuaXNlZCgpXCJcbiAgICAgID5cbiAgICAgICAgPHNwYW5cbiAgICAgICAgICAqbmdJZj1cImlzU2VsZWN0ZWQobnVsbClcIlxuICAgICAgICAgIGNsYXNzPVwiY3FhLWFic29sdXRlIGNxYS1sZWZ0LTAgY3FhLXRvcC0wIGNxYS1ib3R0b20tMCBjcWEtdy1bM3B4XSBjcWEtYmctaW5kaWdvLTYwMFwiXG4gICAgICAgID48L3NwYW4+XG4gICAgICAgIDxtYXQtaWNvbiBjbGFzcz1cImNxYS10ZXh0LW5ldXRyYWwtNTAwXCIgc3R5bGU9XCJmb250LXNpemU6MTZweDt3aWR0aDoxNnB4O2hlaWdodDoxNnB4XCI+aW5ib3g8L21hdC1pY29uPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImNxYS1mbGV4LTEgY3FhLXRleHQtc20gY3FhLXRleHQtbmV1dHJhbC03MDBcIj57eyBsYWJlbHMudW5vcmdhbmlzZWQgfX08L3NwYW4+XG4gICAgICAgIDxzcGFuICpuZ0lmPVwic2hvd0NvdW50c1wiIGNsYXNzPVwiY3FhLXRleHQteHMgY3FhLXRleHQtbmV1dHJhbC00MDAgY3FhLXRhYnVsYXItbnVtc1wiPnt7IHVub3JnYW5pc2VkQ291bnQgfX08L3NwYW4+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDwhLS0gRm9sZGVyIGNvbnRleHQgbWVudSAocmlnaHQtY2xpY2sgLyBlbGxpcHNpcykuIEFuY2hvcmVkIHRvIHZpZXdwb3J0IGNvb3Jkcy4gLS0+XG4gICAgPGRpdlxuICAgICAgKm5nSWY9XCJjb250ZXh0TWVudUZvbGRlcklkICE9PSBudWxsXCJcbiAgICAgIHJvbGU9XCJtZW51XCJcbiAgICAgIGNsYXNzPVwiY3FhLWZpeGVkIGNxYS16LTUwIGNxYS1taW4tdy1bMTgwcHhdIGNxYS1iZy13aGl0ZSBjcWEtYm9yZGVyIGNxYS1ib3JkZXItbmV1dHJhbC0yMDAgY3FhLXJvdW5kZWQtbWQgY3FhLXNoYWRvdy1sZyBjcWEtcHktMVwiXG4gICAgICBbc3R5bGUubGVmdC5weF09XCJjb250ZXh0TWVudVBvc2l0aW9uLnhcIlxuICAgICAgW3N0eWxlLnRvcC5weF09XCJjb250ZXh0TWVudVBvc2l0aW9uLnlcIlxuICAgICAgKGNsaWNrKT1cIiRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiXG4gICAgICAoY29udGV4dG1lbnUpPVwiJGV2ZW50LnByZXZlbnREZWZhdWx0KCk7ICRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiXG4gICAgPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImZvbGRlckJ5SWQoY29udGV4dE1lbnVGb2xkZXJJZCkgYXMgbWVudU5vZGVcIj5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICpuZ0lmPVwiYWxsb3dDcmVhdGVcIlxuICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgIHJvbGU9XCJtZW51aXRlbVwiXG4gICAgICAgICAgY2xhc3M9XCJjcWEtZmxleCBjcWEtaXRlbXMtY2VudGVyIGNxYS1nYXAtMiBjcWEtdy1mdWxsIGNxYS1weC0zIGNxYS1weS0xLjUgY3FhLXRleHQtc20gY3FhLXRleHQtbmV1dHJhbC04MDAgaG92ZXI6Y3FhLWJnLW5ldXRyYWwtMTAwIGNxYS10ZXh0LWxlZnRcIlxuICAgICAgICAgIChjbGljayk9XCJvbkNvbnRleHRDcmVhdGVTdWJmb2xkZXIobWVudU5vZGUpXCJcbiAgICAgICAgPlxuICAgICAgICAgIDxtYXQtaWNvbiBzdHlsZT1cImZvbnQtc2l6ZToxNnB4O3dpZHRoOjE2cHg7aGVpZ2h0OjE2cHhcIj5jcmVhdGVfbmV3X2ZvbGRlcjwvbWF0LWljb24+XG4gICAgICAgICAgPHNwYW4+e3sgbGFiZWxzLmZvbGRlck1lbnVDcmVhdGVTdWJmb2xkZXIgfX08L3NwYW4+XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgKm5nSWY9XCJhbGxvd1JlbmFtZVwiXG4gICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgcm9sZT1cIm1lbnVpdGVtXCJcbiAgICAgICAgICBjbGFzcz1cImNxYS1mbGV4IGNxYS1pdGVtcy1jZW50ZXIgY3FhLWdhcC0yIGNxYS13LWZ1bGwgY3FhLXB4LTMgY3FhLXB5LTEuNSBjcWEtdGV4dC1zbSBjcWEtdGV4dC1uZXV0cmFsLTgwMCBob3ZlcjpjcWEtYmctbmV1dHJhbC0xMDAgY3FhLXRleHQtbGVmdFwiXG4gICAgICAgICAgKGNsaWNrKT1cIm9uQ29udGV4dFJlbmFtZShtZW51Tm9kZSlcIlxuICAgICAgICA+XG4gICAgICAgICAgPG1hdC1pY29uIHN0eWxlPVwiZm9udC1zaXplOjE2cHg7d2lkdGg6MTZweDtoZWlnaHQ6MTZweFwiPmVkaXQ8L21hdC1pY29uPlxuICAgICAgICAgIDxzcGFuPnt7IGxhYmVscy5mb2xkZXJNZW51UmVuYW1lIH19PC9zcGFuPlxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICpuZ0lmPVwiYWxsb3dNb3ZlXCJcbiAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICByb2xlPVwibWVudWl0ZW1cIlxuICAgICAgICAgIGNsYXNzPVwiY3FhLWZsZXggY3FhLWl0ZW1zLWNlbnRlciBjcWEtZ2FwLTIgY3FhLXctZnVsbCBjcWEtcHgtMyBjcWEtcHktMS41IGNxYS10ZXh0LXNtIGNxYS10ZXh0LW5ldXRyYWwtODAwIGhvdmVyOmNxYS1iZy1uZXV0cmFsLTEwMCBjcWEtdGV4dC1sZWZ0XCJcbiAgICAgICAgICAoY2xpY2spPVwib25Db250ZXh0TW92ZShtZW51Tm9kZSlcIlxuICAgICAgICA+XG4gICAgICAgICAgPG1hdC1pY29uIHN0eWxlPVwiZm9udC1zaXplOjE2cHg7d2lkdGg6MTZweDtoZWlnaHQ6MTZweFwiPmRyaXZlX2ZpbGVfbW92ZTwvbWF0LWljb24+XG4gICAgICAgICAgPHNwYW4+e3sgbGFiZWxzLmZvbGRlck1lbnVNb3ZlIH19PC9zcGFuPlxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICpuZ0lmPVwiYWxsb3dEdXBsaWNhdGVcIlxuICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgIHJvbGU9XCJtZW51aXRlbVwiXG4gICAgICAgICAgY2xhc3M9XCJjcWEtZmxleCBjcWEtaXRlbXMtY2VudGVyIGNxYS1nYXAtMiBjcWEtdy1mdWxsIGNxYS1weC0zIGNxYS1weS0xLjUgY3FhLXRleHQtc20gY3FhLXRleHQtbmV1dHJhbC04MDAgaG92ZXI6Y3FhLWJnLW5ldXRyYWwtMTAwIGNxYS10ZXh0LWxlZnRcIlxuICAgICAgICAgIChjbGljayk9XCJvbkNvbnRleHREdXBsaWNhdGUobWVudU5vZGUpXCJcbiAgICAgICAgPlxuICAgICAgICAgIDxtYXQtaWNvbiBzdHlsZT1cImZvbnQtc2l6ZToxNnB4O3dpZHRoOjE2cHg7aGVpZ2h0OjE2cHhcIj5jb250ZW50X2NvcHk8L21hdC1pY29uPlxuICAgICAgICAgIDxzcGFuPnt7IGxhYmVscy5mb2xkZXJNZW51RHVwbGljYXRlIH19PC9zcGFuPlxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPGRpdiAqbmdJZj1cImFsbG93RGVsZXRlICYmIChhbGxvd0NyZWF0ZSB8fCBhbGxvd1JlbmFtZSB8fCBhbGxvd01vdmUgfHwgYWxsb3dEdXBsaWNhdGUpXCIgY2xhc3M9XCJjcWEtaC1weCBjcWEtYmctbmV1dHJhbC0yMDAgY3FhLW15LTFcIj48L2Rpdj5cbiAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICpuZ0lmPVwiYWxsb3dEZWxldGVcIlxuICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgIHJvbGU9XCJtZW51aXRlbVwiXG4gICAgICAgICAgY2xhc3M9XCJjcWEtZmxleCBjcWEtaXRlbXMtY2VudGVyIGNxYS1nYXAtMiBjcWEtdy1mdWxsIGNxYS1weC0zIGNxYS1weS0xLjUgY3FhLXRleHQtc20gY3FhLXRleHQtcmVkLTYwMCBob3ZlcjpjcWEtYmctcmVkLTUwIGNxYS10ZXh0LWxlZnRcIlxuICAgICAgICAgIChjbGljayk9XCJvbkNvbnRleHREZWxldGUobWVudU5vZGUpXCJcbiAgICAgICAgPlxuICAgICAgICAgIDxtYXQtaWNvbiBzdHlsZT1cImZvbnQtc2l6ZToxNnB4O3dpZHRoOjE2cHg7aGVpZ2h0OjE2cHhcIj5kZWxldGVfb3V0bGluZTwvbWF0LWljb24+XG4gICAgICAgICAgPHNwYW4+e3sgbGFiZWxzLmZvbGRlck1lbnVEZWxldGUgfX08L3NwYW4+XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC9kaXY+XG5cbiAgPC9uZy1jb250YWluZXI+XG48L2FzaWRlPlxuIl19
|