UNPKG

55.1 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright Google LLC All Rights Reserved.
4 *
5 * Use of this source code is governed by an MIT-style license that can be
6 * found in the LICENSE file at https://angular.io/license
7 */
8import { AbstractControl, assertAllValuesPresent, assertControlPresent, pickAsyncValidators, pickValidators, } from './abstract_model';
9/**
10 * Tracks the value and validity state of an array of `FormControl`,
11 * `FormGroup` or `FormArray` instances.
12 *
13 * A `FormArray` aggregates the values of each child `FormControl` into an array.
14 * It calculates its status by reducing the status values of its children. For example, if one of
15 * the controls in a `FormArray` is invalid, the entire array becomes invalid.
16 *
17 * `FormArray` accepts one generic argument, which is the type of the controls inside.
18 * If you need a heterogenous array, use {@link UntypedFormArray}.
19 *
20 * `FormArray` is one of the four fundamental building blocks used to define forms in Angular,
21 * along with `FormControl`, `FormGroup`, and `FormRecord`.
22 *
23 * @usageNotes
24 *
25 * ### Create an array of form controls
26 *
27 * ```
28 * const arr = new FormArray([
29 * new FormControl('Nancy', Validators.minLength(2)),
30 * new FormControl('Drew'),
31 * ]);
32 *
33 * console.log(arr.value); // ['Nancy', 'Drew']
34 * console.log(arr.status); // 'VALID'
35 * ```
36 *
37 * ### Create a form array with array-level validators
38 *
39 * You include array-level validators and async validators. These come in handy
40 * when you want to perform validation that considers the value of more than one child
41 * control.
42 *
43 * The two types of validators are passed in separately as the second and third arg
44 * respectively, or together as part of an options object.
45 *
46 * ```
47 * const arr = new FormArray([
48 * new FormControl('Nancy'),
49 * new FormControl('Drew')
50 * ], {validators: myValidator, asyncValidators: myAsyncValidator});
51 * ```
52 *
53 * ### Set the updateOn property for all controls in a form array
54 *
55 * The options object is used to set a default value for each child
56 * control's `updateOn` property. If you set `updateOn` to `'blur'` at the
57 * array level, all child controls default to 'blur', unless the child
58 * has explicitly specified a different `updateOn` value.
59 *
60 * ```ts
61 * const arr = new FormArray([
62 * new FormControl()
63 * ], {updateOn: 'blur'});
64 * ```
65 *
66 * ### Adding or removing controls from a form array
67 *
68 * To change the controls in the array, use the `push`, `insert`, `removeAt` or `clear` methods
69 * in `FormArray` itself. These methods ensure the controls are properly tracked in the
70 * form's hierarchy. Do not modify the array of `AbstractControl`s used to instantiate
71 * the `FormArray` directly, as that result in strange and unexpected behavior such
72 * as broken change detection.
73 *
74 * @publicApi
75 */
76export class FormArray extends AbstractControl {
77 /**
78 * Creates a new `FormArray` instance.
79 *
80 * @param controls An array of child controls. Each child control is given an index
81 * where it is registered.
82 *
83 * @param validatorOrOpts A synchronous validator function, or an array of
84 * such functions, or an `AbstractControlOptions` object that contains validation functions
85 * and a validation trigger.
86 *
87 * @param asyncValidator A single async validator or array of async validator functions
88 *
89 */
90 constructor(controls, validatorOrOpts, asyncValidator) {
91 super(pickValidators(validatorOrOpts), pickAsyncValidators(asyncValidator, validatorOrOpts));
92 this.controls = controls;
93 this._initObservables();
94 this._setUpdateStrategy(validatorOrOpts);
95 this._setUpControls();
96 this.updateValueAndValidity({
97 onlySelf: true,
98 // If `asyncValidator` is present, it will trigger control status change from `PENDING` to
99 // `VALID` or `INVALID`.
100 // The status should be broadcasted via the `statusChanges` observable, so we set `emitEvent`
101 // to `true` to allow that during the control creation process.
102 emitEvent: !!this.asyncValidator,
103 });
104 }
105 /**
106 * Get the `AbstractControl` at the given `index` in the array.
107 *
108 * @param index Index in the array to retrieve the control. If `index` is negative, it will wrap
109 * around from the back, and if index is greatly negative (less than `-length`), the result is
110 * undefined. This behavior is the same as `Array.at(index)`.
111 */
112 at(index) {
113 return this.controls[this._adjustIndex(index)];
114 }
115 /**
116 * Insert a new `AbstractControl` at the end of the array.
117 *
118 * @param control Form control to be inserted
119 * @param options Specifies whether this FormArray instance should emit events after a new
120 * control is added.
121 * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
122 * `valueChanges` observables emit events with the latest status and value when the control is
123 * inserted. When false, no events are emitted.
124 */
125 push(control, options = {}) {
126 this.controls.push(control);
127 this._registerControl(control);
128 this.updateValueAndValidity({ emitEvent: options.emitEvent });
129 this._onCollectionChange();
130 }
131 /**
132 * Insert a new `AbstractControl` at the given `index` in the array.
133 *
134 * @param index Index in the array to insert the control. If `index` is negative, wraps around
135 * from the back. If `index` is greatly negative (less than `-length`), prepends to the array.
136 * This behavior is the same as `Array.splice(index, 0, control)`.
137 * @param control Form control to be inserted
138 * @param options Specifies whether this FormArray instance should emit events after a new
139 * control is inserted.
140 * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
141 * `valueChanges` observables emit events with the latest status and value when the control is
142 * inserted. When false, no events are emitted.
143 */
144 insert(index, control, options = {}) {
145 this.controls.splice(index, 0, control);
146 this._registerControl(control);
147 this.updateValueAndValidity({ emitEvent: options.emitEvent });
148 }
149 /**
150 * Remove the control at the given `index` in the array.
151 *
152 * @param index Index in the array to remove the control. If `index` is negative, wraps around
153 * from the back. If `index` is greatly negative (less than `-length`), removes the first
154 * element. This behavior is the same as `Array.splice(index, 1)`.
155 * @param options Specifies whether this FormArray instance should emit events after a
156 * control is removed.
157 * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
158 * `valueChanges` observables emit events with the latest status and value when the control is
159 * removed. When false, no events are emitted.
160 */
161 removeAt(index, options = {}) {
162 // Adjust the index, then clamp it at no less than 0 to prevent undesired underflows.
163 let adjustedIndex = this._adjustIndex(index);
164 if (adjustedIndex < 0)
165 adjustedIndex = 0;
166 if (this.controls[adjustedIndex])
167 this.controls[adjustedIndex]._registerOnCollectionChange(() => { });
168 this.controls.splice(adjustedIndex, 1);
169 this.updateValueAndValidity({ emitEvent: options.emitEvent });
170 }
171 /**
172 * Replace an existing control.
173 *
174 * @param index Index in the array to replace the control. If `index` is negative, wraps around
175 * from the back. If `index` is greatly negative (less than `-length`), replaces the first
176 * element. This behavior is the same as `Array.splice(index, 1, control)`.
177 * @param control The `AbstractControl` control to replace the existing control
178 * @param options Specifies whether this FormArray instance should emit events after an
179 * existing control is replaced with a new one.
180 * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
181 * `valueChanges` observables emit events with the latest status and value when the control is
182 * replaced with a new one. When false, no events are emitted.
183 */
184 setControl(index, control, options = {}) {
185 // Adjust the index, then clamp it at no less than 0 to prevent undesired underflows.
186 let adjustedIndex = this._adjustIndex(index);
187 if (adjustedIndex < 0)
188 adjustedIndex = 0;
189 if (this.controls[adjustedIndex])
190 this.controls[adjustedIndex]._registerOnCollectionChange(() => { });
191 this.controls.splice(adjustedIndex, 1);
192 if (control) {
193 this.controls.splice(adjustedIndex, 0, control);
194 this._registerControl(control);
195 }
196 this.updateValueAndValidity({ emitEvent: options.emitEvent });
197 this._onCollectionChange();
198 }
199 /**
200 * Length of the control array.
201 */
202 get length() {
203 return this.controls.length;
204 }
205 /**
206 * Sets the value of the `FormArray`. It accepts an array that matches
207 * the structure of the control.
208 *
209 * This method performs strict checks, and throws an error if you try
210 * to set the value of a control that doesn't exist or if you exclude the
211 * value of a control.
212 *
213 * @usageNotes
214 * ### Set the values for the controls in the form array
215 *
216 * ```
217 * const arr = new FormArray([
218 * new FormControl(),
219 * new FormControl()
220 * ]);
221 * console.log(arr.value); // [null, null]
222 *
223 * arr.setValue(['Nancy', 'Drew']);
224 * console.log(arr.value); // ['Nancy', 'Drew']
225 * ```
226 *
227 * @param value Array of values for the controls
228 * @param options Configure options that determine how the control propagates changes and
229 * emits events after the value changes
230 *
231 * * `onlySelf`: When true, each change only affects this control, and not its parent. Default
232 * is false.
233 * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
234 * `valueChanges`
235 * observables emit events with the latest status and value when the control value is updated.
236 * When false, no events are emitted.
237 * The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
238 * updateValueAndValidity} method.
239 */
240 setValue(value, options = {}) {
241 assertAllValuesPresent(this, false, value);
242 value.forEach((newValue, index) => {
243 assertControlPresent(this, false, index);
244 this.at(index).setValue(newValue, { onlySelf: true, emitEvent: options.emitEvent });
245 });
246 this.updateValueAndValidity(options);
247 }
248 /**
249 * Patches the value of the `FormArray`. It accepts an array that matches the
250 * structure of the control, and does its best to match the values to the correct
251 * controls in the group.
252 *
253 * It accepts both super-sets and sub-sets of the array without throwing an error.
254 *
255 * @usageNotes
256 * ### Patch the values for controls in a form array
257 *
258 * ```
259 * const arr = new FormArray([
260 * new FormControl(),
261 * new FormControl()
262 * ]);
263 * console.log(arr.value); // [null, null]
264 *
265 * arr.patchValue(['Nancy']);
266 * console.log(arr.value); // ['Nancy', null]
267 * ```
268 *
269 * @param value Array of latest values for the controls
270 * @param options Configure options that determine how the control propagates changes and
271 * emits events after the value changes
272 *
273 * * `onlySelf`: When true, each change only affects this control, and not its parent. Default
274 * is false.
275 * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
276 * `valueChanges` observables emit events with the latest status and value when the control
277 * value is updated. When false, no events are emitted. The configuration options are passed to
278 * the {@link AbstractControl#updateValueAndValidity updateValueAndValidity} method.
279 */
280 patchValue(value, options = {}) {
281 // Even though the `value` argument type doesn't allow `null` and `undefined` values, the
282 // `patchValue` can be called recursively and inner data structures might have these values,
283 // so we just ignore such cases when a field containing FormArray instance receives `null` or
284 // `undefined` as a value.
285 if (value == null /* both `null` and `undefined` */)
286 return;
287 value.forEach((newValue, index) => {
288 if (this.at(index)) {
289 this.at(index).patchValue(newValue, { onlySelf: true, emitEvent: options.emitEvent });
290 }
291 });
292 this.updateValueAndValidity(options);
293 }
294 /**
295 * Resets the `FormArray` and all descendants are marked `pristine` and `untouched`, and the
296 * value of all descendants to null or null maps.
297 *
298 * You reset to a specific form state by passing in an array of states
299 * that matches the structure of the control. The state is a standalone value
300 * or a form state object with both a value and a disabled status.
301 *
302 * @usageNotes
303 * ### Reset the values in a form array
304 *
305 * ```ts
306 * const arr = new FormArray([
307 * new FormControl(),
308 * new FormControl()
309 * ]);
310 * arr.reset(['name', 'last name']);
311 *
312 * console.log(arr.value); // ['name', 'last name']
313 * ```
314 *
315 * ### Reset the values in a form array and the disabled status for the first control
316 *
317 * ```
318 * arr.reset([
319 * {value: 'name', disabled: true},
320 * 'last'
321 * ]);
322 *
323 * console.log(arr.value); // ['last']
324 * console.log(arr.at(0).status); // 'DISABLED'
325 * ```
326 *
327 * @param value Array of values for the controls
328 * @param options Configure options that determine how the control propagates changes and
329 * emits events after the value changes
330 *
331 * * `onlySelf`: When true, each change only affects this control, and not its parent. Default
332 * is false.
333 * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
334 * `valueChanges`
335 * observables emit events with the latest status and value when the control is reset.
336 * When false, no events are emitted.
337 * The configuration options are passed to the {@link AbstractControl#updateValueAndValidity
338 * updateValueAndValidity} method.
339 */
340 reset(value = [], options = {}) {
341 this._forEachChild((control, index) => {
342 control.reset(value[index], { onlySelf: true, emitEvent: options.emitEvent });
343 });
344 this._updatePristine(options, this);
345 this._updateTouched(options, this);
346 this.updateValueAndValidity(options);
347 }
348 /**
349 * The aggregate value of the array, including any disabled controls.
350 *
351 * Reports all values regardless of disabled status.
352 */
353 getRawValue() {
354 return this.controls.map((control) => control.getRawValue());
355 }
356 /**
357 * Remove all controls in the `FormArray`.
358 *
359 * @param options Specifies whether this FormArray instance should emit events after all
360 * controls are removed.
361 * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and
362 * `valueChanges` observables emit events with the latest status and value when all controls
363 * in this FormArray instance are removed. When false, no events are emitted.
364 *
365 * @usageNotes
366 * ### Remove all elements from a FormArray
367 *
368 * ```ts
369 * const arr = new FormArray([
370 * new FormControl(),
371 * new FormControl()
372 * ]);
373 * console.log(arr.length); // 2
374 *
375 * arr.clear();
376 * console.log(arr.length); // 0
377 * ```
378 *
379 * It's a simpler and more efficient alternative to removing all elements one by one:
380 *
381 * ```ts
382 * const arr = new FormArray([
383 * new FormControl(),
384 * new FormControl()
385 * ]);
386 *
387 * while (arr.length) {
388 * arr.removeAt(0);
389 * }
390 * ```
391 */
392 clear(options = {}) {
393 if (this.controls.length < 1)
394 return;
395 this._forEachChild((control) => control._registerOnCollectionChange(() => { }));
396 this.controls.splice(0);
397 this.updateValueAndValidity({ emitEvent: options.emitEvent });
398 }
399 /**
400 * Adjusts a negative index by summing it with the length of the array. For very negative
401 * indices, the result may remain negative.
402 * @internal
403 */
404 _adjustIndex(index) {
405 return index < 0 ? index + this.length : index;
406 }
407 /** @internal */
408 _syncPendingControls() {
409 let subtreeUpdated = this.controls.reduce((updated, child) => {
410 return child._syncPendingControls() ? true : updated;
411 }, false);
412 if (subtreeUpdated)
413 this.updateValueAndValidity({ onlySelf: true });
414 return subtreeUpdated;
415 }
416 /** @internal */
417 _forEachChild(cb) {
418 this.controls.forEach((control, index) => {
419 cb(control, index);
420 });
421 }
422 /** @internal */
423 _updateValue() {
424 this.value = this.controls
425 .filter((control) => control.enabled || this.disabled)
426 .map((control) => control.value);
427 }
428 /** @internal */
429 _anyControls(condition) {
430 return this.controls.some((control) => control.enabled && condition(control));
431 }
432 /** @internal */
433 _setUpControls() {
434 this._forEachChild((control) => this._registerControl(control));
435 }
436 /** @internal */
437 _allControlsDisabled() {
438 for (const control of this.controls) {
439 if (control.enabled)
440 return false;
441 }
442 return this.controls.length > 0 || this.disabled;
443 }
444 _registerControl(control) {
445 control.setParent(this);
446 control._registerOnCollectionChange(this._onCollectionChange);
447 }
448 /** @internal */
449 _find(name) {
450 return this.at(name) ?? null;
451 }
452}
453export const UntypedFormArray = FormArray;
454/**
455 * @description
456 * Asserts that the given control is an instance of `FormArray`
457 *
458 * @publicApi
459 */
460export const isFormArray = (control) => control instanceof FormArray;
461//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9ybV9hcnJheS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2Zvcm1zL3NyYy9tb2RlbC9mb3JtX2FycmF5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQU1ILE9BQU8sRUFDTCxlQUFlLEVBRWYsc0JBQXNCLEVBQ3RCLG9CQUFvQixFQUNwQixtQkFBbUIsRUFDbkIsY0FBYyxHQUlmLE1BQU0sa0JBQWtCLENBQUM7QUEyQjFCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrRUc7QUFDSCxNQUFNLE9BQU8sU0FBdUQsU0FBUSxlQUczRTtJQUNDOzs7Ozs7Ozs7Ozs7T0FZRztJQUNILFlBQ0UsUUFBeUIsRUFDekIsZUFBNkUsRUFDN0UsY0FBNkQ7UUFFN0QsS0FBSyxDQUFDLGNBQWMsQ0FBQyxlQUFlLENBQUMsRUFBRSxtQkFBbUIsQ0FBQyxjQUFjLEVBQUUsZUFBZSxDQUFDLENBQUMsQ0FBQztRQUM3RixJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztRQUN6QixJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUN4QixJQUFJLENBQUMsa0JBQWtCLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxzQkFBc0IsQ0FBQztZQUMxQixRQUFRLEVBQUUsSUFBSTtZQUNkLDBGQUEwRjtZQUMxRix3QkFBd0I7WUFDeEIsNkZBQTZGO1lBQzdGLCtEQUErRDtZQUMvRCxTQUFTLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxjQUFjO1NBQ2pDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFJRDs7Ozs7O09BTUc7SUFDSCxFQUFFLENBQUMsS0FBYTtRQUNkLE9BQVEsSUFBSSxDQUFDLFFBQWdCLENBQUMsSUFBSSxDQUFDLFlBQVks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bGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGFuIE1JVC1zdHlsZSBsaWNlbnNlIHRoYXQgY2FuIGJlXG4gKiBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGF0IGh0dHBzOi8vYW5ndWxhci5pby9saWNlbnNlXG4gKi9cblxuaW1wb3J0IHvJtVdyaXRhYmxlIGFzIFdyaXRhYmxlfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuaW1wb3J0IHtBc3luY1ZhbGlkYXRvckZuLCBWYWxpZGF0b3JGbn0gZnJvbSAnLi4vZGlyZWN0aXZlcy92YWxpZGF0b3JzJztcblxuaW1wb3J0IHtcbiAgQWJzdHJhY3RDb250cm9sLFxuICBBYnN0cmFjdENvbnRyb2xPcHRpb25zLFxuICBhc3NlcnRBbGxWYWx1ZXNQcmVzZW50LFxuICBhc3NlcnRDb250cm9sUHJlc2VudCxcbiAgcGlja0FzeW5jVmFsaWRhdG9ycyxcbiAgcGlja1ZhbGlkYXRvcnMsXG4gIMm1UmF3VmFsdWUsXG4gIMm1VHlwZWRPclVudHlwZWQsXG4gIMm1VmFsdWUsXG59IGZyb20gJy4vYWJzdHJhY3RfbW9kZWwnO1xuXG4vKipcbiAqIEZvcm1BcnJheVZhbHVlIGV4dHJhY3RzIHRoZSB0eXBlIG9mIGAudmFsdWVgIGZyb20gYSBGb3JtQXJyYXkncyBlbGVtZW50IHR5cGUsIGFuZCB3cmFwcyBpdCBpbiBhblxuICogYXJyYXkuXG4gKlxuICogQW5ndWxhciB1c2VzIHRoaXMgdHlwZSBpbnRlcm5hbGx5IHRvIHN1cHBvcnQgVHlwZWQgRm9ybXM7IGRvIG5vdCB1c2UgaXQgZGlyZWN0bHkuIFRoZSB1bnR5cGVkXG4gKiBjYXNlIGZhbGxzIGJhY2sgdG8gYW55W10uXG4gKi9cbmV4cG9ydCB0eXBlIMm1Rm9ybUFycmF5VmFsdWU8VCBleHRlbmRzIEFic3RyYWN0Q29udHJvbDxhbnk+PiA9IMm1VHlwZWRPclVudHlwZWQ8XG4gIFQsXG4gIEFycmF5PMm1VmFsdWU8VD4+LFxuICBhbnlbXVxuPjtcblxuLyoqXG4gKiBGb3JtQXJyYXlSYXdWYWx1ZSBleHRyYWN0cyB0aGUgdHlwZSBvZiBgLmdldFJhd1ZhbHVlKClgIGZyb20gYSBGb3JtQXJyYXkncyBlbGVtZW50IHR5cGUsIGFuZFxuICogd3JhcHMgaXQgaW4gYW4gYXJyYXkuIFRoZSB1bnR5cGVkIGNhc2UgZmFsbHMgYmFjayB0byBhbnlbXS5cbiAqXG4gKiBBbmd1bGFyIHVzZXMgdGhpcyB0eXBlIGludGVybmFsbHkgdG8gc3VwcG9ydCBUeXBlZCBGb3JtczsgZG8gbm90IHVzZSBpdCBkaXJlY3RseS5cbiAqL1xuZXhwb3J0IHR5cGUgybVGb3JtQXJyYXlSYXdWYWx1ZTxUIGV4dGVuZHMgQWJzdHJhY3RDb250cm9sPGFueT4+ID0gybVUeXBlZE9yVW50eXBlZDxcbiAgVCxcbiAgQXJyYXk8ybVSYXdWYWx1ZTxUPj4sXG4gIGFueVtdXG4+O1xuXG4vKipcbiAqIFRyYWNrcyB0aGUgdmFsdWUgYW5kIHZhbGlkaXR5IHN0YXRlIG9mIGFuIGFycmF5IG9mIGBGb3JtQ29udHJvbGAsXG4gKiBgRm9ybUdyb3VwYCBvciBgRm9ybUFycmF5YCBpbnN0YW5jZXMuXG4gKlxuICogQSBgRm9ybUFycmF5YCBhZ2dyZWdhdGVzIHRoZSB2YWx1ZXMgb2YgZWFjaCBjaGlsZCBgRm9ybUNvbnRyb2xgIGludG8gYW4gYXJyYXkuXG4gKiBJdCBjYWxjdWxhdGVzIGl0cyBzdGF0dXMgYnkgcmVkdWNpbmcgdGhlIHN0YXR1cyB2YWx1ZXMgb2YgaXRzIGNoaWxkcmVuLiBGb3IgZXhhbXBsZSwgaWYgb25lIG9mXG4gKiB0aGUgY29udHJvbHMgaW4gYSBgRm9ybUFycmF5YCBpcyBpbnZhbGlkLCB0aGUgZW50aXJlIGFycmF5IGJlY29tZXMgaW52YWxpZC5cbiAqXG4gKiBgRm9ybUFycmF5YCBhY2NlcHRzIG9uZSBnZW5lcmljIGFyZ3VtZW50LCB3aGljaCBpcyB0aGUgdHlwZSBvZiB0aGUgY29udHJvbHMgaW5zaWRlLlxuICogSWYgeW91IG5lZWQgYSBoZXRlcm9nZW5vdXMgYXJyYXksIHVzZSB7QGxpbmsgVW50eXBlZEZvcm1BcnJheX0uXG4gKlxuICogYEZvcm1BcnJheWAgaXMgb25lIG9mIHRoZSBmb3VyIGZ1bmRhbWVudGFsIGJ1aWxkaW5nIGJsb2NrcyB1c2VkIHRvIGRlZmluZSBmb3JtcyBpbiBBbmd1bGFyLFxuICogYWxvbmcgd2l0aCBgRm9ybUNvbnRyb2xgLCBgRm9ybUdyb3VwYCwgYW5kIGBGb3JtUmVjb3JkYC5cbiAqXG4gKiBAdXNhZ2VOb3Rlc1xuICpcbiAqICMjIyBDcmVhdGUgYW4gYXJyYXkgb2YgZm9ybSBjb250cm9sc1xuICpcbiAqIGBgYFxuICogY29uc3QgYXJyID0gbmV3IEZvcm1BcnJheShbXG4gKiAgIG5ldyBGb3JtQ29udHJvbCgnTmFuY3knLCBWYWxpZGF0b3JzLm1pbkxlbmd0aCgyKSksXG4gKiAgIG5ldyBGb3JtQ29udHJvbCgnRHJldycpLFxuICogXSk7XG4gKlxuICogY29uc29sZS5sb2coYXJyLnZhbHVlKTsgICAvLyBbJ05hbmN5JywgJ0RyZXcnXVxuICogY29uc29sZS5sb2coYXJyLnN0YXR1cyk7ICAvLyAnVkFMSUQnXG4gKiBgYGBcbiAqXG4gKiAjIyMgQ3JlYXRlIGEgZm9ybSBhcnJheSB3aXRoIGFycmF5LWxldmVsIHZhbGlkYXRvcnNcbiAqXG4gKiBZb3UgaW5jbHVkZSBhcnJheS1sZXZlbCB2YWxpZGF0b3JzIGFuZCBhc3luYyB2YWxpZGF0b3JzLiBUaGVzZSBjb21lIGluIGhhbmR5XG4gKiB3aGVuIHlvdSB3YW50IHRvIHBlcmZvcm0gdmFsaWRhdGlvbiB0aGF0IGNvbnNpZGVycyB0aGUgdmFsdWUgb2YgbW9yZSB0aGFuIG9uZSBjaGlsZFxuICogY29udHJvbC5cbiAqXG4gKiBUaGUgdHdvIHR5cGVzIG9mIHZhbGlkYXRvcnMgYXJlIHBhc3NlZCBpbiBzZXBhcmF0ZWx5IGFzIHRoZSBzZWNvbmQgYW5kIHRoaXJkIGFyZ1xuICogcmVzcGVjdGl2ZWx5LCBvciB0b2dldGhlciBhcyBwYXJ0IG9mIGFuIG9wdGlvbnMgb2JqZWN0LlxuICpcbiAqIGBgYFxuICogY29uc3QgYXJyID0gbmV3IEZvcm1BcnJheShbXG4gKiAgIG5ldyBGb3JtQ29udHJvbCgnTmFuY3knKSxcbiAqICAgbmV3IEZvcm1Db250cm9sKCdEcmV3JylcbiAqIF0sIHt2YWxpZGF0b3JzOiBteVZhbGlkYXRvciwgYXN5bmNWYWxpZGF0b3JzOiBteUFzeW5jVmFsaWRhdG9yfSk7XG4gKiBgYGBcbiAqXG4gKiAjIyMgU2V0IHRoZSB1cGRhdGVPbiBwcm9wZXJ0eSBmb3IgYWxsIGNvbnRyb2xzIGluIGEgZm9ybSBhcnJheVxuICpcbiAqIFRoZSBvcHRpb25zIG9iamVjdCBpcyB1c2VkIHRvIHNldCBhIGRlZmF1bHQgdmFsdWUgZm9yIGVhY2ggY2hpbGRcbiAqIGNvbnRyb2wncyBgdXBkYXRlT25gIHByb3BlcnR5LiBJZiB5b3Ugc2V0IGB1cGRhdGVPbmAgdG8gYCdibHVyJ2AgYXQgdGhlXG4gKiBhcnJheSBsZXZlbCwgYWxsIGNoaWxkIGNvbnRyb2xzIGRlZmF1bHQgdG8gJ2JsdXInLCB1bmxlc3MgdGhlIGNoaWxkXG4gKiBoYXMgZXhwbGljaXRseSBzcGVjaWZpZWQgYSBkaWZmZXJlbnQgYHVwZGF0ZU9uYCB2YWx1ZS5cbiAqXG4gKiBgYGB0c1xuICogY29uc3QgYXJyID0gbmV3IEZvcm1BcnJheShbXG4gKiAgICBuZXcgRm9ybUNvbnRyb2woKVxuICogXSwge3VwZGF0ZU9uOiAnYmx1cid9KTtcbiAqIGBgYFxuICpcbiAqICMjIyBBZGRpbmcgb3IgcmVtb3ZpbmcgY29udHJvbHMgZnJvbSBhIGZvcm0gYXJyYXlcbiAqXG4gKiBUbyBjaGFuZ2UgdGhlIGNvbnRyb2xzIGluIHRoZSBhcnJheSwgdXNlIHRoZSBgcHVzaGAsIGBpbnNlcnRgLCBgcmVtb3ZlQXRgIG9yIGBjbGVhcmAgbWV0aG9kc1xuICogaW4gYEZvcm1BcnJheWAgaXRzZWxmLiBUaGVzZSBtZXRob2RzIGVuc3VyZSB0aGUgY29udHJvbHMgYXJlIHByb3Blcmx5IHRyYWNrZWQgaW4gdGhlXG4gKiBmb3JtJ3MgaGllcmFyY2h5LiBEbyBub3QgbW9kaWZ5IHRoZSBhcnJheSBvZiBgQWJzdHJhY3RDb250cm9sYHMgdXNlZCB0byBpbnN0YW50aWF0ZVxuICogdGhlIGBGb3JtQXJyYXlgIGRpcmVjdGx5LCBhcyB0aGF0IHJlc3VsdCBpbiBzdHJhbmdlIGFuZCB1bmV4cGVjdGVkIGJlaGF2aW9yIHN1Y2hcbiAqIGFzIGJyb2tlbiBjaGFuZ2UgZGV0ZWN0aW9uLlxuICpcbiAqIEBwdWJsaWNBcGlcbiAqL1xuZXhwb3J0IGNsYXNzIEZvcm1BcnJheTxUQ29udHJvbCBleHRlbmRzIEFic3RyYWN0Q29udHJvbDxhbnk+ID0gYW55PiBleHRlbmRzIEFic3RyYWN0Q29udHJvbDxcbiAgybVUeXBlZE9yVW50eXBlZDxUQ29udHJvbCwgybVGb3JtQXJyYXlWYWx1ZTxUQ29udHJvbD4sIGFueT4sXG4gIMm1VHlwZWRPclVudHlwZWQ8VENvbnRyb2wsIMm1Rm9ybUFycmF5UmF3VmFsdWU8VENvbnRyb2w+LCBhbnk+XG4+IHtcbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBuZXcgYEZvcm1BcnJheWAgaW5zdGFuY2UuXG4gICAqXG4gICAqIEBwYXJhbSBjb250cm9scyBBbiBhcnJheSBvZiBjaGlsZCBjb250cm9scy4gRWFjaCBjaGlsZCBjb250cm9sIGlzIGdpdmVuIGFuIGluZGV4XG4gICAqIHdoZXJlIGl0IGlzIHJlZ2lzdGVyZWQuXG4gICAqXG4gICAqIEBwYXJhbSB2YWxpZGF0b3JPck9wdHMgQSBzeW5jaHJvbm91cyB2YWxpZGF0b3IgZnVuY3Rpb24sIG9yIGFuIGFycmF5IG9mXG4gICAqIHN1Y2ggZnVuY3Rpb25zLCBvciBhbiBgQWJzdHJhY3RDb250cm9sT3B0aW9uc2Agb2JqZWN0IHRoYXQgY29udGFpbnMgdmFsaWRhdGlvbiBmdW5jdGlvbnNcbiAgICogYW5kIGEgdmFsaWRhdGlvbiB0cmlnZ2VyLlxuICAgKlxuICAgKiBAcGFyYW0gYXN5bmNWYWxpZGF0b3IgQSBzaW5nbGUgYXN5bmMgdmFsaWRhdG9yIG9yIGFycmF5IG9mIGFzeW5jIHZhbGlkYXRvciBmdW5jdGlvbnNcbiAgICpcbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIGNvbnRyb2xzOiBBcnJheTxUQ29udHJvbD4sXG4gICAgdmFsaWRhdG9yT3JPcHRzPzogVmFsaWRhdG9yRm4gfCBWYWxpZGF0b3JGbltdIHwgQWJzdHJhY3RDb250cm9sT3B0aW9ucyB8IG51bGwsXG4gICAgYXN5bmNWYWxpZGF0b3I/OiBBc3luY1ZhbGlkYXRvckZuIHwgQXN5bmNWYWxpZGF0b3JGbltdIHwgbnVsbCxcbiAgKSB7XG4gICAgc3VwZXIocGlja1ZhbGlkYXRvcnModmFsaWRhdG9yT3JPcHRzKSwgcGlja0FzeW5jVmFsaWRhdG9ycyhhc3luY1ZhbGlkYXRvciwgdmFsaWRhdG9yT3JPcHRzKSk7XG4gICAgdGhpcy5jb250cm9scyA9IGNvbnRyb2xzO1xuICAgIHRoaXMuX2luaXRPYnNlcnZhYmxlcygpO1xuICAgIHRoaXMuX3NldFVwZGF0ZVN0cmF0ZWd5KHZhbGlkYXRvck9yT3B0cyk7XG4gICAgdGhpcy5fc2V0VXBDb250cm9scygpO1xuICAgIHRoaXMudXBkYXRlVmFsdWVBbmRWYWxpZGl0eSh7XG4gICAgICBvbmx5U2VsZjogdHJ1ZSxcbiAgICAgIC8vIElmIGBhc3luY1ZhbGlkYXRvcmAgaXMgcHJlc2VudCwgaXQgd2lsbCB0cmlnZ2VyIGNvbnRyb2wgc3RhdHVzIGNoYW5nZSBmcm9tIGBQRU5ESU5HYCB0b1xuICAgICAgLy8gYFZBTElEYCBvciBgSU5WQUxJRGAuXG4gICAgICAvLyBUaGUgc3RhdHVzIHNob3VsZCBiZSBicm9hZGNhc3RlZCB2aWEgdGhlIGBzdGF0dXNDaGFuZ2VzYCBvYnNlcnZhYmxlLCBzbyB3ZSBzZXQgYGVtaXRFdmVudGBcbiAgICAgIC8vIHRvIGB0cnVlYCB0byBhbGxvdyB0aGF0IGR1cmluZyB0aGUgY29udHJvbCBjcmVhdGlvbiBwcm9jZXNzLlxuICAgICAgZW1pdEV2ZW50OiAhIXRoaXMuYXN5bmNWYWxpZGF0b3IsXG4gICAgfSk7XG4gIH1cblxuICBwdWJsaWMgY29udHJvbHM6IMm1VHlwZWRPclVudHlwZWQ8VENvbnRyb2wsIEFycmF5PFRDb250cm9sPiwgQXJyYXk8QWJzdHJhY3RDb250cm9sPGFueT4+PjtcblxuICAvKipcbiAgICogR2V0IHRoZSBgQWJzdHJhY3RDb250cm9sYCBhdCB0aGUgZ2l2ZW4gYGluZGV4YCBpbiB0aGUgYXJyYXkuXG4gICAqXG4gICAqIEBwYXJhbSBpbmRleCBJbmRleCBpbiB0aGUgYXJyYXkgdG8gcmV0cmlldmUgdGhlIGNvbnRyb2wuIElmIGBpbmRleGAgaXMgbmVnYXRpdmUsIGl0IHdpbGwgd3JhcFxuICAgKiAgICAgYXJvdW5kIGZyb20gdGhlIGJhY2ssIGFuZCBpZiBpbmRleCBpcyBncmVhdGx5IG5lZ2F0aXZlIChsZXNzIHRoYW4gYC1sZW5ndGhgKSwgdGhlIHJlc3VsdCBpc1xuICAgKiB1bmRlZmluZWQuIFRoaXMgYmVoYXZpb3IgaXMgdGhlIHNhbWUgYXMgYEFycmF5LmF0KGluZGV4KWAuXG4gICAqL1xuICBhdChpbmRleDogbnVtYmVyKTogybVUeXBlZE9yVW50eXBlZDxUQ29udHJvbCwgVENvbnRyb2wsIEFic3RyYWN0Q29udHJvbDxhbnk+PiB7XG4gICAgcmV0dXJuICh0aGlzLmNvbnRyb2xzIGFzIGFueSlbdGhpcy5fYWRqdXN0SW5kZXgoaW5kZXgpXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnNlcnQgYSBuZXcgYEFic3RyYWN0Q29udHJvbGAgYXQgdGhlIGVuZCBvZiB0aGUgYXJyYXkuXG4gICAqXG4gICAqIEBwYXJhbSBjb250cm9sIEZvcm0gY29udHJvbCB0byBiZSBpbnNlcnRlZFxuICAgKiBAcGFyYW0gb3B0aW9ucyBTcGVjaWZpZXMgd2hldGhlciB0aGlzIEZvcm1BcnJheSBpbnN0YW5jZSBzaG91bGQgZW1pdCBldmVudHMgYWZ0ZXIgYSBuZXdcbiAgICogICAgIGNvbnRyb2wgaXMgYWRkZWQuXG4gICAqICogYGVtaXRFdmVudGA6IFdoZW4gdHJ1ZSBvciBub3Qgc3VwcGxpZWQgKHRoZSBkZWZhdWx0KSwgYm90aCB0aGUgYHN0YXR1c0NoYW5nZXNgIGFuZFxuICAgKiBgdmFsdWVDaGFuZ2VzYCBvYnNlcnZhYmxlcyBlbWl0IGV2ZW50cyB3aXRoIHRoZSBsYXRlc3Qgc3RhdHVzIGFuZCB2YWx1ZSB3aGVuIHRoZSBjb250cm9sIGlzXG4gICAqIGluc2VydGVkLiBXaGVuIGZhbHNlLCBubyBldmVudHMgYXJlIGVtaXR0ZWQuXG4gICAqL1xuICBwdXNoKGNvbnRyb2w6IFRDb250cm9sLCBvcHRpb25zOiB7ZW1pdEV2ZW50PzogYm9vbGVhbn0gPSB7fSk6IHZvaWQge1xuICAgIHRoaXMuY29udHJvbHMucHVzaChjb250cm9sKTtcbiAgICB0aGlzLl9yZWdpc3RlckNvbnRyb2woY29udHJvbCk7XG4gICAgdGhpcy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KHtlbWl0RXZlbnQ6IG9wdGlvbnMuZW1pdEV2ZW50fSk7XG4gICAgdGhpcy5fb25Db2xsZWN0aW9uQ2hhbmdlKCk7XG4gIH1cblxuICAvKipcbiAgICogSW5zZXJ0IGEgbmV3IGBBYnN0cmFjdENvbnRyb2xgIGF0IHRoZSBnaXZlbiBgaW5kZXhgIGluIHRoZSBhcnJheS5cbiAgICpcbiAgICogQHBhcmFtIGluZGV4IEluZGV4IGluIHRoZSBhcnJheSB0byBpbnNlcnQgdGhlIGNvbnRyb2wuIElmIGBpbmRleGAgaXMgbmVnYXRpdmUsIHdyYXBzIGFyb3VuZFxuICAgKiAgICAgZnJvbSB0aGUgYmFjay4gSWYgYGluZGV4YCBpcyBncmVhdGx5IG5lZ2F0aXZlIChsZXNzIHRoYW4gYC1sZW5ndGhgKSwgcHJlcGVuZHMgdG8gdGhlIGFycmF5LlxuICAgKiBUaGlzIGJlaGF2aW9yIGlzIHRoZSBzYW1lIGFzIGBBcnJheS5zcGxpY2UoaW5kZXgsIDAsIGNvbnRyb2wpYC5cbiAgICogQHBhcmFtIGNvbnRyb2wgRm9ybSBjb250cm9sIHRvIGJlIGluc2VydGVkXG4gICAqIEBwYXJhbSBvcHRpb25zIFNwZWNpZmllcyB3aGV0aGVyIHRoaXMgRm9ybUFycmF5IGluc3RhbmNlIHNob3VsZCBlbWl0IGV2ZW50cyBhZnRlciBhIG5ld1xuICAgKiAgICAgY29udHJvbCBpcyBpbnNlcnRlZC5cbiAgICogKiBgZW1pdEV2ZW50YDogV2hlbiB0cnVlIG9yIG5vdCBzdXBwbGllZCAodGhlIGRlZmF1bHQpLCBib3RoIHRoZSBgc3RhdHVzQ2hhbmdlc2AgYW5kXG4gICAqIGB2YWx1ZUNoYW5nZXNgIG9ic2VydmFibGVzIGVtaXQgZXZlbnRzIHdpdGggdGhlIGxhdGVzdCBzdGF0dXMgYW5kIHZhbHVlIHdoZW4gdGhlIGNvbnRyb2wgaXNcbiAgICogaW5zZXJ0ZWQuIFdoZW4gZmFsc2UsIG5vIGV2ZW50cyBhcmUgZW1pdHRlZC5cbiAgICovXG4gIGluc2VydChpbmRleDogbnVtYmVyLCBjb250cm9sOiBUQ29udHJvbCwgb3B0aW9uczoge2VtaXRFdmVudD86IGJvb2xlYW59ID0ge30pOiB2b2lkIHtcbiAgICB0aGlzLmNvbnRyb2xzLnNwbGljZShpbmRleCwgMCwgY29udHJvbCk7XG5cbiAgICB0aGlzLl9yZWdpc3RlckNvbnRyb2woY29udHJvbCk7XG4gICAgdGhpcy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KHtlbWl0RXZlbnQ6IG9wdGlvbnMuZW1pdEV2ZW50fSk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlIHRoZSBjb250cm9sIGF0IHRoZSBnaXZlbiBgaW5kZXhgIGluIHRoZSBhcnJheS5cbiAgICpcbiAgICogQHBhcmFtIGluZGV4IEluZGV4IGluIHRoZSBhcnJheSB0byByZW1vdmUgdGhlIGNvbnRyb2wuICBJZiBgaW5kZXhgIGlzIG5lZ2F0aXZlLCB3cmFwcyBhcm91bmRcbiAgICogICAgIGZyb20gdGhlIGJhY2suIElmIGBpbmRleGAgaXMgZ3JlYXRseSBuZWdhdGl2ZSAobGVzcyB0aGFuIGAtbGVuZ3RoYCksIHJlbW92ZXMgdGhlIGZpcnN0XG4gICAqICAgICBlbGVtZW50LiBUaGlzIGJlaGF2aW9yIGlzIHRoZSBzYW1lIGFzIGBBcnJheS5zcGxpY2UoaW5kZXgsIDEpYC5cbiAgICogQHBhcmFtIG9wdGlvbnMgU3BlY2lmaWVzIHdoZXRoZXIgdGhpcyBGb3JtQXJyYXkgaW5zdGFuY2Ugc2hvdWxkIGVtaXQgZXZlbnRzIGFmdGVyIGFcbiAgICogICAgIGNvbnRyb2wgaXMgcmVtb3ZlZC5cbiAgICogKiBgZW1pdEV2ZW50YDogV2hlbiB0cnVlIG9yIG5vdCBzdXBwbGllZCAodGhlIGRlZmF1bHQpLCBib3RoIHRoZSBgc3RhdHVzQ2hhbmdlc2AgYW5kXG4gICAqIGB2YWx1ZUNoYW5nZXNgIG9ic2VydmFibGVzIGVtaXQgZXZlbnRzIHdpdGggdGhlIGxhdGVzdCBzdGF0dXMgYW5kIHZhbHVlIHdoZW4gdGhlIGNvbnRyb2wgaXNcbiAgICogcmVtb3ZlZC4gV2hlbiBmYWxzZSwgbm8gZXZlbnRzIGFyZSBlbWl0dGVkLlxuICAgKi9cbiAgcmVtb3ZlQXQoaW5kZXg6IG51bWJlciwgb3B0aW9uczoge2VtaXRFdmVudD86IGJvb2xlYW59ID0ge30pOiB2b2lkIHtcbiAgICAvLyBBZGp1c3QgdGhlIGluZGV4LCB0aGVuIGNsYW1wIGl0IGF0IG5vIGxlc3MgdGhhbiAwIHRvIHByZXZlbnQgdW5kZXNpcmVkIHVuZGVyZmxvd3MuXG4gICAgbGV0IGFkanVzdGVkSW5kZXggPSB0aGlzLl9hZGp1c3RJbmRleChpbmRleCk7XG4gICAgaWYgKGFkanVzdGVkSW5kZXggPCAwKSBhZGp1c3RlZEluZGV4ID0gMDtcblxuICAgIGlmICh0aGlzLmNvbnRyb2xzW2FkanVzdGVkSW5kZXhdKVxuICAgICAgdGhpcy5jb250cm9sc1thZGp1c3RlZEluZGV4XS5fcmVnaXN0ZXJPbkNvbGxlY3Rpb25DaGFuZ2UoKCkgPT4ge30pO1xuICAgIHRoaXMuY29udHJvbHMuc3BsaWNlKGFkanVzdGVkSW5kZXgsIDEpO1xuICAgIHRoaXMudXBkYXRlVmFsdWVBbmRWYWxpZGl0eSh7ZW1pdEV2ZW50OiBvcHRpb25zLmVtaXRFdmVudH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlcGxhY2UgYW4gZXhpc3RpbmcgY29udHJvbC5cbiAgICpcbiAgICogQHBhcmFtIGluZGV4IEluZGV4IGluIHRoZSBhcnJheSB0byByZXBsYWNlIHRoZSBjb250cm9sLiBJZiBgaW5kZXhgIGlzIG5lZ2F0aXZlLCB3cmFwcyBhcm91bmRcbiAgICogICAgIGZyb20gdGhlIGJhY2suIElmIGBpbmRleGAgaXMgZ3JlYXRseSBuZWdhdGl2ZSAobGVzcyB0aGFuIGAtbGVuZ3RoYCksIHJlcGxhY2VzIHRoZSBmaXJzdFxuICAgKiAgICAgZWxlbWVudC4gVGhpcyBiZWhhdmlvciBpcyB0aGUgc2FtZSBhcyBgQXJyYXkuc3BsaWNlKGluZGV4LCAxLCBjb250cm9sKWAuXG4gICAqIEBwYXJhbSBjb250cm9sIFRoZSBgQWJzdHJhY3RDb250cm9sYCBjb250cm9sIHRvIHJlcGxhY2UgdGhlIGV4aXN0aW5nIGNvbnRyb2xcbiAgICogQHBhcmFtIG9wdGlvbnMgU3BlY2lmaWVzIHdoZXRoZXIgdGhpcyBGb3JtQXJyYXkgaW5zdGFuY2Ugc2hvdWxkIGVtaXQgZXZlbnRzIGFmdGVyIGFuXG4gICAqICAgICBleGlzdGluZyBjb250cm9sIGlzIHJlcGxhY2VkIHdpdGggYSBuZXcgb25lLlxuICAgKiAqIGBlbWl0RXZlbnRgOiBXaGVuIHRydWUgb3Igbm90IHN1cHBsaWVkICh0aGUgZGVmYXVsdCksIGJvdGggdGhlIGBzdGF0dXNDaGFuZ2VzYCBhbmRcbiAgICogYHZhbHVlQ2hhbmdlc2Agb2JzZXJ2YWJsZXMgZW1pdCBldmVudHMgd2l0aCB0aGUgbGF0ZXN0IHN0YXR1cyBhbmQgdmFsdWUgd2hlbiB0aGUgY29udHJvbCBpc1xuICAgKiByZXBsYWNlZCB3aXRoIGEgbmV3IG9uZS4gV2hlbiBmYWxzZSwgbm8gZXZlbnRzIGFyZSBlbWl0dGVkLlxuICAgKi9cbiAgc2V0Q29udHJvbChpbmRleDogbnVtYmVyLCBjb250cm9sOiBUQ29udHJvbCwgb3B0aW9uczoge2VtaXRFdmVudD86IGJvb2xlYW59ID0ge30pOiB2b2lkIHtcbiAgICAvLyBBZGp1c3QgdGhlIGluZGV4LCB0aGVuIGNsYW1wIGl0IGF0IG5vIGxlc3MgdGhhbiAwIHRvIHByZXZlbnQgdW5kZXNpcmVkIHVuZGVyZmxvd3MuXG4gICAgbGV0IGFkanVzdGVkSW5kZXggPSB0aGlzLl9hZGp1c3RJbmRleChpbmRleCk7XG4gICAgaWYgKGFkanVzdGVkSW5kZXggPCAwKSBhZGp1c3RlZEluZGV4ID0gMDtcblxuICAgIGlmICh0aGlzLmNvbnRyb2xzW2FkanVzdGVkSW5kZXhdKVxuICAgICAgdGhpcy5jb250cm9sc1thZGp1c3RlZEluZGV4XS5fcmVnaXN0ZXJPbkNvbGxlY3Rpb25DaGFuZ2UoKCkgPT4ge30pO1xuICAgIHRoaXMuY29udHJvbHMuc3BsaWNlKGFkanVzdGVkSW5kZXgsIDEpO1xuXG4gICAgaWYgKGNvbnRyb2wpIHtcbiAgICAgIHRoaXMuY29udHJvbHMuc3BsaWNlKGFkanVzdGVkSW5kZXgsIDAsIGNvbnRyb2wpO1xuICAgICAgdGhpcy5fcmVnaXN0ZXJDb250cm9sKGNvbnRyb2wpO1xuICAgIH1cblxuICAgIHRoaXMudXBkYXRlVmFsdWVBbmRWYWxpZGl0eSh7ZW1pdEV2ZW50OiBvcHRpb25zLmVtaXRFdmVudH0pO1xuICAgIHRoaXMuX29uQ29sbGVjdGlvbkNoYW5nZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIExlbmd0aCBvZiB0aGUgY29udHJvbCBhcnJheS5cbiAgICovXG4gIGdldCBsZW5ndGgoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5jb250cm9scy5sZW5ndGg7XG4gIH1cblxuICAvKipcbiAgICogU2V0cyB0aGUgdmFsdWUgb2YgdGhlIGBGb3JtQXJyYXlgLiBJdCBhY2NlcHRzIGFuIGFycmF5IHRoYXQgbWF0Y2hlc1xuICAgKiB0aGUgc3RydWN0dXJlIG9mIHRoZSBjb250cm9sLlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBwZXJmb3JtcyBzdHJpY3QgY2hlY2tzLCBhbmQgdGhyb3dzIGFuIGVycm9yIGlmIHlvdSB0cnlcbiAgICogdG8gc2V0IHRoZSB2YWx1ZSBvZiBhIGNvbnRyb2wgdGhhdCBkb2Vzbid0IGV4aXN0IG9yIGlmIHlvdSBleGNsdWRlIHRoZVxuICAgKiB2YWx1ZSBvZiBhIGNvbnRyb2wuXG4gICAqXG4gICAqIEB1c2FnZU5vdGVzXG4gICAqICMjIyBTZXQgdGhlIHZhbHVlcyBmb3IgdGhlIGNvbnRyb2xzIGluIHRoZSBmb3JtIGFycmF5XG4gICAqXG4gICAqIGBgYFxuICAgKiBjb25zdCBhcnIgPSBuZXcgRm9ybUFycmF5KFtcbiAgICogICBuZXcgRm9ybUNvbnRyb2woKSxcbiAgICogICBuZXcgRm9ybUNvbnRyb2woKVxuICAgKiBdKTtcbiAgICogY29uc29sZS5sb2coYXJyLnZhbHVlKTsgICAvLyBbbnVsbCwgbnVsbF1cbiAgICpcbiAgICogYXJyLnNldFZhbHVlKFsnTmFuY3knLCAnRHJldyddKTtcbiAgICogY29uc29sZS5sb2coYXJyLnZhbHVlKTsgICAvLyBbJ05hbmN5JywgJ0RyZXcnXVxuICAgKiBgYGBcbiAgICpcbiAgICogQHBhcmFtIHZhbHVlIEFycmF5IG9mIHZhbHVlcyBmb3IgdGhlIGNvbnRyb2xzXG4gICAqIEBwYXJhbSBvcHRpb25zIENvbmZpZ3VyZSBvcHRpb25zIHRoYXQgZGV0ZXJtaW5lIGhvdyB0aGUgY29udHJvbCBwcm9wYWdhdGVzIGNoYW5nZXMgYW5kXG4gICAqIGVtaXRzIGV2ZW50cyBhZnRlciB0aGUgdmFsdWUgY2hhbmdlc1xuICAgKlxuICAgKiAqIGBvbmx5U2VsZmA6IFdoZW4gdHJ1ZSwgZWFjaCBjaGFuZ2Ugb25seSBhZmZlY3RzIHRoaXMgY29udHJvbCwgYW5kIG5vdCBpdHMgcGFyZW50LiBEZWZhdWx0XG4gICAqIGlzIGZhbHNlLlxuICAgKiAqIGBlbWl0RXZlbnRgOiBXaGVuIHRydWUgb3Igbm90IHN1cHBsaWVkICh0aGUgZGVmYXVsdCksIGJvdGggdGhlIGBzdGF0dXNDaGFuZ2VzYCBhbmRcbiAgICogYHZhbHVlQ2hhbmdlc2BcbiAgICogb2JzZXJ2YWJsZXMgZW1pdCBldmVudHMgd2l0aCB0aGUgbGF0ZXN0IHN0YXR1cyBhbmQgdmFsdWUgd2hlbiB0aGUgY29udHJvbCB2YWx1ZSBpcyB1cGRhdGVkLlxuICAgKiBXaGVuIGZhbHNlLCBubyBldmVudHMgYXJlIGVtaXR0ZWQuXG4gICAqIFRoZSBjb25maWd1cmF0aW9uIG9wdGlvbnMgYXJlIHBhc3NlZCB0byB0aGUge0BsaW5rIEFic3RyYWN0Q29udHJvbCN1cGRhdGVWYWx1ZUFuZFZhbGlkaXR5XG4gICAqIHVwZGF0ZVZhbHVlQW5kVmFsaWRpdHl9IG1ldGhvZC5cbiAgICovXG4gIG92ZXJyaWRlIHNldFZhbHVlKFxuICAgIHZhbHVlOiDJtUZvcm1BcnJheVJhd1ZhbHVlPFRDb250cm9sPixcbiAgICBvcHRpb25zOiB7XG4gICAgICBvbmx5U2VsZj86IGJvb2xlYW47XG4gICAgICBlbWl0RXZlbnQ/OiBib29sZWFuO1xuICAgIH0gPSB7fSxcbiAgKTogdm9pZCB7XG4gICAgYXNzZXJ0QWxsVmFsdWVzUHJlc2VudCh0aGlzLCBmYWxzZSwgdmFsdWUpO1xuICAgIHZhbHVlLmZvckVhY2goKG5ld1ZhbHVlOiBhbnksIGluZGV4OiBudW1iZXIpID0+IHtcbiAgICAgIGFzc2VydENvbnRyb2xQcmVzZW50KHRoaXMsIGZhbHNlLCBpbmRleCk7XG4gICAgICB0aGlzLmF0KGluZGV4KS5zZXRWYWx1ZShuZXdWYWx1ZSwge29ubHlTZWxmOiB0cnVlLCBlbWl0RXZlbnQ6IG9wdGlvbnMuZW1pdEV2ZW50fSk7XG4gICAgfSk7XG4gICAgdGhpcy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KG9wdGlvbnMpO1xuICB9XG5cbiAgLyoqXG4gICAqIFBhdGNoZXMgdGhlIHZhbHVlIG9mIHRoZSBgRm9ybUFycmF5YC4gSXQgYWNjZXB0cyBhbiBhcnJheSB0aGF0IG1hdGNoZXMgdGhlXG4gICAqIHN0cnVjdHVyZSBvZiB0aGUgY29udHJvbCwgYW5kIGRvZXMgaXRzIGJlc3QgdG8gbWF0Y2ggdGhlIHZhbHVlcyB0byB0aGUgY29ycmVjdFxuICAgKiBjb250cm9scyBpbiB0aGUgZ3JvdXAuXG4gICAqXG4gICAqIEl0IGFjY2VwdHMgYm90aCBzdXBlci1zZXRzIGFuZCBzdWItc2V0cyBvZiB0aGUgYXJyYXkgd2l0aG91dCB0aHJvd2luZyBhbiBlcnJvci5cbiAgICpcbiAgICogQHVzYWdlTm90ZXNcbiAgICogIyMjIFBhdGNoIHRoZSB2YWx1ZXMgZm9yIGNvbnRyb2xzIGluIGEgZm9ybSBhcnJheVxuICAgKlxuICAgKiBgYGBcbiAgICogY29uc3QgYXJyID0gbmV3IEZvcm1BcnJheShbXG4gICAqICAgIG5ldyBGb3JtQ29udHJvbCgpLFxuICAgKiAgICBuZXcgRm9ybUNvbnRyb2woKVxuICAgKiBdKTtcbiAgICogY29uc29sZS5sb2coYXJyLnZhbHVlKTsgICAvLyBbbnVsbCwgbnVsbF1cbiAgICpcbiAgICogYXJyLnBhdGNoVmFsdWUoWydOYW5jeSddKTtcbiAgICogY29uc29sZS5sb2coYXJyLnZhbHVlKTsgICAvLyBbJ05hbmN5JywgbnVsbF1cbiAgICogYGBgXG4gICAqXG4gICAqIEBwYXJhbSB2YWx1ZSBBcnJheSBvZiBsYXRlc3QgdmFsdWVzIGZvciB0aGUgY29udHJvbHNcbiAgICogQHBhcmFtIG9wdGlvbnMgQ29uZmlndXJlIG9wdGlvbnMgdGhhdCBkZXRlcm1pbmUgaG93IHRoZSBjb250cm9sIHByb3BhZ2F0ZXMgY2hhbmdlcyBhbmRcbiAgICogZW1pdHMgZXZlbnRzIGFmdGVyIHRoZSB2YWx1ZSBjaGFuZ2VzXG4gICAqXG4gICAqICogYG9ubHlTZWxmYDogV2hlbiB0cnVlLCBlYWNoIGNoYW5nZSBvbmx5IGFmZmVjdHMgdGhpcyBjb250cm9sLCBhbmQgbm90IGl0cyBwYXJlbnQuIERlZmF1bHRcbiAgICogaXMgZmFsc2UuXG4gICAqICogYGVtaXRFdmVudGA6IFdoZW4gdHJ1ZSBvciBub3Qgc3VwcGxpZWQgKHRoZSBkZWZhdWx0KSwgYm90aCB0aGUgYHN0YXR1c0NoYW5nZXNgIGFuZFxuICAgKiBgdmFsdWVDaGFuZ2VzYCBvYnNlcnZhYmxlcyBlbWl0IGV2ZW50cyB3aXRoIHRoZSBsYXRlc3Qgc3RhdHVzIGFuZCB2YWx1ZSB3aGVuIHRoZSBjb250cm9sXG4gICAqIHZhbHVlIGlzIHVwZGF0ZWQuIFdoZW4gZmFsc2UsIG5vIGV2ZW50cyBhcmUgZW1pdHRlZC4gVGhlIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyBhcmUgcGFzc2VkIHRvXG4gICAqIHRoZSB7QGxpbmsgQWJzdHJhY3RDb250cm9sI3VwZGF0ZVZhbHVlQW5kVmFsaWRpdHkgdXBkYXRlVmFsdWVBbmRWYWxpZGl0eX0gbWV0aG9kLlxuICAgKi9cbiAgb3ZlcnJpZGUgcGF0Y2hWYWx1ZShcbiAgICB2YWx1ZTogybVGb3JtQXJyYXlWYWx1ZTxUQ29udHJvbD4sXG4gICAgb3B0aW9uczoge1xuICAgICAgb25seVNlbGY/OiBib29sZWFuO1xuICAgICAgZW1pdEV2ZW50PzogYm9vbGVhbjtcbiAgICB9ID0ge30sXG4gICk6IHZvaWQge1xuICAgIC8vIEV2ZW4gdGhvdWdoIHRoZSBgdmFsdWVgIGFyZ3VtZW50IHR5cGUgZG9lc24ndCBhbGxvdyBgbnVsbGAgYW5kIGB1bmRlZmluZWRgIHZhbHVlcywgdGhlXG4gICAgLy8gYHBhdGNoVmFsdWVgIGNhbiBiZSBjYWxsZWQgcmVjdXJzaXZlbHkgYW5kIGlubmVyIGRhdGEgc3RydWN0dXJlcyBtaWdodCBoYXZlIHRoZXNlIHZhbHVlcyxcbiAgICAvLyBzbyB3ZSBqdXN0IGlnbm9yZSBzdWNoIGNhc2VzIHdoZW4gYSBmaWVsZCBjb250YWluaW5nIEZvcm1BcnJheSBpbnN0YW5jZSByZWNlaXZlcyBgbnVsbGAgb3JcbiAgICAvLyBgdW5kZWZpbmVkYCBhcyBhIHZhbHVlLlxuICAgIGlmICh2YWx1ZSA9PSBudWxsIC8qIGJvdGggYG51bGxgIGFuZCBgdW5kZWZpbmVkYCAqLykgcmV0dXJuO1xuXG4gICAgdmFsdWUuZm9yRWFjaCgobmV3VmFsdWUsIGluZGV4KSA9PiB7XG4gICAgICBpZiAodGhpcy5hdChpbmRleCkpIHtcbiAgICAgICAgdGhpcy5hdChpbmRleCkucGF0Y2hWYWx1ZShuZXdWYWx1ZSwge29ubHlTZWxmOiB0cnVlLCBlbWl0RXZlbnQ6IG9wdGlvbnMuZW1pdEV2ZW50fSk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdGhpcy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KG9wdGlvbnMpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc2V0cyB0aGUgYEZvcm1BcnJheWAgYW5kIGFsbCBkZXNjZW5kYW50cyBhcmUgbWFya2VkIGBwcmlzdGluZWAgYW5kIGB1bnRvdWNoZWRgLCBhbmQgdGhlXG4gICAqIHZhbHVlIG9mIGFsbCBkZXNjZW5kYW50cyB0byBudWxsIG9yIG51bGwgbWFwcy5cbiAgICpcbiAgICogWW91IHJlc2V0IHRvIGEgc3BlY2lmaWMgZm9ybSBzdGF0ZSBieSBwYXNzaW5nIGluIGFuIGFycmF5IG9mIHN0YXRlc1xuICAgKiB0aGF0IG1hdGNoZXMgdGhlIHN0cnVjdHVyZSBvZiB0aGUgY29udHJvbC4gVGhlIHN0YXRlIGlzIGEgc3RhbmRhbG9uZSB2YWx1ZVxuICAgKiBvciBhIGZvcm0gc3RhdGUgb2JqZWN0IHdpdGggYm90aCBhIHZhbHVlIGFuZCBhIGRpc2FibGVkIHN0YXR1cy5cbiAgICpcbiAgICogQHVzYWdlTm90ZXNcbiAgICogIyMjIFJlc2V0IHRoZSB2YWx1ZXMgaW4gYSBmb3JtIGFycmF5XG4gICAqXG4gICAqIGBgYHRzXG4gICAqIGNvbnN0IGFyciA9IG5ldyBGb3JtQXJyYXkoW1xuICAgKiAgICBuZXcgRm9ybUNvbnRyb2woKSxcbiAgICogICAgbmV3IEZvcm1Db250cm9sKClcbiAgICogXSk7XG4gICAqIGFyci5yZXNldChbJ25hbWUnLCAnbGFzdCBuYW1lJ10pO1xuICAgKlxuICAgKiBjb25zb2xlLmxvZyhhcnIudmFsdWUpOyAgLy8gWyduYW1lJywgJ2xhc3QgbmFtZSddXG4gICAqIGBgYFxuICAgKlxuICAgKiAjIyMgUmVzZXQgdGhlIHZhbHVlcyBpbiBhIGZvcm0gYXJyYXkgYW5kIHRoZSBkaXNhYmxlZCBzdGF0dXMgZm9yIHRoZSBmaXJzdCBjb250cm9sXG4gICAqXG4gICAqIGBgYFxuICAgKiBhcnIucmVzZXQoW1xuICAgKiAgIHt2YWx1ZTogJ25hbWUnLCBkaXNhYmxlZDogdHJ1ZX0sXG4gICAqICAgJ2xhc3QnXG4gICAqIF0pO1xuICAgKlxuICAgKiBjb25zb2xlLmxvZyhhcnIudmFsdWUpOyAgLy8gWydsYXN0J11cbiAgICogY29uc29sZS5sb2coYXJyLmF0KDApLnN0YXR1cyk7ICAvLyAnRElTQUJMRUQnXG4gICAqIGBgYFxuICAgKlxuICAgKiBAcGFyYW0gdmFsdWUgQXJyYXkgb2YgdmFsdWVzIGZvciB0aGUgY29udHJvbHNcbiAgICogQHBhcmFtIG9wdGlvbnMgQ29uZmlndXJlIG9wdGlvbnMgdGhhdCBkZXRlcm1pbmUgaG93IHRoZSBjb250cm9sIHByb3BhZ2F0ZXMgY2hhbmdlcyBhbmRcbiAgICogZW1pdHMgZXZlbnRzIGFmdGVyIHRoZSB2YWx1ZSBjaGFuZ2VzXG4gICAqXG4gICAqICogYG9ubHlTZWxmYDogV2hlbiB0cnVlLCBlYWNoIGNoYW5nZSBvbmx5IGFmZmVjdHMgdGhpcyBjb250cm9sLCBhbmQgbm90IGl0cyBwYXJlbnQuIERlZmF1bHRcbiAgICogaXMgZmFsc2UuXG4gICAqICogYGVtaXRFdmVudGA6IFdoZW4gdHJ1ZSBvciBub3Qgc3VwcGxpZWQgKHRoZSBkZWZhdWx0KSwgYm90aCB0aGUgYHN0YXR1c0NoYW5nZXNgIGFuZFxuICAgKiBgdmFsdWVDaGFuZ2VzYFxuICAgKiBvYnNlcnZhYmxlcyBlbWl0IGV2ZW50cyB3aXRoIHRoZSBsYXRlc3Qgc3RhdHVzIGFuZCB2YWx1ZSB3aGVuIHRoZSBjb250cm9sIGlzIHJlc2V0LlxuICAgKiBXaGVuIGZhbHNlLCBubyBldmVudHMgYXJlIGVtaXR0ZWQuXG4gICAqIFRoZSBjb25maWd1cmF0aW9uIG9wdGlvbnMgYXJlIHBhc3NlZCB0byB0aGUge0BsaW5rIEFic3RyYWN0Q29udHJvbCN1cGRhdGVWYWx1ZUFuZFZhbGlkaXR5XG4gICAqIHVwZGF0ZVZhbHVlQW5kVmFsaWRpdHl9IG1ldGhvZC5cbiAgICovXG4gIG92ZXJyaWRlIHJlc2V0KFxuICAgIHZhbHVlOiDJtVR5cGVkT3JVbnR5cGVkPFRDb250cm9sLCDJtUZvcm1BcnJheVZhbHVlPFRDb250cm9sPiwgYW55PiA9IFtdLFxuICAgIG9wdGlvbnM6IHtcbiAgICAgIG9ubHlTZWxmPzogYm9vbGVhbjtcbiAgICAgIGVtaXRFdmVudD86IGJvb2xlYW47XG4gICAgfSA9IHt9LFxuICApOiB2b2lkIHtcbiAgICB0aGlzLl9mb3JFYWNoQ2hpbGQoKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCwgaW5kZXg6IG51bWJlcikgPT4ge1xuICAgICAgY29udHJvbC5yZXNldCh2YWx1ZVtpbmRleF0sIHtvbmx5U2VsZjogdHJ1ZSwgZW1pdEV2ZW50OiBvcHRpb25zLmVtaXRFdmVudH0pO1xuICAgIH0pO1xuICAgIHRoaXMuX3VwZGF0ZVByaXN0aW5lKG9wdGlvbnMsIHRoaXMpO1xuICAgIHRoaXMuX3VwZGF0ZVRvdWNoZWQob3B0aW9ucywgdGhpcyk7XG4gICAgdGhpcy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KG9wdGlvbnMpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBhZ2dyZWdhdGUgdmFsdWUgb2YgdGhlIGFycmF5LCBpbmNsdWRpbmcgYW55IGRpc2FibGVkIGNvbnRyb2xzLlxuICAgKlxuICAgKiBSZXBvcnRzIGFsbCB2YWx1ZXMgcmVnYXJkbGVzcyBvZiBkaXNhYmxlZCBzdGF0dXMuXG4gICAqL1xuICBvdmVycmlkZSBnZXRSYXdWYWx1ZSgpOiDJtUZvcm1BcnJheVJhd1ZhbHVlPFRDb250cm9sPiB7XG4gICAgcmV0dXJuIHRoaXMuY29udHJvbHMubWFwKChjb250cm9sOiBBYnN0cmFjdENvbnRyb2wpID0+IGNvbnRyb2wuZ2V0UmF3VmFsdWUoKSk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlIGFsbCBjb250cm9scyBpbiB0aGUgYEZvcm1BcnJheWAuXG4gICAqXG4gICAqIEBwYXJhbSBvcHRpb25zIFNwZWNpZmllcyB3aGV0aGVyIHRoaXMgRm9ybUFycmF5IGluc3RhbmNlIHNob3VsZCBlbWl0IGV2ZW50cyBhZnRlciBhbGxcbiAgICogICAgIGNvbnRyb2xzIGFyZSByZW1vdmVkLlxuICAgKiAqIGBlbWl0RXZlbnRgOiBXaGVuIHRydWUgb3Igbm90IHN1cHBsaWVkICh0aGUgZGVmYXVsdCksIGJvdGggdGhlIGBzdGF0dXNDaGFuZ2VzYCBhbmRcbiAgICogYHZhbHVlQ2hhbmdlc2Agb2JzZXJ2YWJsZXMgZW1pdCBldmVudHMgd2l0aCB0aGUgbGF0ZXN0IHN0YXR1cyBhbmQgdmFsdWUgd2hlbiBhbGwgY29udHJvbHNcbiAgICogaW4gdGhpcyBGb3JtQXJyYXkgaW5zdGFuY2UgYXJlIHJlbW92ZWQuIFdoZW4gZmFsc2UsIG5vIGV2ZW50cyBhcmUgZW1pdHRlZC5cbiAgICpcbiAgICogQHVzYWdlTm90ZXNcbiAgICogIyMjIFJlbW92ZSBhbGwgZWxlbWVudHMgZnJvbSBhIEZvcm1BcnJheVxuICAgKlxuICAgKiBgYGB0c1xuICAgKiBjb25zdCBhcnIgPSBuZXcgRm9ybUFycmF5KFtcbiAgICogICAgbmV3IEZvcm1Db250cm9sKCksXG4gICAqICAgIG5ldyBGb3JtQ29udHJvbCgpXG4gICAqIF0pO1xuICAgKiBjb25zb2xlLmxvZyhhcnIubGVuZ3RoKTsgIC8vIDJcbiAgICpcbiAgICogYXJyLmNsZWFyKCk7XG4gICAqIGNvbnNvbGUubG9nKGFyci5sZW5ndGgpOyAgLy8gMFxuICAgKiBgYGBcbiAgICpcbiAgICogSXQncyBhIHNpbXBsZXIgYW5kIG1vcmUgZWZmaWNpZW50IGFsdGVybmF0aXZlIHRvIHJlbW92aW5nIGFsbCBlbGVtZW50cyBvbmUgYnkgb25lOlxuICAgKlxuICAgKiBgYGB0c1xuICAgKiBjb25zdCBhcnIgPSBuZXcgRm9ybUFycmF5KFtcbiAgICogICAgbmV3IEZvcm1Db250cm9sKCksXG4gICAqICAgIG5ldyBGb3JtQ29udHJvbCgpXG4gICAqIF0pO1xuICAgKlxuICAgKiB3aGlsZSAoYXJyLmxlbmd0aCkge1xuICAgKiAgICBhcnIucmVtb3ZlQXQoMCk7XG4gICAqIH1cbiAgICogYGBgXG4gICAqL1xuICBjbGVhcihvcHRpb25zOiB7ZW1pdEV2ZW50PzogYm9vbGVhbn0gPSB7fSk6IHZvaWQge1xuICAgIGlmICh0aGlzLmNvbnRyb2xzLmxlbmd0aCA8IDEpIHJldHVybjtcbiAgICB0aGlzLl9mb3JFYWNoQ2hpbGQoKGNvbnRyb2wpID0+IGNvbnRyb2wuX3JlZ2lzdGVyT25Db2xsZWN0aW9uQ2hhbmdlKCgpID0+IHt9KSk7XG4gICAgdGhpcy5jb250cm9scy5zcGxpY2UoMCk7XG4gICAgdGhpcy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KHtlbWl0RXZlbnQ6IG9wdGlvbnMuZW1pdEV2ZW50fSk7XG4gIH1cblxuICAvKipcbiAgICogQWRqdXN0cyBhIG5lZ2F0aXZlIGluZGV4IGJ5IHN1bW1pbmcgaXQgd2l0aCB0aGUgbGVuZ3RoIG9mIHRoZSBhcnJheS4gRm9yIHZlcnkgbmVnYXRpdmVcbiAgICogaW5kaWNlcywgdGhlIHJlc3VsdCBtYXkgcmVtYWluIG5lZ2F0aXZlLlxuICAgKiBAaW50ZXJuYWxcbiAgICovXG4gIHByaXZhdGUgX2FkanVzdEluZGV4KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiBpbmRleCA8IDAgPyBpbmRleCArIHRoaXMubGVuZ3RoIDogaW5kZXg7XG4gIH1cblxuICAvKiogQGludGVybmFsICovXG4gIG92ZXJyaWRlIF9zeW5jUGVuZGluZ0NvbnRyb2xzKCk6IGJvb2xlYW4ge1xuICAgIGxldCBzdWJ0cmVlVXBkYXRlZCA9ICh0aGlzLmNvbnRyb2xzIGFzIGFueSkucmVkdWNlKCh1cGRhdGVkOiBhbnksIGNoaWxkOiBhbnkpID0+IHtcbiAgICAgIHJldHVybiBjaGlsZC5fc3luY1BlbmRpbmdDb250cm9scygpID8gdHJ1ZSA6IHVwZGF0ZWQ7XG4gICAgfSwgZmFsc2UpO1xuICAgIGlmIChzdWJ0cmVlVXBkYXRlZCkgdGhpcy51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KHtvbmx5U2VsZjogdHJ1ZX0pO1xuICAgIHJldHVybiBzdWJ0cmVlVXBkYXRlZDtcbiAgfVxuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgb3ZlcnJpZGUgX2ZvckVhY2hDaGlsZChjYjogKGM6IEFic3RyYWN0Q29udHJvbCwgaW5kZXg6IG51bWJlcikgPT4gdm9pZCk6IHZvaWQge1xuICAgIHRoaXMuY29udHJvbHMuZm9yRWFjaCgoY29udHJvbDogQWJzdHJhY3RDb250cm9sLCBpbmRleDogbnVtYmVyKSA9PiB7XG4gICAgICBjYihjb250cm9sLCBpbmRleCk7XG4gICAgfSk7XG4gIH1cblxuICAvKiogQGludGVybmFsICovXG4gIG92ZXJyaWRlIF91cGRhdGVWYWx1ZSgpOiB2b2lkIHtcbiAgICAodGhpcyBhcyBXcml0YWJsZTx0aGlzPikudmFsdWUgPSB0aGlzLmNvbnRyb2xzXG4gICAgICAuZmlsdGVyKChjb250cm9sKSA9PiBjb250cm9sLmVuYWJsZWQgfHwgdGhpcy5kaXNhYmxlZClcbiAgICAgIC5tYXAoKGNvbnRyb2wpID0+IGNvbnRyb2wudmFsdWUpO1xuICB9XG5cbiAgLyoqIEBpbnRlcm5hbCAqL1xuICBvdmVycmlkZSBfYW55Q29udHJvbHMoY29uZGl0aW9uOiAoYzogQWJzdHJhY3RDb250cm9sKSA9PiBib29sZWFuKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuY29udHJvbHMuc29tZSgoY29udHJvbCkgPT4gY29udHJvbC5lbmFibGVkICYmIGNvbmRpdGlvbihjb250cm9sKSk7XG4gIH1cblxuICAvKiogQGludGVybmFsICovXG4gIF9zZXRVcENvbnRyb2xzKCk6IHZvaWQge1xuICAgIHRoaXMuX2ZvckVhY2hDaGlsZCgoY29udHJvbCkgPT4gdGhpcy5fcmVnaXN0ZXJDb250cm9sKGNvbnRyb2wpKTtcbiAgfVxuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgb3ZlcnJpZGUgX2FsbENvbnRyb2xzRGlzYWJsZWQoKTogYm9vbGVhbiB7XG4gICAgZm9yIChjb25zdCBjb250cm9sIG9mIHRoaXMuY29udHJvbHMpIHtcbiAgICAgIGlmIChjb250cm9sLmVuYWJsZWQpIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuY29udHJvbHMubGVuZ3RoID4gMCB8fCB0aGlzLmRpc2FibGVkO1xuICB9XG5cbiAgcHJpdmF0ZSBfcmVnaXN0ZXJDb250cm9sKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCkge1xuICAgIGNvbnRyb2wuc2V0UGFyZW50KHRoaXMpO1xuICAgIGNvbnRyb2wuX3JlZ2lzdGVyT25Db2xsZWN0aW9uQ2hhbmdlKHRoaXMuX29uQ29sbGVjdGlvbkNoYW5nZSk7XG4gIH1cblxuICAvKiogQGludGVybmFsICovXG4gIG92ZXJyaWRlIF9maW5kKG5hbWU6IHN0cmluZyB8IG51bWJlcik6IEFic3RyYWN0Q29udHJvbCB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmF0KG5hbWUgYXMgbnVtYmVyKSA/PyBudWxsO1xuICB9XG59XG5cbmludGVyZmFjZSBVbnR5cGVkRm9ybUFycmF5Q3RvciB7XG4gIG5ldyAoXG4gICAgY29udHJvbHM6IEFic3RyYWN0Q29udHJvbFtdLFxuICAgIHZhbGlkYXRvck9yT3B0cz86IFZhbGlkYXRvckZuIHwgVmFsaWRhdG9yRm5bXSB8IEFic3RyYWN0Q29udHJvbE9wdGlvbnMgfCBudWxsLFxuICAgIGFzeW5jVmFsaWRhdG9yPzogQXN5bmNWYWxpZGF0b3JGbiB8IEFzeW5jVmFsaWRhdG9yRm5bXSB8IG51bGwsXG4gICk6IFVudHlwZWRGb3JtQXJyYXk7XG5cbiAgLyoqXG4gICAqIFRoZSBwcmVzZW5jZSBvZiBhbiBleHBsaWNpdCBgcHJvdG90eXBlYCBwcm9wZXJ0eSBwcm92aWRlcyBiYWNrd2FyZHMtY29tcGF0aWJpbGl0eSBmb3IgYXBwcyB0aGF0XG4gICAqIG1hbnVhbGx5IGluc3BlY3QgdGhlIHByb3RvdHlwZSBjaGFpbi5cbiAgICovXG4gIHByb3RvdHlwZTogRm9ybUFycmF5PGFueT47XG59XG5cbi8qKlxuICogVW50eXBlZEZvcm1BcnJheSBpcyBhIG5vbi1zdHJvbmdseS10eXBlZCB2ZXJzaW9uIG9mIGBGb3JtQXJyYXlgLCB3aGljaFxuICogcGVybWl0cyBoZXRlcm9nZW5vdXMgY29udHJvbHMuXG4gKi9cbmV4cG9ydCB0eXBlIFVudHlwZWRGb3JtQXJyYXkgPSBGb3JtQXJyYXk8YW55PjtcblxuZXhwb3J0IGNvbnN0IFVudHlwZWRGb3JtQXJyYXk6IFVudHlwZWRGb3JtQXJyYXlDdG9yID0gRm9ybUFycmF5O1xuXG4vKipcbiAqIEBkZXNjcmlwdGlvblxuICogQXNzZXJ0cyB0aGF0IHRoZSBnaXZlbiBjb250cm9sIGlzIGFuIGluc3RhbmNlIG9mIGBGb3JtQXJyYXlgXG4gKlxuICogQHB1YmxpY0FwaVxuICovXG5leHBvcnQgY29uc3QgaXNGb3JtQXJyYXkgPSAoY29udHJvbDogdW5rbm93bik6IGNvbnRyb2wgaXMgRm9ybUFycmF5ID0+IGNvbnRyb2wgaW5zdGFuY2VvZiBGb3JtQXJyYXk7XG4iXX0=
\No newline at end of file