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
JavaScript
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