@decaf-ts/for-angular 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +659 -21
- package/README.md +37 -242
- package/dist/lib/README.md +92 -0
- package/dist/lib/assets/i18n/en.json +131 -0
- package/dist/lib/assets/images/angular-logo.svg +45 -0
- package/dist/lib/assets/images/decaf-logo-black.svg +22 -0
- package/dist/lib/assets/images/decaf-logo-lw.svg +50 -0
- package/dist/lib/assets/images/decaf-logo-white.svg +22 -0
- package/dist/lib/assets/images/decaf-logo.svg +54 -0
- package/dist/lib/components/component-renderer/component-renderer.component.d.ts +267 -0
- package/dist/lib/components/crud-field/crud-field.component.d.ts +447 -0
- package/dist/lib/components/crud-form/crud-form.component.d.ts +102 -0
- package/dist/lib/components/model-renderer/model-renderer.component.d.ts +97 -0
- package/dist/lib/engine/DynamicModule.d.ts +17 -0
- package/dist/{for-angular → lib}/engine/NgxCrudFormField.d.ts +37 -30
- package/dist/lib/engine/NgxFormService.d.ts +167 -0
- package/dist/lib/engine/NgxRenderingEngine.d.ts +128 -0
- package/dist/lib/engine/NgxRenderingEngine2.d.ts +251 -0
- package/dist/lib/engine/ValidatorFactory.d.ts +15 -0
- package/dist/lib/engine/constants.d.ts +151 -0
- package/dist/lib/engine/decorators.d.ts +25 -0
- package/dist/lib/engine/index.d.ts +15 -0
- package/dist/lib/engine/types.d.ts +293 -0
- package/dist/lib/esm2022/components/component-renderer/component-renderer.component.mjs +309 -0
- package/dist/lib/esm2022/components/crud-field/crud-field.component.mjs +288 -0
- package/dist/lib/esm2022/components/crud-form/constants.mjs +14 -0
- package/dist/lib/esm2022/components/crud-form/crud-form.component.mjs +140 -0
- package/dist/lib/esm2022/components/crud-form/types.mjs +2 -0
- package/dist/lib/esm2022/components/model-renderer/model-renderer.component.mjs +137 -0
- package/dist/lib/esm2022/engine/DynamicModule.mjs +18 -0
- package/dist/lib/esm2022/engine/NgxCrudFormField.mjs +117 -0
- package/dist/lib/esm2022/engine/NgxFormService.mjs +315 -0
- package/dist/lib/esm2022/engine/NgxRenderingEngine.mjs +194 -0
- package/dist/lib/esm2022/engine/NgxRenderingEngine2.mjs +333 -0
- package/dist/lib/esm2022/engine/ValidatorFactory.mjs +102 -0
- package/dist/lib/esm2022/engine/constants.mjs +160 -0
- package/dist/lib/esm2022/engine/decorators.mjs +38 -0
- package/dist/lib/esm2022/engine/index.mjs +16 -0
- package/dist/lib/esm2022/engine/types.mjs +2 -0
- package/dist/lib/esm2022/for-angular.module.mjs +118 -0
- package/dist/lib/esm2022/interfaces.mjs +2 -0
- package/dist/lib/esm2022/public-apis.mjs +13 -0
- package/dist/lib/fesm2022/decaf-ts-for-angular.mjs +2138 -0
- package/dist/lib/fesm2022/decaf-ts-for-angular.mjs.map +1 -0
- package/dist/lib/for-angular.module.d.ts +44 -0
- package/dist/lib/interfaces.d.ts +28 -0
- package/dist/lib/public-apis.d.ts +12 -0
- package/package.json +74 -25
- package/dist/for-angular/README.md +0 -297
- package/dist/for-angular/assets/i18n/en.json +0 -21
- package/dist/for-angular/components/decaf-crud-field/decaf-crud-field.component.d.ts +0 -22
- package/dist/for-angular/components/decaf-crud-form/decaf-crud-form.component.d.ts +0 -28
- package/dist/for-angular/components/decaf-model-renderer/decaf-model-renderer.component.d.ts +0 -20
- package/dist/for-angular/directives/decaf-field.directive.d.ts +0 -8
- package/dist/for-angular/engine/DynamicModule.d.ts +0 -2
- package/dist/for-angular/engine/NgxFormService.d.ts +0 -119
- package/dist/for-angular/engine/NgxRenderingEngine.d.ts +0 -17
- package/dist/for-angular/engine/ValidatorFactory.d.ts +0 -4
- package/dist/for-angular/engine/constants.d.ts +0 -10
- package/dist/for-angular/engine/decorators.d.ts +0 -1
- package/dist/for-angular/engine/index.d.ts +0 -5
- package/dist/for-angular/engine/types.d.ts +0 -32
- package/dist/for-angular/esm2022/components/decaf-crud-field/decaf-crud-field.component.mjs +0 -66
- package/dist/for-angular/esm2022/components/decaf-crud-form/constants.mjs +0 -14
- package/dist/for-angular/esm2022/components/decaf-crud-form/decaf-crud-form.component.mjs +0 -84
- package/dist/for-angular/esm2022/components/decaf-crud-form/types.mjs +0 -2
- package/dist/for-angular/esm2022/components/decaf-model-renderer/decaf-model-renderer.component.mjs +0 -46
- package/dist/for-angular/esm2022/directives/decaf-field.directive.mjs +0 -23
- package/dist/for-angular/esm2022/engine/DynamicModule.mjs +0 -3
- package/dist/for-angular/esm2022/engine/NgxCrudFormField.mjs +0 -118
- package/dist/for-angular/esm2022/engine/NgxFormService.mjs +0 -232
- package/dist/for-angular/esm2022/engine/NgxRenderingEngine.mjs +0 -35
- package/dist/for-angular/esm2022/engine/ValidatorFactory.mjs +0 -48
- package/dist/for-angular/esm2022/engine/constants.mjs +0 -12
- package/dist/for-angular/esm2022/engine/decorators.mjs +0 -17
- package/dist/for-angular/esm2022/engine/index.mjs +0 -6
- package/dist/for-angular/esm2022/engine/types.mjs +0 -2
- package/dist/for-angular/esm2022/interfaces.mjs +0 -2
- package/dist/for-angular/esm2022/public-apis.mjs +0 -5
- package/dist/for-angular/fesm2022/decaf-ts-for-angular.mjs +0 -675
- package/dist/for-angular/fesm2022/decaf-ts-for-angular.mjs.map +0 -1
- package/dist/for-angular/interfaces.d.ts +0 -8
- package/dist/for-angular/public-apis.d.ts +0 -4
- /package/dist/{for-angular/components/decaf-crud-form → lib/components/crud-form}/constants.d.ts +0 -0
- /package/dist/{for-angular/components/decaf-crud-form → lib/components/crud-form}/types.d.ts +0 -0
- /package/dist/{for-angular → lib}/esm2022/decaf-ts-for-angular.mjs +0 -0
- /package/dist/{for-angular → lib}/index.d.ts +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { IonCheckbox, IonInput, IonSelect, IonTextarea } from '@ionic/angular';
|
|
2
|
+
import { TextFieldTypes } from '@ionic/core';
|
|
3
|
+
import { Injector, Type } from '@angular/core';
|
|
4
|
+
import { FormControl, FormGroup } from '@angular/forms';
|
|
5
|
+
import { FieldProperties } from '@decaf-ts/ui-decorators';
|
|
6
|
+
export type KeyValue = Record<string, any>;
|
|
7
|
+
/**
|
|
8
|
+
* @description Element size options for UI components
|
|
9
|
+
* @summary Defines the possible size values that can be applied to UI elements.
|
|
10
|
+
* These sizes control the dimensions and layout behavior of components.
|
|
11
|
+
* @typedef {('small'|'medium'|'large'|'xlarge'|'2xlarge'|'auto'|'expand'|'block')} ElementSizes
|
|
12
|
+
* @memberOf module:engine
|
|
13
|
+
*/
|
|
14
|
+
export type ElementSizes = 'small' | 'medium' | 'large' | 'xlarge' | '2xlarge' | 'auto' | 'expand' | 'block';
|
|
15
|
+
/**
|
|
16
|
+
* @description Basic position options for UI elements
|
|
17
|
+
* @summary Defines the possible position values that can be applied to UI elements.
|
|
18
|
+
* These positions control the alignment and placement of components.
|
|
19
|
+
* @typedef {('left'|'center'|'right'|'top'|'bottom')} ElementPositions
|
|
20
|
+
* @memberOf module:engine
|
|
21
|
+
*/
|
|
22
|
+
export type ElementPositions = 'left' | 'center' | 'right' | 'top' | 'bottom';
|
|
23
|
+
/**
|
|
24
|
+
* @description Extended position options for flex layouts
|
|
25
|
+
* @summary Extends the basic ElementPositions with additional flex-specific position values.
|
|
26
|
+
* These positions are used for controlling alignment and distribution in flex containers.
|
|
27
|
+
* @typedef {(ElementPositions|'stretch'|'middle'|'around'|'between')} FlexPositions
|
|
28
|
+
* @memberOf module:engine
|
|
29
|
+
*/
|
|
30
|
+
export type FlexPositions = ElementPositions | 'stretch' | 'middle' | 'around' | 'between';
|
|
31
|
+
/**
|
|
32
|
+
* @description Update mode options for form fields
|
|
33
|
+
* @summary Defines when form field values should be updated in the model.
|
|
34
|
+
* - 'change': Update on every change event
|
|
35
|
+
* - 'blur': Update when the field loses focus
|
|
36
|
+
* - 'submit': Update only when the form is submitted
|
|
37
|
+
* @typedef {('change'|'blur'|'submit')} FieldUpdateMode
|
|
38
|
+
* @memberOf module:engine
|
|
39
|
+
*/
|
|
40
|
+
export type FieldUpdateMode = 'change' | 'blur' | 'submit';
|
|
41
|
+
/**
|
|
42
|
+
* @description Metadata structure for Angular components
|
|
43
|
+
* @summary Defines the structure of metadata for Angular components, including
|
|
44
|
+
* change detection strategy, selector, standalone status, imports, template, and styles.
|
|
45
|
+
* This is used for reflection and dynamic component creation.
|
|
46
|
+
* @interface ComponentMetadata
|
|
47
|
+
* @property {number} changeDetection - The change detection strategy number
|
|
48
|
+
* @property {string} selector - The CSS selector for the component
|
|
49
|
+
* @property {boolean} standalone - Whether the component is standalone
|
|
50
|
+
* @property imports - Array of imported modules/components
|
|
51
|
+
* @property {string} template - The HTML template for the component
|
|
52
|
+
* @property {string[]} styles - Array of CSS styles for the component
|
|
53
|
+
* @memberOf module:engine
|
|
54
|
+
*/
|
|
55
|
+
export interface ComponentMetadata {
|
|
56
|
+
changeDetection: number;
|
|
57
|
+
selector: string;
|
|
58
|
+
standalone: boolean;
|
|
59
|
+
imports: (new (...args: unknown[]) => unknown)[];
|
|
60
|
+
template: string;
|
|
61
|
+
styles: string[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* @description Output structure from the Angular rendering engine
|
|
65
|
+
* @summary Defines the structure of the output produced by the NgxRenderingEngine
|
|
66
|
+
* when rendering a component. Contains the component type, inputs, injector,
|
|
67
|
+
* content nodes, and child components.
|
|
68
|
+
* @typedef {Object} AngularDynamicOutput
|
|
69
|
+
* @property {Type<unknown>} component - The Angular component type
|
|
70
|
+
* @property {string} [rendererId] - Optional unique ID for the rendered component
|
|
71
|
+
* @property {Record<string, unknown>} [inputs] - Optional input properties for the component
|
|
72
|
+
* @property {Injector} [injector] - Optional Angular injector for dependency injection
|
|
73
|
+
* @property {Node[][]} [content] - Optional content nodes for projection
|
|
74
|
+
* @property {AngularDynamicOutput[]} [children] - Optional child components
|
|
75
|
+
* @property {Type<unknown>} [instance] - Optional component instance
|
|
76
|
+
* @property {FormGroup} [formGroup] - Optional component FormGroup
|
|
77
|
+
* @property {FormControl} [formControl] - Optional component FormControl
|
|
78
|
+
* @memberOf module:engine
|
|
79
|
+
*/
|
|
80
|
+
export interface AngularDynamicOutput {
|
|
81
|
+
component: Type<unknown>;
|
|
82
|
+
rendererId?: string;
|
|
83
|
+
inputs?: Record<string, unknown>;
|
|
84
|
+
injector?: Injector;
|
|
85
|
+
content?: Node[][];
|
|
86
|
+
children?: AngularDynamicOutput[];
|
|
87
|
+
instance?: Type<unknown>;
|
|
88
|
+
formGroup?: FormGroup;
|
|
89
|
+
formControl?: FormControl;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* @description Interface for models that can be rendered
|
|
93
|
+
* @summary Defines the basic structure for models that can be rendered by the engine.
|
|
94
|
+
* Contains an optional rendererId that uniquely identifies the rendered instance.
|
|
95
|
+
* @interface RenderedModel
|
|
96
|
+
* @property {string} [rendererId] - Optional unique ID for the rendered model instance
|
|
97
|
+
* @memberOf module:engine
|
|
98
|
+
*/
|
|
99
|
+
export interface RenderedModel {
|
|
100
|
+
rendererId?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* @description Possible input types for form fields
|
|
104
|
+
* @summary Defines the possible input types that can be used in form fields.
|
|
105
|
+
* Includes standard HTML input types like checkbox, radio, and select,
|
|
106
|
+
* as well as Ionic's TextFieldTypes and textarea.
|
|
107
|
+
* @typedef {('checkbox'|'radio'|'select'|TextFieldTypes|'textarea')} PossibleInputTypes
|
|
108
|
+
* @memberOf module:engine
|
|
109
|
+
*/
|
|
110
|
+
export type PossibleInputTypes = 'checkbox' | 'radio' | 'select' | TextFieldTypes | 'textarea';
|
|
111
|
+
/**
|
|
112
|
+
* @description Field definition for Angular components
|
|
113
|
+
* @summary A comprehensive type that combines properties from various Ionic components
|
|
114
|
+
* to define the structure of a field in an Angular form. It omits certain properties
|
|
115
|
+
* from IonInput, picks specific properties from IonSelect, IonTextarea, and IonCheckbox,
|
|
116
|
+
* and adds custom properties like type and className.
|
|
117
|
+
* @typedef {Object} AngularFieldDefinition
|
|
118
|
+
* @property {PossibleInputTypes} type - The type of input field
|
|
119
|
+
* @property {string|string[]} className - CSS class name(s) for the field
|
|
120
|
+
* @property {string} [cancelText] - Text for the cancel button (from IonSelect)
|
|
121
|
+
* @property {string} [interface] - Interface style for select (from IonSelect)
|
|
122
|
+
* @property {string} [selectedText] - Text for selected option (from IonSelect)
|
|
123
|
+
* @property {Object} [interfaceOptions] - Options for the interface (from IonSelect)
|
|
124
|
+
* @property {number} [rows] - Number of rows for textarea (from IonTextarea)
|
|
125
|
+
* @property {number} [cols] - Number of columns for textarea (from IonTextarea)
|
|
126
|
+
* @property {string} [alignment] - Alignment of checkbox (from IonCheckbox)
|
|
127
|
+
* @property {string} [justify] - Justification of checkbox (from IonCheckbox)
|
|
128
|
+
* @property {boolean} [checked] - Whether checkbox is checked (from IonCheckbox)
|
|
129
|
+
* @memberOf module:engine
|
|
130
|
+
*/
|
|
131
|
+
export type AngularFieldDefinition = Omit<IonInput, 'ionInput' | 'ionFocus' | 'ionChange' | 'ionBlur' | 'getInputElement' | 'setFocus' | 'label' | 'el' | 'z' | 'type'> & Pick<IonSelect, 'cancelText' | 'interface' | 'selectedText' | 'interfaceOptions'> & Pick<IonTextarea, 'rows' | 'cols'> & Pick<IonCheckbox, 'alignment' | 'justify' | 'checked'> & {
|
|
132
|
+
type: PossibleInputTypes;
|
|
133
|
+
className: string | string[];
|
|
134
|
+
} & Record<string, unknown>;
|
|
135
|
+
/**
|
|
136
|
+
* @description String or boolean representation of a boolean value
|
|
137
|
+
* @summary Represents a value that can be either a boolean or a string representation of a boolean.
|
|
138
|
+
* This is useful for handling attribute values that can be specified as either strings or booleans.
|
|
139
|
+
* @typedef {('true'|'false'|boolean)} StringOrBoolean
|
|
140
|
+
* @memberOf module:engine
|
|
141
|
+
*/
|
|
142
|
+
export type StringOrBoolean = 'true' | 'false' | boolean;
|
|
143
|
+
/**
|
|
144
|
+
* @description Option type for select inputs
|
|
145
|
+
* @summary Extends the InputOption interface with a selected property to indicate
|
|
146
|
+
* whether the option is selected by default.
|
|
147
|
+
* @memberOf module:engine
|
|
148
|
+
*/
|
|
149
|
+
export type SelectOption = InputOption & {
|
|
150
|
+
selected?: boolean;
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* @description Option type for radio inputs
|
|
154
|
+
* @summary Extends the InputOption interface with a checked property to indicate
|
|
155
|
+
* whether the option is checked by default.
|
|
156
|
+
* @memberOf module:engine
|
|
157
|
+
*/
|
|
158
|
+
export type RadioOption = InputOption & {
|
|
159
|
+
checked?: boolean;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* @description Option type for checkbox inputs
|
|
163
|
+
* @summary Alias for RadioOption, as checkbox options have the same structure as radio options.
|
|
164
|
+
* @typedef {RadioOption} CheckboxOption
|
|
165
|
+
* @memberOf module:engine
|
|
166
|
+
*/
|
|
167
|
+
export type CheckboxOption = RadioOption;
|
|
168
|
+
/**
|
|
169
|
+
* @description Base option type for input components
|
|
170
|
+
* @summary Defines the common structure for options used in select, radio, and checkbox inputs.
|
|
171
|
+
* Contains properties for the display text, value, disabled state, CSS class, and icon.
|
|
172
|
+
* @interface InputOption
|
|
173
|
+
* @property {string} text - The display text for the option
|
|
174
|
+
* @property {string|number} value - The value associated with the option
|
|
175
|
+
* @property {StringOrBoolean} [disabled] - Whether the option is disabled
|
|
176
|
+
* @property {string} [className] - CSS class name for styling the option
|
|
177
|
+
* @property {string} [icon] - Icon to display with the option
|
|
178
|
+
* @memberOf module:engine
|
|
179
|
+
*/
|
|
180
|
+
export interface InputOption {
|
|
181
|
+
text: string;
|
|
182
|
+
value: string | number;
|
|
183
|
+
disabled?: StringOrBoolean;
|
|
184
|
+
className?: string;
|
|
185
|
+
icon?: string;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* @description Target options for HTML forms
|
|
189
|
+
* @summary Defines the possible target values for HTML forms, including standard targets
|
|
190
|
+
* like '_blank', '_self', '_parent', and '_top', as well as custom string values.
|
|
191
|
+
* @typedef {('_blank'|'_self'|'_parent'|'_top'|string)} HTMLFormTarget
|
|
192
|
+
* @memberOf module:engine
|
|
193
|
+
*/
|
|
194
|
+
export type HTMLFormTarget = '_blank' | '_self' | '_parent' | '_top' | string;
|
|
195
|
+
/**
|
|
196
|
+
* @description Interface for list component refresh events
|
|
197
|
+
* @summary Defines the structure of a refresh event for list components.
|
|
198
|
+
* Contains an array of key-value pairs representing the new data for the list.
|
|
199
|
+
* @interface IListComponentRefreshEvent
|
|
200
|
+
* @property {KeyValue[]} data - Array of key-value pairs representing the new data
|
|
201
|
+
* @memberOf module:engine
|
|
202
|
+
*/
|
|
203
|
+
export interface IListComponentRefreshEvent {
|
|
204
|
+
data: KeyValue[];
|
|
205
|
+
}
|
|
206
|
+
export interface FormServiceControl {
|
|
207
|
+
control: FormGroup;
|
|
208
|
+
props: AngularFieldDefinition;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* @description Type for form service controls
|
|
212
|
+
* @summary Defines the structure of form controls managed by the form service.
|
|
213
|
+
* It's a nested record where the outer key is the form group name, the inner key
|
|
214
|
+
* is the control name, and the value contains the form group and field properties.
|
|
215
|
+
* @typedef {Record<string, Record<string, { control: FormGroup; props: AngularFieldDefinition }>>} FormServiceControls
|
|
216
|
+
* @memberOf module:engine
|
|
217
|
+
*/
|
|
218
|
+
export type FormServiceControls = Record<string, Record<string, FormServiceControl>>;
|
|
219
|
+
/**
|
|
220
|
+
* @description Interface for model render custom events
|
|
221
|
+
* @summary Defines the structure of custom events triggered during model rendering.
|
|
222
|
+
* Contains the event detail, component name, and event name.
|
|
223
|
+
* @interface ModelRenderCustomEvent
|
|
224
|
+
* @property {BaseCustomEvent} detail - The detailed event information
|
|
225
|
+
* @property {string} component - The component that triggered the event
|
|
226
|
+
* @property {string} name - The name of the event
|
|
227
|
+
* @memberOf module:engine
|
|
228
|
+
*/
|
|
229
|
+
export interface ModelRenderCustomEvent {
|
|
230
|
+
detail: BaseCustomEvent;
|
|
231
|
+
component: string;
|
|
232
|
+
name: string;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* @description Interface for list item custom events
|
|
236
|
+
* @summary Defines the structure of custom events triggered by list items.
|
|
237
|
+
* Extends BaseCustomEvent with additional properties for the action and primary key.
|
|
238
|
+
* @interface ListItemCustomEvent
|
|
239
|
+
* @property {string} action - The action performed on the list item
|
|
240
|
+
* @property {string} [pk] - Optional primary key of the affected item
|
|
241
|
+
* @property {any} data - The data associated with the event (inherited from BaseCustomEvent)
|
|
242
|
+
* @property {HTMLElement} [target] - The target element (inherited from BaseCustomEvent)
|
|
243
|
+
* @property {string} [name] - The name of the event (inherited from BaseCustomEvent)
|
|
244
|
+
* @property {string} component - The component that triggered the event (inherited from BaseCustomEvent)
|
|
245
|
+
* @memberOf module:engine
|
|
246
|
+
*/
|
|
247
|
+
export interface ListItemCustomEvent extends BaseCustomEvent {
|
|
248
|
+
action: string;
|
|
249
|
+
pk?: string;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* @description Base interface for custom events
|
|
253
|
+
* @summary Defines the base structure for custom events in the application.
|
|
254
|
+
* Contains properties for the event data, target element, name, and component.
|
|
255
|
+
* @interface BaseCustomEvent
|
|
256
|
+
* @property {any} data - The data associated with the event
|
|
257
|
+
* @property {HTMLElement} [target] - The target element that triggered the event
|
|
258
|
+
* @property {string} [name] - The name of the event
|
|
259
|
+
* @property {string} component - The component that triggered the event
|
|
260
|
+
* @memberOf module:engine
|
|
261
|
+
*/
|
|
262
|
+
export interface BaseCustomEvent {
|
|
263
|
+
data: any;
|
|
264
|
+
target?: HTMLElement;
|
|
265
|
+
name?: string;
|
|
266
|
+
component: string;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* @description Base interface for custom events
|
|
270
|
+
* @summary Defines the base structure for custom events in the application.
|
|
271
|
+
* Contains properties for the event data, target element, name, and component.
|
|
272
|
+
* @interface BaseCustomEvent
|
|
273
|
+
* @property {any} data - The data associated with the event
|
|
274
|
+
* @property {HTMLElement} [target] - The target element that triggered the event
|
|
275
|
+
* @property {string} [name] - The name of the event
|
|
276
|
+
* @property {string} component - The component that triggered the event
|
|
277
|
+
* @memberOf module:engine
|
|
278
|
+
*/
|
|
279
|
+
export type CrudFormEvent = BaseCustomEvent & {
|
|
280
|
+
handlers?: Record<string, any>;
|
|
281
|
+
};
|
|
282
|
+
export interface ComponentInput extends FieldProperties {
|
|
283
|
+
updateMode?: FieldUpdateMode;
|
|
284
|
+
formGroup?: FormGroup;
|
|
285
|
+
formControl?: FormControl;
|
|
286
|
+
}
|
|
287
|
+
export interface ComponentConfig {
|
|
288
|
+
component: string;
|
|
289
|
+
inputs: ComponentInput;
|
|
290
|
+
injector: any;
|
|
291
|
+
children?: ComponentConfig[];
|
|
292
|
+
}
|
|
293
|
+
export type FormParentGroup = [FormGroup, string];
|
|
@@ -0,0 +1,309 @@
|
|
|
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
|
+
this.createParentComponent();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* @description Cleans up resources when the component is destroyed.
|
|
122
|
+
* @summary Performs cleanup operations when the component is being destroyed by Angular.
|
|
123
|
+
* This includes unsubscribing from all event emitters of the dynamic component and
|
|
124
|
+
* destroying the rendering engine instance to prevent memory leaks.
|
|
125
|
+
*
|
|
126
|
+
* @mermaid
|
|
127
|
+
* sequenceDiagram
|
|
128
|
+
* participant A as Angular Lifecycle
|
|
129
|
+
* participant C as ComponentRendererComponent
|
|
130
|
+
* participant R as NgxRenderingEngine2
|
|
131
|
+
*
|
|
132
|
+
* A->>C: ngOnDestroy()
|
|
133
|
+
* alt component exists
|
|
134
|
+
* C->>C: unsubscribeEvents()
|
|
135
|
+
* C->>R: destroy()
|
|
136
|
+
* end
|
|
137
|
+
*
|
|
138
|
+
* @return {Promise<void>} A promise that resolves when cleanup is complete
|
|
139
|
+
* @memberOf ComponentRendererComponent
|
|
140
|
+
*/
|
|
141
|
+
async ngOnDestroy() {
|
|
142
|
+
if (this.component) {
|
|
143
|
+
this.unsubscribeEvents();
|
|
144
|
+
NgxRenderingEngine2.destroy();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* @description Creates and renders a dynamic component.
|
|
149
|
+
* @summary This method handles the creation of a dynamic component based on the provided tag.
|
|
150
|
+
* It retrieves the component constructor from the rendering engine, processes its inputs,
|
|
151
|
+
* filters out unmapped properties, creates the component instance, and sets up event subscriptions.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} tag - The tag name of the component to create
|
|
154
|
+
* @param {KeyValue} globals - Global properties to pass to the component
|
|
155
|
+
* @return {void}
|
|
156
|
+
*
|
|
157
|
+
* @mermaid
|
|
158
|
+
* sequenceDiagram
|
|
159
|
+
* participant C as ComponentRendererComponent
|
|
160
|
+
* participant R as NgxRenderingEngine2
|
|
161
|
+
* participant V as ViewContainerRef
|
|
162
|
+
*
|
|
163
|
+
* C->>R: components(tag)
|
|
164
|
+
* R-->>C: Return component constructor
|
|
165
|
+
* C->>C: reflectComponentType(component)
|
|
166
|
+
* C->>C: Process input properties
|
|
167
|
+
* C->>C: Filter unmapped properties
|
|
168
|
+
* C->>V: clear()
|
|
169
|
+
* C->>R: createComponent(component, props, metadata, vcr, injector, [])
|
|
170
|
+
* R-->>C: Return component reference
|
|
171
|
+
* C->>C: subscribeEvents()
|
|
172
|
+
*
|
|
173
|
+
* @private
|
|
174
|
+
* @memberOf ComponentRendererComponent
|
|
175
|
+
*/
|
|
176
|
+
createComponent(tag, globals = {}) {
|
|
177
|
+
const component = NgxRenderingEngine2.components(tag)
|
|
178
|
+
?.constructor;
|
|
179
|
+
const metadata = reflectComponentType(component);
|
|
180
|
+
const componentInputs = metadata.inputs;
|
|
181
|
+
const props = globals?.['item'];
|
|
182
|
+
delete props['tag'];
|
|
183
|
+
const inputKeys = Object.keys(props);
|
|
184
|
+
const unmappedKeys = [];
|
|
185
|
+
for (const input of inputKeys) {
|
|
186
|
+
if (!inputKeys.length)
|
|
187
|
+
break;
|
|
188
|
+
const prop = componentInputs.find((item) => item.propName === input);
|
|
189
|
+
if (!prop) {
|
|
190
|
+
delete props[input];
|
|
191
|
+
unmappedKeys.push(input);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (unmappedKeys.length)
|
|
195
|
+
this.logger.info(`Unmapped input properties for component ${tag}: ${unmappedKeys.join(', ')}`);
|
|
196
|
+
this.vcr.clear();
|
|
197
|
+
this.component = NgxRenderingEngine2.createComponent(component, props, metadata, this.vcr, this.injector, []);
|
|
198
|
+
this.subscribeEvents();
|
|
199
|
+
}
|
|
200
|
+
createParentComponent() {
|
|
201
|
+
const { component, inputs } = this.parent;
|
|
202
|
+
const metadata = reflectComponentType(component);
|
|
203
|
+
const template = this.vcr.createEmbeddedView(this.inner, this.injector).rootNodes;
|
|
204
|
+
this.component = NgxRenderingEngine2.createComponent(component, inputs, metadata, this.vcr, this.injector, template);
|
|
205
|
+
this.subscribeEvents();
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* @description Subscribes to events emitted by the dynamic component.
|
|
209
|
+
* @summary This method sets up subscriptions to all EventEmitter properties of the
|
|
210
|
+
* dynamically created component. When an event is emitted by the dynamic component,
|
|
211
|
+
* it is captured and re-emitted through the listenEvent output property with additional
|
|
212
|
+
* metadata about the event source.
|
|
213
|
+
*
|
|
214
|
+
* @mermaid
|
|
215
|
+
* sequenceDiagram
|
|
216
|
+
* participant C as ComponentRendererComponent
|
|
217
|
+
* participant D as Dynamic Component
|
|
218
|
+
* participant P as Parent Component
|
|
219
|
+
*
|
|
220
|
+
* C->>C: subscribeEvents()
|
|
221
|
+
* C->>D: Get instance properties
|
|
222
|
+
* loop For each property
|
|
223
|
+
* C->>C: Check if property is EventEmitter
|
|
224
|
+
* alt is EventEmitter
|
|
225
|
+
* C->>D: Subscribe to event
|
|
226
|
+
* D-->>C: Event emitted
|
|
227
|
+
* C->>P: Re-emit event with metadata
|
|
228
|
+
* end
|
|
229
|
+
* end
|
|
230
|
+
*
|
|
231
|
+
* @private
|
|
232
|
+
* @return {void}
|
|
233
|
+
* @memberOf ComponentRendererComponent
|
|
234
|
+
*/
|
|
235
|
+
subscribeEvents() {
|
|
236
|
+
if (this.component) {
|
|
237
|
+
const instance = this.component?.instance;
|
|
238
|
+
const componentKeys = Object.keys(instance);
|
|
239
|
+
for (const key of componentKeys) {
|
|
240
|
+
const value = instance[key];
|
|
241
|
+
if (value instanceof EventEmitter)
|
|
242
|
+
instance[key].subscribe((event) => {
|
|
243
|
+
this.listenEvent.emit({
|
|
244
|
+
name: key,
|
|
245
|
+
...event,
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* @description Unsubscribes from all events of the dynamic component.
|
|
253
|
+
* @summary This method cleans up event subscriptions when the component is being destroyed.
|
|
254
|
+
* It iterates through all properties of the dynamic component instance and unsubscribes
|
|
255
|
+
* from any EventEmitter properties to prevent memory leaks and unexpected behavior after
|
|
256
|
+
* the component is destroyed.
|
|
257
|
+
*
|
|
258
|
+
* @mermaid
|
|
259
|
+
* sequenceDiagram
|
|
260
|
+
* participant C as ComponentRendererComponent
|
|
261
|
+
* participant D as Dynamic Component
|
|
262
|
+
*
|
|
263
|
+
* C->>C: unsubscribeEvents()
|
|
264
|
+
* C->>D: Get instance properties
|
|
265
|
+
* loop For each property
|
|
266
|
+
* C->>C: Check if property is EventEmitter
|
|
267
|
+
* alt is EventEmitter
|
|
268
|
+
* C->>D: Unsubscribe from event
|
|
269
|
+
* end
|
|
270
|
+
* end
|
|
271
|
+
*
|
|
272
|
+
* @private
|
|
273
|
+
* @return {void}
|
|
274
|
+
* @memberOf ComponentRendererComponent
|
|
275
|
+
*/
|
|
276
|
+
unsubscribeEvents() {
|
|
277
|
+
if (this.component) {
|
|
278
|
+
const instance = this.component?.instance;
|
|
279
|
+
const componentKeys = Object.keys(instance);
|
|
280
|
+
for (const key of componentKeys) {
|
|
281
|
+
const value = instance[key];
|
|
282
|
+
if (value instanceof EventEmitter)
|
|
283
|
+
instance[key].unsubscribe();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ComponentRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
288
|
+
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"] }] }); }
|
|
289
|
+
}
|
|
290
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ComponentRendererComponent, decorators: [{
|
|
291
|
+
type: Component,
|
|
292
|
+
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" }]
|
|
293
|
+
}], ctorParameters: () => [], propDecorators: { vcr: [{
|
|
294
|
+
type: ViewChild,
|
|
295
|
+
args: ['componentViewContainer', { static: true, read: ViewContainerRef }]
|
|
296
|
+
}], tag: [{
|
|
297
|
+
type: Input,
|
|
298
|
+
args: [{ required: true }]
|
|
299
|
+
}], globals: [{
|
|
300
|
+
type: Input
|
|
301
|
+
}], listenEvent: [{
|
|
302
|
+
type: Output
|
|
303
|
+
}], parent: [{
|
|
304
|
+
type: Input
|
|
305
|
+
}], inner: [{
|
|
306
|
+
type: ViewChild,
|
|
307
|
+
args: ['inner', { read: TemplateRef, static: true }]
|
|
308
|
+
}] } });
|
|
309
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9uZW50LXJlbmRlcmVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9jb21wb25lbnQtcmVuZGVyZXIvY29tcG9uZW50LXJlbmRlcmVyLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9jb21wb25lbnQtcmVuZGVyZXIvY29tcG9uZW50LXJlbmRlcmVyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCxTQUFTLEVBR1QsbUJBQW1CLEVBQ25CLFlBQVksRUFDWixNQUFNLEVBRU4sS0FBSyxFQUdMLE1BQU0sRUFDTixvQkFBb0IsRUFDcEIsV0FBVyxFQUVYLFNBQVMsRUFDVCxnQkFBZ0IsR0FDakIsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFFekUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLFNBQVMsRUFBRSxNQUFNLDRCQUE0QixDQUFDOzs7QUFHekU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0NHO0FBUUgsTUFBTSxPQUFPLDBCQUEwQjtJQWtHckM7Ozs7Ozs7T0FPRztJQUNIO1FBOUVBOzs7Ozs7Ozs7O1dBVUc7UUFFSCxZQUFPLEdBQTRCLEVBQUUsQ0FBQztRQUV0Qzs7Ozs7Ozs7O1dBU0c7UUFDSCxhQUFRLEdBQXdCLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1FBYTVEOzs7Ozs7Ozs7V0FTRztRQUVILGdCQUFXLEdBQ1QsSUFBSSxZQUFZLEVBQTBCLENBQUM7UUFlN0MsV0FBTSxHQUFRLFNBQVMsQ0FBQztRQWV0QixJQUFJLENBQUMsTUFBTSxHQUFHLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNoQyxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FzQkc7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R0F2VVUsMEJBQTBCO21HQUExQiwwQkFBMEIseVJBV3NCLGdCQUFnQix1R0FvRi9DLFdBQVcsMkNDcEt6QywrbUJBc0JBLDBERCtDYSwwQkFBMEIsd0lBSDNCLGdCQUFnQjs7NEZBR2YsMEJBQTBCO2tCQVB0QyxTQUFTOytCQUNFLDhCQUE4QixXQUcvQixDQUFDLGdCQUFnQixDQUFDLGNBQ2YsSUFBSTt3REFjaEIsR0FBRztzQkFERixTQUFTO3VCQUFDLHdCQUF3QixFQUFFLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsZ0JBQWdCLEVBQUU7Z0JBZTdFLEdBQUc7c0JBREYsS0FBSzt1QkFBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUU7Z0JBZXpCLE9BQU87c0JBRE4sS0FBSztnQkFxQ04sV0FBVztzQkFEVixNQUFNO2dCQWlCUCxNQUFNO3NCQURMLEtBQUs7Z0JBS04sS0FBSztzQkFESixTQUFTO3VCQUFDLE9BQU8sRUFBRSxFQUFFLElBQUksRUFBRSxXQUFXLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENvbXBvbmVudCxcbiAgQ29tcG9uZW50TWlycm9yLFxuICBDb21wb25lbnRSZWYsXG4gIEVudmlyb25tZW50SW5qZWN0b3IsXG4gIEV2ZW50RW1pdHRlcixcbiAgaW5qZWN0LFxuICBJbmplY3RvcixcbiAgSW5wdXQsXG4gIE9uRGVzdHJveSxcbiAgT25Jbml0LFxuICBPdXRwdXQsXG4gIHJlZmxlY3RDb21wb25lbnRUeXBlLFxuICBUZW1wbGF0ZVJlZixcbiAgVHlwZSxcbiAgVmlld0NoaWxkLFxuICBWaWV3Q29udGFpbmVyUmVmLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE5neFJlbmRlcmluZ0VuZ2luZTIgfSBmcm9tICdzcmMvbGliL2VuZ2luZS9OZ3hSZW5kZXJpbmdFbmdpbmUyJztcbmltcG9ydCB7IEJhc2VDdXN0b21FdmVudCwgS2V5VmFsdWUsIE1vZGVsUmVuZGVyQ3VzdG9tRXZlbnQgfSBmcm9tICcuLi8uLi9lbmdpbmUnO1xuaW1wb3J0IHsgRm9yQW5ndWxhck1vZHVsZSwgZ2V0TG9nZ2VyIH0gZnJvbSAnc3JjL2xpYi9mb3ItYW5ndWxhci5tb2R1bGUnO1xuaW1wb3J0IHsgTG9nZ2VyIH0gZnJvbSAnQGRlY2FmLXRzL2xvZ2dpbmcnO1xuXG4vKipcbiAqIEBkZXNjcmlwdGlvbiBEeW5hbWljIGNvbXBvbmVudCByZW5kZXJlciBmb3IgRGVjYWYgQW5ndWxhciBhcHBsaWNhdGlvbnMuXG4gKiBAc3VtbWFyeSBUaGlzIGNvbXBvbmVudCBwcm92aWRlcyBhIGZsZXhpYmxlIHdheSB0byBkeW5hbWljYWxseSByZW5kZXIgQW5ndWxhciBjb21wb25lbnRzXG4gKiBhdCBydW50aW1lIGJhc2VkIG9uIGEgdGFnIG5hbWUuIEl0IGhhbmRsZXMgdGhlIGNyZWF0aW9uLCBwcm9wZXJ0eSBiaW5kaW5nLCBhbmQgZXZlbnRcbiAqIHN1YnNjcmlwdGlvbiBmb3IgZHluYW1pY2FsbHkgbG9hZGVkIGNvbXBvbmVudHMuIFRoaXMgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3JcbiAqIGJ1aWxkaW5nIGNvbmZpZ3VyYWJsZSBVSXMgd2hlcmUgY29tcG9uZW50cyBuZWVkIHRvIGJlIGRldGVybWluZWQgYXQgcnVudGltZS5cbiAqXG4gKiBAY29tcG9uZW50IHtDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudH1cbiAqIEBleGFtcGxlXG4gKiA8bmd4LWRlY2FmLWNvbXBvbmVudC1yZW5kZXJlclxuICogICBbdGFnXT1cInRhZ1wiXG4gKiAgIFtnbG9iYWxzXT1cImdsb2JhbHNcIlxuICogICAobGlzdGVuRXZlbnQpPVwibGlzdGVuRXZlbnQoJGV2ZW50KVwiPlxuICogPC9uZ3gtZGVjYWYtY29tcG9uZW50LXJlbmRlcmVyPlxuICpcbiAqIEBtZXJtYWlkXG4gKiBjbGFzc0RpYWdyYW1cbiAqICAgY2xhc3MgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnQge1xuICogICAgICtWaWV3Q29udGFpbmVyUmVmIHZjclxuICogICAgICtzdHJpbmcgdGFnXG4gKiAgICAgK1JlY29yZH5zdHJpbmcsIHVua25vd25+IGdsb2JhbHNcbiAqICAgICArRW52aXJvbm1lbnRJbmplY3RvciBpbmplY3RvclxuICogICAgICtDb21wb25lbnRSZWZ+dW5rbm93bn4gY29tcG9uZW50XG4gKiAgICAgK0V2ZW50RW1pdHRlcn5Nb2RlbFJlbmRlckN1c3RvbUV2ZW50fiBsaXN0ZW5FdmVudFxuICogICAgICtuZ09uSW5pdCgpXG4gKiAgICAgK25nT25EZXN0cm95KClcbiAqICAgICArbmdPbkNoYW5nZXMoY2hhbmdlcylcbiAqICAgICAtY3JlYXRlQ29tcG9uZW50KHRhZywgZ2xvYmFscylcbiAqICAgICAtc3Vic2NyaWJlRXZlbnRzKClcbiAqICAgICAtdW5zdWJzY3JpYmVFdmVudHMoKVxuICogICB9XG4gKiAgIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50IC0tfD4gT25Jbml0XG4gKiAgIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50IC0tfD4gT25DaGFuZ2VzXG4gKiAgIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50IC0tfD4gT25EZXN0cm95XG4gKlxuICogQGltcGxlbWVudHMge09uSW5pdH1cbiAqIEBpbXBsZW1lbnRzIHtPbkNoYW5nZXN9XG4gKiBAaW1wbGVtZW50cyB7T25EZXN0cm95fVxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICduZ3gtZGVjYWYtY29tcG9uZW50LXJlbmRlcmVyJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2NvbXBvbmVudC1yZW5kZXJlci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL2NvbXBvbmVudC1yZW5kZXJlci5jb21wb25lbnQuc2NzcyddLFxuICBpbXBvcnRzOiBbRm9yQW5ndWxhck1vZHVsZV0sXG4gIHN0YW5kYWxvbmU6IHRydWUsXG59KVxuZXhwb3J0IGNsYXNzIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gIGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFJlZmVyZW5jZSB0byB0aGUgY29udGFpbmVyIHdoZXJlIHRoZSBkeW5hbWljIGNvbXBvbmVudCB3aWxsIGJlIHJlbmRlcmVkLlxuICAgKiBAc3VtbWFyeSBUaGlzIFZpZXdDb250YWluZXJSZWYgcHJvdmlkZXMgdGhlIGNvbnRhaW5lciB3aGVyZSB0aGUgZHluYW1pY2FsbHkgY3JlYXRlZFxuICAgKiBjb21wb25lbnQgd2lsbCBiZSBpbnNlcnRlZCBpbnRvIHRoZSBET00uIEl0J3MgbWFya2VkIGFzIHN0YXRpYyB0byBlbnN1cmUgaXQncyBhdmFpbGFibGVcbiAgICogZHVyaW5nIHRoZSBuZ09uSW5pdCBsaWZlY3ljbGUgaG9vayB3aGVuIHRoZSBjb21wb25lbnQgaXMgY3JlYXRlZC5cbiAgICpcbiAgICogQHR5cGUge1ZpZXdDb250YWluZXJSZWZ9XG4gICAqIEBtZW1iZXJPZiBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKi9cbiAgQFZpZXdDaGlsZCgnY29tcG9uZW50Vmlld0NvbnRhaW5lcicsIHsgc3RhdGljOiB0cnVlLCByZWFkOiBWaWV3Q29udGFpbmVyUmVmIH0pXG4gIHZjciE6IFZpZXdDb250YWluZXJSZWY7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBUaGUgdGFnIG5hbWUgb2YgdGhlIGNvbXBvbmVudCB0byBiZSBkeW5hbWljYWxseSByZW5kZXJlZC5cbiAgICogQHN1bW1hcnkgVGhpcyBpbnB1dCBwcm9wZXJ0eSBzcGVjaWZpZXMgd2hpY2ggY29tcG9uZW50IHNob3VsZCBiZSByZW5kZXJlZCBieSBwcm92aWRpbmdcbiAgICogaXRzIHJlZ2lzdGVyZWQgdGFnIG5hbWUuIFRoZSB0YWcgbXVzdCBjb3JyZXNwb25kIHRvIGEgY29tcG9uZW50IHRoYXQgaGFzIGJlZW4gcmVnaXN0ZXJlZFxuICAgKiB3aXRoIHRoZSBOZ3hSZW5kZXJpbmdFbmdpbmUyLiBUaGlzIGlzIGEgcmVxdWlyZWQgaW5wdXQgYXMgaXQgZGV0ZXJtaW5lcyB3aGljaCBjb21wb25lbnRcbiAgICogdG8gY3JlYXRlLlxuICAgKlxuICAgKiBAdHlwZSB7c3RyaW5nfVxuICAgKiBAcmVxdWlyZWRcbiAgICogQG1lbWJlck9mIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoeyByZXF1aXJlZDogdHJ1ZSB9KVxuICB0YWchOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBHbG9iYWwgcHJvcGVydGllcyB0byBwYXNzIHRvIHRoZSByZW5kZXJlZCBjb21wb25lbnQuXG4gICAqIEBzdW1tYXJ5IFRoaXMgaW5wdXQgcHJvcGVydHkgYWxsb3dzIHBhc3NpbmcgYSBzZXQgb2YgcHJvcGVydGllcyB0byB0aGUgZHluYW1pY2FsbHlcbiAgICogcmVuZGVyZWQgY29tcG9uZW50LiBUaGVzZSBwcm9wZXJ0aWVzIHdpbGwgYmUgbWFwcGVkIHRvIHRoZSBjb21wb25lbnQncyBpbnB1dHMgaWYgdGhleVxuICAgKiBtYXRjaC4gUHJvcGVydGllcyB0aGF0IGRvbid0IG1hdGNoIGFueSBpbnB1dCBvbiB0aGUgdGFyZ2V0IGNvbXBvbmVudCB3aWxsIGJlIGZpbHRlcmVkIG91dFxuICAgKiB3aXRoIGEgd2FybmluZy5cbiAgICpcbiAgICogQHR5cGUge1JlY29yZDxzdHJpbmcsIHVua25vd24+fVxuICAgKiBAZGVmYXVsdCB7fVxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIEBJbnB1dCgpXG4gIGdsb2JhbHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBJbmplY3RvciB1c2VkIGZvciBkZXBlbmRlbmN5IGluamVjdGlvbiBpbiB0aGUgZHluYW1pYyBjb21wb25lbnQuXG4gICAqIEBzdW1tYXJ5IFRoaXMgaW5qZWN0b3IgaXMgdXNlZCB3aGVuIGNyZWF0aW5nIHRoZSBkeW5hbWljIGNvbXBvbmVudCB0byBwcm92aWRlIGl0IHdpdGhcbiAgICogYWNjZXNzIHRvIHRoZSBhcHBsaWNhdGlvbidzIGRlcGVuZGVuY3kgaW5qZWN0aW9uIHN5c3RlbS4gSXQgZW5zdXJlcyB0aGF0IHRoZSBkeW5hbWljYWxseVxuICAgKiBjcmVhdGVkIGNvbXBvbmVudCBjYW4gYWNjZXNzIHRoZSBzYW1lIHNlcnZpY2VzIGFuZCBkZXBlbmRlbmNpZXMgYXMgc3RhdGljYWxseSBjcmVhdGVkXG4gICAqIGNvbXBvbmVudHMuXG4gICAqXG4gICAqIEB0eXBlIHtFbnZpcm9ubWVudEluamVjdG9yfVxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIGluamVjdG9yOiBFbnZpcm9ubWVudEluamVjdG9yID0gaW5qZWN0KEVudmlyb25tZW50SW5qZWN0b3IpO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUmVmZXJlbmNlIHRvIHRoZSBkeW5hbWljYWxseSBjcmVhdGVkIGNvbXBvbmVudC5cbiAgICogQHN1bW1hcnkgVGhpcyBwcm9wZXJ0eSBob2xkcyBhIHJlZmVyZW5jZSB0byB0aGUgQ29tcG9uZW50UmVmIG9mIHRoZSBkeW5hbWljYWxseSBjcmVhdGVkXG4gICAqIGNvbXBvbmVudC4gSXQncyB1c2VkIHRvIGludGVyYWN0IHdpdGggdGhlIGNvbXBvbmVudCBpbnN0YW5jZSwgc3Vic2NyaWJlIHRvIGl0cyBldmVudHMsXG4gICAqIGFuZCBwcm9wZXJseSBkZXN0cm95IGl0IHdoZW4gdGhlIHJlbmRlcmVyIGlzIGRlc3Ryb3llZC5cbiAgICpcbiAgICogQHR5cGUge0NvbXBvbmVudFJlZjx1bmtub3duPn1cbiAgICogQG1lbWJlck9mIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqL1xuICBjb21wb25lbnQhOiBDb21wb25lbnRSZWY8dW5rbm93bj47XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBFdmVudCBlbWl0dGVyIGZvciBldmVudHMgZnJvbSB0aGUgcmVuZGVyZWQgY29tcG9uZW50LlxuICAgKiBAc3VtbWFyeSBUaGlzIG91dHB1dCBwcm9wZXJ0eSBlbWl0cyBldmVudHMgdGhhdCBvcmlnaW5hdGUgZnJvbSB0aGUgZHluYW1pY2FsbHkgcmVuZGVyZWRcbiAgICogY29tcG9uZW50LiBJdCBhbGxvd3MgdGhlIHBhcmVudCBjb21wb25lbnQgdG8gbGlzdGVuIGZvciBhbmQgcmVzcG9uZCB0byBldmVudHMgZnJvbSB0aGVcbiAgICogZHluYW1pYyBjb21wb25lbnQsIGNyZWF0aW5nIGEgY29tbXVuaWNhdGlvbiBjaGFubmVsIGJldHdlZW4gdGhlIHBhcmVudCBhbmQgdGhlIGR5bmFtaWNhbGx5XG4gICAqIHJlbmRlcmVkIGNoaWxkLlxuICAgKlxuICAgKiBAdHlwZSB7RXZlbnRFbWl0dGVyPE1vZGVsUmVuZGVyQ3VzdG9tRXZlbnQ+fVxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIEBPdXRwdXQoKVxuICBsaXN0ZW5FdmVudDogRXZlbnRFbWl0dGVyPE1vZGVsUmVuZGVyQ3VzdG9tRXZlbnQ+ID1cbiAgICBuZXcgRXZlbnRFbWl0dGVyPE1vZGVsUmVuZGVyQ3VzdG9tRXZlbnQ+KCk7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBMb2dnZXIgaW5zdGFuY2UgZm9yIHRoZSBjb21wb25lbnQuXG4gICAqIEBzdW1tYXJ5IFRoaXMgcHJvcGVydHkgaG9sZHMgYSBMb2dnZXIgaW5zdGFuY2Ugc3BlY2lmaWMgdG8gdGhpcyBjb21wb25lbnQuXG4gICAqIEl0J3MgdXNlZCB0byBsb2cgaW5mb3JtYXRpb24sIHdhcm5pbmdzLCBhbmQgZXJyb3JzIHJlbGF0ZWQgdG8gdGhlIGNvbXBvbmVudCdzXG4gICAqIG9wZXJhdGlvbnMsIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIGRlYnVnZ2luZyBhbmQgbW9uaXRvcmluZyB0aGUgZHluYW1pY1xuICAgKiBjb21wb25lbnQgcmVuZGVyaW5nIHByb2Nlc3MuXG4gICAqXG4gICAqIEB0eXBlIHtMb2dnZXJ9XG4gICAqIEBtZW1iZXJPZiBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKi9cbiAgbG9nZ2VyITogTG9nZ2VyO1xuXG4gIEBJbnB1dCgpXG4gIHBhcmVudDogYW55ID0gdW5kZWZpbmVkO1xuXG5cbiAgQFZpZXdDaGlsZCgnaW5uZXInLCB7IHJlYWQ6IFRlbXBsYXRlUmVmLCBzdGF0aWM6IHRydWUgfSlcbiAgaW5uZXI/OiBUZW1wbGF0ZVJlZjxhbnk+O1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudC5cbiAgICogQHN1bW1hcnkgSW5pdGlhbGl6ZXMgYSBuZXcgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnQuIFRoaXMgY29tcG9uZW50IGRvZXNuJ3QgcmVxdWlyZVxuICAgKiBhbnkgZGVwZW5kZW5jaWVzIHRvIGJlIGluamVjdGVkIGluIGl0cyBjb25zdHJ1Y3RvciBhcyBpdCB1c2VzIHRoZSBpbmplY3QgZnVuY3Rpb24gdG9cbiAgICogb2J0YWluIHRoZSBFbnZpcm9ubWVudEluamVjdG9yLlxuICAgKlxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMubG9nZ2VyID0gZ2V0TG9nZ2VyKHRoaXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBJbml0aWFsaXplcyB0aGUgY29tcG9uZW50IGFmdGVyIEFuZ3VsYXIgZmlyc3QgZGlzcGxheXMgdGhlIGRhdGEtYm91bmQgcHJvcGVydGllcy5cbiAgICogQHN1bW1hcnkgU2V0cyB1cCB0aGUgY29tcG9uZW50IGJ5IGNyZWF0aW5nIHRoZSBkeW5hbWljIGNvbXBvbmVudCBzcGVjaWZpZWQgYnkgdGhlIHRhZyBpbnB1dC5cbiAgICogVGhpcyBtZXRob2QgaXMgY2FsbGVkIG9uY2Ugd2hlbiB0aGUgY29tcG9uZW50IGlzIGluaXRpYWxpemVkIGFuZCB0cmlnZ2VycyB0aGUgZHluYW1pY1xuICAgKiBjb21wb25lbnQgY3JlYXRpb24gcHJvY2VzcyB3aXRoIHRoZSBwcm92aWRlZCB0YWcgbmFtZSBhbmQgZ2xvYmFsIHByb3BlcnRpZXMuXG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEEgYXMgQW5ndWxhciBMaWZlY3ljbGVcbiAgICogICBwYXJ0aWNpcGFudCBDIGFzIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgUiBhcyBOZ3hSZW5kZXJpbmdFbmdpbmUyXG4gICAqXG4gICAqICAgQS0+PkM6IG5nT25Jbml0KClcbiAgICogICBDLT4+QzogY3JlYXRlQ29tcG9uZW50KHRhZywgZ2xvYmFscylcbiAgICogICBDLT4+UjogY29tcG9uZW50cyh0YWcpXG4gICAqICAgUi0tPj5DOiBSZXR1cm4gY29tcG9uZW50IGNvbnN0cnVjdG9yXG4gICAqICAgQy0+PkM6IFByb2Nlc3MgY29tcG9uZW50IGlucHV0c1xuICAgKiAgIEMtPj5DOiBDcmVhdGUgY29tcG9uZW50IGluc3RhbmNlXG4gICAqICAgQy0+PkM6IHN1YnNjcmliZUV2ZW50cygpXG4gICAqXG4gICAqIEByZXR1cm4ge3ZvaWR9XG4gICAqIEBtZW1iZXJPZiBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKi9cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLnBhcmVudClcbiAgICAgIHRoaXMuY3JlYXRlQ29tcG9uZW50KHRoaXMudGFnLCB0aGlzLmdsb2JhbHMpO1xuICAgIHRoaXMuY3JlYXRlUGFyZW50Q29tcG9uZW50KCk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENsZWFucyB1cCByZXNvdXJjZXMgd2hlbiB0aGUgY29tcG9uZW50IGlzIGRlc3Ryb3llZC5cbiAgICogQHN1bW1hcnkgUGVyZm9ybXMgY2xlYW51cCBvcGVyYXRpb25zIHdoZW4gdGhlIGNvbXBvbmVudCBpcyBiZWluZyBkZXN0cm95ZWQgYnkgQW5ndWxhci5cbiAgICogVGhpcyBpbmNsdWRlcyB1bnN1YnNjcmliaW5nIGZyb20gYWxsIGV2ZW50IGVtaXR0ZXJzIG9mIHRoZSBkeW5hbWljIGNvbXBvbmVudCBhbmRcbiAgICogZGVzdHJveWluZyB0aGUgcmVuZGVyaW5nIGVuZ2luZSBpbnN0YW5jZSB0byBwcmV2ZW50IG1lbW9yeSBsZWFrcy5cbiAgICpcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgQSBhcyBBbmd1bGFyIExpZmVjeWNsZVxuICAgKiAgIHBhcnRpY2lwYW50IEMgYXMgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICogICBwYXJ0aWNpcGFudCBSIGFzIE5neFJlbmRlcmluZ0VuZ2luZTJcbiAgICpcbiAgICogICBBLT4+QzogbmdPbkRlc3Ryb3koKVxuICAgKiAgIGFsdCBjb21wb25lbnQgZXhpc3RzXG4gICAqICAgICBDLT4+QzogdW5zdWJzY3JpYmVFdmVudHMoKVxuICAgKiAgICAgQy0+PlI6IGRlc3Ryb3koKVxuICAgKiAgIGVuZFxuICAgKlxuICAgKiBAcmV0dXJuIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIGNsZWFudXAgaXMgY29tcGxldGVcbiAgICogQG1lbWJlck9mIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqL1xuICBhc3luYyBuZ09uRGVzdHJveSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5jb21wb25lbnQpIHtcbiAgICAgIHRoaXMudW5zdWJzY3JpYmVFdmVudHMoKTtcbiAgICAgIE5neFJlbmRlcmluZ0VuZ2luZTIuZGVzdHJveSgpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ3JlYXRlcyBhbmQgcmVuZGVycyBhIGR5bmFtaWMgY29tcG9uZW50LlxuICAgKiBAc3VtbWFyeSBUaGlzIG1ldGhvZCBoYW5kbGVzIHRoZSBjcmVhdGlvbiBvZiBhIGR5bmFtaWMgY29tcG9uZW50IGJhc2VkIG9uIHRoZSBwcm92aWRlZCB0YWcuXG4gICAqIEl0IHJldHJpZXZlcyB0aGUgY29tcG9uZW50IGNvbnN0cnVjdG9yIGZyb20gdGhlIHJlbmRlcmluZyBlbmdpbmUsIHByb2Nlc3NlcyBpdHMgaW5wdXRzLFxuICAgKiBmaWx0ZXJzIG91dCB1bm1hcHBlZCBwcm9wZXJ0aWVzLCBjcmVhdGVzIHRoZSBjb21wb25lbnQgaW5zdGFuY2UsIGFuZCBzZXRzIHVwIGV2ZW50IHN1YnNjcmlwdGlvbnMuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0YWcgLSBUaGUgdGFnIG5hbWUgb2YgdGhlIGNvbXBvbmVudCB0byBjcmVhdGVcbiAgICogQHBhcmFtIHtLZXlWYWx1ZX0gZ2xvYmFscyAtIEdsb2JhbCBwcm9wZXJ0aWVzIHRvIHBhc3MgdG8gdGhlIGNvbXBvbmVudFxuICAgKiBAcmV0dXJuIHt2b2lkfVxuICAgKlxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBDIGFzIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgUiBhcyBOZ3hSZW5kZXJpbmdFbmdpbmUyXG4gICAqICAgcGFydGljaXBhbnQgViBhcyBWaWV3Q29udGFpbmVyUmVmXG4gICAqXG4gICAqICAgQy0+PlI6IGNvbXBvbmVudHModGFnKVxuICAgKiAgIFItLT4+QzogUmV0dXJuIGNvbXBvbmVudCBjb25zdHJ1Y3RvclxuICAgKiAgIEMtPj5DOiByZWZsZWN0Q29tcG9uZW50VHlwZShjb21wb25lbnQpXG4gICAqICAgQy0+PkM6IFByb2Nlc3MgaW5wdXQgcHJvcGVydGllc1xuICAgKiAgIEMtPj5DOiBGaWx0ZXIgdW5tYXBwZWQgcHJvcGVydGllc1xuICAgKiAgIEMtPj5WOiBjbGVhcigpXG4gICAqICAgQy0+PlI6IGNyZWF0ZUNvbXBvbmVudChjb21wb25lbnQsIHByb3BzLCBtZXRhZGF0YSwgdmNyLCBpbmplY3RvciwgW10pXG4gICAqICAgUi0tPj5DOiBSZXR1cm4gY29tcG9uZW50IHJlZmVyZW5jZVxuICAgKiAgIEMtPj5DOiBzdWJzY3JpYmVFdmVudHMoKVxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAbWVtYmVyT2YgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICovXG4gIHByaXZhdGUgY3JlYXRlQ29tcG9uZW50KHRhZzogc3RyaW5nLCBnbG9iYWxzOiBLZXlWYWx1ZSA9IHt9KTogdm9pZCB7XG4gICAgY29uc3QgY29tcG9uZW50ID0gTmd4UmVuZGVyaW5nRW5naW5lMi5jb21wb25lbnRzKHRhZylcbiAgICAgID8uY29uc3RydWN0b3IgYXMgVHlwZTx1bmtub3duPjtcbiAgICBjb25zdCBtZXRhZGF0YSA9IHJlZmxlY3RDb21wb25lbnRUeXBlKGNvbXBvbmVudCk7XG4gICAgY29uc3QgY29tcG9uZW50SW5wdXRzID0gKG1ldGFkYXRhIGFzIENvbXBvbmVudE1pcnJvcjx1bmtub3duPikuaW5wdXRzO1xuICAgIGNvbnN0IHByb3BzID0gZ2xvYmFscz8uWydpdGVtJ107XG4gICAgZGVsZXRlIHByb3BzWyd0YWcnXTtcbiAgICBjb25zdCBpbnB1dEtleXMgPSBPYmplY3Qua2V5cyhwcm9wcyk7XG4gICAgY29uc3QgdW5tYXBwZWRLZXlzID0gW107XG5cbiAgICBmb3IgKGNvbnN0IGlucHV0IG9mIGlucHV0S2V5cykge1xuICAgICAgaWYgKCFpbnB1dEtleXMubGVuZ3RoKSBicmVhaztcbiAgICAgIGNvbnN0IHByb3AgPSBjb21wb25lbnRJbnB1dHMuZmluZChcbiAgICAgICAgKGl0ZW06IHsgcHJvcE5hbWU6IHN0cmluZyB9KSA9PiBpdGVtLnByb3BOYW1lID09PSBpbnB1dCxcbiAgICAgICk7XG4gICAgICBpZiAoIXByb3ApIHtcbiAgICAgICAgZGVsZXRlIHByb3BzW2lucHV0XTtcbiAgICAgICAgdW5tYXBwZWRLZXlzLnB1c2goaW5wdXQpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAodW5tYXBwZWRLZXlzLmxlbmd0aClcbiAgICAgIHRoaXMubG9nZ2VyLmluZm8oYFVubWFwcGVkIGlucHV0IHByb3BlcnRpZXMgZm9yIGNvbXBvbmVudCAke3RhZ306ICR7dW5tYXBwZWRLZXlzLmpvaW4oJywgJyl9YCk7XG5cbiAgICB0aGlzLnZjci5jbGVhcigpO1xuICAgIHRoaXMuY29tcG9uZW50ID0gTmd4UmVuZGVyaW5nRW5naW5lMi5jcmVhdGVDb21wb25lbnQoXG4gICAgICBjb21wb25lbnQsXG4gICAgICBwcm9wcyxcbiAgICAgIG1ldGFkYXRhIGFzIENvbXBvbmVudE1pcnJvcjx1bmtub3duPixcbiAgICAgIHRoaXMudmNyLFxuICAgICAgdGhpcy5pbmplY3RvciBhcyBJbmplY3RvcixcbiAgICAgIFtdLFxuICAgICk7XG4gICAgdGhpcy5zdWJzY3JpYmVFdmVudHMoKTtcbiAgfVxuXG4gIGNyZWF0ZVBhcmVudENvbXBvbmVudCgpIHtcbiAgICBjb25zdCB7IGNvbXBvbmVudCwgaW5wdXRzIH0gPSB0aGlzLnBhcmVudDtcbiAgICBjb25zdCBtZXRhZGF0YSA9IHJlZmxlY3RDb21wb25lbnRUeXBlKGNvbXBvbmVudCkgYXMgQ29tcG9uZW50TWlycm9yPHVua25vd24+O1xuICAgIGNvbnN0IHRlbXBsYXRlID0gdGhpcy52Y3IuY3JlYXRlRW1iZWRkZWRWaWV3KHRoaXMuaW5uZXIgYXMgVGVtcGxhdGVSZWY8YW55PiwgdGhpcy5pbmplY3Rvcikucm9vdE5vZGVzO1xuICAgIHRoaXMuY29tcG9uZW50ID0gTmd4UmVuZGVyaW5nRW5naW5lMi5jcmVhdGVDb21wb25lbnQoXG4gICAgICBjb21wb25lbnQsXG4gICAgICBpbnB1dHMsXG4gICAgICBtZXRhZGF0YSxcbiAgICAgIHRoaXMudmNyLFxuICAgICAgdGhpcy5pbmplY3RvcixcbiAgICAgIHRlbXBsYXRlLFxuICAgICk7XG4gICAgdGhpcy5zdWJzY3JpYmVFdmVudHMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gU3Vic2NyaWJlcyB0byBldmVudHMgZW1pdHRlZCBieSB0aGUgZHluYW1pYyBjb21wb25lbnQuXG4gICAqIEBzdW1tYXJ5IFRoaXMgbWV0aG9kIHNldHMgdXAgc3Vic2NyaXB0aW9ucyB0byBhbGwgRXZlbnRFbWl0dGVyIHByb3BlcnRpZXMgb2YgdGhlXG4gICAqIGR5bmFtaWNhbGx5IGNyZWF0ZWQgY29tcG9uZW50LiBXaGVuIGFuIGV2ZW50IGlzIGVtaXR0ZWQgYnkgdGhlIGR5bmFtaWMgY29tcG9uZW50LFxuICAgKiBpdCBpcyBjYXB0dXJlZCBhbmQgcmUtZW1pdHRlZCB0aHJvdWdoIHRoZSBsaXN0ZW5FdmVudCBvdXRwdXQgcHJvcGVydHkgd2l0aCBhZGRpdGlvbmFsXG4gICAqIG1ldGFkYXRhIGFib3V0IHRoZSBldmVudCBzb3VyY2UuXG4gICAqXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEMgYXMgQ29tcG9uZW50UmVuZGVyZXJDb21wb25lbnRcbiAgICogICBwYXJ0aWNpcGFudCBEIGFzIER5bmFtaWMgQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgUCBhcyBQYXJlbnQgQ29tcG9uZW50XG4gICAqXG4gICAqICAgQy0+PkM6IHN1YnNjcmliZUV2ZW50cygpXG4gICAqICAgQy0+PkQ6IEdldCBpbnN0YW5jZSBwcm9wZXJ0aWVzXG4gICAqICAgbG9vcCBGb3IgZWFjaCBwcm9wZXJ0eVxuICAgKiAgICAgQy0+PkM6IENoZWNrIGlmIHByb3BlcnR5IGlzIEV2ZW50RW1pdHRlclxuICAgKiAgICAgYWx0IGlzIEV2ZW50RW1pdHRlclxuICAgKiAgICAgICBDLT4+RDogU3Vic2NyaWJlIHRvIGV2ZW50XG4gICAqICAgICAgIEQtLT4+QzogRXZlbnQgZW1pdHRlZFxuICAgKiAgICAgICBDLT4+UDogUmUtZW1pdCBldmVudCB3aXRoIG1ldGFkYXRhXG4gICAqICAgICBlbmRcbiAgICogICBlbmRcbiAgICpcbiAgICogQHByaXZhdGVcbiAgICogQHJldHVybiB7dm9pZH1cbiAgICogQG1lbWJlck9mIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqL1xuICBwcml2YXRlIHN1YnNjcmliZUV2ZW50cygpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5jb21wb25lbnQpIHtcbiAgICAgIGNvbnN0IGluc3RhbmNlID0gdGhpcy5jb21wb25lbnQ/Lmluc3RhbmNlIGFzIGFueTtcbiAgICAgIGNvbnN0IGNvbXBvbmVudEtleXMgPSBPYmplY3Qua2V5cyhpbnN0YW5jZSk7XG4gICAgICBmb3IgKGNvbnN0IGtleSBvZiBjb21wb25lbnRLZXlzKSB7XG4gICAgICAgIGNvbnN0IHZhbHVlID0gaW5zdGFuY2Vba2V5XTtcbiAgICAgICAgaWYgKHZhbHVlIGluc3RhbmNlb2YgRXZlbnRFbWl0dGVyKVxuICAgICAgICAgIChpbnN0YW5jZSBhcyBLZXlWYWx1ZSlba2V5XS5zdWJzY3JpYmUoXG4gICAgICAgICAgICAoZXZlbnQ6IFBhcnRpYWw8QmFzZUN1c3RvbUV2ZW50PikgPT4ge1xuICAgICAgICAgICAgICB0aGlzLmxpc3RlbkV2ZW50LmVtaXQoe1xuICAgICAgICAgICAgICAgIG5hbWU6IGtleSxcbiAgICAgICAgICAgICAgICAuLi5ldmVudCxcbiAgICAgICAgICAgICAgfSBhcyBNb2RlbFJlbmRlckN1c3RvbUV2ZW50KTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFVuc3Vic2NyaWJlcyBmcm9tIGFsbCBldmVudHMgb2YgdGhlIGR5bmFtaWMgY29tcG9uZW50LlxuICAgKiBAc3VtbWFyeSBUaGlzIG1ldGhvZCBjbGVhbnMgdXAgZXZlbnQgc3Vic2NyaXB0aW9ucyB3aGVuIHRoZSBjb21wb25lbnQgaXMgYmVpbmcgZGVzdHJveWVkLlxuICAgKiBJdCBpdGVyYXRlcyB0aHJvdWdoIGFsbCBwcm9wZXJ0aWVzIG9mIHRoZSBkeW5hbWljIGNvbXBvbmVudCBpbnN0YW5jZSBhbmQgdW5zdWJzY3JpYmVzXG4gICAqIGZyb20gYW55IEV2ZW50RW1pdHRlciBwcm9wZXJ0aWVzIHRvIHByZXZlbnQgbWVtb3J5IGxlYWtzIGFuZCB1bmV4cGVjdGVkIGJlaGF2aW9yIGFmdGVyXG4gICAqIHRoZSBjb21wb25lbnQgaXMgZGVzdHJveWVkLlxuICAgKlxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBDIGFzIENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50XG4gICAqICAgcGFydGljaXBhbnQgRCBhcyBEeW5hbWljIENvbXBvbmVudFxuICAgKlxuICAgKiAgIEMtPj5DOiB1bnN1YnNjcmliZUV2ZW50cygpXG4gICAqICAgQy0+PkQ6IEdldCBpbnN0YW5jZSBwcm9wZXJ0aWVzXG4gICAqICAgbG9vcCBGb3IgZWFjaCBwcm9wZXJ0eVxuICAgKiAgICAgQy0+PkM6IENoZWNrIGlmIHByb3BlcnR5IGlzIEV2ZW50RW1pdHRlclxuICAgKiAgICAgYWx0IGlzIEV2ZW50RW1pdHRlclxuICAgKiAgICAgICBDLT4+RDogVW5zdWJzY3JpYmUgZnJvbSBldmVudFxuICAgKiAgICAgZW5kXG4gICAqICAgZW5kXG4gICAqXG4gICAqIEBwcml2YXRlXG4gICAqIEByZXR1cm4ge3ZvaWR9XG4gICAqIEBtZW1iZXJPZiBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICAgKi9cbiAgcHJpdmF0ZSB1bnN1YnNjcmliZUV2ZW50cygpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5jb21wb25lbnQpIHtcbiAgICAgIGNvbnN0IGluc3RhbmNlID0gdGhpcy5jb21wb25lbnQ/Lmluc3RhbmNlIGFzIGFueTtcbiAgICAgIGNvbnN0IGNvbXBvbmVudEtleXMgPSBPYmplY3Qua2V5cyhpbnN0YW5jZSk7XG4gICAgICBmb3IgKGNvbnN0IGtleSBvZiBjb21wb25lbnRLZXlzKSB7XG4gICAgICAgIGNvbnN0IHZhbHVlID0gaW5zdGFuY2Vba2V5XTtcbiAgICAgICAgaWYgKHZhbHVlIGluc3RhbmNlb2YgRXZlbnRFbWl0dGVyKSBpbnN0YW5jZVtrZXldLnVuc3Vic2NyaWJlKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG59XG4iLCI8bmctdGVtcGxhdGUgI2NvbXBvbmVudFZpZXdDb250YWluZXI+PC9uZy10ZW1wbGF0ZT5cblxuPG5nLXRlbXBsYXRlICNpbm5lcj5cbiAgQGlmKHBhcmVudD8uY2hpbGRyZW4/Lmxlbmd0aCkge1xuICAgIEBmb3IoY2hpbGQgb2YgcGFyZW50LmNoaWxkcmVuOyB0cmFjayBjaGlsZCkge1xuICAgICAgICBAaWYoIWNoaWxkLmNoaWxkcmVuPy5sZW5ndGgpIHtcbiAgICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICAgICAgKm5nQ29tcG9uZW50T3V0bGV0PVwiXG4gICAgICAgICAgICAgICAgICBjaGlsZC5jb21wb25lbnQ7XG4gICAgICAgICAgICAgICAgICBpbmplY3RvcjogY2hpbGQuaW5qZWN0b3I7XG4gICAgICAgICAgICAgICAgICBpbnB1dHM6IGNoaWxkLmlucHV0cztcbiAgICAgICAgICAgICAgICAgIGNvbnRlbnQ6Y2hpbGQuY29udGVudDtcbiAgICAgICAgICAgICAgICBcIlxuICAgICAgICAgIC8+XG4gICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgIDxuZ3gtZGVjYWYtY29tcG9uZW50LXJlbmRlcmVyIFtwYXJlbnRdPVwiY2hpbGRcIj4gPC9uZ3gtZGVjYWYtY29tcG9uZW50LXJlbmRlcmVyPlxuICAgICAgICB9XG4gICAgIH1cbiAgfVxuPC9uZy10ZW1wbGF0ZT5cblxuXG4iXX0=
|