UNPKG

ngx-pwa

Version:

Provides functionality around the progressive web app functionality in angular. Most notably the an approach to cache POST, UPDATE and DELETE requests.

260 lines 37.9 kB
import { __decorate, __param } from "tslib"; import { isPlatformBrowser } from '@angular/common'; import { Inject, InjectionToken, PLATFORM_ID } from '@angular/core'; import { BehaviorSubject, firstValueFrom } from 'rxjs'; import { LodashUtilities } from '../encapsulation/lodash.utilities'; import { HttpMethod } from '../models/http-method.enum'; // eslint-disable-next-line jsdoc/require-jsdoc, typescript/typedef export const NGX_PWA_OFFLINE_SERVICE = new InjectionToken('Provider for the OfflineService used eg. in the offline request interceptor.', { providedIn: 'root', factory: () => { // eslint-disable-next-line no-console console.error( // eslint-disable-next-line stylistic/max-len 'No OfflineService has been provided for the token NGX_OFFLINE_SERVICE\nAdd this to your app.module.ts provider array:\n{\n provide: NGX_PWA_OFFLINE_SERVICE,\n useExisting: MyOfflineService\n}'); } }); /** * The base class for an offline service. */ let NgxPwaOfflineService = class NgxPwaOfflineService { http; snackBar; zone; platformId; /** * The key under which any requests are saved in local storage. */ CACHED_REQUESTS_KEY = 'requests'; /** * The prefix of offline generated ids. * Is used to check if a request still has unresolved dependencies. */ OFFLINE_ID_PREFIX = 'offline'; /** * A snackbar message to display when the synchronization of all cached requests has been finished. */ ALL_SYNC_FINISHED_SNACK_BAR_MESSAGE = 'Synchronization finished'; /** * A snackbar message to display when the synchronization of all cached requests fails. */ ALL_SYNC_FAILED_SNACK_BAR_MESSAGE = 'Synchronization failed, please try again later'; /** * A snackbar message to display when the synchronization of a single cached requests has been finished. */ SINGLE_SYNC_FINISHED_SNACK_BAR_MESSAGE = 'Synchronization finished'; /** * A snackbar message to display when the synchronization of a single cached requests fails. */ SINGLE_SYNC_FAILED_SNACK_BAR_MESSAGE = 'Synchronization failed, please try again later'; /** * Whether or not the user has no internet connection. */ isOffline = false; /** * A subject of all the requests that have been done while offline. * Needs to be used for applying offline data or syncing the requests to the api. */ cachedRequestsSubject; // eslint-disable-next-line jsdoc/require-returns /** * The currently stored cached requests (if there are any). */ get cachedRequests() { return this.cachedRequestsSubject.value; } set cachedRequests(cachedRequests) { if (!isPlatformBrowser(this.platformId)) { return; } localStorage.setItem(this.CACHED_REQUESTS_KEY, JSON.stringify(cachedRequests)); this.cachedRequestsSubject.next(cachedRequests); } constructor(http, snackBar, zone, platformId) { this.http = http; this.snackBar = snackBar; this.zone = zone; this.platformId = platformId; if (!isPlatformBrowser(platformId)) { this.isOffline = false; this.cachedRequestsSubject = new BehaviorSubject([]); return; } this.isOffline = !navigator.onLine; window.ononline = () => this.isOffline = !navigator.onLine; window.onoffline = () => this.isOffline = !navigator.onLine; const stringData = localStorage.getItem(this.CACHED_REQUESTS_KEY); const requestsData = stringData ? JSON.parse(stringData) : []; this.cachedRequestsSubject = new BehaviorSubject(requestsData); } /** * Applies any offline data that has been cached to the given values. * @param type - The type of the provided entities. Is needed to check if any cached requests of the same type exist. * @param entities - The already existing data. * @returns The already existing entities extended/modified by the offline cached requests. */ applyOfflineData(type, entities) { if (!this.cachedRequests.length) { return entities; } const res = Array.from(entities); const cachedRequests = this.cachedRequests.filter(req => req.metadata.type === type); for (const req of cachedRequests) { switch (req.request.method) { case HttpMethod.POST: { res.push(req.request.body); break; } case HttpMethod.PATCH: { const patchIdKey = req.metadata.idKey; const index = res.findIndex(e => req.request.urlWithParams.includes(`${e[patchIdKey]}`)); res[index] = this.updateOffline(req.request.body, res[index]); break; } case HttpMethod.DELETE: { const deleteIdKey = req.metadata.idKey; res.splice(res.findIndex(e => req.request.urlWithParams.includes(`${e[deleteIdKey]}`)), 1); break; } default: { // eslint-disable-next-line no-console console.error('There was an unknown http-method in one of your cached offline requests:', req.request.method); break; } } } return res; } /** * Applies an UPDATE to an entity without sending a request to the server. * @param changes - The changes that should be made to the entity. * @param entity - The entity that should be updated. * @returns The updated entity. */ updateOffline(changes, entity) { for (const key in changes) { entity[key] = changes[key]; } return entity; } /** * Sends a specific cached request to the server. * @param request - The request that should be synced. */ async sync(request) { const cachedRequestsPriorChanges = LodashUtilities.cloneDeep(this.cachedRequests); try { const res = await this.syncSingleRequest(request); this.zone.run(() => { this.snackBar.open(this.SINGLE_SYNC_FINISHED_SNACK_BAR_MESSAGE, undefined, { duration: 2500 }); }); this.removeSingleRequest(request); this.updateOfflineIdsInRequests(request, res); } catch { this.zone.run(() => { this.snackBar.open(this.SINGLE_SYNC_FAILED_SNACK_BAR_MESSAGE, undefined, { duration: 2500 }); }); this.cachedRequests = cachedRequestsPriorChanges; } } /** * Sends all cached requests to the server. Tries to handle dependencies of requests on each other. */ async syncAll() { const cachedRequestsPriorChanges = LodashUtilities.cloneDeep(this.cachedRequests); try { await this.syncAllRecursive(); this.zone.run(() => { this.snackBar.open(this.ALL_SYNC_FINISHED_SNACK_BAR_MESSAGE, undefined, { duration: 2500 }); }); this.cachedRequests = []; } catch { this.zone.run(() => { this.snackBar.open(this.ALL_SYNC_FAILED_SNACK_BAR_MESSAGE, undefined, { duration: 2500 }); }); this.cachedRequests = cachedRequestsPriorChanges; } } /** * The recursive method used to syn all requests to the api. */ async syncAllRecursive() { // eslint-disable-next-line stylistic/max-len const request = this.cachedRequests.find(r => !this.hasUnresolvedDependency(r)); if (!request) { return; } const res = await this.syncSingleRequest(request); this.updateOfflineIdsInRequests(request, res); await this.syncAllRecursive(); } /** * Sends a single cached request to the server. * @param request - The request that should be synced. * @returns A promise of the request result. */ async syncSingleRequest(request) { if (this.isOffline || this.hasUnresolvedDependency(request)) { throw new Error('Could not sync the request'); } const requestObservable = this.request(request); if (!requestObservable) { throw new Error('Could not sync the request'); } return await firstValueFrom(requestObservable); } updateOfflineIdsInRequests(request, res) { if (this.cachedRequests.length && request.request.body != undefined) { const idKey = request.metadata.idKey; if (res[idKey] != undefined) { const requestsString = `${this.cachedRequests}`.split(request.request.body[idKey]).join(res[idKey]); this.cachedRequests = JSON.parse(requestsString); } } } /** * Calls http.post/patch/delete etc. On the provided request. * @param request - The request that should be sent. * @returns The observable of the request or undefined if something went wrong. */ request(request) { switch (request.request.method) { case HttpMethod.POST: { return this.http.post(request.request.urlWithParams, LodashUtilities.omit(request.request.body, request.metadata.idKey)); } case HttpMethod.PATCH: { return this.http.patch(request.request.urlWithParams, request.request.body); } case HttpMethod.DELETE: { return this.http.delete(request.request.urlWithParams); } default: { return undefined; } } } /** * Checks if the given request has an unresolved dependency by looking for the keyword 'offline' inside of it. * @param request - The request that should be checked. * @returns Whether or no the given request has an unresolved dependency. */ hasUnresolvedDependency(request) { return request.request.urlWithParams.includes(this.OFFLINE_ID_PREFIX) || `${request.request.body}`.includes(this.OFFLINE_ID_PREFIX); } /** * Removes a single request from the cache. * @param request - The request that should be removed. */ removeSingleRequest(request) { this.cachedRequests.splice(this.cachedRequests.indexOf(request), 1); this.cachedRequests = this.cachedRequests; } }; NgxPwaOfflineService = __decorate([ __param(3, Inject(PLATFORM_ID)) ], NgxPwaOfflineService); export { NgxPwaOfflineService }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2ZmbGluZS5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LXB3YS9zcmMvc2VydmljZXMvb2ZmbGluZS5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUVwRCxPQUFPLEVBQUUsTUFBTSxFQUFFLGNBQWMsRUFBVSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFNUUsT0FBTyxFQUFFLGVBQWUsRUFBYyxjQUFjLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFbkUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ3BFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQVF4RCxtRUFBbUU7QUFDbkUsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsSUFBSSxjQUFjLENBQ3JELDhFQUE4RSxFQUM5RTtJQUNJLFVBQVUsRUFBRSxNQUFNO0lBQ2xCLE9BQU8sRUFBRSxHQUFHLEVBQUU7UUFDZCxzQ0FBc0M7UUFDbEMsT0FBTyxDQUFDLEtBQUs7UUFDVCw2Q0FBNkM7UUFDN0MsdU1BQXVNLENBQzFNLENBQUM7SUFDTixDQUFDO0NBQ0osQ0FDSixDQUFDO0FBaUJGOztHQUVHO0FBQ0ksSUFBTSxvQkFBb0IsR0FBMUIsTUFBTSxvQkFBb0I7SUE0RFI7SUFDQTtJQUNBO0lBRUE7SUEvRHJCOztPQUVHO0lBQ00sbUJBQW1CLEdBQVcsVUFBVSxDQUFDO0lBRWxEOzs7T0FHRztJQUNNLGlCQUFpQixHQUFXLFNBQVMsQ0FBQztJQUUvQzs7T0FFRztJQUNnQixtQ0FBbUMsR0FBVywwQkFBMEIsQ0FBQztJQUU1Rjs7T0FFRztJQUNnQixpQ0FBaUMsR0FBVyxnREFBZ0QsQ0FBQztJQUVoSDs7T0FFRztJQUNnQixzQ0FBc0MsR0FBVywwQkFBMEIsQ0FBQztJQUUvRjs7T0FFRztJ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QUMsQ0FBQztRQUNwRSxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUM7SUFDOUMsQ0FBQztDQUNKLENBQUE7QUEzUVksb0JBQW9CO0lBK0R4QixXQUFBLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQTtHQS9EZixvQkFBb0IsQ0EyUWhDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgaXNQbGF0Zm9ybUJyb3dzZXIgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgSHR0cENsaWVudCwgSHR0cFJlcXVlc3QgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyBJbmplY3QsIEluamVjdGlvblRva2VuLCBOZ1pvbmUsIFBMQVRGT1JNX0lEIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBNYXRTbmFja0JhciB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL3NuYWNrLWJhcic7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIE9ic2VydmFibGUsIGZpcnN0VmFsdWVGcm9tIH0gZnJvbSAncnhqcyc7XG5cbmltcG9ydCB7IExvZGFzaFV0aWxpdGllcyB9IGZyb20gJy4uL2VuY2Fwc3VsYXRpb24vbG9kYXNoLnV0aWxpdGllcyc7XG5pbXBvcnQgeyBIdHRwTWV0aG9kIH0gZnJvbSAnLi4vbW9kZWxzL2h0dHAtbWV0aG9kLmVudW0nO1xuaW1wb3J0IHsgUmVxdWVzdE1ldGFkYXRhSW50ZXJuYWwgfSBmcm9tICcuLi9tb2RlbHMvcmVxdWVzdC1tZXRhZGF0YS1pbnRlcm5hbC5tb2RlbCc7XG5cbi8qKlxuICogVGhlIEdlbmVyaWMgQmFzZSBFbnRpdHlUeXBlLlxuICovXG5leHBvcnQgdHlwZSBCYXNlRW50aXR5VHlwZTxUPiA9IHsgW0sgaW4ga2V5b2YgVF06IHVua25vd24gfTtcblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGpzZG9jL3JlcXVpcmUtanNkb2MsIHR5cGVzY3JpcHQvdHlwZWRlZlxuZXhwb3J0IGNvbnN0IE5HWF9QV0FfT0ZGTElORV9TRVJWSUNFID0gbmV3IEluamVjdGlvblRva2VuKFxuICAgICdQcm92aWRlciBmb3IgdGhlIE9mZmxpbmVTZXJ2aWNlIHVzZWQgZWcuIGluIHRoZSBvZmZsaW5lIHJlcXVlc3QgaW50ZXJjZXB0b3IuJyxcbiAgICB7XG4gICAgICAgIHByb3ZpZGVkSW46ICdyb290JyxcbiAgICAgICAgZmFjdG9yeTogKCkgPT4ge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tY29uc29sZVxuICAgICAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgc3R5bGlzdGljL21heC1sZW5cbiAgICAgICAgICAgICAgICAnTm8gT2ZmbGluZVNlcnZpY2UgaGFzIGJlZW4gcHJvdmlkZWQgZm9yIHRoZSB0b2tlbiBOR1hfT0ZGTElORV9TRVJWSUNFXFxuQWRkIHRoaXMgdG8geW91ciBhcHAubW9kdWxlLnRzIHByb3ZpZGVyIGFycmF5OlxcbntcXG4gICAgcHJvdmlkZTogTkdYX1BXQV9PRkZMSU5FX1NFUlZJQ0UsXFxuICAgIHVzZUV4aXN0aW5nOiBNeU9mZmxpbmVTZXJ2aWNlXFxufSdcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICB9XG4pO1xuXG4vKipcbiAqIFRoZSB0eXBlIG9mIGEgY2FjaGVkIG9mZmxpbmUgcmVxdWVzdC5cbiAqIENvbnRhaW5zIHRoZSBodHRwIHJlcXVlc3QgYXMgd2VsbCBhcyBzb21lIG1ldGFkYXRhLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIENhY2hlZFJlcXVlc3Q8VD4ge1xuICAgIC8qKlxuICAgICAqIFRoZSBhY3R1YWwgaHR0cCByZXF1ZXN0LlxuICAgICAqL1xuICAgIHJlcXVlc3Q6IEh0dHBSZXF1ZXN0PFQ+LFxuICAgIC8qKlxuICAgICAqIFRoZSBtZXRhZGF0YSBmb3IgdGhhdCByZXF1ZXN0LlxuICAgICAqL1xuICAgIG1ldGFkYXRhOiBSZXF1ZXN0TWV0YWRhdGFJbnRlcm5hbFxufVxuXG4vKipcbiAqIFRoZSBiYXNlIGNsYXNzIGZvciBhbiBvZmZsaW5lIHNlcnZpY2UuXG4gKi9cbmV4cG9ydCBjbGFzcyBOZ3hQd2FPZmZsaW5lU2VydmljZSB7XG4gICAgLyoqXG4gICAgICogVGhlIGtleSB1bmRlciB3aGljaCBhbnkgcmVxdWVzdHMgYXJlIHNhdmVkIGluIGxvY2FsIHN0b3JhZ2UuXG4gICAgICovXG4gICAgcmVhZG9ubHkgQ0FDSEVEX1JFUVVFU1RTX0tFWTogc3RyaW5nID0gJ3JlcXVlc3RzJztcblxuICAgIC8qKlxuICAgICAqIFRoZSBwcmVmaXggb2Ygb2ZmbGluZSBnZW5lcmF0ZWQgaWRzLlxuICAgICAqIElzIHVzZWQgdG8gY2hlY2sgaWYgYSByZXF1ZXN0IHN0aWxsIGhhcyB1bnJlc29sdmVkIGRlcGVuZGVuY2llcy5cbiAgICAgKi9cbiAgICByZWFkb25seSBPRkZMSU5FX0lEX1BSRUZJWDogc3RyaW5nID0gJ29mZmxpbmUnO1xuXG4gICAgLyoqXG4gICAgICogQSBzbmFja2JhciBtZXNzYWdlIHRvIGRpc3BsYXkgd2hlbiB0aGUgc3luY2hyb25pemF0aW9uIG9mIGFsbCBjYWNoZWQgcmVxdWVzdHMgaGFzIGJlZW4gZmluaXNoZWQuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IEFMTF9TWU5DX0ZJTklTSEVEX1NOQUNLX0JBUl9NRVNTQUdFOiBzdHJpbmcgPSAnU3luY2hyb25pemF0aW9uIGZpbmlzaGVkJztcblxuICAgIC8qKlxuICAgICAqIEEgc25hY2tiYXIgbWVzc2FnZSB0byBkaXNwbGF5IHdoZW4gdGhlIHN5bmNocm9uaXphdGlvbiBvZiBhbGwgY2FjaGVkIHJlcXVlc3RzIGZhaWxzLlxuICAgICAqL1xuICAgIHByb3RlY3RlZCByZWFkb25seSBBTExfU1lOQ19GQUlMRURfU05BQ0tfQkFSX01FU1NBR0U6IHN0cmluZyA9ICdTeW5jaHJvbml6YXRpb24gZmFpbGVkLCBwbGVhc2UgdHJ5IGFnYWluIGxhdGVyJztcblxuICAgIC8qKlxuICAgICAqIEEgc25hY2tiYXIgbWVzc2FnZSB0byBkaXNwbGF5IHdoZW4gdGhlIHN5bmNocm9uaXphdGlvbiBvZiBhIHNpbmdsZSBjYWNoZWQgcmVxdWVzdHMgaGFzIGJlZW4gZmluaXNoZWQuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IFNJTkdMRV9TWU5DX0ZJTklTSEVEX1NOQUNLX0JBUl9NRVNTQUdFOiBzdHJpbmcgPSAnU3luY2hyb25pemF0aW9uIGZpbmlzaGVkJztcblxuICAgIC8qKlxuICAgICAqIEEgc25hY2tiYXIgbWVzc2FnZSB0byBkaXNwbGF5IHdoZW4gdGhlIHN5bmNocm9uaXphdGlvbiBvZiBhIHNpbmdsZSBjYWNoZWQgcmVxdWVzdHMgZmFpbHMuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IFNJTkdMRV9TWU5DX0ZBSUxFRF9TTkFDS19CQVJfTUVTU0FHRTogc3RyaW5nID0gJ1N5bmNocm9uaXphdGlvbiBmYWlsZWQsIHBsZWFzZSB0cnkgYWdhaW4gbGF0ZXInO1xuXG4gICAgLyoqXG4gICAgICogV2hldGhlciBvciBub3QgdGhlIHVzZXIgaGFzIG5vIGludGVybmV0IGNvbm5lY3Rpb24uXG4gICAgICovXG4gICAgaXNPZmZsaW5lOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKipcbiAgICAgKiBBIHN1YmplY3Qgb2YgYWxsIHRoZSByZXF1ZXN0cyB0aGF0IGhhdmUgYmVlbiBkb25lIHdoaWxlIG9mZmxpbmUuXG4gICAgICogTmVlZHMgdG8gYmUgdXNlZCBmb3IgYXBwbHlpbmcgb2ZmbGluZSBkYXRhIG9yIHN5bmNpbmcgdGhlIHJlcXVlc3RzIHRvIHRoZSBhcGkuXG4gICAgICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBjYWNoZWRSZXF1ZXN0c1N1YmplY3Q6IEJlaGF2aW9yU3ViamVjdDxDYWNoZWRSZXF1ZXN0PHVua25vd24+W10+O1xuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGpzZG9jL3JlcXVpcmUtcmV0dXJuc1xuICAgIC8qKlxuICAgICAqIFRoZSBjdXJyZW50bHkgc3RvcmVkIGNhY2hlZCByZXF1ZXN0cyAoaWYgdGhlcmUgYXJlIGFueSkuXG4gICAgICovXG4gICAgZ2V0IGNhY2hlZFJlcXVlc3RzKCk6IENhY2hlZFJlcXVlc3Q8dW5rbm93bj5bXSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNhY2hlZFJlcXVlc3RzU3ViamVjdC52YWx1ZTtcbiAgICB9XG5cbiAgICBzZXQgY2FjaGVkUmVxdWVzdHMoY2FjaGVkUmVxdWVzdHM6IENhY2hlZFJlcXVlc3Q8dW5rbm93bj5bXSkge1xuICAgICAgICBpZiAoIWlzUGxhdGZvcm1Ccm93c2VyKHRoaXMucGxhdGZvcm1JZCkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbSh0aGlzLkNBQ0hFRF9SRVFVRVNUU19LRVksIEpTT04uc3RyaW5naWZ5KGNhY2hlZFJlcXVlc3RzKSk7XG4gICAgICAgIHRoaXMuY2FjaGVkUmVxdWVzdHNTdWJqZWN0Lm5leHQoY2FjaGVkUmVxdWVzdHMpO1xuICAgIH1cblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IGh0dHA6IEh0dHBDbGllbnQsXG4gICAgICAgIHByaXZhdGUgcmVhZG9ubHkgc25hY2tCYXI6IE1hdFNuYWNrQmFyLFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IHpvbmU6IE5nWm9uZSxcbiAgICAgICAgQEluamVjdChQTEFURk9STV9JRClcbiAgICAgICAgcHJpdmF0ZSByZWFkb25seSBwbGF0Zm9ybUlkOiBPYmplY3RcbiAgICApIHtcbiAgICAgICAgaWYgKCFpc1BsYXRmb3JtQnJvd3NlcihwbGF0Zm9ybUlkKSkge1xuICAgICAgICAgICAgdGhpcy5pc09mZmxpbmUgPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMuY2FjaGVkUmVxdWVzdHNTdWJqZWN0ID0gbmV3IEJlaGF2aW9yU3ViamVjdDxDYWNoZWRSZXF1ZXN0PHVua25vd24+W10+KFtdKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmlzT2ZmbGluZSA9ICFuYXZpZ2F0b3Iub25MaW5lO1xuICAgICAgICB3aW5kb3cub25vbmxpbmUgPSAoKSA9PiB0aGlzLmlzT2ZmbGluZSA9ICFuYXZpZ2F0b3Iub25MaW5lO1xuICAgICAgICB3aW5kb3cub25vZmZsaW5lID0gKCkgPT4gdGhpcy5pc09mZmxpbmUgPSAhbmF2aWdhdG9yLm9uTGluZTtcblxuICAgICAgICBjb25zdCBzdHJpbmdEYXRhOiBzdHJpbmcgfCBudWxsID0gbG9jYWxTdG9yYWdlLmdldEl0ZW0odGhpcy5DQUNIRURfUkVRVUVTVFNfS0VZKTtcbiAgICAgICAgY29uc3QgcmVxdWVzdHNEYXRhOiBDYWNoZWRSZXF1ZXN0PHVua25vd24+W10gPSBzdHJpbmdEYXRhID8gSlNPTi5wYXJzZShzdHJpbmdEYXRhKSBhcyBDYWNoZWRSZXF1ZXN0PHVua25vd24+W10gOiBbXTtcbiAgICAgICAgdGhpcy5jYWNoZWRSZXF1ZXN0c1N1YmplY3QgPSBuZXcgQmVoYXZpb3JTdWJqZWN0KHJlcXVlc3RzRGF0YSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQXBwbGllcyBhbnkgb2ZmbGluZSBkYXRhIHRoYXQgaGFzIGJlZW4gY2FjaGVkIHRvIHRoZSBnaXZlbiB2YWx1ZXMuXG4gICAgICogQHBhcmFtIHR5cGUgLSBUaGUgdHlwZSBvZiB0aGUgcHJvdmlkZWQgZW50aXRpZXMuIElzIG5lZWRlZCB0byBjaGVjayBpZiBhbnkgY2FjaGVkIHJlcXVlc3RzIG9mIHRoZSBzYW1lIHR5cGUgZXhpc3QuXG4gICAgICogQHBhcmFtIGVudGl0aWVzIC0gVGhlIGFscmVhZHkgZXhpc3RpbmcgZGF0YS5cbiAgICAgKiBAcmV0dXJucyBUaGUgYWxyZWFkeSBleGlzdGluZyBlbnRpdGllcyBleHRlbmRlZC9tb2RpZmllZCBieSB0aGUgb2ZmbGluZSBjYWNoZWQgcmVxdWVzdHMuXG4gICAgICovXG4gICAgYXBwbHlPZmZsaW5lRGF0YTxFbnRpdHlUeXBlIGV4dGVuZHMgQmFzZUVudGl0eVR5cGU8RW50aXR5VHlwZT4+KFxuICAgICAgICB0eXBlOiBzdHJpbmcsXG4gICAgICAgIGVudGl0aWVzOiBFbnRpdHlUeXBlW11cbiAgICApOiBFbnRpdHlUeXBlW10ge1xuICAgICAgICBpZiAoIXRoaXMuY2FjaGVkUmVxdWVzdHMubGVuZ3RoKSB7XG4gICAgICAgICAgICByZXR1cm4gZW50aXRpZXM7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcmVzOiBFbnRpdHlUeXBlW10gPSBBcnJheS5mcm9tKGVudGl0aWVzKTtcbiAgICAgICAgY29uc3QgY2FjaGVkUmVxdWVzdHM6IENhY2hlZFJlcXVlc3Q8dW5rbm93bj5bXSA9IHRoaXMuY2FjaGVkUmVxdWVzdHMuZmlsdGVyKHJlcSA9PiByZXEubWV0YWRhdGEudHlwZSA9PT0gdHlwZSk7XG4gICAgICAgIGZvciAoY29uc3QgcmVxIG9mIGNhY2hlZFJlcXVlc3RzKSB7XG4gICAgICAgICAgICBzd2l0Y2ggKHJlcS5yZXF1ZXN0Lm1ldGhvZCkge1xuICAgICAgICAgICAgICAgIGNhc2UgSHR0cE1ldGhvZC5QT1NUOiB7XG4gICAgICAgICAgICAgICAgICAgIHJlcy5wdXNoKHJlcS5yZXF1ZXN0LmJvZHkgYXMgRW50aXR5VHlwZSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjYXNlIEh0dHBNZXRob2QuUEFUQ0g6IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcGF0Y2hJZEtleToga2V5b2YgRW50aXR5VHlwZSA9IHJlcS5tZXRhZGF0YS5pZEtleTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgaW5kZXg6IG51bWJlciA9IHJlcy5maW5kSW5kZXgoZSA9PiByZXEucmVxdWVzdC51cmxXaXRoUGFyYW1zLmluY2x1ZGVzKGAke2VbcGF0Y2hJZEtleV19YCkpO1xuICAgICAgICAgICAgICAgICAgICByZXNbaW5kZXhdID0gdGhpcy51cGRhdGVPZmZsaW5lKHJlcS5yZXF1ZXN0LmJvZHkgYXMgRW50aXR5VHlwZSwgcmVzW2luZGV4XSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjYXNlIEh0dHBNZXRob2QuREVMRVRFOiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGRlbGV0ZUlkS2V5OiBrZXlvZiBFbnRpdHlUeXBlID0gcmVxLm1ldGFkYXRhLmlkS2V5O1xuICAgICAgICAgICAgICAgICAgICByZXMuc3BsaWNlKHJlcy5maW5kSW5kZXgoZSA9PiByZXEucmVxdWVzdC51cmxXaXRoUGFyYW1zLmluY2x1ZGVzKGAke2VbZGVsZXRlSWRLZXldfWApKSwgMSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBkZWZhdWx0OiB7XG4gICAgICAgICAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ1RoZXJlIHdhcyBhbiB1bmtub3duIGh0dHAtbWV0aG9kIGluIG9uZSBvZiB5b3VyIGNhY2hlZCBvZmZsaW5lIHJlcXVlc3RzOicsIHJlcS5yZXF1ZXN0Lm1ldGhvZCk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFwcGxpZXMgYW4gVVBEQVRFIHRvIGFuIGVudGl0eSB3aXRob3V0IHNlbmRpbmcgYSByZXF1ZXN0IHRvIHRoZSBzZXJ2ZXIuXG4gICAgICogQHBhcmFtIGNoYW5nZXMgLSBUaGUgY2hhbmdlcyB0aGF0IHNob3VsZCBiZSBtYWRlIHRvIHRoZSBlbnRpdHkuXG4gICAgICogQHBhcmFtIGVudGl0eSAtIFRoZSBlbnRpdHkgdGhhdCBzaG91bGQgYmUgdXBkYXRlZC5cbiAgICAgKiBAcmV0dXJucyBUaGUgdXBkYXRlZCBlbnRpdHkuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHVwZGF0ZU9mZmxpbmU8RW50aXR5VHlwZSBleHRlbmRzIEJhc2VFbnRpdHlUeXBlPEVudGl0eVR5cGU+PihcbiAgICAgICAgY2hhbmdlczogUGFydGlhbDxFbnRpdHlUeXBlPixcbiAgICAgICAgZW50aXR5OiBFbnRpdHlUeXBlXG4gICAgKTogRW50aXR5VHlwZSB7XG4gICAgICAgIGZvciAoY29uc3Qga2V5IGluIGNoYW5nZXMpIHtcbiAgICAgICAgICAgIGVudGl0eVtrZXldID0gY2hhbmdlc1trZXldIGFzIEVudGl0eVR5cGVbRXh0cmFjdDxrZXlvZiBFbnRpdHlUeXBlLCBzdHJpbmc+XTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZW50aXR5O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNlbmRzIGEgc3BlY2lmaWMgY2FjaGVkIHJlcXVlc3QgdG8gdGhlIHNlcnZlci5cbiAgICAgKiBAcGFyYW0gcmVxdWVzdCAtIFRoZSByZXF1ZXN0IHRoYXQgc2hvdWxkIGJlIHN5bmNlZC5cbiAgICAgKi9cbiAgICBhc3luYyBzeW5jPFQ+KHJlcXVlc3Q6IENhY2hlZFJlcXVlc3Q8VD4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgY2FjaGVkUmVxdWVzdHNQcmlvckNoYW5nZXM6IENhY2hlZFJlcXVlc3Q8dW5rbm93bj5bXSA9IExvZGFzaFV0aWxpdGllcy5jbG9uZURlZXAodGhpcy5jYWNoZWRSZXF1ZXN0cyk7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXM6IEF3YWl0ZWQ8VD4gPSBhd2FpdCB0aGlzLnN5bmNTaW5nbGVSZXF1ZXN0KHJlcXVlc3QpO1xuICAgICAgICAgICAgdGhpcy56b25lLnJ1bigoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5zbmFja0Jhci5vcGVuKHRoaXMuU0lOR0xFX1NZTkNfRklOSVNIRURfU05BQ0tfQkFSX01FU1NBR0UsIHVuZGVmaW5lZCwgeyBkdXJhdGlvbjogMjUwMCB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5yZW1vdmVTaW5nbGVSZXF1ZXN0KHJlcXVlc3QpO1xuICAgICAgICAgICAgdGhpcy51cGRhdGVPZmZsaW5lSWRzSW5SZXF1ZXN0cyhyZXF1ZXN0LCByZXMpO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIHtcbiAgICAgICAgICAgIHRoaXMuem9uZS5ydW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc25hY2tCYXIub3Blbih0aGlzLlNJTkdMRV9TWU5DX0ZBSUxFRF9TTkFDS19CQVJfTUVTU0FHRSwgdW5kZWZpbmVkLCB7IGR1cmF0aW9uOiAyNTAwIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLmNhY2hlZFJlcXVlc3RzID0gY2FjaGVkUmVxdWVzdHNQcmlvckNoYW5nZXM7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZW5kcyBhbGwgY2FjaGVkIHJlcXVlc3RzIHRvIHRoZSBzZXJ2ZXIuIFRyaWVzIHRvIGhhbmRsZSBkZXBlbmRlbmNpZXMgb2YgcmVxdWVzdHMgb24gZWFjaCBvdGhlci5cbiAgICAgKi9cbiAgICBhc3luYyBzeW5jQWxsKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBjYWNoZWRSZXF1ZXN0c1ByaW9yQ2hhbmdlczogQ2FjaGVkUmVxdWVzdDx1bmtub3duPltdID0gTG9kYXNoVXRpbGl0aWVzLmNsb25lRGVlcCh0aGlzLmNhY2hlZFJlcXVlc3RzKTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc3luY0FsbFJlY3Vyc2l2ZSgpO1xuICAgICAgICAgICAgdGhpcy56b25lLnJ1bigoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5zbmFja0Jhci5vcGVuKHRoaXMuQUxMX1NZTkNfRklOSVNIRURfU05BQ0tfQkFSX01FU1NBR0UsIHVuZGVmaW5lZCwgeyBkdXJhdGlvbjogMjUwMCB9KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5jYWNoZWRSZXF1ZXN0cyA9IFtdO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIHtcbiAgICAgICAgICAgIHRoaXMuem9uZS5ydW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc25hY2tCYXIub3Blbih0aGlzLkFMTF9TWU5DX0ZBSUxFRF9TTkFDS19CQVJfTUVTU0FHRSwgdW5kZWZpbmVkLCB7IGR1cmF0aW9uOiAyNTAwIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLmNhY2hlZFJlcXVlc3RzID0gY2FjaGVkUmVxdWVzdHNQcmlvckNoYW5nZXM7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBUaGUgcmVjdXJzaXZlIG1ldGhvZCB1c2VkIHRvIHN5biBhbGwgcmVxdWVzdHMgdG8gdGhlIGFwaS5cbiAgICAgKi9cbiAgICBwcm90ZWN0ZWQgYXN5bmMgc3luY0FsbFJlY3Vyc2l2ZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHN0eWxpc3RpYy9tYXgtbGVuXG4gICAgICAgIGNvbnN0IHJlcXVlc3Q6IENhY2hlZFJlcXVlc3Q8QmFzZUVudGl0eVR5cGU8dW5rbm93bj4+IHwgdW5kZWZpbmVkID0gdGhpcy5jYWNoZWRSZXF1ZXN0cy5maW5kKHIgPT4gIXRoaXMuaGFzVW5yZXNvbHZlZERlcGVuZGVuY3kocikpIGFzIENhY2hlZFJlcXVlc3Q8QmFzZUVudGl0eVR5cGU8dW5rbm93bj4+IHwgdW5kZWZpbmVkO1xuICAgICAgICBpZiAoIXJlcXVlc3QpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCByZXM6IEJhc2VFbnRpdHlUeXBlPHVua25vd24+ID0gYXdhaXQgdGhpcy5zeW5jU2luZ2xlUmVxdWVzdChyZXF1ZXN0KTtcbiAgICAgICAgdGhpcy51cGRhdGVPZmZsaW5lSWRzSW5SZXF1ZXN0cyhyZXF1ZXN0LCByZXMpO1xuICAgICAgICBhd2FpdCB0aGlzLnN5bmNBbGxSZWN1cnNpdmUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZW5kcyBhIHNpbmdsZSBjYWNoZWQgcmVxdWVzdCB0byB0aGUgc2VydmVyLlxuICAgICAqIEBwYXJhbSByZXF1ZXN0IC0gVGhlIHJlcXVlc3QgdGhhdCBzaG91bGQgYmUgc3luY2VkLlxuICAgICAqIEByZXR1cm5zIEEgcHJvbWlzZSBvZiB0aGUgcmVxdWVzdCByZXN1bHQuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIGFzeW5jIHN5bmNTaW5nbGVSZXF1ZXN0PFQ+KFxuICAgICAgICByZXF1ZXN0OiBDYWNoZWRSZXF1ZXN0PFQ+XG4gICAgKTogUHJvbWlzZTxUPiB7XG4gICAgICAgIGlmICh0aGlzLmlzT2ZmbGluZSB8fCB0aGlzLmhhc1VucmVzb2x2ZWREZXBlbmRlbmN5KHJlcXVlc3QpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCBzeW5jIHRoZSByZXF1ZXN0Jyk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcmVxdWVzdE9ic2VydmFibGU6IE9ic2VydmFibGU8VD4gfCB1bmRlZmluZWQgPSB0aGlzLnJlcXVlc3QocmVxdWVzdCk7XG4gICAgICAgIGlmICghcmVxdWVzdE9ic2VydmFibGUpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IHN5bmMgdGhlIHJlcXVlc3QnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYXdhaXQgZmlyc3RWYWx1ZUZyb20ocmVxdWVzdE9ic2VydmFibGUpO1xuICAgIH1cblxuICAgIHByaXZhdGUgdXBkYXRlT2ZmbGluZUlkc0luUmVxdWVzdHM8VD4ocmVxdWVzdDogQ2FjaGVkUmVxdWVzdDxUPiwgcmVzOiBUKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLmNhY2hlZFJlcXVlc3RzLmxlbmd0aCAmJiByZXF1ZXN0LnJlcXVlc3QuYm9keSAhPSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGNvbnN0IGlkS2V5OiBrZXlvZiBCYXNlRW50aXR5VHlwZTx1bmtub3duPiA9IHJlcXVlc3QubWV0YWRhdGEuaWRLZXk7XG4gICAgICAgICAgICBpZiAocmVzW2lkS2V5XSAhPSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBjb25zdCByZXF1ZXN0c1N0cmluZzogc3RyaW5nID0gYCR7dGhpcy5jYWNoZWRSZXF1ZXN0c31gLnNwbGl0KHJlcXVlc3QucmVxdWVzdC5ib2R5W2lkS2V5XSkuam9pbihyZXNbaWRLZXldKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNhY2hlZFJlcXVlc3RzID0gSlNPTi5wYXJzZShyZXF1ZXN0c1N0cmluZykgYXMgQ2FjaGVkUmVxdWVzdDxUPltdO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2FsbHMgaHR0cC5wb3N0L3BhdGNoL2RlbGV0ZSBldGMuIE9uIHRoZSBwcm92aWRlZCByZXF1ZXN0LlxuICAgICAqIEBwYXJhbSByZXF1ZXN0IC0gVGhlIHJlcXVlc3QgdGhhdCBzaG91bGQgYmUgc2VudC5cbiAgICAgKiBAcmV0dXJucyBUaGUgb2JzZXJ2YWJsZSBvZiB0aGUgcmVxdWVzdCBvciB1bmRlZmluZWQgaWYgc29tZXRoaW5nIHdlbnQgd3JvbmcuXG4gICAgICovXG4gICAgcHJvdGVjdGVkIHJlcXVlc3Q8RW50aXR5VHlwZSBleHRlbmRzIEJhc2VFbnRpdHlUeXBlPEVudGl0eVR5cGU+PihcbiAgICAgICAgcmVxdWVzdDogQ2FjaGVkUmVxdWVzdDxFbnRpdHlUeXBlPlxuICAgICk6IE9ic2VydmFibGU8RW50aXR5VHlwZT4gfCB1bmRlZmluZWQge1xuICAgICAgICBzd2l0Y2ggKHJlcXVlc3QucmVxdWVzdC5tZXRob2QpIHtcbiAgICAgICAgICAgIGNhc2UgSHR0cE1ldGhvZC5QT1NUOiB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuaHR0cC5wb3N0PEVudGl0eVR5cGU+KFxuICAgICAgICAgICAgICAgICAgICByZXF1ZXN0LnJlcXVlc3QudXJsV2l0aFBhcmFtcyxcbiAgICAgICAgICAgICAgICAgICAgTG9kYXNoVXRpbGl0aWVzLm9taXQocmVxdWVzdC5yZXF1ZXN0LmJvZHksIHJlcXVlc3QubWV0YWRhdGEuaWRLZXkpXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhc2UgSHR0cE1ldGhvZC5QQVRDSDoge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmh0dHAucGF0Y2g8RW50aXR5VHlwZT4ocmVxdWVzdC5yZXF1ZXN0LnVybFdpdGhQYXJhbXMsIHJlcXVlc3QucmVxdWVzdC5ib2R5KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhc2UgSHR0cE1ldGhvZC5ERUxFVEU6IHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5odHRwLmRlbGV0ZTxFbnRpdHlUeXBlPihyZXF1ZXN0LnJlcXVlc3QudXJsV2l0aFBhcmFtcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBkZWZhdWx0OiB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gcmVxdWVzdCBoYXMgYW4gdW5yZXNvbHZlZCBkZXBlbmRlbmN5IGJ5IGxvb2tpbmcgZm9yIHRoZSBrZXl3b3JkICdvZmZsaW5lJyBpbnNpZGUgb2YgaXQuXG4gICAgICogQHBhcmFtIHJlcXVlc3QgLSBUaGUgcmVxdWVzdCB0aGF0IHNob3VsZCBiZSBjaGVja2VkLlxuICAgICAqIEByZXR1cm5zIFdoZXRoZXIgb3Igbm8gdGhlIGdpdmVuIHJlcXVlc3QgaGFzIGFuIHVucmVzb2x2ZWQgZGVwZW5kZW5jeS5cbiAgICAgKi9cbiAgICBoYXNVbnJlc29sdmVkRGVwZW5kZW5jeShyZXF1ZXN0OiBDYWNoZWRSZXF1ZXN0PHVua25vd24+KTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiByZXF1ZXN0LnJlcXVlc3QudXJsV2l0aFBhcmFtcy5pbmNsdWRlcyh0aGlzLk9GRkxJTkVfSURfUFJFRklYKVxuICAgICAgICAgICAgfHwgYCR7cmVxdWVzdC5yZXF1ZXN0LmJvZHl9YC5pbmNsdWRlcyh0aGlzLk9GRkxJTkVfSURfUFJFRklYKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZW1vdmVzIGEgc2luZ2xlIHJlcXVlc3QgZnJvbSB0aGUgY2FjaGUuXG4gICAgICogQHBhcmFtIHJlcXVlc3QgLSBUaGUgcmVxdWVzdCB0aGF0IHNob3VsZCBiZSByZW1vdmVkLlxuICAgICAqL1xuICAgIHJlbW92ZVNpbmdsZVJlcXVlc3QocmVxdWVzdDogQ2FjaGVkUmVxdWVzdDx1bmtub3duPik6IHZvaWQge1xuICAgICAgICB0aGlzLmNhY2hlZFJlcXVlc3RzLnNwbGljZSh0aGlzLmNhY2hlZFJlcXVlc3RzLmluZGV4T2YocmVxdWVzdCksIDEpO1xuICAgICAgICB0aGlzLmNhY2hlZFJlcXVlc3RzID0gdGhpcy5jYWNoZWRSZXF1ZXN0cztcbiAgICB9XG59Il19