UNPKG

@angular/fire

Version:

The official library for Firebase and Angular

364 lines 38.9 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ import { ComponentFactoryResolver, Inject, Injectable, Injector, NgModuleFactory, NgZone, Optional, PLATFORM_ID } from '@angular/core'; import { from, Observable, of } from 'rxjs'; import { filter, groupBy, map, mergeMap, observeOn, pairwise, startWith, switchMap, tap, withLatestFrom } from 'rxjs/operators'; import { ActivationEnd, NavigationEnd, Router, ROUTES } from '@angular/router'; import { ɵAngularFireSchedulers } from '@angular/fire'; import { AngularFireAnalytics, DEBUG_MODE } from './analytics'; import { Title } from '@angular/platform-browser'; import { isPlatformBrowser, isPlatformServer } from '@angular/common'; import * as i0 from "@angular/core"; import * as i1 from "./analytics"; import * as i2 from "@angular/router"; import * as i3 from "@angular/platform-browser"; /** @type {?} */ const FIREBASE_EVENT_ORIGIN_KEY = 'firebase_event_origin'; /** @type {?} */ const FIREBASE_PREVIOUS_SCREEN_CLASS_KEY = 'firebase_previous_class'; /** @type {?} */ const FIREBASE_PREVIOUS_SCREEN_INSTANCE_ID_KEY = 'firebase_previous_id'; /** @type {?} */ const FIREBASE_PREVIOUS_SCREEN_NAME_KEY = 'firebase_previous_screen'; /** @type {?} */ const FIREBASE_SCREEN_CLASS_KEY = 'firebase_screen_class'; /** @type {?} */ const FIREBASE_SCREEN_INSTANCE_ID_KEY = 'firebase_screen_id'; /** @type {?} */ const FIREBASE_SCREEN_NAME_KEY = 'firebase_screen'; /** @type {?} */ const OUTLET_KEY = 'outlet'; /** @type {?} */ const PAGE_PATH_KEY = 'page_path'; /** @type {?} */ const PAGE_TITLE_KEY = 'page_title'; /** @type {?} */ const SCREEN_CLASS_KEY = 'screen_class'; /** @type {?} */ const SCREEN_NAME_KEY = 'screen_name'; /** @type {?} */ const SCREEN_VIEW_EVENT = 'screen_view'; /** @type {?} */ const EVENT_ORIGIN_AUTO = 'auto'; /** @type {?} */ const DEFAULT_SCREEN_CLASS = '???'; /** @type {?} */ const NG_PRIMARY_OUTLET = 'primary'; /** @type {?} */ const SCREEN_INSTANCE_DELIMITER = '#'; /** @type {?} */ const ANNOTATIONS = '__annotations__'; // this is an INT64 in iOS/Android but use INT32 cause javascript /** @type {?} */ let nextScreenInstanceID = Math.floor(Math.random() * (Math.pow(2, 32) - 1)) - Math.pow(2, 31); /** @type {?} */ const knownScreenInstanceIDs = {}; /** @type {?} */ const getScreenInstanceID = (/** * @param {?} params * @return {?} */ (params) => { // unique the screen class against the outlet name /** @type {?} */ const screenInstanceKey = [ params[SCREEN_CLASS_KEY], params[OUTLET_KEY] ].join(SCREEN_INSTANCE_DELIMITER); if (knownScreenInstanceIDs.hasOwnProperty(screenInstanceKey)) { return knownScreenInstanceIDs[screenInstanceKey]; } else { /** @type {?} */ const ret = nextScreenInstanceID++; knownScreenInstanceIDs[screenInstanceKey] = ret; return ret; } }); const ɵ0 = getScreenInstanceID; export class ScreenTrackingService { /** * @param {?} analytics * @param {?} router * @param {?} title * @param {?} componentFactoryResolver * @param {?} platformId * @param {?} debugModeEnabled * @param {?} zone * @param {?} injector */ constructor(analytics, router, title, componentFactoryResolver, // tslint:disable-next-line:ban-types platformId, debugModeEnabled, zone, injector) { if (!router || !isPlatformBrowser(platformId)) { return this; } zone.runOutsideAngular((/** * @return {?} */ () => { /** @type {?} */ const activationEndEvents = router.events.pipe(filter((/** * @param {?} e * @return {?} */ e => e instanceof ActivationEnd))); /** @type {?} */ const navigationEndEvents = router.events.pipe(filter((/** * @param {?} e * @return {?} */ e => e instanceof NavigationEnd))); this.disposable = navigationEndEvents.pipe(withLatestFrom(activationEndEvents), switchMap((/** * @param {?} __0 * @return {?} */ ([navigationEnd, activationEnd]) => { // SEMVER: start using optional chains and nullish coalescing once we support newer typescript /** @type {?} */ const pagePath = navigationEnd.url; /** @type {?} */ const screenName = activationEnd.snapshot.routeConfig && activationEnd.snapshot.routeConfig.path || pagePath; /** @type {?} */ const params = { [SCREEN_NAME_KEY]: screenName, [PAGE_PATH_KEY]: pagePath, [FIREBASE_EVENT_ORIGIN_KEY]: EVENT_ORIGIN_AUTO, [FIREBASE_SCREEN_NAME_KEY]: screenName, [OUTLET_KEY]: activationEnd.snapshot.outlet }; if (title) { params[PAGE_TITLE_KEY] = title.getTitle(); } /** @type {?} */ const component = activationEnd.snapshot.component; /** @type {?} */ const routeConfig = activationEnd.snapshot.routeConfig; /** @type {?} */ const loadChildren = routeConfig && routeConfig.loadChildren; // TODO figure out how to handle minification if (typeof loadChildren === 'string') { // SEMVER: this is the older lazy load style "./path#ClassName", drop this when we drop old ng // TODO is it worth seeing if I can look up the component factory selector from the module name? // it's lazy so it's not registered with componentFactoryResolver yet... seems a pain for a depreciated style return of(Object.assign(Object.assign({}, params), { [SCREEN_CLASS_KEY]: loadChildren.split('#')[1] })); } else if (typeof component === 'string') { return of(Object.assign(Object.assign({}, params), { [SCREEN_CLASS_KEY]: component })); } else if (component) { /** @type {?} */ const componentFactory = componentFactoryResolver.resolveComponentFactory(component); return of(Object.assign(Object.assign({}, params), { [SCREEN_CLASS_KEY]: componentFactory.selector })); } else if (loadChildren) { /** @type {?} */ const loadedChildren = loadChildren(); /** @type {?} */ const loadedChildren$ = (loadedChildren instanceof Observable) ? loadedChildren : from(Promise.resolve(loadedChildren)); return loadedChildren$.pipe(map((/** * @param {?} lazyModule * @return {?} */ lazyModule => { if (lazyModule instanceof NgModuleFactory) { // AOT create an injector /** @type {?} */ const moduleRef = lazyModule.create(injector); // INVESTIGATE is this the right way to get at the matching route? /** @type {?} */ const routes = moduleRef.injector.get(ROUTES); /** @type {?} */ const component = routes[0][0].component; try { /** @type {?} */ const componentFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(component); return Object.assign(Object.assign({}, params), { [SCREEN_CLASS_KEY]: componentFactory.selector }); } catch (_) { return Object.assign(Object.assign({}, params), { [SCREEN_CLASS_KEY]: DEFAULT_SCREEN_CLASS }); } } else { // JIT look at the annotations // INVESTIGATE are there public APIs for this stuff? /** @type {?} */ const declarations = [].concat.apply([], (lazyModule[ANNOTATIONS] || []).map((/** * @param {?} f * @return {?} */ (f) => f.declarations))); /** @type {?} */ const selectors = [].concat.apply([], declarations.map((/** * @param {?} c * @return {?} */ (c) => (c[ANNOTATIONS] || []).map((/** * @param {?} f * @return {?} */ (f) => f.selector))))); // should I just be grabbing the selector like this or should i match against the route component? // const routerModule = lazyModule.ngInjectorDef.imports.find(i => i.ngModule && ....); // const route = routerModule.providers[0].find(p => p.provide == ROUTES).useValue[0]; return Object.assign(Object.assign({}, params), { [SCREEN_CLASS_KEY]: selectors[0] || DEFAULT_SCREEN_CLASS }); } }))); } else { return of(Object.assign(Object.assign({}, params), { [SCREEN_CLASS_KEY]: DEFAULT_SCREEN_CLASS })); } })), map((/** * @param {?} params * @return {?} */ params => (Object.assign({ [FIREBASE_SCREEN_CLASS_KEY]: params[SCREEN_CLASS_KEY], [FIREBASE_SCREEN_INSTANCE_ID_KEY]: getScreenInstanceID(params) }, params)))), tap((/** * @param {?} params * @return {?} */ params => { // TODO perhaps I can be smarter about this, bubble events up to the nearest outlet? if (params[OUTLET_KEY] === NG_PRIMARY_OUTLET) { analytics.setCurrentScreen(params[SCREEN_NAME_KEY]); analytics.updateConfig({ [PAGE_PATH_KEY]: params[PAGE_PATH_KEY], [SCREEN_CLASS_KEY]: params[SCREEN_CLASS_KEY] }); if (title) { analytics.updateConfig({ [PAGE_TITLE_KEY]: params[PAGE_TITLE_KEY] }); } } })), groupBy((/** * @param {?} params * @return {?} */ params => params[OUTLET_KEY])), // tslint:disable-next-line mergeMap((/** * @param {?} group * @return {?} */ group => group.pipe(startWith(undefined), pairwise()))), map((/** * @param {?} __0 * @return {?} */ ([prior, current]) => prior ? Object.assign({ [FIREBASE_PREVIOUS_SCREEN_CLASS_KEY]: prior[SCREEN_CLASS_KEY], [FIREBASE_PREVIOUS_SCREEN_NAME_KEY]: prior[SCREEN_NAME_KEY], [FIREBASE_PREVIOUS_SCREEN_INSTANCE_ID_KEY]: prior[FIREBASE_SCREEN_INSTANCE_ID_KEY] }, current) : current)), // tslint:disable-next-line:no-console tap((/** * @param {?} params * @return {?} */ params => debugModeEnabled && console.info(SCREEN_VIEW_EVENT, params))), tap((/** * @param {?} params * @return {?} */ params => zone.runOutsideAngular((/** * @return {?} */ () => analytics.logEvent(SCREEN_VIEW_EVENT, params)))))).subscribe(); })); } /** * @return {?} */ ngOnDestroy() { if (this.disposable) { this.disposable.unsubscribe(); } } } ScreenTrackingService.decorators = [ { type: Injectable, args: [{ providedIn: 'any' },] } ]; /** @nocollapse */ ScreenTrackingService.ctorParameters = () => [ { type: AngularFireAnalytics }, { type: Router, decorators: [{ type: Optional }] }, { type: Title, decorators: [{ type: Optional }] }, { type: ComponentFactoryResolver }, { type: Object, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DEBUG_MODE,] }] }, { type: NgZone }, { type: Injector } ]; /** @nocollapse */ ScreenTrackingService.ɵprov = i0.ɵɵdefineInjectable({ factory: function ScreenTrackingService_Factory() { return new ScreenTrackingService(i0.ɵɵinject(i1.AngularFireAnalytics), i0.ɵɵinject(i2.Router, 8), i0.ɵɵinject(i3.Title, 8), i0.ɵɵinject(i0.ComponentFactoryResolver), i0.ɵɵinject(i0.PLATFORM_ID), i0.ɵɵinject(i1.DEBUG_MODE, 8), i0.ɵɵinject(i0.NgZone), i0.ɵɵinject(i0.INJECTOR)); }, token: ScreenTrackingService, providedIn: "any" }); if (false) { /** * @type {?} * @private */ ScreenTrackingService.prototype.disposable; } export class UserTrackingService { // TODO a user properties injector /** * @param {?} analytics * @param {?} zone * @param {?} platformId */ constructor(analytics, zone, // tslint:disable-next-line:ban-types platformId) { /** @type {?} */ const schedulers = new ɵAngularFireSchedulers(zone); if (!isPlatformServer(platformId)) { zone.runOutsideAngular((/** * @return {?} */ () => { // @ts-ignore zap the import in the UMD this.disposable = from(import('firebase/auth')).pipe(observeOn(schedulers.outsideAngular), switchMap((/** * @return {?} */ () => analytics.app)), map((/** * @param {?} app * @return {?} */ app => app.auth())), switchMap((/** * @param {?} auth * @return {?} */ auth => new Observable(auth.onAuthStateChanged.bind(auth)))), switchMap((/** * @param {?} user * @return {?} */ user => analytics.setUserId(user ? user.uid : null)))).subscribe(); })); } } /** * @return {?} */ ngOnDestroy() { if (this.disposable) { this.disposable.unsubscribe(); } } } UserTrackingService.decorators = [ { type: Injectable, args: [{ providedIn: 'any' },] } ]; /** @nocollapse */ UserTrackingService.ctorParameters = () => [ { type: AngularFireAnalytics }, { type: NgZone }, { type: Object, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] } ]; /** @nocollapse */ UserTrackingService.ɵprov = i0.ɵɵdefineInjectable({ factory: function UserTrackingService_Factory() { return new UserTrackingService(i0.ɵɵinject(i1.AngularFireAnalytics), i0.ɵɵinject(i0.NgZone), i0.ɵɵinject(i0.PLATFORM_ID)); }, token: UserTrackingService, providedIn: "any" }); if (false) { /** * @type {?} * @private */ UserTrackingService.prototype.disposable; } export { ɵ0 }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW5hbHl0aWNzLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYW5hbHl0aWNzL2FuYWx5dGljcy5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFBQSxPQUFPLEVBQ0wsd0JBQXdCLEVBQ3hCLE1BQU0sRUFDTixVQUFVLEVBQ1YsUUFBUSxFQUNSLGVBQWUsRUFDZixNQUFNLEVBRU4sUUFBUSxFQUNSLFdBQVcsRUFDWixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQWdCLE1BQU0sTUFBTSxDQUFDO0FBQzFELE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxjQUFjLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNoSSxPQUFPLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0UsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3ZELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxVQUFVLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFL0QsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlCQUFpQixDQUFDOzs7Ozs7TUFFaEUseUJBQXlCLEdBQUcsdUJBQXVCOztNQUNuRCxrQ0FBa0MsR0FBRyx5QkFBeUI7O01BQzlELHdDQUF3QyxHQUFHLHNCQUFzQjs7TUFDakUsaUNBQWlDLEdBQUcsMEJBQTBCOztNQUM5RCx5QkFBeUIsR0FBRyx1QkFBdUI7O01BQ25ELCtCQUErQixHQUFHLG9CQUFvQjs7TUFDdEQsd0JBQXdCLEdB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QVUsU0FBQztnQkFDVixVQUFVLEVBQUUsS0FBSzthQUNsQjs7OztZQXBMUSxvQkFBb0I7WUFUM0IsTUFBTTtZQXVNNkIsTUFBTSx1QkFBdEMsTUFBTSxTQUFDLFdBQVc7Ozs7Ozs7O0lBUHJCLHlDQUE2QyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENvbXBvbmVudEZhY3RvcnlSZXNvbHZlcixcbiAgSW5qZWN0LFxuICBJbmplY3RhYmxlLFxuICBJbmplY3RvcixcbiAgTmdNb2R1bGVGYWN0b3J5LFxuICBOZ1pvbmUsXG4gIE9uRGVzdHJveSxcbiAgT3B0aW9uYWwsXG4gIFBMQVRGT1JNX0lEXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgZnJvbSwgT2JzZXJ2YWJsZSwgb2YsIFN1YnNjcmlwdGlvbiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZmlsdGVyLCBncm91cEJ5LCBtYXAsIG1lcmdlTWFwLCBvYnNlcnZlT24sIHBhaXJ3aXNlLCBzdGFydFdpdGgsIHN3aXRjaE1hcCwgdGFwLCB3aXRoTGF0ZXN0RnJvbSB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IEFjdGl2YXRpb25FbmQsIE5hdmlnYXRpb25FbmQsIFJvdXRlciwgUk9VVEVTIH0gZnJvbSAnQGFuZ3VsYXIvcm91dGVyJztcbmltcG9ydCB7IMm1QW5ndWxhckZpcmVTY2hlZHVsZXJzIH0gZnJvbSAnQGFuZ3VsYXIvZmlyZSc7XG5pbXBvcnQgeyBBbmd1bGFyRmlyZUFuYWx5dGljcywgREVCVUdfTU9ERSB9IGZyb20gJy4vYW5hbHl0aWNzJztcbmltcG9ydCB7IFVzZXIgfSBmcm9tICdmaXJlYmFzZS9hcHAnO1xuaW1wb3J0IHsgVGl0bGUgfSBmcm9tICdAYW5ndWxhci9wbGF0Zm9ybS1icm93c2VyJztcbmltcG9ydCB7IGlzUGxhdGZvcm1Ccm93c2VyLCBpc1BsYXRmb3JtU2VydmVyIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcblxuY29uc3QgRklSRUJBU0VfRVZFTlRfT1JJR0lOX0tFWSA9ICdmaXJlYmFzZV9ldmVudF9vcmlnaW4nO1xuY29uc3QgRklSRUJBU0VfUFJFVklPVVNfU0NSRUVOX0NMQVNTX0tFWSA9ICdmaXJlYmFzZV9wcmV2aW91c19jbGFzcyc7XG5jb25zdCBGSVJFQkFTRV9QUkVWSU9VU19TQ1JFRU5fSU5TVEFOQ0VfSURfS0VZID0gJ2ZpcmViYXNlX3ByZXZpb3VzX2lkJztcbmNvbnN0IEZJUkVCQVNFX1BSRVZJT1VTX1NDUkVFTl9OQU1FX0tFWSA9ICdmaXJlYmFzZV9wcmV2aW91c19zY3JlZW4nO1xuY29uc3QgRklSRUJBU0VfU0NSRUVOX0NMQVNTX0tFWSA9ICdmaXJlYmFzZV9zY3JlZW5fY2xhc3MnO1xuY29uc3QgRklSRUJBU0VfU0NSRUVOX0lOU1RBTkNFX0lEX0tFWSA9ICdmaXJlYmFzZV9zY3JlZW5faWQnO1xuY29uc3QgRklSRUJBU0VfU0NSRUVOX05BTUVfS0VZID0gJ2ZpcmViYXNlX3NjcmVlbic7XG5jb25zdCBPVVRMRVRfS0VZID0gJ291dGxldCc7XG5jb25zdCBQQUdFX1BBVEhfS0VZID0gJ3BhZ2VfcGF0aCc7XG5jb25zdCBQQUdFX1RJVExFX0tFWSA9ICdwYWdlX3RpdGxlJztcbmNvbnN0IFNDUkVFTl9DTEFTU19LRVkgPSAnc2NyZWVuX2NsYXNzJztcbmNvbnN0IFNDUkVFTl9OQU1FX0tFWSA9ICdzY3JlZW5fbmFtZSc7XG5cbmNvbnN0IFNDUkVFTl9WSUVXX0VWRU5UID0gJ3NjcmVlbl92aWV3JztcbmNvbnN0IEVWRU5UX09SSUdJTl9BVVRPID0gJ2F1dG8nO1xuY29uc3QgREVGQVVMVF9TQ1JFRU5fQ0xBU1MgPSAnPz8/JztcbmNvbnN0IE5HX1BSSU1BUllfT1VUTEVUID0gJ3ByaW1hcnknO1xuY29uc3QgU0NSRUVOX0lOU1RBTkNFX0RFTElNSVRFUiA9ICcjJztcblxuY29uc3QgQU5OT1RBVElPTlMgPSAnX19hbm5vdGF0aW9uc19fJztcblxuXG4vLyB0aGlzIGlzIGFuIElOVDY0IGluIGlPUy9BbmRyb2lkIGJ1dCB1c2UgSU5UMzIgY2F1c2UgamF2YXNjcmlwdFxubGV0IG5leHRTY3JlZW5JbnN0YW5jZUlEID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogKDIgKiogMzIgLSAxKSkgLSAyICoqIDMxO1xuXG5jb25zdCBrbm93blNjcmVlbkluc3RhbmNlSURzOiB7IFtrZXk6IHN0cmluZ106IG51bWJlciB9ID0ge307XG5cbmNvbnN0IGdldFNjcmVlbkluc3RhbmNlSUQgPSAocGFyYW1zOiB7IFtrZXk6IHN0cmluZ106IGFueSB9KSA9PiB7XG4gIC8vIHVuaXF1ZSB0aGUgc2NyZWVuIGNsYXNzIGFnYWluc3QgdGhlIG91dGxldCBuYW1lXG4gIGNvbnN0IHNjcmVlbkluc3RhbmNlS2V5ID0gW1xuICAgIHBhcmFtc1tTQ1JFRU5fQ0xBU1NfS0VZXSxcbiAgICBwYXJhbXNbT1VUTEVUX0tFWV1cbiAgXS5qb2luKFNDUkVFTl9JTlNUQU5DRV9ERUxJTUlURVIpO1xuICBpZiAoa25vd25TY3JlZW5JbnN0YW5jZUlEcy5oYXNPd25Qcm9wZXJ0eShzY3JlZW5JbnN0YW5jZUtleSkpIHtcbiAgICByZXR1cm4ga25vd25TY3JlZW5JbnN0YW5jZUlEc1tzY3JlZW5JbnN0YW5jZUtleV07XG4gIH0gZWxzZSB7XG4gICAgY29uc3QgcmV0ID0gbmV4dFNjcmVlbkluc3RhbmNlSUQrKztcbiAgICBrbm93blNjcmVlbkluc3RhbmNlSURzW3NjcmVlbkluc3RhbmNlS2V5XSA9IHJldDtcbiAgICByZXR1cm4gcmV0O1xuICB9XG59O1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdhbnknXG59KVxuZXhwb3J0IGNsYXNzIFNjcmVlblRyYWNraW5nU2VydmljZSBpbXBsZW1lbnRzIE9uRGVzdHJveSB7XG5cbiAgcHJpdmF0ZSBkaXNwb3NhYmxlOiBTdWJzY3JpcHRpb24gfCB1bmRlZmluZWQ7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgYW5hbHl0aWNzOiBBbmd1bGFyRmlyZUFuYWx5dGljcyxcbiAgICBAT3B0aW9uYWwoKSByb3V0ZXI6IFJvdXRlcixcbiAgICBAT3B0aW9uYWwoKSB0aXRsZTogVGl0bGUsXG4gICAgY29tcG9uZW50RmFjdG9yeVJlc29sdmVyOiBDb21wb25lbnRGYWN0b3J5UmVzb2x2ZXIsXG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOmJhbi10eXBlc1xuICAgIEBJbmplY3QoUExBVEZPUk1fSUQpIHBsYXRmb3JtSWQ6IE9iamVjdCxcbiAgICBAT3B0aW9uYWwoKSBASW5qZWN0KERFQlVHX01PREUpIGRlYnVnTW9kZUVuYWJsZWQ6IGJvb2xlYW4gfCBudWxsLFxuICAgIHpvbmU6IE5nWm9uZSxcbiAgICBpbmplY3RvcjogSW5qZWN0b3JcbiAgKSB7XG4gICAgaWYgKCFyb3V0ZXIgfHwgIWlzUGxhdGZvcm1Ccm93c2VyKHBsYXRmb3JtSWQpKSB7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgem9uZS5ydW5PdXRzaWRlQW5ndWxhcigoKSA9PiB7XG4gICAgICBjb25zdCBhY3RpdmF0aW9uRW5kRXZlbnRzID0gcm91dGVyLmV2ZW50cy5waXBlKGZpbHRlcjxBY3RpdmF0aW9uRW5kPihlID0+IGUgaW5zdGFuY2VvZiBBY3RpdmF0aW9uRW5kKSk7XG4gICAgICBjb25zdCBuYXZpZ2F0aW9uRW5kRXZlbnRzID0gcm91dGVyLmV2ZW50cy5waXBlKGZpbHRlcjxOYXZpZ2F0aW9uRW5kPihlID0+IGUgaW5zdGFuY2VvZiBOYXZpZ2F0aW9uRW5kKSk7XG4gICAgICB0aGlzLmRpc3Bvc2FibGUgPSBuYXZpZ2F0aW9uRW5kRXZlbnRzLnBpcGUoXG4gICAgICAgIHdpdGhMYXRlc3RGcm9tKGFjdGl2YXRpb25FbmRFdmVudHMpLFxuICAgICAgICBzd2l0Y2hNYXAoKFtuYXZpZ2F0aW9uRW5kLCBhY3RpdmF0aW9uRW5kXSkgPT4ge1xuICAgICAgICAgIC8vIFNFTVZFUjogc3RhcnQgdXNpbmcgb3B0aW9uYWwgY2hhaW5zIGFuZCBudWxsaXNoIGNvYWxlc2Npbmcgb25jZSB3ZSBzdXBwb3J0IG5ld2VyIHR5cGVzY3JpcHRcbiAgICAgICAgICBjb25zdCBwYWdlUGF0aCA9IG5hdmlnYXRpb25FbmQudXJsO1xuICAgICAgICAgIGNvbnN0IHNjcmVlbk5hbWUgPSBhY3RpdmF0aW9uRW5kLnNuYXBzaG90LnJvdXRlQ29uZmlnICYmIGFjdGl2YXRpb25FbmQuc25hcHNob3Qucm91dGVDb25maWcucGF0aCB8fCBwYWdlUGF0aDtcbiAgICAgICAgICBjb25zdCBwYXJhbXMgPSB7XG4gICAgICAgICAgICBbU0NSRUVOX05BTUVfS0VZXTogc2NyZWVuTmFtZSxcbiAgICAgICAgICAgIFtQQUdFX1BBVEhfS0VZXTogcGFnZVBhdGgsXG4gICAgICAgICAgICBbRklSRUJBU0VfRVZFTlRfT1JJR0lOX0tFWV06IEVWRU5UX09SSUdJTl9BVVRPLFxuICAgICAgICAgICAgW0ZJUkVCQVNFX1NDUkVFTl9OQU1FX0tFWV06IHNjcmVlbk5hbWUsXG4gICAgICAgICAgICBbT1VUTEVUX0tFWV06IGFjdGl2YXRpb25FbmQuc25hcHNob3Qub3V0bGV0XG4gICAgICAgICAgfTtcbiAgICAgICAgICBpZiAodGl0bGUpIHtcbiAgICAgICAgICAgIHBhcmFtc1tQQUdFX1RJVExFX0tFWV0gPSB0aXRsZS5nZXRUaXRsZSgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCBjb21wb25lbnQgPSBhY3RpdmF0aW9uRW5kLnNuYXBzaG90LmNvbXBvbmVudDtcbiAgICAgICAgICBjb25zdCByb3V0ZUNvbmZpZyA9IGFjdGl2YXRpb25FbmQuc25hcHNob3Qucm91dGVDb25maWc7XG4gICAgICAgICAgY29uc3QgbG9hZENoaWxkcmVuID0gcm91dGVDb25maWcgJiYgcm91dGVDb25maWcubG9hZENoaWxkcmVuO1xuICAgICAgICAgIC8vIFRPRE8gZmlndXJlIG91dCBob3cgdG8gaGFuZGxlIG1pbmlmaWNhdGlvblxuICAgICAgICAgIGlmICh0eXBlb2YgbG9hZENoaWxkcmVuID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgLy8gU0VNVkVSOiB0aGlzIGlzIHRoZSBvbGRlciBsYXp5IGxvYWQgc3R5bGUgXCIuL3BhdGgjQ2xhc3NOYW1lXCIsIGRyb3AgdGhpcyB3aGVuIHdlIGRyb3Agb2xkIG5nXG4gICAgICAgICAgICAvLyBUT0RPIGlzIGl0IHdvcnRoIHNlZWluZyBpZiBJIGNhbiBsb29rIHVwIHRoZSBjb21wb25lbnQgZmFjdG9yeSBzZWxlY3RvciBmcm9tIHRoZSBtb2R1bGUgbmFtZT9cbiAgICAgICAgICAgIC8vIGl0J3MgbGF6eSBzbyBpdCdzIG5vdCByZWdpc3RlcmVkIHdpdGggY29tcG9uZW50RmFjdG9yeVJlc29sdmVyIHlldC4uLiBzZWVtcyBhIHBhaW4gZm9yIGEgZGVwcmVjaWF0ZWQgc3R5bGVcbiAgICAgICAgICAgIHJldHVybiBvZih7IC4uLnBhcmFtcywgW1NDUkVFTl9DTEFTU19LRVldOiBsb2FkQ2hpbGRyZW4uc3BsaXQoJyMnKVsxXSB9KTtcbiAgICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBjb21wb25lbnQgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICByZXR1cm4gb2YoeyAuLi5wYXJhbXMsIFtTQ1JFRU5fQ0xBU1NfS0VZXTogY29tcG9uZW50IH0pO1xuICAgICAgICAgIH0gZWxzZSBpZiAoY29tcG9uZW50KSB7XG4gICAgICAgICAgICBjb25zdCBjb21wb25lbnRGYWN0b3J5ID0gY29tcG9uZW50RmFjdG9yeVJlc29sdmVyLnJlc29sdmVDb21wb25lbnRGYWN0b3J5KGNvbXBvbmVudCk7XG4gICAgICAgICAgICByZXR1cm4gb2YoeyAuLi5wYXJhbXMsIFtTQ1JFRU5fQ0xBU1NfS0VZXTogY29tcG9uZW50RmFjdG9yeS5zZWxlY3RvciB9KTtcbiAgICAgICAgICB9IGVsc2UgaWYgKGxvYWRDaGlsZHJlbikge1xuICAgICAgICAgICAgY29uc3QgbG9hZGVkQ2hpbGRyZW4gPSBsb2FkQ2hpbGRyZW4oKTtcbiAgICAgICAgICAgIGNvbnN0IGxvYWRlZENoaWxkcmVuJDogT2JzZXJ2YWJsZTxhbnk+ID0gKGxvYWRlZENoaWxkcmVuIGluc3RhbmNlb2YgT2JzZXJ2YWJsZSkgP1xuICAgICAgICAgICAgICBsb2FkZWRDaGlsZHJlbiA6XG4gICAgICAgICAgICAgIGZyb20oUHJvbWlzZS5yZXNvbHZlKGxvYWRlZENoaWxkcmVuKSk7XG4gICAgICAgICAgICByZXR1cm4gbG9hZGVkQ2hpbGRyZW4kLnBpcGUoXG4gICAgICAgICAgICAgIG1hcChsYXp5TW9kdWxlID0+IHtcbiAgICAgICAgICAgICAgICBpZiAobGF6eU1vZHVsZSBpbnN0YW5jZW9mIE5nTW9kdWxlRmFjdG9yeSkge1xuICAgICAgICAgICAgICAgICAgLy8gQU9UIGNyZWF0ZSBhbiBpbmplY3RvclxuICAgICAgICAgICAgICAgICAgY29uc3QgbW9kdWxlUmVmID0gbGF6eU1vZHVsZS5jcmVhdGUoaW5qZWN0b3IpO1xuICAgICAgICAgICAgICAgICAgLy8gSU5WRVNUSUdBVEUgaXMgdGhpcyB0aGUgcmlnaHQgd2F5IHRvIGdldCBhdCB0aGUgbWF0Y2hpbmcgcm91dGU/XG4gICAgICAgICAgICAgICAgICBjb25zdCByb3V0ZXMgPSBtb2R1bGVSZWYuaW5qZWN0b3IuZ2V0KFJPVVRFUyk7XG4gICAgICAgICAgICAgICAgICBjb25zdCBjb21wb25lbnQgPSByb3V0ZXNbMF1bMF0uY29tcG9uZW50OyAvLyBzaG91bGQgaSBqdXN0IGJlIGdyYWJiaW5nIDAtMCBoZXJlP1xuICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgY29tcG9uZW50RmFjdG9yeSA9IG1vZHVsZVJlZi5jb21wb25lbnRGYWN0b3J5UmVzb2x2ZXIucmVzb2x2ZUNvbXBvbmVudEZhY3RvcnkoY29tcG9uZW50KTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHsgLi4ucGFyYW1zLCBbU0NSRUVOX0NMQVNTX0tFWV06IGNvbXBvbmVudEZhY3Rvcnkuc2VsZWN0b3IgfTtcbiAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKF8pIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHsgLi4ucGFyYW1zLCBbU0NSRUVOX0NMQVNTX0tFWV06IERFRkFVTFRfU0NSRUVOX0NMQVNTIH07XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgIC8vIEpJVCBsb29rIGF0IHRoZSBhbm5vdGF0aW9uc1xuICAgICAgICAgICAgICAgICAgLy8gSU5WRVNUSUdBVEUgYXJlIHRoZXJlIHB1YmxpYyBBUElzIGZvciB0aGlzIHN0dWZmP1xuICAgICAgICAgICAgICAgICAgY29uc3QgZGVjbGFyYXRpb25zID0gW10uY29uY2F0LmFwcGx5KFtdLCAobGF6eU1vZHVsZVtBTk5PVEFUSU9OU10gfHwgW10pLm1hcCgoZjogYW55KSA9PiBmLmRlY2xhcmF0aW9ucykpO1xuICAgICAgICAgICAgICAgICAgY29uc3Qgc2VsZWN0b3JzID0gW10uY29uY2F0LmFwcGx5KFtdLCBkZWNsYXJhdGlvbnMubWFwKChjOiBhbnkpID0+IChjW0FOTk9UQVRJT05TXSB8fCBbXSkubWFwKChmOiBhbnkpID0+IGYuc2VsZWN0b3IpKSk7XG4gICAgICAgICAgICAgICAgICAvLyBzaG91bGQgSSBqdXN0IGJlIGdyYWJiaW5nIHRoZSBzZWxlY3RvciBsaWtlIHRoaXMgb3Igc2hvdWxkIGkgbWF0Y2ggYWdhaW5zdCB0aGUgcm91dGUgY29tcG9uZW50P1xuICAgICAgICAgICAgICAgICAgLy8gICBjb25zdCByb3V0ZXJNb2R1bGUgPSBsYXp5TW9kdWxlLm5nSW5qZWN0b3JEZWYuaW1wb3J0cy5maW5kKGkgPT4gaS5uZ01vZHVsZSAmJiAuLi4uKTtcbiAgICAgICAgICAgICAgICAgIC8vICAgY29uc3Qgcm91dGUgPSByb3V0ZXJNb2R1bGUucHJvdmlkZXJzWzBdLmZpbmQocCA9PiBwLnByb3ZpZGUgPT0gUk9VVEVTKS51c2VWYWx1ZVswXTtcbiAgICAgICAgICAgICAgICAgIHJldHVybiB7IC4uLnBhcmFtcywgW1NDUkVFTl9DTEFTU19LRVldOiBzZWxlY3RvcnNbMF0gfHwgREVGQVVMVF9TQ1JFRU5fQ0xBU1MgfTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gb2YoeyAuLi5wYXJhbXMsIFtTQ1JFRU5fQ0xBU1NfS0VZXTogREVGQVVMVF9TQ1JFRU5fQ0xBU1MgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KSxcbiAgICAgICAgbWFwKHBhcmFtcyA9PiAoe1xuICAgICAgICAgIFtGSVJFQkFTRV9TQ1JFRU5fQ0xBU1NfS0VZXTogcGFyYW1zW1NDUkVFTl9DTEFTU19LRVldLFxuICAgICAgICAgIFtGSVJFQkFTRV9TQ1JFRU5fSU5TVEFOQ0VfSURfS0VZXTogZ2V0U2NyZWVuSW5zdGFuY2VJRChwYXJhbXMpLFxuICAgICAgICAgIC4uLnBhcmFtc1xuICAgICAgICB9KSksXG4gICAgICAgIHRhcChwYXJhbXMgPT4ge1xuICAgICAgICAgIC8vIFRPRE8gcGVyaGFwcyBJIGNhbiBiZSBzbWFydGVyIGFib3V0IHRoaXMsIGJ1YmJsZSBldmVudHMgdXAgdG8gdGhlIG5lYXJlc3Qgb3V0bGV0P1xuICAgICAgICAgIGlmIChwYXJhbXNbT1VUTEVUX0tFWV0gPT09IE5HX1BSSU1BUllfT1VUTEVUKSB7XG4gICAgICAgICAgICBhbmFseXRpY3Muc2V0Q3VycmVudFNjcmVlbihwYXJhbXNbU0NSRUVOX05BTUVfS0VZXSk7XG4gICAgICAgICAgICBhbmFseXRpY3MudXBkYXRlQ29uZmlnKHtcbiAgICAgICAgICAgICAgW1BBR0VfUEFUSF9LRVldOiBwYXJhbXNbUEFHRV9QQVRIX0tFWV0sXG4gICAgICAgICAgICAgIFtTQ1JFRU5fQ0xBU1NfS0VZXTogcGFyYW1zW1NDUkVFTl9DTEFTU19LRVldXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGlmICh0aXRsZSkge1xuICAgICAgICAgICAgICBhbmFseXRpY3MudXBkYXRlQ29uZmlnKHsgW1BBR0VfVElUTEVfS0VZXTogcGFyYW1zW1BBR0VfVElUTEVfS0VZXSB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0pLFxuICAgICAgICBncm91cEJ5KHBhcmFtcyA9PiBwYXJhbXNbT1VUTEVUX0tFWV0pLFxuICAgICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmVcbiAgICAgICAgbWVyZ2VNYXAoZ3JvdXAgPT4gZ3JvdXAucGlwZShzdGFydFdpdGgodW5kZWZpbmVkKSwgcGFpcndpc2UoKSkpLFxuICAgICAgICBtYXAoKFtwcmlvciwgY3VycmVudF0pID0+IHByaW9yID8ge1xuICAgICAgICAgIFtGSVJFQkFTRV9QUkVWSU9VU19TQ1JFRU5fQ0xBU1NfS0VZXTogcHJpb3JbU0NSRUVOX0NMQVNTX0tFWV0sXG4gICAgICAgICAgW0ZJUkVCQVNFX1BSRVZJT1VTX1NDUkVFTl9OQU1FX0tFWV06IHByaW9yW1NDUkVFTl9OQU1FX0tFWV0sXG4gICAgICAgICAgW0ZJUkVCQVNFX1BSRVZJT1VTX1NDUkVFTl9JTlNUQU5DRV9JRF9LRVldOiBwcmlvcltGSVJFQkFTRV9TQ1JFRU5fSU5TVEFOQ0VfSURfS0VZXSxcbiAgICAgICAgICAuLi5jdXJyZW50XG4gICAgICAgIH0gOiBjdXJyZW50KSxcbiAgICAgICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWNvbnNvbGVcbiAgICAgICAgdGFwKHBhcmFtcyA9PiBkZWJ1Z01vZGVFbmFibGVkICYmIGNvbnNvbGUuaW5mbyhTQ1JFRU5fVklFV19FVkVOVCwgcGFyYW1zKSksXG4gICAgICAgIHRhcChwYXJhbXMgPT4gem9uZS5ydW5PdXRzaWRlQW5ndWxhcigoKSA9PiBhbmFseXRpY3MubG9nRXZlbnQoU0NSRUVOX1ZJRVdfRVZFTlQsIHBhcmFtcykpKVxuICAgICAgKS5zdWJzY3JpYmUoKTtcbiAgICB9KTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCkge1xuICAgIGlmICh0aGlzLmRpc3Bvc2FibGUpIHtcbiAgICAgIHRoaXMuZGlzcG9zYWJsZS51bnN1YnNjcmliZSgpO1xuICAgIH1cbiAgfVxuXG59XG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ2FueSdcbn0pXG5leHBvcnQgY2xhc3MgVXNlclRyYWNraW5nU2VydmljZSBpbXBsZW1lbnRzIE9uRGVzdHJveSB7XG5cbiAgcHJpdmF0ZSBkaXNwb3NhYmxlOiBTdWJzY3JpcHRpb24gfCB1bmRlZmluZWQ7XG5cbiAgLy8gVE9ETyBhIHVzZXIgcHJvcGVydGllcyBpbmplY3RvclxuICBjb25zdHJ1Y3RvcihcbiAgICBhbmFseXRpY3M6IEFuZ3VsYXJGaXJlQW5hbHl0aWNzLFxuICAgIHpvbmU6IE5nWm9uZSxcbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6YmFuLXR5cGVzXG4gICAgQEluamVjdChQTEFURk9STV9JRCkgcGxhdGZvcm1JZDogT2JqZWN0XG4gICkge1xuICAgIGNvbnN0IHNjaGVkdWxlcnMgPSBuZXcgybVBbmd1bGFyRmlyZVNjaGVkdWxlcnMoem9uZSk7XG5cbiAgICBpZiAoIWlzUGxhdGZvcm1TZXJ2ZXIocGxhdGZvcm1JZCkpIHtcbiAgICAgIHpvbmUucnVuT3V0c2lkZUFuZ3VsYXIoKCkgPT4ge1xuICAgICAgICAvLyBAdHMtaWdub3JlIHphcCB0aGUgaW1wb3J0IGluIHRoZSBVTURcbiAgICAgICAgdGhpcy5kaXNwb3NhYmxlID0gZnJvbShpbXBvcnQoJ2ZpcmViYXNlL2F1dGgnKSkucGlwZShcbiAgICAgICAgICBvYnNlcnZlT24oc2NoZWR1bGVycy5vdXRzaWRlQW5ndWxhciksXG4gICAgICAgICAgc3dpdGNoTWFwKCgpID0+IGFuYWx5dGljcy5hcHApLFxuICAgICAgICAgIG1hcChhcHAgPT4gYXBwLmF1dGgoKSksXG4gICAgICAgICAgc3dpdGNoTWFwKGF1dGggPT4gbmV3IE9ic2VydmFibGU8VXNlciB8IG51bGw+KGF1dGgub25BdXRoU3RhdGVDaGFuZ2VkLmJpbmQoYXV0aCkpKSxcbiAgICAgICAgICBzd2l0Y2hNYXAodXNlciA9PiBhbmFseXRpY3Muc2V0VXNlcklkKHVzZXIgPyB1c2VyLnVpZCA6IG51bGwpKVxuICAgICAgICApLnN1YnNjcmliZSgpO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgaWYgKHRoaXMuZGlzcG9zYWJsZSkge1xuICAgICAgdGhpcy5kaXNwb3NhYmxlLnVuc3Vic2NyaWJlKCk7XG4gICAgfVxuICB9XG59XG4iXX0=