UNPKG

@manekinekko/angular-web-bluetooth

Version:
363 lines 47.9 kB
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmx1ZXRvb3RoLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9tYW5la2luZWtrby9hbmd1bGFyLXdlYi1ibHVldG9vdGgvc3JjL2xpYi9ibHVldG9vdGguc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsWUFBWSxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6RCxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQWtCLFVBQVUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMxRSxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7Ozs7QUFjbEUsTUFBTSxPQUFPLGFBQWE7SUFNeEIsWUFBNkIsTUFBMkIsRUFBbUIsT0FBNkI7UUFBM0UsV0FBTSxHQUFOLE1BQU0sQ0FBcUI7UUFBbUIsWUFBTyxHQUFQLE9BQU8sQ0FBc0I7UUFFdEcsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLFlBQVksRUFBbUIsQ0FBQztRQUNuRCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksWUFBWSxFQUE2QixDQUFDO1FBQzNELElBQUksQ0FBQywyQkFBMkIsR0FBRyxJQUFJLFlBQVksRUFBWSxDQUFDO1FBRWhFLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDO0lBQ3pCLENBQUM7SUFFRCxVQUFVO1FBQ1IsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDO0lBQ3RCLENBQUM7SUFFRCxRQUFRO1FBQ04sT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDO0lBQ3BCLENBQUM7SUFFRCxhQUFhO1FBQ1gsT0FBTyxJQUFJLENBQUMsMkJBQTJCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssSUFBSSxLQUFLLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDL0YsQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxLQUFLLENBQUMsT0FBeUI7UUFDbkMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkNBQTJDLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFdkUsSUFBSSxPQUFPLE9BQU8sQ0FBQyxnQkFBZ0IsS0FBSyxXQUFXLEVBQUUsQ0FBQztZQUNwRCxPQUFPLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDO1FBQ2xDLENBQUM7UUFFRCxJQUFJLE9BQU8sT0FBTyxDQUFDLGdCQUFnQixLQUFLLFdBQVcsRUFBRSxDQUFDO1lBQ3BELE9BQU8sQ0FBQyxnQkFBZ0IsR0FBRyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMvQyxDQUFDO2FBQ0ksQ0FBQztZQUNKLE9BQU8sQ0FBQyxnQkFBZ0IsR0FBRyxDQUFDLEdBQUcsT0FBTyxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDM0QsQ0FBQztRQUVELElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLDJDQUEyQyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRXZFLElBQUksQ0FBQztZQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQztnQkFDakMsZ0JBQWdCLEVBQUUsT0FBTyxDQUFDLGdCQUFnQjtnQkFDMUMsZ0JBQWdCLEVBQUUsT0FBTyxDQUFDLGdCQUFnQjthQUMzQyxDQUFvQixDQUFDO1lBQ3RCLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLDRCQUE0QixFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBRXZELE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUM5QyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQywwQkFBMEIsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUVuRCxNQUFNLGNBQWMsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLE9BQU8sQ0FBK0IsQ0FBQztZQUN6RyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQ0FBcUMsRUFBRSxjQUFjLENBQUMsQ0FBQztZQUV4RSxNQUFNLGNBQWMsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxjQUFjLEVBQUUsT0FBTyxDQUFDLGNBQWMsQ0FBc0MsQ0FBQztZQUNqSSxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQ0FBb0MsRUFBRSxjQUFjLENBQUMsQ0FBQztZQUV2RSxNQUFNLEtBQUssR0FBRyxNQUFNLGNBQWMsQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUMvQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQywyQkFBMkIsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUVyRCxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFDRCxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2IsTUFBTSxJQUFJLEtBQUssQ0FBQyxLQUFZLENBQUMsQ0FBQztRQUNoQyxDQUFDO0lBQ0gsQ0FBQztJQUVELE1BQU0sQ0FBQyxPQUF5QjtRQUM5QixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDbkMsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsS0FBSyxDQUFDLFFBQVEsQ0FBQyxVQUFnQyxFQUEwQjtRQUN2RSxPQUFPLENBQUMsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLGdCQUFnQixJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUUxRSxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnREFBZ0QsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUU1RSxJQUFJLE1BQU0sR0FBRyxJQUFJLENBQUM7UUFDbEIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDbEQsTUFBTSxDQUFDLGdCQUFnQixDQUFDLHdCQUF3QixFQUFFLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztZQUV4RixJQUFJLE1BQU0sRUFBRSxDQUFDO2dCQUNYLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQzVCLENBQUM7aUJBQ0ksQ0FBQztnQkFDSixJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxtRUFBbUUsQ0FBQyxDQUFDO1lBQzFGLENBQUM7UUFFSCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzVCLENBQUM7UUFFRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILG9CQUFvQixDQUFDLEtBQVk7UUFDL0IsTUFBTSxrQkFBa0IsR0FBRyxLQUFLLENBQUMsTUFBeUIsQ0FBQztRQUMzRCxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQ0FBb0MsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBRTNFLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQy9CLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILFNBQVMsQ0FBQyxPQUE4QjtRQUN0QyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLE1BQTRCLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BILENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsS0FBSyxDQUFDLGFBQWEsQ0FBQyxNQUE4QjtRQUVoRCxJQUFJLE1BQU0sS0FBSyxJQUFJLElBQUksT0FBTyxNQUFNLENBQUMsSUFBSSxLQUFLLFdBQVcsRUFBRSxDQUFDO1lBQzFELElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLHNFQUFzRSxDQUFDLENBQUM7WUFDM0YsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDdkIsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBRUQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsOERBQThELEVBQUUsTUFBTSxDQUFDLENBQUM7UUFFekYsSUFBSSxDQUFDO1lBQ0gsTUFBTSxVQUFVLEdBQUcsTUFBTSxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQy9DLElBQUksQ0FBQyxVQUFVLEdBQUcsVUFBVSxDQUFDO1lBQzdCLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQzVCLE9BQU8sVUFBVSxDQUFDO1FBQ3BCLENBQUM7UUFBQyxPQUFPLEtBQVUsRUFBRSxDQUFDO1lBQ3BCLCtDQUErQztZQUMvQyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDbkMsSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUN2QyxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILGNBQWMsQ0FBQyxNQUE4QjtRQUMzQyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDMUMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsZ0JBQWdCO1FBQ2QsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNyQixPQUFPO1FBQ1QsQ0FBQztRQUNELElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLG9EQUFvRCxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUV4RixJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDOUIsSUFBSSxDQUFDLFVBQVUsQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUMvQixDQUFDO2FBQU0sQ0FBQztZQUNOLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLHNEQUFzRCxDQUFDLENBQUM7UUFDM0UsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxLQUFLLENBQUMsaUJBQWlCLENBQUMsSUFBc0MsRUFBRSxPQUE2QjtRQUMzRixJQUFJLENBQUM7WUFDSCxNQUFNLGFBQWEsR0FBRyxNQUFNLElBQUssQ0FBQyxpQkFBaUIsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUM3RCxPQUFPLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUM5QyxDQUFDO1FBQ0QsT0FBTyxLQUFVLEVBQUUsQ0FBQztZQUNsQixPQUFPLE1BQU0sT0FBTyxDQUFDLE1BQU0sQ0FBQyxHQUFHLEtBQUssQ0FBQyxPQUFPLEtBQUssT0FBTyxHQUFHLENBQUMsQ0FBQztRQUMvRCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILGtCQUFrQixDQUFDLElBQXNDLEVBQUUsT0FBNkI7UUFDdEYsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsK0RBQStELEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDO1FBR2pHLElBQUksSUFBSSxFQUFFLENBQUM7WUFDVCxPQUFPLElBQUksQ0FDVCxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUN0QyxDQUFDO1FBQ0osQ0FBQzthQUNJLENBQUM7WUFDSixPQUFPLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLEtBQUssQ0FBQywwRUFBMEUsQ0FBQyxDQUFDLENBQUM7UUFDakgsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxLQUFLLENBQUMsaUJBQWlCLENBQ3JCLGNBQTBDLEVBQzFDLGNBQTJDO1FBRTNDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLCtDQUErQyxFQUFFLGNBQWMsRUFBRSxjQUFjLENBQUMsQ0FBQztRQUVsRyxJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksR0FBRyxNQUFNLGNBQWMsQ0FBQyxpQkFBaUIsQ0FBQyxjQUFjLENBQUMsQ0FBQztZQUNwRSwwQ0FBMEM7WUFDMUMsSUFBSSxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUMzQixJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUU7b0JBQ2pDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLDRDQUE0QyxFQUFFLGNBQWMsQ0FBQyxDQUFDO29CQUMvRSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsNEJBQTRCLEVBQUUsSUFBSSxDQUFDLHVCQUF1QixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO2dCQUMvRixDQUFDLEVBQUUsQ0FBQyxLQUFtQixFQUFFLEVBQUU7b0JBQ3pCLE9BQU8sQ0FBQyxNQUFNLENBQUMsR0FBRyxLQUFLLENBQUMsT0FBTyxLQUFLLGNBQWMsR0FBRyxDQUFDLENBQUM7Z0JBQ3pELENBQUMsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztpQkFDSSxDQUFDO2dCQUNKLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyw0QkFBNEIsRUFBRSxJQUFJLENBQUMsdUJBQXVCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDL0YsQ0FBQztZQUNELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUNELE9BQU8sY0FBYyxFQUFFLENBQUM7WUFDdEIsT0FBTyxDQUFDLE1BQU0sQ0FBQyxHQUFJLGNBQXNCLENBQUMsT0FBTyxLQUFLLGNBQWMsR0FBRyxDQUFDLENBQUM7UUFDM0UsQ0FBQztRQUVELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILGtCQUFrQixDQUNoQixjQUEwQyxFQUMxQyxjQUEyQztRQUUzQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQywrQ0FBK0MsRUFBRSxjQUFjLEVBQUUsY0FBYyxDQUFDLENBQUM7UUFFbEcsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGNBQWMsRUFBRSxjQUFjLENBQUMsQ0FBQyxDQUFDO0lBQ3RFLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsc0JBQXNCLENBQUMsT0FBNkIsRUFBRSxjQUEyQyxFQUFFLEtBQWtCO1FBQ25ILE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRXpFLGNBQWM7YUFDWCxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsZUFBMkMsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLGVBQWUsRUFBRSxjQUFjLENBQUMsQ0FBQyxDQUFDO2FBQ3pILFNBQVMsQ0FBQyxDQUFDLGNBQXdELEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsY0FBYyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFFcEgsT0FBTyxjQUFjLENBQUM7SUFDeEIsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILG9CQUFvQixDQUFDLE9BQTZCLEVBQUUsY0FBMkMsRUFBRSxLQUFXO1FBQzFHLEtBQUssR0FBRyxLQUFLLElBQUksSUFBSSxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3JDLE9BQU8sSUFBSSxDQUFDLHNCQUFzQixDQUFDLE9BQU8sRUFBRSxjQUFjLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILHFCQUFxQixDQUFDLE9BQTZCLEVBQUUsY0FBMkMsRUFBRSxLQUFXO1FBQzNHLEtBQUssR0FBRyxLQUFLLElBQUksSUFBSSxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3JDLE9BQU8sSUFBSSxDQUFDLHNCQUFzQixDQUFDLE9BQU8sRUFBRSxjQUFjLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCx1QkFBdUIsQ0FBQyxLQUFZO1FBQ2xDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLHFEQUFxRCxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBRS9FLE1BQU0sS0FBSyxHQUFJLEtBQUssQ0FBQyxNQUE0QyxDQUFDLEtBQUssQ0FBQztRQUN4RSxJQUFJLENBQUMsMkJBQTJCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQy9DLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILFVBQVUsQ0FBQyxjQUFpRDtRQUMxRCxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1Q0FBdUMsRUFBRSxjQUFjLENBQUMsQ0FBQztRQUUxRSxPQUFPLElBQUksQ0FDVCxjQUFjO2FBQ1gsU0FBUyxFQUFFO2FBQ1gsSUFBSSxDQUFDLENBQUMsSUFBYyxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsS0FBbUIsRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxHQUFHLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQ2hILENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsV0FBVyxDQUFDLGNBQXdELEVBQUUsS0FBK0I7UUFFbkcsSUFBSSxjQUFjLEtBQUssSUFBSSxFQUFFLENBQUM7WUFDNUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsbURBQW1ELENBQUMsQ0FBQztZQUN4RSxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFFRCxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyx1Q0FBdUMsRUFBRSxjQUFjLENBQUMsQ0FBQztRQUUxRSxPQUFPLElBQUksQ0FBQyxjQUFjLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLEtBQW1CLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsR0FBRyxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDMUksQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsYUFBYSxDQUFDLGNBQWlEO1FBRTdELElBQUksY0FBYyxLQUFLLElBQUksSUFBSSxPQUFPLGNBQWMsQ0FBQyxPQUFPLEtBQUssV0FBVyxFQUFFLENBQUM7WUFDN0UsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsa0RBQWtELENBQUMsQ0FBQztZQUN2RSxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFFRCxjQUFjLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUNwQyxNQUFNLFlBQVksR0FBRyxTQUFTLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsd0JBQXdCLENBQUMsQ0FBQztRQUN4RixPQUFPLFNBQVMsQ0FBQyxjQUFjLEVBQUUsNEJBQTRCLENBQUM7YUFDM0QsSUFBSSxDQUNILEdBQUcsQ0FBQyxDQUFDLEtBQVksRUFBRSxFQUFFLENBQUUsS0FBSyxDQUFDLE1BQTRDLENBQUMsS0FBaUIsQ0FBQyxFQUM1RixTQUFTLENBQUMsWUFBWSxDQUFDLENBQ3hCLENBQUM7SUFDTixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsb0JBQW9CLENBQUMsSUFBUyxFQUFFLFVBQWtCO1FBQ2hELE9BQU8sQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxFQUFFLFVBQVUsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQzVHLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxtQkFBbUIsQ0FBQyxJQUFTLEVBQUUsVUFBa0I7UUFDL0MsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ25DLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsUUFBUSxDQUFDLFNBQTBCO1FBQ2pDLElBQUksU0FBUyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQzVCLFNBQVMsR0FBRyxHQUFHLEVBQUU7Z0JBQ2YsTUFBTSxFQUFFLEdBQUcsSUFBSSxRQUFRLENBQUMsSUFBSSxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDNUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQzFDLE9BQU8sRUFBRSxDQUFDO1lBQ1osQ0FBQyxDQUFDO1FBQ0osQ0FBQztRQUVELElBQUksQ0FBQywyQkFBMkIsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUMsQ0FBQztJQUNyRCxDQUFDOzhHQXJhVSxhQUFhO2tIQUFiLGFBQWEsY0FGWixNQUFNOzsyRkFFUCxhQUFhO2tCQUh6QixVQUFVO21CQUFDO29CQUNWLFVBQVUsRUFBRSxNQUFNO2lCQUNuQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEV2ZW50RW1pdHRlciwgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRU1QVFksIGZyb20sIGZyb21FdmVudCwgT2JzZXJ2YWJsZSwgb2YsIHRocm93RXJyb3IgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGZpbHRlciwgbWFwLCBtZXJnZU1hcCwgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgQ29uc29sZUxvZ2dlclNlcnZpY2UgfSBmcm9tICcuL2xvZ2dlci5zZXJ2aWNlJztcbmltcG9ydCB7IEJyb3dzZXJXZWJCbHVldG9vdGggfSBmcm9tICcuL3BsYXRmb3JtL2Jyb3dzZXInO1xuXG50eXBlIFJlYWRWYWx1ZU9wdGlvbnMgPSB7XG4gIGFjY2VwdEFsbERldmljZXM/OiBib29sZWFuO1xuICBvcHRpb25hbFNlcnZpY2VzPzogQmx1ZXRvb3RoU2VydmljZVVVSURbXTtcbiAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRCxcbiAgc2VydmljZTogQmx1ZXRvb3RoU2VydmljZVVVSUQsXG59O1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBCbHVldG9vdGhDb3JlIHtcbiAgcHJpdmF0ZSBkZXZpY2UkOiBFdmVudEVtaXR0ZXI8Qmx1ZXRvb3RoRGV2aWNlPjtcbiAgcHJpdmF0ZSBnYXR0JDogRXZlbnRFbWl0dGVyPEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2ZXI+O1xuICBwcml2YXRlIGNoYXJhY3RlcmlzdGljVmFsdWVDaGFuZ2VzJDogRXZlbnRFbWl0dGVyPERhdGFWaWV3PjtcbiAgcHJpdmF0ZSBnYXR0U2VydmVyOiBCbHVldG9vdGhSZW1vdGVHQVRUU2VydmVyIHwgbnVsbDtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IHdlYkJsZTogQnJvd3NlcldlYkJsdWV0b290aCwgcHJpdmF0ZSByZWFkb25seSBjb25zb2xlOiBDb25zb2xlTG9nZ2VyU2VydmljZSkge1xuXG4gICAgdGhpcy5kZXZpY2UkID0gbmV3IEV2ZW50RW1pdHRlcjxCbHVldG9vdGhEZXZpY2U+KCk7XG4gICAgdGhpcy5nYXR0JCA9IG5ldyBFdmVudEVtaXR0ZXI8Qmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZlcj4oKTtcbiAgICB0aGlzLmNoYXJhY3RlcmlzdGljVmFsdWVDaGFuZ2VzJCA9IG5ldyBFdmVudEVtaXR0ZXI8RGF0YVZpZXc+KCk7XG5cbiAgICB0aGlzLmdhdHRTZXJ2ZXIgPSBudWxsO1xuICB9XG5cbiAgZ2V0RGV2aWNlJCgpOiBPYnNlcnZhYmxlPEJsdWV0b290aERldmljZSB8IG51bGw+IHtcbiAgICByZXR1cm4gdGhpcy5kZXZpY2UkO1xuICB9XG5cbiAgZ2V0R0FUVCQoKTogT2JzZXJ2YWJsZTxCbHVldG9vdGhSZW1vdGVHQVRUU2VydmVyPiB7XG4gICAgcmV0dXJuIHRoaXMuZ2F0dCQ7XG4gIH1cblxuICBzdHJlYW1WYWx1ZXMkKCk6IE9ic2VydmFibGU8RGF0YVZpZXc+IHtcbiAgICByZXR1cm4gdGhpcy5jaGFyYWN0ZXJpc3RpY1ZhbHVlQ2hhbmdlcyQucGlwZShmaWx0ZXIodmFsdWUgPT4gdmFsdWUgJiYgdmFsdWUuYnl0ZUxlbmd0aCA+IDApKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW4gdGhlIGRpc2NvdmVyeSBwcm9jZXNzIGFuZCByZWFkIHRoZSB2YWx1ZSBmb3JtIHRoZSBwcm92aWRlZCBzZXJ2aWNlIGFuZCBjaGFyYWN0ZXJpc3RpY1xuICAgKiBAcGFyYW0gb3B0aW9ucyB0aGUgUmVhZFZhbHVlT3B0aW9uc1xuICAgKi9cbiAgYXN5bmMgdmFsdWUob3B0aW9uczogUmVhZFZhbHVlT3B0aW9ucykge1xuICAgIHRoaXMuY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIFJlYWRpbmcgdmFsdWUgd2l0aCBvcHRpb25zICVvJywgb3B0aW9ucyk7XG5cbiAgICBpZiAodHlwZW9mIG9wdGlvbnMuYWNjZXB0QWxsRGV2aWNlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIG9wdGlvbnMuYWNjZXB0QWxsRGV2aWNlcyA9IHRydWU7XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBvcHRpb25zLm9wdGlvbmFsU2VydmljZXMgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICBvcHRpb25zLm9wdGlvbmFsU2VydmljZXMgPSBbb3B0aW9ucy5zZXJ2aWNlXTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICBvcHRpb25zLm9wdGlvbmFsU2VydmljZXMgPSBbLi4ub3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzXTtcbiAgICB9XG5cbiAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBSZWFkaW5nIHZhbHVlIHdpdGggb3B0aW9ucyAlbycsIG9wdGlvbnMpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGRldmljZSA9IGF3YWl0IHRoaXMuZGlzY292ZXIoe1xuICAgICAgICBhY2NlcHRBbGxEZXZpY2VzOiBvcHRpb25zLmFjY2VwdEFsbERldmljZXMsXG4gICAgICAgIG9wdGlvbmFsU2VydmljZXM6IG9wdGlvbnMub3B0aW9uYWxTZXJ2aWNlc1xuICAgICAgfSkgYXMgQmx1ZXRvb3RoRGV2aWNlO1xuICAgICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gRGV2aWNlIGluZm8gJW8nLCBkZXZpY2UpO1xuXG4gICAgICBjb25zdCBnYXR0ID0gYXdhaXQgdGhpcy5jb25uZWN0RGV2aWNlKGRldmljZSk7XG4gICAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBHQVRUIGluZm8gJW8nLCBnYXR0KTtcblxuICAgICAgY29uc3QgcHJpbWFyeVNlcnZpY2UgPSBhd2FpdCB0aGlzLmdldFByaW1hcnlTZXJ2aWNlKGdhdHQsIG9wdGlvbnMuc2VydmljZSkgYXMgQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZpY2U7XG4gICAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBQcmltYXJ5IFNlcnZpY2UgaW5mbyAlbycsIHByaW1hcnlTZXJ2aWNlKTtcblxuICAgICAgY29uc3QgY2hhcmFjdGVyaXN0aWMgPSBhd2FpdCB0aGlzLmdldENoYXJhY3RlcmlzdGljKHByaW1hcnlTZXJ2aWNlLCBvcHRpb25zLmNoYXJhY3RlcmlzdGljKSBhcyBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWM7XG4gICAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBDaGFyYWN0ZXJpc3RpYyBpbmZvICVvJywgY2hhcmFjdGVyaXN0aWMpO1xuXG4gICAgICBjb25zdCB2YWx1ZSA9IGF3YWl0IGNoYXJhY3RlcmlzdGljLnJlYWRWYWx1ZSgpO1xuICAgICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gVmFsdWUgaW5mbyAlbycsIHZhbHVlKTtcblxuICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIH1cbiAgICBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihlcnJvciBhcyBhbnkpO1xuICAgIH1cbiAgfVxuXG4gIHZhbHVlJChvcHRpb25zOiBSZWFkVmFsdWVPcHRpb25zKSB7XG4gICAgcmV0dXJuIGZyb20odGhpcy52YWx1ZShvcHRpb25zKSk7XG4gIH1cblxuICAvKipcbiAgICogUnVuIHRoZSBkaXNjb3ZlcnkgcHJvY2Vzcy5cbiAgICpcbiAgICogQHBhcmFtIE9wdGlvbnMgc3VjaCBhcyBmaWx0ZXJzIGFuZCBvcHRpb25hbCBzZXJ2aWNlc1xuICAgKiBAcmV0dXJuICBUaGUgR0FUVCBzZXJ2ZXIgZm9yIHRoZSBjaG9zZW4gZGV2aWNlXG4gICAqL1xuICBhc3luYyBkaXNjb3ZlcihvcHRpb25zOiBSZXF1ZXN0RGV2aWNlT3B0aW9ucyA9IHt9IGFzIFJlcXVlc3REZXZpY2VPcHRpb25zKTogUHJvbWlzZTxCbHVldG9vdGhEZXZpY2UgfCBudWxsPiB7XG4gICAgb3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzID0gb3B0aW9ucy5vcHRpb25hbFNlcnZpY2VzIHx8IFsnZ2VuZXJpY19hY2Nlc3MnXTtcblxuICAgIHRoaXMuY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIFJlcXVlc3RpbmcgZGV2aWNlcyB3aXRoIG9wdGlvbnMgJW8nLCBvcHRpb25zKTtcblxuICAgIGxldCBkZXZpY2UgPSBudWxsO1xuICAgIHRyeSB7XG4gICAgICBkZXZpY2UgPSBhd2FpdCB0aGlzLndlYkJsZS5yZXF1ZXN0RGV2aWNlKG9wdGlvbnMpO1xuICAgICAgZGV2aWNlLmFkZEV2ZW50TGlzdGVuZXIoJ2dhdHRzZXJ2ZXJkaXNjb25uZWN0ZWQnLCB0aGlzLm9uRGV2aWNlRGlzY29ubmVjdGVkLmJpbmQodGhpcykpO1xuXG4gICAgICBpZiAoZGV2aWNlKSB7XG4gICAgICAgIHRoaXMuZGV2aWNlJC5lbWl0KGRldmljZSk7XG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgdGhpcy5kZXZpY2UkLmVycm9yKGBbQkxFOjpFcnJvcl0gQ2FuIG5vdCBnZXQgdGhlIEJsdWV0b290aCBSZW1vdGUgR0FUVCBTZXJ2ZXIuIEFib3J0LmApO1xuICAgICAgfVxuXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHRoaXMuY29uc29sZS5lcnJvcihlcnJvcik7XG4gICAgfVxuXG4gICAgcmV0dXJuIGRldmljZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGlzIGhhbmRsZXIgd2lsbCB0cmlnZ2VyIHdoZW4gdGhlIGNsaWVudCBkaXNjb25uZXRzIGZyb20gdGhlIHNlcnZlci5cbiAgICpcbiAgICogQHBhcmFtIGV2ZW50IFRoZSBvbkRldmljZURpc2Nvbm5lY3RlZCBldmVudFxuICAgKi9cbiAgb25EZXZpY2VEaXNjb25uZWN0ZWQoZXZlbnQ6IEV2ZW50KSB7XG4gICAgY29uc3QgZGlzY29ubmVjdGVkRGV2aWNlID0gZXZlbnQudGFyZ2V0IGFzIEJsdWV0b290aERldmljZTtcbiAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBkaXNjb25uZWN0ZWQgZGV2aWNlICVvJywgZGlzY29ubmVjdGVkRGV2aWNlKTtcblxuICAgIHRoaXMuZGV2aWNlJC5lbWl0KHVuZGVmaW5lZCk7XG4gIH1cblxuICAvKipcbiAgICogUnVuIHRoZSBkaXNjb3ZlcnkgcHJvY2Vzcy5cbiAgICpcbiAgICogQHBhcmFtIE9wdGlvbnMgc3VjaCBhcyBmaWx0ZXJzIGFuZCBvcHRpb25hbCBzZXJ2aWNlc1xuICAgKiBAcmV0dXJuICBFbWl0ZXMgdGhlIHZhbHVlIG9mIHRoZSByZXF1ZXN0ZWQgc2VydmljZSByZWFkIGZyb20gdGhlIGRldmljZVxuICAgKi9cbiAgZGlzY292ZXIkKG9wdGlvbnM/OiBSZXF1ZXN0RGV2aWNlT3B0aW9ucyk6IE9ic2VydmFibGU8dm9pZCB8IEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2ZXIgfCBudWxsPiB7XG4gICAgcmV0dXJuIGZyb20odGhpcy5kaXNjb3ZlcihvcHRpb25zKSkucGlwZShtZXJnZU1hcCgoZGV2aWNlOiBCbHVldG9vdGhEZXZpY2V8bnVsbCkgPT4gdGhpcy5jb25uZWN0RGV2aWNlJChkZXZpY2UpKSk7XG4gIH1cblxuICAvKipcbiAgICogQ29ubmVjdCB0byBjdXJyZW50IGRldmljZS5cbiAgICpcbiAgICogQHJldHVybiAgRW1pdGVzIHRoZSBnYXR0IHNlcnZlciBpbnN0YW5jZSBvZiB0aGUgcmVxdWVzdGVkIGRldmljZVxuICAgKi9cbiAgYXN5bmMgY29ubmVjdERldmljZShkZXZpY2U6IEJsdWV0b290aERldmljZSB8IG51bGwpIHtcblxuICAgIGlmIChkZXZpY2UgPT09IG51bGwgfHwgdHlwZW9mIGRldmljZS5nYXR0ID09PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICB0aGlzLmNvbnNvbGUuZXJyb3IoJ1tCTEU6OkVycm9yXSBXYXMgbm90IGFibGUgdG8gY29ubmVjdCB0byBCbHVldG9vdGggUmVtb3RlIEdBVFQgU2VydmVyJyk7XG4gICAgICB0aGlzLmdhdHQkLmVycm9yKG51bGwpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gQ29ubmVjdGluZyB0byBCbHVldG9vdGggUmVtb3RlIEdBVFQgU2VydmVyIG9mICVvJywgZGV2aWNlKTtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBnYXR0U2VydmVyID0gYXdhaXQgZGV2aWNlLmdhdHQuY29ubmVjdCgpO1xuICAgICAgdGhpcy5nYXR0U2VydmVyID0gZ2F0dFNlcnZlcjtcbiAgICAgIHRoaXMuZ2F0dCQuZW1pdChnYXR0U2VydmVyKTtcbiAgICAgIHJldHVybiBnYXR0U2VydmVyO1xuICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgIC8vIHByb2JhYmx5IHRoZSB1c2VyIGhhcyBjYW5jZWxlZCB0aGUgZGlzY292ZXJ5XG4gICAgICBQcm9taXNlLnJlamVjdChgJHtlcnJvci5tZXNzYWdlfWApO1xuICAgICAgdGhpcy5nYXR0JC5lcnJvcihgJHtlcnJvci5tZXNzYWdlfWApO1xuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbm5lY3QgdG8gY3VycmVudCBkZXZpY2UuXG4gICAqXG4gICAqIEByZXR1cm4gIEVtaXRlcyB0aGUgZ2F0dCBzZXJ2ZXIgaW5zdGFuY2Ugb2YgdGhlIHJlcXVlc3RlZCBkZXZpY2VcbiAgICovXG4gIGNvbm5lY3REZXZpY2UkKGRldmljZTogQmx1ZXRvb3RoRGV2aWNlIHwgbnVsbCkge1xuICAgIHJldHVybiBmcm9tKHRoaXMuY29ubmVjdERldmljZShkZXZpY2UpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNjb25uZWN0IHRoZSBjdXJyZW50IGNvbm5lY3RlZCBkZXZpY2VcbiAgICovXG4gIGRpc2Nvbm5lY3REZXZpY2UoKSB7XG4gICAgaWYgKCF0aGlzLmdhdHRTZXJ2ZXIpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gRGlzY29ubmVjdGluZyBmcm9tIEJsdWV0b290aCBEZXZpY2UgJW8nLCB0aGlzLmdhdHRTZXJ2ZXIpO1xuXG4gICAgaWYgKHRoaXMuZ2F0dFNlcnZlci5jb25uZWN0ZWQpIHtcbiAgICAgIHRoaXMuZ2F0dFNlcnZlci5kaXNjb25uZWN0KCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIEJsdWV0b290aCBkZXZpY2UgaXMgYWxyZWFkeSBkaXNjb25uZWN0ZWQnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVxdWVzdHMgdGhlIHByaW1hcnkgc2VydmljZS5cbiAgICpcbiAgICogQHBhcmFtIGdhdHQgVGhlIEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2ZXIgc2V2ZXJcbiAgICogQHBhcmFtIHNlcnZpY2UgVGhlIFVVSUQgb2YgdGhlIHByaW1hcnkgc2VydmljZVxuICAgKiBAcmV0dXJuIFRoZSByZW1vdGUgc2VydmljZSAoYXMgYSBQcm9taXNlKVxuICAgKi9cbiAgYXN5bmMgZ2V0UHJpbWFyeVNlcnZpY2UoZ2F0dDogQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZlciB8IG51bGwsIHNlcnZpY2U6IEJsdWV0b290aFNlcnZpY2VVVUlEKTogUHJvbWlzZTxCbHVldG9vdGhSZW1vdGVHQVRUU2VydmljZT4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZW1vdGVTZXJ2aWNlID0gYXdhaXQgZ2F0dCEuZ2V0UHJpbWFyeVNlcnZpY2Uoc2VydmljZSk7XG4gICAgICByZXR1cm4gYXdhaXQgUHJvbWlzZS5yZXNvbHZlKHJlbW90ZVNlcnZpY2UpO1xuICAgIH1cbiAgICBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgcmV0dXJuIGF3YWl0IFByb21pc2UucmVqZWN0KGAke2Vycm9yLm1lc3NhZ2V9ICgke3NlcnZpY2V9KWApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXF1ZXN0cyB0aGUgcHJpbWFyeSBzZXJ2aWNlLlxuICAgKlxuICAgKiBAcGFyYW0gZ2F0dCBUaGUgQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZlciBzZXZlclxuICAgKiBAcGFyYW0gc2VydmljZSBUaGUgVVVJRCBvZiB0aGUgcHJpbWFyeSBzZXJ2aWNlXG4gICAqIEByZXR1cm4gVGhlIHJlbW90ZSBzZXJ2aWNlIChhcyBhbiBvYnNlcnZhYmxlKS5cbiAgICovXG4gIGdldFByaW1hcnlTZXJ2aWNlJChnYXR0OiBCbHVldG9vdGhSZW1vdGVHQVRUU2VydmVyIHwgbnVsbCwgc2VydmljZTogQmx1ZXRvb3RoU2VydmljZVVVSUQpOiBPYnNlcnZhYmxlPEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2aWNlPiB7XG4gICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gR2V0dGluZyBwcmltYXJ5IHNlcnZpY2UgXCIlc1wiIChpZiBhdmFpbGFibGUpIG9mICVvJywgc2VydmljZSwgZ2F0dCk7XG5cblxuICAgIGlmIChnYXR0KSB7XG4gICAgICByZXR1cm4gZnJvbShcbiAgICAgICAgdGhpcy5nZXRQcmltYXJ5U2VydmljZShnYXR0LCBzZXJ2aWNlKVxuICAgICAgKTtcbiAgICB9XG4gICAgZWxzZSB7XG4gICAgICByZXR1cm4gdGhyb3dFcnJvcigoKSA9PiBuZXcgRXJyb3IoJ1tCTEU6OkVycm9yXSBXYXMgbm90IGFibGUgdG8gY29ubmVjdCB0byB0aGUgQmx1ZXRvb3RoIFJlbW90ZSBHQVRUIFNlcnZlcicpKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVxdWVzdHMgYSBjaGFyYWN0ZXJpc3RpYyBmcm9tIHRoZSBwcmltYXJ5IHNlcnZpY2UuXG4gICAqXG4gICAqIEBwYXJhbSBwcmltYXJ5U2VydmljZSBUaGUgcHJpbWFyeSBzZXJ2aWNlLlxuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgVGhlIGNoYXJhY3RlcmlzdGljJ3MgVVVJRC5cbiAgICogQHJldHVybnMgVGhlIGNoYXJhY3RlcmlzdGljIGRlc2NyaXB0aW9uIChhcyBhIFByb21pc2UpLlxuICAgKi9cbiAgYXN5bmMgZ2V0Q2hhcmFjdGVyaXN0aWMoXG4gICAgcHJpbWFyeVNlcnZpY2U6IEJsdWV0b290aFJlbW90ZUdBVFRTZXJ2aWNlLFxuICAgIGNoYXJhY3RlcmlzdGljOiBCbHVldG9vdGhDaGFyYWN0ZXJpc3RpY1VVSURcbiAgKTogUHJvbWlzZTxCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMgfCBudWxsPiB7XG4gICAgdGhpcy5jb25zb2xlLmxvZygnW0JMRTo6SW5mb10gR2V0dGluZyBDaGFyYWN0ZXJpc3RpYyBcIiVzXCIgb2YgJW8nLCBjaGFyYWN0ZXJpc3RpYywgcHJpbWFyeVNlcnZpY2UpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNoYXIgPSBhd2FpdCBwcmltYXJ5U2VydmljZS5nZXRDaGFyYWN0ZXJpc3RpYyhjaGFyYWN0ZXJpc3RpYyk7XG4gICAgICAvLyBsaXN0ZW4gZm9yIGNoYXJhY3RlcmlzdGljIHZhbHVlIGNoYW5nZXNcbiAgICAgIGlmIChjaGFyLnByb3BlcnRpZXMubm90aWZ5KSB7XG4gICAgICAgIGNoYXIuc3RhcnROb3RpZmljYXRpb25zKCkudGhlbihfID0+IHtcbiAgICAgICAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBTdGFydGluZyBub3RpZmljYXRpb25zIG9mIFwiJXNcIicsIGNoYXJhY3RlcmlzdGljKTtcbiAgICAgICAgICBjaGFyLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYXJhY3RlcmlzdGljdmFsdWVjaGFuZ2VkJywgdGhpcy5vbkNoYXJhY3RlcmlzdGljQ2hhbmdlZC5iaW5kKHRoaXMpKTtcbiAgICAgICAgfSwgKGVycm9yOiBET01FeGNlcHRpb24pID0+IHtcbiAgICAgICAgICBQcm9taXNlLnJlamVjdChgJHtlcnJvci5tZXNzYWdlfSAoJHtjaGFyYWN0ZXJpc3RpY30pYCk7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgZWxzZSB7XG4gICAgICAgIGNoYXIuYWRkRXZlbnRMaXN0ZW5lcignY2hhcmFjdGVyaXN0aWN2YWx1ZWNoYW5nZWQnLCB0aGlzLm9uQ2hhcmFjdGVyaXN0aWNDaGFuZ2VkLmJpbmQodGhpcykpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGNoYXI7XG4gICAgfVxuICAgIGNhdGNoIChyZWplY3Rpb25FcnJvcikge1xuICAgICAgUHJvbWlzZS5yZWplY3QoYCR7KHJlamVjdGlvbkVycm9yIGFzIGFueSkubWVzc2FnZX0gKCR7Y2hhcmFjdGVyaXN0aWN9KWApO1xuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlcXVlc3RzIGEgY2hhcmFjdGVyaXN0aWMgZnJvbSB0aGUgcHJpbWFyeSBzZXJ2aWNlLlxuICAgKlxuICAgKiBAcGFyYW0gcHJpbWFyeVNlcnZpY2UgVGhlIHByaW1hcnkgc2VydmljZS5cbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIFRoZSBjaGFyYWN0ZXJpc3RpYydzIFVVSUQuXG4gICAqIEByZXR1cm5zIFRoZSBjaGFyYWN0ZXJpc3RpYyBkZXNjcmlwdGlvbiAoYXMgYSBPYnNlcnZhYmxlKS5cbiAgICovXG4gIGdldENoYXJhY3RlcmlzdGljJChcbiAgICBwcmltYXJ5U2VydmljZTogQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZpY2UsXG4gICAgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRFxuICApOiBPYnNlcnZhYmxlPG51bGwgfCBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWM+IHtcbiAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBHZXR0aW5nIENoYXJhY3RlcmlzdGljIFwiJXNcIiBvZiAlbycsIGNoYXJhY3RlcmlzdGljLCBwcmltYXJ5U2VydmljZSk7XG5cbiAgICByZXR1cm4gZnJvbSh0aGlzLmdldENoYXJhY3RlcmlzdGljKHByaW1hcnlTZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYykpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgdGhlIGNoYXJhY3RlcmlzdGljJ3Mgc3RhdGUuXG4gICAqXG4gICAqIEBwYXJhbSBzZXJ2aWNlIFRoZSBwYXJlbnQgc2VydmljZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWMuXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyBUaGUgcmVxdWVzdGVkIGNoYXJhY3RlcmlzdGljXG4gICAqIEBwYXJhbSBzdGF0ZSBBbiBBcnJheUJ1ZmZlciBjb250YWluaW5nIHRoZSB2YWx1ZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWMuXG4gICAqIEByZXR1cm4gVGhlIHByaW1hcnkgc2VydmljZSAodXNlZnVsIGZvciBjaGFpbmluZykuXG4gICAqL1xuICBzZXRDaGFyYWN0ZXJpc3RpY1N0YXRlKHNlcnZpY2U6IEJsdWV0b290aFNlcnZpY2VVVUlELCBjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoQ2hhcmFjdGVyaXN0aWNVVUlELCBzdGF0ZTogQXJyYXlCdWZmZXIpIHtcbiAgICBjb25zdCBwcmltYXJ5U2VydmljZSA9IHRoaXMuZ2V0UHJpbWFyeVNlcnZpY2UkKHRoaXMuZ2F0dFNlcnZlciwgc2VydmljZSk7XG5cbiAgICBwcmltYXJ5U2VydmljZVxuICAgICAgLnBpcGUobWVyZ2VNYXAoKF9wcmltYXJ5U2VydmljZTogQmx1ZXRvb3RoUmVtb3RlR0FUVFNlcnZpY2UpID0+IHRoaXMuZ2V0Q2hhcmFjdGVyaXN0aWMkKF9wcmltYXJ5U2VydmljZSwgY2hhcmFjdGVyaXN0aWMpKSlcbiAgICAgIC5zdWJzY3JpYmUoKGNoYXJhY3RlcmlzdGljOiBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMgfCBudWxsKSA9PiB0aGlzLndyaXRlVmFsdWUkKGNoYXJhY3RlcmlzdGljLCBzdGF0ZSkpO1xuXG4gICAgcmV0dXJuIHByaW1hcnlTZXJ2aWNlO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuYWJsZXMgdGhlIHNwZWNpZmllZCBjaGFyYWN0ZXJpc3RpYyBvZiBhIGdpdmVuIHNlcnZpY2UuXG4gICAqXG4gICAqIEBwYXJhbSBzZXJ2aWNlIFRoZSBwYXJlbnQgc2VydmljZSBvZiB0aGUgY2hhcmFjdGVyaXN0aWMuXG4gICAqIEBwYXJhbSBjaGFyYWN0ZXJpc3RpYyBUaGUgcmVxdWVzdGVkIGNoYXJhY3RlcmlzdGljXG4gICAqIEByZXR1cm4gVGhlIHByaW1hcnkgc2VydmljZSAodXNlZnVsIGZvciBjaGFpbmluZykuXG4gICAqL1xuICBlbmFibGVDaGFyYWN0ZXJpc3RpYyhzZXJ2aWNlOiBCbHVldG9vdGhTZXJ2aWNlVVVJRCwgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRCwgc3RhdGU/OiBhbnkpIHtcbiAgICBzdGF0ZSA9IHN0YXRlIHx8IG5ldyBVaW50OEFycmF5KFsxXSk7XG4gICAgcmV0dXJuIHRoaXMuc2V0Q2hhcmFjdGVyaXN0aWNTdGF0ZShzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgc3RhdGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIERpc2FibGVzIHRoZSBzcGVjaWZpZWQgY2hhcmFjdGVyaXN0aWMgb2YgYSBnaXZlbiBzZXJ2aWNlLlxuICAgKlxuICAgKiBAcGFyYW0gc2VydmljZSBUaGUgcGFyZW50IHNlcnZpY2Ugb2YgdGhlIGNoYXJhY3RlcmlzdGljLlxuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgVGhlIHJlcXVlc3RlZCBjaGFyYWN0ZXJpc3RpYy5cbiAgICogQHJldHVybiBUaGUgcHJpbWFyeSBzZXJ2aWNlICh1c2VmdWwgZm9yIGNoYWluaW5nKS5cbiAgICovXG4gIGRpc2JhbGVDaGFyYWN0ZXJpc3RpYyhzZXJ2aWNlOiBCbHVldG9vdGhTZXJ2aWNlVVVJRCwgY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aENoYXJhY3RlcmlzdGljVVVJRCwgc3RhdGU/OiBhbnkpIHtcbiAgICBzdGF0ZSA9IHN0YXRlIHx8IG5ldyBVaW50OEFycmF5KFswXSk7XG4gICAgcmV0dXJuIHRoaXMuc2V0Q2hhcmFjdGVyaXN0aWNTdGF0ZShzZXJ2aWNlLCBjaGFyYWN0ZXJpc3RpYywgc3RhdGUpO1xuICB9XG5cbiAgLyoqXG4gICAqIERpc3BhdGNoZXMgbmV3IHZhbHVlcyBlbWl0dGVkIGJ5IGEgY2hhcmFjdGVyaXN0aWMuXG4gICAqXG4gICAqIEBwYXJhbSBldmVudCB0aGUgZGlzdHBhdGNoZWQgZXZlbnQuXG4gICAqL1xuICBvbkNoYXJhY3RlcmlzdGljQ2hhbmdlZChldmVudDogRXZlbnQpIHtcbiAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBEaXNwYXRjaGluZyBuZXcgY2hhcmFjdGVyaXN0aWMgdmFsdWUgJW8nLCBldmVudCk7XG5cbiAgICBjb25zdCB2YWx1ZSA9IChldmVudC50YXJnZXQgYXMgQmx1ZXRvb3RoUmVtb3RlR0FUVENoYXJhY3RlcmlzdGljKS52YWx1ZTtcbiAgICB0aGlzLmNoYXJhY3RlcmlzdGljVmFsdWVDaGFuZ2VzJC5lbWl0KHZhbHVlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWFkcyBhIHZhbHVlIGZyb20gdGhlIGNoYXJhY3RlcmlzdGljcywgYXMgYSBEYXRhVmlldy5cbiAgICpcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIFRoZSByZXF1ZXN0ZWQgY2hhcmFjdGVyaXN0aWMuXG4gICAqIEByZXR1cm4gdGhlIERhdGFWaWV3IHZhbHVlIChhcyBhbiBPYnNlcnZhYmxlKS5cbiAgICovXG4gIHJlYWRWYWx1ZSQoY2hhcmFjdGVyaXN0aWM6IEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYyk6IE9ic2VydmFibGU8RGF0YVZpZXc+IHtcbiAgICB0aGlzLmNvbnNvbGUubG9nKCdbQkxFOjpJbmZvXSBSZWFkaW5nIENoYXJhY3RlcmlzdGljICVvJywgY2hhcmFjdGVyaXN0aWMpO1xuXG4gICAgcmV0dXJuIGZyb20oXG4gICAgICBjaGFyYWN0ZXJpc3RpY1xuICAgICAgICAucmVhZFZhbHVlKClcbiAgICAgICAgLnRoZW4oKGRhdGE6IERhdGFWaWV3KSA9PiBQcm9taXNlLnJlc29sdmUoZGF0YSksIChlcnJvcjogRE9NRXhjZXB0aW9uKSA9PiBQcm9taXNlLnJlamVjdChgJHtlcnJvci5tZXNzYWdlfWApKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogV3JpdGVzIGEgdmFsdWUgaW50byB0aGUgc3BlY2lmaWVkIGNoYXJhY3RlcmlzdGljLlxuICAgKlxuICAgKiBAcGFyYW0gY2hhcmFjdGVyaXN0aWMgVGhlIHJlcXVlc3RlZCBjaGFyYWN0ZXJpc3RpYy5cbiAgICogQHBhcmFtIHZhbHVlIFRoZSB2YWx1ZSB0byBiZSB3cml0dGVuIChhcyBhbiBBcnJheUJ1ZmZlciBvciBVaW50OEFycmF5KS5cbiAgICogQHJldHVybiBhbiB2b2lkIE9ic2VydmFibGUuXG4gICAqL1xuICB3cml0ZVZhbHVlJChjaGFyYWN0ZXJpc3RpYzogQmx1ZXRvb3RoUmVtb3RlR0FUVENoYXJhY3RlcmlzdGljIHwgbnVsbCwgdmFsdWU6IEFycmF5QnVmZmVyIHwgVWludDhBcnJheSkge1xuXG4gICAgaWYgKGNoYXJhY3RlcmlzdGljID09PSBudWxsKSB7XG4gICAgICB0aGlzLmNvbnNvbGUuZXJyb3IoJ1tCTEU6OkVycm9yXSBXYXMgbm90IGFibGUgdG8gd3JpdGUgY2hhcmFjdGVyaXN0aWMnKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIHRoaXMuY29uc29sZS5sb2coJ1tCTEU6OkluZm9dIFdyaXRpbmcgQ2hhcmFjdGVyaXN0aWMgJW8nLCBjaGFyYWN0ZXJpc3RpYyk7XG5cbiAgICByZXR1cm4gZnJvbShjaGFyYWN0ZXJpc3RpYy53cml0ZVZhbHVlKHZhbHVlKS50aGVuKF8gPT4gUHJvbWlzZS5yZXNvbHZlKCksIChlcnJvcjogRE9NRXhjZXB0aW9uKSA9PiBQcm9taXNlLnJlamVjdChgJHtlcnJvci5tZXNzYWdlfWApKSk7XG4gIH1cblxuICAvKipcbiAgICogQSBzdHJlYW0gb2YgRGF0YVZpZXcgdmFsdWVzIGVtaXR0ZWQgYnkgdGhlIHNwZWNpZmllZCBjaGFyYWN0ZXJpc3RpYy5cbiAgICpcbiAgICogQHBhcmFtIGNoYXJhY3RlcmlzdGljIFRoZSBjaGFyYWN0ZXJpc3RpYyB3aGljaCB2YWx1ZSB5b3Ugd2FudCB0byBvYnNlcnZlXG4gICAqIEByZXR1cm4gVGhlIHN0cmVhbSBvZiBEYXRhVmlldyB2YWx1ZXMuXG4gICAqL1xuICBvYnNlcnZlVmFsdWUkKGNoYXJhY3RlcmlzdGljOiBCbHVldG9vdGhSZW1vdGVHQVRUQ2hhcmFjdGVyaXN0aWMpOiBPYnNlcnZhYmxlPERhdGFWaWV3PiB7XG5cbiAgICBpZiAoY2hhcmFjdGVyaXN0aWMgPT09IG51bGwgfHwgdHlwZW9mIGNoYXJhY3RlcmlzdGljLnNlcnZpY2UgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB0aGlzLmNvbnNvbGUuZXJyb3IoJ1tCTEU6OkVycm9yXSBXYXMgbm90IGFibGUgdG8gcmVhZCBjaGFyYWN0ZXJpc3RpYycpO1xuICAgICAgcmV0dXJuIEVNUFRZO1xuICAgIH1cblxuICAgIGNoYXJhY3RlcmlzdGljLnN0YXJ0Tm90aWZpY2F0aW9ucygpO1xuICAgIGNvbnN0IGRpc2Nvbm5lY3RlZCA9IGZyb21FdmVudChjaGFyYWN0ZXJpc3RpYy5zZXJ2aWNlLmRldmljZSwgJ2dhdHRzZXJ2ZXJkaXNjb25uZWN0ZWQnKTtcbiAgICByZXR1cm4gZnJvbUV2ZW50KGNoYXJhY3RlcmlzdGljLCAnY2hhcmFjdGVyaXN0aWN2YWx1ZWNoYW5nZWQnKVxuICAgICAgLnBpcGUoXG4gICAgICAgIG1hcCgoZXZlbnQ6IEV2ZW50KSA9PiAoZXZlbnQudGFyZ2V0IGFzIEJsdWV0b290aFJlbW90ZUdBVFRDaGFyYWN0ZXJpc3RpYykudmFsdWUgYXMgRGF0YVZpZXcpLFxuICAgICAgICB0YWtlVW50aWwoZGlzY29ubmVjdGVkKVxuICAgICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBIHV0aWxpdHkgbWV0aG9kIHRvIGNvbnZlcnQgTEUgdG8gYW4gdW5zaWduZWQgMTYtYml0IGludGVnZXIgdmFsdWVzLlxuICAgKlxuICAgKiBAcGFyYW0gZGF0YSBUaGUgRGF0YVZpZXcgYmluYXJ5IGRhdGEuXG4gICAqIEBwYXJhbSBieXRlT2Zmc2V0IFRoZSBvZmZzZXQsIGluIGJ5dGUsIGZyb20gdGhlIHN0YXJ0IG9mIHRoZSB2aWV3IHdoZXJlIHRvIHJlYWQgdGhlIGRhdGEuXG4gICAqIEByZXR1cm4gQW4gdW5zaWduZWQgMTYtYml0IGludGVnZXIgbnVtYmVyLlxuICAgKi9cbiAgbGl0dGxlRW5kaWFuVG9VaW50MTYoZGF0YTogYW55LCBieXRlT2Zmc2V0OiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiAodGhpcy5saXR0bGVFbmRpYW5Ub1VpbnQ4KGRhdGEsIGJ5dGVPZmZzZXQgKyAxKSA8PCA4KSArIHRoaXMubGl0dGxlRW5kaWFuVG9VaW50OChkYXRhLCBieXRlT2Zmc2V0KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBIHV0aWxpdHkgbWV0aG9kIHRvIGNvbnZlcnQgTEUgdG8gYW4gdW5zaWduZWQgOC1iaXQgaW50ZWdlciB2YWx1ZXMuXG4gICAqXG4gICAqIEBwYXJhbSBkYXRhIFRoZSBEYXRhVmlldyBiaW5hcnkgZGF0YS5cbiAgICogQHBhcmFtIGJ5dGVPZmZzZXQgVGhlIG9mZnNldCwgaW4gYnl0ZSwgZnJvbSB0aGUgc3RhcnQgb2YgdGhlIHZpZXcgd2hlcmUgdG8gcmVhZCB0aGUgZGF0YS5cbiAgICogQHJldHVybiBBbiB1bnNpZ25lZCA4LWJpdCBpbnRlZ2VyIG51bWJlci5cbiAgICovXG4gIGxpdHRsZUVuZGlhblRvVWludDgoZGF0YTogYW55LCBieXRlT2Zmc2V0OiBudW1iZXIpOiBudW1iZXIge1xuICAgIHJldHVybiBkYXRhLmdldFVpbnQ4KGJ5dGVPZmZzZXQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNlbmRzIHJhbmRvbSBkYXRhIChmb3IgdGVzdGluZyBwdXJwb3NlcyBvbmx5KS5cbiAgICpcbiAgICogQHJldHVybiBSYW5kb20gdW5zaWduZWQgOC1iaXQgaW50ZWdlciB2YWx1ZXMuXG4gICAqL1xuICBmYWtlTmV4dChmYWtlVmFsdWU/OiAoKSA9PiBEYXRhVmlldykge1xuICAgIGlmIChmYWtlVmFsdWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgZmFrZVZhbHVlID0gKCkgPT4ge1xuICAgICAgICBjb25zdCBkdiA9IG5ldyBEYXRhVmlldyhuZXcgQXJyYXlCdWZmZXIoOCkpO1xuICAgICAgICBkdi5zZXRVaW50OCgwLCAoTWF0aC5yYW5kb20oKSAqIDExMCkgfCAwKTtcbiAgICAgICAgcmV0dXJuIGR2O1xuICAgICAgfTtcbiAgICB9XG5cbiAgICB0aGlzLmNoYXJhY3RlcmlzdGljVmFsdWVDaGFuZ2VzJC5lbWl0KGZha2VWYWx1ZSgpKTtcbiAgfVxufVxuIl19