UNPKG

@nebulae/angular-ble

Version:

A Web Bluetooth (Bluetooth Low Energy) module for angular (v2+)

1,639 lines 151 kB
import { Injectable, NgModule, InjectionToken, defineInjectable, EventEmitter, inject } from '@angular/core';
import { ModeOfOperation, Counter, utils } from 'aes-js';
import { __extends, __read, __values } from 'tslib';
import { map, mergeMap, concat, mapTo, filter, takeUntil, scan, tap, take, timeout, retryWhen, delay } from 'rxjs/operators';
import { Observable, forkJoin, Subject, defer, fromEvent, of, from } from 'rxjs';
import { CommonModule } from '@angular/common';

/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
 */
var BrowserWebBluetooth = /** @class */ (function () {
    function BrowserWebBluetooth() {
        this._ble = navigator.bluetooth;
        if (!this._ble) {
            console.log('error cargando bluetooth');
            // bluetoothService.setBluetoothAvailable(false);
            // throw new Error('Your browser does not support Smart Bluetooth. See http://caniuse.com/#search=Bluetooth for more details.');
        }
    }
    /**
     * @param {?} options
     * @return {?}
     */
    BrowserWebBluetooth.prototype.requestDevice = /**
     * @param {?} options
     * @return {?}
     */
    function (options) {
        return this._ble.requestDevice(options);
    };
    BrowserWebBluetooth.decorators = [
        { type: Injectable },
    ];
    /** @nocollapse */
    BrowserWebBluetooth.ctorParameters = function () { return []; };
    return BrowserWebBluetooth;
}());

/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
 */
/** @type {?} */
var GattServices = {
    GENERIC_ACCESS: {
        SERVICE: 'generic_access',
        DEVICE_NAME: 'device_name',
        APPEARANCE: 'appearance',
        PRIVACY_FLAG: 'privacy_flag',
        RECONNECTION_ADDRESS: 'reconnection_address',
        PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS: 'peripheral_preferred_connection_parameters'
    },
    BATTERY: {
        SERVICE: 'battery_service',
        BATTERY_LEVEL: 'battery_level'
    },
    DEVICE_INFORMATION: {
        SERVICE: 'device_information',
        MANUFACTURER_NAME: 'manufacturer_name_string',
        MODEL_NUMBER: 'model_number_string',
        SERIAL_NUMBER: 'serial_number_string',
        HARDWARE_REVISION: 'hardware_revision_string',
        FIRMWARE_REVISION: 'firmware_revision_string',
        SOFTWARE_REVISION: 'software_revision_string',
        SYSTEM_ID: 'system_id',
        PNP_ID: 'pnp_id'
    }
};
/** @type {?} */
var GATT_SERVICES = Object.keys(GattServices).map(function (key) { return GattServices[key].SERVICE; });

/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
 */
var CypherAesService = /** @class */ (function () {
    function CypherAesService() {
        this.masterKey = [];
        this.initialVector = [];
        this.encryptMethod = 'CBC';
        this.isStaticInitialVector = true;
        this.isConfigExecuted = false;
    }
    /**
     * Initial config used to initalice all required params
     * @param masterKey key used to encrypt and decrypt
     * @param initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
     * @param encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
     * @param additionalEncryptMethodParams configuration params used by the selected encrypt method.
     * Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
     * By CTR require the param counter and by CFB require the param segmentSize
     * @param isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
     */
    /**
     * Initial config used to initalice all required params
     * @param {?} masterKey key used to encrypt and decrypt
     * @param {?=} initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
     * @param {?=} encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
     * @param {?=} additionalEncryptMethodParams configuration params used by the selected encrypt method.
     * Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
     * By CTR require the param counter and by CFB require the param segmentSize
     * @param {?=} isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
     * @return {?}
     */
    CypherAesService.prototype.config = /**
     * Initial config used to initalice all required params
     * @param {?} masterKey key used to encrypt and decrypt
     * @param {?=} initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
     * @param {?=} encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
     * @param {?=} additionalEncryptMethodParams configuration params used by the selected encrypt method.
     * Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
     * By CTR require the param counter and by CFB require the param segmentSize
     * @param {?=} isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
     * @return {?}
     */
    function (masterKey, initialVector, encryptMethod, additionalEncryptMethodParams, isStaticInitialVector) {
        if (initialVector === void 0) { initialVector = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; }
        if (encryptMethod === void 0) { encryptMethod = 'CBC'; }
        if (additionalEncryptMethodParams === void 0) { additionalEncryptMethodParams = {}; }
        if (isStaticInitialVector === void 0) { isStaticInitialVector = true; }
        this.isConfigExecuted = true;
        this.masterKey = masterKey;
        this.initialVector = initialVector;
        this.encryptMethod = encryptMethod;
        this.isStaticInitialVector = isStaticInitialVector;
        this.additionalEncryptMethodParams = additionalEncryptMethodParams;
        if (!isStaticInitialVector) {
            this.enctrypMethodInstance = this.generateEncryptMethodInstance();
        }
    };
    /**
     * Encrypt the data using the encrypt method previously configured
     * @param dataArrayBuffer data to encrypt
     */
    /**
     * Encrypt the data using the encrypt method previously configured
     * @param {?} dataArrayBuffer data to encrypt
     * @return {?}
     */
    CypherAesService.prototype.encrypt = /**
     * Encrypt the data using the encrypt method previously configured
     * @param {?} dataArrayBuffer data to encrypt
     * @return {?}
     */
    function (dataArrayBuffer) {
        if (!this.isConfigExecuted) {
            throw new Error('Must configurate cypher-aes before call this method, use the method config()');
        }
        if (this.encryptMethod === 'CBC' || this.encryptMethod === 'ECB') {
            dataArrayBuffer = this.addPadding(dataArrayBuffer);
        }
        return this.isStaticInitialVector
            ? this.generateEncryptMethodInstance().encrypt(dataArrayBuffer)
            : this.enctrypMethodInstance.encrypt(dataArrayBuffer);
    };
    /**
     * Decrypt the data using the encrypt method previously configured
     * @param dataArrayBuffer data to decrypt
     */
    /**
     * Decrypt the data using the encrypt method previously configured
     * @param {?} dataArrayBuffer data to decrypt
     * @return {?}
     */
    CypherAesService.prototype.decrypt = /**
     * Decrypt the data using the encrypt method previously configured
     * @param {?} dataArrayBuffer data to decrypt
     * @return {?}
     */
    function (dataArrayBuffer) {
        if (!this.isConfigExecuted) {
            throw new Error('Must configurate cypher-aes before call this method, use the method config()');
        }
        return this.isStaticInitialVector
            ? this.generateEncryptMethodInstance().decrypt(dataArrayBuffer)
            : this.enctrypMethodInstance.decrypt(dataArrayBuffer);
    };
    /**
     * Change the current initalVector
     * @param initialVector new initalVector
     */
    /**
     * Change the current initalVector
     * @param {?} initialVector new initalVector
     * @return {?}
     */
    CypherAesService.prototype.changeInitialVector = /**
     * Change the current initalVector
     * @param {?} initialVector new initalVector
     * @return {?}
     */
    function (initialVector) {
        if (!this.isStaticInitialVector) {
            this.enctrypMethodInstance = this.generateEncryptMethodInstance();
        }
        this.initialVector = initialVector;
    };
    /**
     * Change the current encyptMethod
     * @param encryptMethod new encryptMethod
     */
    /**
     * Change the current encyptMethod
     * @param {?} encryptMethod new encryptMethod
     * @return {?}
     */
    CypherAesService.prototype.changeEncryptMethod = /**
     * Change the current encyptMethod
     * @param {?} encryptMethod new encryptMethod
     * @return {?}
     */
    function (encryptMethod) {
        if (!this.isStaticInitialVector) {
            this.enctrypMethodInstance = this.generateEncryptMethodInstance();
        }
        this.encryptMethod = encryptMethod;
    };
    /**
     * Change the current isStaticInitialVector
     * @param isStaticInitialVector new isStaticInitalVector
     */
    /**
     * Change the current isStaticInitialVector
     * @param {?} isStaticInitialVector new isStaticInitalVector
     * @return {?}
     */
    CypherAesService.prototype.changeStaticInitialVector = /**
     * Change the current isStaticInitialVector
     * @param {?} isStaticInitialVector new isStaticInitalVector
     * @return {?}
     */
    function (isStaticInitialVector) {
        if (!isStaticInitialVector) {
            this.enctrypMethodInstance = this.generateEncryptMethodInstance();
        }
        this.isStaticInitialVector = isStaticInitialVector;
    };
    /**
     * Change the current masterKey
     * @param masterKey new masterKey
     */
    /**
     * Change the current masterKey
     * @param {?} masterKey new masterKey
     * @return {?}
     */
    CypherAesService.prototype.changeMasterKey = /**
     * Change the current masterKey
     * @param {?} masterKey new masterKey
     * @return {?}
     */
    function (masterKey) {
        if (!this.isStaticInitialVector) {
            this.enctrypMethodInstance = this.generateEncryptMethodInstance();
        }
        this.masterKey = masterKey;
    };
    /**
     * Add padding to the list
     * @param {?} arrayBuffer
     * @return {?}
     */
    CypherAesService.prototype.addPadding = /**
     * Add padding to the list
     * @param {?} arrayBuffer
     * @return {?}
     */
    function (arrayBuffer) {
        /** @type {?} */
        var paddingLength = Math.ceil(Array.from(arrayBuffer).length / 16) * 16;
        /** @type {?} */
        var paddingList = new Array(paddingLength - Array.from(arrayBuffer).length).fill(0);
        return new Uint8Array(Array.from(arrayBuffer).concat(paddingList));
    };
    /**
     * generate a instance of the encrypt method using the current method configured
     * @return {?}
     */
    CypherAesService.prototype.generateEncryptMethodInstance = /**
     * generate a instance of the encrypt method using the current method configured
     * @return {?}
     */
    function () {
        /** @type {?} */
        var enctrypMethodInstance;
        switch (this.encryptMethod) {
            case 'CBC':
                enctrypMethodInstance = new ModeOfOperation.cbc(this.masterKey, this.initialVector);
                break;
            case 'CTR':
                if (!this.additionalEncryptMethodParams.counter) {
                    throw new Error('additionalEncryptMethodParams.counter is required to use encrypt method CTR');
                }
                enctrypMethodInstance = new ModeOfOperation.ctr(this.masterKey, this.initialVector, new Counter(this.additionalEncryptMethodParams.counter));
                break;
            case 'CFB':
                if (!this.additionalEncryptMethodParams.segmentSize) {
                    throw new Error('additionalEncryptMethodParams.segmentSize is required to use encrypt method CFB');
                }
                enctrypMethodInstance = new ModeOfOperation.cfb(this.masterKey, this.initialVector, this.additionalEncryptMethodParams.segmentSize);
                break;
            case 'OFB':
                enctrypMethodInstance = new ModeOfOperation.ofb(this.masterKey, this.initialVector);
                break;
            case 'ECB':
                enctrypMethodInstance = new ModeOfOperation.ecb(this.masterKey);
                break;
        }
        return enctrypMethodInstance;
    };
    // #region UTILS
    /**
     * Convert the text to bytes
     * @param text Text to convert
     */
    /**
     * Convert the text to bytes
     * @param {?} text Text to convert
     * @return {?}
     */
    CypherAesService.prototype.textToBytes = /**
     * Convert the text to bytes
     * @param {?} text Text to convert
     * @return {?}
     */
    function (text) {
        return utils.utf8.toBytes(text);
    };
    /**
     * Convert the bytes to text
     * @param bytes Bytes to convert
     */
    /**
     * Convert the bytes to text
     * @param {?} bytes Bytes to convert
     * @return {?}
     */
    CypherAesService.prototype.bytesToText = /**
     * Convert the bytes to text
     * @param {?} bytes Bytes to convert
     * @return {?}
     */
    function (bytes) {
        return utils.utf8.fromBytes(bytes);
    };
    /**
     * Convert the bytes to hex
     * @param bytes bytes to convert
     */
    /**
     * Convert the bytes to hex
     * @param {?} bytes bytes to convert
     * @return {?}
     */
    CypherAesService.prototype.bytesTohex = /**
     * Convert the bytes to hex
     * @param {?} bytes bytes to convert
     * @return {?}
     */
    function (bytes) {
        return utils.hex.fromBytes(bytes);
    };
    /**
     * Convert the hex to bytes
     * @param hex Hex to convert
     */
    /**
     * Convert the hex to bytes
     * @param {?} hex Hex to convert
     * @return {?}
     */
    CypherAesService.prototype.hexToBytes = /**
     * Convert the hex to bytes
     * @param {?} hex Hex to convert
     * @return {?}
     */
    function (hex) {
        return utils.hex.toBytes(hex);
    };
    // #endregion
    /**
     * @param {?} key
     * @return {?}
     */
    CypherAesService.prototype.generateSubkeys = /**
     * @param {?} key
     * @return {?}
     */
    function (key) {
        /** @type {?} */
        var const_Zero = new Uint8Array(16);
        /** @type {?} */
        var const_Rb = new Buffer('00000000000000000000000000000087', 'hex');
        /** @type {?} */
        var enctrypMethodInstance = new ModeOfOperation.cbc(key, new Uint8Array(16));
        /** @type {?} */
        var lEncrypted = enctrypMethodInstance.encrypt(const_Zero);
        /** @type {?} */
        var l = new Buffer(this.bytesTohex(lEncrypted), 'hex');
        /** @type {?} */
        var subkey1 = this.bitShiftLeft(l);
        // tslint:disable-next-line:no-bitwise
        if (l[0] & 0x80) {
            subkey1 = this.xor(subkey1, const_Rb);
        }
        /** @type {?} */
        var subkey2 = this.bitShiftLeft(subkey1);
        // tslint:disable-next-line:no-bitwise
        if (subkey1[0] & 0x80) {
            subkey2 = this.xor(subkey2, const_Rb);
        }
        return { subkey1: subkey1, subkey2: subkey2 };
    };
    /**
     * @param {?} key
     * @param {?} message
     * @return {?}
     */
    CypherAesService.prototype.aesCmac = /**
     * @param {?} key
     * @param {?} message
     * @return {?}
     */
    function (key, message) {
        console.log('INICIA CIFRADO!!!!!!!!!!!!!!!!');
        /** @type {?} */
        var subkeys = this.generateSubkeys(key);
        /** @type {?} */
        var blockCount = Math.ceil(message.length / 16);
        /** @type {?} */
        var lastBlockCompleteFlag;
        /** @type {?} */
        var lastBlock;
        /** @type {?} */
        var lastBlockIndex;
        if (blockCount === 0) {
            blockCount = 1;
            lastBlockCompleteFlag = false;
        }
        else {
            lastBlockCompleteFlag = message.length % 16 === 0;
        }
        lastBlockIndex = blockCount - 1;
        if (lastBlockCompleteFlag) {
            lastBlock = this.xor(this.getMessageBlock(message, lastBlockIndex), subkeys.subkey1);
        }
        else {
            lastBlock = this.xor(this.getPaddedMessageBlock(message, lastBlockIndex), subkeys.subkey2);
        }
        /** @type {?} */
        var x = new Buffer('00000000000000000000000000000000', 'hex');
        /** @type {?} */
        var y;
        /** @type {?} */
        var enctrypMethodInstance;
        for (var index = 0; index < lastBlockIndex; index++) {
            enctrypMethodInstance = new ModeOfOperation.cbc(key, new Uint8Array(16));
            y = this.xor(x, this.getMessageBlock(message, index));
            /** @type {?} */
            var xEncrypted = enctrypMethodInstance.encrypt(y);
            console.log('X normal ===============> ', this.bytesTohex(y));
            console.log('X encrypted ==============> ', this.bytesTohex(xEncrypted));
            x = new Buffer(this.bytesTohex(xEncrypted), 'hex');
        }
        y = this.xor(lastBlock, x);
        enctrypMethodInstance = new ModeOfOperation.cbc(key, new Uint8Array(16));
        /** @type {?} */
        var yEncrypted = enctrypMethodInstance.encrypt(y);
        console.log('Y normal ==============> ', this.bytesTohex(y));
        console.log('Y encrypted ==============> ', this.bytesTohex(yEncrypted));
        return yEncrypted;
    };
    /**
     * @param {?} message
     * @param {?} blockIndex
     * @return {?}
     */
    CypherAesService.prototype.getMessageBlock = /**
     * @param {?} message
     * @param {?} blockIndex
     * @return {?}
     */
    function (message, blockIndex) {
        /** @type {?} */
        var block = new Buffer(16);
        /** @type {?} */
        var start = blockIndex * 16;
        /** @type {?} */
        var end = start + 16;
        /** @type {?} */
        var blockI = 0;
        for (var i = start; i < end; i++) {
            block[blockI] = message[i];
            blockI++;
        }
        return block;
    };
    /**
     * @param {?} message
     * @param {?} blockIndex
     * @return {?}
     */
    CypherAesService.prototype.getPaddedMessageBlock = /**
     * @param {?} message
     * @param {?} blockIndex
     * @return {?}
     */
    function (message, blockIndex) {
        /** @type {?} */
        var block = new Buffer(16);
        /** @type {?} */
        var start = blockIndex * 16;
        /** @type {?} */
        var end = message.length;
        block.fill(0);
        /** @type {?} */
        var blockI = 0;
        for (var i = start; i < end; i++) {
            block[blockI] = message[i];
            blockI++;
        }
        block[end - start] = 0x80;
        return block;
    };
    /**
     * @param {?} buffer
     * @return {?}
     */
    CypherAesService.prototype.bitShiftLeft = /**
     * @param {?} buffer
     * @return {?}
     */
    function (buffer) {
        /** @type {?} */
        var shifted = new Buffer(buffer.length);
        /** @type {?} */
        var last = buffer.length - 1;
        for (var index = 0; index < last; index++) {
            // tslint:disable-next-line:no-bitwise
            shifted[index] = buffer[index] << 1;
            // tslint:disable-next-line:no-bitwise
            if (buffer[index + 1] & 0x80) {
                shifted[index] += 0x01;
            }
        }
        // tslint:disable-next-line:no-bitwise
        shifted[last] = buffer[last] << 1;
        return shifted;
    };
    /**
     * @param {?} bufferA
     * @param {?} bufferB
     * @return {?}
     */
    CypherAesService.prototype.xor = /**
     * @param {?} bufferA
     * @param {?} bufferB
     * @return {?}
     */
    function (bufferA, bufferB) {
        /** @type {?} */
        var length = Math.min(bufferA.length, bufferB.length);
        /** @type {?} */
        var output = new Buffer(length);
        for (var index = 0; index < length; index++) {
            // tslint:disable-next-line:no-bitwise
            output[index] = bufferA[index] ^ bufferB[index];
        }
        return output;
    };
    CypherAesService.decorators = [
        { type: Injectable, args: [{
                    providedIn: 'root'
                },] },
    ];
    /** @nocollapse */
    CypherAesService.ctorParameters = function () { return []; };
    /** @nocollapse */ CypherAesService.ngInjectableDef = defineInjectable({ factory: function CypherAesService_Factory() { return new CypherAesService(); }, token: CypherAesService, providedIn: "root" });
    return CypherAesService;
}());

