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.

381 lines 62 kB
import { Injectable } from '@angular/core'; import { regexes } from '../../constants/regexes'; import { matchAll } from './utils'; import { getParseOptionDefaults } from '../settings/options'; import * as i0 from "@angular/core"; import * as i1 from "./dataTypeEncoder"; import * as i2 from "./logger"; /** * A parser that can evaluate stringified variables and turn them into their corresponding data types */ export class DataTypeParser { constructor(dataTypeEncoder, logger) { this.dataTypeEncoder = dataTypeEncoder; this.logger = logger; } /** * Takes a string containing a Javascript data type as it would appear in code, such a number ('15'), a string ('"hello"'), * an array ('[1,2,3]'), an object ('{prop: "something"}') etc., and evaluates it to be an an actual variable. * * Note: This function works without invoking eval() and instead uses JSON.parse() for the heavy lifting. As such, it should be safe * to use and should cover most forms of input. * * @param dataTypeString - The string to parse * @param context - (optional) A context object to load variables from * @param event - (optional) An event object to place $event vars with * @param unescapeStrings - (optional) Whether to unescape strings or not * @param trackContextVariables - (optional) An object that will be filled out with all found context vars * @param allowContextFunctionCalls - (optional) Whether to allow function calls in context vars * @param options - (optional) The current parseOptions */ evaluate(dataTypeString, context = {}, event, unescapeStrings = true, trackContextVariables = {}, allowContextFunctionCalls = true, options = getParseOptionDefaults()) { // a) Simple types // -------------------- // null or undefined if (dataTypeString === 'null') { return null; } if (dataTypeString === 'undefined') { return undefined; } // boolean if (dataTypeString === 'true') { return true; } if (dataTypeString === 'false') { return false; } // number if (!isNaN(dataTypeString)) { return parseInt(dataTypeString, 10); } // string if ((dataTypeString.startsWith('"') && dataTypeString.endsWith('"')) || (dataTypeString.startsWith("'") && dataTypeString.endsWith("'")) || (dataTypeString.startsWith("`") && dataTypeString.endsWith("`"))) { // Remove outer quotes, potentially unescape and return let decodedString = dataTypeString.substring(1, dataTypeString.length - 1); decodedString = unescapeStrings ? this.dataTypeEncoder.stripSlashes(decodedString) : decodedString; return decodedString; } // b) Complex types // -------------------- // IMPORTANT: To properly parse complex object structures as well as context variables with regex, the string needs to be prepared. This means: // 1. Substrings must be rendered 'harmless', meaning all special characters that regex might confuse with variable syntax must be encoded. // 2. The brackets of subfunctions (e.g. context.fn(otherFn(param)).var), must be encoded as well. Regex can't handle nested substructures and wouldn't know which bracket closes the outer function. // 3. The brackets of subbrackets (e.g. context[context['something']].var) must be encoded for the same reason. dataTypeString = this.encodeDataTypeString(dataTypeString); // array or object literal if ((dataTypeString.startsWith('{') && dataTypeString.endsWith('}')) || (dataTypeString.startsWith('[') && dataTypeString.endsWith(']'))) { // Prepare string and parse as JSON const json = this.parseAsJSON(dataTypeString, unescapeStrings); // Load variables return this.loadJSONVariables(json, context, event, unescapeStrings, trackContextVariables, allowContextFunctionCalls, options); } // event variable name if (dataTypeString === '$event') { return event; } // context variable name if (dataTypeString.match(new RegExp('^\\s*' + regexes.contextVariableRegex + '\\s*$', 'gm'))) { return this.loadContextVariable(dataTypeString, context, event, unescapeStrings, trackContextVariables, allowContextFunctionCalls, options); } throw Error('Data type for following input was not recognized and could not be parsed: "' + dataTypeString + '"'); } /** * Encodes a data type string * * @param dataTypeString - The string to encode */ encodeDataTypeString(dataTypeString) { dataTypeString = this.dataTypeEncoder.encodeSubstrings(dataTypeString); // Encode all potential substrings dataTypeString = this.dataTypeEncoder.encodeSubfunctions(dataTypeString); // Encode all potential subfunctions dataTypeString = this.dataTypeEncoder.encodeVariableSubbrackets(dataTypeString); // Encode all potential subbrackets of variables return dataTypeString; } /** * Decodes a data type string * * @param dataTypeString - The string to decode */ decodeDataTypeString(dataTypeString) { dataTypeString = this.dataTypeEncoder.decodeStringSpecialChars(dataTypeString); // Decode special chars from substrings dataTypeString = this.dataTypeEncoder.decodeFunctionBrackets(dataTypeString); // Decode subfunctions dataTypeString = this.dataTypeEncoder.decodeVariableBrackets(dataTypeString); // Decode subbrackets dataTypeString = dataTypeString.trim(); // Trim whitespace return dataTypeString; } /** * In order to successfully parse a data type string with JSON.parse(), it needs to follow certain formatting rules. * This function ensures that these are followed and corrects the input if not. * * @param JSONString - The string to be given to JSON.parse() * @param unescapeStrings - Whether to unescape the strings of this JSON */ parseAsJSON(JSONString, unescapeStrings) { // Find all single- and grave-quote-delimited strings and convert them to double quote strings const singleQuoteStringRegex = /\'(\\.|[^\'])*?\'/gm; JSONString = JSONString.replace(singleQuoteStringRegex, match => { return '"' + match.slice(1, -1) + '"'; }); const graveQuoteStringRegex = /\`(\\.|[^\`])*?\`/gm; JSONString = JSONString.replace(graveQuoteStringRegex, match => { return '"' + match.slice(1, -1) + '"'; }); // Add double-quotes around JSON property names where still missing const JSONPropertyRegex = /"?([a-z0-9A-Z_]+)"?\s*:/g; JSONString = JSONString.replace(JSONPropertyRegex, '"$1": '); // Prevent setting protected properties if (JSONString.match(/"?__proto__"?\s*:/g)) { throw Error('Setting the "__proto__" property in a hook input object is not allowed.'); } if (JSONString.match(/"?prototype"?\s*:/g)) { throw Error('Setting the "prototype" property in a hook input object is not allowed.'); } if (JSONString.match(/"?constructor"?\s*:/g)) { throw Error('Setting the "constructor" property in a hook input object is not allowed.'); } // Replace undefined with null JSONString = this.replaceValuesInJSONString(JSONString, 'undefined', match => 'null'); // Replace context vars with string placeholders JSONString = this.replaceValuesInJSONString(JSONString, regexes.contextVariableRegex, (match) => { return '"' + this.dataTypeEncoder.transformContextVarIntoPlacerholder(match) + '"'; }); // Replace $event with string placeholders JSONString = this.replaceValuesInJSONString(JSONString, '\\$event', match => '"__EVENT__"'); // PARSE const json = JSON.parse(JSONString); // Decode all strings that are not context vars or the event object this.decodeJSONStrings(json, unescapeStrings); return json; } /** * Given a stringified json and a json value regex, allows you to replace all occurences * of those values in the json via a callback function. * * IMPORTANT: JSONString must be already encoded via this.encodeDataTypeString() for this to work. * * @param JSONString - The stringified JSON * @param valueRegex - The values to find * @param callbackFn - A callback fn that returns what you want to replace them with */ replaceValuesInJSONString(JSONString, valueRegex, callbackFn) { // With lookbehinds (too new for some browsers) const withLookBehindsRegex = '(?:' + '(?<=:\\s*)' + valueRegex + '(?=\\s*[,}])' + '|' + '(?<=[\\[,]\\s*)' + valueRegex + '(?=\\s*[\\],])' + ')'; // Without lookbehinds (make sure to keep the lookaheads, though. This way, the same comma can be the end of one regex and the beginning of the next) const regex = '(?:' + '(:\\s*)(' + valueRegex + ')(?=\\s*[,}])' + '|' + // Value in object: ':' followed by value followed by ',' or '}' '([\\[,]\\s*)(' + valueRegex + ')(?=\\s*[\\],])' + // Value in array: '[' or ',' followed by value followed by ',' or ']' ')'; return JSONString.replace(new RegExp(regex, 'gm'), (full, p1, p2, p3, p4) => { const startPart = p1 ? p1 : p3; const value = p2 ? p2 : p4; return startPart + callbackFn(value); }); } /** * Decodes all 'normal' strings without special meaning in a JSON-like object * * @param jsonLevel - The current level of parsing * @param unescapeStrings - Whether to unescape the decoded strings as well */ decodeJSONStrings(jsonLevel, unescapeStrings) { for (const prop in jsonLevel) { if (typeof jsonLevel[prop] === 'string') { // Ignore var placeholders if (jsonLevel[prop] === '__EVENT__"' || jsonLevel[prop].match(new RegExp('^\\s*' + regexes.placeholderContextVariableRegex + '\\s*$', 'gm'))) { continue; } // Otherwise decode string let decodedString = this.decodeDataTypeString(jsonLevel[prop]); decodedString = unescapeStrings ? this.dataTypeEncoder.stripSlashes(decodedString) : decodedString; jsonLevel[prop] = decodedString; } else if (typeof jsonLevel[prop] === 'object') { this.decodeJSONStrings(jsonLevel[prop], unescapeStrings); } } } // Loading variables // ---------------------------------------------------------------------------------------------------------------------------------------- /** * Travels a JSON-like object to find all context vars and event objects and replaces their placeholders with the actual values * * @param arrayOrObject - The property of the JSON to analyze * @param context - The current context object, if any * @param event - The current event object, if any * @param unescapeStrings - Whether to unescape strings or not * @param trackContextVariables - Whether to unescape strings or not * @param allowContextFunctionCalls - Whether function calls in context vars are allowed * @param options - The current parseOptions */ loadJSONVariables(arrayOrObject, context, event, unescapeStrings, trackContextVariables, allowContextFunctionCalls, options) { for (const prop in arrayOrObject) { // Only interested in strings if (typeof arrayOrObject[prop] === 'string') { // If event placeholder if (arrayOrObject[prop] === '__EVENT__') { arrayOrObject[prop] = event; // If context var placeholder } else if (arrayOrObject[prop].match(new RegExp('^\\s*' + regexes.placeholderContextVariableRegex + '\\s*$', 'gm'))) { const contextVar = this.dataTypeEncoder.transformPlaceholderIntoContextVar(arrayOrObject[prop].trim()); arrayOrObject[prop] = this.loadContextVariable(contextVar, context, event, unescapeStrings, trackContextVariables, allowContextFunctionCalls, options); } } else if (typeof arrayOrObject[prop] === 'object') { this.loadJSONVariables(arrayOrObject[prop], context, event, unescapeStrings, trackContextVariables, allowContextFunctionCalls, options); } } return arrayOrObject; } /** * Takes a context variable string and evaluates it to get the desired value * * IMPORTANT: To correctly parse variables, their substrings, subfunction and subbrackets must be encoded (done in evaluate()) * * @param contextVar - The context var * @param context - The context object * @param event - An event object, if available * @param unescapeStrings - Whether to unescape strings or not * @param trackContextVariables - An optional object that will be filled out with all found context vars * @param allowContextFunctionCalls - Whether function calls in context vars are allowed * @param options - The current parseOptions */ loadContextVariable(contextVar, context = {}, event, unescapeStrings = true, trackContextVariables = {}, allowContextFunctionCalls = true, options = getParseOptionDefaults()) { try { const shortContextVar = contextVar.substring(7); // Cut off 'context' from the front // If context object is requested directly if (shortContextVar.trim() === '') { return context; } // Otherwise, create variable path array and fetch value, so the context object can be easily travelled. // Variable path example: 'restaurants["newOrleans"].reviews[5]' becomes ['restaurants', 'newOrleans', 'reviews', 5], const path = []; const pathMatches = matchAll(shortContextVar, new RegExp(regexes.variablePathPartRegex, 'gm')); for (const match of pathMatches) { // 1. If dot notation if (match[0].startsWith('.')) { path.push({ type: 'property', value: match[0].substring(1) }); } // 2. If bracket notation if (match[0].startsWith('[') && match[0].endsWith(']')) { let bracketValue = match[0].substring(1, match[0].length - 1); // Evaluate bracket parameter bracketValue = this.decodeDataTypeString(bracketValue); // Decode variable bracketValue = this.evaluate(bracketValue, context, event, unescapeStrings, trackContextVariables, allowContextFunctionCalls); // Recursively repeat the process path.push({ type: 'property', value: bracketValue }); } // 3. If function call if (match[0].startsWith('(') && match[0].endsWith(')')) { // Check if function calls are allowed if (!allowContextFunctionCalls) { throw Error('Tried to call a function in a context variable. This has been disallowed in the current config.'); } const funcParams = match[0].substring(1, match[0].length - 1); // Strip outer brackets // Evaluate function parameters const paramsArray = []; if (funcParams !== '') { for (const param of funcParams.split(',')) { let p = this.decodeDataTypeString(param); // Decode variable p = this.evaluate(p, context, event, unescapeStrings, trackContextVariables, allowContextFunctionCalls); // Recursively repeat the process paramsArray.push(p); } } // Add function to path path.push({ type: 'function', value: paramsArray }); } } try { const resolvedContextVar = this.fetchContextVariable(context, path); trackContextVariables[this.decodeDataTypeString(contextVar)] = resolvedContextVar; return resolvedContextVar; } catch (e) { throw Error('The required context variable "' + this.decodeDataTypeString(contextVar) + '" could not be found in the context object. Returning undefined instead.'); } } catch (e) { this.logger.warn([e], options); trackContextVariables[this.decodeDataTypeString(contextVar)] = undefined; return undefined; } } /** * Recursively travels an object with the help of a path array and returns the specified value, * or undefined if not found * * @param contextLevel - The object to travel * @param path - The property path array */ fetchContextVariable(contextLevel, path) { // Prevent accessing protected properties if (path[0].value === '__proto__') { throw Error('Accessing the __proto__ property through a context variable is not allowed.'); } if (path[0].value === 'prototype') { throw Error('Accessing the prototype property through a context variable is not allowed.'); } if (path[0].value === 'constructor') { throw Error('Accessing the constructor property through a context variable is not allowed.'); } if (contextLevel === undefined) { throw Error('Context variable path could not be resolved. Trying to access ' + (path[0].type === 'property' ? 'property "' + path[0].value + '" of undefined.' : 'undefined function.')); } // Get property let result; if (path[0].type === 'property') { if (contextLevel.hasOwnProperty(path[0].value)) { result = contextLevel[path[0].value]; // It makes a difference to JavaScript whether you call a function by 'obj.func()' or by 'let func = obj.func; func();' // In the latter case, 'this' will be undefined and not point to the parent. Since this recursive approach uses that latter version, // manually bind each function to the parent to restore the normal behavior. // Also: If the user has submitted a bound function himself, calling .bind here again does nothing, which is the desired behaviour. if (typeof result === 'function') { result = result.bind(contextLevel); } // Check '__proto__' as well as functions tend to live here instead of directly on the instance } else if (contextLevel.__proto__.hasOwnProperty(path[0].value)) { result = contextLevel.__proto__[path[0].value]; if (typeof result === 'function') { result = result.bind(contextLevel); } } else { result = undefined; } } else if (path[0].type === 'function') { result = contextLevel(...path[0].value); } path.shift(); // Recursively travel path if (path.length > 0) { result = this.fetchContextVariable(result, path); } return result; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DataTypeParser, deps: [{ token: i1.DataTypeEncoder }, { token: i2.Logger }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DataTypeParser, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DataTypeParser, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.DataTypeEncoder }, { type: i2.Logger }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YVR5cGVQYXJzZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtZHluYW1pYy1ob29rcy9zcmMvbGliL3NlcnZpY2VzL3V0aWxzL2RhdGFUeXBlUGFyc2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFM0MsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRWxELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFFbkMsT0FBTyxFQUFFLHNCQUFzQixFQUFnQixNQUFNLHFCQUFxQixDQUFDOzs7O0FBRzNFOztHQUVHO0FBSUgsTUFBTSxPQUFPLGNBQWM7SUFFekIsWUFBb0IsZUFBZ0MsRUFBVSxNQUFjO1FBQXhELG9CQUFlLEdBQWYsZUFBZSxDQUFpQjtRQUFVLFdBQU0sR0FBTixNQUFNLENBQVE7SUFDNUUsQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7OztPQWNHO0lBQ0gsUUFBUSxDQUFDLGNBQXNCLEVBQUUsVUFBZSxFQUFFLEVBQUUsS0FBVyxFQUFFLGtCQUEyQixJQUFJLEVBQUUsd0JBQTZCLEVBQUUsRUFBRSw0QkFBcUMsSUFBSSxFQUFFLFVBQXdCLHNCQUFzQixFQUFFO1FBRTVOLGtCQUFrQjtRQUNsQix1QkFBdUI7UUFDdkIsb0JBQW9CO1FBQ3BCLElBQUksY0FBYyxLQUFLLE1BQU0sRUFBRSxDQUFDO1lBQUMsT0FBTyxJQUFJLENBQUM7UUFBQyxDQUFDO1FBQy9DLElBQUksY0FBYyxLQUFLLFdBQVcsRUFBRSxDQUFDO1lBQUMsT0FBTyxTQUFTLENBQUM7UUFBQyxDQUFDO1FBQ3pELFVBQVU7UUFDVixJQUFJLGNBQWMsS0FBSyxNQUFNLEVBQUUsQ0FBQztZQUFDLE9BQU8sSUFBSSxDQUFDO1FBQUMsQ0FBQztRQUMvQyxJQUFJLGNBQWMsS0FBSyxPQUFPLEVBQUUsQ0FB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Q0FBQyxLQUFLLENBQUMsRUFBRyxDQUFDO2dCQUNqRSxNQUFNLEdBQUcsWUFBWSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQy9DLElBQUksT0FBTyxNQUFNLEtBQUssVUFBVSxFQUFFLENBQUM7b0JBQ2pDLE1BQU0sR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO2dCQUNyQyxDQUFDO1lBQ0gsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLE1BQU0sR0FBRyxTQUFTLENBQUM7WUFDckIsQ0FBQztRQUNILENBQUM7YUFBTSxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDdkMsTUFBTSxHQUFHLFlBQVksQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUMxQyxDQUFDO1FBQ0QsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBRWIsMEJBQTBCO1FBQzFCLElBQUksSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNwQixNQUFNLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQztRQUNuRCxDQUFDO1FBQ0QsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQzsrR0FqWVUsY0FBYzttSEFBZCxjQUFjLGNBRmIsTUFBTTs7NEZBRVAsY0FBYztrQkFIMUIsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7IHJlZ2V4ZXMgfSBmcm9tICcuLi8uLi9jb25zdGFudHMvcmVnZXhlcyc7XG5pbXBvcnQgeyBEYXRhVHlwZUVuY29kZXIgfSBmcm9tICcuL2RhdGFUeXBlRW5jb2Rlcic7XG5pbXBvcnQgeyBtYXRjaEFsbCB9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHsgTG9nZ2VyIH0gZnJvbSAnLi9sb2dnZXInO1xuaW1wb3J0IHsgZ2V0UGFyc2VPcHRpb25EZWZhdWx0cywgUGFyc2VPcHRpb25zIH0gZnJvbSAnLi4vc2V0dGluZ3Mvb3B0aW9ucyc7XG5cblxuLyoqXG4gKiBBIHBhcnNlciB0aGF0IGNhbiBldmFsdWF0ZSBzdHJpbmdpZmllZCB2YXJpYWJsZXMgYW5kIHR1cm4gdGhlbSBpbnRvIHRoZWlyIGNvcnJlc3BvbmRpbmcgZGF0YSB0eXBlc1xuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBEYXRhVHlwZVBhcnNlciB7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBkYXRhVHlwZUVuY29kZXI6IERhdGFUeXBlRW5jb2RlciwgcHJpdmF0ZSBsb2dnZXI6IExvZ2dlcikge1xuICB9XG5cbiAgLyoqXG4gICAqIFRha2VzIGEgc3RyaW5nIGNvbnRhaW5pbmcgYSBKYXZhc2NyaXB0IGRhdGEgdHlwZSBhcyBpdCB3b3VsZCBhcHBlYXIgaW4gY29kZSwgc3VjaCBhIG51bWJlciAoJzE1JyksIGEgc3RyaW5nICgnXCJoZWxsb1wiJyksXG4gICAqIGFuIGFycmF5ICgnWzEsMiwzXScpLCBhbiBvYmplY3QgKCd7cHJvcDogXCJzb21ldGhpbmdcIn0nKSBldGMuLCBhbmQgZXZhbHVhdGVzIGl0IHRvIGJlIGFuIGFuIGFjdHVhbCB2YXJpYWJsZS5cbiAgICpcbiAgICogTm90ZTogVGhpcyBmdW5jdGlvbiB3b3JrcyB3aXRob3V0IGludm9raW5nIGV2YWwoKSBhbmQgaW5zdGVhZCB1c2VzIEpTT04ucGFyc2UoKSBmb3IgdGhlIGhlYXZ5IGxpZnRpbmcuIEFzIHN1Y2gsIGl0IHNob3VsZCBiZSBzYWZlXG4gICAqIHRvIHVzZSBhbmQgc2hvdWxkIGNvdmVyIG1vc3QgZm9ybXMgb2YgaW5wdXQuXG4gICAqXG4gICAqIEBwYXJhbSBkYXRhVHlwZVN0cmluZyAtIFRoZSBzdHJpbmcgdG8gcGFyc2VcbiAgICogQHBhcmFtIGNvbnRleHQgLSAob3B0aW9uYWwpIEEgY29udGV4dCBvYmplY3QgdG8gbG9hZCB2YXJpYWJsZXMgZnJvbVxuICAgKiBAcGFyYW0gZXZlbnQgLSAob3B0aW9uYWwpIEFuIGV2ZW50IG9iamVjdCB0byBwbGFjZSAkZXZlbnQgdmFycyB3aXRoXG4gICAqIEBwYXJhbSB1bmVzY2FwZVN0cmluZ3MgLSAob3B0aW9uYWwpIFdoZXRoZXIgdG8gdW5lc2NhcGUgc3RyaW5ncyBvciBub3RcbiAgICogQHBhcmFtIHRyYWNrQ29udGV4dFZhcmlhYmxlcyAtIChvcHRpb25hbCkgQW4gb2JqZWN0IHRoYXQgd2lsbCBiZSBmaWxsZWQgb3V0IHdpdGggYWxsIGZvdW5kIGNvbnRleHQgdmFyc1xuICAgKiBAcGFyYW0gYWxsb3dDb250ZXh0RnVuY3Rpb25DYWxscyAtIChvcHRpb25hbCkgV2hldGhlciB0byBhbGxvdyBmdW5jdGlvbiBjYWxscyBpbiBjb250ZXh0IHZhcnNcbiAgICogQHBhcmFtIG9wdGlvbnMgLSAob3B0aW9uYWwpIFRoZSBjdXJyZW50IHBhcnNlT3B0aW9uc1xuICAgKi9cbiAgZXZhbHVhdGUoZGF0YVR5cGVTdHJpbmc6IHN0cmluZywgY29udGV4dDogYW55ID0ge30sIGV2ZW50PzogYW55LCB1bmVzY2FwZVN0cmluZ3M6IGJvb2xlYW4gPSB0cnVlLCB0cmFja0NvbnRleHRWYXJpYWJsZXM6IGFueSA9IHt9LCBhbGxvd0NvbnRleHRGdW5jdGlvbkNhbGxzOiBib29sZWFuID0gdHJ1ZSwgb3B0aW9uczogUGFyc2VPcHRpb25zID0gZ2V0UGFyc2VPcHRpb25EZWZhdWx0cygpKTogYW55IHtcblxuICAgIC8vIGEpIFNpbXBsZSB0eXBlc1xuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLy8gbnVsbCBvciB1bmRlZmluZWRcbiAgICBpZiAoZGF0YVR5cGVTdHJpbmcgPT09ICdudWxsJykgeyByZXR1cm4gbnVsbDsgfVxuICAgIGlmIChkYXRhVHlwZVN0cmluZyA9PT0gJ3VuZGVmaW5lZCcpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfVxuICAgIC8vIGJvb2xlYW5cbiAgICBpZiAoZGF0YVR5cGVTdHJpbmcgPT09ICd0cnVlJykgeyByZXR1cm4gdHJ1ZTsgfVxuICAgIGlmIChkYXRhVHlwZVN0cmluZyA9PT0gJ2ZhbHNlJykgeyByZXR1cm4gZmFsc2U7IH1cbiAgICAvLyBudW1iZXJcbiAgICBpZiAoIWlzTmFOKGRhdGFUeXBlU3RyaW5nIGFzIGFueSkpICB7IHJldHVybiBwYXJzZUludChkYXRhVHlwZVN0cmluZywgMTApOyB9XG4gICAgLy8gc3RyaW5nXG4gICAgaWYgKFxuICAgICAgKGRhdGFUeXBlU3RyaW5nLnN0YXJ0c1dpdGgoJ1wiJykgJiYgZGF0YVR5cGVTdHJpbmcuZW5kc1dpdGgoJ1wiJykpIHx8XG4gICAgICAoZGF0YVR5cGVTdHJpbmcuc3RhcnRzV2l0aChcIidcIikgJiYgZGF0YVR5cGVTdHJpbmcuZW5kc1dpdGgoXCInXCIpKSB8fFxuICAgICAgKGRhdGFUeXBlU3RyaW5nLnN0YXJ0c1dpdGgoXCJgXCIpICYmIGRhdGFUeXBlU3RyaW5nLmVuZHNXaXRoKFwiYFwiKSlcbiAgICApIHtcbiAgICAgIC8vIFJlbW92ZSBvdXRlciBxdW90ZXMsIHBvdGVudGlhbGx5IHVuZXNjYXBlIGFuZCByZXR1cm5cbiAgICAgIGxldCBkZWNvZGVkU3RyaW5nID0gZGF0YVR5cGVTdHJpbmcuc3Vic3RyaW5nKDEsIGRhdGFUeXBlU3RyaW5nLmxlbmd0aCAtIDEpO1xuICAgICAgZGVjb2RlZFN0cmluZyA9IHVuZXNjYXBlU3RyaW5ncyA/IHRoaXMuZGF0YVR5cGVFbmNvZGVyLnN0cmlwU2xhc2hlcyhkZWNvZGVkU3RyaW5nKSA6IGRlY29kZWRTdHJpbmc7XG4gICAgICByZXR1cm4gZGVjb2RlZFN0cmluZztcbiAgICB9XG5cbiAgICAvLyBiKSBDb21wbGV4IHR5cGVzXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvLyBJTVBPUlRBTlQ6IFRvIHByb3Blcmx5IHBhcnNlIGNvbXBsZXggb2JqZWN0IHN0cnVjdHVyZXMgYXMgd2VsbCBhcyBjb250ZXh0IHZhcmlhYmxlcyB3aXRoIHJlZ2V4LCB0aGUgc3RyaW5nIG5lZWRzIHRvIGJlIHByZXBhcmVkLiBUaGlzIG1lYW5zOlxuICAgIC8vIDEuIFN1YnN0cmluZ3MgbXVzdCBiZSByZW5kZXJlZCAnaGFybWxlc3MnLCBtZWFuaW5nIGFsbCBzcGVjaWFsIGNoYXJhY3RlcnMgdGhhdCByZWdleCBtaWdodCBjb25mdXNlIHdpdGggdmFyaWFibGUgc3ludGF4IG11c3QgYmUgZW5jb2RlZC5cbiAgICAvLyAyLiBUaGUgYnJhY2tldHMgb2Ygc3ViZnVuY3Rpb25zIChlLmcuIGNvbnRleHQuZm4ob3RoZXJGbihwYXJhbSkpLnZhciksIG11c3QgYmUgZW5jb2RlZCBhcyB3ZWxsLiBSZWdleCBjYW4ndCBoYW5kbGUgbmVzdGVkIHN1YnN0cnVjdHVyZXMgYW5kIHdvdWxkbid0IGtub3cgd2hpY2ggYnJhY2tldCBjbG9zZXMgdGhlIG91dGVyIGZ1bmN0aW9uLlxuICAgIC8vIDMuIFRoZSBicmFja2V0cyBvZiBzdWJicmFja2V0cyAoZS5nLiBjb250ZXh0W2NvbnRleHRbJ3NvbWV0aGluZyddXS52YXIpIG11c3QgYmUgZW5jb2RlZCBmb3IgdGhlIHNhbWUgcmVhc29uLlxuICAgIGRhdGFUeXBlU3RyaW5nID0gdGhpcy5lbmNvZGVEYXRhVHlwZVN0cmluZyhkYXRhVHlwZVN0cmluZyk7XG5cbiAgICAvLyBhcnJheSBvciBvYmplY3QgbGl0ZXJhbFxuICAgIGlmIChcbiAgICAgIChkYXRhVHlwZVN0cmluZy5zdGFydHNXaXRoKCd7JykgJiYgZGF0YVR5cGVTdHJpbmcuZW5kc1dpdGgoJ30nKSkgfHxcbiAgICAgIChkYXRhVHlwZVN0cmluZy5zdGFydHNXaXRoKCdbJykgJiYgZGF0YVR5cGVTdHJpbmcuZW5kc1dpdGgoJ10nKSlcbiAgICApIHtcbiAgICAgIC8vIFByZXBhcmUgc3RyaW5nIGFuZCBwYXJzZSBhcyBKU09OXG4gICAgICBjb25zdCBqc29uID0gdGhpcy5wYXJzZUFzSlNPTihkYXRhVHlwZVN0cmluZywgdW5lc2NhcGVTdHJpbmdzKTtcbiAgICAgIC8vIExvYWQgdmFyaWFibGVzXG4gICAgICByZXR1cm4gdGhpcy5sb2FkSlNPTlZhcmlhYmxlcyhqc29uLCBjb250ZXh0LCBldmVudCwgdW5lc2NhcGVTdHJpbmdzLCB0cmFja0NvbnRleHRWYXJpYWJsZXMsIGFsbG93Q29udGV4dEZ1bmN0aW9uQ2FsbHMsIG9wdGlvbnMpO1xuICAgIH1cblxuICAgIC8vIGV2ZW50IHZhcmlhYmxlIG5hbWVcbiAgICBpZiAoZGF0YVR5cGVTdHJpbmcgPT09ICckZXZlbnQnKSB7XG4gICAgICByZXR1cm4gZXZlbnQ7XG4gICAgfVxuXG4gICAgLy8gY29udGV4dCB2YXJpYWJsZSBuYW1lXG4gICAgaWYgKGRhdGFUeXBlU3RyaW5nLm1hdGNoKG5ldyBSZWdFeHAoJ15cXFxccyonICsgcmVnZXhlcy5jb250ZXh0VmFyaWFibGVSZWdleCArICdcXFxccyokJywgJ2dtJykpKSB7XG4gICAgICByZXR1cm4gdGhpcy5sb2FkQ29udGV4dFZhcmlhYmxlKGRhdGFUeXBlU3RyaW5nLCBjb250ZXh0LCBldmVudCwgdW5lc2NhcGVTdHJpbmdzLCB0cmFja0NvbnRleHRWYXJpYWJsZXMsIGFsbG93Q29udGV4dEZ1bmN0aW9uQ2FsbHMsIG9wdGlvbnMpO1xuICAgIH1cblxuICAgIHRocm93IEVycm9yKCdEYXRhIHR5cGUgZm9yIGZvbGxvd2luZyBpbnB1dCB3YXMgbm90IHJlY29nbml6ZWQgYW5kIGNvdWxkIG5vdCBiZSBwYXJzZWQ6IFwiJyArIGRhdGFUeXBlU3RyaW5nICsgJ1wiJyk7XG4gIH1cblxuICAvKipcbiAgICogRW5jb2RlcyBhIGRhdGEgdHlwZSBzdHJpbmdcbiAgICpcbiAgICogQHBhcmFtIGRhdGFUeXBlU3RyaW5nIC0gVGhlIHN0cmluZyB0byBlbmNvZGVcbiAgICovXG4gIGVuY29kZURhdGFUeXBlU3RyaW5nKGRhdGFUeXBlU3RyaW5nOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGRhdGFUeXBlU3RyaW5nID0gdGhpcy5kYXRhVHlwZUVuY29kZXIuZW5jb2RlU3Vic3RyaW5ncyhkYXRhVHlwZVN0cmluZyk7ICAgICAgICAgICAgICAvLyBFbmNvZGUgYWxsIHBvdGVudGlhbCBzdWJzdHJpbmdzXG4gICAgZGF0YVR5cGVTdHJpbmcgPSB0aGlzLmRhdGFUeXBlRW5jb2Rlci5lbmNvZGVTdWJmdW5jdGlvbnMoZGF0YVR5cGVTdHJpbmcpOyAgICAgICAgICAgIC8vIEVuY29kZSBhbGwgcG90ZW50aWFsIHN1YmZ1bmN0aW9uc1xuICAgIGRhdGFUeXBlU3RyaW5nID0gdGhpcy5kYXRhVHlwZUVuY29kZXIuZW5jb2RlVmFyaWFibGVTdWJicmFja2V0cyhkYXRhVHlwZVN0cmluZyk7ICAgICAvLyBFbmNvZGUgYWxsIHBvdGVudGlhbCBzdWJicmFja2V0cyBvZiB2YXJpYWJsZXNcbiAgICByZXR1cm4gZGF0YVR5cGVTdHJpbmc7XG4gIH1cblxuICAvKipcbiAgICogRGVjb2RlcyBhIGRhdGEgdHlwZSBzdHJpbmdcbiAgICpcbiAgICogQHBhcmFtIGRhdGFUeXBlU3RyaW5nIC0gVGhlIHN0cmluZyB0byBkZWNvZGVcbiAgICovXG4gIGRlY29kZURhdGFUeXBlU3RyaW5nKGRhdGFUeXBlU3RyaW5nOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGRhdGFUeXBlU3RyaW5nID0gdGhpcy5kYXRhVHlwZUVuY29kZXIuZGVjb2RlU3RyaW5nU3BlY2lhbENoYXJzKGRhdGFUeXBlU3RyaW5nKTsgICAgIC8vIERlY29kZSBzcGVjaWFsIGNoYXJzIGZyb20gc3Vic3RyaW5nc1xuICAgIGRhdGFUeXBlU3RyaW5nID0gdGhpcy5kYXRhVHlwZUVuY29kZXIuZGVjb2RlRnVuY3Rpb25CcmFja2V0cyhkYXRhVHlwZVN0cmluZyk7ICAgICAgIC8vIERlY29kZSBzdWJmdW5jdGlvbnNcbiAgICBkYXRhVHlwZVN0cmluZyA9IHRoaXMuZGF0YVR5cGVFbmNvZGVyLmRlY29kZVZhcmlhYmxlQnJhY2tldHMoZGF0YVR5cGVTdHJpbmcpOyAgICAgICAvLyBEZWNvZGUgc3ViYnJhY2tldHNcbiAgICBkYXRhVHlwZVN0cmluZyA9IGRhdGFUeXBlU3RyaW5nLnRyaW0oKTsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBUcmltIHdoaXRlc3BhY2VcbiAgICByZXR1cm4gZGF0YVR5cGVTdHJpbmc7XG4gIH1cblxuICAvKipcbiAgICogSW4gb3JkZXIgdG8gc3VjY2Vzc2Z1bGx5IHBhcnNlIGEgZGF0YSB0eXBlIHN0cmluZyB3aXRoIEpTT04ucGFyc2UoKSwgaXQgbmVlZHMgdG8gZm9sbG93IGNlcnRhaW4gZm9ybWF0dGluZyBydWxlcy5cbiAgICogVGhpcyBmdW5jdGlvbiBlbnN1cmVzIHRoYXQgdGhlc2UgYXJlIGZvbGxvd2VkIGFuZCBjb3JyZWN0cyB0aGUgaW5wdXQgaWYgbm90LlxuICAgKlxuICAgKiBAcGFyYW0gSlNPTlN0cmluZyAtIFRoZSBzdHJpbmcgdG8gYmUgZ2l2ZW4gdG8gSlNPTi5wYXJzZSgpXG4gICAqIEBwYXJhbSB1bmVzY2FwZVN0cmluZ3MgLSBXaGV0aGVyIHRvIHVuZXNjYXBlIHRoZSBzdHJpbmdzIG9mIHRoaXMgSlNPTlxuICAgKi9cbiAgcHJpdmF0ZSBwYXJzZUFzSlNPTihKU09OU3RyaW5nOiBzdHJpbmcsIHVuZXNjYXBlU3RyaW5nczogYm9vbGVhbik6IGFueSB7XG5cbiAgICAvLyBGaW5kIGFsbCBzaW5nbGUtIGFuZCBncmF2ZS1xdW90ZS1kZWxpbWl0ZWQgc3RyaW5ncyBhbmQgY29udmVydCB0aGVtIHRvIGRvdWJsZSBxdW90ZSBzdHJpbmdzXG4gICAgY29uc3Qgc2luZ2xlUXVvdGVTdHJpbmdSZWdleCA9IC9cXCcoXFxcXC58W15cXCddKSo/XFwnL2dtO1xuICAgIEpTT05TdHJpbmcgPSBKU09OU3RyaW5nLnJlcGxhY2Uoc2luZ2xlUXVvdGVTdHJpbmdSZWdleCwgbWF0Y2ggPT4ge1xuICAgICAgcmV0dXJuICdcIicgKyBtYXRjaC5zbGljZSgxLCAtMSkgKyAnXCInO1xuICAgIH0pO1xuICAgIGNvbnN0IGdyYXZlUXVvdGVTdHJpbmdSZWdleCA9IC9cXGAoXFxcXC58W15cXGBdKSo/XFxgL2dtO1xuICAgIEpTT05TdHJpbmcgPSBKU09OU3RyaW5nLnJlcGxhY2UoZ3JhdmVRdW90ZVN0cmluZ1JlZ2V4LCBtYXRjaCA9PiB7XG4gICAgICByZXR1cm4gJ1wiJyArIG1hdGNoLnNsaWNlKDEsIC0xKSArICdcIic7XG4gICAgfSk7XG5cbiAgICAvLyBBZGQgZG91YmxlLXF1b3RlcyBhcm91bmQgSlNPTiBwcm9wZXJ0eSBuYW1lcyB3aGVyZSBzdGlsbCBtaXNzaW5nXG4gICAgY29uc3QgSlNPTlByb3BlcnR5UmVnZXggPSAvXCI/KFthLXowLTlBLVpfXSspXCI/XFxzKjovZztcbiAgICBKU09OU3RyaW5nID0gSlNPTlN0cmluZy5yZXBsYWNlKEpTT05Qcm9wZXJ0eVJlZ2V4LCAnXCIkMVwiOiAnKTtcblxuICAgIC8vIFByZXZlbnQgc2V0dGluZyBwcm90ZWN0ZWQgcHJvcGVydGllc1xuICAgIGlmIChKU09OU3RyaW5nLm1hdGNoKC9cIj9fX3Byb3RvX19cIj9cXHMqOi9nKSkge1xuICAgICAgdGhyb3cgRXJyb3IoJ1NldHRpbmcgdGhlIFwiX19wcm90b19fXCIgcHJvcGVydHkgaW4gYSBob29rIGlucHV0IG9iamVjdCBpcyBub3QgYWxsb3dlZC4nKTtcbiAgICB9XG4gICAgaWYgKEpTT05TdHJpbmcubWF0Y2goL1wiP3Byb3RvdHlwZVwiP1xccyo6L2cpKSB7XG4gICAgICB0aHJvdyBFcnJvcignU2V0dGluZyB0aGUgXCJwcm90b3R5cGVcIiBwcm9wZXJ0eSBpbiBhIGhvb2sgaW5wdXQgb2JqZWN0IGlzIG5vdCBhbGxvd2VkLicpO1xuICAgIH1cbiAgICBpZiAoSlNPTlN0cmluZy5tYXRjaCgvXCI/Y29uc3RydWN0b3JcIj9cXHMqOi9nKSkge1xuICAgICAgdGhyb3cgRXJyb3IoJ1NldHRpbmcgdGhlIFwiY29uc3RydWN0b3JcIiBwcm9wZXJ0eSBpbiBhIGhvb2sgaW5wdXQgb2JqZWN0IGlzIG5vdCBhbGxvd2VkLicpO1xuICAgIH1cblxuICAgIC8vIFJlcGxhY2UgdW5kZWZpbmVkIHdpdGggbnVsbFxuICAgIEpTT05TdHJpbmcgPSB0aGlzLnJlcGxhY2VWYWx1ZXNJbkpTT05TdHJpbmcoSlNPTlN0cmluZywgJ3VuZGVmaW5lZCcsIG1hdGNoID0+ICdudWxsJyk7XG5cbiAgICAvLyBSZXBsYWNlIGNvbnRleHQgdmFycyB3aXRoIHN0cmluZyBwbGFjZWhvbGRlcnNcbiAgICBKU09OU3RyaW5nID0gdGhpcy5yZXBsYWNlVmFsdWVzSW5KU09OU3RyaW5nKEpTT05TdHJpbmcsIHJlZ2V4ZXMuY29udGV4dFZhcmlhYmxlUmVnZXgsIChtYXRjaCkgPT4ge1xuICAgICAgcmV0dXJuICdcIicgKyB0aGlzLmRhdGFUeXBlRW5jb2Rlci50cmFuc2Zvcm1Db250ZXh0VmFySW50b1BsYWNlcmhvbGRlcihtYXRjaCkgKyAnXCInO1xuICAgIH0pO1xuXG4gICAgLy8gUmVwbGFjZSAkZXZlbnQgd2l0aCBzdHJpbmcgcGxhY2Vob2xkZXJzXG4gICAgSlNPTlN0cmluZyA9IHRoaXMucmVwbGFjZVZhbHVlc0luSlNPTlN0cmluZyhKU09OU3RyaW5nLCAnXFxcXCRldmVudCcsIG1hdGNoID0+ICdcIl9fRVZFTlRfX1wiJyk7XG5cbiAgICAvLyBQQVJTRVxuICAgIGNvbnN0IGpzb24gPSBKU09OLnBhcnNlKEpTT05TdHJpbmcpO1xuXG4gICAgLy8gRGVjb2RlIGFsbCBzdHJpbmdzIHRoYXQgYXJlIG5vdCBjb250ZXh0IHZhcnMgb3IgdGhlIGV2ZW50IG9iamVjdFxuICAgIHRoaXMuZGVjb2RlSlNPTlN0cmluZ3MoanNvbiwgdW5lc2NhcGVTdHJpbmdzKTtcblxuICAgIHJldHVybiBqc29uO1xuICB9XG5cbiAgLyoqXG4gICAqIEdpdmVuIGEgc3RyaW5naWZpZWQganNvbiBhbmQgYSBqc29uIHZhbHVlIHJlZ2V4LCBhbGxvd3MgeW91IHRvIHJlcGxhY2UgYWxsIG9jY3VyZW5jZXNcbiAgICogb2YgdGhvc2UgdmFsdWVzIGluIHRoZSBqc29uIHZpYSBhIGNhbGxiYWNrIGZ1bmN0aW9uLlxuICAgKlxuICAgKiBJTVBPUlRBTlQ6IEpTT05TdHJpbmcgbXVzdCBiZSBhbHJlYWR5IGVuY29kZWQgdmlhIHRoaXMuZW5jb2RlRGF0YVR5cGVTdHJpbmcoKSBmb3IgdGhpcyB0byB3b3JrLlxuICAgKlxuICAgKiBAcGFyYW0gSlNPTlN0cmluZyAtIFRoZSBzdHJpbmdpZmllZCBKU09OXG4gICAqIEBwYXJhbSB2YWx1ZVJlZ2V4IC0gVGhlIHZhbHVlcyB0byBmaW5kXG4gICAqIEBwYXJhbSBjYWxsYmFja0ZuIC0gQSBjYWxsYmFjayBmbiB0aGF0IHJldHVybnMgd2hhdCB5b3Ugd2FudCB0byByZXBsYWNlIHRoZW0gd2l0aFxuICAgKi9cbiAgcHJpdmF0ZSByZXBsYWNlVmFsdWVzSW5KU09OU3RyaW5nKEpTT05TdHJpbmc6IHN0cmluZywgdmFsdWVSZWdleDogc3RyaW5nLCBjYWxsYmFja0ZuOiAobWF0Y2g6IHN0cmluZykgPT4gc3RyaW5nKTogc3RyaW5nIHtcbiAgICAvLyBXaXRoIGxvb2tiZWhpbmRzICh0b28gbmV3IGZvciBzb21lIGJyb3dzZXJzKVxuICAgIGNvbnN0IHdpdGhMb29rQmVoaW5kc1JlZ2V4ID0gJyg/OicgK1xuICAgICAgJyg/PD06XFxcXHMqKScgKyB2YWx1ZVJlZ2V4ICsgJyg/PVxcXFxzKlssfV0pJyArICd8JyArXG4gICAgICAnKD88PVtcXFxcWyxdXFxcXHMqKScgKyB2YWx1ZVJlZ2V4ICsgJyg/PVxcXFxzKltcXFxcXSxdKScgK1xuICAgICcpJztcblxuICAgIC8vIFdpdGhvdXQgbG9va2JlaGluZHMgKG1ha2Ugc3VyZSB0byBrZWVwIHRoZSBsb29rYWhlYWRzLCB0aG91Z2guIFRoaXMgd2F5LCB0aGUgc2FtZSBjb21tYSBjYW4gYmUgdGhlIGVuZCBvZiBvbmUgcmVnZXggYW5kIHRoZSBiZWdpbm5pbmcgb2YgdGhlIG5leHQpXG4gICAgY29uc3QgcmVnZXggPSAnKD86JyArXG4gICAgICAnKDpcXFxccyopKCcgKyB2YWx1ZVJlZ2V4ICsgJykoPz1cXFxccypbLH1dKScgKyAnfCcgKyAgICAvLyBWYWx1ZSBpbiBvYmplY3Q6ICc6JyBmb2xsb3dlZCBieSB2YWx1ZSBmb2xsb3dlZCBieSAnLCcgb3IgJ30nXG4gICAgICAnKFtcXFxcWyxdXFxcXHMqKSgnICsgdmFsdWVSZWdleCArICcpKD89XFxcXHMqW1xcXFxdLF0pJyArICAgLy8gVmFsdWUgaW4gYXJyYXk6ICdbJyBvciAnLCcgZm9sbG93ZWQgYnkgdmFsdWUgZm9sbG93ZWQgYnkgJywnIG9yICddJ1xuICAgICcpJztcblxuICAgIHJldHVybiBKU09OU3RyaW5nLnJlcGxhY2UobmV3IFJlZ0V4cChyZWdleCwgJ2dtJyksIChmdWxsLCBwMSwgcDIsIHAzLCBwNCkgPT4ge1xuICAgICAgY29uc3Qgc3RhcnRQYXJ0ID0gcDEgPyBwMSA6IHAzO1xuICAgICAgY29uc3QgdmFsdWUgPSBwMiA/IHAyIDogcDQ7XG4gICAgICByZXR1cm4gc3RhcnRQYXJ0ICsgY2FsbGJhY2tGbih2YWx1ZSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogRGVjb2RlcyBhbGwgJ25vcm1hbCcgc3RyaW5ncyB3aXRob3V0IHNwZWNpYWwgbWVhbmluZyBpbiBhIEpTT04tbGlrZSBvYmplY3RcbiAgICpcbiAgICogQHBhcmFtIGpzb25MZXZlbCAtIFRoZSBjdXJyZW50IGxldmVsIG9mIHBhcnNpbmdcbiAgICogQHBhcmFtIHVuZXNjYXBlU3RyaW5ncyAtIFdoZXRoZXIgdG8gdW5lc2NhcGUgdGhlIGRlY29kZWQgc3RyaW5ncyBhcyB3ZWxsXG4gICAqL1xuICBwcml2YXRlIGRlY29kZUpTT05TdHJpbmdzKGpzb25MZXZlbDogYW55LCB1bmVzY2FwZVN0cmluZ3M6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICBmb3IgKGNvbnN0IHByb3AgaW4ganNvbkxldmVsKSB7XG4gICAgICBpZiAodHlwZW9mIGpzb25MZXZlbFtwcm9wXSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgLy8gSWdub3JlIHZhciBwbGFjZWhvbGRlcnNcbiAgICAgICAgaWYgKGpzb25MZXZlbFtwcm9wXSA9PT0gJ19fRVZFTlRfX1wiJyB8fCBqc29uTGV2ZWxbcHJvcF0ubWF0Y2gobmV3IFJlZ0V4cCgnXlxcXFxzKicgKyByZWdleGVzLnBsYWNlaG9sZGVyQ29udGV4dFZhcmlhYmxlUmVnZXggKyAnXFxcXHMqJCcsICdnbScpKSkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIC8vIE90aGVyd2lzZSBkZWNvZGUgc3RyaW5nXG4gICAgICAg