"use strict";
/*
* Core utility functions for the RSpade framework.
* These functions handle type checking, type conversion, string manipulation,
* and object/array utilities. They mirror functionality from PHP functions.
*
* Other utility functions are organized in:
* - async.js: Async utilities (sleep, debounce, mutex)
* - browser.js: Browser/DOM utilities (is_mobile, scroll functions)
* - datetime.js: Date/time utilities
* - hash.js: Hashing and comparison
* - error.js: Error handling
*/
// Todo: test that prod build identifies and removes uncalled functions from the final bundle.
// ============================================================================
// CONSTANTS AND HELPERS
// ============================================================================
// Define commonly used constants
const undef = 'undefined';
/**
* Iterates over arrays or objects with promise support
*
* Works with both synchronous and asynchronous callbacks. If the callback
* returns promises, they are executed in parallel and this function returns
* a promise that resolves when all parallel tasks complete.
*
* @param {Array|Object} obj - Collection to iterate
* @param {Function} callback - Function to call for each item (value, key) - can be async
* @returns {Promise|undefined} Promise if any callbacks return promises, undefined otherwise
*
* @example
* // Synchronous usage
* foreach([1,2,3], (val) => console.log(val));
*
* @example
* // Asynchronous usage - waits for all to complete
* await foreach([1,2,3], async (val) => {
* await fetch('/api/process/' + val);
* });
*/
function foreach(obj, callback) {
const results = [];
if (Array.isArray(obj)) {
obj.forEach((value, index) => {
results.push(callback(value, index));
});
} else if (obj && typeof obj === 'object') {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
results.push(callback(obj[key], key));
}
}
}
// Filter for promises
const promises = results.filter(result => result && typeof result.then === 'function');
// If there are any promises, return Promise.all to wait for all to complete
if (promises.length > 0) {
return Promise.all(promises);
}
// No promises returned, so we're done
return undefined;
}
// ============================================================================
// TYPE CHECKING FUNCTIONS
// ============================================================================
/**
* Checks if a value is numeric
* @param {*} n - Value to check
* @returns {boolean} True if the value is a finite number
*/
function is_numeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
/**
* Checks if a value is a string
* @param {*} s - Value to check
* @returns {boolean} True if the value is a string
*/
function is_string(s) {
return typeof s == 'string';
}
/**
* Checks if a value is an integer
* @param {*} n - Value to check
* @returns {boolean} True if the value is an integer
*/
function is_integer(n) {
return Number.isInteger(n);
}
/**
* Checks if a value is a promise-like object
* @param {*} obj - Value to check
* @returns {boolean} True if the value has a then method
*/
function is_promise(obj) {
return typeof obj == 'object' && typeof obj.then == 'function';
}
/**
* Checks if a value is an array
* @param {*} obj - Value to check
* @returns {boolean} True if the value is an array
*/
function is_array(obj) {
return Array.isArray(obj);
}
/**
* Checks if a value is an object (excludes null)
* @param {*} obj - Value to check
* @returns {boolean} True if the value is an object and not null
*/
function is_object(obj) {
return typeof obj === 'object' && obj !== null;
}
/**
* Checks if a value is a function
* @param {*} function_to_check - Value to check
* @returns {boolean} True if the value is a function
*/
function is_function(function_to_check) {
return function_to_check && {}.toString.call(function_to_check) === '[object Function]';
}
/**
* Checks if a string is a valid email address
* Uses a practical RFC 5322 compliant regex that matches 99.99% of real-world email addresses
* @param {string} email - Email address to validate
* @returns {boolean} True if the string is a valid email address
*/
function is_email(email) {
if (!is_string(email)) {
return false;
}
const regex = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
return regex.test(email);
}
/**
* Checks if a value is defined (not undefined)
* @param {*} value - Value to check
* @returns {boolean} True if value is not undefined
*/
function isset(value) {
return typeof value != undef;
}
/**
* Checks if a value is empty (null, undefined, 0, "", empty array/object)
* @param {*} object - Value to check
* @returns {boolean} True if the value is considered empty
*/
function empty(object) {
if (typeof object == undef) {
return true;
}
if (object === null) {
return true;
}
if (typeof object == 'string' && object == '') {
return true;
}
if (typeof object == 'number') {
return object == 0;
}
if (Array.isArray(object)) {
return !object.length;
}
if (typeof object == 'function') {
return false;
}
for (let key in object) {
if (object.hasOwnProperty(key)) {
return false;
}
}
return true;
}
// ============================================================================
// TYPE CONVERSION FUNCTIONS
// ============================================================================
/**
* Converts a value to a floating point number
* Returns 0 for null, undefined, NaN, or non-numeric values
* @param {*} val - Value to convert
* @returns {number} Floating point number
*/
function float(val) {
// Handle null, undefined, empty string
if (val === null || val === undefined || val === '') {
return 0.0;
}
// Try to parse the value
const parsed = parseFloat(val);
// Check for NaN and return 0 if parsing failed
return isNaN(parsed) ? 0.0 : parsed;
}
/**
* Converts a value to an integer
* Returns 0 for null, undefined, NaN, or non-numeric values
* @param {*} val - Value to convert
* @returns {number} Integer value
*/
function int(val) {
// Handle null, undefined, empty string
if (val === null || val === undefined || val === '') {
return 0;
}
// Try to parse the value
const parsed = parseInt(val, 10);
// Check for NaN and return 0 if parsing failed
return isNaN(parsed) ? 0 : parsed;
}
/**
* Converts a value to a string
* Returns empty string for null or undefined
* @param {*} val - Value to convert
* @returns {string} String representation
*/
function str(val) {
// Handle null and undefined specially
if (val === null || val === undefined) {
return '';
}
// Convert to string
return String(val);
}
/**
* Converts numeric strings to numbers, returns all other values unchanged
* Used when you need to ensure numeric types but don't want to force
* conversion of non-numeric values (which would become 0)
* @param {*} val - Value to convert
* @returns {*} Number if input was numeric string, otherwise unchanged
*/
function value_unless_numeric_string_then_numeric_value(val) {
// If it's already a number, return it
if (typeof val === 'number') {
return val;
}
// If it's a string and numeric, convert it
if (is_string(val) && is_numeric(val)) {
// Use parseFloat to handle both integers and floats
return parseFloat(val);
}
// Return everything else unchanged (null, objects, non-numeric strings, etc.)
return val;
}
// ============================================================================
// STRING MANIPULATION FUNCTIONS
// ============================================================================
/**
* Escapes HTML special characters (uses Lodash escape)
* @param {string} str - String to escape
* @returns {string} HTML-escaped string
*/
function html(str) {
return _.escape(str);
}
/**
* Converts newlines to HTML line breaks
* @param {string} str - String to convert
* @returns {string} String with newlines replaced by
*/
function nl2br(str) {
if (typeof str === undef || str === null) {
return '';
}
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1
$2');
}
/**
* Escapes HTML and converts newlines to
* @param {string} str - String to process
* @returns {string} HTML-escaped string with line breaks
*/
function htmlbr(str) {
return nl2br(html(str));
}
/**
* URL-encodes a string
* @param {string} str - String to encode
* @returns {string} URL-encoded string
*/
function urlencode(str) {
return encodeURIComponent(str);
}
/**
* URL-decodes a string
* @param {string} str - String to decode
* @returns {string} URL-decoded string
*/
function urldecode(str) {
return decodeURIComponent(str);
}
/**
* JSON-encodes a value
* @param {*} value - Value to encode
* @returns {string} JSON string
*/
function json_encode(value) {
return JSON.stringify(value);
}
/**
* JSON-decodes a string
* @param {string} str - JSON string to decode
* @returns {*} Decoded value
*/
function json_decode(str) {
return JSON.parse(str);
}
/**
* Console debug output with channel filtering
* Alias for Debugger.console_debug
* @param {string} channel - Debug channel name
* @param {...*} values - Values to log
*/
function console_debug(channel) {
for (var _len = arguments.length, values = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
values[_key - 1] = arguments[_key];
}
Debugger.console_debug(channel, ...values);
}
/**
* Replaces all occurrences of a substring in a string
* @param {string} string - String to search in
* @param {string} search - Substring to find
* @param {string} replace - Replacement substring
* @returns {string} String with all occurrences replaced
*/
function replace_all(string, search, replace) {
if (!is_string(string)) {
string = string + '';
}
return string.split(search).join(replace);
}
/**
* Capitalizes the first letter of each word
* @param {string} input - String to capitalize
* @returns {string} String with first letter of each word capitalized
*/
function ucwords(input) {
return input.split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
}
// ============================================================================
// OBJECT AND ARRAY UTILITIES
// ============================================================================
/**
* Counts the number of properties in an object or elements in an array
* @param {Object|Array} o - Object or array to count
* @returns {number} Number of own properties/elements
*/
function count(o) {
let c = 0;
for (const k in o) {
if (o.hasOwnProperty(k)) {
++c;
}
}
return c;
}
/**
* Creates a shallow clone of an object, array, or function
* @param {*} obj - Value to clone
* @returns {*} Cloned value
*/
function clone(obj) {
if (typeof Function.prototype.__clone == undef) {
Function.prototype.__clone = function () {
//https://stackoverflow.com/questions/1833588/javascript-clone-a-function
const that = this;
let temp = function cloned() {
return that.apply(this, arguments);
};
for (let key in this) {
if (this.hasOwnProperty(key)) {
temp[key] = this[key];
}
}
return temp;
};
}
if (typeof obj == 'function') {
return obj.__clone();
} else if (obj.constructor && obj.constructor == Array) {
return obj.slice(0);
} else {
// https://stackoverflow.com/questions/728360/how-do-i-correctly-clone-a-javascript-object/30042948#30042948
return Object.assign({}, obj);
}
}
/**
* Returns the first non-null/undefined value from arguments
* @param {...*} arguments - Values to check
* @returns {*} First non-null/undefined value, or null if none found
*/
function coalesce() {
let args = Array.from(arguments);
let return_val = null;
args.forEach(function (arg) {
if (return_val === null && typeof arg != undef && arg !== null) {
return_val = arg;
}
});
return return_val;
}
/**
* Converts CSV string to array, trimming each element
* @param {string} str_csv - CSV string to convert
* @returns {Array} Array of trimmed values
* @todo Handle quoted/escaped characters
*/
function csv_to_array_trim(str_csv) {
const parts = str_csv.split(',');
const ret = [];
foreach(parts, part => {
ret.push(part.trim());
});
return ret;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ1bmRlZiIsImZvcmVhY2giLCJvYmoiLCJjYWxsYmFjayIsInJlc3VsdHMiLCJBcnJheSIsImlzQXJyYXkiLCJmb3JFYWNoIiwidmFsdWUiLCJpbmRleCIsInB1c2giLCJrZXkiLCJoYXNPd25Qcm9wZXJ0eSIsInByb21pc2VzIiwiZmlsdGVyIiwicmVzdWx0IiwidGhlbiIsImxlbmd0aCIsIlByb21pc2UiLCJhbGwiLCJ1bmRlZmluZWQiLCJpc19udW1lcmljIiwibiIsImlzTmFOIiwicGFyc2VGbG9hdCIsImlzRmluaXRlIiwiaXNfc3RyaW5nIiwicyIsImlzX2ludGVnZXIiLCJOdW1iZXIiLCJpc0ludGVnZXIiLCJpc19wcm9taXNlIiwiaXNfYXJyYXkiLCJpc19vYmplY3QiLCJpc19mdW5jdGlvbiIsImZ1bmN0aW9uX3RvX2NoZWNrIiwidG9TdHJpbmciLCJjYWxsIiwiaXNfZW1haWwiLCJlbWFpbCIsInJlZ2V4IiwidGVzdCIsImlzc2V0IiwiZW1wdHkiLCJvYmplY3QiLCJmbG9hdCIsInZhbCIsInBhcnNlZCIsImludCIsInBhcnNlSW50Iiwic3RyIiwiU3RyaW5nIiwidmFsdWVfdW5sZXNzX251bWVyaWNfc3RyaW5nX3RoZW5fbnVtZXJpY192YWx1ZSIsImh0bWwiLCJfIiwiZXNjYXBlIiwibmwyYnIiLCJyZXBsYWNlIiwiaHRtbGJyIiwidXJsZW5jb2RlIiwiZW5jb2RlVVJJQ29tcG9uZW50IiwidXJsZGVjb2RlIiwiZGVjb2RlVVJJQ29tcG9uZW50IiwianNvbl9lbmNvZGUiLCJKU09OIiwic3RyaW5naWZ5IiwianNvbl9kZWNvZGUiLCJwYXJzZSIsImNvbnNvbGVfZGVidWciLCJjaGFubmVsIiwiX2xlbiIsImFyZ3VtZW50cyIsInZhbHVlcyIsIl9rZXkiLCJEZWJ1Z2dlciIsInJlcGxhY2VfYWxsIiwic3RyaW5nIiwic2VhcmNoIiwic3BsaXQiLCJqb2luIiwidWN3b3JkcyIsImlucHV0IiwibWFwIiwid29yZCIsImNoYXJBdCIsInRvVXBwZXJDYXNlIiwic2xpY2UiLCJjb3VudCIsIm8iLCJjIiwiayIsImNsb25lIiwiRnVuY3Rpb24iLCJwcm90b3R5cGUiLCJfX2Nsb25lIiwidGhhdCIsInRlbXAiLCJjbG9uZWQiLCJhcHBseSIsImNvbnN0cnVjdG9yIiwiT2JqZWN0IiwiYXNzaWduIiwiY29hbGVzY2UiLCJhcmdzIiwiZnJvbSIsInJldHVybl92YWwiLCJhcmciLCJjc3ZfdG9fYXJyYXlfdHJpbSIsInN0cl9jc3YiLCJwYXJ0cyIsInJldCIsInBhcnQiLCJ0cmltIl0sInNvdXJjZXMiOlsiYXBwL1JTcGFkZS9Db3JlL0pzL2Z1bmN0aW9ucy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogQ29yZSB1dGlsaXR5IGZ1bmN0aW9ucyBmb3IgdGhlIFJTcGFkZSBmcmFtZXdvcmsuXG4gKiBUaGVzZSBmdW5jdGlvbnMgaGFuZGxlIHR5cGUgY2hlY2tpbmcsIHR5cGUgY29udmVyc2lvbiwgc3RyaW5nIG1hbmlwdWxhdGlvbixcbiAqIGFuZCBvYmplY3QvYXJyYXkgdXRpbGl0aWVzLiBUaGV5IG1pcnJvciBmdW5jdGlvbmFsaXR5IGZyb20gUEhQIGZ1bmN0aW9ucy5cbiAqXG4gKiBPdGhlciB1dGlsaXR5IGZ1bmN0aW9ucyBhcmUgb3JnYW5pemVkIGluOlxuICogLSBhc3luYy5qczogQXN5bmMgdXRpbGl0aWVzIChzbGVlcCwgZGVib3VuY2UsIG11dGV4KVxuICogLSBicm93c2VyLmpzOiBCcm93c2VyL0RPTSB1dGlsaXRpZXMgKGlzX21vYmlsZSwgc2Nyb2xsIGZ1bmN0aW9ucylcbiAqIC0gZGF0ZXRpbWUuanM6IERhdGUvdGltZSB1dGlsaXRpZXNcbiAqIC0gaGFzaC5qczogSGFzaGluZyBhbmQgY29tcGFyaXNvblxuICogLSBlcnJvci5qczogRXJyb3IgaGFuZGxpbmdcbiAqL1xuXG4vLyBUb2RvOiB0ZXN0IHRoYXQgcHJvZCBidWlsZCBpZGVudGlmaWVzIGFuZCByZW1vdmVzIHVuY2FsbGVkIGZ1bmN0aW9ucyBmcm9tIHRoZSBmaW5hbCBidW5kbGUuXG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIENPTlNUQU5UUyBBTkQgSEVMUEVSU1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vLyBEZWZpbmUgY29tbW9ubHkgdXNlZCBjb25zdGFudHNcbmNvbnN0IHVuZGVmID0gJ3VuZGVmaW5lZCc7XG5cbi8qKlxuICogSXRlcmF0ZXMgb3ZlciBhcnJheXMgb3Igb2JqZWN0cyB3aXRoIHByb21pc2Ugc3VwcG9ydFxuICpcbiAqIFdvcmtzIHdpdGggYm90aCBzeW5jaHJvbm91cyBhbmQgYXN5bmNocm9ub3VzIGNhbGxiYWNrcy4gSWYgdGhlIGNhbGxiYWNrXG4gKiByZXR1cm5zIHByb21pc2VzLCB0aGV5IGFyZSBleGVjdXRlZCBpbiBwYXJhbGxlbCBhbmQgdGhpcyBmdW5jdGlvbiByZXR1cm5zXG4gKiBhIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIGFsbCBwYXJhbGxlbCB0YXNrcyBjb21wbGV0ZS5cbiAqXG4gKiBAcGFyYW0ge0FycmF5fE9iamVjdH0gb2JqIC0gQ29sbGVjdGlvbiB0byBpdGVyYXRlXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBjYWxsYmFjayAtIEZ1bmN0aW9uIHRvIGNhbGwgZm9yIGVhY2ggaXRlbSAodmFsdWUsIGtleSkgLSBjYW4gYmUgYXN5bmNcbiAqIEByZXR1cm5zIHtQcm9taXNlfHVuZGVmaW5lZH0gUHJvbWlzZSBpZiBhbnkgY2FsbGJhY2tzIHJldHVybiBwcm9taXNlcywgdW5kZWZpbmVkIG90aGVyd2lzZVxuICpcbiAqIEBleGFtcGxlXG4gKiAvLyBTeW5jaHJvbm91cyB1c2FnZVxuICogZm9yZWFjaChbMSwyLDNdLCAodmFsKSA9PiBjb25zb2xlLmxvZyh2YWwpKTtcbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gQXN5bmNocm9ub3VzIHVzYWdlIC0gd2FpdHMgZm9yIGFsbCB0byBjb21wbGV0ZVxuICogYXdhaXQgZm9yZWFjaChbMSwyLDNdLCBhc3luYyAodmFsKSA9PiB7XG4gKiAgICAgYXdhaXQgZmV0Y2goJy9hcGkvcHJvY2Vzcy8nICsgdmFsKTtcbiAqIH0pO1xuICovXG5mdW5jdGlvbiBmb3JlYWNoKG9iaiwgY2FsbGJhY2spIHtcbiAgICBjb25zdCByZXN1bHRzID0gW107XG5cbiAgICBpZiAoQXJyYXkuaXNBcnJheShvYmopKSB7XG4gICAgICAgIG9iai5mb3JFYWNoKCh2YWx1ZSwgaW5kZXgpID0+IHtcbiAgICAgICAgICAgIHJlc3VsdHMucHVzaChjYWxsYmFjayh2YWx1ZSwgaW5kZXgpKTtcbiAgICAgICAgfSk7XG4gICAgfSBlbHNlIGlmIChvYmogJiYgdHlwZW9mIG9iaiA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgZm9yIChsZXQga2V5IGluIG9iaikge1xuICAgICAgICAgICAgaWYgKG9iai5oYXNPd25Qcm9wZXJ0eShrZXkpKSB7XG4gICAgICAgICAgICAgICAgcmVzdWx0cy5wdXNoKGNhbGxiYWNrKG9ialtrZXldLCBrZXkpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8vIEZpbHRlciBmb3IgcHJvbWlzZXNcbiAgICBjb25zdCBwcm9taXNlcyA9IHJlc3VsdHMuZmlsdGVyKChyZXN1bHQpID0+IHJlc3VsdCAmJiB0eXBlb2YgcmVzdWx0LnRoZW4gPT09ICdmdW5jdGlvbicpO1xuXG4gICAgLy8gSWYgdGhlcmUgYXJlIGFueSBwcm9taXNlcywgcmV0dXJuIFByb21pc2UuYWxsIHRvIHdhaXQgZm9yIGFsbCB0byBjb21wbGV0ZVxuICAgIGlmIChwcm9taXNlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIHJldHVybiBQcm9taXNlLmFsbChwcm9taXNlcyk7XG4gICAgfVxuXG4gICAgLy8gTm8gcHJvbWlzZXMgcmV0dXJuZWQsIHNvIHdlJ3JlIGRvbmVcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xufVxuXG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFRZUEUgQ0hFQ0tJTkcgRlVOQ1RJT05TXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qKlxuICogQ2hlY2tzIGlmIGEgdmFsdWUgaXMgbnVtZXJpY1xuICogQHBhcmFtIHsqfSBuIC0gVmFsdWUgdG8gY2hlY2tcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSB2YWx1ZSBpcyBhIGZpbml0ZSBudW1iZXJcbiAqL1xuZnVuY3Rpb24gaXNfbnVtZXJpYyhuKSB7XG4gICAgcmV0dXJuICFpc05hTihwYXJzZUZsb2F0KG4pKSAmJiBpc0Zpbml0ZShuKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYSB2YWx1ZSBpcyBhIHN0cmluZ1xuICogQHBhcmFtIHsqfSBzIC0gVmFsdWUgdG8gY2hlY2tcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSB2YWx1ZSBpcyBhIHN0cmluZ1xuICovXG5mdW5jdGlvbiBpc19zdHJpbmcocykge1xuICAgIHJldHVybiB0eXBlb2YgcyA9PSAnc3RyaW5nJztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYSB2YWx1ZSBpcyBhbiBpbnRlZ2VyXG4gKiBAcGFyYW0geyp9IG4gLSBWYWx1ZSB0byBjaGVja1xuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIHZhbHVlIGlzIGFuIGludGVnZXJcbiAqL1xuZnVuY3Rpb24gaXNfaW50ZWdlcihuKSB7XG4gICAgcmV0dXJuIE51bWJlci5pc0ludGVnZXIobik7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGEgdmFsdWUgaXMgYSBwcm9taXNlLWxpa2Ugb2JqZWN0XG4gKiBAcGFyYW0geyp9IG9iaiAtIFZhbHVlIHRvIGNoZWNrXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgdmFsdWUgaGFzIGEgdGhlbiBtZXRob2RcbiAqL1xuZnVuY3Rpb24gaXNfcHJvbWlzZShvYmopIHtcbiAgICByZXR1cm4gdHlwZW9mIG9iaiA9PSAnb2JqZWN0JyAmJiB0eXBlb2Ygb2JqLnRoZW4gPT0gJ2Z1bmN0aW9uJztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYSB2YWx1ZSBpcyBhbiBhcnJheVxuICogQHBhcmFtIHsqfSBvYmogLSBWYWx1ZSB0byBjaGVja1xuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIHZhbHVlIGlzIGFuIGFycmF5XG4gKi9cbmZ1bmN0aW9uIGlzX2FycmF5KG9iaikge1xuICAgIHJldHVybiBBcnJheS5pc0FycmF5KG9iaik7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGEgdmFsdWUgaXMgYW4gb2JqZWN0IChleGNsdWRlcyBudWxsKVxuICogQHBhcmFtIHsqfSBvYmogLSBWYWx1ZSB0byBjaGVja1xuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIHZhbHVlIGlzIGFuIG9iamVjdCBhbmQgbm90IG51bGxcbiAqL1xuZnVuY3Rpb24gaXNfb2JqZWN0KG9iaikge1xuICAgIHJldHVybiB0eXBlb2Ygb2JqID09PSAnb2JqZWN0JyAmJiBvYmogIT09IG51bGw7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGEgdmFsdWUgaXMgYSBmdW5jdGlvblxuICogQHBhcmFtIHsqfSBmdW5jdGlvbl90b19jaGVjayAtIFZhbHVlIHRvIGNoZWNrXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgdmFsdWUgaXMgYSBmdW5jdGlvblxuICovXG5mdW5jdGlvbiBpc19mdW5jdGlvbihmdW5jdGlvbl90b19jaGVjaykge1xuICAgIHJldHVybiBmdW5jdGlvbl90b19jaGVjayAmJiB7fS50b1N0cmluZy5jYWxsKGZ1bmN0aW9uX3RvX2NoZWNrKSA9PT0gJ1tvYmplY3QgRnVuY3Rpb25dJztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYSBzdHJpbmcgaXMgYSB2YWxpZCBlbWFpbCBhZGRyZXNzXG4gKiBVc2VzIGEgcHJhY3RpY2FsIFJGQyA1MzIyIGNvbXBsaWFudCByZWdleCB0aGF0IG1hdGNoZXMgOTkuOTklIG9mIHJlYWwtd29ybGQgZW1haWwgYWRkcmVzc2VzXG4gKiBAcGFyYW0ge3N0cmluZ30gZW1haWwgLSBFbWFpbCBhZGRyZXNzIHRvIHZhbGlkYXRlXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgc3RyaW5nIGlzIGEgdmFsaWQgZW1haWwgYWRkcmVzc1xuICovXG5mdW5jdGlvbiBpc19lbWFpbChlbWFpbCkge1xuICAgIGlmICghaXNfc3RyaW5nKGVtYWlsKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGNvbnN0IHJlZ2V4ID0gL15bYS16MC05ISMkJSYnKisvPT9eX2B7fH1+LV0rKD86XFwuW2EtejAtOSEjJCUmJyorLz0/Xl9ge3x9fi1dKykqQCg/OlthLXowLTldKD86W2EtejAtOS1dKlthLXowLTldKT9cXC4pK1thLXowLTldKD86W2EtejAtOS1dKlthLXowLTldKT8kL2k7XG4gICAgcmV0dXJuIHJlZ2V4LnRlc3QoZW1haWwpO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBhIHZhbHVlIGlzIGRlZmluZWQgKG5vdCB1bmRlZmluZWQpXG4gKiBAcGFyYW0geyp9IHZhbHVlIC0gVmFsdWUgdG8gY2hlY2tcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHZhbHVlIGlzIG5vdCB1bmRlZmluZWRcbiAqL1xuZnVuY3Rpb24gaXNzZXQodmFsdWUpIHtcbiAgICByZXR1cm4gdHlwZW9mIHZhbHVlICE9IHVuZGVmO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBhIHZhbHVlIGlzIGVtcHR5IChudWxsLCB1bmRlZmluZWQsIDAsIFwiXCIsIGVtcHR5IGFycmF5L29iamVjdClcbiAqIEBwYXJhbSB7Kn0gb2JqZWN0IC0gVmFsdWUgdG8gY2hlY2tcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSB2YWx1ZSBpcyBjb25zaWRlcmVkIGVtcHR5XG4gKi9cbmZ1bmN0aW9uIGVtcHR5KG9iamVjdCkge1xuICAgIGlmICh0eXBlb2Ygb2JqZWN0ID09IHVuZGVmKSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBpZiAob2JqZWN0ID09PSBudWxsKSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBpZiAodHlwZW9mIG9iamVjdCA9PSAnc3RyaW5nJyAmJiBvYmplY3QgPT0gJycpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIGlmICh0eXBlb2Ygb2JqZWN0ID09ICdudW1iZXInKSB7XG4gICAgICAgIHJldHVybiBvYmplY3QgPT0gMDtcbiAgICB9XG4gICAgaWYgKEFycmF5LmlzQXJyYXkob2JqZWN0KSkge1xuICAgICAgICByZXR1cm4gIW9iamVjdC5sZW5ndGg7XG4gICAgfVxuICAgIGlmICh0eXBlb2Ygb2JqZWN0ID09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBmb3IgKGxldCBrZXkgaW4gb2JqZWN0KSB7XG4gICAgICAgIGlmIChvYmplY3QuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBUWVBFIENPTlZFUlNJT04gRlVOQ1RJT05TXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qKlxuICogQ29udmVydHMgYSB2YWx1ZSB0byBhIGZsb2F0aW5nIHBvaW50IG51bWJlclxuICogUmV0dXJucyAwIGZvciBudWxsLCB1bmRlZmluZWQsIE5hTiwgb3Igbm9uLW51bWVyaWMgdmFsdWVzXG4gKiBAcGFyYW0geyp9IHZhbCAtIFZhbHVlIHRvIGNvbnZlcnRcbiAqIEByZXR1cm5zIHtudW1iZXJ9IEZsb2F0aW5nIHBvaW50IG51bWJlclxuICovXG5mdW5jdGlvbiBmbG9hdCh2YWwpIHtcbiAgICAvLyBIYW5kbGUgbnVsbCwgdW5kZWZpbmVkLCBlbXB0eSBzdHJpbmdcbiAgICBpZiAodmFsID09PSBudWxsIHx8IHZhbCA9PT0gdW5kZWZpbmVkIHx8IHZhbCA9PT0gJycpIHtcbiAgICAgICAgcmV0dXJuIDAuMDtcbiAgICB9XG5cbiAgICAvLyBUcnkgdG8gcGFyc2UgdGhlIHZhbHVlXG4gICAgY29uc3QgcGFyc2VkID0gcGFyc2VGbG9hdCh2YWwpO1xuXG4gICAgLy8gQ2hlY2sgZm9yIE5hTiBhbmQgcmV0dXJuIDAgaWYgcGFyc2luZyBmYWlsZWRcbiAgICByZXR1cm4gaXNOYU4ocGFyc2VkKSA/IDAuMCA6IHBhcnNlZDtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBhIHZhbHVlIHRvIGFuIGludGVnZXJcbiAqIFJldHVybnMgMCBmb3IgbnVsbCwgdW5kZWZpbmVkLCBOYU4sIG9yIG5vbi1udW1lcmljIHZhbHVlc1xuICogQHBhcmFtIHsqfSB2YWwgLSBWYWx1ZSB0byBjb252ZXJ0XG4gKiBAcmV0dXJucyB7bnVtYmVyfSBJbnRlZ2VyIHZhbHVlXG4gKi9cbmZ1bmN0aW9uIGludCh2YWwpIHtcbiAgICAvLyBIYW5kbGUgbnVsbCwgdW5kZWZpbmVkLCBlbXB0eSBzdHJpbmdcbiAgICBpZiAodmFsID09PSBudWxsIHx8IHZhbCA9PT0gdW5kZWZpbmVkIHx8IHZhbCA9PT0gJycpIHtcbiAgICAgICAgcmV0dXJuIDA7XG4gICAgfVxuXG4gICAgLy8gVHJ5IHRvIHBhcnNlIHRoZSB2YWx1ZVxuICAgIGNvbnN0IHBhcnNlZCA9IHBhcnNlSW50KHZhbCwgMTApO1xuXG4gICAgLy8gQ2hlY2sgZm9yIE5hTiBhbmQgcmV0dXJuIDAgaWYgcGFyc2luZyBmYWlsZWRcbiAgICByZXR1cm4gaXNOYU4ocGFyc2VkKSA/IDAgOiBwYXJzZWQ7XG59XG5cbi8qKlxuICogQ29udmVydHMgYSB2YWx1ZSB0byBhIHN0cmluZ1xuICogUmV0dXJucyBlbXB0eSBzdHJpbmcgZm9yIG51bGwgb3IgdW5kZWZpbmVkXG4gKiBAcGFyYW0geyp9IHZhbCAtIFZhbHVlIHRvIGNvbnZlcnRcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFN0cmluZyByZXByZXNlbnRhdGlvblxuICovXG5mdW5jdGlvbiBzdHIodmFsKSB7XG4gICAgLy8gSGFuZGxlIG51bGwgYW5kIHVuZGVmaW5lZCBzcGVjaWFsbHlcbiAgICBpZiAodmFsID09PSBudWxsIHx8IHZhbCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiAnJztcbiAgICB9XG5cbiAgICAvLyBDb252ZXJ0IHRvIHN0cmluZ1xuICAgIHJldHVybiBTdHJpbmcodmFsKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBudW1lcmljIHN0cmluZ3MgdG8gbnVtYmVycywgcmV0dXJucyBhbGwgb3RoZXIgdmFsdWVzIHVuY2hhbmdlZFxuICogVXNlZCB3aGVuIHlvdSBuZWVkIHRvIGVuc3VyZSBudW1lcmljIHR5cGVzIGJ1dCBkb24ndCB3YW50IHRvIGZvcmNlXG4gKiBjb252ZXJzaW9uIG9mIG5vbi1udW1lcmljIHZhbHVlcyAod2hpY2ggd291bGQgYmVjb21lIDApXG4gKiBAcGFyYW0geyp9IHZhbCAtIFZhbHVlIHRvIGNvbnZlcnRcbiAqIEByZXR1cm5zIHsqfSBOdW1iZXIgaWYgaW5wdXQgd2FzIG51bWVyaWMgc3RyaW5nLCBvdGhlcndpc2UgdW5jaGFuZ2VkXG4gKi9cbmZ1bmN0aW9uIHZhbHVlX3VubGVzc19udW1lcmljX3N0cmluZ190aGVuX251bWVyaWNfdmFsdWUodmFsKSB7XG4gICAgLy8gSWYgaXQncyBhbHJlYWR5IGEgbnVtYmVyLCByZXR1cm4gaXRcbiAgICBpZiAodHlwZW9mIHZhbCA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgcmV0dXJuIHZhbDtcbiAgICB9XG5cbiAgICAvLyBJZiBpdCdzIGEgc3RyaW5nIGFuZCBudW1lcmljLCBjb252ZXJ0IGl0XG4gICAgaWYgKGlzX3N0cmluZyh2YWwpICYmIGlzX251bWVyaWModmFsKSkge1xuICAgICAgICAvLyBVc2UgcGFyc2VGbG9hdCB0byBoYW5kbGUgYm90aCBpbnRlZ2VycyBhbmQgZmxvYXRzXG4gICAgICAgIHJldHVybiBwYXJzZUZsb2F0KHZhbCk7XG4gICAgfVxuXG4gICAgLy8gUmV0dXJuIGV2ZXJ5dGhpbmcgZWxzZSB1bmNoYW5nZWQgKG51bGwsIG9iamVjdHMsIG5vbi1udW1lcmljIHN0cmluZ3MsIGV0Yy4pXG4gICAgcmV0dXJuIHZhbDtcbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gU1RSSU5HIE1BTklQVUxBVElPTiBGVU5DVElPTlNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBFc2NhcGVzIEhUTUwgc3BlY2lhbCBjaGFyYWN0ZXJzICh1c2VzIExvZGFzaCBlc2NhcGUpXG4gKiBAcGFyYW0ge3N0cmluZ30gc3RyIC0gU3RyaW5nIHRvIGVzY2FwZVxuICogQHJldHVybnMge3N0cmluZ30gSFRNTC1lc2NhcGVkIHN0cmluZ1xuICovXG5mdW5jdGlvbiBodG1sKHN0cikge1xuICAgIHJldHVybiBfLmVzY2FwZShzdHIpO1xufVxuXG4vKipcbiAqIENvbnZlcnRzIG5ld2xpbmVzIHRvIEhUTUwgbGluZSBicmVha3NcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdHIgLSBTdHJpbmcgdG8gY29udmVydFxuICogQHJldHVybnMge3N0cmluZ30gU3RyaW5nIHdpdGggbmV3bGluZXMgcmVwbGFjZWQgYnkgPGJyIC8+XG4gKi9cbmZ1bmN0aW9uIG5sMmJyKHN0cikge1xuICAgIGlmICh0eXBlb2Ygc3RyID09PSB1bmRlZiB8fCBzdHIgPT09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuICcnO1xuICAgIH1cbiAgICByZXR1cm4gKHN0ciArICcnKS5yZXBsYWNlKC8oW14+XFxyXFxuXT8pKFxcclxcbnxcXG5cXHJ8XFxyfFxcbikvZywgJyQxPGJyIC8+JDInKTtcbn1cblxuLyoqXG4gKiBFc2NhcGVzIEhUTUwgYW5kIGNvbnZlcnRzIG5ld2xpbmVzIHRvIDxiciAvPlxuICogQHBhcmFtIHtzdHJpbmd9IHN0ciAtIFN0cmluZyB0byBwcm9jZXNzXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBIVE1MLWVzY2FwZWQgc3RyaW5nIHdpdGggbGluZSBicmVha3NcbiAqL1xuZnVuY3Rpb24gaHRtbGJyKHN0cikge1xuICAgIHJldHVybiBubDJicihodG1sKHN0cikpO1xufVxuXG4vKipcbiAqIFVSTC1lbmNvZGVzIGEgc3RyaW5nXG4gKiBAcGFyYW0ge3N0cmluZ30gc3RyIC0gU3RyaW5nIHRvIGVuY29kZVxuICogQHJldHVybnMge3N0cmluZ30gVVJMLWVuY29kZWQgc3RyaW5nXG4gKi9cbmZ1bmN0aW9uIHVybGVuY29kZShzdHIpIHtcbiAgICByZXR1cm4gZW5jb2RlVVJJQ29tcG9uZW50KHN0cik7XG59XG5cbi8qKlxuICogVVJMLWRlY29kZXMgYSBzdHJpbmdcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdHIgLSBTdHJpbmcgdG8gZGVjb2RlXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBVUkwtZGVjb2RlZCBzdHJpbmdcbiAqL1xuZnVuY3Rpb24gdXJsZGVjb2RlKHN0cikge1xuICAgIHJldHVybiBkZWNvZGVVUklDb21wb25lbnQoc3RyKTtcbn1cblxuLyoqXG4gKiBKU09OLWVuY29kZXMgYSB2YWx1ZVxuICogQHBhcmFtIHsqfSB2YWx1ZSAtIFZhbHVlIHRvIGVuY29kZVxuICogQHJldHVybnMge3N0cmluZ30gSlNPTiBzdHJpbmdcbiAqL1xuZnVuY3Rpb24ganNvbl9lbmNvZGUodmFsdWUpIHtcbiAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkodmFsdWUpO1xufVxuXG4vKipcbiAqIEpTT04tZGVjb2RlcyBhIHN0cmluZ1xuICogQHBhcmFtIHtzdHJpbmd9IHN0ciAtIEpTT04gc3RyaW5nIHRvIGRlY29kZVxuICogQHJldHVybnMgeyp9IERlY29kZWQgdmFsdWVcbiAqL1xuZnVuY3Rpb24ganNvbl9kZWNvZGUoc3RyKSB7XG4gICAgcmV0dXJuIEpTT04ucGFyc2Uoc3RyKTtcbn1cblxuLyoqXG4gKiBDb25zb2xlIGRlYnVnIG91dHB1dCB3aXRoIGNoYW5uZWwgZmlsdGVyaW5nXG4gKiBBbGlhcyBmb3IgRGVidWdnZXIuY29uc29sZV9kZWJ1Z1xuICogQHBhcmFtIHtzdHJpbmd9IGNoYW5uZWwgLSBEZWJ1ZyBjaGFubmVsIG5hbWVcbiAqIEBwYXJhbSB7Li4uKn0gdmFsdWVzIC0gVmFsdWVzIHRvIGxvZ1xuICovXG5mdW5jdGlvbiBjb25zb2xlX2RlYnVnKGNoYW5uZWwsIC4uLnZhbHVlcykge1xuICAgIERlYnVnZ2VyLmNvbnNvbGVfZGVidWcoY2hhbm5lbCwgLi4udmFsdWVzKTtcbn1cblxuLyoqXG4gKiBSZXBsYWNlcyBhbGwgb2NjdXJyZW5jZXMgb2YgYSBzdWJzdHJpbmcgaW4gYSBzdHJpbmdcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdHJpbmcgLSBTdHJpbmcgdG8gc2VhcmNoIGluXG4gKiBAcGFyYW0ge3N0cmluZ30gc2VhcmNoIC0gU3Vic3RyaW5nIHRvIGZpbmRcbiAqIEBwYXJhbSB7c3RyaW5nfSByZXBsYWNlIC0gUmVwbGFjZW1lbnQgc3Vic3RyaW5nXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBTdHJpbmcgd2l0aCBhbGwgb2NjdXJyZW5jZXMgcmVwbGFjZWRcbiAqL1xuZnVuY3Rpb24gcmVwbGFjZV9hbGwoc3RyaW5nLCBzZWFyY2gsIHJlcGxhY2UpIHtcbiAgICBpZiAoIWlzX3N0cmluZyhzdHJpbmcpKSB7XG4gICAgICAgIHN0cmluZyA9IHN0cmluZyArICcnO1xuICAgIH1cbiAgICByZXR1cm4gc3RyaW5nLnNwbGl0KHNlYXJjaCkuam9pbihyZXBsYWNlKTtcbn1cblxuLyoqXG4gKiBDYXBpdGFsaXplcyB0aGUgZmlyc3QgbGV0dGVyIG9mIGVhY2ggd29yZFxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0IC0gU3RyaW5nIHRvIGNhcGl0YWxpemVcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFN0cmluZyB3aXRoIGZpcnN0IGxldHRlciBvZiBlYWNoIHdvcmQgY2FwaXRhbGl6ZWRcbiAqL1xuZnVuY3Rpb24gdWN3b3JkcyhpbnB1dCkge1xuICAgIHJldHVybiBpbnB1dFxuICAgICAgICAuc3BsaXQoJyAnKVxuICAgICAgICAubWFwKCh3b3JkKSA9PiB3b3JkLmNoYXJBdCgwKS50b1VwcGVyQ2FzZSgpICsgd29yZC5zbGljZSgxKSlcbiAgICAgICAgLmpvaW4oJyAnKTtcbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gT0JKRUNUIEFORCBBUlJBWSBVVElMSVRJRVNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBDb3VudHMgdGhlIG51bWJlciBvZiBwcm9wZXJ0aWVzIGluIGFuIG9iamVjdCBvciBlbGVtZW50cyBpbiBhbiBhcnJheVxuICogQHBhcmFtIHtPYmplY3R8QXJyYXl9IG8gLSBPYmplY3Qgb3IgYXJyYXkgdG8gY291bnRcbiAqIEByZXR1cm5zIHtudW1iZXJ9IE51bWJlciBvZiBvd24gcHJvcGVydGllcy9lbGVtZW50c1xuICovXG5mdW5jdGlvbiBjb3VudChvKSB7XG4gICAgbGV0IGMgPSAwO1xuICAgIGZvciAoY29uc3QgayBpbiBvKSB7XG4gICAgICAgIGlmIChvLmhhc093blByb3BlcnR5KGspKSB7XG4gICAgICAgICAgICArK2M7XG4gICAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGM7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHNoYWxsb3cgY2xvbmUgb2YgYW4gb2JqZWN0LCBhcnJheSwgb3IgZnVuY3Rpb25cbiAqIEBwYXJhbSB7Kn0gb2JqIC0gVmFsdWUgdG8gY2xvbmVcbiAqIEByZXR1cm5zIHsqfSBDbG9uZWQgdmFsdWVcbiAqL1xuZnVuY3Rpb24gY2xvbmUob2JqKSB7XG4gICAgaWYgKHR5cGVvZiBGdW5jdGlvbi5wcm90b3R5cGUuX19jbG9uZSA9PSB1bmRlZikge1xuICAgICAgICBGdW5jdGlvbi5wcm90b3R5cGUuX19jbG9uZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIC8vaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTgzMzU4OC9qYXZhc2NyaXB0LWNsb25lLWEtZnVuY3Rpb25cbiAgICAgICAgICAgIGNvbnN0IHRoYXQgPSB0aGlzO1xuICAgICAgICAgICAgbGV0IHRlbXAgPSBmdW5jdGlvbiBjbG9uZWQoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoYXQuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBmb3IgKGxldCBrZXkgaW4gdGhpcykge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmhhc093blByb3BlcnR5KGtleSkpIHtcbiAgICAgICAgICAgICAgICAgICAgdGVtcFtrZXldID0gdGhpc1trZXldO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0ZW1wO1xuICAgICAgICB9O1xuICAgIH1cblxuICAgIGlmICh0eXBlb2Ygb2JqID09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgcmV0dXJuIG9iai5fX2Nsb25lKCk7XG4gICAgfSBlbHNlIGlmIChvYmouY29uc3RydWN0b3IgJiYgb2JqLmNvbnN0cnVjdG9yID09IEFycmF5KSB7XG4gICAgICAgIHJldHVybiBvYmouc2xpY2UoMCk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgLy8gaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNzI4MzYwL2hvdy1kby1pLWNvcnJlY3RseS1jbG9uZS1hLWphdmFzY3JpcHQtb2JqZWN0LzMwMDQyOTQ4IzMwMDQyOTQ4XG4gICAgICAgIHJldHVybiBPYmplY3QuYXNzaWduKHt9LCBvYmopO1xuICAgIH1cbn1cblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBmaXJzdCBub24tbnVsbC91bmRlZmluZWQgdmFsdWUgZnJvbSBhcmd1bWVudHNcbiAqIEBwYXJhbSB7Li4uKn0gYXJndW1lbnRzIC0gVmFsdWVzIHRvIGNoZWNrXG4gKiBAcmV0dXJucyB7Kn0gRmlyc3Qgbm9uLW51bGwvdW5kZWZpbmVkIHZhbHVlLCBvciBudWxsIGlmIG5vbmUgZm91bmRcbiAqL1xuZnVuY3Rpb24gY29hbGVzY2UoKSB7XG4gICAgbGV0IGFyZ3MgPSBBcnJheS5mcm9tKGFyZ3VtZW50cyk7XG4gICAgbGV0IHJldHVybl92YWwgPSBudWxsO1xuICAgIGFyZ3MuZm9yRWFjaChmdW5jdGlvbiAoYXJnKSB7XG4gICAgICAgIGlmIChyZXR1cm5fdmFsID09PSBudWxsICYmIHR5cGVvZiBhcmcgIT0gdW5kZWYgJiYgYXJnICE9PSBudWxsKSB7XG4gICAgICAgICAgICByZXR1cm5fdmFsID0gYXJnO1xuICAgICAgICB9XG4gICAgfSk7XG4gICAgcmV0dXJuIHJldHVybl92YWw7XG59XG5cbi8qKlxuICogQ29udmVydHMgQ1NWIHN0cmluZyB0byBhcnJheSwgdHJpbW1pbmcgZWFjaCBlbGVtZW50XG4gKiBAcGFyYW0ge3N0cmluZ30gc3RyX2NzdiAtIENTViBzdHJpbmcgdG8gY29udmVydFxuICogQHJldHVybnMge0FycmF5PHN0cmluZz59IEFycmF5IG9mIHRyaW1tZWQgdmFsdWVzXG4gKiBAdG9kbyBIYW5kbGUgcXVvdGVkL2VzY2FwZWQgY2hhcmFjdGVyc1xuICovXG5mdW5jdGlvbiBjc3ZfdG9fYXJyYXlfdHJpbShzdHJfY3N2KSB7XG4gICAgY29uc3QgcGFydHMgPSBzdHJfY3N2LnNwbGl0KCcsJyk7XG4gICAgY29uc3QgcmV0ID0gW107XG4gICAgZm9yZWFjaChwYXJ0cywgKHBhcnQpID0+IHtcbiAgICAgICAgcmV0LnB1c2gocGFydC50cmltKCkpO1xuICAgIH0pO1xuICAgIHJldHVybiByZXQ7XG59XG4iXSwibWFwcGluZ3MiOiI7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLE1BQU1BLEtBQUssR0FBRyxXQUFXOztBQUV6QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTQyxPQUFPQSxDQUFDQyxHQUFHLEVBQUVDLFFBQVEsRUFBRTtFQUM1QixNQUFNQyxPQUFPLEdBQUcsRUFBRTtFQUVsQixJQUFJQyxLQUFLLENBQUNDLE9BQU8sQ0FBQ0osR0FBRyxDQUFDLEVBQUU7SUFDcEJBLEdBQUcsQ0FBQ0ssT0FBTyxDQUFDLENBQUNDLEtBQUssRUFBRUMsS0FBSyxLQUFLO01BQzFCTCxPQUFPLENBQUNNLElBQUksQ0FBQ1AsUUFBUSxDQUFDSyxLQUFLLEVBQUVDLEtBQUssQ0FBQyxDQUFDO0lBQ3hDLENBQUMsQ0FBQztFQUNOLENBQUMsTUFBTSxJQUFJUCxHQUFHLElBQUksT0FBT0EsR0FBRyxLQUFLLFFBQVEsRUFBRTtJQUN2QyxLQUFLLElBQUlTLEdBQUcsSUFBSVQsR0FBRyxFQUFFO01BQ2pCLElBQUlBLEdBQUcsQ0FBQ1UsY0FBYyxDQUFDRCxHQUFHLENBQUMsRUFBRTtRQUN6QlAsT0FBTyxDQUFDTSxJQUFJLENBQUNQ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QUN0QyxDQUFDO01BQ0QsS0FBSyxJQUFJNUQsR0FBRyxJQUFJLElBQUksRUFBRTtRQUNsQixJQUFJLElBQUksQ0FBQ0MsY0FBYyxDQUFDRCxHQUFHLENBQUMsRUFBRTtVQUMxQnFGLElBQUksQ0FBQ3JGLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQ0EsR0FBRyxDQUFDO1FBQ3pCO01BQ0o7TUFDQSxPQUFPcUYsSUFBSTtJQUNmLENBQUM7RUFDTDtFQUVBLElBQUksT0FBTzlGLEdBQUcsSUFBSSxVQUFVLEVBQUU7SUFDMUIsT0FBT0EsR0FBRyxDQUFDNEYsT0FBTyxDQUFDLENBQUM7RUFDeEIsQ0FBQyxNQUFNLElBQUk1RixHQUFHLENBQUNpRyxXQUFXLElBQUlqRyxHQUFHLENBQUNpRyxXQUFXLElBQUk5RixLQUFLLEVBQUU7SUFDcEQsT0FBT0gsR0FBRyxDQUFDb0YsS0FBSyxDQUFDLENBQUMsQ0FBQztFQUN2QixDQUFDLE1BQU07SUFDSDtJQUNBLE9BQU9jLE1BQU0sQ0FBQ0MsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFbkcsR0FBRyxDQUFDO0VBQ2pDO0FBQ0o7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVNvRyxRQUFRQSxDQUFBLEVBQUc7RUFDaEIsSUFBSUMsSUFBSSxHQUFHbEcsS0FBSyxDQUFDbUcsSUFBSSxDQUFDakMsU0FBUyxDQUFDO0VBQ2hDLElBQUlrQyxVQUFVLEdBQUcsSUFBSTtFQUNyQkYsSUFBSSxDQUFDaEcsT0FBTyxDQUFDLFVBQVVtRyxHQUFHLEVBQUU7SUFDeEIsSUFBSUQsVUFBVSxLQUFLLElBQUksSUFBSSxPQUFPQyxHQUFHLElBQUkxRyxLQUFLLElBQUkwRyxHQUFHLEtBQUssSUFBSSxFQUFFO01BQzVERCxVQUFVLEdBQUdDLEdBQUc7SUFDcEI7RUFDSixDQUFDLENBQUM7RUFDRixPQUFPRCxVQUFVO0FBQ3JCOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVNFLGlCQUFpQkEsQ0FBQ0MsT0FBTyxFQUFFO0VBQ2hDLE1BQU1DLEtBQUssR0FBR0QsT0FBTyxDQUFDOUIsS0FBSyxDQUFDLEdBQUcsQ0FBQztFQUNoQyxNQUFNZ0MsR0FBRyxHQUFHLEVBQUU7RUFDZDdHLE9BQU8sQ0FBQzRHLEtBQUssRUFBR0UsSUFBSSxJQUFLO0lBQ3JCRCxHQUFHLENBQUNwRyxJQUFJLENBQUNxRyxJQUFJLENBQUNDLElBQUksQ0FBQyxDQUFDLENBQUM7RUFDekIsQ0FBQyxDQUFDO0VBQ0YsT0FBT0YsR0FBRztBQUNkIiwiaWdub3JlTGlzdCI6W119