/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
 */
var ConsoleLoggerService = /** @class */ (function () {
    function ConsoleLoggerService() {
    }
    /**
     * @param {...?} args
     * @return {?}
     */
    ConsoleLoggerService.prototype.log = /**
     * @param {...?} args
     * @return {?}
     */
    function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        console.log.apply(console, args);
    };
    /**
     * @param {...?} args
     * @return {?}
     */
    ConsoleLoggerService.prototype.error = /**
     * @param {...?} args
     * @return {?}
     */
    function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        console.error.apply(console, args);
    };
    /**
     * @param {...?} args
     * @return {?}
     */
    ConsoleLoggerService.prototype.warn = /**
     * @param {...?} args
     * @return {?}
     */
    function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        console.warn.apply(console, args);
    };
    ConsoleLoggerService.decorators = [
        { type: Injectable, args: [{
                    providedIn: 'root'
                },] },
    ];
    /** @nocollapse */ ConsoleLoggerService.ngInjectableDef = defineInjectable({ factory: function ConsoleLoggerService_Factory() { return new ConsoleLoggerService(); }, token: ConsoleLoggerService, providedIn: "root" });
    return ConsoleLoggerService;
}());
var NoLoggerService = /** @class */ (function () {
    function NoLoggerService() {
    }
    /**
     * @param {...?} args
     * @return {?}
     */
    NoLoggerService.prototype.log = /**
     * @param {...?} args
     * @return {?}
     */
    function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
    };
    /**
     * @param {...?} args
     * @return {?}
     */
    NoLoggerService.prototype.error = /**
     * @param {...?} args
     * @return {?}
     */
    function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
    };
    /**
     * @param {...?} args
     * @return {?}
     */
    NoLoggerService.prototype.warn = /**
     * @param {...?} args
     * @return {?}
     */
    function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
    };
    NoLoggerService.decorators = [
        { type: Injectable, args: [{
                    providedIn: 'root'
                },] },
    ];
    /** @nocollapse */ NoLoggerService.ngInjectableDef = defineInjectable({ factory: function NoLoggerService_Factory() { return new NoLoggerService(); }, token: NoLoggerService, providedIn: "root" });
    return NoLoggerService;
}());

/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
 */
var BluetoothService = /** @class */ (function (_super) {
    __extends(BluetoothService, _super);
    function BluetoothService(_webBle, cypherAesService, _console) {
        var _this = _super.call(this) || this;
        _this._webBle = _webBle;
        _this.cypherAesService = cypherAesService;
        _this._console = _console;
        _this.serviceCharacteristicVsSubscriptionList = {};
        _this.notifierSubject = new Subject();
        _this.notifierStartedSubject = new Subject();
        _this.bluetoothAvailable = false;
        _this._device$ = new EventEmitter();
        if (_webBle._ble) {
            _this.bluetoothAvailable = true;
        }
        return _this;
    }
    /**
     * @return {?}
     */
    BluetoothService.prototype.isBluetoothAvailable = /**
     * @return {?}
     */
    function () {
        return this.bluetoothAvailable;
    };
    /**
     * get the current device, if the device return null is because the connection has lost
     * @returns the current connceted device
     */
    /**
     * get the current device, if the device return null is because the connection has lost
     * @return {?} the current connceted device
     */
    BluetoothService.prototype.getDevice$ = /**
     * get the current device, if the device return null is because the connection has lost
     * @return {?} the current connceted device
     */
    function () {
        return this._device$;
    };
    /**
     * @return {?}
     */
    BluetoothService.prototype.getNotifierStartedSubject$ = /**
     * @return {?}
     */
    function () {
        return this.notifierStartedSubject;
    };
    /**
     * start a stream by notifiers characteristics
     * @param service The service to which the characteristic belongs
     * @param characteristic The characteristic whose value you want to listen
     * @param options object that contains the
     * startByte:number (required), stopByte:number (required),
     * lengthPosition: { start: number, end: number, lengthPadding: number } (required)
     * @returns A DataView than contains the characteristic value
     */
    /**
     * start a stream by notifiers characteristics
     * @param {?} service The service to which the characteristic belongs
     * @param {?} characteristic The characteristic whose value you want to listen
     * @param {?} options object that contains the
     * startByte:number (required), stopByte:number (required),
     * lengthPosition: { start: number, end: number, lengthPadding: number } (required)
     * @return {?} A DataView than contains the characteristic value
     */
    BluetoothService.prototype.startNotifierListener$ = /**
     * start a stream by notifiers characteristics
     * @param {?} service The service to which the characteristic belongs
     * @param {?} characteristic The characteristic whose value you want to listen
     * @param {?} options object that contains the
     * startByte:number (required), stopByte:number (required),
     * lengthPosition: { start: number, end: number, lengthPadding: number } (required)
     * @return {?} A DataView than contains the characteristic value
     */
    function (service, characteristic, options) {
        var _this = this;
        return defer(function () {
            of(service)
                .pipe(tap(function () { return _this._console.log('Inicia el notifier con la instancia: ', _this.serviceCharacteristicVsSubscriptionList); }), filter(function (_) {
                return Object.keys(_this.serviceCharacteristicVsSubscriptionList).indexOf(service + "-" + characteristic) === -1;
            }))
                .subscribe(function () {
                _this.serviceCharacteristicVsSubscriptionList[service + "-" + characteristic] =
                    _this.buildNotifierListener$(service, characteristic, options).subscribe(function (message) {
                        _this.notifierSubject.next(message);
                    });
            }, function (err) {
                _this._console.log('[BLE::Info] Error in notifier: ', err);
            }, function () { });
            return of("notifier as subscribed: service= " + service + ", characteristic= " + characteristic);
        });
    };
    /**
     * @param {?} service
     * @param {?} characteristic
     * @return {?}
     */
    BluetoothService.prototype.stopNotifierListener$ = /**
     * @param {?} service
     * @param {?} characteristic
     * @return {?}
     */
    function (service, characteristic) {
        var _this = this;
        return defer(function () {
            // this.serviceCharacteristicVsSubscriptionList[`${service}-${characteristic}`].unsubscribe();
            delete _this.serviceCharacteristicVsSubscriptionList[service + "-" + characteristic];
            return of("the notifier of the characteristic " + characteristic + " as been stopped");
        });
    };
    /**
     * @param {?} service
     * @param {?} characteristic
     * @param {?} options
     * @return {?}
     */
    BluetoothService.prototype.buildNotifierListener$ = /**
     * @param {?} service
     * @param {?} characteristic
     * @param {?} options
     * @return {?}
     */
    function (service, characteristic, options) {
        var _this = this;
        return this.getPrimaryService$(service).pipe(tap(function (serviceInstance) { return _this._console.log("toma exitosamente el servicio ================> " + serviceInstance); }), mergeMap(function (primaryService) {
            return _this.getCharacteristic$(primaryService, characteristic)
                .pipe(tap(function (char) { return _this._console.log("toma exitosamente la caracteristica ================> " + char); }));
        }), mergeMap(function (char) {
            return defer(function () {
                // enable the characteristic notifier
                return char.startNotifications();
            }).pipe(retryWhen(function (error) {
                return error.pipe(tap(function () { return _this._console.log('ERROR EN EL startNotifications ================> '); }), delay(1000), take(5));
            }), tap(function () {
                _this.notifierStartedSubject.next(true);
                _this._console.log("incia las notifiaciones de la caracteristica 2 ================> " + characteristic);
            }), mergeMap(function (_) {
                // start the lister from the even characteristicvaluechanged to get all changes on the specific
                // characteristic
                return fromEvent(char, 'characteristicvaluechanged').pipe(takeUntil(fromEvent(char, 'gattserverdisconnected')), map(function (event) {
                    // get a event from the characteristic and map that
                    return {
                        startByteMatches: false,
                        stopByteMatches: false,
                        lengthMatches: false,
                        messageLength: 0,
                        timestamp: Date.now(),
                        data: Array.from(new Uint8Array((/** @type {?} */ ((/** @type {?} */ (event.target))
                            .value)).buffer))
                    };
                }), scan(function (acc, value) {
                    acc.timestamp = acc.timestamp === 0 ? value.timestamp : acc.timestamp;
                    // if the current accumulator value is a valid message, then is restarted to get the next
                    // message
                    if ((acc.lengthMatches &&
                        acc.startByteMatches &&
                        acc.stopByteMatches)
                        || acc.timestamp + 1000 < Date.now()) {
                        acc = {
                            startByteMatches: false,
                            stopByteMatches: false,
                            lengthMatches: false,
                            messageLength: 0,
                            timestamp: 0,
                            data: []
                        };
                    }
                    // validate the start byte
                    if (!acc.startByteMatches &&
                        value.data[0] === options.startByte) {
                        // get the message length using the start and end position
                        acc.messageLength =
                            new DataView(new Uint8Array(value.data.slice(options.lengthPosition.start, options.lengthPosition.end)).buffer).getInt16(0, false) +
                                (options.lengthPosition.lengthPadding
                                    ? options.lengthPosition.lengthPadding
                                    : 0);
                        // valid that the initial byte was found
                        acc.startByteMatches = true;
                    }
                    if (!acc.stopByteMatches &&
                        value.data[value.data.length - 1] === options.stopByte) {
                        // valid that the end byte was found
                        acc.stopByteMatches = true;
                    }
                    if (acc.startByteMatches) {
                        // merge the new data bytes to the old bytes
                        acc.data = acc.data.concat(value.data);
                    }
                    acc.lengthMatches =
                        acc.startByteMatches &&
                            acc.stopByteMatches &&
                            acc.messageLength === acc.data.length;
                    return acc;
                }, {
                    startByteMatches: false,
                    stopByteMatches: false,
                    lengthMatches: false,
                    messageLength: 0,
                    timestamp: 0,
                    data: []
                }), 
                // only publish the complete and valid message
                filter(function (data) {
                    return data.lengthMatches &&
                        data.startByteMatches &&
                        data.stopByteMatches;
                }), 
                // remove all custom data and publish the message data
                map(function (result) { return result.data; }));
            }));
        }));
    };
    /**
     * Send a request to the device and wait a unique response
     * @param message Message to send
     * @param service The service to which the characteristic belongs
     * @param characteristic The characteristic whose value you want to send the message
     * @param responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
     *  {position: 13, byteToMatch: 0x45}]
     * @param cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     */
    /**
     * Send a request to the device and wait a unique response
     * @param {?} message Message to send
     * @param {?} service The service to which the characteristic belongs
     * @param {?} characteristic The characteristic whose value you want to send the message
     * @param {?} responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
     *  {position: 13, byteToMatch: 0x45}]
     * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     * @return {?}
     */
    BluetoothService.prototype.sendAndWaitResponse$ = /**
     * Send a request to the device and wait a unique response
     * @param {?} message Message to send
     * @param {?} service The service to which the characteristic belongs
     * @param {?} characteristic The characteristic whose value you want to send the message
     * @param {?} responseType filter to use to identify the response, Sample: [{position: 3, byteToMatch: 0x83},
     *  {position: 13, byteToMatch: 0x45}]
     * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     * @return {?}
     */
    function (message, service, characteristic, responseType, cypherMasterKey) {
        this._console.log('[BLE::Info] Send message to device: ', this.cypherAesService.bytesTohex(message));
        return forkJoin(this.subscribeToNotifierListener(responseType, cypherMasterKey).pipe(take(1)), this.sendToNotifier$(message, service, characteristic)).pipe(map(function (_a) {
            var _b = __read(_a, 2), messageResp = _b[0], _ = _b[1];
            return messageResp;
        }), timeout(3000));
    };
    /**
     * Subscribe to the notifiers filtering by byte checking
     * @param filterOptions must specific the position and the byte to match Sample:
     * [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
     * @param cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     */
    /**
     * Subscribe to the notifiers filtering by byte checking
     * @param {?} filterOptions must specific the position and the byte to match Sample:
     * [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
     * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     * @return {?}
     */
    BluetoothService.prototype.subscribeToNotifierListener = /**
     * Subscribe to the notifiers filtering by byte checking
     * @param {?} filterOptions must specific the position and the byte to match Sample:
     * [{position: 3, byteToMatch: 0x83}, {position: 13, byteToMatch: 0x45}]
     * @param {?=} cypherMasterKey master key to decrypt the message, only use this para if the message to receive is encrypted
     * @return {?}
     */
    function (filterOptions, cypherMasterKey) {
        var _this = this;
        return this.notifierSubject.pipe(map(function (messageUnformated) {
            /** @type {?} */
            var messageFormmated = /** @type {?} */ (messageUnformated);
            // validate if the message is cyphered
            if (cypherMasterKey) {
                /** @type {?} */
                var datablockLength = new DataView(new Uint8Array(messageFormmated.slice(1, 3)).buffer).getInt16(0, false);
                _this.cypherAesService.config(cypherMasterKey);
                /** @type {?} */
                var datablock = Array.from(_this.cypherAesService.decrypt((/** @type {?} */ (messageUnformated)).slice(3, datablockLength + 3)));
                // merge the datablock and the message
                messageFormmated = (/** @type {?} */ (messageUnformated))
                    .slice(0, 3)
                    .concat(datablock)
                    .concat((/** @type {?} */ (messageUnformated)).slice(-2));
            }
            _this._console.log('[BLE::Info] Notification reived from device: ', _this.cypherAesService.bytesTohex(messageFormmated));
            return messageFormmated;
        }), 
        // filter the message using the filter options
        filter(function (message) {
            /** @type {?} */
            var availableMessage = false;
            try {
                for (var filterOptions_1 = __values(filterOptions), filterOptions_1_1 = filterOptions_1.next(); !filterOptions_1_1.done; filterOptions_1_1 = filterOptions_1.next()) {
                    var option = filterOptions_1_1.value;
                    if (message[option.position] === option.byteToMatch) {
                        availableMessage = true;
                    }
                    else {
                        availableMessage = false;
                        break;
                    }
                }
            }
            catch (e_1_1) { e_1 = { error: e_1_1 }; }
            finally {
                try {
                    if (filterOptions_1_1 && !filterOptions_1_1.done && (_a = filterOptions_1.return)) _a.call(filterOptions_1);
                }
                finally { if (e_1) throw e_1.error; }
            }
            return availableMessage;
            var e_1, _a;
        }));
    };
    /**
     * Start a request to the browser to list all available bluetooth devices
     * @param {?=} options Options to request the devices the structure is:
     * acceptAllDevices: true|false
     * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
     * optionalServices: [] (services that are going to be used in
     * communication with the device, must use the UIID or GATT identfier to list ther services)
     * @return {?}
     */
    BluetoothService.prototype.discoverDevice$ = /**
     * Start a request to the browser to list all available bluetooth devices
     * @param {?=} options Options to request the devices the structure is:
     * acceptAllDevices: true|false
     * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
     * optionalServices: [] (services that are going to be used in
     * communication with the device, must use the UIID or GATT identfier to list ther services)
     * @return {?}
     */
    function (options) {
        var _this = this;
        if (options === void 0) { options = /** @type {?} */ ({}); }
        return defer(function () { return _this._webBle.requestDevice(options); }).pipe(mergeMap(function (device) {
            _this.device = device;
            _this._device$.emit(device);
            return _this.configureDeviceDisconnection$(device).pipe(mapTo(device));
        }));
    };
    /**
     * @param {?} device
     * @return {?}
     */
    BluetoothService.prototype.configureDeviceDisconnection$ = /**
     * @param {?} device
     * @return {?}
     */
    function (device) {
        var _this = this;
        return Observable.create(function (observer) {
            of(device)
                .pipe(mergeMap(function (dev) { return fromEvent(device, 'gattserverdisconnected'); }), take(1))
                .subscribe(function () {
                _this._console.log('Se desconecta disp en OnDevice disconnected!!!!!!!');
                _this.device = null;
                _this._device$.emit(null);
            }, function (err) {
                _this._console.log('[BLE::Info] Error in notifier: ', err);
                observer.error(err);
            }, function () {
            });
            observer.next("DisconnectionEvent as been register");
        });
    };
    /**
     * Discover all available devices and connect to a selected device
     * @param options Options to request the devices the structure is:
     * acceptAllDevices: true|false
     * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
     * optionalServices: [] (services that are going to be used in
     * communication with the device, must use the UIID or GATT identfier to list ther services)
     * @returns the connected device
     */
    /**
     * Discover all available devices and connect to a selected device
     * @param {?=} options Options to request the devices the structure is:
     * acceptAllDevices: true|false
     * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
     * optionalServices: [] (services that are going to be used in
     * communication with the device, must use the UIID or GATT identfier to list ther services)
     * @return {?} the connected device
     */
    BluetoothService.prototype.connectDevice$ = /**
     * Discover all available devices and connect to a selected device
     * @param {?=} options Options to request the devices the structure is:
     * acceptAllDevices: true|false
     * filters: BluetoothDataFilterInit (see https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothlescanfilterinit for more info)
     * optionalServices: [] (services that are going to be used in
     * communication with the device, must use the UIID or GATT identfier to list ther services)
     * @return {?} the connected device
     */
    function (options) {
        if (!options) {
            options = {
                acceptAllDevices: true,
                optionalServices: []
            };
        }
        else if (!options.optionalServices) {
            options.optionalServices = [];
        }
        options.optionalServices.push(GattServices.GENERIC_ACCESS.SERVICE);
        options.optionalServices.push(GattServices.BATTERY.SERVICE);
        options.optionalServices.push(GattServices.DEVICE_INFORMATION.SERVICE);
        return this.discoverDevice$(options).pipe(mergeMap(function (device) {
            return defer(function () { return (/** @type {?} */ (device)).gatt.connect(); });
        }));
    };
    /**
     * Disconnect the current device
     */
    /**
     * Disconnect the current device
     * @return {?}
     */
    BluetoothService.prototype.disconnectDevice = /**
     * Disconnect the current device
     * @return {?}
     */
    function () {
        if (this.device) {
            this._console.log('se deconecta dispositivo');
            this.device.gatt.disconnect();
        }
    };
    /**
     * get a data from the device using the characteristic
     * @param service UUID or GATT identifier service
     * @param characteristic UUID or GATT identifier characteristic
     * @return The characteristic data in a DataView object
     */
    /**
     * get a data from the device using the characteristic
     * @param {?} service UUID or GATT identifier service
     * @param {?} characteristic UUID or GATT identifier characteristic
     * @return {?} The characteristic data in a DataView object
     */
    BluetoothService.prototype.readDeviceValue$ = /**
     * get a data from the device using the characteristic
     * @param {?} service UUID or GATT identifier service
     * @param {?} characteristic UUID or GATT identifier characteristic
     * @return {?} The characteristic data in a DataView object
     */
    function (service, characteristic) {
        var _this = this;
        if (!this.device) {
            throw new Error('Must start a connection to a device before read the device value');
        }
        return this.getPrimaryService$(service).pipe(mergeMap(function (primaryService) {
            return _this.getCharacteristic$(primaryService, characteristic);
        }), mergeMap(function (characteristicValue) {
            return _this.readValue$(characteristicValue);
        }));
    };
    /**
     * write a value in the selected characteristic
     * @param characteristic the characterisitc where you want write the value
     * @param value value the value to write
     */
    /**
     * write a value in the selected characteristic
     * @param {?} service
     * @param {?} characteristic the characterisitc where you want write the value
     * @param {?} value value the value to write
     * @return {?}
     */
    BluetoothService.prototype.writeDeviceValue$ = /**
     * write a value in the selected characteristic
     * @param {?} service
     * @param {?} characteristic the characterisitc where you want write the value
     * @param {?} value value the value to write
     * @return {?}
     */
    function (service, characteristic, value) {
        var _this = this;
        if (!this.device) {
            throw new Error('Must start a connection to a device before read the device value');
        }
        return this.getPrimaryService$(service).pipe(mergeMap(function (primaryService) {
            return _this.getCharacteristic$(primaryService, characteristic);
        }), mergeMap(function (characteristicValue) {
            return _this.writeValue$(characteristicValue, value);
        }));
    };
    /**
     * get a primary service instance using the service UIID or GATT identifier
     * @param service service identifier
     * @return service instance
     */
    /**
     * get a primary service instance using the service UIID or GATT identifier
     * @param {?} service service identifier
     * @return {?} service instance
     */
    BluetoothService.prototype.getPrimaryService$ = /**
     * get a primary service instance using the service UIID or GATT identifier
     * @param {?} service service identifier
     * @return {?} service instance
     */
    function (service) {
        return of(this.device).pipe(mergeMap(function (device) {
            return device.gatt.getPrimaryService(service);
        }));
    };
    /**
     * Get a characterisitic instance using the service instance and a characteristic UUID
     * @param primaryService service instance
     * @param characteristic characterisitic identifier
     * @return characteristic instance
     */
    /**
     * Get a characterisitic instance using the service instance and a characteristic UUID
     * @param {?} primaryService service instance
     * @param {?} characteristic characterisitic identifier
     * @return {?} characteristic instance
     */
    BluetoothService.prototype.getCharacteristic$ = /**
     * Get a characterisitic instance using the service instance and a characteristic UUID
     * @param {?} primaryService service instance
     * @param {?} characteristic characterisitic identifier
     * @return {?} characteristic instance
     */
    function (primaryService, characteristic) {
        return defer(function () { return primaryService.getCharacteristic(characteristic); });
    };
    /**
     * read the characteristic value
     * @param {?} characteristic characteristic instance
     * @return {?} The characteristic data in a DataView object
     */
    BluetoothService.prototype.readValue$ = /**
     * read the characteristic value
     * @param {?} characteristic characteristic instance
     * @return {?} The characteristic data in a DataView object
     */
    function (characteristic) {
        return from(characteristic
            .readValue()
            .then(function (data) { return Promise.resolve(data); }, function (error) { return Promise.reject("" + error.message); }));
    };
    /**
     * write a value in the selected characteristic
     * @param {?} characteristic the characterisitc where you want write the value
     * @param {?} value the value to write
     * @return {?}
     */
    BluetoothService.prototype.writeValue$ = /**
     * write a value in the selected characteristic
     * @param {?} characteristic the characterisitc where you want write the value
     * @param {?} value the value to write
     * @return {?}
     */
    function (characteristic, value) {
        return defer(function () {
            return characteristic
                .writeValue(value);
        });
    };
    /**
     * change the state of the characteristic to enable it
     * @param service  parent service of the characteristic
     * @param characteristic characteristic to change the state
     * @param state new state
     */
    /**
     * change the state of the characteristic to enable it
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?=} state new state
     * @return {?}
     */
    BluetoothService.prototype.enableCharacteristic$ = /**
     * change the state of the characteristic to enable it
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?=} state new state
     * @return {?}
     */
    function (service, characteristic, state) {
        state = state || new Uint8Array([1]);
        return this.setCharacteristicState$(service, characteristic, state);
    };
    /**
     * change the state of the characteristic to disable it
     * @param service  parent service of the characteristic
     * @param characteristic characteristic to change the state
     * @param state new state
     */
    /**
     * change the state of the characteristic to disable it
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?=} state new state
     * @return {?}
     */
    BluetoothService.prototype.disbaleCharacteristic$ = /**
     * change the state of the characteristic to disable it
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?=} state new state
     * @return {?}
     */
    function (service, characteristic, state) {
        state = state || new Uint8Array([0]);
        return this.setCharacteristicState$(service, characteristic, state);
    };
    /**
     * set a state to an specific characteristic
     * @param service  parent service of the characteristic
     * @param characteristic characteristic to change the state
     * @param state new state
     * @return
     */
    /**
     * set a state to an specific characteristic
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?} state new state
     * @return {?}
     */
    BluetoothService.prototype.setCharacteristicState$ = /**
     * set a state to an specific characteristic
     * @param {?} service  parent service of the characteristic
     * @param {?} characteristic characteristic to change the state
     * @param {?} state new state
     * @return {?}
     */
    function (service, characteristic, state) {
        var _this = this;
        /** @type {?} */
        var primaryService = this.getPrimaryService$(service);
        return primaryService.pipe(mergeMap(function (_primaryService) {
            return _this.getCharacteristic$(_primaryService, characteristic);
        }), map(function (_characteristic) {
            return _this.writeValue$(_characteristic, state);
        }));
    };
    /**
     * Send a message using a notifier characteristic
     * @param message message to send
     * @param service service to which the characteristic belongs
     * @param characteristic feature in which you want to send the notification
     */
    /**
     * Send a message using a notifier characteristic
     * @param {?} message message to send
     * @param {?} service service to which the characteristic belongs
     * @param {?} characteristic feature in which you want to send the notification
     * @return {?}
     */
    BluetoothService.prototype.sendToNotifier$ = /**
     * Send a message using a notifier characteristic
     * @param {?} message message to send
     * @param {?} service service to which the characteristic belongs
     * @param {?} characteristic feature in which you want to send the notification
     * @return {?}
     */
    function (message, service, characteristic) {
        var _this = this;
        return this.getPrimaryService$(service).pipe(mergeMap(function (primaryService) {
            return _this.getCharacteristic$(primaryService, characteristic);
        }), mergeMap(function (char) {
            if (message.length > 16) {
                /** @type {?} */
                var obsTest = of(undefined);
                while (message.length > 16) {
                    obsTest = obsTest.pipe(concat(_this.writeValue$(char, message.slice(0, 16))));
                    message = message.slice(16, message.length);
                }
                if (message.length > 0) {
                    obsTest = obsTest.pipe(concat(_this.writeValue$(char, message)));
                }
                return obsTest;
            }
            else {
                return _this.writeValue$(char, message);
            }
        }));
    };
    /**
     * The Battery Level characteristic is read using the GATT Read Characteristic
     *  Value sub-procedure and returns the current battery level as a percentage
     *  from 0% to 100%; 0% represents a battery that is fully discharged, 100%
     * represents a battery that is fully charged
     */
    /**
     * The Battery Level characteristic is read using the GATT Read Characteristic
     *  Value sub-procedure and returns the current battery level as a percentage
     *  from 0% to 100%; 0% represents a battery that is fully discharged, 100%
     * represents a battery that is fully charged
     * @return {?}
     */
    BluetoothService.prototype.getBatteryLevel$ = /**
     * The Battery Level characteristic is read using the GATT Read Characteristic
     *  Value sub-procedure and returns the current battery level as a percentage
     *  from 0% to 100%; 0% represents a battery that is fully discharged, 100%
     * represents a battery that is fully charged
     * @return {?}
     */
    function () {
        return this.readDeviceValue$(GattServices.BATTERY.SERVICE, GattServices.BATTERY.BATTERY_LEVEL).pipe(map(function (value) { return value.getUint8(0); }));
    };
    /**
     * This characteristic represents the name of the manufacturer of the device.
     */
    /**
     * This characteristic represents the name of the manufacturer of the device.
     * @return {?}
     */
    BluetoothService.prototype.getManufacturerName$ = /**
     * This characteristic represents the name of the manufacturer of the device.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.MANUFACTURER_NAME).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents the model number that is assigned by the device vendor.
     */
    /**
     * This characteristic represents the model number that is assigned by the device vendor.
     * @return {?}
     */
    BluetoothService.prototype.getModelNumber$ = /**
     * This characteristic represents the model number that is assigned by the device vendor.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.MODEL_NUMBER).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents the serial number for a particular instance of the device.
     */
    /**
     * This characteristic represents the serial number for a particular instance of the device.
     * @return {?}
     */
    BluetoothService.prototype.getSerialNumber$ = /**
     * This characteristic represents the serial number for a particular instance of the device.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SERIAL_NUMBER).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents the hardware revision for the hardware within the device.
     */
    /**
     * This characteristic represents the hardware revision for the hardware within the device.
     * @return {?}
     */
    BluetoothService.prototype.getHardwareRevision$ = /**
     * This characteristic represents the hardware revision for the hardware within the device.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.HARDWARE_REVISION).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents the firmware revision for the firmware within the device.
     */
    /**
     * This characteristic represents the firmware revision for the firmware within the device.
     * @return {?}
     */
    BluetoothService.prototype.getFirmwareRevision$ = /**
     * This characteristic represents the firmware revision for the firmware within the device.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.FIRMWARE_REVISION).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents the software revision for the software within the device.
     */
    /**
     * This characteristic represents the software revision for the software within the device.
     * @return {?}
     */
    BluetoothService.prototype.getSoftwareRevision$ = /**
     * This characteristic represents the software revision for the software within the device.
     * @return {?}
     */
    function () {
        var _this = this;
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SOFTWARE_REVISION).pipe(map(function (dataView) {
            return _this.cypherAesService.bytesToText(new Uint8Array(dataView.buffer));
        }));
    };
    /**
     * This characteristic represents a structure containing an Organizationally Unique Identifier
     *  (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
     */
    /**
     * This characteristic represents a structure containing an Organizationally Unique Identifier
     *  (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
     * @return {?}
     */
    BluetoothService.prototype.getSystemId$ = /**
     * This characteristic represents a structure containing an Organizationally Unique Identifier
     *  (OUI) followed by a manufacturer-defined identifier and is unique for each individual instance of the product.
     * @return {?}
     */
    function () {
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.SYSTEM_ID);
    };
    /**
     * The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
     */
    /**
     * The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
     * @return {?}
     */
    BluetoothService.prototype.getPnpId$ = /**
     * The PnP_ID characteristic is a set of values used to create a device ID value that is unique for this device.
     * @return {?}
     */
    function () {
        return this.readDeviceValue$(GattServices.DEVICE_INFORMATION.SERVICE, GattServices.DEVICE_INFORMATION.PNP_ID);
    };
    BluetoothService.decorators = [
        { type: Injectable, args: [{
                    providedIn: 'root'
                },] },
    ];
    /** @nocollapse */
    BluetoothService.ctorParameters = function () { return [
        { type: BrowserWebBluetooth },
        { type: CypherAesService },
        { type: ConsoleLoggerService }
    ]; };
    /** @nocollapse */ BluetoothService.ngInjectableDef = defineInjectable({ factory: function BluetoothService_Factory() { return new BluetoothService(inject(BrowserWebBluetooth), inject(CypherAesService), inject(ConsoleLoggerService)); }, token: BluetoothService, providedIn: "root" });
    return BluetoothService;
}(Subject));

