UNPKG

mat-contenteditable

Version:

Angular contenteditable directive for Angular forms and Material Design

704 lines (694 loc) 59.4 kB
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>&nbsp;</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+IHtcbiAgICAgIGNvbnN0IHRleHQgPSBnZXRUZXh0KGNvbnRyb2wudmFsdWUpO1xuICAgICAgcmV0dXJuIHRleHQubGVuZ3RoID4gbWF4TGVuZ3RoID9cbiAgICAgICAgeyAnbWF4bGVuZ3RoJzogeyAncmVxdWlyZWRMZW5ndGgnOiBtYXhMZW5ndGgsICdhY3R1YWxMZW5ndGgnOiB0ZXh0Lmxlbmd0aCB9IH0gOlxuICAgICAgICBudWxsO1xuICAgIH07XG4gICAgcmV0dXJuIGZuO1xuICB9XG59XG4iXSwibmFtZXMiOlsiTWF0Rm9ybUZpZWxkQ29udHJvbCIsIkVycm9yU3RhdGVNYXRjaGVyIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFBQTs7O0FBbUJBLE1BQU0sWUFBWTs7Ozs7OztJQUNoQixZQUFtQix5QkFBNEMsRUFDNUMsYUFDQSxrQkFFQTtRQUpBLDhCQUF5QixHQUF6Qix5QkFBeUIsQ0FBbUI7UUFDNUMsZ0JBQVcsR0FBWCxXQUFXO1FBQ1gscUJBQWdCLEdBQWhCLGdCQUFnQjtRQUVoQixjQUFTLEdBQVQsU0FBUztLQUFlO0NBQzVDOztBQUNELE1BQWEsa0JBQWtCLEdBQzdCLGVBQWUsQ0FBQyxZQUFZLENBQUMsQ0FBQztBQVNoQyxNQUFhLDJCQUE0QixTQUFRLGtCQUFrQjs7Ozs7Ozs7O0lBNEVqRSxZQUNVLFlBQ0EsVUFDbUIsU0FBb0IsRUFDbkMsV0FBbUIsRUFDbkIsZ0JBQW9DLEVBQ2hELHlCQUE0QztRQUU1QyxLQUFLLENBQUMseUJBQXlCLEVBQUUsV0FBVyxFQUFFLGdCQUFnQixFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBUG5FLGVBQVUsR0FBVixVQUFVO1FBQ1YsYUFBUSxHQUFSLFFBQVE7UUFDVyxjQUFTLEdBQVQsU0FBUyxDQUFXO1FBN0RqRCxvQkFBdUMsSUFBSSxPQUFPLEVBQVEsQ0FBQztRQUUzRCxVQUFvQixhQUFhLDJCQUEyQixDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUM7UUFZeEUsZUFBVSxLQUFLLENBQUM7UUFFaEIsb0JBQXVDLENBQUMsTUFBTSxFQUFFLGlCQUFpQixDQUFDLENBQUM7eUJBZS9DLEtBQUs7eUJBVUwsS0FBSztRQUt6QixtQkFBYyxXQUFXLENBQUM7UUFFMUIsbUJBQW9ELEVBQUUsQ0FBQztRQVF2RCx5QkFBNkIsV0FBVyxDQUFDOzs7UUFhdkMsSUFBSSxJQUFJLENBQUMsU0FBUyxJQUFJLElBQUksRUFBRTtZQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQztTQUFFO0tBQ3JFOzs7O0lBL0VELElBQ0ksS0FBSyxLQUFhLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUMsRUFBRTs7Ozs7SUFDckYsSUFBSSxLQUFLLENBQUMsS0FBYTtRQUNyQixJQUFJLEtBQUssS0FBSyxJQUFJLENBQUMsS0FBSyxFQUFFO1lBQ3hCLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLEtBQUssQ0FBQztZQUM5RCxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksRUFBRSxDQUFDO1NBQzFCO0tBQ0Y7Ozs7SUFNRCxJQUNJLFdBQVc7UUFDYixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUM7S0FDMUI7Ozs7O0lBQ0QsSUFBSSxXQUFXLENBQUMsR0FBRztRQUNqQixJQUFJLENBQUMsWUFBWSxHQUFHLEdBQUcsQ0FBQztRQUN4QixJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksRUFBRSxDQUFDO0tBQzFCOzs7O0lBTUQsSUFBSSxLQUFLO1FBQ1AsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0tBQzlEOzs7O0lBRUQsSUFBSSxnQkFBZ0IsS0FBYyxPQUFPLElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUU7Ozs7SUFFdkUsSUFDSSxRQUFRO1FBQ1YsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDO0tBQ3ZCOzs7OztJQUNELElBQUksUUFBUSxDQUFDLEdBQUc7UUFDZCxJQUFJLENBQUMsU0FBUyxHQUFHLHFCQUFxQixDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzVDLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLENBQUM7S0FDMUI7Ozs7SUFHRCxJQUNJLFFBQVE7UUFDVixPQUFPLElBQUksQ0FBQyxTQUFTLENBQUM7S0FDdkI7Ozs7O0lBQ0QsSUFBSSxRQUFRLENBQUMsR0FBRztRQUNkLElBQUksQ0FBQyxTQUFTLEdBQUcscUJBQXFCLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDNUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsQ0FBQztLQUMxQjs7OztJQWdDRCxTQUFTO1FBQ1AsSUFBSSxJQUFJLENBQUMsU0FBUyxFQUFFOzs7O1lBSWxCLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1NBQ3pCO0tBQ0Y7Ozs7SUFHRCxZQUFZO1FBQ1YsSUFBSSxPQUFPLElBQUksQ0FBQyxRQUFRLEtBQUssVUFBVSxFQUFFO1lBQ3ZDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQztTQUN0RTtLQUNGOzs7O0lBR0QsYUFBYTtRQUNYLElBQUksSUFBSSxDQUFDLE9BQU8sS0FBSyxJQUFJLEVBQUU7WUFDekIsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7WUFDcEIsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLEVBQUUsQ0FBQztTQUMxQjtLQUNGOzs7O0lBR0QsYUFBYTtRQUNYLElBQUksT0FBTyxJQUFJLENBQUMsU0FBUyxLQUFLLFVBQVUsRUFBRTtZQUN4QyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7U0FDbEI7UUFDRCxJQUFJLElBQUksQ0FBQyxPQUFPLEtBQUssS0FBSyxFQUFFO1lBQzFCLElBQUksQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDO1lBQ3JCLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLENBQUM7U0FDMUI7S0FDRjs7Ozs7SUFFRCxpQkFBaUIsQ0FBQyxHQUFhO1FBQzdCLElBQUksQ0FBQyxXQUFXLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztLQUNsQzs7OztJQUVELGdCQUFnQixLQUFLLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLEtBQUssRUFBRSxDQUFDLEVBQUU7Ozs7Ozs7Ozs7SUFTN0QsVUFBVSxDQUFDLEtBQVU7O1FBQ25CLE1BQU0sZUFBZSxHQUFHLEtBQUssSUFBSSxJQUFJLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQztRQUNuRCxJQUFJLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLGFBQWEsRUFBRSxJQUFJLENBQUMsaUJBQWlCLEVBQUUsZUFBZSxDQUFDLENBQUM7S0FDbkc7Ozs7Ozs7Ozs7SUFTRCxnQkFBZ0IsQ0FBQyxFQUFjO1FBQzdCLElBQUksQ0FBQyxRQUFRLEdBQUcsRUFBRSxD