UNPKG

ngx-dynamic-hooks

Version:

Automatically insert live Angular components into a dynamic string of content (based on their selector or any pattern of your choice) and render the result in the DOM.

145 lines 20.1 kB
import { Injectable } from '@angular/core'; import { matchAll } from './utils'; import { getParseOptionDefaults } from '../settings/options'; import * as i0 from "@angular/core"; import * as i1 from "./logger"; /** * A utility service to easily parse hooks from text content */ export class HookFinder { constructor(logger) { this.logger = logger; } /** * Finds all text hooks in a piece of content, e.g. <hook>...</hook>, and returns their positions * * @param content - The text to parse * @param openingTagRegex - The regex for the opening tag * @param closingTagRegex - The regex for the closing tag * @param includeNested - Whether to include nested hooks in the result * @param options - The current ParseOptions */ find(content, openingTagRegex, closingTagRegex, includeNested, options = getParseOptionDefaults()) { if (!closingTagRegex) { return this.findSingletagHooks(content, openingTagRegex); } else { return this.findEnclosingHooks(content, openingTagRegex, closingTagRegex, includeNested, options); } } /** * Finds all text hooks that are non-enclosing in a piece of text, e.g. <hook> * * @param content - The text to search * @param hookRegex - The regex to use for the hook */ findSingletagHooks(content, hookRegex) { const result = []; // Find all hooks const openingTagMatches = matchAll(content, hookRegex); for (const match of openingTagMatches) { result.push({ openingTagStartIndex: match.index, openingTagEndIndex: match.index + match[0].length, closingTagStartIndex: null, closingTagEndIndex: null, }); } return result; } /** * Finds all text hooks that are enclosing in a piece of text, e.g. <hook>...</hook> * * Correctly finding enclosing hooks requires a programmatic parser rather then just regex alone, as regex cannot handle * patterns that are potentially nested within themselves. * * - If the content between the opening and closing is lazy (.*?), it would take the first closing tag after the opening tag, * regardless if it belongs to the opening tag or actually a nested hook. This would falsely match the first and third tag * in this example: '<hook><hook></hook></hook>' * * - If the content between the opening and closing is greedy (.*), it would only end on the last closing tag in the string, * ignoring any previous closing tags. This would falsely match the first and fourth tag in this example: * '<hook></hook><hook></hook>' * * There is no regex that works for both scenarios. This method therefore manually counts and compares the opening tags with the closing tags. * * @param content - The text to parse * @param openingTagRegex - The regex for the opening tag * @param closingTagRegex - The regex for the closing tag * @param includeNested - Whether to include nested hooks in the result * @param options - The current parseOptions */ findEnclosingHooks(content, openingTagRegex, closingTagRegex, includeNested, options = getParseOptionDefaults()) { const allTags = []; const result = []; // Find all opening tags const openingTagMatches = matchAll(content, openingTagRegex); for (const match of openingTagMatches) { allTags.push({ isOpening: true, value: match[0], startIndex: match.index, endIndex: match.index + match[0].length }); } // Find all closing tags const closingTagMatches = matchAll(content, closingTagRegex); for (const match of closingTagMatches) { allTags.push({ isOpening: false, value: match[0], startIndex: match.index, endIndex: match.index + match[0].length }); } // Sort by startIndex allTags.sort((a, b) => a.startIndex - b.startIndex); // Create HookPositions by figuring out which opening tag belongs to which closing tag const openedTags = []; allTagsLoop: for (const [index, tag] of allTags.entries()) { // Any subsequent tag is only allowed to start after previous tag has ended if (index > 0 && tag.startIndex < allTags[index - 1].endIndex) { this.logger.warn(['Syntax error - New tag "' + tag.value + '" started at position ' + tag.startIndex + ' before previous tag "' + allTags[index - 1].value + '" ended at position ' + allTags[index - 1].endIndex + '. Ignoring.'], options); continue; } // Opening or closing tag? if (tag.isOpening) { openedTags.push(tag); } else { // Syntax error: Closing tag without preceding opening tag. Syntax error. if (openedTags.length === 0) { this.logger.warn(['Syntax error - Closing tag without preceding opening tag found: "' + tag.value + '". Ignoring.'], options); continue; } // If nested hooks not allowed and more than one tag is open, discard both this closing tag and the latest opening tag if (includeNested === false && openedTags.length > 1) { openedTags.pop(); continue; } // Valid hook! Add to result array const openingTag = openedTags[openedTags.length - 1]; result.push({ openingTagStartIndex: openingTag.startIndex, openingTagEndIndex: openingTag.startIndex + openingTag.value.length, closingTagStartIndex: tag.startIndex, closingTagEndIndex: tag.startIndex + tag.value.length }); openedTags.pop(); } } if (openedTags.length > 0) { this.logger.warn(['Syntax error - Opening tags without corresponding closing tags found.'], options); } return result; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HookFinder, deps: [{ token: i1.Logger }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HookFinder, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HookFinder, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.Logger }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaG9va0ZpbmRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1keW5hbWljLWhvb2tzL3NyYy9saWIvc2VydmljZXMvdXRpbHMvaG9va0ZpbmRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTNDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFFbkMsT0FBTyxFQUFFLHNCQUFzQixFQUFnQixNQUFNLHFCQUFxQixDQUFDOzs7QUFFM0U7O0dBRUc7QUFJSCxNQUFNLE9BQU8sVUFBVTtJQUVyQixZQUFvQixNQUFjO1FBQWQsV0FBTSxHQUFOLE1BQU0sQ0FBUTtJQUFHLENBQUM7SUFFdEM7Ozs7Ozs7O09BUUc7SUFDSCxJQUFJLENBQUMsT0FBZSxFQUFFLGVBQXVCLEVBQUUsZUFBd0IsRUFBRSxhQUF1QixFQUFFLFVBQXdCLHNCQUFzQixFQUFFO1FBQ2hKLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUNyQixPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLEVBQUUsZUFBZSxDQUFDLENBQUM7UUFDM0QsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLEVBQUUsZUFBZSxFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQUUsT0FBTyxDQUFDLENBQUE7UUFDbkcsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7T0FLRztJQUNILGtCQUFrQixDQUFDLE9BQWUsRUFBRSxTQUFpQjtRQUNuRCxNQUFNLE1BQU0sR0FBbUIsRUFBRSxDQUFDO1FBRWxDLGlCQUFpQjtRQUNqQixNQUFNLGlCQUFpQixHQUFHLFFBQVEsQ0FBQyxPQUFPLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFFdkQsS0FBSyxNQUFNLEtBQUssSUFBSSxpQkFBaUIsRUFBRSxDQUFDO1lBQ3RDLE1BQU0s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QUNWLFVBQVUsRUFBRSxNQUFNO2lCQUNuQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEhvb2tQb3NpdGlvbiB9IGZyb20gJy4uLy4uL2ludGVyZmFjZXNQdWJsaWMnO1xuaW1wb3J0IHsgbWF0Y2hBbGwgfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7IExvZ2dlciB9IGZyb20gJy4vbG9nZ2VyJztcbmltcG9ydCB7IGdldFBhcnNlT3B0aW9uRGVmYXVsdHMsIFBhcnNlT3B0aW9ucyB9IGZyb20gJy4uL3NldHRpbmdzL29wdGlvbnMnO1xuXG4vKipcbiAqIEEgdXRpbGl0eSBzZXJ2aWNlIHRvIGVhc2lseSBwYXJzZSBob29rcyBmcm9tIHRleHQgY29udGVudFxuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBIb29rRmluZGVyIHtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGxvZ2dlcjogTG9nZ2VyKSB7fVxuXG4gIC8qKlxuICAgKiBGaW5kcyBhbGwgdGV4dCBob29rcyBpbiBhIHBpZWNlIG9mIGNvbnRlbnQsIGUuZy4gPGhvb2s+Li4uPC9ob29rPiwgYW5kIHJldHVybnMgdGhlaXIgcG9zaXRpb25zXG4gICAqIFxuICAgKiBAcGFyYW0gY29udGVudCAtIFRoZSB0ZXh0IHRvIHBhcnNlXG4gICAqIEBwYXJhbSBvcGVuaW5nVGFnUmVnZXggLSBUaGUgcmVnZXggZm9yIHRoZSBvcGVuaW5nIHRhZ1xuICAgKiBAcGFyYW0gY2xvc2luZ1RhZ1JlZ2V4IC0gVGhlIHJlZ2V4IGZvciB0aGUgY2xvc2luZyB0YWdcbiAgICogQHBhcmFtIGluY2x1ZGVOZXN0ZWQgLSBXaGV0aGVyIHRvIGluY2x1ZGUgbmVzdGVkIGhvb2tzIGluIHRoZSByZXN1bHRcbiAgICogQHBhcmFtIG9wdGlvbnMgLSBUaGUgY3VycmVudCBQYXJzZU9wdGlvbnNcbiAgICovXG4gIGZpbmQoY29udGVudDogc3RyaW5nLCBvcGVuaW5nVGFnUmVnZXg6IFJlZ0V4cCwgY2xvc2luZ1RhZ1JlZ2V4PzogUmVnRXhwLCBpbmNsdWRlTmVzdGVkPzogYm9vbGVhbiwgb3B0aW9uczogUGFyc2VPcHRpb25zID0gZ2V0UGFyc2VPcHRpb25EZWZhdWx0cygpKTogSG9va1Bvc2l0aW9uW10ge1xuICAgIGlmICghY2xvc2luZ1RhZ1JlZ2V4KSB7XG4gICAgICByZXR1cm4gdGhpcy5maW5kU2luZ2xldGFnSG9va3MoY29udGVudCwgb3BlbmluZ1RhZ1JlZ2V4KTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHRoaXMuZmluZEVuY2xvc2luZ0hvb2tzKGNvbnRlbnQsIG9wZW5pbmdUYWdSZWdleCwgY2xvc2luZ1RhZ1JlZ2V4LCBpbmNsdWRlTmVzdGVkLCBvcHRpb25zKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kcyBhbGwgdGV4dCBob29rcyB0aGF0IGFyZSBub24tZW5jbG9zaW5nIGluIGEgcGllY2Ugb2YgdGV4dCwgZS5nLiA8aG9vaz5cbiAgICpcbiAgICogQHBhcmFtIGNvbnRlbnQgLSBUaGUgdGV4dCB0byBzZWFyY2hcbiAgICogQHBhcmFtIGhvb2tSZWdleCAtIFRoZSByZWdleCB0byB1c2UgZm9yIHRoZSBob29rXG4gICAqL1xuICBmaW5kU2luZ2xldGFnSG9va3MoY29udGVudDogc3RyaW5nLCBob29rUmVnZXg6IFJlZ0V4cCk6IEhvb2tQb3NpdGlvbltdIHtcbiAgICBjb25zdCByZXN1bHQ6IEhvb2tQb3NpdGlvbltdID0gW107XG5cbiAgICAvLyBGaW5kIGFsbCBob29rc1xuICAgIGNvbnN0IG9wZW5pbmdUYWdNYXRjaGVzID0gbWF0Y2hBbGwoY29udGVudCwgaG9va1JlZ2V4KTtcblxuICAgIGZvciAoY29uc3QgbWF0Y2ggb2Ygb3BlbmluZ1RhZ01hdGNoZXMpIHtcbiAgICAgIHJlc3VsdC5wdXNoKHtcbiAgICAgICAgb3BlbmluZ1RhZ1N0YXJ0SW5kZXg6IG1hdGNoLmluZGV4LFxuICAgICAgICBvcGVuaW5nVGFnRW5kSW5kZXg6IG1hdGNoLmluZGV4ICsgbWF0Y2hbMF0ubGVuZ3RoLFxuICAgICAgICBjbG9zaW5nVGFnU3RhcnRJbmRleDogbnVsbCxcbiAgICAgICAgY2xvc2luZ1RhZ0VuZEluZGV4OiBudWxsLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kcyBhbGwgdGV4dCBob29rcyB0aGF0IGFyZSBlbmNsb3NpbmcgaW4gYSBwaWVjZSBvZiB0ZXh0LCBlLmcuIDxob29rPi4uLjwvaG9vaz5cbiAgICpcbiAgICogQ29ycmVjdGx5IGZpbmRpbmcgZW5jbG9zaW5nIGhvb2tzIHJlcXVpcmVzIGEgcHJvZ3JhbW1hdGljIHBhcnNlciByYXRoZXIgdGhlbiBqdXN0IHJlZ2V4IGFsb25lLCBhcyByZWdleCBjYW5ub3QgaGFuZGxlXG4gICAqIHBhdHRlcm5zIHRoYXQgYXJlIHBvdGVudGlhbGx5IG5lc3RlZCB3aXRoaW4gdGhlbXNlbHZlcy5cbiAgICpcbiAgICogLSBJZiB0aGUgY29udGVudCBiZXR3ZWVuIHRoZSBvcGVuaW5nIGFuZCBjbG9zaW5nIGlzIGxhenkgKC4qPyksIGl0IHdvdWxkIHRha2UgdGhlIGZpcnN0IGNsb3NpbmcgdGFnIGFmdGVyIHRoZSBvcGVuaW5nIHRhZyxcbiAgICogICByZWdhcmRsZXNzIGlmIGl0IGJlbG9uZ3MgdG8gdGhlIG9wZW5pbmcgdGFnIG9yIGFjdHVhbGx5IGEgbmVzdGVkIGhvb2suIFRoaXMgd291bGQgZmFsc2VseSBtYXRjaCB0aGUgZmlyc3QgYW5kIHRoaXJkIHRhZ1xuICAgKiAgIGluIHRoaXMgZXhhbXBsZTogJzxob29rPjxob29rPjwvaG9vaz48L2hvb2s+J1xuICAgKlxuICAgKiAtIElmIHRoZSBjb250ZW50IGJldHdlZW4gdGhlIG9wZW5pbmcgYW5kIGNsb3NpbmcgaXMgZ3JlZWR5ICguKiksIGl0IHdvdWxkIG9ubHkgZW5kIG9uIHRoZSBsYXN0IGNsb3NpbmcgdGFnIGluIHRoZSBzdHJpbmcsXG4gICAqICAgaWdub3JpbmcgYW55IHByZXZpb3VzIGNsb3NpbmcgdGFncy4gVGhpcyB3b3VsZCBmYWxzZWx5IG1hdGNoIHRoZSBmaXJzdCBhbmQgZm91cnRoIHRhZyBpbiB0aGlzIGV4YW1wbGU6XG4gICAqICAgJzxob29rPjwvaG9vaz48aG9vaz48L2hvb2s+J1xuICAgKlxuICAgKiBUaGVyZSBpcyBubyByZWdleCB0aGF0IHdvcmtzIGZvciBib3RoIHNjZW5hcmlvcy4gVGhpcyBtZXRob2QgdGhlcmVmb3JlIG1hbnVhbGx5IGNvdW50cyBhbmQgY29tcGFyZXMgdGhlIG9wZW5pbmcgdGFncyB3aXRoIHRoZSBjbG9zaW5nIHRhZ3MuXG4gICAqXG4gICAqIEBwYXJhbSBjb250ZW50IC0gVGhlIHRleHQgdG8gcGFyc2VcbiAgICogQHBhcmFtIG9wZW5pbmdUYWdSZWdleCAtIFRoZSByZWdleCBmb3IgdGhlIG9wZW5pbmcgdGFnXG4gICAqIEBwYXJhbSBjbG9zaW5nVGFnUmVnZXggLSBUaGUgcmVnZXggZm9yIHRoZSBjbG9zaW5nIHRhZ1xuICAgKiBAcGFyYW0gaW5jbHVkZU5lc3RlZCAtIFdoZXRoZXIgdG8gaW5jbHVkZSBuZXN0ZWQgaG9va3MgaW4gdGhlIHJlc3VsdFxuICAgKiBAcGFyYW0gb3B0aW9ucyAtIFRoZSBjdXJyZW50IHBhcnNlT3B0aW9uc1xuICAgKi9cbiAgZmluZEVuY2xvc2luZ0hvb2tzKGNvbnRlbnQ6IHN0cmluZywgb3BlbmluZ1RhZ1JlZ2V4OiBSZWdFeHAsIGNsb3NpbmdUYWdSZWdleDogUmVnRXhwLCBpbmNsdWRlTmVzdGVkPzogYm9vbGVhbiwgb3B0aW9uczogUGFyc2VPcHRpb25zID0gZ2V0UGFyc2VPcHRpb25EZWZhdWx0cygpKTogSG9va1Bvc2l0aW9uW10ge1xuICAgIGNvbnN0IGFsbFRhZ3MgPSBbXTtcbiAgICBjb25zdCByZXN1bHQ6IEhvb2tQb3NpdGlvbltdID0gW107XG5cbiAgICAvLyBGaW5kIGFsbCBvcGVuaW5nIHRhZ3NcbiAgICBjb25zdCBvcGVuaW5nVGFnTWF0Y2hlcyA9IG1hdGNoQWxsKGNvbnRlbnQsIG9wZW5pbmdUYWdSZWdleCk7XG4gICAgZm9yIChjb25zdCBtYXRjaCBvZiBvcGVuaW5nVGFnTWF0Y2hlcykge1xuICAgICAgYWxsVGFncy5wdXNoKHtcbiAgICAgICAgaXNPcGVuaW5nOiB0cnVlLFxuICAgICAgICB2YWx1ZTogbWF0Y2hbMF0sXG4gICAgICAgIHN0YXJ0SW5kZXg6IG1hdGNoLmluZGV4LFxuICAgICAgICBlbmRJbmRleDogbWF0Y2guaW5kZXggKyBtYXRjaFswXS5sZW5ndGhcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEZpbmQgYWxsIGNsb3NpbmcgdGFnc1xuICAgIGNvbnN0IGNsb3NpbmdUYWdNYXRjaGVzID0gbWF0Y2hBbGwoY29udGVudCwgY2xvc2luZ1RhZ1JlZ2V4KTtcbiAgICBmb3IgKGNvbnN0IG1hdGNoIG9mIGNsb3NpbmdUYWdNYXRjaGVzKSB7XG4gICAgICBhbGxUYWdzLnB1c2goe1xuICAgICAgICBpc09wZW5pbmc6IGZhbHNlLFxuICAgICAgICB2YWx1ZTogbWF0Y2hbMF0sXG4gICAgICAgIHN0YXJ0SW5kZXg6IG1hdGNoLmluZGV4LFxuICAgICAgICBlbmRJbmRleDogbWF0Y2guaW5kZXggKyBtYXRjaFswXS5sZW5ndGhcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIFNvcnQgYnkgc3RhcnRJbmRleFxuICAgIGFsbFRhZ3Muc29ydCgoYSwgYikgPT4gYS5zdGFydEluZGV4IC0gYi5zdGFydEluZGV4KTtcblxuICAgIC8vIENyZWF0ZSBIb29rUG9zaXRpb25zIGJ5IGZpZ3VyaW5nIG91dCB3aGljaCBvcGVuaW5nIHRhZyBiZWxvbmdzIHRvIHdoaWNoIGNsb3NpbmcgdGFnXG4gICAgY29uc3Qgb3BlbmVkVGFncyA9IFtdO1xuICAgIGFsbFRhZ3NMb29wOiBmb3IgKGNvbnN0IFtpbmRleCwgdGFnXSBvZiBhbGxUYWdzLmVudHJpZXMoKSkge1xuXG4gICAgICAvLyBBbnkgc3Vic2VxdWVudCB0YWcgaXMgb25seSBhbGxvd2VkIHRvIHN0YXJ0IGFmdGVyIHByZXZpb3VzIHRhZyBoYXMgZW5kZWRcbiAgICAgIGlmIChpbmRleCA+IDAgJiYgdGFnLnN0YXJ0SW5kZXggPCBhbGxUYWdzW2luZGV4IC0gMV0uZW5kSW5kZXgpIHtcbiAgICAgICAgdGhpcy5sb2dnZXIud2FybihbJ1N5bnRheCBlcnJvciAtIE5ldyB0YWcgXCInICsgdGFnLnZhbHVlICsgJ1wiIHN0YXJ0ZWQgYXQgcG9zaXRpb24gJyArIHRhZy5zdGFydEluZGV4ICsgJyBiZWZvcmUgcHJldmlvdXMgdGFnIFwiJyArIGFsbFRhZ3NbaW5kZXggLSAxXS52YWx1ZSArICdcIiBlbmRlZCBhdCBwb3NpdGlvbiAnICsgYWxsVGFnc1tpbmRleCAtIDFdLmVuZEluZGV4ICsgJy4gSWdub3JpbmcuJ10sIG9wdGlvbnMpO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gT3BlbmluZyBvciBjbG9zaW5nIHRhZz9cbiAgICAgIGlmICh0YWcuaXNPcGVuaW5nKSB7XG4gICAgICAgIG9wZW5lZFRhZ3MucHVzaCh0YWcpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gU3ludGF4IGVycm9yOiBDbG9zaW5nIHRhZyB3aXRob3V0IHByZWNlZGluZyBvcGVuaW5nIHRhZy4gU3ludGF4IGVycm9yLlxuICAgICAgICBpZiAob3BlbmVkVGFncy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICB0aGlzLmxvZ2dlci53YXJuKFsnU3ludGF4IGVycm9yIC0gQ2xvc2luZyB0YWcgd2l0aG91dCBwcmVjZWRpbmcgb3BlbmluZyB0YWcgZm91bmQ6IFwiJyArIHRhZy52YWx1ZSArICdcIi4gSWdub3JpbmcuJ10sIG9wdGlvbnMpO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gSWYgbmVzdGVkIGhvb2tzIG5vdCBhbGxvd2VkIGFuZCBtb3JlIHRoYW4gb25lIHRhZyBpcyBvcGVuLCBkaXNjYXJkIGJvdGggdGhpcyBjbG9zaW5nIHRhZyBhbmQgdGhlIGxhdGVzdCBvcGVuaW5nIHRhZ1xuICAgICAgICBpZiAoaW5jbHVkZU5lc3RlZCA9PT0gZmFsc2UgJiYgb3BlbmVkVGFncy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgb3BlbmVkVGFncy5wb3AoKTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFZhbGlkIGhvb2shIEFkZCB0byByZXN1bHQgYXJyYXlcbiAgICAgICAgY29uc3Qgb3BlbmluZ1RhZyA9IG9wZW5lZFRhZ3Nbb3BlbmVkVGFncy5sZW5ndGggLSAxXTtcbiAgICAgICAgcmVzdWx0LnB1c2goe1xuICAgICAgICAgIG9wZW5pbmdUYWdTdGFydEluZGV4OiBvcGVuaW5nVGFnLnN0YXJ0SW5kZXgsXG4gICAgICAgICAgb3BlbmluZ1RhZ0VuZEluZGV4OiBvcGVuaW5nVGFnLnN0YXJ0SW5kZXggKyBvcGVuaW5nVGFnLnZhbHVlLmxlbmd0aCxcbiAgICAgICAgICBjbG9zaW5nVGFnU3RhcnRJbmRleDogdGFnLnN0YXJ0SW5kZXgsXG4gICAgICAgICAgY2xvc2luZ1RhZ0VuZEluZGV4OiB0YWcuc3RhcnRJbmRleCArIHRhZy52YWx1ZS5sZW5ndGhcbiAgICAgICAgfSk7XG4gICAgICAgIG9wZW5lZFRhZ3MucG9wKCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKG9wZW5lZFRhZ3MubGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy5sb2dnZXIud2FybihbJ1N5bnRheCBlcnJvciAtIE9wZW5pbmcgdGFncyB3aXRob3V0IGNvcnJlc3BvbmRpbmcgY2xvc2luZyB0YWdzIGZvdW5kLiddLCBvcHRpb25zKTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG59XG4iXX0=