@iotize/ionic
Version:
Iotize specific building blocks on top of @ionic/angular.
427 lines • 63.8 kB
JavaScript
import { Injectable, NgZone } from '@angular/core';
import { Platform } from '@ionic/angular';
import { BLEScanner, getIoTizeBleCordovaPlugin, getScanRecordsFromBytes, } from '@iotize/device-com-ble.cordova';
import { ReplaySubject, defer, pipe, throwError, } from 'rxjs';
import { filter, map, share, switchMap } from 'rxjs/operators';
import { runInZone } from '../../../lib/rx-utility/run-in-zone';
import { checkManufacturerDataMatch, parseScanRecords, } from './manufacturer-data-filter';
import { WebBluetoothScanner, PeripheralAdapter, } from '@iotize/device-com-web-bluetooth.js';
import * as i0 from "@angular/core";
import * as i1 from "@ionic/angular";
export function filterUndefined() {
return pipe(filter((value) => value !== undefined));
}
export class BleService {
platform;
ngZone;
bleScanner;
scannerSubscription;
plugin;
webBluetoothPeripheralAdapters = new Map();
lastResults;
_results$ = new ReplaySubject();
results;
resultsWithRecords;
constructor(platform, ngZone) {
this.platform = platform;
this.ngZone = ngZone;
if (this.platform.is('cordova'))
this.plugin = getIoTizeBleCordovaPlugin();
this.results = this._results$.pipe(filterUndefined(), runInZone(this.ngZone), share());
this.resultsWithRecords = this._results$.pipe(filterUndefined(), map((devices) => {
return devices?.map((device) => {
if (!this.platform.is('cordova') && device.name) {
//Web Bluetooth
let adapter = this.getDeviceFromPeripheralAdapter(device.name);
if (adapter == undefined) {
adapter = new PeripheralAdapter(device);
this.webBluetoothPeripheralAdapters.set(device.name, adapter);
// set deviceAdvertisement listener
this.setWatchAdvertisement(adapter);
}
return {
address: adapter.id,
name: adapter.name,
rssi: adapter.rssi,
txPower: adapter.txPower,
records: getScanRecordsFromBytes(new Uint8Array(adapter.scanRecord || [])),
};
}
else {
device = device;
return {
address: device.address,
name: device.name,
rssi: device.rssi,
txPower: device.txPower,
records: getScanRecordsFromBytes(new Uint8Array(device.scanRecord || [])),
};
}
});
}));
}
createNewScanner(options) {
this.clearPreviousScanner();
if (this.platform.is('cordova')) {
if (options != undefined && options.filters != undefined) {
this.bleScanner = new BLEScanner(undefined, {
filters: options.filters,
});
}
else {
this.bleScanner = new BLEScanner(undefined);
}
}
else {
let webBLEOptions = {
acceptAllDevices: true,
};
const filters = options?.filters?.reduce((acc, current) => {
if (current.services && current.services.length != 0) {
acc.push({
services: current.services,
});
}
const manufacturerDataFilter = current.manufacturerData?.reduce((acc, current) => {
if (current.companyIdentifier != undefined) {
let newFilter = {
companyIdentifier: this.companyIdentifierToNumber(current.companyIdentifier),
// dataPrefix: undefined,
// mask: undefined
};
if (current.dataPrefix) {
newFilter.dataPrefix = new Uint8Array(current.dataPrefix);
}
if (current.mask) {
newFilter.mask = new Uint8Array(current.mask);
}
acc.push(newFilter);
}
return acc;
}, []);
if (manufacturerDataFilter && manufacturerDataFilter?.length != 0) {
acc.push({
manufacturerData: manufacturerDataFilter,
});
}
return acc;
}, []);
if (filters != undefined && filters.length != 0) {
webBLEOptions = {
filters,
};
}
this.bleScanner = new WebBluetoothScanner({
requestDeviceOptions: webBLEOptions,
});
}
this.scannerSubscription = this.bleScanner.results.subscribe((results) => {
if (options) {
const manufacturerDataFilters = options.filters?.reduce((filters, current) => {
if (current.manufacturerData) {
filters.push(...current.manufacturerData);
}
return filters;
}, []);
if (manufacturerDataFilters?.length) {
results = results.filter((result) => {
const scanRecord = result.scanRecord;
if (!scanRecord?.length || manufacturerDataFilters.length === 0) {
return false;
}
const scanRecords = parseScanRecords(scanRecord);
return checkManufacturerDataMatch(scanRecords, manufacturerDataFilters);
});
}
}
this.emitNexResults(results);
});
return this.bleScanner;
}
emitNexResults(results) {
this.lastResults = results;
this._results$.next(results);
}
clearPreviousScanner() {
if (this.bleScanner) {
this.scannerSubscription?.unsubscribe();
this.emitNexResults(undefined);
this.bleScanner = undefined;
}
}
async startScan(options) {
const newScanner = this.createNewScanner(options);
return newScanner.start();
}
async stopScan() {
if (this.platform.is('cordova')) {
if (this.bleScanner == undefined) {
throw new Error('BLE Scanner not defined');
}
return this.bleScanner.stop();
}
else {
// // stop watch advertisements
for (let [id, adapter] of this.webBluetoothPeripheralAdapters) {
this.unsetWatchAdvertisement(adapter);
}
return this.bleScanner?.stop();
}
}
connectByName(bleDeviceName) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
return throwError(() => new Error('BLE plugin is not available'));
}
return this.plugin.connect(bleDeviceName).pipe(share());
}
else {
return defer(async () => {
const device = this.getDeviceFromPeripheralAdapter(bleDeviceName);
if (device) {
device.connect().then(() => {
device._peripheral.watchAdvertisements();
});
return device.stateChange;
}
throwError(() => new Error(`ConnectionError, can not find device ${bleDeviceName}`));
}).pipe(filterUndefined(), switchMap((v) => v));
}
}
async disconnectByName(bleDeviceName) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin.disConnect(bleDeviceName);
}
else {
const device = await this.getDeviceFromPeripheralAdapterOrThrow(bleDeviceName);
// stop watch advertisements
this.unsetWatchAdvertisement(device);
await device.disconnect();
return 'OK';
}
}
async closeByName(bleDeviceName) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin.close(bleDeviceName);
}
else {
// Nothing to do on web browser
return 'OK';
}
}
async readCharacteristic(deviceName, serviceId, characteristicId) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin.characteristicReadValue(deviceName, serviceId, characteristicId);
}
else {
const characteristic = await this.getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId);
return characteristic.read();
}
}
async writeCharacteristic(deviceName, serviceId, characteristicId, data, withResponse) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
let writePromise;
if (withResponse) {
writePromise = this.plugin.characteristicWrite(deviceName, serviceId, characteristicId, data);
}
else {
writePromise = this.plugin.characteristicWriteWithoutResponse(deviceName, serviceId, characteristicId, data);
}
return writePromise.then((_) => true);
}
else {
const characteristic = await this.getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId);
await characteristic.write(data, !!withResponse);
return true;
}
}
listenCharacteristicChange(deviceName, serviceId, characteristicId) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
return throwError(new Error('BLE plugin is not available'));
}
return this.plugin
.characteristicChanged(deviceName, serviceId, characteristicId)
.pipe(runInZone(this.ngZone)
// share(),
);
}
else {
return this.getCharacteristicFromPeripheralAdapterObservable(deviceName, serviceId, characteristicId).pipe(switchMap((char) => {
return char.data;
}), map((data) => data.data), runInZone(this.ngZone));
}
}
async discoverServices(deviceName) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin.discoverServices(deviceName);
}
else {
const device = await this.getDeviceFromPeripheralAdapterOrThrow(deviceName);
if (!device._peripheral.gatt?.connected) {
throw new Error(`device ${deviceName} is not connected`);
}
const servicesAdapters = await device.discoverServices();
const services = [];
for (let serviceName in servicesAdapters) {
const adapter = servicesAdapters[serviceName];
if (adapter) {
const description = {
uuid: adapter.uuid,
};
const characteristicDescriptions = [];
for (let characteristic of await adapter.getCharacteristics()) {
const descriptors = await characteristic.getDescriptors();
characteristicDescriptions.push({
uuid: characteristic.uuid,
properties: characteristic.properties,
descriptors: descriptors.map((desc) => {
return { uuid: desc.uuid, value: desc.value };
}),
});
}
if (characteristicDescriptions.length != 0) {
description.characteristics = characteristicDescriptions;
}
services.push(description);
}
}
return services;
}
}
async startNotification(deviceName, serviceId, characteristicId) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin.characteristicStartNotification(deviceName, serviceId, characteristicId);
}
else {
const characteristic = await this.getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId);
await characteristic.enableNotifications(true);
return 'OK';
}
}
async stopNotification(deviceName, serviceId, characteristicId) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin
.characteristicStopNotification(deviceName, serviceId, characteristicId)
.then((r) => true);
}
else {
const characteristic = await this.getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId);
await characteristic.enableNotifications(false);
return true;
}
}
getDeviceFromPeripheralAdapter(deviceName) {
const device = this.webBluetoothPeripheralAdapters.get(deviceName);
if (device != undefined) {
return device;
}
//try with id
for (let [id, adapter] of this.webBluetoothPeripheralAdapters) {
if (adapter.id == deviceName) {
return adapter;
}
}
return undefined;
}
async getDeviceFromPeripheralAdapterOrThrow(deviceName) {
const device = this.getDeviceFromPeripheralAdapter(deviceName);
if (device) {
return device;
}
throw new Error(`Can not find device ${device}`);
}
async getDeviceServiceFromPeripheralAdapter(deviceName, serviceId) {
const device = await this.getDeviceFromPeripheralAdapterOrThrow(deviceName);
const service = await device.getService(serviceId);
if (service == undefined) {
throw new Error(`Can not find service ${serviceId} on device ${device}`);
}
return service;
}
getCharacteristicFromPeripheralAdapterObservable(deviceName, serviceId, characteristicId) {
return defer(() => {
return this.getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId);
}).pipe(filterUndefined());
}
async getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId) {
const service = await this.getDeviceServiceFromPeripheralAdapter(deviceName, serviceId);
if (service) {
return await service.getCharacteristic(characteristicId);
}
throw new Error(`Can not find characteristic ${characteristicId} on service ${serviceId} on device ${deviceName}`);
}
setWatchAdvertisement(adapter) {
console.log('setWatchAdvertisement', adapter.name);
adapter._peripheral.addEventListener('advertisementreceived', (event) => {
console.log('UPDATE ADVERTISEMENT');
console.log(event);
this.updateAdvertisementData(event, adapter);
}, true);
adapter.watchAdvertisementsAbortController = new AbortController();
adapter._peripheral.watchAdvertisements({
signal: adapter.watchAdvertisementsAbortController.signal,
});
// (adapter._peripheral as any).onadvertisementreceived = (event: any) => {
// console.log("UPDATE ADVERTISEMENT");
// console.log(event);
// }
}
unsetWatchAdvertisement(adapter) {
// adapter._peripheral.removeEventListener('advertisementreceived', null);
adapter.watchAdvertisementsAbortController?.abort();
}
updateAdvertisementData(event, adapter) {
adapter.rssi = event.rssi;
adapter.txPower = event.txPower;
if (event.manufacturerData) {
adapter.scanRecord = [];
for (let [key, value] of event.manufacturerData) {
adapter.scanRecord.push(0xff, (key & 0xff00) >> 8, key & 0xff);
adapter.scanRecord.push(...new Uint8Array(value));
}
}
this.emitNexResults(this.lastResults);
}
companyIdentifierToNumber(companyIdentifier) {
if (typeof companyIdentifier == 'number') {
return companyIdentifier;
}
else {
return new Array(companyIdentifier).reduce((acc, next, i) => {
acc += next[i] << i;
return acc;
}, 0);
}
}
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BleService, deps: [{ token: i1.Platform }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BleService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BleService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: () => [{ type: i1.Platform }, { type: i0.NgZone }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxlLXNlcnZpY2Uuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2lvdGl6ZS1pb25pYy9zcmMvbGliL3RhcC1zY2FubmVyL3RhcC1zY2FubmVyLWJsZS9ibGUtc2VydmljZS5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ25ELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMxQyxPQUFPLEVBQ0wsVUFBVSxFQUVWLHlCQUF5QixFQUN6Qix1QkFBdUIsR0FDeEIsTUFBTSxnQ0FBZ0MsQ0FBQztBQUd4QyxPQUFPLEVBR0wsYUFBYSxFQUdiLEtBQUssRUFDTCxJQUFJLEVBQ0osVUFBVSxHQUNYLE1BQU0sTUFBTSxDQUFDO0FBQ2QsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQy9ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUNoRSxPQUFPLEVBRUwsMEJBQTBCLEVBQzFCLGdCQUFnQixHQUNqQixNQUFNLDRCQUE0QixDQUFDO0FBQ3BDLE9BQU8sRUFDTCxtQkFBbUIsRUFDbkIsaUJBQWlCLEdBRWxCLE1BQU0scUNBQXFDLENBQUM7OztBQWtDN0MsTUFBTSxVQUFVLGVBQWU7SUFJN0IsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLEtBQUssU0FBUyxDQUFDLENBR2pELENBQUM7QUFDSixDQUFDO0FBS0QsTUFBTSxPQUFPLFVBQVU7SUFnQkQ7SUFBNEI7SUFmeEMsVUFBVSxDQUFnQztJQUMxQyxtQkFBbUIsQ0FBZ0I7SUFDbkMsTUFBTSxDQUEwQjtJQUNoQyw4QkFBOEIsR0FHbEMsSUFBSSxHQUFH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SSxPQUFPLGlCQUFpQixJQUFJLFFBQVEsRUFBRSxDQUFDO1lBQ3pDLE9BQU8saUJBQWlCLENBQUM7UUFDM0IsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLElBQUksS0FBSyxDQUFDLGlCQUFpQixDQUFDLENBQUMsTUFBTSxDQUFTLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxDQUFDLEVBQUUsRUFBRTtnQkFDbEUsR0FBRyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3BCLE9BQU8sR0FBRyxDQUFDO1lBQ2IsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ1IsQ0FBQztJQUNILENBQUM7MkhBNWlCVSxVQUFVOytIQUFWLFVBQVUsY0FGVCxNQUFNOzs0RkFFUCxVQUFVO2tCQUh0QixVQUFVO21CQUFDO29CQUNWLFVBQVUsRUFBRSxNQUFNO2lCQUNuQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIE5nWm9uZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgUGxhdGZvcm0gfSBmcm9tICdAaW9uaWMvYW5ndWxhcic7XG5pbXBvcnQge1xuICBCTEVTY2FubmVyLFxuICBDb3Jkb3ZhQkxFU2NhblJlc3VsdCxcbiAgZ2V0SW9UaXplQmxlQ29yZG92YVBsdWdpbixcbiAgZ2V0U2NhblJlY29yZHNGcm9tQnl0ZXMsXG59IGZyb20gJ0Bpb3RpemUvZGV2aWNlLWNvbS1ibGUuY29yZG92YSc7XG5pbXBvcnQgeyBJb1RpemVCbGVDb3Jkb3ZhUGx1Z2luIH0gZnJvbSAnQGlvdGl6ZS9kZXZpY2UtY29tLWJsZS5jb3Jkb3ZhL2xpYi9pb3RpemUtYmxlLWNvcmRvdmEtcGx1Z2luJztcbmltcG9ydCB7IERldmljZVNjYW5uZXIgfSBmcm9tICdAaW90aXplL3RhcC9zY2FubmVyL2FwaSc7XG5pbXBvcnQge1xuICBPYnNlcnZhYmxlLFxuICBPcGVyYXRvckZ1bmN0aW9uLFxuICBSZXBsYXlTdWJqZWN0LFxuICBTdWJzY3JpcHRpb24sXG4gIFVuYXJ5RnVuY3Rpb24sXG4gIGRlZmVyLFxuICBwaXBlLFxuICB0aHJvd0Vycm9yLFxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGZpbHRlciwgbWFwLCBzaGFyZSwgc3dpdGNoTWFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgcnVuSW5ab25lIH0gZnJvbSAnLi4vLi4vLi4vbGliL3J4LXV0aWxpdHkvcnVuLWluLXpvbmUnO1xuaW1wb3J0IHtcbiAgV2ViQmx1ZXRvb3RoTWFudWZhY3R1cmVyRGF0YUZpbHRlcixcbiAgY2hlY2tNYW51ZmFjdHVyZXJEYXRhTWF0Y2gsXG4gIHBhcnNlU2NhblJlY29yZHMsXG59IGZyb20gJy4vbWFudWZhY3R1cmVyLWRhdGEtZmlsdGVyJztcbmltcG9ydCB7XG4gIFdlYkJsdWV0b290aFNjYW5uZXIsXG4gIFBlcmlwaGVyYWxBZGFwdGVyLFxuICBXZWJCbHVldG9vdGhEZXZpY2UsXG59IGZyb20gJ0Bpb3RpemUvZGV2aWNlLWNvbS13ZWItYmx1ZXRvb3RoLmpzJztcbmltcG9ydCB7XG4gIENoYXJhY3RlcmlzdGljRGVzY3JpcHRpb24sXG4gIFNlcnZpY2VEZXNjcmlwdGlvbixcbiAgU2VydmljZUxpc3QsXG59IGZyb20gJ0Bpb3RpemUvZGV2aWNlLWNvbS1ibGUuY29yZG92YS9saWIvZGVmaW5pdGlvbnMnO1xuXG5leHBvcnQgdHlwZSBCbGVTY2FubmVyT3B0aW9ucyA9IHtcbiAgZmlsdGVycz86IHtcbiAgICBzZXJ2aWNlcz86IHN0cmluZ1tdO1xuICAgIG1hbnVmYWN0dXJlckRhdGE/OiBXZWJCbHVldG9vdGhNYW51ZmFjdHVyZXJEYXRhRmlsdGVyW107XG4gIH1bXTtcbn07XG5cbmV4cG9ydCB0eXBlIHNjYW5uZXJSZXN1bHQgPSBDb3Jkb3ZhQkxFU2NhblJlc3VsdCB8IFdlYkJsdWV0b290aERldmljZTtcblxuZXhwb3J0IHR5cGUgQkxFU2NhblJlc3VsdFdpdGhSZWNvcmRzID0ge1xuICBhZGRyZXNzOiBzdHJpbmc7XG4gIG5hbWU6IHN0cmluZztcbiAgcnNzaT86IG51bWJlcjtcbiAgdHhQb3dlcj86IG51bWJlcjtcbiAgcmVjb3Jkcz86IHtcbiAgICB0eXBlOiBudW1iZXI7XG4gICAgZGF0YTogVWludDhBcnJheTtcbiAgfVtdO1xufTtcblxuZXhwb3J0IHR5cGUgUGVyaXBoZXJhbEFkYXB0ZXJXaXRoUmVjb3JkcyA9IFBlcmlwaGVyYWxBZGFwdGVyICYge1xuICByc3NpPzogbnVtYmVyO1xuICB0eFBvd2VyPzogbnVtYmVyO1xuICBzY2FuUmVjb3JkPzogbnVtYmVyW107XG4gIHdhdGNoQWR2ZXJ0aXNlbWVudHNBYm9ydENvbnRyb2xsZXI/OiBBYm9ydENvbnRyb2xsZXI7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZmlsdGVyVW5kZWZpbmVkPFQ+KCk6IFVuYXJ5RnVuY3Rpb248XG4gIE9ic2VydmFibGU8VCB8IHVuZGVmaW5lZD4sXG4gIE9ic2VydmFibGU8VD5cbj4ge1xuICByZXR1cm4gcGlwZShmaWx0ZXIoKHZhbHVlKSA9PiB2YWx1ZSAhPT0gdW5kZWZpbmVkKSkgYXMgT3BlcmF0b3JGdW5jdGlvbjxcbiAgICBUIHwgdW5kZWZpbmVkLFxuICAgIFRcbiAgPjtcbn1cblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCcsXG59KVxuZXhwb3J0IGNsYXNzIEJsZVNlcnZpY2Uge1xuICBwcml2YXRlIGJsZVNjYW5uZXI/OiBEZXZpY2VTY2FubmVyPHNjYW5uZXJSZXN1bHQ+O1xuICBwcml2YXRlIHNjYW5uZXJTdWJzY3JpcHRpb24/OiBTdWJzY3JpcHRpb247XG4gIHByaXZhdGUgcGx1Z2luPzogSW9UaXplQmxlQ29yZG92YVBsdWdpbjtcbiAgcHJpdmF0ZSB3ZWJCbHVldG9vdGhQZXJpcGhlcmFsQWRhcHRlcnM6IE1hcDxcbiAgICBzdHJpbmcsXG4gICAgUGVyaXBoZXJhbEFkYXB0ZXJXaXRoUmVjb3Jkc1xuICA+ID0gbmV3IE1hcDxzdHJpbmcsIFBlcmlwaGVyYWxBZGFwdGVyV2l0aFJlY29yZHM+KCk7XG5cbiAgcHJpdmF0ZSBsYXN0UmVzdWx0cz86IHNjYW5uZXJSZXN1bHRbXTtcbiAgcHJpdmF0ZSBfcmVzdWx0cyQgPSBuZXcgUmVwbGF5U3ViamVjdDxzY2FubmVyUmVzdWx0W10gfCB1bmRlZmluZWQ+KCk7XG5cbiAgcHVibGljIHJlYWRvbmx5IHJlc3VsdHM6IE9ic2VydmFibGU8c2Nhbm5lclJlc3VsdFtdPjtcblxuICBwdWJsaWMgcmVhZG9ubHkgcmVzdWx0c1dpdGhSZWNvcmRzOiBPYnNlcnZhYmxlPEJMRVNjYW5SZXN1bHRXaXRoUmVjb3Jkc1tdPjtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHBsYXRmb3JtOiBQbGF0Zm9ybSwgcHJpdmF0ZSBuZ1pvbmU6IE5nWm9uZSkge1xuICAgIGlmICh0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykpIHRoaXMucGx1Z2luID0gZ2V0SW9UaXplQmxlQ29yZG92YVBsdWdpbigpO1xuXG4gICAgdGhpcy5yZXN1bHRzID0gdGhpcy5fcmVzdWx0cyQucGlwZShcbiAgICAgIGZpbHRlclVuZGVmaW5lZCgpLFxuICAgICAgcnVuSW5ab25lKHRoaXMubmdab25lKSxcbiAgICAgIHNoYXJlKClcbiAgICApO1xuXG4gICAgdGhpcy5yZXN1bHRzV2l0aFJlY29yZHMgPSB0aGlzLl9yZXN1bHRzJC5waXBlKFxuICAgICAgZmlsdGVyVW5kZWZpbmVkKCksXG4gICAgICBtYXAoKGRldmljZXMpID0+IHtcbiAgICAgICAgcmV0dXJuIGRldmljZXM/Lm1hcCgoZGV2aWNlKSA9PiB7XG4gICAgICAgICAgaWYgKCF0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykgJiYgZGV2aWNlLm5hbWUpIHtcbiAgICAgICAgICAgIC8vV2ViIEJsdWV0b290aFxuICAgICAgICAgICAgbGV0IGFkYXB0ZXIgPSB0aGlzLmdldERldmljZUZyb21QZXJpcGhlcmFsQWRhcHRlcihkZXZpY2UubmFtZSk7XG4gICAgICAgICAgICBpZiAoYWRhcHRlciA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgYWRhcHRlciA9IG5ldyBQZXJpcGhlcmFsQWRhcHRlcihkZXZpY2UgYXMgQmx1ZXRvb3RoRGV2aWNlKTtcbiAgICAgICAgICAgICAgdGhpcy53ZWJCbHVldG9vdGhQZXJpcGhlcmFsQWRhcHRlcnMuc2V0KGRldmljZS5uYW1lLCBhZGFwdGVyKTtcbiAgICAgICAgICAgICAgLy8gc2V0IGRldmljZUFkdmVydGlzZW1lbnQgbGlzdGVuZXJcbiAgICAgICAgICAgICAgdGhpcy5zZXRXYXRjaEFkdmVydGlzZW1lbnQoYWRhcHRlcik7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIGFkZHJlc3M6IGFkYXB0ZXIuaWQsXG4gICAgICAgICAgICAgIG5hbWU6IGFkYXB0ZXIubmFtZSBhcyBzdHJpbmcsXG4gICAgICAgICAgICAgIHJzc2k6IGFkYXB0ZXIucnNzaSxcbiAgICAgICAgICAgICAgdHhQb3dlcjogYWRhcHRlci50eFBvd2VyLFxuICAgICAgICAgICAgICByZWNvcmRzOiBnZXRTY2FuUmVjb3Jkc0Zyb21CeXRlcyhcbiAgICAgICAgICAgICAgICBuZXcgVWludDhBcnJheShhZGFwdGVyLnNjYW5SZWNvcmQgfHwgW10pXG4gICAgICAgICAgICAgICksXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBkZXZpY2UgPSBkZXZpY2UgYXMgQ29yZG92YUJMRVNjYW5SZXN1bHQ7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBhZGRyZXNzOiBkZXZpY2UuYWRkcmVzcyxcbiAgICAgICAgICAgICAgbmFtZTogZGV2aWNlLm5hbWUsXG4gICAgICAgICAgICAgIHJzc2k6IGRldmljZS5yc3NpLFxuICAgICAgICAgICAgICB0eFBvd2VyOiBkZXZpY2UudHhQb3dlcixcbiAgICAgICAgICAgICAgcmVjb3JkczogZ2V0U2NhblJlY29yZHNGcm9tQnl0ZXMoXG4gICAgICAgICAgICAgICAgbmV3IFVpbnQ4QXJyYXkoZGV2aWNlLnNjYW5SZWNvcmQgfHwgW10pXG4gICAgICAgICAgICAgICksXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICBjcmVhdGVOZXdTY2FubmVyKG9wdGlvbnM/OiBCbGVTY2FubmVyT3B0aW9ucykge1xuICAgIHRoaXMuY2xlYXJQcmV2aW91c1NjYW5uZXIoKTtcbiAgICBpZiAodGhpcy5wbGF0Zm9ybS5pcygnY29yZG92YScpKSB7XG4gICAgICBpZiAob3B0aW9ucyAhPSB1bmRlZmluZWQgJiYgb3B0aW9ucy5maWx0ZXJzICE9IHVuZGVmaW5lZCkge1xuICAgICAgICB0aGlzLmJsZVNjYW5uZXIgPSBuZXcgQkxFU2Nhbm5lcih1bmRlZmluZWQsIHtcbiAgICAgICAgICBmaWx0ZXJzOiBvcHRpb25zLmZpbHRlcnMsXG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5ibGVTY2FubmVyID0gbmV3IEJMRVNjYW5uZXIodW5kZWZpbmVkKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgbGV0IHdlYkJMRU9wdGlvbnM6IFJlcXVlc3REZXZpY2VPcHRpb25zID0ge1xuICAgICAgICBhY2NlcHRBbGxEZXZpY2VzOiB0cnVlLFxuICAgICAgfTtcblxuICAgICAgY29uc3QgZmlsdGVyczogQmx1ZXRvb3RoTEVTY2FuRmlsdGVyW10gfCB1bmRlZmluZWQgPVxuICAgICAgICBvcHRpb25zPy5maWx0ZXJzPy5yZWR1Y2U8Qmx1ZXRvb3RoTEVTY2FuRmlsdGVyW10+KChhY2MsIGN1cnJlbnQpID0+IHtcbiAgICAgICAgICBpZiAoY3VycmVudC5zZXJ2aWNlcyAmJiBjdXJyZW50LnNlcnZpY2VzLmxlbmd0aCAhPSAwKSB7XG4gICAgICAgICAgICBhY2MucHVzaCh7XG4gICAgICAgICAgICAgIHNlcnZpY2VzOiBjdXJyZW50LnNlcnZpY2VzLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IG1hbnVmYWN0dXJlckRhdGFGaWx0ZXIgPSBjdXJyZW50Lm1hbnVmYWN0dXJlckRhdGE/LnJlZHVjZTxcbiAgICAgICAgICAgIEJsdWV0b290aE1hbnVmYWN0dXJlckRhdGFGaWx0ZXJbXVxuICAgICAgICAgID4oKGFjYywgY3VycmVudCkgPT4ge1xuICAgICAgICAgICAgaWYgKGN1cnJlbnQuY29tcGFueUlkZW50aWZpZXIgIT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgIGxldCBuZXdGaWx0ZXI6IGFueSA9IHtcbiAgICAgICAgICAgICAgICBjb21wYW55SWRlbnRpZmllcjogdGhpcy5jb21wYW55SWRlbnRpZmllclRvTnVtYmVyKFxuICAgICAgICAgICAgICAgICAgY3VycmVudC5jb21wYW55SWRlbnRpZmllclxuICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgLy8gZGF0YVByZWZpeDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIC8vIG1hc2s6IHVuZGVmaW5lZFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICBpZiAoY3VycmVudC5kYXRhUHJlZml4KSB7XG4gICAgICAgICAgICAgICAgbmV3RmlsdGVyLmRhdGFQcmVmaXggPSBuZXcgVWludDhBcnJheShjdXJyZW50LmRhdGFQcmVmaXgpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmIChjdXJyZW50Lm1hc2spIHtcbiAgICAgICAgICAgICAgICBuZXdGaWx0ZXIubWFzayA9IG5ldyBVaW50OEFycmF5KGN1cnJlbnQubWFzayk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgYWNjLnB1c2gobmV3RmlsdGVyIGFzIEJsdWV0b290aE1hbnVmYWN0dXJlckRhdGFGaWx0ZXIpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgICAgICB9LCBbXSk7XG4gICAgICAgICAgaWYgKG1hbnVmYWN0dXJlckRhdGFGaWx0ZXIgJiYgbWFudWZhY3R1cmVyRGF0YUZpbHRlcj8ubGVuZ3RoICE9IDApIHtcbiAgICAgICAgICAgIGFjYy5wdXNoKHtcbiAgICAgICAgICAgICAgbWFudWZhY3R1cmVyRGF0YTogbWFudWZhY3R1cmVyRGF0YUZpbHRlcixcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgICB9LCBbXSk7XG5cbiAgICAgIGlmIChmaWx0ZXJzICE9IHVuZGVmaW5lZCAmJiBmaWx0ZXJzLmxlbmd0aCAhPSAwKSB7XG4gICAgICAgIHdlYkJMRU9wdGlvbnMgPSB7XG4gICAgICAgICAgZmlsdGVycyxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5ibGVTY2FubmVyID0gbmV3IFdlYkJsdWV0b290aFNjYW5uZXIoe1xuICAgICAgICByZXF1ZXN0RGV2aWNlT3B0aW9uczogd2ViQkxFT3B0aW9ucyxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHRoaXMuc2Nhbm5lclN1YnNjcmlwdGlvbiA9IHRoaXMuYmxlU2Nhbm5lci5yZXN1bHRzLnN1YnNjcmliZSgocmVzdWx0cykgPT4ge1xuICAgICAgaWYgKG9wdGlvbnMpIHtcbiAgICAgICAgY29uc3QgbWFudWZhY3R1cmVyRGF0YUZpbHRlcnMgPSBvcHRpb25zLmZpbHRlcnM/LnJlZHVjZTxcbiAgICAgICAgICBXZWJCbHVldG9vdGhNYW51ZmFjdHVyZXJEYXRhRmlsdGVyW11cbiAgICAgICAgPigoZmlsdGVycywgY3VycmVudCkgPT4ge1xuICAgICAgICAgIGlmIChjdXJyZW50Lm1hbnVmYWN0dXJlckRhdGEpIHtcbiAgICAgICAgICAgIGZpbHRlcnMucHVzaCguLi5jdXJyZW50Lm1hbnVmYWN0dXJlckRhdGEpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gZmlsdGVycztcbiAgICAgICAgfSwgW10pO1xuICAgICAgICBpZiAobWFudWZhY3R1cmVyRGF0YUZpbHRlcnM/Lmxlbmd0aCkge1xuICAgICAgICAgIHJlc3VsdHMgPSByZXN1bHRzLmZpbHRlcigocmVzdWx0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCBzY2FuUmVjb3JkID0gKHJlc3VsdCBhcyBhbnkpLnNjYW5SZWNvcmQ7XG4gICAgICAgICAgICBpZiAoIXNjYW5SZWNvcmQ/Lmxlbmd0aCB8fCBtYW51ZmFjdHVyZXJEYXRhRmlsdGVycy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3Qgc2NhblJlY29yZHMgPSBwYXJzZVNjYW5SZWNvcmRzKHNjYW5SZWNvcmQpO1xuXG4gICAgICAgICAgICByZXR1cm4gY2hlY2tNYW51ZmFjdHVyZXJEYXRhTWF0Y2goXG4gICAgICAgICAgICAgIHNjYW5SZWNvcmRzLFxuICAgICAgICAgICAgICBtYW51ZmFjdHVyZXJEYXRhRmlsdGVyc1xuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdGhpcy5lbWl0TmV4UmVzdWx0cyhyZXN1bHRzKTtcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcy5ibGVTY2FubmVyO1xuICB9XG4gIHByaXZhdGUgZW1pdE5leFJlc3VsdHMocmVzdWx0cz86IHNjYW5uZXJSZXN1bHRbXSkge1xuICAgIHRoaXMubGFzdFJlc3VsdHMgPSByZXN1bHRzO1xuICAgIHRoaXMuX3Jlc3VsdHMkLm5leHQocmVzdWx0cyk7XG4gIH1cblxuICBwcml2YXRlIGNsZWFyUHJldmlvdXNTY2FubmVyKCkge1xuICAgIGlmICh0aGlzLmJsZVNjYW5uZXIpIHtcbiAgICAgIHRoaXMuc2Nhbm5lclN1YnNjcmlwdGlvbj8udW5zdWJzY3JpYmUoKTtcbiAgICAgIHRoaXMuZW1pdE5leFJlc3VsdHModW5kZWZpbmVkKTtcbiAgICAgIHRoaXMuYmxlU2Nhbm5lciA9IHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cblxuICBhc3luYyBzdGFydFNjYW4ob3B0aW9ucz86IEJsZVNjYW5uZXJPcHRpb25zKSB7XG4gICAgY29uc3QgbmV3U2Nhbm5lciA9IHRoaXMuY3JlYXRlTmV3U2Nhbm5lcihvcHRpb25zKTtcbiAgICByZXR1cm4gbmV3U2Nhbm5lci5zdGFydCgpO1xuICB9XG5cbiAgYXN5bmMgc3RvcFNjYW4oKSB7XG4gICAgaWYgKHRoaXMucGxhdGZvcm0uaXMoJ2NvcmRvdmEnKSkge1xuICAgICAgaWYgKHRoaXMuYmxlU2Nhbm5lciA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdCTEUgU2Nhbm5lciBub3QgZGVmaW5lZCcpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuYmxlU2Nhbm5lci5zdG9wKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIC8vIHN0b3Agd2F0Y2ggYWR2ZXJ0aXNlbWVudHNcbiAgICAgIGZvciAobGV0IFtpZCwgYWRhcHRlcl0gb2YgdGhpcy53ZWJCbHVldG9vdGhQZXJpcGhlcmFsQWRhcHRlcnMpIHtcbiAgICAgICAgdGhpcy51bnNldFdhdGNoQWR2ZXJ0aXNlbWVudChhZGFwdGVyKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLmJsZVNjYW5uZXI/LnN0b3AoKTtcbiAgICB9XG4gIH1cblxuICBjb25uZWN0QnlOYW1lKGJsZURldmljZU5hbWU6IHN0cmluZykge1xuICAgIGlmICh0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykpIHtcbiAgICAgIGlmICh0aGlzLnBsdWdpbiA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIHRocm93RXJyb3IoKCkgPT4gbmV3IEVycm9yKCdCTEUgcGx1Z2luIGlzIG5vdCBhdmFpbGFibGUnKSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5wbHVnaW4uY29ubmVjdChibGVEZXZpY2VOYW1lKS5waXBlKHNoYXJlKCkpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gZGVmZXIoYXN5bmMgKCkgPT4ge1xuICAgICAgICBjb25zdCBkZXZpY2UgPSB0aGlzLmdldERldmljZUZyb21QZXJpcGhlcmFsQWRhcHRlcihibGVEZXZpY2VOYW1lKTtcbiAgICAgICAgaWYgKGRldmljZSkge1xuICAgICAgICAgIGRldmljZS5jb25uZWN0KCkudGhlbigoKSA9PiB7XG4gICAgICAgICAgICBkZXZpY2UuX3BlcmlwaGVyYWwud2F0Y2hBZHZlcnRpc2VtZW50cygpO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIHJldHVybiBkZXZpY2Uuc3RhdGVDaGFuZ2U7XG4gICAgICAgIH1cbiAgICAgICAgdGhyb3dFcnJvcihcbiAgICAgICAgICAoKSA9PlxuICAgICAgICAgICAgbmV3IEVycm9yKGBDb25uZWN0aW9uRXJyb3IsIGNhbiBub3QgZmluZCBkZXZpY2UgJHtibGVEZXZpY2VOYW1lfWApXG4gICAgICAgICk7XG4gICAgICB9KS5waXBlKFxuICAgICAgICBmaWx0ZXJVbmRlZmluZWQoKSxcbiAgICAgICAgc3dpdGNoTWFwKCh2KSA9PiB2KVxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBkaXNjb25uZWN0QnlOYW1lKGJsZURldmljZU5hbWU6IHN0cmluZykge1xuICAgIGlmICh0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykpIHtcbiAgICAgIGlmICh0aGlzLnBsdWdpbiA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdCTEUgcGx1Z2luIGlzIG5vdCBhdmFpbGFibGUnKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnBsdWdpbi5kaXNDb25uZWN0KGJsZURldmljZU5hbWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBkZXZpY2UgPSBhd2FpdCB0aGlzLmdldERldmljZUZyb21QZXJpcGhlcmFsQWRhcHRlck9yVGhyb3coXG4gICAgICAgIGJsZURldmljZU5hbWVcbiAgICAgICk7XG4gICAgICAvLyBzdG9wIHdhdGNoIGFkdmVydGlzZW1lbnRzXG4gICAgICB0aGlzLnVuc2V0V2F0Y2hBZHZlcnRpc2VtZW50KGRldmljZSk7XG4gICAgICBhd2FpdCBkZXZpY2UuZGlzY29ubmVjdCgpO1xuICAgICAgcmV0dXJuICdPSyc7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgY2xvc2VCeU5hbWUoYmxlRGV2aWNlTmFtZTogc3RyaW5nKTogUHJvbWlzZTwnT0snPiB7XG4gICAgaWYgKHRoaXMucGxhdGZvcm0uaXMoJ2NvcmRvdmEnKSkge1xuICAgICAgaWYgKHRoaXMucGx1Z2luID09IHVuZGVmaW5lZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0JMRSBwbHVnaW4gaXMgbm90IGF2YWlsYWJsZScpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMucGx1Z2luLmNsb3NlKGJsZURldmljZU5hbWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBOb3RoaW5nIHRvIGRvIG9uIHdlYiBicm93c2VyXG4gICAgICByZXR1cm4gJ09LJztcbiAgICB9XG4gIH1cblxuICBhc3luYyByZWFkQ2hhcmFjdGVyaXN0aWMoXG4gICAgZGV2aWNlTmFtZTogc3RyaW5nLFxuICAgIHNlcnZpY2VJZDogc3RyaW5nLFxuICAgIGNoYXJhY3RlcmlzdGljSWQ6IHN0cmluZ1xuICApIHtcbiAgICBpZiAodGhpcy5wbGF0Zm9ybS5pcygnY29yZG92YScpKSB7XG4gICAgICBpZiAodGhpcy5wbHVnaW4gPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQkxFIHBsdWdpbiBpcyBub3QgYXZhaWxhYmxlJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5wbHVnaW4uY2hhcmFjdGVyaXN0aWNSZWFkVmFsdWUoXG4gICAgICAgIGRldmljZU5hbWUsXG4gICAgICAgIHNlcnZpY2VJZCxcbiAgICAgICAgY2hhcmFjdGVyaXN0aWNJZFxuICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgY2hhcmFjdGVyaXN0aWMgPVxuICAgICAgICBhd2FpdCB0aGlzLmdldENoYXJhY3RlcmlzdGljRnJvbVBlcmlwaGVyYWxBZGFwdGVyUHJvbWlzZShcbiAgICAgICAgICBkZXZpY2VOYW1lLFxuICAgICAgICAgIHNlcnZpY2VJZCxcbiAgICAgICAgICBjaGFyYWN0ZXJpc3RpY0lkXG4gICAgICAgICk7XG4gICAgICByZXR1cm4gY2hhcmFjdGVyaXN0aWMucmVhZCgpO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIHdyaXRlQ2hhcmFjdGVyaXN0aWMoXG4gICAgZGV2aWNlTmFtZTogc3RyaW5nLFxuICAgIHNlcnZpY2VJZDogc3RyaW5nLFxuICAgIGNoYXJhY3RlcmlzdGljSWQ6IHN0cmluZyxcbiAgICBkYXRhOiBVaW50OEFycmF5LFxuICAgIHdpdGhSZXNwb25zZT86IGJvb2xlYW5cbiAgKTogUHJvbWlzZTx0cnVlPiB7XG4gICAgaWYgKHRoaXMucGxhdGZvcm0uaXMoJ2NvcmRvdmEnKSkge1xuICAgICAgaWYgKHRoaXMucGx1Z2luID09IHVuZGVmaW5lZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0JMRSBwbHVnaW4gaXMgbm90IGF2YWlsYWJsZScpO1xuICAgICAgfVxuICAgICAgbGV0IHdyaXRlUHJvbWlzZTogUHJvbWlzZTwnT0snPjtcbiAgICAgIGlmICh3aXRoUmVzcG9uc2UpIHtcbiAgICAgICAgd3JpdGVQcm9taXNlID0gdGhpcy5wbHVnaW4uY2hhcmFjdGVyaXN0aWNXcml0ZShcbiAgICAgICAgICBkZXZpY2VOYW1lLFxuICAgICAgICAgIHNlcnZpY2VJZCxcbiAgICAgICAgICBjaGFyYWN0ZXJpc3RpY0lkLFxuICAgICAgICAgIGRhdGFcbiAgICAgICAgKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHdyaXRlUHJvbWlzZSA9IHRoaXMucGx1Z2luLmNoYXJhY3RlcmlzdGljV3JpdGVXaXRob3V0UmVzcG9uc2UoXG4gICAgICAgICAgZGV2aWNlTmFtZSxcbiAgICAgICAgICBzZXJ2aWNlSWQsXG4gICAgICAgICAgY2hhcmFjdGVyaXN0aWNJZCxcbiAgICAgICAgICBkYXRhXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICByZXR1cm4gd3JpdGVQcm9taXNlLnRoZW4oKF8pID0+IHRydWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBjaGFyYWN0ZXJpc3RpYyA9XG4gICAgICAgIGF3YWl0IHRoaXMuZ2V0Q2hhcmFjdGVyaXN0aWNGcm9tUGVyaXBoZXJhbEFkYXB0ZXJQcm9taXNlKFxuICAgICAgICAgIGRldmljZU5hbWUsXG4gICAgICAgICAgc2VydmljZUlkLFxuICAgICAgICAgIGNoYXJhY3RlcmlzdGljSWRcbiAgICAgICAgKTtcbiAgICAgIGF3YWl0IGNoYXJhY3RlcmlzdGljLndyaXRlKGRhdGEsICEhd2l0aFJlc3BvbnNlKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfVxuXG4gIGxpc3RlbkNoYXJhY3RlcmlzdGljQ2hhbmdlKFxuICAgIGRldmljZU5hbWU6IHN0cmluZyxcbiAgICBzZXJ2aWNlSWQ6IHN0cmluZyxcbiAgICBjaGFyYWN0ZXJpc3RpY0lkOiBzdHJpbmdcbiAgKTogT2JzZXJ2YWJsZTxVaW50OEFycmF5PiB7XG4gICAgaWYgKHRoaXMucGxhdGZvcm0uaXMoJ2NvcmRvdmEnKSkge1xuICAgICAgaWYgKHRoaXMucGx1Z2luID09IHVuZGVmaW5lZCkge1xuICAgICAgICByZXR1cm4gdGhyb3dFcnJvcihuZXcgRXJyb3IoJ0JMRSBwbHVnaW4gaXMgbm90IGF2YWlsYWJsZScpKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnBsdWdpblxuICAgICAgICAuY2hhcmFjdGVyaXN0aWNDaGFuZ2VkKGRldmljZU5hbWUsIHNlcnZpY2VJZCwgY2hhcmFjdGVyaXN0aWNJZClcbiAgICAgICAgLnBpcGUoXG4gICAgICAgICAgcnVuSW5ab25lKHRoaXMubmdab25lKVxuICAgICAgICAgIC8vIHNoYXJlKCksXG4gICAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB0aGlzLmdldENoYXJhY3RlcmlzdGljRnJvbVBlcmlwaGVyYWxBZGFwdGVyT2JzZXJ2YWJsZShcbiAgICAgICAgZGV2aWNlTmFtZSxcbiAgICAgICAgc2VydmljZUlkLFxuICAgICAgICBjaGFyYWN0ZXJpc3RpY0lkXG4gICAgICApLnBpcGUoXG4gICAgICAgIHN3aXRjaE1hcCgoY2hhcikgPT4ge1xuICAgICAgICAgIHJldHVybiBjaGFyLmRhdGE7XG4gICAgICAgIH0pLFxuICAgICAgICBtYXAoKGRhdGEpID0+IGRhdGEuZGF0YSksXG4gICAgICAgIHJ1bkluWm9uZSh0aGlzLm5nWm9uZSlcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZGlzY292ZXJTZXJ2aWNlcyhkZXZpY2VOYW1lOiBzdHJpbmcpOiBQcm9taXNlPFNlcnZpY2VMaXN0PiB7XG4gICAgaWYgKHRoaXMucGxhdGZvcm0uaXMoJ2NvcmRvdmEnKSkge1xuICAgICAgaWYgKHRoaXMucGx1Z2luID09IHVuZGVmaW5lZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0JMRSBwbHVnaW4gaXMgbm90IGF2YWlsYWJsZScpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMucGx1Z2luLmRpc2NvdmVyU2VydmljZXMoZGV2aWNlTmFtZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGRldmljZSA9IGF3YWl0IHRoaXMuZ2V0RGV2aWNlRnJvbVBlcmlwaGVyYWxBZGFwdGVyT3JUaHJvdyhcbiAgICAgICAgZGV2aWNlTmFtZVxuICAgICAgKTtcbiAgICAgIGlmICghZGV2aWNlLl9wZXJpcGhlcmFsLmdhdHQ/LmNvbm5lY3RlZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYGRldmljZSAke2RldmljZU5hbWV9IGlzIG5vdCBjb25uZWN0ZWRgKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHNlcnZpY2VzQWRhcHRlcnMgPSBhd2FpdCBkZXZpY2UuZGlzY292ZXJTZXJ2aWNlcygpO1xuICAgICAgY29uc3Qgc2VydmljZXM6IFNlcnZpY2VMaXN0ID0gW107XG4gICAgICBmb3IgKGxldCBzZXJ2aWNlTmFtZSBpbiBzZXJ2aWNlc0FkYXB0ZXJzKSB7XG4gICAgICAgIGNvbnN0IGFkYXB0ZXIgPSBzZXJ2aWNlc0FkYXB0ZXJzW3NlcnZpY2VOYW1lXTtcbiAgICAgICAgaWYgKGFkYXB0ZXIpIHtcbiAgICAgICAgICBjb25zdCBkZXNjcmlwdGlvbjogU2VydmljZURlc2NyaXB0aW9uID0ge1xuICAgICAgICAgICAgdXVpZDogYWRhcHRlci51dWlkLFxuICAgICAgICAgIH07XG4gICAgICAgICAgY29uc3QgY2hhcmFjdGVyaXN0aWNEZXNjcmlwdGlvbnM6IENoYXJhY3RlcmlzdGljRGVzY3JpcHRpb25bXSA9IFtdO1xuICAgICAgICAgIGZvciAobGV0IGNoYXJhY3RlcmlzdGljIG9mIGF3YWl0IGFkYXB0ZXIuZ2V0Q2hhcmFjdGVyaXN0aWNzKCkpIHtcbiAgICAgICAgICAgIGNvbnN0IGRlc2NyaXB0b3JzID0gYXdhaXQgY2hhcmFjdGVyaXN0aWMuZ2V0RGVzY3JpcHRvcnMoKTtcbiAgICAgICAgICAgIGNoYXJhY3RlcmlzdGljRGVzY3JpcHRpb25zLnB1c2goe1xuICAgICAgICAgICAgICB1dWlkOiBjaGFyYWN0ZXJpc3RpYy51dWlkLFxuICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiBjaGFyYWN0ZXJpc3RpYy5wcm9wZXJ0aWVzLFxuICAgICAgICAgICAgICBkZXNjcmlwdG9yczogZGVzY3JpcHRvcnMubWFwKChkZXNjKSA9PiB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHsgdXVpZDogZGVzYy51dWlkLCB2YWx1ZTogZGVzYy52YWx1ZSB9O1xuICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoY2hhcmFjdGVyaXN0aWNEZXNjcmlwdGlvbnMubGVuZ3RoICE9IDApIHtcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uLmNoYXJhY3RlcmlzdGljcyA9IGNoYXJhY3RlcmlzdGljRGVzY3JpcHRpb25zO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHNlcnZpY2VzLnB1c2goZGVzY3JpcHRpb24pO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBzZXJ2aWNlcztcbiAgICB9XG4gIH1cblxuICBhc3luYyBzdGFydE5vdGlmaWNhdGlvbihcbiAgICBkZXZpY2VOYW1lOiBzdHJpbmcsXG4gICAgc2VydmljZUlkOiBzdHJpbmcsXG4gICAgY2hhcmFjdGVyaXN0aWNJZDogc3RyaW5nXG4gICk6IFByb21pc2U8J09LJz4ge1xuICAgIGlmICh0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykpIHtcbiAgICAgIGlmICh0aGlzLnBsdWdpbiA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdCTEUgcGx1Z2luIGlzIG5vdCBhdmFpbGFibGUnKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnBsdWdpbi5jaGFyYWN0ZXJpc3RpY1N0YXJ0Tm90aWZpY2F0aW9uKFxuICAgICAgICBkZXZpY2VOYW1lLFxuICAgICAgICBzZXJ2aWNlSWQsXG4gICAgICAgIGNoYXJhY3RlcmlzdGljSWRcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGNoYXJhY3RlcmlzdGljID1cbiAgICAgICAgYXdhaXQgdGhpcy5nZXRDaGFyYWN0ZXJpc3RpY0Zyb21QZXJpcGhlcmFsQWRhcHRlclByb21pc2UoXG4gICAgICAgICAgZGV2aWNlTmFtZSxcbiAgICAgICAgICBzZXJ2aWNlSWQsXG4gICAgICAgICAgY2hhcmFjdGVyaXN0aWNJZFxuICAgICAgICApO1xuICAgICAgYXdhaXQgY2hhcmFjdGVyaXN0aWMuZW5hYmxlTm90aWZpY2F0aW9ucyh0cnVlKTtcbiAgICAgIHJldHVybiAnT0snO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIHN0b3BOb3RpZmljYXRpb24oXG4gICAgZGV2aWNlTmFtZTogc3RyaW5nLFxuICAgIHNlcnZpY2VJZDogc3RyaW5nLFxuICAgIGNoYXJhY3RlcmlzdGljSWQ6IHN0cmluZ1xuICApOiBQcm9taXNlPHRydWU+IHtcbiAgICBpZiAodGhpcy5wbGF0Zm9ybS5pcygnY29yZG92YScpKSB7XG4gICAgICBpZiAodGhpcy5wbHVnaW4gPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQkxFIHBsdWdpbiBpcyBub3QgYXZhaWxhYmxlJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5wbHVnaW5cbiAgICAgICAgLmNoYXJhY3RlcmlzdGljU3RvcE5vdGlmaWNhdGlvbihkZXZpY2VOYW1lLCBzZXJ2aWNlSWQsIGNoYXJhY3RlcmlzdGljSWQpXG4gICAgICAgIC50aGVuKChyKSA9PiB0cnVlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgY2hhcmFjdGVyaXN0aWMgPVxuICAgICAgICBhd2FpdCB0aGlzLmdldENoYXJhY3RlcmlzdGljRnJvbVBlcmlwaGVyYWxBZGFwdGVyUHJvbWlzZShcbiAgICAgICAgICBkZXZpY2VOYW1lLFxuICAgICAgICAgIHNlcnZpY2VJZCxcbiAgICAgICAgICBjaGFyYWN0ZXJpc3RpY0lkXG4gICAgICAgICk7XG4gICAgICBhd2FpdCBjaGFyYWN0ZXJpc3RpYy5lbmFibGVOb3RpZmljYXRpb25zKGZhbHNlKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZ2V0RGV2aWNlRnJvbVBlcmlwaGVyYWxBZGFwdGVyKGRldmljZU5hbWU6IHN0cmluZykge1xuICAgIGNvbnN0IGRldmljZSA9IHRoaXMud2ViQmx1ZXRvb3RoUGVyaXBoZXJhbEFkYXB0ZXJzLmdldChkZXZpY2VOYW1lKTtcbiAgICBpZiAoZGV2aWNlICE9IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIGRldmljZTtcbiAgICB9XG4gICAgLy90cnkgd2l0aCBpZFxuICAgIGZvciAobGV0IFtpZCwgYWRhcHRlcl0gb2YgdGhpcy53ZWJCbHVldG9vdGhQZXJpcGhlcmFsQWRhcHRlcnMpIHtcbiAgICAgIGlmIChhZGFwdGVyLmlkID09IGRldmljZU5hbWUpIHtcbiAgICAgICAgcmV0dXJuIGFkYXB0ZXI7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGdldERldmljZUZyb21QZXJpcGhlcmFsQWRhcHRlck9yVGhyb3coZGV2aWNlTmFtZTogc3RyaW5nKSB7XG4gICAgY29uc3QgZGV2aWNlID0gdGhpcy5nZXREZXZpY2VGcm9tUGVyaXBoZXJhbEFkYXB0ZXIoZGV2aWNlTmFtZSk7XG4gICAgaWYgKGRldmljZSkge1xuICAgICAgcmV0dXJuIGRldmljZTtcbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKGBDYW4gbm90IGZpbmQgZGV2aWNlICR7ZGV2aWNlfWApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBnZXREZXZpY2VTZXJ2aWNlRnJvbVBlcmlwaGVyYWxBZGFwdGVyKFxuICAgIGRldmljZU5hbWU6IHN0cmluZyxcbiAgICBzZXJ2aWNlSWQ6IHN0cmluZ1xuICApIHtcbiAgICBjb25zdCBkZXZpY2UgPSBhd2FpdCB0aGlzLmdldERldmljZUZyb21QZXJpcGhlcmFsQWRhcHRlck9yVGhyb3coZGV2aWNlTmFtZSk7XG4gICAgY29uc3Qgc2VydmljZSA9IGF3YWl0IGRldmljZS5nZXRTZXJ2aWNlKHNlcnZpY2VJZCk7XG4gICAgaWYgKHNlcnZpY2UgPT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYENhbiBub3QgZmluZCBzZXJ2aWNlICR7c2VydmljZUlkfSBvbiBkZXZpY2UgJHtkZXZpY2V9YCk7XG4gICAgfVxuICAgIHJldHVybiBzZXJ2aWNlO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRDaGFyYWN0ZXJpc3RpY0Zyb21QZXJpcGhlcmFsQWRhcHRlck9ic2VydmFibGUoXG4gICAgZGV2aWNlTmFtZTogc3RyaW5nLFxuICAgIHNlcnZpY2VJZDogc3RyaW5nLFxuICAgIGNoYXJhY3RlcmlzdGljSWQ6IHN0cmluZ1xuICApIHtcbiAgICByZXR1cm4gZGVmZXIoKCkgPT4ge1xuICAgICAgcmV0dXJuIHRoaXMuZ2V0Q2hhcmFjdGVyaXN0aWNGcm9tUGVyaXBoZXJhbEFkYXB0ZXJQcm9taXNlKFxuICAgICAgICBkZXZpY2VOYW1lLFxuICAgICAgICBzZXJ2aWNlSWQsXG4gICAgICAgIGNoYXJhY3RlcmlzdGljSWRcbiAgICAgICk7XG4gICAgfSkucGlwZShmaWx0ZXJVbmRlZmluZWQoKSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGdldENoYXJhY3RlcmlzdGljRnJvbVBlcmlwaGVyYWxBZGFwdGVyUHJvbWlzZShcbiAgICBkZXZpY2VOYW1lOiBzdHJpbmcsXG4gICAgc2VydmljZUlkOiBzdHJpbmcsXG4gICAgY2hhcmFjdGVyaXN0aWNJZDogc3RyaW5nXG4gICkge1xuICAgIGNvbnN0IHNlcnZpY2UgPSBhd2FpdCB0aGlzLmdldERldmljZVNlcnZpY2VGcm9tUGVyaXBoZXJhbEFkYXB0ZXIoXG4gICAgICBkZXZpY2VOYW1lLFxuICAgICAgc2VydmljZUlkXG4gICAgKTtcbiAgICBpZiAoc2VydmljZSkge1xuICAgICAgcmV0dXJuIGF3YWl0IHNlcnZpY2UuZ2V0Q2hhcmFjdGVyaXN0aWMoY2hhcmFjdGVyaXN0aWNJZCk7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBDYW4gbm90IGZpbmQgY2hhcmFjdGVyaXN0aWMgJHtjaGFyYWN0ZXJpc3RpY0lkfSBvbiBzZXJ2aWNlICR7c2VydmljZUlkfSBvbiBkZXZpY2UgJHtkZXZpY2VOYW1lfWBcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXRXYXRjaEFkdmVydGlzZW1lbnQoYWRhcHRlcjogUGVyaXBoZXJhbEFkYXB0ZXJXaXRoUmVjb3Jkcykge1xuICAgIGNvbnNvbGUubG9nKCdzZXRXYXRjaEFkdmVydGlzZW1lbnQnLCBhZGFwdGVyLm5hbWUpO1xuICAgIGFkYXB0ZXIuX3BlcmlwaGVyYWwuYWRkRXZlbnRMaXN0ZW5lcihcbiAgICAgICdhZHZlcnRpc2VtZW50cmVjZWl2ZWQnLFxuICAgICAgKGV2ZW50KSA9PiB7XG4gICAgICAgIGNvbnNvbGUubG9nKCdVUERBVEUgQURWRVJUSVNFTUVOVCcpO1xuICAgICAgICBjb25zb2xlLmxvZyhldmVudCk7XG4gICAgICAgIHRoaXMudXBkYXRlQWR2ZXJ0aXNlbWVudERhdGEoZXZlbnQsIGFkYXB0ZXIpO1xuICAgICAgfSxcbiAgICAgIHRydWVcbiAgICApO1xuXG4gICAgYWRhcHRlci53YXRjaEFkdmVydGlzZW1lbnRzQWJvcnRDb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuXG4gICAgYWRhcHRlci5fcGVyaXBoZXJhbC53YXRjaEFkdmVydGlzZW1lbnRzKHtcbiAgICAgIHNpZ25hbDogYWRhcHRlci53YXRjaEFkdmVydGlzZW1lbnRzQWJvcnRDb250cm9sbGVyLnNpZ25hbCxcbiAgICB9KTtcblxuICAgIC8vIChhZGFwdGVyLl9wZXJpcGhlcmFsIGFzIGFueSkub25hZHZlcnRpc2VtZW50cmVjZWl2ZWQgPSAoZXZlbnQ6IGFueSkgPT4ge1xuICAgIC8vICAgY29uc29sZS5sb2coXCJVUERBVEUgQURWRVJUSVNFTUVOVFwiKTtcbiAgICAvLyAgIGNvbnNvbGUubG9nKGV2ZW50KTtcbiAgICAvLyB9XG4gIH1cbiAgcHJpdmF0ZSB1bnNldFdhdGNoQWR2ZXJ0aXNlbWVudChhZGFwdGVyOiBQZXJpcGhlcmFsQWRhcHRlcldpdGhSZWNvcmRzKSB7XG4gICAgLy8gYWRhcHRlci5fcGVyaXBoZXJhbC5yZW1vdmVFdmVudExpc3RlbmVyKCdhZHZlcnRpc2VtZW50cmVjZWl2ZWQnLCBudWxsKTtcbiAgICBhZGFwdGVyLndhdGNoQWR2ZXJ0aXNlbWVudHNBYm9ydENvbnRyb2xsZXI/LmFib3J0KCk7XG4gIH1cblxuICBwcml2YXRlIHVwZGF0ZUFkdmVydGlzZW1lbnREYXRhKFxuICAgIGV2ZW50OiBhbnksXG4gICAgYWRhcHRlcjogUGVyaXBoZXJhbEFkYXB0ZXJXaXRoUmVjb3Jkc1xuICApIHtcbiAgICBhZGFwdGVyLnJzc2kgPSBldmVudC5yc3NpO1xuICAgIGFkYXB0ZXIudHhQb3dlciA9IGV2ZW50LnR4UG93ZXI7XG4gICAgaWYgKGV2ZW50Lm1hbnVmYWN0dXJlckRhdGEpIHtcbiAgICAgIGFkYXB0ZXIuc2NhblJlY29yZCA9IFtdO1xuICAgICAgZm9yIChsZXQgW2tleSwgdmFsdWVdIG9mIGV2ZW50Lm1hbnVmYWN0dXJlckRhdGEgYXMgTWFwPFxuICAgICAgICBudW1iZXIsXG4gICAgICAgIEFycmF5QnVmZmVyXG4gICAgICA+KSB7XG4gICAgICAgIGFkYXB0ZXIuc2NhblJlY29yZC5wdXNoKDB4ZmYsIChrZXkgJiAweGZmMDApID4+IDgsIGtleSAmIDB4ZmYpO1xuICAgICAgICBhZGFwdGVyLnNjYW5SZWNvcmQucHVzaCguLi5uZXcgVWludDhBcnJheSh2YWx1ZSkpO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLmVtaXROZXhSZXN1bHRzKHRoaXMubGFzdFJlc3VsdHMpO1xuICB9XG5cbiAgcHJpdmF0ZSBjb21wYW55SWRlbnRpZmllclRvTnVtYmVyKFxuICAgIGNvbXBhbnlJZGVudGlmaWVyOiBudW1iZXIgfCBudW1iZXJbXSB8IFVpbnQ4QXJyYXlcbiAgKSB7XG4gICAgaWYgKHR5cGVvZiBjb21wYW55SWRlbnRpZmllciA9PSAnbnVtYmVyJykge1xuICAgICAgcmV0dXJuIGNvbXBhbnlJZGVudGlmaWVyO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gbmV3IEFycmF5KGNvbXBhbnlJZGVudGlmaWVyKS5yZWR1Y2U8bnVtYmVyPigoYWNjLCBuZXh0LCBpKSA9PiB7XG4gICAgICAgIGFjYyArPSBuZXh0W2ldIDw8IGk7XG4gICAgICAgIHJldHVybiBhY2M7XG4gICAgICB9LCAwKTtcbiAgICB9XG4gIH1cbn1cbiJdfQ==