/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
 */
/**
 * @return {?}
 */
function browserWebBluetooth() {
    return new BrowserWebBluetooth();
}
/**
 * @param {?} options
 * @return {?}
 */
function consoleLoggerServiceConfig(options) {
    if (options && options.enableTracing) {
        return new ConsoleLoggerService();
    }
    else {
        return new NoLoggerService();
    }
}
/**
 * @return {?}
 */
function makeMeTokenInjector() {
    return new InjectionToken('AWBOptions');
}
var AngularBleModule = /** @class */ (function () {
    function AngularBleModule() {
    }
    /**
     * @param {?=} options
     * @return {?}
     */
    AngularBleModule.forRoot = /**
     * @param {?=} options
     * @return {?}
     */
    function (options) {
        if (options === void 0) { options = {}; }
        return {
            ngModule: AngularBleModule,
            providers: [
                BluetoothService,
                {
                    provide: BrowserWebBluetooth,
                    useFactory: browserWebBluetooth
                },
                {
                    provide: makeMeTokenInjector,
                    useValue: options
                },
                {
                    provide: ConsoleLoggerService,
                    useFactory: consoleLoggerServiceConfig,
                    deps: [makeMeTokenInjector]
                }
            ]
        };
    };
    AngularBleModule.decorators = [
        { type: NgModule, args: [{
                    imports: [CommonModule]
                },] },
    ];
    return AngularBleModule;
}());

/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
 */

/**
 * @fileoverview added by tsickle
 * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
 */

export { BrowserWebBluetooth, browserWebBluetooth, consoleLoggerServiceConfig, makeMeTokenInjector, AngularBleModule, CypherAesService, BluetoothService, ConsoleLoggerService as ɵa };

