UNPKG

@ionic/angular

Version:

Angular specific wrappers for @ionic/core

480 lines 66.6 kB
import { ViewContainerRef, inject, Attribute, Directive, EventEmitter, Optional, Output, SkipSelf, EnvironmentInjector, Input, InjectionToken, Injectable, reflectComponentType, } from '@angular/core'; import { Router, ActivatedRoute, ChildrenOutletContexts, PRIMARY_OUTLET } from '@angular/router'; import { componentOnReady } from '@ionic/core/components'; import { BehaviorSubject, combineLatest, of } from 'rxjs'; import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators'; import { Config } from '../../providers/config'; import { NavController } from '../../providers/nav-controller'; import { StackController } from './stack-controller'; import { getUrl, isTabSwitch } from './stack-utils'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; import * as i2 from "@angular/router"; // TODO(FW-2827): types // eslint-disable-next-line @angular-eslint/directive-class-suffix export class IonRouterOutlet { parentOutlet; nativeEl; activatedView = null; tabsPrefix; _swipeGesture; stackCtrl; // Maintain map of activated route proxies for each component instance proxyMap = new WeakMap(); // Keep the latest activated route in a subject for the proxy routes to switch map to currentActivatedRoute$ = new BehaviorSubject(null); activated = null; /** @internal */ get activatedComponentRef() { return this.activated; } _activatedRoute = null; /** * The name of the outlet */ name = PRIMARY_OUTLET; /** @internal */ stackWillChange = new EventEmitter(); /** @internal */ stackDidChange = new EventEmitter(); // eslint-disable-next-line @angular-eslint/no-output-rename activateEvents = new EventEmitter(); // eslint-disable-next-line @angular-eslint/no-output-rename deactivateEvents = new EventEmitter(); parentContexts = inject(ChildrenOutletContexts); location = inject(ViewContainerRef); environmentInjector = inject(EnvironmentInjector); inputBinder = inject(INPUT_BINDER, { optional: true }); /** @nodoc */ supportsBindingToComponentInputs = true; // Ionic providers config = inject(Config); navCtrl = inject(NavController); set animation(animation) { this.nativeEl.animation = animation; } set animated(animated) { this.nativeEl.animated = animated; } set swipeGesture(swipe) { this._swipeGesture = swipe; this.nativeEl.swipeHandler = swipe ? { canStart: () => this.stackCtrl.canGoBack(1) && !this.stackCtrl.hasRunningTask(), onStart: () => this.stackCtrl.startBackTransition(), onEnd: (shouldContinue) => this.stackCtrl.endBackTransition(shouldContinue), } : undefined; } constructor(name, tabs, commonLocation, elementRef, router, zone, activatedRoute, parentOutlet) { this.parentOutlet = parentOutlet; this.nativeEl = elementRef.nativeElement; this.name = name || PRIMARY_OUTLET; this.tabsPrefix = tabs === 'true' ? getUrl(router, activatedRoute) : undefined; this.stackCtrl = new StackController(this.tabsPrefix, this.nativeEl, router, this.navCtrl, zone, commonLocation); this.parentContexts.onChildOutletCreated(this.name, this); } ngOnDestroy() { this.stackCtrl.destroy(); this.inputBinder?.unsubscribeFromRouteData(this); } getContext() { return this.parentContexts.getContext(this.name); } ngOnInit() { this.initializeOutletWithName(); } // Note: Ionic deviates from the Angular Router implementation here initializeOutletWithName() { if (!this.activated) { // If the outlet was not instantiated at the time the route got activated we need to populate // the outlet when it is initialized (ie inside a NgIf) const context = this.getContext(); if (context?.route) { this.activateWith(context.route, context.injector); } } new Promise((resolve) => componentOnReady(this.nativeEl, resolve)).then(() => { if (this._swipeGesture === undefined) { this.swipeGesture = this.config.getBoolean('swipeBackEnabled', this.nativeEl.mode === 'ios'); } }); } get isActivated() { return !!this.activated; } get component() { if (!this.activated) { throw new Error('Outlet is not activated'); } return this.activated.instance; } get activatedRoute() { if (!this.activated) { throw new Error('Outlet is not activated'); } return this._activatedRoute; } get activatedRouteData() { if (this._activatedRoute) { return this._activatedRoute.snapshot.data; } return {}; } /** * Called when the `RouteReuseStrategy` instructs to detach the subtree */ detach() { throw new Error('incompatible reuse strategy'); } /** * Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree */ // eslint-disable-next-line @typescript-eslint/no-unused-vars attach(_ref, _activatedRoute) { throw new Error('incompatible reuse strategy'); } deactivate() { if (this.activated) { if (this.activatedView) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const context = this.getContext(); this.activatedView.savedData = new Map(context.children['contexts']); /** * Angular v11.2.10 introduced a change * where this route context is cleared out when * a router-outlet is deactivated, However, * we need this route information in order to * return a user back to the correct tab when * leaving and then going back to the tab context. */ const primaryOutlet = this.activatedView.savedData.get('primary'); if (primaryOutlet && context.route) { primaryOutlet.route = { ...context.route }; } /** * Ensure we are saving the NavigationExtras * data otherwise it will be lost */ this.activatedView.savedExtras = {}; if (context.route) { const contextSnapshot = context.route.snapshot; this.activatedView.savedExtras.queryParams = contextSnapshot.queryParams; this.activatedView.savedExtras.fragment = contextSnapshot.fragment; } } const c = this.component; this.activatedView = null; this.activated = null; this._activatedRoute = null; this.deactivateEvents.emit(c); } } activateWith(activatedRoute, environmentInjector) { if (this.isActivated) { throw new Error('Cannot activate an already activated outlet'); } this._activatedRoute = activatedRoute; let cmpRef; let enteringView = this.stackCtrl.getExistingView(activatedRoute); if (enteringView) { cmpRef = this.activated = enteringView.ref; const saved = enteringView.savedData; if (saved) { // self-restore // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const context = this.getContext(); context.children['contexts'] = saved; } // Updated activated route proxy for this component this.updateActivatedRouteProxy(cmpRef.instance, activatedRoute); } else { const snapshot = activatedRoute._futureSnapshot; /** * Angular 14 introduces a new `loadComponent` property to the route config. * This function will assign a `component` property to the route snapshot. * We check for the presence of this property to determine if the route is * using standalone components. */ const childContexts = this.parentContexts.getOrCreateContext(this.name).children; // We create an activated route proxy object that will maintain future updates for this component // over its lifecycle in the stack. const component$ = new BehaviorSubject(null); const activatedRouteProxy = this.createActivatedRouteProxy(component$, activatedRoute); const injector = new OutletInjector(activatedRouteProxy, childContexts, this.location.injector); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const component = snapshot.routeConfig.component ?? snapshot.component; /** * View components need to be added as a child of ion-router-outlet * for page transitions and swipe to go back. * However, createComponent mounts components as siblings of the * ViewContainerRef. As a result, outletContent must reference * an ng-container inside of ion-router-outlet and not * ion-router-outlet itself. */ cmpRef = this.activated = this.outletContent.createComponent(component, { index: this.outletContent.length, injector, environmentInjector: environmentInjector ?? this.environmentInjector, }); // Once the component is created we can push it to our local subject supplied to the proxy component$.next(cmpRef.instance); // Calling `markForCheck` to make sure we will run the change detection when the // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component. /** * At this point this.activated has been set earlier * in this function, so it is guaranteed to be non-null. */ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion enteringView = this.stackCtrl.createView(this.activated, activatedRoute); // Store references to the proxy by component this.proxyMap.set(cmpRef.instance, activatedRouteProxy); this.currentActivatedRoute$.next({ component: cmpRef.instance, activatedRoute }); } this.inputBinder?.bindActivatedRouteToOutletComponent(this); this.activatedView = enteringView; /** * The top outlet is set prior to the entering view's transition completing, * so that when we have nested outlets (e.g. ion-tabs inside an ion-router-outlet), * the tabs outlet will be assigned as the top outlet when a view inside tabs is * activated. * * In this scenario, activeWith is called for both the tabs and the root router outlet. * To avoid a race condition, we assign the top outlet synchronously. */ this.navCtrl.setTopOutlet(this); const leavingView = this.stackCtrl.getActiveView(); this.stackWillChange.emit({ enteringView, tabSwitch: isTabSwitch(enteringView, leavingView), }); this.stackCtrl.setActive(enteringView).then((data) => { this.activateEvents.emit(cmpRef.instance); this.stackDidChange.emit(data); }); } /** * Returns `true` if there are pages in the stack to go back. */ canGoBack(deep = 1, stackId) { return this.stackCtrl.canGoBack(deep, stackId); } /** * Resolves to `true` if it the outlet was able to sucessfully pop the last N pages. */ pop(deep = 1, stackId) { return this.stackCtrl.pop(deep, stackId); } /** * Returns the URL of the active page of each stack. */ getLastUrl(stackId) { const active = this.stackCtrl.getLastUrl(stackId); return active ? active.url : undefined; } /** * Returns the RouteView of the active page of each stack. * @internal */ getLastRouteView(stackId) { return this.stackCtrl.getLastUrl(stackId); } /** * Returns the root view in the tab stack. * @internal */ getRootView(stackId) { return this.stackCtrl.getRootUrl(stackId); } /** * Returns the active stack ID. In the context of ion-tabs, it means the active tab. */ getActiveStackId() { return this.stackCtrl.getActiveStackId(); } /** * Since the activated route can change over the life time of a component in an ion router outlet, we create * a proxy so that we can update the values over time as a user navigates back to components already in the stack. */ createActivatedRouteProxy(component$, activatedRoute) { const proxy = new ActivatedRoute(); proxy._futureSnapshot = activatedRoute._futureSnapshot; proxy._routerState = activatedRoute._routerState; proxy.snapshot = activatedRoute.snapshot; proxy.outlet = activatedRoute.outlet; proxy.component = activatedRoute.component; // Setup wrappers for the observables so consumers don't have to worry about switching to new observables as the state updates proxy._paramMap = this.proxyObservable(component$, 'paramMap'); proxy._queryParamMap = this.proxyObservable(component$, 'queryParamMap'); proxy.url = this.proxyObservable(component$, 'url'); proxy.params = this.proxyObservable(component$, 'params'); proxy.queryParams = this.proxyObservable(component$, 'queryParams'); proxy.fragment = this.proxyObservable(component$, 'fragment'); proxy.data = this.proxyObservable(component$, 'data'); return proxy; } /** * Create a wrapped observable that will switch to the latest activated route matched by the given component */ proxyObservable(component$, path) { return component$.pipe( // First wait until the component instance is pushed filter((component) => !!component), switchMap((component) => this.currentActivatedRoute$.pipe(filter((current) => current !== null && current.component === component), switchMap((current) => current && current.activatedRoute[path]), distinctUntilChanged()))); } /** * Updates the activated route proxy for the given component to the new incoming router state */ updateActivatedRouteProxy(component, activatedRoute) { const proxy = this.proxyMap.get(component); if (!proxy) { throw new Error(`Could not find activated route proxy for view`); } proxy._futureSnapshot = activatedRoute._futureSnapshot; proxy._routerState = activatedRoute._routerState; proxy.snapshot = activatedRoute.snapshot; proxy.outlet = activatedRoute.outlet; proxy.component = activatedRoute.component; this.currentActivatedRoute$.next({ component, activatedRoute }); } /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonRouterOutlet, deps: [{ token: 'name', attribute: true }, { token: 'tabs', attribute: true, optional: true }, { token: i1.Location }, { token: i0.ElementRef }, { token: i2.Router }, { token: i0.NgZone }, { token: i2.ActivatedRoute }, { token: IonRouterOutlet, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive }); /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: IonRouterOutlet, selector: "ion-router-outlet", inputs: { animated: "animated", animation: "animation", mode: "mode", swipeGesture: "swipeGesture", name: "name" }, outputs: { stackWillChange: "stackWillChange", stackDidChange: "stackDidChange", activateEvents: "activate", deactivateEvents: "deactivate" }, exportAs: ["outlet"], ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: IonRouterOutlet, decorators: [{ type: Directive, args: [{ selector: 'ion-router-outlet', exportAs: 'outlet', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property inputs: ['animated', 'animation', 'mode', 'swipeGesture'], }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Attribute, args: ['name'] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Attribute, args: ['tabs'] }] }, { type: i1.Location }, { type: i0.ElementRef }, { type: i2.Router }, { type: i0.NgZone }, { type: i2.ActivatedRoute }, { type: IonRouterOutlet, decorators: [{ type: SkipSelf }, { type: Optional }] }]; }, propDecorators: { name: [{ type: Input }], stackWillChange: [{ type: Output }], stackDidChange: [{ type: Output }], activateEvents: [{ type: Output, args: ['activate'] }], deactivateEvents: [{ type: Output, args: ['deactivate'] }] } }); class OutletInjector { route; childContexts; parent; constructor(route, childContexts, parent) { this.route = route; this.childContexts = childContexts; this.parent = parent; } get(token, notFoundValue) { if (token === ActivatedRoute) { return this.route; } if (token === ChildrenOutletContexts) { return this.childContexts; } return this.parent.get(token, notFoundValue); } } // TODO: FW-4785 - Remove this once Angular 15 support is dropped const INPUT_BINDER = new InjectionToken(''); /** * Injectable used as a tree-shakable provider for opting in to binding router data to component * inputs. * * The RouterOutlet registers itself with this service when an `ActivatedRoute` is attached or * activated. When this happens, the service subscribes to the `ActivatedRoute` observables (params, * queryParams, data) and sets the inputs of the component using `ComponentRef.setInput`. * Importantly, when an input does not have an item in the route data with a matching key, this * input is set to `undefined`. If it were not done this way, the previous information would be * retained if the data got removed from the route (i.e. if a query parameter is removed). * * The `RouterOutlet` should unregister itself when destroyed via `unsubscribeFromRouteData` so that * the subscriptions are cleaned up. */ class RoutedComponentInputBinder { outletDataSubscriptions = new Map(); bindActivatedRouteToOutletComponent(outlet) { this.unsubscribeFromRouteData(outlet); this.subscribeToRouteData(outlet); } unsubscribeFromRouteData(outlet) { this.outletDataSubscriptions.get(outlet)?.unsubscribe(); this.outletDataSubscriptions.delete(outlet); } subscribeToRouteData(outlet) { const { activatedRoute } = outlet; const dataSubscription = combineLatest([activatedRoute.queryParams, activatedRoute.params, activatedRoute.data]) .pipe(switchMap(([queryParams, params, data], index) => { data = { ...queryParams, ...params, ...data }; // Get the first result from the data subscription synchronously so it's available to // the component as soon as possible (and doesn't require a second change detection). if (index === 0) { return of(data); } // Promise.resolve is used to avoid synchronously writing the wrong data when // two of the Observables in the `combineLatest` stream emit one after // another. return Promise.resolve(data); })) .subscribe((data) => { // Outlet may have been deactivated or changed names to be associated with a different // route if (!outlet.isActivated || !outlet.activatedComponentRef || outlet.activatedRoute !== activatedRoute || activatedRoute.component === null) { this.unsubscribeFromRouteData(outlet); return; } const mirror = reflectComponentType(activatedRoute.component); if (!mirror) { this.unsubscribeFromRouteData(outlet); return; } for (const { templateName } of mirror.inputs) { outlet.activatedComponentRef.setInput(templateName, data[templateName]); } }); this.outletDataSubscriptions.set(outlet, dataSubscription); } /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RoutedComponentInputBinder, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RoutedComponentInputBinder }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RoutedComponentInputBinder, decorators: [{ type: Injectable }] }); export const provideComponentInputBinding = () => { return { provide: INPUT_BINDER, useFactory: componentInputBindingFactory, deps: [Router], }; }; function componentInputBindingFactory(router) { /** * We cast the router to any here, since the componentInputBindingEnabled * property is not available until Angular v16. */ if (router?.componentInputBindingEnabled) { return new RoutedComponentInputBinder(); } return null; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVyLW91dGxldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL2NvbW1vbi9zcmMvZGlyZWN0aXZlcy9uYXZpZ2F0aW9uL3JvdXRlci1vdXRsZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQU9MLGdCQUFnQixFQUNoQixNQUFNLEVBQ04sU0FBUyxFQUNULFNBQVMsRUFDVCxZQUFZLEVBQ1osUUFBUSxFQUNSLE1BQU0sRUFDTixRQUFRLEVBQ1IsbUJBQW1CLEVBQ25CLEtBQUssRUFDTCxjQUFjLEVBQ2QsVUFBVSxFQUNWLG9CQUFvQixHQUNyQixNQUFNLGVBQWUsQ0FBQztBQUV2QixPQUFPLEVBQWlCLE1BQU0sRUFBRSxjQUFjLEVBQUUsc0JBQXNCLEVBQUUsY0FBYyxFQUFRLE1BQU0saUJBQWlCLENBQUM7QUFDdEgsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFMUQsT0FBTyxFQUFjLGVBQWUsRUFBZ0IsYUFBYSxFQUFFLEVBQUUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNwRixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXpFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNoRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFL0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3JELE9BQU8sRUFBd0QsTUFBTSxFQUFFLFdBQVcsRUFBRSxNQUFNLGVBQWUsQ0FBQzs7OztBQUUxRyx1QkFBdUI7QUFRdkIsa0VBQWtFO0FBQ2xFLE1BQU0sT0FBZ0IsZUFBZTtJQTRFQTtJQXpFbkMsUUFBUSxDQUE2QjtJQUNyQyxhQUFhLEdBQXFCLElBQUksQ0FBQztJQUN2QyxVQUFVLENBQXFCO0lBRXZCLGFBQWEsQ0FBVztJQUN4QixTQUFT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QUFFRixTQUFTLDRCQUE0QixDQUFDLE1BQWU7SUFDbkQ7OztPQUdHO0lBQ0gsSUFBSyxNQUFjLEVBQUUsNEJBQTRCLEVBQUU7UUFDakQsT0FBTyxJQUFJLDBCQUEwQixFQUFFLENBQUM7S0FDekM7SUFDRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBMb2NhdGlvbiB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQge1xuICBDb21wb25lbnRSZWYsXG4gIEVsZW1lbnRSZWYsXG4gIEluamVjdG9yLFxuICBOZ1pvbmUsXG4gIE9uRGVzdHJveSxcbiAgT25Jbml0LFxuICBWaWV3Q29udGFpbmVyUmVmLFxuICBpbmplY3QsXG4gIEF0dHJpYnV0ZSxcbiAgRGlyZWN0aXZlLFxuICBFdmVudEVtaXR0ZXIsXG4gIE9wdGlvbmFsLFxuICBPdXRwdXQsXG4gIFNraXBTZWxmLFxuICBFbnZpcm9ubWVudEluamVjdG9yLFxuICBJbnB1dCxcbiAgSW5qZWN0aW9uVG9rZW4sXG4gIEluamVjdGFibGUsXG4gIHJlZmxlY3RDb21wb25lbnRUeXBlLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB0eXBlIHsgUHJvdmlkZXIgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE91dGxldENvbnRleHQsIFJvdXRlciwgQWN0aXZhdGVkUm91dGUsIENoaWxkcmVuT3V0bGV0Q29udGV4dHMsIFBSSU1BUllfT1VUTEVULCBEYXRhIH0gZnJvbSAnQGFuZ3VsYXIvcm91dGVyJztcbmltcG9ydCB7IGNvbXBvbmVudE9uUmVhZHkgfSBmcm9tICdAaW9uaWMvY29yZS9jb21wb25lbnRzJztcbmltcG9ydCB0eXBlIHsgQW5pbWF0aW9uQnVpbGRlciB9IGZyb20gJ0Bpb25pYy9jb3JlL2NvbXBvbmVudHMnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgQmVoYXZpb3JTdWJqZWN0LCBTdWJzY3JpcHRpb24sIGNvbWJpbmVMYXRlc3QsIG9mIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBkaXN0aW5jdFVudGlsQ2hhbmdlZCwgZmlsdGVyLCBzd2l0Y2hNYXAgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5cbmltcG9ydCB7IENvbmZpZyB9IGZyb20gJy4uLy4uL3Byb3ZpZGVycy9jb25maWcnO1xuaW1wb3J0IHsgTmF2Q29udHJvbGxlciB9IGZyb20gJy4uLy4uL3Byb3ZpZGVycy9uYXYtY29udHJvbGxlcic7XG5cbmltcG9ydCB7IFN0YWNrQ29udHJvbGxlciB9IGZyb20gJy4vc3RhY2stY29udHJvbGxlcic7XG5pbXBvcnQgeyBSb3V0ZVZpZXcsIFN0YWNrRGlkQ2hhbmdlRXZlbnQsIFN0YWNrV2lsbENoYW5nZUV2ZW50LCBnZXRVcmwsIGlzVGFiU3dpdGNoIH0gZnJvbSAnLi9zdGFjay11dGlscyc7XG5cbi8vIFRPRE8oRlctMjgyNyk6IHR5cGVzXG5cbkBEaXJlY3RpdmUoe1xuICBzZWxlY3RvcjogJ2lvbi1yb3V0ZXItb3V0bGV0JyxcbiAgZXhwb3J0QXM6ICdvdXRsZXQnLFxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQGFuZ3VsYXItZXNsaW50L25vLWlucHV0cy1tZXRhZGF0YS1wcm9wZXJ0eVxuICBpbnB1dHM6IFsnYW5pbWF0ZWQnLCAnYW5pbWF0aW9uJywgJ21vZGUnLCAnc3dpcGVHZXN0dXJlJ10sXG59KVxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEBhbmd1bGFyLWVzbGludC9kaXJlY3RpdmUtY2xhc3Mtc3VmZml4XG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgSW9uUm91dGVyT3V0bGV0IGltcGxlbWVudHMgT25EZXN0cm95LCBPbkluaXQge1xuICBhYnN0cmFjdCBvdXRsZXRDb250ZW50OiBhbnk7XG5cbiAgbmF0aXZlRWw6IEhUTUxJb25Sb3V0ZXJPdXRsZXRFbGVtZW50O1xuICBhY3RpdmF0ZWRWaWV3OiBSb3V0ZVZpZXcgfCBudWxsID0gbnVsbDtcbiAgdGFic1ByZWZpeDogc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG4gIHByaXZhdGUgX3N3aXBlR2VzdHVyZT86IGJvb2xlYW47XG4gIHByaXZhdGUgc3RhY2tDdHJsOiBTdGFja0NvbnRyb2xsZXI7XG5cbiAgLy8gTWFpbnRhaW4gbWFwIG9mIGFjdGl2YXRlZCByb3V0ZSBwcm94aWVzIGZvciBlYWNoIGNvbXBvbmVudCBpbnN0YW5jZVxuICBwcml2YXRlIHByb3h5TWFwID0gbmV3IFdlYWtNYXA8YW55LCBBY3RpdmF0ZWRSb3V0ZT4oKTtcbiAgLy8gS2VlcCB0aGUgbGF0ZXN0IGFjdGl2YXRlZCByb3V0ZSBpbiBhIHN1YmplY3QgZm9yIHRoZSBwcm94eSByb3V0ZXMgdG8gc3dpdGNoIG1hcCB0b1xuICBwcml2YXRlIGN1cnJlbnRBY3RpdmF0ZWRSb3V0ZSQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PHsgY29tcG9uZW50OiBhbnk7IGFjdGl2YXRlZFJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSB9IHwgbnVsbD4obnVsbCk7XG5cbiAgcHJpdmF0ZSBhY3RpdmF0ZWQ6IENvbXBvbmVudFJlZjxhbnk+IHwgbnVsbCA9IG51bGw7XG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgZ2V0IGFjdGl2YXRlZENvbXBvbmVudFJlZigpOiBDb21wb25lbnRSZWY8YW55PiB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmFjdGl2YXRlZDtcbiAgfVxuICBwcml2YXRlIF9hY3RpdmF0ZWRSb3V0ZTogQWN0aXZhdGVkUm91dGUgfCBudWxsID0gbnVsbDtcblxuICAvKipcbiAgICogVGhlIG5hbWUgb2YgdGhlIG91dGxldFxuICAgKi9cbiAgQElucHV0KCkgbmFtZSA9IFBSSU1BUllfT1VUTEVUO1xuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgQE91dHB1dCgpIHN0YWNrV2lsbENoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8U3RhY2tXaWxsQ2hhbmdlRXZlbnQ+KCk7XG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgQE91dHB1dCgpIHN0YWNrRGlkQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxTdGFja0RpZENoYW5nZUV2ZW50PigpO1xuXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAYW5ndWxhci1lc2xpbnQvbm8tb3V0cHV0LXJlbmFtZVxuICBAT3V0cHV0KCdhY3RpdmF0ZScpIGFjdGl2YXRlRXZlbnRzID0gbmV3IEV2ZW50RW1pdHRlcjxhbnk+KCk7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAYW5ndWxhci1lc2xpbnQvbm8tb3V0cHV0LXJlbmFtZVxuICBAT3V0cHV0KCdkZWFjdGl2YXRlJykgZGVhY3RpdmF0ZUV2ZW50cyA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuXG4gIHByaXZhdGUgcGFyZW50Q29udGV4dHMgPSBpbmplY3QoQ2hpbGRyZW5PdXRsZXRDb250ZXh0cyk7XG4gIHByaXZhdGUgbG9jYXRpb24gPSBpbmplY3QoVmlld0NvbnRhaW5lclJlZik7XG4gIHByaXZhdGUgZW52aXJvbm1lbnRJbmplY3RvciA9IGluamVjdChFbnZpcm9ubWVudEluamVjdG9yKTtcbiAgcHJpdmF0ZSBpbnB1dEJpbmRlciA9IGluamVjdChJTlBVVF9CSU5ERVIsIHsgb3B0aW9uYWw6IHRydWUgfSk7XG4gIC8qKiBAbm9kb2MgKi9cbiAgcmVhZG9ubHkgc3VwcG9ydHNCaW5kaW5nVG9Db21wb25lbnRJbnB1dHMgPSB0cnVlO1xuXG4gIC8vIElvbmljIHByb3ZpZGVyc1xuICBwcml2YXRlIGNvbmZpZyA9IGluamVjdChDb25maWcpO1xuICBwcml2YXRlIG5hdkN0cmwgPSBpbmplY3QoTmF2Q29udHJvbGxlcik7XG5cbiAgc2V0IGFuaW1hdGlvbihhbmltYXRpb246IEFuaW1hdGlvbkJ1aWxkZXIpIHtcbiAgICB0aGlzLm5hdGl2ZUVsLmFuaW1hdGlvbiA9IGFuaW1hdGlvbjtcbiAgfVxuXG4gIHNldCBhbmltYXRlZChhbmltYXRlZDogYm9vbGVhbikge1xuICAgIHRoaXMubmF0aXZlRWwuYW5pbWF0ZWQgPSBhbmltYXRlZDtcbiAgfVxuXG4gIHNldCBzd2lwZUdlc3R1cmUoc3dpcGU6IGJvb2xlYW4pIHtcbiAgICB0aGlzLl9zd2lwZUdlc3R1cmUgPSBzd2lwZTtcblxuICAgIHRoaXMubmF0aXZlRWwuc3dpcGVIYW5kbGVyID0gc3dpcGVcbiAgICAgID8ge1xuICAgICAgICAgIGNhblN0YXJ0OiAoKSA9PiB0aGlzLnN0YWNrQ3RybC5jYW5Hb0JhY2soMSkgJiYgIXRoaXMuc3RhY2tDdHJsLmhhc1J1bm5pbmdUYXNrKCksXG4gICAgICAgICAgb25TdGFydDogKCkgPT4gdGhpcy5zdGFja0N0cmwuc3RhcnRCYWNrVHJhbnNpdGlvbigpLFxuICAgICAgICAgIG9uRW5kOiAoc2hvdWxkQ29udGludWUpID0+IHRoaXMuc3RhY2tDdHJsLmVuZEJhY2tUcmFuc2l0aW9uKHNob3VsZENvbnRpbnVlKSxcbiAgICAgICAgfVxuICAgICAgOiB1bmRlZmluZWQ7XG4gIH1cblxuICBjb25zdHJ1Y3RvcihcbiAgICBAQXR0cmlidXRlKCduYW1lJykgbmFtZTogc3RyaW5nLFxuICAgIEBPcHRpb25hbCgpIEBBdHRyaWJ1dGUoJ3RhYnMnKSB0YWJzOiBzdHJpbmcsXG4gICAgY29tbW9uTG9jYXRpb246IExvY2F0aW9uLFxuICAgIGVsZW1lbnRSZWY6IEVsZW1lbnRSZWYsXG4gICAgcm91dGVyOiBSb3V0ZXIsXG4gICAgem9uZTogTmdab25lLFxuICAgIGFjdGl2YXRlZFJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSxcbiAgICBAU2tpcFNlbGYoKSBAT3B0aW9uYWwoKSByZWFkb25seSBwYXJlbnRPdXRsZXQ/OiBJb25Sb3V0ZXJPdXRsZXRcbiAgKSB7XG4gICAgdGhpcy5uYXRpdmVFbCA9IGVsZW1lbnRSZWYubmF0aXZlRWxlbWVudDtcbiAgICB0aGlzLm5hbWUgPSBuYW1lIHx8IFBSSU1BUllfT1VUTEVUO1xuICAgIHRoaXMudGFic1ByZWZpeCA9IHRhYnMgPT09ICd0cnVlJyA/IGdldFVybChyb3V0ZXIsIGFjdGl2YXRlZFJvdXRlKSA6IHVuZGVmaW5lZDtcbiAgICB0aGlzLnN0YWNrQ3RybCA9IG5ldyBTdGFja0NvbnRyb2xsZXIodGhpcy50YWJzUHJlZml4LCB0aGlzLm5hdGl2ZUVsLCByb3V0ZXIsIHRoaXMubmF2Q3RybCwgem9uZSwgY29tbW9uTG9jYXRpb24pO1xuICAgIHRoaXMucGFyZW50Q29udGV4dHMub25DaGlsZE91dGxldENyZWF0ZWQodGhpcy5uYW1lLCB0aGlzIGFzIGFueSk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLnN0YWNrQ3RybC5kZXN0cm95KCk7XG4gICAgdGhpcy5pbnB1dEJpbmRlcj8udW5zdWJzY3JpYmVGcm9tUm91dGVEYXRhKHRoaXMpO1xuICB9XG5cbiAgZ2V0Q29udGV4dCgpOiBPdXRsZXRDb250ZXh0IHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMucGFyZW50Q29udGV4dHMuZ2V0Q29udGV4dCh0aGlzLm5hbWUpO1xuICB9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgdGhpcy5pbml0aWFsaXplT3V0bGV0V2l0aE5hbWUoKTtcbiAgfVxuXG4gIC8vIE5vdGU6IElvbmljIGRldmlhdGVzIGZyb20gdGhlIEFuZ3VsYXIgUm91dGVyIGltcGxlbWVudGF0aW9uIGhlcmVcbiAgcHJpdmF0ZSBpbml0aWFsaXplT3V0bGV0V2l0aE5hbWUoKSB7XG4gICAgaWYgKCF0aGlzLmFjdGl2YXRlZCkge1xuICAgICAgLy8gSWYgdGhlIG91dGxldCB3YXMgbm90IGluc3RhbnRpYXRlZCBhdCB0aGUgdGltZSB0aGUgcm91dGUgZ290IGFjdGl2YXRlZCB3ZSBuZWVkIHRvIHBvcHVsYXRlXG4gICAgICAvLyB0aGUgb3V0bGV0IHdoZW4gaXQgaXMgaW5pdGlhbGl6ZWQgKGllIGluc2lkZSBhIE5nSWYpXG4gICAgICBjb25zdCBjb250ZXh0ID0gdGhpcy5nZXRDb250ZXh0KCk7XG4gICAgICBpZiAoY29udGV4dD8ucm91dGUpIHtcbiAgICAgICAgdGhpcy5hY3RpdmF0ZVdpdGgoY29udGV4dC5yb3V0ZSwgY29udGV4dC5pbmplY3Rvcik7XG4gICAgICB9XG4gICAgfVxuXG4gICAgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IGNvbXBvbmVudE9uUmVhZHkodGhpcy5uYXRpdmVFbCwgcmVzb2x2ZSkpLnRoZW4oKCkgPT4ge1xuICAgICAgaWYgKHRoaXMuX3N3aXBlR2VzdHVyZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRoaXMuc3dpcGVHZXN0dXJlID0gdGhpcy5jb25maWcuZ2V0Qm9vbGVhbignc3dpcGVCYWNrRW5hYmxlZCcsICh0aGlzLm5hdGl2ZUVsIGFzIGFueSkubW9kZSA9PT0gJ2lvcycpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgZ2V0IGlzQWN0aXZhdGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAhIXRoaXMuYWN0aXZhdGVkO1xuICB9XG5cbiAgZ2V0IGNvbXBvbmVudCgpOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gICAgaWYgKCF0aGlzLmFjdGl2YXRlZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdPdXRsZXQgaXMgbm90IGFjdGl2YXRlZCcpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5hY3RpdmF0ZWQuaW5zdGFuY2U7XG4gIH1cblxuICBnZXQgYWN0aXZhdGVkUm91dGUoKTogQWN0aXZhdGVkUm91dGUge1xuICAgIGlmICghdGhpcy5hY3RpdmF0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignT3V0bGV0IGlzIG5vdCBhY3RpdmF0ZWQnKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2FjdGl2YXRlZFJvdXRlIGFzIEFjdGl2YXRlZFJvdXRlO1xuICB9XG5cbiAgZ2V0IGFjdGl2YXRlZFJvdXRlRGF0YSgpOiBEYXRhIHtcbiAgICBpZiAodGhpcy5fYWN0aXZhdGVkUm91dGUpIHtcbiAgICAgIHJldHVybiB0aGlzLl9hY3RpdmF0ZWRSb3V0ZS5zbmFwc2hvdC5kYXRhO1xuICAgIH1cbiAgICByZXR1cm4ge307XG4gIH1cblxuICAvKipcbiAgICogQ2FsbGVkIHdoZW4gdGhlIGBSb3V0ZVJldXNlU3RyYXRlZ3lgIGluc3RydWN0cyB0byBkZXRhY2ggdGhlIHN1YnRyZWVcbiAgICovXG4gIGRldGFjaCgpOiBDb21wb25lbnRSZWY8YW55PiB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdpbmNvbXBhdGlibGUgcmV1c2Ugc3RyYXRlZ3knKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxsZWQgd2hlbiB0aGUgYFJvdXRlUmV1c2VTdHJhdGVneWAgaW5zdHJ1Y3RzIHRvIHJlLWF0dGFjaCBhIHByZXZpb3VzbHkgZGV0YWNoZWQgc3VidHJlZVxuICAgKi9cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby11bnVzZWQtdmFyc1xuICBhdHRhY2goX3JlZjogQ29tcG9uZW50UmVmPGFueT4sIF9hY3RpdmF0ZWRSb3V0ZTogQWN0aXZhdGVkUm91dGUpOiB2b2lkIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2luY29tcGF0aWJsZSByZXVzZSBzdHJhdGVneScpO1xuICB9XG5cbiAgZGVhY3RpdmF0ZSgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5hY3RpdmF0ZWQpIHtcbiAgICAgIGlmICh0aGlzLmFjdGl2YXRlZFZpZXcpIHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb25cbiAgICAgICAgY29uc3QgY29udGV4dCA9IHRoaXMuZ2V0Q29udGV4dCgpITtcbiAgICAgICAgdGhpcy5hY3RpdmF0ZWRWaWV3LnNhdmVkRGF0YSA9IG5ldyBNYXAoY29udGV4dC5jaGlsZHJlblsnY29udGV4dHMnXSk7XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEFuZ3VsYXIgdjExLjIuMTAgaW50cm9kdWNlZCBhIGNoYW5nZVxuICAgICAgICAgKiB3aGVyZSB0aGlzIHJvdXRlIGNvbnRleHQgaXMgY2xlYXJlZCBvdXQgd2hlblxuICAgICAgICAgKiBhIHJvdXRlci1vdXRsZXQgaXMgZGVhY3RpdmF0ZWQsIEhvd2V2ZXIsXG4gICAgICAgICAqIHdlIG5lZWQgdGhpcyByb3V0ZSBpbmZvcm1hdGlvbiBpbiBvcmRlciB0b1xuICAgICAgICAgKiByZXR1cm4gYSB1c2VyIGJhY2sgdG8gdGhlIGNvcnJlY3QgdGFiIHdoZW5cbiAgICAgICAgICogbGVhdmluZyBhbmQgdGhlbiBnb2luZyBiYWNrIHRvIHRoZSB0YWIgY29udGV4dC5cbiAgICAgICAgICovXG4gICAgICAgIGNvbnN0IHByaW1hcnlPdXRsZXQgPSB0aGlzLmFjdGl2YXRlZFZpZXcuc2F2ZWREYXRhLmdldCgncHJpbWFyeScpO1xuICAgICAgICBpZiAocHJpbWFyeU91dGxldCAmJiBjb250ZXh0LnJvdXRlKSB7XG4gICAgICAgICAgcHJpbWFyeU91dGxldC5yb3V0ZSA9IHsgLi4uY29udGV4dC5yb3V0ZSB9O1xuICAgICAgICB9XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIEVuc3VyZSB3ZSBhcmUgc2F2aW5nIHRoZSBOYXZpZ2F0aW9uRXh0cmFzXG4gICAgICAgICAqIGRhdGEgb3RoZXJ3aXNlIGl0IHdpbGwgYmUgbG9zdFxuICAgICAgICAgKi9cbiAgICAgICAgdGhpcy5hY3RpdmF0ZWRWaWV3LnNhdmVkRXh0cmFzID0ge307XG4gICAgICAgIGlmIChjb250ZXh0LnJvdXRlKSB7XG4gICAgICAgICAgY29uc3QgY29udGV4dFNuYXBzaG90ID0gY29udGV4dC5yb3V0ZS5zbmFwc2hvdDtcblxuICAgICAgICAgIHRoaXMuYWN0aXZhdGVkVmlldy5zYXZlZEV4dHJhcy5xdWVyeVBhcmFtcyA9IGNvbnRleHRTbmFwc2hvdC5xdWVyeVBhcmFtcztcbiAgICAgICAgICAodGhpcy5hY3RpdmF0ZWRWaWV3LnNhdmVkRXh0cmFzLmZyYWdtZW50IGFzIHN0cmluZyB8IG51bGwpID0gY29udGV4dFNuYXBzaG90LmZyYWdtZW50O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjb25zdCBjID0gdGhpcy5jb21wb25lbnQ7XG4gICAgICB0aGlzLmFjdGl2YXRlZFZpZXcgPSBudWxsO1xuICAgICAgdGhpcy5hY3RpdmF0ZWQgPSBudWxsO1xuICAgICAgdGhpcy5fYWN0aXZhdGVkUm91dGUgPSBudWxsO1x