UNPKG

ng-keyboard-shortcuts

Version:

Dead Simple Keyboard Shortcuts Management for Angular 2+

427 lines 64.1 kB
import { Inject, Injectable } from "@angular/core";
import { _INVERTED_SHIFT_MAP, _KEYCODE_MAP, _MAP, _SHIFT_MAP, _SPECIAL_CASES, modifiers } from "./keys";
import { BehaviorSubject, fromEvent, of, Subject, throwError, timer } from "rxjs";
import { AllowIn } from "./ng-keyboard-shortcuts.interfaces";
import { catchError, filter, first, map, repeat, scan, switchMap, takeUntil, tap, throttle } from "rxjs/operators";
import { allPass, any, difference, identity, isFunction, isNill, maxArrayProp } from "./utils";
import { DOCUMENT } from "@angular/common";
import * as i0 from "@angular/core";
/**
 * @ignore
 * @type {number}
 */
let guid = 0;
export class KeyboardShortcutsService {
    /**
     * @ignore
     */
    constructor(document) {
        this.document = document;
        /**
         * Parsed shortcuts
         * for each key create a predicate function
         */
        this._shortcuts = [];
        this._sequences = [];
        /**
         * Throttle the keypress event.
         */
        this.throttleTime = 0;
        this._pressed = new Subject();
        /**
         * Streams of pressed events, can be used instead or with a command.
         */
        this.pressed$ = this._pressed.asObservable();
        /**
         * Disable all keyboard shortcuts
         */
        this.disabled = false;
        this._shortcutsSub = new BehaviorSubject([]);
        this.shortcuts$ = this._shortcutsSub
            .pipe(filter((shortcuts) => !!shortcuts.length));
        this._ignored = [AllowIn.Input, AllowIn.Textarea, AllowIn.Select, AllowIn.ContentEditable];
        /**
         * @ignore
         * @param shortcut
         */
        this.isAllowed = (shortcut) => {
            const target = shortcut.event.target;
            const isContentEditable = !!target.getAttribute("contenteditable");
            const nodeName = isContentEditable ? AllowIn.ContentEditable : target.nodeName;
            if (target === shortcut.target) {
                return true;
            }
            // if (shortcut.allowIn.includes(AllowIn.ContentEditable)) {
            //     return !!target.getAttribute("contenteditable");
            // }
            if (shortcut.allowIn.length) {
                return !difference(this._ignored, shortcut.allowIn).includes(nodeName);
            }
            if (isContentEditable) {
                return false;
            }
            return !this._ignored.includes(target.nodeName);
        };
        /**
         * @ignore
         * @param event
         */
        this.mapEvent = (event) => {
            return this._shortcuts
                .filter((shortcut) => !shortcut.isSequence)
                .map((shortcut) => Object.assign({}, shortcut, {
                predicates: any(identity, shortcut.predicates.map((predicates) => allPass(predicates)(event))),
                event: event
            }))
                .filter((shortcut) => shortcut.predicates)
                .reduce((acc, shortcut) => (acc.priority > shortcut.priority ? acc : shortcut), {
                priority: 0
            });
        };
        /**
         * @ignore
         * Subscription for on destroy.
         */
        this.subscriptions = [];
        this.keydown$ = fromEvent(this.document, "keydown");
        /**
         * fixes for firefox prevent default
         * on click event on button focus:
         * see issue:
         * keeping this here for now, but it is commented out
         * Firefox reference bug:
         * https://bugzilla.mozilla.org/show_bug.cgi?id=1487102
         * and my repo:
         *
         * https://github.com/omridevk/ng-keyboard-shortcuts/issues/35
         */
        this.ignore$ = this.pressed$.pipe(filter((e) => e.event.defaultPrevented), switchMap(() => this.clicks$.pipe(first())), tap((e) => {
            e.preventDefault();
            e.stopPropagation();
        }), repeat());
        /**
         * @ignore
         */
        this.clicks$ = fromEvent(this.document, "click", { capture: true });
        this.keyup$ = fromEvent(this.document, "keyup");
        /**
         * @ignore
         */
        this.keydownCombo$ = this.keydown$.pipe(filter((_) => !this.disabled), map(this.mapEvent), filter((shortcut) => !shortcut.target || shortcut.event.target === shortcut.target), filter((shortcut) => isFunction(shortcut.command)), filter((shortcut) => this.isAllowed(shortcut)), tap((shortcut) => {
            if (!shortcut.preventDefault) {
                return;
            }
            shortcut.event.preventDefault();
            shortcut.event.stopPropagation();
        }), throttle((shortcut) => timer(shortcut.throttleTime)), tap((shortcut) => shortcut.command({ event: shortcut.event, key: shortcut.key })), tap((shortcut) => this._pressed.next({ event: shortcut.event, key: shortcut.key })), takeUntil(this.keyup$), repeat(), catchError((error) => throwError(error)));
        /**
         * @ignore
         */
        this.timer$ = new Subject();
        /**
         * @ignore
         */
        this.resetCounter$ = this.timer$
            .asObservable()
            .pipe(switchMap(() => timer(KeyboardShortcutsService.TIMEOUT_SEQUENCE)));
        this.keydownSequence$ = this.keydown$.pipe(map((event) => ({
            event,
            sequences: this._sequences
        })), scan((acc, arg) => {
            const { event } = arg;
            const currentLength = acc.events.length;
            const sequences = currentLength ? acc.sequences : arg.sequences;
            const [characters] = this.characterFromEvent(event);
            const allChars = Array.isArray(characters)
                ? [...characters, event.key]
                : [characters, event.key];
            const result = sequences
                .map((sequence) => {
                const sequences = sequence.sequence.filter((seque) => allChars.some((key) => (_SPECIAL_CASES[seque[currentLength]] ||
                    seque[currentLength]) === key));
                const partialMatch = sequences.length > 0;
                if (sequence.fullMatch) {
                    return sequence;
                }
                return {
                    ...sequence,
                    sequence: sequences,
                    partialMatch,
                    event: event,
                    fullMatch: partialMatch &&
                        this.isFullMatch({ command: sequence, events: acc.events })
                };
            })
                .filter((sequences) => sequences.partialMatch || sequences.fullMatch);
            const [match] = result;
            if (!match || this.modifiersOn(event)) {
                return { events: [], sequences: this._sequences };
            }
            /*
             * handle case of "?" sequence and "? a" sequence
             * need to determine which one to trigger.
             * if both match, we pick the longer one (? a) in this case.
             */
            const guess = maxArrayProp("priority", result);
            if (result.length > 1 && guess.fullMatch) {
                return { events: [], command: guess, sequences: this._sequences };
            }
            if (result.length > 1) {
                return {
                    events: [...acc.events, event],
                    command: result,
                    sequences: result
                };
            }
            if (match.fullMatch) {
                return { events: [], command: match, sequences: this._sequences };
            }
            return {
                events: [...acc.events, event],
                command: result,
                sequences: result
            };
        }, { events: [], sequences: [] }), switchMap(({ command }) => {
            if (Array.isArray(command)) {
                /*
                 * Add a timer to handle the case where for example:
                 * a sequence "?" is registered and "? a" is registered as well
                 * if the user does not hit any key for 500ms, the single sequence will trigger
                 * if any keydown event occur, this timer will reset, given a chance to complete
                 * the full sequence (? a) in this case.
                 * This delay only occurs when single key sequence is the beginning of another sequence.
                 */
                return timer(500).pipe(map(() => ({
                    command: command.filter((command) => command.fullMatch)[0]
                })));
            }
            return of({ command });
        }), takeUntil(this.pressed$), filter(({ command }) => command && command.fullMatch), map(({ command }) => command), filter((shortcut) => isFunction(shortcut.command)), filter((shortcut) => !shortcut.target || shortcut.event.target === shortcut.target), filter(this.isAllowed), tap((shortcut) => !shortcut.preventDefault || shortcut.event.preventDefault()), throttle((shortcut) => timer(shortcut.throttleTime)), tap((shortcut) => shortcut.command({ event: shortcut.event, key: shortcut.key })), tap((shortcut) => this._pressed.next({ event: shortcut.event, key: shortcut.key })), takeUntil(this.resetCounter$), repeat());
        /**
         * @ignore
         * transforms a shortcut to:
         * a predicate function
         */
        this.getKeys = (keys) => {
            return keys
                .map((key) => key.trim())
                .filter((key) => key !== "+")
                .map((key) => {
                // for modifiers like control key
                // look for event['ctrlKey']
                // otherwise use the keyCode
                key = _SPECIAL_CASES[key] || key;
                // eslint-disable-next-line no-prototype-builtins
                if (modifiers.hasOwnProperty(key)) {
                    return (event) => {
                        return !!event[modifiers[key]];
                    };
                }
                return (event) => {
                    const isUpper = key === key.toUpperCase();
                    const isNonAlpha = /[^a-zA-Z\d\s:]/.test(key);
                    const inShiftMap = _INVERTED_SHIFT_MAP[key];
                    const [characters] = this.characterFromEvent(event);
                    const allModifiers = Object.keys(modifiers).map((key) => {
                        return modifiers[key];
                    });
                    const hasModifiers = allModifiers.some((modifier) => event[modifier]);
                    const result = Array.isArray(characters)
                        ? [...characters, event.key]
                        : [characters, event.key];
                    // if has modifiers:
                    // we want to make sure it is not upper case letters
                    // (because upper has modifiers so we want continue the check)
                    // we also want to make sure it is not alphanumeric char like ? / ^ & and others (since those could require modifiers as well)
                    // we also want to check this only if the length of
                    // of the keys is one (i.e the command key is "?" or "c"
                    // this while check is here to verify that:
                    // if registered key like "e"
                    // it won't be fired when clicking ctrl + e, or any modifiers + the key
                    // we only want to trigger when the single key is clicked alone
                    // thus all these edge cases.
                    // hopefully this would cover all cases
                    // TODO:: find a way simplify this
                    if (hasModifiers &&
                        (!isUpper || isNonAlpha) &&
                        !inShiftMap &&
                        keys.length === 1) {
                        return false;
                    }
                    return result.some((char) => {
                        if (char === key && isUpper) {
                            return true;
                        }
                        return key === char;
                    });
                };
            });
        };
        this.subscriptions.push(this.keydownSequence$.subscribe(), this.keydownCombo$.subscribe()
        // this.ignore$.subscribe()
        );
    }
    /**
     * @ignore
     * @param command
     * @param events
     */
    isFullMatch({ command, events }) {
        if (!command) {
            return false;
        }
        return command.sequence.some((sequence) => {
            return sequence.length === events.length + 1;
        });
    }
    /**
     * @ignore
     */
    get shortcuts() {
        return this._shortcuts;
    }
    /**
     * @ignore
     * @param event
     */
    _characterFromEvent(event) {
        if (typeof event.which !== "number") {
            event.which = event.keyCode;
        }
        if (_SPECIAL_CASES[event.which]) {
            return [_SPECIAL_CASES[event.which], event.shiftKey];
        }
        if (_MAP[event.which]) {
            // for non keypress events the special maps are needed
            return [_MAP[event.which], event.shiftKey];
        }
        if (_KEYCODE_MAP[event.which]) {
            return [_KEYCODE_MAP[event.which], event.shiftKey];
        }
        // in case event key is lower case but registered key is upper case
        // return it in the lower case
        if (String.fromCharCode(event.which).toLowerCase() !== event.key) {
            return [String.fromCharCode(event.which).toLowerCase(), event.shiftKey];
        }
        return [event.key, event.shiftKey];
    }
    characterFromEvent(event) {
        const [key, shiftKey] = this._characterFromEvent(event);
        if (shiftKey && _SHIFT_MAP[key]) {
            return [_SHIFT_MAP[key], shiftKey];
        }
        return [key, shiftKey];
    }
    /**
     * @ignore
     * Remove subscription.
     */
    ngOnDestroy() {
        this.subscriptions.forEach((sub) => sub.unsubscribe());
    }
    /**
     * @ignore
     * @param shortcuts
     */
    isSequence(shortcuts) {
        return !shortcuts.some((shortcut) => shortcut.includes("+") || shortcut.length === 1);
    }
    /**
     * Add new shortcut/s
     */
    add(shortcuts) {
        shortcuts = Array.isArray(shortcuts) ? shortcuts : [shortcuts];
        const commands = this.parseCommand(shortcuts);
        commands.forEach((command) => {
            if (command.isSequence) {
                this._sequences.push(command);
                return;
            }
            this._shortcuts.push(command);
        });
        setTimeout(() => {
            this._shortcutsSub.next([...this._shortcuts, ...this._sequences]);
        });
        return commands.map((command) => command.id);
    }
    /**
     * Remove a command based on key or array of keys.
     * can be used for cleanup.
     * @returns
     * @param ids
     */
    remove(ids) {
        ids = Array.isArray(ids) ? ids : [ids];
        this._shortcuts = this._shortcuts.filter((shortcut) => !ids.includes(shortcut.id));
        this._sequences = this._sequences.filter((shortcut) => !ids.includes(shortcut.id));
        setTimeout(() => {
            this._shortcutsSub.next([...this._shortcuts, ...this._sequences]);
        });
        return this;
    }
    /**
     * Returns an observable of keyboard shortcut filtered by a specific key.
     * @param key - the key to filter the observable by.
     */
    select(key) {
        return this.pressed$.pipe(filter(({ event, key: eventKeys }) => {
            eventKeys = Array.isArray(eventKeys) ? eventKeys : [eventKeys];
            return !!eventKeys.find((eventKey) => eventKey === key);
        }));
    }
    /**
     * @ignore
     * @param event
     */
    modifiersOn(event) {
        return ["metaKey", "altKey", "ctrlKey"].some((mod) => event[mod]);
    }
    /**
     * @ignore
     * Parse each command using getKeys function
     */
    parseCommand(command) {
        const commands = Array.isArray(command) ? command : [command];
        return commands.map((command) => {
            const keys = Array.isArray(command.key) ? command.key : [command.key];
            const priority = Math.max(...keys.map((key) => key.split(" ").filter(identity).length));
            const predicates = keys.map((key) => this.getKeys(key.split(" ").filter(identity)));
            const isSequence = this.isSequence(keys);
            const sequence = isSequence
                ? keys.map((key) => key
                    .split(" ")
                    .filter(identity)
                    .map((key) => key.trim()))
                : [];
            return {
                ...command,
                isSequence,
                sequence: isSequence ? sequence : [],
                allowIn: command.allowIn || [],
                key: keys,
                id: `${guid++}`,
                throttle: isNill(command.throttleTime) ? this.throttleTime : command.throttleTime,
                priority: priority,
                predicates: predicates
            };
        });
    }
}
/**
 * @ignore
 * 2000 ms window to allow between key sequences otherwise
 * the sequence will reset.
 */
