@decaf-ts/for-angular 0.0.25 → 0.0.26

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 (78) hide show
  1. package/fesm2022/decaf-ts-for-angular.mjs +1465 -1488
  2. package/fesm2022/decaf-ts-for-angular.mjs.map +1 -1
  3. package/index.d.ts +7470 -3
  4. package/package.json +14 -17
  5. package/components/component-renderer/component-renderer.component.d.ts +0 -278
  6. package/components/crud-field/crud-field.component.d.ts +0 -611
  7. package/components/crud-form/constants.d.ts +0 -5
  8. package/components/crud-form/crud-form.component.d.ts +0 -288
  9. package/components/crud-form/types.d.ts +0 -17
  10. package/components/empty-state/empty-state.component.d.ts +0 -300
  11. package/components/fieldset/fieldset.component.d.ts +0 -555
  12. package/components/filter/filter.component.d.ts +0 -514
  13. package/components/for-angular-components.module.d.ts +0 -20
  14. package/components/index.d.ts +0 -16
  15. package/components/layout/layout.component.d.ts +0 -110
  16. package/components/list/list.component.d.ts +0 -848
  17. package/components/list-item/list-item.component.d.ts +0 -390
  18. package/components/model-renderer/model-renderer.component.d.ts +0 -97
  19. package/components/pagination/constants.d.ts +0 -7
  20. package/components/pagination/pagination.component.d.ts +0 -264
  21. package/components/searchbar/searchbar.component.d.ts +0 -407
  22. package/components/stepped-form/stepped-form.component.d.ts +0 -255
  23. package/directives/collapsable.directive.d.ts +0 -9
  24. package/directives/index.d.ts +0 -1
  25. package/engine/DynamicModule.d.ts +0 -17
  26. package/engine/NgxBaseComponent.d.ts +0 -541
  27. package/engine/NgxCrudFormField.d.ts +0 -123
  28. package/engine/NgxFormService.d.ts +0 -601
  29. package/engine/NgxRenderingEngine.d.ts +0 -282
  30. package/engine/ValidatorFactory.d.ts +0 -15
  31. package/engine/constants.d.ts +0 -168
  32. package/engine/decorators.d.ts +0 -25
  33. package/engine/index.d.ts +0 -18
  34. package/engine/interfaces.d.ts +0 -271
  35. package/engine/types.d.ts +0 -200
  36. package/esm2022/components/component-renderer/component-renderer.component.mjs +0 -321
  37. package/esm2022/components/crud-field/crud-field.component.mjs +0 -518
  38. package/esm2022/components/crud-form/constants.mjs +0 -14
  39. package/esm2022/components/crud-form/crud-form.component.mjs +0 -259
  40. package/esm2022/components/crud-form/types.mjs +0 -2
  41. package/esm2022/components/empty-state/empty-state.component.mjs +0 -345
  42. package/esm2022/components/fieldset/fieldset.component.mjs +0 -677
  43. package/esm2022/components/filter/filter.component.mjs +0 -700
  44. package/esm2022/components/for-angular-components.module.mjs +0 -84
  45. package/esm2022/components/index.mjs +0 -20
  46. package/esm2022/components/layout/layout.component.mjs +0 -150
  47. package/esm2022/components/list/list.component.mjs +0 -1238
  48. package/esm2022/components/list-item/list-item.component.mjs +0 -405
  49. package/esm2022/components/model-renderer/model-renderer.component.mjs +0 -144
  50. package/esm2022/components/pagination/constants.mjs +0 -2
  51. package/esm2022/components/pagination/pagination.component.mjs +0 -321
  52. package/esm2022/components/searchbar/searchbar.component.mjs +0 -491
  53. package/esm2022/components/stepped-form/stepped-form.component.mjs +0 -306
  54. package/esm2022/decaf-ts-for-angular.mjs +0 -5
  55. package/esm2022/directives/collapsable.directive.mjs +0 -29
  56. package/esm2022/directives/index.mjs +0 -2
  57. package/esm2022/engine/DynamicModule.mjs +0 -18
  58. package/esm2022/engine/NgxBaseComponent.mjs +0 -541
  59. package/esm2022/engine/NgxCrudFormField.mjs +0 -137
  60. package/esm2022/engine/NgxFormService.mjs +0 -917
  61. package/esm2022/engine/NgxRenderingEngine.mjs +0 -376
  62. package/esm2022/engine/ValidatorFactory.mjs +0 -106
  63. package/esm2022/engine/constants.mjs +0 -170
  64. package/esm2022/engine/decorators.mjs +0 -38
  65. package/esm2022/engine/index.mjs +0 -19
  66. package/esm2022/engine/interfaces.mjs +0 -4
  67. package/esm2022/engine/types.mjs +0 -2
  68. package/esm2022/for-angular-common.module.mjs +0 -84
  69. package/esm2022/helpers/index.mjs +0 -13
  70. package/esm2022/helpers/utils.mjs +0 -436
  71. package/esm2022/i18n/Loader.mjs +0 -86
  72. package/esm2022/i18n/data/en.json +0 -85
  73. package/esm2022/public-apis.mjs +0 -15
  74. package/for-angular-common.module.d.ts +0 -50
  75. package/helpers/index.d.ts +0 -12
  76. package/helpers/utils.d.ts +0 -279
  77. package/i18n/Loader.d.ts +0 -43
  78. package/public-apis.d.ts +0 -14
