UNPKG

@neblartechnologies/descardid

Version:

DesCardId (of Card Identification) is a php library used for identifying credit card numbers in text.

737 lines (651 loc) 77.1 kB
/** * DesCardId * DesCardId (of Card Identification) is a php library used for identifying credit card numbers in text. * @version v1.0.0 * @since December 26, 2017 * @link http://neblar.com * @copyright 2017 Neblar Technologies * @license MIT */ 'use strict'; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * ValidationConstants * This class contains all the constants that are used for various * validations. * * @author Rijul Gupta <rijulg@neblar.com> * @since 24 Dec 2017 * @copyright 2017 Neblar Technologies * @license MIT */ var ValidationConstants = function ValidationConstants() { _classCallCheck(this, ValidationConstants); this.MIN_POSSIBLE_LENGTH = 7; this.MAX_POSSIBLE_LENGTH = 19; /* *These are probabilities based on the most common card lengths */ this.PROBABILITIES_LENGTH = { 16: 100, /*This is the most common card number length*/ 15: 100, /*American Express has cards of this length*/ 13: 80 /*VISA sometimes makes cards of this length*/ }; /* * If the identification fingerprints of a type of card are too few * i.e. if the regex patter is too short, which in turns means that * it might produce more false positives the probability assigned * to that particular regex is low. */ this.PROBABILITIES_REGEX_PROVIDERS = { /*Regex to identify mastercard*/ '^(5[1-5][0-9]{5,}|222[1-9][0-9]{3,}|22[3-9][0-9]{4,}|2[3-6][0-9]{5,}|27[01][0-9]{4,}|2720[0-9]{3,})': 100, /*Regex to identify american express*/ '^(3[47][0-9]{5,})': 70, /*Regex to identify VISA*/ '^(4[0-9]{6,})': 50, /*Regex to identify Diners Club*/ '^(3(?:0[0-5]|[68][0-9])[0-9]{4,})': 85, /*Regex to identify Discover*/ '^(6(?:011|5[0-9]{2})[0-9]{3,})': 80, /*Regex to identify JCB*/ '^((?:2131|1800|35[0-9]{3})[0-9]{3,})': 85 }; /* * These are the test numbers that are openly provided by various providers * so that we can correctly identify if someone checks for any of These * with full certainty */ this.KNOWN_TEST_NUMBERS = ['378282246310005', /*American Express*/ '371449635398431', /*American Express*/ '345436849253786', /*American Express*/ '344343597098739', /*American Express*/ '348195053148184', /*American Express*/ '346761128958196', /*American Express*/ '379983963916986', /*American Express*/ '376749501879009', /*American Express*/ '349204254634213', /*American Express*/ '376432510463566', /*American Express*/ '378734493671000', /*American Express Corporate*/ '5610591081018250', /*Australian BankCard*/ '30569309025904', /*Diners Club*/ '38520000023237', /*Diners Club*/ '30467323783394', /*Diners Club (Carte Blanche)*/ '30389589049437', /*Diners Club (Carte Blanche)*/ '30213469782901', /*Diners Club (Carte Blanche)*/ '36197365718891', /*Diners Club (International)*/ '36823785024749', /*Diners Club (International)*/ '36251701871102', /*Diners Club (International)*/ '5485157059278227', /*Diners Club (North America)*/ '5418199988362484', /*Diners Club (North America)*/ '5402093870675764', /*Diners Club (North America)*/ '6011111111111117', /*Discover*/ '6011000990139424', /*Discover*/ '6011540018341759', /*Discover*/ '6011052057723921', /*Discover*/ '6011277618211484585', /*Discover*/ '6011861286835722', /*Discover*/ '6011890376173660', /*Discover*/ '6011464247892518', /*Discover*/ '6011244758428047', /*Discover*/ '6011469345729306', /*Discover*/ '6382961806046593', /*InstaPayment*/ '6373413397497463', /*InstaPayment*/ '6375275217437369', /*InstaPayment*/ '3530111333300000', /*JCB*/ '3566002020360505', /*JCB*/ '3566111111111113', /*JCB*/ '3529844470994754', /*JCB*/ '3535754231437369', /*JCB*/ '3541031337467299722', /*JCB*/ '6762678941084830', /*Maestro*/ '5018131548158304', /*Maestro*/ '6304521934333993', /*Maestro*/ '50339619890917', /*Maestro (International)*/ '586824160825533338', /*Maestro (International)*/ '6759411100000008', /*Maestro (UK Domestic)*/ '6759560045005727054', /*Maestro (UK Domestic)*/ '5641821111166669', /*Maestro (UK Domestic)*/ '5555555555554444', /*MasterCard*/ '5105105105105100', /*MasterCard*/ '2222420000001113', /*MasterCard*/ '2222630000001125', /*MasterCard*/ '5246772059242294', /*MasterCard*/ '5365643412360922', /*MasterCard*/ '5310506475502852', /*MasterCard*/ '5192310560826646', /*MasterCard*/ '5174224924081487', /*MasterCard*/ '5353732311938484', /*MasterCard*/ '5203246075883952', /*MasterCard*/ '5186682476306626', /*MasterCard*/ '4111111111111111', /*VISA*/ '4012888888881881', /*VISA*/ '4222222222222', /*VISA*/ '4330954187429262', /*VISA*/ '4916861873042626', /*VISA*/ '4024007176658892119', /*VISA*/ '4485992558886887', /*VISA*/ '4556556689853209', /*VISA*/ '4532379342751077', /*VISA*/ '4024007153524987', /*VISA*/ '4485643204102613', /*VISA*/ '4508138079686538', /*VISA (electron)*/ '4026207140510119', /*VISA (electron)*/ '4508608593847550', /*VISA (electron)*/ '5019717010103742', /*PBS*/ '6331101999990016', /*Paymentech*/ '135412345678911']; }; /** * ValidationFunctions * This class contains all the various algorithms we employ in determining * that a given number belongs to a credit card or not. * * Only a valid, formatted number should be passed to these functions. * Formatted here implies that there should be no spaces or special * characters between the provided number. * * The functions do not have an extra layer of number validation because * that particular check should happen before you access any of these. * * @author Rijul Gupta <rijulg@neblar.com> * @since 24 Dec 2017 * @copyright 2017 Neblar Technologies * @license MIT */ var ValidationFunctions = function () { /** * ValidationFunctions constructor. * The constructor allows us to customize the constants that will be used to conduct the validations. * * @param {ValidationConstants} constantsClass the constants used for validation */ function ValidationFunctions() { var constantsClass = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; _classCallCheck(this, ValidationFunctions); this.constants = constantsClass === null ? new ValidationConstants() : constantsClass; } /** * validateKnownTestNumbers * checks if the given number matches any of the disclosed test numbers * provided by the major companies * * @param {string} number the number to be checked * @return {boolean} true if number matches a known test number, false otherwise */ _createClass(ValidationFunctions, [{ key: 'validateKnownTestNumbers', value: function validateKnownTestNumbers(number) { return this.constants.KNOWN_TEST_NUMBERS.indexOf(number) !== -1; } /** * validateLength * returns the likelihood of a number being a credit card number based * just on it's length. So a common length like 16 would return 100, * an uncommon one like 18 would return 60 while others would return 0. * * @param {string} number the number to be checked * @return {int} likelihood associated with length of number */ }, { key: 'validateLength', value: function validateLength(number) { var length = number.length; if (this.constants.PROBABILITIES_LENGTH.hasOwnProperty(length)) { return this.constants.PROBABILITIES_LENGTH[length]; } return 0; } /** * validateLUHN * performs a LUHN check on the number * * @param {string} number the number to be checked * @return {boolean} true if passes the check, false otherwise */ }, { key: 'validateLUHN', value: function validateLUHN(number) { var sum = 0; var length = number.length; var lastDigit = parseInt(number[length - 1]); number = number.substr(0, length - 1); number = this.reverseString(number); for (var i = 0; i < length - 1; i++) { var digit = parseInt(number[i]); if (i % 2 === 0) { digit *= 2; if (digit > 9) { digit -= 9; } } sum += digit; } return (sum + lastDigit) % 10 === 0; } /** * validatePossibility * determines whether it is at least possible for a given number * to be a credit card or not. * * @param {string} number the number to be checked * @return {boolean} true if it is possible, false otherwise */ }, { key: 'validatePossibility', value: function validatePossibility(number) { if ((!isNaN(parseFloat(number)) && isFinite(number)) === false) { return false; } var length = number.length; if (length < this.constants.MIN_POSSIBLE_LENGTH) { return false; } if (length > this.constants.MAX_POSSIBLE_LENGTH) { return false; } return true; } /** * validateProvider * checks if a provider can be identified for the given card number * and returns the certainty of identification mapped from 0 to 100 * * @param {string} number the number to be checked * @return {number} probability of surety of identification of a provider */ }, { key: 'validateProvider', value: function validateProvider(number) { var providers = this.constants.PROBABILITIES_REGEX_PROVIDERS; for (var regex in providers) { if (providers.hasOwnProperty(regex)) { if (new RegExp(regex).test(number)) { return providers[regex]; } } } return 0; } /** * reverseString * reverses a given string * @param {string} string * @return {string} the reversed string */ }, { key: 'reverseString', value: function reverseString(string) { var i = string.length - 1, out = ''; for (; i >= 0;) { out += string[i--]; } return out; } }]); return ValidationFunctions; }(); /** * CardNumberValidator * This class provides with the functions that can be used to validate a * card number. * * You should first ensure that a number can possibly be a credit card number * by calling "isImpossibleToBeACreditCard" function and checking that it is * false, after that you can check if we can surely say that a number is a * credit card number or not by calling "isSurelyACreditCardNumber". * The level of threshold for check can be set and should be decided based * on how sure you want to be. * * @author Rijul Gupta <rijulg@neblar.com> * @since 24 Dec 2017 * @copyright 2017 Neblar Technologies * @license MIT */ var CardNumberValidator = function () { /** * CardNumberValidator constructor. * the constructor allows us to customize the probabilities used for the validation check as well as * define the constants that should be used to conduct the validation * * @param {Object} probabilities the probabilities assigned to various validations in ValidationFunctions * @param {ValidationConstants} constantsClass the constants used for validation */ function CardNumberValidator() { var probabilities = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var constantsClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; _classCallCheck(this, CardNumberValidator); this.DEFAULT_THRESHOLD = 85; if (probabilities === null) { this.probabilities = { 'LUHN': 60, 'TEST_NUMBERS': 100, 'PROVIDERS': 15, 'LENGTH': 15 }; } else { this.probabilities = probabilities; } if (constantsClass === null) { this.validator = new ValidationFunctions(); } else { this.validator = new ValidationFunctions(constantsClass); } } /** * calculateProbabilityOfBeingACreditCard * This function calculates the probability of a number being associated * with a credit card. * The probability associated with each check is declared separately in * a constants class * * @param {string} number the number to be checked * @return {number} total probability of a number being a credit card number */ _createClass(CardNumberValidator, [{ key: 'calculateProbabilityOfBeingACreditCard', value: function calculateProbabilityOfBeingACreditCard(number) { var probability = 0; if (this.validator.validateKnownTestNumbers(number)) { probability += this.probabilities['TEST_NUMBERS']; } if (this.validator.validateLUHN(number)) { probability += this.probabilities['LUHN']; } probability += this.validator.validateProvider(number) * (this.probabilities['PROVIDERS'] / 100); probability += this.validator.validateLength(number) * (this.probabilities['LENGTH'] / 100); return probability; } /** * isPossibleToBeACreditCard * wrapper function to check whether the number can possibly be a credit * card number or not. * * @param {string} number the number to be checked * @return {boolean} true if number can be a credit card, false otherwise */ }, { key: 'isPossibleToBeACreditCard', value: function isPossibleToBeACreditCard(number) { return this.validator.validatePossibility(number); } /** * isSurelyACreditCardNumber * compares the probability of given number being a credit card number * to specified threshold. If the threshold is not specified, it gets * defaulted to the defaults set in the constants class * * @param {string} number the number to be checked * @param {number} threshold the tolerable level of uncertainty * @return {boolean} true if we are sure, false otherwise */ }, { key: 'isSurelyACreditCardNumber', value: function isSurelyACreditCardNumber(number) { var threshold = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; if (threshold === null) { threshold = this.DEFAULT_THRESHOLD; } var probability = this.calculateProbabilityOfBeingACreditCard(number); return probability > threshold; } /** * setProbabilities * This is a helper function that helps set the probabilities for * calculation. This allows the users to customize the calculation for * their needs. * If the setup fails on any step it reverts to the original settings. * * @param {Array} keyValuePairs array of key => value pairs to be set * @return {boolean} false if fails on any step, true otherwise */ }, { key: 'setProbabilities', value: function setProbabilities(keyValuePairs) { var originalProbabilities = this.probabilities; for (var key in keyValuePairs) { if (keyValuePairs.hasOwnProperty(key)) { if (key in this.probabilities) { this.probabilities[key] = keyValuePairs[key]; } else { this.probabilities = originalProbabilities; return false; } } } return true; } }]); return CardNumberValidator; }(); /** * TextManipulator * This class provides with the s that are used to break the text into fragments, * and perform other text manipulations. * * @author Rijul Gupta <rijulg@neblar.com> * @since 24 Dec 2017 * @copyright 2017 Neblar Technologies * @license MIT */ var TextManipulator = function () { /** * TextManipulator constructor. * The constructor allows to set the minimum and maximum card lengths, these are * used to break the text into fragments. * * @param {int} maxCardLength the maximum length of card to detect, this is used to break the text into fragments only * @param {int} minCardLength the minimum length of card to detect, this is used to break the text into fragments only */ function TextManipulator() { var maxCardLength = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var minCardLength = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; _classCallCheck(this, TextManipulator); this.maxCardLength = maxCardLength === null ? 20 : maxCardLength; this.minCardLength = minCardLength === null ? 7 : minCardLength; } /** * extractNumberFromText * extracts a number from the provided text * * @param {string} text the text from which to extract the number * @return {string|null} the extracted number or null */ _createClass(TextManipulator, [{ key: 'extractNumberFromText', value: function extractNumberFromText(text) { var number = text.replace(new RegExp("[^0-9]", "g"), ''); if (number.length === 0) { return null; } return number; } /** * getContinuousNumbers * returns the numbers that appear continuously in given text. For ex: * " bla bla 123456 bla bla" will return ["123456"] * * @param {string} text the text from which to extract the numbers * @return {Array} the array of numbers extracted */ }, { key: 'getContinuousNumbers', value: function getContinuousNumbers(text) { return text.match(new RegExp("([0-9]+)", "g")); } /** * getDiscontinuousNumbers * returns the numbers that appear continuously in given text. For ex: * " bla bla 123 456 bla 321-4 5 bla 9876" will return ["123 456", "321-4 5", "9876"] * * @param {string} text the text from which to extract the numbers * @return {Array} the array of numbers extracted */ }, { key: 'getDiscontinuousNumbers', value: function getDiscontinuousNumbers(text) { return text.match(new RegExp("([0-9]+(((?![a-zA-Z]))([^a-zA-Z]+))[0-9])", "g")); } /** * getSuspectedFragments * breaks the given text into suspected fragments which contain a number. * This number will further be inspected for accessing whether it belongs to * a credit card or not. * * @param {string} text the string from which to extract the fragments * @param {int} checkLevel the level of check used to inspect the text and identify numbers [1-2] * @return {Array} the array of suspected fragments */ }, { key: 'getSuspectedFragments', value: function getSuspectedFragments(text, checkLevel) { var fragments = void 0; switch (checkLevel) { case 2: fragments = this.getDiscontinuousNumbers(text); break; case 1: default: fragments = this.getContinuousNumbers(text); } if (fragments === null) { return []; } return fragments.filter(function (el, i, a) { return i === a.indexOf(el); }); } /** * markFragment * marks the specified fragment in given text with the provided marker * The main text is passed by reference, so the original text gets changed. * * @param {string} text the text in which to do the marking * @param {string} fragment the fragment that needs to be marked * @param {string} marker the marker used to identify the mark */ }, { key: 'markFragment', value: function markFragment(text, fragment, marker) { if (fragment === '') return text; var replacement = "{{" + fragment + "}[" + marker + "]}"; return text.replace(new RegExp(fragment, "g"), replacement); } }]); return TextManipulator; }(); /** * CardIdentifier * This class provides with the functions that can be used to identify card * numbers from a given piece of text. * * Any given text will be returned with a formatted duplicate which will * have any fragments of text that are suspected of being credit card * numbers marked. * * @author Rijul Gupta <rijulg@neblar.com> * @since 24 Dec 2017 * @copyright 2017 Neblar Technologies * @license MIT */ var CardIdentifier = function () { /** * CardIdentifier constructor. * The parameters are provided here to customize the functionality of checks by altering * the level of checks, the probabilities assigned to various validations, the constants * used for validation and the markers used to identify suspected entries * * @param {number} thresholdAlert the threshold used for separating numbers which we are sure about and those we aren't * @param {number} thresholdNotice the threshold used for separating numbers which we are sure about and those we aren't * @param {int} checkLevel the level of check used to inspect the text and identify numbers [1-2] * @param {string} alert the text used to identify the numbers that are identified with certainty * @param {string} notice the text used to identify the numbers that are identified without uncertainty * @param {int} maxCardLength the maximum length of card to detect, this is used to break the text into fragments only * @param {int} minCardLength the minimum length of card to detect, this is used to break the text into fragments only * @param {Object} probabilities the probabilities assigned to various validations in ValidationFunctions * @param {ValidationConstants} constantsClass the constants used for validation */ function CardIdentifier() { var thresholdAlert = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; var thresholdNotice = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var checkLevel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; var alert = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; var notice = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null; var maxCardLength = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null; var minCardLength = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null; var probabilities = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null; var constantsClass = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null; _classCallCheck(this, CardIdentifier); this.alert = alert === null ? 'ALERT' : alert; this.checkLevel = checkLevel === null ? 2 : checkLevel; this.notice = notice === null ? 'NOTICE' : notice; this.textManipulator = new TextManipulator(maxCardLength, minCardLength); this.thresholdAlert = thresholdAlert; this.thresholdNotice = thresholdNotice; this.validator = new CardNumberValidator(probabilities, constantsClass); } /** * inspectText * inspects the provided text and formats the text to reflect identified * card numbers in the text. * This function works without setting any of the properties in the constructor * with defaults in all the subsequent classes. * * @param {string} text the text that needs to be inspected * @return string formatted text */ _createClass(CardIdentifier, [{ key: 'inspectText', value: function inspectText(text) { var fragments = this.textManipulator.getSuspectedFragments(text, this.checkLevel); for (var i = 0; i < fragments.length; i++) { var number = this.textManipulator.extractNumberFromText(fragments[i]); if (number !== null) { if (this.validator.isPossibleToBeACreditCard(number)) { if (this.validator.isSurelyACreditCardNumber(number, this.thresholdAlert)) { text = this.textManipulator.markFragment(text, fragments[i], this.alert); } } } } return text; } /** * inspectTextWithNotices * inspects the provided text and formats the text to reflect identified * card numbers in the text. * A notice is added to numbers that have a probability more than thresholdNotice * of being a credit card. * * To make use of this function properly set the thresholdAlert and thresholdNotice * in the constructor to a desired value. If you don't want any thresholds for the * notices then just leave it blank or pass null. * * @param {string} text the text that needs to be inspected * @return string formatted text */ }, { key: 'inspectTextWithNotices', value: function inspectTextWithNotices(text) { var fragments = this.textManipulator.getSuspectedFragments(text, this.checkLevel); for (var i = 0; i < fragments.length; i++) { var number = this.textManipulator.extractNumberFromText(fragments[i]); if (number !== null) { if (this.validator.isPossibleToBeACreditCard(number)) { var probability = this.validator.calculateProbabilityOfBeingACreditCard(number); if (this.thresholdAlert === null || probability > this.thresholdAlert) { text = this.textManipulator.markFragment(text, fragments[i], this.alert); } else if (this.thresholdNotice === null || probability > this.thresholdNotice) { text = this.textManipulator.markFragment(text, fragments[i], this.notice); } } } } return text; } }]); return CardIdentifier; }(); //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIlZhbGlkYXRpb25Db25zdGFudHMuanMiLCJWYWxpZGF0aW9uRnVuY3Rpb25zLmpzIiwiQ2FyZE51bWJlclZhbGlkYXRvci5qcyIsIlRleHRNYW5pcHVsYXRvci5qcyIsIkNhcmRJZGVudGlmaWVyLmpzIl0sIm5hbWVzIjpbIlZhbGlkYXRpb25Db25zdGFudHMiLCJNSU5fUE9TU0lCTEVfTEVOR1RIIiwiTUFYX1BPU1NJQkxFX0xFTkdUSCIsIlBST0JBQklMSVRJRVNfTEVOR1RIIiwiUFJPQkFCSUxJVElFU19SRUdFWF9QUk9WSURFUlMiLCJLTk9XTl9URVNUX05VTUJFUlMiLCJWYWxpZGF0aW9uRnVuY3Rpb25zIiwiY29uc3RhbnRzQ2xhc3MiLCJjb25zdGFudHMiLCJudW1iZXIiLCJpbmRleE9mIiwibGVuZ3RoIiwiaGFzT3duUHJvcGVydHkiLCJzdW0iLCJsYXN0RGlnaXQiLCJwYXJzZUludCIsInN1YnN0ciIsInJldmVyc2VTdHJpbmciLCJpIiwiZGlnaXQiLCJpc05hTiIsInBhcnNlRmxvYXQiLCJpc0Zpbml0ZSIsInByb3ZpZGVycyIsInJlZ2V4IiwiUmVnRXhwIiwidGVzdCIsInN0cmluZyIsIm91dCIsIkNhcmROdW1iZXJWYWxpZGF0b3IiLCJwcm9iYWJpbGl0aWVzIiwiREVGQVVMVF9USFJFU0hPTEQiLCJ2YWxpZGF0b3IiLCJwcm9iYWJpbGl0eSIsInZhbGlkYXRlS25vd25UZXN0TnVtYmVycyIsInZhbGlkYXRlTFVITiIsInZhbGlkYXRlUHJvdmlkZXIiLCJ2YWxpZGF0ZUxlbmd0aCIsInZhbGlkYXRlUG9zc2liaWxpdHkiLCJ0aHJlc2hvbGQiLCJjYWxjdWxhdGVQcm9iYWJpbGl0eU9mQmVpbmdBQ3JlZGl0Q2FyZCIsImtleVZhbHVlUGFpcnMiLCJvcmlnaW5hbFByb2JhYmlsaXRpZXMiLCJrZXkiLCJUZXh0TWFuaXB1bGF0b3IiLCJtYXhDYXJkTGVuZ3RoIiwibWluQ2FyZExlbmd0aCIsInRleHQiLCJyZXBsYWNlIiwibWF0Y2giLCJjaGVja0xldmVsIiwiZnJhZ21lbnRzIiwiZ2V0RGlzY29udGludW91c051bWJlcnMiLCJnZXRDb250aW51b3VzTnVtYmVycyIsImZpbHRlciIsImVsIiwiYSIsImZyYWdtZW50IiwibWFya2VyIiwicmVwbGFjZW1lbnQiLCJDYXJkSWRlbnRpZmllciIsInRocmVzaG9sZEFsZXJ0IiwidGhyZXNob2xkTm90aWNlIiwiYWxlcnQiLCJub3RpY2UiLCJ0ZXh0TWFuaXB1bGF0b3IiLCJnZXRTdXNwZWN0ZWRGcmFnbWVudHMiLCJleHRyYWN0TnVtYmVyRnJvbVRleHQiLCJpc1Bvc3NpYmxlVG9CZUFDcmVkaXRDYXJkIiwiaXNTdXJlbHlBQ3JlZGl0Q2FyZE51bWJlciIsIm1hcmtGcmFnbWVudCJdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUE7Ozs7Ozs7Ozs7O0lBV0FBLG1CLEdBRUEsK0JBQUE7QUFBQTs7QUFDQSxTQUFBQyxtQkFBQSxHQUFBLENBQUE7QUFDQSxTQUFBQyxtQkFBQSxHQUFBLEVBQUE7O0FBRUE7OztBQUdBLFNBQUFDLG9CQUFBLEdBQUE7QUFDQSxZQUFBLEdBREEsRUFDQTtBQUNBLFlBQUEsR0FGQSxFQUVBO0FBQ0EsWUFBQSxFQUhBLENBR0E7QUFIQSxLQUFBOztBQU1BOzs7Ozs7QUFNQSxTQUFBQyw2QkFBQSxHQUFBO0FBQ0E7QUFDQSwrR0FBQSxHQUZBO0FBR0E7QUFDQSw2QkFBQSxFQUpBO0FBS0E7QUFDQSx5QkFBQSxFQU5BO0FBT0E7QUFDQSw2Q0FBQSxFQVJBO0FBU0E7QUFDQSwwQ0FBQSxFQVZBO0FBV0E7QUFDQSxnREFBQTtBQVpBLEtBQUE7O0FBZUE7Ozs7O0FBS0EsU0FBQUMsa0JBQUEsR0FBQSxDQUNBLGlCQURBLEVBQ0E7QUFDQSxxQkFGQSxFQUVBO0FBQ0EscUJBSEEsRUFHQTtBQUNBLHFCQUpBLEVBSUE7QUFDQSxxQkFMQSxFQUtBO0FBQ0EscUJBTkEsRUFNQTtBQUNBLHFCQVBBLEVBT0E7QUFDQSxxQkFSQSxFQVFBO0FBQ0EscUJBVEEsRUFTQTtBQUNBLHFCQVZBLEVBVUE7QUFDQSxxQkFYQSxFQVdBO0FBQ0Esc0JBWkEsRUFZQTtBQUNBLG9CQWJBLEVBYUE7QUFDQSxvQkFkQSxFQWNBO0FBQ0Esb0JBZkEsRUFlQTtBQUNBLG9CQWhCQSxFQWdCQTtBQUNBLG9CQWpCQSxFQWlCQTtBQUNBLG9CQWxCQSxFQWtCQTtBQUNBLG9CQW5CQSxFQW1CQTtBQUNBLG9CQXBCQSxFQW9CQTtBQUNBLHNCQXJCQSxFQXFCQTtBQUNBLHNCQXRCQSxFQXNCQTtBQUNBLHNCQXZCQSxFQXVCQTtBQUNBLHNCQXhCQSxFQXdCQTtBQUNBLHNCQXpCQSxFQXlCQTtBQUNBLHNCQTFCQSxFQTBCQTtBQUNBLHNCQTNCQSxFQTJCQTtBQUNBLHlCQTVCQSxFQTRCQTtBQUNBLHNCQTdCQSxFQTZCQTtBQUNBLHNCQTlCQSxFQThCQTtBQUNBLHNCQS9CQSxFQStCQTtBQUNBLHNCQWhDQSxFQWdDQTtBQUNBLHNCQWpDQSxFQWlDQTtBQUNBLHNCQWxDQSxFQWtDQTtBQUNBLHNCQW5DQSxFQW1DQTtBQUNBLHNCQXBDQSxFQW9DQTtBQUNBLHNCQXJDQSxFQXFDQTtBQUNBLHNCQXRDQSxFQXNDQTtBQUNBLHNCQXZDQSxFQXVDQTtBQUNBLHNCQXhDQSxFQXdDQTtBQUNBLHNCQXpDQSxFQXlDQTtBQUNBLHlCQTFDQSxFQTBDQTtBQUNBLHNCQTNDQSxFQTJDQTtBQUNBLHNCQTVDQSxFQTRDQTtBQUNBLHNCQTdDQSxFQTZDQTtBQUNBLG9CQTlDQSxFQThDQTtBQUNBLHdCQS9DQSxFQStDQTtBQUNBLHNCQWhEQSxFQWdEQTtBQUNBLHlCQWpEQSxFQWlEQTtBQUNBLHNCQWxEQSxFQWtEQTtBQUNBLHNCQW5EQSxFQW1EQTtBQUNBLHNCQXBEQSxFQW9EQTtBQUNBLHNCQXJEQSxFQXFEQTtBQUNBLHNCQXREQSxFQXNEQTtBQUNBLHNCQXZEQSxFQXVEQTtBQUNBLHNCQXhEQSxFQXdEQTtBQUNBLHNCQXpEQSxFQXlEQTtBQUNBLHNCQTFEQSxFQTBEQTtBQUNBLHNCQTNEQSxFQTJEQTtBQUNBLHNCQTVEQSxFQTREQTtBQUNBLHNCQTdEQSxF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QSxDQUFBRSxxQkFBQSxDQUFBaEIsVUFBQWpDLENBQUEsQ0FBQSxDQUFBO0FBQ0Esb0JBQUFULFdBQUEsSUFBQSxFQUFBO0FBQ0Esd0JBQUEsS0FBQXVCLFNBQUEsQ0FBQW9DLHlCQUFBLENBQUEzRCxNQUFBLENBQUEsRUFBQTtBQUNBLDRCQUFBd0IsY0FBQSxLQUFBRCxTQUFBLENBQUFRLHNDQUFBLENBQUEvQixNQUFBLENBQUE7QUFDQSw0QkFBQSxLQUFBb0QsY0FBQSxLQUFBLElBQUEsSUFBQTVCLGNBQUEsS0FBQTRCLGNBQUEsRUFBQTtBQUNBZCxtQ0FBQSxLQUFBa0IsZUFBQSxDQUFBSyxZQUFBLENBQUF2QixJQUFBLEVBQUFJLFVBQUFqQyxDQUFBLENBQUEsRUFBQSxLQUFBNkMsS0FBQSxDQUFBO0FBQ0EseUJBRkEsTUFHQSxJQUFBLEtBQUFELGVBQUEsS0FBQSxJQUFBLElBQUE3QixjQUFBLEtBQUE2QixlQUFBLEVBQUE7QUFDQWYsbUNBQUEsS0FBQWtCLGVBQUEsQ0FBQUssWUFBQSxDQUFBdkIsSUFBQSxFQUFBSSxVQUFBakMsQ0FBQSxDQUFBLEVBQUEsS0FBQThDLE1BQUEsQ0FBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUJBQUFqQixJQUFBO0FBQ0EiLCJmaWxlIjoiRGVzQ2FyZElkLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIFZhbGlkYXRpb25Db25zdGFudHNcclxuICogVGhpcyBjbGFzcyBjb250YWlucyBhbGwgdGhlIGNvbnN0YW50cyB0aGF0IGFyZSB1c2VkIGZvciB2YXJpb3VzXHJcbiAqIHZhbGlkYXRpb25zLlxyXG4gKlxyXG4gKiBAYXV0aG9yICAgICAgUmlqdWwgR3VwdGEgPHJpanVsZ0BuZWJsYXIuY29tPlxyXG4gKiBAc2luY2UgICAgICAgMjQgRGVjIDIwMTdcclxuICogQGNvcHlyaWdodCAgIDIwMTcgTmVibGFyIFRlY2hub2xvZ2llc1xyXG4gKiBAbGljZW5zZSAgICAgTUlUXHJcbiovXHJcblxyXG5jbGFzcyBWYWxpZGF0aW9uQ29uc3RhbnRze1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKCl7XHJcbiAgICAgICAgdGhpcy5NSU5fUE9TU0lCTEVfTEVOR1RIID0gNztcclxuICAgICAgICB0aGlzLk1BWF9QT1NTSUJMRV9MRU5HVEggPSAxOTtcclxuXHJcbiAgICAgICAgLypcclxuICAgICAgICAgKlRoZXNlIGFyZSBwcm9iYWJpbGl0aWVzIGJhc2VkIG9uIHRoZSBtb3N0IGNvbW1vbiBjYXJkIGxlbmd0aHNcclxuICAgICAgICAqL1xyXG4gICAgICAgIHRoaXMuUFJPQkFCSUxJVElFU19MRU5HVEggPSB7XHJcbiAgICAgICAgICAgIDE2IDogMTAwLCAvKlRoaXMgaXMgdGhlIG1vc3QgY29tbW9uIGNhcmQgbnVtYmVyIGxlbmd0aCovXHJcbiAgICAgICAgICAgIDE1IDogMTAwLCAvKkFtZXJpY2FuIEV4cHJlc3MgaGFzIGNhcmRzIG9mIHRoaXMgbGVuZ3RoKi9cclxuICAgICAgICAgICAgMTMgOiA4MCwgLypWSVNBIHNvbWV0aW1lcyBtYWtlcyBjYXJkcyBvZiB0aGlzIGxlbmd0aCovXHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgLypcclxuICAgICAgICAgKiBJZiB0aGUgaWRlbnRpZmljYXRpb24gZmluZ2VycHJpbnRzIG9mIGEgdHlwZSBvZiBjYXJkIGFyZSB0b28gZmV3XHJcbiAgICAgICAgICogaS5lLiBpZiB0aGUgcmVnZXggcGF0dGVyIGlzIHRvbyBzaG9ydCwgd2hpY2ggaW4gdHVybnMgbWVhbnMgdGhhdFxyXG4gICAgICAgICAqIGl0IG1pZ2h0IHByb2R1Y2UgbW9yZSBmYWxzZSBwb3NpdGl2ZXMgdGhlIHByb2JhYmlsaXR5IGFzc2lnbmVkXHJcbiAgICAgICAgICogdG8gdGhhdCBwYXJ0aWN1bGFyIHJlZ2V4IGlzIGxvdy5cclxuICAgICAgICAqL1xyXG4gICAgICAgIHRoaXMuUFJPQkFCSUxJVElFU19SRUdFWF9QUk9WSURFUlMgPSB7XHJcbiAgICAgICAgICAgIC8qUmVnZXggdG8gaWRlbnRpZnkgbWFzdGVyY2FyZCovXHJcbiAgICAgICAgICAgICdeKDVbMS01XVswLTldezUsfXwyMjJbMS05XVswLTldezMsfXwyMlszLTldWzAtOV17NCx9fDJbMy02XVswLTldezUsfXwyN1swMV1bMC05XXs0LH18MjcyMFswLTldezMsfSknOiAxMDAsXHJcbiAgICAgICAgICAgIC8qUmVnZXggdG8gaWRlbnRpZnkgYW1lcmljYW4gZXhwcmVzcyovXHJcbiAgICAgICAgICAgICdeKDNbNDddWzAtOV17NSx9KSc6IDcwLFxyXG4gICAgICAgICAgICAvKlJlZ2V4IHRvIGlkZW50aWZ5IFZJU0EqL1xyXG4gICAgICAgICAgICAnXig0WzAtOV17Nix9KSc6IDUwLFxyXG4gICAgICAgICAgICAvKlJlZ2V4IHRvIGlkZW50aWZ5IERpbmVycyBDbHViKi9cclxuICAgICAgICAgICAgJ14oMyg/OjBbMC01XXxbNjhdWzAtOV0pWzAtOV17NCx9KSc6IDg1LFxyXG4gICAgICAgICAgICAvKlJlZ2V4IHRvIGlkZW50aWZ5IERpc2NvdmVyKi9cclxuICAgICAgICAgICAgJ14oNig/OjAxMXw1WzAtOV17Mn0pWzAtOV17Myx9KSc6IDgwLFxyXG4gICAgICAgICAgICAvKlJlZ2V4IHRvIGlkZW50aWZ5IEpDQiovXHJcbiAgICAgICAgICAgICdeKCg/OjIxMzF8MTgwMHwzNVswLTldezN9KVswLTldezMsfSknOiA4NSxcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICAvKlxyXG4gICAgICAgICAqIFRoZXNlIGFyZSB0aGUgdGVzdCBudW1iZXJzIHRoYXQgYXJlIG9wZW5seSBwcm92aWRlZCBieSB2YXJpb3VzIHByb3ZpZGVyc1xyXG4gICAgICAgICAqIHNvIHRoYXQgd2UgY2FuIGNvcnJlY3RseSBpZGVudGlmeSBpZiBzb21lb25lIGNoZWNrcyBmb3IgYW55IG9mIFRoZXNlXHJcbiAgICAgICAgICogd2l0aCBmdWxsIGNlcnRhaW50eVxyXG4gICAgICAgICovXHJcbiAgICAgICAgdGhpcy5LTk9XTl9URVNUX05VTUJFUlMgPSBbXHJcbiAgICAgICAgICAgICczNzgyODIyNDYzMTAwMDUnLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyovXHJcbiAgICAgICAgICAgICczNzE0NDk2MzUzOTg0MzEnLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyovXHJcbiAgICAgICAgICAgICczNDU0MzY4NDkyNTM3ODYnLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyovXHJcbiAgICAgICAgICAgICczNDQzNDM1OTcwOTg3MzknLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyovXHJcbiAgICAgICAgICAgICczNDgxOTUwNTMxNDgxODQnLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyovXHJcbiAgICAgICAgICAgICczNDY3NjExMjg5NTgxOTYnLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyovXHJcbiAgICAgICAgICAgICczNzk5ODM5NjM5MTY5ODYnLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyovXHJcbiAgICAgICAgICAgICczNzY3NDk1MDE4NzkwMDknLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyovXHJcbiAgICAgICAgICAgICczNDkyMDQyNTQ2MzQyMTMnLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyovXHJcbiAgICAgICAgICAgICczNzY0MzI1MTA0NjM1NjYnLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyovXHJcbiAgICAgICAgICAgICczNzg3MzQ0OTM2NzEwMDAnLCAgICAgIC8qQW1lcmljYW4gRXhwcmVzcyBDb3Jwb3JhdGUqL1xyXG4gICAgICAgICAgICAnNTYxMDU5MTA4MTAxODI1MCcsICAgICAvKkF1c3RyYWxpYW4gQmFua0NhcmQqL1xyXG4gICAgICAgICAgICAnMzA1NjkzMDkwMjU5MDQnLCAgICAgICAvKkRpbmVycyBDbHViKi9cclxuICAgICAgICAgICAgJzM4NTIwMDAwMDIzMjM3JywgICAgICAgLypEaW5lcnMgQ2x1YiovXHJcbiAgICAgICAgICAgICczMDQ2NzMyMzc4MzM5NCcsICAgICAgIC8qRGluZXJzIENsdWIgKENhcnRlIEJsYW5jaGUpKi9cclxuICAgICAgICAgICAgJzMwMzg5NTg5MDQ5NDM3JywgICAgICAgLypEaW5lcnMgQ2x1YiAoQ2FydGUgQmxhbmNoZSkqL1xyXG4gICAgICAgICAgICAnMzAyMTM0Njk3ODI5MDEnLCAgICAgICAvKkRpbmVycyBDbHViIChDYXJ0ZSBCbGFuY2hlKSovXHJcbiAgICAgICAgICAgICczNjE5NzM2NTcxODg5MScsICAgICAgIC8qRGluZXJzIENsdWIgKEludGVybmF0aW9uYWwpKi9cclxuICAgICAgICAgICAgJzM2ODIzNzg1MDI0NzQ5JywgICAgICAgLypEaW5lcnMgQ2x1YiAoSW50ZXJuYXRpb25hbCkqL1xyXG4gICAgICAgICAgICAnMzYyNTE3MDE4NzExMDInLCAgICAgICAvKkRpbmVycyBDbHViIChJbnRlcm5hdGlvbmFsKSovXHJcbiAgICAgICAgICAgICc1NDg1MTU3MDU5Mjc4MjI3JywgICAgIC8qRGluZXJzIENsdWIgKE5vcnRoIEFtZXJpY2EpKi9cclxuICAgICAgICAgICAgJzU0MTgxOTk5ODgzNjI0ODQnLCAgICAgLypEaW5lcnMgQ2x1YiAoTm9ydGggQW1lcmljYSkqL1xyXG4gICAgICAgICAgICAnNTQwMjA5Mzg3MDY3NTc2NCcsICAgICAvKkRpbmVycyBDbHViIChOb3J0aCBBbWVyaWNhKSovXHJcbiAgICAgICAgICAgICc2MDExMTExMTExMTExMTE3JywgICAgIC8qRGlzY292ZXIqL1xyXG4gICAgICAgICAgICAnNjAxMTAwMDk5MDEzOTQyNCcsICAgICAvKkRpc2NvdmVyKi9cclxuICAgICAgICAgICAgJzYwMTE1NDAwMTgzNDE3NTknLCAgICAgLypEaXNjb3ZlciovXHJcbiAgICAgICAgICAgICc2MDExMDUyMDU3NzIzOTIxJywgICAgIC8qRGlzY292ZXIqL1xyXG4gICAgICAgICAgICAnNjAxMTI3NzYxODIxMTQ4NDU4NScsICAvKkRpc2NvdmVyKi9cclxuICAgICAgICAgICAgJzYwMTE4NjEyODY4MzU3MjInLCAgICAgLypEaXNjb3ZlciovXHJcbiAgICAgICAgICAgICc2MDExODkwMzc2MTczNjYwJywgICAgIC8qRGlzY292ZXIqL1xyXG4gICAgICAgICAgICAnNjAxMTQ2NDI0Nzg5MjUxOCcsICAgICAvKkRpc2NvdmVyKi9cclxuICAgICAgICAgICAgJzYwMTEyNDQ3NTg0MjgwNDcnLCAgICAgLypEaXNjb3ZlciovXHJcbiAgICAgICAgICAgICc2MDExNDY5MzQ1NzI5MzA2JywgICAgIC8qRGlzY292ZXIqL1xyXG4gICAgICAgICAgICAnNjM4Mjk2MTgwNjA0NjU5MycsICAgICAvKkluc3RhUGF5bWVudCovXHJcbiAgICAgICAgICAgICc2MzczNDEzMzk3NDk3NDYzJywgICAgIC8qSW5zdGFQYXltZW50Ki9cclxuICAgICAgICAgICAgJzYzNzUyNzUyMTc0MzczNjknLCAgICAgLypJbnN0YVBheW1lbnQqL1xyXG4gICAgICAgICAgICAnMzUzMDExMTMzMzMwMDAwMCcsICAgICAvKkpDQiovXHJcbiAgICAgICAgICAgICczNTY2MDAyMDIwMzYwNTA1JywgICAgIC8qSkNCKi9cclxuICAgICAgICAgICAgJzM1NjYxMTExMTExMTExMTMnLCAgICAgLypKQ0IqL1xyXG4gICAgICAgICAgICAnMzUyOTg0NDQ3MDk5NDc1NCcsICAgICAvKkpDQiovXHJcbiAgICAgICAgICAgICczNTM1NzU0MjMxNDM3MzY5JywgICAgIC8qSkNCKi9cclxuICAgICAgICAgICAgJzM1NDEwMzEzMzc0NjcyOTk3MjInLCAgLypKQ0IqL1xyXG4gICAgICAgICAgICAnNjc2MjY3ODk0MTA4NDgzMCcsICAgICAvKk1hZXN0cm8qL1xyXG4gICAgICAgICAgICAnNTAxODEzMTU0ODE1ODMwNCcsICAgICAvKk1hZXN0cm8qL1xyXG4gICAgICAgICAgICAnNjMwNDUyMTkzNDMzMzk5MycsICAgICAvKk1hZXN0cm8qL1xyXG4gICAgICAgICAgICAnNTAzMzk2MTk4OTA5MTcnLCAgICAgICAvKk1hZXN0cm8gKEludGVybmF0aW9uYWwpKi9cclxuICAgICAgICAgICAgJzU4NjgyNDE2MDgyNTUzMzMzOCcsICAgLypNYWVzdHJvIChJbnRlcm5hdGlvbmFsKSovXHJcbiAgICAgICAgICAgICc2NzU5NDExMTAwMDAwMDA4JywgICAgIC8qTWFlc3RybyAoVUsgRG9tZXN0aWMpKi9cclxuICAgICAgICAgICAgJzY3NTk1NjAwNDUwMDU3MjcwNTQnLCAgLypNYWVzdHJvIChVSyBEb21lc3RpYykqL1xyXG4gICAgICAgICAgICAnNTY0MTgyMTExMTE2NjY2OScsICAgICAvKk1hZXN0cm8gKFVLIERvbWVzdGljKSovXHJcbiAgICAgICAgICAgICc1NTU1NTU1NTU1NTU0NDQ0JywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICc1MTA1MTA1MTA1MTA1MTAwJywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICcyMjIyNDIwMDAwMDAxMTEzJywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICcyMjIyNjMwMDAwMDAxMTI1JywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICc1MjQ2NzcyMDU5MjQyMjk0JywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICc1MzY1NjQzNDEyMzYwOTIyJywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICc1MzEwNTA2NDc1NTAyODUyJywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICc1MTkyMzEwNTYwODI2NjQ2JywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICc1MTc0MjI0OTI0MDgxNDg3JywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICc1MzUzNzMyMzExOTM4NDg0JywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICc1MjAzMjQ2MDc1ODgzOTUyJywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICc1MTg2NjgyNDc2MzA2NjI2JywgICAgIC8qTWFzdGVyQ2FyZCovXHJcbiAgICAgICAgICAgICc0MTExMTExMTExMTExMTExJywgICAgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0MDEyODg4ODg4ODgxODgxJywgICAgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0MjIyMjIyMjIyMjIyJywgICAgICAgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0MzMwOTU0MTg3NDI5MjYyJywgICAgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0OTE2ODYxODczMDQyNjI2JywgICAgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0MDI0MDA3MTc2NjU4ODkyMTE5JywgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0NDg1OTkyNTU4ODg2ODg3JywgICAgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0NTU2NTU2Njg5ODUzMjA5JywgICAgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0NTMyMzc5MzQyNzUxMDc3JywgICAgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0MDI0MDA3MTUzNTI0OTg3JywgICAgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0NDg1NjQzMjA0MTAyNjEzJywgICAgIC8qVklTQSovXHJcbiAgICAgICAgICAgICc0NTA4MTM4MDc5Njg2NTM4JywgICAgIC8qVklTQSAoZ