angular-phone-number-input
Version:
``` npm i angular-phone-number-input ``` Have implemented country-specific validations using [libphonenumber-js](https://www.npmjs.com/package/libphonenumber-js)
281 lines • 46.8 kB
JavaScript
import { Component, Input, forwardRef, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validators, } from '@angular/forms';
import { DomSanitizer } from '@angular/platform-browser';
import { Countries } from '../assets/country-dataset';
import { globeSVG } from '../assets/globe-svg';
import { isValidPhoneNumber } from 'libphonenumber-js';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common";
import * as i2 from "@angular/forms";
export class AngularPhoneNumberInput {
constructor() {
this.error = false;
this.border = true;
this.sanitizer = inject(DomSanitizer);
this.countryList = Countries;
this.dataSet = Countries;
this.showModal = false;
this.inputFocused = false;
this.disabled = false;
this.globeSVG = globeSVG;
/** Callback function triggered on value change. */
this.onChange = () => { };
/** Callback function triggered on input touch. */
this.onTouched = () => { };
/** Handles the input blur event. */
this.onBlur = () => {
this.inputFocused = false;
};
/**
* Handles the input focus event.
*/
this.onFocus = () => {
this.inputFocused = true;
};
/**
* Writes a new value to the input.
* @param obj - The new value.
*/
this.writeValue = (value) => {
if (!!value) {
this.extractValues(value);
}
};
/**
* Registers a callback function to be executed when the input value changes.
* @param fn - The callback function.
*/
this.registerOnChange = (fn) => {
this.onChange = fn;
};
/**
* Registers a callback function to be executed when the input is touched.
* @param fn - The callback function.
*/
this.registerOnTouched = (fn) => {
this.onTouched = fn;
};
/**
* Sets the disabled state of the input.
* @param isDisabled - The disabled state.
*/
this.setDisabledState = (isDisabled) => {
this.disabled = isDisabled;
};
/**
* Sets the default country based on the provided defaultCountry value.
* If the default country exists in the countryList, sets the selectedCountry accordingly.
*/
this.setDefaultCountry = () => {
const defCountryData = this.countryList[this.defaultCountry];
this.selectedCountry = defCountryData ?? null;
};
/**
* Sets preferred countries based on the provided preferredCountries array.
* Filters the countryList to contain only the preferred countries if they exist.
*/
this.setPreferredCountries = () => {
if (this.preferredCountries && this.preferredCountries.length > 0) {
// Filter the countries to include only preferred countries
const preferredCountryList = Object.fromEntries(Object.entries(Countries).filter(([code]) => this.preferredCountries.includes(code)));
// If preferred countries are found, update the countryList
if (Object.keys(preferredCountryList).length > 0) {
this.countryList = preferredCountryList;
}
}
};
/**
* Sets the full phone number by concatenating the country dial code and the input value.
* Updates the value using the onChange callback.
* @returns {void}
*/
this.setFullPhoneNumber = () => {
const fullPhoneNumber = this.selectedCountry ? this.selectedCountry.dialCode + (this.value || '') : this.value || '';
// Trigger the onChange callback with the updated full phone number
this.onChange(fullPhoneNumber);
};
/**
* Extracts the country code and phone number from the provided phone number string.
* @param {string} phoneNumber - The input phone number string.
* @returns {void}
*/
this.extractValues = (phoneNumber) => {
let country; // Variable to store the extracted country data
const countryCodes = Object.keys(Countries);
// Sort the country codes by length in descending order
const sortedCodes = countryCodes.sort((a, b) => Countries[b].dialCode.length - Countries[a].dialCode.length);
// Iterate through sorted country codes to find a match in the input value
for (const code of sortedCodes) {
if (phoneNumber.startsWith(Countries[code].dialCode)) {
country = Countries[code];
this.value = phoneNumber.substring(country.dialCode.length);
this.selectedCountry = country;
return;
}
}
// If no matching country code is found, attempt to extract country code based on length (1 to 4 digits)
for (let i = 1; i <= 4; i++) {
const potentialCode = phoneNumber.substring(0, i);
const matchingCode = countryCodes.find(codeKey => Countries[codeKey].dialCode === `+${potentialCode}`);
if (matchingCode) {
country = Countries[matchingCode];
this.value = phoneNumber.substring(i);
this.selectedCountry = country;
return;
}
}
// If no country code is identified, consider the entire input as the phone number
this.value = phoneNumber.replace('+', '');
return;
};
/**
* Retrieves the sanitized HTML representation of an icon string.
* @param {string | undefined} iconString - The string representation of an SVG icon.
* @returns {SafeHtml | undefined} The sanitized HTML representation of the icon string.
*/
this.getIcon = (iconString) => {
if (iconString) {
return this.sanitizer.bypassSecurityTrustHtml(iconString);
}
return undefined;
};
/**
* Validates the input control.
* @param control - The AbstractControl instance.
* @returns ValidationErrors if value is null.
*/
this.validate = (control) => {
const fullPhoneNumber = this.selectedCountry ? this.selectedCountry.dialCode + (this.value || '') : '+' + this.value || '';
// if no value update and inputted control has validation with required or return true
if (!this.value || this.value === '') {
if (control.hasValidator(Validators.required))
return { required: true };
return null;
}
// check valid phone number or not with libphonenumber-js
if (!isValidPhoneNumber(fullPhoneNumber.toString())) {
return { status: "INVALID" };
}
return null;
};
}
/**
* Initializes default country and preferred countries on component initialization.
* Calls setDefaultCountry and setPreferredCountries methods.
*/
ngOnInit() {
this.setDefaultCountry();
this.setPreferredCountries();
}
/**
* Toggles the modal visibility and sets the dataSet to the current countryList.
*/
toggleModal() {
this.dataSet = this.countryList;
this.showModal = !this.showModal;
}
/**
* Selects a country and emits the corresponding event.
* @param {Country} data - The selected country data.
*/
selectCountry(data) {
this.selectedCountry = data.value;
this.setFullPhoneNumber();
this.toggleModal();
}
/**
* Tries to determine the selected country based on the provided input value.
* Updates the selected country and value if a matching country code is found.
* @returns {void}
*/
getCountryWithInput() {
if (this.value) {
// temp number to hold the input value
const tempNum = '+' + this.value.toString();
if (!this.selectedCountry && tempNum.length === 5) {
const countryCodes = Object.keys(Countries);
const sortedCodes = countryCodes.sort((a, b) => Countries[b].dialCode.length - Countries[a].dialCode.length);
for (const code of sortedCodes) {
if (tempNum.startsWith(Countries[code].dialCode)) {
this.selectedCountry = Countries[code];
// remove the country code from input value
this.value = this.value.toString().substring(this.selectedCountry.dialCode.length - 1);
break;
}
}
}
}
}
/**
* Emits the current input value.
*/
onInputChanged() {
this.setFullPhoneNumber();
this.getCountryWithInput();
}
/**
* Filters the countries based on the provided search term and updates the dataset.
* @param {string} searchTerm - The term to search for within country names or dial codes.
* @returns {void}
*/
filterCountries(searchTerm) {
// Convert the searchTerm to lowercase for case-insensitive matching
searchTerm = searchTerm.toLowerCase();
// Filter the countryList based on the searchTerm and update dataSet
this.dataSet = Object.keys(this.countryList)
.filter(code => this.countryList[code].country.toLowerCase().includes(searchTerm) ||
this.countryList[code].dialCode.includes(searchTerm))
.reduce((obj, code) => {
// Reduce the filtered keys back into an object
obj[code] = this.countryList[code];
return obj;
}, {});
}
/**
* Handles changes in the search input by invoking the filterCountries method with the input value.
* @param {Event} event - The event object from the search input.
* @returns {void}
*/
onSearchChange(event) {
const searchTerm = event.target.value;
this.filterCountries(searchTerm);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AngularPhoneNumberInput, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: AngularPhoneNumberInput, isStandalone: true, selector: "angular-phone-number-input", inputs: { error: "error", defaultCountry: "defaultCountry", preferredCountries: "preferredCountries", border: "border" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AngularPhoneNumberInput),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => AngularPhoneNumberInput),
multi: true,
},
], ngImport: i0, template: "<div class=\"wrap flex align-center\" [class.border]=\"border\" [class.active]=\"inputFocused\" [class.error]=\"error\">\n <div class=\"flex\">\n @if(selectedCountry){\n <div class=\"flex align-center\" (click)=\"toggleModal()\">\n <span class=\"flag\" [innerHTML]=\"getIcon(selectedCountry.flag)\"></span>\n\n <svg class=\"ml-5\" xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"6\" viewBox=\"0 0 10 6\" fill=\"none\">\n <path d=\"M4.99959 5.13698L0.725586 0.862976H9.27359L4.99959 5.13698Z\" fill=\"black\" fill-opacity=\"0.6\" />\n </svg>\n </div>\n <span class=\"text ml-5\">{{selectedCountry.dialCode}}</span>\n }@else{\n <div class=\"flex align-center\" (click)=\"toggleModal()\">\n <span class=\"flag\" [innerHTML]=\"getIcon(globeSVG)\"></span>\n <svg class=\"ml-5\" xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"6\" viewBox=\"0 0 10 6\" fill=\"none\">\n <path d=\"M4.99959 5.13698L0.725586 0.862976H9.27359L4.99959 5.13698Z\" fill=\"black\" fill-opacity=\"0.6\" />\n </svg>\n </div>\n }\n\n </div>\n\n <input class=\"input-box text\" type=\"number\" placeholder=\"\" [(ngModel)]=\"value\" [disabled]=\"disabled\"\n (input)=\"onChange(value); onInputChanged()\" (blur)=\"onBlur(); onTouched()\" (focus)=\"onFocus()\" />\n\n @if(showModal){\n <div class=\"modal-wrap\">\n <div>\n <input class=\"search-box\" (input)=\"onSearchChange($event)\" placeholder=\"Search\" />\n </div>\n <div class=\"country-list\">\n <div style=\"display: flex;\" *ngFor=\"let data of dataSet | keyvalue\">\n <div class=\"modal-country-wrap flex\" (click)=\"selectCountry(data)\">\n <span class=\"flag\" [innerHTML]=\"getIcon(data.value.flag)\"></span>\n <span class=\"modal-country ml-5\">{{data.value.dialCode}}</span>\n <span class=\"modal-country ml-5\">{{data.value.country}}</span>\n </div>\n </div>\n </div>\n </div>\n }\n</div>", styles: ["input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number]{-moz-appearance:textfield}.wrap{position:relative;border-bottom:1px solid rgba(0,0,0,.1);border-radius:4px;height:52px;padding:0 12px;background-color:#fff}.wrap .modal-wrap{position:absolute;background-color:#fff;top:55px;left:0;border:1px solid rgba(0,0,0,.1);border-radius:4px;padding:6px}.wrap .modal-wrap .country-list{margin-top:10px;overflow:-moz-scrollbars-none;overflow-y:scroll;width:280px;max-height:250px}.wrap .modal-wrap .country-list::-webkit-scrollbar{display:none}.wrap .modal-wrap .modal-country-wrap{width:100%;padding:5px 0}.wrap .modal-wrap .modal-country-wrap:hover{background-color:#0000001a}.wrap .modal-wrap .search-box{border:none;outline:none;width:100%;border-bottom:1px solid rgba(0,0,0,.1);font-size:13px}.wrap .input-box{border:none;outline:none;width:100%;padding:0 8px}.wrap .text{font-size:16px;font-weight:400;line-height:24px;letter-spacing:-.011px}.wrap .flag{width:20px;height:auto}.active{border-bottom:1px solid #0043CE!important}.error{border-bottom:1px solid #DD3130!important}.border{border:1px solid rgba(0,0,0,.1)}.flex{display:flex}.align-center{align-items:center}.ml-5{margin-left:5px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AngularPhoneNumberInput, decorators: [{
type: Component,
args: [{ selector: 'angular-phone-number-input', standalone: true, imports: [CommonModule, FormsModule], providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AngularPhoneNumberInput),
multi: true,
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => AngularPhoneNumberInput),
multi: true,
},
], template: "<div class=\"wrap flex align-center\" [class.border]=\"border\" [class.active]=\"inputFocused\" [class.error]=\"error\">\n <div class=\"flex\">\n @if(selectedCountry){\n <div class=\"flex align-center\" (click)=\"toggleModal()\">\n <span class=\"flag\" [innerHTML]=\"getIcon(selectedCountry.flag)\"></span>\n\n <svg class=\"ml-5\" xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"6\" viewBox=\"0 0 10 6\" fill=\"none\">\n <path d=\"M4.99959 5.13698L0.725586 0.862976H9.27359L4.99959 5.13698Z\" fill=\"black\" fill-opacity=\"0.6\" />\n </svg>\n </div>\n <span class=\"text ml-5\">{{selectedCountry.dialCode}}</span>\n }@else{\n <div class=\"flex align-center\" (click)=\"toggleModal()\">\n <span class=\"flag\" [innerHTML]=\"getIcon(globeSVG)\"></span>\n <svg class=\"ml-5\" xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"6\" viewBox=\"0 0 10 6\" fill=\"none\">\n <path d=\"M4.99959 5.13698L0.725586 0.862976H9.27359L4.99959 5.13698Z\" fill=\"black\" fill-opacity=\"0.6\" />\n </svg>\n </div>\n }\n\n </div>\n\n <input class=\"input-box text\" type=\"number\" placeholder=\"\" [(ngModel)]=\"value\" [disabled]=\"disabled\"\n (input)=\"onChange(value); onInputChanged()\" (blur)=\"onBlur(); onTouched()\" (focus)=\"onFocus()\" />\n\n @if(showModal){\n <div class=\"modal-wrap\">\n <div>\n <input class=\"search-box\" (input)=\"onSearchChange($event)\" placeholder=\"Search\" />\n </div>\n <div class=\"country-list\">\n <div style=\"display: flex;\" *ngFor=\"let data of dataSet | keyvalue\">\n <div class=\"modal-country-wrap flex\" (click)=\"selectCountry(data)\">\n <span class=\"flag\" [innerHTML]=\"getIcon(data.value.flag)\"></span>\n <span class=\"modal-country ml-5\">{{data.value.dialCode}}</span>\n <span class=\"modal-country ml-5\">{{data.value.country}}</span>\n </div>\n </div>\n </div>\n </div>\n }\n</div>", styles: ["input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}input[type=number]{-moz-appearance:textfield}.wrap{position:relative;border-bottom:1px solid rgba(0,0,0,.1);border-radius:4px;height:52px;padding:0 12px;background-color:#fff}.wrap .modal-wrap{position:absolute;background-color:#fff;top:55px;left:0;border:1px solid rgba(0,0,0,.1);border-radius:4px;padding:6px}.wrap .modal-wrap .country-list{margin-top:10px;overflow:-moz-scrollbars-none;overflow-y:scroll;width:280px;max-height:250px}.wrap .modal-wrap .country-list::-webkit-scrollbar{display:none}.wrap .modal-wrap .modal-country-wrap{width:100%;padding:5px 0}.wrap .modal-wrap .modal-country-wrap:hover{background-color:#0000001a}.wrap .modal-wrap .search-box{border:none;outline:none;width:100%;border-bottom:1px solid rgba(0,0,0,.1);font-size:13px}.wrap .input-box{border:none;outline:none;width:100%;padding:0 8px}.wrap .text{font-size:16px;font-weight:400;line-height:24px;letter-spacing:-.011px}.wrap .flag{width:20px;height:auto}.active{border-bottom:1px solid #0043CE!important}.error{border-bottom:1px solid #DD3130!important}.border{border:1px solid rgba(0,0,0,.1)}.flex{display:flex}.align-center{align-items:center}.ml-5{margin-left:5px}\n"] }]
}], propDecorators: { error: [{
type: Input
}], defaultCountry: [{
type: Input
}], preferredCountries: [{
type: Input
}], border: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW5ndWxhci1waG9uZS1udW1iZXItaW5wdXQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvYW5ndWxhci1waG9uZS1udW1iZXItaW5wdXQvc3JjL2xpYi9hbmd1bGFyLXBob25lLW51bWJlci1pbnB1dC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi9wcm9qZWN0cy9hbmd1bGFyLXBob25lLW51bWJlci1pbnB1dC9zcmMvbGliL2FuZ3VsYXItcGhvbmUtbnVtYmVyLWlucHV0LmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDckUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFHTCxXQUFXLEVBQ1gsYUFBYSxFQUNiLGlCQUFpQixFQUdqQixVQUFVLEdBQ1gsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQUUsWUFBWSxFQUFZLE1BQU0sMkJBQTJCLENBQUM7QUFDbkUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRXRELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUUvQyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQzs7OztBQXFCdkQsTUFBTSxPQUFPLHVCQUF1QjtJQW5CcEM7UUFvQlcsVUFBSyxHQUF3QixLQUFLLENBQUM7UUFHbkMsV0FBTSxHQUFZLElBQUksQ0FBQztRQUVoQyxjQUFTLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ2pDLGdCQUFXLEdBQWdCLFNBQVMsQ0FBQztRQUNyQyxZQUFPLEdBQWdCLFNBQVMsQ0FBQztRQUNqQyxjQUFTLEdBQVksS0FBSyxDQUFDO1FBRTNCLGlCQUFZLEdBQVks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bmVudCwgSW5wdXQsIGZvcndhcmRSZWYsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7XG4gIEFic3RyYWN0Q29udHJvbCxcbiAgQ29udHJvbFZhbHVlQWNjZXNzb3IsXG4gIEZvcm1zTW9kdWxlLFxuICBOR19WQUxJREFUT1JTLFxuICBOR19WQUxVRV9BQ0NFU1NPUixcbiAgVmFsaWRhdGlvbkVycm9ycyxcbiAgVmFsaWRhdG9yLFxuICBWYWxpZGF0b3JzLFxufSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgeyBEb21TYW5pdGl6ZXIsIFNhZmVIdG1sIH0gZnJvbSAnQGFuZ3VsYXIvcGxhdGZvcm0tYnJvd3Nlcic7XG5pbXBvcnQgeyBDb3VudHJpZXMgfSBmcm9tICcuLi9hc3NldHMvY291bnRyeS1kYXRhc2V0JztcbmltcG9ydCB7IENvdW50cnlEYXRhIH0gZnJvbSAnLi4vZGF0YS10eXBlcy9jb3VudHJ5TGlzdCc7XG5pbXBvcnQgeyBnbG9iZVNWRyB9IGZyb20gJy4uL2Fzc2V0cy9nbG9iZS1zdmcnO1xuaW1wb3J0IHsgQ291bnRyeSB9IGZyb20gJy4uL2RhdGEtdHlwZXMvY291bnRyeSc7XG5pbXBvcnQgeyBpc1ZhbGlkUGhvbmVOdW1iZXIgfSBmcm9tICdsaWJwaG9uZW51bWJlci1qcyc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2FuZ3VsYXItcGhvbmUtbnVtYmVyLWlucHV0JyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZSwgRm9ybXNNb2R1bGVdLFxuICB0ZW1wbGF0ZVVybDogJy4vYW5ndWxhci1waG9uZS1udW1iZXItaW5wdXQuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybDogJy4vYW5ndWxhci1waG9uZS1udW1iZXItaW5wdXQuY29tcG9uZW50LnNjc3MnLFxuICBwcm92aWRlcnM6IFtcbiAgICB7XG4gICAgICBwcm92aWRlOiBOR19WQUxVRV9BQ0NFU1NPUixcbiAgICAgIHVzZUV4aXN0aW5nOiBmb3J3YXJkUmVmKCgpID0+IEFuZ3VsYXJQaG9uZU51bWJlcklucHV0KSxcbiAgICAgIG11bHRpOiB0cnVlLFxuICAgIH0sXG4gICAge1xuICAgICAgcHJvdmlkZTogTkdfVkFMSURBVE9SUyxcbiAgICAgIHVzZUV4aXN0aW5nOiBmb3J3YXJkUmVmKCgpID0+IEFuZ3VsYXJQaG9uZU51bWJlcklucHV0KSxcbiAgICAgIG11bHRpOiB0cnVlLFxuICAgIH0sXG4gIF0sXG59KVxuZXhwb3J0IGNsYXNzIEFuZ3VsYXJQaG9uZU51bWJlcklucHV0IGltcGxlbWVudHMgQ29udHJvbFZhbHVlQWNjZXNzb3IsIFZhbGlkYXRvciB7XG4gIEBJbnB1dCgpIGVycm9yOiBib29sZWFuIHwgdW5kZWZpbmVkID0gZmFsc2U7XG4gIEBJbnB1dCgpIGRlZmF1bHRDb3VudHJ5ITogc3RyaW5nO1xuICBASW5wdXQoKSBwcmVmZXJyZWRDb3VudHJpZXMhOiBzdHJpbmdbXTtcbiAgQElucHV0KCkgYm9yZGVyOiBib29sZWFuID0gdHJ1ZTtcblxuICBzYW5pdGl6ZXIgPSBpbmplY3QoRG9tU2FuaXRpemVyKTtcbiAgY291bnRyeUxpc3Q6IENvdW50cnlEYXRhID0gQ291bnRyaWVzO1xuICBkYXRhU2V0OiBDb3VudHJ5RGF0YSA9IENvdW50cmllcztcbiAgc2hvd01vZGFsOiBib29sZWFuID0gZmFsc2U7XG4gIHNlbGVjdGVkQ291bnRyeSE6IENvdW50cnk7XG4gIGlucHV0Rm9jdXNlZDogYm9vbGVhbiA9IGZhbHNlO1xuICBkaXNhYmxlZDogYm9vbGVhbiA9IGZhbHNlO1xuICB2YWx1ZSE6IHN0cmluZztcbiAgZ2xvYmVTVkc6IHN0cmluZyA9IGdsb2JlU1ZHO1xuXG4gIC8qKiBDYWxsYmFjayBmdW5jdGlvbiB0cmlnZ2VyZWQgb24gdmFsdWUgY2hhbmdlLiAqL1xuICBvbkNoYW5nZTogKHZhbHVlOiBzdHJpbmcpID0+IHZvaWQgPSAoKSA9PiB7IH07XG5cbiAgLyoqIENhbGxiYWNrIGZ1bmN0aW9uIHRyaWdnZXJlZCBvbiBpbnB1dCB0b3VjaC4gKi9cbiAgb25Ub3VjaGVkOiAoKSA9PiB2b2lkID0gKCkgPT4geyB9O1xuXG4gIC8qKiBIYW5kbGVzIHRoZSBpbnB1dCBibHVyIGV2ZW50LiAqL1xuICBvbkJsdXIgPSAoKSA9PiB7XG4gICAgdGhpcy5pbnB1dEZvY3VzZWQgPSBmYWxzZTtcbiAgfTtcblxuICAvKipcbiAgICogSGFuZGxlcyB0aGUgaW5wdXQgZm9jdXMgZXZlbnQuXG4gICAqL1xuICBvbkZvY3VzID0gKCk6IHZvaWQgPT4ge1xuICAgIHRoaXMuaW5wdXRGb2N1c2VkID0gdHJ1ZTtcbiAgfTtcblxuICAvKipcbiAgICogV3JpdGVzIGEgbmV3IHZhbHVlIHRvIHRoZSBpbnB1dC5cbiAgICogQHBhcmFtIG9iaiAtIFRoZSBuZXcgdmFsdWUuXG4gICAqL1xuICB3cml0ZVZhbHVlID0gKHZhbHVlOiBzdHJpbmcpOiB2b2lkID0+IHtcbiAgICBpZiAoISF2YWx1ZSkge1xuICAgICAgdGhpcy5leHRyYWN0VmFsdWVzKHZhbHVlKVxuICAgIH1cbiAgfTtcblxuICAvKipcbiAgICogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgZXhlY3V0ZWQgd2hlbiB0aGUgaW5wdXQgdmFsdWUgY2hhbmdlcy5cbiAgICogQHBhcmFtIGZuIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uLlxuICAgKi9cbiAgcmVnaXN0ZXJPbkNoYW5nZSA9IChmbjogKCkgPT4gdm9pZCk6IHZvaWQgPT4ge1xuICAgIHRoaXMub25DaGFuZ2UgPSBmbjtcbiAgfTtcblxuICAvKipcbiAgICogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgZXhlY3V0ZWQgd2hlbiB0aGUgaW5wdXQgaXMgdG91Y2hlZC5cbiAgICogQHBhcmFtIGZuIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uLlxuICAgKi9cbiAgcmVnaXN0ZXJPblRvdWNoZWQgPSAoZm46ICgpID0+IHZvaWQpOiB2b2lkID0+IHtcbiAgICB0aGlzLm9uVG91Y2hlZCA9IGZuO1xuICB9O1xuXG4gIC8qKlxuICAgKiBTZXRzIHRoZSBkaXNhYmxlZCBzdGF0ZSBvZiB0aGUgaW5wdXQuXG4gICAqIEBwYXJhbSBpc0Rpc2FibGVkIC0gVGhlIGRpc2FibGVkIHN0YXRlLlxuICAgKi9cbiAgc2V0RGlzYWJsZWRTdGF0ZSA9IChpc0Rpc2FibGVkOiBib29sZWFuKTogdm9pZCA9PiB7XG4gICAgdGhpcy5kaXNhYmxlZCA9IGlzRGlzYWJsZWQ7XG4gIH07XG5cbiAgLyoqXG4gICAqIEluaXRpYWxpemVzIGRlZmF1bHQgY291bnRyeSBhbmQgcHJlZmVycmVkIGNvdW50cmllcyBvbiBjb21wb25lbnQgaW5pdGlhbGl6YXRpb24uXG4gICAqIENhbGxzIHNldERlZmF1bHRDb3VudHJ5IGFuZCBzZXRQcmVmZXJyZWRDb3VudHJpZXMgbWV0aG9kcy5cbiAgICovXG4gIG5nT25Jbml0KCkge1xuICAgIHRoaXMuc2V0RGVmYXVsdENvdW50cnkoKTtcbiAgICB0aGlzLnNldFByZWZlcnJlZENvdW50cmllcygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgdGhlIGRlZmF1bHQgY291bnRyeSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgZGVmYXVsdENvdW50cnkgdmFsdWUuXG4gICAqIElmIHRoZSBkZWZhdWx0IGNvdW50cnkgZXhpc3RzIGluIHRoZSBjb3VudHJ5TGlzdCwgc2V0cyB0aGUgc2VsZWN0ZWRDb3VudHJ5IGFjY29yZGluZ2x5LlxuICAgKi9cbiAgc2V0RGVmYXVsdENvdW50cnkgPSAoKSA9PiB7XG4gICAgY29uc3QgZGVmQ291bnRyeURhdGEgPSB0aGlzLmNvdW50cnlMaXN0W3RoaXMuZGVmYXVsdENvdW50cnldO1xuICAgIHRoaXMuc2VsZWN0ZWRDb3VudHJ5ID0gZGVmQ291bnRyeURhdGEgPz8gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXRzIHByZWZlcnJlZCBjb3VudHJpZXMgYmFzZWQgb24gdGhlIHByb3ZpZGVkIHByZWZlcnJlZENvdW50cmllcyBhcnJheS5cbiAgICogRmlsdGVycyB0aGUgY291bnRyeUxpc3QgdG8gY29udGFpbiBvbmx5IHRoZSBwcmVmZXJyZWQgY291bnRyaWVzIGlmIHRoZXkgZXhpc3QuXG4gICAqL1xuICBzZXRQcmVmZXJyZWRDb3VudHJpZXMgPSAoKSA9PiB7XG4gICAgaWYgKHRoaXMucHJlZmVycmVkQ291bnRyaWVzICYmIHRoaXMucHJlZmVycmVkQ291bnRyaWVzLmxlbmd0aCA+IDApIHtcbiAgICAgIC8vIEZpbHRlciB0aGUgY291bnRyaWVzIHRvIGluY2x1ZGUgb25seSBwcmVmZXJyZWQgY291bnRyaWVzXG4gICAgICBjb25zdCBwcmVmZXJyZWRDb3VudHJ5TGlzdCA9IE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgICAgT2JqZWN0LmVudHJpZXMoQ291bnRyaWVzKS5maWx0ZXIoKFtjb2RlXSkgPT4gdGhpcy5wcmVmZXJyZWRDb3VudHJpZXMuaW5jbHVkZXMoY29kZSkpXG4gICAgICApO1xuXG4gICAgICAvLyBJZiBwcmVmZXJyZWQgY291bnRyaWVzIGFyZSBmb3VuZCwgdXBkYXRlIHRoZSBjb3VudHJ5TGlzdFxuICAgICAgaWYgKE9iamVjdC5rZXlzKHByZWZlcnJlZENvdW50cnlMaXN0KS5sZW5ndGggPiAwKSB7XG4gICAgICAgIHRoaXMuY291bnRyeUxpc3QgPSBwcmVmZXJyZWRDb3VudHJ5TGlzdDtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVG9nZ2xlcyB0aGUgbW9kYWwgdmlzaWJpbGl0eSBhbmQgc2V0cyB0aGUgZGF0YVNldCB0byB0aGUgY3VycmVudCBjb3VudHJ5TGlzdC5cbiAgICovXG4gIHRvZ2dsZU1vZGFsKCk6IHZvaWQge1xuICAgIHRoaXMuZGF0YVNldCA9IHRoaXMuY291bnRyeUxpc3RcbiAgICB0aGlzLnNob3dNb2RhbCA9ICF0aGlzLnNob3dNb2RhbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXRzIHRoZSBmdWxsIHBob25lIG51bWJlciBieSBjb25jYXRlbmF0aW5nIHRoZSBjb3VudHJ5IGRpYWwgY29kZSBhbmQgdGhlIGlucHV0IHZhbHVlLlxuICAgKiBVcGRhdGVzIHRoZSB2YWx1ZSB1c2luZyB0aGUgb25DaGFuZ2UgY2FsbGJhY2suXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgc2V0RnVsbFBob25lTnVtYmVyID0gKCkgPT4ge1xuICAgIGNvbnN0IGZ1bGxQaG9uZU51bWJlciA9IHRoaXMuc2VsZWN0ZWRDb3VudHJ5ID8gdGhpcy5zZWxlY3RlZENvdW50cnkuZGlhbENvZGUgKyAodGhpcy52YWx1ZSB8fCAnJykgOiB0aGlzLnZhbHVlIHx8ICcnO1xuICAgIC8vIFRyaWdnZXIgdGhlIG9uQ2hhbmdlIGNhbGxiYWNrIHdpdGggdGhlIHVwZGF0ZWQgZnVsbCBwaG9uZSBudW1iZXJcbiAgICB0aGlzLm9uQ2hhbmdlKGZ1bGxQaG9uZU51bWJlcik7XG4gIH1cblxuICAvKipcbiAgICogU2VsZWN0cyBhIGNvdW50cnkgYW5kIGVtaXRzIHRoZSBjb3JyZXNwb25kaW5nIGV2ZW50LlxuICAgKiBAcGFyYW0ge0NvdW50cnl9IGRhdGEgLSBUaGUgc2VsZWN0ZWQgY291bnRyeSBkYXRhLlxuICAgKi9cbiAgc2VsZWN0Q291bnRyeShkYXRhOiB7IGtleTogc3RyaW5nOyB2YWx1ZTogQ291bnRyeURhdGFbc3RyaW5nXSB9KTogdm9pZCB7XG4gICAgdGhpcy5zZWxlY3RlZENvdW50cnkgPSBkYXRhLnZhbHVlO1xuICAgIHRoaXMuc2V0RnVsbFBob25lTnVtYmVyKCk7XG4gICAgdGhpcy50b2dnbGVNb2RhbCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4dHJhY3RzIHRoZSBjb3VudHJ5IGNvZGUgYW5kIHBob25lIG51bWJlciBmcm9tIHRoZSBwcm92aWRlZCBwaG9uZSBudW1iZXIgc3RyaW5nLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcGhvbmVOdW1iZXIgLSBUaGUgaW5wdXQgcGhvbmUgbnVtYmVyIHN0cmluZy5cbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBleHRyYWN0VmFsdWVzID0gKHBob25lTnVtYmVyOiBzdHJpbmcpID0+IHtcbiAgICBsZXQgY291bnRyeSE6IENvdW50cnk7IC8vIFZhcmlhYmxlIHRvIHN0b3JlIHRoZSBleHRyYWN0ZWQgY291bnRyeSBkYXRhXG4gICAgY29uc3QgY291bnRyeUNvZGVzID0gT2JqZWN0LmtleXMoQ291bnRyaWVzKTtcblxuICAgIC8vIFNvcnQgdGhlIGNvdW50cnkgY29kZXMgYnkgbGVuZ3RoIGluIGRlc2NlbmRpbmcgb3JkZXJcbiAgICBjb25zdCBzb3J0ZWRDb2RlcyA9IGNvdW50cnlDb2Rlcy5zb3J0KChhLCBiKSA9PiBDb3VudHJpZXNbYl0uZGlhbENvZGUubGVuZ3RoIC0gQ291bnRyaWVzW2FdLmRpYWxDb2RlLmxlbmd0aCk7XG5cbiAgICAvLyBJdGVyYXRlIHRocm91Z2ggc29ydGVkIGNvdW50cnkgY29kZXMgdG8gZmluZCBhIG1hdGNoIGluIHRoZSBpbnB1dCB2YWx1ZVxuICAgIGZvciAoY29uc3QgY29kZSBvZiBzb3J0ZWRDb2Rlcykge1xuICAgICAgaWYgKHBob25lTnVtYmVyLnN0YXJ0c1dpdGgoQ291bnRyaWVzW2NvZGVdLmRpYWxDb2RlKSkge1xuICAgICAgICBjb3VudHJ5ID0gQ291bnRyaWVzW2NvZGVdO1xuICAgICAgICB0aGlzLnZhbHVlID0gcGhvbmVOdW1iZXIuc3Vic3RyaW5nKGNvdW50cnkuZGlhbENvZGUubGVuZ3RoKTtcbiAgICAgICAgdGhpcy5zZWxlY3RlZENvdW50cnkgPSBjb3VudHJ5XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBJZiBubyBtYXRjaGluZyBjb3VudHJ5IGNvZGUgaXMgZm91bmQsIGF0dGVtcHQgdG8gZXh0cmFjdCBjb3VudHJ5IGNvZGUgYmFzZWQgb24gbGVuZ3RoICgxIHRvIDQgZGlnaXRzKVxuICAgIGZvciAobGV0IGkgPSAxOyBpIDw9IDQ7IGkrKykge1xuICAgICAgY29uc3QgcG90ZW50aWFsQ29kZSA9IHBob25lTnVtYmVyLnN1YnN0cmluZygwLCBpKTtcbiAgICAgIGNvbnN0IG1hdGNoaW5nQ29kZSA9IGNvdW50cnlDb2Rlcy5maW5kKGNvZGVLZXkgPT4gQ291bnRyaWVzW2NvZGVLZXldLmRpYWxDb2RlID09PSBgKyR7cG90ZW50aWFsQ29kZX1gKTtcbiAgICAgIGlmIChtYXRjaGluZ0NvZGUpIHtcbiAgICAgICAgY291bnRyeSA9IENvdW50cmllc1ttYXRjaGluZ0NvZGVdO1xuICAgICAgICB0aGlzLnZhbHVlID0gcGhvbmVOdW1iZXIuc3Vic3RyaW5nKGkpO1xuICAgICAgICB0aGlzLnNlbGVjdGVkQ291bnRyeSA9IGNvdW50cnk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBJZiBubyBjb3VudHJ5IGNvZGUgaXMgaWRlbnRpZmllZCwgY29uc2lkZXIgdGhlIGVudGlyZSBpbnB1dCBhcyB0aGUgcGhvbmUgbnVtYmVyXG4gICAgdGhpcy52YWx1ZSA9IHBob25lTnVtYmVyLnJlcGxhY2UoJysnLCAnJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyaWVzIHRvIGRldGVybWluZSB0aGUgc2VsZWN0ZWQgY291bnRyeSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgaW5wdXQgdmFsdWUuXG4gICAqIFVwZGF0ZXMgdGhlIHNlbGVjdGVkIGNvdW50cnkgYW5kIHZhbHVlIGlmIGEgbWF0Y2hpbmcgY291bnRyeSBjb2RlIGlzIGZvdW5kLlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIGdldENvdW50cnlXaXRoSW5wdXQoKSB7XG4gICAgaWYgKHRoaXMudmFsdWUpIHtcbiAgICAgIC8vIHRlbXAgbnVtYmVyIHRvIGhvbGQgdGhlIGlucHV0IHZhbHVlXG4gICAgICBjb25zdCB0ZW1wTnVtID0gJysnICsgdGhpcy52YWx1ZS50b1N0cmluZygpO1xuICAgICAgaWYgKCF0aGlzLnNlbGVjdGVkQ291bnRyeSAmJiB0ZW1wTnVtLmxlbmd0aCA9PT0gNSkge1xuICAgICAgICBjb25zdCBjb3VudHJ5Q29kZXMgPSBPYmplY3Qua2V5cyhDb3VudHJpZXMpO1xuICAgICAgICBjb25zdCBzb3J0ZWRDb2RlcyA9IGNvdW50cnlDb2Rlcy5zb3J0KChhLCBiKSA9PiBDb3VudHJpZXNbYl0uZGlhbENvZGUubGVuZ3RoIC0gQ291bnRyaWVzW2FdLmRpYWxDb2RlLmxlbmd0aCk7XG4gICAgICAgIGZvciAoY29uc3QgY29kZSBvZiBzb3J0ZWRDb2Rlcykge1xuICAgICAgICAgIGlmICh0ZW1wTnVtLnN0YXJ0c1dpdGgoQ291bnRyaWVzW2NvZGVdLmRpYWxDb2RlKSkge1xuICAgICAgICAgICAgdGhpcy5zZWxlY3RlZENvdW50cnkgPSBDb3VudHJpZXNbY29kZV07XG4gICAgICAgICAgICAvLyByZW1vdmUgdGhlIGNvdW50cnkgY29kZSBmcm9tIGlucHV0IHZhbHVlXG4gICAgICAgICAgICB0aGlzLnZhbHVlID0gdGhpcy52YWx1ZS50b1N0cmluZygpLnN1YnN0cmluZyh0aGlzLnNlbGVjdGVkQ291bnRyeS5kaWFsQ29kZS5sZW5ndGggLSAxKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBFbWl0cyB0aGUgY3VycmVudCBpbnB1dCB2YWx1ZS5cbiAgICovXG4gIG9uSW5wdXRDaGFuZ2VkKCk6IHZvaWQge1xuICAgIHRoaXMuc2V0RnVsbFBob25lTnVtYmVyKCk7XG4gICAgdGhpcy5nZXRDb3VudHJ5V2l0aElucHV0KCk7XG4gIH1cblxuICAvKipcbiAgICogRmlsdGVycyB0aGUgY291bnRyaWVzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBzZWFyY2ggdGVybSBhbmQgdXBkYXRlcyB0aGUgZGF0YXNldC5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHNlYXJjaFRlcm0gLSBUaGUgdGVybSB0byBzZWFyY2ggZm9yIHdpdGhpbiBjb3VudHJ5IG5hbWVzIG9yIGRpYWwgY29kZXMuXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgZmlsdGVyQ291bnRyaWVzKHNlYXJjaFRlcm06IHN0cmluZyk6IHZvaWQge1xuICAgIC8vIENvbnZlcnQgdGhlIHNlYXJjaFRlcm0gdG8gbG93ZXJjYXNlIGZvciBjYXNlLWluc2Vuc2l0aXZlIG1hdGNoaW5nXG4gICAgc2VhcmNoVGVybSA9IHNlYXJjaFRlcm0udG9Mb3dlckNhc2UoKTtcbiAgICAvLyBGaWx0ZXIgdGhlIGNvdW50cnlMaXN0IGJhc2VkIG9uIHRoZSBzZWFyY2hUZXJtIGFuZCB1cGRhdGUgZGF0YVNldFxuICAgIHRoaXMuZGF0YVNldCA9IE9iamVjdC5rZXlzKHRoaXMuY291bnRyeUxpc3QpXG4gICAgICAuZmlsdGVyKGNvZGUgPT5cbiAgICAgICAgdGhpcy5jb3VudHJ5TGlzdFtjb2RlXS5jb3VudHJ5LnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoc2VhcmNoVGVybSkgfHxcbiAgICAgICAgdGhpcy5jb3VudHJ5TGlzdFtjb2RlXS5kaWFsQ29kZS5pbmNsdWRlcyhzZWFyY2hUZXJtKVxuICAgICAgKVxuICAgICAgLnJlZHVjZSgob2JqOiBDb3VudHJ5RGF0YSwgY29kZSkgPT4ge1xuICAgICAgICAvLyBSZWR1Y2UgdGhlIGZpbHRlcmVkIGtleXMgYmFjayBpbnRvIGFuIG9iamVjdFxuICAgICAgICBvYmpbY29kZV0gPSB0aGlzLmNvdW50cnlMaXN0W2NvZGVdO1xuICAgICAgICByZXR1cm4gb2JqO1xuICAgICAgfSwge30pO1xuICB9XG5cbiAgLyoqXG4gICAqIEhhbmRsZXMgY2hhbmdlcyBpbiB0aGUgc2VhcmNoIGlucHV0IGJ5IGludm9raW5nIHRoZSBmaWx0ZXJDb3VudHJpZXMgbWV0aG9kIHdpdGggdGhlIGlucHV0IHZhbHVlLlxuICAgKiBAcGFyYW0ge0V2ZW50fSBldmVudCAtIFRoZSBldmVudCBvYmplY3QgZnJvbSB0aGUgc2VhcmNoIGlucHV0LlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIG9uU2VhcmNoQ2hhbmdlKGV2ZW50OiBFdmVudCk6IHZvaWQge1xuICAgIGNvbnN0IHNlYXJjaFRlcm0gPSAoZXZlbnQudGFyZ2V0IGFzIEhUTUxJbnB1dEVsZW1lbnQpLnZhbHVlO1xuICAgIHRoaXMuZmlsdGVyQ291bnRyaWVzKHNlYXJjaFRlcm0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlcyB0aGUgc2FuaXRpemVkIEhUTUwgcmVwcmVzZW50YXRpb24gb2YgYW4gaWNvbiBzdHJpbmcuXG4gICAqIEBwYXJhbSB7c3RyaW5nIHwgdW5kZWZpbmVkfSBpY29uU3RyaW5nIC0gVGhlIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBhbiBTVkcgaWNvbi5cbiAgICogQHJldHVybnMge1NhZmVIdG1sIHwgdW5kZWZpbmVkfSBUaGUgc2FuaXRpemVkIEhUTUwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGljb24gc3RyaW5nLlxuICAgKi9cbiAgZ2V0SWNvbiA9IChpY29uU3RyaW5nOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBTYWZlSHRtbCB8IHVuZGVmaW5lZCA9PiB7XG4gICAgaWYgKGljb25TdHJpbmcpIHtcbiAgICAgIHJldHVybiB0aGlzLnNhbml0aXplci5ieXBhc3NTZWN1cml0eVRydXN0SHRtbChpY29uU3RyaW5nKTtcbiAgICB9XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfTtcblxuICAvKipcbiAgICogVmFsaWRhdGVzIHRoZSBpbnB1dCBjb250cm9sLlxuICAgKiBAcGFyYW0gY29udHJvbCAtIFRoZSBBYnN0cmFjdENvbnRyb2wgaW5zdGFuY2UuXG4gICAqIEByZXR1cm5zIFZhbGlkYXRpb25FcnJvcnMgaWYgdmFsdWUgaXMgbnVsbC5cbiAgICovXG4gIHZhbGlkYXRlID0gKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCk6IFZhbGlkYXRpb25FcnJvcnMgfCBudWxsID0+IHtcbiAgICBjb25zdCBmdWxsUGhvbmVOdW1iZXIgPSB0aGlzLnNlbGVjdGVkQ291bnRyeSA/IHRoaXMuc2VsZWN0ZWRDb3VudHJ5LmRpYWxDb2RlICsgKHRoaXMudmFsdWUgfHwgJycpIDogJysnICsgdGhpcy52YWx1ZSB8fCAnJztcbiAgICAvLyBpZiBubyB2YWx1ZSB1cGRhdGUgYW5kIGlucHV0dGVkIGNvbnRyb2wgaGFzIHZhbGlkYXRpb24gd2l0aCByZXF1aXJlZCBvciByZXR1cm4gdHJ1ZVxuICAgIGlmICghdGhpcy52YWx1ZSB8fCB0aGlzLnZhbHVlID09PSAnJykge1xuICAgICAgaWYgKGNvbnRyb2wuaGFzVmFsaWRhdG9yKFZhbGlkYXRvcnMucmVxdWlyZWQpKSByZXR1cm4geyByZXF1aXJlZDogdHJ1ZSB9XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICAvLyBjaGVjayB2YWxpZCBwaG9uZSBudW1iZXIgb3Igbm90IHdpdGggbGlicGhvbmVudW1iZXItanNcbiAgICBpZiAoIWlzVmFsaWRQaG9uZU51bWJlcihmdWxsUGhvbmVOdW1iZXIudG9TdHJpbmcoKSkpIHtcbiAgICAgIHJldHVybiB7IHN0YXR1czogXCJJTlZBTElEXCIgfTtcbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfTtcbn1cbiIsIjxkaXYgY2xhc3M9XCJ3cmFwIGZsZXggYWxpZ24tY2VudGVyXCIgW2NsYXNzLmJvcmRlcl09XCJib3JkZXJcIiBbY2xhc3MuYWN0aXZlXT1cImlucHV0Rm9jdXNlZFwiIFtjbGFzcy5lcnJvcl09XCJlcnJvclwiPlxuICAgIDxkaXYgY2xhc3M9XCJmbGV4XCI+XG4gICAgICAgIEBpZihzZWxlY3RlZENvdW50cnkpe1xuICAgICAgICA8ZGl2IGNsYXNzPVwiZmxleCBhbGlnbi1jZW50ZXJcIiAoY2xpY2spPVwidG9nZ2xlTW9kYWwoKVwiPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGFnXCIgW2lubmVySFRNTF09XCJnZXRJY29uKHNlbGVjdGVkQ291bnRyeS5mbGFnKVwiPjwvc3Bhbj5cblxuICAgICAgICAgICAgPHN2ZyBjbGFzcz1cIm1sLTVcIiB4bWxucz1cImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnXCIgd2lkdGg9XCIxMFwiIGhlaWdodD1cIjZcIiB2aWV3Qm94PVwiMCAwIDEwIDZcIiBmaWxsPVwibm9uZVwiPlxuICAgICAgICAgICAgICAgIDxwYXRoIGQ9XCJNNC45OTk1OSA1LjEzNjk4TDAuNzI1NTg2IDAuODYyOTc2SDkuMjczNTlMNC45OTk1OSA1LjEzNjk4WlwiIGZpbGw9XCJibGFja1wiIGZpbGwtb3BhY2l0eT1cIjAuNlwiIC8+XG4gICAgICAgICAgICA8L3N2Zz5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwidGV4dCBtbC01XCI+e3tzZWxlY3RlZENvdW50cnkuZGlhbENvZGV9fTwvc3Bhbj5cbiAgICAgICAgfUBlbHNle1xuICAgICAgICA8ZGl2IGNsYXNzPVwiZmxleCBhbGlnbi1jZW50ZXJcIiAoY2xpY2spPVwidG9nZ2xlTW9kYWwoKVwiPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJmbGFnXCIgW2lubmVySFRNTF09XCJnZXRJY29uKGdsb2JlU1ZHKVwiPjwvc3Bhbj5cbiAgICAgICAgICAgIDxzdmcgY2xhc3M9XCJtbC01XCIgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiIHdpZHRoPVwiMTBcIiBoZWlnaHQ9XCI2XCIgdmlld0JveD1cIjAgMCAxMCA2XCIgZmlsbD1cIm5vbmVcIj5cbiAgICAgICAgICAgICAgICA8cGF0aCBkPVwiTTQuOTk5NTkgNS4xMzY5OEwwLjcyNTU4NiAwLjg2Mjk3Nkg5LjI3MzU5TDQuOTk5NTkgNS4xMzY5OFpcIiBmaWxsPVwiYmxhY2tcIiBmaWxsLW9wYWNpdHk9XCIwLjZcIiAvPlxuICAgICAgICAgICAgPC9zdmc+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICB9XG5cbiAgICA8L2Rpdj5cblxuICAgIDxpbnB1dCBjbGFzcz1cImlucHV0LWJveCB0ZXh0XCIgdHlwZT1cIm51bWJlclwiIHBsYWNlaG9sZGVyPVwiXCIgWyhuZ01vZGVsKV09XCJ2YWx1ZVwiIFtkaXNhYmxlZF09XCJkaXNhYmxlZFwiXG4gICAgICAgIChpbnB1dCk9XCJvbkNoYW5nZSh2YWx1ZSk7IG9uSW5wdXRDaGFuZ2VkKClcIiAoYmx1cik9XCJvbkJsdXIoKTsgb25Ub3VjaGVkKClcIiAoZm9jdXMpPVwib25Gb2N1cygpXCIgLz5cblxuICAgIEBpZihzaG93TW9kYWwpe1xuICAgIDxkaXYgY2xhc3M9XCJtb2RhbC13cmFwXCI+XG4gICAgICAgIDxkaXY+XG4gICAgICAgICAgICA8aW5wdXQgY2xhc3M9XCJzZWFyY2gtYm94XCIgKGlucHV0KT1cIm9uU2VhcmNoQ2hhbmdlKCRldmVudClcIiBwbGFjZWhvbGRlcj1cIlNlYXJjaFwiIC8+XG4gICAgICAgIDwvZGl2PlxuICAgICAgICA8ZGl2IGNsYXNzPVwiY291bnRyeS1saXN0XCI+XG4gICAgICAgICAgICA8ZGl2IHN0eWxlPVwiZGlzcGxheTogZmxleDtcIiAqbmdGb3I9XCJsZXQgZGF0YSBvZiBkYXRhU2V0IHwga2V5dmFsdWVcIj5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibW9kYWwtY291bnRyeS13cmFwIGZsZXhcIiAoY2xpY2spPVwic2VsZWN0Q291bnRyeShkYXRhKVwiPlxuICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cImZsYWdcIiBbaW5uZXJIVE1MXT1cImdldEljb24oZGF0YS52YWx1ZS5mbGFnKVwiPjwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJtb2RhbC1jb3VudHJ5IG1sLTVcIj57e2RhdGEudmFsdWUuZGlhbENvZGV9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJtb2RhbC1jb3VudHJ5IG1sLTVcIj57e2RhdGEudmFsdWUuY291bnRyeX19PC9zcGFuPlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICAgIH1cbjwvZGl2PiJdfQ==