@decaf-ts/for-angular 0.0.10 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/esm2022/components/component-renderer/component-renderer.component.mjs +313 -0
- package/dist/lib/esm2022/components/crud-field/crud-field.component.mjs +301 -0
- package/dist/lib/esm2022/components/crud-form/constants.mjs +14 -0
- package/dist/lib/esm2022/components/crud-form/crud-form.component.mjs +139 -0
- package/dist/lib/esm2022/components/crud-form/types.mjs +2 -0
- package/dist/lib/esm2022/components/empty-state/empty-state.component.mjs +348 -0
- package/dist/lib/esm2022/components/fieldset/fieldset.component.mjs +225 -0
- package/dist/lib/esm2022/components/filter/filter.component.mjs +689 -0
- package/dist/lib/esm2022/components/for-angular-components.module.mjs +71 -0
- package/dist/lib/esm2022/components/index.mjs +20 -0
- package/dist/lib/esm2022/components/layout/layout.component.mjs +176 -0
- package/dist/lib/esm2022/components/list/constants.mjs +6 -0
- package/dist/lib/esm2022/components/list/list.component.mjs +1236 -0
- package/dist/{esm2022 → lib/esm2022}/components/list-item/list-item.component.mjs +1 -1
- package/dist/lib/esm2022/components/model-renderer/model-renderer.component.mjs +138 -0
- package/dist/lib/esm2022/components/pagination/constants.mjs +2 -0
- package/dist/lib/esm2022/components/pagination/pagination.component.mjs +323 -0
- package/dist/lib/esm2022/components/searchbar/searchbar.component.mjs +493 -0
- package/dist/lib/esm2022/decaf-ts-for-angular.mjs +5 -0
- package/dist/lib/esm2022/directives/collapsable.directive.mjs +28 -0
- package/dist/lib/esm2022/directives/index.mjs +2 -0
- package/dist/lib/esm2022/engine/DynamicModule.mjs +18 -0
- package/dist/lib/esm2022/engine/NgxBaseComponent.mjs +539 -0
- package/dist/lib/esm2022/engine/NgxCrudFormField.mjs +125 -0
- package/dist/lib/esm2022/engine/NgxFormService.mjs +315 -0
- package/dist/lib/esm2022/engine/NgxRenderingEngine.mjs +192 -0
- package/dist/lib/esm2022/engine/NgxRenderingEngine2.mjs +332 -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 +17 -0
- package/dist/lib/esm2022/engine/types.mjs +4 -0
- package/dist/lib/esm2022/for-angular.module.mjs +118 -0
- package/dist/lib/esm2022/helpers/index.mjs +13 -0
- package/dist/lib/esm2022/helpers/utils.mjs +415 -0
- package/dist/lib/esm2022/interfaces.mjs +2 -0
- package/dist/lib/esm2022/public-apis.mjs +14 -0
- package/dist/lib/fesm2022/decaf-ts-for-angular.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/esm2022/components/component-renderer/component-renderer.component.mjs +0 -313
- package/dist/esm2022/components/crud-field/crud-field.component.mjs +0 -301
- package/dist/esm2022/components/crud-form/constants.mjs +0 -14
- package/dist/esm2022/components/crud-form/crud-form.component.mjs +0 -139
- package/dist/esm2022/components/crud-form/types.mjs +0 -2
- package/dist/esm2022/components/empty-state/empty-state.component.mjs +0 -348
- package/dist/esm2022/components/fieldset/fieldset.component.mjs +0 -225
- package/dist/esm2022/components/filter/filter.component.mjs +0 -689
- package/dist/esm2022/components/for-angular-components.module.mjs +0 -71
- package/dist/esm2022/components/index.mjs +0 -20
- package/dist/esm2022/components/layout/layout.component.mjs +0 -176
- package/dist/esm2022/components/list/constants.mjs +0 -6
- package/dist/esm2022/components/list/list.component.mjs +0 -1236
- package/dist/esm2022/components/model-renderer/model-renderer.component.mjs +0 -138
- package/dist/esm2022/components/pagination/constants.mjs +0 -2
- package/dist/esm2022/components/pagination/pagination.component.mjs +0 -323
- package/dist/esm2022/components/searchbar/searchbar.component.mjs +0 -493
- package/dist/esm2022/decaf-ts-for-angular.mjs +0 -5
- package/dist/esm2022/directives/collapsable.directive.mjs +0 -28
- package/dist/esm2022/directives/index.mjs +0 -2
- package/dist/esm2022/engine/DynamicModule.mjs +0 -18
- package/dist/esm2022/engine/NgxBaseComponent.mjs +0 -539
- package/dist/esm2022/engine/NgxCrudFormField.mjs +0 -125
- package/dist/esm2022/engine/NgxFormService.mjs +0 -315
- package/dist/esm2022/engine/NgxRenderingEngine.mjs +0 -192
- package/dist/esm2022/engine/NgxRenderingEngine2.mjs +0 -332
- package/dist/esm2022/engine/ValidatorFactory.mjs +0 -102
- package/dist/esm2022/engine/constants.mjs +0 -160
- package/dist/esm2022/engine/decorators.mjs +0 -38
- package/dist/esm2022/engine/index.mjs +0 -17
- package/dist/esm2022/engine/types.mjs +0 -4
- package/dist/esm2022/for-angular.module.mjs +0 -118
- package/dist/esm2022/helpers/index.mjs +0 -13
- package/dist/esm2022/helpers/utils.mjs +0 -415
- package/dist/esm2022/interfaces.mjs +0 -2
- package/dist/esm2022/public-apis.mjs +0 -14
- package/dist/fesm2022/decaf-ts-for-angular.mjs.map +0 -1
- /package/dist/{README.md → lib/README.md} +0 -0
- /package/dist/{assets → lib/assets}/i18n/en.json +0 -0
- /package/dist/{assets → lib/assets}/images/angular-logo.svg +0 -0
- /package/dist/{assets → lib/assets}/images/decaf-logo-black.svg +0 -0
- /package/dist/{assets → lib/assets}/images/decaf-logo-lw.svg +0 -0
- /package/dist/{assets → lib/assets}/images/decaf-logo-white.svg +0 -0
- /package/dist/{assets → lib/assets}/images/decaf-logo.svg +0 -0
- /package/dist/{components → lib/components}/component-renderer/component-renderer.component.d.ts +0 -0
- /package/dist/{components → lib/components}/crud-field/crud-field.component.d.ts +0 -0
- /package/dist/{components → lib/components}/crud-form/constants.d.ts +0 -0
- /package/dist/{components → lib/components}/crud-form/crud-form.component.d.ts +0 -0
- /package/dist/{components → lib/components}/crud-form/types.d.ts +0 -0
- /package/dist/{components → lib/components}/empty-state/empty-state.component.d.ts +0 -0
- /package/dist/{components → lib/components}/fieldset/fieldset.component.d.ts +0 -0
- /package/dist/{components → lib/components}/filter/filter.component.d.ts +0 -0
- /package/dist/{components → lib/components}/for-angular-components.module.d.ts +0 -0
- /package/dist/{components → lib/components}/index.d.ts +0 -0
- /package/dist/{components → lib/components}/layout/layout.component.d.ts +0 -0
- /package/dist/{components → lib/components}/list/constants.d.ts +0 -0
- /package/dist/{components → lib/components}/list/list.component.d.ts +0 -0
- /package/dist/{components → lib/components}/list-item/list-item.component.d.ts +0 -0
- /package/dist/{components → lib/components}/model-renderer/model-renderer.component.d.ts +0 -0
- /package/dist/{components → lib/components}/pagination/constants.d.ts +0 -0
- /package/dist/{components → lib/components}/pagination/pagination.component.d.ts +0 -0
- /package/dist/{components → lib/components}/searchbar/searchbar.component.d.ts +0 -0
- /package/dist/{directives → lib/directives}/collapsable.directive.d.ts +0 -0
- /package/dist/{directives → lib/directives}/index.d.ts +0 -0
- /package/dist/{engine → lib/engine}/DynamicModule.d.ts +0 -0
- /package/dist/{engine → lib/engine}/NgxBaseComponent.d.ts +0 -0
- /package/dist/{engine → lib/engine}/NgxCrudFormField.d.ts +0 -0
- /package/dist/{engine → lib/engine}/NgxFormService.d.ts +0 -0
- /package/dist/{engine → lib/engine}/NgxRenderingEngine.d.ts +0 -0
- /package/dist/{engine → lib/engine}/NgxRenderingEngine2.d.ts +0 -0
- /package/dist/{engine → lib/engine}/ValidatorFactory.d.ts +0 -0
- /package/dist/{engine → lib/engine}/constants.d.ts +0 -0
- /package/dist/{engine → lib/engine}/decorators.d.ts +0 -0
- /package/dist/{engine → lib/engine}/index.d.ts +0 -0
- /package/dist/{engine → lib/engine}/types.d.ts +0 -0
- /package/dist/{fesm2022 → lib/fesm2022}/decaf-ts-for-angular.mjs +0 -0
- /package/dist/{for-angular.module.d.ts → lib/for-angular.module.d.ts} +0 -0
- /package/dist/{helpers → lib/helpers}/index.d.ts +0 -0
- /package/dist/{helpers → lib/helpers}/utils.d.ts +0 -0
- /package/dist/{index.d.ts → lib/index.d.ts} +0 -0
- /package/dist/{interfaces.d.ts → lib/interfaces.d.ts} +0 -0
- /package/dist/{public-apis.d.ts → lib/public-apis.d.ts} +0 -0
|
@@ -0,0 +1,1236 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import { Component, EventEmitter, Output, Input, HostListener } from '@angular/core';
|
|
3
|
+
import { IonInfiniteScroll, IonInfiniteScrollContent, IonItem, IonLabel, IonList, IonRefresher, IonRefresherContent, IonSkeletonText, IonText, IonThumbnail, IonLoading } from '@ionic/angular/standalone';
|
|
4
|
+
import { debounceTime, Subject } from 'rxjs';
|
|
5
|
+
import { OperationKeys } from '@decaf-ts/db-decorators';
|
|
6
|
+
import { Model, Primitives } from '@decaf-ts/decorator-validation';
|
|
7
|
+
import { Condition, OrderDirection } from '@decaf-ts/core';
|
|
8
|
+
import { Dynamic, EventConstants, ComponentsTagNames } from '../../engine';
|
|
9
|
+
import { ForAngularModule } from '../../for-angular.module';
|
|
10
|
+
import { NgxBaseComponent } from '../../engine/NgxBaseComponent';
|
|
11
|
+
import { stringToBoolean, formatDate, isValidDate } from '../../helpers';
|
|
12
|
+
import { SearchbarComponent } from '../searchbar/searchbar.component';
|
|
13
|
+
import { EmptyStateComponent } from '../empty-state/empty-state.component';
|
|
14
|
+
import { ListItemComponent } from '../list-item/list-item.component';
|
|
15
|
+
import { ComponentRendererComponent } from '../component-renderer/component-renderer.component';
|
|
16
|
+
import { PaginationComponent } from '../pagination/pagination.component';
|
|
17
|
+
import { ListComponentsTypes } from './constants';
|
|
18
|
+
import { FilterComponent } from '../filter/filter.component';
|
|
19
|
+
import * as i0 from "@angular/core";
|
|
20
|
+
import * as i1 from "@ionic/angular/standalone";
|
|
21
|
+
import * as i2 from "@angular/common";
|
|
22
|
+
import * as i3 from "@ngx-translate/core";
|
|
23
|
+
/**
|
|
24
|
+
* @description A versatile list component that supports various data display modes.
|
|
25
|
+
* @summary This component provides a flexible way to display lists of data with support
|
|
26
|
+
* for infinite scrolling, pagination, searching, and custom item rendering. It can fetch
|
|
27
|
+
* data from various sources including models, functions, or direct data input.
|
|
28
|
+
*
|
|
29
|
+
* The component supports two main display types:
|
|
30
|
+
* 1. Infinite scrolling - Loads more data as the user scrolls
|
|
31
|
+
* 2. Pagination - Displays data in pages with navigation controls
|
|
32
|
+
*
|
|
33
|
+
* Additional features include:
|
|
34
|
+
* - Pull-to-refresh functionality
|
|
35
|
+
* - Search filtering
|
|
36
|
+
* - Empty state customization
|
|
37
|
+
* - Custom item rendering
|
|
38
|
+
* - Event emission for interactions
|
|
39
|
+
*
|
|
40
|
+
* @mermaid
|
|
41
|
+
* sequenceDiagram
|
|
42
|
+
* participant U as User
|
|
43
|
+
* participant L as ListComponent
|
|
44
|
+
* participant D as Data Source
|
|
45
|
+
* participant E as External Components
|
|
46
|
+
*
|
|
47
|
+
* U->>L: Initialize component
|
|
48
|
+
* L->>L: ngOnInit()
|
|
49
|
+
* L->>D: Request initial data
|
|
50
|
+
* D-->>L: Return data
|
|
51
|
+
* L->>L: Process and display data
|
|
52
|
+
*
|
|
53
|
+
* alt User scrolls (Infinite mode)
|
|
54
|
+
* U->>L: Scroll to bottom
|
|
55
|
+
* L->>D: Request more data
|
|
56
|
+
* D-->>L: Return additional data
|
|
57
|
+
* L->>L: Append to existing data
|
|
58
|
+
* else User changes page (Paginated mode)
|
|
59
|
+
* U->>L: Click page number
|
|
60
|
+
* L->>L: handlePaginate()
|
|
61
|
+
* L->>D: Request data for page
|
|
62
|
+
* D-->>L: Return page data
|
|
63
|
+
* L->>L: Replace displayed data
|
|
64
|
+
* end
|
|
65
|
+
*
|
|
66
|
+
* alt User searches
|
|
67
|
+
* U->>L: Enter search term
|
|
68
|
+
* L->>L: handleSearch()
|
|
69
|
+
* L->>D: Filter data by search term
|
|
70
|
+
* D-->>L: Return filtered data
|
|
71
|
+
* L->>L: Update displayed data
|
|
72
|
+
* end
|
|
73
|
+
*
|
|
74
|
+
* alt User clicks item
|
|
75
|
+
* U->>L: Click list item
|
|
76
|
+
* L->>L: handleClick()
|
|
77
|
+
* L->>E: Emit clickEvent
|
|
78
|
+
* end
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* <ngx-decaf-list
|
|
82
|
+
* [source]="dataSource"
|
|
83
|
+
* [limit]="10"
|
|
84
|
+
* [type]="'infinite'"
|
|
85
|
+
* [showSearchbar]="true"
|
|
86
|
+
* (clickEvent)="handleItemClick($event)"
|
|
87
|
+
* (refreshEvent)="handleRefresh($event)">
|
|
88
|
+
* </ngx-decaf-list>
|
|
89
|
+
*
|
|
90
|
+
* @extends {NgxBaseComponent}
|
|
91
|
+
* @implements {OnInit}
|
|
92
|
+
*/
|
|
93
|
+
let ListComponent = class ListComponent extends NgxBaseComponent {
|
|
94
|
+
/**
|
|
95
|
+
* @description Initializes a new instance of the ListComponent.
|
|
96
|
+
* @summary Creates a new ListComponent and sets up the base component with the appropriate
|
|
97
|
+
* component name. This constructor is called when Angular instantiates the component and
|
|
98
|
+
* before any input properties are set. It passes the component name to the parent class
|
|
99
|
+
* constructor to enable proper localization and component identification.
|
|
100
|
+
*
|
|
101
|
+
* The constructor is intentionally minimal, with most initialization logic deferred to
|
|
102
|
+
* the ngOnInit lifecycle hook. This follows Angular best practices by keeping the constructor
|
|
103
|
+
* focused on dependency injection and basic setup, while complex initialization that depends
|
|
104
|
+
* on input properties is handled in ngOnInit.
|
|
105
|
+
*
|
|
106
|
+
* @memberOf ListComponent
|
|
107
|
+
*/
|
|
108
|
+
constructor() {
|
|
109
|
+
super("ListComponent");
|
|
110
|
+
/**
|
|
111
|
+
* @description The display mode for the list component.
|
|
112
|
+
* @summary Determines how the list data is loaded and displayed. Options include:
|
|
113
|
+
* - INFINITE: Loads more data as the user scrolls (infinite scrolling)
|
|
114
|
+
* - PAGINATED: Displays data in pages with navigation controls
|
|
115
|
+
*
|
|
116
|
+
* @type {ListComponentsTypes}
|
|
117
|
+
* @default ListComponentsTypes.INFINITE
|
|
118
|
+
* @memberOf ListComponent
|
|
119
|
+
*/
|
|
120
|
+
this.type = ListComponentsTypes.INFINITE;
|
|
121
|
+
/**
|
|
122
|
+
* @description Controls whether the component uses translation services.
|
|
123
|
+
* @summary When set to true, the component will attempt to use translation services
|
|
124
|
+
* for any text content. This allows for internationalization of the list component.
|
|
125
|
+
*
|
|
126
|
+
* @type {StringOrBoolean}
|
|
127
|
+
* @default true
|
|
128
|
+
* @memberOf ListComponent
|
|
129
|
+
*/
|
|
130
|
+
this.translatable = true;
|
|
131
|
+
/**
|
|
132
|
+
* @description Controls the visibility of the search bar.
|
|
133
|
+
* @summary When set to true, displays a search bar at the top of the list that allows
|
|
134
|
+
* users to filter the list items. The search functionality works by filtering the
|
|
135
|
+
* existing data or by triggering a new data fetch with search parameters.
|
|
136
|
+
*
|
|
137
|
+
* @type {StringOrBoolean}
|
|
138
|
+
* @default true
|
|
139
|
+
* @memberOf ListComponent
|
|
140
|
+
*/
|
|
141
|
+
this.showSearchbar = true;
|
|
142
|
+
/**
|
|
143
|
+
* @description Direct data input for the list component.
|
|
144
|
+
* @summary Provides a way to directly pass data to the list component instead of
|
|
145
|
+
* fetching it from a source. When both data and source are provided, the component
|
|
146
|
+
* will use the source to fetch data only if the data array is empty.
|
|
147
|
+
*
|
|
148
|
+
* @type {KeyValue[] | undefined}
|
|
149
|
+
* @default undefined
|
|
150
|
+
* @memberOf ListComponent
|
|
151
|
+
*/
|
|
152
|
+
this.data = undefined;
|
|
153
|
+
/**
|
|
154
|
+
* @description The starting index for data fetching.
|
|
155
|
+
* @summary Specifies the index from which to start fetching data. This is used
|
|
156
|
+
* for pagination and infinite scrolling to determine which subset of data to load.
|
|
157
|
+
*
|
|
158
|
+
* @type {number}
|
|
159
|
+
* @default 0
|
|
160
|
+
* @memberOf ListComponent
|
|
161
|
+
*/
|
|
162
|
+
this.start = 0;
|
|
163
|
+
/**
|
|
164
|
+
* @description The number of items to fetch per page or load operation.
|
|
165
|
+
* @summary Determines how many items are loaded at once during pagination or
|
|
166
|
+
* infinite scrolling. This affects the size of data chunks requested from the source.
|
|
167
|
+
*
|
|
168
|
+
* @type {number}
|
|
169
|
+
* @default 10
|
|
170
|
+
* @memberOf ListComponent
|
|
171
|
+
*/
|
|
172
|
+
this.limit = 10;
|
|
173
|
+
/**
|
|
174
|
+
* @description Controls whether more data can be loaded.
|
|
175
|
+
* @summary When set to true, the component will allow loading additional data
|
|
176
|
+
* through infinite scrolling or pagination. When false, the component will not
|
|
177
|
+
* attempt to load more data beyond what is initially displayed.
|
|
178
|
+
*
|
|
179
|
+
* @type {StringOrBoolean}
|
|
180
|
+
* @default true
|
|
181
|
+
* @memberOf ListComponent
|
|
182
|
+
*/
|
|
183
|
+
this.loadMoreData = true;
|
|
184
|
+
/**
|
|
185
|
+
* @description The style of dividing lines between list items.
|
|
186
|
+
* @summary Determines how dividing lines appear between list items. Options include:
|
|
187
|
+
* - "inset": Lines are inset from the edges
|
|
188
|
+
* - "full": Lines extend the full width
|
|
189
|
+
* - "none": No dividing lines
|
|
190
|
+
*
|
|
191
|
+
* @type {"inset" | "full" | "none"}
|
|
192
|
+
* @default "full"
|
|
193
|
+
* @memberOf ListComponent
|
|
194
|
+
*/
|
|
195
|
+
this.lines = "full";
|
|
196
|
+
/**
|
|
197
|
+
* @description Controls whether the list has inset styling.
|
|
198
|
+
* @summary When set to true, the list will have inset styling with rounded corners
|
|
199
|
+
* and margin around the edges. This creates a card-like appearance for the list.
|
|
200
|
+
*
|
|
201
|
+
* @type {StringOrBoolean}
|
|
202
|
+
* @default false
|
|
203
|
+
* @memberOf ListComponent
|
|
204
|
+
*/
|
|
205
|
+
this.inset = false;
|
|
206
|
+
/**
|
|
207
|
+
* @description The threshold for triggering infinite scroll loading.
|
|
208
|
+
* @summary Specifies how close to the bottom of the list the user must scroll
|
|
209
|
+
* before the component triggers loading of additional data. This is expressed
|
|
210
|
+
* as a percentage of the list height.
|
|
211
|
+
*
|
|
212
|
+
* @type {string}
|
|
213
|
+
* @default "15%"
|
|
214
|
+
* @memberOf ListComponent
|
|
215
|
+
*/
|
|
216
|
+
this.scrollThreshold = "15%";
|
|
217
|
+
/**
|
|
218
|
+
* @description The position where new items are added during infinite scrolling.
|
|
219
|
+
* @summary Determines whether new items are added to the top or bottom of the list
|
|
220
|
+
* when loading more data through infinite scrolling.
|
|
221
|
+
*
|
|
222
|
+
* @type {"bottom" | "top"}
|
|
223
|
+
* @default "bottom"
|
|
224
|
+
* @memberOf ListComponent
|
|
225
|
+
*/
|
|
226
|
+
this.scrollPosition = "bottom";
|
|
227
|
+
/**
|
|
228
|
+
* @description Controls the visibility of the pull-to-refresh feature.
|
|
229
|
+
* @summary When set to true, enables the pull-to-refresh functionality that allows
|
|
230
|
+
* users to refresh the list data by pulling down from the top of the list.
|
|
231
|
+
*
|
|
232
|
+
* @type {StringOrBoolean}
|
|
233
|
+
* @default true
|
|
234
|
+
* @memberOf ListComponent
|
|
235
|
+
*/
|
|
236
|
+
this.showRefresher = true;
|
|
237
|
+
/**
|
|
238
|
+
* @description The type of spinner to display during loading operations.
|
|
239
|
+
* @summary Specifies the visual style of the loading spinner shown during data
|
|
240
|
+
* fetching operations. Uses Ionic's predefined spinner types.
|
|
241
|
+
*
|
|
242
|
+
* @type {SpinnerTypes}
|
|
243
|
+
* @default "circular"
|
|
244
|
+
* @memberOf ListComponent
|
|
245
|
+
*/
|
|
246
|
+
this.loadingSpinner = "circular";
|
|
247
|
+
// /**
|
|
248
|
+
// * @description Query parameters for data fetching.
|
|
249
|
+
// * @summary Specifies additional query parameters to use when fetching data from
|
|
250
|
+
// * the source. This can be provided as a string (JSON) or a direct object.
|
|
251
|
+
// *
|
|
252
|
+
// * @type {string | KeyValue | undefined}
|
|
253
|
+
// * @memberOf ListComponent
|
|
254
|
+
// */
|
|
255
|
+
// @Input()
|
|
256
|
+
// query?: string | KeyValue;
|
|
257
|
+
/**
|
|
258
|
+
* @description Controls whether the filtering functionality is enabled.
|
|
259
|
+
* @summary When set to true, enables the filter component that allows users to create
|
|
260
|
+
* complex search criteria with multiple field filters, conditions, and values.
|
|
261
|
+
* When false, disables the filter interface entirely.
|
|
262
|
+
*
|
|
263
|
+
* @type {StringOrBoolean}
|
|
264
|
+
* @default true
|
|
265
|
+
* @memberOf ListComponent
|
|
266
|
+
*/
|
|
267
|
+
this.enableFilter = true;
|
|
268
|
+
/**
|
|
269
|
+
* @description Sorting parameters for data fetching.
|
|
270
|
+
* @summary Specifies how the fetched data should be sorted. This can be provided
|
|
271
|
+
* as a string (field name with optional direction) or a direct object.
|
|
272
|
+
*
|
|
273
|
+
* @type {string | KeyValue | undefined}
|
|
274
|
+
* @memberOf ListComponent
|
|
275
|
+
*/
|
|
276
|
+
this.sortDirection = OrderDirection.DSC;
|
|
277
|
+
/**
|
|
278
|
+
* @description Controls whether sorting functionality is disabled.
|
|
279
|
+
* @summary When set to true, disables the sort controls and prevents users from
|
|
280
|
+
* changing the sort order or field. The list will maintain its default or
|
|
281
|
+
* programmatically set sort configuration without user interaction.
|
|
282
|
+
*
|
|
283
|
+
* @type {StringOrBoolean}
|
|
284
|
+
* @default false
|
|
285
|
+
* @memberOf ListComponent
|
|
286
|
+
*/
|
|
287
|
+
this.disableSort = false;
|
|
288
|
+
/**
|
|
289
|
+
* @description Icon to display when the list is empty.
|
|
290
|
+
* @summary Specifies the icon shown in the empty state when no data is available.
|
|
291
|
+
* This can be any icon name supported by the application's icon system.
|
|
292
|
+
*
|
|
293
|
+
* @type {string | undefined}
|
|
294
|
+
* @default 'ti-database-exclamation'
|
|
295
|
+
* @memberOf ListComponent
|
|
296
|
+
*/
|
|
297
|
+
this.emptyIcon = 'ti-database-exclamation';
|
|
298
|
+
/**
|
|
299
|
+
* @description Configuration for the empty state display.
|
|
300
|
+
* @summary Customizes how the empty state is displayed when no data is available.
|
|
301
|
+
* This includes the title, subtitle, button text, icon, and navigation link.
|
|
302
|
+
*
|
|
303
|
+
* @type {Partial<IListEmptyResult>}
|
|
304
|
+
* @default {
|
|
305
|
+
* title: 'empty.title',
|
|
306
|
+
* subtitle: 'empty.subtitle',
|
|
307
|
+
* showButton: false,
|
|
308
|
+
* icon: 'alert-circle-outline',
|
|
309
|
+
* buttonText: 'locale.empty.button',
|
|
310
|
+
* link: ''
|
|
311
|
+
* }
|
|
312
|
+
* @memberOf ListComponent
|
|
313
|
+
*/
|
|
314
|
+
this.empty = {
|
|
315
|
+
title: 'empty.title',
|
|
316
|
+
subtitle: 'empty.subtitle',
|
|
317
|
+
showButton: false,
|
|
318
|
+
icon: 'alert-circle-outline',
|
|
319
|
+
buttonText: 'locale.empty.button',
|
|
320
|
+
link: ''
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* @description The current page number in paginated mode.
|
|
324
|
+
* @summary Tracks which page is currently being displayed when the component
|
|
325
|
+
* is in paginated mode. This is used for pagination controls and data fetching.
|
|
326
|
+
*
|
|
327
|
+
* @type {number}
|
|
328
|
+
* @default 1
|
|
329
|
+
* @memberOf ListComponent
|
|
330
|
+
*/
|
|
331
|
+
this.page = 1;
|
|
332
|
+
/**
|
|
333
|
+
* @description Indicates whether a refresh operation is in progress.
|
|
334
|
+
* @summary When true, the component is currently fetching new data. This is used
|
|
335
|
+
* to control loading indicators and prevent duplicate refresh operations from
|
|
336
|
+
* being triggered simultaneously.
|
|
337
|
+
*
|
|
338
|
+
* @type {boolean}
|
|
339
|
+
* @default false
|
|
340
|
+
* @memberOf ListComponent
|
|
341
|
+
*/
|
|
342
|
+
this.refreshing = false;
|
|
343
|
+
/**
|
|
344
|
+
* @description Array used for rendering skeleton loading placeholders.
|
|
345
|
+
* @summary Contains placeholder items that are displayed during data loading.
|
|
346
|
+
* The length of this array determines how many skeleton items are shown.
|
|
347
|
+
*
|
|
348
|
+
* @type {string[]}
|
|
349
|
+
* @default new Array(2)
|
|
350
|
+
* @memberOf ListComponent
|
|
351
|
+
*/
|
|
352
|
+
this.skeletonData = new Array(2);
|
|
353
|
+
/**
|
|
354
|
+
* @description The last page number that was displayed.
|
|
355
|
+
* @summary Keeps track of the previously displayed page number, which is useful
|
|
356
|
+
* for handling navigation and search operations in paginated mode.
|
|
357
|
+
*
|
|
358
|
+
* @type {number}
|
|
359
|
+
* @default 1
|
|
360
|
+
* @memberOf ListComponent
|
|
361
|
+
*/
|
|
362
|
+
this.lastPage = 1;
|
|
363
|
+
/**
|
|
364
|
+
* @description Event emitter for refresh operations.
|
|
365
|
+
* @summary Emits an event when the list data is refreshed, either through pull-to-refresh
|
|
366
|
+
* or programmatic refresh. The event includes the refreshed data and component information.
|
|
367
|
+
*
|
|
368
|
+
* @type {EventEmitter<BaseCustomEvent>}
|
|
369
|
+
* @memberOf ListComponent
|
|
370
|
+
*/
|
|
371
|
+
this.refreshEvent = new EventEmitter();
|
|
372
|
+
/**
|
|
373
|
+
* @description Event emitter for item click interactions.
|
|
374
|
+
* @summary Emits an event when a list item is clicked. The event includes the data
|
|
375
|
+
* of the clicked item, allowing parent components to respond to the interaction.
|
|
376
|
+
*
|
|
377
|
+
* @type {EventEmitter<KeyValue>}
|
|
378
|
+
* @memberOf ListComponent
|
|
379
|
+
*/
|
|
380
|
+
this.clickEvent = new EventEmitter();
|
|
381
|
+
/**
|
|
382
|
+
* @description Subject for debouncing click events.
|
|
383
|
+
* @summary Uses RxJS Subject to collect click events and emit them after a debounce
|
|
384
|
+
* period. This prevents multiple rapid clicks from triggering multiple events.
|
|
385
|
+
*
|
|
386
|
+
* @private
|
|
387
|
+
* @type {Subject<CustomEvent | ListItemCustomEvent | RendererCustomEvent>}
|
|
388
|
+
* @memberOf ListComponent
|
|
389
|
+
*/
|
|
390
|
+
this.clickItemSubject = new Subject();
|
|
391
|
+
/**
|
|
392
|
+
* @description Subject for debouncing repository observation events.
|
|
393
|
+
* @summary RxJS Subject that collects repository change events and emits them after
|
|
394
|
+
* a debounce period. This prevents multiple rapid repository changes from triggering
|
|
395
|
+
* multiple list refresh operations, improving performance and user experience.
|
|
396
|
+
*
|
|
397
|
+
* @private
|
|
398
|
+
* @type {Subject<any>}
|
|
399
|
+
* @memberOf ListComponent
|
|
400
|
+
*/
|
|
401
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
402
|
+
this.observerSubjet = new Subject();
|
|
403
|
+
/**
|
|
404
|
+
* @description Observer object for repository change notifications.
|
|
405
|
+
* @summary Implements the Observer interface to receive notifications when the
|
|
406
|
+
* underlying data repository changes. This enables automatic list updates when
|
|
407
|
+
* data is created, updated, or deleted through the repository.
|
|
408
|
+
*
|
|
409
|
+
* @private
|
|
410
|
+
* @type {Observer}
|
|
411
|
+
* @memberOf ListComponent
|
|
412
|
+
*/
|
|
413
|
+
this.observer = { refresh: async (...args) => this.observeRepository(...args) };
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* @description Initializes the component after Angular sets the input properties.
|
|
417
|
+
* @summary Sets up the component by initializing event subscriptions, processing boolean
|
|
418
|
+
* inputs, and loading the initial data. This method prepares the component for user
|
|
419
|
+
* interaction by ensuring all properties are properly initialized and data is loaded.
|
|
420
|
+
*
|
|
421
|
+
* @returns {Promise<void>}
|
|
422
|
+
*
|
|
423
|
+
* @mermaid
|
|
424
|
+
* sequenceDiagram
|
|
425
|
+
* participant A as Angular Lifecycle
|
|
426
|
+
* participant L as ListComponent
|
|
427
|
+
* participant D as Data Source
|
|
428
|
+
*
|
|
429
|
+
* A->>L: ngOnInit()
|
|
430
|
+
* L->>L: Set up click event debouncing
|
|
431
|
+
* L->>L: Process boolean inputs
|
|
432
|
+
* L->>L: Configure component based on inputs
|
|
433
|
+
* L->>L: refresh()
|
|
434
|
+
* L->>D: Request initial data
|
|
435
|
+
* D-->>L: Return data
|
|
436
|
+
* L->>L: Process and display data
|
|
437
|
+
* L->>L: Configure empty state if needed
|
|
438
|
+
* L->>L: initialize()
|
|
439
|
+
*
|
|
440
|
+
* @memberOf ListComponent
|
|
441
|
+
*/
|
|
442
|
+
async ngOnInit() {
|
|
443
|
+
this.clickItemSubject.pipe(debounceTime(100)).subscribe(event => this.clickEventEmit(event));
|
|
444
|
+
this.observerSubjet.pipe(debounceTime(100)).subscribe(args => this.handleObserveEvent(args[0], args[1], args[2]));
|
|
445
|
+
this.enableFilter = stringToBoolean(this.enableFilter);
|
|
446
|
+
this.limit = Number(this.limit);
|
|
447
|
+
this.start = Number(this.start);
|
|
448
|
+
this.inset = stringToBoolean(this.inset);
|
|
449
|
+
this.showRefresher = stringToBoolean(this.showRefresher);
|
|
450
|
+
this.loadMoreData = stringToBoolean(this.loadMoreData);
|
|
451
|
+
this.showSearchbar = stringToBoolean(this.showSearchbar);
|
|
452
|
+
this.disableSort = stringToBoolean(this.disableSort);
|
|
453
|
+
if (typeof this.item?.['tag'] === 'boolean' && this.item?.['tag'] === true)
|
|
454
|
+
this.item['tag'] = ComponentsTagNames.LIST_ITEM;
|
|
455
|
+
await this.refresh();
|
|
456
|
+
if (this.operations.includes(OperationKeys.CREATE) && this.route)
|
|
457
|
+
this.empty.link = `${this.route}/${OperationKeys.CREATE}`;
|
|
458
|
+
this.initialize();
|
|
459
|
+
if (this.model instanceof Model && this._repository)
|
|
460
|
+
this._repository.observe(this.observer);
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* @description Cleans up resources when the component is destroyed.
|
|
464
|
+
* @summary Performs cleanup operations when the component is being removed from the DOM.
|
|
465
|
+
* This includes clearing references to models and data to prevent memory leaks.
|
|
466
|
+
*
|
|
467
|
+
* @returns {void}
|
|
468
|
+
* @memberOf ListComponent
|
|
469
|
+
*/
|
|
470
|
+
ngOnDestroy() {
|
|
471
|
+
if (this._repository)
|
|
472
|
+
this._repository.unObserve(this.observer);
|
|
473
|
+
this.data = this.model = this._repository = this.paginator = undefined;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* @description Handles repository observation events with debouncing.
|
|
477
|
+
* @summary Processes repository change notifications and routes them appropriately.
|
|
478
|
+
* For CREATE events with a UID, handles them immediately. For other events,
|
|
479
|
+
* passes them to the debounced observer subject to prevent excessive updates.
|
|
480
|
+
*
|
|
481
|
+
* @param {...unknown[]} args - The repository event arguments including table, event type, and UID
|
|
482
|
+
* @returns {Promise<void>}
|
|
483
|
+
* @memberOf ListComponent
|
|
484
|
+
*/
|
|
485
|
+
async observeRepository(...args) {
|
|
486
|
+
const [table, event, uid] = args;
|
|
487
|
+
if (event === OperationKeys.CREATE && !!uid)
|
|
488
|
+
return this.handleObserveEvent(table, event, uid);
|
|
489
|
+
return this.observerSubjet.next(args);
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* @description Handles specific repository events and updates the list accordingly.
|
|
493
|
+
* @summary Processes repository change events (CREATE, UPDATE, DELETE) and performs
|
|
494
|
+
* the appropriate list operations. This includes adding new items, updating existing
|
|
495
|
+
* ones, or removing deleted items from the list display.
|
|
496
|
+
*
|
|
497
|
+
* @param {string} table - The table/model name that changed
|
|
498
|
+
* @param {OperationKeys} event - The type of operation (CREATE, UPDATE, DELETE)
|
|
499
|
+
* @param {string | number} uid - The unique identifier of the affected item
|
|
500
|
+
* @returns {Promise<void>}
|
|
501
|
+
* @memberOf ListComponent
|
|
502
|
+
*/
|
|
503
|
+
async handleObserveEvent(table, event, uid) {
|
|
504
|
+
if (event === OperationKeys.CREATE) {
|
|
505
|
+
if (uid) {
|
|
506
|
+
await this.handleCreate(uid);
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
await this.refresh(true);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
if (event === OperationKeys.UPDATE)
|
|
514
|
+
await this.handleUpdate(uid);
|
|
515
|
+
if (event === OperationKeys.DELETE)
|
|
516
|
+
this.handleDelete(uid);
|
|
517
|
+
this.refreshEventEmit();
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* @description Function for tracking items in the list.
|
|
522
|
+
* @summary Provides a tracking function for the `*ngFor` directive in the component template.
|
|
523
|
+
* This function is used to identify and control the rendering of items in the list,
|
|
524
|
+
* preventing duplicate or unnecessary rendering.
|
|
525
|
+
*
|
|
526
|
+
* The `trackItemFn` function takes two parameters: `index` (the index of the item in the list)
|
|
527
|
+
* and `item` (the actual item from the list). It returns the tracking key, which in this case
|
|
528
|
+
* is the union of the `uid` of the item with the model name.
|
|
529
|
+
*
|
|
530
|
+
* @param {number} index - The index of the item in the list.
|
|
531
|
+
|
|
532
|
+
* @param {KeyValue | string | number} item - The actual item from the list.
|
|
533
|
+
* @returns {string | number} The tracking key for the item.
|
|
534
|
+
* @memberOf ListComponent
|
|
535
|
+
*/
|
|
536
|
+
trackItemFn(index, item) {
|
|
537
|
+
return `${item?.['uid'] || item?.[this.pk]}-${index}`;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Handles the create event from the repository.
|
|
541
|
+
*
|
|
542
|
+
* @param {string | number} uid - The ID of the item to create.
|
|
543
|
+
* @returns {Promise<void>} A promise that resolves when the item is created and added to the list.
|
|
544
|
+
*/
|
|
545
|
+
async handleCreate(uid) {
|
|
546
|
+
const result = await this._repository?.read(uid);
|
|
547
|
+
const item = this.mapResults([result])[0];
|
|
548
|
+
this.items = this.data = [item, ...this.items || []];
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* @description Handles the update event from the repository.
|
|
552
|
+
* @summary Updates the list item with the specified ID based on the new data.
|
|
553
|
+
*
|
|
554
|
+
* @param {string | number} uid - The ID of the item to update
|
|
555
|
+
* @returns {Promise<void>}
|
|
556
|
+
* @private
|
|
557
|
+
* @memberOf ListComponent
|
|
558
|
+
*/
|
|
559
|
+
async handleUpdate(uid) {
|
|
560
|
+
const item = this.itemMapper(await this._repository?.read(uid) || {}, this.mapper);
|
|
561
|
+
this.data = [];
|
|
562
|
+
for (const key in this.items) {
|
|
563
|
+
const child = this.items[key];
|
|
564
|
+
if (child['uid'] === item['uid']) {
|
|
565
|
+
this.items[key] = Object.assign({}, child, item);
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
setTimeout(() => {
|
|
570
|
+
this.data = [...this.items];
|
|
571
|
+
}, 0);
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* @description Removes an item from the list by ID.
|
|
575
|
+
* @summary Filters out an item with the specified ID from the data array and
|
|
576
|
+
* refreshes the list display. This is typically used after a delete operation.
|
|
577
|
+
*
|
|
578
|
+
* @param {string} uid - The ID of the item to delete
|
|
579
|
+
* @param {string} pk - The primary key field name
|
|
580
|
+
* @returns {Promise<void>}
|
|
581
|
+
*
|
|
582
|
+
* @memberOf ListComponent
|
|
583
|
+
*/
|
|
584
|
+
handleDelete(uid, pk) {
|
|
585
|
+
if (!pk)
|
|
586
|
+
pk = this.pk;
|
|
587
|
+
this.items = this.data?.filter((item) => item['uid'] !== uid) || [];
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* @description Handles click events from list items.
|
|
591
|
+
* @summary Listens for global ListItemClickEvent events and passes them to the
|
|
592
|
+
* debounced click subject. This allows the component to respond to clicks on
|
|
593
|
+
* list items regardless of where they originate from.
|
|
594
|
+
*
|
|
595
|
+
* @param {ListItemCustomEvent | RendererCustomEvent} event - The click event
|
|
596
|
+
* @returns {void}
|
|
597
|
+
*
|
|
598
|
+
* @memberOf ListComponent
|
|
599
|
+
*/
|
|
600
|
+
handleClick(event) {
|
|
601
|
+
this.clickItemSubject.next(event);
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* @description Handles search events from the search bar.
|
|
605
|
+
* @summary Processes search queries from the search bar component, updating the
|
|
606
|
+
* displayed data based on the search term. The behavior differs between infinite
|
|
607
|
+
* and paginated modes to provide the best user experience for each mode.
|
|
608
|
+
*
|
|
609
|
+
* @param {string | undefined} value - The search term or undefined to clear search
|
|
610
|
+
* @returns {Promise<void>}
|
|
611
|
+
*
|
|
612
|
+
* @mermaid
|
|
613
|
+
* flowchart TD
|
|
614
|
+
* A[Search Event] --> B{Type is Infinite?}
|
|
615
|
+
* B -->|Yes| C[Disable loadMoreData]
|
|
616
|
+
* B -->|No| D[Enable loadMoreData]
|
|
617
|
+
* C --> E{Search value undefined?}
|
|
618
|
+
* E -->|Yes| F[Enable loadMoreData]
|
|
619
|
+
* E -->|No| G[Store search value]
|
|
620
|
+
* D --> G
|
|
621
|
+
* F --> H[Reset page to 1]
|
|
622
|
+
* G --> I[Refresh data]
|
|
623
|
+
* H --> I
|
|
624
|
+
*
|
|
625
|
+
* @memberOf ListComponent
|
|
626
|
+
*/
|
|
627
|
+
async handleSearch(value) {
|
|
628
|
+
if (this.type === ListComponentsTypes.INFINITE) {
|
|
629
|
+
this.loadMoreData = false;
|
|
630
|
+
if (value === undefined) {
|
|
631
|
+
this.loadMoreData = true;
|
|
632
|
+
this.page = 1;
|
|
633
|
+
}
|
|
634
|
+
this.searchValue = value;
|
|
635
|
+
await this.refresh(true);
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
this.loadMoreData = true;
|
|
639
|
+
this.searchValue = value;
|
|
640
|
+
if (value === undefined)
|
|
641
|
+
this.page = this.lastPage;
|
|
642
|
+
await this.refresh(true);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* @description Handles filter events from the filter component.
|
|
647
|
+
* @summary Processes filter queries from the filter component and applies them
|
|
648
|
+
* to the list data. This method acts as a bridge between the filter component
|
|
649
|
+
* and the search functionality, converting filter queries into search operations.
|
|
650
|
+
*
|
|
651
|
+
* @param {IFilterQuery | undefined} value - The filter query object or undefined to clear filters
|
|
652
|
+
* @returns {Promise<void>}
|
|
653
|
+
* @memberOf ListComponent
|
|
654
|
+
*/
|
|
655
|
+
async handleFilter(value) {
|
|
656
|
+
await this.handleSearch(value);
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* @description Clears the current search and resets the list.
|
|
660
|
+
* @summary Convenience method that clears the search by calling handleSearch
|
|
661
|
+
* with undefined. This resets the list to show all data without filtering.
|
|
662
|
+
*
|
|
663
|
+
* @returns {Promise<void>}
|
|
664
|
+
* @memberOf ListComponent
|
|
665
|
+
*/
|
|
666
|
+
async clearSearch() {
|
|
667
|
+
await this.handleSearch(undefined);
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* @description Emits a refresh event with the current data.
|
|
671
|
+
* @summary Creates and emits a refresh event containing the current list data.
|
|
672
|
+
* This notifies parent components that the list data has been refreshed.
|
|
673
|
+
*
|
|
674
|
+
* @param {KeyValue[]} [data] - Optional data to include in the event
|
|
675
|
+
* @returns {void}
|
|
676
|
+
*
|
|
677
|
+
* @memberOf ListComponent
|
|
678
|
+
*/
|
|
679
|
+
refreshEventEmit(data) {
|
|
680
|
+
if (!data)
|
|
681
|
+
data = this.items;
|
|
682
|
+
this.skeletonData = new Array(data?.length || 2);
|
|
683
|
+
this.refreshEvent.emit({
|
|
684
|
+
name: EventConstants.REFRESH_EVENT,
|
|
685
|
+
data: data || [],
|
|
686
|
+
component: this.componentName
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* @description Emits a click event for a list item.
|
|
691
|
+
* @summary Processes and emits a click event when a list item is clicked.
|
|
692
|
+
* This extracts the relevant data from the event and passes it to parent components.
|
|
693
|
+
*
|
|
694
|
+
* @private
|
|
695
|
+
* @param {ListItemCustomEvent | RendererCustomEvent} event - The click event
|
|
696
|
+
* @returns {void}
|
|
697
|
+
*
|
|
698
|
+
* @memberOf ListComponent
|
|
699
|
+
*/
|
|
700
|
+
clickEventEmit(event) {
|
|
701
|
+
this.clickEvent.emit(event);
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* @description Refreshes the list data from the configured source.
|
|
705
|
+
* @summary This method handles both initial data loading and subsequent refresh operations,
|
|
706
|
+
* including pull-to-refresh and infinite scrolling. It manages the data fetching process,
|
|
707
|
+
* updates the component's state, and handles pagination or infinite scrolling logic based
|
|
708
|
+
* on the component's configuration.
|
|
709
|
+
*
|
|
710
|
+
* The method performs the following steps:
|
|
711
|
+
* 1. Sets the refreshing flag to indicate a data fetch is in progress
|
|
712
|
+
* 2. Calculates the appropriate start and limit values based on pagination settings
|
|
713
|
+
* 3. Fetches data from the appropriate source (model or request)
|
|
714
|
+
* 4. Updates the component's data and emits a refresh event
|
|
715
|
+
* 5. Handles pagination or infinite scrolling state updates
|
|
716
|
+
* 6. Completes any provided event (like InfiniteScrollCustomEvent)
|
|
717
|
+
*
|
|
718
|
+
* @param {InfiniteScrollCustomEvent | RefresherCustomEvent | boolean} event - The event that triggered the refresh,
|
|
719
|
+
* or a boolean flag indicating if this is a forced refresh
|
|
720
|
+
* @returns {Promise<void>} A promise that resolves when the refresh operation is complete
|
|
721
|
+
*
|
|
722
|
+
* @mermaid
|
|
723
|
+
* sequenceDiagram
|
|
724
|
+
* participant L as ListComponent
|
|
725
|
+
* participant D as Data Source
|
|
726
|
+
* participant E as Event System
|
|
727
|
+
*
|
|
728
|
+
* L->>L: refresh(event)
|
|
729
|
+
* L->>L: Set refreshing flag
|
|
730
|
+
* L->>L: Calculate start and limit
|
|
731
|
+
* alt Using model
|
|
732
|
+
* L->>D: getFromModel(force, start, limit)
|
|
733
|
+
* D-->>L: Return data
|
|
734
|
+
* else Using request
|
|
735
|
+
* L->>D: getFromRequest(force, start, limit)
|
|
736
|
+
* D-->>L: Return data
|
|
737
|
+
* end
|
|
738
|
+
* L->>E: refreshEventEmit()
|
|
739
|
+
* alt Infinite scrolling mode
|
|
740
|
+
* L->>L: Check if reached last page
|
|
741
|
+
* alt Last page reached
|
|
742
|
+
* L->>L: Complete scroll event
|
|
743
|
+
* L->>L: Disable loadMoreData
|
|
744
|
+
* else More pages available
|
|
745
|
+
* L->>L: Increment page number
|
|
746
|
+
* L->>L: Complete scroll event after delay
|
|
747
|
+
* end
|
|
748
|
+
* else Paginated mode
|
|
749
|
+
* L->>L: Clear refreshing flag after delay
|
|
750
|
+
* end
|
|
751
|
+
*
|
|
752
|
+
* @memberOf ListComponent
|
|
753
|
+
*/
|
|
754
|
+
async refresh(event = false) {
|
|
755
|
+
// if(typeof force !== 'boolean' && force.type === EventConstants.BACK_BUTTON_NAVIGATION) {
|
|
756
|
+
// const {refresh} = (force as CustomEvent).detail;
|
|
757
|
+
// if(!refresh)
|
|
758
|
+
// return false;
|
|
759
|
+
// }
|
|
760
|
+
this.refreshing = true;
|
|
761
|
+
const start = this.page > 1 ? (this.page - 1) * this.limit : this.start;
|
|
762
|
+
const limit = (this.page * (this.limit > 12 ? 12 : this.limit));
|
|
763
|
+
this.data = !this.model ?
|
|
764
|
+
await this.getFromRequest(!!event, start, limit)
|
|
765
|
+
: await this.getFromModel(!!event);
|
|
766
|
+
this.refreshEventEmit();
|
|
767
|
+
if (this.type === ListComponentsTypes.INFINITE) {
|
|
768
|
+
if (this.page === this.pages) {
|
|
769
|
+
if (event?.target)
|
|
770
|
+
event.target.complete();
|
|
771
|
+
this.loadMoreData = false;
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
this.page += 1;
|
|
775
|
+
this.refreshing = false;
|
|
776
|
+
setTimeout(() => {
|
|
777
|
+
if (event?.target && event?.type !== EventConstants.BACK_BUTTON_NAVIGATION)
|
|
778
|
+
event.target.complete();
|
|
779
|
+
}, 200);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
setTimeout(() => {
|
|
784
|
+
this.refreshing = false;
|
|
785
|
+
}, 200);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* @description Handles pagination events from the pagination component.
|
|
790
|
+
* @summary Processes pagination events by updating the current page number and
|
|
791
|
+
* refreshing the list data to display the selected page. This method is called
|
|
792
|
+
* when a user interacts with the pagination controls to navigate between pages.
|
|
793
|
+
*
|
|
794
|
+
* @param {PaginationCustomEvent} event - The pagination event containing page information
|
|
795
|
+
* @returns {void}
|
|
796
|
+
*
|
|
797
|
+
* @memberOf ListComponent
|
|
798
|
+
*/
|
|
799
|
+
handlePaginate(event) {
|
|
800
|
+
const { page } = event.data;
|
|
801
|
+
this.page = page;
|
|
802
|
+
this.refresh(true);
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* @description Handles pull-to-refresh events from the refresher component.
|
|
806
|
+
* @summary Processes refresh events triggered by the user pulling down on the list
|
|
807
|
+
* or by programmatic refresh requests. This method refreshes the list data and
|
|
808
|
+
* completes the refresher animation when the data is loaded.
|
|
809
|
+
*
|
|
810
|
+
* @param {InfiniteScrollCustomEvent | CustomEvent} [event] - The refresh event
|
|
811
|
+
* @returns {Promise<void>} A promise that resolves when the refresh operation is complete
|
|
812
|
+
*
|
|
813
|
+
* @memberOf ListComponent
|
|
814
|
+
*/
|
|
815
|
+
async handleRefresh(event) {
|
|
816
|
+
await this.refresh(event || true);
|
|
817
|
+
if (event instanceof CustomEvent)
|
|
818
|
+
setTimeout(() => {
|
|
819
|
+
// Any calls to load data go here
|
|
820
|
+
event.target.complete();
|
|
821
|
+
}, 400);
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* @description Filters data based on a search string.
|
|
825
|
+
* @summary Processes the current data array to find items that match the provided
|
|
826
|
+
* search string. This uses the arrayQueryByString utility to perform the filtering
|
|
827
|
+
* across all properties of the items.
|
|
828
|
+
*
|
|
829
|
+
* @param {KeyValue[]} results - The array of items to search through
|
|
830
|
+
* @param {string} search - The search string to filter by
|
|
831
|
+
* @returns {KeyValue[]} A promise that resolves to the filtered array of items
|
|
832
|
+
*
|
|
833
|
+
* @memberOf ListComponent
|
|
834
|
+
*/
|
|
835
|
+
parseSearchResults(results, search) {
|
|
836
|
+
return results.filter((item) => Object.values(item).some(value => value.toString().toLowerCase().includes(search?.toLowerCase())));
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* @description Fetches data from a request source.
|
|
840
|
+
* @summary Retrieves data from the configured source function or URL, processes it,
|
|
841
|
+
* and updates the component's data state. This method handles both initial data loading
|
|
842
|
+
* and subsequent refresh operations when using an external data source rather than a model.
|
|
843
|
+
*
|
|
844
|
+
* @param {boolean} force - Whether to force a refresh even if data already exists
|
|
845
|
+
* @param {number} start - The starting index for pagination
|
|
846
|
+
* @param {number} limit - The maximum number of items to retrieve
|
|
847
|
+
* @returns {Promise<KeyValue[]>} A promise that resolves to the fetched data
|
|
848
|
+
*
|
|
849
|
+
* @memberOf ListComponent
|
|
850
|
+
*/
|
|
851
|
+
async getFromRequest(force = false, start, limit) {
|
|
852
|
+
let request = [];
|
|
853
|
+
if (!this.data?.length || force || this.searchValue?.length || !!this.searchValue) {
|
|
854
|
+
// (self.data as ListItem[]) = [];
|
|
855
|
+
if (!this.searchValue?.length && !this.searchValue) {
|
|
856
|
+
if (!this.source && !this.data?.length) {
|
|
857
|
+
this.logger.info('No data and source passed to infinite list');
|
|
858
|
+
return [];
|
|
859
|
+
}
|
|
860
|
+
if (this.source instanceof Function)
|
|
861
|
+
request = await this.source();
|
|
862
|
+
if (!Array.isArray(request))
|
|
863
|
+
request = request?.['response']?.['data'] || request?.['results'] || [];
|
|
864
|
+
this.data = [...await this.parseResult(request)];
|
|
865
|
+
if (this.data?.length)
|
|
866
|
+
this.items = this.type === ListComponentsTypes.INFINITE ?
|
|
867
|
+
(this.items || []).concat([...this.data.slice(start, limit)]) : [...request.slice(start, limit)];
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
this.data = this.parseSearchResults(this.data, this.searchValue);
|
|
871
|
+
this.items = this.data;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (this.loadMoreData && this.type === ListComponentsTypes.PAGINATED)
|
|
875
|
+
this.getMoreData(this.data?.length || 0);
|
|
876
|
+
return this.data || [];
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* @description Fetches data from a model source.
|
|
880
|
+
* @summary Retrieves data from the configured model using its pagination or find methods,
|
|
881
|
+
* processes it, and updates the component's data state. This method handles both initial
|
|
882
|
+
* data loading and subsequent refresh operations when using a model as the data source.
|
|
883
|
+
*
|
|
884
|
+
* @param {boolean} force - Whether to force a refresh even if data already exists
|
|
885
|
+
* @param {number} start - The starting index for pagination
|
|
886
|
+
* @param {number} limit - The maximum number of items to retrieve
|
|
887
|
+
* @returns {Promise<KeyValue[]>} A promise that resolves to the fetched data
|
|
888
|
+
*
|
|
889
|
+
* @memberOf ListComponent
|
|
890
|
+
*/
|
|
891
|
+
async getFromModel(force = false) {
|
|
892
|
+
let data = [...this.data || []];
|
|
893
|
+
let request = [];
|
|
894
|
+
// getting model repository
|
|
895
|
+
if (!this._repository)
|
|
896
|
+
this._repository = this.repository;
|
|
897
|
+
const repo = this._repository;
|
|
898
|
+
if (!this.data?.length || force || this.searchValue?.length || !!this.searchValue) {
|
|
899
|
+
try {
|
|
900
|
+
if (!this.searchValue?.length && !this.searchValue) {
|
|
901
|
+
this.data = [];
|
|
902
|
+
// const rawQuery = this.parseQuery(self.model as Repository<Model>, start, limit);
|
|
903
|
+
// request = this.parseResult(await (this.model as any)?.paginate(start, limit));
|
|
904
|
+
if (!this.paginator) {
|
|
905
|
+
this.paginator = await repo
|
|
906
|
+
.select()
|
|
907
|
+
.orderBy([this.pk, this.sortDirection])
|
|
908
|
+
.paginate(this.limit);
|
|
909
|
+
}
|
|
910
|
+
request = await this.parseResult(this.paginator);
|
|
911
|
+
}
|
|
912
|
+
else {
|
|
913
|
+
if (!this.indexes)
|
|
914
|
+
this.indexes = (Object.values(this.mapper) || [this.pk]);
|
|
915
|
+
const condition = this.parseConditions(this.searchValue);
|
|
916
|
+
request = await this.parseResult(await repo.query(condition, (this.sortBy || this.pk), this.sortDirection));
|
|
917
|
+
data = [];
|
|
918
|
+
}
|
|
919
|
+
data = this.type === ListComponentsTypes.INFINITE ? [...(data).concat(request)] : [...request];
|
|
920
|
+
}
|
|
921
|
+
catch (error) {
|
|
922
|
+
this.logger.error(error?.message || `Unable to find ${this.model} on registry. Return empty array from component`);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
if (data?.length) {
|
|
926
|
+
if (this.searchValue) {
|
|
927
|
+
this.items = [...data];
|
|
928
|
+
if (this.items?.length <= this.limit)
|
|
929
|
+
this.loadMoreData = false;
|
|
930
|
+
}
|
|
931
|
+
else {
|
|
932
|
+
this.items = [...data];
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (this.type === ListComponentsTypes.PAGINATED && this.paginator)
|
|
936
|
+
this.getMoreData(this.paginator.total);
|
|
937
|
+
return data || [];
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* @description Converts search values or filter queries into database conditions.
|
|
941
|
+
* @summary Transforms search input or complex filter queries into Condition objects
|
|
942
|
+
* that can be used for database querying. Handles both simple string/number searches
|
|
943
|
+
* across indexed fields and complex filter queries with multiple criteria.
|
|
944
|
+
*
|
|
945
|
+
* For simple searches (string/number):
|
|
946
|
+
* - Creates conditions that search across all indexed fields
|
|
947
|
+
* - Uses equality for numeric values and regex for string values
|
|
948
|
+
* - Combines conditions with OR logic to search multiple fields
|
|
949
|
+
*
|
|
950
|
+
* For complex filter queries:
|
|
951
|
+
* - Processes each filter item with its specific condition type
|
|
952
|
+
* - Supports Equal, Not Equal, Contains, Not Contains, Greater Than, Less Than
|
|
953
|
+
* - Updates sort configuration based on the filter query
|
|
954
|
+
* - Combines multiple filter conditions with OR logic
|
|
955
|
+
*
|
|
956
|
+
* @param {string | number | IFilterQuery} value - The search value or filter query object
|
|
957
|
+
* @returns {Condition<Model>} A Condition object for database querying
|
|
958
|
+
* @memberOf ListComponent
|
|
959
|
+
*/
|
|
960
|
+
parseConditions(value) {
|
|
961
|
+
let _condition;
|
|
962
|
+
if (typeof value === Primitives.STRING || typeof value === Primitives.NUMBER) {
|
|
963
|
+
_condition = Condition.attribute(this.pk).eq(!isNaN(value) ? Number(value) : value);
|
|
964
|
+
for (const index of this.indexes) {
|
|
965
|
+
if (index === this.pk)
|
|
966
|
+
continue;
|
|
967
|
+
let orCondition;
|
|
968
|
+
if (!isNaN(value)) {
|
|
969
|
+
orCondition = Condition.attribute(index).eq(Number(value));
|
|
970
|
+
}
|
|
971
|
+
else {
|
|
972
|
+
orCondition = Condition.attribute(index).regexp(value);
|
|
973
|
+
}
|
|
974
|
+
_condition = _condition.or(orCondition);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
const { query, sort } = value;
|
|
979
|
+
_condition = Condition.attribute(this.pk).dif('null');
|
|
980
|
+
if (query?.length)
|
|
981
|
+
_condition = undefined;
|
|
982
|
+
(query || []).forEach((item) => {
|
|
983
|
+
const { value, condition, index } = item;
|
|
984
|
+
let val = value;
|
|
985
|
+
if (index === this.pk || !isNaN(val))
|
|
986
|
+
val = Number(val);
|
|
987
|
+
let orCondition;
|
|
988
|
+
switch (condition) {
|
|
989
|
+
case "Equal":
|
|
990
|
+
orCondition = Condition.attribute(index).eq(val);
|
|
991
|
+
break;
|
|
992
|
+
case "Not Equal":
|
|
993
|
+
orCondition = Condition.attribute(index).dif(val);
|
|
994
|
+
break;
|
|
995
|
+
case "Not Contains":
|
|
996
|
+
orCondition = !Condition.attribute(index).regexp(new RegExp(`^(?!.*${val}).*$`));
|
|
997
|
+
break;
|
|
998
|
+
case "Contains":
|
|
999
|
+
orCondition = Condition.attribute(index).regexp(val);
|
|
1000
|
+
break;
|
|
1001
|
+
case "Greater Than":
|
|
1002
|
+
orCondition = Condition.attribute(index).gte(val);
|
|
1003
|
+
break;
|
|
1004
|
+
case "Less Than":
|
|
1005
|
+
orCondition = Condition.attribute(index).lte(val);
|
|
1006
|
+
break;
|
|
1007
|
+
}
|
|
1008
|
+
_condition = (!_condition ?
|
|
1009
|
+
orCondition : _condition.and(orCondition));
|
|
1010
|
+
});
|
|
1011
|
+
this.sortBy = sort?.value || this.pk;
|
|
1012
|
+
this.sortDirection = sort?.direction || this.sortDirection;
|
|
1013
|
+
}
|
|
1014
|
+
console.log(_condition);
|
|
1015
|
+
return _condition;
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* @description Processes query results into a standardized format.
|
|
1019
|
+
* @summary Handles different result formats from various data sources, extracting
|
|
1020
|
+
* pagination information when available and applying any configured data mapping.
|
|
1021
|
+
* This ensures consistent data structure regardless of the source.
|
|
1022
|
+
*
|
|
1023
|
+
* @protected
|
|
1024
|
+
* @param {KeyValue[] | Paginator} result - The raw query result
|
|
1025
|
+
* @returns {KeyValue[]} The processed array of items
|
|
1026
|
+
*
|
|
1027
|
+
* @memberOf ListComponent
|
|
1028
|
+
*/
|
|
1029
|
+
async parseResult(result) {
|
|
1030
|
+
if (!Array.isArray(result) && ('page' in result && 'total' in result)) {
|
|
1031
|
+
const paginator = result;
|
|
1032
|
+
result = await paginator.page(this.page);
|
|
1033
|
+
// TODO: Chage for result.total;
|
|
1034
|
+
this.getMoreData(paginator.total);
|
|
1035
|
+
}
|
|
1036
|
+
else {
|
|
1037
|
+
this.getMoreData(result?.length || 0);
|
|
1038
|
+
}
|
|
1039
|
+
return (Object.keys(this.mapper || {}).length) ?
|
|
1040
|
+
this.mapResults(result) : result;
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* @description Updates pagination state based on data length.
|
|
1044
|
+
* @summary Calculates whether more data is available and how many pages exist
|
|
1045
|
+
* based on the total number of items and the configured limit per page.
|
|
1046
|
+
* This information is used to control pagination UI and infinite scrolling behavior.
|
|
1047
|
+
*
|
|
1048
|
+
* @param {number} length - The total number of items available
|
|
1049
|
+
* @returns {void}
|
|
1050
|
+
*
|
|
1051
|
+
* @memberOf ListComponent
|
|
1052
|
+
*/
|
|
1053
|
+
getMoreData(length) {
|
|
1054
|
+
if (this.type === ListComponentsTypes.INFINITE) {
|
|
1055
|
+
if (this.paginator)
|
|
1056
|
+
length = length * this.limit;
|
|
1057
|
+
if (length <= this.limit) {
|
|
1058
|
+
this.loadMoreData = false;
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
this.pages = Math.floor(length / this.limit);
|
|
1062
|
+
if ((this.pages * this.limit) < length)
|
|
1063
|
+
this.pages += 1;
|
|
1064
|
+
if (this.pages === 1)
|
|
1065
|
+
this.loadMoreData = false;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
else {
|
|
1069
|
+
this.pages = length;
|
|
1070
|
+
if (this.pages === 1)
|
|
1071
|
+
this.loadMoreData = false;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* @description Maps a single item using the configured mapper.
|
|
1076
|
+
* @summary Transforms a data item according to the mapping configuration,
|
|
1077
|
+
* extracting nested properties and formatting values as needed. This allows
|
|
1078
|
+
* the component to display data in a format different from how it's stored.
|
|
1079
|
+
*
|
|
1080
|
+
* @protected
|
|
1081
|
+
* @param {KeyValue} item - The item to map
|
|
1082
|
+
* @param {KeyValue} mapper - The mapping configuration
|
|
1083
|
+
* @param {KeyValue} [props] - Additional properties to include
|
|
1084
|
+
* @returns {KeyValue} The mapped item
|
|
1085
|
+
*
|
|
1086
|
+
* @memberOf ListComponent
|
|
1087
|
+
*/
|
|
1088
|
+
itemMapper(item, mapper, props) {
|
|
1089
|
+
return Object.entries(mapper).reduce((accum, [key, value]) => {
|
|
1090
|
+
const arrayValue = value.split('.');
|
|
1091
|
+
if (!value) {
|
|
1092
|
+
accum[key] = value;
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
if (arrayValue.length === 1) {
|
|
1096
|
+
value = item?.[value] || value;
|
|
1097
|
+
if (isValidDate(value))
|
|
1098
|
+
value = `${formatDate(value)}`;
|
|
1099
|
+
accum[key] = value;
|
|
1100
|
+
}
|
|
1101
|
+
else {
|
|
1102
|
+
let val;
|
|
1103
|
+
for (const _value of arrayValue)
|
|
1104
|
+
val = !val
|
|
1105
|
+
? item[_value]
|
|
1106
|
+
: (typeof val === 'string' ? JSON.parse(val) : val)[_value];
|
|
1107
|
+
if (isValidDate(new Date(val)))
|
|
1108
|
+
val = `${formatDate(val)}`;
|
|
1109
|
+
accum[key] = val === null || val === undefined ? value : val;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
return Object.assign({}, props || {}, accum);
|
|
1113
|
+
}, {});
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* @description Maps all result items using the configured mapper.
|
|
1117
|
+
* @summary Applies the itemMapper to each item in the result set, adding
|
|
1118
|
+
* common properties like operations and route information. This transforms
|
|
1119
|
+
* the raw data into the format expected by the list item components.
|
|
1120
|
+
*
|
|
1121
|
+
* @param {KeyValue[]} data - The array of items to map
|
|
1122
|
+
* @returns {KeyValue[]} The array of mapped items
|
|
1123
|
+
*
|
|
1124
|
+
* @memberOf ListComponent
|
|
1125
|
+
*/
|
|
1126
|
+
mapResults(data) {
|
|
1127
|
+
if (!data || !data.length)
|
|
1128
|
+
return [];
|
|
1129
|
+
// passing uid as prop to mapper
|
|
1130
|
+
this.mapper = { ...this.mapper, ...{ uid: this.pk } };
|
|
1131
|
+
const props = Object.assign({
|
|
1132
|
+
operations: this.operations,
|
|
1133
|
+
route: this.route,
|
|
1134
|
+
...Object.keys(this.item).reduce((acc, key) => {
|
|
1135
|
+
acc[key] = this.item[key];
|
|
1136
|
+
return acc;
|
|
1137
|
+
}, {}),
|
|
1138
|
+
// ... (!this.item.render ? {} : Object.keys(this.item).reduce((acc: KeyValue, key: string) => {
|
|
1139
|
+
// acc[key] = this.item[key as keyof IListItemProp];
|
|
1140
|
+
// return acc;
|
|
1141
|
+
// }, {}))
|
|
1142
|
+
});
|
|
1143
|
+
return data.reduce((accum, curr) => {
|
|
1144
|
+
accum.push({ ...this.itemMapper(curr, this.mapper, props), ...{ pk: this.pk } });
|
|
1145
|
+
return accum;
|
|
1146
|
+
}, []);
|
|
1147
|
+
}
|
|
1148
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1149
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: ListComponent, isStandalone: true, selector: "ngx-decaf-list", inputs: { type: "type", translatable: "translatable", showSearchbar: "showSearchbar", data: "data", source: "source", start: "start", limit: "limit", loadMoreData: "loadMoreData", lines: "lines", inset: "inset", scrollThreshold: "scrollThreshold", scrollPosition: "scrollPosition", loadingText: "loadingText", showRefresher: "showRefresher", loadingSpinner: "loadingSpinner", enableFilter: "enableFilter", sortDirection: "sortDirection", sortBy: "sortBy", disableSort: "disableSort", emptyIcon: "emptyIcon", empty: "empty" }, outputs: { refreshEvent: "refreshEvent", clickEvent: "clickEvent" }, host: { listeners: { "window:ListItemClickEvent": "handleClick($event)", "window:searchbarEvent": "handleSearch($event)", "window:BackButtonNavigationEndEvent": "refresh($event)" } }, usesInheritance: true, ngImport: i0, template: "\n@if(showRefresher) {\n <ion-refresher slot=\"fixed\" [pullFactor]=\"1\" [pullMin]=\"100\" [pullMax]=\"200\" (ionRefresh)=\"handleRefresh($event)\">\n <ion-refresher-content />\n </ion-refresher>\n}\n\n@if(showSearchbar) {\n @if(model && enableFilter) {\n <ngx-decaf-filter\n [model]=\"model\"\n [sortDirection]=\"sortDirection\"\n [disableSort]=\"disableSort\"\n (filterEvent)=\"handleFilter($event)\"\n (searchEvent)=\"handleSearch($event)\"\n />\n } @else {\n <ngx-decaf-searchbar [emitEventToWindow]=\"false\" [debounce]=\"500\" (searchEvent)=\"handleSearch($event)\" />\n }\n}\n\n@if(data?.length) {\n <ion-list [inset]=\"inset\" [lines]=\"lines\" #component>\n @if(item?.tag) {\n @for(child of items; track trackItemFn($index, child)) {\n <ngx-decaf-component-renderer\n [tag]=\"item.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]='{\n item: child,\n mapper: mapper,\n route: route\n }'>\n </ngx-decaf-component-renderer>\n }\n } @else {\n <ng-content></ng-content>\n }\n </ion-list>\n\n @if(loadMoreData) {\n @if(pages > 0 && type === 'paginated' && !searchValue?.length) {\n <ngx-decaf-pagination\n [totalPages]=\"pages\"\n [current]=\"page\"\n (clickEvent)=\"handlePaginate($event)\"\n />\n\n } @else {\n <ion-infinite-scroll\n [class]=\"searchValue?.length ? 'dcf-hidden' : ''\"\n [position]=\"scrollPosition\"\n [threshold]=\"scrollThreshold\"\n (ionInfinite)=\"handleRefresh($event)\">\n <ion-infinite-scroll-content [loadingSpinner]=\"loadingSpinner\" [loadingText]=\"loadingText\" />\n </ion-infinite-scroll>\n }\n }\n} @else {\n @if(refreshing) {\n <ion-item *ngFor=\"let skl of skeletonData\">\n <ion-thumbnail slot=\"start\">\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n </ion-thumbnail>\n <ion-label>\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n <ion-text class=\"date\" style=\"width: 20%;\"><ion-skeleton-text [animated]=\"true\"></ion-skeleton-text></ion-text>\n </ion-label>\n </ion-item>\n } @else {\n @if(!searchValue?.length) {\n <ngx-decaf-empty-state\n [title]=\"(locale + '.'+ empty.title) | translate\"\n [subtitle]=\"(locale + '.'+ empty.subtitle) | translate\"\n [buttonText]=\"empty.showButton ? (locale + '.'+ empty.button | translate) : ''\"\n [buttonLink]=\"empty.showButton ? empty.route : ''\"\n />\n } @else {\n <ngx-decaf-empty-state\n icon=\"search-outline\"\n ngClass=\"empty-search\"\n [translatable]=\"true\"\n title=\"search.title\"\n subtitle=\"search.subtitle\"\n [searchValue]=\"searchValue\"\n />\n }\n }\n}\n\n", styles: ["ion-infinite-scroll-content ion-spinner{--color: var(--ion-color-primary)}@media (max-width: 768px){#end,[slot=end]{display:none!important}}\n"], dependencies: [{ kind: "ngmodule", type: ForAngularModule }, { kind: "component", type: i1.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "component", type: IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: PaginationComponent, selector: "ngx-decaf-pagination", inputs: ["totalPages", "current"], outputs: ["clickEvent"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonThumbnail, selector: "ion-thumbnail" }, { kind: "component", type: IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { kind: "component", type: IonInfiniteScroll, selector: "ion-infinite-scroll", inputs: ["disabled", "position", "threshold"] }, { kind: "component", type: IonInfiniteScrollContent, selector: "ion-infinite-scroll-content", inputs: ["loadingSpinner", "loadingText"] }, { kind: "component", type: SearchbarComponent, selector: "ngx-decaf-searchbar", inputs: ["autocomplete", "autocorrect", "animated", "buttonCancelText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value", "queryKeys", "isVisible", "wrapper", "wrapperColor", "emitEventToWindow"], outputs: ["searchEvent"] }, { kind: "component", type: EmptyStateComponent, selector: "ngx-decaf-empty-state", inputs: ["title", "titleColor", "subtitle", "subtitleColor", "showIcon", "icon", "iconSize", "iconColor", "buttonLink", "buttonText", "buttonFill", "buttonColor", "buttonSize", "searchValue"] }, { kind: "component", type: FilterComponent, selector: "ngx-decaf-filter", inputs: ["indexes", "conditions", "sortBy", "disableSort"], outputs: ["filterEvent", "searchEvent"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "model", "parent"], outputs: ["listenEvent"] }] }); }
|
|
1150
|
+
};
|
|
1151
|
+
ListComponent = __decorate([
|
|
1152
|
+
Dynamic(),
|
|
1153
|
+
__metadata("design:paramtypes", [])
|
|
1154
|
+
], ListComponent);
|
|
1155
|
+
export { ListComponent };
|
|
1156
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ListComponent, decorators: [{
|
|
1157
|
+
type: Component,
|
|
1158
|
+
args: [{ selector: 'ngx-decaf-list', standalone: true, imports: [
|
|
1159
|
+
ForAngularModule,
|
|
1160
|
+
IonRefresher,
|
|
1161
|
+
IonLoading,
|
|
1162
|
+
PaginationComponent,
|
|
1163
|
+
IonList,
|
|
1164
|
+
IonItem,
|
|
1165
|
+
IonThumbnail,
|
|
1166
|
+
IonSkeletonText,
|
|
1167
|
+
IonLabel,
|
|
1168
|
+
IonText,
|
|
1169
|
+
IonRefresherContent,
|
|
1170
|
+
IonInfiniteScroll,
|
|
1171
|
+
IonInfiniteScrollContent,
|
|
1172
|
+
IonThumbnail,
|
|
1173
|
+
IonSkeletonText,
|
|
1174
|
+
SearchbarComponent,
|
|
1175
|
+
EmptyStateComponent,
|
|
1176
|
+
ListItemComponent,
|
|
1177
|
+
FilterComponent,
|
|
1178
|
+
ComponentRendererComponent
|
|
1179
|
+
], template: "\n@if(showRefresher) {\n <ion-refresher slot=\"fixed\" [pullFactor]=\"1\" [pullMin]=\"100\" [pullMax]=\"200\" (ionRefresh)=\"handleRefresh($event)\">\n <ion-refresher-content />\n </ion-refresher>\n}\n\n@if(showSearchbar) {\n @if(model && enableFilter) {\n <ngx-decaf-filter\n [model]=\"model\"\n [sortDirection]=\"sortDirection\"\n [disableSort]=\"disableSort\"\n (filterEvent)=\"handleFilter($event)\"\n (searchEvent)=\"handleSearch($event)\"\n />\n } @else {\n <ngx-decaf-searchbar [emitEventToWindow]=\"false\" [debounce]=\"500\" (searchEvent)=\"handleSearch($event)\" />\n }\n}\n\n@if(data?.length) {\n <ion-list [inset]=\"inset\" [lines]=\"lines\" #component>\n @if(item?.tag) {\n @for(child of items; track trackItemFn($index, child)) {\n <ngx-decaf-component-renderer\n [tag]=\"item.tag\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]='{\n item: child,\n mapper: mapper,\n route: route\n }'>\n </ngx-decaf-component-renderer>\n }\n } @else {\n <ng-content></ng-content>\n }\n </ion-list>\n\n @if(loadMoreData) {\n @if(pages > 0 && type === 'paginated' && !searchValue?.length) {\n <ngx-decaf-pagination\n [totalPages]=\"pages\"\n [current]=\"page\"\n (clickEvent)=\"handlePaginate($event)\"\n />\n\n } @else {\n <ion-infinite-scroll\n [class]=\"searchValue?.length ? 'dcf-hidden' : ''\"\n [position]=\"scrollPosition\"\n [threshold]=\"scrollThreshold\"\n (ionInfinite)=\"handleRefresh($event)\">\n <ion-infinite-scroll-content [loadingSpinner]=\"loadingSpinner\" [loadingText]=\"loadingText\" />\n </ion-infinite-scroll>\n }\n }\n} @else {\n @if(refreshing) {\n <ion-item *ngFor=\"let skl of skeletonData\">\n <ion-thumbnail slot=\"start\">\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n </ion-thumbnail>\n <ion-label>\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n <ion-text class=\"date\" style=\"width: 20%;\"><ion-skeleton-text [animated]=\"true\"></ion-skeleton-text></ion-text>\n </ion-label>\n </ion-item>\n } @else {\n @if(!searchValue?.length) {\n <ngx-decaf-empty-state\n [title]=\"(locale + '.'+ empty.title) | translate\"\n [subtitle]=\"(locale + '.'+ empty.subtitle) | translate\"\n [buttonText]=\"empty.showButton ? (locale + '.'+ empty.button | translate) : ''\"\n [buttonLink]=\"empty.showButton ? empty.route : ''\"\n />\n } @else {\n <ngx-decaf-empty-state\n icon=\"search-outline\"\n ngClass=\"empty-search\"\n [translatable]=\"true\"\n title=\"search.title\"\n subtitle=\"search.subtitle\"\n [searchValue]=\"searchValue\"\n />\n }\n }\n}\n\n", styles: ["ion-infinite-scroll-content ion-spinner{--color: var(--ion-color-primary)}@media (max-width: 768px){#end,[slot=end]{display:none!important}}\n"] }]
|
|
1180
|
+
}], ctorParameters: () => [], propDecorators: { type: [{
|
|
1181
|
+
type: Input
|
|
1182
|
+
}], translatable: [{
|
|
1183
|
+
type: Input
|
|
1184
|
+
}], showSearchbar: [{
|
|
1185
|
+
type: Input
|
|
1186
|
+
}], data: [{
|
|
1187
|
+
type: Input
|
|
1188
|
+
}], source: [{
|
|
1189
|
+
type: Input
|
|
1190
|
+
}], start: [{
|
|
1191
|
+
type: Input
|
|
1192
|
+
}], limit: [{
|
|
1193
|
+
type: Input
|
|
1194
|
+
}], loadMoreData: [{
|
|
1195
|
+
type: Input
|
|
1196
|
+
}], lines: [{
|
|
1197
|
+
type: Input
|
|
1198
|
+
}], inset: [{
|
|
1199
|
+
type: Input
|
|
1200
|
+
}], scrollThreshold: [{
|
|
1201
|
+
type: Input
|
|
1202
|
+
}], scrollPosition: [{
|
|
1203
|
+
type: Input
|
|
1204
|
+
}], loadingText: [{
|
|
1205
|
+
type: Input
|
|
1206
|
+
}], showRefresher: [{
|
|
1207
|
+
type: Input
|
|
1208
|
+
}], loadingSpinner: [{
|
|
1209
|
+
type: Input
|
|
1210
|
+
}], enableFilter: [{
|
|
1211
|
+
type: Input
|
|
1212
|
+
}], sortDirection: [{
|
|
1213
|
+
type: Input
|
|
1214
|
+
}], sortBy: [{
|
|
1215
|
+
type: Input
|
|
1216
|
+
}], disableSort: [{
|
|
1217
|
+
type: Input
|
|
1218
|
+
}], emptyIcon: [{
|
|
1219
|
+
type: Input
|
|
1220
|
+
}], empty: [{
|
|
1221
|
+
type: Input
|
|
1222
|
+
}], refreshEvent: [{
|
|
1223
|
+
type: Output
|
|
1224
|
+
}], clickEvent: [{
|
|
1225
|
+
type: Output
|
|
1226
|
+
}], handleClick: [{
|
|
1227
|
+
type: HostListener,
|
|
1228
|
+
args: ['window:ListItemClickEvent', ['$event']]
|
|
1229
|
+
}], handleSearch: [{
|
|
1230
|
+
type: HostListener,
|
|
1231
|
+
args: ['window:searchbarEvent', ['$event']]
|
|
1232
|
+
}], refresh: [{
|
|
1233
|
+
type: HostListener,
|
|
1234
|
+
args: ['window:BackButtonNavigationEndEvent', ['$event']]
|
|
1235
|
+
}] } });
|
|
1236
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlzdC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvbGlzdC9saXN0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9saXN0L2xpc3QuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQVUsWUFBWSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFjLE1BQU0sZUFBZSxDQUFDO0FBRXpHLE9BQU8sRUFDTCxpQkFBaUIsRUFDakIsd0JBQXdCLEVBQ3hCLE9BQU8sRUFDUCxRQUFRLEVBQ1IsT0FBTyxFQUNQLFlBQVksRUFDWixtQkFBbUIsRUFDbkIsZUFBZSxFQUNmLE9BQU8sRUFDUCxZQUFZLEVBQ1osVUFBVSxFQUNYLE1BQU0sMkJBQTJCLENBQUM7QUFDbkMsT0FBTyxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDN0MsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3hELE9BQU8sRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDbkUsT0FBTyxFQUFFLFNBQVMsRUFBWSxjQUFjLEVBQWEsTUFBTSxnQkFBZ0IsQ0FBQztBQUNoRixPQUFPLEVBRUwsT0FBTyxFQUNQLGNBQWMsRUFDZCxrQkFBa0IsRUFLbkIsTUFBTSxjQUFjLENBQUM7QUFDdEIsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDakUsT0FBTyxFQUNMLGVBQWUsRUFDZixVQUFVLEVBQ1YsV0FBVyxFQUNaLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGtDQUFrQyxD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c0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBPbkluaXQsIEV2ZW50RW1pdHRlciwgT3V0cHV0LCBJbnB1dCwgSG9zdExpc3RlbmVyLCBPbkRlc3Ryb3kgIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBJbmZpbml0ZVNjcm9sbEN1c3RvbUV2ZW50LCBSZWZyZXNoZXJDdXN0b21FdmVudCwgU3Bpbm5lclR5cGVzIH0gZnJvbSAnQGlvbmljL2FuZ3VsYXInO1xuaW1wb3J0IHtcbiAgSW9uSW5maW5pdGVTY3JvbGwsXG4gIElvbkluZmluaXRlU2Nyb2xsQ29udGVudCxcbiAgSW9uSXRlbSxcbiAgSW9uTGFiZWwsXG4gIElvbkxpc3QsXG4gIElvblJlZnJlc2hlcixcbiAgSW9uUmVmcmVzaGVyQ29udGVudCxcbiAgSW9uU2tlbGV0b25UZXh0LFxuICBJb25UZXh0LFxuICBJb25UaHVtYm5haWwsXG4gIElvbkxvYWRpbmdcbn0gZnJvbSAnQGlvbmljL2FuZ3VsYXIvc3RhbmRhbG9uZSc7XG5pbXBvcnQgeyBkZWJvdW5jZVRpbWUsIFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IE9wZXJhdGlvbktleXMgfSBmcm9tICdAZGVjYWYtdHMvZGItZGVjb3JhdG9ycyc7XG5pbXBvcnQgeyBNb2RlbCwgUHJpbWl0aXZlcyB9IGZyb20gJ0BkZWNhZi10cy9kZWNvcmF0b3ItdmFsaWRhdGlvbic7XG5pbXBvcnQgeyBDb25kaXRpb24sIE9ic2VydmVyLCBPcmRlckRpcmVjdGlvbiwgUGFnaW5hdG9yIH0gZnJvbSAnQGRlY2FmLXRzL2NvcmUnO1xuaW1wb3J0IHtcbiAgQmFzZUN1c3RvbUV2ZW50LFxuICBEeW5hbWljLFxuICBFdmVudENvbnN0YW50cyxcbiAgQ29tcG9uZW50c1RhZ05hbWVzLFxuICBSZW5kZXJlckN1c3RvbUV2ZW50LFxuICBTdHJpbmdPckJvb2xlYW4sXG4gIEtleVZhbHVlLFxuICBMaXN0SXRlbUN1c3RvbUV2ZW50XG59IGZyb20gJy4uLy4uL2VuZ2luZSc7XG5pbXBvcnQgeyBGb3JBbmd1bGFyTW9kdWxlIH0gZnJvbSAnLi4vLi4vZm9yLWFuZ3VsYXIubW9kdWxlJztcbmltcG9ydCB7IE5neEJhc2VDb21wb25lbnQgfSBmcm9tICcuLi8uLi9lbmdpbmUvTmd4QmFzZUNvbXBvbmVudCc7XG5pbXBvcnQge1xuICBzdHJpbmdUb0Jvb2xlYW4sXG4gIGZvcm1hdERhdGUsXG4gIGlzVmFsaWREYXRlXG59IGZyb20gJy4uLy4uL2hlbHBlcnMnO1xuaW1wb3J0IHsgU2VhcmNoYmFyQ29tcG9uZW50IH0gZnJvbSAnLi4vc2VhcmNoYmFyL3NlYXJjaGJhci5jb21wb25lbnQnO1xuaW1wb3J0IHsgRW1wdHlTdGF0ZUNvbXBvbmVudCB9IGZyb20gJy4uL2VtcHR5LXN0YXRlL2VtcHR5LXN0YXRlLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBMaXN0SXRlbUNvbXBvbmVudCB9IGZyb20gJy4uL2xpc3QtaXRlbS9saXN0LWl0ZW0uY29tcG9uZW50JztcbmltcG9ydCB7IENvbXBvbmVudFJlbmRlcmVyQ29tcG9uZW50IH0gZnJvbSAnLi4vY29tcG9uZW50LXJlbmRlcmVyL2NvbXBvbmVudC1yZW5kZXJlci5jb21wb25lbnQnO1xuaW1wb3J0IHsgUGFnaW5hdGlvbkNvbXBvbmVudCB9IGZyb20gJy4uL3BhZ2luYXRpb24vcGFnaW5hdGlvbi5jb21wb25lbnQnO1xuaW1wb3J0IHsgUGFnaW5hdGlvbkN1c3RvbUV2ZW50IH0gZnJvbSAnLi4vcGFnaW5hdGlvbi9jb25zdGFudHMnO1xuaW1wb3J0IHsgSUxpc3RFbXB0eVJlc3VsdCwgTGlzdENvbXBvbmVudHNUeXBlcywgRGVjYWZSZXBvc2l0b3J5IH0gZnJvbSAnLi9jb25zdGFudHMnO1xuaW1wb3J0IHsgRnVuY3Rpb25MaWtlLCBJRmlsdGVyUXVlcnksIElGaWx0ZXJRdWVyeUl0ZW0gfSBmcm9tICcuLi8uLi9lbmdpbmUvdHlwZXMnO1xuaW1wb3J0IHsgRmlsdGVyQ29tcG9uZW50IH0gZnJvbSAnLi4vZmlsdGVyL2ZpbHRlci5jb21wb25lbnQnO1xuXG4vKipcbiAqIEBkZXNjcmlwdGlvbiBBIHZlcnNhdGlsZSBsaXN0IGNvbXBvbmVudCB0aGF0IHN1cHBvcnRzIHZhcmlvdXMgZGF0YSBkaXNwbGF5IG1vZGVzLlxuICogQHN1bW1hcnkgVGhpcyBjb21wb25lbnQgcHJvdmlkZXMgYSBmbGV4aWJsZSB3YXkgdG8gZGlzcGxheSBsaXN0cyBvZiBkYXRhIHdpdGggc3VwcG9ydFxuICogZm9yIGluZmluaXRlIHNjcm9sbGluZywgcGFnaW5hdGlvbiwgc2VhcmNoaW5nLCBhbmQgY3VzdG9tIGl0ZW0gcmVuZGVyaW5nLiBJdCBjYW4gZmV0Y2hcbiAqIGRhdGEgZnJvbSB2YXJpb3VzIHNvdXJjZXMgaW5jbHVkaW5nIG1vZGVscywgZnVuY3Rpb25zLCBvciBkaXJlY3QgZGF0YSBpbnB1dC5cbiAqXG4gKiBUaGUgY29tcG9uZW50IHN1cHBvcnRzIHR3byBtYWluIGRpc3BsYXkgdHlwZXM6XG4gKiAxLiBJbmZpbml0ZSBzY3JvbGxpbmcgLSBMb2FkcyBtb3JlIGRhdGEgYXMgdGhlIHVzZXIgc2Nyb2xsc1xuICogMi4gUGFnaW5hdGlvbiAtIERpc3BsYXlzIGRhdGEgaW4gcGFnZXMgd2l0aCBuYXZpZ2F0aW9uIGNvbnRyb2xzXG4gKlxuICogQWRkaXRpb25hbCBmZWF0dXJlcyBpbmNsdWRlOlxuICogLSBQdWxsLXRvLXJlZnJlc2ggZnVuY3Rpb25hbGl0eVxuICogLSBTZWFyY2ggZmlsdGVyaW5nXG4gKiAtIEVtcHR5IHN0YXRlIGN1c3RvbWl6YXRpb25cbiAqIC0gQ3VzdG9tIGl0ZW0gcmVuZGVyaW5nXG4gKiAtIEV2ZW50IGVtaXNzaW9uIGZvciBpbnRlcmFjdGlvbnNcbiAqXG4gKiBAbWVybWFpZFxuICogc2VxdWVuY2VEaWFncmFtXG4gKiAgIHBhcnRpY2lwYW50IFUgYXMgVXNlclxuICogICBwYXJ0aWNpcGFudCBMIGFzIExpc3RDb21wb25lbnRcbiAqICAgcGFydGljaXBhbnQgRCBhcyBEYXRhIFNvdXJjZVxuICogICBwYXJ0aWNpcGFudCBFIGFzIEV4dGVybmFsIENvbXBvbmVudHNcbiAqXG4gKiAgIFUtPj5MOiBJbml0aWFsaXplIGNvbXBvbmVudFxuICogICBMLT4+TDogbmdPbkluaXQoKVxuICogICBMLT4+RDogUmVxdWVzdCBpbml0aWFsIGRhdGFcbiAqICAgRC0tPj5MOiBSZXR1cm4gZGF0YVxuICogICBMLT4+TDogUHJvY2VzcyBhbmQgZGlzcGxheSBkYXRhXG4gKlxuICogICBhbHQgVXNlciBzY3JvbGxzIChJbmZpbml0ZSBtb2RlKVxuICogICAgIFUtPj5MOiBTY3JvbGwgdG8gYm90dG9tXG4gKiAgICAgTC0+PkQ6IFJlcXVlc3QgbW9yZSBkYXRhXG4gKiAgICAgRC0tPj5MOiBSZXR1cm4gYWRkaXRpb25hbCBkYXRhXG4gKiAgICAgTC0+Pkw6IEFwcGVuZCB0byBleGlzdGluZyBkYXRhXG4gKiAgIGVsc2UgVXNlciBjaGFuZ2VzIHBhZ2UgKFBhZ2luYXRlZCBtb2RlKVxuICogICAgIFUtPj5MOiBDbGljayBwYWdlIG51bWJlclxuICogICAgIEwtPj5MOiBoYW5kbGVQYWdpbmF0ZSgpXG4gKiAgICAgTC0+PkQ6IFJlcXVlc3QgZGF0YSBmb3IgcGFnZVxuICogICAgIEQtLT4+TDogUmV0dXJuIHBhZ2UgZGF0YVxuICogICAgIEwtPj5MOiBSZXBsYWNlIGRpc3BsYXllZCBkYXRhXG4gKiAgIGVuZFxuICpcbiAqICAgYWx0IFVzZXIgc2VhcmNoZXNcbiAqICAgICBVLT4+TDogRW50ZXIgc2VhcmNoIHRlcm1cbiAqICAgICBMLT4+TDogaGFuZGxlU2VhcmNoKClcbiAqICAgICBMLT4+RDogRmlsdGVyIGRhdGEgYnkgc2VhcmNoIHRlcm1cbiAqICAgICBELS0+Pkw6IFJldHVybiBmaWx0ZXJlZCBkYXRhXG4gKiAgICAgTC0+Pkw6IFVwZGF0ZSBkaXNwbGF5ZWQgZGF0YVxuICogICBlbmRcbiAqXG4gKiAgIGFsdCBVc2VyIGNsaWNrcyBpdGVtXG4gKiAgICAgVS0+Pkw6IENsaWNrIGxpc3QgaXRlbVxuICogICAgIEwtPj5MOiBoYW5kbGVDbGljaygpXG4gKiAgICAgTC0+PkU6IEVtaXQgY2xpY2tFdmVudFxuICogICBlbmRcbiAqXG4gKiBAZXhhbXBsZVxuICogPG5neC1kZWNhZi1saXN0XG4gKiAgIFtzb3VyY2VdPVwiZGF0YVNvdXJjZVwiXG4gKiAgIFtsaW1pdF09XCIxMFwiXG4gKiAgIFt0eXBlXT1cIidpbmZpbml0ZSdcIlxuICogICBbc2hvd1NlYXJjaGJhcl09XCJ0cnVlXCJcbiAqICAgKGNsaWNrRXZlbnQpPVwiaGFuZGxlSXRlbUNsaWNrKCRldmVudClcIlxuICogICAocmVmcmVzaEV2ZW50KT1cImhhbmRsZVJlZnJlc2goJGV2ZW50KVwiPlxuICogPC9uZ3gtZGVjYWYtbGlzdD5cbiAqXG4gKiBAZXh0ZW5kcyB7Tmd4QmFzZUNvbXBvbmVudH1cbiAqIEBpbXBsZW1lbnRzIHtPbkluaXR9XG4gKi9cbkBEeW5hbWljKClcbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ25neC1kZWNhZi1saXN0JyxcbiAgdGVtcGxhdGVVcmw6ICcuL2xpc3QuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9saXN0LmNvbXBvbmVudC5zY3NzJ10sXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtcbiAgICBGb3JBbmd1bGFyTW9kdWxlLFxuICAgIElvblJlZnJlc2hlcixcbiAgICBJb25Mb2FkaW5nLFxuICAgIFBhZ2luYXRpb25Db21wb25lbnQsXG4gICAgSW9uTGlzdCxcbiAgICBJb25JdGVtLFxuICAgIElvblRodW1ibmFpbCxcbiAgICBJb25Ta2VsZXRvblRleHQsXG4gICAgSW9uTGFiZWwsXG4gICAgSW9uVGV4dCxcbiAgICBJb25SZWZyZXNoZXJDb250ZW50LFxuICAgIElvbkluZmluaXRlU2Nyb2xsLFxuICAgIElvbkluZmluaXRlU2Nyb2xsQ29udGVudCxcbiAgICBJb25UaHVtYm5haWwsXG4gICAgSW9uU2tlbGV0b25UZXh0LFxuICAgIFNlYXJjaGJhckNvbXBvbmVudCxcbiAgICBFbXB0eVN0YXRlQ29tcG9uZW50LFxuICAgIExpc3RJdGVtQ29tcG9uZW50LFxuICAgIEZpbHRlckNvbXBvbmVudCxcbiAgICBDb21wb25lbnRSZW5kZXJlckNvbXBvbmVudFxuICBdXG59KVxuZXhwb3J0IGNsYXNzIExpc3RDb21wb25lbnQgZXh0ZW5kcyBOZ3hCYXNlQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gVGhlIGRpc3BsYXkgbW9kZSBmb3IgdGhlIGxpc3QgY29tcG9uZW50LlxuICAgKiBAc3VtbWFyeSBEZXRlcm1pbmVzIGhvdyB0aGUgbGlzdCBkYXRhIGlzIGxvYWRlZCBhbmQgZGlzcGxheWVkLiBPcHRpb25zIGluY2x1ZGU6XG4gICAqIC0gSU5GSU5JVEU6IExvYWRzIG1vcmUgZGF0YSBhcyB0aGUgdXNlciBzY3JvbGxzIChpbmZpbml0ZSBzY3JvbGxpbmcpXG4gICAqIC0gUEFHSU5BVEVEOiBEaXNwbGF5cyBkYXRhIGluIHBhZ2VzIHdpdGggbmF2aWdhdGlvbiBjb250cm9sc1xuICAgKlxuICAgKiBAdHlwZSB7TGlzdENvbXBvbmVudHNUeXBlc31cbiAgICogQGRlZmF1bHQgTGlzdENvbXBvbmVudHNUeXBlcy5JTkZJTklURVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgQElucHV0KClcbiAgdHlwZTogTGlzdENvbXBvbmVudHNUeXBlcyA9IExpc3RDb21wb25lbnRzVHlwZXMuSU5GSU5JVEU7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDb250cm9scyB3aGV0aGVyIHRoZSBjb21wb25lbnQgdXNlcyB0cmFuc2xhdGlvbiBzZXJ2aWNlcy5cbiAgICogQHN1bW1hcnkgV2hlbiBzZXQgdG8gdHJ1ZSwgdGhlIGNvbXBvbmVudCB3aWxsIGF0dGVtcHQgdG8gdXNlIHRyYW5zbGF0aW9uIHNlcnZpY2VzXG4gICAqIGZvciBhbnkgdGV4dCBjb250ZW50LiBUaGlzIGFsbG93cyBmb3IgaW50ZXJuYXRpb25hbGl6YXRpb24gb2YgdGhlIGxpc3QgY29tcG9uZW50LlxuICAgKlxuICAgKiBAdHlwZSB7U3RyaW5nT3JCb29sZWFufVxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoKVxuICBvdmVycmlkZSB0cmFuc2xhdGFibGU6IFN0cmluZ09yQm9vbGVhbiA9IHRydWU7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDb250cm9scyB0aGUgdmlzaWJpbGl0eSBvZiB0aGUgc2VhcmNoIGJhci5cbiAgICogQHN1bW1hcnkgV2hlbiBzZXQgdG8gdHJ1ZSwgZGlzcGxheXMgYSBzZWFyY2ggYmFyIGF0IHRoZSB0b3Agb2YgdGhlIGxpc3QgdGhhdCBhbGxvd3NcbiAgICogdXNlcnMgdG8gZmlsdGVyIHRoZSBsaXN0IGl0ZW1zLiBUaGUgc2VhcmNoIGZ1bmN0aW9uYWxpdHkgd29ya3MgYnkgZmlsdGVyaW5nIHRoZVxuICAgKiBleGlzdGluZyBkYXRhIG9yIGJ5IHRyaWdnZXJpbmcgYSBuZXcgZGF0YSBmZXRjaCB3aXRoIHNlYXJjaCBwYXJhbWV0ZXJzLlxuICAgKlxuICAgKiBAdHlwZSB7U3RyaW5nT3JCb29sZWFufVxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoKVxuICBzaG93U2VhcmNoYmFyOiBTdHJpbmdPckJvb2xlYW4gPSB0cnVlO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gRGlyZWN0IGRhdGEgaW5wdXQgZm9yIHRoZSBsaXN0IGNvbXBvbmVudC5cbiAgICogQHN1bW1hcnkgUHJvdmlkZXMgYSB3YXkgdG8gZGlyZWN0bHkgcGFzcyBkYXRhIHRvIHRoZSBsaXN0IGNvbXBvbmVudCBpbnN0ZWFkIG9mXG4gICAqIGZldGNoaW5nIGl0IGZyb20gYSBzb3VyY2UuIFdoZW4gYm90aCBkYXRhIGFuZCBzb3VyY2UgYXJlIHByb3ZpZGVkLCB0aGUgY29tcG9uZW50XG4gICAqIHdpbGwgdXNlIHRoZSBzb3VyY2UgdG8gZmV0Y2ggZGF0YSBvbmx5IGlmIHRoZSBkYXRhIGFycmF5IGlzIGVtcHR5LlxuICAgKlxuICAgKiBAdHlwZSB7S2V5VmFsdWVbXSB8IHVuZGVmaW5lZH1cbiAgICogQGRlZmF1bHQgdW5kZWZpbmVkXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoKVxuICBkYXRhPzogS2V5VmFsdWVbXSB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFRoZSBkYXRhIHNvdXJjZSBmb3IgdGhlIGxpc3QgY29tcG9uZW50LlxuICAgKiBAc3VtbWFyeSBTcGVjaWZpZXMgd2hlcmUgdGhlIGxpc3Qgc2hvdWxkIGZldGNoIGl0cyBkYXRhIGZyb20uIFRoaXMgY2FuIGJlIGVpdGhlcjpcbiAgICogLSBBIHN0cmluZyBVUkwgb3IgZW5kcG9pbnQgaWRlbnRpZmllclxuICAgKiAtIEEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGRhdGEgd2hlbiBjYWxsZWRcbiAgICogVGhlIGNvbXBvbmVudCB3aWxsIGNhbGwgdGhpcyBzb3VyY2Ugd2hlbiBpdCBuZWVkcyB0byBsb2FkIG9yIHJlZnJlc2ggZGF0YS5cbiAgICpcbiAgICogQHR5cGUge3N0cmluZyB8IEZ1bmN0aW9uTGlrZX1cbiAgICogQHJlcXVpcmVkXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoKVxuICBzb3VyY2UhOiBzdHJpbmcgfCBGdW5jdGlvbkxpa2U7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBUaGUgc3RhcnRpbmcgaW5kZXggZm9yIGRhdGEgZmV0Y2hpbmcuXG4gICAqIEBzdW1tYXJ5IFNwZWNpZmllcyB0aGUgaW5kZXggZnJvbSB3aGljaCB0byBzdGFydCBmZXRjaGluZyBkYXRhLiBUaGlzIGlzIHVzZWRcbiAgICogZm9yIHBhZ2luYXRpb24gYW5kIGluZmluaXRlIHNjcm9sbGluZyB0byBkZXRlcm1pbmUgd2hpY2ggc3Vic2V0IG9mIGRhdGEgdG8gbG9hZC5cbiAgICpcbiAgICogQHR5cGUge251bWJlcn1cbiAgICogQGRlZmF1bHQgMFxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgQElucHV0KClcbiAgc3RhcnQ6IG51bWJlciA9IDA7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBUaGUgbnVtYmVyIG9mIGl0ZW1zIHRvIGZldGNoIHBlciBwYWdlIG9yIGxvYWQgb3BlcmF0aW9uLlxuICAgKiBAc3VtbWFyeSBEZXRlcm1pbmVzIGhvdyBtYW55IGl0ZW1zIGFyZSBsb2FkZWQgYXQgb25jZSBkdXJpbmcgcGFnaW5hdGlvbiBvclxuICAgKiBpbmZpbml0ZSBzY3JvbGxpbmcuIFRoaXMgYWZmZWN0cyB0aGUgc2l6ZSBvZiBkYXRhIGNodW5rcyByZXF1ZXN0ZWQgZnJvbSB0aGUgc291cmNlLlxuICAgKlxuICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgKiBAZGVmYXVsdCAxMFxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgQElucHV0KClcbiAgbGltaXQ6IG51bWJlciA9IDEwO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ29udHJvbHMgd2hldGhlciBtb3JlIGRhdGEgY2FuIGJlIGxvYWRlZC5cbiAgICogQHN1bW1hcnkgV2hlbiBzZXQgdG8gdHJ1ZSwgdGhlIGNvbXBvbmVudCB3aWxsIGFsbG93IGxvYWRpbmcgYWRkaXRpb25hbCBkYXRhXG4gICAqIHRocm91Z2ggaW5maW5pdGUgc2Nyb2xsaW5nIG9yIHBhZ2luYXRpb24uIFdoZW4gZmFsc2UsIHRoZSBjb21wb25lbnQgd2lsbCBub3RcbiAgICogYXR0ZW1wdCB0byBsb2FkIG1vcmUgZGF0YSBiZXlvbmQgd2hhdCBpcyBpbml0aWFsbHkgZGlzcGxheWVkLlxuICAgKlxuICAgKiBAdHlwZSB7U3RyaW5nT3JCb29sZWFufVxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoKVxuICBsb2FkTW9yZURhdGE6IFN0cmluZ09yQm9vbGVhbiA9IHRydWVcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFRoZSBzdHlsZSBvZiBkaXZpZGluZyBsaW5lcyBiZXR3ZWVuIGxpc3QgaXRlbXMuXG4gICAqIEBzdW1tYXJ5IERldGVybWluZXMgaG93IGRpdmlkaW5nIGxpbmVzIGFwcGVhciBiZXR3ZWVuIGxpc3QgaXRlbXMuIE9wdGlvbnMgaW5jbHVkZTpcbiAgICogLSBcImluc2V0XCI6IExpbmVzIGFyZSBpbnNldCBmcm9tIHRoZSBlZGdlc1xuICAgKiAtIFwiZnVsbFwiOiBMaW5lcyBleHRlbmQgdGhlIGZ1bGwgd2lkdGhcbiAgICogLSBcIm5vbmVcIjogTm8gZGl2aWRpbmcgbGluZXNcbiAgICpcbiAgICogQHR5cGUge1wiaW5zZXRcIiB8IFwiZnVsbFwiIHwgXCJub25lXCJ9XG4gICAqIEBkZWZhdWx0IFwiZnVsbFwiXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoKVxuICBsaW5lczogXCJpbnNldFwiIHwgXCJmdWxsXCIgfCBcIm5vbmVcIiA9IFwiZnVsbFwiO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ29udHJvbHMgd2hldGhlciB0aGUgbGlzdCBoYXMgaW5zZXQgc3R5bGluZy5cbiAgICogQHN1bW1hcnkgV2hlbiBzZXQgdG8gdHJ1ZSwgdGhlIGxpc3Qgd2lsbCBoYXZlIGluc2V0IHN0eWxpbmcgd2l0aCByb3VuZGVkIGNvcm5lcnNcbiAgICogYW5kIG1hcmdpbiBhcm91bmQgdGhlIGVkZ2VzLiBUaGlzIGNyZWF0ZXMgYSBjYXJkLWxpa2UgYXBwZWFyYW5jZSBmb3IgdGhlIGxpc3QuXG4gICAqXG4gICAqIEB0eXBlIHtTdHJpbmdPckJvb2xlYW59XG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoKVxuICBpbnNldDogU3RyaW5nT3JCb29sZWFuID0gZmFsc2U7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBUaGUgdGhyZXNob2xkIGZvciB0cmlnZ2VyaW5nIGluZmluaXRlIHNjcm9sbCBsb2FkaW5nLlxuICAgKiBAc3VtbWFyeSBTcGVjaWZpZXMgaG93IGNsb3NlIHRvIHRoZSBib3R0b20gb2YgdGhlIGxpc3QgdGhlIHVzZXIgbXVzdCBzY3JvbGxcbiAgICogYmVmb3JlIHRoZSBjb21wb25lbnQgdHJpZ2dlcnMgbG9hZGluZyBvZiBhZGRpdGlvbmFsIGRhdGEuIFRoaXMgaXMgZXhwcmVzc2VkXG4gICAqIGFzIGEgcGVyY2VudGFnZSBvZiB0aGUgbGlzdCBoZWlnaHQuXG4gICAqXG4gICAqIEB0eXBlIHtzdHJpbmd9XG4gICAqIEBkZWZhdWx0IFwiMTUlXCJcbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIEBJbnB1dCgpXG4gIHNjcm9sbFRocmVzaG9sZDogc3RyaW5nID0gXCIxNSVcIjtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFRoZSBwb3NpdGlvbiB3aGVyZSBuZXcgaXRlbXMgYXJlIGFkZGVkIGR1cmluZyBpbmZpbml0ZSBzY3JvbGxpbmcuXG4gICAqIEBzdW1tYXJ5IERldGVybWluZXMgd2hldGhlciBuZXcgaXRlbXMgYXJlIGFkZGVkIHRvIHRoZSB0b3Agb3IgYm90dG9tIG9mIHRoZSBsaXN0XG4gICAqIHdoZW4gbG9hZGluZyBtb3JlIGRhdGEgdGhyb3VnaCBpbmZpbml0ZSBzY3JvbGxpbmcuXG4gICAqXG4gICAqIEB0eXBlIHtcImJvdHRvbVwiIHwgXCJ0b3BcIn1cbiAgICogQGRlZmF1bHQgXCJib3R0b21cIlxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgQElucHV0KClcbiAgc2Nyb2xsUG9zaXRpb246IFwiYm90dG9tXCIgfCBcInRvcFwiID0gXCJib3R0b21cIjtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEN1c3RvbSB0ZXh0IHRvIGRpc3BsYXkgZHVyaW5nIGxvYWRpbmcgb3BlcmF0aW9ucy5cbiAgICogQHN1bW1hcnkgU3BlY2lmaWVzIHRoZSB0ZXh0IHNob3duIGluIHRoZSBsb2FkaW5nIGluZGljYXRvciB3aGVuIHRoZSBjb21wb25lbnRcbiAgICogaXMgZmV0Y2hpbmcgZGF0YS4gSWYgbm90IHByb3ZpZGVkLCBhIGRlZmF1bHQgbG9hZGluZyBtZXNzYWdlIHdpbGwgYmUgdXNlZC5cbiAgICpcbiAgICogQHR5cGUge3N0cmluZyB8IHVuZGVmaW5lZH1cbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIEBJbnB1dCgpXG4gIGxvYWRpbmdUZXh0Pzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ29udHJvbHMgdGhlIHZpc2liaWxpdHkgb2YgdGhlIHB1bGwtdG8tcmVmcmVzaCBmZWF0dXJlLlxuICAgKiBAc3VtbWFyeSBXaGVuIHNldCB0byB0cnVlLCBlbmFibGVzIHRoZSBwdWxsLXRvLXJlZnJlc2ggZnVuY3Rpb25hbGl0eSB0aGF0IGFsbG93c1xuICAgKiB1c2VycyB0byByZWZyZXNoIHRoZSBsaXN0IGRhdGEgYnkgcHVsbGluZyBkb3duIGZyb20gdGhlIHRvcCBvZiB0aGUgbGlzdC5cbiAgICpcbiAgICogQHR5cGUge1N0cmluZ09yQm9vbGVhbn1cbiAgICogQGRlZmF1bHQgdHJ1ZVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgQElucHV0KClcbiAgc2hvd1JlZnJlc2hlcjogU3RyaW5nT3JCb29sZWFuID0gdHJ1ZTtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFRoZSB0eXBlIG9mIHNwaW5uZXIgdG8gZGlzcGxheSBkdXJpbmcgbG9hZGluZyBvcGVyYXRpb25zLlxuICAgKiBAc3VtbWFyeSBTcGVjaWZpZXMgdGhlIHZpc3VhbCBzdHlsZSBvZiB0aGUgbG9hZGluZyBzcGlubmVyIHNob3duIGR1cmluZyBkYXRhXG4gICAqIGZldGNoaW5nIG9wZXJhdGlvbnMuIFVzZXMgSW9uaWMncyBwcmVkZWZpbmVkIHNwaW5uZXIgdHlwZXMuXG4gICAqXG4gICAqIEB0eXBlIHtTcGlubmVyVHlwZXN9XG4gICAqIEBkZWZhdWx0IFwiY2lyY3VsYXJcIlxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgQElucHV0KClcbiAgbG9hZGluZ1NwaW5uZXI6IFNwaW5uZXJUeXBlcyA9IFwiY2lyY3VsYXJcIjtcblxuICAvLyAvKipcbiAgLy8gICogQGRlc2NyaXB0aW9uIFF1ZXJ5IHBhcmFtZXRlcnMgZm9yIGRhdGEgZmV0Y2hpbmcuXG4gIC8vICAqIEBzdW1tYXJ5IFNwZWNpZmllcyBhZGRpdGlvbmFsIHF1ZXJ5IHBhcmFtZXRlcnMgdG8gdXNlIHdoZW4gZmV0Y2hpbmcgZGF0YSBmcm9tXG4gIC8vICAqIHRoZSBzb3VyY2UuIFRoaXMgY2FuIGJlIHByb3ZpZGVkIGFzIGEgc3RyaW5nIChKU09OKSBvciBhIGRpcmVjdCBvYmplY3QuXG4gIC8vICAqXG4gIC8vICAqIEB0eXBlIHtzdHJpbmcgfCBLZXlWYWx1ZSB8IHVuZGVmaW5lZH1cbiAgLy8gICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgLy8gICovXG4gIC8vIEBJbnB1dCgpXG4gIC8vIHF1ZXJ5Pzogc3RyaW5nIHwgS2V5VmFsdWU7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDb250cm9scyB3aGV0aGVyIHRoZSBmaWx0ZXJpbmcgZnVuY3Rpb25hbGl0eSBpcyBlbmFibGVkLlxuICAgKiBAc3VtbWFyeSBXaGVuIHNldCB0byB0cnVlLCBlbmFibGVzIHRoZSBmaWx0ZXIgY29tcG9uZW50IHRoYXQgYWxsb3dzIHVzZXJzIHRvIGNyZWF0ZVxuICAgKiBjb21wbGV4IHNlYXJjaCBjcml0ZXJpYSB3aXRoIG11bHRpcGxlIGZpZWxkIGZpbHRlcnMsIGNvbmRpdGlvbnMsIGFuZCB2YWx1ZXMuXG4gICAqIFdoZW4gZmFsc2UsIGRpc2FibGVzIHRoZSBmaWx0ZXIgaW50ZXJmYWNlIGVudGlyZWx5LlxuICAgKlxuICAgKiBAdHlwZSB7U3RyaW5nT3JCb29sZWFufVxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoKVxuICBlbmFibGVGaWx0ZXI6IFN0cmluZ09yQm9vbGVhbiA9IHRydWU7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBTb3J0aW5nIHBhcmFtZXRlcnMgZm9yIGRhdGEgZmV0Y2hpbmcuXG4gICAqIEBzdW1tYXJ5IFNwZWNpZmllcyBob3cgdGhlIGZldGNoZWQgZGF0YSBzaG91bGQgYmUgc29ydGVkLiBUaGlzIGNhbiBiZSBwcm92aWRlZFxuICAgKiBhcyBhIHN0cmluZyAoZmllbGQgbmFtZSB3aXRoIG9wdGlvbmFsIGRpcmVjdGlvbikgb3IgYSBkaXJlY3Qgb2JqZWN0LlxuICAgKlxuICAgKiBAdHlwZSB7c3RyaW5nIHwgS2V5VmFsdWUgfCB1bmRlZmluZWR9XG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBASW5wdXQoKVxuICBzb3J0RGlyZWN0aW9uOiBPcmRlckRpcmVjdGlvbiA9IE9yZGVyRGlyZWN0aW9uLkRTQztcblxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gU29ydGluZyBwYXJhbWV0ZXJzIGZvciBkYXRhIGZldGNoaW5nLlxuICAgKiBAc3VtbWFyeSBTcGVjaWZpZXMgaG93IHRoZSBmZXRjaGVkIGRhdGEgc2hvdWxkIGJlIHNvcnRlZC4gVGhpcyBjYW4gYmUgcHJvdmlkZWRcbiAgICogYXMgYSBzdHJpbmcgKGZpZWxkIG5hbWUgd2l0aCBvcHRpb25hbCBkaXJlY3Rpb24pIG9yIGEgZGlyZWN0IG9iamVjdC5cbiAgICpcbiAgICogQHR5cGUge3N0cmluZyB8IEtleVZhbHVlIHwgdW5kZWZpbmVkfVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgQElucHV0KClcbiAgc29ydEJ5ITogc3RyaW5nO1xuXG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDb250cm9scyB3aGV0aGVyIHNvcnRpbmcgZnVuY3Rpb25hbGl0eSBpcyBkaXNhYmxlZC5cbiAgICogQHN1bW1hcnkgV2hlbiBzZXQgdG8gdHJ1ZSwgZGlzYWJsZXMgdGhlIHNvcnQgY29udHJvbHMgYW5kIHByZXZlbnRzIHVzZXJzIGZyb21cbiAgICogY2hhbmdpbmcgdGhlIHNvcnQgb3JkZXIgb3IgZmllbGQuIFRoZSBsaXN0IHdpbGwgbWFpbnRhaW4gaXRzIGRlZmF1bHQgb3JcbiAgICogcHJvZ3JhbW1hdGljYWxseSBzZXQgc29ydCBjb25maWd1cmF0aW9uIHdpdGhvdXQgdXNlciBpbnRlcmFjdGlvbi5cbiAgICpcbiAgICogQHR5cGUge1N0cmluZ09yQm9vbGVhbn1cbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIEBJbnB1dCgpXG4gIGRpc2FibGVTb3J0OiBTdHJpbmdPckJvb2xlYW4gPSBmYWxzZTtcblxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSWNvbiB0byBkaXNwbGF5IHdoZW4gdGhlIGxpc3QgaXMgZW1wdHkuXG4gICAqIEBzdW1tYXJ5IFNwZWNpZmllcyB0aGUgaWNvbiBzaG93biBpbiB0aGUgZW1wdHkgc3RhdGUgd2hlbiBubyBkYXRhIGlzIGF2YWlsYWJsZS5cbiAgICogVGhpcyBjYW4gYmUgYW55IGljb24gbmFtZSBzdXBwb3J0ZWQgYnkgdGhlIGFwcGxpY2F0aW9uJ3MgaWNvbiBzeXN0ZW0uXG4gICAqXG4gICAqIEB0eXBlIHtzdHJpbmcgfCB1bmRlZmluZWR9XG4gICAqIEBkZWZhdWx0ICd0aS1kYXRhYmFzZS1leGNsYW1hdGlvbidcbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIEBJbnB1dCgpXG4gIGVtcHR5SWNvbj86IHN0cmluZyA9ICd0aS1kYXRhYmFzZS1leGNsYW1hdGlvbic7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDb25maWd1cmF0aW9uIGZvciB0aGUgZW1wdHkgc3RhdGUgZGlzcGxheS5cbiAgICogQHN1bW1hcnkgQ3VzdG9taXplcyBob3cgdGhlIGVtcHR5IHN0YXRlIGlzIGRpc3BsYXllZCB3aGVuIG5vIGRhdGEgaXMgYXZhaWxhYmxlLlxuICAgKiBUaGlzIGluY2x1ZGVzIHRoZSB0aXRsZSwgc3VidGl0bGUsIGJ1dHRvbiB0ZXh0LCBpY29uLCBhbmQgbmF2aWdhdGlvbiBsaW5rLlxuICAgKlxuICAgKiBAdHlwZSB7UGFydGlhbDxJTGlzdEVtcHR5UmVzdWx0Pn1cbiAgICogQGRlZmF1bHQge1xuICAgKiAgIHRpdGxlOiAnZW1wdHkudGl0bGUnLFxuICAgKiAgIHN1YnRpdGxlOiAnZW1wdHkuc3VidGl0bGUnLFxuICAgKiAgIHNob3dCdXR0b246IGZhbHNlLFxuICAgKiAgIGljb246ICdhbGVydC1jaXJjbGUtb3V0bGluZScsXG4gICAqICAgYnV0dG9uVGV4dDogJ2xvY2FsZS5lbXB0eS5idXR0b24nLFxuICAgKiAgIGxpbms6ICcnXG4gICAqIH1cbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIEBJbnB1dCgpXG4gIGVtcHR5OiBQYXJ0aWFsPElMaXN0RW1wdHlSZXN1bHQ+ID0ge1xuICAgIHRpdGxlOiAnZW1wdHkudGl0bGUnLFxuICAgIHN1YnRpdGxlOiAnZW1wdHkuc3VidGl0bGUnLFxuICAgIHNob3dCdXR0b246IGZhbHNlLFxuICAgIGljb246ICdhbGVydC1jaXJjbGUtb3V0bGluZScsXG4gICAgYnV0dG9uVGV4dDogJ2xvY2FsZS5lbXB0eS5idXR0b24nLFxuICAgIGxpbms6ICcnXG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFRoZSBjdXJyZW50IHBhZ2UgbnVtYmVyIGluIHBhZ2luYXRlZCBtb2RlLlxuICAgKiBAc3VtbWFyeSBUcmFja3Mgd2hpY2ggcGFnZSBpcyBjdXJyZW50bHkgYmVpbmcgZGlzcGxheWVkIHdoZW4gdGhlIGNvbXBvbmVudFxuICAgKiBpcyBpbiBwYWdpbmF0ZWQgbW9kZS4gVGhpcyBpcyB1c2VkIGZvciBwYWdpbmF0aW9uIGNvbnRyb2xzIGFuZCBkYXRhIGZldGNoaW5nLlxuICAgKlxuICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgKiBAZGVmYXVsdCAxXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBwYWdlOiBudW1iZXIgPSAxO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gVGhlIHRvdGFsIG51bWJlciBvZiBwYWdlcyBhdmFpbGFibGUuXG4gICAqIEBzdW1tYXJ5IFN0b3JlcyB0aGUgY2FsY3VsYXRlZCB0b3RhbCBudW1iZXIgb2YgcGFnZXMgYmFzZWQgb24gdGhlIGRhdGEgc2l6ZVxuICAgKiBhbmQgbGltaXQuIFRoaXMgaXMgdXNlZCBmb3IgcGFnaW5hdGlvbiBjb250cm9scyBhbmQgYm91bmRhcnkgY2hlY2tpbmcuXG4gICAqXG4gICAqIEB0eXBlIHtudW1iZXJ9XG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBwYWdlcyE6IG51bWJlcjtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEluZGljYXRlcyB3aGV0aGVyIGEgcmVmcmVzaCBvcGVyYXRpb24gaXMgaW4gcHJvZ3Jlc3MuXG4gICAqIEBzdW1tYXJ5IFdoZW4gdHJ1ZSwgdGhlIGNvbXBvbmVudCBpcyBjdXJyZW50bHkgZmV0Y2hpbmcgbmV3IGRhdGEuIFRoaXMgaXMgdXNlZFxuICAgKiB0byBjb250cm9sIGxvYWRpbmcgaW5kaWNhdG9ycyBhbmQgcHJldmVudCBkdXBsaWNhdGUgcmVmcmVzaCBvcGVyYXRpb25zIGZyb21cbiAgICogYmVpbmcgdHJpZ2dlcmVkIHNpbXVsdGFuZW91c2x5LlxuICAgKlxuICAgKiBAdHlwZSB7Ym9vbGVhbn1cbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIHJlZnJlc2hpbmc6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEFycmF5IHVzZWQgZm9yIHJlbmRlcmluZyBza2VsZXRvbiBsb2FkaW5nIHBsYWNlaG9sZGVycy5cbiAgICogQHN1bW1hcnkgQ29udGFpbnMgcGxhY2Vob2xkZXIgaXRlbXMgdGhhdCBhcmUgZGlzcGxheWVkIGR1cmluZyBkYXRhIGxvYWRpbmcuXG4gICAqIFRoZSBsZW5ndGggb2YgdGhpcyBhcnJheSBkZXRlcm1pbmVzIGhvdyBtYW55IHNrZWxldG9uIGl0ZW1zIGFyZSBzaG93bi5cbiAgICpcbiAgICogQHR5cGUge3N0cmluZ1tdfVxuICAgKiBAZGVmYXVsdCBuZXcgQXJyYXkoMilcbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIHNrZWxldG9uRGF0YTogc3RyaW5nW10gPSBuZXcgQXJyYXkoMik7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBUaGUgcHJvY2Vzc2VkIGxpc3QgaXRlbXMgcmVhZHkgZm9yIGRpc3BsYXkuXG4gICAqIEBzdW1tYXJ5IFN0b3JlcyB0aGUgY3VycmVudCBzZXQgb2YgaXRlbXMgYmVpbmcgZGlzcGxheWVkIGluIHRoZSBsaXN0IGFmdGVyXG4gICAqIHByb2Nlc3NpbmcgZnJvbSB0aGUgcmF3IGRhdGEgc291cmNlLiBUaGlzIG1heSBiZSBhIHN1YnNldCBvZiB0aGUgZnVsbCBkYXRhXG4gICAqIHdoZW4gdXNpbmcgcGFnaW5hdGlvbiBvciBpbmZpbml0ZSBzY3JvbGxpbmcuXG4gICAqXG4gICAqIEB0eXBlIHtLZXlWYWx1ZVtdfVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgaXRlbXMhOiBLZXlWYWx1ZVtdO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gVGhlIGN1cnJlbnQgc2VhcmNoIHF1ZXJ5IHZhbHVlLlxuICAgKiBAc3VtbWFyeSBTdG9yZXMgdGhlIHRleHQgZW50ZXJlZCBpbiB0aGUgc2VhcmNoIGJhci4gVGhpcyBpcyB1c2VkIHRvIGZpbHRlclxuICAgKiB0aGUgbGlzdCBkYXRhIG9yIHRvIHNlbmQgYXMgYSBzZWFyY2ggcGFyYW1ldGVyIHdoZW4gZmV0Y2hpbmcgbmV3IGRhdGEuXG4gICAqXG4gICAqIEB0eXBlIHtzdHJpbmcgfCB1bmRlZmluZWR9XG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBzZWFyY2hWYWx1ZT86IHN0cmluZyB8IElGaWx0ZXJRdWVyeSB8IHVuZGVmaW5lZDtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEEgcGFnaW5hdG9yIG9iamVjdCBmb3IgaGFuZGxpbmcgcGFnaW5hdGlvbiBvcGVyYXRpb25zLlxuICAgKiBAc3VtbWFyeSBQcm92aWRlcyBhIHBhZ2luYXRvciBvYmplY3QgdGhhdCBjYW4gYmUgdXNlZCB0byByZXRyaWV2ZSBhbmQgbmF2aWdhdGVcbiAgICogdGhyb3VnaCBkYXRhIGluIGNodW5rcywgcmVkdWNpbmcgbWVtb3J5IHVzYWdlIGFuZCBpbXByb3ZpbmcgcGVyZm9ybWFuY2UuXG4gICAqXG4gICAqIFRoZSBwYWdpbmF0b3Igb2JqZWN0IGlzIGluaXRpYWxpemVkIGluIHRoZSBgbmdPbkluaXRgIGxpZmVjeWNsZSBob29rIGFuZCBpc1xuICAgKiB1c2VkIHRvIGZldGNoIGFuZCBkaXNwbGF5IGRhdGEgaW4gdGhlIHBhZ2luYXRpb24gY29tcG9uZW50LiBJdCBpcyBhbiBpbnN0YW5jZVxuICAgKiBvZiB0aGUgYFBhZ2luYXRvcmAgY2xhc3MgZnJvbSB0aGUgYEBkZWNhZi10cy9jb3JlYCBwYWNrYWdlLCB3aGljaCBwcm92aWRlc1xuICAgKiBtZXRob2RzIGZvciBxdWVyeWluZyBhbmQgbWFuaXB1bGF0aW5nIHBhZ2luYXRlZCBkYXRhLlxuICAgKlxuICAgKiBAdHlwZSB7UGFnaW5hdG9yPE1vZGVsPn1cbiAgICogQG1lbWJlck9mIFBhZ2luYXRpb25Db21wb25lbnRcbiAgICovXG4gIHBhZ2luYXRvciE6IFBhZ2luYXRvcjxNb2RlbD4gfCB1bmRlZmluZWQ7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBUaGUgbGFzdCBwYWdlIG51bWJlciB0aGF0IHdhcyBkaXNwbGF5ZWQuXG4gICAqIEBzdW1tYXJ5IEtlZXBzIHRyYWNrIG9mIHRoZSBwcmV2aW91c2x5IGRpc3BsYXllZCBwYWdlIG51bWJlciwgd2hpY2ggaXMgdXNlZnVsXG4gICAqIGZvciBoYW5kbGluZyBuYXZpZ2F0aW9uIGFuZCBzZWFyY2ggb3BlcmF0aW9ucyBpbiBwYWdpbmF0ZWQgbW9kZS5cbiAgICpcbiAgICogQHR5cGUge251bWJlcn1cbiAgICogQGRlZmF1bHQgMVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgbGFzdFBhZ2U6IG51bWJlciA9IDFcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEV2ZW50IGVtaXR0ZXIgZm9yIHJlZnJlc2ggb3BlcmF0aW9ucy5cbiAgICogQHN1bW1hcnkgRW1pdHMgYW4gZXZlbnQgd2hlbiB0aGUgbGlzdCBkYXRhIGlzIHJlZnJlc2hlZCwgZWl0aGVyIHRocm91Z2ggcHVsbC10by1yZWZyZXNoXG4gICAqIG9yIHByb2dyYW1tYXRpYyByZWZyZXNoLiBUaGUgZXZlbnQgaW5jbHVkZXMgdGhlIHJlZnJlc2hlZCBkYXRhIGFuZCBjb21wb25lbnQgaW5mb3JtYXRpb24uXG4gICAqXG4gICAqIEB0eXBlIHtFdmVudEVtaXR0ZXI8QmFzZUN1c3RvbUV2ZW50Pn1cbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIEBPdXRwdXQoKVxuICByZWZyZXNoRXZlbnQ6IEV2ZW50RW1pdHRlcjxCYXNlQ3VzdG9tRXZlbnQ+ID0gbmV3IEV2ZW50RW1pdHRlcjxCYXNlQ3VzdG9tRXZlbnQ+KCk7XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBFdmVudCBlbWl0dGVyIGZvciBpdGVtIGNsaWNrIGludGVyYWN0aW9ucy5cbiAgICogQHN1bW1hcnkgRW1pdHMgYW4gZXZlbnQgd2hlbiBhIGxpc3QgaXRlbSBpcyBjbGlja2VkLiBUaGUgZXZlbnQgaW5jbHVkZXMgdGhlIGRhdGFcbiAgICogb2YgdGhlIGNsaWNrZWQgaXRlbSwgYWxsb3dpbmcgcGFyZW50IGNvbXBvbmVudHMgdG8gcmVzcG9uZCB0byB0aGUgaW50ZXJhY3Rpb24uXG4gICAqXG4gICAqIEB0eXBlIHtFdmVudEVtaXR0ZXI8S2V5VmFsdWU+fVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgQE91dHB1dCgpXG4gIGNsaWNrRXZlbnQ6ICBFdmVudEVtaXR0ZXI8TGlzdEl0ZW1DdXN0b21FdmVudHxSZW5kZXJlckN1c3RvbUV2ZW50PiA9IG5ldyBFdmVudEVtaXR0ZXI8TGlzdEl0ZW1DdXN0b21FdmVudHxSZW5kZXJlckN1c3RvbUV2ZW50PigpO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gU3ViamVjdCBmb3IgZGVib3VuY2luZyBjbGljayBldmVudHMuXG4gICAqIEBzdW1tYXJ5IFVzZXMgUnhKUyBTdWJqZWN0IHRvIGNvbGxlY3QgY2xpY2sgZXZlbnRzIGFuZCBlbWl0IHRoZW0gYWZ0ZXIgYSBkZWJvdW5jZVxuICAgKiBwZXJpb2QuIFRoaXMgcHJldmVudHMgbXVsdGlwbGUgcmFwaWQgY2xpY2tzIGZyb20gdHJpZ2dlcmluZyBtdWx0aXBsZSBldmVudHMuXG4gICAqXG4gICAqIEBwcml2YXRlXG4gICAqIEB0eXBlIHtTdWJqZWN0PEN1c3RvbUV2ZW50IHwgTGlzdEl0ZW1DdXN0b21FdmVudCB8IFJlbmRlcmVyQ3VzdG9tRXZlbnQ+fVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgcHJpdmF0ZSBjbGlja0l0ZW1TdWJqZWN0OiBTdWJqZWN0PEN1c3RvbUV2ZW50IHwgTGlzdEl0ZW1DdXN0b21FdmVudCB8IFJlbmRlcmVyQ3VzdG9tRXZlbnQ+ID0gbmV3IFN1YmplY3Q8Q3VzdG9tRXZlbnQgfCBMaXN0SXRlbUN1c3RvbUV2ZW50IHwgUmVuZGVyZXJDdXN0b21FdmVudD4oKTtcblxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gU3ViamVjdCBmb3IgZGVib3VuY2luZyByZXBvc2l0b3J5IG9ic2VydmF0aW9uIGV2ZW50cy5cbiAgICogQHN1bW1hcnkgUnhKUyBTdWJqZWN0IHRoYXQgY29sbGVjdHMgcmVwb3NpdG9yeSBjaGFuZ2UgZXZlbnRzIGFuZCBlbWl0cyB0aGVtIGFmdGVyXG4gICAqIGEgZGVib3VuY2UgcGVyaW9kLiBUaGlzIHByZXZlbnRzIG11bHRpcGxlIHJhcGlkIHJlcG9zaXRvcnkgY2hhbmdlcyBmcm9tIHRyaWdnZXJpbmdcbiAgICogbXVsdGlwbGUgbGlzdCByZWZyZXNoIG9wZXJhdGlvbnMsIGltcHJvdmluZyBwZXJmb3JtYW5jZSBhbmQgdXNlciBleHBlcmllbmNlLlxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAdHlwZSB7U3ViamVjdDxhbnk+fVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcbiAgcHJpdmF0ZSBvYnNlcnZlclN1YmpldDogU3ViamVjdDxhbnk+ID0gbmV3IFN1YmplY3Q8YW55PigpO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gT2JzZXJ2ZXIgb2JqZWN0IGZvciByZXBvc2l0b3J5IGNoYW5nZSBub3RpZmljYXRpb25zLlxuICAgKiBAc3VtbWFyeSBJbXBsZW1lbnRzIHRoZSBPYnNlcnZlciBpbnRlcmZhY2UgdG8gcmVjZWl2ZSBub3RpZmljYXRpb25zIHdoZW4gdGhlXG4gICAqIHVuZGVybHlpbmcgZGF0YSByZXBvc2l0b3J5IGNoYW5nZXMuIFRoaXMgZW5hYmxlcyBhdXRvbWF0aWMgbGlzdCB1cGRhdGVzIHdoZW5cbiAgICogZGF0YSBpcyBjcmVhdGVkLCB1cGRhdGVkLCBvciBkZWxldGVkIHRocm91Z2ggdGhlIHJlcG9zaXRvcnkuXG4gICAqXG4gICAqIEBwcml2YXRlXG4gICAqIEB0eXBlIHtPYnNlcnZlcn1cbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIHByaXZhdGUgb2JzZXJ2ZXI6IE9ic2VydmVyID0geyByZWZyZXNoOiBhc3luYyAoLi4uIGFyZ3M6IHVua25vd25bXSk6IFByb21pc2U8dm9pZD4gPT4gdGhpcy5vYnNlcnZlUmVwb3NpdG9yeSguLi5hcmdzKX1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIExpc3Qgb2YgYXZhaWxhYmxlIGluZGV4ZXMgZm9yIGRhdGEgcXVlcnlpbmcgYW5kIGZpbHRlcmluZy5cbiAgICogQHN1bW1hcnkgUHJvdmlkZXMgYSBsaXN0IG9mIGluZGV4IG5hbWVzIHRoYXQgY2FuIGJlIHVzZWQgdG8gb3B0aW1pemUgZGF0YSBxdWVyeWluZyBhbmQgZmlsdGVyaW5nXG4gICAqIG9wZXJhdGlvbnMsIGVzcGVjaWFsbHkgaW4gc2NlbmFyaW9zIHdpdGggbGFyZ2UgZGF0YXNldHMuXG4gICAqXG4gICAqIEluZGV4ZXMgY2FuIHNpZ25pZmljYW50bHkgaW1wcm92ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgZGF0YSByZXRyaWV2YWwgYnkgYWxsb3dpbmcgdGhlIGRhdGFiYXNlXG4gICAqIHRvIHF1aWNrbHkgbG9jYXRlIGFuZCByZXRyaWV2ZSByZWxldmFudCBkYXRhIGJhc2VkIG9uIGluZGV4ZWQgZmllbGRzLlxuICAgKlxuICAgKiBAdHlwZSB7c3RyaW5nW119XG4gICAqIEBkZWZhdWx0IFtdXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBpbmRleGVzITogc3RyaW5nW107XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBJbml0aWFsaXplcyBhIG5ldyBpbnN0YW5jZSBvZiB0aGUgTGlzdENvbXBvbmVudC5cbiAgICogQHN1bW1hcnkgQ3JlYXRlcyBhIG5ldyBMaXN0Q29tcG9uZW50IGFuZCBzZXRzIHVwIHRoZSBiYXNlIGNvbXBvbmVudCB3aXRoIHRoZSBhcHByb3ByaWF0ZVxuICAgKiBjb21wb25lbnQgbmFtZS4gVGhpcyBjb25zdHJ1Y3RvciBpcyBjYWxsZWQgd2hlbiBBbmd1bGFyIGluc3RhbnRpYXRlcyB0aGUgY29tcG9uZW50IGFuZFxuICAgKiBiZWZvcmUgYW55IGlucHV0IHByb3BlcnRpZXMgYXJlIHNldC4gSXQgcGFzc2VzIHRoZSBjb21wb25lbnQgbmFtZSB0byB0aGUgcGFyZW50IGNsYXNzXG4gICAqIGNvbnN0cnVjdG9yIHRvIGVuYWJsZSBwcm9wZXIgbG9jYWxpemF0aW9uIGFuZCBjb21wb25lbnQgaWRlbnRpZmljYXRpb24uXG4gICAqXG4gICAqIFRoZSBjb25zdHJ1Y3RvciBpcyBpbnRlbnRpb25hbGx5IG1pbmltYWwsIHdpdGggbW9zdCBpbml0aWFsaXphdGlvbiBsb2dpYyBkZWZlcnJlZCB0b1xuICAgKiB0aGUgbmdPbkluaXQgbGlmZWN5Y2xlIGhvb2suIFRoaXMgZm9sbG93cyBBbmd1bGFyIGJlc3QgcHJhY3RpY2VzIGJ5IGtlZXBpbmcgdGhlIGNvbnN0cnVjdG9yXG4gICAqIGZvY3VzZWQgb24gZGVwZW5kZW5jeSBpbmplY3Rpb24gYW5kIGJhc2ljIHNldHVwLCB3aGlsZSBjb21wbGV4IGluaXRpYWxpemF0aW9uIHRoYXQgZGVwZW5kc1xuICAgKiBvbiBpbnB1dCBwcm9wZXJ0aWVzIGlzIGhhbmRsZWQgaW4gbmdPbkluaXQuXG4gICAqXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBjb25zdHJ1Y3RvcigpIHtcbiAgICBzdXBlcihcIkxpc3RDb21wb25lbnRcIik7XG4gIH1cblxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSW5pdGlhbGl6ZXMgdGhlIGNvbXBvbmVudCBhZnRlciBBbmd1bGFyIHNldHMgdGhlIGlucHV0IHByb3BlcnRpZXMuXG4gICAqIEBzdW1tYXJ5IFNldHMgdXAgdGhlIGNvbXBvbmVudCBieSBpbml0aWFsaXppbmcgZXZlbnQgc3Vic2NyaXB0aW9ucywgcHJvY2Vzc2luZyBib29sZWFuXG4gICAqIGlucHV0cywgYW5kIGxvYWRpbmcgdGhlIGluaXRpYWwgZGF0YS4gVGhpcyBtZXRob2QgcHJlcGFyZXMgdGhlIGNvbXBvbmVudCBmb3IgdXNlclxuICAgKiBpbnRlcmFjdGlvbiBieSBlbnN1cmluZyBhbGwgcHJvcGVydGllcyBhcmUgcHJvcGVybHkgaW5pdGlhbGl6ZWQgYW5kIGRhdGEgaXMgbG9hZGVkLlxuICAgKlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICpcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgQSBhcyBBbmd1bGFyIExpZmVjeWNsZVxuICAgKiAgIHBhcnRpY2lwYW50IEwgYXMgTGlzdENvbXBvbmVudFxuICAgKiAgIHBhcnRpY2lwYW50IEQgYXMgRGF0YSBTb3VyY2VcbiAgICpcbiAgICogICBBLT4+TDogbmdPbkluaXQoKVxuICAgKiAgIEwtPj5MOiBTZXQgdXAgY2xpY2sgZXZlbnQgZGVib3VuY2luZ1xuICAgKiAgIEwtPj5MOiBQcm9jZXNzIGJvb2xlYW4gaW5wdXRzXG4gICAqICAgTC0+Pkw6IENvbmZpZ3VyZSBjb21wb25lbnQgYmFzZWQgb24gaW5wdXRzXG4gICAqICAgTC0+Pkw6IHJlZnJlc2goKVxuICAgKiAgIEwtPj5EOiBSZXF1ZXN0IGluaXRpYWwgZGF0YVxuICAgKiAgIEQtLT4+TDogUmV0dXJuIGRhdGFcbiAgICogICBMLT4+TDogUHJvY2VzcyBhbmQgZGlzcGxheSBkYXRhXG4gICAqICAgTC0+Pkw6IENvbmZpZ3VyZSBlbXB0eSBzdGF0ZSBpZiBuZWVkZWRcbiAgICogICBMLT4+TDogaW5pdGlhbGl6ZSgpXG4gICAqXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBhc3luYyBuZ09uSW5pdCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmNsaWNrSXRlbVN1YmplY3QucGlwZShkZWJvdW5jZVRpbWUoMTAwKSkuc3Vic2NyaWJlKGV2ZW50ID0+IHRoaXMuY2xpY2tFdmVudEVtaXQoZXZlbnQgYXMgTGlzdEl0ZW1DdXN0b21FdmVudCB8IFJlbmRlcmVyQ3VzdG9tRXZlbnQpKTtcbiAgICB0aGlzLm9ic2VydmVyU3ViamV0LnBpcGUoZGVib3VuY2VUaW1lKDEwMCkpLnN1YnNjcmliZShhcmdzID0+IHRoaXMuaGFuZGxlT2JzZXJ2ZUV2ZW50KGFyZ3NbMF0sIGFyZ3NbMV0sIGFyZ3NbMl0pKTtcbiAgICB0aGlzLmVuYWJsZUZpbHRlciA9IHN0cmluZ1RvQm9vbGVhbih0aGlzLmVuYWJsZUZpbHRlcik7XG4gICAgdGhpcy5saW1pdCA9IE51bWJlcih0aGlzLmxpbWl0KTtcbiAgICB0aGlzLnN0YXJ0ID0gTnVtYmVyKHRoaXMuc3RhcnQpO1xuICAgIHRoaXMuaW5zZXQgPSBzdHJpbmdUb0Jvb2xlYW4odGhpcy5pbnNldCk7XG4gICAgdGhpcy5zaG93UmVmcmVzaGVyID0gc3RyaW5nVG9Cb29sZWFuKHRoaXMuc2hvd1JlZnJlc2hlcik7XG4gICAgdGhpcy5sb2FkTW9yZURhdGEgPSBzdHJpbmdUb0Jvb2xlYW4odGhpcy5sb2FkTW9yZURhdGEpO1xuICAgIHRoaXMuc2hvd1NlYXJjaGJhciA9IHN0cmluZ1RvQm9vbGVhbih0aGlzLnNob3dTZWFyY2hiYXIpO1xuICAgIHRoaXMuZGlzYWJsZVNvcnQgPSBzdHJpbmdUb0Jvb2xlYW4odGhpcy5kaXNhYmxlU29ydCk7XG4gICAgaWYodHlwZW9mIHRoaXMuaXRlbT8uWyd0YWcnXSA9PT0gJ2Jvb2xlYW4nICYmIHRoaXMuaXRlbT8uWyd0YWcnXSA9PT0gdHJ1ZSlcbiAgICAgIHRoaXMuaXRlbVsndGFnJ10gPSBDb21wb25lbnRzVGFnTmFtZXMuTElTVF9JVEVNIGFzIHN0cmluZztcblxuICAgIGF3YWl0IHRoaXMucmVmcmVzaCgpO1xuXG4gICAgaWYodGhpcy5vcGVyYXRpb25zLmluY2x1ZGVzKE9wZXJhdGlvbktleXMuQ1JFQVRFKSAmJiB0aGlzLnJvdXRlKVxuICAgICAgdGhpcy5lbXB0eS5saW5rID0gYCR7dGhpcy5yb3V0ZX0vJHtPcGVyYXRpb25LZXlzLkNSRUFURX1gO1xuXG4gICAgdGhpcy5pbml0aWFsaXplKCk7XG5cbiAgICBpZih0aGlzLm1vZGVsIGluc3RhbmNlb2YgTW9kZWwgJiYgdGhpcy5fcmVwb3NpdG9yeSlcbiAgICAgIHRoaXMuX3JlcG9zaXRvcnkub2JzZXJ2ZSh0aGlzLm9ic2VydmVyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ2xlYW5zIHVwIHJlc291cmNlcyB3aGVuIHRoZSBjb21wb25lbnQgaXMgZGVzdHJveWVkLlxuICAgKiBAc3VtbWFyeSBQZXJmb3JtcyBjbGVhbnVwIG9wZXJhdGlvbnMgd2hlbiB0aGUgY29tcG9uZW50IGlzIGJlaW5nIHJlbW92ZWQgZnJvbSB0aGUgRE9NLlxuICAgKiBUaGlzIGluY2x1ZGVzIGNsZWFyaW5nIHJlZmVyZW5jZXMgdG8gbW9kZWxzIGFuZCBkYXRhIHRvIHByZXZlbnQgbWVtb3J5IGxlYWtzLlxuICAgKlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIGlmKHRoaXMuX3JlcG9zaXRvcnkpXG4gICAgICB0aGlzLl9yZXBvc2l0b3J5LnVuT2JzZXJ2ZSh0aGlzLm9ic2VydmVyKTtcbiAgICB0aGlzLmRhdGEgPSAgdGhpcy5tb2RlbCA9IHRoaXMuX3JlcG9zaXRvcnkgPSB0aGlzLnBhZ2luYXRvciA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSGFuZGxlcyByZXBvc2l0b3J5IG9ic2VydmF0aW9uIGV2ZW50cyB3aXRoIGRlYm91bmNpbmcuXG4gICAqIEBzdW1tYXJ5IFByb2Nlc3NlcyByZXBvc2l0b3J5IGNoYW5nZSBub3RpZmljYXRpb25zIGFuZCByb3V0ZXMgdGhlbSBhcHByb3ByaWF0ZWx5LlxuICAgKiBGb3IgQ1JFQVRFIGV2ZW50cyB3aXRoIGEgVUlELCBoYW5kbGVzIHRoZW0gaW1tZWRpYXRlbHkuIEZvciBvdGhlciBldmVudHMsXG4gICAqIHBhc3NlcyB0aGVtIHRvIHRoZSBkZWJvdW5jZWQgb2JzZXJ2ZXIgc3ViamVjdCB0byBwcmV2ZW50IGV4Y2Vzc2l2ZSB1cGRhdGVzLlxuICAgKlxuICAgKiBAcGFyYW0gey4uLnVua25vd25bXX0gYXJncyAtIFRoZSByZXBvc2l0b3J5IGV2ZW50IGFyZ3VtZW50cyBpbmNsdWRpbmcgdGFibGUsIGV2ZW50IHR5cGUsIGFuZCBVSURcbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBhc3luYyBvYnNlcnZlUmVwb3NpdG9yeSguLi5hcmdzOiB1bmtub3duW10pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBbdGFibGUsIGV2ZW50LCB1aWRdID0gYXJncztcbiAgICBpZihldmVudCA9PT0gT3BlcmF0aW9uS2V5cy5DUkVBVEUgJiYgISF1aWQpXG4gICAgICByZXR1cm4gdGhpcy5oYW5kbGVPYnNlcnZlRXZlbnQodGFibGUgYXMgc3RyaW5nLCBldmVudCwgdWlkIGFzIHN0cmluZyB8IG51bWJlcik7XG4gICAgcmV0dXJuIHRoaXMub2JzZXJ2ZXJTdWJqZXQubmV4dChhcmdzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSGFuZGxlcyBzcGVjaWZpYyByZXBvc2l0b3J5IGV2ZW50cyBhbmQgdXBkYXRlcyB0aGUgbGlzdCBhY2NvcmRpbmdseS5cbiAgICogQHN1bW1hcnkgUHJvY2Vzc2VzIHJlcG9zaXRvcnkgY2hhbmdlIGV2ZW50cyAoQ1JFQVRFLCBVUERBVEUsIERFTEVURSkgYW5kIHBlcmZvcm1zXG4gICAqIHRoZSBhcHByb3ByaWF0ZSBsaXN0IG9wZXJhdGlvbnMuIFRoaXMgaW5jbHVkZXMgYWRkaW5nIG5ldyBpdGVtcywgdXBkYXRpbmcgZXhpc3RpbmdcbiAgICogb25lcywgb3IgcmVtb3ZpbmcgZGVsZXRlZCBpdGVtcyBmcm9tIHRoZSBsaXN0IGRpc3BsYXkuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0YWJsZSAtIFRoZSB0YWJsZS9tb2RlbCBuYW1lIHRoYXQgY2hhbmdlZFxuICAgKiBAcGFyYW0ge09wZXJhdGlvbktleXN9IGV2ZW50IC0gVGhlIHR5cGUgb2Ygb3BlcmF0aW9uIChDUkVBVEUsIFVQREFURSwgREVMRVRFKVxuICAgKiBAcGFyYW0ge3N0cmluZyB8IG51bWJlcn0gdWlkIC0gVGhlIHVuaXF1ZSBpZGVudGlmaWVyIG9mIHRoZSBhZmZlY3RlZCBpdGVtXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgYXN5bmMgaGFuZGxlT2JzZXJ2ZUV2ZW50KHRhYmxlOiBzdHJpbmcsIGV2ZW50OiBPcGVyYXRpb25LZXlzLCB1aWQ6IHN0cmluZyB8IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmKGV2ZW50ID09PSBPcGVyYXRpb25LZXlzLkNSRUFURSkge1xuICAgICAgaWYodWlkKSB7XG4gICAgICAgIGF3YWl0IHRoaXMuaGFuZGxlQ3JlYXRlKHVpZCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhd2FpdCB0aGlzLnJlZnJlc2godHJ1ZSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmKGV2ZW50ID09PSBPcGVyYXRpb25LZXlzLlVQREFURSlcbiAgICAgICAgYXdhaXQgdGhpcy5oYW5kbGVVcGRhdGUodWlkKTtcbiAgICAgIGlmKGV2ZW50ID09PSBPcGVyYXRpb25LZXlzLkRFTEVURSlcbiAgICAgICAgdGhpcy5oYW5kbGVEZWxldGUodWlkKTtcbiAgICAgIHRoaXMucmVmcmVzaEV2ZW50RW1pdCgpO1xuICAgIH1cbiAgfVxuXG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBGdW5jdGlvbiBmb3IgdHJhY2tpbmcgaXRlbXMgaW4gdGhlIGxpc3QuXG4gICAqIEBzdW1tYXJ5IFByb3ZpZGVzIGEgdHJhY2tpbmcgZnVuY3Rpb24gZm9yIHRoZSBgKm5nRm9yYCBkaXJlY3RpdmUgaW4gdGhlIGNvbXBvbmVudCB0ZW1wbGF0ZS5cbiAgICogVGhpcyBmdW5jdGlvbiBpcyB1c2VkIHRvIGlkZW50aWZ5IGFuZCBjb250cm9sIHRoZSByZW5kZXJpbmcgb2YgaXRlbXMgaW4gdGhlIGxpc3QsXG4gICAqIHByZXZlbnRpbmcgZHVwbGljYXRlIG9yIHVubmVjZXNzYXJ5IHJlbmRlcmluZy5cbiAgICpcbiAgICogVGhlIGB0cmFja0l0ZW1GbmAgZnVuY3Rpb24gdGFrZXMgdHdvIHBhcmFtZXRlcnM6IGBpbmRleGAgKHRoZSBpbmRleCBvZiB0aGUgaXRlbSBpbiB0aGUgbGlzdClcbiAgICogYW5kIGBpdGVtYCAodGhlIGFjdHVhbCBpdGVtIGZyb20gdGhlIGxpc3QpLiBJdCByZXR1cm5zIHRoZSB0cmFja2luZyBrZXksIHdoaWNoIGluIHRoaXMgY2FzZVxuICAgKiBpcyB0aGUgdW5pb24gb2YgdGhlIGB1aWRgIG9mIHRoZSBpdGVtIHdpdGggdGhlIG1vZGVsIG5hbWUuXG4gICAqXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBpbmRleCAtIFRoZSBpbmRleCBvZiB0aGUgaXRlbSBpbiB0aGUgbGlzdC5cblxuICAgKiBAcGFyYW0ge0tleVZhbHVlIHwgc3RyaW5nIHwgbnVtYmVyfSBpdGVtIC0gVGhlIGFjdHVhbCBpdGVtIGZyb20gdGhlIGxpc3QuXG4gICAqIEByZXR1cm5zIHtzdHJpbmcgfCBudW1iZXJ9IFRoZSB0cmFja2luZyBrZXkgZm9yIHRoZSBpdGVtLlxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgb3ZlcnJpZGUgdHJhY2tJdGVtRm4oaW5kZXg6IG51bWJlciwgaXRlbTogS2V5VmFsdWUgfCBzdHJpbmcgfCBudW1iZXIpOiBzdHJpbmcgfCBudW1iZXIge1xuICAgIHJldHVybiBgJHsgKGl0ZW0gYXMgS2V5VmFsdWUpPy5bJ3VpZCddIHx8IChpdGVtIGFzIEtleVZhbHVlKT8uW3RoaXMucGtdfS0ke2luZGV4fWA7XG4gIH1cblxuXG4gIC8qKlxuICAgKiBIYW5kbGVzIHRoZSBjcmVhdGUgZXZlbnQgZnJvbSB0aGUgcmVwb3NpdG9yeS5cbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmcgfCBudW1iZXJ9IHVpZCAtIFRoZSBJRCBvZiB0aGUgaXRlbSB0byBjcmVhdGUuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBpdGVtIGlzIGNyZWF0ZWQgYW5kIGFkZGVkIHRvIHRoZSBsaXN0LlxuICAgKi9cbiAgYXN5bmMgaGFuZGxlQ3JlYXRlKHVpZDogc3RyaW5nIHwgbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5fcmVwb3NpdG9yeT8ucmVhZCh1aWQpO1xuICAgIGNvbnN0IGl0ZW0gPSB0aGlzLm1hcFJlc3VsdHMoW3Jlc3VsdCBhcyBLZXlWYWx1ZV0pWzBdO1xuICAgIHRoaXMuaXRlbXMgPSB0aGlzLmRhdGEgPSBbaXRlbSwgLi4udGhpcy5pdGVtcyB8fCBbXV07XG4gIH1cblxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSGFuZGxlcyB0aGUgdXBkYXRlIGV2ZW50IGZyb20gdGhlIHJlcG9zaXRvcnkuXG4gICAqIEBzdW1tYXJ5IFVwZGF0ZXMgdGhlIGxpc3QgaXRlbSB3aXRoIHRoZSBzcGVjaWZpZWQgSUQgYmFzZWQgb24gdGhlIG5ldyBkYXRhLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZyB8IG51bWJlcn0gdWlkIC0gVGhlIElEIG9mIHRoZSBpdGVtIHRvIHVwZGF0ZVxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICogQHByaXZhdGVcbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIGFzeW5jIGhhbmRsZVVwZGF0ZSh1aWQ6IHN0cmluZyB8IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGl0ZW06IEtleVZhbHVlID0gdGhpcy5pdGVtTWFwcGVyKGF3YWl0IHRoaXMuX3JlcG9zaXRvcnk/LnJlYWQodWlkKSB8fCB7fSwgdGhpcy5tYXBwZXIpO1xuICAgIHRoaXMuZGF0YSA9IFtdO1xuICAgIGZvcihjb25zdCBrZXkgaW4gdGhpcy5pdGVtcyBhcyBLZXlWYWx1ZVtdKSB7XG4gICAgICAgIGNvbnN0IGNoaWxkID0gdGhpcy5pdGVtc1trZXldIGFzIEtleVZhbHVlO1xuICAgICAgICBpZihjaGlsZFsndWlkJ10gPT09IGl0ZW1bJ3VpZCddKSB7XG4gICAgICAgICAgdGhpcy5pdGVtc1trZXldID0gT2JqZWN0LmFzc2lnbih7fSwgY2hpbGQsIGl0ZW0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgfVxuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgdGhpcy5kYXRhID0gWyAuLi50aGlzLml0ZW1zXTtcbiAgICB9LCAwKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUmVtb3ZlcyBhbiBpdGVtIGZyb20gdGhlIGxpc3QgYnkgSUQuXG4gICAqIEBzdW1tYXJ5IEZpbHRlcnMgb3V0IGFuIGl0ZW0gd2l0aCB0aGUgc3BlY2lmaWVkIElEIGZyb20gdGhlIGRhdGEgYXJyYXkgYW5kXG4gICAqIHJlZnJlc2hlcyB0aGUgbGlzdCBkaXNwbGF5LiBUaGlzIGlzIHR5cGljYWxseSB1c2VkIGFmdGVyIGEgZGVsZXRlIG9wZXJhdGlvbi5cbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IHVpZCAtIFRoZSBJRCBvZiB0aGUgaXRlbSB0byBkZWxldGVcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBrIC0gVGhlIHByaW1hcnkga2V5IGZpZWxkIG5hbWVcbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBoYW5kbGVEZWxldGUodWlkOiBzdHJpbmcgfCBudW1iZXIsIHBrPzogc3RyaW5nKTogdm9pZCAge1xuICAgIGlmKCFwaylcbiAgICAgIHBrID0gdGhpcy5waztcbiAgICB0aGlzLml0ZW1zID0gdGhpcy5kYXRhPy5maWx0ZXIoKGl0ZW06IEtleVZhbHVlKSA9PiBpdGVtWyd1aWQnXSAhPT0gdWlkKSB8fCBbXTtcbiAgfVxuXG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBIYW5kbGVzIGNsaWNrIGV2ZW50cyBmcm9tIGxpc3QgaXRlbXMuXG4gICAqIEBzdW1tYXJ5IExpc3RlbnMgZm9yIGdsb2JhbCBMaXN0SXRlbUNsaWNrRXZlbnQgZXZlbnRzIGFuZCBwYXNzZXMgdGhlbSB0byB0aGVcbiAgICogZGVib3VuY2VkIGNsaWNrIHN1YmplY3QuIFRoaXMgYWxsb3dzIHRoZSBjb21wb25lbnQgdG8gcmVzcG9uZCB0byBjbGlja3Mgb25cbiAgICogbGlzdCBpdGVtcyByZWdhcmRsZXNzIG9mIHdoZXJlIHRoZXkgb3JpZ2luYXRlIGZyb20uXG4gICAqXG4gICAqIEBwYXJhbSB7TGlzdEl0ZW1DdXN0b21FdmVudCB8IFJlbmRlcmVyQ3VzdG9tRXZlbnR9IGV2ZW50IC0gVGhlIGNsaWNrIGV2ZW50XG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKlxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgQEhvc3RMaXN0ZW5lcignd2luZG93Okxpc3RJdGVtQ2xpY2tFdmVudCcsIFsnJGV2ZW50J10pXG4gIGhhbmRsZUNsaWNrKGV2ZW50OiBMaXN0SXRlbUN1c3RvbUV2ZW50IHwgUmVuZGVyZXJDdXN0b21FdmVudCk6IHZvaWQge1xuICAgIHRoaXMuY2xpY2tJdGVtU3ViamVjdC5uZXh0KGV2ZW50KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSGFuZGxlcyBzZWFyY2ggZXZlbnRzIGZyb20gdGhlIHNlYXJjaCBiYXIuXG4gICAqIEBzdW1tYXJ5IFByb2Nlc3NlcyBzZWFyY2ggcXVlcmllcyBmcm9tIHRoZSBzZWFyY2ggYmFyIGNvbXBvbmVudCwgdXBkYXRpbmcgdGhlXG4gICAqIGRpc3BsYXllZCBkYXRhIGJhc2VkIG9uIHRoZSBzZWFyY2ggdGVybS4gVGhlIGJlaGF2aW9yIGRpZmZlcnMgYmV0d2VlbiBpbmZpbml0ZVxuICAgKiBhbmQgcGFnaW5hdGVkIG1vZGVzIHRvIHByb3ZpZGUgdGhlIGJlc3QgdXNlciBleHBlcmllbmNlIGZvciBlYWNoIG1vZGUuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nIHwgdW5kZWZpbmVkfSB2YWx1ZSAtIFRoZSBzZWFyY2ggdGVybSBvciB1bmRlZmluZWQgdG8gY2xlYXIgc2VhcmNoXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKlxuICAgKiBAbWVybWFpZFxuICAgKiBmbG93Y2hhcnQgVERcbiAgICogICBBW1NlYXJjaCBFdmVudF0gLS0+IEJ7VHlwZSBpcyBJbmZpbml0ZT99XG4gICAqICAgQiAtLT58WWVzfCBDW0Rpc2FibGUgbG9hZE1vcmVEYXRhXVxuICAgKiAgIEIgLS0+fE5vfCBEW0VuYWJsZSBsb2FkTW9yZURhdGFdXG4gICAqICAgQyAtLT4gRXtTZWFyY2ggdmFsdWUgdW5kZWZpbmVkP31cbiAgICogICBFIC0tPnxZZXN8IEZbRW5hYmxlIGxvYWRNb3JlRGF0YV1cbiAgICogICBFIC0tPnxOb3wgR1tTdG9yZSBzZWFyY2ggdmFsdWVdXG4gICAqICAgRCAtLT4gR1xuICAgKiAgIEYgLS0+IEhbUmVzZXQgcGFnZSB0byAxXVxuICAgKiAgIEcgLS0+IElbUmVmcmVzaCBkYXRhXVxuICAgKiAgIEggLS0+IElcbiAgICpcbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzpzZWFyY2hiYXJFdmVudCcsIFsnJGV2ZW50J10pXG4gIGFzeW5jIGhhbmRsZVNlYXJjaCh2YWx1ZTogc3RyaW5nIHwgSUZpbHRlclF1ZXJ5IHwgdW5kZWZpbmVkKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYodGhpcy50eXBlID09PSBMaXN0Q29tcG9uZW50c1R5cGVzLklORklOSVRFKSB7XG4gICAgICB0aGlzLmxvYWRNb3JlRGF0YSA9IGZhbHNlO1xuICAgICAgaWYodmFsdWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICB0aGlzLmxvYWRNb3JlRGF0YSA9IHRydWU7XG4gICAgICAgIHRoaXMucGFnZSA9IDE7XG4gICAgICB9XG4gICAgICB0aGlzLnNlYXJjaFZhbHVlID0gdmFsdWU7XG4gICAgICBhd2FpdCB0aGlzLnJlZnJlc2godHJ1ZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMubG9hZE1vcmVEYXRhID0gdHJ1ZTtcbiAgICAgIHRoaXMuc2VhcmNoVmFsdWUgPSB2YWx1ZTtcbiAgICAgIGlmKHZhbHVlID09PSB1bmRlZmluZWQpXG4gICAgICAgIHRoaXMucGFnZSA9IHRoaXMubGFzdFBhZ2U7XG4gICAgICBhd2FpdCB0aGlzLnJlZnJlc2godHJ1ZSk7XG4gICAgfVxuICB9XG5cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEhhbmRsZXMgZmlsdGVyIGV2ZW50cyBmcm9tIHRoZSBmaWx0ZXIgY29tcG9uZW50LlxuICAgKiBAc3VtbWFyeSBQcm9jZXNzZXMgZmlsdGVyIHF1ZXJpZXMgZnJvbSB0aGUgZmlsdGVyIGNvbXBvbmVudCBhbmQgYXBwbGllcyB0aGVtXG4gICAqIHRvIHRoZSBsaXN0IGRhdGEuIFRoaXMgbWV0aG9kIGFjdHMgYXMgYSBicmlkZ2UgYmV0d2VlbiB0aGUgZmlsdGVyIGNvbXBvbmVudFxuICAgKiBhbmQgdGhlIHNlYXJjaCBmdW5jdGlvbmFsaXR5LCBjb252ZXJ0aW5nIGZpbHRlciBxdWVyaWVzIGludG8gc2VhcmNoIG9wZXJhdGlvbnMuXG4gICAqXG4gICAqIEBwYXJhbSB7SUZpbHRlclF1ZXJ5IHwgdW5kZWZpbmVkfSB2YWx1ZSAtIFRoZSBmaWx0ZXIgcXVlcnkgb2JqZWN0IG9yIHVuZGVmaW5lZCB0byBjbGVhciBmaWx0ZXJzXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgYXN5bmMgaGFuZGxlRmlsdGVyKHZhbHVlOiBJRmlsdGVyUXVlcnkgfCB1bmRlZmluZWQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLmhhbmRsZVNlYXJjaCh2YWx1ZSk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENsZWFycyB0aGUgY3VycmVudCBzZWFyY2ggYW5kIHJlc2V0cyB0aGUgbGlzdC5cbiAgICogQHN1bW1hcnkgQ29udmVuaWVuY2UgbWV0aG9kIHRoYXQgY2xlYXJzIHRoZSBzZWFyY2ggYnkgY2FsbGluZyBoYW5kbGVTZWFyY2hcbiAgICogd2l0aCB1bmRlZmluZWQuIFRoaXMgcmVzZXRzIHRoZSBsaXN0IHRvIHNob3cgYWxsIGRhdGEgd2l0aG91dCBmaWx0ZXJpbmcuXG4gICAqXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICAgKi9cbiAgYXN5bmMgY2xlYXJTZWFyY2goKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgdGhpcy5oYW5kbGVTZWFyY2godW5kZWZpbmVkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gRW1pdHMgYSByZWZyZXNoIGV2ZW50IHdpdGggdGhlIGN1cnJlbnQgZGF0YS5cbiAgICogQHN1bW1hcnkgQ3JlYXRlcyBhbmQgZW1pdHMgYSByZWZyZXNoIGV2ZW50IGNvbnRhaW5pbmcgdGhlIGN1cnJlbnQgbGlzdCBkYXRhLlxuICAgKiBUaGlzIG5vdGlmaWVzIHBhcmVudCBjb21wb25lbnRzIHRoYXQgdGhlIGxpc3QgZGF0YSBoYXMgYmVlbiByZWZyZXNoZWQuXG4gICAqXG4gICAqIEBwYXJhbSB7S2V5VmFsdWVbXX0gW2RhdGFdIC0gT3B0aW9uYWwgZGF0YSB0byBpbmNsdWRlIGluIHRoZSBldmVudFxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICpcbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIHJlZnJlc2hFdmVudEVtaXQoZGF0YT86IEtleVZhbHVlW10pOiB2b2lkIHtcbiAgICBpZighZGF0YSlcbiAgICAgIGRhdGEgPSB0aGlzLml0ZW1zO1xuICAgIHRoaXMuc2tlbGV0b25EYXRhID0gbmV3IEFycmF5KGRhdGE/Lmxlbmd0aCB8fCAyKTtcbiAgICB0aGlzLnJlZnJlc2hFdmVudC5lbWl0KHtcbiAgICAgIG5hbWU6IEV2ZW50Q29uc3RhbnRzLlJFRlJFU0hfRVZFTlQsXG4gICAgICBkYXRhOiBkYXRhIHx8IFtdLFxuICAgICAgY29tcG9uZW50OiB0aGlzLmNvbXBvbmVudE5hbWVcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gRW1pdHMgYSBjbGljayBldmVudCBmb3IgYSBsaXN0IGl0ZW0uXG4gICAqIEBzdW1tYXJ5IFByb2Nlc3NlcyBhbmQgZW1pdHMgYSBjbGljayBldmVudCB3aGVuIGEgbGlzdCBpdGVtIGlzIGNsaWNrZWQuXG4gICAqIFRoaXMgZXh0cmFjdHMgdGhlIHJlbGV2YW50IGRhdGEgZnJvbSB0aGUgZXZlbnQgYW5kIHBhc3NlcyBpdCB0byBwYXJlbnQgY29tcG9uZW50cy5cbiAgICpcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtMaXN0SXRlbUN1c3RvbUV2ZW50IHwgUmVuZGVyZXJDdXN0b21FdmVudH0gZXZlbnQgLSBUaGUgY2xpY2sgZXZlbnRcbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqXG4gICAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gICAqL1xuICBwcml2YXRlIGNsaWNrRXZlbnRFbWl0KGV2ZW50OiBMaXN0SXRlbUN1c3RvbUV2ZW50IHwgUmVuZGVyZXJDdXN0b21FdmVudCk6IHZvaWQge1xuICAgIHRoaXMuY2xpY2tFdmVudC5lbWl0KGV2ZW50KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUmVmcmVzaGVzIHRoZSBsaXN0IGRhdGEgZnJvbSB0aGUgY29uZmlndXJlZCBzb3VyY2UuXG4gICAqIEBzdW1tYXJ5IFRoaXMgbWV0aG9kIGhhbmRsZXMgYm90aCBpbml0aWFsIGRhdGEgbG9hZGluZyBhbmQgc3Vic2VxdWVudCByZWZyZXNoIG9wZXJhdGlvbnMsXG4gICAqIGluY2x1ZGluZyBwdWxsLXRvLXJlZnJlc2ggYW5kIGluZmluaXRlIHNjcm9sbGluZy4gSXQgbWFuYWdlcyB0aGUgZGF0YSBmZXRjaGluZyBwcm9jZXNzLFxuICAgKiB1cGRhdGVzIHRoZSBjb21wb25lbnQncyBzdGF0ZSwgYW5kIGhhbmRsZXMgcGFnaW5hdGlvbiBvciBpbmZpbml0ZSBzY3JvbGxpbmcgbG9naWMgYmFzZWRcbiAgICogb24gdGhlIGNvbXBvbmVudCdzIGNvbmZpZ3VyYXRpb24uXG4gICAqXG4gICAqIFRoZSBtZXRob2QgcGVyZm9ybXMgdGhlIGZvbGxvd2luZyBzdGVwczpcbiAgICogMS4gU2V0cyB0aGUgcmVmcmVzaGluZyBmbGFnIHRvIGluZGljYXRlIGEgZGF0YSBmZXRjaCBpcyBpbiBwcm9ncmVzc1xuICAgKiAyLiBDYWxjdWxhdGVzIHRoZSBhcHByb3ByaWF0ZSBzdGFydCBhbmQgbGltaXQgdmFsdWVzIGJhc2VkIG9uIHBhZ2luYXRpb24gc2V0dGluZ3NcbiAgICogMy4gRmV0Y2hlcyBkYXRhIGZyb20gdGhlIGFwcHJvcHJpYXRlIHNvdXJjZSAobW9kZWwgb3IgcmVxdWVzdClcbiAgICogNC4gVXBkYXRlcyB0aGUgY29tcG9uZW50J3MgZGF0YSBhbmQgZW1pdHMgYSByZWZyZXNoIGV2ZW50XG4gICAqIDUuIEhhbmRsZXMgcGFnaW5hdGlvbiBvciBpbmZpbml0ZSBzY3JvbGxpbmcgc3RhdGUgdXBkYXRlc1xuICAgKiA2LiBDb21wbGV0ZXMgYW55IHByb3ZpZGVkIGV2ZW50IChsaWtlIEluZmluaXRlU2Nyb2xsQ3VzdG9tRXZlbnQpXG4gICAqXG4gICAqIEBwYXJhbSB7SW5maW5pdGVTY3JvbGxDdXN0b21FdmVudCB8IFJlZnJlc2hlckN1c3RvbUV2ZW50IHwgYm9vbGVhbn0gZXZlbnQgLSBUaGUgZXZlbnQgdGhhdCB0cmlnZ2VyZWQgdGhlIHJlZnJlc2gsXG4gICAqIG9yIGEgYm9vbGVhbiBmbGFnIGluZGljYXRpbmcgaWYgdGhpcyBpcyBhIGZvcmNlZCByZWZyZXNoXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSByZWZyZXNoIG9wZXJhdGlvbiBpcyBjb21wbGV0ZVxuICAgKlxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBMIGFzIExpc3RDb21wb25lbnRcbiAgICogICBwYXJ0aWNpcGFudCBEIGFzIERhdGEgU291cmNlXG4gICAqICAgcGFydGljaXBhbnQgRSBhcyBFdmVudCBTeXN0ZW1cbiAgICpcbiAgICogICBMLT4+TDogcmVmcmVzaChldmVudClcbiAgICogICBMLT4+TDogU2V0IHJlZnJlc2hpbmcgZmxhZ1xuICAgKiAgIEwtPj5MOiBDYWxjdWxhdGUgc3RhcnQgYW5kIGxpbWl0XG4gICAqICAgYWx0IFVzaW5nIG1vZGVsXG4gICAqICAgICBMLT4+RDogZ2V0RnJvbU1vZGVsKGZvcmNlLCBzdGFydCwgbGltaXQpXG4gICAqICAgICBELS0+Pkw6IFJldHVybiBkYXRhXG4gICAqICAgZWxzZSBVc2luZyByZXF1ZXN0XG4gICAqICAgICBMLT4+RDogZ2V0RnJvbVJlcXVlc3QoZm9yY2UsIHN0YXJ0LCBsaW1pdClcbiAgICogICAgIEQtLT4+TDogUmV0dXJuIGRhdGFcbiAgICogICBlbmRcbiAgICogICBMLT4+RTogcmVmcmVzaEV2ZW50RW1pdCgpXG4gICAqICAgYWx0IEluZmluaXRlIHNjcm9sbGluZyBtb2RlXG4gICAqICAgICBMLT4+TDogQ2hlY2sgaWYgcmVhY2hlZCBsYXN0IHBhZ2VcbiAgICogICAgIGFsdCBMYXN0IHBhZ2UgcmVhY2hlZFxuICAgKiAgICAgICBMLT4+TDogQ29tcGxldGUgc2Nyb2xsIGV2ZW50XG4gICAqICAgICAgIEwtPj5MOiBEaXNhYmxlIGxvYWRNb3JlRGF0YVxuICAgKiAgICAgZWxzZSBNb3JlIHBhZ2VzIGF2YWlsYWJsZVxuICAgKiAgICAgICBMLT4+TDogSW5jcmVtZW50IHBhZ2UgbnVtYmVyXG4gICAqICAgICAgIEwtPj5MOiBDb21wbGV0ZSBzY3JvbGwgZXZlbnQgYWZ0ZXIgZGVsYXlcbiAgICogICAgIGVuZFxuICAgKiAgIGVsc2UgUGFnaW5hdGVkIG1vZGVcbiAgICogICAgIEwtPj5MOiBDbGVhciByZWZyZXNoaW5nIGZsYWcgYWZ0ZXIgZGVsYXlcbiAgICogICBlbmRcbiAgICpcbiAgICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAgICovXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzpCYWNrQnV0dG9uTmF2aWdhdGlvbkVuZEV2ZW50JywgWyckZXZlbnQnXSlcbiAgYXN5bmMgcmVmcmVzaChldmVudDogSW5maW5pdGVTY3JvbGxDdXN0b21FdmVudCB8IFJlZnJlc2hlckN1c3RvbUV2ZW50IHwgYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gIGlmKHR5cGVvZiBmb3JjZSAhPT0gJ2Jvb2xlYW4nICYmIGZvcmNlLnR5cGUgPT09IEV2ZW50Q29uc3RhbnRzLkJBQ0tfQlVUVE9OX05BVklHQVRJT04pIHtcbiAgICAvLyAgICBjb25zdCB7cmVmcmVzaH0gPSAoZm9yY2UgYXMgQ3VzdG9tRXZlbnQpLmRldGFpbDtcbiAgICAvLyAgICBpZighcmVmcmVzaClcbiAgICAvLyAgICAgIHJldHVybiBmYWxzZTtcbiAgICAvLyAgfVxuXG4gICAgdGhpcy5yZWZyZXNoaW5nID0gdHJ1ZTtcbiAgICBjb25zdCBzdGFydDogbnVtYmVyID0gdGhpcy5wYWdlID4gMSA/ICh0aGlzLnBhZ2UgLSAxKSAqIHRoaXMubGltaXQgOiB0aGlzLnN0YXJ0O1xuICAgIGNvbnN0IGxpbWl0OiBudW1iZXIgPSAodGhpcy5wYWdlICogKHRoaXMubGltaXQgPiAxMiA/IDEyIDogdGhpcy5saW1pdCkpO1xuXG4gICAgdGhpcy5kYXRhID0gIXRoaXMubW9kZWwgP1xuICAgICAgYXdhaXQgdGhpcy5nZXRGcm9tUmVxdWVzdCghIWV2ZW50LCBzdGFydCwgbGltaXQpXG4gICAgICA6IGF3YWl0IHRoaXMuZ2V0RnJvbU1vZGVsKCEhZXZlbnQpIGFzIEtleVZhbHVlW107XG5cbiAgICB0aGlzLnJlZnJlc2hFdmVudEVtaXQoKTtcblxuICAgIGlmKHRoaXMudHlwZSA9PT0gTGlzdENvbXBvbmVudHNUeXBlcy5JTkZJTklURSkge1xuICAgICAgaWYodGhpcy5wYWdlID09PSB0aGlzLnBhZ2VzKSB7XG4gICAgICAgIGlmKChldmVudCBhcyBJbmZpbml0ZVNjcm9sbEN1c3RvbUV2ZW50KT8udGFyZ2V0KVxuICAgICAgICAgIChldmVudCBhcyBJbmZpbml0ZVNjcm9sbEN1c3RvbUV2ZW50KS50YXJnZXQuY29tcGxldGUoKTtcbiAgICAgICAgdGhpcy5sb2FkTW9yZURhdGEgPSBmYWxzZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMucGFnZSArPSAxO1xuICAgICAgICB0aGlzLnJlZnJlc2hpbmcgPSBmYWxzZTtcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICBpZigoZXZlbnQgYXMgSW5maW5pdGVTY3JvbGxDdXN0b21FdmVudCk/LnRhcmdldCAmJiAoZXZlbnQgYXMgQ3VzdG9tRXZlbnQpPy50eXBlICE9PSBFdmVudENvbnN0YW50cy5CQUNLX0JVVFRPTl9OQVZJR0FUSU9OKVxuICAgICAgICAgICAgICAoZXZlbnQgYXMgSW5maW5pdGVTY3JvbGxDdXN0b21FdmVudCkudGFyZ2V0LmNvbXBsZXRlKCk7XG4gICAgICAgIH0sIDIwMCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICB0aGlzLnJlZnJlc2hpbmcgPSBmYWxzZTtcbiAgICAgIH0sIDIwMClcbiAgICB9XG4gIH1cblxuICAvKipcbiAqIEBkZXNjcmlwdGlvbiBIYW5kbGVzIHBhZ2luYXRpb24gZXZlbnRzIGZyb20gdGhlIHBhZ2luYXRpb24gY29tcG9uZW50LlxuICogQHN1bW1hcnkgUHJvY2Vzc2VzIHBhZ2luYXRpb24gZXZlbnRzIGJ5IHVwZGF0aW5nIHRoZSBjdXJyZW50IHBhZ2UgbnVtYmVyIGFuZFxuICogcmVmcmVzaGluZyB0aGUgbGlzdCBkYXRhIHRvIGRpc3BsYXkgdGhlIHNlbGVjdGVkIHBhZ2UuIFRoaXMgbWV0aG9kIGlzIGNhbGxlZFxuICogd2hlbiBhIHVzZXIgaW50ZXJhY3RzIHdpdGggdGhlIHBhZ2luYXRpb24gY29udHJvbHMgdG8gbmF2aWdhdGUgYmV0d2VlbiBwYWdlcy5cbiAqXG4gKiBAcGFyYW0ge1BhZ2luYXRpb25DdXN0b21FdmVudH0gZXZlbnQgLSBUaGUgcGFnaW5hdGlvbiBldmVudCBjb250YWluaW5nIHBhZ2UgaW5mb3JtYXRpb25cbiAqIEByZXR1cm5zIHt2b2lkfVxuICpcbiAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gKi9cbmhhbmRsZVBhZ2luYXRlKGV2ZW50OiBQYWdpbmF0aW9uQ3VzdG9tRXZlbnQpOiB2b2lkIHtcbiAgY29uc3QgeyBwYWdlfSA9IGV2ZW50LmRhdGE7XG4gIHRoaXMucGFnZSA9IHBhZ2U7XG4gIHRoaXMucmVmcmVzaCh0cnVlKTtcbn1cblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gSGFuZGxlcyBwdWxsLXRvLXJlZnJlc2ggZXZlbnRzIGZyb20gdGhlIHJlZnJlc2hlciBjb21wb25lbnQuXG4gKiBAc3VtbWFyeSBQcm9jZXNzZXMgcmVmcmVzaCBldmVudHMgdHJpZ2dlcmVkIGJ5IHRoZSB1c2VyIHB1bGxpbmcgZG93biBvbiB0aGUgbGlzdFxuICogb3IgYnkgcHJvZ3JhbW1hdGljIHJlZnJlc2ggcmVxdWVzdHMuIFRoaXMgbWV0aG9kIHJlZnJlc2hlcyB0aGUgbGlzdCBkYXRhIGFuZFxuICogY29tcGxldGVzIHRoZSByZWZyZXNoZXIgYW5pbWF0aW9uIHdoZW4gdGhlIGRhdGEgaXMgbG9hZGVkLlxuICpcbiAqIEBwYXJhbSB7SW5maW5pdGVTY3JvbGxDdXN0b21FdmVudCB8IEN1c3RvbUV2ZW50fSBbZXZlbnRdIC0gVGhlIHJlZnJlc2ggZXZlbnRcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSByZWZyZXNoIG9wZXJhdGlvbiBpcyBjb21wbGV0ZVxuICpcbiAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gKi9cbmFzeW5jIGhhbmRsZVJlZnJlc2goZXZlbnQ/OiBJbmZpbml0ZVNjcm9sbEN1c3RvbUV2ZW50IHwgQ3VzdG9tRXZlbnQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgdGhpcy5yZWZyZXNoKGV2ZW50IGFzIEluZmluaXRlU2Nyb2xsQ3VzdG9tRXZlbnQgfHwgdHJ1ZSk7XG4gIGlmKGV2ZW50IGluc3RhbmNlb2YgQ3VzdG9tRXZlbnQpXG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAvLyBBbnkgY2FsbHMgdG8gbG9hZCBkYXRhIGdvIGhlcmVcbiAgICAgIChldmVudC50YXJnZXQgYXMgSFRNTElvblJlZnJlc2hlckVsZW1lbnQpLmNvbXBsZXRlKCk7XG4gICAgfSwgNDAwKTtcbn1cblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gRmlsdGVycyBkYXRhIGJhc2VkIG9uIGEgc2VhcmNoIHN0cmluZy5cbiAqIEBzdW1tYXJ5IFByb2Nlc3NlcyB0aGUgY3VycmVudCBkYXRhIGFycmF5IHRvIGZpbmQgaXRlbXMgdGhhdCBtYXRjaCB0aGUgcHJvdmlkZWRcbiAqIHNlYXJjaCBzdHJpbmcuIFRoaXMgdXNlcyB0aGUgYXJyYXlRdWVyeUJ5U3RyaW5nIHV0aWxpdHkgdG8gcGVyZm9ybSB0aGUgZmlsdGVyaW5nXG4gKiBhY3Jvc3MgYWxsIHByb3BlcnRpZXMgb2YgdGhlIGl0ZW1zLlxuICpcbiAqIEBwYXJhbSB7S2V5VmFsdWVbXX0gcmVzdWx0cyAtIFRoZSBhcnJheSBvZiBpdGVtcyB0byBzZWFyY2ggdGhyb3VnaFxuICogQHBhcmFtIHtzdHJpbmd9IHNlYXJjaCAtIFRoZSBzZWFyY2ggc3RyaW5nIHRvIGZpbHRlciBieVxuICogQHJldHVybnMge0tleVZhbHVlW119IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBmaWx0ZXJlZCBhcnJheSBvZiBpdGVtc1xuICpcbiAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gKi9cbiAgcGFyc2VTZWFyY2hSZXN1bHRzKHJlc3VsdHM6IEtleVZhbHVlW10sIHNlYXJjaDogc3RyaW5nKTogS2V5VmFsdWVbXSB7XG4gICAgcmV0dXJuIHJlc3VsdHMuZmlsdGVyKChpdGVtOiBLZXlWYWx1ZSkgPT5cbiAgICAgIE9iamVjdC52YWx1ZXMoaXRlbSkuc29tZSh2YWx1ZSA9PlxuICAgICAgICAgIHZhbHVlLnRvU3RyaW5nKCkudG9Mb3dlckNhc2UoKS5pbmNsdWRlcygoc2VhcmNoIGFzIHN0cmluZyk/LnRvTG93ZXJDYXNlKCkpXG4gICAgICAgIClcbiAgICApO1xuICB9XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIEZldGNoZXMgZGF0YSBmcm9tIGEgcmVxdWVzdCBzb3VyY2UuXG4gKiBAc3VtbWFyeSBSZXRyaWV2ZXMgZGF0YSBmcm9tIHRoZSBjb25maWd1cmVkIHNvdXJjZSBmdW5jdGlvbiBvciBVUkwsIHByb2Nlc3NlcyBpdCxcbiAqIGFuZCB1cGRhdGVzIHRoZSBjb21wb25lbnQncyBkYXRhIHN0YXRlLiBUaGlzIG1ldGhvZCBoYW5kbGVzIGJvdGggaW5pdGlhbCBkYXRhIGxvYWRpbmdcbiAqIGFuZCBzdWJzZXF1ZW50IHJlZnJlc2ggb3BlcmF0aW9ucyB3aGVuIHVzaW5nIGFuIGV4dGVybmFsIGRhdGEgc291cmNlIHJhdGhlciB0aGFuIGEgbW9kZWwuXG4gKlxuICogQHBhcmFtIHtib29sZWFufSBmb3JjZSAtIFdoZXRoZXIgdG8gZm9yY2UgYSByZWZyZXNoIGV2ZW4gaWYgZGF0YSBhbHJlYWR5IGV4aXN0c1xuICogQHBhcmFtIHtudW1iZXJ9IHN0YXJ0IC0gVGhlIHN0YXJ0aW5nIGluZGV4IGZvciBwYWdpbmF0aW9uXG4gKiBAcGFyYW0ge251bWJlcn0gbGltaXQgLSBUaGUgbWF4aW11bSBudW1iZXIgb2YgaXRlbXMgdG8gcmV0cmlldmVcbiAqIEByZXR1cm5zIHtQcm9taXNlPEtleVZhbHVlW10+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZmV0Y2hlZCBkYXRhXG4gKlxuICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAqL1xuYXN5bmMgZ2V0RnJvbVJlcXVlc3QoZm9yY2U6IGJvb2xlYW4gPSBmYWxzZSwgc3RhcnQ6IG51bWJlciwgbGltaXQ6IG51bWJlcik6IFByb21pc2U8S2V5VmFsdWVbXT4ge1xuICBsZXQgcmVxdWVzdDogS2V5VmFsdWVbXSA9IFtdO1xuICBpZighdGhpcy5kYXRhPy5sZW5ndGggfHwgZm9yY2UgfHwgKHRoaXMuc2VhcmNoVmFsdWUgYXMgc3RyaW5nKT8ubGVuZ3RoIHx8ICEhKHRoaXMuc2VhcmNoVmFsdWUgYXMgSUZpbHRlclF1ZXJ5KSkge1xuICAgIC8vIChzZWxmLmRhdGEgYXMgTGlzdEl0ZW1bXSkgPSBbXTtcbiAgICBpZighKHRoaXMuc2VhcmNoVmFsdWUgYXMgc3RyaW5nKT8ubGVuZ3RoICYmICEodGhpcy5zZWFyY2hWYWx1ZSBhcyBJRmlsdGVyUXVlcnkpKSB7XG4gICAgICBpZighdGhpcy5zb3VyY2UgJiYgIXRoaXMuZGF0YT8ubGVuZ3RoKSB7XG4gICAgICAgIHRoaXMubG9nZ2VyLmluZm8oJ05vIGRhdGEgYW5kIHNvdXJjZSBwYXNzZWQgdG8gaW5maW5pdGUgbGlzdCcpO1xuICAgICAgICByZXR1cm4gW107XG4gICAgICB9XG5cbiAgICAgIGlmKHRoaXMuc291cmNlIGluc3RhbmNlb2YgRnVuY3Rpb24pXG4gICAgICAgIHJlcXVlc3QgPSBhd2FpdCB0aGlzLnNvdXJjZSgpO1xuXG4gICAgICBpZighQXJyYXkuaXNBcnJheShyZXF1ZXN0KSlcbiAgICAgICAgcmVxdWVzdCA9IHJlcXVlc3Q/LlsncmVzcG9uc2UnXT8uWydkYXRhJ10gfHwgcmVxdWVzdD8uWydyZXN1bHRzJ10gfHwgW107XG4gICAgICB0aGlzLmRhdGEgPSBbLi4uIGF3YWl0IHRoaXMucGFyc2VSZXN1bHQocmVxdWVzdCldO1xuICAgICAgaWYodGhpcy5kYXRhPy5sZW5ndGgpXG4gICAgICAgIHRoaXMuaXRlbXMgPSB0aGlzLnR5cGUgPT09IExpc3RDb21wb25lbnRzVHlwZXMuSU5GSU5JVEUgP1xuICAgICAgICAgICh0aGlzLml0ZW1zIHx8IFtdKS5jb25jYXQoWy4uLnRoaXMuZGF0YS5zbGljZShzdGFydCwgbGltaXQpXSkgOiBbLi4ucmVxdWVzdC5zbGljZShzdGFydCwgbGltaXQpIGFzIEtleVZhbHVlW11dO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmRhdGEgPSB0aGlzLnBhcnNlU2VhcmNoUmVzdWx0cyh0aGlzLmRhdGEgYXMgW10sIHRoaXMuc2VhcmNoVmFsdWUgYXMgc3RyaW5nKTtcbiAgICAgIHRoaXMuaXRlbXMgPSB0aGlzLmRhdGE7XG4gICAgfVxuICB9XG5cbiAgaWYodGhpcy5sb2FkTW9yZURhdGEgJiYgdGhpcy50eXBlID09PSBMaXN0Q29tcG9uZW50c1R5cGVzLlBBR0lOQVRFRClcbiAgICB0aGlzLmdldE1vcmVEYXRhKHRoaXMuZGF0YT8ubGVuZ3RoIHx8IDApO1xuICByZXR1cm4gdGhpcy5kYXRhIHx8IFtdIGFzIEtleVZhbHVlW107XG59XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIEZldGNoZXMgZGF0YSBmcm9tIGEgbW9kZWwgc291cmNlLlxuICogQHN1bW1hcnkgUmV0cmlldmVzIGRhdGEgZnJvbSB0aGUgY29uZmlndXJlZCBtb2RlbCB1c2luZyBpdHMgcGFnaW5hdGlvbiBvciBmaW5kIG1ldGhvZHMsXG4gKiBwcm9jZXNzZXMgaXQsIGFuZCB1cGRhdGVzIHRoZSBjb21wb25lbnQncyBkYXRhIHN0YXRlLiBUaGlzIG1ldGhvZCBoYW5kbGVzIGJvdGggaW5pdGlhbFxuICogZGF0YSBsb2FkaW5nIGFuZCBzdWJzZXF1ZW50IHJlZnJlc2ggb3BlcmF0aW9ucyB3aGVuIHVzaW5nIGEgbW9kZWwgYXMgdGhlIGRhdGEgc291cmNlLlxuICpcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gZm9yY2UgLSBXaGV0aGVyIHRvIGZvcmNlIGEgcmVmcmVzaCBldmVuIGlmIGRhdGEgYWxyZWFkeSBleGlzdHNcbiAqIEBwYXJhbSB7bnVtYmVyfSBzdGFydCAtIFRoZSBzdGFydGluZyBpbmRleCBmb3IgcGFnaW5hdGlvblxuICogQHBhcmFtIHtudW1iZXJ9IGxpbWl0IC0gVGhlIG1heGltdW0gbnVtYmVyIG9mIGl0ZW1zIHRvIHJldHJpZXZlXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxLZXlWYWx1ZVtdPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGZldGNoZWQgZGF0YVxuICpcbiAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gKi9cbmFzeW5jIGdldEZyb21Nb2RlbChmb3JjZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTxLZXlWYWx1ZVtdPiB7XG4gIGxldCBkYXRhID0gWyAuLi4gdGhpcy5kYXRhIHx8IFtdXTtcbiAgbGV0IHJlcXVlc3Q6IEtleVZhbHVlW10gPSBbXTtcblxuICAvLyBnZXR0aW5nIG1vZGVsIHJlcG9zaXRvcnlcbiAgaWYoIXRoaXMuX3JlcG9zaXRvcnkpXG4gICAgdGhpcy5fcmVwb3NpdG9yeSA9IHRoaXMucmVwb3NpdG9yeTtcbiAgY29uc3QgcmVwbyA9IHRoaXMuX3JlcG9zaXRvcnkgYXMgRGVjYWZSZXBvc2l0b3J5PE1vZGVsPjtcbiAgaWYoIXRoaXMuZGF0YT8ubGVuZ3RoIHx8IGZvcmNlIHx8ICh0aGlzLnNlYXJjaFZhbHVlIGFzIHN0cmluZyk/Lmxlbmd0aCB8fCAhISh0aGlzLnNlYXJjaFZhbHVlIGFzIElGaWx0ZXJRdWVyeSkpIHtcbiAgICB0cnkge1xuICAgICBpZighKHRoaXMuc2VhcmNoVmFsdWUgYXMgc3RyaW5nKT8ubGVuZ3RoICYmICEodGhpcy5zZWFyY2hWYWx1ZSBhcyBJRmlsdGVyUXVlcnkpKSB7XG4gICAgICAgICh0aGlzLmRhdGEgYXMgS2V5VmFsdWVbXSkgPSBbXTtcbiAgICAgICAgLy8gY29uc3QgcmF3UXVlcnkgPSB0aGlzLnBhcnNlUXVlcnkoc2VsZi5tb2RlbCBhcyBSZXBvc2l0b3J5PE1vZGVsPiwgc3RhcnQsIGxpbWl0KTtcbiAgICAgICAgLy8gcmVxdWVzdCA9IHRoaXMucGFyc2VSZXN1bHQoYXdhaXQgKHRoaXMubW9kZWwgYXMgYW55KT8ucGFnaW5hdGUoc3RhcnQsIGxpbWl0KSk7XG4gICAgICAgICAgaWYoIXRoaXMucGFnaW5hdG9yKSB7XG4gICAgICAgICAgICB0aGlzLnBhZ2luYXRvciA9IGF3YWl0IHJlcG9cbiAgICAgICAgICAgICAgLnNlbGVjdCgpXG4gICAgICAgICAgICAgIC5vcmRlckJ5KFt0aGlzLnBrIGFzIGtleW9mIE1vZGVsLCB0aGlzLnNvcnREaXJlY3Rpb25dKVxuICAgICAgICAgICAgICAucGFnaW5hdGUodGhpcy5saW1pdCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJlcXVlc3QgPSBhd2FpdCB0aGlzLnBhcnNlUmVzdWx0KHRoaXMucGFnaW5hdG9yKTtcbiAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgaWYoIXRoaXMuaW5kZXhlcylcbiAgICAgICAgICB0aGlzLmluZGV4ZXMgPSAoT2JqZWN0LnZhbHVlcyh0aGlzLm1hcHBlcikgfHwgW3RoaXMucGtdKTtcblxuICAgICAgICBjb25zdCBjb25kaXRpb24gPSB0aGlzLnBhcnNlQ29uZGl0aW9ucyh0aGlzLnNlYXJjaFZhbHVlIGFzIHN0cmluZyB8IG51bWJlciB8IElGaWx0ZXJRdWVyeSk7XG4gICAgICAgIHJlcXVlc3QgPSBhd2FpdCB0aGlzLnBhcnNlUmVzdWx0KGF3YWl0IHJlcG8ucXVlcnkoY29uZGl0aW9uLCAodGhpcy5zb3J0QnkgfHwgdGhpcy5waykgYXMga2V5b2YgTW9kZWwsIHRoaXMuc29ydERpcmVjdGlvbikpO1xuICAgICAgICBkYXRhID0gW107XG4gICAgICB9XG4gICAgICBkYXRhID0gdGhpcy50eXBlID09PSBMaXN0Q29tcG9uZW50c1R5cGVzLklORklOSVRFID8gWy4uLiAoZGF0YSkuY29uY2F0KHJlcXVlc3QpXSA6IFsuLi5yZXF1ZXN0XTtcbiAgICB9IGNhdGNoKGVycm9yOiB1bmtub3duKSB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcigoZXJyb3IgYXMgRXJyb3IpPy5tZXNzYWdlIHx8IGBVbmFibGUgdG8gZmluZCAke3RoaXMubW9kZWx9IG9uIHJlZ2lzdHJ5LiBSZXR1cm4gZW1wdHkgYXJyYXkgZnJvbSBjb21wb25lbnRgKTtcbiAgICB9XG4gIH1cblxuICBpZihkYXRhPy5sZW5ndGgpIHtcbiAgICBpZih0aGlzLnNlYXJjaFZhbHVlKSB7XG4gICAgICB0aGlzLml0ZW1zID0gWy4uLmRhdGFdO1xuICAgICAgaWYodGhpcy5pdGVtcz8ubGVuZ3RoIDw9IHRoaXMubGltaXQpXG4gICAgICAgIHRoaXMubG9hZE1vcmVEYXRhID0gZmFsc2U7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuaXRlbXMgPSBbLi4uZGF0YV07XG4gICAgfVxuICB9XG4gIGlmKHRoaXMudHlwZSA9PT0gTGlzdENvbXBvbmVudHNUeXBlcy5QQUdJTkFURUQgJiYgdGhpcy5wYWdpbmF0b3IpXG4gICAgICB0aGlzLmdldE1vcmVEYXRhKHRoaXMucGFnaW5hdG9yLnRvdGFsKTtcbiAgcmV0dXJuIGRhdGEgfHwgW10gYXMgS2V5VmFsdWVbXTtcbn1cblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gQ29udmVydHMgc2VhcmNoIHZhbHVlcyBvciBmaWx0ZXIgcXVlcmllcyBpbnRvIGRhdGFiYXNlIGNvbmRpdGlvbnMuXG4gKiBAc3VtbWFyeSBUcmFuc2Zvcm1zIHNlYXJjaCBpbnB1dCBvciBjb21wbGV4IGZpbHRlciBxdWVyaWVzIGludG8gQ29uZGl0aW9uIG9iamVjdHNcbiAqIHRoYXQgY2FuIGJlIHVzZWQgZm9yIGRhdGFiYXNlIHF1ZXJ5aW5nLiBIYW5kbGVzIGJvdGggc2ltcGxlIHN0cmluZy9udW1iZXIgc2VhcmNoZXNcbiAqIGFjcm9zcyBpbmRleGVkIGZpZWxkcyBhbmQgY29tcGxleCBmaWx0ZXIgcXVlcmllcyB3aXRoIG11bHRpcGxlIGNyaXRlcmlhLlxuICpcbiAqIEZvciBzaW1wbGUgc2VhcmNoZXMgKHN0cmluZy9udW1iZXIpOlxuICogLSBDcmVhdGVzIGNvbmRpdGlvbnMgdGhhdCBzZWFyY2ggYWNyb3NzIGFsbCBpbmRleGVkIGZpZWxkc1xuICogLSBVc2VzIGVxdWFsaXR5IGZvciBudW1lcmljIHZhbHVlcyBhbmQgcmVnZXggZm9yIHN0cmluZyB2YWx1ZXNcbiAqIC0gQ29tYmluZXMgY29uZGl0aW9ucyB3aXRoIE9SIGxvZ2ljIHRvIHNlYXJjaCBtdWx0aXBsZSBmaWVsZHNcbiAqXG4gKiBGb3IgY29tcGxleCBmaWx0ZXIgcXVlcmllczpcbiAqIC0gUHJvY2Vzc2VzIGVhY2ggZmlsdGVyIGl0ZW0gd2l0aCBpdHMgc3BlY2lmaWMgY29uZGl0aW9uIHR5cGVcbiAqIC0gU3VwcG9ydHMgRXF1YWwsIE5vdCBFcXVhbCwgQ29udGFpbnMsIE5vdCBDb250YWlucywgR3JlYXRlciBUaGFuLCBMZXNzIFRoYW5cbiAqIC0gVXBkYXRlcyBzb3J0IGNvbmZpZ3VyYXRpb24gYmFzZWQgb24gdGhlIGZpbHRlciBxdWVyeVxuICogLSBDb21iaW5lcyBtdWx0aXBsZSBmaWx0ZXIgY29uZGl0aW9ucyB3aXRoIE9SIGxvZ2ljXG4gKlxuICogQHBhcmFtIHtzdHJpbmcgfCBudW1iZXIgfCBJRmlsdGVyUXVlcnl9IHZhbHVlIC0gVGhlIHNlYXJjaCB2YWx1ZSBvciBmaWx0ZXIgcXVlcnkgb2JqZWN0XG4gKiBAcmV0dXJucyB7Q29uZGl0aW9uPE1vZGVsPn0gQSBDb25kaXRpb24gb2JqZWN0IGZvciBkYXRhYmFzZSBxdWVyeWluZ1xuICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAqL1xucGFyc2VDb25kaXRpb25zKHZhbHVlOiBzdHJpbmcgfCBudW1iZXIgfCBJRmlsdGVyUXVlcnkpOiBDb25kaXRpb248TW9kZWw+IHtcbiAgbGV0IF9jb25kaXRpb246IENvbmRpdGlvbjxNb2RlbD47XG4gIGlmKHR5cGVvZiB2YWx1ZSA9PT0gUHJpbWl0aXZlcy5TVFJJTkcgfHwgdHlwZW9mIHZhbHVlID09PSBQcmltaXRpdmVzLk5VTUJFUikge1xuICAgIF9jb25kaXRpb24gPSBDb25kaXRpb24uYXR0cmlidXRlPE1vZGVsPih0aGlzLnBrIGFzIGtleW9mIE1vZGVsKS5lcSghaXNOYU4odmFsdWUgYXMgbnVtYmVyKSA/IE51bWJlcih2YWx1ZSkgOiB2YWx1ZSk7XG4gICAgZm9yIChjb25zdCBpbmRleCBvZiB0aGlzLmluZGV4ZXMpIHtcbiAgICAgICAgaWYoaW5kZXggPT09IHRoaXMucGspXG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIGxldCBvckNvbmRpdGlvbjtcbiAgICAgICAgaWYoIWlzTmFOKHZhbHVlIGFzIG51bWJlcikpIHtcbiAgICAgICAgICBvckNvbmRpdGlvbiA9IENvbmRpdGlvbi5hdHRyaWJ1dGU8TW9kZWw+KGluZGV4IGFzIGtleW9mIE1vZGVsKS5lcShOdW1iZXIodmFsdWUpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBvckNvbmRpdGlvbiA9IENvbmRpdGlvbi5hdHRyaWJ1dGU8TW9kZWw+KGluZGV4IGFzIGtleW9mIE1vZGVsKS5yZWdleHAodmFsdWUgYXMgc3RyaW5nKTtcbiAgICAgICAgfVxuICAgICAgICBfY29uZGl0aW9uID0gX2NvbmRpdGlvbi5vcihvckNvbmRpdGlvbik7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIGNvbnN0IHtxdWVyeSwgc29ydH0gPSB2YWx1ZSBhcyBJRmlsdGVyUXVlcnk7XG4gICAgX2NvbmRpdGlvbiA9IENvbmRpdGlvbi5hdHRyaWJ1dGU8TW9kZWw+KHRoaXMucGsgYXMga2V5b2YgTW9kZWwpLmRpZignbnVsbCcpO1xuXG4gICAgaWYocXVlcnk/Lmxlbmd0aClcbiAgICAgIF9jb25kaXRpb24gPSB1bmRlZmluZWQgYXMgdW5rbm93biBhcyBDb25kaXRpb248TW9kZWw+O1xuXG4gICAgKHF1ZXJ5IHx8IFtdKS5mb3JFYWNoKChpdGVtOiBJRmlsdGVyUXVlcnlJdGVtKSA9PiB7XG4gICAgICBjb25zdCB7dmFsdWUsIGNvbmRpdGlvbiwgaW5kZXh9ID0gaXRlbTtcbiAgICAgIGxldCB2YWwgPSB2YWx1ZSBhcyBzdHJpbmcgfCBudW1iZXI7XG4gICAgICBpZihpbmRleCA9PT0gdGhpcy5wayB8fCAhaXNOYU4odmFsIGFzIG51bWJlcikpXG4gICAgICAgIHZhbCA9IE51bWJlcih2YWwpO1xuICAgICAgbGV0IG9yQ29uZGl0aW9uO1xuICAgICAgc3dpdGNoIChjb25kaXRpb24pIHtcbiAgICAgICAgY2FzZSBcIkVxdWFsXCI6XG4gICAgICAgICAgb3JDb25kaXRpb24gPSBDb25kaXRpb24uYXR0cmlidXRlPE1vZGVsPihpbmRleCBhcyBrZXlvZiBNb2RlbCkuZXEodmFsKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcIk5vdCBFcXVhbFwiOlxuICAgICAgICAgIG9yQ29uZGl0aW9uID0gQ29uZGl0aW9uLmF0dHJpYnV0ZTxNb2RlbD4oaW5kZXggYXMga2V5b2YgTW9kZWwpLmRpZih2YWwpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiTm90IENvbnRhaW5zXCI6XG4gICAgICAgICAgb3JDb25kaXRpb24gPSAhQ29uZGl0aW9uLmF0dHJpYnV0ZTxNb2RlbD4oaW5kZXggYXMga2V5b2YgTW9kZWwpLnJlZ2V4cChuZXcgUmVnRXhwKGBeKD8hLioke3ZhbH0pLiokYCkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiQ29udGFpbnNcIjpcbiAgICAgICAgICBvckNvbmRpdGlvbiA9IENvbmRpdGlvbi5hdHRyaWJ1dGU8TW9kZWw+KGluZGV4IGFzIGtleW9mIE1vZGVsKS5yZWdleHAodmFsIGFzIHN0cmluZyk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJHcmVhdGVyIFRoYW5cIjpcbiAgICAgICAgICBvckNvbmRpdGlvbiA9IENvbmRpdGlvbi5hdHRyaWJ1dGU8TW9kZWw+KGluZGV4IGFzIGtleW9mIE1vZGVsKS5ndGUodmFsKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcIkxlc3MgVGhhblwiOlxuICAgICAgICAgIG9yQ29uZGl0aW9uID0gQ29uZGl0aW9uLmF0dHJpYnV0ZTxNb2RlbD4oaW5kZXggYXMga2V5b2YgTW9kZWwpLmx0ZSh2YWwpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgX2NvbmRpdGlvbiA9ICghX2NvbmRpdGlvbiA/XG4gICAgICAgIG9yQ29uZGl0aW9uIDogX2NvbmRpdGlvbi5hbmQob3JDb25kaXRpb24gYXMgdW5rbm93biBhcyBDb25kaXRpb248TW9kZWw+KSkgYXMgQ29uZGl0aW9uPE1vZGVsPjtcbiAgICB9KTtcblxuICAgIHRoaXMuc29ydEJ5ID0gc29ydD8udmFsdWUgYXMga2V5b2YgTW9kZWwgfHwgdGhpcy5waztcbiAgICB0aGlzLnNvcnREaXJlY3Rpb24gPSBzb3J0Py5kaXJlY3Rpb24gfHwgdGhpcy5zb3J0RGlyZWN0aW9uO1xuICB9XG4gIGNvbnNvbGUubG9nKF9jb25kaXRpb24pO1xuICByZXR1cm4gX2NvbmRpdGlvbiBhcyBDb25kaXRpb248TW9kZWw+O1xuXG59XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIFByb2Nlc3NlcyBxdWVyeSByZXN1bHRzIGludG8gYSBzdGFuZGFyZGl6ZWQgZm9ybWF0LlxuICogQHN1bW1hcnkgSGFuZGxlcyBkaWZmZXJlbnQgcmVzdWx0IGZvcm1hdHMgZnJvbSB2YXJpb3VzIGRhdGEgc291cmNlcywgZXh0cmFjdGluZ1xuICogcGFnaW5hdGlvbiBpbmZvcm1hdGlvbiB3aGVuIGF2YWlsYWJsZSBhbmQgYXBwbHlpbmcgYW55IGNvbmZpZ3VyZWQgZGF0YSBtYXBwaW5nLlxuICogVGhpcyBlbnN1cmVzIGNvbnNpc3RlbnQgZGF0YSBzdHJ1Y3R1cmUgcmVnYXJkbGVzcyBvZiB0aGUgc291cmNlLlxuICpcbiAqIEBwcm90ZWN0ZWRcbiAqIEBwYXJhbSB7S2V5VmFsdWVbXSB8IFBhZ2luYXRvcn0gcmVzdWx0IC0gVGhlIHJhdyBxdWVyeSByZXN1bHRcbiAqIEByZXR1cm5zIHtLZXlWYWx1ZVtdfSBUaGUgcHJvY2Vzc2VkIGFycmF5IG9mIGl0ZW1zXG4gKlxuICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAqL1xucHJvdGVjdGVkIGFzeW5jIHBhcnNlUmVzdWx0KHJlc3VsdDogS2V5VmFsdWVbXSB8IFBhZ2luYXRvcjxNb2RlbD4pOiBQcm9taXNlPEtleVZhbHVlW10+IHtcbiAgaWYoIUFycmF5LmlzQXJyYXkocmVzdWx0KSAmJiAoJ3BhZ2UnIGluIHJlc3VsdCAmJiAndG90YWwnIGluIHJlc3VsdCkpIHtcbiAgICBjb25zdCBwYWdpbmF0b3IgPSByZXN1bHQgYXMgUGFnaW5hdG9yPE1vZGVsPjtcbiAgICByZXN1bHQgPSBhd2FpdCBwYWdpbmF0b3IucGFnZSh0aGlzLnBhZ2UpO1xuICAgIC8vIFRPRE86IENoYWdlIGZvciByZXN1bHQudG90YWw7XG4gICAgdGhpcy5nZXRNb3JlRGF0YShwYWdpbmF0b3IudG90YWwpO1xuICB9IGVsc2Uge1xuICAgIHRoaXMuZ2V0TW9yZURhdGEoKHJlc3VsdCBhcyBLZXlWYWx1ZVtdKT8ubGVuZ3RoIHx8IDApO1xuICB9XG4gIHJldHVybiAoT2JqZWN0LmtleXModGhpcy5tYXBwZXIgfHwge30pLmxlbmd0aCkgP1xuICAgIHRoaXMubWFwUmVzdWx0cyhyZXN1bHQpIDogcmVzdWx0O1xufVxuXG4vKipcbiAqIEBkZXNjcmlwdGlvbiBVcGRhdGVzIHBhZ2luYXRpb24gc3RhdGUgYmFzZWQgb24gZGF0YSBsZW5ndGguXG4gKiBAc3VtbWFyeSBDYWxjdWxhdGVzIHdoZXRoZXIgbW9yZSBkYXRhIGlzIGF2YWlsYWJsZSBhbmQgaG93IG1hbnkgcGFnZXMgZXhpc3RcbiAqIGJhc2VkIG9uIHRoZSB0b3RhbCBudW1iZXIgb2YgaXRlbXMgYW5kIHRoZSBjb25maWd1cmVkIGxpbWl0IHBlciBwYWdlLlxuICogVGhpcyBpbmZvcm1hdGlvbiBpcyB1c2VkIHRvIGNvbnRyb2wgcGFnaW5hdGlvbiBVSSBhbmQgaW5maW5pdGUgc2Nyb2xsaW5nIGJlaGF2aW9yLlxuICpcbiAqIEBwYXJhbSB7bnVtYmVyfSBsZW5ndGggLSBUaGUgdG90YWwgbnVtYmVyIG9mIGl0ZW1zIGF2YWlsYWJsZVxuICogQHJldHVybnMge3ZvaWR9XG4gKlxuICogQG1lbWJlck9mIExpc3RDb21wb25lbnRcbiAqL1xuZ2V0TW9yZURhdGEobGVuZ3RoOiBudW1iZXIpOiB2b2lkIHtcbiAgaWYodGhpcy50eXBlID09PSBMaXN0Q29tcG9uZW50c1R5cGVzLklORklOSVRFKSB7XG4gICAgaWYodGhpcy5wYWdpbmF0b3IpXG4gICAgICBsZW5ndGggPSBsZW5ndGggKiB0aGlzLmxpbWl0O1xuICAgIGlmKGxlbmd0aCA8PSB0aGlzLmxpbWl0KSB7XG4gICAgICB0aGlzLmxvYWRNb3JlRGF0YSA9IGZhbHNlO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnBhZ2VzID0gTWF0aC5mbG9vcihsZW5ndGggLyB0aGlzLmxpbWl0KTtcbiAgICAgIGlmKCh0aGlzLnBhZ2VzICogdGhpcy5saW1pdCkgPCBsZW5ndGgpXG4gICAgICAgIHRoaXMucGFnZXMgKz0gMTtcbiAgICAgIGlmKHRoaXMucGFnZXMgPT09IDEpXG4gICAgICAgIHRoaXMubG9hZE1vcmVEYXRhID0gZmFsc2U7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIHRoaXMucGFnZXMgPSBsZW5ndGg7XG4gICAgaWYodGhpcy5wYWdlcyA9PT0gMSlcbiAgICAgIHRoaXMubG9hZE1vcmVEYXRhID0gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gTWFwcyBhIHNpbmdsZSBpdGVtIHVzaW5nIHRoZSBjb25maWd1cmVkIG1hcHBlci5cbiAqIEBzdW1tYXJ5IFRyYW5zZm9ybXMgYSBkYXRhIGl0ZW0gYWNjb3JkaW5nIHRvIHRoZSBtYXBwaW5nIGNvbmZpZ3VyYXRpb24sXG4gKiBleHRyYWN0aW5nIG5lc3RlZCBwcm9wZXJ0aWVzIGFuZCBmb3JtYXR0aW5nIHZhbHVlcyBhcyBuZWVkZWQuIFRoaXMgYWxsb3dzXG4gKiB0aGUgY29tcG9uZW50IHRvIGRpc3BsYXkgZGF0YSBpbiBhIGZvcm1hdCBkaWZmZXJlbnQgZnJvbSBob3cgaXQncyBzdG9yZWQuXG4gKlxuICogQHByb3RlY3RlZFxuICogQHBhcmFtIHtLZXlWYWx1ZX0gaXRlbSAtIFRoZSBpdGVtIHRvIG1hcFxuICogQHBhcmFtIHtLZXlWYWx1ZX0gbWFwcGVyIC0gVGhlIG1hcHBpbmcgY29uZmlndXJhdGlvblxuICogQHBhcmFtIHtLZXlWYWx1ZX0gW3Byb3BzXSAtIEFkZGl0aW9uYWwgcHJvcGVydGllcyB0byBpbmNsdWRlXG4gKiBAcmV0dXJucyB7S2V5VmFsdWV9IFRoZSBtYXBwZWQgaXRlbVxuICpcbiAqIEBtZW1iZXJPZiBMaXN0Q29tcG9uZW50XG4gKi9cbnByb3RlY3RlZCBpdGVtTWFwcGVyKGl0ZW06IEtleVZhbHVlLCBtYXBwZXI6IEtleVZhbHVlLCBwcm9wcz86IEtleVZhbHVlKTogS2V5VmFsdWUge1xuICByZXR1cm4gT2JqZWN0LmVudHJpZXMobWFwcGVyKS5yZWR1Y2UoKGFjY3VtOiBLZXlWYWx1ZSwgW2tleSwgdmFsdWVdKSA9PiB7XG4gICAgY29uc3QgYXJyYXlWYWx1ZSA9IHZhbHVlLnNwbGl0KCcuJyk7XG4gICAgaWYgKCF2YWx1ZSkge1xuICAgICAgYWNjdW1ba2V5XSA9IHZhbHVlO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoYXJyYXlWYWx1ZS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgdmFsdWUgPSBpdGVtPy5bdmFsdWVdIHx8IHZhbHVlO1xuICAgICAgICBpZihpc1ZhbGlkRGF0ZSh2YWx1ZSkpXG4gICAgICAgICAgdmFsdWUgPSBgJHtmb3JtYXREYXRlKHZhbHVlKX1gO1xuICAgICAgICBhY2N1bVtrZXldID0gdmFsdWU7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsZXQgdmFsO1xuXG4gICAgICAgIGZvciAoY29uc3QgX3ZhbHVlIG9mIGFycmF5VmFsdWUpXG4gICAgICAgICAgdmFsID0gIXZhbFxuICAgICAgICAgICAgPyBpdGVtW192YWx1ZV1cbiAgICAgICAgICAgIDogKHR5cGVvZiB2YWwgPT09ICdzdHJpbmcnID8gSlNPTi5wYXJzZSh2YWwpIDogdmFsKVtfdmFsdWVdO1xuXG5cbiAgICAgICAgaWYgKGlzVmFsaWREYXRlKG5ldyBEYXRlKHZhbCkpKVxuICAgICAgICAgIHZhbCA9IGAke2Zvcm1hdERhdGUodmFsKX1gO1xuXG4gICAgICAgIGFjY3VtW2tleV0gPSB2YWwgPT09IG51bGwgfHwgdmFsID09PSB1bmRlZmluZWQgPyB2YWx1ZSA6IHZhbDtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oe30sIHByb3BzIHx8IHt9LCBhY2N1bSk7XG4gIH0sIHt9KTtcbn1cblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gTWFwcyBhbGwgcmVzdWx0IGl0ZW1zIHVzaW5nIHRoZSBjb25maWd1cmVkIG1hcHBlci5cbiAqIEBzdW1tYXJ5IEFwcGxpZXMgdGhlIGl0ZW1NYXBwZXIgdG8gZWFjaCBpdGVtIGluIHRoZSByZXN1bHQgc2V0LCBhZGRpbmdcbiAqIGNvbW1vbiBwcm9wZXJ0aWVzIGxpa2Ugb3BlcmF0aW9ucyBhbmQgcm91dGUgaW5mb3JtYXRpb24uIFRoaXMgdHJhbnNmb3Jtc1xuICogdGhlIHJhdyBkYXRhIGludG8gdGhlIGZvcm1hdCBleHBlY3RlZCBieSB0aGUgbGlzdCBpdGVtIGNvbXBvbmVudHMuXG4gKlxuICogQHBhcmFtIHtLZXlWYWx1ZVtdfSBkYXRhIC0gVGhlIGFycmF5IG9mIGl0ZW1zIHRvIG1hcFxuICogQHJldHVybnMge0tleVZhbHVlW119IFRoZSBhcnJheSBvZiBtYXBwZWQgaXRlbXNcbiAqXG4gKiBAbWVtYmVyT2YgTGlzdENvbXBvbmVudFxuICovXG4gIG1hcFJlc3VsdHMoZGF0YTogS2V5VmFsdWVbXSk6IEtleVZhbHVlW10ge1xuICAgIGlmKCFkYXRhIHx8ICFkYXRhLmxlbmd0aClcbiAgICAgIHJldHVybiBbXTtcbiAgICAvLyBwYXNzaW5nIHVpZCBhcyBwcm9wIHRvIG1hcHBlclxuICAgIHRoaXMubWFwcGVyID0gey4uLiB0aGlzLm1hcHBlciwgLi4uIHt1aWQ6IHRoaXMucGt9fTtcbiAgICBjb25zdCBwcm9wcyA9IE9iamVjdC5hc3NpZ24oe1xuICAgICAgb3BlcmF0aW9uczogdGhpcy5vcGVyYXRpb25zLFxuICAgICAgcm91dGU6IHRoaXMucm91dGUsXG4gICAgICAuLi4gIE9iamVjdC5rZXlzKHRoaXMuaXRlbSkucmVkdWNlKChhY2M6IEtleVZhbHVlLCBrZXk6IHN0cmluZykgPT4ge1xuICAgICAgICBhY2Nba2V5XSA9IHRoaXMuaXRlbVtrZXldO1xuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSwge30pLFxuICAgICAgLy8gLi4uICghdGhpcy5pdGVtLnJlbmRlciA/IHt9IDogIE9iamVjdC5rZXlzKHRoaXMuaXRlbSkucmVkdWNlKChhY2M6IEtleVZhbHVlLCBrZXk6IHN0cmluZykgPT4ge1xuICAgICAgLy8gICBhY2Nba2V5XSA9IHRoaXMuaXRlbVtrZXkgYXMga2V5b2YgSUxpc3RJdGVtUHJvcF07XG4gICAgICAvLyAgIHJldHVybiBhY2M7XG4gICAgICAvLyB9LCB7fSkpXG4gICAgfSk7XG4gICAgcmV0dXJuIGRhdGEucmVkdWNlKChhY2N1bTogS2V5VmFsdWVbXSwgY3VycikgPT4ge1xuICAgICAgICBhY2N1bS5wdXNoKHsuLi4gdGhpcy5pdGVtTWFwcGVyKGN1cnIsIHRoaXMubWFwcGVyIGFzIEtleVZhbHVlLCBwcm9wcyksIC4uLiB7cGs6IHRoaXMucGt9fSk7XG4gICAgICAgIHJldHVybiBhY2N1bTtcbiAgICB9LCBbXSk7XG4gIH1cbn1cblxuIiwiXG5AaWYoc2hvd1JlZnJlc2hlcikge1xuICA8aW9uLXJlZnJlc2hlciBzbG90PVwiZml4ZWRcIiBbcHVsbEZhY3Rvcl09XCIxXCIgW3B1bGxNaW5dPVwiMTAwXCIgW3B1bGxNYXhdPVwiMjAwXCIgKGlvblJlZnJlc2gpPVwiaGFuZGxlUmVmcmVzaCgkZXZlbnQpXCI+XG4gICAgPGlvbi1yZWZyZXNoZXItY29udGVudCAvPlxuICA8L2lvbi1yZWZyZXNoZXI+XG59XG5cbkBpZihzaG93U2VhcmNoYmFyKSB7XG4gIEBpZihtb2RlbCAmJiBlbmFibGVGaWx0ZXIpIHtcbiAgICA8bmd4LWRlY2FmLWZpbHRlclxuICAgICAgW21vZGVsXT1cIm1vZGVsXCJcbiAgICAgIFtzb3J0RGlyZWN0aW9uXT1cInNvcnREaXJlY3Rpb25cIlxuICAgICAgW2Rpc2FibGVTb3J0XT1cImRpc2FibGVTb3J0XCJcbiAgICAgIChmaWx0ZXJFdmVudCk9XCJoYW5kbGVGaWx0ZXIoJGV2ZW50KVwiXG4gICAgICAoc2VhcmNoRXZlbnQpPVwiaGFuZGxlU2VhcmNoKCRldmVudClcIlxuICAgIC8+XG4gIH0gQGVsc2Uge1xuICAgIDxuZ3gtZGVjYWYtc2VhcmNoYmFyIFtlbWl0RXZlbnRUb1dpbmRvd109XCJmYWxzZVwiIFtkZWJvdW5jZV09XCI1MDBcIiAoc2VhcmNoRXZlbnQpPVwiaGFuZGxlU2VhcmNoKCRldmVudClcIiAvPlxuICB9XG59XG5cbkBpZihkYXRhPy5sZW5ndGgpIHtcbiAgPGlvbi1saXN0IFtpbnNldF09XCJpbnNldFwiIFtsaW5lc109XCJsaW5lc1wiICNjb21wb25lbnQ+XG4gICAgQGlmKGl0ZW0/LnRhZykge1xuICAgICAgQGZvcihjaGlsZCBvZiBpdGVtczsgdHJhY2sgdHJhY2tJdGVtRm4oJGluZGV4LCBjaGlsZCkpIHtcbiAgICAgICAgPG5neC1kZWNhZi1jb21wb25lbnQtcmVuZGVyZXJcbiAgICAgICAgICBbdGFnXT1cIml0ZW0udGFnXCJcbiAgICAgICAgICAobGlzdGVuRXZlbnQpPVwiaGFuZGxlRXZlbnQoJGV2ZW50KVwiXG4gICAgICAgICAgW2dsb2JhbHNdPSd7XG4gICAgICAgICAgICBpdGVtOiBjaGlsZCxcbiAgICAgICAgICAgIG1hcHBlcjogbWFwcGVyLFxuICAgICAgICAgICAgcm91dGU6IHJvdXRlXG4gICAgICAgICAgfSc+XG4gICAgICAgICAgPC9uZ3gtZGVjYWYtY29tcG9uZW50LXJlbmRlcmVyPlxuICAgICAgICB9XG4gICAgfSBAZWxzZSB7XG4gICAgICA8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG4gICAgfVxuICA8L2lvbi1saXN0PlxuXG4gIEBpZihsb2FkTW9yZURhdGEpIHtcbiAgICBAaWYocGFnZXMgPiAwICYmIHR5cGUgPT09ICdwYWdpbmF0ZWQnICYmICFzZWFyY2hWYWx1ZT8ubGVuZ3RoKSB7XG4gICAgICA8bmd4LWRlY2FmLXBhZ2luYXRpb25cbiAgICAgICAgW3RvdGFsUGFnZXNdPVwicGFnZXNcIlxuICAgICAgICBbY3VycmVudF09XCJwYWdlXCJcbiAgICAgICAgKGNsaWNrRXZlbnQpPVwiaGFuZGxlUGFnaW5hdGUoJGV2ZW50KVwiXG4gICAgICAvPlxuXG4gICAgfSBAZWxzZSB7XG4gICAgICA8aW9uLWluZmluaXRlLXNjcm9sbFxuICAgICAgICBbY2xhc3NdPVwic2VhcmNoVmFsdWU/Lmxlbmd0aCA/ICdkY2YtaGlkZGVuJyA6ICcnXCJcbiAgICAgICAgW3Bvc2l0aW9uXT1cInNjcm9sbFBvc2l0aW9uXCJcbiAgICAgICAgW3RocmVzaG9sZF09XCJzY3JvbGxUaHJlc2hvbGRcIlxuICAgICAgICAoaW9uSW5maW5pdGUpPVwiaGFuZGxlUmVmcmVzaCgkZXZlbnQpXCI+XG4gICAgICAgIDxpb24taW5maW5pdGUtc2Nyb2xsLWNvbnRlbnQgW2xvYWRpbmdTcGlubmVyXT1cImxvYWRpbmdTcGlubmVyXCIgW2xvYWRpbmdUZXh0XT1cImxvYWRpbmdUZXh0XCIgLz5cbiAgICAgIDwvaW9uLWluZmluaXRlLXNjcm9sbD5cbiAgICB9XG4gIH1cbn0gQGVsc2Uge1xuICBAaWYocmVmcmVzaGluZykge1xuICAgIDxpb24taXRlbSAqbmdGb3I9XCJsZXQgc2tsIG9mIHNrZWxldG9uRGF0YVwiPlxuICAgICAgPGlvbi10aHVtYm5haWwgc2xvdD1cInN0YXJ0XCI+XG4gICAgICAgIDxpb24tc2tlbGV0b24tdGV4dCBbYW5pbWF0ZWRdPVwidHJ1ZVwiPjwvaW9uLXNrZWxldG9uLXRleHQ+XG4gICAgICA8L2lvbi10aHVtYm5haWw+XG4gICAgICA8aW9uLWxhYmVsPlxuICAgICAgICA8aW9uLXNrZWxldG9uLXRleHQgW2FuaW1hdGVkXT1cInRydWVcIj48L2lvbi1za2VsZXRvbi10ZXh0PlxuICAgICAgICA8aW9uLXRleHQgY2xhc3M9XCJkYXRlXCIgc3R5bGU9XCJ3aWR0aDogMjAlO1wiPjxpb24tc2tlbGV0b24tdGV4dCBbYW5pbWF0ZWRdPVwidHJ1ZVwiPjwvaW9uLXNrZWxldG9uLXRleHQ+PC9pb24tdGV4dD5cbiAgICAgIDwvaW9uLWxhYmVsPlxuICAgIDwvaW9uLWl0ZW0+XG4gIH0gQGVsc2Uge1xuICAgIEBpZighc2VhcmNoVmFsdWU/Lmxlbmd0aCkge1xuICAgICAgPG5neC1kZWNhZi1lbXB0eS1zdGF0ZVxuICAgICAgICBbdGl0bGVdPVwiKGxvY2FsZSArICcuJysgZW1wdHkudGl0bGUpIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgW3N1YnRpdGxlXT1cIihsb2NhbGUgKyAnLicrIGVtcHR5LnN1YnRpdGxlKSB8IHRyYW5zbGF0ZVwiXG4gICAgICAgIFtidXR0b25UZXh0XT1cImVtcHR5LnNob3dCdXR0b24gPyAobG9jYWxlICsgJy4nKyBlbXB0eS5idXR0b24gfCB0cmFuc2xhdGUpIDogJydcIlxuICAgICAgICBbYnV0dG9uTGlua109XCJlbXB0eS5zaG93QnV0dG9uID8gZW1wdHkucm91dGUgOiAnJ1wiXG4gICAgICAvPlxuICAgIH0gQGVsc2Uge1xuICAgICAgPG5neC1kZWNhZi1lbXB0eS1zdGF0ZVxuICAgICAgICBpY29uPVwic2VhcmNoLW91dGxpbmVcIlxuICAgICAgICBuZ0NsYXNzPVwiZW1wdHktc2VhcmNoXCJcbiAgICAgICAgW3RyYW5zbGF0YWJsZV09XCJ0cnVlXCJcbiAgICAgICAgdGl0bGU9XCJzZWFyY2gudGl0bGVcIlxuICAgICAgICBzdWJ0aXRsZT1cInNlYXJjaC5zdWJ0aXRsZVwiXG4gICAgICAgIFtzZWFyY2hWYWx1ZV09XCJzZWFyY2hWYWx1ZVwiXG4gICAgICAvPlxuICAgIH1cbiAgfVxufVxuXG4iXX0=
|