1
0
Fork 0
mirror of https://github.com/Oreolek/undum.git synced 2024-05-17 00:18:17 +03:00
undum/games/media/js/undum.js
2015-07-23 17:18:36 +07:00

2123 lines
184 KiB
JavaScript

(function () {// -----------------------------------------------------------------------
// Internal Infrastructure Implementations [NB: These have to be
// at the top, because we use them below, but you can safely
// ignore them and skip down to the next section.]
// -----------------------------------------------------------------------
/* Crockford's inherit function */
Function.prototype.inherits = function(Parent) {
var d = {}, p = (this.prototype = new Parent());
this.prototype.uber = function(name) {
if (!(name in d)) d[name] = 0;
var f, r, t = d[name], v = Parent.prototype;
if (t) {
while (t) {
v = v.constructor.prototype;
t -= 1;
}
f = v[name];
} else {
f = p[name];
if (f == this[name]) {
f = v[name];
}
}
d[name] += 1;
r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
d[name] -= 1;
return r;
};
return this;
};
// Feature detection
var hasLocalStorage = function() {
var hasStorage = false;
try {
hasStorage = ('localStorage' in window) &&
window.localStorage !== null &&
window.localStorage !== undefined;
}
catch (err) {
// Firefox with the "Always Ask" cookie accept setting
// will throw an error when attempting to access localStorage
hasStorage = false;
}
return hasStorage;
};
/// Animations - you can totally redefine these! Fade in and fade out by default.
/// @param id string or object
var showBlock = function(id) {
var block = id;
if (typeof id === "string") {
var block = document.getElementById(id);
}
block.classList.add('show');
block.classList.remove('hide');
block.style.display = 'block';
}
var hideBlock = function(id) {
var block = id; // typeof block === "element"
if (typeof id === "string") {
var block = document.getElementById(id);
}
if (typeof id === "object") { // probably NodeList
if (id.length == 0)
return;
Array.prototype.forEach.call(id, function(element, index) {
element.classList.add('hide');
element.classList.remove('show');
});
return;
}
if (typeof block.classList === "undefined")
{
console.log("Tried to hide an undefined block.");
console.log(id);
return;
}
block.classList.add('hide');
block.classList.remove('show');
}
// Assertion
var AssertionError = function(message) {
this.message = message;
this.name = AssertionError;
};
AssertionError.inherits(Error);
var assert = function(expression, message) {
if (!expression) {
throw new AssertionError(message);
}
};
// Object extention
var extend = function(out) {
out = out || {};
for (var i = 1; i < arguments.length; i++) {
var obj = arguments[i];
if (!obj)
continue;
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object')
extend(out[key], obj[key]);
else
out[key] = obj[key];
}
}
}
return out;
};
// -----------------------------------------------------------------------
// Types for Author Use
// -----------------------------------------------------------------------
/* The game is split into situations, which respond to user
* choices. Situation is the base type. It has three methods:
* enter, act and exit, which you implement to perform any
* processing and output any content. The default implementations
* do nothing.
*
* You can either create your own type of Situation, and add
* enter, act and/or exit functions to the prototype (see
* SimpleSituation in this file for an example of that), or you
* can give those functions in the opts parameter. The opts
* parameter is an object. So you could write:
*
* var situation = Situation({
* enter: function(character, system, from) {
* ... your implementation ...
* }
* });
*
* If you pass in enter, act and/or exit through these options,
* then they should have the same function signature as the full
* function definitions, below.
*
* Note that SimpleSituation, a derived type of Situation, calls
* passed in enter, act and exit functions AS WELL AS their normal
* action. This is most often what you want: the normal behavior
* plus a little extra custom behavior. If you want to override
* the behavior of a SimpleSituation, you'll have to create a
* derived type and set the enter, act and/or exit function on
* their prototypes. In most cases, however, if you want to do
* something completely different, it is better to derive your
* type from this type: Situation, rather than one of its
* children.
*
* In addition to enter, exit and act, the following options
* related to implicit situation selection are available:
*
* optionText: a string or a function(character, system,
* situation) which should return the label to put in an
* option block where a link to this situation can be
* chosen. The situation passed in is the situation where the
* option block is being displayed.
*
* canView: a function(character, system, situation) which should
* return true if this situation should be visible in an
* option block in the given situation.
*
* canChoose: a function(character, system, situation) which should
* return true if this situation should appear clickable in an
* option block. Returning false allows you to present the
* option but prevent it being selected. You may want to
* indicate to the player that they need to collect some
* important object before the option is available, for
* example.
*
* tags: a list of tags for this situation, which can be used for
* implicit situation selection. The tags can also be given as
* space, tab or comma separated tags in a string. Note that,
* when calling `getSituationIdChoices`, tags are prefixed with
* a hash, but that should not be the case here. Just use the
* plain tag name.
*
* priority: a numeric priority value (default = 1). When
* selecting situations implicitly, higher priority situations
* are considered first.
*
* frequency: a numeric relative frequency (default = 1), so 100
* would be 100 times more frequent. When there are more
* options that can be displayed, situations will be selected
* randomly based on their frequency.
*
* displayOrder: a numeric ordering value (default = 1). When
* situations are selected implicitly, the results are ordered
* by increasing displayOrder.
*/
var Situation = function(opts) {
if (opts) {
if (opts.enter) this._enter = opts.enter;
if (opts.act) this._act = opts.act;
if (opts.exit) this._exit = opts.exit;
// Options related to this situation being automatically
// selected and displayed in a list of options.
this._optionText = opts.optionText;
this._canView = opts.canView || true;
this._canChoose = opts.canChoose || true;
this._priority = (opts.priority !== undefined) ? opts.priority : 1;
this._frequency =
(opts.frequency !== undefined) ? opts.frequency : 1;
this._displayOrder =
(opts.displayOrder !== undefined) ? opts.displayOrder : 1;
// Tag are not stored with an underscore, because they are
// accessed directy. They should not be context sensitive
// (use the canView function to do context sensitive
// manipulation).
if (opts.tags !== undefined) {
if (Array.isArray(opts.tags)) {
this.tags = opts.tags;
} else {
this.tags = opts.tags.split(/[ \t,]+/);
}
} else {
this.tags = [];
}
} else {
this._canView = true;
this._canChoose = true;
this._priority = 1;
this._frequency = 1;
this._displayOrder = 1;
this.tags = [];
}
};
/* A function that takes action when we enter a situation. The
* last parameter indicates the situation we have just left: it
* may be null if this is the starting situation. Unlike the
* exit() method, this method cannot prevent the transition
* happening: its return value is ignored. */
Situation.prototype.enter = function(character, system, from) {
if (this._enter) this._enter(character, system, from);
};
/* A function that takes action when we carry out some action in a
* situation that isn't intended to lead to a new situation. */
Situation.prototype.act = function(character, system, action) {
if (this._act) this._act(character, system, action);
};
/* A function that takes action when we exit a situation. The last
* parameter indicates the situation we are going to. */
Situation.prototype.exit = function(character, system, to) {
if (this._exit) this._exit(character, system, to);
};
/* Determines whether this situation should be contained within a
* list of options generated automatically by the given
* situation. */
Situation.prototype.canView = function(character, system, situation) {
if (typeof(this._canView) === "function" ) {
return this._canView(character, system, situation);
} else {
return this._canView;
}
};
/* Determines whether this situation should be clickable within a
* list of options generated automatically by the given situation. */
Situation.prototype.canChoose = function(character, system, situation) {
if (typeof(this._canChoose) === "function") {
return this._canChoose(character, system, situation);
} else {
return this._canChoose;
}
};
/* Returns the text that should be used to display this situation
* in an automatically generated list of choices. */
Situation.prototype.optionText = function(character, system, situation) {
if (typeof(this._optionText) === "function") {
return this._optionText(character, system, situation);
} else {
return this._optionText;
}
};
/* Returns the priority, frequency and displayOrder for this situation,
* when being selected using `system.getSituationIdChoices`. */
Situation.prototype.choiceData = function(character, system, situation) {
return {
priority: this._priority,
frequency: this._frequency,
displayOrder: this._displayOrder
};
};
/* A simple situation has a block of content that it displays when
* the situation is entered. The content must be valid "Display
* Content" (see `System.prototype.write` for a definition). This
* constructor has options that control its behavior:
*
* heading: The optional `heading` will be used as a section title
* before the content is displayed. The heading can be any
* HTML string, it doesn't need to be "Display Content". If
* the heading is not given, no heading will be displayed. If
* a heading is given, and no optionText is specified (see
* `Situation` for more information on `optionText`), then the
* heading will also be used for the situation's option text.
*
* actions: This should be an object mapping action Ids to a
* response. The response should either be "Display Content"
* to display if this action is carried out, or it should be a
* function(character, system, action) that will process the
* action.
*
* choices: A list of situation ids and tags that, if given, will
* be used to compile an implicit option block using
* `getSituationIdChoices` (see that function for more details
* of how this works). Tags in this list should be prefixed
* with a hash # symbol, to distinguish them from situation
* ids. If just a single tag or id is needed, it can be passed
* in as a string without wrapping into a list.
*
* minChoices: If `choices` is given, and an implicit choice block
* should be compiled, set this option to require at least
* this number of options to be displayed. See
* `getSituationIdChoices` for a description of the algorithm by
* which this happens. If you do not specify the `choices`
* option, then this option will be ignored.
*
* maxChoices: If `choices` is given, and an implicit choice block
* should be compiled, set this option to require no more than
* this number of options to be displayed. See
* `getSituationIdChoices` for a description of the algorithm
* by which this happens. If you do not specify the `choices`
* option, then this option will be ignored.
*
* The remaining options in the `opts` parameter are the same as for
* the base Situation.
*/
var SimpleSituation = function(content, opts) {
Situation.call(this, opts);
this.content = content;
this.heading = opts && opts.heading;
this.actions = opts && opts.actions;
this.choices = opts && opts.choices;
this.minChoices = opts && opts.minChoices;
this.maxChoices = opts && opts.maxChoices;
};
SimpleSituation.inherits(Situation);
SimpleSituation.prototype.enter = function(character, system, from) {
if (this.heading) {
if (typeof(this.heading) === "function") {
system.writeHeading(this.heading());
} else {
system.writeHeading(this.heading);
}
}
if (this._enter) this._enter(character, system, from);
if (this.content) {
if (typeof(this.content) === "function") {
system.write(this.content());
} else {
system.write(this.content);
}
}
if (this.choices) {
var choices = system.getSituationIdChoices(this.choices,
this.minChoices,
this.maxChoices);
system.writeChoices(choices);
}
};
SimpleSituation.prototype.act = function(character, system, action) {
var response = this.actions[action];
try {
response(character, system, action);
} catch (err) {
if (response) system.write(response);
}
if (this._act) this._act(character, system, action);
};
SimpleSituation.prototype.optionText = function(character, system, sitn) {
var parentResult = Situation.prototype.optionText.call(this, character,
system, sitn);
if (parentResult === undefined) {
return this.heading;
} else {
return parentResult;
}
};
/* Instances of this class define the qualities that characters
* may possess. The title should be a string, and can contain
* HTML. Options are passed in in the opts parameter. The
* following options are available.
*
* priority - A string used to sort qualities within their
* groups. When the system displays a list of qualities they
* will be sorted by this string. If you don't give a
* priority, then the title will be used, so you'll get
* alphabetic order. Normally you either don't give a
* priority, or else use a priority string containing 0-padded
* numbers (e.g. "00001").
*
* group - The Id of a group in which to display this
* parameter. The corresponding group must be defined in
* your `undum.game.qualityGroups` property.
*
* extraClasses - These classes will be attached to the <div> tag
* that surrounds the quality when it is displayed. A common
* use for this is to add icons representing the quality. In
* your CSS define a class for each icon, then pass those
* classes into the appropriate quality definitions.
*
* One key purpose of QualityDefinition is to format the quality
* value for display. Quality values are always stored as numeric
* values, but may be displayed in words or symbols. A number of
* sub-types of QualityDefinition are given that format their
* values in different ways.
*/
var QualityDefinition = function(title, opts) {
var myOpts = extend(opts, {
priority: title,
group: null,
extraClasses: null
});
this.title = title;
this.priority = myOpts.priority;
this.group = myOpts.group;
this.extraClasses = myOpts.extraClasses;
};
/* Formats the value (which is always numeric) into the value to
* be displayed. The result should be HTML (but no tags are
* needed). If null is returned, then the quality definition will
* not be displayed, so if you want an empty value return an empty
* string. */
QualityDefinition.prototype.format = function(character, value) {
return value.toString();
};
/* A quality that is always displayed as the nearest integer of
* the current value, rounded down. Options (in the opts
* parameter) are the same as for QualityDefinition. */
var IntegerQuality = function(title, opts) {
QualityDefinition.call(this, title, opts);
};
IntegerQuality.inherits(QualityDefinition);
IntegerQuality.prototype.format = function(character, value) {
return Math.floor(value).toString();
};
/* A quality that displays as an IntegerQuality, unless it is
* zero, when it is omitted. Options (in the opts * parameter) are
* the same as for QualityDefinition. */
var NonZeroIntegerQuality = function(title, opts) {
IntegerQuality.call(this, title, opts);
};
NonZeroIntegerQuality.inherits(IntegerQuality);
NonZeroIntegerQuality.prototype.format = function(character, value) {
if (value === 0) {
return null;
} else {
return IntegerQuality.prototype.format.call(
this, character, value
);
}
};
/* A quality that displays its full numeric value, including
* decimal component. This is actually a trivial wrapper around
* the QualityDefinition class, which formats in the same
* way. Options (in the opts parameter) are the same as for
* QualityDefinition. */
var NumericQuality = function(title, opts) {
QualityDefinition.call(this, title, opts);
};
NumericQuality.inherits(QualityDefinition);
/* A quality that displays its values as one of a set of
* words. The quality value is first rounded down to the nearest
* integer, then this value is used to select a word to
* display. The offset parameter (optionally passed in as part of
* the opts object) controls what number maps to what word.
*
* The following options (in the opts parameter) are available:
*
* offset - With offset=0 (the default), the quantity value of 0
* will map to the first word, and so on. If offset is
* non-zero then the value given will correspond to the first
* word in the list. So if offset=4, then the first word in
* the list will be used for value=4.
*
* useBonuses - If this is true (the default), then values outside
* the range of words will be construced from the word and a
* numeric bonus. So with offset=0 and five words, the last of
* which is 'amazing', a score of six would give 'amazing+1'.
* if this is false, then the bonus would be omitted, so
* anything beyond 'amazing' is still 'amazing'.
*
* Other options are the same as for QualityDefinition.
*/
var WordScaleQuality = function(title, values, opts) {
var myOpts = extend(opts, {
offset: null,
useBonuses: true
});
QualityDefinition.call(this, title, opts);
this.values = values;
this.offset = myOpts.offset;
this.useBonuses = myOpts.useBonuses;
};
WordScaleQuality.inherits(QualityDefinition);
WordScaleQuality.prototype.format = function(character, value) {
var val = Math.floor(value - this.offset);
var mod = "";
if (val < 0) {
mod = val.toString();
val = 0;
} else if (val >= this.values.length) {
mod = "+" + (val - this.values.length + 1).toString();
val = this.values.length - 1;
}
if (!this.useBonuses) mod = "";
if (this.values[val] === null) return null;
return this.values[val] + mod; // Type coercion
};
/* A specialization of WordScaleQuality that uses the FUDGE RPG's
* adjective scale (from 'terrible' at -3 to 'superb' at +3). The
* options are as for WordScaleQuality. In particular you can use
* the offset option to control where the scale starts. So you
* could model a quality that everyone starts off as 'terrible'
* (such as Nuclear Physics) with an offset of 0, while another that
* is more common (such as Health) could have an offset of -5 so
* everyone starts with 'great'.
*/
var FudgeAdjectivesQuality = function(title, opts) {
WordScaleQuality.call(this, title, [
"terrible".l(),
"poor".l(),
"mediocre".l(),
"fair".l(),
"good".l(),
"great".l(),
"superb".l()
], opts);
if (!('offset' in opts)) this.offset = -3;
};
FudgeAdjectivesQuality.inherits(WordScaleQuality);
/* An boolean quality that removes itself from the quality list if
* it has a zero value. If it has a non-zero value, its value
* field is usually left empty, but you can specify your own
* string to display as the `onDisplay` parameter of the opts
* object. Other options (in the opts parameter) are the same as
* for QualityDefinition. */
var OnOffQuality = function(title, opts) {
var myOpts = extend(opts, {
onDisplay: ""
});
QualityDefinition.call(this, title, opts);
this.onDisplay = myOpts.onDisplay;
};
OnOffQuality.inherits(QualityDefinition);
OnOffQuality.prototype.format = function(character, value) {
if (value) return this.onDisplay;
else return null;
};
/* A boolean quality that has different output text for zero or
* non-zero quality values. Unlike OnOffQuality, this definition
* doesn't remove itself from the list when it is 0. The options
* are as for QualityDefinition, with the addition of options
* 'yesDisplay' and 'noDisplay', which contain the HTML fragments
* used to display true and false values. If not given, these
* default to 'yes' and 'no'.
*/
var YesNoQuality = function(title, opts) {
var myOpts = extend(opts,{
yesDisplay: "yes".l(),
noDisplay: "no".l()
});
QualityDefinition.call(this, title, opts);
this.yesDisplay = myOpts.yesDisplay;
this.noDisplay = myOpts.noDisplay;
};
YesNoQuality.inherits(QualityDefinition);
YesNoQuality.prototype.format = function(character, value) {
if (value) return this.yesDisplay;
else return this.noDisplay;
};
/* Defines a group of qualities that should be displayed together,
* under the given optional title. These should be defined in the
* `undum.game.qualityGroups` parameter. */
var QualityGroup = function(title, opts) {
var myOpts = extend(opts,{
priority: title,
extraClasses: null
});
this.title = title;
this.priority = myOpts.priority;
this.extraClasses = myOpts.extraClasses;
};
/* A system object is passed into the enter, act and exit
* functions of each situation. It is used to interact with the
* UI.
*/
var System = function() {
this.rnd = null;
this.time = 0;
};
/* Removes all content from the page, clearing the main content area.
*
* If an elementSelector is given, then only that selector will be
* cleared. Note that all content from the cleared element is removed,
* but the element itself remains, ready to be filled again using
* System.write.
*/
System.prototype.clearContent = function(elementSelector) {
var $element;
if (elementSelector) $element = document.querySelectorAll(elementSelector);
if (!$element) $element = document.getElementById("content");
$element.innerHTML = '';
};
/* Outputs regular content to the page. The content supplied must
* be valid "Display Content".
*
* "Display Content" is any HTML string that begins with a HTML
* start tag, ends with either an end or a closed tag, and is a
* valid and self-contained snippet of HTML. Note that the string
* doesn't have to consist of only one HTML tag. You could have
* several paragraphs, for example, as long as the content starts
* with the <p> of the first paragraph, and ends with the </p> of
* the last. So "<p>Foo</p><img src='bar'>" is valid, but "foo<img
* src='bar'>" is not.
*
* The content goes to the end of the page, unless you supply the
* optional selector argument. If you do, the content appears
* after the element that matches that selector.
*/
System.prototype.write = function(content, elementSelector) {
doWrite(content, elementSelector);
};
/* Outputs the given content in a heading on the page. The content
* supplied must be valid "Display Content".
*
* The content goes to the end of the page, unless you supply the
* optional selector argument. If you do, the content appears
* after the element that matches that selector.
*/
System.prototype.writeHeading = function(headingContent, elementSelector) {
var heading = document.createElement("<h1>");
heading.innerHTML = headingContent;
doWrite(heading, elementSelector);
};
/* Outputs regular content to the page. The content supplied must
* be valid "Display Content".
*
* The content goes to the beginning of the page, unless you
* supply the optional selector argument. If you do, the content
* appears after the element that matches that selector.
*/
System.prototype.writeBefore = function(content, elementSelector) {
doWrite(content, elementSelector, 'prepend', 'before');
};
/* Outputs regular content to the page. The content supplied must
* be valid "Display Content".
*
* When a selector is not specified, this behaves identically to
* System.prototype.write. If you supply a selector, the content
* appears as a child node at the end of the content of the
* element that matches that selector.
*/
System.prototype.writeInto = function(content, elementSelector) {
doWrite(content, elementSelector, 'append', 'append');
};
/* Replaces content with the content supplied, which must be valid
* "Display Content".
*
* When a selector is not specified, this replaces the entire
* content of the page. Otherwise, it replaces the element matched
* with the selector. This replaces the entire element, including
* the matched tags, so ideally the content supplied should fit
* in its place in the DOM with the same kind of display element.
*/
System.prototype.replaceWith = function(content, elementSelector) {
doWrite(content, elementSelector, 'replaceWith', 'replaceWith');
};
/* Carries out the given situation change or action, as if it were
* in a link that has been clicked. This allows you to do
* procedural transitions. You might have an action that builds up
* the character's strength, and depletes their magic. When the
* magic is all gone, you can force a situation change by calling
* this method. */
System.prototype.doLink = function(code) {
processLink(code);
};
/* Turns any links that target the given href into plain
* text. This can be used to remove action options when an action
* is no longer available. It is used automatically when you give
* a link the 'once' class. */
System.prototype.clearLinks = function(code) {
var links = document.querySelectorAll("a[href='" + code + "']");
Array.prototype.forEach.call(links, function(element, index){
element.querySelectorAll("span").classList.add("ex_link");
});
};
/* Given a list of situation ids, this outputs a standard option
* block with the situation choices in the given order.
*
* The contents of each choice will be a link to the situation,
* the text of the link will be given by the situation's
* outputText property. Note that the canChoose function is
* called, and if it returns false, then the text will appear, but
* the link will not be clickable.
*
* Although canChoose is honored, canView and displayOrder are
* not. If you need to honor these, you should either do so
* manually, ot else use the `getSituationIdChoices` method to
* return an ordered list of valid viewable situation ids.
*/
System.prototype.writeChoices = function(listOfIds, elementSelector) {
if (listOfIds.length === 0) return;
var currentSituation = getCurrentSituation();
var $options = document.getElementById("content").querySelectorAll("ul").classList.add("options");
for (var i = 0; i < listOfIds.length; ++i) {
var situationId = listOfIds[i];
var situation = game.situations[situationId];
assert(situation, "unknown_situation".l({id:situationId}));
var optionText = situation.optionText(character, this,
currentSituation);
if (!optionText) optionText = "choice".l({number:i+1});
var $option = document.getElementById("content").querySelectorAll("li");
var $a;
if (situation.canChoose(character, this, currentSituation)) {
$a = "<a href='"+situationId+"'>"+optionText+"</a>"
} else {
$a = "<span>"+optionText+"</span>";
}
$option.innerHTML = $a;
$options.appendChild($option);
}
doWrite($options, elementSelector, 'append', 'after');
};
/* Returns a list of situation ids to choose from, given a set of
* specifications.
*
* This function is a complex and powerful way of compiling
* implicit situation choices. You give it a list of situation ids
* and situation tags (if a single id or tag is needed just that
* string can be given, it doesn't need to be wrapped in a
* list). Tags should be prefixed with a hash # to differentiate
* them from situation ids. The function then considers all
* matching situations in descending priority order, calling their
* canView functions and filtering out any that should not be
* shown, given the current state. Without additional parameters
* the function returns a list of the situation ids at the highest
* level of priority that has any valid results. So, for example,
* if a tag #places matches three situations, one with priority 2,
* and two with priority 3, and all of them can be viewed in the
* current context, then only the two with priority 3 will be
* returned. This allows you to have high-priority situations that
* trump any lower situations when they are valid, such as
* situations that force the player to go to one destination if
* the player is out of money, for example.
*
* If a minChoices value is given, then the function will attempt
* to return at least that many results. If not enough results are
* available at the highest priority, then lower priorities will
* be considered in turn, until enough situations are found. In
* the example above, if we had a minChoices of three, then all
* three situations would be returned, even though they have
* different priorities. If you need to return all valid
* situations, regardless of their priorities, set minChoices to a
* large number, such as `Number.MAX_VALUE`, and leave maxChoices
* undefined.
*
* If a maxChoices value is given, then the function will not
* return any more than the given number of results. If there are
* more than this number of results possible, then the highest
* priority resuls will be guaranteed to be returned, but the
* lowest priority group will have to fight it out for the
* remaining places. In this case, a random sample is chosen,
* taking into account the frequency of each situation. So a
* situation with a frequency of 100 will be chosen 100 times more
* often than a situation with a frequency of 1, if there is one
* space available. Often these frequencies have to be taken as a
* guideline, and the actual probabilities will only be
* approximate. Consider three situations with frequencies of 1,
* 1, 100, competing for two spaces. The 100-frequency situation
* will be chosen almost every time, but for the other space, one
* of the 1-frequency situations must be chosen. So the actual
* probabilities will be roughly 50%, 50%, 100%. When selecting
* more than one result, frequencies can only be a guide.
*
* Before this function returns its result, it sorts the
* situations in increasing order of their displayOrder values.
*/
System.prototype.getSituationIdChoices = function(listOfOrOneIdsOrTags,
minChoices, maxChoices)
{
var datum;
var i;
// First check if we have a single string for the id or tag.
if (Object.prototype.toString.call(listOfOrOneIdsOrTags).replace(/^\[object (.+)\]$/, "$1").toLowerCase() == 'string') {
listOfOrOneIdsOrTags = [listOfOrOneIdsOrTags];
}
// First we build a list of all candidate ids.
var allIds = {};
for (i = 0; i < listOfOrOneIdsOrTags.length; ++i) {
var tagOrId = listOfOrOneIdsOrTags[i];
if (tagOrId.substr(0, 1) == '#') {
var ids = getSituationIdsWithTag(tagOrId.substr(1));
for (var j = 0; j < ids.length; ++j) {
allIds[ids[j]] = true;
}
} else {
allIds[tagOrId] = true;
}
}
// Filter out anything that can't be viewed right now.
var currentSituation = getCurrentSituation();
var viewableSituationData = [];
for (var situationId in allIds) {
var situation = game.situations[situationId];
assert(situation, "unknown_situation".l({id:situationId}));
if (situation.canView(character, system, currentSituation)) {
// While we're here, get the selection data.
var viewableSituationDatum =
situation.choiceData(character, system, currentSituation);
viewableSituationDatum.id = situationId;
viewableSituationData.push(viewableSituationDatum);
}
}
// Then we sort in descending priority order.
viewableSituationData.sort(function(a, b) {
return b.priority - a.priority;
});
var committed = [];
var candidatesAtLastPriority = [];
var lastPriority;
// In descending priority order.
for (i = 0; i < viewableSituationData.length; ++i) {
datum = viewableSituationData[i];
if (datum.priority != lastPriority) {
if (lastPriority !== undefined) {
// We've dropped a priority group, see if we have enough
// situations so far, and stop if we do.
if (minChoices === undefined || i >= minChoices) break;
}
// Continue to acccumulate more options.
committed.push.apply(committed, candidatesAtLastPriority);
candidatesAtLastPriority = [];
lastPriority = datum.priority;
}
candidatesAtLastPriority.push(datum);
}
// So the values in committed we're committed to, because without
// them we wouldn't hit our minimum. But those in
// candidatesAtLastPriority might take us over our maximum, so
// figure out how many we should choose.
var totalChoices = committed.length + candidatesAtLastPriority.length;
if (maxChoices === undefined || maxChoices >= totalChoices) {
// We can use all the choices.
committed.push.apply(committed, candidatesAtLastPriority);
} else if (maxChoices >= committed.length) {
// We can only use the commited ones.
// NO-OP
} else {
// We have to sample the candidates, using their relative frequency.
var candidatesToInclude = maxChoices - committed.length;
for (i = 0; i < candidatesAtLastPriority.length; ++i) {
datum = candidatesAtLastPriority[i];
datum._frequencyValue = this.rnd.random() / datum.frequency;
}
candidatesToInclude.sort(function(a, b) {
return a._frequencyValue - b._frequencyValue;
});
var chosen = candidatesToInclude.slice(0, candidatesToInclude);
committed.push.apply(committed, chosen);
}
// Now sort in ascending display order.
committed.sort(function(a, b) {
return a.displayOrder - b.displayOrder;
});
// And return as a list of ids only.
var result = [];
for (i = 0; i < committed.length; ++i) {
result.push(committed[i].id);
}
return result;
};
/* Call this to change the character text: the text in the right
* toolbar before the qualities list. This text is designed to be
* a short description of the current state of your character. The
* content you give should be "Display Content" (see
* `System.prototype.write` for the definition).
*/
System.prototype.setCharacterText = function(content) {
var block = document.getElementById("character_text_content");
var oldContent = block.innerHTML;
var newContent = augmentLinks(content);
if (interactive && block.offsetWidth > 0 && block.offsetHeight > 0) {
hideBlock(block);
block.innerHTML = newContent;
showBlock(block);
showHighlight(block.parent);
} else {
block.innerHTML = newContent;
}
};
/* Call this to change the value of a character quality. Don't
* directly change quality values, because that will not update
* the UI. (You can change any data in the character's sandbox
* directly, however, since that isn't displayed). */
System.prototype.setQuality = function(quality, newValue) {
var oldValue = character.qualities[quality];
character.qualities[quality] = newValue;
if (!interactive) return;
// Work out how to display the values.
var newDisplay;
var qualityDefinition = game.qualities[quality];
if (qualityDefinition) {
newDisplay = qualityDefinition.format(character, newValue);
} else {
// We shouldn't display qualities that have no definition.
return;
}
// Add the data block, if we need it.
var qualityBlock = document.getElementById("#q_"+quality);
if (qualityBlock.length <= 0) {
if (newDisplay === null) return;
qualityBlock = addQualityBlock(quality).hide().fadeIn(500);
} else {
// Do nothing if there's nothing to do.
if (oldValue == newValue) return;
// Change the value.
if (newDisplay === null) {
// Remove the block, and possibly the whole group, if
// it is the last quality in the group.
var toRemove = null;
var groupBlock = qualityBlock.parents('.quality_group');
if (groupBlock.find('.quality').length <= 1) {
toRemove = groupBlock;
} else {
toRemove = qualityBlock;
}
toRemove.fadeOut(1000, function() {
toRemove.remove();
});
} else {
var valBlock = qualityBlock.find("[data-attr='value']");
valBlock.fadeOut(250, function() {
valBlock.html(newDisplay);
valBlock.fadeIn(750);
});
}
}
showHighlight(qualityBlock);
};
/* Changes a quality to a new value, but also should show a progress bar
* animation of the change. Removed with the progress bar functionality. */
System.prototype.animateQuality = function(quality, newValue, opts) {
this.setQuality(quality, newValue);
};
/* The character that is passed into each situation is of this
* form.
*
* The `qualities` data member maps the Ids of each quality to its
* current value. When implementing enter, act or exit functions,
* you should consider this to be read-only. Make all
* modifications through `System.prototype.setQuality`, or
* `System.prototype.animateQuality`. In your `init` function, you
* can set these values directly.
*
* The `sandbox` data member is designed to allow your code to
* track any data it needs to. The only proviso is that the data
* structure should be serializable into JSON (this means it must
* only consist of primitive types [objects, arrays, numbers,
* booleans, strings], and it must not contain circular series of
* references). The data in the sandbox is not displayed in the
* UI, although you are free to use it to create suitable output
* for the player..
*/
var Character = function() {
this.qualities = {};
this.sandbox = {};
};
/* The data structure holding the content for the game. By default
* this holds nothing. It is this data structure that is populated
* in the `.game.js` file. Each element in the structure is
* commented, below.
*
* This should be static data that never changes through the
* course of the game. It is never saved, so anything that might
* change should be stored in the character.
*/
var game = {
// Situations
/* An object mapping from the unique id of each situation, to
* the situation object itself. This is the heart of the game
* specification. */
situations: {},
/* The unique id of the situation to enter at the start of a
* new game. */
start: "start",
// Quality display definitions
/* An object mapping the unique id of each quality to its
* QualityDefinition. You don't need definitions for every
* quality, but only qualities in this mapping will be
* displayed in the character box of the UI. */
qualities: {},
/* Qualities can have an optional group Id. This maps those
* Ids to the group definitions that says how to format its
* qualities.
*/
qualityGroups: {},
// Hooks
/* This function is called at the start of the game. It is
* normally overridden to provide initial character creation
* (setting initial quality values, setting the
* character-text. This is optional, however, as set-up
* processing could also be done by the first situation's
* enter function. If this function is given it should have
* the signature function(character, system).
*/
init: null,
/* This function is called before entering any new
* situation. It is called before the corresponding situation
* has its `enter` method called. It can be used to implement
* timed triggers, but is totally optional. If this function
* is given it should have the signature:
*
* function(character, system, oldSituationId, newSituationId);
*/
enter: null,
/* Hook for when the situation has already been carried out
* and printed. The signature is:
*
* function(character, system, oldSituationId, newSituationId);
*/
afterEnter: null,
/* This function is called before carrying out any action in
* any situation. It is called before the corresponding
* situation has its `act` method called. If this optional
* function is given it should have the signature:
*
* function(character, system, situationId, actionId);
*
* If the function returns true, then it is indicating that it
* has consumed the action, and the action will not be passed
* on to the situation. Note that this is the only one of
* these global handlers that can consume the event.
*/
beforeAction: null,
/* This function is called after carrying out any action in
* any situation. It is called after the corresponding
* situation has its `act` method called. If this optional
* function is given it should have the signature:
*
* function(character, system, situationId, actionId);
*/
afterAction: null,
/* This function is called after leaving any situation. It is
* called after the corresponding situation has its `exit`
* method called. If this optional function is given it should
* have the signature:
*
* function(character, system, oldSituationId, newSituationId);
*/
exit: null
};
// =======================================================================
// Code below doesn't form part of the public API for UNDUM, so
// you shouldn't find you need to use it.
// -----------------------------------------------------------------------
// Internal Data
// -----------------------------------------------------------------------
/* The global system object. */
var system = new System();
/* This is the data on the player's progress that gets saved. */
var progress = {
// A random seed string, used internally to make random
// sequences predictable.
seed: null,
// Keeps track of the links clicked, and when.
sequence: [],
// The time when the progress was saved.
saveTime: null
};
/* The Id of the current situation the player is in. */
var current = null;
/* This is the current character. It should be reconstructable
* from the above progress data. */
var character = null;
/* Tracks whether we're in interactive mode or batch mode. */
var interactive = true;
/* The system time when the game was initialized. */
var startTime;
/* The stack of links, resulting from the last action, still be to
* resolved. */
var linkStack = null;
// -----------------------------------------------------------------------
// Utility Functions
// -----------------------------------------------------------------------
var getCurrentSituation = function() {
if (current) {
return game.situations[current];
} else {
return null;
}
};
var parse = function(str) {
if (str === undefined) {
return str;
} else {
return parseFloat(str);
}
};
var parseList = function(str, canBeUndefined) {
if (str === undefined || str === null) {
if (canBeUndefined) {
return undefined;
} else {
return [];
}
} else {
return str.split(/[ ,\t]+/);
}
};
var parseFn = function(str) {
if (str === undefined) {
return str;
} else {
var fstr = "(function(character, system, situation) {\n" +
str + "\n})";
var fn = eval(fstr);
return fn;
}
};
var loadHTMLSituations = function() {
var $htmlSituations = document.querySelectorAll("div.situation");
Array.prototype.forEach.call($htmlSituations, function($situation){
var id = $situation.getAttribute("id");
assert(game.situations[id] === undefined, "existing_situation".l({id:id}));
var content = $situation.innerHTML;
var opts = {
// Situation content
optionText: $situation.getAttribute("data-option-text"),
canView: parseFn($situation.getAttribute("data-can-view")),
canChoose: parseFn($situation.getAttribute("data-can-choose")),
priority: parse($situation.getAttribute("data-priority")),
frequency: parse($situation.getAttribute("data-frequency")),
displayOrder: parse($situation.getAttribute("data-display-order")),
tags: parseList($situation.getAttribute("data-tags"), false),
// Simple Situation content.
heading: $situation.getAttribute("data-heading"),
choices: parseList($situation.getAttribute("data-choices"), true),
minChoices: parse($situation.getAttribute("data-min-choices")),
maxChoices: parse($situation.getAttribute("data-max-choices"))
};
game.situations[id] = new SimpleSituation(content, opts);
});
};
/* Outputs regular content to the page. Used by write and
* writeBefore, the last two arguments control what jQuery methods
* are used to add the content.
*/
// TODO: this function can append text, prepend text or replace text in selector with the supplied one.
var doWrite = function(content, selector) {
continueOutputTransaction();
var output = augmentLinks(content);
var element;
if (selector) element = document.querySelector(selector);
if (element) {
// TODO: scroll to the last position
dimensions = element.getBoundingClientRect();
console.log(dimensions);
window.scroll(0,150);
// TODO: scrollStack[scrollStack.length-1] = scrollPoint;*/
}
if (!element) {
document.getElementById('content').innerHTML = output;
}
else {
element.appendChild(output);
}
/* We want to scroll this new element to the bottom of the screen.
* while still being visible. The easiest way is to find the
* top edge of the *following* element and move that exactly
* to the bottom (while still ensuring that this element is fully
* visible.) */
/*var nextel = output.last().next();
var scrollPoint;
if (!nextel.length) {
scrollPoint = $("#content").height() + $("#title").height() + 60;
} else {
scrollPoint = nextel.offset().top - $(window).height();
}
if (scrollPoint > output.offset().top)
scrollPoint = output.offset().top;
scrollStack[scrollStack.length-1] = scrollPoint;*/
};
/* Gets the unique id used to identify saved games. */
var getSaveId = function() {
return 'undum_'+game.id+"_"+game.version;
};
/* Adds the quality blocks to the character tools. */
var showQualities = function() {
document.getElementById("qualities").innerHTML = '';
for (var qualityId in character.qualities) {
addQualityBlock(qualityId);
}
};
/* Fades in and out a highlight on the given element. */
var showHighlight = function(domElement) {
var highlight = domElement.querySelector(".highlight");
if (highlight.length <= 0) {
highlight = document.createElement("<div></div>").classList.add('highlight');
domElement.appendChild(highlight);
}
showBlock(highlight);
setTimeout(function() {
hideBlock(highlight);
}, 2000);
};
/* Finds the correct location and inserts a particular DOM element
* fits into an existing list of DOM elements. This is done by
* priority order, so all elements (existing and new) must have
* their data-priority attribute set. */
var insertAtCorrectPosition = function(parent, newItem) {
var newPriority = newItem.getAttribute('data-priority');
var _children = parent.children;
if (_children != undefined)
{
for (var i = 0; i < _children.length; i++) {
var child = _children[i];
if (newPriority < child.getAttribute('data-priority')) {
child.before(newItem);
return;
}
}
parent.appendChild(newItem);
}
};
/* Adds a new group to the correct location in the quality list. */
var addGroupBlock = function(groupId) {
var groupDefinition = game.qualityGroups[groupId];
// Build the group div with appropriate heading.
var groupBlock = document.getElementById("quality_group").cloneNode(true);
groupBlock.setAttribute("id", "g_"+groupId);
groupBlock.setAttribute("data-priority", groupDefinition.priority);
var titleElement = groupBlock.querySelectorAll("[data-attr='title']");
if (groupDefinition.title) {
titleElement.innerHTML = groupDefinition.title;
} else {
if (titleElement.parentNode != undefined)
{
titleElement.parentNode.removeChild(titleElement);
}
}
if (groupDefinition.extraClasses) {
for (var i = 0; i < groupDefinition.extraClasses.length; i++) {
groupBlock.addClass(groupDefinition.extraClasses[i]);
}
}
// Add the block to the correct place.
var qualities = document.getElementById("qualities");
insertAtCorrectPosition(qualities, groupBlock);
return groupBlock;
};
/* Adds a new quality to the correct location in the quality list. */
var addQualityBlock = function(qualityId) {
// Make sure we want to display this quality.
var qualityDefinition = game.qualities[qualityId];
if (!qualityDefinition) {
throw new Error("Can't display a quality that hasn't been defined: "+
qualityId);
}
// Work out how the value should be displayed.
var name = qualityDefinition.title;
var val = qualityDefinition.format(
character, character.qualities[qualityId]
);
if (val === null) return null;
// Create the quality output.
var qualityBlock = document.getElementById("quality").cloneNode(true);
qualityBlock.setAttribute("id", "q_"+qualityId);
qualityBlock.setAttribute("data-priority", qualityDefinition.priority);
qualityBlock.querySelectorAll("[data-attr='name']").innerHTML = name;
qualityBlock.querySelectorAll("[data-attr='value']").innerHTML = val;
if (qualityDefinition.extraClasses) {
for (var i = 0; i < qualityDefinition.extraClasses.length; i++) {
qualityBlock.className.add(qualityDefinition.extraClasses[i]);
}
}
// Find or create the group block.
var groupBlock;
var groupId = qualityDefinition.group;
if (groupId) {
var group = game.qualityGroups[groupId];
assert(group, "no_group_definition".l({id: groupId}));
groupBlock = document.getElementById("g_"+groupId);
if (groupBlock == null || groupBlock.length <= 0) {
groupBlock = addGroupBlock(groupId);
}
}
// Position it correctly.
var groupQualityList = groupBlock.querySelectorAll(".qualities_in_group");
insertAtCorrectPosition(groupQualityList, qualityBlock);
return qualityBlock;
};
/* Output events are tracked, so we can make sure we scroll
* correctly. We do this in a stack because one click might cause
* a chain reaction. Of output events, only when we return to the
* top level will we do the scroll.
*
* However, that leaves the question of where to scroll *to*.
* (Remember that elements could be inserted anywhere in the
* document.) Whenever we do a write(), we'll have to update the
* top (last) stack element to that position.
*/
var scrollStack = [];
var pendingFirstWrite = false;
var startOutputTransaction = function() {
if (scrollStack.length === 0) {
pendingFirstWrite = true;
}
// The default is "all the way down".
scrollStack.push(
document.getElementById("content").offsetHeight + document.getElementById("title").offsetHeight + 60
);
};
var continueOutputTransaction = function() {
if (pendingFirstWrite) {
pendingFirstWrite = false;
var separator = $("#ui_library #turn_separator").clone();
separator.removeAttr("id");
$("#content").append(separator);
}
};
var endOutputTransaction = function() {
var scrollPoint = scrollStack.pop();
if (scrollStack.length === 0 && scrollPoint !== null) {
if (interactive) {
window.scroll(0,scrollPoint);
}
scrollPoint = null;
}
};
/* This gets called when a link needs to be followed, regardless
* of whether it was user action that initiated it. */
var linkRe = /^([a-z0-9_-]+|\.)(\/([0-9a-z_-]+))?$/;
var processLink = function(code) {
// Check if we should do this now, or if processing is already
// underway.
if (linkStack !== null) {
linkStack.push(code);
return;
}
// Track where we're about to add new content.
startOutputTransaction();
// We're processing, so make the stack available.
linkStack = [];
// Handle each link in turn.
processOneLink(code);
while (linkStack.length > 0) {
code = linkStack.shift();
processOneLink(code);
}
// We're done, so remove the stack to prevent future pushes.
linkStack = null;
// Scroll to the top of the new content.
endOutputTransaction();
// We're able to save, if we weren't already.
document.getElementById("save").setAttribute('disabled', false);
};
/* This gets called to actually do the work of processing a code.
* When one doLink is called (or a link is clicked), this may set call
* code that further calls doLink, and so on. This method processes
* each one, and processLink manages this.
*/
var processOneLink = function(code) {
var match = code.match(linkRe);
assert(match, "link_not_valid".l({link:code}));
var situation = match[1];
var action = match[3];
// Change the situation
if (situation !== '.') {
if (situation !== current) {
doTransitionTo(situation);
}
} else {
// We should have an action if we have no situation change.
assert(action, "link_no_action".l());
}
// Carry out the action
if (action) {
situation = getCurrentSituation();
if (situation) {
if (game.beforeAction) {
// Try the global act handler, and see if we need
// to notify the situation.
var consumed = game.beforeAction(
character, system, current, action
);
if (consumed !== true) {
situation.act(character, system, action);
}
} else {
// We have no global act handler, always notify
// the situation.
situation.act(character, system, action);
}
if (game.afterAction) {
game.afterAction(character, system, current, action);
}
}
}
};
/* This function listens on content block to filter out link clicks. */
var linkClickHandler = function(event) {
if (event.target.tagName.toLowerCase() === 'a') {
event.preventDefault();
processClick(event.target.href);
}
}
/* This gets called when the user clicks a link to carry out an
* action. */
var processClick = function(code) {
var now = (new Date()).getTime() * 0.001;
system.time = now - startTime;
progress.sequence.push({link:code, when:system.time});
return processLink(code);
};
/* Transitions between situations. */
var doTransitionTo = function(newSituationId) {
var oldSituationId = current;
var oldSituation = getCurrentSituation();
var newSituation = game.situations[newSituationId];
assert(newSituation, "unknown_situation".l({id:newSituationId}));
// We might not have an old situation if this is the start of
// the game.
if (oldSituation) {
// Notify the exiting situation.
oldSituation.exit(character, system, newSituationId);
if (game.exit) {
game.exit(character, system, oldSituationId, newSituationId);
}
}
// Remove links and transient sections.
var content = document.getElementById("content");
links = content.querySelectorAll("a");
Array.prototype.forEach.call(links, function(element, index) {
var a = element;
if (a.classList.contains('sticky') || a.getAttribute("href").match(/[?&]sticky[=&]?/))
return;
if (a.getAttribute("href").match(/[?&]transient[=&]?/)) {
hideBlock(a);
}
a.innerHTML = "<span class='ex_link'>"+a.innerHTML+"</span>";
});
hideBlock(content.querySelectorAll(".transient"));
hideBlock(content.querySelectorAll("ul.options"));
// Move the character.
current = newSituationId;
// Notify the incoming situation.
if (game.enter) {
game.enter(character, system, oldSituationId, newSituationId);
}
newSituation.enter(character, system, oldSituationId);
// additional hook for when the situation text has already been printed
if (game.afterEnter) {
game.afterEnter(character, system, oldSituationId, newSituationId);
}
};
/* Returns HTML from the given content with the non-raw links
* wired up.
* @param content string HTML code
* @retval string */
var augmentLinks = function(content) {
// Wire up the links for regular <a> tags.
output = document.createElement('div');
output.innerHTML = content;
var links = output.querySelectorAll("a");
Array.prototype.forEach.call(links, function(element, index){
var href = element.getAttribute('href');
if (!element.classList.contains("raw")|| href.match(/[?&]raw[=&]?/)) {
if (href.match(linkRe)) {
element.onclick = function(event) {
event.preventDefault();
// If we're a once-click, remove all matching links.
if (element.classList.contains("once") || href.match(/[?&]once[=&]?/)) {
system.clearLinks(href);
}
processClick(href);
return false;
};
} else {
element.classList.add("raw");
}
}
});
return output.innerHTML;
};
/* Erases the character in local storage. This is permanent! */
var doErase = function(force) {
var saveId = getSaveId();
if (localStorage[saveId]) {
if (force || confirm("erase_message".l())) {
delete localStorage[saveId];
document.getElementById("erase").setAttribute('disabled', true);
startGame();
}
}
};
/* Find and return a list of ids for all situations with the given tag. */
var getSituationIdsWithTag = function(tag) {
var result = [];
for (var situationId in game.situations) {
var situation = game.situations[situationId];
for (var i = 0; i < situation.tags.length; ++i) {
if (situation.tags[i] == tag) {
result.push(situationId);
break;
}
}
}
return result;
};
/* Set up the screen from scratch to reflect the current game
* state. */
var initGameDisplay = function() {
// Transition into the first situation,
document.getElementById("content").innerHTML = "";
var situation = getCurrentSituation();
assert(situation, "no_current_situation".l());
showQualities();
};
/* Clear the current game output and start again. */
var startGame = function() {
progress.seed = new Date().toString();
character = new Character();
system.rnd = new Random(progress.seed);
progress.sequence = [{link:game.start, when:0}];
// Empty the display
document.getElementById("content").innerHTML = '';
// Start the game
startTime = new Date().getTime() * 0.001;
system.time = 0;
if (game.init) game.init(character, system);
showQualities();
// Do the first state.
doTransitionTo(game.start);
};
/* Saves the character to local storage. */
var saveGame = function() {
// Store when we're saving the game, to avoid exploits where a
// player loads their file to gain extra time.
var now = (new Date()).getTime() * 0.001;
progress.saveTime = now - startTime;
// Save the game.
localStorage[getSaveId()] = JSON.stringify(progress);
// Switch the button highlights.
document.getElementById("erase").setAttribute('disabled', false);
document.getElementById("save").setAttribute('disabled', true);
};
/* Loads the game from the given data */
var loadGame = function(characterData) {
progress = characterData;
character = new Character();
system.rnd = new Random(progress.seed);
// Empty the display
document.getElementById("content").innerHTML = "";
showQualities();
// Now play through the actions so far:
if (game.init) game.init(character, system);
// Run through all the player's history.
interactive = false;
for (var i = 0; i < progress.sequence.length; i++) {
var step = progress.sequence[i];
// The action must be done at the recorded time.
system.time = step.when;
processLink(step.link);
}
interactive = true;
// Reverse engineer the start time.
var now = new Date().getTime() * 0.001;
startTime = now - progress.saveTime;
// Because we did the run through non-interactively, now we
// need to update the UI.
showQualities();
};
// Internationalization support based on the code provided by Oreolek.
(function() {
var codesToTry = {};
/* Compiles a list of fallback languages to try if the given code
* doesn't have the message we need. Caches it for future use. */
var getCodesToTry = function(languageCode) {
var codeArray = codesToTry[languageCode];
if (codeArray) return codeArray;
codeArray = [];
if (languageCode in undum.language) {
codeArray.push(languageCode);
}
var elements = languageCode.split('-');
for (var i = elements.length-2; i > 0; i--) {
var thisCode = elements.slice(0, i).join('-');
if (thisCode in undum.language) {
codeArray.push(thisCode);
}
}
codeArray.push("");
codesToTry[languageCode] = codeArray;
return codeArray;
};
var lookup = function(languageCode, message) {
var languageData = undum.language[languageCode];
if (!languageData) return null;
return languageData[message];
};
var localize = function(languageCode, message) {
var localized, thisCode;
var languageCodes = getCodesToTry(languageCode);
for (var i = 0; i < languageCodes.length; i++) {
thisCode = languageCodes[i];
localized = lookup(thisCode, message);
if (localized) return localized;
}
return message;
};
// API
String.prototype.l = function(args) {
// Get lang attribute from html tag.
var lang = document.querySelector("html").getAttribute("lang") || "";
// Find the localized form.
var localized = localize(lang, this);
// Merge in any replacement content.
if (args) {
for (var name in args) {
localized = localized.replace(
new RegExp("\\{"+name+"\\}"), args[name]
);
}
}
return localized;
};
})();
// Random Number generation based on seedrandom.js code by David Bau.
// Copyright 2010 David Bau, all rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// conditions are met:
//
// 1. Redistributions of source code must retain the above
// copyright notice, this list of conditions and the
// following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of this module nor the names of its
// contributors may be used to endorse or promote products
// derived from this software without specific prior written
// permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
var Random = (function() {
// Within this closure function the code is basically
// David's. Undum's custom extensions are added to the
// prototype outside of this function.
var width = 256;
var chunks = 6;
var significanceExponent = 52;
var startdenom = Math.pow(width, chunks);
var significance = Math.pow(2, significanceExponent);
var overflow = significance * 2;
var Random = function(seed) {
this.random = null;
if (!seed) throw {
name: "RandomSeedError",
message: "random_seed_error".l()
};
var key = [];
mixkey(seed, key);
var arc4 = new ARC4(key);
this.random = function() {
var n = arc4.g(chunks);
var d = startdenom;
var x = 0;
while (n < significance) {
n = (n + x) * width;
d *= width;
x = arc4.g(1);
}
while (n >= overflow) {
n /= 2;
d /= 2;
x >>>= 1;
}
return (n + x) / d;
};
};
// Helper type.
var ARC4 = function(key) {
var t, u, me = this, keylen = key.length;
var i = 0, j = me.i = me.j = me.m = 0;
me.S = [];
me.c = [];
if (!keylen) { key = [keylen++]; }
while (i < width) { me.S[i] = i++; }
for (i = 0; i < width; i++) {
t = me.S[i];
j = lowbits(j + t + key[i % keylen]);
u = me.S[j];
me.S[i] = u;
me.S[j] = t;
}
me.g = function getnext(count) {
var s = me.S;
var i = lowbits(me.i + 1); var t = s[i];
var j = lowbits(me.j + t); var u = s[j];
s[i] = u;
s[j] = t;
var r = s[lowbits(t + u)];
while (--count) {
i = lowbits(i + 1); t = s[i];
j = lowbits(j + t); u = s[j];
s[i] = u;
s[j] = t;
r = r * width + s[lowbits(t + u)];
}
me.i = i;
me.j = j;
return r;
};
me.g(width);
};
// Helper functions.
var mixkey = function(seed, key) {
seed += '';
var smear = 0;
for (var j = 0; j < seed.length; j++) {
var lb = lowbits(j);
smear ^= key[lb];
key[lb] = lowbits(smear*19 + seed.charCodeAt(j));
}
seed = '';
for (j in key) {
seed += String.fromCharCode(key[j]);
}
return seed;
};
var lowbits = function(n) {
return n & (width - 1);
};
return Random;
})();
/* Returns a random floating point number between zero and
* one. NB: The prototype implementation below just throws an
* error, it will be overridden in each Random object when the
* seed has been correctly configured. */
Random.prototype.random = function() {
throw {
name:"RandomError",
message: "random_error".l()
};
};
/* Returns an integer between the given min and max values,
* inclusive. */
Random.prototype.randomInt = function(min, max) {
return min + Math.floor((max-min+1)*this.random());
};
/* Returns the result of rolling n dice with dx sides, and adding
* plus. */
Random.prototype.dice = function(n, dx, plus) {
var result = 0;
for (var i = 0; i < n; i++) {
result += this.randomInt(1, dx);
}
if (plus) result += plus;
return result;
};
/* Returns the result of rolling n averaging dice (i.e. 6 sided dice
* with sides 2,3,3,4,4,5). And adding plus. */
Random.prototype.aveDice = (function() {
var mapping = [2,3,3,4,4,5];
return function(n, plus) {
var result = 0;
for (var i = 0; i < n; i++) {
result += mapping[this.randomInt(0, 5)];
}
if (plus) result += plus;
return result;
};
})();
/* Returns a dice-roll result from the given string dice
* specification. The specification should be of the form xdy+z,
* where the x component and z component are optional. This rolls
* x dice of with y sides, and adds z to the result, the z
* component can also be negative: xdy-z. The y component can be
* either a number of sides, or can be the special values 'F', for
* a fudge die (with 3 sides, +,0,-), '%' for a 100 sided die, or
* 'A' for an averaging die (with sides 2,3,3,4,4,5).
*/
Random.prototype.diceString = (function() {
var diceRe = /^([1-9][0-9]*)?d([%FA]|[1-9][0-9]*)([-+][1-9][0-9]*)?$/;
return function(def) {
var match = def.match(diceRe);
if (!match) {
throw new Error("dice_string_error".l({string:def}));
}
var num = match[1]?parseInt(match[1], 10):1;
var sides;
var bonus = match[3]?parseInt(match[3], 10):0;
switch (match[2]) {
case 'A':
return this.aveDice(num, bonus);
case 'F':
sides = 3;
bonus -= num*2;
break;
case '%':
sides = 100;
break;
default:
sides = parseInt(match[2], 10);
break;
}
return this.dice(num, sides, bonus);
};
})();
// -----------------------------------------------------------------------
// Setup
// -----------------------------------------------------------------------
/* Export our API. */
window.undum = {
Situation: Situation,
SimpleSituation: SimpleSituation,
QualityDefinition: QualityDefinition,
IntegerQuality: IntegerQuality,
NonZeroIntegerQuality: NonZeroIntegerQuality,
NumericQuality: NumericQuality,
WordScaleQuality: WordScaleQuality,
FudgeAdjectivesQuality: FudgeAdjectivesQuality,
OnOffQuality: OnOffQuality,
YesNoQuality: YesNoQuality,
QualityGroup: QualityGroup,
game: game,
isInteractive: function() { return interactive; },
// The undum set of translated strings.
language: {}
};
// -----------------------------------------------------------------------
// Default Messages
// -----------------------------------------------------------------------
var en = {
terrible: "terrible",
poor: "poor",
mediocre: "mediocre",
fair: "fair",
good: "good",
great: "great",
superb: "superb",
yes: "yes",
no: "no",
choice: "Choice {number}",
no_group_definition: "Couldn't find a group definition for {id}.",
link_not_valid: "The link '{link}' doesn't appear to be valid.",
link_no_action: "A link with a situation of '.', must have an action.",
unknown_situation: "You can't move to an unknown situation: {id}.",
existing_situation: "You can't override situation {id} in HTML.",
erase_message: "This will permanently delete this character and immediately return you to the start of the game. Are you sure?",
no_current_situation: "I can't display, because we don't have a current situation.",
no_local_storage: "No local storage available.",
random_seed_error: "You must provide a valid random seed.",
random_error: "Initialize the Random with a non-empty seed before use.",
dice_string_error: "Couldn't interpret your dice string: '{string}'."
};
// Set this data as both the default fallback language, and the english
// preferred language.
undum.language[""] = en;
undum.language["en"] = en;
/* Set up the game when everything is loaded. */
function ready(fn) {
if (document.readyState != 'loading'){
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(function() {
// Compile additional situations from HTML
loadHTMLSituations();
// Handle storage.
if (hasLocalStorage()) {
var erase = document.getElementById("erase");
erase.onclick = doErase;
erase.keydown = doErase;
var save = document.getElementById("save");
save.onclick = saveGame;
save.keydown = saveGame;
var storedCharacter = localStorage[getSaveId()];
if (storedCharacter) {
try {
loadGame(JSON.parse(storedCharacter));
save.setAttribute('disabled', true);
erase.setAttribute("disabled", false);
} catch(err) {
doErase(true);
}
} else {
save.setAttribute('disabled', true);
erase.setAttribute("disabled", true);
startGame();
}
} else {
document.querySelector(".buttons").innerHTML = "<p>"+"no_local_storage".l()+"</p>";
startGame();
}
// handle the link clicks
document.getElementById("content").addEventListener("click", linkClickHandler, false);
// Display the "click to begin" message. (We do this in code
// so that, if Javascript is off, it doesn't happen.)
showBlock("click_message");
// Show the game when we click on the title.
// Note: if you do events with onclick, you have to have only one click event handler.
// You can use more complex methods if you expect to have more.
document.getElementById("title").onclick = function() {
showBlock("content")
showBlock("content_wrapper");
showBlock("legal");
showBlock("tools_wrapper");
document.getElementById("title").style.cursor = "default";
hideBlock("click_message");
};
/*
// Any point that an option list appears, its options are its
// first links.
var optionLinkEvent = function(event) {
// Make option clicks pass through to their first link.
var link = $("a", this);
if (link.length > 0) {
$(link.get(0)).click();
}
};
items = document.querySelectorAll("ul.options li, #menu li");
Array.prototype.forEach.call(items, function(element, index){
element.addEventListener('click', optionLinkEvent);
});
*/
});
})();
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImludGVybmFsLmpzIiwiYXV0aG9yLmpzIiwic3lzdGVtLmpzIiwicHJpdmF0ZS5qcyIsInNldHVwLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDeEhBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDbmVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUNsZ0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDaDJCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6InVuZHVtLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIEludGVybmFsIEluZnJhc3RydWN0dXJlIEltcGxlbWVudGF0aW9ucyBbTkI6IFRoZXNlIGhhdmUgdG8gYmVcbi8vIGF0IHRoZSB0b3AsIGJlY2F1c2Ugd2UgdXNlIHRoZW0gYmVsb3csIGJ1dCB5b3UgY2FuIHNhZmVseVxuLy8gaWdub3JlIHRoZW0gYW5kIHNraXAgZG93biB0byB0aGUgbmV4dCBzZWN0aW9uLl1cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi8qIENyb2NrZm9yZCdzIGluaGVyaXQgZnVuY3Rpb24gKi9cbkZ1bmN0aW9uLnByb3RvdHlwZS5pbmhlcml0cyA9IGZ1bmN0aW9uKFBhcmVudCkge1xuICB2YXIgZCA9IHt9LCBwID0gKHRoaXMucHJvdG90eXBlID0gbmV3IFBhcmVudCgpKTtcbiAgdGhpcy5wcm90b3R5cGUudWJlciA9IGZ1bmN0aW9uKG5hbWUpIHtcbiAgICBpZiAoIShuYW1lIGluIGQpKSBkW25hbWVdID0gMDtcbiAgICB2YXIgZiwgciwgdCA9IGRbbmFtZV0sIHYgPSBQYXJlbnQucHJvdG90eXBlO1xuICAgIGlmICh0KSB7XG4gICAgICB3aGlsZSAodCkge1xuICAgICAgICB2ID0gdi5jb25zdHJ1Y3Rvci5wcm90b3R5cGU7XG4gICAgICAgIHQgLT0gMTtcbiAgICAgIH1cbiAgICAgIGYgPSB2W25hbWVdO1xuICAgIH0gZWxzZSB7XG4gICAgICBmID0gcFtuYW1lXTtcbiAgICAgIGlmIChmID09IHRoaXNbbmFtZV0pIHtcbiAgICAgICAgZiA9IHZbbmFtZV07XG4gICAgICB9XG4gICAgfVxuICAgIGRbbmFtZV0gKz0gMTtcbiAgICByID0gZi5hcHBseSh0aGlzLCBBcnJheS5wcm90b3R5cGUuc2xpY2UuYXBwbHkoYXJndW1lbnRzLCBbMV0pKTtcbiAgICBkW25hbWVdIC09IDE7XG4gICAgcmV0dXJuIHI7XG4gIH07XG4gIHJldHVybiB0aGlzO1xufTtcblxuLy8gRmVhdHVyZSBkZXRlY3Rpb25cblxudmFyIGhhc0xvY2FsU3RvcmFnZSA9IGZ1bmN0aW9uKCkge1xuICB2YXIgaGFzU3RvcmFnZSA9IGZhbHNlO1xuICB0cnkge1xuICAgIGhhc1N0b3JhZ2UgPSAoJ2xvY2FsU3RvcmFnZScgaW4gd2luZG93KSAmJlxuICAgICAgd2luZG93LmxvY2FsU3RvcmFnZSAhPT0gbnVsbCAmJlxuICAgICAgd2luZG93LmxvY2FsU3RvcmFnZSAhPT0gdW5kZWZpbmVkO1xuICB9XG4gIGNhdGNoIChlcnIpIHtcbiAgICAvLyBGaXJlZm94IHdpdGggdGhlIFwiQWx3YXlzIEFza1wiIGNvb2tpZSBhY2NlcHQgc2V0dGluZ1xuICAgIC8vIHdpbGwgdGhyb3cgYW4gZXJyb3Igd2hlbiBhdHRlbXB0aW5nIHRvIGFjY2VzcyBsb2NhbFN0b3JhZ2VcbiAgICBoYXNTdG9yYWdlID0gZmFsc2U7XG4gIH1cbiAgcmV0dXJuIGhhc1N0b3JhZ2U7XG59O1xuXG4vLy8gQW5pbWF0aW9ucyAtIHlvdSBjYW4gdG90YWxseSByZWRlZmluZSB0aGVzZSEgRmFkZSBpbiBhbmQgZmFkZSBvdXQgYnkgZGVmYXVsdC5cbi8vLyBAcGFyYW0gaWQgc3RyaW5nIG9yIG9iamVjdFxudmFyIHNob3dCbG9jayA9IGZ1bmN0aW9uKGlkKSB7XG4gIHZhciBibG9jayA9IGlkO1xuICBpZiAodHlwZW9mIGlkID09PSBcInN0cmluZ1wiKSB7XG4gICAgdmFyIGJsb2NrID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoaWQpO1xuICB9XG4gIGJsb2NrLmNsYXNzTGlzdC5hZGQoJ3Nob3cnKTtcbiAgYmxvY2suY2xhc3NMaXN0LnJlbW92ZSgnaGlkZScpO1xuICBibG9jay5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJztcbn1cblxudmFyIGhpZGVCbG9jayA9IGZ1bmN0aW9uKGlkKSB7XG4gIHZhciBibG9jayA9IGlkOyAvLyB0eXBlb2YgYmxvY2sgPT09IFwiZWxlbWVudFwiXG4gIGlmICh0eXBlb2YgaWQgPT09IFwic3RyaW5nXCIpIHtcbiAgICB2YXIgYmxvY2sgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCk7XG4gIH1cbiAgaWYgKHR5cGVvZiBpZCA9PT0gXCJvYmplY3RcIikgeyAvLyBwcm9iYWJseSBOb2RlTGlzdFxuICAgIGlmIChpZC5sZW5ndGggPT0gMClcbiAgICAgIHJldHVybjtcbiAgICBBcnJheS5wcm90b3R5cGUuZm9yRWFjaC5jYWxsKGlkLCBmdW5jdGlvbihlbGVtZW50LCBpbmRleCkge1xuICAgICAgZWxlbWVudC5jbGFzc0xpc3QuYWRkKCdoaWRlJyk7XG4gICAgICBlbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoJ3Nob3cnKTtcbiAgICB9KTtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKHR5cGVvZiBibG9jay5jbGFzc0xpc3QgPT09IFwidW5kZWZpbmVkXCIpXG4gIHtcbiAgICBjb25zb2xlLmxvZyhcIlRyaWVkIHRvIGhpZGUgYW4gdW5kZWZpbmVkIGJsb2NrLlwiKTtcbiAgICBjb25zb2xlLmxvZyhpZCk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGJsb2NrLmNsYXNzTGlzdC5hZGQoJ2hpZGUnKTtcbiAgYmxvY2suY2xhc3NMaXN0LnJlbW92ZSgnc2hvdycpO1xufVxuXG4vLyBBc3NlcnRpb25cblxudmFyIEFzc2VydGlvbkVycm9yID0gZnVuY3Rpb24obWVzc2FnZSkge1xuICB0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xuICB0aGlzLm5hbWUgPSBBc3NlcnRpb25FcnJvcjtcbn07XG5Bc3NlcnRpb25FcnJvci5pbmhlcml0cyhFcnJvcik7XG5cbnZhciBhc3NlcnQgPSBmdW5jdGlvbihleHByZXNzaW9uLCBtZXNzYWdlKSB7XG4gIGlmICghZXhwcmVzc2lvbikge1xuICAgIHRocm93IG5ldyBBc3NlcnRpb25FcnJvcihtZXNzYWdlKTtcbiAgfVxufTtcblxuLy8gT2JqZWN0IGV4dGVudGlvblxudmFyIGV4dGVuZCA9IGZ1bmN0aW9uKG91dCkge1xuICBvdXQgPSBvdXQgfHwge307XG4gIGZvciAodmFyIGkgPSAxOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIG9iaiA9IGFyZ3VtZW50c1tpXTtcblxuICAgIGlmICghb2JqKVxuICAgICAgY29udGludWU7XG5cbiAgICBmb3IgKHZhciBrZXkgaW4gb2JqKSB7XG4gICAgICBpZiAob2JqLmhhc093blByb3BlcnR5KGtleSkpIHtcbiAgICAgICAgaWYgKHR5cGVvZiBvYmpba2V5XSA9PT0gJ29iamVjdCcpXG4gICAgICAgICAgZXh0ZW5kKG91dFtrZXldLCBvYmpba2V5XSk7XG4gICAgICAgIGVsc2VcbiAgICAgICAgICBvdXRba2V5XSA9IG9ialtrZXldO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBvdXQ7XG59O1xuIiwiLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIFR5cGVzIGZvciBBdXRob3IgVXNlXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4vKiBUaGUgZ2FtZSBpcyBzcGxpdCBpbnRvIHNpdHVhdGlvbnMsIHdoaWNoIHJlc3BvbmQgdG8gdXNlclxuICogY2hvaWNlcy4gU2l0dWF0aW9uIGlzIHRoZSBiYXNlIHR5cGUuIEl0IGhhcyB0aHJlZSBtZXRob2RzOlxuICogZW50ZXIsIGFjdCBhbmQgZXhpdCwgd2hpY2ggeW91IGltcGxlbWVudCB0byBwZXJmb3JtIGFueVxuICogcHJvY2Vzc2luZyBhbmQgb3V0cHV0IGFueSBjb250ZW50LiBUaGUgZGVmYXVsdCBpbXBsZW1lbnRhdGlvbnNcbiAqIGRvIG5vdGhpbmcuXG4gKlxuICogWW91IGNhbiBlaXRoZXIgY3JlYXRlIHlvdXIgb3duIHR5cGUgb2YgU2l0dWF0aW9uLCBhbmQgYWRkXG4gKiBlbnRlciwgYWN0IGFuZC9vciBleGl0IGZ1bmN0aW9ucyB0byB0aGUgcHJvdG90eXBlIChzZWVcbiAqIFNpbXBsZVNpdHVhdGlvbiBpbiB0aGlzIGZpbGUgZm9yIGFuIGV4YW1wbGUgb2YgdGhhdCksIG9yIHlvdVxuICogY2FuIGdpdmUgdGhvc2UgZnVuY3Rpb25zIGluIHRoZSBvcHRzIHBhcmFtZXRlci4gVGhlIG9wdHNcbiAqIHBhcmFtZXRlciBpcyBhbiBvYmplY3QuIFNvIHlvdSBjb3VsZCB3cml0ZTpcbiAqXG4gKiAgICB2YXIgc2l0dWF0aW9uID0gU2l0dWF0aW9uKHtcbiAqICAgICAgICBlbnRlcjogZnVuY3Rpb24oY2hhcmFjdGVyLCBzeXN0ZW0sIGZyb20pIHtcbiAqICAgICAgICAgICAgLi4uIHlvdXIgaW1wbGVtZW50YXRpb24gLi4uXG4gKiAgICAgICAgfVxuICogICAgfSk7XG4gKlxuICogSWYgeW91IHBhc3MgaW4gZW50ZXIsIGFjdCBhbmQvb3IgZXhpdCB0aHJvdWdoIHRoZXNlIG9wdGlvbnMsXG4gKiB0aGVuIHRoZXkgc2hvdWxkIGhhdmUgdGhlIHNhbWUgZnVuY3Rpb24gc2lnbmF0dXJlIGFzIHRoZSBmdWxsXG4gKiBmdW5jdGlvbiBkZWZpbml0aW9ucywgYmVsb3cuXG4gKlxuICogTm90ZSB0aGF0IFNpbXBsZVNpdHVhdGlvbiwgYSBkZXJpdmVkIHR5cGUgb2YgU2l0dWF0aW9uLCBjYWxsc1xuICogcGFzc2VkIGluIGVudGVyLCBhY3QgYW5kIGV4aXQgZnVuY3Rpb25zIEFTIFdFTEwgQVMgdGhlaXIgbm9ybWFsXG4gKiBhY3Rpb24uIFRoaXMgaXMgbW9zdCBvZnRlbiB3aGF0IHlvdSB3YW50OiB0aGUgbm9ybWFsIGJlaGF2aW9yXG4gKiBwbHVzIGEgbGl0dGxlIGV4dHJhIGN1c3RvbSBiZWhhdmlvci4gSWYgeW91IHdhbnQgdG8gb3ZlcnJpZGVcbiAqIHRoZSBiZWhhdmlvciBvZiBhIFNpbXBsZVNpdHVhdGlvbiwgeW91J2xsIGhhdmUgdG8gY3JlYXRlIGFcbiAqIGRlcml2ZWQgdHlwZSBhbmQgc2V0IHRoZSBlbnRlciwgYWN0IGFuZC9vciBleGl0IGZ1bmN0aW9uIG9uXG4gKiB0aGVpciBwcm90b3R5cGVzLiBJbiBtb3N0IGNhc2VzLCBob3dldmVyLCBpZiB5b3Ugd2FudCB0byBkb1xuICogc29tZXRoaW5nIGNvbXBsZXRlbHkgZGlmZmVyZW50LCBpdCBpcyBiZXR0ZXIgdG8gZGVyaXZlIHlvdXJcbiAqIHR5cGUgZnJvbSB0aGlzIHR5cGU6IFNpdHVhdGlvbiwgcmF0aGVyIHRoYW4gb25lIG9mIGl0c1xuICogY2hpbGRyZW4uXG4gKlxuICogSW4gYWRkaXRpb24gdG8gZW50ZXIsIGV4aXQgYW5kIGFjdCwgdGhlIGZvbGxvd2luZyBvcHRpb25zXG4gKiByZWxhdGVkIHRvIGltcGxpY2l0IHNpdHVhdGlvbiBzZWxlY3Rpb24gYXJlIGF2YWlsYWJsZTpcbiAqXG4gKiBvcHRpb25UZXh0OiBhIHN0cmluZyBvciBhIGZ1bmN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLFxuICogICAgIHNpdHVhdGlvbikgd2hpY2ggc2hvdWxkIHJldHVybiB0aGUgbGFiZWwgdG8gcHV0IGluIGFuXG4gKiAgICAgb3B0aW9uIGJsb2NrIHdoZXJlIGEgbGluayB0byB0aGlzIHNpdHVhdGlvbiBjYW4gYmVcbiAqICAgICBjaG9zZW4uIFRoZSBzaXR1YXRpb24gcGFzc2VkIGluIGlzIHRoZSBzaXR1YXRpb24gd2hlcmUgdGhlXG4gKiAgICAgb3B0aW9uIGJsb2NrIGlzIGJlaW5nIGRpc3BsYXllZC5cbiAqXG4gKiBjYW5WaWV3OiBhIGZ1bmN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLCBzaXR1YXRpb24pIHdoaWNoIHNob3VsZFxuICogICAgIHJldHVybiB0cnVlIGlmIHRoaXMgc2l0dWF0aW9uIHNob3VsZCBiZSB2aXNpYmxlIGluIGFuXG4gKiAgICAgb3B0aW9uIGJsb2NrIGluIHRoZSBnaXZlbiBzaXR1YXRpb24uXG4gKlxuICogY2FuQ2hvb3NlOiBhIGZ1bmN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLCBzaXR1YXRpb24pIHdoaWNoIHNob3VsZFxuICogICAgIHJldHVybiB0cnVlIGlmIHRoaXMgc2l0dWF0aW9uIHNob3VsZCBhcHBlYXIgY2xpY2thYmxlIGluIGFuXG4gKiAgICAgb3B0aW9uIGJsb2NrLiBSZXR1cm5pbmcgZmFsc2UgYWxsb3dzIHlvdSB0byBwcmVzZW50IHRoZVxuICogICAgIG9wdGlvbiBidXQgcHJldmVudCBpdCBiZWluZyBzZWxlY3RlZC4gWW91IG1heSB3YW50IHRvXG4gKiAgICAgaW5kaWNhdGUgdG8gdGhlIHBsYXllciB0aGF0IHRoZXkgbmVlZCB0byBjb2xsZWN0IHNvbWVcbiAqICAgICBpbXBvcnRhbnQgb2JqZWN0IGJlZm9yZSB0aGUgb3B0aW9uIGlzIGF2YWlsYWJsZSwgZm9yXG4gKiAgICAgZXhhbXBsZS5cbiAqXG4gKiB0YWdzOiBhIGxpc3Qgb2YgdGFncyBmb3IgdGhpcyBzaXR1YXRpb24sIHdoaWNoIGNhbiBiZSB1c2VkIGZvclxuICogICAgIGltcGxpY2l0IHNpdHVhdGlvbiBzZWxlY3Rpb24uIFRoZSB0YWdzIGNhbiBhbHNvIGJlIGdpdmVuIGFzXG4gKiAgICAgc3BhY2UsIHRhYiBvciBjb21tYSBzZXBhcmF0ZWQgdGFncyBpbiBhIHN0cmluZy4gTm90ZSB0aGF0LFxuICogICAgIHdoZW4gY2FsbGluZyBgZ2V0U2l0dWF0aW9uSWRDaG9pY2VzYCwgdGFncyBhcmUgcHJlZml4ZWQgd2l0aFxuICogICAgIGEgaGFzaCwgYnV0IHRoYXQgc2hvdWxkIG5vdCBiZSB0aGUgY2FzZSBoZXJlLiBKdXN0IHVzZSB0aGVcbiAqICAgICBwbGFpbiB0YWcgbmFtZS5cbiAqXG4gKiBwcmlvcml0eTogYSBudW1lcmljIHByaW9yaXR5IHZhbHVlIChkZWZhdWx0ID0gMSkuIFdoZW5cbiAqICAgICBzZWxlY3Rpbmcgc2l0dWF0aW9ucyBpbXBsaWNpdGx5LCBoaWdoZXIgcHJpb3JpdHkgc2l0dWF0aW9uc1xuICogICAgIGFyZSBjb25zaWRlcmVkIGZpcnN0LlxuICpcbiAqIGZyZXF1ZW5jeTogYSBudW1lcmljIHJlbGF0aXZlIGZyZXF1ZW5jeSAoZGVmYXVsdCA9IDEpLCBzbyAxMDBcbiAqICAgICB3b3VsZCBiZSAxMDAgdGltZXMgbW9yZSBmcmVxdWVudC4gV2hlbiB0aGVyZSBhcmUgbW9yZVxuICogICAgIG9wdGlvbnMgdGhhdCBjYW4gYmUgZGlzcGxheWVkLCBzaXR1YXRpb25zIHdpbGwgYmUgc2VsZWN0ZWRcbiAqICAgICByYW5kb21seSBiYXNlZCBvbiB0aGVpciBmcmVxdWVuY3kuXG4gKlxuICogZGlzcGxheU9yZGVyOiBhIG51bWVyaWMgb3JkZXJpbmcgdmFsdWUgKGRlZmF1bHQgPSAxKS4gV2hlblxuKiAgICAgc2l0dWF0aW9ucyBhcmUgc2VsZWN0ZWQgaW1wbGljaXRseSwgdGhlIHJlc3VsdHMgYXJlIG9yZGVyZWRcbiogICAgIGJ5IGluY3JlYXNpbmcgZGlzcGxheU9yZGVyLlxuKi9cbnZhciBTaXR1YXRpb24gPSBmdW5jdGlvbihvcHRzKSB7XG4gIGlmIChvcHRzKSB7XG4gICAgaWYgKG9wdHMuZW50ZXIpIHRoaXMuX2VudGVyID0gb3B0cy5lbnRlcjtcbiAgICBpZiAob3B0cy5hY3QpIHRoaXMuX2FjdCA9IG9wdHMuYWN0O1xuICAgIGlmIChvcHRzLmV4aXQpIHRoaXMuX2V4aXQgPSBvcHRzLmV4aXQ7XG5cbiAgICAvLyBPcHRpb25zIHJlbGF0ZWQgdG8gdGhpcyBzaXR1YXRpb24gYmVpbmcgYXV0b21hdGljYWxseVxuICAgIC8vIHNlbGVjdGVkIGFuZCBkaXNwbGF5ZWQgaW4gYSBsaXN0IG9mIG9wdGlvbnMuXG4gICAgdGhpcy5fb3B0aW9uVGV4dCA9IG9wdHMub3B0aW9uVGV4dDtcbiAgICB0aGlzLl9jYW5WaWV3ID0gb3B0cy5jYW5WaWV3IHx8IHRydWU7XG4gICAgdGhpcy5fY2FuQ2hvb3NlID0gb3B0cy5jYW5DaG9vc2UgfHwgdHJ1ZTtcbiAgICB0aGlzLl9wcmlvcml0eSA9IChvcHRzLnByaW9yaXR5ICE9PSB1bmRlZmluZWQpID8gb3B0cy5wcmlvcml0eSA6IDE7XG4gICAgdGhpcy5fZnJlcXVlbmN5ID1cbiAgICAgIChvcHRzLmZyZXF1ZW5jeSAhPT0gdW5kZWZpbmVkKSA/IG9wdHMuZnJlcXVlbmN5IDogMTtcbiAgICB0aGlzLl9kaXNwbGF5T3JkZXIgPVxuICAgICAgKG9wdHMuZGlzcGxheU9yZGVyICE9PSB1bmRlZmluZWQpID8gb3B0cy5kaXNwbGF5T3JkZXIgOiAxO1xuXG4gICAgLy8gVGFnIGFyZSBub3Qgc3RvcmVkIHdpdGggYW4gdW5kZXJzY29yZSwgYmVjYXVzZSB0aGV5IGFyZVxuICAgIC8vIGFjY2Vzc2VkIGRpcmVjdHkuIFRoZXkgc2hvdWxkIG5vdCBiZSBjb250ZXh0IHNlbnNpdGl2ZVxuICAgIC8vICh1c2UgdGhlIGNhblZpZXcgZnVuY3Rpb24gdG8gZG8gY29udGV4dCBzZW5zaXRpdmVcbiAgICAvLyBtYW5pcHVsYXRpb24pLlxuICAgIGlmIChvcHRzLnRhZ3MgIT09IHVuZGVmaW5lZCkge1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkob3B0cy50YWdzKSkge1xuICAgICAgICB0aGlzLnRhZ3MgPSBvcHRzLnRhZ3M7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnRhZ3MgPSBvcHRzLnRhZ3Muc3BsaXQoL1sgXFx0LF0rLyk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMudGFncyA9IFtdO1xuICAgIH1cbiAgfSBlbHNlIHtcbiAgICB0aGlzLl9jYW5WaWV3ID0gdHJ1ZTtcbiAgICB0aGlzLl9jYW5DaG9vc2UgPSB0cnVlO1xuICAgIHRoaXMuX3ByaW9yaXR5ID0gMTtcbiAgICB0aGlzLl9mcmVxdWVuY3kgPSAxO1xuICAgIHRoaXMuX2Rpc3BsYXlPcmRlciA9IDE7XG4gICAgdGhpcy50YWdzID0gW107XG4gIH1cbn07XG4vKiBBIGZ1bmN0aW9uIHRoYXQgdGFrZXMgYWN0aW9uIHdoZW4gd2UgZW50ZXIgYSBzaXR1YXRpb24uIFRoZVxuICogbGFzdCBwYXJhbWV0ZXIgaW5kaWNhdGVzIHRoZSBzaXR1YXRpb24gd2UgaGF2ZSBqdXN0IGxlZnQ6IGl0XG4gKiBtYXkgYmUgbnVsbCBpZiB0aGlzIGlzIHRoZSBzdGFydGluZyBzaXR1YXRpb24uIFVubGlrZSB0aGVcbiAqIGV4aXQoKSBtZXRob2QsIHRoaXMgbWV0aG9kIGNhbm5vdCBwcmV2ZW50IHRoZSB0cmFuc2l0aW9uXG4gKiBoYXBwZW5pbmc6IGl0cyByZXR1cm4gdmFsdWUgaXMgaWdub3JlZC4gKi9cblNpdHVhdGlvbi5wcm90b3R5cGUuZW50ZXIgPSBmdW5jdGlvbihjaGFyYWN0ZXIsIHN5c3RlbSwgZnJvbSkge1xuICBpZiAodGhpcy5fZW50ZXIpIHRoaXMuX2VudGVyKGNoYXJhY3Rlciwgc3lzdGVtLCBmcm9tKTtcbn07XG4vKiBBIGZ1bmN0aW9uIHRoYXQgdGFrZXMgYWN0aW9uIHdoZW4gd2UgY2Fycnkgb3V0IHNvbWUgYWN0aW9uIGluIGFcbiAqIHNpdHVhdGlvbiB0aGF0IGlzbid0IGludGVuZGVkIHRvIGxlYWQgdG8gYSBuZXcgc2l0dWF0aW9uLiAqL1xuU2l0dWF0aW9uLnByb3RvdHlwZS5hY3QgPSBmdW5jdGlvbihjaGFyYWN0ZXIsIHN5c3RlbSwgYWN0aW9uKSB7XG4gIGlmICh0aGlzLl9hY3QpIHRoaXMuX2FjdChjaGFyYWN0ZXIsIHN5c3RlbSwgYWN0aW9uKTtcbn07XG4vKiBBIGZ1bmN0aW9uIHRoYXQgdGFrZXMgYWN0aW9uIHdoZW4gd2UgZXhpdCBhIHNpdHVhdGlvbi4gVGhlIGxhc3RcbiAqIHBhcmFtZXRlciBpbmRpY2F0ZXMgdGhlIHNpdHVhdGlvbiB3ZSBhcmUgZ29pbmcgdG8uICovXG5TaXR1YXRpb24ucHJvdG90eXBlLmV4aXQgPSBmdW5jdGlvbihjaGFyYWN0ZXIsIHN5c3RlbSwgdG8pIHtcbiAgaWYgKHRoaXMuX2V4aXQpIHRoaXMuX2V4aXQoY2hhcmFjdGVyLCBzeXN0ZW0sIHRvKTtcbn07XG4vKiBEZXRlcm1pbmVzIHdoZXRoZXIgdGhpcyBzaXR1YXRpb24gc2hvdWxkIGJlIGNvbnRhaW5lZCB3aXRoaW4gYVxuICogbGlzdCBvZiBvcHRpb25zIGdlbmVyYXRlZCBhdXRvbWF0aWNhbGx5IGJ5IHRoZSBnaXZlblxuICogc2l0dWF0aW9uLiAqL1xuU2l0dWF0aW9uLnByb3RvdHlwZS5jYW5WaWV3ID0gZnVuY3Rpb24oY2hhcmFjdGVyLCBzeXN0ZW0sIHNpdHVhdGlvbikge1xuICBpZiAodHlwZW9mKHRoaXMuX2NhblZpZXcpID09PSBcImZ1bmN0aW9uXCIgKSB7XG4gICAgcmV0dXJuIHRoaXMuX2NhblZpZXcoY2hhcmFjdGVyLCBzeXN0ZW0sIHNpdHVhdGlvbik7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHRoaXMuX2NhblZpZXc7XG4gIH1cbn07XG4vKiBEZXRlcm1pbmVzIHdoZXRoZXIgdGhpcyBzaXR1YXRpb24gc2hvdWxkIGJlIGNsaWNrYWJsZSB3aXRoaW4gYVxuICogbGlzdCBvZiBvcHRpb25zIGdlbmVyYXRlZCBhdXRvbWF0aWNhbGx5IGJ5IHRoZSBnaXZlbiBzaXR1YXRpb24uICovXG5TaXR1YXRpb24ucHJvdG90eXBlLmNhbkNob29zZSA9IGZ1bmN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLCBzaXR1YXRpb24pIHtcbiAgaWYgKHR5cGVvZih0aGlzLl9jYW5DaG9vc2UpID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICByZXR1cm4gdGhpcy5fY2FuQ2hvb3NlKGNoYXJhY3Rlciwgc3lzdGVtLCBzaXR1YXRpb24pO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiB0aGlzLl9jYW5DaG9vc2U7XG4gIH1cbn07XG4vKiBSZXR1cm5zIHRoZSB0ZXh0IHRoYXQgc2hvdWxkIGJlIHVzZWQgdG8gZGlzcGxheSB0aGlzIHNpdHVhdGlvblxuICogaW4gYW4gYXV0b21hdGljYWxseSBnZW5lcmF0ZWQgbGlzdCBvZiBjaG9pY2VzLiAqL1xuU2l0dWF0aW9uLnByb3RvdHlwZS5vcHRpb25UZXh0ID0gZnVuY3Rpb24oY2hhcmFjdGVyLCBzeXN0ZW0sIHNpdHVhdGlvbikge1xuICBpZiAodHlwZW9mKHRoaXMuX29wdGlvblRleHQpID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICByZXR1cm4gdGhpcy5fb3B0aW9uVGV4dChjaGFyYWN0ZXIsIHN5c3RlbSwgc2l0dWF0aW9uKTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gdGhpcy5fb3B0aW9uVGV4dDtcbiAgfVxufTtcbi8qIFJldHVybnMgdGhlIHByaW9yaXR5LCBmcmVxdWVuY3kgYW5kIGRpc3BsYXlPcmRlciBmb3IgdGhpcyBzaXR1YXRpb24sXG4gKiB3aGVuIGJlaW5nIHNlbGVjdGVkIHVzaW5nIGBzeXN0ZW0uZ2V0U2l0dWF0aW9uSWRDaG9pY2VzYC4gKi9cblNpdHVhdGlvbi5wcm90b3R5cGUuY2hvaWNlRGF0YSA9IGZ1bmN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLCBzaXR1YXRpb24pIHtcbiAgcmV0dXJuIHtcbnByaW9yaXR5OiB0aGlzLl9wcmlvcml0eSxcbiAgICAgICAgICAgIGZyZXF1ZW5jeTogdGhpcy5fZnJlcXVlbmN5LFxuICAgICAgICAgICAgZGlzcGxheU9yZGVyOiB0aGlzLl9kaXNwbGF5T3JkZXJcbiAgfTtcbn07XG5cbi8qIEEgc2ltcGxlIHNpdHVhdGlvbiBoYXMgYSBibG9jayBvZiBjb250ZW50IHRoYXQgaXQgZGlzcGxheXMgd2hlblxuICogdGhlIHNpdHVhdGlvbiBpcyBlbnRlcmVkLiBUaGUgY29udGVudCBtdXN0IGJlIHZhbGlkIFwiRGlzcGxheVxuICogQ29udGVudFwiIChzZWUgYFN5c3RlbS5wcm90b3R5cGUud3JpdGVgIGZvciBhIGRlZmluaXRpb24pLiBUaGlzXG4gKiBjb25zdHJ1Y3RvciBoYXMgb3B0aW9ucyB0aGF0IGNvbnRyb2wgaXRzIGJlaGF2aW9yOlxuICpcbiAqIGhlYWRpbmc6IFRoZSBvcHRpb25hbCBgaGVhZGluZ2Agd2lsbCBiZSB1c2VkIGFzIGEgc2VjdGlvbiB0aXRsZVxuICogICAgIGJlZm9yZSB0aGUgY29udGVudCBpcyBkaXNwbGF5ZWQuIFRoZSBoZWFkaW5nIGNhbiBiZSBhbnlcbiAqICAgICBIVE1MIHN0cmluZywgaXQgZG9lc24ndCBuZWVkIHRvIGJlIFwiRGlzcGxheSBDb250ZW50XCIuIElmXG4gKiAgICAgdGhlIGhlYWRpbmcgaXMgbm90IGdpdmVuLCBubyBoZWFkaW5nIHdpbGwgYmUgZGlzcGxheWVkLiBJZlxuICogICAgIGEgaGVhZGluZyBpcyBnaXZlbiwgYW5kIG5vIG9wdGlvblRleHQgaXMgc3BlY2lmaWVkIChzZWVcbiAqICAgICBgU2l0dWF0aW9uYCBmb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiBgb3B0aW9uVGV4dGApLCB0aGVuIHRoZVxuICogICAgIGhlYWRpbmcgd2lsbCBhbHNvIGJlIHVzZWQgZm9yIHRoZSBzaXR1YXRpb24ncyBvcHRpb24gdGV4dC5cbiAqXG4gKiBhY3Rpb25zOiBUaGlzIHNob3VsZCBiZSBhbiBvYmplY3QgbWFwcGluZyBhY3Rpb24gSWRzIHRvIGFcbiAqICAgICByZXNwb25zZS4gVGhlIHJlc3BvbnNlIHNob3VsZCBlaXRoZXIgYmUgXCJEaXNwbGF5IENvbnRlbnRcIlxuICogICAgIHRvIGRpc3BsYXkgaWYgdGhpcyBhY3Rpb24gaXMgY2FycmllZCBvdXQsIG9yIGl0IHNob3VsZCBiZSBhXG4gKiAgICAgZnVuY3Rpb24oY2hhcmFjdGVyLCBzeXN0ZW0sIGFjdGlvbikgdGhhdCB3aWxsIHByb2Nlc3MgdGhlXG4gKiAgICAgYWN0aW9uLlxuICpcbiAqIGNob2ljZXM6IEEgbGlzdCBvZiBzaXR1YXRpb24gaWRzIGFuZCB0YWdzIHRoYXQsIGlmIGdpdmVuLCB3aWxsXG4gKiAgICAgYmUgdXNlZCB0byBjb21waWxlIGFuIGltcGxpY2l0IG9wdGlvbiBibG9jayB1c2luZ1xuICogICAgIGBnZXRTaXR1YXRpb25JZENob2ljZXNgIChzZWUgdGhhdCBmdW5jdGlvbiBmb3IgbW9yZSBkZXRhaWxzXG4gKiAgICAgb2YgaG93IHRoaXMgd29ya3MpLiBUYWdzIGluIHRoaXMgbGlzdCBzaG91bGQgYmUgcHJlZml4ZWRcbiAqICAgICB3aXRoIGEgaGFzaCAjIHN5bWJvbCwgdG8gZGlzdGluZ3Vpc2ggdGhlbSBmcm9tIHNpdHVhdGlvblxuICogICAgIGlkcy4gSWYganVzdCBhIHNpbmdsZSB0YWcgb3IgaWQgaXMgbmVlZGVkLCBpdCBjYW4gYmUgcGFzc2VkXG4gKiAgICAgaW4gYXMgYSBzdHJpbmcgd2l0aG91dCB3cmFwcGluZyBpbnRvIGEgbGlzdC5cbiAqXG4gKiBtaW5DaG9pY2VzOiBJZiBgY2hvaWNlc2AgaXMgZ2l2ZW4sIGFuZCBhbiBpbXBsaWNpdCBjaG9pY2UgYmxvY2tcbiAqICAgICBzaG91bGQgYmUgY29tcGlsZWQsIHNldCB0aGlzIG9wdGlvbiB0byByZXF1aXJlIGF0IGxlYXN0XG4gKiAgICAgdGhpcyBudW1iZXIgb2Ygb3B0aW9ucyB0byBiZSBkaXNwbGF5ZWQuIFNlZVxuICogICAgIGBnZXRTaXR1YXRpb25JZENob2ljZXNgIGZvciBhIGRlc2NyaXB0aW9uIG9mIHRoZSBhbGdvcml0aG0gYnlcbiAqICAgICB3aGljaCB0aGlzIGhhcHBlbnMuIElmIHlvdSBkbyBub3Qgc3BlY2lmeSB0aGUgYGNob2ljZXNgXG4gKiAgICAgb3B0aW9uLCB0aGVuIHRoaXMgb3B0aW9uIHdpbGwgYmUgaWdub3JlZC5cbiAqXG4gKiBtYXhDaG9pY2VzOiBJZiBgY2hvaWNlc2AgaXMgZ2l2ZW4sIGFuZCBhbiBpbXBsaWNpdCBjaG9pY2UgYmxvY2tcbiAqICAgICBzaG91bGQgYmUgY29tcGlsZWQsIHNldCB0aGlzIG9wdGlvbiB0byByZXF1aXJlIG5vIG1vcmUgdGhhblxuICogICAgIHRoaXMgbnVtYmVyIG9mIG9wdGlvbnMgdG8gYmUgZGlzcGxheWVkLiBTZWVcbiAqICAgICBgZ2V0U2l0dWF0aW9uSWRDaG9pY2VzYCBmb3IgYSBkZXNjcmlwdGlvbiBvZiB0aGUgYWxnb3JpdGhtXG4gKiAgICAgYnkgd2hpY2ggdGhpcyBoYXBwZW5zLiBJZiB5b3UgZG8gbm90IHNwZWNpZnkgdGhlIGBjaG9pY2VzYFxuICogICAgIG9wdGlvbiwgdGhlbiB0aGlzIG9wdGlvbiB3aWxsIGJlIGlnbm9yZWQuXG4gKlxuICogVGhlIHJlbWFpbmluZyBvcHRpb25zIGluIHRoZSBgb3B0c2AgcGFyYW1ldGVyIGFyZSB0aGUgc2FtZSBhcyBmb3JcbiAqIHRoZSBiYXNlIFNpdHVhdGlvbi5cbiAqL1xudmFyIFNpbXBsZVNpdHVhdGlvbiA9IGZ1bmN0aW9uKGNvbnRlbnQsIG9wdHMpIHtcbiAgU2l0dWF0aW9uLmNhbGwodGhpcywgb3B0cyk7XG4gIHRoaXMuY29udGVudCA9IGNvbnRlbnQ7XG4gIHRoaXMuaGVhZGluZyA9IG9wdHMgJiYgb3B0cy5oZWFkaW5nO1xuICB0aGlzLmFjdGlvbnMgPSBvcHRzICYmIG9wdHMuYWN0aW9ucztcblxuICB0aGlzLmNob2ljZXMgPSBvcHRzICYmIG9wdHMuY2hvaWNlcztcbiAgdGhpcy5taW5DaG9pY2VzID0gb3B0cyAmJiBvcHRzLm1pbkNob2ljZXM7XG4gIHRoaXMubWF4Q2hvaWNlcyA9IG9wdHMgJiYgb3B0cy5tYXhDaG9pY2VzO1xufTtcblNpbXBsZVNpdHVhdGlvbi5pbmhlcml0cyhTaXR1YXRpb24pO1xuU2ltcGxlU2l0dWF0aW9uLnByb3RvdHlwZS5lbnRlciA9IGZ1bmN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLCBmcm9tKSB7XG4gIGlmICh0aGlzLmhlYWRpbmcpIHtcbiAgICBpZiAodHlwZW9mKHRoaXMuaGVhZGluZykgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgc3lzdGVtLndyaXRlSGVhZGluZyh0aGlzLmhlYWRpbmcoKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHN5c3RlbS53cml0ZUhlYWRpbmcodGhpcy5oZWFkaW5nKTtcbiAgICB9XG4gIH1cbiAgaWYgKHRoaXMuX2VudGVyKSB0aGlzLl9lbnRlcihjaGFyYWN0ZXIsIHN5c3RlbSwgZnJvbSk7XG4gIGlmICh0aGlzLmNvbnRlbnQpIHtcbiAgICBpZiAodHlwZW9mKHRoaXMuY29udGVudCkgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgc3lzdGVtLndyaXRlKHRoaXMuY29udGVudCgpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgc3lzdGVtLndyaXRlKHRoaXMuY29udGVudCk7XG4gICAgfVxuICB9XG4gIGlmICh0aGlzLmNob2ljZXMpIHtcbiAgICB2YXIgY2hvaWNlcyA9IHN5c3RlbS5nZXRTaXR1YXRpb25JZENob2ljZXModGhpcy5jaG9pY2VzLFxuICAgICAgICB0aGlzLm1pbkNob2ljZXMsXG4gICAgICAgIHRoaXMubWF4Q2hvaWNlcyk7XG4gICAgc3lzdGVtLndyaXRlQ2hvaWNlcyhjaG9pY2VzKTtcbiAgfVxufTtcblNpbXBsZVNpdHVhdGlvbi5wcm90b3R5cGUuYWN0ID0gZnVuY3Rpb24oY2hhcmFjdGVyLCBzeXN0ZW0sIGFjdGlvbikge1xuICB2YXIgcmVzcG9uc2UgPSB0aGlzLmFjdGlvbnNbYWN0aW9uXTtcbiAgdHJ5IHtcbiAgICByZXNwb25zZShjaGFyYWN0ZXIsIHN5c3RlbSwgYWN0aW9uKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgaWYgKHJlc3BvbnNlKSBzeXN0ZW0ud3JpdGUocmVzcG9uc2UpO1xuICB9XG4gIGlmICh0aGlzLl9hY3QpIHRoaXMuX2FjdChjaGFyYWN0ZXIsIHN5c3RlbSwgYWN0aW9uKTtcbn07XG5TaW1wbGVTaXR1YXRpb24ucHJvdG90eXBlLm9wdGlvblRleHQgPSBmdW5jdGlvbihjaGFyYWN0ZXIsIHN5c3RlbSwgc2l0bikge1xuICB2YXIgcGFyZW50UmVzdWx0ID0gU2l0dWF0aW9uLnByb3RvdHlwZS5vcHRpb25UZXh0LmNhbGwodGhpcywgY2hhcmFjdGVyLFxuICAgICAgc3lzdGVtLCBzaXRuKTtcbiAgaWYgKHBhcmVudFJlc3VsdCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIHRoaXMuaGVhZGluZztcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gcGFyZW50UmVzdWx0O1xuICB9XG59O1xuXG4vKiBJbnN0YW5jZXMgb2YgdGhpcyBjbGFzcyBkZWZpbmUgdGhlIHF1YWxpdGllcyB0aGF0IGNoYXJhY3RlcnNcbiAqIG1heSBwb3NzZXNzLiBUaGUgdGl0bGUgc2hvdWxkIGJlIGEgc3RyaW5nLCBhbmQgY2FuIGNvbnRhaW5cbiAqIEhUTUwuIE9wdGlvbnMgYXJlIHBhc3NlZCBpbiBpbiB0aGUgb3B0cyBwYXJhbWV0ZXIuIFRoZVxuICogZm9sbG93aW5nIG9wdGlvbnMgYXJlIGF2YWlsYWJsZS5cbiAqXG4gKiBwcmlvcml0eSAtIEEgc3RyaW5nIHVzZWQgdG8gc29ydCBxdWFsaXRpZXMgd2l0aGluIHRoZWlyXG4gKiAgICAgZ3JvdXBzLiBXaGVuIHRoZSBzeXN0ZW0gZGlzcGxheXMgYSBsaXN0IG9mIHF1YWxpdGllcyB0aGV5XG4gKiAgICAgd2lsbCBiZSBzb3J0ZWQgYnkgdGhpcyBzdHJpbmcuIElmIHlvdSBkb24ndCBnaXZlIGFcbiAqICAgICBwcmlvcml0eSwgdGhlbiB0aGUgdGl0bGUgd2lsbCBiZSB1c2VkLCBzbyB5b3UnbGwgZ2V0XG4gKiAgICAgYWxwaGFiZXRpYyBvcmRlci4gTm9ybWFsbHkgeW91IGVpdGhlciBkb24ndCBnaXZlIGFcbiAqICAgICBwcmlvcml0eSwgb3IgZWxzZSB1c2UgYSBwcmlvcml0eSBzdHJpbmcgY29udGFpbmluZyAwLXBhZGRlZFxuICogICAgIG51bWJlcnMgKGUuZy4gXCIwMDAwMVwiKS5cbiAqXG4gKiBncm91cCAtIFRoZSBJZCBvZiBhIGdyb3VwIGluIHdoaWNoIHRvIGRpc3BsYXkgdGhpc1xuICogICAgIHBhcmFtZXRlci4gVGhlIGNvcnJlc3BvbmRpbmcgZ3JvdXAgbXVzdCBiZSBkZWZpbmVkIGluXG4gKiAgICAgeW91ciBgdW5kdW0uZ2FtZS5xdWFsaXR5R3JvdXBzYCBwcm9wZXJ0eS5cbiAqXG4gKiBleHRyYUNsYXNzZXMgLSBUaGVzZSBjbGFzc2VzIHdpbGwgYmUgYXR0YWNoZWQgdG8gdGhlIDxkaXY+IHRhZ1xuICogICAgIHRoYXQgc3Vycm91bmRzIHRoZSBxdWFsaXR5IHdoZW4gaXQgaXMgZGlzcGxheWVkLiBBIGNvbW1vblxuICogICAgIHVzZSBmb3IgdGhpcyBpcyB0byBhZGQgaWNvbnMgcmVwcmVzZW50aW5nIHRoZSBxdWFsaXR5LiBJblxuICogICAgIHlvdXIgQ1NTIGRlZmluZSBhIGNsYXNzIGZvciBlYWNoIGljb24sIHRoZW4gcGFzcyB0aG9zZVxuICogICAgIGNsYXNzZXMgaW50byB0aGUgYXBwcm9wcmlhdGUgcXVhbGl0eSBkZWZpbml0aW9ucy5cbiAqXG4gKiBPbmUga2V5IHB1cnBvc2Ugb2YgUXVhbGl0eURlZmluaXRpb24gaXMgdG8gZm9ybWF0IHRoZSBxdWFsaXR5XG4gKiB2YWx1ZSBmb3IgZGlzcGxheS4gUXVhbGl0eSB2YWx1ZXMgYXJlIGFsd2F5cyBzdG9yZWQgYXMgbnVtZXJpY1xuICogdmFsdWVzLCBidXQgbWF5IGJlIGRpc3BsYXllZCBpbiB3b3JkcyBvciBzeW1ib2xzLiBBIG51bWJlciBvZlxuICogc3ViLXR5cGVzIG9mIFF1YWxpdHlEZWZpbml0aW9uIGFyZSBnaXZlbiB0aGF0IGZvcm1hdCB0aGVpclxuICogdmFsdWVzIGluIGRpZmZlcmVudCB3YXlzLlxuICovXG52YXIgUXVhbGl0eURlZmluaXRpb24gPSBmdW5jdGlvbih0aXRsZSwgb3B0cykge1xuICB2YXIgbXlPcHRzID0gZXh0ZW5kKG9wdHMsIHtcbiAgICBwcmlvcml0eTogdGl0bGUsXG4gICAgZ3JvdXA6IG51bGwsXG4gICAgZXh0cmFDbGFzc2VzOiBudWxsXG4gIH0pO1xuICB0aGlzLnRpdGxlID0gdGl0bGU7XG4gIHRoaXMucHJpb3JpdHkgPSBteU9wdHMucHJpb3JpdHk7XG4gIHRoaXMuZ3JvdXAgPSBteU9wdHMuZ3JvdXA7XG4gIHRoaXMuZXh0cmFDbGFzc2VzID0gbXlPcHRzLmV4dHJhQ2xhc3Nlcztcbn07XG4vKiBGb3JtYXRzIHRoZSB2YWx1ZSAod2hpY2ggaXMgYWx3YXlzIG51bWVyaWMpIGludG8gdGhlIHZhbHVlIHRvXG4gKiBiZSBkaXNwbGF5ZWQuIFRoZSByZXN1bHQgc2hvdWxkIGJlIEhUTUwgKGJ1dCBubyB0YWdzIGFyZVxuICogbmVlZGVkKS4gSWYgbnVsbCBpcyByZXR1cm5lZCwgdGhlbiB0aGUgcXVhbGl0eSBkZWZpbml0aW9uIHdpbGxcbiAqIG5vdCBiZSBkaXNwbGF5ZWQsIHNvIGlmIHlvdSB3YW50IGFuIGVtcHR5IHZhbHVlIHJldHVybiBhbiBlbXB0eVxuICogc3RyaW5nLiAqL1xuUXVhbGl0eURlZmluaXRpb24ucHJvdG90eXBlLmZvcm1hdCA9IGZ1bmN0aW9uKGNoYXJhY3RlciwgdmFsdWUpIHtcbiAgcmV0dXJuIHZhbHVlLnRvU3RyaW5nKCk7XG59O1xuXG4vKiBBIHF1YWxpdHkgdGhhdCBpcyBhbHdheXMgZGlzcGxheWVkIGFzIHRoZSBuZWFyZXN0IGludGVnZXIgb2ZcbiAqIHRoZSBjdXJyZW50IHZhbHVlLCByb3VuZGVkIGRvd24uIE9wdGlvbnMgKGluIHRoZSBvcHRzXG4gKiBwYXJhbWV0ZXIpIGFyZSB0aGUgc2FtZSBhcyBmb3IgUXVhbGl0eURlZmluaXRpb24uICovXG52YXIgSW50ZWdlclF1YWxpdHkgPSBmdW5jdGlvbih0aXRsZSwgb3B0cykge1xuICBRdWFsaXR5RGVmaW5pdGlvbi5jYWxsKHRoaXMsIHRpdGxlLCBvcHRzKTtcbn07XG5JbnRlZ2VyUXVhbGl0eS5pbmhlcml0cyhRdWFsaXR5RGVmaW5pdGlvbik7XG5JbnRlZ2VyUXVhbGl0eS5wcm90b3R5cGUuZm9ybWF0ID0gZnVuY3Rpb24oY2hhcmFjdGVyLCB2YWx1ZSkge1xuICByZXR1cm4gTWF0aC5mbG9vcih2YWx1ZSkudG9TdHJpbmcoKTtcbn07XG5cbi8qIEEgcXVhbGl0eSB0aGF0IGRpc3BsYXlzIGFzIGFuIEludGVnZXJRdWFsaXR5LCB1bmxlc3MgaXQgaXNcbiAqIHplcm8sIHdoZW4gaXQgaXMgb21pdHRlZC4gT3B0aW9ucyAoaW4gdGhlIG9wdHMgKiBwYXJhbWV0ZXIpIGFyZVxuICogdGhlIHNhbWUgYXMgZm9yIFF1YWxpdHlEZWZpbml0aW9uLiAqL1xudmFyIE5vblplcm9JbnRlZ2VyUXVhbGl0eSA9IGZ1bmN0aW9uKHRpdGxlLCBvcHRzKSB7XG4gIEludGVnZXJRdWFsaXR5LmNhbGwodGhpcywgdGl0bGUsIG9wdHMpO1xufTtcbk5vblplcm9JbnRlZ2VyUXVhbGl0eS5pbmhlcml0cyhJbnRlZ2VyUXVhbGl0eSk7XG5Ob25aZXJvSW50ZWdlclF1YWxpdHkucHJvdG90eXBlLmZvcm1hdCA9IGZ1bmN0aW9uKGNoYXJhY3RlciwgdmFsdWUpIHtcbiAgaWYgKHZhbHVlID09PSAwKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIEludGVnZXJRdWFsaXR5LnByb3RvdHlwZS5mb3JtYXQuY2FsbChcbiAgICAgIHRoaXMsIGNoYXJhY3RlciwgdmFsdWVcbiAgICApO1xuICB9XG59O1xuXG4vKiBBIHF1YWxpdHkgdGhhdCBkaXNwbGF5cyBpdHMgZnVsbCBudW1lcmljIHZhbHVlLCBpbmNsdWRpbmdcbiAqIGRlY2ltYWwgY29tcG9uZW50LiBUaGlzIGlzIGFjdHVhbGx5IGEgdHJpdmlhbCB3cmFwcGVyIGFyb3VuZFxuICogdGhlIFF1YWxpdHlEZWZpbml0aW9uIGNsYXNzLCB3aGljaCBmb3JtYXRzIGluIHRoZSBzYW1lXG4gKiB3YXkuIE9wdGlvbnMgKGluIHRoZSBvcHRzIHBhcmFtZXRlcikgYXJlIHRoZSBzYW1lIGFzIGZvclxuICogUXVhbGl0eURlZmluaXRpb24uICovXG52YXIgTnVtZXJpY1F1YWxpdHkgPSBmdW5jdGlvbih0aXRsZSwgb3B0cykge1xuICBRdWFsaXR5RGVmaW5pdGlvbi5jYWxsKHRoaXMsIHRpdGxlLCBvcHRzKTtcbn07XG5OdW1lcmljUXVhbGl0eS5pbmhlcml0cyhRdWFsaXR5RGVmaW5pdGlvbik7XG5cbi8qIEEgcXVhbGl0eSB0aGF0IGRpc3BsYXlzIGl0cyB2YWx1ZXMgYXMgb25lIG9mIGEgc2V0IG9mXG4gKiB3b3Jkcy4gVGhlIHF1YWxpdHkgdmFsdWUgaXMgZmlyc3Qgcm91bmRlZCBkb3duIHRvIHRoZSBuZWFyZXN0XG4gKiBpbnRlZ2VyLCB0aGVuIHRoaXMgdmFsdWUgaXMgdXNlZCB0byBzZWxlY3QgYSB3b3JkIHRvXG4gKiBkaXNwbGF5LiBUaGUgb2Zmc2V0IHBhcmFtZXRlciAob3B0aW9uYWxseSBwYXNzZWQgaW4gYXMgcGFydCBvZlxuICogdGhlIG9wdHMgb2JqZWN0KSBjb250cm9scyB3aGF0IG51bWJlciBtYXBzIHRvIHdoYXQgd29yZC5cbiAqXG4gKiBUaGUgZm9sbG93aW5nIG9wdGlvbnMgKGluIHRoZSBvcHRzIHBhcmFtZXRlcikgYXJlIGF2YWlsYWJsZTpcbiAqXG4gKiBvZmZzZXQgLSBXaXRoIG9mZnNldD0wICh0aGUgZGVmYXVsdCksIHRoZSBxdWFudGl0eSB2YWx1ZSBvZiAwXG4gKiAgICAgd2lsbCBtYXAgdG8gdGhlIGZpcnN0IHdvcmQsIGFuZCBzbyBvbi4gSWYgb2Zmc2V0IGlzXG4gKiAgICAgbm9uLXplcm8gdGhlbiB0aGUgdmFsdWUgZ2l2ZW4gd2lsbCBjb3JyZXNwb25kIHRvIHRoZSBmaXJzdFxuICogICAgIHdvcmQgaW4gdGhlIGxpc3QuIFNvIGlmIG9mZnNldD00LCB0aGVuIHRoZSBmaXJzdCB3b3JkIGluXG4gKiAgICAgdGhlIGxpc3Qgd2lsbCBiZSB1c2VkIGZvciB2YWx1ZT00LlxuICpcbiAqIHVzZUJvbnVzZXMgLSBJZiB0aGlzIGlzIHRydWUgKHRoZSBkZWZhdWx0KSwgdGhlbiB2YWx1ZXMgb3V0c2lkZVxuICogICAgIHRoZSByYW5nZSBvZiB3b3JkcyB3aWxsIGJlIGNvbnN0cnVjZWQgZnJvbSB0aGUgd29yZCBhbmQgYVxuICogICAgIG51bWVyaWMgYm9udXMuIFNvIHdpdGggb2Zmc2V0PTAgYW5kIGZpdmUgd29yZHMsIHRoZSBsYXN0IG9mXG4gKiAgICAgd2hpY2ggaXMgJ2FtYXppbmcnLCBhIHNjb3JlIG9mIHNpeCB3b3VsZCBnaXZlICdhbWF6aW5nKzEnLlxuICogICAgIGlmIHRoaXMgaXMgZmFsc2UsIHRoZW4gdGhlIGJvbnVzIHdvdWxkIGJlIG9taXR0ZWQsIHNvXG4gKiAgICAgYW55dGhpbmcgYmV5b25kICdhbWF6aW5nJyBpcyBzdGlsbCAnYW1hemluZycuXG4gKlxuICogT3RoZXIgb3B0aW9ucyBhcmUgdGhlIHNhbWUgYXMgZm9yIFF1YWxpdHlEZWZpbml0aW9uLlxuICovXG52YXIgV29yZFNjYWxlUXVhbGl0eSA9IGZ1bmN0aW9uKHRpdGxlLCB2YWx1ZXMsIG9wdHMpIHtcbiAgdmFyIG15T3B0cyA9IGV4dGVuZChvcHRzLCB7XG4gICAgb2Zmc2V0OiBudWxsLFxuICAgIHVzZUJvbnVzZXM6IHRydWVcbiAgfSk7XG4gIFF1YWxpdHlEZWZpbml0aW9uLmNhbGwodGhpcywgdGl0bGUsIG9wdHMpO1xuICB0aGlzLnZhbHVlcyA9IHZhbHVlcztcbiAgdGhpcy5vZmZzZXQgPSBteU9wdHMub2Zmc2V0O1xuICB0aGlzLnVzZUJvbnVzZXMgPSBteU9wdHMudXNlQm9udXNlcztcbn07XG5Xb3JkU2NhbGVRdWFsaXR5LmluaGVyaXRzKFF1YWxpdHlEZWZpbml0aW9uKTtcbldvcmRTY2FsZVF1YWxpdHkucHJvdG90eXBlLmZvcm1hdCA9IGZ1bmN0aW9uKGNoYXJhY3RlciwgdmFsdWUpIHtcbiAgdmFyIHZhbCA9IE1hdGguZmxvb3IodmFsdWUgLSB0aGlzLm9mZnNldCk7XG4gIHZhciBtb2QgPSBcIlwiO1xuICBpZiAodmFsIDwgMCkge1xuICAgIG1vZCA9IHZhbC50b1N0cmluZygpO1xuICAgIHZhbCA9IDA7XG4gIH0gZWxzZSBpZiAodmFsID49IHRoaXMudmFsdWVzLmxlbmd0aCkge1xuICAgIG1vZCA9IFwiK1wiICsgKHZhbCAtIHRoaXMudmFsdWVzLmxlbmd0aCArIDEpLnRvU3RyaW5nKCk7XG4gICAgdmFsID0gdGhpcy52YWx1ZXMubGVuZ3RoIC0gMTtcbiAgfVxuICBpZiAoIXRoaXMudXNlQm9udXNlcykgbW9kID0gXCJcIjtcbiAgaWYgKHRoaXMudmFsdWVzW3ZhbF0gPT09IG51bGwpIHJldHVybiBudWxsO1xuICByZXR1cm4gdGhpcy52YWx1ZXNbdmFsXSArIG1vZDsgLy8gVHlwZSBjb2VyY2lvblxufTtcblxuLyogQSBzcGVjaWFsaXphdGlvbiBvZiBXb3JkU2NhbGVRdWFsaXR5IHRoYXQgdXNlcyB0aGUgRlVER0UgUlBHJ3NcbiAqIGFkamVjdGl2ZSBzY2FsZSAoZnJvbSAndGVycmlibGUnIGF0IC0zIHRvICdzdXBlcmInIGF0ICszKS4gVGhlXG4gKiBvcHRpb25zIGFyZSBhcyBmb3IgV29yZFNjYWxlUXVhbGl0eS4gSW4gcGFydGljdWxhciB5b3UgY2FuIHVzZVxuICogdGhlIG9mZnNldCBvcHRpb24gdG8gY29udHJvbCB3aGVyZSB0aGUgc2NhbGUgc3RhcnRzLiBTbyB5b3VcbiAqIGNvdWxkIG1vZGVsIGEgcXVhbGl0eSB0aGF0IGV2ZXJ5b25lIHN0YXJ0cyBvZmYgYXMgJ3RlcnJpYmxlJ1xuICogKHN1Y2ggYXMgTnVjbGVhciBQaHlzaWNzKSB3aXRoIGFuIG9mZnNldCBvZiAwLCB3aGlsZSBhbm90aGVyIHRoYXRcbiAqIGlzIG1vcmUgY29tbW9uIChzdWNoIGFzIEhlYWx0aCkgY291bGQgaGF2ZSBhbiBvZmZzZXQgb2YgLTUgc29cbiAqIGV2ZXJ5b25lIHN0YXJ0cyB3aXRoICdncmVhdCcuXG4gKi9cbnZhciBGdWRnZUFkamVjdGl2ZXNRdWFsaXR5ID0gZnVuY3Rpb24odGl0bGUsIG9wdHMpIHtcbiAgV29yZFNjYWxlUXVhbGl0eS5jYWxsKHRoaXMsIHRpdGxlLCBbXG4gICAgXCJ0ZXJyaWJsZVwiLmwoKSxcbiAgICBcInBvb3JcIi5sKCksXG4gICAgXCJtZWRpb2NyZVwiLmwoKSxcbiAgICBcImZhaXJcIi5sKCksXG4gICAgXCJnb29kXCIubCgpLFxuICAgIFwiZ3JlYXRcIi5sKCksXG4gICAgXCJzdXBlcmJcIi5sKClcbiAgXSwgb3B0cyk7XG4gIGlmICghKCdvZmZzZXQnIGluIG9wdHMpKSB0aGlzLm9mZnNldCA9IC0zO1xufTtcbkZ1ZGdlQWRqZWN0aXZlc1F1YWxpdHkuaW5oZXJpdHMoV29yZFNjYWxlUXVhbGl0eSk7XG5cbi8qIEFuIGJvb2xlYW4gcXVhbGl0eSB0aGF0IHJlbW92ZXMgaXRzZWxmIGZyb20gdGhlIHF1YWxpdHkgbGlzdCBpZlxuICogaXQgaGFzIGEgemVybyB2YWx1ZS4gSWYgaXQgaGFzIGEgbm9uLXplcm8gdmFsdWUsIGl0cyB2YWx1ZVxuICogZmllbGQgaXMgdXN1YWxseSBsZWZ0IGVtcHR5LCBidXQgeW91IGNhbiBzcGVjaWZ5IHlvdXIgb3duXG4gKiBzdHJpbmcgdG8gZGlzcGxheSBhcyB0aGUgYG9uRGlzcGxheWAgcGFyYW1ldGVyIG9mIHRoZSBvcHRzXG4gKiBvYmplY3QuIE90aGVyIG9wdGlvbnMgKGluIHRoZSBvcHRzIHBhcmFtZXRlcikgYXJlIHRoZSBzYW1lIGFzXG4gKiBmb3IgUXVhbGl0eURlZmluaXRpb24uICovXG52YXIgT25PZmZRdWFsaXR5ID0gZnVuY3Rpb24odGl0bGUsIG9wdHMpIHtcbiAgdmFyIG15T3B0cyA9IGV4dGVuZChvcHRzLCB7XG4gICAgb25EaXNwbGF5OiBcIlwiXG4gIH0pO1xuICBRdWFsaXR5RGVmaW5pdGlvbi5jYWxsKHRoaXMsIHRpdGxlLCBvcHRzKTtcbiAgdGhpcy5vbkRpc3BsYXkgPSBteU9wdHMub25EaXNwbGF5O1xufTtcbk9uT2ZmUXVhbGl0eS5pbmhlcml0cyhRdWFsaXR5RGVmaW5pdGlvbik7XG5Pbk9mZlF1YWxpdHkucHJvdG90eXBlLmZvcm1hdCA9IGZ1bmN0aW9uKGNoYXJhY3RlciwgdmFsdWUpIHtcbiAgaWYgKHZhbHVlKSByZXR1cm4gdGhpcy5vbkRpc3BsYXk7XG4gIGVsc2UgcmV0dXJuIG51bGw7XG59O1xuXG4vKiBBIGJvb2xlYW4gcXVhbGl0eSB0aGF0IGhhcyBkaWZmZXJlbnQgb3V0cHV0IHRleHQgZm9yIHplcm8gb3JcbiAqIG5vbi16ZXJvIHF1YWxpdHkgdmFsdWVzLiBVbmxpa2UgT25PZmZRdWFsaXR5LCB0aGlzIGRlZmluaXRpb25cbiAqIGRvZXNuJ3QgcmVtb3ZlIGl0c2VsZiBmcm9tIHRoZSBsaXN0IHdoZW4gaXQgaXMgMC4gVGhlIG9wdGlvbnNcbiAqIGFyZSBhcyBmb3IgUXVhbGl0eURlZmluaXRpb24sIHdpdGggdGhlIGFkZGl0aW9uIG9mIG9wdGlvbnNcbiAqICd5ZXNEaXNwbGF5JyBhbmQgJ25vRGlzcGxheScsIHdoaWNoIGNvbnRhaW4gdGhlIEhUTUwgZnJhZ21lbnRzXG4gKiB1c2VkIHRvIGRpc3BsYXkgdHJ1ZSBhbmQgZmFsc2UgdmFsdWVzLiBJZiBub3QgZ2l2ZW4sIHRoZXNlXG4gKiBkZWZhdWx0IHRvICd5ZXMnIGFuZCAnbm8nLlxuICovXG52YXIgWWVzTm9RdWFsaXR5ID0gZnVuY3Rpb24odGl0bGUsIG9wdHMpIHtcbiAgdmFyIG15T3B0cyA9IGV4dGVuZChvcHRzLHtcbiAgICB5ZXNEaXNwbGF5OiBcInllc1wiLmwoKSxcbiAgICBub0Rpc3BsYXk6IFwibm9cIi5sKClcbiAgfSk7XG4gIFF1YWxpdHlEZWZpbml0aW9uLmNhbGwodGhpcywgdGl0bGUsIG9wdHMpO1xuICB0aGlzLnllc0Rpc3BsYXkgPSBteU9wdHMueWVzRGlzcGxheTtcbiAgdGhpcy5ub0Rpc3BsYXkgPSBteU9wdHMubm9EaXNwbGF5O1xufTtcblllc05vUXVhbGl0eS5pbmhlcml0cyhRdWFsaXR5RGVmaW5pdGlvbik7XG5ZZXNOb1F1YWxpdHkucHJvdG90eXBlLmZvcm1hdCA9IGZ1bmN0aW9uKGNoYXJhY3RlciwgdmFsdWUpIHtcbiAgaWYgKHZhbHVlKSByZXR1cm4gdGhpcy55ZXNEaXNwbGF5O1xuICBlbHNlIHJldHVybiB0aGlzLm5vRGlzcGxheTtcbn07XG5cbi8qIERlZmluZXMgYSBncm91cCBvZiBxdWFsaXRpZXMgdGhhdCBzaG91bGQgYmUgZGlzcGxheWVkIHRvZ2V0aGVyLFxuICogdW5kZXIgdGhlIGdpdmVuIG9wdGlvbmFsIHRpdGxlLiBUaGVzZSBzaG91bGQgYmUgZGVmaW5lZCBpbiB0aGVcbiAqIGB1bmR1bS5nYW1lLnF1YWxpdHlHcm91cHNgIHBhcmFtZXRlci4gKi9cbnZhciBRdWFsaXR5R3JvdXAgPSBmdW5jdGlvbih0aXRsZSwgb3B0cykge1xuICB2YXIgbXlPcHRzID0gZXh0ZW5kKG9wdHMse1xuICAgIHByaW9yaXR5OiB0aXRsZSxcbiAgICBleHRyYUNsYXNzZXM6IG51bGxcbiAgfSk7XG4gIHRoaXMudGl0bGUgPSB0aXRsZTtcbiAgdGhpcy5wcmlvcml0eSA9IG15T3B0cy5wcmlvcml0eTtcbiAgdGhpcy5leHRyYUNsYXNzZXMgPSBteU9wdHMuZXh0cmFDbGFzc2VzO1xufTtcbiIsIi8qIEEgc3lzdGVtIG9iamVjdCBpcyBwYXNzZWQgaW50byB0aGUgZW50ZXIsIGFjdCBhbmQgZXhpdFxuICogZnVuY3Rpb25zIG9mIGVhY2ggc2l0dWF0aW9uLiBJdCBpcyB1c2VkIHRvIGludGVyYWN0IHdpdGggdGhlXG4gKiBVSS5cbiAqL1xudmFyIFN5c3RlbSA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnJuZCA9IG51bGw7XG4gIHRoaXMudGltZSA9IDA7XG59O1xuXG4vKiBSZW1vdmVzIGFsbCBjb250ZW50IGZyb20gdGhlIHBhZ2UsIGNsZWFyaW5nIHRoZSBtYWluIGNvbnRlbnQgYXJlYS5cbiAqXG4gKiBJZiBhbiBlbGVtZW50U2VsZWN0b3IgaXMgZ2l2ZW4sIHRoZW4gb25seSB0aGF0IHNlbGVjdG9yIHdpbGwgYmVcbiAqIGNsZWFyZWQuIE5vdGUgdGhhdCBhbGwgY29udGVudCBmcm9tIHRoZSBjbGVhcmVkIGVsZW1lbnQgaXMgcmVtb3ZlZCxcbiAqIGJ1dCB0aGUgZWxlbWVudCBpdHNlbGYgcmVtYWlucywgcmVhZHkgdG8gYmUgZmlsbGVkIGFnYWluIHVzaW5nXG4gKiBTeXN0ZW0ud3JpdGUuXG4gKi9cblN5c3RlbS5wcm90b3R5cGUuY2xlYXJDb250ZW50ID0gZnVuY3Rpb24oZWxlbWVudFNlbGVjdG9yKSB7XG4gIHZhciAkZWxlbWVudDtcbiAgaWYgKGVsZW1lbnRTZWxlY3RvcikgJGVsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKGVsZW1lbnRTZWxlY3Rvcik7XG4gIGlmICghJGVsZW1lbnQpICRlbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJjb250ZW50XCIpO1xuICAkZWxlbWVudC5pbm5lckhUTUwgPSAnJztcbn07XG5cbi8qIE91dHB1dHMgcmVndWxhciBjb250ZW50IHRvIHRoZSBwYWdlLiBUaGUgY29udGVudCBzdXBwbGllZCBtdXN0XG4gKiBiZSB2YWxpZCBcIkRpc3BsYXkgQ29udGVudFwiLlxuICpcbiAqIFwiRGlzcGxheSBDb250ZW50XCIgaXMgYW55IEhUTUwgc3RyaW5nIHRoYXQgYmVnaW5zIHdpdGggYSBIVE1MXG4gKiBzdGFydCB0YWcsIGVuZHMgd2l0aCBlaXRoZXIgYW4gZW5kIG9yIGEgY2xvc2VkIHRhZywgYW5kIGlzIGFcbiAqIHZhbGlkIGFuZCBzZWxmLWNvbnRhaW5lZCBzbmlwcGV0IG9mIEhUTUwuIE5vdGUgdGhhdCB0aGUgc3RyaW5nXG4gKiBkb2Vzbid0IGhhdmUgdG8gY29uc2lzdCBvZiBvbmx5IG9uZSBIVE1MIHRhZy4gWW91IGNvdWxkIGhhdmVcbiAqIHNldmVyYWwgcGFyYWdyYXBocywgZm9yIGV4YW1wbGUsIGFzIGxvbmcgYXMgdGhlIGNvbnRlbnQgc3RhcnRzXG4gKiB3aXRoIHRoZSA8cD4gb2YgdGhlIGZpcnN0IHBhcmFncmFwaCwgYW5kIGVuZHMgd2l0aCB0aGUgPC9wPiBvZlxuICogdGhlIGxhc3QuIFNvIFwiPHA+Rm9vPC9wPjxpbWcgc3JjPSdiYXInPlwiIGlzIHZhbGlkLCBidXQgXCJmb288aW1nXG4gKiBzcmM9J2Jhcic+XCIgaXMgbm90LlxuICpcbiAqIFRoZSBjb250ZW50IGdvZXMgdG8gdGhlIGVuZCBvZiB0aGUgcGFnZSwgdW5sZXNzIHlvdSBzdXBwbHkgdGhlXG4gKiBvcHRpb25hbCBzZWxlY3RvciBhcmd1bWVudC4gSWYgeW91IGRvLCB0aGUgY29udGVudCBhcHBlYXJzXG4gKiBhZnRlciB0aGUgZWxlbWVudCB0aGF0IG1hdGNoZXMgdGhhdCBzZWxlY3Rvci5cbiAqL1xuU3lzdGVtLnByb3RvdHlwZS53cml0ZSA9IGZ1bmN0aW9uKGNvbnRlbnQsIGVsZW1lbnRTZWxlY3Rvcikge1xuICBkb1dyaXRlKGNvbnRlbnQsIGVsZW1lbnRTZWxlY3Rvcik7XG59O1xuXG4vKiBPdXRwdXRzIHRoZSBnaXZlbiBjb250ZW50IGluIGEgaGVhZGluZyBvbiB0aGUgcGFnZS4gVGhlIGNvbnRlbnRcbiAqIHN1cHBsaWVkIG11c3QgYmUgdmFsaWQgXCJEaXNwbGF5IENvbnRlbnRcIi5cbiAqXG4gKiBUaGUgY29udGVudCBnb2VzIHRvIHRoZSBlbmQgb2YgdGhlIHBhZ2UsIHVubGVzcyB5b3Ugc3VwcGx5IHRoZVxuICogb3B0aW9uYWwgc2VsZWN0b3IgYXJndW1lbnQuIElmIHlvdSBkbywgdGhlIGNvbnRlbnQgYXBwZWFyc1xuICogYWZ0ZXIgdGhlIGVsZW1lbnQgdGhhdCBtYXRjaGVzIHRoYXQgc2VsZWN0b3IuXG4gKi9cblN5c3RlbS5wcm90b3R5cGUud3JpdGVIZWFkaW5nID0gZnVuY3Rpb24oaGVhZGluZ0NvbnRlbnQsIGVsZW1lbnRTZWxlY3Rvcikge1xuICB2YXIgaGVhZGluZyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCI8aDE+XCIpO1xuICBoZWFkaW5nLmlubmVySFRNTCA9IGhlYWRpbmdDb250ZW50O1xuICBkb1dyaXRlKGhlYWRpbmcsIGVsZW1lbnRTZWxlY3Rvcik7XG59O1xuXG4vKiBPdXRwdXRzIHJlZ3VsYXIgY29udGVudCB0byB0aGUgcGFnZS4gVGhlIGNvbnRlbnQgc3VwcGxpZWQgbXVzdFxuICogYmUgdmFsaWQgXCJEaXNwbGF5IENvbnRlbnRcIi5cbiAqXG4gKiBUaGUgY29udGVudCBnb2VzIHRvIHRoZSBiZWdpbm5pbmcgb2YgdGhlIHBhZ2UsIHVubGVzcyB5b3VcbiAqIHN1cHBseSB0aGUgb3B0aW9uYWwgc2VsZWN0b3IgYXJndW1lbnQuIElmIHlvdSBkbywgdGhlIGNvbnRlbnRcbiAqIGFwcGVhcnMgYWZ0ZXIgdGhlIGVsZW1lbnQgdGhhdCBtYXRjaGVzIHRoYXQgc2VsZWN0b3IuXG4gKi9cblN5c3RlbS5wcm90b3R5cGUud3JpdGVCZWZvcmUgPSBmdW5jdGlvbihjb250ZW50LCBlbGVtZW50U2VsZWN0b3IpIHtcbiAgZG9Xcml0ZShjb250ZW50LCBlbGVtZW50U2VsZWN0b3IsICdwcmVwZW5kJywgJ2JlZm9yZScpO1xufTtcblxuLyogT3V0cHV0cyByZWd1bGFyIGNvbnRlbnQgdG8gdGhlIHBhZ2UuIFRoZSBjb250ZW50IHN1cHBsaWVkIG11c3RcbiAqIGJlIHZhbGlkIFwiRGlzcGxheSBDb250ZW50XCIuXG4gKlxuICogV2hlbiBhIHNlbGVjdG9yIGlzIG5vdCBzcGVjaWZpZWQsIHRoaXMgYmVoYXZlcyBpZGVudGljYWxseSB0b1xuICogU3lzdGVtLnByb3RvdHlwZS53cml0ZS4gSWYgeW91IHN1cHBseSBhIHNlbGVjdG9yLCB0aGUgY29udGVudFxuICogYXBwZWFycyBhcyBhIGNoaWxkIG5vZGUgYXQgdGhlIGVuZCBvZiB0aGUgY29udGVudCBvZiB0aGVcbiAqIGVsZW1lbnQgdGhhdCBtYXRjaGVzIHRoYXQgc2VsZWN0b3IuXG4gKi9cblxuU3lzdGVtLnByb3RvdHlwZS53cml0ZUludG8gPSBmdW5jdGlvbihjb250ZW50LCBlbGVtZW50U2VsZWN0b3IpIHtcbiAgZG9Xcml0ZShjb250ZW50LCBlbGVtZW50U2VsZWN0b3IsICdhcHBlbmQnLCAnYXBwZW5kJyk7XG59O1xuXG4vKiBSZXBsYWNlcyBjb250ZW50IHdpdGggdGhlIGNvbnRlbnQgc3VwcGxpZWQsIHdoaWNoIG11c3QgYmUgdmFsaWRcbiAqIFwiRGlzcGxheSBDb250ZW50XCIuXG4gKlxuICogV2hlbiBhIHNlbGVjdG9yIGlzIG5vdCBzcGVjaWZpZWQsIHRoaXMgcmVwbGFjZXMgdGhlIGVudGlyZVxuICogY29udGVudCBvZiB0aGUgcGFnZS4gT3RoZXJ3aXNlLCBpdCByZXBsYWNlcyB0aGUgZWxlbWVudCBtYXRjaGVkXG4gKiB3aXRoIHRoZSBzZWxlY3Rvci4gVGhpcyByZXBsYWNlcyB0aGUgZW50aXJlIGVsZW1lbnQsIGluY2x1ZGluZ1xuICogdGhlIG1hdGNoZWQgdGFncywgc28gaWRlYWxseSB0aGUgY29udGVudCBzdXBwbGllZCBzaG91bGQgZml0XG4gKiBpbiBpdHMgcGxhY2UgaW4gdGhlIERPTSB3aXRoIHRoZSBzYW1lIGtpbmQgb2YgZGlzcGxheSBlbGVtZW50LlxuICovXG5cblN5c3RlbS5wcm90b3R5cGUucmVwbGFjZVdpdGggPSBmdW5jdGlvbihjb250ZW50LCBlbGVtZW50U2VsZWN0b3IpIHtcbiAgZG9Xcml0ZShjb250ZW50LCBlbGVtZW50U2VsZWN0b3IsICdyZXBsYWNlV2l0aCcsICdyZXBsYWNlV2l0aCcpO1xufTtcblxuLyogQ2FycmllcyBvdXQgdGhlIGdpdmVuIHNpdHVhdGlvbiBjaGFuZ2Ugb3IgYWN0aW9uLCBhcyBpZiBpdCB3ZXJlXG4gKiBpbiBhIGxpbmsgdGhhdCBoYXMgYmVlbiBjbGlja2VkLiBUaGlzIGFsbG93cyB5b3UgdG8gZG9cbiAqIHByb2NlZHVyYWwgdHJhbnNpdGlvbnMuIFlvdSBtaWdodCBoYXZlIGFuIGFjdGlvbiB0aGF0IGJ1aWxkcyB1cFxuICogdGhlIGNoYXJhY3RlcidzIHN0cmVuZ3RoLCBhbmQgZGVwbGV0ZXMgdGhlaXIgbWFnaWMuIFdoZW4gdGhlXG4gKiBtYWdpYyBpcyBhbGwgZ29uZSwgeW91IGNhbiBmb3JjZSBhIHNpdHVhdGlvbiBjaGFuZ2UgYnkgY2FsbGluZ1xuICogdGhpcyBtZXRob2QuICovXG5TeXN0ZW0ucHJvdG90eXBlLmRvTGluayA9IGZ1bmN0aW9uKGNvZGUpIHtcbiAgcHJvY2Vzc0xpbmsoY29kZSk7XG59O1xuXG4vKiBUdXJucyBhbnkgbGlua3MgdGhhdCB0YXJnZXQgdGhlIGdpdmVuIGhyZWYgaW50byBwbGFpblxuICogdGV4dC4gVGhpcyBjYW4gYmUgdXNlZCB0byByZW1vdmUgYWN0aW9uIG9wdGlvbnMgd2hlbiBhbiBhY3Rpb25cbiAqIGlzIG5vIGxvbmdlciBhdmFpbGFibGUuIEl0IGlzIHVzZWQgYXV0b21hdGljYWxseSB3aGVuIHlvdSBnaXZlXG4gKiBhIGxpbmsgdGhlICdvbmNlJyBjbGFzcy4gKi9cblN5c3RlbS5wcm90b3R5cGUuY2xlYXJMaW5rcyA9IGZ1bmN0aW9uKGNvZGUpIHtcbiAgdmFyIGxpbmtzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbChcImFbaHJlZj0nXCIgKyBjb2RlICsgXCInXVwiKTtcbiAgQXJyYXkucHJvdG90eXBlLmZvckVhY2guY2FsbChsaW5rcywgZnVuY3Rpb24oZWxlbWVudCwgaW5kZXgpe1xuICAgIGVsZW1lbnQucXVlcnlTZWxlY3RvckFsbChcInNwYW5cIikuY2xhc3NMaXN0LmFkZChcImV4X2xpbmtcIik7XG4gIH0pO1xufTtcblxuLyogR2l2ZW4gYSBsaXN0IG9mIHNpdHVhdGlvbiBpZHMsIHRoaXMgb3V0cHV0cyBhIHN0YW5kYXJkIG9wdGlvblxuICogYmxvY2sgd2l0aCB0aGUgc2l0dWF0aW9uIGNob2ljZXMgaW4gdGhlIGdpdmVuIG9yZGVyLlxuICpcbiAqIFRoZSBjb250ZW50cyBvZiBlYWNoIGNob2ljZSB3aWxsIGJlIGEgbGluayB0byB0aGUgc2l0dWF0aW9uLFxuICogdGhlIHRleHQgb2YgdGhlIGxpbmsgd2lsbCBiZSBnaXZlbiBieSB0aGUgc2l0dWF0aW9uJ3NcbiAqIG91dHB1dFRleHQgcHJvcGVydHkuIE5vdGUgdGhhdCB0aGUgY2FuQ2hvb3NlIGZ1bmN0aW9uIGlzXG4gKiBjYWxsZWQsIGFuZCBpZiBpdCByZXR1cm5zIGZhbHNlLCB0aGVuIHRoZSB0ZXh0IHdpbGwgYXBwZWFyLCBidXRcbiAqIHRoZSBsaW5rIHdpbGwgbm90IGJlIGNsaWNrYWJsZS5cbiAqXG4gKiBBbHRob3VnaCBjYW5DaG9vc2UgaXMgaG9ub3JlZCwgY2FuVmlldyBhbmQgZGlzcGxheU9yZGVyIGFyZVxuICogbm90LiBJZiB5b3UgbmVlZCB0byBob25vciB0aGVzZSwgeW91IHNob3VsZCBlaXRoZXIgZG8gc29cbiAqIG1hbnVhbGx5LCBvdCBlbHNlIHVzZSB0aGUgYGdldFNpdHVhdGlvbklkQ2hvaWNlc2AgbWV0aG9kIHRvXG4gKiByZXR1cm4gYW4gb3JkZXJlZCBsaXN0IG9mIHZhbGlkIHZpZXdhYmxlIHNpdHVhdGlvbiBpZHMuXG4gKi9cblN5c3RlbS5wcm90b3R5cGUud3JpdGVDaG9pY2VzID0gZnVuY3Rpb24obGlzdE9mSWRzLCBlbGVtZW50U2VsZWN0b3IpIHtcbiAgaWYgKGxpc3RPZklkcy5sZW5ndGggPT09IDApIHJldHVybjtcblxuICB2YXIgY3VycmVudFNpdHVhdGlvbiA9IGdldEN1cnJlbnRTaXR1YXRpb24oKTtcbiAgdmFyICRvcHRpb25zID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJjb250ZW50XCIpLnF1ZXJ5U2VsZWN0b3JBbGwoXCJ1bFwiKS5jbGFzc0xpc3QuYWRkKFwib3B0aW9uc1wiKTtcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBsaXN0T2ZJZHMubGVuZ3RoOyArK2kpIHtcbiAgICB2YXIgc2l0dWF0aW9uSWQgPSBsaXN0T2ZJZHNbaV07XG4gICAgdmFyIHNpdHVhdGlvbiA9IGdhbWUuc2l0dWF0aW9uc1tzaXR1YXRpb25JZF07XG4gICAgYXNzZXJ0KHNpdHVhdGlvbiwgXCJ1bmtub3duX3NpdHVhdGlvblwiLmwoe2lkOnNpdHVhdGlvbklkfSkpO1xuXG4gICAgdmFyIG9wdGlvblRleHQgPSBzaXR1YXRpb24ub3B0aW9uVGV4dChjaGFyYWN0ZXIsIHRoaXMsXG4gICAgICAgIGN1cnJlbnRTaXR1YXRpb24pO1xuICAgIGlmICghb3B0aW9uVGV4dCkgb3B0aW9uVGV4dCA9IFwiY2hvaWNlXCIubCh7bnVtYmVyOmkrMX0pO1xuICAgIHZhciAkb3B0aW9uID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJjb250ZW50XCIpLnF1ZXJ5U2VsZWN0b3JBbGwoXCJsaVwiKTtcbiAgICB2YXIgJGE7XG4gICAgaWYgKHNpdHVhdGlvbi5jYW5DaG9vc2UoY2hhcmFjdGVyLCB0aGlzLCBjdXJyZW50U2l0dWF0aW9uKSkge1xuICAgICAgJGEgPSBcIjxhIGhyZWY9J1wiK3NpdHVhdGlvbklkK1wiJz5cIitvcHRpb25UZXh0K1wiPC9hPlwiXG4gICAgfSBlbHNlIHtcbiAgICAgICRhID0gXCI8c3Bhbj5cIitvcHRpb25UZXh0K1wiPC9zcGFuPlwiO1xuICAgIH1cbiAgICAkb3B0aW9uLmlubmVySFRNTCA9ICRhO1xuICAgICRvcHRpb25zLmFwcGVuZENoaWxkKCRvcHRpb24pO1xuICB9XG4gIGRvV3JpdGUoJG9wdGlvbnMsIGVsZW1lbnRTZWxlY3RvciwgJ2FwcGVuZCcsICdhZnRlcicpO1xufTtcblxuLyogUmV0dXJucyBhIGxpc3Qgb2Ygc2l0dWF0aW9uIGlkcyB0byBjaG9vc2UgZnJvbSwgZ2l2ZW4gYSBzZXQgb2ZcbiAqIHNwZWNpZmljYXRpb25zLlxuICpcbiAqIFRoaXMgZnVuY3Rpb24gaXMgYSBjb21wbGV4IGFuZCBwb3dlcmZ1bCB3YXkgb2YgY29tcGlsaW5nXG4gKiBpbXBsaWNpdCBzaXR1YXRpb24gY2hvaWNlcy4gWW91IGdpdmUgaXQgYSBsaXN0IG9mIHNpdHVhdGlvbiBpZHNcbiAqIGFuZCBzaXR1YXRpb24gdGFncyAoaWYgYSBzaW5nbGUgaWQgb3IgdGFnIGlzIG5lZWRlZCBqdXN0IHRoYXRcbiAqIHN0cmluZyBjYW4gYmUgZ2l2ZW4sIGl0IGRvZXNuJ3QgbmVlZCB0byBiZSB3cmFwcGVkIGluIGFcbiAqIGxpc3QpLiBUYWdzIHNob3VsZCBiZSBwcmVmaXhlZCB3aXRoIGEgaGFzaCAjIHRvIGRpZmZlcmVudGlhdGVcbiAqIHRoZW0gZnJvbSBzaXR1YXRpb24gaWRzLiBUaGUgZnVuY3Rpb24gdGhlbiBjb25zaWRlcnMgYWxsXG4gKiBtYXRjaGluZyBzaXR1YXRpb25zIGluIGRlc2NlbmRpbmcgcHJpb3JpdHkgb3JkZXIsIGNhbGxpbmcgdGhlaXJcbiAqIGNhblZpZXcgZnVuY3Rpb25zIGFuZCBmaWx0ZXJpbmcgb3V0IGFueSB0aGF0IHNob3VsZCBub3QgYmVcbiAqIHNob3duLCBnaXZlbiB0aGUgY3VycmVudCBzdGF0ZS4gV2l0aG91dCBhZGRpdGlvbmFsIHBhcmFtZXRlcnNcbiAqIHRoZSBmdW5jdGlvbiByZXR1cm5zIGEgbGlzdCBvZiB0aGUgc2l0dWF0aW9uIGlkcyBhdCB0aGUgaGlnaGVzdFxuICogbGV2ZWwgb2YgcHJpb3JpdHkgdGhhdCBoYXMgYW55IHZhbGlkIHJlc3VsdHMuIFNvLCBmb3IgZXhhbXBsZSxcbiAqIGlmIGEgdGFnICNwbGFjZXMgbWF0Y2hlcyB0aHJlZSBzaXR1YXRpb25zLCBvbmUgd2l0aCBwcmlvcml0eSAyLFxuICogYW5kIHR3byB3aXRoIHByaW9yaXR5IDMsIGFuZCBhbGwgb2YgdGhlbSBjYW4gYmUgdmlld2VkIGluIHRoZVxuICogY3VycmVudCBjb250ZXh0LCB0aGVuIG9ubHkgdGhlIHR3byB3aXRoIHByaW9yaXR5IDMgd2lsbCBiZVxuICogcmV0dXJuZWQuIFRoaXMgYWxsb3dzIHlvdSB0byBoYXZlIGhpZ2gtcHJpb3JpdHkgc2l0dWF0aW9ucyB0aGF0XG4gKiB0cnVtcCBhbnkgbG93ZXIgc2l0dWF0aW9ucyB3aGVuIHRoZXkgYXJlIHZhbGlkLCBzdWNoIGFzXG4gKiBzaXR1YXRpb25zIHRoYXQgZm9yY2UgdGhlIHBsYXllciB0byBnbyB0byBvbmUgZGVzdGluYXRpb24gaWZcbiAqIHRoZSBwbGF5ZXIgaXMgb3V0IG9mIG1vbmV5LCBmb3IgZXhhbXBsZS5cbiAqXG4gKiBJZiBhIG1pbkNob2ljZXMgdmFsdWUgaXMgZ2l2ZW4sIHRoZW4gdGhlIGZ1bmN0aW9uIHdpbGwgYXR0ZW1wdFxuICogdG8gcmV0dXJuIGF0IGxlYXN0IHRoYXQgbWFueSByZXN1bHRzLiBJZiBub3QgZW5vdWdoIHJlc3VsdHMgYXJlXG4gKiBhdmFpbGFibGUgYXQgdGhlIGhpZ2hlc3QgcHJpb3JpdHksIHRoZW4gbG93ZXIgcHJpb3JpdGllcyB3aWxsXG4gKiBiZSBjb25zaWRlcmVkIGluIHR1cm4sIHVudGlsIGVub3VnaCBzaXR1YXRpb25zIGFyZSBmb3VuZC4gSW5cbiAqIHRoZSBleGFtcGxlIGFib3ZlLCBpZiB3ZSBoYWQgYSBtaW5DaG9pY2VzIG9mIHRocmVlLCB0aGVuIGFsbFxuICogdGhyZWUgc2l0dWF0aW9ucyB3b3VsZCBiZSByZXR1cm5lZCwgZXZlbiB0aG91Z2ggdGhleSBoYXZlXG4gKiBkaWZmZXJlbnQgcHJpb3JpdGllcy4gSWYgeW91IG5lZWQgdG8gcmV0dXJuIGFsbCB2YWxpZFxuICogc2l0dWF0aW9ucywgcmVnYXJkbGVzcyBvZiB0aGVpciBwcmlvcml0aWVzLCBzZXQgbWluQ2hvaWNlcyB0byBhXG4gKiBsYXJnZSBudW1iZXIsIHN1Y2ggYXMgYE51bWJlci5NQVhfVkFMVUVgLCBhbmQgbGVhdmUgbWF4Q2hvaWNlc1xuICogdW5kZWZpbmVkLlxuICpcbiAqIElmIGEgbWF4Q2hvaWNlcyB2YWx1ZSBpcyBnaXZlbiwgdGhlbiB0aGUgZnVuY3Rpb24gd2lsbCBub3RcbiAqIHJldHVybiBhbnkgbW9yZSB0aGFuIHRoZSBnaXZlbiBudW1iZXIgb2YgcmVzdWx0cy4gSWYgdGhlcmUgYXJlXG4gKiBtb3JlIHRoYW4gdGhpcyBudW1iZXIgb2YgcmVzdWx0cyBwb3NzaWJsZSwgdGhlbiB0aGUgaGlnaGVzdFxuICogcHJpb3JpdHkgcmVzdWxzIHdpbGwgYmUgZ3VhcmFudGVlZCB0byBiZSByZXR1cm5lZCwgYnV0IHRoZVxuICogbG93ZXN0IHByaW9yaXR5IGdyb3VwIHdpbGwgaGF2ZSB0byBmaWdodCBpdCBvdXQgZm9yIHRoZVxuICogcmVtYWluaW5nIHBsYWNlcy4gSW4gdGhpcyBjYXNlLCBhIHJhbmRvbSBzYW1wbGUgaXMgY2hvc2VuLFxuICogdGFraW5nIGludG8gYWNjb3VudCB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggc2l0dWF0aW9uLiBTbyBhXG4gKiBzaXR1YXRpb24gd2l0aCBhIGZyZXF1ZW5jeSBvZiAxMDAgd2lsbCBiZSBjaG9zZW4gMTAwIHRpbWVzIG1vcmVcbiAqIG9mdGVuIHRoYW4gYSBzaXR1YXRpb24gd2l0aCBhIGZyZXF1ZW5jeSBvZiAxLCBpZiB0aGVyZSBpcyBvbmVcbiAqIHNwYWNlIGF2YWlsYWJsZS4gT2Z0ZW4gdGhlc2UgZnJlcXVlbmNpZXMgaGF2ZSB0byBiZSB0YWtlbiBhcyBhXG4gKiBndWlkZWxpbmUsIGFuZCB0aGUgYWN0dWFsIHByb2JhYmlsaXRpZXMgd2lsbCBvbmx5IGJlXG4gKiBhcHByb3hpbWF0ZS4gQ29uc2lkZXIgdGhyZWUgc2l0dWF0aW9ucyB3aXRoIGZyZXF1ZW5jaWVzIG9mIDEsXG4gKiAxLCAxMDAsIGNvbXBldGluZyBmb3IgdHdvIHNwYWNlcy4gVGhlIDEwMC1mcmVxdWVuY3kgc2l0dWF0aW9uXG4gKiB3aWxsIGJlIGNob3NlbiBhbG1vc3QgZXZlcnkgdGltZSwgYnV0IGZvciB0aGUgb3RoZXIgc3BhY2UsIG9uZVxuICogb2YgdGhlIDEtZnJlcXVlbmN5IHNpdHVhdGlvbnMgbXVzdCBiZSBjaG9zZW4uIFNvIHRoZSBhY3R1YWxcbiAqIHByb2JhYmlsaXRpZXMgd2lsbCBiZSByb3VnaGx5IDUwJSwgNTAlLCAxMDAlLiBXaGVuIHNlbGVjdGluZ1xuICogbW9yZSB0aGFuIG9uZSByZXN1bHQsIGZyZXF1ZW5jaWVzIGNhbiBvbmx5IGJlIGEgZ3VpZGUuXG4gKlxuICogQmVmb3JlIHRoaXMgZnVuY3Rpb24gcmV0dXJucyBpdHMgcmVzdWx0LCBpdCBzb3J0cyB0aGVcbiAqIHNpdHVhdGlvbnMgaW4gaW5jcmVhc2luZyBvcmRlciBvZiB0aGVpciBkaXNwbGF5T3JkZXIgdmFsdWVzLlxuICovXG5TeXN0ZW0ucHJvdG90eXBlLmdldFNpdHVhdGlvbklkQ2hvaWNlcyA9IGZ1bmN0aW9uKGxpc3RPZk9yT25lSWRzT3JUYWdzLFxuICAgIG1pbkNob2ljZXMsIG1heENob2ljZXMpXG57XG4gIHZhciBkYXR1bTtcbiAgdmFyIGk7XG5cbiAgLy8gRmlyc3QgY2hlY2sgaWYgd2UgaGF2ZSBhIHNpbmdsZSBzdHJpbmcgZm9yIHRoZSBpZCBvciB0YWcuXG4gIGlmIChPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwobGlzdE9mT3JPbmVJZHNPclRhZ3MpLnJlcGxhY2UoL15cXFtvYmplY3QgKC4rKVxcXSQvLCBcIiQxXCIpLnRvTG93ZXJDYXNlKCkgPT0gJ3N0cmluZycpIHtcbiAgICBsaXN0T2ZPck9uZUlkc09yVGFncyA9IFtsaXN0T2ZPck9uZUlkc09yVGFnc107XG4gIH1cblxuICAvLyBGaXJzdCB3ZSBidWlsZCBhIGxpc3Qgb2YgYWxsIGNhbmRpZGF0ZSBpZHMuXG4gIHZhciBhbGxJZHMgPSB7fTtcbiAgZm9yIChpID0gMDsgaSA8IGxpc3RPZk9yT25lSWRzT3JUYWdzLmxlbmd0aDsgKytpKSB7XG4gICAgdmFyIHRhZ09ySWQgPSBsaXN0T2ZPck9uZUlkc09yVGFnc1tpXTtcbiAgICBpZiAodGFnT3JJZC5zdWJzdHIoMCwgMSkgPT0gJyMnKSB7XG4gICAgICB2YXIgaWRzID0gZ2V0U2l0dWF0aW9uSWRzV2l0aFRhZyh0YWdPcklkLnN1YnN0cigxKSk7XG4gICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGlkcy5sZW5ndGg7ICsraikge1xuICAgICAgICBhbGxJZHNbaWRzW2pdXSA9IHRydWU7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGFsbElkc1t0YWdPcklkXSA9IHRydWU7XG4gICAgfVxuICB9XG5cbiAgLy8gRmlsdGVyIG91dCBhbnl0aGluZyB0aGF0IGNhbid0IGJlIHZpZXdlZCByaWdodCBub3cuXG4gIHZhciBjdXJyZW50U2l0dWF0aW9uID0gZ2V0Q3VycmVudFNpdHVhdGlvbigpO1xuICB2YXIgdmlld2FibGVTaXR1YXRpb25EYXRhID0gW107XG4gIGZvciAodmFyIHNpdHVhdGlvbklkIGluIGFsbElkcykge1xuICAgIHZhciBzaXR1YXRpb24gPSBnYW1lLnNpdHVhdGlvbnNbc2l0dWF0aW9uSWRdO1xuICAgIGFzc2VydChzaXR1YXRpb24sIFwidW5rbm93bl9zaXR1YXRpb25cIi5sKHtpZDpzaXR1YXRpb25JZH0pKTtcblxuICAgIGlmIChzaXR1YXRpb24uY2FuVmlldyhjaGFyYWN0ZXIsIHN5c3RlbSwgY3VycmVudFNpdHVhdGlvbikpIHtcbiAgICAgIC8vIFdoaWxlIHdlJ3JlIGhlcmUsIGdldCB0aGUgc2VsZWN0aW9uIGRhdGEuXG4gICAgICB2YXIgdmlld2FibGVTaXR1YXRpb25EYXR1bSA9XG4gICAgICAgIHNpdHVhdGlvbi5jaG9pY2VEYXRhKGNoYXJhY3Rlciwgc3lzdGVtLCBjdXJyZW50U2l0dWF0aW9uKTtcbiAgICAgIHZpZXdhYmxlU2l0dWF0aW9uRGF0dW0uaWQgPSBzaXR1YXRpb25JZDtcbiAgICAgIHZpZXdhYmxlU2l0dWF0aW9uRGF0YS5wdXNoKHZpZXdhYmxlU2l0dWF0aW9uRGF0dW0pO1xuICAgIH1cbiAgfVxuXG4gIC8vIFRoZW4gd2Ugc29ydCBpbiBkZXNjZW5kaW5nIHByaW9yaXR5IG9yZGVyLlxuICB2aWV3YWJsZVNpdHVhdGlvbkRhdGEuc29ydChmdW5jdGlvbihhLCBiKSB7XG4gICAgICByZXR1cm4gYi5wcmlvcml0eSAtIGEucHJpb3JpdHk7XG4gICAgICB9KTtcblxuICB2YXIgY29tbWl0dGVkID0gW107XG4gIHZhciBjYW5kaWRhdGVzQXRMYXN0UHJpb3JpdHkgPSBbXTtcbiAgdmFyIGxhc3RQcmlvcml0eTtcbiAgLy8gSW4gZGVzY2VuZGluZyBwcmlvcml0eSBvcmRlci5cbiAgZm9yIChpID0gMDsgaSA8IHZpZXdhYmxlU2l0dWF0aW9uRGF0YS5sZW5ndGg7ICsraSkge1xuICAgIGRhdHVtID0gdmlld2FibGVTaXR1YXRpb25EYXRhW2ldO1xuICAgIGlmIChkYXR1bS5wcmlvcml0eSAhPSBsYXN0UHJpb3JpdHkpIHtcbiAgICAgIGlmIChsYXN0UHJpb3JpdHkgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAvLyBXZSd2ZSBkcm9wcGVkIGEgcHJpb3JpdHkgZ3JvdXAsIHNlZSBpZiB3ZSBoYXZlIGVub3VnaFxuICAgICAgICAvLyBzaXR1YXRpb25zIHNvIGZhciwgYW5kIHN0b3AgaWYgd2UgZG8uXG4gICAgICAgIGlmIChtaW5DaG9pY2VzID09PSB1bmRlZmluZWQgfHwgaSA+PSBtaW5DaG9pY2VzKSBicmVhaztcbiAgICAgIH1cbiAgICAgIC8vIENvbnRpbnVlIHRvIGFjY2N1bXVsYXRlIG1vcmUgb3B0aW9ucy5cbiAgICAgIGNvbW1pdHRlZC5wdXNoLmFwcGx5KGNvbW1pdHRlZCwgY2FuZGlkYXRlc0F0TGFzdFByaW9yaXR5KTtcbiAgICAgIGNhbmRpZGF0ZXNBdExhc3RQcmlvcml0eSA9IFtdO1xuICAgICAgbGFzdFByaW9yaXR5ID0gZGF0dW0ucHJpb3JpdHk7XG4gICAgfVxuICAgIGNhbmRpZGF0ZXNBdExhc3RQcmlvcml0eS5wdXNoKGRhdHVtKTtcbiAgfVxuXG4gIC8vIFNvIHRoZSB2YWx1ZXMgaW4gY29tbWl0dGVkIHdlJ3JlIGNvbW1pdHRlZCB0bywgYmVjYXVzZSB3aXRob3V0XG4gIC8vIHRoZW0gd2Ugd291bGRuJ3QgaGl0IG91ciBtaW5pbXVtLiBCdXQgdGhvc2UgaW5cbiAgLy8gY2FuZGlkYXRlc0F0TGFzdFByaW9yaXR5IG1pZ2h0IHRha2UgdXMgb3ZlciBvdXIgbWF4aW11bSwgc29cbiAgLy8gZmlndXJlIG91dCBob3cgbWFueSB3ZSBzaG91bGQgY2hvb3NlLlxuICB2YXIgdG90YWxDaG9pY2VzID0gY29tbWl0dGVkLmxlbmd0aCArIGNhbmRpZGF0ZXNBdExhc3RQcmlvcml0eS5sZW5ndGg7XG4gIGlmIChtYXhDaG9pY2VzID09PSB1bmRlZmluZWQgfHwgbWF4Q2hvaWNlcyA+PSB0b3RhbENob2ljZXMpIHtcbiAgICAvLyBXZSBjYW4gdXNlIGFsbCB0aGUgY2hvaWNlcy5cbiAgICBjb21taXR0ZWQucHVzaC5hcHBseShjb21taXR0ZWQsIGNhbmRpZGF0ZXNBdExhc3RQcmlvcml0eSk7XG4gIH0gZWxzZSBpZiAobWF4Q2hvaWNlcyA+PSBjb21taXR0ZWQubGVuZ3RoKSB7XG4gICAgLy8gV2UgY2FuIG9ubHkgdXNlIHRoZSBjb21taXRlZCBvbmVzLlxuICAgIC8vIE5PLU9QXG4gIH0gZWxzZSB7XG4gICAgLy8gV2UgaGF2ZSB0byBzYW1wbGUgdGhlIGNhbmRpZGF0ZXMsIHVzaW5nIHRoZWlyIHJlbGF0aXZlIGZyZXF1ZW5jeS5cbiAgICB2YXIgY2FuZGlkYXRlc1RvSW5jbHVkZSA9IG1heENob2ljZXMgLSBjb21taXR0ZWQubGVuZ3RoO1xuICAgIGZvciAoaSA9IDA7IGkgPCBjYW5kaWRhdGVzQXRMYXN0UHJpb3JpdHkubGVuZ3RoOyArK2kpIHtcbiAgICAgIGRhdHVtID0gY2FuZGlkYXRlc0F0TGFzdFByaW9yaXR5W2ldO1xuICAgICAgZGF0dW0uX2ZyZXF1ZW5jeVZhbHVlID0gdGhpcy5ybmQucmFuZG9tKCkgLyBkYXR1bS5mcmVxdWVuY3k7XG4gICAgfVxuICAgIGNhbmRpZGF0ZXNUb0luY2x1ZGUuc29ydChmdW5jdGlvbihhLCBiKSB7XG4gICAgICAgIHJldHVybiBhLl9mcmVxdWVuY3lWYWx1ZSAtIGIuX2ZyZXF1ZW5jeVZhbHVlO1xuICAgICAgICB9KTtcbiAgICB2YXIgY2hvc2VuID0gY2FuZGlkYXRlc1RvSW5jbHVkZS5zbGljZSgwLCBjYW5kaWRhdGVzVG9JbmNsdWRlKTtcbiAgICBjb21taXR0ZWQucHVzaC5hcHBseShjb21taXR0ZWQsIGNob3Nlbik7XG4gIH1cblxuICAvLyBOb3cgc29ydCBpbiBhc2NlbmRpbmcgZGlzcGxheSBvcmRlci5cbiAgY29tbWl0dGVkLnNvcnQoZnVuY3Rpb24oYSwgYikge1xuICAgICAgcmV0dXJuIGEuZGlzcGxheU9yZGVyIC0gYi5kaXNwbGF5T3JkZXI7XG4gICAgICB9KTtcblxuICAvLyBBbmQgcmV0dXJuIGFzIGEgbGlzdCBvZiBpZHMgb25seS5cbiAgdmFyIHJlc3VsdCA9IFtdO1xuICBmb3IgKGkgPSAwOyBpIDwgY29tbWl0dGVkLmxlbmd0aDsgKytpKSB7XG4gICAgcmVzdWx0LnB1c2goY29tbWl0dGVkW2ldLmlkKTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufTtcblxuLyogQ2FsbCB0aGlzIHRvIGNoYW5nZSB0aGUgY2hhcmFjdGVyIHRleHQ6IHRoZSB0ZXh0IGluIHRoZSByaWdodFxuICogdG9vbGJhciBiZWZvcmUgdGhlIHF1YWxpdGllcyBsaXN0LiBUaGlzIHRleHQgaXMgZGVzaWduZWQgdG8gYmVcbiAqIGEgc2hvcnQgZGVzY3JpcHRpb24gb2YgdGhlIGN1cnJlbnQgc3RhdGUgb2YgeW91ciBjaGFyYWN0ZXIuIFRoZVxuICogY29udGVudCB5b3UgZ2l2ZSBzaG91bGQgYmUgXCJEaXNwbGF5IENvbnRlbnRcIiAoc2VlXG4gKiBgU3lzdGVtLnByb3RvdHlwZS53cml0ZWAgZm9yIHRoZSBkZWZpbml0aW9uKS5cbiAqL1xuU3lzdGVtLnByb3RvdHlwZS5zZXRDaGFyYWN0ZXJUZXh0ID0gZnVuY3Rpb24oY29udGVudCkge1xuICB2YXIgYmxvY2sgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcImNoYXJhY3Rlcl90ZXh0X2NvbnRlbnRcIik7XG4gIHZhciBvbGRDb250ZW50ID0gYmxvY2suaW5uZXJIVE1MO1xuICB2YXIgbmV3Q29udGVudCA9IGF1Z21lbnRMaW5rcyhjb250ZW50KTtcbiAgaWYgKGludGVyYWN0aXZlICYmIGJsb2NrLm9mZnNldFdpZHRoID4gMCAmJiBibG9jay5vZmZzZXRIZWlnaHQgPiAwKSB7XG4gICAgaGlkZUJsb2NrKGJsb2NrKTtcbiAgICBibG9jay5pbm5lckhUTUwgPSBuZXdDb250ZW50O1xuICAgIHNob3dCbG9jayhibG9jayk7XG4gICAgc2hvd0hpZ2hsaWdodChibG9jay5wYXJlbnQpO1xuICB9IGVsc2Uge1xuICAgIGJsb2NrLmlubmVySFRNTCA9IG5ld0NvbnRlbnQ7XG4gIH1cbn07XG5cbi8qIENhbGwgdGhpcyB0byBjaGFuZ2UgdGhlIHZhbHVlIG9mIGEgY2hhcmFjdGVyIHF1YWxpdHkuIERvbid0XG4gKiBkaXJlY3RseSBjaGFuZ2UgcXVhbGl0eSB2YWx1ZXMsIGJlY2F1c2UgdGhhdCB3aWxsIG5vdCB1cGRhdGVcbiAqIHRoZSBVSS4gKFlvdSBjYW4gY2hhbmdlIGFueSBkYXRhIGluIHRoZSBjaGFyYWN0ZXIncyBzYW5kYm94XG4gKiBkaXJlY3RseSwgaG93ZXZlciwgc2luY2UgdGhhdCBpc24ndCBkaXNwbGF5ZWQpLiAqL1xuU3lzdGVtLnByb3RvdHlwZS5zZXRRdWFsaXR5ID0gZnVuY3Rpb24ocXVhbGl0eSwgbmV3VmFsdWUpIHtcbiAgdmFyIG9sZFZhbHVlID0gY2hhcmFjdGVyLnF1YWxpdGllc1txdWFsaXR5XTtcbiAgY2hhcmFjdGVyLnF1YWxpdGllc1txdWFsaXR5XSA9IG5ld1ZhbHVlO1xuICBpZiAoIWludGVyYWN0aXZlKSByZXR1cm47XG5cbiAgLy8gV29yayBvdXQgaG93IHRvIGRpc3BsYXkgdGhlIHZhbHVlcy5cbiAgdmFyIG5ld0Rpc3BsYXk7XG4gIHZhciBxdWFsaXR5RGVmaW5pdGlvbiA9IGdhbWUucXVhbGl0aWVzW3F1YWxpdHldO1xuICBpZiAocXVhbGl0eURlZmluaXRpb24pIHtcbiAgICBuZXdEaXNwbGF5ID0gcXVhbGl0eURlZmluaXRpb24uZm9ybWF0KGNoYXJhY3RlciwgbmV3VmFsdWUpO1xuICB9IGVsc2Uge1xuICAgIC8vIFdlIHNob3VsZG4ndCBkaXNwbGF5IHF1YWxpdGllcyB0aGF0IGhhdmUgbm8gZGVmaW5pdGlvbi5cbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBBZGQgdGhlIGRhdGEgYmxvY2ssIGlmIHdlIG5lZWQgaXQuXG4gIHZhciBxdWFsaXR5QmxvY2sgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcIiNxX1wiK3F1YWxpdHkpO1xuICBpZiAocXVhbGl0eUJsb2NrLmxlbmd0aCA8PSAwKSB7XG4gICAgaWYgKG5ld0Rpc3BsYXkgPT09IG51bGwpIHJldHVybjtcbiAgICBxdWFsaXR5QmxvY2sgPSBhZGRRdWFsaXR5QmxvY2socXVhbGl0eSkuaGlkZSgpLmZhZGVJbig1MDApO1xuICB9IGVsc2Uge1xuICAgIC8vIERvIG5vdGhpbmcgaWYgdGhlcmUncyBub3RoaW5nIHRvIGRvLlxuICAgIGlmIChvbGRWYWx1ZSA9PSBuZXdWYWx1ZSkgcmV0dXJuO1xuXG4gICAgLy8gQ2hhbmdlIHRoZSB2YWx1ZS5cbiAgICBpZiAobmV3RGlzcGxheSA9PT0gbnVsbCkge1xuICAgICAgLy8gUmVtb3ZlIHRoZSBibG9jaywgYW5kIHBvc3NpYmx5IHRoZSB3aG9sZSBncm91cCwgaWZcbiAgICAgIC8vIGl0IGlzIHRoZSBsYXN0IHF1YWxpdHkgaW4gdGhlIGdyb3VwLlxuICAgICAgdmFyIHRvUmVtb3ZlID0gbnVsbDtcbiAgICAgIHZhciBncm91cEJsb2NrID0gcXVhbGl0eUJsb2NrLnBhcmVudHMoJy5xdWFsaXR5X2dyb3VwJyk7XG4gICAgICBpZiAoZ3JvdXBCbG9jay5maW5kKCcucXVhbGl0eScpLmxlbmd0aCA8PSAxKSB7XG4gICAgICAgIHRvUmVtb3ZlID0gZ3JvdXBCbG9jaztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRvUmVtb3ZlID0gcXVhbGl0eUJsb2NrO1xuICAgICAgfVxuXG4gICAgICB0b1JlbW92ZS5mYWRlT3V0KDEwMDAsIGZ1bmN0aW9uKCkge1xuICAgICAgICAgIHRvUmVtb3ZlLnJlbW92ZSgpO1xuICAgICAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgdmFsQmxvY2sgPSBxdWFsaXR5QmxvY2suZmluZChcIltkYXRhLWF0dHI9J3ZhbHVlJ11cIik7XG4gICAgICB2YWxCbG9jay5mYWRlT3V0KDI1MCwgZnVuY3Rpb24oKSB7XG4gICAgICAgICAgdmFsQmxvY2suaHRtbChuZXdEaXNwbGF5KTtcbiAgICAgICAgICB2YWxCbG9jay5mYWRlSW4oNzUwKTtcbiAgICAgICAgICB9KTtcbiAgICB9XG4gIH1cbiAgc2hvd0hpZ2hsaWdodChxdWFsaXR5QmxvY2spO1xufTtcblxuLyogQ2hhbmdlcyBhIHF1YWxpdHkgdG8gYSBuZXcgdmFsdWUsIGJ1dCBhbHNvIHNob3VsZCBzaG93IGEgcHJvZ3Jlc3MgYmFyXG4gKiBhbmltYXRpb24gb2YgdGhlIGNoYW5nZS4gUmVtb3ZlZCB3aXRoIHRoZSBwcm9ncmVzcyBiYXIgZnVuY3Rpb25hbGl0eS4gKi9cblN5c3RlbS5wcm90b3R5cGUuYW5pbWF0ZVF1YWxpdHkgPSBmdW5jdGlvbihxdWFsaXR5LCBuZXdWYWx1ZSwgb3B0cykge1xuICB0aGlzLnNldFF1YWxpdHkocXVhbGl0eSwgbmV3VmFsdWUpO1xufTtcblxuLyogVGhlIGNoYXJhY3RlciB0aGF0IGlzIHBhc3NlZCBpbnRvIGVhY2ggc2l0dWF0aW9uIGlzIG9mIHRoaXNcbiAqIGZvcm0uXG4gKlxuICogVGhlIGBxdWFsaXRpZXNgIGRhdGEgbWVtYmVyIG1hcHMgdGhlIElkcyBvZiBlYWNoIHF1YWxpdHkgdG8gaXRzXG4gKiBjdXJyZW50IHZhbHVlLiBXaGVuIGltcGxlbWVudGluZyBlbnRlciwgYWN0IG9yIGV4aXQgZnVuY3Rpb25zLFxuICogeW91IHNob3VsZCBjb25zaWRlciB0aGlzIHRvIGJlIHJlYWQtb25seS4gTWFrZSBhbGxcbiAqIG1vZGlmaWNhdGlvbnMgdGhyb3VnaCBgU3lzdGVtLnByb3RvdHlwZS5zZXRRdWFsaXR5YCwgb3JcbiAqIGBTeXN0ZW0ucHJvdG90eXBlLmFuaW1hdGVRdWFsaXR5YC4gSW4geW91ciBgaW5pdGAgZnVuY3Rpb24sIHlvdVxuICogY2FuIHNldCB0aGVzZSB2YWx1ZXMgZGlyZWN0bHkuXG4gKlxuICogVGhlIGBzYW5kYm94YCBkYXRhIG1lbWJlciBpcyBkZXNpZ25lZCB0byBhbGxvdyB5b3VyIGNvZGUgdG9cbiAqIHRyYWNrIGFueSBkYXRhIGl0IG5lZWRzIHRvLiBUaGUgb25seSBwcm92aXNvIGlzIHRoYXQgdGhlIGRhdGFcbiAqIHN0cnVjdHVyZSBzaG91bGQgYmUgc2VyaWFsaXphYmxlIGludG8gSlNPTiAodGhpcyBtZWFucyBpdCBtdXN0XG4gKiBvbmx5IGNvbnNpc3Qgb2YgcHJpbWl0aXZlIHR5cGVzIFtvYmplY3RzLCBhcnJheXMsIG51bWJlcnMsXG4gKiBib29sZWFucywgc3RyaW5nc10sIGFuZCBpdCBtdXN0IG5vdCBjb250YWluIGNpcmN1bGFyIHNlcmllcyBvZlxuICogcmVmZXJlbmNlcykuIFRoZSBkYXRhIGluIHRoZSBzYW5kYm94IGlzIG5vdCBkaXNwbGF5ZWQgaW4gdGhlXG4gKiBVSSwgYWx0aG91Z2ggeW91IGFyZSBmcmVlIHRvIHVzZSBpdCB0byBjcmVhdGUgc3VpdGFibGUgb3V0cHV0XG4gKiBmb3IgdGhlIHBsYXllci4uXG4gKi9cbnZhciBDaGFyYWN0ZXIgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5xdWFsaXRpZXMgPSB7fTtcbiAgdGhpcy5zYW5kYm94ID0ge307XG59O1xuXG4vKiBUaGUgZGF0YSBzdHJ1Y3R1cmUgaG9sZGluZyB0aGUgY29udGVudCBmb3IgdGhlIGdhbWUuIEJ5IGRlZmF1bHRcbiAqIHRoaXMgaG9sZHMgbm90aGluZy4gSXQgaXMgdGhpcyBkYXRhIHN0cnVjdHVyZSB0aGF0IGlzIHBvcHVsYXRlZFxuICogaW4gdGhlIGAuZ2FtZS5qc2AgZmlsZS4gRWFjaCBlbGVtZW50IGluIHRoZSBzdHJ1Y3R1cmUgaXNcbiAqIGNvbW1lbnRlZCwgYmVsb3cuXG4gKlxuICogVGhpcyBzaG91bGQgYmUgc3RhdGljIGRhdGEgdGhhdCBuZXZlciBjaGFuZ2VzIHRocm91Z2ggdGhlXG4gKiBjb3Vyc2Ugb2YgdGhlIGdhbWUuIEl0IGlzIG5ldmVyIHNhdmVkLCBzbyBhbnl0aGluZyB0aGF0IG1pZ2h0XG4gKiBjaGFuZ2Ugc2hvdWxkIGJlIHN0b3JlZCBpbiB0aGUgY2hhcmFjdGVyLlxuICovXG52YXIgZ2FtZSA9IHtcblxuICAvLyBTaXR1YXRpb25zXG5cbiAgLyogQW4gb2JqZWN0IG1hcHBpbmcgZnJvbSB0aGUgdW5pcXVlIGlkIG9mIGVhY2ggc2l0dWF0aW9uLCB0b1xuICAgKiB0aGUgc2l0dWF0aW9uIG9iamVjdCBpdHNlbGYuIFRoaXMgaXMgdGhlIGhlYXJ0IG9mIHRoZSBnYW1lXG4gICAqIHNwZWNpZmljYXRpb24uICovXG4gIHNpdHVhdGlvbnM6IHt9LFxuXG4gIC8qIFRoZSB1bmlxdWUgaWQgb2YgdGhlIHNpdHVhdGlvbiB0byBlbnRlciBhdCB0aGUgc3RhcnQgb2YgYVxuICAgKiBuZXcgZ2FtZS4gKi9cbiAgc3RhcnQ6IFwic3RhcnRcIixcblxuICAvLyBRdWFsaXR5IGRpc3BsYXkgZGVmaW5pdGlvbnNcblxuICAvKiBBbiBvYmplY3QgbWFwcGluZyB0aGUgdW5pcXVlIGlkIG9mIGVhY2ggcXVhbGl0eSB0byBpdHNcbiAgICogUXVhbGl0eURlZmluaXRpb24uIFlvdSBkb24ndCBuZWVkIGRlZmluaXRpb25zIGZvciBldmVyeVxuICAgKiBxdWFsaXR5LCBidXQgb25seSBxdWFsaXRpZXMgaW4gdGhpcyBtYXBwaW5nIHdpbGwgYmVcbiAgICogZGlzcGxheWVkIGluIHRoZSBjaGFyYWN0ZXIgYm94IG9mIHRoZSBVSS4gKi9cbiAgcXVhbGl0aWVzOiB7fSxcblxuICAvKiBRdWFsaXRpZXMgY2FuIGhhdmUgYW4gb3B0aW9uYWwgZ3JvdXAgSWQuIFRoaXMgbWFwcyB0aG9zZVxuICAgKiBJZHMgdG8gdGhlIGdyb3VwIGRlZmluaXRpb25zIHRoYXQgc2F5cyBob3cgdG8gZm9ybWF0IGl0c1xuICAgKiBxdWFsaXRpZXMuXG4gICAqL1xuICBxdWFsaXR5R3JvdXBzOiB7fSxcblxuICAvLyBIb29rc1xuXG4gIC8qIFRoaXMgZnVuY3Rpb24gaXMgY2FsbGVkIGF0IHRoZSBzdGFydCBvZiB0aGUgZ2FtZS4gSXQgaXNcbiAgICogbm9ybWFsbHkgb3ZlcnJpZGRlbiB0byBwcm92aWRlIGluaXRpYWwgY2hhcmFjdGVyIGNyZWF0aW9uXG4gICAqIChzZXR0aW5nIGluaXRpYWwgcXVhbGl0eSB2YWx1ZXMsIHNldHRpbmcgdGhlXG4gICAqIGNoYXJhY3Rlci10ZXh0LiBUaGlzIGlzIG9wdGlvbmFsLCBob3dldmVyLCBhcyBzZXQtdXBcbiAgICogcHJvY2Vzc2luZyBjb3VsZCBhbHNvIGJlIGRvbmUgYnkgdGhlIGZpcnN0IHNpdHVhdGlvbidzXG4gICAqIGVudGVyIGZ1bmN0aW9uLiBJZiB0aGlzIGZ1bmN0aW9uIGlzIGdpdmVuIGl0IHNob3VsZCBoYXZlXG4gICAqIHRoZSBzaWduYXR1cmUgZnVuY3Rpb24oY2hhcmFjdGVyLCBzeXN0ZW0pLlxuICAgKi9cbiAgaW5pdDogbnVsbCxcblxuICAvKiBUaGlzIGZ1bmN0aW9uIGlzIGNhbGxlZCBiZWZvcmUgZW50ZXJpbmcgYW55IG5ld1xuICAgKiBzaXR1YXRpb24uIEl0IGlzIGNhbGxlZCBiZWZvcmUgdGhlIGNvcnJlc3BvbmRpbmcgc2l0dWF0aW9uXG4gICAqIGhhcyBpdHMgYGVudGVyYCBtZXRob2QgY2FsbGVkLiBJdCBjYW4gYmUgdXNlZCB0byBpbXBsZW1lbnRcbiAgICogdGltZWQgdHJpZ2dlcnMsIGJ1dCBpcyB0b3RhbGx5IG9wdGlvbmFsLiBJZiB0aGlzIGZ1bmN0aW9uXG4gICAqIGlzIGdpdmVuIGl0IHNob3VsZCBoYXZlIHRoZSBzaWduYXR1cmU6XG4gICAqXG4gICAqIGZ1bmN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLCBvbGRTaXR1YXRpb25JZCwgbmV3U2l0dWF0aW9uSWQpO1xuICAgKi9cbiAgZW50ZXI6IG51bGwsXG5cbiAgLyogSG9vayBmb3Igd2hlbiB0aGUgc2l0dWF0aW9uIGhhcyBhbHJlYWR5IGJlZW4gY2FycmllZCBvdXRcbiAgICogYW5kIHByaW50ZWQuIFRoZSBzaWduYXR1cmUgaXM6XG4gICAqXG4gICAqIGZ1bmN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLCBvbGRTaXR1YXRpb25JZCwgbmV3U2l0dWF0aW9uSWQpO1xuICAgKi9cbiAgYWZ0ZXJFbnRlcjogbnVsbCxcblxuICAvKiBUaGlzIGZ1bmN0aW9uIGlzIGNhbGxlZCBiZWZvcmUgY2Fycnlpbmcgb3V0IGFueSBhY3Rpb24gaW5cbiAgICogYW55IHNpdHVhdGlvbi4gSXQgaXMgY2FsbGVkIGJlZm9yZSB0aGUgY29ycmVzcG9uZGluZ1xuICAgKiBzaXR1YXRpb24gaGFzIGl0cyBgYWN0YCBtZXRob2QgY2FsbGVkLiBJZiB0aGlzIG9wdGlvbmFsXG4gICAqIGZ1bmN0aW9uIGlzIGdpdmVuIGl0IHNob3VsZCBoYXZlIHRoZSBzaWduYXR1cmU6XG4gICAqXG4gICAqIGZ1bmN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLCBzaXR1YXRpb25JZCwgYWN0aW9uSWQpO1xuICAgKlxuICAgKiBJZiB0aGUgZnVuY3Rpb24gcmV0dXJucyB0cnVlLCB0aGVuIGl0IGlzIGluZGljYXRpbmcgdGhhdCBpdFxuICAgKiBoYXMgY29uc3VtZWQgdGhlIGFjdGlvbiwgYW5kIHRoZSBhY3Rpb24gd2lsbCBub3QgYmUgcGFzc2VkXG4gICAqIG9uIHRvIHRoZSBzaXR1YXRpb24uIE5vdGUgdGhhdCB0aGlzIGlzIHRoZSBvbmx5IG9uZSBvZlxuICAgKiB0aGVzZSBnbG9iYWwgaGFuZGxlcnMgdGhhdCBjYW4gY29uc3VtZSB0aGUgZXZlbnQuXG4gICAqL1xuICBiZWZvcmVBY3Rpb246IG51bGwsXG5cbiAgLyogVGhpcyBmdW5jdGlvbiBpcyBjYWxsZWQgYWZ0ZXIgY2Fycnlpbmcgb3V0IGFueSBhY3Rpb24gaW5cbiAgICogYW55IHNpdHVhdGlvbi4gSXQgaXMgY2FsbGVkIGFmdGVyIHRoZSBjb3JyZXNwb25kaW5nXG4gICAqIHNpdHVhdGlvbiBoYXMgaXRzIGBhY3RgIG1ldGhvZCBjYWxsZWQuIElmIHRoaXMgb3B0aW9uYWxcbiAgICogZnVuY3Rpb24gaXMgZ2l2ZW4gaXQgc2hvdWxkIGhhdmUgdGhlIHNpZ25hdHVyZTpcbiAgICpcbiAgICogZnVuY3Rpb24oY2hhcmFjdGVyLCBzeXN0ZW0sIHNpdHVhdGlvbklkLCBhY3Rpb25JZCk7XG4gICAqL1xuICBhZnRlckFjdGlvbjogbnVsbCxcblxuICAvKiBUaGlzIGZ1bmN0aW9uIGlzIGNhbGxlZCBhZnRlciBsZWF2aW5nIGFueSBzaXR1YXRpb24uIEl0IGlzXG4gICAqIGNhbGxlZCBhZnRlciB0aGUgY29ycmVzcG9uZGluZyBzaXR1YXRpb24gaGFzIGl0cyBgZXhpdGBcbiAgICogbWV0aG9kIGNhbGxlZC4gSWYgdGhpcyBvcHRpb25hbCBmdW5jdGlvbiBpcyBnaXZlbiBpdCBzaG91bGRcbiAgICogaGF2ZSB0aGUgc2lnbmF0dXJlOlxuICAgKlxuICAgKiBmdW5jdGlvbihjaGFyYWN0ZXIsIHN5c3RlbSwgb2xkU2l0dWF0aW9uSWQsIG5ld1NpdHVhdGlvbklkKTtcbiAgICovXG4gIGV4aXQ6IG51bGxcbn07XG4iLCIvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vLyBDb2RlIGJlbG93IGRvZXNuJ3QgZm9ybSBwYXJ0IG9mIHRoZSBwdWJsaWMgQVBJIGZvciBVTkRVTSwgc29cbi8vIHlvdSBzaG91bGRuJ3QgZmluZCB5b3UgbmVlZCB0byB1c2UgaXQuXG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBJbnRlcm5hbCBEYXRhXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4vKiBUaGUgZ2xvYmFsIHN5c3RlbSBvYmplY3QuICovXG52YXIgc3lzdGVtID0gbmV3IFN5c3RlbSgpO1xuXG4vKiBUaGlzIGlzIHRoZSBkYXRhIG9uIHRoZSBwbGF5ZXIncyBwcm9ncmVzcyB0aGF0IGdldHMgc2F2ZWQuICovXG52YXIgcHJvZ3Jlc3MgPSB7XG4gIC8vIEEgcmFuZG9tIHNlZWQgc3RyaW5nLCB1c2VkIGludGVybmFsbHkgdG8gbWFrZSByYW5kb21cbiAgLy8gc2VxdWVuY2VzIHByZWRpY3RhYmxlLlxuc2VlZDogbnVsbCxcbiAgICAgIC8vIEtlZXBzIHRyYWNrIG9mIHRoZSBsaW5rcyBjbGlja2VkLCBhbmQgd2hlbi5cbiAgICAgIHNlcXVlbmNlOiBbXSxcbiAgICAgIC8vIFRoZSB0aW1lIHdoZW4gdGhlIHByb2dyZXNzIHdhcyBzYXZlZC5cbiAgICAgIHNhdmVUaW1lOiBudWxsXG59O1xuXG4vKiBUaGUgSWQgb2YgdGhlIGN1cnJlbnQgc2l0dWF0aW9uIHRoZSBwbGF5ZXIgaXMgaW4uICovXG52YXIgY3VycmVudCA9IG51bGw7XG5cbi8qIFRoaXMgaXMgdGhlIGN1cnJlbnQgY2hhcmFjdGVyLiBJdCBzaG91bGQgYmUgcmVjb25zdHJ1Y3RhYmxlXG4gKiBmcm9tIHRoZSBhYm92ZSBwcm9ncmVzcyBkYXRhLiAqL1xudmFyIGNoYXJhY3RlciA9IG51bGw7XG5cbi8qIFRyYWNrcyB3aGV0aGVyIHdlJ3JlIGluIGludGVyYWN0aXZlIG1vZGUgb3IgYmF0Y2ggbW9kZS4gKi9cbnZhciBpbnRlcmFjdGl2ZSA9IHRydWU7XG5cbi8qIFRoZSBzeXN0ZW0gdGltZSB3aGVuIHRoZSBnYW1lIHdhcyBpbml0aWFsaXplZC4gKi9cbnZhciBzdGFydFRpbWU7XG5cbi8qIFRoZSBzdGFjayBvZiBsaW5rcywgcmVzdWx0aW5nIGZyb20gdGhlIGxhc3QgYWN0aW9uLCBzdGlsbCBiZSB0b1xuICogcmVzb2x2ZWQuICovXG52YXIgbGlua1N0YWNrID0gbnVsbDtcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIFV0aWxpdHkgRnVuY3Rpb25zXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG52YXIgZ2V0Q3VycmVudFNpdHVhdGlvbiA9IGZ1bmN0aW9uKCkge1xuICBpZiAoY3VycmVudCkge1xuICAgIHJldHVybiBnYW1lLnNpdHVhdGlvbnNbY3VycmVudF07XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn07XG5cbnZhciBwYXJzZSA9IGZ1bmN0aW9uKHN0cikge1xuICBpZiAoc3RyID09PSB1bmRlZmluZWQpIHtcbiAgICByZXR1cm4gc3RyO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBwYXJzZUZsb2F0KHN0cik7XG4gIH1cbn07XG5cbnZhciBwYXJzZUxpc3QgPSBmdW5jdGlvbihzdHIsIGNhbkJlVW5kZWZpbmVkKSB7XG4gIGlmIChzdHIgPT09IHVuZGVmaW5lZCB8fCBzdHIgPT09IG51bGwpIHtcbiAgICBpZiAoY2FuQmVVbmRlZmluZWQpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHN0ci5zcGxpdCgvWyAsXFx0XSsvKTtcbiAgfVxufTtcblxudmFyIHBhcnNlRm4gPSBmdW5jdGlvbihzdHIpIHtcbiAgaWYgKHN0ciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIHN0cjtcbiAgfSBlbHNlIHtcbiAgICB2YXIgZnN0ciA9IFwiKGZ1bmN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLCBzaXR1YXRpb24pIHtcXG5cIiArXG4gICAgICBzdHIgKyBcIlxcbn0pXCI7XG4gICAgdmFyIGZuID0gZXZhbChmc3RyKTtcbiAgICByZXR1cm4gZm47XG4gIH1cbn07XG5cbnZhciBsb2FkSFRNTFNpdHVhdGlvbnMgPSBmdW5jdGlvbigpIHtcbiAgdmFyICRodG1sU2l0dWF0aW9ucyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoXCJkaXYuc2l0dWF0aW9uXCIpO1xuICBBcnJheS5wcm90b3R5cGUuZm9yRWFjaC5jYWxsKCRodG1sU2l0dWF0aW9ucywgZnVuY3Rpb24oJHNpdHVhdGlvbil7XG4gICAgdmFyIGlkID0gJHNpdHVhdGlvbi5nZXRBdHRyaWJ1dGUoXCJpZFwiKTtcbiAgICBhc3NlcnQoZ2FtZS5zaXR1YXRpb25zW2lkXSA9PT0gdW5kZWZpbmVkLCBcImV4aXN0aW5nX3NpdHVhdGlvblwiLmwoe2lkOmlkfSkpO1xuXG4gICAgdmFyIGNvbnRlbnQgPSAkc2l0dWF0aW9uLmlubmVySFRNTDtcbiAgICB2YXIgb3B0cyA9IHtcbiAgICAgIC8vIFNpdHVhdGlvbiBjb250ZW50XG4gICAgICBvcHRpb25UZXh0OiAkc2l0dWF0aW9uLmdldEF0dHJpYnV0ZShcImRhdGEtb3B0aW9uLXRleHRcIiksXG4gICAgICBjYW5WaWV3OiBwYXJzZUZuKCRzaXR1YXRpb24uZ2V0QXR0cmlidXRlKFwiZGF0YS1jYW4tdmlld1wiKSksXG4gICAgICBjYW5DaG9vc2U6IHBhcnNlRm4oJHNpdHVhdGlvbi5nZXRBdHRyaWJ1dGUoXCJkYXRhLWNhbi1jaG9vc2VcIikpLFxuICAgICAgcHJpb3JpdHk6IHBhcnNlKCRzaXR1YXRpb24uZ2V0QXR0cmlidXRlKFwiZGF0YS1wcmlvcml0eVwiKSksXG4gICAgICBmcmVxdWVuY3k6IHBhcnNlKCRzaXR1YXRpb24uZ2V0QXR0cmlidXRlKFwiZGF0YS1mcmVxdWVuY3lcIikpLFxuICAgICAgZGlzcGxheU9yZGVyOiBwYXJzZSgkc2l0dWF0aW9uLmdldEF0dHJpYnV0ZShcImRhdGEtZGlzcGxheS1vcmRlclwiKSksXG4gICAgICB0YWdzOiBwYXJzZUxpc3QoJHNpdHVhdGlvbi5nZXRBdHRyaWJ1dGUoXCJkYXRhLXRhZ3NcIiksIGZhbHNlKSxcbiAgICAgIC8vIFNpbXBsZSBTaXR1YXRpb24gY29udGVudC5cbiAgICAgIGhlYWRpbmc6ICRzaXR1YXRpb24uZ2V0QXR0cmlidXRlKFwiZGF0YS1oZWFkaW5nXCIpLFxuICAgICAgY2hvaWNlczogcGFyc2VMaXN0KCRzaXR1YXRpb24uZ2V0QXR0cmlidXRlKFwiZGF0YS1jaG9pY2VzXCIpLCB0cnVlKSxcbiAgICAgIG1pbkNob2ljZXM6IHBhcnNlKCRzaXR1YXRpb24uZ2V0QXR0cmlidXRlKFwiZGF0YS1taW4tY2hvaWNlc1wiKSksXG4gICAgICBtYXhDaG9pY2VzOiBwYXJzZSgkc2l0dWF0aW9uLmdldEF0dHJpYnV0ZShcImRhdGEtbWF4LWNob2ljZXNcIikpXG4gICAgfTtcblxuICAgIGdhbWUuc2l0dWF0aW9uc1tpZF0gPSBuZXcgU2ltcGxlU2l0dWF0aW9uKGNvbnRlbnQsIG9wdHMpO1xuICB9KTtcbn07XG5cblxuLyogT3V0cHV0cyByZWd1bGFyIGNvbnRlbnQgdG8gdGhlIHBhZ2UuIFVzZWQgYnkgd3JpdGUgYW5kXG4gKiB3cml0ZUJlZm9yZSwgdGhlIGxhc3QgdHdvIGFyZ3VtZW50cyBjb250cm9sIHdoYXQgalF1ZXJ5IG1ldGhvZHNcbiAqIGFyZSB1c2VkIHRvIGFkZCB0aGUgY29udGVudC5cbiAqL1xuLy8gVE9ETzogdGhpcyBmdW5jdGlvbiBjYW4gYXBwZW5kIHRleHQsIHByZXBlbmQgdGV4dCBvciByZXBsYWNlIHRleHQgaW4gc2VsZWN0b3Igd2l0aCB0aGUgc3VwcGxpZWQgb25lLlxudmFyIGRvV3JpdGUgPSBmdW5jdGlvbihjb250ZW50LCBzZWxlY3Rvcikge1xuICBjb250aW51ZU91dHB1dFRyYW5zYWN0aW9uKCk7XG4gIHZhciBvdXRwdXQgPSBhdWdtZW50TGlua3MoY29udGVudCk7XG4gIHZhciBlbGVtZW50O1xuICBpZiAoc2VsZWN0b3IpIGVsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKHNlbGVjdG9yKTtcbiAgaWYgKGVsZW1lbnQpIHtcbiAgICAvLyBUT0RPOiBzY3JvbGwgdG8gdGhlIGxhc3QgcG9zaXRpb25cbiAgICBkaW1lbnNpb25zID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICBjb25zb2xlLmxvZyhkaW1lbnNpb25zKTtcbiAgICB3aW5kb3cuc2Nyb2xsKDAsMTUwKTtcbiAgICAvLyBUT0RPOiBzY3JvbGxTdGFja1tzY3JvbGxTdGFjay5sZW5ndGgtMV0gPSBzY3JvbGxQb2ludDsqL1xuICB9XG4gIGlmICghZWxlbWVudCkge1xuICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb250ZW50JykuaW5uZXJIVE1MID0gb3V0cHV0O1xuICB9XG4gIGVsc2Uge1xuICAgIGVsZW1lbnQuYXBwZW5kQ2hpbGQob3V0cHV0KTtcbiAgfVxuICAvKiBXZSB3YW50IHRvIHNjcm9sbCB0aGlzIG5ldyBlbGVtZW50IHRvIHRoZSBib3R0b20gb2YgdGhlIHNjcmVlbi5cbiAgICogd2hpbGUgc3RpbGwgYmVpbmcgdmlzaWJsZS4gVGhlIGVhc2llc3Qgd2F5IGlzIHRvIGZpbmQgdGhlXG4gICAqIHRvcCBlZGdlIG9mIHRoZSAqZm9sbG93aW5nKiBlbGVtZW50IGFuZCBtb3ZlIHRoYXQgZXhhY3RseVxuICAgKiB0byB0aGUgYm90dG9tICh3aGlsZSBzdGlsbCBlbnN1cmluZyB0aGF0IHRoaXMgZWxlbWVudCBpcyBmdWxseVxuICAgKiB2aXNpYmxlLikgKi9cbiAgLyp2YXIgbmV4dGVsID0gb3V0cHV0Lmxhc3QoKS5uZXh0KCk7XG4gICAgdmFyIHNjcm9sbFBvaW50O1xuICAgIGlmICghbmV4dGVsLmxlbmd0aCkge1xuICAgIHNjcm9sbFBvaW50ID0gJChcIiNjb250ZW50XCIpLmhlaWdodCgpICsgJChcIiN0aXRsZVwiKS5oZWlnaHQoKSArIDYwO1xuICAgIH0gZWxzZSB7XG4gICAgc2Nyb2xsUG9pbnQgPSBuZXh0ZWwub2Zmc2V0KCkudG9wIC0gJCh3aW5kb3cpLmhlaWdodCgpO1xuICAgIH1cbiAgICBpZiAoc2Nyb2xsUG9pbnQgPiBvdXRwdXQub2Zmc2V0KCkudG9wKVxuICAgIHNjcm9sbFBvaW50ID0gb3V0cHV0Lm9mZnNldCgpLnRvcDtcbiAgICBzY3JvbGxTdGFja1tzY3JvbGxTdGFjay5sZW5ndGgtMV0gPSBzY3JvbGxQb2ludDsqL1xufTtcblxuLyogR2V0cyB0aGUgdW5pcXVlIGlkIHVzZWQgdG8gaWRlbnRpZnkgc2F2ZWQgZ2FtZXMuICovXG52YXIgZ2V0U2F2ZUlkID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiAndW5kdW1fJytnYW1lLmlkK1wiX1wiK2dhbWUudmVyc2lvbjtcbn07XG5cbi8qIEFkZHMgdGhlIHF1YWxpdHkgYmxvY2tzIHRvIHRoZSBjaGFyYWN0ZXIgdG9vbHMuICovXG52YXIgc2hvd1F1YWxpdGllcyA9IGZ1bmN0aW9uKCkge1xuICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInF1YWxpdGllc1wiKS5pbm5lckhUTUwgPSAnJztcbiAgZm9yICh2YXIgcXVhbGl0eUlkIGluIGNoYXJhY3Rlci5xdWFsaXRpZXMpIHtcbiAgICBhZGRRdWFsaXR5QmxvY2socXVhbGl0eUlkKTtcbiAgfVxufTtcblxuLyogRmFkZXMgaW4gYW5kIG91dCBhIGhpZ2hsaWdodCBvbiB0aGUgZ2l2ZW4gZWxlbWVudC4gKi9cbnZhciBzaG93SGlnaGxpZ2h0ID0gZnVuY3Rpb24oZG9tRWxlbWVudCkge1xuICB2YXIgaGlnaGxpZ2h0ID0gZG9tRWxlbWVudC5xdWVyeVNlbGVjdG9yKFwiLmhpZ2hsaWdodFwiKTtcbiAgaWYgKGhpZ2hsaWdodC5sZW5ndGggPD0gMCkge1xuICAgIGhpZ2hsaWdodCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCI8ZGl2PjwvZGl2PlwiKS5jbGFzc0xpc3QuYWRkKCdoaWdobGlnaHQnKTtcbiAgICBkb21FbGVtZW50LmFwcGVuZENoaWxkKGhpZ2hsaWdodCk7XG4gIH1cbiAgc2hvd0Jsb2NrKGhpZ2hsaWdodCk7XG4gIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgaGlkZUJsb2NrKGhpZ2hsaWdodCk7XG4gIH0sIDIwMDApO1xufTtcblxuLyogRmluZHMgdGhlIGNvcnJlY3QgbG9jYXRpb24gYW5kIGluc2VydHMgYSBwYXJ0aWN1bGFyIERPTSBlbGVtZW50XG4gKiBmaXRzIGludG8gYW4gZXhpc3RpbmcgbGlzdCBvZiBET00gZWxlbWVudHMuIFRoaXMgaXMgZG9uZSBieVxuICogcHJpb3JpdHkgb3JkZXIsIHNvIGFsbCBlbGVtZW50cyAoZXhpc3RpbmcgYW5kIG5ldykgbXVzdCBoYXZlXG4gKiB0aGVpciBkYXRhLXByaW9yaXR5IGF0dHJpYnV0ZSBzZXQuICovXG52YXIgaW5zZXJ0QXRDb3JyZWN0UG9zaXRpb24gPSBmdW5jdGlvbihwYXJlbnQsIG5ld0l0ZW0pIHtcbiAgdmFyIG5ld1ByaW9yaXR5ID0gbmV3SXRlbS5nZXRBdHRyaWJ1dGUoJ2RhdGEtcHJpb3JpdHknKTtcbiAgdmFyIF9jaGlsZHJlbiA9IHBhcmVudC5jaGlsZHJlbjtcbiAgaWYgKF9jaGlsZHJlbiAhPSB1bmRlZmluZWQpXG4gIHtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IF9jaGlsZHJlbi5sZW5ndGg7IGkrKykge1xuICAgICAgdmFyIGNoaWxkID0gX2NoaWxkcmVuW2ldO1xuICAgICAgaWYgKG5ld1ByaW9yaXR5IDwgY2hpbGQuZ2V0QXR0cmlidXRlKCdkYXRhLXByaW9yaXR5JykpIHtcbiAgICAgICAgY2hpbGQuYmVmb3JlKG5ld0l0ZW0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfVxuICAgIHBhcmVudC5hcHBlbmRDaGlsZChuZXdJdGVtKTtcbiAgfVxufTtcblxuLyogQWRkcyBhIG5ldyBncm91cCB0byB0aGUgY29ycmVjdCBsb2NhdGlvbiBpbiB0aGUgcXVhbGl0eSBsaXN0LiAqL1xudmFyIGFkZEdyb3VwQmxvY2sgPSBmdW5jdGlvbihncm91cElkKSB7XG4gIHZhciBncm91cERlZmluaXRpb24gPSBnYW1lLnF1YWxpdHlHcm91cHNbZ3JvdXBJZF07XG5cbiAgLy8gQnVpbGQgdGhlIGdyb3VwIGRpdiB3aXRoIGFwcHJvcHJpYXRlIGhlYWRpbmcuXG4gIHZhciBncm91cEJsb2NrID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJxdWFsaXR5X2dyb3VwXCIpLmNsb25lTm9kZSh0cnVlKTtcbiAgZ3JvdXBCbG9jay5zZXRBdHRyaWJ1dGUoXCJpZFwiLCBcImdfXCIrZ3JvdXBJZCk7XG4gIGdyb3VwQmxvY2suc2V0QXR0cmlidXRlKFwiZGF0YS1wcmlvcml0eVwiLCBncm91cERlZmluaXRpb24ucHJpb3JpdHkpO1xuXG4gIHZhciB0aXRsZUVsZW1lbnQgPSBncm91cEJsb2NrLnF1ZXJ5U2VsZWN0b3JBbGwoXCJbZGF0YS1hdHRyPSd0aXRsZSddXCIpO1xuICBpZiAoZ3JvdXBEZWZpbml0aW9uLnRpdGxlKSB7XG4gICAgdGl0bGVFbGVtZW50LmlubmVySFRNTCA9IGdyb3VwRGVmaW5pdGlvbi50aXRsZTtcbiAgfSBlbHNlIHtcbiAgICBpZiAodGl0bGVFbGVtZW50LnBhcmVudE5vZGUgIT0gdW5kZWZpbmVkKVxuICAgIHtcbiAgICAgIHRpdGxlRWxlbWVudC5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKHRpdGxlRWxlbWVudCk7XG4gICAgfVxuICB9XG5cbiAgaWYgKGdyb3VwRGVmaW5pdGlvbi5leHRyYUNsYXNzZXMpIHtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGdyb3VwRGVmaW5pdGlvbi5leHRyYUNsYXNzZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGdyb3VwQmxvY2suYWRkQ2xhc3MoZ3JvdXBEZWZpbml0aW9uLmV4dHJhQ2xhc3Nlc1tpXSk7XG4gICAgfVxuICB9XG5cbiAgLy8gQWRkIHRoZSBibG9jayB0byB0aGUgY29ycmVjdCBwbGFjZS5cbiAgdmFyIHF1YWxpdGllcyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicXVhbGl0aWVzXCIpO1xuICBpbnNlcnRBdENvcnJlY3RQb3NpdGlvbihxdWFsaXRpZXMsIGdyb3VwQmxvY2spO1xuICByZXR1cm4gZ3JvdXBCbG9jaztcbn07XG5cbi8qIEFkZHMgYSBuZXcgcXVhbGl0eSB0byB0aGUgY29ycmVjdCBsb2NhdGlvbiBpbiB0aGUgcXVhbGl0eSBsaXN0LiAqL1xudmFyIGFkZFF1YWxpdHlCbG9jayA9IGZ1bmN0aW9uKHF1YWxpdHlJZCkge1xuICAvLyBNYWtlIHN1cmUgd2Ugd2FudCB0byBkaXNwbGF5IHRoaXMgcXVhbGl0eS5cbiAgdmFyIHF1YWxpdHlEZWZpbml0aW9uID0gZ2FtZS5xdWFsaXRpZXNbcXVhbGl0eUlkXTtcbiAgaWYgKCFxdWFsaXR5RGVmaW5pdGlvbikge1xuICAgIHRocm93IG5ldyBFcnJvcihcIkNhbid0IGRpc3BsYXkgYSBxdWFsaXR5IHRoYXQgaGFzbid0IGJlZW4gZGVmaW5lZDogXCIrXG4gICAgICAgIHF1YWxpdHlJZCk7XG4gIH1cblxuICAvLyBXb3JrIG91dCBob3cgdGhlIHZhbHVlIHNob3VsZCBiZSBkaXNwbGF5ZWQuXG4gIHZhciBuYW1lID0gcXVhbGl0eURlZmluaXRpb24udGl0bGU7XG4gIHZhciB2YWwgPSBxdWFsaXR5RGVmaW5pdGlvbi5mb3JtYXQoXG4gICAgICBjaGFyYWN0ZXIsIGNoYXJhY3Rlci5xdWFsaXRpZXNbcXVhbGl0eUlkXVxuICAgICAgKTtcbiAgaWYgKHZhbCA9PT0gbnVsbCkgcmV0dXJuIG51bGw7XG5cbiAgLy8gQ3JlYXRlIHRoZSBxdWFsaXR5IG91dHB1dC5cbiAgdmFyIHF1YWxpdHlCbG9jayA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicXVhbGl0eVwiKS5jbG9uZU5vZGUodHJ1ZSk7XG4gIHF1YWxpdHlCbG9jay5zZXRBdHRyaWJ1dGUoXCJpZFwiLCBcInFfXCIrcXVhbGl0eUlkKTtcbiAgcXVhbGl0eUJsb2NrLnNldEF0dHJpYnV0ZShcImRhdGEtcHJpb3JpdHlcIiwgcXVhbGl0eURlZmluaXRpb24ucHJpb3JpdHkpO1xuICBxdWFsaXR5QmxvY2sucXVlcnlTZWxlY3RvckFsbChcIltkYXRhLWF0dHI9J25hbWUnXVwiKS5pbm5lckhUTUwgPSBuYW1lO1xuICBxdWFsaXR5QmxvY2sucXVlcnlTZWxlY3RvckFsbChcIltkYXRhLWF0dHI9J3ZhbHVlJ11cIikuaW5uZXJIVE1MID0gdmFsO1xuICBpZiAocXVhbGl0eURlZmluaXRpb24uZXh0cmFDbGFzc2VzKSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBxdWFsaXR5RGVmaW5pdGlvbi5leHRyYUNsYXNzZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHF1YWxpdHlCbG9jay5jbGFzc05hbWUuYWRkKHF1YWxpdHlEZWZpbml0aW9uLmV4dHJhQ2xhc3Nlc1tpXSk7XG4gICAgfVxuICB9XG5cbiAgLy8gRmluZCBvciBjcmVhdGUgdGhlIGdyb3VwIGJsb2NrLlxuICB2YXIgZ3JvdXBCbG9jaztcbiAgdmFyIGdyb3VwSWQgPSBxdWFsaXR5RGVmaW5pdGlvbi5ncm91cDtcbiAgaWYgKGdyb3VwSWQpIHtcbiAgICB2YXIgZ3JvdXAgPSBnYW1lLnF1YWxpdHlHcm91cHNbZ3JvdXBJZF07XG4gICAgYXNzZXJ0KGdyb3VwLCBcIm5vX2dyb3VwX2RlZmluaXRpb25cIi5sKHtpZDogZ3JvdXBJZH0pKTtcbiAgICBncm91cEJsb2NrID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJnX1wiK2dyb3VwSWQpO1xuICAgIGlmIChncm91cEJsb2NrID09IG51bGwgfHwgZ3JvdXBCbG9jay5sZW5ndGggPD0gMCkge1xuICAgICAgZ3JvdXBCbG9jayA9IGFkZEdyb3VwQmxvY2soZ3JvdXBJZCk7XG4gICAgfVxuICB9XG5cbiAgLy8gUG9zaXRpb24gaXQgY29ycmVjdGx5LlxuICB2YXIgZ3JvdXBRdWFsaXR5TGlzdCA9IGdyb3VwQmxvY2sucXVlcnlTZWxlY3RvckFsbChcIi5xdWFsaXRpZXNfaW5fZ3JvdXBcIik7XG4gIGluc2VydEF0Q29ycmVjdFBvc2l0aW9uKGdyb3VwUXVhbGl0eUxpc3QsIHF1YWxpdHlCbG9jayk7XG4gIHJldHVybiBxdWFsaXR5QmxvY2s7XG59O1xuXG4vKiBPdXRwdXQgZXZlbnRzIGFyZSB0cmFja2VkLCBzbyB3ZSBjYW4gbWFrZSBzdXJlIHdlIHNjcm9sbFxuICogY29ycmVjdGx5LiBXZSBkbyB0aGlzIGluIGEgc3RhY2sgYmVjYXVzZSBvbmUgY2xpY2sgbWlnaHQgY2F1c2VcbiAqIGEgY2hhaW4gcmVhY3Rpb24uIE9mIG91dHB1dCBldmVudHMsIG9ubHkgd2hlbiB3ZSByZXR1cm4gdG8gdGhlXG4gKiB0b3AgbGV2ZWwgd2lsbCB3ZSBkbyB0aGUgc2Nyb2xsLlxuICpcbiAqIEhvd2V2ZXIsIHRoYXQgbGVhdmVzIHRoZSBxdWVzdGlvbiBvZiB3aGVyZSB0byBzY3JvbGwgKnRvKi5cbiAqIChSZW1lbWJlciB0aGF0IGVsZW1lbnRzIGNvdWxkIGJlIGluc2VydGVkIGFueXdoZXJlIGluIHRoZVxuICogZG9jdW1lbnQuKSBXaGVuZXZlciB3ZSBkbyBhIHdyaXRlKCksIHdlJ2xsIGhhdmUgdG8gdXBkYXRlIHRoZVxuICogdG9wIChsYXN0KSBzdGFjayBlbGVtZW50IHRvIHRoYXQgcG9zaXRpb24uXG4gKi9cbnZhciBzY3JvbGxTdGFjayA9IFtdO1xudmFyIHBlbmRpbmdGaXJzdFdyaXRlID0gZmFsc2U7XG52YXIgc3RhcnRPdXRwdXRUcmFuc2FjdGlvbiA9IGZ1bmN0aW9uKCkge1xuICBpZiAoc2Nyb2xsU3RhY2subGVuZ3RoID09PSAwKSB7XG4gICAgcGVuZGluZ0ZpcnN0V3JpdGUgPSB0cnVlO1xuICB9XG4gIC8vIFRoZSBkZWZhdWx0IGlzIFwiYWxsIHRoZSB3YXkgZG93blwiLlxuICBzY3JvbGxTdGFjay5wdXNoKFxuICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwiY29udGVudFwiKS5vZmZzZXRIZWlnaHQgKyBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInRpdGxlXCIpLm9mZnNldEhlaWdodCArIDYwXG4gICk7XG59O1xudmFyIGNvbnRpbnVlT3V0cHV0VHJhbnNhY3Rpb24gPSBmdW5jdGlvbigpIHtcbiAgaWYgKHBlbmRpbmdGaXJzdFdyaXRlKSB7XG4gICAgcGVuZGluZ0ZpcnN0V3JpdGUgPSBmYWxzZTtcbiAgICB2YXIgc2VwYXJhdG9yID0gJChcIiN1aV9saWJyYXJ5ICN0dXJuX3NlcGFyYXRvclwiKS5jbG9uZSgpO1xuICAgIHNlcGFyYXRvci5yZW1vdmVBdHRyKFwiaWRcIik7XG4gICAgJChcIiNjb250ZW50XCIpLmFwcGVuZChzZXBhcmF0b3IpO1xuICB9XG59O1xudmFyIGVuZE91dHB1dFRyYW5zYWN0aW9uID0gZnVuY3Rpb24oKSB7XG4gIHZhciBzY3JvbGxQb2ludCA9IHNjcm9sbFN0YWNrLnBvcCgpO1xuICBpZiAoc2Nyb2xsU3RhY2subGVuZ3RoID09PSAwICYmIHNjcm9sbFBvaW50ICE9PSBudWxsKSB7XG4gICAgaWYgKGludGVyYWN0aXZlKSB7XG4gICAgICB3aW5kb3cuc2Nyb2xsKDAsc2Nyb2xsUG9pbnQpO1xuICAgIH1cbiAgICBzY3JvbGxQb2ludCA9IG51bGw7XG4gIH1cbn07XG5cbi8qIFRoaXMgZ2V0cyBjYWxsZWQgd2hlbiBhIGxpbmsgbmVlZHMgdG8gYmUgZm9sbG93ZWQsIHJlZ2FyZGxlc3NcbiAqIG9mIHdoZXRoZXIgaXQgd2FzIHVzZXIgYWN0aW9uIHRoYXQgaW5pdGlhdGVkIGl0LiAqL1xudmFyIGxpbmtSZSA9IC9eKFthLXowLTlfLV0rfFxcLikoXFwvKFswLTlhLXpfLV0rKSk/JC87XG52YXIgcHJvY2Vzc0xpbmsgPSBmdW5jdGlvbihjb2RlKSB7XG4gIC8vIENoZWNrIGlmIHdlIHNob3VsZCBkbyB0aGlzIG5vdywgb3IgaWYgcHJvY2Vzc2luZyBpcyBhbHJlYWR5XG4gIC8vIHVuZGVyd2F5LlxuICBpZiAobGlua1N0YWNrICE9PSBudWxsKSB7XG4gICAgbGlua1N0YWNrLnB1c2goY29kZSk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gVHJhY2sgd2hlcmUgd2UncmUgYWJvdXQgdG8gYWRkIG5ldyBjb250ZW50LlxuICBzdGFydE91dHB1dFRyYW5zYWN0aW9uKCk7XG5cbiAgLy8gV2UncmUgcHJvY2Vzc2luZywgc28gbWFrZSB0aGUgc3RhY2sgYXZhaWxhYmxlLlxuICBsaW5rU3RhY2sgPSBbXTtcblxuICAvLyBIYW5kbGUgZWFjaCBsaW5rIGluIHR1cm4uXG4gIHByb2Nlc3NPbmVMaW5rKGNvZGUpO1xuICB3aGlsZSAobGlua1N0YWNrLmxlbmd0aCA+IDApIHtcbiAgICBjb2RlID0gbGlua1N0YWNrLnNoaWZ0KCk7XG4gICAgcHJvY2Vzc09uZUxpbmsoY29kZSk7XG4gIH1cblxuICAvLyBXZSdyZSBkb25lLCBzbyByZW1vdmUgdGhlIHN0YWNrIHRvIHByZXZlbnQgZnV0dXJlIHB1c2hlcy5cbiAgbGlua1N0YWNrID0gbnVsbDtcblxuICAvLyBTY3JvbGwgdG8gdGhlIHRvcCBvZiB0aGUgbmV3IGNvbnRlbnQuXG4gIGVuZE91dHB1dFRyYW5zYWN0aW9uKCk7XG5cbiAgLy8gV2UncmUgYWJsZSB0byBzYXZlLCBpZiB3ZSB3ZXJlbid0IGFscmVhZHkuXG4gIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwic2F2ZVwiKS5zZXRBdHRyaWJ1dGUoJ2Rpc2FibGVkJywgZmFsc2UpO1xufTtcblxuLyogVGhpcyBnZXRzIGNhbGxlZCB0byBhY3R1YWxseSBkbyB0aGUgd29yayBvZiBwcm9jZXNzaW5nIGEgY29kZS5cbiAqIFdoZW4gb25lIGRvTGluayBpcyBjYWxsZWQgKG9yIGEgbGluayBpcyBjbGlja2VkKSwgdGhpcyBtYXkgc2V0IGNhbGxcbiAqIGNvZGUgdGhhdCBmdXJ0aGVyIGNhbGxzIGRvTGluaywgYW5kIHNvIG9uLiBUaGlzIG1ldGhvZCBwcm9jZXNzZXNcbiAqIGVhY2ggb25lLCBhbmQgcHJvY2Vzc0xpbmsgbWFuYWdlcyB0aGlzLlxuICovXG52YXIgcHJvY2Vzc09uZUxpbmsgPSBmdW5jdGlvbihjb2RlKSB7XG4gIHZhciBtYXRjaCA9IGNvZGUubWF0Y2gobGlua1JlKTtcbiAgYXNzZXJ0KG1hdGNoLCBcImxpbmtfbm90X3ZhbGlkXCIubCh7bGluazpjb2RlfSkpO1xuXG4gIHZhciBzaXR1YXRpb24gPSBtYXRjaFsxXTtcbiAgdmFyIGFjdGlvbiA9IG1hdGNoWzNdO1xuXG4gIC8vIENoYW5nZSB0aGUgc2l0dWF0aW9uXG4gIGlmIChzaXR1YXRpb24gIT09ICcuJykge1xuICAgIGlmIChzaXR1YXRpb24gIT09IGN1cnJlbnQpIHtcbiAgICAgIGRvVHJhbnNpdGlvblRvKHNpdHVhdGlvbik7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIC8vIFdlIHNob3VsZCBoYXZlIGFuIGFjdGlvbiBpZiB3ZSBoYXZlIG5vIHNpdHVhdGlvbiBjaGFuZ2UuXG4gICAgYXNzZXJ0KGFjdGlvbiwgXCJsaW5rX25vX2FjdGlvblwiLmwoKSk7XG4gIH1cblxuICAvLyBDYXJyeSBvdXQgdGhlIGFjdGlvblxuICBpZiAoYWN0aW9uKSB7XG4gICAgc2l0dWF0aW9uID0gZ2V0Q3VycmVudFNpdHVhdGlvbigpO1xuICAgIGlmIChzaXR1YXRpb24pIHtcbiAgICAgIGlmIChnYW1lLmJlZm9yZUFjdGlvbikge1xuICAgICAgICAvLyBUcnkgdGhlIGdsb2JhbCBhY3QgaGFuZGxlciwgYW5kIHNlZSBpZiB3ZSBuZWVkXG4gICAgICAgIC8vIHRvIG5vdGlmeSB0aGUgc2l0dWF0aW9uLlxuICAgICAgICB2YXIgY29uc3VtZWQgPSBnYW1lLmJlZm9yZUFjdGlvbihcbiAgICAgICAgICAgIGNoYXJhY3Rlciwgc3lzdGVtLCBjdXJyZW50LCBhY3Rpb25cbiAgICAgICAgICAgICk7XG4gICAgICAgIGlmIChjb25zdW1lZCAhPT0gdHJ1ZSkge1xuICAgICAgICAgIHNpdHVhdGlvbi5hY3QoY2hhcmFjdGVyLCBzeXN0ZW0sIGFjdGlvbik7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIFdlIGhhdmUgbm8gZ2xvYmFsIGFjdCBoYW5kbGVyLCBhbHdheXMgbm90aWZ5XG4gICAgICAgIC8vIHRoZSBzaXR1YXRpb24uXG4gICAgICAgIHNpdHVhdGlvbi5hY3QoY2hhcmFjdGVyLCBzeXN0ZW0sIGFjdGlvbik7XG4gICAgICB9XG4gICAgICBpZiAoZ2FtZS5hZnRlckFjdGlvbikge1xuICAgICAgICBnYW1lLmFmdGVyQWN0aW9uKGNoYXJhY3Rlciwgc3lzdGVtLCBjdXJyZW50LCBhY3Rpb24pO1xuICAgICAgfVxuICAgIH1cbiAgfVxufTtcblxuLyogVGhpcyBmdW5jdGlvbiBsaXN0ZW5zIG9uIGNvbnRlbnQgYmxvY2sgdG8gZmlsdGVyIG91dCBsaW5rIGNsaWNrcy4gKi9cbnZhciBsaW5rQ2xpY2tIYW5kbGVyID0gZnVuY3Rpb24oZXZlbnQpIHtcbiAgaWYgKGV2ZW50LnRhcmdldC50YWdOYW1lLnRvTG93ZXJDYXNlKCkgPT09ICdhJykge1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgcHJvY2Vzc0NsaWNrKGV2ZW50LnRhcmdldC5ocmVmKTtcbiAgfVxufVxuXG4vKiBUaGlzIGdldHMgY2FsbGVkIHdoZW4gdGhlIHVzZXIgY2xpY2tzIGEgbGluayB0byBjYXJyeSBvdXQgYW5cbiAqIGFjdGlvbi4gKi9cbnZhciBwcm9jZXNzQ2xpY2sgPSBmdW5jdGlvbihjb2RlKSB7XG4gIHZhciBub3cgPSAobmV3IERhdGUoKSkuZ2V0VGltZSgpICogMC4wMDE7XG4gIHN5c3RlbS50aW1lID0gbm93IC0gc3RhcnRUaW1lO1xuICBwcm9ncmVzcy5zZXF1ZW5jZS5wdXNoKHtsaW5rOmNvZGUsIHdoZW46c3lzdGVtLnRpbWV9KTtcbiAgcmV0dXJuIHByb2Nlc3NMaW5rKGNvZGUpO1xufTtcblxuLyogVHJhbnNpdGlvbnMgYmV0d2VlbiBzaXR1YXRpb25zLiAqL1xudmFyIGRvVHJhbnNpdGlvblRvID0gZnVuY3Rpb24obmV3U2l0dWF0aW9uSWQpIHtcbiAgdmFyIG9sZFNpdHVhdGlvbklkID0gY3VycmVudDtcbiAgdmFyIG9sZFNpdHVhdGlvbiA9IGdldEN1cnJlbnRTaXR1YXRpb24oKTtcbiAgdmFyIG5ld1NpdHVhdGlvbiA9IGdhbWUuc2l0dWF0aW9uc1tuZXdTaXR1YXRpb25JZF07XG5cbiAgYXNzZXJ0KG5ld1NpdHVhdGlvbiwgXCJ1bmtub3duX3NpdHVhdGlvblwiLmwoe2lkOm5ld1NpdHVhdGlvbklkfSkpO1xuXG4gIC8vIFdlIG1pZ2h0IG5vdCBoYXZlIGFuIG9sZCBzaXR1YXRpb24gaWYgdGhpcyBpcyB0aGUgc3RhcnQgb2ZcbiAgLy8gdGhlIGdhbWUuXG4gIGlmIChvbGRTaXR1YXRpb24pIHtcbiAgICAvLyBOb3RpZnkgdGhlIGV4aXRpbmcgc2l0dWF0aW9uLlxuICAgIG9sZFNpdHVhdGlvbi5leGl0KGNoYXJhY3Rlciwgc3lzdGVtLCBuZXdTaXR1YXRpb25JZCk7XG4gICAgaWYgKGdhbWUuZXhpdCkge1xuICAgICAgZ2FtZS5leGl0KGNoYXJhY3Rlciwgc3lzdGVtLCBvbGRTaXR1YXRpb25JZCwgbmV3U2l0dWF0aW9uSWQpO1xuICAgIH1cbiAgfVxuXG4gIC8vICBSZW1vdmUgbGlua3MgYW5kIHRyYW5zaWVudCBzZWN0aW9ucy5cbiAgdmFyIGNvbnRlbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcImNvbnRlbnRcIik7XG4gIGxpbmtzID0gY29udGVudC5xdWVyeVNlbGVjdG9yQWxsKFwiYVwiKTtcbiAgQXJyYXkucHJvdG90eXBlLmZvckVhY2guY2FsbChsaW5rcywgZnVuY3Rpb24oZWxlbWVudCwgaW5kZXgpIHtcbiAgICB2YXIgYSA9IGVsZW1lbnQ7XG4gICAgaWYgKGEuY2xhc3NMaXN0LmNvbnRhaW5zKCdzdGlja3knKSB8fCBhLmdldEF0dHJpYnV0ZShcImhyZWZcIikubWF0Y2goL1s/Jl1zdGlja3lbPSZdPy8pKVxuICAgICAgcmV0dXJuO1xuICAgIGlmIChhLmdldEF0dHJpYnV0ZShcImhyZWZcIikubWF0Y2goL1s/Jl10cmFuc2llbnRbPSZdPy8pKSB7XG4gICAgICBoaWRlQmxvY2soYSk7XG4gICAgfVxuICAgIGEuaW5uZXJIVE1MID0gXCI8c3BhbiBjbGFzcz0nZXhfbGluayc+XCIrYS5pbm5lckhUTUwrXCI8L3NwYW4+XCI7XG4gIH0pO1xuICBoaWRlQmxvY2soY29udGVudC5xdWVyeVNlbGVjdG9yQWxsKFwiLnRyYW5zaWVudFwiKSk7XG4gIGhpZGVCbG9jayhjb250ZW50LnF1ZXJ5U2VsZWN0b3JBbGwoXCJ1bC5vcHRpb25zXCIpKTtcblxuICAvLyBNb3ZlIHRoZSBjaGFyYWN0ZXIuXG4gIGN1cnJlbnQgPSBuZXdTaXR1YXRpb25JZDtcblxuICAvLyBOb3RpZnkgdGhlIGluY29taW5nIHNpdHVhdGlvbi5cbiAgaWYgKGdhbWUuZW50ZXIpIHtcbiAgICBnYW1lLmVudGVyKGNoYXJhY3Rlciwgc3lzdGVtLCBvbGRTaXR1YXRpb25JZCwgbmV3U2l0dWF0aW9uSWQpO1xuICB9XG4gIG5ld1NpdHVhdGlvbi5lbnRlcihjaGFyYWN0ZXIsIHN5c3RlbSwgb2xkU2l0dWF0aW9uSWQpO1xuXG4gIC8vIGFkZGl0aW9uYWwgaG9vayBmb3Igd2hlbiB0aGUgc2l0dWF0aW9uIHRleHQgaGFzIGFscmVhZHkgYmVlbiBwcmludGVkXG4gIGlmIChnYW1lLmFmdGVyRW50ZXIpIHtcbiAgICBnYW1lLmFmdGVyRW50ZXIoY2hhcmFjdGVyLCBzeXN0ZW0sIG9sZFNpdHVhdGlvbklkLCBuZXdTaXR1YXRpb25JZCk7XG4gIH1cbn07XG5cbi8qIFJldHVybnMgSFRNTCBmcm9tIHRoZSBnaXZlbiBjb250ZW50IHdpdGggdGhlIG5vbi1yYXcgbGlua3NcbiAqIHdpcmVkIHVwLiBcbiAqIEBwYXJhbSBjb250ZW50IHN0cmluZyBIVE1MIGNvZGUgXG4gKiBAcmV0dmFsIHN0cmluZyAqL1xudmFyIGF1Z21lbnRMaW5rcyA9IGZ1bmN0aW9uKGNvbnRlbnQpIHtcbiAgLy8gV2lyZSB1cCB0aGUgbGlua3MgZm9yIHJlZ3VsYXIgPGE+IHRhZ3MuXG4gIG91dHB1dCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICBvdXRwdXQuaW5uZXJIVE1MID0gY29udGVudDtcbiAgdmFyIGxpbmtzID0gb3V0cHV0LnF1ZXJ5U2VsZWN0b3JBbGwoXCJhXCIpO1xuICBBcnJheS5wcm90b3R5cGUuZm9yRWFjaC5jYWxsKGxpbmtzLCBmdW5jdGlvbihlbGVtZW50LCBpbmRleCl7XG4gICAgdmFyIGhyZWYgPSBlbGVtZW50LmdldEF0dHJpYnV0ZSgnaHJlZicpO1xuICAgIGlmICghZWxlbWVudC5jbGFzc0xpc3QuY29udGFpbnMoXCJyYXdcIil8fCBocmVmLm1hdGNoKC9bPyZdcmF3Wz0mXT8vKSkge1xuICAgICAgaWYgKGhyZWYubWF0Y2gobGlua1JlKSkge1xuICAgICAgICBlbGVtZW50Lm9uY2xpY2sgPSBmdW5jdGlvbihldmVudCkge1xuICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cbiAgICAgICAgICAvLyBJZiB3ZSdyZSBhIG9uY2UtY2xpY2ssIHJlbW92ZSBhbGwgbWF0Y2hpbmcgbGlua3MuXG4gICAgICAgICAgaWYgKGVsZW1lbnQuY2xhc3NMaXN0LmNvbnRhaW5zKFwib25jZVwiKSB8fCBocmVmLm1hdGNoKC9bPyZdb25jZVs9Jl0/LykpIHtcbiAgICAgICAgICAgIHN5c3RlbS5jbGVhckxpbmtzKGhyZWYpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHByb2Nlc3NDbGljayhocmVmKTtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBlbGVtZW50LmNsYXNzTGlzdC5hZGQoXCJyYXdcIik7XG4gICAgICB9XG4gICAgfVxuICB9KTtcblxuICByZXR1cm4gb3V0cHV0LmlubmVySFRNTDtcbn07XG5cbi8qIEVyYXNlcyB0aGUgY2hhcmFjdGVyIGluIGxvY2FsIHN0b3JhZ2UuIFRoaXMgaXMgcGVybWFuZW50ISAqL1xudmFyIGRvRXJhc2UgPSBmdW5jdGlvbihmb3JjZSkge1xuICB2YXIgc2F2ZUlkID0gZ2V0U2F2ZUlkKCk7XG4gIGlmIChsb2NhbFN0b3JhZ2Vbc2F2ZUlkXSkge1xuICAgIGlmIChmb3JjZSB8fCBjb25maXJtKFwiZXJhc2VfbWVzc2FnZVwiLmwoKSkpIHtcbiAgICAgIGRlbGV0ZSBsb2NhbFN0b3JhZ2Vbc2F2ZUlkXTtcbiAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwiZXJhc2VcIikuc2V0QXR0cmlidXRlKCdkaXNhYmxlZCcsIHRydWUpO1xuICAgICAgc3RhcnRHYW1lKCk7XG4gICAgfVxuICB9XG59O1xuXG4vKiBGaW5kIGFuZCByZXR1cm4gYSBsaXN0IG9mIGlkcyBmb3IgYWxsIHNpdHVhdGlvbnMgd2l0aCB0aGUgZ2l2ZW4gdGFnLiAqL1xudmFyIGdldFNpdHVhdGlvbklkc1dpdGhUYWcgPSBmdW5jdGlvbih0YWcpIHtcbiAgdmFyIHJlc3VsdCA9IFtdO1xuICBmb3IgKHZhciBzaXR1YXRpb25JZCBpbiBnYW1lLnNpdHVhdGlvbnMpIHtcbiAgICB2YXIgc2l0dWF0aW9uID0gZ2FtZS5zaXR1YXRpb25zW3NpdHVhdGlvbklkXTtcblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc2l0dWF0aW9uLnRhZ3MubGVuZ3RoOyArK2kpIHtcbiAgICAgIGlmIChzaXR1YXRpb24udGFnc1tpXSA9PSB0YWcpIHtcbiAgICAgICAgcmVzdWx0LnB1c2goc2l0dWF0aW9uSWQpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn07XG5cbi8qIFNldCB1cCB0aGUgc2NyZWVuIGZyb20gc2NyYXRjaCB0byByZWZsZWN0IHRoZSBjdXJyZW50IGdhbWVcbiAqIHN0YXRlLiAqL1xudmFyIGluaXRHYW1lRGlzcGxheSA9IGZ1bmN0aW9uKCkge1xuICAvLyBUcmFuc2l0aW9uIGludG8gdGhlIGZpcnN0IHNpdHVhdGlvbixcbiAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJjb250ZW50XCIpLmlubmVySFRNTCA9IFwiXCI7XG5cbiAgdmFyIHNpdHVhdGlvbiA9IGdldEN1cnJlbnRTaXR1YXRpb24oKTtcbiAgYXNzZXJ0KHNpdHVhdGlvbiwgXCJub19jdXJyZW50X3NpdHVhdGlvblwiLmwoKSk7XG5cbiAgc2hvd1F1YWxpdGllcygpO1xufTtcblxuLyogQ2xlYXIgdGhlIGN1cnJlbnQgZ2FtZSBvdXRwdXQgYW5kIHN0YXJ0IGFnYWluLiAqL1xudmFyIHN0YXJ0R2FtZSA9IGZ1bmN0aW9uKCkge1xuICBwcm9ncmVzcy5zZWVkID0gbmV3IERhdGUoKS50b1N0cmluZygpO1xuXG4gIGNoYXJhY3RlciA9IG5ldyBDaGFyYWN0ZXIoKTtcbiAgc3lzdGVtLnJuZCA9IG5ldyBSYW5kb20ocHJvZ3Jlc3Muc2VlZCk7XG4gIHByb2dyZXNzLnNlcXVlbmNlID0gW3tsaW5rOmdhbWUuc3RhcnQsIHdoZW46MH1dO1xuXG4gIC8vIEVtcHR5IHRoZSBkaXNwbGF5XG4gIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwiY29udGVudFwiKS5pbm5lckhUTUwgPSAnJztcblxuICAvLyBTdGFydCB0aGUgZ2FtZVxuICBzdGFydFRpbWUgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKSAqIDAuMDAxO1xuICBzeXN0ZW0udGltZSA9IDA7XG4gIGlmIChnYW1lLmluaXQpIGdhbWUuaW5pdChjaGFyYWN0ZXIsIHN5c3RlbSk7XG4gIHNob3dRdWFsaXRpZXMoKTtcblxuICAvLyBEbyB0aGUgZmlyc3Qgc3RhdGUuXG4gIGRvVHJhbnNpdGlvblRvKGdhbWUuc3RhcnQpO1xufTtcblxuLyogU2F2ZXMgdGhlIGNoYXJhY3RlciB0byBsb2NhbCBzdG9yYWdlLiAqL1xudmFyIHNhdmVHYW1lID0gZnVuY3Rpb24oKSB7XG4gIC8vIFN0b3JlIHdoZW4gd2UncmUgc2F2aW5nIHRoZSBnYW1lLCB0byBhdm9pZCBleHBsb2l0cyB3aGVyZSBhXG4gIC8vIHBsYXllciBsb2FkcyB0aGVpciBmaWxlIHRvIGdhaW4gZXh0cmEgdGltZS5cbiAgdmFyIG5vdyA9IChuZXcgRGF0ZSgpKS5nZXRUaW1lKCkgKiAwLjAwMTtcbiAgcHJvZ3Jlc3Muc2F2ZVRpbWUgPSBub3cgLSBzdGFydFRpbWU7XG5cbiAgLy8gU2F2ZSB0aGUgZ2FtZS5cbiAgbG9jYWxTdG9yYWdlW2dldFNhdmVJZCgpXSA9IEpTT04uc3RyaW5naWZ5KHByb2dyZXNzKTtcblxuICAvLyBTd2l0Y2ggdGhlIGJ1dHRvbiBoaWdobGlnaHRzLlxuICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcImVyYXNlXCIpLnNldEF0dHJpYnV0ZSgnZGlzYWJsZWQnLCBmYWxzZSk7XG4gIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwic2F2ZVwiKS5zZXRBdHRyaWJ1dGUoJ2Rpc2FibGVkJywgdHJ1ZSk7XG59O1xuXG4vKiBMb2FkcyB0aGUgZ2FtZSBmcm9tIHRoZSBnaXZlbiBkYXRhICovXG52YXIgbG9hZEdhbWUgPSBmdW5jdGlvbihjaGFyYWN0ZXJEYXRhKSB7XG4gIHByb2dyZXNzID0gY2hhcmFjdGVyRGF0YTtcblxuICBjaGFyYWN0ZXIgPSBuZXcgQ2hhcmFjdGVyKCk7XG4gIHN5c3RlbS5ybmQgPSBuZXcgUmFuZG9tKHByb2dyZXNzLnNlZWQpO1xuXG4gIC8vIEVtcHR5IHRoZSBkaXNwbGF5XG4gIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwiY29udGVudFwiKS5pbm5lckhUTUwgPSBcIlwiO1xuICBzaG93UXVhbGl0aWVzKCk7XG5cbiAgLy8gTm93IHBsYXkgdGhyb3VnaCB0aGUgYWN0aW9ucyBzbyBmYXI6XG4gIGlmIChnYW1lLmluaXQpIGdhbWUuaW5pdChjaGFyYWN0ZXIsIHN5c3RlbSk7XG5cbiAgLy8gUnVuIHRocm91Z2ggYWxsIHRoZSBwbGF5ZXIncyBoaXN0b3J5LlxuICBpbnRlcmFjdGl2ZSA9IGZhbHNlO1xuICBmb3IgKHZhciBpID0gMDsgaSA8IHByb2dyZXNzLnNlcXVlbmNlLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIHN0ZXAgPSBwcm9ncmVzcy5zZXF1ZW5jZVtpXTtcbiAgICAvLyBUaGUgYWN0aW9uIG11c3QgYmUgZG9uZSBhdCB0aGUgcmVjb3JkZWQgdGltZS5cbiAgICBzeXN0ZW0udGltZSA9IHN0ZXAud2hlbjtcbiAgICBwcm9jZXNzTGluayhzdGVwLmxpbmspO1xuICB9XG4gIGludGVyYWN0aXZlID0gdHJ1ZTtcblxuICAvLyBSZXZlcnNlIGVuZ2luZWVyIHRoZSBzdGFydCB0aW1lLlxuICB2YXIgbm93ID0gbmV3IERhdGUoKS5nZXRUaW1lKCkgKiAwLjAwMTtcbiAgc3RhcnRUaW1lID0gbm93IC0gcHJvZ3Jlc3Muc2F2ZVRpbWU7XG5cbiAgLy8gQmVjYXVzZSB3ZSBkaWQgdGhlIHJ1biB0aHJvdWdoIG5vbi1pbnRlcmFjdGl2ZWx5LCBub3cgd2VcbiAgLy8gbmVlZCB0byB1cGRhdGUgdGhlIFVJLlxuICBzaG93UXVhbGl0aWVzKCk7XG59O1xuXG4vLyBJbnRlcm5hdGlvbmFsaXphdGlvbiBzdXBwb3J0IGJhc2VkIG9uIHRoZSBjb2RlIHByb3ZpZGVkIGJ5IE9yZW9sZWsuXG4oZnVuY3Rpb24oKSB7XG4gdmFyIGNvZGVzVG9UcnkgPSB7fTtcbiAvKiBDb21waWxlcyBhIGxpc3Qgb2YgZmFsbGJhY2sgbGFuZ3VhZ2VzIHRvIHRyeSBpZiB0aGUgZ2l2ZW4gY29kZVxuICAqIGRvZXNuJ3QgaGF2ZSB0aGUgbWVzc2FnZSB3ZSBuZWVkLiBDYWNoZXMgaXQgZm9yIGZ1dHVyZSB1c2UuICovXG4gdmFyIGdldENvZGVzVG9UcnkgPSBmdW5jdGlvbihsYW5ndWFnZUNvZGUpIHtcbiB2YXIgY29kZUFycmF5ID0gY29kZXNUb1RyeVtsYW5ndWFnZUNvZGVdO1xuIGlmIChjb2RlQXJyYXkpIHJldHVybiBjb2RlQXJyYXk7XG5cbiBjb2RlQXJyYXkgPSBbXTtcbiBpZiAobGFuZ3VhZ2VDb2RlIGluIHVuZHVtLmxhbmd1YWdlKSB7XG4gY29kZUFycmF5LnB1c2gobGFuZ3VhZ2VDb2RlKTtcbiB9XG4gdmFyIGVsZW1lbnRzID0gbGFuZ3VhZ2VDb2RlLnNwbGl0KCctJyk7XG4gZm9yICh2YXIgaSA9IGVsZW1lbnRzLmxlbmd0aC0yOyBpID4gMDsgaS0tKSB7XG4gdmFyIHRoaXNDb2RlID0gZWxlbWVudHMuc2xpY2UoMCwgaSkuam9pbignLScpO1xuIGlmICh0aGlzQ29kZSBpbiB1bmR1bS5sYW5ndWFnZSkge1xuIGNvZGVBcnJheS5wdXNoKHRoaXNDb2RlKTtcbiB9XG4gfVxuIGNvZGVBcnJheS5wdXNoKFwiXCIpO1xuIGNvZGVzVG9UcnlbbGFuZ3VhZ2VDb2RlXSA9IGNvZGVBcnJheTtcbiByZXR1cm4gY29kZUFycmF5O1xuIH07XG4gdmFyIGxvb2t1cCA9IGZ1bmN0aW9uKGxhbmd1YWdlQ29kZSwgbWVzc2FnZSkge1xuICAgdmFyIGxhbmd1YWdlRGF0YSA9IHVuZHVtLmxhbmd1YWdlW2xhbmd1YWdlQ29kZV07XG4gICBpZiAoIWxhbmd1YWdlRGF0YSkgcmV0dXJuIG51bGw7XG4gICByZXR1cm4gbGFuZ3VhZ2VEYXRhW21lc3NhZ2VdO1xuIH07XG4gdmFyIGxvY2FsaXplID0gZnVuY3Rpb24obGFuZ3VhZ2VDb2RlLCBtZXNzYWdlKSB7XG4gICB2YXIgbG9jYWxpemVkLCB0aGlzQ29kZTtcbiAgIHZhciBsYW5ndWFnZUNvZGVzID0gZ2V0Q29kZXNUb1RyeShsYW5ndWFnZUNvZGUpO1xuICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsYW5ndWFnZUNvZGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgIHRoaXNDb2RlID0gbGFuZ3VhZ2VDb2Rlc1tpXTtcbiAgICAgbG9jYWxpemVkID0gbG9va3VwKHRoaXNDb2RlLCBtZXNzYWdlKTtcbiAgICAgaWYgKGxvY2FsaXplZCkgcmV0dXJuIGxvY2FsaXplZDtcbiAgIH1cbiAgIHJldHVybiBtZXNzYWdlO1xuIH07XG5cbiAvLyBBUElcbiBTdHJpbmcucHJvdG90eXBlLmwgPSBmdW5jdGlvbihhcmdzKSB7XG4gICAvLyBHZXQgbGFuZyBhdHRyaWJ1dGUgZnJvbSBodG1sIHRhZy5cbiAgIHZhciBsYW5nID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcImh0bWxcIikuZ2V0QXR0cmlidXRlKFwibGFuZ1wiKSB8fCBcIlwiO1xuXG4gICAvLyBGaW5kIHRoZSBsb2NhbGl6ZWQgZm9ybS5cbiAgIHZhciBsb2NhbGl6ZWQgPSBsb2NhbGl6ZShsYW5nLCB0aGlzKTtcblxuICAgLy8gTWVyZ2UgaW4gYW55IHJlcGxhY2VtZW50IGNvbnRlbnQuXG4gICBpZiAoYXJncykge1xuICAgICBmb3IgKHZhciBuYW1lIGluIGFyZ3MpIHtcbiAgICAgICBsb2NhbGl6ZWQgPSBsb2NhbGl6ZWQucmVwbGFjZShcbiAgICAgICAgICAgbmV3IFJlZ0V4cChcIlxcXFx7XCIrbmFtZStcIlxcXFx9XCIpLCBhcmdzW25hbWVdXG4gICAgICAgICAgICk7XG4gICAgIH1cbiAgIH1cbiAgIHJldHVybiBsb2NhbGl6ZWQ7XG4gfTtcbn0pKCk7XG5cbi8vIFJhbmRvbSBOdW1iZXIgZ2VuZXJhdGlvbiBiYXNlZCBvbiBzZWVkcmFuZG9tLmpzIGNvZGUgYnkgRGF2aWQgQmF1LlxuLy8gQ29weXJpZ2h0IDIwMTAgRGF2aWQgQmF1LCBhbGwgcmlnaHRzIHJlc2VydmVkLlxuLy9cbi8vIFJlZGlzdHJpYnV0aW9uIGFuZCB1c2UgaW4gc291cmNlIGFuZCBiaW5hcnkgZm9ybXMsIHdpdGggb3Jcbi8vIHdpdGhvdXQgbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZ1xuLy8gY29uZGl0aW9ucyBhcmUgbWV0OlxuLy9cbi8vICAgMS4gUmVkaXN0cmlidXRpb25zIG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZVxuLy8gICAgICBjb3B5cmlnaHQgbm90aWNlLCB0aGlzIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlXG4vLyAgICAgIGZvbGxvd2luZyBkaXNjbGFpbWVyLlxuLy9cbi8vICAgMi4gUmVkaXN0cmlidXRpb25zIGluIGJpbmFyeSBmb3JtIG11c3QgcmVwcm9kdWNlIHRoZSBhYm92ZVxuLy8gICAgICBjb3B5cmlnaHQgbm90aWNlLCB0aGlzIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlXG4vLyAgICAgIGZvbGxvd2luZyBkaXNjbGFpbWVyIGluIHRoZSBkb2N1bWVudGF0aW9uIGFuZC9vciBvdGhlclxuLy8gICAgICBtYXRlcmlhbHMgcHJvdmlkZWQgd2l0aCB0aGUgZGlzdHJpYnV0aW9uLlxuLy9cbi8vICAgMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGlzIG1vZHVsZSBub3IgdGhlIG5hbWVzIG9mIGl0c1xuLy8gICAgICBjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzXG4vLyAgICAgIGRlcml2ZWQgZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlblxuLy8gICAgICBwZXJtaXNzaW9uLlxuLy9cbi8vIFRISVMgU09GVFdBUkUgSVMgUFJPVklERUQgQlkgVEhFIENPUFlSSUdIVCBIT0xERVJTIEFORFxuLy8gQ09OVFJJQlVUT1JTIFwiQVMgSVNcIiBBTkQgQU5ZIEVYUFJFU1MgT1IgSU1QTElFRCBXQVJSQU5USUVTLFxuLy8gSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSBJTVBMSUVEIFdBUlJBTlRJRVMgT0Zcbi8vIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFXG4vLyBESVNDTEFJTUVELiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQ09QWVJJR0hUIE9XTkVSIE9SXG4vLyBDT05UUklCVVRPUlMgQkUgTElBQkxFIEZPUiBBTlkgRElSRUNULCBJTkRJUkVDVCwgSU5DSURFTlRBTCxcbi8vIFNQRUNJQUwsIEVYRU1QTEFSWSwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIChJTkNMVURJTkcsIEJVVFxuLy8gTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7XG4vLyBMT1NTIE9GIFVTRSwgREFUQSwgT1IgUFJPRklUUzsgT1IgQlVTSU5FU1MgSU5URVJSVVBUSU9OKVxuLy8gSE9XRVZFUiBDQVVTRUQgQU5EIE9OIEFOWSBUSEVPUlkgT0YgTElBQklMSVRZLCBXSEVUSEVSIElOXG4vLyBDT05UUkFDVCwgU1RSSUNUIExJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1Jcbi8vIE9USEVSV0lTRSkgQVJJU0lORyBJTiBBTlkgV0FZIE9VVCBPRiBUSEUgVVNFIE9GIFRISVMgU09GVFdBUkUsXG4vLyBFVkVOIElGIEFEVklTRUQgT0YgVEhFIFBPU1NJQklMSVRZIE9GIFNVQ0ggREFNQUdFLlxudmFyIFJhbmRvbSA9IChmdW5jdGlvbigpIHtcbiAgLy8gV2l0aGluIHRoaXMgY2xvc3VyZSBmdW5jdGlvbiB0aGUgY29kZSBpcyBiYXNpY2FsbHlcbiAgLy8gRGF2aWQncy4gVW5kdW0ncyBjdXN0b20gZXh0ZW5zaW9ucyBhcmUgYWRkZWQgdG8gdGhlXG4gIC8vIHByb3RvdHlwZSBvdXRzaWRlIG9mIHRoaXMgZnVuY3Rpb24uXG4gIHZhciB3aWR0aCA9IDI1NjtcbiAgdmFyIGNodW5rcyA9IDY7XG4gIHZhciBzaWduaWZpY2FuY2VFeHBvbmVudCA9IDUyO1xuICB2YXIgc3RhcnRkZW5vbSA9IE1hdGgucG93KHdpZHRoLCBjaHVua3MpO1xuICB2YXIgc2lnbmlmaWNhbmNlID0gTWF0aC5wb3coMiwgc2lnbmlmaWNhbmNlRXhwb25lbnQpO1xuICB2YXIgb3ZlcmZsb3cgPSBzaWduaWZpY2FuY2UgKiAyO1xuXG4gIHZhciBSYW5kb20gPSBmdW5jdGlvbihzZWVkKSB7XG4gICAgdGhpcy5yYW5kb20gPSBudWxsO1xuICAgIGlmICghc2VlZCkgdGhyb3cge1xuICAgICAgbmFtZTogXCJSYW5kb21TZWVkRXJyb3JcIixcbiAgICAgIG1lc3NhZ2U6IFwicmFuZG9tX3NlZWRfZXJyb3JcIi5sKClcbiAgICB9O1xuICAgIHZhciBrZXkgPSBbXTtcbiAgICBtaXhrZXkoc2VlZCwga2V5KTtcbiAgICB2YXIgYXJjNCA9IG5ldyBBUkM0KGtleSk7XG4gICAgdGhpcy5yYW5kb20gPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBuID0gYXJjNC5nKGNodW5rcyk7XG4gICAgICB2YXIgZCA9IHN0YXJ0ZGVub207XG4gICAgICB2YXIgeCA9IDA7XG4gICAgICB3aGlsZSAobiA8IHNpZ25pZmljYW5jZSkge1xuICAgICAgICBuID0gKG4gKyB4KSAqIHdpZHRoO1xuICAgICAgICBkICo9IHdpZHRoO1xuICAgICAgICB4ID0gYXJjNC5nKDEpO1xuICAgICAgfVxuICAgICAgd2hpbGUgKG4gPj0gb3ZlcmZsb3cpIHtcbiAgICAgICAgbiAvPSAyO1xuICAgICAgICBkIC89IDI7XG4gICAgICAgIHggPj4+PSAxO1xuICAgICAgfVxuICAgICAgcmV0dXJuIChuICsgeCkgLyBkO1xuICAgIH07XG4gIH07XG4gIC8vIEhlbHBlciB0eXBlLlxuICB2YXIgQVJDNCA9IGZ1bmN0aW9uKGtleSkge1xuICAgIHZhciB0LCB1LCBtZSA9IHRoaXMsIGtleWxlbiA9IGtleS5sZW5ndGg7XG4gICAgdmFyIGkgPSAwLCBqID0gbWUuaSA9IG1lLmogPSBtZS5tID0gMDtcbiAgICBtZS5TID0gW107XG4gICAgbWUuYyA9IFtdO1xuICAgIGlmICgha2V5bGVuKSB7IGtleSA9IFtrZXlsZW4rK107IH1cbiAgICB3aGlsZSAoaSA8IHdpZHRoKSB7IG1lLlNbaV0gPSBpKys7IH1cbiAgICBmb3IgKGkgPSAwOyBpIDwgd2lkdGg7IGkrKykge1xuICAgICAgdCA9IG1lLlNbaV07XG4gICAgICBqID0gbG93Yml0cyhqICsgdCArIGtleVtpICUga2V5bGVuXSk7XG4gICAgICB1ID0gbWUuU1tqXTtcbiAgICAgIG1lLlNbaV0gPSB1O1xuICAgICAgbWUuU1tqXSA9IHQ7XG4gICAgfVxuICAgIG1lLmcgPSBmdW5jdGlvbiBnZXRuZXh0KGNvdW50KSB7XG4gICAgICB2YXIgcyA9IG1lLlM7XG4gICAgICB2YXIgaSA9IGxvd2JpdHMobWUuaSArIDEpOyB2YXIgdCA9IHNbaV07XG4gICAgICB2YXIgaiA9IGxvd2JpdHMobWUuaiArIHQpOyB2YXIgdSA9IHNbal07XG4gICAgICBzW2ldID0gdTtcbiAgICAgIHNbal0gPSB0O1xuICAgICAgdmFyIHIgPSBzW2xvd2JpdHModCArIHUpXTtcbiAgICAgIHdoaWxlICgtLWNvdW50KSB7XG4gICAgICAgIGkgPSBsb3diaXRzKGkgKyAxKTsgdCA9IHNbaV07XG4gICAgICAgIGogPSBsb3diaXRzKGogKyB0KTsgdSA9IHNbal07XG4gICAgICAgIHNbaV0gPSB1O1xuICAgICAgICBzW2pdID0gdDtcbiAgICAgICAgciA9IHIgKiB3aWR0aCArIHNbbG93Yml0cyh0ICsgdSldO1xuICAgICAgfVxuICAgICAgbWUuaSA9IGk7XG4gICAgICBtZS5qID0gajtcbiAgICAgIHJldHVybiByO1xuICAgIH07XG4gICAgbWUuZyh3aWR0aCk7XG4gIH07XG4gIC8vIEhlbHBlciBmdW5jdGlvbnMuXG4gIHZhciBtaXhrZXkgPSBmdW5jdGlvbihzZWVkLCBrZXkpIHtcbiAgICBzZWVkICs9ICcnO1xuICAgIHZhciBzbWVhciA9IDA7XG4gICAgZm9yICh2YXIgaiA9IDA7IGogPCBzZWVkLmxlbmd0aDsgaisrKSB7XG4gICAgICB2YXIgbGIgPSBsb3diaXRzKGopO1xuICAgICAgc21lYXIgXj0ga2V5W2xiXTtcbiAgICAgIGtleVtsYl0gPSBsb3diaXRzKHNtZWFyKjE5ICsgc2VlZC5jaGFyQ29kZUF0KGopKTtcbiAgICB9XG4gICAgc2VlZCA9ICcnO1xuICAgIGZvciAoaiBpbiBrZXkpIHtcbiAgICAgIHNlZWQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShrZXlbal0pO1xuICAgIH1cbiAgICByZXR1cm4gc2VlZDtcbiAgfTtcbiAgdmFyIGxvd2JpdHMgPSBmdW5jdGlvbihuKSB7XG4gICAgcmV0dXJuIG4gJiAod2lkdGggLSAxKTtcbiAgfTtcblxuICByZXR1cm4gUmFuZG9tO1xufSkoKTtcbi8qIFJldHVybnMgYSByYW5kb20gZmxvYXRpbmcgcG9pbnQgbnVtYmVyIGJldHdlZW4gemVybyBhbmRcbiAqIG9uZS4gTkI6IFRoZSBwcm90b3R5cGUgaW1wbGVtZW50YXRpb24gYmVsb3cganVzdCB0aHJvd3MgYW5cbiAqIGVycm9yLCBpdCB3aWxsIGJlIG92ZXJyaWRkZW4gaW4gZWFjaCBSYW5kb20gb2JqZWN0IHdoZW4gdGhlXG4gKiBzZWVkIGhhcyBiZWVuIGNvcnJlY3RseSBjb25maWd1cmVkLiAqL1xuUmFuZG9tLnByb3RvdHlwZS5yYW5kb20gPSBmdW5jdGlvbigpIHtcbiAgdGhyb3cge1xuICAgIG5hbWU6XCJSYW5kb21FcnJvclwiLFxuICAgIG1lc3NhZ2U6IFwicmFuZG9tX2Vycm9yXCIubCgpXG4gIH07XG59O1xuLyogUmV0dXJucyBhbiBpbnRlZ2VyIGJldHdlZW4gdGhlIGdpdmVuIG1pbiBhbmQgbWF4IHZhbHVlcyxcbiAqIGluY2x1c2l2ZS4gKi9cblJhbmRvbS5wcm90b3R5cGUucmFuZG9tSW50ID0gZnVuY3Rpb24obWluLCBtYXgpIHtcbiAgcmV0dXJuIG1pbiArIE1hdGguZmxvb3IoKG1heC1taW4rMSkqdGhpcy5yYW5kb20oKSk7XG59O1xuLyogUmV0dXJucyB0aGUgcmVzdWx0IG9mIHJvbGxpbmcgbiBkaWNlIHdpdGggZHggc2lkZXMsIGFuZCBhZGRpbmdcbiAqIHBsdXMuICovXG5SYW5kb20ucHJvdG90eXBlLmRpY2UgPSBmdW5jdGlvbihuLCBkeCwgcGx1cykge1xuICB2YXIgcmVzdWx0ID0gMDtcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBuOyBpKyspIHtcbiAgICByZXN1bHQgKz0gdGhpcy5yYW5kb21JbnQoMSwgZHgpO1xuICB9XG4gIGlmIChwbHVzKSByZXN1bHQgKz0gcGx1cztcbiAgcmV0dXJuIHJlc3VsdDtcbn07XG4vKiBSZXR1cm5zIHRoZSByZXN1bHQgb2Ygcm9sbGluZyBuIGF2ZXJhZ2luZyBkaWNlIChpLmUuIDYgc2lkZWQgZGljZVxuICogd2l0aCBzaWRlcyAyLDMsMyw0LDQsNSkuIEFuZCBhZGRpbmcgcGx1cy4gKi9cblJhbmRvbS5wcm90b3R5cGUuYXZlRGljZSA9IChmdW5jdGlvbigpIHtcbiAgdmFyIG1hcHBpbmcgPSBbMiwzLDMsNCw0LDVdO1xuICByZXR1cm4gZnVuY3Rpb24obiwgcGx1cykge1xuICAgIHZhciByZXN1bHQgPSAwO1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbjsgaSsrKSB7XG4gICAgICByZXN1bHQgKz0gbWFwcGluZ1t0aGlzLnJhbmRvbUludCgwLCA1KV07XG4gICAgfVxuICAgIGlmIChwbHVzKSByZXN1bHQgKz0gcGx1cztcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9O1xufSkoKTtcbi8qIFJldHVybnMgYSBkaWNlLXJvbGwgcmVzdWx0IGZyb20gdGhlIGdpdmVuIHN0cmluZyBkaWNlXG4gKiBzcGVjaWZpY2F0aW9uLiBUaGUgc3BlY2lmaWNhdGlvbiBzaG91bGQgYmUgb2YgdGhlIGZvcm0geGR5K3osXG4gKiB3aGVyZSB0aGUgeCBjb21wb25lbnQgYW5kIHogY29tcG9uZW50IGFyZSBvcHRpb25hbC4gVGhpcyByb2xsc1xuICogeCBkaWNlIG9mIHdpdGggeSBzaWRlcywgYW5kIGFkZHMgeiB0byB0aGUgcmVzdWx0LCB0aGUgelxuICogY29tcG9uZW50IGNhbiBhbHNvIGJlIG5lZ2F0aXZlOiB4ZHktei4gVGhlIHkgY29tcG9uZW50IGNhbiBiZVxuICogZWl0aGVyIGEgbnVtYmVyIG9mIHNpZGVzLCBvciBjYW4gYmUgdGhlIHNwZWNpYWwgdmFsdWVzICdGJywgZm9yXG4gKiBhIGZ1ZGdlIGRpZSAod2l0aCAzIHNpZGVzLCArLDAsLSksICclJyBmb3IgYSAxMDAgc2lkZWQgZGllLCBvclxuICogJ0EnIGZvciBhbiBhdmVyYWdpbmcgZGllICh3aXRoIHNpZGVzIDIsMywzLDQsNCw1KS5cbiAqL1xuUmFuZG9tLnByb3RvdHlwZS5kaWNlU3RyaW5nID0gKGZ1bmN0aW9uKCkge1xuICB2YXIgZGljZVJlID0gL14oWzEtOV1bMC05XSopP2QoWyVGQV18WzEtOV1bMC05XSopKFstK11bMS05XVswLTldKik/JC87XG4gIHJldHVybiBmdW5jdGlvbihkZWYpIHtcbiAgICB2YXIgbWF0Y2ggPSBkZWYubWF0Y2goZGljZVJlKTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJkaWNlX3N0cmluZ19lcnJvclwiLmwoe3N0cmluZzpkZWZ9KSk7XG4gICAgfVxuXG4gICAgdmFyIG51bSA9IG1hdGNoWzFdP3BhcnNlSW50KG1hdGNoWzFdLCAxMCk6MTtcbiAgICB2YXIgc2lkZXM7XG4gICAgdmFyIGJvbnVzID0gbWF0Y2hbM10/cGFyc2VJbnQobWF0Y2hbM10sIDEwKTowO1xuXG4gICAgc3dpdGNoIChtYXRjaFsyXSkge1xuICAgICAgY2FzZSAnQSc6XG4gICAgICAgIHJldHVybiB0aGlzLmF2ZURpY2UobnVtLCBib251cyk7XG4gICAgICBjYXNlICdGJzpcbiAgICAgICAgc2lkZXMgPSAzO1xuICAgICAgICBib251cyAtPSBudW0qMjtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICclJzpcbiAgICAgICAgc2lkZXMgPSAxMDA7XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgc2lkZXMgPSBwYXJzZUludChtYXRjaFsyXSwgMTApO1xuICAgICAgYnJlYWs7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmRpY2UobnVtLCBzaWRlcywgYm9udXMpO1xuICB9O1xufSkoKTtcbiIsIi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBTZXR1cFxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLyogRXhwb3J0IG91ciBBUEkuICovXG53aW5kb3cudW5kdW0gPSB7XG4gIFNpdHVhdGlvbjogU2l0dWF0aW9uLFxuICBTaW1wbGVTaXR1YXRpb246IFNpbXBsZVNpdHVhdGlvbixcblxuICBRdWFsaXR5RGVmaW5pdGlvbjogUXVhbGl0eURlZmluaXRpb24sXG4gIEludGVnZXJRdWFsaXR5OiBJbnRlZ2VyUXVhbGl0eSxcbiAgTm9uWmVyb0ludGVnZXJRdWFsaXR5OiBOb25aZXJvSW50ZWdlclF1YWxpdHksXG4gIE51bWVyaWNRdWFsaXR5OiBOdW1lcmljUXVhbGl0eSxcbiAgV29yZFNjYWxlUXVhbGl0eTogV29yZFNjYWxlUXVhbGl0eSxcbiAgRnVkZ2VBZGplY3RpdmVzUXVhbGl0eTogRnVkZ2VBZGplY3RpdmVzUXVhbGl0eSxcbiAgT25PZmZRdWFsaXR5OiBPbk9mZlF1YWxpdHksXG4gIFllc05vUXVhbGl0eTogWWVzTm9RdWFsaXR5LFxuXG4gIFF1YWxpdHlHcm91cDogUXVhbGl0eUdyb3VwLFxuXG4gIGdhbWU6IGdhbWUsXG5cbiAgaXNJbnRlcmFjdGl2ZTogZnVuY3Rpb24oKSB7IHJldHVybiBpbnRlcmFjdGl2ZTsgfSxcblxuICAvLyBUaGUgdW5kdW0gc2V0IG9mIHRyYW5zbGF0ZWQgc3RyaW5ncy5cbiAgbGFuZ3VhZ2U6IHt9XG59O1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gRGVmYXVsdCBNZXNzYWdlc1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbnZhciBlbiA9IHtcbiAgdGVycmlibGU6IFwidGVycmlibGVcIixcbiAgcG9vcjogXCJwb29yXCIsXG4gIG1lZGlvY3JlOiBcIm1lZGlvY3JlXCIsXG4gIGZhaXI6IFwiZmFpclwiLFxuICBnb29kOiBcImdvb2RcIixcbiAgZ3JlYXQ6IFwiZ3JlYXRcIixcbiAgc3VwZXJiOiBcInN1cGVyYlwiLFxuICB5ZXM6IFwieWVzXCIsXG4gIG5vOiBcIm5vXCIsXG4gIGNob2ljZTogXCJDaG9pY2Uge251bWJlcn1cIixcbiAgbm9fZ3JvdXBfZGVmaW5pdGlvbjogXCJDb3VsZG4ndCBmaW5kIGEgZ3JvdXAgZGVmaW5pdGlvbiBmb3Ige2lkfS5cIixcbiAgbGlua19ub3RfdmFsaWQ6IFwiVGhlIGxpbmsgJ3tsaW5rfScgZG9lc24ndCBhcHBlYXIgdG8gYmUgdmFsaWQuXCIsXG4gIGxpbmtfbm9fYWN0aW9uOiBcIkEgbGluayB3aXRoIGEgc2l0dWF0aW9uIG9mICcuJywgbXVzdCBoYXZlIGFuIGFjdGlvbi5cIixcbiAgdW5rbm93bl9zaXR1YXRpb246IFwiWW91IGNhbid0IG1vdmUgdG8gYW4gdW5rbm93biBzaXR1YXRpb246IHtpZH0uXCIsXG4gIGV4aXN0aW5nX3NpdHVhdGlvbjogXCJZb3UgY2FuJ3Qgb3ZlcnJpZGUgc2l0dWF0aW9uIHtpZH0gaW4gSFRNTC5cIixcbiAgZXJhc2VfbWVzc2FnZTogXCJUaGlzIHdpbGwgcGVybWFuZW50bHkgZGVsZXRlIHRoaXMgY2hhcmFjdGVyIGFuZCBpbW1lZGlhdGVseSByZXR1cm4geW91IHRvIHRoZSBzdGFydCBvZiB0aGUgZ2FtZS4gQXJlIHlvdSBzdXJlP1wiLFxuICBub19jdXJyZW50X3NpdHVhdGlvbjogXCJJIGNhbid0IGRpc3BsYXksIGJlY2F1c2Ugd2UgZG9uJ3QgaGF2ZSBhIGN1cnJlbnQgc2l0dWF0aW9uLlwiLFxuICBub19sb2NhbF9zdG9yYWdlOiBcIk5vIGxvY2FsIHN0b3JhZ2UgYXZhaWxhYmxlLlwiLFxuICByYW5kb21fc2VlZF9lcnJvcjogXCJZb3UgbXVzdCBwcm92aWRlIGEgdmFsaWQgcmFuZG9tIHNlZWQuXCIsXG4gIHJhbmRvbV9lcnJvcjogXCJJbml0aWFsaXplIHRoZSBSYW5kb20gd2l0aCBhIG5vbi1lbXB0eSBzZWVkIGJlZm9yZSB1c2UuXCIsXG4gIGRpY2Vfc3RyaW5nX2Vycm9yOiBcIkNvdWxkbid0IGludGVycHJldCB5b3VyIGRpY2Ugc3RyaW5nOiAne3N0cmluZ30nLlwiXG59O1xuXG4vLyBTZXQgdGhpcyBkYXRhIGFzIGJvdGggdGhlIGRlZmF1bHQgZmFsbGJhY2sgbGFuZ3VhZ2UsIGFuZCB0aGUgZW5nbGlzaFxuLy8gcHJlZmVycmVkIGxhbmd1YWdlLlxudW5kdW0ubGFuZ3VhZ2VbXCJcIl0gPSBlbjtcbnVuZHVtLmxhbmd1YWdlW1wiZW5cIl0gPSBlbjtcblxuLyogU2V0IHVwIHRoZSBnYW1lIHdoZW4gZXZlcnl0aGluZyBpcyBsb2FkZWQuICovXG5mdW5jdGlvbiByZWFkeShmbikge1xuICBpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSAhPSAnbG9hZGluZycpe1xuICAgIGZuKCk7XG4gIH0gZWxzZSB7XG4gICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsIGZuKTtcbiAgfVxufVxuXG5yZWFkeShmdW5jdGlvbigpIHtcbiAgLy8gQ29tcGlsZSBhZGRpdGlvbmFsIHNpdHVhdGlvbnMgZnJvbSBIVE1MXG4gIGxvYWRIVE1MU2l0dWF0aW9ucygpO1xuXG4gIC8vIEhhbmRsZSBzdG9yYWdlLlxuICBpZiAoaGFzTG9jYWxTdG9yYWdlKCkpIHtcbiAgICB2YXIgZXJhc2UgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcImVyYXNlXCIpO1xuICAgIGVyYXNlLm9uY2xpY2sgPSBkb0VyYXNlO1xuICAgIGVyYXNlLmtleWRvd24gPSBkb0VyYXNlO1xuICAgIHZhciBzYXZlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJzYXZlXCIpO1xuICAgIHNhdmUub25jbGljayA9IHNhdmVHYW1lO1xuICAgIHNhdmUua2V5ZG93biA9IHNhdmVHYW1lO1xuXG4gICAgdmFyIHN0b3JlZENoYXJhY3RlciA9IGxvY2FsU3RvcmFnZVtnZXRTYXZlSWQoKV07XG4gICAgaWYgKHN0b3JlZENoYXJhY3Rlcikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgbG9hZEdhbWUoSlNPTi5wYXJzZShzdG9yZWRDaGFyYWN0ZXIpKTtcbiAgICAgICAgc2F2ZS5zZXRBdHRyaWJ1dGUoJ2Rpc2FibGVkJywgdHJ1ZSk7XG4gICAgICAgIGVyYXNlLnNldEF0dHJpYnV0ZShcImRpc2FibGVkXCIsIGZhbHNlKTtcbiAgICAgIH0gY2F0Y2goZXJyKSB7XG4gICAgICAgIGRvRXJhc2UodHJ1ZSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHNhdmUuc2V0QXR0cmlidXRlKCdkaXNhYmxlZCcsIHRydWUpO1xuICAgICAgZXJhc2Uuc2V0QXR0cmlidXRlKFwiZGlzYWJsZWRcIiwgdHJ1ZSk7XG4gICAgICBzdGFydEdhbWUoKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcIi5idXR0b25zXCIpLmlubmVySFRNTCA9IFwiPHA+XCIrXCJub19sb2NhbF9zdG9yYWdlXCIubCgpK1wiPC9wPlwiO1xuICAgIHN0YXJ0R2FtZSgpO1xuICB9XG5cbiAgLy8gaGFuZGxlIHRoZSBsaW5rIGNsaWNrc1xuICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcImNvbnRlbnRcIikuYWRkRXZlbnRMaXN0ZW5lcihcImNsaWNrXCIsIGxpbmtDbGlja0hhbmRsZXIsIGZhbHNlKTtcblxuICAvLyBEaXNwbGF5IHRoZSBcImNsaWNrIHRvIGJlZ2luXCIgbWVzc2FnZS4gKFdlIGRvIHRoaXMgaW4gY29kZVxuICAvLyBzbyB0aGF0LCBpZiBKYXZhc2NyaXB0IGlzIG9mZiwgaXQgZG9lc24ndCBoYXBwZW4uKVxuICBzaG93QmxvY2soXCJjbGlja19tZXNzYWdlXCIpO1xuXG4gIC8vIFNob3cgdGhlIGdhbWUgd2hlbiB3ZSBjbGljayBvbiB0aGUgdGl0bGUuXG4gIC8vIE5vdGU6IGlmIHlvdSBkbyBldmVudHMgd2l0aCBvbmNsaWNrLCB5b3UgaGF2ZSB0byBoYXZlIG9ubHkgb25lIGNsaWNrIGV2ZW50IGhhbmRsZXIuXG4gIC8vIFlvdSBjYW4gdXNlIG1vcmUgY29tcGxleCBtZXRob2RzIGlmIHlvdSBleHBlY3QgdG8gaGF2ZSBtb3JlLlxuICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInRpdGxlXCIpLm9uY2xpY2sgPSBmdW5jdGlvbigpIHtcbiAgICBzaG93QmxvY2soXCJjb250ZW50XCIpXG4gICAgc2hvd0Jsb2NrKFwiY29udGVudF93cmFwcGVyXCIpO1xuICAgIHNob3dCbG9jayhcImxlZ2FsXCIpO1xuICAgIHNob3dCbG9jayhcInRvb2xzX3dyYXBwZXJcIik7XG4gICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJ0aXRsZVwiKS5zdHlsZS5jdXJzb3IgPSBcImRlZmF1bHRcIjtcbiAgICBoaWRlQmxvY2soXCJjbGlja19tZXNzYWdlXCIpO1xuICB9O1xuXG4vKlxuICAvLyBBbnkgcG9pbnQgdGhhdCBhbiBvcHRpb24gbGlzdCBhcHBlYXJzLCBpdHMgb3B0aW9ucyBhcmUgaXRzXG4gIC8vIGZpcnN0IGxpbmtzLlxuICB2YXIgb3B0aW9uTGlua0V2ZW50ID0gZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAvLyBNYWtlIG9wdGlvbiBjbGlja3MgcGFzcyB0aHJvdWdoIHRvIHRoZWlyIGZpcnN0IGxpbmsuXG4gICAgdmFyIGxpbmsgPSAkKFwiYVwiLCB0aGlzKTtcbiAgICBpZiAobGluay5sZW5ndGggPiAwKSB7XG4gICAgICAkKGxpbmsuZ2V0KDApKS5jbGljaygpO1xuICAgIH1cbiAgfTtcbiAgaXRlbXMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKFwidWwub3B0aW9ucyBsaSwgI21lbnUgbGlcIik7XG4gIEFycmF5LnByb3RvdHlwZS5mb3JFYWNoLmNhbGwoaXRlbXMsIGZ1bmN0aW9uKGVsZW1lbnQsIGluZGV4KXtcbiAgICBlbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgb3B0aW9uTGlua0V2ZW50KTtcbiAgfSk7XG4qL1xufSk7XG4iXSwic291cmNlUm9vdCI6Ii9zb3VyY2UvIn0=