@ionic/angular
Version:
Angular specific wrappers for @ionic/core
461 lines • 65.1 kB
JavaScript
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 {
constructor(name, tabs, commonLocation, elementRef, router, zone, activatedRoute, parentOutlet) {
this.parentOutlet = parentOutlet;
this.activatedView = null;
// Maintain map of activated route proxies for each component instance
this.proxyMap = new WeakMap();
// Keep the latest activated route in a subject for the proxy routes to switch map to
this.currentActivatedRoute$ = new BehaviorSubject(null);
this.activated = null;
this._activatedRoute = null;
/**
* The name of the outlet
*/
this.name = PRIMARY_OUTLET;
/** @internal */
this.stackWillChange = new EventEmitter();
/** @internal */
this.stackDidChange = new EventEmitter();
// eslint-disable-next-line @angular-eslint/no-output-rename
this.activateEvents = new EventEmitter();
// eslint-disable-next-line @angular-eslint/no-output-rename
this.deactivateEvents = new EventEmitter();
this.parentContexts = inject(ChildrenOutletContexts);
this.location = inject(ViewContainerRef);
this.environmentInjector = inject(EnvironmentInjector);
this.inputBinder = inject(INPUT_BINDER, { optional: true });
/** @nodoc */
this.supportsBindingToComponentInputs = true;
// Ionic providers
this.config = inject(Config);
this.navCtrl = inject(NavController);
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);
}
/** @internal */
get activatedComponentRef() {
return this.activated;
}
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;
}
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;
cmpRef = this.activated = this.location.createComponent(component, {
index: this.location.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.
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 */ IonRouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.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 */ IonRouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.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: "14.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 {
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 {
constructor() {
this.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 */ RoutedComponentInputBinder.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RoutedComponentInputBinder, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ RoutedComponentInputBinder.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RoutedComponentInputBinder });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVyLW91dGxldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL2NvbW1vbi9zcmMvZGlyZWN0aXZlcy9uYXZpZ2F0aW9uL3JvdXRlci1vdXRsZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQU9MLGdCQUFnQixFQUNoQixNQUFNLEVBQ04sU0FBUyxFQUNULFNBQVMsRUFDVCxZQUFZLEVBQ1osUUFBUSxFQUNSLE1BQU0sRUFDTixRQUFRLEVBQ1IsbUJBQW1CLEVBQ25CLEtBQUssRUFDTCxjQUFjLEVBQ2QsVUFBVSxFQUNWLG9CQUFvQixHQUNyQixNQUFNLGVBQWUsQ0FBQztBQUV2QixPQUFPLEVBQWlCLE1BQU0sRUFBRSxjQUFjLEVBQUUsc0JBQXNCLEVBQUUsY0FBYyxFQUFRLE1BQU0saUJBQWlCLENBQUM7QUFDdEgsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFMUQsT0FBTyxFQUFjLGVBQWUsRUFBZ0IsYUFBYSxFQUFFLEVBQUUsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNwRixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXpFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNoRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFL0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ3JELE9BQU8sRUFBd0QsTUFBTSxFQUFFLFdBQVcsRUFBRSxNQUFNLGVBQWUsQ0FBQzs7OztBQUUxRyx1QkFBdUI7QUFRdkIsa0VBQWtFO0FBQ2xFLE1BQU0sT0FBTyxlQUFlO0lBa0UxQixZQUNxQixJQUFZLEVBQ0EsSUFBWSxFQUMzQyxjQUF3QixFQUN4QixVQUFzQixFQUN0QixNQUFjLEVBQ2QsSUFBWSxFQUNaLGNBQThCLEVBQ0csWUFBOEI7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QztJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IExvY2F0aW9uIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7XG4gIENvbXBvbmVudFJlZixcbiAgRWxlbWVudFJlZixcbiAgSW5qZWN0b3IsXG4gIE5nWm9uZSxcbiAgT25EZXN0cm95LFxuICBPbkluaXQsXG4gIFZpZXdDb250YWluZXJSZWYsXG4gIGluamVjdCxcbiAgQXR0cmlidXRlLFxuICBEaXJlY3RpdmUsXG4gIEV2ZW50RW1pdHRlcixcbiAgT3B0aW9uYWwsXG4gIE91dHB1dCxcbiAgU2tpcFNlbGYsXG4gIEVudmlyb25tZW50SW5qZWN0b3IsXG4gIElucHV0LFxuICBJbmplY3Rpb25Ub2tlbixcbiAgSW5qZWN0YWJsZSxcbiAgcmVmbGVjdENvbXBvbmVudFR5cGUsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHR5cGUgeyBQcm92aWRlciB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgT3V0bGV0Q29udGV4dCwgUm91dGVyLCBBY3RpdmF0ZWRSb3V0ZSwgQ2hpbGRyZW5PdXRsZXRDb250ZXh0cywgUFJJTUFSWV9PVVRMRVQsIERhdGEgfSBmcm9tICdAYW5ndWxhci9yb3V0ZXInO1xuaW1wb3J0IHsgY29tcG9uZW50T25SZWFkeSB9IGZyb20gJ0Bpb25pYy9jb3JlL2NvbXBvbmVudHMnO1xuaW1wb3J0IHR5cGUgeyBBbmltYXRpb25CdWlsZGVyIH0gZnJvbSAnQGlvbmljL2NvcmUvY29tcG9uZW50cyc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlLCBCZWhhdmlvclN1YmplY3QsIFN1YnNjcmlwdGlvbiwgY29tYmluZUxhdGVzdCwgb2YgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGRpc3RpbmN0VW50aWxDaGFuZ2VkLCBmaWx0ZXIsIHN3aXRjaE1hcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHsgQ29uZmlnIH0gZnJvbSAnLi4vLi4vcHJvdmlkZXJzL2NvbmZpZyc7XG5pbXBvcnQgeyBOYXZDb250cm9sbGVyIH0gZnJvbSAnLi4vLi4vcHJvdmlkZXJzL25hdi1jb250cm9sbGVyJztcblxuaW1wb3J0IHsgU3RhY2tDb250cm9sbGVyIH0gZnJvbSAnLi9zdGFjay1jb250cm9sbGVyJztcbmltcG9ydCB7IFJvdXRlVmlldywgU3RhY2tEaWRDaGFuZ2VFdmVudCwgU3RhY2tXaWxsQ2hhbmdlRXZlbnQsIGdldFVybCwgaXNUYWJTd2l0Y2ggfSBmcm9tICcuL3N0YWNrLXV0aWxzJztcblxuLy8gVE9ETyhGVy0yODI3KTogdHlwZXNcblxuQERpcmVjdGl2ZSh7XG4gIHNlbGVjdG9yOiAnaW9uLXJvdXRlci1vdXRsZXQnLFxuICBleHBvcnRBczogJ291dGxldCcsXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAYW5ndWxhci1lc2xpbnQvbm8taW5wdXRzLW1ldGFkYXRhLXByb3BlcnR5XG4gIGlucHV0czogWydhbmltYXRlZCcsICdhbmltYXRpb24nLCAnbW9kZScsICdzd2lwZUdlc3R1cmUnXSxcbn0pXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQGFuZ3VsYXItZXNsaW50L2RpcmVjdGl2ZS1jbGFzcy1zdWZmaXhcbmV4cG9ydCBjbGFzcyBJb25Sb3V0ZXJPdXRsZXQgaW1wbGVtZW50cyBPbkRlc3Ryb3ksIE9uSW5pdCB7XG4gIG5hdGl2ZUVsOiBIVE1MSW9uUm91dGVyT3V0bGV0RWxlbWVudDtcbiAgYWN0aXZhdGVkVmlldzogUm91dGVWaWV3IHwgbnVsbCA9IG51bGw7XG4gIHRhYnNQcmVmaXg6IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICBwcml2YXRlIF9zd2lwZUdlc3R1cmU/OiBib29sZWFuO1xuICBwcml2YXRlIHN0YWNrQ3RybDogU3RhY2tDb250cm9sbGVyO1xuXG4gIC8vIE1haW50YWluIG1hcCBvZiBhY3RpdmF0ZWQgcm91dGUgcHJveGllcyBmb3IgZWFjaCBjb21wb25lbnQgaW5zdGFuY2VcbiAgcHJpdmF0ZSBwcm94eU1hcCA9IG5ldyBXZWFrTWFwPGFueSwgQWN0aXZhdGVkUm91dGU+KCk7XG4gIC8vIEtlZXAgdGhlIGxhdGVzdCBhY3RpdmF0ZWQgcm91dGUgaW4gYSBzdWJqZWN0IGZvciB0aGUgcHJveHkgcm91dGVzIHRvIHN3aXRjaCBtYXAgdG9cbiAgcHJpdmF0ZSBjdXJyZW50QWN0aXZhdGVkUm91dGUkID0gbmV3IEJlaGF2aW9yU3ViamVjdDx7IGNvbXBvbmVudDogYW55OyBhY3RpdmF0ZWRSb3V0ZTogQWN0aXZhdGVkUm91dGUgfSB8IG51bGw+KG51bGwpO1xuXG4gIHByaXZhdGUgYWN0aXZhdGVkOiBDb21wb25lbnRSZWY8YW55PiB8IG51bGwgPSBudWxsO1xuICAvKiogQGludGVybmFsICovXG4gIGdldCBhY3RpdmF0ZWRDb21wb25lbnRSZWYoKTogQ29tcG9uZW50UmVmPGFueT4gfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5hY3RpdmF0ZWQ7XG4gIH1cbiAgcHJpdmF0ZSBfYWN0aXZhdGVkUm91dGU6IEFjdGl2YXRlZFJvdXRlIHwgbnVsbCA9IG51bGw7XG5cbiAgLyoqXG4gICAqIFRoZSBuYW1lIG9mIHRoZSBvdXRsZXRcbiAgICovXG4gIEBJbnB1dCgpIG5hbWUgPSBQUklNQVJZX09VVExFVDtcblxuICAvKiogQGludGVybmFsICovXG4gIEBPdXRwdXQoKSBzdGFja1dpbGxDaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPFN0YWNrV2lsbENoYW5nZUV2ZW50PigpO1xuICAvKiogQGludGVybmFsICovXG4gIEBPdXRwdXQoKSBzdGFja0RpZENoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8U3RhY2tEaWRDaGFuZ2VFdmVudD4oKTtcblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQGFuZ3VsYXItZXNsaW50L25vLW91dHB1dC1yZW5hbWVcbiAgQE91dHB1dCgnYWN0aXZhdGUnKSBhY3RpdmF0ZUV2ZW50cyA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQGFuZ3VsYXItZXNsaW50L25vLW91dHB1dC1yZW5hbWVcbiAgQE91dHB1dCgnZGVhY3RpdmF0ZScpIGRlYWN0aXZhdGVFdmVudHMgPSBuZXcgRXZlbnRFbWl0dGVyPGFueT4oKTtcblxuICBwcml2YXRlIHBhcmVudENvbnRleHRzID0gaW5qZWN0KENoaWxkcmVuT3V0bGV0Q29udGV4dHMpO1xuICBwcml2YXRlIGxvY2F0aW9uID0gaW5qZWN0KFZpZXdDb250YWluZXJSZWYpO1xuICBwcml2YXRlIGVudmlyb25tZW50SW5qZWN0b3IgPSBpbmplY3QoRW52aXJvbm1lbnRJbmplY3Rvcik7XG4gIHByaXZhdGUgaW5wdXRCaW5kZXIgPSBpbmplY3QoSU5QVVRfQklOREVSLCB7IG9wdGlvbmFsOiB0cnVlIH0pO1xuICAvKiogQG5vZG9jICovXG4gIHJlYWRvbmx5IHN1cHBvcnRzQmluZGluZ1RvQ29tcG9uZW50SW5wdXRzID0gdHJ1ZTtcblxuICAvLyBJb25pYyBwcm92aWRlcnNcbiAgcHJpdmF0ZSBjb25maWcgPSBpbmplY3QoQ29uZmlnKTtcbiAgcHJpdmF0ZSBuYXZDdHJsID0gaW5qZWN0KE5hdkNvbnRyb2xsZXIpO1xuXG4gIHNldCBhbmltYXRpb24oYW5pbWF0aW9uOiBBbmltYXRpb25CdWlsZGVyKSB7XG4gICAgdGhpcy5uYXRpdmVFbC5hbmltYXRpb24gPSBhbmltYXRpb247XG4gIH1cblxuICBzZXQgYW5pbWF0ZWQoYW5pbWF0ZWQ6IGJvb2xlYW4pIHtcbiAgICB0aGlzLm5hdGl2ZUVsLmFuaW1hdGVkID0gYW5pbWF0ZWQ7XG4gIH1cblxuICBzZXQgc3dpcGVHZXN0dXJlKHN3aXBlOiBib29sZWFuKSB7XG4gICAgdGhpcy5fc3dpcGVHZXN0dXJlID0gc3dpcGU7XG5cbiAgICB0aGlzLm5hdGl2ZUVsLnN3aXBlSGFuZGxlciA9IHN3aXBlXG4gICAgICA/IHtcbiAgICAgICAgICBjYW5TdGFydDogKCkgPT4gdGhpcy5zdGFja0N0cmwuY2FuR29CYWNrKDEpICYmICF0aGlzLnN0YWNrQ3RybC5oYXNSdW5uaW5nVGFzaygpLFxuICAgICAgICAgIG9uU3RhcnQ6ICgpID0+IHRoaXMuc3RhY2tDdHJsLnN0YXJ0QmFja1RyYW5zaXRpb24oKSxcbiAgICAgICAgICBvbkVuZDogKHNob3VsZENvbnRpbnVlKSA9PiB0aGlzLnN0YWNrQ3RybC5lbmRCYWNrVHJhbnNpdGlvbihzaG91bGRDb250aW51ZSksXG4gICAgICAgIH1cbiAgICAgIDogdW5kZWZpbmVkO1xuICB9XG5cbiAgY29uc3RydWN0b3IoXG4gICAgQEF0dHJpYnV0ZSgnbmFtZScpIG5hbWU6IHN0cmluZyxcbiAgICBAT3B0aW9uYWwoKSBAQXR0cmlidXRlKCd0YWJzJykgdGFiczogc3RyaW5nLFxuICAgIGNvbW1vbkxvY2F0aW9uOiBMb2NhdGlvbixcbiAgICBlbGVtZW50UmVmOiBFbGVtZW50UmVmLFxuICAgIHJvdXRlcjogUm91dGVyLFxuICAgIHpvbmU6IE5nWm9uZSxcbiAgICBhY3RpdmF0ZWRSb3V0ZTogQWN0aXZhdGVkUm91dGUsXG4gICAgQFNraXBTZWxmKCkgQE9wdGlvbmFsKCkgcmVhZG9ubHkgcGFyZW50T3V0bGV0PzogSW9uUm91dGVyT3V0bGV0XG4gICkge1xuICAgIHRoaXMubmF0aXZlRWwgPSBlbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQ7XG4gICAgdGhpcy5uYW1lID0gbmFtZSB8fCBQUklNQVJZX09VVExFVDtcbiAgICB0aGlzLnRhYnNQcmVmaXggPSB0YWJzID09PSAndHJ1ZScgPyBnZXRVcmwocm91dGVyLCBhY3RpdmF0ZWRSb3V0ZSkgOiB1bmRlZmluZWQ7XG4gICAgdGhpcy5zdGFja0N0cmwgPSBuZXcgU3RhY2tDb250cm9sbGVyKHRoaXMudGFic1ByZWZpeCwgdGhpcy5uYXRpdmVFbCwgcm91dGVyLCB0aGlzLm5hdkN0cmwsIHpvbmUsIGNvbW1vbkxvY2F0aW9uKTtcbiAgICB0aGlzLnBhcmVudENvbnRleHRzLm9uQ2hpbGRPdXRsZXRDcmVhdGVkKHRoaXMubmFtZSwgdGhpcyBhcyBhbnkpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5zdGFja0N0cmwuZGVzdHJveSgpO1xuICAgIHRoaXMuaW5wdXRCaW5kZXI/LnVuc3Vic2NyaWJlRnJvbVJvdXRlRGF0YSh0aGlzKTtcbiAgfVxuXG4gIGdldENvbnRleHQoKTogT3V0bGV0Q29udGV4dCB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLnBhcmVudENvbnRleHRzLmdldENvbnRleHQodGhpcy5uYW1lKTtcbiAgfVxuXG4gIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIHRoaXMuaW5pdGlhbGl6ZU91dGxldFdpdGhOYW1lKCk7XG4gIH1cblxuICAvLyBOb3RlOiBJb25pYyBkZXZpYXRlcyBmcm9tIHRoZSBBbmd1bGFyIFJvdXRlciBpbXBsZW1lbnRhdGlvbiBoZXJlXG4gIHByaXZhdGUgaW5pdGlhbGl6ZU91dGxldFdpdGhOYW1lKCkge1xuICAgIGlmICghdGhpcy5hY3RpdmF0ZWQpIHtcbiAgICAgIC8vIElmIHRoZSBvdXRsZXQgd2FzIG5vdCBpbnN0YW50aWF0ZWQgYXQgdGhlIHRpbWUgdGhlIHJvdXRlIGdvdCBhY3RpdmF0ZWQgd2UgbmVlZCB0byBwb3B1bGF0ZVxuICAgICAgLy8gdGhlIG91dGxldCB3aGVuIGl0IGlzIGluaXRpYWxpemVkIChpZSBpbnNpZGUgYSBOZ0lmKVxuICAgICAgY29uc3QgY29udGV4dCA9IHRoaXMuZ2V0Q29udGV4dCgpO1xuICAgICAgaWYgKGNvbnRleHQ/LnJvdXRlKSB7XG4gICAgICAgIHRoaXMuYWN0aXZhdGVXaXRoKGNvbnRleHQucm91dGUsIGNvbnRleHQuaW5qZWN0b3IpO1xuICAgICAgfVxuICAgIH1cblxuICAgIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBjb21wb25lbnRPblJlYWR5KHRoaXMubmF0aXZlRWwsIHJlc29sdmUpKS50aGVuKCgpID0+IHtcbiAgICAgIGlmICh0aGlzLl9zd2lwZUdlc3R1cmUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICB0aGlzLnN3aXBlR2VzdHVyZSA9IHRoaXMuY29uZmlnLmdldEJvb2xlYW4oJ3N3aXBlQmFja0VuYWJsZWQnLCAodGhpcy5uYXRpdmVFbCBhcyBhbnkpLm1vZGUgPT09ICdpb3MnKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGdldCBpc0FjdGl2YXRlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gISF0aGlzLmFjdGl2YXRlZDtcbiAgfVxuXG4gIGdldCBjb21wb25lbnQoKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICAgIGlmICghdGhpcy5hY3RpdmF0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignT3V0bGV0IGlzIG5vdCBhY3RpdmF0ZWQnKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuYWN0aXZhdGVkLmluc3RhbmNlO1xuICB9XG5cbiAgZ2V0IGFjdGl2YXRlZFJvdXRlKCk6IEFjdGl2YXRlZFJvdXRlIHtcbiAgICBpZiAoIXRoaXMuYWN0aXZhdGVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ091dGxldCBpcyBub3QgYWN0aXZhdGVkJyk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9hY3RpdmF0ZWRSb3V0ZSBhcyBBY3RpdmF0ZWRSb3V0ZTtcbiAgfVxuXG4gIGdldCBhY3RpdmF0ZWRSb3V0ZURhdGEoKTogRGF0YSB7XG4gICAgaWYgKHRoaXMuX2FjdGl2YXRlZFJvdXRlKSB7XG4gICAgICByZXR1cm4gdGhpcy5fYWN0aXZhdGVkUm91dGUuc25hcHNob3QuZGF0YTtcbiAgICB9XG4gICAgcmV0dXJuIHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGxlZCB3aGVuIHRoZSBgUm91dGVSZXVzZVN0cmF0ZWd5YCBpbnN0cnVjdHMgdG8gZGV0YWNoIHRoZSBzdWJ0cmVlXG4gICAqL1xuICBkZXRhY2goKTogQ29tcG9uZW50UmVmPGFueT4ge1xuICAgIHRocm93IG5ldyBFcnJvcignaW5jb21wYXRpYmxlIHJldXNlIHN0cmF0ZWd5Jyk7XG4gIH1cblxuICAvKipcbiAgICogQ2FsbGVkIHdoZW4gdGhlIGBSb3V0ZVJldXNlU3RyYXRlZ3lgIGluc3RydWN0cyB0byByZS1hdHRhY2ggYSBwcmV2aW91c2x5IGRldGFjaGVkIHN1YnRyZWVcbiAgICovXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdW51c2VkLXZhcnNcbiAgYXR0YWNoKF9yZWY6IENvbXBvbmVudFJlZjxhbnk+LCBfYWN0aXZhdGVkUm91dGU6IEFjdGl2YXRlZFJvdXRlKTogdm9pZCB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdpbmNvbXBhdGlibGUgcmV1c2Ugc3RyYXRlZ3knKTtcbiAgfVxuXG4gIGRlYWN0aXZhdGUoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuYWN0aXZhdGVkKSB7XG4gICAgICBpZiAodGhpcy5hY3RpdmF0ZWRWaWV3KSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tbm9uLW51bGwtYXNzZXJ0aW9uXG4gICAgICAgIGNvbnN0IGNvbnRleHQgPSB0aGlzLmdldENvbnRleHQoKSE7XG4gICAgICAgIHRoaXMuYWN0aXZhdGVkVmlldy5zYXZlZERhdGEgPSBuZXcgTWFwKGNvbnRleHQuY2hpbGRyZW5bJ2NvbnRleHRzJ10pO1xuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBBbmd1bGFyIHYxMS4yLjEwIGludHJvZHVjZWQgYSBjaGFuZ2VcbiAgICAgICAgICogd2hlcmUgdGhpcyByb3V0ZSBjb250ZXh0IGlzIGNsZWFyZWQgb3V0IHdoZW5cbiAgICAgICAgICogYSByb3V0ZXItb3V0bGV0IGlzIGRlYWN0aXZhdGVkLCBIb3dldmVyLFxuICAgICAgICAgKiB3ZSBuZWVkIHRoaXMgcm91dGUgaW5mb3JtYXRpb24gaW4gb3JkZXIgdG9cbiAgICAgICAgICogcmV0dXJuIGEgdXNlciBiYWNrIHRvIHRoZSBjb3JyZWN0IHRhYiB3aGVuXG4gICAgICAgICAqIGxlYXZpbmcgYW5kIHRoZW4gZ29pbmcgYmFjayB0byB0aGUgdGFiIGNvbnRleHQuXG4gICAgICAgICAqL1xuICAgICAgICBjb25zdCBwcmltYXJ5T3V0bGV0ID0gdGhpcy5hY3RpdmF0ZWRWaWV3LnNhdmVkRGF0YS5nZXQoJ3ByaW1hcnknKTtcbiAgICAgICAgaWYgKHByaW1hcnlPdXRsZXQgJiYgY29udGV4dC5yb3V0ZSkge1xuICAgICAgICAgIHByaW1hcnlPdXRsZXQucm91dGUgPSB7IC4uLmNvbnRleHQucm91dGUgfTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBFbnN1cmUgd2UgYXJlIHNhdmluZyB0aGUgTmF2aWdhdGlvbkV4dHJhc1xuICAgICAgICAgKiBkYXRhIG90aGVyd2lzZSBpdCB3aWxsIGJlIGxvc3RcbiAgICAgICAgICovXG4gICAgICAgIHRoaXMuYWN0aXZhdGVkVmlldy5zYXZlZEV4dHJhcyA9IHt9O1xuICAgICAgICBpZiAoY29udGV4dC5yb3V0ZSkge1xuICAgICAgICAgIGNvbnN0IGNvbnRleHRTbmFwc2hvdCA9IGNvbnRleHQucm91dGUuc25hcHNob3Q7XG5cbiAgICAgICAgICB0aGlzLmFjdGl2YXRlZFZpZXcuc2F2ZWRFeHRyYXMucXVlcnlQYXJhbXMgPSBjb250ZXh0U25hcHNob3QucXVlcnlQYXJhbXM7XG4gICAgICAgICAgKHRoaXMuYWN0aXZhdGVkVmlldy5zYXZlZEV4dHJhcy5mcmFnbWVudCBhcyBzdHJpbmcgfCBudWxsKSA9IGNvbnRleHRTbmFwc2hvdC5mcmFnbWVudDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgY29uc3QgYyA9IHRoaXMuY29tcG9uZW50O1xuICAgICAgdGhpcy5hY3RpdmF0ZWRWaWV3ID0gbnVsbDtcbiAgICAgIHRoaXMuYWN0aXZhdGVkID0gbnVsbDtcbiAgICAgIHRoaXMuX2FjdGl2YXRlZFJvdXRlID0gbnVsbDtcbiAgICAgIHRoaXMuZGVhY3RpdmF0ZUV2ZW50cy5lbWl0KGMpO1xuICAgIH1cbiAgfVxuXG4gIGFjdGl2YXRlV2l0aChhY3RpdmF0ZWRSb3V0ZTogQWN0aXZhdGVkUm91dGUsIGVudmlyb25tZW50SW5qZWN0b3I6IEVudmlyb25tZW50SW5qZWN0b3IgfCBudWxsKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuaXNBY3RpdmF0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IGFjdGl2YXRlIGFuIGFscmVhZHkgYWN0aXZhdGVkIG91dGxldCcpO1xuICAgIH1cbiAgICB0aGlzLl9hY3RpdmF0ZWRSb3V0ZSA9IGFjdGl2YXRlZFJvdXRlO1xuXG4gICAgbGV0IGNtcFJlZjogYW55O1xuICAgIGxldCBlbnRlcmluZ1ZpZXcgPSB0aGlzLnN0YWNrQ3RybC5nZXRFeGlzdGluZ1ZpZXcoYWN0aXZhdGVkUm91dGUpO1xuICAgIGlmIChlbnRlcmluZ1ZpZXcpIHtcbiAgICAgIGNtcFJlZiA9IHRoaXMuYWN0aXZhdGVkID0gZW50ZXJpbmdWaWV3LnJlZjtcbiAgICAgIGNvbnN0IHNhdmVkID0gZW50ZXJpbmdWaWV3LnNhdmVkRGF0YTtcbiAgICAgIGlmIChzYXZlZCkge1xuICAgICAgICAv