@@ -1,917 +0,0 @@
1
- import { escapeHtml, HTML5CheckTypes, HTML5InputTypes, parseToNumber } from '@decaf-ts/ui-decorators';
2
- import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
3
- import { isValidDate, ModelKeys, parseDate, Primitives, Validation } from '@decaf-ts/decorator-validation';
4
- import { ValidatorFactory } from './ValidatorFactory';
5
- import { cleanSpaces } from '../helpers';
6
- import { OperationKeys } from '@decaf-ts/db-decorators';
7
- import { AngularEngineKeys, BaseComponentProps } from '../engine/constants';
8
- /**
9
- * @description Service for managing Angular forms and form controls.
10
- * @summary The NgxFormService provides utility methods for creating, managing, and validating Angular forms and form controls. It includes functionality for registering forms, adding controls, validating fields, and handling form data.
11
- *
12
- * @class
13
- * @param {WeakMap<AbstractControl, FieldProperties>} controls - A WeakMap to store control properties.
14
- * @param {Map<string, FormGroup>} formRegistry - A Map to store registered forms.
15
- *
16
- * @example
17
- * // Creating a form from components
18
- * const components = [
19
- * { inputs: { name: 'username', type: 'text', required: true } },
20
- * { inputs: { name: 'password', type: 'password', minLength: 8 } }
21
- * ];
22
- * const form = NgxFormService.createFormFromComponents('loginForm', components, true);
23
- *
24
- * // Validating fields
25
- * NgxFormService.validateFields(form);
26
- *
27
- * // Getting form data
28
- * const formData = NgxFormService.getFormData(form);
29
- *
30
- * @mermaid
31
- * sequenceDiagram
32
- * participant C as Component
33
- * participant NFS as NgxFormService
34
- * participant AF as Angular Forms
35
- * C->>NFS: createFormFromComponents()
36
- * NFS->>AF: new FormGroup()
37
- * NFS->>NFS: addFormControl()
38
- * NFS->>AF: addControl()
39
- * NFS-->>C: Return FormGroup
40
- * C->>NFS: validateFields()
41
- * NFS->>AF: markAsTouched(), markAsDirty(), updateValueAndValidity()
42
- * C->>NFS: getFormData()
43
- * NFS->>AF: Get control values
44
- * NFS-->>C: Return form data
45
- */
46
- export class NgxFormService {
47
- /**
48
- * @description WeakMap that stores control properties for form controls.
49
- * @summary A WeakMap that associates AbstractControl instances with their corresponding FieldProperties.
50
- * This allows the service to track metadata for form controls without creating memory leaks.
51
- *
52
- * @type {WeakMap<AbstractControl, FieldProperties>}
53
- * @private
54
- * @static
55
- * @memberOf NgxFormService
56
- */
57
- static { this.controls = new WeakMap(); }
58
- /**
59
- * @description Registry of form groups indexed by their unique identifiers.
60
- * @summary A Map that stores FormGroup instances with their unique string identifiers.
61
- * This allows global access to registered forms throughout the application.
62
- *
63
- * @type {Map<string, FormGroup>}
64
- * @private
65
- * @static
66
- * @memberOf NgxFormService
67
- */
68
- static { this.formRegistry = new Map(); }
69
- static { this.pageMapper = {}; }
70
- /**
71
- * @description Creates a new form group or form array with the specified identifier.
72
- * @summary Generates a FormGroup or FormArray based on the provided properties. If pages are specified
73
- * and greater than 1, creates a FormArray; otherwise creates a FormGroup. The form can optionally
74
- * be registered in the global form registry for later access throughout the application.
75
- *
76
- * @param {string} id - Unique identifier for the form
77
- * @param {Partial<IComponentInput>} [props={}] - Configuration properties for the form
78
- * @param {boolean} [registry=true] - Whether to register the form in the global registry
79
- * @return {FormGroup | FormArray} The created form instance
80
- *
81
- * @mermaid
82
- * sequenceDiagram
83
- * participant C as Component
84
- * participant NFS as NgxFormService
85
- * participant FR as Form Registry
86
- * participant AF as Angular Forms
87
- *
88
- * C->>NFS: createForm(id, props, registry)
89
- * NFS->>FR: Check if form exists
90
- * alt Form doesn't exist
91
- * alt props.pages > 1
92
- * NFS->>AF: new FormArray([])
93
- * else
94
- * NFS->>AF: new FormGroup({})
95
- * end
96
- * alt registry is true
97
- * NFS->>FR: addRegistry(id, form)
98
- * end
99
- * end
100
- * NFS-->>C: Return FormGroup | FormArray
101
- *
102
- * @static
103
- * @memberOf NgxFormService
104
- */
105
- static createForm(id, props = {}, registry = true) {
106
- const form = this.formRegistry.get(id) ?? (props?.pages && props?.pages > 1 ? new FormArray([]) : new FormGroup({}));
107
- if (!this.formRegistry.has(id) && registry)
108
- this.addRegistry(id, form);
109
- return form;
110
- }
111
- /**
112
- * @description Adds a form to the registry.
113
- * @summary Registers a FormGroup or FormArray with a unique identifier for global access throughout
114
- * the application. This allows forms to be retrieved and managed centrally. Throws an error if
115
- * the identifier is already in use to prevent conflicts.
116
- *
117
- * @param {string} formId - The unique identifier for the form
118
- * @param {FormParent} formGroup - The FormGroup or FormArray to be registered
119
- * @return {void}
120
- * @throws {Error} If a FormGroup with the given id is already registered
121
- *
122
- * @static
123
- * @memberOf NgxFormService
124
- */
125
- static addRegistry(formId, formGroup) {
126
- if (this.formRegistry.has(formId))
127
- throw new Error(`A FormGroup with id '${formId}' is already registered.`);
128
- this.formRegistry.set(formId, formGroup);
129
- }
130
- /**
131
- * @description Removes a form from the registry.
132
- * @summary Deletes a FormGroup or FormArray from the registry using its unique identifier.
133
- * This cleans up the registry and allows the identifier to be reused. The form itself
134
- * is not destroyed, only removed from the central registry.
135
- *
136
- * @param {string} formId - The unique identifier of the form to be removed
137
- * @return {void}
138
- *
139
- * @static
140
- * @memberOf NgxFormService
141
- */
142
- static removeRegistry(formId) {
143
- this.formRegistry.delete(formId);
144
- }
145
- /**
146
- * @description Resolves the parent group and control name from a path.
147
- * @summary Traverses the form group structure to find the parent group and control name for a given path.
148
- * Handles complex nested structures including arrays and sub-groups. Creates missing intermediate
149
- * groups as needed and properly configures FormArray controls for multiple value scenarios.
150
- *
151
- * @param {FormGroup} formGroup - The root FormGroup to traverse
152
- * @param {string} path - The dot-separated path to the control (e.g., 'user.address.street')
153
- * @param {IComponentInput} componentProps - Properties defining the component configuration
154
- * @param {KeyValue} parentProps - Properties from the parent component for context
155
- * @return {FormParentGroup} A tuple containing the parent FormGroup and the control name
156
- *
157
- * @private
158
- * @mermaid
159
- * sequenceDiagram
160
- * participant NFS as NgxFormService
161
- * participant FG as FormGroup
162
- * participant FA as FormArray
163
- *
164
- * NFS->>NFS: Split path into parts
165
- * loop For each path part
166
- * alt Control doesn't exist
167
- * alt isMultiple and part is childOf
168
- * NFS->>FA: new FormArray([new FormGroup({})])
169
- * else
170
- * NFS->>FG: new FormGroup({})
171
- * end
172
- * NFS->>FG: addControl(part, newControl)
173
- * end
174
- * NFS->>NFS: Navigate to next level
175
- * end
176
- * NFS-->>NFS: Return [parentGroup, controlName]
177
- *
178
- * @static
179
- * @memberOf NgxFormService
180
- */
181
- static resolveParentGroup(formGroup, path, componentProps, parentProps) {
182
- const isMultiple = parentProps?.['multiple'] || parentProps?.['type'] === 'Array' || false;
183
- const parts = path.split('.');
184
- const controlName = parts.pop();
185
- const { childOf } = componentProps;
186
- let currentGroup = formGroup;
187
- function setArrayComponentProps(formGroupArray) {
188
- const props = formGroupArray[AngularEngineKeys.FORM_GROUP_COMPONENT_PROPS] || {};
189
- if (!props[ModelKeys.MODEL][controlName])
190
- props[ModelKeys.MODEL] = Object.assign({}, props[ModelKeys.MODEL], { [controlName]: { ...componentProps } });
191
- }
192
- for (const part of parts) {
193
- if (!currentGroup.get(part)) {
194
- const partFormGroup = (isMultiple && part === childOf) ? new FormArray([new FormGroup({})]) : new FormGroup({});
195
- partFormGroup[AngularEngineKeys.FORM_GROUP_COMPONENT_PROPS] = {
196
- childOf: childOf || '',
197
- isMultiple: isMultiple,
198
- name: part,
199
- pk: componentProps?.['pk'] || parentProps?.['pk'] || '',
200
- [ModelKeys.MODEL]: {},
201
- };
202
- if (currentGroup instanceof FormArray) {
203
- currentGroup.push(partFormGroup);
204
- }
205
- else {
206
- for (const control of Object.values(partFormGroup.controls)) {
207
- if (control instanceof FormControl)
208
- this.register(control, componentProps);
209
- }
210
- if (partFormGroup instanceof AbstractControl)
211
- this.register(partFormGroup, componentProps);
212
- currentGroup.addControl(part, partFormGroup);
213
- }
214
- }
215
- if (childOf && currentGroup instanceof FormArray)
216
- setArrayComponentProps(currentGroup);
217
- currentGroup = currentGroup.get(part);
218
- }
219
- return [currentGroup, controlName];
220
- }
221
- /**
222
- * @description Retrieves component properties from a FormGroup or FormArray.
223
- * @summary Extracts component properties stored in the form group metadata. If a FormGroup is provided
224
- * and groupArrayName is specified, it will look for the FormArray within the form structure.
225
- *
226
- * @param {FormGroup | FormArray} formGroup - The form group or form array to extract properties from
227
- * @param {string} [key] - Optional key to retrieve a specific property
228
- * @param {string} [groupArrayName] - Optional name of the group array if formGroup is not a FormArray
229
- * @return {Partial<FieldProperties>} The component properties or a specific property if key is provided
230
- *
231
- * @static
232
- * @memberOf NgxFormService
233
- */
234
- static getComponentPropsFromGroupArray(formGroup, key, groupArrayName) {
235
- if (!(formGroup instanceof FormArray) && typeof groupArrayName === Primitives.STRING)
236
- formGroup = formGroup.root.get(groupArrayName) || {};
237
- const props = formGroup?.[AngularEngineKeys.FORM_GROUP_COMPONENT_PROPS] || {};
238
- return (!key ? props : props?.[key]) || {};
239
- }
240
- /**
241
- * @description Adds a new group to a parent FormArray.
242
- * @summary Creates and adds a new FormGroup to the specified parent FormArray based on the
243
- * component properties stored in the parent's metadata. This is used for dynamic form arrays
244
- * where new groups need to be added at runtime.
245
- *
246
- * @param {FormGroup} formGroup - The root form group containing the parent FormArray
247
- * @param {string} parentName - The name of the parent FormArray to add the group to
248
- * @param {number} [index=1] - The index position where the new group should be added
249
- * @return {FormGroup} The newly created and added FormGroup
250
- *
251
- * @static
252
- * @memberOf NgxFormService
253
- */
254
- static addGroupToParent(formGroup, parentName, index = 1) {
255
- const componentProps = this.getComponentPropsFromGroupArray(formGroup, ModelKeys.MODEL, parentName);
256
- Object.entries(componentProps).forEach(([, value]) => {
257
- return this.addFormControl(formGroup, value, { multiple: true }, index);
258
- });
259
- return this.getGroupFromParent(formGroup, parentName, index);
260
- }
261
- /**
262
- * @description Retrieves a FormGroup from a parent FormArray at the specified index.
263
- * @summary Gets a FormGroup from the specified parent FormArray. If the group doesn't exist
264
- * at the given index, it will create a new one using addGroupToParent.
265
- *
266
- * @param {FormGroup} formGroup - The root form group containing the parent FormArray
267
- * @param {string} parentName - The name of the parent FormArray to retrieve the group from
268
- * @param {number} [index=1] - The index of the group to retrieve
269
- * @return {FormGroup} The FormGroup at the specified index
270
- *
271
- * @static
272
- * @memberOf NgxFormService
273
- */
274
- static getGroupFromParent(formGroup, parentName, index = 1) {
275
- const childGroup = (formGroup.get(parentName) || formGroup).at(index);
276
- if (childGroup instanceof FormGroup)
277
- return childGroup;
278
- return this.addGroupToParent(formGroup, parentName, index);
279
- }
280
- /**
281
- * @description Checks if a value is unique within a FormArray group.
282
- * @summary Validates that the primary key value in a FormGroup is unique among all groups
283
- * in the parent FormArray. The uniqueness check behavior differs based on the operation type.
284
- *
285
- * @param {FormGroup} formGroup - The FormGroup to check for uniqueness
286
- * @param {number} index - The index of the current group within the FormArray
287
- * @param {OperationKeys} [operation=OperationKeys.CREATE] - The type of operation being performed
288
- * @return {boolean} True if the value is unique, false otherwise
289
- *
290
- * @static
291
- * @memberOf NgxFormService
292
- */
293
- static isUniqueOnGroup(formGroup, index, operation = OperationKeys.CREATE) {
294
- const formGroupArray = formGroup.parent;
295
- const pk = this.getComponentPropsFromGroupArray(formGroupArray, BaseComponentProps.PK);
296
- const controlName = Object.keys(formGroup.controls)[0];
297
- // only check for unique if is the pk control
298
- if (controlName !== pk)
299
- return true;
300
- const controlValue = cleanSpaces(`${formGroup.get(pk)?.value}`, true);
301
- if (operation === OperationKeys.CREATE)
302
- return !formGroupArray.controls.some((group, i) => i !== index && cleanSpaces(`${group.get(pk)?.value}`, true) === controlValue);
303
- return !formGroupArray.controls.some((group, i) => {
304
- const value = cleanSpaces(`${group.get(pk)?.value}`, true);
305
- return i !== index && controlValue === value;
306
- });
307
- }
308
- /**
309
- * @description Enables all controls within a FormGroup or FormArray.
310
- * @summary Recursively enables all form controls within the provided FormGroup or FormArray.
311
- * This is useful for making all controls interactive after they have been disabled.
312
- *
313
- * @param {FormArray | FormGroup} formGroup - The FormGroup or FormArray to enable all controls for
314
- * @return {void}
315
- *
316
- * @static
317
- * @memberOf NgxFormService
318
- */
319
- static enableAllGroupControls(formGroup) {
320
- Object.keys(formGroup.controls).forEach(key => {
321
- const control = formGroup.get(key);
322
- if (control instanceof FormArray) {
323
- control.controls.forEach(child => {
324
- if (child instanceof FormGroup) {
325
- child.enable({ emitEvent: false });
326
- child.updateValueAndValidity({ emitEvent: true });
327
- }
328
- });
329
- }
330
- });
331
- }
332
- /**
333
- * @description Adds a form control to a form group based on component properties.
334
- * @summary Creates and configures a FormControl within the specified FormGroup using the provided
335
- * component properties. Handles nested paths, multiple controls (FormArrays), and control registration.
336
- * This method supports complex form structures with nested groups and arrays.
337
- *
338
- * @param {FormGroup} formGroup - The form group to add the control to
339
- * @param {IComponentInput} componentProps - The component properties defining the control configuration
340
- * @param {KeyValue} [parentProps={}] - Properties from the parent component for context
341
- * @param {number} [index=0] - The index for multiple controls in FormArrays
342
- * @return {void}
343
- *
344
- * @private
345
- * @static
346
- * @memberOf NgxFormService
347
- */
348
- static addFormControl(formGroup, componentProps, parentProps = {}, index = 0) {
349
- const isMultiple = parentProps?.['multiple'] || parentProps?.['type'] === 'Array' || false;
350
- const { name, childOf, } = componentProps;
351
- if (isMultiple)
352
- componentProps['pk'] = componentProps['pk'] || parentProps?.['pk'] || '';
353
- const fullPath = childOf ? isMultiple ? `${childOf}.${index}.${name}` : `${childOf}.${name}` : name;
354
- const [parentGroup, controlName] = this.resolveParentGroup(formGroup, fullPath, componentProps, parentProps);
355
- if (!parentGroup.get(controlName)) {
356
- const control = NgxFormService.fromProps(componentProps, componentProps.updateMode || 'change');
357
- NgxFormService.register(control, componentProps);
358
- if (parentGroup instanceof FormGroup) {
359
- parentGroup.addControl(controlName, control);
360
- }
361
- if (parentGroup instanceof FormArray) {
362
- const root = parentGroup.controls[componentProps?.['page'] - 1];
363
- if (root) {
364
- root.addControl(controlName, control);
365
- }
366
- else {
367
- parentGroup.push({ [controlName]: control });
368
- }
369
- }
370
- }
371
- const root = parentGroup instanceof FormArray ? parentGroup.controls[componentProps?.['page'] - 1] : parentGroup;
372
- componentProps['formGroup'] = root;
373
- componentProps['formControl'] = parentGroup.get(controlName);
374
- componentProps['multiple'] = isMultiple;
375
- }
376
- /**
377
- * @description Retrieves a control from a registered form.
378
- * @summary Finds and returns an AbstractControl from a registered form using the form id and optional path.
379
- * This method provides centralized access to form controls across the application by leveraging
380
- * the form registry system.
381
- *
382
- * @param {string} formId - The unique identifier of the form in the registry
383
- * @param {string} [path] - The optional dot-separated path to a specific control within the form
384
- * @return {AbstractControl} The requested AbstractControl (FormGroup, FormArray, or FormControl)
385
- * @throws {Error} If the form is not found in the registry or the control is not found in the form
386
- *
387
- * @mermaid
388
- * sequenceDiagram
389
- * participant C as Component
390
- * participant NFS as NgxFormService
391
- * participant FR as Form Registry
392
- *
393
- * C->>NFS: getControlFromForm(formId, path?)
394
- * NFS->>FR: Get form by formId
395
- * alt Form not found
396
- * FR-->>NFS: null
397
- * NFS-->>C: Throw Error
398
- * else Form found
399
- * FR-->>NFS: Return form
400
- * alt path provided
401
- * NFS->>NFS: form.get(path)
402
- * alt Control not found
403
- * NFS-->>C: Throw Error
404
- * else
405
- * NFS-->>C: Return control
406
- * end
407
- * else
408
- * NFS-->>C: Return form
409
- * end
410
- * end
411
- *
412
- * @static
413
- * @memberOf NgxFormService
414
- */
415
- static getControlFromForm(formId, path) {
416
- const form = this.formRegistry.get(formId);
417
- if (!form)
418
- throw new Error(`Form with id '${formId}' not found in the registry.`);
419
- if (!path)
420
- return form;
421
- const control = form.get(path);
422
- if (!control)
423
- throw new Error(`Control with path '${path}' not found in form '${formId}'.`);
424
- return control;
425
- }
426
- /**
427
- * @description Creates a form from UI model metadata children.
428
- * @summary Generates a FormGroup from an array of UIModelMetadata objects, extracting component
429
- * properties and creating appropriate form controls. This method is specifically designed to work
430
- * with the UI decorator system and provides automatic form generation from metadata.
431
- *
432
- * @param {string} id - Unique identifier for the form
433
- * @param {boolean} [registry=false] - Whether to register the created form in the global registry
434
- * @param {UIModelMetadata[]} [children] - Array of UI model metadata objects to create controls from
435
- * @return {FormGroup} The created FormGroup with controls for each child metadata
436
- *
437
- * @mermaid
438
- * sequenceDiagram
439
- * participant C as Component
440
- * participant NFS as NgxFormService
441
- * participant AF as Angular Forms
442
- *
443
- * C->>NFS: createFormFromChildren(id, registry, children)
444
- * NFS->>AF: new FormGroup({})
445
- * loop For each child metadata
446
- * NFS->>NFS: addFormControl(form, child.props)
447
- * NFS->>AF: Create and add FormControl
448
- * end
449
- * alt registry is true
450
- * NFS->>NFS: addRegistry(id, form)
451
- * end
452
- * NFS-->>C: Return FormGroup
453
- *
454
- * @static
455
- * @memberOf NgxFormService
456
- */
457
- static createFormFromChildren(id, registry = false, children) {
458
- const form = new FormGroup({});
459
- if (children?.length)
460
- children.forEach(child => {
461
- this.addFormControl(form, child.props);
462
- });
463
- if (registry)
464
- this.addRegistry(id, form);
465
- return form;
466
- }
467
- /**
468
- * @description Creates a form from component configurations.
469
- * @summary Generates a FormGroup based on an array of component configurations and optionally registers it.
470
- * This method processes component input configurations to create appropriate form controls with
471
- * validation and initial values.
472
- *
473
- * @param {string} id - The unique identifier for the form
474
- * @param {IComponentConfig[]} components - An array of component configurations defining the form structure
475
- * @param {boolean} [registry=false] - Whether to register the created form in the global registry
476
- * @return {FormGroup} The created FormGroup with controls for each component configuration
477
- *
478
- * @mermaid
479
- * sequenceDiagram
480
- * participant C as Component
481
- * participant NFS as NgxFormService
482
- * participant AF as Angular Forms
483
- *
484
- * C->>NFS: createFormFromComponents(id, components, registry)
485
- * NFS->>AF: new FormGroup({})
486
- * loop For each component config
487
- * NFS->>NFS: addFormControl(form, component.inputs)
488
- * NFS->>AF: Create and add FormControl
489
- * end
490
- * alt registry is true
491
- * NFS->>NFS: addRegistry(id, form)
492
- * end
493
- * NFS-->>C: Return FormGroup
494
- *
495
- * @static
496
- * @memberOf NgxFormService
497
- */
498
- static createFormFromComponents(id, components, registry = false) {
499
- const form = new FormGroup({});
500
- components.forEach(component => {
501
- this.addFormControl(form, component.inputs);
502
- });
503
- if (registry)
504
- this.addRegistry(id, form);
505
- return form;
506
- }
507
- /**
508
- * @description Adds a control to a form based on component properties.
509
- * @summary Creates and adds a form control to a form (existing or new) based on the provided component properties.
510
- * Handles multi-page forms by managing FormArray structures and proper indexing. This method supports
511
- * complex form scenarios including nested controls and page-based form organization.
512
- *
513
- * @param {string} id - The unique identifier of the form
514
- * @param {FieldProperties} componentProperties - The properties of the component to create the control from
515
- * @param {FieldProperties} [parentProps] - Optional parent properties for context and configuration
516
- * @return {AbstractControl} The form or created control
517
- *
518
- * @mermaid
519
- * sequenceDiagram
520
- * participant C as Component
521
- * participant NFS as NgxFormService
522
- * participant F as Form
523
- *
524
- * C->>NFS: addControlFromProps(id, componentProps, parentProps?)
525
- * NFS->>NFS: createForm(id, parentProps, true)
526
- * alt Multi-page form (parentProps.pages > 1)
527
- * NFS->>NFS: Calculate page index
528
- * NFS->>F: Get or create FormGroup at index
529
- * NFS->>NFS: Set form to page FormGroup
530
- * end
531
- * alt componentProperties has path
532
- * NFS->>NFS: addFormControl(form, componentProperties, parentProps)
533
- * end
534
- * NFS-->>C: Return form/control
535
- *
536
- * @static
537
- * @memberOf NgxFormService
538
- */
539
- static addControlFromProps(id, componentProperties, parentProps) {
540
- let form = this.createForm(id, parentProps, true);
541
- const formLength = form.length;
542
- if (parentProps?.pages && parentProps?.pages > 1) {
543
- let index = componentProperties.page || parentProps.page;
544
- if (!(typeof index === 'number') || index === 0)
545
- throw Error(`Property 'page' is required and greather than 0 on ${componentProperties.name}`);
546
- if (index > formLength) {
547
- if (form?.['lastIndex'] && index === form['lastIndex']['page']) {
548
- index = form['lastIndex']['index'];
549
- }
550
- else {
551
- form['lastIndex'] = {
552
- page: index,
553
- index: formLength + 1
554
- };
555
- index = formLength + 1;
556
- }
557
- }
558
- let group = form.controls[index - 1];
559
- if (!group) {
560
- group = new FormGroup({});
561
- form.insert(index, group);
562
- }
563
- form = group;
564
- }
565
- if (componentProperties.path)
566
- this.addFormControl(form, componentProperties, parentProps);
567
- return form;
568
- }
569
- /**
570
- * @description Retrieves form data from a FormGroup.
571
- * @summary Extracts and processes the data from a FormGroup, handling different input types and nested form groups.
572
- * Performs type conversion for various HTML5 input types, validates nested controls, and manages
573
- * multiple control scenarios. Automatically enables all group controls after data extraction.
574
- *
575
- * @param {FormGroup} formGroup - The FormGroup to extract data from
576
- * @return {Record<string, unknown>} An object containing the processed form data with proper type conversions
577
- *
578
- * @mermaid
579
- * sequenceDiagram
580
- * participant C as Component
581
- * participant NFS as NgxFormService
582
- * participant FG as FormGroup
583
- * participant FC as FormControl
584
- *
585
- * C->>NFS: getFormData(formGroup)
586
- * loop For each control in formGroup
587
- * alt Control is not FormControl
588
- * NFS->>NFS: Recursive getFormData(control)
589
- * alt parentProps.multiple and !isValid
590
- * NFS->>NFS: reset(control)
591
- * end
592
- * else Control is FormControl
593
- * NFS->>FC: Get control value
594
- * NFS->>NFS: Apply type conversion based on props.type
595
- * alt HTML5CheckTypes
596
- * NFS->>NFS: Keep boolean value
597
- * else NUMBER type
598
- * NFS->>NFS: parseToNumber(value)
599
- * else DATE/DATETIME types
600
- * NFS->>NFS: new Date(value)
601
- * else Other types
602
- * NFS->>NFS: escapeHtml(value)
603
- * end
604
- * end
605
- * end
606
- * NFS->>NFS: enableAllGroupControls(formGroup)
607
- * NFS-->>C: Return processed data object
608
- *
609
- * @static
610
- * @memberOf NgxFormService
611
- */
612
- static getFormData(formGroup) {
613
- const data = {};
614
- for (const key in formGroup.controls) {
615
- const control = formGroup.controls[key];
616
- const parentProps = NgxFormService.getPropsFromControl(formGroup);
617
- if (!(control instanceof FormControl)) {
618
- const value = NgxFormService.getFormData(control);
619
- const isValid = control.valid;
620
- if (parentProps.multiple) {
621
- if (isValid) {
622
- data[key] = value;
623
- }
624
- else {
625
- this.reset(control);
626
- }
627
- continue;
628
- }
629
- data[key] = value;
630
- continue;
631
- }
632
- const props = NgxFormService.getPropsFromControl(control);
633
- let value = control.value;
634
- if (!HTML5CheckTypes.includes(props['type'])) {
635
- switch (props['type']) {
636
- case HTML5InputTypes.NUMBER:
637
- value = parseToNumber(value);
638
- break;
639
- case HTML5InputTypes.DATE:
640
- case HTML5InputTypes.DATETIME_LOCAL:
641
- value = new Date(value);
642
- break;
643
- default:
644
- value = escapeHtml(value);
645
- }
646
- }
647
- data[key] = value;
648
- }
649
- NgxFormService.enableAllGroupControls(formGroup);
650
- return data;
651
- }
652
- /**
653
- * @description Validates fields in a form control or form group.
654
- * @summary Recursively validates all fields in a form control or form group, marking them as touched and dirty.
655
- * Performs comprehensive validation including uniqueness checks for primary keys in FormArray scenarios.
656
- * This method ensures all validation rules are applied and form state is properly updated.
657
- *
658
- * @param {AbstractControl} control - The control or form group to validate
659
- * @param {string} [pk] - Optional primary key field name for uniqueness validation
660
- * @param {string} [path] - The path to the control within the form for error reporting
661
- * @return {boolean} True if all fields are valid, false otherwise
662
- * @throws {Error} If no control is found at the specified path or if the control type is unknown
663
- *
664
- * @mermaid
665
- * sequenceDiagram
666
- * participant C as Component
667
- * participant NFS as NgxFormService
668
- * participant FC as FormControl
669
- * participant FG as FormGroup
670
- * participant FA as FormArray
671
- *
672
- * C->>NFS: validateFields(control, pk?, path?)
673
- * alt Control is FormControl
674
- * NFS->>FC: markAsTouched()
675
- * NFS->>FC: markAsDirty()
676
- * NFS->>FC: updateValueAndValidity()
677
- * alt Is in FormArray group
678
- * NFS->>NFS: Check uniqueness in group
679
- * alt Not unique
680
- * NFS->>FC: setErrors({notUnique: true})
681
- * end
682
- * end
683
- * NFS-->>C: Return control.valid
684
- * else Control is FormGroup
685
- * loop For each child control
686
- * NFS->>NFS: Recursive validateFields(child)
687
- * end
688
- * NFS-->>C: Return allValid
689
- * else Control is FormArray
690
- * loop For each array control
691
- * NFS->>NFS: Recursive validateFields(child)
692
- * end
693
- * NFS-->>C: Return allValid
694
- * else Unknown control type
695
- * NFS-->>C: Throw Error
696
- * end
697
- *
698
- * @static
699
- * @memberOf NgxFormService
700
- */
701
- static validateFields(control, pk, path) {
702
- control = path ? control.get(path) : control;
703
- if (!control)
704
- throw new Error(`No control found at path: ${path || 'root'}.`);
705
- const isAllowed = [FormArray, FormGroup, FormControl].some(type => control instanceof type);
706
- if (!isAllowed)
707
- throw new Error(`Unknown control type at: ${path || 'root'}`);
708
- control.markAsTouched();
709
- control.markAsDirty();
710
- control.updateValueAndValidity({ emitEvent: true });
711
- if (control instanceof FormGroup) {
712
- Object.values(control.controls).forEach(childControl => {
713
- this.validateFields(childControl);
714
- });
715
- }
716
- if (control instanceof FormArray) {
717
- const totalGroups = control.length;
718
- const hasValid = control.controls.some(control => control.valid);
719
- if (totalGroups > 1 && hasValid) {
720
- for (let i = control.length - 1; i >= 0; i--) {
721
- const childControl = control.at(i);
722
- // disable no valid groups on array
723
- if (!childControl.valid) {
724
- childControl.parent.setErrors(null);
725
- childControl.parent.updateValueAndValidity({ emitEvent: true });
726
- childControl.disable();
727
- }
728
- else {
729
- this.validateFields(childControl);
730
- }
731
- }
732
- }
733
- else {
734
- Object.values(control.controls).forEach(childControl => {
735
- this.validateFields(childControl);
736
- });
737
- }
738
- }
739
- // function getControlName(control: AbstractControl): string | null {
740
- // const group = control.parent as FormGroup;
741
- // if (!group)
742
- // return null;
743
- // return Object.keys(group.controls).find(name => control === group.get(name)) || null;
744
- // }
745
- return control.valid;
746
- }
747
- /**
748
- * @description Generates validators from component properties.
749
- * @summary Creates an array of ValidatorFn based on the supported validation keys in the component properties.
750
- * @param {FieldProperties} props - The component properties.
751
- * @return {ValidatorFn[]} An array of validator functions.
752
- */
753
- static validatorsFromProps(props) {
754
- const supportedValidationKeys = Validation.keys();
755
- return Object.keys(props)
756
- .filter((k) => supportedValidationKeys.includes(k))
757
- .map((k) => {
758
- return ValidatorFactory.spawn(props, k);
759
- });
760
- }
761
- /**
762
- * @description Creates a FormControl from component properties.
763
- * @summary Generates a FormControl with validators and initial configuration based on the provided
764
- * component properties. Handles different input types, sets initial values, and configures
765
- * validation rules and update modes.
766
- *
767
- * @param {FieldProperties} props - The component properties defining the control configuration
768
- * @param {FieldUpdateMode} [updateMode='change'] - The update mode for the control ('change', 'blur', 'submit')
769
- * @return {FormControl} The created FormControl with proper configuration and validators
770
- *
771
- * @mermaid
772
- * sequenceDiagram
773
- * participant C as Component
774
- * participant NFS as NgxFormService
775
- * participant VF as ValidatorFactory
776
- * participant AF as Angular Forms
777
- *
778
- * C->>NFS: fromProps(props, updateMode?)
779
- * NFS->>NFS: validatorsFromProps(props)
780
- * NFS->>VF: Create validators from props
781
- * VF-->>NFS: Return validator array
782
- * NFS->>NFS: Compose validators
783
- * alt props.value exists and not checkbox
784
- * alt props.type is DATE
785
- * NFS->>NFS: Validate date format
786
- * end
787
- * NFS->>NFS: Set initial value
788
- * end
789
- * NFS->>AF: new FormControl(config)
790
- * AF-->>NFS: Return FormControl
791
- * NFS-->>C: Return configured FormControl
792
- *
793
- * @static
794
- * @memberOf NgxFormService
795
- */
796
- static fromProps(props, updateMode = 'change') {
797
- const validators = this.validatorsFromProps(props);
798
- const composed = validators.length ? Validators.compose(validators) : null;
799
- return new FormControl({
800
- value: props.value && props.type !== HTML5InputTypes.CHECKBOX
801
- ? props.type === HTML5InputTypes.DATE
802
- ? !isValidDate(parseDate(props.format, props.value))
803
- ? undefined : props.value :
804
- props.value : undefined,
805
- disabled: props.disabled,
806
- }, {
807
- validators: composed,
808
- updateOn: updateMode,
809
- });
810
- }
811
- /**
812
- * @description Retrieves properties from a FormControl, FormArray, or FormGroup.
813
- * @summary Gets the FieldProperties associated with a form control from the internal WeakMap.
814
- * This method provides access to the original component properties that were used to create
815
- * the control, enabling validation, rendering, and behavior configuration.
816
- *
817
- * @param {FormControl | FormArray | FormGroup} control - The form control to get properties for
818
- * @return {FieldProperties} The properties associated with the control, or empty object if not found
819
- *
820
- * @static
821
- * @memberOf NgxFormService
822
- */
823
- static getPropsFromControl(control) {
824
- return this.controls.get(control) || {};
825
- }
826
- /**
827
- * @description Finds a parent element with a specific tag in the DOM tree.
828
- * @summary Traverses up the DOM tree to find the nearest parent element with the specified tag name.
829
- * This is useful for finding container elements or specific parent components in the DOM hierarchy.
830
- * The search is case-insensitive for tag name matching.
831
- *
832
- * @param {HTMLElement} el - The starting element to traverse from
833
- * @param {string} tag - The tag name to search for (case-insensitive)
834
- * @return {HTMLElement} The found parent element with the specified tag
835
- * @throws {Error} If no parent with the specified tag is found in the DOM tree
836
- *
837
- * @mermaid
838
- * sequenceDiagram
839
- * participant C as Component
840
- * participant NFS as NgxFormService
841
- * participant DOM as DOM Tree
842
- *
843
- * C->>NFS: getParentEl(element, tagName)
844
- * loop Traverse up DOM tree
845
- * NFS->>DOM: Get parentElement
846
- * DOM-->>NFS: Return parent or null
847
- * alt Parent exists and tag matches
848
- * NFS-->>C: Return parent element
849
- * else Parent is null
850
- * NFS-->>C: Throw Error
851
- * end
852
- * end
853
- *
854
- * @static
855
- * @memberOf NgxFormService
856
- */
857
- static getParentEl(el, tag) {
858
- let parent;
859
- while ((parent = el.parentElement) !== null) {
860
- if (parent.tagName.toLowerCase() === tag.toLowerCase()) {
861
- return parent;
862
- }
863
- el = parent;
864
- }
865
- throw new Error(`No parent with the tag ${tag} was found for provided element`);
866
- }
867
- /**
868
- * @description Registers a control with its properties in the internal WeakMap.
869
- * @summary Associates a form control with its component properties for later retrieval.
870
- * This enables the service to maintain metadata about controls without creating memory leaks,
871
- * as WeakMap automatically cleans up references when controls are garbage collected.
872
- *
873
- * @param {AbstractControl} control - The control to register (FormControl, FormGroup, or FormArray)
874
- * @param {FieldProperties} props - The properties to associate with the control
875
- * @return {void}
876
- *
877
- * @static
878
- * @memberOf NgxFormService
879
- */
880
- static register(control, props) {
881
- this.controls.set(control, props);
882
- }
883
- /**
884
- * @description Unregisters a control.
885
- * @summary Removes a control and its associated properties from the internal WeakMap.
886
- * @param {AbstractControl} control - The control to unregister.
887
- * @return {boolean} True if the control was successfully unregistered, false otherwise.
888
- */
889
- static unregister(control) {
890
- return this.controls.delete(control);
891
- }
892
- /**
893
- * @description Resets a form group.
894
- * @summary Recursively resets all controls in a form group, clearing values, errors, and marking them as pristine and untouched.
895
- * @param {FormGroup} formGroup - The form group to reset.
896
- */
897
- static reset(formGroup) {
898
- if (formGroup instanceof FormControl) {
899
- const control = formGroup;
900
- const { type } = NgxFormService.getPropsFromControl(control);
901
- if (!HTML5CheckTypes.includes(type))
902
- control.setValue("");
903
- control.markAsPristine();
904
- control.markAsUntouched();
905
- control.setErrors(null);
906
- control.updateValueAndValidity();
907
- }
908
- else {
909
- for (const key in formGroup.controls) {
910
- const control = formGroup.controls[key];
911
- NgxFormService.reset(control);
912
- continue;
913
- }
914
- }
915
- }
916
- }
917
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTmd4Rm9ybVNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2VuZ2luZS9OZ3hGb3JtU2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFtQixlQUFlLEVBQUUsZUFBZSxFQUFFLGFBQWEsRUFBbUIsTUFBTSx5QkFBeUIsQ0FBQztBQUd4SSxPQUFPLEVBQUUsZUFBZSxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFlLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdHLE9BQU8sRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDM0csT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDdEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUN6QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLGtCQUFrQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFHNUU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FxQ0c7QUFDSCxNQUFNLE9BQU8sY0FBYztJQUN6Qjs7Ozs7Ozs7O09BU0c7YUFDWSxhQUFRLEdBQThDLElBQUksT0FBTyxFQUFvQyxDQUFDO0lBRXJIOzs7Ozs7Ozs7T0FTRzthQUNZLGlCQUFZLEdBQTRCLElBQUksR0FBRyxFQUFzQixDQUFDO2FBRXRFLGVBQVUsR0FBYSxFQUFFLENBQUM7SUFFekM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FrQ0c7SUFDSCxNQUFNLENBQUMsVUFBVSxDQUFDLEVBQVUsRUFBRSxRQUFrQyxFQUFFLEVBQUUsV0FBb0IsSUFBSTtRQUMxRixNQUFNLElBQUksR0FBRyxJ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RSxDQUFDO1FBQ25DLENBQUM7YUFBTSxDQUFDO1lBQ04sS0FBSyxNQUFNLEdBQUcsSUFBSSxTQUFTLENBQUMsUUFBUSxFQUFFLENBQUM7Z0JBQ3JDLE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7Z0JBQ3hDLGNBQWMsQ0FBQyxLQUFLLENBQUMsT0FBc0IsQ0FBQyxDQUFDO2dCQUM3QyxTQUFTO1lBQ1gsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZXNjYXBlSHRtbCwgRmllbGRQcm9wZXJ0aWVzLCBIVE1MNUNoZWNrVHlwZXMsIEhUTUw1SW5wdXRUeXBlcywgcGFyc2VUb051bWJlciwgVUlNb2RlbE1ldGFkYXRhIH0gZnJvbSAnQGRlY2FmLXRzL3VpLWRlY29yYXRvcnMnO1xuaW1wb3J0IHsgRmllbGRVcGRhdGVNb2RlLCBGb3JtUGFyZW50LCBGb3JtUGFyZW50R3JvdXAsIEtleVZhbHVlIH0gZnJvbSAnLi90eXBlcyc7XG5pbXBvcnQgeyBJQ29tcG9uZW50Q29uZmlnLCBJQ29tcG9uZW50SW5wdXQgfSBmcm9tICcuL2ludGVyZmFjZXMnO1xuaW1wb3J0IHsgQWJzdHJhY3RDb250cm9sLCBGb3JtQXJyYXksIEZvcm1Db250cm9sLCBGb3JtR3JvdXAsIFZhbGlkYXRvckZuLCBWYWxpZGF0b3JzIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgaXNWYWxpZERhdGUsIE1vZGVsS2V5cywgcGFyc2VEYXRlLCBQcmltaXRpdmVzLCBWYWxpZGF0aW9uIH0gZnJvbSAnQGRlY2FmLXRzL2RlY29yYXRvci12YWxpZGF0aW9uJztcbmltcG9ydCB7IFZhbGlkYXRvckZhY3RvcnkgfSBmcm9tICcuL1ZhbGlkYXRvckZhY3RvcnknO1xuaW1wb3J0IHsgY2xlYW5TcGFjZXMgfSBmcm9tICcuLi9oZWxwZXJzJztcbmltcG9ydCB7IE9wZXJhdGlvbktleXMgfSBmcm9tICdAZGVjYWYtdHMvZGItZGVjb3JhdG9ycyc7XG5pbXBvcnQgeyBBbmd1bGFyRW5naW5lS2V5cywgQmFzZUNvbXBvbmVudFByb3BzIH0gZnJvbSAnLi4vZW5naW5lL2NvbnN0YW50cyc7XG5cblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gU2VydmljZSBmb3IgbWFuYWdpbmcgQW5ndWxhciBmb3JtcyBhbmQgZm9ybSBjb250cm9scy5cbiAqIEBzdW1tYXJ5IFRoZSBOZ3hGb3JtU2VydmljZSBwcm92aWRlcyB1dGlsaXR5IG1ldGhvZHMgZm9yIGNyZWF0aW5nLCBtYW5hZ2luZywgYW5kIHZhbGlkYXRpbmcgQW5ndWxhciBmb3JtcyBhbmQgZm9ybSBjb250cm9scy4gSXQgaW5jbHVkZXMgZnVuY3Rpb25hbGl0eSBmb3IgcmVnaXN0ZXJpbmcgZm9ybXMsIGFkZGluZyBjb250cm9scywgdmFsaWRhdGluZyBmaWVsZHMsIGFuZCBoYW5kbGluZyBmb3JtIGRhdGEuXG4gKlxuICogQGNsYXNzXG4gKiBAcGFyYW0ge1dlYWtNYXA8QWJzdHJhY3RDb250cm9sLCBGaWVsZFByb3BlcnRpZXM+fSBjb250cm9scyAtIEEgV2Vha01hcCB0byBzdG9yZSBjb250cm9sIHByb3BlcnRpZXMuXG4gKiBAcGFyYW0ge01hcDxzdHJpbmcsIEZvcm1Hcm91cD59IGZvcm1SZWdpc3RyeSAtIEEgTWFwIHRvIHN0b3JlIHJlZ2lzdGVyZWQgZm9ybXMuXG4gKlxuICogQGV4YW1wbGVcbiAqIC8vIENyZWF0aW5nIGEgZm9ybSBmcm9tIGNvbXBvbmVudHNcbiAqIGNvbnN0IGNvbXBvbmVudHMgPSBbXG4gKiAgIHsgaW5wdXRzOiB7IG5hbWU6ICd1c2VybmFtZScsIHR5cGU6ICd0ZXh0JywgcmVxdWlyZWQ6IHRydWUgfSB9LFxuICogICB7IGlucHV0czogeyBuYW1lOiAncGFzc3dvcmQnLCB0eXBlOiAncGFzc3dvcmQnLCBtaW5MZW5ndGg6IDggfSB9XG4gKiBdO1xuICogY29uc3QgZm9ybSA9IE5neEZvcm1TZXJ2aWNlLmNyZWF0ZUZvcm1Gcm9tQ29tcG9uZW50cygnbG9naW5Gb3JtJywgY29tcG9uZW50cywgdHJ1ZSk7XG4gKlxuICogLy8gVmFsaWRhdGluZyBmaWVsZHNcbiAqIE5neEZvcm1TZXJ2aWNlLnZhbGlkYXRlRmllbGRzKGZvcm0pO1xuICpcbiAqIC8vIEdldHRpbmcgZm9ybSBkYXRhXG4gKiBjb25zdCBmb3JtRGF0YSA9IE5neEZvcm1TZXJ2aWNlLmdldEZvcm1EYXRhKGZvcm0pO1xuICpcbiAqIEBtZXJtYWlkXG4gKiBzZXF1ZW5jZURpYWdyYW1cbiAqICAgcGFydGljaXBhbnQgQyBhcyBDb21wb25lbnRcbiAqICAgcGFydGljaXBhbnQgTkZTIGFzIE5neEZvcm1TZXJ2aWNlXG4gKiAgIHBhcnRpY2lwYW50IEFGIGFzIEFuZ3VsYXIgRm9ybXNcbiAqICAgQy0+Pk5GUzogY3JlYXRlRm9ybUZyb21Db21wb25lbnRzKClcbiAqICAgTkZTLT4+QUY6IG5ldyBGb3JtR3JvdXAoKVxuICogICBORlMtPj5ORlM6IGFkZEZvcm1Db250cm9sKClcbiAqICAgTkZTLT4+QUY6IGFkZENvbnRyb2woKVxuICogICBORlMtLT4+QzogUmV0dXJuIEZvcm1Hcm91cFxuICogICBDLT4+TkZTOiB2YWxpZGF0ZUZpZWxkcygpXG4gKiAgIE5GUy0+PkFGOiBtYXJrQXNUb3VjaGVkKCksIG1hcmtBc0RpcnR5KCksIHVwZGF0ZVZhbHVlQW5kVmFsaWRpdHkoKVxuICogICBDLT4+TkZTOiBnZXRGb3JtRGF0YSgpXG4gKiAgIE5GUy0+PkFGOiBHZXQgY29udHJvbCB2YWx1ZXNcbiAqICAgTkZTLS0+PkM6IFJldHVybiBmb3JtIGRhdGFcbiAqL1xuZXhwb3J0IGNsYXNzIE5neEZvcm1TZXJ2aWNlIHtcbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBXZWFrTWFwIHRoYXQgc3RvcmVzIGNvbnRyb2wgcHJvcGVydGllcyBmb3IgZm9ybSBjb250cm9scy5cbiAgICogQHN1bW1hcnkgQSBXZWFrTWFwIHRoYXQgYXNzb2NpYXRlcyBBYnN0cmFjdENvbnRyb2wgaW5zdGFuY2VzIHdpdGggdGhlaXIgY29ycmVzcG9uZGluZyBGaWVsZFByb3BlcnRpZXMuXG4gICAqIFRoaXMgYWxsb3dzIHRoZSBzZXJ2aWNlIHRvIHRyYWNrIG1ldGFkYXRhIGZvciBmb3JtIGNvbnRyb2xzIHdpdGhvdXQgY3JlYXRpbmcgbWVtb3J5IGxlYWtzLlxuICAgKlxuICAgKiBAdHlwZSB7V2Vha01hcDxBYnN0cmFjdENvbnRyb2wsIEZpZWxkUHJvcGVydGllcz59XG4gICAqIEBwcml2YXRlXG4gICAqIEBzdGF0aWNcbiAgICogQG1lbWJlck9mIE5neEZvcm1TZXJ2aWNlXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBjb250cm9sczogV2Vha01hcDxBYnN0cmFjdENvbnRyb2wsIEZpZWxkUHJvcGVydGllcz4gPSBuZXcgV2Vha01hcDxBYnN0cmFjdENvbnRyb2wsIEZpZWxkUHJvcGVydGllcz4oKTtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFJlZ2lzdHJ5IG9mIGZvcm0gZ3JvdXBzIGluZGV4ZWQgYnkgdGhlaXIgdW5pcXVlIGlkZW50aWZpZXJzLlxuICAgKiBAc3VtbWFyeSBBIE1hcCB0aGF0IHN0b3JlcyBGb3JtR3JvdXAgaW5zdGFuY2VzIHdpdGggdGhlaXIgdW5pcXVlIHN0cmluZyBpZGVudGlmaWVycy5cbiAgICogVGhpcyBhbGxvd3MgZ2xvYmFsIGFjY2VzcyB0byByZWdpc3RlcmVkIGZvcm1zIHRocm91Z2hvdXQgdGhlIGFwcGxpY2F0aW9uLlxuICAgKlxuICAgKiBAdHlwZSB7TWFwPHN0cmluZywgRm9ybUdyb3VwPn1cbiAgICogQHByaXZhdGVcbiAgICogQHN0YXRpY1xuICAgKiBAbWVtYmVyT2YgTmd4Rm9ybVNlcnZpY2VcbiAgICovXG4gIHByaXZhdGUgc3RhdGljIGZvcm1SZWdpc3RyeTogTWFwPHN0cmluZywgRm9ybVBhcmVudD4gPSBuZXcgTWFwPHN0cmluZywgRm9ybVBhcmVudD4oKTtcblxuICBwcml2YXRlIHN0YXRpYyBwYWdlTWFwcGVyOiBLZXlWYWx1ZSA9IHt9O1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ3JlYXRlcyBhIG5ldyBmb3JtIGdyb3VwIG9yIGZvcm0gYXJyYXkgd2l0aCB0aGUgc3BlY2lmaWVkIGlkZW50aWZpZXIuXG4gICAqIEBzdW1tYXJ5IEdlbmVyYXRlcyBhIEZvcm1Hcm91cCBvciBGb3JtQXJyYXkgYmFzZWQgb24gdGhlIHByb3ZpZGVkIHByb3BlcnRpZXMuIElmIHBhZ2VzIGFyZSBzcGVjaWZpZWRcbiAgICogYW5kIGdyZWF0ZXIgdGhhbiAxLCBjcmVhdGVzIGEgRm9ybUFycmF5OyBvdGhlcndpc2UgY3JlYXRlcyBhIEZvcm1Hcm91cC4gVGhlIGZvcm0gY2FuIG9wdGlvbmFsbHlcbiAgICogYmUgcmVnaXN0ZXJlZCBpbiB0aGUgZ2xvYmFsIGZvcm0gcmVnaXN0cnkgZm9yIGxhdGVyIGFjY2VzcyB0aHJvdWdob3V0IHRoZSBhcHBsaWNhdGlvbi5cbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkIC0gVW5pcXVlIGlkZW50aWZpZXIgZm9yIHRoZSBmb3JtXG4gICAqIEBwYXJhbSB7UGFydGlhbDxJQ29tcG9uZW50SW5wdXQ+fSBbcHJvcHM9e31dIC0gQ29uZmlndXJhdGlvbiBwcm9wZXJ0aWVzIGZvciB0aGUgZm9ybVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtyZWdpc3RyeT10cnVlXSAtIFdoZXRoZXIgdG8gcmVnaXN0ZXIgdGhlIGZvcm0gaW4gdGhlIGdsb2JhbCByZWdpc3RyeVxuICAgKiBAcmV0dXJuIHtGb3JtR3JvdXAgfCBGb3JtQXJyYXl9IFRoZSBjcmVhdGVkIGZvcm0gaW5zdGFuY2VcbiAgICpcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgQyBhcyBDb21wb25lbnRcbiAgICogICBwYXJ0aWNpcGFudCBORlMgYXMgTmd4Rm9ybVNlcnZpY2VcbiAgICogICBwYXJ0aWNpcGFudCBGUiBhcyBGb3JtIFJlZ2lzdHJ5XG4gICAqICAgcGFydGljaXBhbnQgQUYgYXMgQW5ndWxhciBGb3Jtc1xuICAgKlxuICAgKiAgIEMtPj5ORlM6IGNyZWF0ZUZvcm0oaWQsIHByb3BzLCByZWdpc3RyeSlcbiAgICogICBORlMtPj5GUjogQ2hlY2sgaWYgZm9ybSBleGlzdHNcbiAgICogICBhbHQgRm9ybSBkb2Vzbid0IGV4aXN0XG4gICAqICAgICBhbHQgcHJvcHMucGFnZXMgPiAxXG4gICAqICAgICAgIE5GUy0+PkFGOiBuZXcgRm9ybUFycmF5KFtdKVxuICAgKiAgICAgZWxzZVxuICAgKiAgICAgICBORlMtPj5BRjogbmV3IEZvcm1Hcm91cCh7fSlcbiAgICogICAgIGVuZFxuICAgKiAgICAgYWx0IHJlZ2lzdHJ5IGlzIHRydWVcbiAgICogICAgICAgTkZTLT4+RlI6IGFkZFJlZ2lzdHJ5KGlkLCBmb3JtKVxuICAgKiAgICAgZW5kXG4gICAqICAgZW5kXG4gICAqICAgTkZTLS0+PkM6IFJldHVybiBGb3JtR3JvdXAgfCBGb3JtQXJyYXlcbiAgICpcbiAgICogQHN0YXRpY1xuICAgKiBAbWVtYmVyT2YgTmd4Rm9ybVNlcnZpY2VcbiAgICovXG4gIHN0YXRpYyBjcmVhdGVGb3JtKGlkOiBzdHJpbmcsIHByb3BzOiBQYXJ0aWFsPElDb21wb25lbnRJbnB1dD4gPSB7fSwgcmVnaXN0cnk6IGJvb2xlYW4gPSB0cnVlKTogRm9ybUdyb3VwIHwgRm9ybUFycmF5IHtcbiAgICBjb25zdCBmb3JtID0gdGhpcy5mb3JtUmVnaXN0cnkuZ2V0KGlkKSA/PyAocHJvcHM/LnBhZ2VzICYmIHByb3BzPy5wYWdlcyAgPiAxID8gbmV3IEZvcm1BcnJheShbXSkgOiBuZXcgRm9ybUdyb3VwKHt9KSk7XG4gICAgaWYgKCF0aGlzLmZvcm1SZWdpc3RyeS5oYXMoaWQpICYmIHJlZ2lzdHJ5KVxuICAgICAgdGhpcy5hZGRSZWdpc3RyeShpZCwgZm9ybSBhcyBGb3JtQXJyYXkgfCBGb3JtR3JvdXApO1xuICAgIHJldHVybiBmb3JtIGFzIEZvcm1BcnJheSB8IEZvcm1Hcm91cDtcbiAgfVxuXG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBBZGRzIGEgZm9ybSB0byB0aGUgcmVnaXN0cnkuXG4gICAqIEBzdW1tYXJ5IFJlZ2lzdGVycyBhIEZvcm1Hcm91cCBvciBGb3JtQXJyYXkgd2l0aCBhIHVuaXF1ZSBpZGVudGlmaWVyIGZvciBnbG9iYWwgYWNjZXNzIHRocm91Z2hvdXRcbiAgICogdGhlIGFwcGxpY2F0aW9uLiBUaGlzIGFsbG93cyBmb3JtcyB0byBiZSByZXRyaWV2ZWQgYW5kIG1hbmFnZWQgY2VudHJhbGx5LiBUaHJvd3MgYW4gZXJyb3IgaWZcbiAgICogdGhlIGlkZW50aWZpZXIgaXMgYWxyZWFkeSBpbiB1c2UgdG8gcHJldmVudCBjb25mbGljdHMuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBmb3JtSWQgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgZm9yIHRoZSBmb3JtXG4gICAqIEBwYXJhbSB7Rm9ybVBhcmVudH0gZm9ybUdyb3VwIC0gVGhlIEZvcm1Hcm91cCBvciBGb3JtQXJyYXkgdG8gYmUgcmVnaXN0ZXJlZFxuICAgKiBAcmV0dXJuIHt2b2lkfVxuICAgKiBAdGhyb3dzIHtFcnJvcn0gSWYgYSBGb3JtR3JvdXAgd2l0aCB0aGUgZ2l2ZW4gaWQgaXMgYWxyZWFkeSByZWdpc3RlcmVkXG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQG1lbWJlck9mIE5neEZvcm1TZXJ2aWNlXG4gICAqL1xuICBzdGF0aWMgYWRkUmVnaXN0cnkoZm9ybUlkOiBzdHJpbmcsIGZvcm1Hcm91cDogRm9ybVBhcmVudCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmZvcm1SZWdpc3RyeS5oYXMoZm9ybUlkKSlcbiAgICAgIHRocm93IG5ldyBFcnJvcihgQSBGb3JtR3JvdXAgd2l0aCBpZCAnJHtmb3JtSWR9JyBpcyBhbHJlYWR5IHJlZ2lzdGVyZWQuYCk7XG4gICAgdGhpcy5mb3JtUmVnaXN0cnkuc2V0KGZvcm1JZCwgZm9ybUdyb3VwKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUmVtb3ZlcyBhIGZvcm0gZnJvbSB0aGUgcmVnaXN0cnkuXG4gICAqIEBzdW1tYXJ5IERlbGV0ZXMgYSBGb3JtR3JvdXAgb3IgRm9ybUFycmF5IGZyb20gdGhlIHJlZ2lzdHJ5IHVzaW5nIGl0cyB1bmlxdWUgaWRlbnRpZmllci5cbiAgICogVGhpcyBjbGVhbnMgdXAgdGhlIHJlZ2lzdHJ5IGFuZCBhbGxvd3MgdGhlIGlkZW50aWZpZXIgdG8gYmUgcmV1c2VkLiBUaGUgZm9ybSBpdHNlbGZcbiAgICogaXMgbm90IGRlc3Ryb3llZCwgb25seSByZW1vdmVkIGZyb20gdGhlIGNlbnRyYWwgcmVnaXN0cnkuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBmb3JtSWQgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGZvcm0gdG8gYmUgcmVtb3ZlZFxuICAgKiBAcmV0dXJuIHt2b2lkfVxuICAgKlxuICAgKiBAc3RhdGljXG4gICAqIEBtZW1iZXJPZiBOZ3hGb3JtU2VydmljZVxuICAgKi9cbiAgc3RhdGljIHJlbW92ZVJlZ2lzdHJ5KGZvcm1JZDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5mb3JtUmVnaXN0cnkuZGVsZXRlKGZvcm1JZCk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFJlc29sdmVzIHRoZSBwYXJlbnQgZ3JvdXAgYW5kIGNvbnRyb2wgbmFtZSBmcm9tIGEgcGF0aC5cbiAgICogQHN1bW1hcnkgVHJhdmVyc2VzIHRoZSBmb3JtIGdyb3VwIHN0cnVjdHVyZSB0byBmaW5kIHRoZSBwYXJlbnQgZ3JvdXAgYW5kIGNvbnRyb2wgbmFtZSBmb3IgYSBnaXZlbiBwYXRoLlxuICAgKiBIYW5kbGVzIGNvbXBsZXggbmVzdGVkIHN0cnVjdHVyZXMgaW5jbHVkaW5nIGFycmF5cyBhbmQgc3ViLWdyb3Vwcy4gQ3JlYXRlcyBtaXNzaW5nIGludGVybWVkaWF0ZVxuICAgKiBncm91cHMgYXMgbmVlZGVkIGFuZCBwcm9wZXJseSBjb25maWd1cmVzIEZvcm1BcnJheSBjb250cm9scyBmb3IgbXVsdGlwbGUgdmFsdWUgc2NlbmFyaW9zLlxuICAgKlxuICAgKiBAcGFyYW0ge0Zvcm1Hcm91cH0gZm9ybUdyb3VwIC0gVGhlIHJvb3QgRm9ybUdyb3VwIHRvIHRyYXZlcnNlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIGRvdC1zZXBhcmF0ZWQgcGF0aCB0byB0aGUgY29udHJvbCAoZS5nLiwgJ3VzZXIuYWRkcmVzcy5zdHJlZXQnKVxuICAgKiBAcGFyYW0ge0lDb21wb25lbnRJbnB1dH0gY29tcG9uZW50UHJvcHMgLSBQcm9wZXJ0aWVzIGRlZmluaW5nIHRoZSBjb21wb25lbnQgY29uZmlndXJhdGlvblxuICAgKiBAcGFyYW0ge0tleVZhbHVlfSBwYXJlbnRQcm9wcyAtIFByb3BlcnRpZXMgZnJvbSB0aGUgcGFyZW50IGNvbXBvbmVudCBmb3IgY29udGV4dFxuICAgKiBAcmV0dXJuIHtGb3JtUGFyZW50R3JvdXB9IEEgdHVwbGUgY29udGFpbmluZyB0aGUgcGFyZW50IEZvcm1Hcm91cCBhbmQgdGhlIGNvbnRyb2wgbmFtZVxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBORlMgYXMgTmd4Rm9ybVNlcnZpY2VcbiAgICogICBwYXJ0aWNpcGFudCBGRyBhcyBGb3JtR3JvdXBcbiAgICogICBwYXJ0aWNpcGFudCBGQSBhcyBGb3JtQXJyYXlcbiAgICpcbiAgICogICBORlMtPj5ORlM6IFNwbGl0IHBhdGggaW50byBwYXJ0c1xuICAgKiAgIGxvb3AgRm9yIGVhY2ggcGF0aCBwYXJ0XG4gICAqICAgICBhbHQgQ29udHJvbCBkb2Vzbid0IGV4aXN0XG4gICAqICAgICAgIGFsdCBpc011bHRpcGxlIGFuZCBwYXJ0IGlzIGNoaWxkT2ZcbiAgICogICAgICAgICBORlMtPj5GQTogbmV3IEZvcm1BcnJheShbbmV3IEZvcm1Hcm91cCh7fSldKVxuICAgKiAgICAgICBlbHNlXG4gICAqICAgICAgICAgTkZTLT4+Rkc6IG5ldyBGb3JtR3JvdXAoe30pXG4gICAqICAgICAgIGVuZFxuICAgKiAgICAgICBORlMtPj5GRzogYWRkQ29udHJvbChwYXJ0LCBuZXdDb250cm9sKVxuICAgKiAgICAgZW5kXG4gICAqICAgICBORlMtPj5ORlM6IE5hdmlnYXRlIHRvIG5leHQgbGV2ZWxcbiAgICogICBlbmRcbiAgICogICBORlMtLT4+TkZTOiBSZXR1cm4gW3BhcmVudEdyb3VwLCBjb250cm9sTmFtZV1cbiAgICpcbiAgICogQHN0YXRpY1xuICAgKiBAbWVtYmVyT2YgTmd4Rm9ybVNlcnZpY2VcbiAgICovXG4gIHByaXZhdGUgc3RhdGljIHJlc29sdmVQYXJlbnRHcm91cChmb3JtR3JvdXA6IEZvcm1Hcm91cCwgcGF0aDogc3RyaW5nLCBjb21wb25lbnRQcm9wczogSUNvbXBvbmVudElucHV0LCBwYXJlbnRQcm9wczogS2V5VmFsdWUpOiBGb3JtUGFyZW50R3JvdXAge1xuICAgIGNvbnN0IGlzTXVsdGlwbGUgPSBwYXJlbnRQcm9wcz8uWydtdWx0aXBsZSddIHx8IHBhcmVudFByb3BzPy5bJ3R5cGUnXSA9PT0gJ0FycmF5JyB8fCBmYWxzZTtcbiAgICBjb25zdCBwYXJ0cyA9IHBhdGguc3BsaXQoJy4nKTtcbiAgICBjb25zdCBjb250cm9sTmFtZSA9IHBhcnRzLnBvcCgpIGFzIHN0cmluZztcbiAgICBjb25zdCB7Y2hpbGRPZn0gPSBjb21wb25lbnRQcm9wc1xuICAgIGxldCBjdXJyZW50R3JvdXAgPSBmb3JtR3JvdXA7XG5cbiAgICBmdW5jdGlvbiBzZXRBcnJheUNvbXBvbmVudFByb3BzKGZvcm1Hcm91cEFycmF5OiBGb3JtQXJyYXkpIHtcbiAgICAgIGNvbnN0IHByb3BzID0gKGZvcm1Hcm91cEFycmF5IGFzIEtleVZhbHVlKVtBbmd1bGFyRW5naW5lS2V5cy5GT1JNX0dST1VQX0NPTVBPTkVOVF9QUk9QU10gfHwge307XG4gICAgICAgIGlmKCFwcm9wc1tNb2RlbEtleXMuTU9ERUxdW2NvbnRyb2xOYW1lXSlcbiAgICAgICAgICBwcm9wc1tNb2RlbEtleXMuTU9ERUxdID0gT2JqZWN0LmFzc2lnbih7fSwgcHJvcHNbTW9kZWxLZXlzLk1PREVMXSwge1tjb250cm9sTmFtZV06IHsuLi5jb21wb25lbnRQcm9wc319KTtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IHBhcnQgb2YgcGFydHMpIHtcbiAgICAgIGlmICghY3VycmVudEdyb3VwLmdldChwYXJ0KSkge1xuICAgICAgICBjb25zdCBwYXJ0Rm9ybUdyb3VwID0gKGlzTXVsdGlwbGUgJiYgcGFydCA9PT0gY2hpbGRPZikgPyBuZXcgRm9ybUFycmF5KFtuZXcgRm9ybUdyb3VwKHt9KV0pIDogbmV3IEZvcm1Hcm91cCh7fSk7XG4gICAgICAgIChwYXJ0Rm9ybUdyb3VwIGFzIEtleVZhbHVlKVtBbmd1bGFyRW5naW5lS2V5cy5GT1JNX0dST1VQX0NPTVBPTkVOVF9QUk9QU10gPSB7XG4gICAgICAgICAgY2hpbGRPZjogY2hpbGRPZiB8fCAnJyxcbiAgICAgICAgICBpc011bHRpcGxlOiBpc011bHRpcGxlLFxuICAgICAgICAgIG5hbWU6IHBhcnQsXG4gICAgICAgICAgcGs6IGNvbXBvbmVudFByb3BzPy5bJ3BrJ10gfHwgcGFyZW50UHJvcHM/LlsncGsnXSB8fCAnJyxcbiAgICAgICAgICBbTW9kZWxLZXlzLk1PREVMXToge30sXG4gICAgICAgIH0gYXMgUGFydGlhbDxGaWVsZFByb3BlcnRpZXM+ICYge21vZGVsOiBLZXlWYWx1ZX07XG5cbiAgICAgICAgaWYoY3VycmVudEdyb3VwIGluc3RhbmNlb2YgRm9ybUFycmF5KSB7XG4gICAgICAgICAgKGN1cnJlbnRHcm91cCBhcyBGb3JtQXJyYXkpLnB1c2gocGFydEZvcm1Hcm91cCk7XG4gICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgICBmb3IoY29uc3QgY29udHJvbCBvZiBPYmplY3QudmFsdWVzKHBhcnRGb3JtR3JvdXAuY29udHJvbHMpKSB7XG4gICAgICAgICAgICBpZihjb250cm9sIGluc3RhbmNlb2YgRm9ybUNvbnRyb2wpXG4gICAgICAgICAgICAgIHRoaXMucmVnaXN0ZXIoY29udHJvbCBhcyBBYnN0cmFjdENvbnRyb2wsIGNvbXBvbmVudFByb3BzKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZihwYXJ0Rm9ybUdyb3VwIGluc3RhbmNlb2YgQWJzdHJhY3RDb250cm9sKVxuICAgICAgICAgICAgdGhpcy5yZWdpc3RlcihwYXJ0Rm9ybUdyb3VwIGFzIEFic3RyYWN0Q29udHJvbCwgY29tcG9uZW50UHJvcHMpO1xuXG4gICAgICAgICAgY3VycmVudEdyb3VwLmFkZENvbnRyb2wocGFydCwgcGFydEZvcm1Hcm91cCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmKGNoaWxkT2YgJiYgY3VycmVudEdyb3VwIGluc3RhbmNlb2YgRm9ybUFycmF5KVxuICAgICAgICBzZXRBcnJheUNvbXBvbmVudFByb3BzKGN1cnJlbnRHcm91cCk7XG5cbiAgICAgIGN1cnJlbnRHcm91cCA9IGN1cnJlbnRHcm91cC5nZXQocGFydCkgYXMgRm9ybUdyb3VwO1xuICAgIH1cbiAgICByZXR1cm4gW2N1cnJlbnRHcm91cCwgY29udHJvbE5hbWVdO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBSZXRyaWV2ZXMgY29tcG9uZW50IHByb3BlcnRpZXMgZnJvbSBhIEZvcm1Hcm91cCBvciBGb3JtQXJyYXkuXG4gICAqIEBzdW1tYXJ5IEV4dHJhY3RzIGNvbXBvbmVudCBwcm9wZXJ0aWVzIHN0b3JlZCBpbiB0aGUgZm9ybSBncm91cCBtZXRhZGF0YS4gSWYgYSBGb3JtR3JvdXAgaXMgcHJvdmlkZWRcbiAgICogYW5kIGdyb3VwQXJyYXlOYW1lIGlzIHNwZWNpZmllZCwgaXQgd2lsbCBsb29rIGZvciB0aGUgRm9ybUFycmF5IHdpdGhpbiB0aGUgZm9ybSBzdHJ1Y3R1cmUuXG4gICAqXG4gICAqIEBwYXJhbSB7Rm9ybUdyb3VwIHwgRm9ybUFycmF5fSBmb3JtR3JvdXAgLSBUaGUgZm9ybSBncm91cCBvciBmb3JtIGFycmF5IHRvIGV4dHJhY3QgcHJvcGVydGllcyBmcm9tXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBba2V5XSAtIE9wdGlvbmFsIGtleSB0byByZXRyaWV2ZSBhIHNwZWNpZmljIHByb3BlcnR5XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbZ3JvdXBBcnJheU5hbWVdIC0gT3B0aW9uYWwgbmFtZSBvZiB0aGUgZ3JvdXAgYXJyYXkgaWYgZm9ybUdyb3VwIGlzIG5vdCBhIEZvcm1BcnJheVxuICAgKiBAcmV0dXJuIHtQYXJ0aWFsPEZpZWxkUHJvcGVydGllcz59IFRoZSBjb21wb25lbnQgcHJvcGVydGllcyBvciBhIHNwZWNpZmljIHByb3BlcnR5IGlmIGtleSBpcyBwcm92aWRlZFxuICAgKlxuICAgKiBAc3RhdGljXG4gICAqIEBtZW1iZXJPZiBOZ3hGb3JtU2VydmljZVxuICAgKi9cbiAgc3RhdGljIGdldENvbXBvbmVudFByb3BzRnJvbUdyb3VwQXJyYXkoZm9ybUdyb3VwOiBGb3JtR3JvdXAgfCBGb3JtQXJyYXksIGtleT86IHN0cmluZywgZ3JvdXBBcnJheU5hbWU/OiBzdHJpbmcgfCB1bmRlZmluZWQpOiBQYXJ0aWFsPEZpZWxkUHJvcGVydGllcz4ge1xuICAgIGlmKCEoZm9ybUdyb3VwIGluc3RhbmNlb2YgRm9ybUFycmF5KSAmJiB0eXBlb2YgZ3JvdXBBcnJheU5hbWUgPT09IFByaW1pdGl2ZXMuU1RSSU5HKVxuICAgICAgZm9ybUdyb3VwID0gZm9ybUdyb3VwLnJvb3QuZ2V0KGdyb3VwQXJyYXlOYW1lIGFzIHN0cmluZykgYXMgRm9ybUFycmF5IHx8IHt9O1xuICAgIGNvbnN0IHByb3BzID0gKGZvcm1Hcm91cCBhcyBLZXlWYWx1ZSk/LltBbmd1bGFyRW5naW5lS2V5cy5GT1JNX0dST1VQX0NPTVBPTkVOVF9QUk9QU10gfHwge307XG4gICAgcmV0dXJuICgha2V5ID8gcHJvcHMgOiBwcm9wcz8uW2tleV0pIHx8IHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBBZGRzIGEgbmV3IGdyb3VwIHRvIGEgcGFyZW50IEZvcm1BcnJheS5cbiAgICogQHN1bW1hcnkgQ3JlYXRlcyBhbmQgYWRkcyBhIG5ldyBGb3JtR3JvdXAgdG8gdGhlIHNwZWNpZmllZCBwYXJlbnQgRm9ybUFycmF5IGJhc2VkIG9uIHRoZVxuICAgKiBjb21wb25lbnQgcHJvcGVydGllcyBzdG9yZWQgaW4gdGhlIHBhcmVudCdzIG1ldGFkYXRhLiBUaGlzIGlzIHVzZWQgZm9yIGR5bmFtaWMgZm9ybSBhcnJheXNcbiAgICogd2hlcmUgbmV3IGdyb3VwcyBuZWVkIHRvIGJlIGFkZGVkIGF0IHJ1bnRpbWUuXG4gICAqXG4gICAqIEBwYXJhbSB7Rm9ybUdyb3VwfSBmb3JtR3JvdXAgLSBUaGUgcm9vdCBmb3JtIGdyb3VwIGNvbnRhaW5pbmcgdGhlIHBhcmVudCBGb3JtQXJyYXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmVudE5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgcGFyZW50IEZvcm1BcnJheSB0byBhZGQgdGhlIGdyb3VwIHRvXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbaW5kZXg9MV0gLSBUaGUgaW5kZXggcG9zaXRpb24gd2hlcmUgdGhlIG5ldyBncm91cCBzaG91bGQgYmUgYWRkZWRcbiAgICogQHJldHVybiB7Rm9ybUdyb3VwfSBUaGUgbmV3bHkgY3JlYXRlZCBhbmQgYWRkZWQgRm9ybUdyb3VwXG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQG1lbWJlck9mIE5neEZvcm1TZXJ2aWNlXG4gICAqL1xuICBzdGF0aWMgYWRkR3JvdXBUb1BhcmVudChmb3JtR3JvdXA6IEZvcm1Hcm91cCwgcGFyZW50TmFtZTogc3RyaW5nLCBpbmRleDogbnVtYmVyID0gMSk6IEZvcm1Hcm91cCB7XG4gICAgY29uc3QgY29tcG9uZW50UHJvcHMgPSB0aGlzLmdldENvbXBvbmVudFByb3BzRnJvbUdyb3VwQXJyYXkoZm9ybUdyb3VwLCBNb2RlbEtleXMuTU9ERUwsIHBhcmVudE5hbWUpO1xuICAgIE9iamVjdC5lbnRyaWVzKGNvbXBvbmVudFByb3BzIGFzIEtleVZhbHVlKS5mb3JFYWNoKChbLCB2YWx1ZV0pID0+IHtcbiAgICAgIHJldHVybiB0aGlzLmFkZEZvcm1Db250cm9sKGZvcm1Hcm91cCwgdmFsdWUsIHttdWx0aXBsZTogdHJ1ZX0sIGluZGV4KTtcbiAgICB9KTtcblxuICAgIHJldHVybiB0aGlzLmdldEdyb3VwRnJvbVBhcmVudChmb3JtR3JvdXAsIHBhcmVudE5hbWUsIGluZGV4KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUmV0cmlldmVzIGEgRm9ybUdyb3VwIGZyb20gYSBwYXJlbnQgRm9ybUFycmF5IGF0IHRoZSBzcGVjaWZpZWQgaW5kZXguXG4gICAqIEBzdW1tYXJ5IEdldHMgYSBGb3JtR3JvdXAgZnJvbSB0aGUgc3BlY2lmaWVkIHBhcmVudCBGb3JtQXJyYXkuIElmIHRoZSBncm91cCBkb2Vzbid0IGV4aXN0XG4gICAqIGF0IHRoZSBnaXZlbiBpbmRleCwgaXQgd2lsbCBjcmVhdGUgYSBuZXcgb25lIHVzaW5nIGFkZEdyb3VwVG9QYXJlbnQuXG4gICAqXG4gICAqIEBwYXJhbSB7Rm9ybUdyb3VwfSBmb3JtR3JvdXAgLSBUaGUgcm9vdCBmb3JtIGdyb3VwIGNvbnRhaW5pbmcgdGhlIHBhcmVudCBGb3JtQXJyYXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmVudE5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgcGFyZW50IEZvcm1BcnJheSB0byByZXRyaWV2ZSB0aGUgZ3JvdXAgZnJvbVxuICAgKiBAcGFyYW0ge251bWJlcn0gW2luZGV4PTFdIC0gVGhlIGluZGV4IG9mIHRoZSBncm91cCB0byByZXRyaWV2ZVxuICAgKiBAcmV0dXJuIHtGb3JtR3JvdXB9IFRoZSBGb3JtR3JvdXAgYXQgdGhlIHNwZWNpZmllZCBpbmRleFxuICAgKlxuICAgKiBAc3RhdGljXG4gICAqIEBtZW1iZXJPZiBOZ3hGb3JtU2VydmljZVxuICAgKi9cbiAgc3RhdGljIGdldEdyb3VwRnJvbVBhcmVudChmb3JtR3JvdXA6IEZvcm1Hcm91cCwgcGFyZW50TmFtZTogc3RyaW5nLCBpbmRleDogbnVtYmVyID0gMSk6IEZvcm1Hcm91cCB7XG4gICAgY29uc3QgY2hpbGRHcm91cCA9ICgoZm9ybUdyb3VwLmdldChwYXJlbnROYW1lKSB8fCBmb3JtR3JvdXApIGFzIEZvcm1BcnJheSkuYXQoaW5kZXgpO1xuICAgIGlmKGNoaWxkR3JvdXAgaW5zdGFuY2VvZiBGb3JtR3JvdXApXG4gICAgICByZXR1cm4gY2hpbGRHcm91cDtcbiAgICByZXR1cm4gdGhpcy5hZGRHcm91cFRvUGFyZW50KGZvcm1Hcm91cCwgcGFyZW50TmFtZSwgaW5kZXgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDaGVja3MgaWYgYSB2YWx1ZSBpcyB1bmlxdWUgd2l0aGluIGEgRm9ybUFycmF5IGdyb3VwLlxuICAgKiBAc3VtbWFyeSBWYWxpZGF0ZXMgdGhhdCB0aGUgcHJpbWFyeSBrZXkgdmFsdWUgaW4gYSBGb3JtR3JvdXAgaXMgdW5pcXVlIGFtb25nIGFsbCBncm91cHNcbiAgICogaW4gdGhlIHBhcmVudCBGb3JtQXJyYXkuIFRoZSB1bmlxdWVuZXNzIGNoZWNrIGJlaGF2aW9yIGRpZmZlcnMgYmFzZWQgb24gdGhlIG9wZXJhdGlvbiB0eXBlLlxuICAgKlxuICAgKiBAcGFyYW0ge0Zvcm1Hcm91cH0gZm9ybUdyb3VwIC0gVGhlIEZvcm1Hcm91cCB0byBjaGVjayBmb3IgdW5pcXVlbmVzc1xuICAgKiBAcGFyYW0ge251bWJlcn0gaW5kZXggLSBUaGUgaW5kZXggb2YgdGhlIGN1cnJlbnQgZ3JvdXAgd2l0aGluIHRoZSBGb3JtQXJyYXlcbiAgICogQHBhcmFtIHtPcGVyYXRpb25LZXlzfSBbb3BlcmF0aW9uPU9wZXJhdGlvbktleXMuQ1JFQVRFXSAtIFRoZSB0eXBlIG9mIG9wZXJhdGlvbiBiZWluZyBwZXJmb3JtZWRcbiAgICogQHJldHVybiB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgdmFsdWUgaXMgdW5pcXVlLCBmYWxzZSBvdGhlcndpc2VcbiAgICpcbiAgICogQHN0YXRpY1xuICAgKiBAbWVtYmVyT2YgTmd4Rm9ybVNlcnZpY2VcbiAgICovXG4gIHN0YXRpYyBpc1VuaXF1ZU9uR3JvdXAoZm9ybUdyb3VwOiBGb3JtR3JvdXAsIGluZGV4OiBudW1iZXIsIG9wZXJhdGlvbjogT3BlcmF0aW9uS2V5cyA9IE9wZXJhdGlvbktleXMuQ1JFQVRFKTogYm9vbGVhbiB7XG4gICAgY29uc3QgZm9ybUdyb3VwQXJyYXkgPSBmb3JtR3JvdXAucGFyZW50IGFzIEZvcm1BcnJheTtcbiAgICBjb25zdCBwayA9IHRoaXMuZ2V0Q29tcG9uZW50UHJvcHNGcm9tR3JvdXBBcnJheShmb3JtR3JvdXBBcnJheSwgQmFzZUNvbXBvbmVudFByb3BzLlBLIGFzIHN0cmluZykgYXMgc3RyaW5nO1xuICAgIGNvbnN0IGNvbnRyb2xOYW1lID0gT2JqZWN0LmtleXMoZm9ybUdyb3VwLmNvbnRyb2xzKVswXTtcblxuICAgIC8vIG9ubHkgY2hlY2sgZm9yIHVuaXF1ZSBpZiBpcyB0aGUgcGsgY29udHJvbFxuICAgIGlmKGNvbnRyb2xOYW1lICE9PSBwaylcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIGNvbnN0IGNvbnRyb2xWYWx1ZSA9IGNsZWFuU3BhY2VzKGAke2Zvcm1Hcm91cC5nZXQocGspPy52YWx1ZX1gLCB0cnVlKTtcbiAgICBpZihvcGVyYXRpb24gPT09IE9wZXJhdGlvbktleXMuQ1JFQVRFKVxuICAgICAgcmV0dXJuICFmb3JtR3JvdXBBcnJheS5jb250cm9scy5zb21lKChncm91cCwgaSkgPT4gaSAhPT0gaW5kZXggJiYgY2xlYW5TcGFjZXMoYCR7Z3JvdXAuZ2V0KHBrKT8udmFsdWV9YCwgdHJ1ZSkgPT09IGNvbnRyb2xWYWx1ZSk7XG5cbiAgICByZXR1cm4gIWZvcm1Hcm91cEFycmF5LmNvbnRyb2xzLnNvbWUoKGdyb3VwLCBpKSA9PiB7XG4gICAgICBjb25zdCB2YWx1ZSA9IGNsZWFuU3BhY2VzKGAke2dyb3VwLmdldChwayk/LnZhbHVlfWAsIHRydWUpO1xuICAgICAgcmV0dXJuIGkgIT09IGluZGV4ICYmIGNvbnRyb2xWYWx1ZSA9PT0gdmFsdWU7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEVuYWJsZXMgYWxsIGNvbnRyb2xzIHdpdGhpbiBhIEZvcm1Hcm91cCBvciBGb3JtQXJyYXkuXG4gICAqIEBzdW1tYXJ5IFJlY3Vyc2l2ZWx5IGVuYWJsZXMgYWxsIGZvcm0gY29udHJvbHMgd2l0aGluIHRoZSBwcm92aWRlZCBGb3JtR3JvdXAgb3IgRm9ybUFycmF5LlxuICAgKiBUaGlzIGlzIHVzZWZ1bCBmb3IgbWFraW5nIGFsbCBjb250cm9scyBpbnRlcmFjdGl2ZSBhZnRlciB0aGV5IGhhdmUgYmVlbiBkaXNhYmxlZC5cbiAgICpcbiAgICogQHBhcmFtIHtGb3JtQXJyYXkgfCBGb3JtR3JvdXB9IGZvcm1Hcm91cCAtIFRoZSBGb3JtR3JvdXAgb3IgRm9ybUFycmF5IHRvIGVuYWJsZSBhbGwgY29udHJvbHMgZm9yXG4gICAqIEByZXR1cm4ge3ZvaWR9XG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQG1lbWJlck9mIE5neEZvcm1TZXJ2aWNlXG4gICAqL1xuICBzdGF0aWMgZW5hYmxlQWxsR3JvdXBDb250cm9scyhmb3JtR3JvdXA6IEZvcm1BcnJheSB8IEZvcm1Hcm91cCk6IHZvaWQge1xuICAgIE9iamVjdC5rZXlzKGZvcm1Hcm91cC5jb250cm9scykuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgY29uc3QgY29udHJvbCA9IGZvcm1Hcm91cC5nZXQoa2V5KTtcbiAgICAgIGlmIChjb250cm9sIGluc3RhbmNlb2YgRm9ybUFycmF5KSB7XG4gICAgICAgIGNvbnRyb2wuY29udHJvbHMuZm9yRWFjaChjaGlsZCA9PiB7XG4gICAgICAgICAgaWYgKGNoaWxkIGluc3RhbmNlb2YgRm9ybUdyb3VwKSB7XG4gICAgICAgICAgICBjaGlsZC5lbmFibGUoeyBlbWl0RXZlbnQ6IGZhbHNlIH0pO1xuICAgICAgICAgICAgY2hpbGQudXBkYXRlVmFsdWVBbmRWYWxpZGl0eSh7IGVtaXRFdmVudDogdHJ1ZSB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBBZGRzIGEgZm9ybSBjb250cm9sIHRvIGEgZm9ybSBncm91cCBiYXNlZCBvbiBjb21wb25lbnQgcHJvcGVydGllcy5cbiAgICogQHN1bW1hcnkgQ3JlYXRlcyBhbmQgY29uZmlndXJlcyBhIEZvcm1Db250cm9sIHdpdGhpbiB0aGUgc3BlY2lmaWVkIEZvcm1Hcm91cCB1c2luZyB0aGUgcHJvdmlkZWRcbiAgICogY29tcG9uZW50IHByb3BlcnRpZXMuIEhhbmRsZXMgbmVzdGVkIHBhdGhzLCBtdWx0aXBsZSBjb250cm9scyAoRm9ybUFycmF5cyksIGFuZCBjb250cm9sIHJlZ2lzdHJhdGlvbi5cbiAgICogVGhpcyBtZXRob2Qgc3VwcG9ydHMgY29tcGxleCBmb3JtIHN0cnVjdHVyZXMgd2l0aCBuZXN0ZWQgZ3JvdXBzIGFuZCBhcnJheXMuXG4gICAqXG4gICAqIEBwYXJhbSB7Rm9ybUdyb3VwfSBmb3JtR3JvdXAgLSBUaGUgZm9ybSBncm91cCB0byBhZGQgdGhlIGNvbnRyb2wgdG9cbiAgICogQHBhcmFtIHtJQ29tcG9uZW50SW5wdXR9IGNvbXBvbmVudFByb3BzIC0gVGhlIGNvbXBvbmVudCBwcm9wZXJ0aWVzIGRlZmluaW5nIHRoZSBjb250cm9sIGNvbmZpZ3VyYXRpb25cbiAgICogQHBhcmFtIHtLZXlWYWx1ZX0gW3BhcmVudFByb3BzPXt9XSAtIFByb3BlcnRpZXMgZnJvbSB0aGUgcGFyZW50IGNvbXBvbmVudCBmb3IgY29udGV4dFxuICAgKiBAcGFyYW0ge251bWJlcn0gW2luZGV4PTBdIC0gVGhlIGluZGV4IGZvciBtdWx0aXBsZSBjb250cm9scyBpbiBGb3JtQXJyYXlzXG4gICAqIEByZXR1cm4ge3ZvaWR9XG4gICAqXG4gICAqIEBwcml2YXRlXG4gICAqIEBzdGF0aWNcbiAgICogQG1lbWJlck9mIE5neEZvcm1TZXJ2aWNlXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBhZGRGb3JtQ29udHJvbChmb3JtR3JvdXA6IEZvcm1QYXJlbnQsIGNvbXBvbmVudFByb3BzOiBJQ29tcG9uZW50SW5wdXQsIHBhcmVudFByb3BzOiBLZXlWYWx1ZSA9IHt9LCBpbmRleDogbnVtYmVyID0gMCk6IHZvaWQge1xuXG4gICAgY29uc3QgaXNNdWx0aXBsZSA9IHBhcmVudFByb3BzPy5bJ211bHRpcGxlJ10gfHwgcGFyZW50UHJvcHM/LlsndHlwZSddID09PSAnQXJyYXknIHx8IGZhbHNlO1xuICAgIGNvbnN0IHsgbmFtZSwgY2hpbGRPZiwgfSA9IGNvbXBvbmVudFByb3BzO1xuICAgIGlmKGlzTXVsdGlwbGUpXG4gICAgICBjb21wb25lbnRQcm9wc1sncGsnXSA9IGNvbXBvbmVudFByb3BzWydwayddIHx8IHBhcmVudFByb3BzPy5bJ3BrJ10gfHwgJyc7XG4gICAgY29uc3QgZnVsbFBhdGggPSBjaGlsZE9mID8gaXNNdWx0aXBsZSA/IGAke2NoaWxkT2Z9LiR7aW5kZXh9LiR7bmFtZX1gIDogYCR7Y2hpbGRPZn0uJHtuYW1lfWAgOiBuYW1lO1xuICAgIGNvbnN0IFtwYXJlbnRHcm91cCwgY29udHJvbE5hbWVdID0gdGhpcy5yZXNvbHZlUGFyZW50R3JvdXAoZm9ybUdyb3VwIGFzIEZvcm1Hcm91cCwgZnVsbFBhdGgsIGNvbXBvbmVudFByb3BzLCBwYXJlbnRQcm9wcyk7XG5cbiAgICBpZiAoIXBhcmVudEdyb3VwLmdldChjb250cm9sTmFtZSkpIHtcbiAgICAgIGNvbnN0IGNvbnRyb2wgPSBOZ3hGb3JtU2VydmljZS5mcm9tUHJvcHMoXG4gICAgICAgIGNvbXBvbmVudFByb3BzLFxuICAgICAgICBjb21wb25lbnRQcm9wcy51cGRhdGVNb2RlIHx8ICdjaGFuZ2UnLFxuICAgICAgKTtcbiAgICAgIE5neEZvcm1TZXJ2aWNlLnJlZ2lzdGVyKGNvbnRyb2wsIGNvbXBvbmVudFByb3BzKTtcbiAgICAgIGlmIChwYXJlbnRHcm91cCBpbnN0YW5jZW9mIEZvcm1Hcm91cCkge1xuICAgICAgICBwYXJlbnRHcm91cC5hZGRDb250cm9sKGNvbnRyb2xOYW1lLCBjb250cm9sKTtcbiAgICAgIH1cbiAgICAgIGlmKHBhcmVudEdyb3VwIGluc3RhbmNlb2YgRm9ybUFycmF5KSB7XG4gICAgICAgIGNvbnN0IHJvb3QgPSBwYXJlbnRHcm91cC5jb250cm9sc1soY29tcG9uZW50UHJvcHMgYXMgS2V5VmFsdWUpPy5bJ3BhZ2UnXSAtIDFdIGFzIEZvcm1Hcm91cDtcbiAgICAgICAgaWYocm9vdCkge1xuICAgICAgICAgICByb290LmFkZENvbnRyb2woY29udHJvbE5hbWUsIGNvbnRyb2wpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHBhcmVudEdyb3VwLnB1c2goe1tjb250cm9sTmFtZV06IGNvbnRyb2x9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBjb25zdCByb290ID0gcGFyZW50R3JvdXAgaW5zdGFuY2VvZiBGb3JtQXJyYXkgPyBwYXJlbnRHcm91cC5jb250cm9sc1soY29tcG9uZW50UHJvcHMgYXMgS2V5VmFsdWUpPy5bJ3BhZ2UnXSAtIDFdIDogcGFyZW50R3JvdXA7XG4gICAgY29tcG9uZW50UHJvcHNbJ2Zvcm1Hcm91cCddID0gcm9vdCBhcyBGb3JtR3JvdXA7XG4gICAgY29tcG9uZW50UHJvcHNbJ2Zvcm1Db250cm9sJ10gPSBwYXJlbnRHcm91cC5nZXQoY29udHJvbE5hbWUpIGFzIEZvcm1Db250cm9sO1xuICAgIGNvbXBvbmVudFByb3BzWydtdWx0aXBsZSddID0gaXNNdWx0aXBsZVxuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBSZXRyaWV2ZXMgYSBjb250cm9sIGZyb20gYSByZWdpc3RlcmVkIGZvcm0uXG4gICAqIEBzdW1tYXJ5IEZpbmRzIGFuZCByZXR1cm5zIGFuIEFic3RyYWN0Q29udHJvbCBmcm9tIGEgcmVnaXN0ZXJlZCBmb3JtIHVzaW5nIHRoZSBmb3JtIGlkIGFuZCBvcHRpb25hbCBwYXRoLlxuICAgKiBUaGlzIG1ldGhvZCBwcm92aWRlcyBjZW50cmFsaXplZCBhY2Nlc3MgdG8gZm9ybSBjb250cm9scyBhY3Jvc3MgdGhlIGFwcGxpY2F0aW9uIGJ5IGxldmVyYWdpbmdcbiAgICogdGhlIGZvcm0gcmVnaXN0cnkgc3lzdGVtLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gZm9ybUlkIC0gVGhlIHVuaXF1ZSBpZGVudGlmaWVyIG9mIHRoZSBmb3JtIGluIHRoZSByZWdpc3RyeVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW3BhdGhdIC0gVGhlIG9wdGlvbmFsIGRvdC1zZXBhcmF0ZWQgcGF0aCB0byBhIHNwZWNpZmljIGNvbnRyb2wgd2l0aGluIHRoZSBmb3JtXG4gICAqIEByZXR1cm4ge0Fic3RyYWN0Q29udHJvbH0gVGhlIHJlcXVlc3RlZCBBYnN0cmFjdENvbnRyb2wgKEZvcm1Hcm91cCwgRm9ybUFycmF5LCBvciBGb3JtQ29udHJvbClcbiAgICogQHRocm93cyB7RXJyb3J9IElmIHRoZSBmb3JtIGlzIG5vdCBmb3VuZCBpbiB0aGUgcmVnaXN0cnkgb3IgdGhlIGNvbnRyb2wgaXMgbm90IGZvdW5kIGluIHRoZSBmb3JtXG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEMgYXMgQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgTkZTIGFzIE5neEZvcm1TZXJ2aWNlXG4gICAqICAgcGFydGljaXBhbnQgRlIgYXMgRm9ybSBSZWdpc3RyeVxuICAgKlxuICAgKiAgIEMtPj5ORlM6IGdldENvbnRyb2xGcm9tRm9ybShmb3JtSWQsIHBhdGg/KVxuICAgKiAgIE5GUy0+PkZSOiBHZXQgZm9ybSBieSBmb3JtSWRcbiAgICogICBhbHQgRm9ybSBub3QgZm91bmRcbiAgICogICAgIEZSLS0+Pk5GUzogbnVsbFxuICAgKiAgICAgTkZTLS0+PkM6IFRocm93IEVycm9yXG4gICAqICAgZWxzZSBGb3JtIGZvdW5kXG4gICAqICAgICBGUi0tPj5ORlM6IFJldHVybiBmb3JtXG4gICAqICAgICBhbHQgcGF0aCBwcm92aWRlZFxuICAgKiAgICAgICBORlMtPj5ORlM6IGZvcm0uZ2V0KHBhdGgpXG4gICAqICAgICAgIGFsdCBDb250cm9sIG5vdCBmb3VuZFxuICAgKiAgICAgICAgIE5GUy0tPj5DOiBUaHJvdyBFcnJvclxuICAgKiAgICAgICBlbHNlXG4gICAqICAgICAgICAgTkZTLS0+PkM6IFJldHVybiBjb250cm9sXG4gICAqICAgICAgIGVuZFxuICAgKiAgICAgZWxzZVxuICAgKiAgICAgICBORlMtLT4+QzogUmV0dXJuIGZvcm1cbiAgICogICAgIGVuZFxuICAgKiAgIGVuZFxuICAgKlxuICAgKiBAc3RhdGljXG4gICAqIEBtZW1iZXJPZiBOZ3hGb3JtU2VydmljZVxuICAgKi9cbiAgc3RhdGljIGdldENvbnRyb2xGcm9tRm9ybShmb3JtSWQ6IHN0cmluZywgcGF0aD86IHN0cmluZyk6IEFic3RyYWN0Q29udHJvbCB7XG4gICAgY29uc3QgZm9ybSA9IHRoaXMuZm9ybVJlZ2lzdHJ5LmdldChmb3JtSWQpO1xuICAgIGlmICghZm9ybSlcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRm9ybSB3aXRoIGlkICcke2Zvcm1JZH0nIG5vdCBmb3VuZCBpbiB0aGUgcmVnaXN0cnkuYCk7XG5cbiAgICBpZiAoIXBhdGgpXG4gICAgICByZXR1cm4gZm9ybTtcblxuICAgIGNvbnN0IGNvbnRyb2wgPSBmb3JtLmdldChwYXRoKTtcbiAgICBpZiAoIWNvbnRyb2wpXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYENvbnRyb2wgd2l0aCBwYXRoICcke3BhdGh9JyBub3QgZm91bmQgaW4gZm9ybSAnJHtmb3JtSWR9Jy5gKTtcbiAgICByZXR1cm4gY29udHJvbDtcbiAgfVxuXG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDcmVhdGVzIGEgZm9ybSBmcm9tIFVJIG1vZGVsIG1ldGFkYXRhIGNoaWxkcmVuLlxuICAgKiBAc3VtbWFyeSBHZW5lcmF0ZXMgYSBGb3JtR3JvdXAgZnJvbSBhbiBhcnJheSBvZiBVSU1vZGVsTWV0YWRhdGEgb2JqZWN0cywgZXh0cmFjdGluZyBjb21wb25lbnRcbiAgICogcHJvcGVydGllcyBhbmQgY3JlYXRpbmcgYXBwcm9wcmlhdGUgZm9ybSBjb250cm9scy4gVGhpcyBtZXRob2QgaXMgc3BlY2lmaWNhbGx5IGRlc2lnbmVkIHRvIHdvcmtcbiAgICogd2l0aCB0aGUgVUkgZGVjb3JhdG9yIHN5c3RlbSBhbmQgcHJvdmlkZXMgYXV0b21hdGljIGZvcm0gZ2VuZXJhdGlvbiBmcm9tIG1ldGFkYXRhLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWQgLSBVbmlxdWUgaWRlbnRpZmllciBmb3IgdGhlIGZvcm1cbiAgICogQHBhcmFtIHtib29sZWFufSBbcmVnaXN0cnk9ZmFsc2VdIC0gV2hldGhlciB0byByZWdpc3RlciB0aGUgY3JlYXRlZCBmb3JtIGluIHRoZSBnbG9iYWwgcmVnaXN0cnlcbiAgICogQHBhcmFtIHtVSU1vZGVsTWV0YWRhdGFbXX0gW2NoaWxkcmVuXSAtIEFycmF5IG9mIFVJIG1vZGVsIG1ldGFkYXRhIG9iamVjdHMgdG8gY3JlYXRlIGNvbnRyb2xzIGZyb21cbiAgICogQHJldHVybiB7Rm9ybUdyb3VwfSBUaGUgY3JlYXRlZCBGb3JtR3JvdXAgd2l0aCBjb250cm9scyBmb3IgZWFjaCBjaGlsZCBtZXRhZGF0YVxuICAgKlxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBDIGFzIENvbXBvbmVudFxuICAgKiAgIHBhcnRpY2lwYW50IE5GUyBhcyBOZ3hGb3JtU2VydmljZVxuICAgKiAgIHBhcnRpY2lwYW50IEFGIGFzIEFuZ3VsYXIgRm9ybXNcbiAgICpcbiAgICogICBDLT4+TkZTOiBjcmVhdGVGb3JtRnJvbUNoaWxkcmVuKGlkLCByZWdpc3RyeSwgY2hpbGRyZW4pXG4gICAqICAgTkZTLT4+QUY6IG5ldyBGb3JtR3JvdXAoe30pXG4gICAqICAgbG9vcCBGb3IgZWFjaCBjaGlsZCBtZXRhZGF0YVxuICAgKiAgICAgTkZTLT4+TkZTOiBhZGRGb3JtQ29udHJvbChmb3JtLCBjaGlsZC5wcm9wcylcbiAgICogICAgIE5GUy0+PkFGOiBDcmVhdGUgYW5kIGFkZCBGb3JtQ29udHJvbFxuICAgKiAgIGVuZFxuICAgKiAgIGFsdCByZWdpc3RyeSBpcyB0cnVlXG4gICAqICAgICBORlMtPj5ORlM6IGFkZFJlZ2lzdHJ5KGlkLCBmb3JtKVxuICAgKiAgIGVuZFxuICAgKiAgIE5GUy0tPj5DOiBSZXR1cm4gRm9ybUdyb3VwXG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQG1lbWJlck9mIE5neEZvcm1TZXJ2aWNlXG4gICAqL1xuICBzdGF0aWMgY3JlYXRlRm9ybUZyb21DaGlsZHJlbihpZDogc3RyaW5nLCByZWdpc3RyeTogYm9vbGVhbiA9IGZhbHNlLCAgY2hpbGRyZW4/OiBVSU1vZGVsTWV0YWRhdGFbXSwpOiBGb3JtR3JvdXAge1xuICAgIGNvbnN0IGZvcm0gPSBuZXcgRm9ybUdyb3VwKHt9KTtcbiAgICBpZihjaGlsZHJlbj8ubGVuZ3RoKVxuICAgICAgY2hpbGRyZW4uZm9yRWFjaChjaGlsZCA9PiB7XG4gICAgICAgIHRoaXMuYWRkRm9ybUNvbnRyb2woZm9ybSwgY2hpbGQucHJvcHMgYXMgSUNvbXBvbmVudElucHV0KTtcbiAgICAgIH0pO1xuICAgIGlmIChyZWdpc3RyeSlcbiAgICAgIHRoaXMuYWRkUmVnaXN0cnkoaWQsIGZvcm0pO1xuICAgIHJldHVybiBmb3JtO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDcmVhdGVzIGEgZm9ybSBmcm9tIGNvbXBvbmVudCBjb25maWd1cmF0aW9ucy5cbiAgICogQHN1bW1hcnkgR2VuZXJhdGVzIGEgRm9ybUdyb3VwIGJhc2VkIG9uIGFuIGFycmF5IG9mIGNvbXBvbmVudCBjb25maWd1cmF0aW9ucyBhbmQgb3B0aW9uYWxseSByZWdpc3RlcnMgaXQuXG4gICAqIFRoaXMgbWV0aG9kIHByb2Nlc3NlcyBjb21wb25lbnQgaW5wdXQgY29uZmlndXJhdGlvbnMgdG8gY3JlYXRlIGFwcHJvcHJpYXRlIGZvcm0gY29udHJvbHMgd2l0aFxuICAgKiB2YWxpZGF0aW9uIGFuZCBpbml0aWFsIHZhbHVlcy5cbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkIC0gVGhlIHVuaXF1ZSBpZGVudGlmaWVyIGZvciB0aGUgZm9ybVxuICAgKiBAcGFyYW0ge0lDb21wb25lbnRDb25maWdbXX0gY29tcG9uZW50cyAtIEFuIGFycmF5IG9mIGNvbXBvbmVudCBjb25maWd1cmF0aW9ucyBkZWZpbmluZyB0aGUgZm9ybSBzdHJ1Y3R1cmVcbiAgICogQHBhcmFtIHtib29sZWFufSBbcmVnaXN0cnk9ZmFsc2VdIC0gV2hldGhlciB0byByZWdpc3RlciB0aGUgY3JlYXRlZCBmb3JtIGluIHRoZSBnbG9iYWwgcmVnaXN0cnlcbiAgICogQHJldHVybiB7Rm9ybUdyb3VwfSBUaGUgY3JlYXRlZCBGb3JtR3JvdXAgd2l0aCBjb250cm9scyBmb3IgZWFjaCBjb21wb25lbnQgY29uZmlndXJhdGlvblxuICAgKlxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBDIGFzIENvbXBvbmVudFxuICAgKiAgIHBhcnRpY2lwYW50IE5GUyBhcyBOZ3hGb3JtU2VydmljZVxuICAgKiAgIHBhcnRpY2lwYW50IEFGIGFzIEFuZ3VsYXIgRm9ybXNcbiAgICpcbiAgICogICBDLT4+TkZTOiBjcmVhdGVGb3JtRnJvbUNvbXBvbmVudHMoaWQsIGNvbXBvbmVudHMsIHJlZ2lzdHJ5KVxuICAgKiAgIE5GUy0+PkFGOiBuZXcgRm9ybUdyb3VwKHt9KVxuICAgKiAgIGxvb3AgRm9yIGVhY2ggY29tcG9uZW50IGNvbmZpZ1xuICAgKiAgICAgTkZTLT4+TkZTOiBhZGRGb3JtQ29udHJvbChmb3JtLCBjb21wb25lbnQuaW5wdXRzKVxuICAgKiAgICAgTkZTLT4+QUY6IENyZWF0ZSBhbmQgYWRkIEZvcm1Db250cm9sXG4gICAqICAgZW5kXG4gICAqICAgYWx0IHJlZ2lzdHJ5IGlzIHRydWVcbiAgICogICAgIE5GUy0+Pk5GUzogYWRkUmVnaXN0cnkoaWQsIGZvcm0pXG4gICAqICAgZW5kXG4gICAqICAgTkZTLS0+PkM6IFJldHVybiBGb3JtR3JvdXBcbiAgICpcbiAgICogQHN0YXRpY1xuICAgKiBAbWVtYmVyT2YgTmd4Rm9ybVNlcnZpY2VcbiAgICovXG4gIHN0YXRpYyBjcmVhdGVGb3JtRnJvbUNvbXBvbmVudHMoaWQ6IHN0cmluZywgY29tcG9uZW50czogSUNvbXBvbmVudENvbmZpZ1tdLCByZWdpc3RyeTogYm9vbGVhbiA9IGZhbHNlKTogRm9ybUdyb3VwIHtcbiAgICBjb25zdCBmb3JtID0gbmV3IEZvcm1Hcm91cCh7fSk7XG4gICAgY29tcG9uZW50cy5mb3JFYWNoKGNvbXBvbmVudCA9PiB7XG4gICAgICB0aGlzLmFkZEZvcm1Db250cm9sKGZvcm0sIGNvbXBvbmVudC5pbnB1dHMpO1xuICAgIH0pO1xuXG4gICAgaWYgKHJlZ2lzdHJ5KVxuICAgICAgdGhpcy5hZGRSZWdpc3RyeShpZCwgZm9ybSk7XG5cbiAgICByZXR1cm4gZm9ybTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQWRkcyBhIGNvbnRyb2wgdG8gYSBmb3JtIGJhc2VkIG9uIGNvbXBvbmVudCBwcm9wZXJ0aWVzLlxuICAgKiBAc3VtbWFyeSBDcmVhdGVzIGFuZCBhZGRzIGEgZm9ybSBjb250cm9sIHRvIGEgZm9ybSAoZXhpc3Rpbmcgb3IgbmV3KSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29tcG9uZW50IHByb3BlcnRpZXMuXG4gICAqIEhhbmRsZXMgbXVsdGktcGFnZSBmb3JtcyBieSBtYW5hZ2luZyBGb3JtQXJyYXkgc3RydWN0dXJlcyBhbmQgcHJvcGVyIGluZGV4aW5nLiBUaGlzIG1ldGhvZCBzdXBwb3J0c1xuICAgKiBjb21wbGV4IGZvcm0gc2NlbmFyaW9zIGluY2x1ZGluZyBuZXN0ZWQgY29udHJvbHMgYW5kIHBhZ2UtYmFzZWQgZm9ybSBvcmdhbml6YXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpZCAtIFRoZSB1bmlxdWUgaWRlbnRpZmllciBvZiB0aGUgZm9ybVxuICAgKiBAcGFyYW0ge0ZpZWxkUHJvcGVydGllc30gY29tcG9uZW50UHJvcGVydGllcyAtIFRoZSBwcm9wZXJ0aWVzIG9mIHRoZSBjb21wb25lbnQgdG8gY3JlYXRlIHRoZSBjb250cm9sIGZyb21cbiAgICogQHBhcmFtIHtGaWVsZFByb3BlcnRpZXN9IFtwYXJlbnRQcm9wc10gLSBPcHRpb25hbCBwYXJlbnQgcHJvcGVydGllcyBmb3IgY29udGV4dCBhbmQgY29uZmlndXJhdGlvblxuICAgKiBAcmV0dXJuIHtBYnN0cmFjdENvbnRyb2x9IFRoZSBmb3JtIG9yIGNyZWF0ZWQgY29udHJvbFxuICAgKlxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBDIGFzIENvbXBvbmVudFxuICAgKiAgIHBhcnRpY2lwYW50IE5GUyBhcyBOZ3hGb3JtU2VydmljZVxuICAgKiAgIHBhcnRpY2lwYW50IEYgYXMgRm9ybVxuICAgKlxuICAgKiAgIEMtPj5ORlM6IGFkZENvbnRyb2xGcm9tUHJvcHMoaWQsIGNvbXBvbmVudFByb3BzLCBwYXJlbnRQcm9wcz8pXG4gICAqICAgTkZTLT4+TkZTOiBjcmVhdGVGb3JtKGlkLCBwYXJlbnRQcm9wcywgdHJ1ZSlcbiAgICogICBhbHQgTXVsdGktcGFnZSBmb3JtIChwYXJlbnRQcm9wcy5wYWdlcyA+IDEpXG4gICAqICAgICBORlMtPj5ORlM6IENhbGN1bGF0ZSBwYWdlIGluZGV4XG4gICAqICAgICBORlMtPj5GOiBHZXQgb3IgY3JlYXRlIEZvcm1Hcm91cCBhdCBpbmRleFxuICAgKiAgICAgTkZTLT4+TkZTOiBTZXQgZm9ybSB0byBwYWdlIEZvcm1Hcm91cFxuICAgKiAgIGVuZFxuICAgKiAgIGFsdCBjb21wb25lbnRQcm9wZXJ0aWVzIGhhcyBwYXRoXG4gICAqICAgICBORlMtPj5ORlM6IGFkZEZvcm1Db250cm9sKGZvcm0sIGNvbXBvbmVudFByb3BlcnRpZXMsIHBhcmVudFByb3BzKVxuICAgKiAgIGVuZFxuICAgKiAgIE5GUy0tPj5DOiBSZXR1cm4gZm9ybS9jb250cm9sXG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQG1lbWJlck9mIE5neEZvcm1TZXJ2aWNlXG4gICAqL1xuICBzdGF0aWMgYWRkQ29udHJvbEZyb21Qcm9wcyhpZDogc3RyaW5nLCBjb21wb25lbnRQcm9wZXJ0aWVzOiBGaWVsZFByb3BlcnRpZXMsIHBhcmVudFByb3BzPzogRmllbGRQcm9wZXJ0aWVzKTogQWJzdHJhY3RDb250cm9sIHtcbiAgICBsZXQgZm9ybSA9IHRoaXMuY3JlYXRlRm9ybShpZCwgcGFyZW50UHJvcHMsIHRydWUpO1xuICAgIGNvbnN0IGZvcm1MZW5ndGggPSAoZm9ybSBhcyBGb3JtQXJyYXkpLmxlbmd0aDtcbiAgICBpZihwYXJlbnRQcm9wcz8ucGFnZXMgJiYgcGFyZW50UHJvcHM/LnBhZ2VzID4gMSkge1xuICAgICAgbGV0IGluZGV4ID0gY29tcG9uZW50UHJvcGVydGllcy5wYWdlIHx8IHBhcmVudFByb3BzLnBhZ2U7XG4gICAgICBpZighKHR5cGVvZiBpbmRleCA9PT0gJ251bWJlcicpIHx8IGluZGV4ID09PSAwKVxuICAgICAgICB0aHJvdyBFcnJvcihgUHJvcGVydHkgJ3BhZ2UnIGlzIHJlcXVpcmVkIGFuZCBncmVhdGhlciB0aGFuIDAgb24gJHtjb21wb25lbnRQcm9wZXJ0aWVzLm5hbWV9YCk7XG5cbiAgICAgIGlmKGluZGV4ID4gZm9ybUxlbmd0aCkge1xuICAgICAgICBpZigoZm9ybSBhcyBLZXlWYWx1ZSk/LlsnbGFzdEluZGV4J10gJiYgaW5kZXggPT09IChmb3JtIGFzIEtleVZhbHVlKVsnbGFzdEluZGV4J11bJ3BhZ2UnXSkge1xuICAgICAgICAgIGluZGV4ID0gKGZvcm0gYXMgS2V5VmFsdWUpWydsYXN0SW5kZXgnXVsnaW5kZXgnXTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAoZm9ybSBhcyBLZXlWYWx1ZSlbJ2xhc3RJbmRleCddID0ge1xuICAgICAgICAgICAgcGFnZTogaW5kZXgsXG4gICAgICAgICAgICBpbmRleDogZm9ybUxlbmd0aCArIDFcbiAgICAgICAgICB9O1xuICAgICAgICAgIGluZGV4ID0gZm9ybUxlbmd0aCArIDE7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgbGV0IGdyb3VwID0gKGZvcm0gYXMgRm9ybUFycmF5KS5jb250cm9sc1soaW5kZXggYXMgbnVtYmVyKSAtIDFdO1xuICAgICAgaWYoIWdyb3VwKSB7XG4gICAgICAgIGdyb3VwID0gbmV3IEZvcm1Hcm91cCh7fSk7XG4gICAgICAgIChmb3JtIGFzIEZvcm1BcnJheSkuaW5zZXJ0KGluZGV4IGFzIG51bWJlciwgZ3JvdXApO1xuICAgICAgfVxuICAgICAgZm9ybSA9IGdyb3VwIGFzIEZvcm1Hcm91cDtcbiAgICB9XG4gICAgaWYoY29tcG9uZW50UHJvcGVydGllcy5wYXRoKVxuICAgICAgdGhpcy5hZGRGb3JtQ29udHJvbChmb3JtLCBjb21wb25lbnRQcm9wZXJ0aWVzLCBwYXJlbnRQcm9wcyk7XG4gICAgcmV0dXJuIGZvcm07XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFJldHJpZXZlcyBmb3JtIGRhdGEgZnJvbSBhIEZvcm1Hcm91cC5cbiAgICogQHN1bW1hcnkgRXh0cmFjdHMgYW5kIHByb2Nlc3NlcyB0aGUgZGF0YSBmcm9tIGEgRm9ybUdyb3VwLCBoYW5kbGluZyBkaWZmZXJlbnQgaW5wdXQgdHlwZXMgYW5kIG5lc3RlZCBmb3JtIGdyb3Vwcy5cbiAgICogUGVyZm9ybXMgdHlwZSBjb252ZXJzaW9uIGZvciB2YXJpb3VzIEhUTUw1IGlucHV0IHR5cGVzLCB2YWxpZGF0ZXMgbmVzdGVkIGNvbnRyb2xzLCBhbmQgbWFuYWdlc1xuICAgKiBtdWx0aXBsZSBjb250cm9sIHNjZW5hcmlvcy4gQXV0b21hdGljYWxseSBlbmFibGVzIGFsbCBncm91cCBjb250cm9scyBhZnRlciBkYXRhIGV4dHJhY3Rpb24uXG4gICAqXG4gICAqIEBwYXJhbSB7Rm9ybUdyb3VwfSBmb3JtR3JvdXAgLSBUaGUgRm9ybUdyb3VwIHRvIGV4dHJhY3QgZGF0YSBmcm9tXG4gICAqIEByZXR1cm4ge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgcHJvY2Vzc2VkIGZvcm0gZGF0YSB3aXRoIHByb3BlciB0eXBlIGNvbnZlcnNpb25zXG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEMgYXMgQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgTkZTIGFzIE5neEZvcm1TZXJ2aWNlXG4gICAqICAgcGFydGljaXBhbnQgRkcgYXMgRm9ybUdyb3VwXG4gICAqICAgcGFydGljaXBhbnQgRkMgYXMgRm9ybUNvbnRyb2xcbiAgICpcbiAgICogICBDLT4+TkZTOiBnZXRGb3JtRGF0YShmb3JtR3JvdXApXG4gICAqICAgbG9vcCBGb3IgZWFjaCBjb250cm9sIGluIGZvcm1Hcm91cFxuICAgKiAgICAgYWx0IENvbnRyb2wgaXMgbm90IEZvcm1Db250cm9sXG4gICAqICAgICAgIE5GUy0+Pk5GUzogUmVjdXJzaXZlIGdldEZvcm1EYXRhKGNvbnRyb2wpXG4gICAqICAgICAgIGFsdCBwYXJlbnRQcm9wcy5tdWx0aXBsZSBhbmQgIWlzVmFsaWRcbiAgICogICAgICAgICBORlMtPj5ORlM6IHJlc2V0KGNvbnRyb2wpXG4gICAqICAgICAgIGVuZFxuICAgKiAgICAgZWxzZSBDb250cm9sIGlzIEZvcm1Db250cm9sXG4gICAqICAgICAgIE5GUy0+PkZDOiBHZXQgY29udHJvbCB2YWx1ZVxuICAgKiAgICAgICBORlMtPj5ORlM6IEFwcGx5IHR5cGUgY29udmVyc2lvbiBiYXNlZCBvbiBwcm9wcy50eXBlXG4gICAqICAgICAgIGFsdCBIVE1MNUNoZWNrVHlwZXNcbiAgICogICAgICAgICBORlMtPj5ORlM6IEtlZXAgYm9vbGVhbiB2YWx1ZVxuICAgKiAgICAgICBlbHNlIE5VTUJFUiB0eXBlXG4gICAqICAgICAgICAgTkZTLT4+TkZTOiBwYXJzZVRvTnVtYmVyKHZhbHVlKVxuICAgKiAgICAgICBlbHNlIERBVEUvREFURVRJTUUgdHlwZXNcbiAgICogICAgICAgICBORlMtPj5ORlM6IG5ldyBEYXRlKHZhbHVlKVxuICAgKiAgICAgICBlbHNlIE90aGVyIHR5cGVzXG4gICAqICAgICAgICAgTkZTLT4+TkZTOiBlc2NhcGVIdG1sKHZhbHVlKVxuICAgKiAgICAgICBlbmRcbiAgICogICAgIGVuZFxuICAgKiAgIGVuZFxuICAgKiAgIE5GUy0+Pk5GUzogZW5hYmxlQWxsR3JvdXBDb250cm9scyhmb3JtR3JvdXApXG4gICAqICAgTkZTLS0+PkM6IFJldHVybiBwcm9jZXNzZWQgZGF0YSBvYmplY3RcbiAgICpcbiAgICogQHN0YXRpY1xuICAgKiBAbWVtYmVyT2YgTmd4Rm9ybVNlcnZpY2VcbiAgICovXG4gIHN0YXRpYyBnZXRGb3JtRGF0YShmb3JtR3JvdXA6IEZvcm1Hcm91cCk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgICBjb25zdCBkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuICAgIGZvciAoY29uc3Qga2V5IGluIGZvcm1Hcm91cC5jb250cm9scykge1xuICAgICAgY29uc3QgY29udHJvbCA9IGZvcm1Hcm91cC5jb250cm9sc1trZXldO1xuICAgICAgY29uc3QgcGFyZW50UHJvcHMgPSBOZ3hGb3JtU2VydmljZS5nZXRQcm9wc0Zyb21Db250cm9sKGZvcm1Hcm91cCBhcyBGb3JtR3JvdXAgfCBGb3JtQXJyYXkpO1xuICAgICAgaWYgKCEoY29udHJvbCBpbnN0YW5jZW9mIEZvcm1Db250cm9sKSkge1xuICAgICAgICBjb25zdCB2YWx1ZSA9IE5neEZvcm1TZXJ2aWNlLmdldEZvcm1EYXRhKGNvbnRyb2wgYXMgRm9ybUdyb3VwKTtcbiAgICAgICAgY29uc3QgaXNWYWxpZCA9IGNvbnRyb2wudmFsaWQ7XG4gICAgICAgIGlmKHBhcmVudFByb3BzLm11bHRpcGxlKSB7XG4gICAgICAgICAgICBpZihpc1ZhbGlkKSB7XG4gICAgICAgICAgICAgICBkYXRhW2tleV0gPSB2YWx1ZTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHRoaXMucmVzZXQoY29udHJvbCBhcyBGb3JtQ29udHJvbCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGRhdGFba2V5XSA9IHZhbHVlO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgcHJvcHMgPSBOZ3hGb3JtU2VydmljZS5nZXRQcm9wc0Zyb21Db250cm9sKGNvbnRyb2wgYXMgRm9ybUNvbnRyb2wgfCBGb3JtQXJyYXkpO1xuICAgICAgbGV0IHZhbHVlID0gY29udHJvbC52YWx1ZTtcbiAgICAgIGlmICghSFRNTDVDaGVja1R5cGVzLmluY2x1ZGVzKHByb3BzWyd0eXBlJ10pKSB7XG4gICAgICAgIHN3aXRjaCAocHJvcHNbJ3R5cGUnXSkge1xuICAgICAgICAgIGNhc2UgSFRNTDVJbnB1dFR5cGVzLk5VTUJFUjpcbiAgICAgICAgICAgIHZhbHVlID0gcGFyc2VUb051bWJlcih2YWx1ZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlIEhUTUw1SW5wdXRUeXBlcy5EQVRFOlxuICAgICAgICAgIGNhc2UgSFRNTDVJbnB1dFR5cGVzLkRBVEVUSU1FX0xPQ0FMOlxuICAgICAgICAgICAgdmFsdWUgPSBuZXcgRGF0ZSh2YWx1ZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgdmFsdWUgPSBlc2NhcGVIdG1sKHZhbHVlKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZGF0YVtrZXldID0gdmFsdWU7XG4gICAgfVxuICAgIE5neEZvcm1TZXJ2aWNlLmVuYWJsZUFsbEdyb3VwQ29udHJvbHMoZm9ybUdyb3VwIGFzIEZvcm1Hcm91cCk7XG4gICAgcmV0dXJuIGRhdGE7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFZhbGlkYXRlcyBmaWVsZHMgaW4gYSBmb3JtIGNvbnRyb2wgb3IgZm9ybSBncm91cC5cbiAgICogQHN1bW1hcnkgUmVjdXJzaXZlbHkgdmFsaWRhdGVzIGFsbCBmaWVsZHMgaW4gYSBmb3JtIGNvbnRyb2wgb3IgZm9ybSBncm91cCwgbWFya2luZyB0aGVtIGFzIHRvdWNoZWQgYW5kIGRpcnR5LlxuICAgKiBQZXJmb3JtcyBjb21wcmVoZW5zaXZlIHZhbGlkYXRpb24gaW5jbHVkaW5nIHVuaXF1ZW5lc3MgY2hlY2tzIGZvciBwcmltYXJ5IGtleXMgaW4gRm9ybUFycmF5IHNjZW5hcmlvcy5cbiAgICogVGhpcyBtZXRob2QgZW5zdXJlcyBhbGwgdmFsaWRhdGlvbiBydWxlcyBhcmUgYXBwbGllZCBhbmQgZm9ybSBzdGF0ZSBpcyBwcm9wZXJseSB1cGRhdGVkLlxuICAgKlxuICAgKiBAcGFyYW0ge0Fic3RyYWN0Q29udHJvbH0gY29udHJvbCAtIFRoZSBjb250cm9sIG9yIGZvcm0gZ3JvdXAgdG8gdmFsaWRhdGVcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtwa10gLSBPcHRpb25hbCBwcmltYXJ5IGtleSBmaWVsZCBuYW1lIGZvciB1bmlxdWVuZXNzIHZhbGlkYXRpb25cbiAgICogQHBhcmFtIHtzdHJpbmd9IFtwYXRoXSAtIFRoZSBwYXRoIHRvIHRoZSBjb250cm9sIHdpdGhpbiB0aGUgZm9ybSBmb3IgZXJyb3IgcmVwb3J0aW5nXG4gICAqIEByZXR1cm4ge2Jvb2xlYW59IFRydWUgaWYgYWxsIGZpZWxkcyBhcmUgdmFsaWQsIGZhbHNlIG90aGVyd2lzZVxuICAgKiBAdGhyb3dzIHtFcnJvcn0gSWYgbm8gY29udHJvbCBpcyBmb3VuZCBhdCB0aGUgc3BlY2lmaWVkIHBhdGggb3IgaWYgdGhlIGNvbnRyb2wgdHlwZSBpcyB1bmtub3duXG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEMgYXMgQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgTkZTIGFzIE5neEZvcm1TZXJ2aWNlXG4gICAqICAgcGFydGljaXBhbnQgRkMgYXMgRm9ybUNvbnRyb2xcbiAgICogICBwYXJ0aWNpcGFudCBGRyBhcyBGb3JtR3JvdXBcbiAgICogICBwYXJ0aWNpcGFudCBGQSBhcyBGb3JtQXJyYXlcbiAgICpcbiAgICogICBDLT4+TkZTOiB2YWxpZGF0ZUZpZWxkcyhjb250cm9sLCBwaz8sIHBhdGg/KVxuICAgKiAgIGFsdCBDb250cm9sIGlzIEZvcm1Db250cm9sXG4gICAqICAgICBORlMtPj5GQzogbWFya0FzVG91Y2hlZCgpXG4gICAqICAgICBORlMtPj5GQzogbWFya0FzRGlydHkoKVxuICAgKiAgICAgTkZTLT4+RkM6IHVwZGF0ZVZhbHVlQW5kVmFsaWRpdHkoKVxuICAgKiAgICAgYWx0IElzIGluIEZvcm1BcnJheSBncm91cFxuICAgKiAgICAgICBORlMtPj5ORlM6IENoZWNrIHVuaXF1ZW5lc3MgaW4gZ3JvdXBcbiAgICogICAgICAgYWx0IE5vdCB1bmlxdWVcbiAgICogICAgICAgICBORlMtPj5GQzogc2V0RXJyb3JzKHtub3RVbmlxdWU6IHRydWV9KVxuICAgKiAgICAgICBlbmRcbiAgICogICAgIGVuZFxuICAgKiAgICAgTkZTLS0+PkM6IFJldHVybiBjb250cm9sLnZhbGlkXG4gICAqICAgZWxzZSBDb250cm9sIGlzIEZvcm1Hcm91cFxuICAgKiAgICAgbG9vcCBGb3IgZWFjaCBjaGlsZCBjb250cm9sXG4gICAqICAgICAgIE5GUy0+Pk5GUzogUmVjdXJzaXZlIHZhbGlkYXRlRmllbGRzKGNoaWxkKVxuICAgKiAgICAgZW5kXG4gICAqICAgICBORlMtLT4+QzogUmV0dXJuIGFsbFZhbGlkXG4gICAqICAgZWxzZSBDb250cm9sIGlzIEZvcm1BcnJheVxuICAgKiAgICAgbG9vcCBGb3IgZWFjaCBhcnJheSBjb250cm9sXG4gICAqICAgICAgIE5GUy0+Pk5GUzogUmVjdXJzaXZlIHZhbGlkYXRlRmllbGRzKGNoaWxkKVxuICAgKiAgICAgZW5kXG4gICAqICAgICBORlMtLT4+QzogUmV0dXJuIGFsbFZhbGlkXG4gICAqICAgZWxzZSBVbmtub3duIGNvbnRyb2wgdHlwZVxuICAgKiAgICAgTkZTLS0+PkM6IFRocm93IEVycm9yXG4gICAqICAgZW5kXG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQG1lbWJlck9mIE5neEZvcm1TZXJ2aWNlXG4gICAqL1xuICBzdGF0aWMgdmFsaWRhdGVGaWVsZHMoY29udHJvbDogQWJzdHJhY3RDb250cm9sLCBwaz86IHN0cmluZywgIHBhdGg/OiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICBjb250cm9sID0gcGF0aCA/IGNvbnRyb2wuZ2V0KHBhdGgpIGFzIEFic3RyYWN0Q29udHJvbCA6IGNvbnRyb2w7XG4gICAgaWYgKCFjb250cm9sKVxuICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBjb250cm9sIGZvdW5kIGF0IHBhdGg6ICR7cGF0aCB8fCAncm9vdCd9LmApO1xuXG4gICAgY29uc3QgaXNBbGxvd2VkID0gW0Zvcm1BcnJheSwgRm9ybUdyb3VwLCBGb3JtQ29udHJvbF0uc29tZSh0eXBlID0+IGNvbnRyb2wgaW5zdGFuY2VvZiB0eXBlKTtcbiAgICBpZiAoIWlzQWxsb3dlZClcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVW5rbm93biBjb250cm9sIHR5cGUgYXQ6ICR7cGF0aCB8fCAncm9vdCd9YCk7XG5cbiAgICBjb250cm9sLm1hcmtBc1RvdWNoZWQoKTtcbiAgICBjb250cm9sLm1hcmtBc0RpcnR5KCk7XG4gICAgY29udHJvbC51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KHsgZW1pdEV2ZW50OiB0cnVlIH0pO1xuXG4gICAgaWYgKGNvbnRyb2wgaW5zdGFuY2VvZiBGb3JtR3JvdXApIHtcbiAgICAgIE9iamVjdC52YWx1ZXMoY29udHJvbC5jb250cm9scykuZm9yRWFjaChjaGlsZENvbnRyb2wgPT4ge1xuICAgICAgICB0aGlzLnZhbGlkYXRlRmllbGRzKGNoaWxkQ29udHJvbCk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBpZiAoY29udHJvbCBpbnN0YW5jZW9mIEZvcm1BcnJheSkge1xuICAgICAgY29uc3QgdG90YWxHcm91cHMgPSBjb250cm9sLmxlbmd0aDtcbiAgICAgIGNvbnN0IGhhc1ZhbGlkID0gY29udHJvbC5jb250cm9scy5zb21lKGNvbnRyb2wgPT4gY29udHJvbC52YWxpZCk7XG4gICAgICBpZih0b3RhbEdyb3VwcyA+IDEgJiYgaGFzVmFsaWQpIHtcbiAgICAgICAgIGZvciAobGV0IGkgPSBjb250cm9sLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XG4gICAgICAgICAgY29uc3QgY2hpbGRDb250cm9sID0gY29udHJvbC5hdChpKTtcbiAgICAgICAgICAvLyBkaXNhYmxlIG5vIHZhbGlkIGdyb3VwcyBvbiBhcnJheVxuICAgICAgICAgIGlmICghY2hpbGRDb250cm9sLnZhbGlkKSB7XG4gICAgICAgICAgICAoY2hpbGRDb250cm9sLnBhcmVudCBhcyBGb3JtR3JvdXApLnNldEVycm9ycyhudWxsKTtcbiAgICAgICAgICAgICAoY2hpbGRDb250cm9sLnBhcmVudCBhcyBGb3JtR3JvdXApLnVwZGF0ZVZhbHVlQW5kVmFsaWRpdHkoeyBlbWl0RXZlbnQ6IHRydWUgfSk7XG4gICAgICAgICAgICBjaGlsZENvbnRyb2wuZGlzYWJsZSgpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnZhbGlkYXRlRmllbGRzKGNoaWxkQ29udHJvbCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBPYmplY3QudmFsdWVzKGNvbnRyb2wuY29udHJvbHMpLmZvckVhY2goY2hpbGRDb250cm9sID0+IHtcbiAgICAgICAgICB0aGlzLnZhbGlkYXRlRmllbGRzKGNoaWxkQ29udHJvbCk7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIGZ1bmN0aW9uIGdldENvbnRyb2xOYW1lKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCk6IHN0cmluZyB8IG51bGwge1xuICAgIC8vICAgY29uc3QgZ3JvdXAgPSBjb250cm9sLnBhcmVudCBhcyBGb3JtR3JvdXA7XG4gICAgLy8gICBpZiAoIWdyb3VwKVxuICAgIC8vICAgICAgIHJldHVybiBudWxsO1xuICAgIC8vICAgcmV0dXJuIE9iamVjdC5rZXlzKGdyb3VwLmNvbnRyb2xzKS5maW5kKG5hbWUgPT4gY29udHJvbCA9PT0gZ3JvdXAuZ2V0KG5hbWUpKSB8fCBudWxsO1xuICAgIC8vIH1cblxuICAgIHJldHVybiBjb250cm9sLnZhbGlkO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBHZW5lcmF0ZXMgdmFsaWRhdG9ycyBmcm9tIGNvbXBvbmVudCBwcm9wZXJ0aWVzLlxuICAgKiBAc3VtbWFyeSBDcmVhdGVzIGFuIGFycmF5IG9mIFZhbGlkYXRvckZuIGJhc2VkIG9uIHRoZSBzdXBwb3J0ZWQgdmFsaWRhdGlvbiBrZXlzIGluIHRoZSBjb21wb25lbnQgcHJvcGVydGllcy5cbiAgICogQHBhcmFtIHtGaWVsZFByb3BlcnRpZXN9IHByb3BzIC0gVGhlIGNvbXBvbmVudCBwcm9wZXJ0aWVzLlxuICAgKiBAcmV0dXJuIHtWYWxpZGF0b3JGbltdfSBBbiBhcnJheSBvZiB2YWxpZGF0b3IgZnVuY3Rpb25zLlxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgdmFsaWRhdG9yc0Zyb21Qcm9wcyhwcm9wczogRmllbGRQcm9wZXJ0aWVzKTogVmFsaWRhdG9yRm5bXSB7XG4gICAgY29uc3Qgc3VwcG9ydGVkVmFsaWRhdGlvbktleXMgPSBWYWxpZGF0aW9uLmtleXMoKTtcbiAgICByZXR1cm4gT2JqZWN0LmtleXMocHJvcHMpXG4gICAgICAuZmlsdGVyKChrOiBzdHJpbmcpID0+IHN1cHBvcnRlZFZhbGlkYXRpb25LZXlzLmluY2x1ZGVzKGspKVxuICAgICAgLm1hcCgoazogc3RyaW5nKSA9PiB7XG4gICAgICAgIHJldHVybiBWYWxpZGF0b3JGYWN0b3J5LnNwYXduKHByb3BzLCBrKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDcmVhdGVzIGEgRm9ybUNvbnRyb2wgZnJvbSBjb21wb25lbnQgcHJvcGVydGllcy5cbiAgICogQHN1bW1hcnkgR2VuZXJhdGVzIGEgRm9ybUNvbnRyb2wgd2l0aCB2YWxpZGF0b3JzIGFuZCBpbml0aWFsIGNvbmZpZ3VyYXRpb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkXG4gICAqIGNvbXBvbmVudCBwcm9wZXJ0aWVzLiBIYW5kbGVzIGRpZmZlcmVudCBpbnB1dCB0eXBlcywgc2V0cyBpbml0aWFsIHZhbHVlcywgYW5kIGNvbmZpZ3VyZXNcbiAgICogdmFsaWRhdGlvbiBydWxlcyBhbmQgdXBkYXRlIG1vZGVzLlxuICAgKlxuICAgKiBAcGFyYW0ge0ZpZWxkUHJvcGVydGllc30gcHJvcHMgLSBUaGUgY29tcG9uZW50IHByb3BlcnRpZXMgZGVmaW5pbmcgdGhlIGNvbnRyb2wgY29uZmlndXJhdGlvblxuICAgKiBAcGFyYW0ge0ZpZWxkVXBkYXRlTW9kZX0gW3VwZGF0ZU1vZGU9J2NoYW5nZSddIC0gVGhlIHVwZGF0ZSBtb2RlIGZvciB0aGUgY29udHJvbCAoJ2NoYW5nZScsICdibHVyJywgJ3N1Ym1pdCcpXG4gICAqIEByZXR1cm4ge0Zvcm1Db250cm9sfSBUaGUgY3JlYXRlZCBGb3JtQ29udHJvbCB3aXRoIHByb3BlciBjb25maWd1cmF0aW9uIGFuZCB2YWxpZGF0b3JzXG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEMgYXMgQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgTkZTIGFzIE5neEZvcm1TZXJ2aWNlXG4gICAqICAgcGFydGljaXBhbnQgVkYgYXMgVmFsaWRhdG9yRmFjdG9yeVxuICAgKiAgIHBhcnRpY2lwYW50IEFGIGFzIEFuZ3VsYXIgRm9ybXNcbiAgICpcbiAgICogICBDLT4+TkZTOiBmcm9tUHJvcHMocHJvcHMsIHVwZGF0ZU1vZGU/KVxuICAgKiAgIE5GUy0+Pk5GUzogdmFsaWRhdG9yc0Zyb21Qcm9wcyhwcm9wcylcbiAgICogICBORlMtPj5WRjogQ3JlYXRlIHZhbGlkYXRvcnMgZnJvbSBwcm9wc1xuICAgKiAgIFZGLS0+Pk5GUzogUmV0dXJuIHZhbGlkYXRvciBhcnJheVxuICAgKiAgIE5GUy0+Pk5GUzogQ29tcG9zZSB2YWxpZGF0b3JzXG4gICAqICAgYWx0IHByb3BzLnZhbHVlIGV4aXN0cyBhbmQgbm90IGNoZWNrYm94XG4gICAqICAgICBhbHQgcHJvcHMudHlwZSBpcyBEQVRFXG4gICAqICAgICAgIE5GUy0+Pk5GUzogVmFsaWRhdGUgZGF0ZSBmb3JtYXRcbiAgICogICAgIGVuZFxuICAgKiAgICAgTkZTLT4+TkZTOiBTZXQgaW5pdGlhbCB2YWx1ZVxuICAgKiAgIGVuZFxuICAgKiAgIE5GUy0+PkFGOiBuZXcgRm9ybUNvbnRyb2woY29uZmlnKVxuICAgKiAgIEFGLS0+Pk5GUzogUmV0dXJuIEZvcm1Db250cm9sXG4gICAqICAgTkZTLS0+PkM6IFJldHVybiBjb25maWd1cmVkIEZvcm1Db250cm9sXG4gICAqXG4gICAqIEBzdGF0aWNcbiAgICogQG1lbWJlck9mIE5neEZvcm1TZXJ2aWNlXG4gICAqL1xuICBzdGF0aWMgZnJvbVByb3BzKHByb3BzOiBGaWVsZFByb3BlcnRpZXMsIHVwZGF0ZU1vZGU6IEZpZWxkVXBkYXRlTW9kZSA9ICdjaGFuZ2UnKTogRm9ybUNvbnRyb2wge1xuICAgIGNvbnN0IHZhbGlkYXRvcnMgPSB0aGlzLnZhbGlkYXRvcnNGcm9tUHJvcHMocHJvcHMpO1xuICAgIGNvbnN0IGNvbXBvc2VkID0gdmFsaWRhdG9ycy5sZW5ndGggPyBWYWxpZGF0b3JzLmNvbXBvc2UodmFsaWRhdG9ycykgOiBudWxsO1xuICAgIHJldHVybiBuZXcgRm9ybUNvbnRyb2woXG4gICAgICB7XG4gICAgICAgIHZhbHVlOlxuICAgICAgICAgIHByb3BzLnZhbHVlICYmIHByb3BzLnR5cGUgIT09IEhUTUw1SW5wdXRUeXBlcy5DSEVDS0JPWFxuICAgICAgICAgICAgPyBwcm9wcy50eXBlID09PSBIVE1MNUlucHV0VHlwZXMuREFURVxuICAgICAgICAgICAgICA/ICFpc1ZhbGlkRGF0ZShwYXJzZURhdGUocHJvcHMuZm9ybWF0IGFzIHN0cmluZywgcHJvcHMudmFsdWUgYXMgc3RyaW5nKSlcbiAgICAgICAgICAgICAgICA/IHVuZGVmaW5lZCA6IHByb3BzLnZhbHVlIDpcbiAgICAgICAgICAgICAgKHByb3BzLnZhbHVlIGFzIHVua25vd24pIDogdW5kZWZpbmVkLFxuICAgICAgICBkaXNhYmxlZDogcHJvcHMuZGlzYWJsZWQsXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICB2YWxpZGF0b3JzOiBjb21wb3NlZCxcbiAgICAgICAgdXBkYXRlT246IHVwZGF0ZU1vZGUsXG4gICAgICB9LFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFJldHJpZXZlcyBwcm9wZXJ0aWVzIGZyb20gYSBGb3JtQ29udHJvbCwgRm9ybUFycmF5LCBvciBGb3JtR3JvdXAuXG4gICAqIEBzdW1tYXJ5IEdldHMgdGhlIEZpZWxkUHJvcGVydGllcyBhc3NvY2lhdGVkIHdpdGggYSBmb3JtIGNvbnRyb2wgZnJvbSB0aGUgaW50ZXJuYWwgV2Vha01hcC5cbiAgICogVGhpcyBtZXRob2QgcHJvdmlkZXMgYWNjZXNzIHRvIHRoZSBvcmlnaW5hbCBjb21wb25lbnQgcHJvcGVydGllcyB0aGF0IHdlcmUgdXNlZCB0byBjcmVhdGVcbiAgICogdGhlIGNvbnRyb2wsIGVuYWJsaW5nIHZhbGlkYXRpb24sIHJlbmRlcmluZywgYW5kIGJlaGF2aW9yIGNvbmZpZ3VyYXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSB7Rm9ybUNvbnRyb2wgfCBGb3JtQXJyYXkgfCBGb3JtR3JvdXB9IGNvbnRyb2wgLSBUaGUgZm9ybSBjb250cm9sIHRvIGdldCBwcm9wZXJ0aWVzIGZvclxuICAgKiBAcmV0dXJuIHtGaWVsZFByb3BlcnRpZXN9IFRoZSBwcm9wZXJ0aWVzIGFzc29jaWF0ZWQgd2l0aCB0aGUgY29udHJvbCwgb3IgZW1wdHkgb2JqZWN0IGlmIG5vdCBmb3VuZFxuICAgKlxuICAgKiBAc3RhdGljXG4gICAqIEBtZW1iZXJPZiBOZ3hGb3JtU2VydmljZVxuICAgKi9cbiAgc3RhdGljIGdldFByb3BzRnJvbUNvbnRyb2woY29udHJvbDogRm9ybUNvbnRyb2wgfCBGb3JtQXJyYXkgfCBGb3JtR3JvdXApOiBGaWVsZFByb3BlcnRpZXMge1xuICAgIHJldHVybiB0aGlzLmNvbnRyb2xzLmdldChjb250cm9sKSB8fCB7fSBhcyBGaWVsZFByb3BlcnRpZXM7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEZpbmRzIGEgcGFyZW50IGVsZW1lbnQgd2l0aCBhIHNwZWNpZmljIHRhZyBpbiB0aGUgRE9NIHRyZWUuXG4gICAqIEBzdW1tYXJ5IFRyYXZlcnNlcyB1cCB0aGUgRE9NIHRyZWUgdG8gZmluZCB0aGUgbmVhcmVzdCBwYXJlbnQgZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdGFnIG5hbWUuXG4gICAqIFRoaXMgaXMgdXNlZnVsIGZvciBmaW5kaW5nIGNvbnRhaW5lciBlbGVtZW50cyBvciBzcGVjaWZpYyBwYXJlbnQgY29tcG9uZW50cyBpbiB0aGUgRE9NIGhpZXJhcmNoeS5cbiAgICogVGhlIHNlYXJjaCBpcyBjYXNlLWluc2Vuc2l0aXZlIGZvciB0YWcgbmFtZSBtYXRjaGluZy5cbiAgICpcbiAgICogQHBhcmFtIHtIVE1MRWxlbWVudH0gZWwgLSBUaGUgc3RhcnRpbmcgZWxlbWVudCB0byB0cmF2ZXJzZSBmcm9tXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0YWcgLSBUaGUgdGFnIG5hbWUgdG8gc2VhcmNoIGZvciAoY2FzZS1pbnNlbnNpdGl2ZSlcbiAgICogQHJldHVybiB7SFRNTEVsZW1lbnR9IFRoZSBmb3VuZCBwYXJlbnQgZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdGFnXG4gICAqIEB0aHJvd3Mge0Vycm9yfSBJZiBubyBwYXJlbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHRhZyBpcyBmb3VuZCBpbiB0aGUgRE9NIHRyZWVcbiAgICpcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgQyBhcyBDb21wb25lbnRcbiAgICogICBwYXJ0aWNpcGFudCBORlMgYXMgTmd4Rm9ybVNlcnZpY2VcbiAgICogICBwYXJ0aWNpcGFudCBET00gYXMgRE9NIFRyZWVcbiAgICpcbiAgICogICBDLT4+TkZTOiBnZXRQYXJlbnRFbChlbGVtZW50LCB0YWdOYW1lKVxuICAgKiAgIGxvb3AgVHJhdmVyc2UgdXAgRE9NIHRyZWVcbiAgICogICAgIE5GUy0+PkRPTTogR2V0IHBhcmVudEVsZW1lbnRcbiAgICogICAgIERPTS0tPj5ORlM6IFJldHVybiBwYXJlbnQgb3IgbnVsbFxuICAgKiAgICAgYWx0IFBhcmVudCBleGlzdHMgYW5kIHRhZyBtYXRjaGVzXG4gICAqICAgICAgIE5GUy0tPj5DOiBSZXR1cm4gcGFyZW50IGVsZW1lbnRcbiAgICogICAgIGVsc2UgUGFyZW50IGlzIG51bGxcbiAgICogICAgICAgTkZTLS0+PkM6IFRocm93IEVycm9yXG4gICAqICAgICBlbmRcbiAgICogICBlbmRcbiAgICpcbiAgICogQHN0YXRpY1xuICAgKiBAbWVtYmVyT2YgTmd4Rm9ybVNlcnZpY2VcbiAgICovXG4gIHN0YXRpYyBnZXRQYXJlbnRFbChlbDogSFRNTEVsZW1lbnQsIHRhZzogc3RyaW5nKTogSFRNTEVsZW1lbnQge1xuICAgIGxldCBwYXJlbnQ6IEhUTUxFbGVtZW50IHwgbnVsbDtcbiAgICB3aGlsZSAoKHBhcmVudCA9IGVsLnBhcmVudEVsZW1lbnQpICE9PSBudWxsKSB7XG4gICAgICBpZiAocGFyZW50LnRhZ05hbWUudG9Mb3dlckNhc2UoKSA9PT0gdGFnLnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgICAgcmV0dXJuIHBhcmVudDtcbiAgICAgIH1cbiAgICAgIGVsID0gcGFyZW50O1xuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgTm8gcGFyZW50IHdpdGggdGhlIHRhZyAke3RhZ30gd2FzIGZvdW5kIGZvciBwcm92aWRlZCBlbGVtZW50YCxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBSZWdpc3RlcnMgYSBjb250cm9sIHdpdGggaXRzIHByb3BlcnRpZXMgaW4gdGhlIGludGVybmFsIFdlYWtNYXAuXG4gICAqIEBzdW1tYXJ5IEFzc29jaWF0ZXMgYSBmb3JtIGNvbnRyb2wgd2l0aCBpdHMgY29tcG9uZW50IHByb3BlcnRpZXMgZm9yIGxhdGVyIHJldHJpZXZhbC5cbiAgICogVGhpcyBlbmFibGVzIHRoZSBzZXJ2aWNlIHRvIG1haW50YWluIG1ldGFkYXRhIGFib3V0IGNvbnRyb2xzIHdpdGhvdXQgY3JlYXRpbmcgbWVtb3J5IGxlYWtzLFxuICAgKiBhcyBXZWFrTWFwIGF1dG9tYXRpY2FsbHkgY2xlYW5zIHVwIHJlZmVyZW5jZXMgd2hlbiBjb250cm9scyBhcmUgZ2FyYmFnZSBjb2xsZWN0ZWQuXG4gICAqXG4gICAqIEBwYXJhbSB7QWJzdHJhY3RDb250cm9sfSBjb250cm9sIC0gVGhlIGNvbnRyb2wgdG8gcmVnaXN0ZXIgKEZvcm1Db250cm9sLCBGb3JtR3JvdXAsIG9yIEZvcm1BcnJheSlcbiAgICogQHBhcmFtIHtGaWVsZFByb3BlcnRpZXN9IHByb3BzIC0gVGhlIHByb3BlcnRpZXMgdG8gYXNzb2NpYXRlIHdpdGggdGhlIGNvbnRyb2xcbiAgICogQHJldHVybiB7dm9pZH1cbiAgICpcbiAgICogQHN0YXRpY1xuICAgKiBAbWVtYmVyT2YgTmd4Rm9ybVNlcnZpY2VcbiAgICovXG4gIHN0YXRpYyByZWdpc3Rlcihjb250cm9sOiBBYnN0cmFjdENvbnRyb2wsIHByb3BzOiBGaWVsZFByb3BlcnRpZXMpOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzLnNldChjb250cm9sLCBwcm9wcyk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFVucmVnaXN0ZXJzIGEgY29udHJvbC5cbiAgICogQHN1bW1hcnkgUmVtb3ZlcyBhIGNvbnRyb2wgYW5kIGl0cyBhc3NvY2lhdGVkIHByb3BlcnRpZXMgZnJvbSB0aGUgaW50ZXJuYWwgV2Vha01hcC5cbiAgICogQHBhcmFtIHtBYnN0cmFjdENvbnRyb2x9IGNvbnRyb2wgLSBUaGUgY29udHJvbCB0byB1bnJlZ2lzdGVyLlxuICAgKiBAcmV0dXJuIHtib29sZWFufSBUcnVlIGlmIHRoZSBjb250cm9sIHdhcyBzdWNjZXNzZnVsbHkgdW5yZWdpc3RlcmVkLCBmYWxzZSBvdGhlcndpc2UuXG4gICAqL1xuICBzdGF0aWMgdW5yZWdpc3Rlcihjb250cm9sOiBBYnN0cmFjdENvbnRyb2wpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5jb250cm9scy5kZWxldGUoY29udHJvbCk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFJlc2V0cyBhIGZvcm0gZ3JvdXAuXG4gICAqIEBzdW1tYXJ5IFJlY3Vyc2l2ZWx5IHJlc2V0cyBhbGwgY29udHJvbHMgaW4gYSBmb3JtIGdyb3VwLCBjbGVhcmluZyB2YWx1ZXMsIGVycm9ycywgYW5kIG1hcmtpbmcgdGhlbSBhcyBwcmlzdGluZSBhbmQgdW50b3VjaGVkLlxuICAgKiBAcGFyYW0ge0Zvcm1Hcm91cH0gZm9ybUdyb3VwIC0gVGhlIGZvcm0gZ3JvdXAgdG8gcmVzZXQuXG4gICAqL1xuICBzdGF0aWMgcmVzZXQoZm9ybUdyb3VwOiBGb3JtR3JvdXAgfCBGb3JtQ29udHJvbCk6IHZvaWQge1xuICAgIGlmKGZvcm1Hcm91cCBpbnN0YW5jZW9mIEZvcm1Db250cm9sKSB7XG4gICAgICBjb25zdCBjb250cm9sID0gZm9ybUdyb3VwIGFzIEZvcm1Db250cm9sO1xuICAgICAgY29uc3QgeyB0eXBlIH0gPSBOZ3hGb3JtU2VydmljZS5nZXRQcm9wc0Zyb21Db250cm9sKGNvbnRyb2wpO1xuICAgICAgaWYgKCFIVE1MNUNoZWNrVHlwZXMuaW5jbHVkZXModHlwZSkpXG4gICAgICAgIGNvbnRyb2wuc2V0VmFsdWUoXCJcIik7XG4gICAgICBjb250cm9sLm1hcmtBc1ByaXN0aW5lKCk7XG4gICAgICBjb250cm9sLm1hcmtBc1VudG91Y2hlZCgpO1xuICAgICAgY29udHJvbC5zZXRFcnJvcnMobnVsbCk7XG4gICAgICBjb250cm9sLnVwZGF0ZVZhbHVlQW5kVmFsaWRpdHkoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZm9yIChjb25zdCBrZXkgaW4gZm9ybUdyb3VwLmNvbnRyb2xzKSB7XG4gICAgICAgIGNvbnN0IGNvbnRyb2wgPSBmb3JtR3JvdXAuY29udHJvbHNba2V5XTtcbiAgICAgICAgTmd4Rm9ybVNlcnZpY2UucmVzZXQoY29udHJvbCBhcyBGb3JtQ29udHJvbCk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuIl19