@decaf-ts/for-angular 0.0.4 → 0.0.6

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 (85) hide show
  1. package/LICENSE.md +646 -144
  2. package/README.md +37 -242
  3. package/dist/lib/README.md +92 -0
  4. package/dist/lib/assets/i18n/en.json +131 -0
  5. package/dist/lib/assets/images/angular-logo.svg +45 -0
  6. package/dist/lib/assets/images/decaf-logo-black.svg +22 -0
  7. package/dist/lib/assets/images/decaf-logo-lw.svg +50 -0
  8. package/dist/lib/assets/images/decaf-logo-white.svg +22 -0
  9. package/dist/lib/assets/images/decaf-logo.svg +54 -0
  10. package/dist/lib/components/component-renderer/component-renderer.component.d.ts +267 -0
  11. package/dist/lib/components/crud-field/crud-field.component.d.ts +445 -0
  12. package/dist/{for-angular/components/decaf-crud-form → lib/components/crud-form}/constants.d.ts +0 -0
  13. package/dist/lib/components/crud-form/crud-form.component.d.ts +102 -0
  14. package/dist/{for-angular/components/decaf-crud-form → lib/components/crud-form}/types.d.ts +0 -0
  15. package/dist/lib/components/model-renderer/model-renderer.component.d.ts +96 -0
  16. package/dist/lib/engine/DynamicModule.d.ts +17 -0
  17. package/dist/{for-angular → lib}/engine/NgxCrudFormField.d.ts +21 -17
  18. package/dist/lib/engine/NgxFormService.d.ts +167 -0
  19. package/dist/lib/engine/NgxRenderingEngine.d.ts +127 -0
  20. package/dist/lib/engine/NgxRenderingEngine2.d.ts +250 -0
  21. package/dist/lib/engine/ValidatorFactory.d.ts +15 -0
  22. package/dist/lib/engine/constants.d.ts +151 -0
  23. package/dist/lib/engine/decorators.d.ts +25 -0
  24. package/dist/lib/engine/index.d.ts +15 -0
  25. package/dist/lib/engine/types.d.ts +294 -0
  26. package/dist/lib/esm2022/components/component-renderer/component-renderer.component.mjs +312 -0
  27. package/dist/lib/esm2022/components/crud-field/crud-field.component.mjs +297 -0
  28. package/dist/lib/esm2022/components/crud-form/constants.mjs +14 -0
  29. package/dist/lib/esm2022/components/crud-form/crud-form.component.mjs +139 -0
  30. package/dist/lib/esm2022/components/crud-form/types.mjs +2 -0
  31. package/dist/lib/esm2022/components/model-renderer/model-renderer.component.mjs +138 -0
  32. package/dist/{for-angular → lib}/esm2022/decaf-ts-for-angular.mjs +0 -0
  33. package/dist/lib/esm2022/engine/DynamicModule.mjs +18 -0
  34. package/dist/lib/esm2022/engine/NgxCrudFormField.mjs +123 -0
  35. package/dist/lib/esm2022/engine/NgxFormService.mjs +315 -0
  36. package/dist/lib/esm2022/engine/NgxRenderingEngine.mjs +192 -0
  37. package/dist/lib/esm2022/engine/NgxRenderingEngine2.mjs +332 -0
  38. package/dist/lib/esm2022/engine/ValidatorFactory.mjs +102 -0
  39. package/dist/lib/esm2022/engine/constants.mjs +160 -0
  40. package/dist/lib/esm2022/engine/decorators.mjs +38 -0
  41. package/dist/lib/esm2022/engine/index.mjs +16 -0
  42. package/dist/lib/esm2022/engine/types.mjs +2 -0
  43. package/dist/lib/esm2022/for-angular.module.mjs +118 -0
  44. package/dist/lib/esm2022/interfaces.mjs +2 -0
  45. package/dist/lib/esm2022/public-apis.mjs +13 -0
  46. package/dist/lib/fesm2022/decaf-ts-for-angular.mjs +2152 -0
  47. package/dist/lib/fesm2022/decaf-ts-for-angular.mjs.map +1 -0
  48. package/dist/lib/for-angular.module.d.ts +45 -0
  49. package/dist/{for-angular → lib}/index.d.ts +0 -0
  50. package/dist/lib/interfaces.d.ts +28 -0
  51. package/dist/lib/public-apis.d.ts +12 -0
  52. package/package.json +71 -26
  53. package/dist/for-angular/README.md +0 -297
  54. package/dist/for-angular/assets/i18n/en.json +0 -21
  55. package/dist/for-angular/components/decaf-crud-field/decaf-crud-field.component.d.ts +0 -49
  56. package/dist/for-angular/components/decaf-crud-form/decaf-crud-form.component.d.ts +0 -28
  57. package/dist/for-angular/components/decaf-model-renderer/decaf-model-renderer.component.d.ts +0 -20
  58. package/dist/for-angular/engine/DynamicModule.d.ts +0 -2
  59. package/dist/for-angular/engine/NgxFormService.d.ts +0 -119
  60. package/dist/for-angular/engine/NgxRenderingEngine.d.ts +0 -18
  61. package/dist/for-angular/engine/ValidatorFactory.d.ts +0 -4
  62. package/dist/for-angular/engine/constants.d.ts +0 -13
  63. package/dist/for-angular/engine/decorators.d.ts +0 -1
  64. package/dist/for-angular/engine/index.d.ts +0 -5
  65. package/dist/for-angular/engine/types.d.ts +0 -44
  66. package/dist/for-angular/esm2022/components/decaf-crud-field/decaf-crud-field.component.mjs +0 -129
  67. package/dist/for-angular/esm2022/components/decaf-crud-form/constants.mjs +0 -14
  68. package/dist/for-angular/esm2022/components/decaf-crud-form/decaf-crud-form.component.mjs +0 -80
  69. package/dist/for-angular/esm2022/components/decaf-crud-form/types.mjs +0 -2
  70. package/dist/for-angular/esm2022/components/decaf-model-renderer/decaf-model-renderer.component.mjs +0 -47
  71. package/dist/for-angular/esm2022/engine/DynamicModule.mjs +0 -3
  72. package/dist/for-angular/esm2022/engine/NgxCrudFormField.mjs +0 -115
  73. package/dist/for-angular/esm2022/engine/NgxFormService.mjs +0 -235
  74. package/dist/for-angular/esm2022/engine/NgxRenderingEngine.mjs +0 -84
  75. package/dist/for-angular/esm2022/engine/ValidatorFactory.mjs +0 -48
  76. package/dist/for-angular/esm2022/engine/constants.mjs +0 -15
  77. package/dist/for-angular/esm2022/engine/decorators.mjs +0 -14
  78. package/dist/for-angular/esm2022/engine/index.mjs +0 -6
  79. package/dist/for-angular/esm2022/engine/types.mjs +0 -2
  80. package/dist/for-angular/esm2022/interfaces.mjs +0 -2
  81. package/dist/for-angular/esm2022/public-apis.mjs +0 -6
  82. package/dist/for-angular/fesm2022/decaf-ts-for-angular.mjs +0 -759
  83. package/dist/for-angular/fesm2022/decaf-ts-for-angular.mjs.map +0 -1
  84. package/dist/for-angular/interfaces.d.ts +0 -8
  85. package/dist/for-angular/public-apis.d.ts +0 -5
