mat-contenteditable
Version:
Angular contenteditable directive for Angular forms and Material Design
704 lines (694 loc) • 59.4 kB
JavaScript
import { Directive, ElementRef, Renderer2, HostListener, Input, HostBinding, Optional, Self, ViewContainerRef, NgModule } from '@angular/core';
import { MatFormFieldControl } from '@angular/material/form-field';
import { FormGroupDirective, NgControl, NgForm } from '@angular/forms';
import { ErrorStateMatcher, mixinErrorState } from '@angular/material/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Subject } from 'rxjs';
import { MatFormFieldControl as MatFormFieldControl$1, ErrorStateMatcher as ErrorStateMatcher$1 } from '@angular/material';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* \@docs-private
*/
class MatInputBase {
/**
* @param {?} _defaultErrorStateMatcher
* @param {?} _parentForm
* @param {?} _parentFormGroup
* @param {?} ngControl
*/
constructor(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl) {
this._defaultErrorStateMatcher = _defaultErrorStateMatcher;
this._parentForm = _parentForm;
this._parentFormGroup = _parentFormGroup;
this.ngControl = ngControl;
}
}
/** @type {?} */
const _MatInputMixinBase = mixinErrorState(MatInputBase);
class MatContenteditableDirective extends _MatInputMixinBase {
/**
* @param {?} elementRef
* @param {?} renderer
* @param {?} ngControl
* @param {?} _parentForm
* @param {?} _parentFormGroup
* @param {?} _defaultErrorStateMatcher
*/
constructor(elementRef, renderer, ngControl, _parentForm, _parentFormGroup, _defaultErrorStateMatcher) {
super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
this.elementRef = elementRef;
this.renderer = renderer;
this.ngControl = ngControl;
this.stateChanges = new Subject();
this.id = `mat-input-${MatContenteditableDirective.nextId++}`;
this.focused = false;
this.contentEmpty = ['<br>', '<div><br></div>'];
this._required = false;
this._disabled = false;
this.controlType = 'mat-input';
this.describedBy = '';
this.propValueAccessor = 'innerHTML';
// Setting the value accessor directly (instead of using
// the providers) to avoid running into a circular import.
if (this.ngControl != null) {
this.ngControl.valueAccessor = this;
}
}
/**
* @return {?}
*/
get value() { return this.elementRef.nativeElement[this.propValueAccessor]; }
/**
* @param {?} value
* @return {?}
*/
set value(value) {
if (value !== this.value) {
this.elementRef.nativeElement[this.propValueAccessor] = value;
this.stateChanges.next();
}
}
/**
* @return {?}
*/
get placeholder() {
return this._placeholder;
}
/**
* @param {?} plh
* @return {?}
*/
set placeholder(plh) {
this._placeholder = plh;
this.stateChanges.next();
}
/**
* @return {?}
*/
get empty() {
return !this.value || this.contentEmpty.includes(this.value);
}
/**
* @return {?}
*/
get shouldLabelFloat() { return this.focused || !this.empty; }
/**
* @return {?}
*/
get required() {
return this._required;
}
/**
* @param {?} req
* @return {?}
*/
set required(req) {
this._required = coerceBooleanProperty(req);
this.stateChanges.next();
}
/**
* @return {?}
*/
get disabled() {
return this._disabled;
}
/**
* @param {?} dis
* @return {?}
*/
set disabled(dis) {
this._disabled = coerceBooleanProperty(dis);
this.stateChanges.next();
}
/**
* @return {?}
*/
ngDoCheck() {
if (this.ngControl) {
// We need to re-evaluate this on every change detection cycle, because there are some
// error triggers that we can't subscribe to (e.g. parent form submissions). This means
// that whatever logic is in here has to be super lean or we risk destroying the performance.
this.updateErrorState();
}
}
/**
* @return {?}
*/
callOnChange() {
if (typeof this.onChange === 'function') {
this.onChange(this.elementRef.nativeElement[this.propValueAccessor]);
}
}
/**
* @return {?}
*/
callOnFocused() {
if (this.focused !== true) {
this.focused = true;
this.stateChanges.next();
}
}
/**
* @return {?}
*/
callOnTouched() {
if (typeof this.onTouched === 'function') {
this.onTouched();
}
if (this.focused !== false) {
this.focused = false;
this.stateChanges.next();
}
}
/**
* @param {?} ids
* @return {?}
*/
setDescribedByIds(ids) {
this.describedBy = ids.join(' ');
}
/**
* @return {?}
*/
onContainerClick() { this.elementRef.nativeElement.focus(); }
/**
* Writes a new value to the element.
* This method will be called by the forms API to write
* to the view when programmatic (model -> view) changes are requested.
*
* See: [ControlValueAccessor](https://angular.io/api/forms/ControlValueAccessor#members)
* @param {?} value
* @return {?}
*/
writeValue(value) {
/** @type {?} */
const normalizedValue = value == null ? '' : value;
this.renderer.setProperty(this.elementRef.nativeElement, this.propValueAccessor, normalizedValue);
}
/**
* Registers a callback function that should be called when
* the control's value changes in the UI.
*
* This is called by the forms API on initialization so it can update
* the form model when values propagate from the view (view -> model).
* @param {?} fn
* @return {?}
*/
registerOnChange(fn) {
this.onChange = fn;
}
/**
* Registers a callback function that should be called when the control receives a blur event.
* This is called by the forms API on initialization so it can update the form model on blur.
* @param {?} fn
* @return {?}
*/
registerOnTouched(fn) {
this.onTouched = fn;
}
/**
* This function is called by the forms API when the control status changes to or from "DISABLED".
* Depending on the value, it should enable or disable the appropriate DOM element.
* @param {?} isDisabled
* @return {?}
*/
setDisabledState(isDisabled) {
if (isDisabled) {
this.renderer.setAttribute(this.elementRef.nativeElement, 'disabled', 'true');
this.removeDisabledState = this.renderer.listen(this.elementRef.nativeElement, 'keydown', this.listenerDisabledState);
}
else {
if (this.removeDisabledState) {
this.renderer.removeAttribute(this.elementRef.nativeElement, 'disabled');
this.removeDisabledState();
}
}
}
/**
* @param {?} e
* @return {?}
*/
listenerDisabledState(e) {
e.preventDefault();
}
}
/**
* Implemented as part of MatFormFieldControl.
* See https://material.angular.io/guide/creating-a-custom-form-field-control
*/
MatContenteditableDirective.nextId = 0;
MatContenteditableDirective.decorators = [
{ type: Directive, args: [{
selector: '[contenteditable]',
providers: [
{ provide: MatFormFieldControl, useExisting: MatContenteditableDirective },
]
},] }
];
/** @nocollapse */
MatContenteditableDirective.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 },
{ type: NgControl, decorators: [{ type: Optional }, { type: Self }] },
{ type: NgForm, decorators: [{ type: Optional }] },
{ type: FormGroupDirective, decorators: [{ type: Optional }] },
{ type: ErrorStateMatcher }
];
MatContenteditableDirective.propDecorators = {
value: [{ type: Input }],
id: [{ type: HostBinding }],
placeholder: [{ type: Input }],
contentEmpty: [{ type: Input }],
required: [{ type: Input }],
disabled: [{ type: Input }],
errorState: [{ type: HostBinding, args: ['attr.aria-invalid',] }],
errorStateMatcher: [{ type: Input }],
describedBy: [{ type: HostBinding, args: ['attr.aria-describedby',] }],
propValueAccessor: [{ type: Input }],
callOnChange: [{ type: HostListener, args: ['input',] }],
callOnFocused: [{ type: HostListener, args: ['focus',] }],
callOnTouched: [{ type: HostListener, args: ['blur',] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class MatCkeditorDirective extends _MatInputMixinBase {
/**
* @param {?} viewRef
* @param {?} ngControl
* @param {?} _parentForm
* @param {?} _parentFormGroup
* @param {?} _defaultErrorStateMatcher
*/
constructor(
// @Host() @Self() @Optional() public editor: CKEditorComponent,
viewRef, ngControl, _parentForm, _parentFormGroup, _defaultErrorStateMatcher) {
super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
this.viewRef = viewRef;
this.ngControl = ngControl;
this.stateChanges = new Subject();
this.id = `mat-input-${MatCkeditorDirective.nextId++}`;
// Need support from Ckeditor
this.placeholder = '';
this.contentEmpty = ['<br>', '<p> </p>'];
this.focused = false;
this.required = false;
this.controlType = 'mat-input';
this.describedBy = '';
}
/**
* @return {?}
*/
get value() {
return !!this.editor.editorInstance && this.editor.editorInstance.getData();
}
/**
* @param {?} value
* @return {?}
*/
set value(value) {
if (value !== this.value) {
this.editor.data = value;
this.stateChanges.next();
}
}
/**
* @return {?}
*/
get empty() {
return !this.value || this.contentEmpty.includes(this.value);
}
/**
* @return {?}
*/
get shouldLabelFloat() { return this.focused || !this.empty; }
/**
* @param {?} isDisabled
* @return {?}
*/
set disabled(isDisabled) {
this.editor.setDisabledState(isDisabled);
this.stateChanges.next();
}
/**
* @return {?}
*/
get disabled() {
return this.editor.disabled;
}
/**
* @return {?}
*/
ngOnInit() {
// Can't use injection to get component reference
// https://github.com/angular/angular/issues/8277
this.editor = this.viewRef['_data'].componentView.component;
this.editor.blur.subscribe(() => {
this.focused = false;
this.stateChanges.next();
});
this.editor.focus.subscribe(() => {
this.focused = true;
this.stateChanges.next();
});
}
/**
* @return {?}
*/
ngDoCheck() {
if (this.ngControl) {
// We need to re-evaluate this on every change detection cycle, because there are some
// error triggers that we can't subscribe to (e.g. parent form submissions). This means
// that whatever logic is in here has to be super lean or we risk destroying the performance.
this.updateErrorState();
}
}
/**
* @param {?} ids
* @return {?}
*/
setDescribedByIds(ids) {
this.describedBy = ids.join(' ');
}
/**
* @return {?}
*/
onContainerClick() {
if (this.editor.editorInstance) {
this.editor.editorInstance.editing.view.focus();
this.stateChanges.next();
}
}
}
/**
* Implemented as part of MatFormFieldControl.
* See https://material.angular.io/guide/creating-a-custom-form-field-control
*/
MatCkeditorDirective.nextId = 0;
MatCkeditorDirective.decorators = [
{ type: Directive, args: [{
selector: '[matCkeditor]',
providers: [
{ provide: MatFormFieldControl$1, useExisting: MatCkeditorDirective },
]
},] }
];
/** @nocollapse */
MatCkeditorDirective.ctorParameters = () => [
{ type: ViewContainerRef },
{ type: NgControl, decorators: [{ type: Optional }, { type: Self }] },
{ type: NgForm, decorators: [{ type: Optional }] },
{ type: FormGroupDirective, decorators: [{ type: Optional }] },
{ type: ErrorStateMatcher$1 }
];
MatCkeditorDirective.propDecorators = {
value: [{ type: Input }],
id: [{ type: HostBinding }],
placeholder: [{ type: Input }],
contentEmpty: [{ type: Input }],
required: [{ type: Input }],
disabled: [{ type: Input }],
errorState: [{ type: HostBinding, args: ['attr.aria-invalid',] }],
errorStateMatcher: [{ type: Input }],
describedBy: [{ type: HostBinding, args: ['attr.aria-describedby',] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class MatCkeditorBalloonDirective extends MatCkeditorDirective {
/**
* @param {?} show
* @return {?}
*/
set toolbar(show) {
if (this.editor && show !== this.toolbarOpen) {
/** @type {?} */
const balloon = this.editor.editorInstance.plugins.get('BalloonToolbar');
if (show) {
this.showToolbar(balloon);
}
else {
balloon.hide();
this.toolbarOpen = false;
}
}
}
/**
* @return {?}
*/
ngOnInit() {
super.ngOnInit();
this.editor.ready.subscribe(editor => {
/** @type {?} */
const balloon = editor.plugins.get('BalloonToolbar');
balloon.stopListening(editor.model.document.selection, 'change:range');
balloon.stopListening(balloon, '_selectionChangeDebounced');
});
this.editor.focus.subscribe(() => {
if (this.toolbarOpen) {
/** @type {?} */
const balloon = this.editor.editorInstance.plugins.get('BalloonToolbar');
this.showToolbar(balloon);
}
});
}
/**
* @param {?} balloon
* @return {?}
*/
showToolbar(balloon) {
if (!balloon._balloon.hasView(balloon.toolbarView)) {
balloon.listenTo(this.editor.editorInstance.ui, 'update', () => {
balloon._balloon.updatePosition(balloon._getBalloonPositionData());
});
balloon._balloon.add({
view: balloon.toolbarView,
position: balloon._getBalloonPositionData(),
balloonClassName: 'ck-toolbar-container'
});
this.toolbarOpen = true;
}
}
}
MatCkeditorBalloonDirective.decorators = [
{ type: Directive, args: [{
selector: '[matCkeditorBalloon]',
providers: [
{ provide: MatFormFieldControl, useExisting: MatCkeditorBalloonDirective },
]
},] }
];
MatCkeditorBalloonDirective.propDecorators = {
toolbar: [{ type: Input }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class FormFieldSizerDirective {
/**
* @param {?} renderer
* @param {?} elementRef
*/
constructor(renderer, elementRef) {
this.renderer = renderer;
this.elementRef = elementRef;
}
/**
* @return {?}
*/
ngAfterContentInit() {
this.updateSize();
}
/**
* @return {?}
*/
updateSize() {
/** @type {?} */
const infix = this.getElement('mat-form-field-infix');
this.renderer.removeStyle(infix, 'min-height');
setTimeout(() => {
/** @type {?} */
const wrapper = this.getElement('mat-form-field-wrapper');
/** @type {?} */
const offset = this.elementRef.nativeElement.offsetHeight -
wrapper.offsetHeight -
parseFloat(getComputedStyle(wrapper).marginTop) -
parseFloat(getComputedStyle(wrapper).marginBottom) +
parseFloat(getComputedStyle(infix).height);
this.renderer.setStyle(infix, 'min-height', `${offset}px`);
});
}
/**
* @param {?} name
* @return {?}
*/
getElement(name) {
return this.elementRef.nativeElement.getElementsByClassName(name).item(0);
}
}
FormFieldSizerDirective.decorators = [
{ type: Directive, args: [{
selector: '[formFieldSizer]'
},] }
];
/** @nocollapse */
FormFieldSizerDirective.ctorParameters = () => [
{ type: Renderer2 },
{ type: ElementRef }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
class MatContenteditableModule {
}
MatContenteditableModule.decorators = [
{ type: NgModule, args: [{
imports: [],
declarations: [MatContenteditableDirective, MatCkeditorDirective, MatCkeditorBalloonDirective, FormFieldSizerDirective],
exports: [MatContenteditableDirective, MatCkeditorDirective, MatCkeditorBalloonDirective, FormFieldSizerDirective],
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @param {?} html
* @return {?}
*/
function getText(html) {
if (html) {
/** @type {?} */
const element = document.createElement('span');
element.innerHTML = html;
return element.textContent.replace(/\s/g, '');
}
return '';
}
/**
* \@description
* Provides a set of built-in validators that can be used by form controls.
*
* A validator is a function that processes a `FormControl` or collection of
* controls and returns an error map or null. A null map means that validation has passed.
*
* @see [Form Validation](/guide/form-validation)
*
* \@publicApi
*/
class HtmlValidators {
/**
* \@description
* Validator that requires the control have a non-empty value.
*
* \@usageNotes
*
* ### Validate that the field is non-empty
*
* ```typescript
* const control = new FormControl('', Validators.required);
*
* console.log(control.errors); // {required: true}
* ```
*
* @param {?} control
* @return {?} An error map with the `required` property
* if the validation check fails, otherwise `null`.
*
*/
static required(control) {
/** @type {?} */
const text = getText(control.value);
return text ? null : { 'required': true };
}
/**
* \@description
* Validator that requires the length of the control's value to be greater than or equal
* to the provided minimum length. This validator is also provided by default if you use the
* the HTML5 `minlength` attribute.
*
* \@usageNotes
*
* ### Validate that the field has a minimum of 3 characters
*
* ```typescript
* const control = new FormControl('ng', Validators.minLength(3));
*
* console.log(control.errors); // {minlength: {requiredLength: 3, actualLength: 2}}
* ```
*
* ```html
* <input minlength="5">
* ```
*
* @param {?} minLength
* @return {?} A validator function that returns an error map with the
* `minlength` if the validation check fails, otherwise `null`.
*/
static minLength(minLength) {
/** @type {?} */
const fn = (control) => {
/** @type {?} */
const text = getText(control.value);
if (text) {
return text.length < minLength ?
{ 'minlength': { 'requiredLength': minLength, 'actualLength': text.length } } :
null;
}
return null; // don't validate empty values to allow optional controls
};
return fn;
}
/**
* \@description
* Validator that requires the length of the control's value to be less than or equal
* to the provided maximum length. This validator is also provided by default if you use the
* the HTML5 `maxlength` attribute.
*
* \@usageNotes
*
* ### Validate that the field has maximum of 5 characters
*
* ```typescript
* const control = new FormControl('Angular', Validators.maxLength(5));
*
* console.log(control.errors); // {maxlength: {requiredLength: 5, actualLength: 7}}
* ```
*
* ```html
* <input maxlength="5">
* ```
*
* @param {?} maxLength
* @return {?} A validator function that returns an error map with the
* `maxlength` property if the validation check fails, otherwise `null`.
*/
static maxLength(maxLength) {
/** @type {?} */
const fn = (control) => {
/** @type {?} */
const text = getText(control.value);
return text.length > maxLength ?
{ 'maxlength': { 'requiredLength': maxLength, 'actualLength': text.length } } :
null;
};
return fn;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
export { _MatInputMixinBase, MatContenteditableDirective, MatCkeditorDirective, MatCkeditorBalloonDirective, FormFieldSizerDirective, MatContenteditableModule, HtmlValidators };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWF0LWNvbnRlbnRlZGl0YWJsZS5qcy5tYXAiLCJzb3VyY2VzIjpbIm5nOi8vbWF0LWNvbnRlbnRlZGl0YWJsZS9saWIvbWF0LWNvbnRlbnRlZGl0YWJsZS5kaXJlY3RpdmUudHMiLCJuZzovL21hdC1jb250ZW50ZWRpdGFibGUvbGliL21hdC1ja2VkaXRvci5kaXJlY3RpdmUudHMiLCJuZzovL21hdC1jb250ZW50ZWRpdGFibGUvbGliL21hdC1ja2VkaXRvci1iYWxsb29uLmRpcmVjdGl2ZS50cyIsIm5nOi8vbWF0LWNvbnRlbnRlZGl0YWJsZS9saWIvZm9ybS1maWVsZC1zaXplci5kaXJlY3RpdmUudHMiLCJuZzovL21hdC1jb250ZW50ZWRpdGFibGUvbGliL21hdC1jb250ZW50ZWRpdGFibGUubW9kdWxlLnRzIiwibmc6Ly9tYXQtY29udGVudGVkaXRhYmxlL2xpYi92YWxpZGF0b3JzLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIERpcmVjdGl2ZSxcbiAgRWxlbWVudFJlZixcbiAgUmVuZGVyZXIyLFxuICBIb3N0TGlzdGVuZXIsXG4gIElucHV0LFxuICBIb3N0QmluZGluZyxcbiAgT3B0aW9uYWwsXG4gIFNlbGYsXG4gIERvQ2hlY2ssXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgTWF0Rm9ybUZpZWxkQ29udHJvbCB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2Zvcm0tZmllbGQnO1xuaW1wb3J0IHsgQ29udHJvbFZhbHVlQWNjZXNzb3IsIEZvcm1Hcm91cERpcmVjdGl2ZSwgTmdDb250cm9sLCBOZ0Zvcm0gfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgeyBFcnJvclN0YXRlTWF0Y2hlciwgbWl4aW5FcnJvclN0YXRlLCBDYW5VcGRhdGVFcnJvclN0YXRlQ3RvciwgQ2FuVXBkYXRlRXJyb3JTdGF0ZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2NvcmUnO1xuaW1wb3J0IHsgY29lcmNlQm9vbGVhblByb3BlcnR5IH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2NvZXJjaW9uJztcbmltcG9ydCB7IFN1YmplY3QgfSBmcm9tICdyeGpzJztcblxuLy8gQm9pbGVycGxhdGUgZm9yIGFwcGx5aW5nIG1peGlucyB0byBNYXRJbnB1dC5cbi8qKiBAZG9jcy1wcml2YXRlICovXG5jbGFzcyBNYXRJbnB1dEJhc2Uge1xuICBjb25zdHJ1Y3RvcihwdWJsaWMgX2RlZmF1bHRFcnJvclN0YXRlTWF0Y2hlcjogRXJyb3JTdGF0ZU1hdGNoZXIsXG4gICAgICAgICAgICAgIHB1YmxpYyBfcGFyZW50Rm9ybTogTmdGb3JtLFxuICAgICAgICAgICAgICBwdWJsaWMgX3BhcmVudEZvcm1Hcm91cDogRm9ybUdyb3VwRGlyZWN0aXZlLFxuICAgICAgICAgICAgICAvKiogQGRvY3MtcHJpdmF0ZSAqL1xuICAgICAgICAgICAgICBwdWJsaWMgbmdDb250cm9sOiBOZ0NvbnRyb2wpIHt9XG59XG5leHBvcnQgY29uc3QgX01hdElucHV0TWl4aW5CYXNlOiBDYW5VcGRhdGVFcnJvclN0YXRlQ3RvciAmIHR5cGVvZiBNYXRJbnB1dEJhc2UgPVxuICBtaXhpbkVycm9yU3RhdGUoTWF0SW5wdXRCYXNlKTtcblxuXG5ARGlyZWN0aXZlKHtcbiAgc2VsZWN0b3I6ICdbY29udGVudGVkaXRhYmxlXScsXG4gIHByb3ZpZGVyczogW1xuICAgIHsgcHJvdmlkZTogTWF0Rm9ybUZpZWxkQ29udHJvbCwgdXNlRXhpc3Rpbmc6IE1hdENvbnRlbnRlZGl0YWJsZURpcmVjdGl2ZSB9LFxuICBdXG59KVxuZXhwb3J0IGNsYXNzIE1hdENvbnRlbnRlZGl0YWJsZURpcmVjdGl2ZSBleHRlbmRzIF9NYXRJbnB1dE1peGluQmFzZVxuICBpbXBsZW1lbnRzIENvbnRyb2xWYWx1ZUFjY2Vzc29yLCBNYXRGb3JtRmllbGRDb250cm9sPHN0cmluZz4sIERvQ2hlY2ssIENhblVwZGF0ZUVycm9yU3RhdGUge1xuXG4gIC8qKlxuICAgKiBJbXBsZW1lbnRlZCBhcyBwYXJ0IG9mIE1hdEZvcm1GaWVsZENvbnRyb2wuXG4gICAqIFNlZSBodHRwczovL21hdGVyaWFsLmFuZ3VsYXIuaW8vZ3VpZGUvY3JlYXRpbmctYS1jdXN0b20tZm9ybS1maWVsZC1jb250cm9sXG4gICAqL1xuICBzdGF0aWMgbmV4dElkID0gMDtcblxuICBASW5wdXQoKVxuICBnZXQgdmFsdWUoKTogc3RyaW5nIHsgcmV0dXJuIHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50W3RoaXMucHJvcFZhbHVlQWNjZXNzb3JdOyB9XG4gIHNldCB2YWx1ZSh2YWx1ZTogc3RyaW5nKSB7XG4gICAgaWYgKHZhbHVlICE9PSB0aGlzLnZhbHVlKSB7XG4gICAgICB0aGlzLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudFt0aGlzLnByb3BWYWx1ZUFjY2Vzc29yXSA9IHZhbHVlO1xuICAgICAgdGhpcy5zdGF0ZUNoYW5nZXMubmV4dCgpO1xuICAgIH1cbiAgfVxuXG4gIHJlYWRvbmx5IHN0YXRlQ2hhbmdlczogU3ViamVjdDx2b2lkPiA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgQEhvc3RCaW5kaW5nKCkgaWQgPSBgbWF0LWlucHV0LSR7TWF0Q29udGVudGVkaXRhYmxlRGlyZWN0aXZlLm5leHRJZCsrfWA7XG5cbiAgQElucHV0KClcbiAgZ2V0IHBsYWNlaG9sZGVyKCkge1xuICAgIHJldHVybiB0aGlzLl9wbGFjZWhvbGRlcjtcbiAgfVxuICBzZXQgcGxhY2Vob2xkZXIocGxoKSB7XG4gICAgdGhpcy5fcGxhY2Vob2xkZXIgPSBwbGg7XG4gICAgdGhpcy5zdGF0ZUNoYW5nZXMubmV4dCgpO1xuICB9XG4gIHByaXZhdGUgX3BsYWNlaG9sZGVyOiBzdHJpbmc7XG5cbiAgZm9jdXNlZCA9IGZhbHNlO1xuXG4gIEBJbnB1dCgpIGNvbnRlbnRFbXB0eTogQXJyYXk8c3RyaW5nPiA9IFsnPGJyPicsICc8ZGl2Pjxicj48L2Rpdj4nXTtcbiAgZ2V0IGVtcHR5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAhdGhpcy52YWx1ZSB8fCB0aGlzLmNvbnRlbnRFbXB0eS5pbmNsdWRlcyh0aGlzLnZhbHVlKTtcbiAgfVxuXG4gIGdldCBzaG91bGRMYWJlbEZsb2F0KCk6IGJvb2xlYW4geyByZXR1cm4gdGhpcy5mb2N1c2VkIHx8ICF0aGlzLmVtcHR5OyB9XG5cbiAgQElucHV0KClcbiAgZ2V0IHJlcXVpcmVkKCkge1xuICAgIHJldHVybiB0aGlzLl9yZXF1aXJlZDtcbiAgfVxuICBzZXQgcmVxdWlyZWQocmVxKSB7XG4gICAgdGhpcy5fcmVxdWlyZWQgPSBjb2VyY2VCb29sZWFuUHJvcGVydHkocmVxKTtcbiAgICB0aGlzLnN0YXRlQ2hhbmdlcy5uZXh0KCk7XG4gIH1cbiAgcHJpdmF0ZSBfcmVxdWlyZWQgPSBmYWxzZTtcblxuICBASW5wdXQoKVxuICBnZXQgZGlzYWJsZWQoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2Rpc2FibGVkO1xuICB9XG4gIHNldCBkaXNhYmxlZChkaXMpIHtcbiAgICB0aGlzLl9kaXNhYmxlZCA9IGNvZXJjZUJvb2xlYW5Qcm9wZXJ0eShkaXMpO1xuICAgIHRoaXMuc3RhdGVDaGFuZ2VzLm5leHQoKTtcbiAgfVxuICBwcml2YXRlIF9kaXNhYmxlZCA9IGZhbHNlO1xuXG4gIEBIb3N0QmluZGluZygnYXR0ci5hcmlhLWludmFsaWQnKSBlcnJvclN0YXRlOiBib29sZWFuO1xuICBASW5wdXQoKSBlcnJvclN0YXRlTWF0Y2hlcjogRXJyb3JTdGF0ZU1hdGNoZXI7XG5cbiAgY29udHJvbFR5cGUgPSAnbWF0LWlucHV0JztcblxuICBASG9zdEJpbmRpbmcoJ2F0dHIuYXJpYS1kZXNjcmliZWRieScpIGRlc2NyaWJlZEJ5ID0gJyc7XG5cblxuICAvLyBQYXJ0IG9mIENvbnRyb2xWYWx1ZUFjY2Vzc29yXG4gIHByaXZhdGUgb25DaGFuZ2U6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkO1xuICBwcml2YXRlIG9uVG91Y2hlZDogKCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSByZW1vdmVEaXNhYmxlZFN0YXRlOiAoKSA9PiB2b2lkO1xuXG4gIEBJbnB1dCgpIHByb3BWYWx1ZUFjY2Vzc29yID0gJ2lubmVySFRNTCc7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBlbGVtZW50UmVmOiBFbGVtZW50UmVmLFxuICAgIHByaXZhdGUgcmVuZGVyZXI6IFJlbmRlcmVyMixcbiAgICBAT3B0aW9uYWwoKSBAU2VsZigpIHB1YmxpYyBuZ0NvbnRyb2w6IE5nQ29udHJvbCxcbiAgICBAT3B0aW9uYWwoKSBfcGFyZW50Rm9ybTogTmdGb3JtLFxuICAgIEBPcHRpb25hbCgpIF9wYXJlbnRGb3JtR3JvdXA6IEZvcm1Hcm91cERpcmVjdGl2ZSxcbiAgICBfZGVmYXVsdEVycm9yU3RhdGVNYXRjaGVyOiBFcnJvclN0YXRlTWF0Y2hlcixcbiAgKSB7XG4gICAgc3VwZXIoX2RlZmF1bHRFcnJvclN0YXRlTWF0Y2hlciwgX3BhcmVudEZvcm0sIF9wYXJlbnRGb3JtR3JvdXAsIG5nQ29udHJvbCk7XG4gICAgLy8gU2V0dGluZyB0aGUgdmFsdWUgYWNjZXNzb3IgZGlyZWN0bHkgKGluc3RlYWQgb2YgdXNpbmdcbiAgICAvLyB0aGUgcHJvdmlkZXJzKSB0byBhdm9pZCBydW5uaW5nIGludG8gYSBjaXJjdWxhciBpbXBvcnQuXG4gICAgaWYgKHRoaXMubmdDb250cm9sICE9IG51bGwpIHsgdGhpcy5uZ0NvbnRyb2wudmFsdWVBY2Nlc3NvciA9IHRoaXM7IH1cbiAgfVxuXG4gIG5nRG9DaGVjaygpIHtcbiAgICBpZiAodGhpcy5uZ0NvbnRyb2wpIHtcbiAgICAgIC8vIFdlIG5lZWQgdG8gcmUtZXZhbHVhdGUgdGhpcyBvbiBldmVyeSBjaGFuZ2UgZGV0ZWN0aW9uIGN5Y2xlLCBiZWNhdXNlIHRoZXJlIGFyZSBzb21lXG4gICAgICAvLyBlcnJvciB0cmlnZ2VycyB0aGF0IHdlIGNhbid0IHN1YnNjcmliZSB0byAoZS5nLiBwYXJlbnQgZm9ybSBzdWJtaXNzaW9ucykuIFRoaXMgbWVhbnNcbiAgICAgIC8vIHRoYXQgd2hhdGV2ZXIgbG9naWMgaXMgaW4gaGVyZSBoYXMgdG8gYmUgc3VwZXIgbGVhbiBvciB3ZSByaXNrIGRlc3Ryb3lpbmcgdGhlIHBlcmZvcm1hbmNlLlxuICAgICAgdGhpcy51cGRhdGVFcnJvclN0YXRlKCk7XG4gICAgfVxuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignaW5wdXQnKVxuICBjYWxsT25DaGFuZ2UoKSB7XG4gICAgaWYgKHR5cGVvZiB0aGlzLm9uQ2hhbmdlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICB0aGlzLm9uQ2hhbmdlKHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50W3RoaXMucHJvcFZhbHVlQWNjZXNzb3JdKTtcbiAgICB9XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdmb2N1cycpXG4gIGNhbGxPbkZvY3VzZWQoKSB7XG4gICAgaWYgKHRoaXMuZm9jdXNlZCAhPT0gdHJ1ZSkge1xuICAgICAgdGhpcy5mb2N1c2VkID0gdHJ1ZTtcbiAgICAgIHRoaXMuc3RhdGVDaGFuZ2VzLm5leHQoKTtcbiAgICB9XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdibHVyJylcbiAgY2FsbE9uVG91Y2hlZCgpIHtcbiAgICBpZiAodHlwZW9mIHRoaXMub25Ub3VjaGVkID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICB0aGlzLm9uVG91Y2hlZCgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5mb2N1c2VkICE9PSBmYWxzZSkge1xuICAgICAgdGhpcy5mb2N1c2VkID0gZmFsc2U7XG4gICAgICB0aGlzLnN0YXRlQ2hhbmdlcy5uZXh0KCk7XG4gICAgfVxuICB9XG5cbiAgc2V0RGVzY3JpYmVkQnlJZHMoaWRzOiBzdHJpbmdbXSkge1xuICAgIHRoaXMuZGVzY3JpYmVkQnkgPSBpZHMuam9pbignICcpO1xuICB9XG5cbiAgb25Db250YWluZXJDbGljaygpIHsgdGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQuZm9jdXMoKTsgfVxuXG4gIC8qKlxuICAgKiBXcml0ZXMgYSBuZXcgdmFsdWUgdG8gdGhlIGVsZW1lbnQuXG4gICAqIFRoaXMgbWV0aG9kIHdpbGwgYmUgY2FsbGVkIGJ5IHRoZSBmb3JtcyBBUEkgdG8gd3JpdGVcbiAgICogdG8gdGhlIHZpZXcgd2hlbiBwcm9ncmFtbWF0aWMgKG1vZGVsIC0+IHZpZXcpIGNoYW5nZXMgYXJlIHJlcXVlc3RlZC5cbiAgICpcbiAgICogU2VlOiBbQ29udHJvbFZhbHVlQWNjZXNzb3JdKGh0dHBzOi8vYW5ndWxhci5pby9hcGkvZm9ybXMvQ29udHJvbFZhbHVlQWNjZXNzb3IjbWVtYmVycylcbiAgICovXG4gIHdyaXRlVmFsdWUodmFsdWU6IGFueSk6IHZvaWQge1xuICAgIGNvbnN0IG5vcm1hbGl6ZWRWYWx1ZSA9IHZhbHVlID09IG51bGwgPyAnJyA6IHZhbHVlO1xuICAgIHRoaXMucmVuZGVyZXIuc2V0UHJvcGVydHkodGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQsIHRoaXMucHJvcFZhbHVlQWNjZXNzb3IsIG5vcm1hbGl6ZWRWYWx1ZSk7XG4gIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgZnVuY3Rpb24gdGhhdCBzaG91bGQgYmUgY2FsbGVkIHdoZW5cbiAgICogdGhlIGNvbnRyb2wncyB2YWx1ZSBjaGFuZ2VzIGluIHRoZSBVSS5cbiAgICpcbiAgICogVGhpcyBpcyBjYWxsZWQgYnkgdGhlIGZvcm1zIEFQSSBvbiBpbml0aWFsaXphdGlvbiBzbyBpdCBjYW4gdXBkYXRlXG4gICAqIHRoZSBmb3JtIG1vZGVsIHdoZW4gdmFsdWVzIHByb3BhZ2F0ZSBmcm9tIHRoZSB2aWV3ICh2aWV3IC0+IG1vZGVsKS5cbiAgICovXG4gIHJlZ2lzdGVyT25DaGFuZ2UoZm46ICgpID0+IHZvaWQpOiB2b2lkIHtcbiAgICB0aGlzLm9uQ2hhbmdlID0gZm47XG4gIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgZnVuY3Rpb24gdGhhdCBzaG91bGQgYmUgY2FsbGVkIHdoZW4gdGhlIGNvbnRyb2wgcmVjZWl2ZXMgYSBibHVyIGV2ZW50LlxuICAgKiBUaGlzIGlzIGNhbGxlZCBieSB0aGUgZm9ybXMgQVBJIG9uIGluaXRpYWxpemF0aW9uIHNvIGl0IGNhbiB1cGRhdGUgdGhlIGZvcm0gbW9kZWwgb24gYmx1ci5cbiAgICovXG4gIHJlZ2lzdGVyT25Ub3VjaGVkKGZuOiAoKSA9PiB2b2lkKTogdm9pZCB7XG4gICAgdGhpcy5vblRvdWNoZWQgPSBmbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGlzIGZ1bmN0aW9uIGlzIGNhbGxlZCBieSB0aGUgZm9ybXMgQVBJIHdoZW4gdGhlIGNvbnRyb2wgc3RhdHVzIGNoYW5nZXMgdG8gb3IgZnJvbSBcIkRJU0FCTEVEXCIuXG4gICAqIERlcGVuZGluZyBvbiB0aGUgdmFsdWUsIGl0IHNob3VsZCBlbmFibGUgb3IgZGlzYWJsZSB0aGUgYXBwcm9wcmlhdGUgRE9NIGVsZW1lbnQuXG4gICAqL1xuICBzZXREaXNhYmxlZFN0YXRlKGlzRGlzYWJsZWQ6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICBpZiAoaXNEaXNhYmxlZCkge1xuICAgICAgdGhpcy5yZW5kZXJlci5zZXRBdHRyaWJ1dGUodGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQsICdkaXNhYmxlZCcsICd0cnVlJyk7XG4gICAgICB0aGlzLnJlbW92ZURpc2FibGVkU3RhdGUgPSB0aGlzLnJlbmRlcmVyLmxpc3Rlbih0aGlzLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudCwgJ2tleWRvd24nLCB0aGlzLmxpc3RlbmVyRGlzYWJsZWRTdGF0ZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGlmICh0aGlzLnJlbW92ZURpc2FibGVkU3RhdGUpIHtcbiAgICAgICAgdGhpcy5yZW5kZXJlci5yZW1vdmVBdHRyaWJ1dGUodGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQsICdkaXNhYmxlZCcpO1xuICAgICAgICB0aGlzLnJlbW92ZURpc2FibGVkU3RhdGUoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGxpc3RlbmVyRGlzYWJsZWRTdGF0ZShlOiBLZXlib2FyZEV2ZW50KSB7XG4gICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICB9XG59XG4iLCJpbXBvcnQgeyBEaXJlY3RpdmUsIElucHV0LCBIb3N0QmluZGluZywgVmlld0NvbnRhaW5lclJlZiwgT25Jbml0LCBPcHRpb25hbCwgU2VsZiwgRG9DaGVjayB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgTWF0Rm9ybUZpZWxkQ29udHJvbCwgRXJyb3JTdGF0ZU1hdGNoZXIsIENhblVwZGF0ZUVycm9yU3RhdGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbCc7XG4vLyBpbXBvcnQgeyBDS0VkaXRvckNvbXBvbmVudCB9IGZyb20gJ0Bja2VkaXRvci9ja2VkaXRvcjUtYW5ndWxhci8vY2tlZGl0b3IuY29tcG9uZW50JztcbmltcG9ydCB7IFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IE5nQ29udHJvbCwgTmdGb3JtLCBGb3JtR3JvdXBEaXJlY3RpdmUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgeyBfTWF0SW5wdXRNaXhpbkJhc2UgfSBmcm9tICcuL21hdC1jb250ZW50ZWRpdGFibGUuZGlyZWN0aXZlJztcblxuXG5ARGlyZWN0aXZlKHtcbiAgc2VsZWN0b3I6ICdbbWF0Q2tlZGl0b3JdJyxcbiAgcHJvdmlkZXJzOiBbXG4gICAgeyBwcm92aWRlOiBNYXRGb3JtRmllbGRDb250cm9sLCB1c2VFeGlzdGluZzogTWF0Q2tlZGl0b3JEaXJlY3RpdmUgfSxcbiAgXVxufSlcbmV4cG9ydCBjbGFzcyBNYXRDa2VkaXRvckRpcmVjdGl2ZSAgZXh0ZW5kcyBfTWF0SW5wdXRNaXhpbkJhc2VcbiAgaW1wbGVtZW50cyBNYXRGb3JtRmllbGRDb250cm9sPHN0cmluZz4sIERvQ2hlY2ssIENhblVwZGF0ZUVycm9yU3RhdGUgLCBPbkluaXQge1xuXG4gIC8qKlxuICAgKiBJbXBsZW1lbnRlZCBhcyBwYXJ0IG9mIE1hdEZvcm1GaWVsZENvbnRyb2wuXG4gICAqIFNlZSBodHRwczovL21hdGVyaWFsLmFuZ3VsYXIuaW8vZ3VpZGUvY3JlYXRpbmctYS1jdXN0b20tZm9ybS1maWVsZC1jb250cm9sXG4gICAqL1xuICBzdGF0aWMgbmV4dElkID0gMDtcblxuICBASW5wdXQoKVxuICBnZXQgdmFsdWUoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gISF0aGlzLmVkaXRvci5lZGl0b3JJbnN0YW5jZSAmJiB0aGlzLmVkaXRvci5lZGl0b3JJbnN0YW5jZS5nZXREYXRhKCk7XG4gIH1cbiAgc2V0IHZhbHVlKHZhbHVlOiBzdHJpbmcpIHtcbiAgICBpZiAodmFsdWUgIT09IHRoaXMudmFsdWUpIHtcbiAgICAgIHRoaXMuZWRpdG9yLmRhdGEgPSB2YWx1ZTtcbiAgICAgIHRoaXMuc3RhdGVDaGFuZ2VzLm5leHQoKTtcbiAgICB9XG4gIH1cblxuICByZWFkb25seSBzdGF0ZUNoYW5nZXM6IFN1YmplY3Q8dm9pZD4gPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gIEBIb3N0QmluZGluZygpIGlkID0gYG1hdC1pbnB1dC0ke01hdENrZWRpdG9yRGlyZWN0aXZlLm5leHRJZCsrfWA7XG5cbiAgLy8gTmVlZCBzdXBwb3J0IGZyb20gQ2tlZGl0b3JcbiAgQElucHV0KCkgcGxhY2Vob2xkZXIgPSAnJztcblxuICBASW5wdXQoKSBjb250ZW50RW1wdHk6IHN0cmluZ1tdID0gWyc8YnI+JywgJzxwPiZuYnNwOzwvcD4nXTtcbiAgZ2V0IGVtcHR5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAhdGhpcy52YWx1ZSB8fCB0aGlzLmNvbnRlbnRFbXB0eS5pbmNsdWRlcyh0aGlzLnZhbHVlKTtcbiAgfVxuXG4gIGdldCBzaG91bGRMYWJlbEZsb2F0KCk6IGJvb2xlYW4geyByZXR1cm4gdGhpcy5mb2N1c2VkIHx8ICF0aGlzLmVtcHR5OyB9XG5cbiAgZm9jdXNlZCA9IGZhbHNlO1xuXG4gIEBJbnB1dCgpIHJlcXVpcmVkID0gZmFsc2U7XG5cbiAgQElucHV0KClcbiAgc2V0IGRpc2FibGVkKGlzRGlzYWJsZWQ6IGJvb2xlYW4pIHtcbiAgICB0aGlzLmVkaXRvci5zZXREaXNhYmxlZFN0YXRlKGlzRGlzYWJsZWQpO1xuICAgIHRoaXMuc3RhdGVDaGFuZ2VzLm5leHQoKTtcbiAgfVxuICBnZXQgZGlzYWJsZWQoKSB7XG4gICAgcmV0dXJuIHRoaXMuZWRpdG9yLmRpc2FibGVkO1xuICB9XG5cbiAgQEhvc3RCaW5kaW5nKCdhdHRyLmFyaWEtaW52YWxpZCcpIGVycm9yU3RhdGU6IGJvb2xlYW47XG4gIEBJbnB1dCgpIGVycm9yU3RhdGVNYXRjaGVyOiBFcnJvclN0YXRlTWF0Y2hlcjtcblxuICBjb250cm9sVHlwZSA9ICdtYXQtaW5wdXQnO1xuXG4gIEBIb3N0QmluZGluZygnYXR0ci5hcmlhLWRlc2NyaWJlZGJ5JykgZGVzY3JpYmVkQnkgPSAnJztcblxuICBwcm90ZWN0ZWQgZWRpdG9yO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIC8vIEBIb3N0KCkgQFNlbGYoKSBAT3B0aW9uYWwoKSBwdWJsaWMgZWRpdG9yOiBDS0VkaXRvckNvbXBvbmVudCxcbiAgICBwcm90ZWN0ZWQgcmVhZG9ubHkgdmlld1JlZjogVmlld0NvbnRhaW5lclJlZixcbiAgICBAT3B0aW9uYWwoKSBAU2VsZigpIHB1YmxpYyBuZ0NvbnRyb2w6IE5nQ29udHJvbCxcbiAgICBAT3B0aW9uYWwoKSBfcGFyZW50Rm9ybTogTmdGb3JtLFxuICAgIEBPcHRpb25hbCgpIF9wYXJlbnRGb3JtR3JvdXA6IEZvcm1Hcm91cERpcmVjdGl2ZSxcbiAgICBfZGVmYXVsdEVycm9yU3RhdGVNYXRjaGVyOiBFcnJvclN0YXRlTWF0Y2hlcixcbiAgKSB7XG4gICAgc3VwZXIoX2RlZmF1bHRFcnJvclN0YXRlTWF0Y2hlciwgX3BhcmVudEZvcm0sIF9wYXJlbnRGb3JtR3JvdXAsIG5nQ29udHJvbCk7XG4gIH1cblxuICBuZ09uSW5pdCgpIHtcbiAgICAvLyBDYW4ndCB1c2UgaW5qZWN0aW9uIHRvIGdldCBjb21wb25lbnQgcmVmZXJlbmNlXG4gICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2FuZ3VsYXIvYW5ndWxhci9pc3N1ZXMvODI3N1xuICAgIHRoaXMuZWRpdG9yID0gdGhpcy52aWV3UmVmWydfZGF0YSddLmNvbXBvbmVudFZpZXcuY29tcG9uZW50O1xuICAgIHRoaXMuZWRpdG9yLmJsdXIuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgIHRoaXMuZm9jdXNlZCA9IGZhbHNlO1xuICAgICAgdGhpcy5zdGF0ZUNoYW5nZXMubmV4dCgpO1xuICAgIH0pO1xuICAgIHRoaXMuZWRpdG9yLmZvY3VzLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICB0aGlzLmZvY3VzZWQgPSB0cnVlO1xuICAgICAgdGhpcy5zdGF0ZUNoYW5nZXMubmV4dCgpO1xuICAgIH0pO1xuICB9XG5cbiAgbmdEb0NoZWNrKCkge1xuICAgIGlmICh0aGlzLm5nQ29udHJvbCkge1xuICAgICAgLy8gV2UgbmVlZCB0byByZS1ldmFsdWF0ZSB0aGlzIG9uIGV2ZXJ5IGNoYW5nZSBkZXRlY3Rpb24gY3ljbGUsIGJlY2F1c2UgdGhlcmUgYXJlIHNvbWVcbiAgICAgIC8vIGVycm9yIHRyaWdnZXJzIHRoYXQgd2UgY2FuJ3Qgc3Vic2NyaWJlIHRvIChlLmcuIHBhcmVudCBmb3JtIHN1Ym1pc3Npb25zKS4gVGhpcyBtZWFuc1xuICAgICAgLy8gdGhhdCB3aGF0ZXZlciBsb2dpYyBpcyBpbiBoZXJlIGhhcyB0byBiZSBzdXBlciBsZWFuIG9yIHdlIHJpc2sgZGVzdHJveWluZyB0aGUgcGVyZm9ybWFuY2UuXG4gICAgICB0aGlzLnVwZGF0ZUVycm9yU3RhdGUoKTtcbiAgICB9XG4gIH1cblxuICBzZXREZXNjcmliZWRCeUlkcyhpZHM6IHN0cmluZ1tdKSB7XG4gICAgdGhpcy5kZXNjcmliZWRCeSA9IGlkcy5qb2luKCcgJyk7XG4gIH1cblxuICBvbkNvbnRhaW5lckNsaWNrKCkge1xuICAgIGlmICh0aGlzLmVkaXRvci5lZGl0b3JJbnN0YW5jZSkge1xuICAgICAgdGhpcy5lZGl0b3IuZWRpdG9ySW5zdGFuY2UuZWRpdGluZy52aWV3LmZvY3VzKCk7XG4gICAgICB0aGlzLnN0YXRlQ2hhbmdlcy5uZXh0KCk7XG4gICAgfVxuICB9XG5cbn1cbiIsImltcG9ydCB7IERpcmVjdGl2ZSwgSW5wdXQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE1hdEZvcm1GaWVsZENvbnRyb2wgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9mb3JtLWZpZWxkJztcblxuaW1wb3J0IHsgTWF0Q2tlZGl0b3JEaXJlY3RpdmUgfSBmcm9tICcuL21hdC1ja2VkaXRvci5kaXJlY3RpdmUnO1xuXG5ARGlyZWN0aXZlKHtcbiAgc2VsZWN0b3I6ICdbbWF0Q2tlZGl0b3JCYWxsb29uXScsXG4gIHByb3ZpZGVyczogW1xuICAgIHsgcHJvdmlkZTogTWF0Rm9ybUZpZWxkQ29udHJvbCwgdXNlRXhpc3Rpbmc6IE1hdENrZWRpdG9yQmFsbG9vbkRpcmVjdGl2ZSB9LFxuICBdXG59KVxuZXhwb3J0IGNsYXNzIE1hdENrZWRpdG9yQmFsbG9vbkRpcmVjdGl2ZSBleHRlbmRzIE1hdENrZWRpdG9yRGlyZWN0aXZlIHtcblxuICBASW5wdXQoKVxuICBzZXQgdG9vbGJhcihzaG93OiBib29sZWFuKSB7XG4gICAgaWYgKHRoaXMuZWRpdG9yICYmIHNob3cgIT09IHRoaXMudG9vbGJhck9wZW4pIHtcbiAgICAgIGNvbnN0IGJhbGxvb24gPSB0aGlzLmVkaXRvci5lZGl0b3JJbnN0YW5jZS5wbHVnaW5zLmdldCgnQmFsbG9vblRvb2xiYXInKTtcbiAgICAgIGlmIChzaG93KSB7XG4gICAgICAgIHRoaXMuc2hvd1Rvb2xiYXIoYmFsbG9vbik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBiYWxsb29uLmhpZGUoKTtcbiAgICAgICAgdGhpcy50b29sYmFyT3BlbiA9IGZhbHNlO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBwcml2YXRlIHRvb2xiYXJPcGVuOiBib29sZWFuO1xuXG4gIG5nT25Jbml0KCkge1xuICAgIHN1cGVyLm5nT25Jbml0KCk7XG4gICAgdGhpcy5lZGl0b3IucmVhZHkuc3Vic2NyaWJlKGVkaXRvciA9PiB7XG4gICAgICBjb25zdCBiYWxsb29uID0gZWRpdG9yLnBsdWdpbnMuZ2V0KCdCYWxsb29uVG9vbGJhcicpO1xuICAgICAgYmFsbG9vbi5zdG9wTGlzdGVuaW5nKGVkaXRvci5tb2RlbC5kb2N1bWVudC5zZWxlY3Rpb24sICdjaGFuZ2U6cmFuZ2UnKTtcbiAgICAgIGJhbGxvb24uc3RvcExpc3RlbmluZyhiYWxsb29uLCAnX3NlbGVjdGlvbkNoYW5nZURlYm91bmNlZCcpO1xuICAgIH0pO1xuICAgIHRoaXMuZWRpdG9yLmZvY3VzLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICBpZiAodGhpcy50b29sYmFyT3Blbikge1xuICAgICAgICBjb25zdCBiYWxsb29uID0gdGhpcy5lZGl0b3IuZWRpdG9ySW5zdGFuY2UucGx1Z2lucy5nZXQoJ0JhbGxvb25Ub29sYmFyJyk7XG4gICAgICAgIHRoaXMuc2hvd1Rvb2xiYXIoYmFsbG9vbik7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIHNob3dUb29sYmFyKGJhbGxvb24pIHtcbiAgICBpZiAoIWJhbGxvb24uX2JhbGxvb24uaGFzVmlldyhiYWxsb29uLnRvb2xiYXJWaWV3KSkge1xuICAgICAgYmFsbG9vbi5saXN0ZW5Ubyh0aGlzLmVkaXRvci5lZGl0b3JJbnN0YW5jZS51aSwgJ3VwZGF0ZScsICgpID0+IHtcbiAgICAgICAgYmFsbG9vbi5fYmFsbG9vbi51cGRhdGVQb3NpdGlvbihiYWxsb29uLl9nZXRCYWxsb29uUG9zaXRpb25EYXRhKCkpO1xuICAgICAgfSk7XG4gICAgICBiYWxsb29uLl9iYWxsb29uLmFkZCh7XG4gICAgICAgIHZpZXc6IGJhbGxvb24udG9vbGJhclZpZXcsXG4gICAgICAgIHBvc2l0aW9uOiBiYWxsb29uLl9nZXRCYWxsb29uUG9zaXRpb25EYXRhKCksXG4gICAgICAgIGJhbGxvb25DbGFzc05hbWU6ICdjay10b29sYmFyLWNvbnRhaW5lcidcbiAgICAgIH0pO1xuICAgICAgdGhpcy50b29sYmFyT3BlbiA9IHRydWU7XG4gICAgfVxuICB9XG5cbn1cbiIsImltcG9ydCB7IERpcmVjdGl2ZSwgUmVuZGVyZXIyLCBFbGVtZW50UmVmLCBBZnRlckNvbnRlbnRJbml0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBEaXJlY3RpdmUoe1xuICBzZWxlY3RvcjogJ1tmb3JtRmllbGRTaXplcl0nXG59KVxuZXhwb3J0IGNsYXNzIEZvcm1GaWVsZFNpemVyRGlyZWN0aXZlIGltcGxlbWVudHMgQWZ0ZXJDb250ZW50SW5pdCB7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSByZW5kZXJlcjogUmVuZGVyZXIyLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgZWxlbWVudFJlZjogRWxlbWVudFJlZixcbiAgKSB7IH1cblxuICBuZ0FmdGVyQ29udGVudEluaXQoKTogdm9pZCB7XG4gICAgdGhpcy51cGRhdGVTaXplKCk7XG4gIH1cblxuICB1cGRhdGVTaXplKCkge1xuICAgIGNvbnN0IGluZml4ID0gdGhpcy5nZXRFbGVtZW50KCdtYXQtZm9ybS1maWVsZC1pbmZpeCcpO1xuICAgIHRoaXMucmVuZGVyZXIucmVtb3ZlU3R5bGUoaW5maXgsICdtaW4taGVpZ2h0Jyk7XG5cbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGNvbnN0IHdyYXBwZXIgPSB0aGlzLmdldEVsZW1lbnQoJ21hdC1mb3JtLWZpZWxkLXdyYXBwZXInKTtcbiAgICAgIGNvbnN0IG9mZnNldCA9IHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50Lm9mZnNldEhlaWdodCAtXG4gICAgICAgIHdyYXBwZXIub2Zmc2V0SGVpZ2h0IC1cbiAgICAgICAgcGFyc2VGbG9hdChnZXRDb21wdXRlZFN0eWxlKHdyYXBwZXIpLm1hcmdpblRvcCkgLVxuICAgICAgICBwYXJzZUZsb2F0KGdldENvbXB1dGVkU3R5bGUod3JhcHBlcikubWFyZ2luQm90dG9tKSArXG4gICAgICAgIHBhcnNlRmxvYXQoZ2V0Q29tcHV0ZWRTdHlsZShpbmZpeCkuaGVpZ2h0KTtcblxuICAgICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZShpbmZpeCwgJ21pbi1oZWlnaHQnLCBgJHtvZmZzZXR9cHhgKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0RWxlbWVudChuYW1lOiBzdHJpbmcpOiBIVE1MRWxlbWVudCB7XG4gICAgcmV0dXJuIHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUobmFtZSkuaXRlbSgwKTtcbiAgfVxuXG59XG4iLCJpbXBvcnQgeyBOZ01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgeyBNYXRDb250ZW50ZWRpdGFibGVEaXJlY3RpdmUgfSBmcm9tICcuL21hdC1jb250ZW50ZWRpdGFibGUuZGlyZWN0aXZlJztcbmltcG9ydCB7IE1hdENrZWRpdG9yRGlyZWN0aXZlIH0gZnJvbSAnLi9tYXQtY2tlZGl0b3IuZGlyZWN0aXZlJztcbmltcG9ydCB7IE1hdENrZWRpdG9yQmFsbG9vbkRpcmVjdGl2ZSB9IGZyb20gJy4vbWF0LWNrZWRpdG9yLWJhbGxvb24uZGlyZWN0aXZlJztcbmltcG9ydCB7IEZvcm1GaWVsZFNpemVyRGlyZWN0aXZlIH0gZnJvbSAnLi9mb3JtLWZpZWxkLXNpemVyLmRpcmVjdGl2ZSc7XG5cbkBOZ01vZHVsZSh7XG4gIGltcG9ydHM6IFtcbiAgXSxcbiAgZGVjbGFyYXRpb25zOiBbIE1hdENvbnRlbnRlZGl0YWJsZURpcmVjdGl2ZSwgTWF0Q2tlZGl0b3JEaXJlY3RpdmUsIE1hdENrZWRpdG9yQmFsbG9vbkRpcmVjdGl2ZSwgRm9ybUZpZWxkU2l6ZXJEaXJlY3RpdmUgXSxcbiAgZXhwb3J0czogWyBNYXRDb250ZW50ZWRpdGFibGVEaXJlY3RpdmUsIE1hdENrZWRpdG9yRGlyZWN0aXZlLCBNYXRDa2VkaXRvckJhbGxvb25EaXJlY3RpdmUsIEZvcm1GaWVsZFNpemVyRGlyZWN0aXZlIF0sXG59KVxuZXhwb3J0IGNsYXNzIE1hdENvbnRlbnRlZGl0YWJsZU1vZHVsZSB7IH1cbiIsImltcG9ydCB7IEFic3RyYWN0Q29udHJvbCwgVmFsaWRhdGlvbkVycm9ycywgVmFsaWRhdG9yRm4gfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5cbmZ1bmN0aW9uIGdldFRleHQoaHRtbDogc3RyaW5nKSB7XG4gIGlmIChodG1sKSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICBlbGVtZW50LmlubmVySFRNTCA9IGh0bWw7XG4gICAgcmV0dXJuIGVsZW1lbnQudGV4dENvbnRlbnQucmVwbGFjZSgvXFxzL2csICcnKTtcbiAgfVxuICByZXR1cm4gJyc7XG59XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uXG4gKiBQcm92aWRlcyBhIHNldCBvZiBidWlsdC1pbiB2YWxpZGF0b3JzIHRoYXQgY2FuIGJlIHVzZWQgYnkgZm9ybSBjb250cm9scy5cbiAqXG4gKiBBIHZhbGlkYXRvciBpcyBhIGZ1bmN0aW9uIHRoYXQgcHJvY2Vzc2VzIGEgYEZvcm1Db250cm9sYCBvciBjb2xsZWN0aW9uIG9mXG4gKiBjb250cm9scyBhbmQgcmV0dXJucyBhbiBlcnJvciBtYXAgb3IgbnVsbC4gQSBudWxsIG1hcCBtZWFucyB0aGF0IHZhbGlkYXRpb24gaGFzIHBhc3NlZC5cbiAqXG4gKiBAc2VlIFtGb3JtIFZhbGlkYXRpb25dKC9ndWlkZS9mb3JtLXZhbGlkYXRpb24pXG4gKlxuICogQHB1YmxpY0FwaVxuICovXG5leHBvcnQgY2xhc3MgSHRtbFZhbGlkYXRvcnMge1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb25cbiAgICogVmFsaWRhdG9yIHRoYXQgcmVxdWlyZXMgdGhlIGNvbnRyb2wgaGF2ZSBhIG5vbi1lbXB0eSB2YWx1ZS5cbiAgICpcbiAgICogQHVzYWdlTm90ZXNcbiAgICpcbiAgICogIyMjIFZhbGlkYXRlIHRoYXQgdGhlIGZpZWxkIGlzIG5vbi1lbXB0eVxuICAgKlxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IGNvbnRyb2wgPSBuZXcgRm9ybUNvbnRyb2woJycsIFZhbGlkYXRvcnMucmVxdWlyZWQpO1xuICAgKlxuICAgKiBjb25zb2xlLmxvZyhjb250cm9sLmVycm9ycyk7IC8vIHtyZXF1aXJlZDogdHJ1ZX1cbiAgICogYGBgXG4gICAqXG4gICAqIEByZXR1cm5zIEFuIGVycm9yIG1hcCB3aXRoIHRoZSBgcmVxdWlyZWRgIHByb3BlcnR5XG4gICAqIGlmIHRoZSB2YWxpZGF0aW9uIGNoZWNrIGZhaWxzLCBvdGhlcndpc2UgYG51bGxgLlxuICAgKlxuICAgKi9cbiAgc3RhdGljIHJlcXVpcmVkKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCk6IFZhbGlkYXRpb25FcnJvcnMgfCBudWxsIHtcbiAgICBjb25zdCB0ZXh0ID0gZ2V0VGV4dChjb250cm9sLnZhbHVlKTtcbiAgICByZXR1cm4gdGV4dCA/IG51bGwgOiB7ICdyZXF1aXJlZCc6IHRydWUgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb25cbiAgICogVmFsaWRhdG9yIHRoYXQgcmVxdWlyZXMgdGhlIGxlbmd0aCBvZiB0aGUgY29udHJvbCdzIHZhbHVlIHRvIGJlIGdyZWF0ZXIgdGhhbiBvciBlcXVhbFxuICAgKiB0byB0aGUgcHJvdmlkZWQgbWluaW11bSBsZW5ndGguIFRoaXMgdmFsaWRhdG9yIGlzIGFsc28gcHJvdmlkZWQgYnkgZGVmYXVsdCBpZiB5b3UgdXNlIHRoZVxuICAgKiB0aGUgSFRNTDUgYG1pbmxlbmd0aGAgYXR0cmlidXRlLlxuICAgKlxuICAgKiBAdXNhZ2VOb3Rlc1xuICAgKlxuICAgKiAjIyMgVmFsaWRhdGUgdGhhdCB0aGUgZmllbGQgaGFzIGEgbWluaW11bSBvZiAzIGNoYXJhY3RlcnNcbiAgICpcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCBjb250cm9sID0gbmV3IEZvcm1Db250cm9sKCduZycsIFZhbGlkYXRvcnMubWluTGVuZ3RoKDMpKTtcbiAgICpcbiAgICogY29uc29sZS5sb2coY29udHJvbC5lcnJvcnMpOyAvLyB7bWlubGVuZ3RoOiB7cmVxdWlyZWRMZW5ndGg6IDMsIGFjdHVhbExlbmd0aDogMn19XG4gICAqIGBgYFxuICAgKlxuICAgKiBgYGBodG1sXG4gICAqIDxpbnB1dCBtaW5sZW5ndGg9XCI1XCI+XG4gICAqIGBgYFxuICAgKlxuICAgKiBAcmV0dXJucyBBIHZhbGlkYXRvciBmdW5jdGlvbiB0aGF0IHJldHVybnMgYW4gZXJyb3IgbWFwIHdpdGggdGhlXG4gICAqIGBtaW5sZW5ndGhgIGlmIHRoZSB2YWxpZGF0aW9uIGNoZWNrIGZhaWxzLCBvdGhlcndpc2UgYG51bGxgLlxuICAgKi9cbiAgc3RhdGljIG1pbkxlbmd0aChtaW5MZW5ndGg6IG51bWJlcik6IFZhbGlkYXRvckZuIHtcbiAgICBjb25zdCBmbiA9IChjb250cm9sOiBBYnN0cmFjdENvbnRyb2wpOiBWYWxpZGF0aW9uRXJyb3JzIHwgbnVsbCA9PiB7XG4gICAgICBjb25zdCB0ZXh0ID0gZ2V0VGV4dChjb250cm9sLnZhbHVlKTtcbiAgICAgIGlmICh0ZXh0KSB7XG4gICAgICAgIHJldHVybiB0ZXh0Lmxlbmd0aCA8IG1pbkxlbmd0aCA/XG4gICAgICAgICAgeyAnbWlubGVuZ3RoJzogeyAncmVxdWlyZWRMZW5ndGgnOiBtaW5MZW5ndGgsICdhY3R1YWxMZW5ndGgnOiB0ZXh0Lmxlbmd0aCB9IH0gOlxuICAgICAgICAgIG51bGw7XG4gICAgICB9XG4gICAgICByZXR1cm4gbnVsbDsgIC8vIGRvbid0IHZhbGlkYXRlIGVtcHR5IHZhbHVlcyB0byBhbGxvdyBvcHRpb25hbCBjb250cm9sc1xuICAgIH07XG4gICAgcmV0dXJuIGZuO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvblxuICAgKiBWYWxpZGF0b3IgdGhhdCByZXF1aXJlcyB0aGUgbGVuZ3RoIG9mIHRoZSBjb250cm9sJ3MgdmFsdWUgdG8gYmUgbGVzcyB0aGFuIG9yIGVxdWFsXG4gICAqIHRvIHRoZSBwcm92aWRlZCBtYXhpbXVtIGxlbmd0aC4gVGhpcyB2YWxpZGF0b3IgaXMgYWxzbyBwcm92aWRlZCBieSBkZWZhdWx0IGlmIHlvdSB1c2UgdGhlXG4gICAqIHRoZSBIVE1MNSBgbWF4bGVuZ3RoYCBhdHRyaWJ1dGUuXG4gICAqXG4gICAqIEB1c2FnZU5vdGVzXG4gICAqXG4gICAqICMjIyBWYWxpZGF0ZSB0aGF0IHRoZSBmaWVsZCBoYXMgbWF4aW11bSBvZiA1IGNoYXJhY3RlcnNcbiAgICpcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCBjb250cm9sID0gbmV3IEZvcm1Db250cm9sKCdBbmd1bGFyJywgVmFsaWRhdG9ycy5tYXhMZW5ndGgoNSkpO1xuICAgKlxuICAgKiBjb25zb2xlLmxvZyhjb250cm9sLmVycm9ycyk7IC8vIHttYXhsZW5ndGg6IHtyZXF1aXJlZExlbmd0aDogNSwgYWN0dWFsTGVuZ3RoOiA3fX1cbiAgICogYGBgXG4gICAqXG4gICAqIGBgYGh0bWxcbiAgICogPGlucHV0IG1heGxlbmd0aD1cIjVcIj5cbiAgICogYGBgXG4gICAqXG4gICAqIEByZXR1cm5zIEEgdmFsaWRhdG9yIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhbiBlcnJvciBtYXAgd2l0aCB0aGVcbiAgICogYG1heGxlbmd0aGAgcHJvcGVydHkgaWYgdGhlIHZhbGlkYXRpb24gY2hlY2sgZmFpbHMsIG90aGVyd2lzZSBgbnVsbGAuXG4gICAqL1xuICBzdGF0aWMgbWF4TGVuZ3RoKG1heExlbmd0aDogbnVtYmVyKTogVmFsaWRhdG9yRm4ge1xuICAgIGNvbnN0IGZuID0gKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCk6IFZhbGlkYXRpb25FcnJvcnMgfCBudWxsID0+IHtcbiAgICAgIGNvbnN0IHRleHQgPSBnZXRUZXh0KGNvbnRyb2wudmFsdWUpO1xuICAgICAgcmV0dXJuIHRleHQubGVuZ3RoID4gbWF4TGVuZ3RoID9cbiAgICAgICAgeyAnbWF4bGVuZ3RoJzogeyAncmVxdWlyZWRMZW5ndGgnOiBtYXhMZW5ndGgsICdhY3R1YWxMZW5ndGgnOiB0ZXh0Lmxlbmd0aCB9IH0gOlxuICAgICAgICBudWxsO1xuICAgIH07XG4gICAgcmV0dXJuIGZuO1xuICB9XG59XG4iXSwibmFtZXMiOlsiTWF0Rm9ybUZpZWxkQ29udHJvbCIsIkVycm9yU3RhdGVNYXRjaGVyIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFBQTs7O0FBbUJBLE1BQU0sWUFBWTs7Ozs7OztJQUNoQixZQUFtQix5QkFBNEMsRUFDNUMsYUFDQSxrQkFFQTtRQUpBLDhCQUF5QixHQUF6Qix5QkFBeUIsQ0FBbUI7UUFDNUMsZ0JBQVcsR0FBWCxXQUFXO1FBQ1gscUJBQWdCLEdBQWhCLGdCQUFnQjtRQUVoQixjQUFTLEdBQVQsU0FBUztLQUFlO0NBQzVDOztBQUNELE1BQWEsa0JBQWtCLEdBQzdCLGVBQWUsQ0FBQyxZQUFZLENBQUMsQ0FBQztBQVNoQyxNQUFhLDJCQUE0QixTQUFRLGtCQUFrQjs7Ozs7Ozs7O0lBNEVqRSxZQUNVLFlBQ0EsVUFDbUIsU0FBb0IsRUFDbkMsV0FBbUIsRUFDbkIsZ0JBQW9DLEVBQ2hELHlCQUE0QztRQUU1QyxLQUFLLENBQUMseUJBQXlCLEVBQUUsV0FBVyxFQUFFLGdCQUFnQixFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBUG5FLGVBQVUsR0FBVixVQUFVO1FBQ1YsYUFBUSxHQUFSLFFBQVE7UUFDVyxjQUFTLEdBQVQsU0FBUyxDQUFXO1FBN0RqRCxvQkFBdUMsSUFBSSxPQUFPLEVBQVEsQ0FBQztRQUUzRCxVQUFvQixhQUFhLDJCQUEyQixDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUM7UUFZeEUsZUFBVSxLQUFLLENBQUM7UUFFaEIsb0JBQXVDLENBQUMsTUFBTSxFQUFFLGlCQUFpQixDQUFDLENBQUM7eUJBZS9DLEtBQUs7eUJBVUwsS0FBSztRQUt6QixtQkFBYyxXQUFXLENBQUM7UUFFMUIsbUJBQW9ELEVBQUUsQ0FBQztRQVF2RCx5QkFBNkIsV0FBVyxDQUFDOzs7UUFhdkMsSUFBSSxJQUFJLENBQUMsU0FBUyxJQUFJLElBQUksRUFBRTtZ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