interact-js
Version:
A small interaction event unifiying library
625 lines (507 loc) • 51.9 kB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
var interactions = [],
minMoveDistance = 5,
interact,
maximumMovesToPersist = 1000, // Should be plenty..
propertiesToCopy = 'target,pageX,pageY,clientX,clientY,offsetX,offsetY,screenX,screenY,shiftKey,x,y'.split(','), // Stuff that will be on every interaction.
d = typeof document !== 'undefined' ? document : null;
function Interact(){
this._elements = [];
}
Interact.prototype.on = function(eventName, target, callback){
if(!target){
return;
}
target._interactEvents = target._interactEvents || {};
target._interactEvents[eventName] = target._interactEvents[eventName] || []
target._interactEvents[eventName].push({
callback: callback,
interact: this
});
return this;
};
Interact.prototype.emit = function(eventName, target, event, interaction){
if(!target){
return;
}
var interact = this,
currentTarget = target;
interaction.originalEvent = event;
interaction.preventDefault = function(){
event.preventDefault();
}
interaction.stopPropagation = function(){
event.stopPropagation();
}
while(currentTarget){
currentTarget._interactEvents &&
currentTarget._interactEvents[eventName] &&
currentTarget._interactEvents[eventName].forEach(function(listenerInfo){
if(listenerInfo.interact === interact){
listenerInfo.callback.call(interaction, interaction);
}
});
currentTarget = currentTarget.parentNode;
}
return this;
};
Interact.prototype.off =
Interact.prototype.removeListener = function(eventName, target, callback){
if(!target || !target._interactEvents || !target._interactEvents[eventName]){
return;
}
var interactListeners = target._interactEvents[eventName],
listenerInfo;
for(var i = 0; i < interactListeners.length; i++) {
listenerInfo = interactListeners[i];
if(listenerInfo.interact === interact && listenerInfo.callback === callback){
interactListeners.splice(i,1);
i--;
}
}
return this;
};
interact = new Interact();
// For some reason touch browsers never change the event target during a touch.
// This is, lets face it, fucking stupid.
function getActualTarget() {
var scrollX = window.scrollX,
scrollY = window.scrollY;
// IE is stupid and doesn't support scrollX/Y
if(scrollX === undefined){
scrollX = d.body.scrollLeft;
scrollY = d.body.scrollTop;
}
return d.elementFromPoint(this.pageX - window.scrollX, this.pageY - window.scrollY);
}
function getMoveDistance(x1,y1,x2,y2){
var adj = Math.abs(x1 - x2),
opp = Math.abs(y1 - y2);
return Math.sqrt(Math.pow(adj,2) + Math.pow(opp,2));
}
function destroyInteraction(interaction){
for(var i = 0; i < interactions.length; i++){
if(interactions[i].identifier === interaction.identifier){
interactions.splice(i,1);
}
}
}
function getInteraction(identifier){
for(var i = 0; i < interactions.length; i++){
if(interactions[i].identifier === identifier){
return interactions[i];
}
}
}
function setInheritedData(interaction, data){
for(var i = 0; i < propertiesToCopy.length; i++) {
interaction[propertiesToCopy[i]] = data[propertiesToCopy[i]]
}
}
function getAngle(deltaPoint){
return Math.atan2(deltaPoint.x, -deltaPoint.y) * 180 / Math.PI;
}
function Interaction(event, interactionInfo){
// If there is no event (eg: desktop) just make the identifier undefined
if(!event){
event = {};
}
// If there is no extra info about the interaction (eg: desktop) just use the event itself
if(!interactionInfo){
interactionInfo = event;
}
// If there is another interaction with the same ID, something went wrong.
// KILL IT WITH FIRE!
var oldInteraction = getInteraction(interactionInfo.identifier);
oldInteraction && oldInteraction.destroy();
this.identifier = interactionInfo.identifier;
this.moves = [];
interactions.push(this);
}
Interaction.prototype = {
constructor: Interaction,
getActualTarget: getActualTarget,
destroy: function(){
interact.on('destroy', this.target, this, this);
destroyInteraction(this);
},
start: function(event, interactionInfo){
// If there is no extra info about the interaction (eg: desktop) just use the event itself
if(!interactionInfo){
interactionInfo = event;
}
var lastStart = {
time: new Date(),
phase: 'start'
};
setInheritedData(lastStart, interactionInfo);
this.lastStart = lastStart;
setInheritedData(this, interactionInfo);
this.phase = 'start';
interact.emit('start', event.target, event, this);
return this;
},
move: function(event, interactionInfo){
// If there is no extra info about the interaction (eg: desktop) just use the event itself
if(!interactionInfo){
interactionInfo = event;
}
var currentTouch = {
time: new Date(),
phase: 'move'
};
setInheritedData(currentTouch, interactionInfo);
// Update the interaction
setInheritedData(this, interactionInfo);
this.moves.push(currentTouch);
// Memory saver, culls any moves that are over the maximum to keep.
this.moves = this.moves.slice(-maximumMovesToPersist);
var moveDelta = this.getMoveDelta(),
angle = 0;
if(moveDelta){
angle = getAngle(moveDelta);
}
this.angle = currentTouch.angle = angle;
this.phase = 'move';
interact.emit('move', event.target, event, this);
return this;
},
drag: function(event, interactionInfo){
// If there is no extra info about the interaction (eg: desktop) just use the event itself
if(!interactionInfo){
interactionInfo = event;
}
var currentTouch = {
time: new Date(),
phase: 'drag'
};
setInheritedData(currentTouch, interactionInfo);
// Update the interaction
setInheritedData(this, interactionInfo);
if(!this.moves){
this.moves = [];
}
this.moves.push(currentTouch);
// Memory saver, culls any moves that are over the maximum to keep.
this.moves = this.moves.slice(-maximumMovesToPersist);
if(!this.dragStarted && getMoveDistance(this.lastStart.pageX, this.lastStart.pageY, currentTouch.pageX, currentTouch.pageY) > minMoveDistance){
this.dragStarted = true;
}
var moveDelta = this.getMoveDelta(),
angle = 0;
if(moveDelta){
angle = getAngle(moveDelta);
}
this.angle = currentTouch.angle = angle;
if(this.dragStarted){
this.phase = 'drag';
interact.emit('drag', event.target, event, this);
}
return this;
},
end: function(event, interactionInfo){
if(!interactionInfo){
interactionInfo = event;
}
// Update the interaction
setInheritedData(this, interactionInfo);
if(!this.moves){
this.moves = [];
}
// Update the interaction
setInheritedData(this, interactionInfo);
this.phase = 'end';
interact.emit('end', event.target, event, this);
return this;
},
cancel: function(event, interactionInfo){
if(!interactionInfo){
interactionInfo = event;
}
// Update the interaction
setInheritedData(this, interactionInfo);
this.phase = 'cancel';
interact.emit('cancel', event.target, event, this);
return this;
},
getMoveDistance: function(){
if(this.moves.length > 1){
var current = this.moves[this.moves.length-1],
previous = this.moves[this.moves.length-2];
return getMoveDistance(current.pageX, current.pageY, previous.pageX, previous.pageY);
}
},
getMoveDelta: function(){
var current = this.moves[this.moves.length-1],
previous = this.moves[this.moves.length-2] || this.lastStart;
if(!current || !previous){
return;
}
return {
x: current.pageX - previous.pageX,
y: current.pageY - previous.pageY
};
},
getSpeed: function(){
if(this.moves.length > 1){
var current = this.moves[this.moves.length-1],
previous = this.moves[this.moves.length-2];
return this.getMoveDistance() / (current.time - previous.time);
}
return 0;
},
getCurrentAngle: function(blend){
var phase = this.phase,
currentPosition,
lastAngle,
i = this.moves.length-1,
angle,
firstAngle,
angles = [],
blendSteps = 20/(this.getSpeed()*2+1),
stepsUsed = 1;
if(this.moves && this.moves.length){
currentPosition = this.moves[i];
angle = firstAngle = currentPosition.angle;
if(blend && this.moves.length > 1){
while(
--i > 0 &&
this.moves.length - i < blendSteps &&
this.moves[i].phase === phase
){
lastAngle = this.moves[i].angle;
if(Math.abs(lastAngle - firstAngle) > 180){
angle -= lastAngle;
}else{
angle += lastAngle;
}
stepsUsed++;
}
angle = angle/stepsUsed;
}
}
if(angle === Infinity){
return firstAngle;
}
return angle;
},
getAllInteractions: function(){
return interactions.slice();
}
};
function start(event){
var touch;
for(var i = 0; i < event.changedTouches.length; i++){
touch = event.changedTouches[i];
new Interaction(event, event.changedTouches[i]).start(event, touch);
}
}
function drag(event){
var touch;
for(var i = 0; i < event.changedTouches.length; i++){
touch = event.changedTouches[i];
getInteraction(touch.identifier).drag(event, touch);
}
}
function end(event){
var touch;
for(var i = 0; i < event.changedTouches.length; i++){
touch = event.changedTouches[i];
getInteraction(touch.identifier).end(event, touch).destroy();
}
}
function cancel(event){
var touch;
for(var i = 0; i < event.changedTouches.length; i++){
touch = event.changedTouches[i];
getInteraction(touch.identifier).cancel(event, touch).destroy();
}
}
addEvent(d, 'touchstart', start);
addEvent(d, 'touchmove', drag);
addEvent(d, 'touchend', end);
addEvent(d, 'touchcancel', cancel);
var mouseIsDown = false;
addEvent(d, 'mousedown', function(event){
mouseIsDown = true;
if(!interactions.length){
new Interaction(event);
}
var interaction = getInteraction();
if(!interaction){
return;
}
getInteraction().start(event);
});
addEvent(d, 'mousemove', function(event){
if(!interactions.length){
new Interaction(event);
}
var interaction = getInteraction();
if(!interaction){
return;
}
if(mouseIsDown){
interaction.drag(event);
}else{
interaction.move(event);
}
});
addEvent(d, 'mouseup', function(event){
mouseIsDown = false;
var interaction = getInteraction();
if(!interaction){
return;
}
interaction.end(event, null);
interaction.destroy();
});
function addEvent(element, type, callback) {
if(element == null){
return;
}
if(element.addEventListener){
element.addEventListener(type, callback, { passive: false });
}
else if(d.attachEvent){
element.attachEvent("on"+ type, callback, { passive: false });
}
}
module.exports = interact;
},{}],2:[function(require,module,exports){
//Copyright (C) 2012 Kory Nunn
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/*
This code is not formatted for readability, but rather run-speed and to assist compilers.
However, the code's intention should be transparent.
*** IE SUPPORT ***
If you require this library to work in IE7, add the following after declaring crel.
var testDiv = document.createElement('div'),
testLabel = document.createElement('label');
testDiv.setAttribute('class', 'a');
testDiv['className'] !== 'a' ? crel.attrMap['class'] = 'className':undefined;
testDiv.setAttribute('name','a');
testDiv['name'] !== 'a' ? crel.attrMap['name'] = function(element, value){
element.id = value;
}:undefined;
testLabel.setAttribute('for', 'a');
testLabel['htmlFor'] !== 'a' ? crel.attrMap['for'] = 'htmlFor':undefined;
*/
(function (root, factory) {
if (typeof exports === 'object') {
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
define(factory);
} else {
root.crel = factory();
}
}(this, function () {
// based on http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
var isNode = typeof Node === 'function'
? function (object) { return object instanceof Node; }
: function (object) {
return object
&& typeof object === 'object'
&& typeof object.nodeType === 'number'
&& typeof object.nodeName === 'string';
};
var isArray = function(a){ return a instanceof Array; };
var appendChild = function(element, child) {
if(!isNode(child)){
child = document.createTextNode(child);
}
element.appendChild(child);
};
function crel(){
var document = window.document,
args = arguments, //Note: assigned to a variable to assist compilers. Saves about 40 bytes in closure compiler. Has negligable effect on performance.
element = args[0],
child,
settings = args[1],
childIndex = 2,
argumentsLength = args.length,
attributeMap = crel.attrMap;
element = isNode(element) ? element : document.createElement(element);
// shortcut
if(argumentsLength === 1){
return element;
}
if(typeof settings !== 'object' || isNode(settings) || isArray(settings)) {
--childIndex;
settings = null;
}
// shortcut if there is only one child that is a string
if((argumentsLength - childIndex) === 1 && typeof args[childIndex] === 'string' && element.textContent !== undefined){
element.textContent = args[childIndex];
}else{
for(; childIndex < argumentsLength; ++childIndex){
child = args[childIndex];
if(child == null){
continue;
}
if (isArray(child)) {
for (var i=0; i < child.length; ++i) {
appendChild(element, child[i]);
}
} else {
appendChild(element, child);
}
}
}
for(var key in settings){
if(!attributeMap[key]){
element.setAttribute(key, settings[key]);
}else{
var attr = crel.attrMap[key];
if(typeof attr === 'function'){
attr(element, settings[key]);
}else{
element.setAttribute(attr, settings[key]);
}
}
}
return element;
}
// Used for mapping one kind of attribute to the supported version of that in bad browsers.
// String referenced so that compilers maintain the property name.
crel['attrMap'] = {};
// String referenced so that compilers maintain the property name.
crel["isNode"] = isNode;
return crel;
}));
},{}],3:[function(require,module,exports){
var interact = require('./'),
crel = require('crel');
window.onload = function(){
var log = [],
output
crel(document.body,
output = crel('div')
);
var toLog = 'pageX pageY identifier angle'.split(' ');
var eventHandler = function(interaction){
interaction.preventDefault();
log.push(interaction);
var info = '';
for(var key in interaction){
if(~toLog.indexOf(key)){
info += key + ': ' + interaction[key] + ' ';
}
}
crel(output,
crel('p', interaction.phase, crel('br'), info, crel('br'), 'nicer angle: ' + interaction.getCurrentAngle(true))
);
if(output.childNodes.length > 10){
output.removeChild(output.childNodes[0]);
}
};
interact.on('move', document.body, eventHandler);
interact.on('start', document.body, eventHandler);
interact.on('drag', document.body, eventHandler);
interact.on('end', document.body, eventHandler);
interact.on('cancel', document.body, eventHandler);
};
},{"./":1,"crel":2}]},{},[3])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJpbnRlcmFjdC5qcyIsIm5vZGVfbW9kdWxlcy9jcmVsL2NyZWwuanMiLCJ0ZXN0LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzliQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbigpe2Z1bmN0aW9uIHIoZSxuLHQpe2Z1bmN0aW9uIG8oaSxmKXtpZighbltpXSl7aWYoIWVbaV0pe3ZhciBjPVwiZnVuY3Rpb25cIj09dHlwZW9mIHJlcXVpcmUmJnJlcXVpcmU7aWYoIWYmJmMpcmV0dXJuIGMoaSwhMCk7aWYodSlyZXR1cm4gdShpLCEwKTt2YXIgYT1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK2krXCInXCIpO3Rocm93IGEuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixhfXZhciBwPW5baV09e2V4cG9ydHM6e319O2VbaV1bMF0uY2FsbChwLmV4cG9ydHMsZnVuY3Rpb24ocil7dmFyIG49ZVtpXVsxXVtyXTtyZXR1cm4gbyhufHxyKX0scCxwLmV4cG9ydHMscixlLG4sdCl9cmV0dXJuIG5baV0uZXhwb3J0c31mb3IodmFyIHU9XCJmdW5jdGlvblwiPT10eXBlb2YgcmVxdWlyZSYmcmVxdWlyZSxpPTA7aTx0Lmxlbmd0aDtpKyspbyh0W2ldKTtyZXR1cm4gb31yZXR1cm4gcn0pKCkiLCJ2YXIgaW50ZXJhY3Rpb25zID0gW10sXHJcbiAgICBtaW5Nb3ZlRGlzdGFuY2UgPSA1LFxyXG4gICAgaW50ZXJhY3QsXHJcbiAgICBtYXhpbXVtTW92ZXNUb1BlcnNpc3QgPSAxMDAwLCAvLyBTaG91bGQgYmUgcGxlbnR5Li5cclxuICAgIHByb3BlcnRpZXNUb0NvcHkgPSAndGFyZ2V0LHBhZ2VYLHBhZ2VZLGNsaWVudFgsY2xpZW50WSxvZmZzZXRYLG9mZnNldFksc2NyZWVuWCxzY3JlZW5ZLHNoaWZ0S2V5LHgseScuc3BsaXQoJywnKSwgLy8gU3R1ZmYgdGhhdCB3aWxsIGJlIG9uIGV2ZXJ5IGludGVyYWN0aW9uLlxyXG4gICAgZCA9IHR5cGVvZiBkb2N1bWVudCAhPT0gJ3VuZGVmaW5lZCcgPyBkb2N1bWVudCA6IG51bGw7XHJcblxyXG5mdW5jdGlvbiBJbnRlcmFjdCgpe1xyXG4gICAgdGhpcy5fZWxlbWVudHMgPSBbXTtcclxufVxyXG5JbnRlcmFjdC5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldmVudE5hbWUsIHRhcmdldCwgY2FsbGJhY2spe1xyXG4gICAgaWYoIXRhcmdldCl7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gICAgdGFyZ2V0Ll9pbnRlcmFjdEV2ZW50cyA9IHRhcmdldC5faW50ZXJhY3RFdmVudHMgfHwge307XHJcbiAgICB0YXJnZXQuX2ludGVyYWN0RXZlbnRzW2V2ZW50TmFtZV0gPSB0YXJnZXQuX2ludGVyYWN0RXZlbnRzW2V2ZW50TmFtZV0gfHwgW11cclxuICAgIHRhcmdldC5faW50ZXJhY3RFdmVudHNbZXZlbnROYW1lXS5wdXNoKHtcclxuICAgICAgICBjYWxsYmFjazogY2FsbGJhY2ssXHJcbiAgICAgICAgaW50ZXJhY3Q6IHRoaXNcclxuICAgIH0pO1xyXG5cclxuICAgIHJldHVybiB0aGlzO1xyXG59O1xyXG5JbnRlcmFjdC5wcm90b3R5cGUuZW1pdCA9IGZ1bmN0aW9uKGV2ZW50TmFtZSwgdGFyZ2V0LCBldmVudCwgaW50ZXJhY3Rpb24pe1xyXG4gICAgaWYoIXRhcmdldCl7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBpbnRlcmFjdCA9IHRoaXMsXHJcbiAgICAgICAgY3VycmVudFRhcmdldCA9IHRhcmdldDtcclxuXHJcbiAgICBpbnRlcmFjdGlvbi5vcmlnaW5hbEV2ZW50ID0gZXZlbnQ7XHJcbiAgICBpbnRlcmFjdGlvbi5wcmV2ZW50RGVmYXVsdCA9IGZ1bmN0aW9uKCl7XHJcbiAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcclxuICAgIH1cclxuICAgIGludGVyYWN0aW9uLnN0b3BQcm9wYWdhdGlvbiA9IGZ1bmN0aW9uKCl7XHJcbiAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XHJcbiAgICB9XHJcblxyXG4gICAgd2hpbGUoY3VycmVudFRhcmdldCl7XHJcbiAgICAgICAgY3VycmVudFRhcmdldC5faW50ZXJhY3RFdmVudHMgJiZcclxuICAgICAgICBjdXJyZW50VGFyZ2V0Ll9pbnRlcmFjdEV2ZW50c1tldmVudE5hbWVdICYmXHJcbiAgICAgICAgY3VycmVudFRhcmdldC5faW50ZXJhY3RFdmVudHNbZXZlbnROYW1lXS5mb3JFYWNoKGZ1bmN0aW9uKGxpc3RlbmVySW5mbyl7XHJcbiAgICAgICAgICAgIGlmKGxpc3RlbmVySW5mby5pbnRlcmFjdCA9PT0gaW50ZXJhY3Qpe1xyXG4gICAgICAgICAgICAgICAgbGlzdGVuZXJJbmZvLmNhbGxiYWNrLmNhbGwoaW50ZXJhY3Rpb24sIGludGVyYWN0aW9uKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIGN1cnJlbnRUYXJnZXQgPSBjdXJyZW50VGFyZ2V0LnBhcmVudE5vZGU7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbn07XHJcbkludGVyYWN0LnByb3RvdHlwZS5vZmYgPVxyXG5JbnRlcmFjdC5wcm90b3R5cGUucmVtb3ZlTGlzdGVuZXIgPSBmdW5jdGlvbihldmVudE5hbWUsIHRhcmdldCwgY2FsbGJhY2spe1xyXG4gICAgaWYoIXRhcmdldCB8fCAhdGFyZ2V0Ll9pbnRlcmFjdEV2ZW50cyB8fCAhdGFyZ2V0Ll9pbnRlcmFjdEV2ZW50c1tldmVudE5hbWVdKXtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgICB2YXIgaW50ZXJhY3RMaXN0ZW5lcnMgPSB0YXJnZXQuX2ludGVyYWN0RXZlbnRzW2V2ZW50TmFtZV0sXHJcbiAgICAgICAgbGlzdGVuZXJJbmZvO1xyXG4gICAgZm9yKHZhciBpID0gMDsgaSA8IGludGVyYWN0TGlzdGVuZXJzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgbGlzdGVuZXJJbmZvID0gaW50ZXJhY3RMaXN0ZW5lcnNbaV07XHJcbiAgICAgICAgaWYobGlzdGVuZXJJbmZvLmludGVyYWN0ID09PSBpbnRlcmFjdCAmJiBsaXN0ZW5lckluZm8uY2FsbGJhY2sgPT09IGNhbGxiYWNrKXtcclxuICAgICAgICAgICAgaW50ZXJhY3RMaXN0ZW5lcnMuc3BsaWNlKGksMSk7XHJcbiAgICAgICAgICAgIGktLTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbn07XHJcbmludGVyYWN0ID0gbmV3IEludGVyYWN0KCk7XHJcblxyXG4gICAgLy8gRm9yIHNvbWUgcmVhc29uIHRvdWNoIGJyb3dzZXJzIG5ldmVyIGNoYW5nZSB0aGUgZXZlbnQgdGFyZ2V0IGR1cmluZyBhIHRvdWNoLlxyXG4gICAgLy8gVGhpcyBpcywgbGV0cyBmYWNlIGl0LCBmdWNraW5nIHN0dXBpZC5cclxuZnVuY3Rpb24gZ2V0QWN0dWFsVGFyZ2V0KCkge1xyXG4gICAgdmFyIHNjcm9sbFggPSB3aW5kb3cuc2Nyb2xsWCxcclxuICAgICAgICBzY3JvbGxZID0gd2luZG93LnNjcm9sbFk7XHJcblxyXG4gICAgLy8gSUUgaXMgc3R1cGlkIGFuZCBkb2Vzbid0IHN1cHBvcnQgc2Nyb2xsWC9ZXHJcbiAgICBpZihzY3JvbGxYID09PSB1bmRlZmluZWQpe1xyXG4gICAgICAgIHNjcm9sbFggPSBkLmJvZHkuc2Nyb2xsTGVmdDtcclxuICAgICAgICBzY3JvbGxZID0gZC5ib2R5LnNjcm9sbFRvcDtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gZC5lbGVtZW50RnJvbVBvaW50KHRoaXMucGFnZVggLSB3aW5kb3cuc2Nyb2xsWCwgdGhpcy5wYWdlWSAtIHdpbmRvdy5zY3JvbGxZKTtcclxufVxyXG5cclxuZnVuY3Rpb24gZ2V0TW92ZURpc3RhbmNlKHgxLHkxLHgyLHkyKXtcclxuICAgIHZhciBhZGogPSBNYXRoLmFicyh4MSAtIHgyKSxcclxuICAgICAgICBvcHAgPSBNYXRoLmFicyh5MSAtIHkyKTtcclxuXHJcbiAgICByZXR1cm4gTWF0aC5zcXJ0KE1hdGgucG93KGFkaiwyKSArIE1hdGgucG93KG9wcCwyKSk7XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGRlc3Ryb3lJbnRlcmFjdGlvbihpbnRlcmFjdGlvbil7XHJcbiAgICBmb3IodmFyIGkgPSAwOyBpIDwgaW50ZXJhY3Rpb25zLmxlbmd0aDsgaSsrKXtcclxuICAgICAgICBpZihpbnRlcmFjdGlvbnNbaV0uaWRlbnRpZmllciA9PT0gaW50ZXJhY3Rpb24uaWRlbnRpZmllcil7XHJcbiAgICAgICAgICAgIGludGVyYWN0aW9ucy5zcGxpY2UoaSwxKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbn1cclxuXHJcbmZ1bmN0aW9uIGdldEludGVyYWN0aW9uKGlkZW50aWZpZXIpe1xyXG4gICAgZm9yKHZhciBpID0gMDsgaSA8IGludGVyYWN0aW9ucy5sZW5ndGg7IGkrKyl7XHJcbiAgICAgICAgaWYoaW50ZXJhY3Rpb25zW2ldLmlkZW50aWZpZXIgPT09IGlkZW50aWZpZXIpe1xyXG4gICAgICAgICAgICByZXR1cm4gaW50ZXJhY3Rpb25zW2ldO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxufVxyXG5cclxuZnVuY3Rpb24gc2V0SW5oZXJpdGVkRGF0YShpbnRlcmFjdGlvbiwgZGF0YSl7XHJcbiAgICBmb3IodmFyIGkgPSAwOyBpIDwgcHJvcGVydGllc1RvQ29weS5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgIGludGVyYWN0aW9uW3Byb3BlcnRpZXNUb0NvcHlbaV1dID0gZGF0YVtwcm9wZXJ0aWVzVG9Db3B5W2ldXVxyXG4gICAgfVxyXG59XHJcblxyXG5mdW5jdGlvbiBnZXRBbmdsZShkZWx0YVBvaW50KXtcclxuICAgIHJldHVybiBNYXRoLmF0YW4yKGRlbHRhUG9pbnQueCwgLWRlbHRhUG9pbnQueSkgKiAxODAgLyBNYXRoLlBJO1xyXG59XHJcblxyXG5mdW5jdGlvbiBJbnRlcmFjdGlvbihldmVudCwgaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgIC8vIElmIHRoZXJlIGlzIG5vIGV2ZW50IChlZzogZGVza3RvcCkganVzdCBtYWtlIHRoZSBpZGVudGlmaWVyIHVuZGVmaW5lZFxyXG4gICAgaWYoIWV2ZW50KXtcclxuICAgICAgICBldmVudCA9IHt9O1xyXG4gICAgfVxyXG4gICAgLy8gSWYgdGhlcmUgaXMgbm8gZXh0cmEgaW5mbyBhYm91dCB0aGUgaW50ZXJhY3Rpb24gKGVnOiBkZXNrdG9wKSBqdXN0IHVzZSB0aGUgZXZlbnQgaXRzZWxmXHJcbiAgICBpZighaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICBpbnRlcmFjdGlvbkluZm8gPSBldmVudDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBJZiB0aGVyZSBpcyBhbm90aGVyIGludGVyYWN0aW9uIHdpdGggdGhlIHNhbWUgSUQsIHNvbWV0aGluZyB3ZW50IHdyb25nLlxyXG4gICAgLy8gS0lMTCBJVCBXSVRIIEZJUkUhXHJcbiAgICB2YXIgb2xkSW50ZXJhY3Rpb24gPSBnZXRJbnRlcmFjdGlvbihpbnRlcmFjdGlvbkluZm8uaWRlbnRpZmllcik7XHJcbiAgICBvbGRJbnRlcmFjdGlvbiAmJiBvbGRJbnRlcmFjdGlvbi5kZXN0cm95KCk7XHJcblxyXG4gICAgdGhpcy5pZGVudGlmaWVyID0gaW50ZXJhY3Rpb25JbmZvLmlkZW50aWZpZXI7XHJcblxyXG4gICAgdGhpcy5tb3ZlcyA9IFtdO1xyXG5cclxuICAgIGludGVyYWN0aW9ucy5wdXNoKHRoaXMpO1xyXG59XHJcblxyXG5JbnRlcmFjdGlvbi5wcm90b3R5cGUgPSB7XHJcbiAgICBjb25zdHJ1Y3RvcjogSW50ZXJhY3Rpb24sXHJcbiAgICBnZXRBY3R1YWxUYXJnZXQ6IGdldEFjdHVhbFRhcmdldCxcclxuICAgIGRlc3Ryb3k6IGZ1bmN0aW9uKCl7XHJcbiAgICAgICAgaW50ZXJhY3Qub24oJ2Rlc3Ryb3knLCB0aGlzLnRhcmdldCwgdGhpcywgdGhpcyk7XHJcbiAgICAgICAgZGVzdHJveUludGVyYWN0aW9uKHRoaXMpO1xyXG4gICAgfSxcclxuICAgIHN0YXJ0OiBmdW5jdGlvbihldmVudCwgaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICAvLyBJZiB0aGVyZSBpcyBubyBleHRyYSBpbmZvIGFib3V0IHRoZSBpbnRlcmFjdGlvbiAoZWc6IGRlc2t0b3ApIGp1c3QgdXNlIHRoZSBldmVudCBpdHNlbGZcclxuICAgICAgICBpZighaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICAgICAgaW50ZXJhY3Rpb25JbmZvID0gZXZlbnQ7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB2YXIgbGFzdFN0YXJ0ID0ge1xyXG4gICAgICAgICAgICAgICAgdGltZTogbmV3IERhdGUoKSxcclxuICAgICAgICAgICAgICAgIHBoYXNlOiAnc3RhcnQnXHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgc2V0SW5oZXJpdGVkRGF0YShsYXN0U3RhcnQsIGludGVyYWN0aW9uSW5mbyk7XHJcbiAgICAgICAgdGhpcy5sYXN0U3RhcnQgPSBsYXN0U3RhcnQ7XHJcblxyXG4gICAgICAgIHNldEluaGVyaXRlZERhdGEodGhpcywgaW50ZXJhY3Rpb25JbmZvKTtcclxuXHJcbiAgICAgICAgdGhpcy5waGFzZSA9ICdzdGFydCc7XHJcbiAgICAgICAgaW50ZXJhY3QuZW1pdCgnc3RhcnQnLCBldmVudC50YXJnZXQsIGV2ZW50LCB0aGlzKTtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH0sXHJcbiAgICBtb3ZlOiBmdW5jdGlvbihldmVudCwgaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICAvLyBJZiB0aGVyZSBpcyBubyBleHRyYSBpbmZvIGFib3V0IHRoZSBpbnRlcmFjdGlvbiAoZWc6IGRlc2t0b3ApIGp1c3QgdXNlIHRoZSBldmVudCBpdHNlbGZcclxuICAgICAgICBpZighaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICAgICAgaW50ZXJhY3Rpb25JbmZvID0gZXZlbnQ7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB2YXIgY3VycmVudFRvdWNoID0ge1xyXG4gICAgICAgICAgICAgICAgdGltZTogbmV3IERhdGUoKSxcclxuICAgICAgICAgICAgICAgIHBoYXNlOiAnbW92ZSdcclxuICAgICAgICAgICAgfTtcclxuXHJcbiAgICAgICAgc2V0SW5oZXJpdGVkRGF0YShjdXJyZW50VG91Y2gsIGludGVyYWN0aW9uSW5mbyk7XHJcblxyXG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgaW50ZXJhY3Rpb25cclxuICAgICAgICBzZXRJbmhlcml0ZWREYXRhKHRoaXMsIGludGVyYWN0aW9uSW5mbyk7XHJcblxyXG4gICAgICAgIHRoaXMubW92ZXMucHVzaChjdXJyZW50VG91Y2gpO1xyXG5cclxuICAgICAgICAvLyBNZW1vcnkgc2F2ZXIsIGN1bGxzIGFueSBtb3ZlcyB0aGF0IGFyZSBvdmVyIHRoZSBtYXhpbXVtIHRvIGtlZXAuXHJcbiAgICAgICAgdGhpcy5tb3ZlcyA9IHRoaXMubW92ZXMuc2xpY2UoLW1heGltdW1Nb3Zlc1RvUGVyc2lzdCk7XHJcblxyXG4gICAgICAgIHZhciBtb3ZlRGVsdGEgPSB0aGlzLmdldE1vdmVEZWx0YSgpLFxyXG4gICAgICAgICAgICBhbmdsZSA9IDA7XHJcbiAgICAgICAgaWYobW92ZURlbHRhKXtcclxuICAgICAgICAgICAgYW5nbGUgPSBnZXRBbmdsZShtb3ZlRGVsdGEpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5hbmdsZSA9IGN1cnJlbnRUb3VjaC5hbmdsZSA9IGFuZ2xlO1xyXG5cclxuICAgICAgICB0aGlzLnBoYXNlID0gJ21vdmUnO1xyXG4gICAgICAgIGludGVyYWN0LmVtaXQoJ21vdmUnLCBldmVudC50YXJnZXQsIGV2ZW50LCB0aGlzKTtcclxuICAgICAgICByZXR1cm4gdGhpcztcclxuICAgIH0sXHJcbiAgICBkcmFnOiBmdW5jdGlvbihldmVudCwgaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICAvLyBJZiB0aGVyZSBpcyBubyBleHRyYSBpbmZvIGFib3V0IHRoZSBpbnRlcmFjdGlvbiAoZWc6IGRlc2t0b3ApIGp1c3QgdXNlIHRoZSBldmVudCBpdHNlbGZcclxuICAgICAgICBpZighaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICAgICAgaW50ZXJhY3Rpb25JbmZvID0gZXZlbnQ7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB2YXIgY3VycmVudFRvdWNoID0ge1xyXG4gICAgICAgICAgICAgICAgdGltZTogbmV3IERhdGUoKSxcclxuICAgICAgICAgICAgICAgIHBoYXNlOiAnZHJhZydcclxuICAgICAgICAgICAgfTtcclxuXHJcbiAgICAgICAgc2V0SW5oZXJpdGVkRGF0YShjdXJyZW50VG91Y2gsIGludGVyYWN0aW9uSW5mbyk7XHJcblxyXG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgaW50ZXJhY3Rpb25cclxuICAgICAgICBzZXRJbmhlcml0ZWREYXRhKHRoaXMsIGludGVyYWN0aW9uSW5mbyk7XHJcblxyXG4gICAgICAgIGlmKCF0aGlzLm1vdmVzKXtcclxuICAgICAgICAgICAgdGhpcy5tb3ZlcyA9IFtdO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5tb3Zlcy5wdXNoKGN1cnJlbnRUb3VjaCk7XHJcblxyXG4gICAgICAgIC8vIE1lbW9yeSBzYXZlciwgY3VsbHMgYW55IG1vdmVzIHRoYXQgYXJlIG92ZXIgdGhlIG1heGltdW0gdG8ga2VlcC5cclxuICAgICAgICB0aGlzLm1vdmVzID0gdGhpcy5tb3Zlcy5zbGljZSgtbWF4aW11bU1vdmVzVG9QZXJzaXN0KTtcclxuXHJcbiAgICAgICAgaWYoIXRoaXMuZHJhZ1N0YXJ0ZWQgJiYgZ2V0TW92ZURpc3RhbmNlKHRoaXMubGFzdFN0YXJ0LnBhZ2VYLCB0aGlzLmxhc3RTdGFydC5wYWdlWSwgY3VycmVudFRvdWNoLnBhZ2VYLCBjdXJyZW50VG91Y2gucGFnZVkpID4gbWluTW92ZURpc3RhbmNlKXtcclxuICAgICAgICAgICAgdGhpcy5kcmFnU3RhcnRlZCA9IHRydWU7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB2YXIgbW92ZURlbHRhID0gdGhpcy5nZXRNb3ZlRGVsdGEoKSxcclxuICAgICAgICAgICAgYW5nbGUgPSAwO1xyXG4gICAgICAgIGlmKG1vdmVEZWx0YSl7XHJcbiAgICAgICAgICAgIGFuZ2xlID0gZ2V0QW5nbGUobW92ZURlbHRhKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRoaXMuYW5nbGUgPSBjdXJyZW50VG91Y2guYW5nbGUgPSBhbmdsZTtcclxuXHJcbiAgICAgICAgaWYodGhpcy5kcmFnU3RhcnRlZCl7XHJcbiAgICAgICAgICAgIHRoaXMucGhhc2UgPSAnZHJhZyc7XHJcbiAgICAgICAgICAgIGludGVyYWN0LmVtaXQoJ2RyYWcnLCBldmVudC50YXJnZXQsIGV2ZW50LCB0aGlzKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9LFxyXG4gICAgZW5kOiBmdW5jdGlvbihldmVudCwgaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICBpZighaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICAgICAgaW50ZXJhY3Rpb25JbmZvID0gZXZlbnQ7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBVcGRhdGUgdGhlIGludGVyYWN0aW9uXHJcbiAgICAgICAgc2V0SW5oZXJpdGVkRGF0YSh0aGlzLCBpbnRlcmFjdGlvbkluZm8pO1xyXG5cclxuICAgICAgICBpZighdGhpcy5tb3Zlcyl7XHJcbiAgICAgICAgICAgIHRoaXMubW92ZXMgPSBbXTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgaW50ZXJhY3Rpb25cclxuICAgICAgICBzZXRJbmhlcml0ZWREYXRhKHRoaXMsIGludGVyYWN0aW9uSW5mbyk7XHJcblxyXG4gICAgICAgIHRoaXMucGhhc2UgPSAnZW5kJztcclxuICAgICAgICBpbnRlcmFjdC5lbWl0KCdlbmQnLCBldmVudC50YXJnZXQsIGV2ZW50LCB0aGlzKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXM7XHJcbiAgICB9LFxyXG4gICAgY2FuY2VsOiBmdW5jdGlvbihldmVudCwgaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICBpZighaW50ZXJhY3Rpb25JbmZvKXtcclxuICAgICAgICAgICAgaW50ZXJhY3Rpb25JbmZvID0gZXZlbnQ7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBVcGRhdGUgdGhlIGludGVyYWN0aW9uXHJcbiAgICAgICAgc2V0SW5oZXJpdGVkRGF0YSh0aGlzLCBpbnRlcmFjdGlvbkluZm8pO1xyXG5cclxuICAgICAgICB0aGlzLnBoYXNlID0gJ2NhbmNlbCc7XHJcbiAgICAgICAgaW50ZXJhY3QuZW1pdCgnY2FuY2VsJywgZXZlbnQudGFyZ2V0LCBldmVudCwgdGhpcyk7XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzO1xyXG4gICAgfSxcclxuICAgIGdldE1vdmVEaXN0YW5jZTogZnVuY3Rpb24oKXtcclxuICAgICAgICBpZih0aGlzLm1vdmVzLmxlbmd0aCA+IDEpe1xyXG4gICAgICAgICAgICB2YXIgY3VycmVudCA9IHRoaXMubW92ZXNbdGhpcy5tb3Zlcy5sZW5ndGgtMV0sXHJcbiAgICAgICAgICAgICAgICBwcmV2aW91cyA9IHRoaXMubW92ZXNbdGhpcy5tb3Zlcy5sZW5ndGgtMl07XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gZ2V0TW92ZURpc3RhbmNlKGN1cnJlbnQucGFnZVgsIGN1cnJlbnQucGFnZVksIHByZXZpb3VzLnBhZ2VYLCBwcmV2aW91cy5wYWdlWSk7XHJcbiAgICAgICAgfVxyXG4gICAgfSxcclxuICAgIGdldE1vdmVEZWx0YTogZnVuY3Rpb24oKXtcclxuICAgICAgICB2YXIgY3VycmVudCA9IHRoaXMubW92ZXNbdGhpcy5tb3Zlcy5sZW5ndGgtMV0sXHJcbiAgICAgICAgICAgIHByZXZpb3VzID0gdGhpcy5tb3Zlc1t0aGlzLm1vdmVzLmxlbmd0aC0yXSB8fCB0aGlzLmxhc3RTdGFydDtcclxuXHJcbiAgICAgICAgaWYoIWN1cnJlbnQgfHwgIXByZXZpb3VzKXtcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgeDogY3VycmVudC5wYWdlWCAtIHByZXZpb3VzLnBhZ2VYLFxyXG4gICAgICAgICAgICB5OiBjdXJyZW50LnBhZ2VZIC0gcHJldmlvdXMucGFnZVlcclxuICAgICAgICB9O1xyXG4gICAgfSxcclxuICAgIGdldFNwZWVkOiBmdW5jdGlvbigpe1xyXG4gICAgICAgIGlmKHRoaXMubW92ZXMubGVuZ3RoID4gMSl7XHJcbiAgICAgICAgICAgIHZhciBjdXJyZW50ID0gdGhpcy5tb3Zlc1t0aGlzLm1vdmVzLmxlbmd0aC0xXSxcclxuICAgICAgICAgICAgICAgIHByZXZpb3VzID0gdGhpcy5tb3Zlc1t0aGlzLm1vdmVzLmxlbmd0aC0yXTtcclxuXHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmdldE1vdmVEaXN0YW5jZSgpIC8gKGN1cnJlbnQudGltZSAtIHByZXZpb3VzLnRpbWUpO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gMDtcclxuICAgIH0sXHJcbiAgICBnZXRDdXJyZW50QW5nbGU6IGZ1bmN0aW9uKGJsZW5kKXtcclxuICAgICAgICB2YXIgcGhhc2UgPSB0aGlzLnBoYXNlLFxyXG4gICAgICAgICAgICBjdXJyZW50UG9zaXRpb24sXHJcbiAgICAgICAgICAgIGxhc3RBbmdsZSxcclxuICAgICAgICAgICAgaSA9IHRoaXMubW92ZXMubGVuZ3RoLTEsXHJcbiAgICAgICAgICAgIGFuZ2xlLFxyXG4gICAgICAgICAgICBmaXJzdEFuZ2xlLFxyXG4gICAgICAgICAgICBhbmdsZXMgPSBbXSxcclxuICAgICAgICAgICAgYmxlbmRTdGVwcyA9IDIwLyh0aGlzLmdldFNwZWVkKCkqMisxKSxcclxuICAgICAgICAgICAgc3RlcHNVc2VkID0gMTtcclxuXHJcbiAgICAgICAgaWYodGhpcy5tb3ZlcyAmJiB0aGlzLm1vdmVzLmxlbmd0aCl7XHJcblxyXG4gICAgICAgICAgICBjdXJyZW50UG9zaXRpb24gPSB0aGlzLm1vdmVzW2ldO1xyXG4gICAgICAgICAgICBhbmdsZSA9IGZpcnN0QW5nbGUgPSBjdXJyZW50UG9zaXRpb24uYW5nbGU7XHJcblxyXG4gICAgICAgICAgICBpZihibGVuZCAmJiB0aGlzLm1vdmVzLmxlbmd0aCA+IDEpe1xyXG4gICAgICAgICAgICAgICAgd2hpbGUoXHJcbiAgICAgICAgICAgICAgICAgICAgLS1pID4gMCAmJlxyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMubW92ZXMubGVuZ3RoIC0gaSA8IGJsZW5kU3RlcHMgJiZcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLm1vdmVzW2ldLnBoYXNlID09PSBwaGFzZVxyXG4gICAgICAgICAgICAgICAgKXtcclxuICAgICAgICAgICAgICAgICAgICBsYXN0QW5nbGUgPSB0aGlzLm1vdmVzW2ldLmFuZ2xlO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmKE1hdGguYWJzKGxhc3RBbmdsZSAtIGZpcnN0QW5nbGUpID4gMTgwKXtcclxuICAgICAgICAgICAgICAgICAgICAgICAgYW5nbGUgLT0gbGFzdEFuZ2xlO1xyXG4gICAgICAgICAgICAgICAgICAgIH1lbHNle1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSArPSBsYXN0QW5nbGU7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIHN0ZXBzVXNlZCsrO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgYW5nbGUgPSBhbmdsZS9zdGVwc1VzZWQ7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICAgaWYoYW5nbGUgPT09IEluZmluaXR5KXtcclxuICAgICAgICAgICAgcmV0dXJuIGZpcnN0QW5nbGU7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBhbmdsZTtcclxuICAgIH0sXHJcbiAgICBnZXRBbGxJbnRlcmFjdGlvbnM6IGZ1bmN0aW9uKCl7XHJcbiAgICAgICAgcmV0dXJuIGludGVyYWN0aW9ucy5zbGljZSgpO1xyXG4gICAgfVxyXG59O1xyXG5cclxuZnVuY3Rpb24gc3RhcnQoZXZlbnQpe1xyXG4gICAgdmFyIHRvdWNoO1xyXG5cclxuICAgIGZvcih2YXIgaSA9IDA7IGkgPCBldmVudC5jaGFuZ2VkVG91Y2hlcy5sZW5ndGg7IGkrKyl7XHJcbiAgICAgICAgdG91Y2ggPSBldmVudC5jaGFuZ2VkVG91Y2hlc1tpXTtcclxuICAgICAgICBuZXcgSW50ZXJhY3Rpb24oZXZlbnQsIGV2ZW50LmNoYW5nZWRUb3VjaGVzW2ldKS5zdGFydChldmVudCwgdG91Y2gpO1xyXG4gICAgfVxyXG59XHJcbmZ1bmN0aW9uIGRyYWcoZXZlbnQpe1xyXG4gICAgdmFyIHRvdWNoO1xyXG5cclxuICAgIGZvcih2YXIgaSA9IDA7IGkgPCBldmVudC5jaGFuZ2VkVG91Y2hlcy5sZW5ndGg7IGkrKyl7XHJcbiAgICAgICAgdG91Y2ggPSBldmVudC5jaGFuZ2VkVG91Y2hlc1tpXTtcclxuICAgICAgICBnZXRJbnRlcmFjdGlvbih0b3VjaC5pZGVudGlmaWVyKS5kcmFnKGV2ZW50LCB0b3VjaCk7XHJcbiAgICB9XHJcbn1cclxuZnVuY3Rpb24gZW5kKGV2ZW50KXtcclxuICAgIHZhciB0b3VjaDtcclxuXHJcbiAgICBmb3IodmFyIGkgPSAwOyBpIDwgZXZlbnQuY2hhbmdlZFRvdWNoZXMubGVuZ3RoOyBpKyspe1xyXG4gICAgICAgIHRvdWNoID0gZXZlbnQuY2hhbmdlZFRvdWNoZXNbaV07XHJcbiAgICAgICAgZ2V0SW50ZXJhY3Rpb24odG91Y2guaWRlbnRpZmllcikuZW5kKGV2ZW50LCB0b3VjaCkuZGVzdHJveSgpO1xyXG4gICAgfVxyXG59XHJcbmZ1bmN0aW9uIGNhbmNlbChldmVudCl7XHJcbiAgICB2YXIgdG91Y2g7XHJcblxyXG4gICAgZm9yKHZhciBpID0gMDsgaSA8IGV2ZW50LmNoYW5nZWRUb3VjaGVzLmxlbmd0aDsgaSsrKXtcclxuICAgICAgICB0b3VjaCA9IGV2ZW50LmNoYW5nZWRUb3VjaGVzW2ldO1xyXG4gICAgICAgIGdldEludGVyYWN0aW9uKHRvdWNoLmlkZW50aWZpZXIpLmNhbmNlbChldmVudCwgdG91Y2gpLmRlc3Ryb3koKTtcclxuICAgIH1cclxufVxyXG5cclxuYWRkRXZlbnQoZCwgJ3RvdWNoc3RhcnQnLCBzdGFydCk7XHJcbmFkZEV2ZW50KGQsICd0b3VjaG1vdmUnLCBkcmFnKTtcclxuYWRkRXZlbnQoZCwgJ3RvdWNoZW5kJywgZW5kKTtcclxuYWRkRXZlbnQoZCwgJ3RvdWNoY2FuY2VsJywgY2FuY2VsKTtcclxuXHJcbnZhciBtb3VzZUlzRG93biA9IGZhbHNlO1xyXG5hZGRFdmVudChkLCAnbW91c2Vkb3duJywgZnVuY3Rpb24oZXZlbnQpe1xyXG4gICAgbW91c2VJc0Rvd24gPSB0cnVlO1xyXG5cclxuICAgIGlmKCFpbnRlcmFjdGlvbnMubGVuZ3RoKXtcclxuICAgICAgICBuZXcgSW50ZXJhY3Rpb24oZXZlbnQpO1xyXG4gICAgfVxyXG5cclxuICAgIHZhciBpbnRlcmFjdGlvbiA9IGdldEludGVyYWN0aW9uKCk7XHJcblxyXG4gICAgaWYoIWludGVyYWN0aW9uKXtcclxuICAgICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0SW50ZXJhY3Rpb24oKS5zdGFydChldmVudCk7XHJcbn0pO1xyXG5hZGRFdmVudChkLCAnbW91c2Vtb3ZlJywgZnVuY3Rpb24oZXZlbnQpe1xyXG4gICAgaWYoIWludGVyYWN0aW9ucy5sZW5ndGgpe1xyXG4gICAgICAgIG5ldyBJbnRlcmFjdGlvbihldmVudCk7XHJcbiAgICB9XHJcblxyXG4gICAgdmFyIGludGVyYWN0aW9uID0gZ2V0SW50ZXJhY3Rpb24oKTtcclxuXHJcbiAgICBpZighaW50ZXJhY3Rpb24pe1xyXG4gICAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICBpZihtb3VzZUlzRG93bil7XHJcbiAgICAgICAgaW50ZXJhY3Rpb24uZHJhZyhldmVudCk7XHJcbiAgICB9ZWxzZXtcclxuICAgICAgICBpbnRlcmFjdGlvbi5tb3ZlKGV2ZW50KTtcclxuICAgIH1cclxufSk7XHJcbmFkZEV2ZW50KGQsICdtb3VzZXVwJywgZnVuY3Rpb24oZXZlbnQpe1xyXG4gICAgbW91c2VJc0Rvd24gPSBmYWxzZTtcclxuXHJcbiAgICB2YXIgaW50ZXJhY3Rpb24gPSBnZXRJbnRlcmFjdGlvbigpO1xyXG5cclxuICAgIGlmKCFpbnRlcmFjdGlvbil7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGludGVyYWN0aW9uLmVuZChldmVudCwgbnVsbCk7XHJcbiAgICBpbnRlcmFjdGlvbi5kZXN0cm95KCk7XHJcbn0pO1xyXG5cclxuZnVuY3Rpb24gYWRkRXZlbnQoZWxlbWVudCwgdHlwZSwgY2FsbGJhY2spIHtcclxuICAgIGlmKGVsZW1lbnQgPT0gbnVsbCl7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGlmKGVsZW1lbnQuYWRkRXZlbnRMaXN0ZW5lcil7XHJcbiAgICAgICAgZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKHR5cGUsIGNhbGxiYWNrLCB7IHBhc3NpdmU6IGZhbHNlIH0pO1xyXG4gICAgfVxyXG4gICAgZWxzZSBpZihkLmF0dGFjaEV2ZW50KXtcclxuICAgICAgICBlbGVtZW50LmF0dGFjaEV2ZW50KFwib25cIisgdHlwZSwgY2FsbGJhY2ssIHsgcGFzc2l2ZTogZmFsc2UgfSk7XHJcbiAgICB9XHJcbn1cclxuXHJcbm1vZHVsZS5leHBvcnRzID0gaW50ZXJhY3Q7IiwiLy9Db3B5cmlnaHQgKEMpIDIwMTIgS29yeSBOdW5uXHJcblxyXG4vL1Blcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHkgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG8gZGVhbCBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwgZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6XHJcblxyXG4vL1RoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxyXG5cclxuLy9USEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTIE9SIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZLCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwgREFNQUdFUyBPUiBPVEhFUiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLCBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOIFRIRSBTT0ZUV0FSRS5cclxuXHJcbi8qXHJcblxyXG4gICAgVGhpcyBjb2RlIGlzIG5vdCBmb3JtYXR0ZWQgZm9yIHJlYWRhYmlsaXR5LCBidXQgcmF0aGVyIHJ1bi1zcGVlZCBhbmQgdG8gYXNzaXN0IGNvbXBpbGVycy5cclxuXHJcbiAgICBIb3dldmVyLCB0aGUgY29kZSdzIGludGVudGlvbiBzaG91bGQgYmUgdHJhbnNwYXJlbnQuXHJcblxyXG4gICAgKioqIElFIFNVUFBPUlQgKioqXHJcblxyXG4gICAgSWYgeW91IHJlcXVpcmUgdGhpcyBsaWJyYXJ5IHRvIHdvcmsgaW4gSUU3LCBhZGQgdGhlIGZvbGxvd2luZyBhZnRlciBkZWNsYXJpbmcgY3JlbC5cclxuXHJcbiAgICB2YXIgdGVzdERpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpLFxyXG4gICAgICAgIHRlc3RMYWJlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2xhYmVsJyk7XHJcblxyXG4gICAgdGVzdERpdi5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgJ2EnKTtcclxuICAgIHRlc3REaXZbJ2NsYXNzTmFtZSddICE9PSAnYScgPyBjcmVsLmF0dHJNYXBbJ2NsYXNzJ10gPSAnY2xhc3NOYW1lJzp1bmRlZmluZWQ7XHJcbiAgICB0ZXN0RGl2LnNldEF0dHJpYnV0ZSgnbmFtZScsJ2EnKTtcclxuICAgIHRlc3REaXZbJ25hbWUnXSAhPT0gJ2EnID8gY3JlbC5hdHRyTWFwWyduYW1lJ10gPSBmdW5jdGlvbihlbGVtZW50LCB2YWx1ZSl7XHJcbiAgICAgICAgZWxlbWVudC5pZCA9IHZhbHVlO1xyXG4gICAgfTp1bmRlZmluZWQ7XHJcblxyXG5cclxuICAgIHRlc3RMYWJlbC5zZXRBdHRyaWJ1dGUoJ2ZvcicsICdhJyk7XHJcbiAgICB0ZXN0TGFiZWxbJ2h0bWxGb3InXSAhPT0gJ2EnID8gY3JlbC5hdHRyTWFwWydmb3InXSA9ICdodG1sRm9yJzp1bmRlZmluZWQ7XHJcblxyXG5cclxuXHJcbiovXHJcblxyXG4oZnVuY3Rpb24gKHJvb3QsIGZhY3RvcnkpIHtcclxuICAgIGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcpIHtcclxuICAgICAgICBtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoKTtcclxuICAgIH0gZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kKSB7XHJcbiAgICAgICAgZGVmaW5lKGZhY3RvcnkpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgICByb290LmNyZWwgPSBmYWN0b3J5KCk7XHJcbiAgICB9XHJcbn0odGhpcywgZnVuY3Rpb24gKCkge1xyXG4gICAgLy8gYmFzZWQgb24gaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8zODQyODYvamF2YXNjcmlwdC1pc2RvbS1ob3ctZG8teW91LWNoZWNrLWlmLWEtamF2YXNjcmlwdC1vYmplY3QtaXMtYS1kb20tb2JqZWN0XHJcbiAgICB2YXIgaXNOb2RlID0gdHlwZW9mIE5vZGUgPT09ICdmdW5jdGlvbidcclxuICAgICAgICA/IGZ1bmN0aW9uIChvYmplY3QpIHsgcmV0dXJuIG9iamVjdCBpbnN0YW5jZW9mIE5vZGU7IH1cclxuICAgICAgICA6IGZ1bmN0aW9uIChvYmplY3QpIHtcclxuICAgICAgICAgICAgcmV0dXJuIG9iamVjdFxyXG4gICAgICAgICAgICAgICAgJiYgdHlwZW9mIG9iamVjdCA9PT0gJ29iamVjdCdcclxuICAgICAgICAgICAgICAgICYmIHR5cGVvZiBvYmplY3Qubm9kZVR5cGUgPT09ICdudW1iZXInXHJcbiAgICAgICAgICAgICAgICAmJiB0eXBlb2Ygb2JqZWN0Lm5vZGVOYW1lID09PSAnc3RyaW5nJztcclxuICAgICAgICB9O1xyXG4gICAgdmFyIGlzQXJyYXkgPSBmdW5jdGlvbihhKXsgcmV0dXJuIGEgaW5zdGFuY2VvZiBBcnJheTsgfTtcclxuICAgIHZhciBhcHBlbmRDaGlsZCA9IGZ1bmN0aW9uKGVsZW1lbnQsIGNoaWxkKSB7XHJcbiAgICAgIGlmKCFpc05vZGUoY2hpbGQpKXtcclxuICAgICAgICAgIGNoaWxkID0gZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoY2hpbGQpO1xyXG4gICAgICB9XHJcbiAgICAgIGVsZW1lbnQuYXBwZW5kQ2hpbGQoY2hpbGQpO1xyXG4gICAgfTtcclxuXHJcblxyXG4gICAgZnVuY3Rpb24gY3JlbCgpe1xyXG4gICAgICAgIHZhciBkb2N1bWVudCA9IHdpbmRvdy5kb2N1bWVudCxcclxuICAgICAgICAgICAgYXJncyA9IGFyZ3VtZW50cywgLy9Ob3RlOiBhc3NpZ25lZCB0byBhIHZhcmlhYmxlIHRvIGFzc2lzdCBjb21waWxlcnMuIFNhdmVzIGFib3V0IDQwIGJ5dGVzIGluIGNsb3N1cmUgY29tcGlsZXIuIEhhcyBuZWdsaWdhYmxlIGVmZmVjdCBvbiBwZXJmb3JtYW5jZS5cclxuICAgICAgICAgICAgZWxlbWVudCA9IGFyZ3NbMF0sXHJcbiAgICAgICAgICAgIGNoaWxkLFxyXG4gICAgICAgICAgICBzZXR0aW5ncyA9IGFyZ3NbMV0sXHJcbiAgICAgICAgICAgIGNoaWxkSW5kZXggPSAyLFxyXG4gICAgICAgICAgICBhcmd1bWVudHNMZW5ndGggPSBhcmdzLmxlbmd0aCxcclxuICAgICAgICAgICAgYXR0cmlidXRlTWFwID0gY3JlbC5hdHRyTWFwO1xyXG5cclxuICAgICAgICBlbGVtZW50ID0gaXNOb2RlKGVsZW1lbnQpID8gZWxlbWVudCA6IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoZWxlbWVudCk7XHJcbiAgICAgICAgLy8gc2hvcnRjdXRcclxuICAgICAgICBpZihhcmd1bWVudHNMZW5ndGggPT09IDEpe1xyXG4gICAgICAgICAgICByZXR1cm4gZWxlbWVudDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmKHR5cGVvZiBzZXR0aW5ncyAhPT0gJ29iamVjdCcgfHwgaXNOb2RlKHNldHRpbmdzKSB8fCBpc0FycmF5KHNldHRpbmdzKSkge1xyXG4gICAgICAgICAgICAtLWNoaWxkSW5kZXg7XHJcbiAgICAgICAgICAgIHNldHRpbmdzID0gbnVsbDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIHNob3J0Y3V0IGlmIHRoZXJlIGlzIG9ubHkgb25lIGNoaWxkIHRoYXQgaXMgYSBzdHJpbmdcclxuICAgICAgICBpZigoYXJndW1lbnRzTGVuZ3RoIC0gY2hpbGRJbmRleCkgPT09IDEgJiYgdHlwZW9mIGFyZ3NbY2hpbGRJbmRleF0gPT09ICdzdHJpbmcnICYmIGVsZW1lbnQudGV4dENvbnRlbnQgIT09IHVuZGVmaW5lZCl7XHJcbiAgICAgICAgICAgIGVsZW1lbnQudGV4dENvbnRlbnQgPSBhcmdzW2NoaWxkSW5kZXhdO1xyXG4gICAgICAgIH1lbHNle1xyXG4gICAgICAgICAgICBmb3IoOyBjaGlsZEluZGV4IDwgYXJndW1lbnRzTGVuZ3RoOyArK2NoaWxkSW5kZXgpe1xyXG4gICAgICAgICAgICAgICAgY2hpbGQgPSBhcmdzW2NoaWxkSW5kZXhdO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmKGNoaWxkID09IG51bGwpe1xyXG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIGlmIChpc0FycmF5KGNoaWxkKSkge1xyXG4gICAgICAgICAgICAgICAgICBmb3IgKHZhciBpPTA7IGkgPCBjaGlsZC5sZW5ndGg7ICsraSkge1xyXG4gICAgICAgICAgICAgICAgICAgIGFwcGVuZENoaWxkKGVsZW1lbnQsIGNoaWxkW2ldKTtcclxuICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgYXBwZW5kQ2hpbGQoZWxlbWVudCwgY2hpbGQpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBmb3IodmFyIGtleSBpbiBzZXR0aW5ncyl7XHJcbiAgICAgICAgICAgIGlmKCFhdHRyaWJ1dGVNYXBba2V5XSl7XHJcbiAgICAgICAgICAgICAgICBlbGVtZW50LnNldEF0dHJpYnV0ZShrZXksIHNldHRpbmdzW2tleV0pO1xyXG4gICAgICAgICAgICB9ZWxzZXtcclxuICAgICAgICAgICAgICAgIHZhciBhdHRyID0gY3JlbC5hdHRyTWFwW2tleV07XHJcbiAgICAgICAgICAgICAgICBpZih0eXBlb2YgYXR0ciA9PT0gJ2Z1bmN0aW9uJyl7XHJcbiAgICAgICAgICAgICAgICAgICAgYXR0cihlbGVtZW50LCBzZXR0aW5nc1trZXldKTtcclxuICAgICAgICAgICAgICAgIH1lbHNle1xyXG4gICAgICAgICAgICAgICAgICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlKGF0dHIsIHNldHRpbmdzW2tleV0pO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gZWxlbWVudDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBVc2VkIGZvciBtYXBwaW5nIG9uZSBraW5kIG9mIGF0dHJpYnV0ZSB0byB0aGUgc3VwcG9ydGVkIHZlcnNpb24gb2YgdGhhdCBpbiBiYWQgYnJvd3NlcnMuXHJcbiAgICAvLyBTdHJpbmcgcmVmZXJlbmNlZCBzbyB0aGF0IGNvbXBpbGVycyBtYWludGFpbiB0aG