@nebulae/angular-ble
Version:
A Web Bluetooth (Bluetooth Low Energy) module for angular (v2+)
1,639 lines • 151 kB
JavaScript
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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