KeyboardShortcutsService.TIMEOUT_SEQUENCE = 1000;
KeyboardShortcutsService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.7", ngImport: i0, type: KeyboardShortcutsService, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
KeyboardShortcutsService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.7", ngImport: i0, type: KeyboardShortcutsService, providedIn: "root" });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.7", ngImport: i0, type: KeyboardShortcutsService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: "root"
                }]
        }], ctorParameters: function () { return [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [DOCUMENT]
                }] }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmcta2V5Ym9hcmQtc2hvcnRjdXRzLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9saWJzL25nLWtleWJvYXJkLXNob3J0Y3V0cy9zcmMvbGliL25nLWtleWJvYXJkLXNob3J0Y3V0cy5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFhLE1BQU0sZUFBZSxDQUFDO0FBQzlELE9BQU8sRUFDSCxtQkFBbUIsRUFDbkIsWUFBWSxFQUNaLElBQUksRUFDSixVQUFVLEVBQ1YsY0FBYyxFQUNkLFNBQVMsRUFDWixNQUFNLFFBQVEsQ0FBQztBQUNoQixPQUFPLEVBQ0gsZUFBZSxFQUdmLFNBQVMsRUFHVCxFQUFFLEVBRUYsT0FBTyxFQUVQLFVBQVUsRUFDVixLQUFLLEVBRVIsTUFBTSxNQUFNLENBQUM7QUFDZCxPQUFPLEVBQ0gsT0FBTyxFQUlWLE1BQU0sb0NBQW9DLENBQUM7QUFDNUMsT0FBTyxFQUNILFVBQVUsRUFDVixNQUFNLEVBQ04sS0FBSyxFQUNMLEdBQUcsRUFDSCxNQUFNLEVBQ04sSUFBSSxFQUNKLFNBQVMsRUFDVCxTQUFTLEVBQ1QsR0FBRyxFQUNILFFBQVEsRUFDWCxNQUFNLGdCQUFnQixDQUFDO0FBQ3hCLE9BQU8sRUFBRSxPQUFPLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDL0YsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGlCQUFpQixDQUFDOztBQUUzQzs7O0dBR0c7QUFDSCxJQUFJLElBQUksR0FBRyxDQUFDLENBQUM7QUFLYixNQUFNLE9BQU8sd0JBQXdCO0lBeVJqQzs7T0FFRztJQUNILFlBQXNDLFFBQWE7UUFBYixhQUFRLEdBQVIsUUFBUSxDQUFLO1FBM1JuRDs7O1dBR0c7UUFDSyxlQUFVLEdBQXFCLEVBQUUs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QUFVLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsRUFBRTtnQkFDcEMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxPQUFPLElBQUksRUFBRTtnQkFDOUIsR0FBRyxFQUFFLElBQUk7Z0JBQ1QsRUFBRSxFQUFFLEdBQUcsSUFBSSxFQUFFLEVBQUU7Z0JBQ2YsUUFBUSxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxZQUFZO2dCQUNqRixRQUFRLEVBQUUsUUFBUTtnQkFDbEIsVUFBVSxFQUFFLFVBQVU7YUFDUCxDQUFDO1FBQ3hCLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQzs7QUF2ZEQ7Ozs7R0FJRztBQUNxQix5Q0FBZ0IsR0FBRyxJQUFLLENBQUE7cUhBOUJ2Qyx3QkFBd0Isa0JBNFJiLFFBQVE7eUhBNVJuQix3QkFBd0IsY0FGckIsTUFBTTsyRkFFVCx3QkFBd0I7a0JBSHBDLFVBQVU7bUJBQUM7b0JBQ1IsVUFBVSxFQUFFLE1BQU07aUJBQ3JCOzswQkE2UmdCLE1BQU07MkJBQUMsUUFBUSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdCwgSW5qZWN0YWJsZSwgT25EZXN0cm95IH0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7XG4gICAgX0lOVkVSVEVEX1NISUZUX01BUCxcbiAgICBfS0VZQ09ERV9NQVAsXG4gICAgX01BUCxcbiAgICBfU0hJRlRfTUFQLFxuICAgIF9TUEVDSUFMX0NBU0VTLFxuICAgIG1vZGlmaWVyc1xufSBmcm9tIFwiLi9rZXlzXCI7XG5pbXBvcnQge1xuICAgIEJlaGF2aW9yU3ViamVjdCxcbiAgICBjb21iaW5lTGF0ZXN0LFxuICAgIGNvbmNhdE1hcCxcbiAgICBmcm9tRXZlbnQsXG4gICAgbWVyZ2VNYXAsXG4gICAgT2JzZXJ2YWJsZSxcbiAgICBvZixcbiAgICBzaGFyZVJlcGxheSxcbiAgICBTdWJqZWN0LFxuICAgIFN1YnNjcmlwdGlvbixcbiAgICB0aHJvd0Vycm9yLFxuICAgIHRpbWVyLFxuICAgIHppcFxufSBmcm9tIFwicnhqc1wiO1xuaW1wb3J0IHtcbiAgICBBbGxvd0luLFxuICAgIFBhcnNlZFNob3J0Y3V0LFxuICAgIFNob3J0Y3V0RXZlbnRPdXRwdXQsXG4gICAgU2hvcnRjdXRJbnB1dFxufSBmcm9tIFwiLi9uZy1rZXlib2FyZC1zaG9ydGN1dHMuaW50ZXJmYWNlc1wiO1xuaW1wb3J0IHtcbiAgICBjYXRjaEVycm9yLFxuICAgIGZpbHRlcixcbiAgICBmaXJzdCxcbiAgICBtYXAsXG4gICAgcmVwZWF0LFxuICAgIHNjYW4sXG4gICAgc3dpdGNoTWFwLFxuICAgIHRha2VVbnRpbCxcbiAgICB0YXAsXG4gICAgdGhyb3R0bGVcbn0gZnJvbSBcInJ4anMvb3BlcmF0b3JzXCI7XG5pbXBvcnQgeyBhbGxQYXNzLCBhbnksIGRpZmZlcmVuY2UsIGlkZW50aXR5LCBpc0Z1bmN0aW9uLCBpc05pbGwsIG1heEFycmF5UHJvcCB9IGZyb20gXCIuL3V0aWxzXCI7XG5pbXBvcnQgeyBET0NVTUVOVCB9IGZyb20gXCJAYW5ndWxhci9jb21tb25cIjtcblxuLyoqXG4gKiBAaWdub3JlXG4gKiBAdHlwZSB7bnVtYmVyfVxuICovXG5sZXQgZ3VpZCA9IDA7XG5cbkBJbmplY3RhYmxlKHtcbiAgICBwcm92aWRlZEluOiBcInJvb3RcIlxufSlcbmV4cG9ydCBjbGFzcyBLZXlib2FyZFNob3J0Y3V0c1NlcnZpY2UgaW1wbGVtZW50cyBPbkRlc3Ryb3kge1xuICAgIC8qKlxuICAgICAqIFBhcnNlZCBzaG9ydGN1dHNcbiAgICAgKiBmb3IgZWFjaCBrZXkgY3JlYXRlIGEgcHJlZGljYXRlIGZ1bmN0aW9uXG4gICAgICovXG4gICAgcHJpdmF0ZSBfc2hvcnRjdXRzOiBQYXJzZWRTaG9ydGN1dFtdID0gW107XG5cbiAgICBwcml2YXRlIF9zZXF1ZW5jZXM6IFBhcnNlZFNob3J0Y3V0W10gPSBbXTtcblxuICAgIC8qKlxuICAgICAqIFRocm90dGxlIHRoZSBrZXlwcmVzcyBldmVudC5cbiAgICAgKi9cbiAgICBwcml2YXRlIHRocm90dGxlVGltZSA9IDA7XG5cbiAgICBwcml2YXRlIF9wcmVzc2VkID0gbmV3IFN1YmplY3Q8U2hvcnRjdXRFdmVudE91dHB1dD4oKTtcblxuICAgIC8qKlxuICAgICAqIFN0cmVhbXMgb2YgcHJlc3NlZCBldmVudHMsIGNhbiBiZSB1c2VkIGluc3RlYWQgb3Igd2l0aCBhIGNvbW1hbmQuXG4gICAgICovXG4gICAgcHVibGljIHByZXNzZWQkID0gdGhpcy5fcHJlc3NlZC5hc09ic2VydmFibGUoKTtcblxuICAgIC8qKlxuICAgICAqIERpc2FibGUgYWxsIGtleWJvYXJkIHNob3J0Y3V0c1xuICAgICAqL1xuICAgIHByaXZhdGUgZGlzYWJsZWQgPSBmYWxzZTtcbiAgICAvKipcbiAgICAgKiBAaWdub3JlXG4gICAgICogMjAwMCBtcyB3aW5kb3cgdG8gYWxsb3cgYmV0d2VlbiBrZXkgc2VxdWVuY2VzIG90aGVyd2lzZVxuICAgICAqIHRoZSBzZXF1ZW5jZSB3aWxsIHJlc2V0LlxuICAgICAqL1xuICAgIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IFRJTUVPVVRfU0VRVUVOQ0UgPSAxMDAwO1xuXG4gICAgcHJpdmF0ZSBfc2hvcnRjdXRzU3ViID0gbmV3IEJlaGF2aW9yU3ViamVjdDxQYXJzZWRTaG9ydGN1dFtdPihbXSk7XG4gICAgcHVibGljIHNob3J0Y3V0cyQgPSB0aGlzLl9zaG9ydGN1dHNTdWJcbiAgICAgICAgLnBpcGUoZmlsdGVyKChzaG9ydGN1dHMpID0+ICEhc2hvcnRjdXRzLmxlbmd0aCkpO1xuXG4gICAgcHJpdmF0ZSBfaWdub3JlZCA9IFtBbGxvd0luLklucHV0LCBBbGxvd0luLlRleHRhcmVhLCBBbGxvd0luLlNlbGVjdCwgQWxsb3dJbi5Db250ZW50RWRpdGFibGVdO1xuXG4gICAgLyoqXG4gICAgICogQGlnbm9yZVxuICAgICAqIEBwYXJhbSBzaG9ydGN1dFxuICAgICAqL1xuICAgIHByaXZhdGUgaXNBbGxvd2VkID0gKHNob3J0Y3V0OiBQYXJzZWRTaG9ydGN1dCkgPT4ge1xuICAgICAgICBjb25zdCB0YXJnZXQgPSBzaG9ydGN1dC5ldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7XG4gICAgICAgIGNvbnN0IGlzQ29udGVudEVkaXRhYmxlID0gISF0YXJnZXQuZ2V0QXR0cmlidXRlKFwiY29udGVudGVkaXRhYmxlXCIpO1xuICAgICAgICBjb25zdCBub2RlTmFtZSA9IGlzQ29udGVudEVkaXRhYmxlID8gQWxsb3dJbi5Db250ZW50RWRpdGFibGUgOiB0YXJnZXQubm9kZU5hbWU7XG4gICAgICAgIGlmICh0YXJnZXQgPT09IHNob3J0Y3V0LnRhcmdldCkge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gaWYgKHNob3J0Y3V0LmFsbG93SW4uaW5jbHVkZXMoQWxsb3dJbi5Db250ZW50RWRpdGFibGUpKSB7XG4gICAgICAgIC8vICAgICByZXR1cm4gISF0YXJnZXQuZ2V0QXR0cmlidXRlKFwiY29udGVudGVkaXRhYmxlXCIpO1xuICAgICAgICAvLyB9XG4gICAgICAgIGlmIChzaG9ydGN1dC5hbGxvd0luLmxlbmd0aCkge1xuICAgICAgICAgICAgcmV0dXJuICFkaWZmZXJlbmNlKHRoaXMuX2lnbm9yZWQsIHNob3J0Y3V0LmFsbG93SW4pLmluY2x1ZGVzKG5vZGVOYW1lKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoaXNDb250ZW50RWRpdGFibGUpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gIXRoaXMuX2lnbm9yZWQuaW5jbHVkZXModGFyZ2V0Lm5vZGVOYW1lIGFzIEFsbG93SW4pO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBAaWdub3JlXG4gICAgICogQHBhcmFtIGV2ZW50XG4gICAgICovXG4gICAgcHJpdmF0ZSBtYXBFdmVudCA9IChldmVudCkgPT4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fc2hvcnRjdXRzXG4gICAgICAgICAgICAuZmlsdGVyKChzaG9ydGN1dCkgPT4gIXNob3J0Y3V0LmlzU2VxdWVuY2UpXG4gICAgICAgICAgICAubWFwKChzaG9ydGN1dCkgPT5cbiAgICAgICAgICAgICAgICBPYmplY3QuYXNzaWduKHt9LCBzaG9ydGN1dCwge1xuICAgICAgICAgICAgICAgICAgICBwcmVkaWNhdGVzOiBhbnkoXG4gICAgICAgICAgICAgICAgICAgICAgICBpZGVudGl0eSxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNob3J0Y3V0LnByZWRpY2F0ZXMubWFwKChwcmVkaWNhdGVzOiBhbnkpID0+IGFsbFBhc3MocHJlZGljYXRlcykoZXZlbnQpKVxuICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgICBldmVudDogZXZlbnRcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgLmZpbHRlcigoc2hvcnRjdXQpID0+IHNob3J0Y3V0LnByZWRpY2F0ZXMpXG4gICAgICAgICAgICAucmVkdWNlKChhY2MsIHNob3J0Y3V0KSA9PiAoYWNjLnByaW9yaXR5ID4gc2hvcnRjdXQucHJpb3JpdHkgPyBhY2MgOiBzaG9ydGN1dCksIHtcbiAgICAgICAgICAgICAgICBwcmlvcml0eTogMFxuICAgICAgICAgICAgfSBhcyBQYXJzZWRTaG9ydGN1dCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEBpZ25vcmVcbiAgICAgKiBTdWJzY3JpcHRpb24gZm9yIG9uIGRlc3Ryb3kuXG4gICAgICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBzdWJzY3JpcHRpb25zOiBTdWJzY3JpcHRpb25bXSA9IFtdO1xuXG4gICAgcHJpdmF0ZSBrZXlkb3duJDogT2JzZXJ2YWJsZTxLZXlib2FyZEV2ZW50PiA9IGZyb21FdmVudCh0aGlzLmRvY3VtZW50LCBcImtleWRvd25cIik7XG4gICAgLyoqXG4gICAgICogZml4ZXMgZm9yIGZpcmVmb3ggcHJldmVudCBkZWZhdWx0XG4gICAgICogb24gY2xpY2sgZXZlbnQgb24gYnV0dG9uIGZvY3VzOlxuICAgICAqIHNlZSBpc3N1ZTpcbiAgICAgKiBrZWVwaW5nIHRoaXMgaGVyZSBmb3Igbm93LCBidXQgaXQgaXMgY29tbWVudGVkIG91dFxuICAgICAqIEZpcmVmb3ggcmVmZXJlbmNlIGJ1ZzpcbiAgICAgKiBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xNDg3MTAyXG4gICAgICogYW5kIG15IHJlcG86XG4gICAgICpcbiAgICAgKiBodHRwczovL2dpdGh1Yi5jb20vb21yaWRldmsvbmcta2V5Ym9hcmQtc2hvcnRjdXRzL2lzc3Vlcy8zNVxuICAgICAqL1xuICAgIHByaXZhdGUgaWdub3JlJCA9IHRoaXMucHJlc3NlZCQucGlwZShcbiAgICAgICAgZmlsdGVyKChlKSA9PiBlLmV2ZW50LmRlZmF1bHRQcmV2ZW50ZWQpLFxuICAgICAgICBzd2l0Y2hNYXAoKCkgPT4gdGhpcy5jbGlja3MkLnBpcGUoZmlyc3QoKSkpLFxuICAgICAgICB0YXAoKGU6IGFueSkgPT4ge1xuICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgfSksXG4gICAgICAgIHJlcGVhdCgpXG4gICAgKTtcbiAgICAvKipcbiAgICAgKiBAaWdub3JlXG4gICAgICovXG4gICAgcHJpdmF0ZSBjbGlja3MkID0gZnJvbUV2ZW50KHRoaXMuZG9jdW1lbnQsIFwiY2xpY2tcIiwgeyBjYXB0dXJlOiB0cnVlIH0pO1xuXG4gICAgcHJpdmF0ZSBrZXl1cCQgPSBmcm9tRXZlbnQodGhpcy5kb2N1bWVudCwgXCJrZXl1cFwiKTtcblxuICAgIC8qKlxuICAgICAqIEBpZ25vcmVcbiAgICAgKi9cbiAgICBwcml2YXRlIGtleWRvd25Db21ibyQgPSB0aGlzLmtleWRvd24kLnBpcGUoXG4gICAgICAgIGZpbHRlcigoXykgPT4gIXRoaXMuZGlzYWJsZWQpLFxuICAgICAgICBtYXAodGhpcy5tYXBFdmVudCksXG4gICAgICAgIGZpbHRlcihcbiAgICAgICAgICAgIChzaG9ydGN1dDogUGFyc2VkU2hvcnRjdXQpID0+XG4gICAgICAgICAgICAgICAgIXNob3J0Y3V0LnRhcmdldCB8fCBzaG9ydGN1dC5ldmVudC50YXJnZXQgPT09IHNob3J0Y3V0LnRhcmdldFxuICAgICAgICApLFxuICAgICAgICBmaWx0ZXIoKHNob3J0Y3V0OiBQYXJzZWRTaG9ydGN1dCkgPT4gaXNGdW5jdGlvbihzaG9ydGN1dC5jb21tYW5kKSksXG4gICAgICAgIGZpbHRlcigoc2hvcnRjdXQpID0+IHRoaXMuaXNBbGxvd2VkKHNob3J0Y3V0KSksXG4gICAgICAgIHRhcCgoc2hvcnRjdXQpID0+IHtcbiAgICAgICAgICAgIGlmICghc2hvcnRjdXQucHJldmVudERlZmF1bHQpIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzaG9ydGN1dC5ldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgc2hvcnRjdXQuZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgIH0pLFxuICAgICAgICB0aHJvdHRsZSgoc2hvcnRjdXQpID0+IHRpbWVyKHNob3J0Y3V0LnRocm90dGxlVGltZSkpLFxuICAgICAgICB0YXAoKHNob3J0Y3V0KSA9PiBzaG9ydGN1dC5jb21tYW5kKHsgZXZlbnQ6IHNob3J0Y3V0LmV2ZW50LCBrZXk6IHNob3J0Y3V0LmtleSB9KSksXG4gICAgICAgIHRhcCgoc2hvcnRjdXQpID0+IHRoaXMuX3ByZXNzZWQubmV4dCh7IGV2ZW50OiBzaG9ydGN1dC5ldmVudCwga2V5OiBzaG9ydGN1dC5rZXkgfSkpLFxuICAgICAgICB0YWtlVW50aWwodGhpcy5rZXl1cCQpLFxuICAgICAgICByZXBlYXQoKSxcbiAgICAgICAgY2F0Y2hFcnJvcigoZXJyb3IpID0+IHRocm93RXJyb3IoZXJyb3IpKVxuICAgICk7XG5cbiAgICAvKipcbiAgICAgKiBAaWdub3JlXG4gICAgICovXG4gICAgcHJpdmF0ZSB0aW1lciQgPSBuZXcgU3ViamVjdCgpO1xuICAgIC8qKlxuICAgICAqIEBpZ25vcmVcbiAgICAgKi9cbiAgICBwcml2YXRlIHJlc2V0Q291bnRlciQgPSB0aGlzLnRpbWVyJFxuICAgICAgICAuYXNPYnNlcnZhYmxlKClcbiAgICAgICAgLnBpcGUoc3dpdGNoTWFwKCgpID0+IHRpbWVyKEtleWJvYXJkU2hvcnRjdXRzU2VydmljZS5USU1FT1VUX1NFUVVFTkNFKSkpO1xuXG4gICAgcHJpdmF0ZSBrZXlkb3duU2VxdWVuY2UkID0gdGhpcy5rZXlkb3duJC5waXBlKFxuICAgICAgICBtYXAoKGV2ZW50KSA9PiAoe1xuICAgICAgICAgICAgZXZlbnQsXG4gICAgICAgICAgICBzZXF1ZW5jZXM6IHRoaXMuX3NlcXVlbmNlc1xuICAgICAgICB9KSksXG4gICAgICAgIHNjYW4oXG4gICAgICAgICAgICAoYWNjOiB7IGV2ZW50czogYW55W107IGNvbW1hbmQ/OiBhbnk7IHNlcXVlbmNlczogYW55W10gfSwgYXJnOiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCB7IGV2ZW50IH0gPSBhcmc7XG4gICAgICAgICAgICAgICAgY29uc3QgY3VycmVudExlbmd0aCA9IGFjYy5ldmVudHMubGVuZ3RoO1xuICAgICAgICAgICAgICAgIGNvbnN0IHNlcXVlbmNlcyA9IGN1cnJlbnRMZW5ndGggPyBhY2Muc2VxdWVuY2VzIDogYXJnLnNlcXVlbmNlcztcbiAgICAgICAgICAgICAgICBjb25zdCBbY2hhcmFjdGVyc10gPSB0aGlzLmNoYXJhY3RlckZyb21FdmVudChldmVudCk7XG4gICAgICAgICAgICAgICAgY29uc3QgYWxsQ2hhcnMgPSBBcnJheS5pc0FycmF5KGNoYXJhY3RlcnMpXG4gICAgICAgICAgICAgICAgICAgID8gWy4uLmNoYXJhY3RlcnMsIGV2ZW50LmtleV1cbiAgICAgICAgICAgICAgICAgICAgOiBbY2hhcmFjdGVycywgZXZlbnQua2V5XTtcbiAgICAgICAgICAgICAgICBjb25zdCByZXN1bHQgPSBzZXF1ZW5jZXNcbiAgICAgICAgICAgICAgICAgICAgLm1hcCgoc2VxdWVuY2UpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHNlcXVlbmNlcyA9IHNlcXVlbmNlLnNlcXVlbmNlLmZpbHRlcigoc2VxdWUpID0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsQ2hhcnMuc29tZShcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGtleSkgPT5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChfU1BFQ0lBTF9DQVNFU1tzZXF1ZVtjdXJyZW50TGVuZ3RoXV0gfHxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXF1ZVtjdXJyZW50TGVuZ3RoXSkgPT09IGtleVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBwYXJ0aWFsTWF0Y2ggPSBzZXF1ZW5jZXMubGVuZ3RoID4gMDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChzZXF1ZW5jZS5mdWxsTWF0Y2gpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gc2VxdWVuY2U7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4uLnNlcXVlbmNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcXVlbmNlOiBzZXF1ZW5jZXMsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFydGlhbE1hdGNoLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV2ZW50OiBldmVudCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsTWF0Y2g6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcnRpYWxNYXRjaCAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmlzRnVsbE1hdGNoKHsgY29tbWFuZDogc2VxdWVuY2UsIGV2ZW50czogYWNjLmV2ZW50cyB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgLmZpbHRlcigoc2VxdWVuY2VzKSA9PiBzZXF1ZW5jZXMucGFydGlhbE1hdGNoIHx8IHNlcXVlbmNlcy5mdWxsTWF0Y2gpO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgW21hdGNoXSA9IHJlc3VsdDtcbiAgICAgICAgICAgICAgICBpZiAoIW1hdGNoIHx8IHRoaXMubW9kaWZpZXJzT24oZXZlbnQpKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB7IGV2ZW50czogW10sIHNlcXVlbmNlczogdGhpcy5fc2VxdWVuY2VzIH07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8qXG4gICAgICAgICAgICAgICAgICogaGFuZGxlIGNhc2Ugb2YgXCI/XCIgc2VxdWVuY2UgYW5kIFwiPyBhXCIgc2VxdWVuY2VcbiAgICAgICAgICAgICAgICAgKiBuZWVkIHRvIGRldGVybWluZSB3aGljaCBvbmUgdG8gdHJpZ2dlci5cbiAgICAgICAgICAgICAgICAgKiBpZiBib3RoIG1hdGNoLCB3ZSBwaWNrIHRoZSBsb25nZXIgb25lICg/IGEpIGluIHRoaXMgY2FzZS5cbiAgICAgICAgICAgICAgICAgKi9cbiAgICAgICAgICAgICAgICBjb25zdCBndWVzcyA9IG1heEFycmF5UHJvcChcInByaW9yaXR5XCIsIHJlc3VsdCk7XG4gICAgICAgICAgICAgICAgaWYgKHJlc3VsdC5sZW5ndGggPiAxICYmIGd1ZXNzLmZ1bGxNYXRjaCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4geyBldmVudHM6IFtdLCBjb21tYW5kOiBndWVzcywgc2VxdWVuY2VzOiB0aGlzLl9zZXF1ZW5jZXMgfTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKHJlc3VsdC5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBldmVudHM6IFsuLi5hY2MuZXZlbnRzLCBldmVudF0sXG4gICAgICAgICAgICAgICAgICAgICAgICBjb21tYW5kOiByZXN1bHQsXG4gICAgICAgICAgICAgICAgICAgICAgICBzZXF1ZW5jZXM6IHJlc3VsdFxuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAobWF0Y2guZnVsbE1hdGNoKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB7IGV2ZW50czogW10sIGNvbW1hbmQ6IG1hdGNoLCBzZXF1ZW5jZXM6IHRoaXMuX3NlcXVlbmNlcyB9O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgICBldmVudHM6IFsuLi5hY2MuZXZlbnRzLCBldmVudF0sXG4gICAgICAgICAgICAgICAgICAgIGNvbW1hbmQ6IHJlc3VsdCxcbiAgICAgICAgICAgICAgICAgICAgc2VxdWVuY2VzOiByZXN1bHRcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHsgZXZlbnRzOiBbXSwgc2VxdWVuY2VzOiBbXSB9XG4gICAgICAgICksXG4gICAgICAgIHN3aXRjaE1hcCgoeyBjb21tYW5kIH0pID0+IHtcbiAgICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KGNvbW1hbmQpKSB7XG4gICAgICAgICAgICAgICAgLypcbiAgICAgICAgICAgICAgICAgKiBBZGQgYSB0aW1lciB0byBoYW5kbGUgdGhlIGNhc2Ugd2hlcmUgZm9yIGV4YW1wbGU6XG4gICAgICAgICAgICAgICAgICogYSBzZXF1ZW5jZSBcIj9cIiBpcyByZWdpc3RlcmVkIGFuZCBcIj8gYVwiIGlzIHJlZ2lzdGVyZWQgYXMgd2VsbFxuICAgICAgICAgICAgICAgICAqIGlmIHRoZSB1c2VyIGRvZXMgbm90IGhpdCBhbnkga2V5IGZvciA1MDBtcywgdGhlIHNpbmdsZSBzZXF1ZW5jZSB3aWxsIHRyaWdnZXJcbiAgICAgICAgICAgICAgICAgKiBpZiBhbnkga2V5ZG93biBldmVudCBvY2N1ciwgdGhpcyB0aW1lciB3aWxsIHJlc2V0LCBnaXZlbiBhIGNoYW5jZSB0byBjb21wbGV0ZVxuICAgICAgICAgICAgICAgICAqIHRoZSBmdWxsIHNlcXVlbmNlICg/IGEpIGluIHRoaXMgY2FzZS5cbiAgICAgICAgICAgICAgICAgKiBUaGlzIGRlbGF5IG9ubHkgb2NjdXJzIHdoZW4gc2luZ2xlIGtleSBzZXF1ZW5jZSBpcyB0aGUgYmVnaW5uaW5nIG9mIGFub3RoZXIgc2VxdWVuY2UuXG4gICAgICAgICAgICAgICAgICovXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRpbWVyKDUwMCkucGlwZShcbiAgICAgICAgICAgICAgICAgICAgbWFwKCgpID0+ICh7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb21tYW5kOiBjb21tYW5kLmZpbHRlcigoY29tbWFuZCkgPT4gY29tbWFuZC5mdWxsTWF0Y2gpWzBdXG4gICAgICAgICAgICAgICAgICAgIH0pKVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gb2YoeyBjb21tYW5kIH0pO1xuICAgICAgICB9KSxcbiAgICAgICAgdGFrZVVudGlsKHRoaXMucHJlc3NlZCQpLFxuICAgICAgICBmaWx0ZXIoKHsgY29tbWFuZCB9KSA9PiBjb21tYW5kICYmIGNvbW1hbmQuZnVsbE1hdGNoKSxcbiAgICAgICAgbWFwKCh7IGNvbW1hbmQgfSkgPT4gY29tbWFuZCksXG4gICAgICAgIGZpbHRlcigoc2hvcnRjdXQ6IFBhcnNlZFNob3J0Y3V0KSA9PiBpc0Z1bmN0aW9uKHNob3J0Y3V0LmNvbW1hbmQpKSxcbiAgICAgICAgZmlsdGVyKFxuICAgICAgICAgICAgKHNob3J0Y3V0OiBQYXJzZWRTaG9ydGN1dCkgPT5cbiAgICAgICAgICAgICAgICAhc2hvcnRjdXQudGFyZ2V0IHx8IHNob3J0Y3V0LmV2ZW50LnRhcmdldCA9PT0gc2hvcnRjdXQudGFyZ2V0XG4gICAgICAgICksXG4gICAgICAgIGZpbHRlcih0aGlzLmlzQWxsb3dlZCksXG4gICAgICAgIHRhcCgoc2hvcnRjdXQpID0+ICFzaG9ydGN1dC5wcmV2ZW50RGVmYXVsdCB8fCBzaG9ydGN1dC5ldmVudC5wcmV2ZW50RGVmYXVsdCgpKSxcbiAgICAgICAgdGhyb3R0bGUoKHNob3J0Y3V0KSA9PiB0aW1lcihzaG9ydGN1dC50aHJvdHRsZVRpbWUpKSxcbiAgICAgICAgdGFwKChzaG9ydGN1dCkgPT4gc2hvcnRjdXQuY29tbWFuZCh7IGV2ZW50OiBzaG9ydGN1dC5ldmVudCwga2V5OiBzaG9ydGN1dC5rZXkgfSkpLFxuICAgICAgICB0YXAoKHNob3J0Y3V0KSA9PiB0aGlzLl9wcmVzc2VkLm5leHQoeyBldmVudDogc2hvcnRjdXQuZXZlbnQsIGtleTogc2hvcnRjdXQua2V5IH0pKSxcbiAgICAgICAgdGFrZVVudGlsKHRoaXMucmVzZXRDb3VudGVyJCksXG4gICAgICAgIHJlcGVhdCgpXG4gICAgKTtcblxuICAgIC8qKlxuICAgICAqIEBpZ25vcmVcbiAgICAgKiBAcGFyYW0gY29tbWFuZFxuICAgICAqIEBwYXJhbSBldmVudHNcbiAgICAgKi9cbiAgICBwcml2YXRlIGlzRnVsbE1hdGNoKHsgY29tbWFuZCwgZXZlbnRzIH0pIHtcbiAgICAgICAgaWYgKCFjb21tYW5kKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNvbW1hbmQuc2VxdWVuY2Uuc29tZSgoc2VxdWVuY2UpID0+IHtcbiAgICAgICAgICAgIHJldHVybiBzZXF1ZW5jZS5sZW5ndGggPT09IGV2ZW50cy5sZW5ndGggKyAxO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBAaWdub3JlXG4gICAgICovXG4gICAgcHJpdmF0ZSBnZXQgc2hvcnRjdXRzKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fc2hvcnRjdXRzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEBpZ25vcmVcbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcihASW5qZWN0KERPQ1VNRU5UKSBwcml2YXRlIGRvY3VtZW50OiBhbnkpIHtcbiAgICAgICAgdGhpcy5zdWJzY3JpcHRpb25zLnB1c2goXG4gICAgICAgICAgICB0aGlzLmtleWRvd25TZXF1ZW5jZSQuc3Vic2NyaWJlKCksXG4gICAgICAgICAgICB0aGlzLmtleWRvd25Db21ibyQuc3Vic2NyaWJlKClcbiAgICAgICAgICAgIC8vIHRoaXMuaWdub3JlJC5zdWJzY3JpYmUoKVxuICAgICAgICApO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEBpZ25vcmVcbiAgICAgKiBAcGFyYW0gZXZlbnRcbiAgICAgKi9cbiAgICBwcml2YXRlIF9jaGFyYWN0ZXJGcm9tRXZlbnQoZXZlbnQpOiBbc3RyaW5nLCBib29sZWFuXSB7XG4gICAgICAgIGlmICh0eXBlb2YgZXZlbnQud2hpY2ggIT09IFwibnVtYmVyXCIpIHtcbiAgICAgICAgICAgIGV2ZW50LndoaWNoID0gZXZlbnQua2V5Q29kZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoX1NQRUNJQUxfQ0FTRVNbZXZlbnQud2hpY2hdKSB7XG4gICAgICAgICAgICByZXR1cm4gW19TUEVDSUFMX0NBU0VTW2V2ZW50LndoaWNoXSwgZXZlbnQuc2hpZnRLZXldO1xuICAgICAgICB9XG4gICAgICAgIGlmIChfTUFQW2V2ZW50LndoaWNoXSkge1xuICAgICAgICAgICAgLy8gZm9yIG5vbiBrZXlwcmVzcyBldmVudHMgdGhlIHNwZWNpYWwgbWFwcyBhcmUgbmVlZGVkXG4gICAgICAgICAgICByZXR1cm4gW19NQVBbZXZlbnQud2hpY2hdLCBldmVudC5zaGlmdEtleV07XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoX0tFWUNPREVfTUFQW2V2ZW50LndoaWNoXSkge1xuICAgICAgICAgICAgcmV0dXJuIFtfS0VZQ09ERV9NQVBbZXZlbnQud2hpY2hdLCBldmVudC5zaGlmdEtleV07XG4gICAgICAgIH1cbiAgICAgICAgLy8gaW4gY2FzZSBldmVudCBrZXkgaXMgbG93ZXIgY2FzZSBidXQgcmVnaXN0ZXJlZCBrZXkgaXMgdXBwZXIgY2FzZVxuICAgICAgICAvLyByZXR1cm4gaXQgaW4gdGhlIGxvd2VyIGNhc2VcbiAgICAgICAgaWYgKFN0cmluZy5mcm9tQ2hhckNvZGUoZXZlbnQud2hpY2gpLnRvTG93ZXJDYXNlKCkgIT09IGV2ZW50LmtleSkge1xuICAgICAgICAgICAgcmV0dXJuIFtTdHJpbmcuZnJvbUNoYXJDb2RlKGV2ZW50LndoaWNoKS50b0xvd2VyQ2FzZSgpLCBldmVudC5zaGlmdEtleV07XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFtldmVudC5rZXksIGV2ZW50LnNoaWZ0S2V5XTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGNoYXJhY3RlckZyb21FdmVudChldmVudCkge1xuICAgICAgICBjb25zdCBba2V5LCBzaGlmdEtleV0gPSB0aGlzLl9jaGFyYWN0ZXJGcm9tRXZlbnQoZXZlbnQpO1xuICAgICAgICBpZiAoc2hpZnRLZXkgJiYgX1NISUZUX01BUFtrZXldKSB7XG4gICAgICAgICAgICByZXR1cm4gW19TSElGVF9NQVBba2V5XSwgc2hpZnRLZXldO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBba2V5LCBzaGlmdEtleV07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQGlnbm9yZVxuICAgICAqIFJlbW92ZSBzdWJzY3JpcHRpb24uXG4gICAgICovXG4gICAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuc3Vic2NyaXB0aW9ucy5mb3JFYWNoKChzdWIpID0+IHN1Yi51bnN1YnNjcmliZSgpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBAaWdub3JlXG4gICAgICogQHBhcmFtIHNob3J0Y3V0c1xuICAgICAqL1xuICAgIHByaXZhdGUgaXNTZXF1ZW5jZShzaG9ydGN1dHM6IHN0cmluZ1tdKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiAhc2hvcnRjdXRzLnNvbWUoKHNob3J0Y3V0KSA9PiBzaG9ydGN1dC5pbmNsdWRlcyhcIitcIikgfHwgc2hvcnRjdXQubGVuZ3RoID09PSAxKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBZGQgbmV3IHNob3J0Y3V0L3NcbiAgICAgKi9cbiAgICBwdWJsaWMgYWRkKHNob3J0Y3V0czogU2hvcnRjdXRJbnB1dFtdIHwgU2hvcnRjdXRJbnB1dCk6IHN0cmluZ1tdIHtcbiAgICAgICAgc2hvcnRjdXRzID0gQXJyYXkuaXNBcnJheShzaG9ydGN1dHMpID8gc2hvcnRjdXRzIDogW3Nob3J0Y3V0c107XG4gICAgICAgIGNvbnN0IGNvbW1hbmRzID0gdGhpcy5wYXJzZUNvbW1hbmQoc2hvcnRjdXRzKTtcbiAgICAgICAgY29tbWFuZHMuZm9yRWFjaCgoY29tbWFuZCkgPT4ge1xuICAgICAgICAgICAgaWYgKGNvbW1hbmQuaXNTZXF1ZW5jZSkge1xuICAgICAgICAgICAgICAgIHRoaXMuX3NlcXVlbmNlcy5wdXNoKGNvbW1hbmQpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuX3Nob3J0Y3V0cy5wdXNoKGNvbW1hbmQpO1xuICAgICAgICB9KTtcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLl9zaG9ydGN1dHNTdWIubmV4dChbLi4udGhpcy5fc2hvcnRjdXRzLCAuLi50aGlzLl9zZXF1ZW5jZXNdKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBjb21tYW5kcy5tYXAoKGNvbW1hbmQpID0+IGNvbW1hbmQuaWQpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbW92ZSBhIGNvbW1hbmQgYmFzZWQgb24ga2V5IG9yIGFycmF5IG9mIGtleXMuXG4gICAgICogY2FuIGJlIHVzZWQgZm9yIGNsZWFudXAuXG4gICAgICogQHJldHVybnNcbiAgICAgKiBAcGFyYW0gaWRzXG4gICAgICovXG4gICAgcHVibGljIHJlbW92ZShpZHM6IHN0cmluZyB8IHN0cmluZ1tdKTogS2V5Ym9hcmRTaG9ydGN1dHNTZXJ2aWNlIHtcbiAgICAgICAgaWRzID0gQXJyYXkuaXNBcnJheShpZHMpID8gaWRzIDogW2lkc107XG4gICAgICAgIHRoaXMuX3Nob3J0Y3V0cyA9IHRoaXMuX3Nob3J0Y3V0cy5maWx0ZXIoKHNob3J0Y3V0KSA9PiAhaWRzLmluY2x1ZGVzKHNob3J0Y3V0LmlkKSk7XG4gICAgICAgIHRoaXMuX3NlcXVlbmNlcyA9IHRoaXMuX3NlcXVlbmNlcy5maWx0ZXIoKHNob3J0Y3V0KSA9PiAhaWRzLmluY2x1ZGVzKHNob3J0Y3V0LmlkKSk7XG4gICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5fc2hvcnRjdXRzU3ViLm5leHQoWy4uLnRoaXMuX3Nob3J0Y3V0cywgLi4udGhpcy5fc2VxdWVuY2VzXSk7XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGFuIG9ic2VydmFibGUgb2Yga2V5Ym9hcmQgc2hvcnRjdXQgZmlsdGVyZWQgYnkgYSBzcGVjaWZpYyBrZXkuXG4gICAgICogQHBhcmFtIGtleSAtIHRoZSBrZXkgdG8gZmlsdGVyIHRoZSBvYnNlcnZhYmxlIGJ5LlxuICAgICAqL1xuICAgIHB1YmxpYyBzZWxlY3Qoa2V5OiBzdHJpbmcpOiBPYnNlcnZhYmxlPFNob3J0Y3V0RXZlbnRPdXRwdXQ+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMucHJlc3NlZCQucGlwZShcbiAgICAgICAgICAgIGZpbHRlcigoeyBldmVudCwga2V5OiBldmVudEtleXMgfSkgPT4ge1xuICAgICAgICAgICAgICAgIGV2ZW50S2V5cyA9IEFycmF5LmlzQXJyYXkoZXZlbnRLZXlzKSA/IGV2ZW50S2V5cyA6IFtldmVudEtleXNdO1xuICAgICAgICAgICAgICAgIHJldHVybiAhIWV2ZW50S2V5cy5maW5kKChldmVudEtleSkgPT4gZXZlbnRLZXkgPT09IGtleSk7XG4gICAgICAgICAgICB9KVxuICAgICAgICApO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEBpZ25vcmVcbiAgICAgKiB0cmFuc2Zvcm1zIGEgc2hvcnRjdXQgdG86XG4gICAgICogYSBwcmVkaWNhdGUgZnVuY3Rpb25cbiAgICAgKi9cbiAgICBwcml2YXRlIGdldEtleXMgPSAoa2V5czogc3RyaW5nW10pID0+IHtcbiAgICAgICAgcmV0dXJuIGtleXNcbiAgICAgICAgICAgIC5tYXAoKGtleSkgPT4ga2V5LnRyaW0oKSlcbiAgICAgICAgICAgIC5maWx0ZXIoKGtleSkgPT4ga2V5ICE9PSBcIitcIilcbiAgICAgICAgICAgIC5tYXAoKGtleSkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIGZvciBtb2RpZmllcnMgbGlrZSBjb250cm9sIGtleVxuICAgICAgICAgICAgICAgIC8vIGxvb2sgZm9yIGV2ZW50WydjdHJsS2V5J11cbiAgICAgICAgICAgICAgICAvLyBvdGhlcndpc2UgdXNlIHRoZSBrZXlDb2RlXG4gICAgICAgICAgICAgICAga2V5ID0gX1NQRUNJQUxfQ0FTRVNba2V5XSB8fCBrZXk7XG4gICAgICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXByb3RvdHlwZS1idWlsdGluc1xuICAgICAgICAgICAgICAgIGlmIChtb2RpZmllcnMuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gISFldmVudFttb2RpZmllcnNba2V5XV07XG4gICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgcmV0dXJuIChldmVudDogS2V5Ym9hcmRFdmVudCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBpc1VwcGVyID0ga2V5ID09PSBrZXkudG9VcHBlckNhc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgaXNOb25BbHBoYSA9IC9bXmEtekEtWlxcZFxcczpdLy50ZXN0KGtleSk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGluU2hpZnRNYXAgPSBfSU5WRVJURURfU0hJRlRfTUFQW2tleV07XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IFtjaGFyYWN0ZXJzXSA9IHRoaXMuY2hhcmFjdGVyRnJvbUV2ZW50KGV2ZW50KTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgYWxsTW9kaWZpZXJzID0gT2JqZWN0LmtleXMobW9kaWZpZXJzKS5tYXAoKGtleSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG1vZGlmaWVyc1trZXldO1xuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgaGFzTW9kaWZpZXJzID0gYWxsTW9kaWZpZXJzLnNvbWUoKG1vZGlmaWVyKSA9PiBldmVudFttb2RpZmllcl0pO1xuXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IEFycmF5LmlzQXJyYXkoY2hhcmFjdGVycylcbiAgICAgICAgICAgICAgICAgICAgICAgID8gWy4uLmNoYXJhY3RlcnMsIGV2ZW50LmtleV1cbiAgICAgICAgICAgICAgICAgICAgICAgIDogW2NoYXJhY3RlcnMsIGV2ZW50LmtleV07XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gaWYgaGFzIG1vZGlmaWVyczpcbiAgICAgICAgICAgICAgICAgICAgLy8gd2Ugd2FudCB0byBtYWtlIHN1cmUgaXQgaXMgbm90IHVwcGVyIGNhc2UgbGV0dGVyc1xuICAgICAgICAgICAgICAgICAgICAvLyAoYmVjYXVzZSB1cHBlciBoYXMgbW9kaWZpZXJzIHNvIHdlIHdhbnQgY29udGludWUgdGhlIGNoZWNrKVxuICAgICAgICAgICAgICAgICAgICAvLyB3ZSBhbHNvIHdhbnQgdG8gbWFrZSBzdXJlIGl0IGlzIG5vdCBhbHBoYW51bWVyaWMgY2hhciBsaWtlID8gLyBeICYgYW5kIG90aGVycyAoc2luY2UgdGhvc2UgY291bGQgcmVxdWlyZSBtb2RpZmllcnMgYXMgd2VsbClcbiAgICAgICAgICAgICAgICAgICAgLy8gd2UgYWxzbyB3YW50IHRvIGNoZWNrIHRoaXMgb25seSBpZiB0aGUgbGVuZ3RoIG9mXG4gICAgICAgICAgICAgICAgICAgIC8vIG9mIHRoZSBrZXlzIGlzIG9uZSAoaS5lIHRoZSBjb21tYW5kIGtleSBpcyBcIj9cIiBvciBcImNcIlxuICAgICAgICAgICAgICAgICAgICAvLyB0aGlzIHdoaWxlIGNoZWNrIGlzIGhlcmUgdG8gdmVyaWZ5IHRoYXQ6XG4gICAgICAgICAgICAgICAgICAgIC8vIGlmIHJlZ2lzdGVyZWQga2V5IGxpa2UgXCJlXCJcbiAgICAgICAgICAgICAgICAgICAgLy8gaXQgd29uJ3QgYmUgZmlyZWQgd2hlbiBjbGlja2luZyBjdHJsICsgZSwgb3IgYW55IG1vZGlmaWVycyArIHRoZSBrZXlcbiAgICAgICAgICAgICAgICAgICAgLy8gd2Ugb25seSB3YW50IHRvIHRyaWdnZXIgd2hlbiB0aGUgc2luZ2xlIGtleSBpcyBjbGlja2VkIGFsb25lXG4gICAgICAgICAgICAgICAgICAgIC8vIHRodXMgYWxsIHRoZXNlIGVkZ2UgY2FzZXMuXG4gICAgICAgICAgICAgICAgICAgIC8vIGhvcGVmdWxseSB0aGlzIHdvdWxkIGNvdmVyIGFsbCBjYXNlc1xuICAgICAgICAgICAgICAgICAgICAvLyBUT0RPOjogZmluZCBhIHdheSBzaW1wbGlmeSB0aGlzXG4gICAgICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgICAgICAgIGhhc01vZGlmaWVycyAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgKCFpc1VwcGVyIHx8IGlzTm9uQWxwaGEpICYmXG4gICAgICAgICAgICAgICAgICAgICAgICAhaW5TaGlmdE1hcCAmJlxuICAgICAgICAgICAgICAgICAgICAgICAga2V5cy5sZW5ndGggPT09IDFcbiAgICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdC5zb21lKChjaGFyKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoY2hhciA9PT0ga2V5ICYmIGlzVXBwZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBrZXkgPT09IGNoYXI7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgLyoqXG4gICAgICogQGlnbm9yZVxuICAgICAqIEBwYXJhbSBldmVudFxuICAgICAqL1xuICAgIHByaXZhdGUgbW9kaWZpZXJzT24oZXZlbnQpIHtcbiAgICAgICAgcmV0dXJuIFtcIm1ldGFLZXlcIiwgXCJhbHRLZXlcIiwgXCJjdHJsS2V5XCJdLnNvbWUoKG1vZCkgPT4gZXZlbnRbbW9kXSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQGlnbm9yZVxuICAgICAqIFBhcnNlIGVhY2ggY29tbWFuZCB1c2luZyBnZXRLZXlzIGZ1bmN0aW9uXG4gICAgICovXG4gICAgcHJpdmF0ZSBwYXJzZUNvbW1hbmQoY29tbWFuZDogU2hvcnRjdXRJbnB1dCB8IFNob3J0Y3V0SW5wdXRbXSk6IFBhcnNlZFNob3J0Y3V0W10ge1xuICAgICAgICBjb25zdCBjb21tYW5kcyA9IEFycmF5LmlzQXJyYXkoY29tbWFuZCkgPyBjb21tYW5kIDogW2NvbW1hbmRdO1xuICAgICAgICByZXR1cm4gY29tbWFuZHMubWFwKChjb21tYW5kKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBrZXlzID0gQXJyYXkuaXNBcnJheShjb21tYW5kLmtleSkgPyBjb21tYW5kLmtleSA6IFtjb21tYW5kLmtleV07XG4gICAgICAgICAgICBjb25zdCBwcmlvcml0eSA9IE1hdGgubWF4KC4uLmtleXMubWFwKChrZXkpID0+IGtleS5zcGxpdChcIiBcIikuZmlsdGVyKGlkZW50aXR5KS5sZW5ndGgpKTtcbiAgICAgICAgICAgIGNvbnN0IHByZWRpY2F0ZXMgPSBrZXlzLm1hcCgoa2V5KSA9PiB0aGlzLmdldEtleXMoa2V5LnNwbGl0KFwiIFwiKS5maWx0ZXIoaWRlbnRpdHkpKSk7XG4gICAgICAgICAgICBjb25zdCBpc1NlcXVlbmNlID0gdGhpcy5pc1NlcXVlbmNlKGtleXMpO1xuICAgICAgICAgICAgY29uc3Qgc2VxdWVuY2UgPSBpc1NlcXVlbmNlXG4gICAgICAgICAgICAgICAgPyBrZXlzLm1hcCgoa2V5KSA9PlxuICAgICAgICAgICAgICAgICAgICAgIGtleVxuICAgICAgICAgICAgICAgICAgICAgICAgICAuc3BsaXQoXCIgXCIpXG4gICAgICAgICAgICAgICAgICAgICAgICAgIC5maWx0ZXIoaWRlbnRpdHkpXG4gICAgICAgICAgICAgICAgICAgICAgICAgIC5tYXAoKGtleSkgPT4ga2V5LnRyaW0oKSlcbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICA6IFtdO1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAuLi5jb21tYW5kLFxuICAgICAgICAgICAgICAgIGlzU2VxdWVuY2UsXG4gICAgICAgICAgICAgICAgc2VxdWVuY2U6IGlzU2VxdWVuY2UgPyBzZXF1ZW5jZSA6IFtdLFxuICAgICAgICAgICAgICAgIGFsbG93SW46IGNvbW1hbmQuYWxsb3dJbiB8fCBbXSxcbiAgICAgICAgICAgICAgICBrZXk6IGtleXMsXG4gICAgICAgICAgICAgICAgaWQ6IGAke2d1aWQrK31gLFxuICAgICAgICAgICAgICAgIHRocm90dGxlOiBpc05pbGwoY29tbWFuZC50aHJvdHRsZVRpbWUpID8gdGhpcy50aHJvdHRsZVRpbWUgOiBjb21tYW5kLnRocm90dGxlVGltZSxcbiAgICAgICAgICAgICAgICBwcmlvcml0eTogcHJpb3JpdHksXG4gICAgICAgICAgICAgICAgcHJlZGljYXRlczogcHJlZGljYXRlc1xuICAgICAgICAgICAgfSBhcyBQYXJzZWRTaG9ydGN1dDtcbiAgICAgICAgfSk7XG4gICAgfVxufVxuIl19