UNPKG

@dotglitch/ngx-lazy-loader

Version:

A hackable lazy loader for Angular components

360 lines 54.5 kB
import { Input, ViewContainerRef, isDevMode, EventEmitter, Optional, ViewChild, Component, Inject, Output } from '@angular/core';
import { NgComponentOutlet, NgIf, NgTemplateOutlet } from '@angular/common';
import { MAT_DIALOG_DATA, } from '@angular/material/dialog';
import { debounceTime } from 'rxjs';
import { NgxLazyLoaderService } from './ngx-lazy-loader.service';
import { stringToSlug } from '../../utils';
import * as i0 from "@angular/core";
import * as i1 from "./ngx-lazy-loader.service";
import * as i2 from "@angular/cdk/dialog";
export class NgxLazyLoaderComponent {
    /**
     * The id of the component that will be lazy loaded
     */
    set id(data) {
        const id = stringToSlug(data);
        // Check if there is a change to the loaded component's id
        // if it's updated, we destroy and rehydrate the entire container
        if (this.initialized && this._id != id) {
            this._id = id;
            this.ngAfterViewInit();
        }
        else {
            this._id = id;
        }
    }
    ;
    set group(data) {
        const group = stringToSlug(data);
        if (typeof group != "string" || !group)
            return;
        // If the group was updated, retry to bootstrap something into the container.
        if (this.initialized && this._group != group) {
            this._group = group;
            this.ngAfterViewInit();
            return;
        }
        this._group = group;
    }
    get group() { return this._group; }
    /**
     * A map of inputs to bind to the child.
     * Supports change detection. (May fail on deep JSON changes)
     *
     * ```html
     * <lazy-loader component="MyLazyComponent"
     *       [inputs]="{
     *          prop1: true,
     *          prop2: false,
     *          complex: {
     *              a: true,
     *              b: 0
     *          }
     *       }"
     * >
     * </lazy-loader>
     * ```
     */
    set inputs(data) {
        if (data == undefined)
            return;
        let previous = this._inputs;
        this._inputs = data;
        if (data == undefined)
            console.trace(data);
        if (this.targetComponentFactory) {
            const { inputs } = this.targetComponentFactory.ɵcmp;
            const currentKeys = Object.keys(inputs);
            const oldKeys = Object.keys(previous).filter(key => currentKeys.includes(key));
            const newKeys = Object.keys(data).filter(key => currentKeys.includes(key));
            const removed = oldKeys.filter(key => !newKeys.includes(key));
            // ? perhaps set to null or undefined instead
            removed.forEach(k => this.targetComponentInstance[k] = null);
            this.bindInputs();
        }
    }
    /**
     * A map of outputs to bind from the child.
     * Should support change detection.
     * ```html
     * <lazy-loader component="MyLazyComponent"
     *       [outputs]="{
     *           prop3: onOutputFire
     *       }"
     * >
     * </lazy-loader>
     * ```
     */
    set outputs(data) {
        let previous = this._outputs;
        this._outputs = data;
        if (this.targetComponentFactory) {
            const { inputs } = this.targetComponentFactory.ɵcmp;
            const currentKeys = Object.keys(inputs);
            const removed = Object.keys(previous).filter(key => !currentKeys.includes(key));
            removed.forEach(k => {
                // Unsubscribe from observable
                this.outputSubscriptions[k]?.unsubscribe();
                delete this.targetComponentInstance[k];
            });
            this.bindOutputs();
        }
    }
    constructor(service, viewContainerRef, dialog, dialogArguments) {
        this.service = service;
        this.viewContainerRef = viewContainerRef;
        this.dialog = dialog;
        this.dialogArguments = dialogArguments;
        this._group = "default";
        this.outputSubscriptions = {};
        /**
         * Emits errors encountered when loading components
         */
        this.componentLoadError = new EventEmitter();
        /**
         * Emits when the component is fully constructed
         * and had it's inputs and outputs bound
         * > before `OnInit`
         *
         * Returns the active class instance of the lazy-loaded component
         */
        this.componentLoaded = new EventEmitter();
        // Force 500ms delay before revealing the spinner
        this.loaderEmitter = new EventEmitter();
        this.clearLoader$ = this.loaderEmitter.pipe(debounceTime(300));
        this.showLoader = true; // whether we render the DOM for the spinner
        this.isClearingLoader = false; // should the spinner start fading out
        this.initialized = false;
        this.config = NgxLazyLoaderService.config;
        this.err = NgxLazyLoaderService.config.logger.err;
        this.warn = NgxLazyLoaderService.config.logger.warn;
        this.log = NgxLazyLoaderService.config.logger.log;
        // First, check for dialog arguments
        if (this.dialogArguments) {
            this.inputs = this.dialogArguments.inputs || this.dialogArguments.data;
            this.outputs = this.dialogArguments.outputs;
            this.id = this.dialogArguments.id;
            this.group = this.dialogArguments.group;
        }
        this.loaderSub = this.clearLoader$.subscribe(() => {
            this.showLoader = false;
        });
    }
    async ngAfterViewInit() {
        this.ngOnDestroy(false);
        this.isClearingLoader = false;
        this.showLoader = true;
        this.initialized = true;
        if (!this._id) {
            this.warn("No component was specified!");
            return this.loadDefault();
        }
        try {
            const _entry = this.service.resolveRegistrationEntry(this._id, this._group);
            if (!_entry || !_entry.entry) {
                this.err(`Failed to find Component '${this._id}' in group '${this._group}' in registry!`);
                return this.loadDefault();
            }
            const { entry, matchGroups } = _entry;
            this._matchGroups = matchGroups;
            // Download the "module" (the standalone component)
            const bundle = this.targetModule = await entry.load();
            // Check if there is some corruption on the bundle.
            if (!bundle || typeof bundle != 'object' || bundle['__esModule'] !== true || bundle.toString() != "[object Module]") {
                this.err(`Failed to load component/module for '${this._id}'! Parsed resource is invalid.`);
                return this.loadError();
            }
            const modules = Object.keys(bundle)
                .map(k => {
                const entry = bundle[k];
                // Strictly check for exported modules or standalone components
                if (typeof entry == "function" && typeof entry["ɵfac"] == "function")
                    return entry;
                return null;
            })
                .filter(e => e != null)
                .filter(entry => {
                entry['_isModule'] = !!entry['ɵmod']; // module
                entry['_isComponent'] = !!entry['ɵcmp']; // component
                return (entry['_isModule'] || entry['_isComponent']);
            });
            if (modules.length == 0) {
                this.err(`Component/Module loaded for '${this._id}' has no exported components or modules!`);
                return this.loadError();
            }
            const component = this.targetComponentFactory = this.service.resolveComponent(this._id, "default", modules);
            if (!component) {
                this.err(`Component '${this._id}' is invalid or corrupted!`);
                return this.loadError();
            }
            // Bootstrap the component into the container
            const componentRef = this.targetComponentContainerRef = this.targetContainer.createComponent(component);
            this.targetRef = this.targetContainer.insert(this.targetComponentContainerRef.hostView);
            const instance = this.targetComponentInstance = componentRef['instance'];
            this.bindInputs();
            this.bindOutputs();
            this.componentLoaded.next(instance);
            this.instance = instance;
            // Look for an observable called isLoading$ that will make us show/hide
            // the same distractor that is used on basic loading
            const isLoading$ = instance['ngxShowDistractor$'];
            if (isLoading$ && typeof isLoading$.subscribe == "function") {
                this.distractorSubscription = isLoading$.subscribe(loading => {
                    if (!loading) {
                        this.isClearingLoader = true;
                        this.loaderEmitter.emit();
                    }
                    else {
                        this.showLoader = true;
                        this.isClearingLoader = false;
                    }
                });
            }
            else {
                this.isClearingLoader = true;
            }
            const name = Object.keys(bundle)[0];
            this.log(`Loaded '${name}'`);
            this.loaderEmitter.emit();
            return componentRef;
        }
        catch (ex) {
            if (isDevMode()) {
                console.warn("Component " + this._id + " threw an error on mount!");
                console.warn("This will cause you to see a 404 panel.");
                console.error(ex);
            }
            // Network errors throw a toast and return an error component
            if (ex && !isDevMode()) {
                console.error("Uncaught error when loading component");
                throw ex;
            }
            return this.loadDefault();
        }
    }
    ngOnDestroy(clearAll = true) {
        // unsubscribe from all subscriptions
        Object.entries(this.outputSubscriptions).forEach(([key, sub]) => {
            sub.unsubscribe();
        });
        this.outputSubscriptions = {};
        // Clear all things
        if (clearAll) {
            this.loaderSub?.unsubscribe();
        }
        this.distractorSubscription?.unsubscribe();
        // Clear target container
        this.targetRef?.destroy();
        this.targetComponentContainerRef?.destroy();
        this.targetContainer?.clear();
        // Wipe the rest of the state clean
        this.targetRef = null;
        this.targetComponentContainerRef = null;
    }
    /**
     * Bind the input values to the child component.
     */
    bindInputs() {
        if (!this._inputs || !this.targetComponentInstance)
            return;
        // Merge match groups
        if (typeof this._matchGroups == "object") {
            Object.entries(this._matchGroups).forEach(([key, val]) => {
                if (typeof this._inputs[key] == 'undefined')
                    this._inputs[key] = val;
            });
        }
        // forward-bind inputs
        const { inputs } = this.targetComponentFactory.ɵcmp;
        // Returns a list of entries that need to be set
        // This makes it so that unnecessary setters are not invoked.
        const updated = Object.entries(inputs).filter(([parentKey, childKey]) => {
            return this.targetComponentInstance[childKey] != this._inputs[parentKey];
        });
        updated.forEach(([parentKey, childKey]) => {
            if (this._inputs.hasOwnProperty(parentKey))
                this.targetComponentInstance[childKey] = this._inputs[parentKey];
        });
    }
    /**
     * Bind the output handlers to the loaded child component
     */
    bindOutputs() {
        if (!this._outputs || !this.targetComponentInstance)
            return;
        const { outputs } = this.targetComponentFactory.ɵcmp;
        // Get a list of unregistered outputs
        const newOutputs = Object.entries(outputs).filter(([parentKey, childKey]) => {
            return !this.outputSubscriptions[parentKey];
        });
        // Reverse bind via subscription
        newOutputs.forEach(([parentKey, childKey]) => {
            if (this._outputs.hasOwnProperty(parentKey)) {
                const target = this.targetComponentInstance[childKey];
                const outputs = this._outputs;
                // Angular folks, stop making this so difficult.
                const ctx = this.viewContainerRef['_hostLView'][8];
                const sub = target.subscribe(outputs[parentKey].bind(ctx)); // Subscription
                this.outputSubscriptions[parentKey] = sub;
            }
        });
    }
    /**
     * Load the "Default" component (404) screen normally.
     * This is shown when the component id isn't in the
     * registry or otherwise doesn't match
     *
     * This
     */
    loadDefault() {
        if (this.config.notFoundComponent)
            this.targetContainer.createComponent(this.config.notFoundComponent);
        this.showLoader = false;
    }
    /**
     * Load the "Error" component.
     * This is shown when we are able to resolve the component
     * in the registry, but have some issue boostrapping the
     * component into the viewContainer
     */
    loadError() {
        if (this.config.errorComponent)
            this.targetContainer.createComponent(this.config.errorComponent);
        this.showLoader = false;
    }
}
/** @nocollapse */ NgxLazyLoaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderComponent, deps: [{ token: i1.NgxLazyLoaderService }, { token: i0.ViewContainerRef, optional: true }, { token: i2.DialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component });
/** @nocollapse */ NgxLazyLoaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.0", type: NgxLazyLoaderComponent, isStandalone: true, selector: "ngx-lazy-loader", inputs: { id: ["component", "id"], group: "group", inputs: "inputs", outputs: "outputs" }, outputs: { componentLoadError: "componentLoadError", componentLoaded: "componentLoaded" }, viewQueries: [{ propertyName: "targetContainer", first: true, predicate: ["content"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "<ng-container #content></ng-container>\n\n<div class=\"ngx-lazy-loader-distractor\" [class.destroying]=\"isClearingLoader\">\n    <ng-container *ngIf=\"config.loaderDistractorComponent\" [ngComponentOutlet]=\"config.loaderDistractorComponent\"></ng-container>\n    <ng-container *ngIf=\"config.loaderDistractorTemplate\" [ngTemplateOutlet]=\"config.loaderDistractorTemplate\" [ngTemplateOutletContext]=\"{ '$implicit': inputs }\"></ng-container>\n</div>\n", styles: [":host{display:contents;contain:content;z-index:1;position:relative}.ngx-lazy-loader-distractor{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;background-color:var(--background-color, #212121);opacity:1;transition:opacity .3s ease;z-index:999999}.ngx-lazy-loader-distractor.destroying{opacity:0;pointer-events:none}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.0", ngImport: i0, type: NgxLazyLoaderComponent, decorators: [{
            type: Component,
            args: [{ selector: 'ngx-lazy-loader', imports: [NgIf, NgComponentOutlet, NgTemplateOutlet], standalone: true, template: "<ng-container #content></ng-container>\n\n<div class=\"ngx-lazy-loader-distractor\" [class.destroying]=\"isClearingLoader\">\n    <ng-container *ngIf=\"config.loaderDistractorComponent\" [ngComponentOutlet]=\"config.loaderDistractorComponent\"></ng-container>\n    <ng-container *ngIf=\"config.loaderDistractorTemplate\" [ngTemplateOutlet]=\"config.loaderDistractorTemplate\" [ngTemplateOutletContext]=\"{ '$implicit': inputs }\"></ng-container>\n</div>\n", styles: [":host{display:contents;contain:content;z-index:1;position:relative}.ngx-lazy-loader-distractor{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;background-color:var(--background-color, #212121);opacity:1;transition:opacity .3s ease;z-index:999999}.ngx-lazy-loader-distractor.destroying{opacity:0;pointer-events:none}\n"] }]
        }], ctorParameters: function () { return [{ type: i1.NgxLazyLoaderService }, { type: i0.ViewContainerRef, decorators: [{
                    type: Optional
                }] }, { type: i2.DialogRef, decorators: [{
                    type: Optional
                }] }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [MAT_DIALOG_DATA]
                }] }]; }, propDecorators: { targetContainer: [{
                type: ViewChild,
                args: ["content", { read: ViewContainerRef }]
            }], id: [{
                type: Input,
                args: ["component"]
            }], group: [{
                type: Input,
                args: ["group"]
            }], inputs: [{
                type: Input,
                args: ["inputs"]
            }], outputs: [{
                type: Input,
                args: ["outputs"]
            }], componentLoadError: [{
                type: Output
            }], componentLoaded: [{
                type: Output
            }] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWxhenktbG9hZGVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvbGF6eS1sb2FkZXIvbmd4LWxhenktbG9hZGVyLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uL3NyYy9saWIvbGF6eS1sb2FkZXIvbmd4LWxhenktbG9hZGVyLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsU0FBUyxFQUFnQixZQUFZLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBbUMsTUFBTSxlQUFlLENBQUM7QUFDaEwsT0FBTyxFQUFFLGlCQUFpQixFQUFFLElBQUksRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzVFLE9BQU8sRUFBRSxlQUFlLEdBQUcsTUFBTSwwQkFBMEIsQ0FBQztBQUU1RCxPQUFPLEVBQW1CLFlBQVksRUFBZ0IsTUFBTSxNQUFNLENBQUM7QUFDbkUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDakUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGFBQWEsQ0FBQzs7OztBQVczQyxNQUFNLE9BQU8sc0JBQXNCO0lBZ0IvQjs7T0FFRztJQUNILElBQXdCLEVBQUUsQ0FBQyxJQUFZO1FBQ25DLE1BQU0sRUFBRSxHQUFHLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUU5QiwwREFBMEQ7UUFDMUQsaUVBQWlFO1FBQ2pFLElBQUksSUFBSSxDQUFDLFdBQVcsSUFBSSxJQUFJLENBQUMsR0FBRyxJQUFJLEVBQUUsRUFBRTtZQUNwQyxJQUFJLENBQUMsR0FBRyxHQUFHLEVBQUUsQ0FBQztZQUNkLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztTQUMxQjthQUNJO1lBQ0QsSUFBSSxDQUFDLEdBQUcsR0FBRyxFQUFFLENBQUM7U0FDakI7SUFDTCxDQUFDO0lBQUEsQ0FBQztJQUlGLElBQW9CLEtBQUssQ0FBQyxJQUFZO1FBQ2xDLE1BQU0sS0FBSyxHQUFHLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUVqQyxJQUFJLE9BQU8sS0FBSyxJQUFJLFFBQVEsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPO1FBRS9DLDZFQUE2RTtRQUM3RSxJQUFJLElBQUksQ0FBQyxXQUFXLElBQUksSUFBSSxDQUFDLE1BQU0sSUFBSSxLQUFLLEVBQUU7WUFDMUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7WUFFcEIsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQ3ZCLE9BQU87U0FDVjtRQUVELElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO0lBQ3hCLENBQUM7SUFDRCxJQUFJLEtBQUssS0FBSyxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUEsQ0FBQyxDQUFDO0lBSWxDOzs7Ozs7Ozs7Ozs7Ozs7OztPQWlCRztJQUNILElBQXFCLE1BQU0sQ0FBQyxJQUE2QjtRQUNyRCxJQUFJLElBQUksSUFBSSxTQUFTO1lBQUUsT0FBTztRQUU5QixJQUFJLFFBQVEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDO1FBQzVCLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO1FBQ3BCLElBQUksSUFBSSxJQUFJLFNBQVM7WUFDakIsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUV4QixJQUFJLElBQUksQ0FBQyxzQkFBc0IsRUFBRTtZQUM3QixNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLHNCQUFzQixDQUFDLElBQUksQ0FBQztZQUVwRCxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBRXhDLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQy9FLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBRTNFLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztZQUU5RCw2Q0FBNkM7WUFDN0MsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQztZQUU3RCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7U0FDckI7SUFDTCxDQUFDO0lBS0Q7Ozs7Ozs7Ozs7O09BV0c7SUFDSCxJQUFzQixPQUFPLENBQUMsSUFBa0M7UUFDNUQsSUFBSSxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQztRQUM3QixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQztRQUVyQixJQUFJLElBQUksQ0FBQyxzQkFBc0IsRUFBRTtZQUM3QixNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLHNCQUFzQixDQUFDLElBQUksQ0FBQztZQUVwRCxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ3hDLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFFaEYsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRTtnQkFDaEIsOEJBQThCO2dCQUM5QixJQUFJLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLEVBQUUsV0FBVyxFQUFFLENBQUM7Z0JBQzNDLE9BQU8sSUFBSSxDQUFDLHVCQUF1QixDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzNDLENBQUMsQ0FBQyxDQUFDO1lBRUgsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1NBQ3RCO0lBQ0wsQ0FBQztJQTRERCxZQUNZLE9BQTZCLEVBQ2pCLGdCQUFrQyxFQUNuQyxNQUFpQixFQUNRLGVBQWU7UUFIbkQsWUFBTyxHQUFQLE9BQU8sQ0FBc0I7UUFDakIscUJBQWdCLEdBQWhCLGdCQUFnQixDQUFrQjtRQUNuQyxXQUFNLEdBQU4sTUFBTSxDQUFXO1FBQ1Esb0JBQWUsR0FBZixlQUFlLENBQUE7UUFoS3ZELFdBQU0sR0FBRyxTQUFTLENBQUM7UUFnRW5CLHdCQUFtQixHQUFxQyxFQUFFLENBQUM7UUFrQ25FOztXQUVHO1FBQ08sdUJBQWtCLEdBQUcsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUVsRDs7Ozs7O1dBTUc7UUFDTyxvQkFBZSxHQUFHLElBQUksWUFBWSxFQUFFLENBQUM7UUF1Qy9DLGlEQUFpRDtRQUN6QyxrQkFBYSxHQUFHLElBQUksWUFBWSxFQUFFLENBQUM7UUFDbkMsaUJBQVksR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUVsRSxlQUFVLEdBQUcsSUFBSSxDQUFDLENBQUMsNENBQTRDO1FBQy9ELHFCQUFnQixHQUFHLEtBQUssQ0FBQyxDQUFDLHNDQUFzQztRQTBCeEQsZ0JBQVcsR0FBRyxLQUFLLENBQUM7UUFsQnhCLElBQUksQ0FBQyxNQUFNLEdBQUcsb0JBQW9CLENBQUMsTUFBTSxDQUFDO1FBQzFDLElBQUksQ0FBQyxHQUFHLEdBQUcsb0JBQW9CLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUM7UUFDbEQsSUFBSSxDQUFDLElBQUksR0FBRyxvQkFBb0IsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQztRQUNwRCxJQUFJLENBQUMsR0FBRyxHQUFHLG9CQUFvQixDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDO1FBRWxELG9DQUFvQztRQUNwQyxJQUFJLElBQUksQ0FBQyxlQUFlLEVBQUU7WUFDdEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQztZQUN2RSxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsT0FBTyxDQUFDO1lBQzVDLElBQUksQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUM7WUFDbEMsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLEtBQUssQ0FBQztTQUMzQztRQUVELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFO1lBQzlDLElBQUksQ0FBQyxVQUFVLEdBQUcsS0FBSyxDQUFDO1FBQzVCLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUdELEtBQUssQ0FBQyxlQUFlO1FBQ2pCLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDeEIsSUFBSSxDQUFDLGdCQUFnQixHQUFHLEtBQUssQ0FBQztRQUM5QixJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQztRQUV2QixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztRQUV4QixJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUNYLElBQUksQ0FBQyxJQUFJLENBQUMsNkJBQTZCLENBQUMsQ0FBQztZQUN6QyxPQUFPLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztTQUM3QjtRQUVELElBQUk7WUFDQSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHdCQUF3QixDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQzVFLElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFO2dCQUMxQixJQUFJLENBQUMsR0FBRyxDQUFDLDZCQUE2QixJQUFJLENBQUMsR0FBRyxlQUFlLElBQUksQ0FBQyxNQUFNLGdCQUFnQixDQUFDLENBQUM7Z0JBQzFGLE9BQU8sSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO2FBQzdCO1lBRUQsTUFBTSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsR0FBRyxNQUFNLENBQUM7WUFDdEMsSUFBSSxDQUFDLFlBQVksR0FBRyxXQUFXLENBQUM7WUFFaEMsbURBQW1EO1lBQ25ELE1BQU0sTUFBTSxHQUFtQixJQUFJLENBQUMsWUFBWSxHQUFHLE1BQU0sS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO1lBR3RFLG1EQUFtRDtZQUNuRCxJQUFJLENBQUMsTUFBTSxJQUFJLE9BQU8sTUFBTSxJQUFJLFFBQVEsSUFBSSxNQUFNLENBQUMsWUFBWSxDQUFRLEtBQUssSUFBSSxJQUFJLE1BQU0sQ0FBQyxRQUFRLEVBQUUsSUFBSSxpQkFBaUIsRUFBRTtnQkFDeEgsSUFBSSxDQUFDLEdBQUcsQ0FBQyx3Q0FBd0MsSUFBSSxDQUFDLEdBQUcsZ0NBQWdDLENBQUMsQ0FBQztnQkFDM0YsT0FBTyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7YUFDM0I7WUFFRCxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztpQkFDOUIsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFO2dCQUNMLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFFeEIsK0RBQStEO2dCQUMvRCxJQUFJLE9BQU8sS0FBSyxJQUFJLFVBQVUsSUFBSSxPQUFPLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxVQUFVO29CQUNoRSxPQUFPLEtBQUssQ0FBQztnQkFDakIsT0FBTyxJQUFJLENBQUM7WUFDaEIsQ0FBQyxDQUFDO2lCQUNELE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUM7aUJBQ3RCLE1BQU0sQ0FBQyxLQUFLLENBQUMsRUFBRTtnQkFDWixLQUFLLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLFNBQVM7Z0JBQy9DLEtBQUssQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsWUFBWTtnQkFFckQsT0FBTyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsSUFBSSxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQztZQUN6RCxDQUFDLENBQUMsQ0FBQztZQUVQLElBQUksT0FBTyxDQUFDLE1BQU0sSUFBSSxDQUFDLEVBQUU7Z0JBQ3JCLElBQUksQ0FBQyxHQUFHLENBQUMsZ0NBQWdDLElBQUksQ0FBQyxHQUFHLDBDQUEwQyxDQUFDLENBQUM7Z0JBQzdGLE9BQU8sSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO2FBQzNCO1lBRUQsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLHNCQUFzQixHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxTQUFTLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFFNUcsSUFBSSxDQUFDLFNBQVMsRUFBRTtnQkFDWixJQUFJLENBQUMsR0FBRyxDQUFDLGNBQWMsSUFBSSxDQUFDLEdBQUcsNEJBQTRCLENBQUMsQ0FBQztnQkFDN0QsT0FBTyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7YUFDM0I7WUFHRCw2Q0FBNkM7WUFDN0MsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLDJCQUEyQixHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsZUFBZSxDQUFDLFNBQWdCLENBQUMsQ0FBQztZQUMvRyxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUV4RixNQUFNLFFBQVEsR0FBUSxJQUFJLENBQUMsdUJBQXVCLEdBQUcsWUFBWSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBRTlFLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNsQixJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7WUFFbkIsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDcEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7WUFFekIsdUVBQXVFO1lBQ3ZFLG9EQUFvRDtZQUNwRCxNQUFNLFVBQVUsR0FBRyxRQUFRLENBQUMsb0JBQW9CLENBQTZCLENBQUM7WUFFOUUsSUFBSSxVQUFVLElBQUksT0FBTyxVQUFVLENBQUMsU0FBUyxJQUFJLFVBQVUsRUFBRTtnQkFDekQsSUFBSSxDQUFDLHNCQUFzQixHQUFHLFVBQVUsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLEVBQUU7b0JBQ3pELElBQUksQ0FBQyxPQUFPLEVBQUU7d0JBQ1YsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQzt3QkFDN0IsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztxQkFDN0I7eUJBQ0k7d0JBQ0QsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7d0JBQ3ZCLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxLQUFLLENBQUM7cUJBQ2pDO2dCQUNMLENBQUMsQ0FBQyxDQUFDO2FBQ047aUJBQ0k7Z0JBQ0QsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQzthQUNoQztZQUVELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDcEMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLElBQUksR0FBRyxDQUFDLENBQUM7WUFDN0IsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUUxQixPQUFPLFlBQVksQ0FBQztTQUN2QjtRQUNELE9BQU8sRUFBRSxFQUFFO1lBRVAsSUFBSSxTQUFTLEVBQUUsRUFBRTtnQkFDYixPQUFPLENBQUMsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsR0FBRyxHQUFHLDJCQUEyQixDQUFDLENBQUM7Z0JBQ3BFLE9BQU8sQ0FBQyxJQUFJLENBQUMseUNBQXlDLENBQUMsQ0FBQztnQkFDeEQsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQzthQUNyQjtZQUVELDZEQUE2RDtZQUM3RCxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRSxFQUFFO2dCQUNwQixPQUFPLENBQUMsS0FBSyxDQUFDLHVDQUF1QyxDQUFDLENBQUM7Z0JBQ3ZELE1BQU0sRUFBRSxDQUFDO2FBQ1o7WUFFRCxPQUFPLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztTQUM3QjtJQUNMLENBQUM7SUFFRCxXQUFXLENBQUMsUUFBUSxHQUFHLElBQUk7UUFDdkIscUNBQXFDO1FBQ3JDLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLEVBQUUsRUFBRTtZQUM1RCxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDdEIsQ0FBQyxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsbUJBQW1CLEdBQUcsRUFBRSxDQUFDO1FBRTlCLG1CQUFtQjtRQUNuQixJQUFJLFFBQVEsRUFBRTtZQUNWLElBQUksQ0FBQyxTQUFTLEVBQUUsV0FBVyxFQUFFLENBQUM7U0FDakM7UUFFRCxJQUFJLENBQUMsc0JBQXNCLEVBQUUsV0FBVyxFQUFFLENBQUM7UUFFM0MseUJBQXlCO1FBQ3pCLElBQUksQ0FBQyxTQUFTLEVBQUUsT0FBTyxFQUFFLENBQUM7UUFDMUIsSUFBSSxDQUFDLDJCQUEyQixFQUFFLE9BQU8sRUFBRSxDQUFDO1FBQzVDLElBQUksQ0FBQyxlQUFlLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFFOUIsbUNBQW1DO1FBQ25DLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO1FBQ3RCLElBQUksQ0FBQywyQkFBMkIsR0FBRyxJQUFJLENBQUM7SUFDNUMsQ0FBQztJQUVEOztPQUVHO0lBQ0ssVUFBVTtRQUNkLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLHVCQUF1QjtZQUFFLE9BQU87UUFFM0QscUJBQXFCO1FBQ3JCLElBQUksT0FBTyxJQUFJLENBQUMsWUFBWSxJQUFJLFFBQVEsRUFBRTtZQUN0QyxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsRUFBRSxFQUFFO2dCQUNyRCxJQUFJLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxXQUFXO29CQUN2QyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEdBQUcsQ0FBQztZQUNoQyxDQUFDLENBQUMsQ0FBQztTQUNOO1FBRUQsc0JBQXNCO1FBQ3RCLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsc0JBQXNCLENBQUMsSUFBSSxDQUFDO1FBRXBELGdEQUFnRDtRQUNoRCw2REFBNkQ7UUFDN0QsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQW1CLEVBQUUsRUFBRTtZQUN0RixPQUFPLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxRQUFRLENBQUMsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzdFLENBQUMsQ0FBQyxDQUFDO1FBRUgsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBbUIsRUFBRSxFQUFFO1lBQ3hELElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsU0FBUyxDQUFDO2dCQUN0QyxJQUFJLENBQUMsdUJBQXVCLENBQUMsUUFBUSxDQUFDLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN6RSxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFRDs7T0FFRztJQUNLLFdBQVc7UUFDZixJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsSUFBSSxDQUFDLElBQUksQ0FBQyx1QkFBdUI7WUFBRSxPQUFPO1FBRTVELE1BQU0sRUFBRSxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUMsc0JBQXNCLENBQUMsSUFBSSxDQUFDO1FBRXJELHFDQUFxQztRQUNyQyxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBbUIsRUFBRSxFQUFFO1lBQzFGLE9BQU8sQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDaEQsQ0FBQyxDQUFDLENBQUM7UUFFSCxnQ0FBZ0M7UUFDaEMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBbUIsRUFBRSxFQUFFO1lBQzNELElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsU0FBUyxDQUFDLEVBQUU7Z0JBQ3pDLE1BQU0sTUFBTSxHQUEwQixJQUFJLENBQUMsdUJBQXVCLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQzdFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUM7Z0JBRTlCLGdEQUFnRDtnQkFDaEQsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNuRCxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLGVBQWU7Z0JBRTNFLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxTQUFTLENBQUMsR0FBRyxHQUFHLENBQUM7YUFDN0M7UUFDTCxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSyxXQUFXO1FBQ2YsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLGlCQUFpQjtZQUM3QixJQUFJLENBQUMsZUFBZSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFFeEUsSUFBSSxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUM7SUFDNUIsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ssU0FBUztRQUNiLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxjQUFjO1lBQzFCLElBQUksQ0FBQyxlQUFlLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUM7UUFFckUsSUFBSSxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUM7SUFDNUIsQ0FBQzs7c0lBdmJRLHNCQUFzQiwrSUFrTVAsZUFBZTswSEFsTTlCLHNCQUFzQix3VkFDRCxnQkFBZ0IsNkJDbEJsRCx5Y0FNQSw4YURRZSxJQUFJLDZGQUFFLGlCQUFpQix5TkFBRSxnQkFBZ0I7MkZBRzNDLHNCQUFzQjtrQkFQbEMsU0FBUzsrQkFDSSxpQkFBaUIsV0FHbEIsQ0FBRSxJQUFJLEVBQUUsaUJBQWlCLEVBQUUsZ0JBQWdCLENBQUUsY0FDMUMsSUFBSTs7MEJBa01YLFFBQVE7OzBCQUNSLFFBQVE7OzBCQUNSLFFBQVE7OzBCQUFJLE1BQU07MkJBQUMsZUFBZTs0Q0FqTVcsZUFBZTtzQkFBaEUsU0FBUzt1QkFBQyxTQUFTLEVBQUUsRUFBRSxJQUFJLEVBQUUsZ0JBQWdCLEVBQUU7Z0JBa0J4QixFQUFFO3NCQUF6QixLQUFLO3VCQUFDLFdBQVc7Z0JBZ0JFLEtBQUs7c0JBQXhCLEtBQUs7dUJBQUMsT0FBTztnQkFxQ08sTUFBTTtzQkFBMUIsS0FBSzt1QkFBQyxRQUFRO2dCQXdDTyxPQUFPO3NCQUE1QixLQUFLO3VCQUFDLFNBQVM7Z0JBdUJOLGtCQUFrQjtzQkFBM0IsTUFBTTtnQkFTRyxlQUFlO3NCQUF4QixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5wdXQsIFZpZXdDb250YWluZXJSZWYsIGlzRGV2TW9kZSwgQ29tcG9uZW50UmVmLCBFdmVudEVtaXR0ZXIsIE9wdGlvbmFsLCBWaWV3Q2hpbGQsIENvbXBvbmVudCwgSW5qZWN0LCBPdXRwdXQsIE5nTW9kdWxlLCBBZnRlclZpZXdJbml0LCBPbkluaXQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IE5nQ29tcG9uZW50T3V0bGV0LCBOZ0lmLCBOZ1RlbXBsYXRlT3V0bGV0IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7IE1BVF9ESUFMT0dfREFUQSwgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9kaWFsb2cnO1xuaW1wb3J0IHsgRGlhbG9nUmVmIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2RpYWxvZyc7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIGRlYm91bmNlVGltZSwgU3Vic2NyaXB0aW9uIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBOZ3hMYXp5TG9hZGVyU2VydmljZSB9IGZyb20gJy4vbmd4LWxhenktbG9hZGVyLnNlcnZpY2UnO1xuaW1wb3J0IHsgc3RyaW5nVG9TbHVnIH0gZnJvbSAnLi4vLi4vdXRpbHMnO1xuaW1wb3J0IHsgQ29tcGlsZWRCdW5kbGUsIE5neExhenlMb2FkZXJDb25maWcgfSBmcm9tICcuL3R5cGVzJztcblxuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ25neC1sYXp5LWxvYWRlcicsXG4gICAgdGVtcGxhdGVVcmw6ICcuL25neC1sYXp5LWxvYWRlci5jb21wb25lbnQuaHRtbCcsXG4gICAgc3R5bGVVcmxzOiBbICcuL25neC1sYXp5LWxvYWRlci5jb21wb25lbnQuc2NzcycgXSxcbiAgICBpbXBvcnRzOiBbIE5nSWYsIE5nQ29tcG9uZW50T3V0bGV0LCBOZ1RlbXBsYXRlT3V0bGV0IF0sXG4gICAgc3RhbmRhbG9uZTogdHJ1ZVxufSlcbmV4cG9ydCBjbGFzcyBOZ3hMYXp5TG9hZGVyQ29tcG9uZW50IGltcGxlbWVudHMgQWZ0ZXJWaWV3SW5pdCB7XG4gICAgQFZpZXdDaGlsZChcImNvbnRlbnRcIiwgeyByZWFkOiBWaWV3Q29udGFpbmVyUmVmIH0pIHRhcmdldENvbnRhaW5lcjogVmlld0NvbnRhaW5lclJlZjtcblxuICAgIC8qKlxuICAgICAqICEgSGVyZSBiZSBkcmFnb25zLlxuICAgICAqIE9ubHkgdGhlIGJyYXZlc3Qgb2YgQWR2ZW50dXJlcnMgY2FuIHN1cnZpdmUgdGhlIGJhdHRsZXMgYmVsb3csXG4gICAgICogYW5kIHRoZXkgbXVzdCBiZSB0cmFpbmVkIGFuZCByZWFkeSBmb3IgdGhlIGdydWVsbGluZyBqb3VybmV5IGFoZWFkLlxuICAgICAqIE1hbnkgYSBzb3VsIGhhcyB0cmllZCB0byBiZXN0IHRoZXNlIERyYWdvbnMsIHlldCBvbmx5IG9uZSBoYXNcbiAgICAgKiBzdWNjZWVkZWQgc2luY2Ugb3VyIGZvdW5kaW5nLlxuICAgICAqXG4gICAgICogVEw7RFIgLS0gRG9uJ3QgbWVzcyB3aXRoIHRoaXMgdW5sZXNzIHlvdSBrbm93IHdoYXQgeW91J3JlIGRvaW5nLlxuICAgICAqICAgICBUaGlzIGlzIGNlbnRyYWwgdG8gYSB0b24gb2YgbW92aW5nIHBhcnRzIC0tIGJyZWFraW5nIGl0IHdpbGxcbiAgICAgKiAgICAgY2F1c2UgbW9yZSBjb2xsYXRlcmFsIGRhbWFnZSB0aGFuIHlvdSBtYXkgcmVhbGl6ZS5cbiAgICAgKi9cblxuICAgIHByaXZhdGUgX2lkOiBzdHJpbmc7XG4gICAgLyoqXG4gICAgICogVGhlIGlkIG9mIHRoZSBjb21wb25lbnQgdGhhdCB3aWxsIGJlIGxhenkgbG9hZGVkXG4gICAgICovXG4gICAgQElucHV0KFwiY29tcG9uZW50XCIpIHNldCBpZChkYXRhOiBzdHJpbmcpIHtcbiAgICAgICAgY29uc3QgaWQgPSBzdHJpbmdUb1NsdWcoZGF0YSk7XG5cbiAgICAgICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgYSBjaGFuZ2UgdG8gdGhlIGxvYWRlZCBjb21wb25lbnQncyBpZFxuICAgICAgICAvLyBpZiBpdCdzIHVwZGF0ZWQsIHdlIGRlc3Ryb3kgYW5kIHJlaHlkcmF0ZSB0aGUgZW50aXJlIGNvbnRhaW5lclxuICAgICAgICBpZiAodGhpcy5pbml0aWFsaXplZCAmJiB0aGlzLl9pZCAhPSBpZCkge1xuICAgICAgICAgICAgdGhpcy5faWQgPSBpZDtcbiAgICAgICAgICAgIHRoaXMubmdBZnRlclZpZXdJbml0KCk7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICB0aGlzLl9pZCA9IGlkO1xuICAgICAgICB9XG4gICAgfTtcblxuXG4gICAgcHJpdmF0ZSBfZ3JvdXAgPSBcImRlZmF1bHRcIjtcbiAgICBASW5wdXQoXCJncm91cFwiKSBzZXQgZ3JvdXAoZGF0YTogc3RyaW5nKSB7XG4gICAgICAgIGNvbnN0IGdyb3VwID0gc3RyaW5nVG9TbHVnKGRhdGEpO1xuXG4gICAgICAgIGlmICh0eXBlb2YgZ3JvdXAgIT0gXCJzdHJpbmdcIiB8fCAhZ3JvdXApIHJldHVybjtcblxuICAgICAgICAvLyBJZiB0aGUgZ3JvdXAgd2FzIHVwZGF0ZWQsIHJldHJ5IHRvIGJvb3RzdHJhcCBzb21ldGhpbmcgaW50byB0aGUgY29udGFpbmVyLlxuICAgICAgICBpZiAodGhpcy5pbml0aWFsaXplZCAmJiB0aGlzLl9ncm91cCAhPSBncm91cCkge1xuICAgICAgICAgICAgdGhpcy5fZ3JvdXAgPSBncm91cDtcblxuICAgICAgICAgICAgdGhpcy5uZ0FmdGVyVmlld0luaXQoKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX2dyb3VwID0gZ3JvdXA7XG4gICAgfVxuICAgIGdldCBncm91cCgpIHsgcmV0dXJuIHRoaXMuX2dyb3VwIH1cblxuICAgIHByaXZhdGUgX21hdGNoR3JvdXBzOiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB9O1xuICAgIHByaXZhdGUgX2lucHV0czogeyBba2V5OiBzdHJpbmddOiBhbnk7IH07XG4gICAgLyoqXG4gICAgICogQSBtYXAgb2YgaW5wdXRzIHRvIGJpbmQgdG8gdGhlIGNoaWxkLlxuICAgICAqIFN1cHBvcnRzIGNoYW5nZSBkZXRlY3Rpb24uIChNYXkgZmFpbCBvbiBkZWVwIEpTT04gY2hhbmdlcylcbiAgICAgKlxuICAgICAqIGBgYGh0bWxcbiAgICAgKiA8bGF6eS1sb2FkZXIgY29tcG9uZW50PVwiTXlMYXp5Q29tcG9uZW50XCJcbiAgICAgKiAgICAgICBbaW5wdXRzXT1cIntcbiAgICAgKiAgICAgICAgICBwcm9wMTogdHJ1ZSxcbiAgICAgKiAgICAgICAgICBwcm9wMjogZmFsc2UsXG4gICAgICogICAgICAgICAgY29tcGxleDoge1xuICAgICAqICAgICAgICAgICAgICBhOiB0cnVlLFxuICAgICAqICAgICAgICAgICAgICBiOiAwXG4gICAgICogICAgICAgICAgfVxuICAgICAqICAgICAgIH1cIlxuICAgICAqID5cbiAgICAgKiA8L2xhenktbG9hZGVyPlxuICAgICAqIGBgYFxuICAgICAqL1xuICAgIEBJbnB1dChcImlucHV0c1wiKSBzZXQgaW5wdXRzKGRhdGE6IHsgW2tleTogc3RyaW5nXTogYW55OyB9KSB7XG4gICAgICAgIGlmIChkYXRhID09IHVuZGVmaW5lZCkgcmV0dXJuO1xuXG4gICAgICAgIGxldCBwcmV2aW91cyA9IHRoaXMuX2lucHV0cztcbiAgICAgICAgdGhpcy5faW5wdXRzID0gZGF0YTtcbiAgICAgICAgaWYgKGRhdGEgPT0gdW5kZWZpbmVkKVxuICAgICAgICAgICAgY29uc29sZS50cmFjZShkYXRhKTtcblxuICAgICAgICBpZiAodGhpcy50YXJnZXRDb21wb25lbnRGYWN0b3J5KSB7XG4gICAgICAgICAgICBjb25zdCB7IGlucHV0cyB9ID0gdGhpcy50YXJnZXRDb21wb25lbnRGYWN0b3J5Lsm1Y21wO1xuXG4gICAgICAgICAgICBjb25zdCBjdXJyZW50S2V5cyA9IE9iamVjdC5rZXlzKGlucHV0cyk7XG5cbiAgICAgICAgICAgIGNvbnN0IG9sZEtleXMgPSBPYmplY3Qua2V5cyhwcmV2aW91cykuZmlsdGVyKGtleSA9PiBjdXJyZW50S2V5cy5pbmNsdWRlcyhrZXkpKTtcbiAgICAgICAgICAgIGNvbnN0IG5ld0tleXMgPSBPYmplY3Qua2V5cyhkYXRhKS5maWx0ZXIoa2V5ID0+IGN1cnJlbnRLZXlzLmluY2x1ZGVzKGtleSkpO1xuXG4gICAgICAgICAgICBjb25zdCByZW1vdmVkID0gb2xkS2V5cy5maWx0ZXIoa2V5ID0+ICFuZXdLZXlzLmluY2x1ZGVzKGtleSkpO1xuXG4gICAgICAgICAgICAvLyA/IHBlcmhhcHMgc2V0IHRvIG51bGwgb3IgdW5kZWZpbmVkIGluc3RlYWRcbiAgICAgICAgICAgIHJlbW92ZWQuZm9yRWFjaChrID0+IHRoaXMudGFyZ2V0Q29tcG9uZW50SW5zdGFuY2Vba10gPSBudWxsKTtcblxuICAgICAgICAgICAgdGhpcy5iaW5kSW5wdXRzKCk7XG4gICAgICAgIH1cbiAgICB9XG5cblxuICAgIHByaXZhdGUgb3V0cHV0U3Vic2NyaXB0aW9uczogeyBba2V5OiBzdHJpbmddOiBTdWJzY3JpcHRpb247IH0gPSB7fTtcbiAgICBwcml2YXRlIF9vdXRwdXRzOiB7IFtrZXk6IHN0cmluZ106IEZ1bmN0aW9uOyB9O1xuICAgIC8qKlxuICAgICAqIEEgbWFwIG9mIG91dHB1dHMgdG8gYmluZCBmcm9tIHRoZSBjaGlsZC5cbiAgICAgKiBTaG91bGQgc3VwcG9ydCBjaGFuZ2UgZGV0ZWN0aW9uLlxuICAgICAqIGBgYGh0bWxcbiAgICAgKiA8bGF6eS1sb2FkZXIgY29tcG9uZW50PVwiTXlMYXp5Q29tcG9uZW50XCJcbiAgICAgKiAgICAgICBbb3V0cHV0c109XCJ7XG4gICAgICogICAgICAgICAgIHByb3AzOiBvbk91dHB1dEZpcmVcbiAgICAgKiAgICAgICB9XCJcbiAgICAgKiA+XG4gICAgICogPC9sYXp5LWxvYWRlcj5cbiAgICAgKiBgYGBcbiAgICAgKi9cbiAgICBASW5wdXQoXCJvdXRwdXRzXCIpIHNldCBvdXRwdXRzKGRhdGE6IHsgW2tleTogc3RyaW5nXTogRnVuY3Rpb247IH0pIHtcbiAgICAgICAgbGV0IHByZXZpb3VzID0gdGhpcy5fb3V0cHV0cztcbiAgICAgICAgdGhpcy5fb3V0cHV0cyA9IGRhdGE7XG5cbiAgICAgICAgaWYgKHRoaXMudGFyZ2V0Q29tcG9uZW50RmFjdG9yeSkge1xuICAgICAgICAgICAgY29uc3QgeyBpbnB1dHMgfSA9IHRoaXMudGFyZ2V0Q29tcG9uZW50RmFjdG9yeS7JtWNtcDtcblxuICAgICAgICAgICAgY29uc3QgY3VycmVudEtleXMgPSBPYmplY3Qua2V5cyhpbnB1dHMpO1xuICAgICAgICAgICAgY29uc3QgcmVtb3ZlZCA9IE9iamVjdC5rZXlzKHByZXZpb3VzKS5maWx0ZXIoa2V5ID0+ICFjdXJyZW50S2V5cy5pbmNsdWRlcyhrZXkpKTtcblxuICAgICAgICAgICAgcmVtb3ZlZC5mb3JFYWNoKGsgPT4ge1xuICAgICAgICAgICAgICAgIC8vIFVuc3Vic2NyaWJlIGZyb20gb2JzZXJ2YWJsZVxuICAgICAgICAgICAgICAgIHRoaXMub3V0cHV0U3Vic2NyaXB0aW9uc1trXT8udW5zdWJzY3JpYmUoKTtcbiAgICAgICAgICAgICAgICBkZWxldGUgdGhpcy50YXJnZXRDb21wb25lbnRJbnN0YW5jZVtrXTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICB0aGlzLmJpbmRPdXRwdXRzKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBFbWl0cyBlcnJvcnMgZW5jb3VudGVyZWQgd2hlbiBsb2FkaW5nIGNvbXBvbmVudHNcbiAgICAgKi9cbiAgICBAT3V0cHV0KCkgY29tcG9uZW50TG9hZEVycm9yID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuXG4gICAgLyoqXG4gICAgICogRW1pdHMgd2hlbiB0aGUgY29tcG9uZW50IGlzIGZ1bGx5IGNvbnN0cnVjdGVkXG4gICAgICogYW5kIGhhZCBpdCdzIGlucHV0cyBhbmQgb3V0cHV0cyBib3VuZFxuICAgICAqID4gYmVmb3JlIGBPbkluaXRgXG4gICAgICpcbiAgICAgKiBSZXR1cm5zIHRoZSBhY3RpdmUgY2xhc3MgaW5zdGFuY2Ugb2YgdGhlIGxhenktbG9hZGVkIGNvbXBvbmVudFxuICAgICAqL1xuICAgIEBPdXRwdXQoKSBjb21wb25lbnRMb2FkZWQgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cblxuICAgIC8qKlxuICAgICAqIFRoaXMgaXMgYW4gaW5zdGFuY2Ugb2YgdGhlIGNvbXBvbmVudCB0aGF0IGlzIGN1cnJlbnRseSBsb2FkZWQuXG4gICAgICovXG4gICAgcHVibGljIGluc3RhbmNlOiBhbnk7XG5cblxuICAgIC8qKlxuICAgICAqIENvbnRhaW5lciB0aGF0IHByb3ZpZGVzIHRoZSBjb21wb25lbnQgZGF0YVxuICAgICAqL1xuICAgIHByaXZhdGUgdGFyZ2V0TW9kdWxlOiBDb21waWxlZEJ1bmRsZTtcblxuICAgIC8qKlxuICAgICAqIENvbXBvbmVudCBkZWZpbml0aW9uXG4gICAgICovXG4gICAgcHJpdmF0ZSB0YXJnZXRDb21wb25lbnRGYWN0b3J5OiBhbnk7XG5cbiAgICAvKipcbiAgICAgKiBBY3RpdmUgY29tcG9uZW50IGNvbnRhaW5lciByZWZlcmVuY2VcbiAgICAgKi9cbiAgICBwcml2YXRlIHRhcmdldENvbXBvbmVudENvbnRhaW5lclJlZjogQ29tcG9uZW50UmVmPGFueT47XG4gICAgcHJpdmF0ZSB0YXJnZXRSZWY6IGFueTtcbiAgICAvKipcbiAgICAgKiBSZWZlcmVuY2UgdG8gdGhlIGNvbXBvbmVudCBjbGFzcyBpbnN0YW5jZVxuICAgICAqL1xuICAgIHByaXZhdGUgdGFyZ2V0Q29tcG9uZW50SW5zdGFuY2U6IGFueTtcblxuICAgIC8qKlxuICAgICAqIFN1YnNjcmlwdGlvbiB3aXRoIHRydWUvZmFsc2Ugc3RhdGUgb24gd2hldGhlciB0aGUgZGlzdHJhY3RvciBzaG91bGQgYmVcbiAgICAgKi9cbiAgICBwcml2YXRlIGRpc3RyYWN0b3JTdWJzY3JpcHRpb246IFN1YnNjcmlwdGlvbjtcblxuICAgIHB1YmxpYyBjb25maWc6IE5neExhenlMb2FkZXJDb25maWc7XG4gICAgcHJpdmF0ZSBlcnI7XG4gICAgcHJpdmF0ZSB3YXJuO1xuICAgIHByaXZhdGUgbG9nO1xuXG4gICAgLy8gRm9yY2UgNTAwbXMgZGVsYXkgYmVmb3JlIHJldmVhbGluZyB0aGUgc3Bpbm5lclxuICAgIHByaXZhdGUgbG9hZGVyRW1pdHRlciA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbiAgICBwcml2YXRlIGNsZWFyTG9hZGVyJCA9IHRoaXMubG9hZGVyRW1pdHRlci5waXBlKGRlYm91bmNlVGltZSgzMDApKTtcbiAgICBwcml2YXRlIGxvYWRlclN1YjogU3Vic2NyaXB0aW9uO1xuICAgIHNob3dMb2FkZXIgPSB0cnVlOyAvLyB3aGV0aGVyIHdlIHJlbmRlciB0aGUgRE9NIGZvciB0aGUgc3Bpbm5lclxuICAgIGlzQ2xlYXJpbmdMb2FkZXIgPSBmYWxzZTsgLy8gc2hvdWxkIHRoZSBzcGlubmVyIHN0YXJ0IGZhZGluZyBvdXRcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwcml2YXRlIHNlcnZpY2U6IE5neExhenlMb2FkZXJTZXJ2aWNlLFxuICAgICAgICBAT3B0aW9uYWwoKSBwcml2YXRlIHZpZXdDb250YWluZXJSZWY6IFZpZXdDb250YWluZXJSZWYsXG4gICAgICAgIEBPcHRpb25hbCgpIHB1YmxpYyBkaWFsb2c6IERpYWxvZ1JlZixcbiAgICAgICAgQE9wdGlvbmFsKCkgQEluamVjdChNQVRfRElBTE9HX0RBVEEpIHB1YmxpYyBkaWFsb2dBcmd1bWVudHNcbiAgICApIHtcbiAgICAgICAgdGhpcy5jb25maWcgPSBOZ3hMYXp5TG9hZGVyU2VydmljZS5jb25maWc7XG4gICAgICAgIHRoaXMuZXJyID0gTmd4TGF6eUxvYWRlclNlcnZpY2UuY29uZmlnLmxvZ2dlci5lcnI7XG4gICAgICAgIHRoaXMud2FybiA9IE5neExhenlMb2FkZXJTZXJ2aWNlLmNvbmZpZy5sb2dnZXIud2FybjtcbiAgICAgICAgdGhpcy5sb2cgPSBOZ3hMYXp5TG9hZGVyU2VydmljZS5jb25maWcubG9nZ2VyLmxvZztcblxuICAgICAgICAvLyBGaXJzdCwgY2hlY2sgZm9yIGRpYWxvZyBhcmd1bWVudHNcbiAgICAgICAgaWYgKHRoaXMuZGlhbG9nQXJndW1lbnRzKSB7XG4gICAgICAgICAgICB0aGlzLmlucHV0cyA9IHRoaXMuZGlhbG9nQXJndW1lbnRzLmlucHV0cyB8fCB0aGlzLmRpYWxvZ0FyZ3VtZW50cy5kYXRhO1xuICAgICAgICAgICAgdGhpcy5vdXRwdXRzID0gdGhpcy5kaWFsb2dBcmd1bWVudHMub3V0cHV0cztcbiAgICAgICAgICAgIHRoaXMuaWQgPSB0aGlzLmRpYWxvZ0FyZ3VtZW50cy5pZDtcbiAgICAgICAgICAgIHRoaXMuZ3JvdXAgPSB0aGlzLmRpYWxvZ0FyZ3VtZW50cy5ncm91cDtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMubG9hZGVyU3ViID0gdGhpcy5jbGVhckxvYWRlciQuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuc2hvd0xvYWRlciA9IGZhbHNlO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGluaXRpYWxpemVkID0gZmFsc2U7XG4gICAgYXN5bmMgbmdBZnRlclZpZXdJbml0KCkge1xuICAgICAgICB0aGlzLm5nT25EZXN0cm95KGZhbHNlKTtcbiAgICAgICAgdGhpcy5pc0NsZWFyaW5nTG9hZGVyID0gZmFsc2U7XG4gICAgICAgIHRoaXMuc2hvd0xvYWRlciA9IHRydWU7XG5cbiAgICAgICAgdGhpcy5pbml0aWFsaXplZCA9IHRydWU7XG5cbiAgICAgICAgaWYgKCF0aGlzLl9pZCkge1xuICAgICAgICAgICAgdGhpcy53YXJuKFwiTm8gY29tcG9uZW50IHdhcyBzcGVjaWZpZWQhXCIpO1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMubG9hZERlZmF1bHQoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBfZW50cnkgPSB0aGlzLnNlcnZpY2UucmVzb2x2ZVJlZ2lzdHJhdGlvbkVudHJ5KHRoaXMuX2lkLCB0aGlzLl9ncm91cCk7XG4gICAgICAgICAgICBpZiAoIV9lbnRyeSB8fCAhX2VudHJ5LmVudHJ5KSB7XG4gICAgICAgICAgICAgICAgdGhpcy5lcnIoYEZhaWxlZCB0byBmaW5kIENvbXBvbmVudCAnJHt0aGlzLl9pZH0nIGluIGdyb3VwICcke3RoaXMuX2dyb3VwfScgaW4gcmVnaXN0cnkhYCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubG9hZERlZmF1bHQoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgeyBlbnRyeSwgbWF0Y2hHcm91cHMgfSA9IF9lbnRyeTtcbiAgICAgICAgICAgIHRoaXMuX21hdGNoR3JvdXBzID0gbWF0Y2hHcm91cHM7XG5cbiAgICAgICAgICAgIC8vIERvd25sb2FkIHRoZSBcIm1vZHVsZVwiICh0aGUgc3RhbmRhbG9uZSBjb21wb25lbnQpXG4gICAgICAgICAgICBjb25zdCBidW5kbGU6IENvbXBpbGVkQnVuZGxlID0gdGhpcy50YXJnZXRNb2R1bGUgPSBhd2FpdCBlbnRyeS5sb2FkKCk7XG5cblxuICAgICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc29tZSBjb3JydXB0aW9uIG9uIHRoZSBidW5kbGUuXG4gICAgICAgICAgICBpZiAoIWJ1bmRsZSB8fCB0eXBlb2YgYnVuZGxlICE9ICdvYmplY3QnIHx8IGJ1bmRsZVsnX19lc01vZHVsZSddIGFzIGFueSAhPT0gdHJ1ZSB8fCBidW5kbGUudG9TdHJpbmcoKSAhPSBcIltvYmplY3QgTW9kdWxlXVwiKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5lcnIoYEZhaWxlZCB0byBsb2FkIGNvbXBvbmVudC9tb2R1bGUgZm9yICcke3RoaXMuX2lkfSchIFBhcnNlZCByZXNvdXJjZSBpcyBpbnZhbGlkLmApO1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmxvYWRFcnJvcigpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBtb2R1bGVzID0gT2JqZWN0LmtleXMoYnVuZGxlKVxuICAgICAgICAgICAgICAgIC5tYXAoayA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGVudHJ5ID0gYnVuZGxlW2tdO1xuXG4gICAgICAgICAgICAgICAgICAgIC8vIFN0cmljdGx5IGNoZWNrIGZvciBleHBvcnRlZCBtb2R1bGVzIG9yIHN0YW5kYWxvbmUgY29tcG9uZW50c1xuICAgICAgICAgICAgICAgICAgICBpZiAodHlwZW9mIGVudHJ5ID09IFwiZnVuY3Rpb25cIiAmJiB0eXBlb2YgZW50cnlbXCLJtWZhY1wiXSA9PSBcImZ1bmN0aW9uXCIpXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZW50cnk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZpbHRlcihlID0+IGUgIT0gbnVsbClcbiAgICAgICAgICAgICAgICAuZmlsdGVyKGVudHJ5ID0+IHtcbiAgICAgICAgICAgICAgICAgICAgZW50cnlbJ19pc01vZHVsZSddID0gISFlbnRyeVsnybVtb2QnXTsgLy8gbW9kdWxlXG4gICAgICAgICAgICAgICAgICAgIGVudHJ5WydfaXNDb21wb25lbnQnXSA9ICEhZW50cnlbJ8m1Y21wJ107IC8vIGNvbXBvbmVudFxuXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiAoZW50cnlbJ19pc01vZHVsZSddIHx8IGVudHJ5WydfaXNDb21wb25lbnQnXSk7XG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIGlmIChtb2R1bGVzLmxlbmd0aCA9PSAwKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5lcnIoYENvbXBvbmVudC9Nb2R1bGUgbG9hZGVkIGZvciAnJHt0aGlzLl9pZH0nIGhhcyBubyBleHBvcnRlZCBjb21wb25lbnRzIG9yIG1vZHVsZXMhYCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubG9hZEVycm9yKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IGNvbXBvbmVudCA9IHRoaXMudGFyZ2V0Q29tcG9uZW50RmFjdG9yeSA9IHRoaXMuc2VydmljZS5yZXNvbHZlQ29tcG9uZW50KHRoaXMuX2lkLCBcImRlZmF1bHRcIiwgbW9kdWxlcyk7XG5cbiAgICAgICAgICAgIGlmICghY29tcG9uZW50KSB7XG4gICAgICAgICAgICAgICAgdGhpcy5lcnIoYENvbXBvbmVudCAnJHt0aGlzLl9pZH0nIGlzIGludmFsaWQgb3IgY29ycnVwdGVkIWApO1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmxvYWRFcnJvcigpO1xuICAgICAgICAgICAgfVxuXG5cbiAgICAgICAgICAgIC8vIEJvb3RzdHJhcCB0aGUgY29tcG9uZW50IGludG8gdGhlIGNvbnRhaW5lclxuICAgICAgICAgICAgY29uc3QgY29tcG9uZW50UmVmID0gdGhpcy50YXJnZXRDb21wb25lbnRDb250YWluZXJSZWYgPSB0aGlzLnRhcmdldENvbnRhaW5lci5jcmVhdGVDb21wb25lbnQoY29tcG9uZW50IGFzIGFueSk7XG4gICAgICAgICAgICB0aGlzLnRhcmdldFJlZiA9IHRoaXMudGFyZ2V0Q29udGFpbmVyLmluc2VydCh0aGlzLnRhcmdldENvbXBvbmVudENvbnRhaW5lclJlZi5ob3N0Vmlldyk7XG5cbiAgICAgICAgICAgIGNvbnN0IGluc3RhbmNlOiBhbnkgPSB0aGlzLnRhcmdldENvbXBvbmVudEluc3RhbmNlID0gY29tcG9uZW50UmVmWydpbnN0YW5jZSddO1xuXG4gICAgICAgICAgICB0aGlzLmJpbmRJbnB1dHMoKTtcbiAgICAgICAgICAgIHRoaXMuYmluZE91dHB1dHMoKTtcblxuICAgICAgICAgICAgdGhpcy5jb21wb25lbnRMb2FkZWQubmV4dChpbnN0YW5jZSk7XG4gICAgICAgICAgICB0aGlzLmluc3RhbmNlID0gaW5zdGFuY2U7XG5cbiAgICAgICAgICAgIC8vIExvb2sgZm9yIGFuIG9ic2VydmFibGUgY2FsbGVkIGlzTG9hZGluZyQgdGhhdCB3aWxsIG1ha2UgdXMgc2hvdy9oaWRlXG4gICAgICAgICAgICAvLyB0aGUgc2FtZSBkaXN0cmFjdG9yIHRoYXQgaXMgdXNlZCBvbiBiYXNpYyBsb2FkaW5nXG4gICAgICAgICAgICBjb25zdCBpc0xvYWRpbmckID0gaW5zdGFuY2VbJ25neFNob3dEaXN0cmFjdG9yJCddIGFzIEJlaGF2aW9yU3ViamVjdDxib29sZWFuPjtcblxuICAgICAgICAgICAgaWYgKGlzTG9hZGluZyQgJiYgdHlwZW9mIGlzTG9hZGluZyQuc3Vic2NyaWJlID09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgICAgICAgIHRoaXMuZGlzdHJhY3RvclN1YnNjcmlwdGlvbiA9IGlzTG9hZGluZyQuc3Vic2NyaWJlKGxvYWRpbmcgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAoIWxvYWRpbmcpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaXNDbGVhcmluZ0xvYWRlciA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvYWRlckVtaXR0ZXIuZW1pdCgpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5zaG93TG9hZGVyID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaXNDbGVhcmluZ0xvYWRlciA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmlzQ2xlYXJpbmdMb2FkZXIgPSB0cnVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBuYW1lID0gT2JqZWN0LmtleXMoYnVuZGxlKVswXTtcbiAgICAgICAgICAgIHRoaXMubG9nKGBMb2FkZWQgJyR7bmFtZX0nYCk7XG4gICAgICAgICAgICB0aGlzLmxvYWRlckVtaXR0ZXIuZW1pdCgpO1xuXG4gICAgICAgICAgICByZXR1cm4gY29tcG9uZW50UmVmO1xuICAgICAgICB9XG4gICAgICAgIGNhdGNoIChleCkge1xuXG4gICAgICAgICAgICBpZiAoaXNEZXZNb2RlKCkpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXCJDb21wb25lbnQgXCIgKyB0aGlzLl9pZCArIFwiIHRocmV3IGFuIGVycm9yIG9uIG1vdW50IVwiKTtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXCJUaGlzIHdpbGwgY2F1c2UgeW91IHRvIHNlZSBhIDQwNCBwYW5lbC5cIik7XG4gICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihleCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIE5ldHdvcmsgZXJyb3JzIHRocm93IGEgdG9hc3QgYW5kIHJldHVybiBhbiBlcnJvciBjb21wb25lbnRcbiAgICAgICAgICAgIGlmIChleCAmJiAhaXNEZXZNb2RlKCkpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiVW5jYXVnaHQgZXJyb3Igd2hlbiBsb2FkaW5nIGNvbXBvbmVudFwiKTtcbiAgICAgICAgICAgICAgICB0aHJvdyBleDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHRoaXMubG9hZERlZmF1bHQoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG5nT25EZXN0cm95KGNsZWFyQWxsID0gdHJ1ZSkge1xuICAgICAgICAvLyB1bnN1YnNjcmliZSBmcm9tIGFsbCBzdWJzY3JpcHRpb25zXG4gICAgICAgIE9iamVjdC5lbnRyaWVzKHRoaXMub3V0cHV0U3Vic2NyaXB0aW9ucykuZm9yRWFjaCgoW2tleSwgc3ViXSkgPT4ge1xuICAgICAgICAgICAgc3ViLnVuc3Vic2NyaWJlKCk7XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLm91dHB1dFN1YnNjcmlwdGlvbnMgPSB7fTtcblxuICAgICAgICAvLyBDbGVhciBhbGwgdGhpbmdzXG4gICAgICAgIGlmIChjbGVhckFsbCkge1xuICAgICAgICAgICAgdGhpcy5sb2FkZXJTdWI/LnVuc3Vic2NyaWJlKCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmRpc3RyYWN0b3JTdWJzY3JpcHRpb24/LnVuc3Vic2NyaWJlKCk7XG5cbiAgICAgICAgLy8gQ2xlYXIgdGFyZ2V0IGNvbnRhaW5lclxuICAgICAgICB0aGlzLnRhcmdldFJlZj8uZGVzdHJveSgpO1xuICAgICAgICB0aGlzLnRhcmdldENvbXBvbmVudENvbnRhaW5lclJlZj8uZGVzdHJveSgpO1xuICAgICAgICB0aGlzLnRhcmdldENvbnRhaW5lcj8uY2xlYXIoKTtcblxuICAgICAgICAvLyBXaXBlIHRoZSByZXN0IG9mIHRoZSBzdGF0ZSBjbGVhblxuICAgICAgICB0aGlzLnRhcmdldFJlZiA9IG51bGw7XG4gICAgICAgIHRoaXMudGFyZ2V0Q29tcG9uZW50Q29udGFpbmVyUmVmID0gbnVsbDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBCaW5kIHRoZSBpbnB1dCB2YWx1ZXMgdG8gdGhlIGNoaWxkIGNvbXBvbmVudC5cbiAgICAgKi9cbiAgICBwcml2YXRlIGJpbmRJbnB1dHMoKSB7XG4gICAgICAgIGlmICghdGhpcy5faW5wdXRzIHx8ICF0aGlzLnRhcmdldENvbXBvbmVudEluc3RhbmNlKSByZXR1cm47XG5cbiAgICAgICAgLy8gTWVyZ2UgbWF0Y2ggZ3JvdXBzXG4gICAgICAgIGlmICh0eXBlb2YgdGhpcy5fbWF0Y2hHcm91cHMgPT0gXCJvYmplY3RcIikge1xuICAgICAgICAgICAgT2JqZWN0LmVudHJpZXModGhpcy5fbWF0Y2hHcm91cHMpLmZvckVhY2goKFtrZXksIHZhbF0pID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHRoaXMuX2lucHV0c1trZXldID09ICd1bmRlZmluZWQnKVxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9pbnB1dHNba2V5XSA9IHZhbDtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gZm9yd2FyZC1iaW5kIGlucHV0c1xuICAgICAgICBjb25zdCB7IGlucHV0cyB9ID0gdGhpcy50YXJnZXRDb21wb25lbnRGYWN0b3J5Lsm1Y21wO1xuXG4gICAgICAgIC8vIFJldHVybnMgYSBsaXN0IG9mIGVudHJpZXMgdGhhdCBuZWVkIHRvIGJlIHNldFxuICAgICAgICAvLyBUaGlzIG1ha2VzIGl0IHNvIHRoYXQgdW5uZWNlc3Nhcnkgc2V0dGVycyBhcmUgbm90IGludm9rZWQuXG4gICAgICAgIGNvbnN0IHVwZGF0ZWQgPSBPYmplY3QuZW50cmllcyhpbnB1dHMpLmZpbHRlcigoW3BhcmVudEtleSwgY2hpbGRLZXldOiBbc3RyaW5nLCBzdHJpbmddKSA9PiB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy50YXJnZXRDb21wb25lbnRJbnN0YW5jZVtjaGlsZEtleV0gIT0gdGhpcy5faW5wdXRzW3BhcmVudEtleV07XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHVwZGF0ZWQuZm9yRWFjaCgoW3BhcmVudEtleSwgY2hpbGRLZXldOiBbc3RyaW5nLCBzdHJpbmddKSA9PiB7XG4gICAgICAgICAgICBpZiAodGhpcy5faW5wdXRzLmhhc093blByb3BlcnR5KHBhcmVudEtleSkpXG4gICAgICAgICAgICAgICAgdGhpcy50YXJnZXRDb21wb25lbnRJbnN0YW5jZVtjaGlsZEtleV0gPSB0aGlzLl9pbnB1dHNbcGFyZW50S2V5XTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQmluZCB0aGUgb3V0cHV0IGhhbmRsZXJzIHRvIHRoZSBsb2FkZWQgY2hpbGQgY29tcG9uZW50XG4gICAgICovXG4gICAgcHJpdmF0ZSBiaW5kT3V0cHV0cygpIHtcbiAgICAgICAgaWYgKCF0aGlzLl9vdXRwdXRzIHx8ICF0aGlzLnRhcmdldENvbXBvbmVudEluc3RhbmNlKSByZXR1cm47XG5cbiAgICAgICAgY29uc3QgeyBvdXRwdXRzIH0gPSB0aGlzLnRhcmdldENvbXBvbmVudEZhY3RvcnkuybVjbXA7XG5cbiAgICAgICAgLy8gR2V0IGEgbGlzdCBvZiB1bnJlZ2lzdGVyZWQgb3V0cHV0c1xuICAgICAgICBjb25zdCBuZXdPdXRwdXRzID0gT2JqZWN0LmVudHJpZXMob3V0cHV0cykuZmlsdGVyKChbcGFyZW50S2V5LCBjaGlsZEtleV06IFtzdHJpbmcsIHN0cmluZ10pID0+IHtcbiAgICAgICAgICAgIHJldHVybiAhdGhpcy5vdXRwdXRTdWJzY3JpcHRpb25zW3BhcmVudEtleV07XG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIFJldmVyc2UgYmluZCB2aWEgc3Vic2NyaXB0aW9uXG4gICAgICAgIG5ld091dHB1dHMuZm9yRWFjaCgoW3BhcmVudEtleSwgY2hpbGRLZXldOiBbc3RyaW5nLCBzdHJpbmddKSA9PiB7XG4gICAgICAgICAgICBpZiAodGhpcy5fb3V0cHV0cy5oYXNPd25Qcm9wZXJ0eShwYXJlbnRLZXkpKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgdGFyZ2V0OiBFdmVudEVtaXR0ZXI8dW5rbm93bj4gPSB0aGlzLnRhcmdldENvbXBvbmVudEluc3RhbmNlW2NoaWxkS2V5XTtcbiAgICAgICAgICAgICAgICBjb25zdCBvdXRwdXRzID0gdGhpcy5fb3V0cHV0cztcblxuICAgICAgICAgICAgICAgIC8vIEFuZ3VsYXIgZm9sa3MsIHN0b3AgbWFraW5nIHRoaXMgc28gZGlmZmljdWx0LlxuICAgICAgICAgICAgICAgIGNvbnN0IGN0eCA9IHRoaXMudmlld0NvbnRhaW5lclJlZlsnX2hvc3RMVmlldyddWzhdO1xuICAgICAgICAgICAgICAgIGNvbnN0IHN1YiA9IHRhcmdldC5zdWJzY3JpYmUob3V0cHV0c1twYXJlbnRLZXldLmJpbmQoY3R4KSk7IC8vIFN1YnNjcmlwdGlvblxuXG4gICAgICAgICAgICAgICAgdGhpcy5vdXRwdXRTdWJzY3JpcHRpb25zW3BhcmVudEtleV0gPSBzdWI7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIExvYWQgdGhlIFwiRGVmYXVsdFwiIGNvbXBvbmVudCAoNDA0KSBzY3JlZW4gbm9ybWFsbHkuXG4gICAgICogVGhpcyBpcyBzaG93biB3aGVuIHRoZSBjb21wb25lbnQgaWQgaXNuJ3QgaW4gdGhlXG4gICAgICogcmVnaXN0cnkgb3Igb3RoZXJ3aXNlIGRvZXNuJ3QgbWF0Y2hcbiAgICAgKlxuICAgICAqIFRoaXNcbiAgICAgKi9cbiAgICBwcml2YXRlIGxvYWREZWZhdWx0KCkge1xuICAgICAgICBpZiAodGhpcy5jb25maWcubm90Rm91bmRDb21wb25lbnQpXG4gICAgICAgICAgICB0aGlzLnRhcmdldENvbnRhaW5lci5jcmVhdGVDb21wb25lbnQodGhpcy5jb25maWcubm90Rm91bmRDb21wb25lbnQpO1xuXG4gICAgICAgIHRoaXMuc2hvd0xvYWRlciA9IGZhbHNlO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIExvYWQgdGhlIFwiRXJyb3JcIiBjb21wb25lbnQuXG4gICAgICogVGhpcyBpcyBzaG93biB3aGVuIHdlIGFyZSBhYmxlIHRvIHJlc29sdmUgdGhlIGNvbXBvbmVudFxuICAgICAqIGluIHRoZSByZWdpc3RyeSwgYnV0IGhhdmUgc29tZSBpc3N1ZSBib29zdHJhcHBpbmcgdGhlXG4gICAgICogY29tcG9uZW50IGludG8gdGhlIHZpZXdDb250YWluZXJcbiAgICAgKi9cbiAgICBwcml2YXRlIGxvYWRFcnJvcigpIHtcbiAgICAgICAgaWYgKHRoaXMuY29uZmlnLmVycm9yQ29tcG9uZW50KVxuICAgICAgICAgICAgdGhpcy50YXJnZXRDb250YWluZXIuY3JlYXRlQ29tcG9uZW50KHRoaXMuY29uZmlnLmVycm9yQ29tcG9uZW50KTtcblxuICAgICAgICB0aGlzLnNob3dMb2FkZXIgPSBmYWxzZTtcbiAgICB9XG59XG4iLCI8bmctY29udGFpbmVyICNjb250ZW50PjwvbmctY29udGFpbmVyPlxuXG48ZGl2IGNsYXNzPVwibmd4LWxhenktbG9hZGVyLWRpc3RyYWN0b3JcIiBbY2xhc3MuZGVzdHJveWluZ109XCJpc0NsZWFyaW5nTG9hZGVyXCI+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImNvbmZpZy5sb2FkZXJEaXN0cmFjdG9yQ29tcG9uZW50XCIgW25nQ29tcG9uZW50T3V0bGV0XT1cImNvbmZpZy5sb2FkZXJEaXN0cmFjdG9yQ29tcG9uZW50XCI+PC9uZy1jb250YWluZXI+XG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImNvbmZpZy5sb2FkZXJEaXN0cmFjdG9yVGVtcGxhdGVcIiBbbmdUZW1wbGF0ZU91dGxldF09XCJjb25maWcubG9hZGVyRGlzdHJhY3RvclRlbXBsYXRlXCIgW25nVGVtcGxhdGVPdXRsZXRDb250ZXh0XT1cInsgJyRpbXBsaWNpdCc6IGlucHV0cyB9XCI+PC9uZy1jb250YWluZXI+XG48L2Rpdj5cbiJdfQ==