@decaf-ts/for-angular 0.0.10 → 0.0.11

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 (121) hide show
  1. package/dist/lib/esm2022/components/component-renderer/component-renderer.component.mjs +313 -0
  2. package/dist/lib/esm2022/components/crud-field/crud-field.component.mjs +301 -0
  3. package/dist/lib/esm2022/components/crud-form/constants.mjs +14 -0
  4. package/dist/lib/esm2022/components/crud-form/crud-form.component.mjs +139 -0
  5. package/dist/lib/esm2022/components/crud-form/types.mjs +2 -0
  6. package/dist/lib/esm2022/components/empty-state/empty-state.component.mjs +348 -0
  7. package/dist/lib/esm2022/components/fieldset/fieldset.component.mjs +225 -0
  8. package/dist/lib/esm2022/components/filter/filter.component.mjs +689 -0
  9. package/dist/lib/esm2022/components/for-angular-components.module.mjs +71 -0
  10. package/dist/lib/esm2022/components/index.mjs +20 -0
  11. package/dist/lib/esm2022/components/layout/layout.component.mjs +176 -0
  12. package/dist/lib/esm2022/components/list/constants.mjs +6 -0
  13. package/dist/lib/esm2022/components/list/list.component.mjs +1236 -0
  14. package/dist/{esm2022 → lib/esm2022}/components/list-item/list-item.component.mjs +1 -1
  15. package/dist/lib/esm2022/components/model-renderer/model-renderer.component.mjs +138 -0
  16. package/dist/lib/esm2022/components/pagination/constants.mjs +2 -0
  17. package/dist/lib/esm2022/components/pagination/pagination.component.mjs +323 -0
  18. package/dist/lib/esm2022/components/searchbar/searchbar.component.mjs +493 -0
  19. package/dist/lib/esm2022/decaf-ts-for-angular.mjs +5 -0
  20. package/dist/lib/esm2022/directives/collapsable.directive.mjs +28 -0
  21. package/dist/lib/esm2022/directives/index.mjs +2 -0
  22. package/dist/lib/esm2022/engine/DynamicModule.mjs +18 -0
  23. package/dist/lib/esm2022/engine/NgxBaseComponent.mjs +539 -0
  24. package/dist/lib/esm2022/engine/NgxCrudFormField.mjs +125 -0
  25. package/dist/lib/esm2022/engine/NgxFormService.mjs +315 -0
  26. package/dist/lib/esm2022/engine/NgxRenderingEngine.mjs +192 -0
  27. package/dist/lib/esm2022/engine/NgxRenderingEngine2.mjs +332 -0
  28. package/dist/lib/esm2022/engine/ValidatorFactory.mjs +102 -0
  29. package/dist/lib/esm2022/engine/constants.mjs +160 -0
  30. package/dist/lib/esm2022/engine/decorators.mjs +38 -0
  31. package/dist/lib/esm2022/engine/index.mjs +17 -0
  32. package/dist/lib/esm2022/engine/types.mjs +4 -0
  33. package/dist/lib/esm2022/for-angular.module.mjs +118 -0
  34. package/dist/lib/esm2022/helpers/index.mjs +13 -0
  35. package/dist/lib/esm2022/helpers/utils.mjs +415 -0
  36. package/dist/lib/esm2022/interfaces.mjs +2 -0
  37. package/dist/lib/esm2022/public-apis.mjs +14 -0
  38. package/dist/lib/fesm2022/decaf-ts-for-angular.mjs.map +1 -0
  39. package/package.json +1 -1
  40. package/dist/esm2022/components/component-renderer/component-renderer.component.mjs +0 -313
  41. package/dist/esm2022/components/crud-field/crud-field.component.mjs +0 -301
  42. package/dist/esm2022/components/crud-form/constants.mjs +0 -14
  43. package/dist/esm2022/components/crud-form/crud-form.component.mjs +0 -139
  44. package/dist/esm2022/components/crud-form/types.mjs +0 -2
  45. package/dist/esm2022/components/empty-state/empty-state.component.mjs +0 -348
  46. package/dist/esm2022/components/fieldset/fieldset.component.mjs +0 -225
  47. package/dist/esm2022/components/filter/filter.component.mjs +0 -689
  48. package/dist/esm2022/components/for-angular-components.module.mjs +0 -71
  49. package/dist/esm2022/components/index.mjs +0 -20
  50. package/dist/esm2022/components/layout/layout.component.mjs +0 -176
  51. package/dist/esm2022/components/list/constants.mjs +0 -6
  52. package/dist/esm2022/components/list/list.component.mjs +0 -1236
  53. package/dist/esm2022/components/model-renderer/model-renderer.component.mjs +0 -138
  54. package/dist/esm2022/components/pagination/constants.mjs +0 -2
  55. package/dist/esm2022/components/pagination/pagination.component.mjs +0 -323
  56. package/dist/esm2022/components/searchbar/searchbar.component.mjs +0 -493
  57. package/dist/esm2022/decaf-ts-for-angular.mjs +0 -5
  58. package/dist/esm2022/directives/collapsable.directive.mjs +0 -28
  59. package/dist/esm2022/directives/index.mjs +0 -2
  60. package/dist/esm2022/engine/DynamicModule.mjs +0 -18
  61. package/dist/esm2022/engine/NgxBaseComponent.mjs +0 -539
  62. package/dist/esm2022/engine/NgxCrudFormField.mjs +0 -125
  63. package/dist/esm2022/engine/NgxFormService.mjs +0 -315
  64. package/dist/esm2022/engine/NgxRenderingEngine.mjs +0 -192
  65. package/dist/esm2022/engine/NgxRenderingEngine2.mjs +0 -332
  66. package/dist/esm2022/engine/ValidatorFactory.mjs +0 -102
  67. package/dist/esm2022/engine/constants.mjs +0 -160
  68. package/dist/esm2022/engine/decorators.mjs +0 -38
  69. package/dist/esm2022/engine/index.mjs +0 -17
  70. package/dist/esm2022/engine/types.mjs +0 -4
  71. package/dist/esm2022/for-angular.module.mjs +0 -118
  72. package/dist/esm2022/helpers/index.mjs +0 -13
  73. package/dist/esm2022/helpers/utils.mjs +0 -415
  74. package/dist/esm2022/interfaces.mjs +0 -2
  75. package/dist/esm2022/public-apis.mjs +0 -14
  76. package/dist/fesm2022/decaf-ts-for-angular.mjs.map +0 -1
  77. /package/dist/{README.md → lib/README.md} +0 -0
  78. /package/dist/{assets → lib/assets}/i18n/en.json +0 -0
  79. /package/dist/{assets → lib/assets}/images/angular-logo.svg +0 -0
  80. /package/dist/{assets → lib/assets}/images/decaf-logo-black.svg +0 -0
  81. /package/dist/{assets → lib/assets}/images/decaf-logo-lw.svg +0 -0
  82. /package/dist/{assets → lib/assets}/images/decaf-logo-white.svg +0 -0
  83. /package/dist/{assets → lib/assets}/images/decaf-logo.svg +0 -0
  84. /package/dist/{components → lib/components}/component-renderer/component-renderer.component.d.ts +0 -0
  85. /package/dist/{components → lib/components}/crud-field/crud-field.component.d.ts +0 -0
  86. /package/dist/{components → lib/components}/crud-form/constants.d.ts +0 -0
  87. /package/dist/{components → lib/components}/crud-form/crud-form.component.d.ts +0 -0
  88. /package/dist/{components → lib/components}/crud-form/types.d.ts +0 -0
  89. /package/dist/{components → lib/components}/empty-state/empty-state.component.d.ts +0 -0
  90. /package/dist/{components → lib/components}/fieldset/fieldset.component.d.ts +0 -0
  91. /package/dist/{components → lib/components}/filter/filter.component.d.ts +0 -0
  92. /package/dist/{components → lib/components}/for-angular-components.module.d.ts +0 -0
  93. /package/dist/{components → lib/components}/index.d.ts +0 -0
  94. /package/dist/{components → lib/components}/layout/layout.component.d.ts +0 -0
  95. /package/dist/{components → lib/components}/list/constants.d.ts +0 -0
  96. /package/dist/{components → lib/components}/list/list.component.d.ts +0 -0
  97. /package/dist/{components → lib/components}/list-item/list-item.component.d.ts +0 -0
  98. /package/dist/{components → lib/components}/model-renderer/model-renderer.component.d.ts +0 -0
  99. /package/dist/{components → lib/components}/pagination/constants.d.ts +0 -0
  100. /package/dist/{components → lib/components}/pagination/pagination.component.d.ts +0 -0
  101. /package/dist/{components → lib/components}/searchbar/searchbar.component.d.ts +0 -0
  102. /package/dist/{directives → lib/directives}/collapsable.directive.d.ts +0 -0
  103. /package/dist/{directives → lib/directives}/index.d.ts +0 -0
  104. /package/dist/{engine → lib/engine}/DynamicModule.d.ts +0 -0
  105. /package/dist/{engine → lib/engine}/NgxBaseComponent.d.ts +0 -0
  106. /package/dist/{engine → lib/engine}/NgxCrudFormField.d.ts +0 -0
  107. /package/dist/{engine → lib/engine}/NgxFormService.d.ts +0 -0
  108. /package/dist/{engine → lib/engine}/NgxRenderingEngine.d.ts +0 -0
  109. /package/dist/{engine → lib/engine}/NgxRenderingEngine2.d.ts +0 -0
  110. /package/dist/{engine → lib/engine}/ValidatorFactory.d.ts +0 -0
  111. /package/dist/{engine → lib/engine}/constants.d.ts +0 -0
  112. /package/dist/{engine → lib/engine}/decorators.d.ts +0 -0
  113. /package/dist/{engine → lib/engine}/index.d.ts +0 -0
  114. /package/dist/{engine → lib/engine}/types.d.ts +0 -0
  115. /package/dist/{fesm2022 → lib/fesm2022}/decaf-ts-for-angular.mjs +0 -0
  116. /package/dist/{for-angular.module.d.ts → lib/for-angular.module.d.ts} +0 -0
  117. /package/dist/{helpers → lib/helpers}/index.d.ts +0 -0
  118. /package/dist/{helpers → lib/helpers}/utils.d.ts +0 -0
  119. /package/dist/{index.d.ts → lib/index.d.ts} +0 -0
  120. /package/dist/{interfaces.d.ts → lib/interfaces.d.ts} +0 -0
  121. /package/dist/{public-apis.d.ts → lib/public-apis.d.ts} +0 -0
