UNPKG

@angular/core

Version:

Angular - the core framework

569 lines 78.4 kB
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { ENVIRONMENT_INITIALIZER } from '../../di/initializer_token'; import { getInjectorDef } from '../../di/interface/defs'; import { INJECTOR_DEF_TYPES } from '../../di/internal_tokens'; import { NullInjector } from '../../di/null_injector'; import { walkProviderTree } from '../../di/provider_collection'; import { EnvironmentInjector, R3Injector } from '../../di/r3_injector'; import { NgModuleRef as viewEngine_NgModuleRef } from '../../linker/ng_module_factory'; import { deepForEach } from '../../util/array_utils'; import { throwError } from '../../util/assert'; import { assertTNode, assertTNodeForLView } from '../assert'; import { ChainedInjector } from '../chained_injector'; import { getFrameworkDIDebugData } from '../debug/framework_injector_profiler'; import { getComponentDef } from '../definition'; import { getNodeInjectorLView, getNodeInjectorTNode, getParentInjectorLocation, NodeInjector, } from '../di'; import { INJECTOR, TVIEW } from '../interfaces/view'; import { getParentInjectorIndex, getParentInjectorView, hasParentInjector, isRouterOutletInjector, } from './injector_utils'; import { getNativeByTNode } from './view_utils'; /** * Discovers the dependencies of an injectable instance. Provides DI information about each * dependency that the injectable was instantiated with, including where they were provided from. * * @param injector An injector instance * @param token a DI token that was constructed by the given injector instance * @returns an object that contains the created instance of token as well as all of the dependencies * that it was instantiated with OR undefined if the token was not created within the given * injector. */ export function getDependenciesFromInjectable(injector, token) { // First we check to see if the token given maps to an actual instance in the injector given. // We use `self: true` because we only want to look at the injector we were given. // We use `optional: true` because it's possible that the token we were given was never // constructed by the injector we were given. const instance = injector.get(token, null, { self: true, optional: true }); if (instance === null) { throw new Error(`Unable to determine instance of ${token} in given injector`); } const unformattedDependencies = getDependenciesForTokenInInjector(token, injector); const resolutionPath = getInjectorResolutionPath(injector); const dependencies = unformattedDependencies.map((dep) => { // injectedIn contains private fields, so we omit it from the response const formattedDependency = { value: dep.value, }; // convert injection flags to booleans const flags = dep.flags; formattedDependency.flags = { optional: (8 /* InternalInjectFlags.Optional */ & flags) === 8 /* InternalInjectFlags.Optional */, host: (1 /* InternalInjectFlags.Host */ & flags) === 1 /* InternalInjectFlags.Host */, self: (2 /* InternalInjectFlags.Self */ & flags) === 2 /* InternalInjectFlags.Self */, skipSelf: (4 /* InternalInjectFlags.SkipSelf */ & flags) === 4 /* InternalInjectFlags.SkipSelf */, }; // find the injector that provided the dependency for (let i = 0; i < resolutionPath.length; i++) { const injectorToCheck = resolutionPath[i]; // if skipSelf is true we skip the first injector if (i === 0 && formattedDependency.flags.skipSelf) { continue; } // host only applies to NodeInjectors if (formattedDependency.flags.host && injectorToCheck instanceof EnvironmentInjector) { break; } const instance = injectorToCheck.get(dep.token, null, { self: true, optional: true, }); if (instance !== null) { // if host flag is true we double check that we can get the service from the first element // in the resolution path by using the host flag. This is done to make sure that we've found // the correct providing injector, and not a node injector that is connected to our path via // a router outlet. if (formattedDependency.flags.host) { const firstInjector = resolutionPath[0]; const lookupFromFirstInjector = firstInjector.get(dep.token, null, { ...formattedDependency.flags, optional: true, }); if (lookupFromFirstInjector !== null) { formattedDependency.providedIn = injectorToCheck; } break; } formattedDependency.providedIn = injectorToCheck; break; } // if self is true we stop after the first injector if (i === 0 && formattedDependency.flags.self) { break; } } if (dep.token) formattedDependency.token = dep.token; return formattedDependency; }); return { instance, dependencies }; } function getDependenciesForTokenInInjector(token, injector) { const { resolverToTokenToDependencies } = getFrameworkDIDebugData(); if (!(injector instanceof NodeInjector)) { return resolverToTokenToDependencies.get(injector)?.get?.(token) ?? []; } const lView = getNodeInjectorLView(injector); const tokenDependencyMap = resolverToTokenToDependencies.get(lView); const dependencies = tokenDependencyMap?.get(token) ?? []; // In the NodeInjector case, all injections for every node are stored in the same lView. // We use the injectedIn field of the dependency to filter out the dependencies that // do not come from the same node as the instance we're looking at. return dependencies.filter((dependency) => { const dependencyNode = dependency.injectedIn?.tNode; if (dependencyNode === undefined) { return false; } const instanceNode = getNodeInjectorTNode(injector); assertTNode(dependencyNode); assertTNode(instanceNode); return dependencyNode === instanceNode; }); } /** * Gets the class associated with an injector that contains a provider `imports` array in it's * definition * * For Module Injectors this returns the NgModule constructor. * * For Standalone injectors this returns the standalone component constructor. * * @param injector Injector an injector instance * @returns the constructor where the `imports` array that configures this injector is located */ function getProviderImportsContainer(injector) { const { standaloneInjectorToComponent } = getFrameworkDIDebugData(); // standalone components configure providers through a component def, so we have to // use the standalone component associated with this injector if Injector represents // a standalone components EnvironmentInjector if (standaloneInjectorToComponent.has(injector)) { return standaloneInjectorToComponent.get(injector); } // Module injectors configure providers through their NgModule def, so we use the // injector to lookup its NgModuleRef and through that grab its instance const defTypeRef = injector.get(viewEngine_NgModuleRef, null, { self: true, optional: true }); // If we can't find an associated imports container, return null. // This could be the case if this function is called with an R3Injector that does not represent // a standalone component or NgModule. if (defTypeRef === null) { return null; } // In standalone applications, the root environment injector created by bootstrapApplication // may have no associated "instance". if (defTypeRef.instance === null) { return null; } return defTypeRef.instance.constructor; } /** * Gets the providers configured on a NodeInjector * * @param injector A NodeInjector instance * @returns ProviderRecord[] an array of objects representing the providers configured on this * injector */ function getNodeInjectorProviders(injector) { const diResolver = getNodeInjectorTNode(injector); const { resolverToProviders } = getFrameworkDIDebugData(); return resolverToProviders.get(diResolver) ?? []; } /** * Gets a mapping of providers configured on an injector to their import paths * * ModuleA -> imports ModuleB * ModuleB -> imports ModuleC * ModuleB -> provides MyServiceA * ModuleC -> provides MyServiceB * * getProviderImportPaths(ModuleA) * > Map(2) { * MyServiceA => [ModuleA, ModuleB] * MyServiceB => [ModuleA, ModuleB, ModuleC] * } * * @param providerImportsContainer constructor of class that contains an `imports` array in it's * definition * @returns A Map object that maps providers to an array of constructors representing it's import * path * */ function getProviderImportPaths(providerImportsContainer) { const providerToPath = new Map(); const visitedContainers = new Set(); const visitor = walkProviderTreeToDiscoverImportPaths(providerToPath, visitedContainers); walkProviderTree(providerImportsContainer, visitor, [], new Set()); return providerToPath; } /** * * Higher order function that returns a visitor for WalkProviderTree * * Takes in a Map and Set to keep track of the providers and containers * visited, so that we can discover the import paths of these providers * during the traversal. * * This visitor takes advantage of the fact that walkProviderTree performs a * postorder traversal of the provider tree for the passed in container. Because postorder * traversal recursively processes subtrees from leaf nodes until the traversal reaches the root, * we write a visitor that constructs provider import paths in reverse. * * * We use the visitedContainers set defined outside this visitor * because we want to run some logic only once for * each container in the tree. That logic can be described as: * * * 1. for each discovered_provider and discovered_path in the incomplete provider paths we've * already discovered * 2. get the first container in discovered_path * 3. if that first container is in the imports array of the container we're visiting * Then the container we're visiting is also in the import path of discovered_provider, so we * unshift discovered_path with the container we're currently visiting * * * Example Run: * ``` * ┌──────────┐ * │containerA│ * ┌─imports-─┤ ├──imports─┐ * │ │ provA │ │ * │ │ provB │ │ * │ └──────────┘ │ * │ │ * ┌▼─────────┐ ┌────────▼─┐ * │containerB│ │containerC│ * │ │ │ │ * │ provD │ │ provF │ * │ provE │ │ provG │ * └──────────┘ └──────────┘ * ``` * * Each step of the traversal, * * ``` * visitor(provD, containerB) * providerToPath === Map { provD => [containerB] } * visitedContainers === Set { containerB } * * visitor(provE, containerB) * providerToPath === Map { provD => [containerB], provE => [containerB] } * visitedContainers === Set { containerB } * * visitor(provF, containerC) * providerToPath === Map { provD => [containerB], provE => [containerB], provF => [containerC] } * visitedContainers === Set { containerB, containerC } * * visitor(provG, containerC) * providerToPath === Map { * provD => [containerB], provE => [containerB], provF => [containerC], provG => [containerC] * } * visitedContainers === Set { containerB, containerC } * * visitor(provA, containerA) * providerToPath === Map { * provD => [containerA, containerB], * provE => [containerA, containerB], * provF => [containerA, containerC], * provG => [containerA, containerC], * provA => [containerA] * } * visitedContainers === Set { containerB, containerC, containerA } * * visitor(provB, containerA) * providerToPath === Map { * provD => [containerA, containerB], * provE => [containerA, containerB], * provF => [containerA, containerC], * provG => [containerA, containerC], * provA => [containerA] * provB => [containerA] * } * visitedContainers === Set { containerB, containerC, containerA } * ``` * * @param providerToPath Map map of providers to paths that this function fills * @param visitedContainers Set a set to keep track of the containers we've already visited * @return function(provider SingleProvider, container: Type<unknown> | InjectorType<unknown>) => * void */ function walkProviderTreeToDiscoverImportPaths(providerToPath, visitedContainers) { return (provider, container) => { // If the provider is not already in the providerToPath map, // add an entry with the provider as the key and an array containing the current container as // the value if (!providerToPath.has(provider)) { providerToPath.set(provider, [container]); } // This block will run exactly once for each container in the import tree. // This is where we run the logic to check the imports array of the current // container to see if it's the next container in the path for our currently // discovered providers. if (!visitedContainers.has(container)) { // Iterate through the providers we've already seen for (const prov of providerToPath.keys()) { const existingImportPath = providerToPath.get(prov); let containerDef = getInjectorDef(container); if (!containerDef) { const ngModule = container.ngModule; containerDef = getInjectorDef(ngModule); } if (!containerDef) { return; } const lastContainerAddedToPath = existingImportPath[0]; let isNextStepInPath = false; deepForEach(containerDef.imports, (moduleImport) => { if (isNextStepInPath) { return; } isNextStepInPath = moduleImport.ngModule === lastContainerAddedToPath || moduleImport === lastContainerAddedToPath; if (isNextStepInPath) { providerToPath.get(prov)?.unshift(container); } }); } } visitedContainers.add(container); }; } /** * Gets the providers configured on an EnvironmentInjector * * @param injector EnvironmentInjector * @returns an array of objects representing the providers of the given injector */ function getEnvironmentInjectorProviders(injector) { const providerRecordsWithoutImportPaths = getFrameworkDIDebugData().resolverToProviders.get(injector) ?? []; // platform injector has no provider imports container so can we skip trying to // find import paths if (isPlatformInjector(injector)) { return providerRecordsWithoutImportPaths; } const providerImportsContainer = getProviderImportsContainer(injector); if (providerImportsContainer === null) { // We assume that if an environment injector exists without an associated provider imports // container, it was created without such a container. Some examples cases where this could // happen: // - The root injector of a standalone application // - A router injector created by using the providers array in a lazy loaded route // - A manually created injector that is attached to the injector tree // Since each of these cases has no provider container, there is no concept of import paths, // so we can simply return the provider records. return providerRecordsWithoutImportPaths; } const providerToPath = getProviderImportPaths(providerImportsContainer); const providerRecords = []; for (const providerRecord of providerRecordsWithoutImportPaths) { const provider = providerRecord.provider; // Ignore these special providers for now until we have a cleaner way of // determing when they are provided by the framework vs provided by the user. const token = provider.provide; if (token === ENVIRONMENT_INITIALIZER || token === INJECTOR_DEF_TYPES) { continue; } let importPath = providerToPath.get(provider) ?? []; const def = getComponentDef(providerImportsContainer); const isStandaloneComponent = !!def?.standalone; // We prepend the component constructor in the standalone case // because walkProviderTree does not visit this constructor during it's traversal if (isStandaloneComponent) { importPath = [providerImportsContainer, ...importPath]; } providerRecords.push({ ...providerRecord, importPath }); } return providerRecords; } function isPlatformInjector(injector) { return injector instanceof R3Injector && injector.scopes.has('platform'); } /** * Gets the providers configured on an injector. * * @param injector the injector to lookup the providers of * @returns ProviderRecord[] an array of objects representing the providers of the given injector */ export function getInjectorProviders(injector) { if (injector instanceof NodeInjector) { return getNodeInjectorProviders(injector); } else if (injector instanceof EnvironmentInjector) { return getEnvironmentInjectorProviders(injector); } throwError('getInjectorProviders only supports NodeInjector and EnvironmentInjector'); } /** * * Given an injector, this function will return * an object containing the type and source of the injector. * * | | type | source | * |--------------|-------------|-------------------------------------------------------------| * | NodeInjector | element | DOM element that created this injector | * | R3Injector | environment | `injector.source` | * | NullInjector | null | null | * * @param injector the Injector to get metadata for * @returns an object containing the type and source of the given injector. If the injector metadata * cannot be determined, returns null. */ export function getInjectorMetadata(injector) { if (injector instanceof NodeInjector) { const lView = getNodeInjectorLView(injector); const tNode = getNodeInjectorTNode(injector); assertTNodeForLView(tNode, lView); return { type: 'element', source: getNativeByTNode(tNode, lView) }; } if (injector instanceof R3Injector) { return { type: 'environment', source: injector.source ?? null }; } if (injector instanceof NullInjector) { return { type: 'null', source: null }; } return null; } export function getInjectorResolutionPath(injector) { const resolutionPath = [injector]; getInjectorResolutionPathHelper(injector, resolutionPath); return resolutionPath; } function getInjectorResolutionPathHelper(injector, resolutionPath) { const parent = getInjectorParent(injector); // if getInjectorParent can't find a parent, then we've either reached the end // of the path, or we need to move from the Element Injector tree to the // module injector tree using the first injector in our path as the connection point. if (parent === null) { if (injector instanceof NodeInjector) { const firstInjector = resolutionPath[0]; if (firstInjector instanceof NodeInjector) { const moduleInjector = getModuleInjectorOfNodeInjector(firstInjector); if (moduleInjector === null) { throwError('NodeInjector must have some connection to the module injector tree'); } resolutionPath.push(moduleInjector); getInjectorResolutionPathHelper(moduleInjector, resolutionPath); } return resolutionPath; } } else { resolutionPath.push(parent); getInjectorResolutionPathHelper(parent, resolutionPath); } return resolutionPath; } /** * Gets the parent of an injector. * * This function is not able to make the jump from the Element Injector Tree to the Module * injector tree. This is because the "parent" (the next step in the reoslution path) * of a root NodeInjector is dependent on which NodeInjector ancestor initiated * the DI lookup. See getInjectorResolutionPath for a function that can make this jump. * * In the below diagram: * ```ts * getInjectorParent(NodeInjectorB) * > NodeInjectorA * getInjectorParent(NodeInjectorA) // or getInjectorParent(getInjectorParent(NodeInjectorB)) * > null // cannot jump to ModuleInjector tree * ``` * * ``` * ┌───────┐ ┌───────────────────┐ * ┌───────────┤ModuleA├───Injector────►│EnvironmentInjector│ * │ └───┬───┘ └───────────────────┘ * │ │ * │ bootstraps * │ │ * │ │ * │ ┌────▼─────┐ ┌─────────────┐ * declares │ComponentA├────Injector────►│NodeInjectorA│ * │ └────┬─────┘ └─────▲───────┘ * │ │ │ * │ renders parent * │ │ │ * │ ┌────▼─────┐ ┌─────┴───────┐ * └─────────►│ComponentB├────Injector────►│NodeInjectorB│ * └──────────┘ └─────────────┘ *``` * * @param injector an Injector to get the parent of * @returns Injector the parent of the given injector */ function getInjectorParent(injector) { if (injector instanceof R3Injector) { const parent = injector.parent; if (isRouterOutletInjector(parent)) { // This is a special case for a `ChainedInjector` instance, which represents // a combination of a Router's `OutletInjector` and an EnvironmentInjector, // which represents a `@defer` block. Since the `OutletInjector` doesn't store // any tokens itself, we point to the parent injector instead. See the // `OutletInjector.__ngOutletInjector` field for additional information. return parent.parentInjector; } return parent; } let tNode; let lView; if (injector instanceof NodeInjector) { tNode = getNodeInjectorTNode(injector); lView = getNodeInjectorLView(injector); } else if (injector instanceof NullInjector) { return null; } else if (injector instanceof ChainedInjector) { return injector.parentInjector; } else { throwError('getInjectorParent only support injectors of type R3Injector, NodeInjector, NullInjector'); } const parentLocation = getParentInjectorLocation(tNode, lView); if (hasParentInjector(parentLocation)) { const parentInjectorIndex = getParentInjectorIndex(parentLocation); const parentLView = getParentInjectorView(parentLocation, lView); const parentTView = parentLView[TVIEW]; const parentTNode = parentTView.data[parentInjectorIndex + 8 /* NodeInjectorOffset.TNODE */]; return new NodeInjector(parentTNode, parentLView); } else { const chainedInjector = lView[INJECTOR]; // Case where chainedInjector.injector is an OutletInjector and chainedInjector.injector.parent // is a NodeInjector. // todo(aleksanderbodurri): ideally nothing in packages/core should deal // directly with router concerns. Refactor this so that we can make the jump from // NodeInjector -> OutletInjector -> NodeInjector // without explicitly relying on types contracts from packages/router const injectorParent = chainedInjector.injector?.parent; if (injectorParent instanceof NodeInjector) { return injectorParent; } } return null; } /** * Gets the module injector of a NodeInjector. * * @param injector NodeInjector to get module injector of * @returns Injector representing module injector of the given NodeInjector */ function getModuleInjectorOfNodeInjector(injector) { let lView; if (injector instanceof NodeInjector) { lView = getNodeInjectorLView(injector); } else { throwError('getModuleInjectorOfNodeInjector must be called with a NodeInjector'); } const inj = lView[INJECTOR]; const moduleInjector = inj instanceof ChainedInjector ? inj.parentInjector : inj.parent; if (!moduleInjector) { throwError('NodeInjector must have some connection to the module injector tree'); } return moduleInjector; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5qZWN0b3JfZGlzY292ZXJ5X3V0aWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vcGFja2FnZXMvY29yZS9zcmMvcmVuZGVyMy91dGlsL2luamVjdG9yX2Rpc2NvdmVyeV91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFFSCxPQUFPLEVBQUMsdUJBQXVCLEVBQUMsTUFBTSw0QkFBNEIsQ0FBQztBQUduRSxPQUFPLEVBQUMsY0FBYyxFQUFlLE1BQU0seUJBQXlCLENBQUM7QUFHckUsT0FBTyxFQUFDLGtCQUFrQixFQUFDLE1BQU0sMEJBQTBCLENBQUM7QUFDNUQsT0FBTyxFQUFDLFlBQVksRUFBQyxNQUFNLHdCQUF3QixDQUFDO0FBQ3BELE9BQU8sRUFBaUIsZ0JBQWdCLEVBQUMsTUFBTSw4QkFBOEIsQ0FBQztBQUM5RSxPQUFPLEVBQUMsbUJBQW1CLEVBQUUsVUFBVSxFQUFDLE1BQU0sc0JBQXNCLENBQUM7QUFFckUsT0FBTyxFQUFDLFdBQVcsSUFBSSxzQkFBc0IsRUFBQyxNQUFNLGdDQUFnQyxDQUFDO0FBQ3JGLE9BQU8sRUFBQyxXQUFXLEVBQUMsTUFBTSx3QkFBd0IsQ0FBQztBQUNuRCxPQUFPLEVBQUMsVUFBVSxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFDN0MsT0FBTyxFQUFDLFdBQVcsRUFBRSxtQkFBbUIsRUFBQyxNQUFNLFdBQVcsQ0FBQztBQUMzRCxPQUFPLEVBQUMsZUFBZSxFQUFDLE1BQU0scUJBQXFCLENBQUM7QUFDcEQsT0FBTyxFQUFDLHVCQUF1QixFQUFDLE1BQU0sc0NBQXNDLENBQUM7QUFFN0UsT0FBTyxFQUFDLGVBQWUsRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUM5QyxPQUFPLEVBQ0wsb0JBQW9CLEVBQ3BCLG9CQUFvQixFQUNwQix5QkFBeUIsRUFDekIsWUFBWSxHQUNiLE1BQU0sT0FBTyxDQUFDO0FBSWYsT0FBTyxFQUFDLFFBQVEsRUFBUyxLQUFLLEVBQUMsTUFBTSxvQkFBb0IsQ0FBQztBQUUxRCxPQUFPLEVBQ0wsc0JBQXNCLEVBQ3RCLHFCQUFxQixFQUNyQixpQkFBaUIsRUFDakIsc0JBQXNCLEdBQ3ZCLE1BQU0sa0JBQWtCLENBQUM7QUFDMUIsT0FBTyxFQUFDLGdCQUFnQixFQUFDLE1BQU0sY0FBYyxDQUFDO0FBRTlDOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sVUFBVSw2QkFBNkIsQ0FDM0MsUUFBa0IsRUFDbEIsS0FBa0M7SUFFbEMsNkZBQTZGO0lBQzdGLGtGQUFrRjtJQUNsRix1RkFBdUY7SUFDdkYsNkNBQTZDO0lBQzdDLE1BQU0sUUFBUSxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxFQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBQyxDQUFDLENBQUM7SUFDekUsSUFBSSxRQUFRLEtBQUssSUFBSSxFQUFFLENBQUM7UUFDdEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxtQ0FBbUMsS0FBSyxvQkFBb0IsQ0FBQyxDQUFDO0lBQ2hGLENBQUM7SUFFRCxNQUFNLHVCQUF1QixHQUFHLGlDQUFpQyxDQUFDLEtBQUssRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNuRixNQUFNLGNBQWMsR0FBRyx5QkFBeUIsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUUzRCxNQUFNLFlBQVksR0FBRyx1QkFBdUIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRTtRQUN2RCxzRUFBc0U7UUFDdEUsTUFBTSxtQkFBbUIsR0FBd0M7WUFDL0QsS0FBSyxFQUFFLEdBQUcsQ0FBQyxLQUFLO1NBQ2pCLENBQUM7UUFFRixzQ0FBc0M7UUFDdEMsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLEtBQTRCLENBQUM7UUFDL0MsbUJBQW1CLENBQUMsS0FBSyxHQUFHO1lBQzFCLFFBQVEsRUFBRSxDQUFDLHVDQUErQixLQUFLLENBQUMseUNBQWlDO1lBQ2pGLElBQUksRUFBRSxDQUFDLG1DQUEyQixLQUFLLENBQUMscUNBQTZCO1lBQ3JFLElBQUksRUFBRSxDQUFDLG1DQUEyQixLQUFLLENBQUMscUNBQTZCO1lBQ3JFLFFBQVEsRUFBRSxDQUFDLHVDQUErQixLQUFLLENBQUMseUNBQWlDO1NBQ2xGLENBQUM7UUFFRixpREFBaUQ7UUFDakQsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLGNBQWMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUMvQyxNQUFNLGVBQWUsR0FBRyxjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFFMUMsaURBQWlEO1lBQ2pELElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxtQkFBbUIsQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUM7Z0JBQ2xELFNBQVM7WUFDWCxDQUFDO1lBRUQscUNBQXFDO1lBQ3JDLElBQUksbUJBQW1CLENBQUMsS0FBSyxDQUFDLElBQUksSUFBSSxlQUFlLFlBQVksbUJBQW1CLEVBQUUsQ0FBQztnQkFDckYsTUFBTTtZQUNSLENBQUM7WUFFRCxNQUFNLFFBQVEsR0FBRyxlQUFlLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxLQUFzQixFQUFFLElBQUksRUFBRTtnQkFDckUsSUFBSSxFQUFFLElBQUk7Z0JBQ1YsUUFBUSxFQUFFLElBQUk7YUFDZixDQUFDLENBQUM7WUFFSCxJQUFJLFFBQVEsS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDdEIsMEZBQTBGO2dCQUMxRiw0RkFBNEY7Z0JBQzVGLDRGQUE0RjtnQkFDNUYsbUJBQW1CO2dCQUNuQixJQUFJLG1CQUFtQixDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztvQkFDbkMsTUFBTSxhQUFhLEdBQUcsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDO29CQUN4QyxNQUFNLHVCQUF1QixHQUFHLGFBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEtBQXNCLEVBQUUsSUFBSSxFQUFFO3dCQUNsRixHQUFHLG1CQUFtQixDQUFDLEtBQUs7d0JBQzVCLFFBQVEsRUFBRSxJQUFJO3FCQUNmLENBQUMsQ0FBQztvQkFFSCxJQUFJLHVCQUF1QixLQUFLLElBQUksRUFBRSxDQUFDO3dCQUNyQyxtQkFBbUIsQ0FBQyxVQUFVLEdBQUcsZUFBZSxDQUFDO29CQUNuRCxDQUFDO29CQUVELE1BQU07Z0JBQ1IsQ0FBQztnQkFFRCxtQkFBbUIsQ0FBQyxVQUFVLEdBQUcsZUFBZSxDQUFDO2dCQUNqRCxNQUFNO1lBQ1IsQ0FBQztZQUVELG1EQUFtRDtZQUNuRCxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksbUJBQW1CLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUM5QyxNQUFNO1lBQ1IsQ0FBQztRQUNILENBQUM7UUFFRCxJQUFJLEdBQUcsQ0FBQyxLQUFLO1lBQUUsbUJBQW1CLENBQUMsS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUM7UUFFckQsT0FBTyxtQkFBbUIsQ0FBQztJQUM3QixDQUFDLENBQUMsQ0FBQztJQUVILE9BQU8sRUFBQyxRQUFRLEVBQUUsWUFBWSxFQUFDLENBQUM7QUFDbEMsQ0FBQztBQUVELFNBQVMsaUNBQWlDLENBQ3hDLEtBQWtDLEVBQ2xDLFFBQWtCO0lBRWxCLE1BQU0sRUFBQyw2QkFBNkIsRUFBQyxHQUFHLHVCQUF1QixFQUFFLENBQUM7SUFFbEUsSUFBSSxDQUFDLENBQUMsUUFBUSxZQUFZLFlBQVksQ0FBQyxFQUFFLENBQUM7UUFDeEMsT0FBTyw2QkFBNkIsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsS0FBZ0IsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNwRixDQUFDO0lBRUQsTUFBTSxLQUFLLEdBQUcsb0JBQW9CLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDN0MsTUFBTSxrQkFBa0IsR0FBRyw2QkFBNkIsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDcEUsTUFBTSxZQUFZLEdBQUcsa0JBQWtCLEVBQUUsR0FBRyxDQUFDLEtBQWdCLENBQUMsSUFBSSxFQUFFLENBQUM7SUFFckUsd0ZBQXdGO0lBQ3hGLG9GQUFvRjtJQUNwRixtRUFBbUU7SUFDbkUsT0FBTyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsVUFBVSxFQUFFLEVBQUU7UUFDeEMsTUFBTSxjQUFjLEdBQUcsVUFBVSxDQUFDLFVBQVUsRUFBRSxLQUFLLENBQUM7UUFDcEQsSUFBSSxjQUFjLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDakMsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBRUQsTUFBTSxZQUFZLEdBQUcsb0JBQW9CLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDcEQsV0FBVyxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQzVCLFdBQVcsQ0FBQyxZQUFhLENBQUMsQ0FBQztRQUUzQixPQUFPLGNBQWMsS0FBSyxZQUFZLENBQUM7SUFDekMsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7R0FVRztBQUNILFNBQVMsMkJBQTJCLENBQUMsUUFBa0I7SUFDckQsTUFBTSxFQUFDLDZCQUE2QixFQUFDLEdBQUcsdUJBQXVCLEVBQUUsQ0FBQztJQUVsRSxtRkFBbUY7SUFDbkYsb0ZBQW9GO0lBQ3BGLDhDQUE4QztJQUM5QyxJQUFJLDZCQUE2QixDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1FBQ2hELE9BQU8sNkJBQTZCLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBRSxDQUFDO0lBQ3RELENBQUM7SUFFRCxpRkFBaUY7SUFDakYsd0VBQXdFO0lBQ3hFLE1BQU0sVUFBVSxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsc0JBQXNCLEVBQUUsSUFBSSxFQUFFLEVBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFDLENBQUUsQ0FBQztJQUU3RixpRUFBaUU7SUFDakUsK0ZBQStGO0lBQy9GLHNDQUFzQztJQUN0QyxJQUFJLFVBQVUsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUN4QixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCw0RkFBNEY7SUFDNUYscUNBQXFDO0lBQ3JDLElBQUksVUFBVSxDQUFDLFFBQVEsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUNqQyxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCxPQUFPLFVBQVUsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDO0FBQ3pDLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxTQUFTLHdCQUF3QixDQUFDLFFBQXNCO0lBQ3RELE1BQU0sVUFBVSxHQUFHLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ2xELE1BQU0sRUFBQyxtQkFBbUIsRUFBQyxHQUFHLHVCQUF1QixFQUFFLENBQUM7SUFDeEQsT0FBTyxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsVUFBbUIsQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUM1RCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQkc7QUFDSCxTQUFTLHNCQUFzQixDQUM3Qix3QkFBdUM7SUFFdkMsTUFBTSxjQUFjLEdBQUcsSUFBSSxHQUFHLEVBQTZELENBQUM7SUFDNUYsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsRUFBaUIsQ0FBQztJQUNuRCxNQUFNLE9BQU8sR0FBRyxxQ0FBcUMsQ0FBQyxjQUFjLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztJQUV6RixnQkFBZ0IsQ0FBQyx3QkFBd0IsRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFLElBQUksR0FBRyxFQUFFLENBQUMsQ0FBQztJQUVuRSxPQUFPLGNBQWMsQ0FBQztBQUN4QixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EyRkc7QUFDSCxTQUFTLHFDQUFxQyxDQUM1QyxjQUE4RSxFQUM5RSxpQkFBcUM7SUFFckMsT0FBTyxDQUFDLFFBQXdCLEVBQUUsU0FBZ0QsRUFBRSxFQUFFO1FBQ3BGLDREQUE0RDtRQUM1RCw2RkFBNkY7UUFDN0YsWUFBWTtRQUNaLElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDbEMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO1FBQzVDLENBQUM7UUFFRCwwRUFBMEU7UUFDMUUsMkVBQTJFO1FBQzNFLDRFQUE0RTtRQUM1RSx3QkFBd0I7UUFDeEIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDO1lBQ3RDLG1EQUFtRDtZQUNuRCxLQUFLLE1BQU0sSUFBSSxJQUFJLGNBQWMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDO2dCQUN6QyxNQUFNLGtCQUFrQixHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFFLENBQUM7Z0JBRXJELElBQUksWUFBWSxHQUFHLGNBQWMsQ0FBQyxTQUFTLENBQUMsQ0FBQztnQkFDN0MsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO29CQUNsQixNQUFNLFFBQVEsR0FBK0IsU0FBaUIsQ0FBQyxRQUVsRCxDQUFDO29CQUNkLFlBQVksR0FBRyxjQUFjLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQzFDLENBQUM7Z0JBRUQsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO29CQUNsQixPQUFPO2dCQUNULENBQUM7Z0JBRUQsTUFBTSx3QkFBd0IsR0FBRyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFFdkQsSUFBSSxnQkFBZ0IsR0FBRyxLQUFLLENBQUM7Z0JBQzdCLFdBQVcsQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLENBQUMsWUFBWSxFQUFFLEVBQUU7b0JBQ2pELElBQUksZ0JBQWdCLEVBQUUsQ0FBQzt3QkFDckIsT0FBTztvQkFDVCxDQUFDO29CQUVELGdCQUFnQjt3QkFDYixZQUFvQixDQUFDLFFBQVEsS0FBSyx3QkFBd0I7NEJBQzNELFlBQVksS0FBSyx3QkFBd0IsQ0FBQztvQkFFNUMsSUFBSSxnQkFBZ0IsRUFBRSxDQUFDO3dCQUNyQixjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQztvQkFDL0MsQ0FBQztnQkFDSCxDQUFDLENBQUMsQ0FBQztZQUNMLENBQUM7UUFDSCxDQUFDO1FBRUQsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ25DLENBQUMsQ0FBQztBQUNKLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILFNBQVMsK0JBQStCLENBQUMsUUFBNkI7SUFDcEUsTUFBTSxpQ0FBaUMsR0FDckMsdUJBQXVCLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO0lBRXBFLCtFQUErRTtJQUMvRSxvQkFBb0I7SUFDcEIsSUFBSSxrQkFBa0IsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1FBQ2pDLE9BQU8saUNBQWlDLENBQUM7SUFDM0MsQ0FBQztJQUVELE1BQU0sd0JBQXdCLEdBQUcsMkJBQTJCLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDdkUsSUFBSSx3QkFBd0IsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUN0QywwRkFBMEY7UUFDMUYsMkZBQTJGO1FBQzNGLFVBQVU7UUFDVixrREFBa0Q7UUFDbEQsa0ZBQWtGO1FBQ2xGLHNFQUFzRTtRQUN0RSw0RkFBNEY7UUFDNUYsZ0RBQWdEO1FBQ2hELE9BQU8saUNBQWlDLENBQUM7SUFDM0MsQ0FBQztJQUVELE1BQU0sY0FBYyxHQUFHLHNCQUFzQixDQUFDLHdCQUF3QixDQUFDLENBQUM7SUFDeEUsTUFBTSxlQUFlLEdBQUcsRUFBRSxDQUFDO0lBRTNCLEtBQUssTUFBTSxjQUFjLElBQUksaUNBQWlDLEVBQUUsQ0FBQztRQUMvRCxNQUFNLFFBQVEsR0FBRyxjQUFjLENBQUMsUUFBUSxDQUFDO1FBQ3pDLHdFQUF3RTtRQUN4RSw2RUFBNkU7UUFDN0UsTUFBTSxLQUFLLEdBQUksUUFBMEIsQ0FBQyxPQUFPLENBQUM7UUFDbEQsSUFBSSxLQUFLLEtBQUssdUJBQXVCLElBQUksS0FBSyxLQUFLLGtCQUFrQixFQUFFLENBQUM7WUFDdEUsU0FBUztRQUNYLENBQUM7UUFFRCxJQUFJLFVBQVUsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUVwRCxNQUFNLEdBQUcsR0FBRyxlQUFlLENBQUMsd0JBQXdCLENBQUMsQ0FBQztRQUN0RCxNQUFNLHFCQUFxQixHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsVUFBVSxDQUFDO1FBQ2hELDhEQUE4RDtRQUM5RCxpRkFBaUY7UUFDakYsSUFBSSxxQkFBcUIsRUFBRSxDQUFDO1lBQzFCLFVBQVUsR0FBRyxDQUFDLHdCQUF3QixFQUFFLEdBQUcsVUFBVSxDQUFDLENBQUM7UUFDekQsQ0FBQztRQUVELGVBQWUsQ0FBQyxJQUFJLENBQUMsRUFBQyxHQUFHLGNBQWMsRUFBRSxVQUFVLEVBQUMsQ0FBQyxDQUFDO0lBQ3hELENBQUM7SUFDRCxPQUFPLGVBQWUsQ0FBQztBQUN6QixDQUFDO0FBRUQsU0FBUyxrQkFBa0IsQ0FBQyxRQUFrQjtJQUM1QyxPQUFPLFFBQVEsWUFBWSxVQUFVLElBQUksUUFBUSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7QUFDM0UsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFDLFFBQWtCO0lBQ3JELElBQUksUUFBUSxZQUFZLFlBQVksRUFBRSxDQUFDO1FBQ3JDLE9BQU8sd0JBQXdCLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDNUMsQ0FBQztTQUFNLElBQUksUUFBUSxZQUFZLG1CQUFtQixFQUFFLENBQUM7UUFDbkQsT0FBTywrQkFBK0IsQ0FBQyxRQUErQixDQUFDLENBQUM7SUFDMUUsQ0FBQztJQUVELFVBQVUsQ0FBQyx5RUFBeUUsQ0FBQyxDQUFDO0FBQ3hGLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FDakMsUUFBa0I7SUFNbEIsSUFBSSxRQUFRLFlBQVksWUFBWSxFQUFFLENBQUM7UUFDckMsTUFBTSxLQUFLLEdBQUcsb0JBQW9CLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDN0MsTUFBTSxLQUFLLEdBQUcsb0JBQW9CLENBQUMsUUFBUSxDQUFFLENBQUM7UUFDOUMsbUJBQW1CLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBRWxDLE9BQU8sRUFBQyxJQUFJLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxnQkFBZ0IsQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFhLEVBQUMsQ0FBQztJQUMvRSxDQUFDO0lBRUQsSUFBSSxRQUFRLFlBQVksVUFBVSxFQUFFLENBQUM7UUFDbkMsT0FBTyxFQUFDLElBQUksRUFBRSxhQUFhLEVBQUUsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNLElBQUksSUFBSSxFQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVELElBQUksUUFBUSxZQUFZLFlBQVksRUFBRSxDQUFDO1FBQ3JDLE9BQU8sRUFBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUMsQ0FBQztJQUN0QyxDQUFDO0lBRUQsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDO0FBRUQsTUFBTSxVQUFVLHlCQUF5QixDQUFDLFFBQWtCO0lBQzFELE1BQU0sY0FBYyxHQUFlLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDOUMsK0JBQStCLENBQUMsUUFBUSxFQUFFLGNBQWMsQ0FBQyxDQUFDO0lBQzFELE9BQU8sY0FBYyxDQUFDO0FBQ3hCLENBQUM7QUFFRCxTQUFTLCtCQUErQixDQUN0QyxRQUFrQixFQUNsQixjQUEwQjtJQUUxQixNQUFNLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUUzQyw4RUFBOEU7SUFDOUUsd0VBQXdFO0lBQ3hFLHFGQUFxRjtJQUNyRixJQUFJLE1BQU0sS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUNwQixJQUFJLFFBQVEsWUFBWSxZQUFZLEVBQUUsQ0FBQztZQUNyQyxNQUFNLGFBQWEsR0FBRyxjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDeEMsSUFBSSxhQUFhLFlBQVksWUFBWSxFQUFFLENBQUM7Z0JBQzFDLE1BQU0sY0FBYyxHQUFHLCtCQUErQixDQUFDLGFBQWEsQ0FBQyxDQUFDO2dCQUN0RSxJQUFJLGNBQWMsS0FBSyxJQUFJLEVBQUUsQ0FBQztvQkFDNUIsVUFBVSxDQUFDLG9FQUFvRSxDQUFDLENBQUM7Z0JBQ25GLENBQUM7Z0JBRUQsY0FBYyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztnQkFDcEMsK0JBQStCLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxDQUFDO1lBQ2xFLENBQUM7WUFFRCxPQUFPLGNBQWMsQ0FBQztRQUN4QixDQUFDO0lBQ0gsQ0FBQztTQUFNLENBQUM7UUFDTixjQUFjLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzVCLCtCQUErQixDQUFDLE1BQU0sRUFBRSxjQUFjLENBQUMsQ0FBQztJQUMxRCxDQUFDO0lBRUQsT0FBTyxjQUFjLENBQUM7QUFDeEIsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBcUNHO0FBQ0gsU0FBUyxpQkFBaUIsQ0FBQyxRQUFrQjtJQUMzQyxJQUFJLFFBQVEsWUFBWSxVQUFVLEVBQUUsQ0FBQztRQUNuQyxNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsTUFBTSxDQUFDO1FBQy9CLElBQUksc0JBQXNCLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUNuQyw0RUFBNEU7WUFDNUUsMkVBQTJFO1lBQzNFLDhFQUE4RTtZQUM5RSxzRUFBc0U7WUFDdEUsd0VBQXdFO1lBQ3hFLE9BQVEsTUFBMEIsQ0FBQyxjQUFjLENBQUM7UUFDcEQsQ0FBQztRQUNELE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFFRCxJQUFJLEtBQW1FLENBQUM7SUFDeEUsSUFBSSxLQUFxQixDQUFDO0lBQzFCLElBQUksUUFBUSxZQUFZLFlBQVksRUFBRSxDQUFDO1FBQ3JDLEtBQUssR0FBRyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUN2QyxLQUFLLEdBQUcsb0JBQW9CLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDekMsQ0FBQztTQUFNLElBQUksUUFBUSxZQUFZLFlBQVksRUFBRSxDQUFDO1FBQzVDLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztTQUFNLElBQUksUUFBUSxZQUFZLGVBQWUsRUFBRSxDQUFDO1FBQy9DLE9BQU8sUUFBUSxDQUFDLGNBQWMsQ0FBQztJQUNqQyxDQUFDO1NBQU0sQ0FBQztRQUNOLFVBQVUsQ0FDUix5RkFBeUYsQ0FDMUYsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLGNBQWMsR0FBRyx5QkFBeUIsQ0FDOUMsS0FBOEQsRUFDOUQsS0FBSyxDQUNOLENBQUM7SUFFRixJQUFJLGlCQUFpQixDQUFDLGNBQWMsQ0FBQyxFQUFFLENBQUM7UUFDdEMsTUFBTSxtQkFBbUIsR0FBRyxzQkFBc0IsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUNuRSxNQUFNLFdBQVcsR0FBRyxxQkFBcUIsQ0FBQyxjQUFjLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDakUsTUFBTSxXQUFXLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3ZDLE1BQU0sV0FBVyxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLG1DQUEyQixDQUFVLENBQUM7UUFDOUYsT0FBTyxJQUFJLFlBQVksQ0FDckIsV0FBb0UsRUFDcEUsV0FBVyxDQUNaLENBQUM7SUFDSixDQUFDO1NBQU0sQ0FBQztRQUNOLE1BQU0sZUFBZSxHQUFHLEtBQUssQ0FBQyxRQUFRLENBQW9CLENBQUM7UUFFM0QsK0ZBQStGO1FBQy9GLHFCQUFxQjtRQUNyQix3RUFBd0U7UUFDeEUsaUZBQWlGO1FBQ2pGLGlEQUFpRDtRQUNqRCxxRUFBcUU7UUFDckUsTUFBTSxjQUFjLEdBQUksZUFBZSxDQUFDLFFBQWdCLEVBQUUsTUFBa0IsQ0FBQztRQUU3RSxJQUFJLGNBQWMsWUFBWSxZQUFZLEVBQUUsQ0FBQztZQUMzQyxPQUFPLGNBQWMsQ0FBQztRQUN4QixDQUFDO0lBQ0gsQ0FBQztJQUVELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUywrQkFBK0IsQ0FBQyxRQUFzQjtJQUM3RCxJQUFJLEtBQXFCLENBQUM7SUFDMUIsSUFBSSxRQUFRLFlBQVksWUFBWSxFQUFFLENBQUM7UUFDckMsS0FBSyxHQUFHLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3pDLENBQUM7U0FBTSxDQUFDO1FBQ04sVUFBVSxDQUFDLG9FQUFvRSxDQUFDLENBQUM7SUFDbkYsQ0FBQztJQUVELE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxRQUFRLENBQWlDLENBQUM7SUFDNUQsTUFBTSxjQUFjLEdBQUcsR0FBRyxZQUFZLGVBQWUsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQztJQUN4RixJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDcEIsVUFBVSxDQUFDLG9FQUFvRSxDQUFDLENBQUM7SUFDbkYsQ0FBQztJQUVELE9BQU8sY0FBYyxDQUFDO0FBQ3hCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgR29vZ2xlIExMQyBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGFuIE1JVC1zdHlsZSBsaWNlbnNlIHRoYXQgY2FuIGJlXG4gKiBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGF0IGh0dHBzOi8vYW5ndWxhci5pby9saWNlbnNlXG4gKi9cblxuaW1wb3J0IHtFTlZJUk9OTUVOVF9JTklUSUFMSVpFUn0gZnJvbSAnLi4vLi4vZGkvaW5pdGlhbGl6ZXJfdG9rZW4nO1xuaW1wb3J0IHtJbmplY3Rpb25Ub2tlbn0gZnJvbSAnLi4vLi4vZGkvaW5qZWN0aW9uX3Rva2VuJztcbmltcG9ydCB7SW5qZWN0b3J9IGZyb20gJy4uLy4uL2RpL2luamVjdG9yJztcbmltcG9ydCB7Z2V0SW5qZWN0b3JEZWYsIEluamVjdG9yVHlwZX0gZnJvbSAnLi4vLi4vZGkvaW50ZXJmYWNlL2RlZnMnO1xuaW1wb3J0IHtJbnRlcm5hbEluamVjdEZsYWdzfSBmcm9tICcuLi8uLi9kaS9pbnRlcmZhY2UvaW5qZWN0b3InO1xuaW1wb3J0IHtWYWx1ZVByb3ZpZGVyfSBmcm9tICcuLi8uLi9kaS9pbnRlcmZhY2UvcHJvdmlkZXInO1xuaW1wb3J0IHtJTkpFQ1RPUl9ERUZfVFlQRVN9IGZyb20gJy4uLy4uL2RpL2ludGVybmFsX3Rva2Vucyc7XG5pbXBvcnQge051bGxJbmplY3Rvcn0gZnJvbSAnLi4vLi4vZGkvbnVsbF9pbmplY3Rvcic7XG5pbXBvcnQge1NpbmdsZVByb3ZpZGVyLCB3YWxrUHJvdmlkZXJUcmVlfSBmcm9tICcuLi8uLi9kaS9wcm92aWRlcl9jb2xsZWN0aW9uJztcbmltcG9ydCB7RW52aXJvbm1lbnRJbmplY3RvciwgUjNJbmplY3Rvcn0gZnJvbSAnLi4vLi4vZGkvcjNfaW5qZWN0b3InO1xuaW1wb3J0IHtUeXBlfSBmcm9tICcuLi8uLi9pbnRlcmZhY2UvdHlwZSc7XG5pbXBvcnQge05nTW9kdWxlUmVmIGFzIHZpZXdFbmdpbmVfTmdNb2R1bGVSZWZ9IGZyb20gJy4uLy4uL2xpbmtlci9uZ19tb2R1bGVfZmFjdG9yeSc7XG5pbXBvcnQge2RlZXBGb3JFYWNofSBmcm9tICcuLi8uLi91dGlsL2FycmF5X3V0aWxzJztcbmltcG9ydCB7dGhyb3dFcnJvcn0gZnJvbSAnLi4vLi4vdXRpbC9hc3NlcnQnO1xuaW1wb3J0IHthc3NlcnRUTm9kZSwgYXNzZXJ0VE5vZGVGb3JMVmlld30gZnJvbSAnLi4vYXNzZXJ0JztcbmltcG9ydCB7Q2hhaW5lZEluamVjdG9yfSBmcm9tICcuLi9jaGFpbmVkX2luamVjdG9yJztcbmltcG9ydCB7Z2V0RnJhbWV3b3JrRElEZWJ1Z0RhdGF9IGZyb20gJy4uL2RlYnVnL2ZyYW1ld29ya19pbmplY3Rvcl9wcm9maWxlcic7XG5pbXBvcnQge0luamVjdGVkU2VydmljZSwgUHJvdmlkZXJSZWNvcmR9IGZyb20gJy4uL2RlYnVnL2luamVjdG9yX3Byb2ZpbGVyJztcbmltcG9ydCB7Z2V0Q29tcG9uZW50RGVmfSBmcm9tICcuLi9kZWZpbml0aW9uJztcbmltcG9ydCB7XG4gIGdldE5vZGVJbmplY3RvckxWaWV3LFxuICBnZXROb2RlSW5qZWN0b3JUTm9kZSxcbiAgZ2V0UGFyZW50SW5qZWN0b3JMb2NhdGlvbixcbiAgTm9kZUluamVjdG9yLFxufSBmcm9tICcuLi9kaSc7XG5pbXBvcnQge05vZGVJbmplY3Rvck9mZnNldH0gZnJvbSAnLi4vaW50ZXJmYWNlcy9pbmplY3Rvcic7XG5pbXBvcnQge1RDb250YWluZXJOb2RlLCBURWxlbWVudENvbnRhaW5lck5vZGUsIFRFbGVtZW50Tm9kZSwgVE5vZGV9IGZyb20gJy4uL2ludGVyZmFjZXMvbm9kZSc7XG5pbXBvcnQge1JFbGVtZW50fSBmcm9tICcuLi9pbnRlcmZhY2VzL3JlbmRlcmVyX2RvbSc7XG5pbXBvcnQge0lOSkVDVE9SLCBMVmlldywgVFZJRVd9IGZyb20gJy4uL2ludGVyZmFjZXMvdmlldyc7XG5cbmltcG9ydCB7XG4gIGdldFBhcmVudEluamVjdG9ySW5kZXgsXG4gIGdldFBhcmVudEluamVjdG9yVmlldyxcbiAgaGFzUGFyZW50SW5qZWN0b3IsXG4gIGlzUm91dGVyT3V0bGV0SW5qZWN0b3IsXG59IGZyb20gJy4vaW5qZWN0b3JfdXRpbHMnO1xuaW1wb3J0IHtnZXROYXRpdmVCeVROb2RlfSBmcm9tICcuL3ZpZXdfdXRpbHMnO1xuXG4vKipcbiAqIERpc2NvdmVycyB0aGUgZGVwZW5kZW5jaWVzIG9mIGFuIGluamVjdGFibGUgaW5zdGFuY2UuIFByb3ZpZGVzIERJIGluZm9ybWF0aW9uIGFib3V0IGVhY2hcbiAqIGRlcGVuZGVuY3kgdGhhdCB0aGUgaW5qZWN0YWJsZSB3YXMgaW5zdGFudGlhdGVkIHdpdGgsIGluY2x1ZGluZyB3aGVyZSB0aGV5IHdlcmUgcHJvdmlkZWQgZnJvbS5cbiAqXG4gKiBAcGFyYW0gaW5qZWN0b3IgQW4gaW5qZWN0b3IgaW5zdGFuY2VcbiAqIEBwYXJhbSB0b2tlbiBhIERJIHRva2VuIHRoYXQgd2FzIGNvbnN0cnVjdGVkIGJ5IHRoZSBnaXZlbiBpbmplY3RvciBpbnN0YW5jZVxuICogQHJldHVybnMgYW4gb2JqZWN0IHRoYXQgY29udGFpbnMgdGhlIGNyZWF0ZWQgaW5zdGFuY2Ugb2YgdG9rZW4gYXMgd2VsbCBhcyBhbGwgb2YgdGhlIGRlcGVuZGVuY2llc1xuICogdGhhdCBpdCB3YXMgaW5zdGFudGlhdGVkIHdpdGggT1IgdW5kZWZpbmVkIGlmIHRoZSB0b2tlbiB3YXMgbm90IGNyZWF0ZWQgd2l0aGluIHRoZSBnaXZlblxuICogaW5qZWN0b3IuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXREZXBlbmRlbmNpZXNGcm9tSW5qZWN0YWJsZTxUPihcbiAgaW5qZWN0b3I6IEluamVjdG9yLFxuICB0b2tlbjogVHlwZTxUPiB8IEluamVjdGlvblRva2VuPFQ+LFxuKToge2luc3RhbmNlOiBUOyBkZXBlbmRlbmNpZXM6IE9taXQ8SW5qZWN0ZWRTZXJ2aWNlLCAnaW5qZWN0ZWRJbic+W119IHwgdW5kZWZpbmVkIHtcbiAgLy8gRmlyc3Qgd2UgY2hlY2sgdG8gc2VlIGlmIHRoZSB0b2tlbiBnaXZlbiBtYXBzIHRvIGFuIGFjdHVhbCBpbnN0YW5jZSBpbiB0aGUgaW5qZWN0b3IgZ2l2ZW4uXG4gIC8vIFdlIHVzZSBgc2VsZjogdHJ1ZWAgYmVjYXVzZSB3ZSBvbmx5IHdhbnQgdG8gbG9vayBhdCB0aGUgaW5qZWN0b3Igd2Ugd2VyZSBnaXZlbi5cbiAgLy8gV2UgdXNlIGBvcHRpb25hbDogdHJ1ZWAgYmVjYXVzZSBpdCdzIHBvc3NpYmxlIHRoYXQgdGhlIHRva2VuIHdlIHdlcmUgZ2l2ZW4gd2FzIG5ldmVyXG4gIC8vIGNvbnN0cnVjdGVkIGJ5IHRoZSBpbmplY3RvciB3ZSB3ZXJlIGdpdmVuLlxuICBjb25zdCBpbnN0YW5jZSA9IGluamVjdG9yLmdldCh0b2tlbiwgbnVsbCwge3NlbGY6IHRydWUsIG9wdGlvbmFsOiB0cnVlfSk7XG4gIGlmIChpbnN0YW5jZSA9PT0gbnVsbCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgVW5hYmxlIHRvIGRldGVybWluZSBpbnN0YW5jZSBvZiAke3Rva2VufSBpbiBnaXZlbiBpbmplY3RvcmApO1xuICB9XG5cbiAgY29uc3QgdW5mb3JtYXR0ZWREZXBlbmRlbmNpZXMgPSBnZXREZXBlbmRlbmNpZXNGb3JUb2tlbkluSW5qZWN0b3IodG9rZW4sIGluamVjdG9yKTtcbiAgY29uc3QgcmVzb2x1dGlvblBhdGggPSBnZXRJbmplY3RvclJlc29sdXRpb25QYXRoKGluamVjdG9yKTtcblxuICBjb25zdCBkZXBlbmRlbmNpZXMgPSB1bmZvcm1hdHRlZERlcGVuZGVuY2llcy5tYXAoKGRlcCkgPT4ge1xuICAgIC8vIGluamVjdGVkSW4gY29udGFpbnMgcHJpdmF0ZSBmaWVsZHMsIHNvIHdlIG9taXQgaXQgZnJvbSB0aGUgcmVzcG9uc2VcbiAgICBjb25zdCBmb3JtYXR0ZWREZXBlbmRlbmN5OiBPbWl0PEluamVjdGVkU2VydmljZSwgJ2luamVjdGVkSW4nPiA9IHtcbiAgICAgIHZhbHVlOiBkZXAudmFsdWUsXG4gICAgfTtcblxuICAgIC8vIGNvbnZlcnQgaW5qZWN0aW9uIGZsYWdzIHRvIGJvb2xlYW5zXG4gICAgY29uc3QgZmxhZ3MgPSBkZXAuZmxhZ3MgYXMgSW50ZXJuYWxJbmplY3RGbGFncztcbiAgICBmb3JtYXR0ZWREZXBlbmRlbmN5LmZsYWdzID0ge1xuICAgICAgb3B0aW9uYWw6IChJbnRlcm5hbEluamVjdEZsYWdzLk9wdGlvbmFsICYgZmxhZ3MpID09PSBJbnRlcm5hbEluamVjdEZsYWdzLk9wdGlvbmFsLFxuICAgICAgaG9zdDogKEludGVybmFsSW5qZWN0RmxhZ3MuSG9zdCAmIGZsYWdzKSA9PT0gSW50ZXJuYWxJbmplY3RGbGFncy5Ib3N0LFxuICAgICAgc2VsZjogKEludGVybmFsSW5qZWN0RmxhZ3MuU2VsZiAmIGZsYWdzKSA9PT0gSW50ZXJuYWxJbmplY3RGbGFncy5TZWxmLFxuICAgICAgc2tpcFNlbGY6IChJbnRlcm5hbEluamVjdEZsYWdzLlNraXBTZWxmICYgZmxhZ3MpID09PSBJbnRlcm5hbEluamVjdEZsYWdzLlNraXBTZWxmLFxuICAgIH07XG5cbiAgICAvLyBmaW5kIHRoZSBpbmplY3RvciB0aGF0IHByb3ZpZGVkIHRoZSBkZXBlbmRlbmN5XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCByZXNvbHV0aW9uUGF0aC5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgaW5qZWN0b3JUb0NoZWNrID0gcmVzb2x1dGlvblBhdGhbaV07XG5cbiAgICAgIC8vIGlmIHNraXBTZWxmIGlzIHRydWUgd2Ugc2tpcCB0aGUgZmlyc3QgaW5qZWN0b3JcbiAgICAgIGlmIChpID09PSAwICYmIGZvcm1hdHRlZERlcGVuZGVuY3kuZmxhZ3Muc2tpcFNlbGYpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIC8vIGhvc3Qgb25seSBhcHBsaWVzIHRvIE5vZGVJbmplY3RvcnNcbiAgICAgIGlmIChmb3JtYXR0ZWREZXBlbmRlbmN5LmZsYWdzLmhvc3QgJiYgaW5qZWN0b3JUb0NoZWNrIGluc3RhbmNlb2YgRW52aXJvbm1lbnRJbmplY3Rvcikge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgY29uc3QgaW5zdGFuY2UgPSBpbmplY3RvclRvQ2hlY2suZ2V0KGRlcC50b2tlbiBhcyBUeXBlPHVua25vd24+LCBudWxsLCB7XG4gICAgICAgIHNlbGY6IHRydWUsXG4gICAgICAgIG9wdGlvbmFsOiB0cnVlLFxuICAgICAgfSk7XG5cbiAgICAgIGlmIChpbnN0YW5jZSAhPT0gbnVsbCkge1xuICAgICAgICAvLyBpZiBob3N0IGZsYWcgaXMgdHJ1ZSB3ZSBkb3VibGUgY2hlY2sgdGhhdCB3ZSBjYW4gZ2V0IHRoZSBzZXJ2aWNlIGZyb20gdGhlIGZpcnN0IGVsZW1lbnRcbiAgICAgICAgLy8gaW4gdGhlIHJlc29sdXRpb24gcGF0aCBieSB1c2luZyB0aGUgaG9zdCBmbGFnLiBUaGlzIGlzIGRvbmUgdG8gbWFrZSBzdXJlIHRoYXQgd2UndmUgZm91bmRcbiAgICAgICAgLy8gdGhlIGNvcnJlY3QgcHJvdmlkaW5nIGluamVjdG9yLCBhbmQgbm90IGEgbm9kZSBpbmplY3RvciB0aGF0IGlzIGNvbm5lY3RlZCB0byBvdXIgcGF0aCB2aWFcbiAgICAgICAgLy8gYSByb3V0ZXIgb3V0bGV0LlxuICAgICAgICBpZiAoZm9ybWF0dGVkRGVwZW5kZW5jeS5mbGFncy5ob3N0KSB7XG4gICAgICAgICAgY29uc3QgZmlyc3RJbmplY3RvciA9IHJlc29sdXRpb25QYXRoWzBdO1xuICAgICAgICAgIGNvbnN0IGxvb2t1cEZyb21GaXJzdEluamVjdG9yID0gZmlyc3RJbmplY3Rvci5nZXQoZGVwLnRva2VuIGFzIFR5cGU8dW5rbm93bj4sIG51bGwsIHtcbiAgICAgICAgICAgIC4uLmZvcm1hdHRlZERlcGVuZGVuY3kuZmxhZ3MsXG4gICAgICAgICAgICBvcHRpb25hbDogdHJ1ZSxcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIGlmIChsb29rdXBGcm9tRmlyc3RJbmplY3RvciAhPT0gbnVsbCkge1xuICAgICAgICAgICAgZm9ybWF0dGVkRGVwZW5kZW5jeS5wcm92aWRlZEluID0gaW5qZWN0b3JUb0NoZWNrO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG5cbiAgICAgICAgZm9ybWF0dGVkRGVwZW5kZW5jeS5wcm92aWRlZEluID0gaW5qZWN0b3JUb0NoZWNrO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgLy8gaWYgc2VsZiBpcyB0cnVlIHdlIHN0b3AgYWZ0ZXIgdGhlIGZpcnN0IGluamVjdG9yXG4gICAgICBpZiAoaSA9PT0gMCAmJiBmb3JtYXR0ZWREZXBlbmRlbmN5LmZsYWdzLnNlbGYpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKGRlcC50b2tlbikgZm9ybWF0dGVkRGVwZW5kZW5jeS50b2tlbiA9IGRlcC50b2tlbjtcblxuICAgIHJldHVybiBmb3JtYXR0ZWREZXBlbmRlbmN5O1xuICB9KTtcblxuICByZXR1cm4ge2luc3RhbmNlLCBkZXBlbmRlbmNpZXN9O1xufVxuXG5mdW5jdGlvbiBnZXREZXBlbmRlbmNpZXNGb3JUb2tlbkluSW5qZWN0b3I8VD4oXG4gIHRva2VuOiBUeXBlPFQ+IHwgSW5qZWN0aW9uVG9rZW48VD4sXG4gIGluamVjdG9yOiBJbmplY3Rvcixcbik6IEluamVjdGVkU2VydmljZVtdIHtcbiAgY29uc3Qge3Jlc29sdmVyVG9Ub2tlblRvRGVwZW5kZW5jaWVzfSA9IGdldEZyYW1ld29ya0RJRGVidWdEYXRhKCk7XG5cbiAgaWYgKCEoaW5qZWN0b3IgaW5zdGFuY2VvZiBOb2RlSW5qZWN0b3IpKSB7XG4gICAgcmV0dXJuIHJlc29sdmVyVG9Ub2tlblRvRGVwZW5kZW5jaWVzLmdldChpbmplY3Rvcik/LmdldD8uKHRva2VuIGFzIFR5cGU8VD4pID8/IFtdO1xuICB9XG5cbiAgY29uc3QgbFZpZXcgPSBnZXROb2RlSW5qZWN0b3JMVmlldyhpbmplY3Rvcik7XG4gIGNvbnN0IHRva2VuRGVwZW5kZW5jeU1hcCA9IHJlc29sdmVyVG9Ub2tlblRvRGVwZW5kZW5jaWVzLmdldChsVmlldyk7XG4gIGNvbnN0IGRlcGVuZGVuY2llcyA9IHRva2VuRGVwZW5kZW5jeU1hcD8uZ2V0KHRva2VuIGFzIFR5cGU8VD4pID8/IFtdO1xuXG4gIC8vIEluIHRoZSBOb2RlSW5qZWN0b3IgY2FzZSwgYWxsIGluamVjdGlvbnMgZm9yIGV2ZXJ5IG5vZGUgYXJlIHN0b3JlZCBpbiB0aGUgc2FtZSBsVmlldy5cbiAgLy8gV2UgdXNlIHRoZSBpbmplY3RlZEluIGZpZWxkIG9mIHRoZSBkZXBlbmRlbmN5IHRvIGZpbHRlciBvdXQgdGhlIGRlcGVuZGVuY2llcyB0aGF0XG4gIC8vIGRvIG5vdCBjb21lIGZyb20gdGhlIHNhbWUgbm9kZSBhcyB0aGUgaW5zdGFuY2Ugd2UncmUgbG9va2luZyBhdC5cbiAgcmV0dXJuIGRlcGVuZGVuY2llcy5maWx0ZXIoKGRlcGVuZGVuY3kpID0+IHtcbiAgICBjb25zdCBkZXBlbmRlbmN5Tm9kZSA9IGRlcGVuZGVuY3kuaW5qZWN0ZWRJbj8udE5vZGU7XG4gICAgaWYgKGRlcGVuZGVuY3lOb2RlID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBjb25zdCBpbnN0YW5jZU5vZGUgPSBnZXROb2RlSW5qZWN0b3JUTm9kZShpbmplY3Rvcik7XG4gICAgYXNzZXJ0VE5vZGUoZGVwZW5kZW5jeU5vZGUpO1xuICAgIGFzc2VydFROb2RlKGluc3RhbmNlTm9kZSEpO1xuXG4gICAgcmV0dXJuIGRlcGVuZGVuY3lOb2RlID09PSBpbn