cloud-ide-element 0.0.1 → 1.0.0

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.
Files changed (50) hide show
  1. package/README.md +271 -24
  2. package/esm2022/lib/components/confirmation-modal/confirmation-modal.component.mjs +182 -0
  3. package/esm2022/lib/components/data-grid/data-grid.component.mjs +1363 -0
  4. package/esm2022/lib/components/data-grid/data-grid.types.mjs +37 -0
  5. package/esm2022/lib/components/dropdown/dropdown.component.mjs +396 -0
  6. package/esm2022/lib/components/global-notifications/global-notifications.component.mjs +30 -0
  7. package/esm2022/lib/components/json-editor/json-editor.component.mjs +521 -0
  8. package/esm2022/lib/components/skeleton-loader/skeleton-loader.component.mjs +33 -0
  9. package/esm2022/lib/components/toast-notification/toast-notification.component.mjs +152 -0
  10. package/esm2022/lib/elements/button/cide-ele-button.component.mjs +249 -0
  11. package/esm2022/lib/elements/file-input/file-input.component.mjs +83 -0
  12. package/esm2022/lib/elements/icon/icon.component.mjs +5 -3
  13. package/esm2022/lib/elements/input/input.component.mjs +34 -20
  14. package/esm2022/lib/elements/select/select.component.mjs +471 -0
  15. package/esm2022/lib/elements/tab/cide-ele-tab.component.mjs +74 -0
  16. package/esm2022/lib/elements/textarea/textarea.component.mjs +157 -0
  17. package/esm2022/lib/services/confirmation.service.mjs +151 -0
  18. package/esm2022/lib/services/dropdown-manager.service.mjs +93 -0
  19. package/esm2022/lib/services/notification.service.mjs +196 -0
  20. package/esm2022/lib/utils/directives/resizer/resizer.directive.mjs +231 -0
  21. package/esm2022/lib/utils/directives/tooltip/tooltip.directive.mjs +294 -0
  22. package/esm2022/lib/utils/services/elements/elements.service.mjs +9 -7
  23. package/esm2022/public-api.mjs +23 -2
  24. package/fesm2022/cloud-ide-element.mjs +4646 -47
  25. package/fesm2022/cloud-ide-element.mjs.map +1 -1
  26. package/lib/components/confirmation-modal/confirmation-modal.component.d.ts +16 -0
  27. package/lib/components/data-grid/data-grid.component.d.ts +244 -0
  28. package/lib/components/data-grid/data-grid.types.d.ts +146 -0
  29. package/lib/components/dropdown/dropdown.component.d.ts +75 -0
  30. package/lib/components/global-notifications/global-notifications.component.d.ts +5 -0
  31. package/lib/components/json-editor/json-editor.component.d.ts +116 -0
  32. package/lib/components/skeleton-loader/skeleton-loader.component.d.ts +11 -0
  33. package/lib/components/toast-notification/toast-notification.component.d.ts +13 -0
  34. package/lib/elements/button/cide-ele-button.component.d.ts +85 -0
  35. package/lib/elements/file-input/file-input.component.d.ts +25 -0
  36. package/lib/elements/input/input.component.d.ts +7 -4
  37. package/lib/elements/select/select.component.d.ts +91 -0
  38. package/lib/elements/tab/cide-ele-tab.component.d.ts +26 -0
  39. package/lib/elements/textarea/textarea.component.d.ts +47 -0
  40. package/lib/services/confirmation.service.d.ts +65 -0
  41. package/lib/services/dropdown-manager.service.d.ts +22 -0
  42. package/lib/services/notification.service.d.ts +81 -0
  43. package/lib/utils/directives/resizer/resizer.directive.d.ts +44 -0
  44. package/lib/utils/directives/tooltip/tooltip.directive.d.ts +43 -0
  45. package/package.json +32 -4
  46. package/public-api.d.ts +18 -1
  47. package/src/lib/assets/css/cide-ele-style.scss +85 -0
  48. package/src/lib/assets/css/cide-ele-variable.scss +336 -0
  49. package/esm2022/lib/elements/button/button.component.mjs +0 -60
  50. package/lib/elements/button/button.component.d.ts +0 -27
@@ -0,0 +1,1363 @@
1
+ import { Component, Input, Output, EventEmitter, signal, computed } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { DEFAULT_GRID_CONFIG } from './data-grid.types';
5
+ import { CideInputComponent } from '../../elements/input/input.component';
6
+ import { CideSelectComponent } from '../../elements/select/select.component';
7
+ import { CideIconComponent } from '../../elements/icon/icon.component';
8
+ import { CideEleButtonComponent } from '../../../public-api';
9
+ import * as i0 from "@angular/core";
10
+ import * as i1 from "@angular/common";
11
+ import * as i2 from "@angular/forms";
12
+ export class CideEleDataGridComponent {
13
+ constructor() {
14
+ this.customRenderers = {};
15
+ this.templateRenderers = {};
16
+ this.customFormatters = {};
17
+ this.actionHandlers = {};
18
+ this.serverSidePagination = false;
19
+ this.totalServerItems = 0;
20
+ this.currentServerPage = 1;
21
+ this.currentServerPageSize = 10;
22
+ this.dragDropEnabled = false;
23
+ this.gridEvent = new EventEmitter();
24
+ // Internal state using Angular Signals
25
+ this.internalData = signal([]);
26
+ this.filteredData = signal([]);
27
+ // Grid state
28
+ this.currentPage = signal(1);
29
+ this.pageSize = signal(10);
30
+ this.totalItems = signal(0);
31
+ this.totalPages = signal(0);
32
+ this.searchQuery = signal('');
33
+ this.loading = signal(false);
34
+ this.isRefreshing = signal(false);
35
+ this.pageChangeLoading = signal(false);
36
+ this.jumpToPage = 1;
37
+ this.isDragOverRow = null;
38
+ this.isDragging = signal(false);
39
+ this.hasOrderChanged = signal(false);
40
+ this.originalOrderIds = []; // Store only IDs for order tracking
41
+ this.initialDataOrderIds = []; // Store only IDs for initial order
42
+ this.rowOrderMap = new Map();
43
+ this.localReorderedData = []; // Local data for visual reordering
44
+ this.isDataUpdate = false;
45
+ // Computed properties
46
+ this.hasNextPage = computed(() => this.currentPage() < this.totalPages());
47
+ this.hasPreviousPage = computed(() => this.currentPage() > 1);
48
+ // Merged configuration with defaults
49
+ this.mergedConfig = signal(this.config);
50
+ }
51
+ ngOnInit() {
52
+ this.initializeGrid();
53
+ this.initializeOriginalOrder();
54
+ }
55
+ ngOnDestroy() {
56
+ // Clean up search debounce timer
57
+ if (this.searchDebounceTimer) {
58
+ clearTimeout(this.searchDebounceTimer);
59
+ }
60
+ }
61
+ ngOnChanges(changes) {
62
+ if (changes['config']) {
63
+ // Store current search query to preserve it during re-initialization
64
+ const currentSearchQuery = this.searchQuery();
65
+ this.mergeConfigWithDefaults();
66
+ // Check if this is a data-only update (same config except data)
67
+ const isDataOnlyUpdate = this.isDataOnlyUpdate(changes['config']);
68
+ if (isDataOnlyUpdate && this.serverSidePagination) {
69
+ console.log('🔄 DataGrid: isDataOnlyUpdate', isDataOnlyUpdate);
70
+ // For data-only updates, preserve search query and use updateDataOnly
71
+ this.updateDataOnly(changes['config'].currentValue.data || []);
72
+ if (currentSearchQuery) {
73
+ // this.searchQuery.set(currentSearchQuery);
74
+ }
75
+ }
76
+ else {
77
+ // For full config changes, use initializeGrid
78
+ this.initializeGrid();
79
+ // Restore search query after initialization
80
+ if (currentSearchQuery) {
81
+ // this.searchQuery.set(currentSearchQuery);
82
+ }
83
+ }
84
+ // Reinitialize original order when data changes
85
+ this.initializeOriginalOrder();
86
+ // Also refresh order tracking to ensure it's up to date
87
+ this.refreshOrderTracking();
88
+ }
89
+ if (this.serverSidePagination) {
90
+ if (changes['totalServerItems']) {
91
+ // console.log('🔄 DataGrid: totalServerItems changed:', this.totalServerItems);
92
+ this.totalItems.set(this.totalServerItems);
93
+ this.updatePaginationState();
94
+ }
95
+ if (changes['currentServerPage']) {
96
+ // console.log('🔄 DataGrid: currentServerPage changed:', this.currentServerPage);
97
+ this.currentPage.set(this.currentServerPage);
98
+ }
99
+ if (changes['currentServerPageSize']) {
100
+ // console.log('🔄 DataGrid: currentServerPageSize changed:', this.currentServerPageSize);
101
+ this.pageSize.set(this.currentServerPageSize);
102
+ this.updatePaginationState();
103
+ }
104
+ // Clear page change loading when server-side data is updated
105
+ if (changes['config'] && this.pageChangeLoading()) {
106
+ this.clearPageChangeLoading();
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * Check if the config change is only a data update
112
+ */
113
+ isDataOnlyUpdate(change) {
114
+ if (change.firstChange || !change.previousValue || !change.currentValue) {
115
+ return false;
116
+ }
117
+ const prev = change.previousValue;
118
+ const curr = change.currentValue;
119
+ // Check if all properties except 'data' are the same
120
+ const prevWithoutData = { ...prev };
121
+ const currWithoutData = { ...curr };
122
+ delete prevWithoutData.data;
123
+ delete currWithoutData.data;
124
+ return JSON.stringify(prevWithoutData) === JSON.stringify(currWithoutData);
125
+ }
126
+ /**
127
+ * Update only the data without triggering full grid re-initialization
128
+ * This prevents the search input and other UI state from being reset
129
+ */
130
+ updateDataOnly(data) {
131
+ let processedData = data;
132
+ // Transform to tree structure if tree config is enabled
133
+ if (this.mergedConfig().tree?.enabled) {
134
+ processedData = this.transformToTree(data);
135
+ console.log('processedData', processedData);
136
+ }
137
+ this.internalData.set(processedData);
138
+ console.log('processedData', processedData);
139
+ // For server-side pagination, set filtered data directly
140
+ if (this.serverSidePagination) {
141
+ console.log('processedData', processedData);
142
+ this.filteredData.set(processedData);
143
+ }
144
+ else {
145
+ // For client-side pagination, apply filters
146
+ this.applyFilters();
147
+ }
148
+ // Refresh order tracking when data changes
149
+ this.refreshOrderTracking();
150
+ this.clearPageChangeLoading();
151
+ }
152
+ mergeConfigWithDefaults() {
153
+ this.mergedConfig.set({
154
+ ...DEFAULT_GRID_CONFIG,
155
+ ...this.config,
156
+ pagination: { ...DEFAULT_GRID_CONFIG.pagination, ...this.config.pagination },
157
+ search: { ...DEFAULT_GRID_CONFIG.search, ...this.config.search },
158
+ loading: { ...DEFAULT_GRID_CONFIG.loading, ...this.config.loading },
159
+ scroll: { ...DEFAULT_GRID_CONFIG.scroll, ...this.config.scroll }
160
+ });
161
+ }
162
+ initializeGrid() {
163
+ // Ensure we have valid data
164
+ let data = this.config?.data || [];
165
+ // Transform to tree structure if tree config is enabled
166
+ if (this.mergedConfig().tree?.enabled) {
167
+ data = this.transformToTree(data);
168
+ }
169
+ this.internalData.set(data);
170
+ // Set pagination values based on mode
171
+ if (this.serverSidePagination) {
172
+ this.currentPage.set(this.currentServerPage);
173
+ this.pageSize.set(this.currentServerPageSize);
174
+ this.totalItems.set(this.totalServerItems);
175
+ // For server-side pagination, set filtered data directly without applying filters
176
+ this.filteredData.set(data);
177
+ }
178
+ else {
179
+ // Client-side pagination
180
+ const pageSize = this.mergedConfig().pagination?.pageSize || 10;
181
+ this.pageSize.set(pageSize);
182
+ this.totalItems.set(data.length);
183
+ // For client-side pagination, apply filters
184
+ this.applyFilters();
185
+ }
186
+ this.updatePaginationState();
187
+ // Initialize original order for drag and drop
188
+ this.initializeOriginalOrder();
189
+ // Also refresh order tracking to ensure it's up to date
190
+ this.refreshOrderTracking();
191
+ }
192
+ /**
193
+ * Get the unique identifier for an item
194
+ */
195
+ getItemId(item) {
196
+ if (item && typeof item === 'object') {
197
+ const id = item['_id'];
198
+ return id ? String(id) : null;
199
+ }
200
+ return null;
201
+ }
202
+ /**
203
+ * Get the order value for an item from the config
204
+ */
205
+ getItemOrder(item) {
206
+ const dragDropConfig = this.getDragDropConfig();
207
+ if (dragDropConfig?.orderField) {
208
+ const orderValue = this.getNestedValue(item, dragDropConfig.orderField);
209
+ return typeof orderValue === 'number' ? orderValue : 0;
210
+ }
211
+ return 0;
212
+ }
213
+ /**
214
+ * Initialize the original order for drag and drop
215
+ */
216
+ initializeOriginalOrder() {
217
+ if (this.isDragDropEnabled()) {
218
+ // Get the full dataset from the config (not just displayedData)
219
+ const fullData = this.mergedConfig().data || [];
220
+ // Completely build the order tracking from current data
221
+ this.rowOrderMap.clear();
222
+ this.initialDataOrderIds = [];
223
+ // Build the order tracking with current data
224
+ fullData.forEach((item, index) => {
225
+ const itemId = this.getItemId(item);
226
+ if (itemId) {
227
+ const actualOrder = this.getItemOrder(item);
228
+ this.initialDataOrderIds.push(itemId);
229
+ this.rowOrderMap.set(itemId, {
230
+ oldPosition: actualOrder || (index + 1), // Use actual order or fallback to index
231
+ newPosition: actualOrder || (index + 1)
232
+ });
233
+ }
234
+ });
235
+ // Clear any existing local reordered data
236
+ this.localReorderedData = [];
237
+ console.log('🔍 Initial order tracking built from full dataset:', this.initialDataOrderIds.length, 'items');
238
+ console.log('🔍 Initial order map created with actual order values:', Array.from(this.rowOrderMap.entries()));
239
+ }
240
+ }
241
+ /**
242
+ * Refresh order tracking with current data (called when data updates)
243
+ */
244
+ refreshOrderTracking() {
245
+ if (this.isDragDropEnabled()) {
246
+ // Get the current full dataset
247
+ const fullData = this.mergedConfig().data || [];
248
+ // Completely rebuild the order tracking from current data
249
+ this.rowOrderMap.clear();
250
+ this.initialDataOrderIds = [];
251
+ // Rebuild the order tracking with current data
252
+ fullData.forEach((item, index) => {
253
+ const itemId = this.getItemId(item);
254
+ if (itemId) {
255
+ const actualOrder = this.getItemOrder(item);
256
+ this.initialDataOrderIds.push(itemId);
257
+ this.rowOrderMap.set(itemId, {
258
+ oldPosition: actualOrder || (index + 1),
259
+ newPosition: actualOrder || (index + 1)
260
+ });
261
+ }
262
+ });
263
+ // Clear any local reordered data since data has changed
264
+ this.localReorderedData = [];
265
+ this.hasOrderChanged.set(false);
266
+ console.log('🔍 Order tracking completely rebuilt with current data:', this.initialDataOrderIds.length, 'items');
267
+ }
268
+ }
269
+ // ===== Data Processing =====
270
+ applyFilters() {
271
+ let filtered = [...this.internalData()];
272
+ // Apply search filter only for client-side pagination
273
+ // For server-side pagination, search is handled by the server
274
+ if (!this.serverSidePagination) {
275
+ const query = this.searchQuery()?.toLowerCase();
276
+ if (query && this.mergedConfig().search.enabled) {
277
+ filtered = filtered.filter(item => this.mergedConfig().search.searchableColumns.some(columnKey => {
278
+ const value = this.getNestedValue(item, columnKey);
279
+ return String(value || '')?.toLowerCase()?.includes(query);
280
+ }));
281
+ }
282
+ }
283
+ // Flatten tree structure for display if tree is enabled
284
+ if (this.mergedConfig().tree?.enabled) {
285
+ filtered = this.flattenTreeForDisplay(filtered);
286
+ }
287
+ this.filteredData.set(filtered);
288
+ // For server-side pagination, totalItems should always be totalServerItems
289
+ // For client-side pagination, totalItems should be the filtered data length
290
+ if (this.serverSidePagination) {
291
+ this.totalItems.set(this.totalServerItems);
292
+ }
293
+ else {
294
+ this.totalItems.set(filtered.length);
295
+ }
296
+ this.updatePaginationState();
297
+ }
298
+ /**
299
+ * Transform flat data to tree structure based on foreign key relationships
300
+ */
301
+ transformToTree(data) {
302
+ console.log('transformToTree - input data:', data.length, 'items');
303
+ const treeConfig = this.mergedConfig().tree;
304
+ if (!treeConfig)
305
+ return data;
306
+ const { primaryKey, foreignKey, childrenKey = 'children', levelKey = 'level', expandedKey = 'isExpanded', hasChildrenKey = 'hasChildren' } = treeConfig;
307
+ // Always add level key to each row during processing
308
+ // Create a map for quick lookup
309
+ const itemMap = new Map();
310
+ const processedItems = new Set();
311
+ const rootItems = [];
312
+ // First pass: create tree items with default properties and level key
313
+ data.forEach(item => {
314
+ const treeItem = {
315
+ ...item,
316
+ [levelKey]: 0,
317
+ level: 0, // Always add 'level' property for consistent access
318
+ [expandedKey]: false,
319
+ [hasChildrenKey]: false,
320
+ [childrenKey]: []
321
+ };
322
+ itemMap.set(String(this.getNestedValue(item, primaryKey) || ''), treeItem);
323
+ });
324
+ // Multi-pass hierarchy building to handle items in any order
325
+ let remainingItems = [...data];
326
+ const maxIterations = data.length; // Prevent infinite loops
327
+ let iteration = 0;
328
+ while (remainingItems.length > 0 && iteration < maxIterations) {
329
+ const itemsToProcess = [...remainingItems];
330
+ remainingItems = [];
331
+ console.log(`Iteration ${iteration + 1}: Processing ${itemsToProcess.length} items`);
332
+ itemsToProcess.forEach(item => {
333
+ const itemId = String(this.getNestedValue(item, primaryKey) || '');
334
+ const parentIdValue = this.getNestedValue(item, foreignKey);
335
+ const parentId = parentIdValue !== null && parentIdValue !== undefined ? String(parentIdValue) : '';
336
+ const treeItem = itemMap.get(itemId);
337
+ // Skip if already processed
338
+ if (processedItems.has(itemId)) {
339
+ return;
340
+ }
341
+ if (treeItem && parentId && itemMap.has(parentId)) {
342
+ // This is a child item - check if parent is processed or is a root
343
+ const parent = itemMap.get(parentId);
344
+ const parentParentId = this.getNestedValue(parent, foreignKey);
345
+ const isParentRoot = !parentParentId || parentParentId === null || parentParentId === undefined;
346
+ if (isParentRoot || processedItems.has(parentId)) {
347
+ // Parent is processed or is root, we can process this child
348
+ const parentChildren = this.getNestedValue(parent, childrenKey) || [];
349
+ // Avoid adding duplicates
350
+ const childExists = parentChildren.some(child => String(this.getNestedValue(child, primaryKey) || '') === itemId);
351
+ if (!childExists) {
352
+ // Calculate level based on parent - automatically set level key
353
+ const parentLevel = this.getNestedValue(parent, 'level') || 0;
354
+ const childLevel = parentLevel + 1;
355
+ // Update child item with correct level
356
+ const updatedTreeItem = {
357
+ ...treeItem,
358
+ [levelKey]: childLevel,
359
+ level: childLevel // Always add 'level' property for consistent access
360
+ };
361
+ itemMap.set(itemId, updatedTreeItem);
362
+ // Add updated child to parent's children array
363
+ parentChildren.push(updatedTreeItem);
364
+ // Update parent properties
365
+ const updatedParent = {
366
+ ...parent,
367
+ [childrenKey]: parentChildren,
368
+ [hasChildrenKey]: true
369
+ };
370
+ itemMap.set(parentId, updatedParent);
371
+ processedItems.add(itemId);
372
+ }
373
+ }
374
+ else {
375
+ // Parent not processed yet, defer this item
376
+ remainingItems.push(item);
377
+ }
378
+ }
379
+ else if (treeItem) {
380
+ // This is a root item - automatically set level to 0
381
+ const updatedRootItem = {
382
+ ...treeItem,
383
+ [levelKey]: 0,
384
+ level: 0 // Always add 'level' property for consistent access
385
+ };
386
+ itemMap.set(itemId, updatedRootItem);
387
+ if (!rootItems.some(rootItem => String(this.getNestedValue(rootItem, primaryKey) || '') === itemId)) {
388
+ rootItems.push(updatedRootItem);
389
+ processedItems.add(itemId);
390
+ }
391
+ }
392
+ });
393
+ iteration++;
394
+ }
395
+ // Handle any remaining unprocessed items as roots (orphaned items)
396
+ remainingItems.forEach(item => {
397
+ const itemId = String(this.getNestedValue(item, primaryKey) || '');
398
+ if (!processedItems.has(itemId)) {
399
+ const treeItem = itemMap.get(itemId);
400
+ if (treeItem && !rootItems.includes(treeItem)) {
401
+ console.log(`Adding orphaned item ${itemId} as root`);
402
+ rootItems.push(treeItem);
403
+ }
404
+ }
405
+ });
406
+ // Final pass: Update hasChildren property for all items in the tree
407
+ const updateHasChildren = (items) => {
408
+ items.forEach((item, index) => {
409
+ const itemId = String(this.getNestedValue(item, primaryKey) || '');
410
+ const children = this.getNestedValue(item, childrenKey) || [];
411
+ const hasChildren = children.length > 0;
412
+ // Update the item with correct hasChildren property
413
+ const updatedItem = {
414
+ ...item,
415
+ [hasChildrenKey]: hasChildren
416
+ };
417
+ // Update in the map
418
+ itemMap.set(itemId, updatedItem);
419
+ // Update the item in the array
420
+ items[index] = updatedItem;
421
+ // Recursively update children
422
+ if (children.length > 0) {
423
+ updateHasChildren(children);
424
+ }
425
+ });
426
+ };
427
+ // Get updated root items from the map to ensure we have the latest versions
428
+ const updatedRootItems = rootItems.map(rootItem => {
429
+ const rootId = String(this.getNestedValue(rootItem, primaryKey) || '');
430
+ return itemMap.get(rootId) || rootItem;
431
+ });
432
+ // Update all root items and their descendants
433
+ updateHasChildren(updatedRootItems);
434
+ console.log('transformToTree - root items:', updatedRootItems.length);
435
+ console.log('CC - processed items:', processedItems.size);
436
+ console.log('transformToTree - sample root item with level:', updatedRootItems[0]);
437
+ return updatedRootItems;
438
+ }
439
+ /**
440
+ * Recursively calculate item level in hierarchy for unlimited depth support
441
+ */
442
+ calculateItemLevel(item, itemMap, primaryKey, foreignKey, levelKey) {
443
+ // Check if level is already set correctly
444
+ const currentLevel = this.getNestedValue(item, levelKey);
445
+ if (currentLevel !== undefined && currentLevel > 0) {
446
+ return currentLevel;
447
+ }
448
+ // Get parent information
449
+ const itemId = String(this.getNestedValue(item, primaryKey) || '');
450
+ const parentIdValue = this.getNestedValue(item, foreignKey);
451
+ const parentId = parentIdValue !== null && parentIdValue !== undefined ? String(parentIdValue) : '';
452
+ // If no parent, this is a root item (level 0)
453
+ if (!parentId || !itemMap.has(parentId)) {
454
+ return 0;
455
+ }
456
+ // Recursively calculate parent level and add 1
457
+ const parent = itemMap.get(parentId);
458
+ const parentLevel = this.calculateItemLevel(parent, itemMap, primaryKey, foreignKey, levelKey);
459
+ // Update the item's level in the map
460
+ const updatedItem = {
461
+ ...item,
462
+ [levelKey]: parentLevel + 1
463
+ };
464
+ itemMap.set(itemId, updatedItem);
465
+ return parentLevel + 1;
466
+ }
467
+ /**
468
+ * Flatten tree structure for display, respecting expansion state with unlimited nesting
469
+ */
470
+ flattenTreeForDisplay(treeItems) {
471
+ const result = [];
472
+ const treeConfig = this.mergedConfig().tree;
473
+ if (!treeConfig)
474
+ return treeItems;
475
+ const { childrenKey = 'children', expandedKey = 'isExpanded' } = treeConfig;
476
+ const flatten = (items, currentLevel = 0) => {
477
+ items.forEach(item => {
478
+ result.push(item);
479
+ const children = this.getNestedValue(item, childrenKey) || [];
480
+ const isExpanded = this.getNestedValue(item, expandedKey) || false;
481
+ if (children.length > 0 && isExpanded) {
482
+ console.log(`Flattening children for item at level ${currentLevel}, children count: ${children.length}`);
483
+ flatten(children, currentLevel + 1);
484
+ }
485
+ });
486
+ };
487
+ flatten(treeItems);
488
+ console.log('flattenTreeForDisplay - result:', result.length, 'items');
489
+ return result;
490
+ }
491
+ /**
492
+ * Toggle expand/collapse state of a tree item with unlimited nesting support
493
+ */
494
+ toggleTreeExpand(item) {
495
+ const treeConfig = this.mergedConfig().tree;
496
+ if (!treeConfig)
497
+ return;
498
+ const { expandedKey = 'isExpanded', primaryKey } = treeConfig;
499
+ const currentExpanded = this.getNestedValue(item, expandedKey) || false;
500
+ const itemId = String(this.getNestedValue(item, primaryKey) || '');
501
+ console.log(`toggleTreeExpand - item: ${itemId}, current expanded: ${currentExpanded}`);
502
+ // Update the expanded state
503
+ const updatedItem = {
504
+ ...item,
505
+ [expandedKey]: !currentExpanded
506
+ };
507
+ // Update the item in the tree structure recursively
508
+ const updateItemInTree = (treeData) => {
509
+ return treeData.map(treeItem => {
510
+ const treeItemId = String(this.getNestedValue(treeItem, primaryKey) || '');
511
+ if (treeItemId === itemId) {
512
+ console.log(`Found item to update: ${itemId}, new expanded state: ${!currentExpanded}`);
513
+ return updatedItem;
514
+ }
515
+ // Check children recursively
516
+ const children = this.getNestedValue(treeItem, 'children') || [];
517
+ if (children.length > 0) {
518
+ const updatedChildren = updateItemInTree(children);
519
+ return {
520
+ ...treeItem,
521
+ children: updatedChildren
522
+ };
523
+ }
524
+ return treeItem;
525
+ });
526
+ };
527
+ // Update the internal data
528
+ const updatedData = updateItemInTree([...this.internalData()]);
529
+ this.internalData.set(updatedData);
530
+ // Re-apply filters to update the display
531
+ this.applyFilters();
532
+ }
533
+ updatePaginationState() {
534
+ const total = Math.ceil(this.totalItems() / this.pageSize());
535
+ this.totalPages.set(total);
536
+ // Ensure current page is within bounds
537
+ if (total === 0) {
538
+ this.currentPage.set(1);
539
+ }
540
+ else if (this.currentPage() > total) {
541
+ this.currentPage.set(total);
542
+ }
543
+ }
544
+ // ===== Pagination Methods =====
545
+ onPageChange(page) {
546
+ console.log('🔄 DataGrid: onPageChange called with page:', page);
547
+ const pageNum = typeof page === 'string' ? parseInt(page, 10) : page;
548
+ console.log('🔄 DataGrid: Parsed page number:', pageNum, 'Total pages:', this.totalPages());
549
+ if (pageNum >= 1 && pageNum <= this.totalPages()) {
550
+ // Set page change loading state
551
+ this.pageChangeLoading.set(true);
552
+ console.log('🔄 DataGrid: Set page change loading to true');
553
+ this.currentPage.set(pageNum);
554
+ if (this.serverSidePagination) {
555
+ const pageData = { page: pageNum, pageSize: this.pageSize() };
556
+ console.log('🔄 DataGrid: Emitting pageChange event for server-side pagination:', pageData);
557
+ // Emit page data object for server-side pagination
558
+ this.emitEvent('pageChange', pageData);
559
+ // Don't clear loading state here - let the parent component handle it
560
+ // The loading state will be cleared when new data arrives via ngOnChanges
561
+ }
562
+ else {
563
+ console.log('🔄 DataGrid: Emitting pageChange event for client-side pagination:', pageNum);
564
+ // Emit simple page number for client-side pagination
565
+ this.emitEvent('pageChange', pageNum);
566
+ // Clear loading state for client-side pagination
567
+ this.clearPageChangeLoading();
568
+ }
569
+ }
570
+ else {
571
+ console.log('🔄 DataGrid: Page number out of range, not emitting event');
572
+ }
573
+ }
574
+ onPageSizeChange() {
575
+ // Set page change loading state
576
+ this.pageChangeLoading.set(true);
577
+ this.currentPage.set(1);
578
+ this.updatePaginationState();
579
+ if (this.serverSidePagination) {
580
+ // Emit page data object for server-side pagination
581
+ this.emitEvent('pageChange', { page: 1, pageSize: this.pageSize() });
582
+ // Don't clear loading state here - let the parent component handle it
583
+ // The loading state will be cleared when new data arrives via ngOnChanges
584
+ }
585
+ else {
586
+ // Emit simple page number for client-side pagination
587
+ this.emitEvent('pageChange', 1);
588
+ // Clear loading state for client-side pagination
589
+ this.clearPageChangeLoading();
590
+ }
591
+ }
592
+ updatePageSize(value) {
593
+ this.pageSize.set(+value);
594
+ this.onPageSizeChange();
595
+ }
596
+ previousPage() {
597
+ if (this.hasPreviousPage()) {
598
+ this.onPageChange(this.currentPage() - 1);
599
+ }
600
+ }
601
+ nextPage() {
602
+ if (this.hasNextPage()) {
603
+ this.onPageChange(this.currentPage() + 1);
604
+ }
605
+ }
606
+ onJumpToPage() {
607
+ if (this.jumpToPage >= 1 && this.jumpToPage <= this.totalPages()) {
608
+ this.onPageChange(this.jumpToPage);
609
+ }
610
+ }
611
+ getEnhancedPageNumbers() {
612
+ const total = this.totalPages();
613
+ const current = this.currentPage();
614
+ const pages = [];
615
+ if (total <= 7) {
616
+ for (let i = 1; i <= total; i++) {
617
+ pages.push(i);
618
+ }
619
+ }
620
+ else {
621
+ pages.push(1);
622
+ if (current <= 4) {
623
+ for (let i = 2; i <= 5; i++) {
624
+ pages.push(i);
625
+ }
626
+ pages.push('...');
627
+ pages.push(total);
628
+ }
629
+ else if (current >= total - 3) {
630
+ pages.push('...');
631
+ for (let i = total - 4; i <= total; i++) {
632
+ pages.push(i);
633
+ }
634
+ }
635
+ else {
636
+ pages.push('...');
637
+ for (let i = current - 1; i <= current + 1; i++) {
638
+ pages.push(i);
639
+ }
640
+ pages.push('...');
641
+ pages.push(total);
642
+ }
643
+ }
644
+ return pages;
645
+ }
646
+ // ===== Search Methods =====
647
+ updateSearchQuery(value) {
648
+ console.log('updateSearchQuery', value);
649
+ const searchValue = value;
650
+ this.searchQuery.set(searchValue);
651
+ // Clear any existing debounce timer
652
+ if (this.searchDebounceTimer) {
653
+ clearTimeout(this.searchDebounceTimer);
654
+ }
655
+ // Apply debounce for search
656
+ const debounceMs = this.mergedConfig().search.debounceMs || 300;
657
+ this.searchDebounceTimer = setTimeout(() => {
658
+ // Only emit search event for server-side pagination
659
+ // Don't trigger any client-side filtering that might cause re-renders
660
+ if (this.serverSidePagination) {
661
+ this.currentPage.set(1);
662
+ this.emitEvent('search', searchValue);
663
+ }
664
+ else {
665
+ this.onSearch();
666
+ }
667
+ }, debounceMs);
668
+ }
669
+ onSearch() {
670
+ this.currentPage.set(1);
671
+ if (this.serverSidePagination) {
672
+ // For server-side pagination, only emit the search event
673
+ // Don't apply client-side filters as the server will handle the search
674
+ this.emitEvent('search', this.searchQuery());
675
+ }
676
+ else {
677
+ // For client-side pagination, apply filters locally
678
+ this.applyFilters();
679
+ this.emitEvent('search', this.searchQuery());
680
+ }
681
+ }
682
+ // ===== Action Methods =====
683
+ onRefresh() {
684
+ this.isRefreshing.set(true);
685
+ this.emitEvent('refresh', null);
686
+ // The parent component should handle the actual refresh and call setRefreshing(false) when done
687
+ // We don't auto-reset here to allow the parent to control the refresh state
688
+ }
689
+ // Method to allow parent components to control the refreshing state
690
+ setRefreshing(isRefreshing) {
691
+ this.isRefreshing.set(isRefreshing);
692
+ }
693
+ // Method to clear page change loading state when new data is received
694
+ clearPageChangeLoading() {
695
+ this.pageChangeLoading.set(false);
696
+ }
697
+ // ===== Tree View Helper Methods =====
698
+ /**
699
+ * Check if tree view is enabled
700
+ */
701
+ isTreeEnabled() {
702
+ const enabled = this.mergedConfig().tree?.enabled || false;
703
+ return enabled;
704
+ }
705
+ /**
706
+ * Get tree configuration
707
+ */
708
+ getTreeConfig() {
709
+ return this.mergedConfig().tree;
710
+ }
711
+ /**
712
+ * Check if an item has children
713
+ */
714
+ hasChildren(item) {
715
+ const treeConfig = this.getTreeConfig();
716
+ if (!treeConfig)
717
+ return false;
718
+ const { hasChildrenKey = 'hasChildren', childrenKey = 'children' } = treeConfig;
719
+ // Check hasChildren property first
720
+ const hasChildren = this.getNestedValue(item, hasChildrenKey);
721
+ if (hasChildren !== undefined) {
722
+ return hasChildren;
723
+ }
724
+ // Fallback to checking children array
725
+ const children = this.getNestedValue(item, childrenKey);
726
+ return children && children.length > 0;
727
+ }
728
+ /**
729
+ * Get item level in tree
730
+ */
731
+ getItemLevel(item) {
732
+ // Always try to get level from standard 'level' property first (set by transformToTree)
733
+ let level = this.getNestedValue(item, 'level');
734
+ // If not found, try tree config level key
735
+ if (level === undefined || level === null) {
736
+ const treeConfig = this.getTreeConfig();
737
+ if (treeConfig) {
738
+ const { levelKey = 'level' } = treeConfig;
739
+ level = this.getNestedValue(item, levelKey) || 0;
740
+ }
741
+ else {
742
+ level = 0;
743
+ }
744
+ }
745
+ return level;
746
+ }
747
+ /**
748
+ * Check if item is expanded
749
+ */
750
+ isItemExpanded(item) {
751
+ const treeConfig = this.getTreeConfig();
752
+ if (!treeConfig)
753
+ return false;
754
+ const { expandedKey = 'isExpanded' } = treeConfig;
755
+ return this.getNestedValue(item, expandedKey) || false;
756
+ }
757
+ /**
758
+ * Get tree indent style
759
+ */
760
+ getTreeIndentStyle(item) {
761
+ const level = this.getItemLevel(item);
762
+ // Specific padding progression: Level 1=12px, Level 2=22px, Level 3=32px, Level 4=52px
763
+ const basePadding = 12; // Base cell padding (replaces tw-px-3)
764
+ let levelPadding = 0;
765
+ switch (level) {
766
+ case 0: // Level 1 (root)
767
+ levelPadding = 0;
768
+ break;
769
+ case 1: // Level 2
770
+ levelPadding = 10;
771
+ break;
772
+ case 2: // Level 3
773
+ levelPadding = 20;
774
+ break;
775
+ case 3: // Level 4
776
+ levelPadding = 40;
777
+ break;
778
+ default: // Level 5+
779
+ levelPadding = 40 + ((level - 3) * 20); // Continue with 20px increments
780
+ break;
781
+ }
782
+ const totalPadding = `${basePadding + levelPadding}px`;
783
+ return totalPadding;
784
+ }
785
+ /**
786
+ * Get tree level background class for visual distinction
787
+ */
788
+ getTreeLevelClass(item) {
789
+ const level = this.getItemLevel(item);
790
+ if (level === 0)
791
+ return 'tree-level-0'; // Root level
792
+ if (level === 1)
793
+ return 'tree-level-1'; // First child level
794
+ if (level === 2)
795
+ return 'tree-level-2'; // Second child level
796
+ return 'tree-level-deep'; // Deep nesting (3+ levels)
797
+ }
798
+ // ===== Drag and Drop Helper Methods =====
799
+ /**
800
+ * Check if drag and drop is enabled
801
+ */
802
+ isDragDropEnabled() {
803
+ return this.dragDropEnabled || this.mergedConfig().dragDrop?.enabled || false;
804
+ }
805
+ /**
806
+ * Get drag and drop configuration
807
+ */
808
+ getDragDropConfig() {
809
+ return this.mergedConfig().dragDrop;
810
+ }
811
+ /**
812
+ * Handle drag start event
813
+ */
814
+ onDragStart(event, item, index) {
815
+ if (!this.isDragDropEnabled())
816
+ return;
817
+ this.isDragging.set(true);
818
+ // Store the current order IDs when drag starts for comparison
819
+ this.originalOrderIds = this.displayedData.map(item => this.getItemId(item)).filter(id => id !== null);
820
+ console.log('🔍 Drag started, current order IDs stored:', this.originalOrderIds);
821
+ const dragConfig = this.getDragDropConfig();
822
+ const dragClass = dragConfig?.dragClass || 'tw-opacity-50 tw-bg-blue-50';
823
+ if (event.dataTransfer) {
824
+ event.dataTransfer.effectAllowed = 'move';
825
+ event.dataTransfer.setData('text/plain', JSON.stringify({ item, index }));
826
+ }
827
+ // Find the closest tr element and apply drag styles
828
+ const rowElement = event.target.closest('tr');
829
+ if (rowElement) {
830
+ dragClass.split(' ').forEach(cls => rowElement.classList.add(cls));
831
+ // Add additional visual feedback
832
+ rowElement.style.transform = 'scale(1.02)';
833
+ rowElement.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)';
834
+ rowElement.style.zIndex = '1000';
835
+ }
836
+ }
837
+ /**
838
+ * Handle drag over event
839
+ */
840
+ onDragOver(event) {
841
+ if (!this.isDragDropEnabled())
842
+ return;
843
+ event.preventDefault();
844
+ event.dataTransfer.dropEffect = 'move';
845
+ // Find the row index from the closest tr element
846
+ const rowElement = event.target.closest('tr');
847
+ if (rowElement) {
848
+ const tbody = rowElement.closest('tbody');
849
+ if (tbody) {
850
+ const rowIndex = Array.from(tbody.children).indexOf(rowElement);
851
+ this.isDragOverRow = rowIndex;
852
+ }
853
+ }
854
+ }
855
+ /**
856
+ * Handle drag leave event
857
+ */
858
+ onDragLeave(event) {
859
+ console.log('onDragLeave', event);
860
+ if (!this.isDragDropEnabled())
861
+ return;
862
+ // Clear drag over row tracking
863
+ this.isDragOverRow = null;
864
+ }
865
+ /**
866
+ * Handle drop event
867
+ */
868
+ onDrop(event, targetItem, targetIndex) {
869
+ if (!this.isDragDropEnabled())
870
+ return;
871
+ event.preventDefault();
872
+ // Clear drag over row tracking
873
+ this.isDragOverRow = null;
874
+ try {
875
+ const data = JSON.parse(event.dataTransfer.getData('text/plain'));
876
+ const sourceItem = data.item;
877
+ const sourceIndex = data.index;
878
+ if (sourceIndex !== targetIndex) {
879
+ const newOrder = this.getNewOrder(sourceIndex, targetIndex);
880
+ console.log('🔍 Drop completed, updating local order only:', { sourceItem, sourceIndex, targetItem, targetIndex, newOrder });
881
+ // Update the row order map with new positions (LOCAL ONLY)
882
+ this.updateRowOrderMap(newOrder);
883
+ // Update local data for visual reordering (LOCAL ONLY)
884
+ this.updateLocalDataOrder(newOrder);
885
+ // Check if any row has changed position
886
+ const hasActuallyChanged = this.checkIfOrderChanged();
887
+ this.hasOrderChanged.set(hasActuallyChanged);
888
+ console.log('🔍 Local order changed:', hasActuallyChanged);
889
+ // IMPORTANT: DO NOT emit reorder event here - only store locally
890
+ // External components will be updated only when "Save Order" is clicked
891
+ console.log('🔍 Order change stored locally - external components not updated until save');
892
+ }
893
+ else {
894
+ console.log('Source and target index are the same, skipping reorder');
895
+ }
896
+ }
897
+ catch (error) {
898
+ console.error('Error parsing drag data:', error);
899
+ }
900
+ }
901
+ /**
902
+ * Handle drag end event
903
+ */
904
+ onDragEnd(event) {
905
+ if (!this.isDragDropEnabled())
906
+ return;
907
+ this.isDragging.set(false);
908
+ // Check if order has changed after drag ends
909
+ const hasActuallyChanged = this.checkIfOrderChanged();
910
+ this.hasOrderChanged.set(hasActuallyChanged);
911
+ const dragConfig = this.getDragDropConfig();
912
+ const dragClass = dragConfig?.dragClass || 'tw-opacity-50 tw-bg-blue-50';
913
+ // Clear drag over row tracking
914
+ this.isDragOverRow = null;
915
+ // Find the closest tr element and remove drag styles
916
+ const rowElement = event.target.closest('tr');
917
+ if (rowElement) {
918
+ dragClass.split(' ').forEach(cls => rowElement.classList.remove(cls));
919
+ // Remove additional visual feedback
920
+ rowElement.style.transform = '';
921
+ rowElement.style.boxShadow = '';
922
+ rowElement.style.zIndex = '';
923
+ }
924
+ }
925
+ /**
926
+ * Calculate new order after reordering
927
+ */
928
+ getNewOrder(sourceIndex, targetIndex) {
929
+ const data = [...this.displayedData];
930
+ const [movedItem] = data.splice(sourceIndex, 1);
931
+ data.splice(targetIndex, 0, movedItem);
932
+ return data;
933
+ }
934
+ /**
935
+ * Get the current order number for a specific item
936
+ */
937
+ getCurrentOrderNumber(item) {
938
+ const itemId = this.getItemId(item);
939
+ if (itemId && this.rowOrderMap.has(itemId)) {
940
+ return this.rowOrderMap.get(itemId).newPosition;
941
+ }
942
+ // Fallback to actual order field value from config
943
+ const actualOrder = this.getItemOrder(item);
944
+ if (actualOrder > 0) {
945
+ return actualOrder;
946
+ }
947
+ // Final fallback to index-based calculation
948
+ const currentIndex = this.displayedData.findIndex(currentItem => {
949
+ if (item && currentItem && typeof item === 'object' && typeof currentItem === 'object') {
950
+ const itemId = item['_id'];
951
+ const currentItemId = currentItem['_id'];
952
+ return itemId && currentItemId && itemId === currentItemId;
953
+ }
954
+ return item === currentItem;
955
+ });
956
+ return currentIndex >= 0 ? currentIndex + 1 : 0;
957
+ }
958
+ /**
959
+ * Update the row order map with new positions after reordering
960
+ */
961
+ updateRowOrderMap(newOrder) {
962
+ newOrder.forEach((item, index) => {
963
+ const itemId = this.getItemId(item);
964
+ if (itemId && this.rowOrderMap.has(itemId)) {
965
+ const currentEntry = this.rowOrderMap.get(itemId);
966
+ // Calculate new order value based on position and original order field
967
+ const dragDropConfig = this.getDragDropConfig();
968
+ if (dragDropConfig?.orderField) {
969
+ // Use the order field from config to calculate new order values
970
+ const baseOrder = Math.min(...this.displayedData.map(item => this.getItemOrder(item)));
971
+ const newOrderValue = baseOrder + index;
972
+ currentEntry.newPosition = newOrderValue;
973
+ }
974
+ else {
975
+ // Fallback to index-based position
976
+ currentEntry.newPosition = index + 1;
977
+ }
978
+ this.rowOrderMap.set(itemId, currentEntry);
979
+ }
980
+ });
981
+ console.log('🔍 Row order map updated with new order values:', Array.from(this.rowOrderMap.entries()));
982
+ }
983
+ /**
984
+ * Check if any row has changed position by comparing old vs new positions
985
+ */
986
+ checkIfOrderChanged() {
987
+ for (const [itemId, orderInfo] of this.rowOrderMap) {
988
+ if (orderInfo.oldPosition !== orderInfo.newPosition) {
989
+ console.log('🔍 Order change detected for item:', itemId, orderInfo);
990
+ return true;
991
+ }
992
+ }
993
+ console.log('🔍 No order change detected');
994
+ return false;
995
+ }
996
+ /**
997
+ * Reset the row order map to original positions (for reset action)
998
+ */
999
+ resetRowOrderMap() {
1000
+ // Get the current full dataset from the config (not cached data)
1001
+ const fullData = this.mergedConfig().data || [];
1002
+ // Completely rebuild the order tracking from current data
1003
+ this.rowOrderMap.clear();
1004
+ this.initialDataOrderIds = [];
1005
+ // Rebuild the order tracking with current data
1006
+ fullData.forEach((item, index) => {
1007
+ const itemId = this.getItemId(item);
1008
+ if (itemId) {
1009
+ const actualOrder = this.getItemOrder(item);
1010
+ this.initialDataOrderIds.push(itemId);
1011
+ this.rowOrderMap.set(itemId, {
1012
+ oldPosition: actualOrder || (index + 1),
1013
+ newPosition: actualOrder || (index + 1)
1014
+ });
1015
+ }
1016
+ });
1017
+ // Clear any local reordered data
1018
+ this.localReorderedData = [];
1019
+ this.hasOrderChanged.set(false);
1020
+ console.log('🔍 Row order map completely rebuilt from current data:', this.initialDataOrderIds.length, 'items');
1021
+ }
1022
+ /**
1023
+ * Update the row order map baseline to current positions (for save action)
1024
+ */
1025
+ updateRowOrderMapBaseline() {
1026
+ // Get the current full dataset from the config
1027
+ const fullData = this.mergedConfig().data || [];
1028
+ // Completely rebuild the order tracking from current data
1029
+ this.rowOrderMap.clear();
1030
+ this.initialDataOrderIds = [];
1031
+ // Rebuild the order tracking with current data
1032
+ fullData.forEach((item, index) => {
1033
+ const itemId = this.getItemId(item);
1034
+ if (itemId) {
1035
+ const actualOrder = this.getItemOrder(item);
1036
+ this.initialDataOrderIds.push(itemId);
1037
+ this.rowOrderMap.set(itemId, {
1038
+ oldPosition: actualOrder || (index + 1),
1039
+ newPosition: actualOrder || (index + 1)
1040
+ });
1041
+ }
1042
+ });
1043
+ console.log('🔍 Row order map baseline completely rebuilt from current data:', this.initialDataOrderIds.length, 'items');
1044
+ }
1045
+ /**
1046
+ * Get the current order array from the row order map
1047
+ */
1048
+ getCurrentOrderFromMap() {
1049
+ const orderedItems = [];
1050
+ // Get the full dataset from the config (not just displayedData)
1051
+ const fullData = this.mergedConfig().data || [];
1052
+ // Collect all items with their new positions from the full dataset
1053
+ this.rowOrderMap.forEach((orderInfo, itemId) => {
1054
+ const item = fullData.find(dataItem => this.getItemId(dataItem) === itemId);
1055
+ if (item) {
1056
+ orderedItems.push({ item, newPosition: orderInfo.newPosition });
1057
+ }
1058
+ });
1059
+ // Sort by new position and return items in order
1060
+ return orderedItems
1061
+ .sort((a, b) => a.newPosition - b.newPosition)
1062
+ .map(orderedItem => orderedItem.item);
1063
+ }
1064
+ /**
1065
+ * Update local data order for visual reordering (LOCAL ONLY)
1066
+ */
1067
+ updateLocalDataOrder(newOrder) {
1068
+ // Store the reordered data locally for visual display
1069
+ this.localReorderedData = [...newOrder];
1070
+ console.log('🔍 Local data order updated for visual display:', this.localReorderedData.length, 'items');
1071
+ }
1072
+ /**
1073
+ * Check if the current order has changed from the initial order
1074
+ */
1075
+ checkOrderChanged(currentOrder) {
1076
+ if (this.initialDataOrderIds.length !== currentOrder.length) {
1077
+ return true;
1078
+ }
1079
+ // Create arrays of IDs to compare order
1080
+ const currentIds = currentOrder.map(item => this.getItemId(item)).filter(id => id !== null);
1081
+ // Compare the sequences
1082
+ for (let i = 0; i < this.initialDataOrderIds.length; i++) {
1083
+ if (this.initialDataOrderIds[i] !== currentIds[i]) {
1084
+ console.log('🔍 Order changed detected:', {
1085
+ position: i,
1086
+ initial: this.initialDataOrderIds[i],
1087
+ current: currentIds[i]
1088
+ });
1089
+ return true;
1090
+ }
1091
+ }
1092
+ console.log('🔍 No order change detected');
1093
+ return false;
1094
+ }
1095
+ onRowClick(item) {
1096
+ if (this.mergedConfig().onRowClick) {
1097
+ this.emitEvent('rowClick', item);
1098
+ }
1099
+ }
1100
+ onActionClick(item, action) {
1101
+ // Handle tree expand/collapse actions
1102
+ if (action.key === 'toggle-expand' && this.mergedConfig().tree?.enabled) {
1103
+ this.toggleTreeExpand(item);
1104
+ }
1105
+ // Handle order management actions
1106
+ if (action.key === 'reset-order') {
1107
+ this.hasOrderChanged.set(false);
1108
+ // Reset the row order map to current positions (LOCAL ONLY)
1109
+ this.resetRowOrderMap();
1110
+ // Clear local reordered data to revert to original display
1111
+ this.localReorderedData = [];
1112
+ console.log('🔍 Reset clicked, row order map reset locally, visual display reverted');
1113
+ }
1114
+ else if (action.key === 'save-order') {
1115
+ this.hasOrderChanged.set(false);
1116
+ // Update the row order map to current positions as new baseline
1117
+ this.updateRowOrderMapBaseline();
1118
+ // Clear local reordered data as it's now the baseline
1119
+ this.localReorderedData = [];
1120
+ console.log('🔍 Save clicked, row order map updated to new baseline, local data cleared');
1121
+ // NOW emit the reorder event to update external components
1122
+ const currentOrder = this.getCurrentOrderFromMap();
1123
+ if (currentOrder.length > 0) {
1124
+ console.log('🔍 Emitting rowReorder event to update external components:', currentOrder);
1125
+ // Use the expected GridEvent structure for rowReorder
1126
+ this.emitEvent('rowReorder', {
1127
+ sourceItem: currentOrder[0], // First item as source
1128
+ sourceIndex: 0, // First position as source
1129
+ targetItem: currentOrder[currentOrder.length - 1], // Last item as target
1130
+ targetIndex: currentOrder.length - 1, // Last position as target
1131
+ newOrder: currentOrder // The complete new order
1132
+ });
1133
+ }
1134
+ }
1135
+ if (this.actionHandlers[action.onClick]) {
1136
+ this.actionHandlers[action.onClick](item, action);
1137
+ }
1138
+ this.emitEvent('action', item, undefined, action);
1139
+ }
1140
+ // ===== Utility Methods =====
1141
+ emitEvent(type, data, column, action) {
1142
+ this.gridEvent.emit({ type, data, column, action });
1143
+ }
1144
+ /**
1145
+ * Get nested value from an object
1146
+ * @param obj - The object to get the nested value from
1147
+ * @param path - The path to the nested value (e.g., 'contact.email')
1148
+ * @returns The nested value or undefined if not found
1149
+ * @example
1150
+ * const obj = { contact: { email: 'test@example.com' } };
1151
+ * const value = getNestedValue(obj, 'contact.email');
1152
+ * // value === 'test@example.com'
1153
+ */
1154
+ getNestedValue(obj, path) {
1155
+ return path.split('.').reduce((current, key) => {
1156
+ return current && typeof current === 'object' ? current[key] : undefined;
1157
+ }, obj);
1158
+ }
1159
+ formatValue(value, column) {
1160
+ if (column.formatter) {
1161
+ if (column.formatter.type === 'custom' && column.formatter.customFunction) {
1162
+ const formatter = this.customFormatters[column.formatter.customFunction];
1163
+ if (formatter) {
1164
+ return formatter(value, column.formatter.format);
1165
+ }
1166
+ }
1167
+ // Built-in formatters
1168
+ switch (column.formatter.type) {
1169
+ case 'date':
1170
+ return this.formatDate(value);
1171
+ case 'currency':
1172
+ return this.formatCurrency(value, column.formatter.format);
1173
+ case 'percentage':
1174
+ return this.formatPercentage(value);
1175
+ default:
1176
+ return String(value || '');
1177
+ }
1178
+ }
1179
+ return String(value || '');
1180
+ }
1181
+ formatDate(value) {
1182
+ if (!value)
1183
+ return 'N/A';
1184
+ const date = new Date(value);
1185
+ return isNaN(date.getTime()) ? 'Invalid Date' : date.toLocaleDateString();
1186
+ }
1187
+ formatCurrency(value, currency = 'USD') {
1188
+ if (value === null || value === undefined)
1189
+ return 'N/A';
1190
+ const num = Number(value);
1191
+ return isNaN(num) ? 'N/A' : new Intl.NumberFormat('en-US', {
1192
+ style: 'currency',
1193
+ currency
1194
+ }).format(num);
1195
+ }
1196
+ formatPercentage(value) {
1197
+ if (value === null || value === undefined)
1198
+ return 'N/A';
1199
+ const num = Number(value);
1200
+ return isNaN(num) ? 'N/A' : `${num}%`;
1201
+ }
1202
+ renderCustomCell(value, row, column) {
1203
+ if (column.renderer && this.customRenderers[column.renderer]) {
1204
+ return this.customRenderers[column.renderer](value, row);
1205
+ }
1206
+ return this.formatValue(value, column);
1207
+ }
1208
+ // Enhanced renderer methods for template support
1209
+ isTemplateRenderer(rendererKey) {
1210
+ return !!this.templateRenderers[rendererKey];
1211
+ }
1212
+ isStringRenderer(rendererKey) {
1213
+ return !!this.customRenderers[rendererKey];
1214
+ }
1215
+ getTemplateRenderer(rendererKey) {
1216
+ return this.templateRenderers[rendererKey];
1217
+ }
1218
+ getStringRenderer(rendererKey) {
1219
+ return this.customRenderers[rendererKey] || null;
1220
+ }
1221
+ getTemplateContext(value, row, column) {
1222
+ return {
1223
+ $implicit: value,
1224
+ row,
1225
+ value,
1226
+ column
1227
+ };
1228
+ }
1229
+ getColumnWidthClass(width) {
1230
+ if (!width || width === 'auto')
1231
+ return '';
1232
+ const widthMap = {
1233
+ 'xs': 'tw-w-16',
1234
+ 'sm': 'tw-w-24',
1235
+ 'md': 'tw-w-32',
1236
+ 'lg': 'tw-w-48',
1237
+ 'xl': 'tw-w-64'
1238
+ };
1239
+ return widthMap[width] || `tw-w-[${width}]`;
1240
+ }
1241
+ getColumnMaxWidthClass(width) {
1242
+ if (!width || width === 'auto')
1243
+ return 'tw-max-w-xs';
1244
+ const maxWidthMap = {
1245
+ 'xs': 'tw-max-w-16',
1246
+ 'sm': 'tw-max-w-24',
1247
+ 'md': 'tw-max-w-32',
1248
+ 'lg': 'tw-max-w-48',
1249
+ 'xl': 'tw-max-w-64'
1250
+ };
1251
+ return maxWidthMap[width] || `tw-max-w-[${width}]`;
1252
+ }
1253
+ getStatusClass(value, statusConfig) {
1254
+ if (!statusConfig)
1255
+ return '';
1256
+ return value === statusConfig.activeValue ? statusConfig.activeClass : statusConfig.inactiveClass;
1257
+ }
1258
+ getStatusText(value, statusConfig) {
1259
+ if (!statusConfig)
1260
+ return String(value || '');
1261
+ return value === statusConfig.activeValue ? statusConfig.activeLabel : statusConfig.inactiveLabel;
1262
+ }
1263
+ getItemRangeText() {
1264
+ const total = this.totalItems();
1265
+ if (this.serverSidePagination) {
1266
+ // For server-side pagination, calculate based on actual displayed data
1267
+ const displayedCount = this.displayedData.length;
1268
+ const start = (this.currentPage() - 1) * this.pageSize() + 1;
1269
+ const end = start + displayedCount - 1;
1270
+ return `${start}-${end} of ${total}`;
1271
+ }
1272
+ else {
1273
+ // For client-side pagination, use the original logic
1274
+ const start = (this.currentPage() - 1) * this.pageSize() + 1;
1275
+ const end = Math.min(this.currentPage() * this.pageSize(), total);
1276
+ return `${start}-${end} of ${total}`;
1277
+ }
1278
+ }
1279
+ getPageSizeOptions() {
1280
+ return this.mergedConfig().pagination.pageSizeOptions.map(size => ({
1281
+ value: size,
1282
+ label: size.toString()
1283
+ }));
1284
+ }
1285
+ trackByFn(index, item) {
1286
+ const trackBy = this.mergedConfig().trackBy || '_id';
1287
+ return this.getNestedValue(item, trackBy) || index;
1288
+ }
1289
+ getSkeletonArray() {
1290
+ return Array.from({ length: this.loadingConfig.skeletonRows }, (_, i) => i);
1291
+ }
1292
+ // ===== Template Helper Methods =====
1293
+ get displayedData() {
1294
+ // If we have local reordered data (from drag & drop), use that for visual display
1295
+ if (this.localReorderedData.length > 0 && this.hasOrderChanged()) {
1296
+ console.log('🔍 Using local reordered data for display:', this.localReorderedData.length, 'items');
1297
+ return this.localReorderedData;
1298
+ }
1299
+ const data = this.filteredData();
1300
+ // For server-side pagination, the server already provides the correct page of data
1301
+ // For client-side pagination, we need to slice the data
1302
+ if (this.serverSidePagination) {
1303
+ return data;
1304
+ }
1305
+ else {
1306
+ const start = (this.currentPage() - 1) * this.pageSize();
1307
+ const end = start + this.pageSize();
1308
+ return data.slice(start, end);
1309
+ }
1310
+ }
1311
+ get columns() {
1312
+ const baseColumns = this.mergedConfig().columns || [];
1313
+ return baseColumns;
1314
+ }
1315
+ get paginationConfig() {
1316
+ return this.mergedConfig().pagination;
1317
+ }
1318
+ get searchConfig() {
1319
+ return this.mergedConfig().search;
1320
+ }
1321
+ get loadingConfig() {
1322
+ return this.mergedConfig().loading;
1323
+ }
1324
+ get scrollConfig() {
1325
+ return this.mergedConfig().scroll;
1326
+ }
1327
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: CideEleDataGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1328
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.7", type: CideEleDataGridComponent, isStandalone: true, selector: "cide-ele-data-grid", inputs: { config: "config", customRenderers: "customRenderers", templateRenderers: "templateRenderers", customFormatters: "customFormatters", actionHandlers: "actionHandlers", serverSidePagination: "serverSidePagination", totalServerItems: "totalServerItems", currentServerPage: "currentServerPage", currentServerPageSize: "currentServerPageSize", dragDropEnabled: "dragDropEnabled" }, outputs: { gridEvent: "gridEvent" }, usesOnChanges: true, ngImport: i0, template: " <!-- Data Grid Component -->\n <div class=\"data-grid-container tw-bg-white tw-shadow tw-rounded-lg tw-overflow-visible tw-flex tw-flex-col\" \n [ngClass]=\"[\n mergedConfig().tableClass || '',\n mergedConfig().fullHeight ? 'tw-h-full' : '',\n isDragDropEnabled() ? 'drag-drop-enabled' : '',\n isTreeEnabled() ? 'tree-enabled' : ''\n ]\">\n \n <!-- Header Section -->\n @if (mergedConfig().title || mergedConfig().subtitle) {\n <div class=\"tw-px-3 tw-py-2 tw-border-b tw-border-gray-200\">\n @if (mergedConfig().title) {\n <h3 class=\"tw-text-base tw-font-semibold tw-text-gray-900\">\n {{ mergedConfig().title }}\n </h3>\n }\n @if (mergedConfig().subtitle) {\n <p class=\"tw-text-sm tw-text-gray-600 tw-mt-0.5\">\n {{ mergedConfig().subtitle }}\n </p>\n }\n </div>\n }\n\n <!-- Search Section -->\n @if (searchConfig.enabled) {\n <div class=\"tw-px-3 tw-py-2 tw-border-b tw-border-gray-200\">\n <div class=\"tw-flex tw-items-center tw-justify-between\">\n <!-- Search Input -->\n <div class=\"tw-max-w-md\">\n <cide-ele-input [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\" id=\"search-input\" type=\"text\"\n [ngModel]=\"searchQuery()\"\n (ngModelChange)=\"updateSearchQuery($event)\"\n [placeholder]=\"searchConfig.placeholder\"\n [disabled]=\"loading() || isRefreshing()\"\n leadingIcon=\"search\"\n fill=\"outline\">\n </cide-ele-input>\n </div>\n \n <!-- Drag Order Actions -->\n @if (isDragDropEnabled() && (isDragging() || hasOrderChanged())) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <button cideEleButton \n variant=\"outline\" \n size=\"sm\" \n (click)=\"onActionClick(null, { key: 'reset-order', label: 'Reset Order', icon: 'undo', variant: 'outline', onClick: 'resetOrder' })\"\n class=\"tw-text-blue-700 tw-border-blue-300 hover:tw-bg-blue-100\">\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">undo</cide-ele-icon>\n Reset Order\n </button>\n <button cideEleButton \n variant=\"primary\" \n size=\"sm\" \n (click)=\"onActionClick(null, { key: 'save-order', label: 'Save Order', icon: 'save', variant: 'primary', onClick: 'saveOrder' })\"\n class=\"tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white\">\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">save</cide-ele-icon>\n Save Order\n </button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Table Section -->\n <div class=\"tw-overflow-x-auto tw-relative\"\n [ngClass]=\"{\n 'tw-flex-1 tw-min-h-0': mergedConfig().fullHeight,\n 'tw-overflow-y-auto': scrollConfig?.enabled,\n 'tw-max-h-full': scrollConfig?.enabled\n }\"\n [style.maxHeight]=\"scrollConfig?.enabled ? scrollConfig?.maxHeight : null\"\n [style.minHeight]=\"scrollConfig?.enabled ? scrollConfig?.minHeight : null\">\n <table class=\"tw-min-w-full tw-divide-y tw-divide-gray-200 tw-h-full tw-table-fixed\"\n [class.empty-table]=\"displayedData.length === 0\"\n [ngClass]=\"{\n 'tw-table-striped': mergedConfig().striped,\n 'tw-border': mergedConfig().bordered,\n 'tw-table-sm': mergedConfig().compact\n }\"\n style=\"table-layout: fixed;\">\n \n <!-- Table Header -->\n <thead class=\"tw-bg-gray-50\" \n [ngClass]=\"[\n mergedConfig().headerClass || '',\n scrollConfig?.enabled && scrollConfig?.stickyHeader ? 'tw-sticky tw-top-0 tw-z-10' : ''\n ]\">\n <tr>\n @for (column of columns; track column.key) {\n <th\n class=\"tw-px-3 tw-py-2 tw-text-left tw-text-xs tw-font-medium tw-text-gray-500 tw-uppercase tw-tracking-wider tw-truncate\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width),\n column.align === 'center' ? 'tw-text-center' : '',\n column.align === 'right' ? 'tw-text-right' : ''\n ]\"\n [title]=\"column.header\">\n {{ column.header }}\n </th>\n }\n </tr>\n </thead>\n\n <!-- Table Body -->\n <tbody class=\"tw-bg-white tw-divide-y tw-divide-gray-200\">\n @if (loading() || isRefreshing() || pageChangeLoading()) {\n <!-- Skeleton Loading Rows -->\n @for (skeletonItem of getSkeletonArray(); track $index) {\n <tr class=\"tw-animate-pulse tw-border-b tw-border-gray-200\">\n @for (column of columns; track column.key) {\n <td class=\"tw-px-3 tw-py-2 tw-whitespace-nowrap\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width)\n ]\">\n <div class=\"tw-h-2 tw-bg-gray-200 tw-rounded tw-w-3/4\"></div>\n </td>\n }\n </tr>\n }\n } @else {\n @for (item of displayedData; track trackByFn($index, item)) {\n <tr class=\"tw-group hover:tw-bg-gray-50 tw-border-b-2 tw-border-gray-200\"\n [ngClass]=\"[\n mergedConfig().rowClass || '',\n isRefreshing() ? 'tw-opacity-60 tw-pointer-events-none' : '',\n isDragDropEnabled() ? 'tw-cursor-move tw-border-2 tw-border-transparent' : '',\n !isDragDropEnabled() ? 'tw-transition-colors tw-duration-150' : '',\n isTreeEnabled() ? getTreeLevelClass(item) : ''\n ]\"\n [style.border-color]=\"isDragOverRow === $index ? '#3b82f6' : 'transparent'\"\n [style.background-color]=\"isDragOverRow === $index ? '#eff6ff' : ''\"\n (click)=\"onRowClick(item)\"\n (keydown.enter)=\"onRowClick(item)\"\n (keydown.space)=\"onRowClick(item)\"\n [class.tw-cursor-pointer]=\"mergedConfig().onRowClick && !isDragDropEnabled()\"\n [tabindex]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 0 : -1\"\n [attr.role]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'button' : null\"\n [attr.aria-label]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'Select row' : null\"\n [draggable]=\"isDragDropEnabled()\"\n (dragstart)=\"onDragStart($event, item, $index)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event, item, $index)\"\n (dragend)=\"onDragEnd($event)\">\n \n @for (column of columns; track column.key) {\n <td class=\"tw-pr-3 tw-py-1 tw-relative\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width),\n mergedConfig().cellClass || '',\n column.align === 'center' ? 'tw-text-center' : '',\n column.align === 'right' ? 'tw-text-right' : '',\n column.truncate !== false ? 'tw-whitespace-nowrap' : 'tw-whitespace-normal'\n ]\"\n [style.paddingLeft]=\"isTreeEnabled() && $index === 0 ? getTreeIndentStyle(item) : '12px'\"\n [style.maxWidth]=\"getColumnMaxWidthClass(column.width) === 'tw-max-w-xs' ? '200px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-sm' ? '300px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-md' ? '400px' : 'none'\"\n [style.minWidth]=\"isTreeEnabled() && $index === 0 ? '150px' : '100px'\">\n <!-- Tree Expand/Collapse Button (only for first column when tree is enabled) -->\n @if (isTreeEnabled() && $index === 0) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <!-- Tree Indentation -->\n <div class=\"tw-flex tw-items-center\">\n @if (hasChildren(item)) {\n <button \n variant=\"outline\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, { key: 'toggle-expand', label: 'Toggle', icon: '', variant: 'ghost', onClick: 'toggle-expand' }); $event.stopPropagation()\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-text-gray-500 hover:tw-text-gray-700 tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [title]=\"isItemExpanded(item) ? 'Collapse' : 'Expand'\">\n <cide-ele-icon \n class=\"tw-w-3 tw-h-3\"\n [class.tw-transition-transform]=\"!isDragDropEnabled()\"\n [class.tw-rotate-90]=\"isItemExpanded(item)\"\n size=\"xs\">\n chevron_right\n </cide-ele-icon>\n </button>\n } @else {\n <div class=\"tw-w-8 tw-h-5 tw-flex tw-items-center tw-justify-center\">\n <!-- <div class=\"tw-w-1 tw-h-1 tw-bg-gray-300 tw-rounded-full\"></div> -->\n </div>\n }\n </div>\n \n <!-- Cell Content -->\n <div class=\"tw-flex-1 tw-w-full\">\n @if (column.type === 'text') {\n <p class=\"tw-text-sm tw-text-gray-900\"\n [class.tw-truncate]=\"column.truncate\"\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </p>\n } @else if (column.type === 'number') {\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'date') {\n <span class=\"tw-text-sm tw-text-gray-600\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'boolean') {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\n </span>\n } @else if (column.type === 'status' && column.statusConfig) {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\n </span>\n } @else if (column.type === 'actions' && column.actions) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n @for (action of column.actions; track action.key) {\n <button\n cideEleButton\n [variant]=\"action.variant || 'ghost'\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\n [title]=\"action.tooltip || action.label\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [ngClass]=\"{\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\n }\">\n @if (action.icon) {\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\n </svg>\n }\n {{ action.label }}\n </button>\n }\n </div>\n } @else if (column.type === 'custom') {\n <!-- Template Renderer -->\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\n <ng-container \n *ngTemplateOutlet=\"getTemplateRenderer(column.renderer)!; \n context: getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </ng-container>\n }\n <!-- String Renderer -->\n @else if (column.renderer && isStringRenderer(column.renderer)) {\n <div [innerHTML]=\"renderCustomCell(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </div>\n }\n <!-- Default rendering -->\n @else {\n <div [innerHTML]=\"renderCustomCell(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </div>\n }\n }\n </div>\n </div>\n } @else {\n <!-- Regular cell content (non-tree or non-first column) -->\n @if (column.type === 'text') {\n <p class=\"tw-text-sm tw-text-gray-900\"\n [class.tw-truncate]=\"column.truncate\"\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </p>\n } @else if (column.type === 'number') {\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'date') {\n <span class=\"tw-text-sm tw-text-gray-600\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'boolean') {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\n </span>\n } @else if (column.type === 'status' && column.statusConfig) {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\n </span>\n } @else if (column.type === 'actions' && column.actions) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n @for (action of column.actions; track action.key) {\n <button\n cideEleButton\n [variant]=\"action.variant || 'ghost'\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\n [title]=\"action.tooltip || action.label\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [ngClass]=\"{\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\n }\">\n @if (action.icon) {\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\n </svg>\n }\n {{ action.label }}\n </button>\n }\n </div>\n } @else if (column.type === 'custom') {\n <!-- Template Renderer -->\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\n <ng-container \n *ngTemplateOutlet=\"getTemplateRenderer(column.renderer)!; \n context: getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </ng-container>\n }\n <!-- String Renderer -->\n @else if (column.renderer && isStringRenderer(column.renderer)) {\n <div [innerHTML]=\"renderCustomCell(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </div>\n }\n <!-- Default rendering -->\n @else {\n <div [innerHTML]=\"renderCustomCell(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </div>\n }\n }\n }\n </td>\n }\n </tr>\n }\n \n <!-- Empty State -->\n @if (displayedData.length === 0) {\n <tr class=\"tw-h-full\">\n <td [attr.colspan]=\"columns.length\" class=\"tw-px-6 tw-py-22 tw-text-center tw-h-full tw-align-middle\">\n <div class=\"tw-text-gray-500 tw-flex tw-flex-col tw-items-center tw-justify-center tw-min-h-[300px]\">\n <svg class=\"tw-mx-auto tw-h-12 tw-w-12 tw-text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"></path>\n </svg>\n <h3 class=\"tw-mt-2 tw-text-sm tw-font-medium tw-text-gray-900\">No data found</h3>\n <p class=\"tw-mt-1 tw-text-sm tw-text-gray-500\">\n @if (searchQuery()) {\n No results match your search criteria.\n } @else {\n There are no items to display.\n }\n </p>\n </div>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n\n <!-- Pagination Section -->\n @if (paginationConfig.enabled && totalItems() > 0) {\n <div class=\"tw-px-3 tw-py-2 tw-border-t tw-border-gray-200 tw-bg-white tw-relative tw-z-20\"\n [class.tw-opacity-60]=\"isRefreshing()\">\n \n <!-- Results Info and Page Size -->\n <div class=\"tw-flex tw-flex-col sm:tw-flex-row tw-justify-between tw-items-start sm:tw-items-center tw-space-y-2 sm:tw-space-y-0\">\n \n <!-- Results Info -->\n @if (paginationConfig.showPageInfo) {\n <div class=\"tw-flex tw-items-center tw-space-x-4\">\n <p class=\"tw-text-sm tw-text-gray-700\">\n Showing {{ getItemRangeText() }} results\n </p>\n \n <!-- Page Size Selector -->\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <span class=\"tw-text-sm tw-text-gray-700\">Per page:</span>\n <div class=\"tw-w-20 tw-relative\">\n <cide-ele-select\n [labelHide]=\"true\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"updatePageSize($event)\"\n [options]=\"getPageSizeOptions()\"\n [disabled]=\"isRefreshing()\"\n fill=\"outline\"\n size=\"sm\"\n class=\"tw-z-30\">\n </cide-ele-select>\n </div>\n </div>\n </div>\n }\n\n <!-- Pagination Controls -->\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\n \n <!-- Previous/Next and Page Numbers -->\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\n \n <!-- First Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(1)\"\n [disabled]=\"currentPage() === 1 || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u00AB\u00AB\n </button>\n \n <!-- Previous Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"previousPage()\"\n [disabled]=\"!hasPreviousPage() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u2039\n </button>\n \n <!-- Page Numbers -->\n @for (page of getEnhancedPageNumbers(); track page) {\n @if (page === '...') {\n <span class=\"tw-px-2 tw-py-2 tw-text-sm tw-text-gray-500\">...</span>\n } @else {\n <button\n cideEleButton\n [variant]=\"currentPage() === page ? 'primary' : 'outline'\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(page)\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-border tw-rounded-md tw-transition-colors\"\n [ngClass]=\"{\n 'tw-bg-blue-600 tw-text-white tw-border-blue-600': currentPage() === page,\n 'tw-bg-white tw-text-gray-700 tw-border-gray-300 hover:tw-bg-gray-50': currentPage() !== page\n }\">\n {{ page }}\n </button>\n }\n }\n \n <!-- Next Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"nextPage()\"\n [disabled]=\"!hasNextPage() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u203A\n </button>\n \n <!-- Last Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(totalPages())\"\n [disabled]=\"currentPage() === totalPages() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u00BB\u00BB\n </button>\n \n </div>\n\n <!-- Quick Jump and Refresh -->\n @if (paginationConfig.showQuickJump || paginationConfig.showRefresh) {\n <div class=\"tw-flex tw-items-center tw-space-x-2 tw-border-l tw-border-gray-200 tw-pl-3\">\n \n <!-- Quick Jump -->\n @if (paginationConfig.showQuickJump) {\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\n <span class=\"tw-text-sm tw-text-gray-700\">Go to:</span>\n <div class=\"tw-w-20\">\n <cide-ele-input id=\"jump-to-page-input\" type=\"number\" [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\"\n [(ngModel)]=\"jumpToPage\" [min]=\"1\" [max]=\"totalPages()\"\n [disabled]=\"isRefreshing()\"\n (keydown.enter)=\"onJumpToPage()\">\n </cide-ele-input>\n </div>\n <button\n cideEleButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onJumpToPage()\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-bg-gray-100 tw-text-gray-700 tw-border tw-border-gray-300 tw-rounded-md hover:tw-bg-gray-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n Go\n </button>\n </div>\n }\n \n <!-- Refresh Button -->\n @if (paginationConfig.showRefresh) {\n <button\n cideEleButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onRefresh()\"\n [disabled]=\"isRefreshing()\"\n class=\"!tw-w-10 !tw-h-8 tw-flex tw-items-center tw-justify-center tw-text-gray-600 tw-bg-gray-100 tw-border tw-border-gray-300 tw-rounded-md hover:tw-bg-gray-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n @if (isRefreshing()) {\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"tw-opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n } @else {\n <svg class=\"tw-h-4 tw-w-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"></path>\n </svg>\n }\n </button>\n }\n </div>\n }\n \n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".data-grid-container{width:100%;display:flex;flex-direction:column}.data-grid-container.tw-h-full{height:100%;min-height:100%}.data-grid-container .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}.data-grid-container .tw-overflow-x-auto.tw-flex-1{flex:1;min-height:0}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar{height:6px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:3px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af}.data-grid-container table{min-height:300px}.data-grid-container.tw-h-full table,.data-grid-container.tw-h-full tbody{height:100%}.data-grid-container.tw-table-striped tbody tr:nth-child(2n){background-color:#f9fafb}.data-grid-container.tw-table-sm th,.data-grid-container.tw-table-sm td{padding:.5rem 1rem}.data-grid-container tbody tr:hover{background-color:#f3f4f6}.data-grid-container tbody tr.tw-cursor-pointer:hover{background-color:#e5f3ff}.data-grid-container.loading-overlay{position:relative}.data-grid-container.loading-overlay:after{content:\"\";position:absolute;inset:0;background:#fffc;z-index:10}.data-grid-container .tw-animate-pulse div{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.data-grid-container .action-buttons{display:flex;gap:.25rem}.data-grid-container .action-buttons button{transition:all .15s ease-in-out}.data-grid-container .action-buttons button:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 2px 4px #0000001a}.data-grid-container .action-buttons button:disabled{cursor:not-allowed;opacity:.5}.data-grid-container .pagination-controls button{transition:all .15s ease-in-out}.data-grid-container .pagination-controls button:hover:not(:disabled){transform:translateY(-1px)}.data-grid-container .pagination-controls button:disabled{cursor:not-allowed}.data-grid-container .pagination-controls button.active{box-shadow:0 2px 4px #3b82f64d}.data-grid-container .pagination-controls input[type=number]{transition:border-color .15s ease-in-out}.data-grid-container .pagination-controls input[type=number]:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.data-grid-container .status-badge{font-weight:500;letter-spacing:.025em}.data-grid-container .status-badge.active{background-color:#d1fae5;color:#065f46}.data-grid-container .status-badge.inactive{background-color:#fee2e2;color:#991b1b}.data-grid-container .search-input{position:relative}.data-grid-container .search-input input{transition:all .15s ease-in-out}.data-grid-container .search-input input:focus{box-shadow:0 0 0 3px #3b82f61a}.data-grid-container .search-input .search-icon{pointer-events:none}.data-grid-container .empty-state{padding:3rem 1.5rem;text-align:center}.data-grid-container .empty-state svg{margin:0 auto 1rem;opacity:.4}.data-grid-container .empty-state h3{margin-bottom:.5rem}.data-grid-container.tw-h-full tbody tr:only-child td{height:100%;vertical-align:middle}.data-grid-container.tw-h-full table.empty-table{height:100%}.data-grid-container.tw-h-full tbody tr:only-child{height:100%}@media (max-width: 640px){.data-grid-container .tw-px-6{padding-left:1rem;padding-right:1rem}.data-grid-container .pagination-controls{flex-direction:column;gap:1rem}.data-grid-container .pagination-controls .flex{justify-content:center}.data-grid-container .pagination-info{display:none}.data-grid-container .search-actions{flex-direction:column;gap:1rem}}@media (prefers-color-scheme: dark){.data-grid-container.dark-mode{background-color:#1f2937;color:#f9fafb}.data-grid-container.dark-mode table{background-color:#1f2937}.data-grid-container.dark-mode thead{background-color:#374151}.data-grid-container.dark-mode tbody tr{border-color:#374151}.data-grid-container.dark-mode tbody tr:hover{background-color:#374151}.data-grid-container.dark-mode .tw-text-gray-900{color:#f9fafb!important}.data-grid-container.dark-mode .tw-text-gray-600{color:#d1d5db!important}.data-grid-container.dark-mode .tw-text-gray-500{color:#9ca3af!important}}.data-grid-container.drag-drop-enabled tbody tr{transition:all .2s ease}.data-grid-container.drag-drop-enabled tbody tr:hover{background-color:#f3f4f6}.data-grid-container.drag-drop-enabled tbody tr.tw-opacity-50{background-color:#dbeafecc;border:2px dashed #3b82f6;border-radius:4px}.data-grid-container.drag-drop-enabled tbody tr.tw-bg-blue-50{background-color:#eff6ffe6}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]{cursor:move;position:relative}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:hover{background-color:#f8fafc;box-shadow:0 1px 3px #0000001a}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:active{cursor:grabbing}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]{position:relative}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]:before{content:\"\";position:absolute;top:-2px;left:0;right:0;height:2px;background:linear-gradient(90deg,#3b82f6,#60a5fa);z-index:10}.data-grid-container.tree-enabled tbody tr td:first-child{position:relative}.data-grid-container.tree-enabled tbody tr td:first-child button{transition:all .15s ease}.data-grid-container.tree-enabled tbody tr td:first-child button:hover{background-color:#f3f4f6;border-radius:2px}.data-grid-container.tree-enabled tbody tr td:first-child button svg{transition:transform .2s ease}.data-grid-container.tree-enabled tbody tr[style*=padding-left]{border-left:2px solid transparent}.data-grid-container.tree-enabled tbody tr[style*=padding-left]:hover{border-left-color:#e5e7eb}.tree-level-0{border-bottom:2px solid #e5e7eb!important;background-color:#fff}.tree-level-0:hover{background-color:#f5f5f5!important}.tree-level-0 td:first-child{font-weight:600}.tree-level-1{border-bottom:1px solid #d1d5db!important;background-color:#fafafa}.tree-level-1:hover{background-color:#f1f5f9!important}.tree-level-1 td:first-child{font-weight:500}.tree-level-2{border-bottom:1px solid #d1d5db!important;background-color:#f9fafb}.tree-level-2:hover{background-color:#f3f4f6!important}.tree-level-2 td:first-child{font-weight:400}.tree-level-deep{border-bottom:1px solid #e5e7eb!important;background-color:#fefefe}.tree-level-deep:hover{background-color:#f9fafb!important}.tree-level-deep td:first-child{font-weight:300}table td{box-sizing:border-box;word-wrap:break-word;overflow-wrap:break-word}table td.tw-overflow-hidden{overflow:hidden;text-overflow:ellipsis}table td:first-child{min-width:150px}table td:first-child>div{display:flex;align-items:center;min-width:0}table td:first-child>div .tw-flex{min-width:0}table td .tw-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: CideInputComponent, selector: "cide-ele-input", inputs: ["fill", "label", "labelHide", "disabled", "clearInput", "labelPlacement", "labelDir", "placeholder", "leadingIcon", "trailingIcon", "helperText", "helperTextCollapse", "hideHelperAndErrorText", "errorText", "maxlength", "minlength", "required", "autocapitalize", "autocomplete", "type", "width", "id", "ngModel", "option", "min", "max", "size"], outputs: ["ngModelChange"] }, { kind: "component", type: CideSelectComponent, selector: "cide-ele-select", inputs: ["label", "labelHide", "placeholder", "helperText", "errorText", "required", "disabled", "id", "ngModel", "size", "fill", "labelPlacement", "labelDir", "leadingIcon", "trailingIcon", "clearInput", "options", "multiple", "searchable", "showSearchInput", "loading"], outputs: ["ngModelChange", "change", "searchChange"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton]", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }] }); }
1329
+ }
1330
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: CideEleDataGridComponent, decorators: [{
1331
+ type: Component,
1332
+ args: [{ selector: 'cide-ele-data-grid', standalone: true, imports: [
1333
+ CommonModule,
1334
+ FormsModule,
1335
+ CideInputComponent,
1336
+ CideSelectComponent,
1337
+ CideIconComponent,
1338
+ CideEleButtonComponent
1339
+ ], template: " <!-- Data Grid Component -->\n <div class=\"data-grid-container tw-bg-white tw-shadow tw-rounded-lg tw-overflow-visible tw-flex tw-flex-col\" \n [ngClass]=\"[\n mergedConfig().tableClass || '',\n mergedConfig().fullHeight ? 'tw-h-full' : '',\n isDragDropEnabled() ? 'drag-drop-enabled' : '',\n isTreeEnabled() ? 'tree-enabled' : ''\n ]\">\n \n <!-- Header Section -->\n @if (mergedConfig().title || mergedConfig().subtitle) {\n <div class=\"tw-px-3 tw-py-2 tw-border-b tw-border-gray-200\">\n @if (mergedConfig().title) {\n <h3 class=\"tw-text-base tw-font-semibold tw-text-gray-900\">\n {{ mergedConfig().title }}\n </h3>\n }\n @if (mergedConfig().subtitle) {\n <p class=\"tw-text-sm tw-text-gray-600 tw-mt-0.5\">\n {{ mergedConfig().subtitle }}\n </p>\n }\n </div>\n }\n\n <!-- Search Section -->\n @if (searchConfig.enabled) {\n <div class=\"tw-px-3 tw-py-2 tw-border-b tw-border-gray-200\">\n <div class=\"tw-flex tw-items-center tw-justify-between\">\n <!-- Search Input -->\n <div class=\"tw-max-w-md\">\n <cide-ele-input [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\" id=\"search-input\" type=\"text\"\n [ngModel]=\"searchQuery()\"\n (ngModelChange)=\"updateSearchQuery($event)\"\n [placeholder]=\"searchConfig.placeholder\"\n [disabled]=\"loading() || isRefreshing()\"\n leadingIcon=\"search\"\n fill=\"outline\">\n </cide-ele-input>\n </div>\n \n <!-- Drag Order Actions -->\n @if (isDragDropEnabled() && (isDragging() || hasOrderChanged())) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <button cideEleButton \n variant=\"outline\" \n size=\"sm\" \n (click)=\"onActionClick(null, { key: 'reset-order', label: 'Reset Order', icon: 'undo', variant: 'outline', onClick: 'resetOrder' })\"\n class=\"tw-text-blue-700 tw-border-blue-300 hover:tw-bg-blue-100\">\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">undo</cide-ele-icon>\n Reset Order\n </button>\n <button cideEleButton \n variant=\"primary\" \n size=\"sm\" \n (click)=\"onActionClick(null, { key: 'save-order', label: 'Save Order', icon: 'save', variant: 'primary', onClick: 'saveOrder' })\"\n class=\"tw-bg-blue-600 hover:tw-bg-blue-700 tw-text-white\">\n <cide-ele-icon size=\"xs\" class=\"tw-w-4 tw-h-4 tw-mr-1\">save</cide-ele-icon>\n Save Order\n </button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Table Section -->\n <div class=\"tw-overflow-x-auto tw-relative\"\n [ngClass]=\"{\n 'tw-flex-1 tw-min-h-0': mergedConfig().fullHeight,\n 'tw-overflow-y-auto': scrollConfig?.enabled,\n 'tw-max-h-full': scrollConfig?.enabled\n }\"\n [style.maxHeight]=\"scrollConfig?.enabled ? scrollConfig?.maxHeight : null\"\n [style.minHeight]=\"scrollConfig?.enabled ? scrollConfig?.minHeight : null\">\n <table class=\"tw-min-w-full tw-divide-y tw-divide-gray-200 tw-h-full tw-table-fixed\"\n [class.empty-table]=\"displayedData.length === 0\"\n [ngClass]=\"{\n 'tw-table-striped': mergedConfig().striped,\n 'tw-border': mergedConfig().bordered,\n 'tw-table-sm': mergedConfig().compact\n }\"\n style=\"table-layout: fixed;\">\n \n <!-- Table Header -->\n <thead class=\"tw-bg-gray-50\" \n [ngClass]=\"[\n mergedConfig().headerClass || '',\n scrollConfig?.enabled && scrollConfig?.stickyHeader ? 'tw-sticky tw-top-0 tw-z-10' : ''\n ]\">\n <tr>\n @for (column of columns; track column.key) {\n <th\n class=\"tw-px-3 tw-py-2 tw-text-left tw-text-xs tw-font-medium tw-text-gray-500 tw-uppercase tw-tracking-wider tw-truncate\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width),\n column.align === 'center' ? 'tw-text-center' : '',\n column.align === 'right' ? 'tw-text-right' : ''\n ]\"\n [title]=\"column.header\">\n {{ column.header }}\n </th>\n }\n </tr>\n </thead>\n\n <!-- Table Body -->\n <tbody class=\"tw-bg-white tw-divide-y tw-divide-gray-200\">\n @if (loading() || isRefreshing() || pageChangeLoading()) {\n <!-- Skeleton Loading Rows -->\n @for (skeletonItem of getSkeletonArray(); track $index) {\n <tr class=\"tw-animate-pulse tw-border-b tw-border-gray-200\">\n @for (column of columns; track column.key) {\n <td class=\"tw-px-3 tw-py-2 tw-whitespace-nowrap\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width)\n ]\">\n <div class=\"tw-h-2 tw-bg-gray-200 tw-rounded tw-w-3/4\"></div>\n </td>\n }\n </tr>\n }\n } @else {\n @for (item of displayedData; track trackByFn($index, item)) {\n <tr class=\"tw-group hover:tw-bg-gray-50 tw-border-b-2 tw-border-gray-200\"\n [ngClass]=\"[\n mergedConfig().rowClass || '',\n isRefreshing() ? 'tw-opacity-60 tw-pointer-events-none' : '',\n isDragDropEnabled() ? 'tw-cursor-move tw-border-2 tw-border-transparent' : '',\n !isDragDropEnabled() ? 'tw-transition-colors tw-duration-150' : '',\n isTreeEnabled() ? getTreeLevelClass(item) : ''\n ]\"\n [style.border-color]=\"isDragOverRow === $index ? '#3b82f6' : 'transparent'\"\n [style.background-color]=\"isDragOverRow === $index ? '#eff6ff' : ''\"\n (click)=\"onRowClick(item)\"\n (keydown.enter)=\"onRowClick(item)\"\n (keydown.space)=\"onRowClick(item)\"\n [class.tw-cursor-pointer]=\"mergedConfig().onRowClick && !isDragDropEnabled()\"\n [tabindex]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 0 : -1\"\n [attr.role]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'button' : null\"\n [attr.aria-label]=\"mergedConfig().onRowClick && !isDragDropEnabled() ? 'Select row' : null\"\n [draggable]=\"isDragDropEnabled()\"\n (dragstart)=\"onDragStart($event, item, $index)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event, item, $index)\"\n (dragend)=\"onDragEnd($event)\">\n \n @for (column of columns; track column.key) {\n <td class=\"tw-pr-3 tw-py-1 tw-relative\"\n [ngClass]=\"[\n getColumnWidthClass(column.width),\n getColumnMaxWidthClass(column.width),\n mergedConfig().cellClass || '',\n column.align === 'center' ? 'tw-text-center' : '',\n column.align === 'right' ? 'tw-text-right' : '',\n column.truncate !== false ? 'tw-whitespace-nowrap' : 'tw-whitespace-normal'\n ]\"\n [style.paddingLeft]=\"isTreeEnabled() && $index === 0 ? getTreeIndentStyle(item) : '12px'\"\n [style.maxWidth]=\"getColumnMaxWidthClass(column.width) === 'tw-max-w-xs' ? '200px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-sm' ? '300px' : getColumnMaxWidthClass(column.width) === 'tw-max-w-md' ? '400px' : 'none'\"\n [style.minWidth]=\"isTreeEnabled() && $index === 0 ? '150px' : '100px'\">\n <!-- Tree Expand/Collapse Button (only for first column when tree is enabled) -->\n @if (isTreeEnabled() && $index === 0) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <!-- Tree Indentation -->\n <div class=\"tw-flex tw-items-center\">\n @if (hasChildren(item)) {\n <button \n variant=\"outline\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, { key: 'toggle-expand', label: 'Toggle', icon: '', variant: 'ghost', onClick: 'toggle-expand' }); $event.stopPropagation()\"\n class=\"tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5 tw-text-gray-500 hover:tw-text-gray-700 tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [title]=\"isItemExpanded(item) ? 'Collapse' : 'Expand'\">\n <cide-ele-icon \n class=\"tw-w-3 tw-h-3\"\n [class.tw-transition-transform]=\"!isDragDropEnabled()\"\n [class.tw-rotate-90]=\"isItemExpanded(item)\"\n size=\"xs\">\n chevron_right\n </cide-ele-icon>\n </button>\n } @else {\n <div class=\"tw-w-8 tw-h-5 tw-flex tw-items-center tw-justify-center\">\n <!-- <div class=\"tw-w-1 tw-h-1 tw-bg-gray-300 tw-rounded-full\"></div> -->\n </div>\n }\n </div>\n \n <!-- Cell Content -->\n <div class=\"tw-flex-1 tw-w-full\">\n @if (column.type === 'text') {\n <p class=\"tw-text-sm tw-text-gray-900\"\n [class.tw-truncate]=\"column.truncate\"\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </p>\n } @else if (column.type === 'number') {\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'date') {\n <span class=\"tw-text-sm tw-text-gray-600\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'boolean') {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\n </span>\n } @else if (column.type === 'status' && column.statusConfig) {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\n </span>\n } @else if (column.type === 'actions' && column.actions) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n @for (action of column.actions; track action.key) {\n <button\n cideEleButton\n [variant]=\"action.variant || 'ghost'\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\n [title]=\"action.tooltip || action.label\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [ngClass]=\"{\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\n }\">\n @if (action.icon) {\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\n </svg>\n }\n {{ action.label }}\n </button>\n }\n </div>\n } @else if (column.type === 'custom') {\n <!-- Template Renderer -->\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\n <ng-container \n *ngTemplateOutlet=\"getTemplateRenderer(column.renderer)!; \n context: getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </ng-container>\n }\n <!-- String Renderer -->\n @else if (column.renderer && isStringRenderer(column.renderer)) {\n <div [innerHTML]=\"renderCustomCell(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </div>\n }\n <!-- Default rendering -->\n @else {\n <div [innerHTML]=\"renderCustomCell(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </div>\n }\n }\n </div>\n </div>\n } @else {\n <!-- Regular cell content (non-tree or non-first column) -->\n @if (column.type === 'text') {\n <p class=\"tw-text-sm tw-text-gray-900\"\n [class.tw-truncate]=\"column.truncate\"\n [title]=\"column.truncate ? (getNestedValue(item, column.valueGetter || column.key) || '').toString() : ''\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </p>\n } @else if (column.type === 'number') {\n <span class=\"tw-text-sm tw-text-gray-900 tw-font-mono\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'date') {\n <span class=\"tw-text-sm tw-text-gray-600\">\n {{ formatValue(getNestedValue(item, column.valueGetter || column.key), column) }}\n </span>\n } @else if (column.type === 'boolean') {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getNestedValue(item, column.valueGetter || column.key) ? 'tw-bg-green-100 tw-text-green-800' : 'tw-bg-red-100 tw-text-red-800'\">\n {{ getNestedValue(item, column.valueGetter || column.key) ? 'Yes' : 'No' }}\n </span>\n } @else if (column.type === 'status' && column.statusConfig) {\n <span class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium\"\n [ngClass]=\"getStatusClass(getNestedValue(item, column.valueGetter || column.key), column.statusConfig)\">\n {{ getStatusText(getNestedValue(item, column.valueGetter || column.key), column.statusConfig) }}\n </span>\n } @else if (column.type === 'actions' && column.actions) {\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n @for (action of column.actions; track action.key) {\n <button\n cideEleButton\n [variant]=\"action.variant || 'ghost'\"\n size=\"xs\"\n type=\"button\"\n (click)=\"onActionClick(item, action); $event.stopPropagation()\"\n [title]=\"action.tooltip || action.label\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-inline-flex tw-items-center tw-px-2 tw-py-2 tw-text-xs tw-font-medium tw-rounded\"\n [class.tw-transition-colors]=\"!isDragDropEnabled()\"\n [ngClass]=\"{\n 'tw-text-gray-700 tw-bg-gray-100 hover:tw-bg-gray-200': action.variant === 'ghost',\n 'tw-text-white tw-bg-blue-600 hover:tw-bg-blue-700': action.variant === 'primary',\n 'tw-text-blue-700 tw-bg-blue-50 tw-border tw-border-blue-200 hover:tw-bg-blue-100': action.variant === 'outline',\n 'tw-text-white tw-bg-red-600 hover:tw-bg-red-700': action.variant === 'danger'\n }\">\n @if (action.icon) {\n <svg class=\"tw-w-3 tw-h-3 tw-mr-1\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path d=\"M10 12l-5-5h10l-5 5z\"/>\n </svg>\n }\n {{ action.label }}\n </button>\n }\n </div>\n } @else if (column.type === 'custom') {\n <!-- Template Renderer -->\n @if (column.renderer && isTemplateRenderer(column.renderer)) {\n <ng-container \n *ngTemplateOutlet=\"getTemplateRenderer(column.renderer)!; \n context: getTemplateContext(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </ng-container>\n }\n <!-- String Renderer -->\n @else if (column.renderer && isStringRenderer(column.renderer)) {\n <div [innerHTML]=\"renderCustomCell(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </div>\n }\n <!-- Default rendering -->\n @else {\n <div [innerHTML]=\"renderCustomCell(getNestedValue(item, column.valueGetter || column.key), item, column)\">\n </div>\n }\n }\n }\n </td>\n }\n </tr>\n }\n \n <!-- Empty State -->\n @if (displayedData.length === 0) {\n <tr class=\"tw-h-full\">\n <td [attr.colspan]=\"columns.length\" class=\"tw-px-6 tw-py-22 tw-text-center tw-h-full tw-align-middle\">\n <div class=\"tw-text-gray-500 tw-flex tw-flex-col tw-items-center tw-justify-center tw-min-h-[300px]\">\n <svg class=\"tw-mx-auto tw-h-12 tw-w-12 tw-text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"></path>\n </svg>\n <h3 class=\"tw-mt-2 tw-text-sm tw-font-medium tw-text-gray-900\">No data found</h3>\n <p class=\"tw-mt-1 tw-text-sm tw-text-gray-500\">\n @if (searchQuery()) {\n No results match your search criteria.\n } @else {\n There are no items to display.\n }\n </p>\n </div>\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n </div>\n\n <!-- Pagination Section -->\n @if (paginationConfig.enabled && totalItems() > 0) {\n <div class=\"tw-px-3 tw-py-2 tw-border-t tw-border-gray-200 tw-bg-white tw-relative tw-z-20\"\n [class.tw-opacity-60]=\"isRefreshing()\">\n \n <!-- Results Info and Page Size -->\n <div class=\"tw-flex tw-flex-col sm:tw-flex-row tw-justify-between tw-items-start sm:tw-items-center tw-space-y-2 sm:tw-space-y-0\">\n \n <!-- Results Info -->\n @if (paginationConfig.showPageInfo) {\n <div class=\"tw-flex tw-items-center tw-space-x-4\">\n <p class=\"tw-text-sm tw-text-gray-700\">\n Showing {{ getItemRangeText() }} results\n </p>\n \n <!-- Page Size Selector -->\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <span class=\"tw-text-sm tw-text-gray-700\">Per page:</span>\n <div class=\"tw-w-20 tw-relative\">\n <cide-ele-select\n [labelHide]=\"true\"\n [ngModel]=\"pageSize()\"\n (ngModelChange)=\"updatePageSize($event)\"\n [options]=\"getPageSizeOptions()\"\n [disabled]=\"isRefreshing()\"\n fill=\"outline\"\n size=\"sm\"\n class=\"tw-z-30\">\n </cide-ele-select>\n </div>\n </div>\n </div>\n }\n\n <!-- Pagination Controls -->\n <div class=\"tw-flex tw-items-center tw-space-x-3\">\n \n <!-- Previous/Next and Page Numbers -->\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\n \n <!-- First Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(1)\"\n [disabled]=\"currentPage() === 1 || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u00AB\u00AB\n </button>\n \n <!-- Previous Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"previousPage()\"\n [disabled]=\"!hasPreviousPage() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u2039\n </button>\n \n <!-- Page Numbers -->\n @for (page of getEnhancedPageNumbers(); track page) {\n @if (page === '...') {\n <span class=\"tw-px-2 tw-py-2 tw-text-sm tw-text-gray-500\">...</span>\n } @else {\n <button\n cideEleButton\n [variant]=\"currentPage() === page ? 'primary' : 'outline'\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(page)\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-border tw-rounded-md tw-transition-colors\"\n [ngClass]=\"{\n 'tw-bg-blue-600 tw-text-white tw-border-blue-600': currentPage() === page,\n 'tw-bg-white tw-text-gray-700 tw-border-gray-300 hover:tw-bg-gray-50': currentPage() !== page\n }\">\n {{ page }}\n </button>\n }\n }\n \n <!-- Next Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"nextPage()\"\n [disabled]=\"!hasNextPage() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u203A\n </button>\n \n <!-- Last Page -->\n <button\n cideEleButton\n variant=\"ghost\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onPageChange(totalPages())\"\n [disabled]=\"currentPage() === totalPages() || isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-lg tw-text-gray-500 hover:tw-text-gray-700 hover:tw-bg-gray-100 tw-rounded-md tw-transition-colors disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n \u00BB\u00BB\n </button>\n \n </div>\n\n <!-- Quick Jump and Refresh -->\n @if (paginationConfig.showQuickJump || paginationConfig.showRefresh) {\n <div class=\"tw-flex tw-items-center tw-space-x-2 tw-border-l tw-border-gray-200 tw-pl-3\">\n \n <!-- Quick Jump -->\n @if (paginationConfig.showQuickJump) {\n <div class=\"tw-flex tw-items-center tw-space-x-1\">\n <span class=\"tw-text-sm tw-text-gray-700\">Go to:</span>\n <div class=\"tw-w-20\">\n <cide-ele-input id=\"jump-to-page-input\" type=\"number\" [labelHide]=\"true\" [hideHelperAndErrorText]=\"true\"\n [(ngModel)]=\"jumpToPage\" [min]=\"1\" [max]=\"totalPages()\"\n [disabled]=\"isRefreshing()\"\n (keydown.enter)=\"onJumpToPage()\">\n </cide-ele-input>\n </div>\n <button\n cideEleButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onJumpToPage()\"\n [disabled]=\"isRefreshing()\"\n class=\"tw-px-3 tw-py-2 tw-text-sm tw-bg-gray-100 tw-text-gray-700 tw-border tw-border-gray-300 tw-rounded-md hover:tw-bg-gray-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n Go\n </button>\n </div>\n }\n \n <!-- Refresh Button -->\n @if (paginationConfig.showRefresh) {\n <button\n cideEleButton\n variant=\"outline\"\n size=\"sm\"\n type=\"button\"\n (click)=\"onRefresh()\"\n [disabled]=\"isRefreshing()\"\n class=\"!tw-w-10 !tw-h-8 tw-flex tw-items-center tw-justify-center tw-text-gray-600 tw-bg-gray-100 tw-border tw-border-gray-300 tw-rounded-md hover:tw-bg-gray-200 disabled:tw-opacity-50 disabled:tw-cursor-not-allowed\">\n @if (isRefreshing()) {\n <svg class=\"tw-animate-spin tw-h-4 tw-w-4\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"tw-opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"tw-opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n } @else {\n <svg class=\"tw-h-4 tw-w-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"></path>\n </svg>\n }\n </button>\n }\n </div>\n }\n \n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".data-grid-container{width:100%;display:flex;flex-direction:column}.data-grid-container.tw-h-full{height:100%;min-height:100%}.data-grid-container .tw-overflow-x-auto{scrollbar-width:thin;scrollbar-color:#d1d5db #f9fafb}.data-grid-container .tw-overflow-x-auto.tw-flex-1{flex:1;min-height:0}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar{height:6px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-track{background:#f9fafb}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:3px}.data-grid-container .tw-overflow-x-auto::-webkit-scrollbar-thumb:hover{background:#9ca3af}.data-grid-container table{min-height:300px}.data-grid-container.tw-h-full table,.data-grid-container.tw-h-full tbody{height:100%}.data-grid-container.tw-table-striped tbody tr:nth-child(2n){background-color:#f9fafb}.data-grid-container.tw-table-sm th,.data-grid-container.tw-table-sm td{padding:.5rem 1rem}.data-grid-container tbody tr:hover{background-color:#f3f4f6}.data-grid-container tbody tr.tw-cursor-pointer:hover{background-color:#e5f3ff}.data-grid-container.loading-overlay{position:relative}.data-grid-container.loading-overlay:after{content:\"\";position:absolute;inset:0;background:#fffc;z-index:10}.data-grid-container .tw-animate-pulse div{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.data-grid-container .action-buttons{display:flex;gap:.25rem}.data-grid-container .action-buttons button{transition:all .15s ease-in-out}.data-grid-container .action-buttons button:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 2px 4px #0000001a}.data-grid-container .action-buttons button:disabled{cursor:not-allowed;opacity:.5}.data-grid-container .pagination-controls button{transition:all .15s ease-in-out}.data-grid-container .pagination-controls button:hover:not(:disabled){transform:translateY(-1px)}.data-grid-container .pagination-controls button:disabled{cursor:not-allowed}.data-grid-container .pagination-controls button.active{box-shadow:0 2px 4px #3b82f64d}.data-grid-container .pagination-controls input[type=number]{transition:border-color .15s ease-in-out}.data-grid-container .pagination-controls input[type=number]:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.data-grid-container .status-badge{font-weight:500;letter-spacing:.025em}.data-grid-container .status-badge.active{background-color:#d1fae5;color:#065f46}.data-grid-container .status-badge.inactive{background-color:#fee2e2;color:#991b1b}.data-grid-container .search-input{position:relative}.data-grid-container .search-input input{transition:all .15s ease-in-out}.data-grid-container .search-input input:focus{box-shadow:0 0 0 3px #3b82f61a}.data-grid-container .search-input .search-icon{pointer-events:none}.data-grid-container .empty-state{padding:3rem 1.5rem;text-align:center}.data-grid-container .empty-state svg{margin:0 auto 1rem;opacity:.4}.data-grid-container .empty-state h3{margin-bottom:.5rem}.data-grid-container.tw-h-full tbody tr:only-child td{height:100%;vertical-align:middle}.data-grid-container.tw-h-full table.empty-table{height:100%}.data-grid-container.tw-h-full tbody tr:only-child{height:100%}@media (max-width: 640px){.data-grid-container .tw-px-6{padding-left:1rem;padding-right:1rem}.data-grid-container .pagination-controls{flex-direction:column;gap:1rem}.data-grid-container .pagination-controls .flex{justify-content:center}.data-grid-container .pagination-info{display:none}.data-grid-container .search-actions{flex-direction:column;gap:1rem}}@media (prefers-color-scheme: dark){.data-grid-container.dark-mode{background-color:#1f2937;color:#f9fafb}.data-grid-container.dark-mode table{background-color:#1f2937}.data-grid-container.dark-mode thead{background-color:#374151}.data-grid-container.dark-mode tbody tr{border-color:#374151}.data-grid-container.dark-mode tbody tr:hover{background-color:#374151}.data-grid-container.dark-mode .tw-text-gray-900{color:#f9fafb!important}.data-grid-container.dark-mode .tw-text-gray-600{color:#d1d5db!important}.data-grid-container.dark-mode .tw-text-gray-500{color:#9ca3af!important}}.data-grid-container.drag-drop-enabled tbody tr{transition:all .2s ease}.data-grid-container.drag-drop-enabled tbody tr:hover{background-color:#f3f4f6}.data-grid-container.drag-drop-enabled tbody tr.tw-opacity-50{background-color:#dbeafecc;border:2px dashed #3b82f6;border-radius:4px}.data-grid-container.drag-drop-enabled tbody tr.tw-bg-blue-50{background-color:#eff6ffe6}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]{cursor:move;position:relative}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:hover{background-color:#f8fafc;box-shadow:0 1px 3px #0000001a}.data-grid-container.drag-drop-enabled tbody tr[draggable=true]:active{cursor:grabbing}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]{position:relative}.data-grid-container.drag-drop-enabled tbody tr[style*=border-top]:before{content:\"\";position:absolute;top:-2px;left:0;right:0;height:2px;background:linear-gradient(90deg,#3b82f6,#60a5fa);z-index:10}.data-grid-container.tree-enabled tbody tr td:first-child{position:relative}.data-grid-container.tree-enabled tbody tr td:first-child button{transition:all .15s ease}.data-grid-container.tree-enabled tbody tr td:first-child button:hover{background-color:#f3f4f6;border-radius:2px}.data-grid-container.tree-enabled tbody tr td:first-child button svg{transition:transform .2s ease}.data-grid-container.tree-enabled tbody tr[style*=padding-left]{border-left:2px solid transparent}.data-grid-container.tree-enabled tbody tr[style*=padding-left]:hover{border-left-color:#e5e7eb}.tree-level-0{border-bottom:2px solid #e5e7eb!important;background-color:#fff}.tree-level-0:hover{background-color:#f5f5f5!important}.tree-level-0 td:first-child{font-weight:600}.tree-level-1{border-bottom:1px solid #d1d5db!important;background-color:#fafafa}.tree-level-1:hover{background-color:#f1f5f9!important}.tree-level-1 td:first-child{font-weight:500}.tree-level-2{border-bottom:1px solid #d1d5db!important;background-color:#f9fafb}.tree-level-2:hover{background-color:#f3f4f6!important}.tree-level-2 td:first-child{font-weight:400}.tree-level-deep{border-bottom:1px solid #e5e7eb!important;background-color:#fefefe}.tree-level-deep:hover{background-color:#f9fafb!important}.tree-level-deep td:first-child{font-weight:300}table td{box-sizing:border-box;word-wrap:break-word;overflow-wrap:break-word}table td.tw-overflow-hidden{overflow:hidden;text-overflow:ellipsis}table td:first-child{min-width:150px}table td:first-child>div{display:flex;align-items:center;min-width:0}table td:first-child>div .tw-flex{min-width:0}table td .tw-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}\n"] }]
1340
+ }], propDecorators: { config: [{
1341
+ type: Input
1342
+ }], customRenderers: [{
1343
+ type: Input
1344
+ }], templateRenderers: [{
1345
+ type: Input
1346
+ }], customFormatters: [{
1347
+ type: Input
1348
+ }], actionHandlers: [{
1349
+ type: Input
1350
+ }], serverSidePagination: [{
1351
+ type: Input
1352
+ }], totalServerItems: [{
1353
+ type: Input
1354
+ }], currentServerPage: [{
1355
+ type: Input
1356
+ }], currentServerPageSize: [{
1357
+ type: Input
1358
+ }], dragDropEnabled: [{
1359
+ type: Input
1360
+ }], gridEvent: [{
1361
+ type: Output
1362
+ }] } });
1363
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YS1ncmlkLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2Nsb3VkLWlkZS1lbGVtZW50L3NyYy9saWIvY29tcG9uZW50cy9kYXRhLWdyaWQvZGF0YS1ncmlkLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2Nsb3VkLWlkZS1lbGVtZW50L3NyYy9saWIvY29tcG9uZW50cy9kYXRhLWdyaWQvZGF0YS1ncmlkLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQTZELE1BQU0sRUFBRSxRQUFRLEVBQWUsTUFBTSxlQUFlLENBQUM7QUFDakssT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUM3QyxPQUFPLEVBS0wsbUJBQW1CLEVBR3BCLE1BQU0sbUJBQW1CLENBQUM7QUFDM0IsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sc0NBQXNDLENBQUM7QUFDMUUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDN0UsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFFdkUsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0scUJBQXFCLENBQUM7Ozs7QUE0QjdELE1BQU0sT0FBTyx3QkFBd0I7SUFkckM7UUFnQlcsb0JBQWUsR0FBc0MsRUFBRSxDQUFDO1FBQ3hELHNCQUFpQixHQUE0QixFQUFFLENBQUM7UUFDaEQscUJBQWdCLEdBQWdFLEVBQUUsQ0FBQztRQUNuRixtQkFBYyxHQUEyRCxFQUFFLENBQUM7UUFDNUUseUJBQW9CLEdBQUcsS0FBSyxDQUFDO1FBQzdCLHFCQUFnQixHQUFHLENBQUMsQ0FB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TUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgSW5wdXQsIE91dHB1dCwgRXZlbnRFbWl0dGVyLCBPbkluaXQsIE9uQ2hhbmdlcywgT25EZXN0cm95LCBTaW1wbGVDaGFuZ2VzLCBTaW1wbGVDaGFuZ2UsIHNpZ25hbCwgY29tcHV0ZWQsIFRlbXBsYXRlUmVmIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgeyBcbiAgR3JpZENvbmZpZ3VyYXRpb24sIFxuICBHcmlkQ29sdW1uLCBcbiAgR3JpZEV2ZW50LCBcbiAgR3JpZEFjdGlvbixcbiAgREVGQVVMVF9HUklEX0NPTkZJRyxcbiAgQ29sdW1uV2lkdGgsXG4gIFN0YXR1c0NvbmZpZ1xufSBmcm9tICcuL2RhdGEtZ3JpZC50eXBlcyc7XG5pbXBvcnQgeyBDaWRlSW5wdXRDb21wb25lbnQgfSBmcm9tICcuLi8uLi9lbGVtZW50cy9pbnB1dC9pbnB1dC5jb21wb25lbnQnO1xuaW1wb3J0IHsgQ2lkZVNlbGVjdENvbXBvbmVudCB9IGZyb20gJy4uLy4uL2VsZW1lbnRzL3NlbGVjdC9zZWxlY3QuY29tcG9uZW50JztcbmltcG9ydCB7IENpZGVJY29uQ29tcG9uZW50IH0gZnJvbSAnLi4vLi4vZWxlbWVudHMvaWNvbi9pY29uLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBpbnB1dFR5cGUgfSBmcm9tICdjbG91ZC1pZGUtbG1zLW1vZGVsJztcbmltcG9ydCB7IENpZGVFbGVCdXR0b25Db21wb25lbnQgfSBmcm9tICcuLi8uLi8uLi9wdWJsaWMtYXBpJztcblxuLy8gRW5oYW5jZWQgdHlwZXMgZm9yIHRlbXBsYXRlIHN1cHBvcnRcbmV4cG9ydCBpbnRlcmZhY2UgVGVtcGxhdGVDb250ZXh0PFQgPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj4ge1xuICAkaW1wbGljaXQ6IHVua25vd247XG4gIHJvdzogVDtcbiAgdmFsdWU6IHVua25vd247XG4gIGNvbHVtbjogR3JpZENvbHVtbjtcbn1cblxuZXhwb3J0IHR5cGUgVGVtcGxhdGVSZW5kZXJlcjxUID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4+ID0gVGVtcGxhdGVSZWY8VGVtcGxhdGVDb250ZXh0PFQ+PjtcbmV4cG9ydCB0eXBlIFN0cmluZ1JlbmRlcmVyPFQgPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj4gPSAodmFsdWU6IHVua25vd24sIHJvdzogVCkgPT4gc3RyaW5nO1xuZXhwb3J0IHR5cGUgUmVuZGVyZXI8VCA9IFJlY29yZDxzdHJpbmcsIHVua25vd24+PiA9IFN0cmluZ1JlbmRlcmVyPFQ+IHwgVGVtcGxhdGVSZW5kZXJlcjxUPjtcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnY2lkZS1lbGUtZGF0YS1ncmlkJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIENvbW1vbk1vZHVsZSwgXG4gICAgRm9ybXNNb2R1bGUsXG4gICAgQ2lkZUlucHV0Q29tcG9uZW50LFxuICAgIENpZGVTZWxlY3RDb21wb25lbnQsXG4gICAgQ2lkZUljb25Db21wb25lbnQsXG4gICAgQ2lkZUVsZUJ1dHRvbkNvbXBvbmVudFxuICBdLFxuICB0ZW1wbGF0ZVVybDogJy4vZGF0YS1ncmlkLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vZGF0YS1ncmlkLmNvbXBvbmVudC5zY3NzJ11cbn0pXG5leHBvcnQgY2xhc3MgQ2lkZUVsZURhdGFHcmlkQ29tcG9uZW50PFQgPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj4gaW1wbGVtZW50cyBPbkluaXQsIE9uQ2hhbmdlcywgT25EZXN0cm95IHtcbiAgQElucHV0KCkgY29uZmlnITogR3JpZENvbmZpZ3VyYXRpb248VD47XG4gIEBJbnB1dCgpIGN1c3RvbVJlbmRlcmVyczogUmVjb3JkPHN0cmluZywgU3RyaW5nUmVuZGVyZXI8VD4+ID0ge307XG4gIEBJbnB1dCgpIHRlbXBsYXRlUmVuZGVyZXJzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuICBASW5wdXQoKSBjdXN0b21Gb3JtYXR0ZXJzOiBSZWNvcmQ8c3RyaW5nLCAodmFsdWU6IHVua25vd24sIGZvcm1hdD86IHN0cmluZykgPT4gc3RyaW5nPiA9IHt9O1xuICBASW5wdXQoKSBhY3Rpb25IYW5kbGVyczogUmVjb3JkPHN0cmluZywgKGRhdGE6IFQsIGFjdGlvbj86IEdyaWRBY3Rpb24pID0+IHZvaWQ+ID0ge307XG4gIEBJbnB1dCgpIHNlcnZlclNpZGVQYWdpbmF0aW9uID0gZmFsc2U7XG4gIEBJbnB1dCgpIHRvdGFsU2VydmVySXRlbXMgPSAwO1xuICBASW5wdXQoKSBjdXJyZW50U2VydmVyUGFnZSA9IDE7XG4gIEBJbnB1dCgpIGN1cnJlbnRTZXJ2ZXJQYWdlU2l6ZSA9IDEwO1xuICBASW5wdXQoKSBkcmFnRHJvcEVuYWJsZWQgPSBmYWxzZTtcblxuICBAT3V0cHV0KCkgZ3JpZEV2ZW50ID0gbmV3IEV2ZW50RW1pdHRlcjxHcmlkRXZlbnQ8VD4+KCk7XG5cbiAgLy8gSW50ZXJuYWwgc3RhdGUgdXNpbmcgQW5ndWxhciBTaWduYWxzXG4gIHByaXZhdGUgaW50ZXJuYWxEYXRhID0gc2lnbmFsPFRbXT4oW10pO1xuICBwcml2YXRlIGZpbHRlcmVkRGF0YSA9IHNpZ25hbDxUW10+KFtdKTtcbiAgXG4gIC8vIEdyaWQgc3RhdGVcbiAgY3VycmVudFBhZ2UgPSBzaWduYWwoMSk7XG4gIHBhZ2VTaXplID0gc2lnbmFsKDEwKTtcbiAgdG90YWxJdGVtcyA9IHNpZ25hbCgwKTtcbiAgdG90YWxQYWdlcyA9IHNpZ25hbCgwKTtcbiAgc2VhcmNoUXVlcnkgPSBzaWduYWwoJycpO1xuICBsb2FkaW5nID0gc2lnbmFsKGZhbHNlKTtcbiAgaXNSZWZyZXNoaW5nID0gc2lnbmFsKGZhbHNlKTtcbiAgcGFnZUNoYW5nZUxvYWRpbmcgPSBzaWduYWwoZmFsc2UpO1xuICBqdW1wVG9QYWdlID0gMTtcbiAgaXNEcmFnT3ZlclJvdzogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG4gIGlzRHJhZ2dpbmcgPSBzaWduYWwoZmFsc2UpO1xuICBoYXNPcmRlckNoYW5nZWQgPSBzaWduYWwoZmFsc2UpO1xuICBwcml2YXRlIG9yaWdpbmFsT3JkZXJJZHM6IHN0cmluZ1tdID0gW107IC8vIFN0b3JlIG9ubHkgSURzIGZvciBvcmRlciB0cmFja2luZ1xuICBwcml2YXRlIGluaXRpYWxEYXRhT3JkZXJJZHM6IHN0cmluZ1tdID0gW107IC8vIFN0b3JlIG9ubHkgSURzIGZvciBpbml0aWFsIG9yZGVyXG4gIHByaXZhdGUgcm93T3JkZXJNYXAgPSBuZXcgTWFwPHN0cmluZywgeyBvbGRQb3NpdGlvbjogbnVtYmVyOyBuZXdQb3NpdGlvbjogbnVtYmVyIH0+KCk7XG4gIHByaXZhdGUgbG9jYWxSZW9yZGVyZWREYXRhOiBUW10gPSBbXTsgLy8gTG9jYWwgZGF0YSBmb3IgdmlzdWFsIHJlb3JkZXJpbmdcbiAgcHJpdmF0ZSBzZWFyY2hEZWJvdW5jZVRpbWVyPzogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD47XG4gIHByaXZhdGUgaXNEYXRhVXBkYXRlID0gZmFsc2U7XG5cbiAgLy8gQ29tcHV0ZWQgcHJvcGVydGllc1xuICBoYXNOZXh0UGFnZSA9IGNvbXB1dGVkKCgpID0+IHRoaXMuY3VycmVudFBhZ2UoKSA8IHRoaXMudG90YWxQYWdlcygpKTtcbiAgaGFzUHJldmlvdXNQYWdlID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jdXJyZW50UGFnZSgpID4gMSk7XG4gIFxuICAvLyBNZXJnZWQgY29uZmlndXJhdGlvbiB3aXRoIGRlZmF1bHRzXG4gIG1lcmdlZENvbmZpZyA9IHNpZ25hbDxHcmlkQ29uZmlndXJhdGlvbjxUPj4odGhpcy5jb25maWcpO1xuXG4gIG5nT25Jbml0KCkge1xuICAgIHRoaXMuaW5pdGlhbGl6ZUdyaWQoKTtcbiAgICB0aGlzLmluaXRpYWxpemVPcmlnaW5hbE9yZGVyKCk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpIHtcbiAgICAvLyBDbGVhbiB1cCBzZWFyY2ggZGVib3VuY2UgdGltZXJcbiAgICBpZiAodGhpcy5zZWFyY2hEZWJvdW5jZVRpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5zZWFyY2hEZWJvdW5jZVRpbWVyKTtcbiAgICB9XG4gIH1cblxuICBuZ09uQ2hhbmdlcyhjaGFuZ2VzOiBTaW1wbGVDaGFuZ2VzKSB7XG4gICAgaWYgKGNoYW5nZXNbJ2NvbmZpZyddKSB7XG4gICAgICAvLyBTdG9yZSBjdXJyZW50IHNlYXJjaCBxdWVyeSB0byBwcmVzZXJ2ZSBpdCBkdXJpbmcgcmUtaW5pdGlhbGl6YXRpb25cbiAgICAgIGNvbnN0IGN1cnJlbnRTZWFyY2hRdWVyeSA9IHRoaXMuc2VhcmNoUXVlcnkoKTtcbiAgICAgIFxuICAgICAgdGhpcy5tZXJnZUNvbmZpZ1dpdGhEZWZhdWx0cygpO1xuICAgICAgXG4gICAgICAvLyBDaGVjayBpZiB0aGlzIGlzIGEgZGF0YS1vbmx5IHVwZGF0ZSAoc2FtZSBjb25maWcgZXhjZXB0IGRhdGEpXG4gICAgICBjb25zdCBpc0RhdGFPbmx5VXBkYXRlID0gdGhpcy5pc0RhdGFPbmx5VXBkYXRlKGNoYW5nZXNbJ2NvbmZpZyddKTtcbiAgICAgIFxuICAgICAgaWYgKGlzRGF0YU9ubHlVcGRhdGUgJiYgdGhpcy5zZXJ2ZXJTaWRlUGFnaW5hdGlvbikge1xuICAgICAgICBjb25zb2xlLmxvZygn8J+UhCBEYXRhR3JpZDogaXNEYXRhT25seVVwZGF0ZScsIGlzRGF0YU9ubHlVcGRhdGUpO1xuICAgICAgICAvLyBGb3IgZGF0YS1vbmx5IHVwZGF0ZXMsIHByZXNlcnZlIHNlYXJjaCBxdWVyeSBhbmQgdXNlIHVwZGF0ZURhdGFPbmx5XG4gICAgICAgIHRoaXMudXBkYXRlRGF0YU9ubHkoY2hhbmdlc1snY29uZmlnJ10uY3VycmVudFZhbHVlLmRhdGEgfHwgW10pO1xuICAgICAgICBpZiAoY3VycmVudFNlYXJjaFF1ZXJ5KSB7XG4gICAgICAgICAgLy8gdGhpcy5zZWFyY2hRdWVyeS5zZXQoY3VycmVudFNlYXJjaFF1ZXJ5KTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gRm9yIGZ1bGwgY29uZmlnIGNoYW5nZXMsIHVzZSBpbml0aWFsaXplR3JpZFxuICAgICAgICB0aGlzLmluaXRpYWxpemVHcmlkKCk7XG4gICAgICAgIFxuICAgICAgICAvLyBSZXN0b3JlIHNlYXJjaCBxdWVyeSBhZnRlciBpbml0aWFsaXphdGlvblxuICAgICAgICBpZiAoY3VycmVudFNlYXJjaFF1ZXJ5KSB7XG4gICAgICAgICAgLy8gdGhpcy5zZWFyY2hRdWVyeS5zZXQoY3VycmVudFNlYXJjaFF1ZXJ5KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBSZWluaXRpYWxpemUgb3JpZ2luYWwgb3JkZXIgd2hlbiBkYXRhIGNoYW5nZXNcbiAgICAgIHRoaXMuaW5pdGlhbGl6ZU9yaWdpbmFsT3JkZXIoKTtcbiAgICAgIFxuICAgICAgLy8gQWxzbyByZWZyZXNoIG9yZGVyIHRyYWNraW5nIHRvIGVuc3VyZSBpdCdzIHVwIHRvIGRhdGVcbiAgICAgIHRoaXMucmVmcmVzaE9yZGVyVHJhY2tpbmcoKTtcbiAgICB9XG4gICAgXG4gICAgaWYgKHRoaXMuc2VydmVyU2lkZVBhZ2luYXRpb24pIHtcbiAgICAgIGlmIChjaGFuZ2VzWyd0b3RhbFNlcnZlckl0ZW1zJ10pIHtcbiAgICAgICAgLy8gY29uc29sZS5sb2coJ/CflIQgRGF0YUdyaWQ6IHRvdGFsU2VydmVySXRlbXMgY2hhbmdlZDonLCB0aGlzLnRvdGFsU2VydmVySXRlbXMpO1xuICAgICAgICB0aGlzLnRvdGFsSXRlbXMuc2V0KHRoaXMudG90YWxTZXJ2ZXJJdGVtcyk7XG4gICAgICAgIHRoaXMudXBkYXRlUGFnaW5hdGlvblN0YXRlKCk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIGlmIChjaGFuZ2VzWydjdXJyZW50U2VydmVyUGFnZSddKSB7XG4gICAgICAgIC8vIGNvbnNvbGUubG9nKCfwn5SEIERhdGFHcmlkOiBjdXJyZW50U2VydmVyUGFnZSBjaGFuZ2VkOicsIHRoaXMuY3VycmVudFNlcnZlclBhZ2UpO1xuICAgICAgICB0aGlzLmN1cnJlbnRQYWdlLnNldCh0aGlzLmN1cnJlbnRTZXJ2ZXJQYWdlKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgaWYgKGNoYW5nZXNbJ2N1cnJlbnRTZXJ2ZXJQYWdlU2l6ZSddKSB7XG4gICAgICAgIC8vIGNvbnNvbGUubG9nKCfwn5SEIERhdGFHcmlkOiBjdXJyZW50U2VydmVyUGFnZVNpemUgY2hhbmdlZDonLCB0aGlzLmN1cnJlbnRTZXJ2ZXJQYWdlU2l6ZSk7XG4gICAgICAgIHRoaXMucGFnZVNpemUuc2V0KHRoaXMuY3VycmVudFNlcnZlclBhZ2VTaXplKTtcbiAgICAgICAgdGhpcy51cGRhdGVQYWdpbmF0aW9uU3RhdGUoKTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gQ2xlYXIgcGFnZSBjaGFuZ2UgbG9hZGluZyB3aGVuIHNlcnZlci1zaWRlIGRhdGEgaXMgdXBkYXRlZFxuICAgICAgaWYgKGNoYW5nZXNbJ2NvbmZpZyddICYmIHRoaXMucGFnZUNoYW5nZUxvYWRpbmcoKSkge1xuICAgICAgICB0aGlzLmNsZWFyUGFnZUNoYW5nZUxvYWRpbmcoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgdGhlIGNvbmZpZyBjaGFuZ2UgaXMgb25seSBhIGRhdGEgdXBkYXRlXG4gICAqL1xuICBwcml2YXRlIGlzRGF0YU9ubHlVcGRhdGUoY2hhbmdlOiBTaW1wbGVDaGFuZ2UpOiBib29sZWFuIHtcbiAgICBpZiAoY2hhbmdlLmZpcnN0Q2hhbmdlIHx8ICFjaGFuZ2UucHJldmlvdXNWYWx1ZSB8fCAhY2hhbmdlLmN1cnJlbnRWYWx1ZSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBcbiAgICBjb25zdCBwcmV2ID0gY2hhbmdlLnByZXZpb3VzVmFsdWU7XG4gICAgY29uc3QgY3VyciA9IGNoYW5nZS5jdXJyZW50VmFsdWU7XG4gICAgXG4gICAgLy8gQ2hlY2sgaWYgYWxsIHByb3BlcnRpZXMgZXhjZXB0ICdkYXRhJyBhcmUgdGhlIHNhbWVcbiAgICBjb25zdCBwcmV2V2l0aG91dERhdGEgPSB7IC4uLnByZXYgfTtcbiAgICBjb25zdCBjdXJyV2l0aG91dERhdGEgPSB7IC4uLmN1cnIgfTtcbiAgICBkZWxldGUgcHJldldpdGhvdXREYXRhLmRhdGE7XG4gICAgZGVsZXRlIGN1cnJXaXRob3V0RGF0YS5kYXRhO1xuICAgIFxuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShwcmV2V2l0aG91dERhdGEpID09PSBKU09OLnN0cmluZ2lmeShjdXJyV2l0aG91dERhdGEpO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSBvbmx5IHRoZSBkYXRhIHdpdGhvdXQgdHJpZ2dlcmluZyBmdWxsIGdyaWQgcmUtaW5pdGlhbGl6YXRpb25cbiAgICogVGhpcyBwcmV2ZW50cyB0aGUgc2VhcmNoIGlucHV0IGFuZCBvdGhlciBVSSBzdGF0ZSBmcm9tIGJlaW5nIHJlc2V0XG4gICAqL1xuICBwcml2YXRlIHVwZGF0ZURhdGFPbmx5KGRhdGE6IFRbXSkge1xuICAgIGxldCBwcm9jZXNzZWREYXRhID0gZGF0YTtcbiAgICBcbiAgICAvLyBUcmFuc2Zvcm0gdG8gdHJlZSBzdHJ1Y3R1cmUgaWYgdHJlZSBjb25maWcgaXMgZW5hYmxlZFxuICAgIGlmICh0aGlzLm1lcmdlZENvbmZpZygpLnRyZWU/LmVuYWJsZWQpIHtcbiAgICAgIHByb2Nlc3NlZERhdGEgPSB0aGlzLnRyYW5zZm9ybVRvVHJlZShkYXRhKTtcbiAgICAgIGNvbnNvbGUubG9nKCdwcm9jZXNzZWREYXRhJywgcHJvY2Vzc2VkRGF0YSk7XG4gICAgfVxuICAgIFxuICAgIHRoaXMuaW50ZXJuYWxEYXRhLnNldChwcm9jZXNzZWREYXRhKTtcbiAgICBjb25zb2xlLmxvZygncHJvY2Vzc2VkRGF0YScsIHByb2Nlc3NlZERhdGEpO1xuICAgIC8vIEZvciBzZXJ2ZXItc2lkZSBwYWdpbmF0aW9uLCBzZXQgZmlsdGVyZWQgZGF0YSBkaXJlY3RseVxuICAgIGlmICh0aGlzLnNlcnZlclNpZGVQYWdpbmF0aW9uKSB7XG4gICAgICBjb25zb2xlLmxvZygncHJvY2Vzc2VkRGF0YScsIHByb2Nlc3NlZERhdGEpO1xuICAgICAgdGhpcy5maWx0ZXJlZERhdGEuc2V0KHByb2Nlc3NlZERhdGEpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBGb3IgY2xpZW50LXNpZGUgcGFnaW5hdGlvbiwgYXBwbHkgZmlsdGVyc1xuICAgICAgdGhpcy5hcHBseUZpbHRlcnMoKTtcbiAgICB9XG4gICAgXG4gICAgLy8gUmVmcmVzaCBvcmRlciB0cmFja2luZyB3aGVuIGRhdGEgY2hhbmdlc1xuICAgIHRoaXMucmVmcmVzaE9yZGVyVHJhY2tpbmcoKTtcbiAgICBcbiAgICB0aGlzLmNsZWFyUGFnZUNoYW5nZUxvYWRpbmcoKTtcbiAgfVxuXG4gIHByaXZhdGUgbWVyZ2VDb25maWdXaXRoRGVmYXVsdHMoKSB7XG4gICAgdGhpcy5tZXJnZWRDb25maWcuc2V0KHtcbiAgICAgIC4uLkRFRkFVTFRfR1JJRF9DT05GSUcsXG4gICAgICAuLi50aGlzLmNvbmZpZyxcbiAgICAgIHBhZ2luYXRpb246IHsgLi4uREVGQVVMVF9HUklEX0NPTkZJRy5wYWdpbmF0aW9uISwgLi4udGhpcy5jb25maWcucGFnaW5hdGlvbiB9LFxuICAgICAgc2VhcmNoOiB7IC4uLkRFRkFVTFRfR1JJRF9DT05GSUcuc2VhcmNoISwgLi4udGhpcy5jb25maWcuc2VhcmNoIH0sXG4gICAgICBsb2FkaW5nOiB7IC4uLkRFRkFVTFRfR1JJRF9DT05GSUcubG9hZGluZyEsIC4uLnRoaXMuY29uZmlnLmxvYWRpbmcgfSxcbiAgICAgIHNjcm9sbDogeyAuLi5ERUZBVUxUX0dSSURfQ09ORklHLnNjcm9sbCEsIC4uLnRoaXMuY29uZmlnLnNjcm9sbCB9XG4gICAgfSBhcyBHcmlkQ29uZmlndXJhdGlvbjxUPik7XG4gIH1cblxuICBwcml2YXRlIGluaXRpYWxpemVHcmlkKCkge1xuICAgIC8vIEVuc3VyZSB3ZSBoYXZlIHZhbGlkIGRhdGFcbiAgICBsZXQgZGF0YSA9IHRoaXMuY29uZmlnPy5kYXRhIHx8IFtdO1xuICAgIFxuICAgIC8vIFRyYW5zZm9ybSB0byB0cmVlIHN0cnVjdHVyZSBpZiB0cmVlIGNvbmZpZyBpcyBlbmFibGVkXG4gICAgaWYgKHRoaXMubWVyZ2VkQ29uZmlnKCkudHJlZT8uZW5hYmxlZCkge1xuICAgICAgZGF0YSA9IHRoaXMudHJhbnNmb3JtVG9UcmVlKGRhdGEpO1xuICAgIH1cbiAgICBcbiAgICB0aGlzLmludGVybmFsRGF0YS5zZXQoZGF0YSk7XG4gICAgXG4gICAgLy8gU2V0IHBhZ2luYXRpb24gdmFsdWVzIGJhc2VkIG9uIG1vZGVcbiAgICBpZiAodGhpcy5zZXJ2ZXJTaWRlUGFnaW5hdGlvbikge1xuICAgICAgdGhpcy5jdXJyZW50UGFnZS5zZXQodGhpcy5jdXJyZW50U2VydmVyUGFnZSk7XG4gICAgICB0aGlzLnBhZ2VTaXplLnNldCh0aGlzLmN1cnJlbnRTZXJ2ZXJQYWdlU2l6ZSk7XG4gICAgICB0aGlzLnRvdGFsSXRlbXMuc2V0KHRoaXMudG90YWxTZXJ2ZXJJdGVtcyk7XG4gICAgICAvLyBGb3Igc2VydmVyLXNpZGUgcGFnaW5hdGlvbiwgc2V0IGZpbHRlcmVkIGRhdGEgZGlyZWN0bHkgd2l0aG91dCBhcHBseWluZyBmaWx0ZXJzXG4gICAgICB0aGlzLmZpbHRlcmVkRGF0YS5zZXQoZGF0YSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIENsaWVudC1zaWRlIHBhZ2luYXRpb25cbiAgICAgIGNvbnN0IHBhZ2VTaXplID0gdGhpcy5tZXJnZWRDb25maWcoKS5wYWdpbmF0aW9uPy5wYWdlU2l6ZSB8fCAxMDtcbiAgICAgIHRoaXMucGFnZVNpemUuc2V0KHBhZ2VTaXplKTtcbiAgICAgIHRoaXMudG90YWxJdGVtcy5zZXQoZGF0YS5sZW5ndGgpO1xuICAgICAgLy8gRm9yIGNsaWVudC1zaWRlIHBhZ2luYXRpb24sIGFwcGx5IGZpbHRlcnNcbiAgICAgIHRoaXMuYXBwbHlGaWx0ZXJzKCk7XG4gICAgfVxuICAgIFxuICAgIHRoaXMudXBkYXRlUGFnaW5hdGlvblN0YXRlKCk7XG4gICAgXG4gICAgLy8gSW5pdGlhbGl6ZSBvcmlnaW5hbCBvcmRlciBmb3IgZHJhZyBhbmQgZHJvcFxuICAgIHRoaXMuaW5pdGlhbGl6ZU9yaWdpbmFsT3JkZXIoKTtcbiAgICBcbiAgICAvLyBBbHNvIHJlZnJlc2ggb3JkZXIgdHJhY2tpbmcgdG8gZW5zdXJlIGl0J3MgdXAgdG8gZGF0ZVxuICAgIHRoaXMucmVmcmVzaE9yZGVyVHJhY2tpbmcoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIHVuaXF1ZSBpZGVudGlmaWVyIGZvciBhbiBpdGVtXG4gICAqL1xuICBwcml2YXRlIGdldEl0ZW1JZChpdGVtOiBUKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgaWYgKGl0ZW0gJiYgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnKSB7XG4gICAgICBjb25zdCBpZCA9IChpdGVtIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KVsnX2lkJ107XG4gICAgICByZXR1cm4gaWQgPyBTdHJpbmcoaWQpIDogbnVsbDtcbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBvcmRlciB2YWx1ZSBmb3IgYW4gaXRlbSBmcm9tIHRoZSBjb25maWdcbiAgICovXG4gIHByaXZhdGUgZ2V0SXRlbU9yZGVyKGl0ZW06IFQpOiBudW1iZXIge1xuICAgIGNvbnN0IGRyYWdEcm9wQ29uZmlnID0gdGhpcy5nZXREcmFnRHJvcENvbmZpZygpO1xuICAgIGlmIChkcmFnRHJvcENvbmZpZz8ub3JkZXJGaWVsZCkge1xuICAgICAgY29uc3Qgb3JkZXJWYWx1ZSA9IHRoaXMuZ2V0TmVzdGVkVmFsdWUoaXRlbSwgZHJhZ0Ryb3BDb25maWcub3JkZXJGaWVsZCk7XG4gICAgICByZXR1cm4gdHlwZW9mIG9yZGVyVmFsdWUgPT09ICdudW1iZXInID8gb3JkZXJWYWx1ZSA6IDA7XG4gICAgfVxuICAgIHJldHVybiAwO1xuICB9XG5cbiAgLyoqXG4gICAqIEluaXRpYWxpemUgdGhlIG9yaWdpbmFsIG9yZGVyIGZvciBkcmFnIGFuZCBkcm9wXG4gICAqL1xuICBwcml2YXRlIGluaXRpYWxpemVPcmlnaW5hbE9yZGVyKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmlzRHJhZ0Ryb3BFbmFibGVkKCkpIHtcbiAgICAgIC8vIEdldCB0aGUgZnVsbCBkYXRhc2V0IGZyb20gdGhlIGNvbmZpZyAobm90IGp1c3QgZGlzcGxheWVkRGF0YSlcbiAgICAgIGNvbnN0IGZ1bGxEYXRhID0gdGhpcy5tZXJnZWRDb25maWcoKS5kYXRhIHx8IFtdO1xuICAgICAgXG4gICAgICAvLyBDb21wbGV0ZWx5IGJ1aWxkIHRoZSBvcmRlciB0cmFja2luZyBmcm9tIGN1cnJlbnQgZGF0YVxuICAgICAgdGhpcy5yb3dPcmRlck1hcC5jbGVhcigpO1xuICAgICAgdGhpcy5pbml0aWFsRGF0YU9yZGVySWRzID0gW107XG4gICAgICBcbiAgICAgIC8vIEJ1aWxkIHRoZSBvcmRlciB0cmFja2luZyB3aXRoIGN1cnJlbnQgZGF0YVxuICAgICAgZnVsbERhdGEuZm9yRWFjaCgoaXRlbSwgaW5kZXgpID0+IHtcbiAgICAgICAgY29uc3QgaXRlbUlkID0gdGhpcy5nZXRJdGVtSWQoaXRlbSk7XG4gICAgICAgIGlmIChpdGVtSWQpIHtcbiAgICAgICAgICBjb25zdCBhY3R1YWxPcmRlciA9IHRoaXMuZ2V0SXRlbU9yZGVyKGl0ZW0pO1xuICAgICAgICAgIHRoaXMuaW5pdGlhbERhdGFPcmRlcklkcy5wdXNoKGl0ZW1JZCk7XG4gICAgICAgICAgdGhpcy5yb3dPcmRlck1hcC5zZXQoaXRlbUlkLCB7XG4gICAgICAgICAgICBvbGRQb3NpdGlvbjogYWN0dWFsT3JkZXIgfHwgKGluZGV4ICsgMSksIC8vIFVzZSBhY3R1YWwgb3JkZXIgb3IgZmFsbGJhY2sgdG8gaW5kZXhcbiAgICAgICAgICAgIG5ld1Bvc2l0aW9uOiBhY3R1YWxPcmRlciB8fCAoaW5kZXggKyAxKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgLy8gQ2xlYXIgYW55IGV4aXN0aW5nIGxvY2FsIHJlb3JkZXJlZCBkYXRhXG4gICAgICB0aGlzLmxvY2FsUmVvcmRlcmVkRGF0YSA9IFtdO1xuICAgICAgXG4gICAgICBjb25zb2xlLmxvZygn8J+UjSBJbml0aWFsIG9yZGVyIHRyYWNraW5nIGJ1aWx0IGZyb20gZnVsbCBkYXRhc2V0OicsIHRoaXMuaW5pdGlhbERhdGFPcmRlcklkcy5sZW5ndGgsICdpdGVtcycpO1xuICAgICAgY29uc29sZS5sb2coJ/CflI0gSW5pdGlhbCBvcmRlciBtYXAgY3JlYXRlZCB3aXRoIGFjdHVhbCBvcmRlciB2YWx1ZXM6JywgQXJyYXkuZnJvbSh0aGlzLnJvd09yZGVyTWFwLmVudHJpZXMoKSkpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZWZyZXNoIG9yZGVyIHRyYWNraW5nIHdpdGggY3VycmVudCBkYXRhIChjYWxsZWQgd2hlbiBkYXRhIHVwZGF0ZXMpXG4gICAqL1xuICBwcml2YXRlIHJlZnJlc2hPcmRlclRyYWNraW5nKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmlzRHJhZ0Ryb3BFbmFibGVkKCkpIHtcbiAgICAgIC8vIEdldCB0aGUgY3VycmVudCBmdWxsIGRhdGFzZXRcbiAgICAgIGNvbnN0IGZ1bGxEYXRhID0gdGhpcy5tZXJnZWRDb25maWcoKS5kYXRhIHx8IFtdO1xuICAgICAgXG4gICAgICAvLyBDb21wbGV0ZWx5IHJlYnVpbGQgdGhlIG9yZGVyIHRyYWNraW5nIGZyb20gY3VycmVudCBkYXRhXG4gICAgICB0aGlzLnJvd09yZGVyTWFwLmNsZWFyKCk7XG4gICAgICB0aGlzLmluaXRpYWxEYXRhT3JkZXJJZHMgPSBbXTtcbiAgICAgIFxuICAgICAgLy8gUmVidWlsZCB0aGUgb3JkZXIgdHJhY2tpbmcgd2l0aCBjdXJyZW50IGRhdGFcbiAgICAgIGZ1bGxEYXRhLmZvckVhY2goKGl0ZW0sIGluZGV4KSA9PiB7XG4gICAgICAgIGNvbnN0IGl0ZW1JZCA9IHRoaXMuZ2V0SXRlbUlkKGl0ZW0pO1xuICAgICAgICBpZiAoaXRlbUlkKSB7XG4gICAgICAgICAgY29uc3QgYWN0dWFsT3JkZXIgPSB0aGlzLmdldEl0ZW1PcmRlcihpdGVtKTtcbiAgICAgICAgICB0aGlzLmluaXRpYWxEYXRhT3JkZXJJZHMucHVzaChpdGVtSWQpO1xuICAgICAgICAgIHRoaXMucm93T3JkZXJNYXAuc2V0KGl0ZW1JZCwge1xuICAgICAgICAgICAgb2xkUG9zaXRpb246IGFjdHVhbE9yZGVyIHx8IChpbmRleCArIDEpLFxuICAgICAgICAgICAgbmV3UG9zaXRpb246IGFjdHVhbE9yZGVyIHx8IChpbmRleCArIDEpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgXG4gICAgICAvLyBDbGVhciBhbnkgbG9jYWwgcmVvcmRlcmVkIGRhdGEgc2luY2UgZGF0YSBoYXMgY2hhbmdlZFxuICAgICAgdGhpcy5sb2NhbFJlb3JkZXJlZERhdGEgPSBbXTtcbiAgICAgIHRoaXMuaGFzT3JkZXJDaGFuZ2VkLnNldChmYWxzZSk7XG4gICAgICBcbiAgICAgIGNvbnNvbGUubG9nKCfwn5SNIE9yZGVyIHRyYWNraW5nIGNvbXBsZXRlbHkgcmVidWlsdCB3aXRoIGN1cnJlbnQgZGF0YTonLCB0aGlzLmluaXRpYWxEYXRhT3JkZXJJZHMubGVuZ3RoLCAnaXRlbXMnKTtcbiAgICB9XG4gIH1cblxuICAvLyA9PT09PSBEYXRhIFByb2Nlc3NpbmcgPT09PT1cbiAgXG4gIHByaXZhdGUgYXBwbHlGaWx0ZXJzKCkge1xuICAgIGxldCBmaWx0ZXJlZCA9IFsuLi50aGlzLmludGVybmFsRGF0YSgpXTtcbiAgICBcbiAgICAvLyBBcHBseSBzZWFyY2ggZmlsdGVyIG9ubHkgZm9yIGNsaWVudC1zaWRlIHBhZ2luYXRpb25cbiAgICAvLyBGb3Igc2VydmVyLXNpZGUgcGFnaW5hdGlvbiwgc2VhcmNoIGlzIGhhbmRsZWQgYnkgdGhlIHNlcnZlclxuICAgIGlmICghdGhpcy5zZXJ2ZXJTaWRlUGFnaW5hdGlvbikge1xuICAgICAgY29uc3QgcXVlcnkgPSB0aGlzLnNlYXJjaFF1ZXJ5KCk/LnRvTG93ZXJDYXNlKCk7XG4gICAgICBpZiAocXVlcnkgJiYgdGhpcy5tZXJnZWRDb25maWcoKS5zZWFyY2guZW5hYmxlZCkge1xuICAgICAgICBmaWx0ZXJlZCA9IGZpbHRlcmVkLmZpbHRlcihpdGVtID0+IFxuICAgICAgICAgIHRoaXMubWVyZ2VkQ29uZmlnKCkuc2VhcmNoLnNlYXJjaGFibGVDb2x1bW5zLnNvbWUoY29sdW1uS2V5ID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHZhbHVlID0gdGhpcy5nZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW5LZXkpO1xuICAgICAgICAgICAgcmV0dXJuIFN0cmluZyh2YWx1ZSB8fCAnJyk/LnRvTG93ZXJDYXNlKCk/LmluY2x1ZGVzKHF1ZXJ5KTtcbiAgICAgICAgICB9KVxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvLyBGbGF0dGVuIHRyZWUgc3RydWN0dXJlIGZvciBkaXNwbGF5IGlmIHRyZWUgaXMgZW5hYmxlZFxuICAgIGlmICh0aGlzLm1lcmdlZENvbmZpZygpLnRyZWU/LmVuYWJsZWQpIHtcbiAgICAgIGZpbHRlcmVkID0gdGhpcy5mbGF0dGVuVHJlZUZvckRpc3BsYXkoZmlsdGVyZWQpO1xuICAgIH1cbiAgICBcbiAgICB0aGlzLmZpbHRlcmVkRGF0YS5zZXQoZmlsdGVyZWQpO1xuICAgIFxuICAgIC8vIEZvciBzZXJ2ZXItc2lkZSBwYWdpbmF0aW9uLCB0b3RhbEl0ZW1zIHNob3VsZCBhbHdheXMgYmUgdG90YWxTZXJ2ZXJJdGVtc1xuICAgIC8vIEZvciBjbGllbnQtc2lkZSBwYWdpbmF0aW9uLCB0b3RhbEl0ZW1zIHNob3VsZCBiZSB0aGUgZmlsdGVyZWQgZGF0YSBsZW5ndGhcbiAgICBpZiAodGhpcy5zZXJ2ZXJTaWRlUGFnaW5hdGlvbikge1xuICAgICAgdGhpcy50b3RhbEl0ZW1zLnNldCh0aGlzLnRvdGFsU2VydmVySXRlbXMpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnRvdGFsSXRlbXMuc2V0KGZpbHRlcmVkLmxlbmd0aCk7XG4gICAgfVxuICAgIFxuICAgIHRoaXMudXBkYXRlUGFnaW5hdGlvblN0YXRlKCk7XG4gIH1cblxuICAvKipcbiAgICogVHJhbnNmb3JtIGZsYXQgZGF0YSB0byB0cmVlIHN0cnVjdHVyZSBiYXNlZCBvbiBmb3JlaWduIGtleSByZWxhdGlvbnNoaXBzXG4gICAqL1xuICBwcml2YXRlIHRyYW5zZm9ybVRvVHJlZShkYXRhOiBUW10pOiBUW10ge1xuICAgIGNvbnNvbGUubG9nKCd0cmFuc2Zvcm1Ub1RyZWUgLSBpbnB1dCBkYXRhOicsIGRhdGEubGVuZ3RoLCAnaXRlbXMnKTtcbiAgICBjb25zdCB0cmVlQ29uZmlnID0gdGhpcy5tZXJnZWRDb25maWcoKS50cmVlO1xuICAgIGlmICghdHJlZUNvbmZpZykgcmV0dXJuIGRhdGE7XG5cbiAgICBjb25zdCB7IHByaW1hcnlLZXksIGZvcmVpZ25LZXksIGNoaWxkcmVuS2V5ID0gJ2NoaWxkcmVuJywgbGV2ZWxLZXkgPSAnbGV2ZWwnLCBleHBhbmRlZEtleSA9ICdpc0V4cGFuZGVkJywgaGFzQ2hpbGRyZW5LZXkgPSAnaGFzQ2hpbGRyZW4nIH0gPSB0cmVlQ29uZmlnO1xuICAgIFxuICAgIC8vIEFsd2F5cyBhZGQgbGV2ZWwga2V5IHRvIGVhY2ggcm93IGR1cmluZyBwcm9jZXNzaW5nXG4gICAgXG4gICAgLy8gQ3JlYXRlIGEgbWFwIGZvciBxdWljayBsb29rdXBcbiAgICBjb25zdCBpdGVtTWFwID0gbmV3IE1hcDxzdHJpbmcsIFQ+KCk7XG4gICAgY29uc3QgcHJvY2Vzc2VkSXRlbXMgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICBjb25zdCByb290SXRlbXM6IFRbXSA9IFtdO1xuXG4gICAgLy8gRmlyc3QgcGFzczogY3JlYXRlIHRyZWUgaXRlbXMgd2l0aCBkZWZhdWx0IHByb3BlcnRpZXMgYW5kIGxldmVsIGtleVxuICAgIGRhdGEuZm9yRWFjaChpdGVtID0+IHtcbiAgICAgIGNvbnN0IHRyZWVJdGVtID0ge1xuICAgICAgICAuLi5pdGVtLFxuICAgICAgICBbbGV2ZWxLZXldOiAwLFxuICAgICAgICBsZXZlbDogMCwgIC8vIEFsd2F5cyBhZGQgJ2xldmVsJyBwcm9wZXJ0eSBmb3IgY29uc2lzdGVudCBhY2Nlc3NcbiAgICAgICAgW2V4cGFuZGVkS2V5XTogZmFsc2UsXG4gICAgICAgIFtoYXNDaGlsZHJlbktleV06IGZhbHNlLFxuICAgICAgICBbY2hpbGRyZW5LZXldOiBbXVxuICAgICAgfSBhcyBUO1xuICAgICAgaXRlbU1hcC5zZXQoU3RyaW5nKHRoaXMuZ2V0TmVzdGVkVmFsdWUoaXRlbSwgcHJpbWFyeUtleSkgfHwgJycpLCB0cmVlSXRlbSk7XG4gICAgfSk7XG5cbiAgICAvLyBNdWx0aS1wYXNzIGhpZXJhcmNoeSBidWlsZGluZyB0byBoYW5kbGUgaXRlbXMgaW4gYW55IG9yZGVyXG4gICAgbGV0IHJlbWFpbmluZ0l0ZW1zID0gWy4uLmRhdGFdO1xuICAgIGNvbnN0IG1heEl0ZXJhdGlvbnMgPSBkYXRhLmxlbmd0aDsgLy8gUHJldmVudCBpbmZpbml0ZSBsb29wc1xuICAgIGxldCBpdGVyYXRpb24gPSAwO1xuXG4gICAgd2hpbGUgKHJlbWFpbmluZ0l0ZW1zLmxlbmd0aCA+IDAgJiYgaXRlcmF0aW9uIDwgbWF4SXRlcmF0aW9ucykge1xuICAgICAgY29uc3QgaXRlbXNUb1Byb2Nlc3MgPSBbLi4ucmVtYWluaW5nSXRlbXNdO1xuICAgICAgcmVtYWluaW5nSXRlbXMgPSBbXTtcbiAgICAgIFxuICAgICAgY29uc29sZS5sb2coYEl0ZXJhdGlvbiAke2l0ZXJhdGlvbiArIDF9OiBQcm9jZXNzaW5nICR7aXRlbXNUb1Byb2Nlc3MubGVuZ3RofSBpdGVtc2ApO1xuXG4gICAgICBpdGVtc1RvUHJvY2Vzcy5mb3JFYWNoKGl0ZW0gPT4ge1xuICAgICAgICBjb25zdCBpdGVtSWQgPSBTdHJpbmcodGhpcy5nZXROZXN0ZWRWYWx1ZShpdGVtLCBwcmltYXJ5S2V5KSB8fCAnJyk7XG4gICAgICAgIGNvbnN0IHBhcmVudElkVmFsdWUgPSB0aGlzLmdldE5lc3RlZFZhbHVlKGl0ZW0sIGZvcmVpZ25LZXkpO1xuICAgICAgICBjb25zdCBwYXJlbnRJZCA9IHBhcmVudElkVmFsdWUgIT09IG51bGwgJiYgcGFyZW50SWRWYWx1ZSAhPT0gdW5kZWZpbmVkID8gU3RyaW5nKHBhcmVudElkVmFsdWUpIDogJyc7XG4gICAgICAgIGNvbnN0IHRyZWVJdGVtID0gaXRlbU1hcC5nZXQoaXRlbUlkKTtcbiAgICAgICAgXG4gICAgICAgIC8vIFNraXAgaWYgYWxyZWFkeSBwcm9jZXNzZWRcbiAgICAgICAgaWYgKHByb2Nlc3NlZEl0ZW1zLmhhcyhpdGVtSWQpKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRyZWVJdGVtICYmIHBhcmVudElkICYmIGl0ZW1NYXAuaGFzKHBhcmVudElkKSkge1xuICAgICAgICAgIC8vIFRoaXMgaXMgYSBjaGlsZCBpdGVtIC0gY2hlY2sgaWYgcGFyZW50IGlzIHByb2Nlc3NlZCBvciBpcyBhIHJvb3RcbiAgICAgICAgICBjb25zdCBwYXJlbnQgPSBpdGVtTWFwLmdldChwYXJlbnRJZCkhO1xuICAgICAgICAgIGNvbnN0IHBhcmVudFBhcmVudElkID0gdGhpcy5nZXROZXN0ZWRWYWx1ZShwYXJlbnQsIGZvcmVpZ25LZXkpO1xuICAgICAgICAgIGNvbnN0IGlzUGFyZW50Um9vdCA9ICFwYXJlbnRQYXJlbnRJZCB8fCBwYXJlbnRQYXJlbnRJZCA9PT0gbnVsbCB8fCBwYXJlbnRQYXJlbnRJZCA9PT0gdW5kZWZpbmVkO1xuICAgICAgICAgIFxuICAgICAgICAgIGlmIChpc1BhcmVudFJvb3QgfHwgcHJvY2Vzc2VkSXRlbXMuaGFzKHBhcmVudElkKSkge1xuICAgICAgICAgICAgLy8gUGFyZW50IGlzIHByb2Nlc3NlZCBvciBpcyByb290LCB3ZSBjYW4gcHJvY2VzcyB0aGlzIGNoaWxkXG4gICAgICAgICAgICBjb25zdCBwYXJlbnRDaGlsZHJlbiA9IHRoaXMuZ2V0TmVzdGVkVmFsdWUocGFyZW50LCBjaGlsZHJlbktleSkgYXMgVFtdIHx8IFtdO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyBBdm9pZCBhZGRpbmcgZHVwbGljYXRlc1xuICAgICAgICAgICAgY29uc3QgY2hpbGRFeGlzdHMgPSBwYXJlbnRDaGlsZHJlbi5zb21lKGNoaWxkID0+IFxuICAgICAgICAgICAgICBTdHJpbmcodGhpcy5nZXROZXN0ZWRWYWx1ZShjaGlsZCwgcHJpbWFyeUtleSkgfHwgJycpID09PSBpdGVtSWRcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIGlmICghY2hpbGRFeGlzdHMpIHtcbiAgICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIGxldmVsIGJhc2VkIG9uIHBhcmVudCAtIGF1dG9tYXRpY2FsbHkgc2V0IGxldmVsIGtleVxuICAgICAgICAgICAgICBjb25zdCBwYXJlbnRMZXZlbCA9IHRoaXMuZ2V0TmVzdGVkVmFsdWUocGFyZW50LCAnbGV2ZWwnKSBhcyBudW1iZXIgfHwgMDtcbiAgICAgICAgICAgICAgY29uc3QgY2hpbGRMZXZlbCA9IHBhcmVudExldmVsICsgMTtcbiAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgIC8vIFVwZGF0ZSBjaGlsZCBpdGVtIHdpdGggY29ycmVjdCBsZXZlbFxuICAgICAgICAgICAgICBjb25zdCB1cGRhdGVkVHJlZUl0ZW0gPSB7XG4gICAgICAgICAgICAgICAgLi4udHJlZUl0ZW0sXG4gICAgICAgICAgICAgICAgW2xldmVsS2V5XTogY2hpbGRMZXZlbCxcbiAgICAgICAgICAgICAgICBsZXZlbDogY2hpbGRMZXZlbCAgLy8gQWx3YXlzIGFkZCAnbGV2ZWwnIHByb3BlcnR5IGZvciBjb25zaXN0ZW50IGFjY2Vzc1xuICAgICAgICAgICAgICB9IGFzIFQ7XG4gICAgICAgICAgICAgIGl0ZW1NYXAuc2V0KGl0ZW1JZCwgdXBkYXRlZFRyZWVJdGVtKTtcbiAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgIC8vIEFkZCB1cGRhdGVkIGNoaWxkIHRvIHBhcmVudCdzIGNoaWxkcmVuIGFycmF5XG4gICAgICAgICAgICAgIHBhcmVudENoaWxkcmVuLnB1c2godXBkYXRlZFRyZWVJdGVtKTtcbiAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgIC8vIFVwZGF0ZSBwYXJlbnQgcHJvcGVydGllc1xuICAgICAgICAgICAgICBjb25zdCB1cGRhdGVkUGFyZW50ID0ge1xuICAgICAgICAgICAgICAgIC4uLnBhcmVudCxcbiAgICAgICAgICAgICAgICBbY2hpbGRyZW5LZXldOiBwYXJlbnRDaGlsZHJlbixcbiAgICAgICAgICAgICAgICBbaGFzQ2hpbGRyZW5LZXldOiB0cnVlXG4gICAgICAgICAgICAgIH0gYXMgVDtcbiAgICAgICAgICAgICAgaXRlbU1hcC5zZXQocGFyZW50SWQsIHVwZGF0ZWRQYXJlbnQpO1xuICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgcHJvY2Vzc2VkSXRlbXMuYWRkKGl0ZW1JZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIFBhcmVudCBub3QgcHJvY2Vzc2VkIHlldCwgZGVmZXIgdGhpcyBpdGVtXG4gICAgICAgICAgICByZW1haW5pbmdJdGVtcy5wdXNoKGl0ZW0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmICh0cmVlSXRlbSkge1xuICAgICAgICAgIC8vIFRoaXMgaXMgYSByb290IGl0ZW0gLSBhdXRvbWF0aWNhbGx5IHNldCBsZXZlbCB0byAwXG4gICAgICAgICAgY29uc3QgdXBkYXRlZFJvb3RJdGVtID0ge1xuICAgICAgICAgICAgLi4udHJlZUl0ZW0sXG4gICAgICAgICAgICBbbGV2ZWxLZXldOiAwLFxuICAgICAgICAgICAgbGV2ZWw6IDAgIC8vIEFsd2F5cyBhZGQgJ2xldmVsJyBwcm9wZXJ0eSBmb3IgY29uc2lzdGVudCBhY2Nlc3NcbiAgICAgICAgICB9IGFzIFQ7XG4gICAgICAgICAgaXRlbU1hcC5zZXQoaXRlbUlkLCB1cGRhdGVkUm9vdEl0ZW0pO1xuICAgICAgICAgIFxuICAgICAgICAgIGlmICghcm9vdEl0ZW1zLnNvbWUocm9vdEl0ZW0gPT4gXG4gICAgICAgICAgICBTdHJpbmcodGhpcy5nZXROZXN0ZWRWYWx1ZShyb290SXRlbSwgcHJpbWFyeUtleSkgfHwgJycpID09PSBpdGVtSWRcbiAgICAgICAgICApKSB7XG4gICAgICAgICAgICByb290SXRlbXMucHVzaCh1cGRhdGVkUm9vdEl0ZW0pO1xuICAgICAgICAgICAgcHJvY2Vzc2VkSXRlbXMuYWRkKGl0ZW1JZCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9KTtcblxuICAgICAgaXRlcmF0aW9uKys7XG4gICAgfVxuXG4gICAgLy8gSGFuZGxlIGFueSByZW1haW5pbmcgdW5wcm9jZXNzZWQgaXRlbXMgYXMgcm9vdHMgKG9ycGhhbmVkIGl0ZW1zKVxuICAgIHJlbWFpbmluZ0l0ZW1zLmZvckVhY2goaXRlbSA9PiB7XG4gICAgICBjb25zdCBpdGVtSWQgPSBTdHJpbmcodGhpcy5nZXROZXN0ZWRWYWx1ZShpdGVtLCBwcmltYXJ5S2V5KSB8fCAnJyk7XG4gICAgICBpZiAoIXByb2Nlc3NlZEl0ZW1zLmhhcyhpdGVtSWQpKSB7XG4gICAgICAgIGNvbnN0IHRyZWVJdGVtID0gaXRlbU1hcC5nZXQoaXRlbUlkKTtcbiAgICAgICAgaWYgKHRyZWVJdGVtICYmICFyb290SXRlbXMuaW5jbHVkZXModHJlZUl0ZW0pKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coYEFkZGluZyBvcnBoYW5lZCBpdGVtICR7aXRlbUlkfSBhcyByb290YCk7XG4gICAgICAgICAgcm9vdEl0ZW1zLnB1c2godHJlZUl0ZW0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICAvLyBGaW5hbCBwYXNzOiBVcGRhdGUgaGFzQ2hpbGRyZW4gcHJvcGVydHkgZm9yIGFsbCBpdGVtcyBpbiB0aGUgdHJlZVxuICAgIGNvbnN0IHVwZGF0ZUhhc0NoaWxkcmVuID0gKGl0ZW1zOiBUW10pID0+IHtcbiAgICAgIGl0ZW1zLmZvckVhY2goKGl0ZW0sIGluZGV4KSA9PiB7XG4gICAgICAgIGNvbnN0IGl0ZW1JZCA9IFN0cmluZyh0aGlzLmdldE5lc3RlZFZhbHVlKGl0ZW0sIHByaW1hcnlLZXkpIHx8ICcnKTtcbiAgICAgICAgY29uc3QgY2hpbGRyZW4gPSB0aGlzLmdldE5lc3RlZFZhbHVlKGl0ZW0sIGNoaWxkcmVuS2V5KSBhcyBUW10gfHwgW107XG4gICAgICAgIGNvbnN0IGhhc0NoaWxkcmVuID0gY2hpbGRyZW4ubGVuZ3RoID4gMDtcbiAgICAgICAgXG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgaXRlbSB3aXRoIGNvcnJlY3QgaGFzQ2hpbGRyZW4gcHJvcGVydHlcbiAgICAgICAgY29uc3QgdXBkYXRlZEl0ZW0gPSB7XG4gICAgICAgICAgLi4uaXRlbSxcbiAgICAgICAgICBbaGFzQ2hpbGRyZW5LZXldOiBoYXNDaGlsZHJlblxuICAgICAgICB9IGFzIFQ7XG4gICAgICAgIFxuICAgICAgICAvLyBVcGRhdGUgaW4gdGhlIG1hcFxuICAgICAgICBpdGVtTWFwLnNldChpdGVtSWQsIHVwZGF0ZWRJdGVtKTtcbiAgICAgICAgXG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgaXRlbSBpbiB0aGUgYXJyYXlcbiAgICAgICAgaXRlbXNbaW5kZXhdID0gdXBkYXRlZEl0ZW07XG4gICAgICAgIFxuICAgICAgICAvLyBSZWN1cnNpdmVseSB1cGRhdGUgY2hpbGRyZW5cbiAgICAgICAgaWYgKGNoaWxkcmVuLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICB1cGRhdGVIYXNDaGlsZHJlbihjaGlsZHJlbik7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH07XG5cbiAgICAvLyBHZXQgdXBkYXRlZCByb290IGl0ZW1zIGZyb20gdGhlIG1hcCB0byBlbnN1cmUgd2UgaGF2ZSB0aGUgbGF0ZXN0IHZlcnNpb25zXG4gICAgY29uc3QgdXBkYXRlZFJvb3RJdGVtcyA9IHJvb3RJdGVtcy5tYXAocm9vdEl0ZW0gPT4ge1xuICAgICAgY29uc3Qgcm9vdElkID0gU3RyaW5nKHRoaXMuZ2V0TmVzdGVkVmFsdWUocm9vdEl0ZW0sIHByaW1hcnlLZXkpIHx8ICcnKTtcbiAgICAgIHJldHVybiBpdGVtTWFwLmdldChyb290SWQpIHx8IHJvb3RJdGVtO1xuICAgIH0pO1xuICAgIFxuICAgIC8vIFVwZGF0ZSBhbGwgcm9vdCBpdGVtcyBhbmQgdGhlaXIgZGVzY2VuZGFudHNcbiAgICB1cGRhdGVIYXNDaGlsZHJlbih1cGRhdGVkUm9vdEl0ZW1zKTtcbiAgICBcbiAgICBjb25zb2xlLmxvZygndHJhbnNmb3JtVG9UcmVlIC0gcm9vdCBpdGVtczonLCB1cGRhdGVkUm9vdEl0ZW1zLmxlbmd0aCk7XG4gICAgY29uc29sZS5sb2coJ0NDIC0gcHJvY2Vzc2VkIGl0ZW1zOicsIHByb2Nlc3NlZEl0ZW1zLnNpemUpO1xuICAgIGNvbnNvbGUubG9nKCd0cmFuc2Zvcm1Ub1RyZWUgLSBzYW1wbGUgcm9vdCBpdGVtIHdpdGggbGV2ZWw6JywgdXBkYXRlZFJvb3RJdGVtc1swXSk7XG4gICAgcmV0dXJuIHVwZGF0ZWRSb290SXRlbXM7XG4gIH1cblxuICAvKipcbiAgICogUmVjdXJzaXZlbHkgY2FsY3VsYXRlIGl0ZW0gbGV2ZWwgaW4gaGllcmFyY2h5IGZvciB1bmxpbWl0ZWQgZGVwdGggc3VwcG9ydFxuICAgKi9cbiAgcHJpdmF0ZSBjYWxjdWxhdGVJdGVtTGV2ZWwoaXRlbTogVCwgaXRlbU1hcDogTWFwPHN0cmluZywgVD4sIHByaW1hcnlLZXk6IHN0cmluZywgZm9yZWlnbktleTogc3RyaW5nLCBsZXZlbEtleTogc3RyaW5nKTogbnVtYmVyIHtcbiAgICAvLyBDaGVjayBpZiBsZXZlbCBpcyBhbHJlYWR5IHNldCBjb3JyZWN0bHlcbiAgICBjb25zdCBjdXJyZW50TGV2ZWwgPSB0aGlzLmdldE5lc3RlZFZhbHVlKGl0ZW0sIGxldmVsS2V5KSBhcyBudW1iZXI7XG4gICAgaWYgKGN1cnJlbnRMZXZlbCAhPT0gdW5kZWZpbmVkICYmIGN1cnJlbnRMZXZlbCA+IDApIHtcbiAgICAgIHJldHVybiBjdXJyZW50TGV2ZWw7XG4gICAgfVxuXG4gICAgLy8gR2V0IHBhcmVudCBpbmZvcm1hdGlvblxuICAgIGNvbnN0IGl0ZW1JZCA9IFN0cmluZyh0aGlzLmdldE5lc3RlZFZhbHVlKGl0ZW0sIHByaW1hcnlLZXkpIHx8ICcnKTtcbiAgICBjb25zdCBwYXJlbnRJZFZhbHVlID0gdGhpcy5nZXROZXN0ZWRWYWx1ZShpdGVtLCBmb3JlaWduS2V5KTtcbiAgICBjb25zdCBwYXJlbnRJZCA9IHBhcmVudElkVmFsdWUgIT09IG51bGwgJiYgcGFyZW50SWRWYWx1ZSAhPT0gdW5kZWZpbmVkID8gU3RyaW5nKHBhcmVudElkVmFsdWUpIDogJyc7XG4gICAgXG4gICAgLy8gSWYgbm8gcGFyZW50LCB0aGlzIGlzIGEgcm9vdCBpdGVtIChsZXZlbCAwKVxuICAgIGlmICghcGFyZW50SWQgfHwgIWl0ZW1NYXAuaGFzKHBhcmVudElkKSkge1xuICAgICAgcmV0dXJuIDA7XG4gICAgfVxuICAgIFxuICAgIC8vIFJlY3Vyc2l2ZWx5IGNhbGN1bGF0ZSBwYXJlbnQgbGV2ZWwgYW5kIGFkZCAxXG4gICAgY29uc3QgcGFyZW50ID0gaXRlbU1hcC5nZXQocGFyZW50SWQpITtcbiAgICBjb25zdCBwYXJlbnRMZXZlbCA9IHRoaXMuY2FsY3VsYXRlSXRlbUxldmVsKHBhcmVudCwgaXRlbU1hcCwgcHJpbWFyeUtleSwgZm9yZWlnbktleSwgbGV2ZWxLZXkpO1xuICAgIFxuICAgIC8vIFVwZGF0ZSB0aGUgaXRlbSdzIGxldmVsIGluIHRoZSBtYXBcbiAgICBjb25zdCB1cGRhdGVkSXRlbSA9IHtcbiAgICAgIC4uLml0ZW0sXG4gICAgICBbbGV2ZWxLZXldOiBwYXJlbnRMZXZlbCArIDFcbiAgICB9IGFzIFQ7XG4gICAgaXRlbU1hcC5zZXQoaXRlbUlkLCB1cGRhdGVkSXRlbSk7XG4gICAgXG4gICAgcmV0dXJuIHBhcmVudExldmVsICsgMTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGbGF0dGVuIHRyZWUgc3RydWN0dXJlIGZvciBkaXNwbGF5LCByZXNwZWN0aW5nIGV4cGFuc2lvbiBzdGF0ZSB3aXRoIHVubGltaXRlZCBuZXN0aW5nXG4gICAqL1xuICBwcml2YXRlIGZsYXR0ZW5UcmVlRm9yRGlzcGxheSh0cmVlSXRlbXM6IFRbXSk6IFRbXSB7XG4gICAgY29uc3QgcmVzdWx0OiBUW10gPSBbXTtcbiAgICBjb25zdCB0cmVlQ29uZmlnID0gdGhpcy5tZXJnZWRDb25maWcoKS50cmVlO1xuICAgIGlmICghdHJlZUNvbmZpZykgcmV0dXJuIHRyZWVJdGVtcztcbiAgICBcbiAgICBjb25zdCB7IGNoaWxkcmVuS2V5ID0gJ2NoaWxkcmVuJywgZXhwYW5kZWRLZXkgPSAnaXNFeHBhbmRlZCcgfSA9IHRyZWVDb25maWc7XG4gICAgXG4gICAgY29uc3QgZmxhdHRlbiA9IChpdGVtczogVFtdLCBjdXJyZW50TGV2ZWw6IG51bWJlciA9IDApID0+IHtcbiAgICAgIGl0ZW1zLmZvckVhY2goaXRlbSA9PiB7XG4gICAgICAgIHJlc3VsdC5wdXNoKGl0ZW0pO1xuICAgICAgICBjb25zdCBjaGlsZHJlbiA9IHRoaXMuZ2V0TmVzdGVkVmFsdWUoaXRlbSwgY2hpbGRyZW5LZXkpIGFzIFRbXSB8fCBbXTtcbiAgICAgICAgY29uc3QgaXNFeHBhbmRlZCA9IHRoaXMuZ2V0TmVzdGVkVmFsdWUoaXRlbSwgZXhwYW5kZWRLZXkpIGFzIGJvb2xlYW4gfHwgZmFsc2U7XG4gICAgICAgIFxuICAgICAgICBpZiAoY2hpbGRyZW4ubGVuZ3RoID4gMCAmJiBpc0V4cGFuZGVkKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coYEZsYXR0ZW5pbmcgY2hpbGRyZW4gZm9yIGl0ZW0gYXQgbGV2ZWwgJHtjdXJyZW50TGV2ZWx9LCBjaGlsZHJlbiBjb3VudDogJHtjaGlsZHJlbi5sZW5ndGh9YCk7XG4gICAgICAgICAgZmxhdHRlbihjaGlsZHJlbiwgY3VycmVudExldmVsICsgMSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH07XG4gICAgXG4gICAgZmxhdHRlbih0cmVlSXRlbXMpO1xuICAgIGNvbnNvbGUubG9nKCdmbGF0dGVuVHJlZUZvckRpc3BsYXkgLSByZXN1bHQ6JywgcmVzdWx0Lmxlbmd0aCwgJ2l0ZW1zJyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBUb2dnbGUgZXhwYW5kL2NvbGxhcHNlIHN0YXRlIG9mIGEgdHJlZSBpdGVtIHdpdGggdW5saW1pdGVkIG5lc3Rpbmcgc3VwcG9ydFxuICAgKi9cbiAgcHJpdmF0ZSB0b2dnbGVUcmVlRXhwYW5kKGl0ZW06IFQpOiB2b2lkIHtcbiAgICBjb25zdCB0cmVlQ29uZmlnID0gdGhpcy5tZXJnZWRDb25maWcoKS50cmVlO1xuICAgIGlmICghdHJlZUNvbmZpZykgcmV0dXJuO1xuICAgIFxuICAgIGNvbnN0IHsgZXhwYW5kZWRLZXkgPSAnaXNFeHBhbmRlZCcsIHByaW1hcnlLZXkgfSA9IHRyZWVDb25maWc7XG4gICAgY29uc3QgY3VycmVudEV4cGFuZGVkID0gdGhpcy5nZXROZXN0ZWRWYWx1ZShpdGVtLCBleHBhbmRlZEtleSkgYXMgYm9vbGVhbiB8fCBmYWxzZTtcbiAgICBjb25zdCBpdGVtSWQgPSBTdHJpbmcodGhpcy5nZXROZXN0ZWRWYWx1ZShpdGVtLCBwcmltYXJ5S2V5KSB8fCAnJyk7XG4gICAgXG4gICAgY29uc29sZS5sb2coYHRvZ2dsZVRyZWVFeHBhbmQgLSBpdGVtOiAke2l0ZW1JZH0sIGN1cnJlbnQgZXhwYW5kZWQ6ICR7Y3VycmVudEV4cGFuZGVkfWApO1xuICAgIFxuICAgIC8vIFVwZGF0ZSB0aGUgZXhwYW5kZWQgc3RhdGVcbiAgICBjb25zdCB1cGRhdGVkSXRlbSA9IHtcbiAgICAgIC4uLml0ZW0sXG4gICAgICBbZXhwYW5kZWRLZXldOiAhY3VycmVudEV4cGFuZGVkXG4gICAgfSBhcyBUO1xuICAgIFxuICAgIC8vIFVwZGF0ZSB0aGUgaXRlbSBpbiB0aGUgdHJlZSBzdHJ1Y3R1cmUgcmVjdXJzaXZlbHlcbiAgICBjb25zdCB1cGRhdGVJdGVtSW5UcmVlID0gKHRyZWVEYXRhOiBUW10pOiBUW10gPT4ge1xuICAgICAgcmV0dXJuIHRyZWVEYXRhLm1hcCh0cmVlSXRlbSA9PiB7XG4gICAgICAgIGNvbnN0IHRyZWVJdGVtSWQgPSBTdHJpbmcodGhpcy5nZXROZXN0ZWRWYWx1ZSh0cmVlSXRlbSwgcHJpbWFyeUtleSkgfHwgJycpO1xuICAgICAgICBcbiAgICAgICAgaWYgKHRyZWVJdGVtSWQgPT09IGl0ZW1JZCkge1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBGb3VuZCBpdGVtIHRvIHVwZGF0ZTogJHtpdGVtSWR9LCBuZXcgZXhwYW5kZWQgc3RhdGU6ICR7IWN1cnJlbnRFeHBhbmRlZH1gKTtcbiAgICAgICAgICByZXR1cm4gdXBkYXRlZEl0ZW07XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIC8vIENoZWNrIGNoaWxkcmVuIHJlY3Vyc2l2ZWx5XG4gICAgICAgIGNvbnN0IGNoaWxkcmVuID0gdGhpcy5nZXROZXN0ZWRWYWx1ZSh0cmVlSXRlbSwgJ2NoaWxkcmVuJykgYXMgVFtdIHx8IFtdO1xuICAgICAgICBpZiAoY2hpbGRyZW4ubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGNvbnN0IHVwZGF0ZWRDaGlsZHJlbiA9IHVwZGF0ZUl0ZW1JblRyZWUoY2hpbGRyZW4pO1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAuLi50cmVlSXRlbSxcbiAgICAgICAgICAgIGNoaWxkcmVuOiB1cGRhdGVkQ2hpbGRyZW5cbiAgICAgICAgICB9IGFzIFQ7XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIHJldHVybiB0cmVlSXRlbTtcbiAgICAgIH0pO1xuICAgIH07XG4gICAgXG4gICAgLy8gVXBkYXRlIHRoZSBpbnRlcm5hbCBkYXRhXG4gICAgY29uc3QgdXBkYXRlZERhdGEgPSB1cGRhdGVJdGVtSW5UcmVlKFsuLi50aGlzLmludGVybmFsRGF0YSgpXSk7XG4gICAgdGhpcy5pbnRlcm5hbERhdGEuc2V0KHVwZGF0ZWREYXRhKTtcbiAgICBcbiAgICAvLyBSZS1hcHBseSBmaWx0ZXJzIHRvIHVwZGF0ZSB0aGUgZGlzcGxheVxuICAgIHRoaXMuYXBwbHlGaWx0ZXJzKCk7XG4gIH1cblxuICBwcml2YXRlIHVwZGF0ZVBhZ2luYXRpb25TdGF0ZSgpIHtcbiAgICBjb25zdCB0b3RhbCA9IE1hdGguY2VpbCh0aGlzLnRvdGFsSXRlbXMoKSAvIHRoaXMucGFnZVNpemUoKSk7XG4gICAgdGhpcy50b3RhbFBhZ2VzLnNldCh0b3RhbCk7XG4gICAgXG4gICAgLy8gRW5zdXJlIGN1cnJlbnQgcGFnZSBpcyB3aXRoaW4gYm91bmRzXG4gICAgaWYgKHRvdGFsID09PSAwKSB7XG4gICAgICB0aGlzLmN1cnJlbnRQYWdlLnNldCgxKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuY3VycmVudFBhZ2UoKSA+IHRvdGFsKSB7XG4gICAgICB0aGlzLmN1cnJlbnRQYWdlLnNldCh0b3RhbCk7XG4gICAgfVxuICB9XG5cbiAgLy8gPT09PT0gUGFnaW5hdGlvbiBNZXRob2RzID09PT09XG4gIFxuICBvblBhZ2VDaGFuZ2UocGFnZTogbnVtYmVyIHwgc3RyaW5nKSB7XG4gICAgY29uc29sZS5sb2coJ/CflIQgRGF0YUdyaWQ6IG9uUGFnZUNoYW5nZSBjYWxsZWQgd2l0aCBwYWdlOicsIHBhZ2UpO1xuICAgIGNvbnN0IHBhZ2VOdW0gPSB0eXBlb2YgcGFnZSA9PT0gJ3N0cmluZycgPyBwYXJzZUludChwYWdlLCAxMCkgOiBwYWdlO1xuICAgIGNvbnNvbGUubG9nKCfwn5SEIERhdGFHcmlkOiBQYXJzZWQgcGFnZSBudW1iZXI6JywgcGFnZU51bSwgJ1RvdGFsIHBhZ2VzOicsIHRoaXMudG90YWxQYWdlcygpKTtcbiAgICBcbiAgICBpZiAocGFnZU51bSA+PSAxICYmIHBhZ2VOdW0gPD0gdGhpcy50b3RhbFBhZ2VzKCkpIHtcbiAgICAgIC8vIFNldCBwYWdlIGNoYW5nZSBsb2FkaW5nIHN0YXRlXG4gICAgICB0aGlzLnBhZ2VDaGFuZ2VMb2FkaW5nLnNldCh0cnVlKTtcbiAgICAgIGNvbnNvbGUubG9nKCfwn5SEIERhdGFHcmlkOiBTZXQgcGFnZSBjaGFuZ2UgbG9hZGluZyB0byB0cnVlJyk7XG4gICAgICBcbiAgICAgIHRoaXMuY3VycmVudFBhZ2Uuc2V0KHBhZ2VOdW0pO1xuICAgICAgXG4gICAgICBpZiAodGhpcy5zZXJ2ZXJTaWRlUGFnaW5hdGlvbikge1xuICAgICAgICBjb25zdCBwYWdlRGF0YSA9IHsgcGFnZTogcGFnZU51bSwgcGFnZVNpemU6IHRoaXMucGFnZVNpemUoKSB9O1xuICAgICAgICBjb25zb2xlLmxvZygn8J+UhCBEYXRhR3JpZDogRW1pdHRpbmcgcGFnZUNoYW5nZSBldmVudCBmb3Igc2VydmVyLXNpZGUgcGFnaW5hdGlvbjonLCBwYWdlRGF0YSk7XG4gICAgICAgIC8vIEVtaXQgcGFnZSBkYXRhIG9iamVjdCBmb3Igc2VydmVyLXNpZGUgcGFnaW5hdGlvblxuICAgICAgICB0aGlzLmVtaXRFdmVudCgncGFnZUNoYW5nZScsIHBhZ2VEYXRhIGFzIHVua25vd24gYXMgVCk7XG4gICAgICAgIC8vIERvbid0IGNsZWFyIGxvYWRpbmcgc3RhdGUgaGVyZSAtIGxldCB0aGUgcGFyZW50IGNvbXBvbmVudCBoYW5kbGUgaXRcbiAgICAgICAgLy8gVGhlIGxvYWRpbmcgc3RhdGUgd2lsbCBiZSBjbGVhcmVkIHdoZW4gbmV3IGRhdGEgYXJyaXZlcyB2aWEgbmdPbkNoYW5nZXNcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCfwn5SEIERhdGFHcmlkOiBFbWl0dGluZyBwYWdlQ2hhbmdlIGV2ZW50IGZvciBjbGllbnQtc2lkZSBwYWdpbmF0aW9uOicsIHBhZ2VOdW0pO1xuICAgICAgICAvLyBFbWl0IHNpbXBsZSBwYWdlIG51bWJlciBmb3IgY2xpZW50LXNpZGUgcGFnaW5hdGlvblxuICAgICAgICB0aGlzLmVtaXRFdmVudCgncGFnZUNoYW5nZScsIHBhZ2VOdW0pO1xuICAgICAgICAvLyBDbGVhciBsb2FkaW5nIHN0YXRlIGZvciBjbGllbnQtc2lkZSBwYWdpbmF0aW9uXG4gICAgICAgIHRoaXMuY2xlYXJQYWdlQ2hhbmdlTG9hZGluZygpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBjb25zb2xlLmxvZygn8J+UhCBEYXRhR3JpZDogUGFnZSBudW1iZXIgb3V0IG9mIHJhbmdlLCBub3QgZW1pdHRpbmcgZXZlbnQnKTtcbiAgICB9XG4gIH1cblxuICBvblBhZ2VTaXplQ2hhbmdlKCkge1xuICAgIC8vIFNldCBwYWdlIGNoYW5nZSBsb2FkaW5nIHN0YXRlXG4gICAgdGhpcy5wYWdlQ2hhbmdlTG9hZGluZy5zZXQodHJ1ZSk7XG4gICAgXG4gICAgdGhpcy5jdXJyZW50UGFnZS5zZXQoMSk7XG4gICAgdGhpcy51cGRhdGVQYWdpbmF0aW9uU3RhdGUoKTtcbiAgICBcbiAgICBpZiAodGhpcy5zZXJ2ZXJTaWRlUGFnaW5hdGlvbikge1xuICAgICAgLy8gRW1pdCBwYWdlIGRhdGEgb2JqZWN0IGZvciBzZXJ2ZXItc2lkZSBwYWdpbmF0aW9uXG4gICAgICB0aGlzLmVtaXRFdmVudCgncGFnZUNoYW5nZScsIHsgcGFnZTogMSwgcGFnZVNpemU6IHRoaXMucGFnZVNpemUoKSB9IGFzIHVua25vd24gYXMgVCk7XG4gICAgICAvLyBEb24ndCBjbGVhciBsb2FkaW5nIHN0YXRlIGhlcmUgLSBsZXQgdGhlIHBhcmVudCBjb21wb25lbnQgaGFuZGxlIGl0XG4gICAgICAvLyBUaGUgbG9hZGluZyBzdGF0ZSB3aWxsIGJlIGNsZWFyZWQgd2hlbiBuZXcgZGF0YSBhcnJpdmVzIHZpYSBuZ09uQ2hhbmdlc1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBFbWl0IHNpbXBsZSBwYWdlIG51bWJlciBmb3IgY2xpZW50LXNpZGUgcGFnaW5hdGlvblxuICAgICAgdGhpcy5lbWl0RXZlbnQoJ3BhZ2VDaGFuZ2UnLCAxKTtcbiAgICAgIC8vIENsZWFyIGxvYWRpbmcgc3RhdGUgZm9yIGNsaWVudC1zaWRlIHBhZ2luYXRpb25cbiAgICAgIHRoaXMuY2xlYXJQYWdlQ2hhbmdlTG9hZGluZygpO1xuICAgIH1cbiAgfVxuXG4gIHVwZGF0ZVBhZ2VTaXplKHZhbHVlOiBzdHJpbmcgfCBudW1iZXIpIHtcbiAgICB0aGlzLnBhZ2VTaXplLnNldCgrdmFsdWUpO1xuICAgIHRoaXMub25QYWdlU2l6ZUNoYW5nZSgpO1xuICB9XG5cbiAgcHJldmlvdXNQYWdlKCkge1xuICAgIGlmICh0aGlzLmhhc1ByZXZpb3VzUGFnZSgpKSB7XG4gICAgICB0aGlzLm9uUGFnZUNoYW5nZSh0aGlzLmN1cnJlbnRQYWdlKCkgLSAxKTtcbiAgICB9XG4gIH1cblxuICBuZXh0UGFnZSgpIHtcbiAgICBpZiAodGhpcy5oYXNOZXh0UGFnZSgpKSB7XG4gICAgICB0aGlzLm9uUGFnZUNoYW5nZSh0aGlzLmN1cnJlbnRQYWdlKCkgKyAxKTtcbiAgICB9XG4gIH1cblxuICBvbkp1bXBUb1BhZ2UoKSB7XG4gICAgaWYgKHRoaXMuanVtcFRvUGFnZSA+PSAxICYmIHRoaXMuanVtcFRvUGFnZSA8PSB0aGlzLnRvdGFsUGFnZXMoKSkge1xuICAgICAgdGhpcy5vblBhZ2VDaGFuZ2UodGhpcy5qdW1wVG9QYWdlKTtcbiAgICB9XG4gIH1cblxuICBnZXRFbmhhbmNlZFBhZ2VOdW1iZXJzKCk6IChudW1iZXIgfCBzdHJpbmcpW10ge1xuICAgIGNvbnN0IHRvdGFsID0gdGhpcy50b3RhbFBhZ2VzKCk7XG4gICAgY29uc3QgY3VycmVudCA9IHRoaXMuY3VycmVudFBhZ2UoKTtcbiAgICBjb25zdCBwYWdlczogKG51bWJlciB8IHN0cmluZylbXSA9IFtdO1xuICAgIFxuICAgIGlmICh0b3RhbCA8PSA3KSB7XG4gICAgICBmb3IgKGxldCBpID0gMTsgaSA8PSB0b3RhbDsgaSsrKSB7XG4gICAgICAgIHBhZ2VzLnB1c2goaSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHBhZ2VzLnB1c2goMSk7XG4gICAgICBcbiAgICAgIGlmIChjdXJyZW50IDw9IDQpIHtcbiAgICAgICAgZm9yIChsZXQgaSA9IDI7IGkgPD0gNTsgaSsrKSB7XG4gICAgICAgICAgcGFnZXMucHVzaChpKTtcbiAgICAgICAgfVxuICAgICAgICBwYWdlcy5wdXNoKCcuLi4nKTtcbiAgICAgICAgcGFnZXMucHVzaCh0b3RhbCk7XG4gICAgICB9IGVsc2UgaWYgKGN1cnJlbnQgPj0gdG90YWwgLSAzKSB7XG4gICAgICAgIHBhZ2VzLnB1c2goJy4uLicpO1xuICAgICAgICBmb3IgKGxldCBpID0gdG90YWwgLSA0OyBpIDw9IHRvdGFsOyBpKyspIHtcbiAgICAgICAgICBwYWdlcy5wdXNoKGkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBwYWdlcy5wdXNoKCcuLi4nKTtcbiAgICAgICAgZm9yIChsZXQgaSA9IGN1cnJlbnQgLSAxOyBpIDw9IGN1cnJlbnQgKyAxOyBpKyspIHtcbiAgICAgICAgICBwYWdlcy5wdXNoKGkpO1xuICAgICAgICB9XG4gICAgICAgIHBhZ2VzLnB1c2goJy4uLicpO1xuICAgICAgICBwYWdlcy5wdXNoKHRvdGFsKTtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHBhZ2VzO1xuICB9XG5cbiAgLy8gPT09PT0gU2VhcmNoIE1ldGhvZHMgPT09PT1cbiAgXG4gIHVwZGF0ZVNlYXJjaFF1ZXJ5KHZhbHVlOiBpbnB1dFR5cGUpIHtcbiAgICBjb25zb2xlLmxvZygndXBkYXRlU2VhcmNoUXVlcnknLCB2YWx1ZSk7XG4gICAgY29uc3Qgc2VhcmNoVmFsdWUgPSB2YWx1ZSBhcyBzdHJpbmc7XG4gICAgdGhpcy5zZWFyY2hRdWVyeS5zZXQoc2VhcmNoVmFsdWUpO1xuICAgIFxuICAgIC8vIENsZWFyIGFueSBleGlzdGluZyBkZWJvdW5jZSB0aW1lclxuICAgIGlmICh0aGlzLnNlYXJjaERlYm91bmNlVGltZXIpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aGlzLnNlYXJjaERlYm91bmNlVGltZXIpO1xuICAgIH1cbiAgICBcbiAgICAvLyBBcHBseSBkZWJvdW5jZSBmb3Igc2VhcmNoXG4gICAgY29uc3QgZGVib3VuY2VNcyA9IHRoaXMubWVyZ2VkQ29uZmlnKCkuc2VhcmNoLmRlYm91bmNlTXMgfHwgMzAwO1xuICAgIHRoaXMuc2VhcmNoRGVib3VuY2VUaW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgLy8gT25seSBlbWl0IHNlYXJjaCBldmVudCBmb3Igc2VydmVyLXNpZGUgcGFnaW5hdGlvblxuICAgICAgLy8gRG9uJ3QgdHJpZ2dlciBhbnkgY2xpZW50LXNpZGUgZmlsdGVyaW5nIHRoYXQgbWlnaHQgY2F1c2UgcmUtcmVuZGVyc1xuICAgICAgaWYgKHRoaXMuc2VydmVyU2lkZVBhZ2luYXRpb24pIHtcbiAgICAgICAgdGhpcy5jdXJyZW50UGFnZS5zZXQoMSk7XG4gICAgICAgIHRoaXMuZW1pdEV2ZW50KCdzZWFyY2gnLCBzZWFyY2hWYWx1ZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLm9uU2VhcmNoKCk7XG4gICAgICB9XG4gICAgfSwgZGVib3VuY2VNcyk7XG4gIH1cbiAgXG4gIG9uU2VhcmNoKCkge1xuICAgIHRoaXMuY3VycmVudFBhZ2Uuc2V0KDEpO1xuICAgIFxuICAgIGlmICh0aGlzLnNlcnZlclNpZGVQYWdpbmF0aW9uKSB7XG4gICAgICAvLyBGb3Igc2VydmVyLXNpZGUgcGFnaW5hdGlvbiwgb25seSBlbWl0IHRoZSBzZWFyY2ggZXZlbnRcbiAgICAgIC8vIERvbid0IGFwcGx5IGNsaWVudC1zaWRlIGZpbHRlcnMgYXMgdGhlIHNlcnZlciB3aWxsIGhhbmRsZSB0aGUgc2VhcmNoXG4gICAgICB0aGlzLmVtaXRFdmVudCgnc2VhcmNoJywgdGhpcy5zZWFyY2hRdWVyeSgpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gRm9yIGNsaWVudC1zaWRlIHBhZ2luYXRpb24sIGFwcGx5IGZpbHRlcnMgbG9jYWxseVxuICAgICAgdGhpcy5hcHBseUZpbHRlcnMoKTtcbiAgICAgIHRoaXMuZW1pdEV2ZW50KCdzZWFyY2gnLCB0aGlzLnNlYXJjaFF1ZXJ5KCkpO1xuICAgIH1cbiAgfVxuXG4gIC8vID09PT09IEFjdGlvbiBNZXRob2RzID09PT09XG4gIFxuICBvblJlZnJlc2goKSB7XG4gICAgdGhpcy5pc1JlZnJlc2hpbmcuc2V0KHRydWUpO1xuICAgIHRoaXMuZW1pdEV2ZW50KCdyZWZyZXNoJywgbnVsbCk7XG4gICAgXG4gICAgLy8gVGhlIHBhcmVudCBjb21wb25lbnQgc2hvdWxkIGhhbmRsZSB0aGUgYWN0dWFsIHJlZnJlc2ggYW5kIGNhbGwgc2V0UmVmcmVzaGluZyhmYWxzZSkgd2hlbiBkb25lXG4gICAgLy8gV2UgZG9uJ3QgYXV0by1yZXNldCBoZXJlIHRvIGFsbG93IHRoZSBwYXJlbnQgdG8gY29udHJvbCB0aGUgcmVmcmVzaCBzdGF0ZVxuICB9XG5cbiAgLy8gTWV0aG9kIHRvIGFsbG93IHBhcmVudCBjb21wb25lbnRzIHRvIGNvbnRyb2wgdGhlIHJlZnJlc2hpbmcgc3RhdGVcbiAgc2V0UmVmcmVzaGluZyhpc1JlZnJlc2hpbmc6IGJvb2xlYW4pIHtcbiAgICB0aGlzLmlzUmVmcmVzaGluZy5zZXQoaXNSZWZyZXNoaW5nKTtcbiAgfVxuXG4gIC8vIE1ldGhvZCB0byBjbGVhciBwYWdlIGNoYW5nZSBsb2FkaW5nIHN0YXRlIHdoZW4gbmV3IGRhdGEgaXMgcmVjZWl2ZWRcbiAgY2xlYXJQYWdlQ2hhbmdlTG9hZGluZygpIHtcbiAgICB0aGlzLnBhZ2VDaGFuZ2VMb2FkaW5nLnNldChmYWxzZSk7XG4gIH1cblxuICAvLyA9PT09PSBUcmVlIFZpZXcgSGVscGVyIE1ldGhvZHMgPT09PT1cbiAgXG4gIC8qKlxuICAgKiBDaGVjayBpZiB0cmVlIHZpZXcgaXMgZW5hYmxlZFxuICAgKi9cbiAgaXNUcmVlRW5hYmxlZCgpOiBib29sZWFuIHtcbiAgICBjb25zdCBlbmFibGVkID0gdGhpcy5tZXJnZWRDb25maWcoKS50cmVlPy5lbmFibGVkIHx8IGZhbHNlO1xuICAgIHJldHVybiBlbmFibGVkO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0cmVlIGNvbmZpZ3VyYXRpb25cbiAgICovXG4gIGdldFRyZWVDb25maWcoKSB7XG4gICAgcmV0dXJuIHRoaXMubWVyZ2VkQ29uZmlnKCkudHJlZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBhbiBpdGVtIGhhcyBjaGlsZHJlblxuICAgKi9cbiAgaGFzQ2hpbGRyZW4oaXRlbTogVCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IHRyZWVDb25maWcgPSB0aGlzLmdldFRyZWVDb25maWcoKTtcbiAgICBpZiAoIXRyZWVDb25maWcpIHJldHVybiBmYWxzZTtcbiAgICBcbiAgICBjb25zdCB7IGhhc0NoaWxkcmVuS2V5ID0gJ2hhc0NoaWxkcmVuJywgY2hpbGRyZW5LZXkgPSAnY2hpbGRyZW4nIH0gPSB0cmVlQ29uZmlnO1xuICAgIFxuICAgIC8vIENoZWNrIGhhc0NoaWxkcmVuIHByb3BlcnR5IGZpcnN0XG4gICAgY29uc3QgaGFzQ2hpbGRyZW4gPSB0aGlzLmdldE5lc3RlZFZhbHVlKGl0ZW0sIGhhc0NoaWxkcmVuS2V5KSBhcyBib29sZWFuO1xuICAgIGlmIChoYXNDaGlsZHJlbiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gaGFzQ2hpbGRyZW47XG4gICAgfVxuICAgIFxuICAgIC8vIEZhbGxiYWNrIHRvIGNoZWNraW5nIGNoaWxkcmVuIGFycmF5XG4gICAgY29uc3QgY2hpbGRyZW4gPSB0aGlzLmdldE5lc3RlZFZhbHVlKGl0ZW0sIGNoaWxkcmVuS2V5KSBhcyBUW107XG4gICAgcmV0dXJuIGNoaWxkcmVuICYmIGNoaWxkcmVuLmxlbmd0aCA+IDA7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGl0ZW0gbGV2ZWwgaW4gdHJlZVxuICAgKi9cbiAgZ2V0SXRlbUxldmVsKGl0ZW06IFQpOiBudW1iZXIge1xuICAgIC8vIEFsd2F5cyB0cnkgdG8gZ2V0IGxldmVsIGZyb20gc3RhbmRhcmQgJ2xldmVsJyBwcm9wZXJ0eSBmaXJzdCAoc2V0IGJ5IHRyYW5zZm9ybVRvVHJlZSlcbiAgICBsZXQgbGV2ZWwgPSB0aGlzLmdldE5lc3RlZFZhbHVlKGl0ZW0sICdsZXZlbCcpIGFzIG51bWJlcjtcbiAgICBcbiAgICAvLyBJZiBub3QgZm91bmQsIHRyeSB0cmVlIGNvbmZpZyBsZXZlbCBrZXlcbiAgICBpZiAobGV2ZWwgPT09IHVuZGVmaW5lZCB8fCBsZXZlbCA9PT0gbnVsbCkge1xuICAgICAgY29uc3QgdHJlZUNvbmZpZyA9IHRoaXMuZ2V0VHJlZUNvbmZpZygpO1xuICAgICAgaWYgKHRyZWVDb25maWcpIHtcbiAgICAgICAgY29uc3QgeyBsZXZlbEtleSA9ICdsZXZlbCcgfSA9IHRyZWVDb25maWc7XG4gICAgICAgIGxldmVsID0gdGhpcy5nZXROZXN0ZWRWYWx1ZShpdGVtLCBsZXZlbEtleSkgYXMgbnVtYmVyIHx8IDA7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsZXZlbCA9IDA7XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBsZXZlbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBpdGVtIGlzIGV4cGFuZGVkXG4gICAqL1xuICBpc0l0ZW1FeHBhbmRlZChpdGVtOiBUKTogYm9vbGVhbiB7XG4gICAgY29uc3QgdHJlZUNvbmZpZyA9IHRoaXMuZ2V0VHJlZUNvbmZpZygpO1xuICAgIGlmICghdHJlZUNvbmZpZykgcmV0dXJuIGZhbHNlO1xuICAgIFxuICAgIGNvbnN0IHsgZXhwYW5kZWRLZXkgPSAnaXNFeHBhbmRlZCcgfSA9IHRyZWVDb25maWc7XG4gICAgcmV0dXJuIHRoaXMuZ2V0TmVzdGVkVmFsdWUoaXRlbSwgZXhwYW5kZWRLZXkpIGFzIGJvb2xlYW4gfHwgZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRyZWUgaW5kZW50IHN0eWxlXG4gICAqL1xuICBnZXRUcmVlSW5kZW50U3R5bGUoaXRlbTogVCk6IHN0cmluZyB7XG4gICAgY29uc3QgbGV2ZWwgPSB0aGlzLmdldEl0ZW1MZXZlbChpdGVtKTtcbiAgICBcbiAgICAvLyBTcGVjaWZpYyBwYWRkaW5nIHByb2dyZXNzaW9uOiBMZXZlbCAxPTEycHgsIExldmVsIDI9MjJweCwgTGV2ZWwgMz0zMnB4LCBMZXZlbCA0PTUycHhcbiAgICBjb25zdCBiYXNlUGFkZGluZyA9IDEyOyAvLyBCYXNlIGNlbGwgcGFkZGluZyAocmVwbGFjZXMgdHctcHgtMylcbiAgICBsZXQgbGV2ZWxQYWRkaW5nID0gMDtcbiAgICBcbiAgICBzd2l0Y2ggKGxldmVsKSB7XG4gICAgICBjYXNlIDA6IC8vIExldmVsIDEgKHJvb3QpXG4gICAgICAgIGxldmVsUGFkZGluZyA9IDA7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAxOiAvLyBMZXZlbCAyXG4gICAgICAgIGxldmVsUGFkZGluZyA9IDEwO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMjogLy8gTGV2ZWwgM1xuICAgICAgICBsZXZlbFBhZGRpbmcgPSAyMDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDM6IC8vIExldmVsIDRcbiAgICAgICAgbGV2ZWxQYWRkaW5nID0gNDA7XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDogLy8gTGV2ZWwgNStcbiAgICAgICAgbGV2ZWxQYWRkaW5nID0gNDAgKyAoKGxldmVsIC0gMykgKiAyMCk7IC8vIENvbnRpbnVlIHdpdGggMjBweCBpbmNyZW1lbnRzXG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgICBcbiAgICBjb25zdCB0b3RhbFBhZGRpbmcgPSBgJHtiYXNlUGFkZGluZyArIGxldmVsUGFkZGluZ31weGA7XG4gICAgcmV0dXJuIHRvdGFsUGFkZGluZztcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdHJlZSBsZXZlbCBiYWNrZ3JvdW5kIGNsYXNzIGZvciB2aXN1YWwgZGlzdGluY3Rpb25cbiAgICovXG4gIGdldFRyZWVMZXZlbENsYXNzKGl0ZW06IFQpOiBzdHJpbmcge1xuICAgIGNvbnN0IGxldmVsID0gdGhpcy5nZXRJdGVtTGV2ZWwoaXRlbSk7XG4gICAgaWYgKGxldmVsID09PSAwKSByZXR1cm4gJ3RyZWUtbGV2ZWwtMCc7IC8vIFJvb3QgbGV2ZWxcbiAgICBpZiAobGV2ZWwgPT09IDEpIHJldHVybiAndHJlZS1sZXZlbC0xJzsgLy8gRmlyc3QgY2hpbGQgbGV2ZWwgIFxuICAgIGlmIChsZXZlbCA9PT0gMikgcmV0dXJuICd0cmVlLWxldmVsLTInOyAvLyBTZWNvbmQgY2hpbGQgbGV2ZWxcbiAgICByZXR1cm4gJ3RyZWUtbGV2ZWwtZGVlcCc7IC8vIERlZXAgbmVzdGluZyAoMysgbGV2ZWxzKVxuICB9XG5cbiAgLy8gPT09PT0gRHJhZyBhbmQgRHJvcCBIZWxwZXIgTWV0aG9kcyA9PT09PVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIGRyYWcgYW5kIGRyb3AgaXMgZW5hYmxlZFxuICAgKi9cbiAgaXNEcmFnRHJvcEVuYWJsZWQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuZHJhZ0Ryb3BFbmFibGVkIHx8IHRoaXMubWVyZ2VkQ29uZmlnKCkuZHJhZ0Ryb3A/LmVuYWJsZWQgfHwgZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGRyYWcgYW5kIGRyb3AgY29uZmlndXJhdGlvblxuICAgKi9cbiAgZ2V0RHJhZ0Ryb3BDb25maWcoKSB7XG4gICAgcmV0dXJuIHRoaXMubWVyZ2VkQ29uZmlnKCkuZHJhZ0Ryb3A7XG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlIGRyYWcgc3RhcnQgZXZlbnRcbiAgICovXG4gIG9uRHJhZ1N0YXJ0KGV2ZW50OiBEcmFnRXZlbnQsIGl0ZW06IFQsIGluZGV4OiBudW1iZXIpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNEcmFnRHJvcEVuYWJsZWQoKSkgcmV0dXJuO1xuICAgIFxuICAgIHRoaXMuaXNEcmFnZ2luZy5zZXQodHJ1ZSk7XG4gICAgXG4gICAgLy8gU3RvcmUgdGhlIGN1cnJlbnQgb3JkZXIgSURzIHdoZW4gZHJhZyBzdGFydHMgZm9yIGNvbXBhcmlzb25cbiAgICB0aGlzLm9yaWdpbmFsT3JkZXJJZHMgPSB0aGlzLmRpc3BsYXllZERhdGEubWFwKGl0ZW0gPT4gdGhpcy5nZXRJdGVtSWQoaXRlbSkpLmZpbHRlcihpZCA9PiBpZCAhPT0gbnVsbCkgYXMgc3RyaW5nW107XG4gICAgY29uc29sZS5sb2coJ/CflI0gRHJhZyBzdGFydGVkLCBjdXJyZW50IG9yZGVyIElEcyBzdG9yZWQ6JywgdGhpcy5vcmlnaW5hbE9yZGVySWRzKTtcbiAgICBcbiAgICBjb25zdCBkcmFnQ29uZmlnID0gdGhpcy5nZXREcmFnRHJvcENvbmZpZygpO1xuICAgIGNvbnN0IGRyYWdDbGFzcyA9IGRyYWdDb25maWc/LmRyYWdDbGFzcyB8fCAndHctb3BhY2l0eS01MCB0dy1iZy1ibHVlLTUwJztcbiAgICBcbiAgICBpZiAoZXZlbnQuZGF0YVRyYW5zZmVyKSB7XG4gICAgICBldmVudC5kYXRhVHJhbnNmZXIuZWZmZWN0QWxsb3dlZCA9ICdtb3ZlJztcbiAgICAgIGV2ZW50LmRhdGFUcmFuc2Zlci5zZXREYXRhKCd0ZXh0L3BsYWluJywgSlNPTi5zdHJpbmdpZnkoeyBpdGVtLCBpbmRleCB9KSk7XG4gICAgfVxuICAgIFxuICAgIC8vIEZpbmQgdGhlIGNsb3Nlc3QgdHIgZWxlbWVudCBhbmQgYXBwbHkgZHJhZyBzdHlsZXNcbiAgICBjb25zdCByb3dFbGVtZW50ID0gKGV2ZW50LnRhcmdldCBhcyBIVE1MRWxlbWVudCkuY2xvc2VzdCgndHInKTtcbiAgICBpZiAocm93RWxlbWVudCkge1xuICAgICAgZHJhZ0NsYXNzLnNwbGl0KCcgJykuZm9yRWFjaChjbHMgPT4gcm93RWxlbWVudC5jbGFzc0xpc3QuYWRkKGNscykpO1xuICAgICAgLy8gQWRkIGFkZGl0aW9uYWwgdmlzdWFsIGZlZWRiYWNrXG4gICAgICByb3dFbGVtZW50LnN0eWxlLnRyYW5zZm9ybSA9ICdzY2FsZSgxLjAyKSc7XG4gICAgICByb3dFbGVtZW50LnN0eWxlLmJveFNoYWRvdyA9ICcwIDRweCAxMnB4IHJnYmEoMCwgMCwgMCwgMC4xNSknO1xuICAgICAgcm93RWxlbWVudC5zdHlsZS56SW5kZXggPSAnMTAwMCc7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEhhbmRsZSBkcmFnIG92ZXIgZXZlbnRcbiAgICovXG4gIG9uRHJhZ092ZXIoZXZlbnQ6IERyYWdFdmVudCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc0RyYWdEcm9wRW5hYmxlZCgpKSByZXR1cm47XG4gICAgXG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICBldmVudC5kYXRhVHJhbnNmZXIhLmRyb3BFZmZlY3QgPSAnbW92ZSc7XG4gICAgXG4gICAgLy8gRmluZCB0aGUgcm93IGluZGV4IGZyb20gdGhlIGNsb3Nlc3QgdHIgZWxlbWVudFxuICAgIGNvbnN0IHJvd0VsZW1lbnQgPSAoZXZlbnQudGFyZ2V0IGFzIEhUTUxFbGVtZW50KS5jbG9zZXN0KCd0cicpO1xuICAgIGlmIChyb3dFbGVtZW50KSB7XG4gICAgICBjb25zdCB0Ym9keSA9IHJvd0VsZW1lbnQuY2xvc2VzdCgndGJvZHknKTtcbiAgICAgIGlmICh0Ym9keSkge1xuICAgICAgICBjb25zdCByb3dJbmRleCA9IEFycmF5LmZyb20odGJvZHkuY2hpbGRyZW4pLmluZGV4T2Yocm93RWxlbWVudCk7XG4gICAgICAgIHRoaXMuaXNEcmFnT3ZlclJvdyA9IHJvd0luZGV4O1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBIYW5kbGUgZHJhZyBsZWF2ZSBldmVudFxuICAgKi9cbiAgb25EcmFnTGVhdmUoZXZlbnQ6IERyYWdFdmVudCk6IHZvaWQge1xuICAgIGNvbnNvbGUubG9nKCdvbkRyYWdMZWF2ZScsIGV2ZW50KTtcbiAgICBpZiAoIXRoaXMuaXNEcmFnRHJvcEVuYWJsZWQoKSkgcmV0dXJuO1xuICAgIFxuICAgIC8vIENsZWFyIGRyYWcgb3ZlciByb3cgdHJhY2tpbmdcbiAgICB0aGlzLmlzRHJhZ092ZXJSb3cgPSBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIEhhbmRsZSBkcm9wIGV2ZW50XG4gICAqL1xuICBvbkRyb3AoZXZlbnQ6IERyYWdFdmVudCwgdGFyZ2V0SXRlbTogVCwgdGFyZ2V0SW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc0RyYWdEcm9wRW5hYmxlZCgpKSByZXR1cm47XG4gICAgXG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICBcbiAgICAvLyBDbGVhciBkcmFnIG92ZXIgcm93IHRyYWNraW5nXG4gICAgdGhpcy5pc0RyYWdPdmVyUm93ID0gbnVsbDtcbiAgICBcbiAgICB0cnkge1xuICAgICAgY29uc3QgZGF0YSA9IEpTT04ucGFyc2UoZXZlbnQuZGF0YVRyYW5zZmVyIS5nZXREYXRhKCd0ZXh0L3BsYWluJykpO1xuICAgICAgY29uc3Qgc291cmNlSXRlbSA9IGRhdGEuaXRlbSBhcyBUO1xuICAgICAgY29uc3Qgc291cmNlSW5kZXggPSBkYXRhLmluZGV4IGFzIG51bWJlcjtcbiAgICAgIFxuICAgICAgaWYgKHNvdXJjZUluZGV4ICE9PSB0YXJnZXRJbmRleCkge1xuICAgICAgICBjb25zdCBuZXdPcmRlciA9IHRoaXMuZ2V0TmV3T3JkZXIoc291cmNlSW5kZXgsIHRhcmdldEluZGV4KTtcbiAgICAgICAgY29uc29sZS5sb2coJ/CflI0gRHJvcCBjb21wbGV0ZWQsIHVwZGF0aW5nIGxvY2FsIG9yZGVyIG9ubHk6JywgeyBzb3VyY2VJdGVtLCBzb3VyY2VJbmRleCwgdGFyZ2V0SXRlbSwgdGFyZ2V0SW5kZXgsIG5ld09yZGVyIH0pO1xuICAgICAgICBcbiAgICAgICAgLy8gVXBkYXRlIHRoZSByb3cgb3JkZXIgbWFwIHdpdGggbmV3IHBvc2l0aW9ucyAoTE9DQUwgT05MWSlcbiAgICAgICAgdGhpcy51cGRhdGVSb3dPcmRlck1hcChuZXdPcmRlcik7XG4gICAgICAgIFxuICAgICAgICAvLyBVcGRhdGUgbG9jYWwgZGF0YSBmb3IgdmlzdWFsIHJlb3JkZXJpbmcgKExPQ0FMIE9OTFkpXG4gICAgICAgIHRoaXMudXBkYXRlTG9jYWxEYXRhT3JkZXIobmV3T3JkZXIpO1xuICAgICAgICBcbiAgICAgICAgLy8gQ2hlY2sgaWYgYW55IHJvdyBoYXMgY2hhbmdlZCBwb3NpdGlvblxuICAgICAgICBjb25zdCBoYXNBY3R1YWxseUNoYW5nZWQgPSB0aGlzLmNoZWNrSWZPcmRlckNoYW5nZWQoKTtcbiAgICAgICAgdGhpcy5oYXNPcmRlckNoYW5nZWQuc2V0KGhhc0FjdHVhbGx5Q2hhbmdlZCk7XG4gICAgICAgIGNvbnNvbGUubG9nKCfwn5SNIExvY2FsIG9yZGVyIGNoYW5nZWQ6JywgaGFzQWN0dWFsbHlDaGFuZ2VkKTtcbiAgICAgICAgXG4gICAgICAgIC8vIElNUE9SVEFOVDogRE8gTk9UIGVtaXQgcmVvcmRlciBldmVudCBoZXJlIC0gb25seSBzdG9yZSBsb2NhbGx5XG4gICAgICAgIC8vIEV4dGVybmFsIGNvbXBvbmVudHMgd2lsbCBiZSB1cGRhdGVkIG9ubHkgd2hlbiBcIlNhdmUgT3JkZXJcIiBpcyBjbGlja2VkXG4gICAgICAgIGNvbnNvbGUubG9nKCfwn5SNIE9yZGVyIGNoYW5nZSBzdG9yZWQgbG9jYWxseSAtIGV4dGVybmFsIGNvbXBvbmVudHMgbm90IHVwZGF0ZWQgdW50aWwgc2F2ZScpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc29sZS5sb2coJ1NvdXJjZSBhbmQgdGFyZ2V0IGluZGV4IGFyZSB0aGUgc2FtZSwgc2tpcHBpbmcgcmVvcmRlcicpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBwYXJzaW5nIGRyYWcgZGF0YTonLCBlcnJvcik7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEhhbmRsZSBkcmFnIGVuZCBldmVudFxuICAgKi9cbiAgb25EcmFnRW5kKGV2ZW50OiBEcmFnRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNEcmFnRHJvcEVuYWJsZWQoKSkgcmV0dXJuO1xuICAgIFxuICAgIHRoaXMuaXNEcmFnZ2luZy5zZXQoZmFsc2UpO1xuICAgIFxuICAgIC8vIENoZWNrIGlmIG9yZGVyIGhhcyBjaGFuZ2VkIGFmdGVyIGRyYWcgZW5kc1xuICAgIGNvbnN0IGhhc0FjdHVhbGx5Q2hhbmdlZCA9IHRoaXMuY2hlY2tJZk9yZGVyQ2hhbmdlZCgpO1xuICAgIHRoaXMuaGFzT3JkZXJDaGFuZ2VkLnNldChoYXNBY3R1YWxseUNoYW5nZWQpO1xuICAgIFxuICAgIGNvbnN0IGRyYWdDb25maWcgPSB0aGlzLmdldERyYWdEcm9wQ29uZmlnKCk7XG4gICAgY29uc3QgZHJhZ0NsYXNzID0gZHJhZ0NvbmZpZz8uZHJhZ0NsYXNzIHx8ICd0dy1vcGFjaXR5LTUwIHR3LWJnLWJsdWUtNTAnO1xuICAgIFxuICAgIC8vIENsZWFyIGRyYWcgb3ZlciByb3cgdHJhY2tpbmdcbiAgICB0aGlzLmlzRHJhZ092ZXJSb3cgPSBudWxsO1xuICAgIFxuICAgIC8vIEZpbmQgdGhlIGNsb3Nlc3QgdHIgZWxlbWVudCBhbmQgcmVtb3ZlIGRyYWcgc3R5bGVzXG4gICAgY29uc3Qgcm93RWxlbWVudCA9IChldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQpLmNsb3Nlc3QoJ3RyJyk7XG4gICAgaWYgKHJvd0VsZW1lbnQpIHtcbiAgICAgIGRyYWdDbGFzcy5zcGxpdCgnICcpLmZvckVhY2goY2xzID0+IHJvd0VsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZShjbHMpKTtcbiAgICAgIC8vIFJlbW92ZSBhZGRpdGlvbmFsIHZpc3VhbCBmZWVkYmFja1xuICAgICAgcm93RWxlbWVudC5zdHlsZS50cmFuc2Zvcm0gPSAnJztcbiAgICAgIHJvd0VsZW1lbnQuc3R5bGUuYm94U2hhZG93ID0gJyc7XG4gICAgICByb3dFbGVtZW50LnN0eWxlLnpJbmRleCA9ICcnO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxjdWxhdGUgbmV3IG9yZGVyIGFmdGVyIHJlb3JkZXJpbmdcbiAgICovXG4gIHByaXZhdGUgZ2V0TmV3T3JkZXIoc291cmNlSW5kZXg6IG51bWJlciwgdGFyZ2V0SW5kZXg6IG51bWJlcik6IFRbXSB7XG4gICAgY29uc3QgZGF0YSA9IFsuLi50aGlzLmRpc3BsYXllZERhdGFdO1xuICAgIGNvbnN0IFttb3ZlZEl0ZW1dID0gZGF0YS5zcGxpY2Uoc291cmNlSW5kZXgsIDEpO1xuICAgIGRhdGEuc3BsaWNlKHRhcmdldEluZGV4LCAwLCBtb3ZlZEl0ZW0pO1xuICAgIHJldHVybiBkYXRhO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgY3VycmVudCBvcmRlciBudW1iZXIgZm9yIGEgc3BlY2lmaWMgaXRlbVxuICAgKi9cbiAgZ2V0Q3VycmVudE9yZGVyTnVtYmVyKGl0ZW06IFQpOiBudW1iZXIge1xuICAgIGNvbnN0IGl0ZW1JZCA9IHRoaXMuZ2V0SXRlbUlkKGl0ZW0pO1xuICAgIGlmIChpdGVtSWQgJiYgdGhpcy5yb3dPcmRlck1hcC5oYXMoaXRlbUlkKSkge1xuICAgICAgcmV0dXJuIHRoaXMucm93T3JkZXJNYXAuZ2V0KGl0ZW1JZCkhLm5ld1Bvc2l0aW9uO1xuICAgIH1cbiAgICBcbiAgICAvLyBGYWxsYmFjayB0byBhY3R1YWwgb3JkZXIgZmllbGQgdmFsdWUgZnJvbSBjb25maWdcbiAgICBjb25zdCBhY3R1YWxPcmRlciA9IHRoaXMuZ2V0SXRlbU9yZGVyKGl0ZW0pO1xuICAgIGlmIChhY3R1YWxPcmRlciA+IDApIHtcbiAgICAgIHJldHVybiBhY3R1YWxPcmRlcjtcbiAgICB9XG4gICAgXG4gICAgLy8gRmluYWwgZmFsbGJhY2sgdG8gaW5kZXgtYmFzZWQgY2FsY3VsYXRpb25cbiAgICBjb25zdCBjdXJyZW50SW5kZXggPSB0aGlzLmRpc3BsYXllZERhdGEuZmluZEluZGV4KGN1cnJlbnRJdGVtID0+IHtcbiAgICAgIGlmIChpdGVtICYmIGN1cnJlbnRJdGVtICYmIHR5cGVvZiBpdGVtID09PSAnb2JqZWN0JyAmJiB0eXBlb2YgY3VycmVudEl0ZW0gPT09ICdvYmplY3QnKSB7XG4gICAgICAgIGNvbnN0IGl0ZW1JZCA9IChpdGVtIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KVsnX2lkJ107XG4gICAgICAgIGNvbnN0IGN1cnJlbnRJdGVtSWQgPSAoY3VycmVudEl0ZW0gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pWydfaWQnXTtcbiAgICAgICAgcmV0dXJuIGl0ZW1JZCAmJiBjdXJyZW50SXRlbUlkICYmIGl0ZW1JZCA9PT0gY3VycmVudEl0ZW1JZDtcbiAgICAgIH1cbiAgICAgIHJldHVybiBpdGVtID09PSBjdXJyZW50SXRlbTtcbiAgICB9KTtcbiAgICByZXR1cm4gY3VycmVudEluZGV4ID49IDAgPyBjdXJyZW50SW5kZXggKyAxIDogMDtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgdGhlIHJvdyBvcmRlciBtYXAgd2l0aCBuZXcgcG9zaXRpb25zIGFmdGVyIHJlb3JkZXJpbmdcbiAgICovXG4gIHByaXZhdGUgdXBkYXRlUm93T3JkZXJNYXAobmV3T3JkZXI6IFRbXSk6IHZvaWQge1xuICAgIG5ld09yZGVyLmZvckVhY2goKGl0ZW0sIGluZGV4KSA9PiB7XG4gICAgICBjb25zdCBpdGVtSWQgPSB0aGlzLmdldEl0ZW1JZChpdGVtKTtcbiAgICAgIGlmIChpdGVtSWQgJiYgdGhpcy5yb3dPcmRlck1hcC5oYXMoaXRlbUlkKSkge1xuICAgICAgICBjb25zdCBjdXJyZW50RW50cnkgPSB0aGlzLnJvd09yZGVyTWFwLmdldChpdGVtSWQpITtcbiAgICAgICAgLy8gQ2FsY3VsYXRlIG5ldyBvcmRlciB2YWx1ZSBiYXNlZCBvbiBwb3NpdGlvbiBhbmQgb3JpZ2luYWwgb3JkZXIgZmllbGRcbiAgICAgICAgY29uc3QgZHJhZ0Ryb3BDb25maWcgPSB0aGlzLmdldERyYWdEcm9wQ29uZmlnKCk7XG4gICAgICAgIGlmIChkcmFnRHJvcENvbmZpZz8ub3JkZXJGaWVsZCkge1xuICAgICAgICAgIC8vIFVzZSB0aGUgb3JkZXIgZmllbGQgZnJvbSBjb25maWcgdG8gY2FsY3VsYXRlIG5ldyBvcmRlciB2YWx1ZXNcbiAgICAgICAgICBjb25zdCBiYXNlT3JkZXIgPSBNYXRoLm1pbiguLi50aGlzLmRpc3BsYXllZERhdGEubWFwKGl0ZW0gPT4gdGhpcy5nZXRJdGVtT3JkZXIoaXRlbSkpKTtcbiAgICAgICAgICBjb25zdCBuZXdPcmRlclZhbHVlID0gYmFzZU9yZGVyICsgaW5kZXg7XG4gICAgICAgICAgY3VycmVudEVudHJ5Lm5ld1Bvc2l0aW9uID0gbmV3T3JkZXJWYWx1ZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBGYWxsYmFjayB0byBpbmRleC1iYXNlZCBwb3NpdGlvblxuICAgICAgICAgIGN1cnJlbnRFbnRyeS5uZXdQb3NpdGlvbiA9IGluZGV4ICsgMTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnJvd09yZGVyTWFwLnNldChpdGVtSWQsIGN1cnJlbnRFbnRyeSk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgXG4gICAgY29uc29sZS5sb2coJ/CflI0gUm93IG9yZGVyIG1hcCB1cGRhdGVkIHdpdGggbmV3IG9yZGVyIHZhbHVlczonLCBBcnJheS5mcm9tKHRoaXMucm93T3JkZXJNYXAuZW50cmllcygpKSk7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgYW55IHJvdyBoYXMgY2hhbmdlZCBwb3NpdGlvbiBieSBjb21wYXJpbmcgb2xkIHZzIG5ldyBwb3NpdGlvbnNcbiAgICovXG4gIHByaXZhdGUgY2hlY2tJZk9yZGVyQ2hhbmdlZCgpOiBib29sZWFuIHtcbiAgICBmb3IgKGNvbnN0IFtpdGVtSWQsIG9yZGVySW5mb10gb2YgdGhpcy5yb3dPcmRlck1hcCkge1xuICAgICAgaWYgKG9yZGVySW5mby5vbGRQb3NpdGlvbiAhPT0gb3JkZXJJbmZvLm5ld1Bvc2l0aW9uKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKCfwn5SNIE9yZGVyIGNoYW5nZSBkZXRlY3RlZCBmb3IgaXRlbTonLCBpdGVtSWQsIG9yZGVySW5mbyk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICBjb25zb2xlLmxvZygn8J+UjSBObyBvcmRlciBjaGFuZ2UgZGV0ZWN0ZWQnKTtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogUmVzZXQgdGhlIHJvdyBvcmRlciBtYXAgdG8gb3JpZ2luYWwgcG9zaXRpb25zIChmb3IgcmVzZXQgYWN0aW9uKVxuICAgKi9cbiAgcHJpdmF0ZSByZXNldFJvd09yZGVyTWFwKCk6IHZvaWQge1xuICAgIC8vIEdldCB0aGUgY3VycmVudCBmdWxsIGRhdGFzZXQgZnJvbSB0aGUgY29uZmlnIChub3QgY2FjaGVkIGRhdGEpXG4gICAgY29uc3QgZnVsbERhdGEgPSB0aGlzLm1lcmdlZENvbmZpZygpLmRhdGEgfHwgW107XG4gICAgXG4gICAgLy8gQ29tcGxldGVseSByZWJ1aWxkIHRoZSBvcmRlciB0cmFja2luZyBmcm9tIGN1cnJlbnQgZGF0YVxuICAgIHRoaXMucm93T3JkZXJNYXAuY2xlYXIoKTtcbiAgICB0aGlzLmluaXRpYWxEYXRhT3JkZXJJZHMgPSBbXTtcbiAgICBcbiAgICAvLyBSZWJ1aWxkIHRoZSBvcmRlciB0cmFja2luZyB3aXRoIGN1cnJlbnQgZGF0YVxuICAgIGZ1bGxEYXRhLmZvckVhY2goKGl0ZW0sIGluZGV4KSA9PiB7XG4gICAgICBjb25zdCBpdGVtSWQgPSB0aGlzLmdldEl0ZW1JZChpdGVtKTtcbiAgICAgIGlmIChpdGVtSWQpIHtcbiAgICAgICAgY29uc3QgYWN0dWFsT3JkZXIgPSB0aGlzLmdldEl0ZW1PcmRlcihpdGVtKTtcbiAgICAgICAgdGhpcy5pbml0aWFsRGF0YU9yZGVySWRzLnB1c2goaXRlbUlkKTtcbiAgICAgICAgdGhpcy5yb3dPcmRlck1hcC5zZXQoaXRlbUlkLCB7XG4gICAgICAgICAgb2xkUG9zaXRpb246IGFjdHVhbE9yZGVyIHx8IChpbmRleCArIDEpLFxuICAgICAgICAgIG5ld1Bvc2l0aW9uOiBhY3R1YWxPcmRlciB8fCAoaW5kZXggKyAxKVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBcbiAgICAvLyBDbGVhciBhbnkgbG9jYWwgcmVvcmRlcmVkIGRhdGFcbiAgICB0aGlzLmxvY2FsUmVvcmRlcmVkRGF0YSA9IFtdO1xuICAgIHRoaXMuaGFzT3JkZXJDaGFuZ2VkLnNldChmYWxzZSk7XG4gICAgXG4gICAgY29uc29sZS5sb2coJ/CflI0gUm93IG9yZGVyIG1hcCBjb21wbGV0ZWx5IHJlYnVpbHQgZnJvbSBjdXJyZW50IGRhdGE6JywgdGhpcy5pbml0aWFsRGF0YU9yZGVySWRzLmxlbmd0aCwgJ2l0ZW1zJyk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIHRoZSByb3cgb3JkZXIgbWFwIGJhc2VsaW5lIHRvIGN1cnJlbnQgcG9zaXRpb25zIChmb3Igc2F2ZSBhY3Rpb24pXG4gICAqL1xuICBwcml2YXRlIHVwZGF0ZVJvd09yZGVyTWFwQmFzZWxpbmUoKTogdm9pZCB7XG4gICAgLy8gR2V0IHRoZSBjdXJyZW50IGZ1bGwgZGF0YXNldCBmcm9tIHRoZSBjb25maWdcbiAgICBjb25zdCBmdWxsRGF0YSA9IHRoaXMubWVyZ2VkQ29uZmlnKCkuZGF0YSB8fCBbXTtcbiAgICBcbiAgICAvLyBDb21wbGV0ZWx5IHJlYnVpbGQgdGhlIG9yZGVyIHRyYWNraW5nIGZyb20gY3VycmVudCBkYXRhXG4gICAgdGhpcy5yb3dPcmRlck1hcC5jbGVhcigpO1xuICAgIHRoaXMuaW5pdGlhbERhdGFPcmRlcklkcyA9IFtdO1xuICAgIFxuICAgIC8vIFJlYnVpbGQgdGhlIG9yZGVyIHRyYWNraW5nIHdpdGggY3VycmVudCBkYXRhXG4gICAgZnVsbERhdGEuZm9yRWFjaCgoaXRlbSwgaW5kZXgpID0+IHtcbiAgICAgIGNvbnN0IGl0ZW1JZCA9IHRoaXMuZ2V0SXRlbUlkKGl0ZW0pO1xuICAgICAgaWYgKGl0ZW1JZCkge1xuICAgICAgICBjb25zdCBhY3R1YWxPcmRlciA9IHRoaXMuZ2V0SXRlbU9yZGVyKGl0ZW0pO1xuICAgICAgICB0aGlzLmluaXRpYWxEYXRhT3JkZXJJZHMucHVzaChpdGVtSWQpO1xuICAgICAgICB0aGlzLnJvd09yZGVyTWFwLnNldChpdGVtSWQsIHtcbiAgICAgICAgICBvbGRQb3NpdGlvbjogYWN0dWFsT3JkZXIgfHwgKGluZGV4ICsgMSksXG4gICAgICAgICAgbmV3UG9zaXRpb246IGFjdHVhbE9yZGVyIHx8IChpbmRleCArIDEpXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0pO1xuICAgIFxuICAgIGNvbnNvbGUubG9nKCfwn5SNIFJvdyBvcmRlciBtYXAgYmFzZWxpbmUgY29tcGxldGVseSByZWJ1aWx0IGZyb20gY3VycmVudCBkYXRhOicsIHRoaXMuaW5pdGlhbERhdGFPcmRlcklkcy5sZW5ndGgsICdpdGVtcycpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgY3VycmVudCBvcmRlciBhcnJheSBmcm9tIHRoZSByb3cgb3JkZXIgbWFwXG4gICAqL1xuICBwcml2YXRlIGdldEN1cnJlbnRPcmRlckZyb21NYXAoKTogVFtdIHtcbiAgICBjb25zdCBvcmRlcmVkSXRlbXM6IEFycmF5PHsgaXRlbTogVDsgbmV3UG9zaXRpb246IG51bWJlciB9PiA9IFtdO1xuICAgIFxuICAgIC8vIEdldCB0aGUgZnVsbCBkYXRhc2V0IGZyb20gdGhlIGNvbmZpZyAobm90IGp1c3QgZGlzcGxheWVkRGF0YSlcbiAgICBjb25zdCBmdWxsRGF0YSA9IHRoaXMubWVyZ2VkQ29uZmlnKCkuZGF0YSB8fCBbXTtcbiAgICBcbiAgICAvLyBDb2xsZWN0IGFsbCBpdGVtcyB3aXRoIHRoZWlyIG5ldyBwb3NpdGlvbnMgZnJvbSB0aGUgZnVsbCBkYXRhc2V0XG4gICAgdGhpcy5yb3dPcmRlck1hcC5mb3JFYWNoKChvcmRlckluZm8sIGl0ZW1JZCkgPT4ge1xuICAgICAgY29uc3QgaXRlbSA9IGZ1bGxEYXRhLmZpbmQoZGF0YUl0ZW0gPT4gdGhpcy5nZXRJdGVtSWQoZGF0YUl0ZW0pID09PSBpdGVtSWQpO1xuICAgICAgaWYgKGl0ZW0pIHtcbiAgICAgICAgb3JkZXJlZEl0ZW1zLnB1c2goeyBpdGVtLCBuZXdQb3NpdGlvbjogb3JkZXJJbmZvLm5ld1Bvc2l0aW9uIH0pO1xuICAgICAgfVxuICAgIH0pO1xuICAgIFxuICAgIC8vIFNvcnQgYnkgbmV3IHBvc2l0aW9uIGFuZCByZXR1cm4gaXRlbXMgaW4gb3JkZXJcbiAgICByZXR1cm4gb3JkZXJlZEl0ZW1zXG4gICAgICAuc29ydCgoYSwgYikgPT4gYS5uZXdQb3NpdGlvbiAtIGIubmV3UG9zaXRpb24pXG4gICAgICAubWFwKG9yZGVyZWRJdGVtID0+IG9yZGVyZWRJdGVtLml0ZW0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZSBsb2NhbCBkYXRhIG9yZGVyIGZvciB2aXN1YWwgcmVvcmRlcmluZyAoTE9DQUwgT05MWSlcbiAgICovXG4gIHByaXZhdGUgdXBkYXRlTG9jYWxEYXRhT3JkZXIobmV3T3JkZXI6IFRbXSk6IHZvaWQge1xuICAgIC8vIFN0b3JlIHRoZSByZW9yZGVyZWQgZGF0YSBsb2NhbGx5IGZvciB2aXN1YWwgZGlzcGxheVxuICAgIHRoaXMubG9jYWxSZW9yZGVyZWREYXRhID0gWy4uLm5ld09yZGVyXTtcbiAgICBjb25zb2xlLmxvZygn8J+UjSBMb2NhbCBkYXRhIG9yZGVyIHVwZGF0ZWQgZm9yIHZpc3VhbCBkaXNwbGF5OicsIHRoaXMubG9jYWxSZW9yZGVyZWREYXRhLmxlbmd0aCwgJ2l0ZW1zJyk7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgdGhlIGN1cnJlbnQgb3JkZXIgaGFzIGNoYW5nZWQgZnJvbSB0aGUgaW5pdGlhbCBvcmRlclxuICAgKi9cbiAgcHJpdmF0ZSBjaGVja09yZGVyQ2hhbmdlZChjdXJyZW50T3JkZXI6IFRbXSk6IGJvb2xlYW4ge1xuICAgIGlmICh0aGlzLmluaXRpYWxEYXRhT3JkZXJJZHMubGVuZ3RoICE9PSBjdXJyZW50T3JkZXIubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgXG4gICAgLy8gQ3JlYXRlIGFycmF5cyBvZiBJRHMgdG8gY29tcGFyZSBvcmRlclxuICAgIGNvbnN0IGN1cnJlbnRJZHMgPSBjdXJyZW50T3JkZXIubWFwKGl0ZW0gPT4gdGhpcy5nZXRJdGVtSWQoaXRlbSkpLmZpbHRlcihpZCA9PiBpZCAhPT0gbnVsbCkgYXMgc3RyaW5nW107XG4gICAgXG4gICAgLy8gQ29tcGFyZSB0aGUgc2VxdWVuY2VzXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmluaXRpYWxEYXRhT3JkZXJJZHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmICh0aGlzLmluaXRpYWxEYXRhT3JkZXJJZHNbaV0gIT09IGN1cnJlbnRJZHNbaV0pIHtcbiAgICAgICAgY29uc29sZS5sb2coJ/CflI0gT3JkZXIgY2hhbmdlZCBkZXRlY3RlZDonLCB7XG4gICAgICAgICAgcG9zaXRpb246IGksXG4gICAgICAgICAgaW5pdGlhbDogdGhpcy5pbml0aWFsRGF0YU9yZGVySWRzW2ldLFxuICAgICAgICAgIGN1cnJlbnQ6IGN1cnJlbnRJZHNbaV1cbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICBjb25zb2xlLmxvZygn8J+UjSBObyBvcmRlciBjaGFuZ2UgZGV0ZWN0ZWQnKTtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBvblJvd0NsaWNrKGl0ZW06IFQpIHtcbiAgICBpZiAodGhpcy5tZXJnZWRDb25maWcoKS5vblJvd0NsaWNrKSB7XG4gICAgICB0aGlzLmVtaXRFdmVudCgncm93Q2xpY2snLCBpdGVtKTtcbiAgICB9XG4gIH1cblxuICBvbkFjdGlvbkNsaWNrKGl0ZW06IFQsIGFjdGlvbjogR3JpZEFjdGlvbikge1xuICAgIC8vIEhhbmRsZSB0cmVlIGV4cGFuZC9jb2xsYXBzZSBhY3Rpb25zXG4gICAgaWYgKGFjdGlvbi5rZXkgPT09ICd0b2dnbGUtZXhwYW5kJyAmJiB0aGlzLm1lcmdlZENvbmZpZygpLnRyZWU/LmVuYWJsZWQpIHtcbiAgICAgIHRoaXMudG9nZ2xlVHJlZUV4cGFuZChpdGVtKTtcbiAgICB9XG4gICAgXG4gICAgLy8gSGFuZGxlIG9yZGVyIG1hbmFnZW1lbnQgYWN0aW9uc1xuICAgIGlmIChhY3Rpb24ua2V5ID09PSAncmVzZXQtb3JkZXInKSB7XG4gICAgICB0aGlzLmhhc09yZGVyQ2hhbmdlZC5zZXQoZmFsc2UpO1xuICAgICAgLy8gUmVzZXQgdGhlIHJvdyBvcmRlciBtYXAgdG8gY3VycmVudCBwb3NpdGlvbnMgKExPQ0FMIE9OTFkpXG4gICAgICB0aGlzLnJlc2V0Um93T3JkZXJNYXAoKTtcbiAgICAgIC8vIENsZWFyIGxvY2FsIHJlb3JkZXJlZCBkYXRhIHRvIHJldmVydCB0byBvcmlnaW5hbCBkaXNwbGF5XG4gICAgICB0aGlzLmxvY2FsUmVvcmRlcmVkRGF0YSA9IFtdO1xuICAgICAgY29uc29sZS5sb2coJ/CflI0gUmVzZXQgY2xpY2tlZCwgcm93IG9yZGVyIG1hcCByZXNldCBsb2NhbGx5LCB2aXN1YWwgZGlzcGxheSByZXZlcnRlZCcpO1xuICAgIH0gZWxzZSBpZiAoYWN0aW9uLmtleSA9PT0gJ3NhdmUtb3JkZXInKSB7XG4gICAgICB0aGlzLmhhc09yZGVyQ2hhbmdlZC5zZXQoZmFsc2UpO1xuICAgICAgLy8gVXBkYXRlIHRoZSByb3cgb3JkZXIgbWFwIHRvIGN1cnJlbnQgcG9zaXRpb25zIGFzIG5ldyBiYXNlbGluZVxuICAgICAgdGhpcy51cGRhdGVSb3dPcmRlck1hcEJhc2VsaW5lKCk7XG4gICAgICAvLyBDbGVhciBsb2NhbCByZW9yZGVyZWQgZGF0YSBhcyBpdCdzIG5vdyB0aGUgYmFzZWxpbmVcbiAgICAgIHRoaXMubG9jYWxSZW9yZGVyZWREYXRhID0gW107XG4gICAgICBjb25zb2xlLmxvZygn8J+UjSBTYXZlIGNsaWNrZWQsIHJvdyBvcmRlciBtYXAgdXBkYXRlZCB0byBuZXcgYmFzZWxpbmUsIGxvY2FsIGRhdGEgY2xlYXJlZCcpO1xuICAgICAgXG4gICAgICAvLyBOT1cgZW1pdCB0aGUgcmVvcmRlciBldmVudCB0byB1cGRhdGUgZXh0ZXJuYWwgY29tcG9uZW50c1xuICAgICAgY29uc3QgY3VycmVudE9yZGVyID0gdGhpcy5nZXRDdXJyZW50T3JkZXJGcm9tTWFwKCk7XG4gICAgICBpZiAoY3VycmVudE9yZGVyLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc29sZS5sb2coJ/CflI0gRW1pdHRpbmcgcm93UmVvcmRlciBldmVudCB0byB1cGRhdGUgZXh0ZXJuYWwgY29tcG9uZW50czonLCBjdXJyZW50T3JkZXIpO1xuICAgICAgICAvLyBVc2UgdGhlIGV4cGVjdGVkIEdyaWRFdmVudCBzdHJ1Y3R1cmUgZm9yIHJvd1Jlb3JkZXJcbiAgICAgICAgdGhpcy5lbWl0RXZlbnQoJ3Jvd1Jlb3JkZXInLCB7XG4gICAgICAgICAgc291cmNlSXRlbTogY3VycmVudE9yZGVyWzBdLCAvLyBGaXJzdCBpdGVtIGFzIHNvdXJjZVxuICAgICAgICAgIHNvdXJjZUluZGV4OiAwLCAvLyBGaXJzdCBwb3NpdGlvbiBhcyBzb3VyY2VcbiAgICAgICAgICB0YXJnZXRJdGVtOiBjdXJyZW50T3JkZXJbY3VycmVudE9yZGVyLmxlbmd0aCAtIDFdLCAvLyBMYXN0IGl0ZW0gYXMgdGFyZ2V0XG4gICAgICAgICAgdGFyZ2V0SW5kZXg6IGN1cnJlbnRPcmRlci5sZW5ndGggLSAxLCAvLyBMYXN0IHBvc2l0aW9uIGFzIHRhcmdldFxuICAgICAgICAgIG5ld09yZGVyOiBjdXJyZW50T3JkZXIgLy8gVGhlIGNvbXBsZXRlIG5ldyBvcmRlclxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgaWYgKHRoaXMuYWN0aW9uSGFuZGxlcnNbYWN0aW9uLm9uQ2xpY2tdKSB7XG4gICAgICB0aGlzLmFjdGlvbkhhbmRsZXJzW2FjdGlvbi5vbkNsaWNrXShpdGVtLCBhY3Rpb24pO1xuICAgIH1cbiAgICB0aGlzLmVtaXRFdmVudCgnYWN0aW9uJywgaXRlbSwgdW5kZWZpbmVkLCBhY3Rpb24pO1xuICB9XG5cbiAgLy8gPT09PT0gVXRpbGl0eSBNZXRob2RzID09PT09XG4gIFxuICBwcml2YXRlIGVtaXRFdmVudCh0eXBlOiBHcmlkRXZlbnQ8VD5bJ3R5cGUnXSwgZGF0YTogR3JpZEV2ZW50PFQ+WydkYXRhJ10sIGNvbHVtbj86IEdyaWRDb2x1bW4sIGFjdGlvbj86IEdyaWRBY3Rpb24pIHtcbiAgICB0aGlzLmdyaWRFdmVudC5lbWl0KHsgdHlwZSwgZGF0YSwgY29sdW1uLCBhY3Rpb24gfSBhcyBHcmlkRXZlbnQ8VD4pO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBuZXN0ZWQgdmFsdWUgZnJvbSBhbiBvYmplY3RcbiAgICogQHBhcmFtIG9iaiAtIFRoZSBvYmplY3QgdG8gZ2V0IHRoZSBuZXN0ZWQgdmFsdWUgZnJvbVxuICAgKiBAcGFyYW0gcGF0aCAtIFRoZSBwYXRoIHRvIHRoZSBuZXN0ZWQgdmFsdWUgKGUuZy4sICdjb250YWN0LmVtYWlsJylcbiAgICogQHJldHVybnMgVGhlIG5lc3RlZCB2YWx1ZSBvciB1bmRlZmluZWQgaWYgbm90IGZvdW5kXG4gICAqIEBleGFtcGxlXG4gICAqIGNvbnN0IG9iaiA9IHsgY29udGFjdDogeyBlbWFpbDogJ3Rlc3RAZXhhbXBsZS5jb20nIH0gfTtcbiAgICogY29uc3QgdmFsdWUgPSBnZXROZXN0ZWRWYWx1ZShvYmosICdjb250YWN0LmVtYWlsJyk7XG4gICAqIC8vIHZhbHVlID09PSAndGVzdEBleGFtcGxlLmNvbSdcbiAgICovXG4gIGdldE5lc3RlZFZhbHVlKG9iajogVCwgcGF0aDogc3RyaW5nKTogdW5rbm93biB7XG4gICAgcmV0dXJuIHBhdGguc3BsaXQoJy4nKS5yZWR1Y2UoKGN1cnJlbnQ6IHVua25vd24sIGtleTogc3RyaW5nKSA9PiB7XG4gICAgICByZXR1cm4gY3VycmVudCAmJiB0eXBlb2YgY3VycmVudCA9PT0gJ29iamVjdCcgPyAoY3VycmVudCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPilba2V5XSA6IHVuZGVmaW5lZDtcbiAgICB9LCBvYmopO1xuICB9XG5cbiAgZm9ybWF0VmFsdWUodmFsdWU6IHVua25vd24sIGNvbHVtbjogR3JpZENvbHVtbik6IHN0cmluZyB7XG4gICAgaWYgKGNvbHVtbi5mb3JtYXR0ZXIpIHtcbiAgICAgIGlmIChjb2x1bW4uZm9ybWF0dGVyLnR5cGUgPT09ICdjdXN0b20nICYmIGNvbHVtbi5mb3JtYXR0ZXIuY3VzdG9tRnVuY3Rpb24pIHtcbiAgICAgICAgY29uc3QgZm9ybWF0dGVyID0gdGhpcy5jdXN0b21Gb3JtYXR0ZXJzW2NvbHVtbi5mb3JtYXR0ZXIuY3VzdG9tRnVuY3Rpb25dO1xuICAgICAgICBpZiAoZm9ybWF0dGVyKSB7XG4gICAgICAgICAgcmV0dXJuIGZvcm1hdHRlcih2YWx1ZSwgY29sdW1uLmZvcm1hdHRlci5mb3JtYXQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIEJ1aWx0LWluIGZvcm1hdHRlcnNcbiAgICAgIHN3aXRjaCAoY29sdW1uLmZvcm1hdHRlci50eXBlKSB7XG4gICAgICAgIGNhc2UgJ2RhdGUnOlxuICAgICAgICAgIHJldHVybiB0aGlzLmZvcm1hdERhdGUodmFsdWUpO1xuICAgICAgICBjYXNlICdjdXJyZW5jeSc6XG4gICAgICAgICAgcmV0dXJuIHRoaXMuZm9ybWF0Q3VycmVuY3kodmFsdWUsIGNvbHVtbi5mb3JtYXR0ZXIuZm9ybWF0KTtcbiAgICAgICAgY2FzZSAncGVyY2VudGFnZSc6XG4gICAgICAgICAgcmV0dXJuIHRoaXMuZm9ybWF0UGVyY2VudGFnZSh2YWx1ZSk7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgcmV0dXJuIFN0cmluZyh2YWx1ZSB8fCAnJyk7XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBTdHJpbmcodmFsdWUgfHwgJycpO1xuICB9XG5cbiAgcHJpdmF0ZSBmb3JtYXREYXRlKHZhbHVlOiB1bmtub3duKTogc3RyaW5nIHtcbiAgICBpZiAoIXZhbHVlKSByZXR1cm4gJ04vQSc7XG4gICAgY29uc3QgZGF0ZSA9IG5ldyBEYXRlKHZhbHVlIGFzIHN0cmluZyk7XG4gICAgcmV0dXJuIGlzTmFOKGRhdGUuZ2V0VGltZSgpKSA/ICdJbnZhbGlkIERhdGUnIDogZGF0ZS50b0xvY2FsZURhdGVTdHJpbmcoKTtcbiAgfVxuXG4gIHByaXZhdGUgZm9ybWF0Q3VycmVuY3kodmFsdWU6IHVua25vd24sIGN1cnJlbmN5ID0gJ1VTRCcpOiBzdHJpbmcge1xuICAgIGlmICh2YWx1ZSA9PT0gbnVsbCB8fCB2YWx1ZSA9PT0gdW5kZWZpbmVkKSByZXR1cm4gJ04vQSc7XG4gICAgY29uc3QgbnVtID0gTnVtYmVyKHZhbHVlKTtcbiAgICByZXR1cm4gaXNOYU4obnVtKSA/ICdOL0EnIDogbmV3IEludGwuTnVtYmVyRm9ybWF0KCdlbi1VUycsIHsgXG4gICAgICBzdHlsZTogJ2N1cnJlbmN5JywgXG4gICAgICBjdXJyZW5jeSBcbiAgICB9KS5mb3JtYXQobnVtKTtcbiAgfVxuXG4gIHByaXZhdGUgZm9ybWF0UGVyY2VudGFnZSh2YWx1ZTogdW5rbm93bik6IHN0cmluZyB7XG4gICAgaWYgKHZhbHVlID09PSBudWxsIHx8IHZhbHVlID09PSB1bmRlZmluZWQpIHJldHVybiAnTi9BJztcbiAgICBjb25zdCBudW0gPSBOdW1iZXIodmFsdWUpO1xuICAgIHJldHVybiBpc05hTihudW0pID8gJ04vQScgOiBgJHtudW19JWA7XG4gIH1cblxuICByZW5kZXJDdXN0b21DZWxsKHZhbHVlOiB1bmtub3duLCByb3c6IFQsIGNvbHVtbjogR3JpZENvbHVtbik6IHN0cmluZyB7XG4gICAgaWYgKGNvbHVtbi5yZW5kZXJlciAmJiB0aGlzLmN1c3RvbVJlbmRlcmVyc1tjb2x1bW4ucmVuZGVyZXJdKSB7XG4gICAgICByZXR1cm4gdGhpcy5jdXN0b21SZW5kZXJlcnNbY29sdW1uLnJlbmRlcmVyXSh2YWx1ZSwgcm93KTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZm9ybWF0VmFsdWUodmFsdWUsIGNvbHVtbik7XG4gIH1cblxuICAvLyBFbmhhbmNlZCByZW5kZXJlciBtZXRob2RzIGZvciB0ZW1wbGF0ZSBzdXBwb3J0XG4gIGlzVGVtcGxhdGVSZW5kZXJlcihyZW5kZXJlcktleTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICEhdGhpcy50ZW1wbGF0ZVJlbmRlcmVyc1tyZW5kZXJlcktleV07XG4gIH1cblxuICBpc1N0cmluZ1JlbmRlcmVyKHJlbmRlcmVyS2V5OiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gISF0aGlzLmN1c3RvbVJlbmRlcmVyc1tyZW5kZXJlcktleV07XG4gIH1cblxuICBnZXRUZW1wbGF0ZVJlbmRlcmVyKHJlbmRlcmVyS2V5OiBzdHJpbmcpOiBUZW1wbGF0ZVJlbmRlcmVyPFQ+IHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMudGVtcGxhdGVSZW5kZXJlcnNbcmVuZGVyZXJLZXldIGFzIFRlbXBsYXRlUmVuZGVyZXI8VD4gfCBudWxsO1xuICB9XG5cbiAgZ2V0U3RyaW5nUmVuZGVyZXIocmVuZGVyZXJLZXk6IHN0cmluZyk6IFN0cmluZ1JlbmRlcmVyPFQ+IHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuY3VzdG9tUmVuZGVyZXJzW3JlbmRlcmVyS2V5XSB8fCBudWxsO1xuICB9XG5cbiAgZ2V0VGVtcGxhdGVDb250ZXh0KHZhbHVlOiB1bmtub3duLCByb3c6IFQsIGNvbHVtbjogR3JpZENvbHVtbik6IFRlbXBsYXRlQ29udGV4dDxUPiB7XG4gICAgcmV0dXJuIHtcbiAgICAgICRpbXBsaWNpdDogdmFsdWUsXG4gICAgICByb3csXG4gICAgICB2YWx1ZSxcbiAgICAgIGNvbHVtblxuICAgIH07XG4gIH1cblxuICBnZXRDb2x1bW5XaWR0aENsYXNzKHdpZHRoPzogQ29sdW1uV2lkdGgpOiBzdHJpbmcge1xuICAgIGlmICghd2lkdGggfHwgd2lkdGggPT09ICdhdXRvJykgcmV0dXJuICcnO1xuICAgIFxuICAgIGNvbnN0IHdpZHRoTWFwID0ge1xuICAgICAgJ3hzJzogJ3R3LXctMTYnLFxuICAgICAgJ3NtJzogJ3R3LXctMjQnLFxuICAgICAgJ21kJzogJ3R3LXctMzInLFxuICAgICAgJ2xnJzogJ3R3LXctNDgnLFxuICAgICAgJ3hsJzogJ3R3LXctNjQnXG4gICAgfTtcbiAgICBcbiAgICByZXR1cm4gd2lkdGhNYXBbd2lkdGggYXMga2V5b2YgdHlwZW9mIHdpZHRoTWFwXSB8fCBgdHctdy1bJHt3aWR0aH1dYDtcbiAgfVxuXG4gIGdldENvbHVtbk1heFdpZHRoQ2xhc3Mod2lkdGg/OiBDb2x1bW5XaWR0aCk6IHN0cmluZyB7XG4gICAgaWYgKCF3aWR0aCB8fCB3aWR0aCA9PT0gJ2F1dG8nKSByZXR1cm4gJ3R3LW1heC13LXhzJztcbiAgICBcbiAgICBjb25zdCBtYXhXaWR0aE1hcCA9IHtcbiAgICAgICd4cyc6ICd0dy1tYXgtdy0xNicsXG4gICAgICAnc20nOiAndHctbWF4LXctMjQnLFxuICAgICAgJ21kJzogJ3R3LW1heC13LTMyJyxcbiAgICAgICdsZyc6ICd0dy1tYXgtdy00OCcsXG4gICAgICAneGwnOiAndHctbWF4LXctNjQnXG4gICAgfTtcbiAgICBcbiAgICByZXR1cm4gbWF4V2lkdGhNYXBbd2lkdGggYXMga2V5b2YgdHlwZW9mIG1heFdpZHRoTWFwXSB8fCBgdHctbWF4LXctWyR7d2lkdGh9XWA7XG4gIH1cblxuICBnZXRTdGF0dXNDbGFzcyh2YWx1ZTogdW5rbm93biwgc3RhdHVzQ29uZmlnPzogU3RhdHVzQ29uZmlnKTogc3RyaW5nIHtcbiAgICBpZiAoIXN0YXR1c0NvbmZpZykgcmV0dXJuICcnO1xuICAgIHJldHVybiB2YWx1ZSA9PT0gc3RhdHVzQ29uZmlnLmFjdGl2ZVZhbHVlID8gc3RhdHVzQ29uZmlnLmFjdGl2ZUNsYXNzIDogc3RhdHVzQ29uZmlnLmluYWN0aXZlQ2xhc3M7XG4gIH1cblxuICBnZXRTdGF0dXNUZXh0KHZhbHVlOiB1bmtub3duLCBzdGF0dXNDb25maWc/OiBTdGF0dXNDb25maWcpOiBzdHJpbmcge1xuICAgIGlmICghc3RhdHVzQ29uZmlnKSByZXR1cm4gU3RyaW5nKHZhbHVlIHx8ICcnKTtcbiAgICByZXR1cm4gdmFsdWUgPT09IHN0YXR1c0NvbmZpZy5hY3RpdmVWYWx1ZSA/IHN0YXR1c0NvbmZpZy5hY3RpdmVMYWJlbCA6IHN0YXR1c0NvbmZpZy5pbmFjdGl2ZUxhYmVsO1xuICB9XG5cbiAgZ2V0SXRlbVJhbmdlVGV4dCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IHRvdGFsID0gdGhpcy50b3RhbEl0ZW1zKCk7XG4gICAgXG4gICAgaWYgKHRoaXMuc2VydmVyU2lkZVBhZ2luYXRpb24pIHtcbiAgICAgIC8vIEZvciBzZXJ2ZXItc2lkZSBwYWdpbmF0aW9uLCBjYWxjdWxhdGUgYmFzZWQgb24gYWN0dWFsIGRpc3BsYXllZCBkYXRhXG4gICAgICBjb25zdCBkaXNwbGF5ZWRDb3VudCA9IHRoaXMuZGlzcGxheWVkRGF0YS5sZW5ndGg7XG4gICAgICBjb25zdCBzdGFydCA9ICh0aGlzLmN1cnJlbnRQYWdlKCkgLSAxKSAqIHRoaXMucGFnZVNpemUoKSArIDE7XG4gICAgICBjb25zdCBlbmQgPSBzdGFydCArIGRpc3BsYXllZENvdW50IC0gMTtcbiAgICAgIHJldHVybiBgJHtzdGFydH0tJHtlbmR9IG9mICR7dG90YWx9YDtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gRm9yIGNsaWVudC1zaWRlIHBhZ2luYXRpb24sIHVzZSB0aGUgb3JpZ2luYWwgbG9naWNcbiAgICAgIGNvbnN0IHN0YXJ0ID0gKHRoaXMuY3VycmVudFBhZ2UoKSAtIDEpICogdGhpcy5wYWdlU2l6ZSgpICsgMTtcbiAgICAgIGNvbnN0IGVuZCA9IE1hdGgubWluKHRoaXMuY3VycmVudFBhZ2UoKSAqIHRoaXMucGFnZVNpemUoKSwgdG90YWwpO1xuICAgICAgcmV0dXJuIGAke3N0YXJ0fS0ke2VuZH0gb2YgJHt0b3RhbH1gO1xuICAgIH1cbiAgfVxuXG4gIGdldFBhZ2VTaXplT3B0aW9ucygpIHtcbiAgICByZXR1cm4gdGhpcy5tZXJnZWRDb25maWcoKS5wYWdpbmF0aW9uLnBhZ2VTaXplT3B0aW9ucy5tYXAoc2l6ZSA9PiAoe1xuICAgICAgdmFsdWU6IHNpemUsXG4gICAgICBsYWJlbDogc2l6ZS50b1N0cmluZygpXG4gICAgfSkpO1xuICB9XG5cbiAgdHJhY2tCeUZuKGluZGV4OiBudW1iZXIsIGl0ZW06IFQpOiB1bmtub3duIHtcbiAgICBjb25zdCB0cmFja0J5ID0gdGhpcy5tZXJnZWRDb25maWcoKS50cmFja0J5IHx8ICdfaWQnO1xuICAgIHJldHVybiB0aGlzLmdldE5lc3RlZFZhbHVlKGl0ZW0sIHRyYWNrQnkpIHx8IGluZGV4O1xuICB9XG5cbiAgZ2V0U2tlbGV0b25BcnJheSgpOiBudW1iZXJbXSB7XG4gICAgcmV0dXJuIEFycmF5LmZyb20oeyBsZW5ndGg6IHRoaXMubG9hZGluZ0NvbmZpZy5za2VsZXRvblJvd3MgfSwgKF8sIGkpID0+IGkpO1xuICB9XG5cbiAgLy8gPT09PT0gVGVtcGxhdGUgSGVscGVyIE1ldGhvZHMgPT09PT1cbiAgXG4gIGdldCBkaXNwbGF5ZWREYXRhKCk6IFRbXSB7XG4gICAgLy8gSWYgd2UgaGF2ZSBsb2NhbCByZW9yZGVyZWQgZGF0YSAoZnJvbSBkcmFnICYgZHJvcCksIHVzZSB0aGF0IGZvciB2aXN1YWwgZGlzcGxheVxuICAgIGlmICh0aGlzLmxvY2FsUmVvcmRlcmVkRGF0YS5sZW5ndGggPiAwICYmIHRoaXMuaGFzT3JkZXJDaGFuZ2VkKCkpIHtcbiAgICAgIGNvbnNvbGUubG9nKCfwn5SNIFVzaW5nIGxvY2FsIHJlb3JkZXJlZCBkYXRhIGZvciBkaXNwbGF5OicsIHRoaXMubG9jYWxSZW9yZGVyZWREYXRhLmxlbmd0aCwgJ2l0ZW1zJyk7XG4gICAgICByZXR1cm4gdGhpcy5sb2NhbFJlb3JkZXJlZERhdGE7XG4gICAgfVxuICAgIFxuICAgIGNvbnN0IGRhdGEgPSB0aGlzLmZpbHRlcmVkRGF0YSgpO1xuICAgIFxuICAgIC8vIEZvciBzZXJ2ZXItc2lkZSBwYWdpbmF0aW9uLCB0aGUgc2VydmVyIGFscmVhZHkgcHJvdmlkZXMgdGhlIGNvcnJlY3QgcGFnZSBvZiBkYXRhXG4gICAgLy8gRm9yIGNsaWVudC1zaWRlIHBhZ2luYXRpb24sIHdlIG5lZWQgdG8gc2xpY2UgdGhlIGRhdGFcbiAgICBpZiAodGhpcy5zZXJ2ZXJTaWRlUGFnaW5hdGlvbikge1xuICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHN0YXJ0ID0gKHRoaXMuY3VycmVudFBhZ2UoKSAtIDEpICogdGhpcy5wYWdlU2l6ZSgpO1xuICAgICAgY29uc3QgZW5kID0gc3RhcnQgKyB0aGlzLnBhZ2VTaXplKCk7XG4gICAgICByZXR1cm4gZGF0YS5zbGljZShzdGFydCwgZW5kKTtcbiAgICB9XG4gIH1cblxuICBnZXQgY29sdW1ucygpOiBHcmlkQ29sdW1uW10ge1xuICAgIGNvbnN0IGJhc2VDb2x1bW5zID0gdGhpcy5tZXJnZWRDb25maWcoKS5jb2x1bW5zIHx8IFtdO1xuICAgIHJldHVybiBiYXNlQ29sdW1ucztcbiAgfVxuXG4gIGdldCBwYWdpbmF0aW9uQ29uZmlnKCkge1xuICAgIHJldHVybiB0aGlzLm1lcmdlZENvbmZpZygpLnBhZ2luYXRpb247XG4gIH1cblxuICBnZXQgc2VhcmNoQ29uZmlnKCkge1xuICAgIHJldHVybiB0aGlzLm1lcmdlZENvbmZpZygpLnNlYXJjaDtcbiAgfVxuXG4gIGdldCBsb2FkaW5nQ29uZmlnKCkge1xuICAgIHJldHVybiB0aGlzLm1lcmdlZENvbmZpZygpLmxvYWRpbmc7XG4gIH1cblxuICBnZXQgc2Nyb2xsQ29uZmlnKCkge1xuICAgIHJldHVybiB0aGlzLm1lcmdlZENvbmZpZygpLnNjcm9sbDtcbiAgfVxufSAiLCIgIDwhLS0gRGF0YSBHcmlkIENvbXBvbmVudCAtLT5cbiAgPGRpdiBjbGFzcz1cImRhdGEtZ3JpZC1jb250YWluZXIgdHctYmctd2hpdGUgdHctc2hhZG93IHR3LXJvdW5kZWQtbGcgdHctb3ZlcmZsb3ctdmlzaWJsZSB0dy1mbGV4IHR3LWZsZXgtY29sXCIgXG4gICAgICAgW25nQ2xhc3NdPVwiW1xuICAgICAgICAgbWVyZ2VkQ29uZmlnKCkudGFibGVDbGFzcyB8fCAnJyxcbiAgICAgICAgIG1lcmdlZENvbmZpZygpLmZ1bGxIZWlnaHQgPyAndHctaC1mdWxsJyA6ICcnLFxuICAgICAgICAgaXNEcmFnRHJvcEVuYWJsZWQoKSA/ICdkcmFnLWRyb3AtZW5hYmxlZCcgOiAnJyxcbiAgICAgICAgIGlzVHJlZUVuYWJsZWQoKSA/ICd0cmVlLWVuYWJsZWQnIDogJydcbiAgICAgICBdXCI+XG4gIFxuICA8IS0tIEhlYWRlciBTZWN0aW9uIC0tPlxuICBAaWYgKG1lcmdlZENvbmZpZygpLnRpdGxlIHx8IG1lcmdlZENvbmZpZygpLnN1YnRpdGxlKSB7XG4gICAgPGRpdiBjbGFzcz1cInR3LXB4LTMgdHctcHktMiB0dy1ib3JkZXItYiB0dy1ib3JkZXItZ3JheS0yMDBcIj5cbiAgICAgIEBpZiAobWVyZ2VkQ29uZmlnKCkudGl0bGUpIHtcbiAgICAgICAgPGgzIGNsYXNzPVwidHctdGV4dC1iYXNlIHR3LWZvbnQtc2VtaWJvbGQgdHctdGV4dC1ncmF5LTkwMFwiPlxuICAgICAgICAgIHt7IG1lcmdlZENvbmZpZygpLnRpdGxlIH19XG4gICAgICAgIDwvaDM+XG4gICAgICB9XG4gICAgICBAaWYgKG1lcmdlZENvbmZpZygpLnN1YnRpdGxlKSB7XG4gICAgICAgIDxwIGNsYXNzPVwidHctdGV4dC1zbSB0dy10ZXh0LWdyYXktNjAwIHR3LW10LTAuNVwiPlxuICAgICAgICAgIHt7IG1lcmdlZENvbmZpZygpLnN1YnRpdGxlIH19XG4gICAgICAgIDwvcD5cbiAgICAgIH1cbiAgICA8L2Rpdj5cbiAgfVxuXG4gIDwhLS0gU2VhcmNoIFNlY3Rpb24gLS0+XG4gIEBpZiAoc2VhcmNoQ29uZmlnLmVuYWJsZWQpIHtcbiAgICA8ZGl2IGNsYXNzPVwidHctcHgtMyB0dy1weS0yIHR3LWJvcmRlci1iIHR3LWJvcmRlci1ncmF5LTIwMFwiPlxuICAgICAgPGRpdiBjbGFzcz1cInR3LWZsZXggdHctaXRlbXMtY2VudGVyIHR3LWp1c3RpZnktYmV0d2VlblwiPlxuICAgICAgICA8IS0tIFNlYXJjaCBJbnB1dCAtLT5cbiAgICAgICAgPGRpdiBjbGFzcz1cInR3LW1heC13LW1kXCI+XG4gICAgICAgICAgPGNpZGUtZWxlLWlucHV0IFtsYWJlbEhpZGVdPVwidHJ1ZVwiIFtoaWRlSGVscGVyQW5kRXJyb3JUZXh0XT1cInRydWVcIiBpZD1cInNlYXJjaC1pbnB1dFwiIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgICAgIFtuZ01vZGVsXT1cInNlYXJjaFF1ZXJ5KClcIlxuICAgICAgICAgICAgKG5nTW9kZWxDaGFuZ2UpPVwidXBkYXRlU2VhcmNoUXVlcnkoJGV2ZW50KVwiXG4gICAgICAgICAgICBbcGxhY2Vob2xkZXJdPVwic2VhcmNoQ29uZmlnLnBsYWNlaG9sZGVyXCJcbiAgICAgICAgICAgIFtkaXNhYmxlZF09XCJsb2FkaW5nKCkgfHwgaXNSZWZyZXNoaW5nKClcIlxuICAgICAgICAgICAgbGVhZGluZ0ljb249XCJzZWFyY2hcIlxuICAgICAgICAgICAgZmlsbD1cIm91dGxpbmVcIj5cbiAgICAgICAgICA8L2NpZGUtZWxlLWlucHV0PlxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgXG4gICAgICAgIDwhLS0gRHJhZyBPcmRlciBBY3Rpb25zIC0tPlxuICAgICAgICBAaWYgKGlzRHJhZ0Ryb3BFbmFibGVkKCkgJiYgKGlzRHJhZ2dpbmcoKSB8fCBoYXNPcmRlckNoYW5nZWQoKSkpIHtcbiAgICAgICAgICA8ZGl2IGNsYXNzPVwidHctZmxleCB0dy1pdGVtcy1jZW50ZXIgdHctc3BhY2UteC0yXCI+XG4gICAgICAgICAgICA8YnV0dG9uIGNpZGVFbGVCdXR0b24gXG4gICAgICAgICAgICAgICAgICAgIHZhcmlhbnQ9XCJvdXRsaW5lXCIgXG4gICAgICAgICAgICAgICAgICAgIHNpemU9XCJzbVwiIFxuICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwib25BY3Rpb25DbGljayhudWxsLCB7IGtleTogJ3Jlc2V0LW9yZGVyJywgbGFiZWw6ICdSZXNldCBPcmRlcicsIGljb246ICd1bmRvJywgdmFyaWFudDogJ291dGxpbmUnLCBvbkNsaWNrOiAncmVzZXRPcmRlcicgfSlcIlxuICAgICAgICAgICAgICAgICAgICBjbGFzcz1cInR3LXRleHQtYmx1ZS03MDAgdHctYm9yZGVyLWJsdWUtMzAwIGhvdmVyOnR3LWJnLWJsdWUtMTAwXCI+XG4gICAgICAgICAgICAgIDxjaWRlLWVsZS1pY29uIHNpemU9XCJ4c1wiIGNsYXNzPVwidHctdy00IHR3LWgtNCB0dy1tci0xXCI+dW5kbzwvY2lkZS1lbGUtaWNvbj5cbiAgICAgICAgICAgICAgUmVzZXQgT3JkZXJcbiAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgPGJ1dHRvbiBjaWRlRWxlQnV0dG9uIFxuICAgICAgICAgICAgICAgICAgICB2YXJpYW50PVwicHJpbWFyeVwiIFxuICAgICAgICAgICAgICAgICAgICBzaXplPVwic21cIiBcbiAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uQWN0aW9uQ2xpY2sobnVsbCwgeyBrZXk6ICdzYXZlLW9yZGVyJywgbGFiZWw6ICdTYXZlIE9yZGVyJywgaWNvbjogJ3NhdmUnLCB2YXJpYW50OiAncHJpbWFyeScsIG9uQ2xpY2s6ICdzYXZlT3JkZXInIH0pXCJcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJ0dy1iZy1ibHVlLTYwMCBob3Zlcjp0dy1iZy1ibHVlLTcwMCB0dy10ZXh0LXdoaXRlXCI+XG4gICAgICAgICAgICAgIDxjaWRlLWVsZS1pY29uIHNpemU9XCJ4c1wiIGNsYXNzPVwidHctdy00IHR3LWgtNCB0dy1tci0xXCI+c2F2ZTwvY2lkZS1lbGUtaWNvbj5cbiAgICAgICAgICAgICAgU2F2ZSBPcmRlclxuICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICB9XG5cbiAgICAgPCEtLSBUYWJsZSBTZWN0aW9uIC0tPlxuICAgPGRpdiBjbGFzcz1cInR3LW92ZXJmbG93LXgtYXV0byB0dy1yZWxhdGl2ZVwiXG4gICAgICAgIFtuZ0NsYXNzXT1cIntcbiAgICAgICAgICAndHctZmxleC0xIHR3LW1pbi1oLTAnOiBtZXJnZWRDb25maWcoKS5mdWxsSGVpZ2h0LFxuICAgICAgICAgICd0dy1vdmVyZmxvdy15LWF1dG8nOiBzY3JvbGxDb25maWc/LmVuYWJsZWQsXG4gICAgICAgICAgJ3R3LW1heC1oLWZ1bGwnOiBzY3JvbGxDb25maWc/LmVuYWJsZWRcbiAgICAgICAgfVwiXG4gICAgICAgIFtzdHlsZS5tYXhIZWlnaHRdPVwic2Nyb2xsQ29uZmlnPy5lbmFibGVkID8gc2Nyb2xsQ29uZmlnPy5tYXhIZWlnaHQgOiBudWxsXCJcbiAgICAgICAgW3N0eWxlLm1pbkhlaWdodF09XCJzY3JvbGxDb25maWc/LmVuYWJsZWQgPyBzY3JvbGxDb25maWc/Lm1pbkhlaWdodCA6IG51bGxcIj5cbiAgICA8dGFibGUgY2xhc3M9XCJ0dy1taW4tdy1mdWxsIHR3LWRpdmlkZS15IHR3LWRpdmlkZS1ncmF5LTIwMCB0dy1oLWZ1bGwgdHctdGFibGUtZml4ZWRcIlxuICAgICAgICAgICBbY2xhc3MuZW1wdHktdGFibGVdPVwiZGlzcGxheWVkRGF0YS5sZW5ndGggPT09IDBcIlxuICAgICAgICAgICBbbmdDbGFzc109XCJ7XG4gICAgICAgICAgICAgJ3R3LXRhYmxlLXN0cmlwZWQnOiBtZXJnZWRDb25maWcoKS5zdHJpcGVkLFxuICAgICAgICAgICAgICd0dy1ib3JkZXInOiBtZXJnZWRDb25maWcoKS5ib3JkZXJlZCxcbiAgICAgICAgICAgICAndHctdGFibGUtc20nOiBtZXJnZWRDb25maWcoKS5jb21wYWN0XG4gICAgICAgICAgIH1cIlxuICAgICAgICAgICBzdHlsZT1cInRhYmxlLWxheW91dDogZml4ZWQ7XCI+XG4gICAgICBcbiAgICAgIDwhLS0gVGFibGUgSGVhZGVyIC0tPlxuICAgICAgPHRoZWFkIGNsYXNzPVwidHctYmctZ3JheS01MFwiIFxuICAgICAgICAgICAgW25nQ2xhc3NdPVwiW1xuICAgICAgICAgICAgICBtZXJnZWRDb25maWcoKS5oZWFkZXJDbGFzcyB8fCAnJyxcbiAgICAgICAgICAgICAgc2Nyb2xsQ29uZmlnPy5lbmFibGVkICYmIHNjcm9sbENvbmZpZz8uc3RpY2t5SGVhZGVyID8gJ3R3LXN0aWNreSB0dy10b3AtMCB0dy16LTEwJyA6ICcnXG4gICAgICAgICAgICBdXCI+XG4gICAgICAgIDx0cj5cbiAgICAgICAgICBAZm9yIChjb2x1bW4gb2YgY29sdW1uczsgdHJhY2sgY29sdW1uLmtleSkge1xuICAgICAgICAgICAgPHRoXG4gICAgICAgICAgICAgIGNsYXNzPVwidHctcHgtMyB0dy1weS0yIHR3LXRleHQtbGVmdCB0dy10ZXh0LXhzIHR3LWZvbnQtbWVkaXVtIHR3LXRleHQtZ3JheS01MDAgdHctdXBwZXJjYXNlIHR3LXRyYWNraW5nLXdpZGVyIHR3LXRydW5jYXRlXCJcbiAgICAgICAgICAgICAgW25nQ2xhc3NdPVwiW1xuICAgICAgICAgICAgICAgIGdldENvbHVtbldpZHRoQ2xhc3MoY29sdW1uLndpZHRoKSxcbiAgICAgICAgICAgICAgICBnZXRDb2x1bW5NYXhXaWR0aENsYXNzKGNvbHVtbi53aWR0aCksXG4gICAgICAgICAgICAgICAgY29sdW1uLmFsaWduID09PSAnY2VudGVyJyA/ICd0dy10ZXh0LWNlbnRlcicgOiAnJyxcbiAgICAgICAgICAgICAgICBjb2x1bW4uYWxpZ24gPT09ICdyaWdodCcgPyAndHctdGV4dC1yaWdodCcgOiAnJ1xuICAgICAgICAgICAgICBdXCJcbiAgICAgICAgICAgICAgW3RpdGxlXT1cImNvbHVtbi5oZWFkZXJcIj5cbiAgICAgICAgICAgICAge3sgY29sdW1uLmhlYWRlciB9fVxuICAgICAgICAgICAgPC90aD5cbiAgICAgICAgICB9XG4gICAgICAgIDwvdHI+XG4gICAgICA8L3RoZWFkPlxuXG4gICAgICA8IS0tIFRhYmxlIEJvZHkgLS0+XG4gICAgICA8dGJvZHkgY2xhc3M9XCJ0dy1iZy13aGl0ZSB0dy1kaXZpZGUteSB0dy1kaXZpZGUtZ3JheS0yMDBcIj5cbiAgICAgICAgQGlmIChsb2FkaW5nKCkgfHwgaXNSZWZyZXNoaW5nKCkgfHwgcGFnZUNoYW5nZUxvYWRpbmcoKSkge1xuICAgICAgICAgIDwhLS0gU2tlbGV0b24gTG9hZGluZyBSb3dzIC0tPlxuICAgICAgICAgIEBmb3IgKHNrZWxldG9uSXRlbSBvZiBnZXRTa2VsZXRvbkFycmF5KCk7IHRyYWNrICRpbmRleCkge1xuICAgICAgICAgICAgPHRyIGNsYXNzPVwidHctYW5pbWF0ZS1wdWxzZSB0dy1ib3JkZXItYiB0dy1ib3JkZXItZ3JheS0yMDBcIj5cbiAgICAgICAgICAgICAgQGZvciAoY29sdW1uIG9mIGNvbHVtbnM7IHRyYWNrIGNvbHVtbi5rZXkpIHtcbiAgICAgICAgICAgICAgICA8dGQgY2xhc3M9XCJ0dy1weC0zIHR3LXB5LTIgdHctd2hpdGVzcGFjZS1ub3dyYXBcIlxuICAgICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJbXG4gICAgICAgICAgICAgICAgICAgICAgZ2V0Q29sdW1uV2lkdGhDbGFzcyhjb2x1bW4ud2lkdGgpLFxuICAgICAgICAgICAgICAgICAgICAgIGdldENvbHVtbk1heFdpZHRoQ2xhc3MoY29sdW1uLndpZHRoKVxuICAgICAgICAgICAgICAgICAgICBdXCI+XG4gICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidHctaC0yIHR3LWJnLWdyYXktMjAwIHR3LXJvdW5kZWQgdHctdy0zLzRcIj48L2Rpdj5cbiAgICAgICAgICAgICAgICA8L3RkPlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA8L3RyPlxuICAgICAgICAgIH1cbiAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgQGZvciAoaXRlbSBvZiBkaXNwbGF5ZWREYXRhOyB0cmFjayB0cmFja0J5Rm4oJGluZGV4LCBpdGVtKSkge1xuICAgICAgICAgICAgPHRyIGNsYXNzPVwidHctZ3JvdXAgaG92ZXI6dHctYmctZ3JheS01MCB0dy1ib3JkZXItYi0yIHR3LWJvcmRlci1ncmF5LTIwMFwiXG4gICAgICAgICAgICAgICAgW25nQ2xhc3NdPVwiW1xuICAgICAgICAgICAgICAgICAgbWVyZ2VkQ29uZmlnKCkucm93Q2xhc3MgfHwgJycsXG4gICAgICAgICAgICAgICAgICBpc1JlZnJlc2hpbmcoKSA/ICd0dy1vcGFjaXR5LTYwIHR3LXBvaW50ZXItZXZlbnRzLW5vbmUnIDogJycsXG4gICAgICAgICAgICAgICAgICBpc0RyYWdEcm9wRW5hYmxlZCgpID8gJ3R3LWN1cnNvci1tb3ZlIHR3LWJvcmRlci0yIHR3LWJvcmRlci10cmFuc3BhcmVudCcgOiAnJyxcbiAgICAgICAgICAgICAgICAgICFpc0RyYWdEcm9wRW5hYmxlZCgpID8gJ3R3LXRyYW5zaXRpb24tY29sb3JzIHR3LWR1cmF0aW9uLTE1MCcgOiAnJyxcbiAgICAgICAgICAgICAgICAgIGlzVHJlZUVuYWJsZWQoKSA/IGdldFRyZWVMZXZlbENsYXNzKGl0ZW0pIDogJydcbiAgICAgICAgICAgICAgICBdXCJcbiAgICAgICAgICAgICAgICBbc3R5bGUuYm9yZGVyLWNvbG9yXT1cImlzRHJhZ092ZXJSb3cgPT09ICRpbmRleCA/ICcjM2I4MmY2JyA6ICd0cmFuc3BhcmVudCdcIlxuICAgICAgICAgICAgICAgIFtzdHlsZS5iYWNrZ3JvdW5kLWNvbG9yXT1cImlzRHJhZ092ZXJSb3cgPT09ICRpbmRleCA/ICcjZWZmNmZmJyA6ICcnXCJcbiAgICAgICAgICAgICAgICAoY2xpY2spPVwib25Sb3dDbGljayhpdGVtKVwiXG4gICAgICAgICAgICAgICAgKGtleWRvd24uZW50ZXIpPVwib25Sb3dDbGljayhpdGVtKVwiXG4gICAgICAgICAgICAgICAgKGtleWRvd24uc3BhY2UpPVwib25Sb3dDbGljayhpdGVtKVwiXG4gICAgICAgICAgICAgICAgW2NsYXNzLnR3LWN1cnNvci1wb2ludGVyXT1cIm1lcmdlZENvbmZpZygpLm9uUm93Q2xpY2sgJiYgIWlzRHJhZ0Ryb3BFbmFibGVkKClcIlxuICAgICAgICAgICAgICAgIFt0YWJpbmRleF09XCJtZXJnZWRDb25maWcoKS5vblJvd0NsaWNrICYmICFpc0RyYWdEcm9wRW5hYmxlZCgpID8gMCA6IC0xXCJcbiAgICAgICAgICAgICAgICBbYXR0ci5yb2xlXT1cIm1lcmdlZENvbmZpZygpLm9uUm93Q2xpY2sgJiYgIWlzRHJhZ0Ryb3BFbmFibGVkKCkgPyAnYnV0dG9uJyA6IG51bGxcIlxuICAgICAgICAgICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwibWVyZ2VkQ29uZmlnKCkub25Sb3dDbGljayAmJiAhaXNEcmFnRHJvcEVuYWJsZWQoKSA/ICdTZWxlY3Qgcm93JyA6IG51bGxcIlxuICAgICAgICAgICAgICAgIFtkcmFnZ2FibGVdPVwiaXNEcmFnRHJvcEVuYWJsZWQoKVwiXG4gICAgICAgICAgICAgICAgKGRyYWdzdGFydCk9XCJvbkRyYWdTdGFydCgkZXZlbnQsIGl0ZW0sICRpbmRleClcIlxuICAgICAgICAgICAgICAgIChkcmFnb3Zlcik9XCJvbkRyYWdPdmVyKCRldmVudClcIlxuICAgICAgICAgICAgICAgIChkcmFnbGVhdmUpPVwib25EcmFnTGVhdmUoJGV2ZW50KVwiXG4gICAgICAgICAgICAgICAgKGRyb3ApPVwib25Ecm9wKCRldmVudCwgaXRlbSwgJGluZGV4KVwiXG4gICAgICAgICAgICAgICAgKGRyYWdlbmQpPVwib25EcmFnRW5kKCRldmVudClcIj5cbiAgICAgICAgICAgICAgXG4gICAgICAgICAgICAgIEBmb3IgKGNvbHVtbiBvZiBjb2x1bW5zOyB0cmFjayBjb2x1bW4ua2V5KSB7XG4gICAgICAgICAgICAgICAgPHRkIGNsYXNzPVwidHctcHItMyB0dy1weS0xIHR3LXJlbGF0aXZlXCJcbiAgICAgICAgICAgICAgICAgICAgW25nQ2xhc3NdPVwiW1xuICAgICAgICAgICAgICAgICAgICAgIGdldENvbHVtbldpZHRoQ2xhc3MoY29sdW1uLndpZHRoKSxcbiAgICAgICAgICAgICAgICAgICAgICBnZXRDb2x1bW5NYXhXaWR0aENsYXNzKGNvbHVtbi53aWR0aCksXG4gICAgICAgICAgICAgICAgICAgICAgbWVyZ2VkQ29uZmlnKCkuY2VsbENsYXNzIHx8ICcnLFxuICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbi5hbGlnbiA9PT0gJ2NlbnRlcicgPyAndHctdGV4dC1jZW50ZXInIDogJycsXG4gICAgICAgICAgICAgICAgICAgICAgY29sdW1uLmFsaWduID09PSAncmlnaHQnID8gJ3R3LXRleHQtcmlnaHQnIDogJycsXG4gICAgICAgICAgICAgICAgICAgICAgY29sdW1uLnRydW5jYXRlICE9PSBmYWxzZSA/ICd0dy13aGl0ZXNwYWNlLW5vd3JhcCcgOiAndHctd2hpdGVzcGFjZS1ub3JtYWwnXG4gICAgICAgICAgICAgICAgICAgIF1cIlxuICAgICAgICAgICAgICAgICAgICBbc3R5bGUucGFkZGluZ0xlZnRdPVwiaXNUcmVlRW5hYmxlZCgpICYmICRpbmRleCA9PT0gMCA/IGdldFRyZWVJbmRlbnRTdHlsZShpdGVtKSA6ICcxMnB4J1wiXG4gICAgICAgICAgICAgICAgICAgIFtzdHlsZS5tYXhXaWR0aF09XCJnZXRDb2x1bW5NYXhXaWR0aENsYXNzKGNvbHVtbi53aWR0aCkgPT09ICd0dy1tYXgtdy14cycgPyAnMjAwcHgnIDogZ2V0Q29sdW1uTWF4V2lkdGhDbGFzcyhjb2x1bW4ud2lkdGgpID09PSAndHctbWF4LXctc20nID8gJzMwMHB4JyA6IGdldENvbHVtbk1heFdpZHRoQ2xhc3MoY29sdW1uLndpZHRoKSA9PT0gJ3R3LW1heC13LW1kJyA/ICc0MDBweCcgOiAnbm9uZSdcIlxuICAgICAgICAgICAgICAgICAgICBbc3R5bGUubWluV2lkdGhdPVwiaXNUcmVlRW5hYmxlZCgpICYmICRpbmRleCA9PT0gMCA/ICcxNTBweCcgOiAnMTAwcHgnXCI+XG4gICAgICAgICAgICAgICAgICA8IS0tIFRyZWUgRXhwYW5kL0NvbGxhcHNlIEJ1dHRvbiAob25seSBmb3IgZmlyc3QgY29sdW1uIHdoZW4gdHJlZSBpcyBlbmFibGVkKSAtLT5cbiAgICAgICAgICAgICAgICAgIEBpZiAoaXNUcmVlRW5hYmxlZCgpICYmICRpbmRleCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidHctZmxleCB0dy1pdGVtcy1jZW50ZXIgdHctc3BhY2UteC0yXCI+XG4gICAgICAgICAgICAgICAgICAgICAgPCEtLSBUcmVlIEluZGVudGF0aW9uIC0tPlxuICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0dy1mbGV4IHR3LWl0ZW1zLWNlbnRlclwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgQGlmIChoYXNDaGlsZHJlbihpdGVtKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICA8YnV0dG9uIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhbnQ9XCJvdXRsaW5lXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPVwieHNcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIChjbGljayk9XCJvbkFjdGlvbkNsaWNrKGl0ZW0sIHsga2V5OiAndG9nZ2xlLWV4cGFuZCcsIGxhYmVsOiAnVG9nZ2xlJywgaWNvbjogJycsIHZhcmlhbnQ6ICdnaG9zdCcsIG9uQ2xpY2s6ICd0b2dnbGUtZXhwYW5kJyB9KTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGFzcz1cInR3LWZsZXggdHctaXRlbXMtY2VudGVyIHR3LWp1c3RpZnktY2VudGVyIHR3LXctNSB0dy1oLTUgdHctdGV4dC1ncmF5LTUwMCBob3Zlcjp0dy10ZXh0LWdyYXktNzAwIHR3LXJvdW5kZWRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtjbGFzcy50dy10cmFuc2l0aW9uLWNvbG9yc109XCIhaXNEcmFnRHJvcEVuYWJsZWQoKVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgW3RpdGxlXT1cImlzSXRlbUV4cGFuZGVkKGl0ZW0pID8gJ0NvbGxhcHNlJyA6ICdFeHBhbmQnXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNpZGUtZWxlLWljb24gXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGFzcz1cInR3LXctMyB0dy1oLTNcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2NsYXNzLnR3LXRyYW5zaXRpb24tdHJhbnNmb3JtXT1cIiFpc0RyYWdEcm9wRW5hYmxlZCgpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtjbGFzcy50dy1yb3RhdGUtOTBdPVwiaXNJdGVtRXhwYW5kZWQoaXRlbSlcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT1cInhzXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGV2cm9uX3JpZ2h0XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9jaWRlLWVsZS1pY29uPlxuICAgICAgICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidHctdy04IHR3LWgtNSB0dy1mbGV4IHR3LWl0ZW1zLWNlbnRlciB0dy1qdXN0aWZ5LWNlbnRlclwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwhLS0gPGRpdiBjbGFzcz1cInR3LXctMSB0dy1oLTEgdHctYmctZ3JheS0zMDAgdHctcm91bmRlZC1mdWxsXCI+PC9kaXY+IC0tPlxuICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICAgICAgICA8IS0tIENlbGwgQ29udGVudCAtLT5cbiAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidHctZmxleC0xIHR3LXctZnVsbFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgQGlmIChjb2x1bW4udHlwZSA9PT0gJ3RleHQnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDxwIGNsYXNzPVwidHctdGV4dC1zbSB0dy10ZXh0LWdyYXktOTAwXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW2NsYXNzLnR3LXRydW5jYXRlXT1cImNvbHVtbi50cnVuY2F0ZVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIFt0aXRsZV09XCJjb2x1bW4udHJ1bmNhdGUgPyAoZ2V0TmVzdGVkVmFsdWUoaXRlbSwgY29sdW1uLnZhbHVlR2V0dGVyIHx8IGNvbHVtbi5rZXkpIHx8ICcnKS50b1N0cmluZygpIDogJydcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7eyBmb3JtYXRWYWx1ZShnZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW4udmFsdWVHZXR0ZXIgfHwgY29sdW1uLmtleSksIGNvbHVtbikgfX1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPC9wPlxuICAgICAgICAgICAgICAgICAgICAgICAgfSBAZWxzZSBpZiAoY29sdW1uLnR5cGUgPT09ICdudW1iZXInKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidHctdGV4dC1zbSB0dy10ZXh0LWdyYXktOTAwIHR3LWZvbnQtbW9ub1wiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHt7IGZvcm1hdFZhbHVlKGdldE5lc3RlZFZhbHVlKGl0ZW0sIGNvbHVtbi52YWx1ZUdldHRlciB8fCBjb2x1bW4ua2V5KSwgY29sdW1uKSB9fVxuICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgICAgICB9IEBlbHNlIGlmIChjb2x1bW4udHlwZSA9PT0gJ2RhdGUnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidHctdGV4dC1zbSB0dy10ZXh0LWdyYXktNjAwXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAge3sgZm9ybWF0VmFsdWUoZ2V0TmVzdGVkVmFsdWUoaXRlbSwgY29sdW1uLnZhbHVlR2V0dGVyIHx8IGNvbHVtbi5rZXkpLCBjb2x1bW4pIH19XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgICAgIH0gQGVsc2UgaWYgKGNvbHVtbi50eXBlID09PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0dy1pbmxpbmUtZmxleCB0dy1pdGVtcy1jZW50ZXIgdHctcHgtMiB0dy1weS0wLjUgdHctcm91bmRlZC1mdWxsIHR3LXRleHQteHMgdHctZm9udC1tZWRpdW1cIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJnZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW4udmFsdWVHZXR0ZXIgfHwgY29sdW1uLmtleSkgPyAndHctYmctZ3JlZW4tMTAwIHR3LXRleHQtZ3JlZW4tODAwJyA6ICd0dy1iZy1yZWQtMTAwIHR3LXRleHQtcmVkLTgwMCdcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7eyBnZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW4udmFsdWVHZXR0ZXIgfHwgY29sdW1uLmtleSkgPyAnWWVzJyA6ICdObycgfX1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgICAgICAgICAgICAgICAgfSBAZWxzZSBpZiAoY29sdW1uLnR5cGUgPT09ICdzdGF0dXMnICYmIGNvbHVtbi5zdGF0dXNDb25maWcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0dy1pbmxpbmUtZmxleCB0dy1pdGVtcy1jZW50ZXIgdHctcHgtMiB0dy1weS0wLjUgdHctcm91bmRlZC1mdWxsIHR3LXRleHQteHMgdHctZm9udC1tZWRpdW1cIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJnZXRTdGF0dXNDbGFzcyhnZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW4udmFsdWVHZXR0ZXIgfHwgY29sdW1uLmtleSksIGNvbHVtbi5zdGF0dXNDb25maWcpXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAge3sgZ2V0U3RhdHVzVGV4dChnZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW4udmFsdWVHZXR0ZXIgfHwgY29sdW1uLmtleSksIGNvbHVtbi5zdGF0dXNDb25maWcpIH19XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgICAgIH0gQGVsc2UgaWYgKGNvbHVtbi50eXBlID09PSAnYWN0aW9ucycgJiYgY29sdW1uLmFjdGlvbnMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInR3LWZsZXggdHctaXRlbXMtY2VudGVyIHR3LXNwYWNlLXgtMlwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIEBmb3IgKGFjdGlvbiBvZiBjb2x1bW4uYWN0aW9uczsgdHJhY2sgYWN0aW9uLmtleSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaWRlRWxlQnV0dG9uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFt2YXJpYW50XT1cImFjdGlvbi52YXJpYW50IHx8ICdnaG9zdCdcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPVwieHNcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uQWN0aW9uQ2xpY2soaXRlbSwgYWN0aW9uKTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgW3RpdGxlXT1cImFjdGlvbi50b29sdGlwIHx8IGFjdGlvbi5sYWJlbFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCJpc1JlZnJlc2hpbmcoKVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPVwidHctaW5saW5lLWZsZXggdHctaXRlbXMtY2VudGVyIHR3LXB4LTIgdHctcHktMiB0dy10ZXh0LXhzIHR3LWZvbnQtbWVkaXVtIHR3LXJvdW5kZWRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbY2xhc3MudHctdHJhbnNpdGlvbi1jb2xvcnNdPVwiIWlzRHJhZ0Ryb3BFbmFibGVkKClcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJ7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3R3LXRleHQtZ3JheS03MDAgdHctYmctZ3JheS0xMDAgaG92ZXI6dHctYmctZ3JheS0yMDAnOiBhY3Rpb24udmFyaWFudCA9PT0gJ2dob3N0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAndHctdGV4dC13aGl0ZSB0dy1iZy1ibHVlLTYwMCBob3Zlcjp0dy1iZy1ibHVlLTcwMCc6IGFjdGlvbi52YXJpYW50ID09PSAncHJpbWFyeScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3R3LXRleHQtYmx1ZS03MDAgdHctYmctYmx1ZS01MCB0dy1ib3JkZXIgdHctYm9yZGVyLWJsdWUtMjAwIGhvdmVyOnR3LWJnLWJsdWUtMTAwJzogYWN0aW9uLnZhcmlhbnQgPT09ICdvdXRsaW5lJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAndHctdGV4dC13aGl0ZSB0dy1iZy1yZWQtNjAwIGhvdmVyOnR3LWJnLXJlZC03MDAnOiBhY3Rpb24udmFyaWFudCA9PT0gJ2RhbmdlcidcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBAaWYgKGFjdGlvbi5pY29uKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHN2ZyBjbGFzcz1cInR3LXctMyB0dy1oLTMgdHctbXItMVwiIGZpbGw9XCJjdXJyZW50Q29sb3JcIiB2aWV3Qm94PVwiMCAwIDIwIDIwXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPVwiTTEwIDEybC01LTVoMTBsLTUgNXpcIi8+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zdmc+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge3sgYWN0aW9uLmxhYmVsIH19XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgfSBAZWxzZSBpZiAoY29sdW1uLnR5cGUgPT09ICdjdXN0b20nKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDwhLS0gVGVtcGxhdGUgUmVuZGVyZXIgLS0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgIEBpZiAoY29sdW1uLnJlbmRlcmVyICYmIGlzVGVtcGxhdGVSZW5kZXJlcihjb2x1bW4ucmVuZGVyZXIpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lciBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICpuZ1RlbXBsYXRlT3V0bGV0PVwiZ2V0VGVtcGxhdGVSZW5kZXJlcihjb2x1bW4ucmVuZGVyZXIpITsgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250ZXh0OiBnZXRUZW1wbGF0ZUNvbnRleHQoZ2V0TmVzdGVkVmFsdWUoaXRlbSwgY29sdW1uLnZhbHVlR2V0dGVyIHx8IGNvbHVtbi5rZXkpLCBpdGVtLCBjb2x1bW4pXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPCEtLSBTdHJpbmcgUmVuZGVyZXIgLS0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgIEBlbHNlIGlmIChjb2x1bW4ucmVuZGVyZXIgJiYgaXNTdHJpbmdSZW5kZXJlcihjb2x1bW4ucmVuZGVyZXIpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBbaW5uZXJIVE1MXT1cInJlbmRlckN1c3RvbUNlbGwoZ2V0TmVzdGVkVmFsdWUoaXRlbSwgY29sdW1uLnZhbHVlR2V0dGVyIHx8IGNvbHVtbi5rZXkpLCBpdGVtLCBjb2x1bW4pXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPCEtLSBEZWZhdWx0IHJlbmRlcmluZyAtLT5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgQGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgW2lubmVySFRNTF09XCJyZW5kZXJDdXN0b21DZWxsKGdldE5lc3RlZFZhbHVlKGl0ZW0sIGNvbHVtbi52YWx1ZUdldHRlciB8fCBjb2x1bW4ua2V5KSwgaXRlbSwgY29sdW1uKVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIDwhLS0gUmVndWxhciBjZWxsIGNvbnRlbnQgKG5vbi10cmVlIG9yIG5vbi1maXJzdCBjb2x1bW4pIC0tPlxuICAgICAgICAgICAgICAgICAgICBAaWYgKGNvbHVtbi50eXBlID09PSAndGV4dCcpIHtcbiAgICAgICAgICAgICAgICAgICAgICA8cCBjbGFzcz1cInR3LXRleHQtc20gdHctdGV4dC1ncmF5LTkwMFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgW2NsYXNzLnR3LXRydW5jYXRlXT1cImNvbHVtbi50cnVuY2F0ZVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgW3RpdGxlXT1cImNvbHVtbi50cnVuY2F0ZSA/IChnZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW4udmFsdWVHZXR0ZXIgfHwgY29sdW1uLmtleSkgfHwgJycpLnRvU3RyaW5nKCkgOiAnJ1wiPlxuICAgICAgICAgICAgICAgICAgICAgICAge3sgZm9ybWF0VmFsdWUoZ2V0TmVzdGVkVmFsdWUoaXRlbSwgY29sdW1uLnZhbHVlR2V0dGVyIHx8IGNvbHVtbi5rZXkpLCBjb2x1bW4pIH19XG4gICAgICAgICAgICAgICAgICAgICAgPC9wPlxuICAgICAgICAgICAgICAgICAgICB9IEBlbHNlIGlmIChjb2x1bW4udHlwZSA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInR3LXRleHQtc20gdHctdGV4dC1ncmF5LTkwMCB0dy1mb250LW1vbm9cIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIHt7IGZvcm1hdFZhbHVlKGdldE5lc3RlZFZhbHVlKGl0ZW0sIGNvbHVtbi52YWx1ZUdldHRlciB8fCBjb2x1bW4ua2V5KSwgY29sdW1uKSB9fVxuICAgICAgICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgfSBAZWxzZSBpZiAoY29sdW1uLnR5cGUgPT09ICdkYXRlJykge1xuICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidHctdGV4dC1zbSB0dy10ZXh0LWdyYXktNjAwXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICB7eyBmb3JtYXRWYWx1ZShnZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW4udmFsdWVHZXR0ZXIgfHwgY29sdW1uLmtleSksIGNvbHVtbikgfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgIH0gQGVsc2UgaWYgKGNvbHVtbi50eXBlID09PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInR3LWlubGluZS1mbGV4IHR3LWl0ZW1zLWNlbnRlciB0dy1weC0yIHR3LXB5LTAuNSB0dy1yb3VuZGVkLWZ1bGwgdHctdGV4dC14cyB0dy1mb250LW1lZGl1bVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgW25nQ2xhc3NdPVwiZ2V0TmVzdGVkVmFsdWUoaXRlbSwgY29sdW1uLnZhbHVlR2V0dGVyIHx8IGNvbHVtbi5rZXkpID8gJ3R3LWJnLWdyZWVuLTEwMCB0dy10ZXh0LWdyZWVuLTgwMCcgOiAndHctYmctcmVkLTEwMCB0dy10ZXh0LXJlZC04MDAnXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICB7eyBnZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW4udmFsdWVHZXR0ZXIgfHwgY29sdW1uLmtleSkgPyAnWWVzJyA6ICdObycgfX1cbiAgICAgICAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgIH0gQGVsc2UgaWYgKGNvbHVtbi50eXBlID09PSAnc3RhdHVzJyAmJiBjb2x1bW4uc3RhdHVzQ29uZmlnKSB7XG4gICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0dy1pbmxpbmUtZmxleCB0dy1pdGVtcy1jZW50ZXIgdHctcHgtMiB0dy1weS0wLjUgdHctcm91bmRlZC1mdWxsIHR3LXRleHQteHMgdHctZm9udC1tZWRpdW1cIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtuZ0NsYXNzXT1cImdldFN0YXR1c0NsYXNzKGdldE5lc3RlZFZhbHVlKGl0ZW0sIGNvbHVtbi52YWx1ZUdldHRlciB8fCBjb2x1bW4ua2V5KSwgY29sdW1uLnN0YXR1c0NvbmZpZylcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIHt7IGdldFN0YXR1c1RleHQoZ2V0TmVzdGVkVmFsdWUoaXRlbSwgY29sdW1uLnZhbHVlR2V0dGVyIHx8IGNvbHVtbi5rZXkpLCBjb2x1bW4uc3RhdHVzQ29uZmlnKSB9fVxuICAgICAgICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgfSBAZWxzZSBpZiAoY29sdW1uLnR5cGUgPT09ICdhY3Rpb25zJyAmJiBjb2x1bW4uYWN0aW9ucykge1xuICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0dy1mbGV4IHR3LWl0ZW1zLWNlbnRlciB0dy1zcGFjZS14LTJcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIEBmb3IgKGFjdGlvbiBvZiBjb2x1bW4uYWN0aW9uczsgdHJhY2sgYWN0aW9uLmtleSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2lkZUVsZUJ1dHRvblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFt2YXJpYW50XT1cImFjdGlvbi52YXJpYW50IHx8ICdnaG9zdCdcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9XCJ4c1wiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uQWN0aW9uQ2xpY2soaXRlbSwgYWN0aW9uKTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBbdGl0bGVdPVwiYWN0aW9uLnRvb2x0aXAgfHwgYWN0aW9uLmxhYmVsXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiaXNSZWZyZXNoaW5nKClcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPVwidHctaW5saW5lLWZsZXggdHctaXRlbXMtY2VudGVyIHR3LXB4LTIgdHctcHktMiB0dy10ZXh0LXhzIHR3LWZvbnQtbWVkaXVtIHR3LXJvdW5kZWRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtjbGFzcy50dy10cmFuc2l0aW9uLWNvbG9yc109XCIhaXNEcmFnRHJvcEVuYWJsZWQoKVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgW25nQ2xhc3NdPVwie1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3R3LXRleHQtZ3JheS03MDAgdHctYmctZ3JheS0xMDAgaG92ZXI6dHctYmctZ3JheS0yMDAnOiBhY3Rpb24udmFyaWFudCA9PT0gJ2dob3N0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICd0dy10ZXh0LXdoaXRlIHR3LWJnLWJsdWUtNjAwIGhvdmVyOnR3LWJnLWJsdWUtNzAwJzogYWN0aW9uLnZhcmlhbnQgPT09ICdwcmltYXJ5JyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICd0dy10ZXh0LWJsdWUtNzAwIHR3LWJnLWJsdWUtNTAgdHctYm9yZGVyIHR3LWJvcmRlci1ibHVlLTIwMCBob3Zlcjp0dy1iZy1ibHVlLTEwMCc6IGFjdGlvbi52YXJpYW50ID09PSAnb3V0bGluZScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAndHctdGV4dC13aGl0ZSB0dy1iZy1yZWQtNjAwIGhvdmVyOnR3LWJnLXJlZC03MDAnOiBhY3Rpb24udmFyaWFudCA9PT0gJ2RhbmdlcidcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgQGlmIChhY3Rpb24uaWNvbikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHN2ZyBjbGFzcz1cInR3LXctMyB0dy1oLTMgdHctbXItMVwiIGZpbGw9XCJjdXJyZW50Q29sb3JcIiB2aWV3Qm94PVwiMCAwIDIwIDIwXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9XCJNMTAgMTJsLTUtNWgxMGwtNSA1elwiLz5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc3ZnPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7eyBhY3Rpb24ubGFiZWwgfX1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgIH0gQGVsc2UgaWYgKGNvbHVtbi50eXBlID09PSAnY3VzdG9tJykge1xuICAgICAgICAgICAgICAgICAgICAgIDwhLS0gVGVtcGxhdGUgUmVuZGVyZXIgLS0+XG4gICAgICAgICAgICAgICAgICAgICAgQGlmIChjb2x1bW4ucmVuZGVyZXIgJiYgaXNUZW1wbGF0ZVJlbmRlcmVyKGNvbHVtbi5yZW5kZXJlcikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXIgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICpuZ1RlbXBsYXRlT3V0bGV0PVwiZ2V0VGVtcGxhdGVSZW5kZXJlcihjb2x1bW4ucmVuZGVyZXIpITsgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRleHQ6IGdldFRlbXBsYXRlQ29udGV4dChnZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW4udmFsdWVHZXR0ZXIgfHwgY29sdW1uLmtleSksIGl0ZW0sIGNvbHVtbilcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICA8IS0tIFN0cmluZyBSZW5kZXJlciAtLT5cbiAgICAgICAgICAgICAgICAgICAgICBAZWxzZSBpZiAoY29sdW1uLnJlbmRlcmVyICYmIGlzU3RyaW5nUmVuZGVyZXIoY29sdW1uLnJlbmRlcmVyKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBbaW5uZXJIVE1MXT1cInJlbmRlckN1c3RvbUNlbGwoZ2V0TmVzdGVkVmFsdWUoaXRlbSwgY29sdW1uLnZhbHVlR2V0dGVyIHx8IGNvbHVtbi5rZXkpLCBpdGVtLCBjb2x1bW4pXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgPCEtLSBEZWZhdWx0IHJlbmRlcmluZyAtLT5cbiAgICAgICAgICAgICAgICAgICAgICBAZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IFtpbm5lckhUTUxdPVwicmVuZGVyQ3VzdG9tQ2VsbChnZXROZXN0ZWRWYWx1ZShpdGVtLCBjb2x1bW4udmFsdWVHZXR0ZXIgfHwgY29sdW1uLmtleSksIGl0ZW0sIGNvbHVtbilcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIDwvdGQ+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDwvdHI+XG4gICAgICAgICAgfVxuICAgICAgICAgIFxuICAgICAgICAgIDwhLS0gRW1wdHkgU3RhdGUgLS0+XG4gICAgICAgICAgQGlmIChkaXNwbGF5ZWREYXRhLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgPHRyIGNsYXNzPVwidHctaC1mdWxsXCI+XG4gICAgICAgICAgICAgIDx0ZCBbYXR0ci5jb2xzcGFuXT1cImNvbHVtbnMubGVuZ3RoXCIgY2xhc3M9XCJ0dy1weC02IHR3LXB5LTIyIHR3LXRleHQtY2VudGVyIHR3LWgtZnVsbCB0dy1hbGlnbi1taWRkbGVcIj5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidHctdGV4dC1ncmF5LTUwMCB0dy1mbGV4IHR3LWZsZXgtY29sIHR3LWl0ZW1zLWNlbnRlciB0dy1qdXN0aWZ5LWNlbnRlciB0dy1taW4taC1bMzAwcHhdXCI+XG4gICAgICAgICAgICAgICAgICA8c3ZnIGNsYXNzPVwidHctbXgtYXV0byB0dy1oLTEyIHR3LXctMTIgdHctdGV4dC1ncmF5LTQwMFwiIGZpbGw9XCJub25lXCIgc3Ryb2tlPVwiY3VycmVudENvbG9yXCIgdmlld0JveD1cIjAgMCAyNCAyNFwiPlxuICAgICAgICAgICAgICAgICAgICA8cGF0aCBzdHJva2UtbGluZWNhcD1cInJvdW5kXCIgc3Ryb2tlLWxpbmVqb2luPVwicm91bmRcIiBzdHJva2Utd2lkdGg9XCIyXCIgZD1cIk05IDEyaDZtLTYgNGg2bTIgNUg3YTIgMiAwIDAxLTItMlY1YTIgMiAwIDAxMi0yaDUuNTg2YTEgMSAwIDAxLjcwNy4yOTNsNS40MTQgNS40MTRhMSAxIDAgMDEuMjkzLjcwN1YxOWEyIDIgMCAwMS0yIDJ6XCI+PC9wYXRoPlxuICAgICAgICAgICAgICAgICAgPC9zdmc+XG4gICAgICAgICAgICAgICAgICA8aDMgY2xhc3M9XCJ0dy1tdC0yIHR3LXRleHQtc20gdHctZm9udC1tZWRpdW0gdHctdGV4dC1ncmF5LTkwMFwiPk5vIGRhdGEgZm91bmQ8L2gzPlxuICAgICAgICAgICAgICAgICAgPHAgY2xhc3M9XCJ0dy1tdC0xIHR3LXRleHQtc20gdHctdGV4dC1ncmF5LTUwMFwiPlxuICAgICAgICAgICAgICAgICAgICBAaWYgKHNlYXJjaFF1ZXJ5KCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICBObyByZXN1bHRzIG1hdGNoIHlvdXIgc2VhcmNoIGNyaXRlcmlhLlxuICAgICAgICAgICAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICBUaGVyZSBhcmUgbm8gaXRlbXMgdG8gZGlzcGxheS5cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgPC9wPlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICA8L3RkPlxuICAgICAgICAgICAgPC90cj5cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIDwvdGJvZHk+XG4gICAgPC90YWJsZT5cbiAgPC9kaXY+XG5cbiAgICAgPCEtLSBQYWdpbmF0aW9uIFNlY3Rpb24gLS0+XG4gICBAaWYgKHBhZ2luYXRpb25Db25maWcuZW5hYmxlZCAmJiB0b3RhbEl0ZW1zKCkgPiAwKSB7XG4gICAgIDxkaXYgY2xhc3M9XCJ0dy1weC0zIHR3LXB5LTIgdHctYm9yZGVyLXQgdHctYm9yZGVyLWdyYXktMjAwIHR3LWJnLXdoaXRlIHR3LXJlbGF0aXZlIHR3LXotMjBcIlxuICAgICAgICAgIFtjbGFzcy50dy1vcGFjaXR5LTYwXT1cImlzUmVmcmVzaGluZygpXCI+XG4gICAgICBcbiAgICAgIDwhLS0gUmVzdWx0cyBJbmZvIGFuZCBQYWdlIFNpemUgLS0+XG4gICAgICA8ZGl2IGNsYXNzPVwidHctZmxleCB0dy1mbGV4LWNvbCBzbTp0dy1mbGV4LXJvdyB0dy1qdXN0aWZ5LWJldHdlZW4gdHctaXRlbXMtc3RhcnQgc206dHctaXRlbXMtY2VudGVyIHR3LXNwYWNlLXktMiBzbTp0dy1zcGFjZS15LTBcIj5cbiAgICAgICAgXG4gICAgICAgIDwhLS0gUmVzdWx0cyBJbmZvIC0tPlxuICAgICAgICBAaWYgKHBhZ2luYXRpb25Db25maWcuc2hvd1BhZ2VJbmZvKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cInR3LWZsZXggdHctaXRlbXMtY2VudGVyIHR3LXNwYWNlLXgtNFwiPlxuICAgICAgICAgICAgPHAgY2xhc3M9XCJ0dy10ZXh0LXNtIHR3LXRleHQtZ3JheS03MDBcIj5cbiAgICAgICAgICAgICAgU2hvd2luZyB7eyBnZXRJdGVtUmFuZ2VUZXh0KCkgfX0gcmVzdWx0c1xuICAgICAgICAgICAgPC9wPlxuICAgICAgICAgICAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgPCEtLSBQYWdlIFNpemUgU2VsZWN0b3IgLS0+XG4gICAgICAgICAgICAgPGRpdiBjbGFzcz1cInR3LWZsZXggdHctaXRlbXMtY2VudGVyIHR3LXNwYWNlLXgtMlwiPlxuICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0dy10ZXh0LXNtIHR3LXRleHQtZ3JheS03MDBcIj5QZXIgcGFnZTo8L3NwYW4+XG4gICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwidHctdy0yMCB0dy1yZWxhdGl2ZVwiPlxuICAgICAgICAgICAgICAgICA8Y2lkZS1lbGUtc2VsZWN0XG4gICAgICAgICAgICAgICAgICAgW2xhYmVsSGlkZV09XCJ0cnVlXCJcbiAgICAgICAgICAgICAgICAgICBbbmdNb2RlbF09XCJwYWdlU2l6ZSgpXCJcbiAgICAgICAgICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJ1cGRhdGVQYWdlU2l6ZSgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICAgICBbb3B0aW9uc109XCJnZXRQYWdlU2l6ZU9wdGlvbnMoKVwiXG4gICAgICAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cImlzUmVmcmVzaGluZygpXCJcbiAgICAgICAgICAgICAgICAgICBmaWxsPVwib3V0bGluZVwiXG4gICAgICAgICAgICAgICAgICAgc2l6ZT1cInNtXCJcbiAgICAgICAgICAgICAgICAgICBjbGFzcz1cInR3LXotMzBcIj5cbiAgICAgICAgICAgICAgICAgPC9jaWRlLWVsZS1zZWxlY3Q+XG4gICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgfVxuXG4gICAgICAgIDwhLS0gUGFnaW5hdGlvbiBDb250cm9scyAtLT5cbiAgICAgICAgPGRpdiBjbGFzcz1cInR3LWZsZXggdHctaXRlbXMtY2VudGVyIHR3LXNwYWNlLXgtM1wiPlxuICAgICAgICAgIFxuICAgICAgICAgIDwhLS0gUHJldmlvdXMvTmV4dCBhbmQgUGFnZSBOdW1iZXJzIC0tPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJ0dy1mbGV4IHR3LWl0ZW1zLWNlbnRlciB0dy1zcGFjZS14LTFcIj5cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgPCEtLSBGaXJzdCBQYWdlIC0tPlxuICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICBjaWRlRWxlQnV0dG9uXG4gICAgICAgICAgICAgIHZhcmlhbnQ9XCJnaG9zdFwiXG4gICAgICAgICAgICAgIHNpemU9XCJzbVwiXG4gICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAoY2xpY2spPVwib25QYWdlQ2hhbmdlKDEpXCJcbiAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cImN1cnJlbnRQYWdlKCkgPT09IDEgfHwgaXNSZWZyZXNoaW5nKClcIlxuICAgICAgICAgICAgICBjbGFzcz1cInR3LXB4LTMgdHctcHktMiB0dy10ZXh0LWxnIHR3LXRleHQtZ3JheS01MDAgaG92ZXI6dHctdGV4dC1ncmF5LTcwMCBob3Zlcjp0dy1iZy1ncmF5LTEwMCB0dy1yb3VuZGVkLW1kIHR3LXRyYW5zaXRpb24tY29sb3JzIGRpc2FibGVkOnR3LW9wYWNpdHktNTAgZGlzYWJsZWQ6dHctY3Vyc29yLW5vdC1hbGxvd2VkXCI+XG4gICAgICAgICAgICAgIMKrwqtcbiAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgXG4gICAgICAgICAgICA8IS0tIFByZXZpb3VzIFBhZ2UgLS0+XG4gICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgIGNpZGVFbGVCdXR0b25cbiAgICAgICAgICAgICAgdmFyaWFudD1cImdob3N0XCJcbiAgICAgICAgICAgICAgc2l6ZT1cInNtXCJcbiAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJwcmV2aW91c1BhZ2UoKVwiXG4gICAgICAgICAgICAgIFtkaXNhYmxlZF09XCIhaGFzUHJldmlvdXNQYWdlKCkgfHwgaXNSZWZyZXNoaW5nKClcIlxuICAgICAgICAgICAgICBjbGFzcz1cInR3LXB4LTMgdHctcHktMiB0dy10ZXh0LWxnIHR3LXRleHQtZ3JheS01MDAgaG92ZXI6dHctdGV4dC1ncmF5LTcwMCBob3Zlcjp0dy1iZy1ncmF5LTEwMCB0dy1yb3VuZGVkLW1kIHR3LXRyYW5zaXRpb24tY29sb3JzIGRpc2FibGVkOnR3LW9wYWNpdHktNTAgZGlzYWJsZWQ6dHctY3Vyc29yLW5vdC1hbGxvd2VkXCI+XG4gICAgICAgICAgICAgIOKAuVxuICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICBcbiAgICAgICAgICAgIDwhLS0gUGFnZSBOdW1iZXJzIC0tPlxuICAgICAgICAgICAgQGZvciAocGFnZSBvZiBnZXRFbmhhbmNlZFBhZ2VOdW1iZXJzKCk7IHRyYWNrIHBhZ2UpIHtcbiAgICAgICAgICAgICAgQGlmIChwYWdlID09PSAnLi4uJykge1xuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidHctcHgtMiB0dy1weS0yIHR3LXRleHQtc20gdHctdGV4dC1ncmF5LTUwMFwiPi4uLjwvc3Bhbj5cbiAgICAgICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICAgICAgY2lkZUVsZUJ1dHRvblxuICAgICAgICAgICAgICAgICAgW3ZhcmlhbnRdPVwiY3VycmVudFBhZ2UoKSA9PT0gcGFnZSA/ICdwcmltYXJ5JyA6ICdvdXRsaW5lJ1wiXG4gICAgICAgICAgICAgICAgICBzaXplPVwic21cIlxuICAgICAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgICAgICAoY2xpY2spPVwib25QYWdlQ2hhbmdlKHBhZ2UpXCJcbiAgICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCJpc1JlZnJlc2hpbmcoKVwiXG4gICAgICAgICAgICAgICAgICBjbGFzcz1cInR3LXB4LTMgdHctcHktMiB0dy10ZXh0LXNtIHR3LWJvcmRlciB0dy1yb3VuZGVkLW1kIHR3LXRyYW5zaXRpb24tY29sb3JzXCJcbiAgICAgICAgICAgICAgICAgIFtuZ0NsYXNzXT1cIntcbiAgICAgICAgICAgICAgICAgICAgJ3R3LWJnLWJsdWUtNjAwIHR3LXRleHQtd2hpdGUgdHctYm9yZGVyLWJsdWUtNjAwJzogY3VycmVudFBhZ2UoKSA9PT0gcGFnZSxcbiAgICAgICAgICAgICAgICAgICAgJ3R3LWJnLXdoaXRlIHR3LXRleHQtZ3JheS03MDAgdHctYm9yZGVyLWdyYXktMzAwIGhvdmVyOnR3LWJnLWdyYXktNTAnOiBjdXJyZW50UGFnZSgpICE9PSBwYWdlXG4gICAgICAgICAgICAgICAgICB9XCI+XG4gICAgICAgICAgICAgICAgICB7eyBwYWdlIH19XG4gICAgICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIFxuICAgICAgICAgICAgPCEtLSBOZXh0IFBhZ2UgLS0+XG4gICAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICAgIGNpZGVFbGVCdXR0b25cbiAgICAgICAgICAgICAgdmFyaWFudD1cImdob3N0XCJcbiAgICAgICAgICAgICAgc2l6ZT1cInNtXCJcbiAgICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAgIChjbGljayk9XCJuZXh0UGFnZSgpXCJcbiAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cIiFoYXNOZXh0UGFnZSgpIHx8IGlzUmVmcmVzaGluZygpXCJcbiAgICAgICAgICAgICAgY2xhc3M9XCJ0dy1weC0zIHR3LXB5LTIgdHctdGV4dC1sZyB0dy10ZXh0LWdyYXktNTAwIGhvdmVyOnR3LXRleHQtZ3JheS03MDAgaG92ZXI6dHctYmctZ3JheS0xMDAgdHctcm91bmRlZC1tZCB0dy10cmFuc2l0aW9uLWNvbG9ycyBkaXNhYmxlZDp0dy1vcGFjaXR5LTUwIGRpc2FibGVkOnR3LWN1cnNvci1ub3QtYWxsb3dlZFwiPlxuICAgICAgICAgICAgICDigLpcbiAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgICAgXG4gICAgICAgICAgICA8IS0tIExhc3QgUGFnZSAtLT5cbiAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgY2lkZUVsZUJ1dHRvblxuICAgICAgICAgICAgICB2YXJpYW50PVwiZ2hvc3RcIlxuICAgICAgICAgICAgICBzaXplPVwic21cIlxuICAgICAgICAgICAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uUGFnZUNoYW5nZSh0b3RhbFBhZ2VzKCkpXCJcbiAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cImN1cnJlbnRQYWdlKCkgPT09IHRvdGFsUGFnZXMoKSB8fCBpc1JlZnJlc2hpbmcoKVwiXG4gICAgICAgICAgICAgIGNsYXNzPVwidHctcHgtMyB0dy1weS0yIHR3LXRleHQtbGcgdHctdGV4dC1ncmF5LTUwMCBob3Zlcjp0dy10ZXh0LWdyYXktNzAwIGhvdmVyOnR3LWJnLWdyYXktMTAwIHR3LXJvdW5kZWQtbWQgdHctdHJhbnNpdGlvbi1jb2xvcnMgZGlzYWJsZWQ6dHctb3BhY2l0eS01MCBkaXNhYmxlZDp0dy1jdXJzb3Itbm90LWFsbG93ZWRcIj5cbiAgICAgICAgICAgICAgwrvCu1xuICAgICAgICAgICAgPC9idXR0b24+XG4gICAgICAgICAgICBcbiAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgIDwhLS0gUXVpY2sgSnVtcCBhbmQgUmVmcmVzaCAtLT5cbiAgICAgICAgICBAaWYgKHBhZ2luYXRpb25Db25maWcuc2hvd1F1aWNrSnVtcCB8fCBwYWdpbmF0aW9uQ29uZmlnLnNob3dSZWZyZXNoKSB7XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwidHctZmxleCB0dy1pdGVtcy1jZW50ZXIgdHctc3BhY2UteC0yIHR3LWJvcmRlci1sIHR3LWJvcmRlci1ncmF5LTIwMCB0dy1wbC0zXCI+XG4gICAgICAgICAgICAgIFxuICAgICAgICAgICAgICA8IS0tIFF1aWNrIEp1bXAgLS0+XG4gICAgICAgICAgICAgIEBpZiAocGFnaW5hdGlvbkNvbmZpZy5zaG93UXVpY2tKdW1wKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInR3LWZsZXggdHctaXRlbXMtY2VudGVyIHR3LXNwYWNlLXgtMVwiPlxuICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cInR3LXRleHQtc20gdHctdGV4dC1ncmF5LTcwMFwiPkdvIHRvOjwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cInR3LXctMjBcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y2lkZS1lbGUtaW5wdXQgaWQ9XCJqdW1wLXRvLXBhZ2UtaW5wdXRcIiB0eXBlPVwibnVtYmVyXCIgW2xhYmVsSGlkZV09XCJ0cnVlXCIgW2hpZGVIZWxwZXJBbmRFcnJvclRleHRdPVwidHJ1ZVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgIFsobmdNb2RlbCldPVwianVtcFRvUGFnZVwiIFttaW5dPVwiMVwiIFttYXhdPVwidG90YWxQYWdlcygpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cImlzUmVmcmVzaGluZygpXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgKGtleWRvd24uZW50ZXIpPVwib25KdW1wVG9QYWdlKClcIj5cbiAgICAgICAgICAgICAgICAgICAgICA8L2NpZGUtZWxlLWlucHV0PlxuICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgICAgICAgY2lkZUVsZUJ1dHRvblxuICAgICAgICAgICAgICAgICAgICB2YXJpYW50PVwib3V0bGluZVwiXG4gICAgICAgICAgICAgICAgICAgIHNpemU9XCJzbVwiXG4gICAgICAgICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwib25KdW1wVG9QYWdlKClcIlxuICAgICAgICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiaXNSZWZyZXNoaW5nKClcIlxuICAgICAgICAgICAgICAgICAgICBjbGFzcz1cInR3LXB4LTMgdHctcHktMiB0dy10ZXh0LXNtIHR3LWJnLWdyYXktMTAwIHR3LXRleHQtZ3JheS03MDAgdHctYm9yZGVyIHR3LWJvcmRlci1ncmF5LTMwMCB0dy1yb3VuZGVkLW1kIGhvdmVyOnR3LWJnLWdyYXktMjAwIGRpc2FibGVkOnR3LW9wYWNpdHktNTAgZGlzYWJsZWQ6dHctY3Vyc29yLW5vdC1hbGxvd2VkXCI+XG4gICAgICAgICAgICAgICAgICAgIEdvXG4gICAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgPCEtLSBSZWZyZXNoIEJ1dHRvbiAtLT5cbiAgICAgICAgICAgICAgQGlmIChwYWdpbmF0aW9uQ29uZmlnLnNob3dSZWZyZXNoKSB7XG4gICAgICAgICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgICAgICAgY2lkZUVsZUJ1dHRvblxuICAgICAgICAgICAgICAgICAgdmFyaWFudD1cIm91dGxpbmVcIlxuICAgICAgICAgICAgICAgICAgc2l6ZT1cInNtXCJcbiAgICAgICAgICAgICAgICAgIHR5cGU9XCJidXR0b25cIlxuICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uUmVmcmVzaCgpXCJcbiAgICAgICAgICAgICAgICAgIFtkaXNhYmxlZF09XCJpc1JlZnJlc2hpbmcoKVwiXG4gICAgICAgICAgICAgICAgICBjbGFzcz1cIiF0dy13LTEwICF0dy1oLTggdHctZmxleCB0dy1pdGVtcy1jZW50ZXIgdHctanVzdGlmeS1jZW50ZXIgdHctdGV4dC1ncmF5LTYwMCB0dy1iZy1ncmF5LTEwMCB0dy1ib3JkZXIgdHctYm9yZGVyLWdyYXktMzAwIHR3LXJvdW5kZWQtbWQgaG92ZXI6dHctYmctZ3JheS0yMDAgZGlzYWJsZWQ6dHctb3BhY2l0eS01MCBkaXNhYmxlZDp0dy1jdXJzb3Itbm90LWFsbG93ZWRcIj5cbiAgICAgICAgICAgICAgICAgIEBpZiAoaXNSZWZyZXNoaW5nKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgPHN2ZyBjbGFzcz1cInR3LWFuaW1hdGUtc3BpbiB0dy1oLTQgdHctdy00XCIgZmlsbD1cIm5vbmVcIiB2aWV3Qm94PVwiMCAwIDI0IDI0XCI+XG4gICAgICAgICAgICAgICAgICAgICAgPGNpcmNsZSBjbGFzcz1cInR3LW9wYWNpdHktMjVcIiBjeD1cIjEyXCIgY3k9XCIxMlwiIHI9XCIxMFwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHN0cm9rZS13aWR0aD1cIjRcIj48L2NpcmNsZT5cbiAgICAgICAgICAgICAgICAgICAgICA8cGF0aCBjbGFzcz1cInR3LW9wYWNpdHktNzVcIiBmaWxsPVwiY3VycmVudENvbG9yXCIgZD1cIk00IDEyYTggOCAwIDAxOC04VjBDNS4zNzMgMCAwIDUuMzczIDAgMTJoNHptMiA1LjI5MUE3Ljk2MiA3Ljk2MiAwIDAxNCAxMkgwYzAgMy4wNDIgMS4xMzUgNS44MjQgMyA3LjkzOGwzLTIuNjQ3elwiPjwvcGF0aD5cbiAgICAgICAgICAgICAgICAgICAgPC9zdmc+XG4gICAgICAgICAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgPHN2ZyBjbGFzcz1cInR3LWgtNCB0dy13LTRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIj5cbiAgICAgICAgICAgICAgICAgICAgICA8cGF0aCBzdHJva2UtbGluZWNhcD1cInJvdW5kXCIgc3Ryb2tlLWxpbmVqb2luPVwicm91bmRcIiBzdHJva2Utd2lkdGg9XCIyXCIgZD1cIk00IDR2NWguNTgybTE1LjM1NiAyQTguMDAxIDguMDAxIDAgMDA0LjU4MiA5bTAgMEg5bTExIDExdi01aC0uNTgxbTAgMGE4LjAwMyA4LjAwMyAwIDAxLTE1LjM1Ny0ybTE1LjM1NyAySDE1XCI+PC9wYXRoPlxuICAgICAgICAgICAgICAgICAgICA8L3N2Zz5cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgfVxuICAgICAgICAgIFxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICB9XG48L2Rpdj5cbiJdfQ==