UNPKG

@firestitch/address

Version:
379 lines 58.5 kB
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, ElementRef, EventEmitter, forwardRef, HostBinding, inject, Input, NgZone, Output, ViewChild, } from '@angular/core'; import { NG_VALIDATORS, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms'; import { FocusMonitor } from '@angular/cdk/a11y'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { MatAutocomplete, MatAutocompleteTrigger, } from '@angular/material/autocomplete'; import { guid } from '@firestitch/common'; import { FsMap } from '@firestitch/map'; import { from, fromEvent, of } from 'rxjs'; import { debounceTime, distinctUntilChanged, filter, map, switchMap, tap, } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { AddressFormat } from '../../enums/address-format.enum'; import { addressIsEmpty } from '../../helpers/address-is-empty'; import { createEmptyAddress } from '../../helpers/create-empty-address'; import { extractUnit } from '../../helpers/extract-unit'; import { googlePlaceToFsAddress } from '../../helpers/google-place-to-address'; import { MatFormField, MatLabel, MatHint } from '@angular/material/form-field'; import { MatInput } from '@angular/material/input'; import { FsFormModule } from '@firestitch/form'; import { FsClearModule } from '@firestitch/clear'; import { MatOption as MatOption_1 } from '@angular/material/core'; import * as i0 from "@angular/core"; import * as i1 from "@angular/forms"; import * as i2 from "@firestitch/form"; import * as i3 from "@firestitch/clear"; export class FsAddressAutocompleteComponent { _map = inject(FsMap); _ngZone = inject(NgZone); _fm = inject(FocusMonitor); _elementRef = inject(ElementRef); _cdRef = inject(ChangeDetectorRef); static nextId = 0; format = AddressFormat.TwoLine; readonly = false; showClear = true; suggestions = false; set config(value) { this._config = value; if (this._config) { this.required = ((this.config.name && this.config.name.required) || (this.config.country && this.config.country.required) || (this.config.region && this.config.region.required) || (this.config.city && this.config.city.required) || (this.config.street && this.config.street.required) || (this.config.address2 && this.config.address2.required) || (this.config.address3 && this.config.address3.required) || (this.config.zip && this.config.zip.required)); } } get config() { return this._config; } addressChange = new EventEmitter(); addressManual = new EventEmitter(); searchElement; autoCompleteRef; autocompleteTrigger; id = `fs-address-autocomplete-${FsAddressAutocompleteComponent.nextId++}`; inputAddress = this._defaultInputAddress(); googleSuggestions = []; googlePlace = null; onChange; onTouched; focused = false; autocompleteName = `search-${guid('xxxxxxxx')}`; _config = {}; _address = {}; _searchText = ''; _disabled = false; _required = false; _placeholder; _destroyRef = inject(DestroyRef); set value(value) { this._address = value; this.onChange(this._address); } get value() { return this._address; } get disabled() { return this._disabled; } set disabled(value) { this._disabled = coerceBooleanProperty(value); } get required() { return this._required; } set required(req) { this._required = coerceBooleanProperty(req); } get placeholder() { return this._placeholder; } set placeholder(plh) { this._placeholder = plh; } get shouldLabelFloat() { return this.focused; } get empty() { return addressIsEmpty(this.value); } ngOnInit() { this._initGoogleMap(); this._listenUserTyping(); this._listenAutocompleteSelection(); this._registerFocusMonitor(); } writeValue(value) { this._address = value; this.inputAddress = value; this._cdRef.markForCheck(); } onContainerClick(event) { if (event.target.tagName.toLowerCase() !== 'input') { this.searchElement.nativeElement.focus(); this._elementRef.nativeElement.querySelector('input').focus(); } } registerOnChange(fn) { this.onChange = fn; } registerOnTouched(fn) { this.onTouched = fn; } displayWith = (value) => { if (value && typeof value === 'object') { return this.value?.street; } else if (!this.empty) { return ''; } }; validate(control) { const validationErrors = {}; const requiredField = []; const parts = ['name', 'street', 'city', 'region', 'zip', 'country', 'lat', 'lng']; if (this.required && this.empty) { validationErrors.required = true; } if (!this.empty) { parts.forEach((part) => { if (this.config[part] && this.config[part].required && !this.value[part]) { requiredField.push([part]); } }); if (((this.config.lat && this.config.lat.required) || (this.config.lng && this.config.lng.required)) && (!this.value.lat || !this.value.lat)) { validationErrors.invalid = 'position on map'; } if (requiredField.length) { if (requiredField.length === 1) { validationErrors.invalid = `The ${requiredField[0]} is required`; } else { const last = requiredField.pop(); validationErrors.invalid = `The ${requiredField.join(', ')} and ${last} are required`; } } } return validationErrors; } clear() { this.inputAddress = this._defaultInputAddress(); this.value = createEmptyAddress(); this.addressChange.emit(null); this._clearPredictions(); setTimeout(() => { this.autocompleteTrigger.openPanel(); }); } manual(value) { this.addressManual.emit(value); } // Search input can't be null. We implemented required validation to show asterisk if needed // But general validation placed in another level and not depends of this input // This hack allow us to show asterisk but disable extra validation _defaultInputAddress() { return null; } _listenUserTyping() { this._ngZone.runOutsideAngular(() => { fromEvent(this.searchElement.nativeElement, 'keydown') .pipe(filter((event) => event.code === 'Tab'), map(() => this.autocompleteTrigger.activeOption?.value), filter((place) => !!place && this.googleSuggestions.length !== 0), switchMap((place) => this._placeToAddress(place)), takeUntilDestroyed(this._destroyRef)) .subscribe((address) => { this._selectAddress(address); this._clearPredictions(); }); fromEvent(this.searchElement.nativeElement, 'keyup') .pipe(debounceTime(200), filter((event) => { return event.code !== 'Enter' && event.code !== 'Tab'; }), map((event) => { return event.target.value; }), tap((text) => { if (!text) { this._clearPredictions(); } }), filter((value) => !!value), tap((value) => { this._searchText = value; if (!value) { this._address = { ...this._address, street: value, }; this._selectAddress(this._address); } }), distinctUntilChanged(), switchMap((text) => { return this._getPlaceSuggestions(text); }), takeUntilDestroyed(this._destroyRef)) .subscribe((suggestions) => { this._ngZone.run(() => { this.googleSuggestions = [ ...suggestions, ]; this._cdRef.markForCheck(); }); }); }); } _clearPredictions() { this.googleSuggestions = []; this._cdRef.markForCheck(); } _selectAddress(address) { this.value = address; this.addressChange.emit(address); } _placeToAddress(suggestion) { if (!suggestion || !this.googlePlace) { return of(null); } const place = suggestion.placePrediction.toPlace(); const fetchFieldsRequestOptions = { fields: [ 'displayName', 'location', 'addressComponents', 'formattedAddress', ], }; return from(place.fetchFields(fetchFieldsRequestOptions)) .pipe(map(({ place }) => { if (!place) { return {}; } return googlePlaceToFsAddress(place, this.config); })); } _listenAutocompleteSelection() { this.autoCompleteRef.optionSelected .pipe(map((event) => event.option), // used to get the value from input when "manual" option selected filter((option) => { if (option.value instanceof google.maps.places.AutocompleteSuggestion) { return true; } this.manual(option.value.value); return false; }), map((option) => { return option.value; }), switchMap((value) => this._placeToAddress(value)), takeUntilDestroyed(this._destroyRef)) .subscribe((address) => { this._ngZone.run(() => { this.searchElement.nativeElement.blur(); this.value = address; const { unit } = extractUnit(this._searchText); if (unit) { address.address2 = unit; } this.addressChange.emit(address); this.inputAddress = address; this._cdRef.markForCheck(); }); }); } _initGoogleMap() { this._ngZone.runOutsideAngular(() => { this._map.loaded$ .pipe(takeUntilDestroyed(this._destroyRef)) .subscribe(() => { this.googlePlace = new google.maps.places.Place({ id: this.id }); }); }); } _getPlaceSuggestions(address) { const { text } = extractUnit(address); const placesRequest = google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions({ input: text }); return placesRequest .then((result) => { return result.suggestions; }) .catch(() => { return []; }); } _registerFocusMonitor() { this._fm.monitor(this._elementRef, true) .pipe(filter(() => !this.disabled), takeUntilDestroyed(this._destroyRef)) .subscribe((origin) => { this.focused = !!origin; }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: FsAddressAutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.7", type: FsAddressAutocompleteComponent, isStandalone: true, selector: "fs-address-autocomplete", inputs: { format: "format", readonly: "readonly", showClear: "showClear", suggestions: "suggestions", config: "config", disabled: "disabled", required: "required", placeholder: "placeholder" }, outputs: { addressChange: "addressChange", addressManual: "addressManual" }, host: { properties: { "id": "this.id", "class.floating": "this.shouldLabelFloat" } }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FsAddressAutocompleteComponent), multi: true, }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => FsAddressAutocompleteComponent), multi: true, }, ], viewQueries: [{ propertyName: "searchElement", first: true, predicate: ["searchInput"], descendants: true, read: ElementRef, static: true }, { propertyName: "autoCompleteRef", first: true, predicate: MatAutocomplete, descendants: true, static: true }, { propertyName: "autocompleteTrigger", first: true, predicate: MatAutocompleteTrigger, descendants: true, static: true }], ngImport: i0, template: "<mat-form-field [floatLabel]=\"empty ? 'auto' : 'always'\">\n <mat-label>\n {{ placeholder }}\n </mat-label>\n <ng-content></ng-content>\n <input\n matInput\n type=\"text\"\n autocomplete=\"off\"\n [(ngModel)]=\"inputAddress\"\n [matAutocomplete]=\"autocomplete\"\n [name]=\"autocompleteName\"\n [disabled]=\"disabled\"\n [fsClear]=\"showClear && !empty && !disabled && !readonly\"\n (cleared)=\"clear()\"\n #searchInput=\"ngModel\">\n <mat-autocomplete\n [displayWith]=\"displayWith\"\n autoActiveFirstOption\n [class]=\"'fs-autocomplete-pane'\"\n #autocomplete=\"matAutocomplete\">\n @for (option of googleSuggestions; track option) {\n <mat-option [value]=\"option\">\n {{ option.placePrediction.text.text }}\n </mat-option>\n }\n @if (!config.hideEnterManually) {\n <div class=\"static-options\">\n <mat-option [value]=\"{ manual: true, value: searchInput.value }\">\n Enter address manually\n </mat-option>\n </div>\n }\n </mat-autocomplete>\n @if (config.hint) {\n <mat-hint>\n {{ config.hint }}\n </mat-hint>\n }\n</mat-form-field>", styles: [".static-options{position:sticky;bottom:0;width:100%;background:#fff;border-top:1px solid #e0e0e0}mat-form-field{width:100%}mat-option ::ng-deep mat-pseudo-checkbox{display:none}:host(.ng-invalid.ng-dirty) ::ng-deep .mat-form-field-outline{color:#f44336}:host(.ng-invalid.ng-dirty) ::ng-deep .mat-form-field-outline-thick{opacity:1}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: FsFormModule }, { kind: "directive", type: i2.FsFormNoFsValidatorsDirective, selector: "[ngModel]:not([required]):not([fsFormRequired]):not([fsFormCompare]):not([fsFormDateRange]):not([fsFormEmail]):not([fsFormEmails]):not([fsFormFunction]):not([fsFormGreater]):not([fsFormGreaterEqual]):not([fsFormInteger]):not([fsFormLesser]):not([fsFormMax]):not([fsFormMaxLength]):not([fsFormMin]):not([fsFormMinLength]):not([fsFormNumeric]):not([fsFormPattern]):not([fsFormPhone]):not([fsFormUrl]):not([validate])" }, { kind: "ngmodule", type: FsClearModule }, { kind: "component", type: i3.FsClearComponent, selector: "[fsClear]", inputs: ["ngModel", "visible", "fsClear"], outputs: ["ngModelChange", "cleared"] }, { kind: "component", type: MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: MatOption_1, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: FsAddressAutocompleteComponent, decorators: [{ type: Component, args: [{ selector: 'fs-address-autocomplete', changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FsAddressAutocompleteComponent), multi: true, }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => FsAddressAutocompleteComponent), multi: true, }, ], standalone: true, imports: [ MatFormField, MatLabel, MatInput, FormsModule, MatAutocompleteTrigger, FsFormModule, FsClearModule, MatAutocomplete, MatOption_1, MatHint, ], template: "<mat-form-field [floatLabel]=\"empty ? 'auto' : 'always'\">\n <mat-label>\n {{ placeholder }}\n </mat-label>\n <ng-content></ng-content>\n <input\n matInput\n type=\"text\"\n autocomplete=\"off\"\n [(ngModel)]=\"inputAddress\"\n [matAutocomplete]=\"autocomplete\"\n [name]=\"autocompleteName\"\n [disabled]=\"disabled\"\n [fsClear]=\"showClear && !empty && !disabled && !readonly\"\n (cleared)=\"clear()\"\n #searchInput=\"ngModel\">\n <mat-autocomplete\n [displayWith]=\"displayWith\"\n autoActiveFirstOption\n [class]=\"'fs-autocomplete-pane'\"\n #autocomplete=\"matAutocomplete\">\n @for (option of googleSuggestions; track option) {\n <mat-option [value]=\"option\">\n {{ option.placePrediction.text.text }}\n </mat-option>\n }\n @if (!config.hideEnterManually) {\n <div class=\"static-options\">\n <mat-option [value]=\"{ manual: true, value: searchInput.value }\">\n Enter address manually\n </mat-option>\n </div>\n }\n </mat-autocomplete>\n @if (config.hint) {\n <mat-hint>\n {{ config.hint }}\n </mat-hint>\n }\n</mat-form-field>", styles: [".static-options{position:sticky;bottom:0;width:100%;background:#fff;border-top:1px solid #e0e0e0}mat-form-field{width:100%}mat-option ::ng-deep mat-pseudo-checkbox{display:none}:host(.ng-invalid.ng-dirty) ::ng-deep .mat-form-field-outline{color:#f44336}:host(.ng-invalid.ng-dirty) ::ng-deep .mat-form-field-outline-thick{opacity:1}\n"] }] }], propDecorators: { format: [{ type: Input }], readonly: [{ type: Input }], showClear: [{ type: Input }], suggestions: [{ type: Input }], config: [{ type: Input }], addressChange: [{ type: Output }], addressManual: [{ type: Output }], searchElement: [{ type: ViewChild, args: ['searchInput', { static: true, read: ElementRef }] }], autoCompleteRef: [{ type: ViewChild, args: [MatAutocomplete, { static: true }] }], autocompleteTrigger: [{ type: ViewChild, args: [MatAutocompleteTrigger, { static: true }] }], id: [{ type: HostBinding }], disabled: [{ type: Input }], required: [{ type: Input }], placeholder: [{ type: Input }], shouldLabelFloat: [{ type: HostBinding, args: ['class.floating'] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRkcmVzcy1hdXRvY29tcGxldGUuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9jb21wb25lbnRzL2FkZHJlc3MtYXV0b2NvbXBsZXRlL2FkZHJlc3MtYXV0b2NvbXBsZXRlLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3NyYy9hcHAvY29tcG9uZW50cy9hZGRyZXNzLWF1dG9jb21wbGV0ZS9hZGRyZXNzLWF1dG9jb21wbGV0ZS5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLGlCQUFpQixFQUNqQixTQUFTLEVBQ1QsVUFBVSxFQUNWLFVBQVUsRUFDVixZQUFZLEVBQ1osVUFBVSxFQUNWLFdBQVcsRUFDWCxNQUFNLEVBQ04sS0FBSyxFQUNMLE1BQU0sRUFFTixNQUFNLEVBQ04sU0FBUyxHQUNWLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBeUMsYUFBYSxFQUFFLGlCQUFpQixFQUErQixXQUFXLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVuSixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDakQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDOUQsT0FBTyxFQUNMLGVBQWUsRUFFZixzQkFBc0IsR0FFdkIsTUFBTSxnQ0FBZ0MsQ0FBQztBQUV4QyxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDMUMsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRXhDLE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFjLEVBQUUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN2RCxPQUFPLEVBQ0wsWUFBWSxFQUNaLG9CQUFvQixFQUNwQixNQUFNLEVBQ04sR0FBRyxFQUNILFNBQVMsRUFDVCxHQUFHLEdBQ0osTUFBTSxn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QUFnQjtzQkFEMUIsV0FBVzt1QkFBQyxnQkFBZ0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSxcbiAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gIENvbXBvbmVudCxcbiAgRGVzdHJveVJlZixcbiAgRWxlbWVudFJlZixcbiAgRXZlbnRFbWl0dGVyLFxuICBmb3J3YXJkUmVmLFxuICBIb3N0QmluZGluZyxcbiAgaW5qZWN0LFxuICBJbnB1dCxcbiAgTmdab25lLFxuICBPbkluaXQsXG4gIE91dHB1dCxcbiAgVmlld0NoaWxkLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEFic3RyYWN0Q29udHJvbCwgQ29udHJvbFZhbHVlQWNjZXNzb3IsIE5HX1ZBTElEQVRPUlMsIE5HX1ZBTFVFX0FDQ0VTU09SLCBWYWxpZGF0aW9uRXJyb3JzLCBWYWxpZGF0b3IsIEZvcm1zTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuXG5pbXBvcnQgeyBGb2N1c01vbml0b3IgfSBmcm9tICdAYW5ndWxhci9jZGsvYTExeSc7XG5pbXBvcnQgeyBjb2VyY2VCb29sZWFuUHJvcGVydHkgfSBmcm9tICdAYW5ndWxhci9jZGsvY29lcmNpb24nO1xuaW1wb3J0IHtcbiAgTWF0QXV0b2NvbXBsZXRlLFxuICBNYXRBdXRvY29tcGxldGVTZWxlY3RlZEV2ZW50LFxuICBNYXRBdXRvY29tcGxldGVUcmlnZ2VyLFxuICBNYXRPcHRpb24sXG59IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2F1dG9jb21wbGV0ZSc7XG5cbmltcG9ydCB7IGd1aWQgfSBmcm9tICdAZmlyZXN0aXRjaC9jb21tb24nO1xuaW1wb3J0IHsgRnNNYXAgfSBmcm9tICdAZmlyZXN0aXRjaC9tYXAnO1xuXG5pbXBvcnQgeyBmcm9tLCBmcm9tRXZlbnQsIE9ic2VydmFibGUsIG9mIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQge1xuICBkZWJvdW5jZVRpbWUsXG4gIGRpc3RpbmN0VW50aWxDaGFuZ2VkLFxuICBmaWx0ZXIsXG4gIG1hcCxcbiAgc3dpdGNoTWFwLFxuICB0YXAsXG59IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHsgdGFrZVVudGlsRGVzdHJveWVkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZS9yeGpzLWludGVyb3AnO1xuXG5pbXBvcnQgeyBBZGRyZXNzRm9ybWF0IH0gZnJvbSAnLi4vLi4vZW51bXMvYWRkcmVzcy1mb3JtYXQuZW51bSc7XG5pbXBvcnQgeyBhZGRyZXNzSXNFbXB0eSB9IGZyb20gJy4uLy4uL2hlbHBlcnMvYWRkcmVzcy1pcy1lbXB0eSc7XG5pbXBvcnQgeyBjcmVhdGVFbXB0eUFkZHJlc3MgfSBmcm9tICcuLi8uLi9oZWxwZXJzL2NyZWF0ZS1lbXB0eS1hZGRyZXNzJztcbmltcG9ydCB7IGV4dHJhY3RVbml0IH0gZnJvbSAnLi4vLi4vaGVscGVycy9leHRyYWN0LXVuaXQnO1xuaW1wb3J0IHsgZ29vZ2xlUGxhY2VUb0ZzQWRkcmVzcyB9IGZyb20gJy4uLy4uL2hlbHBlcnMvZ29vZ2xlLXBsYWNlLXRvLWFkZHJlc3MnO1xuaW1wb3J0IHsgRnNBZGRyZXNzQ29uZmlnIH0gZnJvbSAnLi4vLi4vaW50ZXJmYWNlcy9hZGRyZXNzLWNvbmZpZy5pbnRlcmZhY2UnO1xuaW1wb3J0IHsgRnNBZGRyZXNzIH0gZnJvbSAnLi4vLi4vaW50ZXJmYWNlcy9hZGRyZXNzLmludGVyZmFjZSc7XG5pbXBvcnQgeyBNYXRGb3JtRmllbGQsIE1hdExhYmVsLCBNYXRIaW50IH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvZm9ybS1maWVsZCc7XG5pbXBvcnQgeyBNYXRJbnB1dCB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2lucHV0JztcbmltcG9ydCB7IEZzRm9ybU1vZHVsZSB9IGZyb20gJ0BmaXJlc3RpdGNoL2Zvcm0nO1xuaW1wb3J0IHsgRnNDbGVhck1vZHVsZSB9IGZyb20gJ0BmaXJlc3RpdGNoL2NsZWFyJztcbmltcG9ydCB7IE1hdE9wdGlvbiBhcyBNYXRPcHRpb25fMSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2NvcmUnO1xuXG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiAnZnMtYWRkcmVzcy1hdXRvY29tcGxldGUnLFxuICAgIHRlbXBsYXRlVXJsOiAnLi9hZGRyZXNzLWF1dG9jb21wbGV0ZS5jb21wb25lbnQuaHRtbCcsXG4gICAgc3R5bGVVcmxzOiBbJy4vYWRkcmVzcy1hdXRvY29tcGxldGUuY29tcG9uZW50LnNjc3MnXSxcbiAgICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgICBwcm92aWRlcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgICAgcHJvdmlkZTogTkdfVkFMVUVfQUNDRVNTT1IsXG4gICAgICAgICAgICB1c2VFeGlzdGluZzogZm9yd2FyZFJlZigoKSA9PiBGc0FkZHJlc3NBdXRvY29tcGxldGVDb21wb25lbnQpLFxuICAgICAgICAgICAgbXVsdGk6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICAgIHByb3ZpZGU6IE5HX1ZBTElEQVRPUlMsXG4gICAgICAgICAgICB1c2VFeGlzdGluZzogZm9yd2FyZFJlZigoKSA9PiBGc0FkZHJlc3NBdXRvY29tcGxldGVDb21wb25lbnQpLFxuICAgICAgICAgICAgbXVsdGk6IHRydWUsXG4gICAgICAgIH0sXG4gICAgXSxcbiAgICBzdGFuZGFsb25lOiB0cnVlLFxuICAgIGltcG9ydHM6IFtcbiAgICAgICAgTWF0Rm9ybUZpZWxkLFxuICAgICAgICBNYXRMYWJlbCxcbiAgICAgICAgTWF0SW5wdXQsXG4gICAgICAgIEZvcm1zTW9kdWxlLFxuICAgICAgICBNYXRBdXRvY29tcGxldGVUcmlnZ2VyLFxuICAgICAgICBGc0Zvcm1Nb2R1bGUsXG4gICAgICAgIEZzQ2xlYXJNb2R1bGUsXG4gICAgICAgIE1hdEF1dG9jb21wbGV0ZSxcbiAgICAgICAgTWF0T3B0aW9uXzEsXG4gICAgICAgIE1hdEhpbnQsXG4gICAgXSxcbn0pXG5leHBvcnQgY2xhc3MgRnNBZGRyZXNzQXV0b2NvbXBsZXRlQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBDb250cm9sVmFsdWVBY2Nlc3NvciwgVmFsaWRhdG9yIHtcbiAgcHJpdmF0ZSBfbWFwID0gaW5qZWN0KEZzTWFwKTtcbiAgcHJpdmF0ZSBfbmdab25lID0gaW5qZWN0KE5nWm9uZSk7XG4gIHByaXZhdGUgX2ZtID0gaW5qZWN0KEZvY3VzTW9uaXRvcik7XG4gIHByaXZhdGUgX2VsZW1lbnRSZWYgPSBpbmplY3QoRWxlbWVudFJlZik7XG4gIHByaXZhdGUgX2NkUmVmID0gaW5qZWN0KENoYW5nZURldGVjdG9yUmVmKTtcblxuXG4gIHB1YmxpYyBzdGF0aWMgbmV4dElkID0gMDtcblxuICBASW5wdXQoKVxuICBwdWJsaWMgZm9ybWF0ID0gQWRkcmVzc0Zvcm1hdC5Ud29MaW5lO1xuXG4gIEBJbnB1dCgpXG4gIHB1YmxpYyByZWFkb25seSA9IGZhbHNlO1xuXG4gIEBJbnB1dCgpXG4gIHB1YmxpYyBzaG93Q2xlYXIgPSB0cnVlO1xuXG4gIEBJbnB1dCgpXG4gIHB1YmxpYyBzdWdnZXN0aW9ucyA9IGZhbHNlO1xuXG4gIEBJbnB1dCgpXG4gIHB1YmxpYyBzZXQgY29uZmlnKHZhbHVlOiBGc0FkZHJlc3NDb25maWcpIHtcbiAgICB0aGlzLl9jb25maWcgPSB2YWx1ZTtcbiAgICBpZiAodGhpcy5fY29uZmlnKSB7XG4gICAgICB0aGlzLnJlcXVpcmVkID1cbiAgICAgICAgKCh0aGlzLmNvbmZpZy5uYW1lICYmIHRoaXMuY29uZmlnLm5hbWUucmVxdWlyZWQpIHx8XG4gICAgICAgICAgKHRoaXMuY29uZmlnLmNvdW50cnkgJiYgdGhpcy5jb25maWcuY291bnRyeS5yZXF1aXJlZCkgfHxcbiAgICAgICAgICAodGhpcy5jb25maWcucmVnaW9uICYmIHRoaXMuY29uZmlnLnJlZ2lvbi5yZXF1aXJlZCkgfHxcbiAgICAgICAgICAodGhpcy5jb25maWcuY2l0eSAmJiB0aGlzLmNvbmZpZy5jaXR5LnJlcXVpcmVkKSB8fFxuICAgICAgICAgICh0aGlzLmNvbmZpZy5zdHJlZXQgJiYgdGhpcy5jb25maWcuc3RyZWV0LnJlcXVpcmVkKSB8fFxuICAgICAgICAgICh0aGlzLmNvbmZpZy5hZGRyZXNzMiAmJiB0aGlzLmNvbmZpZy5hZGRyZXNzMi5yZXF1aXJlZCkgfHxcbiAgICAgICAgICAodGhpcy5jb25maWcuYWRkcmVzczMgJiYgdGhpcy5jb25maWcuYWRkcmVzczMucmVxdWlyZWQpIHx8XG4gICAgICAgICAgKHRoaXMuY29uZmlnLnppcCAmJiB0aGlzLmNvbmZpZy56aXAucmVxdWlyZWQpKTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgZ2V0IGNvbmZpZygpOiBGc0FkZHJlc3NDb25maWcge1xuICAgIHJldHVybiB0aGlzLl9jb25maWc7XG4gIH1cblxuICBAT3V0cHV0KClcbiAgcHVibGljIHJlYWRvbmx5IGFkZHJlc3NDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgQE91dHB1dCgpXG4gIHB1YmxpYyByZWFkb25seSBhZGRyZXNzTWFudWFsID0gbmV3IEV2ZW50RW1pdHRlcjxzdHJpbmc+KCk7XG5cbiAgQFZpZXdDaGlsZCgnc2VhcmNoSW5wdXQnLCB7IHN0YXRpYzogdHJ1ZSwgcmVhZDogRWxlbWVudFJlZiB9KVxuICBwdWJsaWMgcmVhZG9ubHkgc2VhcmNoRWxlbWVudDogRWxlbWVudFJlZjtcblxuICBAVmlld0NoaWxkKE1hdEF1dG9jb21wbGV0ZSwgeyBzdGF0aWM6IHRydWUgfSlcbiAgcHVibGljIHJlYWRvbmx5IGF1dG9Db21wbGV0ZVJlZjogTWF0QXV0b2NvbXBsZXRlO1xuXG4gIEBWaWV3Q2hpbGQoTWF0QXV0b2NvbXBsZXRlVHJpZ2dlciwgeyBzdGF0aWM6IHRydWUgfSlcbiAgcHVibGljIHJlYWRvbmx5IGF1dG9jb21wbGV0ZVRyaWdnZXI6IE1hdEF1dG9jb21wbGV0ZVRyaWdnZXI7XG5cbiAgQEhvc3RCaW5kaW5nKClcbiAgcHVibGljIGlkID0gYGZzLWFkZHJlc3MtYXV0b2NvbXBsZXRlLSR7RnNBZGRyZXNzQXV0b2NvbXBsZXRlQ29tcG9uZW50Lm5leHRJZCsrfWA7XG5cbiAgcHVibGljIGlucHV0QWRkcmVzczogRnNBZGRyZXNzID0gdGhpcy5fZGVmYXVsdElucHV0QWRkcmVzcygpO1xuICBwdWJsaWMgZ29vZ2xlU3VnZ2VzdGlvbnM6IGdvb2dsZS5tYXBzLnBsYWNlcy5BdXRvY29tcGxldGVTdWdnZXN0aW9uW10gPSBbXTtcbiAgcHVibGljIGdvb2dsZVBsYWNlOiBnb29nbGUubWFwcy5wbGFjZXMuUGxhY2UgPSBudWxsO1xuICBwdWJsaWMgb25DaGFuZ2U6IChkYXRhOiBhbnkpID0+IHZvaWQ7XG4gIHB1YmxpYyBvblRvdWNoZWQ6ICgpID0+IHZvaWQ7XG4gIHB1YmxpYyBmb2N1c2VkID0gZmFsc2U7XG4gIHB1YmxpYyByZWFkb25seSBhdXRvY29tcGxldGVOYW1lID0gYHNlYXJjaC0ke2d1aWQoJ3h4eHh4eHh4Jyl9YDtcblxuICBwcml2YXRlIF9jb25maWc6IEZzQWRkcmVzc0NvbmZpZyA9IHt9O1xuICBwcml2YXRlIF9hZGRyZXNzOiBGc0FkZHJlc3MgPSB7fTtcbiAgcHJpdmF0ZSBfc2VhcmNoVGV4dCA9ICcnO1xuICBwcml2YXRlIF9kaXNhYmxlZCA9IGZhbHNlO1xuICBwcml2YXRlIF9yZXF1aXJlZCA9IGZhbHNlO1xuICBwcml2YXRlIF9wbGFjZWhvbGRlcjogc3RyaW5nO1xuXG4gIHByaXZhdGUgX2Rlc3Ryb3lSZWYgPSBpbmplY3QoRGVzdHJveVJlZik7XG5cbiAgcHVibGljIHNldCB2YWx1ZSh2YWx1ZTogRnNBZGRyZXNzKSB7XG4gICAgdGhpcy5fYWRkcmVzcyA9IHZhbHVlO1xuICAgIHRoaXMub25DaGFuZ2UodGhpcy5fYWRkcmVzcyk7XG4gIH1cblxuICBwdWJsaWMgZ2V0IHZhbHVlKCk6IEZzQWRkcmVzcyB7XG4gICAgcmV0dXJuIHRoaXMuX2FkZHJlc3M7XG4gIH1cblxuICBASW5wdXQoKVxuICBwdWJsaWMgZ2V0IGRpc2FibGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLl9kaXNhYmxlZDtcbiAgfVxuXG4gIHB1YmxpYyBzZXQgZGlzYWJsZWQodmFsdWU6IGJvb2xlYW4pIHtcbiAgICB0aGlzLl9kaXNhYmxlZCA9IGNvZXJjZUJvb2xlYW5Qcm9wZXJ0eSh2YWx1ZSk7XG4gIH1cblxuICBASW5wdXQoKVxuICBwdWJsaWMgZ2V0IHJlcXVpcmVkKCkge1xuICAgIHJldHVybiB0aGlzLl9yZXF1aXJlZDtcbiAgfVxuXG4gIHB1YmxpYyBzZXQgcmVxdWlyZWQocmVxKSB7XG4gICAgdGhpcy5fcmVxdWlyZWQgPSBjb2VyY2VCb29sZWFuUHJvcGVydHkocmVxKTtcbiAgfVxuXG4gIEBJbnB1dCgpXG4gIHB1YmxpYyBnZXQgcGxhY2Vob2xkZXIoKSB7XG4gICAgcmV0dXJuIHRoaXMuX3BsYWNlaG9sZGVyO1xuICB9XG4gIHB1YmxpYyBzZXQgcGxhY2Vob2xkZXIocGxoKSB7XG4gICAgdGhpcy5fcGxhY2Vob2xkZXIgPSBwbGg7XG4gIH1cblxuICBASG9zdEJpbmRpbmcoJ2NsYXNzLmZsb2F0aW5nJylcbiAgcHVibGljIGdldCBzaG91bGRMYWJlbEZsb2F0KCkge1xuICAgIHJldHVybiB0aGlzLmZvY3VzZWQ7XG4gIH1cblxuICBwdWJsaWMgZ2V0IGVtcHR5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBhZGRyZXNzSXNFbXB0eSh0aGlzLnZhbHVlKTtcbiAgfVxuXG4gIHB1YmxpYyBuZ09uSW5pdCgpIHtcbiAgICB0aGlzLl9pbml0R29vZ2xlTWFwKCk7XG4gICAgdGhpcy5fbGlzdGVuVXNlclR5cGluZygpO1xuICAgIHRoaXMuX2xpc3RlbkF1dG9jb21wbGV0ZVNlbGVjdGlvbigpO1xuICAgIHRoaXMuX3JlZ2lzdGVyRm9jdXNNb25pdG9yKCk7XG4gIH1cblxuICBwdWJsaWMgd3JpdGVWYWx1ZSh2YWx1ZTogRnNBZGRyZXNzIHwgbnVsbCkge1xuICAgIHRoaXMuX2FkZHJlc3MgPSB2YWx1ZTtcbiAgICB0aGlzLmlucHV0QWRkcmVzcyA9IHZhbHVlO1xuICAgIHRoaXMuX2NkUmVmLm1hcmtGb3JDaGVjaygpO1xuICB9XG5cbiAgcHVibGljIG9uQ29udGFpbmVyQ2xpY2soZXZlbnQ6IE1vdXNlRXZlbnQpIHtcbiAgICBpZiAoKGV2ZW50LnRhcmdldCBhcyBFbGVtZW50KS50YWdOYW1lLnRvTG93ZXJDYXNlKCkgIT09ICdpbnB1dCcpIHtcbiAgICAgIHRoaXMuc2VhcmNoRWxlbWVudC5uYXRpdmVFbGVtZW50LmZvY3VzKCk7XG4gICAgICB0aGlzLl9lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3RvcignaW5wdXQnKS5mb2N1cygpO1xuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyByZWdpc3Rlck9uQ2hhbmdlKGZuOiAoZGF0YTogYW55KSA9PiB2b2lkKTogdm9pZCB7XG4gICAgdGhpcy5vbkNoYW5nZSA9IGZuO1xuICB9XG5cbiAgcHVibGljIHJlZ2lzdGVyT25Ub3VjaGVkKGZuOiAoKSA9PiB2b2lkKTogdm9pZCB7XG4gICAgdGhpcy5vblRvdWNoZWQgPSBmbjtcbiAgfVxuXG4gIHB1YmxpYyBkaXNwbGF5V2l0aCA9ICh2YWx1ZTogRnNBZGRyZXNzKSA9PiB7XG4gICAgaWYgKHZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcpIHtcbiAgICAgIHJldHVybiB0aGlzLnZhbHVlPy5zdHJlZXQ7XG4gICAgfSBlbHNlIGlmICghdGhpcy5lbXB0eSkge1xuICAgICAgcmV0dXJuICcnO1xuICAgIH1cbiAgfTtcblxuICBwdWJsaWMgdmFsaWRhdGUoY29udHJvbDogQWJzdHJhY3RDb250cm9sKTogVmFsaWRhdGlvbkVycm9ycyB8IG51bGwge1xuICAgIGNvbnN0IHZhbGlkYXRpb25FcnJvcnM6IFZhbGlkYXRpb25FcnJvcnMgPSB7fTtcbiAgICBjb25zdCByZXF1aXJlZEZpZWxkID0gW107XG4gICAgY29uc3QgcGFydHMgPSBbJ25hbWUnLCAnc3RyZWV0JywgJ2NpdHknLCAncmVnaW9uJywgJ3ppcCcsICdjb3VudHJ5JywgJ2xhdCcsICdsbmcnXTtcblxuICAgIGlmICh0aGlzLnJlcXVpcmVkICYmIHRoaXMuZW1wdHkpIHtcbiAgICAgIHZhbGlkYXRpb25FcnJvcnMucmVxdWlyZWQgPSB0cnVlO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5lbXB0eSkge1xuICAgICAgcGFydHMuZm9yRWFjaCgocGFydCkgPT4ge1xuICAgICAgICBpZiAodGhpcy5jb25maWdbcGFydF0gJiYgdGhpcy5jb25maWdbcGFydF0ucmVxdWlyZWQgJiYgIXRoaXMudmFsdWVbcGFydF0pIHtcbiAgICAgICAgICByZXF1aXJlZEZpZWxkLnB1c2goW3BhcnRdKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICAgIGlmICgoKHRoaXMuY29uZmlnLmxhdCAmJiB0aGlzLmNvbmZpZy5sYXQucmVxdWlyZWQpIHx8XG4gICAgICAgICh0aGlzLmNvbmZpZy5sbmcgJiYgdGhpcy5jb25maWcubG5nLnJlcXVpcmVkKSkgJiZcbiAgICAgICAgKCF0aGlzLnZhbHVlLmxhdCB8fCAhdGhpcy52YWx1ZS5sYXQpKSB7XG4gICAgICAgIHZhbGlkYXRpb25FcnJvcnMuaW52YWxpZCA9ICdwb3NpdGlvbiBvbiBtYXAnO1xuICAgICAgfVxuXG4gICAgICBpZiAocmVxdWlyZWRGaWVsZC5sZW5ndGgpIHtcbiAgICAgICAgaWYgKHJlcXVpcmVkRmllbGQubGVuZ3RoID09PSAxKSB7XG4gICAgICAgICAgdmFsaWRhdGlvbkVycm9ycy5pbnZhbGlkID0gYFRoZSAke3JlcXVpcmVkRmllbGRbMF19IGlzIHJlcXVpcmVkYDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCBsYXN0ID0gcmVxdWlyZWRGaWVsZC5wb3AoKTtcbiAgICAgICAgICB2YWxpZGF0aW9uRXJyb3JzLmludmFsaWQgPSBgVGhlICR7cmVxdWlyZWRGaWVsZC5qb2luKCcsICcpfSBhbmQgJHtsYXN0fSBhcmUgcmVxdWlyZWRgO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHZhbGlkYXRpb25FcnJvcnM7XG4gIH1cblxuICBwdWJsaWMgY2xlYXIoKTogdm9pZCB7XG4gICAgdGhpcy5pbnB1dEFkZHJlc3MgPSB0aGlzLl9kZWZhdWx0SW5wdXRBZGRyZXNzKCk7XG4gICAgdGhpcy52YWx1ZSA9IGNyZWF0ZUVtcHR5QWRkcmVzcygpO1xuICAgIHRoaXMuYWRkcmVzc0NoYW5nZS5lbWl0KG51bGwpO1xuICAgIHRoaXMuX2NsZWFyUHJlZGljdGlvbnMoKTtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHRoaXMuYXV0b2NvbXBsZXRlVHJpZ2dlci5vcGVuUGFuZWwoKTtcbiAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBtYW51YWwodmFsdWU6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuYWRkcmVzc01hbnVhbC5lbWl0KHZhbHVlKTtcbiAgfVxuXG4gIC8vIFNlYXJjaCBpbnB1dCBjYW4ndCBiZSBudWxsLiBXZSBpbXBsZW1lbnRlZCByZXF1aXJlZCB2YWxpZGF0aW9uIHRvIHNob3cgYXN0ZXJpc2sgaWYgbmVlZGVkXG4gIC8vIEJ1dCBnZW5lcmFsIHZhbGlkYXRpb24gcGxhY2VkIGluIGFub3RoZXIgbGV2ZWwgYW5kIG5vdCBkZXBlbmRzIG9mIHRoaXMgaW5wdXRcbiAgLy8gVGhpcyBoYWNrIGFsbG93IHVzIHRvIHNob3cgYXN0ZXJpc2sgYnV0IGRpc2FibGUgZXh0cmEgdmFsaWRhdGlvblxuICBwcml2YXRlIF9kZWZhdWx0SW5wdXRBZGRyZXNzKCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgcHJpdmF0ZSBfbGlzdGVuVXNlclR5cGluZygpOiB2b2lkIHtcbiAgICB0aGlzLl9uZ1pvbmUucnVuT3V0c2l