//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmVidWxhZS1hbmd1bGFyLWJsZS5qcy5tYXAiLCJzb3VyY2VzIjpbIm5nOi8vQG5lYnVsYWUvYW5ndWxhci1ibGUvbGliL3BsYXRmb3JtL2Jyb3dzZXIudHMiLCJuZzovL0BuZWJ1bGFlL2FuZ3VsYXItYmxlL2xpYi9ibHVldG9vdGgvZ2F0dC1zZXJ2aWNlcy50cyIsIm5nOi8vQG5lYnVsYWUvYW5ndWxhci1ibGUvbGliL2N5cGhlci9jeXBoZXItYWVzLnNlcnZpY2UudHMiLCJuZzovL0BuZWJ1bGFlL2FuZ3VsYXItYmxlL2xpYi9sb2dnZXIuc2VydmljZS50cyIsIm5nOi8vQG5lYnVsYWUvYW5ndWxhci1ibGUvbGliL2JsdWV0b290aC9ibHVldG9vdGguc2VydmljZS50cyIsIm5nOi8vQG5lYnVsYWUvYW5ndWxhci1ibGUvbGliL2FuZ3VsYXItYmxlLm1vZHVsZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBCbHVldG9vdGhTZXJ2aWNlIH0gZnJvbSAnLi4vYmx1ZXRvb3RoL2JsdWV0b290aC5zZXJ2aWNlJztcblxuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIEJyb3dzZXJXZWJCbHVldG9vdGgge1xuICBwdWJsaWMgX2JsZTtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLl9ibGUgPSBuYXZpZ2F0b3IuYmx1ZXRvb3RoO1xuICAgIGlmICghdGhpcy5fYmxlKSB7XG4gICAgICBjb25zb2xlLmxvZygnZXJyb3IgY2FyZ2FuZG8gYmx1ZXRvb3RoJyk7XG4gICAgICAvLyBibHVldG9vdGhTZXJ2aWNlLnNldEJsdWV0b290aEF2YWlsYWJsZShmYWxzZSk7XG4gICAgICAvLyB0aHJvdyBuZXcgRXJyb3IoJ1lvdXIgYnJvd3NlciBkb2VzIG5vdCBzdXBwb3J0IFNtYXJ0IEJsdWV0b290aC4gU2VlIGh0dHA6Ly9jYW5pdXNlLmNvbS8jc2VhcmNoPUJsdWV0b290aCBmb3IgbW9yZSBkZXRhaWxzLicpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBibHVldG9vdGhTZXJ2aWNlLnNldEJsdWV0b290aEF2YWlsYWJsZSh0cnVlKTtcbiAgICB9XG4gIH1cblxuICByZXF1ZXN0RGV2aWNlKG9wdGlvbnM6IFJlcXVlc3REZXZpY2VPcHRpb25zKTogUHJvbWlzZTxCbHVldG9vdGhEZXZpY2U+IHtcbiAgICByZXR1cm4gdGhpcy5fYmxlLnJlcXVlc3REZXZpY2Uob3B0aW9ucyk7XG4gIH1cbn1cbiIsImV4cG9ydCBjb25zdCBHYXR0U2VydmljZXMgPSB7XG4gIEdFTkVSSUNfQUNDRVNTOiB7XG4gICAgU0VSVklDRTogJ2dlbmVyaWNfYWNjZXNzJyxcbiAgICBERVZJQ0VfTkFNRTogJ2RldmljZV9uYW1lJyxcbiAgICBBUFBFQVJBTkNFOiAnYXBwZWFyYW5jZScsXG4gICAgUFJJVkFDWV9GTEFHOiAncHJpdmFjeV9mbGFnJyxcbiAgICBSRUNPTk5FQ1RJT05fQUREUkVTUzogJ3JlY29ubmVjdGlvbl9hZGRyZXNzJyxcbiAgICBQRVJJUEhFUkFMX1BSRUZFUlJFRF9DT05ORUNUSU9OX1BBUkFNRVRFUlM6ICdwZXJpcGhlcmFsX3ByZWZlcnJlZF9jb25uZWN0aW9uX3BhcmFtZXRlcnMnXG4gIH0sXG4gIEJBVFRFUlk6IHtcbiAgICBTRVJWSUNFOiAnYmF0dGVyeV9zZXJ2aWNlJyxcbiAgICBCQVRURVJZX0xFVkVMOiAnYmF0dGVyeV9sZXZlbCdcbiAgfSxcbiAgREVWSUNFX0lORk9STUFUSU9OOiB7XG4gICAgU0VSVklDRTogJ2RldmljZV9pbmZvcm1hdGlvbicsXG4gICAgTUFOVUZBQ1RVUkVSX05BTUU6ICdtYW51ZmFjdHVyZXJfbmFtZV9zdHJpbmcnLFxuICAgIE1PREVMX05VTUJFUjogJ21vZGVsX251bWJlcl9zdHJpbmcnLFxuICAgIFNFUklBTF9OVU1CRVI6ICdzZXJpYWxfbnVtYmVyX3N0cmluZycsXG4gICAgSEFSRFdBUkVfUkVWSVNJT046ICdoYXJkd2FyZV9yZXZpc2lvbl9zdHJpbmcnLFxuICAgIEZJUk1XQVJFX1JFVklTSU9OOiAnZmlybXdhcmVfcmV2aXNpb25fc3RyaW5nJyxcbiAgICBTT0ZUV0FSRV9SRVZJU0lPTjogJ3NvZnR3YXJlX3JldmlzaW9uX3N0cmluZycsXG4gICAgU1lTVEVNX0lEOiAnc3lzdGVtX2lkJyxcbiAgICBQTlBfSUQ6ICdwbnBfaWQnXG4gIH1cbn07XG5cbmV4cG9ydCBjb25zdCBHQVRUX1NFUlZJQ0VTID0gT2JqZWN0LmtleXMoR2F0dFNlcnZpY2VzKS5tYXAoXG4gIGtleSA9PiBHYXR0U2VydmljZXNba2V5XS5TRVJWSUNFXG4pO1xuIiwiZGVjbGFyZSBjb25zdCBCdWZmZXI7XG5pbXBvcnQgKiBhcyBhZXMgZnJvbSAnYWVzLWpzJztcbmltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgQ3lwaGVyQWVzU2VydmljZSB7XG4gIHByaXZhdGUgbWFzdGVyS2V5ID0gW107XG4gIHByaXZhdGUgaW5pdGlhbFZlY3RvciA9IFtdO1xuICBwcml2YXRlIGVuY3J5cHRNZXRob2QgPSAnQ0JDJztcbiAgcHJpdmF0ZSBpc1N0YXRpY0luaXRpYWxWZWN0b3IgPSB0cnVlO1xuICBwcml2YXRlIGVuY3RyeXBNZXRob2RJbnN0YW5jZTtcbiAgcHJpdmF0ZSBpc0NvbmZpZ0V4ZWN1dGVkID0gZmFsc2U7XG4gIHByaXZhdGUgYWRkaXRpb25hbEVuY3J5cHRNZXRob2RQYXJhbXM7XG4gIGNvbnN0cnVjdG9yKCkge31cbiAgLyoqXG4gICAqIEluaXRpYWwgY29uZmlnIHVzZWQgdG8gaW5pdGFsaWNlIGFsbCByZXF1aXJlZCBwYXJhbXNcbiAgICogQHBhcmFtIG1hc3RlcktleSBrZXkgdXNlZCB0byBlbmNyeXB0IGFuZCBkZWNyeXB0XG4gICAqIEBwYXJhbSBpbml0aWFsVmVjdG9yIHZlY3RvciB1c2VkIHRvIGVuY3J5cHQgYWJkIGRlY3J5cHQgZXhjZXB0IHdoZW4gRUNCIGVuY3J5cHQgbWV0aG9kIGlzIHVzZWRcbiAgICogQHBhcmFtIGVuY3J5cHRNZXRob2QgdHlwZSBvZiBlbmNyeXB0IG1ldGhvZCBpcyB1c2VkLCB0aGUgcG9zc2libGUgb3B0aW9ucyBhcmU6IENCQywgQ1RSLCBDRkIsIE9GQiwgRUNCXG4gICAqIEBwYXJhbSBhZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcyBjb25maWd1cmF0aW9uIHBhcmFtcyB1c2VkIGJ5IHRoZSBzZWxlY3RlZCBlbmNyeXB0IG1ldGhvZC5cbiAgICogTm90ZTogaWYgdGhlIG1ldGhvZCBDVFIgb3IgQ0ZCIGlzIHVzZWQgdGhpcyBwYXJhbSBpcyByZXF1aXJlZCBvdGhlcndpc2UgaXMgYW4gb3B0aW5hbCBwYXJhbS5cbiAgICogQnkgQ1RSIHJlcXVpcmUgdGhlIHBhcmFtIGNvdW50ZXIgYW5kIGJ5IENGQiByZXF1aXJlIHRoZSBwYXJhbSBzZWdtZW50U2l6ZVxuICAgKiBAcGFyYW0gaXNTdGF0aWNJbml0aWFsVmVjdG9yIGRlZmluZXMgaWYgdGhlIGluaXRpYWwgdmVjdG9yIGlzIGNoYW5nZWQgb3Igbm90IHdoZW4gdGhlIGRhdGEgYXJlIGVuY3J5cHRlZCBvciBub3RcbiAgICovXG4gIGNvbmZpZyhcbiAgICBtYXN0ZXJLZXksXG4gICAgaW5pdGlhbFZlY3RvciA9IFswLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwXSxcbiAgICBlbmNyeXB0TWV0aG9kID0gJ0NCQycsXG4gICAgYWRkaXRpb25hbEVuY3J5cHRNZXRob2RQYXJhbXMgPSB7fSxcbiAgICBpc1N0YXRpY0luaXRpYWxWZWN0b3IgPSB0cnVlXG4gICkge1xuICAgIHRoaXMuaXNDb25maWdFeGVjdXRlZCA9IHRydWU7XG4gICAgdGhpcy5tYXN0ZXJLZXkgPSBtYXN0ZXJLZXk7XG4gICAgdGhpcy5pbml0aWFsVmVjdG9yID0gaW5pdGlhbFZlY3RvcjtcbiAgICB0aGlzLmVuY3J5cHRNZXRob2QgPSBlbmNyeXB0TWV0aG9kO1xuICAgIHRoaXMuaXNTdGF0aWNJbml0aWFsVmVjdG9yID0gaXNTdGF0aWNJbml0aWFsVmVjdG9yO1xuICAgIHRoaXMuYWRkaXRpb25hbEVuY3J5cHRNZXRob2RQYXJhbXMgPSBhZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcztcbiAgICBpZiAoIWlzU3RhdGljSW5pdGlhbFZlY3Rvcikge1xuICAgICAgdGhpcy5lbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSB0aGlzLmdlbmVyYXRlRW5jcnlwdE1ldGhvZEluc3RhbmNlKCk7XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiBFbmNyeXB0IHRoZSBkYXRhIHVzaW5nIHRoZSBlbmNyeXB0IG1ldGhvZCBwcmV2aW91c2x5IGNvbmZpZ3VyZWRcbiAgICogQHBhcmFtIGRhdGFBcnJheUJ1ZmZlciBkYXRhIHRvIGVuY3J5cHRcbiAgICovXG4gIGVuY3J5cHQoZGF0YUFycmF5QnVmZmVyOiBVaW50OEFycmF5IHwgVWludDE2QXJyYXkgfCBVaW50MzJBcnJheSkge1xuICAgIGlmICghdGhpcy5pc0NvbmZpZ0V4ZWN1dGVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdNdXN0IGNvbmZpZ3VyYXRlIGN5cGhlci1hZXMgYmVmb3JlIGNhbGwgdGhpcyBtZXRob2QsIHVzZSB0aGUgbWV0aG9kIGNvbmZpZygpJ1xuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuZW5jcnlwdE1ldGhvZCA9PT0gJ0NCQycgfHwgdGhpcy5lbmNyeXB0TWV0aG9kID09PSAnRUNCJykge1xuICAgICAgZGF0YUFycmF5QnVmZmVyID0gdGhpcy5hZGRQYWRkaW5nKGRhdGFBcnJheUJ1ZmZlcik7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmlzU3RhdGljSW5pdGlhbFZlY3RvclxuICAgICAgPyB0aGlzLmdlbmVyYXRlRW5jcnlwdE1ldGhvZEluc3RhbmNlKCkuZW5jcnlwdChkYXRhQXJyYXlCdWZmZXIpXG4gICAgICA6IHRoaXMuZW5jdHJ5cE1ldGhvZEluc3RhbmNlLmVuY3J5cHQoZGF0YUFycmF5QnVmZmVyKTtcbiAgfVxuICAvKipcbiAgICogRGVjcnlwdCB0aGUgZGF0YSB1c2luZyB0aGUgZW5jcnlwdCBtZXRob2QgcHJldmlvdXNseSBjb25maWd1cmVkXG4gICAqIEBwYXJhbSBkYXRhQXJyYXlCdWZmZXIgZGF0YSB0byBkZWNyeXB0XG4gICAqL1xuICBkZWNyeXB0KGRhdGFBcnJheUJ1ZmZlcjogVWludDhBcnJheSB8IFVpbnQxNkFycmF5IHwgVWludDMyQXJyYXkpIHtcbiAgICBpZiAoIXRoaXMuaXNDb25maWdFeGVjdXRlZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnTXVzdCBjb25maWd1cmF0ZSBjeXBoZXItYWVzIGJlZm9yZSBjYWxsIHRoaXMgbWV0aG9kLCB1c2UgdGhlIG1ldGhvZCBjb25maWcoKSdcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmlzU3RhdGljSW5pdGlhbFZlY3RvclxuICAgICAgPyB0aGlzLmdlbmVyYXRlRW5jcnlwdE1ldGhvZEluc3RhbmNlKCkuZGVjcnlwdChkYXRhQXJyYXlCdWZmZXIpXG4gICAgICA6IHRoaXMuZW5jdHJ5cE1ldGhvZEluc3RhbmNlLmRlY3J5cHQoZGF0YUFycmF5QnVmZmVyKTtcbiAgfVxuICAvKipcbiAgICogQ2hhbmdlIHRoZSBjdXJyZW50IGluaXRhbFZlY3RvclxuICAgKiBAcGFyYW0gaW5pdGlhbFZlY3RvciBuZXcgaW5pdGFsVmVjdG9yXG4gICAqL1xuICBjaGFuZ2VJbml0aWFsVmVjdG9yKGluaXRpYWxWZWN0b3IpIHtcbiAgICBpZiAoIXRoaXMuaXNTdGF0aWNJbml0aWFsVmVjdG9yKSB7XG4gICAgICB0aGlzLmVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKTtcbiAgICB9XG4gICAgdGhpcy5pbml0aWFsVmVjdG9yID0gaW5pdGlhbFZlY3RvcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGFuZ2UgdGhlIGN1cnJlbnQgZW5jeXB0TWV0aG9kXG4gICAqIEBwYXJhbSBlbmNyeXB0TWV0aG9kIG5ldyBlbmNyeXB0TWV0aG9kXG4gICAqL1xuICBjaGFuZ2VFbmNyeXB0TWV0aG9kKGVuY3J5cHRNZXRob2QpIHtcbiAgICBpZiAoIXRoaXMuaXNTdGF0aWNJbml0aWFsVmVjdG9yKSB7XG4gICAgICB0aGlzLmVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKTtcbiAgICB9XG4gICAgdGhpcy5lbmNyeXB0TWV0aG9kID0gZW5jcnlwdE1ldGhvZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGFuZ2UgdGhlIGN1cnJlbnQgaXNTdGF0aWNJbml0aWFsVmVjdG9yXG4gICAqIEBwYXJhbSBpc1N0YXRpY0luaXRpYWxWZWN0b3IgbmV3IGlzU3RhdGljSW5pdGFsVmVjdG9yXG4gICAqL1xuICBjaGFuZ2VTdGF0aWNJbml0aWFsVmVjdG9yKGlzU3RhdGljSW5pdGlhbFZlY3Rvcikge1xuICAgIGlmICghaXNTdGF0aWNJbml0aWFsVmVjdG9yKSB7XG4gICAgICB0aGlzLmVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKTtcbiAgICB9XG4gICAgdGhpcy5pc1N0YXRpY0luaXRpYWxWZWN0b3IgPSBpc1N0YXRpY0luaXRpYWxWZWN0b3I7XG4gIH1cbiAgLyoqXG4gICAqIENoYW5nZSB0aGUgY3VycmVudCBtYXN0ZXJLZXlcbiAgICogQHBhcmFtIG1hc3RlcktleSBuZXcgbWFzdGVyS2V5XG4gICAqL1xuICBjaGFuZ2VNYXN0ZXJLZXkobWFzdGVyS2V5KSB7XG4gICAgaWYgKCF0aGlzLmlzU3RhdGljSW5pdGlhbFZlY3Rvcikge1xuICAgICAgdGhpcy5lbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSB0aGlzLmdlbmVyYXRlRW5jcnlwdE1ldGhvZEluc3RhbmNlKCk7XG4gICAgfVxuICAgIHRoaXMubWFzdGVyS2V5ID0gbWFzdGVyS2V5O1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBwYWRkaW5nIHRvIHRoZSBsaXN0XG4gICAqL1xuICBwcml2YXRlIGFkZFBhZGRpbmcoYXJyYXlCdWZmZXI6IFVpbnQ4QXJyYXkgfCBVaW50MTZBcnJheSB8IFVpbnQzMkFycmF5KSB7XG4gICAgY29uc3QgcGFkZGluZ0xlbmd0aCA9IE1hdGguY2VpbChBcnJheS5mcm9tKGFycmF5QnVmZmVyKS5sZW5ndGggLyAxNikgKiAxNjtcbiAgICBjb25zdCBwYWRkaW5nTGlzdCA9IG5ldyBBcnJheShcbiAgICAgIHBhZGRpbmdMZW5ndGggLSBBcnJheS5mcm9tKGFycmF5QnVmZmVyKS5sZW5ndGhcbiAgICApLmZpbGwoMCk7XG4gICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KEFycmF5LmZyb20oYXJyYXlCdWZmZXIpLmNvbmNhdChwYWRkaW5nTGlzdCkpO1xuICB9XG4gIC8qKlxuICAgKiBnZW5lcmF0ZSBhIGluc3RhbmNlIG9mIHRoZSBlbmNyeXB0IG1ldGhvZCB1c2luZyB0aGUgY3VycmVudCBtZXRob2QgY29uZmlndXJlZFxuICAgKi9cbiAgcHJpdmF0ZSBnZW5lcmF0ZUVuY3J5cHRNZXRob2RJbnN0YW5jZSgpIHtcbiAgICBsZXQgZW5jdHJ5cE1ldGhvZEluc3RhbmNlO1xuICAgIHN3aXRjaCAodGhpcy5lbmNyeXB0TWV0aG9kKSB7XG4gICAgICBjYXNlICdDQkMnOlxuICAgICAgICBlbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSBuZXcgYWVzLk1vZGVPZk9wZXJhdGlvbi5jYmMoXG4gICAgICAgICAgdGhpcy5tYXN0ZXJLZXksXG4gICAgICAgICAgdGhpcy5pbml0aWFsVmVjdG9yXG4gICAgICAgICk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnQ1RSJzpcbiAgICAgICAgaWYgKCF0aGlzLmFkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zLmNvdW50ZXIpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAnYWRkaXRpb25hbEVuY3J5cHRNZXRob2RQYXJhbXMuY291bnRlciBpcyByZXF1aXJlZCB0byB1c2UgZW5jcnlwdCBtZXRob2QgQ1RSJ1xuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gbmV3IGFlcy5Nb2RlT2ZPcGVyYXRpb24uY3RyKFxuICAgICAgICAgIHRoaXMubWFzdGVyS2V5LFxuICAgICAgICAgIHRoaXMuaW5pdGlhbFZlY3RvcixcbiAgICAgICAgICBuZXcgYWVzLkNvdW50ZXIodGhpcy5hZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcy5jb3VudGVyKVxuICAgICAgICApO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ0NGQic6XG4gICAgICAgIGlmICghdGhpcy5hZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcy5zZWdtZW50U2l6ZSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICdhZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcy5zZWdtZW50U2l6ZSBpcyByZXF1aXJlZCB0byB1c2UgZW5jcnlwdCBtZXRob2QgQ0ZCJ1xuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gbmV3IGFlcy5Nb2RlT2ZPcGVyYXRpb24uY2ZiKFxuICAgICAgICAgIHRoaXMubWFzdGVyS2V5LFxuICAgICAgICAgIHRoaXMuaW5pdGlhbFZlY3RvcixcbiAgICAgICAgICB0aGlzLmFkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zLnNlZ21lbnRTaXplXG4gICAgICAgICk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnT0ZCJzpcbiAgICAgICAgZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gbmV3IGFlcy5Nb2RlT2ZPcGVyYXRpb24ub2ZiKFxuICAgICAgICAgIHRoaXMubWFzdGVyS2V5LFxuICAgICAgICAgIHRoaXMuaW5pdGlhbFZlY3RvclxuICAgICAgICApO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ0VDQic6XG4gICAgICAgIGVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IG5ldyBhZXMuTW9kZU9mT3BlcmF0aW9uLmVjYih0aGlzLm1hc3RlcktleSk7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgICByZXR1cm4gZW5jdHJ5cE1ldGhvZEluc3RhbmNlO1xuICB9XG4gIC8vICNyZWdpb24gVVRJTFNcbiAgLyoqXG4gICAqIENvbnZlcnQgdGhlIHRleHQgdG8gYnl0ZXNcbiAgICogQHBhcmFtIHRleHQgVGV4dCB0byBjb252ZXJ0XG4gICAqL1xuICB0ZXh0VG9CeXRlcyh0ZXh0KSB7XG4gICAgcmV0dXJuIGFlcy51dGlscy51dGY4LnRvQnl0ZXModGV4dCk7XG4gIH1cbiAgLyoqXG4gICAqIENvbnZlcnQgdGhlIGJ5dGVzIHRvIHRleHRcbiAgICogQHBhcmFtIGJ5dGVzIEJ5dGVzIHRvIGNvbnZlcnRcbiAgICovXG4gIGJ5dGVzVG9UZXh0KGJ5dGVzOiBVaW50OEFycmF5IHwgVWludDE2QXJyYXkgfCBVaW50MzJBcnJheSkge1xuICAgIHJldHVybiBhZXMudXRpbHMudXRmOC5mcm9tQnl0ZXMoYnl0ZXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbnZlcnQgdGhlIGJ5dGVzIHRvIGhleFxuICAgKiBAcGFyYW0gYnl0ZXMgYnl0ZXMgdG8gY29udmVydFxuICAgKi9cbiAgYnl0ZXNUb2hleChieXRlcykge1xuICAgIHJldHVybiBhZXMudXRpbHMuaGV4LmZyb21CeXRlcyhieXRlcyk7XG4gIH1cblxuICAvKipcbiAgICogQ29udmVydCB0aGUgaGV4IHRvIGJ5dGVzXG4gICAqIEBwYXJhbSBoZXggSGV4IHRvIGNvbnZlcnRcbiAgICovXG4gIGhleFRvQnl0ZXMoaGV4KSB7XG4gICAgcmV0dXJuIGFlcy51dGlscy5oZXgudG9CeXRlcyhoZXgpO1xuICB9XG4gIC8vICNlbmRyZWdpb25cblxuICBnZW5lcmF0ZVN1YmtleXMoa2V5KSB7XG4gICAgY29uc3QgY29uc3RfWmVybyA9IG5ldyBVaW50OEFycmF5KDE2KTtcbiAgICBjb25zdCBjb25zdF9SYiA9IG5ldyBCdWZmZXIoJzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDg3JywgJ2hleCcpO1xuICAgIGNvbnN0IGVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IG5ldyBhZXMuTW9kZU9mT3BlcmF0aW9uLmNiYyhrZXksIG5ldyBVaW50OEFycmF5KDE2KSk7XG5cbiAgICBjb25zdCBsRW5jcnlwdGVkID0gZW5jdHJ5cE1ldGhvZEluc3RhbmNlLmVuY3J5cHQoY29uc3RfWmVybyk7XG4gICAgY29uc3QgbCA9IG5ldyBCdWZmZXIodGhpcy5ieXRlc1RvaGV4KGxFbmNyeXB0ZWQpLCAnaGV4Jyk7XG4gICAgbGV0IHN1YmtleTEgPSB0aGlzLmJpdFNoaWZ0TGVmdChsKTtcbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYml0d2lzZVxuICAgIGlmIChsWzBdICYgMHg4MCkge1xuICAgICAgc3Via2V5MSA9IHRoaXMueG9yKHN1YmtleTEsIGNvbnN0X1JiKTtcbiAgICB9XG5cbiAgICBsZXQgc3Via2V5MiA9IHRoaXMuYml0U2hpZnRMZWZ0KHN1YmtleTEpO1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1iaXR3aXNlXG4gICAgaWYgKHN1YmtleTFbMF0gJiAweDgwKSB7XG4gICAgICBzdWJrZXkyID0gdGhpcy54b3Ioc3Via2V5MiwgY29uc3RfUmIpO1xuICAgIH1cblxuICAgIHJldHVybiB7IHN1YmtleTE6IHN1YmtleTEsIHN1YmtleTI6IHN1YmtleTIgfTtcbiAgfVxuXG4gIGFlc0NtYWMoa2V5LCBtZXNzYWdlKSB7XG4gICAgY29uc29sZS5sb2coJ0lOSUNJQSBDSUZSQURPISEhISEhISEhISEhISEhIScpO1xuICAgIGNvbnN0IHN1YmtleXMgPSB0aGlzLmdlbmVyYXRlU3Via2V5cyhrZXkpO1xuICAgIGxldCBibG9ja0NvdW50ID0gTWF0aC5jZWlsKG1lc3NhZ2UubGVuZ3RoIC8gMTYpO1xuICAgIGxldCBsYXN0QmxvY2tDb21wbGV0ZUZsYWcsIGxhc3RCbG9jaywgbGFzdEJsb2NrSW5kZXg7XG5cbiAgICBpZiAoYmxvY2tDb3VudCA9PT0gMCkge1xuICAgICAgYmxvY2tDb3VudCA9IDE7XG4gICAgICBsYXN0QmxvY2tDb21wbGV0ZUZsYWcgPSBmYWxzZTtcbiAgICB9IGVsc2Uge1xuICAgICAgbGFzdEJsb2NrQ29tcGxldGVGbGFnID0gbWVzc2FnZS5sZW5ndGggJSAxNiA9PT0gMDtcbiAgICB9XG4gICAgbGFzdEJsb2NrSW5kZXggPSBibG9ja0NvdW50IC0gMTtcblxuICAgIGlmIChsYXN0QmxvY2tDb21wbGV0ZUZsYWcpIHtcbiAgICAgIGxhc3RCbG9jayA9IHRoaXMueG9yKFxuICAgICAgICB0aGlzLmdldE1lc3NhZ2VCbG9jayhtZXNzYWdlLCBsYXN0QmxvY2tJbmRleCksXG4gICAgICAgIHN1YmtleXMuc3Via2V5MVxuICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgbGFzdEJsb2NrID0gdGhpcy54b3IoXG4gICAgICAgIHRoaXMuZ2V0UGFkZGVkTWVzc2FnZUJsb2NrKG1lc3NhZ2UsIGxhc3RCbG9ja0luZGV4KSxcbiAgICAgICAgc3Via2V5cy5zdWJrZXkyXG4gICAgICApO1xuICAgIH1cblxuICAgIGxldCB4ID0gbmV3IEJ1ZmZlcignMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAnLCAnaGV4Jyk7XG4gICAgbGV0IHk7XG5cbiAgICBsZXQgZW5jdHJ5cE1ldGhvZEluc3RhbmNlO1xuICAgIGZvciAobGV0IGluZGV4ID0gMDsgaW5kZXggPCBsYXN0QmxvY2tJbmRleDsgaW5kZXgrKykge1xuICAgICAgZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gbmV3IGFlcy5Nb2RlT2ZPcGVyYXRpb24uY2JjKGtleSwgbmV3IFVpbnQ4QXJyYXkoMTYpKTtcbiAgICAgIHkgPSB0aGlzLnhvcih4LCB0aGlzLmdldE1lc3NhZ2VCbG9jayhtZXNzYWdlLCBpbmRleCkpO1xuICAgICAgY29uc3QgeEVuY3J5cHRlZCA9IGVuY3RyeXBNZXRob2RJbnN0YW5jZS5lbmNyeXB0KHkpO1xuICAgICAgY29uc29sZS5sb2coJ1ggbm9ybWFsID09PT09PT09PT09PT09PT4gJywgdGhpcy5ieXRlc1RvaGV4KHkpKTtcbiAgICAgIGNvbnNvbGUubG9nKCdYIGVuY3J5cHRlZCA9PT09PT09PT09PT09PT4gJywgdGhpcy5ieXRlc1RvaGV4KHhFbmNyeXB0ZWQpKTtcbiAgICAgIHggPSBuZXcgQnVmZmVyKHRoaXMuYnl0ZXNUb2hleCh4RW5jcnlwdGVkKSwgJ2hleCcpO1xuICAgIH1cbiAgICB5ID0gdGhpcy54b3IobGFzdEJsb2NrLCB4KTtcbiAgICBlbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSBuZXcgYWVzLk1vZGVPZk9wZXJhdGlvbi5jYmMoa2V5LCBuZXcgVWludDhBcnJheSgxNikpO1xuICAgIGNvbnN0IHlFbmNyeXB0ZWQgPSBlbmN0cnlwTWV0aG9kSW5zdGFuY2UuZW5jcnlwdCh5KTtcbiAgICBjb25zb2xlLmxvZygnWSBub3JtYWwgPT09PT09PT09PT09PT0+ICcsIHRoaXMuYnl0ZXNUb2hleCh5KSk7XG4gICAgY29uc29sZS5sb2coJ1kgZW5jcnlwdGVkID09PT09PT09PT09PT09PiAnLCB0aGlzLmJ5dGVzVG9oZXgoeUVuY3J5cHRlZCkpO1xuICAgIHJldHVybiB5RW5jcnlwdGVkO1xuICB9XG5cbiAgZ2V0TWVzc2FnZUJsb2NrKG1lc3NhZ2UsIGJsb2NrSW5kZXgpIHtcbiAgICBjb25zdCBibG9jayA9IG5ldyBCdWZmZXIoMTYpO1xuICAgIGNvbnN0IHN0YXJ0ID0gYmxvY2tJbmRleCAqIDE2O1xuICAgIGNvbnN0IGVuZCA9IHN0YXJ0ICsgMTY7XG4gICAgbGV0IGJsb2NrSSA9IDA7XG4gICAgZm9yIChsZXQgaSA9IHN0YXJ0OyBpIDwgZW5kOyBpKyspIHtcbiAgICAgIGJsb2NrW2Jsb2NrSV0gPSBtZXNzYWdlW2ldO1xuICAgICAgYmxvY2tJKys7XG4gICAgfVxuICAgIHJldHVybiBibG9jaztcbiAgfVxuXG4gIGdldFBhZGRlZE1lc3NhZ2VCbG9jayhtZXNzYWdlLCBibG9ja0luZGV4KSB7XG4gICAgY29uc3QgYmxvY2sgPSBuZXcgQnVmZmVyKDE2KTtcbiAgICBjb25zdCBzdGFydCA9IGJsb2NrSW5kZXggKiAxNjtcbiAgICBjb25zdCBlbmQgPSBtZXNzYWdlLmxlbmd0aDtcblxuICAgIGJsb2NrLmZpbGwoMCk7XG4gICAgbGV0IGJsb2NrSSA9IDA7XG4gICAgZm9yIChsZXQgaSA9IHN0YXJ0OyBpIDwgZW5kOyBpKyspIHtcbiAgICAgIGJsb2NrW2Jsb2NrSV0gPSBtZXNzYWdlW2ldO1xuICAgICAgYmxvY2tJKys7XG4gICAgfVxuICAgIGJsb2NrW2VuZCAtIHN0YXJ0XSA9IDB4ODA7XG5cbiAgICByZXR1cm4gYmxvY2s7XG4gIH1cblxuICBiaXRTaGlmdExlZnQoYnVmZmVyKSB7XG4gICAgY29uc3Qgc2hpZnRlZCA9IG5ldyBCdWZmZXIoYnVmZmVyLmxlbmd0aCk7XG4gICAgY29uc3QgbGFzdCA9IGJ1ZmZlci5sZW5ndGggLSAxO1xuICAgIGZvciAobGV0IGluZGV4ID0gMDsgaW5kZXggPCBsYXN0OyBpbmRleCsrKSB7XG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYml0d2lzZVxuICAgICAgc2hpZnRlZFtpbmRleF0gPSBidWZmZXJbaW5kZXhdIDw8IDE7XG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYml0d2lzZVxuICAgICAgaWYgKGJ1ZmZlcltpbmRleCArIDFdICYgMHg4MCkge1xuICAgICAgICBzaGlmdGVkW2luZGV4XSArPSAweDAxO1xuICAgICAgfVxuICAgIH1cbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYml0d2lzZVxuICAgIHNoaWZ0ZWRbbGFzdF0gPSBidWZmZXJbbGFzdF0gPDwgMTtcbiAgICByZXR1cm4gc2hpZnRlZDtcbiAgfVxuXG4gIHhvcihidWZmZXJBLCBidWZmZXJCKSB7XG4gICAgY29uc3QgbGVuZ3RoID0gTWF0aC5taW4oYnVmZmVyQS5sZW5ndGgsIGJ1ZmZlckIubGVuZ3RoKTtcbiAgICBjb25zdCBvdXRwdXQgPSBuZXcgQnVmZmVyKGxlbmd0aCk7XG4gICAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IGxlbmd0aDsgaW5kZXgrKykge1xuICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWJpdHdpc2VcbiAgICAgIG91dHB1dFtpbmRleF0gPSBidWZmZXJBW2luZGV4XSBeIGJ1ZmZlckJbaW5kZXhdO1xuICAgIH1cbiAgICByZXR1cm4gb3V0cHV0O1xuICB9XG59XG4iLCJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTG9nZ2VyIHtcbiAgbG9nKGFyZ3M6IHN0cmluZ1tdKTogdm9pZDtcbiAgZXJyb3IoYXJnczogc3RyaW5nW10pOiB2b2lkO1xuICB3YXJuKGFyZ3M6IHN0cmluZ1tdKTogdm9pZDtcbn1cblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgQ29uc29sZUxvZ2dlclNlcnZpY2UgaW1wbGVtZW50cyBMb2dnZXIge1xuICBsb2coLi4uYXJncykge1xuICAgIGNvbnNvbGUubG9nLmFwcGx5KGNvbnNvbGUsIGFyZ3MpO1xuICB9XG4gIGVycm9yKC4uLmFyZ3MpIHtcbiAgICBjb25zb2xlLmVycm9yLmFwcGx5KGNvbnNvbGUsIGFyZ3MpO1xuICB9XG4gIHdhcm4oLi4uYXJncykge1xuICAgIGNvbnNvbGUud2Fybi5hcHBseShjb25zb2xlLCBhcmdzKTtcbiAgfVxufVxuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBOb0xvZ2dlclNlcnZpY2UgaW1wbGVtZW50cyBMb2dnZXIge1xuICBsb2coLi4uYXJncykge31cbiAgZXJyb3IoLi4uYXJncykge31cbiAgd2FybiguLi5hcmdzKSB7fVxufVxuIiwiaW1wb3J0IHsgSW5qZWN0YWJsZSwgRXZlbnRFbWl0dGVyIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICBtYXAsXG4gIG1lcmdlTWFwLFxuICBjb25jYXQsXG4gIG1hcFRvLFxuICBmaWx0ZXIsXG4gIHRha2VVbnRpbCxcbiAgYnVmZmVyVG9nZ2xlLFxuICBzY2FuLFxuICB0YXAsXG4gIHRha2UsXG4gIHRpbWVvdXQsXG4gIHJldHJ5V2hlbixcbiAgZGVsYXlcbn0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHtcbiAgT2JzZXJ2YWJsZSxcbiAgZm9ya0pvaW4sXG4gIFN1YmplY3QsXG4gIGRlZmVyLFxuICBmcm9tRXZlbnQsXG4gIG9mLFxuICBmcm9tLFxuICBpbnRlcnZhbFxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEdhdHRTZXJ2aWNlcyB9IGZyb20gJy4vZ2F0dC1zZXJ2aWNlcyc7XG5pbXBvcnQgeyBCcm93c2VyV2ViQmx1ZXRvb3RoIH0gZnJvbSAnLi4vcGxhdGZvcm0vYnJvd3Nlcic7XG5pbXBvcnQgeyBDeXBoZXJBZXNTZXJ2aWNlIH0gZnJvbSAnLi4vY3lwaGVyL2N5cGhlci1hZXMuc2VydmljZSc7XG5pbXBvcnQgeyBDb25zb2xlTG9nZ2VyU2VydmljZSB9IGZyb20gJy4uL2xvZ2dlci5zZXJ2aWNlJztcblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgQmx1ZXRvb3RoU2VydmljZSBleHRlbmRzIFN1YmplY3Q8Qmx1ZXRvb3RoU2VydmljZT4ge1xuICBwdWJsaWMgX2RldmljZSQ6IEV2ZW50RW1pdHRlcjxCbHVldG9vdGhEZXZpY2U+O1xuICBwcml2YXRlIGRldmljZTogQmx1ZXRvb3RoRGV2aWNlO1xuICBwcml2YXRlIHNlcnZpY2VDaGFyYWN0ZXJpc3RpY1ZzU3Vic2NyaXB0aW9uTGlzdCA9IHt9O1xuICBwcml2YXRlIG5vdGlmaWVyU3ViamVjdCA9IG5ldyBTdWJqZWN0KCk7XG4gIHByaXZhdGUgbm90aWZpZXJTdGFydGVkU3ViamVjdCA9IG5ldyBTdWJqZWN0KCk7XG4gIHByaXZhdGUgYmx1ZXRvb3RoQXZhaWxhYmxlID0gZmFsc2U7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHB1YmxpYyBfd2ViQmxlOiBCcm93c2VyV2ViQmx1ZXRvb3RoLFxuICAgIHByaXZhdGUgY3lwaGVyQWVzU2VydmljZTogQ3lwaGVyQWVzU2VydmljZSxcbiAgICBwdWJsaWMgX2NvbnNvbGU6IENvbnNvbGVMb2dnZXJTZXJ2aWNlXG4gICkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5fZGV2aWNlJCA9IG5ldyBFdmVudEVtaXR0ZXI8Qmx1ZXRvb3RoRGV2aWNlPigpO1xuICAgIGlmIChfd2ViQmxlLl9ibGUpIHtcbiAgICAgIHRoaXMuYmx1ZXRvb3RoQXZhaWxhYmxlID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICBpc0JsdWV0b290aEF2YWlsYWJsZSgpIHtcbiAgICByZXR1cm4gdGhpcy5ibHVldG9vdGhBdmFpbGFibGU7XG4gIH1cbiAgLyoqXG4gICAqIGdldCB0aGUgY3VycmVudCBkZXZpY2UsIGlmIHRoZSBkZXZpY2UgcmV0dXJuIG51bGwgaXMgYmVjYXVzZSB0aGUgY29ubmVjdGlvbiBoYXMgbG9zdFxuICAgKiBAcmV0dXJucyB0aGUgY3VycmVudCBjb25uY2V0ZWQgZGV2aWNlXG4gICAqL1xuICBnZXREZXZpY2UkKCk6IE9ic2VydmFibGU8Qmx1ZXRvb3RoRGV2aWNlPiB7XG4gICAgcmV0dXJuIHRoaXMuX2RldmljZSQ7XG4gIH1cblxuICBnZXROb3RpZmllclN0YXJ0ZWRTdWJqZWN0JCgpIHtcbiAgICByZXR1cm4gdGhpcy5ub3RpZmllclN0YXJ0ZWRTdWJqZWN0O1xuICB9XG4gIC8qKlxuICAgKiBzdGFydCBhIHN0cmVhbSBieSBub3RpZmllcnMgY2hhcmFjdGVyaXN0aWNzXG4gICAqIEBwYXJhbSBzZXJ2aWNlIFRoZSBzZXJ2aWNlIHRvIHdoaWNoIHRoZSBjaGFyYWN0ZXJpc3RpYyBiZWxvbmdzXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyBUaGUgY2hhcmFjdGVyaXN0aWMgd2hvc2UgdmFsdWUgeW91IHdhbnQgdG8gbGlzdGVuXG4gICAqIEBwYXJhbSBvcHRpb25zIG9iamVjdCB0aGF0IGNvbnRhaW5zIHRoZVxuICAgKiBzdGFydEJ5dGU6bnVtYmVyIChyZXF1aXJlZCksIHN0b3BCeXRlOm51bWJlciAocmVxdWlyZWQpLFxuICAgKiBsZW5ndGhQb3NpdGlvbjogeyBzdGFydDogbnVtYmVyLCBlbmQ6IG51bWJlciwgbGVuZ3RoUGFkZGluZzogbnVtYmVyIH0gKHJlcXVpcmVkKVxuICAgKiBAcmV0dXJucyBBIERhdGFWaWV3IHRoYW4gY29udGFpbnMgdGhlIGNoYXJhY3RlcmlzdGljIHZhbHVlXG4gICAqL1xuICBzdGFydE5vdGlmaWVyTGlzdGVuZXIkKHNlcnZpY2UsIGNoYXJhY3RlcmlzdGljLCBvcHRpb25zKSB7XG4gICAgcmV0dXJuIGRlZmVyKCgpID0+IHtcbiAgICAgIG9mKHNlcnZpY2UpXG4gICAgICAgIC5waXBlKFxuICAgICAgICAgIHRhcCgoKSA9PiB0aGlzLl9jb25zb2xlLmxvZygnSW5pY2lhIGVsIG5vdGlmaWVyIGNvbiBsYSBpbnN0YW5jaWE6ICcsIHRoaXMuc2VydmljZUNoYXJhY3RlcmlzdGljVnNTdWJzY3JpcHRpb25MaXN0KSksXG4gICAgICAgICAgZmlsdGVyKFxuICAgICAgICAgICAgXyA9PlxuICAgICAgICAgICAgICBPYmplY3Qua2V5cyh0aGlzLnNlcnZpY2VDaGFyYWN0ZXJpc3RpY1ZzU3Vic2NyaXB0aW9uTGlzdCkuaW5kZXhPZihcbiAgICAgICAgICAgICAgICBgJHtzZXJ2aWNlfS0ke2NoYXJhY3RlcmlzdGljfWBcbiAgICAgICAgICAgICAgKSA9PT0gLTFcbiAgICAgICAgICApXG4gICAgICAgIClcbiAgICAgICAgLnN1YnNjcmliZShcbiAgICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnNlcnZpY2VDaGFyYWN0ZXJpc3RpY1ZzU3Vic2NyaXB0aW9uTGlzdFtgJHtzZXJ2aWNlfS0ke2NoYXJhY3RlcmlzdGljfWBdID1cbiAgICAgICAgICAgICAgdGhpcy5idWlsZE5vdGlmaWVyTGlzdGVuZXIkKFxuICAgICAgICAgICAgICAgIHNlcnZpY2UsXG4gICAgICAgICAgICAgICAgY2hhcmFjdGVyaXN0aWMsXG4gICAgICAgICAgICAgICAgb3B0aW9uc1xuICAgICAgICAgICAgICApLnN1YnNjcmliZShtZXNzYWdlID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLm5vdGlmaWVyU3ViamVjdC5uZXh0KG1lc3NhZ2UpO1xuICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgIH0sXG4gICAgICAgICAgZXJyID0+IHtcbiAgICAgICAgICAgIHRoaXMuX2NvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBFcnJvciBpbiBub3RpZmllcjogJywgZXJyKTtcbiAgICAgICAgICB9LFxuICAgICAgICAgICgpID0+IHsgfVxuICAgICAgKTtcbiAgICAgIHJldHVybiBvZihgbm90aWZpZXIgYXMgc3Vic2NyaWJlZDogc2VydmljZT0gJHtzZXJ2aWNlfSwgY2hhcmFjdGVyaXN0aWM9ICR7Y2hhcmFjdGVyaXN0aWN9YCk7XG4gICAgfSk7XG5cbiAgfVxuXG4gIHN0b3BOb3RpZmllckxpc3RlbmVyJChzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYykge1xuICAgIHJldHVybiBkZWZlcigoKSA9PiB7XG4gICAgICAvLyB0aGlzLnNlcnZpY2VDaGFyYWN0ZXJpc3RpY1ZzU3Vic2NyaXB0aW9uTGlzdFtgJHtzZXJ2aWNlfS0ke2NoYXJhY3RlcmlzdGljfWBdLnVuc3Vic2NyaWJlKCk7XG4gICAgICBkZWxldGUgdGhpcy5zZXJ2aWNlQ2hhcmFjdGVyaXN0aWNWc1N1YnNjcmlwdGlvbkxpc3RbYCR7c2VydmljZX0tJHtjaGFyYWN0ZXJpc3RpY31gXTtcbiAgICAgIHJldHVybiBvZihgdGhlIG5vdGlmaWVyIG9mIHRoZSBjaGFyYWN0ZXJpc3RpYyAke2NoYXJhY3RlcmlzdGljfSBhcyBiZWVuIHN0b3BwZWRgKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYnVpbGROb3RpZmllckxpc3RlbmVyJChzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgb3B0aW9ucykge1xuICAgIHJldHVybiB0aGlzLmdldFByaW1hcnlTZXJ2aWNlJChzZXJ2aWNlKS5waXBlKFxuICAgICAgdGFwKHNlcnZpY2VJbnN0YW5jZSA9PiB0aGlzLl9jb25zb2xlLmxvZyhgdG9tYSBleGl0b3NhbWVudGUgZWwgc2VydmljaW8gPT09PT09PT09PT09PT09PT4gJHtzZXJ2aWNlSW5zdGFuY2V9YCkpLFxuICAgICAgbWVyZ2VNYXAocHJpbWFyeVNlcnZpY2UgPT5cbiAgICAgICAgdGhpcy5nZXRDaGFyYWN0ZXJpc3RpYyQocHJpbWFyeVNlcnZpY2UsIGNoYXJhY3RlcmlzdGljKVxuICAgICAgICAucGlwZSh0YXAoY2hhciA9PiB0aGlzLl9jb25zb2xlLmxvZyhgdG9tYSBleGl0b3NhbWVudGUgbGEgY2FyYWN0ZXJpc3RpY2EgPT09PT09PT09PT09PT09PT4gJHtjaGFyfWApKSlcbiAgICAgICksXG4gICAgICBtZXJnZU1hcCgoY2hhcjogQmx1ZXRvb3RoUmVtb3RlR0FUVENoYXJhY3RlcmlzdGljKSA9PiB7XG4gICAgICAgIHJldHVybiBkZWZlcigoKSA9PiB7XG4gICAgICAgICAgLy8gZW5hYmxlIHRoZSBjaGFyYWN0ZXJpc3RpYyBub3RpZmllclxuICAgICAgICAgIHJldHVybiBjaGFyLnN0YXJ0Tm90aWZpY2F0aW9ucygpO1xuICAgICAgICB9KS5waXBlKFxuICAgICAgICAgIHJldHJ5V2hlbihlcnJvciA9PlxuICAgICAgICAgICAgZXJyb3IucGlwZShcbiAgICAgICAgICAgICAgdGFwKCgpID0+IHRoaXMuX2NvbnNvbGUubG9nKCdFUlJPUiBFTiBFTCBzdGFydE5vdGlmaWNhdGlvbnMgPT09PT09PT09PT09PT09PT4gJykpLFxuICAgICAgICAgICAgICBkZWxheSgxMDAwKSxcbiAgICAgICAgICAgICAgdGFrZSg1KVxuICAgICAgICAgICAgKVxuICAgICAgICAgICksXG4gICAgICAgICAgdGFwKCgpID0+IHtcbiAgICAgICAgICAgIHRoaXMubm90aWZpZXJTdGFydGVkU3ViamVjdC5uZXh0KHRydWUpO1xuICAgICAgICAgICAgdGhpcy5fY29uc29sZS5sb2coYGluY2lhIGxhcyBub3RpZmlhY2lvbmVzIGRlIGxhIGNhcmFjdGVyaXN0aWNhIDIgPT09PT09PT09PT09PT09PT4gJHtjaGFyYWN0ZXJpc3RpY31gKTtcbiAgICAgICAgICB9KSxcbiAgICAgICAgICBtZXJnZU1hcChfID0+IHtcbiAgICAgICAgICAgIC8vIHN0YXJ0IHRoZSBsaXN0ZXIgZnJvbSB0aGUgZXZlbiBjaGFyYWN0ZXJpc3RpY3ZhbHVlY2hhbmdlZCB0byBnZXQgYWxsIGNoYW5nZXMgb24gdGhlIHNwZWNpZmljXG4gICAgICAgICAgICAvLyBjaGFyYWN0ZXJpc3RpY1xuICAgICAgICAgICAgcmV0dXJuIGZyb21FdmVudChjaGFyLCAnY2hhcmFjdGVyaXN0aWN2YWx1ZWNoYW5nZWQnKS5waXBlKFxuICAgICAgICAgICAgICB0YWtlVW50aWwoZnJvbUV2ZW50KGNoYXIsICdnYXR0c2VydmVyZGlzY29ubmVjdGVkJykpLFxuICAgICAgICAgICAgICBtYXAoKGV2ZW50OiBFdmVudCkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIGdldCBhIGV2ZW50IGZyb20gdGhlIGNoYXJhY3RlcmlzdGljIGFuZCBtYXAgdGhhdFxuICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICBzdGFydEJ5dGVNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIHN0b3BCeXRlTWF0Y2hlczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICBsZW5ndGhNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIG1lc3NhZ2VMZW5ndGg6IDAsXG4gICAgICAgICAgICAgICAgICB0aW1lc3RhbXA6IERhdGUubm93KCksXG4gICAgICAgICAgICAgICAgICBkYXRhOiBBcnJheS5mcm9tKFxuICAgICAgICAgICAgICAgICAgICBuZXcgVWludDhBcnJheShcbiAgICAgICAgICAgICAgICAgICAgICAoKGV2ZW50LnRhcmdldCBhcyBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMpXG4gICAgICAgICAgICAgICAgICAgICAgICAudmFsdWUgYXMgRGF0YVZpZXcpLmJ1ZmZlclxuICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgIHNjYW4oXG4gICAgICAgICAgICAgICAgKGFjYywgdmFsdWUpID0+IHtcbiAgICAgICAgICAgICAgICAgIGFjYy50aW1lc3RhbXAgPSBhY2MudGltZXN0YW1wID09PSAwID8gdmFsdWUudGltZXN0YW1wIDogYWNjLnRpbWVzdGFtcDtcbiAgICAgICAgICAgICAgICAgIC8vIGlmIHRoZSBjdXJyZW50IGFjY3VtdWxhdG9yIHZhbHVlIGlzIGEgdmFsaWQgbWVzc2FnZSwgdGhlbiBpcyByZXN0YXJ0ZWQgdG8gZ2V0IHRoZSBuZXh0XG4gICAgICAgICAgICAgICAgICAvLyBtZXNzYWdlXG4gICAgICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgICAgIChhY2MubGVuZ3RoTWF0Y2hlcyAmJlxuICAgICAgICAgICAgICAgICAgICAgIGFjYy5zdGFydEJ5dGVNYXRjaGVzICYmXG4gICAgICAgICAgICAgICAgICAgICAgYWNjLnN0b3BCeXRlTWF0Y2hlcylcbiAgICAgICAgICAgICAgICAgICAgfHwgYWNjLnRpbWVzdGFtcCArIDEwMDAgPCBEYXRlLm5vdygpXG4gICAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgYWNjID0ge1xuICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0Qnl0ZU1hdGNoZXM6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgIHN0b3BCeXRlTWF0Y2hlczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoTWF0Y2hlczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZUxlbmd0aDogMCxcbiAgICAgICAgICAgICAgICAgICAgICB0aW1lc3RhbXA6IDAsXG4gICAgICAgICAgICAgICAgICAgICAgZGF0YTogW11cbiAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIC8vIHZhbGlkYXRlIHRoZSBzdGFydCBieXRlXG4gICAgICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgICAgICFhY2Muc3RhcnRCeXRlTWF0Y2hlcyAmJlxuICAgICAgICAgICAgICAgICAgICB2YWx1ZS5kYXRhWzBdID09PSBvcHRpb25zLnN0YXJ0Qnl0ZVxuICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIGdldCB0aGUgbWVzc2FnZSBsZW5ndGggdXNpbmcgdGhlIHN0YXJ0IGFuZCBlbmQgcG9zaXRpb25cbiAgICAgICAgICAgICAgICAgICAgYWNjLm1lc3NhZ2VMZW5ndGggPVxuICAgICAgICAgICAgICAgICAgICAgIG5ldyBEYXRhVmlldyhcbiAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBVaW50OEFycmF5KFxuICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5kYXRhLnNsaWNlKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wdGlvbnMubGVuZ3RoUG9zaXRpb24uc3RhcnQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW9ucy5sZW5ndGhQb3NpdGlvbi5lbmRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgICAgICAgICAgKS5idWZmZXJcbiAgICAgICAgICAgICAgICAgICAgICApLmdldEludDE2KDAsIGZhbHNlKSArXG4gICAgICAgICAgICAgICAgICAgICAgKG9wdGlvbnMubGVuZ3RoUG9zaXRpb24ubGVuZ3RoUGFkZGluZ1xuICAgICAgICAgICAgICAgICAgICAgICAgPyBvcHRpb25zLmxlbmd0aFBvc2l0aW9uLmxlbmd0aFBhZGRpbmdcbiAgICAgICAgICAgICAgICAgICAgICAgIDogMCk7XG4gICAgICAgICAgICAgICAgICAgIC8vIHZhbGlkIHRoYXQgdGhlIGluaXRpYWwgYnl0ZSB3YXMgZm91bmRcbiAgICAgICAgICAgICAgICAgICAgYWNjLnN0YXJ0Qnl0ZU1hdGNoZXMgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgICAhYWNjLnN0b3BCeXRlTWF0Y2hlcyAmJlxuICAgICAgICAgICAgICAgICAgICB2YWx1ZS5kYXRhW3ZhbHVlLmRhdGEubGVuZ3RoIC0gMV0gPT09IG9wdGlvbnMuc3RvcEJ5dGVcbiAgICAgICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgICAgICAvLyB2YWxpZCB0aGF0IHRoZSBlbmQgYnl0ZSB3YXMgZm91bmRcbiAgICAgICAgICAgICAgICAgICAgYWNjLnN0b3BCeXRlTWF0Y2hlcyA9IHRydWU7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBpZiAoYWNjLnN0YXJ0Qnl0ZU1hdGNoZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gbWVyZ2UgdGhlIG5ldyBkYXRhIGJ5dGVzIHRvIHRoZSBvbGQgYnl0ZXNcbiAgICAgICAgICAgICAgICAgICAgYWNjLmRhdGEgPSBhY2MuZGF0YS5jb25jYXQodmFsdWUuZGF0YSk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBhY2MubGVuZ3RoTWF0Y2hlcyA9XG4gICAgICAgICAgICAgICAgICAgIGFjYy5zdGFydEJ5dGVNYXRjaGVzICYmXG4gICAgICAgICAgICAgICAgICAgIGFjYy5zdG9wQnl0ZU1hdGNoZXMgJiZcbiAgICAgICAgICAgICAgICAgICAgYWNjLm1lc3NhZ2VMZW5ndGggPT09IGFjYy5kYXRhLmxlbmd0aDtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBhY2M7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBzdGFydEJ5dGVNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIHN0b3BCeXRlTWF0Y2hlczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICBsZW5ndGhNYXRjaGVzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIG1lc3NhZ2VMZW5ndGg6IDAsXG4gICAgICAgICAgICAgICAgICB0aW1lc3RhbXA6IDAsXG4gICAgICAgICAgICAgICAgICBkYXRhOiBbXVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgLy8gb25seSBwdWJsaXNoIHRoZSBjb21wbGV0ZSBhbmQgdmFsaWQgbWVzc2FnZVxuICAgICAgICAgICAgICBmaWx0ZXIoXG4gICAgICAgICAgICAgICAgZGF0YSA9PlxuICAgICAgICAgICAgICAgICAgZGF0YS5sZW5ndGhNYXRjaGVzICYmXG4gICAgICAgICAgICAgICAgICBkYXRhLnN0YXJ0Qnl0ZU1hdGNoZXMgJiZcbiAgICAgICAgICAgICAgICAgIGRhdGEuc3RvcEJ5dGVNYXRjaGVzXG4gICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgIC8vIHJlbW92ZSBhbGwgY3VzdG9tIGRhdGEgYW5kIHB1Ymxpc2ggdGhlIG1lc3NhZ2UgZGF0YVxuICAgICAgICAgICAgICBtYXAocmVzdWx0ID0+IHJlc3VsdC5kYXRhKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9KVxuICAgICAgICApO1xuICAgICAgfSlcbiAgICApO1xuICB9XG4gIC8qKlxuICAgKiBTZW5kIGEgcmVxdWVzdCB0byB0aGUgZGV2aWNlIGFuZCB3YWl0IGEgdW5pcXVlIHJlc3BvbnNlXG4gICAqIEBwYXJhbSBtZXNzYWdlIE1lc3NhZ2UgdG8gc2VuZFxuICAgKiBAcGFyYW0gc2VydmljZSBUaGUgc2VydmljZSB0byB3aGljaCB0aGUgY2hhcmFjdGVyaXN0aWMgYmVsb25nc1xuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgVGhlIGNoYXJhY3RlcmlzdGljIHdob3NlIHZhbHVlIHlvdSB3YW50IHRvIHNlbmQgdGhlIG1lc3NhZ2VcbiAgICogQHBhcmFtIHJlc3BvbnNlVHlwZSBmaWx0ZXIgdG8gdXNlIHRvIGlkZW50aWZ5IHRoZSByZXNwb25zZSwgU2FtcGxlOiBbe3Bvc2l0aW9uOiAzLCBieXRlVG9NYXRjaDogMHg4M30sXG4gICAqICB7cG9zaXRpb246IDEzLCBieXRlVG9NYXRjaDogMHg0NX1dXG4gICAqIEBwYXJhbSBjeXBoZXJNYXN0ZXJLZXkgbWFzdGVyIGtleSB0byBkZWNyeXB0IHRoZSBtZXNzYWdlLCBvbmx5IHVzZSB0aGlzIHBhcmEgaWYgdGhlIG1lc3NhZ2UgdG8gcmVjZWl2ZSBpcyBlbmNyeXB0ZWRcbiAgICovXG4gIHNlbmRBbmRXYWl0UmVzcG9uc2UkKFxuICAgIG1lc3NhZ2UsXG4gICAgc2VydmljZSxcbiAgICBjaGFyYWN0ZXJpc3RpYyxcbiAgICByZXNwb25zZVR5cGUsXG4gICAgY3lwaGVyTWFzdGVyS2V5P1xuICApIHtcbiAgICB0aGlzLl9jb25zb2xlLmxvZyhcbiAgICAgICdbQkxFOjpJbmZvXSBTZW5kIG1lc3NhZ2UgdG8gZGV2aWNlOiAnLFxuICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9oZXgobWVzc2FnZSlcbiAgICApO1xuICAgIHJldHVybiBmb3JrSm9pbihcbiAgICAgIHRoaXMuc3Vic2NyaWJlVG9Ob3RpZmllckxpc3RlbmVyKHJlc3BvbnNlVHlwZSwgY3lwaGVyTWFzdGVyS2V5KS5waXBlKFxuICAgICAgICB0YWtlKDEpXG4gICAgICApLFxuICAgICAgdGhpcy5zZW5kVG9Ob3RpZmllciQobWVzc2FnZSwgc2VydmljZSwgY2hhcmFjdGVyaXN0aWMpXG4gICAgKS5waXBlKFxuICAgICAgbWFwKChbbWVzc2FnZVJlc3AsIF9dKSA9PiBtZXNzYWdlUmVzcCksXG4gICAgICB0aW1lb3V0KDMwMDApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogU3Vic2NyaWJlIHRvIHRoZSBub3RpZmllcnMgZmlsdGVyaW5nIGJ5IGJ5dGUgY2hlY2tpbmdcbiAgICogQHBhcmFtIGZpbHRlck9wdGlvbnMgbXVzdCBzcGVjaWZpYyB0aGUgcG9zaXRpb24gYW5kIHRoZSBieXRlIHRvIG1hdGNoIFNhbXBsZTpcbiAgICogW3twb3NpdGlvbjogMywgYnl0ZVRvTWF0Y2g6IDB4ODN9LCB7cG9zaXRpb246IDEzLCBieXRlVG9NYXRjaDogMHg0NX1dXG4gICAqIEBwYXJhbSBjeXBoZXJNYXN0ZXJLZXkgbWFzdGVyIGtleSB0byBkZWNyeXB0IHRoZSBtZXNzYWdlLCBvbmx5IHVzZSB0aGlzIHBhcmEgaWYgdGhlIG1lc3NhZ2UgdG8gcmVjZWl2ZSBpcyBlbmNyeXB0ZWRcbiAgICovXG4gIHN1YnNjcmliZVRvTm90aWZpZXJMaXN0ZW5lcihmaWx0ZXJPcHRpb25zLCBjeXBoZXJNYXN0ZXJLZXk/KSB7XG4gICAgcmV0dXJuIHRoaXMubm90aWZpZXJTdWJqZWN0LnBpcGUoXG4gICAgICBtYXAobWVzc2FnZVVuZm9ybWF0ZWQgPT4ge1xuICAgICAgICAvLyByZWNlaXZlIHRoZSBwdXJlIG1lc3NhZ2VcbiAgICAgICAgbGV0IG1lc3NhZ2VGb3JtbWF0ZWQgPSBtZXNzYWdlVW5mb3JtYXRlZCBhcyBhbnk7XG4gICAgICAgIC8vIHZhbGlkYXRlIGlmIHRoZSBtZXNzYWdlIGlzIGN5cGhlcmVkXG4gICAgICAgIGlmIChjeXBoZXJNYXN0ZXJLZXkpIHtcbiAgICAgICAgICAvLyBnZXQgdGhlIG1lc3NhZ2VMZW5ndGhcbiAgICAgICAgICBjb25zdCBkYXRhYmxvY2tMZW5ndGggPSBuZXcgRGF0YVZpZXcoXG4gICAgICAgICAgICBuZXcgVWludDhBcnJheShtZXNzYWdlRm9ybW1hdGVkLnNsaWNlKDEsIDMpKS5idWZmZXJcbiAgICAgICAgICApLmdldEludDE2KDAsIGZhbHNlKTtcbiAgICAgICAgICB0aGlzLmN5cGhlckFlc1NlcnZpY2UuY29uZmlnKGN5cGhlck1hc3RlcktleSk7XG4gICAgICAgICAgLy8gZ2V0IHRoZSBkYXRhYmxvY2sgb2YgdGhlIG1lc3NhZ2UgYW5kIGRlY3J5cHQgdGhpcyBzZWN0aW9uXG4gICAgICAgICAgY29uc3QgZGF0YWJsb2NrID0gQXJyYXkuZnJvbShcbiAgICAgICAgICAgIHRoaXMuY3lwaGVyQWVzU2VydmljZS5kZWNyeXB0KFxuICAgICAgICAgICAgICAobWVzc2FnZVVuZm9ybWF0ZWQgYXMgYW55KS5zbGljZSgzLCBkYXRhYmxvY2tMZW5ndGggKyAzKVxuICAgICAgICAgICAgKVxuICAgICAgICAgICk7XG4gICAgICAgICAgLy8gbWVyZ2UgdGhlIGRhdGFibG9jayBhbmQgdGhlIG1lc3NhZ2VcbiAgICAgICAgICBtZXNzYWdlRm9ybW1hdGVkID0gKG1lc3NhZ2VVbmZvcm1hdGVkIGFzIGFueSlcbiAgICAgICAgICAgIC5zbGljZSgwLCAzKVxuICAgICAgICAgICAgLmNvbmNhdChkYXRhYmxvY2spXG4gICAgICAgICAgICAuY29uY2F0KChtZXNzYWdlVW5mb3JtYXRlZCBhcyBhbnkpLnNsaWNlKC0yKSk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5fY29uc29sZS5sb2coXG4gICAgICAgICAgJ1tCTEU6OkluZm9dIE5vdGlmaWNhdGlvbiByZWl2ZWQgZnJvbSBkZXZpY2U6ICcsXG4gICAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9oZXgobWVzc2FnZUZvcm1tYXRlZClcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIG1lc3NhZ2VGb3JtbWF0ZWQ7XG4gICAgICB9KSxcbiAgICAgIC8vIGZpbHRlciB0aGUgbWVzc2FnZSB1c2luZyB0aGUgZmlsdGVyIG9wdGlvbnNcbiAgICAgIGZpbHRlcihtZXNzYWdlID0+IHtcbiAgICAgICAgbGV0IGF2YWlsYWJsZU1lc3NhZ2UgPSBmYWxzZTtcbiAgICAgICAgZm9yIChjb25zdCBvcHRpb24gb2YgZmlsdGVyT3B0aW9ucykge1xuICAgICAgICAgIGlmIChtZXNzYWdlW29wdGlvbi5wb3NpdGlvbl0gPT09IG9wdGlvbi5ieXRlVG9NYXRjaCkge1xuICAgICAgICAgICAgYXZhaWxhYmxlTWVzc2FnZSA9IHRydWU7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGF2YWlsYWJsZU1lc3NhZ2UgPSBmYWxzZTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYXZhaWxhYmxlTWVzc2FnZTtcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydCBhIHJlcXVlc3QgdG8gdGhlIGJyb3dzZXIgdG8gbGlzdCBhbGwgYXZhaWxhYmxlIGJsdWV0b290aCBkZXZpY2VzXG4gICAqIEBwYXJhbSBvcHRpb25zIE9wdGlvbnMgdG8gcmVxdWVzdCB0aGUgZGV2aWNlcyB0aGUgc3RydWN0dXJlIGlzOlxuICAgKiBhY2NlcHRBbGxEZXZpY2VzOiB0cnVlfGZhbHNlXG4gICAqIGZpbHRlcnM6IEJsdWV0b290aERhdGFGaWx0ZXJJbml0IChzZWUgaHR0cHM6Ly93ZWJibHVldG9vdGhjZy5naXRodWIuaW8vd2ViLWJsdWV0b290aC8jZGljdGRlZi1ibHVldG9vdGhsZXNjYW5maWx0ZXJpbml0IGZvciBtb3JlIGluZm8pXG4gICAqIG9wdGlvbmFsU2VydmljZXM6IFtdIChzZXJ2aWNlcyB0aGF0IGFyZSBnb2luZyB0byBiZSB1c2VkIGluXG4gICAqIGNvbW11bmljYXRpb24gd2l0aCB0aGUgZGV2aWNlLCBtdXN0IHVzZSB0aGUgVUlJRCBvciBHQVRUIGlkZW50ZmllciB0byBsaXN0IHRoZXIgc2VydmljZXMpXG4gICAqL1xuICBwcml2YXRlIGRpc2NvdmVyRGV2aWNlJChcbiAgICBvcHRpb25zOiBSZXF1ZXN0RGV2aWNlT3B0aW9ucyA9IHt9IGFzIFJlcXVlc3REZXZpY2VPcHRpb25zXG4gICkge1xuICAgIHJldHVybiBkZWZlcigoKSA9PiB0aGlzLl93ZWJCbGUucmVxdWVzdERldmljZShvcHRpb25zKSkucGlwZShcbiAgICAgIG1lcmdlTWFwKGRldmljZSA9PiB7XG4gICAgICAgIHRoaXMuZGV2aWNlID0gZGV2aWNlO1xuICAgICAgICB0aGlzLl9kZXZpY2UkLmVtaXQoZGV2aWNlKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29uZmlndXJlRGV2aWNlRGlzY29ubmVjdGlvbiQoZGV2aWNlKS5waXBlKG1hcFRvKGRldmljZSkpO1xuICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBjb25maWd1cmVEZXZpY2VEaXNjb25uZWN0aW9uJChkZXZpY2UpIHtcbiAgICByZXR1cm4gT2JzZXJ2YWJsZS5jcmVhdGUob2JzZXJ2ZXIgPT4ge1xuICAgICAgb2YoZGV2aWNlKVxuICAgICAgICAucGlwZShcbiAgICAgICAgICBtZXJnZU1hcChkZXYgPT4gZnJvbUV2ZW50KGRldmljZSwgJ2dhdHRzZXJ2ZXJkaXNjb25uZWN0ZWQnKSksXG4gICAgICAgICAgdGFrZSgxKVxuICAgICAgICApXG4gICAgICAgIC5zdWJzY3JpYmUoXG4gICAgICAgICAgKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5fY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICdTZSBkZXNjb25lY3RhIGRpc3AgZW4gT25EZXZpY2UgZGlzY29ubmVjdGVkISEhISEhISdcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICB0aGlzLmRldmljZSA9IG51bGw7XG4gICAgICAgICAgICB0aGlzLl9kZXZpY2UkLmVtaXQobnVsbCk7XG4gICAgICAgICAgfSxcbiAgICAgICAgICBlcnIgPT4ge1xuICAgICAgICAgICAgdGhpcy5fY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIEVycm9yIGluIG5vdGlmaWVyOiAnLCBlcnIpO1xuICAgICAgICAgICAgb2JzZXJ2ZXIuZXJyb3IoZXJyKTtcbiAgICAgICAgICB9LFxuICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgfVxuICAgICAgICApO1xuICAgICAgb2JzZXJ2ZXIubmV4dChgRGlzY29ubmVjdGlvbkV2ZW50IGFzIGJlZW4gcmVnaXN0ZXJgKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNjb3ZlciBhbGwgYXZhaWxhYmxlIGRldmljZXMgYW5kIGNvbm5lY3QgdG8gYSBzZWxlY3RlZCBkZXZpY2VcbiAgICogQHBhcmFtIG9wdGlvbnMgT3B0aW9ucyB0byByZXF1ZXN0IHRoZSBkZXZpY2VzIHRoZSBzdHJ1Y3R1cmUgaXM6XG4gICAqIGFjY2VwdEFsbERldmljZXM6IHRydWV8ZmFsc2VcbiAgICogZmlsdGVyczogQmx1ZXRvb3RoRGF0YUZpbHRlckluaXQgKHNlZSBodHRwczovL3dlYmJsdWV0b290aGNnLmdpdGh1Yi5pby93ZWItYmx1ZXRvb3RoLyNkaWN0ZGVmLWJsdWV0b290aGxlc2NhbmZpbHRlcmluaXQgZm9yIG1vcmUgaW5mbylcbiAgICogb3B0aW9uYWxTZXJ2aWNlczogW10gKHNlcnZpY2VzIHRoYXQgYXJlIGdvaW5nIHRvIGJlIHVzZWQgaW5cbiAgICogY29tbXVuaWNhdGlvbiB3aXRoIHRoZSBkZXZpY2UsIG11c3QgdXNlIHRoZSBVSUlEIG9yIEdBVFQgaWRlbnRmaWVyIHRvIGxpc3QgdGhlciBzZXJ2aWNlcylcbiAgICogQHJldHVybnMgdGhlIGNvbm5lY3RlZCBkZXZpY2VcbiAgICovXG4gIGNvbm5lY3REZXZpY2UkKG9wdGlvbnM/OiBSZXF1ZXN0RGV2aWNlT3B0aW9ucykge1xuICAgIGlmICghb3B0aW9ucykge1xuICAgICAgb3B0aW9ucyA9IHtcbiAgICAgICAgYWNjZXB0QWxsRGV2aWNlczogdHJ1ZSxcbiAgICAgICAgb3B0aW9uYWxTZXJ2aWNlczogW11cbiAgICAgIH07XG4gICAgfSBlbHNlIGlmICghb3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzKSB7XG4gICAgICBvcHRpb25zLm9wdGlvbmFsU2VydmljZXMgPSBbXTtcbiAgICB9XG4gICAgb3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzLnB1c2goR2F0dFNlcnZpY2VzLkdFTkVSSUNfQUNDRVNTLlNFUlZJQ0UpO1xuICAgIG9wdGlvbnMub3B0aW9uYWxTZXJ2aWNlcy5wdXNoKEdhdHRTZXJ2aWNlcy5CQVRURVJZLlNFUlZJQ0UpO1xuICAgIG9wdGlvbnMub3B0aW9uYWxTZXJ2aWNlcy5wdXNoKEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSk7XG4gICAgcmV0dXJuIHRoaXMuZGlzY292ZXJEZXZpY2UkKG9wdGlvbnMpLnBpcGUoXG4gICAgICBtZXJnZU1hcChkZXZpY2UgPT4ge1xuICAgICAgICByZXR1cm4gZGVmZXIoKCkgPT4gKGRldmljZSBhcyBhbnkpLmdhdHQuY29ubmVjdCgpKTtcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogRGlzY29ubmVjdCB0aGUgY3VycmVudCBkZXZpY2VcbiAgICovXG4gIGRpc2Nvbm5lY3REZXZpY2UoKSB7XG4gICAgaWYgKHRoaXMuZGV2aWNlKSB7XG4gICAgICB0aGlzLl9jb25zb2xlLmxvZygnc2UgZGVjb25lY3RhIGRpc3Bvc2l0aXZvJyk7XG4gICAgICB0aGlzLmRldmljZS5nYXR0LmRpc2Nvbm5lY3QoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogZ2V0IGEgZGF0YSBmcm9tIHRoZSBkZXZpY2UgdXNpbmcgdGhlIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBzZXJ2aWNlIFVVSUQgb3IgR0FUVCBpZGVudGlmaWVyIHNlcnZpY2VcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIFVVSUQgb3IgR0FUVCBpZGVudGlmaWVyIGNoYXJhY3RlcmlzdGljXG4gICAqIEByZXR1cm4gVGhlIGNoYXJhY3RlcmlzdGljIGRhdGEgaW4gYSBEYXRhVmlldyBvYmplY3RcbiAgICovXG4gIHJlYWREZXZpY2VWYWx1ZSQoc2VydmljZSwgY2hhcmFjdGVyaXN0aWMpIHtcbiAgICBpZiAoIXRoaXMuZGV2aWNlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdNdXN0IHN0YXJ0IGEgY29ubmVjdGlvbiB0byBhIGRldmljZSBiZWZvcmUgcmVhZCB0aGUgZGV2aWNlIHZhbHVlJ1xuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZ2V0UHJpbWFyeVNlcnZpY2UkKHNlcnZpY2UpLnBpcGUoXG4gICAgICBtZXJnZU1hcChwcmltYXJ5U2VydmljZSA9PlxuICAgICAgICB0aGlzLmdldENoYXJhY3RlcmlzdGljJChwcmltYXJ5U2VydmljZSwgY2hhcmFjdGVyaXN0aWMpXG4gICAgICApLFxuICAgICAgbWVyZ2VNYXAoKGNoYXJhY3RlcmlzdGljVmFsdWU6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYykgPT5cbiAgICAgICAgdGhpcy5yZWFkVmFsdWUkKGNoYXJhY3RlcmlzdGljVmFsdWUpXG4gICAgICApXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiB3cml0ZSBhIHZhbHVlIGluIHRoZSBzZWxlY3RlZCBjaGFyYWN0ZXJpc3RpY1xuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgdGhlIGNoYXJhY3RlcmlzaXRjIHdoZXJlIHlvdSB3YW50IHdyaXRlIHRoZSB2YWx1ZVxuICAgKiBAcGFyYW0gdmFsdWUgdmFsdWUgdGhlIHZhbHVlIHRvIHdyaXRlXG4gICAqL1xuICB3cml0ZURldmljZVZhbHVlJChzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgdmFsdWUpIHtcbiAgICBpZiAoIXRoaXMuZGV2aWNlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdNdXN0IHN0YXJ0IGEgY29ubmVjdGlvbiB0byBhIGRldmljZSBiZWZvcmUgcmVhZCB0aGUgZGV2aWNlIHZhbHVlJ1xuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZ2V0UHJpbWFyeVNlcnZpY2UkKHNlcnZpY2UpLnBpcGUoXG4gICAgICBtZXJnZU1hcChwcmltYXJ5U2VydmljZSA9PlxuICAgICAgICB0aGlzLmdldENoYXJhY3RlcmlzdGljJChwcmltYXJ5U2VydmljZSwgY2hhcmFjdGVyaXN0aWMpXG4gICAgICApLFxuICAgICAgbWVyZ2VNYXAoKGNoYXJhY3RlcmlzdGljVmFsdWU6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYykgPT5cbiAgICAgICAgdGhpcy53cml0ZVZhbHVlJChjaGFyYWN0ZXJpc3RpY1ZhbHVlLCB2YWx1ZSlcbiAgICAgIClcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIGdldCBhIHByaW1hcnkgc2VydmljZSBpbnN0YW5jZSB1c2luZyB0aGUgc2VydmljZSBVSUlEIG9yIEdBVFQgaWRlbnRpZmllclxuICAgKiBAcGFyYW0gc2VydmljZSBzZXJ2aWNlIGlkZW50aWZpZXJcbiAgICogQHJldHVybiBzZXJ2aWNlIGluc3RhbmNlXG4gICAqL1xuICBnZXRQcmltYXJ5U2VydmljZSQoc2VydmljZTogQmx1ZXRvb3RoU2VydmljZVVVSUQpIHtcbiAgICByZXR1cm4gb2YodGhpcy5kZXZpY2UpLnBpcGUoXG4gICAgICBtZXJnZU1hcChkZXZpY2UgPT4ge1xuICAgICAgICByZXR1cm4gZGV2aWNlLmdhdHQuZ2V0UHJpbWFyeVNlcnZpY2Uoc2VydmljZSk7XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGEgY2hhcmFjdGVyaXNpdGljIGluc3RhbmNlIHVzaW5nIHRoZSBzZXJ2aWNlIGluc3RhbmNlIGFuZCBhIGNoYXJhY3RlcmlzdGljIFVVSURcbiAgICogQHBhcmFtIHByaW1hcnlTZXJ2aWNlIHNlcnZpY2UgaW5zdGFuY2VcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIGNoYXJhY3RlcmlzaXRpYyBpZGVudGlmaWVyXG4gICAqIEByZXR1cm4gY2hhcmFjdGVyaXN0aWMgaW5zdGFuY2VcbiAgICovXG4gIGdldENoYXJhY3RlcmlzdGljJChcbiAgICBwcmltYXJ5U2VydmljZTogQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZpY2UsXG4gICAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRFxuICApOiBPYnNlcnZhYmxlPHZvaWQgfCBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWM+IHtcbiAgICByZXR1cm4gZGVmZXIoKCkgPT4gcHJpbWFyeVNlcnZpY2UuZ2V0Q2hhcmFjdGVyaXN0aWMoY2hhcmFjdGVyaXN0aWMpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiByZWFkIHRoZSBjaGFyYWN0ZXJpc3RpYyB2YWx1ZVxuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgY2hhcmFjdGVyaXN0aWMgaW5zdGFuY2VcbiAgICogQHJldHVybiBUaGUgY2hhcmFjdGVyaXN0aWMgZGF0YSBpbiBhIERhdGFWaWV3IG9iamVjdFxuICAgKi9cbiAgcHJpdmF0ZSByZWFkVmFsdWUkKFxuICAgIGNoYXJhY3RlcmlzdGljOiBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWNcbiAgKTogT2JzZXJ2YWJsZTxEYXRhVmlldz4ge1xuICAgIHJldHVybiBmcm9tKFxuICAgICAgY2hhcmFjdGVyaXN0aWNcbiAgICAgICAgLnJlYWRWYWx1ZSgpXG4gICAgICAgIC50aGVuKFxuICAgICAgICAgIChkYXRhOiBEYXRhVmlldykgPT4gUHJvbWlzZS5yZXNvbHZlKGRhdGEpLFxuICAgICAgICAgIChlcnJvcjogRE9NRXhjZXB0aW9uKSA9PiBQcm9taXNlLnJlamVjdChgJHtlcnJvci5tZXNzYWdlfWApXG4gICAgICAgIClcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIHdyaXRlIGEgdmFsdWUgaW4gdGhlIHNlbGVjdGVkIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyB0aGUgY2hhcmFjdGVyaXNpdGMgd2hlcmUgeW91IHdhbnQgd3JpdGUgdGhlIHZhbHVlXG4gICAqIEBwYXJhbSB2YWx1ZSB0aGUgdmFsdWUgdG8gd3JpdGVcbiAgICovXG4gIHByaXZhdGUgd3JpdGVWYWx1ZSQoXG4gICAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYyxcbiAgICB2YWx1ZTogQXJyYXlCdWZmZXIgfCBVaW50OEFycmF5XG4gICkge1xuICAgIHJldHVybiBkZWZlcigoKSA9PlxuICAgICAgY2hhcmFjdGVyaXN0aWNcbiAgICAgICAgLndyaXRlVmFsdWUodmFsdWUpXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBjaGFuZ2UgdGhlIHN0YXRlIG9mIHRoZSBjaGFyYWN0ZXJpc3RpYyB0byBlbmFibGUgaXRcbiAgICogQHBhcmFtIHNlcnZpY2UgIHBhcmVudCBzZXJ2aWNlIG9mIHRoZSBjaGFyYWN0ZXJpc3RpY1xuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgY2hhcmFjdGVyaXN0aWMgdG8gY2hhbmdlIHRoZSBzdGF0ZVxuICAgKiBAcGFyYW0gc3RhdGUgbmV3IHN0YXRlXG4gICAqL1xuICBlbmFibGVDaGFyYWN0ZXJpc3RpYyQoXG4gICAgc2VydmljZTogQmx1ZXRvb3RoU2VydmljZVVVSUQsXG4gICAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRCxcbiAgICBzdGF0ZT86IGFueVxuICApIHtcbiAgICBzdGF0ZSA9IHN0YXRlIHx8IG5ldyBVaW50OEFycmF5KFsxXSk7XG4gICAgcmV0dXJuIHRoaXMuc2V0Q2hhcmFjdGVyaXN0aWNTdGF0ZSQoc2VydmljZSwgY2hhcmFjdGVyaXN0aWMsIHN0YXRlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBjaGFuZ2UgdGhlIHN0YXRlIG9mIHRoZSBjaGFyYWN0ZXJpc3RpYyB0byBkaXNhYmxlIGl0XG4gICAqIEBwYXJhbSBzZXJ2aWNlICBwYXJlbnQgc2VydmljZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWNcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIGNoYXJhY3RlcmlzdGljIHRvIGNoYW5nZSB0aGUgc3RhdGVcbiAgICogQHBhcmFtIHN0YXRlIG5ldyBzdGF0ZVxuICAgKi9cbiAgZGlzYmFsZUNoYXJhY3RlcmlzdGljJChcbiAgICBzZXJ2aWNlOiBCbHVldG9vdGhTZXJ2aWNlVVVJRCxcbiAgICBjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoQ2hhcmFjdGVyaXN0aWNVVUlELFxuICAgIHN0YXRlPzogYW55XG4gICkge1xuICAgIHN0YXRlID0gc3RhdGUgfHwgbmV3IFVpbnQ4QXJyYXkoWzBdKTtcbiAgICByZXR1cm4gdGhpcy5zZXRDaGFyYWN0ZXJpc3RpY1N0YXRlJChzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgc3RhdGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIHNldCBhIHN0YXRlIHRvIGFuIHNwZWNpZmljIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBzZXJ2aWNlICBwYXJlbnQgc2VydmljZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWNcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIGNoYXJhY3RlcmlzdGljIHRvIGNoYW5nZSB0aGUgc3RhdGVcbiAgICogQHBhcmFtIHN0YXRlIG5ldyBzdGF0ZVxuICAgKiBAcmV0dXJuXG4gICAqL1xuICBzZXRDaGFyYWN0ZXJpc3RpY1N0YXRlJChcbiAgICBzZXJ2aWNlOiBCbHVldG9vdGhTZXJ2aWNlVVVJRCxcbiAgICBjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoQ2hhcmFjdGVyaXN0aWNVVUlELFxuICAgIHN0YXRlOiBBcnJheUJ1ZmZlclxuICApIHtcbiAgICBjb25zdCBwcmltYXJ5U2VydmljZSA9IHRoaXMuZ2V0UHJpbWFyeVNlcnZpY2UkKHNlcnZpY2UpO1xuICAgIHJldHVybiBwcmltYXJ5U2VydmljZS5waXBlKFxuICAgICAgbWVyZ2VNYXAoX3ByaW1hcnlTZXJ2aWNlID0+XG4gICAgICAgIHRoaXMuZ2V0Q2hhcmFjdGVyaXN0aWMkKF9wcmltYXJ5U2VydmljZSwgY2hhcmFjdGVyaXN0aWMpXG4gICAgICApLFxuICAgICAgbWFwKChfY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYykgPT5cbiAgICAgICAgdGhpcy53cml0ZVZhbHVlJChfY2hhcmFjdGVyaXN0aWMsIHN0YXRlKVxuICAgICAgKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogU2VuZCBhIG1lc3NhZ2UgdXNpbmcgYSBub3RpZmllciBjaGFyYWN0ZXJpc3RpY1xuICAgKiBAcGFyYW0gbWVzc2FnZSBtZXNzYWdlIHRvIHNlbmRcbiAgICogQHBhcmFtIHNlcnZpY2Ugc2VydmljZSB0byB3aGljaCB0aGUgY2hhcmFjdGVyaXN0aWMgYmVsb25nc1xuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgZmVhdHVyZSBpbiB3aGljaCB5b3Ugd2FudCB0byBzZW5kIHRoZSBub3RpZmljYXRpb25cbiAgICovXG4gIHNlbmRUb05vdGlmaWVyJChtZXNzYWdlLCBzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYykge1xuICAgIHJldHVybiB0aGlzLmdldFByaW1hcnlTZXJ2aWNlJChzZXJ2aWNlKS5waXBlKFxuICAgICAgbWVyZ2VNYXAoKHByaW1hcnlTZXJ2aWNlOiBCbHVldG9vdGhSZW1vdGVHQVRUU2VydmljZSkgPT5cbiAgICAgICAgdGhpcy5nZXRDaGFyYWN0ZXJpc3RpYyQocHJpbWFyeVNlcnZpY2UsIGNoYXJhY3RlcmlzdGljKVxuICAgICAgKSxcbiAgICAgIG1lcmdlTWFwKChjaGFyOiBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMpID0+IHtcbiAgICAgICAgaWYgKG1lc3NhZ2UubGVuZ3RoID4gMTYpIHtcbiAgICAgICAgICBsZXQgb2JzVGVzdCA9IG9mKHVuZGVmaW5lZCk7XG4gICAgICAgICAgd2hpbGUgKG1lc3NhZ2UubGVuZ3RoID4gMTYpIHtcbiAgICAgICAgICAgIG9ic1Rlc3QgPSBvYnNUZXN0LnBpcGUoXG4gICAgICAgICAgICAgIGNvbmNhdCh0aGlzLndyaXRlVmFsdWUkKGNoYXIsIG1lc3NhZ2Uuc2xpY2UoMCwgMTYpKSlcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBtZXNzYWdlID0gbWVzc2FnZS5zbGljZSgxNiwgbWVzc2FnZS5sZW5ndGgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAobWVzc2FnZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBvYnNUZXN0ID0gb2JzVGVzdC5waXBlKGNvbmNhdCh0aGlzLndyaXRlVmFsdWUkKGNoYXIsIG1lc3NhZ2UpKSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBvYnNUZXN0O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiB0aGlzLndyaXRlVmFsdWUkKGNoYXIsIG1lc3NhZ2UpO1xuICAgICAgICB9XG4gICAgICB9KVxuICAgICk7XG4gIH1cbiAgLyoqXG4gICAqIFRoZSBCYXR0ZXJ5IExldmVsIGNoYXJhY3RlcmlzdGljIGlzIHJlYWQgdXNpbmcgdGhlIEdBVFQgUmVhZCBDaGFyYWN0ZXJpc3RpY1xuICAgKiAgVmFsdWUgc3ViLXByb2NlZHVyZSBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBiYXR0ZXJ5IGxldmVsIGFzIGEgcGVyY2VudGFnZVxuICAgKiAgZnJvbSAwJSB0byAxMDAlOyAwJSByZXByZXNlbnRzIGEgYmF0dGVyeSB0aGF0IGlzIGZ1bGx5IGRpc2NoYXJnZWQsIDEwMCVcbiAgICogcmVwcmVzZW50cyBhIGJhdHRlcnkgdGhhdCBpcyBmdWxseSBjaGFyZ2VkXG4gICAqL1xuICBnZXRCYXR0ZXJ5TGV2ZWwkKCkge1xuICAgIHJldHVybiB0aGlzLnJlYWREZXZpY2VWYWx1ZSQoXG4gICAgICBHYXR0U2VydmljZXMuQkFUVEVSWS5TRVJWSUNFLFxuICAgICAgR2F0dFNlcnZpY2VzLkJBVFRFUlkuQkFUVEVSWV9MRVZFTFxuICAgICkucGlwZShtYXAodmFsdWUgPT4gdmFsdWUuZ2V0VWludDgoMCkpKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIHRoZSBuYW1lIG9mIHRoZSBtYW51ZmFjdHVyZXIgb2YgdGhlIGRldmljZS5cbiAgICovXG4gIGdldE1hbnVmYWN0dXJlck5hbWUkKCkge1xuICAgIHJldHVybiB0aGlzLnJlYWREZXZpY2VWYWx1ZSQoXG4gICAgICBHYXR0U2VydmljZXMuREVWSUNFX0lORk9STUFUSU9OLlNFUlZJQ0UsXG4gICAgICBHYXR0U2VydmljZXMuREVWSUNFX0lORk9STUFUSU9OLk1BTlVGQUNUVVJFUl9OQU1FXG4gICAgKS5waXBlKFxuICAgICAgbWFwKGRhdGFWaWV3ID0+XG4gICAgICAgIHRoaXMuY3lwaGVyQWVzU2VydmljZS5ieXRlc1RvVGV4dChuZXcgVWludDhBcnJheShkYXRhVmlldy5idWZmZXIpKVxuICAgICAgKVxuICAgICk7XG4gIH1cbiAgLyoqXG4gICAqIFRoaXMgY2hhcmFjdGVyaXN0aWMgcmVwcmVzZW50cyB0aGUgbW9kZWwgbnVtYmVyIHRoYXQgaXMgYXNzaWduZWQgYnkgdGhlIGRldmljZSB2ZW5kb3IuXG4gICAqL1xuICBnZXRNb2RlbE51bWJlciQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uTU9ERUxfTlVNQkVSXG4gICAgKS5waXBlKFxuICAgICAgbWFwKGRhdGFWaWV3ID0+XG4gICAgICAgIHRoaXMuY3lwaGVyQWVzU2VydmljZS5ieXRlc1RvVGV4dChuZXcgVWludDhBcnJheShkYXRhVmlldy5idWZmZXIpKVxuICAgICAgKVxuICAgICk7XG4gIH1cbiAgLyoqXG4gICAqIFRoaXMgY2hhcmFjdGVyaXN0aWMgcmVwcmVzZW50cyB0aGUgc2VyaWFsIG51bWJlciBmb3IgYSBwYXJ0aWN1bGFyIGluc3RhbmNlIG9mIHRoZSBkZXZpY2UuXG4gICAqL1xuICBnZXRTZXJpYWxOdW1iZXIkKCkge1xuICAgIHJldHVybiB0aGlzLnJlYWREZXZpY2VWYWx1ZSQoXG4gICAgICBHYXR0U2VydmljZXMuREVWSUNFX0lORk9STUFUSU9OLlNFUlZJQ0UsXG4gICAgICBHYXR0U2VydmljZXMuREVWSUNFX0lORk9STUFUSU9OLlNFUklBTF9OVU1CRVJcbiAgICApLnBpcGUoXG4gICAgICBtYXAoZGF0YVZpZXcgPT5cbiAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9UZXh0KG5ldyBVaW50OEFycmF5KGRhdGFWaWV3LmJ1ZmZlcikpXG4gICAgICApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIHRoZSBoYXJkd2FyZSByZXZpc2lvbiBmb3IgdGhlIGhhcmR3YXJlIHdpdGhpbiB0aGUgZGV2aWNlLlxuICAgKi9cbiAgZ2V0SGFyZHdhcmVSZXZpc2lvbiQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uSEFSRFdBUkVfUkVWSVNJT05cbiAgICApLnBpcGUoXG4gICAgICBtYXAoZGF0YVZpZXcgPT5cbiAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9UZXh0KG5ldyBVaW50OEFycmF5KGRhdGFWaWV3LmJ1ZmZlcikpXG4gICAgICApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIHRoZSBmaXJtd2FyZSByZXZpc2lvbiBmb3IgdGhlIGZpcm13YXJlIHdpdGhpbiB0aGUgZGV2aWNlLlxuICAgKi9cbiAgZ2V0RmlybXdhcmVSZXZpc2lvbiQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uRklSTVdBUkVfUkVWSVNJT05cbiAgICApLnBpcGUoXG4gICAgICBtYXAoZGF0YVZpZXcgPT5cbiAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9UZXh0KG5ldyBVaW50OEFycmF5KGRhdGFWaWV3LmJ1ZmZlcikpXG4gICAgICApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIHRoZSBzb2Z0d2FyZSByZXZpc2lvbiBmb3IgdGhlIHNvZnR3YXJlIHdpdGhpbiB0aGUgZGV2aWNlLlxuICAgKi9cbiAgZ2V0U29mdHdhcmVSZXZpc2lvbiQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU09GVFdBUkVfUkVWSVNJT05cbiAgICApLnBpcGUoXG4gICAgICBtYXAoZGF0YVZpZXcgPT5cbiAgICAgICAgdGhpcy5jeXBoZXJBZXNTZXJ2aWNlLmJ5dGVzVG9UZXh0KG5ldyBVaW50OEFycmF5KGRhdGFWaWV3LmJ1ZmZlcikpXG4gICAgICApXG4gICAgKTtcbiAgfVxuICAvKipcbiAgICogVGhpcyBjaGFyYWN0ZXJpc3RpYyByZXByZXNlbnRzIGEgc3RydWN0dXJlIGNvbnRhaW5pbmcgYW4gT3JnYW5pemF0aW9uYWxseSBVbmlxdWUgSWRlbnRpZmllclxuICAgKiAgKE9VSSkgZm9sbG93ZWQgYnkgYSBtYW51ZmFjdHVyZXItZGVmaW5lZCBpZGVudGlmaWVyIGFuZCBpcyB1bmlxdWUgZm9yIGVhY2ggaW5kaXZpZHVhbCBpbnN0YW5jZSBvZiB0aGUgcHJvZHVjdC5cbiAgICovXG4gIGdldFN5c3RlbUlkJCgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkRGV2aWNlVmFsdWUkKFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TRVJWSUNFLFxuICAgICAgR2F0dFNlcnZpY2VzLkRFVklDRV9JTkZPUk1BVElPTi5TWVNURU1fSURcbiAgICApO1xuICB9XG4gIC8qKlxuICAgKiBUaGUgUG5QX0lEIGNoYXJhY3RlcmlzdGljIGlzIGEgc2V0IG9mIHZhbHVlcyB1c2VkIHRvIGNyZWF0ZSBhIGRldmljZSBJRCB2YWx1ZSB0aGF0IGlzIHVuaXF1ZSBmb3IgdGhpcyBkZXZpY2UuXG4gICAqL1xuICBnZXRQbnBJZCQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZERldmljZVZhbHVlJChcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uU0VSVklDRSxcbiAgICAgIEdhdHRTZXJ2aWNlcy5ERVZJQ0VfSU5GT1JNQVRJT04uUE5QX0lEXG4gICAgKTtcbiAgfVxufVxuIiwiaW1wb3J0IHsgTmdNb2R1bGUsIE1vZHVsZVdpdGhQcm92aWRlcnMsIEluamVjdGlvblRva2VuICB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQnJvd3NlcldlYkJsdWV0b290aCB9IGZyb20gJy4vcGxhdGZvcm0vYnJvd3Nlcic7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQmx1ZXRvb3RoU2VydmljZSB9IGZyb20gJy4vYmx1ZXRvb3RoL2JsdWV0b290aC5zZXJ2aWNlJztcbmltcG9ydCB7IENvbnNvbGVMb2dnZXJTZXJ2aWNlLCBOb0xvZ2dlclNlcnZpY2UgfSBmcm9tICcuL2xvZ2dlci5zZXJ2aWNlJztcblxuZXhwb3J0IGludGVyZmFjZSBBV0JPcHRpb25zIHtcbiAgZW5hYmxlVHJhY2luZz86IGJvb2xlYW47XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBicm93c2VyV2ViQmx1ZXRvb3RoKCkge1xuICByZXR1cm4gbmV3IEJyb3dzZXJXZWJCbHVldG9vdGgoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNvbnNvbGVMb2dnZXJTZXJ2aWNlQ29uZmlnKG9wdGlvbnM6IEFXQk9wdGlvbnMpIHtcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5lbmFibGVUcmFjaW5nKSB7XG4gICAgcmV0dXJuIG5ldyBDb25zb2xlTG9nZ2VyU2VydmljZSgpO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBuZXcgTm9Mb2dnZXJTZXJ2aWNlKCk7XG4gIH1cbn1cbmV4cG9ydCBmdW5jdGlvbiBtYWtlTWVUb2tlbkluamVjdG9yKCkge1xuICByZXR1cm4gbmV3IEluamVjdGlvblRva2VuKCdBV0JPcHRpb25zJyk7XG59XG5cblxuQE5nTW9kdWxlKHtcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZV1cbn0pXG5leHBvcnQgY2xhc3MgQW5ndWxhckJsZU1vZHVsZSB7XG4gIHN0YXRpYyBmb3JSb290KG9wdGlvbnM6IEFXQk9wdGlvbnMgPSB7fSk6IE1vZHVsZVdpdGhQcm92aWRlcnMge1xuICAgIHJldHVybiB7XG4gICAgICBuZ01vZHVsZTogQW5ndWxhckJsZU1vZHVsZSxcbiAgICAgIHByb3ZpZGVyczogW1xuICAgICAgICBCbHVldG9vdGhTZXJ2aWNlLFxuICAgICAgICB7XG4gICAgICAgICAgcHJvdmlkZTogQnJvd3NlcldlYkJsdWV0b290aCxcbiAgICAgICAgICB1c2VGYWN0b3J5OiBicm93c2VyV2ViQmx1ZXRvb3RoXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBwcm92aWRlOiBtYWtlTWVUb2tlbkluamVjdG9yLFxuICAgICAgICAgIHVzZVZhbHVlOiBvcHRpb25zXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBwcm92aWRlOiBDb25zb2xlTG9nZ2VyU2VydmljZSxcbiAgICAgICAgICB1c2VGYWN0b3J5OiBjb25zb2xlTG9nZ2VyU2VydmljZUNvbmZpZyxcbiAgICAgICAgICBkZXBzOiBbbWFrZU1lVG9rZW5JbmplY3Rvcl1cbiAgICAgICAgfVxuICAgICAgXVxuICAgIH07XG4gIH1cbn1cbiJdLCJuYW1lcyI6WyJhZXMuTW9kZU9mT3BlcmF0aW9uIiwiYWVzLkNv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