@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
JavaScript
/**
* 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