@@ -0,0 +1,689 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
3
+ import { ForAngularModule } from '../../for-angular.module';
4
+ import { NgxBaseComponent } from '../../engine/NgxBaseComponent';
5
+ import { IonChip, IonIcon, IonItem, IonLabel, IonSelect } from '@ionic/angular/standalone';
6
+ import { Dynamic } from '../../engine';
7
+ import { getWindowWidth } from '../../helpers/utils';
8
+ import { debounceTime, fromEvent } from 'rxjs';
9
+ import { OrderDirection, Repository } from '@decaf-ts/core';
10
+ import { SearchbarComponent } from '../searchbar/searchbar.component';
11
+ import { addIcons } from 'ionicons';
12
+ import { chevronDownOutline, chevronUpOutline } from 'ionicons/icons';
13
+ import * as i0 from "@angular/core";
14
+ import * as i1 from "@ionic/angular/standalone";
15
+ import * as i2 from "@angular/common";
16
+ import * as i3 from "@angular/forms";
17
+ import * as i4 from "@ngx-translate/core";
18
+ /**
19
+ * @description Advanced filter component for creating dynamic search filters with step-by-step construction.
20
+ * @summary This component provides a comprehensive filtering interface that allows users to build
21
+ * complex search criteria using a three-step approach: select index → select condition → enter value.
22
+ * It supports filtering by multiple field indexes, comparison conditions, and values, displaying
23
+ * selected filters as removable chips. The component is responsive and includes auto-suggestions
24
+ * with keyboard navigation support.
25
+ *
26
+ * @example
27
+ * ```html
28
+ * <ngx-decaf-filter
29
+ * [indexes]="['name', 'email', 'department', 'status']"
30
+ * [conditions]="['Equal', 'Contains', 'Greater Than', 'Less Than']"
31
+ * [sort]="['createdAt', 'updatedAt']"
32
+ * [disableSort]="false"
33
+ * (filterEvent)="onFiltersChanged($event)">
34
+ * </ngx-decaf-filter>
35
+ * ```
36
+ *
37
+ * @mermaid
38
+ * sequenceDiagram
39
+ * participant U as User
40
+ * participant F as FilterComponent
41
+ * participant P as Parent Component
42
+ *
43
+ * U->>F: Focus input field
44
+ * F->>F: handleFocus() - Show available indexes
45
+ * U->>F: Select index (e.g., "name")
46
+ * F->>F: addFilter() - Step 1 completed
47
+ * F->>F: Show available conditions
48
+ * U->>F: Select condition (e.g., "Contains")
49
+ * F->>F: addFilter() - Step 2 completed
50
+ * F->>F: Show value input prompt
51
+ * U->>F: Enter value and press Enter
52
+ * F->>F: addFilter() - Step 3 completed
53
+ * F->>F: Create complete filter object
54
+ * F->>P: Emit filterEvent with new filter array
55
+ * F->>F: Reset to step 1 for next filter
56
+ *
57
+ * @memberOf ForAngularModule
58
+ */
59
+ let FilterComponent = class FilterComponent extends NgxBaseComponent {
60
+ /**
61
+ * @description Constructor for FilterComponent.
62
+ * @summary Initializes a new instance of the FilterComponent.
63
+ * Calls the parent constructor with the component name to establish base locale string generation
64
+ * and internationalization support.
65
+ *
66
+ * @memberOf FilterComponent
67
+ */
68
+ constructor() {
69
+ super("FilterComponent");
70
+ /**
71
+ * @description Available field indexes for filtering operations.
72
+ * @summary Defines the list of field names that users can filter by. These represent
73
+ * the data properties available for filtering operations. Each index corresponds to
74
+ * a field in the data model that supports comparison operations.
75
+ *
76
+ * @type {string[]}
77
+ * @default []
78
+ * @memberOf FilterComponent
79
+ */
80
+ this.indexes = [];
81
+ /**
82
+ * @description Available comparison conditions for filters.
83
+ * @summary Defines the list of comparison operators that can be used when creating filters.
84
+ * These conditions determine how the filter value is compared against the field value.
85
+ * Common conditions include equality, containment, and numerical comparison operations.
86
+ *
87
+ * @type {string[]}
88
+ * @default []
89
+ * @memberOf FilterComponent
90
+ */
91
+ this.conditions = ['Equal', 'Contains', 'Not Contains', 'Greater Than', 'Less Than', 'Not Equal'];
92
+ /**
93
+ * @description Available sorting options for the filtered data.
94
+ * @summary Defines the list of field names that can be used for sorting the filtered results.
95
+ * When disableSort is false, this array is automatically merged with the indexes array
96
+ * to provide comprehensive sorting capabilities.
97
+ *
98
+ * @type {string[]}
99
+ * @default []
100
+ * @memberOf FilterComponent
101
+ */
102
+ this.sortBy = [];
103
+ /**
104
+ * @description Controls whether sorting functionality is disabled.
105
+ * @summary When set to true, prevents the automatic merging of sort and indexes arrays,
106
+ * effectively disabling sorting capabilities. This is useful when you want to provide
107
+ * filtering without sorting options.
108
+ *
109
+ * @type {boolean}
110
+ * @default false
111
+ * @memberOf FilterComponent
112
+ */
113
+ this.disableSort = false;
114
+ /**
115
+ * @description Available options for the current filter step.
116
+ * @summary Contains the list of options available for selection in the current step.
117
+ * This array changes dynamically based on the current step: indexes → conditions → empty for value input.
118
+ *
119
+ * @type {string[]}
120
+ * @default []
121
+ * @memberOf FilterComponent
122
+ */
123
+ this.options = [];
124
+ /**
125
+ * @description Filtered options based on user input.
126
+ * @summary Contains the subset of options that match the current user input for real-time
127
+ * filtering. This array is updated as the user types to show only relevant suggestions
128
+ * in the dropdown menu.
129
+ *
130
+ * @type {string[]}
131
+ * @default []
132
+ * @memberOf FilterComponent
133
+ */
134
+ this.filteredOptions = [];
135
+ /**
136
+ * @description Complete filter objects created by the user.
137
+ * @summary Array of complete filter objects, each containing index, condition, and value properties.
138
+ * These represent the active filters that can be applied to data operations.
139
+ *
140
+ * @type {KeyValue[]}
141
+ * @default []
142
+ * @memberOf FilterComponent
143
+ */
144
+ this.filterValue = [];
145
+ /**
146
+ * @description Current filter being constructed.
147
+ * @summary Temporary object that accumulates filter properties (index, condition, value)
148
+ * during the three-step filter creation process. Gets added to filterValue when complete.
149
+ *
150
+ * @type {KeyValue}
151
+ * @default {}
152
+ * @memberOf FilterComponent
153
+ */
154
+ this.lastFilter = {};
155
+ /**
156
+ * @description Current step in the filter creation process.
157
+ * @summary Tracks the current step of filter creation: 1 = index selection, 2 = condition selection,
158
+ * 3 = value input. Automatically resets to 1 after completing a filter.
159
+ *
160
+ * @type {number}
161
+ * @default 1
162
+ * @memberOf FilterComponent
163
+ */
164
+ this.step = 1;
165
+ /**
166
+ * @description Controls dropdown visibility state.
167
+ * @summary Boolean flag that determines whether the options dropdown is currently visible.
168
+ * Used to manage the dropdown's open/close state and coordinate with focus/blur events.
169
+ *
170
+ * @type {boolean}
171
+ * @default false
172
+ * @memberOf FilterComponent
173
+ */
174
+ this.dropdownOpen = false;
175
+ /**
176
+ * @description Current input field value.
177
+ * @summary Stores the current text input value that the user is typing. This value is
178
+ * bound to the input field and is cleared after each successful filter step completion.
179
+ *
180
+ * @type {string}
181
+ * @default ''
182
+ * @memberOf FilterComponent
183
+ */
184
+ this.value = '';
185
+ /**
186
+ * @description Current sorting field value.
187
+ * @summary Stores the field name currently selected for sorting operations.
188
+ * This value determines which field is used to order the filtered results.
189
+ * Defaults to 'id' and can be changed through the sort dropdown selection.
190
+ *
191
+ * @type {string}
192
+ * @default 'id'
193
+ * @memberOf FilterComponent
194
+ */
195
+ this.sortValue = 'id';
196
+ /**
197
+ * @description Current sorting direction.
198
+ * @summary Defines the direction of the sort operation - ascending or descending.
199
+ * This value works in conjunction with sortValue to determine the complete
200
+ * sorting configuration for filtered results.
201
+ *
202
+ * @type {OrderDirection}
203
+ * @default OrderDirection.DSC
204
+ * @memberOf FilterComponent
205
+ */
206
+ this.sortDirection = OrderDirection.DSC;
207
+ /**
208
+ * @description Event emitter for filter changes.
209
+ * @summary Emits filter events when the user creates, modifies, or clears filters.
210
+ * The emitted value contains an array of complete filter objects or undefined when
211
+ * filters are cleared. Parent components listen to this event to update their data display.
212
+ *
213
+ * @type {EventEmitter<KeyValue[] | undefined>}
214
+ * @memberOf FilterComponent
215
+ */
216
+ this.filterEvent = new EventEmitter();
217
+ /**
218
+ * @description Event emitter for search events.
219
+ * @summary Emits search events when the user interacts with the searchbar.
220
+ * @type {EventEmitter<string>}
221
+ * @memberOf FilterComponent
222
+ */
223
+ this.searchEvent = new EventEmitter();
224
+ addIcons({ chevronDownOutline, chevronUpOutline });
225
+ }
226
+ /**
227
+ * @description Initializes the component after Angular first displays the data-bound properties.
228
+ * @summary Sets up the component by initializing window width tracking, setting up resize event
229
+ * subscriptions with debouncing, configuring sorting options, and calling the base initialization.
230
+ * This method prepares the component for user interaction and responsive behavior.
231
+ *
232
+ * @mermaid
233
+ * sequenceDiagram
234
+ * participant A as Angular Lifecycle
235
+ * participant F as FilterComponent
236
+ * participant W as Window
237
+ * participant R as RxJS
238
+ *
239
+ * A->>F: ngOnInit()
240
+ * F->>W: getWindowWidth()
241
+ * W-->>F: Return current width
242
+ * F->>R: Setup resize subscription with debounce
243
+ * R-->>F: Subscription created
244
+ * alt disableSort is false
245
+ * F->>F: Merge sort and indexes arrays
246
+ * end
247
+ * F->>F: Call initialize()
248
+ *
249
+ * @returns {void}
250
+ * @memberOf FilterComponent
251
+ */
252
+ ngOnInit() {
253
+ this.windowWidth = getWindowWidth();
254
+ this.windowResizeSubscription = fromEvent(window, 'resize')
255
+ .pipe(debounceTime(300))
256
+ .subscribe(() => {
257
+ this.windowWidth = getWindowWidth();
258
+ });
259
+ this.getIndexes();
260
+ this.initialize();
261
+ }
262
+ /**
263
+ * @description Retrieves and configures available indexes for filtering and sorting.
264
+ * @summary Extracts field indexes from the model if available and merges them with
265
+ * sorting options when sorting is enabled. This method sets up the available field
266
+ * options for both filtering and sorting operations based on the model structure.
267
+ *
268
+ * @returns {void}
269
+ * @memberOf FilterComponent
270
+ */
271
+ getIndexes() {
272
+ if (this.model)
273
+ this.indexes = Object.keys(Repository.indexes(this.model) || {});
274
+ if (!this.disableSort)
275
+ this.sortBy = [...this.sortBy, ...this.indexes];
276
+ }
277
+ /**
278
+ * @description Cleanup method called when the component is destroyed.
279
+ * @summary Unsubscribes from window resize events to prevent memory leaks.
280
+ * This is essential for proper cleanup of RxJS subscriptions when the component
281
+ * is removed from the DOM.
282
+ *
283
+ * @returns {void}
284
+ * @memberOf FilterComponent
285
+ */
286
+ ngOnDestroy() {
287
+ this.windowResizeSubscription.unsubscribe();
288
+ this.clear();
289
+ }
290
+ /**
291
+ * @description Handles input events from the text field.
292
+ * @summary Processes user input and filters the available options based on the typed value.
293
+ * This method provides real-time filtering of suggestions as the user types in the input field.
294
+ *
295
+ * @param {InputEvent} event - The input event containing the new value
296
+ * @returns {void}
297
+ * @memberOf FilterComponent
298
+ */
299
+ handleInput(event) {
300
+ const { value } = event.target;
301
+ this.filteredOptions = this.filterOptions(value);
302
+ }
303
+ /**
304
+ * @description Handles focus events on the input field.
305
+ * @summary Sets up the available options when the input field receives focus and opens the dropdown.
306
+ * If no options are provided, automatically determines the appropriate options based on current step.
307
+ * This method initializes the dropdown with contextually relevant suggestions.
308
+ *
309
+ * @param {string[]} options - Optional array of options to display
310
+ * @returns {void}
311
+ * @memberOf FilterComponent
312
+ */
313
+ handleFocus(options = []) {
314
+ if (!options.length)
315
+ options = this.getOptions();
316
+ this.filteredOptions = this.options = options;
317
+ this.dropdownOpen = true;
318
+ }
319
+ /**
320
+ * @description Handles blur events on the input field with delayed closing.
321
+ * @summary Manages the dropdown closing behavior with a delay to allow for option selection.
322
+ * Uses a two-phase approach to prevent premature closing when users click on dropdown options.
323
+ *
324
+ * @param {boolean} close - Internal flag to control the closing phase
325
+ * @returns {void}
326
+ * @memberOf FilterComponent
327
+ */
328
+ handleBlur(close = false) {
329
+ if (!close) {
330
+ this.dropdownOpen = false;
331
+ setTimeout(() => {
332
+ this.handleBlur(true);
333
+ }, 100);
334
+ }
335
+ else {
336
+ if (!this.dropdownOpen && this.options.length) {
337
+ setTimeout(() => {
338
+ this.options = [];
339
+ this.dropdownOpen = false;
340
+ }, 50);
341
+ }
342
+ }
343
+ }
344
+ /**
345
+ * @description Determines the appropriate options based on the current filter step.
346
+ * @summary Returns the contextually relevant options for the current step in the filter creation process.
347
+ * Step 1 shows indexes, Step 2 shows conditions, Step 3 shows no options (value input).
348
+ *
349
+ * @returns {string[]} Array of options appropriate for the current step
350
+ * @memberOf FilterComponent
351
+ */
352
+ getOptions() {
353
+ switch (this.step) {
354
+ case 1:
355
+ this.options = this.indexes;
356
+ break;
357
+ case 2:
358
+ this.options = this.conditions;
359
+ break;
360
+ case 3:
361
+ this.options = [];
362
+ break;
363
+ }
364
+ return this.options;
365
+ }
366
+ /**
367
+ * @description Adds a filter step or completes filter creation through a three-step process.
368
+ * @summary Core method for building filters step by step: Step 1 (Index) → Step 2 (Condition) → Step 3 (Value).
369
+ * When all steps are complete, creates a complete filter object and adds it to the filter collection.
370
+ * Handles both keyboard events (Enter to submit) and programmatic calls.
371
+ *
372
+ * @param {string} value - The value to add for the current step
373
+ * @param {CustomEvent} event - Optional event (KeyboardEvent triggers submission when value is empty)
374
+ * @returns {void}
375
+ *
376
+ * @mermaid
377
+ * sequenceDiagram
378
+ * participant U as User
379
+ * participant F as FilterComponent
380
+ *
381
+ * U->>F: addFilter(value, event)
382
+ * F->>F: Trim and validate value
383
+ * alt KeyboardEvent && empty value
384
+ * F->>F: submit() - Send current filters
385
+ * else Valid value or step 3
386
+ * alt Step 1 (Index)
387
+ * F->>F: lastFilter.index = value
388
+ * F->>F: options = conditions
389
+ * else Step 2 (Condition)
390
+ * F->>F: lastFilter.condition = value
391
+ * F->>F: options = []
392
+ * else Step 3 (Value)
393
+ * F->>F: lastFilter.value = value
394
+ * F->>F: Add complete filter to filterValue
395
+ * F->>F: Reset step to 1
396
+ * end
397
+ * F->>F: Increment step
398
+ * F->>F: Clear input & focus
399
+ * F->>F: Show next options
400
+ * end
401
+ *
402
+ * @memberOf FilterComponent
403
+ */
404
+ addFilter(value, event) {
405
+ value = value.trim();
406
+ if (event instanceof KeyboardEvent && !value) {
407
+ this.submit();
408
+ }
409
+ else {
410
+ if ((value && (!(event instanceof KeyboardEvent)) || this.step === 3)) {
411
+ const filter = this.lastFilter;
412
+ switch (this.step) {
413
+ case 1:
414
+ filter['index'] = value;
415
+ this.options = this.conditions;
416
+ break;
417
+ case 2:
418
+ filter['condition'] = value;
419
+ this.options = [];
420
+ break;
421
+ case 3:
422
+ filter['value'] = value;
423
+ this.options = this.indexes;
424
+ break;
425
+ }
426
+ if (!this.filterValue.length) {
427
+ this.filterValue.push(filter);
428
+ }
429
+ else {
430
+ if (this.step === 1)
431
+ this.filterValue.push(filter);
432
+ }
433
+ if (this.step === 3) {
434
+ this.step = 0;
435
+ this.filterValue[this.filterValue.length - 1] = filter;
436
+ this.lastFilter = {};
437
+ }
438
+ this.step++;
439
+ this.value = '';
440
+ if (this.options.length)
441
+ this.handleFocus(this.options);
442
+ this.component.nativeElement.focus();
443
+ }
444
+ }
445
+ }
446
+ /**
447
+ * @description Selects an option from the dropdown suggestions.
448
+ * @summary Handles option selection when a user clicks on a suggestion in the dropdown.
449
+ * This method acts as a bridge between dropdown clicks and the main addFilter logic.
450
+ *
451
+ * @param {CustomEvent} event - The click event from the dropdown option
452
+ * @param {string} value - The selected option value
453
+ * @returns {void}
454
+ * @memberOf FilterComponent
455
+ */
456
+ selectOption(value) {
457
+ this.addFilter(value);
458
+ }
459
+ /**
460
+ * @description Determines if a filter option can be individually removed.
461
+ * @summary Checks whether a filter component should display a close icon for removal.
462
+ * Only value options can be removed individually; index and condition options are part
463
+ * of the complete filter structure and cannot be removed separately.
464
+ *
465
+ * @param {string} option - The filter option text to check
466
+ * @returns {boolean} True if the option can be cleared individually, false otherwise
467
+ * @memberOf FilterComponent
468
+ */
469
+ allowClear(option) {
470
+ return this.indexes.indexOf(option) === -1 && this.conditions.indexOf(option) === -1;
471
+ }
472
+ /**
473
+ * @description Removes a complete filter from the collection based on filter value.
474
+ * @summary Removes a complete filter by matching the provided value against filter values
475
+ * in the collection. Uses string normalization to handle accents and case differences.
476
+ * After removal, resets the interface to show available indexes for new filter creation.
477
+ *
478
+ * @param {string} filter - The filter value to remove (matches against filter.value property)
479
+ * @returns {void}
480
+ *
481
+ * @mermaid
482
+ * sequenceDiagram
483
+ * participant U as User
484
+ * participant F as FilterComponent
485
+ *
486
+ * U->>F: removeFilter(filterValue)
487
+ * F->>F: cleanString(filterValue)
488
+ * F->>F: Filter out matching filter objects
489
+ * F->>F: Clear input value
490
+ * F->>F: handleFocus(indexes) - Reset to index selection
491
+ * Note over F: Filter removed and UI reset
492
+ *
493
+ * @memberOf FilterComponent
494
+ */
495
+ removeFilter(filter) {
496
+ function cleanString(filter) {
497
+ return filter
498
+ .toLowerCase() // convert all characters to lowercase
499
+ .normalize("NFD") // separate accent marks from characters
500
+ .replace(/[\u0300-\u036f]/g, "") // remove accent marks
501
+ .replace(/\s+/g, ""); // remove all whitespace
502
+ }
503
+ this.value = "";
504
+ this.filterValue = this.filterValue.filter((item) => item?.['value'] && cleanString(item?.['value']) !== cleanString(filter));
505
+ if (this.filterValue.length === 0) {
506
+ this.step = 1;
507
+ this.lastFilter = {};
508
+ }
509
+ this.handleFocus(this.indexes);
510
+ }
511
+ /**
512
+ * @description Resets the component to its initial state.
513
+ * @summary Clears all filter data, options, and resets the step counter to 1.
514
+ * This method provides a clean slate for new filter creation without emitting events.
515
+ *
516
+ * @returns {void}
517
+ * @memberOf FilterComponent
518
+ */
519
+ reset() {
520
+ this.options = this.filteredOptions = this.filterValue = [];
521
+ this.step = 1;
522
+ this.lastFilter = {};
523
+ this.value = '';
524
+ setTimeout(() => {
525
+ this.submit();
526
+ }, 100);
527
+ }
528
+ /**
529
+ * @description Clears all filters and notifies parent components.
530
+ * @summary Resets the component state and emits undefined to notify parent components
531
+ * that all filters have been cleared. This triggers any connected data refresh logic.
532
+ *
533
+ * @param {string} value - Optional parameter (currently unused)
534
+ * @returns {void}
535
+ * @memberOf FilterComponent
536
+ */
537
+ clear(value) {
538
+ if (!value)
539
+ this.reset();
540
+ }
541
+ /**
542
+ * @description Submits the current filter collection to parent components.
543
+ * @summary Emits the current filter array to parent components when filters are ready
544
+ * to be applied. Only emits if there are active filters. Clears options after submission.
545
+ *
546
+ * @returns {void}
547
+ * @memberOf FilterComponent
548
+ */
549
+ submit() {
550
+ this.filterEvent.emit({
551
+ query: this.filterValue.length > 0 ? this.filterValue : undefined,
552
+ sort: {
553
+ value: this.sortValue,
554
+ direction: this.sortDirection
555
+ }
556
+ });
557
+ if (this.filterValue.length === 0)
558
+ this.options = [];
559
+ }
560
+ /**
561
+ * @description Toggles the sort direction between ascending and descending.
562
+ * @summary Handles sort direction changes by toggling between ASC and DSC values.
563
+ * When the direction changes, automatically triggers a submit to apply the new
564
+ * sorting configuration to the filtered results.
565
+ *
566
+ * @returns {void}
567
+ * @memberOf FilterComponent
568
+ */
569
+ handleSortDirectionChange() {
570
+ const direction = this.sortDirection === OrderDirection.ASC ? OrderDirection.DSC : OrderDirection.ASC;
571
+ if (direction !== this.sortDirection) {
572
+ this.sortDirection = direction;
573
+ this.submit();
574
+ }
575
+ }
576
+ /**
577
+ * @description Handles sort field selection changes from the dropdown.
578
+ * @summary Processes sort field changes when users select a different field
579
+ * from the sort dropdown. Updates the sortValue property and triggers
580
+ * a submit to apply the new sorting configuration if the value has changed.
581
+ *
582
+ * @param {CustomEvent} event - The select change event containing the new sort field value
583
+ * @returns {void}
584
+ * @memberOf FilterComponent
585
+ */
586
+ handleSortChange(event) {
587
+ const target = event.target;
588
+ const value = target.value;
589
+ if (value !== this.sortValue) {
590
+ this.sortValue = value;
591
+ this.submit();
592
+ }
593
+ }
594
+ /**
595
+ * @description Filters available options based on user input with visual highlighting.
596
+ * @summary Performs real-time filtering of available options based on user input.
597
+ * Also handles visual highlighting of matching options in the dropdown. Returns all
598
+ * options if input is less than 2 characters for performance optimization.
599
+ *
600
+ * @param {string | null | undefined} value - The search value to filter by
601
+ * @returns {string[]} Array of filtered options that match the input
602
+ *
603
+ * @mermaid
604
+ * sequenceDiagram
605
+ * participant U as User
606
+ * participant F as FilterComponent
607
+ * participant D as DOM
608
+ *
609
+ * U->>F: filterOptions(inputValue)
610
+ * alt inputValue < 2 characters
611
+ * F->>D: Remove existing highlights
612
+ * F-->>U: Return all options
613
+ * else inputValue >= 2 characters
614
+ * F->>D: Query all option elements
615
+ * F->>D: Add highlight to first matching option
616
+ * F->>F: Filter options by substring match
617
+ * F-->>U: Return filtered options
618
+ * end
619
+ *
620
+ * @memberOf FilterComponent
621
+ */
622
+ filterOptions(value) {
623
+ const optionsElement = this.optionsFilterElement.nativeElement;
624
+ if (!value?.length || !value || value.length < 2) {
625
+ const filteredOption = optionsElement.querySelector('.dcf-filtering-item');
626
+ if (filteredOption)
627
+ filteredOption.classList.remove('dcf-filtering-item');
628
+ return this.options;
629
+ }
630
+ const options = optionsElement.querySelectorAll('.dcf-item');
631
+ for (const option of options) {
632
+ const isActive = option.textContent?.toLowerCase().includes(value.toLowerCase());
633
+ if (isActive) {
634
+ option.classList.add('dcf-filtering-item');
635
+ break;
636
+ }
637
+ }
638
+ return this.options.filter((option) => option.toLowerCase().includes(value.toLowerCase()));
639
+ }
640
+ /**
641
+ * @description Handles search events from the integrated searchbar component.
642
+ * @summary Processes search input from the searchbar and emits search events
643
+ * to parent components. This method acts as a bridge between the internal
644
+ * searchbar component and external search event listeners.
645
+ *
646
+ * @param {string | undefined} value - The search value entered by the user
647
+ * @returns {void}
648
+ * @memberOf FilterComponent
649
+ */
650
+ handleSearch(value) {
651
+ this.searchEvent.emit(value);
652
+ }
653
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
654
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: FilterComponent, isStandalone: true, selector: "ngx-decaf-filter", inputs: { indexes: "indexes", conditions: "conditions", sortBy: "sortBy", disableSort: "disableSort" }, outputs: { filterEvent: "filterEvent", searchEvent: "searchEvent" }, viewQueries: [{ propertyName: "optionsFilterElement", first: true, predicate: ["optionsFilterElement"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0, template: "\n@if(!indexes.length) {\n <ngx-decaf-searchbar [emitEventToWindow]=\"false\" [debounce]=\"500\" (searchEvent)=\"handleSearch($event)\" />\n}\n\n<div class=\"dcf-grid dcf-grid-small dcf-grid-match dcf-filter-grid\" [ngClass]=\"{'dcf-hidden': !indexes.length}\">\n <div class=\"dcf-width-expand\">\n <div class=\"dcf-filter\">\n <div class=\"dcf-input\">\n @for(filter of filterValue; track trackItemFn($index, filter?.['index'])) {\n @if(filter?.['index']) {\n <ion-chip [outline]=\"true\">{{ filter?.['index'] }}</ion-chip>\n }\n @if(filter?.['condition']) {\n <ion-chip [outline]=\"true\">{{ filter?.['condition'] }}</ion-chip>\n }\n @if(filter?.['value']) {\n <ion-chip [outline]=\"true\" class=\"dcf-filter-value\">\n {{ filter?.['value'] }}\n <ion-icon name=\"close\" (click)=\"removeFilter(filter?.['value'])\" size=\"small\"></ion-icon>\n </ion-chip>\n }\n }\n <div class=\"dcf-width-1-1\">\n <input\n fill=\"none\"\n [(ngModel)]=\"value\"\n (keydown.enter)=\"addFilter(value, $event)\"\n (keydown.backspace)=\"clear(value)\"\n (input)=\"handleInput($event)\"\n (click)=\"handleFocus()\"\n (blur)=\"handleBlur()\"\n type=\"text\"\n [readonly]=\"step !== 3\"\n placeholder=\"{{ locale + (step === 3 ? '.type' : '.select') | translate }}\"\n #component\n />\n @if(windowWidth >= 768) {\n <div [class]=\"'dcf-dropdown ' + (options.length > 0 ? ' dcf-active' : '')\" #optionsFilterElement>\n <div>\n @if(filteredOptions.length > 0) {\n @for(key of filteredOptions; track key) {\n <div\n class=\"dcf-item\"\n tabindex=\"0\"\n (keydown.enter)=\"selectOption(key)\"\n (click)=\"selectOption(key)\">\n {{ key }}\n </div>\n }\n } @else {\n <div class=\"dcf-empty\"\n (click)=\"filteredOptions = options; value = ''\"\n tabindex=\"0\"\n (keydown.enter)=\"filteredOptions = options; value = ''\"\n >\n {{ 'no_suggestions' | translate }}\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n @if(filterValue.length > 0) {\n <div class=\"dcf-icon-clear\">\n <ion-button fill=\"clear\" size=\"small\" (click)=\"clear()\">\n <ion-icon name=\"trash-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n </div>\n }\n <div class=\"dcf-icon-search\">\n <ion-button fill=\"clear\" size=\"small\" (click)=\"submit()\">\n <ion-icon name=\"search-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n </div>\n </div>\n @if(windowWidth < 768) {\n <div [class]=\"'dcf-dropdown ' + (options.length > 0 ? ' dcf-active' : '')\" #optionsFilterElement>\n <div>\n @if(filteredOptions.length > 0) {\n @for(key of filteredOptions; track key) {\n <div\n class=\"dcf-item\"\n tabindex=\"0\"\n (keydown.enter)=\"selectOption(key)\"\n (click)=\"selectOption(key)\">\n {{ key }}\n </div>\n }\n } @else {\n <div class=\"dcf-empty\"\n (click)=\"filteredOptions = options; value = ''\"\n tabindex=\"0\"\n (keydown.enter)=\"filteredOptions = options; value = ''\"\n >\n {{ 'no_suggestions' | translate }}\n </div>\n }\n </div>\n </div>\n }\n </div>\n @if(!disableSort) {\n <div class=\"dcf-width-1-5@m dcf-width-1-1 dcf-sort-container\">\n <div class=\"dcf-grid dcf-grid-collapse dcf-flex dcf-flex-middle dcf-grid-match\">\n <div class=\"dcf-width-expand\">\n <ion-select\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n class=\"dcf-sort-select\"\n (ionChange)=\"handleSortChange($event)\"\n interface=\"popover\"\n [value]=\"sortValue\"\n label-placement=\"floating\"\n fill=\"outline\"\n >\n @for(sort of sortBy; track sort) {\n aa\n <ion-select-option [value]=\"sort\">{{ sort | translate }}</ion-select-option>\n }\n </ion-select>\n </div>\n <div class=\"dcf-width-auto\">\n <ion-button (click)=\"handleSortDirectionChange()\" fill=\"clear\">\n <ion-icon slot=\"icon-only\" [name]=\"sortDirection === 'desc' ? 'arrow-down-outline' : 'arrow-up-outline'\"></ion-icon>\n </ion-button>\n </div>\n </div>\n </div>\n }\n</div>\n\n\n", styles: [".dcf-filter-grid{padding:0 .5rem;margin-top:.75rem;margin-bottom:.75rem}ion-select{min-height:44px!important}.dcf-hidden{display:none!important}.dcf-filter{display:flex;width:100%;min-height:40px;border:1px solid var(--ion-color-gray-3);box-shadow:0 1px 2px #0a0d120d;background-color:#fff;border-radius:var(--dcf-border-radius);box-sizing:border-box}.dcf-filter:focus-within{border-color:var(--ion-color-primary);background-color:#fff}.dcf-filter ion-chip{border-radius:6px;padding:0 8px!important;height:24px;min-height:24px;font-size:.75rem;border:1px solid var(--ion-color-gray-3);color:var(--ion-color-gray-7);font-style:normal;font-weight:500;flex-shrink:0;margin-right:2px;white-space:nowrap}.dcf-filter ion-chip.dcf-filter-value{background:var(--ion-color-gray-2);border-color:var(--ion-color-gray-4)!important;color:var(--ion-color-gray-8)!important}.dcf-filter ion-chip.sc-ion-chip-md-h,.dcf-filter ion-chip.sc-ion-chip-ios-h{height:24px;min-height:24px}.dcf-filter ion-chip.sc-ion-chip-md-h .chip-native,.dcf-filter ion-chip.sc-ion-chip-ios-h .chip-native{padding:0 8px!important;height:24px;min-height:24px}.dcf-filter ion-chip ion-label{padding:0 4px;margin:0;font-size:.75rem;white-space:nowrap}.dcf-filter ion-chip ion-icon{margin:0 2px;font-size:.75rem}.dcf-filter .dcf-input{width:100%;display:flex;align-items:center;overflow-x:auto;overflow-y:hidden;white-space:nowrap;padding-left:.5rem}.dcf-filter .dcf-input input{min-height:40px;color:var(--ion-color-gray-7);min-width:100px;width:100%;font-size:1rem;border:none;outline:none;background:transparent;border:0px!important;outline:none!important}.dcf-filter .dcf-input input:focus{border:0px!important;outline:none!important}.dcf-filter .dcf-icon-clear,.dcf-filter .dcf-icon-search{display:flex;justify-content:center;text-align:center;align-items:center;min-width:40px}.dcf-filter .dcf-icon-search ion-icon{font-size:1.25rem}.dcf-sort-container{min-width:200px!important;width:auto}@media (min-width: 990px){.dcf-sort-container{max-width:20%!important}}@media (max-width: 680px){.dcf-sort-container{min-width:100%!important;margin:.75rem 0rem}}.dcf-dropdown{position:absolute;max-height:200px;overflow-y:auto;background-color:#fff;border-radius:4px;z-index:1000!important;min-width:200px;max-width:300px;display:none}.dcf-dropdown.dcf-active{display:block;margin-top:-3px;box-shadow:0 12px 16px -4px #0a0d1214,0 4px 6px -2px #0a0d1208,0 2px 2px -1px #0a0d120a!important;border:1px solid var(--ion-color-gray-2);border-radius:var(--dcf-border-radius);padding:.5rem .25rem}@media (max-width: 768px){.dcf-dropdown.dcf-active{margin-top:55px}}.dcf-dropdown.dcf-active>div>div{cursor:pointer;height:35px;padding:.5rem 1rem;border:1px solid transparent;font-size:1rem;display:flex;align-items:center;color:var(--ion-color-gray-8);border-radius:6px}.dcf-dropdown.dcf-active>div>div.dcf-filtering-item,.dcf-dropdown.dcf-active>div>div:only-child{border-color:var(--ion-color-primary)}.dcf-dropdown.dcf-active>div>div.dcf-filtering-item.dcf-empty,.dcf-dropdown.dcf-active>div>div:only-child.dcf-empty{border-color:var(--ion-color-gray-7)!important}.dcf-dropdown.dcf-active>div>div:hover{background-color:var(--ion-color-gray-1)}\n"], dependencies: [{ kind: "ngmodule", type: ForAngularModule }, { kind: "component", type: i1.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "component", type: IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: SearchbarComponent, selector: "ngx-decaf-searchbar", inputs: ["autocomplete", "autocorrect", "animated", "buttonCancelText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value", "queryKeys", "isVisible", "wrapper", "wrapperColor", "emitEventToWindow"], outputs: ["searchEvent"] }] }); }
655
+ };
656
+ FilterComponent = __decorate([
657
+ Dynamic(),
658
+ __metadata("design:paramtypes", [])
659
+ ], FilterComponent);
660
+ export { FilterComponent };
661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FilterComponent, decorators: [{
662
+ type: Component,
663
+ args: [{ selector: 'ngx-decaf-filter', imports: [
664
+ ForAngularModule,
665
+ IonLabel,
666
+ IonItem,
667
+ IonChip,
668
+ IonIcon,
669
+ IonSelect,
670
+ IonIcon,
671
+ SearchbarComponent
672
+ ], standalone: true, template: "\n@if(!indexes.length) {\n <ngx-decaf-searchbar [emitEventToWindow]=\"false\" [debounce]=\"500\" (searchEvent)=\"handleSearch($event)\" />\n}\n\n<div class=\"dcf-grid dcf-grid-small dcf-grid-match dcf-filter-grid\" [ngClass]=\"{'dcf-hidden': !indexes.length}\">\n <div class=\"dcf-width-expand\">\n <div class=\"dcf-filter\">\n <div class=\"dcf-input\">\n @for(filter of filterValue; track trackItemFn($index, filter?.['index'])) {\n @if(filter?.['index']) {\n <ion-chip [outline]=\"true\">{{ filter?.['index'] }}</ion-chip>\n }\n @if(filter?.['condition']) {\n <ion-chip [outline]=\"true\">{{ filter?.['condition'] }}</ion-chip>\n }\n @if(filter?.['value']) {\n <ion-chip [outline]=\"true\" class=\"dcf-filter-value\">\n {{ filter?.['value'] }}\n <ion-icon name=\"close\" (click)=\"removeFilter(filter?.['value'])\" size=\"small\"></ion-icon>\n </ion-chip>\n }\n }\n <div class=\"dcf-width-1-1\">\n <input\n fill=\"none\"\n [(ngModel)]=\"value\"\n (keydown.enter)=\"addFilter(value, $event)\"\n (keydown.backspace)=\"clear(value)\"\n (input)=\"handleInput($event)\"\n (click)=\"handleFocus()\"\n (blur)=\"handleBlur()\"\n type=\"text\"\n [readonly]=\"step !== 3\"\n placeholder=\"{{ locale + (step === 3 ? '.type' : '.select') | translate }}\"\n #component\n />\n @if(windowWidth >= 768) {\n <div [class]=\"'dcf-dropdown ' + (options.length > 0 ? ' dcf-active' : '')\" #optionsFilterElement>\n <div>\n @if(filteredOptions.length > 0) {\n @for(key of filteredOptions; track key) {\n <div\n class=\"dcf-item\"\n tabindex=\"0\"\n (keydown.enter)=\"selectOption(key)\"\n (click)=\"selectOption(key)\">\n {{ key }}\n </div>\n }\n } @else {\n <div class=\"dcf-empty\"\n (click)=\"filteredOptions = options; value = ''\"\n tabindex=\"0\"\n (keydown.enter)=\"filteredOptions = options; value = ''\"\n >\n {{ 'no_suggestions' | translate }}\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n @if(filterValue.length > 0) {\n <div class=\"dcf-icon-clear\">\n <ion-button fill=\"clear\" size=\"small\" (click)=\"clear()\">\n <ion-icon name=\"trash-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n </div>\n }\n <div class=\"dcf-icon-search\">\n <ion-button fill=\"clear\" size=\"small\" (click)=\"submit()\">\n <ion-icon name=\"search-outline\" color=\"dark\" slot=\"icon-only\"></ion-icon>\n </ion-button>\n </div>\n </div>\n @if(windowWidth < 768) {\n <div [class]=\"'dcf-dropdown ' + (options.length > 0 ? ' dcf-active' : '')\" #optionsFilterElement>\n <div>\n @if(filteredOptions.length > 0) {\n @for(key of filteredOptions; track key) {\n <div\n class=\"dcf-item\"\n tabindex=\"0\"\n (keydown.enter)=\"selectOption(key)\"\n (click)=\"selectOption(key)\">\n {{ key }}\n </div>\n }\n } @else {\n <div class=\"dcf-empty\"\n (click)=\"filteredOptions = options; value = ''\"\n tabindex=\"0\"\n (keydown.enter)=\"filteredOptions = options; value = ''\"\n >\n {{ 'no_suggestions' | translate }}\n </div>\n }\n </div>\n </div>\n }\n </div>\n @if(!disableSort) {\n <div class=\"dcf-width-1-5@m dcf-width-1-1 dcf-sort-container\">\n <div class=\"dcf-grid dcf-grid-collapse dcf-flex dcf-flex-middle dcf-grid-match\">\n <div class=\"dcf-width-expand\">\n <ion-select\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n class=\"dcf-sort-select\"\n (ionChange)=\"handleSortChange($event)\"\n interface=\"popover\"\n [value]=\"sortValue\"\n label-placement=\"floating\"\n fill=\"outline\"\n >\n @for(sort of sortBy; track sort) {\n aa\n <ion-select-option [value]=\"sort\">{{ sort | translate }}</ion-select-option>\n }\n </ion-select>\n </div>\n <div class=\"dcf-width-auto\">\n <ion-button (click)=\"handleSortDirectionChange()\" fill=\"clear\">\n <ion-icon slot=\"icon-only\" [name]=\"sortDirection === 'desc' ? 'arrow-down-outline' : 'arrow-up-outline'\"></ion-icon>\n </ion-button>\n </div>\n </div>\n </div>\n }\n</div>\n\n\n", styles: [".dcf-filter-grid{padding:0 .5rem;margin-top:.75rem;margin-bottom:.75rem}ion-select{min-height:44px!important}.dcf-hidden{display:none!important}.dcf-filter{display:flex;width:100%;min-height:40px;border:1px solid var(--ion-color-gray-3);box-shadow:0 1px 2px #0a0d120d;background-color:#fff;border-radius:var(--dcf-border-radius);box-sizing:border-box}.dcf-filter:focus-within{border-color:var(--ion-color-primary);background-color:#fff}.dcf-filter ion-chip{border-radius:6px;padding:0 8px!important;height:24px;min-height:24px;font-size:.75rem;border:1px solid var(--ion-color-gray-3);color:var(--ion-color-gray-7);font-style:normal;font-weight:500;flex-shrink:0;margin-right:2px;white-space:nowrap}.dcf-filter ion-chip.dcf-filter-value{background:var(--ion-color-gray-2);border-color:var(--ion-color-gray-4)!important;color:var(--ion-color-gray-8)!important}.dcf-filter ion-chip.sc-ion-chip-md-h,.dcf-filter ion-chip.sc-ion-chip-ios-h{height:24px;min-height:24px}.dcf-filter ion-chip.sc-ion-chip-md-h .chip-native,.dcf-filter ion-chip.sc-ion-chip-ios-h .chip-native{padding:0 8px!important;height:24px;min-height:24px}.dcf-filter ion-chip ion-label{padding:0 4px;margin:0;font-size:.75rem;white-space:nowrap}.dcf-filter ion-chip ion-icon{margin:0 2px;font-size:.75rem}.dcf-filter .dcf-input{width:100%;display:flex;align-items:center;overflow-x:auto;overflow-y:hidden;white-space:nowrap;padding-left:.5rem}.dcf-filter .dcf-input input{min-height:40px;color:var(--ion-color-gray-7);min-width:100px;width:100%;font-size:1rem;border:none;outline:none;background:transparent;border:0px!important;outline:none!important}.dcf-filter .dcf-input input:focus{border:0px!important;outline:none!important}.dcf-filter .dcf-icon-clear,.dcf-filter .dcf-icon-search{display:flex;justify-content:center;text-align:center;align-items:center;min-width:40px}.dcf-filter .dcf-icon-search ion-icon{font-size:1.25rem}.dcf-sort-container{min-width:200px!important;width:auto}@media (min-width: 990px){.dcf-sort-container{max-width:20%!important}}@media (max-width: 680px){.dcf-sort-container{min-width:100%!important;margin:.75rem 0rem}}.dcf-dropdown{position:absolute;max-height:200px;overflow-y:auto;background-color:#fff;border-radius:4px;z-index:1000!important;min-width:200px;max-width:300px;display:none}.dcf-dropdown.dcf-active{display:block;margin-top:-3px;box-shadow:0 12px 16px -4px #0a0d1214,0 4px 6px -2px #0a0d1208,0 2px 2px -1px #0a0d120a!important;border:1px solid var(--ion-color-gray-2);border-radius:var(--dcf-border-radius);padding:.5rem .25rem}@media (max-width: 768px){.dcf-dropdown.dcf-active{margin-top:55px}}.dcf-dropdown.dcf-active>div>div{cursor:pointer;height:35px;padding:.5rem 1rem;border:1px solid transparent;font-size:1rem;display:flex;align-items:center;color:var(--ion-color-gray-8);border-radius:6px}.dcf-dropdown.dcf-active>div>div.dcf-filtering-item,.dcf-dropdown.dcf-active>div>div:only-child{border-color:var(--ion-color-primary)}.dcf-dropdown.dcf-active>div>div.dcf-filtering-item.dcf-empty,.dcf-dropdown.dcf-active>div>div:only-child.dcf-empty{border-color:var(--ion-color-gray-7)!important}.dcf-dropdown.dcf-active>div>div:hover{background-color:var(--ion-color-gray-1)}\n"] }]
673
+ }], ctorParameters: () => [], propDecorators: { optionsFilterElement: [{
674
+ type: ViewChild,
675
+ args: ['optionsFilterElement', { read: ElementRef, static: false }]
676
+ }], indexes: [{
677
+ type: Input
678
+ }], conditions: [{
679
+ type: Input
680
+ }], sortBy: [{
681
+ type: Input
682
+ }], disableSort: [{
683
+ type: Input
684
+ }], filterEvent: [{
685
+ type: Output
686
+ }], searchEvent: [{
687
+ type: Output
688
+ }] } });
689
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsdGVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9maWx0ZXIvZmlsdGVyLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9maWx0ZXIvZmlsdGVyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFxQixNQUFNLEVBQUUsU0FBUyxFQUFHLE1BQU0sZUFBZSxDQUFDO0FBQ2xILE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzVELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFDLE1BQU0sMkJBQTJCLENBQUM7QUFDMUYsT0FBTyxFQUFFLE9BQU8sRUFBa0MsTUFBTSxjQUFjLENBQUM7QUFDdkUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFnQixNQUFNLE1BQU0sQ0FBQztBQUM3RCxPQUFPLEVBQUUsY0FBYyxFQUFFLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTVELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ3RFLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDcEMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7Ozs7OztBQUV0RTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdDRztBQWtCSSxJQUFNLGVBQWUsR0FBckIsTUFB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QUMsTUFBTSxDQUFDLENBQUMsTUFBYyxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQVksQ0FBQyxDQUFDLENBQUM7SUFDL0csQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNILFlBQVksQ0FBQyxLQUF5QjtRQUNwQyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUMvQixDQUFDOytHQTNwQlUsZUFBZTttR0FBZixlQUFlLGtXQVdpQixVQUFVLG9EQ2xGdkQseWtLQXNJQSxpckdEMUVJLGdCQUFnQiwwZ0NBR2hCLE9BQU8sdUdBQ1AsT0FBTywySkFDUCxTQUFTLGtWQUVULGtCQUFrQjs7QUFJVCxlQUFlO0lBakIzQixPQUFPLEVBQUU7O0dBaUJHLGVBQWUsQ0E2cEIzQjs7NEZBN3BCWSxlQUFlO2tCQWhCM0IsU0FBUzsrQkFDRSxrQkFBa0IsV0FHbkI7d0JBQ1AsZ0JBQWdCO3dCQUNoQixRQUFRO3dCQUNSLE9BQU87d0JBQ1AsT0FBTzt3QkFDUCxPQUFPO3dCQUNQLFNBQVM7d0JBQ1QsT0FBTzt3QkFDUCxrQkFBa0I7cUJBQ25CLGNBQ1csSUFBSTt3REFjaEIsb0JBQW9CO3NCQURuQixTQUFTO3VCQUFDLHNCQUFzQixFQUFFLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFO2dCQWN0RSxPQUFPO3NCQUROLEtBQUs7Z0JBY04sVUFBVTtzQkFEVCxLQUFLO2dCQWNOLE1BQU07c0JBREwsS0FBSztnQkFjTixXQUFXO3NCQURWLEtBQUs7Z0JBeUlOLFdBQVc7c0JBRFYsTUFBTTtnQkFVUCxXQUFXO3NCQURWLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIEVsZW1lbnRSZWYsIEV2ZW50RW1pdHRlciwgSW5wdXQsIE9uRGVzdHJveSwgT25Jbml0LCBPdXRwdXQsIFZpZXdDaGlsZCAgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEZvckFuZ3VsYXJNb2R1bGUgfSBmcm9tICcuLi8uLi9mb3ItYW5ndWxhci5tb2R1bGUnO1xuaW1wb3J0IHsgTmd4QmFzZUNvbXBvbmVudCB9IGZyb20gJy4uLy4uL2VuZ2luZS9OZ3hCYXNlQ29tcG9uZW50JztcbmltcG9ydCB7IElvbkNoaXAsIElvbkljb24sIElvbkl0ZW0sIElvbkxhYmVsLCBJb25TZWxlY3R9IGZyb20gJ0Bpb25pYy9hbmd1bGFyL3N0YW5kYWxvbmUnO1xuaW1wb3J0IHsgRHluYW1pYywgSUZpbHRlclF1ZXJ5LCBJRmlsdGVyUXVlcnlJdGVtIH0gZnJvbSAnLi4vLi4vZW5naW5lJztcbmltcG9ydCB7IGdldFdpbmRvd1dpZHRoIH0gZnJvbSAnLi4vLi4vaGVscGVycy91dGlscyc7XG5pbXBvcnQgeyBkZWJvdW5jZVRpbWUsIGZyb21FdmVudCwgU3Vic2NyaXB0aW9uIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBPcmRlckRpcmVjdGlvbiwgUmVwb3NpdG9yeSB9IGZyb20gJ0BkZWNhZi10cy9jb3JlJztcbmltcG9ydCB7IE1vZGVsIH0gZnJvbSAnQGRlY2FmLXRzL2RlY29yYXRvci12YWxpZGF0aW9uJztcbmltcG9ydCB7IFNlYXJjaGJhckNvbXBvbmVudCB9IGZyb20gJy4uL3NlYXJjaGJhci9zZWFyY2hiYXIuY29tcG9uZW50JztcbmltcG9ydCB7IGFkZEljb25zIH0gZnJvbSAnaW9uaWNvbnMnO1xuaW1wb3J0IHsgY2hldnJvbkRvd25PdXRsaW5lLCBjaGV2cm9uVXBPdXRsaW5lIH0gZnJvbSAnaW9uaWNvbnMvaWNvbnMnO1xuXG4vKipcbiAqIEBkZXNjcmlwdGlvbiBBZHZhbmNlZCBmaWx0ZXIgY29tcG9uZW50IGZvciBjcmVhdGluZyBkeW5hbWljIHNlYXJjaCBmaWx0ZXJzIHdpdGggc3RlcC1ieS1zdGVwIGNvbnN0cnVjdGlvbi5cbiAqIEBzdW1tYXJ5IFRoaXMgY29tcG9uZW50IHByb3ZpZGVzIGEgY29tcHJlaGVuc2l2ZSBmaWx0ZXJpbmcgaW50ZXJmYWNlIHRoYXQgYWxsb3dzIHVzZXJzIHRvIGJ1aWxkXG4gKiBjb21wbGV4IHNlYXJjaCBjcml0ZXJpYSB1c2luZyBhIHRocmVlLXN0ZXAgYXBwcm9hY2g6IHNlbGVjdCBpbmRleCDihpIgc2VsZWN0IGNvbmRpdGlvbiDihpIgZW50ZXIgdmFsdWUuXG4gKiBJdCBzdXBwb3J0cyBmaWx0ZXJpbmcgYnkgbXVsdGlwbGUgZmllbGQgaW5kZXhlcywgY29tcGFyaXNvbiBjb25kaXRpb25zLCBhbmQgdmFsdWVzLCBkaXNwbGF5aW5nXG4gKiBzZWxlY3RlZCBmaWx0ZXJzIGFzIHJlbW92YWJsZSBjaGlwcy4gVGhlIGNvbXBvbmVudCBpcyByZXNwb25zaXZlIGFuZCBpbmNsdWRlcyBhdXRvLXN1Z2dlc3Rpb25zXG4gKiB3aXRoIGtleWJvYXJkIG5hdmlnYXRpb24gc3VwcG9ydC5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgaHRtbFxuICogPG5neC1kZWNhZi1maWx0ZXJcbiAqICAgW2luZGV4ZXNdPVwiWyduYW1lJywgJ2VtYWlsJywgJ2RlcGFydG1lbnQnLCAnc3RhdHVzJ11cIlxuICogICBbY29uZGl0aW9uc109XCJbJ0VxdWFsJywgJ0NvbnRhaW5zJywgJ0dyZWF0ZXIgVGhhbicsICdMZXNzIFRoYW4nXVwiXG4gKiAgIFtzb3J0XT1cIlsnY3JlYXRlZEF0JywgJ3VwZGF0ZWRBdCddXCJcbiAqICAgW2Rpc2FibGVTb3J0XT1cImZhbHNlXCJcbiAqICAgKGZpbHRlckV2ZW50KT1cIm9uRmlsdGVyc0NoYW5nZWQoJGV2ZW50KVwiPlxuICogPC9uZ3gtZGVjYWYtZmlsdGVyPlxuICogYGBgXG4gKlxuICogQG1lcm1haWRcbiAqIHNlcXVlbmNlRGlhZ3JhbVxuICogICBwYXJ0aWNpcGFudCBVIGFzIFVzZXJcbiAqICAgcGFydGljaXBhbnQgRiBhcyBGaWx0ZXJDb21wb25lbnRcbiAqICAgcGFydGljaXBhbnQgUCBhcyBQYXJlbnQgQ29tcG9uZW50XG4gKlxuICogICBVLT4+RjogRm9jdXMgaW5wdXQgZmllbGRcbiAqICAgRi0+PkY6IGhhbmRsZUZvY3VzKCkgLSBTaG93IGF2YWlsYWJsZSBpbmRleGVzXG4gKiAgIFUtPj5GOiBTZWxlY3QgaW5kZXggKGUuZy4sIFwibmFtZVwiKVxuICogICBGLT4+RjogYWRkRmlsdGVyKCkgLSBTdGVwIDEgY29tcGxldGVkXG4gKiAgIEYtPj5GOiBTaG93IGF2YWlsYWJsZSBjb25kaXRpb25zXG4gKiAgIFUtPj5GOiBTZWxlY3QgY29uZGl0aW9uIChlLmcuLCBcIkNvbnRhaW5zXCIpXG4gKiAgIEYtPj5GOiBhZGRGaWx0ZXIoKSAtIFN0ZXAgMiBjb21wbGV0ZWRcbiAqICAgRi0+PkY6IFNob3cgdmFsdWUgaW5wdXQgcHJvbXB0XG4gKiAgIFUtPj5GOiBFbnRlciB2YWx1ZSBhbmQgcHJlc3MgRW50ZXJcbiAqICAgRi0+PkY6IGFkZEZpbHRlcigpIC0gU3RlcCAzIGNvbXBsZXRlZFxuICogICBGLT4+RjogQ3JlYXRlIGNvbXBsZXRlIGZpbHRlciBvYmplY3RcbiAqICAgRi0+PlA6IEVtaXQgZmlsdGVyRXZlbnQgd2l0aCBuZXcgZmlsdGVyIGFycmF5XG4gKiAgIEYtPj5GOiBSZXNldCB0byBzdGVwIDEgZm9yIG5leHQgZmlsdGVyXG4gKlxuICogQG1lbWJlck9mIEZvckFuZ3VsYXJNb2R1bGVcbiAqL1xuQER5bmFtaWMoKVxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbmd4LWRlY2FmLWZpbHRlcicsXG4gIHRlbXBsYXRlVXJsOiAnLi9maWx0ZXIuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9maWx0ZXIuY29tcG9uZW50LnNjc3MnXSxcbiAgaW1wb3J0czogW1xuICAgIEZvckFuZ3VsYXJNb2R1bGUsXG4gICAgSW9uTGFiZWwsXG4gICAgSW9uSXRlbSxcbiAgICBJb25DaGlwLFxuICAgIElvbkljb24sXG4gICAgSW9uU2VsZWN0LFxuICAgIElvbkljb24sXG4gICAgU2VhcmNoYmFyQ29tcG9uZW50XG4gIF0sXG4gIHN0YW5kYWxvbmU6IHRydWUsXG59KVxuZXhwb3J0IGNsYXNzIEZpbHRlckNvbXBvbmVudCBleHRlbmRzIE5neEJhc2VDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBSZWZlcmVuY2UgdG8gdGhlIGRyb3Bkb3duIG9wdGlvbnMgY29udGFpbmVyIGVsZW1lbnQuXG4gICAqIEBzdW1tYXJ5IFZpZXdDaGlsZCByZWZlcmVuY2UgdXNlZCB0byBhY2Nlc3MgYW5kIG1hbmlwdWxhdGUgdGhlIGRyb3Bkb3duIG9wdGlvbnMgZWxlbWVudFxuICAgKiBmb3IgaGlnaGxpZ2h0aW5nIGZpbHRlcmVkIGl0ZW1zIGFuZCBtYW5hZ2luZyB2aXN1YWwgZmVlZGJhY2sgZHVyaW5nIG9wdGlvbiBzZWxlY3Rpb24uXG4gICAqIFRoaXMgZWxlbWVudCBjb250YWlucyB0aGUgZmlsdGVyYWJsZSBzdWdnZXN0aW9ucyB0aGF0IHVzZXJzIGNhbiBpbnRlcmFjdCB3aXRoLlxuICAgKlxuICAgKiBAdHlwZSB7RWxlbWVudFJlZn1cbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgQFZpZXdDaGlsZCgnb3B0aW9uc0ZpbHRlckVsZW1lbnQnLCB7IHJlYWQ6IEVsZW1lbnRSZWYsIHN0YXRpYzogZmFsc2UgfSlcbiAgb3B0aW9uc0ZpbHRlckVsZW1lbnQhOiBFbGVtZW50UmVmO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQXZhaWxhYmxlIGZpZWxkIGluZGV4ZXMgZm9yIGZpbHRlcmluZyBvcGVyYXRpb25zLlxuICAgKiBAc3VtbWFyeSBEZWZpbmVzIHRoZSBsaXN0IG9mIGZpZWxkIG5hbWVzIHRoYXQgdXNlcnMgY2FuIGZpbHRlciBieS4gVGhlc2UgcmVwcmVzZW50XG4gICAqIHRoZSBkYXRhIHByb3BlcnRpZXMgYXZhaWxhYmxlIGZvciBmaWx0ZXJpbmcgb3BlcmF0aW9ucy4gRWFjaCBpbmRleCBjb3JyZXNwb25kcyB0b1xuICAgKiBhIGZpZWxkIGluIHRoZSBkYXRhIG1vZGVsIHRoYXQgc3VwcG9ydHMgY29tcGFyaXNvbiBvcGVyYXRpb25zLlxuICAgKlxuICAgKiBAdHlwZSB7c3RyaW5nW119XG4gICAqIEBkZWZhdWx0IFtdXG4gICAqIEBtZW1iZXJPZiBGaWx0ZXJDb21wb25lbnRcbiAgICovXG4gIEBJbnB1dCgpXG4gIGluZGV4ZXM6IHN0cmluZ1tdID0gW107XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBBdmFpbGFibGUgY29tcGFyaXNvbiBjb25kaXRpb25zIGZvciBmaWx0ZXJzLlxuICAgKiBAc3VtbWFyeSBEZWZpbmVzIHRoZSBsaXN0IG9mIGNvbXBhcmlzb24gb3BlcmF0b3JzIHRoYXQgY2FuIGJlIHVzZWQgd2hlbiBjcmVhdGluZyBmaWx0ZXJzLlxuICAgKiBUaGVzZSBjb25kaXRpb25zIGRldGVybWluZSBob3cgdGhlIGZpbHRlciB2YWx1ZSBpcyBjb21wYXJlZCBhZ2FpbnN0IHRoZSBmaWVsZCB2YWx1ZS5cbiAgICogQ29tbW9uIGNvbmRpdGlvbnMgaW5jbHVkZSBlcXVhbGl0eSwgY29udGFpbm1lbnQsIGFuZCBudW1lcmljYWwgY29tcGFyaXNvbiBvcGVyYXRpb25zLlxuICAgKlxuICAgKiBAdHlwZSB7c3RyaW5nW119XG4gICAqIEBkZWZhdWx0IFtdXG4gICAqIEBtZW1iZXJPZiBGaWx0ZXJDb21wb25lbnRcbiAgICovXG4gIEBJbnB1dCgpXG4gIGNvbmRpdGlvbnM6IHN0cmluZ1tdID0gWydFcXVhbCcsICdDb250YWlucycsICdOb3QgQ29udGFpbnMnLCAnR3JlYXRlciBUaGFuJywgJ0xlc3MgVGhhbicsICdOb3QgRXF1YWwnXTtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEF2YWlsYWJsZSBzb3J0aW5nIG9wdGlvbnMgZm9yIHRoZSBmaWx0ZXJlZCBkYXRhLlxuICAgKiBAc3VtbWFyeSBEZWZpbmVzIHRoZSBsaXN0IG9mIGZpZWxkIG5hbWVzIHRoYXQgY2FuIGJlIHVzZWQgZm9yIHNvcnRpbmcgdGhlIGZpbHRlcmVkIHJlc3VsdHMuXG4gICAqIFdoZW4gZGlzYWJsZVNvcnQgaXMgZmFsc2UsIHRoaXMgYXJyYXkgaXMgYXV0b21hdGljYWxseSBtZXJnZWQgd2l0aCB0aGUgaW5kZXhlcyBhcnJheVxuICAgKiB0byBwcm92aWRlIGNvbXByZWhlbnNpdmUgc29ydGluZyBjYXBhYmlsaXRpZXMuXG4gICAqXG4gICAqIEB0eXBlIHtzdHJpbmdbXX1cbiAgICogQGRlZmF1bHQgW11cbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgQElucHV0KClcbiAgc29ydEJ5OiBzdHJpbmdbXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ29udHJvbHMgd2hldGhlciBzb3J0aW5nIGZ1bmN0aW9uYWxpdHkgaXMgZGlzYWJsZWQuXG4gICAqIEBzdW1tYXJ5IFdoZW4gc2V0IHRvIHRydWUsIHByZXZlbnRzIHRoZSBhdXRvbWF0aWMgbWVyZ2luZyBvZiBzb3J0IGFuZCBpbmRleGVzIGFycmF5cyxcbiAgICogZWZmZWN0aXZlbHkgZGlzYWJsaW5nIHNvcnRpbmcgY2FwYWJpbGl0aWVzLiBUaGlzIGlzIHVzZWZ1bCB3aGVuIHlvdSB3YW50IHRvIHByb3ZpZGVcbiAgICogZmlsdGVyaW5nIHdpdGhvdXQgc29ydGluZyBvcHRpb25zLlxuICAgKlxuICAgKiBAdHlwZSB7Ym9vbGVhbn1cbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgQElucHV0KClcbiAgZGlzYWJsZVNvcnQ6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEN1cnJlbnQgd2luZG93IHdpZHRoIGZvciByZXNwb25zaXZlIGJlaGF2aW9yLlxuICAgKiBAc3VtbWFyeSBTdG9yZXMgdGhlIGN1cnJlbnQgYnJvd3NlciB3aW5kb3cgd2lkdGggaW4gcGl4ZWxzLiBUaGlzIHZhbHVlIGlzIHVwZGF0ZWRcbiAgICogb24gd2luZG93IHJlc2l6ZSBldmVudHMgdG8gZW5hYmxlIHJlc3BvbnNpdmUgZmlsdGVyaW5nIGJlaGF2aW9yIGFuZCBsYXlvdXQgYWRqdXN0bWVudHNcbiAgICogYmFzZWQgb24gYXZhaWxhYmxlIHNjcmVlbiBzcGFjZS5cbiAgICpcbiAgICogQHR5cGUge251bWJlcn1cbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgd2luZG93V2lkdGghOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBBdmFpbGFibGUgb3B0aW9ucyBmb3IgdGhlIGN1cnJlbnQgZmlsdGVyIHN0ZXAuXG4gICAqIEBzdW1tYXJ5IENvbnRhaW5zIHRoZSBsaXN0IG9mIG9wdGlvbnMgYXZhaWxhYmxlIGZvciBzZWxlY3Rpb24gaW4gdGhlIGN1cnJlbnQgc3RlcC5cbiAgICogVGhpcyBhcnJheSBjaGFuZ2VzIGR5bmFtaWNhbGx5IGJhc2VkIG9uIHRoZSBjdXJyZW50IHN0ZXA6IGluZGV4ZXMg4oaSIGNvbmRpdGlvbnMg4oaSIGVtcHR5IGZvciB2YWx1ZSBpbnB1dC5cbiAgICpcbiAgICogQHR5cGUge3N0cmluZ1tdfVxuICAgKiBAZGVmYXVsdCBbXVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBvcHRpb25zOiBzdHJpbmdbXSA9IFtdO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gRmlsdGVyZWQgb3B0aW9ucyBiYXNlZCBvbiB1c2VyIGlucHV0LlxuICAgKiBAc3VtbWFyeSBDb250YWlucyB0aGUgc3Vic2V0IG9mIG9wdGlvbnMgdGhhdCBtYXRjaCB0aGUgY3VycmVudCB1c2VyIGlucHV0IGZvciByZWFsLXRpbWVcbiAgICogZmlsdGVyaW5nLiBUaGlzIGFycmF5IGlzIHVwZGF0ZWQgYXMgdGhlIHVzZXIgdHlwZXMgdG8gc2hvdyBvbmx5IHJlbGV2YW50IHN1Z2dlc3Rpb25zXG4gICAqIGluIHRoZSBkcm9wZG93biBtZW51LlxuICAgKlxuICAgKiBAdHlwZSB7c3RyaW5nW119XG4gICAqIEBkZWZhdWx0IFtdXG4gICAqIEBtZW1iZXJPZiBGaWx0ZXJDb21wb25lbnRcbiAgICovXG4gIGZpbHRlcmVkT3B0aW9uczogc3RyaW5nW10gPSBbXTtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENvbXBsZXRlIGZpbHRlciBvYmplY3RzIGNyZWF0ZWQgYnkgdGhlIHVzZXIuXG4gICAqIEBzdW1tYXJ5IEFycmF5IG9mIGNvbXBsZXRlIGZpbHRlciBvYmplY3RzLCBlYWNoIGNvbnRhaW5pbmcgaW5kZXgsIGNvbmRpdGlvbiwgYW5kIHZhbHVlIHByb3BlcnRpZXMuXG4gICAqIFRoZXNlIHJlcHJlc2VudCB0aGUgYWN0aXZlIGZpbHRlcnMgdGhhdCBjYW4gYmUgYXBwbGllZCB0byBkYXRhIG9wZXJhdGlvbnMuXG4gICAqXG4gICAqIEB0eXBlIHtLZXlWYWx1ZVtdfVxuICAgKiBAZGVmYXVsdCBbXVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBmaWx0ZXJWYWx1ZTogSUZpbHRlclF1ZXJ5SXRlbVtdID0gW107XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDdXJyZW50IGZpbHRlciBiZWluZyBjb25zdHJ1Y3RlZC5cbiAgICogQHN1bW1hcnkgVGVtcG9yYXJ5IG9iamVjdCB0aGF0IGFjY3VtdWxhdGVzIGZpbHRlciBwcm9wZXJ0aWVzIChpbmRleCwgY29uZGl0aW9uLCB2YWx1ZSlcbiAgICogZHVyaW5nIHRoZSB0aHJlZS1zdGVwIGZpbHRlciBjcmVhdGlvbiBwcm9jZXNzLiBHZXRzIGFkZGVkIHRvIGZpbHRlclZhbHVlIHdoZW4gY29tcGxldGUuXG4gICAqXG4gICAqIEB0eXBlIHtLZXlWYWx1ZX1cbiAgICogQGRlZmF1bHQge31cbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgbGFzdEZpbHRlcjogSUZpbHRlclF1ZXJ5SXRlbSA9IHt9O1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ3VycmVudCBzdGVwIGluIHRoZSBmaWx0ZXIgY3JlYXRpb24gcHJvY2Vzcy5cbiAgICogQHN1bW1hcnkgVHJhY2tzIHRoZSBjdXJyZW50IHN0ZXAgb2YgZmlsdGVyIGNyZWF0aW9uOiAxID0gaW5kZXggc2VsZWN0aW9uLCAyID0gY29uZGl0aW9uIHNlbGVjdGlvbixcbiAgICogMyA9IHZhbHVlIGlucHV0LiBBdXRvbWF0aWNhbGx5IHJlc2V0cyB0byAxIGFmdGVyIGNvbXBsZXRpbmcgYSBmaWx0ZXIuXG4gICAqXG4gICAqIEB0eXBlIHtudW1iZXJ9XG4gICAqIEBkZWZhdWx0IDFcbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgc3RlcDogbnVtYmVyID0gMTtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENvbnRyb2xzIGRyb3Bkb3duIHZpc2liaWxpdHkgc3RhdGUuXG4gICAqIEBzdW1tYXJ5IEJvb2xlYW4gZmxhZyB0aGF0IGRldGVybWluZXMgd2hldGhlciB0aGUgb3B0aW9ucyBkcm9wZG93biBpcyBjdXJyZW50bHkgdmlzaWJsZS5cbiAgICogVXNlZCB0byBtYW5hZ2UgdGhlIGRyb3Bkb3duJ3Mgb3Blbi9jbG9zZSBzdGF0ZSBhbmQgY29vcmRpbmF0ZSB3aXRoIGZvY3VzL2JsdXIgZXZlbnRzLlxuICAgKlxuICAgKiBAdHlwZSB7Ym9vbGVhbn1cbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgZHJvcGRvd25PcGVuOiBib29sZWFuID0gZmFsc2U7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDdXJyZW50IGlucHV0IGZpZWxkIHZhbHVlLlxuICAgKiBAc3VtbWFyeSBTdG9yZXMgdGhlIGN1cnJlbnQgdGV4dCBpbnB1dCB2YWx1ZSB0aGF0IHRoZSB1c2VyIGlzIHR5cGluZy4gVGhpcyB2YWx1ZSBpc1xuICAgKiBib3VuZCB0byB0aGUgaW5wdXQgZmllbGQgYW5kIGlzIGNsZWFyZWQgYWZ0ZXIgZWFjaCBzdWNjZXNzZnVsIGZpbHRlciBzdGVwIGNvbXBsZXRpb24uXG4gICAqXG4gICAqIEB0eXBlIHtzdHJpbmd9XG4gICAqIEBkZWZhdWx0ICcnXG4gICAqIEBtZW1iZXJPZiBGaWx0ZXJDb21wb25lbnRcbiAgICovXG4gIHZhbHVlOiBzdHJpbmcgPSAnJztcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEN1cnJlbnQgc29ydGluZyBmaWVsZCB2YWx1ZS5cbiAgICogQHN1bW1hcnkgU3RvcmVzIHRoZSBmaWVsZCBuYW1lIGN1cnJlbnRseSBzZWxlY3RlZCBmb3Igc29ydGluZyBvcGVyYXRpb25zLlxuICAgKiBUaGlzIHZhbHVlIGRldGVybWluZXMgd2hpY2ggZmllbGQgaXMgdXNlZCB0byBvcmRlciB0aGUgZmlsdGVyZWQgcmVzdWx0cy5cbiAgICogRGVmYXVsdHMgdG8gJ2lkJyBhbmQgY2FuIGJlIGNoYW5nZWQgdGhyb3VnaCB0aGUgc29ydCBkcm9wZG93biBzZWxlY3Rpb24uXG4gICAqXG4gICAqIEB0eXBlIHtzdHJpbmd9XG4gICAqIEBkZWZhdWx0ICdpZCdcbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgc29ydFZhbHVlOiBzdHJpbmcgPSAnaWQnO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ3VycmVudCBzb3J0aW5nIGRpcmVjdGlvbi5cbiAgICogQHN1bW1hcnkgRGVmaW5lcyB0aGUgZGlyZWN0aW9uIG9mIHRoZSBzb3J0IG9wZXJhdGlvbiAtIGFzY2VuZGluZyBvciBkZXNjZW5kaW5nLlxuICAgKiBUaGlzIHZhbHVlIHdvcmtzIGluIGNvbmp1bmN0aW9uIHdpdGggc29ydFZhbHVlIHRvIGRldGVybWluZSB0aGUgY29tcGxldGVcbiAgICogc29ydGluZyBjb25maWd1cmF0aW9uIGZvciBmaWx0ZXJlZCByZXN1bHRzLlxuICAgKlxuICAgKiBAdHlwZSB7T3JkZXJEaXJlY3Rpb259XG4gICAqIEBkZWZhdWx0IE9yZGVyRGlyZWN0aW9uLkRTQ1xuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBzb3J0RGlyZWN0aW9uOiBPcmRlckRpcmVjdGlvbiA9IE9yZGVyRGlyZWN0aW9uLkRTQztcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFN1YnNjcmlwdGlvbiBmb3Igd2luZG93IHJlc2l6ZSBldmVudHMuXG4gICAqIEBzdW1tYXJ5IFJ4SlMgc3Vic2NyaXB0aW9uIHRoYXQgbGlzdGVucyBmb3Igd2luZG93IHJlc2l6ZSBldmVudHMgd2l0aCBkZWJvdW5jaW5nXG4gICAqIHRvIHVwZGF0ZSB0aGUgd2luZG93V2lkdGggcHJvcGVydHkuIFRoaXMgZW5hYmxlcyByZXNwb25zaXZlIGJlaGF2aW9yIGFuZCBwcmV2ZW50c1xuICAgKiBleGNlc3NpdmUgdXBkYXRlcyBkdXJpbmcgcmVzaXplIG9wZXJhdGlvbnMuXG4gICAqXG4gICAqIEB0eXBlIHtTdWJzY3JpcHRpb259XG4gICAqIEBtZW1iZXJPZiBGaWx0ZXJDb21wb25lbnRcbiAgICovXG4gIHdpbmRvd1Jlc2l6ZVN1YnNjcmlwdGlvbiE6IFN1YnNjcmlwdGlvbjtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEV2ZW50IGVtaXR0ZXIgZm9yIGZpbHRlciBjaGFuZ2VzLlxuICAgKiBAc3VtbWFyeSBFbWl0cyBmaWx0ZXIgZXZlbnRzIHdoZW4gdGhlIHVzZXIgY3JlYXRlcywgbW9kaWZpZXMsIG9yIGNsZWFycyBmaWx0ZXJzLlxuICAgKiBUaGUgZW1pdHRlZCB2YWx1ZSBjb250YWlucyBhbiBhcnJheSBvZiBjb21wbGV0ZSBmaWx0ZXIgb2JqZWN0cyBvciB1bmRlZmluZWQgd2hlblxuICAgKiBmaWx0ZXJzIGFyZSBjbGVhcmVkLiBQYXJlbnQgY29tcG9uZW50cyBsaXN0ZW4gdG8gdGhpcyBldmVudCB0byB1cGRhdGUgdGhlaXIgZGF0YSBkaXNwbGF5LlxuICAgKlxuICAgKiBAdHlwZSB7RXZlbnRFbWl0dGVyPEtleVZhbHVlW10gfCB1bmRlZmluZWQ+fVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBAT3V0cHV0KClcbiAgZmlsdGVyRXZlbnQ6IEV2ZW50RW1pdHRlcjxJRmlsdGVyUXVlcnkgfCB1bmRlZmluZWQ+ID0gbmV3IEV2ZW50RW1pdHRlcjxJRmlsdGVyUXVlcnkgfCB1bmRlZmluZWQ+KCk7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBFdmVudCBlbWl0dGVyIGZvciBzZWFyY2ggZXZlbnRzLlxuICAgKiBAc3VtbWFyeSBFbWl0cyBzZWFyY2ggZXZlbnRzIHdoZW4gdGhlIHVzZXIgaW50ZXJhY3RzIHdpdGggdGhlIHNlYXJjaGJhci5cbiAgICogQHR5cGUge0V2ZW50RW1pdHRlcjxzdHJpbmc+fVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBAT3V0cHV0KClcbiAgc2VhcmNoRXZlbnQ6IEV2ZW50RW1pdHRlcjxzdHJpbmc+ID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG5cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENvbnN0cnVjdG9yIGZvciBGaWx0ZXJDb21wb25lbnQuXG4gICAqIEBzdW1tYXJ5IEluaXRpYWxpemVzIGEgbmV3IGluc3RhbmNlIG9mIHRoZSBGaWx0ZXJDb21wb25lbnQuXG4gICAqIENhbGxzIHRoZSBwYXJlbnQgY29uc3RydWN0b3Igd2l0aCB0aGUgY29tcG9uZW50IG5hbWUgdG8gZXN0YWJsaXNoIGJhc2UgbG9jYWxlIHN0cmluZyBnZW5lcmF0aW9uXG4gICAqIGFuZCBpbnRlcm5hdGlvbmFsaXphdGlvbiBzdXBwb3J0LlxuICAgKlxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBjb25zdHJ1Y3RvcigpIHtcbiAgICBzdXBlcihcIkZpbHRlckNvbXBvbmVudFwiKTtcbiAgICBhZGRJY29ucyh7Y2hldnJvbkRvd25PdXRsaW5lLCBjaGV2cm9uVXBPdXRsaW5lfSk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEluaXRpYWxpemVzIHRoZSBjb21wb25lbnQgYWZ0ZXIgQW5ndWxhciBmaXJzdCBkaXNwbGF5cyB0aGUgZGF0YS1ib3VuZCBwcm9wZXJ0aWVzLlxuICAgKiBAc3VtbWFyeSBTZXRzIHVwIHRoZSBjb21wb25lbnQgYnkgaW5pdGlhbGl6aW5nIHdpbmRvdyB3aWR0aCB0cmFja2luZywgc2V0dGluZyB1cCByZXNpemUgZXZlbnRcbiAgICogc3Vic2NyaXB0aW9ucyB3aXRoIGRlYm91bmNpbmcsIGNvbmZpZ3VyaW5nIHNvcnRpbmcgb3B0aW9ucywgYW5kIGNhbGxpbmcgdGhlIGJhc2UgaW5pdGlhbGl6YXRpb24uXG4gICAqIFRoaXMgbWV0aG9kIHByZXBhcmVzIHRoZSBjb21wb25lbnQgZm9yIHVzZXIgaW50ZXJhY3Rpb24gYW5kIHJlc3BvbnNpdmUgYmVoYXZpb3IuXG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEEgYXMgQW5ndWxhciBMaWZlY3ljbGVcbiAgICogICBwYXJ0aWNpcGFudCBGIGFzIEZpbHRlckNvbXBvbmVudFxuICAgKiAgIHBhcnRpY2lwYW50IFcgYXMgV2luZG93XG4gICAqICAgcGFydGljaXBhbnQgUiBhcyBSeEpTXG4gICAqXG4gICAqICAgQS0+PkY6IG5nT25Jbml0KClcbiAgICogICBGLT4+VzogZ2V0V2luZG93V2lkdGgoKVxuICAgKiAgIFctLT4+RjogUmV0dXJuIGN1cnJlbnQgd2lkdGhcbiAgICogICBGLT4+UjogU2V0dXAgcmVzaXplIHN1YnNjcmlwdGlvbiB3aXRoIGRlYm91bmNlXG4gICAqICAgUi0tPj5GOiBTdWJzY3JpcHRpb24gY3JlYXRlZFxuICAgKiAgIGFsdCBkaXNhYmxlU29ydCBpcyBmYWxzZVxuICAgKiAgICAgRi0+PkY6IE1lcmdlIHNvcnQgYW5kIGluZGV4ZXMgYXJyYXlzXG4gICAqICAgZW5kXG4gICAqICAgRi0+PkY6IENhbGwgaW5pdGlhbGl6ZSgpXG4gICAqXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLndpbmRvd1dpZHRoID0gZ2V0V2luZG93V2lkdGgoKSBhcyBudW1iZXI7XG4gICAgdGhpcy53aW5kb3dSZXNpemVTdWJzY3JpcHRpb24gPSBmcm9tRXZlbnQod2luZG93LCAncmVzaXplJylcbiAgICAucGlwZShkZWJvdW5jZVRpbWUoMzAwKSlcbiAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgdGhpcy53aW5kb3dXaWR0aCA9IGdldFdpbmRvd1dpZHRoKCkgYXMgbnVtYmVyO1xuICAgIH0pO1xuXG4gICAgdGhpcy5nZXRJbmRleGVzKCk7XG4gICAgdGhpcy5pbml0aWFsaXplKCk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFJldHJpZXZlcyBhbmQgY29uZmlndXJlcyBhdmFpbGFibGUgaW5kZXhlcyBmb3IgZmlsdGVyaW5nIGFuZCBzb3J0aW5nLlxuICAgKiBAc3VtbWFyeSBFeHRyYWN0cyBmaWVsZCBpbmRleGVzIGZyb20gdGhlIG1vZGVsIGlmIGF2YWlsYWJsZSBhbmQgbWVyZ2VzIHRoZW0gd2l0aFxuICAgKiBzb3J0aW5nIG9wdGlvbnMgd2hlbiBzb3J0aW5nIGlzIGVuYWJsZWQuIFRoaXMgbWV0aG9kIHNldHMgdXAgdGhlIGF2YWlsYWJsZSBmaWVsZFxuICAgKiBvcHRpb25zIGZvciBib3RoIGZpbHRlcmluZyBhbmQgc29ydGluZyBvcGVyYXRpb25zIGJhc2VkIG9uIHRoZSBtb2RlbCBzdHJ1Y3R1cmUuXG4gICAqXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBnZXRJbmRleGVzKCk6IHZvaWQge1xuICAgIGlmKHRoaXMubW9kZWwpXG4gICAgICB0aGlzLmluZGV4ZXMgPSBPYmplY3Qua2V5cyhSZXBvc2l0b3J5LmluZGV4ZXModGhpcy5tb2RlbCBhcyBNb2RlbCkgfHwge30pO1xuICAgIGlmKCF0aGlzLmRpc2FibGVTb3J0KVxuICAgICAgdGhpcy5zb3J0QnkgPSBbLi4uIHRoaXMuc29ydEJ5LCAuLi50aGlzLmluZGV4ZXNdO1xuICB9XG5cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENsZWFudXAgbWV0aG9kIGNhbGxlZCB3aGVuIHRoZSBjb21wb25lbnQgaXMgZGVzdHJveWVkLlxuICAgKiBAc3VtbWFyeSBVbnN1YnNjcmliZXMgZnJvbSB3aW5kb3cgcmVzaXplIGV2ZW50cyB0byBwcmV2ZW50IG1lbW9yeSBsZWFrcy5cbiAgICogVGhpcyBpcyBlc3NlbnRpYWwgZm9yIHByb3BlciBjbGVhbnVwIG9mIFJ4SlMgc3Vic2NyaXB0aW9ucyB3aGVuIHRoZSBjb21wb25lbnRcbiAgICogaXMgcmVtb3ZlZCBmcm9tIHRoZSBET00uXG4gICAqXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLndpbmRvd1Jlc2l6ZVN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgIHRoaXMuY2xlYXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSGFuZGxlcyBpbnB1dCBldmVudHMgZnJvbSB0aGUgdGV4dCBmaWVsZC5cbiAgICogQHN1bW1hcnkgUHJvY2Vzc2VzIHVzZXIgaW5wdXQgYW5kIGZpbHRlcnMgdGhlIGF2YWlsYWJsZSBvcHRpb25zIGJhc2VkIG9uIHRoZSB0eXBlZCB2YWx1ZS5cbiAgICogVGhpcyBtZXRob2QgcHJvdmlkZXMgcmVhbC10aW1lIGZpbHRlcmluZyBvZiBzdWdnZXN0aW9ucyBhcyB0aGUgdXNlciB0eXBlcyBpbiB0aGUgaW5wdXQgZmllbGQuXG4gICAqXG4gICAqIEBwYXJhbSB7SW5wdXRFdmVudH0gZXZlbnQgLSBUaGUgaW5wdXQgZXZlbnQgY29udGFpbmluZyB0aGUgbmV3IHZhbHVlXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBoYW5kbGVJbnB1dChldmVudDogSW5wdXRFdmVudCk6IHZvaWQge1xuICAgIGNvbnN0IHt2YWx1ZX0gPSBldmVudC50YXJnZXQgYXMgSFRNTElucHV0RWxlbWVudDtcbiAgICAgIHRoaXMuZmlsdGVyZWRPcHRpb25zID0gdGhpcy5maWx0ZXJPcHRpb25zKHZhbHVlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSGFuZGxlcyBmb2N1cyBldmVudHMgb24gdGhlIGlucHV0IGZpZWxkLlxuICAgKiBAc3VtbWFyeSBTZXRzIHVwIHRoZSBhdmFpbGFibGUgb3B0aW9ucyB3aGVuIHRoZSBpbnB1dCBmaWVsZCByZWNlaXZlcyBmb2N1cyBhbmQgb3BlbnMgdGhlIGRyb3Bkb3duLlxuICAgKiBJZiBubyBvcHRpb25zIGFyZSBwcm92aWRlZCwgYXV0b21hdGljYWxseSBkZXRlcm1pbmVzIHRoZSBhcHByb3ByaWF0ZSBvcHRpb25zIGJhc2VkIG9uIGN1cnJlbnQgc3RlcC5cbiAgICogVGhpcyBtZXRob2QgaW5pdGlhbGl6ZXMgdGhlIGRyb3Bkb3duIHdpdGggY29udGV4dHVhbGx5IHJlbGV2YW50IHN1Z2dlc3Rpb25zLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBvcHRpb25zIC0gT3B0aW9uYWwgYXJyYXkgb2Ygb3B0aW9ucyB0byBkaXNwbGF5XG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBoYW5kbGVGb2N1cyhvcHRpb25zOiBzdHJpbmdbXSAgPSBbXSk6IHZvaWQge1xuICAgIGlmKCFvcHRpb25zLmxlbmd0aClcbiAgICAgb3B0aW9ucyA9IHRoaXMuZ2V0T3B0aW9ucygpO1xuICAgIHRoaXMuZmlsdGVyZWRPcHRpb25zID0gdGhpcy5vcHRpb25zID0gb3B0aW9ucztcbiAgICB0aGlzLmRyb3Bkb3duT3BlbiA9IHRydWU7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEhhbmRsZXMgYmx1ciBldmVudHMgb24gdGhlIGlucHV0IGZpZWxkIHdpdGggZGVsYXllZCBjbG9zaW5nLlxuICAgKiBAc3VtbWFyeSBNYW5hZ2VzIHRoZSBkcm9wZG93biBjbG9zaW5nIGJlaGF2aW9yIHdpdGggYSBkZWxheSB0byBhbGxvdyBmb3Igb3B0aW9uIHNlbGVjdGlvbi5cbiAgICogVXNlcyBhIHR3by1waGFzZSBhcHByb2FjaCB0byBwcmV2ZW50IHByZW1hdHVyZSBjbG9zaW5nIHdoZW4gdXNlcnMgY2xpY2sgb24gZHJvcGRvd24gb3B0aW9ucy5cbiAgICpcbiAgICogQHBhcmFtIHtib29sZWFufSBjbG9zZSAtIEludGVybmFsIGZsYWcgdG8gY29udHJvbCB0aGUgY2xvc2luZyBwaGFzZVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgaGFuZGxlQmx1cihjbG9zZTogYm9vbGVhbiA9IGZhbHNlKTogdm9pZCB7XG4gICAgaWYoIWNsb3NlKSB7XG4gICAgICB0aGlzLmRyb3Bkb3duT3BlbiA9IGZhbHNlO1xuICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIHRoaXMuaGFuZGxlQmx1cih0cnVlKTtcbiAgICAgIH0sIDEwMCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmKCF0aGlzLmRyb3Bkb3duT3BlbiAmJiB0aGlzLm9wdGlvbnMubGVuZ3RoKSB7XG4gICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgIHRoaXMub3B0aW9ucyA9IFtdO1xuICAgICAgICAgIHRoaXMuZHJvcGRvd25PcGVuID0gZmFsc2U7XG4gICAgICAgIH0sIDUwKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIERldGVybWluZXMgdGhlIGFwcHJvcHJpYXRlIG9wdGlvbnMgYmFzZWQgb24gdGhlIGN1cnJlbnQgZmlsdGVyIHN0ZXAuXG4gICAqIEBzdW1tYXJ5IFJldHVybnMgdGhlIGNvbnRleHR1YWxseSByZWxldmFudCBvcHRpb25zIGZvciB0aGUgY3VycmVudCBzdGVwIGluIHRoZSBmaWx0ZXIgY3JlYXRpb24gcHJvY2Vzcy5cbiAgICogU3RlcCAxIHNob3dzIGluZGV4ZXMsIFN0ZXAgMiBzaG93cyBjb25kaXRpb25zLCBTdGVwIDMgc2hvd3Mgbm8gb3B0aW9ucyAodmFsdWUgaW5wdXQpLlxuICAgKlxuICAgKiBAcmV0dXJucyB7c3RyaW5nW119IEFycmF5IG9mIG9wdGlvbnMgYXBwcm9wcmlhdGUgZm9yIHRoZSBjdXJyZW50IHN0ZXBcbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgZ2V0T3B0aW9ucygpOiBzdHJpbmdbXSB7XG4gICBzd2l0Y2ggKHRoaXMuc3RlcCkge1xuICAgICAgY2FzZSAxOlxuICAgICAgICB0aGlzLm9wdGlvbnMgPSB0aGlzLmluZGV4ZXM7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAyOlxuICAgICAgICB0aGlzLm9wdGlvbnMgPSB0aGlzLmNvbmRpdGlvbnM7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAzOlxuICAgICAgICB0aGlzLm9wdGlvbnMgPSBbXTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLm9wdGlvbnNcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQWRkcyBhIGZpbHRlciBzdGVwIG9yIGNvbXBsZXRlcyBmaWx0ZXIgY3JlYXRpb24gdGhyb3VnaCBhIHRocmVlLXN0ZXAgcHJvY2Vzcy5cbiAgICogQHN1bW1hcnkgQ29yZSBtZXRob2QgZm9yIGJ1aWxkaW5nIGZpbHRlcnMgc3RlcCBieSBzdGVwOiBTdGVwIDEgKEluZGV4KSDihpIgU3RlcCAyIChDb25kaXRpb24pIOKGkiBTdGVwIDMgKFZhbHVlKS5cbiAgICogV2hlbiBhbGwgc3RlcHMgYXJlIGNvbXBsZXRlLCBjcmVhdGVzIGEgY29tcGxldGUgZmlsdGVyIG9iamVjdCBhbmQgYWRkcyBpdCB0byB0aGUgZmlsdGVyIGNvbGxlY3Rpb24uXG4gICAqIEhhbmRsZXMgYm90aCBrZXlib2FyZCBldmVudHMgKEVudGVyIHRvIHN1Ym1pdCkgYW5kIHByb2dyYW1tYXRpYyBjYWxscy5cbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gVGhlIHZhbHVlIHRvIGFkZCBmb3IgdGhlIGN1cnJlbnQgc3RlcFxuICAgKiBAcGFyYW0ge0N1c3RvbUV2ZW50fSBldmVudCAtIE9wdGlvbmFsIGV2ZW50IChLZXlib2FyZEV2ZW50IHRyaWdnZXJzIHN1Ym1pc3Npb24gd2hlbiB2YWx1ZSBpcyBlbXB0eSlcbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IFUgYXMgVXNlclxuICAgKiAgIHBhcnRpY2lwYW50IEYgYXMgRmlsdGVyQ29tcG9uZW50XG4gICAqXG4gICAqICAgVS0+PkY6IGFkZEZpbHRlcih2YWx1ZSwgZXZlbnQpXG4gICAqICAgRi0+PkY6IFRyaW0gYW5kIHZhbGlkYXRlIHZhbHVlXG4gICAqICAgYWx0IEtleWJvYXJkRXZlbnQgJiYgZW1wdHkgdmFsdWVcbiAgICogICAgIEYtPj5GOiBzdWJtaXQoKSAtIFNlbmQgY3VycmVudCBmaWx0ZXJzXG4gICAqICAgZWxzZSBWYWxpZCB2YWx1ZSBvciBzdGVwIDNcbiAgICogICAgIGFsdCBTdGVwIDEgKEluZGV4KVxuICAgKiAgICAgICBGLT4+RjogbGFzdEZpbHRlci5pbmRleCA9IHZhbHVlXG4gICAqICAgICAgIEYtPj5GOiBvcHRpb25zID0gY29uZGl0aW9uc1xuICAgKiAgICAgZWxzZSBTdGVwIDIgKENvbmRpdGlvbilcbiAgICogICAgICAgRi0+PkY6IGxhc3RGaWx0ZXIuY29uZGl0aW9uID0gdmFsdWVcbiAgICogICAgICAgRi0+PkY6IG9wdGlvbnMgPSBbXVxuICAgKiAgICAgZWxzZSBTdGVwIDMgKFZhbHVlKVxuICAgKiAgICAgICBGLT4+RjogbGFzdEZpbHRlci52YWx1ZSA9IHZhbHVlXG4gICAqICAgICAgIEYtPj5GOiBBZGQgY29tcGxldGUgZmlsdGVyIHRvIGZpbHRlclZhbHVlXG4gICAqICAgICAgIEYtPj5GOiBSZXNldCBzdGVwIHRvIDFcbiAgICogICAgIGVuZFxuICAgKiAgICAgRi0+PkY6IEluY3JlbWVudCBzdGVwXG4gICAqICAgICBGLT4+RjogQ2xlYXIgaW5wdXQgJiBmb2N1c1xuICAgKiAgICAgRi0+PkY6IFNob3cgbmV4dCBvcHRpb25zXG4gICAqICAgZW5kXG4gICAqXG4gICAqIEBtZW1iZXJPZiBGaWx0ZXJDb21wb25lbnRcbiAgICovXG4gIGFkZEZpbHRlcih2YWx1ZTogc3RyaW5nLCBldmVudD86IEN1c3RvbUV2ZW50KTogdm9pZCB7XG4gICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XG4gICAgaWYoZXZlbnQgaW5zdGFuY2VvZiBLZXlib2FyZEV2ZW50ICYmICF2YWx1ZSkge1xuICAgICAgdGhpcy5zdWJtaXQoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgIGlmKCh2YWx1ZSAmJiAoIShldmVudCBpbnN0YW5jZW9mIEtleWJvYXJkRXZlbnQpKSB8fCB0aGlzLnN0ZXAgPT09IDMpKSB7XG4gICAgICAgIGNvbnN0IGZpbHRlciA9IHRoaXMubGFzdEZpbHRlcjtcbiAgICAgICAgc3dpdGNoICh0aGlzLnN0ZXApIHtcbiAgICAgICAgICBjYXNlIDE6XG4gICAgICAgICAgICBmaWx0ZXJbJ2luZGV4J10gPSB2YWx1ZTtcbiAgICAgICAgICAgIHRoaXMub3B0aW9ucyA9IHRoaXMuY29uZGl0aW9ucztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgMjpcbiAgICAgICAgICAgIGZpbHRlclsnY29uZGl0aW9uJ10gPSB2YWx1ZTtcbiAgICAgICAgICAgIHRoaXMub3B0aW9ucyA9IFtdO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAzOlxuICAgICAgICAgICAgZmlsdGVyWyd2YWx1ZSddID0gdmFsdWU7XG4gICAgICAgICAgICB0aGlzLm9wdGlvbnMgPSB0aGlzLmluZGV4ZXM7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBpZighdGhpcy5maWx0ZXJWYWx1ZS5sZW5ndGgpIHtcbiAgICAgICAgICB0aGlzLmZpbHRlclZhbHVlLnB1c2goZmlsdGVyKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpZih0aGlzLnN0ZXAgPT09IDEpXG4gICAgICAgICAgICB0aGlzLmZpbHRlclZhbHVlLnB1c2goZmlsdGVyKTtcbiAgICAgICAgfVxuICAgICAgICBpZih0aGlzLnN0ZXAgPT09IDMpIHtcbiAgICAgICAgICB0aGlzLnN0ZXAgPSAwO1xuICAgICAgICAgIHRoaXMuZmlsdGVyVmFsdWVbdGhpcy5maWx0ZXJWYWx1ZS5sZW5ndGggLSAxXSA9IGZpbHRlcjtcbiAgICAgICAgICB0aGlzLmxhc3RGaWx0ZXIgPSB7fTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnN0ZXArKztcbiAgICAgICAgdGhpcy52YWx1ZSA9ICcnO1xuICAgICAgICBpZih0aGlzLm9wdGlvbnMubGVuZ3RoKVxuICAgICAgICAgIHRoaXMuaGFuZGxlRm9jdXModGhpcy5vcHRpb25zKTtcbiAgICAgICAgdGhpcy5jb21wb25lbnQubmF0aXZlRWxlbWVudC5mb2N1cygpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gU2VsZWN0cyBhbiBvcHRpb24gZnJvbSB0aGUgZHJvcGRvd24gc3VnZ2VzdGlvbnMuXG4gICAqIEBzdW1tYXJ5IEhhbmRsZXMgb3B0aW9uIHNlbGVjdGlvbiB3aGVuIGEgdXNlciBjbGlja3Mgb24gYSBzdWdnZXN0aW9uIGluIHRoZSBkcm9wZG93bi5cbiAgICogVGhpcyBtZXRob2QgYWN0cyBhcyBhIGJyaWRnZSBiZXR3ZWVuIGRyb3Bkb3duIGNsaWNrcyBhbmQgdGhlIG1haW4gYWRkRmlsdGVyIGxvZ2ljLlxuICAgKlxuICAgKiBAcGFyYW0ge0N1c3RvbUV2ZW50fSBldmVudCAtIFRoZSBjbGljayBldmVudCBmcm9tIHRoZSBkcm9wZG93biBvcHRpb25cbiAgICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gVGhlIHNlbGVjdGVkIG9wdGlvbiB2YWx1ZVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgc2VsZWN0T3B0aW9uKHZhbHVlOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmFkZEZpbHRlcih2YWx1ZSk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIERldGVybWluZXMgaWYgYSBmaWx0ZXIgb3B0aW9uIGNhbiBiZSBpbmRpdmlkdWFsbHkgcmVtb3ZlZC5cbiAgICogQHN1bW1hcnkgQ2hlY2tzIHdoZXRoZXIgYSBmaWx0ZXIgY29tcG9uZW50IHNob3VsZCBkaXNwbGF5IGEgY2xvc2UgaWNvbiBmb3IgcmVtb3ZhbC5cbiAgICogT25seSB2YWx1ZSBvcHRpb25zIGNhbiBiZSByZW1vdmVkIGluZGl2aWR1YWxseTsgaW5kZXggYW5kIGNvbmRpdGlvbiBvcHRpb25zIGFyZSBwYXJ0XG4gICAqIG9mIHRoZSBjb21wbGV0ZSBmaWx0ZXIgc3RydWN0dXJlIGFuZCBjYW5ub3QgYmUgcmVtb3ZlZCBzZXBhcmF0ZWx5LlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9uIC0gVGhlIGZpbHRlciBvcHRpb24gdGV4dCB0byBjaGVja1xuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgb3B0aW9uIGNhbiBiZSBjbGVhcmVkIGluZGl2aWR1YWxseSwgZmFsc2Ugb3RoZXJ3aXNlXG4gICAqIEBtZW1iZXJPZiBGaWx0ZXJDb21wb25lbnRcbiAgICovXG4gIGFsbG93Q2xlYXIob3B0aW9uOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5pbmRleGVzLmluZGV4T2Yob3B0aW9uKSA9PT0gLTEgJiYgdGhpcy5jb25kaXRpb25zLmluZGV4T2Yob3B0aW9uKSA9PT0gLTE7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFJlbW92ZXMgYSBjb21wbGV0ZSBmaWx0ZXIgZnJvbSB0aGUgY29sbGVjdGlvbiBiYXNlZCBvbiBmaWx0ZXIgdmFsdWUuXG4gICAqIEBzdW1tYXJ5IFJlbW92ZXMgYSBjb21wbGV0ZSBmaWx0ZXIgYnkgbWF0Y2hpbmcgdGhlIHByb3ZpZGVkIHZhbHVlIGFnYWluc3QgZmlsdGVyIHZhbHVlc1xuICAgKiBpbiB0aGUgY29sbGVjdGlvbi4gVXNlcyBzdHJpbmcgbm9ybWFsaXphdGlvbiB0byBoYW5kbGUgYWNjZW50cyBhbmQgY2FzZSBkaWZmZXJlbmNlcy5cbiAgICogQWZ0ZXIgcmVtb3ZhbCwgcmVzZXRzIHRoZSBpbnRlcmZhY2UgdG8gc2hvdyBhdmFpbGFibGUgaW5kZXhlcyBmb3IgbmV3IGZpbHRlciBjcmVhdGlvbi5cbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IGZpbHRlciAtIFRoZSBmaWx0ZXIgdmFsdWUgdG8gcmVtb3ZlIChtYXRjaGVzIGFnYWluc3QgZmlsdGVyLnZhbHVlIHByb3BlcnR5KVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICpcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgVSBhcyBVc2VyXG4gICAqICAgcGFydGljaXBhbnQgRiBhcyBGaWx0ZXJDb21wb25lbnRcbiAgICpcbiAgICogICBVLT4+RjogcmVtb3ZlRmlsdGVyKGZpbHRlclZhbHVlKVxuICAgKiAgIEYtPj5GOiBjbGVhblN0cmluZyhmaWx0ZXJWYWx1ZSlcbiAgICogICBGLT4+RjogRmlsdGVyIG91dCBtYXRjaGluZyBmaWx0ZXIgb2JqZWN0c1xuICAgKiAgIEYtPj5GOiBDbGVhciBpbnB1dCB2YWx1ZVxuICAgKiAgIEYtPj5GOiBoYW5kbGVGb2N1cyhpbmRleGVzKSAtIFJlc2V0IHRvIGluZGV4IHNlbGVjdGlvblxuICAgKiAgIE5vdGUgb3ZlciBGOiBGaWx0ZXIgcmVtb3ZlZCBhbmQgVUkgcmVzZXRcbiAgICpcbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgcmVtb3ZlRmlsdGVyKGZpbHRlcjogc3RyaW5nKTogdm9pZCB7XG4gICAgZnVuY3Rpb24gY2xlYW5TdHJpbmcoZmlsdGVyOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgICAgcmV0dXJuIGZpbHRlclxuICAgICAgICAudG9Mb3dlckNhc2UoKSAgICAgICAgICAgICAgICAgLy8gY29udmVydCBhbGwgY2hhcmFjdGVycyB0byBsb3dlcmNhc2VcbiAgICAgICAgLm5vcm1hbGl6ZShcIk5GRFwiKSAgICAgICAgICAgICAgLy8gc2VwYXJhdGUgYWNjZW50IG1hcmtzIGZyb20gY2hhcmFjdGVyc1xuICAgICAgICAucmVwbGFjZSgvW1xcdTAzMDAtXFx1MDM2Zl0vZywgXCJcIikgLy8gcmVtb3ZlIGFjY2VudCBtYXJrc1xuICAgICAgICAucmVwbGFjZSgvXFxzKy9nLCBcIlwiKTsgICAgICAgICAgLy8gcmVtb3ZlIGFsbCB3aGl0ZXNwYWNlXG4gICAgfVxuICAgIHRoaXMudmFsdWUgPSBcIlwiO1xuICAgIHRoaXMuZmlsdGVyVmFsdWUgPSB0aGlzLmZpbHRlclZhbHVlLmZpbHRlcigoaXRlbSkgPT4gaXRlbT8uWyd2YWx1ZSddICYmIGNsZWFuU3RyaW5nKGl0ZW0/LlsndmFsdWUnXSkgIT09IGNsZWFuU3RyaW5nKGZpbHRlcikpO1xuICAgIGlmKHRoaXMuZmlsdGVyVmFsdWUubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aGlzLnN0ZXAgPSAxO1xuICAgICAgdGhpcy5sYXN0RmlsdGVyID0ge307XG4gICAgfVxuICAgIHRoaXMuaGFuZGxlRm9jdXModGhpcy5pbmRleGVzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUmVzZXRzIHRoZSBjb21wb25lbnQgdG8gaXRzIGluaXRpYWwgc3RhdGUuXG4gICAqIEBzdW1tYXJ5IENsZWFycyBhbGwgZmlsdGVyIGRhdGEsIG9wdGlvbnMsIGFuZCByZXNldHMgdGhlIHN0ZXAgY291bnRlciB0byAxLlxuICAgKiBUaGlzIG1ldGhvZCBwcm92aWRlcyBhIGNsZWFuIHNsYXRlIGZvciBuZXcgZmlsdGVyIGNyZWF0aW9uIHdpdGhvdXQgZW1pdHRpbmcgZXZlbnRzLlxuICAgKlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgcmVzZXQoKTogdm9pZCB7XG4gICAgdGhpcy5vcHRpb25zID0gdGhpcy5maWx0ZXJlZE9wdGlvbnMgPSB0aGlzLmZpbHRlclZhbHVlID0gW107XG4gICAgdGhpcy5zdGVwID0gMTtcbiAgICB0aGlzLmxhc3RGaWx0ZXIgPSB7fTtcbiAgICB0aGlzLnZhbHVlID0gJyc7XG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgdGhpcy5zdWJtaXQoKTtcbiAgICB9LCAxMDApO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDbGVhcnMgYWxsIGZpbHRlcnMgYW5kIG5vdGlmaWVzIHBhcmVudCBjb21wb25lbnRzLlxuICAgKiBAc3VtbWFyeSBSZXNldHMgdGhlIGNvbXBvbmVudCBzdGF0ZSBhbmQgZW1pdHMgdW5kZWZpbmVkIHRvIG5vdGlmeSBwYXJlbnQgY29tcG9uZW50c1xuICAgKiB0aGF0IGFsbCBmaWx0ZXJzIGhhdmUgYmVlbiBjbGVhcmVkLiBUaGlzIHRyaWdnZXJzIGFueSBjb25uZWN0ZWQgZGF0YSByZWZyZXNoIGxvZ2ljLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWUgLSBPcHRpb25hbCBwYXJhbWV0ZXIgKGN1cnJlbnRseSB1bnVzZWQpXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBjbGVhcih2YWx1ZT86IHN0cmluZyk6IHZvaWQge1xuICAgIGlmKCF2YWx1ZSlcbiAgICAgIHRoaXMucmVzZXQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gU3VibWl0cyB0aGUgY3VycmVudCBmaWx0ZXIgY29sbGVjdGlvbiB0byBwYXJlbnQgY29tcG9uZW50cy5cbiAgICogQHN1bW1hcnkgRW1pdHMgdGhlIGN1cnJlbnQgZmlsdGVyIGFycmF5IHRvIHBhcmVudCBjb21wb25lbnRzIHdoZW4gZmlsdGVycyBhcmUgcmVhZHlcbiAgICogdG8gYmUgYXBwbGllZC4gT25seSBlbWl0cyBpZiB0aGVyZSBhcmUgYWN0aXZlIGZpbHRlcnMuIENsZWFycyBvcHRpb25zIGFmdGVyIHN1Ym1pc3Npb24uXG4gICAqXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBzdWJtaXQoKTogdm9pZCB7XG4gICAgdGhpcy5maWx0ZXJFdmVudC5lbWl0KHtcbiAgICAgIHF1ZXJ5OiB0aGlzLmZpbHRlclZhbHVlLmxlbmd0aCA+IDAgPyB0aGlzLmZpbHRlclZhbHVlIDogdW5kZWZpbmVkLFxuICAgICAgc29ydDoge1xuICAgICAgICB2YWx1ZTogdGhpcy5zb3J0VmFsdWUsXG4gICAgICAgIGRpcmVjdGlvbjogdGhpcy5zb3J0RGlyZWN0aW9uXG4gICAgICB9XG4gICAgfSBhcyBJRmlsdGVyUXVlcnkpO1xuICAgIGlmKHRoaXMuZmlsdGVyVmFsdWUubGVuZ3RoID09PSAwKVxuICAgICAgdGhpcy5vcHRpb25zID0gW107XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFRvZ2dsZXMgdGhlIHNvcnQgZGlyZWN0aW9uIGJldHdlZW4gYXNjZW5kaW5nIGFuZCBkZXNjZW5kaW5nLlxuICAgKiBAc3VtbWFyeSBIYW5kbGVzIHNvcnQgZGlyZWN0aW9uIGNoYW5nZXMgYnkgdG9nZ2xpbmcgYmV0d2VlbiBBU0MgYW5kIERTQyB2YWx1ZXMuXG4gICAqIFdoZW4gdGhlIGRpcmVjdGlvbiBjaGFuZ2VzLCBhdXRvbWF0aWNhbGx5IHRyaWdnZXJzIGEgc3VibWl0IHRvIGFwcGx5IHRoZSBuZXdcbiAgICogc29ydGluZyBjb25maWd1cmF0aW9uIHRvIHRoZSBmaWx0ZXJlZCByZXN1bHRzLlxuICAgKlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgIGhhbmRsZVNvcnREaXJlY3Rpb25DaGFuZ2UoKTogdm9pZCB7XG4gICAgY29uc3QgZGlyZWN0aW9uID0gdGhpcy5zb3J0RGlyZWN0aW9uID09PSAgT3JkZXJEaXJlY3Rpb24uQVNDID8gT3JkZXJEaXJlY3Rpb24uRFNDIDogT3JkZXJEaXJlY3Rpb24uQVNDO1xuICAgIGlmKGRpcmVjdGlvbiAhPT0gdGhpcy5zb3J0RGlyZWN0aW9uKSB7XG4gICAgICB0aGlzLnNvcnREaXJlY3Rpb24gPSBkaXJlY3Rpb247XG4gICAgICB0aGlzLnN1Ym1pdCgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSGFuZGxlcyBzb3J0IGZpZWxkIHNlbGVjdGlvbiBjaGFuZ2VzIGZyb20gdGhlIGRyb3Bkb3duLlxuICAgKiBAc3VtbWFyeSBQcm9jZXNzZXMgc29ydCBmaWVsZCBjaGFuZ2VzIHdoZW4gdXNlcnMgc2VsZWN0IGEgZGlmZmVyZW50IGZpZWxkXG4gICAqIGZyb20gdGhlIHNvcnQgZHJvcGRvd24uIFVwZGF0ZXMgdGhlIHNvcnRWYWx1ZSBwcm9wZXJ0eSBhbmQgdHJpZ2dlcnNcbiAgICogYSBzdWJtaXQgdG8gYXBwbHkgdGhlIG5ldyBzb3J0aW5nIGNvbmZpZ3VyYXRpb24gaWYgdGhlIHZhbHVlIGhhcyBjaGFuZ2VkLlxuICAgKlxuICAgKiBAcGFyYW0ge0N1c3RvbUV2ZW50fSBldmVudCAtIFRoZSBzZWxlY3QgY2hhbmdlIGV2ZW50IGNvbnRhaW5pbmcgdGhlIG5ldyBzb3J0IGZpZWxkIHZhbHVlXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBoYW5kbGVTb3J0Q2hhbmdlKGV2ZW50OiBDdXN0b21FdmVudCk6IHZvaWQge1xuICAgIGNvbnN0IHRhcmdldCA9IGV2ZW50LnRhcmdldCBhcyBIVE1MSW9uU2VsZWN0RWxlbWVudDtcbiAgICBjb25zdCB2YWx1ZSA9IHRhcmdldC52YWx1ZTtcbiAgICBpZih2YWx1ZSAhPT0gdGhpcy5zb3J0VmFsdWUpIHtcbiAgICAgIHRoaXMuc29ydFZhbHVlID0gdmFsdWUgYXMgc3RyaW5nO1xuICAgICAgdGhpcy5zdWJtaXQoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEZpbHRlcnMgYXZhaWxhYmxlIG9wdGlvbnMgYmFzZWQgb24gdXNlciBpbnB1dCB3aXRoIHZpc3VhbCBoaWdobGlnaHRpbmcuXG4gICAqIEBzdW1tYXJ5IFBlcmZvcm1zIHJlYWwtdGltZSBmaWx0ZXJpbmcgb2YgYXZhaWxhYmxlIG9wdGlvbnMgYmFzZWQgb24gdXNlciBpbnB1dC5cbiAgICogQWxzbyBoYW5kbGVzIHZpc3VhbCBoaWdobGlnaHRpbmcgb2YgbWF0Y2hpbmcgb3B0aW9ucyBpbiB0aGUgZHJvcGRvd24uIFJldHVybnMgYWxsXG4gICAqIG9wdGlvbnMgaWYgaW5wdXQgaXMgbGVzcyB0aGFuIDIgY2hhcmFjdGVycyBmb3IgcGVyZm9ybWFuY2Ugb3B0aW1pemF0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZyB8IG51bGwgfCB1bmRlZmluZWR9IHZhbHVlIC0gVGhlIHNlYXJjaCB2YWx1ZSB0byBmaWx0ZXIgYnlcbiAgICogQHJldHVybnMge3N0cmluZ1tdfSBBcnJheSBvZiBmaWx0ZXJlZCBvcHRpb25zIHRoYXQgbWF0Y2ggdGhlIGlucHV0XG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IFUgYXMgVXNlclxuICAgKiAgIHBhcnRpY2lwYW50IEYgYXMgRmlsdGVyQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgRCBhcyBET01cbiAgICpcbiAgICogICBVLT4+RjogZmlsdGVyT3B0aW9ucyhpbnB1dFZhbHVlKVxuICAgKiAgIGFsdCBpbnB1dFZhbHVlIDwgMiBjaGFyYWN0ZXJzXG4gICAqICAgICBGLT4+RDogUmVtb3ZlIGV4aXN0aW5nIGhpZ2hsaWdodHNcbiAgICogICAgIEYtLT4+VTogUmV0dXJuIGFsbCBvcHRpb25zXG4gICAqICAgZWxzZSBpbnB1dFZhbHVlID49IDIgY2hhcmFjdGVyc1xuICAgKiAgICAgRi0+PkQ6IFF1ZXJ5IGFsbCBvcHRpb24gZWxlbWVudHNcbiAgICogICAgIEYtPj5EOiBBZGQgaGlnaGxpZ2h0IHRvIGZpcnN0IG1hdGNoaW5nIG9wdGlvblxuICAgKiAgICAgRi0+PkY6IEZpbHRlciBvcHRpb25zIGJ5IHN1YnN0cmluZyBtYXRjaFxuICAgKiAgICAgRi0tPj5VOiBSZXR1cm4gZmlsdGVyZWQgb3B0aW9uc1xuICAgKiAgIGVuZFxuICAgKlxuICAgKiBAbWVtYmVyT2YgRmlsdGVyQ29tcG9uZW50XG4gICAqL1xuICBmaWx0ZXJPcHRpb25zKHZhbHVlOiBzdHJpbmcgfCBudWxsIHwgIHVuZGVmaW5lZCk6IHN0cmluZ1tdIHtcbiAgICBjb25zdCBvcHRpb25zRWxlbWVudCA9IHRoaXMub3B0aW9uc0ZpbHRlckVsZW1lbnQubmF0aXZlRWxlbWVudDtcbiAgICBpZighdmFsdWU/Lmxlbmd0aCB8fCAhdmFsdWUgfHwgdmFsdWUubGVuZ3RoIDwgMikge1xuICAgICAgY29uc3QgZmlsdGVyZWRPcHRpb24gPSBvcHRpb25zRWxlbWVudC5xdWVyeVNlbGVjdG9yKCcuZGNmLWZpbHRlcmluZy1pdGVtJyk7XG4gICAgICBpZihmaWx0ZXJlZE9wdGlvbilcbiAgICAgICAgZmlsdGVyZWRPcHRpb24uY2xhc3NMaXN0LnJlbW92ZSgnZGNmLWZpbHRlcmluZy1pdGVtJyk7XG4gICAgICByZXR1cm4gdGhpcy5vcHRpb25zO1xuICAgIH1cbiAgICBjb25zdCBvcHRpb25zID0gb3B0aW9uc0VsZW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLmRjZi1pdGVtJyk7XG4gICAgZm9yIChjb25zdCBvcHRpb24gb2Ygb3B0aW9ucykge1xuICAgICAgY29uc3QgaXNBY3RpdmUgPSBvcHRpb24udGV4dENvbnRlbnQ/LnRvTG93ZXJDYXNlKCkuaW5jbHVkZXModmFsdWUudG9Mb3dlckNhc2UoKSk7XG4gICAgICBpZihpc0FjdGl2ZSkge1xuICAgICAgICBvcHRpb24uY2xhc3NMaXN0LmFkZCgnZGNmLWZpbHRlcmluZy1pdGVtJyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdGhpcy5vcHRpb25zLmZpbHRlcigob3B0aW9uOiBzdHJpbmcpID0+IG9wdGlvbi50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKHZhbHVlLnRvTG93ZXJDYXNlKCkgYXMgc3RyaW5nKSk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEhhbmRsZXMgc2VhcmNoIGV2ZW50cyBmcm9tIHRoZSBpbnRlZ3JhdGVkIHNlYXJjaGJhciBjb21wb25lbnQuXG4gICAqIEBzdW1tYXJ5IFByb2Nlc3NlcyBzZWFyY2ggaW5wdXQgZnJvbSB0aGUgc2VhcmNoYmFyIGFuZCBlbWl0cyBzZWFyY2ggZXZlbnRzXG4gICAqIHRvIHBhcmVudCBjb21wb25lbnRzLiBUaGlzIG1ldGhvZCBhY3RzIGFzIGEgYnJpZGdlIGJldHdlZW4gdGhlIGludGVybmFsXG4gICAqIHNlYXJjaGJhciBjb21wb25lbnQgYW5kIGV4dGVybmFsIHNlYXJjaCBldmVudCBsaXN0ZW5lcnMuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nIHwgdW5kZWZpbmVkfSB2YWx1ZSAtIFRoZSBzZWFyY2ggdmFsdWUgZW50ZXJlZCBieSB0aGUgdXNlclxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICogQG1lbWJlck9mIEZpbHRlckNvbXBvbmVudFxuICAgKi9cbiAgaGFuZGxlU2VhcmNoKHZhbHVlOiBzdHJpbmcgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICB0aGlzLnNlYXJjaEV2ZW50LmVtaXQodmFsdWUpO1xuICB9XG5cbn1cbiIsIlxuQGlmKCFpbmRleGVzLmxlbmd0aCkge1xuICA8bmd4LWRlY2FmLXNlYXJjaGJhciBbZW1pdEV2ZW50VG9XaW5kb3ddPVwiZmFsc2VcIiBbZGVib3VuY2VdPVwiNTAwXCIgKHNlYXJjaEV2ZW50KT1cImhhbmRsZVNlYXJjaCgkZXZlbnQpXCIgLz5cbn1cblxuPGRpdiBjbGFzcz1cImRjZi1ncmlkIGRjZi1ncmlkLXNtYWxsIGRjZi1ncmlkLW1hdGNoIGRjZi1maWx0ZXItZ3JpZFwiIFtuZ0NsYXNzXT1cInsnZGNmLWhpZGRlbic6ICFpbmRleGVzLmxlbmd0aH1cIj5cbiAgPGRpdiBjbGFzcz1cImRjZi13aWR0aC1leHBhbmRcIj5cbiAgICA8ZGl2IGNsYXNzPVwiZGNmLWZpbHRlclwiPlxuICAgICAgPGRpdiBjbGFzcz1cImRjZi1pbnB1dFwiPlxuICAgICAgICBAZm9yKGZpbHRlciBvZiBmaWx0ZXJWYWx1ZTsgdHJhY2sgdHJhY2tJdGVtRm4oJGluZGV4LCBmaWx0ZXI/LlsnaW5kZXgnXSkpIHtcbiAgICAgICAgICBAaWYoZmlsdGVyPy5bJ2luZGV4J10pIHtcbiAgICAgICAgICAgIDxpb24tY2hpcCBbb3V0bGluZV09XCJ0cnVlXCI+e3sgZmlsdGVyPy5bJ2luZGV4J10gfX08L2lvbi1jaGlwPlxuICAgICAgICAgIH1cbiAgICAgICAgICBAaWYoZmlsdGVyPy5bJ2NvbmRpdGlvbiddKSB7XG4gICAgICAgICAgICA8aW9uLWNoaXAgW291dGxpbmVdPVwidHJ1ZVwiPnt7IGZpbHRlcj8uWydjb25kaXRpb24nXSB9fTwvaW9uLWNoaXA+XG4gICAgICAgICAgfVxuICAgICAgICAgIEBpZihmaWx0ZXI/LlsndmFsdWUnXSkge1xuICAgICAgICAgICAgPGlvbi1jaGlwIFtvdXRsaW5lXT1cInRydWVcIiBjbGFzcz1cImRjZi1maWx0ZXItdmFsdWVcIj5cbiAgICAgICAgICAgICAge3sgZmlsdGVyPy5bJ3ZhbHVlJ10gfX1cbiAgICAgICAgICAgICAgPGlvbi1pY29uIG5hbWU9XCJjbG9zZVwiIChjbGljayk9XCJyZW1vdmVGaWx0ZXIoZmlsdGVyPy5bJ3ZhbHVlJ10pXCIgc2l6ZT1cInNtYWxsXCI+PC9pb24taWNvbj5cbiAgICAgICAgICAgIDwvaW9uLWNoaXA+XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIDxkaXYgY2xhc3M9XCJkY2Ytd2lkdGgtMS0xXCI+XG4gICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICBmaWxsPVwibm9uZVwiXG4gICAgICAgICAgICBbKG5nTW9kZWwpXT1cInZhbHVlXCJcbiAgICAgICAgICAgIChrZXlkb3duLmVudGVyKT1cImFkZEZpbHRlcih2YWx1ZSwgJGV2ZW50KVwiXG4gICAgICAgICAgICAoa2V5ZG93bi5iYWNrc3BhY2UpPVwiY2xlYXIodmFsdWUpXCJcbiAgICAgICAgICAgIChpbnB1dCk9XCJoYW5kbGVJbnB1dCgkZXZlbnQpXCJcbiAgICAgICAgICAgIChjbGljayk9XCJoYW5kbGVGb2N1cygpXCJcbiAgICAgICAgICAgIChibHVyKT1cImhhbmRsZUJsdXIoKVwiXG4gICAgICAgICAgICB0eXBlPVwidGV4dFwiXG4gICAgICAgICAgICBbcmVhZG9ubHldPVwic3RlcCAhPT0gM1wiXG4gICAgICAgICAgICBwbGFjZWhvbGRlcj1cInt7IGxvY2FsZSArIChzdGVwID09PSAzID8gJy50eXBlJyA6ICcuc2VsZWN0JykgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgICAgI2NvbXBvbmVudFxuICAgICAgICAgIC8+XG4gICAgICAgICAgQGlmKHdpbmRvd1dpZHRoID49IDc2OCkge1xuICAgICAgICAgICAgPGRpdiBbY2xhc3NdPVwiJ2RjZi1kcm9wZG93biAnICsgKG9wdGlvbnMubGVuZ3RoID4gMCA/ICcgZGNmLWFjdGl2ZScgOiAnJylcIiAjb3B0aW9uc0ZpbHRlckVsZW1lbnQ+XG4gICAgICAgICAgICAgIDxkaXY+XG4gICAgICAgICAgICAgICAgQGlmKGZpbHRlcmVkT3B0aW9ucy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICBAZm9yKGtleSBvZiBmaWx0ZXJlZE9wdGlvbnM7IHRyYWNrIGtleSkge1xuICAgICAgICAgICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJkY2YtaXRlbVwiXG4gICAgICAgICAgICAgICAgICAgICAgdGFiaW5kZXg9XCIwXCJcbiAgICAgICAgICAgICAgICAgICAgICAoa2V5ZG93bi5lbnRlcik9XCJzZWxlY3RPcHRpb24oa2V5KVwiXG4gICAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cInNlbGVjdE9wdGlvbihrZXkpXCI+XG4gICAgICAgICAgICAgICAgICAgICAge3sga2V5IH19XG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImRjZi1lbXB0eVwiXG4gICAgICAgICAgICAgICAgICAgIChjbGljayk9XCJmaWx0ZXJlZE9wdGlvbnMgPSBvcHRpb25zOyB2YWx1ZSA9ICcnXCJcbiAgICAgICAgICAgICAgICAgICAgdGFiaW5kZXg9XCIwXCJcbiAgICAgICAgICAgICAgICAgICAgKGtleWRvd24uZW50ZXIpPVwiZmlsdGVyZWRPcHRpb25zID0gb3B0aW9uczsgdmFsdWUgPSAnJ1wiXG4gICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgIHt7ICdub19zdWdnZXN0aW9ucycgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICB9XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgICBAaWYoZmlsdGVyVmFsdWUubGVuZ3RoID4gMCkge1xuICAgICAgICA8ZGl2IGNsYXNzPVwiZGNmLWljb24tY2xlYXJcIj5cbiAgICAgICAgICA8aW9uLWJ1dHRvbiBmaWxsPVwiY2xlYXJcIiBzaXplPVwic21hbGxcIiAoY2xpY2spPVwiY2xlYXIoKVwiPlxuICAgICAgICAgICAgPGlvbi1pY29uIG5hbWU9XCJ0cmFzaC1vdXRsaW5lXCIgY29sb3I9XCJkYXJrXCIgc2xvdD1cImljb24tb25seVwiPjwvaW9uLWljb24+XG4gICAgICAgICAgPC9pb24tYnV0dG9uPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIH1cbiAgICAgIDxkaXYgY2xhc3M9XCJkY2YtaWNvbi1zZWFyY2hcIj5cbiAgICAgICAgPGlvbi1idXR0b24gZmlsbD1cImNsZWFyXCIgc2l6ZT1cInNtYWxsXCIgKGNsaWNrKT1cInN1Ym1pdCgpXCI+XG4gICAgICAgICAgPGlvbi1pY29uIG5hbWU9XCJzZWFyY2gtb3V0bGluZVwiIGNvbG9yPVwiZGFya1wiIHNsb3Q9XCJpY29uLW9ubHlcIj48L2lvbi1pY29uPlxuICAgICAgICA8L2lvbi1idXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgICBAaWYod2luZG93V2lkdGggPCA3NjgpIHtcbiAgICAgIDxkaXYgW2NsYXNzXT1cIidkY2YtZHJvcGRvd24gJyArIChvcHRpb25zLmxlbmd0aCA+IDAgPyAnIGRjZi1hY3RpdmUnIDogJycpXCIgI29wdGlvbnNGaWx0ZXJFbGVtZW50PlxuICAgICAgICA8ZGl2PlxuICAgICAgICAgIEBpZihmaWx0ZXJlZE9wdGlvbnMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgQGZvcihrZXkgb2YgZmlsdGVyZWRPcHRpb25zOyB0cmFjayBrZXkpIHtcbiAgICAgICAgICAgICAgPGRpdlxuICAgICAgICAgICAgICAgIGNsYXNzPVwiZGNmLWl0ZW1cIlxuICAgICAgICAgICAgICAgIHRhYmluZGV4PVwiMFwiXG4gICAgICAgICAgICAgICAgKGtleWRvd24uZW50ZXIpPVwic2VsZWN0T3B0aW9uKGtleSlcIlxuICAgICAgICAgICAgICAgIChjbGljayk9XCJzZWxlY3RPcHRpb24oa2V5KVwiPlxuICAgICAgICAgICAgICAgIHt7IGtleSB9fVxuICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IEBlbHNlIHtcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJkY2YtZW1wdHlcIlxuICAgICAgICAgICAgKGNsaWNrKT1cImZpbHRlcmVkT3B0aW9ucyA9IG9wdGlvbnM7IHZhbHVlID0gJydcIlxuICAgICAgICAgICAgdGFiaW5kZXg9XCIwXCJcbiAgICAgICAgICAgIChrZXlkb3duLmVudGVyKT1cImZpbHRlcmVkT3B0aW9ucyA9IG9wdGlvbnM7IHZhbHVlID0gJydcIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICB7eyAnbm9fc3VnZ2VzdGlvbnMnIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICB9XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgfVxuICA8L2Rpdj5cbiAgQGlmKCFkaXNhYmxlU29ydCkge1xuICAgIDxkaXYgY2xhc3M9XCJkY2Ytd2lkdGgtMS01QG0gZGNmLXdpZHRoLTEtMSBkY2Ytc29ydC1jb250YWluZXJcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJkY2YtZ3JpZCBkY2YtZ3JpZC1jb2xsYXBzZSBkY2YtZmxleCBkY2YtZmxleC1taWRkbGUgZGNmLWdyaWQtbWF0Y2hcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImRjZi13aWR0aC1leHBhbmRcIj5cbiAgICAgICAgICA8aW9uLXNlbGVjdFxuICAgICAgICAgICAgICB0b2dnbGVJY29uPVwiY2hldnJvbi1kb3duLW91dGxpbmVcIlxuICAgICAgICAgICAgICBleHBhbmRlZEljb249XCJjaGV2cm9uLXVwLW91dGxpbmVcIlxuICAgICAgICAgICAgICBjbGFzcz1cImRjZi1zb3J0LXNlbGVjdFwiXG4gICAgICAgICAgICAgIChpb25DaGFuZ2UpPVwiaGFuZGxlU29ydENoYW5nZSgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgaW50ZXJmYWNlPVwicG9wb3ZlclwiXG4gICAgICAgICAgICAgIFt2YWx1ZV09XCJzb3J0VmFsdWVcIlxuICAgICAgICAgICAgICBsYWJlbC1wbGFjZW1lbnQ9XCJmbG9hdGluZ1wiXG4gICAgICAgICAgICAgIGZpbGw9XCJvdXRsaW5lXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgIEBmb3Ioc29ydCBvZiBzb3J0Qnk7IHRyYWNrIHNvcnQpIHtcbiAgICAgICAgICAgICAgYWFcbiAgICAgICAgICAgICAgPGlvbi1zZWxlY3Qtb3B0aW9uIFt2YWx1ZV09XCJzb3J0XCI+e3sgc29ydCB8IHRyYW5zbGF0ZSB9fTwvaW9uLXNlbGVjdC1vcHRpb24+XG4gICAgICAgICAgICB9XG4gICAgICAgICAgPC9pb24tc2VsZWN0PlxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImRjZi13aWR0aC1hdXRvXCI+XG4gICAgICAgICAgPGlvbi1idXR0b24gKGNsaWNrKT1cImhhbmRsZVNvcnREaXJlY3Rpb25DaGFuZ2UoKVwiIGZpbGw9XCJjbGVhclwiPlxuICAgICAgICAgICAgPGlvbi1pY29uIHNsb3Q9XCJpY29uLW9ubHlcIiBbbmFtZV09XCJzb3J0RGlyZWN0aW9uID09PSAnZGVzYycgPyAnYXJyb3ctZG93bi1vdXRsaW5lJyA6ICdhcnJvdy11cC1vdXRsaW5lJ1wiPjwvaW9uLWljb24+XG4gICAgICAgICAgPC9pb24tYnV0dG9uPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICB9XG48L2Rpdj5cblxuXG4iXX0=