@manekinekko/angular-web-bluetooth
Version:
The missing Web Bluetooth module for Angular
363 lines • 47.9 kB
JavaScript
import { EventEmitter, Injectable } from '@angular/core';
import { EMPTY, from, fromEvent, throwError } from 'rxjs';
import { filter, map, mergeMap, takeUntil } from 'rxjs/operators';
import * as i0 from "@angular/core";
import * as i1 from "./platform/browser";
import * as i2 from "./logger.service";
export class BluetoothCore {
constructor(webBle, console) {
this.webBle = webBle;
this.console = console;
this.device$ = new EventEmitter();
this.gatt$ = new EventEmitter();
this.characteristicValueChanges$ = new EventEmitter();
this.gattServer = null;
}
getDevice$() {
return this.device$;
}
getGATT$() {
return this.gatt$;
}
streamValues$() {
return this.characteristicValueChanges$.pipe(filter(value => value && value.byteLength > 0));
}
/**
* Run the discovery process and read the value form the provided service and characteristic
* @param options the ReadValueOptions
*/
async value(options) {
this.console.log('[BLE::Info] Reading value with options %o', options);
if (typeof options.acceptAllDevices === 'undefined') {
options.acceptAllDevices = true;
}
if (typeof options.optionalServices === 'undefined') {
options.optionalServices = [options.service];
}
else {
options.optionalServices = [...options.optionalServices];
}
this.console.log('[BLE::Info] Reading value with options %o', options);
try {
const device = await this.discover({
acceptAllDevices: options.acceptAllDevices,
optionalServices: options.optionalServices
});
this.console.log('[BLE::Info] Device info %o', device);
const gatt = await this.connectDevice(device);
this.console.log('[BLE::Info] GATT info %o', gatt);
const primaryService = await this.getPrimaryService(gatt, options.service);
this.console.log('[BLE::Info] Primary Service info %o', primaryService);
const characteristic = await this.getCharacteristic(primaryService, options.characteristic);
this.console.log('[BLE::Info] Characteristic info %o', characteristic);
const value = await characteristic.readValue();
this.console.log('[BLE::Info] Value info %o', value);
return value;
}
catch (error) {
throw new Error(error);
}
}
value$(options) {
return from(this.value(options));
}
/**
* Run the discovery process.
*
* @param Options such as filters and optional services
* @return The GATT server for the chosen device
*/
async discover(options = {}) {
options.optionalServices = options.optionalServices || ['generic_access'];
this.console.log('[BLE::Info] Requesting devices with options %o', options);
let device = null;
try {
device = await this.webBle.requestDevice(options);
device.addEventListener('gattserverdisconnected', this.onDeviceDisconnected.bind(this));
if (device) {
this.device$.emit(device);
}
else {
this.device$.error(`[BLE::Error] Can not get the Bluetooth Remote GATT Server. Abort.`);
}
}
catch (error) {
this.console.error(error);
}
return device;
}
/**
* This handler will trigger when the client disconnets from the server.
*
* @param event The onDeviceDisconnected event
*/
onDeviceDisconnected(event) {
const disconnectedDevice = event.target;
this.console.log('[BLE::Info] disconnected device %o', disconnectedDevice);
this.device$.emit(undefined);
}
/**
* Run the discovery process.
*
* @param Options such as filters and optional services
* @return Emites the value of the requested service read from the device
*/
discover$(options) {
return from(this.discover(options)).pipe(mergeMap((device) => this.connectDevice$(device)));
}
/**
* Connect to current device.
*
* @return Emites the gatt server instance of the requested device
*/
async connectDevice(device) {
if (device === null || typeof device.gatt === "undefined") {
this.console.error('[BLE::Error] Was not able to connect to Bluetooth Remote GATT Server');
this.gatt$.error(null);
return null;
}
this.console.log('[BLE::Info] Connecting to Bluetooth Remote GATT Server of %o', device);
try {
const gattServer = await device.gatt.connect();
this.gattServer = gattServer;
this.gatt$.emit(gattServer);
return gattServer;
}
catch (error) {
// probably the user has canceled the discovery
Promise.reject(`${error.message}`);
this.gatt$.error(`${error.message}`);
}
return null;
}
/**
* Connect to current device.
*
* @return Emites the gatt server instance of the requested device
*/
connectDevice$(device) {
return from(this.connectDevice(device));
}
/**
* Disconnect the current connected device
*/
disconnectDevice() {
if (!this.gattServer) {
return;
}
this.console.log('[BLE::Info] Disconnecting from Bluetooth Device %o', this.gattServer);
if (this.gattServer.connected) {
this.gattServer.disconnect();
}
else {
this.console.log('[BLE::Info] Bluetooth device is already disconnected');
}
}
/**
* Requests the primary service.
*
* @param gatt The BluetoothRemoteGATTServer sever
* @param service The UUID of the primary service
* @return The remote service (as a Promise)
*/
async getPrimaryService(gatt, service) {
try {
const remoteService = await gatt.getPrimaryService(service);
return await Promise.resolve(remoteService);
}
catch (error) {
return await Promise.reject(`${error.message} (${service})`);
}
}
/**
* Requests the primary service.
*
* @param gatt The BluetoothRemoteGATTServer sever
* @param service The UUID of the primary service
* @return The remote service (as an observable).
*/
getPrimaryService$(gatt, service) {
this.console.log('[BLE::Info] Getting primary service "%s" (if available) of %o', service, gatt);
if (gatt) {
return from(this.getPrimaryService(gatt, service));
}
else {
return throwError(() => new Error('[BLE::Error] Was not able to connect to the Bluetooth Remote GATT Server'));
}
}
/**
* Requests a characteristic from the primary service.
*
* @param primaryService The primary service.
* @param characteristic The characteristic's UUID.
* @returns The characteristic description (as a Promise).
*/
async getCharacteristic(primaryService, characteristic) {
this.console.log('[BLE::Info] Getting Characteristic "%s" of %o', characteristic, primaryService);
try {
const char = await primaryService.getCharacteristic(characteristic);
// listen for characteristic value changes
if (char.properties.notify) {
char.startNotifications().then(_ => {
this.console.log('[BLE::Info] Starting notifications of "%s"', characteristic);
char.addEventListener('characteristicvaluechanged', this.onCharacteristicChanged.bind(this));
}, (error) => {
Promise.reject(`${error.message} (${characteristic})`);
});
}
else {
char.addEventListener('characteristicvaluechanged', this.onCharacteristicChanged.bind(this));
}
return char;
}
catch (rejectionError) {
Promise.reject(`${rejectionError.message} (${characteristic})`);
}
return null;
}
/**
* Requests a characteristic from the primary service.
*
* @param primaryService The primary service.
* @param characteristic The characteristic's UUID.
* @returns The characteristic description (as a Observable).
*/
getCharacteristic$(primaryService, characteristic) {
this.console.log('[BLE::Info] Getting Characteristic "%s" of %o', characteristic, primaryService);
return from(this.getCharacteristic(primaryService, characteristic));
}
/**
* Sets the characteristic's state.
*
* @param service The parent service of the characteristic.
* @param characteristic The requested characteristic
* @param state An ArrayBuffer containing the value of the characteristic.
* @return The primary service (useful for chaining).
*/
setCharacteristicState(service, characteristic, state) {
const primaryService = this.getPrimaryService$(this.gattServer, service);
primaryService
.pipe(mergeMap((_primaryService) => this.getCharacteristic$(_primaryService, characteristic)))
.subscribe((characteristic) => this.writeValue$(characteristic, state));
return primaryService;
}
/**
* Enables the specified characteristic of a given service.
*
* @param service The parent service of the characteristic.
* @param characteristic The requested characteristic
* @return The primary service (useful for chaining).
*/
enableCharacteristic(service, characteristic, state) {
state = state || new Uint8Array([1]);
return this.setCharacteristicState(service, characteristic, state);
}
/**
* Disables the specified characteristic of a given service.
*
* @param service The parent service of the characteristic.
* @param characteristic The requested characteristic.
* @return The primary service (useful for chaining).
*/
disbaleCharacteristic(service, characteristic, state) {
state = state || new Uint8Array([0]);
return this.setCharacteristicState(service, characteristic, state);
}
/**
* Dispatches new values emitted by a characteristic.
*
* @param event the distpatched event.
*/
onCharacteristicChanged(event) {
this.console.log('[BLE::Info] Dispatching new characteristic value %o', event);
const value = event.target.value;
this.characteristicValueChanges$.emit(value);
}
/**
* Reads a value from the characteristics, as a DataView.
*
* @param characteristic The requested characteristic.
* @return the DataView value (as an Observable).
*/
readValue$(characteristic) {
this.console.log('[BLE::Info] Reading Characteristic %o', characteristic);
return from(characteristic
.readValue()
.then((data) => Promise.resolve(data), (error) => Promise.reject(`${error.message}`)));
}
/**
* Writes a value into the specified characteristic.
*
* @param characteristic The requested characteristic.
* @param value The value to be written (as an ArrayBuffer or Uint8Array).
* @return an void Observable.
*/
writeValue$(characteristic, value) {
if (characteristic === null) {
this.console.error('[BLE::Error] Was not able to write characteristic');
return null;
}
this.console.log('[BLE::Info] Writing Characteristic %o', characteristic);
return from(characteristic.writeValue(value).then(_ => Promise.resolve(), (error) => Promise.reject(`${error.message}`)));
}
/**
* A stream of DataView values emitted by the specified characteristic.
*
* @param characteristic The characteristic which value you want to observe
* @return The stream of DataView values.
*/
observeValue$(characteristic) {
if (characteristic === null || typeof characteristic.service === 'undefined') {
this.console.error('[BLE::Error] Was not able to read characteristic');
return EMPTY;
}
characteristic.startNotifications();
const disconnected = fromEvent(characteristic.service.device, 'gattserverdisconnected');
return fromEvent(characteristic, 'characteristicvaluechanged')
.pipe(map((event) => event.target.value), takeUntil(disconnected));
}
/**
* A utility method to convert LE to an unsigned 16-bit integer values.
*
* @param data The DataView binary data.
* @param byteOffset The offset, in byte, from the start of the view where to read the data.
* @return An unsigned 16-bit integer number.
*/
littleEndianToUint16(data, byteOffset) {
return (this.littleEndianToUint8(data, byteOffset + 1) << 8) + this.littleEndianToUint8(data, byteOffset);
}
/**
* A utility method to convert LE to an unsigned 8-bit integer values.
*
* @param data The DataView binary data.
* @param byteOffset The offset, in byte, from the start of the view where to read the data.
* @return An unsigned 8-bit integer number.
*/
littleEndianToUint8(data, byteOffset) {
return data.getUint8(byteOffset);
}
/**
* Sends random data (for testing purposes only).
*
* @return Random unsigned 8-bit integer values.
*/
fakeNext(fakeValue) {
if (fakeValue === undefined) {
fakeValue = () => {
const dv = new DataView(new ArrayBuffer(8));
dv.setUint8(0, (Math.random() * 110) | 0);
return dv;
};
}
this.characteristicValueChanges$.emit(fakeValue());
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: BluetoothCore, deps: [{ token: i1.BrowserWebBluetooth }, { token: i2.ConsoleLoggerService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: BluetoothCore, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.3", ngImport: i0, type: BluetoothCore, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i1.BrowserWebBluetooth }, { type: i2.ConsoleLoggerService }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmx1ZXRvb3RoLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9tYW5la2luZWtrby9hbmd1bGFyLXdlYi1ibHVldG9vdGgvc3JjL2xpYi9ibHVldG9vdGguc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsWUFBWSxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6RCxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQWtCLFVBQVUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMxRSxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7Ozs7QUFjbEUsTUFBTSxPQUFPLGFBQWE7SUFNeEIsWUFBNkIsTUFBMkIsRUFBbUIsT0FBNkI7UUFBM0UsV0FBTSxHQUFOLE1BQU0sQ0FBcUI7UUFBbUIsWUFBTyxHQUFQLE9BQU8sQ0FBc0I7UUFFdEcsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLFlBQVksRUFBbUIsQ0FBQztRQUNuRCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksWUFBWSxFQUE2QixDQUFDO1FBQzNELElBQUksQ0FBQywyQkFBMkIsR0FBRyxJQUFJLFlBQVksRUFBWSxDQUFDO1FBRWhFLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDO0lBQ3pCLENBQUM7SUFFRCxVQUFVO1FBQ1IsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDO0lBQ3RCLENBQUM7SUFFRCxRQUFRO1FBQ04sT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDO0lBQ3BCLENBQUM7SUFFRCxhQUFhO1FBQ1gsT0FBTyxJQUFJLENBQUMsMkJBQTJCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssSUFBSSxLQUFLLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQyxD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Z0JBQ2YsTUFBTSxFQUFFLEdBQUcsSUFBSSxRQUFRLENBQUMsSUFBSSxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDNUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQzFDLE9BQU8sRUFBRSxDQUFDO1lBQ1osQ0FBQyxDQUFDO1FBQ0osQ0FBQztRQUVELElBQUksQ0FBQywyQkFBMkIsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQztJQUNyRCxDQUFDOzhHQXJhVSxhQUFhO2tIQUFiLGFBQWEsY0FGWixNQUFNOzsyRkFFUCxhQUFhO2tCQUh6QixVQUFVO21CQUFDO29CQUNWLFVBQVUsRUFBRSxNQUFNO2lCQUNuQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEV2ZW50RW1pdHRlciwgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRU1QVFksIGZyb20sIGZyb21FdmVudCwgT2JzZXJ2YWJsZSwgb2YsIHRocm93RXJyb3IgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGZpbHRlciwgbWFwLCBtZXJnZU1hcCwgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgQ29uc29sZUxvZ2dlclNlcnZpY2UgfSBmcm9tICcuL2xvZ2dlci5zZXJ2aWNlJztcbmltcG9ydCB7IEJyb3dzZXJXZWJCbHVldG9vdGggfSBmcm9tICcuL3BsYXRmb3JtL2Jyb3dzZXInO1xuXG50eXBlIFJlYWRWYWx1ZU9wdGlvbnMgPSB7XG4gIGFjY2VwdEFsbERldmljZXM/OiBib29sZWFuO1xuICBvcHRpb25hbFNlcnZpY2VzPzogQmx1ZXRvb3RoU2VydmljZVVVSURbXTtcbiAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRCxcbiAgc2VydmljZTogQmx1ZXRvb3RoU2VydmljZVVVSUQsXG59O1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBCbHVldG9vdGhDb3JlIHtcbiAgcHJpdmF0ZSBkZXZpY2UkOiBFdmVudEVtaXR0ZXI8Qmx1ZXRvb3RoRGV2aWNlPjtcbiAgcHJpdmF0ZSBnYXR0JDogRXZlbnRFbWl0dGVyPEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2ZXI+O1xuICBwcml2YXRlIGNoYXJhY3RlcmlzdGljVmFsdWVDaGFuZ2VzJDogRXZlbnRFbWl0dGVyPERhdGFWaWV3PjtcbiAgcHJpdmF0ZSBnYXR0U2VydmVyOiBCbHVldG9vdGhSZW1vdGVHQVRUU2VydmVyIHwgbnVsbDtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IHdlYkJsZTogQnJvd3NlcldlYkJsdWV0b290aCwgcHJpdmF0ZSByZWFkb25seSBjb25zb2xlOiBDb25zb2xlTG9nZ2VyU2VydmljZSkge1xuXG4gICAgdGhpcy5kZXZpY2UkID0gbmV3IEV2ZW50RW1pdHRlcjxCbHVldG9vdGhEZXZpY2U+KCk7XG4gICAgdGhpcy5nYXR0JCA9IG5ldyBFdmVudEVtaXR0ZXI8Qmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZlcj4oKTtcbiAgICB0aGlzLmNoYXJhY3RlcmlzdGljVmFsdWVDaGFuZ2VzJCA9IG5ldyBFdmVudEVtaXR0ZXI8RGF0YVZpZXc+KCk7XG5cbiAgICB0aGlzLmdhdHRTZXJ2ZXIgPSBudWxsO1xuICB9XG5cbiAgZ2V0RGV2aWNlJCgpOiBPYnNlcnZhYmxlPEJsdWV0b290aERldmljZSB8IG51bGw+IHtcbiAgICByZXR1cm4gdGhpcy5kZXZpY2UkO1xuICB9XG5cbiAgZ2V0R0FUVCQoKTogT2JzZXJ2YWJsZTxCbHVldG9vdGhSZW1vdGVHQVRUU2VydmVyPiB7XG4gICAgcmV0dXJuIHRoaXMuZ2F0dCQ7XG4gIH1cblxuICBzdHJlYW1WYWx1ZXMkKCk6IE9ic2VydmFibGU8RGF0YVZpZXc+IHtcbiAgICByZXR1cm4gdGhpcy5jaGFyYWN0ZXJpc3RpY1ZhbHVlQ2hhbmdlcyQucGlwZShmaWx0ZXIodmFsdWUgPT4gdmFsdWUgJiYgdmFsdWUuYnl0ZUxlbmd0aCA+IDApKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW4gdGhlIGRpc2NvdmVyeSBwcm9jZXNzIGFuZCByZWFkIHRoZSB2YWx1ZSBmb3JtIHRoZSBwcm92aWRlZCBzZXJ2aWNlIGFuZCBjaGFyYWN0ZXJpc3RpY1xuICAgKiBAcGFyYW0gb3B0aW9ucyB0aGUgUmVhZFZhbHVlT3B0aW9uc1xuICAgKi9cbiAgYXN5bmMgdmFsdWUob3B0aW9uczogUmVhZFZhbHVlT3B0aW9ucykge1xuICAgIHRoaXMuY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIFJlYWRpbmcgdmFsdWUgd2l0aCBvcHRpb25zICVvJywgb3B0aW9ucyk7XG5cbiAgICBpZiAodHlwZW9mIG9wdGlvbnMuYWNjZXB0QWxsRGV2aWNlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIG9wdGlvbnMuYWNjZXB0QWxsRGV2aWNlcyA9IHRydWU7XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBvcHRpb25zLm9wdGlvbmFsU2VydmljZXMgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICBvcHRpb25zLm9wdGlvbmFsU2VydmljZXMgPSBbb3B0aW9ucy5zZXJ2aWNlXTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICBvcHRpb25zLm9wdGlvbmFsU2VydmljZXMgPSBbLi4ub3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzXTtcbiAgICB9XG5cbiAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBSZWFkaW5nIHZhbHVlIHdpdGggb3B0aW9ucyAlbycsIG9wdGlvbnMpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGRldmljZSA9IGF3YWl0IHRoaXMuZGlzY292ZXIoe1xuICAgICAgICBhY2NlcHRBbGxEZXZpY2VzOiBvcHRpb25zLmFjY2VwdEFsbERldmljZXMsXG4gICAgICAgIG9wdGlvbmFsU2VydmljZXM6IG9wdGlvbnMub3B0aW9uYWxTZXJ2aWNlc1xuICAgICAgfSkgYXMgQmx1ZXRvb3RoRGV2aWNlO1xuICAgICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gRGV2aWNlIGluZm8gJW8nLCBkZXZpY2UpO1xuXG4gICAgICBjb25zdCBnYXR0ID0gYXdhaXQgdGhpcy5jb25uZWN0RGV2aWNlKGRldmljZSk7XG4gICAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBHQVRUIGluZm8gJW8nLCBnYXR0KTtcblxuICAgICAgY29uc3QgcHJpbWFyeVNlcnZpY2UgPSBhd2FpdCB0aGlzLmdldFByaW1hcnlTZXJ2aWNlKGdhdHQsIG9wdGlvbnMuc2VydmljZSkgYXMgQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZpY2U7XG4gICAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBQcmltYXJ5IFNlcnZpY2UgaW5mbyAlbycsIHByaW1hcnlTZXJ2aWNlKTtcblxuICAgICAgY29uc3QgY2hhcmFjdGVyaXN0aWMgPSBhd2FpdCB0aGlzLmdldENoYXJhY3RlcmlzdGljKHByaW1hcnlTZXJ2aWNlLCBvcHRpb25zLmNoYXJhY3RlcmlzdGljKSBhcyBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWM7XG4gICAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBDaGFyYWN0ZXJpc3RpYyBpbmZvICVvJywgY2hhcmFjdGVyaXN0aWMpO1xuXG4gICAgICBjb25zdCB2YWx1ZSA9IGF3YWl0IGNoYXJhY3RlcmlzdGljLnJlYWRWYWx1ZSgpO1xuICAgICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gVmFsdWUgaW5mbyAlbycsIHZhbHVlKTtcblxuICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIH1cbiAgICBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihlcnJvciBhcyBhbnkpO1xuICAgIH1cbiAgfVxuXG4gIHZhbHVlJChvcHRpb25zOiBSZWFkVmFsdWVPcHRpb25zKSB7XG4gICAgcmV0dXJuIGZyb20odGhpcy52YWx1ZShvcHRpb25zKSk7XG4gIH1cblxuICAvKipcbiAgICogUnVuIHRoZSBkaXNjb3ZlcnkgcHJvY2Vzcy5cbiAgICpcbiAgICogQHBhcmFtIE9wdGlvbnMgc3VjaCBhcyBmaWx0ZXJzIGFuZCBvcHRpb25hbCBzZXJ2aWNlc1xuICAgKiBAcmV0dXJuICBUaGUgR0FUVCBzZXJ2ZXIgZm9yIHRoZSBjaG9zZW4gZGV2aWNlXG4gICAqL1xuICBhc3luYyBkaXNjb3ZlcihvcHRpb25zOiBSZXF1ZXN0RGV2aWNlT3B0aW9ucyA9IHt9IGFzIFJlcXVlc3REZXZpY2VPcHRpb25zKTogUHJvbWlzZTxCbHVldG9vdGhEZXZpY2UgfCBudWxsPiB7XG4gICAgb3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzID0gb3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzIHx8IFsnZ2VuZXJpY19hY2Nlc3MnXTtcblxuICAgIHRoaXMuY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIFJlcXVlc3RpbmcgZGV2aWNlcyB3aXRoIG9wdGlvbnMgJW8nLCBvcHRpb25zKTtcblxuICAgIGxldCBkZXZpY2UgPSBudWxsO1xuICAgIHRyeSB7XG4gICAgICBkZXZpY2UgPSBhd2FpdCB0aGlzLndlYkJsZS5yZXF1ZXN0RGV2aWNlKG9wdGlvbnMpO1xuICAgICAgZGV2aWNlLmFkZEV2ZW50TGlzdGVuZXIoJ2dhdHRzZXJ2ZXJkaXNjb25uZWN0ZWQnLCB0aGlzLm9uRGV2aWNlRGlzY29ubmVjdGVkLmJpbmQodGhpcykpO1xuXG4gICAgICBpZiAoZGV2aWNlKSB7XG4gICAgICAgIHRoaXMuZGV2aWNlJC5lbWl0KGRldmljZSk7XG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgdGhpcy5kZXZpY2UkLmVycm9yKGBbQkxFOjpFcnJvcl0gQ2FuIG5vdCBnZXQgdGhlIEJsdWV0b290aCBSZW1vdGUgR0FUVCBTZXJ2ZXIuIEFib3J0LmApO1xuICAgICAgfVxuXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHRoaXMuY29uc29sZS5lcnJvcihlcnJvcik7XG4gICAgfVxuXG4gICAgcmV0dXJuIGRldmljZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGlzIGhhbmRsZXIgd2lsbCB0cmlnZ2VyIHdoZW4gdGhlIGNsaWVudCBkaXNjb25uZXRzIGZyb20gdGhlIHNlcnZlci5cbiAgICpcbiAgICogQHBhcmFtIGV2ZW50IFRoZSBvbkRldmljZURpc2Nvbm5lY3RlZCBldmVudFxuICAgKi9cbiAgb25EZXZpY2VEaXNjb25uZWN0ZWQoZXZlbnQ6IEV2ZW50KSB7XG4gICAgY29uc3QgZGlzY29ubmVjdGVkRGV2aWNlID0gZXZlbnQudGFyZ2V0IGFzIEJsdWV0b290aERldmljZTtcbiAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBkaXNjb25uZWN0ZWQgZGV2aWNlICVvJywgZGlzY29ubmVjdGVkRGV2aWNlKTtcblxuICAgIHRoaXMuZGV2aWNlJC5lbWl0KHVuZGVmaW5lZCk7XG4gIH1cblxuICAvKipcbiAgICogUnVuIHRoZSBkaXNjb3ZlcnkgcHJvY2Vzcy5cbiAgICpcbiAgICogQHBhcmFtIE9wdGlvbnMgc3VjaCBhcyBmaWx0ZXJzIGFuZCBvcHRpb25hbCBzZXJ2aWNlc1xuICAgKiBAcmV0dXJuICBFbWl0ZXMgdGhlIHZhbHVlIG9mIHRoZSByZXF1ZXN0ZWQgc2VydmljZSByZWFkIGZyb20gdGhlIGRldmljZVxuICAgKi9cbiAgZGlzY292ZXIkKG9wdGlvbnM/OiBSZXF1ZXN0RGV2aWNlT3B0aW9ucyk6IE9ic2VydmFibGU8dm9pZCB8IEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2ZXIgfCBudWxsPiB7XG4gICAgcmV0dXJuIGZyb20odGhpcy5kaXNjb3ZlcihvcHRpb25zKSkucGlwZShtZXJnZU1hcCgoZGV2aWNlOiBCbHVldG9vdGhEZXZpY2V8bnVsbCkgPT4gdGhpcy5jb25uZWN0RGV2aWNlJChkZXZpY2UpKSk7XG4gIH1cblxuICAvKipcbiAgICogQ29ubmVjdCB0byBjdXJyZW50IGRldmljZS5cbiAgICpcbiAgICogQHJldHVybiAgRW1pdGVzIHRoZSBnYXR0IHNlcnZlciBpbnN0YW5jZSBvZiB0aGUgcmVxdWVzdGVkIGRldmljZVxuICAgKi9cbiAgYXN5bmMgY29ubmVjdERldmljZShkZXZpY2U6IEJsdWV0b290aERldmljZSB8IG51bGwpIHtcblxuICAgIGlmIChkZXZpY2UgPT09IG51bGwgfHwgdHlwZW9mIGRldmljZS5nYXR0ID09PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICB0aGlzLmNvbnNvbGUuZXJyb3IoJ1tCTEU6OkVycm9yXSBXYXMgbm90IGFibGUgdG8gY29ubmVjdCB0byBCbHVldG9vdGggUmVtb3RlIEdBVFQgU2VydmVyJyk7XG4gICAgICB0aGlzLmdhdHQkLmVycm9yKG51bGwpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gQ29ubmVjdGluZyB0byBCbHVldG9vdGggUmVtb3RlIEdBVFQgU2VydmVyIG9mICVvJywgZGV2aWNlKTtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBnYXR0U2VydmVyID0gYXdhaXQgZGV2aWNlLmdhdHQuY29ubmVjdCgpO1xuICAgICAgdGhpcy5nYXR0U2VydmVyID0gZ2F0dFNlcnZlcjtcbiAgICAgIHRoaXMuZ2F0dCQuZW1pdChnYXR0U2VydmVyKTtcbiAgICAgIHJldHVybiBnYXR0U2VydmVyO1xuICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgIC8vIHByb2JhYmx5IHRoZSB1c2VyIGhhcyBjYW5jZWxlZCB0aGUgZGlzY292ZXJ5XG4gICAgICBQcm9taXNlLnJlamVjdChgJHtlcnJvci5tZXNzYWdlfWApO1xuICAgICAgdGhpcy5nYXR0JC5lcnJvcihgJHtlcnJvci5tZXNzYWdlfWApO1xuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbm5lY3QgdG8gY3VycmVudCBkZXZpY2UuXG4gICAqXG4gICAqIEByZXR1cm4gIEVtaXRlcyB0aGUgZ2F0dCBzZXJ2ZXIgaW5zdGFuY2Ugb2YgdGhlIHJlcXVlc3RlZCBkZXZpY2VcbiAgICovXG4gIGNvbm5lY3REZXZpY2UkKGRldmljZTogQmx1ZXRvb3RoRGV2aWNlIHwgbnVsbCkge1xuICAgIHJldHVybiBmcm9tKHRoaXMuY29ubmVjdERldmljZShkZXZpY2UpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNjb25uZWN0IHRoZSBjdXJyZW50IGNvbm5lY3RlZCBkZXZpY2VcbiAgICovXG4gIGRpc2Nvbm5lY3REZXZpY2UoKSB7XG4gICAgaWYgKCF0aGlzLmdhdHRTZXJ2ZXIpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gRGlzY29ubmVjdGluZyBmcm9tIEJsdWV0b290aCBEZXZpY2UgJW8nLCB0aGlzLmdhdHRTZXJ2ZXIpO1xuXG4gICAgaWYgKHRoaXMuZ2F0dFNlcnZlci5jb25uZWN0ZWQpIHtcbiAgICAgIHRoaXMuZ2F0dFNlcnZlci5kaXNjb25uZWN0KCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIEJsdWV0b290aCBkZXZpY2UgaXMgYWxyZWFkeSBkaXNjb25uZWN0ZWQnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVxdWVzdHMgdGhlIHByaW1hcnkgc2VydmljZS5cbiAgICpcbiAgICogQHBhcmFtIGdhdHQgVGhlIEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2ZXIgc2V2ZXJcbiAgICogQHBhcmFtIHNlcnZpY2UgVGhlIFVVSUQgb2YgdGhlIHByaW1hcnkgc2VydmljZVxuICAgKiBAcmV0dXJuIFRoZSByZW1vdGUgc2VydmljZSAoYXMgYSBQcm9taXNlKVxuICAgKi9cbiAgYXN5bmMgZ2V0UHJpbWFyeVNlcnZpY2UoZ2F0dDogQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZlciB8IG51bGwsIHNlcnZpY2U6IEJsdWV0b290aFNlcnZpY2VVVUlEKTogUHJvbWlzZTxCbHVldG9vdGhSZW1vdGVHQVRUU2VydmljZT4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZW1vdGVTZXJ2aWNlID0gYXdhaXQgZ2F0dCEuZ2V0UHJpbWFyeVNlcnZpY2Uoc2VydmljZSk7XG4gICAgICByZXR1cm4gYXdhaXQgUHJvbWlzZS5yZXNvbHZlKHJlbW90ZVNlcnZpY2UpO1xuICAgIH1cbiAgICBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgcmV0dXJuIGF3YWl0IFByb21pc2UucmVqZWN0KGAke2Vycm9yLm1lc3NhZ2V9ICgke3NlcnZpY2V9KWApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXF1ZXN0cyB0aGUgcHJpbWFyeSBzZXJ2aWNlLlxuICAgKlxuICAgKiBAcGFyYW0gZ2F0dCBUaGUgQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZlciBzZXZlclxuICAgKiBAcGFyYW0gc2VydmljZSBUaGUgVVVJRCBvZiB0aGUgcHJpbWFyeSBzZXJ2aWNlXG4gICAqIEByZXR1cm4gVGhlIHJlbW90ZSBzZXJ2aWNlIChhcyBhbiBvYnNlcnZhYmxlKS5cbiAgICovXG4gIGdldFByaW1hcnlTZXJ2aWNlJChnYXR0OiBCbHVldG9vdGhSZW1vdGVHQVRUU2VydmVyIHwgbnVsbCwgc2VydmljZTogQmx1ZXRvb3RoU2VydmljZVVVSUQpOiBPYnNlcnZhYmxlPEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2aWNlPiB7XG4gICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gR2V0dGluZyBwcmltYXJ5IHNlcnZpY2UgXCIlc1wiIChpZiBhdmFpbGFibGUpIG9mICVvJywgc2VydmljZSwgZ2F0dCk7XG5cblxuICAgIGlmIChnYXR0KSB7XG4gICAgICByZXR1cm4gZnJvbShcbiAgICAgICAgdGhpcy5nZXRQcmltYXJ5U2VydmljZShnYXR0LCBzZXJ2aWNlKVxuICAgICAgKTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICByZXR1cm4gdGhyb3dFcnJvcigoKSA9PiBuZXcgRXJyb3IoJ1tCTEU6OkVycm9yXSBXYXMgbm90IGFibGUgdG8gY29ubmVjdCB0byB0aGUgQmx1ZXRvb3RoIFJlbW90ZSBHQVRUIFNlcnZlcicpKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVxdWVzdHMgYSBjaGFyYWN0ZXJpc3RpYyBmcm9tIHRoZSBwcmltYXJ5IHNlcnZpY2UuXG4gICAqXG4gICAqIEBwYXJhbSBwcmltYXJ5U2VydmljZSBUaGUgcHJpbWFyeSBzZXJ2aWNlLlxuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgVGhlIGNoYXJhY3RlcmlzdGljJ3MgVVVJRC5cbiAgICogQHJldHVybnMgVGhlIGNoYXJhY3RlcmlzdGljIGRlc2NyaXB0aW9uIChhcyBhIFByb21pc2UpLlxuICAgKi9cbiAgYXN5bmMgZ2V0Q2hhcmFjdGVyaXN0aWMoXG4gICAgcHJpbWFyeVNlcnZpY2U6IEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2aWNlLFxuICAgIGNoYXJhY3RlcmlzdGljOiBCbHVldG9vdGhDaGFyYWN0ZXJpc3RpY1VVSURcbiAgKTogUHJvbWlzZTxCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMgfCBudWxsPiB7XG4gICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gR2V0dGluZyBDaGFyYWN0ZXJpc3RpYyBcIiVzXCIgb2YgJW8nLCBjaGFyYWN0ZXJpc3RpYywgcHJpbWFyeVNlcnZpY2UpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNoYXIgPSBhd2FpdCBwcmltYXJ5U2VydmljZS5nZXRDaGFyYWN0ZXJpc3RpYyhjaGFyYWN0ZXJpc3RpYyk7XG4gICAgICAvLyBsaXN0ZW4gZm9yIGNoYXJhY3RlcmlzdGljIHZhbHVlIGNoYW5nZXNcbiAgICAgIGlmIChjaGFyLnByb3BlcnRpZXMubm90aWZ5KSB7XG4gICAgICAgIGNoYXIuc3RhcnROb3RpZmljYXRpb25zKCkudGhlbihfID0+IHtcbiAgICAgICAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBTdGFydGluZyBub3RpZmljYXRpb25zIG9mIFwiJXNcIicsIGNoYXJhY3RlcmlzdGljKTtcbiAgICAgICAgICBjaGFyLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYXJhY3RlcmlzdGljdmFsdWVjaGFuZ2VkJywgdGhpcy5vbkNoYXJhY3RlcmlzdGljQ2hhbmdlZC5iaW5kKHRoaXMpKTtcbiAgICAgICAgfSwgKGVycm9yOiBET01FeGNlcHRpb24pID0+IHtcbiAgICAgICAgICBQcm9taXNlLnJlamVjdChgJHtlcnJvci5tZXNzYWdlfSAoJHtjaGFyYWN0ZXJpc3RpY30pYCk7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgZWxzZSB7XG4gICAgICAgIGNoYXIuYWRkRXZlbnRMaXN0ZW5lcignY2hhcmFjdGVyaXN0aWN2YWx1ZWNoYW5nZWQnLCB0aGlzLm9uQ2hhcmFjdGVyaXN0aWNDaGFuZ2VkLmJpbmQodGhpcykpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGNoYXI7XG4gICAgfVxuICAgIGNhdGNoIChyZWplY3Rpb25FcnJvcikge1xuICAgICAgUHJvbWlzZS5yZWplY3QoYCR7KHJlamVjdGlvbkVycm9yIGFzIGFueSkubWVzc2FnZX0gKCR7Y2hhcmFjdGVyaXN0aWN9KWApO1xuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlcXVlc3RzIGEgY2hhcmFjdGVyaXN0aWMgZnJvbSB0aGUgcHJpbWFyeSBzZXJ2aWNlLlxuICAgKlxuICAgKiBAcGFyYW0gcHJpbWFyeVNlcnZpY2UgVGhlIHByaW1hcnkgc2VydmljZS5cbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIFRoZSBjaGFyYWN0ZXJpc3RpYydzIFVVSUQuXG4gICAqIEByZXR1cm5zIFRoZSBjaGFyYWN0ZXJpc3RpYyBkZXNjcmlwdGlvbiAoYXMgYSBPYnNlcnZhYmxlKS5cbiAgICovXG4gIGdldENoYXJhY3RlcmlzdGljJChcbiAgICBwcmltYXJ5U2VydmljZTogQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZpY2UsXG4gICAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRFxuICApOiBPYnNlcnZhYmxlPG51bGwgfCBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWM+IHtcbiAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBHZXR0aW5nIENoYXJhY3RlcmlzdGljIFwiJXNcIiBvZiAlbycsIGNoYXJhY3RlcmlzdGljLCBwcmltYXJ5U2VydmljZSk7XG5cbiAgICByZXR1cm4gZnJvbSh0aGlzLmdldENoYXJhY3RlcmlzdGljKHByaW1hcnlTZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYykpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgdGhlIGNoYXJhY3RlcmlzdGljJ3Mgc3RhdGUuXG4gICAqXG4gICAqIEBwYXJhbSBzZXJ2aWNlIFRoZSBwYXJlbnQgc2VydmljZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWMuXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyBUaGUgcmVxdWVzdGVkIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBzdGF0ZSBBbiBBcnJheUJ1ZmZlciBjb250YWluaW5nIHRoZSB2YWx1ZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWMuXG4gICAqIEByZXR1cm4gVGhlIHByaW1hcnkgc2VydmljZSAodXNlZnVsIGZvciBjaGFpbmluZykuXG4gICAqL1xuICBzZXRDaGFyYWN0ZXJpc3RpY1N0YXRlKHNlcnZpY2U6IEJsdWV0b290aFNlcnZpY2VVVUlELCBjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoQ2hhcmFjdGVyaXN0aWNVVUlELCBzdGF0ZTogQXJyYXlCdWZmZXIpIHtcbiAgICBjb25zdCBwcmltYXJ5U2VydmljZSA9IHRoaXMuZ2V0UHJpbWFyeVNlcnZpY2UkKHRoaXMuZ2F0dFNlcnZlciwgc2VydmljZSk7XG5cbiAgICBwcmltYXJ5U2VydmljZVxuICAgICAgLnBpcGUobWVyZ2VNYXAoKF9wcmltYXJ5U2VydmljZTogQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZpY2UpID0+IHRoaXMuZ2V0Q2hhcmFjdGVyaXN0aWMkKF9wcmltYXJ5U2VydmljZSwgY2hhcmFjdGVyaXN0aWMpKSlcbiAgICAgIC5zdWJzY3JpYmUoKGNoYXJhY3RlcmlzdGljOiBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMgfCBudWxsKSA9PiB0aGlzLndyaXRlVmFsdWUkKGNoYXJhY3RlcmlzdGljLCBzdGF0ZSkpO1xuXG4gICAgcmV0dXJuIHByaW1hcnlTZXJ2aWNlO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuYWJsZXMgdGhlIHNwZWNpZmllZCBjaGFyYWN0ZXJpc3RpYyBvZiBhIGdpdmVuIHNlcnZpY2UuXG4gICAqXG4gICAqIEBwYXJhbSBzZXJ2aWNlIFRoZSBwYXJlbnQgc2VydmljZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWMuXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyBUaGUgcmVxdWVzdGVkIGNoYXJhY3RlcmlzdGljXG4gICAqIEByZXR1cm4gVGhlIHByaW1hcnkgc2VydmljZSAodXNlZnVsIGZvciBjaGFpbmluZykuXG4gICAqL1xuICBlbmFibGVDaGFyYWN0ZXJpc3RpYyhzZXJ2aWNlOiBCbHVldG9vdGhTZXJ2aWNlVVVJRCwgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRCwgc3RhdGU/OiBhbnkpIHtcbiAgICBzdGF0ZSA9IHN0YXRlIHx8IG5ldyBVaW50OEFycmF5KFsxXSk7XG4gICAgcmV0dXJuIHRoaXMuc2V0Q2hhcmFjdGVyaXN0aWNTdGF0ZShzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgc3RhdGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIERpc2FibGVzIHRoZSBzcGVjaWZpZWQgY2hhcmFjdGVyaXN0aWMgb2YgYSBnaXZlbiBzZXJ2aWNlLlxuICAgKlxuICAgKiBAcGFyYW0gc2VydmljZSBUaGUgcGFyZW50IHNlcnZpY2Ugb2YgdGhlIGNoYXJhY3RlcmlzdGljLlxuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgVGhlIHJlcXVlc3RlZCBjaGFyYWN0ZXJpc3RpYy5cbiAgICogQHJldHVybiBUaGUgcHJpbWFyeSBzZXJ2aWNlICh1c2VmdWwgZm9yIGNoYWluaW5nKS5cbiAgICovXG4gIGRpc2JhbGVDaGFyYWN0ZXJpc3RpYyhzZXJ2aWNlOiBCbHVldG9vdGhTZXJ2aWNlVVVJRCwgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRCwgc3RhdGU/OiBhbnkpIHtcbiAgICBzdGF0ZSA9IHN0YXRlIHx8IG5ldyBVaW50OEFycmF5KFswXSk7XG4gICAgcmV0dXJuIHRoaXMuc2V0Q2hhcmFjdGVyaXN0aWNTdGF0ZShzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgc3RhdGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIERpc3BhdGNoZXMgbmV3IHZhbHVlcyBlbWl0dGVkIGJ5IGEgY2hhcmFjdGVyaXN0aWMuXG4gICAqXG4gICAqIEBwYXJhbSBldmVudCB0aGUgZGlzdHBhdGNoZWQgZXZlbnQuXG4gICAqL1xuICBvbkNoYXJhY3RlcmlzdGljQ2hhbmdlZChldmVudDogRXZlbnQpIHtcbiAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBEaXNwYXRjaGluZyBuZXcgY2hhcmFjdGVyaXN0aWMgdmFsdWUgJW8nLCBldmVudCk7XG5cbiAgICBjb25zdCB2YWx1ZSA9IChldmVudC50YXJnZXQgYXMgQmx1ZXRvb3RoUmVtb3RlR0FUVENoYXJhY3RlcmlzdGljKS52YWx1ZTtcbiAgICB0aGlzLmNoYXJhY3RlcmlzdGljVmFsdWVDaGFuZ2VzJC5lbWl0KHZhbHVlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWFkcyBhIHZhbHVlIGZyb20gdGhlIGNoYXJhY3RlcmlzdGljcywgYXMgYSBEYXRhVmlldy5cbiAgICpcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIFRoZSByZXF1ZXN0ZWQgY2hhcmFjdGVyaXN0aWMuXG4gICAqIEByZXR1cm4gdGhlIERhdGFWaWV3IHZhbHVlIChhcyBhbiBPYnNlcnZhYmxlKS5cbiAgICovXG4gIHJlYWRWYWx1ZSQoY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYyk6IE9ic2VydmFibGU8RGF0YVZpZXc+IHtcbiAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBSZWFkaW5nIENoYXJhY3RlcmlzdGljICVvJywgY2hhcmFjdGVyaXN0aWMpO1xuXG4gICAgcmV0dXJuIGZyb20oXG4gICAgICBjaGFyYWN0ZXJpc3RpY1xuICAgICAgICAucmVhZFZhbHVlKClcbiAgICAgICAgLnRoZW4oKGRhdGE6IERhdGFWaWV3KSA9PiBQcm9taXNlLnJlc29sdmUoZGF0YSksIChlcnJvcjogRE9NRXhjZXB0aW9uKSA9PiBQcm9taXNlLnJlamVjdChgJHtlcnJvci5tZXNzYWdlfWApKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogV3JpdGVzIGEgdmFsdWUgaW50byB0aGUgc3BlY2lmaWVkIGNoYXJhY3RlcmlzdGljLlxuICAgKlxuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgVGhlIHJlcXVlc3RlZCBjaGFyYWN0ZXJpc3RpYy5cbiAgICogQHBhcmFtIHZhbHVlIFRoZSB2YWx1ZSB0byBiZSB3cml0dGVuIChhcyBhbiBBcnJheUJ1ZmZlciBvciBVaW50OEFycmF5KS5cbiAgICogQHJldHVybiBhbiB2b2lkIE9ic2VydmFibGUuXG4gICAqL1xuICB3cml0ZVZhbHVlJChjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoUmVtb3RlR0FUVENoYXJhY3RlcmlzdGljIHwgbnVsbCwgdmFsdWU6IEFycmF5QnVmZmVyIHwgVWludDhBcnJheSkge1xuXG4gICAgaWYgKGNoYXJhY3RlcmlzdGljID09PSBudWxsKSB7XG4gICAgICB0aGlzLmNvbnNvbGUuZXJyb3IoJ1tCTEU6OkVycm9yXSBXYXMgbm90IGFibGUgdG8gd3JpdGUgY2hhcmFjdGVyaXN0aWMnKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIHRoaXMuY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIFdyaXRpbmcgQ2hhcmFjdGVyaXN0aWMgJW8nLCBjaGFyYWN0ZXJpc3RpYyk7XG5cbiAgICByZXR1cm4gZnJvbShjaGFyYWN0ZXJpc3RpYy53cml0ZVZhbHVlKHZhbHVlKS50aGVuKF8gPT4gUHJvbWlzZS5yZXNvbHZlKCksIChlcnJvcjogRE9NRXhjZXB0aW9uKSA9PiBQcm9taXNlLnJlamVjdChgJHtlcnJvci5tZXNzYWdlfWApKSk7XG4gIH1cblxuICAvKipcbiAgICogQSBzdHJlYW0gb2YgRGF0YVZpZXcgdmFsdWVzIGVtaXR0ZWQgYnkgdGhlIHNwZWNpZmllZCBjaGFyYWN0ZXJpc3RpYy5cbiAgICpcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIFRoZSBjaGFyYWN0ZXJpc3RpYyB3aGljaCB2YWx1ZSB5b3Ugd2FudCB0byBvYnNlcnZlXG4gICAqIEByZXR1cm4gVGhlIHN0cmVhbSBvZiBEYXRhVmlldyB2YWx1ZXMuXG4gICAqL1xuICBvYnNlcnZlVmFsdWUkKGNoYXJhY3RlcmlzdGljOiBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMpOiBPYnNlcnZhYmxlPERhdGFWaWV3PiB7XG5cbiAgICBpZiAoY2hhcmFjdGVyaXN0aWMgPT09IG51bGwgfHwgdHlwZW9mIGNoYXJhY3RlcmlzdGljLnNlcnZpY2UgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB0aGlzLmNvbnNvbGUuZXJyb3IoJ1tCTEU6OkVycm9yXSBXYXMgbm90IGFibGUgdG8gcmVhZCBjaGFyYWN0ZXJpc3RpYycpO1xuICAgICAgcmV0dXJuIEVNUFRZO1xuICAgIH1cblxuICAgIGNoYXJhY3RlcmlzdGljLnN0YXJ0Tm90aWZpY2F0aW9ucygpO1xuICAgIGNvbnN0IGRpc2Nvbm5lY3RlZCA9IGZyb21FdmVudChjaGFyYWN0ZXJpc3RpYy5zZXJ2aWNlLmRldmljZSwgJ2dhdHRzZXJ2ZXJkaXNjb25uZWN0ZWQnKTtcbiAgICByZXR1cm4gZnJvbUV2ZW50KGNoYXJhY3RlcmlzdGljLCAnY2hhcmFjdGVyaXN0aWN2YWx1ZWNoYW5nZWQnKVxuICAgICAgLnBpcGUoXG4gICAgICAgIG1hcCgoZXZlbnQ6IEV2ZW50KSA9PiAoZXZlbnQudGFyZ2V0IGFzIEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYykudmFsdWUgYXMgRGF0YVZpZXcpLFxuICAgICAgICB0YWtlVW50aWwoZGlzY29ubmVjdGVkKVxuICAgICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBIHV0aWxpdHkgbWV0aG9kIHRvIGNvbnZlcnQgTEUgdG8gYW4gdW5zaWduZWQgMTYtYml0IGludGVnZXIgdmFsdWVzLlxuICAgKlxuICAgKiBAcGFyYW0gZGF0YSBUaGUgRGF0YVZpZXcgYmluYXJ5IGRhdGEuXG4gICAqIEBwYXJhbSBieXRlT2Zmc2V0IFRoZSBvZmZzZXQsIGluIGJ5dGUsIGZyb20gdGhlIHN0YXJ0IG9mIHRoZSB2aWV3IHdoZXJlIHRvIHJlYWQgdGhlIGRhdGEuXG4gICAqIEByZXR1cm4gQW4gdW5zaWduZWQgMTYtYml0IGludGVnZXIgbnVtYmVyLlxuICAgKi9cbiAgbGl0dGxlRW5kaWFuVG9VaW50MTYoZGF0YTogYW55LCBieXRlT2Zmc2V0OiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiAodGhpcy5saXR0bGVFbmRpYW5Ub1VpbnQ4KGRhdGEsIGJ5dGVPZmZzZXQgKyAxKSA8PCA4KSArIHRoaXMubGl0dGxlRW5kaWFuVG9VaW50OChkYXRhLCBieXRlT2Zmc2V0KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBIHV0aWxpdHkgbWV0aG9kIHRvIGNvbnZlcnQgTEUgdG8gYW4gdW5zaWduZWQgOC1iaXQgaW50ZWdlciB2YWx1ZXMuXG4gICAqXG4gICAqIEBwYXJhbSBkYXRhIFRoZSBEYXRhVmlldyBiaW5hcnkgZGF0YS5cbiAgICogQHBhcmFtIGJ5dGVPZmZzZXQgVGhlIG9mZnNldCwgaW4gYnl0ZSwgZnJvbSB0aGUgc3RhcnQgb2YgdGhlIHZpZXcgd2hlcmUgdG8gcmVhZCB0aGUgZGF0YS5cbiAgICogQHJldHVybiBBbiB1bnNpZ25lZCA4LWJpdCBpbnRlZ2VyIG51bWJlci5cbiAgICovXG4gIGxpdHRsZUVuZGlhblRvVWludDgoZGF0YTogYW55LCBieXRlT2Zmc2V0OiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiBkYXRhLmdldFVpbnQ4KGJ5dGVPZmZzZXQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlbmRzIHJhbmRvbSBkYXRhIChmb3IgdGVzdGluZyBwdXJwb3NlcyBvbmx5KS5cbiAgICpcbiAgICogQHJldHVybiBSYW5kb20gdW5zaWduZWQgOC1iaXQgaW50ZWdlciB2YWx1ZXMuXG4gICAqL1xuICBmYWtlTmV4dChmYWtlVmFsdWU/OiAoKSA9PiBEYXRhVmlldykge1xuICAgIGlmIChmYWtlVmFsdWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgZmFrZVZhbHVlID0gKCkgPT4ge1xuICAgICAgICBjb25zdCBkdiA9IG5ldyBEYXRhVmlldyhuZXcgQXJyYXlCdWZmZXIoOCkpO1xuICAgICAgICBkdi5zZXRVaW50OCgwLCAoTWF0aC5yYW5kb20oKSAqIDExMCkgfCAwKTtcbiAgICAgICAgcmV0dXJuIGR2O1xuICAgICAgfTtcbiAgICB9XG5cbiAgICB0aGlzLmNoYXJhY3RlcmlzdGljVmFsdWVDaGFuZ2VzJC5lbWl0KGZha2VWYWx1ZSgpKTtcbiAgfVxufVxuIl19