@@ -0,0 +1,312 @@
1
+ import { Component, EnvironmentInjector, EventEmitter, inject, Input, Output, reflectComponentType, TemplateRef, ViewChild, ViewContainerRef, } from '@angular/core';
2
+ import { NgxRenderingEngine2 } from 'src/lib/engine/NgxRenderingEngine2';
3
+ import { ForAngularModule, getLogger } from 'src/lib/for-angular.module';
4
+ import * as i0 from "@angular/core";
5
+ import * as i1 from "@angular/common";
6
+ /**
7
+ * @description Dynamic component renderer for Decaf Angular applications.
8
+ * @summary This component provides a flexible way to dynamically render Angular components
9
+ * at runtime based on a tag name. It handles the creation, property binding, and event
10
+ * subscription for dynamically loaded components. This is particularly useful for
11
+ * building configurable UIs where components need to be determined at runtime.
12
+ *
13
+ * @component {ComponentRendererComponent}
14
+ * @example
15
+ * <ngx-decaf-component-renderer
16
+ * [tag]="tag"
17
+ * [globals]="globals"
18
+ * (listenEvent)="listenEvent($event)">
19
+ * </ngx-decaf-component-renderer>
20
+ *
21
+ * @mermaid
22
+ * classDiagram
23
+ * class ComponentRendererComponent {
24
+ * +ViewContainerRef vcr
25
+ * +string tag
26
+ * +Record~string, unknown~ globals
27
+ * +EnvironmentInjector injector
28
+ * +ComponentRef~unknown~ component
29
+ * +EventEmitter~ModelRenderCustomEvent~ listenEvent
30
+ * +ngOnInit()
31
+ * +ngOnDestroy()
32
+ * +ngOnChanges(changes)
33
+ * -createComponent(tag, globals)
34
+ * -subscribeEvents()
35
+ * -unsubscribeEvents()
36
+ * }
37
+ * ComponentRendererComponent --|> OnInit
38
+ * ComponentRendererComponent --|> OnChanges
39
+ * ComponentRendererComponent --|> OnDestroy
40
+ *
41
+ * @implements {OnInit}
42
+ * @implements {OnChanges}
43
+ * @implements {OnDestroy}
44
+ */
45
+ export class ComponentRendererComponent {
46
+ /**
47
+ * @description Creates an instance of ComponentRendererComponent.
48
+ * @summary Initializes a new ComponentRendererComponent. This component doesn't require
49
+ * any dependencies to be injected in its constructor as it uses the inject function to
50
+ * obtain the EnvironmentInjector.
51
+ *
52
+ * @memberOf ComponentRendererComponent
53
+ */
54
+ constructor() {
55
+ /**
56
+ * @description Global properties to pass to the rendered component.
57
+ * @summary This input property allows passing a set of properties to the dynamically
58
+ * rendered component. These properties will be mapped to the component's inputs if they
59
+ * match. Properties that don't match any input on the target component will be filtered out
60
+ * with a warning.
61
+ *
62
+ * @type {Record<string, unknown>}
63
+ * @default {}
64
+ * @memberOf ComponentRendererComponent
65
+ */
66
+ this.globals = {};
67
+ /**
68
+ * @description Injector used for dependency injection in the dynamic component.
69
+ * @summary This injector is used when creating the dynamic component to provide it with
70
+ * access to the application's dependency injection system. It ensures that the dynamically
71
+ * created component can access the same services and dependencies as statically created
72
+ * components.
73
+ *
74
+ * @type {EnvironmentInjector}
75
+ * @memberOf ComponentRendererComponent
76
+ */
77
+ this.injector = inject(EnvironmentInjector);
78
+ /**
79
+ * @description Event emitter for events from the rendered component.
80
+ * @summary This output property emits events that originate from the dynamically rendered
81
+ * component. It allows the parent component to listen for and respond to events from the
82
+ * dynamic component, creating a communication channel between the parent and the dynamically
83
+ * rendered child.
84
+ *
85
+ * @type {EventEmitter<ModelRenderCustomEvent>}
86
+ * @memberOf ComponentRendererComponent
87
+ */
88
+ this.listenEvent = new EventEmitter();
89
+ this.parent = undefined;
90
+ this.logger = getLogger(this);
91
+ }
92
+ /**
93
+ * @description Initializes the component after Angular first displays the data-bound properties.
94
+ * @summary Sets up the component by creating the dynamic component specified by the tag input.
95
+ * This method is called once when the component is initialized and triggers the dynamic
96
+ * component creation process with the provided tag name and global properties.
97
+ *
98
+ * @mermaid
99
+ * sequenceDiagram
100
+ * participant A as Angular Lifecycle
101
+ * participant C as ComponentRendererComponent
102
+ * participant R as NgxRenderingEngine2
103
+ *
104
+ * A->>C: ngOnInit()
105
+ * C->>C: createComponent(tag, globals)
106
+ * C->>R: components(tag)
107
+ * R-->>C: Return component constructor
108
+ * C->>C: Process component inputs
109
+ * C->>C: Create component instance
110
+ * C->>C: subscribeEvents()
111
+ *
112
+ * @return {void}
113
+ * @memberOf ComponentRendererComponent
114
+ */
115
+ ngOnInit() {
116
+ if (!this.parent) {
117
+ this.createComponent(this.tag, this.globals);
118
+ }
119
+ else {
120
+ this.createParentComponent();
121
+ }
122
+ }
123
+ /**
124
+ * @description Cleans up resources when the component is destroyed.
125
+ * @summary Performs cleanup operations when the component is being destroyed by Angular.
126
+ * This includes unsubscribing from all event emitters of the dynamic component and
127
+ * destroying the rendering engine instance to prevent memory leaks.
128
+ *
129
+ * @mermaid
130
+ * sequenceDiagram
131
+ * participant A as Angular Lifecycle
132
+ * participant C as ComponentRendererComponent
133
+ * participant R as NgxRenderingEngine2
134
+ *
135
+ * A->>C: ngOnDestroy()
136
+ * alt component exists
137
+ * C->>C: unsubscribeEvents()
138
+ * C->>R: destroy()
139
+ * end
140
+ *
141
+ * @return {Promise<void>} A promise that resolves when cleanup is complete
142
+ * @memberOf ComponentRendererComponent
143
+ */
144
+ async ngOnDestroy() {
145
+ if (this.component) {
146
+ this.unsubscribeEvents();
147
+ NgxRenderingEngine2.destroy();
148
+ }
149
+ }
150
+ /**
151
+ * @description Creates and renders a dynamic component.
152
+ * @summary This method handles the creation of a dynamic component based on the provided tag.
153
+ * It retrieves the component constructor from the rendering engine, processes its inputs,
154
+ * filters out unmapped properties, creates the component instance, and sets up event subscriptions.
155
+ *
156
+ * @param {string} tag - The tag name of the component to create
157
+ * @param {KeyValue} globals - Global properties to pass to the component
158
+ * @return {void}
159
+ *
160
+ * @mermaid
161
+ * sequenceDiagram
162
+ * participant C as ComponentRendererComponent
163
+ * participant R as NgxRenderingEngine2
164
+ * participant V as ViewContainerRef
165
+ *
166
+ * C->>R: components(tag)
167
+ * R-->>C: Return component constructor
168
+ * C->>C: reflectComponentType(component)
169
+ * C->>C: Process input properties
170
+ * C->>C: Filter unmapped properties
171
+ * C->>V: clear()
172
+ * C->>R: createComponent(component, props, metadata, vcr, injector, [])
173
+ * R-->>C: Return component reference
174
+ * C->>C: subscribeEvents()
175
+ *
176
+ * @private
177
+ * @memberOf ComponentRendererComponent
178
+ */
179
+ createComponent(tag, globals = {}) {
180
+ const component = NgxRenderingEngine2.components(tag)
181
+ ?.constructor;
182
+ const metadata = reflectComponentType(component);
183
+ const componentInputs = metadata.inputs;
184
+ const props = globals?.['item'];
185
+ delete props['tag'];
186
+ const inputKeys = Object.keys(props);
187
+ const unmappedKeys = [];
188
+ for (const input of inputKeys) {
189
+ if (!inputKeys.length)
190
+ break;
191
+ const prop = componentInputs.find((item) => item.propName === input);
192
+ if (!prop) {
193
+ delete props[input];
194
+ unmappedKeys.push(input);
195
+ }
196
+ }
197
+ if (unmappedKeys.length)
198
+ this.logger.info(`Unmapped input properties for component ${tag}: ${unmappedKeys.join(', ')}`);
199
+ this.vcr.clear();
200
+ this.component = NgxRenderingEngine2.createComponent(component, props, metadata, this.vcr, this.injector, []);
201
+ this.subscribeEvents();
202
+ }
203
+ createParentComponent() {
204
+ const { component, inputs } = this.parent;
205
+ const metadata = reflectComponentType(component);
206
+ const template = this.vcr.createEmbeddedView(this.inner, this.injector).rootNodes;
207
+ this.component = NgxRenderingEngine2.createComponent(component, inputs, metadata, this.vcr, this.injector, template);
208
+ this.subscribeEvents();
209
+ }
210
+ /**
211
+ * @description Subscribes to events emitted by the dynamic component.
212
+ * @summary This method sets up subscriptions to all EventEmitter properties of the
213
+ * dynamically created component. When an event is emitted by the dynamic component,
214
+ * it is captured and re-emitted through the listenEvent output property with additional
215
+ * metadata about the event source.
216
+ *
217
+ * @mermaid
218
+ * sequenceDiagram
219
+ * participant C as ComponentRendererComponent
220
+ * participant D as Dynamic Component
221
+ * participant P as Parent Component
222
+ *
223
+ * C->>C: subscribeEvents()
224
+ * C->>D: Get instance properties
225
+ * loop For each property
226
+ * C->>C: Check if property is EventEmitter
227
+ * alt is EventEmitter
228
+ * C->>D: Subscribe to event
229
+ * D-->>C: Event emitted
230
+ * C->>P: Re-emit event with metadata
231
+ * end
232
+ * end
233
+ *
234
+ * @private
235
+ * @return {void}
236
+ * @memberOf ComponentRendererComponent
237
+ */
238
+ subscribeEvents() {
239
+ if (this.component) {
240
+ const instance = this.component?.instance;
241
+ const componentKeys = Object.keys(instance);
242
+ for (const key of componentKeys) {
243
+ const value = instance[key];
244
+ if (value instanceof EventEmitter)
245
+ instance[key].subscribe((event) => {
246
+ this.listenEvent.emit({
247
+ name: key,
248
+ ...event,
249
+ });
250
+ });
251
+ }
252
+ }
253
+ }
254
+ /**
255
+ * @description Unsubscribes from all events of the dynamic component.
256
+ * @summary This method cleans up event subscriptions when the component is being destroyed.
257
+ * It iterates through all properties of the dynamic component instance and unsubscribes
258
+ * from any EventEmitter properties to prevent memory leaks and unexpected behavior after
259
+ * the component is destroyed.
260
+ *
261
+ * @mermaid
262
+ * sequenceDiagram
263
+ * participant C as ComponentRendererComponent
264
+ * participant D as Dynamic Component
265
+ *
266
+ * C->>C: unsubscribeEvents()
267
+ * C->>D: Get instance properties
268
+ * loop For each property
269
+ * C->>C: Check if property is EventEmitter
270
+ * alt is EventEmitter
271
+ * C->>D: Unsubscribe from event
272
+ * end
273
+ * end
274
+ *
275
+ * @private
276
+ * @return {void}
277
+ * @memberOf ComponentRendererComponent
278
+ */
279
+ unsubscribeEvents() {
280
+ if (this.component) {
281
+ const instance = this.component?.instance;
282
+ const componentKeys = Object.keys(instance);
283
+ for (const key of componentKeys) {
284
+ const value = instance[key];
285
+ if (value instanceof EventEmitter)
286
+ instance[key].unsubscribe();
287
+ }
288
+ }
289
+ }
290
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ComponentRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
291
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: ComponentRendererComponent, isStandalone: true, selector: "ngx-decaf-component-renderer", inputs: { tag: "tag", globals: "globals", parent: "parent" }, outputs: { listenEvent: "listenEvent" }, viewQueries: [{ propertyName: "vcr", first: true, predicate: ["componentViewContainer"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "inner", first: true, predicate: ["inner"], descendants: true, read: TemplateRef, static: true }], ngImport: i0, template: "<ng-template #componentViewContainer></ng-template>\n\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n</ng-template>\n\n\n", styles: [""], dependencies: [{ kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "parent"], outputs: ["listenEvent"] }, { kind: "ngmodule", type: ForAngularModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }] }); }
292
+ }
293
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ComponentRendererComponent, decorators: [{
294
+ type: Component,
295
+ args: [{ selector: 'ngx-decaf-component-renderer', imports: [ForAngularModule], standalone: true, template: "<ng-template #componentViewContainer></ng-template>\n\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n</ng-template>\n\n\n" }]
296
+ }], ctorParameters: () => [], propDecorators: { vcr: [{
297
+ type: ViewChild,
298
+ args: ['componentViewContainer', { static: true, read: ViewContainerRef }]
299
+ }], tag: [{
300
+ type: Input,
301
+ args: [{ required: true }]
302
+ }], globals: [{
303
+ type: Input
304
+ }], listenEvent: [{
305
+ type: Output
306
+ }], parent: [{
307
+ type: Input
308
+ }], inner: [{
309
+ type: ViewChild,
310
+ args: ['inner', { read: TemplateRef, static: true }]
311
+ }] } });
312
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9uZW50LXJlbmRlcmVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9jb21wb25lbnQtcmVuZGVyZXIvY29tcG9uZW50LXJlbmRlcmVyLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9jb21wb25lbnQtcmVuZGVyZXIvY29tcG9uZW50LXJlbmRlcmVyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCxTQUFTLEVBR1QsbUJBQW1CLEVBQ25CLFlBQVksRUFDWixNQUFNLEVBRU4sS0FBSyxFQUdMLE1BQU0sRUFDTixvQkFBb0IsRUFDcEIsV0FBVyxFQUVYLFNBQVMsRUFDVCxnQkFBZ0IsR0FDakIsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFFekUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLFNBQVMsRUFBRSxNQUFNLDRCQUE0QixDQUFDOzs7QUFHekU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0NHO0FBUUgsTUFBTSxPQUFPLDBCQUEwQjtJQWtHckM7Ozs7Ozs7T0FPRztJQUNIO1FBOUVBOzs7Ozs7Ozs7O1dBVUc7UUFFSCxZQUFPLEdBQTRCLEVBQUUsQ0FBQztRQUV0Qzs7Ozs7Ozs7O1dBU0c7UUFDSCxhQUFRLEdBQXdCLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1FBYTVEOzs7Ozs7Ozs7V0FTRztRQUVILGdCQUFXLEdBQ1QsSUFBSSxZQUFZLEVBQTBCLENBQUM7UUFlN0MsV0FBTSxHQUF5QixTQUFTLENBQUM7UUFldkMsSUFBSSxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDaEMsQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O09Bc0JH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QVcsRUFBRSxDQUFDO1lBQ2pFLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQzsrR0F6VVUsMEJBQTBCO21HQUExQiwwQkFBMEIseVJBV3NCLGdCQUFnQix1R0FvRi9DLFdBQVcsMkNDcEt6QywrbUJBc0JBLDBERCtDYSwwQkFBMEIsd0lBSDNCLGdCQUFnQjs7NEZBR2YsMEJBQTBCO2tCQVB0QyxTQUFTOytCQUNFLDhCQUE4QixXQUcvQixDQUFDLGdCQUFnQixDQUFDLGNBQ2YsSUFBSTt3REFjaEIsR0FBRztzQkFERixTQUFTO3VCQUFDLHdCQUF3QixFQUFFLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsZ0JBQWdCLEVBQUU7Z0JBZTdFLEdBQUc7c0JBREYsS0FBSzt1QkFBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUU7Z0JBZXpCLE9BQU87c0JBRE4sS0FBSztnQkFxQ04sV0FBVztzQkFEVixNQUFNO2dCQWlCUCxNQUFNO3NCQURMLEtBQUs7Z0JBS04sS0FBSztzQkFESixTQUFTO3VCQUFDLE9BQU8sRUFBRSxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENvbXBvbmVudCxcbiAgQ29tcG9uZW50TWlycm9yLFxuICBDb21wb25lbnRSZWYsXG4gIEVudmlyb25tZW50SW5qZWN0b3IsXG4gIEV2ZW50RW1pdHRlcixcbiAgaW5qZWN0LFxuICBJbmplY3RvcixcbiAgSW5wdXQsXG4gIE9uRGVzdHJveSxcbiAgT25Jbml0LFxuICBPdXRwdXQsXG4gIHJlZmxlY3RDb21wb25lbnRUeXBlLFxuICBUZW1wbGF0ZVJlZixcbiAgVHlwZSxcbiAgVmlld0NoaWxkLFxuICBWaWV3Q29udGFpbmVyUmVmLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE5neFJlbmRlcmluZ0VuZ2luZTIgfSBmcm9tICdzcmMvbGliL2VuZ2luZS9OZ3hSZW5kZXJpbmdFbmdpbmUyJztcbmltcG9ydCB7IEJhc2VDdXN0b21FdmVudCwgS2V5VmFsdWUsIE1vZGVsUmVuZGVyQ3VzdG9tRXZlbnQgfSBmcm9tICcuLi8uLi9lbmdpbmUnO1xuaW1wb3J0IHsgRm9yQW5ndWxhck1vZHVsZSwgZ2V0TG9nZ2VyIH0gZnJvbSAnc3JjL2xpYi9mb3ItYW5ndWxhci5tb2R1bGUnO1xuaW1wb3J0IHsgTG9nZ2VyIH0gZnJvbSAnQGRlY2FmLXRzL2xvZ2dpbmcnO1xuXG4vKipcbiAqIEBkZXNjcmlwdGlvbiBEeW5hbWljIGNvbXBvbmVudCByZW5kZXJlciBmb3IgRGVjYWYgQW5ndWxhciBhcHBsaWNhdGlvbnMuXG4gKiBAc3VtbWFyeSBUaGlzIGNvbXBvbmVudCBwcm92aWRlcyBhIGZsZXhpYmxlIHdheSB0byBkeW5hbWljYWxseSByZW5kZXIgQW5ndWxhciBjb21wb25lbnRzXG4gKiBhdCBydW50aW1lIGJhc2VkIG9uIGEgdGFnIG5hbWUuIEl0IGhhbmRsZXMgdGhlIGNyZWF0aW9uLCBwcm9wZXJ0eSBiaW5kaW5nLCBhbmQgZXZlbnRcbiAqIHN1YnNjcmlwdGlvbiBmb3IgZHluYW1pY2FsbHkgbG9hZGVkIGNvbXBvbmVudHMuIFRoaXMgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3JcbiAqIGJ1aWxkaW5nIGNvbmZpZ3VyYWJsZSBVSXMgd2hlcmUgY29tcG9uZW50cyBuZWVkIHRvIGJlIGRldGVybWluZWQgYXQgcnVudGltZS5cbiAqXG4gKiBAY29tcG9uZW50IHtDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudH1cbiAqIEBleGFtcGxlXG4gKiA8bmd4LWRlY2FmLWNvbXBvbmVudC1yZW5kZXJlclxuICogICBbdGFnXT1cInRhZ1wiXG4gKiAgIFtnbG9iYWxzXT1cImdsb2JhbHNcIlxuICogICAobGlzdGVuRXZlbnQpPVwibGlzdGVuRXZlbnQoJGV2ZW50KVwiPlxuICogPC9uZ3gtZGVjYWYtY29tcG9uZW50LXJlbmRlcmVyPlxuICpcbiAqIEBtZXJtYWlkXG4gKiBjbGFzc0RpYWdyYW1cbiAqICAgY2xhc3MgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnQge1xuICogICAgICtWaWV3Q29udGFpbmVyUmVmIHZjclxuICogICAgICtzdHJpbmcgdGFnXG4gKiAgICAgK1JlY29yZH5zdHJpbmcsIHVua25vd25+IGdsb2JhbHNcbiAqICAgICArRW52aXJvbm1lbnRJbmplY3RvciBpbmplY3RvclxuICogICAgICtDb21wb25lbnRSZWZ+dW5rbm93bn4gY29tcG9uZW50XG4gKiAgICAgK0V2ZW50RW1pdHRlcn5Nb2RlbFJlbmRlckN1c3RvbUV2ZW50fiBsaXN0ZW5FdmVudFxuICogICAgICtuZ09uSW5pdCgpXG4gKiAgICAgK25nT25EZXN0cm95KClcbiAqICAgICArbmdPbkNoYW5nZXMoY2hhbmdlcylcbiAqICAgICAtY3JlYXRlQ29tcG9uZW50KHRhZywgZ2xvYmFscylcbiAqICAgICAtc3Vic2NyaWJlRXZlbnRzKClcbiAqICAgICAtdW5zdWJzY3JpYmVFdmVudHMoKVxuICogICB9XG4gKiAgIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50IC0tfD4gT25Jbml0XG4gKiAgIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50IC0tfD4gT25DaGFuZ2VzXG4gKiAgIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50IC0tfD4gT25EZXN0cm95XG4gKlxuICogQGltcGxlbWVudHMge09uSW5pdH1cbiAqIEBpbXBsZW1lbnRzIHtPbkNoYW5nZXN9XG4gKiBAaW1wbGVtZW50cyB7T25EZXN0cm95fVxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICduZ3gtZGVjYWYtY29tcG9uZW50LXJlbmRlcmVyJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2NvbXBvbmVudC1yZW5kZXJlci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL2NvbXBvbmVudC1yZW5kZXJlci5jb21wb25lbnQuc2NzcyddLFxuICBpbXBvcnRzOiBbRm9yQW5ndWxhck1vZHVsZV0sXG4gIHN0YW5kYWxvbmU6IHRydWUsXG59KVxuZXhwb3J0IGNsYXNzIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gIGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFJlZmVyZW5jZSB0byB0aGUgY29udGFpbmVyIHdoZXJlIHRoZSBkeW5hbWljIGNvbXBvbmVudCB3aWxsIGJlIHJlbmRlcmVkLlxuICAgKiBAc3VtbWFyeSBUaGlzIFZpZXdDb250YWluZXJSZWYgcHJvdmlkZXMgdGhlIGNvbnRhaW5lciB3aGVyZSB0aGUgZHluYW1pY2FsbHkgY3JlYXRlZFxuICAgKiBjb21wb25lbnQgd2lsbCBiZSBpbnNlcnRlZCBpbnRvIHRoZSBET00uIEl0J3MgbWFya2VkIGFzIHN0YXRpYyB0byBlbnN1cmUgaXQncyBhdmFpbGFibGVcbiAgICogZHVyaW5nIHRoZSBuZ09uSW5pdCBsaWZlY3ljbGUgaG9vayB3aGVuIHRoZSBjb21wb25lbnQgaXMgY3JlYXRlZC5cbiAgICpcbiAgICogQHR5cGUge1ZpZXdDb250YWluZXJSZWZ9XG4gICAqIEBtZW1iZXJPZiBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKi9cbiAgQFZpZXdDaGlsZCgnY29tcG9uZW50Vmlld0NvbnRhaW5lcicsIHsgc3RhdGljOiB0cnVlLCByZWFkOiBWaWV3Q29udGFpbmVyUmVmIH0pXG4gIHZjciE6IFZpZXdDb250YWluZXJSZWY7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBUaGUgdGFnIG5hbWUgb2YgdGhlIGNvbXBvbmVudCB0byBiZSBkeW5hbWljYWxseSByZW5kZXJlZC5cbiAgICogQHN1bW1hcnkgVGhpcyBpbnB1dCBwcm9wZXJ0eSBzcGVjaWZpZXMgd2hpY2ggY29tcG9uZW50IHNob3VsZCBiZSByZW5kZXJlZCBieSBwcm92aWRpbmdcbiAgICogaXRzIHJlZ2lzdGVyZWQgdGFnIG5hbWUuIFRoZSB0YWcgbXVzdCBjb3JyZXNwb25kIHRvIGEgY29tcG9uZW50IHRoYXQgaGFzIGJlZW4gcmVnaXN0ZXJlZFxuICAgKiB3aXRoIHRoZSBOZ3hSZW5kZXJpbmdFbmdpbmUyLiBUaGlzIGlzIGEgcmVxdWlyZWQgaW5wdXQgYXMgaXQgZGV0ZXJtaW5lcyB3aGljaCBjb21wb25lbnRcbiAgICogdG8gY3JlYXRlLlxuICAgKlxuICAgKiBAdHlwZSB7c3RyaW5nfVxuICAgKiBAcmVxdWlyZWRcbiAgICogQG1lbWJlck9mIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoeyByZXF1aXJlZDogdHJ1ZSB9KVxuICB0YWchOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBHbG9iYWwgcHJvcGVydGllcyB0byBwYXNzIHRvIHRoZSByZW5kZXJlZCBjb21wb25lbnQuXG4gICAqIEBzdW1tYXJ5IFRoaXMgaW5wdXQgcHJvcGVydHkgYWxsb3dzIHBhc3NpbmcgYSBzZXQgb2YgcHJvcGVydGllcyB0byB0aGUgZHluYW1pY2FsbHlcbiAgICogcmVuZGVyZWQgY29tcG9uZW50LiBUaGVzZSBwcm9wZXJ0aWVzIHdpbGwgYmUgbWFwcGVkIHRvIHRoZSBjb21wb25lbnQncyBpbnB1dHMgaWYgdGhleVxuICAgKiBtYXRjaC4gUHJvcGVydGllcyB0aGF0IGRvbid0IG1hdGNoIGFueSBpbnB1dCBvbiB0aGUgdGFyZ2V0IGNvbXBvbmVudCB3aWxsIGJlIGZpbHRlcmVkIG91dFxuICAgKiB3aXRoIGEgd2FybmluZy5cbiAgICpcbiAgICogQHR5cGUge1JlY29yZDxzdHJpbmcsIHVua25vd24+fVxuICAgKiBAZGVmYXVsdCB7fVxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIEBJbnB1dCgpXG4gIGdsb2JhbHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBJbmplY3RvciB1c2VkIGZvciBkZXBlbmRlbmN5IGluamVjdGlvbiBpbiB0aGUgZHluYW1pYyBjb21wb25lbnQuXG4gICAqIEBzdW1tYXJ5IFRoaXMgaW5qZWN0b3IgaXMgdXNlZCB3aGVuIGNyZWF0aW5nIHRoZSBkeW5hbWljIGNvbXBvbmVudCB0byBwcm92aWRlIGl0IHdpdGhcbiAgICogYWNjZXNzIHRvIHRoZSBhcHBsaWNhdGlvbidzIGRlcGVuZGVuY3kgaW5qZWN0aW9uIHN5c3RlbS4gSXQgZW5zdXJlcyB0aGF0IHRoZSBkeW5hbWljYWxseVxuICAgKiBjcmVhdGVkIGNvbXBvbmVudCBjYW4gYWNjZXNzIHRoZSBzYW1lIHNlcnZpY2VzIGFuZCBkZXBlbmRlbmNpZXMgYXMgc3RhdGljYWxseSBjcmVhdGVkXG4gICAqIGNvbXBvbmVudHMuXG4gICAqXG4gICAqIEB0eXBlIHtFbnZpcm9ubWVudEluamVjdG9yfVxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIGluamVjdG9yOiBFbnZpcm9ubWVudEluamVjdG9yID0gaW5qZWN0KEVudmlyb25tZW50SW5qZWN0b3IpO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUmVmZXJlbmNlIHRvIHRoZSBkeW5hbWljYWxseSBjcmVhdGVkIGNvbXBvbmVudC5cbiAgICogQHN1bW1hcnkgVGhpcyBwcm9wZXJ0eSBob2xkcyBhIHJlZmVyZW5jZSB0byB0aGUgQ29tcG9uZW50UmVmIG9mIHRoZSBkeW5hbWljYWxseSBjcmVhdGVkXG4gICAqIGNvbXBvbmVudC4gSXQncyB1c2VkIHRvIGludGVyYWN0IHdpdGggdGhlIGNvbXBvbmVudCBpbnN0YW5jZSwgc3Vic2NyaWJlIHRvIGl0cyBldmVudHMsXG4gICAqIGFuZCBwcm9wZXJseSBkZXN0cm95IGl0IHdoZW4gdGhlIHJlbmRlcmVyIGlzIGRlc3Ryb3llZC5cbiAgICpcbiAgICogQHR5cGUge0NvbXBvbmVudFJlZjx1bmtub3duPn1cbiAgICogQG1lbWJlck9mIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqL1xuICBjb21wb25lbnQhOiBDb21wb25lbnRSZWY8dW5rbm93bj47XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBFdmVudCBlbWl0dGVyIGZvciBldmVudHMgZnJvbSB0aGUgcmVuZGVyZWQgY29tcG9uZW50LlxuICAgKiBAc3VtbWFyeSBUaGlzIG91dHB1dCBwcm9wZXJ0eSBlbWl0cyBldmVudHMgdGhhdCBvcmlnaW5hdGUgZnJvbSB0aGUgZHluYW1pY2FsbHkgcmVuZGVyZWRcbiAgICogY29tcG9uZW50LiBJdCBhbGxvd3MgdGhlIHBhcmVudCBjb21wb25lbnQgdG8gbGlzdGVuIGZvciBhbmQgcmVzcG9uZCB0byBldmVudHMgZnJvbSB0aGVcbiAgICogZHluYW1pYyBjb21wb25lbnQsIGNyZWF0aW5nIGEgY29tbXVuaWNhdGlvbiBjaGFubmVsIGJldHdlZW4gdGhlIHBhcmVudCBhbmQgdGhlIGR5bmFtaWNhbGx5XG4gICAqIHJlbmRlcmVkIGNoaWxkLlxuICAgKlxuICAgKiBAdHlwZSB7RXZlbnRFbWl0dGVyPE1vZGVsUmVuZGVyQ3VzdG9tRXZlbnQ+fVxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIEBPdXRwdXQoKVxuICBsaXN0ZW5FdmVudDogRXZlbnRFbWl0dGVyPE1vZGVsUmVuZGVyQ3VzdG9tRXZlbnQ+ID1cbiAgICBuZXcgRXZlbnRFbWl0dGVyPE1vZGVsUmVuZGVyQ3VzdG9tRXZlbnQ+KCk7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBMb2dnZXIgaW5zdGFuY2UgZm9yIHRoZSBjb21wb25lbnQuXG4gICAqIEBzdW1tYXJ5IFRoaXMgcHJvcGVydHkgaG9sZHMgYSBMb2dnZXIgaW5zdGFuY2Ugc3BlY2lmaWMgdG8gdGhpcyBjb21wb25lbnQuXG4gICAqIEl0J3MgdXNlZCB0byBsb2cgaW5mb3JtYXRpb24sIHdhcm5pbmdzLCBhbmQgZXJyb3JzIHJlbGF0ZWQgdG8gdGhlIGNvbXBvbmVudCdzXG4gICAqIG9wZXJhdGlvbnMsIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIGRlYnVnZ2luZyBhbmQgbW9uaXRvcmluZyB0aGUgZHluYW1pY1xuICAgKiBjb21wb25lbnQgcmVuZGVyaW5nIHByb2Nlc3MuXG4gICAqXG4gICAqIEB0eXBlIHtMb2dnZXJ9XG4gICAqIEBtZW1iZXJPZiBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKi9cbiAgbG9nZ2VyITogTG9nZ2VyO1xuXG4gIEBJbnB1dCgpXG4gIHBhcmVudDogdW5kZWZpbmVkIHwgS2V5VmFsdWUgPSB1bmRlZmluZWQ7XG5cblxuICBAVmlld0NoaWxkKCdpbm5lcicsIHsgcmVhZDogVGVtcGxhdGVSZWYsIHN0YXRpYzogdHJ1ZSB9KVxuICBpbm5lcj86IFRlbXBsYXRlUmVmPHVua25vd24+O1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudC5cbiAgICogQHN1bW1hcnkgSW5pdGlhbGl6ZXMgYSBuZXcgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnQuIFRoaXMgY29tcG9uZW50IGRvZXNuJ3QgcmVxdWlyZVxuICAgKiBhbnkgZGVwZW5kZW5jaWVzIHRvIGJlIGluamVjdGVkIGluIGl0cyBjb25zdHJ1Y3RvciBhcyBpdCB1c2VzIHRoZSBpbmplY3QgZnVuY3Rpb24gdG9cbiAgICogb2J0YWluIHRoZSBFbnZpcm9ubWVudEluamVjdG9yLlxuICAgKlxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMubG9nZ2VyID0gZ2V0TG9nZ2VyKHRoaXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBJbml0aWFsaXplcyB0aGUgY29tcG9uZW50IGFmdGVyIEFuZ3VsYXIgZmlyc3QgZGlzcGxheXMgdGhlIGRhdGEtYm91bmQgcHJvcGVydGllcy5cbiAgICogQHN1bW1hcnkgU2V0cyB1cCB0aGUgY29tcG9uZW50IGJ5IGNyZWF0aW5nIHRoZSBkeW5hbWljIGNvbXBvbmVudCBzcGVjaWZpZWQgYnkgdGhlIHRhZyBpbnB1dC5cbiAgICogVGhpcyBtZXRob2QgaXMgY2FsbGVkIG9uY2Ugd2hlbiB0aGUgY29tcG9uZW50IGlzIGluaXRpYWxpemVkIGFuZCB0cmlnZ2VycyB0aGUgZHluYW1pY1xuICAgKiBjb21wb25lbnQgY3JlYXRpb24gcHJvY2VzcyB3aXRoIHRoZSBwcm92aWRlZCB0YWcgbmFtZSBhbmQgZ2xvYmFsIHByb3BlcnRpZXMuXG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEEgYXMgQW5ndWxhciBMaWZlY3ljbGVcbiAgICogICBwYXJ0aWNpcGFudCBDIGFzIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgUiBhcyBOZ3hSZW5kZXJpbmdFbmdpbmUyXG4gICAqXG4gICAqICAgQS0+PkM6IG5nT25Jbml0KClcbiAgICogICBDLT4+QzogY3JlYXRlQ29tcG9uZW50KHRhZywgZ2xvYmFscylcbiAgICogICBDLT4+UjogY29tcG9uZW50cyh0YWcpXG4gICAqICAgUi0tPj5DOiBSZXR1cm4gY29tcG9uZW50IGNvbnN0cnVjdG9yXG4gICAqICAgQy0+PkM6IFByb2Nlc3MgY29tcG9uZW50IGlucHV0c1xuICAgKiAgIEMtPj5DOiBDcmVhdGUgY29tcG9uZW50IGluc3RhbmNlXG4gICAqICAgQy0+PkM6IHN1YnNjcmliZUV2ZW50cygpXG4gICAqXG4gICAqIEByZXR1cm4ge3ZvaWR9XG4gICAqIEBtZW1iZXJPZiBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKi9cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnBhcmVudCkge1xuICAgICAgdGhpcy5jcmVhdGVDb21wb25lbnQodGhpcy50YWcsIHRoaXMuZ2xvYmFscyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY3JlYXRlUGFyZW50Q29tcG9uZW50KCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDbGVhbnMgdXAgcmVzb3VyY2VzIHdoZW4gdGhlIGNvbXBvbmVudCBpcyBkZXN0cm95ZWQuXG4gICAqIEBzdW1tYXJ5IFBlcmZvcm1zIGNsZWFudXAgb3BlcmF0aW9ucyB3aGVuIHRoZSBjb21wb25lbnQgaXMgYmVpbmcgZGVzdHJveWVkIGJ5IEFuZ3VsYXIuXG4gICAqIFRoaXMgaW5jbHVkZXMgdW5zdWJzY3JpYmluZyBmcm9tIGFsbCBldmVudCBlbWl0dGVycyBvZiB0aGUgZHluYW1pYyBjb21wb25lbnQgYW5kXG4gICAqIGRlc3Ryb3lpbmcgdGhlIHJlbmRlcmluZyBlbmdpbmUgaW5zdGFuY2UgdG8gcHJldmVudCBtZW1vcnkgbGVha3MuXG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEEgYXMgQW5ndWxhciBMaWZlY3ljbGVcbiAgICogICBwYXJ0aWNpcGFudCBDIGFzIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgUiBhcyBOZ3hSZW5kZXJpbmdFbmdpbmUyXG4gICAqXG4gICAqICAgQS0+PkM6IG5nT25EZXN0cm95KClcbiAgICogICBhbHQgY29tcG9uZW50IGV4aXN0c1xuICAgKiAgICAgQy0+PkM6IHVuc3Vic2NyaWJlRXZlbnRzKClcbiAgICogICAgIEMtPj5SOiBkZXN0cm95KClcbiAgICogICBlbmRcbiAgICpcbiAgICogQHJldHVybiB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiBjbGVhbnVwIGlzIGNvbXBsZXRlXG4gICAqIEBtZW1iZXJPZiBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKi9cbiAgYXN5bmMgbmdPbkRlc3Ryb3koKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuY29tcG9uZW50KSB7XG4gICAgICB0aGlzLnVuc3Vic2NyaWJlRXZlbnRzKCk7XG4gICAgICBOZ3hSZW5kZXJpbmdFbmdpbmUyLmRlc3Ryb3koKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENyZWF0ZXMgYW5kIHJlbmRlcnMgYSBkeW5hbWljIGNvbXBvbmVudC5cbiAgICogQHN1bW1hcnkgVGhpcyBtZXRob2QgaGFuZGxlcyB0aGUgY3JlYXRpb24gb2YgYSBkeW5hbWljIGNvbXBvbmVudCBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgdGFnLlxuICAgKiBJdCByZXRyaWV2ZXMgdGhlIGNvbXBvbmVudCBjb25zdHJ1Y3RvciBmcm9tIHRoZSByZW5kZXJpbmcgZW5naW5lLCBwcm9jZXNzZXMgaXRzIGlucHV0cyxcbiAgICogZmlsdGVycyBvdXQgdW5tYXBwZWQgcHJvcGVydGllcywgY3JlYXRlcyB0aGUgY29tcG9uZW50IGluc3RhbmNlLCBhbmQgc2V0cyB1cCBldmVudCBzdWJzY3JpcHRpb25zLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdGFnIC0gVGhlIHRhZyBuYW1lIG9mIHRoZSBjb21wb25lbnQgdG8gY3JlYXRlXG4gICAqIEBwYXJhbSB7S2V5VmFsdWV9IGdsb2JhbHMgLSBHbG9iYWwgcHJvcGVydGllcyB0byBwYXNzIHRvIHRoZSBjb21wb25lbnRcbiAgICogQHJldHVybiB7dm9pZH1cbiAgICpcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgQyBhcyBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKiAgIHBhcnRpY2lwYW50IFIgYXMgTmd4UmVuZGVyaW5nRW5naW5lMlxuICAgKiAgIHBhcnRpY2lwYW50IFYgYXMgVmlld0NvbnRhaW5lclJlZlxuICAgKlxuICAgKiAgIEMtPj5SOiBjb21wb25lbnRzKHRhZylcbiAgICogICBSLS0+PkM6IFJldHVybiBjb21wb25lbnQgY29uc3RydWN0b3JcbiAgICogICBDLT4+QzogcmVmbGVjdENvbXBvbmVudFR5cGUoY29tcG9uZW50KVxuICAgKiAgIEMtPj5DOiBQcm9jZXNzIGlucHV0IHByb3BlcnRpZXNcbiAgICogICBDLT4+QzogRmlsdGVyIHVubWFwcGVkIHByb3BlcnRpZXNcbiAgICogICBDLT4+VjogY2xlYXIoKVxuICAgKiAgIEMtPj5SOiBjcmVhdGVDb21wb25lbnQoY29tcG9uZW50LCBwcm9wcywgbWV0YWRhdGEsIHZjciwgaW5qZWN0b3IsIFtdKVxuICAgKiAgIFItLT4+QzogUmV0dXJuIGNvbXBvbmVudCByZWZlcmVuY2VcbiAgICogICBDLT4+Qzogc3Vic2NyaWJlRXZlbnRzKClcbiAgICpcbiAgICogQHByaXZhdGVcbiAgICogQG1lbWJlck9mIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqL1xuICBwcml2YXRlIGNyZWF0ZUNvbXBvbmVudCh0YWc6IHN0cmluZywgZ2xvYmFsczogS2V5VmFsdWUgPSB7fSk6IHZvaWQge1xuICAgIGNvbnN0IGNvbXBvbmVudCA9IE5neFJlbmRlcmluZ0VuZ2luZTIuY29tcG9uZW50cyh0YWcpXG4gICAgICA/LmNvbnN0cnVjdG9yIGFzIFR5cGU8dW5rbm93bj47XG4gICAgY29uc3QgbWV0YWRhdGEgPSByZWZsZWN0Q29tcG9uZW50VHlwZShjb21wb25lbnQpO1xuICAgIGNvbnN0IGNvbXBvbmVudElucHV0cyA9IChtZXRhZGF0YSBhcyBDb21wb25lbnRNaXJyb3I8dW5rbm93bj4pLmlucHV0cztcbiAgICBjb25zdCBwcm9wcyA9IGdsb2JhbHM/LlsnaXRlbSddO1xuICAgIGRlbGV0ZSBwcm9wc1sndGFnJ107XG4gICAgY29uc3QgaW5wdXRLZXlzID0gT2JqZWN0LmtleXMocHJvcHMpO1xuICAgIGNvbnN0IHVubWFwcGVkS2V5cyA9IFtdO1xuXG4gICAgZm9yIChjb25zdCBpbnB1dCBvZiBpbnB1dEtleXMpIHtcbiAgICAgIGlmICghaW5wdXRLZXlzLmxlbmd0aCkgYnJlYWs7XG4gICAgICBjb25zdCBwcm9wID0gY29tcG9uZW50SW5wdXRzLmZpbmQoXG4gICAgICAgIChpdGVtOiB7IHByb3BOYW1lOiBzdHJpbmcgfSkgPT4gaXRlbS5wcm9wTmFtZSA9PT0gaW5wdXQsXG4gICAgICApO1xuICAgICAgaWYgKCFwcm9wKSB7XG4gICAgICAgIGRlbGV0ZSBwcm9wc1tpbnB1dF07XG4gICAgICAgIHVubWFwcGVkS2V5cy5wdXNoKGlucHV0KTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHVubWFwcGVkS2V5cy5sZW5ndGgpXG4gICAgICB0aGlzLmxvZ2dlci5pbmZvKGBVbm1hcHBlZCBpbnB1dCBwcm9wZXJ0aWVzIGZvciBjb21wb25lbnQgJHt0YWd9OiAke3VubWFwcGVkS2V5cy5qb2luKCcsICcpfWApO1xuXG4gICAgdGhpcy52Y3IuY2xlYXIoKTtcbiAgICB0aGlzLmNvbXBvbmVudCA9IE5neFJlbmRlcmluZ0VuZ2luZTIuY3JlYXRlQ29tcG9uZW50KFxuICAgICAgY29tcG9uZW50LFxuICAgICAgcHJvcHMsXG4gICAgICBtZXRhZGF0YSBhcyBDb21wb25lbnRNaXJyb3I8dW5rbm93bj4sXG4gICAgICB0aGlzLnZjcixcbiAgICAgIHRoaXMuaW5qZWN0b3IgYXMgSW5qZWN0b3IsXG4gICAgICBbXSxcbiAgICApO1xuICAgIHRoaXMuc3Vic2NyaWJlRXZlbnRzKCk7XG4gIH1cblxuICBjcmVhdGVQYXJlbnRDb21wb25lbnQoKSB7XG4gICAgY29uc3QgeyBjb21wb25lbnQsIGlucHV0cyB9ID0gdGhpcy5wYXJlbnQgYXMgS2V5VmFsdWU7XG4gICAgY29uc3QgbWV0YWRhdGEgPSByZWZsZWN0Q29tcG9uZW50VHlwZShjb21wb25lbnQpIGFzIENvbXBvbmVudE1pcnJvcjx1bmtub3duPjtcbiAgICBjb25zdCB0ZW1wbGF0ZSA9IHRoaXMudmNyLmNyZWF0ZUVtYmVkZGVkVmlldyh0aGlzLmlubmVyIGFzIFRlbXBsYXRlUmVmPHVua25vd24+LCB0aGlzLmluamVjdG9yKS5yb290Tm9kZXM7XG4gICAgdGhpcy5jb21wb25lbnQgPSBOZ3hSZW5kZXJpbmdFbmdpbmUyLmNyZWF0ZUNvbXBvbmVudChcbiAgICAgIGNvbXBvbmVudCxcbiAgICAgIGlucHV0cyxcbiAgICAgIG1ldGFkYXRhLFxuICAgICAgdGhpcy52Y3IsXG4gICAgICB0aGlzLmluamVjdG9yLFxuICAgICAgdGVtcGxhdGUsXG4gICAgKTtcbiAgICB0aGlzLnN1YnNjcmliZUV2ZW50cygpO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBTdWJzY3JpYmVzIHRvIGV2ZW50cyBlbWl0dGVkIGJ5IHRoZSBkeW5hbWljIGNvbXBvbmVudC5cbiAgICogQHN1bW1hcnkgVGhpcyBtZXRob2Qgc2V0cyB1cCBzdWJzY3JpcHRpb25zIHRvIGFsbCBFdmVudEVtaXR0ZXIgcHJvcGVydGllcyBvZiB0aGVcbiAgICogZHluYW1pY2FsbHkgY3JlYXRlZCBjb21wb25lbnQuIFdoZW4gYW4gZXZlbnQgaXMgZW1pdHRlZCBieSB0aGUgZHluYW1pYyBjb21wb25lbnQsXG4gICAqIGl0IGlzIGNhcHR1cmVkIGFuZCByZS1lbWl0dGVkIHRocm91Z2ggdGhlIGxpc3RlbkV2ZW50IG91dHB1dCBwcm9wZXJ0eSB3aXRoIGFkZGl0aW9uYWxcbiAgICogbWV0YWRhdGEgYWJvdXQgdGhlIGV2ZW50IHNvdXJjZS5cbiAgICpcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgQyBhcyBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKiAgIHBhcnRpY2lwYW50IEQgYXMgRHluYW1pYyBDb21wb25lbnRcbiAgICogICBwYXJ0aWNpcGFudCBQIGFzIFBhcmVudCBDb21wb25lbnRcbiAgICpcbiAgICogICBDLT4+Qzogc3Vic2NyaWJlRXZlbnRzKClcbiAgICogICBDLT4+RDogR2V0IGluc3RhbmNlIHByb3BlcnRpZXNcbiAgICogICBsb29wIEZvciBlYWNoIHByb3BlcnR5XG4gICAqICAgICBDLT4+QzogQ2hlY2sgaWYgcHJvcGVydHkgaXMgRXZlbnRFbWl0dGVyXG4gICAqICAgICBhbHQgaXMgRXZlbnRFbWl0dGVyXG4gICAqICAgICAgIEMtPj5EOiBTdWJzY3JpYmUgdG8gZXZlbnRcbiAgICogICAgICAgRC0tPj5DOiBFdmVudCBlbWl0dGVkXG4gICAqICAgICAgIEMtPj5QOiBSZS1lbWl0IGV2ZW50IHdpdGggbWV0YWRhdGFcbiAgICogICAgIGVuZFxuICAgKiAgIGVuZFxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcmV0dXJuIHt2b2lkfVxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIHByaXZhdGUgc3Vic2NyaWJlRXZlbnRzKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmNvbXBvbmVudCkge1xuICAgICAgY29uc3QgaW5zdGFuY2UgPSB0aGlzLmNvbXBvbmVudD8uaW5zdGFuY2UgYXMgS2V5VmFsdWU7XG4gICAgICBjb25zdCBjb21wb25lbnRLZXlzID0gT2JqZWN0LmtleXMoaW5zdGFuY2UpO1xuICAgICAgZm9yIChjb25zdCBrZXkgb2YgY29tcG9uZW50S2V5cykge1xuICAgICAgICBjb25zdCB2YWx1ZSA9IGluc3RhbmNlW2tleV07XG4gICAgICAgIGlmICh2YWx1ZSBpbnN0YW5jZW9mIEV2ZW50RW1pdHRlcilcbiAgICAgICAgICAoaW5zdGFuY2UgYXMgS2V5VmFsdWUpW2tleV0uc3Vic2NyaWJlKFxuICAgICAgICAgICAgKGV2ZW50OiBQYXJ0aWFsPEJhc2VDdXN0b21FdmVudD4pID0+IHtcbiAgICAgICAgICAgICAgdGhpcy5saXN0ZW5FdmVudC5lbWl0KHtcbiAgICAgICAgICAgICAgICBuYW1lOiBrZXksXG4gICAgICAgICAgICAgICAgLi4uZXZlbnQsXG4gICAgICAgICAgICAgIH0gYXMgTW9kZWxSZW5kZXJDdXN0b21FdmVudCk7XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBVbnN1YnNjcmliZXMgZnJvbSBhbGwgZXZlbnRzIG9mIHRoZSBkeW5hbWljIGNvbXBvbmVudC5cbiAgICogQHN1bW1hcnkgVGhpcyBtZXRob2QgY2xlYW5zIHVwIGV2ZW50IHN1YnNjcmlwdGlvbnMgd2hlbiB0aGUgY29tcG9uZW50IGlzIGJlaW5nIGRlc3Ryb3llZC5cbiAgICogSXQgaXRlcmF0ZXMgdGhyb3VnaCBhbGwgcHJvcGVydGllcyBvZiB0aGUgZHluYW1pYyBjb21wb25lbnQgaW5zdGFuY2UgYW5kIHVuc3Vic2NyaWJlc1xuICAgKiBmcm9tIGFueSBFdmVudEVtaXR0ZXIgcHJvcGVydGllcyB0byBwcmV2ZW50IG1lbW9yeSBsZWFrcyBhbmQgdW5leHBlY3RlZCBiZWhhdmlvciBhZnRlclxuICAgKiB0aGUgY29tcG9uZW50IGlzIGRlc3Ryb3llZC5cbiAgICpcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgQyBhcyBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKiAgIHBhcnRpY2lwYW50IEQgYXMgRHluYW1pYyBDb21wb25lbnRcbiAgICpcbiAgICogICBDLT4+QzogdW5zdWJzY3JpYmVFdmVudHMoKVxuICAgKiAgIEMtPj5EOiBHZXQgaW5zdGFuY2UgcHJvcGVydGllc1xuICAgKiAgIGxvb3AgRm9yIGVhY2ggcHJvcGVydHlcbiAgICogICAgIEMtPj5DOiBDaGVjayBpZiBwcm9wZXJ0eSBpcyBFdmVudEVtaXR0ZXJcbiAgICogICAgIGFsdCBpcyBFdmVudEVtaXR0ZXJcbiAgICogICAgICAgQy0+PkQ6IFVuc3Vic2NyaWJlIGZyb20gZXZlbnRcbiAgICogICAgIGVuZFxuICAgKiAgIGVuZFxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcmV0dXJuIHt2b2lkfVxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIHByaXZhdGUgdW5zdWJzY3JpYmVFdmVudHMoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuY29tcG9uZW50KSB7XG4gICAgICBjb25zdCBpbnN0YW5jZSA9IHRoaXMuY29tcG9uZW50Py5pbnN0YW5jZSBhcyBLZXlWYWx1ZTtcbiAgICAgIGNvbnN0IGNvbXBvbmVudEtleXMgPSBPYmplY3Qua2V5cyhpbnN0YW5jZSk7XG4gICAgICBmb3IgKGNvbnN0IGtleSBvZiBjb21wb25lbnRLZXlzKSB7XG4gICAgICAgIGNvbnN0IHZhbHVlID0gaW5zdGFuY2Vba2V5XTtcbiAgICAgICAgaWYgKHZhbHVlIGluc3RhbmNlb2YgRXZlbnRFbWl0dGVyKSBpbnN0YW5jZVtrZXldLnVuc3Vic2NyaWJlKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG59XG4iLCI8bmctdGVtcGxhdGUgI2NvbXBvbmVudFZpZXdDb250YWluZXI+PC9uZy10ZW1wbGF0ZT5cblxuPG5nLXRlbXBsYXRlICNpbm5lcj5cbiAgQGlmKHBhcmVudD8uY2hpbGRyZW4/Lmxlbmd0aCkge1xuICAgIEBmb3IoY2hpbGQgb2YgcGFyZW50LmNoaWxkcmVuOyB0cmFjayBjaGlsZCkge1xuICAgICAgICBAaWYoIWNoaWxkLmNoaWxkcmVuPy5sZW5ndGgpIHtcbiAgICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICAgICAgKm5nQ29tcG9uZW50T3V0bGV0PVwiXG4gICAgICAgICAgICAgICAgICBjaGlsZC5jb21wb25lbnQ7XG4gICAgICAgICAgICAgICAgICBpbmplY3RvcjogY2hpbGQuaW5qZWN0b3I7XG4gICAgICAgICAgICAgICAgICBpbnB1dHM6IGNoaWxkLmlucHV0cztcbiAgICAgICAgICAgICAgICAgIGNvbnRlbnQ6Y2hpbGQuY29udGVudDtcbiAgICAgICAgICAgICAgICBcIlxuICAgICAgICAgIC8+XG4gICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgIDxuZ3gtZGVjYWYtY29tcG9uZW50LXJlbmRlcmVyIFtwYXJlbnRdPVwiY2hpbGRcIj4gPC9uZ3gtZGVjYWYtY29tcG9uZW50LXJlbmRlcmVyPlxuICAgICAgICB9XG4gICAgIH1cbiAgfVxuPC9uZy10ZW1wbGF0ZT5cblxuXG4iXX0=