403Webshell
Server IP : 85.158.181.41  /  Your IP : 216.73.217.12
Web Server : Apache
System : Linux cloud9-vm129 6.1.178+1-ph #ph SMP PREEMPT_DYNAMIC Wed Jul 29 09:00:54 UTC 2026 x86_64
User : moncbefd ( 1024)
PHP Version : 7.3.33
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/moncbefd/www.moneta.at/admin/html/assets/javascript/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/moncbefd/www.moneta.at/admin/html/assets/javascript//compatibility-vendor.js
/*!
 * jQuery Migrate - v1.4.1 - 2016-05-19
 * Copyright jQuery Foundation and other contributors
 */
(function( jQuery, window, undefined ) {
// See http://bugs.jquery.com/ticket/13335
// "use strict";


jQuery.migrateVersion = "1.4.1";


var warnedAbout = {};

// List of warnings already given; public read only
jQuery.migrateWarnings = [];

// Set to true to prevent console output; migrateWarnings still maintained
// jQuery.migrateMute = false;

// Show a message on the console so devs know we're active
if ( window.console && window.console.log ) {
	window.console.log( "JQMIGRATE: Migrate is installed" +
		( jQuery.migrateMute ? "" : " with logging active" ) +
		", version " + jQuery.migrateVersion );
}

// Set to false to disable traces that appear with warnings
if ( jQuery.migrateTrace === undefined ) {
	jQuery.migrateTrace = true;
}

// Forget any warnings we've already given; public
jQuery.migrateReset = function() {
	warnedAbout = {};
	jQuery.migrateWarnings.length = 0;
};

function migrateWarn( msg) {
	var console = window.console;
	if ( !warnedAbout[ msg ] ) {
		warnedAbout[ msg ] = true;
		jQuery.migrateWarnings.push( msg );
		if ( console && console.warn && !jQuery.migrateMute ) {
			console.warn( "JQMIGRATE: " + msg );
			if ( jQuery.migrateTrace && console.trace ) {
				console.trace();
			}
		}
	}
}

function migrateWarnProp( obj, prop, value, msg ) {
	if ( Object.defineProperty ) {
		// On ES5 browsers (non-oldIE), warn if the code tries to get prop;
		// allow property to be overwritten in case some other plugin wants it
		try {
			Object.defineProperty( obj, prop, {
				configurable: true,
				enumerable: true,
				get: function() {
					migrateWarn( msg );
					return value;
				},
				set: function( newValue ) {
					migrateWarn( msg );
					value = newValue;
				}
			});
			return;
		} catch( err ) {
			// IE8 is a dope about Object.defineProperty, can't warn there
		}
	}

	// Non-ES5 (or broken) browser; just set the property
	jQuery._definePropertyBroken = true;
	obj[ prop ] = value;
}

if ( document.compatMode === "BackCompat" ) {
	// jQuery has never supported or tested Quirks Mode
	migrateWarn( "jQuery is not compatible with Quirks Mode" );
}


var attrFn = jQuery( "<input/>", { size: 1 } ).attr("size") && jQuery.attrFn,
	oldAttr = jQuery.attr,
	valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get ||
		function() { return null; },
	valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set ||
		function() { return undefined; },
	rnoType = /^(?:input|button)$/i,
	rnoAttrNodeType = /^[238]$/,
	rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
	ruseDefault = /^(?:checked|selected)$/i;

// jQuery.attrFn
migrateWarnProp( jQuery, "attrFn", attrFn || {}, "jQuery.attrFn is deprecated" );

jQuery.attr = function( elem, name, value, pass ) {
	var lowerName = name.toLowerCase(),
		nType = elem && elem.nodeType;

	if ( pass ) {
		// Since pass is used internally, we only warn for new jQuery
		// versions where there isn't a pass arg in the formal params
		if ( oldAttr.length < 4 ) {
			migrateWarn("jQuery.fn.attr( props, pass ) is deprecated");
		}
		if ( elem && !rnoAttrNodeType.test( nType ) &&
			(attrFn ? name in attrFn : jQuery.isFunction(jQuery.fn[name])) ) {
			return jQuery( elem )[ name ]( value );
		}
	}

	// Warn if user tries to set `type`, since it breaks on IE 6/7/8; by checking
	// for disconnected elements we don't warn on $( "<button>", { type: "button" } ).
	if ( name === "type" && value !== undefined && rnoType.test( elem.nodeName ) && elem.parentNode ) {
		migrateWarn("Can't change the 'type' of an input or button in IE 6/7/8");
	}

	// Restore boolHook for boolean property/attribute synchronization
	if ( !jQuery.attrHooks[ lowerName ] && rboolean.test( lowerName ) ) {
		jQuery.attrHooks[ lowerName ] = {
			get: function( elem, name ) {
				// Align boolean attributes with corresponding properties
				// Fall back to attribute presence where some booleans are not supported
				var attrNode,
					property = jQuery.prop( elem, name );
				return property === true || typeof property !== "boolean" &&
					( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?

					name.toLowerCase() :
					undefined;
			},
			set: function( elem, value, name ) {
				var propName;
				if ( value === false ) {
					// Remove boolean attributes when set to false
					jQuery.removeAttr( elem, name );
				} else {
					// value is true since we know at this point it's type boolean and not false
					// Set boolean attributes to the same name and set the DOM property
					propName = jQuery.propFix[ name ] || name;
					if ( propName in elem ) {
						// Only set the IDL specifically if it already exists on the element
						elem[ propName ] = true;
					}

					elem.setAttribute( name, name.toLowerCase() );
				}
				return name;
			}
		};

		// Warn only for attributes that can remain distinct from their properties post-1.9
		if ( ruseDefault.test( lowerName ) ) {
			migrateWarn( "jQuery.fn.attr('" + lowerName + "') might use property instead of attribute" );
		}
	}

	return oldAttr.call( jQuery, elem, name, value );
};

// attrHooks: value
jQuery.attrHooks.value = {
	get: function( elem, name ) {
		var nodeName = ( elem.nodeName || "" ).toLowerCase();
		if ( nodeName === "button" ) {
			return valueAttrGet.apply( this, arguments );
		}
		if ( nodeName !== "input" && nodeName !== "option" ) {
			migrateWarn("jQuery.fn.attr('value') no longer gets properties");
		}
		return name in elem ?
			elem.value :
			null;
	},
	set: function( elem, value ) {
		var nodeName = ( elem.nodeName || "" ).toLowerCase();
		if ( nodeName === "button" ) {
			return valueAttrSet.apply( this, arguments );
		}
		if ( nodeName !== "input" && nodeName !== "option" ) {
			migrateWarn("jQuery.fn.attr('value', val) no longer sets properties");
		}
		// Does not return so that setAttribute is also used
		elem.value = value;
	}
};


var matched, browser,
	oldInit = jQuery.fn.init,
	oldFind = jQuery.find,
	oldParseJSON = jQuery.parseJSON,
	rspaceAngle = /^\s*</,
	rattrHashTest = /\[(\s*[-\w]+\s*)([~|^$*]?=)\s*([-\w#]*?#[-\w#]*)\s*\]/,
	rattrHashGlob = /\[(\s*[-\w]+\s*)([~|^$*]?=)\s*([-\w#]*?#[-\w#]*)\s*\]/g,
	// Note: XSS check is done below after string is trimmed
	rquickExpr = /^([^<]*)(<[\w\W]+>)([^>]*)$/;

// $(html) "looks like html" rule change
jQuery.fn.init = function( selector, context, rootjQuery ) {
	var match, ret;

	if ( selector && typeof selector === "string" ) {
		if ( !jQuery.isPlainObject( context ) &&
				(match = rquickExpr.exec( jQuery.trim( selector ) )) && match[ 0 ] ) {

			// This is an HTML string according to the "old" rules; is it still?
			if ( !rspaceAngle.test( selector ) ) {
				migrateWarn("$(html) HTML strings must start with '<' character");
			}
			if ( match[ 3 ] ) {
				migrateWarn("$(html) HTML text after last tag is ignored");
			}

			// Consistently reject any HTML-like string starting with a hash (gh-9521)
			// Note that this may break jQuery 1.6.x code that otherwise would work.
			if ( match[ 0 ].charAt( 0 ) === "#" ) {
				migrateWarn("HTML string cannot start with a '#' character");
				jQuery.error("JQMIGRATE: Invalid selector string (XSS)");
			}

			// Now process using loose rules; let pre-1.8 play too
			// Is this a jQuery context? parseHTML expects a DOM element (#178)
			if ( context && context.context && context.context.nodeType ) {
				context = context.context;
			}

			if ( jQuery.parseHTML ) {
				return oldInit.call( this,
						jQuery.parseHTML( match[ 2 ], context && context.ownerDocument ||
							context || document, true ), context, rootjQuery );
			}
		}
	}

	ret = oldInit.apply( this, arguments );

	// Fill in selector and context properties so .live() works
	if ( selector && selector.selector !== undefined ) {
		// A jQuery object, copy its properties
		ret.selector = selector.selector;
		ret.context = selector.context;

	} else {
		ret.selector = typeof selector === "string" ? selector : "";
		if ( selector ) {
			ret.context = selector.nodeType? selector : context || document;
		}
	}

	return ret;
};
jQuery.fn.init.prototype = jQuery.fn;

jQuery.find = function( selector ) {
	var args = Array.prototype.slice.call( arguments );

	// Support: PhantomJS 1.x
	// String#match fails to match when used with a //g RegExp, only on some strings
	if ( typeof selector === "string" && rattrHashTest.test( selector ) ) {

		// The nonstandard and undocumented unquoted-hash was removed in jQuery 1.12.0
		// First see if qS thinks it's a valid selector, if so avoid a false positive
		try {
			document.querySelector( selector );
		} catch ( err1 ) {

			// Didn't *look* valid to qSA, warn and try quoting what we think is the value
			selector = selector.replace( rattrHashGlob, function( _, attr, op, value ) {
				return "[" + attr + op + "\"" + value + "\"]";
			} );

			// If the regexp *may* have created an invalid selector, don't update it
			// Note that there may be false alarms if selector uses jQuery extensions
			try {
				document.querySelector( selector );
				migrateWarn( "Attribute selector with '#' must be quoted: " + args[ 0 ] );
				args[ 0 ] = selector;
			} catch ( err2 ) {
				migrateWarn( "Attribute selector with '#' was not fixed: " + args[ 0 ] );
			}
		}
	}

	return oldFind.apply( this, args );
};

// Copy properties attached to original jQuery.find method (e.g. .attr, .isXML)
var findProp;
for ( findProp in oldFind ) {
	if ( Object.prototype.hasOwnProperty.call( oldFind, findProp ) ) {
		jQuery.find[ findProp ] = oldFind[ findProp ];
	}
}

// Let $.parseJSON(falsy_value) return null
jQuery.parseJSON = function( json ) {
	if ( !json ) {
		migrateWarn("jQuery.parseJSON requires a valid JSON string");
		return null;
	}
	return oldParseJSON.apply( this, arguments );
};

jQuery.uaMatch = function( ua ) {
	ua = ua.toLowerCase();

	var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
		/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
		/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
		/(msie) ([\w.]+)/.exec( ua ) ||
		ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
		[];

	return {
		browser: match[ 1 ] || "",
		version: match[ 2 ] || "0"
	};
};

// Don't clobber any existing jQuery.browser in case it's different
if ( !jQuery.browser ) {
	matched = jQuery.uaMatch( navigator.userAgent );
	browser = {};

	if ( matched.browser ) {
		browser[ matched.browser ] = true;
		browser.version = matched.version;
	}

	// Chrome is Webkit, but Webkit is also Safari.
	if ( browser.chrome ) {
		browser.webkit = true;
	} else if ( browser.webkit ) {
		browser.safari = true;
	}

	jQuery.browser = browser;
}

// Warn if the code tries to get jQuery.browser
migrateWarnProp( jQuery, "browser", jQuery.browser, "jQuery.browser is deprecated" );

// jQuery.boxModel deprecated in 1.3, jQuery.support.boxModel deprecated in 1.7
jQuery.boxModel = jQuery.support.boxModel = (document.compatMode === "CSS1Compat");
migrateWarnProp( jQuery, "boxModel", jQuery.boxModel, "jQuery.boxModel is deprecated" );
migrateWarnProp( jQuery.support, "boxModel", jQuery.support.boxModel, "jQuery.support.boxModel is deprecated" );

jQuery.sub = function() {
	function jQuerySub( selector, context ) {
		return new jQuerySub.fn.init( selector, context );
	}
	jQuery.extend( true, jQuerySub, this );
	jQuerySub.superclass = this;
	jQuerySub.fn = jQuerySub.prototype = this();
	jQuerySub.fn.constructor = jQuerySub;
	jQuerySub.sub = this.sub;
	jQuerySub.fn.init = function init( selector, context ) {
		var instance = jQuery.fn.init.call( this, selector, context, rootjQuerySub );
		return instance instanceof jQuerySub ?
			instance :
			jQuerySub( instance );
	};
	jQuerySub.fn.init.prototype = jQuerySub.fn;
	var rootjQuerySub = jQuerySub(document);
	migrateWarn( "jQuery.sub() is deprecated" );
	return jQuerySub;
};

// The number of elements contained in the matched element set
jQuery.fn.size = function() {
	migrateWarn( "jQuery.fn.size() is deprecated; use the .length property" );
	return this.length;
};


var internalSwapCall = false;

// If this version of jQuery has .swap(), don't false-alarm on internal uses
if ( jQuery.swap ) {
	jQuery.each( [ "height", "width", "reliableMarginRight" ], function( _, name ) {
		var oldHook = jQuery.cssHooks[ name ] && jQuery.cssHooks[ name ].get;

		if ( oldHook ) {
			jQuery.cssHooks[ name ].get = function() {
				var ret;

				internalSwapCall = true;
				ret = oldHook.apply( this, arguments );
				internalSwapCall = false;
				return ret;
			};
		}
	});
}

jQuery.swap = function( elem, options, callback, args ) {
	var ret, name,
		old = {};

	if ( !internalSwapCall ) {
		migrateWarn( "jQuery.swap() is undocumented and deprecated" );
	}

	// Remember the old values, and insert the new ones
	for ( name in options ) {
		old[ name ] = elem.style[ name ];
		elem.style[ name ] = options[ name ];
	}

	ret = callback.apply( elem, args || [] );

	// Revert the old values
	for ( name in options ) {
		elem.style[ name ] = old[ name ];
	}

	return ret;
};


// Ensure that $.ajax gets the new parseJSON defined in core.js
jQuery.ajaxSetup({
	converters: {
		"text json": jQuery.parseJSON
	}
});


var oldFnData = jQuery.fn.data;

jQuery.fn.data = function( name ) {
	var ret, evt,
		elem = this[0];

	// Handles 1.7 which has this behavior and 1.8 which doesn't
	if ( elem && name === "events" && arguments.length === 1 ) {
		ret = jQuery.data( elem, name );
		evt = jQuery._data( elem, name );
		if ( ( ret === undefined || ret === evt ) && evt !== undefined ) {
			migrateWarn("Use of jQuery.fn.data('events') is deprecated");
			return evt;
		}
	}
	return oldFnData.apply( this, arguments );
};


var rscriptType = /\/(java|ecma)script/i;

// Since jQuery.clean is used internally on older versions, we only shim if it's missing
if ( !jQuery.clean ) {
	jQuery.clean = function( elems, context, fragment, scripts ) {
		// Set context per 1.8 logic
		context = context || document;
		context = !context.nodeType && context[0] || context;
		context = context.ownerDocument || context;

		migrateWarn("jQuery.clean() is deprecated");

		var i, elem, handleScript, jsTags,
			ret = [];

		jQuery.merge( ret, jQuery.buildFragment( elems, context ).childNodes );

		// Complex logic lifted directly from jQuery 1.8
		if ( fragment ) {
			// Special handling of each script element
			handleScript = function( elem ) {
				// Check if we consider it executable
				if ( !elem.type || rscriptType.test( elem.type ) ) {
					// Detach the script and store it in the scripts array (if provided) or the fragment
					// Return truthy to indicate that it has been handled
					return scripts ?
						scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
						fragment.appendChild( elem );
				}
			};

			for ( i = 0; (elem = ret[i]) != null; i++ ) {
				// Check if we're done after handling an executable script
				if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
					// Append to fragment and handle embedded scripts
					fragment.appendChild( elem );
					if ( typeof elem.getElementsByTagName !== "undefined" ) {
						// handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
						jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );

						// Splice the scripts into ret after their former ancestor and advance our index beyond them
						ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
						i += jsTags.length;
					}
				}
			}
		}

		return ret;
	};
}

var eventAdd = jQuery.event.add,
	eventRemove = jQuery.event.remove,
	eventTrigger = jQuery.event.trigger,
	oldToggle = jQuery.fn.toggle,
	oldLive = jQuery.fn.live,
	oldDie = jQuery.fn.die,
	oldLoad = jQuery.fn.load,
	ajaxEvents = "ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",
	rajaxEvent = new RegExp( "\\b(?:" + ajaxEvents + ")\\b" ),
	rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
	hoverHack = function( events ) {
		if ( typeof( events ) !== "string" || jQuery.event.special.hover ) {
			return events;
		}
		if ( rhoverHack.test( events ) ) {
			migrateWarn("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'");
		}
		return events && events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
	};

// Event props removed in 1.9, put them back if needed; no practical way to warn them
if ( jQuery.event.props && jQuery.event.props[ 0 ] !== "attrChange" ) {
	jQuery.event.props.unshift( "attrChange", "attrName", "relatedNode", "srcElement" );
}

// Undocumented jQuery.event.handle was "deprecated" in jQuery 1.7
if ( jQuery.event.dispatch ) {
	migrateWarnProp( jQuery.event, "handle", jQuery.event.dispatch, "jQuery.event.handle is undocumented and deprecated" );
}

// Support for 'hover' pseudo-event and ajax event warnings
jQuery.event.add = function( elem, types, handler, data, selector ){
	if ( elem !== document && rajaxEvent.test( types ) ) {
		migrateWarn( "AJAX events should be attached to document: " + types );
	}
	eventAdd.call( this, elem, hoverHack( types || "" ), handler, data, selector );
};
jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ){
	eventRemove.call( this, elem, hoverHack( types ) || "", handler, selector, mappedTypes );
};

jQuery.each( [ "load", "unload", "error" ], function( _, name ) {

	jQuery.fn[ name ] = function() {
		var args = Array.prototype.slice.call( arguments, 0 );

		// If this is an ajax load() the first arg should be the string URL;
		// technically this could also be the "Anything" arg of the event .load()
		// which just goes to show why this dumb signature has been deprecated!
		// jQuery custom builds that exclude the Ajax module justifiably die here.
		if ( name === "load" && typeof args[ 0 ] === "string" ) {
			return oldLoad.apply( this, args );
		}

		migrateWarn( "jQuery.fn." + name + "() is deprecated" );

		args.splice( 0, 0, name );
		if ( arguments.length ) {
			return this.bind.apply( this, args );
		}

		// Use .triggerHandler here because:
		// - load and unload events don't need to bubble, only applied to window or image
		// - error event should not bubble to window, although it does pre-1.7
		// See http://bugs.jquery.com/ticket/11820
		this.triggerHandler.apply( this, args );
		return this;
	};

});

jQuery.fn.toggle = function( fn, fn2 ) {

	// Don't mess with animation or css toggles
	if ( !jQuery.isFunction( fn ) || !jQuery.isFunction( fn2 ) ) {
		return oldToggle.apply( this, arguments );
	}
	migrateWarn("jQuery.fn.toggle(handler, handler...) is deprecated");

	// Save reference to arguments for access in closure
	var args = arguments,
		guid = fn.guid || jQuery.guid++,
		i = 0,
		toggler = function( event ) {
			// Figure out which function to execute
			var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
			jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );

			// Make sure that clicks stop
			event.preventDefault();

			// and execute the function
			return args[ lastToggle ].apply( this, arguments ) || false;
		};

	// link all the functions, so any of them can unbind this click handler
	toggler.guid = guid;
	while ( i < args.length ) {
		args[ i++ ].guid = guid;
	}

	return this.click( toggler );
};

jQuery.fn.live = function( types, data, fn ) {
	migrateWarn("jQuery.fn.live() is deprecated");
	if ( oldLive ) {
		return oldLive.apply( this, arguments );
	}
	jQuery( this.context ).on( types, this.selector, data, fn );
	return this;
};

jQuery.fn.die = function( types, fn ) {
	migrateWarn("jQuery.fn.die() is deprecated");
	if ( oldDie ) {
		return oldDie.apply( this, arguments );
	}
	jQuery( this.context ).off( types, this.selector || "**", fn );
	return this;
};

// Turn global events into document-triggered events
jQuery.event.trigger = function( event, data, elem, onlyHandlers  ){
	if ( !elem && !rajaxEvent.test( event ) ) {
		migrateWarn( "Global events are undocumented and deprecated" );
	}
	return eventTrigger.call( this,  event, data, elem || document, onlyHandlers  );
};
jQuery.each( ajaxEvents.split("|"),
	function( _, name ) {
		jQuery.event.special[ name ] = {
			setup: function() {
				var elem = this;

				// The document needs no shimming; must be !== for oldIE
				if ( elem !== document ) {
					jQuery.event.add( document, name + "." + jQuery.guid, function() {
						jQuery.event.trigger( name, Array.prototype.slice.call( arguments, 1 ), elem, true );
					});
					jQuery._data( this, name, jQuery.guid++ );
				}
				return false;
			},
			teardown: function() {
				if ( this !== document ) {
					jQuery.event.remove( document, name + "." + jQuery._data( this, name ) );
				}
				return false;
			}
		};
	}
);

jQuery.event.special.ready = {
	setup: function() {
		if ( this === document ) {
			migrateWarn( "'ready' event is deprecated" );
		}
	}
};

var oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack,
	oldFnFind = jQuery.fn.find;

jQuery.fn.andSelf = function() {
	migrateWarn("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()");
	return oldSelf.apply( this, arguments );
};

jQuery.fn.find = function( selector ) {
	var ret = oldFnFind.apply( this, arguments );
	ret.context = this.context;
	ret.selector = this.selector ? this.selector + " " + selector : selector;
	return ret;
};


// jQuery 1.6 did not support Callbacks, do not warn there
if ( jQuery.Callbacks ) {

	var oldDeferred = jQuery.Deferred,
		tuples = [
			// action, add listener, callbacks, .then handlers, final state
			[ "resolve", "done", jQuery.Callbacks("once memory"),
				jQuery.Callbacks("once memory"), "resolved" ],
			[ "reject", "fail", jQuery.Callbacks("once memory"),
				jQuery.Callbacks("once memory"), "rejected" ],
			[ "notify", "progress", jQuery.Callbacks("memory"),
				jQuery.Callbacks("memory") ]
		];

	jQuery.Deferred = function( func ) {
		var deferred = oldDeferred(),
			promise = deferred.promise();

		deferred.pipe = promise.pipe = function( /* fnDone, fnFail, fnProgress */ ) {
			var fns = arguments;

			migrateWarn( "deferred.pipe() is deprecated" );

			return jQuery.Deferred(function( newDefer ) {
				jQuery.each( tuples, function( i, tuple ) {
					var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
					// deferred.done(function() { bind to newDefer or newDefer.resolve })
					// deferred.fail(function() { bind to newDefer or newDefer.reject })
					// deferred.progress(function() { bind to newDefer or newDefer.notify })
					deferred[ tuple[1] ](function() {
						var returned = fn && fn.apply( this, arguments );
						if ( returned && jQuery.isFunction( returned.promise ) ) {
							returned.promise()
								.done( newDefer.resolve )
								.fail( newDefer.reject )
								.progress( newDefer.notify );
						} else {
							newDefer[ tuple[ 0 ] + "With" ](
								this === promise ? newDefer.promise() : this,
								fn ? [ returned ] : arguments
							);
						}
					});
				});
				fns = null;
			}).promise();

		};

		deferred.isResolved = function() {
			migrateWarn( "deferred.isResolved is deprecated" );
			return deferred.state() === "resolved";
		};

		deferred.isRejected = function() {
			migrateWarn( "deferred.isRejected is deprecated" );
			return deferred.state() === "rejected";
		};

		if ( func ) {
			func.call( deferred, deferred );
		}

		return deferred;
	};

}

})( jQuery, window );

(function() {
  var AjaxMonitor, Bar, DocumentMonitor, ElementMonitor, ElementTracker, EventLagMonitor, Evented, Events, NoTargetError, Pace, RequestIntercept, SOURCE_KEYS, Scaler, SocketRequestTracker, XHRRequestTracker, animation, avgAmplitude, bar, cancelAnimation, cancelAnimationFrame, defaultOptions, extend, extendNative, getFromDOM, getIntercept, handlePushState, ignoreStack, init, now, options, requestAnimationFrame, result, runAnimation, scalers, shouldIgnoreURL, shouldTrack, source, sources, uniScaler, _WebSocket, _XDomainRequest, _XMLHttpRequest, _i, _intercept, _len, _pushState, _ref, _ref1, _replaceState,
    __slice = [].slice,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

  defaultOptions = {
    catchupTime: 100,
    initialRate: .03,
    minTime: 250,
    ghostTime: 100,
    maxProgressPerFrame: 20,
    easeFactor: 1.25,
    startOnPageLoad: true,
    restartOnPushState: true,
    restartOnRequestAfter: 500,
    target: 'body',
    elements: {
      checkInterval: 100,
      selectors: ['body']
    },
    eventLag: {
      minSamples: 10,
      sampleCount: 3,
      lagThreshold: 3
    },
    ajax: {
      trackMethods: ['GET'],
      trackWebSockets: true,
      ignoreURLs: []
    }
  };

  now = function() {
    var _ref;
    return (_ref = typeof performance !== "undefined" && performance !== null ? typeof performance.now === "function" ? performance.now() : void 0 : void 0) != null ? _ref : +(new Date);
  };

  requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;

  cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;

  if (requestAnimationFrame == null) {
    requestAnimationFrame = function(fn) {
      return setTimeout(fn, 50);
    };
    cancelAnimationFrame = function(id) {
      return clearTimeout(id);
    };
  }

  runAnimation = function(fn) {
    var last, tick;
    last = now();
    tick = function() {
      var diff;
      diff = now() - last;
      if (diff >= 33) {
        last = now();
        return fn(diff, function() {
          return requestAnimationFrame(tick);
        });
      } else {
        return setTimeout(tick, 33 - diff);
      }
    };
    return tick();
  };

  result = function() {
    var args, key, obj;
    obj = arguments[0], key = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : [];
    if (typeof obj[key] === 'function') {
      return obj[key].apply(obj, args);
    } else {
      return obj[key];
    }
  };

  extend = function() {
    var key, out, source, sources, val, _i, _len;
    out = arguments[0], sources = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
    for (_i = 0, _len = sources.length; _i < _len; _i++) {
      source = sources[_i];
      if (source) {
        for (key in source) {
          if (!__hasProp.call(source, key)) continue;
          val = source[key];
          if ((out[key] != null) && typeof out[key] === 'object' && (val != null) && typeof val === 'object') {
            extend(out[key], val);
          } else {
            out[key] = val;
          }
        }
      }
    }
    return out;
  };

  avgAmplitude = function(arr) {
    var count, sum, v, _i, _len;
    sum = count = 0;
    for (_i = 0, _len = arr.length; _i < _len; _i++) {
      v = arr[_i];
      sum += Math.abs(v);
      count++;
    }
    return sum / count;
  };

  getFromDOM = function(key, json) {
    var data, e, el;
    if (key == null) {
      key = 'options';
    }
    if (json == null) {
      json = true;
    }
    el = document.querySelector("[data-pace-" + key + "]");
    if (!el) {
      return;
    }
    data = el.getAttribute("data-pace-" + key);
    if (!json) {
      return data;
    }
    try {
      return JSON.parse(data);
    } catch (_error) {
      e = _error;
      return typeof console !== "undefined" && console !== null ? console.error("Error parsing inline pace options", e) : void 0;
    }
  };

  Evented = (function() {
    function Evented() {}

    Evented.prototype.on = function(event, handler, ctx, once) {
      var _base;
      if (once == null) {
        once = false;
      }
      if (this.bindings == null) {
        this.bindings = {};
      }
      if ((_base = this.bindings)[event] == null) {
        _base[event] = [];
      }
      return this.bindings[event].push({
        handler: handler,
        ctx: ctx,
        once: once
      });
    };

    Evented.prototype.once = function(event, handler, ctx) {
      return this.on(event, handler, ctx, true);
    };

    Evented.prototype.off = function(event, handler) {
      var i, _ref, _results;
      if (((_ref = this.bindings) != null ? _ref[event] : void 0) == null) {
        return;
      }
      if (handler == null) {
        return delete this.bindings[event];
      } else {
        i = 0;
        _results = [];
        while (i < this.bindings[event].length) {
          if (this.bindings[event][i].handler === handler) {
            _results.push(this.bindings[event].splice(i, 1));
          } else {
            _results.push(i++);
          }
        }
        return _results;
      }
    };

    Evented.prototype.trigger = function() {
      var args, ctx, event, handler, i, once, _ref, _ref1, _results;
      event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
      if ((_ref = this.bindings) != null ? _ref[event] : void 0) {
        i = 0;
        _results = [];
        while (i < this.bindings[event].length) {
          _ref1 = this.bindings[event][i], handler = _ref1.handler, ctx = _ref1.ctx, once = _ref1.once;
          handler.apply(ctx != null ? ctx : this, args);
          if (once) {
            _results.push(this.bindings[event].splice(i, 1));
          } else {
            _results.push(i++);
          }
        }
        return _results;
      }
    };

    return Evented;

  })();

  Pace = window.Pace || {};

  window.Pace = Pace;

  extend(Pace, Evented.prototype);

  options = Pace.options = extend({}, defaultOptions, window.paceOptions, getFromDOM());

  _ref = ['ajax', 'document', 'eventLag', 'elements'];
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
    source = _ref[_i];
    if (options[source] === true) {
      options[source] = defaultOptions[source];
    }
  }

  NoTargetError = (function(_super) {
    __extends(NoTargetError, _super);

    function NoTargetError() {
      _ref1 = NoTargetError.__super__.constructor.apply(this, arguments);
      return _ref1;
    }

    return NoTargetError;

  })(Error);

  Bar = (function() {
    function Bar() {
      this.progress = 0;
    }

    Bar.prototype.getElement = function() {
      var targetElement;
      if (this.el == null) {
        targetElement = document.querySelector(options.target);
        if (!targetElement) {
          throw new NoTargetError;
        }
        this.el = document.createElement('div');
        this.el.className = "pace pace-active";
        document.body.className = document.body.className.replace(/pace-done/g, '');
        document.body.className += ' pace-running';
        this.el.innerHTML = '<div class="pace-progress">\n  <div class="pace-progress-inner"></div>\n</div>\n<div class="pace-activity"></div>';
        if (targetElement.firstChild != null) {
          targetElement.insertBefore(this.el, targetElement.firstChild);
        } else {
          targetElement.appendChild(this.el);
        }
      }
      return this.el;
    };

    Bar.prototype.finish = function() {
      var el;
      el = this.getElement();
      el.className = el.className.replace('pace-active', '');
      el.className += ' pace-inactive';
      document.body.className = document.body.className.replace('pace-running', '');
      return document.body.className += ' pace-done';
    };

    Bar.prototype.update = function(prog) {
      this.progress = prog;
      return this.render();
    };

    Bar.prototype.destroy = function() {
      try {
        this.getElement().parentNode.removeChild(this.getElement());
      } catch (_error) {
        NoTargetError = _error;
      }
      return this.el = void 0;
    };

    Bar.prototype.render = function() {
      var el, key, progressStr, transform, _j, _len1, _ref2;
      if (document.querySelector(options.target) == null) {
        return false;
      }
      el = this.getElement();
      transform = "translate3d(" + this.progress + "%, 0, 0)";
      _ref2 = ['webkitTransform', 'msTransform', 'transform'];
      for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
        key = _ref2[_j];
        el.children[0].style[key] = transform;
      }
      if (!this.lastRenderedProgress || this.lastRenderedProgress | 0 !== this.progress | 0) {
        el.children[0].setAttribute('data-progress-text', "" + (this.progress | 0) + "%");
        if (this.progress >= 100) {
          progressStr = '99';
        } else {
          progressStr = this.progress < 10 ? "0" : "";
          progressStr += this.progress | 0;
        }
        el.children[0].setAttribute('data-progress', "" + progressStr);
      }
      return this.lastRenderedProgress = this.progress;
    };

    Bar.prototype.done = function() {
      return this.progress >= 100;
    };

    return Bar;

  })();

  Events = (function() {
    function Events() {
      this.bindings = {};
    }

    Events.prototype.trigger = function(name, val) {
      var binding, _j, _len1, _ref2, _results;
      if (this.bindings[name] != null) {
        _ref2 = this.bindings[name];
        _results = [];
        for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
          binding = _ref2[_j];
          _results.push(binding.call(this, val));
        }
        return _results;
      }
    };

    Events.prototype.on = function(name, fn) {
      var _base;
      if ((_base = this.bindings)[name] == null) {
        _base[name] = [];
      }
      return this.bindings[name].push(fn);
    };

    return Events;

  })();

  _XMLHttpRequest = window.XMLHttpRequest;

  _XDomainRequest = window.XDomainRequest;

  _WebSocket = window.WebSocket;

  extendNative = function(to, from) {
    var e, key, _results;
    _results = [];
    for (key in from.prototype) {
      try {
        if ((to[key] == null) && typeof from[key] !== 'function') {
          if (typeof Object.defineProperty === 'function') {
            _results.push(Object.defineProperty(to, key, {
              get: function() {
                return from.prototype[key];
              },
              configurable: true,
              enumerable: true
            }));
          } else {
            _results.push(to[key] = from.prototype[key]);
          }
        } else {
          _results.push(void 0);
        }
      } catch (_error) {
        e = _error;
      }
    }
    return _results;
  };

  ignoreStack = [];

  Pace.ignore = function() {
    var args, fn, ret;
    fn = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
    ignoreStack.unshift('ignore');
    ret = fn.apply(null, args);
    ignoreStack.shift();
    return ret;
  };

  Pace.track = function() {
    var args, fn, ret;
    fn = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
    ignoreStack.unshift('track');
    ret = fn.apply(null, args);
    ignoreStack.shift();
    return ret;
  };

  shouldTrack = function(method) {
    var _ref2;
    if (method == null) {
      method = 'GET';
    }
    if (ignoreStack[0] === 'track') {
      return 'force';
    }
    if (!ignoreStack.length && options.ajax) {
      if (method === 'socket' && options.ajax.trackWebSockets) {
        return true;
      } else if (_ref2 = method.toUpperCase(), __indexOf.call(options.ajax.trackMethods, _ref2) >= 0) {
        return true;
      }
    }
    return false;
  };

  RequestIntercept = (function(_super) {
    __extends(RequestIntercept, _super);

    function RequestIntercept() {
      var monitorXHR,
        _this = this;
      RequestIntercept.__super__.constructor.apply(this, arguments);
      monitorXHR = function(req) {
        var _open;
        _open = req.open;
        return req.open = function(type, url, async) {
          if (shouldTrack(type)) {
            _this.trigger('request', {
              type: type,
              url: url,
              request: req
            });
          }
          return _open.apply(req, arguments);
        };
      };
      window.XMLHttpRequest = function(flags) {
        var req;
        req = new _XMLHttpRequest(flags);
        monitorXHR(req);
        return req;
      };
      try {
        extendNative(window.XMLHttpRequest, _XMLHttpRequest);
      } catch (_error) {}
      if (_XDomainRequest != null) {
        window.XDomainRequest = function() {
          var req;
          req = new _XDomainRequest;
          monitorXHR(req);
          return req;
        };
        try {
          extendNative(window.XDomainRequest, _XDomainRequest);
        } catch (_error) {}
      }
      if ((_WebSocket != null) && options.ajax.trackWebSockets) {
        window.WebSocket = function(url, protocols) {
          var req;
          if (protocols != null) {
            req = new _WebSocket(url, protocols);
          } else {
            req = new _WebSocket(url);
          }
          if (shouldTrack('socket')) {
            _this.trigger('request', {
              type: 'socket',
              url: url,
              protocols: protocols,
              request: req
            });
          }
          return req;
        };
        try {
          extendNative(window.WebSocket, _WebSocket);
        } catch (_error) {}
      }
    }

    return RequestIntercept;

  })(Events);

  _intercept = null;

  getIntercept = function() {
    if (_intercept == null) {
      _intercept = new RequestIntercept;
    }
    return _intercept;
  };

  shouldIgnoreURL = function(url) {
    var pattern, _j, _len1, _ref2;
    _ref2 = options.ajax.ignoreURLs;
    for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
      pattern = _ref2[_j];
      if (typeof pattern === 'string') {
        if (url.indexOf(pattern) !== -1) {
          return true;
        }
      } else {
        if (pattern.test(url)) {
          return true;
        }
      }
    }
    return false;
  };

  getIntercept().on('request', function(_arg) {
    var after, args, request, type, url;
    type = _arg.type, request = _arg.request, url = _arg.url;
    if (shouldIgnoreURL(url)) {
      return;
    }
    if (!Pace.running && (options.restartOnRequestAfter !== false || shouldTrack(type) === 'force')) {
      args = arguments;
      after = options.restartOnRequestAfter || 0;
      if (typeof after === 'boolean') {
        after = 0;
      }
      return setTimeout(function() {
        var stillActive, _j, _len1, _ref2, _ref3, _results;
        if (type === 'socket') {
          stillActive = request.readyState < 2;
        } else {
          stillActive = (0 < (_ref2 = request.readyState) && _ref2 < 4);
        }
        if (stillActive) {
          Pace.restart();
          _ref3 = Pace.sources;
          _results = [];
          for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
            source = _ref3[_j];
            if (source instanceof AjaxMonitor) {
              source.watch.apply(source, args);
              break;
            } else {
              _results.push(void 0);
            }
          }
          return _results;
        }
      }, after);
    }
  });

  AjaxMonitor = (function() {
    function AjaxMonitor() {
      var _this = this;
      this.elements = [];
      getIntercept().on('request', function() {
        return _this.watch.apply(_this, arguments);
      });
    }

    AjaxMonitor.prototype.watch = function(_arg) {
      var request, tracker, type, url;
      type = _arg.type, request = _arg.request, url = _arg.url;
      if (shouldIgnoreURL(url)) {
        return;
      }
      if (type === 'socket') {
        tracker = new SocketRequestTracker(request);
      } else {
        tracker = new XHRRequestTracker(request);
      }
      return this.elements.push(tracker);
    };

    return AjaxMonitor;

  })();

  XHRRequestTracker = (function() {
    function XHRRequestTracker(request) {
      var event, size, _j, _len1, _onreadystatechange, _ref2,
        _this = this;
      this.progress = 0;
      if (window.ProgressEvent != null) {
        size = null;
        request.addEventListener('progress', function(evt) {
          if (evt.lengthComputable) {
            return _this.progress = 100 * evt.loaded / evt.total;
          } else {
            return _this.progress = _this.progress + (100 - _this.progress) / 2;
          }
        }, false);
        _ref2 = ['load', 'abort', 'timeout', 'error'];
        for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
          event = _ref2[_j];
          request.addEventListener(event, function() {
            return _this.progress = 100;
          }, false);
        }
      } else {
        _onreadystatechange = request.onreadystatechange;
        request.onreadystatechange = function() {
          var _ref3;
          if ((_ref3 = request.readyState) === 0 || _ref3 === 4) {
            _this.progress = 100;
          } else if (request.readyState === 3) {
            _this.progress = 50;
          }
          return typeof _onreadystatechange === "function" ? _onreadystatechange.apply(null, arguments) : void 0;
        };
      }
    }

    return XHRRequestTracker;

  })();

  SocketRequestTracker = (function() {
    function SocketRequestTracker(request) {
      var event, _j, _len1, _ref2,
        _this = this;
      this.progress = 0;
      _ref2 = ['error', 'open'];
      for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
        event = _ref2[_j];
        request.addEventListener(event, function() {
          return _this.progress = 100;
        }, false);
      }
    }

    return SocketRequestTracker;

  })();

  ElementMonitor = (function() {
    function ElementMonitor(options) {
      var selector, _j, _len1, _ref2;
      if (options == null) {
        options = {};
      }
      this.elements = [];
      if (options.selectors == null) {
        options.selectors = [];
      }
      _ref2 = options.selectors;
      for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
        selector = _ref2[_j];
        this.elements.push(new ElementTracker(selector));
      }
    }

    return ElementMonitor;

  })();

  ElementTracker = (function() {
    function ElementTracker(selector) {
      this.selector = selector;
      this.progress = 0;
      this.check();
    }

    ElementTracker.prototype.check = function() {
      var _this = this;
      if (document.querySelector(this.selector)) {
        return this.done();
      } else {
        return setTimeout((function() {
          return _this.check();
        }), options.elements.checkInterval);
      }
    };

    ElementTracker.prototype.done = function() {
      return this.progress = 100;
    };

    return ElementTracker;

  })();

  DocumentMonitor = (function() {
    DocumentMonitor.prototype.states = {
      loading: 0,
      interactive: 50,
      complete: 100
    };

    function DocumentMonitor() {
      var _onreadystatechange, _ref2,
        _this = this;
      this.progress = (_ref2 = this.states[document.readyState]) != null ? _ref2 : 100;
      _onreadystatechange = document.onreadystatechange;
      document.onreadystatechange = function() {
        if (_this.states[document.readyState] != null) {
          _this.progress = _this.states[document.readyState];
        }
        return typeof _onreadystatechange === "function" ? _onreadystatechange.apply(null, arguments) : void 0;
      };
    }

    return DocumentMonitor;

  })();

  EventLagMonitor = (function() {
    function EventLagMonitor() {
      var avg, interval, last, points, samples,
        _this = this;
      this.progress = 0;
      avg = 0;
      samples = [];
      points = 0;
      last = now();
      interval = setInterval(function() {
        var diff;
        diff = now() - last - 50;
        last = now();
        samples.push(diff);
        if (samples.length > options.eventLag.sampleCount) {
          samples.shift();
        }
        avg = avgAmplitude(samples);
        if (++points >= options.eventLag.minSamples && avg < options.eventLag.lagThreshold) {
          _this.progress = 100;
          return clearInterval(interval);
        } else {
          return _this.progress = 100 * (3 / (avg + 3));
        }
      }, 50);
    }

    return EventLagMonitor;

  })();

  Scaler = (function() {
    function Scaler(source) {
      this.source = source;
      this.last = this.sinceLastUpdate = 0;
      this.rate = options.initialRate;
      this.catchup = 0;
      this.progress = this.lastProgress = 0;
      if (this.source != null) {
        this.progress = result(this.source, 'progress');
      }
    }

    Scaler.prototype.tick = function(frameTime, val) {
      var scaling;
      if (val == null) {
        val = result(this.source, 'progress');
      }
      if (val >= 100) {
        this.done = true;
      }
      if (val === this.last) {
        this.sinceLastUpdate += frameTime;
      } else {
        if (this.sinceLastUpdate) {
          this.rate = (val - this.last) / this.sinceLastUpdate;
        }
        this.catchup = (val - this.progress) / options.catchupTime;
        this.sinceLastUpdate = 0;
        this.last = val;
      }
      if (val > this.progress) {
        this.progress += this.catchup * frameTime;
      }
      scaling = 1 - Math.pow(this.progress / 100, options.easeFactor);
      this.progress += scaling * this.rate * frameTime;
      this.progress = Math.min(this.lastProgress + options.maxProgressPerFrame, this.progress);
      this.progress = Math.max(0, this.progress);
      this.progress = Math.min(100, this.progress);
      this.lastProgress = this.progress;
      return this.progress;
    };

    return Scaler;

  })();

  sources = null;

  scalers = null;

  bar = null;

  uniScaler = null;

  animation = null;

  cancelAnimation = null;

  Pace.running = false;

  handlePushState = function() {
    if (options.restartOnPushState) {
      return Pace.restart();
    }
  };

  if (window.history.pushState != null) {
    _pushState = window.history.pushState;
    window.history.pushState = function() {
      handlePushState();
      return _pushState.apply(window.history, arguments);
    };
  }

  if (window.history.replaceState != null) {
    _replaceState = window.history.replaceState;
    window.history.replaceState = function() {
      handlePushState();
      return _replaceState.apply(window.history, arguments);
    };
  }

  SOURCE_KEYS = {
    ajax: AjaxMonitor,
    elements: ElementMonitor,
    document: DocumentMonitor,
    eventLag: EventLagMonitor
  };

  (init = function() {
    var type, _j, _k, _len1, _len2, _ref2, _ref3, _ref4;
    Pace.sources = sources = [];
    _ref2 = ['ajax', 'elements', 'document', 'eventLag'];
    for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
      type = _ref2[_j];
      if (options[type] !== false) {
        sources.push(new SOURCE_KEYS[type](options[type]));
      }
    }
    _ref4 = (_ref3 = options.extraSources) != null ? _ref3 : [];
    for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) {
      source = _ref4[_k];
      sources.push(new source(options));
    }
    Pace.bar = bar = new Bar;
    scalers = [];
    return uniScaler = new Scaler;
  })();

  Pace.stop = function() {
    Pace.trigger('stop');
    Pace.running = false;
    bar.destroy();
    cancelAnimation = true;
    if (animation != null) {
      if (typeof cancelAnimationFrame === "function") {
        cancelAnimationFrame(animation);
      }
      animation = null;
    }
    return init();
  };

  Pace.restart = function() {
    Pace.trigger('restart');
    Pace.stop();
    return Pace.start();
  };

  Pace.go = function() {
    var start;
    Pace.running = true;
    bar.render();
    start = now();
    cancelAnimation = false;
    return animation = runAnimation(function(frameTime, enqueueNextFrame) {
      var avg, count, done, element, elements, i, j, remaining, scaler, scalerList, sum, _j, _k, _len1, _len2, _ref2;
      remaining = 100 - bar.progress;
      count = sum = 0;
      done = true;
      for (i = _j = 0, _len1 = sources.length; _j < _len1; i = ++_j) {
        source = sources[i];
        scalerList = scalers[i] != null ? scalers[i] : scalers[i] = [];
        elements = (_ref2 = source.elements) != null ? _ref2 : [source];
        for (j = _k = 0, _len2 = elements.length; _k < _len2; j = ++_k) {
          element = elements[j];
          scaler = scalerList[j] != null ? scalerList[j] : scalerList[j] = new Scaler(element);
          done &= scaler.done;
          if (scaler.done) {
            continue;
          }
          count++;
          sum += scaler.tick(frameTime);
        }
      }
      avg = sum / count;
      bar.update(uniScaler.tick(frameTime, avg));
      if (bar.done() || done || cancelAnimation) {
        bar.update(100);
        Pace.trigger('done');
        return setTimeout(function() {
          bar.finish();
          Pace.running = false;
          return Pace.trigger('hide');
        }, Math.max(options.ghostTime, Math.max(options.minTime - (now() - start), 0)));
      } else {
        return enqueueNextFrame();
      }
    });
  };

  Pace.start = function(_options) {
    extend(options, _options);
    Pace.running = true;
    try {
      bar.render();
    } catch (_error) {
      NoTargetError = _error;
    }
    if (!document.querySelector('.pace')) {
      return setTimeout(Pace.start, 50);
    } else {
      Pace.trigger('start');
      return Pace.go();
    }
  };

  if (typeof define === 'function' && define.amd) {
    define(['pace'], function() {
      return Pace;
    });
  } else if (typeof exports === 'object') {
    module.exports = Pace;
  } else {
    if (options.startOnPageLoad) {
      Pace.start();
    }
  }

}).call(this);

(function(root, factory) {
	// `root` does not resolve to the global window object in a Browserified
	// bundle, so a direct reference to that object is used instead.
	var _srcDoc = window.srcDoc;

	if (typeof define === "function" && define.amd) {
		define(['exports'], function(exports) {
			factory(exports, _srcDoc);
			root.srcDoc = exports;
		});
	} else if (typeof exports === "object") {
		factory(exports, _srcDoc);
	} else {
		root.srcDoc = {};
		factory(root.srcDoc, _srcDoc);
	}
})(this, function(exports, _srcDoc) {
	var idx, iframes;
	var isCompliant = !!("srcdoc" in document.createElement("iframe"));
	var implementations = {
		compliant: function( iframe, content ) {

			if (content) {
				iframe.setAttribute("srcdoc", content);
			}
		},
		legacy: function( iframe, content ) {

			var jsUrl;

			if (!iframe || !iframe.getAttribute) {
				return;
			}

			if (!content) {
				content = iframe.getAttribute("srcdoc");
			} else {
				iframe.setAttribute("srcdoc", content);
			}

			if (content) {
				// The value returned by a script-targeted URL will be used as
				// the iFrame's content. Create such a URL which returns the
				// iFrame element's `srcdoc` attribute.
				jsUrl = "javascript: window.frameElement.getAttribute('srcdoc');";

				iframe.setAttribute("src", jsUrl);

				// Explicitly set the iFrame's window.location for
				// compatability with IE9, which does not react to changes in
				// the `src` attribute when it is a `javascript:` URL, for
				// some reason
				if (iframe.contentWindow) {
					iframe.contentWindow.location = jsUrl;
				}
			}
		}
	};
	var srcDoc = exports;
	// Assume the best
	srcDoc.set = implementations.compliant;
	srcDoc.noConflict = function() {
		window.srcDoc = _srcDoc;
		return srcDoc;
	};

	// If the browser supports srcdoc, no shimming is necessary
	if (isCompliant) {
		return;
	}

	srcDoc.set = implementations.legacy;

	// Automatically shim any iframes already present in the document
	iframes = document.getElementsByTagName("iframe");
	idx = iframes.length;

	while (idx--) {
		srcDoc.set( iframes[idx] );
	}

});

/*! jQuery UI - v1.12.1 - 2016-09-14
* http://jqueryui.com
* Includes: widget.js, position.js, data.js, disable-selection.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js, focusable.js, form-reset-mixin.js, jquery-1-7.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/accordion.js, widgets/autocomplete.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/draggable.js, widgets/droppable.js, widgets/menu.js, widgets/mouse.js, widgets/progressbar.js, widgets/resizable.js, widgets/selectable.js, widgets/selectmenu.js, widgets/slider.js, widgets/sortable.js, widgets/spinner.js, widgets/tabs.js, widgets/tooltip.js
* Copyright jQuery Foundation and other contributors; Licensed MIT */

(function( factory ) {
	if ( typeof define === "function" && define.amd ) {

		// AMD. Register as an anonymous module.
		define([ "jquery" ], factory );
	} else {

		// Browser globals
		factory( jQuery );
	}
}(function( $ ) {

$.ui = $.ui || {};

var version = $.ui.version = "1.12.1";


/*!
 * jQuery UI Widget 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Widget
//>>group: Core
//>>description: Provides a factory for creating stateful widgets with a common API.
//>>docs: http://api.jqueryui.com/jQuery.widget/
//>>demos: http://jqueryui.com/widget/



var widgetUuid = 0;
var widgetSlice = Array.prototype.slice;

$.cleanData = ( function( orig ) {
	return function( elems ) {
		var events, elem, i;
		for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
			try {

				// Only trigger remove when necessary to save time
				events = $._data( elem, "events" );
				if ( events && events.remove ) {
					$( elem ).triggerHandler( "remove" );
				}

			// Http://bugs.jquery.com/ticket/8235
			} catch ( e ) {}
		}
		orig( elems );
	};
} )( $.cleanData );

$.widget = function( name, base, prototype ) {
	var existingConstructor, constructor, basePrototype;

	// ProxiedPrototype allows the provided prototype to remain unmodified
	// so that it can be used as a mixin for multiple widgets (#8876)
	var proxiedPrototype = {};

	var namespace = name.split( "." )[ 0 ];
	name = name.split( "." )[ 1 ];
	var fullName = namespace + "-" + name;

	if ( !prototype ) {
		prototype = base;
		base = $.Widget;
	}

	if ( $.isArray( prototype ) ) {
		prototype = $.extend.apply( null, [ {} ].concat( prototype ) );
	}

	// Create selector for plugin
	$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
		return !!$.data( elem, fullName );
	};

	$[ namespace ] = $[ namespace ] || {};
	existingConstructor = $[ namespace ][ name ];
	constructor = $[ namespace ][ name ] = function( options, element ) {

		// Allow instantiation without "new" keyword
		if ( !this._createWidget ) {
			return new constructor( options, element );
		}

		// Allow instantiation without initializing for simple inheritance
		// must use "new" keyword (the code above always passes args)
		if ( arguments.length ) {
			this._createWidget( options, element );
		}
	};

	// Extend with the existing constructor to carry over any static properties
	$.extend( constructor, existingConstructor, {
		version: prototype.version,

		// Copy the object used to create the prototype in case we need to
		// redefine the widget later
		_proto: $.extend( {}, prototype ),

		// Track widgets that inherit from this widget in case this widget is
		// redefined after a widget inherits from it
		_childConstructors: []
	} );

	basePrototype = new base();

	// We need to make the options hash a property directly on the new instance
	// otherwise we'll modify the options hash on the prototype that we're
	// inheriting from
	basePrototype.options = $.widget.extend( {}, basePrototype.options );
	$.each( prototype, function( prop, value ) {
		if ( !$.isFunction( value ) ) {
			proxiedPrototype[ prop ] = value;
			return;
		}
		proxiedPrototype[ prop ] = ( function() {
			function _super() {
				return base.prototype[ prop ].apply( this, arguments );
			}

			function _superApply( args ) {
				return base.prototype[ prop ].apply( this, args );
			}

			return function() {
				var __super = this._super;
				var __superApply = this._superApply;
				var returnValue;

				this._super = _super;
				this._superApply = _superApply;

				returnValue = value.apply( this, arguments );

				this._super = __super;
				this._superApply = __superApply;

				return returnValue;
			};
		} )();
	} );
	constructor.prototype = $.widget.extend( basePrototype, {

		// TODO: remove support for widgetEventPrefix
		// always use the name + a colon as the prefix, e.g., draggable:start
		// don't prefix for widgets that aren't DOM-based
		widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name
	}, proxiedPrototype, {
		constructor: constructor,
		namespace: namespace,
		widgetName: name,
		widgetFullName: fullName
	} );

	// If this widget is being redefined then we need to find all widgets that
	// are inheriting from it and redefine all of them so that they inherit from
	// the new version of this widget. We're essentially trying to replace one
	// level in the prototype chain.
	if ( existingConstructor ) {
		$.each( existingConstructor._childConstructors, function( i, child ) {
			var childPrototype = child.prototype;

			// Redefine the child widget using the same prototype that was
			// originally used, but inherit from the new version of the base
			$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor,
				child._proto );
		} );

		// Remove the list of existing child constructors from the old constructor
		// so the old child constructors can be garbage collected
		delete existingConstructor._childConstructors;
	} else {
		base._childConstructors.push( constructor );
	}

	$.widget.bridge( name, constructor );

	return constructor;
};

$.widget.extend = function( target ) {
	var input = widgetSlice.call( arguments, 1 );
	var inputIndex = 0;
	var inputLength = input.length;
	var key;
	var value;

	for ( ; inputIndex < inputLength; inputIndex++ ) {
		for ( key in input[ inputIndex ] ) {
			value = input[ inputIndex ][ key ];
			if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {

				// Clone objects
				if ( $.isPlainObject( value ) ) {
					target[ key ] = $.isPlainObject( target[ key ] ) ?
						$.widget.extend( {}, target[ key ], value ) :

						// Don't extend strings, arrays, etc. with objects
						$.widget.extend( {}, value );

				// Copy everything else by reference
				} else {
					target[ key ] = value;
				}
			}
		}
	}
	return target;
};

$.widget.bridge = function( name, object ) {
	var fullName = object.prototype.widgetFullName || name;
	$.fn[ name ] = function( options ) {
		var isMethodCall = typeof options === "string";
		var args = widgetSlice.call( arguments, 1 );
		var returnValue = this;

		if ( isMethodCall ) {

			// If this is an empty collection, we need to have the instance method
			// return undefined instead of the jQuery instance
			if ( !this.length && options === "instance" ) {
				returnValue = undefined;
			} else {
				this.each( function() {
					var methodValue;
					var instance = $.data( this, fullName );

					if ( options === "instance" ) {
						returnValue = instance;
						return false;
					}

					if ( !instance ) {
						return $.error( "cannot call methods on " + name +
							" prior to initialization; " +
							"attempted to call method '" + options + "'" );
					}

					if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) {
						return $.error( "no such method '" + options + "' for " + name +
							" widget instance" );
					}

					methodValue = instance[ options ].apply( instance, args );

					if ( methodValue !== instance && methodValue !== undefined ) {
						returnValue = methodValue && methodValue.jquery ?
							returnValue.pushStack( methodValue.get() ) :
							methodValue;
						return false;
					}
				} );
			}
		} else {

			// Allow multiple hashes to be passed on init
			if ( args.length ) {
				options = $.widget.extend.apply( null, [ options ].concat( args ) );
			}

			this.each( function() {
				var instance = $.data( this, fullName );
				if ( instance ) {
					instance.option( options || {} );
					if ( instance._init ) {
						instance._init();
					}
				} else {
					$.data( this, fullName, new object( options, this ) );
				}
			} );
		}

		return returnValue;
	};
};

$.Widget = function( /* options, element */ ) {};
$.Widget._childConstructors = [];

$.Widget.prototype = {
	widgetName: "widget",
	widgetEventPrefix: "",
	defaultElement: "<div>",

	options: {
		classes: {},
		disabled: false,

		// Callbacks
		create: null
	},

	_createWidget: function( options, element ) {
		element = $( element || this.defaultElement || this )[ 0 ];
		this.element = $( element );
		this.uuid = widgetUuid++;
		this.eventNamespace = "." + this.widgetName + this.uuid;

		this.bindings = $();
		this.hoverable = $();
		this.focusable = $();
		this.classesElementLookup = {};

		if ( element !== this ) {
			$.data( element, this.widgetFullName, this );
			this._on( true, this.element, {
				remove: function( event ) {
					if ( event.target === element ) {
						this.destroy();
					}
				}
			} );
			this.document = $( element.style ?

				// Element within the document
				element.ownerDocument :

				// Element is window or document
				element.document || element );
			this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );
		}

		this.options = $.widget.extend( {},
			this.options,
			this._getCreateOptions(),
			options );

		this._create();

		if ( this.options.disabled ) {
			this._setOptionDisabled( this.options.disabled );
		}

		this._trigger( "create", null, this._getCreateEventData() );
		this._init();
	},

	_getCreateOptions: function() {
		return {};
	},

	_getCreateEventData: $.noop,

	_create: $.noop,

	_init: $.noop,

	destroy: function() {
		var that = this;

		this._destroy();
		$.each( this.classesElementLookup, function( key, value ) {
			that._removeClass( value, key );
		} );

		// We can probably remove the unbind calls in 2.0
		// all event bindings should go through this._on()
		this.element
			.off( this.eventNamespace )
			.removeData( this.widgetFullName );
		this.widget()
			.off( this.eventNamespace )
			.removeAttr( "aria-disabled" );

		// Clean up events and states
		this.bindings.off( this.eventNamespace );
	},

	_destroy: $.noop,

	widget: function() {
		return this.element;
	},

	option: function( key, value ) {
		var options = key;
		var parts;
		var curOption;
		var i;

		if ( arguments.length === 0 ) {

			// Don't return a reference to the internal hash
			return $.widget.extend( {}, this.options );
		}

		if ( typeof key === "string" ) {

			// Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
			options = {};
			parts = key.split( "." );
			key = parts.shift();
			if ( parts.length ) {
				curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
				for ( i = 0; i < parts.length - 1; i++ ) {
					curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
					curOption = curOption[ parts[ i ] ];
				}
				key = parts.pop();
				if ( arguments.length === 1 ) {
					return curOption[ key ] === undefined ? null : curOption[ key ];
				}
				curOption[ key ] = value;
			} else {
				if ( arguments.length === 1 ) {
					return this.options[ key ] === undefined ? null : this.options[ key ];
				}
				options[ key ] = value;
			}
		}

		this._setOptions( options );

		return this;
	},

	_setOptions: function( options ) {
		var key;

		for ( key in options ) {
			this._setOption( key, options[ key ] );
		}

		return this;
	},

	_setOption: function( key, value ) {
		if ( key === "classes" ) {
			this._setOptionClasses( value );
		}

		this.options[ key ] = value;

		if ( key === "disabled" ) {
			this._setOptionDisabled( value );
		}

		return this;
	},

	_setOptionClasses: function( value ) {
		var classKey, elements, currentElements;

		for ( classKey in value ) {
			currentElements = this.classesElementLookup[ classKey ];
			if ( value[ classKey ] === this.options.classes[ classKey ] ||
					!currentElements ||
					!currentElements.length ) {
				continue;
			}

			// We are doing this to create a new jQuery object because the _removeClass() call
			// on the next line is going to destroy the reference to the current elements being
			// tracked. We need to save a copy of this collection so that we can add the new classes
			// below.
			elements = $( currentElements.get() );
			this._removeClass( currentElements, classKey );

			// We don't use _addClass() here, because that uses this.options.classes
			// for generating the string of classes. We want to use the value passed in from
			// _setOption(), this is the new value of the classes option which was passed to
			// _setOption(). We pass this value directly to _classes().
			elements.addClass( this._classes( {
				element: elements,
				keys: classKey,
				classes: value,
				add: true
			} ) );
		}
	},

	_setOptionDisabled: function( value ) {
		this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );

		// If the widget is becoming disabled, then nothing is interactive
		if ( value ) {
			this._removeClass( this.hoverable, null, "ui-state-hover" );
			this._removeClass( this.focusable, null, "ui-state-focus" );
		}
	},

	enable: function() {
		return this._setOptions( { disabled: false } );
	},

	disable: function() {
		return this._setOptions( { disabled: true } );
	},

	_classes: function( options ) {
		var full = [];
		var that = this;

		options = $.extend( {
			element: this.element,
			classes: this.options.classes || {}
		}, options );

		function processClassString( classes, checkOption ) {
			var current, i;
			for ( i = 0; i < classes.length; i++ ) {
				current = that.classesElementLookup[ classes[ i ] ] || $();
				if ( options.add ) {
					current = $( $.unique( current.get().concat( options.element.get() ) ) );
				} else {
					current = $( current.not( options.element ).get() );
				}
				that.classesElementLookup[ classes[ i ] ] = current;
				full.push( classes[ i ] );
				if ( checkOption && options.classes[ classes[ i ] ] ) {
					full.push( options.classes[ classes[ i ] ] );
				}
			}
		}

		this._on( options.element, {
			"remove": "_untrackClassesElement"
		} );

		if ( options.keys ) {
			processClassString( options.keys.match( /\S+/g ) || [], true );
		}
		if ( options.extra ) {
			processClassString( options.extra.match( /\S+/g ) || [] );
		}

		return full.join( " " );
	},

	_untrackClassesElement: function( event ) {
		var that = this;
		$.each( that.classesElementLookup, function( key, value ) {
			if ( $.inArray( event.target, value ) !== -1 ) {
				that.classesElementLookup[ key ] = $( value.not( event.target ).get() );
			}
		} );
	},

	_removeClass: function( element, keys, extra ) {
		return this._toggleClass( element, keys, extra, false );
	},

	_addClass: function( element, keys, extra ) {
		return this._toggleClass( element, keys, extra, true );
	},

	_toggleClass: function( element, keys, extra, add ) {
		add = ( typeof add === "boolean" ) ? add : extra;
		var shift = ( typeof element === "string" || element === null ),
			options = {
				extra: shift ? keys : extra,
				keys: shift ? element : keys,
				element: shift ? this.element : element,
				add: add
			};
		options.element.toggleClass( this._classes( options ), add );
		return this;
	},

	_on: function( suppressDisabledCheck, element, handlers ) {
		var delegateElement;
		var instance = this;

		// No suppressDisabledCheck flag, shuffle arguments
		if ( typeof suppressDisabledCheck !== "boolean" ) {
			handlers = element;
			element = suppressDisabledCheck;
			suppressDisabledCheck = false;
		}

		// No element argument, shuffle and use this.element
		if ( !handlers ) {
			handlers = element;
			element = this.element;
			delegateElement = this.widget();
		} else {
			element = delegateElement = $( element );
			this.bindings = this.bindings.add( element );
		}

		$.each( handlers, function( event, handler ) {
			function handlerProxy() {

				// Allow widgets to customize the disabled handling
				// - disabled as an array instead of boolean
				// - disabled class as method for disabling individual parts
				if ( !suppressDisabledCheck &&
						( instance.options.disabled === true ||
						$( this ).hasClass( "ui-state-disabled" ) ) ) {
					return;
				}
				return ( typeof handler === "string" ? instance[ handler ] : handler )
					.apply( instance, arguments );
			}

			// Copy the guid so direct unbinding works
			if ( typeof handler !== "string" ) {
				handlerProxy.guid = handler.guid =
					handler.guid || handlerProxy.guid || $.guid++;
			}

			var match = event.match( /^([\w:-]*)\s*(.*)$/ );
			var eventName = match[ 1 ] + instance.eventNamespace;
			var selector = match[ 2 ];

			if ( selector ) {
				delegateElement.on( eventName, selector, handlerProxy );
			} else {
				element.on( eventName, handlerProxy );
			}
		} );
	},

	_off: function( element, eventName ) {
		eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) +
			this.eventNamespace;
		element.off( eventName ).off( eventName );

		// Clear the stack to avoid memory leaks (#10056)
		this.bindings = $( this.bindings.not( element ).get() );
		this.focusable = $( this.focusable.not( element ).get() );
		this.hoverable = $( this.hoverable.not( element ).get() );
	},

	_delay: function( handler, delay ) {
		function handlerProxy() {
			return ( typeof handler === "string" ? instance[ handler ] : handler )
				.apply( instance, arguments );
		}
		var instance = this;
		return setTimeout( handlerProxy, delay || 0 );
	},

	_hoverable: function( element ) {
		this.hoverable = this.hoverable.add( element );
		this._on( element, {
			mouseenter: function( event ) {
				this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
			},
			mouseleave: function( event ) {
				this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
			}
		} );
	},

	_focusable: function( element ) {
		this.focusable = this.focusable.add( element );
		this._on( element, {
			focusin: function( event ) {
				this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
			},
			focusout: function( event ) {
				this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
			}
		} );
	},

	_trigger: function( type, event, data ) {
		var prop, orig;
		var callback = this.options[ type ];

		data = data || {};
		event = $.Event( event );
		event.type = ( type === this.widgetEventPrefix ?
			type :
			this.widgetEventPrefix + type ).toLowerCase();

		// The original event may come from any element
		// so we need to reset the target on the new event
		event.target = this.element[ 0 ];

		// Copy original event properties over to the new event
		orig = event.originalEvent;
		if ( orig ) {
			for ( prop in orig ) {
				if ( !( prop in event ) ) {
					event[ prop ] = orig[ prop ];
				}
			}
		}

		this.element.trigger( event, data );
		return !( $.isFunction( callback ) &&
			callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||
			event.isDefaultPrevented() );
	}
};

$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
	$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
		if ( typeof options === "string" ) {
			options = { effect: options };
		}

		var hasOptions;
		var effectName = !options ?
			method :
			options === true || typeof options === "number" ?
				defaultEffect :
				options.effect || defaultEffect;

		options = options || {};
		if ( typeof options === "number" ) {
			options = { duration: options };
		}

		hasOptions = !$.isEmptyObject( options );
		options.complete = callback;

		if ( options.delay ) {
			element.delay( options.delay );
		}

		if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
			element[ method ]( options );
		} else if ( effectName !== method && element[ effectName ] ) {
			element[ effectName ]( options.duration, options.easing, callback );
		} else {
			element.queue( function( next ) {
				$( this )[ method ]();
				if ( callback ) {
					callback.call( element[ 0 ] );
				}
				next();
			} );
		}
	};
} );

var widget = $.widget;


/*!
 * jQuery UI Position 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://api.jqueryui.com/position/
 */

//>>label: Position
//>>group: Core
//>>description: Positions elements relative to other elements.
//>>docs: http://api.jqueryui.com/position/
//>>demos: http://jqueryui.com/position/


( function() {
var cachedScrollbarWidth,
	max = Math.max,
	abs = Math.abs,
	rhorizontal = /left|center|right/,
	rvertical = /top|center|bottom/,
	roffset = /[\+\-]\d+(\.[\d]+)?%?/,
	rposition = /^\w+/,
	rpercent = /%$/,
	_position = $.fn.position;

function getOffsets( offsets, width, height ) {
	return [
		parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
		parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
	];
}

function parseCss( element, property ) {
	return parseInt( $.css( element, property ), 10 ) || 0;
}

function getDimensions( elem ) {
	var raw = elem[ 0 ];
	if ( raw.nodeType === 9 ) {
		return {
			width: elem.width(),
			height: elem.height(),
			offset: { top: 0, left: 0 }
		};
	}
	if ( $.isWindow( raw ) ) {
		return {
			width: elem.width(),
			height: elem.height(),
			offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
		};
	}
	if ( raw.preventDefault ) {
		return {
			width: 0,
			height: 0,
			offset: { top: raw.pageY, left: raw.pageX }
		};
	}
	return {
		width: elem.outerWidth(),
		height: elem.outerHeight(),
		offset: elem.offset()
	};
}

$.position = {
	scrollbarWidth: function() {
		if ( cachedScrollbarWidth !== undefined ) {
			return cachedScrollbarWidth;
		}
		var w1, w2,
			div = $( "<div " +
				"style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" +
				"<div style='height:100px;width:auto;'></div></div>" ),
			innerDiv = div.children()[ 0 ];

		$( "body" ).append( div );
		w1 = innerDiv.offsetWidth;
		div.css( "overflow", "scroll" );

		w2 = innerDiv.offsetWidth;

		if ( w1 === w2 ) {
			w2 = div[ 0 ].clientWidth;
		}

		div.remove();

		return ( cachedScrollbarWidth = w1 - w2 );
	},
	getScrollInfo: function( within ) {
		var overflowX = within.isWindow || within.isDocument ? "" :
				within.element.css( "overflow-x" ),
			overflowY = within.isWindow || within.isDocument ? "" :
				within.element.css( "overflow-y" ),
			hasOverflowX = overflowX === "scroll" ||
				( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ),
			hasOverflowY = overflowY === "scroll" ||
				( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight );
		return {
			width: hasOverflowY ? $.position.scrollbarWidth() : 0,
			height: hasOverflowX ? $.position.scrollbarWidth() : 0
		};
	},
	getWithinInfo: function( element ) {
		var withinElement = $( element || window ),
			isWindow = $.isWindow( withinElement[ 0 ] ),
			isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9,
			hasOffset = !isWindow && !isDocument;
		return {
			element: withinElement,
			isWindow: isWindow,
			isDocument: isDocument,
			offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 },
			scrollLeft: withinElement.scrollLeft(),
			scrollTop: withinElement.scrollTop(),
			width: withinElement.outerWidth(),
			height: withinElement.outerHeight()
		};
	}
};

$.fn.position = function( options ) {
	if ( !options || !options.of ) {
		return _position.apply( this, arguments );
	}

	// Make a copy, we don't want to modify arguments
	options = $.extend( {}, options );

	var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
		target = $( options.of ),
		within = $.position.getWithinInfo( options.within ),
		scrollInfo = $.position.getScrollInfo( within ),
		collision = ( options.collision || "flip" ).split( " " ),
		offsets = {};

	dimensions = getDimensions( target );
	if ( target[ 0 ].preventDefault ) {

		// Force left top to allow flipping
		options.at = "left top";
	}
	targetWidth = dimensions.width;
	targetHeight = dimensions.height;
	targetOffset = dimensions.offset;

	// Clone to reuse original targetOffset later
	basePosition = $.extend( {}, targetOffset );

	// Force my and at to have valid horizontal and vertical positions
	// if a value is missing or invalid, it will be converted to center
	$.each( [ "my", "at" ], function() {
		var pos = ( options[ this ] || "" ).split( " " ),
			horizontalOffset,
			verticalOffset;

		if ( pos.length === 1 ) {
			pos = rhorizontal.test( pos[ 0 ] ) ?
				pos.concat( [ "center" ] ) :
				rvertical.test( pos[ 0 ] ) ?
					[ "center" ].concat( pos ) :
					[ "center", "center" ];
		}
		pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
		pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";

		// Calculate offsets
		horizontalOffset = roffset.exec( pos[ 0 ] );
		verticalOffset = roffset.exec( pos[ 1 ] );
		offsets[ this ] = [
			horizontalOffset ? horizontalOffset[ 0 ] : 0,
			verticalOffset ? verticalOffset[ 0 ] : 0
		];

		// Reduce to just the positions without the offsets
		options[ this ] = [
			rposition.exec( pos[ 0 ] )[ 0 ],
			rposition.exec( pos[ 1 ] )[ 0 ]
		];
	} );

	// Normalize collision option
	if ( collision.length === 1 ) {
		collision[ 1 ] = collision[ 0 ];
	}

	if ( options.at[ 0 ] === "right" ) {
		basePosition.left += targetWidth;
	} else if ( options.at[ 0 ] === "center" ) {
		basePosition.left += targetWidth / 2;
	}

	if ( options.at[ 1 ] === "bottom" ) {
		basePosition.top += targetHeight;
	} else if ( options.at[ 1 ] === "center" ) {
		basePosition.top += targetHeight / 2;
	}

	atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
	basePosition.left += atOffset[ 0 ];
	basePosition.top += atOffset[ 1 ];

	return this.each( function() {
		var collisionPosition, using,
			elem = $( this ),
			elemWidth = elem.outerWidth(),
			elemHeight = elem.outerHeight(),
			marginLeft = parseCss( this, "marginLeft" ),
			marginTop = parseCss( this, "marginTop" ),
			collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
				scrollInfo.width,
			collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
				scrollInfo.height,
			position = $.extend( {}, basePosition ),
			myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );

		if ( options.my[ 0 ] === "right" ) {
			position.left -= elemWidth;
		} else if ( options.my[ 0 ] === "center" ) {
			position.left -= elemWidth / 2;
		}

		if ( options.my[ 1 ] === "bottom" ) {
			position.top -= elemHeight;
		} else if ( options.my[ 1 ] === "center" ) {
			position.top -= elemHeight / 2;
		}

		position.left += myOffset[ 0 ];
		position.top += myOffset[ 1 ];

		collisionPosition = {
			marginLeft: marginLeft,
			marginTop: marginTop
		};

		$.each( [ "left", "top" ], function( i, dir ) {
			if ( $.ui.position[ collision[ i ] ] ) {
				$.ui.position[ collision[ i ] ][ dir ]( position, {
					targetWidth: targetWidth,
					targetHeight: targetHeight,
					elemWidth: elemWidth,
					elemHeight: elemHeight,
					collisionPosition: collisionPosition,
					collisionWidth: collisionWidth,
					collisionHeight: collisionHeight,
					offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
					my: options.my,
					at: options.at,
					within: within,
					elem: elem
				} );
			}
		} );

		if ( options.using ) {

			// Adds feedback as second argument to using callback, if present
			using = function( props ) {
				var left = targetOffset.left - position.left,
					right = left + targetWidth - elemWidth,
					top = targetOffset.top - position.top,
					bottom = top + targetHeight - elemHeight,
					feedback = {
						target: {
							element: target,
							left: targetOffset.left,
							top: targetOffset.top,
							width: targetWidth,
							height: targetHeight
						},
						element: {
							element: elem,
							left: position.left,
							top: position.top,
							width: elemWidth,
							height: elemHeight
						},
						horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
						vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
					};
				if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
					feedback.horizontal = "center";
				}
				if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
					feedback.vertical = "middle";
				}
				if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
					feedback.important = "horizontal";
				} else {
					feedback.important = "vertical";
				}
				options.using.call( this, props, feedback );
			};
		}

		elem.offset( $.extend( position, { using: using } ) );
	} );
};

$.ui.position = {
	fit: {
		left: function( position, data ) {
			var within = data.within,
				withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
				outerWidth = within.width,
				collisionPosLeft = position.left - data.collisionPosition.marginLeft,
				overLeft = withinOffset - collisionPosLeft,
				overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
				newOverRight;

			// Element is wider than within
			if ( data.collisionWidth > outerWidth ) {

				// Element is initially over the left side of within
				if ( overLeft > 0 && overRight <= 0 ) {
					newOverRight = position.left + overLeft + data.collisionWidth - outerWidth -
						withinOffset;
					position.left += overLeft - newOverRight;

				// Element is initially over right side of within
				} else if ( overRight > 0 && overLeft <= 0 ) {
					position.left = withinOffset;

				// Element is initially over both left and right sides of within
				} else {
					if ( overLeft > overRight ) {
						position.left = withinOffset + outerWidth - data.collisionWidth;
					} else {
						position.left = withinOffset;
					}
				}

			// Too far left -> align with left edge
			} else if ( overLeft > 0 ) {
				position.left += overLeft;

			// Too far right -> align with right edge
			} else if ( overRight > 0 ) {
				position.left -= overRight;

			// Adjust based on position and margin
			} else {
				position.left = max( position.left - collisionPosLeft, position.left );
			}
		},
		top: function( position, data ) {
			var within = data.within,
				withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
				outerHeight = data.within.height,
				collisionPosTop = position.top - data.collisionPosition.marginTop,
				overTop = withinOffset - collisionPosTop,
				overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
				newOverBottom;

			// Element is taller than within
			if ( data.collisionHeight > outerHeight ) {

				// Element is initially over the top of within
				if ( overTop > 0 && overBottom <= 0 ) {
					newOverBottom = position.top + overTop + data.collisionHeight - outerHeight -
						withinOffset;
					position.top += overTop - newOverBottom;

				// Element is initially over bottom of within
				} else if ( overBottom > 0 && overTop <= 0 ) {
					position.top = withinOffset;

				// Element is initially over both top and bottom of within
				} else {
					if ( overTop > overBottom ) {
						position.top = withinOffset + outerHeight - data.collisionHeight;
					} else {
						position.top = withinOffset;
					}
				}

			// Too far up -> align with top
			} else if ( overTop > 0 ) {
				position.top += overTop;

			// Too far down -> align with bottom edge
			} else if ( overBottom > 0 ) {
				position.top -= overBottom;

			// Adjust based on position and margin
			} else {
				position.top = max( position.top - collisionPosTop, position.top );
			}
		}
	},
	flip: {
		left: function( position, data ) {
			var within = data.within,
				withinOffset = within.offset.left + within.scrollLeft,
				outerWidth = within.width,
				offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
				collisionPosLeft = position.left - data.collisionPosition.marginLeft,
				overLeft = collisionPosLeft - offsetLeft,
				overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
				myOffset = data.my[ 0 ] === "left" ?
					-data.elemWidth :
					data.my[ 0 ] === "right" ?
						data.elemWidth :
						0,
				atOffset = data.at[ 0 ] === "left" ?
					data.targetWidth :
					data.at[ 0 ] === "right" ?
						-data.targetWidth :
						0,
				offset = -2 * data.offset[ 0 ],
				newOverRight,
				newOverLeft;

			if ( overLeft < 0 ) {
				newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth -
					outerWidth - withinOffset;
				if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
					position.left += myOffset + atOffset + offset;
				}
			} else if ( overRight > 0 ) {
				newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset +
					atOffset + offset - offsetLeft;
				if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
					position.left += myOffset + atOffset + offset;
				}
			}
		},
		top: function( position, data ) {
			var within = data.within,
				withinOffset = within.offset.top + within.scrollTop,
				outerHeight = within.height,
				offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
				collisionPosTop = position.top - data.collisionPosition.marginTop,
				overTop = collisionPosTop - offsetTop,
				overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
				top = data.my[ 1 ] === "top",
				myOffset = top ?
					-data.elemHeight :
					data.my[ 1 ] === "bottom" ?
						data.elemHeight :
						0,
				atOffset = data.at[ 1 ] === "top" ?
					data.targetHeight :
					data.at[ 1 ] === "bottom" ?
						-data.targetHeight :
						0,
				offset = -2 * data.offset[ 1 ],
				newOverTop,
				newOverBottom;
			if ( overTop < 0 ) {
				newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight -
					outerHeight - withinOffset;
				if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
					position.top += myOffset + atOffset + offset;
				}
			} else if ( overBottom > 0 ) {
				newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset +
					offset - offsetTop;
				if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
					position.top += myOffset + atOffset + offset;
				}
			}
		}
	},
	flipfit: {
		left: function() {
			$.ui.position.flip.left.apply( this, arguments );
			$.ui.position.fit.left.apply( this, arguments );
		},
		top: function() {
			$.ui.position.flip.top.apply( this, arguments );
			$.ui.position.fit.top.apply( this, arguments );
		}
	}
};

} )();

var position = $.ui.position;


/*!
 * jQuery UI :data 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: :data Selector
//>>group: Core
//>>description: Selects elements which have data stored under the specified key.
//>>docs: http://api.jqueryui.com/data-selector/


var data = $.extend( $.expr[ ":" ], {
	data: $.expr.createPseudo ?
		$.expr.createPseudo( function( dataName ) {
			return function( elem ) {
				return !!$.data( elem, dataName );
			};
		} ) :

		// Support: jQuery <1.8
		function( elem, i, match ) {
			return !!$.data( elem, match[ 3 ] );
		}
} );

/*!
 * jQuery UI Disable Selection 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: disableSelection
//>>group: Core
//>>description: Disable selection of text content within the set of matched elements.
//>>docs: http://api.jqueryui.com/disableSelection/

// This file is deprecated


var disableSelection = $.fn.extend( {
	disableSelection: ( function() {
		var eventType = "onselectstart" in document.createElement( "div" ) ?
			"selectstart" :
			"mousedown";

		return function() {
			return this.on( eventType + ".ui-disableSelection", function( event ) {
				event.preventDefault();
			} );
		};
	} )(),

	enableSelection: function() {
		return this.off( ".ui-disableSelection" );
	}
} );


/*!
 * jQuery UI Effects 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Effects Core
//>>group: Effects
// jscs:disable maximumLineLength
//>>description: Extends the internal jQuery effects. Includes morphing and easing. Required by all other effects.
// jscs:enable maximumLineLength
//>>docs: http://api.jqueryui.com/category/effects-core/
//>>demos: http://jqueryui.com/effect/



var dataSpace = "ui-effects-",
	dataSpaceStyle = "ui-effects-style",
	dataSpaceAnimated = "ui-effects-animated",

	// Create a local jQuery because jQuery Color relies on it and the
	// global may not exist with AMD and a custom build (#10199)
	jQuery = $;

$.effects = {
	effect: {}
};

/*!
 * jQuery Color Animations v2.1.2
 * https://github.com/jquery/jquery-color
 *
 * Copyright 2014 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * Date: Wed Jan 16 08:47:09 2013 -0600
 */
( function( jQuery, undefined ) {

	var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor " +
		"borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",

	// Plusequals test for += 100 -= 100
	rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,

	// A set of RE's that can match strings and generate color tuples.
	stringParsers = [ {
			re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
			parse: function( execResult ) {
				return [
					execResult[ 1 ],
					execResult[ 2 ],
					execResult[ 3 ],
					execResult[ 4 ]
				];
			}
		}, {
			re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
			parse: function( execResult ) {
				return [
					execResult[ 1 ] * 2.55,
					execResult[ 2 ] * 2.55,
					execResult[ 3 ] * 2.55,
					execResult[ 4 ]
				];
			}
		}, {

			// This regex ignores A-F because it's compared against an already lowercased string
			re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
			parse: function( execResult ) {
				return [
					parseInt( execResult[ 1 ], 16 ),
					parseInt( execResult[ 2 ], 16 ),
					parseInt( execResult[ 3 ], 16 )
				];
			}
		}, {

			// This regex ignores A-F because it's compared against an already lowercased string
			re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
			parse: function( execResult ) {
				return [
					parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
					parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
					parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
				];
			}
		}, {
			re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
			space: "hsla",
			parse: function( execResult ) {
				return [
					execResult[ 1 ],
					execResult[ 2 ] / 100,
					execResult[ 3 ] / 100,
					execResult[ 4 ]
				];
			}
		} ],

	// JQuery.Color( )
	color = jQuery.Color = function( color, green, blue, alpha ) {
		return new jQuery.Color.fn.parse( color, green, blue, alpha );
	},
	spaces = {
		rgba: {
			props: {
				red: {
					idx: 0,
					type: "byte"
				},
				green: {
					idx: 1,
					type: "byte"
				},
				blue: {
					idx: 2,
					type: "byte"
				}
			}
		},

		hsla: {
			props: {
				hue: {
					idx: 0,
					type: "degrees"
				},
				saturation: {
					idx: 1,
					type: "percent"
				},
				lightness: {
					idx: 2,
					type: "percent"
				}
			}
		}
	},
	propTypes = {
		"byte": {
			floor: true,
			max: 255
		},
		"percent": {
			max: 1
		},
		"degrees": {
			mod: 360,
			floor: true
		}
	},
	support = color.support = {},

	// Element for support tests
	supportElem = jQuery( "<p>" )[ 0 ],

	// Colors = jQuery.Color.names
	colors,

	// Local aliases of functions called often
	each = jQuery.each;

// Determine rgba support immediately
supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;

// Define cache name and alpha properties
// for rgba and hsla spaces
each( spaces, function( spaceName, space ) {
	space.cache = "_" + spaceName;
	space.props.alpha = {
		idx: 3,
		type: "percent",
		def: 1
	};
} );

function clamp( value, prop, allowEmpty ) {
	var type = propTypes[ prop.type ] || {};

	if ( value == null ) {
		return ( allowEmpty || !prop.def ) ? null : prop.def;
	}

	// ~~ is an short way of doing floor for positive numbers
	value = type.floor ? ~~value : parseFloat( value );

	// IE will pass in empty strings as value for alpha,
	// which will hit this case
	if ( isNaN( value ) ) {
		return prop.def;
	}

	if ( type.mod ) {

		// We add mod before modding to make sure that negatives values
		// get converted properly: -10 -> 350
		return ( value + type.mod ) % type.mod;
	}

	// For now all property types without mod have min and max
	return 0 > value ? 0 : type.max < value ? type.max : value;
}

function stringParse( string ) {
	var inst = color(),
		rgba = inst._rgba = [];

	string = string.toLowerCase();

	each( stringParsers, function( i, parser ) {
		var parsed,
			match = parser.re.exec( string ),
			values = match && parser.parse( match ),
			spaceName = parser.space || "rgba";

		if ( values ) {
			parsed = inst[ spaceName ]( values );

			// If this was an rgba parse the assignment might happen twice
			// oh well....
			inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
			rgba = inst._rgba = parsed._rgba;

			// Exit each( stringParsers ) here because we matched
			return false;
		}
	} );

	// Found a stringParser that handled it
	if ( rgba.length ) {

		// If this came from a parsed string, force "transparent" when alpha is 0
		// chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
		if ( rgba.join() === "0,0,0,0" ) {
			jQuery.extend( rgba, colors.transparent );
		}
		return inst;
	}

	// Named colors
	return colors[ string ];
}

color.fn = jQuery.extend( color.prototype, {
	parse: function( red, green, blue, alpha ) {
		if ( red === undefined ) {
			this._rgba = [ null, null, null, null ];
			return this;
		}
		if ( red.jquery || red.nodeType ) {
			red = jQuery( red ).css( green );
			green = undefined;
		}

		var inst = this,
			type = jQuery.type( red ),
			rgba = this._rgba = [];

		// More than 1 argument specified - assume ( red, green, blue, alpha )
		if ( green !== undefined ) {
			red = [ red, green, blue, alpha ];
			type = "array";
		}

		if ( type === "string" ) {
			return this.parse( stringParse( red ) || colors._default );
		}

		if ( type === "array" ) {
			each( spaces.rgba.props, function( key, prop ) {
				rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
			} );
			return this;
		}

		if ( type === "object" ) {
			if ( red instanceof color ) {
				each( spaces, function( spaceName, space ) {
					if ( red[ space.cache ] ) {
						inst[ space.cache ] = red[ space.cache ].slice();
					}
				} );
			} else {
				each( spaces, function( spaceName, space ) {
					var cache = space.cache;
					each( space.props, function( key, prop ) {

						// If the cache doesn't exist, and we know how to convert
						if ( !inst[ cache ] && space.to ) {

							// If the value was null, we don't need to copy it
							// if the key was alpha, we don't need to copy it either
							if ( key === "alpha" || red[ key ] == null ) {
								return;
							}
							inst[ cache ] = space.to( inst._rgba );
						}

						// This is the only case where we allow nulls for ALL properties.
						// call clamp with alwaysAllowEmpty
						inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
					} );

					// Everything defined but alpha?
					if ( inst[ cache ] &&
							jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {

						// Use the default of 1
						inst[ cache ][ 3 ] = 1;
						if ( space.from ) {
							inst._rgba = space.from( inst[ cache ] );
						}
					}
				} );
			}
			return this;
		}
	},
	is: function( compare ) {
		var is = color( compare ),
			same = true,
			inst = this;

		each( spaces, function( _, space ) {
			var localCache,
				isCache = is[ space.cache ];
			if ( isCache ) {
				localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
				each( space.props, function( _, prop ) {
					if ( isCache[ prop.idx ] != null ) {
						same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
						return same;
					}
				} );
			}
			return same;
		} );
		return same;
	},
	_space: function() {
		var used = [],
			inst = this;
		each( spaces, function( spaceName, space ) {
			if ( inst[ space.cache ] ) {
				used.push( spaceName );
			}
		} );
		return used.pop();
	},
	transition: function( other, distance ) {
		var end = color( other ),
			spaceName = end._space(),
			space = spaces[ spaceName ],
			startColor = this.alpha() === 0 ? color( "transparent" ) : this,
			start = startColor[ space.cache ] || space.to( startColor._rgba ),
			result = start.slice();

		end = end[ space.cache ];
		each( space.props, function( key, prop ) {
			var index = prop.idx,
				startValue = start[ index ],
				endValue = end[ index ],
				type = propTypes[ prop.type ] || {};

			// If null, don't override start value
			if ( endValue === null ) {
				return;
			}

			// If null - use end
			if ( startValue === null ) {
				result[ index ] = endValue;
			} else {
				if ( type.mod ) {
					if ( endValue - startValue > type.mod / 2 ) {
						startValue += type.mod;
					} else if ( startValue - endValue > type.mod / 2 ) {
						startValue -= type.mod;
					}
				}
				result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
			}
		} );
		return this[ spaceName ]( result );
	},
	blend: function( opaque ) {

		// If we are already opaque - return ourself
		if ( this._rgba[ 3 ] === 1 ) {
			return this;
		}

		var rgb = this._rgba.slice(),
			a = rgb.pop(),
			blend = color( opaque )._rgba;

		return color( jQuery.map( rgb, function( v, i ) {
			return ( 1 - a ) * blend[ i ] + a * v;
		} ) );
	},
	toRgbaString: function() {
		var prefix = "rgba(",
			rgba = jQuery.map( this._rgba, function( v, i ) {
				return v == null ? ( i > 2 ? 1 : 0 ) : v;
			} );

		if ( rgba[ 3 ] === 1 ) {
			rgba.pop();
			prefix = "rgb(";
		}

		return prefix + rgba.join() + ")";
	},
	toHslaString: function() {
		var prefix = "hsla(",
			hsla = jQuery.map( this.hsla(), function( v, i ) {
				if ( v == null ) {
					v = i > 2 ? 1 : 0;
				}

				// Catch 1 and 2
				if ( i && i < 3 ) {
					v = Math.round( v * 100 ) + "%";
				}
				return v;
			} );

		if ( hsla[ 3 ] === 1 ) {
			hsla.pop();
			prefix = "hsl(";
		}
		return prefix + hsla.join() + ")";
	},
	toHexString: function( includeAlpha ) {
		var rgba = this._rgba.slice(),
			alpha = rgba.pop();

		if ( includeAlpha ) {
			rgba.push( ~~( alpha * 255 ) );
		}

		return "#" + jQuery.map( rgba, function( v ) {

			// Default to 0 when nulls exist
			v = ( v || 0 ).toString( 16 );
			return v.length === 1 ? "0" + v : v;
		} ).join( "" );
	},
	toString: function() {
		return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
	}
} );
color.fn.parse.prototype = color.fn;

// Hsla conversions adapted from:
// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021

function hue2rgb( p, q, h ) {
	h = ( h + 1 ) % 1;
	if ( h * 6 < 1 ) {
		return p + ( q - p ) * h * 6;
	}
	if ( h * 2 < 1 ) {
		return q;
	}
	if ( h * 3 < 2 ) {
		return p + ( q - p ) * ( ( 2 / 3 ) - h ) * 6;
	}
	return p;
}

spaces.hsla.to = function( rgba ) {
	if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
		return [ null, null, null, rgba[ 3 ] ];
	}
	var r = rgba[ 0 ] / 255,
		g = rgba[ 1 ] / 255,
		b = rgba[ 2 ] / 255,
		a = rgba[ 3 ],
		max = Math.max( r, g, b ),
		min = Math.min( r, g, b ),
		diff = max - min,
		add = max + min,
		l = add * 0.5,
		h, s;

	if ( min === max ) {
		h = 0;
	} else if ( r === max ) {
		h = ( 60 * ( g - b ) / diff ) + 360;
	} else if ( g === max ) {
		h = ( 60 * ( b - r ) / diff ) + 120;
	} else {
		h = ( 60 * ( r - g ) / diff ) + 240;
	}

	// Chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
	// otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
	if ( diff === 0 ) {
		s = 0;
	} else if ( l <= 0.5 ) {
		s = diff / add;
	} else {
		s = diff / ( 2 - add );
	}
	return [ Math.round( h ) % 360, s, l, a == null ? 1 : a ];
};

spaces.hsla.from = function( hsla ) {
	if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
		return [ null, null, null, hsla[ 3 ] ];
	}
	var h = hsla[ 0 ] / 360,
		s = hsla[ 1 ],
		l = hsla[ 2 ],
		a = hsla[ 3 ],
		q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
		p = 2 * l - q;

	return [
		Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
		Math.round( hue2rgb( p, q, h ) * 255 ),
		Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
		a
	];
};

each( spaces, function( spaceName, space ) {
	var props = space.props,
		cache = space.cache,
		to = space.to,
		from = space.from;

	// Makes rgba() and hsla()
	color.fn[ spaceName ] = function( value ) {

		// Generate a cache for this space if it doesn't exist
		if ( to && !this[ cache ] ) {
			this[ cache ] = to( this._rgba );
		}
		if ( value === undefined ) {
			return this[ cache ].slice();
		}

		var ret,
			type = jQuery.type( value ),
			arr = ( type === "array" || type === "object" ) ? value : arguments,
			local = this[ cache ].slice();

		each( props, function( key, prop ) {
			var val = arr[ type === "object" ? key : prop.idx ];
			if ( val == null ) {
				val = local[ prop.idx ];
			}
			local[ prop.idx ] = clamp( val, prop );
		} );

		if ( from ) {
			ret = color( from( local ) );
			ret[ cache ] = local;
			return ret;
		} else {
			return color( local );
		}
	};

	// Makes red() green() blue() alpha() hue() saturation() lightness()
	each( props, function( key, prop ) {

		// Alpha is included in more than one space
		if ( color.fn[ key ] ) {
			return;
		}
		color.fn[ key ] = function( value ) {
			var vtype = jQuery.type( value ),
				fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
				local = this[ fn ](),
				cur = local[ prop.idx ],
				match;

			if ( vtype === "undefined" ) {
				return cur;
			}

			if ( vtype === "function" ) {
				value = value.call( this, cur );
				vtype = jQuery.type( value );
			}
			if ( value == null && prop.empty ) {
				return this;
			}
			if ( vtype === "string" ) {
				match = rplusequals.exec( value );
				if ( match ) {
					value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
				}
			}
			local[ prop.idx ] = value;
			return this[ fn ]( local );
		};
	} );
} );

// Add cssHook and .fx.step function for each named hook.
// accept a space separated string of properties
color.hook = function( hook ) {
	var hooks = hook.split( " " );
	each( hooks, function( i, hook ) {
		jQuery.cssHooks[ hook ] = {
			set: function( elem, value ) {
				var parsed, curElem,
					backgroundColor = "";

				if ( value !== "transparent" && ( jQuery.type( value ) !== "string" ||
						( parsed = stringParse( value ) ) ) ) {
					value = color( parsed || value );
					if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
						curElem = hook === "backgroundColor" ? elem.parentNode : elem;
						while (
							( backgroundColor === "" || backgroundColor === "transparent" ) &&
							curElem && curElem.style
						) {
							try {
								backgroundColor = jQuery.css( curElem, "backgroundColor" );
								curElem = curElem.parentNode;
							} catch ( e ) {
							}
						}

						value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
							backgroundColor :
							"_default" );
					}

					value = value.toRgbaString();
				}
				try {
					elem.style[ hook ] = value;
				} catch ( e ) {

					// Wrapped to prevent IE from throwing errors on "invalid" values like
					// 'auto' or 'inherit'
				}
			}
		};
		jQuery.fx.step[ hook ] = function( fx ) {
			if ( !fx.colorInit ) {
				fx.start = color( fx.elem, hook );
				fx.end = color( fx.end );
				fx.colorInit = true;
			}
			jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
		};
	} );

};

color.hook( stepHooks );

jQuery.cssHooks.borderColor = {
	expand: function( value ) {
		var expanded = {};

		each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
			expanded[ "border" + part + "Color" ] = value;
		} );
		return expanded;
	}
};

// Basic color names only.
// Usage of any of the other color names requires adding yourself or including
// jquery.color.svg-names.js.
colors = jQuery.Color.names = {

	// 4.1. Basic color keywords
	aqua: "#00ffff",
	black: "#000000",
	blue: "#0000ff",
	fuchsia: "#ff00ff",
	gray: "#808080",
	green: "#008000",
	lime: "#00ff00",
	maroon: "#800000",
	navy: "#000080",
	olive: "#808000",
	purple: "#800080",
	red: "#ff0000",
	silver: "#c0c0c0",
	teal: "#008080",
	white: "#ffffff",
	yellow: "#ffff00",

	// 4.2.3. "transparent" color keyword
	transparent: [ null, null, null, 0 ],

	_default: "#ffffff"
};

} )( jQuery );

/******************************************************************************/
/****************************** CLASS ANIMATIONS ******************************/
/******************************************************************************/
( function() {

var classAnimationActions = [ "add", "remove", "toggle" ],
	shorthandStyles = {
		border: 1,
		borderBottom: 1,
		borderColor: 1,
		borderLeft: 1,
		borderRight: 1,
		borderTop: 1,
		borderWidth: 1,
		margin: 1,
		padding: 1
	};

$.each(
	[ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ],
	function( _, prop ) {
		$.fx.step[ prop ] = function( fx ) {
			if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
				jQuery.style( fx.elem, prop, fx.end );
				fx.setAttr = true;
			}
		};
	}
);

function getElementStyles( elem ) {
	var key, len,
		style = elem.ownerDocument.defaultView ?
			elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
			elem.currentStyle,
		styles = {};

	if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
		len = style.length;
		while ( len-- ) {
			key = style[ len ];
			if ( typeof style[ key ] === "string" ) {
				styles[ $.camelCase( key ) ] = style[ key ];
			}
		}

	// Support: Opera, IE <9
	} else {
		for ( key in style ) {
			if ( typeof style[ key ] === "string" ) {
				styles[ key ] = style[ key ];
			}
		}
	}

	return styles;
}

function styleDifference( oldStyle, newStyle ) {
	var diff = {},
		name, value;

	for ( name in newStyle ) {
		value = newStyle[ name ];
		if ( oldStyle[ name ] !== value ) {
			if ( !shorthandStyles[ name ] ) {
				if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
					diff[ name ] = value;
				}
			}
		}
	}

	return diff;
}

// Support: jQuery <1.8
if ( !$.fn.addBack ) {
	$.fn.addBack = function( selector ) {
		return this.add( selector == null ?
			this.prevObject : this.prevObject.filter( selector )
		);
	};
}

$.effects.animateClass = function( value, duration, easing, callback ) {
	var o = $.speed( duration, easing, callback );

	return this.queue( function() {
		var animated = $( this ),
			baseClass = animated.attr( "class" ) || "",
			applyClassChange,
			allAnimations = o.children ? animated.find( "*" ).addBack() : animated;

		// Map the animated objects to store the original styles.
		allAnimations = allAnimations.map( function() {
			var el = $( this );
			return {
				el: el,
				start: getElementStyles( this )
			};
		} );

		// Apply class change
		applyClassChange = function() {
			$.each( classAnimationActions, function( i, action ) {
				if ( value[ action ] ) {
					animated[ action + "Class" ]( value[ action ] );
				}
			} );
		};
		applyClassChange();

		// Map all animated objects again - calculate new styles and diff
		allAnimations = allAnimations.map( function() {
			this.end = getElementStyles( this.el[ 0 ] );
			this.diff = styleDifference( this.start, this.end );
			return this;
		} );

		// Apply original class
		animated.attr( "class", baseClass );

		// Map all animated objects again - this time collecting a promise
		allAnimations = allAnimations.map( function() {
			var styleInfo = this,
				dfd = $.Deferred(),
				opts = $.extend( {}, o, {
					queue: false,
					complete: function() {
						dfd.resolve( styleInfo );
					}
				} );

			this.el.animate( this.diff, opts );
			return dfd.promise();
		} );

		// Once all animations have completed:
		$.when.apply( $, allAnimations.get() ).done( function() {

			// Set the final class
			applyClassChange();

			// For each animated element,
			// clear all css properties that were animated
			$.each( arguments, function() {
				var el = this.el;
				$.each( this.diff, function( key ) {
					el.css( key, "" );
				} );
			} );

			// This is guarnteed to be there if you use jQuery.speed()
			// it also handles dequeuing the next anim...
			o.complete.call( animated[ 0 ] );
		} );
	} );
};

$.fn.extend( {
	addClass: ( function( orig ) {
		return function( classNames, speed, easing, callback ) {
			return speed ?
				$.effects.animateClass.call( this,
					{ add: classNames }, speed, easing, callback ) :
				orig.apply( this, arguments );
		};
	} )( $.fn.addClass ),

	removeClass: ( function( orig ) {
		return function( classNames, speed, easing, callback ) {
			return arguments.length > 1 ?
				$.effects.animateClass.call( this,
					{ remove: classNames }, speed, easing, callback ) :
				orig.apply( this, arguments );
		};
	} )( $.fn.removeClass ),

	toggleClass: ( function( orig ) {
		return function( classNames, force, speed, easing, callback ) {
			if ( typeof force === "boolean" || force === undefined ) {
				if ( !speed ) {

					// Without speed parameter
					return orig.apply( this, arguments );
				} else {
					return $.effects.animateClass.call( this,
						( force ? { add: classNames } : { remove: classNames } ),
						speed, easing, callback );
				}
			} else {

				// Without force parameter
				return $.effects.animateClass.call( this,
					{ toggle: classNames }, force, speed, easing );
			}
		};
	} )( $.fn.toggleClass ),

	switchClass: function( remove, add, speed, easing, callback ) {
		return $.effects.animateClass.call( this, {
			add: add,
			remove: remove
		}, speed, easing, callback );
	}
} );

} )();

/******************************************************************************/
/*********************************** EFFECTS **********************************/
/******************************************************************************/

( function() {

if ( $.expr && $.expr.filters && $.expr.filters.animated ) {
	$.expr.filters.animated = ( function( orig ) {
		return function( elem ) {
			return !!$( elem ).data( dataSpaceAnimated ) || orig( elem );
		};
	} )( $.expr.filters.animated );
}

if ( $.uiBackCompat !== false ) {
	$.extend( $.effects, {

		// Saves a set of properties in a data storage
		save: function( element, set ) {
			var i = 0, length = set.length;
			for ( ; i < length; i++ ) {
				if ( set[ i ] !== null ) {
					element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
				}
			}
		},

		// Restores a set of previously saved properties from a data storage
		restore: function( element, set ) {
			var val, i = 0, length = set.length;
			for ( ; i < length; i++ ) {
				if ( set[ i ] !== null ) {
					val = element.data( dataSpace + set[ i ] );
					element.css( set[ i ], val );
				}
			}
		},

		setMode: function( el, mode ) {
			if ( mode === "toggle" ) {
				mode = el.is( ":hidden" ) ? "show" : "hide";
			}
			return mode;
		},

		// Wraps the element around a wrapper that copies position properties
		createWrapper: function( element ) {

			// If the element is already wrapped, return it
			if ( element.parent().is( ".ui-effects-wrapper" ) ) {
				return element.parent();
			}

			// Wrap the element
			var props = {
					width: element.outerWidth( true ),
					height: element.outerHeight( true ),
					"float": element.css( "float" )
				},
				wrapper = $( "<div></div>" )
					.addClass( "ui-effects-wrapper" )
					.css( {
						fontSize: "100%",
						background: "transparent",
						border: "none",
						margin: 0,
						padding: 0
					} ),

				// Store the size in case width/height are defined in % - Fixes #5245
				size = {
					width: element.width(),
					height: element.height()
				},
				active = document.activeElement;

			// Support: Firefox
			// Firefox incorrectly exposes anonymous content
			// https://bugzilla.mozilla.org/show_bug.cgi?id=561664
			try {
				active.id;
			} catch ( e ) {
				active = document.body;
			}

			element.wrap( wrapper );

			// Fixes #7595 - Elements lose focus when wrapped.
			if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
				$( active ).trigger( "focus" );
			}

			// Hotfix for jQuery 1.4 since some change in wrap() seems to actually
			// lose the reference to the wrapped element
			wrapper = element.parent();

			// Transfer positioning properties to the wrapper
			if ( element.css( "position" ) === "static" ) {
				wrapper.css( { position: "relative" } );
				element.css( { position: "relative" } );
			} else {
				$.extend( props, {
					position: element.css( "position" ),
					zIndex: element.css( "z-index" )
				} );
				$.each( [ "top", "left", "bottom", "right" ], function( i, pos ) {
					props[ pos ] = element.css( pos );
					if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
						props[ pos ] = "auto";
					}
				} );
				element.css( {
					position: "relative",
					top: 0,
					left: 0,
					right: "auto",
					bottom: "auto"
				} );
			}
			element.css( size );

			return wrapper.css( props ).show();
		},

		removeWrapper: function( element ) {
			var active = document.activeElement;

			if ( element.parent().is( ".ui-effects-wrapper" ) ) {
				element.parent().replaceWith( element );

				// Fixes #7595 - Elements lose focus when wrapped.
				if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
					$( active ).trigger( "focus" );
				}
			}

			return element;
		}
	} );
}

$.extend( $.effects, {
	version: "1.12.1",

	define: function( name, mode, effect ) {
		if ( !effect ) {
			effect = mode;
			mode = "effect";
		}

		$.effects.effect[ name ] = effect;
		$.effects.effect[ name ].mode = mode;

		return effect;
	},

	scaledDimensions: function( element, percent, direction ) {
		if ( percent === 0 ) {
			return {
				height: 0,
				width: 0,
				outerHeight: 0,
				outerWidth: 0
			};
		}

		var x = direction !== "horizontal" ? ( ( percent || 100 ) / 100 ) : 1,
			y = direction !== "vertical" ? ( ( percent || 100 ) / 100 ) : 1;

		return {
			height: element.height() * y,
			width: element.width() * x,
			outerHeight: element.outerHeight() * y,
			outerWidth: element.outerWidth() * x
		};

	},

	clipToBox: function( animation ) {
		return {
			width: animation.clip.right - animation.clip.left,
			height: animation.clip.bottom - animation.clip.top,
			left: animation.clip.left,
			top: animation.clip.top
		};
	},

	// Injects recently queued functions to be first in line (after "inprogress")
	unshift: function( element, queueLength, count ) {
		var queue = element.queue();

		if ( queueLength > 1 ) {
			queue.splice.apply( queue,
				[ 1, 0 ].concat( queue.splice( queueLength, count ) ) );
		}
		element.dequeue();
	},

	saveStyle: function( element ) {
		element.data( dataSpaceStyle, element[ 0 ].style.cssText );
	},

	restoreStyle: function( element ) {
		element[ 0 ].style.cssText = element.data( dataSpaceStyle ) || "";
		element.removeData( dataSpaceStyle );
	},

	mode: function( element, mode ) {
		var hidden = element.is( ":hidden" );

		if ( mode === "toggle" ) {
			mode = hidden ? "show" : "hide";
		}
		if ( hidden ? mode === "hide" : mode === "show" ) {
			mode = "none";
		}
		return mode;
	},

	// Translates a [top,left] array into a baseline value
	getBaseline: function( origin, original ) {
		var y, x;

		switch ( origin[ 0 ] ) {
		case "top":
			y = 0;
			break;
		case "middle":
			y = 0.5;
			break;
		case "bottom":
			y = 1;
			break;
		default:
			y = origin[ 0 ] / original.height;
		}

		switch ( origin[ 1 ] ) {
		case "left":
			x = 0;
			break;
		case "center":
			x = 0.5;
			break;
		case "right":
			x = 1;
			break;
		default:
			x = origin[ 1 ] / original.width;
		}

		return {
			x: x,
			y: y
		};
	},

	// Creates a placeholder element so that the original element can be made absolute
	createPlaceholder: function( element ) {
		var placeholder,
			cssPosition = element.css( "position" ),
			position = element.position();

		// Lock in margins first to account for form elements, which
		// will change margin if you explicitly set height
		// see: http://jsfiddle.net/JZSMt/3/ https://bugs.webkit.org/show_bug.cgi?id=107380
		// Support: Safari
		element.css( {
			marginTop: element.css( "marginTop" ),
			marginBottom: element.css( "marginBottom" ),
			marginLeft: element.css( "marginLeft" ),
			marginRight: element.css( "marginRight" )
		} )
		.outerWidth( element.outerWidth() )
		.outerHeight( element.outerHeight() );

		if ( /^(static|relative)/.test( cssPosition ) ) {
			cssPosition = "absolute";

			placeholder = $( "<" + element[ 0 ].nodeName + ">" ).insertAfter( element ).css( {

				// Convert inline to inline block to account for inline elements
				// that turn to inline block based on content (like img)
				display: /^(inline|ruby)/.test( element.css( "display" ) ) ?
					"inline-block" :
					"block",
				visibility: "hidden",

				// Margins need to be set to account for margin collapse
				marginTop: element.css( "marginTop" ),
				marginBottom: element.css( "marginBottom" ),
				marginLeft: element.css( "marginLeft" ),
				marginRight: element.css( "marginRight" ),
				"float": element.css( "float" )
			} )
			.outerWidth( element.outerWidth() )
			.outerHeight( element.outerHeight() )
			.addClass( "ui-effects-placeholder" );

			element.data( dataSpace + "placeholder", placeholder );
		}

		element.css( {
			position: cssPosition,
			left: position.left,
			top: position.top
		} );

		return placeholder;
	},

	removePlaceholder: function( element ) {
		var dataKey = dataSpace + "placeholder",
				placeholder = element.data( dataKey );

		if ( placeholder ) {
			placeholder.remove();
			element.removeData( dataKey );
		}
	},

	// Removes a placeholder if it exists and restores
	// properties that were modified during placeholder creation
	cleanUp: function( element ) {
		$.effects.restoreStyle( element );
		$.effects.removePlaceholder( element );
	},

	setTransition: function( element, list, factor, value ) {
		value = value || {};
		$.each( list, function( i, x ) {
			var unit = element.cssUnit( x );
			if ( unit[ 0 ] > 0 ) {
				value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
			}
		} );
		return value;
	}
} );

// Return an effect options object for the given parameters:
function _normalizeArguments( effect, options, speed, callback ) {

	// Allow passing all options as the first parameter
	if ( $.isPlainObject( effect ) ) {
		options = effect;
		effect = effect.effect;
	}

	// Convert to an object
	effect = { effect: effect };

	// Catch (effect, null, ...)
	if ( options == null ) {
		options = {};
	}

	// Catch (effect, callback)
	if ( $.isFunction( options ) ) {
		callback = options;
		speed = null;
		options = {};
	}

	// Catch (effect, speed, ?)
	if ( typeof options === "number" || $.fx.speeds[ options ] ) {
		callback = speed;
		speed = options;
		options = {};
	}

	// Catch (effect, options, callback)
	if ( $.isFunction( speed ) ) {
		callback = speed;
		speed = null;
	}

	// Add options to effect
	if ( options ) {
		$.extend( effect, options );
	}

	speed = speed || options.duration;
	effect.duration = $.fx.off ? 0 :
		typeof speed === "number" ? speed :
		speed in $.fx.speeds ? $.fx.speeds[ speed ] :
		$.fx.speeds._default;

	effect.complete = callback || options.complete;

	return effect;
}

function standardAnimationOption( option ) {

	// Valid standard speeds (nothing, number, named speed)
	if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
		return true;
	}

	// Invalid strings - treat as "normal" speed
	if ( typeof option === "string" && !$.effects.effect[ option ] ) {
		return true;
	}

	// Complete callback
	if ( $.isFunction( option ) ) {
		return true;
	}

	// Options hash (but not naming an effect)
	if ( typeof option === "object" && !option.effect ) {
		return true;
	}

	// Didn't match any standard API
	return false;
}

$.fn.extend( {
	effect: function( /* effect, options, speed, callback */ ) {
		var args = _normalizeArguments.apply( this, arguments ),
			effectMethod = $.effects.effect[ args.effect ],
			defaultMode = effectMethod.mode,
			queue = args.queue,
			queueName = queue || "fx",
			complete = args.complete,
			mode = args.mode,
			modes = [],
			prefilter = function( next ) {
				var el = $( this ),
					normalizedMode = $.effects.mode( el, mode ) || defaultMode;

				// Sentinel for duck-punching the :animated psuedo-selector
				el.data( dataSpaceAnimated, true );

				// Save effect mode for later use,
				// we can't just call $.effects.mode again later,
				// as the .show() below destroys the initial state
				modes.push( normalizedMode );

				// See $.uiBackCompat inside of run() for removal of defaultMode in 1.13
				if ( defaultMode && ( normalizedMode === "show" ||
						( normalizedMode === defaultMode && normalizedMode === "hide" ) ) ) {
					el.show();
				}

				if ( !defaultMode || normalizedMode !== "none" ) {
					$.effects.saveStyle( el );
				}

				if ( $.isFunction( next ) ) {
					next();
				}
			};

		if ( $.fx.off || !effectMethod ) {

			// Delegate to the original method (e.g., .show()) if possible
			if ( mode ) {
				return this[ mode ]( args.duration, complete );
			} else {
				return this.each( function() {
					if ( complete ) {
						complete.call( this );
					}
				} );
			}
		}

		function run( next ) {
			var elem = $( this );

			function cleanup() {
				elem.removeData( dataSpaceAnimated );

				$.effects.cleanUp( elem );

				if ( args.mode === "hide" ) {
					elem.hide();
				}

				done();
			}

			function done() {
				if ( $.isFunction( complete ) ) {
					complete.call( elem[ 0 ] );
				}

				if ( $.isFunction( next ) ) {
					next();
				}
			}

			// Override mode option on a per element basis,
			// as toggle can be either show or hide depending on element state
			args.mode = modes.shift();

			if ( $.uiBackCompat !== false && !defaultMode ) {
				if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {

					// Call the core method to track "olddisplay" properly
					elem[ mode ]();
					done();
				} else {
					effectMethod.call( elem[ 0 ], args, done );
				}
			} else {
				if ( args.mode === "none" ) {

					// Call the core method to track "olddisplay" properly
					elem[ mode ]();
					done();
				} else {
					effectMethod.call( elem[ 0 ], args, cleanup );
				}
			}
		}

		// Run prefilter on all elements first to ensure that
		// any showing or hiding happens before placeholder creation,
		// which ensures that any layout changes are correctly captured.
		return queue === false ?
			this.each( prefilter ).each( run ) :
			this.queue( queueName, prefilter ).queue( queueName, run );
	},

	show: ( function( orig ) {
		return function( option ) {
			if ( standardAnimationOption( option ) ) {
				return orig.apply( this, arguments );
			} else {
				var args = _normalizeArguments.apply( this, arguments );
				args.mode = "show";
				return this.effect.call( this, args );
			}
		};
	} )( $.fn.show ),

	hide: ( function( orig ) {
		return function( option ) {
			if ( standardAnimationOption( option ) ) {
				return orig.apply( this, arguments );
			} else {
				var args = _normalizeArguments.apply( this, arguments );
				args.mode = "hide";
				return this.effect.call( this, args );
			}
		};
	} )( $.fn.hide ),

	toggle: ( function( orig ) {
		return function( option ) {
			if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
				return orig.apply( this, arguments );
			} else {
				var args = _normalizeArguments.apply( this, arguments );
				args.mode = "toggle";
				return this.effect.call( this, args );
			}
		};
	} )( $.fn.toggle ),

	cssUnit: function( key ) {
		var style = this.css( key ),
			val = [];

		$.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
			if ( style.indexOf( unit ) > 0 ) {
				val = [ parseFloat( style ), unit ];
			}
		} );
		return val;
	},

	cssClip: function( clipObj ) {
		if ( clipObj ) {
			return this.css( "clip", "rect(" + clipObj.top + "px " + clipObj.right + "px " +
				clipObj.bottom + "px " + clipObj.left + "px)" );
		}
		return parseClip( this.css( "clip" ), this );
	},

	transfer: function( options, done ) {
		var element = $( this ),
			target = $( options.to ),
			targetFixed = target.css( "position" ) === "fixed",
			body = $( "body" ),
			fixTop = targetFixed ? body.scrollTop() : 0,
			fixLeft = targetFixed ? body.scrollLeft() : 0,
			endPosition = target.offset(),
			animation = {
				top: endPosition.top - fixTop,
				left: endPosition.left - fixLeft,
				height: target.innerHeight(),
				width: target.innerWidth()
			},
			startPosition = element.offset(),
			transfer = $( "<div class='ui-effects-transfer'></div>" )
				.appendTo( "body" )
				.addClass( options.className )
				.css( {
					top: startPosition.top - fixTop,
					left: startPosition.left - fixLeft,
					height: element.innerHeight(),
					width: element.innerWidth(),
					position: targetFixed ? "fixed" : "absolute"
				} )
				.animate( animation, options.duration, options.easing, function() {
					transfer.remove();
					if ( $.isFunction( done ) ) {
						done();
					}
				} );
	}
} );

function parseClip( str, element ) {
		var outerWidth = element.outerWidth(),
			outerHeight = element.outerHeight(),
			clipRegex = /^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/,
			values = clipRegex.exec( str ) || [ "", 0, outerWidth, outerHeight, 0 ];

		return {
			top: parseFloat( values[ 1 ] ) || 0,
			right: values[ 2 ] === "auto" ? outerWidth : parseFloat( values[ 2 ] ),
			bottom: values[ 3 ] === "auto" ? outerHeight : parseFloat( values[ 3 ] ),
			left: parseFloat( values[ 4 ] ) || 0
		};
}

$.fx.step.clip = function( fx ) {
	if ( !fx.clipInit ) {
		fx.start = $( fx.elem ).cssClip();
		if ( typeof fx.end === "string" ) {
			fx.end = parseClip( fx.end, fx.elem );
		}
		fx.clipInit = true;
	}

	$( fx.elem ).cssClip( {
		top: fx.pos * ( fx.end.top - fx.start.top ) + fx.start.top,
		right: fx.pos * ( fx.end.right - fx.start.right ) + fx.start.right,
		bottom: fx.pos * ( fx.end.bottom - fx.start.bottom ) + fx.start.bottom,
		left: fx.pos * ( fx.end.left - fx.start.left ) + fx.start.left
	} );
};

} )();

/******************************************************************************/
/*********************************** EASING ***********************************/
/******************************************************************************/

( function() {

// Based on easing equations from Robert Penner (http://www.robertpenner.com/easing)

var baseEasings = {};

$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
	baseEasings[ name ] = function( p ) {
		return Math.pow( p, i + 2 );
	};
} );

$.extend( baseEasings, {
	Sine: function( p ) {
		return 1 - Math.cos( p * Math.PI / 2 );
	},
	Circ: function( p ) {
		return 1 - Math.sqrt( 1 - p * p );
	},
	Elastic: function( p ) {
		return p === 0 || p === 1 ? p :
			-Math.pow( 2, 8 * ( p - 1 ) ) * Math.sin( ( ( p - 1 ) * 80 - 7.5 ) * Math.PI / 15 );
	},
	Back: function( p ) {
		return p * p * ( 3 * p - 2 );
	},
	Bounce: function( p ) {
		var pow2,
			bounce = 4;

		while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
		return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
	}
} );

$.each( baseEasings, function( name, easeIn ) {
	$.easing[ "easeIn" + name ] = easeIn;
	$.easing[ "easeOut" + name ] = function( p ) {
		return 1 - easeIn( 1 - p );
	};
	$.easing[ "easeInOut" + name ] = function( p ) {
		return p < 0.5 ?
			easeIn( p * 2 ) / 2 :
			1 - easeIn( p * -2 + 2 ) / 2;
	};
} );

} )();

var effect = $.effects;


/*!
 * jQuery UI Effects Blind 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Blind Effect
//>>group: Effects
//>>description: Blinds the element.
//>>docs: http://api.jqueryui.com/blind-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectBlind = $.effects.define( "blind", "hide", function( options, done ) {
	var map = {
			up: [ "bottom", "top" ],
			vertical: [ "bottom", "top" ],
			down: [ "top", "bottom" ],
			left: [ "right", "left" ],
			horizontal: [ "right", "left" ],
			right: [ "left", "right" ]
		},
		element = $( this ),
		direction = options.direction || "up",
		start = element.cssClip(),
		animate = { clip: $.extend( {}, start ) },
		placeholder = $.effects.createPlaceholder( element );

	animate.clip[ map[ direction ][ 0 ] ] = animate.clip[ map[ direction ][ 1 ] ];

	if ( options.mode === "show" ) {
		element.cssClip( animate.clip );
		if ( placeholder ) {
			placeholder.css( $.effects.clipToBox( animate ) );
		}

		animate.clip = start;
	}

	if ( placeholder ) {
		placeholder.animate( $.effects.clipToBox( animate ), options.duration, options.easing );
	}

	element.animate( animate, {
		queue: false,
		duration: options.duration,
		easing: options.easing,
		complete: done
	} );
} );


/*!
 * jQuery UI Effects Bounce 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Bounce Effect
//>>group: Effects
//>>description: Bounces an element horizontally or vertically n times.
//>>docs: http://api.jqueryui.com/bounce-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectBounce = $.effects.define( "bounce", function( options, done ) {
	var upAnim, downAnim, refValue,
		element = $( this ),

		// Defaults:
		mode = options.mode,
		hide = mode === "hide",
		show = mode === "show",
		direction = options.direction || "up",
		distance = options.distance,
		times = options.times || 5,

		// Number of internal animations
		anims = times * 2 + ( show || hide ? 1 : 0 ),
		speed = options.duration / anims,
		easing = options.easing,

		// Utility:
		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
		motion = ( direction === "up" || direction === "left" ),
		i = 0,

		queuelen = element.queue().length;

	$.effects.createPlaceholder( element );

	refValue = element.css( ref );

	// Default distance for the BIGGEST bounce is the outer Distance / 3
	if ( !distance ) {
		distance = element[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
	}

	if ( show ) {
		downAnim = { opacity: 1 };
		downAnim[ ref ] = refValue;

		// If we are showing, force opacity 0 and set the initial position
		// then do the "first" animation
		element
			.css( "opacity", 0 )
			.css( ref, motion ? -distance * 2 : distance * 2 )
			.animate( downAnim, speed, easing );
	}

	// Start at the smallest distance if we are hiding
	if ( hide ) {
		distance = distance / Math.pow( 2, times - 1 );
	}

	downAnim = {};
	downAnim[ ref ] = refValue;

	// Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
	for ( ; i < times; i++ ) {
		upAnim = {};
		upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;

		element
			.animate( upAnim, speed, easing )
			.animate( downAnim, speed, easing );

		distance = hide ? distance * 2 : distance / 2;
	}

	// Last Bounce when Hiding
	if ( hide ) {
		upAnim = { opacity: 0 };
		upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;

		element.animate( upAnim, speed, easing );
	}

	element.queue( done );

	$.effects.unshift( element, queuelen, anims + 1 );
} );


/*!
 * jQuery UI Effects Clip 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Clip Effect
//>>group: Effects
//>>description: Clips the element on and off like an old TV.
//>>docs: http://api.jqueryui.com/clip-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectClip = $.effects.define( "clip", "hide", function( options, done ) {
	var start,
		animate = {},
		element = $( this ),
		direction = options.direction || "vertical",
		both = direction === "both",
		horizontal = both || direction === "horizontal",
		vertical = both || direction === "vertical";

	start = element.cssClip();
	animate.clip = {
		top: vertical ? ( start.bottom - start.top ) / 2 : start.top,
		right: horizontal ? ( start.right - start.left ) / 2 : start.right,
		bottom: vertical ? ( start.bottom - start.top ) / 2 : start.bottom,
		left: horizontal ? ( start.right - start.left ) / 2 : start.left
	};

	$.effects.createPlaceholder( element );

	if ( options.mode === "show" ) {
		element.cssClip( animate.clip );
		animate.clip = start;
	}

	element.animate( animate, {
		queue: false,
		duration: options.duration,
		easing: options.easing,
		complete: done
	} );

} );


/*!
 * jQuery UI Effects Drop 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Drop Effect
//>>group: Effects
//>>description: Moves an element in one direction and hides it at the same time.
//>>docs: http://api.jqueryui.com/drop-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectDrop = $.effects.define( "drop", "hide", function( options, done ) {

	var distance,
		element = $( this ),
		mode = options.mode,
		show = mode === "show",
		direction = options.direction || "left",
		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
		motion = ( direction === "up" || direction === "left" ) ? "-=" : "+=",
		oppositeMotion = ( motion === "+=" ) ? "-=" : "+=",
		animation = {
			opacity: 0
		};

	$.effects.createPlaceholder( element );

	distance = options.distance ||
		element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ) / 2;

	animation[ ref ] = motion + distance;

	if ( show ) {
		element.css( animation );

		animation[ ref ] = oppositeMotion + distance;
		animation.opacity = 1;
	}

	// Animate
	element.animate( animation, {
		queue: false,
		duration: options.duration,
		easing: options.easing,
		complete: done
	} );
} );


/*!
 * jQuery UI Effects Explode 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Explode Effect
//>>group: Effects
// jscs:disable maximumLineLength
//>>description: Explodes an element in all directions into n pieces. Implodes an element to its original wholeness.
// jscs:enable maximumLineLength
//>>docs: http://api.jqueryui.com/explode-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectExplode = $.effects.define( "explode", "hide", function( options, done ) {

	var i, j, left, top, mx, my,
		rows = options.pieces ? Math.round( Math.sqrt( options.pieces ) ) : 3,
		cells = rows,
		element = $( this ),
		mode = options.mode,
		show = mode === "show",

		// Show and then visibility:hidden the element before calculating offset
		offset = element.show().css( "visibility", "hidden" ).offset(),

		// Width and height of a piece
		width = Math.ceil( element.outerWidth() / cells ),
		height = Math.ceil( element.outerHeight() / rows ),
		pieces = [];

	// Children animate complete:
	function childComplete() {
		pieces.push( this );
		if ( pieces.length === rows * cells ) {
			animComplete();
		}
	}

	// Clone the element for each row and cell.
	for ( i = 0; i < rows; i++ ) { // ===>
		top = offset.top + i * height;
		my = i - ( rows - 1 ) / 2;

		for ( j = 0; j < cells; j++ ) { // |||
			left = offset.left + j * width;
			mx = j - ( cells - 1 ) / 2;

			// Create a clone of the now hidden main element that will be absolute positioned
			// within a wrapper div off the -left and -top equal to size of our pieces
			element
				.clone()
				.appendTo( "body" )
				.wrap( "<div></div>" )
				.css( {
					position: "absolute",
					visibility: "visible",
					left: -j * width,
					top: -i * height
				} )

				// Select the wrapper - make it overflow: hidden and absolute positioned based on
				// where the original was located +left and +top equal to the size of pieces
				.parent()
					.addClass( "ui-effects-explode" )
					.css( {
						position: "absolute",
						overflow: "hidden",
						width: width,
						height: height,
						left: left + ( show ? mx * width : 0 ),
						top: top + ( show ? my * height : 0 ),
						opacity: show ? 0 : 1
					} )
					.animate( {
						left: left + ( show ? 0 : mx * width ),
						top: top + ( show ? 0 : my * height ),
						opacity: show ? 1 : 0
					}, options.duration || 500, options.easing, childComplete );
		}
	}

	function animComplete() {
		element.css( {
			visibility: "visible"
		} );
		$( pieces ).remove();
		done();
	}
} );


/*!
 * jQuery UI Effects Fade 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Fade Effect
//>>group: Effects
//>>description: Fades the element.
//>>docs: http://api.jqueryui.com/fade-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectFade = $.effects.define( "fade", "toggle", function( options, done ) {
	var show = options.mode === "show";

	$( this )
		.css( "opacity", show ? 0 : 1 )
		.animate( {
			opacity: show ? 1 : 0
		}, {
			queue: false,
			duration: options.duration,
			easing: options.easing,
			complete: done
		} );
} );


/*!
 * jQuery UI Effects Fold 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Fold Effect
//>>group: Effects
//>>description: Folds an element first horizontally and then vertically.
//>>docs: http://api.jqueryui.com/fold-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectFold = $.effects.define( "fold", "hide", function( options, done ) {

	// Create element
	var element = $( this ),
		mode = options.mode,
		show = mode === "show",
		hide = mode === "hide",
		size = options.size || 15,
		percent = /([0-9]+)%/.exec( size ),
		horizFirst = !!options.horizFirst,
		ref = horizFirst ? [ "right", "bottom" ] : [ "bottom", "right" ],
		duration = options.duration / 2,

		placeholder = $.effects.createPlaceholder( element ),

		start = element.cssClip(),
		animation1 = { clip: $.extend( {}, start ) },
		animation2 = { clip: $.extend( {}, start ) },

		distance = [ start[ ref[ 0 ] ], start[ ref[ 1 ] ] ],

		queuelen = element.queue().length;

	if ( percent ) {
		size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
	}
	animation1.clip[ ref[ 0 ] ] = size;
	animation2.clip[ ref[ 0 ] ] = size;
	animation2.clip[ ref[ 1 ] ] = 0;

	if ( show ) {
		element.cssClip( animation2.clip );
		if ( placeholder ) {
			placeholder.css( $.effects.clipToBox( animation2 ) );
		}

		animation2.clip = start;
	}

	// Animate
	element
		.queue( function( next ) {
			if ( placeholder ) {
				placeholder
					.animate( $.effects.clipToBox( animation1 ), duration, options.easing )
					.animate( $.effects.clipToBox( animation2 ), duration, options.easing );
			}

			next();
		} )
		.animate( animation1, duration, options.easing )
		.animate( animation2, duration, options.easing )
		.queue( done );

	$.effects.unshift( element, queuelen, 4 );
} );


/*!
 * jQuery UI Effects Highlight 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Highlight Effect
//>>group: Effects
//>>description: Highlights the background of an element in a defined color for a custom duration.
//>>docs: http://api.jqueryui.com/highlight-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectHighlight = $.effects.define( "highlight", "show", function( options, done ) {
	var element = $( this ),
		animation = {
			backgroundColor: element.css( "backgroundColor" )
		};

	if ( options.mode === "hide" ) {
		animation.opacity = 0;
	}

	$.effects.saveStyle( element );

	element
		.css( {
			backgroundImage: "none",
			backgroundColor: options.color || "#ffff99"
		} )
		.animate( animation, {
			queue: false,
			duration: options.duration,
			easing: options.easing,
			complete: done
		} );
} );


/*!
 * jQuery UI Effects Size 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Size Effect
//>>group: Effects
//>>description: Resize an element to a specified width and height.
//>>docs: http://api.jqueryui.com/size-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectSize = $.effects.define( "size", function( options, done ) {

	// Create element
	var baseline, factor, temp,
		element = $( this ),

		// Copy for children
		cProps = [ "fontSize" ],
		vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
		hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],

		// Set options
		mode = options.mode,
		restore = mode !== "effect",
		scale = options.scale || "both",
		origin = options.origin || [ "middle", "center" ],
		position = element.css( "position" ),
		pos = element.position(),
		original = $.effects.scaledDimensions( element ),
		from = options.from || original,
		to = options.to || $.effects.scaledDimensions( element, 0 );

	$.effects.createPlaceholder( element );

	if ( mode === "show" ) {
		temp = from;
		from = to;
		to = temp;
	}

	// Set scaling factor
	factor = {
		from: {
			y: from.height / original.height,
			x: from.width / original.width
		},
		to: {
			y: to.height / original.height,
			x: to.width / original.width
		}
	};

	// Scale the css box
	if ( scale === "box" || scale === "both" ) {

		// Vertical props scaling
		if ( factor.from.y !== factor.to.y ) {
			from = $.effects.setTransition( element, vProps, factor.from.y, from );
			to = $.effects.setTransition( element, vProps, factor.to.y, to );
		}

		// Horizontal props scaling
		if ( factor.from.x !== factor.to.x ) {
			from = $.effects.setTransition( element, hProps, factor.from.x, from );
			to = $.effects.setTransition( element, hProps, factor.to.x, to );
		}
	}

	// Scale the content
	if ( scale === "content" || scale === "both" ) {

		// Vertical props scaling
		if ( factor.from.y !== factor.to.y ) {
			from = $.effects.setTransition( element, cProps, factor.from.y, from );
			to = $.effects.setTransition( element, cProps, factor.to.y, to );
		}
	}

	// Adjust the position properties based on the provided origin points
	if ( origin ) {
		baseline = $.effects.getBaseline( origin, original );
		from.top = ( original.outerHeight - from.outerHeight ) * baseline.y + pos.top;
		from.left = ( original.outerWidth - from.outerWidth ) * baseline.x + pos.left;
		to.top = ( original.outerHeight - to.outerHeight ) * baseline.y + pos.top;
		to.left = ( original.outerWidth - to.outerWidth ) * baseline.x + pos.left;
	}
	element.css( from );

	// Animate the children if desired
	if ( scale === "content" || scale === "both" ) {

		vProps = vProps.concat( [ "marginTop", "marginBottom" ] ).concat( cProps );
		hProps = hProps.concat( [ "marginLeft", "marginRight" ] );

		// Only animate children with width attributes specified
		// TODO: is this right? should we include anything with css width specified as well
		element.find( "*[width]" ).each( function() {
			var child = $( this ),
				childOriginal = $.effects.scaledDimensions( child ),
				childFrom = {
					height: childOriginal.height * factor.from.y,
					width: childOriginal.width * factor.from.x,
					outerHeight: childOriginal.outerHeight * factor.from.y,
					outerWidth: childOriginal.outerWidth * factor.from.x
				},
				childTo = {
					height: childOriginal.height * factor.to.y,
					width: childOriginal.width * factor.to.x,
					outerHeight: childOriginal.height * factor.to.y,
					outerWidth: childOriginal.width * factor.to.x
				};

			// Vertical props scaling
			if ( factor.from.y !== factor.to.y ) {
				childFrom = $.effects.setTransition( child, vProps, factor.from.y, childFrom );
				childTo = $.effects.setTransition( child, vProps, factor.to.y, childTo );
			}

			// Horizontal props scaling
			if ( factor.from.x !== factor.to.x ) {
				childFrom = $.effects.setTransition( child, hProps, factor.from.x, childFrom );
				childTo = $.effects.setTransition( child, hProps, factor.to.x, childTo );
			}

			if ( restore ) {
				$.effects.saveStyle( child );
			}

			// Animate children
			child.css( childFrom );
			child.animate( childTo, options.duration, options.easing, function() {

				// Restore children
				if ( restore ) {
					$.effects.restoreStyle( child );
				}
			} );
		} );
	}

	// Animate
	element.animate( to, {
		queue: false,
		duration: options.duration,
		easing: options.easing,
		complete: function() {

			var offset = element.offset();

			if ( to.opacity === 0 ) {
				element.css( "opacity", from.opacity );
			}

			if ( !restore ) {
				element
					.css( "position", position === "static" ? "relative" : position )
					.offset( offset );

				// Need to save style here so that automatic style restoration
				// doesn't restore to the original styles from before the animation.
				$.effects.saveStyle( element );
			}

			done();
		}
	} );

} );


/*!
 * jQuery UI Effects Scale 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Scale Effect
//>>group: Effects
//>>description: Grows or shrinks an element and its content.
//>>docs: http://api.jqueryui.com/scale-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectScale = $.effects.define( "scale", function( options, done ) {

	// Create element
	var el = $( this ),
		mode = options.mode,
		percent = parseInt( options.percent, 10 ) ||
			( parseInt( options.percent, 10 ) === 0 ? 0 : ( mode !== "effect" ? 0 : 100 ) ),

		newOptions = $.extend( true, {
			from: $.effects.scaledDimensions( el ),
			to: $.effects.scaledDimensions( el, percent, options.direction || "both" ),
			origin: options.origin || [ "middle", "center" ]
		}, options );

	// Fade option to support puff
	if ( options.fade ) {
		newOptions.from.opacity = 1;
		newOptions.to.opacity = 0;
	}

	$.effects.effect.size.call( this, newOptions, done );
} );


/*!
 * jQuery UI Effects Puff 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Puff Effect
//>>group: Effects
//>>description: Creates a puff effect by scaling the element up and hiding it at the same time.
//>>docs: http://api.jqueryui.com/puff-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectPuff = $.effects.define( "puff", "hide", function( options, done ) {
	var newOptions = $.extend( true, {}, options, {
		fade: true,
		percent: parseInt( options.percent, 10 ) || 150
	} );

	$.effects.effect.scale.call( this, newOptions, done );
} );


/*!
 * jQuery UI Effects Pulsate 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Pulsate Effect
//>>group: Effects
//>>description: Pulsates an element n times by changing the opacity to zero and back.
//>>docs: http://api.jqueryui.com/pulsate-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectPulsate = $.effects.define( "pulsate", "show", function( options, done ) {
	var element = $( this ),
		mode = options.mode,
		show = mode === "show",
		hide = mode === "hide",
		showhide = show || hide,

		// Showing or hiding leaves off the "last" animation
		anims = ( ( options.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
		duration = options.duration / anims,
		animateTo = 0,
		i = 1,
		queuelen = element.queue().length;

	if ( show || !element.is( ":visible" ) ) {
		element.css( "opacity", 0 ).show();
		animateTo = 1;
	}

	// Anims - 1 opacity "toggles"
	for ( ; i < anims; i++ ) {
		element.animate( { opacity: animateTo }, duration, options.easing );
		animateTo = 1 - animateTo;
	}

	element.animate( { opacity: animateTo }, duration, options.easing );

	element.queue( done );

	$.effects.unshift( element, queuelen, anims + 1 );
} );


/*!
 * jQuery UI Effects Shake 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Shake Effect
//>>group: Effects
//>>description: Shakes an element horizontally or vertically n times.
//>>docs: http://api.jqueryui.com/shake-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectShake = $.effects.define( "shake", function( options, done ) {

	var i = 1,
		element = $( this ),
		direction = options.direction || "left",
		distance = options.distance || 20,
		times = options.times || 3,
		anims = times * 2 + 1,
		speed = Math.round( options.duration / anims ),
		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
		positiveMotion = ( direction === "up" || direction === "left" ),
		animation = {},
		animation1 = {},
		animation2 = {},

		queuelen = element.queue().length;

	$.effects.createPlaceholder( element );

	// Animation
	animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
	animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
	animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;

	// Animate
	element.animate( animation, speed, options.easing );

	// Shakes
	for ( ; i < times; i++ ) {
		element
			.animate( animation1, speed, options.easing )
			.animate( animation2, speed, options.easing );
	}

	element
		.animate( animation1, speed, options.easing )
		.animate( animation, speed / 2, options.easing )
		.queue( done );

	$.effects.unshift( element, queuelen, anims + 1 );
} );


/*!
 * jQuery UI Effects Slide 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Slide Effect
//>>group: Effects
//>>description: Slides an element in and out of the viewport.
//>>docs: http://api.jqueryui.com/slide-effect/
//>>demos: http://jqueryui.com/effect/



var effectsEffectSlide = $.effects.define( "slide", "show", function( options, done ) {
	var startClip, startRef,
		element = $( this ),
		map = {
			up: [ "bottom", "top" ],
			down: [ "top", "bottom" ],
			left: [ "right", "left" ],
			right: [ "left", "right" ]
		},
		mode = options.mode,
		direction = options.direction || "left",
		ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
		positiveMotion = ( direction === "up" || direction === "left" ),
		distance = options.distance ||
			element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ),
		animation = {};

	$.effects.createPlaceholder( element );

	startClip = element.cssClip();
	startRef = element.position()[ ref ];

	// Define hide animation
	animation[ ref ] = ( positiveMotion ? -1 : 1 ) * distance + startRef;
	animation.clip = element.cssClip();
	animation.clip[ map[ direction ][ 1 ] ] = animation.clip[ map[ direction ][ 0 ] ];

	// Reverse the animation if we're showing
	if ( mode === "show" ) {
		element.cssClip( animation.clip );
		element.css( ref, animation[ ref ] );
		animation.clip = startClip;
		animation[ ref ] = startRef;
	}

	// Actually animate
	element.animate( animation, {
		queue: false,
		duration: options.duration,
		easing: options.easing,
		complete: done
	} );
} );


/*!
 * jQuery UI Effects Transfer 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Transfer Effect
//>>group: Effects
//>>description: Displays a transfer effect from one element to another.
//>>docs: http://api.jqueryui.com/transfer-effect/
//>>demos: http://jqueryui.com/effect/



var effect;
if ( $.uiBackCompat !== false ) {
	effect = $.effects.define( "transfer", function( options, done ) {
		$( this ).transfer( options, done );
	} );
}
var effectsEffectTransfer = effect;


/*!
 * jQuery UI Focusable 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: :focusable Selector
//>>group: Core
//>>description: Selects elements which can be focused.
//>>docs: http://api.jqueryui.com/focusable-selector/



// Selectors
$.ui.focusable = function( element, hasTabindex ) {
	var map, mapName, img, focusableIfVisible, fieldset,
		nodeName = element.nodeName.toLowerCase();

	if ( "area" === nodeName ) {
		map = element.parentNode;
		mapName = map.name;
		if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
			return false;
		}
		img = $( "img[usemap='#" + mapName + "']" );
		return img.length > 0 && img.is( ":visible" );
	}

	if ( /^(input|select|textarea|button|object)$/.test( nodeName ) ) {
		focusableIfVisible = !element.disabled;

		if ( focusableIfVisible ) {

			// Form controls within a disabled fieldset are disabled.
			// However, controls within the fieldset's legend do not get disabled.
			// Since controls generally aren't placed inside legends, we skip
			// this portion of the check.
			fieldset = $( element ).closest( "fieldset" )[ 0 ];
			if ( fieldset ) {
				focusableIfVisible = !fieldset.disabled;
			}
		}
	} else if ( "a" === nodeName ) {
		focusableIfVisible = element.href || hasTabindex;
	} else {
		focusableIfVisible = hasTabindex;
	}

	return focusableIfVisible && $( element ).is( ":visible" ) && visible( $( element ) );
};

// Support: IE 8 only
// IE 8 doesn't resolve inherit to visible/hidden for computed values
function visible( element ) {
	var visibility = element.css( "visibility" );
	while ( visibility === "inherit" ) {
		element = element.parent();
		visibility = element.css( "visibility" );
	}
	return visibility !== "hidden";
}

$.extend( $.expr[ ":" ], {
	focusable: function( element ) {
		return $.ui.focusable( element, $.attr( element, "tabindex" ) != null );
	}
} );

var focusable = $.ui.focusable;




// Support: IE8 Only
// IE8 does not support the form attribute and when it is supplied. It overwrites the form prop
// with a string, so we need to find the proper form.
var form = $.fn.form = function() {
	return typeof this[ 0 ].form === "string" ? this.closest( "form" ) : $( this[ 0 ].form );
};


/*!
 * jQuery UI Form Reset Mixin 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Form Reset Mixin
//>>group: Core
//>>description: Refresh input widgets when their form is reset
//>>docs: http://api.jqueryui.com/form-reset-mixin/



var formResetMixin = $.ui.formResetMixin = {
	_formResetHandler: function() {
		var form = $( this );

		// Wait for the form reset to actually happen before refreshing
		setTimeout( function() {
			var instances = form.data( "ui-form-reset-instances" );
			$.each( instances, function() {
				this.refresh();
			} );
		} );
	},

	_bindFormResetHandler: function() {
		this.form = this.element.form();
		if ( !this.form.length ) {
			return;
		}

		var instances = this.form.data( "ui-form-reset-instances" ) || [];
		if ( !instances.length ) {

			// We don't use _on() here because we use a single event handler per form
			this.form.on( "reset.ui-form-reset", this._formResetHandler );
		}
		instances.push( this );
		this.form.data( "ui-form-reset-instances", instances );
	},

	_unbindFormResetHandler: function() {
		if ( !this.form.length ) {
			return;
		}

		var instances = this.form.data( "ui-form-reset-instances" );
		instances.splice( $.inArray( this, instances ), 1 );
		if ( instances.length ) {
			this.form.data( "ui-form-reset-instances", instances );
		} else {
			this.form
				.removeData( "ui-form-reset-instances" )
				.off( "reset.ui-form-reset" );
		}
	}
};


/*!
 * jQuery UI Support for jQuery core 1.7.x 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 */

//>>label: jQuery 1.7 Support
//>>group: Core
//>>description: Support version 1.7.x of jQuery core



// Support: jQuery 1.7 only
// Not a great way to check versions, but since we only support 1.7+ and only
// need to detect <1.8, this is a simple check that should suffice. Checking
// for "1.7." would be a bit safer, but the version string is 1.7, not 1.7.0
// and we'll never reach 1.70.0 (if we do, we certainly won't be supporting
// 1.7 anymore). See #11197 for why we're not using feature detection.
if ( $.fn.jquery.substring( 0, 3 ) === "1.7" ) {

	// Setters for .innerWidth(), .innerHeight(), .outerWidth(), .outerHeight()
	// Unlike jQuery Core 1.8+, these only support numeric values to set the
	// dimensions in pixels
	$.each( [ "Width", "Height" ], function( i, name ) {
		var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
			type = name.toLowerCase(),
			orig = {
				innerWidth: $.fn.innerWidth,
				innerHeight: $.fn.innerHeight,
				outerWidth: $.fn.outerWidth,
				outerHeight: $.fn.outerHeight
			};

		function reduce( elem, size, border, margin ) {
			$.each( side, function() {
				size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
				if ( border ) {
					size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
				}
				if ( margin ) {
					size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
				}
			} );
			return size;
		}

		$.fn[ "inner" + name ] = function( size ) {
			if ( size === undefined ) {
				return orig[ "inner" + name ].call( this );
			}

			return this.each( function() {
				$( this ).css( type, reduce( this, size ) + "px" );
			} );
		};

		$.fn[ "outer" + name ] = function( size, margin ) {
			if ( typeof size !== "number" ) {
				return orig[ "outer" + name ].call( this, size );
			}

			return this.each( function() {
				$( this ).css( type, reduce( this, size, true, margin ) + "px" );
			} );
		};
	} );

	$.fn.addBack = function( selector ) {
		return this.add( selector == null ?
			this.prevObject : this.prevObject.filter( selector )
		);
	};
}

;
/*!
 * jQuery UI Keycode 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Keycode
//>>group: Core
//>>description: Provide keycodes as keynames
//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/


var keycode = $.ui.keyCode = {
	BACKSPACE: 8,
	COMMA: 188,
	DELETE: 46,
	DOWN: 40,
	END: 35,
	ENTER: 13,
	ESCAPE: 27,
	HOME: 36,
	LEFT: 37,
	PAGE_DOWN: 34,
	PAGE_UP: 33,
	PERIOD: 190,
	RIGHT: 39,
	SPACE: 32,
	TAB: 9,
	UP: 38
};




// Internal use only
var escapeSelector = $.ui.escapeSelector = ( function() {
	var selectorEscape = /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;
	return function( selector ) {
		return selector.replace( selectorEscape, "\\$1" );
	};
} )();


/*!
 * jQuery UI Labels 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: labels
//>>group: Core
//>>description: Find all the labels associated with a given input
//>>docs: http://api.jqueryui.com/labels/



var labels = $.fn.labels = function() {
	var ancestor, selector, id, labels, ancestors;

	// Check control.labels first
	if ( this[ 0 ].labels && this[ 0 ].labels.length ) {
		return this.pushStack( this[ 0 ].labels );
	}

	// Support: IE <= 11, FF <= 37, Android <= 2.3 only
	// Above browsers do not support control.labels. Everything below is to support them
	// as well as document fragments. control.labels does not work on document fragments
	labels = this.eq( 0 ).parents( "label" );

	// Look for the label based on the id
	id = this.attr( "id" );
	if ( id ) {

		// We don't search against the document in case the element
		// is disconnected from the DOM
		ancestor = this.eq( 0 ).parents().last();

		// Get a full set of top level ancestors
		ancestors = ancestor.add( ancestor.length ? ancestor.siblings() : this.siblings() );

		// Create a selector for the label based on the id
		selector = "label[for='" + $.ui.escapeSelector( id ) + "']";

		labels = labels.add( ancestors.find( selector ).addBack( selector ) );

	}

	// Return whatever we have found for labels
	return this.pushStack( labels );
};


/*!
 * jQuery UI Scroll Parent 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: scrollParent
//>>group: Core
//>>description: Get the closest ancestor element that is scrollable.
//>>docs: http://api.jqueryui.com/scrollParent/



var scrollParent = $.fn.scrollParent = function( includeHidden ) {
	var position = this.css( "position" ),
		excludeStaticParent = position === "absolute",
		overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
		scrollParent = this.parents().filter( function() {
			var parent = $( this );
			if ( excludeStaticParent && parent.css( "position" ) === "static" ) {
				return false;
			}
			return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) +
				parent.css( "overflow-x" ) );
		} ).eq( 0 );

	return position === "fixed" || !scrollParent.length ?
		$( this[ 0 ].ownerDocument || document ) :
		scrollParent;
};


/*!
 * jQuery UI Tabbable 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: :tabbable Selector
//>>group: Core
//>>description: Selects elements which can be tabbed to.
//>>docs: http://api.jqueryui.com/tabbable-selector/



var tabbable = $.extend( $.expr[ ":" ], {
	tabbable: function( element ) {
		var tabIndex = $.attr( element, "tabindex" ),
			hasTabindex = tabIndex != null;
		return ( !hasTabindex || tabIndex >= 0 ) && $.ui.focusable( element, hasTabindex );
	}
} );


/*!
 * jQuery UI Unique ID 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: uniqueId
//>>group: Core
//>>description: Functions to generate and remove uniqueId's
//>>docs: http://api.jqueryui.com/uniqueId/



var uniqueId = $.fn.extend( {
	uniqueId: ( function() {
		var uuid = 0;

		return function() {
			return this.each( function() {
				if ( !this.id ) {
					this.id = "ui-id-" + ( ++uuid );
				}
			} );
		};
	} )(),

	removeUniqueId: function() {
		return this.each( function() {
			if ( /^ui-id-\d+$/.test( this.id ) ) {
				$( this ).removeAttr( "id" );
			}
		} );
	}
} );


/*!
 * jQuery UI Accordion 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Accordion
//>>group: Widgets
// jscs:disable maximumLineLength
//>>description: Displays collapsible content panels for presenting information in a limited amount of space.
// jscs:enable maximumLineLength
//>>docs: http://api.jqueryui.com/accordion/
//>>demos: http://jqueryui.com/accordion/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/accordion.css
//>>css.theme: ../../themes/base/theme.css



var widgetsAccordion = $.widget( "ui.accordion", {
	version: "1.12.1",
	options: {
		active: 0,
		animate: {},
		classes: {
			"ui-accordion-header": "ui-corner-top",
			"ui-accordion-header-collapsed": "ui-corner-all",
			"ui-accordion-content": "ui-corner-bottom"
		},
		collapsible: false,
		event: "click",
		header: "> li > :first-child, > :not(li):even",
		heightStyle: "auto",
		icons: {
			activeHeader: "ui-icon-triangle-1-s",
			header: "ui-icon-triangle-1-e"
		},

		// Callbacks
		activate: null,
		beforeActivate: null
	},

	hideProps: {
		borderTopWidth: "hide",
		borderBottomWidth: "hide",
		paddingTop: "hide",
		paddingBottom: "hide",
		height: "hide"
	},

	showProps: {
		borderTopWidth: "show",
		borderBottomWidth: "show",
		paddingTop: "show",
		paddingBottom: "show",
		height: "show"
	},

	_create: function() {
		var options = this.options;

		this.prevShow = this.prevHide = $();
		this._addClass( "ui-accordion", "ui-widget ui-helper-reset" );
		this.element.attr( "role", "tablist" );

		// Don't allow collapsible: false and active: false / null
		if ( !options.collapsible && ( options.active === false || options.active == null ) ) {
			options.active = 0;
		}

		this._processPanels();

		// handle negative values
		if ( options.active < 0 ) {
			options.active += this.headers.length;
		}
		this._refresh();
	},

	_getCreateEventData: function() {
		return {
			header: this.active,
			panel: !this.active.length ? $() : this.active.next()
		};
	},

	_createIcons: function() {
		var icon, children,
			icons = this.options.icons;

		if ( icons ) {
			icon = $( "<span>" );
			this._addClass( icon, "ui-accordion-header-icon", "ui-icon " + icons.header );
			icon.prependTo( this.headers );
			children = this.active.children( ".ui-accordion-header-icon" );
			this._removeClass( children, icons.header )
				._addClass( children, null, icons.activeHeader )
				._addClass( this.headers, "ui-accordion-icons" );
		}
	},

	_destroyIcons: function() {
		this._removeClass( this.headers, "ui-accordion-icons" );
		this.headers.children( ".ui-accordion-header-icon" ).remove();
	},

	_destroy: function() {
		var contents;

		// Clean up main element
		this.element.removeAttr( "role" );

		// Clean up headers
		this.headers
			.removeAttr( "role aria-expanded aria-selected aria-controls tabIndex" )
			.removeUniqueId();

		this._destroyIcons();

		// Clean up content panels
		contents = this.headers.next()
			.css( "display", "" )
			.removeAttr( "role aria-hidden aria-labelledby" )
			.removeUniqueId();

		if ( this.options.heightStyle !== "content" ) {
			contents.css( "height", "" );
		}
	},

	_setOption: function( key, value ) {
		if ( key === "active" ) {

			// _activate() will handle invalid values and update this.options
			this._activate( value );
			return;
		}

		if ( key === "event" ) {
			if ( this.options.event ) {
				this._off( this.headers, this.options.event );
			}
			this._setupEvents( value );
		}

		this._super( key, value );

		// Setting collapsible: false while collapsed; open first panel
		if ( key === "collapsible" && !value && this.options.active === false ) {
			this._activate( 0 );
		}

		if ( key === "icons" ) {
			this._destroyIcons();
			if ( value ) {
				this._createIcons();
			}
		}
	},

	_setOptionDisabled: function( value ) {
		this._super( value );

		this.element.attr( "aria-disabled", value );

		// Support: IE8 Only
		// #5332 / #6059 - opacity doesn't cascade to positioned elements in IE
		// so we need to add the disabled class to the headers and panels
		this._toggleClass( null, "ui-state-disabled", !!value );
		this._toggleClass( this.headers.add( this.headers.next() ), null, "ui-state-disabled",
			!!value );
	},

	_keydown: function( event ) {
		if ( event.altKey || event.ctrlKey ) {
			return;
		}

		var keyCode = $.ui.keyCode,
			length = this.headers.length,
			currentIndex = this.headers.index( event.target ),
			toFocus = false;

		switch ( event.keyCode ) {
		case keyCode.RIGHT:
		case keyCode.DOWN:
			toFocus = this.headers[ ( currentIndex + 1 ) % length ];
			break;
		case keyCode.LEFT:
		case keyCode.UP:
			toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
			break;
		case keyCode.SPACE:
		case keyCode.ENTER:
			this._eventHandler( event );
			break;
		case keyCode.HOME:
			toFocus = this.headers[ 0 ];
			break;
		case keyCode.END:
			toFocus = this.headers[ length - 1 ];
			break;
		}

		if ( toFocus ) {
			$( event.target ).attr( "tabIndex", -1 );
			$( toFocus ).attr( "tabIndex", 0 );
			$( toFocus ).trigger( "focus" );
			event.preventDefault();
		}
	},

	_panelKeyDown: function( event ) {
		if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
			$( event.currentTarget ).prev().trigger( "focus" );
		}
	},

	refresh: function() {
		var options = this.options;
		this._processPanels();

		// Was collapsed or no panel
		if ( ( options.active === false && options.collapsible === true ) ||
				!this.headers.length ) {
			options.active = false;
			this.active = $();

		// active false only when collapsible is true
		} else if ( options.active === false ) {
			this._activate( 0 );

		// was active, but active panel is gone
		} else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {

			// all remaining panel are disabled
			if ( this.headers.length === this.headers.find( ".ui-state-disabled" ).length ) {
				options.active = false;
				this.active = $();

			// activate previous panel
			} else {
				this._activate( Math.max( 0, options.active - 1 ) );
			}

		// was active, active panel still exists
		} else {

			// make sure active index is correct
			options.active = this.headers.index( this.active );
		}

		this._destroyIcons();

		this._refresh();
	},

	_processPanels: function() {
		var prevHeaders = this.headers,
			prevPanels = this.panels;

		this.headers = this.element.find( this.options.header );
		this._addClass( this.headers, "ui-accordion-header ui-accordion-header-collapsed",
			"ui-state-default" );

		this.panels = this.headers.next().filter( ":not(.ui-accordion-content-active)" ).hide();
		this._addClass( this.panels, "ui-accordion-content", "ui-helper-reset ui-widget-content" );

		// Avoid memory leaks (#10056)
		if ( prevPanels ) {
			this._off( prevHeaders.not( this.headers ) );
			this._off( prevPanels.not( this.panels ) );
		}
	},

	_refresh: function() {
		var maxHeight,
			options = this.options,
			heightStyle = options.heightStyle,
			parent = this.element.parent();

		this.active = this._findActive( options.active );
		this._addClass( this.active, "ui-accordion-header-active", "ui-state-active" )
			._removeClass( this.active, "ui-accordion-header-collapsed" );
		this._addClass( this.active.next(), "ui-accordion-content-active" );
		this.active.next().show();

		this.headers
			.attr( "role", "tab" )
			.each( function() {
				var header = $( this ),
					headerId = header.uniqueId().attr( "id" ),
					panel = header.next(),
					panelId = panel.uniqueId().attr( "id" );
				header.attr( "aria-controls", panelId );
				panel.attr( "aria-labelledby", headerId );
			} )
			.next()
				.attr( "role", "tabpanel" );

		this.headers
			.not( this.active )
				.attr( {
					"aria-selected": "false",
					"aria-expanded": "false",
					tabIndex: -1
				} )
				.next()
					.attr( {
						"aria-hidden": "true"
					} )
					.hide();

		// Make sure at least one header is in the tab order
		if ( !this.active.length ) {
			this.headers.eq( 0 ).attr( "tabIndex", 0 );
		} else {
			this.active.attr( {
				"aria-selected": "true",
				"aria-expanded": "true",
				tabIndex: 0
			} )
				.next()
					.attr( {
						"aria-hidden": "false"
					} );
		}

		this._createIcons();

		this._setupEvents( options.event );

		if ( heightStyle === "fill" ) {
			maxHeight = parent.height();
			this.element.siblings( ":visible" ).each( function() {
				var elem = $( this ),
					position = elem.css( "position" );

				if ( position === "absolute" || position === "fixed" ) {
					return;
				}
				maxHeight -= elem.outerHeight( true );
			} );

			this.headers.each( function() {
				maxHeight -= $( this ).outerHeight( true );
			} );

			this.headers.next()
				.each( function() {
					$( this ).height( Math.max( 0, maxHeight -
						$( this ).innerHeight() + $( this ).height() ) );
				} )
				.css( "overflow", "auto" );
		} else if ( heightStyle === "auto" ) {
			maxHeight = 0;
			this.headers.next()
				.each( function() {
					var isVisible = $( this ).is( ":visible" );
					if ( !isVisible ) {
						$( this ).show();
					}
					maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
					if ( !isVisible ) {
						$( this ).hide();
					}
				} )
				.height( maxHeight );
		}
	},

	_activate: function( index ) {
		var active = this._findActive( index )[ 0 ];

		// Trying to activate the already active panel
		if ( active === this.active[ 0 ] ) {
			return;
		}

		// Trying to collapse, simulate a click on the currently active header
		active = active || this.active[ 0 ];

		this._eventHandler( {
			target: active,
			currentTarget: active,
			preventDefault: $.noop
		} );
	},

	_findActive: function( selector ) {
		return typeof selector === "number" ? this.headers.eq( selector ) : $();
	},

	_setupEvents: function( event ) {
		var events = {
			keydown: "_keydown"
		};
		if ( event ) {
			$.each( event.split( " " ), function( index, eventName ) {
				events[ eventName ] = "_eventHandler";
			} );
		}

		this._off( this.headers.add( this.headers.next() ) );
		this._on( this.headers, events );
		this._on( this.headers.next(), { keydown: "_panelKeyDown" } );
		this._hoverable( this.headers );
		this._focusable( this.headers );
	},

	_eventHandler: function( event ) {
		var activeChildren, clickedChildren,
			options = this.options,
			active = this.active,
			clicked = $( event.currentTarget ),
			clickedIsActive = clicked[ 0 ] === active[ 0 ],
			collapsing = clickedIsActive && options.collapsible,
			toShow = collapsing ? $() : clicked.next(),
			toHide = active.next(),
			eventData = {
				oldHeader: active,
				oldPanel: toHide,
				newHeader: collapsing ? $() : clicked,
				newPanel: toShow
			};

		event.preventDefault();

		if (

				// click on active header, but not collapsible
				( clickedIsActive && !options.collapsible ) ||

				// allow canceling activation
				( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
			return;
		}

		options.active = collapsing ? false : this.headers.index( clicked );

		// When the call to ._toggle() comes after the class changes
		// it causes a very odd bug in IE 8 (see #6720)
		this.active = clickedIsActive ? $() : clicked;
		this._toggle( eventData );

		// Switch classes
		// corner classes on the previously active header stay after the animation
		this._removeClass( active, "ui-accordion-header-active", "ui-state-active" );
		if ( options.icons ) {
			activeChildren = active.children( ".ui-accordion-header-icon" );
			this._removeClass( activeChildren, null, options.icons.activeHeader )
				._addClass( activeChildren, null, options.icons.header );
		}

		if ( !clickedIsActive ) {
			this._removeClass( clicked, "ui-accordion-header-collapsed" )
				._addClass( clicked, "ui-accordion-header-active", "ui-state-active" );
			if ( options.icons ) {
				clickedChildren = clicked.children( ".ui-accordion-header-icon" );
				this._removeClass( clickedChildren, null, options.icons.header )
					._addClass( clickedChildren, null, options.icons.activeHeader );
			}

			this._addClass( clicked.next(), "ui-accordion-content-active" );
		}
	},

	_toggle: function( data ) {
		var toShow = data.newPanel,
			toHide = this.prevShow.length ? this.prevShow : data.oldPanel;

		// Handle activating a panel during the animation for another activation
		this.prevShow.add( this.prevHide ).stop( true, true );
		this.prevShow = toShow;
		this.prevHide = toHide;

		if ( this.options.animate ) {
			this._animate( toShow, toHide, data );
		} else {
			toHide.hide();
			toShow.show();
			this._toggleComplete( data );
		}

		toHide.attr( {
			"aria-hidden": "true"
		} );
		toHide.prev().attr( {
			"aria-selected": "false",
			"aria-expanded": "false"
		} );

		// if we're switching panels, remove the old header from the tab order
		// if we're opening from collapsed state, remove the previous header from the tab order
		// if we're collapsing, then keep the collapsing header in the tab order
		if ( toShow.length && toHide.length ) {
			toHide.prev().attr( {
				"tabIndex": -1,
				"aria-expanded": "false"
			} );
		} else if ( toShow.length ) {
			this.headers.filter( function() {
				return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0;
			} )
				.attr( "tabIndex", -1 );
		}

		toShow
			.attr( "aria-hidden", "false" )
			.prev()
				.attr( {
					"aria-selected": "true",
					"aria-expanded": "true",
					tabIndex: 0
				} );
	},

	_animate: function( toShow, toHide, data ) {
		var total, easing, duration,
			that = this,
			adjust = 0,
			boxSizing = toShow.css( "box-sizing" ),
			down = toShow.length &&
				( !toHide.length || ( toShow.index() < toHide.index() ) ),
			animate = this.options.animate || {},
			options = down && animate.down || animate,
			complete = function() {
				that._toggleComplete( data );
			};

		if ( typeof options === "number" ) {
			duration = options;
		}
		if ( typeof options === "string" ) {
			easing = options;
		}

		// fall back from options to animation in case of partial down settings
		easing = easing || options.easing || animate.easing;
		duration = duration || options.duration || animate.duration;

		if ( !toHide.length ) {
			return toShow.animate( this.showProps, duration, easing, complete );
		}
		if ( !toShow.length ) {
			return toHide.animate( this.hideProps, duration, easing, complete );
		}

		total = toShow.show().outerHeight();
		toHide.animate( this.hideProps, {
			duration: duration,
			easing: easing,
			step: function( now, fx ) {
				fx.now = Math.round( now );
			}
		} );
		toShow
			.hide()
			.animate( this.showProps, {
				duration: duration,
				easing: easing,
				complete: complete,
				step: function( now, fx ) {
					fx.now = Math.round( now );
					if ( fx.prop !== "height" ) {
						if ( boxSizing === "content-box" ) {
							adjust += fx.now;
						}
					} else if ( that.options.heightStyle !== "content" ) {
						fx.now = Math.round( total - toHide.outerHeight() - adjust );
						adjust = 0;
					}
				}
			} );
	},

	_toggleComplete: function( data ) {
		var toHide = data.oldPanel,
			prev = toHide.prev();

		this._removeClass( toHide, "ui-accordion-content-active" );
		this._removeClass( prev, "ui-accordion-header-active" )
			._addClass( prev, "ui-accordion-header-collapsed" );

		// Work around for rendering bug in IE (#5421)
		if ( toHide.length ) {
			toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className;
		}
		this._trigger( "activate", null, data );
	}
} );



var safeActiveElement = $.ui.safeActiveElement = function( document ) {
	var activeElement;

	// Support: IE 9 only
	// IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
	try {
		activeElement = document.activeElement;
	} catch ( error ) {
		activeElement = document.body;
	}

	// Support: IE 9 - 11 only
	// IE may return null instead of an element
	// Interestingly, this only seems to occur when NOT in an iframe
	if ( !activeElement ) {
		activeElement = document.body;
	}

	// Support: IE 11 only
	// IE11 returns a seemingly empty object in some cases when accessing
	// document.activeElement from an <iframe>
	if ( !activeElement.nodeName ) {
		activeElement = document.body;
	}

	return activeElement;
};


/*!
 * jQuery UI Menu 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Menu
//>>group: Widgets
//>>description: Creates nestable menus.
//>>docs: http://api.jqueryui.com/menu/
//>>demos: http://jqueryui.com/menu/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/menu.css
//>>css.theme: ../../themes/base/theme.css



var widgetsMenu = $.widget( "ui.menu", {
	version: "1.12.1",
	defaultElement: "<ul>",
	delay: 300,
	options: {
		icons: {
			submenu: "ui-icon-caret-1-e"
		},
		items: "> *",
		menus: "ul",
		position: {
			my: "left top",
			at: "right top"
		},
		role: "menu",

		// Callbacks
		blur: null,
		focus: null,
		select: null
	},

	_create: function() {
		this.activeMenu = this.element;

		// Flag used to prevent firing of the click handler
		// as the event bubbles up through nested menus
		this.mouseHandled = false;
		this.element
			.uniqueId()
			.attr( {
				role: this.options.role,
				tabIndex: 0
			} );

		this._addClass( "ui-menu", "ui-widget ui-widget-content" );
		this._on( {

			// Prevent focus from sticking to links inside menu after clicking
			// them (focus should always stay on UL during navigation).
			"mousedown .ui-menu-item": function( event ) {
				event.preventDefault();
			},
			"click .ui-menu-item": function( event ) {
				var target = $( event.target );
				var active = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
				if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
					this.select( event );

					// Only set the mouseHandled flag if the event will bubble, see #9469.
					if ( !event.isPropagationStopped() ) {
						this.mouseHandled = true;
					}

					// Open submenu on click
					if ( target.has( ".ui-menu" ).length ) {
						this.expand( event );
					} else if ( !this.element.is( ":focus" ) &&
							active.closest( ".ui-menu" ).length ) {

						// Redirect focus to the menu
						this.element.trigger( "focus", [ true ] );

						// If the active item is on the top level, let it stay active.
						// Otherwise, blur the active item since it is no longer visible.
						if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
							clearTimeout( this.timer );
						}
					}
				}
			},
			"mouseenter .ui-menu-item": function( event ) {

				// Ignore mouse events while typeahead is active, see #10458.
				// Prevents focusing the wrong item when typeahead causes a scroll while the mouse
				// is over an item in the menu
				if ( this.previousFilter ) {
					return;
				}

				var actualTarget = $( event.target ).closest( ".ui-menu-item" ),
					target = $( event.currentTarget );

				// Ignore bubbled events on parent items, see #11641
				if ( actualTarget[ 0 ] !== target[ 0 ] ) {
					return;
				}

				// Remove ui-state-active class from siblings of the newly focused menu item
				// to avoid a jump caused by adjacent elements both having a class with a border
				this._removeClass( target.siblings().children( ".ui-state-active" ),
					null, "ui-state-active" );
				this.focus( event, target );
			},
			mouseleave: "collapseAll",
			"mouseleave .ui-menu": "collapseAll",
			focus: function( event, keepActiveItem ) {

				// If there's already an active item, keep it active
				// If not, activate the first item
				var item = this.active || this.element.find( this.options.items ).eq( 0 );

				if ( !keepActiveItem ) {
					this.focus( event, item );
				}
			},
			blur: function( event ) {
				this._delay( function() {
					var notContained = !$.contains(
						this.element[ 0 ],
						$.ui.safeActiveElement( this.document[ 0 ] )
					);
					if ( notContained ) {
						this.collapseAll( event );
					}
				} );
			},
			keydown: "_keydown"
		} );

		this.refresh();

		// Clicks outside of a menu collapse any open menus
		this._on( this.document, {
			click: function( event ) {
				if ( this._closeOnDocumentClick( event ) ) {
					this.collapseAll( event );
				}

				// Reset the mouseHandled flag
				this.mouseHandled = false;
			}
		} );
	},

	_destroy: function() {
		var items = this.element.find( ".ui-menu-item" )
				.removeAttr( "role aria-disabled" ),
			submenus = items.children( ".ui-menu-item-wrapper" )
				.removeUniqueId()
				.removeAttr( "tabIndex role aria-haspopup" );

		// Destroy (sub)menus
		this.element
			.removeAttr( "aria-activedescendant" )
			.find( ".ui-menu" ).addBack()
				.removeAttr( "role aria-labelledby aria-expanded aria-hidden aria-disabled " +
					"tabIndex" )
				.removeUniqueId()
				.show();

		submenus.children().each( function() {
			var elem = $( this );
			if ( elem.data( "ui-menu-submenu-caret" ) ) {
				elem.remove();
			}
		} );
	},

	_keydown: function( event ) {
		var match, prev, character, skip,
			preventDefault = true;

		switch ( event.keyCode ) {
		case $.ui.keyCode.PAGE_UP:
			this.previousPage( event );
			break;
		case $.ui.keyCode.PAGE_DOWN:
			this.nextPage( event );
			break;
		case $.ui.keyCode.HOME:
			this._move( "first", "first", event );
			break;
		case $.ui.keyCode.END:
			this._move( "last", "last", event );
			break;
		case $.ui.keyCode.UP:
			this.previous( event );
			break;
		case $.ui.keyCode.DOWN:
			this.next( event );
			break;
		case $.ui.keyCode.LEFT:
			this.collapse( event );
			break;
		case $.ui.keyCode.RIGHT:
			if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
				this.expand( event );
			}
			break;
		case $.ui.keyCode.ENTER:
		case $.ui.keyCode.SPACE:
			this._activate( event );
			break;
		case $.ui.keyCode.ESCAPE:
			this.collapse( event );
			break;
		default:
			preventDefault = false;
			prev = this.previousFilter || "";
			skip = false;

			// Support number pad values
			character = event.keyCode >= 96 && event.keyCode <= 105 ?
				( event.keyCode - 96 ).toString() : String.fromCharCode( event.keyCode );

			clearTimeout( this.filterTimer );

			if ( character === prev ) {
				skip = true;
			} else {
				character = prev + character;
			}

			match = this._filterMenuItems( character );
			match = skip && match.index( this.active.next() ) !== -1 ?
				this.active.nextAll( ".ui-menu-item" ) :
				match;

			// If no matches on the current filter, reset to the last character pressed
			// to move down the menu to the first item that starts with that character
			if ( !match.length ) {
				character = String.fromCharCode( event.keyCode );
				match = this._filterMenuItems( character );
			}

			if ( match.length ) {
				this.focus( event, match );
				this.previousFilter = character;
				this.filterTimer = this._delay( function() {
					delete this.previousFilter;
				}, 1000 );
			} else {
				delete this.previousFilter;
			}
		}

		if ( preventDefault ) {
			event.preventDefault();
		}
	},

	_activate: function( event ) {
		if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
			if ( this.active.children( "[aria-haspopup='true']" ).length ) {
				this.expand( event );
			} else {
				this.select( event );
			}
		}
	},

	refresh: function() {
		var menus, items, newSubmenus, newItems, newWrappers,
			that = this,
			icon = this.options.icons.submenu,
			submenus = this.element.find( this.options.menus );

		this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length );

		// Initialize nested menus
		newSubmenus = submenus.filter( ":not(.ui-menu)" )
			.hide()
			.attr( {
				role: this.options.role,
				"aria-hidden": "true",
				"aria-expanded": "false"
			} )
			.each( function() {
				var menu = $( this ),
					item = menu.prev(),
					submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true );

				that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon );
				item
					.attr( "aria-haspopup", "true" )
					.prepend( submenuCaret );
				menu.attr( "aria-labelledby", item.attr( "id" ) );
			} );

		this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" );

		menus = submenus.add( this.element );
		items = menus.find( this.options.items );

		// Initialize menu-items containing spaces and/or dashes only as dividers
		items.not( ".ui-menu-item" ).each( function() {
			var item = $( this );
			if ( that._isDivider( item ) ) {
				that._addClass( item, "ui-menu-divider", "ui-widget-content" );
			}
		} );

		// Don't refresh list items that are already adapted
		newItems = items.not( ".ui-menu-item, .ui-menu-divider" );
		newWrappers = newItems.children()
			.not( ".ui-menu" )
				.uniqueId()
				.attr( {
					tabIndex: -1,
					role: this._itemRole()
				} );
		this._addClass( newItems, "ui-menu-item" )
			._addClass( newWrappers, "ui-menu-item-wrapper" );

		// Add aria-disabled attribute to any disabled menu item
		items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" );

		// If the active item has been removed, blur the menu
		if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
			this.blur();
		}
	},

	_itemRole: function() {
		return {
			menu: "menuitem",
			listbox: "option"
		}[ this.options.role ];
	},

	_setOption: function( key, value ) {
		if ( key === "icons" ) {
			var icons = this.element.find( ".ui-menu-icon" );
			this._removeClass( icons, null, this.options.icons.submenu )
				._addClass( icons, null, value.submenu );
		}
		this._super( key, value );
	},

	_setOptionDisabled: function( value ) {
		this._super( value );

		this.element.attr( "aria-disabled", String( value ) );
		this._toggleClass( null, "ui-state-disabled", !!value );
	},

	focus: function( event, item ) {
		var nested, focused, activeParent;
		this.blur( event, event && event.type === "focus" );

		this._scrollIntoView( item );

		this.active = item.first();

		focused = this.active.children( ".ui-menu-item-wrapper" );
		this._addClass( focused, null, "ui-state-active" );

		// Only update aria-activedescendant if there's a role
		// otherwise we assume focus is managed elsewhere
		if ( this.options.role ) {
			this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
		}

		// Highlight active parent menu item, if any
		activeParent = this.active
			.parent()
				.closest( ".ui-menu-item" )
					.children( ".ui-menu-item-wrapper" );
		this._addClass( activeParent, null, "ui-state-active" );

		if ( event && event.type === "keydown" ) {
			this._close();
		} else {
			this.timer = this._delay( function() {
				this._close();
			}, this.delay );
		}

		nested = item.children( ".ui-menu" );
		if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
			this._startOpening( nested );
		}
		this.activeMenu = item.parent();

		this._trigger( "focus", event, { item: item } );
	},

	_scrollIntoView: function( item ) {
		var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
		if ( this._hasScroll() ) {
			borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0;
			paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0;
			offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
			scroll = this.activeMenu.scrollTop();
			elementHeight = this.activeMenu.height();
			itemHeight = item.outerHeight();

			if ( offset < 0 ) {
				this.activeMenu.scrollTop( scroll + offset );
			} else if ( offset + itemHeight > elementHeight ) {
				this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
			}
		}
	},

	blur: function( event, fromFocus ) {
		if ( !fromFocus ) {
			clearTimeout( this.timer );
		}

		if ( !this.active ) {
			return;
		}

		this._removeClass( this.active.children( ".ui-menu-item-wrapper" ),
			null, "ui-state-active" );

		this._trigger( "blur", event, { item: this.active } );
		this.active = null;
	},

	_startOpening: function( submenu ) {
		clearTimeout( this.timer );

		// Don't open if already open fixes a Firefox bug that caused a .5 pixel
		// shift in the submenu position when mousing over the caret icon
		if ( submenu.attr( "aria-hidden" ) !== "true" ) {
			return;
		}

		this.timer = this._delay( function() {
			this._close();
			this._open( submenu );
		}, this.delay );
	},

	_open: function( submenu ) {
		var position = $.extend( {
			of: this.active
		}, this.options.position );

		clearTimeout( this.timer );
		this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
			.hide()
			.attr( "aria-hidden", "true" );

		submenu
			.show()
			.removeAttr( "aria-hidden" )
			.attr( "aria-expanded", "true" )
			.position( position );
	},

	collapseAll: function( event, all ) {
		clearTimeout( this.timer );
		this.timer = this._delay( function() {

			// If we were passed an event, look for the submenu that contains the event
			var currentMenu = all ? this.element :
				$( event && event.target ).closest( this.element.find( ".ui-menu" ) );

			// If we found no valid submenu ancestor, use the main menu to close all
			// sub menus anyway
			if ( !currentMenu.length ) {
				currentMenu = this.element;
			}

			this._close( currentMenu );

			this.blur( event );

			// Work around active item staying active after menu is blurred
			this._removeClass( currentMenu.find( ".ui-state-active" ), null, "ui-state-active" );

			this.activeMenu = currentMenu;
		}, this.delay );
	},

	// With no arguments, closes the currently active menu - if nothing is active
	// it closes all menus.  If passed an argument, it will search for menus BELOW
	_close: function( startMenu ) {
		if ( !startMenu ) {
			startMenu = this.active ? this.active.parent() : this.element;
		}

		startMenu.find( ".ui-menu" )
			.hide()
			.attr( "aria-hidden", "true" )
			.attr( "aria-expanded", "false" );
	},

	_closeOnDocumentClick: function( event ) {
		return !$( event.target ).closest( ".ui-menu" ).length;
	},

	_isDivider: function( item ) {

		// Match hyphen, em dash, en dash
		return !/[^\-\u2014\u2013\s]/.test( item.text() );
	},

	collapse: function( event ) {
		var newItem = this.active &&
			this.active.parent().closest( ".ui-menu-item", this.element );
		if ( newItem && newItem.length ) {
			this._close();
			this.focus( event, newItem );
		}
	},

	expand: function( event ) {
		var newItem = this.active &&
			this.active
				.children( ".ui-menu " )
					.find( this.options.items )
						.first();

		if ( newItem && newItem.length ) {
			this._open( newItem.parent() );

			// Delay so Firefox will not hide activedescendant change in expanding submenu from AT
			this._delay( function() {
				this.focus( event, newItem );
			} );
		}
	},

	next: function( event ) {
		this._move( "next", "first", event );
	},

	previous: function( event ) {
		this._move( "prev", "last", event );
	},

	isFirstItem: function() {
		return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
	},

	isLastItem: function() {
		return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
	},

	_move: function( direction, filter, event ) {
		var next;
		if ( this.active ) {
			if ( direction === "first" || direction === "last" ) {
				next = this.active
					[ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
					.eq( -1 );
			} else {
				next = this.active
					[ direction + "All" ]( ".ui-menu-item" )
					.eq( 0 );
			}
		}
		if ( !next || !next.length || !this.active ) {
			next = this.activeMenu.find( this.options.items )[ filter ]();
		}

		this.focus( event, next );
	},

	nextPage: function( event ) {
		var item, base, height;

		if ( !this.active ) {
			this.next( event );
			return;
		}
		if ( this.isLastItem() ) {
			return;
		}
		if ( this._hasScroll() ) {
			base = this.active.offset().top;
			height = this.element.height();
			this.active.nextAll( ".ui-menu-item" ).each( function() {
				item = $( this );
				return item.offset().top - base - height < 0;
			} );

			this.focus( event, item );
		} else {
			this.focus( event, this.activeMenu.find( this.options.items )
				[ !this.active ? "first" : "last" ]() );
		}
	},

	previousPage: function( event ) {
		var item, base, height;
		if ( !this.active ) {
			this.next( event );
			return;
		}
		if ( this.isFirstItem() ) {
			return;
		}
		if ( this._hasScroll() ) {
			base = this.active.offset().top;
			height = this.element.height();
			this.active.prevAll( ".ui-menu-item" ).each( function() {
				item = $( this );
				return item.offset().top - base + height > 0;
			} );

			this.focus( event, item );
		} else {
			this.focus( event, this.activeMenu.find( this.options.items ).first() );
		}
	},

	_hasScroll: function() {
		return this.element.outerHeight() < this.element.prop( "scrollHeight" );
	},

	select: function( event ) {

		// TODO: It should never be possible to not have an active item at this
		// point, but the tests don't trigger mouseenter before click.
		this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
		var ui = { item: this.active };
		if ( !this.active.has( ".ui-menu" ).length ) {
			this.collapseAll( event, true );
		}
		this._trigger( "select", event, ui );
	},

	_filterMenuItems: function( character ) {
		var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ),
			regex = new RegExp( "^" + escapedCharacter, "i" );

		return this.activeMenu
			.find( this.options.items )

				// Only match on items, not dividers or other content (#10571)
				.filter( ".ui-menu-item" )
					.filter( function() {
						return regex.test(
							$.trim( $( this ).children( ".ui-menu-item-wrapper" ).text() ) );
					} );
	}
} );


/*!
 * jQuery UI Autocomplete 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Autocomplete
//>>group: Widgets
//>>description: Lists suggested words as the user is typing.
//>>docs: http://api.jqueryui.com/autocomplete/
//>>demos: http://jqueryui.com/autocomplete/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/autocomplete.css
//>>css.theme: ../../themes/base/theme.css



$.widget( "ui.autocomplete", {
	version: "1.12.1",
	defaultElement: "<input>",
	options: {
		appendTo: null,
		autoFocus: false,
		delay: 300,
		minLength: 1,
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},
		source: null,

		// Callbacks
		change: null,
		close: null,
		focus: null,
		open: null,
		response: null,
		search: null,
		select: null
	},

	requestIndex: 0,
	pending: 0,

	_create: function() {

		// Some browsers only repeat keydown events, not keypress events,
		// so we use the suppressKeyPress flag to determine if we've already
		// handled the keydown event. #7269
		// Unfortunately the code for & in keypress is the same as the up arrow,
		// so we use the suppressKeyPressRepeat flag to avoid handling keypress
		// events when we know the keydown event was used to modify the
		// search term. #7799
		var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
			nodeName = this.element[ 0 ].nodeName.toLowerCase(),
			isTextarea = nodeName === "textarea",
			isInput = nodeName === "input";

		// Textareas are always multi-line
		// Inputs are always single-line, even if inside a contentEditable element
		// IE also treats inputs as contentEditable
		// All other element types are determined by whether or not they're contentEditable
		this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );

		this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
		this.isNewMenu = true;

		this._addClass( "ui-autocomplete-input" );
		this.element.attr( "autocomplete", "off" );

		this._on( this.element, {
			keydown: function( event ) {
				if ( this.element.prop( "readOnly" ) ) {
					suppressKeyPress = true;
					suppressInput = true;
					suppressKeyPressRepeat = true;
					return;
				}

				suppressKeyPress = false;
				suppressInput = false;
				suppressKeyPressRepeat = false;
				var keyCode = $.ui.keyCode;
				switch ( event.keyCode ) {
				case keyCode.PAGE_UP:
					suppressKeyPress = true;
					this._move( "previousPage", event );
					break;
				case keyCode.PAGE_DOWN:
					suppressKeyPress = true;
					this._move( "nextPage", event );
					break;
				case keyCode.UP:
					suppressKeyPress = true;
					this._keyEvent( "previous", event );
					break;
				case keyCode.DOWN:
					suppressKeyPress = true;
					this._keyEvent( "next", event );
					break;
				case keyCode.ENTER:

					// when menu is open and has focus
					if ( this.menu.active ) {

						// #6055 - Opera still allows the keypress to occur
						// which causes forms to submit
						suppressKeyPress = true;
						event.preventDefault();
						this.menu.select( event );
					}
					break;
				case keyCode.TAB:
					if ( this.menu.active ) {
						this.menu.select( event );
					}
					break;
				case keyCode.ESCAPE:
					if ( this.menu.element.is( ":visible" ) ) {
						if ( !this.isMultiLine ) {
							this._value( this.term );
						}
						this.close( event );

						// Different browsers have different default behavior for escape
						// Single press can mean undo or clear
						// Double press in IE means clear the whole form
						event.preventDefault();
					}
					break;
				default:
					suppressKeyPressRepeat = true;

					// search timeout should be triggered before the input value is changed
					this._searchTimeout( event );
					break;
				}
			},
			keypress: function( event ) {
				if ( suppressKeyPress ) {
					suppressKeyPress = false;
					if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
						event.preventDefault();
					}
					return;
				}
				if ( suppressKeyPressRepeat ) {
					return;
				}

				// Replicate some key handlers to allow them to repeat in Firefox and Opera
				var keyCode = $.ui.keyCode;
				switch ( event.keyCode ) {
				case keyCode.PAGE_UP:
					this._move( "previousPage", event );
					break;
				case keyCode.PAGE_DOWN:
					this._move( "nextPage", event );
					break;
				case keyCode.UP:
					this._keyEvent( "previous", event );
					break;
				case keyCode.DOWN:
					this._keyEvent( "next", event );
					break;
				}
			},
			input: function( event ) {
				if ( suppressInput ) {
					suppressInput = false;
					event.preventDefault();
					return;
				}
				this._searchTimeout( event );
			},
			focus: function() {
				this.selectedItem = null;
				this.previous = this._value();
			},
			blur: function( event ) {
				if ( this.cancelBlur ) {
					delete this.cancelBlur;
					return;
				}

				clearTimeout( this.searching );
				this.close( event );
				this._change( event );
			}
		} );

		this._initSource();
		this.menu = $( "<ul>" )
			.appendTo( this._appendTo() )
			.menu( {

				// disable ARIA support, the live region takes care of that
				role: null
			} )
			.hide()
			.menu( "instance" );

		this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
		this._on( this.menu.element, {
			mousedown: function( event ) {

				// prevent moving focus out of the text field
				event.preventDefault();

				// IE doesn't prevent moving focus even with event.preventDefault()
				// so we set a flag to know when we should ignore the blur event
				this.cancelBlur = true;
				this._delay( function() {
					delete this.cancelBlur;

					// Support: IE 8 only
					// Right clicking a menu item or selecting text from the menu items will
					// result in focus moving out of the input. However, we've already received
					// and ignored the blur event because of the cancelBlur flag set above. So
					// we restore focus to ensure that the menu closes properly based on the user's
					// next actions.
					if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
						this.element.trigger( "focus" );
					}
				} );
			},
			menufocus: function( event, ui ) {
				var label, item;

				// support: Firefox
				// Prevent accidental activation of menu items in Firefox (#7024 #9118)
				if ( this.isNewMenu ) {
					this.isNewMenu = false;
					if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
						this.menu.blur();

						this.document.one( "mousemove", function() {
							$( event.target ).trigger( event.originalEvent );
						} );

						return;
					}
				}

				item = ui.item.data( "ui-autocomplete-item" );
				if ( false !== this._trigger( "focus", event, { item: item } ) ) {

					// use value to match what will end up in the input, if it was a key event
					if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
						this._value( item.value );
					}
				}

				// Announce the value in the liveRegion
				label = ui.item.attr( "aria-label" ) || item.value;
				if ( label && $.trim( label ).length ) {
					this.liveRegion.children().hide();
					$( "<div>" ).text( label ).appendTo( this.liveRegion );
				}
			},
			menuselect: function( event, ui ) {
				var item = ui.item.data( "ui-autocomplete-item" ),
					previous = this.previous;

				// Only trigger when focus was lost (click on menu)
				if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
					this.element.trigger( "focus" );
					this.previous = previous;

					// #6109 - IE triggers two focus events and the second
					// is asynchronous, so we need to reset the previous
					// term synchronously and asynchronously :-(
					this._delay( function() {
						this.previous = previous;
						this.selectedItem = item;
					} );
				}

				if ( false !== this._trigger( "select", event, { item: item } ) ) {
					this._value( item.value );
				}

				// reset the term after the select event
				// this allows custom select handling to work properly
				this.term = this._value();

				this.close( event );
				this.selectedItem = item;
			}
		} );

		this.liveRegion = $( "<div>", {
			role: "status",
			"aria-live": "assertive",
			"aria-relevant": "additions"
		} )
			.appendTo( this.document[ 0 ].body );

		this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );

		// Turning off autocomplete prevents the browser from remembering the
		// value when navigating through history, so we re-enable autocomplete
		// if the page is unloaded before the widget is destroyed. #7790
		this._on( this.window, {
			beforeunload: function() {
				this.element.removeAttr( "autocomplete" );
			}
		} );
	},

	_destroy: function() {
		clearTimeout( this.searching );
		this.element.removeAttr( "autocomplete" );
		this.menu.element.remove();
		this.liveRegion.remove();
	},

	_setOption: function( key, value ) {
		this._super( key, value );
		if ( key === "source" ) {
			this._initSource();
		}
		if ( key === "appendTo" ) {
			this.menu.element.appendTo( this._appendTo() );
		}
		if ( key === "disabled" && value && this.xhr ) {
			this.xhr.abort();
		}
	},

	_isEventTargetInWidget: function( event ) {
		var menuElement = this.menu.element[ 0 ];

		return event.target === this.element[ 0 ] ||
			event.target === menuElement ||
			$.contains( menuElement, event.target );
	},

	_closeOnClickOutside: function( event ) {
		if ( !this._isEventTargetInWidget( event ) ) {
			this.close();
		}
	},

	_appendTo: function() {
		var element = this.options.appendTo;

		if ( element ) {
			element = element.jquery || element.nodeType ?
				$( element ) :
				this.document.find( element ).eq( 0 );
		}

		if ( !element || !element[ 0 ] ) {
			element = this.element.closest( ".ui-front, dialog" );
		}

		if ( !element.length ) {
			element = this.document[ 0 ].body;
		}

		return element;
	},

	_initSource: function() {
		var array, url,
			that = this;
		if ( $.isArray( this.options.source ) ) {
			array = this.options.source;
			this.source = function( request, response ) {
				response( $.ui.autocomplete.filter( array, request.term ) );
			};
		} else if ( typeof this.options.source === "string" ) {
			url = this.options.source;
			this.source = function( request, response ) {
				if ( that.xhr ) {
					that.xhr.abort();
				}
				that.xhr = $.ajax( {
					url: url,
					data: request,
					dataType: "json",
					success: function( data ) {
						response( data );
					},
					error: function() {
						response( [] );
					}
				} );
			};
		} else {
			this.source = this.options.source;
		}
	},

	_searchTimeout: function( event ) {
		clearTimeout( this.searching );
		this.searching = this._delay( function() {

			// Search if the value has changed, or if the user retypes the same value (see #7434)
			var equalValues = this.term === this._value(),
				menuVisible = this.menu.element.is( ":visible" ),
				modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;

			if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
				this.selectedItem = null;
				this.search( null, event );
			}
		}, this.options.delay );
	},

	search: function( value, event ) {
		value = value != null ? value : this._value();

		// Always save the actual value, not the one passed as an argument
		this.term = this._value();

		if ( value.length < this.options.minLength ) {
			return this.close( event );
		}

		if ( this._trigger( "search", event ) === false ) {
			return;
		}

		return this._search( value );
	},

	_search: function( value ) {
		this.pending++;
		this._addClass( "ui-autocomplete-loading" );
		this.cancelSearch = false;

		this.source( { term: value }, this._response() );
	},

	_response: function() {
		var index = ++this.requestIndex;

		return $.proxy( function( content ) {
			if ( index === this.requestIndex ) {
				this.__response( content );
			}

			this.pending--;
			if ( !this.pending ) {
				this._removeClass( "ui-autocomplete-loading" );
			}
		}, this );
	},

	__response: function( content ) {
		if ( content ) {
			content = this._normalize( content );
		}
		this._trigger( "response", null, { content: content } );
		if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
			this._suggest( content );
			this._trigger( "open" );
		} else {

			// use ._close() instead of .close() so we don't cancel future searches
			this._close();
		}
	},

	close: function( event ) {
		this.cancelSearch = true;
		this._close( event );
	},

	_close: function( event ) {

		// Remove the handler that closes the menu on outside clicks
		this._off( this.document, "mousedown" );

		if ( this.menu.element.is( ":visible" ) ) {
			this.menu.element.hide();
			this.menu.blur();
			this.isNewMenu = true;
			this._trigger( "close", event );
		}
	},

	_change: function( event ) {
		if ( this.previous !== this._value() ) {
			this._trigger( "change", event, { item: this.selectedItem } );
		}
	},

	_normalize: function( items ) {

		// assume all items have the right format when the first item is complete
		if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
			return items;
		}
		return $.map( items, function( item ) {
			if ( typeof item === "string" ) {
				return {
					label: item,
					value: item
				};
			}
			return $.extend( {}, item, {
				label: item.label || item.value,
				value: item.value || item.label
			} );
		} );
	},

	_suggest: function( items ) {
		var ul = this.menu.element.empty();
		this._renderMenu( ul, items );
		this.isNewMenu = true;
		this.menu.refresh();

		// Size and position menu
		ul.show();
		this._resizeMenu();
		ul.position( $.extend( {
			of: this.element
		}, this.options.position ) );

		if ( this.options.autoFocus ) {
			this.menu.next();
		}

		// Listen for interactions outside of the widget (#6642)
		this._on( this.document, {
			mousedown: "_closeOnClickOutside"
		} );
	},

	_resizeMenu: function() {
		var ul = this.menu.element;
		ul.outerWidth( Math.max(

			// Firefox wraps long text (possibly a rounding bug)
			// so we add 1px to avoid the wrapping (#7513)
			ul.width( "" ).outerWidth() + 1,
			this.element.outerWidth()
		) );
	},

	_renderMenu: function( ul, items ) {
		var that = this;
		$.each( items, function( index, item ) {
			that._renderItemData( ul, item );
		} );
	},

	_renderItemData: function( ul, item ) {
		return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
	},

	_renderItem: function( ul, item ) {
		return $( "<li>" )
			.append( $( "<div>" ).text( item.label ) )
			.appendTo( ul );
	},

	_move: function( direction, event ) {
		if ( !this.menu.element.is( ":visible" ) ) {
			this.search( null, event );
			return;
		}
		if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
				this.menu.isLastItem() && /^next/.test( direction ) ) {

			if ( !this.isMultiLine ) {
				this._value( this.term );
			}

			this.menu.blur();
			return;
		}
		this.menu[ direction ]( event );
	},

	widget: function() {
		return this.menu.element;
	},

	_value: function() {
		return this.valueMethod.apply( this.element, arguments );
	},

	_keyEvent: function( keyEvent, event ) {
		if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
			this._move( keyEvent, event );

			// Prevents moving cursor to beginning/end of the text field in some browsers
			event.preventDefault();
		}
	},

	// Support: Chrome <=50
	// We should be able to just use this.element.prop( "isContentEditable" )
	// but hidden elements always report false in Chrome.
	// https://code.google.com/p/chromium/issues/detail?id=313082
	_isContentEditable: function( element ) {
		if ( !element.length ) {
			return false;
		}

		var editable = element.prop( "contentEditable" );

		if ( editable === "inherit" ) {
		  return this._isContentEditable( element.parent() );
		}

		return editable === "true";
	}
} );

$.extend( $.ui.autocomplete, {
	escapeRegex: function( value ) {
		return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
	},
	filter: function( array, term ) {
		var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
		return $.grep( array, function( value ) {
			return matcher.test( value.label || value.value || value );
		} );
	}
} );

// Live region extension, adding a `messages` option
// NOTE: This is an experimental API. We are still investigating
// a full solution for string manipulation and internationalization.
$.widget( "ui.autocomplete", $.ui.autocomplete, {
	options: {
		messages: {
			noResults: "No search results.",
			results: function( amount ) {
				return amount + ( amount > 1 ? " results are" : " result is" ) +
					" available, use up and down arrow keys to navigate.";
			}
		}
	},

	__response: function( content ) {
		var message;
		this._superApply( arguments );
		if ( this.options.disabled || this.cancelSearch ) {
			return;
		}
		if ( content && content.length ) {
			message = this.options.messages.results( content.length );
		} else {
			message = this.options.messages.noResults;
		}
		this.liveRegion.children().hide();
		$( "<div>" ).text( message ).appendTo( this.liveRegion );
	}
} );

var widgetsAutocomplete = $.ui.autocomplete;


/*!
 * jQuery UI Controlgroup 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Controlgroup
//>>group: Widgets
//>>description: Visually groups form control widgets
//>>docs: http://api.jqueryui.com/controlgroup/
//>>demos: http://jqueryui.com/controlgroup/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/controlgroup.css
//>>css.theme: ../../themes/base/theme.css


var controlgroupCornerRegex = /ui-corner-([a-z]){2,6}/g;

var widgetsControlgroup = $.widget( "ui.controlgroup", {
	version: "1.12.1",
	defaultElement: "<div>",
	options: {
		direction: "horizontal",
		disabled: null,
		onlyVisible: true,
		items: {
			"button": "input[type=button], input[type=submit], input[type=reset], button, a",
			"controlgroupLabel": ".ui-controlgroup-label",
			"checkboxradio": "input[type='checkbox'], input[type='radio']",
			"selectmenu": "select",
			"spinner": ".ui-spinner-input"
		}
	},

	_create: function() {
		this._enhance();
	},

	// To support the enhanced option in jQuery Mobile, we isolate DOM manipulation
	_enhance: function() {
		this.element.attr( "role", "toolbar" );
		this.refresh();
	},

	_destroy: function() {
		this._callChildMethod( "destroy" );
		this.childWidgets.removeData( "ui-controlgroup-data" );
		this.element.removeAttr( "role" );
		if ( this.options.items.controlgroupLabel ) {
			this.element
				.find( this.options.items.controlgroupLabel )
				.find( ".ui-controlgroup-label-contents" )
				.contents().unwrap();
		}
	},

	_initWidgets: function() {
		var that = this,
			childWidgets = [];

		// First we iterate over each of the items options
		$.each( this.options.items, function( widget, selector ) {
			var labels;
			var options = {};

			// Make sure the widget has a selector set
			if ( !selector ) {
				return;
			}

			if ( widget === "controlgroupLabel" ) {
				labels = that.element.find( selector );
				labels.each( function() {
					var element = $( this );

					if ( element.children( ".ui-controlgroup-label-contents" ).length ) {
						return;
					}
					element.contents()
						.wrapAll( "<span class='ui-controlgroup-label-contents'></span>" );
				} );
				that._addClass( labels, null, "ui-widget ui-widget-content ui-state-default" );
				childWidgets = childWidgets.concat( labels.get() );
				return;
			}

			// Make sure the widget actually exists
			if ( !$.fn[ widget ] ) {
				return;
			}

			// We assume everything is in the middle to start because we can't determine
			// first / last elements until all enhancments are done.
			if ( that[ "_" + widget + "Options" ] ) {
				options = that[ "_" + widget + "Options" ]( "middle" );
			} else {
				options = { classes: {} };
			}

			// Find instances of this widget inside controlgroup and init them
			that.element
				.find( selector )
				.each( function() {
					var element = $( this );
					var instance = element[ widget ]( "instance" );

					// We need to clone the default options for this type of widget to avoid
					// polluting the variable options which has a wider scope than a single widget.
					var instanceOptions = $.widget.extend( {}, options );

					// If the button is the child of a spinner ignore it
					// TODO: Find a more generic solution
					if ( widget === "button" && element.parent( ".ui-spinner" ).length ) {
						return;
					}

					// Create the widget if it doesn't exist
					if ( !instance ) {
						instance = element[ widget ]()[ widget ]( "instance" );
					}
					if ( instance ) {
						instanceOptions.classes =
							that._resolveClassesValues( instanceOptions.classes, instance );
					}
					element[ widget ]( instanceOptions );

					// Store an instance of the controlgroup to be able to reference
					// from the outermost element for changing options and refresh
					var widgetElement = element[ widget ]( "widget" );
					$.data( widgetElement[ 0 ], "ui-controlgroup-data",
						instance ? instance : element[ widget ]( "instance" ) );

					childWidgets.push( widgetElement[ 0 ] );
				} );
		} );

		this.childWidgets = $( $.unique( childWidgets ) );
		this._addClass( this.childWidgets, "ui-controlgroup-item" );
	},

	_callChildMethod: function( method ) {
		this.childWidgets.each( function() {
			var element = $( this ),
				data = element.data( "ui-controlgroup-data" );
			if ( data && data[ method ] ) {
				data[ method ]();
			}
		} );
	},

	_updateCornerClass: function( element, position ) {
		var remove = "ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all";
		var add = this._buildSimpleOptions( position, "label" ).classes.label;

		this._removeClass( element, null, remove );
		this._addClass( element, null, add );
	},

	_buildSimpleOptions: function( position, key ) {
		var direction = this.options.direction === "vertical";
		var result = {
			classes: {}
		};
		result.classes[ key ] = {
			"middle": "",
			"first": "ui-corner-" + ( direction ? "top" : "left" ),
			"last": "ui-corner-" + ( direction ? "bottom" : "right" ),
			"only": "ui-corner-all"
		}[ position ];

		return result;
	},

	_spinnerOptions: function( position ) {
		var options = this._buildSimpleOptions( position, "ui-spinner" );

		options.classes[ "ui-spinner-up" ] = "";
		options.classes[ "ui-spinner-down" ] = "";

		return options;
	},

	_buttonOptions: function( position ) {
		return this._buildSimpleOptions( position, "ui-button" );
	},

	_checkboxradioOptions: function( position ) {
		return this._buildSimpleOptions( position, "ui-checkboxradio-label" );
	},

	_selectmenuOptions: function( position ) {
		var direction = this.options.direction === "vertical";
		return {
			width: direction ? "auto" : false,
			classes: {
				middle: {
					"ui-selectmenu-button-open": "",
					"ui-selectmenu-button-closed": ""
				},
				first: {
					"ui-selectmenu-button-open": "ui-corner-" + ( direction ? "top" : "tl" ),
					"ui-selectmenu-button-closed": "ui-corner-" + ( direction ? "top" : "left" )
				},
				last: {
					"ui-selectmenu-button-open": direction ? "" : "ui-corner-tr",
					"ui-selectmenu-button-closed": "ui-corner-" + ( direction ? "bottom" : "right" )
				},
				only: {
					"ui-selectmenu-button-open": "ui-corner-top",
					"ui-selectmenu-button-closed": "ui-corner-all"
				}

			}[ position ]
		};
	},

	_resolveClassesValues: function( classes, instance ) {
		var result = {};
		$.each( classes, function( key ) {
			var current = instance.options.classes[ key ] || "";
			current = $.trim( current.replace( controlgroupCornerRegex, "" ) );
			result[ key ] = ( current + " " + classes[ key ] ).replace( /\s+/g, " " );
		} );
		return result;
	},

	_setOption: function( key, value ) {
		if ( key === "direction" ) {
			this._removeClass( "ui-controlgroup-" + this.options.direction );
		}

		this._super( key, value );
		if ( key === "disabled" ) {
			this._callChildMethod( value ? "disable" : "enable" );
			return;
		}

		this.refresh();
	},

	refresh: function() {
		var children,
			that = this;

		this._addClass( "ui-controlgroup ui-controlgroup-" + this.options.direction );

		if ( this.options.direction === "horizontal" ) {
			this._addClass( null, "ui-helper-clearfix" );
		}
		this._initWidgets();

		children = this.childWidgets;

		// We filter here because we need to track all childWidgets not just the visible ones
		if ( this.options.onlyVisible ) {
			children = children.filter( ":visible" );
		}

		if ( children.length ) {

			// We do this last because we need to make sure all enhancment is done
			// before determining first and last
			$.each( [ "first", "last" ], function( index, value ) {
				var instance = children[ value ]().data( "ui-controlgroup-data" );

				if ( instance && that[ "_" + instance.widgetName + "Options" ] ) {
					var options = that[ "_" + instance.widgetName + "Options" ](
						children.length === 1 ? "only" : value
					);
					options.classes = that._resolveClassesValues( options.classes, instance );
					instance.element[ instance.widgetName ]( options );
				} else {
					that._updateCornerClass( children[ value ](), value );
				}
			} );

			// Finally call the refresh method on each of the child widgets.
			this._callChildMethod( "refresh" );
		}
	}
} );

/*!
 * jQuery UI Checkboxradio 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Checkboxradio
//>>group: Widgets
//>>description: Enhances a form with multiple themeable checkboxes or radio buttons.
//>>docs: http://api.jqueryui.com/checkboxradio/
//>>demos: http://jqueryui.com/checkboxradio/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/button.css
//>>css.structure: ../../themes/base/checkboxradio.css
//>>css.theme: ../../themes/base/theme.css



$.widget( "ui.checkboxradio", [ $.ui.formResetMixin, {
	version: "1.12.1",
	options: {
		disabled: null,
		label: null,
		icon: true,
		classes: {
			"ui-checkboxradio-label": "ui-corner-all",
			"ui-checkboxradio-icon": "ui-corner-all"
		}
	},

	_getCreateOptions: function() {
		var disabled, labels;
		var that = this;
		var options = this._super() || {};

		// We read the type here, because it makes more sense to throw a element type error first,
		// rather then the error for lack of a label. Often if its the wrong type, it
		// won't have a label (e.g. calling on a div, btn, etc)
		this._readType();

		labels = this.element.labels();

		// If there are multiple labels, use the last one
		this.label = $( labels[ labels.length - 1 ] );
		if ( !this.label.length ) {
			$.error( "No label found for checkboxradio widget" );
		}

		this.originalLabel = "";

		// We need to get the label text but this may also need to make sure it does not contain the
		// input itself.
		this.label.contents().not( this.element[ 0 ] ).each( function() {

			// The label contents could be text, html, or a mix. We concat each element to get a
			// string representation of the label, without the input as part of it.
			that.originalLabel += this.nodeType === 3 ? $( this ).text() : this.outerHTML;
		} );

		// Set the label option if we found label text
		if ( this.originalLabel ) {
			options.label = this.originalLabel;
		}

		disabled = this.element[ 0 ].disabled;
		if ( disabled != null ) {
			options.disabled = disabled;
		}
		return options;
	},

	_create: function() {
		var checked = this.element[ 0 ].checked;

		this._bindFormResetHandler();

		if ( this.options.disabled == null ) {
			this.options.disabled = this.element[ 0 ].disabled;
		}

		this._setOption( "disabled", this.options.disabled );
		this._addClass( "ui-checkboxradio", "ui-helper-hidden-accessible" );
		this._addClass( this.label, "ui-checkboxradio-label", "ui-button ui-widget" );

		if ( this.type === "radio" ) {
			this._addClass( this.label, "ui-checkboxradio-radio-label" );
		}

		if ( this.options.label && this.options.label !== this.originalLabel ) {
			this._updateLabel();
		} else if ( this.originalLabel ) {
			this.options.label = this.originalLabel;
		}

		this._enhance();

		if ( checked ) {
			this._addClass( this.label, "ui-checkboxradio-checked", "ui-state-active" );
			if ( this.icon ) {
				this._addClass( this.icon, null, "ui-state-hover" );
			}
		}

		this._on( {
			change: "_toggleClasses",
			focus: function() {
				this._addClass( this.label, null, "ui-state-focus ui-visual-focus" );
			},
			blur: function() {
				this._removeClass( this.label, null, "ui-state-focus ui-visual-focus" );
			}
		} );
	},

	_readType: function() {
		var nodeName = this.element[ 0 ].nodeName.toLowerCase();
		this.type = this.element[ 0 ].type;
		if ( nodeName !== "input" || !/radio|checkbox/.test( this.type ) ) {
			$.error( "Can't create checkboxradio on element.nodeName=" + nodeName +
				" and element.type=" + this.type );
		}
	},

	// Support jQuery Mobile enhanced option
	_enhance: function() {
		this._updateIcon( this.element[ 0 ].checked );
	},

	widget: function() {
		return this.label;
	},

	_getRadioGroup: function() {
		var group;
		var name = this.element[ 0 ].name;
		var nameSelector = "input[name='" + $.ui.escapeSelector( name ) + "']";

		if ( !name ) {
			return $( [] );
		}

		if ( this.form.length ) {
			group = $( this.form[ 0 ].elements ).filter( nameSelector );
		} else {

			// Not inside a form, check all inputs that also are not inside a form
			group = $( nameSelector ).filter( function() {
				return $( this ).form().length === 0;
			} );
		}

		return group.not( this.element );
	},

	_toggleClasses: function() {
		var checked = this.element[ 0 ].checked;
		this._toggleClass( this.label, "ui-checkboxradio-checked", "ui-state-active", checked );

		if ( this.options.icon && this.type === "checkbox" ) {
			this._toggleClass( this.icon, null, "ui-icon-check ui-state-checked", checked )
				._toggleClass( this.icon, null, "ui-icon-blank", !checked );
		}

		if ( this.type === "radio" ) {
			this._getRadioGroup()
				.each( function() {
					var instance = $( this ).checkboxradio( "instance" );

					if ( instance ) {
						instance._removeClass( instance.label,
							"ui-checkboxradio-checked", "ui-state-active" );
					}
				} );
		}
	},

	_destroy: function() {
		this._unbindFormResetHandler();

		if ( this.icon ) {
			this.icon.remove();
			this.iconSpace.remove();
		}
	},

	_setOption: function( key, value ) {

		// We don't allow the value to be set to nothing
		if ( key === "label" && !value ) {
			return;
		}

		this._super( key, value );

		if ( key === "disabled" ) {
			this._toggleClass( this.label, null, "ui-state-disabled", value );
			this.element[ 0 ].disabled = value;

			// Don't refresh when setting disabled
			return;
		}
		this.refresh();
	},

	_updateIcon: function( checked ) {
		var toAdd = "ui-icon ui-icon-background ";

		if ( this.options.icon ) {
			if ( !this.icon ) {
				this.icon = $( "<span>" );
				this.iconSpace = $( "<span> </span>" );
				this._addClass( this.iconSpace, "ui-checkboxradio-icon-space" );
			}

			if ( this.type === "checkbox" ) {
				toAdd += checked ? "ui-icon-check ui-state-checked" : "ui-icon-blank";
				this._removeClass( this.icon, null, checked ? "ui-icon-blank" : "ui-icon-check" );
			} else {
				toAdd += "ui-icon-blank";
			}
			this._addClass( this.icon, "ui-checkboxradio-icon", toAdd );
			if ( !checked ) {
				this._removeClass( this.icon, null, "ui-icon-check ui-state-checked" );
			}
			this.icon.prependTo( this.label ).after( this.iconSpace );
		} else if ( this.icon !== undefined ) {
			this.icon.remove();
			this.iconSpace.remove();
			delete this.icon;
		}
	},

	_updateLabel: function() {

		// Remove the contents of the label ( minus the icon, icon space, and input )
		var contents = this.label.contents().not( this.element[ 0 ] );
		if ( this.icon ) {
			contents = contents.not( this.icon[ 0 ] );
		}
		if ( this.iconSpace ) {
			contents = contents.not( this.iconSpace[ 0 ] );
		}
		contents.remove();

		this.label.append( this.options.label );
	},

	refresh: function() {
		var checked = this.element[ 0 ].checked,
			isDisabled = this.element[ 0 ].disabled;

		this._updateIcon( checked );
		this._toggleClass( this.label, "ui-checkboxradio-checked", "ui-state-active", checked );
		if ( this.options.label !== null ) {
			this._updateLabel();
		}

		if ( isDisabled !== this.options.disabled ) {
			this._setOptions( { "disabled": isDisabled } );
		}
	}

} ] );

var widgetsCheckboxradio = $.ui.checkboxradio;


/*!
 * jQuery UI Button 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Button
//>>group: Widgets
//>>description: Enhances a form with themeable buttons.
//>>docs: http://api.jqueryui.com/button/
//>>demos: http://jqueryui.com/button/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/button.css
//>>css.theme: ../../themes/base/theme.css



$.widget( "ui.button", {
	version: "1.12.1",
	defaultElement: "<button>",
	options: {
		classes: {
			"ui-button": "ui-corner-all"
		},
		disabled: null,
		icon: null,
		iconPosition: "beginning",
		label: null,
		showLabel: true
	},

	_getCreateOptions: function() {
		var disabled,

			// This is to support cases like in jQuery Mobile where the base widget does have
			// an implementation of _getCreateOptions
			options = this._super() || {};

		this.isInput = this.element.is( "input" );

		disabled = this.element[ 0 ].disabled;
		if ( disabled != null ) {
			options.disabled = disabled;
		}

		this.originalLabel = this.isInput ? this.element.val() : this.element.html();
		if ( this.originalLabel ) {
			options.label = this.originalLabel;
		}

		return options;
	},

	_create: function() {
		if ( !this.option.showLabel & !this.options.icon ) {
			this.options.showLabel = true;
		}

		// We have to check the option again here even though we did in _getCreateOptions,
		// because null may have been passed on init which would override what was set in
		// _getCreateOptions
		if ( this.options.disabled == null ) {
			this.options.disabled = this.element[ 0 ].disabled || false;
		}

		this.hasTitle = !!this.element.attr( "title" );

		// Check to see if the label needs to be set or if its already correct
		if ( this.options.label && this.options.label !== this.originalLabel ) {
			if ( this.isInput ) {
				this.element.val( this.options.label );
			} else {
				this.element.html( this.options.label );
			}
		}
		this._addClass( "ui-button", "ui-widget" );
		this._setOption( "disabled", this.options.disabled );
		this._enhance();

		if ( this.element.is( "a" ) ) {
			this._on( {
				"keyup": function( event ) {
					if ( event.keyCode === $.ui.keyCode.SPACE ) {
						event.preventDefault();

						// Support: PhantomJS <= 1.9, IE 8 Only
						// If a native click is available use it so we actually cause navigation
						// otherwise just trigger a click event
						if ( this.element[ 0 ].click ) {
							this.element[ 0 ].click();
						} else {
							this.element.trigger( "click" );
						}
					}
				}
			} );
		}
	},

	_enhance: function() {
		if ( !this.element.is( "button" ) ) {
			this.element.attr( "role", "button" );
		}

		if ( this.options.icon ) {
			this._updateIcon( "icon", this.options.icon );
			this._updateTooltip();
		}
	},

	_updateTooltip: function() {
		this.title = this.element.attr( "title" );

		if ( !this.options.showLabel && !this.title ) {
			this.element.attr( "title", this.options.label );
		}
	},

	_updateIcon: function( option, value ) {
		var icon = option !== "iconPosition",
			position = icon ? this.options.iconPosition : value,
			displayBlock = position === "top" || position === "bottom";

		// Create icon
		if ( !this.icon ) {
			this.icon = $( "<span>" );

			this._addClass( this.icon, "ui-button-icon", "ui-icon" );

			if ( !this.options.showLabel ) {
				this._addClass( "ui-button-icon-only" );
			}
		} else if ( icon ) {

			// If we are updating the icon remove the old icon class
			this._removeClass( this.icon, null, this.options.icon );
		}

		// If we are updating the icon add the new icon class
		if ( icon ) {
			this._addClass( this.icon, null, value );
		}

		this._attachIcon( position );

		// If the icon is on top or bottom we need to add the ui-widget-icon-block class and remove
		// the iconSpace if there is one.
		if ( displayBlock ) {
			this._addClass( this.icon, null, "ui-widget-icon-block" );
			if ( this.iconSpace ) {
				this.iconSpace.remove();
			}
		} else {

			// Position is beginning or end so remove the ui-widget-icon-block class and add the
			// space if it does not exist
			if ( !this.iconSpace ) {
				this.iconSpace = $( "<span> </span>" );
				this._addClass( this.iconSpace, "ui-button-icon-space" );
			}
			this._removeClass( this.icon, null, "ui-wiget-icon-block" );
			this._attachIconSpace( position );
		}
	},

	_destroy: function() {
		this.element.removeAttr( "role" );

		if ( this.icon ) {
			this.icon.remove();
		}
		if ( this.iconSpace ) {
			this.iconSpace.remove();
		}
		if ( !this.hasTitle ) {
			this.element.removeAttr( "title" );
		}
	},

	_attachIconSpace: function( iconPosition ) {
		this.icon[ /^(?:end|bottom)/.test( iconPosition ) ? "before" : "after" ]( this.iconSpace );
	},

	_attachIcon: function( iconPosition ) {
		this.element[ /^(?:end|bottom)/.test( iconPosition ) ? "append" : "prepend" ]( this.icon );
	},

	_setOptions: function( options ) {
		var newShowLabel = options.showLabel === undefined ?
				this.options.showLabel :
				options.showLabel,
			newIcon = options.icon === undefined ? this.options.icon : options.icon;

		if ( !newShowLabel && !newIcon ) {
			options.showLabel = true;
		}
		this._super( options );
	},

	_setOption: function( key, value ) {
		if ( key === "icon" ) {
			if ( value ) {
				this._updateIcon( key, value );
			} else if ( this.icon ) {
				this.icon.remove();
				if ( this.iconSpace ) {
					this.iconSpace.remove();
				}
			}
		}

		if ( key === "iconPosition" ) {
			this._updateIcon( key, value );
		}

		// Make sure we can't end up with a button that has neither text nor icon
		if ( key === "showLabel" ) {
				this._toggleClass( "ui-button-icon-only", null, !value );
				this._updateTooltip();
		}

		if ( key === "label" ) {
			if ( this.isInput ) {
				this.element.val( value );
			} else {

				// If there is an icon, append it, else nothing then append the value
				// this avoids removal of the icon when setting label text
				this.element.html( value );
				if ( this.icon ) {
					this._attachIcon( this.options.iconPosition );
					this._attachIconSpace( this.options.iconPosition );
				}
			}
		}

		this._super( key, value );

		if ( key === "disabled" ) {
			this._toggleClass( null, "ui-state-disabled", value );
			this.element[ 0 ].disabled = value;
			if ( value ) {
				this.element.blur();
			}
		}
	},

	refresh: function() {

		// Make sure to only check disabled if its an element that supports this otherwise
		// check for the disabled class to determine state
		var isDisabled = this.element.is( "input, button" ) ?
			this.element[ 0 ].disabled : this.element.hasClass( "ui-button-disabled" );

		if ( isDisabled !== this.options.disabled ) {
			this._setOptions( { disabled: isDisabled } );
		}

		this._updateTooltip();
	}
} );

// DEPRECATED
if ( $.uiBackCompat !== false ) {

	// Text and Icons options
	$.widget( "ui.button", $.ui.button, {
		options: {
			text: true,
			icons: {
				primary: null,
				secondary: null
			}
		},

		_create: function() {
			if ( this.options.showLabel && !this.options.text ) {
				this.options.showLabel = this.options.text;
			}
			if ( !this.options.showLabel && this.options.text ) {
				this.options.text = this.options.showLabel;
			}
			if ( !this.options.icon && ( this.options.icons.primary ||
					this.options.icons.secondary ) ) {
				if ( this.options.icons.primary ) {
					this.options.icon = this.options.icons.primary;
				} else {
					this.options.icon = this.options.icons.secondary;
					this.options.iconPosition = "end";
				}
			} else if ( this.options.icon ) {
				this.options.icons.primary = this.options.icon;
			}
			this._super();
		},

		_setOption: function( key, value ) {
			if ( key === "text" ) {
				this._super( "showLabel", value );
				return;
			}
			if ( key === "showLabel" ) {
				this.options.text = value;
			}
			if ( key === "icon" ) {
				this.options.icons.primary = value;
			}
			if ( key === "icons" ) {
				if ( value.primary ) {
					this._super( "icon", value.primary );
					this._super( "iconPosition", "beginning" );
				} else if ( value.secondary ) {
					this._super( "icon", value.secondary );
					this._super( "iconPosition", "end" );
				}
			}
			this._superApply( arguments );
		}
	} );

	$.fn.button = ( function( orig ) {
		return function() {
			if ( !this.length || ( this.length && this[ 0 ].tagName !== "INPUT" ) ||
					( this.length && this[ 0 ].tagName === "INPUT" && (
						this.attr( "type" ) !== "checkbox" && this.attr( "type" ) !== "radio"
					) ) ) {
				return orig.apply( this, arguments );
			}
			if ( !$.ui.checkboxradio ) {
				$.error( "Checkboxradio widget missing" );
			}
			if ( arguments.length === 0 ) {
				return this.checkboxradio( {
					"icon": false
				} );
			}
			return this.checkboxradio.apply( this, arguments );
		};
	} )( $.fn.button );

	$.fn.buttonset = function() {
		if ( !$.ui.controlgroup ) {
			$.error( "Controlgroup widget missing" );
		}
		if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" && arguments[ 2 ] ) {
			return this.controlgroup.apply( this,
				[ arguments[ 0 ], "items.button", arguments[ 2 ] ] );
		}
		if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" ) {
			return this.controlgroup.apply( this, [ arguments[ 0 ], "items.button" ] );
		}
		if ( typeof arguments[ 0 ] === "object" && arguments[ 0 ].items ) {
			arguments[ 0 ].items = {
				button: arguments[ 0 ].items
			};
		}
		return this.controlgroup.apply( this, arguments );
	};
}

var widgetsButton = $.ui.button;


// jscs:disable maximumLineLength
/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
/*!
 * jQuery UI Datepicker 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Datepicker
//>>group: Widgets
//>>description: Displays a calendar from an input or inline for selecting dates.
//>>docs: http://api.jqueryui.com/datepicker/
//>>demos: http://jqueryui.com/datepicker/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/datepicker.css
//>>css.theme: ../../themes/base/theme.css



$.extend( $.ui, { datepicker: { version: "1.12.1" } } );

var datepicker_instActive;

function datepicker_getZindex( elem ) {
	var position, value;
	while ( elem.length && elem[ 0 ] !== document ) {

		// Ignore z-index if position is set to a value where z-index is ignored by the browser
		// This makes behavior of this function consistent across browsers
		// WebKit always returns auto if the element is positioned
		position = elem.css( "position" );
		if ( position === "absolute" || position === "relative" || position === "fixed" ) {

			// IE returns 0 when zIndex is not specified
			// other browsers return a string
			// we ignore the case of nested elements with an explicit value of 0
			// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
			value = parseInt( elem.css( "zIndex" ), 10 );
			if ( !isNaN( value ) && value !== 0 ) {
				return value;
			}
		}
		elem = elem.parent();
	}

	return 0;
}
/* Date picker manager.
   Use the singleton instance of this class, $.datepicker, to interact with the date picker.
   Settings for (groups of) date pickers are maintained in an instance object,
   allowing multiple different settings on the same page. */

function Datepicker() {
	this._curInst = null; // The current instance in use
	this._keyEvent = false; // If the last event was a key event
	this._disabledInputs = []; // List of date picker inputs that have been disabled
	this._datepickerShowing = false; // True if the popup picker is showing , false if not
	this._inDialog = false; // True if showing within a "dialog", false if not
	this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
	this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
	this._appendClass = "ui-datepicker-append"; // The name of the append marker class
	this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
	this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
	this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
	this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
	this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
	this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
	this.regional = []; // Available regional settings, indexed by language code
	this.regional[ "" ] = { // Default regional settings
		closeText: "Done", // Display text for close link
		prevText: "Prev", // Display text for previous month link
		nextText: "Next", // Display text for next month link
		currentText: "Today", // Display text for current month link
		monthNames: [ "January","February","March","April","May","June",
			"July","August","September","October","November","December" ], // Names of months for drop-down and formatting
		monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], // For formatting
		dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], // For formatting
		dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], // For formatting
		dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ], // Column headings for days starting at Sunday
		weekHeader: "Wk", // Column header for week of the year
		dateFormat: "mm/dd/yy", // See format options on parseDate
		firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
		isRTL: false, // True if right-to-left language, false if left-to-right
		showMonthAfterYear: false, // True if the year select precedes month, false for month then year
		yearSuffix: "" // Additional text to append to the year in the month headers
	};
	this._defaults = { // Global defaults for all the date picker instances
		showOn: "focus", // "focus" for popup on focus,
			// "button" for trigger button, or "both" for either
		showAnim: "fadeIn", // Name of jQuery animation for popup
		showOptions: {}, // Options for enhanced animations
		defaultDate: null, // Used when field is blank: actual date,
			// +/-number for offset from today, null for today
		appendText: "", // Display text following the input box, e.g. showing the format
		buttonText: "...", // Text for trigger button
		buttonImage: "", // URL for trigger button image
		buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
		hideIfNoPrevNext: false, // True to hide next/previous month links
			// if not applicable, false to just disable them
		navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
		gotoCurrent: false, // True if today link goes back to current selection instead
		changeMonth: false, // True if month can be selected directly, false if only prev/next
		changeYear: false, // True if year can be selected directly, false if only prev/next
		yearRange: "c-10:c+10", // Range of years to display in drop-down,
			// either relative to today's year (-nn:+nn), relative to currently displayed year
			// (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
		showOtherMonths: false, // True to show dates in other months, false to leave blank
		selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
		showWeek: false, // True to show week of the year, false to not show it
		calculateWeek: this.iso8601Week, // How to calculate the week of the year,
			// takes a Date and returns the number of the week for it
		shortYearCutoff: "+10", // Short year values < this are in the current century,
			// > this are in the previous century,
			// string value starting with "+" for current year + value
		minDate: null, // The earliest selectable date, or null for no limit
		maxDate: null, // The latest selectable date, or null for no limit
		duration: "fast", // Duration of display/closure
		beforeShowDay: null, // Function that takes a date and returns an array with
			// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
			// [2] = cell title (optional), e.g. $.datepicker.noWeekends
		beforeShow: null, // Function that takes an input field and
			// returns a set of custom settings for the date picker
		onSelect: null, // Define a callback function when a date is selected
		onChangeMonthYear: null, // Define a callback function when the month or year is changed
		onClose: null, // Define a callback function when the datepicker is closed
		numberOfMonths: 1, // Number of months to show at a time
		showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
		stepMonths: 1, // Number of months to step back/forward
		stepBigMonths: 12, // Number of months to step back/forward for the big links
		altField: "", // Selector for an alternate field to store selected dates into
		altFormat: "", // The date format to use for the alternate field
		constrainInput: true, // The input is constrained by the current date format
		showButtonPanel: false, // True to show button panel, false to not show it
		autoSize: false, // True to size the input for the date format, false to leave as is
		disabled: false // The initial disabled state
	};
	$.extend( this._defaults, this.regional[ "" ] );
	this.regional.en = $.extend( true, {}, this.regional[ "" ] );
	this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en );
	this.dpDiv = datepicker_bindHover( $( "<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) );
}

$.extend( Datepicker.prototype, {
	/* Class name added to elements to indicate already configured with a date picker. */
	markerClassName: "hasDatepicker",

	//Keep track of the maximum number of rows displayed (see #7043)
	maxRows: 4,

	// TODO rename to "widget" when switching to widget factory
	_widgetDatepicker: function() {
		return this.dpDiv;
	},

	/* Override the default settings for all instances of the date picker.
	 * @param  settings  object - the new settings to use as defaults (anonymous object)
	 * @return the manager object
	 */
	setDefaults: function( settings ) {
		datepicker_extendRemove( this._defaults, settings || {} );
		return this;
	},

	/* Attach the date picker to a jQuery selection.
	 * @param  target	element - the target input field or division or span
	 * @param  settings  object - the new settings to use for this date picker instance (anonymous)
	 */
	_attachDatepicker: function( target, settings ) {
		var nodeName, inline, inst;
		nodeName = target.nodeName.toLowerCase();
		inline = ( nodeName === "div" || nodeName === "span" );
		if ( !target.id ) {
			this.uuid += 1;
			target.id = "dp" + this.uuid;
		}
		inst = this._newInst( $( target ), inline );
		inst.settings = $.extend( {}, settings || {} );
		if ( nodeName === "input" ) {
			this._connectDatepicker( target, inst );
		} else if ( inline ) {
			this._inlineDatepicker( target, inst );
		}
	},

	/* Create a new instance object. */
	_newInst: function( target, inline ) {
		var id = target[ 0 ].id.replace( /([^A-Za-z0-9_\-])/g, "\\\\$1" ); // escape jQuery meta chars
		return { id: id, input: target, // associated target
			selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
			drawMonth: 0, drawYear: 0, // month being drawn
			inline: inline, // is datepicker inline or not
			dpDiv: ( !inline ? this.dpDiv : // presentation div
			datepicker_bindHover( $( "<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) ) ) };
	},

	/* Attach the date picker to an input field. */
	_connectDatepicker: function( target, inst ) {
		var input = $( target );
		inst.append = $( [] );
		inst.trigger = $( [] );
		if ( input.hasClass( this.markerClassName ) ) {
			return;
		}
		this._attachments( input, inst );
		input.addClass( this.markerClassName ).on( "keydown", this._doKeyDown ).
			on( "keypress", this._doKeyPress ).on( "keyup", this._doKeyUp );
		this._autoSize( inst );
		$.data( target, "datepicker", inst );

		//If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
		if ( inst.settings.disabled ) {
			this._disableDatepicker( target );
		}
	},

	/* Make attachments based on settings. */
	_attachments: function( input, inst ) {
		var showOn, buttonText, buttonImage,
			appendText = this._get( inst, "appendText" ),
			isRTL = this._get( inst, "isRTL" );

		if ( inst.append ) {
			inst.append.remove();
		}
		if ( appendText ) {
			inst.append = $( "<span class='" + this._appendClass + "'>" + appendText + "</span>" );
			input[ isRTL ? "before" : "after" ]( inst.append );
		}

		input.off( "focus", this._showDatepicker );

		if ( inst.trigger ) {
			inst.trigger.remove();
		}

		showOn = this._get( inst, "showOn" );
		if ( showOn === "focus" || showOn === "both" ) { // pop-up date picker when in the marked field
			input.on( "focus", this._showDatepicker );
		}
		if ( showOn === "button" || showOn === "both" ) { // pop-up date picker when button clicked
			buttonText = this._get( inst, "buttonText" );
			buttonImage = this._get( inst, "buttonImage" );
			inst.trigger = $( this._get( inst, "buttonImageOnly" ) ?
				$( "<img/>" ).addClass( this._triggerClass ).
					attr( { src: buttonImage, alt: buttonText, title: buttonText } ) :
				$( "<button type='button'></button>" ).addClass( this._triggerClass ).
					html( !buttonImage ? buttonText : $( "<img/>" ).attr(
					{ src:buttonImage, alt:buttonText, title:buttonText } ) ) );
			input[ isRTL ? "before" : "after" ]( inst.trigger );
			inst.trigger.on( "click", function() {
				if ( $.datepicker._datepickerShowing && $.datepicker._lastInput === input[ 0 ] ) {
					$.datepicker._hideDatepicker();
				} else if ( $.datepicker._datepickerShowing && $.datepicker._lastInput !== input[ 0 ] ) {
					$.datepicker._hideDatepicker();
					$.datepicker._showDatepicker( input[ 0 ] );
				} else {
					$.datepicker._showDatepicker( input[ 0 ] );
				}
				return false;
			} );
		}
	},

	/* Apply the maximum length for the date format. */
	_autoSize: function( inst ) {
		if ( this._get( inst, "autoSize" ) && !inst.inline ) {
			var findMax, max, maxI, i,
				date = new Date( 2009, 12 - 1, 20 ), // Ensure double digits
				dateFormat = this._get( inst, "dateFormat" );

			if ( dateFormat.match( /[DM]/ ) ) {
				findMax = function( names ) {
					max = 0;
					maxI = 0;
					for ( i = 0; i < names.length; i++ ) {
						if ( names[ i ].length > max ) {
							max = names[ i ].length;
							maxI = i;
						}
					}
					return maxI;
				};
				date.setMonth( findMax( this._get( inst, ( dateFormat.match( /MM/ ) ?
					"monthNames" : "monthNamesShort" ) ) ) );
				date.setDate( findMax( this._get( inst, ( dateFormat.match( /DD/ ) ?
					"dayNames" : "dayNamesShort" ) ) ) + 20 - date.getDay() );
			}
			inst.input.attr( "size", this._formatDate( inst, date ).length );
		}
	},

	/* Attach an inline date picker to a div. */
	_inlineDatepicker: function( target, inst ) {
		var divSpan = $( target );
		if ( divSpan.hasClass( this.markerClassName ) ) {
			return;
		}
		divSpan.addClass( this.markerClassName ).append( inst.dpDiv );
		$.data( target, "datepicker", inst );
		this._setDate( inst, this._getDefaultDate( inst ), true );
		this._updateDatepicker( inst );
		this._updateAlternate( inst );

		//If disabled option is true, disable the datepicker before showing it (see ticket #5665)
		if ( inst.settings.disabled ) {
			this._disableDatepicker( target );
		}

		// Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
		// http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
		inst.dpDiv.css( "display", "block" );
	},

	/* Pop-up the date picker in a "dialog" box.
	 * @param  input element - ignored
	 * @param  date	string or Date - the initial date to display
	 * @param  onSelect  function - the function to call when a date is selected
	 * @param  settings  object - update the dialog date picker instance's settings (anonymous object)
	 * @param  pos int[2] - coordinates for the dialog's position within the screen or
	 *					event - with x/y coordinates or
	 *					leave empty for default (screen centre)
	 * @return the manager object
	 */
	_dialogDatepicker: function( input, date, onSelect, settings, pos ) {
		var id, browserWidth, browserHeight, scrollX, scrollY,
			inst = this._dialogInst; // internal instance

		if ( !inst ) {
			this.uuid += 1;
			id = "dp" + this.uuid;
			this._dialogInput = $( "<input type='text' id='" + id +
				"' style='position: absolute; top: -100px; width: 0px;'/>" );
			this._dialogInput.on( "keydown", this._doKeyDown );
			$( "body" ).append( this._dialogInput );
			inst = this._dialogInst = this._newInst( this._dialogInput, false );
			inst.settings = {};
			$.data( this._dialogInput[ 0 ], "datepicker", inst );
		}
		datepicker_extendRemove( inst.settings, settings || {} );
		date = ( date && date.constructor === Date ? this._formatDate( inst, date ) : date );
		this._dialogInput.val( date );

		this._pos = ( pos ? ( pos.length ? pos : [ pos.pageX, pos.pageY ] ) : null );
		if ( !this._pos ) {
			browserWidth = document.documentElement.clientWidth;
			browserHeight = document.documentElement.clientHeight;
			scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
			scrollY = document.documentElement.scrollTop || document.body.scrollTop;
			this._pos = // should use actual width/height below
				[ ( browserWidth / 2 ) - 100 + scrollX, ( browserHeight / 2 ) - 150 + scrollY ];
		}

		// Move input on screen for focus, but hidden behind dialog
		this._dialogInput.css( "left", ( this._pos[ 0 ] + 20 ) + "px" ).css( "top", this._pos[ 1 ] + "px" );
		inst.settings.onSelect = onSelect;
		this._inDialog = true;
		this.dpDiv.addClass( this._dialogClass );
		this._showDatepicker( this._dialogInput[ 0 ] );
		if ( $.blockUI ) {
			$.blockUI( this.dpDiv );
		}
		$.data( this._dialogInput[ 0 ], "datepicker", inst );
		return this;
	},

	/* Detach a datepicker from its control.
	 * @param  target	element - the target input field or division or span
	 */
	_destroyDatepicker: function( target ) {
		var nodeName,
			$target = $( target ),
			inst = $.data( target, "datepicker" );

		if ( !$target.hasClass( this.markerClassName ) ) {
			return;
		}

		nodeName = target.nodeName.toLowerCase();
		$.removeData( target, "datepicker" );
		if ( nodeName === "input" ) {
			inst.append.remove();
			inst.trigger.remove();
			$target.removeClass( this.markerClassName ).
				off( "focus", this._showDatepicker ).
				off( "keydown", this._doKeyDown ).
				off( "keypress", this._doKeyPress ).
				off( "keyup", this._doKeyUp );
		} else if ( nodeName === "div" || nodeName === "span" ) {
			$target.removeClass( this.markerClassName ).empty();
		}

		if ( datepicker_instActive === inst ) {
			datepicker_instActive = null;
		}
	},

	/* Enable the date picker to a jQuery selection.
	 * @param  target	element - the target input field or division or span
	 */
	_enableDatepicker: function( target ) {
		var nodeName, inline,
			$target = $( target ),
			inst = $.data( target, "datepicker" );

		if ( !$target.hasClass( this.markerClassName ) ) {
			return;
		}

		nodeName = target.nodeName.toLowerCase();
		if ( nodeName === "input" ) {
			target.disabled = false;
			inst.trigger.filter( "button" ).
				each( function() { this.disabled = false; } ).end().
				filter( "img" ).css( { opacity: "1.0", cursor: "" } );
		} else if ( nodeName === "div" || nodeName === "span" ) {
			inline = $target.children( "." + this._inlineClass );
			inline.children().removeClass( "ui-state-disabled" );
			inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
				prop( "disabled", false );
		}
		this._disabledInputs = $.map( this._disabledInputs,
			function( value ) { return ( value === target ? null : value ); } ); // delete entry
	},

	/* Disable the date picker to a jQuery selection.
	 * @param  target	element - the target input field or division or span
	 */
	_disableDatepicker: function( target ) {
		var nodeName, inline,
			$target = $( target ),
			inst = $.data( target, "datepicker" );

		if ( !$target.hasClass( this.markerClassName ) ) {
			return;
		}

		nodeName = target.nodeName.toLowerCase();
		if ( nodeName === "input" ) {
			target.disabled = true;
			inst.trigger.filter( "button" ).
				each( function() { this.disabled = true; } ).end().
				filter( "img" ).css( { opacity: "0.5", cursor: "default" } );
		} else if ( nodeName === "div" || nodeName === "span" ) {
			inline = $target.children( "." + this._inlineClass );
			inline.children().addClass( "ui-state-disabled" );
			inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
				prop( "disabled", true );
		}
		this._disabledInputs = $.map( this._disabledInputs,
			function( value ) { return ( value === target ? null : value ); } ); // delete entry
		this._disabledInputs[ this._disabledInputs.length ] = target;
	},

	/* Is the first field in a jQuery collection disabled as a datepicker?
	 * @param  target	element - the target input field or division or span
	 * @return boolean - true if disabled, false if enabled
	 */
	_isDisabledDatepicker: function( target ) {
		if ( !target ) {
			return false;
		}
		for ( var i = 0; i < this._disabledInputs.length; i++ ) {
			if ( this._disabledInputs[ i ] === target ) {
				return true;
			}
		}
		return false;
	},

	/* Retrieve the instance data for the target control.
	 * @param  target  element - the target input field or division or span
	 * @return  object - the associated instance data
	 * @throws  error if a jQuery problem getting data
	 */
	_getInst: function( target ) {
		try {
			return $.data( target, "datepicker" );
		}
		catch ( err ) {
			throw "Missing instance data for this datepicker";
		}
	},

	/* Update or retrieve the settings for a date picker attached to an input field or division.
	 * @param  target  element - the target input field or division or span
	 * @param  name	object - the new settings to update or
	 *				string - the name of the setting to change or retrieve,
	 *				when retrieving also "all" for all instance settings or
	 *				"defaults" for all global defaults
	 * @param  value   any - the new value for the setting
	 *				(omit if above is an object or to retrieve a value)
	 */
	_optionDatepicker: function( target, name, value ) {
		var settings, date, minDate, maxDate,
			inst = this._getInst( target );

		if ( arguments.length === 2 && typeof name === "string" ) {
			return ( name === "defaults" ? $.extend( {}, $.datepicker._defaults ) :
				( inst ? ( name === "all" ? $.extend( {}, inst.settings ) :
				this._get( inst, name ) ) : null ) );
		}

		settings = name || {};
		if ( typeof name === "string" ) {
			settings = {};
			settings[ name ] = value;
		}

		if ( inst ) {
			if ( this._curInst === inst ) {
				this._hideDatepicker();
			}

			date = this._getDateDatepicker( target, true );
			minDate = this._getMinMaxDate( inst, "min" );
			maxDate = this._getMinMaxDate( inst, "max" );
			datepicker_extendRemove( inst.settings, settings );

			// reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
			if ( minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined ) {
				inst.settings.minDate = this._formatDate( inst, minDate );
			}
			if ( maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined ) {
				inst.settings.maxDate = this._formatDate( inst, maxDate );
			}
			if ( "disabled" in settings ) {
				if ( settings.disabled ) {
					this._disableDatepicker( target );
				} else {
					this._enableDatepicker( target );
				}
			}
			this._attachments( $( target ), inst );
			this._autoSize( inst );
			this._setDate( inst, date );
			this._updateAlternate( inst );
			this._updateDatepicker( inst );
		}
	},

	// Change method deprecated
	_changeDatepicker: function( target, name, value ) {
		this._optionDatepicker( target, name, value );
	},

	/* Redraw the date picker attached to an input field or division.
	 * @param  target  element - the target input field or division or span
	 */
	_refreshDatepicker: function( target ) {
		var inst = this._getInst( target );
		if ( inst ) {
			this._updateDatepicker( inst );
		}
	},

	/* Set the dates for a jQuery selection.
	 * @param  target element - the target input field or division or span
	 * @param  date	Date - the new date
	 */
	_setDateDatepicker: function( target, date ) {
		var inst = this._getInst( target );
		if ( inst ) {
			this._setDate( inst, date );
			this._updateDatepicker( inst );
			this._updateAlternate( inst );
		}
	},

	/* Get the date(s) for the first entry in a jQuery selection.
	 * @param  target element - the target input field or division or span
	 * @param  noDefault boolean - true if no default date is to be used
	 * @return Date - the current date
	 */
	_getDateDatepicker: function( target, noDefault ) {
		var inst = this._getInst( target );
		if ( inst && !inst.inline ) {
			this._setDateFromField( inst, noDefault );
		}
		return ( inst ? this._getDate( inst ) : null );
	},

	/* Handle keystrokes. */
	_doKeyDown: function( event ) {
		var onSelect, dateStr, sel,
			inst = $.datepicker._getInst( event.target ),
			handled = true,
			isRTL = inst.dpDiv.is( ".ui-datepicker-rtl" );

		inst._keyEvent = true;
		if ( $.datepicker._datepickerShowing ) {
			switch ( event.keyCode ) {
				case 9: $.datepicker._hideDatepicker();
						handled = false;
						break; // hide on tab out
				case 13: sel = $( "td." + $.datepicker._dayOverClass + ":not(." +
									$.datepicker._currentClass + ")", inst.dpDiv );
						if ( sel[ 0 ] ) {
							$.datepicker._selectDay( event.target, inst.selectedMonth, inst.selectedYear, sel[ 0 ] );
						}

						onSelect = $.datepicker._get( inst, "onSelect" );
						if ( onSelect ) {
							dateStr = $.datepicker._formatDate( inst );

							// Trigger custom callback
							onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] );
						} else {
							$.datepicker._hideDatepicker();
						}

						return false; // don't submit the form
				case 27: $.datepicker._hideDatepicker();
						break; // hide on escape
				case 33: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
							-$.datepicker._get( inst, "stepBigMonths" ) :
							-$.datepicker._get( inst, "stepMonths" ) ), "M" );
						break; // previous month/year on page up/+ ctrl
				case 34: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
							+$.datepicker._get( inst, "stepBigMonths" ) :
							+$.datepicker._get( inst, "stepMonths" ) ), "M" );
						break; // next month/year on page down/+ ctrl
				case 35: if ( event.ctrlKey || event.metaKey ) {
							$.datepicker._clearDate( event.target );
						}
						handled = event.ctrlKey || event.metaKey;
						break; // clear on ctrl or command +end
				case 36: if ( event.ctrlKey || event.metaKey ) {
							$.datepicker._gotoToday( event.target );
						}
						handled = event.ctrlKey || event.metaKey;
						break; // current on ctrl or command +home
				case 37: if ( event.ctrlKey || event.metaKey ) {
							$.datepicker._adjustDate( event.target, ( isRTL ? +1 : -1 ), "D" );
						}
						handled = event.ctrlKey || event.metaKey;

						// -1 day on ctrl or command +left
						if ( event.originalEvent.altKey ) {
							$.datepicker._adjustDate( event.target, ( event.ctrlKey ?
								-$.datepicker._get( inst, "stepBigMonths" ) :
								-$.datepicker._get( inst, "stepMonths" ) ), "M" );
						}

						// next month/year on alt +left on Mac
						break;
				case 38: if ( event.ctrlKey || event.metaKey ) {
							$.datepicker._adjustDate( event.target, -7, "D" );
						}
						handled = event.ctrlKey || event.metaKey;
						break; // -1 week on ctrl or command +up
				case 39: if ( event.ctrlKey || event.metaKey ) {
							$.datepicker._adjustDate( event.target, ( isRTL ? -1 : +1 ), "D" );
						}
						handled = event.ctrlKey || event.metaKey;

						// +1 day on ctrl or command +right
						if ( event.originalEvent.altKey ) {
							$.datepicker._adjustDate( event.target, ( event.ctrlKey ?
								+$.datepicker._get( inst, "stepBigMonths" ) :
								+$.datepicker._get( inst, "stepMonths" ) ), "M" );
						}

						// next month/year on alt +right
						break;
				case 40: if ( event.ctrlKey || event.metaKey ) {
							$.datepicker._adjustDate( event.target, +7, "D" );
						}
						handled = event.ctrlKey || event.metaKey;
						break; // +1 week on ctrl or command +down
				default: handled = false;
			}
		} else if ( event.keyCode === 36 && event.ctrlKey ) { // display the date picker on ctrl+home
			$.datepicker._showDatepicker( this );
		} else {
			handled = false;
		}

		if ( handled ) {
			event.preventDefault();
			event.stopPropagation();
		}
	},

	/* Filter entered characters - based on date format. */
	_doKeyPress: function( event ) {
		var chars, chr,
			inst = $.datepicker._getInst( event.target );

		if ( $.datepicker._get( inst, "constrainInput" ) ) {
			chars = $.datepicker._possibleChars( $.datepicker._get( inst, "dateFormat" ) );
			chr = String.fromCharCode( event.charCode == null ? event.keyCode : event.charCode );
			return event.ctrlKey || event.metaKey || ( chr < " " || !chars || chars.indexOf( chr ) > -1 );
		}
	},

	/* Synchronise manual entry and field/alternate field. */
	_doKeyUp: function( event ) {
		var date,
			inst = $.datepicker._getInst( event.target );

		if ( inst.input.val() !== inst.lastVal ) {
			try {
				date = $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
					( inst.input ? inst.input.val() : null ),
					$.datepicker._getFormatConfig( inst ) );

				if ( date ) { // only if valid
					$.datepicker._setDateFromField( inst );
					$.datepicker._updateAlternate( inst );
					$.datepicker._updateDatepicker( inst );
				}
			}
			catch ( err ) {
			}
		}
		return true;
	},

	/* Pop-up the date picker for a given input field.
	 * If false returned from beforeShow event handler do not show.
	 * @param  input  element - the input field attached to the date picker or
	 *					event - if triggered by focus
	 */
	_showDatepicker: function( input ) {
		input = input.target || input;
		if ( input.nodeName.toLowerCase() !== "input" ) { // find from button/image trigger
			input = $( "input", input.parentNode )[ 0 ];
		}

		if ( $.datepicker._isDisabledDatepicker( input ) || $.datepicker._lastInput === input ) { // already here
			return;
		}

		var inst, beforeShow, beforeShowSettings, isFixed,
			offset, showAnim, duration;

		inst = $.datepicker._getInst( input );
		if ( $.datepicker._curInst && $.datepicker._curInst !== inst ) {
			$.datepicker._curInst.dpDiv.stop( true, true );
			if ( inst && $.datepicker._datepickerShowing ) {
				$.datepicker._hideDatepicker( $.datepicker._curInst.input[ 0 ] );
			}
		}

		beforeShow = $.datepicker._get( inst, "beforeShow" );
		beforeShowSettings = beforeShow ? beforeShow.apply( input, [ input, inst ] ) : {};
		if ( beforeShowSettings === false ) {
			return;
		}
		datepicker_extendRemove( inst.settings, beforeShowSettings );

		inst.lastVal = null;
		$.datepicker._lastInput = input;
		$.datepicker._setDateFromField( inst );

		if ( $.datepicker._inDialog ) { // hide cursor
			input.value = "";
		}
		if ( !$.datepicker._pos ) { // position below input
			$.datepicker._pos = $.datepicker._findPos( input );
			$.datepicker._pos[ 1 ] += input.offsetHeight; // add the height
		}

		isFixed = false;
		$( input ).parents().each( function() {
			isFixed |= $( this ).css( "position" ) === "fixed";
			return !isFixed;
		} );

		offset = { left: $.datepicker._pos[ 0 ], top: $.datepicker._pos[ 1 ] };
		$.datepicker._pos = null;

		//to avoid flashes on Firefox
		inst.dpDiv.empty();

		// determine sizing offscreen
		inst.dpDiv.css( { position: "absolute", display: "block", top: "-1000px" } );
		$.datepicker._updateDatepicker( inst );

		// fix width for dynamic number of date pickers
		// and adjust position before showing
		offset = $.datepicker._checkOffset( inst, offset, isFixed );
		inst.dpDiv.css( { position: ( $.datepicker._inDialog && $.blockUI ?
			"static" : ( isFixed ? "fixed" : "absolute" ) ), display: "none",
			left: offset.left + "px", top: offset.top + "px" } );

		if ( !inst.inline ) {
			showAnim = $.datepicker._get( inst, "showAnim" );
			duration = $.datepicker._get( inst, "duration" );
			inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 );
			$.datepicker._datepickerShowing = true;

			if ( $.effects && $.effects.effect[ showAnim ] ) {
				inst.dpDiv.show( showAnim, $.datepicker._get( inst, "showOptions" ), duration );
			} else {
				inst.dpDiv[ showAnim || "show" ]( showAnim ? duration : null );
			}

			if ( $.datepicker._shouldFocusInput( inst ) ) {
				inst.input.trigger( "focus" );
			}

			$.datepicker._curInst = inst;
		}
	},

	/* Generate the date picker content. */
	_updateDatepicker: function( inst ) {
		this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
		datepicker_instActive = inst; // for delegate hover events
		inst.dpDiv.empty().append( this._generateHTML( inst ) );
		this._attachHandlers( inst );

		var origyearshtml,
			numMonths = this._getNumberOfMonths( inst ),
			cols = numMonths[ 1 ],
			width = 17,
			activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" );

		if ( activeCell.length > 0 ) {
			datepicker_handleMouseover.apply( activeCell.get( 0 ) );
		}

		inst.dpDiv.removeClass( "ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4" ).width( "" );
		if ( cols > 1 ) {
			inst.dpDiv.addClass( "ui-datepicker-multi-" + cols ).css( "width", ( width * cols ) + "em" );
		}
		inst.dpDiv[ ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ? "add" : "remove" ) +
			"Class" ]( "ui-datepicker-multi" );
		inst.dpDiv[ ( this._get( inst, "isRTL" ) ? "add" : "remove" ) +
			"Class" ]( "ui-datepicker-rtl" );

		if ( inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
			inst.input.trigger( "focus" );
		}

		// Deffered render of the years select (to avoid flashes on Firefox)
		if ( inst.yearshtml ) {
			origyearshtml = inst.yearshtml;
			setTimeout( function() {

				//assure that inst.yearshtml didn't change.
				if ( origyearshtml === inst.yearshtml && inst.yearshtml ) {
					inst.dpDiv.find( "select.ui-datepicker-year:first" ).replaceWith( inst.yearshtml );
				}
				origyearshtml = inst.yearshtml = null;
			}, 0 );
		}
	},

	// #6694 - don't focus the input if it's already focused
	// this breaks the change event in IE
	// Support: IE and jQuery <1.9
	_shouldFocusInput: function( inst ) {
		return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
	},

	/* Check positioning to remain on screen. */
	_checkOffset: function( inst, offset, isFixed ) {
		var dpWidth = inst.dpDiv.outerWidth(),
			dpHeight = inst.dpDiv.outerHeight(),
			inputWidth = inst.input ? inst.input.outerWidth() : 0,
			inputHeight = inst.input ? inst.input.outerHeight() : 0,
			viewWidth = document.documentElement.clientWidth + ( isFixed ? 0 : $( document ).scrollLeft() ),
			viewHeight = document.documentElement.clientHeight + ( isFixed ? 0 : $( document ).scrollTop() );

		offset.left -= ( this._get( inst, "isRTL" ) ? ( dpWidth - inputWidth ) : 0 );
		offset.left -= ( isFixed && offset.left === inst.input.offset().left ) ? $( document ).scrollLeft() : 0;
		offset.top -= ( isFixed && offset.top === ( inst.input.offset().top + inputHeight ) ) ? $( document ).scrollTop() : 0;

		// Now check if datepicker is showing outside window viewport - move to a better place if so.
		offset.left -= Math.min( offset.left, ( offset.left + dpWidth > viewWidth && viewWidth > dpWidth ) ?
			Math.abs( offset.left + dpWidth - viewWidth ) : 0 );
		offset.top -= Math.min( offset.top, ( offset.top + dpHeight > viewHeight && viewHeight > dpHeight ) ?
			Math.abs( dpHeight + inputHeight ) : 0 );

		return offset;
	},

	/* Find an object's position on the screen. */
	_findPos: function( obj ) {
		var position,
			inst = this._getInst( obj ),
			isRTL = this._get( inst, "isRTL" );

		while ( obj && ( obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden( obj ) ) ) {
			obj = obj[ isRTL ? "previousSibling" : "nextSibling" ];
		}

		position = $( obj ).offset();
		return [ position.left, position.top ];
	},

	/* Hide the date picker from view.
	 * @param  input  element - the input field attached to the date picker
	 */
	_hideDatepicker: function( input ) {
		var showAnim, duration, postProcess, onClose,
			inst = this._curInst;

		if ( !inst || ( input && inst !== $.data( input, "datepicker" ) ) ) {
			return;
		}

		if ( this._datepickerShowing ) {
			showAnim = this._get( inst, "showAnim" );
			duration = this._get( inst, "duration" );
			postProcess = function() {
				$.datepicker._tidyDialog( inst );
			};

			// DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
			if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
				inst.dpDiv.hide( showAnim, $.datepicker._get( inst, "showOptions" ), duration, postProcess );
			} else {
				inst.dpDiv[ ( showAnim === "slideDown" ? "slideUp" :
					( showAnim === "fadeIn" ? "fadeOut" : "hide" ) ) ]( ( showAnim ? duration : null ), postProcess );
			}

			if ( !showAnim ) {
				postProcess();
			}
			this._datepickerShowing = false;

			onClose = this._get( inst, "onClose" );
			if ( onClose ) {
				onClose.apply( ( inst.input ? inst.input[ 0 ] : null ), [ ( inst.input ? inst.input.val() : "" ), inst ] );
			}

			this._lastInput = null;
			if ( this._inDialog ) {
				this._dialogInput.css( { position: "absolute", left: "0", top: "-100px" } );
				if ( $.blockUI ) {
					$.unblockUI();
					$( "body" ).append( this.dpDiv );
				}
			}
			this._inDialog = false;
		}
	},

	/* Tidy up after a dialog display. */
	_tidyDialog: function( inst ) {
		inst.dpDiv.removeClass( this._dialogClass ).off( ".ui-datepicker-calendar" );
	},

	/* Close date picker if clicked elsewhere. */
	_checkExternalClick: function( event ) {
		if ( !$.datepicker._curInst ) {
			return;
		}

		var $target = $( event.target ),
			inst = $.datepicker._getInst( $target[ 0 ] );

		if ( ( ( $target[ 0 ].id !== $.datepicker._mainDivId &&
				$target.parents( "#" + $.datepicker._mainDivId ).length === 0 &&
				!$target.hasClass( $.datepicker.markerClassName ) &&
				!$target.closest( "." + $.datepicker._triggerClass ).length &&
				$.datepicker._datepickerShowing && !( $.datepicker._inDialog && $.blockUI ) ) ) ||
			( $target.hasClass( $.datepicker.markerClassName ) && $.datepicker._curInst !== inst ) ) {
				$.datepicker._hideDatepicker();
		}
	},

	/* Adjust one of the date sub-fields. */
	_adjustDate: function( id, offset, period ) {
		var target = $( id ),
			inst = this._getInst( target[ 0 ] );

		if ( this._isDisabledDatepicker( target[ 0 ] ) ) {
			return;
		}
		this._adjustInstDate( inst, offset +
			( period === "M" ? this._get( inst, "showCurrentAtPos" ) : 0 ), // undo positioning
			period );
		this._updateDatepicker( inst );
	},

	/* Action for current link. */
	_gotoToday: function( id ) {
		var date,
			target = $( id ),
			inst = this._getInst( target[ 0 ] );

		if ( this._get( inst, "gotoCurrent" ) && inst.currentDay ) {
			inst.selectedDay = inst.currentDay;
			inst.drawMonth = inst.selectedMonth = inst.currentMonth;
			inst.drawYear = inst.selectedYear = inst.currentYear;
		} else {
			date = new Date();
			inst.selectedDay = date.getDate();
			inst.drawMonth = inst.selectedMonth = date.getMonth();
			inst.drawYear = inst.selectedYear = date.getFullYear();
		}
		this._notifyChange( inst );
		this._adjustDate( target );
	},

	/* Action for selecting a new month/year. */
	_selectMonthYear: function( id, select, period ) {
		var target = $( id ),
			inst = this._getInst( target[ 0 ] );

		inst[ "selected" + ( period === "M" ? "Month" : "Year" ) ] =
		inst[ "draw" + ( period === "M" ? "Month" : "Year" ) ] =
			parseInt( select.options[ select.selectedIndex ].value, 10 );

		this._notifyChange( inst );
		this._adjustDate( target );
	},

	/* Action for selecting a day. */
	_selectDay: function( id, month, year, td ) {
		var inst,
			target = $( id );

		if ( $( td ).hasClass( this._unselectableClass ) || this._isDisabledDatepicker( target[ 0 ] ) ) {
			return;
		}

		inst = this._getInst( target[ 0 ] );
		inst.selectedDay = inst.currentDay = $( "a", td ).html();
		inst.selectedMonth = inst.currentMonth = month;
		inst.selectedYear = inst.currentYear = year;
		this._selectDate( id, this._formatDate( inst,
			inst.currentDay, inst.currentMonth, inst.currentYear ) );
	},

	/* Erase the input field and hide the date picker. */
	_clearDate: function( id ) {
		var target = $( id );
		this._selectDate( target, "" );
	},

	/* Update the input field with the selected date. */
	_selectDate: function( id, dateStr ) {
		var onSelect,
			target = $( id ),
			inst = this._getInst( target[ 0 ] );

		dateStr = ( dateStr != null ? dateStr : this._formatDate( inst ) );
		if ( inst.input ) {
			inst.input.val( dateStr );
		}
		this._updateAlternate( inst );

		onSelect = this._get( inst, "onSelect" );
		if ( onSelect ) {
			onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] );  // trigger custom callback
		} else if ( inst.input ) {
			inst.input.trigger( "change" ); // fire the change event
		}

		if ( inst.inline ) {
			this._updateDatepicker( inst );
		} else {
			this._hideDatepicker();
			this._lastInput = inst.input[ 0 ];
			if ( typeof( inst.input[ 0 ] ) !== "object" ) {
				inst.input.trigger( "focus" ); // restore focus
			}
			this._lastInput = null;
		}
	},

	/* Update any alternate field to synchronise with the main field. */
	_updateAlternate: function( inst ) {
		var altFormat, date, dateStr,
			altField = this._get( inst, "altField" );

		if ( altField ) { // update alternate field too
			altFormat = this._get( inst, "altFormat" ) || this._get( inst, "dateFormat" );
			date = this._getDate( inst );
			dateStr = this.formatDate( altFormat, date, this._getFormatConfig( inst ) );
			$( altField ).val( dateStr );
		}
	},

	/* Set as beforeShowDay function to prevent selection of weekends.
	 * @param  date  Date - the date to customise
	 * @return [boolean, string] - is this date selectable?, what is its CSS class?
	 */
	noWeekends: function( date ) {
		var day = date.getDay();
		return [ ( day > 0 && day < 6 ), "" ];
	},

	/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
	 * @param  date  Date - the date to get the week for
	 * @return  number - the number of the week within the year that contains this date
	 */
	iso8601Week: function( date ) {
		var time,
			checkDate = new Date( date.getTime() );

		// Find Thursday of this week starting on Monday
		checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) );

		time = checkDate.getTime();
		checkDate.setMonth( 0 ); // Compare with Jan 1
		checkDate.setDate( 1 );
		return Math.floor( Math.round( ( time - checkDate ) / 86400000 ) / 7 ) + 1;
	},

	/* Parse a string value into a date object.
	 * See formatDate below for the possible formats.
	 *
	 * @param  format string - the expected format of the date
	 * @param  value string - the date in the above format
	 * @param  settings Object - attributes include:
	 *					shortYearCutoff  number - the cutoff year for determining the century (optional)
	 *					dayNamesShort	string[7] - abbreviated names of the days from Sunday (optional)
	 *					dayNames		string[7] - names of the days from Sunday (optional)
	 *					monthNamesShort string[12] - abbreviated names of the months (optional)
	 *					monthNames		string[12] - names of the months (optional)
	 * @return  Date - the extracted date value or null if value is blank
	 */
	parseDate: function( format, value, settings ) {
		if ( format == null || value == null ) {
			throw "Invalid arguments";
		}

		value = ( typeof value === "object" ? value.toString() : value + "" );
		if ( value === "" ) {
			return null;
		}

		var iFormat, dim, extra,
			iValue = 0,
			shortYearCutoffTemp = ( settings ? settings.shortYearCutoff : null ) || this._defaults.shortYearCutoff,
			shortYearCutoff = ( typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
				new Date().getFullYear() % 100 + parseInt( shortYearCutoffTemp, 10 ) ),
			dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
			dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
			monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
			monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
			year = -1,
			month = -1,
			day = -1,
			doy = -1,
			literal = false,
			date,

			// Check whether a format character is doubled
			lookAhead = function( match ) {
				var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
				if ( matches ) {
					iFormat++;
				}
				return matches;
			},

			// Extract a number from the string value
			getNumber = function( match ) {
				var isDoubled = lookAhead( match ),
					size = ( match === "@" ? 14 : ( match === "!" ? 20 :
					( match === "y" && isDoubled ? 4 : ( match === "o" ? 3 : 2 ) ) ) ),
					minSize = ( match === "y" ? size : 1 ),
					digits = new RegExp( "^\\d{" + minSize + "," + size + "}" ),
					num = value.substring( iValue ).match( digits );
				if ( !num ) {
					throw "Missing number at position " + iValue;
				}
				iValue += num[ 0 ].length;
				return parseInt( num[ 0 ], 10 );
			},

			// Extract a name from the string value and convert to an index
			getName = function( match, shortNames, longNames ) {
				var index = -1,
					names = $.map( lookAhead( match ) ? longNames : shortNames, function( v, k ) {
						return [ [ k, v ] ];
					} ).sort( function( a, b ) {
						return -( a[ 1 ].length - b[ 1 ].length );
					} );

				$.each( names, function( i, pair ) {
					var name = pair[ 1 ];
					if ( value.substr( iValue, name.length ).toLowerCase() === name.toLowerCase() ) {
						index = pair[ 0 ];
						iValue += name.length;
						return false;
					}
				} );
				if ( index !== -1 ) {
					return index + 1;
				} else {
					throw "Unknown name at position " + iValue;
				}
			},

			// Confirm that a literal character matches the string value
			checkLiteral = function() {
				if ( value.charAt( iValue ) !== format.charAt( iFormat ) ) {
					throw "Unexpected literal at position " + iValue;
				}
				iValue++;
			};

		for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
			if ( literal ) {
				if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
					literal = false;
				} else {
					checkLiteral();
				}
			} else {
				switch ( format.charAt( iFormat ) ) {
					case "d":
						day = getNumber( "d" );
						break;
					case "D":
						getName( "D", dayNamesShort, dayNames );
						break;
					case "o":
						doy = getNumber( "o" );
						break;
					case "m":
						month = getNumber( "m" );
						break;
					case "M":
						month = getName( "M", monthNamesShort, monthNames );
						break;
					case "y":
						year = getNumber( "y" );
						break;
					case "@":
						date = new Date( getNumber( "@" ) );
						year = date.getFullYear();
						month = date.getMonth() + 1;
						day = date.getDate();
						break;
					case "!":
						date = new Date( ( getNumber( "!" ) - this._ticksTo1970 ) / 10000 );
						year = date.getFullYear();
						month = date.getMonth() + 1;
						day = date.getDate();
						break;
					case "'":
						if ( lookAhead( "'" ) ) {
							checkLiteral();
						} else {
							literal = true;
						}
						break;
					default:
						checkLiteral();
				}
			}
		}

		if ( iValue < value.length ) {
			extra = value.substr( iValue );
			if ( !/^\s+/.test( extra ) ) {
				throw "Extra/unparsed characters found in date: " + extra;
			}
		}

		if ( year === -1 ) {
			year = new Date().getFullYear();
		} else if ( year < 100 ) {
			year += new Date().getFullYear() - new Date().getFullYear() % 100 +
				( year <= shortYearCutoff ? 0 : -100 );
		}

		if ( doy > -1 ) {
			month = 1;
			day = doy;
			do {
				dim = this._getDaysInMonth( year, month - 1 );
				if ( day <= dim ) {
					break;
				}
				month++;
				day -= dim;
			} while ( true );
		}

		date = this._daylightSavingAdjust( new Date( year, month - 1, day ) );
		if ( date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day ) {
			throw "Invalid date"; // E.g. 31/02/00
		}
		return date;
	},

	/* Standard date formats. */
	ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
	COOKIE: "D, dd M yy",
	ISO_8601: "yy-mm-dd",
	RFC_822: "D, d M y",
	RFC_850: "DD, dd-M-y",
	RFC_1036: "D, d M y",
	RFC_1123: "D, d M yy",
	RFC_2822: "D, d M yy",
	RSS: "D, d M y", // RFC 822
	TICKS: "!",
	TIMESTAMP: "@",
	W3C: "yy-mm-dd", // ISO 8601

	_ticksTo1970: ( ( ( 1970 - 1 ) * 365 + Math.floor( 1970 / 4 ) - Math.floor( 1970 / 100 ) +
		Math.floor( 1970 / 400 ) ) * 24 * 60 * 60 * 10000000 ),

	/* Format a date object into a string value.
	 * The format can be combinations of the following:
	 * d  - day of month (no leading zero)
	 * dd - day of month (two digit)
	 * o  - day of year (no leading zeros)
	 * oo - day of year (three digit)
	 * D  - day name short
	 * DD - day name long
	 * m  - month of year (no leading zero)
	 * mm - month of year (two digit)
	 * M  - month name short
	 * MM - month name long
	 * y  - year (two digit)
	 * yy - year (four digit)
	 * @ - Unix timestamp (ms since 01/01/1970)
	 * ! - Windows ticks (100ns since 01/01/0001)
	 * "..." - literal text
	 * '' - single quote
	 *
	 * @param  format string - the desired format of the date
	 * @param  date Date - the date value to format
	 * @param  settings Object - attributes include:
	 *					dayNamesShort	string[7] - abbreviated names of the days from Sunday (optional)
	 *					dayNames		string[7] - names of the days from Sunday (optional)
	 *					monthNamesShort string[12] - abbreviated names of the months (optional)
	 *					monthNames		string[12] - names of the months (optional)
	 * @return  string - the date in the above format
	 */
	formatDate: function( format, date, settings ) {
		if ( !date ) {
			return "";
		}

		var iFormat,
			dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
			dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
			monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
			monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,

			// Check whether a format character is doubled
			lookAhead = function( match ) {
				var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
				if ( matches ) {
					iFormat++;
				}
				return matches;
			},

			// Format a number, with leading zero if necessary
			formatNumber = function( match, value, len ) {
				var num = "" + value;
				if ( lookAhead( match ) ) {
					while ( num.length < len ) {
						num = "0" + num;
					}
				}
				return num;
			},

			// Format a name, short or long as requested
			formatName = function( match, value, shortNames, longNames ) {
				return ( lookAhead( match ) ? longNames[ value ] : shortNames[ value ] );
			},
			output = "",
			literal = false;

		if ( date ) {
			for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
				if ( literal ) {
					if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
						literal = false;
					} else {
						output += format.charAt( iFormat );
					}
				} else {
					switch ( format.charAt( iFormat ) ) {
						case "d":
							output += formatNumber( "d", date.getDate(), 2 );
							break;
						case "D":
							output += formatName( "D", date.getDay(), dayNamesShort, dayNames );
							break;
						case "o":
							output += formatNumber( "o",
								Math.round( ( new Date( date.getFullYear(), date.getMonth(), date.getDate() ).getTime() - new Date( date.getFullYear(), 0, 0 ).getTime() ) / 86400000 ), 3 );
							break;
						case "m":
							output += formatNumber( "m", date.getMonth() + 1, 2 );
							break;
						case "M":
							output += formatName( "M", date.getMonth(), monthNamesShort, monthNames );
							break;
						case "y":
							output += ( lookAhead( "y" ) ? date.getFullYear() :
								( date.getFullYear() % 100 < 10 ? "0" : "" ) + date.getFullYear() % 100 );
							break;
						case "@":
							output += date.getTime();
							break;
						case "!":
							output += date.getTime() * 10000 + this._ticksTo1970;
							break;
						case "'":
							if ( lookAhead( "'" ) ) {
								output += "'";
							} else {
								literal = true;
							}
							break;
						default:
							output += format.charAt( iFormat );
					}
				}
			}
		}
		return output;
	},

	/* Extract all possible characters from the date format. */
	_possibleChars: function( format ) {
		var iFormat,
			chars = "",
			literal = false,

			// Check whether a format character is doubled
			lookAhead = function( match ) {
				var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
				if ( matches ) {
					iFormat++;
				}
				return matches;
			};

		for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
			if ( literal ) {
				if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
					literal = false;
				} else {
					chars += format.charAt( iFormat );
				}
			} else {
				switch ( format.charAt( iFormat ) ) {
					case "d": case "m": case "y": case "@":
						chars += "0123456789";
						break;
					case "D": case "M":
						return null; // Accept anything
					case "'":
						if ( lookAhead( "'" ) ) {
							chars += "'";
						} else {
							literal = true;
						}
						break;
					default:
						chars += format.charAt( iFormat );
				}
			}
		}
		return chars;
	},

	/* Get a setting value, defaulting if necessary. */
	_get: function( inst, name ) {
		return inst.settings[ name ] !== undefined ?
			inst.settings[ name ] : this._defaults[ name ];
	},

	/* Parse existing date and initialise date picker. */
	_setDateFromField: function( inst, noDefault ) {
		if ( inst.input.val() === inst.lastVal ) {
			return;
		}

		var dateFormat = this._get( inst, "dateFormat" ),
			dates = inst.lastVal = inst.input ? inst.input.val() : null,
			defaultDate = this._getDefaultDate( inst ),
			date = defaultDate,
			settings = this._getFormatConfig( inst );

		try {
			date = this.parseDate( dateFormat, dates, settings ) || defaultDate;
		} catch ( event ) {
			dates = ( noDefault ? "" : dates );
		}
		inst.selectedDay = date.getDate();
		inst.drawMonth = inst.selectedMonth = date.getMonth();
		inst.drawYear = inst.selectedYear = date.getFullYear();
		inst.currentDay = ( dates ? date.getDate() : 0 );
		inst.currentMonth = ( dates ? date.getMonth() : 0 );
		inst.currentYear = ( dates ? date.getFullYear() : 0 );
		this._adjustInstDate( inst );
	},

	/* Retrieve the default date shown on opening. */
	_getDefaultDate: function( inst ) {
		return this._restrictMinMax( inst,
			this._determineDate( inst, this._get( inst, "defaultDate" ), new Date() ) );
	},

	/* A date may be specified as an exact value or a relative one. */
	_determineDate: function( inst, date, defaultDate ) {
		var offsetNumeric = function( offset ) {
				var date = new Date();
				date.setDate( date.getDate() + offset );
				return date;
			},
			offsetString = function( offset ) {
				try {
					return $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
						offset, $.datepicker._getFormatConfig( inst ) );
				}
				catch ( e ) {

					// Ignore
				}

				var date = ( offset.toLowerCase().match( /^c/ ) ?
					$.datepicker._getDate( inst ) : null ) || new Date(),
					year = date.getFullYear(),
					month = date.getMonth(),
					day = date.getDate(),
					pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
					matches = pattern.exec( offset );

				while ( matches ) {
					switch ( matches[ 2 ] || "d" ) {
						case "d" : case "D" :
							day += parseInt( matches[ 1 ], 10 ); break;
						case "w" : case "W" :
							day += parseInt( matches[ 1 ], 10 ) * 7; break;
						case "m" : case "M" :
							month += parseInt( matches[ 1 ], 10 );
							day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
							break;
						case "y": case "Y" :
							year += parseInt( matches[ 1 ], 10 );
							day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
							break;
					}
					matches = pattern.exec( offset );
				}
				return new Date( year, month, day );
			},
			newDate = ( date == null || date === "" ? defaultDate : ( typeof date === "string" ? offsetString( date ) :
				( typeof date === "number" ? ( isNaN( date ) ? defaultDate : offsetNumeric( date ) ) : new Date( date.getTime() ) ) ) );

		newDate = ( newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate );
		if ( newDate ) {
			newDate.setHours( 0 );
			newDate.setMinutes( 0 );
			newDate.setSeconds( 0 );
			newDate.setMilliseconds( 0 );
		}
		return this._daylightSavingAdjust( newDate );
	},

	/* Handle switch to/from daylight saving.
	 * Hours may be non-zero on daylight saving cut-over:
	 * > 12 when midnight changeover, but then cannot generate
	 * midnight datetime, so jump to 1AM, otherwise reset.
	 * @param  date  (Date) the date to check
	 * @return  (Date) the corrected date
	 */
	_daylightSavingAdjust: function( date ) {
		if ( !date ) {
			return null;
		}
		date.setHours( date.getHours() > 12 ? date.getHours() + 2 : 0 );
		return date;
	},

	/* Set the date(s) directly. */
	_setDate: function( inst, date, noChange ) {
		var clear = !date,
			origMonth = inst.selectedMonth,
			origYear = inst.selectedYear,
			newDate = this._restrictMinMax( inst, this._determineDate( inst, date, new Date() ) );

		inst.selectedDay = inst.currentDay = newDate.getDate();
		inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
		inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
		if ( ( origMonth !== inst.selectedMonth || origYear !== inst.selectedYear ) && !noChange ) {
			this._notifyChange( inst );
		}
		this._adjustInstDate( inst );
		if ( inst.input ) {
			inst.input.val( clear ? "" : this._formatDate( inst ) );
		}
	},

	/* Retrieve the date(s) directly. */
	_getDate: function( inst ) {
		var startDate = ( !inst.currentYear || ( inst.input && inst.input.val() === "" ) ? null :
			this._daylightSavingAdjust( new Date(
			inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
			return startDate;
	},

	/* Attach the onxxx handlers.  These are declared statically so
	 * they work with static code transformers like Caja.
	 */
	_attachHandlers: function( inst ) {
		var stepMonths = this._get( inst, "stepMonths" ),
			id = "#" + inst.id.replace( /\\\\/g, "\\" );
		inst.dpDiv.find( "[data-handler]" ).map( function() {
			var handler = {
				prev: function() {
					$.datepicker._adjustDate( id, -stepMonths, "M" );
				},
				next: function() {
					$.datepicker._adjustDate( id, +stepMonths, "M" );
				},
				hide: function() {
					$.datepicker._hideDatepicker();
				},
				today: function() {
					$.datepicker._gotoToday( id );
				},
				selectDay: function() {
					$.datepicker._selectDay( id, +this.getAttribute( "data-month" ), +this.getAttribute( "data-year" ), this );
					return false;
				},
				selectMonth: function() {
					$.datepicker._selectMonthYear( id, this, "M" );
					return false;
				},
				selectYear: function() {
					$.datepicker._selectMonthYear( id, this, "Y" );
					return false;
				}
			};
			$( this ).on( this.getAttribute( "data-event" ), handler[ this.getAttribute( "data-handler" ) ] );
		} );
	},

	/* Generate the HTML for the current state of the date picker. */
	_generateHTML: function( inst ) {
		var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
			controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
			monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
			selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
			cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
			printDate, dRow, tbody, daySettings, otherMonth, unselectable,
			tempDate = new Date(),
			today = this._daylightSavingAdjust(
				new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time
			isRTL = this._get( inst, "isRTL" ),
			showButtonPanel = this._get( inst, "showButtonPanel" ),
			hideIfNoPrevNext = this._get( inst, "hideIfNoPrevNext" ),
			navigationAsDateFormat = this._get( inst, "navigationAsDateFormat" ),
			numMonths = this._getNumberOfMonths( inst ),
			showCurrentAtPos = this._get( inst, "showCurrentAtPos" ),
			stepMonths = this._get( inst, "stepMonths" ),
			isMultiMonth = ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ),
			currentDate = this._daylightSavingAdjust( ( !inst.currentDay ? new Date( 9999, 9, 9 ) :
				new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ),
			minDate = this._getMinMaxDate( inst, "min" ),
			maxDate = this._getMinMaxDate( inst, "max" ),
			drawMonth = inst.drawMonth - showCurrentAtPos,
			drawYear = inst.drawYear;

		if ( drawMonth < 0 ) {
			drawMonth += 12;
			drawYear--;
		}
		if ( maxDate ) {
			maxDraw = this._daylightSavingAdjust( new Date( maxDate.getFullYear(),
				maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1, maxDate.getDate() ) );
			maxDraw = ( minDate && maxDraw < minDate ? minDate : maxDraw );
			while ( this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 ) ) > maxDraw ) {
				drawMonth--;
				if ( drawMonth < 0 ) {
					drawMonth = 11;
					drawYear--;
				}
			}
		}
		inst.drawMonth = drawMonth;
		inst.drawYear = drawYear;

		prevText = this._get( inst, "prevText" );
		prevText = ( !navigationAsDateFormat ? prevText : this.formatDate( prevText,
			this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ),
			this._getFormatConfig( inst ) ) );

		prev = ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ?
			"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
			" title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" :
			( hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" ) );

		nextText = this._get( inst, "nextText" );
		nextText = ( !navigationAsDateFormat ? nextText : this.formatDate( nextText,
			this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ),
			this._getFormatConfig( inst ) ) );

		next = ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ?
			"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
			" title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" :
			( hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" ) );

		currentText = this._get( inst, "currentText" );
		gotoDate = ( this._get( inst, "gotoCurrent" ) && inst.currentDay ? currentDate : today );
		currentText = ( !navigationAsDateFormat ? currentText :
			this.formatDate( currentText, gotoDate, this._getFormatConfig( inst ) ) );

		controls = ( !inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" +
			this._get( inst, "closeText" ) + "</button>" : "" );

		buttonPanel = ( showButtonPanel ) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + ( isRTL ? controls : "" ) +
			( this._isInRange( inst, gotoDate ) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" +
			">" + currentText + "</button>" : "" ) + ( isRTL ? "" : controls ) + "</div>" : "";

		firstDay = parseInt( this._get( inst, "firstDay" ), 10 );
		firstDay = ( isNaN( firstDay ) ? 0 : firstDay );

		showWeek = this._get( inst, "showWeek" );
		dayNames = this._get( inst, "dayNames" );
		dayNamesMin = this._get( inst, "dayNamesMin" );
		monthNames = this._get( inst, "monthNames" );
		monthNamesShort = this._get( inst, "monthNamesShort" );
		beforeShowDay = this._get( inst, "beforeShowDay" );
		showOtherMonths = this._get( inst, "showOtherMonths" );
		selectOtherMonths = this._get( inst, "selectOtherMonths" );
		defaultDate = this._getDefaultDate( inst );
		html = "";

		for ( row = 0; row < numMonths[ 0 ]; row++ ) {
			group = "";
			this.maxRows = 4;
			for ( col = 0; col < numMonths[ 1 ]; col++ ) {
				selectedDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, inst.selectedDay ) );
				cornerClass = " ui-corner-all";
				calender = "";
				if ( isMultiMonth ) {
					calender += "<div class='ui-datepicker-group";
					if ( numMonths[ 1 ] > 1 ) {
						switch ( col ) {
							case 0: calender += " ui-datepicker-group-first";
								cornerClass = " ui-corner-" + ( isRTL ? "right" : "left" ); break;
							case numMonths[ 1 ] - 1: calender += " ui-datepicker-group-last";
								cornerClass = " ui-corner-" + ( isRTL ? "left" : "right" ); break;
							default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
						}
					}
					calender += "'>";
				}
				calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
					( /all|left/.test( cornerClass ) && row === 0 ? ( isRTL ? next : prev ) : "" ) +
					( /all|right/.test( cornerClass ) && row === 0 ? ( isRTL ? prev : next ) : "" ) +
					this._generateMonthYearHeader( inst, drawMonth, drawYear, minDate, maxDate,
					row > 0 || col > 0, monthNames, monthNamesShort ) + // draw month headers
					"</div><table class='ui-datepicker-calendar'><thead>" +
					"<tr>";
				thead = ( showWeek ? "<th class='ui-datepicker-week-col'>" + this._get( inst, "weekHeader" ) + "</th>" : "" );
				for ( dow = 0; dow < 7; dow++ ) { // days of the week
					day = ( dow + firstDay ) % 7;
					thead += "<th scope='col'" + ( ( dow + firstDay + 6 ) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "" ) + ">" +
						"<span title='" + dayNames[ day ] + "'>" + dayNamesMin[ day ] + "</span></th>";
				}
				calender += thead + "</tr></thead><tbody>";
				daysInMonth = this._getDaysInMonth( drawYear, drawMonth );
				if ( drawYear === inst.selectedYear && drawMonth === inst.selectedMonth ) {
					inst.selectedDay = Math.min( inst.selectedDay, daysInMonth );
				}
				leadDays = ( this._getFirstDayOfMonth( drawYear, drawMonth ) - firstDay + 7 ) % 7;
				curRows = Math.ceil( ( leadDays + daysInMonth ) / 7 ); // calculate the number of rows to generate
				numRows = ( isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows ); //If multiple months, use the higher number of rows (see #7043)
				this.maxRows = numRows;
				printDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 - leadDays ) );
				for ( dRow = 0; dRow < numRows; dRow++ ) { // create date picker rows
					calender += "<tr>";
					tbody = ( !showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
						this._get( inst, "calculateWeek" )( printDate ) + "</td>" );
					for ( dow = 0; dow < 7; dow++ ) { // create date picker days
						daySettings = ( beforeShowDay ?
							beforeShowDay.apply( ( inst.input ? inst.input[ 0 ] : null ), [ printDate ] ) : [ true, "" ] );
						otherMonth = ( printDate.getMonth() !== drawMonth );
						unselectable = ( otherMonth && !selectOtherMonths ) || !daySettings[ 0 ] ||
							( minDate && printDate < minDate ) || ( maxDate && printDate > maxDate );
						tbody += "<td class='" +
							( ( dow + firstDay + 6 ) % 7 >= 5 ? " ui-datepicker-week-end" : "" ) + // highlight weekends
							( otherMonth ? " ui-datepicker-other-month" : "" ) + // highlight days from other months
							( ( printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent ) || // user pressed key
							( defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime() ) ?

							// or defaultDate is current printedDate and defaultDate is selectedDate
							" " + this._dayOverClass : "" ) + // highlight selected day
							( unselectable ? " " + this._unselectableClass + " ui-state-disabled" : "" ) +  // highlight unselectable days
							( otherMonth && !showOtherMonths ? "" : " " + daySettings[ 1 ] + // highlight custom dates
							( printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "" ) + // highlight selected day
							( printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "" ) ) + "'" + // highlight today (if different)
							( ( !otherMonth || showOtherMonths ) && daySettings[ 2 ] ? " title='" + daySettings[ 2 ].replace( /'/g, "&#39;" ) + "'" : "" ) + // cell title
							( unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'" ) + ">" + // actions
							( otherMonth && !showOtherMonths ? "&#xa0;" : // display for other months
							( unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
							( printDate.getTime() === today.getTime() ? " ui-state-highlight" : "" ) +
							( printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "" ) + // highlight selected day
							( otherMonth ? " ui-priority-secondary" : "" ) + // distinguish dates from other months
							"' href='#'>" + printDate.getDate() + "</a>" ) ) + "</td>"; // display selectable date
						printDate.setDate( printDate.getDate() + 1 );
						printDate = this._daylightSavingAdjust( printDate );
					}
					calender += tbody + "</tr>";
				}
				drawMonth++;
				if ( drawMonth > 11 ) {
					drawMonth = 0;
					drawYear++;
				}
				calender += "</tbody></table>" + ( isMultiMonth ? "</div>" +
							( ( numMonths[ 0 ] > 0 && col === numMonths[ 1 ] - 1 ) ? "<div class='ui-datepicker-row-break'></div>" : "" ) : "" );
				group += calender;
			}
			html += group;
		}
		html += buttonPanel;
		inst._keyEvent = false;
		return html;
	},

	/* Generate the month and year header. */
	_generateMonthYearHeader: function( inst, drawMonth, drawYear, minDate, maxDate,
			secondary, monthNames, monthNamesShort ) {

		var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
			changeMonth = this._get( inst, "changeMonth" ),
			changeYear = this._get( inst, "changeYear" ),
			showMonthAfterYear = this._get( inst, "showMonthAfterYear" ),
			html = "<div class='ui-datepicker-title'>",
			monthHtml = "";

		// Month selection
		if ( secondary || !changeMonth ) {
			monthHtml += "<span class='ui-datepicker-month'>" + monthNames[ drawMonth ] + "</span>";
		} else {
			inMinYear = ( minDate && minDate.getFullYear() === drawYear );
			inMaxYear = ( maxDate && maxDate.getFullYear() === drawYear );
			monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
			for ( month = 0; month < 12; month++ ) {
				if ( ( !inMinYear || month >= minDate.getMonth() ) && ( !inMaxYear || month <= maxDate.getMonth() ) ) {
					monthHtml += "<option value='" + month + "'" +
						( month === drawMonth ? " selected='selected'" : "" ) +
						">" + monthNamesShort[ month ] + "</option>";
				}
			}
			monthHtml += "</select>";
		}

		if ( !showMonthAfterYear ) {
			html += monthHtml + ( secondary || !( changeMonth && changeYear ) ? "&#xa0;" : "" );
		}

		// Year selection
		if ( !inst.yearshtml ) {
			inst.yearshtml = "";
			if ( secondary || !changeYear ) {
				html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
			} else {

				// determine range of years to display
				years = this._get( inst, "yearRange" ).split( ":" );
				thisYear = new Date().getFullYear();
				determineYear = function( value ) {
					var year = ( value.match( /c[+\-].*/ ) ? drawYear + parseInt( value.substring( 1 ), 10 ) :
						( value.match( /[+\-].*/ ) ? thisYear + parseInt( value, 10 ) :
						parseInt( value, 10 ) ) );
					return ( isNaN( year ) ? thisYear : year );
				};
				year = determineYear( years[ 0 ] );
				endYear = Math.max( year, determineYear( years[ 1 ] || "" ) );
				year = ( minDate ? Math.max( year, minDate.getFullYear() ) : year );
				endYear = ( maxDate ? Math.min( endYear, maxDate.getFullYear() ) : endYear );
				inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
				for ( ; year <= endYear; year++ ) {
					inst.yearshtml += "<option value='" + year + "'" +
						( year === drawYear ? " selected='selected'" : "" ) +
						">" + year + "</option>";
				}
				inst.yearshtml += "</select>";

				html += inst.yearshtml;
				inst.yearshtml = null;
			}
		}

		html += this._get( inst, "yearSuffix" );
		if ( showMonthAfterYear ) {
			html += ( secondary || !( changeMonth && changeYear ) ? "&#xa0;" : "" ) + monthHtml;
		}
		html += "</div>"; // Close datepicker_header
		return html;
	},

	/* Adjust one of the date sub-fields. */
	_adjustInstDate: function( inst, offset, period ) {
		var year = inst.selectedYear + ( period === "Y" ? offset : 0 ),
			month = inst.selectedMonth + ( period === "M" ? offset : 0 ),
			day = Math.min( inst.selectedDay, this._getDaysInMonth( year, month ) ) + ( period === "D" ? offset : 0 ),
			date = this._restrictMinMax( inst, this._daylightSavingAdjust( new Date( year, month, day ) ) );

		inst.selectedDay = date.getDate();
		inst.drawMonth = inst.selectedMonth = date.getMonth();
		inst.drawYear = inst.selectedYear = date.getFullYear();
		if ( period === "M" || period === "Y" ) {
			this._notifyChange( inst );
		}
	},

	/* Ensure a date is within any min/max bounds. */
	_restrictMinMax: function( inst, date ) {
		var minDate = this._getMinMaxDate( inst, "min" ),
			maxDate = this._getMinMaxDate( inst, "max" ),
			newDate = ( minDate && date < minDate ? minDate : date );
		return ( maxDate && newDate > maxDate ? maxDate : newDate );
	},

	/* Notify change of month/year. */
	_notifyChange: function( inst ) {
		var onChange = this._get( inst, "onChangeMonthYear" );
		if ( onChange ) {
			onChange.apply( ( inst.input ? inst.input[ 0 ] : null ),
				[ inst.selectedYear, inst.selectedMonth + 1, inst ] );
		}
	},

	/* Determine the number of months to show. */
	_getNumberOfMonths: function( inst ) {
		var numMonths = this._get( inst, "numberOfMonths" );
		return ( numMonths == null ? [ 1, 1 ] : ( typeof numMonths === "number" ? [ 1, numMonths ] : numMonths ) );
	},

	/* Determine the current maximum date - ensure no time components are set. */
	_getMinMaxDate: function( inst, minMax ) {
		return this._determineDate( inst, this._get( inst, minMax + "Date" ), null );
	},

	/* Find the number of days in a given month. */
	_getDaysInMonth: function( year, month ) {
		return 32 - this._daylightSavingAdjust( new Date( year, month, 32 ) ).getDate();
	},

	/* Find the day of the week of the first of a month. */
	_getFirstDayOfMonth: function( year, month ) {
		return new Date( year, month, 1 ).getDay();
	},

	/* Determines if we should allow a "next/prev" month display change. */
	_canAdjustMonth: function( inst, offset, curYear, curMonth ) {
		var numMonths = this._getNumberOfMonths( inst ),
			date = this._daylightSavingAdjust( new Date( curYear,
			curMonth + ( offset < 0 ? offset : numMonths[ 0 ] * numMonths[ 1 ] ), 1 ) );

		if ( offset < 0 ) {
			date.setDate( this._getDaysInMonth( date.getFullYear(), date.getMonth() ) );
		}
		return this._isInRange( inst, date );
	},

	/* Is the given date in the accepted range? */
	_isInRange: function( inst, date ) {
		var yearSplit, currentYear,
			minDate = this._getMinMaxDate( inst, "min" ),
			maxDate = this._getMinMaxDate( inst, "max" ),
			minYear = null,
			maxYear = null,
			years = this._get( inst, "yearRange" );
			if ( years ) {
				yearSplit = years.split( ":" );
				currentYear = new Date().getFullYear();
				minYear = parseInt( yearSplit[ 0 ], 10 );
				maxYear = parseInt( yearSplit[ 1 ], 10 );
				if ( yearSplit[ 0 ].match( /[+\-].*/ ) ) {
					minYear += currentYear;
				}
				if ( yearSplit[ 1 ].match( /[+\-].*/ ) ) {
					maxYear += currentYear;
				}
			}

		return ( ( !minDate || date.getTime() >= minDate.getTime() ) &&
			( !maxDate || date.getTime() <= maxDate.getTime() ) &&
			( !minYear || date.getFullYear() >= minYear ) &&
			( !maxYear || date.getFullYear() <= maxYear ) );
	},

	/* Provide the configuration settings for formatting/parsing. */
	_getFormatConfig: function( inst ) {
		var shortYearCutoff = this._get( inst, "shortYearCutoff" );
		shortYearCutoff = ( typeof shortYearCutoff !== "string" ? shortYearCutoff :
			new Date().getFullYear() % 100 + parseInt( shortYearCutoff, 10 ) );
		return { shortYearCutoff: shortYearCutoff,
			dayNamesShort: this._get( inst, "dayNamesShort" ), dayNames: this._get( inst, "dayNames" ),
			monthNamesShort: this._get( inst, "monthNamesShort" ), monthNames: this._get( inst, "monthNames" ) };
	},

	/* Format the given date for display. */
	_formatDate: function( inst, day, month, year ) {
		if ( !day ) {
			inst.currentDay = inst.selectedDay;
			inst.currentMonth = inst.selectedMonth;
			inst.currentYear = inst.selectedYear;
		}
		var date = ( day ? ( typeof day === "object" ? day :
			this._daylightSavingAdjust( new Date( year, month, day ) ) ) :
			this._daylightSavingAdjust( new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
		return this.formatDate( this._get( inst, "dateFormat" ), date, this._getFormatConfig( inst ) );
	}
} );

/*
 * Bind hover events for datepicker elements.
 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
 * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
 */
function datepicker_bindHover( dpDiv ) {
	var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
	return dpDiv.on( "mouseout", selector, function() {
			$( this ).removeClass( "ui-state-hover" );
			if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
				$( this ).removeClass( "ui-datepicker-prev-hover" );
			}
			if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
				$( this ).removeClass( "ui-datepicker-next-hover" );
			}
		} )
		.on( "mouseover", selector, datepicker_handleMouseover );
}

function datepicker_handleMouseover() {
	if ( !$.datepicker._isDisabledDatepicker( datepicker_instActive.inline ? datepicker_instActive.dpDiv.parent()[ 0 ] : datepicker_instActive.input[ 0 ] ) ) {
		$( this ).parents( ".ui-datepicker-calendar" ).find( "a" ).removeClass( "ui-state-hover" );
		$( this ).addClass( "ui-state-hover" );
		if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
			$( this ).addClass( "ui-datepicker-prev-hover" );
		}
		if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
			$( this ).addClass( "ui-datepicker-next-hover" );
		}
	}
}

/* jQuery extend now ignores nulls! */
function datepicker_extendRemove( target, props ) {
	$.extend( target, props );
	for ( var name in props ) {
		if ( props[ name ] == null ) {
			target[ name ] = props[ name ];
		}
	}
	return target;
}

/* Invoke the datepicker functionality.
   @param  options  string - a command, optionally followed by additional parameters or
					Object - settings for attaching new datepicker functionality
   @return  jQuery object */
$.fn.datepicker = function( options ) {

	/* Verify an empty collection wasn't passed - Fixes #6976 */
	if ( !this.length ) {
		return this;
	}

	/* Initialise the date picker. */
	if ( !$.datepicker.initialized ) {
		$( document ).on( "mousedown", $.datepicker._checkExternalClick );
		$.datepicker.initialized = true;
	}

	/* Append datepicker main container to body if not exist. */
	if ( $( "#" + $.datepicker._mainDivId ).length === 0 ) {
		$( "body" ).append( $.datepicker.dpDiv );
	}

	var otherArgs = Array.prototype.slice.call( arguments, 1 );
	if ( typeof options === "string" && ( options === "isDisabled" || options === "getDate" || options === "widget" ) ) {
		return $.datepicker[ "_" + options + "Datepicker" ].
			apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
	}
	if ( options === "option" && arguments.length === 2 && typeof arguments[ 1 ] === "string" ) {
		return $.datepicker[ "_" + options + "Datepicker" ].
			apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
	}
	return this.each( function() {
		typeof options === "string" ?
			$.datepicker[ "_" + options + "Datepicker" ].
				apply( $.datepicker, [ this ].concat( otherArgs ) ) :
			$.datepicker._attachDatepicker( this, options );
	} );
};

$.datepicker = new Datepicker(); // singleton instance
$.datepicker.initialized = false;
$.datepicker.uuid = new Date().getTime();
$.datepicker.version = "1.12.1";

var widgetsDatepicker = $.datepicker;




// This file is deprecated
var ie = $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );

/*!
 * jQuery UI Mouse 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Mouse
//>>group: Widgets
//>>description: Abstracts mouse-based interactions to assist in creating certain widgets.
//>>docs: http://api.jqueryui.com/mouse/



var mouseHandled = false;
$( document ).on( "mouseup", function() {
	mouseHandled = false;
} );

var widgetsMouse = $.widget( "ui.mouse", {
	version: "1.12.1",
	options: {
		cancel: "input, textarea, button, select, option",
		distance: 1,
		delay: 0
	},
	_mouseInit: function() {
		var that = this;

		this.element
			.on( "mousedown." + this.widgetName, function( event ) {
				return that._mouseDown( event );
			} )
			.on( "click." + this.widgetName, function( event ) {
				if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) {
					$.removeData( event.target, that.widgetName + ".preventClickEvent" );
					event.stopImmediatePropagation();
					return false;
				}
			} );

		this.started = false;
	},

	// TODO: make sure destroying one instance of mouse doesn't mess with
	// other instances of mouse
	_mouseDestroy: function() {
		this.element.off( "." + this.widgetName );
		if ( this._mouseMoveDelegate ) {
			this.document
				.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
				.off( "mouseup." + this.widgetName, this._mouseUpDelegate );
		}
	},

	_mouseDown: function( event ) {

		// don't let more than one widget handle mouseStart
		if ( mouseHandled ) {
			return;
		}

		this._mouseMoved = false;

		// We may have missed mouseup (out of window)
		( this._mouseStarted && this._mouseUp( event ) );

		this._mouseDownEvent = event;

		var that = this,
			btnIsLeft = ( event.which === 1 ),

			// event.target.nodeName works around a bug in IE 8 with
			// disabled inputs (#7620)
			elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ?
				$( event.target ).closest( this.options.cancel ).length : false );
		if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) {
			return true;
		}

		this.mouseDelayMet = !this.options.delay;
		if ( !this.mouseDelayMet ) {
			this._mouseDelayTimer = setTimeout( function() {
				that.mouseDelayMet = true;
			}, this.options.delay );
		}

		if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
			this._mouseStarted = ( this._mouseStart( event ) !== false );
			if ( !this._mouseStarted ) {
				event.preventDefault();
				return true;
			}
		}

		// Click event may never have fired (Gecko & Opera)
		if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) {
			$.removeData( event.target, this.widgetName + ".preventClickEvent" );
		}

		// These delegates are required to keep context
		this._mouseMoveDelegate = function( event ) {
			return that._mouseMove( event );
		};
		this._mouseUpDelegate = function( event ) {
			return that._mouseUp( event );
		};

		this.document
			.on( "mousemove." + this.widgetName, this._mouseMoveDelegate )
			.on( "mouseup." + this.widgetName, this._mouseUpDelegate );

		event.preventDefault();

		mouseHandled = true;
		return true;
	},

	_mouseMove: function( event ) {

		// Only check for mouseups outside the document if you've moved inside the document
		// at least once. This prevents the firing of mouseup in the case of IE<9, which will
		// fire a mousemove event if content is placed under the cursor. See #7778
		// Support: IE <9
		if ( this._mouseMoved ) {

			// IE mouseup check - mouseup happened when mouse was out of window
			if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) &&
					!event.button ) {
				return this._mouseUp( event );

			// Iframe mouseup check - mouseup occurred in another document
			} else if ( !event.which ) {

				// Support: Safari <=8 - 9
				// Safari sets which to 0 if you press any of the following keys
				// during a drag (#14461)
				if ( event.originalEvent.altKey || event.originalEvent.ctrlKey ||
						event.originalEvent.metaKey || event.originalEvent.shiftKey ) {
					this.ignoreMissingWhich = true;
				} else if ( !this.ignoreMissingWhich ) {
					return this._mouseUp( event );
				}
			}
		}

		if ( event.which || event.button ) {
			this._mouseMoved = true;
		}

		if ( this._mouseStarted ) {
			this._mouseDrag( event );
			return event.preventDefault();
		}

		if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
			this._mouseStarted =
				( this._mouseStart( this._mouseDownEvent, event ) !== false );
			( this._mouseStarted ? this._mouseDrag( event ) : this._mouseUp( event ) );
		}

		return !this._mouseStarted;
	},

	_mouseUp: function( event ) {
		this.document
			.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
			.off( "mouseup." + this.widgetName, this._mouseUpDelegate );

		if ( this._mouseStarted ) {
			this._mouseStarted = false;

			if ( event.target === this._mouseDownEvent.target ) {
				$.data( event.target, this.widgetName + ".preventClickEvent", true );
			}

			this._mouseStop( event );
		}

		if ( this._mouseDelayTimer ) {
			clearTimeout( this._mouseDelayTimer );
			delete this._mouseDelayTimer;
		}

		this.ignoreMissingWhich = false;
		mouseHandled = false;
		event.preventDefault();
	},

	_mouseDistanceMet: function( event ) {
		return ( Math.max(
				Math.abs( this._mouseDownEvent.pageX - event.pageX ),
				Math.abs( this._mouseDownEvent.pageY - event.pageY )
			) >= this.options.distance
		);
	},

	_mouseDelayMet: function( /* event */ ) {
		return this.mouseDelayMet;
	},

	// These are placeholder methods, to be overriden by extending plugin
	_mouseStart: function( /* event */ ) {},
	_mouseDrag: function( /* event */ ) {},
	_mouseStop: function( /* event */ ) {},
	_mouseCapture: function( /* event */ ) { return true; }
} );




// $.ui.plugin is deprecated. Use $.widget() extensions instead.
var plugin = $.ui.plugin = {
	add: function( module, option, set ) {
		var i,
			proto = $.ui[ module ].prototype;
		for ( i in set ) {
			proto.plugins[ i ] = proto.plugins[ i ] || [];
			proto.plugins[ i ].push( [ option, set[ i ] ] );
		}
	},
	call: function( instance, name, args, allowDisconnected ) {
		var i,
			set = instance.plugins[ name ];

		if ( !set ) {
			return;
		}

		if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode ||
				instance.element[ 0 ].parentNode.nodeType === 11 ) ) {
			return;
		}

		for ( i = 0; i < set.length; i++ ) {
			if ( instance.options[ set[ i ][ 0 ] ] ) {
				set[ i ][ 1 ].apply( instance.element, args );
			}
		}
	}
};



var safeBlur = $.ui.safeBlur = function( element ) {

	// Support: IE9 - 10 only
	// If the <body> is blurred, IE will switch windows, see #9420
	if ( element && element.nodeName.toLowerCase() !== "body" ) {
		$( element ).trigger( "blur" );
	}
};


/*!
 * jQuery UI Draggable 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Draggable
//>>group: Interactions
//>>description: Enables dragging functionality for any element.
//>>docs: http://api.jqueryui.com/draggable/
//>>demos: http://jqueryui.com/draggable/
//>>css.structure: ../../themes/base/draggable.css



$.widget( "ui.draggable", $.ui.mouse, {
	version: "1.12.1",
	widgetEventPrefix: "drag",
	options: {
		addClasses: true,
		appendTo: "parent",
		axis: false,
		connectToSortable: false,
		containment: false,
		cursor: "auto",
		cursorAt: false,
		grid: false,
		handle: false,
		helper: "original",
		iframeFix: false,
		opacity: false,
		refreshPositions: false,
		revert: false,
		revertDuration: 500,
		scope: "default",
		scroll: true,
		scrollSensitivity: 20,
		scrollSpeed: 20,
		snap: false,
		snapMode: "both",
		snapTolerance: 20,
		stack: false,
		zIndex: false,

		// Callbacks
		drag: null,
		start: null,
		stop: null
	},
	_create: function() {

		if ( this.options.helper === "original" ) {
			this._setPositionRelative();
		}
		if ( this.options.addClasses ) {
			this._addClass( "ui-draggable" );
		}
		this._setHandleClassName();

		this._mouseInit();
	},

	_setOption: function( key, value ) {
		this._super( key, value );
		if ( key === "handle" ) {
			this._removeHandleClassName();
			this._setHandleClassName();
		}
	},

	_destroy: function() {
		if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
			this.destroyOnClear = true;
			return;
		}
		this._removeHandleClassName();
		this._mouseDestroy();
	},

	_mouseCapture: function( event ) {
		var o = this.options;

		// Among others, prevent a drag on a resizable-handle
		if ( this.helper || o.disabled ||
				$( event.target ).closest( ".ui-resizable-handle" ).length > 0 ) {
			return false;
		}

		//Quit if we're not on a valid handle
		this.handle = this._getHandle( event );
		if ( !this.handle ) {
			return false;
		}

		this._blurActiveElement( event );

		this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );

		return true;

	},

	_blockFrames: function( selector ) {
		this.iframeBlocks = this.document.find( selector ).map( function() {
			var iframe = $( this );

			return $( "<div>" )
				.css( "position", "absolute" )
				.appendTo( iframe.parent() )
				.outerWidth( iframe.outerWidth() )
				.outerHeight( iframe.outerHeight() )
				.offset( iframe.offset() )[ 0 ];
		} );
	},

	_unblockFrames: function() {
		if ( this.iframeBlocks ) {
			this.iframeBlocks.remove();
			delete this.iframeBlocks;
		}
	},

	_blurActiveElement: function( event ) {
		var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
			target = $( event.target );

		// Don't blur if the event occurred on an element that is within
		// the currently focused element
		// See #10527, #12472
		if ( target.closest( activeElement ).length ) {
			return;
		}

		// Blur any element that currently has focus, see #4261
		$.ui.safeBlur( activeElement );
	},

	_mouseStart: function( event ) {

		var o = this.options;

		//Create and append the visible helper
		this.helper = this._createHelper( event );

		this._addClass( this.helper, "ui-draggable-dragging" );

		//Cache the helper size
		this._cacheHelperProportions();

		//If ddmanager is used for droppables, set the global draggable
		if ( $.ui.ddmanager ) {
			$.ui.ddmanager.current = this;
		}

		/*
		 * - Position generation -
		 * This block generates everything position related - it's the core of draggables.
		 */

		//Cache the margins of the original element
		this._cacheMargins();

		//Store the helper's css position
		this.cssPosition = this.helper.css( "position" );
		this.scrollParent = this.helper.scrollParent( true );
		this.offsetParent = this.helper.offsetParent();
		this.hasFixedAncestor = this.helper.parents().filter( function() {
				return $( this ).css( "position" ) === "fixed";
			} ).length > 0;

		//The element's absolute position on the page minus margins
		this.positionAbs = this.element.offset();
		this._refreshOffsets( event );

		//Generate the original position
		this.originalPosition = this.position = this._generatePosition( event, false );
		this.originalPageX = event.pageX;
		this.originalPageY = event.pageY;

		//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
		( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) );

		//Set a containment if given in the options
		this._setContainment();

		//Trigger event + callbacks
		if ( this._trigger( "start", event ) === false ) {
			this._clear();
			return false;
		}

		//Recache the helper size
		this._cacheHelperProportions();

		//Prepare the droppable offsets
		if ( $.ui.ddmanager && !o.dropBehaviour ) {
			$.ui.ddmanager.prepareOffsets( this, event );
		}

		// Execute the drag once - this causes the helper not to be visible before getting its
		// correct position
		this._mouseDrag( event, true );

		// If the ddmanager is used for droppables, inform the manager that dragging has started
		// (see #5003)
		if ( $.ui.ddmanager ) {
			$.ui.ddmanager.dragStart( this, event );
		}

		return true;
	},

	_refreshOffsets: function( event ) {
		this.offset = {
			top: this.positionAbs.top - this.margins.top,
			left: this.positionAbs.left - this.margins.left,
			scroll: false,
			parent: this._getParentOffset(),
			relative: this._getRelativeOffset()
		};

		this.offset.click = {
			left: event.pageX - this.offset.left,
			top: event.pageY - this.offset.top
		};
	},

	_mouseDrag: function( event, noPropagation ) {

		// reset any necessary cached properties (see #5009)
		if ( this.hasFixedAncestor ) {
			this.offset.parent = this._getParentOffset();
		}

		//Compute the helpers position
		this.position = this._generatePosition( event, true );
		this.positionAbs = this._convertPositionTo( "absolute" );

		//Call plugins and callbacks and use the resulting position if something is returned
		if ( !noPropagation ) {
			var ui = this._uiHash();
			if ( this._trigger( "drag", event, ui ) === false ) {
				this._mouseUp( new $.Event( "mouseup", event ) );
				return false;
			}
			this.position = ui.position;
		}

		this.helper[ 0 ].style.left = this.position.left + "px";
		this.helper[ 0 ].style.top = this.position.top + "px";

		if ( $.ui.ddmanager ) {
			$.ui.ddmanager.drag( this, event );
		}

		return false;
	},

	_mouseStop: function( event ) {

		//If we are using droppables, inform the manager about the drop
		var that = this,
			dropped = false;
		if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
			dropped = $.ui.ddmanager.drop( this, event );
		}

		//if a drop comes from outside (a sortable)
		if ( this.dropped ) {
			dropped = this.dropped;
			this.dropped = false;
		}

		if ( ( this.options.revert === "invalid" && !dropped ) ||
				( this.options.revert === "valid" && dropped ) ||
				this.options.revert === true || ( $.isFunction( this.options.revert ) &&
				this.options.revert.call( this.element, dropped ) )
		) {
			$( this.helper ).animate(
				this.originalPosition,
				parseInt( this.options.revertDuration, 10 ),
				function() {
					if ( that._trigger( "stop", event ) !== false ) {
						that._clear();
					}
				}
			);
		} else {
			if ( this._trigger( "stop", event ) !== false ) {
				this._clear();
			}
		}

		return false;
	},

	_mouseUp: function( event ) {
		this._unblockFrames();

		// If the ddmanager is used for droppables, inform the manager that dragging has stopped
		// (see #5003)
		if ( $.ui.ddmanager ) {
			$.ui.ddmanager.dragStop( this, event );
		}

		// Only need to focus if the event occurred on the draggable itself, see #10527
		if ( this.handleElement.is( event.target ) ) {

			// The interaction is over; whether or not the click resulted in a drag,
			// focus the element
			this.element.trigger( "focus" );
		}

		return $.ui.mouse.prototype._mouseUp.call( this, event );
	},

	cancel: function() {

		if ( this.helper.is( ".ui-draggable-dragging" ) ) {
			this._mouseUp( new $.Event( "mouseup", { target: this.element[ 0 ] } ) );
		} else {
			this._clear();
		}

		return this;

	},

	_getHandle: function( event ) {
		return this.options.handle ?
			!!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
			true;
	},

	_setHandleClassName: function() {
		this.handleElement = this.options.handle ?
			this.element.find( this.options.handle ) : this.element;
		this._addClass( this.handleElement, "ui-draggable-handle" );
	},

	_removeHandleClassName: function() {
		this._removeClass( this.handleElement, "ui-draggable-handle" );
	},

	_createHelper: function( event ) {

		var o = this.options,
			helperIsFunction = $.isFunction( o.helper ),
			helper = helperIsFunction ?
				$( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
				( o.helper === "clone" ?
					this.element.clone().removeAttr( "id" ) :
					this.element );

		if ( !helper.parents( "body" ).length ) {
			helper.appendTo( ( o.appendTo === "parent" ?
				this.element[ 0 ].parentNode :
				o.appendTo ) );
		}

		// Http://bugs.jqueryui.com/ticket/9446
		// a helper function can return the original element
		// which wouldn't have been set to relative in _create
		if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
			this._setPositionRelative();
		}

		if ( helper[ 0 ] !== this.element[ 0 ] &&
				!( /(fixed|absolute)/ ).test( helper.css( "position" ) ) ) {
			helper.css( "position", "absolute" );
		}

		return helper;

	},

	_setPositionRelative: function() {
		if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
			this.element[ 0 ].style.position = "relative";
		}
	},

	_adjustOffsetFromHelper: function( obj ) {
		if ( typeof obj === "string" ) {
			obj = obj.split( " " );
		}
		if ( $.isArray( obj ) ) {
			obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
		}
		if ( "left" in obj ) {
			this.offset.click.left = obj.left + this.margins.left;
		}
		if ( "right" in obj ) {
			this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
		}
		if ( "top" in obj ) {
			this.offset.click.top = obj.top + this.margins.top;
		}
		if ( "bottom" in obj ) {
			this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
		}
	},

	_isRootNode: function( element ) {
		return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
	},

	_getParentOffset: function() {

		//Get the offsetParent and cache its position
		var po = this.offsetParent.offset(),
			document = this.document[ 0 ];

		// This is a special case where we need to modify a offset calculated on start, since the
		// following happened:
		// 1. The position of the helper is absolute, so it's position is calculated based on the
		// next positioned parent
		// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
		// the document, which means that the scroll is included in the initial calculation of the
		// offset of the parent, and never recalculated upon drag
		if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== document &&
				$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
			po.left += this.scrollParent.scrollLeft();
			po.top += this.scrollParent.scrollTop();
		}

		if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
			po = { top: 0, left: 0 };
		}

		return {
			top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
			left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
		};

	},

	_getRelativeOffset: function() {
		if ( this.cssPosition !== "relative" ) {
			return { top: 0, left: 0 };
		}

		var p = this.element.position(),
			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );

		return {
			top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
				( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
			left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
				( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
		};

	},

	_cacheMargins: function() {
		this.margins = {
			left: ( parseInt( this.element.css( "marginLeft" ), 10 ) || 0 ),
			top: ( parseInt( this.element.css( "marginTop" ), 10 ) || 0 ),
			right: ( parseInt( this.element.css( "marginRight" ), 10 ) || 0 ),
			bottom: ( parseInt( this.element.css( "marginBottom" ), 10 ) || 0 )
		};
	},

	_cacheHelperProportions: function() {
		this.helperProportions = {
			width: this.helper.outerWidth(),
			height: this.helper.outerHeight()
		};
	},

	_setContainment: function() {

		var isUserScrollable, c, ce,
			o = this.options,
			document = this.document[ 0 ];

		this.relativeContainer = null;

		if ( !o.containment ) {
			this.containment = null;
			return;
		}

		if ( o.containment === "window" ) {
			this.containment = [
				$( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
				$( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
				$( window ).scrollLeft() + $( window ).width() -
					this.helperProportions.width - this.margins.left,
				$( window ).scrollTop() +
					( $( window ).height() || document.body.parentNode.scrollHeight ) -
					this.helperProportions.height - this.margins.top
			];
			return;
		}

		if ( o.containment === "document" ) {
			this.containment = [
				0,
				0,
				$( document ).width() - this.helperProportions.width - this.margins.left,
				( $( document ).height() || document.body.parentNode.scrollHeight ) -
					this.helperProportions.height - this.margins.top
			];
			return;
		}

		if ( o.containment.constructor === Array ) {
			this.containment = o.containment;
			return;
		}

		if ( o.containment === "parent" ) {
			o.containment = this.helper[ 0 ].parentNode;
		}

		c = $( o.containment );
		ce = c[ 0 ];

		if ( !ce ) {
			return;
		}

		isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );

		this.containment = [
			( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) +
				( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
			( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) +
				( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
			( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
				( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
				( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
				this.helperProportions.width -
				this.margins.left -
				this.margins.right,
			( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
				( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
				( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
				this.helperProportions.height -
				this.margins.top -
				this.margins.bottom
		];
		this.relativeContainer = c;
	},

	_convertPositionTo: function( d, pos ) {

		if ( !pos ) {
			pos = this.position;
		}

		var mod = d === "absolute" ? 1 : -1,
			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );

		return {
			top: (

				// The absolute mouse position
				pos.top	+

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.top * mod +

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.top * mod -
				( ( this.cssPosition === "fixed" ?
					-this.offset.scroll.top :
					( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod )
			),
			left: (

				// The absolute mouse position
				pos.left +

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.left * mod +

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.left * mod	-
				( ( this.cssPosition === "fixed" ?
					-this.offset.scroll.left :
					( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod )
			)
		};

	},

	_generatePosition: function( event, constrainPosition ) {

		var containment, co, top, left,
			o = this.options,
			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
			pageX = event.pageX,
			pageY = event.pageY;

		// Cache the scroll
		if ( !scrollIsRootNode || !this.offset.scroll ) {
			this.offset.scroll = {
				top: this.scrollParent.scrollTop(),
				left: this.scrollParent.scrollLeft()
			};
		}

		/*
		 * - Position constraining -
		 * Constrain the position to a mix of grid, containment.
		 */

		// If we are not dragging yet, we won't check for options
		if ( constrainPosition ) {
			if ( this.containment ) {
				if ( this.relativeContainer ) {
					co = this.relativeContainer.offset();
					containment = [
						this.containment[ 0 ] + co.left,
						this.containment[ 1 ] + co.top,
						this.containment[ 2 ] + co.left,
						this.containment[ 3 ] + co.top
					];
				} else {
					containment = this.containment;
				}

				if ( event.pageX - this.offset.click.left < containment[ 0 ] ) {
					pageX = containment[ 0 ] + this.offset.click.left;
				}
				if ( event.pageY - this.offset.click.top < containment[ 1 ] ) {
					pageY = containment[ 1 ] + this.offset.click.top;
				}
				if ( event.pageX - this.offset.click.left > containment[ 2 ] ) {
					pageX = containment[ 2 ] + this.offset.click.left;
				}
				if ( event.pageY - this.offset.click.top > containment[ 3 ] ) {
					pageY = containment[ 3 ] + this.offset.click.top;
				}
			}

			if ( o.grid ) {

				//Check for grid elements set to 0 to prevent divide by 0 error causing invalid
				// argument errors in IE (see ticket #6950)
				top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY -
					this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY;
				pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] ||
					top - this.offset.click.top > containment[ 3 ] ) ?
						top :
						( ( top - this.offset.click.top >= containment[ 1 ] ) ?
							top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top;

				left = o.grid[ 0 ] ? this.originalPageX +
					Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] :
					this.originalPageX;
				pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] ||
					left - this.offset.click.left > containment[ 2 ] ) ?
						left :
						( ( left - this.offset.click.left >= containment[ 0 ] ) ?
							left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left;
			}

			if ( o.axis === "y" ) {
				pageX = this.originalPageX;
			}

			if ( o.axis === "x" ) {
				pageY = this.originalPageY;
			}
		}

		return {
			top: (

				// The absolute mouse position
				pageY -

				// Click offset (relative to the element)
				this.offset.click.top -

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.top -

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.top +
				( this.cssPosition === "fixed" ?
					-this.offset.scroll.top :
					( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
			),
			left: (

				// The absolute mouse position
				pageX -

				// Click offset (relative to the element)
				this.offset.click.left -

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.left -

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.left +
				( this.cssPosition === "fixed" ?
					-this.offset.scroll.left :
					( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
			)
		};

	},

	_clear: function() {
		this._removeClass( this.helper, "ui-draggable-dragging" );
		if ( this.helper[ 0 ] !== this.element[ 0 ] && !this.cancelHelperRemoval ) {
			this.helper.remove();
		}
		this.helper = null;
		this.cancelHelperRemoval = false;
		if ( this.destroyOnClear ) {
			this.destroy();
		}
	},

	// From now on bulk stuff - mainly helpers

	_trigger: function( type, event, ui ) {
		ui = ui || this._uiHash();
		$.ui.plugin.call( this, type, [ event, ui, this ], true );

		// Absolute position and offset (see #6884 ) have to be recalculated after plugins
		if ( /^(drag|start|stop)/.test( type ) ) {
			this.positionAbs = this._convertPositionTo( "absolute" );
			ui.offset = this.positionAbs;
		}
		return $.Widget.prototype._trigger.call( this, type, event, ui );
	},

	plugins: {},

	_uiHash: function() {
		return {
			helper: this.helper,
			position: this.position,
			originalPosition: this.originalPosition,
			offset: this.positionAbs
		};
	}

} );

$.ui.plugin.add( "draggable", "connectToSortable", {
	start: function( event, ui, draggable ) {
		var uiSortable = $.extend( {}, ui, {
			item: draggable.element
		} );

		draggable.sortables = [];
		$( draggable.options.connectToSortable ).each( function() {
			var sortable = $( this ).sortable( "instance" );

			if ( sortable && !sortable.options.disabled ) {
				draggable.sortables.push( sortable );

				// RefreshPositions is called at drag start to refresh the containerCache
				// which is used in drag. This ensures it's initialized and synchronized
				// with any changes that might have happened on the page since initialization.
				sortable.refreshPositions();
				sortable._trigger( "activate", event, uiSortable );
			}
		} );
	},
	stop: function( event, ui, draggable ) {
		var uiSortable = $.extend( {}, ui, {
			item: draggable.element
		} );

		draggable.cancelHelperRemoval = false;

		$.each( draggable.sortables, function() {
			var sortable = this;

			if ( sortable.isOver ) {
				sortable.isOver = 0;

				// Allow this sortable to handle removing the helper
				draggable.cancelHelperRemoval = true;
				sortable.cancelHelperRemoval = false;

				// Use _storedCSS To restore properties in the sortable,
				// as this also handles revert (#9675) since the draggable
				// may have modified them in unexpected ways (#8809)
				sortable._storedCSS = {
					position: sortable.placeholder.css( "position" ),
					top: sortable.placeholder.css( "top" ),
					left: sortable.placeholder.css( "left" )
				};

				sortable._mouseStop( event );

				// Once drag has ended, the sortable should return to using
				// its original helper, not the shared helper from draggable
				sortable.options.helper = sortable.options._helper;
			} else {

				// Prevent this Sortable from removing the helper.
				// However, don't set the draggable to remove the helper
				// either as another connected Sortable may yet handle the removal.
				sortable.cancelHelperRemoval = true;

				sortable._trigger( "deactivate", event, uiSortable );
			}
		} );
	},
	drag: function( event, ui, draggable ) {
		$.each( draggable.sortables, function() {
			var innermostIntersecting = false,
				sortable = this;

			// Copy over variables that sortable's _intersectsWith uses
			sortable.positionAbs = draggable.positionAbs;
			sortable.helperProportions = draggable.helperProportions;
			sortable.offset.click = draggable.offset.click;

			if ( sortable._intersectsWith( sortable.containerCache ) ) {
				innermostIntersecting = true;

				$.each( draggable.sortables, function() {

					// Copy over variables that sortable's _intersectsWith uses
					this.positionAbs = draggable.positionAbs;
					this.helperProportions = draggable.helperProportions;
					this.offset.click = draggable.offset.click;

					if ( this !== sortable &&
							this._intersectsWith( this.containerCache ) &&
							$.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
						innermostIntersecting = false;
					}

					return innermostIntersecting;
				} );
			}

			if ( innermostIntersecting ) {

				// If it intersects, we use a little isOver variable and set it once,
				// so that the move-in stuff gets fired only once.
				if ( !sortable.isOver ) {
					sortable.isOver = 1;

					// Store draggable's parent in case we need to reappend to it later.
					draggable._parent = ui.helper.parent();

					sortable.currentItem = ui.helper
						.appendTo( sortable.element )
						.data( "ui-sortable-item", true );

					// Store helper option to later restore it
					sortable.options._helper = sortable.options.helper;

					sortable.options.helper = function() {
						return ui.helper[ 0 ];
					};

					// Fire the start events of the sortable with our passed browser event,
					// and our own helper (so it doesn't create a new one)
					event.target = sortable.currentItem[ 0 ];
					sortable._mouseCapture( event, true );
					sortable._mouseStart( event, true, true );

					// Because the browser event is way off the new appended portlet,
					// modify necessary variables to reflect the changes
					sortable.offset.click.top = draggable.offset.click.top;
					sortable.offset.click.left = draggable.offset.click.left;
					sortable.offset.parent.left -= draggable.offset.parent.left -
						sortable.offset.parent.left;
					sortable.offset.parent.top -= draggable.offset.parent.top -
						sortable.offset.parent.top;

					draggable._trigger( "toSortable", event );

					// Inform draggable that the helper is in a valid drop zone,
					// used solely in the revert option to handle "valid/invalid".
					draggable.dropped = sortable.element;

					// Need to refreshPositions of all sortables in the case that
					// adding to one sortable changes the location of the other sortables (#9675)
					$.each( draggable.sortables, function() {
						this.refreshPositions();
					} );

					// Hack so receive/update callbacks work (mostly)
					draggable.currentItem = draggable.element;
					sortable.fromOutside = draggable;
				}

				if ( sortable.currentItem ) {
					sortable._mouseDrag( event );

					// Copy the sortable's position because the draggable's can potentially reflect
					// a relative position, while sortable is always absolute, which the dragged
					// element has now become. (#8809)
					ui.position = sortable.position;
				}
			} else {

				// If it doesn't intersect with the sortable, and it intersected before,
				// we fake the drag stop of the sortable, but make sure it doesn't remove
				// the helper by using cancelHelperRemoval.
				if ( sortable.isOver ) {

					sortable.isOver = 0;
					sortable.cancelHelperRemoval = true;

					// Calling sortable's mouseStop would trigger a revert,
					// so revert must be temporarily false until after mouseStop is called.
					sortable.options._revert = sortable.options.revert;
					sortable.options.revert = false;

					sortable._trigger( "out", event, sortable._uiHash( sortable ) );
					sortable._mouseStop( event, true );

					// Restore sortable behaviors that were modfied
					// when the draggable entered the sortable area (#9481)
					sortable.options.revert = sortable.options._revert;
					sortable.options.helper = sortable.options._helper;

					if ( sortable.placeholder ) {
						sortable.placeholder.remove();
					}

					// Restore and recalculate the draggable's offset considering the sortable
					// may have modified them in unexpected ways. (#8809, #10669)
					ui.helper.appendTo( draggable._parent );
					draggable._refreshOffsets( event );
					ui.position = draggable._generatePosition( event, true );

					draggable._trigger( "fromSortable", event );

					// Inform draggable that the helper is no longer in a valid drop zone
					draggable.dropped = false;

					// Need to refreshPositions of all sortables just in case removing
					// from one sortable changes the location of other sortables (#9675)
					$.each( draggable.sortables, function() {
						this.refreshPositions();
					} );
				}
			}
		} );
	}
} );

$.ui.plugin.add( "draggable", "cursor", {
	start: function( event, ui, instance ) {
		var t = $( "body" ),
			o = instance.options;

		if ( t.css( "cursor" ) ) {
			o._cursor = t.css( "cursor" );
		}
		t.css( "cursor", o.cursor );
	},
	stop: function( event, ui, instance ) {
		var o = instance.options;
		if ( o._cursor ) {
			$( "body" ).css( "cursor", o._cursor );
		}
	}
} );

$.ui.plugin.add( "draggable", "opacity", {
	start: function( event, ui, instance ) {
		var t = $( ui.helper ),
			o = instance.options;
		if ( t.css( "opacity" ) ) {
			o._opacity = t.css( "opacity" );
		}
		t.css( "opacity", o.opacity );
	},
	stop: function( event, ui, instance ) {
		var o = instance.options;
		if ( o._opacity ) {
			$( ui.helper ).css( "opacity", o._opacity );
		}
	}
} );

$.ui.plugin.add( "draggable", "scroll", {
	start: function( event, ui, i ) {
		if ( !i.scrollParentNotHidden ) {
			i.scrollParentNotHidden = i.helper.scrollParent( false );
		}

		if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] &&
				i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
			i.overflowOffset = i.scrollParentNotHidden.offset();
		}
	},
	drag: function( event, ui, i  ) {

		var o = i.options,
			scrolled = false,
			scrollParent = i.scrollParentNotHidden[ 0 ],
			document = i.document[ 0 ];

		if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
			if ( !o.axis || o.axis !== "x" ) {
				if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY <
						o.scrollSensitivity ) {
					scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
				} else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
					scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
				}
			}

			if ( !o.axis || o.axis !== "y" ) {
				if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX <
						o.scrollSensitivity ) {
					scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
				} else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
					scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
				}
			}

		} else {

			if ( !o.axis || o.axis !== "x" ) {
				if ( event.pageY - $( document ).scrollTop() < o.scrollSensitivity ) {
					scrolled = $( document ).scrollTop( $( document ).scrollTop() - o.scrollSpeed );
				} else if ( $( window ).height() - ( event.pageY - $( document ).scrollTop() ) <
						o.scrollSensitivity ) {
					scrolled = $( document ).scrollTop( $( document ).scrollTop() + o.scrollSpeed );
				}
			}

			if ( !o.axis || o.axis !== "y" ) {
				if ( event.pageX - $( document ).scrollLeft() < o.scrollSensitivity ) {
					scrolled = $( document ).scrollLeft(
						$( document ).scrollLeft() - o.scrollSpeed
					);
				} else if ( $( window ).width() - ( event.pageX - $( document ).scrollLeft() ) <
						o.scrollSensitivity ) {
					scrolled = $( document ).scrollLeft(
						$( document ).scrollLeft() + o.scrollSpeed
					);
				}
			}

		}

		if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
			$.ui.ddmanager.prepareOffsets( i, event );
		}

	}
} );

$.ui.plugin.add( "draggable", "snap", {
	start: function( event, ui, i ) {

		var o = i.options;

		i.snapElements = [];

		$( o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap )
			.each( function() {
				var $t = $( this ),
					$o = $t.offset();
				if ( this !== i.element[ 0 ] ) {
					i.snapElements.push( {
						item: this,
						width: $t.outerWidth(), height: $t.outerHeight(),
						top: $o.top, left: $o.left
					} );
				}
			} );

	},
	drag: function( event, ui, inst ) {

		var ts, bs, ls, rs, l, r, t, b, i, first,
			o = inst.options,
			d = o.snapTolerance,
			x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
			y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;

		for ( i = inst.snapElements.length - 1; i >= 0; i-- ) {

			l = inst.snapElements[ i ].left - inst.margins.left;
			r = l + inst.snapElements[ i ].width;
			t = inst.snapElements[ i ].top - inst.margins.top;
			b = t + inst.snapElements[ i ].height;

			if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d ||
					!$.contains( inst.snapElements[ i ].item.ownerDocument,
					inst.snapElements[ i ].item ) ) {
				if ( inst.snapElements[ i ].snapping ) {
					( inst.options.snap.release &&
						inst.options.snap.release.call(
							inst.element,
							event,
							$.extend( inst._uiHash(), { snapItem: inst.snapElements[ i ].item } )
						) );
				}
				inst.snapElements[ i ].snapping = false;
				continue;
			}

			if ( o.snapMode !== "inner" ) {
				ts = Math.abs( t - y2 ) <= d;
				bs = Math.abs( b - y1 ) <= d;
				ls = Math.abs( l - x2 ) <= d;
				rs = Math.abs( r - x1 ) <= d;
				if ( ts ) {
					ui.position.top = inst._convertPositionTo( "relative", {
						top: t - inst.helperProportions.height,
						left: 0
					} ).top;
				}
				if ( bs ) {
					ui.position.top = inst._convertPositionTo( "relative", {
						top: b,
						left: 0
					} ).top;
				}
				if ( ls ) {
					ui.position.left = inst._convertPositionTo( "relative", {
						top: 0,
						left: l - inst.helperProportions.width
					} ).left;
				}
				if ( rs ) {
					ui.position.left = inst._convertPositionTo( "relative", {
						top: 0,
						left: r
					} ).left;
				}
			}

			first = ( ts || bs || ls || rs );

			if ( o.snapMode !== "outer" ) {
				ts = Math.abs( t - y1 ) <= d;
				bs = Math.abs( b - y2 ) <= d;
				ls = Math.abs( l - x1 ) <= d;
				rs = Math.abs( r - x2 ) <= d;
				if ( ts ) {
					ui.position.top = inst._convertPositionTo( "relative", {
						top: t,
						left: 0
					} ).top;
				}
				if ( bs ) {
					ui.position.top = inst._convertPositionTo( "relative", {
						top: b - inst.helperProportions.height,
						left: 0
					} ).top;
				}
				if ( ls ) {
					ui.position.left = inst._convertPositionTo( "relative", {
						top: 0,
						left: l
					} ).left;
				}
				if ( rs ) {
					ui.position.left = inst._convertPositionTo( "relative", {
						top: 0,
						left: r - inst.helperProportions.width
					} ).left;
				}
			}

			if ( !inst.snapElements[ i ].snapping && ( ts || bs || ls || rs || first ) ) {
				( inst.options.snap.snap &&
					inst.options.snap.snap.call(
						inst.element,
						event,
						$.extend( inst._uiHash(), {
							snapItem: inst.snapElements[ i ].item
						} ) ) );
			}
			inst.snapElements[ i ].snapping = ( ts || bs || ls || rs || first );

		}

	}
} );

$.ui.plugin.add( "draggable", "stack", {
	start: function( event, ui, instance ) {
		var min,
			o = instance.options,
			group = $.makeArray( $( o.stack ) ).sort( function( a, b ) {
				return ( parseInt( $( a ).css( "zIndex" ), 10 ) || 0 ) -
					( parseInt( $( b ).css( "zIndex" ), 10 ) || 0 );
			} );

		if ( !group.length ) { return; }

		min = parseInt( $( group[ 0 ] ).css( "zIndex" ), 10 ) || 0;
		$( group ).each( function( i ) {
			$( this ).css( "zIndex", min + i );
		} );
		this.css( "zIndex", ( min + group.length ) );
	}
} );

$.ui.plugin.add( "draggable", "zIndex", {
	start: function( event, ui, instance ) {
		var t = $( ui.helper ),
			o = instance.options;

		if ( t.css( "zIndex" ) ) {
			o._zIndex = t.css( "zIndex" );
		}
		t.css( "zIndex", o.zIndex );
	},
	stop: function( event, ui, instance ) {
		var o = instance.options;

		if ( o._zIndex ) {
			$( ui.helper ).css( "zIndex", o._zIndex );
		}
	}
} );

var widgetsDraggable = $.ui.draggable;


/*!
 * jQuery UI Resizable 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Resizable
//>>group: Interactions
//>>description: Enables resize functionality for any element.
//>>docs: http://api.jqueryui.com/resizable/
//>>demos: http://jqueryui.com/resizable/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/resizable.css
//>>css.theme: ../../themes/base/theme.css



$.widget( "ui.resizable", $.ui.mouse, {
	version: "1.12.1",
	widgetEventPrefix: "resize",
	options: {
		alsoResize: false,
		animate: false,
		animateDuration: "slow",
		animateEasing: "swing",
		aspectRatio: false,
		autoHide: false,
		classes: {
			"ui-resizable-se": "ui-icon ui-icon-gripsmall-diagonal-se"
		},
		containment: false,
		ghost: false,
		grid: false,
		handles: "e,s,se",
		helper: false,
		maxHeight: null,
		maxWidth: null,
		minHeight: 10,
		minWidth: 10,

		// See #7960
		zIndex: 90,

		// Callbacks
		resize: null,
		start: null,
		stop: null
	},

	_num: function( value ) {
		return parseFloat( value ) || 0;
	},

	_isNumber: function( value ) {
		return !isNaN( parseFloat( value ) );
	},

	_hasScroll: function( el, a ) {

		if ( $( el ).css( "overflow" ) === "hidden" ) {
			return false;
		}

		var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
			has = false;

		if ( el[ scroll ] > 0 ) {
			return true;
		}

		// TODO: determine which cases actually cause this to happen
		// if the element doesn't have the scroll set, see if it's possible to
		// set the scroll
		el[ scroll ] = 1;
		has = ( el[ scroll ] > 0 );
		el[ scroll ] = 0;
		return has;
	},

	_create: function() {

		var margins,
			o = this.options,
			that = this;
		this._addClass( "ui-resizable" );

		$.extend( this, {
			_aspectRatio: !!( o.aspectRatio ),
			aspectRatio: o.aspectRatio,
			originalElement: this.element,
			_proportionallyResizeElements: [],
			_helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
		} );

		// Wrap the element if it cannot hold child nodes
		if ( this.element[ 0 ].nodeName.match( /^(canvas|textarea|input|select|button|img)$/i ) ) {

			this.element.wrap(
				$( "<div class='ui-wrapper' style='overflow: hidden;'></div>" ).css( {
					position: this.element.css( "position" ),
					width: this.element.outerWidth(),
					height: this.element.outerHeight(),
					top: this.element.css( "top" ),
					left: this.element.css( "left" )
				} )
			);

			this.element = this.element.parent().data(
				"ui-resizable", this.element.resizable( "instance" )
			);

			this.elementIsWrapper = true;

			margins = {
				marginTop: this.originalElement.css( "marginTop" ),
				marginRight: this.originalElement.css( "marginRight" ),
				marginBottom: this.originalElement.css( "marginBottom" ),
				marginLeft: this.originalElement.css( "marginLeft" )
			};

			this.element.css( margins );
			this.originalElement.css( "margin", 0 );

			// support: Safari
			// Prevent Safari textarea resize
			this.originalResizeStyle = this.originalElement.css( "resize" );
			this.originalElement.css( "resize", "none" );

			this._proportionallyResizeElements.push( this.originalElement.css( {
				position: "static",
				zoom: 1,
				display: "block"
			} ) );

			// Support: IE9
			// avoid IE jump (hard set the margin)
			this.originalElement.css( margins );

			this._proportionallyResize();
		}

		this._setupHandles();

		if ( o.autoHide ) {
			$( this.element )
				.on( "mouseenter", function() {
					if ( o.disabled ) {
						return;
					}
					that._removeClass( "ui-resizable-autohide" );
					that._handles.show();
				} )
				.on( "mouseleave", function() {
					if ( o.disabled ) {
						return;
					}
					if ( !that.resizing ) {
						that._addClass( "ui-resizable-autohide" );
						that._handles.hide();
					}
				} );
		}

		this._mouseInit();
	},

	_destroy: function() {

		this._mouseDestroy();

		var wrapper,
			_destroy = function( exp ) {
				$( exp )
					.removeData( "resizable" )
					.removeData( "ui-resizable" )
					.off( ".resizable" )
					.find( ".ui-resizable-handle" )
						.remove();
			};

		// TODO: Unwrap at same DOM position
		if ( this.elementIsWrapper ) {
			_destroy( this.element );
			wrapper = this.element;
			this.originalElement.css( {
				position: wrapper.css( "position" ),
				width: wrapper.outerWidth(),
				height: wrapper.outerHeight(),
				top: wrapper.css( "top" ),
				left: wrapper.css( "left" )
			} ).insertAfter( wrapper );
			wrapper.remove();
		}

		this.originalElement.css( "resize", this.originalResizeStyle );
		_destroy( this.originalElement );

		return this;
	},

	_setOption: function( key, value ) {
		this._super( key, value );

		switch ( key ) {
		case "handles":
			this._removeHandles();
			this._setupHandles();
			break;
		default:
			break;
		}
	},

	_setupHandles: function() {
		var o = this.options, handle, i, n, hname, axis, that = this;
		this.handles = o.handles ||
			( !$( ".ui-resizable-handle", this.element ).length ?
				"e,s,se" : {
					n: ".ui-resizable-n",
					e: ".ui-resizable-e",
					s: ".ui-resizable-s",
					w: ".ui-resizable-w",
					se: ".ui-resizable-se",
					sw: ".ui-resizable-sw",
					ne: ".ui-resizable-ne",
					nw: ".ui-resizable-nw"
				} );

		this._handles = $();
		if ( this.handles.constructor === String ) {

			if ( this.handles === "all" ) {
				this.handles = "n,e,s,w,se,sw,ne,nw";
			}

			n = this.handles.split( "," );
			this.handles = {};

			for ( i = 0; i < n.length; i++ ) {

				handle = $.trim( n[ i ] );
				hname = "ui-resizable-" + handle;
				axis = $( "<div>" );
				this._addClass( axis, "ui-resizable-handle " + hname );

				axis.css( { zIndex: o.zIndex } );

				this.handles[ handle ] = ".ui-resizable-" + handle;
				this.element.append( axis );
			}

		}

		this._renderAxis = function( target ) {

			var i, axis, padPos, padWrapper;

			target = target || this.element;

			for ( i in this.handles ) {

				if ( this.handles[ i ].constructor === String ) {
					this.handles[ i ] = this.element.children( this.handles[ i ] ).first().show();
				} else if ( this.handles[ i ].jquery || this.handles[ i ].nodeType ) {
					this.handles[ i ] = $( this.handles[ i ] );
					this._on( this.handles[ i ], { "mousedown": that._mouseDown } );
				}

				if ( this.elementIsWrapper &&
						this.originalElement[ 0 ]
							.nodeName
							.match( /^(textarea|input|select|button)$/i ) ) {
					axis = $( this.handles[ i ], this.element );

					padWrapper = /sw|ne|nw|se|n|s/.test( i ) ?
						axis.outerHeight() :
						axis.outerWidth();

					padPos = [ "padding",
						/ne|nw|n/.test( i ) ? "Top" :
						/se|sw|s/.test( i ) ? "Bottom" :
						/^e$/.test( i ) ? "Right" : "Left" ].join( "" );

					target.css( padPos, padWrapper );

					this._proportionallyResize();
				}

				this._handles = this._handles.add( this.handles[ i ] );
			}
		};

		// TODO: make renderAxis a prototype function
		this._renderAxis( this.element );

		this._handles = this._handles.add( this.element.find( ".ui-resizable-handle" ) );
		this._handles.disableSelection();

		this._handles.on( "mouseover", function() {
			if ( !that.resizing ) {
				if ( this.className ) {
					axis = this.className.match( /ui-resizable-(se|sw|ne|nw|n|e|s|w)/i );
				}
				that.axis = axis && axis[ 1 ] ? axis[ 1 ] : "se";
			}
		} );

		if ( o.autoHide ) {
			this._handles.hide();
			this._addClass( "ui-resizable-autohide" );
		}
	},

	_removeHandles: function() {
		this._handles.remove();
	},

	_mouseCapture: function( event ) {
		var i, handle,
			capture = false;

		for ( i in this.handles ) {
			handle = $( this.handles[ i ] )[ 0 ];
			if ( handle === event.target || $.contains( handle, event.target ) ) {
				capture = true;
			}
		}

		return !this.options.disabled && capture;
	},

	_mouseStart: function( event ) {

		var curleft, curtop, cursor,
			o = this.options,
			el = this.element;

		this.resizing = true;

		this._renderProxy();

		curleft = this._num( this.helper.css( "left" ) );
		curtop = this._num( this.helper.css( "top" ) );

		if ( o.containment ) {
			curleft += $( o.containment ).scrollLeft() || 0;
			curtop += $( o.containment ).scrollTop() || 0;
		}

		this.offset = this.helper.offset();
		this.position = { left: curleft, top: curtop };

		this.size = this._helper ? {
				width: this.helper.width(),
				height: this.helper.height()
			} : {
				width: el.width(),
				height: el.height()
			};

		this.originalSize = this._helper ? {
				width: el.outerWidth(),
				height: el.outerHeight()
			} : {
				width: el.width(),
				height: el.height()
			};

		this.sizeDiff = {
			width: el.outerWidth() - el.width(),
			height: el.outerHeight() - el.height()
		};

		this.originalPosition = { left: curleft, top: curtop };
		this.originalMousePosition = { left: event.pageX, top: event.pageY };

		this.aspectRatio = ( typeof o.aspectRatio === "number" ) ?
			o.aspectRatio :
			( ( this.originalSize.width / this.originalSize.height ) || 1 );

		cursor = $( ".ui-resizable-" + this.axis ).css( "cursor" );
		$( "body" ).css( "cursor", cursor === "auto" ? this.axis + "-resize" : cursor );

		this._addClass( "ui-resizable-resizing" );
		this._propagate( "start", event );
		return true;
	},

	_mouseDrag: function( event ) {

		var data, props,
			smp = this.originalMousePosition,
			a = this.axis,
			dx = ( event.pageX - smp.left ) || 0,
			dy = ( event.pageY - smp.top ) || 0,
			trigger = this._change[ a ];

		this._updatePrevProperties();

		if ( !trigger ) {
			return false;
		}

		data = trigger.apply( this, [ event, dx, dy ] );

		this._updateVirtualBoundaries( event.shiftKey );
		if ( this._aspectRatio || event.shiftKey ) {
			data = this._updateRatio( data, event );
		}

		data = this._respectSize( data, event );

		this._updateCache( data );

		this._propagate( "resize", event );

		props = this._applyChanges();

		if ( !this._helper && this._proportionallyResizeElements.length ) {
			this._proportionallyResize();
		}

		if ( !$.isEmptyObject( props ) ) {
			this._updatePrevProperties();
			this._trigger( "resize", event, this.ui() );
			this._applyChanges();
		}

		return false;
	},

	_mouseStop: function( event ) {

		this.resizing = false;
		var pr, ista, soffseth, soffsetw, s, left, top,
			o = this.options, that = this;

		if ( this._helper ) {

			pr = this._proportionallyResizeElements;
			ista = pr.length && ( /textarea/i ).test( pr[ 0 ].nodeName );
			soffseth = ista && this._hasScroll( pr[ 0 ], "left" ) ? 0 : that.sizeDiff.height;
			soffsetw = ista ? 0 : that.sizeDiff.width;

			s = {
				width: ( that.helper.width()  - soffsetw ),
				height: ( that.helper.height() - soffseth )
			};
			left = ( parseFloat( that.element.css( "left" ) ) +
				( that.position.left - that.originalPosition.left ) ) || null;
			top = ( parseFloat( that.element.css( "top" ) ) +
				( that.position.top - that.originalPosition.top ) ) || null;

			if ( !o.animate ) {
				this.element.css( $.extend( s, { top: top, left: left } ) );
			}

			that.helper.height( that.size.height );
			that.helper.width( that.size.width );

			if ( this._helper && !o.animate ) {
				this._proportionallyResize();
			}
		}

		$( "body" ).css( "cursor", "auto" );

		this._removeClass( "ui-resizable-resizing" );

		this._propagate( "stop", event );

		if ( this._helper ) {
			this.helper.remove();
		}

		return false;

	},

	_updatePrevProperties: function() {
		this.prevPosition = {
			top: this.position.top,
			left: this.position.left
		};
		this.prevSize = {
			width: this.size.width,
			height: this.size.height
		};
	},

	_applyChanges: function() {
		var props = {};

		if ( this.position.top !== this.prevPosition.top ) {
			props.top = this.position.top + "px";
		}
		if ( this.position.left !== this.prevPosition.left ) {
			props.left = this.position.left + "px";
		}
		if ( this.size.width !== this.prevSize.width ) {
			props.width = this.size.width + "px";
		}
		if ( this.size.height !== this.prevSize.height ) {
			props.height = this.size.height + "px";
		}

		this.helper.css( props );

		return props;
	},

	_updateVirtualBoundaries: function( forceAspectRatio ) {
		var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
			o = this.options;

		b = {
			minWidth: this._isNumber( o.minWidth ) ? o.minWidth : 0,
			maxWidth: this._isNumber( o.maxWidth ) ? o.maxWidth : Infinity,
			minHeight: this._isNumber( o.minHeight ) ? o.minHeight : 0,
			maxHeight: this._isNumber( o.maxHeight ) ? o.maxHeight : Infinity
		};

		if ( this._aspectRatio || forceAspectRatio ) {
			pMinWidth = b.minHeight * this.aspectRatio;
			pMinHeight = b.minWidth / this.aspectRatio;
			pMaxWidth = b.maxHeight * this.aspectRatio;
			pMaxHeight = b.maxWidth / this.aspectRatio;

			if ( pMinWidth > b.minWidth ) {
				b.minWidth = pMinWidth;
			}
			if ( pMinHeight > b.minHeight ) {
				b.minHeight = pMinHeight;
			}
			if ( pMaxWidth < b.maxWidth ) {
				b.maxWidth = pMaxWidth;
			}
			if ( pMaxHeight < b.maxHeight ) {
				b.maxHeight = pMaxHeight;
			}
		}
		this._vBoundaries = b;
	},

	_updateCache: function( data ) {
		this.offset = this.helper.offset();
		if ( this._isNumber( data.left ) ) {
			this.position.left = data.left;
		}
		if ( this._isNumber( data.top ) ) {
			this.position.top = data.top;
		}
		if ( this._isNumber( data.height ) ) {
			this.size.height = data.height;
		}
		if ( this._isNumber( data.width ) ) {
			this.size.width = data.width;
		}
	},

	_updateRatio: function( data ) {

		var cpos = this.position,
			csize = this.size,
			a = this.axis;

		if ( this._isNumber( data.height ) ) {
			data.width = ( data.height * this.aspectRatio );
		} else if ( this._isNumber( data.width ) ) {
			data.height = ( data.width / this.aspectRatio );
		}

		if ( a === "sw" ) {
			data.left = cpos.left + ( csize.width - data.width );
			data.top = null;
		}
		if ( a === "nw" ) {
			data.top = cpos.top + ( csize.height - data.height );
			data.left = cpos.left + ( csize.width - data.width );
		}

		return data;
	},

	_respectSize: function( data ) {

		var o = this._vBoundaries,
			a = this.axis,
			ismaxw = this._isNumber( data.width ) && o.maxWidth && ( o.maxWidth < data.width ),
			ismaxh = this._isNumber( data.height ) && o.maxHeight && ( o.maxHeight < data.height ),
			isminw = this._isNumber( data.width ) && o.minWidth && ( o.minWidth > data.width ),
			isminh = this._isNumber( data.height ) && o.minHeight && ( o.minHeight > data.height ),
			dw = this.originalPosition.left + this.originalSize.width,
			dh = this.originalPosition.top + this.originalSize.height,
			cw = /sw|nw|w/.test( a ), ch = /nw|ne|n/.test( a );
		if ( isminw ) {
			data.width = o.minWidth;
		}
		if ( isminh ) {
			data.height = o.minHeight;
		}
		if ( ismaxw ) {
			data.width = o.maxWidth;
		}
		if ( ismaxh ) {
			data.height = o.maxHeight;
		}

		if ( isminw && cw ) {
			data.left = dw - o.minWidth;
		}
		if ( ismaxw && cw ) {
			data.left = dw - o.maxWidth;
		}
		if ( isminh && ch ) {
			data.top = dh - o.minHeight;
		}
		if ( ismaxh && ch ) {
			data.top = dh - o.maxHeight;
		}

		// Fixing jump error on top/left - bug #2330
		if ( !data.width && !data.height && !data.left && data.top ) {
			data.top = null;
		} else if ( !data.width && !data.height && !data.top && data.left ) {
			data.left = null;
		}

		return data;
	},

	_getPaddingPlusBorderDimensions: function( element ) {
		var i = 0,
			widths = [],
			borders = [
				element.css( "borderTopWidth" ),
				element.css( "borderRightWidth" ),
				element.css( "borderBottomWidth" ),
				element.css( "borderLeftWidth" )
			],
			paddings = [
				element.css( "paddingTop" ),
				element.css( "paddingRight" ),
				element.css( "paddingBottom" ),
				element.css( "paddingLeft" )
			];

		for ( ; i < 4; i++ ) {
			widths[ i ] = ( parseFloat( borders[ i ] ) || 0 );
			widths[ i ] += ( parseFloat( paddings[ i ] ) || 0 );
		}

		return {
			height: widths[ 0 ] + widths[ 2 ],
			width: widths[ 1 ] + widths[ 3 ]
		};
	},

	_proportionallyResize: function() {

		if ( !this._proportionallyResizeElements.length ) {
			return;
		}

		var prel,
			i = 0,
			element = this.helper || this.element;

		for ( ; i < this._proportionallyResizeElements.length; i++ ) {

			prel = this._proportionallyResizeElements[ i ];

			// TODO: Seems like a bug to cache this.outerDimensions
			// considering that we are in a loop.
			if ( !this.outerDimensions ) {
				this.outerDimensions = this._getPaddingPlusBorderDimensions( prel );
			}

			prel.css( {
				height: ( element.height() - this.outerDimensions.height ) || 0,
				width: ( element.width() - this.outerDimensions.width ) || 0
			} );

		}

	},

	_renderProxy: function() {

		var el = this.element, o = this.options;
		this.elementOffset = el.offset();

		if ( this._helper ) {

			this.helper = this.helper || $( "<div style='overflow:hidden;'></div>" );

			this._addClass( this.helper, this._helper );
			this.helper.css( {
				width: this.element.outerWidth(),
				height: this.element.outerHeight(),
				position: "absolute",
				left: this.elementOffset.left + "px",
				top: this.elementOffset.top + "px",
				zIndex: ++o.zIndex //TODO: Don't modify option
			} );

			this.helper
				.appendTo( "body" )
				.disableSelection();

		} else {
			this.helper = this.element;
		}

	},

	_change: {
		e: function( event, dx ) {
			return { width: this.originalSize.width + dx };
		},
		w: function( event, dx ) {
			var cs = this.originalSize, sp = this.originalPosition;
			return { left: sp.left + dx, width: cs.width - dx };
		},
		n: function( event, dx, dy ) {
			var cs = this.originalSize, sp = this.originalPosition;
			return { top: sp.top + dy, height: cs.height - dy };
		},
		s: function( event, dx, dy ) {
			return { height: this.originalSize.height + dy };
		},
		se: function( event, dx, dy ) {
			return $.extend( this._change.s.apply( this, arguments ),
				this._change.e.apply( this, [ event, dx, dy ] ) );
		},
		sw: function( event, dx, dy ) {
			return $.extend( this._change.s.apply( this, arguments ),
				this._change.w.apply( this, [ event, dx, dy ] ) );
		},
		ne: function( event, dx, dy ) {
			return $.extend( this._change.n.apply( this, arguments ),
				this._change.e.apply( this, [ event, dx, dy ] ) );
		},
		nw: function( event, dx, dy ) {
			return $.extend( this._change.n.apply( this, arguments ),
				this._change.w.apply( this, [ event, dx, dy ] ) );
		}
	},

	_propagate: function( n, event ) {
		$.ui.plugin.call( this, n, [ event, this.ui() ] );
		( n !== "resize" && this._trigger( n, event, this.ui() ) );
	},

	plugins: {},

	ui: function() {
		return {
			originalElement: this.originalElement,
			element: this.element,
			helper: this.helper,
			position: this.position,
			size: this.size,
			originalSize: this.originalSize,
			originalPosition: this.originalPosition
		};
	}

} );

/*
 * Resizable Extensions
 */

$.ui.plugin.add( "resizable", "animate", {

	stop: function( event ) {
		var that = $( this ).resizable( "instance" ),
			o = that.options,
			pr = that._proportionallyResizeElements,
			ista = pr.length && ( /textarea/i ).test( pr[ 0 ].nodeName ),
			soffseth = ista && that._hasScroll( pr[ 0 ], "left" ) ? 0 : that.sizeDiff.height,
			soffsetw = ista ? 0 : that.sizeDiff.width,
			style = {
				width: ( that.size.width - soffsetw ),
				height: ( that.size.height - soffseth )
			},
			left = ( parseFloat( that.element.css( "left" ) ) +
				( that.position.left - that.originalPosition.left ) ) || null,
			top = ( parseFloat( that.element.css( "top" ) ) +
				( that.position.top - that.originalPosition.top ) ) || null;

		that.element.animate(
			$.extend( style, top && left ? { top: top, left: left } : {} ), {
				duration: o.animateDuration,
				easing: o.animateEasing,
				step: function() {

					var data = {
						width: parseFloat( that.element.css( "width" ) ),
						height: parseFloat( that.element.css( "height" ) ),
						top: parseFloat( that.element.css( "top" ) ),
						left: parseFloat( that.element.css( "left" ) )
					};

					if ( pr && pr.length ) {
						$( pr[ 0 ] ).css( { width: data.width, height: data.height } );
					}

					// Propagating resize, and updating values for each animation step
					that._updateCache( data );
					that._propagate( "resize", event );

				}
			}
		);
	}

} );

$.ui.plugin.add( "resizable", "containment", {

	start: function() {
		var element, p, co, ch, cw, width, height,
			that = $( this ).resizable( "instance" ),
			o = that.options,
			el = that.element,
			oc = o.containment,
			ce = ( oc instanceof $ ) ?
				oc.get( 0 ) :
				( /parent/.test( oc ) ) ? el.parent().get( 0 ) : oc;

		if ( !ce ) {
			return;
		}

		that.containerElement = $( ce );

		if ( /document/.test( oc ) || oc === document ) {
			that.containerOffset = {
				left: 0,
				top: 0
			};
			that.containerPosition = {
				left: 0,
				top: 0
			};

			that.parentData = {
				element: $( document ),
				left: 0,
				top: 0,
				width: $( document ).width(),
				height: $( document ).height() || document.body.parentNode.scrollHeight
			};
		} else {
			element = $( ce );
			p = [];
			$( [ "Top", "Right", "Left", "Bottom" ] ).each( function( i, name ) {
				p[ i ] = that._num( element.css( "padding" + name ) );
			} );

			that.containerOffset = element.offset();
			that.containerPosition = element.position();
			that.containerSize = {
				height: ( element.innerHeight() - p[ 3 ] ),
				width: ( element.innerWidth() - p[ 1 ] )
			};

			co = that.containerOffset;
			ch = that.containerSize.height;
			cw = that.containerSize.width;
			width = ( that._hasScroll ( ce, "left" ) ? ce.scrollWidth : cw );
			height = ( that._hasScroll ( ce ) ? ce.scrollHeight : ch ) ;

			that.parentData = {
				element: ce,
				left: co.left,
				top: co.top,
				width: width,
				height: height
			};
		}
	},

	resize: function( event ) {
		var woset, hoset, isParent, isOffsetRelative,
			that = $( this ).resizable( "instance" ),
			o = that.options,
			co = that.containerOffset,
			cp = that.position,
			pRatio = that._aspectRatio || event.shiftKey,
			cop = {
				top: 0,
				left: 0
			},
			ce = that.containerElement,
			continueResize = true;

		if ( ce[ 0 ] !== document && ( /static/ ).test( ce.css( "position" ) ) ) {
			cop = co;
		}

		if ( cp.left < ( that._helper ? co.left : 0 ) ) {
			that.size.width = that.size.width +
				( that._helper ?
					( that.position.left - co.left ) :
					( that.position.left - cop.left ) );

			if ( pRatio ) {
				that.size.height = that.size.width / that.aspectRatio;
				continueResize = false;
			}
			that.position.left = o.helper ? co.left : 0;
		}

		if ( cp.top < ( that._helper ? co.top : 0 ) ) {
			that.size.height = that.size.height +
				( that._helper ?
					( that.position.top - co.top ) :
					that.position.top );

			if ( pRatio ) {
				that.size.width = that.size.height * that.aspectRatio;
				continueResize = false;
			}
			that.position.top = that._helper ? co.top : 0;
		}

		isParent = that.containerElement.get( 0 ) === that.element.parent().get( 0 );
		isOffsetRelative = /relative|absolute/.test( that.containerElement.css( "position" ) );

		if ( isParent && isOffsetRelative ) {
			that.offset.left = that.parentData.left + that.position.left;
			that.offset.top = that.parentData.top + that.position.top;
		} else {
			that.offset.left = that.element.offset().left;
			that.offset.top = that.element.offset().top;
		}

		woset = Math.abs( that.sizeDiff.width +
			( that._helper ?
				that.offset.left - cop.left :
				( that.offset.left - co.left ) ) );

		hoset = Math.abs( that.sizeDiff.height +
			( that._helper ?
				that.offset.top - cop.top :
				( that.offset.top - co.top ) ) );

		if ( woset + that.size.width >= that.parentData.width ) {
			that.size.width = that.parentData.width - woset;
			if ( pRatio ) {
				that.size.height = that.size.width / that.aspectRatio;
				continueResize = false;
			}
		}

		if ( hoset + that.size.height >= that.parentData.height ) {
			that.size.height = that.parentData.height - hoset;
			if ( pRatio ) {
				that.size.width = that.size.height * that.aspectRatio;
				continueResize = false;
			}
		}

		if ( !continueResize ) {
			that.position.left = that.prevPosition.left;
			that.position.top = that.prevPosition.top;
			that.size.width = that.prevSize.width;
			that.size.height = that.prevSize.height;
		}
	},

	stop: function() {
		var that = $( this ).resizable( "instance" ),
			o = that.options,
			co = that.containerOffset,
			cop = that.containerPosition,
			ce = that.containerElement,
			helper = $( that.helper ),
			ho = helper.offset(),
			w = helper.outerWidth() - that.sizeDiff.width,
			h = helper.outerHeight() - that.sizeDiff.height;

		if ( that._helper && !o.animate && ( /relative/ ).test( ce.css( "position" ) ) ) {
			$( this ).css( {
				left: ho.left - cop.left - co.left,
				width: w,
				height: h
			} );
		}

		if ( that._helper && !o.animate && ( /static/ ).test( ce.css( "position" ) ) ) {
			$( this ).css( {
				left: ho.left - cop.left - co.left,
				width: w,
				height: h
			} );
		}
	}
} );

$.ui.plugin.add( "resizable", "alsoResize", {

	start: function() {
		var that = $( this ).resizable( "instance" ),
			o = that.options;

		$( o.alsoResize ).each( function() {
			var el = $( this );
			el.data( "ui-resizable-alsoresize", {
				width: parseFloat( el.width() ), height: parseFloat( el.height() ),
				left: parseFloat( el.css( "left" ) ), top: parseFloat( el.css( "top" ) )
			} );
		} );
	},

	resize: function( event, ui ) {
		var that = $( this ).resizable( "instance" ),
			o = that.options,
			os = that.originalSize,
			op = that.originalPosition,
			delta = {
				height: ( that.size.height - os.height ) || 0,
				width: ( that.size.width - os.width ) || 0,
				top: ( that.position.top - op.top ) || 0,
				left: ( that.position.left - op.left ) || 0
			};

			$( o.alsoResize ).each( function() {
				var el = $( this ), start = $( this ).data( "ui-resizable-alsoresize" ), style = {},
					css = el.parents( ui.originalElement[ 0 ] ).length ?
							[ "width", "height" ] :
							[ "width", "height", "top", "left" ];

				$.each( css, function( i, prop ) {
					var sum = ( start[ prop ] || 0 ) + ( delta[ prop ] || 0 );
					if ( sum && sum >= 0 ) {
						style[ prop ] = sum || null;
					}
				} );

				el.css( style );
			} );
	},

	stop: function() {
		$( this ).removeData( "ui-resizable-alsoresize" );
	}
} );

$.ui.plugin.add( "resizable", "ghost", {

	start: function() {

		var that = $( this ).resizable( "instance" ), cs = that.size;

		that.ghost = that.originalElement.clone();
		that.ghost.css( {
			opacity: 0.25,
			display: "block",
			position: "relative",
			height: cs.height,
			width: cs.width,
			margin: 0,
			left: 0,
			top: 0
		} );

		that._addClass( that.ghost, "ui-resizable-ghost" );

		// DEPRECATED
		// TODO: remove after 1.12
		if ( $.uiBackCompat !== false && typeof that.options.ghost === "string" ) {

			// Ghost option
			that.ghost.addClass( this.options.ghost );
		}

		that.ghost.appendTo( that.helper );

	},

	resize: function() {
		var that = $( this ).resizable( "instance" );
		if ( that.ghost ) {
			that.ghost.css( {
				position: "relative",
				height: that.size.height,
				width: that.size.width
			} );
		}
	},

	stop: function() {
		var that = $( this ).resizable( "instance" );
		if ( that.ghost && that.helper ) {
			that.helper.get( 0 ).removeChild( that.ghost.get( 0 ) );
		}
	}

} );

$.ui.plugin.add( "resizable", "grid", {

	resize: function() {
		var outerDimensions,
			that = $( this ).resizable( "instance" ),
			o = that.options,
			cs = that.size,
			os = that.originalSize,
			op = that.originalPosition,
			a = that.axis,
			grid = typeof o.grid === "number" ? [ o.grid, o.grid ] : o.grid,
			gridX = ( grid[ 0 ] || 1 ),
			gridY = ( grid[ 1 ] || 1 ),
			ox = Math.round( ( cs.width - os.width ) / gridX ) * gridX,
			oy = Math.round( ( cs.height - os.height ) / gridY ) * gridY,
			newWidth = os.width + ox,
			newHeight = os.height + oy,
			isMaxWidth = o.maxWidth && ( o.maxWidth < newWidth ),
			isMaxHeight = o.maxHeight && ( o.maxHeight < newHeight ),
			isMinWidth = o.minWidth && ( o.minWidth > newWidth ),
			isMinHeight = o.minHeight && ( o.minHeight > newHeight );

		o.grid = grid;

		if ( isMinWidth ) {
			newWidth += gridX;
		}
		if ( isMinHeight ) {
			newHeight += gridY;
		}
		if ( isMaxWidth ) {
			newWidth -= gridX;
		}
		if ( isMaxHeight ) {
			newHeight -= gridY;
		}

		if ( /^(se|s|e)$/.test( a ) ) {
			that.size.width = newWidth;
			that.size.height = newHeight;
		} else if ( /^(ne)$/.test( a ) ) {
			that.size.width = newWidth;
			that.size.height = newHeight;
			that.position.top = op.top - oy;
		} else if ( /^(sw)$/.test( a ) ) {
			that.size.width = newWidth;
			that.size.height = newHeight;
			that.position.left = op.left - ox;
		} else {
			if ( newHeight - gridY <= 0 || newWidth - gridX <= 0 ) {
				outerDimensions = that._getPaddingPlusBorderDimensions( this );
			}

			if ( newHeight - gridY > 0 ) {
				that.size.height = newHeight;
				that.position.top = op.top - oy;
			} else {
				newHeight = gridY - outerDimensions.height;
				that.size.height = newHeight;
				that.position.top = op.top + os.height - newHeight;
			}
			if ( newWidth - gridX > 0 ) {
				that.size.width = newWidth;
				that.position.left = op.left - ox;
			} else {
				newWidth = gridX - outerDimensions.width;
				that.size.width = newWidth;
				that.position.left = op.left + os.width - newWidth;
			}
		}
	}

} );

var widgetsResizable = $.ui.resizable;


/*!
 * jQuery UI Dialog 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Dialog
//>>group: Widgets
//>>description: Displays customizable dialog windows.
//>>docs: http://api.jqueryui.com/dialog/
//>>demos: http://jqueryui.com/dialog/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/dialog.css
//>>css.theme: ../../themes/base/theme.css



$.widget( "ui.dialog", {
	version: "1.12.1",
	options: {
		appendTo: "body",
		autoOpen: true,
		buttons: [],
		classes: {
			"ui-dialog": "ui-corner-all",
			"ui-dialog-titlebar": "ui-corner-all"
		},
		closeOnEscape: true,
		closeText: "Close",
		draggable: true,
		hide: null,
		height: "auto",
		maxHeight: null,
		maxWidth: null,
		minHeight: 150,
		minWidth: 150,
		modal: false,
		position: {
			my: "center",
			at: "center",
			of: window,
			collision: "fit",

			// Ensure the titlebar is always visible
			using: function( pos ) {
				var topOffset = $( this ).css( pos ).offset().top;
				if ( topOffset < 0 ) {
					$( this ).css( "top", pos.top - topOffset );
				}
			}
		},
		resizable: true,
		show: null,
		title: null,
		width: 300,

		// Callbacks
		beforeClose: null,
		close: null,
		drag: null,
		dragStart: null,
		dragStop: null,
		focus: null,
		open: null,
		resize: null,
		resizeStart: null,
		resizeStop: null
	},

	sizeRelatedOptions: {
		buttons: true,
		height: true,
		maxHeight: true,
		maxWidth: true,
		minHeight: true,
		minWidth: true,
		width: true
	},

	resizableRelatedOptions: {
		maxHeight: true,
		maxWidth: true,
		minHeight: true,
		minWidth: true
	},

	_create: function() {
		this.originalCss = {
			display: this.element[ 0 ].style.display,
			width: this.element[ 0 ].style.width,
			minHeight: this.element[ 0 ].style.minHeight,
			maxHeight: this.element[ 0 ].style.maxHeight,
			height: this.element[ 0 ].style.height
		};
		this.originalPosition = {
			parent: this.element.parent(),
			index: this.element.parent().children().index( this.element )
		};
		this.originalTitle = this.element.attr( "title" );
		if ( this.options.title == null && this.originalTitle != null ) {
			this.options.title = this.originalTitle;
		}

		// Dialogs can't be disabled
		if ( this.options.disabled ) {
			this.options.disabled = false;
		}

		this._createWrapper();

		this.element
			.show()
			.removeAttr( "title" )
			.appendTo( this.uiDialog );

		this._addClass( "ui-dialog-content", "ui-widget-content" );

		this._createTitlebar();
		this._createButtonPane();

		if ( this.options.draggable && $.fn.draggable ) {
			this._makeDraggable();
		}
		if ( this.options.resizable && $.fn.resizable ) {
			this._makeResizable();
		}

		this._isOpen = false;

		this._trackFocus();
	},

	_init: function() {
		if ( this.options.autoOpen ) {
			this.open();
		}
	},

	_appendTo: function() {
		var element = this.options.appendTo;
		if ( element && ( element.jquery || element.nodeType ) ) {
			return $( element );
		}
		return this.document.find( element || "body" ).eq( 0 );
	},

	_destroy: function() {
		var next,
			originalPosition = this.originalPosition;

		this._untrackInstance();
		this._destroyOverlay();

		this.element
			.removeUniqueId()
			.css( this.originalCss )

			// Without detaching first, the following becomes really slow
			.detach();

		this.uiDialog.remove();

		if ( this.originalTitle ) {
			this.element.attr( "title", this.originalTitle );
		}

		next = originalPosition.parent.children().eq( originalPosition.index );

		// Don't try to place the dialog next to itself (#8613)
		if ( next.length && next[ 0 ] !== this.element[ 0 ] ) {
			next.before( this.element );
		} else {
			originalPosition.parent.append( this.element );
		}
	},

	widget: function() {
		return this.uiDialog;
	},

	disable: $.noop,
	enable: $.noop,

	close: function( event ) {
		var that = this;

		if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
			return;
		}

		this._isOpen = false;
		this._focusedElement = null;
		this._destroyOverlay();
		this._untrackInstance();

		if ( !this.opener.filter( ":focusable" ).trigger( "focus" ).length ) {

			// Hiding a focused element doesn't trigger blur in WebKit
			// so in case we have nothing to focus on, explicitly blur the active element
			// https://bugs.webkit.org/show_bug.cgi?id=47182
			$.ui.safeBlur( $.ui.safeActiveElement( this.document[ 0 ] ) );
		}

		this._hide( this.uiDialog, this.options.hide, function() {
			that._trigger( "close", event );
		} );
	},

	isOpen: function() {
		return this._isOpen;
	},

	moveToTop: function() {
		this._moveToTop();
	},

	_moveToTop: function( event, silent ) {
		var moved = false,
			zIndices = this.uiDialog.siblings( ".ui-front:visible" ).map( function() {
				return +$( this ).css( "z-index" );
			} ).get(),
			zIndexMax = Math.max.apply( null, zIndices );

		if ( zIndexMax >= +this.uiDialog.css( "z-index" ) ) {
			this.uiDialog.css( "z-index", zIndexMax + 1 );
			moved = true;
		}

		if ( moved && !silent ) {
			this._trigger( "focus", event );
		}
		return moved;
	},

	open: function() {
		var that = this;
		if ( this._isOpen ) {
			if ( this._moveToTop() ) {
				this._focusTabbable();
			}
			return;
		}

		this._isOpen = true;
		this.opener = $( $.ui.safeActiveElement( this.document[ 0 ] ) );

		this._size();
		this._position();
		this._createOverlay();
		this._moveToTop( null, true );

		// Ensure the overlay is moved to the top with the dialog, but only when
		// opening. The overlay shouldn't move after the dialog is open so that
		// modeless dialogs opened after the modal dialog stack properly.
		if ( this.overlay ) {
			this.overlay.css( "z-index", this.uiDialog.css( "z-index" ) - 1 );
		}

		this._show( this.uiDialog, this.options.show, function() {
			that._focusTabbable();
			that._trigger( "focus" );
		} );

		// Track the dialog immediately upon openening in case a focus event
		// somehow occurs outside of the dialog before an element inside the
		// dialog is focused (#10152)
		this._makeFocusTarget();

		this._trigger( "open" );
	},

	_focusTabbable: function() {

		// Set focus to the first match:
		// 1. An element that was focused previously
		// 2. First element inside the dialog matching [autofocus]
		// 3. Tabbable element inside the content element
		// 4. Tabbable element inside the buttonpane
		// 5. The close button
		// 6. The dialog itself
		var hasFocus = this._focusedElement;
		if ( !hasFocus ) {
			hasFocus = this.element.find( "[autofocus]" );
		}
		if ( !hasFocus.length ) {
			hasFocus = this.element.find( ":tabbable" );
		}
		if ( !hasFocus.length ) {
			hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
		}
		if ( !hasFocus.length ) {
			hasFocus = this.uiDialogTitlebarClose.filter( ":tabbable" );
		}
		if ( !hasFocus.length ) {
			hasFocus = this.uiDialog;
		}
		hasFocus.eq( 0 ).trigger( "focus" );
	},

	_keepFocus: function( event ) {
		function checkFocus() {
			var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
				isActive = this.uiDialog[ 0 ] === activeElement ||
					$.contains( this.uiDialog[ 0 ], activeElement );
			if ( !isActive ) {
				this._focusTabbable();
			}
		}
		event.preventDefault();
		checkFocus.call( this );

		// support: IE
		// IE <= 8 doesn't prevent moving focus even with event.preventDefault()
		// so we check again later
		this._delay( checkFocus );
	},

	_createWrapper: function() {
		this.uiDialog = $( "<div>" )
			.hide()
			.attr( {

				// Setting tabIndex makes the div focusable
				tabIndex: -1,
				role: "dialog"
			} )
			.appendTo( this._appendTo() );

		this._addClass( this.uiDialog, "ui-dialog", "ui-widget ui-widget-content ui-front" );
		this._on( this.uiDialog, {
			keydown: function( event ) {
				if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
						event.keyCode === $.ui.keyCode.ESCAPE ) {
					event.preventDefault();
					this.close( event );
					return;
				}

				// Prevent tabbing out of dialogs
				if ( event.keyCode !== $.ui.keyCode.TAB || event.isDefaultPrevented() ) {
					return;
				}
				var tabbables = this.uiDialog.find( ":tabbable" ),
					first = tabbables.filter( ":first" ),
					last = tabbables.filter( ":last" );

				if ( ( event.target === last[ 0 ] || event.target === this.uiDialog[ 0 ] ) &&
						!event.shiftKey ) {
					this._delay( function() {
						first.trigger( "focus" );
					} );
					event.preventDefault();
				} else if ( ( event.target === first[ 0 ] ||
						event.target === this.uiDialog[ 0 ] ) && event.shiftKey ) {
					this._delay( function() {
						last.trigger( "focus" );
					} );
					event.preventDefault();
				}
			},
			mousedown: function( event ) {
				if ( this._moveToTop( event ) ) {
					this._focusTabbable();
				}
			}
		} );

		// We assume that any existing aria-describedby attribute means
		// that the dialog content is marked up properly
		// otherwise we brute force the content as the description
		if ( !this.element.find( "[aria-describedby]" ).length ) {
			this.uiDialog.attr( {
				"aria-describedby": this.element.uniqueId().attr( "id" )
			} );
		}
	},

	_createTitlebar: function() {
		var uiDialogTitle;

		this.uiDialogTitlebar = $( "<div>" );
		this._addClass( this.uiDialogTitlebar,
			"ui-dialog-titlebar", "ui-widget-header ui-helper-clearfix" );
		this._on( this.uiDialogTitlebar, {
			mousedown: function( event ) {

				// Don't prevent click on close button (#8838)
				// Focusing a dialog that is partially scrolled out of view
				// causes the browser to scroll it into view, preventing the click event
				if ( !$( event.target ).closest( ".ui-dialog-titlebar-close" ) ) {

					// Dialog isn't getting focus when dragging (#8063)
					this.uiDialog.trigger( "focus" );
				}
			}
		} );

		// Support: IE
		// Use type="button" to prevent enter keypresses in textboxes from closing the
		// dialog in IE (#9312)
		this.uiDialogTitlebarClose = $( "<button type='button'></button>" )
			.button( {
				label: $( "<a>" ).text( this.options.closeText ).html(),
				icon: "ui-icon-closethick",
				showLabel: false
			} )
			.appendTo( this.uiDialogTitlebar );

		this._addClass( this.uiDialogTitlebarClose, "ui-dialog-titlebar-close" );
		this._on( this.uiDialogTitlebarClose, {
			click: function( event ) {
				event.preventDefault();
				this.close( event );
			}
		} );

		uiDialogTitle = $( "<span>" ).uniqueId().prependTo( this.uiDialogTitlebar );
		this._addClass( uiDialogTitle, "ui-dialog-title" );
		this._title( uiDialogTitle );

		this.uiDialogTitlebar.prependTo( this.uiDialog );

		this.uiDialog.attr( {
			"aria-labelledby": uiDialogTitle.attr( "id" )
		} );
	},

	_title: function( title ) {
		if ( this.options.title ) {
			title.text( this.options.title );
		} else {
			title.html( "&#160;" );
		}
	},

	_createButtonPane: function() {
		this.uiDialogButtonPane = $( "<div>" );
		this._addClass( this.uiDialogButtonPane, "ui-dialog-buttonpane",
			"ui-widget-content ui-helper-clearfix" );

		this.uiButtonSet = $( "<div>" )
			.appendTo( this.uiDialogButtonPane );
		this._addClass( this.uiButtonSet, "ui-dialog-buttonset" );

		this._createButtons();
	},

	_createButtons: function() {
		var that = this,
			buttons = this.options.buttons;

		// If we already have a button pane, remove it
		this.uiDialogButtonPane.remove();
		this.uiButtonSet.empty();

		if ( $.isEmptyObject( buttons ) || ( $.isArray( buttons ) && !buttons.length ) ) {
			this._removeClass( this.uiDialog, "ui-dialog-buttons" );
			return;
		}

		$.each( buttons, function( name, props ) {
			var click, buttonOptions;
			props = $.isFunction( props ) ?
				{ click: props, text: name } :
				props;

			// Default to a non-submitting button
			props = $.extend( { type: "button" }, props );

			// Change the context for the click callback to be the main element
			click = props.click;
			buttonOptions = {
				icon: props.icon,
				iconPosition: props.iconPosition,
				showLabel: props.showLabel,

				// Deprecated options
				icons: props.icons,
				text: props.text
			};

			delete props.click;
			delete props.icon;
			delete props.iconPosition;
			delete props.showLabel;

			// Deprecated options
			delete props.icons;
			if ( typeof props.text === "boolean" ) {
				delete props.text;
			}

			$( "<button></button>", props )
				.button( buttonOptions )
				.appendTo( that.uiButtonSet )
				.on( "click", function() {
					click.apply( that.element[ 0 ], arguments );
				} );
		} );
		this._addClass( this.uiDialog, "ui-dialog-buttons" );
		this.uiDialogButtonPane.appendTo( this.uiDialog );
	},

	_makeDraggable: function() {
		var that = this,
			options = this.options;

		function filteredUi( ui ) {
			return {
				position: ui.position,
				offset: ui.offset
			};
		}

		this.uiDialog.draggable( {
			cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
			handle: ".ui-dialog-titlebar",
			containment: "document",
			start: function( event, ui ) {
				that._addClass( $( this ), "ui-dialog-dragging" );
				that._blockFrames();
				that._trigger( "dragStart", event, filteredUi( ui ) );
			},
			drag: function( event, ui ) {
				that._trigger( "drag", event, filteredUi( ui ) );
			},
			stop: function( event, ui ) {
				var left = ui.offset.left - that.document.scrollLeft(),
					top = ui.offset.top - that.document.scrollTop();

				options.position = {
					my: "left top",
					at: "left" + ( left >= 0 ? "+" : "" ) + left + " " +
						"top" + ( top >= 0 ? "+" : "" ) + top,
					of: that.window
				};
				that._removeClass( $( this ), "ui-dialog-dragging" );
				that._unblockFrames();
				that._trigger( "dragStop", event, filteredUi( ui ) );
			}
		} );
	},

	_makeResizable: function() {
		var that = this,
			options = this.options,
			handles = options.resizable,

			// .ui-resizable has position: relative defined in the stylesheet
			// but dialogs have to use absolute or fixed positioning
			position = this.uiDialog.css( "position" ),
			resizeHandles = typeof handles === "string" ?
				handles :
				"n,e,s,w,se,sw,ne,nw";

		function filteredUi( ui ) {
			return {
				originalPosition: ui.originalPosition,
				originalSize: ui.originalSize,
				position: ui.position,
				size: ui.size
			};
		}

		this.uiDialog.resizable( {
			cancel: ".ui-dialog-content",
			containment: "document",
			alsoResize: this.element,
			maxWidth: options.maxWidth,
			maxHeight: options.maxHeight,
			minWidth: options.minWidth,
			minHeight: this._minHeight(),
			handles: resizeHandles,
			start: function( event, ui ) {
				that._addClass( $( this ), "ui-dialog-resizing" );
				that._blockFrames();
				that._trigger( "resizeStart", event, filteredUi( ui ) );
			},
			resize: function( event, ui ) {
				that._trigger( "resize", event, filteredUi( ui ) );
			},
			stop: function( event, ui ) {
				var offset = that.uiDialog.offset(),
					left = offset.left - that.document.scrollLeft(),
					top = offset.top - that.document.scrollTop();

				options.height = that.uiDialog.height();
				options.width = that.uiDialog.width();
				options.position = {
					my: "left top",
					at: "left" + ( left >= 0 ? "+" : "" ) + left + " " +
						"top" + ( top >= 0 ? "+" : "" ) + top,
					of: that.window
				};
				that._removeClass( $( this ), "ui-dialog-resizing" );
				that._unblockFrames();
				that._trigger( "resizeStop", event, filteredUi( ui ) );
			}
		} )
			.css( "position", position );
	},

	_trackFocus: function() {
		this._on( this.widget(), {
			focusin: function( event ) {
				this._makeFocusTarget();
				this._focusedElement = $( event.target );
			}
		} );
	},

	_makeFocusTarget: function() {
		this._untrackInstance();
		this._trackingInstances().unshift( this );
	},

	_untrackInstance: function() {
		var instances = this._trackingInstances(),
			exists = $.inArray( this, instances );
		if ( exists !== -1 ) {
			instances.splice( exists, 1 );
		}
	},

	_trackingInstances: function() {
		var instances = this.document.data( "ui-dialog-instances" );
		if ( !instances ) {
			instances = [];
			this.document.data( "ui-dialog-instances", instances );
		}
		return instances;
	},

	_minHeight: function() {
		var options = this.options;

		return options.height === "auto" ?
			options.minHeight :
			Math.min( options.minHeight, options.height );
	},

	_position: function() {

		// Need to show the dialog to get the actual offset in the position plugin
		var isVisible = this.uiDialog.is( ":visible" );
		if ( !isVisible ) {
			this.uiDialog.show();
		}
		this.uiDialog.position( this.options.position );
		if ( !isVisible ) {
			this.uiDialog.hide();
		}
	},

	_setOptions: function( options ) {
		var that = this,
			resize = false,
			resizableOptions = {};

		$.each( options, function( key, value ) {
			that._setOption( key, value );

			if ( key in that.sizeRelatedOptions ) {
				resize = true;
			}
			if ( key in that.resizableRelatedOptions ) {
				resizableOptions[ key ] = value;
			}
		} );

		if ( resize ) {
			this._size();
			this._position();
		}
		if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
			this.uiDialog.resizable( "option", resizableOptions );
		}
	},

	_setOption: function( key, value ) {
		var isDraggable, isResizable,
			uiDialog = this.uiDialog;

		if ( key === "disabled" ) {
			return;
		}

		this._super( key, value );

		if ( key === "appendTo" ) {
			this.uiDialog.appendTo( this._appendTo() );
		}

		if ( key === "buttons" ) {
			this._createButtons();
		}

		if ( key === "closeText" ) {
			this.uiDialogTitlebarClose.button( {

				// Ensure that we always pass a string
				label: $( "<a>" ).text( "" + this.options.closeText ).html()
			} );
		}

		if ( key === "draggable" ) {
			isDraggable = uiDialog.is( ":data(ui-draggable)" );
			if ( isDraggable && !value ) {
				uiDialog.draggable( "destroy" );
			}

			if ( !isDraggable && value ) {
				this._makeDraggable();
			}
		}

		if ( key === "position" ) {
			this._position();
		}

		if ( key === "resizable" ) {

			// currently resizable, becoming non-resizable
			isResizable = uiDialog.is( ":data(ui-resizable)" );
			if ( isResizable && !value ) {
				uiDialog.resizable( "destroy" );
			}

			// Currently resizable, changing handles
			if ( isResizable && typeof value === "string" ) {
				uiDialog.resizable( "option", "handles", value );
			}

			// Currently non-resizable, becoming resizable
			if ( !isResizable && value !== false ) {
				this._makeResizable();
			}
		}

		if ( key === "title" ) {
			this._title( this.uiDialogTitlebar.find( ".ui-dialog-title" ) );
		}
	},

	_size: function() {

		// If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
		// divs will both have width and height set, so we need to reset them
		var nonContentHeight, minContentHeight, maxContentHeight,
			options = this.options;

		// Reset content sizing
		this.element.show().css( {
			width: "auto",
			minHeight: 0,
			maxHeight: "none",
			height: 0
		} );

		if ( options.minWidth > options.width ) {
			options.width = options.minWidth;
		}

		// Reset wrapper sizing
		// determine the height of all the non-content elements
		nonContentHeight = this.uiDialog.css( {
			height: "auto",
			width: options.width
		} )
			.outerHeight();
		minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
		maxContentHeight = typeof options.maxHeight === "number" ?
			Math.max( 0, options.maxHeight - nonContentHeight ) :
			"none";

		if ( options.height === "auto" ) {
			this.element.css( {
				minHeight: minContentHeight,
				maxHeight: maxContentHeight,
				height: "auto"
			} );
		} else {
			this.element.height( Math.max( 0, options.height - nonContentHeight ) );
		}

		if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
			this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
		}
	},

	_blockFrames: function() {
		this.iframeBlocks = this.document.find( "iframe" ).map( function() {
			var iframe = $( this );

			return $( "<div>" )
				.css( {
					position: "absolute",
					width: iframe.outerWidth(),
					height: iframe.outerHeight()
				} )
				.appendTo( iframe.parent() )
				.offset( iframe.offset() )[ 0 ];
		} );
	},

	_unblockFrames: function() {
		if ( this.iframeBlocks ) {
			this.iframeBlocks.remove();
			delete this.iframeBlocks;
		}
	},

	_allowInteraction: function( event ) {
		if ( $( event.target ).closest( ".ui-dialog" ).length ) {
			return true;
		}

		// TODO: Remove hack when datepicker implements
		// the .ui-front logic (#8989)
		return !!$( event.target ).closest( ".ui-datepicker" ).length;
	},

	_createOverlay: function() {
		if ( !this.options.modal ) {
			return;
		}

		// We use a delay in case the overlay is created from an
		// event that we're going to be cancelling (#2804)
		var isOpening = true;
		this._delay( function() {
			isOpening = false;
		} );

		if ( !this.document.data( "ui-dialog-overlays" ) ) {

			// Prevent use of anchors and inputs
			// Using _on() for an event handler shared across many instances is
			// safe because the dialogs stack and must be closed in reverse order
			this._on( this.document, {
				focusin: function( event ) {
					if ( isOpening ) {
						return;
					}

					if ( !this._allowInteraction( event ) ) {
						event.preventDefault();
						this._trackingInstances()[ 0 ]._focusTabbable();
					}
				}
			} );
		}

		this.overlay = $( "<div>" )
			.appendTo( this._appendTo() );

		this._addClass( this.overlay, null, "ui-widget-overlay ui-front" );
		this._on( this.overlay, {
			mousedown: "_keepFocus"
		} );
		this.document.data( "ui-dialog-overlays",
			( this.document.data( "ui-dialog-overlays" ) || 0 ) + 1 );
	},

	_destroyOverlay: function() {
		if ( !this.options.modal ) {
			return;
		}

		if ( this.overlay ) {
			var overlays = this.document.data( "ui-dialog-overlays" ) - 1;

			if ( !overlays ) {
				this._off( this.document, "focusin" );
				this.document.removeData( "ui-dialog-overlays" );
			} else {
				this.document.data( "ui-dialog-overlays", overlays );
			}

			this.overlay.remove();
			this.overlay = null;
		}
	}
} );

// DEPRECATED
// TODO: switch return back to widget declaration at top of file when this is removed
if ( $.uiBackCompat !== false ) {

	// Backcompat for dialogClass option
	$.widget( "ui.dialog", $.ui.dialog, {
		options: {
			dialogClass: ""
		},
		_createWrapper: function() {
			this._super();
			this.uiDialog.addClass( this.options.dialogClass );
		},
		_setOption: function( key, value ) {
			if ( key === "dialogClass" ) {
				this.uiDialog
					.removeClass( this.options.dialogClass )
					.addClass( value );
			}
			this._superApply( arguments );
		}
	} );
}

var widgetsDialog = $.ui.dialog;


/*!
 * jQuery UI Droppable 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Droppable
//>>group: Interactions
//>>description: Enables drop targets for draggable elements.
//>>docs: http://api.jqueryui.com/droppable/
//>>demos: http://jqueryui.com/droppable/



$.widget( "ui.droppable", {
	version: "1.12.1",
	widgetEventPrefix: "drop",
	options: {
		accept: "*",
		addClasses: true,
		greedy: false,
		scope: "default",
		tolerance: "intersect",

		// Callbacks
		activate: null,
		deactivate: null,
		drop: null,
		out: null,
		over: null
	},
	_create: function() {

		var proportions,
			o = this.options,
			accept = o.accept;

		this.isover = false;
		this.isout = true;

		this.accept = $.isFunction( accept ) ? accept : function( d ) {
			return d.is( accept );
		};

		this.proportions = function( /* valueToWrite */ ) {
			if ( arguments.length ) {

				// Store the droppable's proportions
				proportions = arguments[ 0 ];
			} else {

				// Retrieve or derive the droppable's proportions
				return proportions ?
					proportions :
					proportions = {
						width: this.element[ 0 ].offsetWidth,
						height: this.element[ 0 ].offsetHeight
					};
			}
		};

		this._addToManager( o.scope );

		o.addClasses && this._addClass( "ui-droppable" );

	},

	_addToManager: function( scope ) {

		// Add the reference and positions to the manager
		$.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || [];
		$.ui.ddmanager.droppables[ scope ].push( this );
	},

	_splice: function( drop ) {
		var i = 0;
		for ( ; i < drop.length; i++ ) {
			if ( drop[ i ] === this ) {
				drop.splice( i, 1 );
			}
		}
	},

	_destroy: function() {
		var drop = $.ui.ddmanager.droppables[ this.options.scope ];

		this._splice( drop );
	},

	_setOption: function( key, value ) {

		if ( key === "accept" ) {
			this.accept = $.isFunction( value ) ? value : function( d ) {
				return d.is( value );
			};
		} else if ( key === "scope" ) {
			var drop = $.ui.ddmanager.droppables[ this.options.scope ];

			this._splice( drop );
			this._addToManager( value );
		}

		this._super( key, value );
	},

	_activate: function( event ) {
		var draggable = $.ui.ddmanager.current;

		this._addActiveClass();
		if ( draggable ) {
			this._trigger( "activate", event, this.ui( draggable ) );
		}
	},

	_deactivate: function( event ) {
		var draggable = $.ui.ddmanager.current;

		this._removeActiveClass();
		if ( draggable ) {
			this._trigger( "deactivate", event, this.ui( draggable ) );
		}
	},

	_over: function( event ) {

		var draggable = $.ui.ddmanager.current;

		// Bail if draggable and droppable are same element
		if ( !draggable || ( draggable.currentItem ||
				draggable.element )[ 0 ] === this.element[ 0 ] ) {
			return;
		}

		if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
				draggable.element ) ) ) {
			this._addHoverClass();
			this._trigger( "over", event, this.ui( draggable ) );
		}

	},

	_out: function( event ) {

		var draggable = $.ui.ddmanager.current;

		// Bail if draggable and droppable are same element
		if ( !draggable || ( draggable.currentItem ||
				draggable.element )[ 0 ] === this.element[ 0 ] ) {
			return;
		}

		if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
				draggable.element ) ) ) {
			this._removeHoverClass();
			this._trigger( "out", event, this.ui( draggable ) );
		}

	},

	_drop: function( event, custom ) {

		var draggable = custom || $.ui.ddmanager.current,
			childrenIntersection = false;

		// Bail if draggable and droppable are same element
		if ( !draggable || ( draggable.currentItem ||
				draggable.element )[ 0 ] === this.element[ 0 ] ) {
			return false;
		}

		this.element
			.find( ":data(ui-droppable)" )
			.not( ".ui-draggable-dragging" )
			.each( function() {
				var inst = $( this ).droppable( "instance" );
				if (
					inst.options.greedy &&
					!inst.options.disabled &&
					inst.options.scope === draggable.options.scope &&
					inst.accept.call(
						inst.element[ 0 ], ( draggable.currentItem || draggable.element )
					) &&
					intersect(
						draggable,
						$.extend( inst, { offset: inst.element.offset() } ),
						inst.options.tolerance, event
					)
				) {
					childrenIntersection = true;
					return false; }
			} );
		if ( childrenIntersection ) {
			return false;
		}

		if ( this.accept.call( this.element[ 0 ],
				( draggable.currentItem || draggable.element ) ) ) {
			this._removeActiveClass();
			this._removeHoverClass();

			this._trigger( "drop", event, this.ui( draggable ) );
			return this.element;
		}

		return false;

	},

	ui: function( c ) {
		return {
			draggable: ( c.currentItem || c.element ),
			helper: c.helper,
			position: c.position,
			offset: c.positionAbs
		};
	},

	// Extension points just to make backcompat sane and avoid duplicating logic
	// TODO: Remove in 1.13 along with call to it below
	_addHoverClass: function() {
		this._addClass( "ui-droppable-hover" );
	},

	_removeHoverClass: function() {
		this._removeClass( "ui-droppable-hover" );
	},

	_addActiveClass: function() {
		this._addClass( "ui-droppable-active" );
	},

	_removeActiveClass: function() {
		this._removeClass( "ui-droppable-active" );
	}
} );

var intersect = $.ui.intersect = ( function() {
	function isOverAxis( x, reference, size ) {
		return ( x >= reference ) && ( x < ( reference + size ) );
	}

	return function( draggable, droppable, toleranceMode, event ) {

		if ( !droppable.offset ) {
			return false;
		}

		var x1 = ( draggable.positionAbs ||
				draggable.position.absolute ).left + draggable.margins.left,
			y1 = ( draggable.positionAbs ||
				draggable.position.absolute ).top + draggable.margins.top,
			x2 = x1 + draggable.helperProportions.width,
			y2 = y1 + draggable.helperProportions.height,
			l = droppable.offset.left,
			t = droppable.offset.top,
			r = l + droppable.proportions().width,
			b = t + droppable.proportions().height;

		switch ( toleranceMode ) {
		case "fit":
			return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b );
		case "intersect":
			return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half
				x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half
				t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half
				y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half
		case "pointer":
			return isOverAxis( event.pageY, t, droppable.proportions().height ) &&
				isOverAxis( event.pageX, l, droppable.proportions().width );
		case "touch":
			return (
				( y1 >= t && y1 <= b ) || // Top edge touching
				( y2 >= t && y2 <= b ) || // Bottom edge touching
				( y1 < t && y2 > b ) // Surrounded vertically
			) && (
				( x1 >= l && x1 <= r ) || // Left edge touching
				( x2 >= l && x2 <= r ) || // Right edge touching
				( x1 < l && x2 > r ) // Surrounded horizontally
			);
		default:
			return false;
		}
	};
} )();

/*
	This manager tracks offsets of draggables and droppables
*/
$.ui.ddmanager = {
	current: null,
	droppables: { "default": [] },
	prepareOffsets: function( t, event ) {

		var i, j,
			m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
			type = event ? event.type : null, // workaround for #2317
			list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack();

		droppablesLoop: for ( i = 0; i < m.length; i++ ) {

			// No disabled and non-accepted
			if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ],
					( t.currentItem || t.element ) ) ) ) {
				continue;
			}

			// Filter out elements in the current dragged item
			for ( j = 0; j < list.length; j++ ) {
				if ( list[ j ] === m[ i ].element[ 0 ] ) {
					m[ i ].proportions().height = 0;
					continue droppablesLoop;
				}
			}

			m[ i ].visible = m[ i ].element.css( "display" ) !== "none";
			if ( !m[ i ].visible ) {
				continue;
			}

			// Activate the droppable if used directly from draggables
			if ( type === "mousedown" ) {
				m[ i ]._activate.call( m[ i ], event );
			}

			m[ i ].offset = m[ i ].element.offset();
			m[ i ].proportions( {
				width: m[ i ].element[ 0 ].offsetWidth,
				height: m[ i ].element[ 0 ].offsetHeight
			} );

		}

	},
	drop: function( draggable, event ) {

		var dropped = false;

		// Create a copy of the droppables in case the list changes during the drop (#9116)
		$.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {

			if ( !this.options ) {
				return;
			}
			if ( !this.options.disabled && this.visible &&
					intersect( draggable, this, this.options.tolerance, event ) ) {
				dropped = this._drop.call( this, event ) || dropped;
			}

			if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ],
					( draggable.currentItem || draggable.element ) ) ) {
				this.isout = true;
				this.isover = false;
				this._deactivate.call( this, event );
			}

		} );
		return dropped;

	},
	dragStart: function( draggable, event ) {

		// Listen for scrolling so that if the dragging causes scrolling the position of the
		// droppables can be recalculated (see #5003)
		draggable.element.parentsUntil( "body" ).on( "scroll.droppable", function() {
			if ( !draggable.options.refreshPositions ) {
				$.ui.ddmanager.prepareOffsets( draggable, event );
			}
		} );
	},
	drag: function( draggable, event ) {

		// If you have a highly dynamic page, you might try this option. It renders positions
		// every time you move the mouse.
		if ( draggable.options.refreshPositions ) {
			$.ui.ddmanager.prepareOffsets( draggable, event );
		}

		// Run through all droppables and check their positions based on specific tolerance options
		$.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {

			if ( this.options.disabled || this.greedyChild || !this.visible ) {
				return;
			}

			var parentInstance, scope, parent,
				intersects = intersect( draggable, this, this.options.tolerance, event ),
				c = !intersects && this.isover ?
					"isout" :
					( intersects && !this.isover ? "isover" : null );
			if ( !c ) {
				return;
			}

			if ( this.options.greedy ) {

				// find droppable parents with same scope
				scope = this.options.scope;
				parent = this.element.parents( ":data(ui-droppable)" ).filter( function() {
					return $( this ).droppable( "instance" ).options.scope === scope;
				} );

				if ( parent.length ) {
					parentInstance = $( parent[ 0 ] ).droppable( "instance" );
					parentInstance.greedyChild = ( c === "isover" );
				}
			}

			// We just moved into a greedy child
			if ( parentInstance && c === "isover" ) {
				parentInstance.isover = false;
				parentInstance.isout = true;
				parentInstance._out.call( parentInstance, event );
			}

			this[ c ] = true;
			this[ c === "isout" ? "isover" : "isout" ] = false;
			this[ c === "isover" ? "_over" : "_out" ].call( this, event );

			// We just moved out of a greedy child
			if ( parentInstance && c === "isout" ) {
				parentInstance.isout = false;
				parentInstance.isover = true;
				parentInstance._over.call( parentInstance, event );
			}
		} );

	},
	dragStop: function( draggable, event ) {
		draggable.element.parentsUntil( "body" ).off( "scroll.droppable" );

		// Call prepareOffsets one final time since IE does not fire return scroll events when
		// overflow was caused by drag (see #5003)
		if ( !draggable.options.refreshPositions ) {
			$.ui.ddmanager.prepareOffsets( draggable, event );
		}
	}
};

// DEPRECATED
// TODO: switch return back to widget declaration at top of file when this is removed
if ( $.uiBackCompat !== false ) {

	// Backcompat for activeClass and hoverClass options
	$.widget( "ui.droppable", $.ui.droppable, {
		options: {
			hoverClass: false,
			activeClass: false
		},
		_addActiveClass: function() {
			this._super();
			if ( this.options.activeClass ) {
				this.element.addClass( this.options.activeClass );
			}
		},
		_removeActiveClass: function() {
			this._super();
			if ( this.options.activeClass ) {
				this.element.removeClass( this.options.activeClass );
			}
		},
		_addHoverClass: function() {
			this._super();
			if ( this.options.hoverClass ) {
				this.element.addClass( this.options.hoverClass );
			}
		},
		_removeHoverClass: function() {
			this._super();
			if ( this.options.hoverClass ) {
				this.element.removeClass( this.options.hoverClass );
			}
		}
	} );
}

var widgetsDroppable = $.ui.droppable;


/*!
 * jQuery UI Progressbar 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Progressbar
//>>group: Widgets
// jscs:disable maximumLineLength
//>>description: Displays a status indicator for loading state, standard percentage, and other progress indicators.
// jscs:enable maximumLineLength
//>>docs: http://api.jqueryui.com/progressbar/
//>>demos: http://jqueryui.com/progressbar/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/progressbar.css
//>>css.theme: ../../themes/base/theme.css



var widgetsProgressbar = $.widget( "ui.progressbar", {
	version: "1.12.1",
	options: {
		classes: {
			"ui-progressbar": "ui-corner-all",
			"ui-progressbar-value": "ui-corner-left",
			"ui-progressbar-complete": "ui-corner-right"
		},
		max: 100,
		value: 0,

		change: null,
		complete: null
	},

	min: 0,

	_create: function() {

		// Constrain initial value
		this.oldValue = this.options.value = this._constrainedValue();

		this.element.attr( {

			// Only set static values; aria-valuenow and aria-valuemax are
			// set inside _refreshValue()
			role: "progressbar",
			"aria-valuemin": this.min
		} );
		this._addClass( "ui-progressbar", "ui-widget ui-widget-content" );

		this.valueDiv = $( "<div>" ).appendTo( this.element );
		this._addClass( this.valueDiv, "ui-progressbar-value", "ui-widget-header" );
		this._refreshValue();
	},

	_destroy: function() {
		this.element.removeAttr( "role aria-valuemin aria-valuemax aria-valuenow" );

		this.valueDiv.remove();
	},

	value: function( newValue ) {
		if ( newValue === undefined ) {
			return this.options.value;
		}

		this.options.value = this._constrainedValue( newValue );
		this._refreshValue();
	},

	_constrainedValue: function( newValue ) {
		if ( newValue === undefined ) {
			newValue = this.options.value;
		}

		this.indeterminate = newValue === false;

		// Sanitize value
		if ( typeof newValue !== "number" ) {
			newValue = 0;
		}

		return this.indeterminate ? false :
			Math.min( this.options.max, Math.max( this.min, newValue ) );
	},

	_setOptions: function( options ) {

		// Ensure "value" option is set after other values (like max)
		var value = options.value;
		delete options.value;

		this._super( options );

		this.options.value = this._constrainedValue( value );
		this._refreshValue();
	},

	_setOption: function( key, value ) {
		if ( key === "max" ) {

			// Don't allow a max less than min
			value = Math.max( this.min, value );
		}
		this._super( key, value );
	},

	_setOptionDisabled: function( value ) {
		this._super( value );

		this.element.attr( "aria-disabled", value );
		this._toggleClass( null, "ui-state-disabled", !!value );
	},

	_percentage: function() {
		return this.indeterminate ?
			100 :
			100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
	},

	_refreshValue: function() {
		var value = this.options.value,
			percentage = this._percentage();

		this.valueDiv
			.toggle( this.indeterminate || value > this.min )
			.width( percentage.toFixed( 0 ) + "%" );

		this
			._toggleClass( this.valueDiv, "ui-progressbar-complete", null,
				value === this.options.max )
			._toggleClass( "ui-progressbar-indeterminate", null, this.indeterminate );

		if ( this.indeterminate ) {
			this.element.removeAttr( "aria-valuenow" );
			if ( !this.overlayDiv ) {
				this.overlayDiv = $( "<div>" ).appendTo( this.valueDiv );
				this._addClass( this.overlayDiv, "ui-progressbar-overlay" );
			}
		} else {
			this.element.attr( {
				"aria-valuemax": this.options.max,
				"aria-valuenow": value
			} );
			if ( this.overlayDiv ) {
				this.overlayDiv.remove();
				this.overlayDiv = null;
			}
		}

		if ( this.oldValue !== value ) {
			this.oldValue = value;
			this._trigger( "change" );
		}
		if ( value === this.options.max ) {
			this._trigger( "complete" );
		}
	}
} );


/*!
 * jQuery UI Selectable 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Selectable
//>>group: Interactions
//>>description: Allows groups of elements to be selected with the mouse.
//>>docs: http://api.jqueryui.com/selectable/
//>>demos: http://jqueryui.com/selectable/
//>>css.structure: ../../themes/base/selectable.css



var widgetsSelectable = $.widget( "ui.selectable", $.ui.mouse, {
	version: "1.12.1",
	options: {
		appendTo: "body",
		autoRefresh: true,
		distance: 0,
		filter: "*",
		tolerance: "touch",

		// Callbacks
		selected: null,
		selecting: null,
		start: null,
		stop: null,
		unselected: null,
		unselecting: null
	},
	_create: function() {
		var that = this;

		this._addClass( "ui-selectable" );

		this.dragged = false;

		// Cache selectee children based on filter
		this.refresh = function() {
			that.elementPos = $( that.element[ 0 ] ).offset();
			that.selectees = $( that.options.filter, that.element[ 0 ] );
			that._addClass( that.selectees, "ui-selectee" );
			that.selectees.each( function() {
				var $this = $( this ),
					selecteeOffset = $this.offset(),
					pos = {
						left: selecteeOffset.left - that.elementPos.left,
						top: selecteeOffset.top - that.elementPos.top
					};
				$.data( this, "selectable-item", {
					element: this,
					$element: $this,
					left: pos.left,
					top: pos.top,
					right: pos.left + $this.outerWidth(),
					bottom: pos.top + $this.outerHeight(),
					startselected: false,
					selected: $this.hasClass( "ui-selected" ),
					selecting: $this.hasClass( "ui-selecting" ),
					unselecting: $this.hasClass( "ui-unselecting" )
				} );
			} );
		};
		this.refresh();

		this._mouseInit();

		this.helper = $( "<div>" );
		this._addClass( this.helper, "ui-selectable-helper" );
	},

	_destroy: function() {
		this.selectees.removeData( "selectable-item" );
		this._mouseDestroy();
	},

	_mouseStart: function( event ) {
		var that = this,
			options = this.options;

		this.opos = [ event.pageX, event.pageY ];
		this.elementPos = $( this.element[ 0 ] ).offset();

		if ( this.options.disabled ) {
			return;
		}

		this.selectees = $( options.filter, this.element[ 0 ] );

		this._trigger( "start", event );

		$( options.appendTo ).append( this.helper );

		// position helper (lasso)
		this.helper.css( {
			"left": event.pageX,
			"top": event.pageY,
			"width": 0,
			"height": 0
		} );

		if ( options.autoRefresh ) {
			this.refresh();
		}

		this.selectees.filter( ".ui-selected" ).each( function() {
			var selectee = $.data( this, "selectable-item" );
			selectee.startselected = true;
			if ( !event.metaKey && !event.ctrlKey ) {
				that._removeClass( selectee.$element, "ui-selected" );
				selectee.selected = false;
				that._addClass( selectee.$element, "ui-unselecting" );
				selectee.unselecting = true;

				// selectable UNSELECTING callback
				that._trigger( "unselecting", event, {
					unselecting: selectee.element
				} );
			}
		} );

		$( event.target ).parents().addBack().each( function() {
			var doSelect,
				selectee = $.data( this, "selectable-item" );
			if ( selectee ) {
				doSelect = ( !event.metaKey && !event.ctrlKey ) ||
					!selectee.$element.hasClass( "ui-selected" );
				that._removeClass( selectee.$element, doSelect ? "ui-unselecting" : "ui-selected" )
					._addClass( selectee.$element, doSelect ? "ui-selecting" : "ui-unselecting" );
				selectee.unselecting = !doSelect;
				selectee.selecting = doSelect;
				selectee.selected = doSelect;

				// selectable (UN)SELECTING callback
				if ( doSelect ) {
					that._trigger( "selecting", event, {
						selecting: selectee.element
					} );
				} else {
					that._trigger( "unselecting", event, {
						unselecting: selectee.element
					} );
				}
				return false;
			}
		} );

	},

	_mouseDrag: function( event ) {

		this.dragged = true;

		if ( this.options.disabled ) {
			return;
		}

		var tmp,
			that = this,
			options = this.options,
			x1 = this.opos[ 0 ],
			y1 = this.opos[ 1 ],
			x2 = event.pageX,
			y2 = event.pageY;

		if ( x1 > x2 ) { tmp = x2; x2 = x1; x1 = tmp; }
		if ( y1 > y2 ) { tmp = y2; y2 = y1; y1 = tmp; }
		this.helper.css( { left: x1, top: y1, width: x2 - x1, height: y2 - y1 } );

		this.selectees.each( function() {
			var selectee = $.data( this, "selectable-item" ),
				hit = false,
				offset = {};

			//prevent helper from being selected if appendTo: selectable
			if ( !selectee || selectee.element === that.element[ 0 ] ) {
				return;
			}

			offset.left   = selectee.left   + that.elementPos.left;
			offset.right  = selectee.right  + that.elementPos.left;
			offset.top    = selectee.top    + that.elementPos.top;
			offset.bottom = selectee.bottom + that.elementPos.top;

			if ( options.tolerance === "touch" ) {
				hit = ( !( offset.left > x2 || offset.right < x1 || offset.top > y2 ||
                    offset.bottom < y1 ) );
			} else if ( options.tolerance === "fit" ) {
				hit = ( offset.left > x1 && offset.right < x2 && offset.top > y1 &&
                    offset.bottom < y2 );
			}

			if ( hit ) {

				// SELECT
				if ( selectee.selected ) {
					that._removeClass( selectee.$element, "ui-selected" );
					selectee.selected = false;
				}
				if ( selectee.unselecting ) {
					that._removeClass( selectee.$element, "ui-unselecting" );
					selectee.unselecting = false;
				}
				if ( !selectee.selecting ) {
					that._addClass( selectee.$element, "ui-selecting" );
					selectee.selecting = true;

					// selectable SELECTING callback
					that._trigger( "selecting", event, {
						selecting: selectee.element
					} );
				}
			} else {

				// UNSELECT
				if ( selectee.selecting ) {
					if ( ( event.metaKey || event.ctrlKey ) && selectee.startselected ) {
						that._removeClass( selectee.$element, "ui-selecting" );
						selectee.selecting = false;
						that._addClass( selectee.$element, "ui-selected" );
						selectee.selected = true;
					} else {
						that._removeClass( selectee.$element, "ui-selecting" );
						selectee.selecting = false;
						if ( selectee.startselected ) {
							that._addClass( selectee.$element, "ui-unselecting" );
							selectee.unselecting = true;
						}

						// selectable UNSELECTING callback
						that._trigger( "unselecting", event, {
							unselecting: selectee.element
						} );
					}
				}
				if ( selectee.selected ) {
					if ( !event.metaKey && !event.ctrlKey && !selectee.startselected ) {
						that._removeClass( selectee.$element, "ui-selected" );
						selectee.selected = false;

						that._addClass( selectee.$element, "ui-unselecting" );
						selectee.unselecting = true;

						// selectable UNSELECTING callback
						that._trigger( "unselecting", event, {
							unselecting: selectee.element
						} );
					}
				}
			}
		} );

		return false;
	},

	_mouseStop: function( event ) {
		var that = this;

		this.dragged = false;

		$( ".ui-unselecting", this.element[ 0 ] ).each( function() {
			var selectee = $.data( this, "selectable-item" );
			that._removeClass( selectee.$element, "ui-unselecting" );
			selectee.unselecting = false;
			selectee.startselected = false;
			that._trigger( "unselected", event, {
				unselected: selectee.element
			} );
		} );
		$( ".ui-selecting", this.element[ 0 ] ).each( function() {
			var selectee = $.data( this, "selectable-item" );
			that._removeClass( selectee.$element, "ui-selecting" )
				._addClass( selectee.$element, "ui-selected" );
			selectee.selecting = false;
			selectee.selected = true;
			selectee.startselected = true;
			that._trigger( "selected", event, {
				selected: selectee.element
			} );
		} );
		this._trigger( "stop", event );

		this.helper.remove();

		return false;
	}

} );


/*!
 * jQuery UI Selectmenu 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Selectmenu
//>>group: Widgets
// jscs:disable maximumLineLength
//>>description: Duplicates and extends the functionality of a native HTML select element, allowing it to be customizable in behavior and appearance far beyond the limitations of a native select.
// jscs:enable maximumLineLength
//>>docs: http://api.jqueryui.com/selectmenu/
//>>demos: http://jqueryui.com/selectmenu/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/selectmenu.css, ../../themes/base/button.css
//>>css.theme: ../../themes/base/theme.css



var widgetsSelectmenu = $.widget( "ui.selectmenu", [ $.ui.formResetMixin, {
	version: "1.12.1",
	defaultElement: "<select>",
	options: {
		appendTo: null,
		classes: {
			"ui-selectmenu-button-open": "ui-corner-top",
			"ui-selectmenu-button-closed": "ui-corner-all"
		},
		disabled: null,
		icons: {
			button: "ui-icon-triangle-1-s"
		},
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},
		width: false,

		// Callbacks
		change: null,
		close: null,
		focus: null,
		open: null,
		select: null
	},

	_create: function() {
		var selectmenuId = this.element.uniqueId().attr( "id" );
		this.ids = {
			element: selectmenuId,
			button: selectmenuId + "-button",
			menu: selectmenuId + "-menu"
		};

		this._drawButton();
		this._drawMenu();
		this._bindFormResetHandler();

		this._rendered = false;
		this.menuItems = $();
	},

	_drawButton: function() {
		var icon,
			that = this,
			item = this._parseOption(
				this.element.find( "option:selected" ),
				this.element[ 0 ].selectedIndex
			);

		// Associate existing label with the new button
		this.labels = this.element.labels().attr( "for", this.ids.button );
		this._on( this.labels, {
			click: function( event ) {
				this.button.focus();
				event.preventDefault();
			}
		} );

		// Hide original select element
		this.element.hide();

		// Create button
		this.button = $( "<span>", {
			tabindex: this.options.disabled ? -1 : 0,
			id: this.ids.button,
			role: "combobox",
			"aria-expanded": "false",
			"aria-autocomplete": "list",
			"aria-owns": this.ids.menu,
			"aria-haspopup": "true",
			title: this.element.attr( "title" )
		} )
			.insertAfter( this.element );

		this._addClass( this.button, "ui-selectmenu-button ui-selectmenu-button-closed",
			"ui-button ui-widget" );

		icon = $( "<span>" ).appendTo( this.button );
		this._addClass( icon, "ui-selectmenu-icon", "ui-icon " + this.options.icons.button );
		this.buttonItem = this._renderButtonItem( item )
			.appendTo( this.button );

		if ( this.options.width !== false ) {
			this._resizeButton();
		}

		this._on( this.button, this._buttonEvents );
		this.button.one( "focusin", function() {

			// Delay rendering the menu items until the button receives focus.
			// The menu may have already been rendered via a programmatic open.
			if ( !that._rendered ) {
				that._refreshMenu();
			}
		} );
	},

	_drawMenu: function() {
		var that = this;

		// Create menu
		this.menu = $( "<ul>", {
			"aria-hidden": "true",
			"aria-labelledby": this.ids.button,
			id: this.ids.menu
		} );

		// Wrap menu
		this.menuWrap = $( "<div>" ).append( this.menu );
		this._addClass( this.menuWrap, "ui-selectmenu-menu", "ui-front" );
		this.menuWrap.appendTo( this._appendTo() );

		// Initialize menu widget
		this.menuInstance = this.menu
			.menu( {
				classes: {
					"ui-menu": "ui-corner-bottom"
				},
				role: "listbox",
				select: function( event, ui ) {
					event.preventDefault();

					// Support: IE8
					// If the item was selected via a click, the text selection
					// will be destroyed in IE
					that._setSelection();

					that._select( ui.item.data( "ui-selectmenu-item" ), event );
				},
				focus: function( event, ui ) {
					var item = ui.item.data( "ui-selectmenu-item" );

					// Prevent inital focus from firing and check if its a newly focused item
					if ( that.focusIndex != null && item.index !== that.focusIndex ) {
						that._trigger( "focus", event, { item: item } );
						if ( !that.isOpen ) {
							that._select( item, event );
						}
					}
					that.focusIndex = item.index;

					that.button.attr( "aria-activedescendant",
						that.menuItems.eq( item.index ).attr( "id" ) );
				}
			} )
			.menu( "instance" );

		// Don't close the menu on mouseleave
		this.menuInstance._off( this.menu, "mouseleave" );

		// Cancel the menu's collapseAll on document click
		this.menuInstance._closeOnDocumentClick = function() {
			return false;
		};

		// Selects often contain empty items, but never contain dividers
		this.menuInstance._isDivider = function() {
			return false;
		};
	},

	refresh: function() {
		this._refreshMenu();
		this.buttonItem.replaceWith(
			this.buttonItem = this._renderButtonItem(

				// Fall back to an empty object in case there are no options
				this._getSelectedItem().data( "ui-selectmenu-item" ) || {}
			)
		);
		if ( this.options.width === null ) {
			this._resizeButton();
		}
	},

	_refreshMenu: function() {
		var item,
			options = this.element.find( "option" );

		this.menu.empty();

		this._parseOptions( options );
		this._renderMenu( this.menu, this.items );

		this.menuInstance.refresh();
		this.menuItems = this.menu.find( "li" )
			.not( ".ui-selectmenu-optgroup" )
				.find( ".ui-menu-item-wrapper" );

		this._rendered = true;

		if ( !options.length ) {
			return;
		}

		item = this._getSelectedItem();

		// Update the menu to have the correct item focused
		this.menuInstance.focus( null, item );
		this._setAria( item.data( "ui-selectmenu-item" ) );

		// Set disabled state
		this._setOption( "disabled", this.element.prop( "disabled" ) );
	},

	open: function( event ) {
		if ( this.options.disabled ) {
			return;
		}

		// If this is the first time the menu is being opened, render the items
		if ( !this._rendered ) {
			this._refreshMenu();
		} else {

			// Menu clears focus on close, reset focus to selected item
			this._removeClass( this.menu.find( ".ui-state-active" ), null, "ui-state-active" );
			this.menuInstance.focus( null, this._getSelectedItem() );
		}

		// If there are no options, don't open the menu
		if ( !this.menuItems.length ) {
			return;
		}

		this.isOpen = true;
		this._toggleAttr();
		this._resizeMenu();
		this._position();

		this._on( this.document, this._documentClick );

		this._trigger( "open", event );
	},

	_position: function() {
		this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) );
	},

	close: function( event ) {
		if ( !this.isOpen ) {
			return;
		}

		this.isOpen = false;
		this._toggleAttr();

		this.range = null;
		this._off( this.document );

		this._trigger( "close", event );
	},

	widget: function() {
		return this.button;
	},

	menuWidget: function() {
		return this.menu;
	},

	_renderButtonItem: function( item ) {
		var buttonItem = $( "<span>" );

		this._setText( buttonItem, item.label );
		this._addClass( buttonItem, "ui-selectmenu-text" );

		return buttonItem;
	},

	_renderMenu: function( ul, items ) {
		var that = this,
			currentOptgroup = "";

		$.each( items, function( index, item ) {
			var li;

			if ( item.optgroup !== currentOptgroup ) {
				li = $( "<li>", {
					text: item.optgroup
				} );
				that._addClass( li, "ui-selectmenu-optgroup", "ui-menu-divider" +
					( item.element.parent( "optgroup" ).prop( "disabled" ) ?
						" ui-state-disabled" :
						"" ) );

				li.appendTo( ul );

				currentOptgroup = item.optgroup;
			}

			that._renderItemData( ul, item );
		} );
	},

	_renderItemData: function( ul, item ) {
		return this._renderItem( ul, item ).data( "ui-selectmenu-item", item );
	},

	_renderItem: function( ul, item ) {
		var li = $( "<li>" ),
			wrapper = $( "<div>", {
				title: item.element.attr( "title" )
			} );

		if ( item.disabled ) {
			this._addClass( li, null, "ui-state-disabled" );
		}
		this._setText( wrapper, item.label );

		return li.append( wrapper ).appendTo( ul );
	},

	_setText: function( element, value ) {
		if ( value ) {
			element.text( value );
		} else {
			element.html( "&#160;" );
		}
	},

	_move: function( direction, event ) {
		var item, next,
			filter = ".ui-menu-item";

		if ( this.isOpen ) {
			item = this.menuItems.eq( this.focusIndex ).parent( "li" );
		} else {
			item = this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
			filter += ":not(.ui-state-disabled)";
		}

		if ( direction === "first" || direction === "last" ) {
			next = item[ direction === "first" ? "prevAll" : "nextAll" ]( filter ).eq( -1 );
		} else {
			next = item[ direction + "All" ]( filter ).eq( 0 );
		}

		if ( next.length ) {
			this.menuInstance.focus( event, next );
		}
	},

	_getSelectedItem: function() {
		return this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
	},

	_toggle: function( event ) {
		this[ this.isOpen ? "close" : "open" ]( event );
	},

	_setSelection: function() {
		var selection;

		if ( !this.range ) {
			return;
		}

		if ( window.getSelection ) {
			selection = window.getSelection();
			selection.removeAllRanges();
			selection.addRange( this.range );

		// Support: IE8
		} else {
			this.range.select();
		}

		// Support: IE
		// Setting the text selection kills the button focus in IE, but
		// restoring the focus doesn't kill the selection.
		this.button.focus();
	},

	_documentClick: {
		mousedown: function( event ) {
			if ( !this.isOpen ) {
				return;
			}

			if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" +
					$.ui.escapeSelector( this.ids.button ) ).length ) {
				this.close( event );
			}
		}
	},

	_buttonEvents: {

		// Prevent text selection from being reset when interacting with the selectmenu (#10144)
		mousedown: function() {
			var selection;

			if ( window.getSelection ) {
				selection = window.getSelection();
				if ( selection.rangeCount ) {
					this.range = selection.getRangeAt( 0 );
				}

			// Support: IE8
			} else {
				this.range = document.selection.createRange();
			}
		},

		click: function( event ) {
			this._setSelection();
			this._toggle( event );
		},

		keydown: function( event ) {
			var preventDefault = true;
			switch ( event.keyCode ) {
			case $.ui.keyCode.TAB:
			case $.ui.keyCode.ESCAPE:
				this.close( event );
				preventDefault = false;
				break;
			case $.ui.keyCode.ENTER:
				if ( this.isOpen ) {
					this._selectFocusedItem( event );
				}
				break;
			case $.ui.keyCode.UP:
				if ( event.altKey ) {
					this._toggle( event );
				} else {
					this._move( "prev", event );
				}
				break;
			case $.ui.keyCode.DOWN:
				if ( event.altKey ) {
					this._toggle( event );
				} else {
					this._move( "next", event );
				}
				break;
			case $.ui.keyCode.SPACE:
				if ( this.isOpen ) {
					this._selectFocusedItem( event );
				} else {
					this._toggle( event );
				}
				break;
			case $.ui.keyCode.LEFT:
				this._move( "prev", event );
				break;
			case $.ui.keyCode.RIGHT:
				this._move( "next", event );
				break;
			case $.ui.keyCode.HOME:
			case $.ui.keyCode.PAGE_UP:
				this._move( "first", event );
				break;
			case $.ui.keyCode.END:
			case $.ui.keyCode.PAGE_DOWN:
				this._move( "last", event );
				break;
			default:
				this.menu.trigger( event );
				preventDefault = false;
			}

			if ( preventDefault ) {
				event.preventDefault();
			}
		}
	},

	_selectFocusedItem: function( event ) {
		var item = this.menuItems.eq( this.focusIndex ).parent( "li" );
		if ( !item.hasClass( "ui-state-disabled" ) ) {
			this._select( item.data( "ui-selectmenu-item" ), event );
		}
	},

	_select: function( item, event ) {
		var oldIndex = this.element[ 0 ].selectedIndex;

		// Change native select element
		this.element[ 0 ].selectedIndex = item.index;
		this.buttonItem.replaceWith( this.buttonItem = this._renderButtonItem( item ) );
		this._setAria( item );
		this._trigger( "select", event, { item: item } );

		if ( item.index !== oldIndex ) {
			this._trigger( "change", event, { item: item } );
		}

		this.close( event );
	},

	_setAria: function( item ) {
		var id = this.menuItems.eq( item.index ).attr( "id" );

		this.button.attr( {
			"aria-labelledby": id,
			"aria-activedescendant": id
		} );
		this.menu.attr( "aria-activedescendant", id );
	},

	_setOption: function( key, value ) {
		if ( key === "icons" ) {
			var icon = this.button.find( "span.ui-icon" );
			this._removeClass( icon, null, this.options.icons.button )
				._addClass( icon, null, value.button );
		}

		this._super( key, value );

		if ( key === "appendTo" ) {
			this.menuWrap.appendTo( this._appendTo() );
		}

		if ( key === "width" ) {
			this._resizeButton();
		}
	},

	_setOptionDisabled: function( value ) {
		this._super( value );

		this.menuInstance.option( "disabled", value );
		this.button.attr( "aria-disabled", value );
		this._toggleClass( this.button, null, "ui-state-disabled", value );

		this.element.prop( "disabled", value );
		if ( value ) {
			this.button.attr( "tabindex", -1 );
			this.close();
		} else {
			this.button.attr( "tabindex", 0 );
		}
	},

	_appendTo: function() {
		var element = this.options.appendTo;

		if ( element ) {
			element = element.jquery || element.nodeType ?
				$( element ) :
				this.document.find( element ).eq( 0 );
		}

		if ( !element || !element[ 0 ] ) {
			element = this.element.closest( ".ui-front, dialog" );
		}

		if ( !element.length ) {
			element = this.document[ 0 ].body;
		}

		return element;
	},

	_toggleAttr: function() {
		this.button.attr( "aria-expanded", this.isOpen );

		// We can't use two _toggleClass() calls here, because we need to make sure
		// we always remove classes first and add them second, otherwise if both classes have the
		// same theme class, it will be removed after we add it.
		this._removeClass( this.button, "ui-selectmenu-button-" +
			( this.isOpen ? "closed" : "open" ) )
			._addClass( this.button, "ui-selectmenu-button-" +
				( this.isOpen ? "open" : "closed" ) )
			._toggleClass( this.menuWrap, "ui-selectmenu-open", null, this.isOpen );

		this.menu.attr( "aria-hidden", !this.isOpen );
	},

	_resizeButton: function() {
		var width = this.options.width;

		// For `width: false`, just remove inline style and stop
		if ( width === false ) {
			this.button.css( "width", "" );
			return;
		}

		// For `width: null`, match the width of the original element
		if ( width === null ) {
			width = this.element.show().outerWidth();
			this.element.hide();
		}

		this.button.outerWidth( width );
	},

	_resizeMenu: function() {
		this.menu.outerWidth( Math.max(
			this.button.outerWidth(),

			// Support: IE10
			// IE10 wraps long text (possibly a rounding bug)
			// so we add 1px to avoid the wrapping
			this.menu.width( "" ).outerWidth() + 1
		) );
	},

	_getCreateOptions: function() {
		var options = this._super();

		options.disabled = this.element.prop( "disabled" );

		return options;
	},

	_parseOptions: function( options ) {
		var that = this,
			data = [];
		options.each( function( index, item ) {
			data.push( that._parseOption( $( item ), index ) );
		} );
		this.items = data;
	},

	_parseOption: function( option, index ) {
		var optgroup = option.parent( "optgroup" );

		return {
			element: option,
			index: index,
			value: option.val(),
			label: option.text(),
			optgroup: optgroup.attr( "label" ) || "",
			disabled: optgroup.prop( "disabled" ) || option.prop( "disabled" )
		};
	},

	_destroy: function() {
		this._unbindFormResetHandler();
		this.menuWrap.remove();
		this.button.remove();
		this.element.show();
		this.element.removeUniqueId();
		this.labels.attr( "for", this.ids.element );
	}
} ] );


/*!
 * jQuery UI Slider 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Slider
//>>group: Widgets
//>>description: Displays a flexible slider with ranges and accessibility via keyboard.
//>>docs: http://api.jqueryui.com/slider/
//>>demos: http://jqueryui.com/slider/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/slider.css
//>>css.theme: ../../themes/base/theme.css



var widgetsSlider = $.widget( "ui.slider", $.ui.mouse, {
	version: "1.12.1",
	widgetEventPrefix: "slide",

	options: {
		animate: false,
		classes: {
			"ui-slider": "ui-corner-all",
			"ui-slider-handle": "ui-corner-all",

			// Note: ui-widget-header isn't the most fittingly semantic framework class for this
			// element, but worked best visually with a variety of themes
			"ui-slider-range": "ui-corner-all ui-widget-header"
		},
		distance: 0,
		max: 100,
		min: 0,
		orientation: "horizontal",
		range: false,
		step: 1,
		value: 0,
		values: null,

		// Callbacks
		change: null,
		slide: null,
		start: null,
		stop: null
	},

	// Number of pages in a slider
	// (how many times can you page up/down to go through the whole range)
	numPages: 5,

	_create: function() {
		this._keySliding = false;
		this._mouseSliding = false;
		this._animateOff = true;
		this._handleIndex = null;
		this._detectOrientation();
		this._mouseInit();
		this._calculateNewMax();

		this._addClass( "ui-slider ui-slider-" + this.orientation,
			"ui-widget ui-widget-content" );

		this._refresh();

		this._animateOff = false;
	},

	_refresh: function() {
		this._createRange();
		this._createHandles();
		this._setupEvents();
		this._refreshValue();
	},

	_createHandles: function() {
		var i, handleCount,
			options = this.options,
			existingHandles = this.element.find( ".ui-slider-handle" ),
			handle = "<span tabindex='0'></span>",
			handles = [];

		handleCount = ( options.values && options.values.length ) || 1;

		if ( existingHandles.length > handleCount ) {
			existingHandles.slice( handleCount ).remove();
			existingHandles = existingHandles.slice( 0, handleCount );
		}

		for ( i = existingHandles.length; i < handleCount; i++ ) {
			handles.push( handle );
		}

		this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );

		this._addClass( this.handles, "ui-slider-handle", "ui-state-default" );

		this.handle = this.handles.eq( 0 );

		this.handles.each( function( i ) {
			$( this )
				.data( "ui-slider-handle-index", i )
				.attr( "tabIndex", 0 );
		} );
	},

	_createRange: function() {
		var options = this.options;

		if ( options.range ) {
			if ( options.range === true ) {
				if ( !options.values ) {
					options.values = [ this._valueMin(), this._valueMin() ];
				} else if ( options.values.length && options.values.length !== 2 ) {
					options.values = [ options.values[ 0 ], options.values[ 0 ] ];
				} else if ( $.isArray( options.values ) ) {
					options.values = options.values.slice( 0 );
				}
			}

			if ( !this.range || !this.range.length ) {
				this.range = $( "<div>" )
					.appendTo( this.element );

				this._addClass( this.range, "ui-slider-range" );
			} else {
				this._removeClass( this.range, "ui-slider-range-min ui-slider-range-max" );

				// Handle range switching from true to min/max
				this.range.css( {
					"left": "",
					"bottom": ""
				} );
			}
			if ( options.range === "min" || options.range === "max" ) {
				this._addClass( this.range, "ui-slider-range-" + options.range );
			}
		} else {
			if ( this.range ) {
				this.range.remove();
			}
			this.range = null;
		}
	},

	_setupEvents: function() {
		this._off( this.handles );
		this._on( this.handles, this._handleEvents );
		this._hoverable( this.handles );
		this._focusable( this.handles );
	},

	_destroy: function() {
		this.handles.remove();
		if ( this.range ) {
			this.range.remove();
		}

		this._mouseDestroy();
	},

	_mouseCapture: function( event ) {
		var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
			that = this,
			o = this.options;

		if ( o.disabled ) {
			return false;
		}

		this.elementSize = {
			width: this.element.outerWidth(),
			height: this.element.outerHeight()
		};
		this.elementOffset = this.element.offset();

		position = { x: event.pageX, y: event.pageY };
		normValue = this._normValueFromMouse( position );
		distance = this._valueMax() - this._valueMin() + 1;
		this.handles.each( function( i ) {
			var thisDistance = Math.abs( normValue - that.values( i ) );
			if ( ( distance > thisDistance ) ||
				( distance === thisDistance &&
					( i === that._lastChangedValue || that.values( i ) === o.min ) ) ) {
				distance = thisDistance;
				closestHandle = $( this );
				index = i;
			}
		} );

		allowed = this._start( event, index );
		if ( allowed === false ) {
			return false;
		}
		this._mouseSliding = true;

		this._handleIndex = index;

		this._addClass( closestHandle, null, "ui-state-active" );
		closestHandle.trigger( "focus" );

		offset = closestHandle.offset();
		mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
		this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
			left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
			top: event.pageY - offset.top -
				( closestHandle.height() / 2 ) -
				( parseInt( closestHandle.css( "borderTopWidth" ), 10 ) || 0 ) -
				( parseInt( closestHandle.css( "borderBottomWidth" ), 10 ) || 0 ) +
				( parseInt( closestHandle.css( "marginTop" ), 10 ) || 0 )
		};

		if ( !this.handles.hasClass( "ui-state-hover" ) ) {
			this._slide( event, index, normValue );
		}
		this._animateOff = true;
		return true;
	},

	_mouseStart: function() {
		return true;
	},

	_mouseDrag: function( event ) {
		var position = { x: event.pageX, y: event.pageY },
			normValue = this._normValueFromMouse( position );

		this._slide( event, this._handleIndex, normValue );

		return false;
	},

	_mouseStop: function( event ) {
		this._removeClass( this.handles, null, "ui-state-active" );
		this._mouseSliding = false;

		this._stop( event, this._handleIndex );
		this._change( event, this._handleIndex );

		this._handleIndex = null;
		this._clickOffset = null;
		this._animateOff = false;

		return false;
	},

	_detectOrientation: function() {
		this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
	},

	_normValueFromMouse: function( position ) {
		var pixelTotal,
			pixelMouse,
			percentMouse,
			valueTotal,
			valueMouse;

		if ( this.orientation === "horizontal" ) {
			pixelTotal = this.elementSize.width;
			pixelMouse = position.x - this.elementOffset.left -
				( this._clickOffset ? this._clickOffset.left : 0 );
		} else {
			pixelTotal = this.elementSize.height;
			pixelMouse = position.y - this.elementOffset.top -
				( this._clickOffset ? this._clickOffset.top : 0 );
		}

		percentMouse = ( pixelMouse / pixelTotal );
		if ( percentMouse > 1 ) {
			percentMouse = 1;
		}
		if ( percentMouse < 0 ) {
			percentMouse = 0;
		}
		if ( this.orientation === "vertical" ) {
			percentMouse = 1 - percentMouse;
		}

		valueTotal = this._valueMax() - this._valueMin();
		valueMouse = this._valueMin() + percentMouse * valueTotal;

		return this._trimAlignValue( valueMouse );
	},

	_uiHash: function( index, value, values ) {
		var uiHash = {
			handle: this.handles[ index ],
			handleIndex: index,
			value: value !== undefined ? value : this.value()
		};

		if ( this._hasMultipleValues() ) {
			uiHash.value = value !== undefined ? value : this.values( index );
			uiHash.values = values || this.values();
		}

		return uiHash;
	},

	_hasMultipleValues: function() {
		return this.options.values && this.options.values.length;
	},

	_start: function( event, index ) {
		return this._trigger( "start", event, this._uiHash( index ) );
	},

	_slide: function( event, index, newVal ) {
		var allowed, otherVal,
			currentValue = this.value(),
			newValues = this.values();

		if ( this._hasMultipleValues() ) {
			otherVal = this.values( index ? 0 : 1 );
			currentValue = this.values( index );

			if ( this.options.values.length === 2 && this.options.range === true ) {
				newVal =  index === 0 ? Math.min( otherVal, newVal ) : Math.max( otherVal, newVal );
			}

			newValues[ index ] = newVal;
		}

		if ( newVal === currentValue ) {
			return;
		}

		allowed = this._trigger( "slide", event, this._uiHash( index, newVal, newValues ) );

		// A slide can be canceled by returning false from the slide callback
		if ( allowed === false ) {
			return;
		}

		if ( this._hasMultipleValues() ) {
			this.values( index, newVal );
		} else {
			this.value( newVal );
		}
	},

	_stop: function( event, index ) {
		this._trigger( "stop", event, this._uiHash( index ) );
	},

	_change: function( event, index ) {
		if ( !this._keySliding && !this._mouseSliding ) {

			//store the last changed value index for reference when handles overlap
			this._lastChangedValue = index;
			this._trigger( "change", event, this._uiHash( index ) );
		}
	},

	value: function( newValue ) {
		if ( arguments.length ) {
			this.options.value = this._trimAlignValue( newValue );
			this._refreshValue();
			this._change( null, 0 );
			return;
		}

		return this._value();
	},

	values: function( index, newValue ) {
		var vals,
			newValues,
			i;

		if ( arguments.length > 1 ) {
			this.options.values[ index ] = this._trimAlignValue( newValue );
			this._refreshValue();
			this._change( null, index );
			return;
		}

		if ( arguments.length ) {
			if ( $.isArray( arguments[ 0 ] ) ) {
				vals = this.options.values;
				newValues = arguments[ 0 ];
				for ( i = 0; i < vals.length; i += 1 ) {
					vals[ i ] = this._trimAlignValue( newValues[ i ] );
					this._change( null, i );
				}
				this._refreshValue();
			} else {
				if ( this._hasMultipleValues() ) {
					return this._values( index );
				} else {
					return this.value();
				}
			}
		} else {
			return this._values();
		}
	},

	_setOption: function( key, value ) {
		var i,
			valsLength = 0;

		if ( key === "range" && this.options.range === true ) {
			if ( value === "min" ) {
				this.options.value = this._values( 0 );
				this.options.values = null;
			} else if ( value === "max" ) {
				this.options.value = this._values( this.options.values.length - 1 );
				this.options.values = null;
			}
		}

		if ( $.isArray( this.options.values ) ) {
			valsLength = this.options.values.length;
		}

		this._super( key, value );

		switch ( key ) {
			case "orientation":
				this._detectOrientation();
				this._removeClass( "ui-slider-horizontal ui-slider-vertical" )
					._addClass( "ui-slider-" + this.orientation );
				this._refreshValue();
				if ( this.options.range ) {
					this._refreshRange( value );
				}

				// Reset positioning from previous orientation
				this.handles.css( value === "horizontal" ? "bottom" : "left", "" );
				break;
			case "value":
				this._animateOff = true;
				this._refreshValue();
				this._change( null, 0 );
				this._animateOff = false;
				break;
			case "values":
				this._animateOff = true;
				this._refreshValue();

				// Start from the last handle to prevent unreachable handles (#9046)
				for ( i = valsLength - 1; i >= 0; i-- ) {
					this._change( null, i );
				}
				this._animateOff = false;
				break;
			case "step":
			case "min":
			case "max":
				this._animateOff = true;
				this._calculateNewMax();
				this._refreshValue();
				this._animateOff = false;
				break;
			case "range":
				this._animateOff = true;
				this._refresh();
				this._animateOff = false;
				break;
		}
	},

	_setOptionDisabled: function( value ) {
		this._super( value );

		this._toggleClass( null, "ui-state-disabled", !!value );
	},

	//internal value getter
	// _value() returns value trimmed by min and max, aligned by step
	_value: function() {
		var val = this.options.value;
		val = this._trimAlignValue( val );

		return val;
	},

	//internal values getter
	// _values() returns array of values trimmed by min and max, aligned by step
	// _values( index ) returns single value trimmed by min and max, aligned by step
	_values: function( index ) {
		var val,
			vals,
			i;

		if ( arguments.length ) {
			val = this.options.values[ index ];
			val = this._trimAlignValue( val );

			return val;
		} else if ( this._hasMultipleValues() ) {

			// .slice() creates a copy of the array
			// this copy gets trimmed by min and max and then returned
			vals = this.options.values.slice();
			for ( i = 0; i < vals.length; i += 1 ) {
				vals[ i ] = this._trimAlignValue( vals[ i ] );
			}

			return vals;
		} else {
			return [];
		}
	},

	// Returns the step-aligned value that val is closest to, between (inclusive) min and max
	_trimAlignValue: function( val ) {
		if ( val <= this._valueMin() ) {
			return this._valueMin();
		}
		if ( val >= this._valueMax() ) {
			return this._valueMax();
		}
		var step = ( this.options.step > 0 ) ? this.options.step : 1,
			valModStep = ( val - this._valueMin() ) % step,
			alignValue = val - valModStep;

		if ( Math.abs( valModStep ) * 2 >= step ) {
			alignValue += ( valModStep > 0 ) ? step : ( -step );
		}

		// Since JavaScript has problems with large floats, round
		// the final value to 5 digits after the decimal point (see #4124)
		return parseFloat( alignValue.toFixed( 5 ) );
	},

	_calculateNewMax: function() {
		var max = this.options.max,
			min = this._valueMin(),
			step = this.options.step,
			aboveMin = Math.round( ( max - min ) / step ) * step;
		max = aboveMin + min;
		if ( max > this.options.max ) {

			//If max is not divisible by step, rounding off may increase its value
			max -= step;
		}
		this.max = parseFloat( max.toFixed( this._precision() ) );
	},

	_precision: function() {
		var precision = this._precisionOf( this.options.step );
		if ( this.options.min !== null ) {
			precision = Math.max( precision, this._precisionOf( this.options.min ) );
		}
		return precision;
	},

	_precisionOf: function( num ) {
		var str = num.toString(),
			decimal = str.indexOf( "." );
		return decimal === -1 ? 0 : str.length - decimal - 1;
	},

	_valueMin: function() {
		return this.options.min;
	},

	_valueMax: function() {
		return this.max;
	},

	_refreshRange: function( orientation ) {
		if ( orientation === "vertical" ) {
			this.range.css( { "width": "", "left": "" } );
		}
		if ( orientation === "horizontal" ) {
			this.range.css( { "height": "", "bottom": "" } );
		}
	},

	_refreshValue: function() {
		var lastValPercent, valPercent, value, valueMin, valueMax,
			oRange = this.options.range,
			o = this.options,
			that = this,
			animate = ( !this._animateOff ) ? o.animate : false,
			_set = {};

		if ( this._hasMultipleValues() ) {
			this.handles.each( function( i ) {
				valPercent = ( that.values( i ) - that._valueMin() ) / ( that._valueMax() -
					that._valueMin() ) * 100;
				_set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
				$( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
				if ( that.options.range === true ) {
					if ( that.orientation === "horizontal" ) {
						if ( i === 0 ) {
							that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
								left: valPercent + "%"
							}, o.animate );
						}
						if ( i === 1 ) {
							that.range[ animate ? "animate" : "css" ]( {
								width: ( valPercent - lastValPercent ) + "%"
							}, {
								queue: false,
								duration: o.animate
							} );
						}
					} else {
						if ( i === 0 ) {
							that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
								bottom: ( valPercent ) + "%"
							}, o.animate );
						}
						if ( i === 1 ) {
							that.range[ animate ? "animate" : "css" ]( {
								height: ( valPercent - lastValPercent ) + "%"
							}, {
								queue: false,
								duration: o.animate
							} );
						}
					}
				}
				lastValPercent = valPercent;
			} );
		} else {
			value = this.value();
			valueMin = this._valueMin();
			valueMax = this._valueMax();
			valPercent = ( valueMax !== valueMin ) ?
					( value - valueMin ) / ( valueMax - valueMin ) * 100 :
					0;
			_set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
			this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );

			if ( oRange === "min" && this.orientation === "horizontal" ) {
				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
					width: valPercent + "%"
				}, o.animate );
			}
			if ( oRange === "max" && this.orientation === "horizontal" ) {
				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
					width: ( 100 - valPercent ) + "%"
				}, o.animate );
			}
			if ( oRange === "min" && this.orientation === "vertical" ) {
				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
					height: valPercent + "%"
				}, o.animate );
			}
			if ( oRange === "max" && this.orientation === "vertical" ) {
				this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
					height: ( 100 - valPercent ) + "%"
				}, o.animate );
			}
		}
	},

	_handleEvents: {
		keydown: function( event ) {
			var allowed, curVal, newVal, step,
				index = $( event.target ).data( "ui-slider-handle-index" );

			switch ( event.keyCode ) {
				case $.ui.keyCode.HOME:
				case $.ui.keyCode.END:
				case $.ui.keyCode.PAGE_UP:
				case $.ui.keyCode.PAGE_DOWN:
				case $.ui.keyCode.UP:
				case $.ui.keyCode.RIGHT:
				case $.ui.keyCode.DOWN:
				case $.ui.keyCode.LEFT:
					event.preventDefault();
					if ( !this._keySliding ) {
						this._keySliding = true;
						this._addClass( $( event.target ), null, "ui-state-active" );
						allowed = this._start( event, index );
						if ( allowed === false ) {
							return;
						}
					}
					break;
			}

			step = this.options.step;
			if ( this._hasMultipleValues() ) {
				curVal = newVal = this.values( index );
			} else {
				curVal = newVal = this.value();
			}

			switch ( event.keyCode ) {
				case $.ui.keyCode.HOME:
					newVal = this._valueMin();
					break;
				case $.ui.keyCode.END:
					newVal = this._valueMax();
					break;
				case $.ui.keyCode.PAGE_UP:
					newVal = this._trimAlignValue(
						curVal + ( ( this._valueMax() - this._valueMin() ) / this.numPages )
					);
					break;
				case $.ui.keyCode.PAGE_DOWN:
					newVal = this._trimAlignValue(
						curVal - ( ( this._valueMax() - this._valueMin() ) / this.numPages ) );
					break;
				case $.ui.keyCode.UP:
				case $.ui.keyCode.RIGHT:
					if ( curVal === this._valueMax() ) {
						return;
					}
					newVal = this._trimAlignValue( curVal + step );
					break;
				case $.ui.keyCode.DOWN:
				case $.ui.keyCode.LEFT:
					if ( curVal === this._valueMin() ) {
						return;
					}
					newVal = this._trimAlignValue( curVal - step );
					break;
			}

			this._slide( event, index, newVal );
		},
		keyup: function( event ) {
			var index = $( event.target ).data( "ui-slider-handle-index" );

			if ( this._keySliding ) {
				this._keySliding = false;
				this._stop( event, index );
				this._change( event, index );
				this._removeClass( $( event.target ), null, "ui-state-active" );
			}
		}
	}
} );


/*!
 * jQuery UI Sortable 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Sortable
//>>group: Interactions
//>>description: Enables items in a list to be sorted using the mouse.
//>>docs: http://api.jqueryui.com/sortable/
//>>demos: http://jqueryui.com/sortable/
//>>css.structure: ../../themes/base/sortable.css



var widgetsSortable = $.widget( "ui.sortable", $.ui.mouse, {
	version: "1.12.1",
	widgetEventPrefix: "sort",
	ready: false,
	options: {
		appendTo: "parent",
		axis: false,
		connectWith: false,
		containment: false,
		cursor: "auto",
		cursorAt: false,
		dropOnEmpty: true,
		forcePlaceholderSize: false,
		forceHelperSize: false,
		grid: false,
		handle: false,
		helper: "original",
		items: "> *",
		opacity: false,
		placeholder: false,
		revert: false,
		scroll: true,
		scrollSensitivity: 20,
		scrollSpeed: 20,
		scope: "default",
		tolerance: "intersect",
		zIndex: 1000,

		// Callbacks
		activate: null,
		beforeStop: null,
		change: null,
		deactivate: null,
		out: null,
		over: null,
		receive: null,
		remove: null,
		sort: null,
		start: null,
		stop: null,
		update: null
	},

	_isOverAxis: function( x, reference, size ) {
		return ( x >= reference ) && ( x < ( reference + size ) );
	},

	_isFloating: function( item ) {
		return ( /left|right/ ).test( item.css( "float" ) ) ||
			( /inline|table-cell/ ).test( item.css( "display" ) );
	},

	_create: function() {
		this.containerCache = {};
		this._addClass( "ui-sortable" );

		//Get the items
		this.refresh();

		//Let's determine the parent's offset
		this.offset = this.element.offset();

		//Initialize mouse events for interaction
		this._mouseInit();

		this._setHandleClassName();

		//We're ready to go
		this.ready = true;

	},

	_setOption: function( key, value ) {
		this._super( key, value );

		if ( key === "handle" ) {
			this._setHandleClassName();
		}
	},

	_setHandleClassName: function() {
		var that = this;
		this._removeClass( this.element.find( ".ui-sortable-handle" ), "ui-sortable-handle" );
		$.each( this.items, function() {
			that._addClass(
				this.instance.options.handle ?
					this.item.find( this.instance.options.handle ) :
					this.item,
				"ui-sortable-handle"
			);
		} );
	},

	_destroy: function() {
		this._mouseDestroy();

		for ( var i = this.items.length - 1; i >= 0; i-- ) {
			this.items[ i ].item.removeData( this.widgetName + "-item" );
		}

		return this;
	},

	_mouseCapture: function( event, overrideHandle ) {
		var currentItem = null,
			validHandle = false,
			that = this;

		if ( this.reverting ) {
			return false;
		}

		if ( this.options.disabled || this.options.type === "static" ) {
			return false;
		}

		//We have to refresh the items data once first
		this._refreshItems( event );

		//Find out if the clicked node (or one of its parents) is a actual item in this.items
		$( event.target ).parents().each( function() {
			if ( $.data( this, that.widgetName + "-item" ) === that ) {
				currentItem = $( this );
				return false;
			}
		} );
		if ( $.data( event.target, that.widgetName + "-item" ) === that ) {
			currentItem = $( event.target );
		}

		if ( !currentItem ) {
			return false;
		}
		if ( this.options.handle && !overrideHandle ) {
			$( this.options.handle, currentItem ).find( "*" ).addBack().each( function() {
				if ( this === event.target ) {
					validHandle = true;
				}
			} );
			if ( !validHandle ) {
				return false;
			}
		}

		this.currentItem = currentItem;
		this._removeCurrentsFromItems();
		return true;

	},

	_mouseStart: function( event, overrideHandle, noActivation ) {

		var i, body,
			o = this.options;

		this.currentContainer = this;

		//We only need to call refreshPositions, because the refreshItems call has been moved to
		// mouseCapture
		this.refreshPositions();

		//Create and append the visible helper
		this.helper = this._createHelper( event );

		//Cache the helper size
		this._cacheHelperProportions();

		/*
		 * - Position generation -
		 * This block generates everything position related - it's the core of draggables.
		 */

		//Cache the margins of the original element
		this._cacheMargins();

		//Get the next scrolling parent
		this.scrollParent = this.helper.scrollParent();

		//The element's absolute position on the page minus margins
		this.offset = this.currentItem.offset();
		this.offset = {
			top: this.offset.top - this.margins.top,
			left: this.offset.left - this.margins.left
		};

		$.extend( this.offset, {
			click: { //Where the click happened, relative to the element
				left: event.pageX - this.offset.left,
				top: event.pageY - this.offset.top
			},
			parent: this._getParentOffset(),

			// This is a relative to absolute position minus the actual position calculation -
			// only used for relative positioned helper
			relative: this._getRelativeOffset()
		} );

		// Only after we got the offset, we can change the helper's position to absolute
		// TODO: Still need to figure out a way to make relative sorting possible
		this.helper.css( "position", "absolute" );
		this.cssPosition = this.helper.css( "position" );

		//Generate the original position
		this.originalPosition = this._generatePosition( event );
		this.originalPageX = event.pageX;
		this.originalPageY = event.pageY;

		//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
		( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) );

		//Cache the former DOM position
		this.domPosition = {
			prev: this.currentItem.prev()[ 0 ],
			parent: this.currentItem.parent()[ 0 ]
		};

		// If the helper is not the original, hide the original so it's not playing any role during
		// the drag, won't cause anything bad this way
		if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
			this.currentItem.hide();
		}

		//Create the placeholder
		this._createPlaceholder();

		//Set a containment if given in the options
		if ( o.containment ) {
			this._setContainment();
		}

		if ( o.cursor && o.cursor !== "auto" ) { // cursor option
			body = this.document.find( "body" );

			// Support: IE
			this.storedCursor = body.css( "cursor" );
			body.css( "cursor", o.cursor );

			this.storedStylesheet =
				$( "<style>*{ cursor: " + o.cursor + " !important; }</style>" ).appendTo( body );
		}

		if ( o.opacity ) { // opacity option
			if ( this.helper.css( "opacity" ) ) {
				this._storedOpacity = this.helper.css( "opacity" );
			}
			this.helper.css( "opacity", o.opacity );
		}

		if ( o.zIndex ) { // zIndex option
			if ( this.helper.css( "zIndex" ) ) {
				this._storedZIndex = this.helper.css( "zIndex" );
			}
			this.helper.css( "zIndex", o.zIndex );
		}

		//Prepare scrolling
		if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
				this.scrollParent[ 0 ].tagName !== "HTML" ) {
			this.overflowOffset = this.scrollParent.offset();
		}

		//Call callbacks
		this._trigger( "start", event, this._uiHash() );

		//Recache the helper size
		if ( !this._preserveHelperProportions ) {
			this._cacheHelperProportions();
		}

		//Post "activate" events to possible containers
		if ( !noActivation ) {
			for ( i = this.containers.length - 1; i >= 0; i-- ) {
				this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
			}
		}

		//Prepare possible droppables
		if ( $.ui.ddmanager ) {
			$.ui.ddmanager.current = this;
		}

		if ( $.ui.ddmanager && !o.dropBehaviour ) {
			$.ui.ddmanager.prepareOffsets( this, event );
		}

		this.dragging = true;

		this._addClass( this.helper, "ui-sortable-helper" );

		// Execute the drag once - this causes the helper not to be visiblebefore getting its
		// correct position
		this._mouseDrag( event );
		return true;

	},

	_mouseDrag: function( event ) {
		var i, item, itemElement, intersection,
			o = this.options,
			scrolled = false;

		//Compute the helpers position
		this.position = this._generatePosition( event );
		this.positionAbs = this._convertPositionTo( "absolute" );

		if ( !this.lastPositionAbs ) {
			this.lastPositionAbs = this.positionAbs;
		}

		//Do scrolling
		if ( this.options.scroll ) {
			if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
					this.scrollParent[ 0 ].tagName !== "HTML" ) {

				if ( ( this.overflowOffset.top + this.scrollParent[ 0 ].offsetHeight ) -
						event.pageY < o.scrollSensitivity ) {
					this.scrollParent[ 0 ].scrollTop =
						scrolled = this.scrollParent[ 0 ].scrollTop + o.scrollSpeed;
				} else if ( event.pageY - this.overflowOffset.top < o.scrollSensitivity ) {
					this.scrollParent[ 0 ].scrollTop =
						scrolled = this.scrollParent[ 0 ].scrollTop - o.scrollSpeed;
				}

				if ( ( this.overflowOffset.left + this.scrollParent[ 0 ].offsetWidth ) -
						event.pageX < o.scrollSensitivity ) {
					this.scrollParent[ 0 ].scrollLeft = scrolled =
						this.scrollParent[ 0 ].scrollLeft + o.scrollSpeed;
				} else if ( event.pageX - this.overflowOffset.left < o.scrollSensitivity ) {
					this.scrollParent[ 0 ].scrollLeft = scrolled =
						this.scrollParent[ 0 ].scrollLeft - o.scrollSpeed;
				}

			} else {

				if ( event.pageY - this.document.scrollTop() < o.scrollSensitivity ) {
					scrolled = this.document.scrollTop( this.document.scrollTop() - o.scrollSpeed );
				} else if ( this.window.height() - ( event.pageY - this.document.scrollTop() ) <
						o.scrollSensitivity ) {
					scrolled = this.document.scrollTop( this.document.scrollTop() + o.scrollSpeed );
				}

				if ( event.pageX - this.document.scrollLeft() < o.scrollSensitivity ) {
					scrolled = this.document.scrollLeft(
						this.document.scrollLeft() - o.scrollSpeed
					);
				} else if ( this.window.width() - ( event.pageX - this.document.scrollLeft() ) <
						o.scrollSensitivity ) {
					scrolled = this.document.scrollLeft(
						this.document.scrollLeft() + o.scrollSpeed
					);
				}

			}

			if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
				$.ui.ddmanager.prepareOffsets( this, event );
			}
		}

		//Regenerate the absolute position used for position checks
		this.positionAbs = this._convertPositionTo( "absolute" );

		//Set the helper position
		if ( !this.options.axis || this.options.axis !== "y" ) {
			this.helper[ 0 ].style.left = this.position.left + "px";
		}
		if ( !this.options.axis || this.options.axis !== "x" ) {
			this.helper[ 0 ].style.top = this.position.top + "px";
		}

		//Rearrange
		for ( i = this.items.length - 1; i >= 0; i-- ) {

			//Cache variables and intersection, continue if no intersection
			item = this.items[ i ];
			itemElement = item.item[ 0 ];
			intersection = this._intersectsWithPointer( item );
			if ( !intersection ) {
				continue;
			}

			// Only put the placeholder inside the current Container, skip all
			// items from other containers. This works because when moving
			// an item from one container to another the
			// currentContainer is switched before the placeholder is moved.
			//
			// Without this, moving items in "sub-sortables" can cause
			// the placeholder to jitter between the outer and inner container.
			if ( item.instance !== this.currentContainer ) {
				continue;
			}

			// Cannot intersect with itself
			// no useless actions that have been done before
			// no action if the item moved is the parent of the item checked
			if ( itemElement !== this.currentItem[ 0 ] &&
				this.placeholder[ intersection === 1 ? "next" : "prev" ]()[ 0 ] !== itemElement &&
				!$.contains( this.placeholder[ 0 ], itemElement ) &&
				( this.options.type === "semi-dynamic" ?
					!$.contains( this.element[ 0 ], itemElement ) :
					true
				)
			) {

				this.direction = intersection === 1 ? "down" : "up";

				if ( this.options.tolerance === "pointer" || this._intersectsWithSides( item ) ) {
					this._rearrange( event, item );
				} else {
					break;
				}

				this._trigger( "change", event, this._uiHash() );
				break;
			}
		}

		//Post events to containers
		this._contactContainers( event );

		//Interconnect with droppables
		if ( $.ui.ddmanager ) {
			$.ui.ddmanager.drag( this, event );
		}

		//Call callbacks
		this._trigger( "sort", event, this._uiHash() );

		this.lastPositionAbs = this.positionAbs;
		return false;

	},

	_mouseStop: function( event, noPropagation ) {

		if ( !event ) {
			return;
		}

		//If we are using droppables, inform the manager about the drop
		if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
			$.ui.ddmanager.drop( this, event );
		}

		if ( this.options.revert ) {
			var that = this,
				cur = this.placeholder.offset(),
				axis = this.options.axis,
				animation = {};

			if ( !axis || axis === "x" ) {
				animation.left = cur.left - this.offset.parent.left - this.margins.left +
					( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
						0 :
						this.offsetParent[ 0 ].scrollLeft
					);
			}
			if ( !axis || axis === "y" ) {
				animation.top = cur.top - this.offset.parent.top - this.margins.top +
					( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
						0 :
						this.offsetParent[ 0 ].scrollTop
					);
			}
			this.reverting = true;
			$( this.helper ).animate(
				animation,
				parseInt( this.options.revert, 10 ) || 500,
				function() {
					that._clear( event );
				}
			);
		} else {
			this._clear( event, noPropagation );
		}

		return false;

	},

	cancel: function() {

		if ( this.dragging ) {

			this._mouseUp( new $.Event( "mouseup", { target: null } ) );

			if ( this.options.helper === "original" ) {
				this.currentItem.css( this._storedCSS );
				this._removeClass( this.currentItem, "ui-sortable-helper" );
			} else {
				this.currentItem.show();
			}

			//Post deactivating events to containers
			for ( var i = this.containers.length - 1; i >= 0; i-- ) {
				this.containers[ i ]._trigger( "deactivate", null, this._uiHash( this ) );
				if ( this.containers[ i ].containerCache.over ) {
					this.containers[ i ]._trigger( "out", null, this._uiHash( this ) );
					this.containers[ i ].containerCache.over = 0;
				}
			}

		}

		if ( this.placeholder ) {

			//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
			// it unbinds ALL events from the original node!
			if ( this.placeholder[ 0 ].parentNode ) {
				this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
			}
			if ( this.options.helper !== "original" && this.helper &&
					this.helper[ 0 ].parentNode ) {
				this.helper.remove();
			}

			$.extend( this, {
				helper: null,
				dragging: false,
				reverting: false,
				_noFinalSort: null
			} );

			if ( this.domPosition.prev ) {
				$( this.domPosition.prev ).after( this.currentItem );
			} else {
				$( this.domPosition.parent ).prepend( this.currentItem );
			}
		}

		return this;

	},

	serialize: function( o ) {

		var items = this._getItemsAsjQuery( o && o.connected ),
			str = [];
		o = o || {};

		$( items ).each( function() {
			var res = ( $( o.item || this ).attr( o.attribute || "id" ) || "" )
				.match( o.expression || ( /(.+)[\-=_](.+)/ ) );
			if ( res ) {
				str.push(
					( o.key || res[ 1 ] + "[]" ) +
					"=" + ( o.key && o.expression ? res[ 1 ] : res[ 2 ] ) );
			}
		} );

		if ( !str.length && o.key ) {
			str.push( o.key + "=" );
		}

		return str.join( "&" );

	},

	toArray: function( o ) {

		var items = this._getItemsAsjQuery( o && o.connected ),
			ret = [];

		o = o || {};

		items.each( function() {
			ret.push( $( o.item || this ).attr( o.attribute || "id" ) || "" );
		} );
		return ret;

	},

	/* Be careful with the following core functions */
	_intersectsWith: function( item ) {

		var x1 = this.positionAbs.left,
			x2 = x1 + this.helperProportions.width,
			y1 = this.positionAbs.top,
			y2 = y1 + this.helperProportions.height,
			l = item.left,
			r = l + item.width,
			t = item.top,
			b = t + item.height,
			dyClick = this.offset.click.top,
			dxClick = this.offset.click.left,
			isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t &&
				( y1 + dyClick ) < b ),
			isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l &&
				( x1 + dxClick ) < r ),
			isOverElement = isOverElementHeight && isOverElementWidth;

		if ( this.options.tolerance === "pointer" ||
			this.options.forcePointerForContainers ||
			( this.options.tolerance !== "pointer" &&
				this.helperProportions[ this.floating ? "width" : "height" ] >
				item[ this.floating ? "width" : "height" ] )
		) {
			return isOverElement;
		} else {

			return ( l < x1 + ( this.helperProportions.width / 2 ) && // Right Half
				x2 - ( this.helperProportions.width / 2 ) < r && // Left Half
				t < y1 + ( this.helperProportions.height / 2 ) && // Bottom Half
				y2 - ( this.helperProportions.height / 2 ) < b ); // Top Half

		}
	},

	_intersectsWithPointer: function( item ) {
		var verticalDirection, horizontalDirection,
			isOverElementHeight = ( this.options.axis === "x" ) ||
				this._isOverAxis(
					this.positionAbs.top + this.offset.click.top, item.top, item.height ),
			isOverElementWidth = ( this.options.axis === "y" ) ||
				this._isOverAxis(
					this.positionAbs.left + this.offset.click.left, item.left, item.width ),
			isOverElement = isOverElementHeight && isOverElementWidth;

		if ( !isOverElement ) {
			return false;
		}

		verticalDirection = this._getDragVerticalDirection();
		horizontalDirection = this._getDragHorizontalDirection();

		return this.floating ?
			( ( horizontalDirection === "right" || verticalDirection === "down" ) ? 2 : 1 )
			: ( verticalDirection && ( verticalDirection === "down" ? 2 : 1 ) );

	},

	_intersectsWithSides: function( item ) {

		var isOverBottomHalf = this._isOverAxis( this.positionAbs.top +
				this.offset.click.top, item.top + ( item.height / 2 ), item.height ),
			isOverRightHalf = this._isOverAxis( this.positionAbs.left +
				this.offset.click.left, item.left + ( item.width / 2 ), item.width ),
			verticalDirection = this._getDragVerticalDirection(),
			horizontalDirection = this._getDragHorizontalDirection();

		if ( this.floating && horizontalDirection ) {
			return ( ( horizontalDirection === "right" && isOverRightHalf ) ||
				( horizontalDirection === "left" && !isOverRightHalf ) );
		} else {
			return verticalDirection && ( ( verticalDirection === "down" && isOverBottomHalf ) ||
				( verticalDirection === "up" && !isOverBottomHalf ) );
		}

	},

	_getDragVerticalDirection: function() {
		var delta = this.positionAbs.top - this.lastPositionAbs.top;
		return delta !== 0 && ( delta > 0 ? "down" : "up" );
	},

	_getDragHorizontalDirection: function() {
		var delta = this.positionAbs.left - this.lastPositionAbs.left;
		return delta !== 0 && ( delta > 0 ? "right" : "left" );
	},

	refresh: function( event ) {
		this._refreshItems( event );
		this._setHandleClassName();
		this.refreshPositions();
		return this;
	},

	_connectWith: function() {
		var options = this.options;
		return options.connectWith.constructor === String ?
			[ options.connectWith ] :
			options.connectWith;
	},

	_getItemsAsjQuery: function( connected ) {

		var i, j, cur, inst,
			items = [],
			queries = [],
			connectWith = this._connectWith();

		if ( connectWith && connected ) {
			for ( i = connectWith.length - 1; i >= 0; i-- ) {
				cur = $( connectWith[ i ], this.document[ 0 ] );
				for ( j = cur.length - 1; j >= 0; j-- ) {
					inst = $.data( cur[ j ], this.widgetFullName );
					if ( inst && inst !== this && !inst.options.disabled ) {
						queries.push( [ $.isFunction( inst.options.items ) ?
							inst.options.items.call( inst.element ) :
							$( inst.options.items, inst.element )
								.not( ".ui-sortable-helper" )
								.not( ".ui-sortable-placeholder" ), inst ] );
					}
				}
			}
		}

		queries.push( [ $.isFunction( this.options.items ) ?
			this.options.items
				.call( this.element, null, { options: this.options, item: this.currentItem } ) :
			$( this.options.items, this.element )
				.not( ".ui-sortable-helper" )
				.not( ".ui-sortable-placeholder" ), this ] );

		function addItems() {
			items.push( this );
		}
		for ( i = queries.length - 1; i >= 0; i-- ) {
			queries[ i ][ 0 ].each( addItems );
		}

		return $( items );

	},

	_removeCurrentsFromItems: function() {

		var list = this.currentItem.find( ":data(" + this.widgetName + "-item)" );

		this.items = $.grep( this.items, function( item ) {
			for ( var j = 0; j < list.length; j++ ) {
				if ( list[ j ] === item.item[ 0 ] ) {
					return false;
				}
			}
			return true;
		} );

	},

	_refreshItems: function( event ) {

		this.items = [];
		this.containers = [ this ];

		var i, j, cur, inst, targetData, _queries, item, queriesLength,
			items = this.items,
			queries = [ [ $.isFunction( this.options.items ) ?
				this.options.items.call( this.element[ 0 ], event, { item: this.currentItem } ) :
				$( this.options.items, this.element ), this ] ],
			connectWith = this._connectWith();

		//Shouldn't be run the first time through due to massive slow-down
		if ( connectWith && this.ready ) {
			for ( i = connectWith.length - 1; i >= 0; i-- ) {
				cur = $( connectWith[ i ], this.document[ 0 ] );
				for ( j = cur.length - 1; j >= 0; j-- ) {
					inst = $.data( cur[ j ], this.widgetFullName );
					if ( inst && inst !== this && !inst.options.disabled ) {
						queries.push( [ $.isFunction( inst.options.items ) ?
							inst.options.items
								.call( inst.element[ 0 ], event, { item: this.currentItem } ) :
							$( inst.options.items, inst.element ), inst ] );
						this.containers.push( inst );
					}
				}
			}
		}

		for ( i = queries.length - 1; i >= 0; i-- ) {
			targetData = queries[ i ][ 1 ];
			_queries = queries[ i ][ 0 ];

			for ( j = 0, queriesLength = _queries.length; j < queriesLength; j++ ) {
				item = $( _queries[ j ] );

				// Data for target checking (mouse manager)
				item.data( this.widgetName + "-item", targetData );

				items.push( {
					item: item,
					instance: targetData,
					width: 0, height: 0,
					left: 0, top: 0
				} );
			}
		}

	},

	refreshPositions: function( fast ) {

		// Determine whether items are being displayed horizontally
		this.floating = this.items.length ?
			this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) :
			false;

		//This has to be redone because due to the item being moved out/into the offsetParent,
		// the offsetParent's position will change
		if ( this.offsetParent && this.helper ) {
			this.offset.parent = this._getParentOffset();
		}

		var i, item, t, p;

		for ( i = this.items.length - 1; i >= 0; i-- ) {
			item = this.items[ i ];

			//We ignore calculating positions of all connected containers when we're not over them
			if ( item.instance !== this.currentContainer && this.currentContainer &&
					item.item[ 0 ] !== this.currentItem[ 0 ] ) {
				continue;
			}

			t = this.options.toleranceElement ?
				$( this.options.toleranceElement, item.item ) :
				item.item;

			if ( !fast ) {
				item.width = t.outerWidth();
				item.height = t.outerHeight();
			}

			p = t.offset();
			item.left = p.left;
			item.top = p.top;
		}

		if ( this.options.custom && this.options.custom.refreshContainers ) {
			this.options.custom.refreshContainers.call( this );
		} else {
			for ( i = this.containers.length - 1; i >= 0; i-- ) {
				p = this.containers[ i ].element.offset();
				this.containers[ i ].containerCache.left = p.left;
				this.containers[ i ].containerCache.top = p.top;
				this.containers[ i ].containerCache.width =
					this.containers[ i ].element.outerWidth();
				this.containers[ i ].containerCache.height =
					this.containers[ i ].element.outerHeight();
			}
		}

		return this;
	},

	_createPlaceholder: function( that ) {
		that = that || this;
		var className,
			o = that.options;

		if ( !o.placeholder || o.placeholder.constructor === String ) {
			className = o.placeholder;
			o.placeholder = {
				element: function() {

					var nodeName = that.currentItem[ 0 ].nodeName.toLowerCase(),
						element = $( "<" + nodeName + ">", that.document[ 0 ] );

						that._addClass( element, "ui-sortable-placeholder",
								className || that.currentItem[ 0 ].className )
							._removeClass( element, "ui-sortable-helper" );

					if ( nodeName === "tbody" ) {
						that._createTrPlaceholder(
							that.currentItem.find( "tr" ).eq( 0 ),
							$( "<tr>", that.document[ 0 ] ).appendTo( element )
						);
					} else if ( nodeName === "tr" ) {
						that._createTrPlaceholder( that.currentItem, element );
					} else if ( nodeName === "img" ) {
						element.attr( "src", that.currentItem.attr( "src" ) );
					}

					if ( !className ) {
						element.css( "visibility", "hidden" );
					}

					return element;
				},
				update: function( container, p ) {

					// 1. If a className is set as 'placeholder option, we don't force sizes -
					// the class is responsible for that
					// 2. The option 'forcePlaceholderSize can be enabled to force it even if a
					// class name is specified
					if ( className && !o.forcePlaceholderSize ) {
						return;
					}

					//If the element doesn't have a actual height by itself (without styles coming
					// from a stylesheet), it receives the inline height from the dragged item
					if ( !p.height() ) {
						p.height(
							that.currentItem.innerHeight() -
							parseInt( that.currentItem.css( "paddingTop" ) || 0, 10 ) -
							parseInt( that.currentItem.css( "paddingBottom" ) || 0, 10 ) );
					}
					if ( !p.width() ) {
						p.width(
							that.currentItem.innerWidth() -
							parseInt( that.currentItem.css( "paddingLeft" ) || 0, 10 ) -
							parseInt( that.currentItem.css( "paddingRight" ) || 0, 10 ) );
					}
				}
			};
		}

		//Create the placeholder
		that.placeholder = $( o.placeholder.element.call( that.element, that.currentItem ) );

		//Append it after the actual current item
		that.currentItem.after( that.placeholder );

		//Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
		o.placeholder.update( that, that.placeholder );

	},

	_createTrPlaceholder: function( sourceTr, targetTr ) {
		var that = this;

		sourceTr.children().each( function() {
			$( "<td>&#160;</td>", that.document[ 0 ] )
				.attr( "colspan", $( this ).attr( "colspan" ) || 1 )
				.appendTo( targetTr );
		} );
	},

	_contactContainers: function( event ) {
		var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom,
			floating, axis,
			innermostContainer = null,
			innermostIndex = null;

		// Get innermost container that intersects with item
		for ( i = this.containers.length - 1; i >= 0; i-- ) {

			// Never consider a container that's located within the item itself
			if ( $.contains( this.currentItem[ 0 ], this.containers[ i ].element[ 0 ] ) ) {
				continue;
			}

			if ( this._intersectsWith( this.containers[ i ].containerCache ) ) {

				// If we've already found a container and it's more "inner" than this, then continue
				if ( innermostContainer &&
						$.contains(
							this.containers[ i ].element[ 0 ],
							innermostContainer.element[ 0 ] ) ) {
					continue;
				}

				innermostContainer = this.containers[ i ];
				innermostIndex = i;

			} else {

				// container doesn't intersect. trigger "out" event if necessary
				if ( this.containers[ i ].containerCache.over ) {
					this.containers[ i ]._trigger( "out", event, this._uiHash( this ) );
					this.containers[ i ].containerCache.over = 0;
				}
			}

		}

		// If no intersecting containers found, return
		if ( !innermostContainer ) {
			return;
		}

		// Move the item into the container if it's not there already
		if ( this.containers.length === 1 ) {
			if ( !this.containers[ innermostIndex ].containerCache.over ) {
				this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
				this.containers[ innermostIndex ].containerCache.over = 1;
			}
		} else {

			// When entering a new container, we will find the item with the least distance and
			// append our item near it
			dist = 10000;
			itemWithLeastDistance = null;
			floating = innermostContainer.floating || this._isFloating( this.currentItem );
			posProperty = floating ? "left" : "top";
			sizeProperty = floating ? "width" : "height";
			axis = floating ? "pageX" : "pageY";

			for ( j = this.items.length - 1; j >= 0; j-- ) {
				if ( !$.contains(
						this.containers[ innermostIndex ].element[ 0 ], this.items[ j ].item[ 0 ] )
				) {
					continue;
				}
				if ( this.items[ j ].item[ 0 ] === this.currentItem[ 0 ] ) {
					continue;
				}

				cur = this.items[ j ].item.offset()[ posProperty ];
				nearBottom = false;
				if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) {
					nearBottom = true;
				}

				if ( Math.abs( event[ axis ] - cur ) < dist ) {
					dist = Math.abs( event[ axis ] - cur );
					itemWithLeastDistance = this.items[ j ];
					this.direction = nearBottom ? "up" : "down";
				}
			}

			//Check if dropOnEmpty is enabled
			if ( !itemWithLeastDistance && !this.options.dropOnEmpty ) {
				return;
			}

			if ( this.currentContainer === this.containers[ innermostIndex ] ) {
				if ( !this.currentContainer.containerCache.over ) {
					this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() );
					this.currentContainer.containerCache.over = 1;
				}
				return;
			}

			itemWithLeastDistance ?
				this._rearrange( event, itemWithLeastDistance, null, true ) :
				this._rearrange( event, null, this.containers[ innermostIndex ].element, true );
			this._trigger( "change", event, this._uiHash() );
			this.containers[ innermostIndex ]._trigger( "change", event, this._uiHash( this ) );
			this.currentContainer = this.containers[ innermostIndex ];

			//Update the placeholder
			this.options.placeholder.update( this.currentContainer, this.placeholder );

			this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
			this.containers[ innermostIndex ].containerCache.over = 1;
		}

	},

	_createHelper: function( event ) {

		var o = this.options,
			helper = $.isFunction( o.helper ) ?
				$( o.helper.apply( this.element[ 0 ], [ event, this.currentItem ] ) ) :
				( o.helper === "clone" ? this.currentItem.clone() : this.currentItem );

		//Add the helper to the DOM if that didn't happen already
		if ( !helper.parents( "body" ).length ) {
			$( o.appendTo !== "parent" ?
				o.appendTo :
				this.currentItem[ 0 ].parentNode )[ 0 ].appendChild( helper[ 0 ] );
		}

		if ( helper[ 0 ] === this.currentItem[ 0 ] ) {
			this._storedCSS = {
				width: this.currentItem[ 0 ].style.width,
				height: this.currentItem[ 0 ].style.height,
				position: this.currentItem.css( "position" ),
				top: this.currentItem.css( "top" ),
				left: this.currentItem.css( "left" )
			};
		}

		if ( !helper[ 0 ].style.width || o.forceHelperSize ) {
			helper.width( this.currentItem.width() );
		}
		if ( !helper[ 0 ].style.height || o.forceHelperSize ) {
			helper.height( this.currentItem.height() );
		}

		return helper;

	},

	_adjustOffsetFromHelper: function( obj ) {
		if ( typeof obj === "string" ) {
			obj = obj.split( " " );
		}
		if ( $.isArray( obj ) ) {
			obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
		}
		if ( "left" in obj ) {
			this.offset.click.left = obj.left + this.margins.left;
		}
		if ( "right" in obj ) {
			this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
		}
		if ( "top" in obj ) {
			this.offset.click.top = obj.top + this.margins.top;
		}
		if ( "bottom" in obj ) {
			this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
		}
	},

	_getParentOffset: function() {

		//Get the offsetParent and cache its position
		this.offsetParent = this.helper.offsetParent();
		var po = this.offsetParent.offset();

		// This is a special case where we need to modify a offset calculated on start, since the
		// following happened:
		// 1. The position of the helper is absolute, so it's position is calculated based on the
		// next positioned parent
		// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
		// the document, which means that the scroll is included in the initial calculation of the
		// offset of the parent, and never recalculated upon drag
		if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== this.document[ 0 ] &&
				$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
			po.left += this.scrollParent.scrollLeft();
			po.top += this.scrollParent.scrollTop();
		}

		// This needs to be actually done for all browsers, since pageX/pageY includes this
		// information with an ugly IE fix
		if ( this.offsetParent[ 0 ] === this.document[ 0 ].body ||
				( this.offsetParent[ 0 ].tagName &&
				this.offsetParent[ 0 ].tagName.toLowerCase() === "html" && $.ui.ie ) ) {
			po = { top: 0, left: 0 };
		}

		return {
			top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
			left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
		};

	},

	_getRelativeOffset: function() {

		if ( this.cssPosition === "relative" ) {
			var p = this.currentItem.position();
			return {
				top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
					this.scrollParent.scrollTop(),
				left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
					this.scrollParent.scrollLeft()
			};
		} else {
			return { top: 0, left: 0 };
		}

	},

	_cacheMargins: function() {
		this.margins = {
			left: ( parseInt( this.currentItem.css( "marginLeft" ), 10 ) || 0 ),
			top: ( parseInt( this.currentItem.css( "marginTop" ), 10 ) || 0 )
		};
	},

	_cacheHelperProportions: function() {
		this.helperProportions = {
			width: this.helper.outerWidth(),
			height: this.helper.outerHeight()
		};
	},

	_setContainment: function() {

		var ce, co, over,
			o = this.options;
		if ( o.containment === "parent" ) {
			o.containment = this.helper[ 0 ].parentNode;
		}
		if ( o.containment === "document" || o.containment === "window" ) {
			this.containment = [
				0 - this.offset.relative.left - this.offset.parent.left,
				0 - this.offset.relative.top - this.offset.parent.top,
				o.containment === "document" ?
					this.document.width() :
					this.window.width() - this.helperProportions.width - this.margins.left,
				( o.containment === "document" ?
					( this.document.height() || document.body.parentNode.scrollHeight ) :
					this.window.height() || this.document[ 0 ].body.parentNode.scrollHeight
				) - this.helperProportions.height - this.margins.top
			];
		}

		if ( !( /^(document|window|parent)$/ ).test( o.containment ) ) {
			ce = $( o.containment )[ 0 ];
			co = $( o.containment ).offset();
			over = ( $( ce ).css( "overflow" ) !== "hidden" );

			this.containment = [
				co.left + ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) +
					( parseInt( $( ce ).css( "paddingLeft" ), 10 ) || 0 ) - this.margins.left,
				co.top + ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) +
					( parseInt( $( ce ).css( "paddingTop" ), 10 ) || 0 ) - this.margins.top,
				co.left + ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
					( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) -
					( parseInt( $( ce ).css( "paddingRight" ), 10 ) || 0 ) -
					this.helperProportions.width - this.margins.left,
				co.top + ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
					( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) -
					( parseInt( $( ce ).css( "paddingBottom" ), 10 ) || 0 ) -
					this.helperProportions.height - this.margins.top
			];
		}

	},

	_convertPositionTo: function( d, pos ) {

		if ( !pos ) {
			pos = this.position;
		}
		var mod = d === "absolute" ? 1 : -1,
			scroll = this.cssPosition === "absolute" &&
				!( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
				$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
					this.offsetParent :
					this.scrollParent,
			scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );

		return {
			top: (

				// The absolute mouse position
				pos.top	+

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.top * mod +

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.top * mod -
				( ( this.cssPosition === "fixed" ?
					-this.scrollParent.scrollTop() :
					( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod )
			),
			left: (

				// The absolute mouse position
				pos.left +

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.left * mod +

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.left * mod	-
				( ( this.cssPosition === "fixed" ?
					-this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 :
					scroll.scrollLeft() ) * mod )
			)
		};

	},

	_generatePosition: function( event ) {

		var top, left,
			o = this.options,
			pageX = event.pageX,
			pageY = event.pageY,
			scroll = this.cssPosition === "absolute" &&
				!( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
				$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
					this.offsetParent :
					this.scrollParent,
				scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );

		// This is another very weird special case that only happens for relative elements:
		// 1. If the css position is relative
		// 2. and the scroll parent is the document or similar to the offset parent
		// we have to refresh the relative offset during the scroll so there are no jumps
		if ( this.cssPosition === "relative" && !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
				this.scrollParent[ 0 ] !== this.offsetParent[ 0 ] ) ) {
			this.offset.relative = this._getRelativeOffset();
		}

		/*
		 * - Position constraining -
		 * Constrain the position to a mix of grid, containment.
		 */

		if ( this.originalPosition ) { //If we are not dragging yet, we won't check for options

			if ( this.containment ) {
				if ( event.pageX - this.offset.click.left < this.containment[ 0 ] ) {
					pageX = this.containment[ 0 ] + this.offset.click.left;
				}
				if ( event.pageY - this.offset.click.top < this.containment[ 1 ] ) {
					pageY = this.containment[ 1 ] + this.offset.click.top;
				}
				if ( event.pageX - this.offset.click.left > this.containment[ 2 ] ) {
					pageX = this.containment[ 2 ] + this.offset.click.left;
				}
				if ( event.pageY - this.offset.click.top > this.containment[ 3 ] ) {
					pageY = this.containment[ 3 ] + this.offset.click.top;
				}
			}

			if ( o.grid ) {
				top = this.originalPageY + Math.round( ( pageY - this.originalPageY ) /
					o.grid[ 1 ] ) * o.grid[ 1 ];
				pageY = this.containment ?
					( ( top - this.offset.click.top >= this.containment[ 1 ] &&
						top - this.offset.click.top <= this.containment[ 3 ] ) ?
							top :
							( ( top - this.offset.click.top >= this.containment[ 1 ] ) ?
								top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) :
								top;

				left = this.originalPageX + Math.round( ( pageX - this.originalPageX ) /
					o.grid[ 0 ] ) * o.grid[ 0 ];
				pageX = this.containment ?
					( ( left - this.offset.click.left >= this.containment[ 0 ] &&
						left - this.offset.click.left <= this.containment[ 2 ] ) ?
							left :
							( ( left - this.offset.click.left >= this.containment[ 0 ] ) ?
								left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) :
								left;
			}

		}

		return {
			top: (

				// The absolute mouse position
				pageY -

				// Click offset (relative to the element)
				this.offset.click.top -

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.top -

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.top +
				( ( this.cssPosition === "fixed" ?
					-this.scrollParent.scrollTop() :
					( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) )
			),
			left: (

				// The absolute mouse position
				pageX -

				// Click offset (relative to the element)
				this.offset.click.left -

				// Only for relative positioned nodes: Relative offset from element to offset parent
				this.offset.relative.left -

				// The offsetParent's offset without borders (offset + border)
				this.offset.parent.left +
				( ( this.cssPosition === "fixed" ?
					-this.scrollParent.scrollLeft() :
					scrollIsRootNode ? 0 : scroll.scrollLeft() ) )
			)
		};

	},

	_rearrange: function( event, i, a, hardRefresh ) {

		a ? a[ 0 ].appendChild( this.placeholder[ 0 ] ) :
			i.item[ 0 ].parentNode.insertBefore( this.placeholder[ 0 ],
				( this.direction === "down" ? i.item[ 0 ] : i.item[ 0 ].nextSibling ) );

		//Various things done here to improve the performance:
		// 1. we create a setTimeout, that calls refreshPositions
		// 2. on the instance, we have a counter variable, that get's higher after every append
		// 3. on the local scope, we copy the counter variable, and check in the timeout,
		// if it's still the same
		// 4. this lets only the last addition to the timeout stack through
		this.counter = this.counter ? ++this.counter : 1;
		var counter = this.counter;

		this._delay( function() {
			if ( counter === this.counter ) {

				//Precompute after each DOM insertion, NOT on mousemove
				this.refreshPositions( !hardRefresh );
			}
		} );

	},

	_clear: function( event, noPropagation ) {

		this.reverting = false;

		// We delay all events that have to be triggered to after the point where the placeholder
		// has been removed and everything else normalized again
		var i,
			delayedTriggers = [];

		// We first have to update the dom position of the actual currentItem
		// Note: don't do it if the current item is already removed (by a user), or it gets
		// reappended (see #4088)
		if ( !this._noFinalSort && this.currentItem.parent().length ) {
			this.placeholder.before( this.currentItem );
		}
		this._noFinalSort = null;

		if ( this.helper[ 0 ] === this.currentItem[ 0 ] ) {
			for ( i in this._storedCSS ) {
				if ( this._storedCSS[ i ] === "auto" || this._storedCSS[ i ] === "static" ) {
					this._storedCSS[ i ] = "";
				}
			}
			this.currentItem.css( this._storedCSS );
			this._removeClass( this.currentItem, "ui-sortable-helper" );
		} else {
			this.currentItem.show();
		}

		if ( this.fromOutside && !noPropagation ) {
			delayedTriggers.push( function( event ) {
				this._trigger( "receive", event, this._uiHash( this.fromOutside ) );
			} );
		}
		if ( ( this.fromOutside ||
				this.domPosition.prev !==
				this.currentItem.prev().not( ".ui-sortable-helper" )[ 0 ] ||
				this.domPosition.parent !== this.currentItem.parent()[ 0 ] ) && !noPropagation ) {

			// Trigger update callback if the DOM position has changed
			delayedTriggers.push( function( event ) {
				this._trigger( "update", event, this._uiHash() );
			} );
		}

		// Check if the items Container has Changed and trigger appropriate
		// events.
		if ( this !== this.currentContainer ) {
			if ( !noPropagation ) {
				delayedTriggers.push( function( event ) {
					this._trigger( "remove", event, this._uiHash() );
				} );
				delayedTriggers.push( ( function( c ) {
					return function( event ) {
						c._trigger( "receive", event, this._uiHash( this ) );
					};
				} ).call( this, this.currentContainer ) );
				delayedTriggers.push( ( function( c ) {
					return function( event ) {
						c._trigger( "update", event, this._uiHash( this ) );
					};
				} ).call( this, this.currentContainer ) );
			}
		}

		//Post events to containers
		function delayEvent( type, instance, container ) {
			return function( event ) {
				container._trigger( type, event, instance._uiHash( instance ) );
			};
		}
		for ( i = this.containers.length - 1; i >= 0; i-- ) {
			if ( !noPropagation ) {
				delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
			}
			if ( this.containers[ i ].containerCache.over ) {
				delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
				this.containers[ i ].containerCache.over = 0;
			}
		}

		//Do what was originally in plugins
		if ( this.storedCursor ) {
			this.document.find( "body" ).css( "cursor", this.storedCursor );
			this.storedStylesheet.remove();
		}
		if ( this._storedOpacity ) {
			this.helper.css( "opacity", this._storedOpacity );
		}
		if ( this._storedZIndex ) {
			this.helper.css( "zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex );
		}

		this.dragging = false;

		if ( !noPropagation ) {
			this._trigger( "beforeStop", event, this._uiHash() );
		}

		//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
		// it unbinds ALL events from the original node!
		this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );

		if ( !this.cancelHelperRemoval ) {
			if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
				this.helper.remove();
			}
			this.helper = null;
		}

		if ( !noPropagation ) {
			for ( i = 0; i < delayedTriggers.length; i++ ) {

				// Trigger all delayed events
				delayedTriggers[ i ].call( this, event );
			}
			this._trigger( "stop", event, this._uiHash() );
		}

		this.fromOutside = false;
		return !this.cancelHelperRemoval;

	},

	_trigger: function() {
		if ( $.Widget.prototype._trigger.apply( this, arguments ) === false ) {
			this.cancel();
		}
	},

	_uiHash: function( _inst ) {
		var inst = _inst || this;
		return {
			helper: inst.helper,
			placeholder: inst.placeholder || $( [] ),
			position: inst.position,
			originalPosition: inst.originalPosition,
			offset: inst.positionAbs,
			item: inst.currentItem,
			sender: _inst ? _inst.element : null
		};
	}

} );


/*!
 * jQuery UI Spinner 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Spinner
//>>group: Widgets
//>>description: Displays buttons to easily input numbers via the keyboard or mouse.
//>>docs: http://api.jqueryui.com/spinner/
//>>demos: http://jqueryui.com/spinner/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/spinner.css
//>>css.theme: ../../themes/base/theme.css



function spinnerModifer( fn ) {
	return function() {
		var previous = this.element.val();
		fn.apply( this, arguments );
		this._refresh();
		if ( previous !== this.element.val() ) {
			this._trigger( "change" );
		}
	};
}

$.widget( "ui.spinner", {
	version: "1.12.1",
	defaultElement: "<input>",
	widgetEventPrefix: "spin",
	options: {
		classes: {
			"ui-spinner": "ui-corner-all",
			"ui-spinner-down": "ui-corner-br",
			"ui-spinner-up": "ui-corner-tr"
		},
		culture: null,
		icons: {
			down: "ui-icon-triangle-1-s",
			up: "ui-icon-triangle-1-n"
		},
		incremental: true,
		max: null,
		min: null,
		numberFormat: null,
		page: 10,
		step: 1,

		change: null,
		spin: null,
		start: null,
		stop: null
	},

	_create: function() {

		// handle string values that need to be parsed
		this._setOption( "max", this.options.max );
		this._setOption( "min", this.options.min );
		this._setOption( "step", this.options.step );

		// Only format if there is a value, prevents the field from being marked
		// as invalid in Firefox, see #9573.
		if ( this.value() !== "" ) {

			// Format the value, but don't constrain.
			this._value( this.element.val(), true );
		}

		this._draw();
		this._on( this._events );
		this._refresh();

		// Turning off autocomplete prevents the browser from remembering the
		// value when navigating through history, so we re-enable autocomplete
		// if the page is unloaded before the widget is destroyed. #7790
		this._on( this.window, {
			beforeunload: function() {
				this.element.removeAttr( "autocomplete" );
			}
		} );
	},

	_getCreateOptions: function() {
		var options = this._super();
		var element = this.element;

		$.each( [ "min", "max", "step" ], function( i, option ) {
			var value = element.attr( option );
			if ( value != null && value.length ) {
				options[ option ] = value;
			}
		} );

		return options;
	},

	_events: {
		keydown: function( event ) {
			if ( this._start( event ) && this._keydown( event ) ) {
				event.preventDefault();
			}
		},
		keyup: "_stop",
		focus: function() {
			this.previous = this.element.val();
		},
		blur: function( event ) {
			if ( this.cancelBlur ) {
				delete this.cancelBlur;
				return;
			}

			this._stop();
			this._refresh();
			if ( this.previous !== this.element.val() ) {
				this._trigger( "change", event );
			}
		},
		mousewheel: function( event, delta ) {
			if ( !delta ) {
				return;
			}
			if ( !this.spinning && !this._start( event ) ) {
				return false;
			}

			this._spin( ( delta > 0 ? 1 : -1 ) * this.options.step, event );
			clearTimeout( this.mousewheelTimer );
			this.mousewheelTimer = this._delay( function() {
				if ( this.spinning ) {
					this._stop( event );
				}
			}, 100 );
			event.preventDefault();
		},
		"mousedown .ui-spinner-button": function( event ) {
			var previous;

			// We never want the buttons to have focus; whenever the user is
			// interacting with the spinner, the focus should be on the input.
			// If the input is focused then this.previous is properly set from
			// when the input first received focus. If the input is not focused
			// then we need to set this.previous based on the value before spinning.
			previous = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] ) ?
				this.previous : this.element.val();
			function checkFocus() {
				var isActive = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] );
				if ( !isActive ) {
					this.element.trigger( "focus" );
					this.previous = previous;

					// support: IE
					// IE sets focus asynchronously, so we need to check if focus
					// moved off of the input because the user clicked on the button.
					this._delay( function() {
						this.previous = previous;
					} );
				}
			}

			// Ensure focus is on (or stays on) the text field
			event.preventDefault();
			checkFocus.call( this );

			// Support: IE
			// IE doesn't prevent moving focus even with event.preventDefault()
			// so we set a flag to know when we should ignore the blur event
			// and check (again) if focus moved off of the input.
			this.cancelBlur = true;
			this._delay( function() {
				delete this.cancelBlur;
				checkFocus.call( this );
			} );

			if ( this._start( event ) === false ) {
				return;
			}

			this._repeat( null, $( event.currentTarget )
				.hasClass( "ui-spinner-up" ) ? 1 : -1, event );
		},
		"mouseup .ui-spinner-button": "_stop",
		"mouseenter .ui-spinner-button": function( event ) {

			// button will add ui-state-active if mouse was down while mouseleave and kept down
			if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
				return;
			}

			if ( this._start( event ) === false ) {
				return false;
			}
			this._repeat( null, $( event.currentTarget )
				.hasClass( "ui-spinner-up" ) ? 1 : -1, event );
		},

		// TODO: do we really want to consider this a stop?
		// shouldn't we just stop the repeater and wait until mouseup before
		// we trigger the stop event?
		"mouseleave .ui-spinner-button": "_stop"
	},

	// Support mobile enhanced option and make backcompat more sane
	_enhance: function() {
		this.uiSpinner = this.element
			.attr( "autocomplete", "off" )
			.wrap( "<span>" )
			.parent()

				// Add buttons
				.append(
					"<a></a><a></a>"
				);
	},

	_draw: function() {
		this._enhance();

		this._addClass( this.uiSpinner, "ui-spinner", "ui-widget ui-widget-content" );
		this._addClass( "ui-spinner-input" );

		this.element.attr( "role", "spinbutton" );

		// Button bindings
		this.buttons = this.uiSpinner.children( "a" )
			.attr( "tabIndex", -1 )
			.attr( "aria-hidden", true )
			.button( {
				classes: {
					"ui-button": ""
				}
			} );

		// TODO: Right now button does not support classes this is already updated in button PR
		this._removeClass( this.buttons, "ui-corner-all" );

		this._addClass( this.buttons.first(), "ui-spinner-button ui-spinner-up" );
		this._addClass( this.buttons.last(), "ui-spinner-button ui-spinner-down" );
		this.buttons.first().button( {
			"icon": this.options.icons.up,
			"showLabel": false
		} );
		this.buttons.last().button( {
			"icon": this.options.icons.down,
			"showLabel": false
		} );

		// IE 6 doesn't understand height: 50% for the buttons
		// unless the wrapper has an explicit height
		if ( this.buttons.height() > Math.ceil( this.uiSpinner.height() * 0.5 ) &&
				this.uiSpinner.height() > 0 ) {
			this.uiSpinner.height( this.uiSpinner.height() );
		}
	},

	_keydown: function( event ) {
		var options = this.options,
			keyCode = $.ui.keyCode;

		switch ( event.keyCode ) {
		case keyCode.UP:
			this._repeat( null, 1, event );
			return true;
		case keyCode.DOWN:
			this._repeat( null, -1, event );
			return true;
		case keyCode.PAGE_UP:
			this._repeat( null, options.page, event );
			return true;
		case keyCode.PAGE_DOWN:
			this._repeat( null, -options.page, event );
			return true;
		}

		return false;
	},

	_start: function( event ) {
		if ( !this.spinning && this._trigger( "start", event ) === false ) {
			return false;
		}

		if ( !this.counter ) {
			this.counter = 1;
		}
		this.spinning = true;
		return true;
	},

	_repeat: function( i, steps, event ) {
		i = i || 500;

		clearTimeout( this.timer );
		this.timer = this._delay( function() {
			this._repeat( 40, steps, event );
		}, i );

		this._spin( steps * this.options.step, event );
	},

	_spin: function( step, event ) {
		var value = this.value() || 0;

		if ( !this.counter ) {
			this.counter = 1;
		}

		value = this._adjustValue( value + step * this._increment( this.counter ) );

		if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false ) {
			this._value( value );
			this.counter++;
		}
	},

	_increment: function( i ) {
		var incremental = this.options.incremental;

		if ( incremental ) {
			return $.isFunction( incremental ) ?
				incremental( i ) :
				Math.floor( i * i * i / 50000 - i * i / 500 + 17 * i / 200 + 1 );
		}

		return 1;
	},

	_precision: function() {
		var precision = this._precisionOf( this.options.step );
		if ( this.options.min !== null ) {
			precision = Math.max( precision, this._precisionOf( this.options.min ) );
		}
		return precision;
	},

	_precisionOf: function( num ) {
		var str = num.toString(),
			decimal = str.indexOf( "." );
		return decimal === -1 ? 0 : str.length - decimal - 1;
	},

	_adjustValue: function( value ) {
		var base, aboveMin,
			options = this.options;

		// Make sure we're at a valid step
		// - find out where we are relative to the base (min or 0)
		base = options.min !== null ? options.min : 0;
		aboveMin = value - base;

		// - round to the nearest step
		aboveMin = Math.round( aboveMin / options.step ) * options.step;

		// - rounding is based on 0, so adjust back to our base
		value = base + aboveMin;

		// Fix precision from bad JS floating point math
		value = parseFloat( value.toFixed( this._precision() ) );

		// Clamp the value
		if ( options.max !== null && value > options.max ) {
			return options.max;
		}
		if ( options.min !== null && value < options.min ) {
			return options.min;
		}

		return value;
	},

	_stop: function( event ) {
		if ( !this.spinning ) {
			return;
		}

		clearTimeout( this.timer );
		clearTimeout( this.mousewheelTimer );
		this.counter = 0;
		this.spinning = false;
		this._trigger( "stop", event );
	},

	_setOption: function( key, value ) {
		var prevValue, first, last;

		if ( key === "culture" || key === "numberFormat" ) {
			prevValue = this._parse( this.element.val() );
			this.options[ key ] = value;
			this.element.val( this._format( prevValue ) );
			return;
		}

		if ( key === "max" || key === "min" || key === "step" ) {
			if ( typeof value === "string" ) {
				value = this._parse( value );
			}
		}
		if ( key === "icons" ) {
			first = this.buttons.first().find( ".ui-icon" );
			this._removeClass( first, null, this.options.icons.up );
			this._addClass( first, null, value.up );
			last = this.buttons.last().find( ".ui-icon" );
			this._removeClass( last, null, this.options.icons.down );
			this._addClass( last, null, value.down );
		}

		this._super( key, value );
	},

	_setOptionDisabled: function( value ) {
		this._super( value );

		this._toggleClass( this.uiSpinner, null, "ui-state-disabled", !!value );
		this.element.prop( "disabled", !!value );
		this.buttons.button( value ? "disable" : "enable" );
	},

	_setOptions: spinnerModifer( function( options ) {
		this._super( options );
	} ),

	_parse: function( val ) {
		if ( typeof val === "string" && val !== "" ) {
			val = window.Globalize && this.options.numberFormat ?
				Globalize.parseFloat( val, 10, this.options.culture ) : +val;
		}
		return val === "" || isNaN( val ) ? null : val;
	},

	_format: function( value ) {
		if ( value === "" ) {
			return "";
		}
		return window.Globalize && this.options.numberFormat ?
			Globalize.format( value, this.options.numberFormat, this.options.culture ) :
			value;
	},

	_refresh: function() {
		this.element.attr( {
			"aria-valuemin": this.options.min,
			"aria-valuemax": this.options.max,

			// TODO: what should we do with values that can't be parsed?
			"aria-valuenow": this._parse( this.element.val() )
		} );
	},

	isValid: function() {
		var value = this.value();

		// Null is invalid
		if ( value === null ) {
			return false;
		}

		// If value gets adjusted, it's invalid
		return value === this._adjustValue( value );
	},

	// Update the value without triggering change
	_value: function( value, allowAny ) {
		var parsed;
		if ( value !== "" ) {
			parsed = this._parse( value );
			if ( parsed !== null ) {
				if ( !allowAny ) {
					parsed = this._adjustValue( parsed );
				}
				value = this._format( parsed );
			}
		}
		this.element.val( value );
		this._refresh();
	},

	_destroy: function() {
		this.element
			.prop( "disabled", false )
			.removeAttr( "autocomplete role aria-valuemin aria-valuemax aria-valuenow" );

		this.uiSpinner.replaceWith( this.element );
	},

	stepUp: spinnerModifer( function( steps ) {
		this._stepUp( steps );
	} ),
	_stepUp: function( steps ) {
		if ( this._start() ) {
			this._spin( ( steps || 1 ) * this.options.step );
			this._stop();
		}
	},

	stepDown: spinnerModifer( function( steps ) {
		this._stepDown( steps );
	} ),
	_stepDown: function( steps ) {
		if ( this._start() ) {
			this._spin( ( steps || 1 ) * -this.options.step );
			this._stop();
		}
	},

	pageUp: spinnerModifer( function( pages ) {
		this._stepUp( ( pages || 1 ) * this.options.page );
	} ),

	pageDown: spinnerModifer( function( pages ) {
		this._stepDown( ( pages || 1 ) * this.options.page );
	} ),

	value: function( newVal ) {
		if ( !arguments.length ) {
			return this._parse( this.element.val() );
		}
		spinnerModifer( this._value ).call( this, newVal );
	},

	widget: function() {
		return this.uiSpinner;
	}
} );

// DEPRECATED
// TODO: switch return back to widget declaration at top of file when this is removed
if ( $.uiBackCompat !== false ) {

	// Backcompat for spinner html extension points
	$.widget( "ui.spinner", $.ui.spinner, {
		_enhance: function() {
			this.uiSpinner = this.element
				.attr( "autocomplete", "off" )
				.wrap( this._uiSpinnerHtml() )
				.parent()

					// Add buttons
					.append( this._buttonHtml() );
		},
		_uiSpinnerHtml: function() {
			return "<span>";
		},

		_buttonHtml: function() {
			return "<a></a><a></a>";
		}
	} );
}

var widgetsSpinner = $.ui.spinner;


/*!
 * jQuery UI Tabs 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Tabs
//>>group: Widgets
//>>description: Transforms a set of container elements into a tab structure.
//>>docs: http://api.jqueryui.com/tabs/
//>>demos: http://jqueryui.com/tabs/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/tabs.css
//>>css.theme: ../../themes/base/theme.css



$.widget( "ui.tabs", {
	version: "1.12.1",
	delay: 300,
	options: {
		active: null,
		classes: {
			"ui-tabs": "ui-corner-all",
			"ui-tabs-nav": "ui-corner-all",
			"ui-tabs-panel": "ui-corner-bottom",
			"ui-tabs-tab": "ui-corner-top"
		},
		collapsible: false,
		event: "click",
		heightStyle: "content",
		hide: null,
		show: null,

		// Callbacks
		activate: null,
		beforeActivate: null,
		beforeLoad: null,
		load: null
	},

	_isLocal: ( function() {
		var rhash = /#.*$/;

		return function( anchor ) {
			var anchorUrl, locationUrl;

			anchorUrl = anchor.href.replace( rhash, "" );
			locationUrl = location.href.replace( rhash, "" );

			// Decoding may throw an error if the URL isn't UTF-8 (#9518)
			try {
				anchorUrl = decodeURIComponent( anchorUrl );
			} catch ( error ) {}
			try {
				locationUrl = decodeURIComponent( locationUrl );
			} catch ( error ) {}

			return anchor.hash.length > 1 && anchorUrl === locationUrl;
		};
	} )(),

	_create: function() {
		var that = this,
			options = this.options;

		this.running = false;

		this._addClass( "ui-tabs", "ui-widget ui-widget-content" );
		this._toggleClass( "ui-tabs-collapsible", null, options.collapsible );

		this._processTabs();
		options.active = this._initialActive();

		// Take disabling tabs via class attribute from HTML
		// into account and update option properly.
		if ( $.isArray( options.disabled ) ) {
			options.disabled = $.unique( options.disabled.concat(
				$.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
					return that.tabs.index( li );
				} )
			) ).sort();
		}

		// Check for length avoids error when initializing empty list
		if ( this.options.active !== false && this.anchors.length ) {
			this.active = this._findActive( options.active );
		} else {
			this.active = $();
		}

		this._refresh();

		if ( this.active.length ) {
			this.load( options.active );
		}
	},

	_initialActive: function() {
		var active = this.options.active,
			collapsible = this.options.collapsible,
			locationHash = location.hash.substring( 1 );

		if ( active === null ) {

			// check the fragment identifier in the URL
			if ( locationHash ) {
				this.tabs.each( function( i, tab ) {
					if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
						active = i;
						return false;
					}
				} );
			}

			// Check for a tab marked active via a class
			if ( active === null ) {
				active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
			}

			// No active tab, set to false
			if ( active === null || active === -1 ) {
				active = this.tabs.length ? 0 : false;
			}
		}

		// Handle numbers: negative, out of range
		if ( active !== false ) {
			active = this.tabs.index( this.tabs.eq( active ) );
			if ( active === -1 ) {
				active = collapsible ? false : 0;
			}
		}

		// Don't allow collapsible: false and active: false
		if ( !collapsible && active === false && this.anchors.length ) {
			active = 0;
		}

		return active;
	},

	_getCreateEventData: function() {
		return {
			tab: this.active,
			panel: !this.active.length ? $() : this._getPanelForTab( this.active )
		};
	},

	_tabKeydown: function( event ) {
		var focusedTab = $( $.ui.safeActiveElement( this.document[ 0 ] ) ).closest( "li" ),
			selectedIndex = this.tabs.index( focusedTab ),
			goingForward = true;

		if ( this._handlePageNav( event ) ) {
			return;
		}

		switch ( event.keyCode ) {
		case $.ui.keyCode.RIGHT:
		case $.ui.keyCode.DOWN:
			selectedIndex++;
			break;
		case $.ui.keyCode.UP:
		case $.ui.keyCode.LEFT:
			goingForward = false;
			selectedIndex--;
			break;
		case $.ui.keyCode.END:
			selectedIndex = this.anchors.length - 1;
			break;
		case $.ui.keyCode.HOME:
			selectedIndex = 0;
			break;
		case $.ui.keyCode.SPACE:

			// Activate only, no collapsing
			event.preventDefault();
			clearTimeout( this.activating );
			this._activate( selectedIndex );
			return;
		case $.ui.keyCode.ENTER:

			// Toggle (cancel delayed activation, allow collapsing)
			event.preventDefault();
			clearTimeout( this.activating );

			// Determine if we should collapse or activate
			this._activate( selectedIndex === this.options.active ? false : selectedIndex );
			return;
		default:
			return;
		}

		// Focus the appropriate tab, based on which key was pressed
		event.preventDefault();
		clearTimeout( this.activating );
		selectedIndex = this._focusNextTab( selectedIndex, goingForward );

		// Navigating with control/command key will prevent automatic activation
		if ( !event.ctrlKey && !event.metaKey ) {

			// Update aria-selected immediately so that AT think the tab is already selected.
			// Otherwise AT may confuse the user by stating that they need to activate the tab,
			// but the tab will already be activated by the time the announcement finishes.
			focusedTab.attr( "aria-selected", "false" );
			this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );

			this.activating = this._delay( function() {
				this.option( "active", selectedIndex );
			}, this.delay );
		}
	},

	_panelKeydown: function( event ) {
		if ( this._handlePageNav( event ) ) {
			return;
		}

		// Ctrl+up moves focus to the current tab
		if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
			event.preventDefault();
			this.active.trigger( "focus" );
		}
	},

	// Alt+page up/down moves focus to the previous/next tab (and activates)
	_handlePageNav: function( event ) {
		if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
			this._activate( this._focusNextTab( this.options.active - 1, false ) );
			return true;
		}
		if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
			this._activate( this._focusNextTab( this.options.active + 1, true ) );
			return true;
		}
	},

	_findNextTab: function( index, goingForward ) {
		var lastTabIndex = this.tabs.length - 1;

		function constrain() {
			if ( index > lastTabIndex ) {
				index = 0;
			}
			if ( index < 0 ) {
				index = lastTabIndex;
			}
			return index;
		}

		while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
			index = goingForward ? index + 1 : index - 1;
		}

		return index;
	},

	_focusNextTab: function( index, goingForward ) {
		index = this._findNextTab( index, goingForward );
		this.tabs.eq( index ).trigger( "focus" );
		return index;
	},

	_setOption: function( key, value ) {
		if ( key === "active" ) {

			// _activate() will handle invalid values and update this.options
			this._activate( value );
			return;
		}

		this._super( key, value );

		if ( key === "collapsible" ) {
			this._toggleClass( "ui-tabs-collapsible", null, value );

			// Setting collapsible: false while collapsed; open first panel
			if ( !value && this.options.active === false ) {
				this._activate( 0 );
			}
		}

		if ( key === "event" ) {
			this._setupEvents( value );
		}

		if ( key === "heightStyle" ) {
			this._setupHeightStyle( value );
		}
	},

	_sanitizeSelector: function( hash ) {
		return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
	},

	refresh: function() {
		var options = this.options,
			lis = this.tablist.children( ":has(a[href])" );

		// Get disabled tabs from class attribute from HTML
		// this will get converted to a boolean if needed in _refresh()
		options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
			return lis.index( tab );
		} );

		this._processTabs();

		// Was collapsed or no tabs
		if ( options.active === false || !this.anchors.length ) {
			options.active = false;
			this.active = $();

		// was active, but active tab is gone
		} else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {

			// all remaining tabs are disabled
			if ( this.tabs.length === options.disabled.length ) {
				options.active = false;
				this.active = $();

			// activate previous tab
			} else {
				this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
			}

		// was active, active tab still exists
		} else {

			// make sure active index is correct
			options.active = this.tabs.index( this.active );
		}

		this._refresh();
	},

	_refresh: function() {
		this._setOptionDisabled( this.options.disabled );
		this._setupEvents( this.options.event );
		this._setupHeightStyle( this.options.heightStyle );

		this.tabs.not( this.active ).attr( {
			"aria-selected": "false",
			"aria-expanded": "false",
			tabIndex: -1
		} );
		this.panels.not( this._getPanelForTab( this.active ) )
			.hide()
			.attr( {
				"aria-hidden": "true"
			} );

		// Make sure one tab is in the tab order
		if ( !this.active.length ) {
			this.tabs.eq( 0 ).attr( "tabIndex", 0 );
		} else {
			this.active
				.attr( {
					"aria-selected": "true",
					"aria-expanded": "true",
					tabIndex: 0
				} );
			this._addClass( this.active, "ui-tabs-active", "ui-state-active" );
			this._getPanelForTab( this.active )
				.show()
				.attr( {
					"aria-hidden": "false"
				} );
		}
	},

	_processTabs: function() {
		var that = this,
			prevTabs = this.tabs,
			prevAnchors = this.anchors,
			prevPanels = this.panels;

		this.tablist = this._getList().attr( "role", "tablist" );
		this._addClass( this.tablist, "ui-tabs-nav",
			"ui-helper-reset ui-helper-clearfix ui-widget-header" );

		// Prevent users from focusing disabled tabs via click
		this.tablist
			.on( "mousedown" + this.eventNamespace, "> li", function( event ) {
				if ( $( this ).is( ".ui-state-disabled" ) ) {
					event.preventDefault();
				}
			} )

			// Support: IE <9
			// Preventing the default action in mousedown doesn't prevent IE
			// from focusing the element, so if the anchor gets focused, blur.
			// We don't have to worry about focusing the previously focused
			// element since clicking on a non-focusable element should focus
			// the body anyway.
			.on( "focus" + this.eventNamespace, ".ui-tabs-anchor", function() {
				if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
					this.blur();
				}
			} );

		this.tabs = this.tablist.find( "> li:has(a[href])" )
			.attr( {
				role: "tab",
				tabIndex: -1
			} );
		this._addClass( this.tabs, "ui-tabs-tab", "ui-state-default" );

		this.anchors = this.tabs.map( function() {
			return $( "a", this )[ 0 ];
		} )
			.attr( {
				role: "presentation",
				tabIndex: -1
			} );
		this._addClass( this.anchors, "ui-tabs-anchor" );

		this.panels = $();

		this.anchors.each( function( i, anchor ) {
			var selector, panel, panelId,
				anchorId = $( anchor ).uniqueId().attr( "id" ),
				tab = $( anchor ).closest( "li" ),
				originalAriaControls = tab.attr( "aria-controls" );

			// Inline tab
			if ( that._isLocal( anchor ) ) {
				selector = anchor.hash;
				panelId = selector.substring( 1 );
				panel = that.element.find( that._sanitizeSelector( selector ) );

			// remote tab
			} else {

				// If the tab doesn't already have aria-controls,
				// generate an id by using a throw-away element
				panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
				selector = "#" + panelId;
				panel = that.element.find( selector );
				if ( !panel.length ) {
					panel = that._createPanel( panelId );
					panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
				}
				panel.attr( "aria-live", "polite" );
			}

			if ( panel.length ) {
				that.panels = that.panels.add( panel );
			}
			if ( originalAriaControls ) {
				tab.data( "ui-tabs-aria-controls", originalAriaControls );
			}
			tab.attr( {
				"aria-controls": panelId,
				"aria-labelledby": anchorId
			} );
			panel.attr( "aria-labelledby", anchorId );
		} );

		this.panels.attr( "role", "tabpanel" );
		this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );

		// Avoid memory leaks (#10056)
		if ( prevTabs ) {
			this._off( prevTabs.not( this.tabs ) );
			this._off( prevAnchors.not( this.anchors ) );
			this._off( prevPanels.not( this.panels ) );
		}
	},

	// Allow overriding how to find the list for rare usage scenarios (#7715)
	_getList: function() {
		return this.tablist || this.element.find( "ol, ul" ).eq( 0 );
	},

	_createPanel: function( id ) {
		return $( "<div>" )
			.attr( "id", id )
			.data( "ui-tabs-destroy", true );
	},

	_setOptionDisabled: function( disabled ) {
		var currentItem, li, i;

		if ( $.isArray( disabled ) ) {
			if ( !disabled.length ) {
				disabled = false;
			} else if ( disabled.length === this.anchors.length ) {
				disabled = true;
			}
		}

		// Disable tabs
		for ( i = 0; ( li = this.tabs[ i ] ); i++ ) {
			currentItem = $( li );
			if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
				currentItem.attr( "aria-disabled", "true" );
				this._addClass( currentItem, null, "ui-state-disabled" );
			} else {
				currentItem.removeAttr( "aria-disabled" );
				this._removeClass( currentItem, null, "ui-state-disabled" );
			}
		}

		this.options.disabled = disabled;

		this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null,
			disabled === true );
	},

	_setupEvents: function( event ) {
		var events = {};
		if ( event ) {
			$.each( event.split( " " ), function( index, eventName ) {
				events[ eventName ] = "_eventHandler";
			} );
		}

		this._off( this.anchors.add( this.tabs ).add( this.panels ) );

		// Always prevent the default action, even when disabled
		this._on( true, this.anchors, {
			click: function( event ) {
				event.preventDefault();
			}
		} );
		this._on( this.anchors, events );
		this._on( this.tabs, { keydown: "_tabKeydown" } );
		this._on( this.panels, { keydown: "_panelKeydown" } );

		this._focusable( this.tabs );
		this._hoverable( this.tabs );
	},

	_setupHeightStyle: function( heightStyle ) {
		var maxHeight,
			parent = this.element.parent();

		if ( heightStyle === "fill" ) {
			maxHeight = parent.height();
			maxHeight -= this.element.outerHeight() - this.element.height();

			this.element.siblings( ":visible" ).each( function() {
				var elem = $( this ),
					position = elem.css( "position" );

				if ( position === "absolute" || position === "fixed" ) {
					return;
				}
				maxHeight -= elem.outerHeight( true );
			} );

			this.element.children().not( this.panels ).each( function() {
				maxHeight -= $( this ).outerHeight( true );
			} );

			this.panels.each( function() {
				$( this ).height( Math.max( 0, maxHeight -
					$( this ).innerHeight() + $( this ).height() ) );
			} )
				.css( "overflow", "auto" );
		} else if ( heightStyle === "auto" ) {
			maxHeight = 0;
			this.panels.each( function() {
				maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
			} ).height( maxHeight );
		}
	},

	_eventHandler: function( event ) {
		var options = this.options,
			active = this.active,
			anchor = $( event.currentTarget ),
			tab = anchor.closest( "li" ),
			clickedIsActive = tab[ 0 ] === active[ 0 ],
			collapsing = clickedIsActive && options.collapsible,
			toShow = collapsing ? $() : this._getPanelForTab( tab ),
			toHide = !active.length ? $() : this._getPanelForTab( active ),
			eventData = {
				oldTab: active,
				oldPanel: toHide,
				newTab: collapsing ? $() : tab,
				newPanel: toShow
			};

		event.preventDefault();

		if ( tab.hasClass( "ui-state-disabled" ) ||

				// tab is already loading
				tab.hasClass( "ui-tabs-loading" ) ||

				// can't switch durning an animation
				this.running ||

				// click on active header, but not collapsible
				( clickedIsActive && !options.collapsible ) ||

				// allow canceling activation
				( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
			return;
		}

		options.active = collapsing ? false : this.tabs.index( tab );

		this.active = clickedIsActive ? $() : tab;
		if ( this.xhr ) {
			this.xhr.abort();
		}

		if ( !toHide.length && !toShow.length ) {
			$.error( "jQuery UI Tabs: Mismatching fragment identifier." );
		}

		if ( toShow.length ) {
			this.load( this.tabs.index( tab ), event );
		}
		this._toggle( event, eventData );
	},

	// Handles show/hide for selecting tabs
	_toggle: function( event, eventData ) {
		var that = this,
			toShow = eventData.newPanel,
			toHide = eventData.oldPanel;

		this.running = true;

		function complete() {
			that.running = false;
			that._trigger( "activate", event, eventData );
		}

		function show() {
			that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" );

			if ( toShow.length && that.options.show ) {
				that._show( toShow, that.options.show, complete );
			} else {
				toShow.show();
				complete();
			}
		}

		// Start out by hiding, then showing, then completing
		if ( toHide.length && this.options.hide ) {
			this._hide( toHide, this.options.hide, function() {
				that._removeClass( eventData.oldTab.closest( "li" ),
					"ui-tabs-active", "ui-state-active" );
				show();
			} );
		} else {
			this._removeClass( eventData.oldTab.closest( "li" ),
				"ui-tabs-active", "ui-state-active" );
			toHide.hide();
			show();
		}

		toHide.attr( "aria-hidden", "true" );
		eventData.oldTab.attr( {
			"aria-selected": "false",
			"aria-expanded": "false"
		} );

		// If we're switching tabs, remove the old tab from the tab order.
		// If we're opening from collapsed state, remove the previous tab from the tab order.
		// If we're collapsing, then keep the collapsing tab in the tab order.
		if ( toShow.length && toHide.length ) {
			eventData.oldTab.attr( "tabIndex", -1 );
		} else if ( toShow.length ) {
			this.tabs.filter( function() {
				return $( this ).attr( "tabIndex" ) === 0;
			} )
				.attr( "tabIndex", -1 );
		}

		toShow.attr( "aria-hidden", "false" );
		eventData.newTab.attr( {
			"aria-selected": "true",
			"aria-expanded": "true",
			tabIndex: 0
		} );
	},

	_activate: function( index ) {
		var anchor,
			active = this._findActive( index );

		// Trying to activate the already active panel
		if ( active[ 0 ] === this.active[ 0 ] ) {
			return;
		}

		// Trying to collapse, simulate a click on the current active header
		if ( !active.length ) {
			active = this.active;
		}

		anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
		this._eventHandler( {
			target: anchor,
			currentTarget: anchor,
			preventDefault: $.noop
		} );
	},

	_findActive: function( index ) {
		return index === false ? $() : this.tabs.eq( index );
	},

	_getIndex: function( index ) {

		// meta-function to give users option to provide a href string instead of a numerical index.
		if ( typeof index === "string" ) {
			index = this.anchors.index( this.anchors.filter( "[href$='" +
				$.ui.escapeSelector( index ) + "']" ) );
		}

		return index;
	},

	_destroy: function() {
		if ( this.xhr ) {
			this.xhr.abort();
		}

		this.tablist
			.removeAttr( "role" )
			.off( this.eventNamespace );

		this.anchors
			.removeAttr( "role tabIndex" )
			.removeUniqueId();

		this.tabs.add( this.panels ).each( function() {
			if ( $.data( this, "ui-tabs-destroy" ) ) {
				$( this ).remove();
			} else {
				$( this ).removeAttr( "role tabIndex " +
					"aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded" );
			}
		} );

		this.tabs.each( function() {
			var li = $( this ),
				prev = li.data( "ui-tabs-aria-controls" );
			if ( prev ) {
				li
					.attr( "aria-controls", prev )
					.removeData( "ui-tabs-aria-controls" );
			} else {
				li.removeAttr( "aria-controls" );
			}
		} );

		this.panels.show();

		if ( this.options.heightStyle !== "content" ) {
			this.panels.css( "height", "" );
		}
	},

	enable: function( index ) {
		var disabled = this.options.disabled;
		if ( disabled === false ) {
			return;
		}

		if ( index === undefined ) {
			disabled = false;
		} else {
			index = this._getIndex( index );
			if ( $.isArray( disabled ) ) {
				disabled = $.map( disabled, function( num ) {
					return num !== index ? num : null;
				} );
			} else {
				disabled = $.map( this.tabs, function( li, num ) {
					return num !== index ? num : null;
				} );
			}
		}
		this._setOptionDisabled( disabled );
	},

	disable: function( index ) {
		var disabled = this.options.disabled;
		if ( disabled === true ) {
			return;
		}

		if ( index === undefined ) {
			disabled = true;
		} else {
			index = this._getIndex( index );
			if ( $.inArray( index, disabled ) !== -1 ) {
				return;
			}
			if ( $.isArray( disabled ) ) {
				disabled = $.merge( [ index ], disabled ).sort();
			} else {
				disabled = [ index ];
			}
		}
		this._setOptionDisabled( disabled );
	},

	load: function( index, event ) {
		index = this._getIndex( index );
		var that = this,
			tab = this.tabs.eq( index ),
			anchor = tab.find( ".ui-tabs-anchor" ),
			panel = this._getPanelForTab( tab ),
			eventData = {
				tab: tab,
				panel: panel
			},
			complete = function( jqXHR, status ) {
				if ( status === "abort" ) {
					that.panels.stop( false, true );
				}

				that._removeClass( tab, "ui-tabs-loading" );
				panel.removeAttr( "aria-busy" );

				if ( jqXHR === that.xhr ) {
					delete that.xhr;
				}
			};

		// Not remote
		if ( this._isLocal( anchor[ 0 ] ) ) {
			return;
		}

		this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );

		// Support: jQuery <1.8
		// jQuery <1.8 returns false if the request is canceled in beforeSend,
		// but as of 1.8, $.ajax() always returns a jqXHR object.
		if ( this.xhr && this.xhr.statusText !== "canceled" ) {
			this._addClass( tab, "ui-tabs-loading" );
			panel.attr( "aria-busy", "true" );

			this.xhr
				.done( function( response, status, jqXHR ) {

					// support: jQuery <1.8
					// http://bugs.jquery.com/ticket/11778
					setTimeout( function() {
						panel.html( response );
						that._trigger( "load", event, eventData );

						complete( jqXHR, status );
					}, 1 );
				} )
				.fail( function( jqXHR, status ) {

					// support: jQuery <1.8
					// http://bugs.jquery.com/ticket/11778
					setTimeout( function() {
						complete( jqXHR, status );
					}, 1 );
				} );
		}
	},

	_ajaxSettings: function( anchor, event, eventData ) {
		var that = this;
		return {

			// Support: IE <11 only
			// Strip any hash that exists to prevent errors with the Ajax request
			url: anchor.attr( "href" ).replace( /#.*$/, "" ),
			beforeSend: function( jqXHR, settings ) {
				return that._trigger( "beforeLoad", event,
					$.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) );
			}
		};
	},

	_getPanelForTab: function( tab ) {
		var id = $( tab ).attr( "aria-controls" );
		return this.element.find( this._sanitizeSelector( "#" + id ) );
	}
} );

// DEPRECATED
// TODO: Switch return back to widget declaration at top of file when this is removed
if ( $.uiBackCompat !== false ) {

	// Backcompat for ui-tab class (now ui-tabs-tab)
	$.widget( "ui.tabs", $.ui.tabs, {
		_processTabs: function() {
			this._superApply( arguments );
			this._addClass( this.tabs, "ui-tab" );
		}
	} );
}

var widgetsTabs = $.ui.tabs;


/*!
 * jQuery UI Tooltip 1.12.1
 * http://jqueryui.com
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

//>>label: Tooltip
//>>group: Widgets
//>>description: Shows additional information for any element on hover or focus.
//>>docs: http://api.jqueryui.com/tooltip/
//>>demos: http://jqueryui.com/tooltip/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/tooltip.css
//>>css.theme: ../../themes/base/theme.css



$.widget( "ui.tooltip", {
	version: "1.12.1",
	options: {
		classes: {
			"ui-tooltip": "ui-corner-all ui-widget-shadow"
		},
		content: function() {

			// support: IE<9, Opera in jQuery <1.7
			// .text() can't accept undefined, so coerce to a string
			var title = $( this ).attr( "title" ) || "";

			// Escape title, since we're going from an attribute to raw HTML
			return $( "<a>" ).text( title ).html();
		},
		hide: true,

		// Disabled elements have inconsistent behavior across browsers (#8661)
		items: "[title]:not([disabled])",
		position: {
			my: "left top+15",
			at: "left bottom",
			collision: "flipfit flip"
		},
		show: true,
		track: false,

		// Callbacks
		close: null,
		open: null
	},

	_addDescribedBy: function( elem, id ) {
		var describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ );
		describedby.push( id );
		elem
			.data( "ui-tooltip-id", id )
			.attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
	},

	_removeDescribedBy: function( elem ) {
		var id = elem.data( "ui-tooltip-id" ),
			describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ ),
			index = $.inArray( id, describedby );

		if ( index !== -1 ) {
			describedby.splice( index, 1 );
		}

		elem.removeData( "ui-tooltip-id" );
		describedby = $.trim( describedby.join( " " ) );
		if ( describedby ) {
			elem.attr( "aria-describedby", describedby );
		} else {
			elem.removeAttr( "aria-describedby" );
		}
	},

	_create: function() {
		this._on( {
			mouseover: "open",
			focusin: "open"
		} );

		// IDs of generated tooltips, needed for destroy
		this.tooltips = {};

		// IDs of parent tooltips where we removed the title attribute
		this.parents = {};

		// Append the aria-live region so tooltips announce correctly
		this.liveRegion = $( "<div>" )
			.attr( {
				role: "log",
				"aria-live": "assertive",
				"aria-relevant": "additions"
			} )
			.appendTo( this.document[ 0 ].body );
		this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );

		this.disabledTitles = $( [] );
	},

	_setOption: function( key, value ) {
		var that = this;

		this._super( key, value );

		if ( key === "content" ) {
			$.each( this.tooltips, function( id, tooltipData ) {
				that._updateContent( tooltipData.element );
			} );
		}
	},

	_setOptionDisabled: function( value ) {
		this[ value ? "_disable" : "_enable" ]();
	},

	_disable: function() {
		var that = this;

		// Close open tooltips
		$.each( this.tooltips, function( id, tooltipData ) {
			var event = $.Event( "blur" );
			event.target = event.currentTarget = tooltipData.element[ 0 ];
			that.close( event, true );
		} );

		// Remove title attributes to prevent native tooltips
		this.disabledTitles = this.disabledTitles.add(
			this.element.find( this.options.items ).addBack()
				.filter( function() {
					var element = $( this );
					if ( element.is( "[title]" ) ) {
						return element
							.data( "ui-tooltip-title", element.attr( "title" ) )
							.removeAttr( "title" );
					}
				} )
		);
	},

	_enable: function() {

		// restore title attributes
		this.disabledTitles.each( function() {
			var element = $( this );
			if ( element.data( "ui-tooltip-title" ) ) {
				element.attr( "title", element.data( "ui-tooltip-title" ) );
			}
		} );
		this.disabledTitles = $( [] );
	},

	open: function( event ) {
		var that = this,
			target = $( event ? event.target : this.element )

				// we need closest here due to mouseover bubbling,
				// but always pointing at the same event target
				.closest( this.options.items );

		// No element to show a tooltip for or the tooltip is already open
		if ( !target.length || target.data( "ui-tooltip-id" ) ) {
			return;
		}

		if ( target.attr( "title" ) ) {
			target.data( "ui-tooltip-title", target.attr( "title" ) );
		}

		target.data( "ui-tooltip-open", true );

		// Kill parent tooltips, custom or native, for hover
		if ( event && event.type === "mouseover" ) {
			target.parents().each( function() {
				var parent = $( this ),
					blurEvent;
				if ( parent.data( "ui-tooltip-open" ) ) {
					blurEvent = $.Event( "blur" );
					blurEvent.target = blurEvent.currentTarget = this;
					that.close( blurEvent, true );
				}
				if ( parent.attr( "title" ) ) {
					parent.uniqueId();
					that.parents[ this.id ] = {
						element: this,
						title: parent.attr( "title" )
					};
					parent.attr( "title", "" );
				}
			} );
		}

		this._registerCloseHandlers( event, target );
		this._updateContent( target, event );
	},

	_updateContent: function( target, event ) {
		var content,
			contentOption = this.options.content,
			that = this,
			eventType = event ? event.type : null;

		if ( typeof contentOption === "string" || contentOption.nodeType ||
				contentOption.jquery ) {
			return this._open( event, target, contentOption );
		}

		content = contentOption.call( target[ 0 ], function( response ) {

			// IE may instantly serve a cached response for ajax requests
			// delay this call to _open so the other call to _open runs first
			that._delay( function() {

				// Ignore async response if tooltip was closed already
				if ( !target.data( "ui-tooltip-open" ) ) {
					return;
				}

				// JQuery creates a special event for focusin when it doesn't
				// exist natively. To improve performance, the native event
				// object is reused and the type is changed. Therefore, we can't
				// rely on the type being correct after the event finished
				// bubbling, so we set it back to the previous value. (#8740)
				if ( event ) {
					event.type = eventType;
				}
				this._open( event, target, response );
			} );
		} );
		if ( content ) {
			this._open( event, target, content );
		}
	},

	_open: function( event, target, content ) {
		var tooltipData, tooltip, delayedShow, a11yContent,
			positionOption = $.extend( {}, this.options.position );

		if ( !content ) {
			return;
		}

		// Content can be updated multiple times. If the tooltip already
		// exists, then just update the content and bail.
		tooltipData = this._find( target );
		if ( tooltipData ) {
			tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content );
			return;
		}

		// If we have a title, clear it to prevent the native tooltip
		// we have to check first to avoid defining a title if none exists
		// (we don't want to cause an element to start matching [title])
		//
		// We use removeAttr only for key events, to allow IE to export the correct
		// accessible attributes. For mouse events, set to empty string to avoid
		// native tooltip showing up (happens only when removing inside mouseover).
		if ( target.is( "[title]" ) ) {
			if ( event && event.type === "mouseover" ) {
				target.attr( "title", "" );
			} else {
				target.removeAttr( "title" );
			}
		}

		tooltipData = this._tooltip( target );
		tooltip = tooltipData.tooltip;
		this._addDescribedBy( target, tooltip.attr( "id" ) );
		tooltip.find( ".ui-tooltip-content" ).html( content );

		// Support: Voiceover on OS X, JAWS on IE <= 9
		// JAWS announces deletions even when aria-relevant="additions"
		// Voiceover will sometimes re-read the entire log region's contents from the beginning
		this.liveRegion.children().hide();
		a11yContent = $( "<div>" ).html( tooltip.find( ".ui-tooltip-content" ).html() );
		a11yContent.removeAttr( "name" ).find( "[name]" ).removeAttr( "name" );
		a11yContent.removeAttr( "id" ).find( "[id]" ).removeAttr( "id" );
		a11yContent.appendTo( this.liveRegion );

		function position( event ) {
			positionOption.of = event;
			if ( tooltip.is( ":hidden" ) ) {
				return;
			}
			tooltip.position( positionOption );
		}
		if ( this.options.track && event && /^mouse/.test( event.type ) ) {
			this._on( this.document, {
				mousemove: position
			} );

			// trigger once to override element-relative positioning
			position( event );
		} else {
			tooltip.position( $.extend( {
				of: target
			}, this.options.position ) );
		}

		tooltip.hide();

		this._show( tooltip, this.options.show );

		// Handle tracking tooltips that are shown with a delay (#8644). As soon
		// as the tooltip is visible, position the tooltip using the most recent
		// event.
		// Adds the check to add the timers only when both delay and track options are set (#14682)
		if ( this.options.track && this.options.show && this.options.show.delay ) {
			delayedShow = this.delayedShow = setInterval( function() {
				if ( tooltip.is( ":visible" ) ) {
					position( positionOption.of );
					clearInterval( delayedShow );
				}
			}, $.fx.interval );
		}

		this._trigger( "open", event, { tooltip: tooltip } );
	},

	_registerCloseHandlers: function( event, target ) {
		var events = {
			keyup: function( event ) {
				if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
					var fakeEvent = $.Event( event );
					fakeEvent.currentTarget = target[ 0 ];
					this.close( fakeEvent, true );
				}
			}
		};

		// Only bind remove handler for delegated targets. Non-delegated
		// tooltips will handle this in destroy.
		if ( target[ 0 ] !== this.element[ 0 ] ) {
			events.remove = function() {
				this._removeTooltip( this._find( target ).tooltip );
			};
		}

		if ( !event || event.type === "mouseover" ) {
			events.mouseleave = "close";
		}
		if ( !event || event.type === "focusin" ) {
			events.focusout = "close";
		}
		this._on( true, target, events );
	},

	close: function( event ) {
		var tooltip,
			that = this,
			target = $( event ? event.currentTarget : this.element ),
			tooltipData = this._find( target );

		// The tooltip may already be closed
		if ( !tooltipData ) {

			// We set ui-tooltip-open immediately upon open (in open()), but only set the
			// additional data once there's actually content to show (in _open()). So even if the
			// tooltip doesn't have full data, we always remove ui-tooltip-open in case we're in
			// the period between open() and _open().
			target.removeData( "ui-tooltip-open" );
			return;
		}

		tooltip = tooltipData.tooltip;

		// Disabling closes the tooltip, so we need to track when we're closing
		// to avoid an infinite loop in case the tooltip becomes disabled on close
		if ( tooltipData.closing ) {
			return;
		}

		// Clear the interval for delayed tracking tooltips
		clearInterval( this.delayedShow );

		// Only set title if we had one before (see comment in _open())
		// If the title attribute has changed since open(), don't restore
		if ( target.data( "ui-tooltip-title" ) && !target.attr( "title" ) ) {
			target.attr( "title", target.data( "ui-tooltip-title" ) );
		}

		this._removeDescribedBy( target );

		tooltipData.hiding = true;
		tooltip.stop( true );
		this._hide( tooltip, this.options.hide, function() {
			that._removeTooltip( $( this ) );
		} );

		target.removeData( "ui-tooltip-open" );
		this._off( target, "mouseleave focusout keyup" );

		// Remove 'remove' binding only on delegated targets
		if ( target[ 0 ] !== this.element[ 0 ] ) {
			this._off( target, "remove" );
		}
		this._off( this.document, "mousemove" );

		if ( event && event.type === "mouseleave" ) {
			$.each( this.parents, function( id, parent ) {
				$( parent.element ).attr( "title", parent.title );
				delete that.parents[ id ];
			} );
		}

		tooltipData.closing = true;
		this._trigger( "close", event, { tooltip: tooltip } );
		if ( !tooltipData.hiding ) {
			tooltipData.closing = false;
		}
	},

	_tooltip: function( element ) {
		var tooltip = $( "<div>" ).attr( "role", "tooltip" ),
			content = $( "<div>" ).appendTo( tooltip ),
			id = tooltip.uniqueId().attr( "id" );

		this._addClass( content, "ui-tooltip-content" );
		this._addClass( tooltip, "ui-tooltip", "ui-widget ui-widget-content" );

		tooltip.appendTo( this._appendTo( element ) );

		return this.tooltips[ id ] = {
			element: element,
			tooltip: tooltip
		};
	},

	_find: function( target ) {
		var id = target.data( "ui-tooltip-id" );
		return id ? this.tooltips[ id ] : null;
	},

	_removeTooltip: function( tooltip ) {
		tooltip.remove();
		delete this.tooltips[ tooltip.attr( "id" ) ];
	},

	_appendTo: function( target ) {
		var element = target.closest( ".ui-front, dialog" );

		if ( !element.length ) {
			element = this.document[ 0 ].body;
		}

		return element;
	},

	_destroy: function() {
		var that = this;

		// Close open tooltips
		$.each( this.tooltips, function( id, tooltipData ) {

			// Delegate to close method to handle common cleanup
			var event = $.Event( "blur" ),
				element = tooltipData.element;
			event.target = event.currentTarget = element[ 0 ];
			that.close( event, true );

			// Remove immediately; destroying an open tooltip doesn't use the
			// hide animation
			$( "#" + id ).remove();

			// Restore the title
			if ( element.data( "ui-tooltip-title" ) ) {

				// If the title attribute has changed since open(), don't restore
				if ( !element.attr( "title" ) ) {
					element.attr( "title", element.data( "ui-tooltip-title" ) );
				}
				element.removeData( "ui-tooltip-title" );
			}
		} );
		this.liveRegion.remove();
	}
} );

// DEPRECATED
// TODO: Switch return back to widget declaration at top of file when this is removed
if ( $.uiBackCompat !== false ) {

	// Backcompat for tooltipClass option
	$.widget( "ui.tooltip", $.ui.tooltip, {
		options: {
			tooltipClass: null
		},
		_tooltip: function() {
			var tooltipData = this._superApply( arguments );
			if ( this.options.tooltipClass ) {
				tooltipData.tooltip.addClass( this.options.tooltipClass );
			}
			return tooltipData;
		}
	} );
}

var widgetsTooltip = $.ui.tooltip;




}));
(function defineMustache(global,factory){if(typeof exports==="object"&&exports&&typeof exports.nodeName!=="string"){factory(exports)}else if(typeof define==="function"&&define.amd){define(["exports"],factory)}else{global.Mustache={};factory(global.Mustache)}})(this,function mustacheFactory(mustache){var objectToString=Object.prototype.toString;var isArray=Array.isArray||function isArrayPolyfill(object){return objectToString.call(object)==="[object Array]"};function isFunction(object){return typeof object==="function"}function typeStr(obj){return isArray(obj)?"array":typeof obj}function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function hasProperty(obj,propName){return obj!=null&&typeof obj==="object"&&propName in obj}var regExpTest=RegExp.prototype.test;function testRegExp(re,string){return regExpTest.call(re,string)}var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string)}var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};function escapeHtml(string){return String(string).replace(/[&<>"'`=\/]/g,function fromEntityMap(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tagsToCompile){if(typeof tagsToCompile==="string")tagsToCompile=tagsToCompile.split(spaceRe,2);if(!isArray(tagsToCompile)||tagsToCompile.length!==2)throw new Error("Invalid tags: "+tagsToCompile);openingTagRe=new RegExp(escapeRegExp(tagsToCompile[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tagsToCompile[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tagsToCompile[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i<valueLength;++i){chr=value.charAt(i);if(isWhitespace(chr)){spaces.push(tokens.length)}else{nonSpace=true}tokens.push(["text",chr,start,start+1]);start+=1;if(chr==="\n")stripSpace()}}if(!scanner.scan(openingTagRe))break;hasTag=true;type=scanner.scan(tagRe)||"name";scanner.scan(whiteRe);if(type==="="){value=scanner.scanUntil(equalsRe);scanner.scan(equalsRe);scanner.scanUntil(closingTagRe)}else if(type==="{"){value=scanner.scanUntil(closingCurlyRe);scanner.scan(curlyRe);scanner.scanUntil(closingTagRe);type="&"}else{value=scanner.scanUntil(closingTagRe)}if(!scanner.scan(closingTagRe))throw new Error("Unclosed tag at "+scanner.pos);token=[type,value,start,scanner.pos];tokens.push(token);if(type==="#"||type==="^"){sections.push(token)}else if(type==="/"){openSection=sections.pop();if(!openSection)throw new Error('Unopened section "'+value+'" at '+start);if(openSection[1]!==value)throw new Error('Unclosed section "'+openSection[1]+'" at '+start)}else if(type==="name"||type==="{"||type==="&"){nonSpace=true}else if(type==="="){compileTags(value)}}openSection=sections.pop();if(openSection)throw new Error('Unclosed section "'+openSection[1]+'" at '+scanner.pos);return nestTokens(squashTokens(tokens))}function squashTokens(tokens){var squashedTokens=[];var token,lastToken;for(var i=0,numTokens=tokens.length;i<numTokens;++i){token=tokens[i];if(token){if(token[0]==="text"&&lastToken&&lastToken[0]==="text"){lastToken[1]+=token[1];lastToken[3]=token[3]}else{squashedTokens.push(token);lastToken=token}}}return squashedTokens}function nestTokens(tokens){var nestedTokens=[];var collector=nestedTokens;var sections=[];var token,section;for(var i=0,numTokens=tokens.length;i<numTokens;++i){token=tokens[i];switch(token[0]){case"#":case"^":collector.push(token);sections.push(token);collector=token[4]=[];break;case"/":section=sections.pop();section[5]=token[2];collector=sections.length>0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function eos(){return this.tail===""};Scanner.prototype.scan=function scan(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function scanUntil(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function push(view){return new Context(view,this)};Context.prototype.lookup=function lookup(name){var cache=this.cache;var value;if(cache.hasOwnProperty(name)){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index<names.length){if(index===names.length-1)lookupHit=hasProperty(value,names[index]);value=value[names[index++]]}}else{value=context.view[name];lookupHit=hasProperty(context.view,name)}if(lookupHit)break;context=context.parent}cache[name]=value}if(isFunction(value))value=value.call(this.view);return value};function Writer(){this.cache={}}Writer.prototype.clearCache=function clearCache(){this.cache={}};Writer.prototype.parse=function parse(template,tags){var cache=this.cache;var tokens=cache[template];if(tokens==null)tokens=cache[template]=parseTemplate(template,tags);return tokens};Writer.prototype.render=function render(template,view,partials){var tokens=this.parse(template);var context=view instanceof Context?view:new Context(view);return this.renderTokens(tokens,context,partials,template)};Writer.prototype.renderTokens=function renderTokens(tokens,context,partials,originalTemplate){var buffer="";var token,symbol,value;for(var i=0,numTokens=tokens.length;i<numTokens;++i){value=undefined;token=tokens[i];symbol=token[0];if(symbol==="#")value=this.renderSection(token,context,partials,originalTemplate);else if(symbol==="^")value=this.renderInverted(token,context,partials,originalTemplate);else if(symbol===">")value=this.renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this.unescapedValue(token,context);else if(symbol==="name")value=this.escapedValue(token,context);else if(symbol==="text")value=this.rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype.renderSection=function renderSection(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;j<valueLength;++j){buffer+=this.renderTokens(token[4],context.push(value[j]),partials,originalTemplate)}}else if(typeof value==="object"||typeof value==="string"||typeof value==="number"){buffer+=this.renderTokens(token[4],context.push(value),partials,originalTemplate)}else if(isFunction(value)){if(typeof originalTemplate!=="string")throw new Error("Cannot use higher-order sections without the original template");value=value.call(context.view,originalTemplate.slice(token[3],token[5]),subRender);if(value!=null)buffer+=value}else{buffer+=this.renderTokens(token[4],context,partials,originalTemplate)}return buffer};Writer.prototype.renderInverted=function renderInverted(token,context,partials,originalTemplate){var value=context.lookup(token[1]);if(!value||isArray(value)&&value.length===0)return this.renderTokens(token[4],context,partials,originalTemplate)};Writer.prototype.renderPartial=function renderPartial(token,context,partials){if(!partials)return;var value=isFunction(partials)?partials(token[1]):partials[token[1]];if(value!=null)return this.renderTokens(this.parse(value),context,partials,value)};Writer.prototype.unescapedValue=function unescapedValue(token,context){var value=context.lookup(token[1]);if(value!=null)return value};Writer.prototype.escapedValue=function escapedValue(token,context){var value=context.lookup(token[1]);if(value!=null)return mustache.escape(value)};Writer.prototype.rawValue=function rawValue(token){return token[1]};mustache.name="mustache.js";mustache.version="2.3.2";mustache.tags=["{{","}}"];var defaultWriter=new Writer;mustache.clearCache=function clearCache(){return defaultWriter.clearCache()};mustache.parse=function parse(template,tags){return defaultWriter.parse(template,tags)};mustache.render=function render(template,view,partials){if(typeof template!=="string"){throw new TypeError('Invalid template! Template should be a "string" '+'but "'+typeStr(template)+'" was given as the first '+"argument for mustache#render(template, view, partials)")}return defaultWriter.render(template,view,partials)};mustache.to_html=function to_html(template,view,partials,send){var result=mustache.render(template,view,partials);if(isFunction(send)){send(result)}else{return result}};mustache.escape=escapeHtml;mustache.Scanner=Scanner;mustache.Context=Context;mustache.Writer=Writer;return mustache});

//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImpxdWVyeS1taWdyYXRlLmpzIiwicGFjZS5qcyIsInNyY2RvYy1wb2x5ZmlsbC5qcyIsImpxdWVyeS11aS5qcyIsIm11c3RhY2hlLm1pbi5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUNodkJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ3Y2QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ2xGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ2p4a0JBO0FBQ0EiLCJmaWxlIjoiY29tcGF0aWJpbGl0eS12ZW5kb3IuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKiFcclxuICogalF1ZXJ5IE1pZ3JhdGUgLSB2MS40LjEgLSAyMDE2LTA1LTE5XHJcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzXHJcbiAqL1xyXG4oZnVuY3Rpb24oIGpRdWVyeSwgd2luZG93LCB1bmRlZmluZWQgKSB7XG4vLyBTZWUgaHR0cDovL2J1Z3MuanF1ZXJ5LmNvbS90aWNrZXQvMTMzMzVcbi8vIFwidXNlIHN0cmljdFwiO1xuXHJcblxualF1ZXJ5Lm1pZ3JhdGVWZXJzaW9uID0gXCIxLjQuMVwiO1xuXHJcblxudmFyIHdhcm5lZEFib3V0ID0ge307XG5cbi8vIExpc3Qgb2Ygd2FybmluZ3MgYWxyZWFkeSBnaXZlbjsgcHVibGljIHJlYWQgb25seVxualF1ZXJ5Lm1pZ3JhdGVXYXJuaW5ncyA9IFtdO1xuXG4vLyBTZXQgdG8gdHJ1ZSB0byBwcmV2ZW50IGNvbnNvbGUgb3V0cHV0OyBtaWdyYXRlV2FybmluZ3Mgc3RpbGwgbWFpbnRhaW5lZFxuLy8galF1ZXJ5Lm1pZ3JhdGVNdXRlID0gZmFsc2U7XG5cbi8vIFNob3cgYSBtZXNzYWdlIG9uIHRoZSBjb25zb2xlIHNvIGRldnMga25vdyB3ZSdyZSBhY3RpdmVcbmlmICggd2luZG93LmNvbnNvbGUgJiYgd2luZG93LmNvbnNvbGUubG9nICkge1xuXHR3aW5kb3cuY29uc29sZS5sb2coIFwiSlFNSUdSQVRFOiBNaWdyYXRlIGlzIGluc3RhbGxlZFwiICtcblx0XHQoIGpRdWVyeS5taWdyYXRlTXV0ZSA/IFwiXCIgOiBcIiB3aXRoIGxvZ2dpbmcgYWN0aXZlXCIgKSArXG5cdFx0XCIsIHZlcnNpb24gXCIgKyBqUXVlcnkubWlncmF0ZVZlcnNpb24gKTtcbn1cblxuLy8gU2V0IHRvIGZhbHNlIHRvIGRpc2FibGUgdHJhY2VzIHRoYXQgYXBwZWFyIHdpdGggd2FybmluZ3NcbmlmICggalF1ZXJ5Lm1pZ3JhdGVUcmFjZSA9PT0gdW5kZWZpbmVkICkge1xuXHRqUXVlcnkubWlncmF0ZVRyYWNlID0gdHJ1ZTtcbn1cblxuLy8gRm9yZ2V0IGFueSB3YXJuaW5ncyB3ZSd2ZSBhbHJlYWR5IGdpdmVuOyBwdWJsaWNcbmpRdWVyeS5taWdyYXRlUmVzZXQgPSBmdW5jdGlvbigpIHtcblx0d2FybmVkQWJvdXQgPSB7fTtcblx0alF1ZXJ5Lm1pZ3JhdGVXYXJuaW5ncy5sZW5ndGggPSAwO1xufTtcblxuZnVuY3Rpb24gbWlncmF0ZVdhcm4oIG1zZykge1xuXHR2YXIgY29uc29sZSA9IHdpbmRvdy5jb25zb2xlO1xuXHRpZiAoICF3YXJuZWRBYm91dFsgbXNnIF0gKSB7XG5cdFx0d2FybmVkQWJvdXRbIG1zZyBdID0gdHJ1ZTtcblx0XHRqUXVlcnkubWlncmF0ZVdhcm5pbmdzLnB1c2goIG1zZyApO1xuXHRcdGlmICggY29uc29sZSAmJiBjb25zb2xlLndhcm4gJiYgIWpRdWVyeS5taWdyYXRlTXV0ZSApIHtcblx0XHRcdGNvbnNvbGUud2FybiggXCJKUU1JR1JBVEU6IFwiICsgbXNnICk7XG5cdFx0XHRpZiAoIGpRdWVyeS5taWdyYXRlVHJhY2UgJiYgY29uc29sZS50cmFjZSApIHtcblx0XHRcdFx0Y29uc29sZS50cmFjZSgpO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxufVxuXG5mdW5jdGlvbiBtaWdyYXRlV2FyblByb3AoIG9iaiwgcHJvcCwgdmFsdWUsIG1zZyApIHtcblx0aWYgKCBPYmplY3QuZGVmaW5lUHJvcGVydHkgKSB7XG5cdFx0Ly8gT24gRVM1IGJyb3dzZXJzIChub24tb2xkSUUpLCB3YXJuIGlmIHRoZSBjb2RlIHRyaWVzIHRvIGdldCBwcm9wO1xuXHRcdC8vIGFsbG93IHByb3BlcnR5IHRvIGJlIG92ZXJ3cml0dGVuIGluIGNhc2Ugc29tZSBvdGhlciBwbHVnaW4gd2FudHMgaXRcblx0XHR0cnkge1xuXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KCBvYmosIHByb3AsIHtcblx0XHRcdFx0Y29uZmlndXJhYmxlOiB0cnVlLFxuXHRcdFx0XHRlbnVtZXJhYmxlOiB0cnVlLFxuXHRcdFx0XHRnZXQ6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdG1pZ3JhdGVXYXJuKCBtc2cgKTtcblx0XHRcdFx0XHRyZXR1cm4gdmFsdWU7XG5cdFx0XHRcdH0sXG5cdFx0XHRcdHNldDogZnVuY3Rpb24oIG5ld1ZhbHVlICkge1xuXHRcdFx0XHRcdG1pZ3JhdGVXYXJuKCBtc2cgKTtcblx0XHRcdFx0XHR2YWx1ZSA9IG5ld1ZhbHVlO1xuXHRcdFx0XHR9XG5cdFx0XHR9KTtcblx0XHRcdHJldHVybjtcblx0XHR9IGNhdGNoKCBlcnIgKSB7XG5cdFx0XHQvLyBJRTggaXMgYSBkb3BlIGFib3V0IE9iamVjdC5kZWZpbmVQcm9wZXJ0eSwgY2FuJ3Qgd2FybiB0aGVyZVxuXHRcdH1cblx0fVxuXG5cdC8vIE5vbi1FUzUgKG9yIGJyb2tlbikgYnJvd3NlcjsganVzdCBzZXQgdGhlIHByb3BlcnR5XG5cdGpRdWVyeS5fZGVmaW5lUHJvcGVydHlCcm9rZW4gPSB0cnVlO1xuXHRvYmpbIHByb3AgXSA9IHZhbHVlO1xufVxuXG5pZiAoIGRvY3VtZW50LmNvbXBhdE1vZGUgPT09IFwiQmFja0NvbXBhdFwiICkge1xuXHQvLyBqUXVlcnkgaGFzIG5ldmVyIHN1cHBvcnRlZCBvciB0ZXN0ZWQgUXVpcmtzIE1vZGVcblx0bWlncmF0ZVdhcm4oIFwialF1ZXJ5IGlzIG5vdCBjb21wYXRpYmxlIHdpdGggUXVpcmtzIE1vZGVcIiApO1xufVxuXHJcblxudmFyIGF0dHJGbiA9IGpRdWVyeSggXCI8aW5wdXQvPlwiLCB7IHNpemU6IDEgfSApLmF0dHIoXCJzaXplXCIpICYmIGpRdWVyeS5hdHRyRm4sXG5cdG9sZEF0dHIgPSBqUXVlcnkuYXR0cixcblx0dmFsdWVBdHRyR2V0ID0galF1ZXJ5LmF0dHJIb29rcy52YWx1ZSAmJiBqUXVlcnkuYXR0ckhvb2tzLnZhbHVlLmdldCB8fFxuXHRcdGZ1bmN0aW9uKCkgeyByZXR1cm4gbnVsbDsgfSxcblx0dmFsdWVBdHRyU2V0ID0galF1ZXJ5LmF0dHJIb29rcy52YWx1ZSAmJiBqUXVlcnkuYXR0ckhvb2tzLnZhbHVlLnNldCB8fFxuXHRcdGZ1bmN0aW9uKCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9LFxuXHRybm9UeXBlID0gL14oPzppbnB1dHxidXR0b24pJC9pLFxuXHRybm9BdHRyTm9kZVR5cGUgPSAvXlsyMzhdJC8sXG5cdHJib29sZWFuID0gL14oPzphdXRvZm9jdXN8YXV0b3BsYXl8YXN5bmN8Y2hlY2tlZHxjb250cm9sc3xkZWZlcnxkaXNhYmxlZHxoaWRkZW58bG9vcHxtdWx0aXBsZXxvcGVufHJlYWRvbmx5fHJlcXVpcmVkfHNjb3BlZHxzZWxlY3RlZCkkL2ksXG5cdHJ1c2VEZWZhdWx0ID0gL14oPzpjaGVja2VkfHNlbGVjdGVkKSQvaTtcblxuLy8galF1ZXJ5LmF0dHJGblxubWlncmF0ZVdhcm5Qcm9wKCBqUXVlcnksIFwiYXR0ckZuXCIsIGF0dHJGbiB8fCB7fSwgXCJqUXVlcnkuYXR0ckZuIGlzIGRlcHJlY2F0ZWRcIiApO1xuXG5qUXVlcnkuYXR0ciA9IGZ1bmN0aW9uKCBlbGVtLCBuYW1lLCB2YWx1ZSwgcGFzcyApIHtcblx0dmFyIGxvd2VyTmFtZSA9IG5hbWUudG9Mb3dlckNhc2UoKSxcblx0XHRuVHlwZSA9IGVsZW0gJiYgZWxlbS5ub2RlVHlwZTtcblxuXHRpZiAoIHBhc3MgKSB7XG5cdFx0Ly8gU2luY2UgcGFzcyBpcyB1c2VkIGludGVybmFsbHksIHdlIG9ubHkgd2FybiBmb3IgbmV3IGpRdWVyeVxuXHRcdC8vIHZlcnNpb25zIHdoZXJlIHRoZXJlIGlzbid0IGEgcGFzcyBhcmcgaW4gdGhlIGZvcm1hbCBwYXJhbXNcblx0XHRpZiAoIG9sZEF0dHIubGVuZ3RoIDwgNCApIHtcblx0XHRcdG1pZ3JhdGVXYXJuKFwialF1ZXJ5LmZuLmF0dHIoIHByb3BzLCBwYXNzICkgaXMgZGVwcmVjYXRlZFwiKTtcblx0XHR9XG5cdFx0aWYgKCBlbGVtICYmICFybm9BdHRyTm9kZVR5cGUudGVzdCggblR5cGUgKSAmJlxuXHRcdFx0KGF0dHJGbiA/IG5hbWUgaW4gYXR0ckZuIDogalF1ZXJ5LmlzRnVuY3Rpb24oalF1ZXJ5LmZuW25hbWVdKSkgKSB7XG5cdFx0XHRyZXR1cm4galF1ZXJ5KCBlbGVtIClbIG5hbWUgXSggdmFsdWUgKTtcblx0XHR9XG5cdH1cblxuXHQvLyBXYXJuIGlmIHVzZXIgdHJpZXMgdG8gc2V0IGB0eXBlYCwgc2luY2UgaXQgYnJlYWtzIG9uIElFIDYvNy84OyBieSBjaGVja2luZ1xuXHQvLyBmb3IgZGlzY29ubmVjdGVkIGVsZW1lbnRzIHdlIGRvbid0IHdhcm4gb24gJCggXCI8YnV0dG9uPlwiLCB7IHR5cGU6IFwiYnV0dG9uXCIgfSApLlxuXHRpZiAoIG5hbWUgPT09IFwidHlwZVwiICYmIHZhbHVlICE9PSB1bmRlZmluZWQgJiYgcm5vVHlwZS50ZXN0KCBlbGVtLm5vZGVOYW1lICkgJiYgZWxlbS5wYXJlbnROb2RlICkge1xuXHRcdG1pZ3JhdGVXYXJuKFwiQ2FuJ3QgY2hhbmdlIHRoZSAndHlwZScgb2YgYW4gaW5wdXQgb3IgYnV0dG9uIGluIElFIDYvNy84XCIpO1xuXHR9XG5cblx0Ly8gUmVzdG9yZSBib29sSG9vayBmb3IgYm9vbGVhbiBwcm9wZXJ0eS9hdHRyaWJ1dGUgc3luY2hyb25pemF0aW9uXG5cdGlmICggIWpRdWVyeS5hdHRySG9va3NbIGxvd2VyTmFtZSBdICYmIHJib29sZWFuLnRlc3QoIGxvd2VyTmFtZSApICkge1xuXHRcdGpRdWVyeS5hdHRySG9va3NbIGxvd2VyTmFtZSBdID0ge1xuXHRcdFx0Z2V0OiBmdW5jdGlvbiggZWxlbSwgbmFtZSApIHtcblx0XHRcdFx0Ly8gQWxpZ24gYm9vbGVhbiBhdHRyaWJ1dGVzIHdpdGggY29ycmVzcG9uZGluZyBwcm9wZXJ0aWVzXG5cdFx0XHRcdC8vIEZhbGwgYmFjayB0byBhdHRyaWJ1dGUgcHJlc2VuY2Ugd2hlcmUgc29tZSBib29sZWFucyBhcmUgbm90IHN1cHBvcnRlZFxuXHRcdFx0XHR2YXIgYXR0ck5vZGUsXG5cdFx0XHRcdFx0cHJvcGVydHkgPSBqUXVlcnkucHJvcCggZWxlbSwgbmFtZSApO1xuXHRcdFx0XHRyZXR1cm4gcHJvcGVydHkgPT09IHRydWUgfHwgdHlwZW9mIHByb3BlcnR5ICE9PSBcImJvb2xlYW5cIiAmJlxuXHRcdFx0XHRcdCggYXR0ck5vZGUgPSBlbGVtLmdldEF0dHJpYnV0ZU5vZGUobmFtZSkgKSAmJiBhdHRyTm9kZS5ub2RlVmFsdWUgIT09IGZhbHNlID9cblxuXHRcdFx0XHRcdG5hbWUudG9Mb3dlckNhc2UoKSA6XG5cdFx0XHRcdFx0dW5kZWZpbmVkO1xuXHRcdFx0fSxcblx0XHRcdHNldDogZnVuY3Rpb24oIGVsZW0sIHZhbHVlLCBuYW1lICkge1xuXHRcdFx0XHR2YXIgcHJvcE5hbWU7XG5cdFx0XHRcdGlmICggdmFsdWUgPT09IGZhbHNlICkge1xuXHRcdFx0XHRcdC8vIFJlbW92ZSBib29sZWFuIGF0dHJpYnV0ZXMgd2hlbiBzZXQgdG8gZmFsc2Vcblx0XHRcdFx0XHRqUXVlcnkucmVtb3ZlQXR0ciggZWxlbSwgbmFtZSApO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdC8vIHZhbHVlIGlzIHRydWUgc2luY2Ugd2Uga25vdyBhdCB0aGlzIHBvaW50IGl0J3MgdHlwZSBib29sZWFuIGFuZCBub3QgZmFsc2Vcblx0XHRcdFx0XHQvLyBTZXQgYm9vbGVhbiBhdHRyaWJ1dGVzIHRvIHRoZSBzYW1lIG5hbWUgYW5kIHNldCB0aGUgRE9NIHByb3BlcnR5XG5cdFx0XHRcdFx0cHJvcE5hbWUgPSBqUXVlcnkucHJvcEZpeFsgbmFtZSBdIHx8IG5hbWU7XG5cdFx0XHRcdFx0aWYgKCBwcm9wTmFtZSBpbiBlbGVtICkge1xuXHRcdFx0XHRcdFx0Ly8gT25seSBzZXQgdGhlIElETCBzcGVjaWZpY2FsbHkgaWYgaXQgYWxyZWFkeSBleGlzdHMgb24gdGhlIGVsZW1lbnRcblx0XHRcdFx0XHRcdGVsZW1bIHByb3BOYW1lIF0gPSB0cnVlO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGVsZW0uc2V0QXR0cmlidXRlKCBuYW1lLCBuYW1lLnRvTG93ZXJDYXNlKCkgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRyZXR1cm4gbmFtZTtcblx0XHRcdH1cblx0XHR9O1xuXG5cdFx0Ly8gV2FybiBvbmx5IGZvciBhdHRyaWJ1dGVzIHRoYXQgY2FuIHJlbWFpbiBkaXN0aW5jdCBmcm9tIHRoZWlyIHByb3BlcnRpZXMgcG9zdC0xLjlcblx0XHRpZiAoIHJ1c2VEZWZhdWx0LnRlc3QoIGxvd2VyTmFtZSApICkge1xuXHRcdFx0bWlncmF0ZVdhcm4oIFwialF1ZXJ5LmZuLmF0dHIoJ1wiICsgbG93ZXJOYW1lICsgXCInKSBtaWdodCB1c2UgcHJvcGVydHkgaW5zdGVhZCBvZiBhdHRyaWJ1dGVcIiApO1xuXHRcdH1cblx0fVxuXG5cdHJldHVybiBvbGRBdHRyLmNhbGwoIGpRdWVyeSwgZWxlbSwgbmFtZSwgdmFsdWUgKTtcbn07XG5cbi8vIGF0dHJIb29rczogdmFsdWVcbmpRdWVyeS5hdHRySG9va3MudmFsdWUgPSB7XG5cdGdldDogZnVuY3Rpb24oIGVsZW0sIG5hbWUgKSB7XG5cdFx0dmFyIG5vZGVOYW1lID0gKCBlbGVtLm5vZGVOYW1lIHx8IFwiXCIgKS50b0xvd2VyQ2FzZSgpO1xuXHRcdGlmICggbm9kZU5hbWUgPT09IFwiYnV0dG9uXCIgKSB7XG5cdFx0XHRyZXR1cm4gdmFsdWVBdHRyR2V0LmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcblx0XHR9XG5cdFx0aWYgKCBub2RlTmFtZSAhPT0gXCJpbnB1dFwiICYmIG5vZGVOYW1lICE9PSBcIm9wdGlvblwiICkge1xuXHRcdFx0bWlncmF0ZVdhcm4oXCJqUXVlcnkuZm4uYXR0cigndmFsdWUnKSBubyBsb25nZXIgZ2V0cyBwcm9wZXJ0aWVzXCIpO1xuXHRcdH1cblx0XHRyZXR1cm4gbmFtZSBpbiBlbGVtID9cblx0XHRcdGVsZW0udmFsdWUgOlxuXHRcdFx0bnVsbDtcblx0fSxcblx0c2V0OiBmdW5jdGlvbiggZWxlbSwgdmFsdWUgKSB7XG5cdFx0dmFyIG5vZGVOYW1lID0gKCBlbGVtLm5vZGVOYW1lIHx8IFwiXCIgKS50b0xvd2VyQ2FzZSgpO1xuXHRcdGlmICggbm9kZU5hbWUgPT09IFwiYnV0dG9uXCIgKSB7XG5cdFx0XHRyZXR1cm4gdmFsdWVBdHRyU2V0LmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcblx0XHR9XG5cdFx0aWYgKCBub2RlTmFtZSAhPT0gXCJpbnB1dFwiICYmIG5vZGVOYW1lICE9PSBcIm9wdGlvblwiICkge1xuXHRcdFx0bWlncmF0ZVdhcm4oXCJqUXVlcnkuZm4uYXR0cigndmFsdWUnLCB2YWwpIG5vIGxvbmdlciBzZXRzIHByb3BlcnRpZXNcIik7XG5cdFx0fVxuXHRcdC8vIERvZXMgbm90IHJldHVybiBzbyB0aGF0IHNldEF0dHJpYnV0ZSBpcyBhbHNvIHVzZWRcblx0XHRlbGVtLnZhbHVlID0gdmFsdWU7XG5cdH1cbn07XG5cclxuXG52YXIgbWF0Y2hlZCwgYnJvd3Nlcixcblx0b2xkSW5pdCA9IGpRdWVyeS5mbi5pbml0LFxuXHRvbGRGaW5kID0galF1ZXJ5LmZpbmQsXG5cdG9sZFBhcnNlSlNPTiA9IGpRdWVyeS5wYXJzZUpTT04sXG5cdHJzcGFjZUFuZ2xlID0gL15cXHMqPC8sXG5cdHJhdHRySGFzaFRlc3QgPSAvXFxbKFxccypbLVxcd10rXFxzKikoW358XiQqXT89KVxccyooWy1cXHcjXSo/I1stXFx3I10qKVxccypcXF0vLFxuXHRyYXR0ckhhc2hHbG9iID0gL1xcWyhcXHMqWy1cXHddK1xccyopKFt+fF4kKl0/PSlcXHMqKFstXFx3I10qPyNbLVxcdyNdKilcXHMqXFxdL2csXG5cdC8vIE5vdGU6IFhTUyBjaGVjayBpcyBkb25lIGJlbG93IGFmdGVyIHN0cmluZyBpcyB0cmltbWVkXG5cdHJxdWlja0V4cHIgPSAvXihbXjxdKikoPFtcXHdcXFddKz4pKFtePl0qKSQvO1xuXG4vLyAkKGh0bWwpIFwibG9va3MgbGlrZSBodG1sXCIgcnVsZSBjaGFuZ2VcbmpRdWVyeS5mbi5pbml0ID0gZnVuY3Rpb24oIHNlbGVjdG9yLCBjb250ZXh0LCByb290alF1ZXJ5ICkge1xuXHR2YXIgbWF0Y2gsIHJldDtcblxuXHRpZiAoIHNlbGVjdG9yICYmIHR5cGVvZiBzZWxlY3RvciA9PT0gXCJzdHJpbmdcIiApIHtcblx0XHRpZiAoICFqUXVlcnkuaXNQbGFpbk9iamVjdCggY29udGV4dCApICYmXG5cdFx0XHRcdChtYXRjaCA9IHJxdWlja0V4cHIuZXhlYyggalF1ZXJ5LnRyaW0oIHNlbGVjdG9yICkgKSkgJiYgbWF0Y2hbIDAgXSApIHtcblxuXHRcdFx0Ly8gVGhpcyBpcyBhbiBIVE1MIHN0cmluZyBhY2NvcmRpbmcgdG8gdGhlIFwib2xkXCIgcnVsZXM7IGlzIGl0IHN0aWxsP1xuXHRcdFx0aWYgKCAhcnNwYWNlQW5nbGUudGVzdCggc2VsZWN0b3IgKSApIHtcblx0XHRcdFx0bWlncmF0ZVdhcm4oXCIkKGh0bWwpIEhUTUwgc3RyaW5ncyBtdXN0IHN0YXJ0IHdpdGggJzwnIGNoYXJhY3RlclwiKTtcblx0XHRcdH1cblx0XHRcdGlmICggbWF0Y2hbIDMgXSApIHtcblx0XHRcdFx0bWlncmF0ZVdhcm4oXCIkKGh0bWwpIEhUTUwgdGV4dCBhZnRlciBsYXN0IHRhZyBpcyBpZ25vcmVkXCIpO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBDb25zaXN0ZW50bHkgcmVqZWN0IGFueSBIVE1MLWxpa2Ugc3RyaW5nIHN0YXJ0aW5nIHdpdGggYSBoYXNoIChnaC05NTIxKVxuXHRcdFx0Ly8gTm90ZSB0aGF0IHRoaXMgbWF5IGJyZWFrIGpRdWVyeSAxLjYueCBjb2RlIHRoYXQgb3RoZXJ3aXNlIHdvdWxkIHdvcmsuXG5cdFx0XHRpZiAoIG1hdGNoWyAwIF0uY2hhckF0KCAwICkgPT09IFwiI1wiICkge1xuXHRcdFx0XHRtaWdyYXRlV2FybihcIkhUTUwgc3RyaW5nIGNhbm5vdCBzdGFydCB3aXRoIGEgJyMnIGNoYXJhY3RlclwiKTtcblx0XHRcdFx0alF1ZXJ5LmVycm9yKFwiSlFNSUdSQVRFOiBJbnZhbGlkIHNlbGVjdG9yIHN0cmluZyAoWFNTKVwiKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gTm93IHByb2Nlc3MgdXNpbmcgbG9vc2UgcnVsZXM7IGxldCBwcmUtMS44IHBsYXkgdG9vXG5cdFx0XHQvLyBJcyB0aGlzIGEgalF1ZXJ5IGNvbnRleHQ/IHBhcnNlSFRNTCBleHBlY3RzIGEgRE9NIGVsZW1lbnQgKCMxNzgpXG5cdFx0XHRpZiAoIGNvbnRleHQgJiYgY29udGV4dC5jb250ZXh0ICYmIGNvbnRleHQuY29udGV4dC5ub2RlVHlwZSApIHtcblx0XHRcdFx0Y29udGV4dCA9IGNvbnRleHQuY29udGV4dDtcblx0XHRcdH1cblxuXHRcdFx0aWYgKCBqUXVlcnkucGFyc2VIVE1MICkge1xuXHRcdFx0XHRyZXR1cm4gb2xkSW5pdC5jYWxsKCB0aGlzLFxuXHRcdFx0XHRcdFx0alF1ZXJ5LnBhcnNlSFRNTCggbWF0Y2hbIDIgXSwgY29udGV4dCAmJiBjb250ZXh0Lm93bmVyRG9jdW1lbnQgfHxcblx0XHRcdFx0XHRcdFx0Y29udGV4dCB8fCBkb2N1bWVudCwgdHJ1ZSApLCBjb250ZXh0LCByb290alF1ZXJ5ICk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0cmV0ID0gb2xkSW5pdC5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cblx0Ly8gRmlsbCBpbiBzZWxlY3RvciBhbmQgY29udGV4dCBwcm9wZXJ0aWVzIHNvIC5saXZlKCkgd29ya3Ncblx0aWYgKCBzZWxlY3RvciAmJiBzZWxlY3Rvci5zZWxlY3RvciAhPT0gdW5kZWZpbmVkICkge1xuXHRcdC8vIEEgalF1ZXJ5IG9iamVjdCwgY29weSBpdHMgcHJvcGVydGllc1xuXHRcdHJldC5zZWxlY3RvciA9IHNlbGVjdG9yLnNlbGVjdG9yO1xuXHRcdHJldC5jb250ZXh0ID0gc2VsZWN0b3IuY29udGV4dDtcblxuXHR9IGVsc2Uge1xuXHRcdHJldC5zZWxlY3RvciA9IHR5cGVvZiBzZWxlY3RvciA9PT0gXCJzdHJpbmdcIiA/IHNlbGVjdG9yIDogXCJcIjtcblx0XHRpZiAoIHNlbGVjdG9yICkge1xuXHRcdFx0cmV0LmNvbnRleHQgPSBzZWxlY3Rvci5ub2RlVHlwZT8gc2VsZWN0b3IgOiBjb250ZXh0IHx8IGRvY3VtZW50O1xuXHRcdH1cblx0fVxuXG5cdHJldHVybiByZXQ7XG59O1xualF1ZXJ5LmZuLmluaXQucHJvdG90eXBlID0galF1ZXJ5LmZuO1xuXG5qUXVlcnkuZmluZCA9IGZ1bmN0aW9uKCBzZWxlY3RvciApIHtcblx0dmFyIGFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCggYXJndW1lbnRzICk7XG5cblx0Ly8gU3VwcG9ydDogUGhhbnRvbUpTIDEueFxuXHQvLyBTdHJpbmcjbWF0Y2ggZmFpbHMgdG8gbWF0Y2ggd2hlbiB1c2VkIHdpdGggYSAvL2cgUmVnRXhwLCBvbmx5IG9uIHNvbWUgc3RyaW5nc1xuXHRpZiAoIHR5cGVvZiBzZWxlY3RvciA9PT0gXCJzdHJpbmdcIiAmJiByYXR0ckhhc2hUZXN0LnRlc3QoIHNlbGVjdG9yICkgKSB7XG5cblx0XHQvLyBUaGUgbm9uc3RhbmRhcmQgYW5kIHVuZG9jdW1lbnRlZCB1bnF1b3RlZC1oYXNoIHdhcyByZW1vdmVkIGluIGpRdWVyeSAxLjEyLjBcblx0XHQvLyBGaXJzdCBzZWUgaWYgcVMgdGhpbmtzIGl0J3MgYSB2YWxpZCBzZWxlY3RvciwgaWYgc28gYXZvaWQgYSBmYWxzZSBwb3NpdGl2ZVxuXHRcdHRyeSB7XG5cdFx0XHRkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCBzZWxlY3RvciApO1xuXHRcdH0gY2F0Y2ggKCBlcnIxICkge1xuXG5cdFx0XHQvLyBEaWRuJ3QgKmxvb2sqIHZhbGlkIHRvIHFTQSwgd2FybiBhbmQgdHJ5IHF1b3Rpbmcgd2hhdCB3ZSB0aGluayBpcyB0aGUgdmFsdWVcblx0XHRcdHNlbGVjdG9yID0gc2VsZWN0b3IucmVwbGFjZSggcmF0dHJIYXNoR2xvYiwgZnVuY3Rpb24oIF8sIGF0dHIsIG9wLCB2YWx1ZSApIHtcblx0XHRcdFx0cmV0dXJuIFwiW1wiICsgYXR0ciArIG9wICsgXCJcXFwiXCIgKyB2YWx1ZSArIFwiXFxcIl1cIjtcblx0XHRcdH0gKTtcblxuXHRcdFx0Ly8gSWYgdGhlIHJlZ2V4cCAqbWF5KiBoYXZlIGNyZWF0ZWQgYW4gaW52YWxpZCBzZWxlY3RvciwgZG9uJ3QgdXBkYXRlIGl0XG5cdFx0XHQvLyBOb3RlIHRoYXQgdGhlcmUgbWF5IGJlIGZhbHNlIGFsYXJtcyBpZiBzZWxlY3RvciB1c2VzIGpRdWVyeSBleHRlbnNpb25zXG5cdFx0XHR0cnkge1xuXHRcdFx0XHRkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCBzZWxlY3RvciApO1xuXHRcdFx0XHRtaWdyYXRlV2FybiggXCJBdHRyaWJ1dGUgc2VsZWN0b3Igd2l0aCAnIycgbXVzdCBiZSBxdW90ZWQ6IFwiICsgYXJnc1sgMCBdICk7XG5cdFx0XHRcdGFyZ3NbIDAgXSA9IHNlbGVjdG9yO1xuXHRcdFx0fSBjYXRjaCAoIGVycjIgKSB7XG5cdFx0XHRcdG1pZ3JhdGVXYXJuKCBcIkF0dHJpYnV0ZSBzZWxlY3RvciB3aXRoICcjJyB3YXMgbm90IGZpeGVkOiBcIiArIGFyZ3NbIDAgXSApO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdHJldHVybiBvbGRGaW5kLmFwcGx5KCB0aGlzLCBhcmdzICk7XG59O1xuXG4vLyBDb3B5IHByb3BlcnRpZXMgYXR0YWNoZWQgdG8gb3JpZ2luYWwgalF1ZXJ5LmZpbmQgbWV0aG9kIChlLmcuIC5hdHRyLCAuaXNYTUwpXG52YXIgZmluZFByb3A7XG5mb3IgKCBmaW5kUHJvcCBpbiBvbGRGaW5kICkge1xuXHRpZiAoIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCggb2xkRmluZCwgZmluZFByb3AgKSApIHtcblx0XHRqUXVlcnkuZmluZFsgZmluZFByb3AgXSA9IG9sZEZpbmRbIGZpbmRQcm9wIF07XG5cdH1cbn1cblxuLy8gTGV0ICQucGFyc2VKU09OKGZhbHN5X3ZhbHVlKSByZXR1cm4gbnVsbFxualF1ZXJ5LnBhcnNlSlNPTiA9IGZ1bmN0aW9uKCBqc29uICkge1xuXHRpZiAoICFqc29uICkge1xuXHRcdG1pZ3JhdGVXYXJuKFwialF1ZXJ5LnBhcnNlSlNPTiByZXF1aXJlcyBhIHZhbGlkIEpTT04gc3RyaW5nXCIpO1xuXHRcdHJldHVybiBudWxsO1xuXHR9XG5cdHJldHVybiBvbGRQYXJzZUpTT04uYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xufTtcblxualF1ZXJ5LnVhTWF0Y2ggPSBmdW5jdGlvbiggdWEgKSB7XG5cdHVhID0gdWEudG9Mb3dlckNhc2UoKTtcblxuXHR2YXIgbWF0Y2ggPSAvKGNocm9tZSlbIFxcL10oW1xcdy5dKykvLmV4ZWMoIHVhICkgfHxcblx0XHQvKHdlYmtpdClbIFxcL10oW1xcdy5dKykvLmV4ZWMoIHVhICkgfHxcblx0XHQvKG9wZXJhKSg/Oi4qdmVyc2lvbnwpWyBcXC9dKFtcXHcuXSspLy5leGVjKCB1YSApIHx8XG5cdFx0Lyhtc2llKSAoW1xcdy5dKykvLmV4ZWMoIHVhICkgfHxcblx0XHR1YS5pbmRleE9mKFwiY29tcGF0aWJsZVwiKSA8IDAgJiYgLyhtb3ppbGxhKSg/Oi4qPyBydjooW1xcdy5dKyl8KS8uZXhlYyggdWEgKSB8fFxuXHRcdFtdO1xuXG5cdHJldHVybiB7XG5cdFx0YnJvd3NlcjogbWF0Y2hbIDEgXSB8fCBcIlwiLFxuXHRcdHZlcnNpb246IG1hdGNoWyAyIF0gfHwgXCIwXCJcblx0fTtcbn07XG5cbi8vIERvbid0IGNsb2JiZXIgYW55IGV4aXN0aW5nIGpRdWVyeS5icm93c2VyIGluIGNhc2UgaXQncyBkaWZmZXJlbnRcbmlmICggIWpRdWVyeS5icm93c2VyICkge1xuXHRtYXRjaGVkID0galF1ZXJ5LnVhTWF0Y2goIG5hdmlnYXRvci51c2VyQWdlbnQgKTtcblx0YnJvd3NlciA9IHt9O1xuXG5cdGlmICggbWF0Y2hlZC5icm93c2VyICkge1xuXHRcdGJyb3dzZXJbIG1hdGNoZWQuYnJvd3NlciBdID0gdHJ1ZTtcblx0XHRicm93c2VyLnZlcnNpb24gPSBtYXRjaGVkLnZlcnNpb247XG5cdH1cblxuXHQvLyBDaHJvbWUgaXMgV2Via2l0LCBidXQgV2Via2l0IGlzIGFsc28gU2FmYXJpLlxuXHRpZiAoIGJyb3dzZXIuY2hyb21lICkge1xuXHRcdGJyb3dzZXIud2Via2l0ID0gdHJ1ZTtcblx0fSBlbHNlIGlmICggYnJvd3Nlci53ZWJraXQgKSB7XG5cdFx0YnJvd3Nlci5zYWZhcmkgPSB0cnVlO1xuXHR9XG5cblx0alF1ZXJ5LmJyb3dzZXIgPSBicm93c2VyO1xufVxuXG4vLyBXYXJuIGlmIHRoZSBjb2RlIHRyaWVzIHRvIGdldCBqUXVlcnkuYnJvd3NlclxubWlncmF0ZVdhcm5Qcm9wKCBqUXVlcnksIFwiYnJvd3NlclwiLCBqUXVlcnkuYnJvd3NlciwgXCJqUXVlcnkuYnJvd3NlciBpcyBkZXByZWNhdGVkXCIgKTtcblxuLy8galF1ZXJ5LmJveE1vZGVsIGRlcHJlY2F0ZWQgaW4gMS4zLCBqUXVlcnkuc3VwcG9ydC5ib3hNb2RlbCBkZXByZWNhdGVkIGluIDEuN1xualF1ZXJ5LmJveE1vZGVsID0galF1ZXJ5LnN1cHBvcnQuYm94TW9kZWwgPSAoZG9jdW1lbnQuY29tcGF0TW9kZSA9PT0gXCJDU1MxQ29tcGF0XCIpO1xubWlncmF0ZVdhcm5Qcm9wKCBqUXVlcnksIFwiYm94TW9kZWxcIiwgalF1ZXJ5LmJveE1vZGVsLCBcImpRdWVyeS5ib3hNb2RlbCBpcyBkZXByZWNhdGVkXCIgKTtcbm1pZ3JhdGVXYXJuUHJvcCggalF1ZXJ5LnN1cHBvcnQsIFwiYm94TW9kZWxcIiwgalF1ZXJ5LnN1cHBvcnQuYm94TW9kZWwsIFwialF1ZXJ5LnN1cHBvcnQuYm94TW9kZWwgaXMgZGVwcmVjYXRlZFwiICk7XG5cbmpRdWVyeS5zdWIgPSBmdW5jdGlvbigpIHtcblx0ZnVuY3Rpb24galF1ZXJ5U3ViKCBzZWxlY3RvciwgY29udGV4dCApIHtcblx0XHRyZXR1cm4gbmV3IGpRdWVyeVN1Yi5mbi5pbml0KCBzZWxlY3RvciwgY29udGV4dCApO1xuXHR9XG5cdGpRdWVyeS5leHRlbmQoIHRydWUsIGpRdWVyeVN1YiwgdGhpcyApO1xuXHRqUXVlcnlTdWIuc3VwZXJjbGFzcyA9IHRoaXM7XG5cdGpRdWVyeVN1Yi5mbiA9IGpRdWVyeVN1Yi5wcm90b3R5cGUgPSB0aGlzKCk7XG5cdGpRdWVyeVN1Yi5mbi5jb25zdHJ1Y3RvciA9IGpRdWVyeVN1Yjtcblx0alF1ZXJ5U3ViLnN1YiA9IHRoaXMuc3ViO1xuXHRqUXVlcnlTdWIuZm4uaW5pdCA9IGZ1bmN0aW9uIGluaXQoIHNlbGVjdG9yLCBjb250ZXh0ICkge1xuXHRcdHZhciBpbnN0YW5jZSA9IGpRdWVyeS5mbi5pbml0LmNhbGwoIHRoaXMsIHNlbGVjdG9yLCBjb250ZXh0LCByb290alF1ZXJ5U3ViICk7XG5cdFx0cmV0dXJuIGluc3RhbmNlIGluc3RhbmNlb2YgalF1ZXJ5U3ViID9cblx0XHRcdGluc3RhbmNlIDpcblx0XHRcdGpRdWVyeVN1YiggaW5zdGFuY2UgKTtcblx0fTtcblx0alF1ZXJ5U3ViLmZuLmluaXQucHJvdG90eXBlID0galF1ZXJ5U3ViLmZuO1xuXHR2YXIgcm9vdGpRdWVyeVN1YiA9IGpRdWVyeVN1Yihkb2N1bWVudCk7XG5cdG1pZ3JhdGVXYXJuKCBcImpRdWVyeS5zdWIoKSBpcyBkZXByZWNhdGVkXCIgKTtcblx0cmV0dXJuIGpRdWVyeVN1Yjtcbn07XG5cbi8vIFRoZSBudW1iZXIgb2YgZWxlbWVudHMgY29udGFpbmVkIGluIHRoZSBtYXRjaGVkIGVsZW1lbnQgc2V0XG5qUXVlcnkuZm4uc2l6ZSA9IGZ1bmN0aW9uKCkge1xuXHRtaWdyYXRlV2FybiggXCJqUXVlcnkuZm4uc2l6ZSgpIGlzIGRlcHJlY2F0ZWQ7IHVzZSB0aGUgLmxlbmd0aCBwcm9wZXJ0eVwiICk7XG5cdHJldHVybiB0aGlzLmxlbmd0aDtcbn07XG5cclxuXG52YXIgaW50ZXJuYWxTd2FwQ2FsbCA9IGZhbHNlO1xuXG4vLyBJZiB0aGlzIHZlcnNpb24gb2YgalF1ZXJ5IGhhcyAuc3dhcCgpLCBkb24ndCBmYWxzZS1hbGFybSBvbiBpbnRlcm5hbCB1c2VzXG5pZiAoIGpRdWVyeS5zd2FwICkge1xuXHRqUXVlcnkuZWFjaCggWyBcImhlaWdodFwiLCBcIndpZHRoXCIsIFwicmVsaWFibGVNYXJnaW5SaWdodFwiIF0sIGZ1bmN0aW9uKCBfLCBuYW1lICkge1xuXHRcdHZhciBvbGRIb29rID0galF1ZXJ5LmNzc0hvb2tzWyBuYW1lIF0gJiYgalF1ZXJ5LmNzc0hvb2tzWyBuYW1lIF0uZ2V0O1xuXG5cdFx0aWYgKCBvbGRIb29rICkge1xuXHRcdFx0alF1ZXJ5LmNzc0hvb2tzWyBuYW1lIF0uZ2V0ID0gZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHZhciByZXQ7XG5cblx0XHRcdFx0aW50ZXJuYWxTd2FwQ2FsbCA9IHRydWU7XG5cdFx0XHRcdHJldCA9IG9sZEhvb2suYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdFx0XHRpbnRlcm5hbFN3YXBDYWxsID0gZmFsc2U7XG5cdFx0XHRcdHJldHVybiByZXQ7XG5cdFx0XHR9O1xuXHRcdH1cblx0fSk7XG59XG5cbmpRdWVyeS5zd2FwID0gZnVuY3Rpb24oIGVsZW0sIG9wdGlvbnMsIGNhbGxiYWNrLCBhcmdzICkge1xuXHR2YXIgcmV0LCBuYW1lLFxuXHRcdG9sZCA9IHt9O1xuXG5cdGlmICggIWludGVybmFsU3dhcENhbGwgKSB7XG5cdFx0bWlncmF0ZVdhcm4oIFwialF1ZXJ5LnN3YXAoKSBpcyB1bmRvY3VtZW50ZWQgYW5kIGRlcHJlY2F0ZWRcIiApO1xuXHR9XG5cblx0Ly8gUmVtZW1iZXIgdGhlIG9sZCB2YWx1ZXMsIGFuZCBpbnNlcnQgdGhlIG5ldyBvbmVzXG5cdGZvciAoIG5hbWUgaW4gb3B0aW9ucyApIHtcblx0XHRvbGRbIG5hbWUgXSA9IGVsZW0uc3R5bGVbIG5hbWUgXTtcblx0XHRlbGVtLnN0eWxlWyBuYW1lIF0gPSBvcHRpb25zWyBuYW1lIF07XG5cdH1cblxuXHRyZXQgPSBjYWxsYmFjay5hcHBseSggZWxlbSwgYXJncyB8fCBbXSApO1xuXG5cdC8vIFJldmVydCB0aGUgb2xkIHZhbHVlc1xuXHRmb3IgKCBuYW1lIGluIG9wdGlvbnMgKSB7XG5cdFx0ZWxlbS5zdHlsZVsgbmFtZSBdID0gb2xkWyBuYW1lIF07XG5cdH1cblxuXHRyZXR1cm4gcmV0O1xufTtcblxyXG5cbi8vIEVuc3VyZSB0aGF0ICQuYWpheCBnZXRzIHRoZSBuZXcgcGFyc2VKU09OIGRlZmluZWQgaW4gY29yZS5qc1xualF1ZXJ5LmFqYXhTZXR1cCh7XG5cdGNvbnZlcnRlcnM6IHtcblx0XHRcInRleHQganNvblwiOiBqUXVlcnkucGFyc2VKU09OXG5cdH1cbn0pO1xuXHJcblxudmFyIG9sZEZuRGF0YSA9IGpRdWVyeS5mbi5kYXRhO1xuXG5qUXVlcnkuZm4uZGF0YSA9IGZ1bmN0aW9uKCBuYW1lICkge1xuXHR2YXIgcmV0LCBldnQsXG5cdFx0ZWxlbSA9IHRoaXNbMF07XG5cblx0Ly8gSGFuZGxlcyAxLjcgd2hpY2ggaGFzIHRoaXMgYmVoYXZpb3IgYW5kIDEuOCB3aGljaCBkb2Vzbid0XG5cdGlmICggZWxlbSAmJiBuYW1lID09PSBcImV2ZW50c1wiICYmIGFyZ3VtZW50cy5sZW5ndGggPT09IDEgKSB7XG5cdFx0cmV0ID0galF1ZXJ5LmRhdGEoIGVsZW0sIG5hbWUgKTtcblx0XHRldnQgPSBqUXVlcnkuX2RhdGEoIGVsZW0sIG5hbWUgKTtcblx0XHRpZiAoICggcmV0ID09PSB1bmRlZmluZWQgfHwgcmV0ID09PSBldnQgKSAmJiBldnQgIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdG1pZ3JhdGVXYXJuKFwiVXNlIG9mIGpRdWVyeS5mbi5kYXRhKCdldmVudHMnKSBpcyBkZXByZWNhdGVkXCIpO1xuXHRcdFx0cmV0dXJuIGV2dDtcblx0XHR9XG5cdH1cblx0cmV0dXJuIG9sZEZuRGF0YS5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG59O1xuXHJcblxudmFyIHJzY3JpcHRUeXBlID0gL1xcLyhqYXZhfGVjbWEpc2NyaXB0L2k7XG5cbi8vIFNpbmNlIGpRdWVyeS5jbGVhbiBpcyB1c2VkIGludGVybmFsbHkgb24gb2xkZXIgdmVyc2lvbnMsIHdlIG9ubHkgc2hpbSBpZiBpdCdzIG1pc3NpbmdcbmlmICggIWpRdWVyeS5jbGVhbiApIHtcblx0alF1ZXJ5LmNsZWFuID0gZnVuY3Rpb24oIGVsZW1zLCBjb250ZXh0LCBmcmFnbWVudCwgc2NyaXB0cyApIHtcblx0XHQvLyBTZXQgY29udGV4dCBwZXIgMS44IGxvZ2ljXG5cdFx0Y29udGV4dCA9IGNvbnRleHQgfHwgZG9jdW1lbnQ7XG5cdFx0Y29udGV4dCA9ICFjb250ZXh0Lm5vZGVUeXBlICYmIGNvbnRleHRbMF0gfHwgY29udGV4dDtcblx0XHRjb250ZXh0ID0gY29udGV4dC5vd25lckRvY3VtZW50IHx8IGNvbnRleHQ7XG5cblx0XHRtaWdyYXRlV2FybihcImpRdWVyeS5jbGVhbigpIGlzIGRlcHJlY2F0ZWRcIik7XG5cblx0XHR2YXIgaSwgZWxlbSwgaGFuZGxlU2NyaXB0LCBqc1RhZ3MsXG5cdFx0XHRyZXQgPSBbXTtcblxuXHRcdGpRdWVyeS5tZXJnZSggcmV0LCBqUXVlcnkuYnVpbGRGcmFnbWVudCggZWxlbXMsIGNvbnRleHQgKS5jaGlsZE5vZGVzICk7XG5cblx0XHQvLyBDb21wbGV4IGxvZ2ljIGxpZnRlZCBkaXJlY3RseSBmcm9tIGpRdWVyeSAxLjhcblx0XHRpZiAoIGZyYWdtZW50ICkge1xuXHRcdFx0Ly8gU3BlY2lhbCBoYW5kbGluZyBvZiBlYWNoIHNjcmlwdCBlbGVtZW50XG5cdFx0XHRoYW5kbGVTY3JpcHQgPSBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdFx0Ly8gQ2hlY2sgaWYgd2UgY29uc2lkZXIgaXQgZXhlY3V0YWJsZVxuXHRcdFx0XHRpZiAoICFlbGVtLnR5cGUgfHwgcnNjcmlwdFR5cGUudGVzdCggZWxlbS50eXBlICkgKSB7XG5cdFx0XHRcdFx0Ly8gRGV0YWNoIHRoZSBzY3JpcHQgYW5kIHN0b3JlIGl0IGluIHRoZSBzY3JpcHRzIGFycmF5IChpZiBwcm92aWRlZCkgb3IgdGhlIGZyYWdtZW50XG5cdFx0XHRcdFx0Ly8gUmV0dXJuIHRydXRoeSB0byBpbmRpY2F0ZSB0aGF0IGl0IGhhcyBiZWVuIGhhbmRsZWRcblx0XHRcdFx0XHRyZXR1cm4gc2NyaXB0cyA/XG5cdFx0XHRcdFx0XHRzY3JpcHRzLnB1c2goIGVsZW0ucGFyZW50Tm9kZSA/IGVsZW0ucGFyZW50Tm9kZS5yZW1vdmVDaGlsZCggZWxlbSApIDogZWxlbSApIDpcblx0XHRcdFx0XHRcdGZyYWdtZW50LmFwcGVuZENoaWxkKCBlbGVtICk7XG5cdFx0XHRcdH1cblx0XHRcdH07XG5cblx0XHRcdGZvciAoIGkgPSAwOyAoZWxlbSA9IHJldFtpXSkgIT0gbnVsbDsgaSsrICkge1xuXHRcdFx0XHQvLyBDaGVjayBpZiB3ZSdyZSBkb25lIGFmdGVyIGhhbmRsaW5nIGFuIGV4ZWN1dGFibGUgc2NyaXB0XG5cdFx0XHRcdGlmICggISggalF1ZXJ5Lm5vZGVOYW1lKCBlbGVtLCBcInNjcmlwdFwiICkgJiYgaGFuZGxlU2NyaXB0KCBlbGVtICkgKSApIHtcblx0XHRcdFx0XHQvLyBBcHBlbmQgdG8gZnJhZ21lbnQgYW5kIGhhbmRsZSBlbWJlZGRlZCBzY3JpcHRzXG5cdFx0XHRcdFx0ZnJhZ21lbnQuYXBwZW5kQ2hpbGQoIGVsZW0gKTtcblx0XHRcdFx0XHRpZiAoIHR5cGVvZiBlbGVtLmdldEVsZW1lbnRzQnlUYWdOYW1lICE9PSBcInVuZGVmaW5lZFwiICkge1xuXHRcdFx0XHRcdFx0Ly8gaGFuZGxlU2NyaXB0IGFsdGVycyB0aGUgRE9NLCBzbyB1c2UgalF1ZXJ5Lm1lcmdlIHRvIGVuc3VyZSBzbmFwc2hvdCBpdGVyYXRpb25cblx0XHRcdFx0XHRcdGpzVGFncyA9IGpRdWVyeS5ncmVwKCBqUXVlcnkubWVyZ2UoIFtdLCBlbGVtLmdldEVsZW1lbnRzQnlUYWdOYW1lKFwic2NyaXB0XCIpICksIGhhbmRsZVNjcmlwdCApO1xuXG5cdFx0XHRcdFx0XHQvLyBTcGxpY2UgdGhlIHNjcmlwdHMgaW50byByZXQgYWZ0ZXIgdGhlaXIgZm9ybWVyIGFuY2VzdG9yIGFuZCBhZHZhbmNlIG91ciBpbmRleCBiZXlvbmQgdGhlbVxuXHRcdFx0XHRcdFx0cmV0LnNwbGljZS5hcHBseSggcmV0LCBbaSArIDEsIDBdLmNvbmNhdCgganNUYWdzICkgKTtcblx0XHRcdFx0XHRcdGkgKz0ganNUYWdzLmxlbmd0aDtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gcmV0O1xuXHR9O1xufVxuXHJcbnZhciBldmVudEFkZCA9IGpRdWVyeS5ldmVudC5hZGQsXG5cdGV2ZW50UmVtb3ZlID0galF1ZXJ5LmV2ZW50LnJlbW92ZSxcblx0ZXZlbnRUcmlnZ2VyID0galF1ZXJ5LmV2ZW50LnRyaWdnZXIsXG5cdG9sZFRvZ2dsZSA9IGpRdWVyeS5mbi50b2dnbGUsXG5cdG9sZExpdmUgPSBqUXVlcnkuZm4ubGl2ZSxcblx0b2xkRGllID0galF1ZXJ5LmZuLmRpZSxcblx0b2xkTG9hZCA9IGpRdWVyeS5mbi5sb2FkLFxuXHRhamF4RXZlbnRzID0gXCJhamF4U3RhcnR8YWpheFN0b3B8YWpheFNlbmR8YWpheENvbXBsZXRlfGFqYXhFcnJvcnxhamF4U3VjY2Vzc1wiLFxuXHRyYWpheEV2ZW50ID0gbmV3IFJlZ0V4cCggXCJcXFxcYig/OlwiICsgYWpheEV2ZW50cyArIFwiKVxcXFxiXCIgKSxcblx0cmhvdmVySGFjayA9IC8oPzpefFxccylob3ZlcihcXC5cXFMrfClcXGIvLFxuXHRob3ZlckhhY2sgPSBmdW5jdGlvbiggZXZlbnRzICkge1xuXHRcdGlmICggdHlwZW9mKCBldmVudHMgKSAhPT0gXCJzdHJpbmdcIiB8fCBqUXVlcnkuZXZlbnQuc3BlY2lhbC5ob3ZlciApIHtcblx0XHRcdHJldHVybiBldmVudHM7XG5cdFx0fVxuXHRcdGlmICggcmhvdmVySGFjay50ZXN0KCBldmVudHMgKSApIHtcblx0XHRcdG1pZ3JhdGVXYXJuKFwiJ2hvdmVyJyBwc2V1ZG8tZXZlbnQgaXMgZGVwcmVjYXRlZCwgdXNlICdtb3VzZWVudGVyIG1vdXNlbGVhdmUnXCIpO1xuXHRcdH1cblx0XHRyZXR1cm4gZXZlbnRzICYmIGV2ZW50cy5yZXBsYWNlKCByaG92ZXJIYWNrLCBcIm1vdXNlZW50ZXIkMSBtb3VzZWxlYXZlJDFcIiApO1xuXHR9O1xuXG4vLyBFdmVudCBwcm9wcyByZW1vdmVkIGluIDEuOSwgcHV0IHRoZW0gYmFjayBpZiBuZWVkZWQ7IG5vIHByYWN0aWNhbCB3YXkgdG8gd2FybiB0aGVtXG5pZiAoIGpRdWVyeS5ldmVudC5wcm9wcyAmJiBqUXVlcnkuZXZlbnQucHJvcHNbIDAgXSAhPT0gXCJhdHRyQ2hhbmdlXCIgKSB7XG5cdGpRdWVyeS5ldmVudC5wcm9wcy51bnNoaWZ0KCBcImF0dHJDaGFuZ2VcIiwgXCJhdHRyTmFtZVwiLCBcInJlbGF0ZWROb2RlXCIsIFwic3JjRWxlbWVudFwiICk7XG59XG5cbi8vIFVuZG9jdW1lbnRlZCBqUXVlcnkuZXZlbnQuaGFuZGxlIHdhcyBcImRlcHJlY2F0ZWRcIiBpbiBqUXVlcnkgMS43XG5pZiAoIGpRdWVyeS5ldmVudC5kaXNwYXRjaCApIHtcblx0bWlncmF0ZVdhcm5Qcm9wKCBqUXVlcnkuZXZlbnQsIFwiaGFuZGxlXCIsIGpRdWVyeS5ldmVudC5kaXNwYXRjaCwgXCJqUXVlcnkuZXZlbnQuaGFuZGxlIGlzIHVuZG9jdW1lbnRlZCBhbmQgZGVwcmVjYXRlZFwiICk7XG59XG5cbi8vIFN1cHBvcnQgZm9yICdob3ZlcicgcHNldWRvLWV2ZW50IGFuZCBhamF4IGV2ZW50IHdhcm5pbmdzXG5qUXVlcnkuZXZlbnQuYWRkID0gZnVuY3Rpb24oIGVsZW0sIHR5cGVzLCBoYW5kbGVyLCBkYXRhLCBzZWxlY3RvciApe1xuXHRpZiAoIGVsZW0gIT09IGRvY3VtZW50ICYmIHJhamF4RXZlbnQudGVzdCggdHlwZXMgKSApIHtcblx0XHRtaWdyYXRlV2FybiggXCJBSkFYIGV2ZW50cyBzaG91bGQgYmUgYXR0YWNoZWQgdG8gZG9jdW1lbnQ6IFwiICsgdHlwZXMgKTtcblx0fVxuXHRldmVudEFkZC5jYWxsKCB0aGlzLCBlbGVtLCBob3ZlckhhY2soIHR5cGVzIHx8IFwiXCIgKSwgaGFuZGxlciwgZGF0YSwgc2VsZWN0b3IgKTtcbn07XG5qUXVlcnkuZXZlbnQucmVtb3ZlID0gZnVuY3Rpb24oIGVsZW0sIHR5cGVzLCBoYW5kbGVyLCBzZWxlY3RvciwgbWFwcGVkVHlwZXMgKXtcblx0ZXZlbnRSZW1vdmUuY2FsbCggdGhpcywgZWxlbSwgaG92ZXJIYWNrKCB0eXBlcyApIHx8IFwiXCIsIGhhbmRsZXIsIHNlbGVjdG9yLCBtYXBwZWRUeXBlcyApO1xufTtcblxualF1ZXJ5LmVhY2goIFsgXCJsb2FkXCIsIFwidW5sb2FkXCIsIFwiZXJyb3JcIiBdLCBmdW5jdGlvbiggXywgbmFtZSApIHtcblxuXHRqUXVlcnkuZm5bIG5hbWUgXSA9IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoIGFyZ3VtZW50cywgMCApO1xuXG5cdFx0Ly8gSWYgdGhpcyBpcyBhbiBhamF4IGxvYWQoKSB0aGUgZmlyc3QgYXJnIHNob3VsZCBiZSB0aGUgc3RyaW5nIFVSTDtcblx0XHQvLyB0ZWNobmljYWxseSB0aGlzIGNvdWxkIGFsc28gYmUgdGhlIFwiQW55dGhpbmdcIiBhcmcgb2YgdGhlIGV2ZW50IC5sb2FkKClcblx0XHQvLyB3aGljaCBqdXN0IGdvZXMgdG8gc2hvdyB3aHkgdGhpcyBkdW1iIHNpZ25hdHVyZSBoYXMgYmVlbiBkZXByZWNhdGVkIVxuXHRcdC8vIGpRdWVyeSBjdXN0b20gYnVpbGRzIHRoYXQgZXhjbHVkZSB0aGUgQWpheCBtb2R1bGUganVzdGlmaWFibHkgZGllIGhlcmUuXG5cdFx0aWYgKCBuYW1lID09PSBcImxvYWRcIiAmJiB0eXBlb2YgYXJnc1sgMCBdID09PSBcInN0cmluZ1wiICkge1xuXHRcdFx0cmV0dXJuIG9sZExvYWQuYXBwbHkoIHRoaXMsIGFyZ3MgKTtcblx0XHR9XG5cblx0XHRtaWdyYXRlV2FybiggXCJqUXVlcnkuZm4uXCIgKyBuYW1lICsgXCIoKSBpcyBkZXByZWNhdGVkXCIgKTtcblxuXHRcdGFyZ3Muc3BsaWNlKCAwLCAwLCBuYW1lICk7XG5cdFx0aWYgKCBhcmd1bWVudHMubGVuZ3RoICkge1xuXHRcdFx0cmV0dXJuIHRoaXMuYmluZC5hcHBseSggdGhpcywgYXJncyApO1xuXHRcdH1cblxuXHRcdC8vIFVzZSAudHJpZ2dlckhhbmRsZXIgaGVyZSBiZWNhdXNlOlxuXHRcdC8vIC0gbG9hZCBhbmQgdW5sb2FkIGV2ZW50cyBkb24ndCBuZWVkIHRvIGJ1YmJsZSwgb25seSBhcHBsaWVkIHRvIHdpbmRvdyBvciBpbWFnZVxuXHRcdC8vIC0gZXJyb3IgZXZlbnQgc2hvdWxkIG5vdCBidWJibGUgdG8gd2luZG93LCBhbHRob3VnaCBpdCBkb2VzIHByZS0xLjdcblx0XHQvLyBTZWUgaHR0cDovL2J1Z3MuanF1ZXJ5LmNvbS90aWNrZXQvMTE4MjBcblx0XHR0aGlzLnRyaWdnZXJIYW5kbGVyLmFwcGx5KCB0aGlzLCBhcmdzICk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH07XG5cbn0pO1xuXG5qUXVlcnkuZm4udG9nZ2xlID0gZnVuY3Rpb24oIGZuLCBmbjIgKSB7XG5cblx0Ly8gRG9uJ3QgbWVzcyB3aXRoIGFuaW1hdGlvbiBvciBjc3MgdG9nZ2xlc1xuXHRpZiAoICFqUXVlcnkuaXNGdW5jdGlvbiggZm4gKSB8fCAhalF1ZXJ5LmlzRnVuY3Rpb24oIGZuMiApICkge1xuXHRcdHJldHVybiBvbGRUb2dnbGUuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHR9XG5cdG1pZ3JhdGVXYXJuKFwialF1ZXJ5LmZuLnRvZ2dsZShoYW5kbGVyLCBoYW5kbGVyLi4uKSBpcyBkZXByZWNhdGVkXCIpO1xuXG5cdC8vIFNhdmUgcmVmZXJlbmNlIHRvIGFyZ3VtZW50cyBmb3IgYWNjZXNzIGluIGNsb3N1cmVcblx0dmFyIGFyZ3MgPSBhcmd1bWVudHMsXG5cdFx0Z3VpZCA9IGZuLmd1aWQgfHwgalF1ZXJ5Lmd1aWQrKyxcblx0XHRpID0gMCxcblx0XHR0b2dnbGVyID0gZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0Ly8gRmlndXJlIG91dCB3aGljaCBmdW5jdGlvbiB0byBleGVjdXRlXG5cdFx0XHR2YXIgbGFzdFRvZ2dsZSA9ICggalF1ZXJ5Ll9kYXRhKCB0aGlzLCBcImxhc3RUb2dnbGVcIiArIGZuLmd1aWQgKSB8fCAwICkgJSBpO1xuXHRcdFx0alF1ZXJ5Ll9kYXRhKCB0aGlzLCBcImxhc3RUb2dnbGVcIiArIGZuLmd1aWQsIGxhc3RUb2dnbGUgKyAxICk7XG5cblx0XHRcdC8vIE1ha2Ugc3VyZSB0aGF0IGNsaWNrcyBzdG9wXG5cdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXG5cdFx0XHQvLyBhbmQgZXhlY3V0ZSB0aGUgZnVuY3Rpb25cblx0XHRcdHJldHVybiBhcmdzWyBsYXN0VG9nZ2xlIF0uYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApIHx8IGZhbHNlO1xuXHRcdH07XG5cblx0Ly8gbGluayBhbGwgdGhlIGZ1bmN0aW9ucywgc28gYW55IG9mIHRoZW0gY2FuIHVuYmluZCB0aGlzIGNsaWNrIGhhbmRsZXJcblx0dG9nZ2xlci5ndWlkID0gZ3VpZDtcblx0d2hpbGUgKCBpIDwgYXJncy5sZW5ndGggKSB7XG5cdFx0YXJnc1sgaSsrIF0uZ3VpZCA9IGd1aWQ7XG5cdH1cblxuXHRyZXR1cm4gdGhpcy5jbGljayggdG9nZ2xlciApO1xufTtcblxualF1ZXJ5LmZuLmxpdmUgPSBmdW5jdGlvbiggdHlwZXMsIGRhdGEsIGZuICkge1xuXHRtaWdyYXRlV2FybihcImpRdWVyeS5mbi5saXZlKCkgaXMgZGVwcmVjYXRlZFwiKTtcblx0aWYgKCBvbGRMaXZlICkge1xuXHRcdHJldHVybiBvbGRMaXZlLmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcblx0fVxuXHRqUXVlcnkoIHRoaXMuY29udGV4dCApLm9uKCB0eXBlcywgdGhpcy5zZWxlY3RvciwgZGF0YSwgZm4gKTtcblx0cmV0dXJuIHRoaXM7XG59O1xuXG5qUXVlcnkuZm4uZGllID0gZnVuY3Rpb24oIHR5cGVzLCBmbiApIHtcblx0bWlncmF0ZVdhcm4oXCJqUXVlcnkuZm4uZGllKCkgaXMgZGVwcmVjYXRlZFwiKTtcblx0aWYgKCBvbGREaWUgKSB7XG5cdFx0cmV0dXJuIG9sZERpZS5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdH1cblx0alF1ZXJ5KCB0aGlzLmNvbnRleHQgKS5vZmYoIHR5cGVzLCB0aGlzLnNlbGVjdG9yIHx8IFwiKipcIiwgZm4gKTtcblx0cmV0dXJuIHRoaXM7XG59O1xuXG4vLyBUdXJuIGdsb2JhbCBldmVudHMgaW50byBkb2N1bWVudC10cmlnZ2VyZWQgZXZlbnRzXG5qUXVlcnkuZXZlbnQudHJpZ2dlciA9IGZ1bmN0aW9uKCBldmVudCwgZGF0YSwgZWxlbSwgb25seUhhbmRsZXJzICApe1xuXHRpZiAoICFlbGVtICYmICFyYWpheEV2ZW50LnRlc3QoIGV2ZW50ICkgKSB7XG5cdFx0bWlncmF0ZVdhcm4oIFwiR2xvYmFsIGV2ZW50cyBhcmUgdW5kb2N1bWVudGVkIGFuZCBkZXByZWNhdGVkXCIgKTtcblx0fVxuXHRyZXR1cm4gZXZlbnRUcmlnZ2VyLmNhbGwoIHRoaXMsICBldmVudCwgZGF0YSwgZWxlbSB8fCBkb2N1bWVudCwgb25seUhhbmRsZXJzICApO1xufTtcbmpRdWVyeS5lYWNoKCBhamF4RXZlbnRzLnNwbGl0KFwifFwiKSxcblx0ZnVuY3Rpb24oIF8sIG5hbWUgKSB7XG5cdFx0alF1ZXJ5LmV2ZW50LnNwZWNpYWxbIG5hbWUgXSA9IHtcblx0XHRcdHNldHVwOiBmdW5jdGlvbigpIHtcblx0XHRcdFx0dmFyIGVsZW0gPSB0aGlzO1xuXG5cdFx0XHRcdC8vIFRoZSBkb2N1bWVudCBuZWVkcyBubyBzaGltbWluZzsgbXVzdCBiZSAhPT0gZm9yIG9sZElFXG5cdFx0XHRcdGlmICggZWxlbSAhPT0gZG9jdW1lbnQgKSB7XG5cdFx0XHRcdFx0alF1ZXJ5LmV2ZW50LmFkZCggZG9jdW1lbnQsIG5hbWUgKyBcIi5cIiArIGpRdWVyeS5ndWlkLCBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHRcdGpRdWVyeS5ldmVudC50cmlnZ2VyKCBuYW1lLCBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCggYXJndW1lbnRzLCAxICksIGVsZW0sIHRydWUgKTtcblx0XHRcdFx0XHR9KTtcblx0XHRcdFx0XHRqUXVlcnkuX2RhdGEoIHRoaXMsIG5hbWUsIGpRdWVyeS5ndWlkKysgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0XHR9LFxuXHRcdFx0dGVhcmRvd246IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRpZiAoIHRoaXMgIT09IGRvY3VtZW50ICkge1xuXHRcdFx0XHRcdGpRdWVyeS5ldmVudC5yZW1vdmUoIGRvY3VtZW50LCBuYW1lICsgXCIuXCIgKyBqUXVlcnkuX2RhdGEoIHRoaXMsIG5hbWUgKSApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdH1cblx0XHR9O1xuXHR9XG4pO1xuXG5qUXVlcnkuZXZlbnQuc3BlY2lhbC5yZWFkeSA9IHtcblx0c2V0dXA6IGZ1bmN0aW9uKCkge1xuXHRcdGlmICggdGhpcyA9PT0gZG9jdW1lbnQgKSB7XG5cdFx0XHRtaWdyYXRlV2FybiggXCIncmVhZHknIGV2ZW50IGlzIGRlcHJlY2F0ZWRcIiApO1xuXHRcdH1cblx0fVxufTtcblxyXG52YXIgb2xkU2VsZiA9IGpRdWVyeS5mbi5hbmRTZWxmIHx8IGpRdWVyeS5mbi5hZGRCYWNrLFxuXHRvbGRGbkZpbmQgPSBqUXVlcnkuZm4uZmluZDtcblxualF1ZXJ5LmZuLmFuZFNlbGYgPSBmdW5jdGlvbigpIHtcblx0bWlncmF0ZVdhcm4oXCJqUXVlcnkuZm4uYW5kU2VsZigpIHJlcGxhY2VkIGJ5IGpRdWVyeS5mbi5hZGRCYWNrKClcIik7XG5cdHJldHVybiBvbGRTZWxmLmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcbn07XG5cbmpRdWVyeS5mbi5maW5kID0gZnVuY3Rpb24oIHNlbGVjdG9yICkge1xuXHR2YXIgcmV0ID0gb2xkRm5GaW5kLmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcblx0cmV0LmNvbnRleHQgPSB0aGlzLmNvbnRleHQ7XG5cdHJldC5zZWxlY3RvciA9IHRoaXMuc2VsZWN0b3IgPyB0aGlzLnNlbGVjdG9yICsgXCIgXCIgKyBzZWxlY3RvciA6IHNlbGVjdG9yO1xuXHRyZXR1cm4gcmV0O1xufTtcblxyXG5cbi8vIGpRdWVyeSAxLjYgZGlkIG5vdCBzdXBwb3J0IENhbGxiYWNrcywgZG8gbm90IHdhcm4gdGhlcmVcbmlmICggalF1ZXJ5LkNhbGxiYWNrcyApIHtcblxuXHR2YXIgb2xkRGVmZXJyZWQgPSBqUXVlcnkuRGVmZXJyZWQsXG5cdFx0dHVwbGVzID0gW1xuXHRcdFx0Ly8gYWN0aW9uLCBhZGQgbGlzdGVuZXIsIGNhbGxiYWNrcywgLnRoZW4gaGFuZGxlcnMsIGZpbmFsIHN0YXRlXG5cdFx0XHRbIFwicmVzb2x2ZVwiLCBcImRvbmVcIiwgalF1ZXJ5LkNhbGxiYWNrcyhcIm9uY2UgbWVtb3J5XCIpLFxuXHRcdFx0XHRqUXVlcnkuQ2FsbGJhY2tzKFwib25jZSBtZW1vcnlcIiksIFwicmVzb2x2ZWRcIiBdLFxuXHRcdFx0WyBcInJlamVjdFwiLCBcImZhaWxcIiwgalF1ZXJ5LkNhbGxiYWNrcyhcIm9uY2UgbWVtb3J5XCIpLFxuXHRcdFx0XHRqUXVlcnkuQ2FsbGJhY2tzKFwib25jZSBtZW1vcnlcIiksIFwicmVqZWN0ZWRcIiBdLFxuXHRcdFx0WyBcIm5vdGlmeVwiLCBcInByb2dyZXNzXCIsIGpRdWVyeS5DYWxsYmFja3MoXCJtZW1vcnlcIiksXG5cdFx0XHRcdGpRdWVyeS5DYWxsYmFja3MoXCJtZW1vcnlcIikgXVxuXHRcdF07XG5cblx0alF1ZXJ5LkRlZmVycmVkID0gZnVuY3Rpb24oIGZ1bmMgKSB7XG5cdFx0dmFyIGRlZmVycmVkID0gb2xkRGVmZXJyZWQoKSxcblx0XHRcdHByb21pc2UgPSBkZWZlcnJlZC5wcm9taXNlKCk7XG5cblx0XHRkZWZlcnJlZC5waXBlID0gcHJvbWlzZS5waXBlID0gZnVuY3Rpb24oIC8qIGZuRG9uZSwgZm5GYWlsLCBmblByb2dyZXNzICovICkge1xuXHRcdFx0dmFyIGZucyA9IGFyZ3VtZW50cztcblxuXHRcdFx0bWlncmF0ZVdhcm4oIFwiZGVmZXJyZWQucGlwZSgpIGlzIGRlcHJlY2F0ZWRcIiApO1xuXG5cdFx0XHRyZXR1cm4galF1ZXJ5LkRlZmVycmVkKGZ1bmN0aW9uKCBuZXdEZWZlciApIHtcblx0XHRcdFx0alF1ZXJ5LmVhY2goIHR1cGxlcywgZnVuY3Rpb24oIGksIHR1cGxlICkge1xuXHRcdFx0XHRcdHZhciBmbiA9IGpRdWVyeS5pc0Z1bmN0aW9uKCBmbnNbIGkgXSApICYmIGZuc1sgaSBdO1xuXHRcdFx0XHRcdC8vIGRlZmVycmVkLmRvbmUoZnVuY3Rpb24oKSB7IGJpbmQgdG8gbmV3RGVmZXIgb3IgbmV3RGVmZXIucmVzb2x2ZSB9KVxuXHRcdFx0XHRcdC8vIGRlZmVycmVkLmZhaWwoZnVuY3Rpb24oKSB7IGJpbmQgdG8gbmV3RGVmZXIgb3IgbmV3RGVmZXIucmVqZWN0IH0pXG5cdFx0XHRcdFx0Ly8gZGVmZXJyZWQucHJvZ3Jlc3MoZnVuY3Rpb24oKSB7IGJpbmQgdG8gbmV3RGVmZXIgb3IgbmV3RGVmZXIubm90aWZ5IH0pXG5cdFx0XHRcdFx0ZGVmZXJyZWRbIHR1cGxlWzFdIF0oZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0XHR2YXIgcmV0dXJuZWQgPSBmbiAmJiBmbi5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdFx0XHRcdFx0XHRpZiAoIHJldHVybmVkICYmIGpRdWVyeS5pc0Z1bmN0aW9uKCByZXR1cm5lZC5wcm9taXNlICkgKSB7XG5cdFx0XHRcdFx0XHRcdHJldHVybmVkLnByb21pc2UoKVxuXHRcdFx0XHRcdFx0XHRcdC5kb25lKCBuZXdEZWZlci5yZXNvbHZlIClcblx0XHRcdFx0XHRcdFx0XHQuZmFpbCggbmV3RGVmZXIucmVqZWN0IClcblx0XHRcdFx0XHRcdFx0XHQucHJvZ3Jlc3MoIG5ld0RlZmVyLm5vdGlmeSApO1xuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0bmV3RGVmZXJbIHR1cGxlWyAwIF0gKyBcIldpdGhcIiBdKFxuXHRcdFx0XHRcdFx0XHRcdHRoaXMgPT09IHByb21pc2UgPyBuZXdEZWZlci5wcm9taXNlKCkgOiB0aGlzLFxuXHRcdFx0XHRcdFx0XHRcdGZuID8gWyByZXR1cm5lZCBdIDogYXJndW1lbnRzXG5cdFx0XHRcdFx0XHRcdCk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fSk7XG5cdFx0XHRcdH0pO1xuXHRcdFx0XHRmbnMgPSBudWxsO1xuXHRcdFx0fSkucHJvbWlzZSgpO1xuXG5cdFx0fTtcblxuXHRcdGRlZmVycmVkLmlzUmVzb2x2ZWQgPSBmdW5jdGlvbigpIHtcblx0XHRcdG1pZ3JhdGVXYXJuKCBcImRlZmVycmVkLmlzUmVzb2x2ZWQgaXMgZGVwcmVjYXRlZFwiICk7XG5cdFx0XHRyZXR1cm4gZGVmZXJyZWQuc3RhdGUoKSA9PT0gXCJyZXNvbHZlZFwiO1xuXHRcdH07XG5cblx0XHRkZWZlcnJlZC5pc1JlamVjdGVkID0gZnVuY3Rpb24oKSB7XG5cdFx0XHRtaWdyYXRlV2FybiggXCJkZWZlcnJlZC5pc1JlamVjdGVkIGlzIGRlcHJlY2F0ZWRcIiApO1xuXHRcdFx0cmV0dXJuIGRlZmVycmVkLnN0YXRlKCkgPT09IFwicmVqZWN0ZWRcIjtcblx0XHR9O1xuXG5cdFx0aWYgKCBmdW5jICkge1xuXHRcdFx0ZnVuYy5jYWxsKCBkZWZlcnJlZCwgZGVmZXJyZWQgKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZGVmZXJyZWQ7XG5cdH07XG5cbn1cclxuXHJcbn0pKCBqUXVlcnksIHdpbmRvdyApO1xyXG4iLCIoZnVuY3Rpb24oKSB7XG4gIHZhciBBamF4TW9uaXRvciwgQmFyLCBEb2N1bWVudE1vbml0b3IsIEVsZW1lbnRNb25pdG9yLCBFbGVtZW50VHJhY2tlciwgRXZlbnRMYWdNb25pdG9yLCBFdmVudGVkLCBFdmVudHMsIE5vVGFyZ2V0RXJyb3IsIFBhY2UsIFJlcXVlc3RJbnRlcmNlcHQsIFNPVVJDRV9LRVlTLCBTY2FsZXIsIFNvY2tldFJlcXVlc3RUcmFja2VyLCBYSFJSZXF1ZXN0VHJhY2tlciwgYW5pbWF0aW9uLCBhdmdBbXBsaXR1ZGUsIGJhciwgY2FuY2VsQW5pbWF0aW9uLCBjYW5jZWxBbmltYXRpb25GcmFtZSwgZGVmYXVsdE9wdGlvbnMsIGV4dGVuZCwgZXh0ZW5kTmF0aXZlLCBnZXRGcm9tRE9NLCBnZXRJbnRlcmNlcHQsIGhhbmRsZVB1c2hTdGF0ZSwgaWdub3JlU3RhY2ssIGluaXQsIG5vdywgb3B0aW9ucywgcmVxdWVzdEFuaW1hdGlvbkZyYW1lLCByZXN1bHQsIHJ1bkFuaW1hdGlvbiwgc2NhbGVycywgc2hvdWxkSWdub3JlVVJMLCBzaG91bGRUcmFjaywgc291cmNlLCBzb3VyY2VzLCB1bmlTY2FsZXIsIF9XZWJTb2NrZXQsIF9YRG9tYWluUmVxdWVzdCwgX1hNTEh0dHBSZXF1ZXN0LCBfaSwgX2ludGVyY2VwdCwgX2xlbiwgX3B1c2hTdGF0ZSwgX3JlZiwgX3JlZjEsIF9yZXBsYWNlU3RhdGUsXG4gICAgX19zbGljZSA9IFtdLnNsaWNlLFxuICAgIF9faGFzUHJvcCA9IHt9Lmhhc093blByb3BlcnR5LFxuICAgIF9fZXh0ZW5kcyA9IGZ1bmN0aW9uKGNoaWxkLCBwYXJlbnQpIHsgZm9yICh2YXIga2V5IGluIHBhcmVudCkgeyBpZiAoX19oYXNQcm9wLmNhbGwocGFyZW50LCBrZXkpKSBjaGlsZFtrZXldID0gcGFyZW50W2tleV07IH0gZnVuY3Rpb24gY3RvcigpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGNoaWxkOyB9IGN0b3IucHJvdG90eXBlID0gcGFyZW50LnByb3RvdHlwZTsgY2hpbGQucHJvdG90eXBlID0gbmV3IGN0b3IoKTsgY2hpbGQuX19zdXBlcl9fID0gcGFyZW50LnByb3RvdHlwZTsgcmV0dXJuIGNoaWxkOyB9LFxuICAgIF9faW5kZXhPZiA9IFtdLmluZGV4T2YgfHwgZnVuY3Rpb24oaXRlbSkgeyBmb3IgKHZhciBpID0gMCwgbCA9IHRoaXMubGVuZ3RoOyBpIDwgbDsgaSsrKSB7IGlmIChpIGluIHRoaXMgJiYgdGhpc1tpXSA9PT0gaXRlbSkgcmV0dXJuIGk7IH0gcmV0dXJuIC0xOyB9O1xuXG4gIGRlZmF1bHRPcHRpb25zID0ge1xuICAgIGNhdGNodXBUaW1lOiAxMDAsXG4gICAgaW5pdGlhbFJhdGU6IC4wMyxcbiAgICBtaW5UaW1lOiAyNTAsXG4gICAgZ2hvc3RUaW1lOiAxMDAsXG4gICAgbWF4UHJvZ3Jlc3NQZXJGcmFtZTogMjAsXG4gICAgZWFzZUZhY3RvcjogMS4yNSxcbiAgICBzdGFydE9uUGFnZUxvYWQ6IHRydWUsXG4gICAgcmVzdGFydE9uUHVzaFN0YXRlOiB0cnVlLFxuICAgIHJlc3RhcnRPblJlcXVlc3RBZnRlcjogNTAwLFxuICAgIHRhcmdldDogJ2JvZHknLFxuICAgIGVsZW1lbnRzOiB7XG4gICAgICBjaGVja0ludGVydmFsOiAxMDAsXG4gICAgICBzZWxlY3RvcnM6IFsnYm9keSddXG4gICAgfSxcbiAgICBldmVudExhZzoge1xuICAgICAgbWluU2FtcGxlczogMTAsXG4gICAgICBzYW1wbGVDb3VudDogMyxcbiAgICAgIGxhZ1RocmVzaG9sZDogM1xuICAgIH0sXG4gICAgYWpheDoge1xuICAgICAgdHJhY2tNZXRob2RzOiBbJ0dFVCddLFxuICAgICAgdHJhY2tXZWJTb2NrZXRzOiB0cnVlLFxuICAgICAgaWdub3JlVVJMczogW11cbiAgICB9XG4gIH07XG5cbiAgbm93ID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIF9yZWY7XG4gICAgcmV0dXJuIChfcmVmID0gdHlwZW9mIHBlcmZvcm1hbmNlICE9PSBcInVuZGVmaW5lZFwiICYmIHBlcmZvcm1hbmNlICE9PSBudWxsID8gdHlwZW9mIHBlcmZvcm1hbmNlLm5vdyA9PT0gXCJmdW5jdGlvblwiID8gcGVyZm9ybWFuY2Uubm93KCkgOiB2b2lkIDAgOiB2b2lkIDApICE9IG51bGwgPyBfcmVmIDogKyhuZXcgRGF0ZSk7XG4gIH07XG5cbiAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lID0gd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSB8fCB3aW5kb3cubW96UmVxdWVzdEFuaW1hdGlvbkZyYW1lIHx8IHdpbmRvdy53ZWJraXRSZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHwgd2luZG93Lm1zUmVxdWVzdEFuaW1hdGlvbkZyYW1lO1xuXG4gIGNhbmNlbEFuaW1hdGlvbkZyYW1lID0gd2luZG93LmNhbmNlbEFuaW1hdGlvbkZyYW1lIHx8IHdpbmRvdy5tb3pDYW5jZWxBbmltYXRpb25GcmFtZTtcblxuICBpZiAocmVxdWVzdEFuaW1hdGlvbkZyYW1lID09IG51bGwpIHtcbiAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUgPSBmdW5jdGlvbihmbikge1xuICAgICAgcmV0dXJuIHNldFRpbWVvdXQoZm4sIDUwKTtcbiAgICB9O1xuICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lID0gZnVuY3Rpb24oaWQpIHtcbiAgICAgIHJldHVybiBjbGVhclRpbWVvdXQoaWQpO1xuICAgIH07XG4gIH1cblxuICBydW5BbmltYXRpb24gPSBmdW5jdGlvbihmbikge1xuICAgIHZhciBsYXN0LCB0aWNrO1xuICAgIGxhc3QgPSBub3coKTtcbiAgICB0aWNrID0gZnVuY3Rpb24oKSB7XG4gICAgICB2YXIgZGlmZjtcbiAgICAgIGRpZmYgPSBub3coKSAtIGxhc3Q7XG4gICAgICBpZiAoZGlmZiA+PSAzMykge1xuICAgICAgICBsYXN0ID0gbm93KCk7XG4gICAgICAgIHJldHVybiBmbihkaWZmLCBmdW5jdGlvbigpIHtcbiAgICAgICAgICByZXR1cm4gcmVxdWVzdEFuaW1hdGlvbkZyYW1lKHRpY2spO1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBzZXRUaW1lb3V0KHRpY2ssIDMzIC0gZGlmZik7XG4gICAgICB9XG4gICAgfTtcbiAgICByZXR1cm4gdGljaygpO1xuICB9O1xuXG4gIHJlc3VsdCA9IGZ1bmN0aW9uKCkge1xuICAgIHZhciBhcmdzLCBrZXksIG9iajtcbiAgICBvYmogPSBhcmd1bWVudHNbMF0sIGtleSA9IGFyZ3VtZW50c1sxXSwgYXJncyA9IDMgPD0gYXJndW1lbnRzLmxlbmd0aCA/IF9fc2xpY2UuY2FsbChhcmd1bWVudHMsIDIpIDogW107XG4gICAgaWYgKHR5cGVvZiBvYmpba2V5XSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgcmV0dXJuIG9ialtrZXldLmFwcGx5KG9iaiwgYXJncyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBvYmpba2V5XTtcbiAgICB9XG4gIH07XG5cbiAgZXh0ZW5kID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGtleSwgb3V0LCBzb3VyY2UsIHNvdXJjZXMsIHZhbCwgX2ksIF9sZW47XG4gICAgb3V0ID0gYXJndW1lbnRzWzBdLCBzb3VyY2VzID0gMiA8PSBhcmd1bWVudHMubGVuZ3RoID8gX19zbGljZS5jYWxsKGFyZ3VtZW50cywgMSkgOiBbXTtcbiAgICBmb3IgKF9pID0gMCwgX2xlbiA9IHNvdXJjZXMubGVuZ3RoOyBfaSA8IF9sZW47IF9pKyspIHtcbiAgICAgIHNvdXJjZSA9IHNvdXJjZXNbX2ldO1xuICAgICAgaWYgKHNvdXJjZSkge1xuICAgICAgICBmb3IgKGtleSBpbiBzb3VyY2UpIHtcbiAgICAgICAgICBpZiAoIV9faGFzUHJvcC5jYWxsKHNvdXJjZSwga2V5KSkgY29udGludWU7XG4gICAgICAgICAgdmFsID0gc291cmNlW2tleV07XG4gICAgICAgICAgaWYgKChvdXRba2V5XSAhPSBudWxsKSAmJiB0eXBlb2Ygb3V0W2tleV0gPT09ICdvYmplY3QnICYmICh2YWwgIT0gbnVsbCkgJiYgdHlwZW9mIHZhbCA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIGV4dGVuZChvdXRba2V5XSwgdmFsKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgb3V0W2tleV0gPSB2YWw7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBvdXQ7XG4gIH07XG5cbiAgYXZnQW1wbGl0dWRlID0gZnVuY3Rpb24oYXJyKSB7XG4gICAgdmFyIGNvdW50LCBzdW0sIHYsIF9pLCBfbGVuO1xuICAgIHN1bSA9IGNvdW50ID0gMDtcbiAgICBmb3IgKF9pID0gMCwgX2xlbiA9IGFyci5sZW5ndGg7IF9pIDwgX2xlbjsgX2krKykge1xuICAgICAgdiA9IGFycltfaV07XG4gICAgICBzdW0gKz0gTWF0aC5hYnModik7XG4gICAgICBjb3VudCsrO1xuICAgIH1cbiAgICByZXR1cm4gc3VtIC8gY291bnQ7XG4gIH07XG5cbiAgZ2V0RnJvbURPTSA9IGZ1bmN0aW9uKGtleSwganNvbikge1xuICAgIHZhciBkYXRhLCBlLCBlbDtcbiAgICBpZiAoa2V5ID09IG51bGwpIHtcbiAgICAgIGtleSA9ICdvcHRpb25zJztcbiAgICB9XG4gICAgaWYgKGpzb24gPT0gbnVsbCkge1xuICAgICAganNvbiA9IHRydWU7XG4gICAgfVxuICAgIGVsID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcIltkYXRhLXBhY2UtXCIgKyBrZXkgKyBcIl1cIik7XG4gICAgaWYgKCFlbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBkYXRhID0gZWwuZ2V0QXR0cmlidXRlKFwiZGF0YS1wYWNlLVwiICsga2V5KTtcbiAgICBpZiAoIWpzb24pIHtcbiAgICAgIHJldHVybiBkYXRhO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgcmV0dXJuIEpTT04ucGFyc2UoZGF0YSk7XG4gICAgfSBjYXRjaCAoX2Vycm9yKSB7XG4gICAgICBlID0gX2Vycm9yO1xuICAgICAgcmV0dXJuIHR5cGVvZiBjb25zb2xlICE9PSBcInVuZGVmaW5lZFwiICYmIGNvbnNvbGUgIT09IG51bGwgPyBjb25zb2xlLmVycm9yKFwiRXJyb3IgcGFyc2luZyBpbmxpbmUgcGFjZSBvcHRpb25zXCIsIGUpIDogdm9pZCAwO1xuICAgIH1cbiAgfTtcblxuICBFdmVudGVkID0gKGZ1bmN0aW9uKCkge1xuICAgIGZ1bmN0aW9uIEV2ZW50ZWQoKSB7fVxuXG4gICAgRXZlbnRlZC5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldmVudCwgaGFuZGxlciwgY3R4LCBvbmNlKSB7XG4gICAgICB2YXIgX2Jhc2U7XG4gICAgICBpZiAob25jZSA9PSBudWxsKSB7XG4gICAgICAgIG9uY2UgPSBmYWxzZTtcbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLmJpbmRpbmdzID09IG51bGwpIHtcbiAgICAgICAgdGhpcy5iaW5kaW5ncyA9IHt9O1xuICAgICAgfVxuICAgICAgaWYgKChfYmFzZSA9IHRoaXMuYmluZGluZ3MpW2V2ZW50XSA9PSBudWxsKSB7XG4gICAgICAgIF9iYXNlW2V2ZW50XSA9IFtdO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuYmluZGluZ3NbZXZlbnRdLnB1c2goe1xuICAgICAgICBoYW5kbGVyOiBoYW5kbGVyLFxuICAgICAgICBjdHg6IGN0eCxcbiAgICAgICAgb25jZTogb25jZVxuICAgICAgfSk7XG4gICAgfTtcblxuICAgIEV2ZW50ZWQucHJvdG90eXBlLm9uY2UgPSBmdW5jdGlvbihldmVudCwgaGFuZGxlciwgY3R4KSB7XG4gICAgICByZXR1cm4gdGhpcy5vbihldmVudCwgaGFuZGxlciwgY3R4LCB0cnVlKTtcbiAgICB9O1xuXG4gICAgRXZlbnRlZC5wcm90b3R5cGUub2ZmID0gZnVuY3Rpb24oZXZlbnQsIGhhbmRsZXIpIHtcbiAgICAgIHZhciBpLCBfcmVmLCBfcmVzdWx0cztcbiAgICAgIGlmICgoKF9yZWYgPSB0aGlzLmJpbmRpbmdzKSAhPSBudWxsID8gX3JlZltldmVudF0gOiB2b2lkIDApID09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKGhhbmRsZXIgPT0gbnVsbCkge1xuICAgICAgICByZXR1cm4gZGVsZXRlIHRoaXMuYmluZGluZ3NbZXZlbnRdO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaSA9IDA7XG4gICAgICAgIF9yZXN1bHRzID0gW107XG4gICAgICAgIHdoaWxlIChpIDwgdGhpcy5iaW5kaW5nc1tldmVudF0ubGVuZ3RoKSB7XG4gICAgICAgICAgaWYgKHRoaXMuYmluZGluZ3NbZXZlbnRdW2ldLmhhbmRsZXIgPT09IGhhbmRsZXIpIHtcbiAgICAgICAgICAgIF9yZXN1bHRzLnB1c2godGhpcy5iaW5kaW5nc1tldmVudF0uc3BsaWNlKGksIDEpKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgX3Jlc3VsdHMucHVzaChpKyspO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gX3Jlc3VsdHM7XG4gICAgICB9XG4gICAgfTtcblxuICAgIEV2ZW50ZWQucHJvdG90eXBlLnRyaWdnZXIgPSBmdW5jdGlvbigpIHtcbiAgICAgIHZhciBhcmdzLCBjdHgsIGV2ZW50LCBoYW5kbGVyLCBpLCBvbmNlLCBfcmVmLCBfcmVmMSwgX3Jlc3VsdHM7XG4gICAgICBldmVudCA9IGFyZ3VtZW50c1swXSwgYXJncyA9IDIgPD0gYXJndW1lbnRzLmxlbmd0aCA/IF9fc2xpY2UuY2FsbChhcmd1bWVudHMsIDEpIDogW107XG4gICAgICBpZiAoKF9yZWYgPSB0aGlzLmJpbmRpbmdzKSAhPSBudWxsID8gX3JlZltldmVudF0gOiB2b2lkIDApIHtcbiAgICAgICAgaSA9IDA7XG4gICAgICAgIF9yZXN1bHRzID0gW107XG4gICAgICAgIHdoaWxlIChpIDwgdGhpcy5iaW5kaW5nc1tldmVudF0ubGVuZ3RoKSB7XG4gICAgICAgICAgX3JlZjEgPSB0aGlzLmJpbmRpbmdzW2V2ZW50XVtpXSwgaGFuZGxlciA9IF9yZWYxLmhhbmRsZXIsIGN0eCA9IF9yZWYxLmN0eCwgb25jZSA9IF9yZWYxLm9uY2U7XG4gICAgICAgICAgaGFuZGxlci5hcHBseShjdHggIT0gbnVsbCA/IGN0eCA6IHRoaXMsIGFyZ3MpO1xuICAgICAgICAgIGlmIChvbmNlKSB7XG4gICAgICAgICAgICBfcmVzdWx0cy5wdXNoKHRoaXMuYmluZGluZ3NbZXZlbnRdLnNwbGljZShpLCAxKSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIF9yZXN1bHRzLnB1c2goaSsrKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIF9yZXN1bHRzO1xuICAgICAgfVxuICAgIH07XG5cbiAgICByZXR1cm4gRXZlbnRlZDtcblxuICB9KSgpO1xuXG4gIFBhY2UgPSB3aW5kb3cuUGFjZSB8fCB7fTtcblxuICB3aW5kb3cuUGFjZSA9IFBhY2U7XG5cbiAgZXh0ZW5kKFBhY2UsIEV2ZW50ZWQucHJvdG90eXBlKTtcblxuICBvcHRpb25zID0gUGFjZS5vcHRpb25zID0gZXh0ZW5kKHt9LCBkZWZhdWx0T3B0aW9ucywgd2luZG93LnBhY2VPcHRpb25zLCBnZXRGcm9tRE9NKCkpO1xuXG4gIF9yZWYgPSBbJ2FqYXgnLCAnZG9jdW1lbnQnLCAnZXZlbnRMYWcnLCAnZWxlbWVudHMnXTtcbiAgZm9yIChfaSA9IDAsIF9sZW4gPSBfcmVmLmxlbmd0aDsgX2kgPCBfbGVuOyBfaSsrKSB7XG4gICAgc291cmNlID0gX3JlZltfaV07XG4gICAgaWYgKG9wdGlvbnNbc291cmNlXSA9PT0gdHJ1ZSkge1xuICAgICAgb3B0aW9uc1tzb3VyY2VdID0gZGVmYXVsdE9wdGlvbnNbc291cmNlXTtcbiAgICB9XG4gIH1cblxuICBOb1RhcmdldEVycm9yID0gKGZ1bmN0aW9uKF9zdXBlcikge1xuICAgIF9fZXh0ZW5kcyhOb1RhcmdldEVycm9yLCBfc3VwZXIpO1xuXG4gICAgZnVuY3Rpb24gTm9UYXJnZXRFcnJvcigpIHtcbiAgICAgIF9yZWYxID0gTm9UYXJnZXRFcnJvci5fX3N1cGVyX18uY29uc3RydWN0b3IuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgIHJldHVybiBfcmVmMTtcbiAgICB9XG5cbiAgICByZXR1cm4gTm9UYXJnZXRFcnJvcjtcblxuICB9KShFcnJvcik7XG5cbiAgQmFyID0gKGZ1bmN0aW9uKCkge1xuICAgIGZ1bmN0aW9uIEJhcigpIHtcbiAgICAgIHRoaXMucHJvZ3Jlc3MgPSAwO1xuICAgIH1cblxuICAgIEJhci5wcm90b3R5cGUuZ2V0RWxlbWVudCA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIHRhcmdldEVsZW1lbnQ7XG4gICAgICBpZiAodGhpcy5lbCA9PSBudWxsKSB7XG4gICAgICAgIHRhcmdldEVsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKG9wdGlvbnMudGFyZ2V0KTtcbiAgICAgICAgaWYgKCF0YXJnZXRFbGVtZW50KSB7XG4gICAgICAgICAgdGhyb3cgbmV3IE5vVGFyZ2V0RXJyb3I7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5lbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgICAgICB0aGlzLmVsLmNsYXNzTmFtZSA9IFwicGFjZSBwYWNlLWFjdGl2ZVwiO1xuICAgICAgICBkb2N1bWVudC5ib2R5LmNsYXNzTmFtZSA9IGRvY3VtZW50LmJvZHkuY2xhc3NOYW1lLnJlcGxhY2UoL3BhY2UtZG9uZS9nLCAnJyk7XG4gICAgICAgIGRvY3VtZW50LmJvZHkuY2xhc3NOYW1lICs9ICcgcGFjZS1ydW5uaW5nJztcbiAgICAgICAgdGhpcy5lbC5pbm5lckhUTUwgPSAnPGRpdiBjbGFzcz1cInBhY2UtcHJvZ3Jlc3NcIj5cXG4gIDxkaXYgY2xhc3M9XCJwYWNlLXByb2dyZXNzLWlubmVyXCI+PC9kaXY+XFxuPC9kaXY+XFxuPGRpdiBjbGFzcz1cInBhY2UtYWN0aXZpdHlcIj48L2Rpdj4nO1xuICAgICAgICBpZiAodGFyZ2V0RWxlbWVudC5maXJzdENoaWxkICE9IG51bGwpIHtcbiAgICAgICAgICB0YXJnZXRFbGVtZW50Lmluc2VydEJlZm9yZSh0aGlzLmVsLCB0YXJnZXRFbGVtZW50LmZpcnN0Q2hpbGQpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRhcmdldEVsZW1lbnQuYXBwZW5kQ2hpbGQodGhpcy5lbCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLmVsO1xuICAgIH07XG5cbiAgICBCYXIucHJvdG90eXBlLmZpbmlzaCA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGVsO1xuICAgICAgZWwgPSB0aGlzLmdldEVsZW1lbnQoKTtcbiAgICAgIGVsLmNsYXNzTmFtZSA9IGVsLmNsYXNzTmFtZS5yZXBsYWNlKCdwYWNlLWFjdGl2ZScsICcnKTtcbiAgICAgIGVsLmNsYXNzTmFtZSArPSAnIHBhY2UtaW5hY3RpdmUnO1xuICAgICAgZG9jdW1lbnQuYm9keS5jbGFzc05hbWUgPSBkb2N1bWVudC5ib2R5LmNsYXNzTmFtZS5yZXBsYWNlKCdwYWNlLXJ1bm5pbmcnLCAnJyk7XG4gICAgICByZXR1cm4gZG9jdW1lbnQuYm9keS5jbGFzc05hbWUgKz0gJyBwYWNlLWRvbmUnO1xuICAgIH07XG5cbiAgICBCYXIucHJvdG90eXBlLnVwZGF0ZSA9IGZ1bmN0aW9uKHByb2cpIHtcbiAgICAgIHRoaXMucHJvZ3Jlc3MgPSBwcm9nO1xuICAgICAgcmV0dXJuIHRoaXMucmVuZGVyKCk7XG4gICAgfTtcblxuICAgIEJhci5wcm90b3R5cGUuZGVzdHJveSA9IGZ1bmN0aW9uKCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgdGhpcy5nZXRFbGVtZW50KCkucGFyZW50Tm9kZS5yZW1vdmVDaGlsZCh0aGlzLmdldEVsZW1lbnQoKSk7XG4gICAgICB9IGNhdGNoIChfZXJyb3IpIHtcbiAgICAgICAgTm9UYXJnZXRFcnJvciA9IF9lcnJvcjtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLmVsID0gdm9pZCAwO1xuICAgIH07XG5cbiAgICBCYXIucHJvdG90eXBlLnJlbmRlciA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIGVsLCBrZXksIHByb2dyZXNzU3RyLCB0cmFuc2Zvcm0sIF9qLCBfbGVuMSwgX3JlZjI7XG4gICAgICBpZiAoZG9jdW1lbnQucXVlcnlTZWxlY3RvcihvcHRpb25zLnRhcmdldCkgPT0gbnVsbCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICBlbCA9IHRoaXMuZ2V0RWxlbWVudCgpO1xuICAgICAgdHJhbnNmb3JtID0gXCJ0cmFuc2xhdGUzZChcIiArIHRoaXMucHJvZ3Jlc3MgKyBcIiUsIDAsIDApXCI7XG4gICAgICBfcmVmMiA9IFsnd2Via2l0VHJhbnNmb3JtJywgJ21zVHJhbnNmb3JtJywgJ3RyYW5zZm9ybSddO1xuICAgICAgZm9yIChfaiA9IDAsIF9sZW4xID0gX3JlZjIubGVuZ3RoOyBfaiA8IF9sZW4xOyBfaisrKSB7XG4gICAgICAgIGtleSA9IF9yZWYyW19qXTtcbiAgICAgICAgZWwuY2hpbGRyZW5bMF0uc3R5bGVba2V5XSA9IHRyYW5zZm9ybTtcbiAgICAgIH1cbiAgICAgIGlmICghdGhpcy5sYXN0UmVuZGVyZWRQcm9ncmVzcyB8fCB0aGlzLmxhc3RSZW5kZXJlZFByb2dyZXNzIHwgMCAhPT0gdGhpcy5wcm9ncmVzcyB8IDApIHtcbiAgICAgICAgZWwuY2hpbGRyZW5bMF0uc2V0QXR0cmlidXRlKCdkYXRhLXByb2dyZXNzLXRleHQnLCBcIlwiICsgKHRoaXMucHJvZ3Jlc3MgfCAwKSArIFwiJVwiKTtcbiAgICAgICAgaWYgKHRoaXMucHJvZ3Jlc3MgPj0gMTAwKSB7XG4gICAgICAgICAgcHJvZ3Jlc3NTdHIgPSAnOTknO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHByb2dyZXNzU3RyID0gdGhpcy5wcm9ncmVzcyA8IDEwID8gXCIwXCIgOiBcIlwiO1xuICAgICAgICAgIHByb2dyZXNzU3RyICs9IHRoaXMucHJvZ3Jlc3MgfCAwO1xuICAgICAgICB9XG4gICAgICAgIGVsLmNoaWxkcmVuWzBdLnNldEF0dHJpYnV0ZSgnZGF0YS1wcm9ncmVzcycsIFwiXCIgKyBwcm9ncmVzc1N0cik7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5sYXN0UmVuZGVyZWRQcm9ncmVzcyA9IHRoaXMucHJvZ3Jlc3M7XG4gICAgfTtcblxuICAgIEJhci5wcm90b3R5cGUuZG9uZSA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMucHJvZ3Jlc3MgPj0gMTAwO1xuICAgIH07XG5cbiAgICByZXR1cm4gQmFyO1xuXG4gIH0pKCk7XG5cbiAgRXZlbnRzID0gKGZ1bmN0aW9uKCkge1xuICAgIGZ1bmN0aW9uIEV2ZW50cygpIHtcbiAgICAgIHRoaXMuYmluZGluZ3MgPSB7fTtcbiAgICB9XG5cbiAgICBFdmVudHMucHJvdG90eXBlLnRyaWdnZXIgPSBmdW5jdGlvbihuYW1lLCB2YWwpIHtcbiAgICAgIHZhciBiaW5kaW5nLCBfaiwgX2xlbjEsIF9yZWYyLCBfcmVzdWx0cztcbiAgICAgIGlmICh0aGlzLmJpbmRpbmdzW25hbWVdICE9IG51bGwpIHtcbiAgICAgICAgX3JlZjIgPSB0aGlzLmJpbmRpbmdzW25hbWVdO1xuICAgICAgICBfcmVzdWx0cyA9IFtdO1xuICAgICAgICBmb3IgKF9qID0gMCwgX2xlbjEgPSBfcmVmMi5sZW5ndGg7IF9qIDwgX2xlbjE7IF9qKyspIHtcbiAgICAgICAgICBiaW5kaW5nID0gX3JlZjJbX2pdO1xuICAgICAgICAgIF9yZXN1bHRzLnB1c2goYmluZGluZy5jYWxsKHRoaXMsIHZhbCkpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBfcmVzdWx0cztcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgRXZlbnRzLnByb3RvdHlwZS5vbiA9IGZ1bmN0aW9uKG5hbWUsIGZuKSB7XG4gICAgICB2YXIgX2Jhc2U7XG4gICAgICBpZiAoKF9iYXNlID0gdGhpcy5iaW5kaW5ncylbbmFtZV0gPT0gbnVsbCkge1xuICAgICAgICBfYmFzZVtuYW1lXSA9IFtdO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuYmluZGluZ3NbbmFtZV0ucHVzaChmbik7XG4gICAgfTtcblxuICAgIHJldHVybiBFdmVudHM7XG5cbiAgfSkoKTtcblxuICBfWE1MSHR0cFJlcXVlc3QgPSB3aW5kb3cuWE1MSHR0cFJlcXVlc3Q7XG5cbiAgX1hEb21haW5SZXF1ZXN0ID0gd2luZG93LlhEb21haW5SZXF1ZXN0O1xuXG4gIF9XZWJTb2NrZXQgPSB3aW5kb3cuV2ViU29ja2V0O1xuXG4gIGV4dGVuZE5hdGl2ZSA9IGZ1bmN0aW9uKHRvLCBmcm9tKSB7XG4gICAgdmFyIGUsIGtleSwgX3Jlc3VsdHM7XG4gICAgX3Jlc3VsdHMgPSBbXTtcbiAgICBmb3IgKGtleSBpbiBmcm9tLnByb3RvdHlwZSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgaWYgKCh0b1trZXldID09IG51bGwpICYmIHR5cGVvZiBmcm9tW2tleV0gIT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICBpZiAodHlwZW9mIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgX3Jlc3VsdHMucHVzaChPYmplY3QuZGVmaW5lUHJvcGVydHkodG8sIGtleSwge1xuICAgICAgICAgICAgICBnZXQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBmcm9tLnByb3RvdHlwZVtrZXldO1xuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgICAgICAgICAgIGVudW1lcmFibGU6IHRydWVcbiAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgX3Jlc3VsdHMucHVzaCh0b1trZXldID0gZnJvbS5wcm90b3R5cGVba2V5XSk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIF9yZXN1bHRzLnB1c2godm9pZCAwKTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoX2Vycm9yKSB7XG4gICAgICAgIGUgPSBfZXJyb3I7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBfcmVzdWx0cztcbiAgfTtcblxuICBpZ25vcmVTdGFjayA9IFtdO1xuXG4gIFBhY2UuaWdub3JlID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGFyZ3MsIGZuLCByZXQ7XG4gICAgZm4gPSBhcmd1bWVudHNbMF0sIGFyZ3MgPSAyIDw9IGFyZ3VtZW50cy5sZW5ndGggPyBfX3NsaWNlLmNhbGwoYXJndW1lbnRzLCAxKSA6IFtdO1xuICAgIGlnbm9yZVN0YWNrLnVuc2hpZnQoJ2lnbm9yZScpO1xuICAgIHJldCA9IGZuLmFwcGx5KG51bGwsIGFyZ3MpO1xuICAgIGlnbm9yZVN0YWNrLnNoaWZ0KCk7XG4gICAgcmV0dXJuIHJldDtcbiAgfTtcblxuICBQYWNlLnRyYWNrID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGFyZ3MsIGZuLCByZXQ7XG4gICAgZm4gPSBhcmd1bWVudHNbMF0sIGFyZ3MgPSAyIDw9IGFyZ3VtZW50cy5sZW5ndGggPyBfX3NsaWNlLmNhbGwoYXJndW1lbnRzLCAxKSA6IFtdO1xuICAgIGlnbm9yZVN0YWNrLnVuc2hpZnQoJ3RyYWNrJyk7XG4gICAgcmV0ID0gZm4uYXBwbHkobnVsbCwgYXJncyk7XG4gICAgaWdub3JlU3RhY2suc2hpZnQoKTtcbiAgICByZXR1cm4gcmV0O1xuICB9O1xuXG4gIHNob3VsZFRyYWNrID0gZnVuY3Rpb24obWV0aG9kKSB7XG4gICAgdmFyIF9yZWYyO1xuICAgIGlmIChtZXRob2QgPT0gbnVsbCkge1xuICAgICAgbWV0aG9kID0gJ0dFVCc7XG4gICAgfVxuICAgIGlmIChpZ25vcmVTdGFja1swXSA9PT0gJ3RyYWNrJykge1xuICAgICAgcmV0dXJuICdmb3JjZSc7XG4gICAgfVxuICAgIGlmICghaWdub3JlU3RhY2subGVuZ3RoICYmIG9wdGlvbnMuYWpheCkge1xuICAgICAgaWYgKG1ldGhvZCA9PT0gJ3NvY2tldCcgJiYgb3B0aW9ucy5hamF4LnRyYWNrV2ViU29ja2V0cykge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH0gZWxzZSBpZiAoX3JlZjIgPSBtZXRob2QudG9VcHBlckNhc2UoKSwgX19pbmRleE9mLmNhbGwob3B0aW9ucy5hamF4LnRyYWNrTWV0aG9kcywgX3JlZjIpID49IDApIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfTtcblxuICBSZXF1ZXN0SW50ZXJjZXB0ID0gKGZ1bmN0aW9uKF9zdXBlcikge1xuICAgIF9fZXh0ZW5kcyhSZXF1ZXN0SW50ZXJjZXB0LCBfc3VwZXIpO1xuXG4gICAgZnVuY3Rpb24gUmVxdWVzdEludGVyY2VwdCgpIHtcbiAgICAgIHZhciBtb25pdG9yWEhSLFxuICAgICAgICBfdGhpcyA9IHRoaXM7XG4gICAgICBSZXF1ZXN0SW50ZXJjZXB0Ll9fc3VwZXJfXy5jb25zdHJ1Y3Rvci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgbW9uaXRvclhIUiA9IGZ1bmN0aW9uKHJlcSkge1xuICAgICAgICB2YXIgX29wZW47XG4gICAgICAgIF9vcGVuID0gcmVxLm9wZW47XG4gICAgICAgIHJldHVybiByZXEub3BlbiA9IGZ1bmN0aW9uKHR5cGUsIHVybCwgYXN5bmMpIHtcbiAgICAgICAgICBpZiAoc2hvdWxkVHJhY2sodHlwZSkpIHtcbiAgICAgICAgICAgIF90aGlzLnRyaWdnZXIoJ3JlcXVlc3QnLCB7XG4gICAgICAgICAgICAgIHR5cGU6IHR5cGUsXG4gICAgICAgICAgICAgIHVybDogdXJsLFxuICAgICAgICAgICAgICByZXF1ZXN0OiByZXFcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gX29wZW4uYXBwbHkocmVxLCBhcmd1bWVudHMpO1xuICAgICAgICB9O1xuICAgICAgfTtcbiAgICAgIHdpbmRvdy5YTUxIdHRwUmVxdWVzdCA9IGZ1bmN0aW9uKGZsYWdzKSB7XG4gICAgICAgIHZhciByZXE7XG4gICAgICAgIHJlcSA9IG5ldyBfWE1MSHR0cFJlcXVlc3QoZmxhZ3MpO1xuICAgICAgICBtb25pdG9yWEhSKHJlcSk7XG4gICAgICAgIHJldHVybiByZXE7XG4gICAgICB9O1xuICAgICAgdHJ5IHtcbiAgICAgICAgZXh0ZW5kTmF0aXZlKHdpbmRvdy5YTUxIdHRwUmVxdWVzdCwgX1hNTEh0dHBSZXF1ZXN0KTtcbiAgICAgIH0gY2F0Y2ggKF9lcnJvcikge31cbiAgICAgIGlmIChfWERvbWFpblJlcXVlc3QgIT0gbnVsbCkge1xuICAgICAgICB3aW5kb3cuWERvbWFpblJlcXVlc3QgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICB2YXIgcmVxO1xuICAgICAgICAgIHJlcSA9IG5ldyBfWERvbWFpblJlcXVlc3Q7XG4gICAgICAgICAgbW9uaXRvclhIUihyZXEpO1xuICAgICAgICAgIHJldHVybiByZXE7XG4gICAgICAgIH07XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgZXh0ZW5kTmF0aXZlKHdpbmRvdy5YRG9tYWluUmVxdWVzdCwgX1hEb21haW5SZXF1ZXN0KTtcbiAgICAgICAgfSBjYXRjaCAoX2Vycm9yKSB7fVxuICAgICAgfVxuICAgICAgaWYgKChfV2ViU29ja2V0ICE9IG51bGwpICYmIG9wdGlvbnMuYWpheC50cmFja1dlYlNvY2tldHMpIHtcbiAgICAgICAgd2luZG93LldlYlNvY2tldCA9IGZ1bmN0aW9uKHVybCwgcHJvdG9jb2xzKSB7XG4gICAgICAgICAgdmFyIHJlcTtcbiAgICAgICAgICBpZiAocHJvdG9jb2xzICE9IG51bGwpIHtcbiAgICAgICAgICAgIHJlcSA9IG5ldyBfV2ViU29ja2V0KHVybCwgcHJvdG9jb2xzKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmVxID0gbmV3IF9XZWJTb2NrZXQodXJsKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHNob3VsZFRyYWNrKCdzb2NrZXQnKSkge1xuICAgICAgICAgICAgX3RoaXMudHJpZ2dlcigncmVxdWVzdCcsIHtcbiAgICAgICAgICAgICAgdHlwZTogJ3NvY2tldCcsXG4gICAgICAgICAgICAgIHVybDogdXJsLFxuICAgICAgICAgICAgICBwcm90b2NvbHM6IHByb3RvY29scyxcbiAgICAgICAgICAgICAgcmVxdWVzdDogcmVxXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHJlcTtcbiAgICAgICAgfTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBleHRlbmROYXRpdmUod2luZG93LldlYlNvY2tldCwgX1dlYlNvY2tldCk7XG4gICAgICAgIH0gY2F0Y2ggKF9lcnJvcikge31cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gUmVxdWVzdEludGVyY2VwdDtcblxuICB9KShFdmVudHMpO1xuXG4gIF9pbnRlcmNlcHQgPSBudWxsO1xuXG4gIGdldEludGVyY2VwdCA9IGZ1bmN0aW9uKCkge1xuICAgIGlmIChfaW50ZXJjZXB0ID09IG51bGwpIHtcbiAgICAgIF9pbnRlcmNlcHQgPSBuZXcgUmVxdWVzdEludGVyY2VwdDtcbiAgICB9XG4gICAgcmV0dXJuIF9pbnRlcmNlcHQ7XG4gIH07XG5cbiAgc2hvdWxkSWdub3JlVVJMID0gZnVuY3Rpb24odXJsKSB7XG4gICAgdmFyIHBhdHRlcm4sIF9qLCBfbGVuMSwgX3JlZjI7XG4gICAgX3JlZjIgPSBvcHRpb25zLmFqYXguaWdub3JlVVJMcztcbiAgICBmb3IgKF9qID0gMCwgX2xlbjEgPSBfcmVmMi5sZW5ndGg7IF9qIDwgX2xlbjE7IF9qKyspIHtcbiAgICAgIHBhdHRlcm4gPSBfcmVmMltfal07XG4gICAgICBpZiAodHlwZW9mIHBhdHRlcm4gPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIGlmICh1cmwuaW5kZXhPZihwYXR0ZXJuKSAhPT0gLTEpIHtcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKHBhdHRlcm4udGVzdCh1cmwpKSB7XG4gICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9O1xuXG4gIGdldEludGVyY2VwdCgpLm9uKCdyZXF1ZXN0JywgZnVuY3Rpb24oX2FyZykge1xuICAgIHZhciBhZnRlciwgYXJncywgcmVxdWVzdCwgdHlwZSwgdXJsO1xuICAgIHR5cGUgPSBfYXJnLnR5cGUsIHJlcXVlc3QgPSBfYXJnLnJlcXVlc3QsIHVybCA9IF9hcmcudXJsO1xuICAgIGlmIChzaG91bGRJZ25vcmVVUkwodXJsKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoIVBhY2UucnVubmluZyAmJiAob3B0aW9ucy5yZXN0YXJ0T25SZXF1ZXN0QWZ0ZXIgIT09IGZhbHNlIHx8IHNob3VsZFRyYWNrKHR5cGUpID09PSAnZm9yY2UnKSkge1xuICAgICAgYXJncyA9IGFyZ3VtZW50cztcbiAgICAgIGFmdGVyID0gb3B0aW9ucy5yZXN0YXJ0T25SZXF1ZXN0QWZ0ZXIgfHwgMDtcbiAgICAgIGlmICh0eXBlb2YgYWZ0ZXIgPT09ICdib29sZWFuJykge1xuICAgICAgICBhZnRlciA9IDA7XG4gICAgICB9XG4gICAgICByZXR1cm4gc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgdmFyIHN0aWxsQWN0aXZlLCBfaiwgX2xlbjEsIF9yZWYyLCBfcmVmMywgX3Jlc3VsdHM7XG4gICAgICAgIGlmICh0eXBlID09PSAnc29ja2V0Jykge1xuICAgICAgICAgIHN0aWxsQWN0aXZlID0gcmVxdWVzdC5yZWFkeVN0YXRlIDwgMjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzdGlsbEFjdGl2ZSA9ICgwIDwgKF9yZWYyID0gcmVxdWVzdC5yZWFkeVN0YXRlKSAmJiBfcmVmMiA8IDQpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChzdGlsbEFjdGl2ZSkge1xuICAgICAgICAgIFBhY2UucmVzdGFydCgpO1xuICAgICAgICAgIF9yZWYzID0gUGFjZS5zb3VyY2VzO1xuICAgICAgICAgIF9yZXN1bHRzID0gW107XG4gICAgICAgICAgZm9yIChfaiA9IDAsIF9sZW4xID0gX3JlZjMubGVuZ3RoOyBfaiA8IF9sZW4xOyBfaisrKSB7XG4gICAgICAgICAgICBzb3VyY2UgPSBfcmVmM1tfal07XG4gICAgICAgICAgICBpZiAoc291cmNlIGluc3RhbmNlb2YgQWpheE1vbml0b3IpIHtcbiAgICAgICAgICAgICAgc291cmNlLndhdGNoLmFwcGx5KHNvdXJjZSwgYXJncyk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgX3Jlc3VsdHMucHVzaCh2b2lkIDApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gX3Jlc3VsdHM7XG4gICAgICAgIH1cbiAgICAgIH0sIGFmdGVyKTtcbiAgICB9XG4gIH0pO1xuXG4gIEFqYXhNb25pdG9yID0gKGZ1bmN0aW9uKCkge1xuICAgIGZ1bmN0aW9uIEFqYXhNb25pdG9yKCkge1xuICAgICAgdmFyIF90aGlzID0gdGhpcztcbiAgICAgIHRoaXMuZWxlbWVudHMgPSBbXTtcbiAgICAgIGdldEludGVyY2VwdCgpLm9uKCdyZXF1ZXN0JywgZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiBfdGhpcy53YXRjaC5hcHBseShfdGhpcywgYXJndW1lbnRzKTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIEFqYXhNb25pdG9yLnByb3RvdHlwZS53YXRjaCA9IGZ1bmN0aW9uKF9hcmcpIHtcbiAgICAgIHZhciByZXF1ZXN0LCB0cmFja2VyLCB0eXBlLCB1cmw7XG4gICAgICB0eXBlID0gX2FyZy50eXBlLCByZXF1ZXN0ID0gX2FyZy5yZXF1ZXN0LCB1cmwgPSBfYXJnLnVybDtcbiAgICAgIGlmIChzaG91bGRJZ25vcmVVUkwodXJsKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAodHlwZSA9PT0gJ3NvY2tldCcpIHtcbiAgICAgICAgdHJhY2tlciA9IG5ldyBTb2NrZXRSZXF1ZXN0VHJhY2tlcihyZXF1ZXN0KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRyYWNrZXIgPSBuZXcgWEhSUmVxdWVzdFRyYWNrZXIocmVxdWVzdCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5lbGVtZW50cy5wdXNoKHRyYWNrZXIpO1xuICAgIH07XG5cbiAgICByZXR1cm4gQWpheE1vbml0b3I7XG5cbiAgfSkoKTtcblxuICBYSFJSZXF1ZXN0VHJhY2tlciA9IChmdW5jdGlvbigpIHtcbiAgICBmdW5jdGlvbiBYSFJSZXF1ZXN0VHJhY2tlcihyZXF1ZXN0KSB7XG4gICAgICB2YXIgZXZlbnQsIHNpemUsIF9qLCBfbGVuMSwgX29ucmVhZHlzdGF0ZWNoYW5nZSwgX3JlZjIsXG4gICAgICAgIF90aGlzID0gdGhpcztcbiAgICAgIHRoaXMucHJvZ3Jlc3MgPSAwO1xuICAgICAgaWYgKHdpbmRvdy5Qcm9ncmVzc0V2ZW50ICE9IG51bGwpIHtcbiAgICAgICAgc2l6ZSA9IG51bGw7XG4gICAgICAgIHJlcXVlc3QuYWRkRXZlbnRMaXN0ZW5lcigncHJvZ3Jlc3MnLCBmdW5jdGlvbihldnQpIHtcbiAgICAgICAgICBpZiAoZXZ0Lmxlbmd0aENvbXB1dGFibGUpIHtcbiAgICAgICAgICAgIHJldHVybiBfdGhpcy5wcm9ncmVzcyA9IDEwMCAqIGV2dC5sb2FkZWQgLyBldnQudG90YWw7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBfdGhpcy5wcm9ncmVzcyA9IF90aGlzLnByb2dyZXNzICsgKDEwMCAtIF90aGlzLnByb2dyZXNzKSAvIDI7XG4gICAgICAgICAgfVxuICAgICAgICB9LCBmYWxzZSk7XG4gICAgICAgIF9yZWYyID0gWydsb2FkJywgJ2Fib3J0JywgJ3RpbWVvdXQnLCAnZXJyb3InXTtcbiAgICAgICAgZm9yIChfaiA9IDAsIF9sZW4xID0gX3JlZjIubGVuZ3RoOyBfaiA8IF9sZW4xOyBfaisrKSB7XG4gICAgICAgICAgZXZlbnQgPSBfcmVmMltfal07XG4gICAgICAgICAgcmVxdWVzdC5hZGRFdmVudExpc3RlbmVyKGV2ZW50LCBmdW5jdGlvbigpIHtcbiAgICAgICAgICAgIHJldHVybiBfdGhpcy5wcm9ncmVzcyA9IDEwMDtcbiAgICAgICAgICB9LCBmYWxzZSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIF9vbnJlYWR5c3RhdGVjaGFuZ2UgPSByZXF1ZXN0Lm9ucmVhZHlzdGF0ZWNoYW5nZTtcbiAgICAgICAgcmVxdWVzdC5vbnJlYWR5c3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgICB2YXIgX3JlZjM7XG4gICAgICAgICAgaWYgKChfcmVmMyA9IHJlcXVlc3QucmVhZHlTdGF0ZSkgPT09IDAgfHwgX3JlZjMgPT09IDQpIHtcbiAgICAgICAgICAgIF90aGlzLnByb2dyZXNzID0gMTAwO1xuICAgICAgICAgIH0gZWxzZSBpZiAocmVxdWVzdC5yZWFkeVN0YXRlID09PSAzKSB7XG4gICAgICAgICAgICBfdGhpcy5wcm9ncmVzcyA9IDUwO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gdHlwZW9mIF9vbnJlYWR5c3RhdGVjaGFuZ2UgPT09IFwiZnVuY3Rpb25cIiA/IF9vbnJlYWR5c3RhdGVjaGFuZ2UuYXBwbHkobnVsbCwgYXJndW1lbnRzKSA6IHZvaWQgMDtcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gWEhSUmVxdWVzdFRyYWNrZXI7XG5cbiAgfSkoKTtcblxuICBTb2NrZXRSZXF1ZXN0VHJhY2tlciA9IChmdW5jdGlvbigpIHtcbiAgICBmdW5jdGlvbiBTb2NrZXRSZXF1ZXN0VHJhY2tlcihyZXF1ZXN0KSB7XG4gICAgICB2YXIgZXZlbnQsIF9qLCBfbGVuMSwgX3JlZjIsXG4gICAgICAgIF90aGlzID0gdGhpcztcbiAgICAgIHRoaXMucHJvZ3Jlc3MgPSAwO1xuICAgICAgX3JlZjIgPSBbJ2Vycm9yJywgJ29wZW4nXTtcbiAgICAgIGZvciAoX2ogPSAwLCBfbGVuMSA9IF9yZWYyLmxlbmd0aDsgX2ogPCBfbGVuMTsgX2orKykge1xuICAgICAgICBldmVudCA9IF9yZWYyW19qXTtcbiAgICAgICAgcmVxdWVzdC5hZGRFdmVudExpc3RlbmVyKGV2ZW50LCBmdW5jdGlvbigpIHtcbiAgICAgICAgICByZXR1cm4gX3RoaXMucHJvZ3Jlc3MgPSAxMDA7XG4gICAgICAgIH0sIGZhbHNlKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gU29ja2V0UmVxdWVzdFRyYWNrZXI7XG5cbiAgfSkoKTtcblxuICBFbGVtZW50TW9uaXRvciA9IChmdW5jdGlvbigpIHtcbiAgICBmdW5jdGlvbiBFbGVtZW50TW9uaXRvcihvcHRpb25zKSB7XG4gICAgICB2YXIgc2VsZWN0b3IsIF9qLCBfbGVuMSwgX3JlZjI7XG4gICAgICBpZiAob3B0aW9ucyA9PSBudWxsKSB7XG4gICAgICAgIG9wdGlvbnMgPSB7fTtcbiAgICAgIH1cbiAgICAgIHRoaXMuZWxlbWVudHMgPSBbXTtcbiAgICAgIGlmIChvcHRpb25zLnNlbGVjdG9ycyA9PSBudWxsKSB7XG4gICAgICAgIG9wdGlvbnMuc2VsZWN0b3JzID0gW107XG4gICAgICB9XG4gICAgICBfcmVmMiA9IG9wdGlvbnMuc2VsZWN0b3JzO1xuICAgICAgZm9yIChfaiA9IDAsIF9sZW4xID0gX3JlZjIubGVuZ3RoOyBfaiA8IF9sZW4xOyBfaisrKSB7XG4gICAgICAgIHNlbGVjdG9yID0gX3JlZjJbX2pdO1xuICAgICAgICB0aGlzLmVsZW1lbnRzLnB1c2gobmV3IEVsZW1lbnRUcmFja2VyKHNlbGVjdG9yKSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIEVsZW1lbnRNb25pdG9yO1xuXG4gIH0pKCk7XG5cbiAgRWxlbWVudFRyYWNrZXIgPSAoZnVuY3Rpb24oKSB7XG4gICAgZnVuY3Rpb24gRWxlbWVudFRyYWNrZXIoc2VsZWN0b3IpIHtcbiAgICAgIHRoaXMuc2VsZWN0b3IgPSBzZWxlY3RvcjtcbiAgICAgIHRoaXMucHJvZ3Jlc3MgPSAwO1xuICAgICAgdGhpcy5jaGVjaygpO1xuICAgIH1cblxuICAgIEVsZW1lbnRUcmFja2VyLnByb3RvdHlwZS5jaGVjayA9IGZ1bmN0aW9uKCkge1xuICAgICAgdmFyIF90aGlzID0gdGhpcztcbiAgICAgIGlmIChkb2N1bWVudC5xdWVyeVNlbGVjdG9yKHRoaXMuc2VsZWN0b3IpKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmRvbmUoKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBzZXRUaW1lb3V0KChmdW5jdGlvbigpIHtcbiAgICAgICAgICByZXR1cm4gX3RoaXMuY2hlY2soKTtcbiAgICAgICAgfSksIG9wdGlvbnMuZWxlbWVudHMuY2hlY2tJbnRlcnZhbCk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIEVsZW1lbnRUcmFja2VyLnByb3RvdHlwZS5kb25lID0gZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gdGhpcy5wcm9ncmVzcyA9IDEwMDtcbiAgICB9O1xuXG4gICAgcmV0dXJuIEVsZW1lbnRUcmFja2VyO1xuXG4gIH0pKCk7XG5cbiAgRG9jdW1lbnRNb25pdG9yID0gKGZ1bmN0aW9uKCkge1xuICAgIERvY3VtZW50TW9uaXRvci5wcm90b3R5cGUuc3RhdGVzID0ge1xuICAgICAgbG9hZGluZzogMCxcbiAgICAgIGludGVyYWN0aXZlOiA1MCxcbiAgICAgIGNvbXBsZXRlOiAxMDBcbiAgICB9O1xuXG4gICAgZnVuY3Rpb24gRG9jdW1lbnRNb25pdG9yKCkge1xuICAgICAgdmFyIF9vbnJlYWR5c3RhdGVjaGFuZ2UsIF9yZWYyLFxuICAgICAgICBfdGhpcyA9IHRoaXM7XG4gICAgICB0aGlzLnByb2dyZXNzID0gKF9yZWYyID0gdGhpcy5zdGF0ZXNbZG9jdW1lbnQucmVhZHlTdGF0ZV0pICE9IG51bGwgPyBfcmVmMiA6IDEwMDtcbiAgICAgIF9vbnJlYWR5c3RhdGVjaGFuZ2UgPSBkb2N1bWVudC5vbnJlYWR5c3RhdGVjaGFuZ2U7XG4gICAgICBkb2N1bWVudC5vbnJlYWR5c3RhdGVjaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKF90aGlzLnN0YXRlc1tkb2N1bWVudC5yZWFkeVN0YXRlXSAhPSBudWxsKSB7XG4gICAgICAgICAgX3RoaXMucHJvZ3Jlc3MgPSBfdGhpcy5zdGF0ZXNbZG9jdW1lbnQucmVhZHlTdGF0ZV07XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHR5cGVvZiBfb25yZWFkeXN0YXRlY2hhbmdlID09PSBcImZ1bmN0aW9uXCIgPyBfb25yZWFkeXN0YXRlY2hhbmdlLmFwcGx5KG51bGwsIGFyZ3VtZW50cykgOiB2b2lkIDA7XG4gICAgICB9O1xuICAgIH1cblxuICAgIHJldHVybiBEb2N1bWVudE1vbml0b3I7XG5cbiAgfSkoKTtcblxuICBFdmVudExhZ01vbml0b3IgPSAoZnVuY3Rpb24oKSB7XG4gICAgZnVuY3Rpb24gRXZlbnRMYWdNb25pdG9yKCkge1xuICAgICAgdmFyIGF2ZywgaW50ZXJ2YWwsIGxhc3QsIHBvaW50cywgc2FtcGxlcyxcbiAgICAgICAgX3RoaXMgPSB0aGlzO1xuICAgICAgdGhpcy5wcm9ncmVzcyA9IDA7XG4gICAgICBhdmcgPSAwO1xuICAgICAgc2FtcGxlcyA9IFtdO1xuICAgICAgcG9pbnRzID0gMDtcbiAgICAgIGxhc3QgPSBub3coKTtcbiAgICAgIGludGVydmFsID0gc2V0SW50ZXJ2YWwoZnVuY3Rpb24oKSB7XG4gICAgICAgIHZhciBkaWZmO1xuICAgICAgICBkaWZmID0gbm93KCkgLSBsYXN0IC0gNTA7XG4gICAgICAgIGxhc3QgPSBub3coKTtcbiAgICAgICAgc2FtcGxlcy5wdXNoKGRpZmYpO1xuICAgICAgICBpZiAoc2FtcGxlcy5sZW5ndGggPiBvcHRpb25zLmV2ZW50TGFnLnNhbXBsZUNvdW50KSB7XG4gICAgICAgICAgc2FtcGxlcy5zaGlmdCgpO1xuICAgICAgICB9XG4gICAgICAgIGF2ZyA9IGF2Z0FtcGxpdHVkZShzYW1wbGVzKTtcbiAgICAgICAgaWYgKCsrcG9pbnRzID49IG9wdGlvbnMuZXZlbnRMYWcubWluU2FtcGxlcyAmJiBhdmcgPCBvcHRpb25zLmV2ZW50TGFnLmxhZ1RocmVzaG9sZCkge1xuICAgICAgICAgIF90aGlzLnByb2dyZXNzID0gMTAwO1xuICAgICAgICAgIHJldHVybiBjbGVhckludGVydmFsKGludGVydmFsKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gX3RoaXMucHJvZ3Jlc3MgPSAxMDAgKiAoMyAvIChhdmcgKyAzKSk7XG4gICAgICAgIH1cbiAgICAgIH0sIDUwKTtcbiAgICB9XG5cbiAgICByZXR1cm4gRXZlbnRMYWdNb25pdG9yO1xuXG4gIH0pKCk7XG5cbiAgU2NhbGVyID0gKGZ1bmN0aW9uKCkge1xuICAgIGZ1bmN0aW9uIFNjYWxlcihzb3VyY2UpIHtcbiAgICAgIHRoaXMuc291cmNlID0gc291cmNlO1xuICAgICAgdGhpcy5sYXN0ID0gdGhpcy5zaW5jZUxhc3RVcGRhdGUgPSAwO1xuICAgICAgdGhpcy5yYXRlID0gb3B0aW9ucy5pbml0aWFsUmF0ZTtcbiAgICAgIHRoaXMuY2F0Y2h1cCA9IDA7XG4gICAgICB0aGlzLnByb2dyZXNzID0gdGhpcy5sYXN0UHJvZ3Jlc3MgPSAwO1xuICAgICAgaWYgKHRoaXMuc291cmNlICE9IG51bGwpIHtcbiAgICAgICAgdGhpcy5wcm9ncmVzcyA9IHJlc3VsdCh0aGlzLnNvdXJjZSwgJ3Byb2dyZXNzJyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgU2NhbGVyLnByb3RvdHlwZS50aWNrID0gZnVuY3Rpb24oZnJhbWVUaW1lLCB2YWwpIHtcbiAgICAgIHZhciBzY2FsaW5nO1xuICAgICAgaWYgKHZhbCA9PSBudWxsKSB7XG4gICAgICAgIHZhbCA9IHJlc3VsdCh0aGlzLnNvdXJjZSwgJ3Byb2dyZXNzJyk7XG4gICAgICB9XG4gICAgICBpZiAodmFsID49IDEwMCkge1xuICAgICAgICB0aGlzLmRvbmUgPSB0cnVlO1xuICAgICAgfVxuICAgICAgaWYgKHZhbCA9PT0gdGhpcy5sYXN0KSB7XG4gICAgICAgIHRoaXMuc2luY2VMYXN0VXBkYXRlICs9IGZyYW1lVGltZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmICh0aGlzLnNpbmNlTGFzdFVwZGF0ZSkge1xuICAgICAgICAgIHRoaXMucmF0ZSA9ICh2YWwgLSB0aGlzLmxhc3QpIC8gdGhpcy5zaW5jZUxhc3RVcGRhdGU7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5jYXRjaHVwID0gKHZhbCAtIHRoaXMucHJvZ3Jlc3MpIC8gb3B0aW9ucy5jYXRjaHVwVGltZTtcbiAgICAgICAgdGhpcy5zaW5jZUxhc3RVcGRhdGUgPSAwO1xuICAgICAgICB0aGlzLmxhc3QgPSB2YWw7XG4gICAgICB9XG4gICAgICBpZiAodmFsID4gdGhpcy5wcm9ncmVzcykge1xuICAgICAgICB0aGlzLnByb2dyZXNzICs9IHRoaXMuY2F0Y2h1cCAqIGZyYW1lVGltZTtcbiAgICAgIH1cbiAgICAgIHNjYWxpbmcgPSAxIC0gTWF0aC5wb3codGhpcy5wcm9ncmVzcyAvIDEwMCwgb3B0aW9ucy5lYXNlRmFjdG9yKTtcbiAgICAgIHRoaXMucHJvZ3Jlc3MgKz0gc2NhbGluZyAqIHRoaXMucmF0ZSAqIGZyYW1lVGltZTtcbiAgICAgIHRoaXMucHJvZ3Jlc3MgPSBNYXRoLm1pbih0aGlzLmxhc3RQcm9ncmVzcyArIG9wdGlvbnMubWF4UHJvZ3Jlc3NQZXJGcmFtZSwgdGhpcy5wcm9ncmVzcyk7XG4gICAgICB0aGlzLnByb2dyZXNzID0gTWF0aC5tYXgoMCwgdGhpcy5wcm9ncmVzcyk7XG4gICAgICB0aGlzLnByb2dyZXNzID0gTWF0aC5taW4oMTAwLCB0aGlzLnByb2dyZXNzKTtcbiAgICAgIHRoaXMubGFzdFByb2dyZXNzID0gdGhpcy5wcm9ncmVzcztcbiAgICAgIHJldHVybiB0aGlzLnByb2dyZXNzO1xuICAgIH07XG5cbiAgICByZXR1cm4gU2NhbGVyO1xuXG4gIH0pKCk7XG5cbiAgc291cmNlcyA9IG51bGw7XG5cbiAgc2NhbGVycyA9IG51bGw7XG5cbiAgYmFyID0gbnVsbDtcblxuICB1bmlTY2FsZXIgPSBudWxsO1xuXG4gIGFuaW1hdGlvbiA9IG51bGw7XG5cbiAgY2FuY2VsQW5pbWF0aW9uID0gbnVsbDtcblxuICBQYWNlLnJ1bm5pbmcgPSBmYWxzZTtcblxuICBoYW5kbGVQdXNoU3RhdGUgPSBmdW5jdGlvbigpIHtcbiAgICBpZiAob3B0aW9ucy5yZXN0YXJ0T25QdXNoU3RhdGUpIHtcbiAgICAgIHJldHVybiBQYWNlLnJlc3RhcnQoKTtcbiAgICB9XG4gIH07XG5cbiAgaWYgKHdpbmRvdy5oaXN0b3J5LnB1c2hTdGF0ZSAhPSBudWxsKSB7XG4gICAgX3B1c2hTdGF0ZSA9IHdpbmRvdy5oaXN0b3J5LnB1c2hTdGF0ZTtcbiAgICB3aW5kb3cuaGlzdG9yeS5wdXNoU3RhdGUgPSBmdW5jdGlvbigpIHtcbiAgICAgIGhhbmRsZVB1c2hTdGF0ZSgpO1xuICAgICAgcmV0dXJuIF9wdXNoU3RhdGUuYXBwbHkod2luZG93Lmhpc3RvcnksIGFyZ3VtZW50cyk7XG4gICAgfTtcbiAgfVxuXG4gIGlmICh3aW5kb3cuaGlzdG9yeS5yZXBsYWNlU3RhdGUgIT0gbnVsbCkge1xuICAgIF9yZXBsYWNlU3RhdGUgPSB3aW5kb3cuaGlzdG9yeS5yZXBsYWNlU3RhdGU7XG4gICAgd2luZG93Lmhpc3RvcnkucmVwbGFjZVN0YXRlID0gZnVuY3Rpb24oKSB7XG4gICAgICBoYW5kbGVQdXNoU3RhdGUoKTtcbiAgICAgIHJldHVybiBfcmVwbGFjZVN0YXRlLmFwcGx5KHdpbmRvdy5oaXN0b3J5LCBhcmd1bWVudHMpO1xuICAgIH07XG4gIH1cblxuICBTT1VSQ0VfS0VZUyA9IHtcbiAgICBhamF4OiBBamF4TW9uaXRvcixcbiAgICBlbGVtZW50czogRWxlbWVudE1vbml0b3IsXG4gICAgZG9jdW1lbnQ6IERvY3VtZW50TW9uaXRvcixcbiAgICBldmVudExhZzogRXZlbnRMYWdNb25pdG9yXG4gIH07XG5cbiAgKGluaXQgPSBmdW5jdGlvbigpIHtcbiAgICB2YXIgdHlwZSwgX2osIF9rLCBfbGVuMSwgX2xlbjIsIF9yZWYyLCBfcmVmMywgX3JlZjQ7XG4gICAgUGFjZS5zb3VyY2VzID0gc291cmNlcyA9IFtdO1xuICAgIF9yZWYyID0gWydhamF4JywgJ2VsZW1lbnRzJywgJ2RvY3VtZW50JywgJ2V2ZW50TGFnJ107XG4gICAgZm9yIChfaiA9IDAsIF9sZW4xID0gX3JlZjIubGVuZ3RoOyBfaiA8IF9sZW4xOyBfaisrKSB7XG4gICAgICB0eXBlID0gX3JlZjJbX2pdO1xuICAgICAgaWYgKG9wdGlvbnNbdHlwZV0gIT09IGZhbHNlKSB7XG4gICAgICAgIHNvdXJjZXMucHVzaChuZXcgU09VUkNFX0tFWVNbdHlwZV0ob3B0aW9uc1t0eXBlXSkpO1xuICAgICAgfVxuICAgIH1cbiAgICBfcmVmNCA9IChfcmVmMyA9IG9wdGlvbnMuZXh0cmFTb3VyY2VzKSAhPSBudWxsID8gX3JlZjMgOiBbXTtcbiAgICBmb3IgKF9rID0gMCwgX2xlbjIgPSBfcmVmNC5sZW5ndGg7IF9rIDwgX2xlbjI7IF9rKyspIHtcbiAgICAgIHNvdXJjZSA9IF9yZWY0W19rXTtcbiAgICAgIHNvdXJjZXMucHVzaChuZXcgc291cmNlKG9wdGlvbnMpKTtcbiAgICB9XG4gICAgUGFjZS5iYXIgPSBiYXIgPSBuZXcgQmFyO1xuICAgIHNjYWxlcnMgPSBbXTtcbiAgICByZXR1cm4gdW5pU2NhbGVyID0gbmV3IFNjYWxlcjtcbiAgfSkoKTtcblxuICBQYWNlLnN0b3AgPSBmdW5jdGlvbigpIHtcbiAgICBQYWNlLnRyaWdnZXIoJ3N0b3AnKTtcbiAgICBQYWNlLnJ1bm5pbmcgPSBmYWxzZTtcbiAgICBiYXIuZGVzdHJveSgpO1xuICAgIGNhbmNlbEFuaW1hdGlvbiA9IHRydWU7XG4gICAgaWYgKGFuaW1hdGlvbiAhPSBudWxsKSB7XG4gICAgICBpZiAodHlwZW9mIGNhbmNlbEFuaW1hdGlvbkZyYW1lID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgY2FuY2VsQW5pbWF0aW9uRnJhbWUoYW5pbWF0aW9uKTtcbiAgICAgIH1cbiAgICAgIGFuaW1hdGlvbiA9IG51bGw7XG4gICAgfVxuICAgIHJldHVybiBpbml0KCk7XG4gIH07XG5cbiAgUGFjZS5yZXN0YXJ0ID0gZnVuY3Rpb24oKSB7XG4gICAgUGFjZS50cmlnZ2VyKCdyZXN0YXJ0Jyk7XG4gICAgUGFjZS5zdG9wKCk7XG4gICAgcmV0dXJuIFBhY2Uuc3RhcnQoKTtcbiAgfTtcblxuICBQYWNlLmdvID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIHN0YXJ0O1xuICAgIFBhY2UucnVubmluZyA9IHRydWU7XG4gICAgYmFyLnJlbmRlcigpO1xuICAgIHN0YXJ0ID0gbm93KCk7XG4gICAgY2FuY2VsQW5pbWF0aW9uID0gZmFsc2U7XG4gICAgcmV0dXJuIGFuaW1hdGlvbiA9IHJ1bkFuaW1hdGlvbihmdW5jdGlvbihmcmFtZVRpbWUsIGVucXVldWVOZXh0RnJhbWUpIHtcbiAgICAgIHZhciBhdmcsIGNvdW50LCBkb25lLCBlbGVtZW50LCBlbGVtZW50cywgaSwgaiwgcmVtYWluaW5nLCBzY2FsZXIsIHNjYWxlckxpc3QsIHN1bSwgX2osIF9rLCBfbGVuMSwgX2xlbjIsIF9yZWYyO1xuICAgICAgcmVtYWluaW5nID0gMTAwIC0gYmFyLnByb2dyZXNzO1xuICAgICAgY291bnQgPSBzdW0gPSAwO1xuICAgICAgZG9uZSA9IHRydWU7XG4gICAgICBmb3IgKGkgPSBfaiA9IDAsIF9sZW4xID0gc291cmNlcy5sZW5ndGg7IF9qIDwgX2xlbjE7IGkgPSArK19qKSB7XG4gICAgICAgIHNvdXJjZSA9IHNvdXJjZXNbaV07XG4gICAgICAgIHNjYWxlckxpc3QgPSBzY2FsZXJzW2ldICE9IG51bGwgPyBzY2FsZXJzW2ldIDogc2NhbGVyc1tpXSA9IFtdO1xuICAgICAgICBlbGVtZW50cyA9IChfcmVmMiA9IHNvdXJjZS5lbGVtZW50cykgIT0gbnVsbCA/IF9yZWYyIDogW3NvdXJjZV07XG4gICAgICAgIGZvciAoaiA9IF9rID0gMCwgX2xlbjIgPSBlbGVtZW50cy5sZW5ndGg7IF9rIDwgX2xlbjI7IGogPSArK19rKSB7XG4gICAgICAgICAgZWxlbWVudCA9IGVsZW1lbnRzW2pdO1xuICAgICAgICAgIHNjYWxlciA9IHNjYWxlckxpc3Rbal0gIT0gbnVsbCA/IHNjYWxlckxpc3Rbal0gOiBzY2FsZXJMaXN0W2pdID0gbmV3IFNjYWxlcihlbGVtZW50KTtcbiAgICAgICAgICBkb25lICY9IHNjYWxlci5kb25lO1xuICAgICAgICAgIGlmIChzY2FsZXIuZG9uZSkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvdW50Kys7XG4gICAgICAgICAgc3VtICs9IHNjYWxlci50aWNrKGZyYW1lVGltZSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGF2ZyA9IHN1bSAvIGNvdW50O1xuICAgICAgYmFyLnVwZGF0ZSh1bmlTY2FsZXIudGljayhmcmFtZVRpbWUsIGF2ZykpO1xuICAgICAgaWYgKGJhci5kb25lKCkgfHwgZG9uZSB8fCBjYW5jZWxBbmltYXRpb24pIHtcbiAgICAgICAgYmFyLnVwZGF0ZSgxMDApO1xuICAgICAgICBQYWNlLnRyaWdnZXIoJ2RvbmUnKTtcbiAgICAgICAgcmV0dXJuIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG4gICAgICAgICAgYmFyLmZpbmlzaCgpO1xuICAgICAgICAgIFBhY2UucnVubmluZyA9IGZhbHNlO1xuICAgICAgICAgIHJldHVybiBQYWNlLnRyaWdnZXIoJ2hpZGUnKTtcbiAgICAgICAgfSwgTWF0aC5tYXgob3B0aW9ucy5naG9zdFRpbWUsIE1hdGgubWF4KG9wdGlvbnMubWluVGltZSAtIChub3coKSAtIHN0YXJ0KSwgMCkpKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBlbnF1ZXVlTmV4dEZyYW1lKCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH07XG5cbiAgUGFjZS5zdGFydCA9IGZ1bmN0aW9uKF9vcHRpb25zKSB7XG4gICAgZXh0ZW5kKG9wdGlvbnMsIF9vcHRpb25zKTtcbiAgICBQYWNlLnJ1bm5pbmcgPSB0cnVlO1xuICAgIHRyeSB7XG4gICAgICBiYXIucmVuZGVyKCk7XG4gICAgfSBjYXRjaCAoX2Vycm9yKSB7XG4gICAgICBOb1RhcmdldEVycm9yID0gX2Vycm9yO1xuICAgIH1cbiAgICBpZiAoIWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5wYWNlJykpIHtcbiAgICAgIHJldHVybiBzZXRUaW1lb3V0KFBhY2Uuc3RhcnQsIDUwKTtcbiAgICB9IGVsc2Uge1xuICAgICAgUGFjZS50cmlnZ2VyKCdzdGFydCcpO1xuICAgICAgcmV0dXJuIFBhY2UuZ28oKTtcbiAgICB9XG4gIH07XG5cbiAgaWYgKHR5cGVvZiBkZWZpbmUgPT09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCkge1xuICAgIGRlZmluZShbJ3BhY2UnXSwgZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gUGFjZTtcbiAgICB9KTtcbiAgfSBlbHNlIGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcpIHtcbiAgICBtb2R1bGUuZXhwb3J0cyA9IFBhY2U7XG4gIH0gZWxzZSB7XG4gICAgaWYgKG9wdGlvbnMuc3RhcnRPblBhZ2VMb2FkKSB7XG4gICAgICBQYWNlLnN0YXJ0KCk7XG4gICAgfVxuICB9XG5cbn0pLmNhbGwodGhpcyk7XG4iLCIoZnVuY3Rpb24ocm9vdCwgZmFjdG9yeSkge1xuXHQvLyBgcm9vdGAgZG9lcyBub3QgcmVzb2x2ZSB0byB0aGUgZ2xvYmFsIHdpbmRvdyBvYmplY3QgaW4gYSBCcm93c2VyaWZpZWRcblx0Ly8gYnVuZGxlLCBzbyBhIGRpcmVjdCByZWZlcmVuY2UgdG8gdGhhdCBvYmplY3QgaXMgdXNlZCBpbnN0ZWFkLlxuXHR2YXIgX3NyY0RvYyA9IHdpbmRvdy5zcmNEb2M7XG5cblx0aWYgKHR5cGVvZiBkZWZpbmUgPT09IFwiZnVuY3Rpb25cIiAmJiBkZWZpbmUuYW1kKSB7XG5cdFx0ZGVmaW5lKFsnZXhwb3J0cyddLCBmdW5jdGlvbihleHBvcnRzKSB7XG5cdFx0XHRmYWN0b3J5KGV4cG9ydHMsIF9zcmNEb2MpO1xuXHRcdFx0cm9vdC5zcmNEb2MgPSBleHBvcnRzO1xuXHRcdH0pO1xuXHR9IGVsc2UgaWYgKHR5cGVvZiBleHBvcnRzID09PSBcIm9iamVjdFwiKSB7XG5cdFx0ZmFjdG9yeShleHBvcnRzLCBfc3JjRG9jKTtcblx0fSBlbHNlIHtcblx0XHRyb290LnNyY0RvYyA9IHt9O1xuXHRcdGZhY3Rvcnkocm9vdC5zcmNEb2MsIF9zcmNEb2MpO1xuXHR9XG59KSh0aGlzLCBmdW5jdGlvbihleHBvcnRzLCBfc3JjRG9jKSB7XG5cdHZhciBpZHgsIGlmcmFtZXM7XG5cdHZhciBpc0NvbXBsaWFudCA9ICEhKFwic3JjZG9jXCIgaW4gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImlmcmFtZVwiKSk7XG5cdHZhciBpbXBsZW1lbnRhdGlvbnMgPSB7XG5cdFx0Y29tcGxpYW50OiBmdW5jdGlvbiggaWZyYW1lLCBjb250ZW50ICkge1xuXG5cdFx0XHRpZiAoY29udGVudCkge1xuXHRcdFx0XHRpZnJhbWUuc2V0QXR0cmlidXRlKFwic3JjZG9jXCIsIGNvbnRlbnQpO1xuXHRcdFx0fVxuXHRcdH0sXG5cdFx0bGVnYWN5OiBmdW5jdGlvbiggaWZyYW1lLCBjb250ZW50ICkge1xuXG5cdFx0XHR2YXIganNVcmw7XG5cblx0XHRcdGlmICghaWZyYW1lIHx8ICFpZnJhbWUuZ2V0QXR0cmlidXRlKSB7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblxuXHRcdFx0aWYgKCFjb250ZW50KSB7XG5cdFx0XHRcdGNvbnRlbnQgPSBpZnJhbWUuZ2V0QXR0cmlidXRlKFwic3JjZG9jXCIpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0aWZyYW1lLnNldEF0dHJpYnV0ZShcInNyY2RvY1wiLCBjb250ZW50KTtcblx0XHRcdH1cblxuXHRcdFx0aWYgKGNvbnRlbnQpIHtcblx0XHRcdFx0Ly8gVGhlIHZhbHVlIHJldHVybmVkIGJ5IGEgc2NyaXB0LXRhcmdldGVkIFVSTCB3aWxsIGJlIHVzZWQgYXNcblx0XHRcdFx0Ly8gdGhlIGlGcmFtZSdzIGNvbnRlbnQuIENyZWF0ZSBzdWNoIGEgVVJMIHdoaWNoIHJldHVybnMgdGhlXG5cdFx0XHRcdC8vIGlGcmFtZSBlbGVtZW50J3MgYHNyY2RvY2AgYXR0cmlidXRlLlxuXHRcdFx0XHRqc1VybCA9IFwiamF2YXNjcmlwdDogd2luZG93LmZyYW1lRWxlbWVudC5nZXRBdHRyaWJ1dGUoJ3NyY2RvYycpO1wiO1xuXG5cdFx0XHRcdGlmcmFtZS5zZXRBdHRyaWJ1dGUoXCJzcmNcIiwganNVcmwpO1xuXG5cdFx0XHRcdC8vIEV4cGxpY2l0bHkgc2V0IHRoZSBpRnJhbWUncyB3aW5kb3cubG9jYXRpb24gZm9yXG5cdFx0XHRcdC8vIGNvbXBhdGFiaWxpdHkgd2l0aCBJRTksIHdoaWNoIGRvZXMgbm90IHJlYWN0IHRvIGNoYW5nZXMgaW5cblx0XHRcdFx0Ly8gdGhlIGBzcmNgIGF0dHJpYnV0ZSB3aGVuIGl0IGlzIGEgYGphdmFzY3JpcHQ6YCBVUkwsIGZvclxuXHRcdFx0XHQvLyBzb21lIHJlYXNvblxuXHRcdFx0XHRpZiAoaWZyYW1lLmNvbnRlbnRXaW5kb3cpIHtcblx0XHRcdFx0XHRpZnJhbWUuY29udGVudFdpbmRvdy5sb2NhdGlvbiA9IGpzVXJsO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHR9O1xuXHR2YXIgc3JjRG9jID0gZXhwb3J0cztcblx0Ly8gQXNzdW1lIHRoZSBiZXN0XG5cdHNyY0RvYy5zZXQgPSBpbXBsZW1lbnRhdGlvbnMuY29tcGxpYW50O1xuXHRzcmNEb2Mubm9Db25mbGljdCA9IGZ1bmN0aW9uKCkge1xuXHRcdHdpbmRvdy5zcmNEb2MgPSBfc3JjRG9jO1xuXHRcdHJldHVybiBzcmNEb2M7XG5cdH07XG5cblx0Ly8gSWYgdGhlIGJyb3dzZXIgc3VwcG9ydHMgc3JjZG9jLCBubyBzaGltbWluZyBpcyBuZWNlc3Nhcnlcblx0aWYgKGlzQ29tcGxpYW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0c3JjRG9jLnNldCA9IGltcGxlbWVudGF0aW9ucy5sZWdhY3k7XG5cblx0Ly8gQXV0b21hdGljYWxseSBzaGltIGFueSBpZnJhbWVzIGFscmVhZHkgcHJlc2VudCBpbiB0aGUgZG9jdW1lbnRcblx0aWZyYW1lcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKFwiaWZyYW1lXCIpO1xuXHRpZHggPSBpZnJhbWVzLmxlbmd0aDtcblxuXHR3aGlsZSAoaWR4LS0pIHtcblx0XHRzcmNEb2Muc2V0KCBpZnJhbWVzW2lkeF0gKTtcblx0fVxuXG59KTtcbiIsIi8qISBqUXVlcnkgVUkgLSB2MS4xMi4xIC0gMjAxNi0wOS0xNFxuKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4qIEluY2x1ZGVzOiB3aWRnZXQuanMsIHBvc2l0aW9uLmpzLCBkYXRhLmpzLCBkaXNhYmxlLXNlbGVjdGlvbi5qcywgZWZmZWN0LmpzLCBlZmZlY3RzL2VmZmVjdC1ibGluZC5qcywgZWZmZWN0cy9lZmZlY3QtYm91bmNlLmpzLCBlZmZlY3RzL2VmZmVjdC1jbGlwLmpzLCBlZmZlY3RzL2VmZmVjdC1kcm9wLmpzLCBlZmZlY3RzL2VmZmVjdC1leHBsb2RlLmpzLCBlZmZlY3RzL2VmZmVjdC1mYWRlLmpzLCBlZmZlY3RzL2VmZmVjdC1mb2xkLmpzLCBlZmZlY3RzL2VmZmVjdC1oaWdobGlnaHQuanMsIGVmZmVjdHMvZWZmZWN0LXB1ZmYuanMsIGVmZmVjdHMvZWZmZWN0LXB1bHNhdGUuanMsIGVmZmVjdHMvZWZmZWN0LXNjYWxlLmpzLCBlZmZlY3RzL2VmZmVjdC1zaGFrZS5qcywgZWZmZWN0cy9lZmZlY3Qtc2l6ZS5qcywgZWZmZWN0cy9lZmZlY3Qtc2xpZGUuanMsIGVmZmVjdHMvZWZmZWN0LXRyYW5zZmVyLmpzLCBmb2N1c2FibGUuanMsIGZvcm0tcmVzZXQtbWl4aW4uanMsIGpxdWVyeS0xLTcuanMsIGtleWNvZGUuanMsIGxhYmVscy5qcywgc2Nyb2xsLXBhcmVudC5qcywgdGFiYmFibGUuanMsIHVuaXF1ZS1pZC5qcywgd2lkZ2V0cy9hY2NvcmRpb24uanMsIHdpZGdldHMvYXV0b2NvbXBsZXRlLmpzLCB3aWRnZXRzL2J1dHRvbi5qcywgd2lkZ2V0cy9jaGVja2JveHJhZGlvLmpzLCB3aWRnZXRzL2NvbnRyb2xncm91cC5qcywgd2lkZ2V0cy9kYXRlcGlja2VyLmpzLCB3aWRnZXRzL2RpYWxvZy5qcywgd2lkZ2V0cy9kcmFnZ2FibGUuanMsIHdpZGdldHMvZHJvcHBhYmxlLmpzLCB3aWRnZXRzL21lbnUuanMsIHdpZGdldHMvbW91c2UuanMsIHdpZGdldHMvcHJvZ3Jlc3NiYXIuanMsIHdpZGdldHMvcmVzaXphYmxlLmpzLCB3aWRnZXRzL3NlbGVjdGFibGUuanMsIHdpZGdldHMvc2VsZWN0bWVudS5qcywgd2lkZ2V0cy9zbGlkZXIuanMsIHdpZGdldHMvc29ydGFibGUuanMsIHdpZGdldHMvc3Bpbm5lci5qcywgd2lkZ2V0cy90YWJzLmpzLCB3aWRnZXRzL3Rvb2x0aXAuanNcbiogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnM7IExpY2Vuc2VkIE1JVCAqL1xuXG4oZnVuY3Rpb24oIGZhY3RvcnkgKSB7XG5cdGlmICggdHlwZW9mIGRlZmluZSA9PT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQgKSB7XG5cblx0XHQvLyBBTUQuIFJlZ2lzdGVyIGFzIGFuIGFub255bW91cyBtb2R1bGUuXG5cdFx0ZGVmaW5lKFsgXCJqcXVlcnlcIiBdLCBmYWN0b3J5ICk7XG5cdH0gZWxzZSB7XG5cblx0XHQvLyBCcm93c2VyIGdsb2JhbHNcblx0XHRmYWN0b3J5KCBqUXVlcnkgKTtcblx0fVxufShmdW5jdGlvbiggJCApIHtcblxuJC51aSA9ICQudWkgfHwge307XG5cbnZhciB2ZXJzaW9uID0gJC51aS52ZXJzaW9uID0gXCIxLjEyLjFcIjtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBXaWRnZXQgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IFdpZGdldFxuLy8+Pmdyb3VwOiBDb3JlXG4vLz4+ZGVzY3JpcHRpb246IFByb3ZpZGVzIGEgZmFjdG9yeSBmb3IgY3JlYXRpbmcgc3RhdGVmdWwgd2lkZ2V0cyB3aXRoIGEgY29tbW9uIEFQSS5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9qUXVlcnkud2lkZ2V0L1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL3dpZGdldC9cblxuXG5cbnZhciB3aWRnZXRVdWlkID0gMDtcbnZhciB3aWRnZXRTbGljZSA9IEFycmF5LnByb3RvdHlwZS5zbGljZTtcblxuJC5jbGVhbkRhdGEgPSAoIGZ1bmN0aW9uKCBvcmlnICkge1xuXHRyZXR1cm4gZnVuY3Rpb24oIGVsZW1zICkge1xuXHRcdHZhciBldmVudHMsIGVsZW0sIGk7XG5cdFx0Zm9yICggaSA9IDA7ICggZWxlbSA9IGVsZW1zWyBpIF0gKSAhPSBudWxsOyBpKysgKSB7XG5cdFx0XHR0cnkge1xuXG5cdFx0XHRcdC8vIE9ubHkgdHJpZ2dlciByZW1vdmUgd2hlbiBuZWNlc3NhcnkgdG8gc2F2ZSB0aW1lXG5cdFx0XHRcdGV2ZW50cyA9ICQuX2RhdGEoIGVsZW0sIFwiZXZlbnRzXCIgKTtcblx0XHRcdFx0aWYgKCBldmVudHMgJiYgZXZlbnRzLnJlbW92ZSApIHtcblx0XHRcdFx0XHQkKCBlbGVtICkudHJpZ2dlckhhbmRsZXIoIFwicmVtb3ZlXCIgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHQvLyBIdHRwOi8vYnVncy5qcXVlcnkuY29tL3RpY2tldC84MjM1XG5cdFx0XHR9IGNhdGNoICggZSApIHt9XG5cdFx0fVxuXHRcdG9yaWcoIGVsZW1zICk7XG5cdH07XG59ICkoICQuY2xlYW5EYXRhICk7XG5cbiQud2lkZ2V0ID0gZnVuY3Rpb24oIG5hbWUsIGJhc2UsIHByb3RvdHlwZSApIHtcblx0dmFyIGV4aXN0aW5nQ29uc3RydWN0b3IsIGNvbnN0cnVjdG9yLCBiYXNlUHJvdG90eXBlO1xuXG5cdC8vIFByb3hpZWRQcm90b3R5cGUgYWxsb3dzIHRoZSBwcm92aWRlZCBwcm90b3R5cGUgdG8gcmVtYWluIHVubW9kaWZpZWRcblx0Ly8gc28gdGhhdCBpdCBjYW4gYmUgdXNlZCBhcyBhIG1peGluIGZvciBtdWx0aXBsZSB3aWRnZXRzICgjODg3Nilcblx0dmFyIHByb3hpZWRQcm90b3R5cGUgPSB7fTtcblxuXHR2YXIgbmFtZXNwYWNlID0gbmFtZS5zcGxpdCggXCIuXCIgKVsgMCBdO1xuXHRuYW1lID0gbmFtZS5zcGxpdCggXCIuXCIgKVsgMSBdO1xuXHR2YXIgZnVsbE5hbWUgPSBuYW1lc3BhY2UgKyBcIi1cIiArIG5hbWU7XG5cblx0aWYgKCAhcHJvdG90eXBlICkge1xuXHRcdHByb3RvdHlwZSA9IGJhc2U7XG5cdFx0YmFzZSA9ICQuV2lkZ2V0O1xuXHR9XG5cblx0aWYgKCAkLmlzQXJyYXkoIHByb3RvdHlwZSApICkge1xuXHRcdHByb3RvdHlwZSA9ICQuZXh0ZW5kLmFwcGx5KCBudWxsLCBbIHt9IF0uY29uY2F0KCBwcm90b3R5cGUgKSApO1xuXHR9XG5cblx0Ly8gQ3JlYXRlIHNlbGVjdG9yIGZvciBwbHVnaW5cblx0JC5leHByWyBcIjpcIiBdWyBmdWxsTmFtZS50b0xvd2VyQ2FzZSgpIF0gPSBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRyZXR1cm4gISEkLmRhdGEoIGVsZW0sIGZ1bGxOYW1lICk7XG5cdH07XG5cblx0JFsgbmFtZXNwYWNlIF0gPSAkWyBuYW1lc3BhY2UgXSB8fCB7fTtcblx0ZXhpc3RpbmdDb25zdHJ1Y3RvciA9ICRbIG5hbWVzcGFjZSBdWyBuYW1lIF07XG5cdGNvbnN0cnVjdG9yID0gJFsgbmFtZXNwYWNlIF1bIG5hbWUgXSA9IGZ1bmN0aW9uKCBvcHRpb25zLCBlbGVtZW50ICkge1xuXG5cdFx0Ly8gQWxsb3cgaW5zdGFudGlhdGlvbiB3aXRob3V0IFwibmV3XCIga2V5d29yZFxuXHRcdGlmICggIXRoaXMuX2NyZWF0ZVdpZGdldCApIHtcblx0XHRcdHJldHVybiBuZXcgY29uc3RydWN0b3IoIG9wdGlvbnMsIGVsZW1lbnQgKTtcblx0XHR9XG5cblx0XHQvLyBBbGxvdyBpbnN0YW50aWF0aW9uIHdpdGhvdXQgaW5pdGlhbGl6aW5nIGZvciBzaW1wbGUgaW5oZXJpdGFuY2Vcblx0XHQvLyBtdXN0IHVzZSBcIm5ld1wiIGtleXdvcmQgKHRoZSBjb2RlIGFib3ZlIGFsd2F5cyBwYXNzZXMgYXJncylcblx0XHRpZiAoIGFyZ3VtZW50cy5sZW5ndGggKSB7XG5cdFx0XHR0aGlzLl9jcmVhdGVXaWRnZXQoIG9wdGlvbnMsIGVsZW1lbnQgKTtcblx0XHR9XG5cdH07XG5cblx0Ly8gRXh0ZW5kIHdpdGggdGhlIGV4aXN0aW5nIGNvbnN0cnVjdG9yIHRvIGNhcnJ5IG92ZXIgYW55IHN0YXRpYyBwcm9wZXJ0aWVzXG5cdCQuZXh0ZW5kKCBjb25zdHJ1Y3RvciwgZXhpc3RpbmdDb25zdHJ1Y3Rvciwge1xuXHRcdHZlcnNpb246IHByb3RvdHlwZS52ZXJzaW9uLFxuXG5cdFx0Ly8gQ29weSB0aGUgb2JqZWN0IHVzZWQgdG8gY3JlYXRlIHRoZSBwcm90b3R5cGUgaW4gY2FzZSB3ZSBuZWVkIHRvXG5cdFx0Ly8gcmVkZWZpbmUgdGhlIHdpZGdldCBsYXRlclxuXHRcdF9wcm90bzogJC5leHRlbmQoIHt9LCBwcm90b3R5cGUgKSxcblxuXHRcdC8vIFRyYWNrIHdpZGdldHMgdGhhdCBpbmhlcml0IGZyb20gdGhpcyB3aWRnZXQgaW4gY2FzZSB0aGlzIHdpZGdldCBpc1xuXHRcdC8vIHJlZGVmaW5lZCBhZnRlciBhIHdpZGdldCBpbmhlcml0cyBmcm9tIGl0XG5cdFx0X2NoaWxkQ29uc3RydWN0b3JzOiBbXVxuXHR9ICk7XG5cblx0YmFzZVByb3RvdHlwZSA9IG5ldyBiYXNlKCk7XG5cblx0Ly8gV2UgbmVlZCB0byBtYWtlIHRoZSBvcHRpb25zIGhhc2ggYSBwcm9wZXJ0eSBkaXJlY3RseSBvbiB0aGUgbmV3IGluc3RhbmNlXG5cdC8vIG90aGVyd2lzZSB3ZSdsbCBtb2RpZnkgdGhlIG9wdGlvbnMgaGFzaCBvbiB0aGUgcHJvdG90eXBlIHRoYXQgd2UncmVcblx0Ly8gaW5oZXJpdGluZyBmcm9tXG5cdGJhc2VQcm90b3R5cGUub3B0aW9ucyA9ICQud2lkZ2V0LmV4dGVuZCgge30sIGJhc2VQcm90b3R5cGUub3B0aW9ucyApO1xuXHQkLmVhY2goIHByb3RvdHlwZSwgZnVuY3Rpb24oIHByb3AsIHZhbHVlICkge1xuXHRcdGlmICggISQuaXNGdW5jdGlvbiggdmFsdWUgKSApIHtcblx0XHRcdHByb3hpZWRQcm90b3R5cGVbIHByb3AgXSA9IHZhbHVlO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRwcm94aWVkUHJvdG90eXBlWyBwcm9wIF0gPSAoIGZ1bmN0aW9uKCkge1xuXHRcdFx0ZnVuY3Rpb24gX3N1cGVyKCkge1xuXHRcdFx0XHRyZXR1cm4gYmFzZS5wcm90b3R5cGVbIHByb3AgXS5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdFx0XHR9XG5cblx0XHRcdGZ1bmN0aW9uIF9zdXBlckFwcGx5KCBhcmdzICkge1xuXHRcdFx0XHRyZXR1cm4gYmFzZS5wcm90b3R5cGVbIHByb3AgXS5hcHBseSggdGhpcywgYXJncyApO1xuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHZhciBfX3N1cGVyID0gdGhpcy5fc3VwZXI7XG5cdFx0XHRcdHZhciBfX3N1cGVyQXBwbHkgPSB0aGlzLl9zdXBlckFwcGx5O1xuXHRcdFx0XHR2YXIgcmV0dXJuVmFsdWU7XG5cblx0XHRcdFx0dGhpcy5fc3VwZXIgPSBfc3VwZXI7XG5cdFx0XHRcdHRoaXMuX3N1cGVyQXBwbHkgPSBfc3VwZXJBcHBseTtcblxuXHRcdFx0XHRyZXR1cm5WYWx1ZSA9IHZhbHVlLmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcblxuXHRcdFx0XHR0aGlzLl9zdXBlciA9IF9fc3VwZXI7XG5cdFx0XHRcdHRoaXMuX3N1cGVyQXBwbHkgPSBfX3N1cGVyQXBwbHk7XG5cblx0XHRcdFx0cmV0dXJuIHJldHVyblZhbHVlO1xuXHRcdFx0fTtcblx0XHR9ICkoKTtcblx0fSApO1xuXHRjb25zdHJ1Y3Rvci5wcm90b3R5cGUgPSAkLndpZGdldC5leHRlbmQoIGJhc2VQcm90b3R5cGUsIHtcblxuXHRcdC8vIFRPRE86IHJlbW92ZSBzdXBwb3J0IGZvciB3aWRnZXRFdmVudFByZWZpeFxuXHRcdC8vIGFsd2F5cyB1c2UgdGhlIG5hbWUgKyBhIGNvbG9uIGFzIHRoZSBwcmVmaXgsIGUuZy4sIGRyYWdnYWJsZTpzdGFydFxuXHRcdC8vIGRvbid0IHByZWZpeCBmb3Igd2lkZ2V0cyB0aGF0IGFyZW4ndCBET00tYmFzZWRcblx0XHR3aWRnZXRFdmVudFByZWZpeDogZXhpc3RpbmdDb25zdHJ1Y3RvciA/ICggYmFzZVByb3RvdHlwZS53aWRnZXRFdmVudFByZWZpeCB8fCBuYW1lICkgOiBuYW1lXG5cdH0sIHByb3hpZWRQcm90b3R5cGUsIHtcblx0XHRjb25zdHJ1Y3RvcjogY29uc3RydWN0b3IsXG5cdFx0bmFtZXNwYWNlOiBuYW1lc3BhY2UsXG5cdFx0d2lkZ2V0TmFtZTogbmFtZSxcblx0XHR3aWRnZXRGdWxsTmFtZTogZnVsbE5hbWVcblx0fSApO1xuXG5cdC8vIElmIHRoaXMgd2lkZ2V0IGlzIGJlaW5nIHJlZGVmaW5lZCB0aGVuIHdlIG5lZWQgdG8gZmluZCBhbGwgd2lkZ2V0cyB0aGF0XG5cdC8vIGFyZSBpbmhlcml0aW5nIGZyb20gaXQgYW5kIHJlZGVmaW5lIGFsbCBvZiB0aGVtIHNvIHRoYXQgdGhleSBpbmhlcml0IGZyb21cblx0Ly8gdGhlIG5ldyB2ZXJzaW9uIG9mIHRoaXMgd2lkZ2V0LiBXZSdyZSBlc3NlbnRpYWxseSB0cnlpbmcgdG8gcmVwbGFjZSBvbmVcblx0Ly8gbGV2ZWwgaW4gdGhlIHByb3RvdHlwZSBjaGFpbi5cblx0aWYgKCBleGlzdGluZ0NvbnN0cnVjdG9yICkge1xuXHRcdCQuZWFjaCggZXhpc3RpbmdDb25zdHJ1Y3Rvci5fY2hpbGRDb25zdHJ1Y3RvcnMsIGZ1bmN0aW9uKCBpLCBjaGlsZCApIHtcblx0XHRcdHZhciBjaGlsZFByb3RvdHlwZSA9IGNoaWxkLnByb3RvdHlwZTtcblxuXHRcdFx0Ly8gUmVkZWZpbmUgdGhlIGNoaWxkIHdpZGdldCB1c2luZyB0aGUgc2FtZSBwcm90b3R5cGUgdGhhdCB3YXNcblx0XHRcdC8vIG9yaWdpbmFsbHkgdXNlZCwgYnV0IGluaGVyaXQgZnJvbSB0aGUgbmV3IHZlcnNpb24gb2YgdGhlIGJhc2Vcblx0XHRcdCQud2lkZ2V0KCBjaGlsZFByb3RvdHlwZS5uYW1lc3BhY2UgKyBcIi5cIiArIGNoaWxkUHJvdG90eXBlLndpZGdldE5hbWUsIGNvbnN0cnVjdG9yLFxuXHRcdFx0XHRjaGlsZC5fcHJvdG8gKTtcblx0XHR9ICk7XG5cblx0XHQvLyBSZW1vdmUgdGhlIGxpc3Qgb2YgZXhpc3RpbmcgY2hpbGQgY29uc3RydWN0b3JzIGZyb20gdGhlIG9sZCBjb25zdHJ1Y3RvclxuXHRcdC8vIHNvIHRoZSBvbGQgY2hpbGQgY29uc3RydWN0b3JzIGNhbiBiZSBnYXJiYWdlIGNvbGxlY3RlZFxuXHRcdGRlbGV0ZSBleGlzdGluZ0NvbnN0cnVjdG9yLl9jaGlsZENvbnN0cnVjdG9ycztcblx0fSBlbHNlIHtcblx0XHRiYXNlLl9jaGlsZENvbnN0cnVjdG9ycy5wdXNoKCBjb25zdHJ1Y3RvciApO1xuXHR9XG5cblx0JC53aWRnZXQuYnJpZGdlKCBuYW1lLCBjb25zdHJ1Y3RvciApO1xuXG5cdHJldHVybiBjb25zdHJ1Y3Rvcjtcbn07XG5cbiQud2lkZ2V0LmV4dGVuZCA9IGZ1bmN0aW9uKCB0YXJnZXQgKSB7XG5cdHZhciBpbnB1dCA9IHdpZGdldFNsaWNlLmNhbGwoIGFyZ3VtZW50cywgMSApO1xuXHR2YXIgaW5wdXRJbmRleCA9IDA7XG5cdHZhciBpbnB1dExlbmd0aCA9IGlucHV0Lmxlbmd0aDtcblx0dmFyIGtleTtcblx0dmFyIHZhbHVlO1xuXG5cdGZvciAoIDsgaW5wdXRJbmRleCA8IGlucHV0TGVuZ3RoOyBpbnB1dEluZGV4KysgKSB7XG5cdFx0Zm9yICgga2V5IGluIGlucHV0WyBpbnB1dEluZGV4IF0gKSB7XG5cdFx0XHR2YWx1ZSA9IGlucHV0WyBpbnB1dEluZGV4IF1bIGtleSBdO1xuXHRcdFx0aWYgKCBpbnB1dFsgaW5wdXRJbmRleCBdLmhhc093blByb3BlcnR5KCBrZXkgKSAmJiB2YWx1ZSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRcdC8vIENsb25lIG9iamVjdHNcblx0XHRcdFx0aWYgKCAkLmlzUGxhaW5PYmplY3QoIHZhbHVlICkgKSB7XG5cdFx0XHRcdFx0dGFyZ2V0WyBrZXkgXSA9ICQuaXNQbGFpbk9iamVjdCggdGFyZ2V0WyBrZXkgXSApID9cblx0XHRcdFx0XHRcdCQud2lkZ2V0LmV4dGVuZCgge30sIHRhcmdldFsga2V5IF0sIHZhbHVlICkgOlxuXG5cdFx0XHRcdFx0XHQvLyBEb24ndCBleHRlbmQgc3RyaW5ncywgYXJyYXlzLCBldGMuIHdpdGggb2JqZWN0c1xuXHRcdFx0XHRcdFx0JC53aWRnZXQuZXh0ZW5kKCB7fSwgdmFsdWUgKTtcblxuXHRcdFx0XHQvLyBDb3B5IGV2ZXJ5dGhpbmcgZWxzZSBieSByZWZlcmVuY2Vcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHR0YXJnZXRbIGtleSBdID0gdmFsdWU7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH1cblx0cmV0dXJuIHRhcmdldDtcbn07XG5cbiQud2lkZ2V0LmJyaWRnZSA9IGZ1bmN0aW9uKCBuYW1lLCBvYmplY3QgKSB7XG5cdHZhciBmdWxsTmFtZSA9IG9iamVjdC5wcm90b3R5cGUud2lkZ2V0RnVsbE5hbWUgfHwgbmFtZTtcblx0JC5mblsgbmFtZSBdID0gZnVuY3Rpb24oIG9wdGlvbnMgKSB7XG5cdFx0dmFyIGlzTWV0aG9kQ2FsbCA9IHR5cGVvZiBvcHRpb25zID09PSBcInN0cmluZ1wiO1xuXHRcdHZhciBhcmdzID0gd2lkZ2V0U2xpY2UuY2FsbCggYXJndW1lbnRzLCAxICk7XG5cdFx0dmFyIHJldHVyblZhbHVlID0gdGhpcztcblxuXHRcdGlmICggaXNNZXRob2RDYWxsICkge1xuXG5cdFx0XHQvLyBJZiB0aGlzIGlzIGFuIGVtcHR5IGNvbGxlY3Rpb24sIHdlIG5lZWQgdG8gaGF2ZSB0aGUgaW5zdGFuY2UgbWV0aG9kXG5cdFx0XHQvLyByZXR1cm4gdW5kZWZpbmVkIGluc3RlYWQgb2YgdGhlIGpRdWVyeSBpbnN0YW5jZVxuXHRcdFx0aWYgKCAhdGhpcy5sZW5ndGggJiYgb3B0aW9ucyA9PT0gXCJpbnN0YW5jZVwiICkge1xuXHRcdFx0XHRyZXR1cm5WYWx1ZSA9IHVuZGVmaW5lZDtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0dmFyIG1ldGhvZFZhbHVlO1xuXHRcdFx0XHRcdHZhciBpbnN0YW5jZSA9ICQuZGF0YSggdGhpcywgZnVsbE5hbWUgKTtcblxuXHRcdFx0XHRcdGlmICggb3B0aW9ucyA9PT0gXCJpbnN0YW5jZVwiICkge1xuXHRcdFx0XHRcdFx0cmV0dXJuVmFsdWUgPSBpbnN0YW5jZTtcblx0XHRcdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZiAoICFpbnN0YW5jZSApIHtcblx0XHRcdFx0XHRcdHJldHVybiAkLmVycm9yKCBcImNhbm5vdCBjYWxsIG1ldGhvZHMgb24gXCIgKyBuYW1lICtcblx0XHRcdFx0XHRcdFx0XCIgcHJpb3IgdG8gaW5pdGlhbGl6YXRpb247IFwiICtcblx0XHRcdFx0XHRcdFx0XCJhdHRlbXB0ZWQgdG8gY2FsbCBtZXRob2QgJ1wiICsgb3B0aW9ucyArIFwiJ1wiICk7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYgKCAhJC5pc0Z1bmN0aW9uKCBpbnN0YW5jZVsgb3B0aW9ucyBdICkgfHwgb3B0aW9ucy5jaGFyQXQoIDAgKSA9PT0gXCJfXCIgKSB7XG5cdFx0XHRcdFx0XHRyZXR1cm4gJC5lcnJvciggXCJubyBzdWNoIG1ldGhvZCAnXCIgKyBvcHRpb25zICsgXCInIGZvciBcIiArIG5hbWUgK1xuXHRcdFx0XHRcdFx0XHRcIiB3aWRnZXQgaW5zdGFuY2VcIiApO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdG1ldGhvZFZhbHVlID0gaW5zdGFuY2VbIG9wdGlvbnMgXS5hcHBseSggaW5zdGFuY2UsIGFyZ3MgKTtcblxuXHRcdFx0XHRcdGlmICggbWV0aG9kVmFsdWUgIT09IGluc3RhbmNlICYmIG1ldGhvZFZhbHVlICE9PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdFx0XHRyZXR1cm5WYWx1ZSA9IG1ldGhvZFZhbHVlICYmIG1ldGhvZFZhbHVlLmpxdWVyeSA/XG5cdFx0XHRcdFx0XHRcdHJldHVyblZhbHVlLnB1c2hTdGFjayggbWV0aG9kVmFsdWUuZ2V0KCkgKSA6XG5cdFx0XHRcdFx0XHRcdG1ldGhvZFZhbHVlO1xuXHRcdFx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSApO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cblx0XHRcdC8vIEFsbG93IG11bHRpcGxlIGhhc2hlcyB0byBiZSBwYXNzZWQgb24gaW5pdFxuXHRcdFx0aWYgKCBhcmdzLmxlbmd0aCApIHtcblx0XHRcdFx0b3B0aW9ucyA9ICQud2lkZ2V0LmV4dGVuZC5hcHBseSggbnVsbCwgWyBvcHRpb25zIF0uY29uY2F0KCBhcmdzICkgKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdFx0dmFyIGluc3RhbmNlID0gJC5kYXRhKCB0aGlzLCBmdWxsTmFtZSApO1xuXHRcdFx0XHRpZiAoIGluc3RhbmNlICkge1xuXHRcdFx0XHRcdGluc3RhbmNlLm9wdGlvbiggb3B0aW9ucyB8fCB7fSApO1xuXHRcdFx0XHRcdGlmICggaW5zdGFuY2UuX2luaXQgKSB7XG5cdFx0XHRcdFx0XHRpbnN0YW5jZS5faW5pdCgpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHQkLmRhdGEoIHRoaXMsIGZ1bGxOYW1lLCBuZXcgb2JqZWN0KCBvcHRpb25zLCB0aGlzICkgKTtcblx0XHRcdFx0fVxuXHRcdFx0fSApO1xuXHRcdH1cblxuXHRcdHJldHVybiByZXR1cm5WYWx1ZTtcblx0fTtcbn07XG5cbiQuV2lkZ2V0ID0gZnVuY3Rpb24oIC8qIG9wdGlvbnMsIGVsZW1lbnQgKi8gKSB7fTtcbiQuV2lkZ2V0Ll9jaGlsZENvbnN0cnVjdG9ycyA9IFtdO1xuXG4kLldpZGdldC5wcm90b3R5cGUgPSB7XG5cdHdpZGdldE5hbWU6IFwid2lkZ2V0XCIsXG5cdHdpZGdldEV2ZW50UHJlZml4OiBcIlwiLFxuXHRkZWZhdWx0RWxlbWVudDogXCI8ZGl2PlwiLFxuXG5cdG9wdGlvbnM6IHtcblx0XHRjbGFzc2VzOiB7fSxcblx0XHRkaXNhYmxlZDogZmFsc2UsXG5cblx0XHQvLyBDYWxsYmFja3Ncblx0XHRjcmVhdGU6IG51bGxcblx0fSxcblxuXHRfY3JlYXRlV2lkZ2V0OiBmdW5jdGlvbiggb3B0aW9ucywgZWxlbWVudCApIHtcblx0XHRlbGVtZW50ID0gJCggZWxlbWVudCB8fCB0aGlzLmRlZmF1bHRFbGVtZW50IHx8IHRoaXMgKVsgMCBdO1xuXHRcdHRoaXMuZWxlbWVudCA9ICQoIGVsZW1lbnQgKTtcblx0XHR0aGlzLnV1aWQgPSB3aWRnZXRVdWlkKys7XG5cdFx0dGhpcy5ldmVudE5hbWVzcGFjZSA9IFwiLlwiICsgdGhpcy53aWRnZXROYW1lICsgdGhpcy51dWlkO1xuXG5cdFx0dGhpcy5iaW5kaW5ncyA9ICQoKTtcblx0XHR0aGlzLmhvdmVyYWJsZSA9ICQoKTtcblx0XHR0aGlzLmZvY3VzYWJsZSA9ICQoKTtcblx0XHR0aGlzLmNsYXNzZXNFbGVtZW50TG9va3VwID0ge307XG5cblx0XHRpZiAoIGVsZW1lbnQgIT09IHRoaXMgKSB7XG5cdFx0XHQkLmRhdGEoIGVsZW1lbnQsIHRoaXMud2lkZ2V0RnVsbE5hbWUsIHRoaXMgKTtcblx0XHRcdHRoaXMuX29uKCB0cnVlLCB0aGlzLmVsZW1lbnQsIHtcblx0XHRcdFx0cmVtb3ZlOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdFx0aWYgKCBldmVudC50YXJnZXQgPT09IGVsZW1lbnQgKSB7XG5cdFx0XHRcdFx0XHR0aGlzLmRlc3Ryb3koKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH0gKTtcblx0XHRcdHRoaXMuZG9jdW1lbnQgPSAkKCBlbGVtZW50LnN0eWxlID9cblxuXHRcdFx0XHQvLyBFbGVtZW50IHdpdGhpbiB0aGUgZG9jdW1lbnRcblx0XHRcdFx0ZWxlbWVudC5vd25lckRvY3VtZW50IDpcblxuXHRcdFx0XHQvLyBFbGVtZW50IGlzIHdpbmRvdyBvciBkb2N1bWVudFxuXHRcdFx0XHRlbGVtZW50LmRvY3VtZW50IHx8IGVsZW1lbnQgKTtcblx0XHRcdHRoaXMud2luZG93ID0gJCggdGhpcy5kb2N1bWVudFsgMCBdLmRlZmF1bHRWaWV3IHx8IHRoaXMuZG9jdW1lbnRbIDAgXS5wYXJlbnRXaW5kb3cgKTtcblx0XHR9XG5cblx0XHR0aGlzLm9wdGlvbnMgPSAkLndpZGdldC5leHRlbmQoIHt9LFxuXHRcdFx0dGhpcy5vcHRpb25zLFxuXHRcdFx0dGhpcy5fZ2V0Q3JlYXRlT3B0aW9ucygpLFxuXHRcdFx0b3B0aW9ucyApO1xuXG5cdFx0dGhpcy5fY3JlYXRlKCk7XG5cblx0XHRpZiAoIHRoaXMub3B0aW9ucy5kaXNhYmxlZCApIHtcblx0XHRcdHRoaXMuX3NldE9wdGlvbkRpc2FibGVkKCB0aGlzLm9wdGlvbnMuZGlzYWJsZWQgKTtcblx0XHR9XG5cblx0XHR0aGlzLl90cmlnZ2VyKCBcImNyZWF0ZVwiLCBudWxsLCB0aGlzLl9nZXRDcmVhdGVFdmVudERhdGEoKSApO1xuXHRcdHRoaXMuX2luaXQoKTtcblx0fSxcblxuXHRfZ2V0Q3JlYXRlT3B0aW9uczogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHt9O1xuXHR9LFxuXG5cdF9nZXRDcmVhdGVFdmVudERhdGE6ICQubm9vcCxcblxuXHRfY3JlYXRlOiAkLm5vb3AsXG5cblx0X2luaXQ6ICQubm9vcCxcblxuXHRkZXN0cm95OiBmdW5jdGlvbigpIHtcblx0XHR2YXIgdGhhdCA9IHRoaXM7XG5cblx0XHR0aGlzLl9kZXN0cm95KCk7XG5cdFx0JC5lYWNoKCB0aGlzLmNsYXNzZXNFbGVtZW50TG9va3VwLCBmdW5jdGlvbigga2V5LCB2YWx1ZSApIHtcblx0XHRcdHRoYXQuX3JlbW92ZUNsYXNzKCB2YWx1ZSwga2V5ICk7XG5cdFx0fSApO1xuXG5cdFx0Ly8gV2UgY2FuIHByb2JhYmx5IHJlbW92ZSB0aGUgdW5iaW5kIGNhbGxzIGluIDIuMFxuXHRcdC8vIGFsbCBldmVudCBiaW5kaW5ncyBzaG91bGQgZ28gdGhyb3VnaCB0aGlzLl9vbigpXG5cdFx0dGhpcy5lbGVtZW50XG5cdFx0XHQub2ZmKCB0aGlzLmV2ZW50TmFtZXNwYWNlIClcblx0XHRcdC5yZW1vdmVEYXRhKCB0aGlzLndpZGdldEZ1bGxOYW1lICk7XG5cdFx0dGhpcy53aWRnZXQoKVxuXHRcdFx0Lm9mZiggdGhpcy5ldmVudE5hbWVzcGFjZSApXG5cdFx0XHQucmVtb3ZlQXR0ciggXCJhcmlhLWRpc2FibGVkXCIgKTtcblxuXHRcdC8vIENsZWFuIHVwIGV2ZW50cyBhbmQgc3RhdGVzXG5cdFx0dGhpcy5iaW5kaW5ncy5vZmYoIHRoaXMuZXZlbnROYW1lc3BhY2UgKTtcblx0fSxcblxuXHRfZGVzdHJveTogJC5ub29wLFxuXG5cdHdpZGdldDogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMuZWxlbWVudDtcblx0fSxcblxuXHRvcHRpb246IGZ1bmN0aW9uKCBrZXksIHZhbHVlICkge1xuXHRcdHZhciBvcHRpb25zID0ga2V5O1xuXHRcdHZhciBwYXJ0cztcblx0XHR2YXIgY3VyT3B0aW9uO1xuXHRcdHZhciBpO1xuXG5cdFx0aWYgKCBhcmd1bWVudHMubGVuZ3RoID09PSAwICkge1xuXG5cdFx0XHQvLyBEb24ndCByZXR1cm4gYSByZWZlcmVuY2UgdG8gdGhlIGludGVybmFsIGhhc2hcblx0XHRcdHJldHVybiAkLndpZGdldC5leHRlbmQoIHt9LCB0aGlzLm9wdGlvbnMgKTtcblx0XHR9XG5cblx0XHRpZiAoIHR5cGVvZiBrZXkgPT09IFwic3RyaW5nXCIgKSB7XG5cblx0XHRcdC8vIEhhbmRsZSBuZXN0ZWQga2V5cywgZS5nLiwgXCJmb28uYmFyXCIgPT4geyBmb286IHsgYmFyOiBfX18gfSB9XG5cdFx0XHRvcHRpb25zID0ge307XG5cdFx0XHRwYXJ0cyA9IGtleS5zcGxpdCggXCIuXCIgKTtcblx0XHRcdGtleSA9IHBhcnRzLnNoaWZ0KCk7XG5cdFx0XHRpZiAoIHBhcnRzLmxlbmd0aCApIHtcblx0XHRcdFx0Y3VyT3B0aW9uID0gb3B0aW9uc1sga2V5IF0gPSAkLndpZGdldC5leHRlbmQoIHt9LCB0aGlzLm9wdGlvbnNbIGtleSBdICk7XG5cdFx0XHRcdGZvciAoIGkgPSAwOyBpIDwgcGFydHMubGVuZ3RoIC0gMTsgaSsrICkge1xuXHRcdFx0XHRcdGN1ck9wdGlvblsgcGFydHNbIGkgXSBdID0gY3VyT3B0aW9uWyBwYXJ0c1sgaSBdIF0gfHwge307XG5cdFx0XHRcdFx0Y3VyT3B0aW9uID0gY3VyT3B0aW9uWyBwYXJ0c1sgaSBdIF07XG5cdFx0XHRcdH1cblx0XHRcdFx0a2V5ID0gcGFydHMucG9wKCk7XG5cdFx0XHRcdGlmICggYXJndW1lbnRzLmxlbmd0aCA9PT0gMSApIHtcblx0XHRcdFx0XHRyZXR1cm4gY3VyT3B0aW9uWyBrZXkgXSA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IGN1ck9wdGlvblsga2V5IF07XG5cdFx0XHRcdH1cblx0XHRcdFx0Y3VyT3B0aW9uWyBrZXkgXSA9IHZhbHVlO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0aWYgKCBhcmd1bWVudHMubGVuZ3RoID09PSAxICkge1xuXHRcdFx0XHRcdHJldHVybiB0aGlzLm9wdGlvbnNbIGtleSBdID09PSB1bmRlZmluZWQgPyBudWxsIDogdGhpcy5vcHRpb25zWyBrZXkgXTtcblx0XHRcdFx0fVxuXHRcdFx0XHRvcHRpb25zWyBrZXkgXSA9IHZhbHVlO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMuX3NldE9wdGlvbnMoIG9wdGlvbnMgKTtcblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdF9zZXRPcHRpb25zOiBmdW5jdGlvbiggb3B0aW9ucyApIHtcblx0XHR2YXIga2V5O1xuXG5cdFx0Zm9yICgga2V5IGluIG9wdGlvbnMgKSB7XG5cdFx0XHR0aGlzLl9zZXRPcHRpb24oIGtleSwgb3B0aW9uc1sga2V5IF0gKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRfc2V0T3B0aW9uOiBmdW5jdGlvbigga2V5LCB2YWx1ZSApIHtcblx0XHRpZiAoIGtleSA9PT0gXCJjbGFzc2VzXCIgKSB7XG5cdFx0XHR0aGlzLl9zZXRPcHRpb25DbGFzc2VzKCB2YWx1ZSApO1xuXHRcdH1cblxuXHRcdHRoaXMub3B0aW9uc1sga2V5IF0gPSB2YWx1ZTtcblxuXHRcdGlmICgga2V5ID09PSBcImRpc2FibGVkXCIgKSB7XG5cdFx0XHR0aGlzLl9zZXRPcHRpb25EaXNhYmxlZCggdmFsdWUgKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRfc2V0T3B0aW9uQ2xhc3NlczogZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdHZhciBjbGFzc0tleSwgZWxlbWVudHMsIGN1cnJlbnRFbGVtZW50cztcblxuXHRcdGZvciAoIGNsYXNzS2V5IGluIHZhbHVlICkge1xuXHRcdFx0Y3VycmVudEVsZW1lbnRzID0gdGhpcy5jbGFzc2VzRWxlbWVudExvb2t1cFsgY2xhc3NLZXkgXTtcblx0XHRcdGlmICggdmFsdWVbIGNsYXNzS2V5IF0gPT09IHRoaXMub3B0aW9ucy5jbGFzc2VzWyBjbGFzc0tleSBdIHx8XG5cdFx0XHRcdFx0IWN1cnJlbnRFbGVtZW50cyB8fFxuXHRcdFx0XHRcdCFjdXJyZW50RWxlbWVudHMubGVuZ3RoICkge1xuXHRcdFx0XHRjb250aW51ZTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gV2UgYXJlIGRvaW5nIHRoaXMgdG8gY3JlYXRlIGEgbmV3IGpRdWVyeSBvYmplY3QgYmVjYXVzZSB0aGUgX3JlbW92ZUNsYXNzKCkgY2FsbFxuXHRcdFx0Ly8gb24gdGhlIG5leHQgbGluZSBpcyBnb2luZyB0byBkZXN0cm95IHRoZSByZWZlcmVuY2UgdG8gdGhlIGN1cnJlbnQgZWxlbWVudHMgYmVpbmdcblx0XHRcdC8vIHRyYWNrZWQuIFdlIG5lZWQgdG8gc2F2ZSBhIGNvcHkgb2YgdGhpcyBjb2xsZWN0aW9uIHNvIHRoYXQgd2UgY2FuIGFkZCB0aGUgbmV3IGNsYXNzZXNcblx0XHRcdC8vIGJlbG93LlxuXHRcdFx0ZWxlbWVudHMgPSAkKCBjdXJyZW50RWxlbWVudHMuZ2V0KCkgKTtcblx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCBjdXJyZW50RWxlbWVudHMsIGNsYXNzS2V5ICk7XG5cblx0XHRcdC8vIFdlIGRvbid0IHVzZSBfYWRkQ2xhc3MoKSBoZXJlLCBiZWNhdXNlIHRoYXQgdXNlcyB0aGlzLm9wdGlvbnMuY2xhc3Nlc1xuXHRcdFx0Ly8gZm9yIGdlbmVyYXRpbmcgdGhlIHN0cmluZyBvZiBjbGFzc2VzLiBXZSB3YW50IHRvIHVzZSB0aGUgdmFsdWUgcGFzc2VkIGluIGZyb21cblx0XHRcdC8vIF9zZXRPcHRpb24oKSwgdGhpcyBpcyB0aGUgbmV3IHZhbHVlIG9mIHRoZSBjbGFzc2VzIG9wdGlvbiB3aGljaCB3YXMgcGFzc2VkIHRvXG5cdFx0XHQvLyBfc2V0T3B0aW9uKCkuIFdlIHBhc3MgdGhpcyB2YWx1ZSBkaXJlY3RseSB0byBfY2xhc3NlcygpLlxuXHRcdFx0ZWxlbWVudHMuYWRkQ2xhc3MoIHRoaXMuX2NsYXNzZXMoIHtcblx0XHRcdFx0ZWxlbWVudDogZWxlbWVudHMsXG5cdFx0XHRcdGtleXM6IGNsYXNzS2V5LFxuXHRcdFx0XHRjbGFzc2VzOiB2YWx1ZSxcblx0XHRcdFx0YWRkOiB0cnVlXG5cdFx0XHR9ICkgKTtcblx0XHR9XG5cdH0sXG5cblx0X3NldE9wdGlvbkRpc2FibGVkOiBmdW5jdGlvbiggdmFsdWUgKSB7XG5cdFx0dGhpcy5fdG9nZ2xlQ2xhc3MoIHRoaXMud2lkZ2V0KCksIHRoaXMud2lkZ2V0RnVsbE5hbWUgKyBcIi1kaXNhYmxlZFwiLCBudWxsLCAhIXZhbHVlICk7XG5cblx0XHQvLyBJZiB0aGUgd2lkZ2V0IGlzIGJlY29taW5nIGRpc2FibGVkLCB0aGVuIG5vdGhpbmcgaXMgaW50ZXJhY3RpdmVcblx0XHRpZiAoIHZhbHVlICkge1xuXHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIHRoaXMuaG92ZXJhYmxlLCBudWxsLCBcInVpLXN0YXRlLWhvdmVyXCIgKTtcblx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0aGlzLmZvY3VzYWJsZSwgbnVsbCwgXCJ1aS1zdGF0ZS1mb2N1c1wiICk7XG5cdFx0fVxuXHR9LFxuXG5cdGVuYWJsZTogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3NldE9wdGlvbnMoIHsgZGlzYWJsZWQ6IGZhbHNlIH0gKTtcblx0fSxcblxuXHRkaXNhYmxlOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5fc2V0T3B0aW9ucyggeyBkaXNhYmxlZDogdHJ1ZSB9ICk7XG5cdH0sXG5cblx0X2NsYXNzZXM6IGZ1bmN0aW9uKCBvcHRpb25zICkge1xuXHRcdHZhciBmdWxsID0gW107XG5cdFx0dmFyIHRoYXQgPSB0aGlzO1xuXG5cdFx0b3B0aW9ucyA9ICQuZXh0ZW5kKCB7XG5cdFx0XHRlbGVtZW50OiB0aGlzLmVsZW1lbnQsXG5cdFx0XHRjbGFzc2VzOiB0aGlzLm9wdGlvbnMuY2xhc3NlcyB8fCB7fVxuXHRcdH0sIG9wdGlvbnMgKTtcblxuXHRcdGZ1bmN0aW9uIHByb2Nlc3NDbGFzc1N0cmluZyggY2xhc3NlcywgY2hlY2tPcHRpb24gKSB7XG5cdFx0XHR2YXIgY3VycmVudCwgaTtcblx0XHRcdGZvciAoIGkgPSAwOyBpIDwgY2xhc3Nlcy5sZW5ndGg7IGkrKyApIHtcblx0XHRcdFx0Y3VycmVudCA9IHRoYXQuY2xhc3Nlc0VsZW1lbnRMb29rdXBbIGNsYXNzZXNbIGkgXSBdIHx8ICQoKTtcblx0XHRcdFx0aWYgKCBvcHRpb25zLmFkZCApIHtcblx0XHRcdFx0XHRjdXJyZW50ID0gJCggJC51bmlxdWUoIGN1cnJlbnQuZ2V0KCkuY29uY2F0KCBvcHRpb25zLmVsZW1lbnQuZ2V0KCkgKSApICk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0Y3VycmVudCA9ICQoIGN1cnJlbnQubm90KCBvcHRpb25zLmVsZW1lbnQgKS5nZXQoKSApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHRoYXQuY2xhc3Nlc0VsZW1lbnRMb29rdXBbIGNsYXNzZXNbIGkgXSBdID0gY3VycmVudDtcblx0XHRcdFx0ZnVsbC5wdXNoKCBjbGFzc2VzWyBpIF0gKTtcblx0XHRcdFx0aWYgKCBjaGVja09wdGlvbiAmJiBvcHRpb25zLmNsYXNzZXNbIGNsYXNzZXNbIGkgXSBdICkge1xuXHRcdFx0XHRcdGZ1bGwucHVzaCggb3B0aW9ucy5jbGFzc2VzWyBjbGFzc2VzWyBpIF0gXSApO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dGhpcy5fb24oIG9wdGlvbnMuZWxlbWVudCwge1xuXHRcdFx0XCJyZW1vdmVcIjogXCJfdW50cmFja0NsYXNzZXNFbGVtZW50XCJcblx0XHR9ICk7XG5cblx0XHRpZiAoIG9wdGlvbnMua2V5cyApIHtcblx0XHRcdHByb2Nlc3NDbGFzc1N0cmluZyggb3B0aW9ucy5rZXlzLm1hdGNoKCAvXFxTKy9nICkgfHwgW10sIHRydWUgKTtcblx0XHR9XG5cdFx0aWYgKCBvcHRpb25zLmV4dHJhICkge1xuXHRcdFx0cHJvY2Vzc0NsYXNzU3RyaW5nKCBvcHRpb25zLmV4dHJhLm1hdGNoKCAvXFxTKy9nICkgfHwgW10gKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZnVsbC5qb2luKCBcIiBcIiApO1xuXHR9LFxuXG5cdF91bnRyYWNrQ2xhc3Nlc0VsZW1lbnQ6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgdGhhdCA9IHRoaXM7XG5cdFx0JC5lYWNoKCB0aGF0LmNsYXNzZXNFbGVtZW50TG9va3VwLCBmdW5jdGlvbigga2V5LCB2YWx1ZSApIHtcblx0XHRcdGlmICggJC5pbkFycmF5KCBldmVudC50YXJnZXQsIHZhbHVlICkgIT09IC0xICkge1xuXHRcdFx0XHR0aGF0LmNsYXNzZXNFbGVtZW50TG9va3VwWyBrZXkgXSA9ICQoIHZhbHVlLm5vdCggZXZlbnQudGFyZ2V0ICkuZ2V0KCkgKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdH0sXG5cblx0X3JlbW92ZUNsYXNzOiBmdW5jdGlvbiggZWxlbWVudCwga2V5cywgZXh0cmEgKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3RvZ2dsZUNsYXNzKCBlbGVtZW50LCBrZXlzLCBleHRyYSwgZmFsc2UgKTtcblx0fSxcblxuXHRfYWRkQ2xhc3M6IGZ1bmN0aW9uKCBlbGVtZW50LCBrZXlzLCBleHRyYSApIHtcblx0XHRyZXR1cm4gdGhpcy5fdG9nZ2xlQ2xhc3MoIGVsZW1lbnQsIGtleXMsIGV4dHJhLCB0cnVlICk7XG5cdH0sXG5cblx0X3RvZ2dsZUNsYXNzOiBmdW5jdGlvbiggZWxlbWVudCwga2V5cywgZXh0cmEsIGFkZCApIHtcblx0XHRhZGQgPSAoIHR5cGVvZiBhZGQgPT09IFwiYm9vbGVhblwiICkgPyBhZGQgOiBleHRyYTtcblx0XHR2YXIgc2hpZnQgPSAoIHR5cGVvZiBlbGVtZW50ID09PSBcInN0cmluZ1wiIHx8IGVsZW1lbnQgPT09IG51bGwgKSxcblx0XHRcdG9wdGlvbnMgPSB7XG5cdFx0XHRcdGV4dHJhOiBzaGlmdCA/IGtleXMgOiBleHRyYSxcblx0XHRcdFx0a2V5czogc2hpZnQgPyBlbGVtZW50IDoga2V5cyxcblx0XHRcdFx0ZWxlbWVudDogc2hpZnQgPyB0aGlzLmVsZW1lbnQgOiBlbGVtZW50LFxuXHRcdFx0XHRhZGQ6IGFkZFxuXHRcdFx0fTtcblx0XHRvcHRpb25zLmVsZW1lbnQudG9nZ2xlQ2xhc3MoIHRoaXMuX2NsYXNzZXMoIG9wdGlvbnMgKSwgYWRkICk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X29uOiBmdW5jdGlvbiggc3VwcHJlc3NEaXNhYmxlZENoZWNrLCBlbGVtZW50LCBoYW5kbGVycyApIHtcblx0XHR2YXIgZGVsZWdhdGVFbGVtZW50O1xuXHRcdHZhciBpbnN0YW5jZSA9IHRoaXM7XG5cblx0XHQvLyBObyBzdXBwcmVzc0Rpc2FibGVkQ2hlY2sgZmxhZywgc2h1ZmZsZSBhcmd1bWVudHNcblx0XHRpZiAoIHR5cGVvZiBzdXBwcmVzc0Rpc2FibGVkQ2hlY2sgIT09IFwiYm9vbGVhblwiICkge1xuXHRcdFx0aGFuZGxlcnMgPSBlbGVtZW50O1xuXHRcdFx0ZWxlbWVudCA9IHN1cHByZXNzRGlzYWJsZWRDaGVjaztcblx0XHRcdHN1cHByZXNzRGlzYWJsZWRDaGVjayA9IGZhbHNlO1xuXHRcdH1cblxuXHRcdC8vIE5vIGVsZW1lbnQgYXJndW1lbnQsIHNodWZmbGUgYW5kIHVzZSB0aGlzLmVsZW1lbnRcblx0XHRpZiAoICFoYW5kbGVycyApIHtcblx0XHRcdGhhbmRsZXJzID0gZWxlbWVudDtcblx0XHRcdGVsZW1lbnQgPSB0aGlzLmVsZW1lbnQ7XG5cdFx0XHRkZWxlZ2F0ZUVsZW1lbnQgPSB0aGlzLndpZGdldCgpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRlbGVtZW50ID0gZGVsZWdhdGVFbGVtZW50ID0gJCggZWxlbWVudCApO1xuXHRcdFx0dGhpcy5iaW5kaW5ncyA9IHRoaXMuYmluZGluZ3MuYWRkKCBlbGVtZW50ICk7XG5cdFx0fVxuXG5cdFx0JC5lYWNoKCBoYW5kbGVycywgZnVuY3Rpb24oIGV2ZW50LCBoYW5kbGVyICkge1xuXHRcdFx0ZnVuY3Rpb24gaGFuZGxlclByb3h5KCkge1xuXG5cdFx0XHRcdC8vIEFsbG93IHdpZGdldHMgdG8gY3VzdG9taXplIHRoZSBkaXNhYmxlZCBoYW5kbGluZ1xuXHRcdFx0XHQvLyAtIGRpc2FibGVkIGFzIGFuIGFycmF5IGluc3RlYWQgb2YgYm9vbGVhblxuXHRcdFx0XHQvLyAtIGRpc2FibGVkIGNsYXNzIGFzIG1ldGhvZCBmb3IgZGlzYWJsaW5nIGluZGl2aWR1YWwgcGFydHNcblx0XHRcdFx0aWYgKCAhc3VwcHJlc3NEaXNhYmxlZENoZWNrICYmXG5cdFx0XHRcdFx0XHQoIGluc3RhbmNlLm9wdGlvbnMuZGlzYWJsZWQgPT09IHRydWUgfHxcblx0XHRcdFx0XHRcdCQoIHRoaXMgKS5oYXNDbGFzcyggXCJ1aS1zdGF0ZS1kaXNhYmxlZFwiICkgKSApIHtcblx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdH1cblx0XHRcdFx0cmV0dXJuICggdHlwZW9mIGhhbmRsZXIgPT09IFwic3RyaW5nXCIgPyBpbnN0YW5jZVsgaGFuZGxlciBdIDogaGFuZGxlciApXG5cdFx0XHRcdFx0LmFwcGx5KCBpbnN0YW5jZSwgYXJndW1lbnRzICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIENvcHkgdGhlIGd1aWQgc28gZGlyZWN0IHVuYmluZGluZyB3b3Jrc1xuXHRcdFx0aWYgKCB0eXBlb2YgaGFuZGxlciAhPT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdFx0aGFuZGxlclByb3h5Lmd1aWQgPSBoYW5kbGVyLmd1aWQgPVxuXHRcdFx0XHRcdGhhbmRsZXIuZ3VpZCB8fCBoYW5kbGVyUHJveHkuZ3VpZCB8fCAkLmd1aWQrKztcblx0XHRcdH1cblxuXHRcdFx0dmFyIG1hdGNoID0gZXZlbnQubWF0Y2goIC9eKFtcXHc6LV0qKVxccyooLiopJC8gKTtcblx0XHRcdHZhciBldmVudE5hbWUgPSBtYXRjaFsgMSBdICsgaW5zdGFuY2UuZXZlbnROYW1lc3BhY2U7XG5cdFx0XHR2YXIgc2VsZWN0b3IgPSBtYXRjaFsgMiBdO1xuXG5cdFx0XHRpZiAoIHNlbGVjdG9yICkge1xuXHRcdFx0XHRkZWxlZ2F0ZUVsZW1lbnQub24oIGV2ZW50TmFtZSwgc2VsZWN0b3IsIGhhbmRsZXJQcm94eSApO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0ZWxlbWVudC5vbiggZXZlbnROYW1lLCBoYW5kbGVyUHJveHkgKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdH0sXG5cblx0X29mZjogZnVuY3Rpb24oIGVsZW1lbnQsIGV2ZW50TmFtZSApIHtcblx0XHRldmVudE5hbWUgPSAoIGV2ZW50TmFtZSB8fCBcIlwiICkuc3BsaXQoIFwiIFwiICkuam9pbiggdGhpcy5ldmVudE5hbWVzcGFjZSArIFwiIFwiICkgK1xuXHRcdFx0dGhpcy5ldmVudE5hbWVzcGFjZTtcblx0XHRlbGVtZW50Lm9mZiggZXZlbnROYW1lICkub2ZmKCBldmVudE5hbWUgKTtcblxuXHRcdC8vIENsZWFyIHRoZSBzdGFjayB0byBhdm9pZCBtZW1vcnkgbGVha3MgKCMxMDA1Nilcblx0XHR0aGlzLmJpbmRpbmdzID0gJCggdGhpcy5iaW5kaW5ncy5ub3QoIGVsZW1lbnQgKS5nZXQoKSApO1xuXHRcdHRoaXMuZm9jdXNhYmxlID0gJCggdGhpcy5mb2N1c2FibGUubm90KCBlbGVtZW50ICkuZ2V0KCkgKTtcblx0XHR0aGlzLmhvdmVyYWJsZSA9ICQoIHRoaXMuaG92ZXJhYmxlLm5vdCggZWxlbWVudCApLmdldCgpICk7XG5cdH0sXG5cblx0X2RlbGF5OiBmdW5jdGlvbiggaGFuZGxlciwgZGVsYXkgKSB7XG5cdFx0ZnVuY3Rpb24gaGFuZGxlclByb3h5KCkge1xuXHRcdFx0cmV0dXJuICggdHlwZW9mIGhhbmRsZXIgPT09IFwic3RyaW5nXCIgPyBpbnN0YW5jZVsgaGFuZGxlciBdIDogaGFuZGxlciApXG5cdFx0XHRcdC5hcHBseSggaW5zdGFuY2UsIGFyZ3VtZW50cyApO1xuXHRcdH1cblx0XHR2YXIgaW5zdGFuY2UgPSB0aGlzO1xuXHRcdHJldHVybiBzZXRUaW1lb3V0KCBoYW5kbGVyUHJveHksIGRlbGF5IHx8IDAgKTtcblx0fSxcblxuXHRfaG92ZXJhYmxlOiBmdW5jdGlvbiggZWxlbWVudCApIHtcblx0XHR0aGlzLmhvdmVyYWJsZSA9IHRoaXMuaG92ZXJhYmxlLmFkZCggZWxlbWVudCApO1xuXHRcdHRoaXMuX29uKCBlbGVtZW50LCB7XG5cdFx0XHRtb3VzZWVudGVyOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdHRoaXMuX2FkZENsYXNzKCAkKCBldmVudC5jdXJyZW50VGFyZ2V0ICksIG51bGwsIFwidWktc3RhdGUtaG92ZXJcIiApO1xuXHRcdFx0fSxcblx0XHRcdG1vdXNlbGVhdmU6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoICQoIGV2ZW50LmN1cnJlbnRUYXJnZXQgKSwgbnVsbCwgXCJ1aS1zdGF0ZS1ob3ZlclwiICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXHR9LFxuXG5cdF9mb2N1c2FibGU6IGZ1bmN0aW9uKCBlbGVtZW50ICkge1xuXHRcdHRoaXMuZm9jdXNhYmxlID0gdGhpcy5mb2N1c2FibGUuYWRkKCBlbGVtZW50ICk7XG5cdFx0dGhpcy5fb24oIGVsZW1lbnQsIHtcblx0XHRcdGZvY3VzaW46IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0dGhpcy5fYWRkQ2xhc3MoICQoIGV2ZW50LmN1cnJlbnRUYXJnZXQgKSwgbnVsbCwgXCJ1aS1zdGF0ZS1mb2N1c1wiICk7XG5cdFx0XHR9LFxuXHRcdFx0Zm9jdXNvdXQ6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoICQoIGV2ZW50LmN1cnJlbnRUYXJnZXQgKSwgbnVsbCwgXCJ1aS1zdGF0ZS1mb2N1c1wiICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXHR9LFxuXG5cdF90cmlnZ2VyOiBmdW5jdGlvbiggdHlwZSwgZXZlbnQsIGRhdGEgKSB7XG5cdFx0dmFyIHByb3AsIG9yaWc7XG5cdFx0dmFyIGNhbGxiYWNrID0gdGhpcy5vcHRpb25zWyB0eXBlIF07XG5cblx0XHRkYXRhID0gZGF0YSB8fCB7fTtcblx0XHRldmVudCA9ICQuRXZlbnQoIGV2ZW50ICk7XG5cdFx0ZXZlbnQudHlwZSA9ICggdHlwZSA9PT0gdGhpcy53aWRnZXRFdmVudFByZWZpeCA/XG5cdFx0XHR0eXBlIDpcblx0XHRcdHRoaXMud2lkZ2V0RXZlbnRQcmVmaXggKyB0eXBlICkudG9Mb3dlckNhc2UoKTtcblxuXHRcdC8vIFRoZSBvcmlnaW5hbCBldmVudCBtYXkgY29tZSBmcm9tIGFueSBlbGVtZW50XG5cdFx0Ly8gc28gd2UgbmVlZCB0byByZXNldCB0aGUgdGFyZ2V0IG9uIHRoZSBuZXcgZXZlbnRcblx0XHRldmVudC50YXJnZXQgPSB0aGlzLmVsZW1lbnRbIDAgXTtcblxuXHRcdC8vIENvcHkgb3JpZ2luYWwgZXZlbnQgcHJvcGVydGllcyBvdmVyIHRvIHRoZSBuZXcgZXZlbnRcblx0XHRvcmlnID0gZXZlbnQub3JpZ2luYWxFdmVudDtcblx0XHRpZiAoIG9yaWcgKSB7XG5cdFx0XHRmb3IgKCBwcm9wIGluIG9yaWcgKSB7XG5cdFx0XHRcdGlmICggISggcHJvcCBpbiBldmVudCApICkge1xuXHRcdFx0XHRcdGV2ZW50WyBwcm9wIF0gPSBvcmlnWyBwcm9wIF07XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLmVsZW1lbnQudHJpZ2dlciggZXZlbnQsIGRhdGEgKTtcblx0XHRyZXR1cm4gISggJC5pc0Z1bmN0aW9uKCBjYWxsYmFjayApICYmXG5cdFx0XHRjYWxsYmFjay5hcHBseSggdGhpcy5lbGVtZW50WyAwIF0sIFsgZXZlbnQgXS5jb25jYXQoIGRhdGEgKSApID09PSBmYWxzZSB8fFxuXHRcdFx0ZXZlbnQuaXNEZWZhdWx0UHJldmVudGVkKCkgKTtcblx0fVxufTtcblxuJC5lYWNoKCB7IHNob3c6IFwiZmFkZUluXCIsIGhpZGU6IFwiZmFkZU91dFwiIH0sIGZ1bmN0aW9uKCBtZXRob2QsIGRlZmF1bHRFZmZlY3QgKSB7XG5cdCQuV2lkZ2V0LnByb3RvdHlwZVsgXCJfXCIgKyBtZXRob2QgXSA9IGZ1bmN0aW9uKCBlbGVtZW50LCBvcHRpb25zLCBjYWxsYmFjayApIHtcblx0XHRpZiAoIHR5cGVvZiBvcHRpb25zID09PSBcInN0cmluZ1wiICkge1xuXHRcdFx0b3B0aW9ucyA9IHsgZWZmZWN0OiBvcHRpb25zIH07XG5cdFx0fVxuXG5cdFx0dmFyIGhhc09wdGlvbnM7XG5cdFx0dmFyIGVmZmVjdE5hbWUgPSAhb3B0aW9ucyA/XG5cdFx0XHRtZXRob2QgOlxuXHRcdFx0b3B0aW9ucyA9PT0gdHJ1ZSB8fCB0eXBlb2Ygb3B0aW9ucyA9PT0gXCJudW1iZXJcIiA/XG5cdFx0XHRcdGRlZmF1bHRFZmZlY3QgOlxuXHRcdFx0XHRvcHRpb25zLmVmZmVjdCB8fCBkZWZhdWx0RWZmZWN0O1xuXG5cdFx0b3B0aW9ucyA9IG9wdGlvbnMgfHwge307XG5cdFx0aWYgKCB0eXBlb2Ygb3B0aW9ucyA9PT0gXCJudW1iZXJcIiApIHtcblx0XHRcdG9wdGlvbnMgPSB7IGR1cmF0aW9uOiBvcHRpb25zIH07XG5cdFx0fVxuXG5cdFx0aGFzT3B0aW9ucyA9ICEkLmlzRW1wdHlPYmplY3QoIG9wdGlvbnMgKTtcblx0XHRvcHRpb25zLmNvbXBsZXRlID0gY2FsbGJhY2s7XG5cblx0XHRpZiAoIG9wdGlvbnMuZGVsYXkgKSB7XG5cdFx0XHRlbGVtZW50LmRlbGF5KCBvcHRpb25zLmRlbGF5ICk7XG5cdFx0fVxuXG5cdFx0aWYgKCBoYXNPcHRpb25zICYmICQuZWZmZWN0cyAmJiAkLmVmZmVjdHMuZWZmZWN0WyBlZmZlY3ROYW1lIF0gKSB7XG5cdFx0XHRlbGVtZW50WyBtZXRob2QgXSggb3B0aW9ucyApO1xuXHRcdH0gZWxzZSBpZiAoIGVmZmVjdE5hbWUgIT09IG1ldGhvZCAmJiBlbGVtZW50WyBlZmZlY3ROYW1lIF0gKSB7XG5cdFx0XHRlbGVtZW50WyBlZmZlY3ROYW1lIF0oIG9wdGlvbnMuZHVyYXRpb24sIG9wdGlvbnMuZWFzaW5nLCBjYWxsYmFjayApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRlbGVtZW50LnF1ZXVlKCBmdW5jdGlvbiggbmV4dCApIHtcblx0XHRcdFx0JCggdGhpcyApWyBtZXRob2QgXSgpO1xuXHRcdFx0XHRpZiAoIGNhbGxiYWNrICkge1xuXHRcdFx0XHRcdGNhbGxiYWNrLmNhbGwoIGVsZW1lbnRbIDAgXSApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdG5leHQoKTtcblx0XHRcdH0gKTtcblx0XHR9XG5cdH07XG59ICk7XG5cbnZhciB3aWRnZXQgPSAkLndpZGdldDtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBQb3NpdGlvbiAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICpcbiAqIGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL3Bvc2l0aW9uL1xuICovXG5cbi8vPj5sYWJlbDogUG9zaXRpb25cbi8vPj5ncm91cDogQ29yZVxuLy8+PmRlc2NyaXB0aW9uOiBQb3NpdGlvbnMgZWxlbWVudHMgcmVsYXRpdmUgdG8gb3RoZXIgZWxlbWVudHMuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vcG9zaXRpb24vXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vcG9zaXRpb24vXG5cblxuKCBmdW5jdGlvbigpIHtcbnZhciBjYWNoZWRTY3JvbGxiYXJXaWR0aCxcblx0bWF4ID0gTWF0aC5tYXgsXG5cdGFicyA9IE1hdGguYWJzLFxuXHRyaG9yaXpvbnRhbCA9IC9sZWZ0fGNlbnRlcnxyaWdodC8sXG5cdHJ2ZXJ0aWNhbCA9IC90b3B8Y2VudGVyfGJvdHRvbS8sXG5cdHJvZmZzZXQgPSAvW1xcK1xcLV1cXGQrKFxcLltcXGRdKyk/JT8vLFxuXHRycG9zaXRpb24gPSAvXlxcdysvLFxuXHRycGVyY2VudCA9IC8lJC8sXG5cdF9wb3NpdGlvbiA9ICQuZm4ucG9zaXRpb247XG5cbmZ1bmN0aW9uIGdldE9mZnNldHMoIG9mZnNldHMsIHdpZHRoLCBoZWlnaHQgKSB7XG5cdHJldHVybiBbXG5cdFx0cGFyc2VGbG9hdCggb2Zmc2V0c1sgMCBdICkgKiAoIHJwZXJjZW50LnRlc3QoIG9mZnNldHNbIDAgXSApID8gd2lkdGggLyAxMDAgOiAxICksXG5cdFx0cGFyc2VGbG9hdCggb2Zmc2V0c1sgMSBdICkgKiAoIHJwZXJjZW50LnRlc3QoIG9mZnNldHNbIDEgXSApID8gaGVpZ2h0IC8gMTAwIDogMSApXG5cdF07XG59XG5cbmZ1bmN0aW9uIHBhcnNlQ3NzKCBlbGVtZW50LCBwcm9wZXJ0eSApIHtcblx0cmV0dXJuIHBhcnNlSW50KCAkLmNzcyggZWxlbWVudCwgcHJvcGVydHkgKSwgMTAgKSB8fCAwO1xufVxuXG5mdW5jdGlvbiBnZXREaW1lbnNpb25zKCBlbGVtICkge1xuXHR2YXIgcmF3ID0gZWxlbVsgMCBdO1xuXHRpZiAoIHJhdy5ub2RlVHlwZSA9PT0gOSApIHtcblx0XHRyZXR1cm4ge1xuXHRcdFx0d2lkdGg6IGVsZW0ud2lkdGgoKSxcblx0XHRcdGhlaWdodDogZWxlbS5oZWlnaHQoKSxcblx0XHRcdG9mZnNldDogeyB0b3A6IDAsIGxlZnQ6IDAgfVxuXHRcdH07XG5cdH1cblx0aWYgKCAkLmlzV2luZG93KCByYXcgKSApIHtcblx0XHRyZXR1cm4ge1xuXHRcdFx0d2lkdGg6IGVsZW0ud2lkdGgoKSxcblx0XHRcdGhlaWdodDogZWxlbS5oZWlnaHQoKSxcblx0XHRcdG9mZnNldDogeyB0b3A6IGVsZW0uc2Nyb2xsVG9wKCksIGxlZnQ6IGVsZW0uc2Nyb2xsTGVmdCgpIH1cblx0XHR9O1xuXHR9XG5cdGlmICggcmF3LnByZXZlbnREZWZhdWx0ICkge1xuXHRcdHJldHVybiB7XG5cdFx0XHR3aWR0aDogMCxcblx0XHRcdGhlaWdodDogMCxcblx0XHRcdG9mZnNldDogeyB0b3A6IHJhdy5wYWdlWSwgbGVmdDogcmF3LnBhZ2VYIH1cblx0XHR9O1xuXHR9XG5cdHJldHVybiB7XG5cdFx0d2lkdGg6IGVsZW0ub3V0ZXJXaWR0aCgpLFxuXHRcdGhlaWdodDogZWxlbS5vdXRlckhlaWdodCgpLFxuXHRcdG9mZnNldDogZWxlbS5vZmZzZXQoKVxuXHR9O1xufVxuXG4kLnBvc2l0aW9uID0ge1xuXHRzY3JvbGxiYXJXaWR0aDogZnVuY3Rpb24oKSB7XG5cdFx0aWYgKCBjYWNoZWRTY3JvbGxiYXJXaWR0aCAhPT0gdW5kZWZpbmVkICkge1xuXHRcdFx0cmV0dXJuIGNhY2hlZFNjcm9sbGJhcldpZHRoO1xuXHRcdH1cblx0XHR2YXIgdzEsIHcyLFxuXHRcdFx0ZGl2ID0gJCggXCI8ZGl2IFwiICtcblx0XHRcdFx0XCJzdHlsZT0nZGlzcGxheTpibG9jaztwb3NpdGlvbjphYnNvbHV0ZTt3aWR0aDo1MHB4O2hlaWdodDo1MHB4O292ZXJmbG93OmhpZGRlbjsnPlwiICtcblx0XHRcdFx0XCI8ZGl2IHN0eWxlPSdoZWlnaHQ6MTAwcHg7d2lkdGg6YXV0bzsnPjwvZGl2PjwvZGl2PlwiICksXG5cdFx0XHRpbm5lckRpdiA9IGRpdi5jaGlsZHJlbigpWyAwIF07XG5cblx0XHQkKCBcImJvZHlcIiApLmFwcGVuZCggZGl2ICk7XG5cdFx0dzEgPSBpbm5lckRpdi5vZmZzZXRXaWR0aDtcblx0XHRkaXYuY3NzKCBcIm92ZXJmbG93XCIsIFwic2Nyb2xsXCIgKTtcblxuXHRcdHcyID0gaW5uZXJEaXYub2Zmc2V0V2lkdGg7XG5cblx0XHRpZiAoIHcxID09PSB3MiApIHtcblx0XHRcdHcyID0gZGl2WyAwIF0uY2xpZW50V2lkdGg7XG5cdFx0fVxuXG5cdFx0ZGl2LnJlbW92ZSgpO1xuXG5cdFx0cmV0dXJuICggY2FjaGVkU2Nyb2xsYmFyV2lkdGggPSB3MSAtIHcyICk7XG5cdH0sXG5cdGdldFNjcm9sbEluZm86IGZ1bmN0aW9uKCB3aXRoaW4gKSB7XG5cdFx0dmFyIG92ZXJmbG93WCA9IHdpdGhpbi5pc1dpbmRvdyB8fCB3aXRoaW4uaXNEb2N1bWVudCA/IFwiXCIgOlxuXHRcdFx0XHR3aXRoaW4uZWxlbWVudC5jc3MoIFwib3ZlcmZsb3cteFwiICksXG5cdFx0XHRvdmVyZmxvd1kgPSB3aXRoaW4uaXNXaW5kb3cgfHwgd2l0aGluLmlzRG9jdW1lbnQgPyBcIlwiIDpcblx0XHRcdFx0d2l0aGluLmVsZW1lbnQuY3NzKCBcIm92ZXJmbG93LXlcIiApLFxuXHRcdFx0aGFzT3ZlcmZsb3dYID0gb3ZlcmZsb3dYID09PSBcInNjcm9sbFwiIHx8XG5cdFx0XHRcdCggb3ZlcmZsb3dYID09PSBcImF1dG9cIiAmJiB3aXRoaW4ud2lkdGggPCB3aXRoaW4uZWxlbWVudFsgMCBdLnNjcm9sbFdpZHRoICksXG5cdFx0XHRoYXNPdmVyZmxvd1kgPSBvdmVyZmxvd1kgPT09IFwic2Nyb2xsXCIgfHxcblx0XHRcdFx0KCBvdmVyZmxvd1kgPT09IFwiYXV0b1wiICYmIHdpdGhpbi5oZWlnaHQgPCB3aXRoaW4uZWxlbWVudFsgMCBdLnNjcm9sbEhlaWdodCApO1xuXHRcdHJldHVybiB7XG5cdFx0XHR3aWR0aDogaGFzT3ZlcmZsb3dZID8gJC5wb3NpdGlvbi5zY3JvbGxiYXJXaWR0aCgpIDogMCxcblx0XHRcdGhlaWdodDogaGFzT3ZlcmZsb3dYID8gJC5wb3NpdGlvbi5zY3JvbGxiYXJXaWR0aCgpIDogMFxuXHRcdH07XG5cdH0sXG5cdGdldFdpdGhpbkluZm86IGZ1bmN0aW9uKCBlbGVtZW50ICkge1xuXHRcdHZhciB3aXRoaW5FbGVtZW50ID0gJCggZWxlbWVudCB8fCB3aW5kb3cgKSxcblx0XHRcdGlzV2luZG93ID0gJC5pc1dpbmRvdyggd2l0aGluRWxlbWVudFsgMCBdICksXG5cdFx0XHRpc0RvY3VtZW50ID0gISF3aXRoaW5FbGVtZW50WyAwIF0gJiYgd2l0aGluRWxlbWVudFsgMCBdLm5vZGVUeXBlID09PSA5LFxuXHRcdFx0aGFzT2Zmc2V0ID0gIWlzV2luZG93ICYmICFpc0RvY3VtZW50O1xuXHRcdHJldHVybiB7XG5cdFx0XHRlbGVtZW50OiB3aXRoaW5FbGVtZW50LFxuXHRcdFx0aXNXaW5kb3c6IGlzV2luZG93LFxuXHRcdFx0aXNEb2N1bWVudDogaXNEb2N1bWVudCxcblx0XHRcdG9mZnNldDogaGFzT2Zmc2V0ID8gJCggZWxlbWVudCApLm9mZnNldCgpIDogeyBsZWZ0OiAwLCB0b3A6IDAgfSxcblx0XHRcdHNjcm9sbExlZnQ6IHdpdGhpbkVsZW1lbnQuc2Nyb2xsTGVmdCgpLFxuXHRcdFx0c2Nyb2xsVG9wOiB3aXRoaW5FbGVtZW50LnNjcm9sbFRvcCgpLFxuXHRcdFx0d2lkdGg6IHdpdGhpbkVsZW1lbnQub3V0ZXJXaWR0aCgpLFxuXHRcdFx0aGVpZ2h0OiB3aXRoaW5FbGVtZW50Lm91dGVySGVpZ2h0KClcblx0XHR9O1xuXHR9XG59O1xuXG4kLmZuLnBvc2l0aW9uID0gZnVuY3Rpb24oIG9wdGlvbnMgKSB7XG5cdGlmICggIW9wdGlvbnMgfHwgIW9wdGlvbnMub2YgKSB7XG5cdFx0cmV0dXJuIF9wb3NpdGlvbi5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdH1cblxuXHQvLyBNYWtlIGEgY29weSwgd2UgZG9uJ3Qgd2FudCB0byBtb2RpZnkgYXJndW1lbnRzXG5cdG9wdGlvbnMgPSAkLmV4dGVuZCgge30sIG9wdGlvbnMgKTtcblxuXHR2YXIgYXRPZmZzZXQsIHRhcmdldFdpZHRoLCB0YXJnZXRIZWlnaHQsIHRhcmdldE9mZnNldCwgYmFzZVBvc2l0aW9uLCBkaW1lbnNpb25zLFxuXHRcdHRhcmdldCA9ICQoIG9wdGlvbnMub2YgKSxcblx0XHR3aXRoaW4gPSAkLnBvc2l0aW9uLmdldFdpdGhpbkluZm8oIG9wdGlvbnMud2l0aGluICksXG5cdFx0c2Nyb2xsSW5mbyA9ICQucG9zaXRpb24uZ2V0U2Nyb2xsSW5mbyggd2l0aGluICksXG5cdFx0Y29sbGlzaW9uID0gKCBvcHRpb25zLmNvbGxpc2lvbiB8fCBcImZsaXBcIiApLnNwbGl0KCBcIiBcIiApLFxuXHRcdG9mZnNldHMgPSB7fTtcblxuXHRkaW1lbnNpb25zID0gZ2V0RGltZW5zaW9ucyggdGFyZ2V0ICk7XG5cdGlmICggdGFyZ2V0WyAwIF0ucHJldmVudERlZmF1bHQgKSB7XG5cblx0XHQvLyBGb3JjZSBsZWZ0IHRvcCB0byBhbGxvdyBmbGlwcGluZ1xuXHRcdG9wdGlvbnMuYXQgPSBcImxlZnQgdG9wXCI7XG5cdH1cblx0dGFyZ2V0V2lkdGggPSBkaW1lbnNpb25zLndpZHRoO1xuXHR0YXJnZXRIZWlnaHQgPSBkaW1lbnNpb25zLmhlaWdodDtcblx0dGFyZ2V0T2Zmc2V0ID0gZGltZW5zaW9ucy5vZmZzZXQ7XG5cblx0Ly8gQ2xvbmUgdG8gcmV1c2Ugb3JpZ2luYWwgdGFyZ2V0T2Zmc2V0IGxhdGVyXG5cdGJhc2VQb3NpdGlvbiA9ICQuZXh0ZW5kKCB7fSwgdGFyZ2V0T2Zmc2V0ICk7XG5cblx0Ly8gRm9yY2UgbXkgYW5kIGF0IHRvIGhhdmUgdmFsaWQgaG9yaXpvbnRhbCBhbmQgdmVydGljYWwgcG9zaXRpb25zXG5cdC8vIGlmIGEgdmFsdWUgaXMgbWlzc2luZyBvciBpbnZhbGlkLCBpdCB3aWxsIGJlIGNvbnZlcnRlZCB0byBjZW50ZXJcblx0JC5lYWNoKCBbIFwibXlcIiwgXCJhdFwiIF0sIGZ1bmN0aW9uKCkge1xuXHRcdHZhciBwb3MgPSAoIG9wdGlvbnNbIHRoaXMgXSB8fCBcIlwiICkuc3BsaXQoIFwiIFwiICksXG5cdFx0XHRob3Jpem9udGFsT2Zmc2V0LFxuXHRcdFx0dmVydGljYWxPZmZzZXQ7XG5cblx0XHRpZiAoIHBvcy5sZW5ndGggPT09IDEgKSB7XG5cdFx0XHRwb3MgPSByaG9yaXpvbnRhbC50ZXN0KCBwb3NbIDAgXSApID9cblx0XHRcdFx0cG9zLmNvbmNhdCggWyBcImNlbnRlclwiIF0gKSA6XG5cdFx0XHRcdHJ2ZXJ0aWNhbC50ZXN0KCBwb3NbIDAgXSApID9cblx0XHRcdFx0XHRbIFwiY2VudGVyXCIgXS5jb25jYXQoIHBvcyApIDpcblx0XHRcdFx0XHRbIFwiY2VudGVyXCIsIFwiY2VudGVyXCIgXTtcblx0XHR9XG5cdFx0cG9zWyAwIF0gPSByaG9yaXpvbnRhbC50ZXN0KCBwb3NbIDAgXSApID8gcG9zWyAwIF0gOiBcImNlbnRlclwiO1xuXHRcdHBvc1sgMSBdID0gcnZlcnRpY2FsLnRlc3QoIHBvc1sgMSBdICkgPyBwb3NbIDEgXSA6IFwiY2VudGVyXCI7XG5cblx0XHQvLyBDYWxjdWxhdGUgb2Zmc2V0c1xuXHRcdGhvcml6b250YWxPZmZzZXQgPSByb2Zmc2V0LmV4ZWMoIHBvc1sgMCBdICk7XG5cdFx0dmVydGljYWxPZmZzZXQgPSByb2Zmc2V0LmV4ZWMoIHBvc1sgMSBdICk7XG5cdFx0b2Zmc2V0c1sgdGhpcyBdID0gW1xuXHRcdFx0aG9yaXpvbnRhbE9mZnNldCA/IGhvcml6b250YWxPZmZzZXRbIDAgXSA6IDAsXG5cdFx0XHR2ZXJ0aWNhbE9mZnNldCA/IHZlcnRpY2FsT2Zmc2V0WyAwIF0gOiAwXG5cdFx0XTtcblxuXHRcdC8vIFJlZHVjZSB0byBqdXN0IHRoZSBwb3NpdGlvbnMgd2l0aG91dCB0aGUgb2Zmc2V0c1xuXHRcdG9wdGlvbnNbIHRoaXMgXSA9IFtcblx0XHRcdHJwb3NpdGlvbi5leGVjKCBwb3NbIDAgXSApWyAwIF0sXG5cdFx0XHRycG9zaXRpb24uZXhlYyggcG9zWyAxIF0gKVsgMCBdXG5cdFx0XTtcblx0fSApO1xuXG5cdC8vIE5vcm1hbGl6ZSBjb2xsaXNpb24gb3B0aW9uXG5cdGlmICggY29sbGlzaW9uLmxlbmd0aCA9PT0gMSApIHtcblx0XHRjb2xsaXNpb25bIDEgXSA9IGNvbGxpc2lvblsgMCBdO1xuXHR9XG5cblx0aWYgKCBvcHRpb25zLmF0WyAwIF0gPT09IFwicmlnaHRcIiApIHtcblx0XHRiYXNlUG9zaXRpb24ubGVmdCArPSB0YXJnZXRXaWR0aDtcblx0fSBlbHNlIGlmICggb3B0aW9ucy5hdFsgMCBdID09PSBcImNlbnRlclwiICkge1xuXHRcdGJhc2VQb3NpdGlvbi5sZWZ0ICs9IHRhcmdldFdpZHRoIC8gMjtcblx0fVxuXG5cdGlmICggb3B0aW9ucy5hdFsgMSBdID09PSBcImJvdHRvbVwiICkge1xuXHRcdGJhc2VQb3NpdGlvbi50b3AgKz0gdGFyZ2V0SGVpZ2h0O1xuXHR9IGVsc2UgaWYgKCBvcHRpb25zLmF0WyAxIF0gPT09IFwiY2VudGVyXCIgKSB7XG5cdFx0YmFzZVBvc2l0aW9uLnRvcCArPSB0YXJnZXRIZWlnaHQgLyAyO1xuXHR9XG5cblx0YXRPZmZzZXQgPSBnZXRPZmZzZXRzKCBvZmZzZXRzLmF0LCB0YXJnZXRXaWR0aCwgdGFyZ2V0SGVpZ2h0ICk7XG5cdGJhc2VQb3NpdGlvbi5sZWZ0ICs9IGF0T2Zmc2V0WyAwIF07XG5cdGJhc2VQb3NpdGlvbi50b3AgKz0gYXRPZmZzZXRbIDEgXTtcblxuXHRyZXR1cm4gdGhpcy5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHR2YXIgY29sbGlzaW9uUG9zaXRpb24sIHVzaW5nLFxuXHRcdFx0ZWxlbSA9ICQoIHRoaXMgKSxcblx0XHRcdGVsZW1XaWR0aCA9IGVsZW0ub3V0ZXJXaWR0aCgpLFxuXHRcdFx0ZWxlbUhlaWdodCA9IGVsZW0ub3V0ZXJIZWlnaHQoKSxcblx0XHRcdG1hcmdpbkxlZnQgPSBwYXJzZUNzcyggdGhpcywgXCJtYXJnaW5MZWZ0XCIgKSxcblx0XHRcdG1hcmdpblRvcCA9IHBhcnNlQ3NzKCB0aGlzLCBcIm1hcmdpblRvcFwiICksXG5cdFx0XHRjb2xsaXNpb25XaWR0aCA9IGVsZW1XaWR0aCArIG1hcmdpbkxlZnQgKyBwYXJzZUNzcyggdGhpcywgXCJtYXJnaW5SaWdodFwiICkgK1xuXHRcdFx0XHRzY3JvbGxJbmZvLndpZHRoLFxuXHRcdFx0Y29sbGlzaW9uSGVpZ2h0ID0gZWxlbUhlaWdodCArIG1hcmdpblRvcCArIHBhcnNlQ3NzKCB0aGlzLCBcIm1hcmdpbkJvdHRvbVwiICkgK1xuXHRcdFx0XHRzY3JvbGxJbmZvLmhlaWdodCxcblx0XHRcdHBvc2l0aW9uID0gJC5leHRlbmQoIHt9LCBiYXNlUG9zaXRpb24gKSxcblx0XHRcdG15T2Zmc2V0ID0gZ2V0T2Zmc2V0cyggb2Zmc2V0cy5teSwgZWxlbS5vdXRlcldpZHRoKCksIGVsZW0ub3V0ZXJIZWlnaHQoKSApO1xuXG5cdFx0aWYgKCBvcHRpb25zLm15WyAwIF0gPT09IFwicmlnaHRcIiApIHtcblx0XHRcdHBvc2l0aW9uLmxlZnQgLT0gZWxlbVdpZHRoO1xuXHRcdH0gZWxzZSBpZiAoIG9wdGlvbnMubXlbIDAgXSA9PT0gXCJjZW50ZXJcIiApIHtcblx0XHRcdHBvc2l0aW9uLmxlZnQgLT0gZWxlbVdpZHRoIC8gMjtcblx0XHR9XG5cblx0XHRpZiAoIG9wdGlvbnMubXlbIDEgXSA9PT0gXCJib3R0b21cIiApIHtcblx0XHRcdHBvc2l0aW9uLnRvcCAtPSBlbGVtSGVpZ2h0O1xuXHRcdH0gZWxzZSBpZiAoIG9wdGlvbnMubXlbIDEgXSA9PT0gXCJjZW50ZXJcIiApIHtcblx0XHRcdHBvc2l0aW9uLnRvcCAtPSBlbGVtSGVpZ2h0IC8gMjtcblx0XHR9XG5cblx0XHRwb3NpdGlvbi5sZWZ0ICs9IG15T2Zmc2V0WyAwIF07XG5cdFx0cG9zaXRpb24udG9wICs9IG15T2Zmc2V0WyAxIF07XG5cblx0XHRjb2xsaXNpb25Qb3NpdGlvbiA9IHtcblx0XHRcdG1hcmdpbkxlZnQ6IG1hcmdpbkxlZnQsXG5cdFx0XHRtYXJnaW5Ub3A6IG1hcmdpblRvcFxuXHRcdH07XG5cblx0XHQkLmVhY2goIFsgXCJsZWZ0XCIsIFwidG9wXCIgXSwgZnVuY3Rpb24oIGksIGRpciApIHtcblx0XHRcdGlmICggJC51aS5wb3NpdGlvblsgY29sbGlzaW9uWyBpIF0gXSApIHtcblx0XHRcdFx0JC51aS5wb3NpdGlvblsgY29sbGlzaW9uWyBpIF0gXVsgZGlyIF0oIHBvc2l0aW9uLCB7XG5cdFx0XHRcdFx0dGFyZ2V0V2lkdGg6IHRhcmdldFdpZHRoLFxuXHRcdFx0XHRcdHRhcmdldEhlaWdodDogdGFyZ2V0SGVpZ2h0LFxuXHRcdFx0XHRcdGVsZW1XaWR0aDogZWxlbVdpZHRoLFxuXHRcdFx0XHRcdGVsZW1IZWlnaHQ6IGVsZW1IZWlnaHQsXG5cdFx0XHRcdFx0Y29sbGlzaW9uUG9zaXRpb246IGNvbGxpc2lvblBvc2l0aW9uLFxuXHRcdFx0XHRcdGNvbGxpc2lvbldpZHRoOiBjb2xsaXNpb25XaWR0aCxcblx0XHRcdFx0XHRjb2xsaXNpb25IZWlnaHQ6IGNvbGxpc2lvbkhlaWdodCxcblx0XHRcdFx0XHRvZmZzZXQ6IFsgYXRPZmZzZXRbIDAgXSArIG15T2Zmc2V0WyAwIF0sIGF0T2Zmc2V0IFsgMSBdICsgbXlPZmZzZXRbIDEgXSBdLFxuXHRcdFx0XHRcdG15OiBvcHRpb25zLm15LFxuXHRcdFx0XHRcdGF0OiBvcHRpb25zLmF0LFxuXHRcdFx0XHRcdHdpdGhpbjogd2l0aGluLFxuXHRcdFx0XHRcdGVsZW06IGVsZW1cblx0XHRcdFx0fSApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHRcdGlmICggb3B0aW9ucy51c2luZyApIHtcblxuXHRcdFx0Ly8gQWRkcyBmZWVkYmFjayBhcyBzZWNvbmQgYXJndW1lbnQgdG8gdXNpbmcgY2FsbGJhY2ssIGlmIHByZXNlbnRcblx0XHRcdHVzaW5nID0gZnVuY3Rpb24oIHByb3BzICkge1xuXHRcdFx0XHR2YXIgbGVmdCA9IHRhcmdldE9mZnNldC5sZWZ0IC0gcG9zaXRpb24ubGVmdCxcblx0XHRcdFx0XHRyaWdodCA9IGxlZnQgKyB0YXJnZXRXaWR0aCAtIGVsZW1XaWR0aCxcblx0XHRcdFx0XHR0b3AgPSB0YXJnZXRPZmZzZXQudG9wIC0gcG9zaXRpb24udG9wLFxuXHRcdFx0XHRcdGJvdHRvbSA9IHRvcCArIHRhcmdldEhlaWdodCAtIGVsZW1IZWlnaHQsXG5cdFx0XHRcdFx0ZmVlZGJhY2sgPSB7XG5cdFx0XHRcdFx0XHR0YXJnZXQ6IHtcblx0XHRcdFx0XHRcdFx0ZWxlbWVudDogdGFyZ2V0LFxuXHRcdFx0XHRcdFx0XHRsZWZ0OiB0YXJnZXRPZmZzZXQubGVmdCxcblx0XHRcdFx0XHRcdFx0dG9wOiB0YXJnZXRPZmZzZXQudG9wLFxuXHRcdFx0XHRcdFx0XHR3aWR0aDogdGFyZ2V0V2lkdGgsXG5cdFx0XHRcdFx0XHRcdGhlaWdodDogdGFyZ2V0SGVpZ2h0XG5cdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdFx0ZWxlbWVudDoge1xuXHRcdFx0XHRcdFx0XHRlbGVtZW50OiBlbGVtLFxuXHRcdFx0XHRcdFx0XHRsZWZ0OiBwb3NpdGlvbi5sZWZ0LFxuXHRcdFx0XHRcdFx0XHR0b3A6IHBvc2l0aW9uLnRvcCxcblx0XHRcdFx0XHRcdFx0d2lkdGg6IGVsZW1XaWR0aCxcblx0XHRcdFx0XHRcdFx0aGVpZ2h0OiBlbGVtSGVpZ2h0XG5cdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdFx0aG9yaXpvbnRhbDogcmlnaHQgPCAwID8gXCJsZWZ0XCIgOiBsZWZ0ID4gMCA/IFwicmlnaHRcIiA6IFwiY2VudGVyXCIsXG5cdFx0XHRcdFx0XHR2ZXJ0aWNhbDogYm90dG9tIDwgMCA/IFwidG9wXCIgOiB0b3AgPiAwID8gXCJib3R0b21cIiA6IFwibWlkZGxlXCJcblx0XHRcdFx0XHR9O1xuXHRcdFx0XHRpZiAoIHRhcmdldFdpZHRoIDwgZWxlbVdpZHRoICYmIGFicyggbGVmdCArIHJpZ2h0ICkgPCB0YXJnZXRXaWR0aCApIHtcblx0XHRcdFx0XHRmZWVkYmFjay5ob3Jpem9udGFsID0gXCJjZW50ZXJcIjtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoIHRhcmdldEhlaWdodCA8IGVsZW1IZWlnaHQgJiYgYWJzKCB0b3AgKyBib3R0b20gKSA8IHRhcmdldEhlaWdodCApIHtcblx0XHRcdFx0XHRmZWVkYmFjay52ZXJ0aWNhbCA9IFwibWlkZGxlXCI7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKCBtYXgoIGFicyggbGVmdCApLCBhYnMoIHJpZ2h0ICkgKSA+IG1heCggYWJzKCB0b3AgKSwgYWJzKCBib3R0b20gKSApICkge1xuXHRcdFx0XHRcdGZlZWRiYWNrLmltcG9ydGFudCA9IFwiaG9yaXpvbnRhbFwiO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGZlZWRiYWNrLmltcG9ydGFudCA9IFwidmVydGljYWxcIjtcblx0XHRcdFx0fVxuXHRcdFx0XHRvcHRpb25zLnVzaW5nLmNhbGwoIHRoaXMsIHByb3BzLCBmZWVkYmFjayApO1xuXHRcdFx0fTtcblx0XHR9XG5cblx0XHRlbGVtLm9mZnNldCggJC5leHRlbmQoIHBvc2l0aW9uLCB7IHVzaW5nOiB1c2luZyB9ICkgKTtcblx0fSApO1xufTtcblxuJC51aS5wb3NpdGlvbiA9IHtcblx0Zml0OiB7XG5cdFx0bGVmdDogZnVuY3Rpb24oIHBvc2l0aW9uLCBkYXRhICkge1xuXHRcdFx0dmFyIHdpdGhpbiA9IGRhdGEud2l0aGluLFxuXHRcdFx0XHR3aXRoaW5PZmZzZXQgPSB3aXRoaW4uaXNXaW5kb3cgPyB3aXRoaW4uc2Nyb2xsTGVmdCA6IHdpdGhpbi5vZmZzZXQubGVmdCxcblx0XHRcdFx0b3V0ZXJXaWR0aCA9IHdpdGhpbi53aWR0aCxcblx0XHRcdFx0Y29sbGlzaW9uUG9zTGVmdCA9IHBvc2l0aW9uLmxlZnQgLSBkYXRhLmNvbGxpc2lvblBvc2l0aW9uLm1hcmdpbkxlZnQsXG5cdFx0XHRcdG92ZXJMZWZ0ID0gd2l0aGluT2Zmc2V0IC0gY29sbGlzaW9uUG9zTGVmdCxcblx0XHRcdFx0b3ZlclJpZ2h0ID0gY29sbGlzaW9uUG9zTGVmdCArIGRhdGEuY29sbGlzaW9uV2lkdGggLSBvdXRlcldpZHRoIC0gd2l0aGluT2Zmc2V0LFxuXHRcdFx0XHRuZXdPdmVyUmlnaHQ7XG5cblx0XHRcdC8vIEVsZW1lbnQgaXMgd2lkZXIgdGhhbiB3aXRoaW5cblx0XHRcdGlmICggZGF0YS5jb2xsaXNpb25XaWR0aCA+IG91dGVyV2lkdGggKSB7XG5cblx0XHRcdFx0Ly8gRWxlbWVudCBpcyBpbml0aWFsbHkgb3ZlciB0aGUgbGVmdCBzaWRlIG9mIHdpdGhpblxuXHRcdFx0XHRpZiAoIG92ZXJMZWZ0ID4gMCAmJiBvdmVyUmlnaHQgPD0gMCApIHtcblx0XHRcdFx0XHRuZXdPdmVyUmlnaHQgPSBwb3NpdGlvbi5sZWZ0ICsgb3ZlckxlZnQgKyBkYXRhLmNvbGxpc2lvbldpZHRoIC0gb3V0ZXJXaWR0aCAtXG5cdFx0XHRcdFx0XHR3aXRoaW5PZmZzZXQ7XG5cdFx0XHRcdFx0cG9zaXRpb24ubGVmdCArPSBvdmVyTGVmdCAtIG5ld092ZXJSaWdodDtcblxuXHRcdFx0XHQvLyBFbGVtZW50IGlzIGluaXRpYWxseSBvdmVyIHJpZ2h0IHNpZGUgb2Ygd2l0aGluXG5cdFx0XHRcdH0gZWxzZSBpZiAoIG92ZXJSaWdodCA+IDAgJiYgb3ZlckxlZnQgPD0gMCApIHtcblx0XHRcdFx0XHRwb3NpdGlvbi5sZWZ0ID0gd2l0aGluT2Zmc2V0O1xuXG5cdFx0XHRcdC8vIEVsZW1lbnQgaXMgaW5pdGlhbGx5IG92ZXIgYm90aCBsZWZ0IGFuZCByaWdodCBzaWRlcyBvZiB3aXRoaW5cblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRpZiAoIG92ZXJMZWZ0ID4gb3ZlclJpZ2h0ICkge1xuXHRcdFx0XHRcdFx0cG9zaXRpb24ubGVmdCA9IHdpdGhpbk9mZnNldCArIG91dGVyV2lkdGggLSBkYXRhLmNvbGxpc2lvbldpZHRoO1xuXHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRwb3NpdGlvbi5sZWZ0ID0gd2l0aGluT2Zmc2V0O1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHQvLyBUb28gZmFyIGxlZnQgLT4gYWxpZ24gd2l0aCBsZWZ0IGVkZ2Vcblx0XHRcdH0gZWxzZSBpZiAoIG92ZXJMZWZ0ID4gMCApIHtcblx0XHRcdFx0cG9zaXRpb24ubGVmdCArPSBvdmVyTGVmdDtcblxuXHRcdFx0Ly8gVG9vIGZhciByaWdodCAtPiBhbGlnbiB3aXRoIHJpZ2h0IGVkZ2Vcblx0XHRcdH0gZWxzZSBpZiAoIG92ZXJSaWdodCA+IDAgKSB7XG5cdFx0XHRcdHBvc2l0aW9uLmxlZnQgLT0gb3ZlclJpZ2h0O1xuXG5cdFx0XHQvLyBBZGp1c3QgYmFzZWQgb24gcG9zaXRpb24gYW5kIG1hcmdpblxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0cG9zaXRpb24ubGVmdCA9IG1heCggcG9zaXRpb24ubGVmdCAtIGNvbGxpc2lvblBvc0xlZnQsIHBvc2l0aW9uLmxlZnQgKTtcblx0XHRcdH1cblx0XHR9LFxuXHRcdHRvcDogZnVuY3Rpb24oIHBvc2l0aW9uLCBkYXRhICkge1xuXHRcdFx0dmFyIHdpdGhpbiA9IGRhdGEud2l0aGluLFxuXHRcdFx0XHR3aXRoaW5PZmZzZXQgPSB3aXRoaW4uaXNXaW5kb3cgPyB3aXRoaW4uc2Nyb2xsVG9wIDogd2l0aGluLm9mZnNldC50b3AsXG5cdFx0XHRcdG91dGVySGVpZ2h0ID0gZGF0YS53aXRoaW4uaGVpZ2h0LFxuXHRcdFx0XHRjb2xsaXNpb25Qb3NUb3AgPSBwb3NpdGlvbi50b3AgLSBkYXRhLmNvbGxpc2lvblBvc2l0aW9uLm1hcmdpblRvcCxcblx0XHRcdFx0b3ZlclRvcCA9IHdpdGhpbk9mZnNldCAtIGNvbGxpc2lvblBvc1RvcCxcblx0XHRcdFx0b3ZlckJvdHRvbSA9IGNvbGxpc2lvblBvc1RvcCArIGRhdGEuY29sbGlzaW9uSGVpZ2h0IC0gb3V0ZXJIZWlnaHQgLSB3aXRoaW5PZmZzZXQsXG5cdFx0XHRcdG5ld092ZXJCb3R0b207XG5cblx0XHRcdC8vIEVsZW1lbnQgaXMgdGFsbGVyIHRoYW4gd2l0aGluXG5cdFx0XHRpZiAoIGRhdGEuY29sbGlzaW9uSGVpZ2h0ID4gb3V0ZXJIZWlnaHQgKSB7XG5cblx0XHRcdFx0Ly8gRWxlbWVudCBpcyBpbml0aWFsbHkgb3ZlciB0aGUgdG9wIG9mIHdpdGhpblxuXHRcdFx0XHRpZiAoIG92ZXJUb3AgPiAwICYmIG92ZXJCb3R0b20gPD0gMCApIHtcblx0XHRcdFx0XHRuZXdPdmVyQm90dG9tID0gcG9zaXRpb24udG9wICsgb3ZlclRvcCArIGRhdGEuY29sbGlzaW9uSGVpZ2h0IC0gb3V0ZXJIZWlnaHQgLVxuXHRcdFx0XHRcdFx0d2l0aGluT2Zmc2V0O1xuXHRcdFx0XHRcdHBvc2l0aW9uLnRvcCArPSBvdmVyVG9wIC0gbmV3T3ZlckJvdHRvbTtcblxuXHRcdFx0XHQvLyBFbGVtZW50IGlzIGluaXRpYWxseSBvdmVyIGJvdHRvbSBvZiB3aXRoaW5cblx0XHRcdFx0fSBlbHNlIGlmICggb3ZlckJvdHRvbSA+IDAgJiYgb3ZlclRvcCA8PSAwICkge1xuXHRcdFx0XHRcdHBvc2l0aW9uLnRvcCA9IHdpdGhpbk9mZnNldDtcblxuXHRcdFx0XHQvLyBFbGVtZW50IGlzIGluaXRpYWxseSBvdmVyIGJvdGggdG9wIGFuZCBib3R0b20gb2Ygd2l0aGluXG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0aWYgKCBvdmVyVG9wID4gb3ZlckJvdHRvbSApIHtcblx0XHRcdFx0XHRcdHBvc2l0aW9uLnRvcCA9IHdpdGhpbk9mZnNldCArIG91dGVySGVpZ2h0IC0gZGF0YS5jb2xsaXNpb25IZWlnaHQ7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdHBvc2l0aW9uLnRvcCA9IHdpdGhpbk9mZnNldDtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0Ly8gVG9vIGZhciB1cCAtPiBhbGlnbiB3aXRoIHRvcFxuXHRcdFx0fSBlbHNlIGlmICggb3ZlclRvcCA+IDAgKSB7XG5cdFx0XHRcdHBvc2l0aW9uLnRvcCArPSBvdmVyVG9wO1xuXG5cdFx0XHQvLyBUb28gZmFyIGRvd24gLT4gYWxpZ24gd2l0aCBib3R0b20gZWRnZVxuXHRcdFx0fSBlbHNlIGlmICggb3ZlckJvdHRvbSA+IDAgKSB7XG5cdFx0XHRcdHBvc2l0aW9uLnRvcCAtPSBvdmVyQm90dG9tO1xuXG5cdFx0XHQvLyBBZGp1c3QgYmFzZWQgb24gcG9zaXRpb24gYW5kIG1hcmdpblxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0cG9zaXRpb24udG9wID0gbWF4KCBwb3NpdGlvbi50b3AgLSBjb2xsaXNpb25Qb3NUb3AsIHBvc2l0aW9uLnRvcCApO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblx0ZmxpcDoge1xuXHRcdGxlZnQ6IGZ1bmN0aW9uKCBwb3NpdGlvbiwgZGF0YSApIHtcblx0XHRcdHZhciB3aXRoaW4gPSBkYXRhLndpdGhpbixcblx0XHRcdFx0d2l0aGluT2Zmc2V0ID0gd2l0aGluLm9mZnNldC5sZWZ0ICsgd2l0aGluLnNjcm9sbExlZnQsXG5cdFx0XHRcdG91dGVyV2lkdGggPSB3aXRoaW4ud2lkdGgsXG5cdFx0XHRcdG9mZnNldExlZnQgPSB3aXRoaW4uaXNXaW5kb3cgPyB3aXRoaW4uc2Nyb2xsTGVmdCA6IHdpdGhpbi5vZmZzZXQubGVmdCxcblx0XHRcdFx0Y29sbGlzaW9uUG9zTGVmdCA9IHBvc2l0aW9uLmxlZnQgLSBkYXRhLmNvbGxpc2lvblBvc2l0aW9uLm1hcmdpbkxlZnQsXG5cdFx0XHRcdG92ZXJMZWZ0ID0gY29sbGlzaW9uUG9zTGVmdCAtIG9mZnNldExlZnQsXG5cdFx0XHRcdG92ZXJSaWdodCA9IGNvbGxpc2lvblBvc0xlZnQgKyBkYXRhLmNvbGxpc2lvbldpZHRoIC0gb3V0ZXJXaWR0aCAtIG9mZnNldExlZnQsXG5cdFx0XHRcdG15T2Zmc2V0ID0gZGF0YS5teVsgMCBdID09PSBcImxlZnRcIiA/XG5cdFx0XHRcdFx0LWRhdGEuZWxlbVdpZHRoIDpcblx0XHRcdFx0XHRkYXRhLm15WyAwIF0gPT09IFwicmlnaHRcIiA/XG5cdFx0XHRcdFx0XHRkYXRhLmVsZW1XaWR0aCA6XG5cdFx0XHRcdFx0XHQwLFxuXHRcdFx0XHRhdE9mZnNldCA9IGRhdGEuYXRbIDAgXSA9PT0gXCJsZWZ0XCIgP1xuXHRcdFx0XHRcdGRhdGEudGFyZ2V0V2lkdGggOlxuXHRcdFx0XHRcdGRhdGEuYXRbIDAgXSA9PT0gXCJyaWdodFwiID9cblx0XHRcdFx0XHRcdC1kYXRhLnRhcmdldFdpZHRoIDpcblx0XHRcdFx0XHRcdDAsXG5cdFx0XHRcdG9mZnNldCA9IC0yICogZGF0YS5vZmZzZXRbIDAgXSxcblx0XHRcdFx0bmV3T3ZlclJpZ2h0LFxuXHRcdFx0XHRuZXdPdmVyTGVmdDtcblxuXHRcdFx0aWYgKCBvdmVyTGVmdCA8IDAgKSB7XG5cdFx0XHRcdG5ld092ZXJSaWdodCA9IHBvc2l0aW9uLmxlZnQgKyBteU9mZnNldCArIGF0T2Zmc2V0ICsgb2Zmc2V0ICsgZGF0YS5jb2xsaXNpb25XaWR0aCAtXG5cdFx0XHRcdFx0b3V0ZXJXaWR0aCAtIHdpdGhpbk9mZnNldDtcblx0XHRcdFx0aWYgKCBuZXdPdmVyUmlnaHQgPCAwIHx8IG5ld092ZXJSaWdodCA8IGFicyggb3ZlckxlZnQgKSApIHtcblx0XHRcdFx0XHRwb3NpdGlvbi5sZWZ0ICs9IG15T2Zmc2V0ICsgYXRPZmZzZXQgKyBvZmZzZXQ7XG5cdFx0XHRcdH1cblx0XHRcdH0gZWxzZSBpZiAoIG92ZXJSaWdodCA+IDAgKSB7XG5cdFx0XHRcdG5ld092ZXJMZWZ0ID0gcG9zaXRpb24ubGVmdCAtIGRhdGEuY29sbGlzaW9uUG9zaXRpb24ubWFyZ2luTGVmdCArIG15T2Zmc2V0ICtcblx0XHRcdFx0XHRhdE9mZnNldCArIG9mZnNldCAtIG9mZnNldExlZnQ7XG5cdFx0XHRcdGlmICggbmV3T3ZlckxlZnQgPiAwIHx8IGFicyggbmV3T3ZlckxlZnQgKSA8IG92ZXJSaWdodCApIHtcblx0XHRcdFx0XHRwb3NpdGlvbi5sZWZ0ICs9IG15T2Zmc2V0ICsgYXRPZmZzZXQgKyBvZmZzZXQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9LFxuXHRcdHRvcDogZnVuY3Rpb24oIHBvc2l0aW9uLCBkYXRhICkge1xuXHRcdFx0dmFyIHdpdGhpbiA9IGRhdGEud2l0aGluLFxuXHRcdFx0XHR3aXRoaW5PZmZzZXQgPSB3aXRoaW4ub2Zmc2V0LnRvcCArIHdpdGhpbi5zY3JvbGxUb3AsXG5cdFx0XHRcdG91dGVySGVpZ2h0ID0gd2l0aGluLmhlaWdodCxcblx0XHRcdFx0b2Zmc2V0VG9wID0gd2l0aGluLmlzV2luZG93ID8gd2l0aGluLnNjcm9sbFRvcCA6IHdpdGhpbi5vZmZzZXQudG9wLFxuXHRcdFx0XHRjb2xsaXNpb25Qb3NUb3AgPSBwb3NpdGlvbi50b3AgLSBkYXRhLmNvbGxpc2lvblBvc2l0aW9uLm1hcmdpblRvcCxcblx0XHRcdFx0b3ZlclRvcCA9IGNvbGxpc2lvblBvc1RvcCAtIG9mZnNldFRvcCxcblx0XHRcdFx0b3ZlckJvdHRvbSA9IGNvbGxpc2lvblBvc1RvcCArIGRhdGEuY29sbGlzaW9uSGVpZ2h0IC0gb3V0ZXJIZWlnaHQgLSBvZmZzZXRUb3AsXG5cdFx0XHRcdHRvcCA9IGRhdGEubXlbIDEgXSA9PT0gXCJ0b3BcIixcblx0XHRcdFx0bXlPZmZzZXQgPSB0b3AgP1xuXHRcdFx0XHRcdC1kYXRhLmVsZW1IZWlnaHQgOlxuXHRcdFx0XHRcdGRhdGEubXlbIDEgXSA9PT0gXCJib3R0b21cIiA/XG5cdFx0XHRcdFx0XHRkYXRhLmVsZW1IZWlnaHQgOlxuXHRcdFx0XHRcdFx0MCxcblx0XHRcdFx0YXRPZmZzZXQgPSBkYXRhLmF0WyAxIF0gPT09IFwidG9wXCIgP1xuXHRcdFx0XHRcdGRhdGEudGFyZ2V0SGVpZ2h0IDpcblx0XHRcdFx0XHRkYXRhLmF0WyAxIF0gPT09IFwiYm90dG9tXCIgP1xuXHRcdFx0XHRcdFx0LWRhdGEudGFyZ2V0SGVpZ2h0IDpcblx0XHRcdFx0XHRcdDAsXG5cdFx0XHRcdG9mZnNldCA9IC0yICogZGF0YS5vZmZzZXRbIDEgXSxcblx0XHRcdFx0bmV3T3ZlclRvcCxcblx0XHRcdFx0bmV3T3ZlckJvdHRvbTtcblx0XHRcdGlmICggb3ZlclRvcCA8IDAgKSB7XG5cdFx0XHRcdG5ld092ZXJCb3R0b20gPSBwb3NpdGlvbi50b3AgKyBteU9mZnNldCArIGF0T2Zmc2V0ICsgb2Zmc2V0ICsgZGF0YS5jb2xsaXNpb25IZWlnaHQgLVxuXHRcdFx0XHRcdG91dGVySGVpZ2h0IC0gd2l0aGluT2Zmc2V0O1xuXHRcdFx0XHRpZiAoIG5ld092ZXJCb3R0b20gPCAwIHx8IG5ld092ZXJCb3R0b20gPCBhYnMoIG92ZXJUb3AgKSApIHtcblx0XHRcdFx0XHRwb3NpdGlvbi50b3AgKz0gbXlPZmZzZXQgKyBhdE9mZnNldCArIG9mZnNldDtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIGlmICggb3ZlckJvdHRvbSA+IDAgKSB7XG5cdFx0XHRcdG5ld092ZXJUb3AgPSBwb3NpdGlvbi50b3AgLSBkYXRhLmNvbGxpc2lvblBvc2l0aW9uLm1hcmdpblRvcCArIG15T2Zmc2V0ICsgYXRPZmZzZXQgK1xuXHRcdFx0XHRcdG9mZnNldCAtIG9mZnNldFRvcDtcblx0XHRcdFx0aWYgKCBuZXdPdmVyVG9wID4gMCB8fCBhYnMoIG5ld092ZXJUb3AgKSA8IG92ZXJCb3R0b20gKSB7XG5cdFx0XHRcdFx0cG9zaXRpb24udG9wICs9IG15T2Zmc2V0ICsgYXRPZmZzZXQgKyBvZmZzZXQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cdGZsaXBmaXQ6IHtcblx0XHRsZWZ0OiBmdW5jdGlvbigpIHtcblx0XHRcdCQudWkucG9zaXRpb24uZmxpcC5sZWZ0LmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcblx0XHRcdCQudWkucG9zaXRpb24uZml0LmxlZnQuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdH0sXG5cdFx0dG9wOiBmdW5jdGlvbigpIHtcblx0XHRcdCQudWkucG9zaXRpb24uZmxpcC50b3AuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdFx0JC51aS5wb3NpdGlvbi5maXQudG9wLmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcblx0XHR9XG5cdH1cbn07XG5cbn0gKSgpO1xuXG52YXIgcG9zaXRpb24gPSAkLnVpLnBvc2l0aW9uO1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIDpkYXRhIDEuMTIuMVxuICogaHR0cDovL2pxdWVyeXVpLmNvbVxuICpcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzXG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UuXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKi9cblxuLy8+PmxhYmVsOiA6ZGF0YSBTZWxlY3RvclxuLy8+Pmdyb3VwOiBDb3JlXG4vLz4+ZGVzY3JpcHRpb246IFNlbGVjdHMgZWxlbWVudHMgd2hpY2ggaGF2ZSBkYXRhIHN0b3JlZCB1bmRlciB0aGUgc3BlY2lmaWVkIGtleS5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9kYXRhLXNlbGVjdG9yL1xuXG5cbnZhciBkYXRhID0gJC5leHRlbmQoICQuZXhwclsgXCI6XCIgXSwge1xuXHRkYXRhOiAkLmV4cHIuY3JlYXRlUHNldWRvID9cblx0XHQkLmV4cHIuY3JlYXRlUHNldWRvKCBmdW5jdGlvbiggZGF0YU5hbWUgKSB7XG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHRcdHJldHVybiAhISQuZGF0YSggZWxlbSwgZGF0YU5hbWUgKTtcblx0XHRcdH07XG5cdFx0fSApIDpcblxuXHRcdC8vIFN1cHBvcnQ6IGpRdWVyeSA8MS44XG5cdFx0ZnVuY3Rpb24oIGVsZW0sIGksIG1hdGNoICkge1xuXHRcdFx0cmV0dXJuICEhJC5kYXRhKCBlbGVtLCBtYXRjaFsgMyBdICk7XG5cdFx0fVxufSApO1xuXG4vKiFcbiAqIGpRdWVyeSBVSSBEaXNhYmxlIFNlbGVjdGlvbiAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogZGlzYWJsZVNlbGVjdGlvblxuLy8+Pmdyb3VwOiBDb3JlXG4vLz4+ZGVzY3JpcHRpb246IERpc2FibGUgc2VsZWN0aW9uIG9mIHRleHQgY29udGVudCB3aXRoaW4gdGhlIHNldCBvZiBtYXRjaGVkIGVsZW1lbnRzLlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2Rpc2FibGVTZWxlY3Rpb24vXG5cbi8vIFRoaXMgZmlsZSBpcyBkZXByZWNhdGVkXG5cblxudmFyIGRpc2FibGVTZWxlY3Rpb24gPSAkLmZuLmV4dGVuZCgge1xuXHRkaXNhYmxlU2VsZWN0aW9uOiAoIGZ1bmN0aW9uKCkge1xuXHRcdHZhciBldmVudFR5cGUgPSBcIm9uc2VsZWN0c3RhcnRcIiBpbiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCBcImRpdlwiICkgP1xuXHRcdFx0XCJzZWxlY3RzdGFydFwiIDpcblx0XHRcdFwibW91c2Vkb3duXCI7XG5cblx0XHRyZXR1cm4gZnVuY3Rpb24oKSB7XG5cdFx0XHRyZXR1cm4gdGhpcy5vbiggZXZlbnRUeXBlICsgXCIudWktZGlzYWJsZVNlbGVjdGlvblwiLCBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHR9ICk7XG5cdFx0fTtcblx0fSApKCksXG5cblx0ZW5hYmxlU2VsZWN0aW9uOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5vZmYoIFwiLnVpLWRpc2FibGVTZWxlY3Rpb25cIiApO1xuXHR9XG59ICk7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgRWZmZWN0cyAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogRWZmZWN0cyBDb3JlXG4vLz4+Z3JvdXA6IEVmZmVjdHNcbi8vIGpzY3M6ZGlzYWJsZSBtYXhpbXVtTGluZUxlbmd0aFxuLy8+PmRlc2NyaXB0aW9uOiBFeHRlbmRzIHRoZSBpbnRlcm5hbCBqUXVlcnkgZWZmZWN0cy4gSW5jbHVkZXMgbW9ycGhpbmcgYW5kIGVhc2luZy4gUmVxdWlyZWQgYnkgYWxsIG90aGVyIGVmZmVjdHMuXG4vLyBqc2NzOmVuYWJsZSBtYXhpbXVtTGluZUxlbmd0aFxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2NhdGVnb3J5L2VmZmVjdHMtY29yZS9cbi8vPj5kZW1vczogaHR0cDovL2pxdWVyeXVpLmNvbS9lZmZlY3QvXG5cblxuXG52YXIgZGF0YVNwYWNlID0gXCJ1aS1lZmZlY3RzLVwiLFxuXHRkYXRhU3BhY2VTdHlsZSA9IFwidWktZWZmZWN0cy1zdHlsZVwiLFxuXHRkYXRhU3BhY2VBbmltYXRlZCA9IFwidWktZWZmZWN0cy1hbmltYXRlZFwiLFxuXG5cdC8vIENyZWF0ZSBhIGxvY2FsIGpRdWVyeSBiZWNhdXNlIGpRdWVyeSBDb2xvciByZWxpZXMgb24gaXQgYW5kIHRoZVxuXHQvLyBnbG9iYWwgbWF5IG5vdCBleGlzdCB3aXRoIEFNRCBhbmQgYSBjdXN0b20gYnVpbGQgKCMxMDE5OSlcblx0alF1ZXJ5ID0gJDtcblxuJC5lZmZlY3RzID0ge1xuXHRlZmZlY3Q6IHt9XG59O1xuXG4vKiFcbiAqIGpRdWVyeSBDb2xvciBBbmltYXRpb25zIHYyLjEuMlxuICogaHR0cHM6Ly9naXRodWIuY29tL2pxdWVyeS9qcXVlcnktY29sb3JcbiAqXG4gKiBDb3B5cmlnaHQgMjAxNCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzXG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UuXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKlxuICogRGF0ZTogV2VkIEphbiAxNiAwODo0NzowOSAyMDEzIC0wNjAwXG4gKi9cbiggZnVuY3Rpb24oIGpRdWVyeSwgdW5kZWZpbmVkICkge1xuXG5cdHZhciBzdGVwSG9va3MgPSBcImJhY2tncm91bmRDb2xvciBib3JkZXJCb3R0b21Db2xvciBib3JkZXJMZWZ0Q29sb3IgYm9yZGVyUmlnaHRDb2xvciBcIiArXG5cdFx0XCJib3JkZXJUb3BDb2xvciBjb2xvciBjb2x1bW5SdWxlQ29sb3Igb3V0bGluZUNvbG9yIHRleHREZWNvcmF0aW9uQ29sb3IgdGV4dEVtcGhhc2lzQ29sb3JcIixcblxuXHQvLyBQbHVzZXF1YWxzIHRlc3QgZm9yICs9IDEwMCAtPSAxMDBcblx0cnBsdXNlcXVhbHMgPSAvXihbXFwtK10pPVxccyooXFxkK1xcLj9cXGQqKS8sXG5cblx0Ly8gQSBzZXQgb2YgUkUncyB0aGF0IGNhbiBtYXRjaCBzdHJpbmdzIGFuZCBnZW5lcmF0ZSBjb2xvciB0dXBsZXMuXG5cdHN0cmluZ1BhcnNlcnMgPSBbIHtcblx0XHRcdHJlOiAvcmdiYT9cXChcXHMqKFxcZHsxLDN9KVxccyosXFxzKihcXGR7MSwzfSlcXHMqLFxccyooXFxkezEsM30pXFxzKig/OixcXHMqKFxcZD8oPzpcXC5cXGQrKT8pXFxzKik/XFwpLyxcblx0XHRcdHBhcnNlOiBmdW5jdGlvbiggZXhlY1Jlc3VsdCApIHtcblx0XHRcdFx0cmV0dXJuIFtcblx0XHRcdFx0XHRleGVjUmVzdWx0WyAxIF0sXG5cdFx0XHRcdFx0ZXhlY1Jlc3VsdFsgMiBdLFxuXHRcdFx0XHRcdGV4ZWNSZXN1bHRbIDMgXSxcblx0XHRcdFx0XHRleGVjUmVzdWx0WyA0IF1cblx0XHRcdFx0XTtcblx0XHRcdH1cblx0XHR9LCB7XG5cdFx0XHRyZTogL3JnYmE/XFwoXFxzKihcXGQrKD86XFwuXFxkKyk/KVxcJVxccyosXFxzKihcXGQrKD86XFwuXFxkKyk/KVxcJVxccyosXFxzKihcXGQrKD86XFwuXFxkKyk/KVxcJVxccyooPzosXFxzKihcXGQ/KD86XFwuXFxkKyk/KVxccyopP1xcKS8sXG5cdFx0XHRwYXJzZTogZnVuY3Rpb24oIGV4ZWNSZXN1bHQgKSB7XG5cdFx0XHRcdHJldHVybiBbXG5cdFx0XHRcdFx0ZXhlY1Jlc3VsdFsgMSBdICogMi41NSxcblx0XHRcdFx0XHRleGVjUmVzdWx0WyAyIF0gKiAyLjU1LFxuXHRcdFx0XHRcdGV4ZWNSZXN1bHRbIDMgXSAqIDIuNTUsXG5cdFx0XHRcdFx0ZXhlY1Jlc3VsdFsgNCBdXG5cdFx0XHRcdF07XG5cdFx0XHR9XG5cdFx0fSwge1xuXG5cdFx0XHQvLyBUaGlzIHJlZ2V4IGlnbm9yZXMgQS1GIGJlY2F1c2UgaXQncyBjb21wYXJlZCBhZ2FpbnN0IGFuIGFscmVhZHkgbG93ZXJjYXNlZCBzdHJpbmdcblx0XHRcdHJlOiAvIyhbYS1mMC05XXsyfSkoW2EtZjAtOV17Mn0pKFthLWYwLTldezJ9KS8sXG5cdFx0XHRwYXJzZTogZnVuY3Rpb24oIGV4ZWNSZXN1bHQgKSB7XG5cdFx0XHRcdHJldHVybiBbXG5cdFx0XHRcdFx0cGFyc2VJbnQoIGV4ZWNSZXN1bHRbIDEgXSwgMTYgKSxcblx0XHRcdFx0XHRwYXJzZUludCggZXhlY1Jlc3VsdFsgMiBdLCAxNiApLFxuXHRcdFx0XHRcdHBhcnNlSW50KCBleGVjUmVzdWx0WyAzIF0sIDE2IClcblx0XHRcdFx0XTtcblx0XHRcdH1cblx0XHR9LCB7XG5cblx0XHRcdC8vIFRoaXMgcmVnZXggaWdub3JlcyBBLUYgYmVjYXVzZSBpdCdzIGNvbXBhcmVkIGFnYWluc3QgYW4gYWxyZWFkeSBsb3dlcmNhc2VkIHN0cmluZ1xuXHRcdFx0cmU6IC8jKFthLWYwLTldKShbYS1mMC05XSkoW2EtZjAtOV0pLyxcblx0XHRcdHBhcnNlOiBmdW5jdGlvbiggZXhlY1Jlc3VsdCApIHtcblx0XHRcdFx0cmV0dXJuIFtcblx0XHRcdFx0XHRwYXJzZUludCggZXhlY1Jlc3VsdFsgMSBdICsgZXhlY1Jlc3VsdFsgMSBdLCAxNiApLFxuXHRcdFx0XHRcdHBhcnNlSW50KCBleGVjUmVzdWx0WyAyIF0gKyBleGVjUmVzdWx0WyAyIF0sIDE2ICksXG5cdFx0XHRcdFx0cGFyc2VJbnQoIGV4ZWNSZXN1bHRbIDMgXSArIGV4ZWNSZXN1bHRbIDMgXSwgMTYgKVxuXHRcdFx0XHRdO1xuXHRcdFx0fVxuXHRcdH0sIHtcblx0XHRcdHJlOiAvaHNsYT9cXChcXHMqKFxcZCsoPzpcXC5cXGQrKT8pXFxzKixcXHMqKFxcZCsoPzpcXC5cXGQrKT8pXFwlXFxzKixcXHMqKFxcZCsoPzpcXC5cXGQrKT8pXFwlXFxzKig/OixcXHMqKFxcZD8oPzpcXC5cXGQrKT8pXFxzKik/XFwpLyxcblx0XHRcdHNwYWNlOiBcImhzbGFcIixcblx0XHRcdHBhcnNlOiBmdW5jdGlvbiggZXhlY1Jlc3VsdCApIHtcblx0XHRcdFx0cmV0dXJuIFtcblx0XHRcdFx0XHRleGVjUmVzdWx0WyAxIF0sXG5cdFx0XHRcdFx0ZXhlY1Jlc3VsdFsgMiBdIC8gMTAwLFxuXHRcdFx0XHRcdGV4ZWNSZXN1bHRbIDMgXSAvIDEwMCxcblx0XHRcdFx0XHRleGVjUmVzdWx0WyA0IF1cblx0XHRcdFx0XTtcblx0XHRcdH1cblx0XHR9IF0sXG5cblx0Ly8gSlF1ZXJ5LkNvbG9yKCApXG5cdGNvbG9yID0galF1ZXJ5LkNvbG9yID0gZnVuY3Rpb24oIGNvbG9yLCBncmVlbiwgYmx1ZSwgYWxwaGEgKSB7XG5cdFx0cmV0dXJuIG5ldyBqUXVlcnkuQ29sb3IuZm4ucGFyc2UoIGNvbG9yLCBncmVlbiwgYmx1ZSwgYWxwaGEgKTtcblx0fSxcblx0c3BhY2VzID0ge1xuXHRcdHJnYmE6IHtcblx0XHRcdHByb3BzOiB7XG5cdFx0XHRcdHJlZDoge1xuXHRcdFx0XHRcdGlkeDogMCxcblx0XHRcdFx0XHR0eXBlOiBcImJ5dGVcIlxuXHRcdFx0XHR9LFxuXHRcdFx0XHRncmVlbjoge1xuXHRcdFx0XHRcdGlkeDogMSxcblx0XHRcdFx0XHR0eXBlOiBcImJ5dGVcIlxuXHRcdFx0XHR9LFxuXHRcdFx0XHRibHVlOiB7XG5cdFx0XHRcdFx0aWR4OiAyLFxuXHRcdFx0XHRcdHR5cGU6IFwiYnl0ZVwiXG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9LFxuXG5cdFx0aHNsYToge1xuXHRcdFx0cHJvcHM6IHtcblx0XHRcdFx0aHVlOiB7XG5cdFx0XHRcdFx0aWR4OiAwLFxuXHRcdFx0XHRcdHR5cGU6IFwiZGVncmVlc1wiXG5cdFx0XHRcdH0sXG5cdFx0XHRcdHNhdHVyYXRpb246IHtcblx0XHRcdFx0XHRpZHg6IDEsXG5cdFx0XHRcdFx0dHlwZTogXCJwZXJjZW50XCJcblx0XHRcdFx0fSxcblx0XHRcdFx0bGlnaHRuZXNzOiB7XG5cdFx0XHRcdFx0aWR4OiAyLFxuXHRcdFx0XHRcdHR5cGU6IFwicGVyY2VudFwiXG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cdHByb3BUeXBlcyA9IHtcblx0XHRcImJ5dGVcIjoge1xuXHRcdFx0Zmxvb3I6IHRydWUsXG5cdFx0XHRtYXg6IDI1NVxuXHRcdH0sXG5cdFx0XCJwZXJjZW50XCI6IHtcblx0XHRcdG1heDogMVxuXHRcdH0sXG5cdFx0XCJkZWdyZWVzXCI6IHtcblx0XHRcdG1vZDogMzYwLFxuXHRcdFx0Zmxvb3I6IHRydWVcblx0XHR9XG5cdH0sXG5cdHN1cHBvcnQgPSBjb2xvci5zdXBwb3J0ID0ge30sXG5cblx0Ly8gRWxlbWVudCBmb3Igc3VwcG9ydCB0ZXN0c1xuXHRzdXBwb3J0RWxlbSA9IGpRdWVyeSggXCI8cD5cIiApWyAwIF0sXG5cblx0Ly8gQ29sb3JzID0galF1ZXJ5LkNvbG9yLm5hbWVzXG5cdGNvbG9ycyxcblxuXHQvLyBMb2NhbCBhbGlhc2VzIG9mIGZ1bmN0aW9ucyBjYWxsZWQgb2Z0ZW5cblx0ZWFjaCA9IGpRdWVyeS5lYWNoO1xuXG4vLyBEZXRlcm1pbmUgcmdiYSBzdXBwb3J0IGltbWVkaWF0ZWx5XG5zdXBwb3J0RWxlbS5zdHlsZS5jc3NUZXh0ID0gXCJiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMSwxLDEsLjUpXCI7XG5zdXBwb3J0LnJnYmEgPSBzdXBwb3J0RWxlbS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IuaW5kZXhPZiggXCJyZ2JhXCIgKSA+IC0xO1xuXG4vLyBEZWZpbmUgY2FjaGUgbmFtZSBhbmQgYWxwaGEgcHJvcGVydGllc1xuLy8gZm9yIHJnYmEgYW5kIGhzbGEgc3BhY2VzXG5lYWNoKCBzcGFjZXMsIGZ1bmN0aW9uKCBzcGFjZU5hbWUsIHNwYWNlICkge1xuXHRzcGFjZS5jYWNoZSA9IFwiX1wiICsgc3BhY2VOYW1lO1xuXHRzcGFjZS5wcm9wcy5hbHBoYSA9IHtcblx0XHRpZHg6IDMsXG5cdFx0dHlwZTogXCJwZXJjZW50XCIsXG5cdFx0ZGVmOiAxXG5cdH07XG59ICk7XG5cbmZ1bmN0aW9uIGNsYW1wKCB2YWx1ZSwgcHJvcCwgYWxsb3dFbXB0eSApIHtcblx0dmFyIHR5cGUgPSBwcm9wVHlwZXNbIHByb3AudHlwZSBdIHx8IHt9O1xuXG5cdGlmICggdmFsdWUgPT0gbnVsbCApIHtcblx0XHRyZXR1cm4gKCBhbGxvd0VtcHR5IHx8ICFwcm9wLmRlZiApID8gbnVsbCA6IHByb3AuZGVmO1xuXHR9XG5cblx0Ly8gfn4gaXMgYW4gc2hvcnQgd2F5IG9mIGRvaW5nIGZsb29yIGZvciBwb3NpdGl2ZSBudW1iZXJzXG5cdHZhbHVlID0gdHlwZS5mbG9vciA/IH5+dmFsdWUgOiBwYXJzZUZsb2F0KCB2YWx1ZSApO1xuXG5cdC8vIElFIHdpbGwgcGFzcyBpbiBlbXB0eSBzdHJpbmdzIGFzIHZhbHVlIGZvciBhbHBoYSxcblx0Ly8gd2hpY2ggd2lsbCBoaXQgdGhpcyBjYXNlXG5cdGlmICggaXNOYU4oIHZhbHVlICkgKSB7XG5cdFx0cmV0dXJuIHByb3AuZGVmO1xuXHR9XG5cblx0aWYgKCB0eXBlLm1vZCApIHtcblxuXHRcdC8vIFdlIGFkZCBtb2QgYmVmb3JlIG1vZGRpbmcgdG8gbWFrZSBzdXJlIHRoYXQgbmVnYXRpdmVzIHZhbHVlc1xuXHRcdC8vIGdldCBjb252ZXJ0ZWQgcHJvcGVybHk6IC0xMCAtPiAzNTBcblx0XHRyZXR1cm4gKCB2YWx1ZSArIHR5cGUubW9kICkgJSB0eXBlLm1vZDtcblx0fVxuXG5cdC8vIEZvciBub3cgYWxsIHByb3BlcnR5IHR5cGVzIHdpdGhvdXQgbW9kIGhhdmUgbWluIGFuZCBtYXhcblx0cmV0dXJuIDAgPiB2YWx1ZSA/IDAgOiB0eXBlLm1heCA8IHZhbHVlID8gdHlwZS5tYXggOiB2YWx1ZTtcbn1cblxuZnVuY3Rpb24gc3RyaW5nUGFyc2UoIHN0cmluZyApIHtcblx0dmFyIGluc3QgPSBjb2xvcigpLFxuXHRcdHJnYmEgPSBpbnN0Ll9yZ2JhID0gW107XG5cblx0c3RyaW5nID0gc3RyaW5nLnRvTG93ZXJDYXNlKCk7XG5cblx0ZWFjaCggc3RyaW5nUGFyc2VycywgZnVuY3Rpb24oIGksIHBhcnNlciApIHtcblx0XHR2YXIgcGFyc2VkLFxuXHRcdFx0bWF0Y2ggPSBwYXJzZXIucmUuZXhlYyggc3RyaW5nICksXG5cdFx0XHR2YWx1ZXMgPSBtYXRjaCAmJiBwYXJzZXIucGFyc2UoIG1hdGNoICksXG5cdFx0XHRzcGFjZU5hbWUgPSBwYXJzZXIuc3BhY2UgfHwgXCJyZ2JhXCI7XG5cblx0XHRpZiAoIHZhbHVlcyApIHtcblx0XHRcdHBhcnNlZCA9IGluc3RbIHNwYWNlTmFtZSBdKCB2YWx1ZXMgKTtcblxuXHRcdFx0Ly8gSWYgdGhpcyB3YXMgYW4gcmdiYSBwYXJzZSB0aGUgYXNzaWdubWVudCBtaWdodCBoYXBwZW4gdHdpY2Vcblx0XHRcdC8vIG9oIHdlbGwuLi4uXG5cdFx0XHRpbnN0WyBzcGFjZXNbIHNwYWNlTmFtZSBdLmNhY2hlIF0gPSBwYXJzZWRbIHNwYWNlc1sgc3BhY2VOYW1lIF0uY2FjaGUgXTtcblx0XHRcdHJnYmEgPSBpbnN0Ll9yZ2JhID0gcGFyc2VkLl9yZ2JhO1xuXG5cdFx0XHQvLyBFeGl0IGVhY2goIHN0cmluZ1BhcnNlcnMgKSBoZXJlIGJlY2F1c2Ugd2UgbWF0Y2hlZFxuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0fSApO1xuXG5cdC8vIEZvdW5kIGEgc3RyaW5nUGFyc2VyIHRoYXQgaGFuZGxlZCBpdFxuXHRpZiAoIHJnYmEubGVuZ3RoICkge1xuXG5cdFx0Ly8gSWYgdGhpcyBjYW1lIGZyb20gYSBwYXJzZWQgc3RyaW5nLCBmb3JjZSBcInRyYW5zcGFyZW50XCIgd2hlbiBhbHBoYSBpcyAwXG5cdFx0Ly8gY2hyb21lLCAoYW5kIG1heWJlIG90aGVycykgcmV0dXJuIFwidHJhbnNwYXJlbnRcIiBhcyByZ2JhKDAsMCwwLDApXG5cdFx0aWYgKCByZ2JhLmpvaW4oKSA9PT0gXCIwLDAsMCwwXCIgKSB7XG5cdFx0XHRqUXVlcnkuZXh0ZW5kKCByZ2JhLCBjb2xvcnMudHJhbnNwYXJlbnQgKTtcblx0XHR9XG5cdFx0cmV0dXJuIGluc3Q7XG5cdH1cblxuXHQvLyBOYW1lZCBjb2xvcnNcblx0cmV0dXJuIGNvbG9yc1sgc3RyaW5nIF07XG59XG5cbmNvbG9yLmZuID0galF1ZXJ5LmV4dGVuZCggY29sb3IucHJvdG90eXBlLCB7XG5cdHBhcnNlOiBmdW5jdGlvbiggcmVkLCBncmVlbiwgYmx1ZSwgYWxwaGEgKSB7XG5cdFx0aWYgKCByZWQgPT09IHVuZGVmaW5lZCApIHtcblx0XHRcdHRoaXMuX3JnYmEgPSBbIG51bGwsIG51bGwsIG51bGwsIG51bGwgXTtcblx0XHRcdHJldHVybiB0aGlzO1xuXHRcdH1cblx0XHRpZiAoIHJlZC5qcXVlcnkgfHwgcmVkLm5vZGVUeXBlICkge1xuXHRcdFx0cmVkID0galF1ZXJ5KCByZWQgKS5jc3MoIGdyZWVuICk7XG5cdFx0XHRncmVlbiA9IHVuZGVmaW5lZDtcblx0XHR9XG5cblx0XHR2YXIgaW5zdCA9IHRoaXMsXG5cdFx0XHR0eXBlID0galF1ZXJ5LnR5cGUoIHJlZCApLFxuXHRcdFx0cmdiYSA9IHRoaXMuX3JnYmEgPSBbXTtcblxuXHRcdC8vIE1vcmUgdGhhbiAxIGFyZ3VtZW50IHNwZWNpZmllZCAtIGFzc3VtZSAoIHJlZCwgZ3JlZW4sIGJsdWUsIGFscGhhIClcblx0XHRpZiAoIGdyZWVuICE9PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRyZWQgPSBbIHJlZCwgZ3JlZW4sIGJsdWUsIGFscGhhIF07XG5cdFx0XHR0eXBlID0gXCJhcnJheVwiO1xuXHRcdH1cblxuXHRcdGlmICggdHlwZSA9PT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdHJldHVybiB0aGlzLnBhcnNlKCBzdHJpbmdQYXJzZSggcmVkICkgfHwgY29sb3JzLl9kZWZhdWx0ICk7XG5cdFx0fVxuXG5cdFx0aWYgKCB0eXBlID09PSBcImFycmF5XCIgKSB7XG5cdFx0XHRlYWNoKCBzcGFjZXMucmdiYS5wcm9wcywgZnVuY3Rpb24oIGtleSwgcHJvcCApIHtcblx0XHRcdFx0cmdiYVsgcHJvcC5pZHggXSA9IGNsYW1wKCByZWRbIHByb3AuaWR4IF0sIHByb3AgKTtcblx0XHRcdH0gKTtcblx0XHRcdHJldHVybiB0aGlzO1xuXHRcdH1cblxuXHRcdGlmICggdHlwZSA9PT0gXCJvYmplY3RcIiApIHtcblx0XHRcdGlmICggcmVkIGluc3RhbmNlb2YgY29sb3IgKSB7XG5cdFx0XHRcdGVhY2goIHNwYWNlcywgZnVuY3Rpb24oIHNwYWNlTmFtZSwgc3BhY2UgKSB7XG5cdFx0XHRcdFx0aWYgKCByZWRbIHNwYWNlLmNhY2hlIF0gKSB7XG5cdFx0XHRcdFx0XHRpbnN0WyBzcGFjZS5jYWNoZSBdID0gcmVkWyBzcGFjZS5jYWNoZSBdLnNsaWNlKCk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9ICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRlYWNoKCBzcGFjZXMsIGZ1bmN0aW9uKCBzcGFjZU5hbWUsIHNwYWNlICkge1xuXHRcdFx0XHRcdHZhciBjYWNoZSA9IHNwYWNlLmNhY2hlO1xuXHRcdFx0XHRcdGVhY2goIHNwYWNlLnByb3BzLCBmdW5jdGlvbigga2V5LCBwcm9wICkge1xuXG5cdFx0XHRcdFx0XHQvLyBJZiB0aGUgY2FjaGUgZG9lc24ndCBleGlzdCwgYW5kIHdlIGtub3cgaG93IHRvIGNvbnZlcnRcblx0XHRcdFx0XHRcdGlmICggIWluc3RbIGNhY2hlIF0gJiYgc3BhY2UudG8gKSB7XG5cblx0XHRcdFx0XHRcdFx0Ly8gSWYgdGhlIHZhbHVlIHdhcyBudWxsLCB3ZSBkb24ndCBuZWVkIHRvIGNvcHkgaXRcblx0XHRcdFx0XHRcdFx0Ly8gaWYgdGhlIGtleSB3YXMgYWxwaGEsIHdlIGRvbid0IG5lZWQgdG8gY29weSBpdCBlaXRoZXJcblx0XHRcdFx0XHRcdFx0aWYgKCBrZXkgPT09IFwiYWxwaGFcIiB8fCByZWRbIGtleSBdID09IG51bGwgKSB7XG5cdFx0XHRcdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdGluc3RbIGNhY2hlIF0gPSBzcGFjZS50byggaW5zdC5fcmdiYSApO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHQvLyBUaGlzIGlzIHRoZSBvbmx5IGNhc2Ugd2hlcmUgd2UgYWxsb3cgbnVsbHMgZm9yIEFMTCBwcm9wZXJ0aWVzLlxuXHRcdFx0XHRcdFx0Ly8gY2FsbCBjbGFtcCB3aXRoIGFsd2F5c0FsbG93RW1wdHlcblx0XHRcdFx0XHRcdGluc3RbIGNhY2hlIF1bIHByb3AuaWR4IF0gPSBjbGFtcCggcmVkWyBrZXkgXSwgcHJvcCwgdHJ1ZSApO1xuXHRcdFx0XHRcdH0gKTtcblxuXHRcdFx0XHRcdC8vIEV2ZXJ5dGhpbmcgZGVmaW5lZCBidXQgYWxwaGE/XG5cdFx0XHRcdFx0aWYgKCBpbnN0WyBjYWNoZSBdICYmXG5cdFx0XHRcdFx0XHRcdGpRdWVyeS5pbkFycmF5KCBudWxsLCBpbnN0WyBjYWNoZSBdLnNsaWNlKCAwLCAzICkgKSA8IDAgKSB7XG5cblx0XHRcdFx0XHRcdC8vIFVzZSB0aGUgZGVmYXVsdCBvZiAxXG5cdFx0XHRcdFx0XHRpbnN0WyBjYWNoZSBdWyAzIF0gPSAxO1xuXHRcdFx0XHRcdFx0aWYgKCBzcGFjZS5mcm9tICkge1xuXHRcdFx0XHRcdFx0XHRpbnN0Ll9yZ2JhID0gc3BhY2UuZnJvbSggaW5zdFsgY2FjaGUgXSApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSApO1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIHRoaXM7XG5cdFx0fVxuXHR9LFxuXHRpczogZnVuY3Rpb24oIGNvbXBhcmUgKSB7XG5cdFx0dmFyIGlzID0gY29sb3IoIGNvbXBhcmUgKSxcblx0XHRcdHNhbWUgPSB0cnVlLFxuXHRcdFx0aW5zdCA9IHRoaXM7XG5cblx0XHRlYWNoKCBzcGFjZXMsIGZ1bmN0aW9uKCBfLCBzcGFjZSApIHtcblx0XHRcdHZhciBsb2NhbENhY2hlLFxuXHRcdFx0XHRpc0NhY2hlID0gaXNbIHNwYWNlLmNhY2hlIF07XG5cdFx0XHRpZiAoIGlzQ2FjaGUgKSB7XG5cdFx0XHRcdGxvY2FsQ2FjaGUgPSBpbnN0WyBzcGFjZS5jYWNoZSBdIHx8IHNwYWNlLnRvICYmIHNwYWNlLnRvKCBpbnN0Ll9yZ2JhICkgfHwgW107XG5cdFx0XHRcdGVhY2goIHNwYWNlLnByb3BzLCBmdW5jdGlvbiggXywgcHJvcCApIHtcblx0XHRcdFx0XHRpZiAoIGlzQ2FjaGVbIHByb3AuaWR4IF0gIT0gbnVsbCApIHtcblx0XHRcdFx0XHRcdHNhbWUgPSAoIGlzQ2FjaGVbIHByb3AuaWR4IF0gPT09IGxvY2FsQ2FjaGVbIHByb3AuaWR4IF0gKTtcblx0XHRcdFx0XHRcdHJldHVybiBzYW1lO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSApO1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIHNhbWU7XG5cdFx0fSApO1xuXHRcdHJldHVybiBzYW1lO1xuXHR9LFxuXHRfc3BhY2U6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciB1c2VkID0gW10sXG5cdFx0XHRpbnN0ID0gdGhpcztcblx0XHRlYWNoKCBzcGFjZXMsIGZ1bmN0aW9uKCBzcGFjZU5hbWUsIHNwYWNlICkge1xuXHRcdFx0aWYgKCBpbnN0WyBzcGFjZS5jYWNoZSBdICkge1xuXHRcdFx0XHR1c2VkLnB1c2goIHNwYWNlTmFtZSApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblx0XHRyZXR1cm4gdXNlZC5wb3AoKTtcblx0fSxcblx0dHJhbnNpdGlvbjogZnVuY3Rpb24oIG90aGVyLCBkaXN0YW5jZSApIHtcblx0XHR2YXIgZW5kID0gY29sb3IoIG90aGVyICksXG5cdFx0XHRzcGFjZU5hbWUgPSBlbmQuX3NwYWNlKCksXG5cdFx0XHRzcGFjZSA9IHNwYWNlc1sgc3BhY2VOYW1lIF0sXG5cdFx0XHRzdGFydENvbG9yID0gdGhpcy5hbHBoYSgpID09PSAwID8gY29sb3IoIFwidHJhbnNwYXJlbnRcIiApIDogdGhpcyxcblx0XHRcdHN0YXJ0ID0gc3RhcnRDb2xvclsgc3BhY2UuY2FjaGUgXSB8fCBzcGFjZS50byggc3RhcnRDb2xvci5fcmdiYSApLFxuXHRcdFx0cmVzdWx0ID0gc3RhcnQuc2xpY2UoKTtcblxuXHRcdGVuZCA9IGVuZFsgc3BhY2UuY2FjaGUgXTtcblx0XHRlYWNoKCBzcGFjZS5wcm9wcywgZnVuY3Rpb24oIGtleSwgcHJvcCApIHtcblx0XHRcdHZhciBpbmRleCA9IHByb3AuaWR4LFxuXHRcdFx0XHRzdGFydFZhbHVlID0gc3RhcnRbIGluZGV4IF0sXG5cdFx0XHRcdGVuZFZhbHVlID0gZW5kWyBpbmRleCBdLFxuXHRcdFx0XHR0eXBlID0gcHJvcFR5cGVzWyBwcm9wLnR5cGUgXSB8fCB7fTtcblxuXHRcdFx0Ly8gSWYgbnVsbCwgZG9uJ3Qgb3ZlcnJpZGUgc3RhcnQgdmFsdWVcblx0XHRcdGlmICggZW5kVmFsdWUgPT09IG51bGwgKSB7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblxuXHRcdFx0Ly8gSWYgbnVsbCAtIHVzZSBlbmRcblx0XHRcdGlmICggc3RhcnRWYWx1ZSA9PT0gbnVsbCApIHtcblx0XHRcdFx0cmVzdWx0WyBpbmRleCBdID0gZW5kVmFsdWU7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRpZiAoIHR5cGUubW9kICkge1xuXHRcdFx0XHRcdGlmICggZW5kVmFsdWUgLSBzdGFydFZhbHVlID4gdHlwZS5tb2QgLyAyICkge1xuXHRcdFx0XHRcdFx0c3RhcnRWYWx1ZSArPSB0eXBlLm1vZDtcblx0XHRcdFx0XHR9IGVsc2UgaWYgKCBzdGFydFZhbHVlIC0gZW5kVmFsdWUgPiB0eXBlLm1vZCAvIDIgKSB7XG5cdFx0XHRcdFx0XHRzdGFydFZhbHVlIC09IHR5cGUubW9kO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0XHRyZXN1bHRbIGluZGV4IF0gPSBjbGFtcCggKCBlbmRWYWx1ZSAtIHN0YXJ0VmFsdWUgKSAqIGRpc3RhbmNlICsgc3RhcnRWYWx1ZSwgcHJvcCApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblx0XHRyZXR1cm4gdGhpc1sgc3BhY2VOYW1lIF0oIHJlc3VsdCApO1xuXHR9LFxuXHRibGVuZDogZnVuY3Rpb24oIG9wYXF1ZSApIHtcblxuXHRcdC8vIElmIHdlIGFyZSBhbHJlYWR5IG9wYXF1ZSAtIHJldHVybiBvdXJzZWxmXG5cdFx0aWYgKCB0aGlzLl9yZ2JhWyAzIF0gPT09IDEgKSB7XG5cdFx0XHRyZXR1cm4gdGhpcztcblx0XHR9XG5cblx0XHR2YXIgcmdiID0gdGhpcy5fcmdiYS5zbGljZSgpLFxuXHRcdFx0YSA9IHJnYi5wb3AoKSxcblx0XHRcdGJsZW5kID0gY29sb3IoIG9wYXF1ZSApLl9yZ2JhO1xuXG5cdFx0cmV0dXJuIGNvbG9yKCBqUXVlcnkubWFwKCByZ2IsIGZ1bmN0aW9uKCB2LCBpICkge1xuXHRcdFx0cmV0dXJuICggMSAtIGEgKSAqIGJsZW5kWyBpIF0gKyBhICogdjtcblx0XHR9ICkgKTtcblx0fSxcblx0dG9SZ2JhU3RyaW5nOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgcHJlZml4ID0gXCJyZ2JhKFwiLFxuXHRcdFx0cmdiYSA9IGpRdWVyeS5tYXAoIHRoaXMuX3JnYmEsIGZ1bmN0aW9uKCB2LCBpICkge1xuXHRcdFx0XHRyZXR1cm4gdiA9PSBudWxsID8gKCBpID4gMiA/IDEgOiAwICkgOiB2O1xuXHRcdFx0fSApO1xuXG5cdFx0aWYgKCByZ2JhWyAzIF0gPT09IDEgKSB7XG5cdFx0XHRyZ2JhLnBvcCgpO1xuXHRcdFx0cHJlZml4ID0gXCJyZ2IoXCI7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHByZWZpeCArIHJnYmEuam9pbigpICsgXCIpXCI7XG5cdH0sXG5cdHRvSHNsYVN0cmluZzogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHByZWZpeCA9IFwiaHNsYShcIixcblx0XHRcdGhzbGEgPSBqUXVlcnkubWFwKCB0aGlzLmhzbGEoKSwgZnVuY3Rpb24oIHYsIGkgKSB7XG5cdFx0XHRcdGlmICggdiA9PSBudWxsICkge1xuXHRcdFx0XHRcdHYgPSBpID4gMiA/IDEgOiAwO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gQ2F0Y2ggMSBhbmQgMlxuXHRcdFx0XHRpZiAoIGkgJiYgaSA8IDMgKSB7XG5cdFx0XHRcdFx0diA9IE1hdGgucm91bmQoIHYgKiAxMDAgKSArIFwiJVwiO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHJldHVybiB2O1xuXHRcdFx0fSApO1xuXG5cdFx0aWYgKCBoc2xhWyAzIF0gPT09IDEgKSB7XG5cdFx0XHRoc2xhLnBvcCgpO1xuXHRcdFx0cHJlZml4ID0gXCJoc2woXCI7XG5cdFx0fVxuXHRcdHJldHVybiBwcmVmaXggKyBoc2xhLmpvaW4oKSArIFwiKVwiO1xuXHR9LFxuXHR0b0hleFN0cmluZzogZnVuY3Rpb24oIGluY2x1ZGVBbHBoYSApIHtcblx0XHR2YXIgcmdiYSA9IHRoaXMuX3JnYmEuc2xpY2UoKSxcblx0XHRcdGFscGhhID0gcmdiYS5wb3AoKTtcblxuXHRcdGlmICggaW5jbHVkZUFscGhhICkge1xuXHRcdFx0cmdiYS5wdXNoKCB+figgYWxwaGEgKiAyNTUgKSApO1xuXHRcdH1cblxuXHRcdHJldHVybiBcIiNcIiArIGpRdWVyeS5tYXAoIHJnYmEsIGZ1bmN0aW9uKCB2ICkge1xuXG5cdFx0XHQvLyBEZWZhdWx0IHRvIDAgd2hlbiBudWxscyBleGlzdFxuXHRcdFx0diA9ICggdiB8fCAwICkudG9TdHJpbmcoIDE2ICk7XG5cdFx0XHRyZXR1cm4gdi5sZW5ndGggPT09IDEgPyBcIjBcIiArIHYgOiB2O1xuXHRcdH0gKS5qb2luKCBcIlwiICk7XG5cdH0sXG5cdHRvU3RyaW5nOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5fcmdiYVsgMyBdID09PSAwID8gXCJ0cmFuc3BhcmVudFwiIDogdGhpcy50b1JnYmFTdHJpbmcoKTtcblx0fVxufSApO1xuY29sb3IuZm4ucGFyc2UucHJvdG90eXBlID0gY29sb3IuZm47XG5cbi8vIEhzbGEgY29udmVyc2lvbnMgYWRhcHRlZCBmcm9tOlxuLy8gaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC9tYWFzaGFhY2svc291cmNlL2Jyb3dzZS9wYWNrYWdlcy9ncmFwaGljcy90cnVuay9zcmMvZ3JhcGhpY3MvY29sb3JzL0hVRTJSR0IuYXM/cj01MDIxXG5cbmZ1bmN0aW9uIGh1ZTJyZ2IoIHAsIHEsIGggKSB7XG5cdGggPSAoIGggKyAxICkgJSAxO1xuXHRpZiAoIGggKiA2IDwgMSApIHtcblx0XHRyZXR1cm4gcCArICggcSAtIHAgKSAqIGggKiA2O1xuXHR9XG5cdGlmICggaCAqIDIgPCAxICkge1xuXHRcdHJldHVybiBxO1xuXHR9XG5cdGlmICggaCAqIDMgPCAyICkge1xuXHRcdHJldHVybiBwICsgKCBxIC0gcCApICogKCAoIDIgLyAzICkgLSBoICkgKiA2O1xuXHR9XG5cdHJldHVybiBwO1xufVxuXG5zcGFjZXMuaHNsYS50byA9IGZ1bmN0aW9uKCByZ2JhICkge1xuXHRpZiAoIHJnYmFbIDAgXSA9PSBudWxsIHx8IHJnYmFbIDEgXSA9PSBudWxsIHx8IHJnYmFbIDIgXSA9PSBudWxsICkge1xuXHRcdHJldHVybiBbIG51bGwsIG51bGwsIG51bGwsIHJnYmFbIDMgXSBdO1xuXHR9XG5cdHZhciByID0gcmdiYVsgMCBdIC8gMjU1LFxuXHRcdGcgPSByZ2JhWyAxIF0gLyAyNTUsXG5cdFx0YiA9IHJnYmFbIDIgXSAvIDI1NSxcblx0XHRhID0gcmdiYVsgMyBdLFxuXHRcdG1heCA9IE1hdGgubWF4KCByLCBnLCBiICksXG5cdFx0bWluID0gTWF0aC5taW4oIHIsIGcsIGIgKSxcblx0XHRkaWZmID0gbWF4IC0gbWluLFxuXHRcdGFkZCA9IG1heCArIG1pbixcblx0XHRsID0gYWRkICogMC41LFxuXHRcdGgsIHM7XG5cblx0aWYgKCBtaW4gPT09IG1heCApIHtcblx0XHRoID0gMDtcblx0fSBlbHNlIGlmICggciA9PT0gbWF4ICkge1xuXHRcdGggPSAoIDYwICogKCBnIC0gYiApIC8gZGlmZiApICsgMzYwO1xuXHR9IGVsc2UgaWYgKCBnID09PSBtYXggKSB7XG5cdFx0aCA9ICggNjAgKiAoIGIgLSByICkgLyBkaWZmICkgKyAxMjA7XG5cdH0gZWxzZSB7XG5cdFx0aCA9ICggNjAgKiAoIHIgLSBnICkgLyBkaWZmICkgKyAyNDA7XG5cdH1cblxuXHQvLyBDaHJvbWEgKGRpZmYpID09IDAgbWVhbnMgZ3JleXNjYWxlIHdoaWNoLCBieSBkZWZpbml0aW9uLCBzYXR1cmF0aW9uID0gMCVcblx0Ly8gb3RoZXJ3aXNlLCBzYXR1cmF0aW9uIGlzIGJhc2VkIG9uIHRoZSByYXRpbyBvZiBjaHJvbWEgKGRpZmYpIHRvIGxpZ2h0bmVzcyAoYWRkKVxuXHRpZiAoIGRpZmYgPT09IDAgKSB7XG5cdFx0cyA9IDA7XG5cdH0gZWxzZSBpZiAoIGwgPD0gMC41ICkge1xuXHRcdHMgPSBkaWZmIC8gYWRkO1xuXHR9IGVsc2Uge1xuXHRcdHMgPSBkaWZmIC8gKCAyIC0gYWRkICk7XG5cdH1cblx0cmV0dXJuIFsgTWF0aC5yb3VuZCggaCApICUgMzYwLCBzLCBsLCBhID09IG51bGwgPyAxIDogYSBdO1xufTtcblxuc3BhY2VzLmhzbGEuZnJvbSA9IGZ1bmN0aW9uKCBoc2xhICkge1xuXHRpZiAoIGhzbGFbIDAgXSA9PSBudWxsIHx8IGhzbGFbIDEgXSA9PSBudWxsIHx8IGhzbGFbIDIgXSA9PSBudWxsICkge1xuXHRcdHJldHVybiBbIG51bGwsIG51bGwsIG51bGwsIGhzbGFbIDMgXSBdO1xuXHR9XG5cdHZhciBoID0gaHNsYVsgMCBdIC8gMzYwLFxuXHRcdHMgPSBoc2xhWyAxIF0sXG5cdFx0bCA9IGhzbGFbIDIgXSxcblx0XHRhID0gaHNsYVsgMyBdLFxuXHRcdHEgPSBsIDw9IDAuNSA/IGwgKiAoIDEgKyBzICkgOiBsICsgcyAtIGwgKiBzLFxuXHRcdHAgPSAyICogbCAtIHE7XG5cblx0cmV0dXJuIFtcblx0XHRNYXRoLnJvdW5kKCBodWUycmdiKCBwLCBxLCBoICsgKCAxIC8gMyApICkgKiAyNTUgKSxcblx0XHRNYXRoLnJvdW5kKCBodWUycmdiKCBwLCBxLCBoICkgKiAyNTUgKSxcblx0XHRNYXRoLnJvdW5kKCBodWUycmdiKCBwLCBxLCBoIC0gKCAxIC8gMyApICkgKiAyNTUgKSxcblx0XHRhXG5cdF07XG59O1xuXG5lYWNoKCBzcGFjZXMsIGZ1bmN0aW9uKCBzcGFjZU5hbWUsIHNwYWNlICkge1xuXHR2YXIgcHJvcHMgPSBzcGFjZS5wcm9wcyxcblx0XHRjYWNoZSA9IHNwYWNlLmNhY2hlLFxuXHRcdHRvID0gc3BhY2UudG8sXG5cdFx0ZnJvbSA9IHNwYWNlLmZyb207XG5cblx0Ly8gTWFrZXMgcmdiYSgpIGFuZCBoc2xhKClcblx0Y29sb3IuZm5bIHNwYWNlTmFtZSBdID0gZnVuY3Rpb24oIHZhbHVlICkge1xuXG5cdFx0Ly8gR2VuZXJhdGUgYSBjYWNoZSBmb3IgdGhpcyBzcGFjZSBpZiBpdCBkb2Vzbid0IGV4aXN0XG5cdFx0aWYgKCB0byAmJiAhdGhpc1sgY2FjaGUgXSApIHtcblx0XHRcdHRoaXNbIGNhY2hlIF0gPSB0byggdGhpcy5fcmdiYSApO1xuXHRcdH1cblx0XHRpZiAoIHZhbHVlID09PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRyZXR1cm4gdGhpc1sgY2FjaGUgXS5zbGljZSgpO1xuXHRcdH1cblxuXHRcdHZhciByZXQsXG5cdFx0XHR0eXBlID0galF1ZXJ5LnR5cGUoIHZhbHVlICksXG5cdFx0XHRhcnIgPSAoIHR5cGUgPT09IFwiYXJyYXlcIiB8fCB0eXBlID09PSBcIm9iamVjdFwiICkgPyB2YWx1ZSA6IGFyZ3VtZW50cyxcblx0XHRcdGxvY2FsID0gdGhpc1sgY2FjaGUgXS5zbGljZSgpO1xuXG5cdFx0ZWFjaCggcHJvcHMsIGZ1bmN0aW9uKCBrZXksIHByb3AgKSB7XG5cdFx0XHR2YXIgdmFsID0gYXJyWyB0eXBlID09PSBcIm9iamVjdFwiID8ga2V5IDogcHJvcC5pZHggXTtcblx0XHRcdGlmICggdmFsID09IG51bGwgKSB7XG5cdFx0XHRcdHZhbCA9IGxvY2FsWyBwcm9wLmlkeCBdO1xuXHRcdFx0fVxuXHRcdFx0bG9jYWxbIHByb3AuaWR4IF0gPSBjbGFtcCggdmFsLCBwcm9wICk7XG5cdFx0fSApO1xuXG5cdFx0aWYgKCBmcm9tICkge1xuXHRcdFx0cmV0ID0gY29sb3IoIGZyb20oIGxvY2FsICkgKTtcblx0XHRcdHJldFsgY2FjaGUgXSA9IGxvY2FsO1xuXHRcdFx0cmV0dXJuIHJldDtcblx0XHR9IGVsc2Uge1xuXHRcdFx0cmV0dXJuIGNvbG9yKCBsb2NhbCApO1xuXHRcdH1cblx0fTtcblxuXHQvLyBNYWtlcyByZWQoKSBncmVlbigpIGJsdWUoKSBhbHBoYSgpIGh1ZSgpIHNhdHVyYXRpb24oKSBsaWdodG5lc3MoKVxuXHRlYWNoKCBwcm9wcywgZnVuY3Rpb24oIGtleSwgcHJvcCApIHtcblxuXHRcdC8vIEFscGhhIGlzIGluY2x1ZGVkIGluIG1vcmUgdGhhbiBvbmUgc3BhY2Vcblx0XHRpZiAoIGNvbG9yLmZuWyBrZXkgXSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0Y29sb3IuZm5bIGtleSBdID0gZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdFx0dmFyIHZ0eXBlID0galF1ZXJ5LnR5cGUoIHZhbHVlICksXG5cdFx0XHRcdGZuID0gKCBrZXkgPT09IFwiYWxwaGFcIiA/ICggdGhpcy5faHNsYSA/IFwiaHNsYVwiIDogXCJyZ2JhXCIgKSA6IHNwYWNlTmFtZSApLFxuXHRcdFx0XHRsb2NhbCA9IHRoaXNbIGZuIF0oKSxcblx0XHRcdFx0Y3VyID0gbG9jYWxbIHByb3AuaWR4IF0sXG5cdFx0XHRcdG1hdGNoO1xuXG5cdFx0XHRpZiAoIHZ0eXBlID09PSBcInVuZGVmaW5lZFwiICkge1xuXHRcdFx0XHRyZXR1cm4gY3VyO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHZ0eXBlID09PSBcImZ1bmN0aW9uXCIgKSB7XG5cdFx0XHRcdHZhbHVlID0gdmFsdWUuY2FsbCggdGhpcywgY3VyICk7XG5cdFx0XHRcdHZ0eXBlID0galF1ZXJ5LnR5cGUoIHZhbHVlICk7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIHZhbHVlID09IG51bGwgJiYgcHJvcC5lbXB0eSApIHtcblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIHZ0eXBlID09PSBcInN0cmluZ1wiICkge1xuXHRcdFx0XHRtYXRjaCA9IHJwbHVzZXF1YWxzLmV4ZWMoIHZhbHVlICk7XG5cdFx0XHRcdGlmICggbWF0Y2ggKSB7XG5cdFx0XHRcdFx0dmFsdWUgPSBjdXIgKyBwYXJzZUZsb2F0KCBtYXRjaFsgMiBdICkgKiAoIG1hdGNoWyAxIF0gPT09IFwiK1wiID8gMSA6IC0xICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGxvY2FsWyBwcm9wLmlkeCBdID0gdmFsdWU7XG5cdFx0XHRyZXR1cm4gdGhpc1sgZm4gXSggbG9jYWwgKTtcblx0XHR9O1xuXHR9ICk7XG59ICk7XG5cbi8vIEFkZCBjc3NIb29rIGFuZCAuZnguc3RlcCBmdW5jdGlvbiBmb3IgZWFjaCBuYW1lZCBob29rLlxuLy8gYWNjZXB0IGEgc3BhY2Ugc2VwYXJhdGVkIHN0cmluZyBvZiBwcm9wZXJ0aWVzXG5jb2xvci5ob29rID0gZnVuY3Rpb24oIGhvb2sgKSB7XG5cdHZhciBob29rcyA9IGhvb2suc3BsaXQoIFwiIFwiICk7XG5cdGVhY2goIGhvb2tzLCBmdW5jdGlvbiggaSwgaG9vayApIHtcblx0XHRqUXVlcnkuY3NzSG9va3NbIGhvb2sgXSA9IHtcblx0XHRcdHNldDogZnVuY3Rpb24oIGVsZW0sIHZhbHVlICkge1xuXHRcdFx0XHR2YXIgcGFyc2VkLCBjdXJFbGVtLFxuXHRcdFx0XHRcdGJhY2tncm91bmRDb2xvciA9IFwiXCI7XG5cblx0XHRcdFx0aWYgKCB2YWx1ZSAhPT0gXCJ0cmFuc3BhcmVudFwiICYmICggalF1ZXJ5LnR5cGUoIHZhbHVlICkgIT09IFwic3RyaW5nXCIgfHxcblx0XHRcdFx0XHRcdCggcGFyc2VkID0gc3RyaW5nUGFyc2UoIHZhbHVlICkgKSApICkge1xuXHRcdFx0XHRcdHZhbHVlID0gY29sb3IoIHBhcnNlZCB8fCB2YWx1ZSApO1xuXHRcdFx0XHRcdGlmICggIXN1cHBvcnQucmdiYSAmJiB2YWx1ZS5fcmdiYVsgMyBdICE9PSAxICkge1xuXHRcdFx0XHRcdFx0Y3VyRWxlbSA9IGhvb2sgPT09IFwiYmFja2dyb3VuZENvbG9yXCIgPyBlbGVtLnBhcmVudE5vZGUgOiBlbGVtO1xuXHRcdFx0XHRcdFx0d2hpbGUgKFxuXHRcdFx0XHRcdFx0XHQoIGJhY2tncm91bmRDb2xvciA9PT0gXCJcIiB8fCBiYWNrZ3JvdW5kQ29sb3IgPT09IFwidHJhbnNwYXJlbnRcIiApICYmXG5cdFx0XHRcdFx0XHRcdGN1ckVsZW0gJiYgY3VyRWxlbS5zdHlsZVxuXHRcdFx0XHRcdFx0KSB7XG5cdFx0XHRcdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0XHRcdFx0YmFja2dyb3VuZENvbG9yID0galF1ZXJ5LmNzcyggY3VyRWxlbSwgXCJiYWNrZ3JvdW5kQ29sb3JcIiApO1xuXHRcdFx0XHRcdFx0XHRcdGN1ckVsZW0gPSBjdXJFbGVtLnBhcmVudE5vZGU7XG5cdFx0XHRcdFx0XHRcdH0gY2F0Y2ggKCBlICkge1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdHZhbHVlID0gdmFsdWUuYmxlbmQoIGJhY2tncm91bmRDb2xvciAmJiBiYWNrZ3JvdW5kQ29sb3IgIT09IFwidHJhbnNwYXJlbnRcIiA/XG5cdFx0XHRcdFx0XHRcdGJhY2tncm91bmRDb2xvciA6XG5cdFx0XHRcdFx0XHRcdFwiX2RlZmF1bHRcIiApO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHZhbHVlID0gdmFsdWUudG9SZ2JhU3RyaW5nKCk7XG5cdFx0XHRcdH1cblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRlbGVtLnN0eWxlWyBob29rIF0gPSB2YWx1ZTtcblx0XHRcdFx0fSBjYXRjaCAoIGUgKSB7XG5cblx0XHRcdFx0XHQvLyBXcmFwcGVkIHRvIHByZXZlbnQgSUUgZnJvbSB0aHJvd2luZyBlcnJvcnMgb24gXCJpbnZhbGlkXCIgdmFsdWVzIGxpa2Vcblx0XHRcdFx0XHQvLyAnYXV0bycgb3IgJ2luaGVyaXQnXG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9O1xuXHRcdGpRdWVyeS5meC5zdGVwWyBob29rIF0gPSBmdW5jdGlvbiggZnggKSB7XG5cdFx0XHRpZiAoICFmeC5jb2xvckluaXQgKSB7XG5cdFx0XHRcdGZ4LnN0YXJ0ID0gY29sb3IoIGZ4LmVsZW0sIGhvb2sgKTtcblx0XHRcdFx0ZnguZW5kID0gY29sb3IoIGZ4LmVuZCApO1xuXHRcdFx0XHRmeC5jb2xvckluaXQgPSB0cnVlO1xuXHRcdFx0fVxuXHRcdFx0alF1ZXJ5LmNzc0hvb2tzWyBob29rIF0uc2V0KCBmeC5lbGVtLCBmeC5zdGFydC50cmFuc2l0aW9uKCBmeC5lbmQsIGZ4LnBvcyApICk7XG5cdFx0fTtcblx0fSApO1xuXG59O1xuXG5jb2xvci5ob29rKCBzdGVwSG9va3MgKTtcblxualF1ZXJ5LmNzc0hvb2tzLmJvcmRlckNvbG9yID0ge1xuXHRleHBhbmQ6IGZ1bmN0aW9uKCB2YWx1ZSApIHtcblx0XHR2YXIgZXhwYW5kZWQgPSB7fTtcblxuXHRcdGVhY2goIFsgXCJUb3BcIiwgXCJSaWdodFwiLCBcIkJvdHRvbVwiLCBcIkxlZnRcIiBdLCBmdW5jdGlvbiggaSwgcGFydCApIHtcblx0XHRcdGV4cGFuZGVkWyBcImJvcmRlclwiICsgcGFydCArIFwiQ29sb3JcIiBdID0gdmFsdWU7XG5cdFx0fSApO1xuXHRcdHJldHVybiBleHBhbmRlZDtcblx0fVxufTtcblxuLy8gQmFzaWMgY29sb3IgbmFtZXMgb25seS5cbi8vIFVzYWdlIG9mIGFueSBvZiB0aGUgb3RoZXIgY29sb3IgbmFtZXMgcmVxdWlyZXMgYWRkaW5nIHlvdXJzZWxmIG9yIGluY2x1ZGluZ1xuLy8ganF1ZXJ5LmNvbG9yLnN2Zy1uYW1lcy5qcy5cbmNvbG9ycyA9IGpRdWVyeS5Db2xvci5uYW1lcyA9IHtcblxuXHQvLyA0LjEuIEJhc2ljIGNvbG9yIGtleXdvcmRzXG5cdGFxdWE6IFwiIzAwZmZmZlwiLFxuXHRibGFjazogXCIjMDAwMDAwXCIsXG5cdGJsdWU6IFwiIzAwMDBmZlwiLFxuXHRmdWNoc2lhOiBcIiNmZjAwZmZcIixcblx0Z3JheTogXCIjODA4MDgwXCIsXG5cdGdyZWVuOiBcIiMwMDgwMDBcIixcblx0bGltZTogXCIjMDBmZjAwXCIsXG5cdG1hcm9vbjogXCIjODAwMDAwXCIsXG5cdG5hdnk6IFwiIzAwMDA4MFwiLFxuXHRvbGl2ZTogXCIjODA4MDAwXCIsXG5cdHB1cnBsZTogXCIjODAwMDgwXCIsXG5cdHJlZDogXCIjZmYwMDAwXCIsXG5cdHNpbHZlcjogXCIjYzBjMGMwXCIsXG5cdHRlYWw6IFwiIzAwODA4MFwiLFxuXHR3aGl0ZTogXCIjZmZmZmZmXCIsXG5cdHllbGxvdzogXCIjZmZmZjAwXCIsXG5cblx0Ly8gNC4yLjMuIFwidHJhbnNwYXJlbnRcIiBjb2xvciBrZXl3b3JkXG5cdHRyYW5zcGFyZW50OiBbIG51bGwsIG51bGwsIG51bGwsIDAgXSxcblxuXHRfZGVmYXVsdDogXCIjZmZmZmZmXCJcbn07XG5cbn0gKSggalF1ZXJ5ICk7XG5cbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqIENMQVNTIEFOSU1BVElPTlMgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbiggZnVuY3Rpb24oKSB7XG5cbnZhciBjbGFzc0FuaW1hdGlvbkFjdGlvbnMgPSBbIFwiYWRkXCIsIFwicmVtb3ZlXCIsIFwidG9nZ2xlXCIgXSxcblx0c2hvcnRoYW5kU3R5bGVzID0ge1xuXHRcdGJvcmRlcjogMSxcblx0XHRib3JkZXJCb3R0b206IDEsXG5cdFx0Ym9yZGVyQ29sb3I6IDEsXG5cdFx0Ym9yZGVyTGVmdDogMSxcblx0XHRib3JkZXJSaWdodDogMSxcblx0XHRib3JkZXJUb3A6IDEsXG5cdFx0Ym9yZGVyV2lkdGg6IDEsXG5cdFx0bWFyZ2luOiAxLFxuXHRcdHBhZGRpbmc6IDFcblx0fTtcblxuJC5lYWNoKFxuXHRbIFwiYm9yZGVyTGVmdFN0eWxlXCIsIFwiYm9yZGVyUmlnaHRTdHlsZVwiLCBcImJvcmRlckJvdHRvbVN0eWxlXCIsIFwiYm9yZGVyVG9wU3R5bGVcIiBdLFxuXHRmdW5jdGlvbiggXywgcHJvcCApIHtcblx0XHQkLmZ4LnN0ZXBbIHByb3AgXSA9IGZ1bmN0aW9uKCBmeCApIHtcblx0XHRcdGlmICggZnguZW5kICE9PSBcIm5vbmVcIiAmJiAhZnguc2V0QXR0ciB8fCBmeC5wb3MgPT09IDEgJiYgIWZ4LnNldEF0dHIgKSB7XG5cdFx0XHRcdGpRdWVyeS5zdHlsZSggZnguZWxlbSwgcHJvcCwgZnguZW5kICk7XG5cdFx0XHRcdGZ4LnNldEF0dHIgPSB0cnVlO1xuXHRcdFx0fVxuXHRcdH07XG5cdH1cbik7XG5cbmZ1bmN0aW9uIGdldEVsZW1lbnRTdHlsZXMoIGVsZW0gKSB7XG5cdHZhciBrZXksIGxlbixcblx0XHRzdHlsZSA9IGVsZW0ub3duZXJEb2N1bWVudC5kZWZhdWx0VmlldyA/XG5cdFx0XHRlbGVtLm93bmVyRG9jdW1lbnQuZGVmYXVsdFZpZXcuZ2V0Q29tcHV0ZWRTdHlsZSggZWxlbSwgbnVsbCApIDpcblx0XHRcdGVsZW0uY3VycmVudFN0eWxlLFxuXHRcdHN0eWxlcyA9IHt9O1xuXG5cdGlmICggc3R5bGUgJiYgc3R5bGUubGVuZ3RoICYmIHN0eWxlWyAwIF0gJiYgc3R5bGVbIHN0eWxlWyAwIF0gXSApIHtcblx0XHRsZW4gPSBzdHlsZS5sZW5ndGg7XG5cdFx0d2hpbGUgKCBsZW4tLSApIHtcblx0XHRcdGtleSA9IHN0eWxlWyBsZW4gXTtcblx0XHRcdGlmICggdHlwZW9mIHN0eWxlWyBrZXkgXSA9PT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdFx0c3R5bGVzWyAkLmNhbWVsQ2FzZSgga2V5ICkgXSA9IHN0eWxlWyBrZXkgXTtcblx0XHRcdH1cblx0XHR9XG5cblx0Ly8gU3VwcG9ydDogT3BlcmEsIElFIDw5XG5cdH0gZWxzZSB7XG5cdFx0Zm9yICgga2V5IGluIHN0eWxlICkge1xuXHRcdFx0aWYgKCB0eXBlb2Ygc3R5bGVbIGtleSBdID09PSBcInN0cmluZ1wiICkge1xuXHRcdFx0XHRzdHlsZXNbIGtleSBdID0gc3R5bGVbIGtleSBdO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdHJldHVybiBzdHlsZXM7XG59XG5cbmZ1bmN0aW9uIHN0eWxlRGlmZmVyZW5jZSggb2xkU3R5bGUsIG5ld1N0eWxlICkge1xuXHR2YXIgZGlmZiA9IHt9LFxuXHRcdG5hbWUsIHZhbHVlO1xuXG5cdGZvciAoIG5hbWUgaW4gbmV3U3R5bGUgKSB7XG5cdFx0dmFsdWUgPSBuZXdTdHlsZVsgbmFtZSBdO1xuXHRcdGlmICggb2xkU3R5bGVbIG5hbWUgXSAhPT0gdmFsdWUgKSB7XG5cdFx0XHRpZiAoICFzaG9ydGhhbmRTdHlsZXNbIG5hbWUgXSApIHtcblx0XHRcdFx0aWYgKCAkLmZ4LnN0ZXBbIG5hbWUgXSB8fCAhaXNOYU4oIHBhcnNlRmxvYXQoIHZhbHVlICkgKSApIHtcblx0XHRcdFx0XHRkaWZmWyBuYW1lIF0gPSB2YWx1ZTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdHJldHVybiBkaWZmO1xufVxuXG4vLyBTdXBwb3J0OiBqUXVlcnkgPDEuOFxuaWYgKCAhJC5mbi5hZGRCYWNrICkge1xuXHQkLmZuLmFkZEJhY2sgPSBmdW5jdGlvbiggc2VsZWN0b3IgKSB7XG5cdFx0cmV0dXJuIHRoaXMuYWRkKCBzZWxlY3RvciA9PSBudWxsID9cblx0XHRcdHRoaXMucHJldk9iamVjdCA6IHRoaXMucHJldk9iamVjdC5maWx0ZXIoIHNlbGVjdG9yIClcblx0XHQpO1xuXHR9O1xufVxuXG4kLmVmZmVjdHMuYW5pbWF0ZUNsYXNzID0gZnVuY3Rpb24oIHZhbHVlLCBkdXJhdGlvbiwgZWFzaW5nLCBjYWxsYmFjayApIHtcblx0dmFyIG8gPSAkLnNwZWVkKCBkdXJhdGlvbiwgZWFzaW5nLCBjYWxsYmFjayApO1xuXG5cdHJldHVybiB0aGlzLnF1ZXVlKCBmdW5jdGlvbigpIHtcblx0XHR2YXIgYW5pbWF0ZWQgPSAkKCB0aGlzICksXG5cdFx0XHRiYXNlQ2xhc3MgPSBhbmltYXRlZC5hdHRyKCBcImNsYXNzXCIgKSB8fCBcIlwiLFxuXHRcdFx0YXBwbHlDbGFzc0NoYW5nZSxcblx0XHRcdGFsbEFuaW1hdGlvbnMgPSBvLmNoaWxkcmVuID8gYW5pbWF0ZWQuZmluZCggXCIqXCIgKS5hZGRCYWNrKCkgOiBhbmltYXRlZDtcblxuXHRcdC8vIE1hcCB0aGUgYW5pbWF0ZWQgb2JqZWN0cyB0byBzdG9yZSB0aGUgb3JpZ2luYWwgc3R5bGVzLlxuXHRcdGFsbEFuaW1hdGlvbnMgPSBhbGxBbmltYXRpb25zLm1hcCggZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgZWwgPSAkKCB0aGlzICk7XG5cdFx0XHRyZXR1cm4ge1xuXHRcdFx0XHRlbDogZWwsXG5cdFx0XHRcdHN0YXJ0OiBnZXRFbGVtZW50U3R5bGVzKCB0aGlzIClcblx0XHRcdH07XG5cdFx0fSApO1xuXG5cdFx0Ly8gQXBwbHkgY2xhc3MgY2hhbmdlXG5cdFx0YXBwbHlDbGFzc0NoYW5nZSA9IGZ1bmN0aW9uKCkge1xuXHRcdFx0JC5lYWNoKCBjbGFzc0FuaW1hdGlvbkFjdGlvbnMsIGZ1bmN0aW9uKCBpLCBhY3Rpb24gKSB7XG5cdFx0XHRcdGlmICggdmFsdWVbIGFjdGlvbiBdICkge1xuXHRcdFx0XHRcdGFuaW1hdGVkWyBhY3Rpb24gKyBcIkNsYXNzXCIgXSggdmFsdWVbIGFjdGlvbiBdICk7XG5cdFx0XHRcdH1cblx0XHRcdH0gKTtcblx0XHR9O1xuXHRcdGFwcGx5Q2xhc3NDaGFuZ2UoKTtcblxuXHRcdC8vIE1hcCBhbGwgYW5pbWF0ZWQgb2JqZWN0cyBhZ2FpbiAtIGNhbGN1bGF0ZSBuZXcgc3R5bGVzIGFuZCBkaWZmXG5cdFx0YWxsQW5pbWF0aW9ucyA9IGFsbEFuaW1hdGlvbnMubWFwKCBmdW5jdGlvbigpIHtcblx0XHRcdHRoaXMuZW5kID0gZ2V0RWxlbWVudFN0eWxlcyggdGhpcy5lbFsgMCBdICk7XG5cdFx0XHR0aGlzLmRpZmYgPSBzdHlsZURpZmZlcmVuY2UoIHRoaXMuc3RhcnQsIHRoaXMuZW5kICk7XG5cdFx0XHRyZXR1cm4gdGhpcztcblx0XHR9ICk7XG5cblx0XHQvLyBBcHBseSBvcmlnaW5hbCBjbGFzc1xuXHRcdGFuaW1hdGVkLmF0dHIoIFwiY2xhc3NcIiwgYmFzZUNsYXNzICk7XG5cblx0XHQvLyBNYXAgYWxsIGFuaW1hdGVkIG9iamVjdHMgYWdhaW4gLSB0aGlzIHRpbWUgY29sbGVjdGluZyBhIHByb21pc2Vcblx0XHRhbGxBbmltYXRpb25zID0gYWxsQW5pbWF0aW9ucy5tYXAoIGZ1bmN0aW9uKCkge1xuXHRcdFx0dmFyIHN0eWxlSW5mbyA9IHRoaXMsXG5cdFx0XHRcdGRmZCA9ICQuRGVmZXJyZWQoKSxcblx0XHRcdFx0b3B0cyA9ICQuZXh0ZW5kKCB7fSwgbywge1xuXHRcdFx0XHRcdHF1ZXVlOiBmYWxzZSxcblx0XHRcdFx0XHRjb21wbGV0ZTogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0XHRkZmQucmVzb2x2ZSggc3R5bGVJbmZvICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9ICk7XG5cblx0XHRcdHRoaXMuZWwuYW5pbWF0ZSggdGhpcy5kaWZmLCBvcHRzICk7XG5cdFx0XHRyZXR1cm4gZGZkLnByb21pc2UoKTtcblx0XHR9ICk7XG5cblx0XHQvLyBPbmNlIGFsbCBhbmltYXRpb25zIGhhdmUgY29tcGxldGVkOlxuXHRcdCQud2hlbi5hcHBseSggJCwgYWxsQW5pbWF0aW9ucy5nZXQoKSApLmRvbmUoIGZ1bmN0aW9uKCkge1xuXG5cdFx0XHQvLyBTZXQgdGhlIGZpbmFsIGNsYXNzXG5cdFx0XHRhcHBseUNsYXNzQ2hhbmdlKCk7XG5cblx0XHRcdC8vIEZvciBlYWNoIGFuaW1hdGVkIGVsZW1lbnQsXG5cdFx0XHQvLyBjbGVhciBhbGwgY3NzIHByb3BlcnRpZXMgdGhhdCB3ZXJlIGFuaW1hdGVkXG5cdFx0XHQkLmVhY2goIGFyZ3VtZW50cywgZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHZhciBlbCA9IHRoaXMuZWw7XG5cdFx0XHRcdCQuZWFjaCggdGhpcy5kaWZmLCBmdW5jdGlvbigga2V5ICkge1xuXHRcdFx0XHRcdGVsLmNzcygga2V5LCBcIlwiICk7XG5cdFx0XHRcdH0gKTtcblx0XHRcdH0gKTtcblxuXHRcdFx0Ly8gVGhpcyBpcyBndWFybnRlZWQgdG8gYmUgdGhlcmUgaWYgeW91IHVzZSBqUXVlcnkuc3BlZWQoKVxuXHRcdFx0Ly8gaXQgYWxzbyBoYW5kbGVzIGRlcXVldWluZyB0aGUgbmV4dCBhbmltLi4uXG5cdFx0XHRvLmNvbXBsZXRlLmNhbGwoIGFuaW1hdGVkWyAwIF0gKTtcblx0XHR9ICk7XG5cdH0gKTtcbn07XG5cbiQuZm4uZXh0ZW5kKCB7XG5cdGFkZENsYXNzOiAoIGZ1bmN0aW9uKCBvcmlnICkge1xuXHRcdHJldHVybiBmdW5jdGlvbiggY2xhc3NOYW1lcywgc3BlZWQsIGVhc2luZywgY2FsbGJhY2sgKSB7XG5cdFx0XHRyZXR1cm4gc3BlZWQgP1xuXHRcdFx0XHQkLmVmZmVjdHMuYW5pbWF0ZUNsYXNzLmNhbGwoIHRoaXMsXG5cdFx0XHRcdFx0eyBhZGQ6IGNsYXNzTmFtZXMgfSwgc3BlZWQsIGVhc2luZywgY2FsbGJhY2sgKSA6XG5cdFx0XHRcdG9yaWcuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdH07XG5cdH0gKSggJC5mbi5hZGRDbGFzcyApLFxuXG5cdHJlbW92ZUNsYXNzOiAoIGZ1bmN0aW9uKCBvcmlnICkge1xuXHRcdHJldHVybiBmdW5jdGlvbiggY2xhc3NOYW1lcywgc3BlZWQsIGVhc2luZywgY2FsbGJhY2sgKSB7XG5cdFx0XHRyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA+IDEgP1xuXHRcdFx0XHQkLmVmZmVjdHMuYW5pbWF0ZUNsYXNzLmNhbGwoIHRoaXMsXG5cdFx0XHRcdFx0eyByZW1vdmU6IGNsYXNzTmFtZXMgfSwgc3BlZWQsIGVhc2luZywgY2FsbGJhY2sgKSA6XG5cdFx0XHRcdG9yaWcuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdH07XG5cdH0gKSggJC5mbi5yZW1vdmVDbGFzcyApLFxuXG5cdHRvZ2dsZUNsYXNzOiAoIGZ1bmN0aW9uKCBvcmlnICkge1xuXHRcdHJldHVybiBmdW5jdGlvbiggY2xhc3NOYW1lcywgZm9yY2UsIHNwZWVkLCBlYXNpbmcsIGNhbGxiYWNrICkge1xuXHRcdFx0aWYgKCB0eXBlb2YgZm9yY2UgPT09IFwiYm9vbGVhblwiIHx8IGZvcmNlID09PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdGlmICggIXNwZWVkICkge1xuXG5cdFx0XHRcdFx0Ly8gV2l0aG91dCBzcGVlZCBwYXJhbWV0ZXJcblx0XHRcdFx0XHRyZXR1cm4gb3JpZy5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0cmV0dXJuICQuZWZmZWN0cy5hbmltYXRlQ2xhc3MuY2FsbCggdGhpcyxcblx0XHRcdFx0XHRcdCggZm9yY2UgPyB7IGFkZDogY2xhc3NOYW1lcyB9IDogeyByZW1vdmU6IGNsYXNzTmFtZXMgfSApLFxuXHRcdFx0XHRcdFx0c3BlZWQsIGVhc2luZywgY2FsbGJhY2sgKTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBXaXRob3V0IGZvcmNlIHBhcmFtZXRlclxuXHRcdFx0XHRyZXR1cm4gJC5lZmZlY3RzLmFuaW1hdGVDbGFzcy5jYWxsKCB0aGlzLFxuXHRcdFx0XHRcdHsgdG9nZ2xlOiBjbGFzc05hbWVzIH0sIGZvcmNlLCBzcGVlZCwgZWFzaW5nICk7XG5cdFx0XHR9XG5cdFx0fTtcblx0fSApKCAkLmZuLnRvZ2dsZUNsYXNzICksXG5cblx0c3dpdGNoQ2xhc3M6IGZ1bmN0aW9uKCByZW1vdmUsIGFkZCwgc3BlZWQsIGVhc2luZywgY2FsbGJhY2sgKSB7XG5cdFx0cmV0dXJuICQuZWZmZWN0cy5hbmltYXRlQ2xhc3MuY2FsbCggdGhpcywge1xuXHRcdFx0YWRkOiBhZGQsXG5cdFx0XHRyZW1vdmU6IHJlbW92ZVxuXHRcdH0sIHNwZWVkLCBlYXNpbmcsIGNhbGxiYWNrICk7XG5cdH1cbn0gKTtcblxufSApKCk7XG5cbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiogRUZGRUNUUyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuKCBmdW5jdGlvbigpIHtcblxuaWYgKCAkLmV4cHIgJiYgJC5leHByLmZpbHRlcnMgJiYgJC5leHByLmZpbHRlcnMuYW5pbWF0ZWQgKSB7XG5cdCQuZXhwci5maWx0ZXJzLmFuaW1hdGVkID0gKCBmdW5jdGlvbiggb3JpZyApIHtcblx0XHRyZXR1cm4gZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHRyZXR1cm4gISEkKCBlbGVtICkuZGF0YSggZGF0YVNwYWNlQW5pbWF0ZWQgKSB8fCBvcmlnKCBlbGVtICk7XG5cdFx0fTtcblx0fSApKCAkLmV4cHIuZmlsdGVycy5hbmltYXRlZCApO1xufVxuXG5pZiAoICQudWlCYWNrQ29tcGF0ICE9PSBmYWxzZSApIHtcblx0JC5leHRlbmQoICQuZWZmZWN0cywge1xuXG5cdFx0Ly8gU2F2ZXMgYSBzZXQgb2YgcHJvcGVydGllcyBpbiBhIGRhdGEgc3RvcmFnZVxuXHRcdHNhdmU6IGZ1bmN0aW9uKCBlbGVtZW50LCBzZXQgKSB7XG5cdFx0XHR2YXIgaSA9IDAsIGxlbmd0aCA9IHNldC5sZW5ndGg7XG5cdFx0XHRmb3IgKCA7IGkgPCBsZW5ndGg7IGkrKyApIHtcblx0XHRcdFx0aWYgKCBzZXRbIGkgXSAhPT0gbnVsbCApIHtcblx0XHRcdFx0XHRlbGVtZW50LmRhdGEoIGRhdGFTcGFjZSArIHNldFsgaSBdLCBlbGVtZW50WyAwIF0uc3R5bGVbIHNldFsgaSBdIF0gKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0sXG5cblx0XHQvLyBSZXN0b3JlcyBhIHNldCBvZiBwcmV2aW91c2x5IHNhdmVkIHByb3BlcnRpZXMgZnJvbSBhIGRhdGEgc3RvcmFnZVxuXHRcdHJlc3RvcmU6IGZ1bmN0aW9uKCBlbGVtZW50LCBzZXQgKSB7XG5cdFx0XHR2YXIgdmFsLCBpID0gMCwgbGVuZ3RoID0gc2V0Lmxlbmd0aDtcblx0XHRcdGZvciAoIDsgaSA8IGxlbmd0aDsgaSsrICkge1xuXHRcdFx0XHRpZiAoIHNldFsgaSBdICE9PSBudWxsICkge1xuXHRcdFx0XHRcdHZhbCA9IGVsZW1lbnQuZGF0YSggZGF0YVNwYWNlICsgc2V0WyBpIF0gKTtcblx0XHRcdFx0XHRlbGVtZW50LmNzcyggc2V0WyBpIF0sIHZhbCApO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSxcblxuXHRcdHNldE1vZGU6IGZ1bmN0aW9uKCBlbCwgbW9kZSApIHtcblx0XHRcdGlmICggbW9kZSA9PT0gXCJ0b2dnbGVcIiApIHtcblx0XHRcdFx0bW9kZSA9IGVsLmlzKCBcIjpoaWRkZW5cIiApID8gXCJzaG93XCIgOiBcImhpZGVcIjtcblx0XHRcdH1cblx0XHRcdHJldHVybiBtb2RlO1xuXHRcdH0sXG5cblx0XHQvLyBXcmFwcyB0aGUgZWxlbWVudCBhcm91bmQgYSB3cmFwcGVyIHRoYXQgY29waWVzIHBvc2l0aW9uIHByb3BlcnRpZXNcblx0XHRjcmVhdGVXcmFwcGVyOiBmdW5jdGlvbiggZWxlbWVudCApIHtcblxuXHRcdFx0Ly8gSWYgdGhlIGVsZW1lbnQgaXMgYWxyZWFkeSB3cmFwcGVkLCByZXR1cm4gaXRcblx0XHRcdGlmICggZWxlbWVudC5wYXJlbnQoKS5pcyggXCIudWktZWZmZWN0cy13cmFwcGVyXCIgKSApIHtcblx0XHRcdFx0cmV0dXJuIGVsZW1lbnQucGFyZW50KCk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIFdyYXAgdGhlIGVsZW1lbnRcblx0XHRcdHZhciBwcm9wcyA9IHtcblx0XHRcdFx0XHR3aWR0aDogZWxlbWVudC5vdXRlcldpZHRoKCB0cnVlICksXG5cdFx0XHRcdFx0aGVpZ2h0OiBlbGVtZW50Lm91dGVySGVpZ2h0KCB0cnVlICksXG5cdFx0XHRcdFx0XCJmbG9hdFwiOiBlbGVtZW50LmNzcyggXCJmbG9hdFwiIClcblx0XHRcdFx0fSxcblx0XHRcdFx0d3JhcHBlciA9ICQoIFwiPGRpdj48L2Rpdj5cIiApXG5cdFx0XHRcdFx0LmFkZENsYXNzKCBcInVpLWVmZmVjdHMtd3JhcHBlclwiIClcblx0XHRcdFx0XHQuY3NzKCB7XG5cdFx0XHRcdFx0XHRmb250U2l6ZTogXCIxMDAlXCIsXG5cdFx0XHRcdFx0XHRiYWNrZ3JvdW5kOiBcInRyYW5zcGFyZW50XCIsXG5cdFx0XHRcdFx0XHRib3JkZXI6IFwibm9uZVwiLFxuXHRcdFx0XHRcdFx0bWFyZ2luOiAwLFxuXHRcdFx0XHRcdFx0cGFkZGluZzogMFxuXHRcdFx0XHRcdH0gKSxcblxuXHRcdFx0XHQvLyBTdG9yZSB0aGUgc2l6ZSBpbiBjYXNlIHdpZHRoL2hlaWdodCBhcmUgZGVmaW5lZCBpbiAlIC0gRml4ZXMgIzUyNDVcblx0XHRcdFx0c2l6ZSA9IHtcblx0XHRcdFx0XHR3aWR0aDogZWxlbWVudC53aWR0aCgpLFxuXHRcdFx0XHRcdGhlaWdodDogZWxlbWVudC5oZWlnaHQoKVxuXHRcdFx0XHR9LFxuXHRcdFx0XHRhY3RpdmUgPSBkb2N1bWVudC5hY3RpdmVFbGVtZW50O1xuXG5cdFx0XHQvLyBTdXBwb3J0OiBGaXJlZm94XG5cdFx0XHQvLyBGaXJlZm94IGluY29ycmVjdGx5IGV4cG9zZXMgYW5vbnltb3VzIGNvbnRlbnRcblx0XHRcdC8vIGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTU2MTY2NFxuXHRcdFx0dHJ5IHtcblx0XHRcdFx0YWN0aXZlLmlkO1xuXHRcdFx0fSBjYXRjaCAoIGUgKSB7XG5cdFx0XHRcdGFjdGl2ZSA9IGRvY3VtZW50LmJvZHk7XG5cdFx0XHR9XG5cblx0XHRcdGVsZW1lbnQud3JhcCggd3JhcHBlciApO1xuXG5cdFx0XHQvLyBGaXhlcyAjNzU5NSAtIEVsZW1lbnRzIGxvc2UgZm9jdXMgd2hlbiB3cmFwcGVkLlxuXHRcdFx0aWYgKCBlbGVtZW50WyAwIF0gPT09IGFjdGl2ZSB8fCAkLmNvbnRhaW5zKCBlbGVtZW50WyAwIF0sIGFjdGl2ZSApICkge1xuXHRcdFx0XHQkKCBhY3RpdmUgKS50cmlnZ2VyKCBcImZvY3VzXCIgKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gSG90Zml4IGZvciBqUXVlcnkgMS40IHNpbmNlIHNvbWUgY2hhbmdlIGluIHdyYXAoKSBzZWVtcyB0byBhY3R1YWxseVxuXHRcdFx0Ly8gbG9zZSB0aGUgcmVmZXJlbmNlIHRvIHRoZSB3cmFwcGVkIGVsZW1lbnRcblx0XHRcdHdyYXBwZXIgPSBlbGVtZW50LnBhcmVudCgpO1xuXG5cdFx0XHQvLyBUcmFuc2ZlciBwb3NpdGlvbmluZyBwcm9wZXJ0aWVzIHRvIHRoZSB3cmFwcGVyXG5cdFx0XHRpZiAoIGVsZW1lbnQuY3NzKCBcInBvc2l0aW9uXCIgKSA9PT0gXCJzdGF0aWNcIiApIHtcblx0XHRcdFx0d3JhcHBlci5jc3MoIHsgcG9zaXRpb246IFwicmVsYXRpdmVcIiB9ICk7XG5cdFx0XHRcdGVsZW1lbnQuY3NzKCB7IHBvc2l0aW9uOiBcInJlbGF0aXZlXCIgfSApO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0JC5leHRlbmQoIHByb3BzLCB7XG5cdFx0XHRcdFx0cG9zaXRpb246IGVsZW1lbnQuY3NzKCBcInBvc2l0aW9uXCIgKSxcblx0XHRcdFx0XHR6SW5kZXg6IGVsZW1lbnQuY3NzKCBcInotaW5kZXhcIiApXG5cdFx0XHRcdH0gKTtcblx0XHRcdFx0JC5lYWNoKCBbIFwidG9wXCIsIFwibGVmdFwiLCBcImJvdHRvbVwiLCBcInJpZ2h0XCIgXSwgZnVuY3Rpb24oIGksIHBvcyApIHtcblx0XHRcdFx0XHRwcm9wc1sgcG9zIF0gPSBlbGVtZW50LmNzcyggcG9zICk7XG5cdFx0XHRcdFx0aWYgKCBpc05hTiggcGFyc2VJbnQoIHByb3BzWyBwb3MgXSwgMTAgKSApICkge1xuXHRcdFx0XHRcdFx0cHJvcHNbIHBvcyBdID0gXCJhdXRvXCI7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9ICk7XG5cdFx0XHRcdGVsZW1lbnQuY3NzKCB7XG5cdFx0XHRcdFx0cG9zaXRpb246IFwicmVsYXRpdmVcIixcblx0XHRcdFx0XHR0b3A6IDAsXG5cdFx0XHRcdFx0bGVmdDogMCxcblx0XHRcdFx0XHRyaWdodDogXCJhdXRvXCIsXG5cdFx0XHRcdFx0Ym90dG9tOiBcImF1dG9cIlxuXHRcdFx0XHR9ICk7XG5cdFx0XHR9XG5cdFx0XHRlbGVtZW50LmNzcyggc2l6ZSApO1xuXG5cdFx0XHRyZXR1cm4gd3JhcHBlci5jc3MoIHByb3BzICkuc2hvdygpO1xuXHRcdH0sXG5cblx0XHRyZW1vdmVXcmFwcGVyOiBmdW5jdGlvbiggZWxlbWVudCApIHtcblx0XHRcdHZhciBhY3RpdmUgPSBkb2N1bWVudC5hY3RpdmVFbGVtZW50O1xuXG5cdFx0XHRpZiAoIGVsZW1lbnQucGFyZW50KCkuaXMoIFwiLnVpLWVmZmVjdHMtd3JhcHBlclwiICkgKSB7XG5cdFx0XHRcdGVsZW1lbnQucGFyZW50KCkucmVwbGFjZVdpdGgoIGVsZW1lbnQgKTtcblxuXHRcdFx0XHQvLyBGaXhlcyAjNzU5NSAtIEVsZW1lbnRzIGxvc2UgZm9jdXMgd2hlbiB3cmFwcGVkLlxuXHRcdFx0XHRpZiAoIGVsZW1lbnRbIDAgXSA9PT0gYWN0aXZlIHx8ICQuY29udGFpbnMoIGVsZW1lbnRbIDAgXSwgYWN0aXZlICkgKSB7XG5cdFx0XHRcdFx0JCggYWN0aXZlICkudHJpZ2dlciggXCJmb2N1c1wiICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGVsZW1lbnQ7XG5cdFx0fVxuXHR9ICk7XG59XG5cbiQuZXh0ZW5kKCAkLmVmZmVjdHMsIHtcblx0dmVyc2lvbjogXCIxLjEyLjFcIixcblxuXHRkZWZpbmU6IGZ1bmN0aW9uKCBuYW1lLCBtb2RlLCBlZmZlY3QgKSB7XG5cdFx0aWYgKCAhZWZmZWN0ICkge1xuXHRcdFx0ZWZmZWN0ID0gbW9kZTtcblx0XHRcdG1vZGUgPSBcImVmZmVjdFwiO1xuXHRcdH1cblxuXHRcdCQuZWZmZWN0cy5lZmZlY3RbIG5hbWUgXSA9IGVmZmVjdDtcblx0XHQkLmVmZmVjdHMuZWZmZWN0WyBuYW1lIF0ubW9kZSA9IG1vZGU7XG5cblx0XHRyZXR1cm4gZWZmZWN0O1xuXHR9LFxuXG5cdHNjYWxlZERpbWVuc2lvbnM6IGZ1bmN0aW9uKCBlbGVtZW50LCBwZXJjZW50LCBkaXJlY3Rpb24gKSB7XG5cdFx0aWYgKCBwZXJjZW50ID09PSAwICkge1xuXHRcdFx0cmV0dXJuIHtcblx0XHRcdFx0aGVpZ2h0OiAwLFxuXHRcdFx0XHR3aWR0aDogMCxcblx0XHRcdFx0b3V0ZXJIZWlnaHQ6IDAsXG5cdFx0XHRcdG91dGVyV2lkdGg6IDBcblx0XHRcdH07XG5cdFx0fVxuXG5cdFx0dmFyIHggPSBkaXJlY3Rpb24gIT09IFwiaG9yaXpvbnRhbFwiID8gKCAoIHBlcmNlbnQgfHwgMTAwICkgLyAxMDAgKSA6IDEsXG5cdFx0XHR5ID0gZGlyZWN0aW9uICE9PSBcInZlcnRpY2FsXCIgPyAoICggcGVyY2VudCB8fCAxMDAgKSAvIDEwMCApIDogMTtcblxuXHRcdHJldHVybiB7XG5cdFx0XHRoZWlnaHQ6IGVsZW1lbnQuaGVpZ2h0KCkgKiB5LFxuXHRcdFx0d2lkdGg6IGVsZW1lbnQud2lkdGgoKSAqIHgsXG5cdFx0XHRvdXRlckhlaWdodDogZWxlbWVudC5vdXRlckhlaWdodCgpICogeSxcblx0XHRcdG91dGVyV2lkdGg6IGVsZW1lbnQub3V0ZXJXaWR0aCgpICogeFxuXHRcdH07XG5cblx0fSxcblxuXHRjbGlwVG9Cb3g6IGZ1bmN0aW9uKCBhbmltYXRpb24gKSB7XG5cdFx0cmV0dXJuIHtcblx0XHRcdHdpZHRoOiBhbmltYXRpb24uY2xpcC5yaWdodCAtIGFuaW1hdGlvbi5jbGlwLmxlZnQsXG5cdFx0XHRoZWlnaHQ6IGFuaW1hdGlvbi5jbGlwLmJvdHRvbSAtIGFuaW1hdGlvbi5jbGlwLnRvcCxcblx0XHRcdGxlZnQ6IGFuaW1hdGlvbi5jbGlwLmxlZnQsXG5cdFx0XHR0b3A6IGFuaW1hdGlvbi5jbGlwLnRvcFxuXHRcdH07XG5cdH0sXG5cblx0Ly8gSW5qZWN0cyByZWNlbnRseSBxdWV1ZWQgZnVuY3Rpb25zIHRvIGJlIGZpcnN0IGluIGxpbmUgKGFmdGVyIFwiaW5wcm9ncmVzc1wiKVxuXHR1bnNoaWZ0OiBmdW5jdGlvbiggZWxlbWVudCwgcXVldWVMZW5ndGgsIGNvdW50ICkge1xuXHRcdHZhciBxdWV1ZSA9IGVsZW1lbnQucXVldWUoKTtcblxuXHRcdGlmICggcXVldWVMZW5ndGggPiAxICkge1xuXHRcdFx0cXVldWUuc3BsaWNlLmFwcGx5KCBxdWV1ZSxcblx0XHRcdFx0WyAxLCAwIF0uY29uY2F0KCBxdWV1ZS5zcGxpY2UoIHF1ZXVlTGVuZ3RoLCBjb3VudCApICkgKTtcblx0XHR9XG5cdFx0ZWxlbWVudC5kZXF1ZXVlKCk7XG5cdH0sXG5cblx0c2F2ZVN0eWxlOiBmdW5jdGlvbiggZWxlbWVudCApIHtcblx0XHRlbGVtZW50LmRhdGEoIGRhdGFTcGFjZVN0eWxlLCBlbGVtZW50WyAwIF0uc3R5bGUuY3NzVGV4dCApO1xuXHR9LFxuXG5cdHJlc3RvcmVTdHlsZTogZnVuY3Rpb24oIGVsZW1lbnQgKSB7XG5cdFx0ZWxlbWVudFsgMCBdLnN0eWxlLmNzc1RleHQgPSBlbGVtZW50LmRhdGEoIGRhdGFTcGFjZVN0eWxlICkgfHwgXCJcIjtcblx0XHRlbGVtZW50LnJlbW92ZURhdGEoIGRhdGFTcGFjZVN0eWxlICk7XG5cdH0sXG5cblx0bW9kZTogZnVuY3Rpb24oIGVsZW1lbnQsIG1vZGUgKSB7XG5cdFx0dmFyIGhpZGRlbiA9IGVsZW1lbnQuaXMoIFwiOmhpZGRlblwiICk7XG5cblx0XHRpZiAoIG1vZGUgPT09IFwidG9nZ2xlXCIgKSB7XG5cdFx0XHRtb2RlID0gaGlkZGVuID8gXCJzaG93XCIgOiBcImhpZGVcIjtcblx0XHR9XG5cdFx0aWYgKCBoaWRkZW4gPyBtb2RlID09PSBcImhpZGVcIiA6IG1vZGUgPT09IFwic2hvd1wiICkge1xuXHRcdFx0bW9kZSA9IFwibm9uZVwiO1xuXHRcdH1cblx0XHRyZXR1cm4gbW9kZTtcblx0fSxcblxuXHQvLyBUcmFuc2xhdGVzIGEgW3RvcCxsZWZ0XSBhcnJheSBpbnRvIGEgYmFzZWxpbmUgdmFsdWVcblx0Z2V0QmFzZWxpbmU6IGZ1bmN0aW9uKCBvcmlnaW4sIG9yaWdpbmFsICkge1xuXHRcdHZhciB5LCB4O1xuXG5cdFx0c3dpdGNoICggb3JpZ2luWyAwIF0gKSB7XG5cdFx0Y2FzZSBcInRvcFwiOlxuXHRcdFx0eSA9IDA7XG5cdFx0XHRicmVhaztcblx0XHRjYXNlIFwibWlkZGxlXCI6XG5cdFx0XHR5ID0gMC41O1xuXHRcdFx0YnJlYWs7XG5cdFx0Y2FzZSBcImJvdHRvbVwiOlxuXHRcdFx0eSA9IDE7XG5cdFx0XHRicmVhaztcblx0XHRkZWZhdWx0OlxuXHRcdFx0eSA9IG9yaWdpblsgMCBdIC8gb3JpZ2luYWwuaGVpZ2h0O1xuXHRcdH1cblxuXHRcdHN3aXRjaCAoIG9yaWdpblsgMSBdICkge1xuXHRcdGNhc2UgXCJsZWZ0XCI6XG5cdFx0XHR4ID0gMDtcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgXCJjZW50ZXJcIjpcblx0XHRcdHggPSAwLjU7XG5cdFx0XHRicmVhaztcblx0XHRjYXNlIFwicmlnaHRcIjpcblx0XHRcdHggPSAxO1xuXHRcdFx0YnJlYWs7XG5cdFx0ZGVmYXVsdDpcblx0XHRcdHggPSBvcmlnaW5bIDEgXSAvIG9yaWdpbmFsLndpZHRoO1xuXHRcdH1cblxuXHRcdHJldHVybiB7XG5cdFx0XHR4OiB4LFxuXHRcdFx0eTogeVxuXHRcdH07XG5cdH0sXG5cblx0Ly8gQ3JlYXRlcyBhIHBsYWNlaG9sZGVyIGVsZW1lbnQgc28gdGhhdCB0aGUgb3JpZ2luYWwgZWxlbWVudCBjYW4gYmUgbWFkZSBhYnNvbHV0ZVxuXHRjcmVhdGVQbGFjZWhvbGRlcjogZnVuY3Rpb24oIGVsZW1lbnQgKSB7XG5cdFx0dmFyIHBsYWNlaG9sZGVyLFxuXHRcdFx0Y3NzUG9zaXRpb24gPSBlbGVtZW50LmNzcyggXCJwb3NpdGlvblwiICksXG5cdFx0XHRwb3NpdGlvbiA9IGVsZW1lbnQucG9zaXRpb24oKTtcblxuXHRcdC8vIExvY2sgaW4gbWFyZ2lucyBmaXJzdCB0byBhY2NvdW50IGZvciBmb3JtIGVsZW1lbnRzLCB3aGljaFxuXHRcdC8vIHdpbGwgY2hhbmdlIG1hcmdpbiBpZiB5b3UgZXhwbGljaXRseSBzZXQgaGVpZ2h0XG5cdFx0Ly8gc2VlOiBodHRwOi8vanNmaWRkbGUubmV0L0paU010LzMvIGh0dHBzOi8vYnVncy53ZWJraXQub3JnL3Nob3dfYnVnLmNnaT9pZD0xMDczODBcblx0XHQvLyBTdXBwb3J0OiBTYWZhcmlcblx0XHRlbGVtZW50LmNzcygge1xuXHRcdFx0bWFyZ2luVG9wOiBlbGVtZW50LmNzcyggXCJtYXJnaW5Ub3BcIiApLFxuXHRcdFx0bWFyZ2luQm90dG9tOiBlbGVtZW50LmNzcyggXCJtYXJnaW5Cb3R0b21cIiApLFxuXHRcdFx0bWFyZ2luTGVmdDogZWxlbWVudC5jc3MoIFwibWFyZ2luTGVmdFwiICksXG5cdFx0XHRtYXJnaW5SaWdodDogZWxlbWVudC5jc3MoIFwibWFyZ2luUmlnaHRcIiApXG5cdFx0fSApXG5cdFx0Lm91dGVyV2lkdGgoIGVsZW1lbnQub3V0ZXJXaWR0aCgpIClcblx0XHQub3V0ZXJIZWlnaHQoIGVsZW1lbnQub3V0ZXJIZWlnaHQoKSApO1xuXG5cdFx0aWYgKCAvXihzdGF0aWN8cmVsYXRpdmUpLy50ZXN0KCBjc3NQb3NpdGlvbiApICkge1xuXHRcdFx0Y3NzUG9zaXRpb24gPSBcImFic29sdXRlXCI7XG5cblx0XHRcdHBsYWNlaG9sZGVyID0gJCggXCI8XCIgKyBlbGVtZW50WyAwIF0ubm9kZU5hbWUgKyBcIj5cIiApLmluc2VydEFmdGVyKCBlbGVtZW50ICkuY3NzKCB7XG5cblx0XHRcdFx0Ly8gQ29udmVydCBpbmxpbmUgdG8gaW5saW5lIGJsb2NrIHRvIGFjY291bnQgZm9yIGlubGluZSBlbGVtZW50c1xuXHRcdFx0XHQvLyB0aGF0IHR1cm4gdG8gaW5saW5lIGJsb2NrIGJhc2VkIG9uIGNvbnRlbnQgKGxpa2UgaW1nKVxuXHRcdFx0XHRkaXNwbGF5OiAvXihpbmxpbmV8cnVieSkvLnRlc3QoIGVsZW1lbnQuY3NzKCBcImRpc3BsYXlcIiApICkgP1xuXHRcdFx0XHRcdFwiaW5saW5lLWJsb2NrXCIgOlxuXHRcdFx0XHRcdFwiYmxvY2tcIixcblx0XHRcdFx0dmlzaWJpbGl0eTogXCJoaWRkZW5cIixcblxuXHRcdFx0XHQvLyBNYXJnaW5zIG5lZWQgdG8gYmUgc2V0IHRvIGFjY291bnQgZm9yIG1hcmdpbiBjb2xsYXBzZVxuXHRcdFx0XHRtYXJnaW5Ub3A6IGVsZW1lbnQuY3NzKCBcIm1hcmdpblRvcFwiICksXG5cdFx0XHRcdG1hcmdpbkJvdHRvbTogZWxlbWVudC5jc3MoIFwibWFyZ2luQm90dG9tXCIgKSxcblx0XHRcdFx0bWFyZ2luTGVmdDogZWxlbWVudC5jc3MoIFwibWFyZ2luTGVmdFwiICksXG5cdFx0XHRcdG1hcmdpblJpZ2h0OiBlbGVtZW50LmNzcyggXCJtYXJnaW5SaWdodFwiICksXG5cdFx0XHRcdFwiZmxvYXRcIjogZWxlbWVudC5jc3MoIFwiZmxvYXRcIiApXG5cdFx0XHR9IClcblx0XHRcdC5vdXRlcldpZHRoKCBlbGVtZW50Lm91dGVyV2lkdGgoKSApXG5cdFx0XHQub3V0ZXJIZWlnaHQoIGVsZW1lbnQub3V0ZXJIZWlnaHQoKSApXG5cdFx0XHQuYWRkQ2xhc3MoIFwidWktZWZmZWN0cy1wbGFjZWhvbGRlclwiICk7XG5cblx0XHRcdGVsZW1lbnQuZGF0YSggZGF0YVNwYWNlICsgXCJwbGFjZWhvbGRlclwiLCBwbGFjZWhvbGRlciApO1xuXHRcdH1cblxuXHRcdGVsZW1lbnQuY3NzKCB7XG5cdFx0XHRwb3NpdGlvbjogY3NzUG9zaXRpb24sXG5cdFx0XHRsZWZ0OiBwb3NpdGlvbi5sZWZ0LFxuXHRcdFx0dG9wOiBwb3NpdGlvbi50b3Bcblx0XHR9ICk7XG5cblx0XHRyZXR1cm4gcGxhY2Vob2xkZXI7XG5cdH0sXG5cblx0cmVtb3ZlUGxhY2Vob2xkZXI6IGZ1bmN0aW9uKCBlbGVtZW50ICkge1xuXHRcdHZhciBkYXRhS2V5ID0gZGF0YVNwYWNlICsgXCJwbGFjZWhvbGRlclwiLFxuXHRcdFx0XHRwbGFjZWhvbGRlciA9IGVsZW1lbnQuZGF0YSggZGF0YUtleSApO1xuXG5cdFx0aWYgKCBwbGFjZWhvbGRlciApIHtcblx0XHRcdHBsYWNlaG9sZGVyLnJlbW92ZSgpO1xuXHRcdFx0ZWxlbWVudC5yZW1vdmVEYXRhKCBkYXRhS2V5ICk7XG5cdFx0fVxuXHR9LFxuXG5cdC8vIFJlbW92ZXMgYSBwbGFjZWhvbGRlciBpZiBpdCBleGlzdHMgYW5kIHJlc3RvcmVzXG5cdC8vIHByb3BlcnRpZXMgdGhhdCB3ZXJlIG1vZGlmaWVkIGR1cmluZyBwbGFjZWhvbGRlciBjcmVhdGlvblxuXHRjbGVhblVwOiBmdW5jdGlvbiggZWxlbWVudCApIHtcblx0XHQkLmVmZmVjdHMucmVzdG9yZVN0eWxlKCBlbGVtZW50ICk7XG5cdFx0JC5lZmZlY3RzLnJlbW92ZVBsYWNlaG9sZGVyKCBlbGVtZW50ICk7XG5cdH0sXG5cblx0c2V0VHJhbnNpdGlvbjogZnVuY3Rpb24oIGVsZW1lbnQsIGxpc3QsIGZhY3RvciwgdmFsdWUgKSB7XG5cdFx0dmFsdWUgPSB2YWx1ZSB8fCB7fTtcblx0XHQkLmVhY2goIGxpc3QsIGZ1bmN0aW9uKCBpLCB4ICkge1xuXHRcdFx0dmFyIHVuaXQgPSBlbGVtZW50LmNzc1VuaXQoIHggKTtcblx0XHRcdGlmICggdW5pdFsgMCBdID4gMCApIHtcblx0XHRcdFx0dmFsdWVbIHggXSA9IHVuaXRbIDAgXSAqIGZhY3RvciArIHVuaXRbIDEgXTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdFx0cmV0dXJuIHZhbHVlO1xuXHR9XG59ICk7XG5cbi8vIFJldHVybiBhbiBlZmZlY3Qgb3B0aW9ucyBvYmplY3QgZm9yIHRoZSBnaXZlbiBwYXJhbWV0ZXJzOlxuZnVuY3Rpb24gX25vcm1hbGl6ZUFyZ3VtZW50cyggZWZmZWN0LCBvcHRpb25zLCBzcGVlZCwgY2FsbGJhY2sgKSB7XG5cblx0Ly8gQWxsb3cgcGFzc2luZyBhbGwgb3B0aW9ucyBhcyB0aGUgZmlyc3QgcGFyYW1ldGVyXG5cdGlmICggJC5pc1BsYWluT2JqZWN0KCBlZmZlY3QgKSApIHtcblx0XHRvcHRpb25zID0gZWZmZWN0O1xuXHRcdGVmZmVjdCA9IGVmZmVjdC5lZmZlY3Q7XG5cdH1cblxuXHQvLyBDb252ZXJ0IHRvIGFuIG9iamVjdFxuXHRlZmZlY3QgPSB7IGVmZmVjdDogZWZmZWN0IH07XG5cblx0Ly8gQ2F0Y2ggKGVmZmVjdCwgbnVsbCwgLi4uKVxuXHRpZiAoIG9wdGlvbnMgPT0gbnVsbCApIHtcblx0XHRvcHRpb25zID0ge307XG5cdH1cblxuXHQvLyBDYXRjaCAoZWZmZWN0LCBjYWxsYmFjaylcblx0aWYgKCAkLmlzRnVuY3Rpb24oIG9wdGlvbnMgKSApIHtcblx0XHRjYWxsYmFjayA9IG9wdGlvbnM7XG5cdFx0c3BlZWQgPSBudWxsO1xuXHRcdG9wdGlvbnMgPSB7fTtcblx0fVxuXG5cdC8vIENhdGNoIChlZmZlY3QsIHNwZWVkLCA/KVxuXHRpZiAoIHR5cGVvZiBvcHRpb25zID09PSBcIm51bWJlclwiIHx8ICQuZnguc3BlZWRzWyBvcHRpb25zIF0gKSB7XG5cdFx0Y2FsbGJhY2sgPSBzcGVlZDtcblx0XHRzcGVlZCA9IG9wdGlvbnM7XG5cdFx0b3B0aW9ucyA9IHt9O1xuXHR9XG5cblx0Ly8gQ2F0Y2ggKGVmZmVjdCwgb3B0aW9ucywgY2FsbGJhY2spXG5cdGlmICggJC5pc0Z1bmN0aW9uKCBzcGVlZCApICkge1xuXHRcdGNhbGxiYWNrID0gc3BlZWQ7XG5cdFx0c3BlZWQgPSBudWxsO1xuXHR9XG5cblx0Ly8gQWRkIG9wdGlvbnMgdG8gZWZmZWN0XG5cdGlmICggb3B0aW9ucyApIHtcblx0XHQkLmV4dGVuZCggZWZmZWN0LCBvcHRpb25zICk7XG5cdH1cblxuXHRzcGVlZCA9IHNwZWVkIHx8IG9wdGlvbnMuZHVyYXRpb247XG5cdGVmZmVjdC5kdXJhdGlvbiA9ICQuZngub2ZmID8gMCA6XG5cdFx0dHlwZW9mIHNwZWVkID09PSBcIm51bWJlclwiID8gc3BlZWQgOlxuXHRcdHNwZWVkIGluICQuZnguc3BlZWRzID8gJC5meC5zcGVlZHNbIHNwZWVkIF0gOlxuXHRcdCQuZnguc3BlZWRzLl9kZWZhdWx0O1xuXG5cdGVmZmVjdC5jb21wbGV0ZSA9IGNhbGxiYWNrIHx8IG9wdGlvbnMuY29tcGxldGU7XG5cblx0cmV0dXJuIGVmZmVjdDtcbn1cblxuZnVuY3Rpb24gc3RhbmRhcmRBbmltYXRpb25PcHRpb24oIG9wdGlvbiApIHtcblxuXHQvLyBWYWxpZCBzdGFuZGFyZCBzcGVlZHMgKG5vdGhpbmcsIG51bWJlciwgbmFtZWQgc3BlZWQpXG5cdGlmICggIW9wdGlvbiB8fCB0eXBlb2Ygb3B0aW9uID09PSBcIm51bWJlclwiIHx8ICQuZnguc3BlZWRzWyBvcHRpb24gXSApIHtcblx0XHRyZXR1cm4gdHJ1ZTtcblx0fVxuXG5cdC8vIEludmFsaWQgc3RyaW5ncyAtIHRyZWF0IGFzIFwibm9ybWFsXCIgc3BlZWRcblx0aWYgKCB0eXBlb2Ygb3B0aW9uID09PSBcInN0cmluZ1wiICYmICEkLmVmZmVjdHMuZWZmZWN0WyBvcHRpb24gXSApIHtcblx0XHRyZXR1cm4gdHJ1ZTtcblx0fVxuXG5cdC8vIENvbXBsZXRlIGNhbGxiYWNrXG5cdGlmICggJC5pc0Z1bmN0aW9uKCBvcHRpb24gKSApIHtcblx0XHRyZXR1cm4gdHJ1ZTtcblx0fVxuXG5cdC8vIE9wdGlvbnMgaGFzaCAoYnV0IG5vdCBuYW1pbmcgYW4gZWZmZWN0KVxuXHRpZiAoIHR5cGVvZiBvcHRpb24gPT09IFwib2JqZWN0XCIgJiYgIW9wdGlvbi5lZmZlY3QgKSB7XG5cdFx0cmV0dXJuIHRydWU7XG5cdH1cblxuXHQvLyBEaWRuJ3QgbWF0Y2ggYW55IHN0YW5kYXJkIEFQSVxuXHRyZXR1cm4gZmFsc2U7XG59XG5cbiQuZm4uZXh0ZW5kKCB7XG5cdGVmZmVjdDogZnVuY3Rpb24oIC8qIGVmZmVjdCwgb3B0aW9ucywgc3BlZWQsIGNhbGxiYWNrICovICkge1xuXHRcdHZhciBhcmdzID0gX25vcm1hbGl6ZUFyZ3VtZW50cy5hcHBseSggdGhpcywgYXJndW1lbnRzICksXG5cdFx0XHRlZmZlY3RNZXRob2QgPSAkLmVmZmVjdHMuZWZmZWN0WyBhcmdzLmVmZmVjdCBdLFxuXHRcdFx0ZGVmYXVsdE1vZGUgPSBlZmZlY3RNZXRob2QubW9kZSxcblx0XHRcdHF1ZXVlID0gYXJncy5xdWV1ZSxcblx0XHRcdHF1ZXVlTmFtZSA9IHF1ZXVlIHx8IFwiZnhcIixcblx0XHRcdGNvbXBsZXRlID0gYXJncy5jb21wbGV0ZSxcblx0XHRcdG1vZGUgPSBhcmdzLm1vZGUsXG5cdFx0XHRtb2RlcyA9IFtdLFxuXHRcdFx0cHJlZmlsdGVyID0gZnVuY3Rpb24oIG5leHQgKSB7XG5cdFx0XHRcdHZhciBlbCA9ICQoIHRoaXMgKSxcblx0XHRcdFx0XHRub3JtYWxpemVkTW9kZSA9ICQuZWZmZWN0cy5tb2RlKCBlbCwgbW9kZSApIHx8IGRlZmF1bHRNb2RlO1xuXG5cdFx0XHRcdC8vIFNlbnRpbmVsIGZvciBkdWNrLXB1bmNoaW5nIHRoZSA6YW5pbWF0ZWQgcHN1ZWRvLXNlbGVjdG9yXG5cdFx0XHRcdGVsLmRhdGEoIGRhdGFTcGFjZUFuaW1hdGVkLCB0cnVlICk7XG5cblx0XHRcdFx0Ly8gU2F2ZSBlZmZlY3QgbW9kZSBmb3IgbGF0ZXIgdXNlLFxuXHRcdFx0XHQvLyB3ZSBjYW4ndCBqdXN0IGNhbGwgJC5lZmZlY3RzLm1vZGUgYWdhaW4gbGF0ZXIsXG5cdFx0XHRcdC8vIGFzIHRoZSAuc2hvdygpIGJlbG93IGRlc3Ryb3lzIHRoZSBpbml0aWFsIHN0YXRlXG5cdFx0XHRcdG1vZGVzLnB1c2goIG5vcm1hbGl6ZWRNb2RlICk7XG5cblx0XHRcdFx0Ly8gU2VlICQudWlCYWNrQ29tcGF0IGluc2lkZSBvZiBydW4oKSBmb3IgcmVtb3ZhbCBvZiBkZWZhdWx0TW9kZSBpbiAxLjEzXG5cdFx0XHRcdGlmICggZGVmYXVsdE1vZGUgJiYgKCBub3JtYWxpemVkTW9kZSA9PT0gXCJzaG93XCIgfHxcblx0XHRcdFx0XHRcdCggbm9ybWFsaXplZE1vZGUgPT09IGRlZmF1bHRNb2RlICYmIG5vcm1hbGl6ZWRNb2RlID09PSBcImhpZGVcIiApICkgKSB7XG5cdFx0XHRcdFx0ZWwuc2hvdygpO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCAhZGVmYXVsdE1vZGUgfHwgbm9ybWFsaXplZE1vZGUgIT09IFwibm9uZVwiICkge1xuXHRcdFx0XHRcdCQuZWZmZWN0cy5zYXZlU3R5bGUoIGVsICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoICQuaXNGdW5jdGlvbiggbmV4dCApICkge1xuXHRcdFx0XHRcdG5leHQoKTtcblx0XHRcdFx0fVxuXHRcdFx0fTtcblxuXHRcdGlmICggJC5meC5vZmYgfHwgIWVmZmVjdE1ldGhvZCApIHtcblxuXHRcdFx0Ly8gRGVsZWdhdGUgdG8gdGhlIG9yaWdpbmFsIG1ldGhvZCAoZS5nLiwgLnNob3coKSkgaWYgcG9zc2libGVcblx0XHRcdGlmICggbW9kZSApIHtcblx0XHRcdFx0cmV0dXJuIHRoaXNbIG1vZGUgXSggYXJncy5kdXJhdGlvbiwgY29tcGxldGUgKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHJldHVybiB0aGlzLmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdGlmICggY29tcGxldGUgKSB7XG5cdFx0XHRcdFx0XHRjb21wbGV0ZS5jYWxsKCB0aGlzICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9ICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0ZnVuY3Rpb24gcnVuKCBuZXh0ICkge1xuXHRcdFx0dmFyIGVsZW0gPSAkKCB0aGlzICk7XG5cblx0XHRcdGZ1bmN0aW9uIGNsZWFudXAoKSB7XG5cdFx0XHRcdGVsZW0ucmVtb3ZlRGF0YSggZGF0YVNwYWNlQW5pbWF0ZWQgKTtcblxuXHRcdFx0XHQkLmVmZmVjdHMuY2xlYW5VcCggZWxlbSApO1xuXG5cdFx0XHRcdGlmICggYXJncy5tb2RlID09PSBcImhpZGVcIiApIHtcblx0XHRcdFx0XHRlbGVtLmhpZGUoKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGRvbmUoKTtcblx0XHRcdH1cblxuXHRcdFx0ZnVuY3Rpb24gZG9uZSgpIHtcblx0XHRcdFx0aWYgKCAkLmlzRnVuY3Rpb24oIGNvbXBsZXRlICkgKSB7XG5cdFx0XHRcdFx0Y29tcGxldGUuY2FsbCggZWxlbVsgMCBdICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoICQuaXNGdW5jdGlvbiggbmV4dCApICkge1xuXHRcdFx0XHRcdG5leHQoKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvLyBPdmVycmlkZSBtb2RlIG9wdGlvbiBvbiBhIHBlciBlbGVtZW50IGJhc2lzLFxuXHRcdFx0Ly8gYXMgdG9nZ2xlIGNhbiBiZSBlaXRoZXIgc2hvdyBvciBoaWRlIGRlcGVuZGluZyBvbiBlbGVtZW50IHN0YXRlXG5cdFx0XHRhcmdzLm1vZGUgPSBtb2Rlcy5zaGlmdCgpO1xuXG5cdFx0XHRpZiAoICQudWlCYWNrQ29tcGF0ICE9PSBmYWxzZSAmJiAhZGVmYXVsdE1vZGUgKSB7XG5cdFx0XHRcdGlmICggZWxlbS5pcyggXCI6aGlkZGVuXCIgKSA/IG1vZGUgPT09IFwiaGlkZVwiIDogbW9kZSA9PT0gXCJzaG93XCIgKSB7XG5cblx0XHRcdFx0XHQvLyBDYWxsIHRoZSBjb3JlIG1ldGhvZCB0byB0cmFjayBcIm9sZGRpc3BsYXlcIiBwcm9wZXJseVxuXHRcdFx0XHRcdGVsZW1bIG1vZGUgXSgpO1xuXHRcdFx0XHRcdGRvbmUoKTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRlZmZlY3RNZXRob2QuY2FsbCggZWxlbVsgMCBdLCBhcmdzLCBkb25lICk7XG5cdFx0XHRcdH1cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGlmICggYXJncy5tb2RlID09PSBcIm5vbmVcIiApIHtcblxuXHRcdFx0XHRcdC8vIENhbGwgdGhlIGNvcmUgbWV0aG9kIHRvIHRyYWNrIFwib2xkZGlzcGxheVwiIHByb3Blcmx5XG5cdFx0XHRcdFx0ZWxlbVsgbW9kZSBdKCk7XG5cdFx0XHRcdFx0ZG9uZSgpO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGVmZmVjdE1ldGhvZC5jYWxsKCBlbGVtWyAwIF0sIGFyZ3MsIGNsZWFudXAgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIFJ1biBwcmVmaWx0ZXIgb24gYWxsIGVsZW1lbnRzIGZpcnN0IHRvIGVuc3VyZSB0aGF0XG5cdFx0Ly8gYW55IHNob3dpbmcgb3IgaGlkaW5nIGhhcHBlbnMgYmVmb3JlIHBsYWNlaG9sZGVyIGNyZWF0aW9uLFxuXHRcdC8vIHdoaWNoIGVuc3VyZXMgdGhhdCBhbnkgbGF5b3V0IGNoYW5nZXMgYXJlIGNvcnJlY3RseSBjYXB0dXJlZC5cblx0XHRyZXR1cm4gcXVldWUgPT09IGZhbHNlID9cblx0XHRcdHRoaXMuZWFjaCggcHJlZmlsdGVyICkuZWFjaCggcnVuICkgOlxuXHRcdFx0dGhpcy5xdWV1ZSggcXVldWVOYW1lLCBwcmVmaWx0ZXIgKS5xdWV1ZSggcXVldWVOYW1lLCBydW4gKTtcblx0fSxcblxuXHRzaG93OiAoIGZ1bmN0aW9uKCBvcmlnICkge1xuXHRcdHJldHVybiBmdW5jdGlvbiggb3B0aW9uICkge1xuXHRcdFx0aWYgKCBzdGFuZGFyZEFuaW1hdGlvbk9wdGlvbiggb3B0aW9uICkgKSB7XG5cdFx0XHRcdHJldHVybiBvcmlnLmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHZhciBhcmdzID0gX25vcm1hbGl6ZUFyZ3VtZW50cy5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdFx0XHRcdGFyZ3MubW9kZSA9IFwic2hvd1wiO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5lZmZlY3QuY2FsbCggdGhpcywgYXJncyApO1xuXHRcdFx0fVxuXHRcdH07XG5cdH0gKSggJC5mbi5zaG93ICksXG5cblx0aGlkZTogKCBmdW5jdGlvbiggb3JpZyApIHtcblx0XHRyZXR1cm4gZnVuY3Rpb24oIG9wdGlvbiApIHtcblx0XHRcdGlmICggc3RhbmRhcmRBbmltYXRpb25PcHRpb24oIG9wdGlvbiApICkge1xuXHRcdFx0XHRyZXR1cm4gb3JpZy5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR2YXIgYXJncyA9IF9ub3JtYWxpemVBcmd1bWVudHMuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdFx0XHRhcmdzLm1vZGUgPSBcImhpZGVcIjtcblx0XHRcdFx0cmV0dXJuIHRoaXMuZWZmZWN0LmNhbGwoIHRoaXMsIGFyZ3MgKTtcblx0XHRcdH1cblx0XHR9O1xuXHR9ICkoICQuZm4uaGlkZSApLFxuXG5cdHRvZ2dsZTogKCBmdW5jdGlvbiggb3JpZyApIHtcblx0XHRyZXR1cm4gZnVuY3Rpb24oIG9wdGlvbiApIHtcblx0XHRcdGlmICggc3RhbmRhcmRBbmltYXRpb25PcHRpb24oIG9wdGlvbiApIHx8IHR5cGVvZiBvcHRpb24gPT09IFwiYm9vbGVhblwiICkge1xuXHRcdFx0XHRyZXR1cm4gb3JpZy5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR2YXIgYXJncyA9IF9ub3JtYWxpemVBcmd1bWVudHMuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdFx0XHRhcmdzLm1vZGUgPSBcInRvZ2dsZVwiO1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5lZmZlY3QuY2FsbCggdGhpcywgYXJncyApO1xuXHRcdFx0fVxuXHRcdH07XG5cdH0gKSggJC5mbi50b2dnbGUgKSxcblxuXHRjc3NVbml0OiBmdW5jdGlvbigga2V5ICkge1xuXHRcdHZhciBzdHlsZSA9IHRoaXMuY3NzKCBrZXkgKSxcblx0XHRcdHZhbCA9IFtdO1xuXG5cdFx0JC5lYWNoKCBbIFwiZW1cIiwgXCJweFwiLCBcIiVcIiwgXCJwdFwiIF0sIGZ1bmN0aW9uKCBpLCB1bml0ICkge1xuXHRcdFx0aWYgKCBzdHlsZS5pbmRleE9mKCB1bml0ICkgPiAwICkge1xuXHRcdFx0XHR2YWwgPSBbIHBhcnNlRmxvYXQoIHN0eWxlICksIHVuaXQgXTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdFx0cmV0dXJuIHZhbDtcblx0fSxcblxuXHRjc3NDbGlwOiBmdW5jdGlvbiggY2xpcE9iaiApIHtcblx0XHRpZiAoIGNsaXBPYmogKSB7XG5cdFx0XHRyZXR1cm4gdGhpcy5jc3MoIFwiY2xpcFwiLCBcInJlY3QoXCIgKyBjbGlwT2JqLnRvcCArIFwicHggXCIgKyBjbGlwT2JqLnJpZ2h0ICsgXCJweCBcIiArXG5cdFx0XHRcdGNsaXBPYmouYm90dG9tICsgXCJweCBcIiArIGNsaXBPYmoubGVmdCArIFwicHgpXCIgKTtcblx0XHR9XG5cdFx0cmV0dXJuIHBhcnNlQ2xpcCggdGhpcy5jc3MoIFwiY2xpcFwiICksIHRoaXMgKTtcblx0fSxcblxuXHR0cmFuc2ZlcjogZnVuY3Rpb24oIG9wdGlvbnMsIGRvbmUgKSB7XG5cdFx0dmFyIGVsZW1lbnQgPSAkKCB0aGlzICksXG5cdFx0XHR0YXJnZXQgPSAkKCBvcHRpb25zLnRvICksXG5cdFx0XHR0YXJnZXRGaXhlZCA9IHRhcmdldC5jc3MoIFwicG9zaXRpb25cIiApID09PSBcImZpeGVkXCIsXG5cdFx0XHRib2R5ID0gJCggXCJib2R5XCIgKSxcblx0XHRcdGZpeFRvcCA9IHRhcmdldEZpeGVkID8gYm9keS5zY3JvbGxUb3AoKSA6IDAsXG5cdFx0XHRmaXhMZWZ0ID0gdGFyZ2V0Rml4ZWQgPyBib2R5LnNjcm9sbExlZnQoKSA6IDAsXG5cdFx0XHRlbmRQb3NpdGlvbiA9IHRhcmdldC5vZmZzZXQoKSxcblx0XHRcdGFuaW1hdGlvbiA9IHtcblx0XHRcdFx0dG9wOiBlbmRQb3NpdGlvbi50b3AgLSBmaXhUb3AsXG5cdFx0XHRcdGxlZnQ6IGVuZFBvc2l0aW9uLmxlZnQgLSBmaXhMZWZ0LFxuXHRcdFx0XHRoZWlnaHQ6IHRhcmdldC5pbm5lckhlaWdodCgpLFxuXHRcdFx0XHR3aWR0aDogdGFyZ2V0LmlubmVyV2lkdGgoKVxuXHRcdFx0fSxcblx0XHRcdHN0YXJ0UG9zaXRpb24gPSBlbGVtZW50Lm9mZnNldCgpLFxuXHRcdFx0dHJhbnNmZXIgPSAkKCBcIjxkaXYgY2xhc3M9J3VpLWVmZmVjdHMtdHJhbnNmZXInPjwvZGl2PlwiIClcblx0XHRcdFx0LmFwcGVuZFRvKCBcImJvZHlcIiApXG5cdFx0XHRcdC5hZGRDbGFzcyggb3B0aW9ucy5jbGFzc05hbWUgKVxuXHRcdFx0XHQuY3NzKCB7XG5cdFx0XHRcdFx0dG9wOiBzdGFydFBvc2l0aW9uLnRvcCAtIGZpeFRvcCxcblx0XHRcdFx0XHRsZWZ0OiBzdGFydFBvc2l0aW9uLmxlZnQgLSBmaXhMZWZ0LFxuXHRcdFx0XHRcdGhlaWdodDogZWxlbWVudC5pbm5lckhlaWdodCgpLFxuXHRcdFx0XHRcdHdpZHRoOiBlbGVtZW50LmlubmVyV2lkdGgoKSxcblx0XHRcdFx0XHRwb3NpdGlvbjogdGFyZ2V0Rml4ZWQgPyBcImZpeGVkXCIgOiBcImFic29sdXRlXCJcblx0XHRcdFx0fSApXG5cdFx0XHRcdC5hbmltYXRlKCBhbmltYXRpb24sIG9wdGlvbnMuZHVyYXRpb24sIG9wdGlvbnMuZWFzaW5nLCBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHR0cmFuc2Zlci5yZW1vdmUoKTtcblx0XHRcdFx0XHRpZiAoICQuaXNGdW5jdGlvbiggZG9uZSApICkge1xuXHRcdFx0XHRcdFx0ZG9uZSgpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSApO1xuXHR9XG59ICk7XG5cbmZ1bmN0aW9uIHBhcnNlQ2xpcCggc3RyLCBlbGVtZW50ICkge1xuXHRcdHZhciBvdXRlcldpZHRoID0gZWxlbWVudC5vdXRlcldpZHRoKCksXG5cdFx0XHRvdXRlckhlaWdodCA9IGVsZW1lbnQub3V0ZXJIZWlnaHQoKSxcblx0XHRcdGNsaXBSZWdleCA9IC9ecmVjdFxcKCgtP1xcZCpcXC4/XFxkKnB4fC0/XFxkKyV8YXV0byksP1xccyooLT9cXGQqXFwuP1xcZCpweHwtP1xcZCslfGF1dG8pLD9cXHMqKC0/XFxkKlxcLj9cXGQqcHh8LT9cXGQrJXxhdXRvKSw/XFxzKigtP1xcZCpcXC4/XFxkKnB4fC0/XFxkKyV8YXV0bylcXCkkLyxcblx0XHRcdHZhbHVlcyA9IGNsaXBSZWdleC5leGVjKCBzdHIgKSB8fCBbIFwiXCIsIDAsIG91dGVyV2lkdGgsIG91dGVySGVpZ2h0LCAwIF07XG5cblx0XHRyZXR1cm4ge1xuXHRcdFx0dG9wOiBwYXJzZUZsb2F0KCB2YWx1ZXNbIDEgXSApIHx8IDAsXG5cdFx0XHRyaWdodDogdmFsdWVzWyAyIF0gPT09IFwiYXV0b1wiID8gb3V0ZXJXaWR0aCA6IHBhcnNlRmxvYXQoIHZhbHVlc1sgMiBdICksXG5cdFx0XHRib3R0b206IHZhbHVlc1sgMyBdID09PSBcImF1dG9cIiA/IG91dGVySGVpZ2h0IDogcGFyc2VGbG9hdCggdmFsdWVzWyAzIF0gKSxcblx0XHRcdGxlZnQ6IHBhcnNlRmxvYXQoIHZhbHVlc1sgNCBdICkgfHwgMFxuXHRcdH07XG59XG5cbiQuZnguc3RlcC5jbGlwID0gZnVuY3Rpb24oIGZ4ICkge1xuXHRpZiAoICFmeC5jbGlwSW5pdCApIHtcblx0XHRmeC5zdGFydCA9ICQoIGZ4LmVsZW0gKS5jc3NDbGlwKCk7XG5cdFx0aWYgKCB0eXBlb2YgZnguZW5kID09PSBcInN0cmluZ1wiICkge1xuXHRcdFx0ZnguZW5kID0gcGFyc2VDbGlwKCBmeC5lbmQsIGZ4LmVsZW0gKTtcblx0XHR9XG5cdFx0ZnguY2xpcEluaXQgPSB0cnVlO1xuXHR9XG5cblx0JCggZnguZWxlbSApLmNzc0NsaXAoIHtcblx0XHR0b3A6IGZ4LnBvcyAqICggZnguZW5kLnRvcCAtIGZ4LnN0YXJ0LnRvcCApICsgZnguc3RhcnQudG9wLFxuXHRcdHJpZ2h0OiBmeC5wb3MgKiAoIGZ4LmVuZC5yaWdodCAtIGZ4LnN0YXJ0LnJpZ2h0ICkgKyBmeC5zdGFydC5yaWdodCxcblx0XHRib3R0b206IGZ4LnBvcyAqICggZnguZW5kLmJvdHRvbSAtIGZ4LnN0YXJ0LmJvdHRvbSApICsgZnguc3RhcnQuYm90dG9tLFxuXHRcdGxlZnQ6IGZ4LnBvcyAqICggZnguZW5kLmxlZnQgLSBmeC5zdGFydC5sZWZ0ICkgKyBmeC5zdGFydC5sZWZ0XG5cdH0gKTtcbn07XG5cbn0gKSgpO1xuXG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqIEVBU0lORyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbiggZnVuY3Rpb24oKSB7XG5cbi8vIEJhc2VkIG9uIGVhc2luZyBlcXVhdGlvbnMgZnJvbSBSb2JlcnQgUGVubmVyIChodHRwOi8vd3d3LnJvYmVydHBlbm5lci5jb20vZWFzaW5nKVxuXG52YXIgYmFzZUVhc2luZ3MgPSB7fTtcblxuJC5lYWNoKCBbIFwiUXVhZFwiLCBcIkN1YmljXCIsIFwiUXVhcnRcIiwgXCJRdWludFwiLCBcIkV4cG9cIiBdLCBmdW5jdGlvbiggaSwgbmFtZSApIHtcblx0YmFzZUVhc2luZ3NbIG5hbWUgXSA9IGZ1bmN0aW9uKCBwICkge1xuXHRcdHJldHVybiBNYXRoLnBvdyggcCwgaSArIDIgKTtcblx0fTtcbn0gKTtcblxuJC5leHRlbmQoIGJhc2VFYXNpbmdzLCB7XG5cdFNpbmU6IGZ1bmN0aW9uKCBwICkge1xuXHRcdHJldHVybiAxIC0gTWF0aC5jb3MoIHAgKiBNYXRoLlBJIC8gMiApO1xuXHR9LFxuXHRDaXJjOiBmdW5jdGlvbiggcCApIHtcblx0XHRyZXR1cm4gMSAtIE1hdGguc3FydCggMSAtIHAgKiBwICk7XG5cdH0sXG5cdEVsYXN0aWM6IGZ1bmN0aW9uKCBwICkge1xuXHRcdHJldHVybiBwID09PSAwIHx8IHAgPT09IDEgPyBwIDpcblx0XHRcdC1NYXRoLnBvdyggMiwgOCAqICggcCAtIDEgKSApICogTWF0aC5zaW4oICggKCBwIC0gMSApICogODAgLSA3LjUgKSAqIE1hdGguUEkgLyAxNSApO1xuXHR9LFxuXHRCYWNrOiBmdW5jdGlvbiggcCApIHtcblx0XHRyZXR1cm4gcCAqIHAgKiAoIDMgKiBwIC0gMiApO1xuXHR9LFxuXHRCb3VuY2U6IGZ1bmN0aW9uKCBwICkge1xuXHRcdHZhciBwb3cyLFxuXHRcdFx0Ym91bmNlID0gNDtcblxuXHRcdHdoaWxlICggcCA8ICggKCBwb3cyID0gTWF0aC5wb3coIDIsIC0tYm91bmNlICkgKSAtIDEgKSAvIDExICkge31cblx0XHRyZXR1cm4gMSAvIE1hdGgucG93KCA0LCAzIC0gYm91bmNlICkgLSA3LjU2MjUgKiBNYXRoLnBvdyggKCBwb3cyICogMyAtIDIgKSAvIDIyIC0gcCwgMiApO1xuXHR9XG59ICk7XG5cbiQuZWFjaCggYmFzZUVhc2luZ3MsIGZ1bmN0aW9uKCBuYW1lLCBlYXNlSW4gKSB7XG5cdCQuZWFzaW5nWyBcImVhc2VJblwiICsgbmFtZSBdID0gZWFzZUluO1xuXHQkLmVhc2luZ1sgXCJlYXNlT3V0XCIgKyBuYW1lIF0gPSBmdW5jdGlvbiggcCApIHtcblx0XHRyZXR1cm4gMSAtIGVhc2VJbiggMSAtIHAgKTtcblx0fTtcblx0JC5lYXNpbmdbIFwiZWFzZUluT3V0XCIgKyBuYW1lIF0gPSBmdW5jdGlvbiggcCApIHtcblx0XHRyZXR1cm4gcCA8IDAuNSA/XG5cdFx0XHRlYXNlSW4oIHAgKiAyICkgLyAyIDpcblx0XHRcdDEgLSBlYXNlSW4oIHAgKiAtMiArIDIgKSAvIDI7XG5cdH07XG59ICk7XG5cbn0gKSgpO1xuXG52YXIgZWZmZWN0ID0gJC5lZmZlY3RzO1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIEVmZmVjdHMgQmxpbmQgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IEJsaW5kIEVmZmVjdFxuLy8+Pmdyb3VwOiBFZmZlY3RzXG4vLz4+ZGVzY3JpcHRpb246IEJsaW5kcyB0aGUgZWxlbWVudC5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9ibGluZC1lZmZlY3QvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vZWZmZWN0L1xuXG5cblxudmFyIGVmZmVjdHNFZmZlY3RCbGluZCA9ICQuZWZmZWN0cy5kZWZpbmUoIFwiYmxpbmRcIiwgXCJoaWRlXCIsIGZ1bmN0aW9uKCBvcHRpb25zLCBkb25lICkge1xuXHR2YXIgbWFwID0ge1xuXHRcdFx0dXA6IFsgXCJib3R0b21cIiwgXCJ0b3BcIiBdLFxuXHRcdFx0dmVydGljYWw6IFsgXCJib3R0b21cIiwgXCJ0b3BcIiBdLFxuXHRcdFx0ZG93bjogWyBcInRvcFwiLCBcImJvdHRvbVwiIF0sXG5cdFx0XHRsZWZ0OiBbIFwicmlnaHRcIiwgXCJsZWZ0XCIgXSxcblx0XHRcdGhvcml6b250YWw6IFsgXCJyaWdodFwiLCBcImxlZnRcIiBdLFxuXHRcdFx0cmlnaHQ6IFsgXCJsZWZ0XCIsIFwicmlnaHRcIiBdXG5cdFx0fSxcblx0XHRlbGVtZW50ID0gJCggdGhpcyApLFxuXHRcdGRpcmVjdGlvbiA9IG9wdGlvbnMuZGlyZWN0aW9uIHx8IFwidXBcIixcblx0XHRzdGFydCA9IGVsZW1lbnQuY3NzQ2xpcCgpLFxuXHRcdGFuaW1hdGUgPSB7IGNsaXA6ICQuZXh0ZW5kKCB7fSwgc3RhcnQgKSB9LFxuXHRcdHBsYWNlaG9sZGVyID0gJC5lZmZlY3RzLmNyZWF0ZVBsYWNlaG9sZGVyKCBlbGVtZW50ICk7XG5cblx0YW5pbWF0ZS5jbGlwWyBtYXBbIGRpcmVjdGlvbiBdWyAwIF0gXSA9IGFuaW1hdGUuY2xpcFsgbWFwWyBkaXJlY3Rpb24gXVsgMSBdIF07XG5cblx0aWYgKCBvcHRpb25zLm1vZGUgPT09IFwic2hvd1wiICkge1xuXHRcdGVsZW1lbnQuY3NzQ2xpcCggYW5pbWF0ZS5jbGlwICk7XG5cdFx0aWYgKCBwbGFjZWhvbGRlciApIHtcblx0XHRcdHBsYWNlaG9sZGVyLmNzcyggJC5lZmZlY3RzLmNsaXBUb0JveCggYW5pbWF0ZSApICk7XG5cdFx0fVxuXG5cdFx0YW5pbWF0ZS5jbGlwID0gc3RhcnQ7XG5cdH1cblxuXHRpZiAoIHBsYWNlaG9sZGVyICkge1xuXHRcdHBsYWNlaG9sZGVyLmFuaW1hdGUoICQuZWZmZWN0cy5jbGlwVG9Cb3goIGFuaW1hdGUgKSwgb3B0aW9ucy5kdXJhdGlvbiwgb3B0aW9ucy5lYXNpbmcgKTtcblx0fVxuXG5cdGVsZW1lbnQuYW5pbWF0ZSggYW5pbWF0ZSwge1xuXHRcdHF1ZXVlOiBmYWxzZSxcblx0XHRkdXJhdGlvbjogb3B0aW9ucy5kdXJhdGlvbixcblx0XHRlYXNpbmc6IG9wdGlvbnMuZWFzaW5nLFxuXHRcdGNvbXBsZXRlOiBkb25lXG5cdH0gKTtcbn0gKTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBFZmZlY3RzIEJvdW5jZSAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogQm91bmNlIEVmZmVjdFxuLy8+Pmdyb3VwOiBFZmZlY3RzXG4vLz4+ZGVzY3JpcHRpb246IEJvdW5jZXMgYW4gZWxlbWVudCBob3Jpem9udGFsbHkgb3IgdmVydGljYWxseSBuIHRpbWVzLlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2JvdW5jZS1lZmZlY3QvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vZWZmZWN0L1xuXG5cblxudmFyIGVmZmVjdHNFZmZlY3RCb3VuY2UgPSAkLmVmZmVjdHMuZGVmaW5lKCBcImJvdW5jZVwiLCBmdW5jdGlvbiggb3B0aW9ucywgZG9uZSApIHtcblx0dmFyIHVwQW5pbSwgZG93bkFuaW0sIHJlZlZhbHVlLFxuXHRcdGVsZW1lbnQgPSAkKCB0aGlzICksXG5cblx0XHQvLyBEZWZhdWx0czpcblx0XHRtb2RlID0gb3B0aW9ucy5tb2RlLFxuXHRcdGhpZGUgPSBtb2RlID09PSBcImhpZGVcIixcblx0XHRzaG93ID0gbW9kZSA9PT0gXCJzaG93XCIsXG5cdFx0ZGlyZWN0aW9uID0gb3B0aW9ucy5kaXJlY3Rpb24gfHwgXCJ1cFwiLFxuXHRcdGRpc3RhbmNlID0gb3B0aW9ucy5kaXN0YW5jZSxcblx0XHR0aW1lcyA9IG9wdGlvbnMudGltZXMgfHwgNSxcblxuXHRcdC8vIE51bWJlciBvZiBpbnRlcm5hbCBhbmltYXRpb25zXG5cdFx0YW5pbXMgPSB0aW1lcyAqIDIgKyAoIHNob3cgfHwgaGlkZSA/IDEgOiAwICksXG5cdFx0c3BlZWQgPSBvcHRpb25zLmR1cmF0aW9uIC8gYW5pbXMsXG5cdFx0ZWFzaW5nID0gb3B0aW9ucy5lYXNpbmcsXG5cblx0XHQvLyBVdGlsaXR5OlxuXHRcdHJlZiA9ICggZGlyZWN0aW9uID09PSBcInVwXCIgfHwgZGlyZWN0aW9uID09PSBcImRvd25cIiApID8gXCJ0b3BcIiA6IFwibGVmdFwiLFxuXHRcdG1vdGlvbiA9ICggZGlyZWN0aW9uID09PSBcInVwXCIgfHwgZGlyZWN0aW9uID09PSBcImxlZnRcIiApLFxuXHRcdGkgPSAwLFxuXG5cdFx0cXVldWVsZW4gPSBlbGVtZW50LnF1ZXVlKCkubGVuZ3RoO1xuXG5cdCQuZWZmZWN0cy5jcmVhdGVQbGFjZWhvbGRlciggZWxlbWVudCApO1xuXG5cdHJlZlZhbHVlID0gZWxlbWVudC5jc3MoIHJlZiApO1xuXG5cdC8vIERlZmF1bHQgZGlzdGFuY2UgZm9yIHRoZSBCSUdHRVNUIGJvdW5jZSBpcyB0aGUgb3V0ZXIgRGlzdGFuY2UgLyAzXG5cdGlmICggIWRpc3RhbmNlICkge1xuXHRcdGRpc3RhbmNlID0gZWxlbWVudFsgcmVmID09PSBcInRvcFwiID8gXCJvdXRlckhlaWdodFwiIDogXCJvdXRlcldpZHRoXCIgXSgpIC8gMztcblx0fVxuXG5cdGlmICggc2hvdyApIHtcblx0XHRkb3duQW5pbSA9IHsgb3BhY2l0eTogMSB9O1xuXHRcdGRvd25BbmltWyByZWYgXSA9IHJlZlZhbHVlO1xuXG5cdFx0Ly8gSWYgd2UgYXJlIHNob3dpbmcsIGZvcmNlIG9wYWNpdHkgMCBhbmQgc2V0IHRoZSBpbml0aWFsIHBvc2l0aW9uXG5cdFx0Ly8gdGhlbiBkbyB0aGUgXCJmaXJzdFwiIGFuaW1hdGlvblxuXHRcdGVsZW1lbnRcblx0XHRcdC5jc3MoIFwib3BhY2l0eVwiLCAwIClcblx0XHRcdC5jc3MoIHJlZiwgbW90aW9uID8gLWRpc3RhbmNlICogMiA6IGRpc3RhbmNlICogMiApXG5cdFx0XHQuYW5pbWF0ZSggZG93bkFuaW0sIHNwZWVkLCBlYXNpbmcgKTtcblx0fVxuXG5cdC8vIFN0YXJ0IGF0IHRoZSBzbWFsbGVzdCBkaXN0YW5jZSBpZiB3ZSBhcmUgaGlkaW5nXG5cdGlmICggaGlkZSApIHtcblx0XHRkaXN0YW5jZSA9IGRpc3RhbmNlIC8gTWF0aC5wb3coIDIsIHRpbWVzIC0gMSApO1xuXHR9XG5cblx0ZG93bkFuaW0gPSB7fTtcblx0ZG93bkFuaW1bIHJlZiBdID0gcmVmVmFsdWU7XG5cblx0Ly8gQm91bmNlcyB1cC9kb3duL2xlZnQvcmlnaHQgdGhlbiBiYWNrIHRvIDAgLS0gdGltZXMgKiAyIGFuaW1hdGlvbnMgaGFwcGVuIGhlcmVcblx0Zm9yICggOyBpIDwgdGltZXM7IGkrKyApIHtcblx0XHR1cEFuaW0gPSB7fTtcblx0XHR1cEFuaW1bIHJlZiBdID0gKCBtb3Rpb24gPyBcIi09XCIgOiBcIis9XCIgKSArIGRpc3RhbmNlO1xuXG5cdFx0ZWxlbWVudFxuXHRcdFx0LmFuaW1hdGUoIHVwQW5pbSwgc3BlZWQsIGVhc2luZyApXG5cdFx0XHQuYW5pbWF0ZSggZG93bkFuaW0sIHNwZWVkLCBlYXNpbmcgKTtcblxuXHRcdGRpc3RhbmNlID0gaGlkZSA/IGRpc3RhbmNlICogMiA6IGRpc3RhbmNlIC8gMjtcblx0fVxuXG5cdC8vIExhc3QgQm91bmNlIHdoZW4gSGlkaW5nXG5cdGlmICggaGlkZSApIHtcblx0XHR1cEFuaW0gPSB7IG9wYWNpdHk6IDAgfTtcblx0XHR1cEFuaW1bIHJlZiBdID0gKCBtb3Rpb24gPyBcIi09XCIgOiBcIis9XCIgKSArIGRpc3RhbmNlO1xuXG5cdFx0ZWxlbWVudC5hbmltYXRlKCB1cEFuaW0sIHNwZWVkLCBlYXNpbmcgKTtcblx0fVxuXG5cdGVsZW1lbnQucXVldWUoIGRvbmUgKTtcblxuXHQkLmVmZmVjdHMudW5zaGlmdCggZWxlbWVudCwgcXVldWVsZW4sIGFuaW1zICsgMSApO1xufSApO1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIEVmZmVjdHMgQ2xpcCAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogQ2xpcCBFZmZlY3Rcbi8vPj5ncm91cDogRWZmZWN0c1xuLy8+PmRlc2NyaXB0aW9uOiBDbGlwcyB0aGUgZWxlbWVudCBvbiBhbmQgb2ZmIGxpa2UgYW4gb2xkIFRWLlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2NsaXAtZWZmZWN0L1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL2VmZmVjdC9cblxuXG5cbnZhciBlZmZlY3RzRWZmZWN0Q2xpcCA9ICQuZWZmZWN0cy5kZWZpbmUoIFwiY2xpcFwiLCBcImhpZGVcIiwgZnVuY3Rpb24oIG9wdGlvbnMsIGRvbmUgKSB7XG5cdHZhciBzdGFydCxcblx0XHRhbmltYXRlID0ge30sXG5cdFx0ZWxlbWVudCA9ICQoIHRoaXMgKSxcblx0XHRkaXJlY3Rpb24gPSBvcHRpb25zLmRpcmVjdGlvbiB8fCBcInZlcnRpY2FsXCIsXG5cdFx0Ym90aCA9IGRpcmVjdGlvbiA9PT0gXCJib3RoXCIsXG5cdFx0aG9yaXpvbnRhbCA9IGJvdGggfHwgZGlyZWN0aW9uID09PSBcImhvcml6b250YWxcIixcblx0XHR2ZXJ0aWNhbCA9IGJvdGggfHwgZGlyZWN0aW9uID09PSBcInZlcnRpY2FsXCI7XG5cblx0c3RhcnQgPSBlbGVtZW50LmNzc0NsaXAoKTtcblx0YW5pbWF0ZS5jbGlwID0ge1xuXHRcdHRvcDogdmVydGljYWwgPyAoIHN0YXJ0LmJvdHRvbSAtIHN0YXJ0LnRvcCApIC8gMiA6IHN0YXJ0LnRvcCxcblx0XHRyaWdodDogaG9yaXpvbnRhbCA/ICggc3RhcnQucmlnaHQgLSBzdGFydC5sZWZ0ICkgLyAyIDogc3RhcnQucmlnaHQsXG5cdFx0Ym90dG9tOiB2ZXJ0aWNhbCA/ICggc3RhcnQuYm90dG9tIC0gc3RhcnQudG9wICkgLyAyIDogc3RhcnQuYm90dG9tLFxuXHRcdGxlZnQ6IGhvcml6b250YWwgPyAoIHN0YXJ0LnJpZ2h0IC0gc3RhcnQubGVmdCApIC8gMiA6IHN0YXJ0LmxlZnRcblx0fTtcblxuXHQkLmVmZmVjdHMuY3JlYXRlUGxhY2Vob2xkZXIoIGVsZW1lbnQgKTtcblxuXHRpZiAoIG9wdGlvbnMubW9kZSA9PT0gXCJzaG93XCIgKSB7XG5cdFx0ZWxlbWVudC5jc3NDbGlwKCBhbmltYXRlLmNsaXAgKTtcblx0XHRhbmltYXRlLmNsaXAgPSBzdGFydDtcblx0fVxuXG5cdGVsZW1lbnQuYW5pbWF0ZSggYW5pbWF0ZSwge1xuXHRcdHF1ZXVlOiBmYWxzZSxcblx0XHRkdXJhdGlvbjogb3B0aW9ucy5kdXJhdGlvbixcblx0XHRlYXNpbmc6IG9wdGlvbnMuZWFzaW5nLFxuXHRcdGNvbXBsZXRlOiBkb25lXG5cdH0gKTtcblxufSApO1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIEVmZmVjdHMgRHJvcCAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogRHJvcCBFZmZlY3Rcbi8vPj5ncm91cDogRWZmZWN0c1xuLy8+PmRlc2NyaXB0aW9uOiBNb3ZlcyBhbiBlbGVtZW50IGluIG9uZSBkaXJlY3Rpb24gYW5kIGhpZGVzIGl0IGF0IHRoZSBzYW1lIHRpbWUuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vZHJvcC1lZmZlY3QvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vZWZmZWN0L1xuXG5cblxudmFyIGVmZmVjdHNFZmZlY3REcm9wID0gJC5lZmZlY3RzLmRlZmluZSggXCJkcm9wXCIsIFwiaGlkZVwiLCBmdW5jdGlvbiggb3B0aW9ucywgZG9uZSApIHtcblxuXHR2YXIgZGlzdGFuY2UsXG5cdFx0ZWxlbWVudCA9ICQoIHRoaXMgKSxcblx0XHRtb2RlID0gb3B0aW9ucy5tb2RlLFxuXHRcdHNob3cgPSBtb2RlID09PSBcInNob3dcIixcblx0XHRkaXJlY3Rpb24gPSBvcHRpb25zLmRpcmVjdGlvbiB8fCBcImxlZnRcIixcblx0XHRyZWYgPSAoIGRpcmVjdGlvbiA9PT0gXCJ1cFwiIHx8IGRpcmVjdGlvbiA9PT0gXCJkb3duXCIgKSA/IFwidG9wXCIgOiBcImxlZnRcIixcblx0XHRtb3Rpb24gPSAoIGRpcmVjdGlvbiA9PT0gXCJ1cFwiIHx8IGRpcmVjdGlvbiA9PT0gXCJsZWZ0XCIgKSA/IFwiLT1cIiA6IFwiKz1cIixcblx0XHRvcHBvc2l0ZU1vdGlvbiA9ICggbW90aW9uID09PSBcIis9XCIgKSA/IFwiLT1cIiA6IFwiKz1cIixcblx0XHRhbmltYXRpb24gPSB7XG5cdFx0XHRvcGFjaXR5OiAwXG5cdFx0fTtcblxuXHQkLmVmZmVjdHMuY3JlYXRlUGxhY2Vob2xkZXIoIGVsZW1lbnQgKTtcblxuXHRkaXN0YW5jZSA9IG9wdGlvbnMuZGlzdGFuY2UgfHxcblx0XHRlbGVtZW50WyByZWYgPT09IFwidG9wXCIgPyBcIm91dGVySGVpZ2h0XCIgOiBcIm91dGVyV2lkdGhcIiBdKCB0cnVlICkgLyAyO1xuXG5cdGFuaW1hdGlvblsgcmVmIF0gPSBtb3Rpb24gKyBkaXN0YW5jZTtcblxuXHRpZiAoIHNob3cgKSB7XG5cdFx0ZWxlbWVudC5jc3MoIGFuaW1hdGlvbiApO1xuXG5cdFx0YW5pbWF0aW9uWyByZWYgXSA9IG9wcG9zaXRlTW90aW9uICsgZGlzdGFuY2U7XG5cdFx0YW5pbWF0aW9uLm9wYWNpdHkgPSAxO1xuXHR9XG5cblx0Ly8gQW5pbWF0ZVxuXHRlbGVtZW50LmFuaW1hdGUoIGFuaW1hdGlvbiwge1xuXHRcdHF1ZXVlOiBmYWxzZSxcblx0XHRkdXJhdGlvbjogb3B0aW9ucy5kdXJhdGlvbixcblx0XHRlYXNpbmc6IG9wdGlvbnMuZWFzaW5nLFxuXHRcdGNvbXBsZXRlOiBkb25lXG5cdH0gKTtcbn0gKTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBFZmZlY3RzIEV4cGxvZGUgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IEV4cGxvZGUgRWZmZWN0XG4vLz4+Z3JvdXA6IEVmZmVjdHNcbi8vIGpzY3M6ZGlzYWJsZSBtYXhpbXVtTGluZUxlbmd0aFxuLy8+PmRlc2NyaXB0aW9uOiBFeHBsb2RlcyBhbiBlbGVtZW50IGluIGFsbCBkaXJlY3Rpb25zIGludG8gbiBwaWVjZXMuIEltcGxvZGVzIGFuIGVsZW1lbnQgdG8gaXRzIG9yaWdpbmFsIHdob2xlbmVzcy5cbi8vIGpzY3M6ZW5hYmxlIG1heGltdW1MaW5lTGVuZ3RoXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vZXhwbG9kZS1lZmZlY3QvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vZWZmZWN0L1xuXG5cblxudmFyIGVmZmVjdHNFZmZlY3RFeHBsb2RlID0gJC5lZmZlY3RzLmRlZmluZSggXCJleHBsb2RlXCIsIFwiaGlkZVwiLCBmdW5jdGlvbiggb3B0aW9ucywgZG9uZSApIHtcblxuXHR2YXIgaSwgaiwgbGVmdCwgdG9wLCBteCwgbXksXG5cdFx0cm93cyA9IG9wdGlvbnMucGllY2VzID8gTWF0aC5yb3VuZCggTWF0aC5zcXJ0KCBvcHRpb25zLnBpZWNlcyApICkgOiAzLFxuXHRcdGNlbGxzID0gcm93cyxcblx0XHRlbGVtZW50ID0gJCggdGhpcyApLFxuXHRcdG1vZGUgPSBvcHRpb25zLm1vZGUsXG5cdFx0c2hvdyA9IG1vZGUgPT09IFwic2hvd1wiLFxuXG5cdFx0Ly8gU2hvdyBhbmQgdGhlbiB2aXNpYmlsaXR5OmhpZGRlbiB0aGUgZWxlbWVudCBiZWZvcmUgY2FsY3VsYXRpbmcgb2Zmc2V0XG5cdFx0b2Zmc2V0ID0gZWxlbWVudC5zaG93KCkuY3NzKCBcInZpc2liaWxpdHlcIiwgXCJoaWRkZW5cIiApLm9mZnNldCgpLFxuXG5cdFx0Ly8gV2lkdGggYW5kIGhlaWdodCBvZiBhIHBpZWNlXG5cdFx0d2lkdGggPSBNYXRoLmNlaWwoIGVsZW1lbnQub3V0ZXJXaWR0aCgpIC8gY2VsbHMgKSxcblx0XHRoZWlnaHQgPSBNYXRoLmNlaWwoIGVsZW1lbnQub3V0ZXJIZWlnaHQoKSAvIHJvd3MgKSxcblx0XHRwaWVjZXMgPSBbXTtcblxuXHQvLyBDaGlsZHJlbiBhbmltYXRlIGNvbXBsZXRlOlxuXHRmdW5jdGlvbiBjaGlsZENvbXBsZXRlKCkge1xuXHRcdHBpZWNlcy5wdXNoKCB0aGlzICk7XG5cdFx0aWYgKCBwaWVjZXMubGVuZ3RoID09PSByb3dzICogY2VsbHMgKSB7XG5cdFx0XHRhbmltQ29tcGxldGUoKTtcblx0XHR9XG5cdH1cblxuXHQvLyBDbG9uZSB0aGUgZWxlbWVudCBmb3IgZWFjaCByb3cgYW5kIGNlbGwuXG5cdGZvciAoIGkgPSAwOyBpIDwgcm93czsgaSsrICkgeyAvLyA9PT0+XG5cdFx0dG9wID0gb2Zmc2V0LnRvcCArIGkgKiBoZWlnaHQ7XG5cdFx0bXkgPSBpIC0gKCByb3dzIC0gMSApIC8gMjtcblxuXHRcdGZvciAoIGogPSAwOyBqIDwgY2VsbHM7IGorKyApIHsgLy8gfHx8XG5cdFx0XHRsZWZ0ID0gb2Zmc2V0LmxlZnQgKyBqICogd2lkdGg7XG5cdFx0XHRteCA9IGogLSAoIGNlbGxzIC0gMSApIC8gMjtcblxuXHRcdFx0Ly8gQ3JlYXRlIGEgY2xvbmUgb2YgdGhlIG5vdyBoaWRkZW4gbWFpbiBlbGVtZW50IHRoYXQgd2lsbCBiZSBhYnNvbHV0ZSBwb3NpdGlvbmVkXG5cdFx0XHQvLyB3aXRoaW4gYSB3cmFwcGVyIGRpdiBvZmYgdGhlIC1sZWZ0IGFuZCAtdG9wIGVxdWFsIHRvIHNpemUgb2Ygb3VyIHBpZWNlc1xuXHRcdFx0ZWxlbWVudFxuXHRcdFx0XHQuY2xvbmUoKVxuXHRcdFx0XHQuYXBwZW5kVG8oIFwiYm9keVwiIClcblx0XHRcdFx0LndyYXAoIFwiPGRpdj48L2Rpdj5cIiApXG5cdFx0XHRcdC5jc3MoIHtcblx0XHRcdFx0XHRwb3NpdGlvbjogXCJhYnNvbHV0ZVwiLFxuXHRcdFx0XHRcdHZpc2liaWxpdHk6IFwidmlzaWJsZVwiLFxuXHRcdFx0XHRcdGxlZnQ6IC1qICogd2lkdGgsXG5cdFx0XHRcdFx0dG9wOiAtaSAqIGhlaWdodFxuXHRcdFx0XHR9IClcblxuXHRcdFx0XHQvLyBTZWxlY3QgdGhlIHdyYXBwZXIgLSBtYWtlIGl0IG92ZXJmbG93OiBoaWRkZW4gYW5kIGFic29sdXRlIHBvc2l0aW9uZWQgYmFzZWQgb25cblx0XHRcdFx0Ly8gd2hlcmUgdGhlIG9yaWdpbmFsIHdhcyBsb2NhdGVkICtsZWZ0IGFuZCArdG9wIGVxdWFsIHRvIHRoZSBzaXplIG9mIHBpZWNlc1xuXHRcdFx0XHQucGFyZW50KClcblx0XHRcdFx0XHQuYWRkQ2xhc3MoIFwidWktZWZmZWN0cy1leHBsb2RlXCIgKVxuXHRcdFx0XHRcdC5jc3MoIHtcblx0XHRcdFx0XHRcdHBvc2l0aW9uOiBcImFic29sdXRlXCIsXG5cdFx0XHRcdFx0XHRvdmVyZmxvdzogXCJoaWRkZW5cIixcblx0XHRcdFx0XHRcdHdpZHRoOiB3aWR0aCxcblx0XHRcdFx0XHRcdGhlaWdodDogaGVpZ2h0LFxuXHRcdFx0XHRcdFx0bGVmdDogbGVmdCArICggc2hvdyA/IG14ICogd2lkdGggOiAwICksXG5cdFx0XHRcdFx0XHR0b3A6IHRvcCArICggc2hvdyA/IG15ICogaGVpZ2h0IDogMCApLFxuXHRcdFx0XHRcdFx0b3BhY2l0eTogc2hvdyA/IDAgOiAxXG5cdFx0XHRcdFx0fSApXG5cdFx0XHRcdFx0LmFuaW1hdGUoIHtcblx0XHRcdFx0XHRcdGxlZnQ6IGxlZnQgKyAoIHNob3cgPyAwIDogbXggKiB3aWR0aCApLFxuXHRcdFx0XHRcdFx0dG9wOiB0b3AgKyAoIHNob3cgPyAwIDogbXkgKiBoZWlnaHQgKSxcblx0XHRcdFx0XHRcdG9wYWNpdHk6IHNob3cgPyAxIDogMFxuXHRcdFx0XHRcdH0sIG9wdGlvbnMuZHVyYXRpb24gfHwgNTAwLCBvcHRpb25zLmVhc2luZywgY2hpbGRDb21wbGV0ZSApO1xuXHRcdH1cblx0fVxuXG5cdGZ1bmN0aW9uIGFuaW1Db21wbGV0ZSgpIHtcblx0XHRlbGVtZW50LmNzcygge1xuXHRcdFx0dmlzaWJpbGl0eTogXCJ2aXNpYmxlXCJcblx0XHR9ICk7XG5cdFx0JCggcGllY2VzICkucmVtb3ZlKCk7XG5cdFx0ZG9uZSgpO1xuXHR9XG59ICk7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgRWZmZWN0cyBGYWRlIDEuMTIuMVxuICogaHR0cDovL2pxdWVyeXVpLmNvbVxuICpcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzXG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UuXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKi9cblxuLy8+PmxhYmVsOiBGYWRlIEVmZmVjdFxuLy8+Pmdyb3VwOiBFZmZlY3RzXG4vLz4+ZGVzY3JpcHRpb246IEZhZGVzIHRoZSBlbGVtZW50LlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2ZhZGUtZWZmZWN0L1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL2VmZmVjdC9cblxuXG5cbnZhciBlZmZlY3RzRWZmZWN0RmFkZSA9ICQuZWZmZWN0cy5kZWZpbmUoIFwiZmFkZVwiLCBcInRvZ2dsZVwiLCBmdW5jdGlvbiggb3B0aW9ucywgZG9uZSApIHtcblx0dmFyIHNob3cgPSBvcHRpb25zLm1vZGUgPT09IFwic2hvd1wiO1xuXG5cdCQoIHRoaXMgKVxuXHRcdC5jc3MoIFwib3BhY2l0eVwiLCBzaG93ID8gMCA6IDEgKVxuXHRcdC5hbmltYXRlKCB7XG5cdFx0XHRvcGFjaXR5OiBzaG93ID8gMSA6IDBcblx0XHR9LCB7XG5cdFx0XHRxdWV1ZTogZmFsc2UsXG5cdFx0XHRkdXJhdGlvbjogb3B0aW9ucy5kdXJhdGlvbixcblx0XHRcdGVhc2luZzogb3B0aW9ucy5lYXNpbmcsXG5cdFx0XHRjb21wbGV0ZTogZG9uZVxuXHRcdH0gKTtcbn0gKTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBFZmZlY3RzIEZvbGQgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IEZvbGQgRWZmZWN0XG4vLz4+Z3JvdXA6IEVmZmVjdHNcbi8vPj5kZXNjcmlwdGlvbjogRm9sZHMgYW4gZWxlbWVudCBmaXJzdCBob3Jpem9udGFsbHkgYW5kIHRoZW4gdmVydGljYWxseS5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9mb2xkLWVmZmVjdC9cbi8vPj5kZW1vczogaHR0cDovL2pxdWVyeXVpLmNvbS9lZmZlY3QvXG5cblxuXG52YXIgZWZmZWN0c0VmZmVjdEZvbGQgPSAkLmVmZmVjdHMuZGVmaW5lKCBcImZvbGRcIiwgXCJoaWRlXCIsIGZ1bmN0aW9uKCBvcHRpb25zLCBkb25lICkge1xuXG5cdC8vIENyZWF0ZSBlbGVtZW50XG5cdHZhciBlbGVtZW50ID0gJCggdGhpcyApLFxuXHRcdG1vZGUgPSBvcHRpb25zLm1vZGUsXG5cdFx0c2hvdyA9IG1vZGUgPT09IFwic2hvd1wiLFxuXHRcdGhpZGUgPSBtb2RlID09PSBcImhpZGVcIixcblx0XHRzaXplID0gb3B0aW9ucy5zaXplIHx8IDE1LFxuXHRcdHBlcmNlbnQgPSAvKFswLTldKyklLy5leGVjKCBzaXplICksXG5cdFx0aG9yaXpGaXJzdCA9ICEhb3B0aW9ucy5ob3JpekZpcnN0LFxuXHRcdHJlZiA9IGhvcml6Rmlyc3QgPyBbIFwicmlnaHRcIiwgXCJib3R0b21cIiBdIDogWyBcImJvdHRvbVwiLCBcInJpZ2h0XCIgXSxcblx0XHRkdXJhdGlvbiA9IG9wdGlvbnMuZHVyYXRpb24gLyAyLFxuXG5cdFx0cGxhY2Vob2xkZXIgPSAkLmVmZmVjdHMuY3JlYXRlUGxhY2Vob2xkZXIoIGVsZW1lbnQgKSxcblxuXHRcdHN0YXJ0ID0gZWxlbWVudC5jc3NDbGlwKCksXG5cdFx0YW5pbWF0aW9uMSA9IHsgY2xpcDogJC5leHRlbmQoIHt9LCBzdGFydCApIH0sXG5cdFx0YW5pbWF0aW9uMiA9IHsgY2xpcDogJC5leHRlbmQoIHt9LCBzdGFydCApIH0sXG5cblx0XHRkaXN0YW5jZSA9IFsgc3RhcnRbIHJlZlsgMCBdIF0sIHN0YXJ0WyByZWZbIDEgXSBdIF0sXG5cblx0XHRxdWV1ZWxlbiA9IGVsZW1lbnQucXVldWUoKS5sZW5ndGg7XG5cblx0aWYgKCBwZXJjZW50ICkge1xuXHRcdHNpemUgPSBwYXJzZUludCggcGVyY2VudFsgMSBdLCAxMCApIC8gMTAwICogZGlzdGFuY2VbIGhpZGUgPyAwIDogMSBdO1xuXHR9XG5cdGFuaW1hdGlvbjEuY2xpcFsgcmVmWyAwIF0gXSA9IHNpemU7XG5cdGFuaW1hdGlvbjIuY2xpcFsgcmVmWyAwIF0gXSA9IHNpemU7XG5cdGFuaW1hdGlvbjIuY2xpcFsgcmVmWyAxIF0gXSA9IDA7XG5cblx0aWYgKCBzaG93ICkge1xuXHRcdGVsZW1lbnQuY3NzQ2xpcCggYW5pbWF0aW9uMi5jbGlwICk7XG5cdFx0aWYgKCBwbGFjZWhvbGRlciApIHtcblx0XHRcdHBsYWNlaG9sZGVyLmNzcyggJC5lZmZlY3RzLmNsaXBUb0JveCggYW5pbWF0aW9uMiApICk7XG5cdFx0fVxuXG5cdFx0YW5pbWF0aW9uMi5jbGlwID0gc3RhcnQ7XG5cdH1cblxuXHQvLyBBbmltYXRlXG5cdGVsZW1lbnRcblx0XHQucXVldWUoIGZ1bmN0aW9uKCBuZXh0ICkge1xuXHRcdFx0aWYgKCBwbGFjZWhvbGRlciApIHtcblx0XHRcdFx0cGxhY2Vob2xkZXJcblx0XHRcdFx0XHQuYW5pbWF0ZSggJC5lZmZlY3RzLmNsaXBUb0JveCggYW5pbWF0aW9uMSApLCBkdXJhdGlvbiwgb3B0aW9ucy5lYXNpbmcgKVxuXHRcdFx0XHRcdC5hbmltYXRlKCAkLmVmZmVjdHMuY2xpcFRvQm94KCBhbmltYXRpb24yICksIGR1cmF0aW9uLCBvcHRpb25zLmVhc2luZyApO1xuXHRcdFx0fVxuXG5cdFx0XHRuZXh0KCk7XG5cdFx0fSApXG5cdFx0LmFuaW1hdGUoIGFuaW1hdGlvbjEsIGR1cmF0aW9uLCBvcHRpb25zLmVhc2luZyApXG5cdFx0LmFuaW1hdGUoIGFuaW1hdGlvbjIsIGR1cmF0aW9uLCBvcHRpb25zLmVhc2luZyApXG5cdFx0LnF1ZXVlKCBkb25lICk7XG5cblx0JC5lZmZlY3RzLnVuc2hpZnQoIGVsZW1lbnQsIHF1ZXVlbGVuLCA0ICk7XG59ICk7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgRWZmZWN0cyBIaWdobGlnaHQgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IEhpZ2hsaWdodCBFZmZlY3Rcbi8vPj5ncm91cDogRWZmZWN0c1xuLy8+PmRlc2NyaXB0aW9uOiBIaWdobGlnaHRzIHRoZSBiYWNrZ3JvdW5kIG9mIGFuIGVsZW1lbnQgaW4gYSBkZWZpbmVkIGNvbG9yIGZvciBhIGN1c3RvbSBkdXJhdGlvbi5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9oaWdobGlnaHQtZWZmZWN0L1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL2VmZmVjdC9cblxuXG5cbnZhciBlZmZlY3RzRWZmZWN0SGlnaGxpZ2h0ID0gJC5lZmZlY3RzLmRlZmluZSggXCJoaWdobGlnaHRcIiwgXCJzaG93XCIsIGZ1bmN0aW9uKCBvcHRpb25zLCBkb25lICkge1xuXHR2YXIgZWxlbWVudCA9ICQoIHRoaXMgKSxcblx0XHRhbmltYXRpb24gPSB7XG5cdFx0XHRiYWNrZ3JvdW5kQ29sb3I6IGVsZW1lbnQuY3NzKCBcImJhY2tncm91bmRDb2xvclwiIClcblx0XHR9O1xuXG5cdGlmICggb3B0aW9ucy5tb2RlID09PSBcImhpZGVcIiApIHtcblx0XHRhbmltYXRpb24ub3BhY2l0eSA9IDA7XG5cdH1cblxuXHQkLmVmZmVjdHMuc2F2ZVN0eWxlKCBlbGVtZW50ICk7XG5cblx0ZWxlbWVudFxuXHRcdC5jc3MoIHtcblx0XHRcdGJhY2tncm91bmRJbWFnZTogXCJub25lXCIsXG5cdFx0XHRiYWNrZ3JvdW5kQ29sb3I6IG9wdGlvbnMuY29sb3IgfHwgXCIjZmZmZjk5XCJcblx0XHR9IClcblx0XHQuYW5pbWF0ZSggYW5pbWF0aW9uLCB7XG5cdFx0XHRxdWV1ZTogZmFsc2UsXG5cdFx0XHRkdXJhdGlvbjogb3B0aW9ucy5kdXJhdGlvbixcblx0XHRcdGVhc2luZzogb3B0aW9ucy5lYXNpbmcsXG5cdFx0XHRjb21wbGV0ZTogZG9uZVxuXHRcdH0gKTtcbn0gKTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBFZmZlY3RzIFNpemUgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IFNpemUgRWZmZWN0XG4vLz4+Z3JvdXA6IEVmZmVjdHNcbi8vPj5kZXNjcmlwdGlvbjogUmVzaXplIGFuIGVsZW1lbnQgdG8gYSBzcGVjaWZpZWQgd2lkdGggYW5kIGhlaWdodC5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9zaXplLWVmZmVjdC9cbi8vPj5kZW1vczogaHR0cDovL2pxdWVyeXVpLmNvbS9lZmZlY3QvXG5cblxuXG52YXIgZWZmZWN0c0VmZmVjdFNpemUgPSAkLmVmZmVjdHMuZGVmaW5lKCBcInNpemVcIiwgZnVuY3Rpb24oIG9wdGlvbnMsIGRvbmUgKSB7XG5cblx0Ly8gQ3JlYXRlIGVsZW1lbnRcblx0dmFyIGJhc2VsaW5lLCBmYWN0b3IsIHRlbXAsXG5cdFx0ZWxlbWVudCA9ICQoIHRoaXMgKSxcblxuXHRcdC8vIENvcHkgZm9yIGNoaWxkcmVuXG5cdFx0Y1Byb3BzID0gWyBcImZvbnRTaXplXCIgXSxcblx0XHR2UHJvcHMgPSBbIFwiYm9yZGVyVG9wV2lkdGhcIiwgXCJib3JkZXJCb3R0b21XaWR0aFwiLCBcInBhZGRpbmdUb3BcIiwgXCJwYWRkaW5nQm90dG9tXCIgXSxcblx0XHRoUHJvcHMgPSBbIFwiYm9yZGVyTGVmdFdpZHRoXCIsIFwiYm9yZGVyUmlnaHRXaWR0aFwiLCBcInBhZGRpbmdMZWZ0XCIsIFwicGFkZGluZ1JpZ2h0XCIgXSxcblxuXHRcdC8vIFNldCBvcHRpb25zXG5cdFx0bW9kZSA9IG9wdGlvbnMubW9kZSxcblx0XHRyZXN0b3JlID0gbW9kZSAhPT0gXCJlZmZlY3RcIixcblx0XHRzY2FsZSA9IG9wdGlvbnMuc2NhbGUgfHwgXCJib3RoXCIsXG5cdFx0b3JpZ2luID0gb3B0aW9ucy5vcmlnaW4gfHwgWyBcIm1pZGRsZVwiLCBcImNlbnRlclwiIF0sXG5cdFx0cG9zaXRpb24gPSBlbGVtZW50LmNzcyggXCJwb3NpdGlvblwiICksXG5cdFx0cG9zID0gZWxlbWVudC5wb3NpdGlvbigpLFxuXHRcdG9yaWdpbmFsID0gJC5lZmZlY3RzLnNjYWxlZERpbWVuc2lvbnMoIGVsZW1lbnQgKSxcblx0XHRmcm9tID0gb3B0aW9ucy5mcm9tIHx8IG9yaWdpbmFsLFxuXHRcdHRvID0gb3B0aW9ucy50byB8fCAkLmVmZmVjdHMuc2NhbGVkRGltZW5zaW9ucyggZWxlbWVudCwgMCApO1xuXG5cdCQuZWZmZWN0cy5jcmVhdGVQbGFjZWhvbGRlciggZWxlbWVudCApO1xuXG5cdGlmICggbW9kZSA9PT0gXCJzaG93XCIgKSB7XG5cdFx0dGVtcCA9IGZyb207XG5cdFx0ZnJvbSA9IHRvO1xuXHRcdHRvID0gdGVtcDtcblx0fVxuXG5cdC8vIFNldCBzY2FsaW5nIGZhY3RvclxuXHRmYWN0b3IgPSB7XG5cdFx0ZnJvbToge1xuXHRcdFx0eTogZnJvbS5oZWlnaHQgLyBvcmlnaW5hbC5oZWlnaHQsXG5cdFx0XHR4OiBmcm9tLndpZHRoIC8gb3JpZ2luYWwud2lkdGhcblx0XHR9LFxuXHRcdHRvOiB7XG5cdFx0XHR5OiB0by5oZWlnaHQgLyBvcmlnaW5hbC5oZWlnaHQsXG5cdFx0XHR4OiB0by53aWR0aCAvIG9yaWdpbmFsLndpZHRoXG5cdFx0fVxuXHR9O1xuXG5cdC8vIFNjYWxlIHRoZSBjc3MgYm94XG5cdGlmICggc2NhbGUgPT09IFwiYm94XCIgfHwgc2NhbGUgPT09IFwiYm90aFwiICkge1xuXG5cdFx0Ly8gVmVydGljYWwgcHJvcHMgc2NhbGluZ1xuXHRcdGlmICggZmFjdG9yLmZyb20ueSAhPT0gZmFjdG9yLnRvLnkgKSB7XG5cdFx0XHRmcm9tID0gJC5lZmZlY3RzLnNldFRyYW5zaXRpb24oIGVsZW1lbnQsIHZQcm9wcywgZmFjdG9yLmZyb20ueSwgZnJvbSApO1xuXHRcdFx0dG8gPSAkLmVmZmVjdHMuc2V0VHJhbnNpdGlvbiggZWxlbWVudCwgdlByb3BzLCBmYWN0b3IudG8ueSwgdG8gKTtcblx0XHR9XG5cblx0XHQvLyBIb3Jpem9udGFsIHByb3BzIHNjYWxpbmdcblx0XHRpZiAoIGZhY3Rvci5mcm9tLnggIT09IGZhY3Rvci50by54ICkge1xuXHRcdFx0ZnJvbSA9ICQuZWZmZWN0cy5zZXRUcmFuc2l0aW9uKCBlbGVtZW50LCBoUHJvcHMsIGZhY3Rvci5mcm9tLngsIGZyb20gKTtcblx0XHRcdHRvID0gJC5lZmZlY3RzLnNldFRyYW5zaXRpb24oIGVsZW1lbnQsIGhQcm9wcywgZmFjdG9yLnRvLngsIHRvICk7XG5cdFx0fVxuXHR9XG5cblx0Ly8gU2NhbGUgdGhlIGNvbnRlbnRcblx0aWYgKCBzY2FsZSA9PT0gXCJjb250ZW50XCIgfHwgc2NhbGUgPT09IFwiYm90aFwiICkge1xuXG5cdFx0Ly8gVmVydGljYWwgcHJvcHMgc2NhbGluZ1xuXHRcdGlmICggZmFjdG9yLmZyb20ueSAhPT0gZmFjdG9yLnRvLnkgKSB7XG5cdFx0XHRmcm9tID0gJC5lZmZlY3RzLnNldFRyYW5zaXRpb24oIGVsZW1lbnQsIGNQcm9wcywgZmFjdG9yLmZyb20ueSwgZnJvbSApO1xuXHRcdFx0dG8gPSAkLmVmZmVjdHMuc2V0VHJhbnNpdGlvbiggZWxlbWVudCwgY1Byb3BzLCBmYWN0b3IudG8ueSwgdG8gKTtcblx0XHR9XG5cdH1cblxuXHQvLyBBZGp1c3QgdGhlIHBvc2l0aW9uIHByb3BlcnRpZXMgYmFzZWQgb24gdGhlIHByb3ZpZGVkIG9yaWdpbiBwb2ludHNcblx0aWYgKCBvcmlnaW4gKSB7XG5cdFx0YmFzZWxpbmUgPSAkLmVmZmVjdHMuZ2V0QmFzZWxpbmUoIG9yaWdpbiwgb3JpZ2luYWwgKTtcblx0XHRmcm9tLnRvcCA9ICggb3JpZ2luYWwub3V0ZXJIZWlnaHQgLSBmcm9tLm91dGVySGVpZ2h0ICkgKiBiYXNlbGluZS55ICsgcG9zLnRvcDtcblx0XHRmcm9tLmxlZnQgPSAoIG9yaWdpbmFsLm91dGVyV2lkdGggLSBmcm9tLm91dGVyV2lkdGggKSAqIGJhc2VsaW5lLnggKyBwb3MubGVmdDtcblx0XHR0by50b3AgPSAoIG9yaWdpbmFsLm91dGVySGVpZ2h0IC0gdG8ub3V0ZXJIZWlnaHQgKSAqIGJhc2VsaW5lLnkgKyBwb3MudG9wO1xuXHRcdHRvLmxlZnQgPSAoIG9yaWdpbmFsLm91dGVyV2lkdGggLSB0by5vdXRlcldpZHRoICkgKiBiYXNlbGluZS54ICsgcG9zLmxlZnQ7XG5cdH1cblx0ZWxlbWVudC5jc3MoIGZyb20gKTtcblxuXHQvLyBBbmltYXRlIHRoZSBjaGlsZHJlbiBpZiBkZXNpcmVkXG5cdGlmICggc2NhbGUgPT09IFwiY29udGVudFwiIHx8IHNjYWxlID09PSBcImJvdGhcIiApIHtcblxuXHRcdHZQcm9wcyA9IHZQcm9wcy5jb25jYXQoIFsgXCJtYXJnaW5Ub3BcIiwgXCJtYXJnaW5Cb3R0b21cIiBdICkuY29uY2F0KCBjUHJvcHMgKTtcblx0XHRoUHJvcHMgPSBoUHJvcHMuY29uY2F0KCBbIFwibWFyZ2luTGVmdFwiLCBcIm1hcmdpblJpZ2h0XCIgXSApO1xuXG5cdFx0Ly8gT25seSBhbmltYXRlIGNoaWxkcmVuIHdpdGggd2lkdGggYXR0cmlidXRlcyBzcGVjaWZpZWRcblx0XHQvLyBUT0RPOiBpcyB0aGlzIHJpZ2h0PyBzaG91bGQgd2UgaW5jbHVkZSBhbnl0aGluZyB3aXRoIGNzcyB3aWR0aCBzcGVjaWZpZWQgYXMgd2VsbFxuXHRcdGVsZW1lbnQuZmluZCggXCIqW3dpZHRoXVwiICkuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgY2hpbGQgPSAkKCB0aGlzICksXG5cdFx0XHRcdGNoaWxkT3JpZ2luYWwgPSAkLmVmZmVjdHMuc2NhbGVkRGltZW5zaW9ucyggY2hpbGQgKSxcblx0XHRcdFx0Y2hpbGRGcm9tID0ge1xuXHRcdFx0XHRcdGhlaWdodDogY2hpbGRPcmlnaW5hbC5oZWlnaHQgKiBmYWN0b3IuZnJvbS55LFxuXHRcdFx0XHRcdHdpZHRoOiBjaGlsZE9yaWdpbmFsLndpZHRoICogZmFjdG9yLmZyb20ueCxcblx0XHRcdFx0XHRvdXRlckhlaWdodDogY2hpbGRPcmlnaW5hbC5vdXRlckhlaWdodCAqIGZhY3Rvci5mcm9tLnksXG5cdFx0XHRcdFx0b3V0ZXJXaWR0aDogY2hpbGRPcmlnaW5hbC5vdXRlcldpZHRoICogZmFjdG9yLmZyb20ueFxuXHRcdFx0XHR9LFxuXHRcdFx0XHRjaGlsZFRvID0ge1xuXHRcdFx0XHRcdGhlaWdodDogY2hpbGRPcmlnaW5hbC5oZWlnaHQgKiBmYWN0b3IudG8ueSxcblx0XHRcdFx0XHR3aWR0aDogY2hpbGRPcmlnaW5hbC53aWR0aCAqIGZhY3Rvci50by54LFxuXHRcdFx0XHRcdG91dGVySGVpZ2h0OiBjaGlsZE9yaWdpbmFsLmhlaWdodCAqIGZhY3Rvci50by55LFxuXHRcdFx0XHRcdG91dGVyV2lkdGg6IGNoaWxkT3JpZ2luYWwud2lkdGggKiBmYWN0b3IudG8ueFxuXHRcdFx0XHR9O1xuXG5cdFx0XHQvLyBWZXJ0aWNhbCBwcm9wcyBzY2FsaW5nXG5cdFx0XHRpZiAoIGZhY3Rvci5mcm9tLnkgIT09IGZhY3Rvci50by55ICkge1xuXHRcdFx0XHRjaGlsZEZyb20gPSAkLmVmZmVjdHMuc2V0VHJhbnNpdGlvbiggY2hpbGQsIHZQcm9wcywgZmFjdG9yLmZyb20ueSwgY2hpbGRGcm9tICk7XG5cdFx0XHRcdGNoaWxkVG8gPSAkLmVmZmVjdHMuc2V0VHJhbnNpdGlvbiggY2hpbGQsIHZQcm9wcywgZmFjdG9yLnRvLnksIGNoaWxkVG8gKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gSG9yaXpvbnRhbCBwcm9wcyBzY2FsaW5nXG5cdFx0XHRpZiAoIGZhY3Rvci5mcm9tLnggIT09IGZhY3Rvci50by54ICkge1xuXHRcdFx0XHRjaGlsZEZyb20gPSAkLmVmZmVjdHMuc2V0VHJhbnNpdGlvbiggY2hpbGQsIGhQcm9wcywgZmFjdG9yLmZyb20ueCwgY2hpbGRGcm9tICk7XG5cdFx0XHRcdGNoaWxkVG8gPSAkLmVmZmVjdHMuc2V0VHJhbnNpdGlvbiggY2hpbGQsIGhQcm9wcywgZmFjdG9yLnRvLngsIGNoaWxkVG8gKTtcblx0XHRcdH1cblxuXHRcdFx0aWYgKCByZXN0b3JlICkge1xuXHRcdFx0XHQkLmVmZmVjdHMuc2F2ZVN0eWxlKCBjaGlsZCApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBBbmltYXRlIGNoaWxkcmVuXG5cdFx0XHRjaGlsZC5jc3MoIGNoaWxkRnJvbSApO1xuXHRcdFx0Y2hpbGQuYW5pbWF0ZSggY2hpbGRUbywgb3B0aW9ucy5kdXJhdGlvbiwgb3B0aW9ucy5lYXNpbmcsIGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRcdC8vIFJlc3RvcmUgY2hpbGRyZW5cblx0XHRcdFx0aWYgKCByZXN0b3JlICkge1xuXHRcdFx0XHRcdCQuZWZmZWN0cy5yZXN0b3JlU3R5bGUoIGNoaWxkICk7XG5cdFx0XHRcdH1cblx0XHRcdH0gKTtcblx0XHR9ICk7XG5cdH1cblxuXHQvLyBBbmltYXRlXG5cdGVsZW1lbnQuYW5pbWF0ZSggdG8sIHtcblx0XHRxdWV1ZTogZmFsc2UsXG5cdFx0ZHVyYXRpb246IG9wdGlvbnMuZHVyYXRpb24sXG5cdFx0ZWFzaW5nOiBvcHRpb25zLmVhc2luZyxcblx0XHRjb21wbGV0ZTogZnVuY3Rpb24oKSB7XG5cblx0XHRcdHZhciBvZmZzZXQgPSBlbGVtZW50Lm9mZnNldCgpO1xuXG5cdFx0XHRpZiAoIHRvLm9wYWNpdHkgPT09IDAgKSB7XG5cdFx0XHRcdGVsZW1lbnQuY3NzKCBcIm9wYWNpdHlcIiwgZnJvbS5vcGFjaXR5ICk7XG5cdFx0XHR9XG5cblx0XHRcdGlmICggIXJlc3RvcmUgKSB7XG5cdFx0XHRcdGVsZW1lbnRcblx0XHRcdFx0XHQuY3NzKCBcInBvc2l0aW9uXCIsIHBvc2l0aW9uID09PSBcInN0YXRpY1wiID8gXCJyZWxhdGl2ZVwiIDogcG9zaXRpb24gKVxuXHRcdFx0XHRcdC5vZmZzZXQoIG9mZnNldCApO1xuXG5cdFx0XHRcdC8vIE5lZWQgdG8gc2F2ZSBzdHlsZSBoZXJlIHNvIHRoYXQgYXV0b21hdGljIHN0eWxlIHJlc3RvcmF0aW9uXG5cdFx0XHRcdC8vIGRvZXNuJ3QgcmVzdG9yZSB0byB0aGUgb3JpZ2luYWwgc3R5bGVzIGZyb20gYmVmb3JlIHRoZSBhbmltYXRpb24uXG5cdFx0XHRcdCQuZWZmZWN0cy5zYXZlU3R5bGUoIGVsZW1lbnQgKTtcblx0XHRcdH1cblxuXHRcdFx0ZG9uZSgpO1xuXHRcdH1cblx0fSApO1xuXG59ICk7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgRWZmZWN0cyBTY2FsZSAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogU2NhbGUgRWZmZWN0XG4vLz4+Z3JvdXA6IEVmZmVjdHNcbi8vPj5kZXNjcmlwdGlvbjogR3Jvd3Mgb3Igc2hyaW5rcyBhbiBlbGVtZW50IGFuZCBpdHMgY29udGVudC5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9zY2FsZS1lZmZlY3QvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vZWZmZWN0L1xuXG5cblxudmFyIGVmZmVjdHNFZmZlY3RTY2FsZSA9ICQuZWZmZWN0cy5kZWZpbmUoIFwic2NhbGVcIiwgZnVuY3Rpb24oIG9wdGlvbnMsIGRvbmUgKSB7XG5cblx0Ly8gQ3JlYXRlIGVsZW1lbnRcblx0dmFyIGVsID0gJCggdGhpcyApLFxuXHRcdG1vZGUgPSBvcHRpb25zLm1vZGUsXG5cdFx0cGVyY2VudCA9IHBhcnNlSW50KCBvcHRpb25zLnBlcmNlbnQsIDEwICkgfHxcblx0XHRcdCggcGFyc2VJbnQoIG9wdGlvbnMucGVyY2VudCwgMTAgKSA9PT0gMCA/IDAgOiAoIG1vZGUgIT09IFwiZWZmZWN0XCIgPyAwIDogMTAwICkgKSxcblxuXHRcdG5ld09wdGlvbnMgPSAkLmV4dGVuZCggdHJ1ZSwge1xuXHRcdFx0ZnJvbTogJC5lZmZlY3RzLnNjYWxlZERpbWVuc2lvbnMoIGVsICksXG5cdFx0XHR0bzogJC5lZmZlY3RzLnNjYWxlZERpbWVuc2lvbnMoIGVsLCBwZXJjZW50LCBvcHRpb25zLmRpcmVjdGlvbiB8fCBcImJvdGhcIiApLFxuXHRcdFx0b3JpZ2luOiBvcHRpb25zLm9yaWdpbiB8fCBbIFwibWlkZGxlXCIsIFwiY2VudGVyXCIgXVxuXHRcdH0sIG9wdGlvbnMgKTtcblxuXHQvLyBGYWRlIG9wdGlvbiB0byBzdXBwb3J0IHB1ZmZcblx0aWYgKCBvcHRpb25zLmZhZGUgKSB7XG5cdFx0bmV3T3B0aW9ucy5mcm9tLm9wYWNpdHkgPSAxO1xuXHRcdG5ld09wdGlvbnMudG8ub3BhY2l0eSA9IDA7XG5cdH1cblxuXHQkLmVmZmVjdHMuZWZmZWN0LnNpemUuY2FsbCggdGhpcywgbmV3T3B0aW9ucywgZG9uZSApO1xufSApO1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIEVmZmVjdHMgUHVmZiAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogUHVmZiBFZmZlY3Rcbi8vPj5ncm91cDogRWZmZWN0c1xuLy8+PmRlc2NyaXB0aW9uOiBDcmVhdGVzIGEgcHVmZiBlZmZlY3QgYnkgc2NhbGluZyB0aGUgZWxlbWVudCB1cCBhbmQgaGlkaW5nIGl0IGF0IHRoZSBzYW1lIHRpbWUuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vcHVmZi1lZmZlY3QvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vZWZmZWN0L1xuXG5cblxudmFyIGVmZmVjdHNFZmZlY3RQdWZmID0gJC5lZmZlY3RzLmRlZmluZSggXCJwdWZmXCIsIFwiaGlkZVwiLCBmdW5jdGlvbiggb3B0aW9ucywgZG9uZSApIHtcblx0dmFyIG5ld09wdGlvbnMgPSAkLmV4dGVuZCggdHJ1ZSwge30sIG9wdGlvbnMsIHtcblx0XHRmYWRlOiB0cnVlLFxuXHRcdHBlcmNlbnQ6IHBhcnNlSW50KCBvcHRpb25zLnBlcmNlbnQsIDEwICkgfHwgMTUwXG5cdH0gKTtcblxuXHQkLmVmZmVjdHMuZWZmZWN0LnNjYWxlLmNhbGwoIHRoaXMsIG5ld09wdGlvbnMsIGRvbmUgKTtcbn0gKTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBFZmZlY3RzIFB1bHNhdGUgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IFB1bHNhdGUgRWZmZWN0XG4vLz4+Z3JvdXA6IEVmZmVjdHNcbi8vPj5kZXNjcmlwdGlvbjogUHVsc2F0ZXMgYW4gZWxlbWVudCBuIHRpbWVzIGJ5IGNoYW5naW5nIHRoZSBvcGFjaXR5IHRvIHplcm8gYW5kIGJhY2suXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vcHVsc2F0ZS1lZmZlY3QvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vZWZmZWN0L1xuXG5cblxudmFyIGVmZmVjdHNFZmZlY3RQdWxzYXRlID0gJC5lZmZlY3RzLmRlZmluZSggXCJwdWxzYXRlXCIsIFwic2hvd1wiLCBmdW5jdGlvbiggb3B0aW9ucywgZG9uZSApIHtcblx0dmFyIGVsZW1lbnQgPSAkKCB0aGlzICksXG5cdFx0bW9kZSA9IG9wdGlvbnMubW9kZSxcblx0XHRzaG93ID0gbW9kZSA9PT0gXCJzaG93XCIsXG5cdFx0aGlkZSA9IG1vZGUgPT09IFwiaGlkZVwiLFxuXHRcdHNob3doaWRlID0gc2hvdyB8fCBoaWRlLFxuXG5cdFx0Ly8gU2hvd2luZyBvciBoaWRpbmcgbGVhdmVzIG9mZiB0aGUgXCJsYXN0XCIgYW5pbWF0aW9uXG5cdFx0YW5pbXMgPSAoICggb3B0aW9ucy50aW1lcyB8fCA1ICkgKiAyICkgKyAoIHNob3doaWRlID8gMSA6IDAgKSxcblx0XHRkdXJhdGlvbiA9IG9wdGlvbnMuZHVyYXRpb24gLyBhbmltcyxcblx0XHRhbmltYXRlVG8gPSAwLFxuXHRcdGkgPSAxLFxuXHRcdHF1ZXVlbGVuID0gZWxlbWVudC5xdWV1ZSgpLmxlbmd0aDtcblxuXHRpZiAoIHNob3cgfHwgIWVsZW1lbnQuaXMoIFwiOnZpc2libGVcIiApICkge1xuXHRcdGVsZW1lbnQuY3NzKCBcIm9wYWNpdHlcIiwgMCApLnNob3coKTtcblx0XHRhbmltYXRlVG8gPSAxO1xuXHR9XG5cblx0Ly8gQW5pbXMgLSAxIG9wYWNpdHkgXCJ0b2dnbGVzXCJcblx0Zm9yICggOyBpIDwgYW5pbXM7IGkrKyApIHtcblx0XHRlbGVtZW50LmFuaW1hdGUoIHsgb3BhY2l0eTogYW5pbWF0ZVRvIH0sIGR1cmF0aW9uLCBvcHRpb25zLmVhc2luZyApO1xuXHRcdGFuaW1hdGVUbyA9IDEgLSBhbmltYXRlVG87XG5cdH1cblxuXHRlbGVtZW50LmFuaW1hdGUoIHsgb3BhY2l0eTogYW5pbWF0ZVRvIH0sIGR1cmF0aW9uLCBvcHRpb25zLmVhc2luZyApO1xuXG5cdGVsZW1lbnQucXVldWUoIGRvbmUgKTtcblxuXHQkLmVmZmVjdHMudW5zaGlmdCggZWxlbWVudCwgcXVldWVsZW4sIGFuaW1zICsgMSApO1xufSApO1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIEVmZmVjdHMgU2hha2UgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IFNoYWtlIEVmZmVjdFxuLy8+Pmdyb3VwOiBFZmZlY3RzXG4vLz4+ZGVzY3JpcHRpb246IFNoYWtlcyBhbiBlbGVtZW50IGhvcml6b250YWxseSBvciB2ZXJ0aWNhbGx5IG4gdGltZXMuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vc2hha2UtZWZmZWN0L1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL2VmZmVjdC9cblxuXG5cbnZhciBlZmZlY3RzRWZmZWN0U2hha2UgPSAkLmVmZmVjdHMuZGVmaW5lKCBcInNoYWtlXCIsIGZ1bmN0aW9uKCBvcHRpb25zLCBkb25lICkge1xuXG5cdHZhciBpID0gMSxcblx0XHRlbGVtZW50ID0gJCggdGhpcyApLFxuXHRcdGRpcmVjdGlvbiA9IG9wdGlvbnMuZGlyZWN0aW9uIHx8IFwibGVmdFwiLFxuXHRcdGRpc3RhbmNlID0gb3B0aW9ucy5kaXN0YW5jZSB8fCAyMCxcblx0XHR0aW1lcyA9IG9wdGlvbnMudGltZXMgfHwgMyxcblx0XHRhbmltcyA9IHRpbWVzICogMiArIDEsXG5cdFx0c3BlZWQgPSBNYXRoLnJvdW5kKCBvcHRpb25zLmR1cmF0aW9uIC8gYW5pbXMgKSxcblx0XHRyZWYgPSAoIGRpcmVjdGlvbiA9PT0gXCJ1cFwiIHx8IGRpcmVjdGlvbiA9PT0gXCJkb3duXCIgKSA/IFwidG9wXCIgOiBcImxlZnRcIixcblx0XHRwb3NpdGl2ZU1vdGlvbiA9ICggZGlyZWN0aW9uID09PSBcInVwXCIgfHwgZGlyZWN0aW9uID09PSBcImxlZnRcIiApLFxuXHRcdGFuaW1hdGlvbiA9IHt9LFxuXHRcdGFuaW1hdGlvbjEgPSB7fSxcblx0XHRhbmltYXRpb24yID0ge30sXG5cblx0XHRxdWV1ZWxlbiA9IGVsZW1lbnQucXVldWUoKS5sZW5ndGg7XG5cblx0JC5lZmZlY3RzLmNyZWF0ZVBsYWNlaG9sZGVyKCBlbGVtZW50ICk7XG5cblx0Ly8gQW5pbWF0aW9uXG5cdGFuaW1hdGlvblsgcmVmIF0gPSAoIHBvc2l0aXZlTW90aW9uID8gXCItPVwiIDogXCIrPVwiICkgKyBkaXN0YW5jZTtcblx0YW5pbWF0aW9uMVsgcmVmIF0gPSAoIHBvc2l0aXZlTW90aW9uID8gXCIrPVwiIDogXCItPVwiICkgKyBkaXN0YW5jZSAqIDI7XG5cdGFuaW1hdGlvbjJbIHJlZiBdID0gKCBwb3NpdGl2ZU1vdGlvbiA/IFwiLT1cIiA6IFwiKz1cIiApICsgZGlzdGFuY2UgKiAyO1xuXG5cdC8vIEFuaW1hdGVcblx0ZWxlbWVudC5hbmltYXRlKCBhbmltYXRpb24sIHNwZWVkLCBvcHRpb25zLmVhc2luZyApO1xuXG5cdC8vIFNoYWtlc1xuXHRmb3IgKCA7IGkgPCB0aW1lczsgaSsrICkge1xuXHRcdGVsZW1lbnRcblx0XHRcdC5hbmltYXRlKCBhbmltYXRpb24xLCBzcGVlZCwgb3B0aW9ucy5lYXNpbmcgKVxuXHRcdFx0LmFuaW1hdGUoIGFuaW1hdGlvbjIsIHNwZWVkLCBvcHRpb25zLmVhc2luZyApO1xuXHR9XG5cblx0ZWxlbWVudFxuXHRcdC5hbmltYXRlKCBhbmltYXRpb24xLCBzcGVlZCwgb3B0aW9ucy5lYXNpbmcgKVxuXHRcdC5hbmltYXRlKCBhbmltYXRpb24sIHNwZWVkIC8gMiwgb3B0aW9ucy5lYXNpbmcgKVxuXHRcdC5xdWV1ZSggZG9uZSApO1xuXG5cdCQuZWZmZWN0cy51bnNoaWZ0KCBlbGVtZW50LCBxdWV1ZWxlbiwgYW5pbXMgKyAxICk7XG59ICk7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgRWZmZWN0cyBTbGlkZSAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogU2xpZGUgRWZmZWN0XG4vLz4+Z3JvdXA6IEVmZmVjdHNcbi8vPj5kZXNjcmlwdGlvbjogU2xpZGVzIGFuIGVsZW1lbnQgaW4gYW5kIG91dCBvZiB0aGUgdmlld3BvcnQuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vc2xpZGUtZWZmZWN0L1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL2VmZmVjdC9cblxuXG5cbnZhciBlZmZlY3RzRWZmZWN0U2xpZGUgPSAkLmVmZmVjdHMuZGVmaW5lKCBcInNsaWRlXCIsIFwic2hvd1wiLCBmdW5jdGlvbiggb3B0aW9ucywgZG9uZSApIHtcblx0dmFyIHN0YXJ0Q2xpcCwgc3RhcnRSZWYsXG5cdFx0ZWxlbWVudCA9ICQoIHRoaXMgKSxcblx0XHRtYXAgPSB7XG5cdFx0XHR1cDogWyBcImJvdHRvbVwiLCBcInRvcFwiIF0sXG5cdFx0XHRkb3duOiBbIFwidG9wXCIsIFwiYm90dG9tXCIgXSxcblx0XHRcdGxlZnQ6IFsgXCJyaWdodFwiLCBcImxlZnRcIiBdLFxuXHRcdFx0cmlnaHQ6IFsgXCJsZWZ0XCIsIFwicmlnaHRcIiBdXG5cdFx0fSxcblx0XHRtb2RlID0gb3B0aW9ucy5tb2RlLFxuXHRcdGRpcmVjdGlvbiA9IG9wdGlvbnMuZGlyZWN0aW9uIHx8IFwibGVmdFwiLFxuXHRcdHJlZiA9ICggZGlyZWN0aW9uID09PSBcInVwXCIgfHwgZGlyZWN0aW9uID09PSBcImRvd25cIiApID8gXCJ0b3BcIiA6IFwibGVmdFwiLFxuXHRcdHBvc2l0aXZlTW90aW9uID0gKCBkaXJlY3Rpb24gPT09IFwidXBcIiB8fCBkaXJlY3Rpb24gPT09IFwibGVmdFwiICksXG5cdFx0ZGlzdGFuY2UgPSBvcHRpb25zLmRpc3RhbmNlIHx8XG5cdFx0XHRlbGVtZW50WyByZWYgPT09IFwidG9wXCIgPyBcIm91dGVySGVpZ2h0XCIgOiBcIm91dGVyV2lkdGhcIiBdKCB0cnVlICksXG5cdFx0YW5pbWF0aW9uID0ge307XG5cblx0JC5lZmZlY3RzLmNyZWF0ZVBsYWNlaG9sZGVyKCBlbGVtZW50ICk7XG5cblx0c3RhcnRDbGlwID0gZWxlbWVudC5jc3NDbGlwKCk7XG5cdHN0YXJ0UmVmID0gZWxlbWVudC5wb3NpdGlvbigpWyByZWYgXTtcblxuXHQvLyBEZWZpbmUgaGlkZSBhbmltYXRpb25cblx0YW5pbWF0aW9uWyByZWYgXSA9ICggcG9zaXRpdmVNb3Rpb24gPyAtMSA6IDEgKSAqIGRpc3RhbmNlICsgc3RhcnRSZWY7XG5cdGFuaW1hdGlvbi5jbGlwID0gZWxlbWVudC5jc3NDbGlwKCk7XG5cdGFuaW1hdGlvbi5jbGlwWyBtYXBbIGRpcmVjdGlvbiBdWyAxIF0gXSA9IGFuaW1hdGlvbi5jbGlwWyBtYXBbIGRpcmVjdGlvbiBdWyAwIF0gXTtcblxuXHQvLyBSZXZlcnNlIHRoZSBhbmltYXRpb24gaWYgd2UncmUgc2hvd2luZ1xuXHRpZiAoIG1vZGUgPT09IFwic2hvd1wiICkge1xuXHRcdGVsZW1lbnQuY3NzQ2xpcCggYW5pbWF0aW9uLmNsaXAgKTtcblx0XHRlbGVtZW50LmNzcyggcmVmLCBhbmltYXRpb25bIHJlZiBdICk7XG5cdFx0YW5pbWF0aW9uLmNsaXAgPSBzdGFydENsaXA7XG5cdFx0YW5pbWF0aW9uWyByZWYgXSA9IHN0YXJ0UmVmO1xuXHR9XG5cblx0Ly8gQWN0dWFsbHkgYW5pbWF0ZVxuXHRlbGVtZW50LmFuaW1hdGUoIGFuaW1hdGlvbiwge1xuXHRcdHF1ZXVlOiBmYWxzZSxcblx0XHRkdXJhdGlvbjogb3B0aW9ucy5kdXJhdGlvbixcblx0XHRlYXNpbmc6IG9wdGlvbnMuZWFzaW5nLFxuXHRcdGNvbXBsZXRlOiBkb25lXG5cdH0gKTtcbn0gKTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBFZmZlY3RzIFRyYW5zZmVyIDEuMTIuMVxuICogaHR0cDovL2pxdWVyeXVpLmNvbVxuICpcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzXG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UuXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKi9cblxuLy8+PmxhYmVsOiBUcmFuc2ZlciBFZmZlY3Rcbi8vPj5ncm91cDogRWZmZWN0c1xuLy8+PmRlc2NyaXB0aW9uOiBEaXNwbGF5cyBhIHRyYW5zZmVyIGVmZmVjdCBmcm9tIG9uZSBlbGVtZW50IHRvIGFub3RoZXIuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vdHJhbnNmZXItZWZmZWN0L1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL2VmZmVjdC9cblxuXG5cbnZhciBlZmZlY3Q7XG5pZiAoICQudWlCYWNrQ29tcGF0ICE9PSBmYWxzZSApIHtcblx0ZWZmZWN0ID0gJC5lZmZlY3RzLmRlZmluZSggXCJ0cmFuc2ZlclwiLCBmdW5jdGlvbiggb3B0aW9ucywgZG9uZSApIHtcblx0XHQkKCB0aGlzICkudHJhbnNmZXIoIG9wdGlvbnMsIGRvbmUgKTtcblx0fSApO1xufVxudmFyIGVmZmVjdHNFZmZlY3RUcmFuc2ZlciA9IGVmZmVjdDtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBGb2N1c2FibGUgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IDpmb2N1c2FibGUgU2VsZWN0b3Jcbi8vPj5ncm91cDogQ29yZVxuLy8+PmRlc2NyaXB0aW9uOiBTZWxlY3RzIGVsZW1lbnRzIHdoaWNoIGNhbiBiZSBmb2N1c2VkLlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2ZvY3VzYWJsZS1zZWxlY3Rvci9cblxuXG5cbi8vIFNlbGVjdG9yc1xuJC51aS5mb2N1c2FibGUgPSBmdW5jdGlvbiggZWxlbWVudCwgaGFzVGFiaW5kZXggKSB7XG5cdHZhciBtYXAsIG1hcE5hbWUsIGltZywgZm9jdXNhYmxlSWZWaXNpYmxlLCBmaWVsZHNldCxcblx0XHRub2RlTmFtZSA9IGVsZW1lbnQubm9kZU5hbWUudG9Mb3dlckNhc2UoKTtcblxuXHRpZiAoIFwiYXJlYVwiID09PSBub2RlTmFtZSApIHtcblx0XHRtYXAgPSBlbGVtZW50LnBhcmVudE5vZGU7XG5cdFx0bWFwTmFtZSA9IG1hcC5uYW1lO1xuXHRcdGlmICggIWVsZW1lbnQuaHJlZiB8fCAhbWFwTmFtZSB8fCBtYXAubm9kZU5hbWUudG9Mb3dlckNhc2UoKSAhPT0gXCJtYXBcIiApIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdFx0aW1nID0gJCggXCJpbWdbdXNlbWFwPScjXCIgKyBtYXBOYW1lICsgXCInXVwiICk7XG5cdFx0cmV0dXJuIGltZy5sZW5ndGggPiAwICYmIGltZy5pcyggXCI6dmlzaWJsZVwiICk7XG5cdH1cblxuXHRpZiAoIC9eKGlucHV0fHNlbGVjdHx0ZXh0YXJlYXxidXR0b258b2JqZWN0KSQvLnRlc3QoIG5vZGVOYW1lICkgKSB7XG5cdFx0Zm9jdXNhYmxlSWZWaXNpYmxlID0gIWVsZW1lbnQuZGlzYWJsZWQ7XG5cblx0XHRpZiAoIGZvY3VzYWJsZUlmVmlzaWJsZSApIHtcblxuXHRcdFx0Ly8gRm9ybSBjb250cm9scyB3aXRoaW4gYSBkaXNhYmxlZCBmaWVsZHNldCBhcmUgZGlzYWJsZWQuXG5cdFx0XHQvLyBIb3dldmVyLCBjb250cm9scyB3aXRoaW4gdGhlIGZpZWxkc2V0J3MgbGVnZW5kIGRvIG5vdCBnZXQgZGlzYWJsZWQuXG5cdFx0XHQvLyBTaW5jZSBjb250cm9scyBnZW5lcmFsbHkgYXJlbid0IHBsYWNlZCBpbnNpZGUgbGVnZW5kcywgd2Ugc2tpcFxuXHRcdFx0Ly8gdGhpcyBwb3J0aW9uIG9mIHRoZSBjaGVjay5cblx0XHRcdGZpZWxkc2V0ID0gJCggZWxlbWVudCApLmNsb3Nlc3QoIFwiZmllbGRzZXRcIiApWyAwIF07XG5cdFx0XHRpZiAoIGZpZWxkc2V0ICkge1xuXHRcdFx0XHRmb2N1c2FibGVJZlZpc2libGUgPSAhZmllbGRzZXQuZGlzYWJsZWQ7XG5cdFx0XHR9XG5cdFx0fVxuXHR9IGVsc2UgaWYgKCBcImFcIiA9PT0gbm9kZU5hbWUgKSB7XG5cdFx0Zm9jdXNhYmxlSWZWaXNpYmxlID0gZWxlbWVudC5ocmVmIHx8IGhhc1RhYmluZGV4O1xuXHR9IGVsc2Uge1xuXHRcdGZvY3VzYWJsZUlmVmlzaWJsZSA9IGhhc1RhYmluZGV4O1xuXHR9XG5cblx0cmV0dXJuIGZvY3VzYWJsZUlmVmlzaWJsZSAmJiAkKCBlbGVtZW50ICkuaXMoIFwiOnZpc2libGVcIiApICYmIHZpc2libGUoICQoIGVsZW1lbnQgKSApO1xufTtcblxuLy8gU3VwcG9ydDogSUUgOCBvbmx5XG4vLyBJRSA4IGRvZXNuJ3QgcmVzb2x2ZSBpbmhlcml0IHRvIHZpc2libGUvaGlkZGVuIGZvciBjb21wdXRlZCB2YWx1ZXNcbmZ1bmN0aW9uIHZpc2libGUoIGVsZW1lbnQgKSB7XG5cdHZhciB2aXNpYmlsaXR5ID0gZWxlbWVudC5jc3MoIFwidmlzaWJpbGl0eVwiICk7XG5cdHdoaWxlICggdmlzaWJpbGl0eSA9PT0gXCJpbmhlcml0XCIgKSB7XG5cdFx0ZWxlbWVudCA9IGVsZW1lbnQucGFyZW50KCk7XG5cdFx0dmlzaWJpbGl0eSA9IGVsZW1lbnQuY3NzKCBcInZpc2liaWxpdHlcIiApO1xuXHR9XG5cdHJldHVybiB2aXNpYmlsaXR5ICE9PSBcImhpZGRlblwiO1xufVxuXG4kLmV4dGVuZCggJC5leHByWyBcIjpcIiBdLCB7XG5cdGZvY3VzYWJsZTogZnVuY3Rpb24oIGVsZW1lbnQgKSB7XG5cdFx0cmV0dXJuICQudWkuZm9jdXNhYmxlKCBlbGVtZW50LCAkLmF0dHIoIGVsZW1lbnQsIFwidGFiaW5kZXhcIiApICE9IG51bGwgKTtcblx0fVxufSApO1xuXG52YXIgZm9jdXNhYmxlID0gJC51aS5mb2N1c2FibGU7XG5cblxuXG5cbi8vIFN1cHBvcnQ6IElFOCBPbmx5XG4vLyBJRTggZG9lcyBub3Qgc3VwcG9ydCB0aGUgZm9ybSBhdHRyaWJ1dGUgYW5kIHdoZW4gaXQgaXMgc3VwcGxpZWQuIEl0IG92ZXJ3cml0ZXMgdGhlIGZvcm0gcHJvcFxuLy8gd2l0aCBhIHN0cmluZywgc28gd2UgbmVlZCB0byBmaW5kIHRoZSBwcm9wZXIgZm9ybS5cbnZhciBmb3JtID0gJC5mbi5mb3JtID0gZnVuY3Rpb24oKSB7XG5cdHJldHVybiB0eXBlb2YgdGhpc1sgMCBdLmZvcm0gPT09IFwic3RyaW5nXCIgPyB0aGlzLmNsb3Nlc3QoIFwiZm9ybVwiICkgOiAkKCB0aGlzWyAwIF0uZm9ybSApO1xufTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBGb3JtIFJlc2V0IE1peGluIDEuMTIuMVxuICogaHR0cDovL2pxdWVyeXVpLmNvbVxuICpcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzXG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UuXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKi9cblxuLy8+PmxhYmVsOiBGb3JtIFJlc2V0IE1peGluXG4vLz4+Z3JvdXA6IENvcmVcbi8vPj5kZXNjcmlwdGlvbjogUmVmcmVzaCBpbnB1dCB3aWRnZXRzIHdoZW4gdGhlaXIgZm9ybSBpcyByZXNldFxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2Zvcm0tcmVzZXQtbWl4aW4vXG5cblxuXG52YXIgZm9ybVJlc2V0TWl4aW4gPSAkLnVpLmZvcm1SZXNldE1peGluID0ge1xuXHRfZm9ybVJlc2V0SGFuZGxlcjogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGZvcm0gPSAkKCB0aGlzICk7XG5cblx0XHQvLyBXYWl0IGZvciB0aGUgZm9ybSByZXNldCB0byBhY3R1YWxseSBoYXBwZW4gYmVmb3JlIHJlZnJlc2hpbmdcblx0XHRzZXRUaW1lb3V0KCBmdW5jdGlvbigpIHtcblx0XHRcdHZhciBpbnN0YW5jZXMgPSBmb3JtLmRhdGEoIFwidWktZm9ybS1yZXNldC1pbnN0YW5jZXNcIiApO1xuXHRcdFx0JC5lYWNoKCBpbnN0YW5jZXMsIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHR0aGlzLnJlZnJlc2goKTtcblx0XHRcdH0gKTtcblx0XHR9ICk7XG5cdH0sXG5cblx0X2JpbmRGb3JtUmVzZXRIYW5kbGVyOiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLmZvcm0gPSB0aGlzLmVsZW1lbnQuZm9ybSgpO1xuXHRcdGlmICggIXRoaXMuZm9ybS5sZW5ndGggKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dmFyIGluc3RhbmNlcyA9IHRoaXMuZm9ybS5kYXRhKCBcInVpLWZvcm0tcmVzZXQtaW5zdGFuY2VzXCIgKSB8fCBbXTtcblx0XHRpZiAoICFpbnN0YW5jZXMubGVuZ3RoICkge1xuXG5cdFx0XHQvLyBXZSBkb24ndCB1c2UgX29uKCkgaGVyZSBiZWNhdXNlIHdlIHVzZSBhIHNpbmdsZSBldmVudCBoYW5kbGVyIHBlciBmb3JtXG5cdFx0XHR0aGlzLmZvcm0ub24oIFwicmVzZXQudWktZm9ybS1yZXNldFwiLCB0aGlzLl9mb3JtUmVzZXRIYW5kbGVyICk7XG5cdFx0fVxuXHRcdGluc3RhbmNlcy5wdXNoKCB0aGlzICk7XG5cdFx0dGhpcy5mb3JtLmRhdGEoIFwidWktZm9ybS1yZXNldC1pbnN0YW5jZXNcIiwgaW5zdGFuY2VzICk7XG5cdH0sXG5cblx0X3VuYmluZEZvcm1SZXNldEhhbmRsZXI6IGZ1bmN0aW9uKCkge1xuXHRcdGlmICggIXRoaXMuZm9ybS5sZW5ndGggKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dmFyIGluc3RhbmNlcyA9IHRoaXMuZm9ybS5kYXRhKCBcInVpLWZvcm0tcmVzZXQtaW5zdGFuY2VzXCIgKTtcblx0XHRpbnN0YW5jZXMuc3BsaWNlKCAkLmluQXJyYXkoIHRoaXMsIGluc3RhbmNlcyApLCAxICk7XG5cdFx0aWYgKCBpbnN0YW5jZXMubGVuZ3RoICkge1xuXHRcdFx0dGhpcy5mb3JtLmRhdGEoIFwidWktZm9ybS1yZXNldC1pbnN0YW5jZXNcIiwgaW5zdGFuY2VzICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuZm9ybVxuXHRcdFx0XHQucmVtb3ZlRGF0YSggXCJ1aS1mb3JtLXJlc2V0LWluc3RhbmNlc1wiIClcblx0XHRcdFx0Lm9mZiggXCJyZXNldC51aS1mb3JtLXJlc2V0XCIgKTtcblx0XHR9XG5cdH1cbn07XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgU3VwcG9ydCBmb3IgalF1ZXJ5IGNvcmUgMS43LnggMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqXG4gKi9cblxuLy8+PmxhYmVsOiBqUXVlcnkgMS43IFN1cHBvcnRcbi8vPj5ncm91cDogQ29yZVxuLy8+PmRlc2NyaXB0aW9uOiBTdXBwb3J0IHZlcnNpb24gMS43Lnggb2YgalF1ZXJ5IGNvcmVcblxuXG5cbi8vIFN1cHBvcnQ6IGpRdWVyeSAxLjcgb25seVxuLy8gTm90IGEgZ3JlYXQgd2F5IHRvIGNoZWNrIHZlcnNpb25zLCBidXQgc2luY2Ugd2Ugb25seSBzdXBwb3J0IDEuNysgYW5kIG9ubHlcbi8vIG5lZWQgdG8gZGV0ZWN0IDwxLjgsIHRoaXMgaXMgYSBzaW1wbGUgY2hlY2sgdGhhdCBzaG91bGQgc3VmZmljZS4gQ2hlY2tpbmdcbi8vIGZvciBcIjEuNy5cIiB3b3VsZCBiZSBhIGJpdCBzYWZlciwgYnV0IHRoZSB2ZXJzaW9uIHN0cmluZyBpcyAxLjcsIG5vdCAxLjcuMFxuLy8gYW5kIHdlJ2xsIG5ldmVyIHJlYWNoIDEuNzAuMCAoaWYgd2UgZG8sIHdlIGNlcnRhaW5seSB3b24ndCBiZSBzdXBwb3J0aW5nXG4vLyAxLjcgYW55bW9yZSkuIFNlZSAjMTExOTcgZm9yIHdoeSB3ZSdyZSBub3QgdXNpbmcgZmVhdHVyZSBkZXRlY3Rpb24uXG5pZiAoICQuZm4uanF1ZXJ5LnN1YnN0cmluZyggMCwgMyApID09PSBcIjEuN1wiICkge1xuXG5cdC8vIFNldHRlcnMgZm9yIC5pbm5lcldpZHRoKCksIC5pbm5lckhlaWdodCgpLCAub3V0ZXJXaWR0aCgpLCAub3V0ZXJIZWlnaHQoKVxuXHQvLyBVbmxpa2UgalF1ZXJ5IENvcmUgMS44KywgdGhlc2Ugb25seSBzdXBwb3J0IG51bWVyaWMgdmFsdWVzIHRvIHNldCB0aGVcblx0Ly8gZGltZW5zaW9ucyBpbiBwaXhlbHNcblx0JC5lYWNoKCBbIFwiV2lkdGhcIiwgXCJIZWlnaHRcIiBdLCBmdW5jdGlvbiggaSwgbmFtZSApIHtcblx0XHR2YXIgc2lkZSA9IG5hbWUgPT09IFwiV2lkdGhcIiA/IFsgXCJMZWZ0XCIsIFwiUmlnaHRcIiBdIDogWyBcIlRvcFwiLCBcIkJvdHRvbVwiIF0sXG5cdFx0XHR0eXBlID0gbmFtZS50b0xvd2VyQ2FzZSgpLFxuXHRcdFx0b3JpZyA9IHtcblx0XHRcdFx0aW5uZXJXaWR0aDogJC5mbi5pbm5lcldpZHRoLFxuXHRcdFx0XHRpbm5lckhlaWdodDogJC5mbi5pbm5lckhlaWdodCxcblx0XHRcdFx0b3V0ZXJXaWR0aDogJC5mbi5vdXRlcldpZHRoLFxuXHRcdFx0XHRvdXRlckhlaWdodDogJC5mbi5vdXRlckhlaWdodFxuXHRcdFx0fTtcblxuXHRcdGZ1bmN0aW9uIHJlZHVjZSggZWxlbSwgc2l6ZSwgYm9yZGVyLCBtYXJnaW4gKSB7XG5cdFx0XHQkLmVhY2goIHNpZGUsIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRzaXplIC09IHBhcnNlRmxvYXQoICQuY3NzKCBlbGVtLCBcInBhZGRpbmdcIiArIHRoaXMgKSApIHx8IDA7XG5cdFx0XHRcdGlmICggYm9yZGVyICkge1xuXHRcdFx0XHRcdHNpemUgLT0gcGFyc2VGbG9hdCggJC5jc3MoIGVsZW0sIFwiYm9yZGVyXCIgKyB0aGlzICsgXCJXaWR0aFwiICkgKSB8fCAwO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmICggbWFyZ2luICkge1xuXHRcdFx0XHRcdHNpemUgLT0gcGFyc2VGbG9hdCggJC5jc3MoIGVsZW0sIFwibWFyZ2luXCIgKyB0aGlzICkgKSB8fCAwO1xuXHRcdFx0XHR9XG5cdFx0XHR9ICk7XG5cdFx0XHRyZXR1cm4gc2l6ZTtcblx0XHR9XG5cblx0XHQkLmZuWyBcImlubmVyXCIgKyBuYW1lIF0gPSBmdW5jdGlvbiggc2l6ZSApIHtcblx0XHRcdGlmICggc2l6ZSA9PT0gdW5kZWZpbmVkICkge1xuXHRcdFx0XHRyZXR1cm4gb3JpZ1sgXCJpbm5lclwiICsgbmFtZSBdLmNhbGwoIHRoaXMgKTtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdCQoIHRoaXMgKS5jc3MoIHR5cGUsIHJlZHVjZSggdGhpcywgc2l6ZSApICsgXCJweFwiICk7XG5cdFx0XHR9ICk7XG5cdFx0fTtcblxuXHRcdCQuZm5bIFwib3V0ZXJcIiArIG5hbWUgXSA9IGZ1bmN0aW9uKCBzaXplLCBtYXJnaW4gKSB7XG5cdFx0XHRpZiAoIHR5cGVvZiBzaXplICE9PSBcIm51bWJlclwiICkge1xuXHRcdFx0XHRyZXR1cm4gb3JpZ1sgXCJvdXRlclwiICsgbmFtZSBdLmNhbGwoIHRoaXMsIHNpemUgKTtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHRoaXMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdCQoIHRoaXMgKS5jc3MoIHR5cGUsIHJlZHVjZSggdGhpcywgc2l6ZSwgdHJ1ZSwgbWFyZ2luICkgKyBcInB4XCIgKTtcblx0XHRcdH0gKTtcblx0XHR9O1xuXHR9ICk7XG5cblx0JC5mbi5hZGRCYWNrID0gZnVuY3Rpb24oIHNlbGVjdG9yICkge1xuXHRcdHJldHVybiB0aGlzLmFkZCggc2VsZWN0b3IgPT0gbnVsbCA/XG5cdFx0XHR0aGlzLnByZXZPYmplY3QgOiB0aGlzLnByZXZPYmplY3QuZmlsdGVyKCBzZWxlY3RvciApXG5cdFx0KTtcblx0fTtcbn1cblxuO1xuLyohXG4gKiBqUXVlcnkgVUkgS2V5Y29kZSAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogS2V5Y29kZVxuLy8+Pmdyb3VwOiBDb3JlXG4vLz4+ZGVzY3JpcHRpb246IFByb3ZpZGUga2V5Y29kZXMgYXMga2V5bmFtZXNcbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9qUXVlcnkudWkua2V5Q29kZS9cblxuXG52YXIga2V5Y29kZSA9ICQudWkua2V5Q29kZSA9IHtcblx0QkFDS1NQQUNFOiA4LFxuXHRDT01NQTogMTg4LFxuXHRERUxFVEU6IDQ2LFxuXHRET1dOOiA0MCxcblx0RU5EOiAzNSxcblx0RU5URVI6IDEzLFxuXHRFU0NBUEU6IDI3LFxuXHRIT01FOiAzNixcblx0TEVGVDogMzcsXG5cdFBBR0VfRE9XTjogMzQsXG5cdFBBR0VfVVA6IDMzLFxuXHRQRVJJT0Q6IDE5MCxcblx0UklHSFQ6IDM5LFxuXHRTUEFDRTogMzIsXG5cdFRBQjogOSxcblx0VVA6IDM4XG59O1xuXG5cblxuXG4vLyBJbnRlcm5hbCB1c2Ugb25seVxudmFyIGVzY2FwZVNlbGVjdG9yID0gJC51aS5lc2NhcGVTZWxlY3RvciA9ICggZnVuY3Rpb24oKSB7XG5cdHZhciBzZWxlY3RvckVzY2FwZSA9IC8oWyFcIiMkJSYnKCkqKywuLzo7PD0+P0BbXFxdXmB7fH1+XSkvZztcblx0cmV0dXJuIGZ1bmN0aW9uKCBzZWxlY3RvciApIHtcblx0XHRyZXR1cm4gc2VsZWN0b3IucmVwbGFjZSggc2VsZWN0b3JFc2NhcGUsIFwiXFxcXCQxXCIgKTtcblx0fTtcbn0gKSgpO1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIExhYmVscyAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogbGFiZWxzXG4vLz4+Z3JvdXA6IENvcmVcbi8vPj5kZXNjcmlwdGlvbjogRmluZCBhbGwgdGhlIGxhYmVscyBhc3NvY2lhdGVkIHdpdGggYSBnaXZlbiBpbnB1dFxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2xhYmVscy9cblxuXG5cbnZhciBsYWJlbHMgPSAkLmZuLmxhYmVscyA9IGZ1bmN0aW9uKCkge1xuXHR2YXIgYW5jZXN0b3IsIHNlbGVjdG9yLCBpZCwgbGFiZWxzLCBhbmNlc3RvcnM7XG5cblx0Ly8gQ2hlY2sgY29udHJvbC5sYWJlbHMgZmlyc3Rcblx0aWYgKCB0aGlzWyAwIF0ubGFiZWxzICYmIHRoaXNbIDAgXS5sYWJlbHMubGVuZ3RoICkge1xuXHRcdHJldHVybiB0aGlzLnB1c2hTdGFjayggdGhpc1sgMCBdLmxhYmVscyApO1xuXHR9XG5cblx0Ly8gU3VwcG9ydDogSUUgPD0gMTEsIEZGIDw9IDM3LCBBbmRyb2lkIDw9IDIuMyBvbmx5XG5cdC8vIEFib3ZlIGJyb3dzZXJzIGRvIG5vdCBzdXBwb3J0IGNvbnRyb2wubGFiZWxzLiBFdmVyeXRoaW5nIGJlbG93IGlzIHRvIHN1cHBvcnQgdGhlbVxuXHQvLyBhcyB3ZWxsIGFzIGRvY3VtZW50IGZyYWdtZW50cy4gY29udHJvbC5sYWJlbHMgZG9lcyBub3Qgd29yayBvbiBkb2N1bWVudCBmcmFnbWVudHNcblx0bGFiZWxzID0gdGhpcy5lcSggMCApLnBhcmVudHMoIFwibGFiZWxcIiApO1xuXG5cdC8vIExvb2sgZm9yIHRoZSBsYWJlbCBiYXNlZCBvbiB0aGUgaWRcblx0aWQgPSB0aGlzLmF0dHIoIFwiaWRcIiApO1xuXHRpZiAoIGlkICkge1xuXG5cdFx0Ly8gV2UgZG9uJ3Qgc2VhcmNoIGFnYWluc3QgdGhlIGRvY3VtZW50IGluIGNhc2UgdGhlIGVsZW1lbnRcblx0XHQvLyBpcyBkaXNjb25uZWN0ZWQgZnJvbSB0aGUgRE9NXG5cdFx0YW5jZXN0b3IgPSB0aGlzLmVxKCAwICkucGFyZW50cygpLmxhc3QoKTtcblxuXHRcdC8vIEdldCBhIGZ1bGwgc2V0IG9mIHRvcCBsZXZlbCBhbmNlc3RvcnNcblx0XHRhbmNlc3RvcnMgPSBhbmNlc3Rvci5hZGQoIGFuY2VzdG9yLmxlbmd0aCA/IGFuY2VzdG9yLnNpYmxpbmdzKCkgOiB0aGlzLnNpYmxpbmdzKCkgKTtcblxuXHRcdC8vIENyZWF0ZSBhIHNlbGVjdG9yIGZvciB0aGUgbGFiZWwgYmFzZWQgb24gdGhlIGlkXG5cdFx0c2VsZWN0b3IgPSBcImxhYmVsW2Zvcj0nXCIgKyAkLnVpLmVzY2FwZVNlbGVjdG9yKCBpZCApICsgXCInXVwiO1xuXG5cdFx0bGFiZWxzID0gbGFiZWxzLmFkZCggYW5jZXN0b3JzLmZpbmQoIHNlbGVjdG9yICkuYWRkQmFjayggc2VsZWN0b3IgKSApO1xuXG5cdH1cblxuXHQvLyBSZXR1cm4gd2hhdGV2ZXIgd2UgaGF2ZSBmb3VuZCBmb3IgbGFiZWxzXG5cdHJldHVybiB0aGlzLnB1c2hTdGFjayggbGFiZWxzICk7XG59O1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIFNjcm9sbCBQYXJlbnQgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IHNjcm9sbFBhcmVudFxuLy8+Pmdyb3VwOiBDb3JlXG4vLz4+ZGVzY3JpcHRpb246IEdldCB0aGUgY2xvc2VzdCBhbmNlc3RvciBlbGVtZW50IHRoYXQgaXMgc2Nyb2xsYWJsZS5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9zY3JvbGxQYXJlbnQvXG5cblxuXG52YXIgc2Nyb2xsUGFyZW50ID0gJC5mbi5zY3JvbGxQYXJlbnQgPSBmdW5jdGlvbiggaW5jbHVkZUhpZGRlbiApIHtcblx0dmFyIHBvc2l0aW9uID0gdGhpcy5jc3MoIFwicG9zaXRpb25cIiApLFxuXHRcdGV4Y2x1ZGVTdGF0aWNQYXJlbnQgPSBwb3NpdGlvbiA9PT0gXCJhYnNvbHV0ZVwiLFxuXHRcdG92ZXJmbG93UmVnZXggPSBpbmNsdWRlSGlkZGVuID8gLyhhdXRvfHNjcm9sbHxoaWRkZW4pLyA6IC8oYXV0b3xzY3JvbGwpLyxcblx0XHRzY3JvbGxQYXJlbnQgPSB0aGlzLnBhcmVudHMoKS5maWx0ZXIoIGZ1bmN0aW9uKCkge1xuXHRcdFx0dmFyIHBhcmVudCA9ICQoIHRoaXMgKTtcblx0XHRcdGlmICggZXhjbHVkZVN0YXRpY1BhcmVudCAmJiBwYXJlbnQuY3NzKCBcInBvc2l0aW9uXCIgKSA9PT0gXCJzdGF0aWNcIiApIHtcblx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIG92ZXJmbG93UmVnZXgudGVzdCggcGFyZW50LmNzcyggXCJvdmVyZmxvd1wiICkgKyBwYXJlbnQuY3NzKCBcIm92ZXJmbG93LXlcIiApICtcblx0XHRcdFx0cGFyZW50LmNzcyggXCJvdmVyZmxvdy14XCIgKSApO1xuXHRcdH0gKS5lcSggMCApO1xuXG5cdHJldHVybiBwb3NpdGlvbiA9PT0gXCJmaXhlZFwiIHx8ICFzY3JvbGxQYXJlbnQubGVuZ3RoID9cblx0XHQkKCB0aGlzWyAwIF0ub3duZXJEb2N1bWVudCB8fCBkb2N1bWVudCApIDpcblx0XHRzY3JvbGxQYXJlbnQ7XG59O1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIFRhYmJhYmxlIDEuMTIuMVxuICogaHR0cDovL2pxdWVyeXVpLmNvbVxuICpcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzXG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UuXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKi9cblxuLy8+PmxhYmVsOiA6dGFiYmFibGUgU2VsZWN0b3Jcbi8vPj5ncm91cDogQ29yZVxuLy8+PmRlc2NyaXB0aW9uOiBTZWxlY3RzIGVsZW1lbnRzIHdoaWNoIGNhbiBiZSB0YWJiZWQgdG8uXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vdGFiYmFibGUtc2VsZWN0b3IvXG5cblxuXG52YXIgdGFiYmFibGUgPSAkLmV4dGVuZCggJC5leHByWyBcIjpcIiBdLCB7XG5cdHRhYmJhYmxlOiBmdW5jdGlvbiggZWxlbWVudCApIHtcblx0XHR2YXIgdGFiSW5kZXggPSAkLmF0dHIoIGVsZW1lbnQsIFwidGFiaW5kZXhcIiApLFxuXHRcdFx0aGFzVGFiaW5kZXggPSB0YWJJbmRleCAhPSBudWxsO1xuXHRcdHJldHVybiAoICFoYXNUYWJpbmRleCB8fCB0YWJJbmRleCA+PSAwICkgJiYgJC51aS5mb2N1c2FibGUoIGVsZW1lbnQsIGhhc1RhYmluZGV4ICk7XG5cdH1cbn0gKTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBVbmlxdWUgSUQgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IHVuaXF1ZUlkXG4vLz4+Z3JvdXA6IENvcmVcbi8vPj5kZXNjcmlwdGlvbjogRnVuY3Rpb25zIHRvIGdlbmVyYXRlIGFuZCByZW1vdmUgdW5pcXVlSWQnc1xuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL3VuaXF1ZUlkL1xuXG5cblxudmFyIHVuaXF1ZUlkID0gJC5mbi5leHRlbmQoIHtcblx0dW5pcXVlSWQ6ICggZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHV1aWQgPSAwO1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uKCkge1xuXHRcdFx0cmV0dXJuIHRoaXMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGlmICggIXRoaXMuaWQgKSB7XG5cdFx0XHRcdFx0dGhpcy5pZCA9IFwidWktaWQtXCIgKyAoICsrdXVpZCApO1xuXHRcdFx0XHR9XG5cdFx0XHR9ICk7XG5cdFx0fTtcblx0fSApKCksXG5cblx0cmVtb3ZlVW5pcXVlSWQ6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0aWYgKCAvXnVpLWlkLVxcZCskLy50ZXN0KCB0aGlzLmlkICkgKSB7XG5cdFx0XHRcdCQoIHRoaXMgKS5yZW1vdmVBdHRyKCBcImlkXCIgKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdH1cbn0gKTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBBY2NvcmRpb24gMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IEFjY29yZGlvblxuLy8+Pmdyb3VwOiBXaWRnZXRzXG4vLyBqc2NzOmRpc2FibGUgbWF4aW11bUxpbmVMZW5ndGhcbi8vPj5kZXNjcmlwdGlvbjogRGlzcGxheXMgY29sbGFwc2libGUgY29udGVudCBwYW5lbHMgZm9yIHByZXNlbnRpbmcgaW5mb3JtYXRpb24gaW4gYSBsaW1pdGVkIGFtb3VudCBvZiBzcGFjZS5cbi8vIGpzY3M6ZW5hYmxlIG1heGltdW1MaW5lTGVuZ3RoXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vYWNjb3JkaW9uL1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL2FjY29yZGlvbi9cbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9jb3JlLmNzc1xuLy8+PmNzcy5zdHJ1Y3R1cmU6IC4uLy4uL3RoZW1lcy9iYXNlL2FjY29yZGlvbi5jc3Ncbi8vPj5jc3MudGhlbWU6IC4uLy4uL3RoZW1lcy9iYXNlL3RoZW1lLmNzc1xuXG5cblxudmFyIHdpZGdldHNBY2NvcmRpb24gPSAkLndpZGdldCggXCJ1aS5hY2NvcmRpb25cIiwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHRvcHRpb25zOiB7XG5cdFx0YWN0aXZlOiAwLFxuXHRcdGFuaW1hdGU6IHt9LFxuXHRcdGNsYXNzZXM6IHtcblx0XHRcdFwidWktYWNjb3JkaW9uLWhlYWRlclwiOiBcInVpLWNvcm5lci10b3BcIixcblx0XHRcdFwidWktYWNjb3JkaW9uLWhlYWRlci1jb2xsYXBzZWRcIjogXCJ1aS1jb3JuZXItYWxsXCIsXG5cdFx0XHRcInVpLWFjY29yZGlvbi1jb250ZW50XCI6IFwidWktY29ybmVyLWJvdHRvbVwiXG5cdFx0fSxcblx0XHRjb2xsYXBzaWJsZTogZmFsc2UsXG5cdFx0ZXZlbnQ6IFwiY2xpY2tcIixcblx0XHRoZWFkZXI6IFwiPiBsaSA+IDpmaXJzdC1jaGlsZCwgPiA6bm90KGxpKTpldmVuXCIsXG5cdFx0aGVpZ2h0U3R5bGU6IFwiYXV0b1wiLFxuXHRcdGljb25zOiB7XG5cdFx0XHRhY3RpdmVIZWFkZXI6IFwidWktaWNvbi10cmlhbmdsZS0xLXNcIixcblx0XHRcdGhlYWRlcjogXCJ1aS1pY29uLXRyaWFuZ2xlLTEtZVwiXG5cdFx0fSxcblxuXHRcdC8vIENhbGxiYWNrc1xuXHRcdGFjdGl2YXRlOiBudWxsLFxuXHRcdGJlZm9yZUFjdGl2YXRlOiBudWxsXG5cdH0sXG5cblx0aGlkZVByb3BzOiB7XG5cdFx0Ym9yZGVyVG9wV2lkdGg6IFwiaGlkZVwiLFxuXHRcdGJvcmRlckJvdHRvbVdpZHRoOiBcImhpZGVcIixcblx0XHRwYWRkaW5nVG9wOiBcImhpZGVcIixcblx0XHRwYWRkaW5nQm90dG9tOiBcImhpZGVcIixcblx0XHRoZWlnaHQ6IFwiaGlkZVwiXG5cdH0sXG5cblx0c2hvd1Byb3BzOiB7XG5cdFx0Ym9yZGVyVG9wV2lkdGg6IFwic2hvd1wiLFxuXHRcdGJvcmRlckJvdHRvbVdpZHRoOiBcInNob3dcIixcblx0XHRwYWRkaW5nVG9wOiBcInNob3dcIixcblx0XHRwYWRkaW5nQm90dG9tOiBcInNob3dcIixcblx0XHRoZWlnaHQ6IFwic2hvd1wiXG5cdH0sXG5cblx0X2NyZWF0ZTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XG5cblx0XHR0aGlzLnByZXZTaG93ID0gdGhpcy5wcmV2SGlkZSA9ICQoKTtcblx0XHR0aGlzLl9hZGRDbGFzcyggXCJ1aS1hY2NvcmRpb25cIiwgXCJ1aS13aWRnZXQgdWktaGVscGVyLXJlc2V0XCIgKTtcblx0XHR0aGlzLmVsZW1lbnQuYXR0ciggXCJyb2xlXCIsIFwidGFibGlzdFwiICk7XG5cblx0XHQvLyBEb24ndCBhbGxvdyBjb2xsYXBzaWJsZTogZmFsc2UgYW5kIGFjdGl2ZTogZmFsc2UgLyBudWxsXG5cdFx0aWYgKCAhb3B0aW9ucy5jb2xsYXBzaWJsZSAmJiAoIG9wdGlvbnMuYWN0aXZlID09PSBmYWxzZSB8fCBvcHRpb25zLmFjdGl2ZSA9PSBudWxsICkgKSB7XG5cdFx0XHRvcHRpb25zLmFjdGl2ZSA9IDA7XG5cdFx0fVxuXG5cdFx0dGhpcy5fcHJvY2Vzc1BhbmVscygpO1xuXG5cdFx0Ly8gaGFuZGxlIG5lZ2F0aXZlIHZhbHVlc1xuXHRcdGlmICggb3B0aW9ucy5hY3RpdmUgPCAwICkge1xuXHRcdFx0b3B0aW9ucy5hY3RpdmUgKz0gdGhpcy5oZWFkZXJzLmxlbmd0aDtcblx0XHR9XG5cdFx0dGhpcy5fcmVmcmVzaCgpO1xuXHR9LFxuXG5cdF9nZXRDcmVhdGVFdmVudERhdGE6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB7XG5cdFx0XHRoZWFkZXI6IHRoaXMuYWN0aXZlLFxuXHRcdFx0cGFuZWw6ICF0aGlzLmFjdGl2ZS5sZW5ndGggPyAkKCkgOiB0aGlzLmFjdGl2ZS5uZXh0KClcblx0XHR9O1xuXHR9LFxuXG5cdF9jcmVhdGVJY29uczogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGljb24sIGNoaWxkcmVuLFxuXHRcdFx0aWNvbnMgPSB0aGlzLm9wdGlvbnMuaWNvbnM7XG5cblx0XHRpZiAoIGljb25zICkge1xuXHRcdFx0aWNvbiA9ICQoIFwiPHNwYW4+XCIgKTtcblx0XHRcdHRoaXMuX2FkZENsYXNzKCBpY29uLCBcInVpLWFjY29yZGlvbi1oZWFkZXItaWNvblwiLCBcInVpLWljb24gXCIgKyBpY29ucy5oZWFkZXIgKTtcblx0XHRcdGljb24ucHJlcGVuZFRvKCB0aGlzLmhlYWRlcnMgKTtcblx0XHRcdGNoaWxkcmVuID0gdGhpcy5hY3RpdmUuY2hpbGRyZW4oIFwiLnVpLWFjY29yZGlvbi1oZWFkZXItaWNvblwiICk7XG5cdFx0XHR0aGlzLl9yZW1vdmVDbGFzcyggY2hpbGRyZW4sIGljb25zLmhlYWRlciApXG5cdFx0XHRcdC5fYWRkQ2xhc3MoIGNoaWxkcmVuLCBudWxsLCBpY29ucy5hY3RpdmVIZWFkZXIgKVxuXHRcdFx0XHQuX2FkZENsYXNzKCB0aGlzLmhlYWRlcnMsIFwidWktYWNjb3JkaW9uLWljb25zXCIgKTtcblx0XHR9XG5cdH0sXG5cblx0X2Rlc3Ryb3lJY29uczogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIHRoaXMuaGVhZGVycywgXCJ1aS1hY2NvcmRpb24taWNvbnNcIiApO1xuXHRcdHRoaXMuaGVhZGVycy5jaGlsZHJlbiggXCIudWktYWNjb3JkaW9uLWhlYWRlci1pY29uXCIgKS5yZW1vdmUoKTtcblx0fSxcblxuXHRfZGVzdHJveTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGNvbnRlbnRzO1xuXG5cdFx0Ly8gQ2xlYW4gdXAgbWFpbiBlbGVtZW50XG5cdFx0dGhpcy5lbGVtZW50LnJlbW92ZUF0dHIoIFwicm9sZVwiICk7XG5cblx0XHQvLyBDbGVhbiB1cCBoZWFkZXJzXG5cdFx0dGhpcy5oZWFkZXJzXG5cdFx0XHQucmVtb3ZlQXR0ciggXCJyb2xlIGFyaWEtZXhwYW5kZWQgYXJpYS1zZWxlY3RlZCBhcmlhLWNvbnRyb2xzIHRhYkluZGV4XCIgKVxuXHRcdFx0LnJlbW92ZVVuaXF1ZUlkKCk7XG5cblx0XHR0aGlzLl9kZXN0cm95SWNvbnMoKTtcblxuXHRcdC8vIENsZWFuIHVwIGNvbnRlbnQgcGFuZWxzXG5cdFx0Y29udGVudHMgPSB0aGlzLmhlYWRlcnMubmV4dCgpXG5cdFx0XHQuY3NzKCBcImRpc3BsYXlcIiwgXCJcIiApXG5cdFx0XHQucmVtb3ZlQXR0ciggXCJyb2xlIGFyaWEtaGlkZGVuIGFyaWEtbGFiZWxsZWRieVwiIClcblx0XHRcdC5yZW1vdmVVbmlxdWVJZCgpO1xuXG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMuaGVpZ2h0U3R5bGUgIT09IFwiY29udGVudFwiICkge1xuXHRcdFx0Y29udGVudHMuY3NzKCBcImhlaWdodFwiLCBcIlwiICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRPcHRpb246IGZ1bmN0aW9uKCBrZXksIHZhbHVlICkge1xuXHRcdGlmICgga2V5ID09PSBcImFjdGl2ZVwiICkge1xuXG5cdFx0XHQvLyBfYWN0aXZhdGUoKSB3aWxsIGhhbmRsZSBpbnZhbGlkIHZhbHVlcyBhbmQgdXBkYXRlIHRoaXMub3B0aW9uc1xuXHRcdFx0dGhpcy5fYWN0aXZhdGUoIHZhbHVlICk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aWYgKCBrZXkgPT09IFwiZXZlbnRcIiApIHtcblx0XHRcdGlmICggdGhpcy5vcHRpb25zLmV2ZW50ICkge1xuXHRcdFx0XHR0aGlzLl9vZmYoIHRoaXMuaGVhZGVycywgdGhpcy5vcHRpb25zLmV2ZW50ICk7XG5cdFx0XHR9XG5cdFx0XHR0aGlzLl9zZXR1cEV2ZW50cyggdmFsdWUgKTtcblx0XHR9XG5cblx0XHR0aGlzLl9zdXBlcigga2V5LCB2YWx1ZSApO1xuXG5cdFx0Ly8gU2V0dGluZyBjb2xsYXBzaWJsZTogZmFsc2Ugd2hpbGUgY29sbGFwc2VkOyBvcGVuIGZpcnN0IHBhbmVsXG5cdFx0aWYgKCBrZXkgPT09IFwiY29sbGFwc2libGVcIiAmJiAhdmFsdWUgJiYgdGhpcy5vcHRpb25zLmFjdGl2ZSA9PT0gZmFsc2UgKSB7XG5cdFx0XHR0aGlzLl9hY3RpdmF0ZSggMCApO1xuXHRcdH1cblxuXHRcdGlmICgga2V5ID09PSBcImljb25zXCIgKSB7XG5cdFx0XHR0aGlzLl9kZXN0cm95SWNvbnMoKTtcblx0XHRcdGlmICggdmFsdWUgKSB7XG5cdFx0XHRcdHRoaXMuX2NyZWF0ZUljb25zKCk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRPcHRpb25EaXNhYmxlZDogZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdHRoaXMuX3N1cGVyKCB2YWx1ZSApO1xuXG5cdFx0dGhpcy5lbGVtZW50LmF0dHIoIFwiYXJpYS1kaXNhYmxlZFwiLCB2YWx1ZSApO1xuXG5cdFx0Ly8gU3VwcG9ydDogSUU4IE9ubHlcblx0XHQvLyAjNTMzMiAvICM2MDU5IC0gb3BhY2l0eSBkb2Vzbid0IGNhc2NhZGUgdG8gcG9zaXRpb25lZCBlbGVtZW50cyBpbiBJRVxuXHRcdC8vIHNvIHdlIG5lZWQgdG8gYWRkIHRoZSBkaXNhYmxlZCBjbGFzcyB0byB0aGUgaGVhZGVycyBhbmQgcGFuZWxzXG5cdFx0dGhpcy5fdG9nZ2xlQ2xhc3MoIG51bGwsIFwidWktc3RhdGUtZGlzYWJsZWRcIiwgISF2YWx1ZSApO1xuXHRcdHRoaXMuX3RvZ2dsZUNsYXNzKCB0aGlzLmhlYWRlcnMuYWRkKCB0aGlzLmhlYWRlcnMubmV4dCgpICksIG51bGwsIFwidWktc3RhdGUtZGlzYWJsZWRcIixcblx0XHRcdCEhdmFsdWUgKTtcblx0fSxcblxuXHRfa2V5ZG93bjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdGlmICggZXZlbnQuYWx0S2V5IHx8IGV2ZW50LmN0cmxLZXkgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dmFyIGtleUNvZGUgPSAkLnVpLmtleUNvZGUsXG5cdFx0XHRsZW5ndGggPSB0aGlzLmhlYWRlcnMubGVuZ3RoLFxuXHRcdFx0Y3VycmVudEluZGV4ID0gdGhpcy5oZWFkZXJzLmluZGV4KCBldmVudC50YXJnZXQgKSxcblx0XHRcdHRvRm9jdXMgPSBmYWxzZTtcblxuXHRcdHN3aXRjaCAoIGV2ZW50LmtleUNvZGUgKSB7XG5cdFx0Y2FzZSBrZXlDb2RlLlJJR0hUOlxuXHRcdGNhc2Uga2V5Q29kZS5ET1dOOlxuXHRcdFx0dG9Gb2N1cyA9IHRoaXMuaGVhZGVyc1sgKCBjdXJyZW50SW5kZXggKyAxICkgJSBsZW5ndGggXTtcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2Uga2V5Q29kZS5MRUZUOlxuXHRcdGNhc2Uga2V5Q29kZS5VUDpcblx0XHRcdHRvRm9jdXMgPSB0aGlzLmhlYWRlcnNbICggY3VycmVudEluZGV4IC0gMSArIGxlbmd0aCApICUgbGVuZ3RoIF07XG5cdFx0XHRicmVhaztcblx0XHRjYXNlIGtleUNvZGUuU1BBQ0U6XG5cdFx0Y2FzZSBrZXlDb2RlLkVOVEVSOlxuXHRcdFx0dGhpcy5fZXZlbnRIYW5kbGVyKCBldmVudCApO1xuXHRcdFx0YnJlYWs7XG5cdFx0Y2FzZSBrZXlDb2RlLkhPTUU6XG5cdFx0XHR0b0ZvY3VzID0gdGhpcy5oZWFkZXJzWyAwIF07XG5cdFx0XHRicmVhaztcblx0XHRjYXNlIGtleUNvZGUuRU5EOlxuXHRcdFx0dG9Gb2N1cyA9IHRoaXMuaGVhZGVyc1sgbGVuZ3RoIC0gMSBdO1xuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXG5cdFx0aWYgKCB0b0ZvY3VzICkge1xuXHRcdFx0JCggZXZlbnQudGFyZ2V0ICkuYXR0ciggXCJ0YWJJbmRleFwiLCAtMSApO1xuXHRcdFx0JCggdG9Gb2N1cyApLmF0dHIoIFwidGFiSW5kZXhcIiwgMCApO1xuXHRcdFx0JCggdG9Gb2N1cyApLnRyaWdnZXIoIFwiZm9jdXNcIiApO1xuXHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHR9XG5cdH0sXG5cblx0X3BhbmVsS2V5RG93bjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdGlmICggZXZlbnQua2V5Q29kZSA9PT0gJC51aS5rZXlDb2RlLlVQICYmIGV2ZW50LmN0cmxLZXkgKSB7XG5cdFx0XHQkKCBldmVudC5jdXJyZW50VGFyZ2V0ICkucHJldigpLnRyaWdnZXIoIFwiZm9jdXNcIiApO1xuXHRcdH1cblx0fSxcblxuXHRyZWZyZXNoOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMub3B0aW9ucztcblx0XHR0aGlzLl9wcm9jZXNzUGFuZWxzKCk7XG5cblx0XHQvLyBXYXMgY29sbGFwc2VkIG9yIG5vIHBhbmVsXG5cdFx0aWYgKCAoIG9wdGlvbnMuYWN0aXZlID09PSBmYWxzZSAmJiBvcHRpb25zLmNvbGxhcHNpYmxlID09PSB0cnVlICkgfHxcblx0XHRcdFx0IXRoaXMuaGVhZGVycy5sZW5ndGggKSB7XG5cdFx0XHRvcHRpb25zLmFjdGl2ZSA9IGZhbHNlO1xuXHRcdFx0dGhpcy5hY3RpdmUgPSAkKCk7XG5cblx0XHQvLyBhY3RpdmUgZmFsc2Ugb25seSB3aGVuIGNvbGxhcHNpYmxlIGlzIHRydWVcblx0XHR9IGVsc2UgaWYgKCBvcHRpb25zLmFjdGl2ZSA9PT0gZmFsc2UgKSB7XG5cdFx0XHR0aGlzLl9hY3RpdmF0ZSggMCApO1xuXG5cdFx0Ly8gd2FzIGFjdGl2ZSwgYnV0IGFjdGl2ZSBwYW5lbCBpcyBnb25lXG5cdFx0fSBlbHNlIGlmICggdGhpcy5hY3RpdmUubGVuZ3RoICYmICEkLmNvbnRhaW5zKCB0aGlzLmVsZW1lbnRbIDAgXSwgdGhpcy5hY3RpdmVbIDAgXSApICkge1xuXG5cdFx0XHQvLyBhbGwgcmVtYWluaW5nIHBhbmVsIGFyZSBkaXNhYmxlZFxuXHRcdFx0aWYgKCB0aGlzLmhlYWRlcnMubGVuZ3RoID09PSB0aGlzLmhlYWRlcnMuZmluZCggXCIudWktc3RhdGUtZGlzYWJsZWRcIiApLmxlbmd0aCApIHtcblx0XHRcdFx0b3B0aW9ucy5hY3RpdmUgPSBmYWxzZTtcblx0XHRcdFx0dGhpcy5hY3RpdmUgPSAkKCk7XG5cblx0XHRcdC8vIGFjdGl2YXRlIHByZXZpb3VzIHBhbmVsXG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0aGlzLl9hY3RpdmF0ZSggTWF0aC5tYXgoIDAsIG9wdGlvbnMuYWN0aXZlIC0gMSApICk7XG5cdFx0XHR9XG5cblx0XHQvLyB3YXMgYWN0aXZlLCBhY3RpdmUgcGFuZWwgc3RpbGwgZXhpc3RzXG5cdFx0fSBlbHNlIHtcblxuXHRcdFx0Ly8gbWFrZSBzdXJlIGFjdGl2ZSBpbmRleCBpcyBjb3JyZWN0XG5cdFx0XHRvcHRpb25zLmFjdGl2ZSA9IHRoaXMuaGVhZGVycy5pbmRleCggdGhpcy5hY3RpdmUgKTtcblx0XHR9XG5cblx0XHR0aGlzLl9kZXN0cm95SWNvbnMoKTtcblxuXHRcdHRoaXMuX3JlZnJlc2goKTtcblx0fSxcblxuXHRfcHJvY2Vzc1BhbmVsczogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHByZXZIZWFkZXJzID0gdGhpcy5oZWFkZXJzLFxuXHRcdFx0cHJldlBhbmVscyA9IHRoaXMucGFuZWxzO1xuXG5cdFx0dGhpcy5oZWFkZXJzID0gdGhpcy5lbGVtZW50LmZpbmQoIHRoaXMub3B0aW9ucy5oZWFkZXIgKTtcblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5oZWFkZXJzLCBcInVpLWFjY29yZGlvbi1oZWFkZXIgdWktYWNjb3JkaW9uLWhlYWRlci1jb2xsYXBzZWRcIixcblx0XHRcdFwidWktc3RhdGUtZGVmYXVsdFwiICk7XG5cblx0XHR0aGlzLnBhbmVscyA9IHRoaXMuaGVhZGVycy5uZXh0KCkuZmlsdGVyKCBcIjpub3QoLnVpLWFjY29yZGlvbi1jb250ZW50LWFjdGl2ZSlcIiApLmhpZGUoKTtcblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5wYW5lbHMsIFwidWktYWNjb3JkaW9uLWNvbnRlbnRcIiwgXCJ1aS1oZWxwZXItcmVzZXQgdWktd2lkZ2V0LWNvbnRlbnRcIiApO1xuXG5cdFx0Ly8gQXZvaWQgbWVtb3J5IGxlYWtzICgjMTAwNTYpXG5cdFx0aWYgKCBwcmV2UGFuZWxzICkge1xuXHRcdFx0dGhpcy5fb2ZmKCBwcmV2SGVhZGVycy5ub3QoIHRoaXMuaGVhZGVycyApICk7XG5cdFx0XHR0aGlzLl9vZmYoIHByZXZQYW5lbHMubm90KCB0aGlzLnBhbmVscyApICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZWZyZXNoOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgbWF4SGVpZ2h0LFxuXHRcdFx0b3B0aW9ucyA9IHRoaXMub3B0aW9ucyxcblx0XHRcdGhlaWdodFN0eWxlID0gb3B0aW9ucy5oZWlnaHRTdHlsZSxcblx0XHRcdHBhcmVudCA9IHRoaXMuZWxlbWVudC5wYXJlbnQoKTtcblxuXHRcdHRoaXMuYWN0aXZlID0gdGhpcy5fZmluZEFjdGl2ZSggb3B0aW9ucy5hY3RpdmUgKTtcblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5hY3RpdmUsIFwidWktYWNjb3JkaW9uLWhlYWRlci1hY3RpdmVcIiwgXCJ1aS1zdGF0ZS1hY3RpdmVcIiApXG5cdFx0XHQuX3JlbW92ZUNsYXNzKCB0aGlzLmFjdGl2ZSwgXCJ1aS1hY2NvcmRpb24taGVhZGVyLWNvbGxhcHNlZFwiICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMuYWN0aXZlLm5leHQoKSwgXCJ1aS1hY2NvcmRpb24tY29udGVudC1hY3RpdmVcIiApO1xuXHRcdHRoaXMuYWN0aXZlLm5leHQoKS5zaG93KCk7XG5cblx0XHR0aGlzLmhlYWRlcnNcblx0XHRcdC5hdHRyKCBcInJvbGVcIiwgXCJ0YWJcIiApXG5cdFx0XHQuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHZhciBoZWFkZXIgPSAkKCB0aGlzICksXG5cdFx0XHRcdFx0aGVhZGVySWQgPSBoZWFkZXIudW5pcXVlSWQoKS5hdHRyKCBcImlkXCIgKSxcblx0XHRcdFx0XHRwYW5lbCA9IGhlYWRlci5uZXh0KCksXG5cdFx0XHRcdFx0cGFuZWxJZCA9IHBhbmVsLnVuaXF1ZUlkKCkuYXR0ciggXCJpZFwiICk7XG5cdFx0XHRcdGhlYWRlci5hdHRyKCBcImFyaWEtY29udHJvbHNcIiwgcGFuZWxJZCApO1xuXHRcdFx0XHRwYW5lbC5hdHRyKCBcImFyaWEtbGFiZWxsZWRieVwiLCBoZWFkZXJJZCApO1xuXHRcdFx0fSApXG5cdFx0XHQubmV4dCgpXG5cdFx0XHRcdC5hdHRyKCBcInJvbGVcIiwgXCJ0YWJwYW5lbFwiICk7XG5cblx0XHR0aGlzLmhlYWRlcnNcblx0XHRcdC5ub3QoIHRoaXMuYWN0aXZlIClcblx0XHRcdFx0LmF0dHIoIHtcblx0XHRcdFx0XHRcImFyaWEtc2VsZWN0ZWRcIjogXCJmYWxzZVwiLFxuXHRcdFx0XHRcdFwiYXJpYS1leHBhbmRlZFwiOiBcImZhbHNlXCIsXG5cdFx0XHRcdFx0dGFiSW5kZXg6IC0xXG5cdFx0XHRcdH0gKVxuXHRcdFx0XHQubmV4dCgpXG5cdFx0XHRcdFx0LmF0dHIoIHtcblx0XHRcdFx0XHRcdFwiYXJpYS1oaWRkZW5cIjogXCJ0cnVlXCJcblx0XHRcdFx0XHR9IClcblx0XHRcdFx0XHQuaGlkZSgpO1xuXG5cdFx0Ly8gTWFrZSBzdXJlIGF0IGxlYXN0IG9uZSBoZWFkZXIgaXMgaW4gdGhlIHRhYiBvcmRlclxuXHRcdGlmICggIXRoaXMuYWN0aXZlLmxlbmd0aCApIHtcblx0XHRcdHRoaXMuaGVhZGVycy5lcSggMCApLmF0dHIoIFwidGFiSW5kZXhcIiwgMCApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLmFjdGl2ZS5hdHRyKCB7XG5cdFx0XHRcdFwiYXJpYS1zZWxlY3RlZFwiOiBcInRydWVcIixcblx0XHRcdFx0XCJhcmlhLWV4cGFuZGVkXCI6IFwidHJ1ZVwiLFxuXHRcdFx0XHR0YWJJbmRleDogMFxuXHRcdFx0fSApXG5cdFx0XHRcdC5uZXh0KClcblx0XHRcdFx0XHQuYXR0cigge1xuXHRcdFx0XHRcdFx0XCJhcmlhLWhpZGRlblwiOiBcImZhbHNlXCJcblx0XHRcdFx0XHR9ICk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fY3JlYXRlSWNvbnMoKTtcblxuXHRcdHRoaXMuX3NldHVwRXZlbnRzKCBvcHRpb25zLmV2ZW50ICk7XG5cblx0XHRpZiAoIGhlaWdodFN0eWxlID09PSBcImZpbGxcIiApIHtcblx0XHRcdG1heEhlaWdodCA9IHBhcmVudC5oZWlnaHQoKTtcblx0XHRcdHRoaXMuZWxlbWVudC5zaWJsaW5ncyggXCI6dmlzaWJsZVwiICkuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHZhciBlbGVtID0gJCggdGhpcyApLFxuXHRcdFx0XHRcdHBvc2l0aW9uID0gZWxlbS5jc3MoIFwicG9zaXRpb25cIiApO1xuXG5cdFx0XHRcdGlmICggcG9zaXRpb24gPT09IFwiYWJzb2x1dGVcIiB8fCBwb3NpdGlvbiA9PT0gXCJmaXhlZFwiICkge1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXHRcdFx0XHRtYXhIZWlnaHQgLT0gZWxlbS5vdXRlckhlaWdodCggdHJ1ZSApO1xuXHRcdFx0fSApO1xuXG5cdFx0XHR0aGlzLmhlYWRlcnMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdG1heEhlaWdodCAtPSAkKCB0aGlzICkub3V0ZXJIZWlnaHQoIHRydWUgKTtcblx0XHRcdH0gKTtcblxuXHRcdFx0dGhpcy5oZWFkZXJzLm5leHQoKVxuXHRcdFx0XHQuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0JCggdGhpcyApLmhlaWdodCggTWF0aC5tYXgoIDAsIG1heEhlaWdodCAtXG5cdFx0XHRcdFx0XHQkKCB0aGlzICkuaW5uZXJIZWlnaHQoKSArICQoIHRoaXMgKS5oZWlnaHQoKSApICk7XG5cdFx0XHRcdH0gKVxuXHRcdFx0XHQuY3NzKCBcIm92ZXJmbG93XCIsIFwiYXV0b1wiICk7XG5cdFx0fSBlbHNlIGlmICggaGVpZ2h0U3R5bGUgPT09IFwiYXV0b1wiICkge1xuXHRcdFx0bWF4SGVpZ2h0ID0gMDtcblx0XHRcdHRoaXMuaGVhZGVycy5uZXh0KClcblx0XHRcdFx0LmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdHZhciBpc1Zpc2libGUgPSAkKCB0aGlzICkuaXMoIFwiOnZpc2libGVcIiApO1xuXHRcdFx0XHRcdGlmICggIWlzVmlzaWJsZSApIHtcblx0XHRcdFx0XHRcdCQoIHRoaXMgKS5zaG93KCk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdG1heEhlaWdodCA9IE1hdGgubWF4KCBtYXhIZWlnaHQsICQoIHRoaXMgKS5jc3MoIFwiaGVpZ2h0XCIsIFwiXCIgKS5oZWlnaHQoKSApO1xuXHRcdFx0XHRcdGlmICggIWlzVmlzaWJsZSApIHtcblx0XHRcdFx0XHRcdCQoIHRoaXMgKS5oaWRlKCk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9IClcblx0XHRcdFx0LmhlaWdodCggbWF4SGVpZ2h0ICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9hY3RpdmF0ZTogZnVuY3Rpb24oIGluZGV4ICkge1xuXHRcdHZhciBhY3RpdmUgPSB0aGlzLl9maW5kQWN0aXZlKCBpbmRleCApWyAwIF07XG5cblx0XHQvLyBUcnlpbmcgdG8gYWN0aXZhdGUgdGhlIGFscmVhZHkgYWN0aXZlIHBhbmVsXG5cdFx0aWYgKCBhY3RpdmUgPT09IHRoaXMuYWN0aXZlWyAwIF0gKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gVHJ5aW5nIHRvIGNvbGxhcHNlLCBzaW11bGF0ZSBhIGNsaWNrIG9uIHRoZSBjdXJyZW50bHkgYWN0aXZlIGhlYWRlclxuXHRcdGFjdGl2ZSA9IGFjdGl2ZSB8fCB0aGlzLmFjdGl2ZVsgMCBdO1xuXG5cdFx0dGhpcy5fZXZlbnRIYW5kbGVyKCB7XG5cdFx0XHR0YXJnZXQ6IGFjdGl2ZSxcblx0XHRcdGN1cnJlbnRUYXJnZXQ6IGFjdGl2ZSxcblx0XHRcdHByZXZlbnREZWZhdWx0OiAkLm5vb3Bcblx0XHR9ICk7XG5cdH0sXG5cblx0X2ZpbmRBY3RpdmU6IGZ1bmN0aW9uKCBzZWxlY3RvciApIHtcblx0XHRyZXR1cm4gdHlwZW9mIHNlbGVjdG9yID09PSBcIm51bWJlclwiID8gdGhpcy5oZWFkZXJzLmVxKCBzZWxlY3RvciApIDogJCgpO1xuXHR9LFxuXG5cdF9zZXR1cEV2ZW50czogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHZhciBldmVudHMgPSB7XG5cdFx0XHRrZXlkb3duOiBcIl9rZXlkb3duXCJcblx0XHR9O1xuXHRcdGlmICggZXZlbnQgKSB7XG5cdFx0XHQkLmVhY2goIGV2ZW50LnNwbGl0KCBcIiBcIiApLCBmdW5jdGlvbiggaW5kZXgsIGV2ZW50TmFtZSApIHtcblx0XHRcdFx0ZXZlbnRzWyBldmVudE5hbWUgXSA9IFwiX2V2ZW50SGFuZGxlclwiO1xuXHRcdFx0fSApO1xuXHRcdH1cblxuXHRcdHRoaXMuX29mZiggdGhpcy5oZWFkZXJzLmFkZCggdGhpcy5oZWFkZXJzLm5leHQoKSApICk7XG5cdFx0dGhpcy5fb24oIHRoaXMuaGVhZGVycywgZXZlbnRzICk7XG5cdFx0dGhpcy5fb24oIHRoaXMuaGVhZGVycy5uZXh0KCksIHsga2V5ZG93bjogXCJfcGFuZWxLZXlEb3duXCIgfSApO1xuXHRcdHRoaXMuX2hvdmVyYWJsZSggdGhpcy5oZWFkZXJzICk7XG5cdFx0dGhpcy5fZm9jdXNhYmxlKCB0aGlzLmhlYWRlcnMgKTtcblx0fSxcblxuXHRfZXZlbnRIYW5kbGVyOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dmFyIGFjdGl2ZUNoaWxkcmVuLCBjbGlja2VkQ2hpbGRyZW4sXG5cdFx0XHRvcHRpb25zID0gdGhpcy5vcHRpb25zLFxuXHRcdFx0YWN0aXZlID0gdGhpcy5hY3RpdmUsXG5cdFx0XHRjbGlja2VkID0gJCggZXZlbnQuY3VycmVudFRhcmdldCApLFxuXHRcdFx0Y2xpY2tlZElzQWN0aXZlID0gY2xpY2tlZFsgMCBdID09PSBhY3RpdmVbIDAgXSxcblx0XHRcdGNvbGxhcHNpbmcgPSBjbGlja2VkSXNBY3RpdmUgJiYgb3B0aW9ucy5jb2xsYXBzaWJsZSxcblx0XHRcdHRvU2hvdyA9IGNvbGxhcHNpbmcgPyAkKCkgOiBjbGlja2VkLm5leHQoKSxcblx0XHRcdHRvSGlkZSA9IGFjdGl2ZS5uZXh0KCksXG5cdFx0XHRldmVudERhdGEgPSB7XG5cdFx0XHRcdG9sZEhlYWRlcjogYWN0aXZlLFxuXHRcdFx0XHRvbGRQYW5lbDogdG9IaWRlLFxuXHRcdFx0XHRuZXdIZWFkZXI6IGNvbGxhcHNpbmcgPyAkKCkgOiBjbGlja2VkLFxuXHRcdFx0XHRuZXdQYW5lbDogdG9TaG93XG5cdFx0XHR9O1xuXG5cdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblxuXHRcdGlmIChcblxuXHRcdFx0XHQvLyBjbGljayBvbiBhY3RpdmUgaGVhZGVyLCBidXQgbm90IGNvbGxhcHNpYmxlXG5cdFx0XHRcdCggY2xpY2tlZElzQWN0aXZlICYmICFvcHRpb25zLmNvbGxhcHNpYmxlICkgfHxcblxuXHRcdFx0XHQvLyBhbGxvdyBjYW5jZWxpbmcgYWN0aXZhdGlvblxuXHRcdFx0XHQoIHRoaXMuX3RyaWdnZXIoIFwiYmVmb3JlQWN0aXZhdGVcIiwgZXZlbnQsIGV2ZW50RGF0YSApID09PSBmYWxzZSApICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdG9wdGlvbnMuYWN0aXZlID0gY29sbGFwc2luZyA/IGZhbHNlIDogdGhpcy5oZWFkZXJzLmluZGV4KCBjbGlja2VkICk7XG5cblx0XHQvLyBXaGVuIHRoZSBjYWxsIHRvIC5fdG9nZ2xlKCkgY29tZXMgYWZ0ZXIgdGhlIGNsYXNzIGNoYW5nZXNcblx0XHQvLyBpdCBjYXVzZXMgYSB2ZXJ5IG9kZCBidWcgaW4gSUUgOCAoc2VlICM2NzIwKVxuXHRcdHRoaXMuYWN0aXZlID0gY2xpY2tlZElzQWN0aXZlID8gJCgpIDogY2xpY2tlZDtcblx0XHR0aGlzLl90b2dnbGUoIGV2ZW50RGF0YSApO1xuXG5cdFx0Ly8gU3dpdGNoIGNsYXNzZXNcblx0XHQvLyBjb3JuZXIgY2xhc3NlcyBvbiB0aGUgcHJldmlvdXNseSBhY3RpdmUgaGVhZGVyIHN0YXkgYWZ0ZXIgdGhlIGFuaW1hdGlvblxuXHRcdHRoaXMuX3JlbW92ZUNsYXNzKCBhY3RpdmUsIFwidWktYWNjb3JkaW9uLWhlYWRlci1hY3RpdmVcIiwgXCJ1aS1zdGF0ZS1hY3RpdmVcIiApO1xuXHRcdGlmICggb3B0aW9ucy5pY29ucyApIHtcblx0XHRcdGFjdGl2ZUNoaWxkcmVuID0gYWN0aXZlLmNoaWxkcmVuKCBcIi51aS1hY2NvcmRpb24taGVhZGVyLWljb25cIiApO1xuXHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIGFjdGl2ZUNoaWxkcmVuLCBudWxsLCBvcHRpb25zLmljb25zLmFjdGl2ZUhlYWRlciApXG5cdFx0XHRcdC5fYWRkQ2xhc3MoIGFjdGl2ZUNoaWxkcmVuLCBudWxsLCBvcHRpb25zLmljb25zLmhlYWRlciApO1xuXHRcdH1cblxuXHRcdGlmICggIWNsaWNrZWRJc0FjdGl2ZSApIHtcblx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCBjbGlja2VkLCBcInVpLWFjY29yZGlvbi1oZWFkZXItY29sbGFwc2VkXCIgKVxuXHRcdFx0XHQuX2FkZENsYXNzKCBjbGlja2VkLCBcInVpLWFjY29yZGlvbi1oZWFkZXItYWN0aXZlXCIsIFwidWktc3RhdGUtYWN0aXZlXCIgKTtcblx0XHRcdGlmICggb3B0aW9ucy5pY29ucyApIHtcblx0XHRcdFx0Y2xpY2tlZENoaWxkcmVuID0gY2xpY2tlZC5jaGlsZHJlbiggXCIudWktYWNjb3JkaW9uLWhlYWRlci1pY29uXCIgKTtcblx0XHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIGNsaWNrZWRDaGlsZHJlbiwgbnVsbCwgb3B0aW9ucy5pY29ucy5oZWFkZXIgKVxuXHRcdFx0XHRcdC5fYWRkQ2xhc3MoIGNsaWNrZWRDaGlsZHJlbiwgbnVsbCwgb3B0aW9ucy5pY29ucy5hY3RpdmVIZWFkZXIgKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fYWRkQ2xhc3MoIGNsaWNrZWQubmV4dCgpLCBcInVpLWFjY29yZGlvbi1jb250ZW50LWFjdGl2ZVwiICk7XG5cdFx0fVxuXHR9LFxuXG5cdF90b2dnbGU6IGZ1bmN0aW9uKCBkYXRhICkge1xuXHRcdHZhciB0b1Nob3cgPSBkYXRhLm5ld1BhbmVsLFxuXHRcdFx0dG9IaWRlID0gdGhpcy5wcmV2U2hvdy5sZW5ndGggPyB0aGlzLnByZXZTaG93IDogZGF0YS5vbGRQYW5lbDtcblxuXHRcdC8vIEhhbmRsZSBhY3RpdmF0aW5nIGEgcGFuZWwgZHVyaW5nIHRoZSBhbmltYXRpb24gZm9yIGFub3RoZXIgYWN0aXZhdGlvblxuXHRcdHRoaXMucHJldlNob3cuYWRkKCB0aGlzLnByZXZIaWRlICkuc3RvcCggdHJ1ZSwgdHJ1ZSApO1xuXHRcdHRoaXMucHJldlNob3cgPSB0b1Nob3c7XG5cdFx0dGhpcy5wcmV2SGlkZSA9IHRvSGlkZTtcblxuXHRcdGlmICggdGhpcy5vcHRpb25zLmFuaW1hdGUgKSB7XG5cdFx0XHR0aGlzLl9hbmltYXRlKCB0b1Nob3csIHRvSGlkZSwgZGF0YSApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0b0hpZGUuaGlkZSgpO1xuXHRcdFx0dG9TaG93LnNob3coKTtcblx0XHRcdHRoaXMuX3RvZ2dsZUNvbXBsZXRlKCBkYXRhICk7XG5cdFx0fVxuXG5cdFx0dG9IaWRlLmF0dHIoIHtcblx0XHRcdFwiYXJpYS1oaWRkZW5cIjogXCJ0cnVlXCJcblx0XHR9ICk7XG5cdFx0dG9IaWRlLnByZXYoKS5hdHRyKCB7XG5cdFx0XHRcImFyaWEtc2VsZWN0ZWRcIjogXCJmYWxzZVwiLFxuXHRcdFx0XCJhcmlhLWV4cGFuZGVkXCI6IFwiZmFsc2VcIlxuXHRcdH0gKTtcblxuXHRcdC8vIGlmIHdlJ3JlIHN3aXRjaGluZyBwYW5lbHMsIHJlbW92ZSB0aGUgb2xkIGhlYWRlciBmcm9tIHRoZSB0YWIgb3JkZXJcblx0XHQvLyBpZiB3ZSdyZSBvcGVuaW5nIGZyb20gY29sbGFwc2VkIHN0YXRlLCByZW1vdmUgdGhlIHByZXZpb3VzIGhlYWRlciBmcm9tIHRoZSB0YWIgb3JkZXJcblx0XHQvLyBpZiB3ZSdyZSBjb2xsYXBzaW5nLCB0aGVuIGtlZXAgdGhlIGNvbGxhcHNpbmcgaGVhZGVyIGluIHRoZSB0YWIgb3JkZXJcblx0XHRpZiAoIHRvU2hvdy5sZW5ndGggJiYgdG9IaWRlLmxlbmd0aCApIHtcblx0XHRcdHRvSGlkZS5wcmV2KCkuYXR0cigge1xuXHRcdFx0XHRcInRhYkluZGV4XCI6IC0xLFxuXHRcdFx0XHRcImFyaWEtZXhwYW5kZWRcIjogXCJmYWxzZVwiXG5cdFx0XHR9ICk7XG5cdFx0fSBlbHNlIGlmICggdG9TaG93Lmxlbmd0aCApIHtcblx0XHRcdHRoaXMuaGVhZGVycy5maWx0ZXIoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRyZXR1cm4gcGFyc2VJbnQoICQoIHRoaXMgKS5hdHRyKCBcInRhYkluZGV4XCIgKSwgMTAgKSA9PT0gMDtcblx0XHRcdH0gKVxuXHRcdFx0XHQuYXR0ciggXCJ0YWJJbmRleFwiLCAtMSApO1xuXHRcdH1cblxuXHRcdHRvU2hvd1xuXHRcdFx0LmF0dHIoIFwiYXJpYS1oaWRkZW5cIiwgXCJmYWxzZVwiIClcblx0XHRcdC5wcmV2KClcblx0XHRcdFx0LmF0dHIoIHtcblx0XHRcdFx0XHRcImFyaWEtc2VsZWN0ZWRcIjogXCJ0cnVlXCIsXG5cdFx0XHRcdFx0XCJhcmlhLWV4cGFuZGVkXCI6IFwidHJ1ZVwiLFxuXHRcdFx0XHRcdHRhYkluZGV4OiAwXG5cdFx0XHRcdH0gKTtcblx0fSxcblxuXHRfYW5pbWF0ZTogZnVuY3Rpb24oIHRvU2hvdywgdG9IaWRlLCBkYXRhICkge1xuXHRcdHZhciB0b3RhbCwgZWFzaW5nLCBkdXJhdGlvbixcblx0XHRcdHRoYXQgPSB0aGlzLFxuXHRcdFx0YWRqdXN0ID0gMCxcblx0XHRcdGJveFNpemluZyA9IHRvU2hvdy5jc3MoIFwiYm94LXNpemluZ1wiICksXG5cdFx0XHRkb3duID0gdG9TaG93Lmxlbmd0aCAmJlxuXHRcdFx0XHQoICF0b0hpZGUubGVuZ3RoIHx8ICggdG9TaG93LmluZGV4KCkgPCB0b0hpZGUuaW5kZXgoKSApICksXG5cdFx0XHRhbmltYXRlID0gdGhpcy5vcHRpb25zLmFuaW1hdGUgfHwge30sXG5cdFx0XHRvcHRpb25zID0gZG93biAmJiBhbmltYXRlLmRvd24gfHwgYW5pbWF0ZSxcblx0XHRcdGNvbXBsZXRlID0gZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHRoYXQuX3RvZ2dsZUNvbXBsZXRlKCBkYXRhICk7XG5cdFx0XHR9O1xuXG5cdFx0aWYgKCB0eXBlb2Ygb3B0aW9ucyA9PT0gXCJudW1iZXJcIiApIHtcblx0XHRcdGR1cmF0aW9uID0gb3B0aW9ucztcblx0XHR9XG5cdFx0aWYgKCB0eXBlb2Ygb3B0aW9ucyA9PT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdGVhc2luZyA9IG9wdGlvbnM7XG5cdFx0fVxuXG5cdFx0Ly8gZmFsbCBiYWNrIGZyb20gb3B0aW9ucyB0byBhbmltYXRpb24gaW4gY2FzZSBvZiBwYXJ0aWFsIGRvd24gc2V0dGluZ3Ncblx0XHRlYXNpbmcgPSBlYXNpbmcgfHwgb3B0aW9ucy5lYXNpbmcgfHwgYW5pbWF0ZS5lYXNpbmc7XG5cdFx0ZHVyYXRpb24gPSBkdXJhdGlvbiB8fCBvcHRpb25zLmR1cmF0aW9uIHx8IGFuaW1hdGUuZHVyYXRpb247XG5cblx0XHRpZiAoICF0b0hpZGUubGVuZ3RoICkge1xuXHRcdFx0cmV0dXJuIHRvU2hvdy5hbmltYXRlKCB0aGlzLnNob3dQcm9wcywgZHVyYXRpb24sIGVhc2luZywgY29tcGxldGUgKTtcblx0XHR9XG5cdFx0aWYgKCAhdG9TaG93Lmxlbmd0aCApIHtcblx0XHRcdHJldHVybiB0b0hpZGUuYW5pbWF0ZSggdGhpcy5oaWRlUHJvcHMsIGR1cmF0aW9uLCBlYXNpbmcsIGNvbXBsZXRlICk7XG5cdFx0fVxuXG5cdFx0dG90YWwgPSB0b1Nob3cuc2hvdygpLm91dGVySGVpZ2h0KCk7XG5cdFx0dG9IaWRlLmFuaW1hdGUoIHRoaXMuaGlkZVByb3BzLCB7XG5cdFx0XHRkdXJhdGlvbjogZHVyYXRpb24sXG5cdFx0XHRlYXNpbmc6IGVhc2luZyxcblx0XHRcdHN0ZXA6IGZ1bmN0aW9uKCBub3csIGZ4ICkge1xuXHRcdFx0XHRmeC5ub3cgPSBNYXRoLnJvdW5kKCBub3cgKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdFx0dG9TaG93XG5cdFx0XHQuaGlkZSgpXG5cdFx0XHQuYW5pbWF0ZSggdGhpcy5zaG93UHJvcHMsIHtcblx0XHRcdFx0ZHVyYXRpb246IGR1cmF0aW9uLFxuXHRcdFx0XHRlYXNpbmc6IGVhc2luZyxcblx0XHRcdFx0Y29tcGxldGU6IGNvbXBsZXRlLFxuXHRcdFx0XHRzdGVwOiBmdW5jdGlvbiggbm93LCBmeCApIHtcblx0XHRcdFx0XHRmeC5ub3cgPSBNYXRoLnJvdW5kKCBub3cgKTtcblx0XHRcdFx0XHRpZiAoIGZ4LnByb3AgIT09IFwiaGVpZ2h0XCIgKSB7XG5cdFx0XHRcdFx0XHRpZiAoIGJveFNpemluZyA9PT0gXCJjb250ZW50LWJveFwiICkge1xuXHRcdFx0XHRcdFx0XHRhZGp1c3QgKz0gZngubm93O1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0gZWxzZSBpZiAoIHRoYXQub3B0aW9ucy5oZWlnaHRTdHlsZSAhPT0gXCJjb250ZW50XCIgKSB7XG5cdFx0XHRcdFx0XHRmeC5ub3cgPSBNYXRoLnJvdW5kKCB0b3RhbCAtIHRvSGlkZS5vdXRlckhlaWdodCgpIC0gYWRqdXN0ICk7XG5cdFx0XHRcdFx0XHRhZGp1c3QgPSAwO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fSApO1xuXHR9LFxuXG5cdF90b2dnbGVDb21wbGV0ZTogZnVuY3Rpb24oIGRhdGEgKSB7XG5cdFx0dmFyIHRvSGlkZSA9IGRhdGEub2xkUGFuZWwsXG5cdFx0XHRwcmV2ID0gdG9IaWRlLnByZXYoKTtcblxuXHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0b0hpZGUsIFwidWktYWNjb3JkaW9uLWNvbnRlbnQtYWN0aXZlXCIgKTtcblx0XHR0aGlzLl9yZW1vdmVDbGFzcyggcHJldiwgXCJ1aS1hY2NvcmRpb24taGVhZGVyLWFjdGl2ZVwiIClcblx0XHRcdC5fYWRkQ2xhc3MoIHByZXYsIFwidWktYWNjb3JkaW9uLWhlYWRlci1jb2xsYXBzZWRcIiApO1xuXG5cdFx0Ly8gV29yayBhcm91bmQgZm9yIHJlbmRlcmluZyBidWcgaW4gSUUgKCM1NDIxKVxuXHRcdGlmICggdG9IaWRlLmxlbmd0aCApIHtcblx0XHRcdHRvSGlkZS5wYXJlbnQoKVsgMCBdLmNsYXNzTmFtZSA9IHRvSGlkZS5wYXJlbnQoKVsgMCBdLmNsYXNzTmFtZTtcblx0XHR9XG5cdFx0dGhpcy5fdHJpZ2dlciggXCJhY3RpdmF0ZVwiLCBudWxsLCBkYXRhICk7XG5cdH1cbn0gKTtcblxuXG5cbnZhciBzYWZlQWN0aXZlRWxlbWVudCA9ICQudWkuc2FmZUFjdGl2ZUVsZW1lbnQgPSBmdW5jdGlvbiggZG9jdW1lbnQgKSB7XG5cdHZhciBhY3RpdmVFbGVtZW50O1xuXG5cdC8vIFN1cHBvcnQ6IElFIDkgb25seVxuXHQvLyBJRTkgdGhyb3dzIGFuIFwiVW5zcGVjaWZpZWQgZXJyb3JcIiBhY2Nlc3NpbmcgZG9jdW1lbnQuYWN0aXZlRWxlbWVudCBmcm9tIGFuIDxpZnJhbWU+XG5cdHRyeSB7XG5cdFx0YWN0aXZlRWxlbWVudCA9IGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQ7XG5cdH0gY2F0Y2ggKCBlcnJvciApIHtcblx0XHRhY3RpdmVFbGVtZW50ID0gZG9jdW1lbnQuYm9keTtcblx0fVxuXG5cdC8vIFN1cHBvcnQ6IElFIDkgLSAxMSBvbmx5XG5cdC8vIElFIG1heSByZXR1cm4gbnVsbCBpbnN0ZWFkIG9mIGFuIGVsZW1lbnRcblx0Ly8gSW50ZXJlc3RpbmdseSwgdGhpcyBvbmx5IHNlZW1zIHRvIG9jY3VyIHdoZW4gTk9UIGluIGFuIGlmcmFtZVxuXHRpZiAoICFhY3RpdmVFbGVtZW50ICkge1xuXHRcdGFjdGl2ZUVsZW1lbnQgPSBkb2N1bWVudC5ib2R5O1xuXHR9XG5cblx0Ly8gU3VwcG9ydDogSUUgMTEgb25seVxuXHQvLyBJRTExIHJldHVybnMgYSBzZWVtaW5nbHkgZW1wdHkgb2JqZWN0IGluIHNvbWUgY2FzZXMgd2hlbiBhY2Nlc3Npbmdcblx0Ly8gZG9jdW1lbnQuYWN0aXZlRWxlbWVudCBmcm9tIGFuIDxpZnJhbWU+XG5cdGlmICggIWFjdGl2ZUVsZW1lbnQubm9kZU5hbWUgKSB7XG5cdFx0YWN0aXZlRWxlbWVudCA9IGRvY3VtZW50LmJvZHk7XG5cdH1cblxuXHRyZXR1cm4gYWN0aXZlRWxlbWVudDtcbn07XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgTWVudSAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogTWVudVxuLy8+Pmdyb3VwOiBXaWRnZXRzXG4vLz4+ZGVzY3JpcHRpb246IENyZWF0ZXMgbmVzdGFibGUgbWVudXMuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vbWVudS9cbi8vPj5kZW1vczogaHR0cDovL2pxdWVyeXVpLmNvbS9tZW51L1xuLy8+PmNzcy5zdHJ1Y3R1cmU6IC4uLy4uL3RoZW1lcy9iYXNlL2NvcmUuY3NzXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2UvbWVudS5jc3Ncbi8vPj5jc3MudGhlbWU6IC4uLy4uL3RoZW1lcy9iYXNlL3RoZW1lLmNzc1xuXG5cblxudmFyIHdpZGdldHNNZW51ID0gJC53aWRnZXQoIFwidWkubWVudVwiLCB7XG5cdHZlcnNpb246IFwiMS4xMi4xXCIsXG5cdGRlZmF1bHRFbGVtZW50OiBcIjx1bD5cIixcblx0ZGVsYXk6IDMwMCxcblx0b3B0aW9uczoge1xuXHRcdGljb25zOiB7XG5cdFx0XHRzdWJtZW51OiBcInVpLWljb24tY2FyZXQtMS1lXCJcblx0XHR9LFxuXHRcdGl0ZW1zOiBcIj4gKlwiLFxuXHRcdG1lbnVzOiBcInVsXCIsXG5cdFx0cG9zaXRpb246IHtcblx0XHRcdG15OiBcImxlZnQgdG9wXCIsXG5cdFx0XHRhdDogXCJyaWdodCB0b3BcIlxuXHRcdH0sXG5cdFx0cm9sZTogXCJtZW51XCIsXG5cblx0XHQvLyBDYWxsYmFja3Ncblx0XHRibHVyOiBudWxsLFxuXHRcdGZvY3VzOiBudWxsLFxuXHRcdHNlbGVjdDogbnVsbFxuXHR9LFxuXG5cdF9jcmVhdGU6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuYWN0aXZlTWVudSA9IHRoaXMuZWxlbWVudDtcblxuXHRcdC8vIEZsYWcgdXNlZCB0byBwcmV2ZW50IGZpcmluZyBvZiB0aGUgY2xpY2sgaGFuZGxlclxuXHRcdC8vIGFzIHRoZSBldmVudCBidWJibGVzIHVwIHRocm91Z2ggbmVzdGVkIG1lbnVzXG5cdFx0dGhpcy5tb3VzZUhhbmRsZWQgPSBmYWxzZTtcblx0XHR0aGlzLmVsZW1lbnRcblx0XHRcdC51bmlxdWVJZCgpXG5cdFx0XHQuYXR0cigge1xuXHRcdFx0XHRyb2xlOiB0aGlzLm9wdGlvbnMucm9sZSxcblx0XHRcdFx0dGFiSW5kZXg6IDBcblx0XHRcdH0gKTtcblxuXHRcdHRoaXMuX2FkZENsYXNzKCBcInVpLW1lbnVcIiwgXCJ1aS13aWRnZXQgdWktd2lkZ2V0LWNvbnRlbnRcIiApO1xuXHRcdHRoaXMuX29uKCB7XG5cblx0XHRcdC8vIFByZXZlbnQgZm9jdXMgZnJvbSBzdGlja2luZyB0byBsaW5rcyBpbnNpZGUgbWVudSBhZnRlciBjbGlja2luZ1xuXHRcdFx0Ly8gdGhlbSAoZm9jdXMgc2hvdWxkIGFsd2F5cyBzdGF5IG9uIFVMIGR1cmluZyBuYXZpZ2F0aW9uKS5cblx0XHRcdFwibW91c2Vkb3duIC51aS1tZW51LWl0ZW1cIjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0fSxcblx0XHRcdFwiY2xpY2sgLnVpLW1lbnUtaXRlbVwiOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdHZhciB0YXJnZXQgPSAkKCBldmVudC50YXJnZXQgKTtcblx0XHRcdFx0dmFyIGFjdGl2ZSA9ICQoICQudWkuc2FmZUFjdGl2ZUVsZW1lbnQoIHRoaXMuZG9jdW1lbnRbIDAgXSApICk7XG5cdFx0XHRcdGlmICggIXRoaXMubW91c2VIYW5kbGVkICYmIHRhcmdldC5ub3QoIFwiLnVpLXN0YXRlLWRpc2FibGVkXCIgKS5sZW5ndGggKSB7XG5cdFx0XHRcdFx0dGhpcy5zZWxlY3QoIGV2ZW50ICk7XG5cblx0XHRcdFx0XHQvLyBPbmx5IHNldCB0aGUgbW91c2VIYW5kbGVkIGZsYWcgaWYgdGhlIGV2ZW50IHdpbGwgYnViYmxlLCBzZWUgIzk0NjkuXG5cdFx0XHRcdFx0aWYgKCAhZXZlbnQuaXNQcm9wYWdhdGlvblN0b3BwZWQoKSApIHtcblx0XHRcdFx0XHRcdHRoaXMubW91c2VIYW5kbGVkID0gdHJ1ZTtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBPcGVuIHN1Ym1lbnUgb24gY2xpY2tcblx0XHRcdFx0XHRpZiAoIHRhcmdldC5oYXMoIFwiLnVpLW1lbnVcIiApLmxlbmd0aCApIHtcblx0XHRcdFx0XHRcdHRoaXMuZXhwYW5kKCBldmVudCApO1xuXHRcdFx0XHRcdH0gZWxzZSBpZiAoICF0aGlzLmVsZW1lbnQuaXMoIFwiOmZvY3VzXCIgKSAmJlxuXHRcdFx0XHRcdFx0XHRhY3RpdmUuY2xvc2VzdCggXCIudWktbWVudVwiICkubGVuZ3RoICkge1xuXG5cdFx0XHRcdFx0XHQvLyBSZWRpcmVjdCBmb2N1cyB0byB0aGUgbWVudVxuXHRcdFx0XHRcdFx0dGhpcy5lbGVtZW50LnRyaWdnZXIoIFwiZm9jdXNcIiwgWyB0cnVlIF0gKTtcblxuXHRcdFx0XHRcdFx0Ly8gSWYgdGhlIGFjdGl2ZSBpdGVtIGlzIG9uIHRoZSB0b3AgbGV2ZWwsIGxldCBpdCBzdGF5IGFjdGl2ZS5cblx0XHRcdFx0XHRcdC8vIE90aGVyd2lzZSwgYmx1ciB0aGUgYWN0aXZlIGl0ZW0gc2luY2UgaXQgaXMgbm8gbG9uZ2VyIHZpc2libGUuXG5cdFx0XHRcdFx0XHRpZiAoIHRoaXMuYWN0aXZlICYmIHRoaXMuYWN0aXZlLnBhcmVudHMoIFwiLnVpLW1lbnVcIiApLmxlbmd0aCA9PT0gMSApIHtcblx0XHRcdFx0XHRcdFx0Y2xlYXJUaW1lb3V0KCB0aGlzLnRpbWVyICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9LFxuXHRcdFx0XCJtb3VzZWVudGVyIC51aS1tZW51LWl0ZW1cIjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXG5cdFx0XHRcdC8vIElnbm9yZSBtb3VzZSBldmVudHMgd2hpbGUgdHlwZWFoZWFkIGlzIGFjdGl2ZSwgc2VlICMxMDQ1OC5cblx0XHRcdFx0Ly8gUHJldmVudHMgZm9jdXNpbmcgdGhlIHdyb25nIGl0ZW0gd2hlbiB0eXBlYWhlYWQgY2F1c2VzIGEgc2Nyb2xsIHdoaWxlIHRoZSBtb3VzZVxuXHRcdFx0XHQvLyBpcyBvdmVyIGFuIGl0ZW0gaW4gdGhlIG1lbnVcblx0XHRcdFx0aWYgKCB0aGlzLnByZXZpb3VzRmlsdGVyICkge1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBhY3R1YWxUYXJnZXQgPSAkKCBldmVudC50YXJnZXQgKS5jbG9zZXN0KCBcIi51aS1tZW51LWl0ZW1cIiApLFxuXHRcdFx0XHRcdHRhcmdldCA9ICQoIGV2ZW50LmN1cnJlbnRUYXJnZXQgKTtcblxuXHRcdFx0XHQvLyBJZ25vcmUgYnViYmxlZCBldmVudHMgb24gcGFyZW50IGl0ZW1zLCBzZWUgIzExNjQxXG5cdFx0XHRcdGlmICggYWN0dWFsVGFyZ2V0WyAwIF0gIT09IHRhcmdldFsgMCBdICkge1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIFJlbW92ZSB1aS1zdGF0ZS1hY3RpdmUgY2xhc3MgZnJvbSBzaWJsaW5ncyBvZiB0aGUgbmV3bHkgZm9jdXNlZCBtZW51IGl0ZW1cblx0XHRcdFx0Ly8gdG8gYXZvaWQgYSBqdW1wIGNhdXNlZCBieSBhZGphY2VudCBlbGVtZW50cyBib3RoIGhhdmluZyBhIGNsYXNzIHdpdGggYSBib3JkZXJcblx0XHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIHRhcmdldC5zaWJsaW5ncygpLmNoaWxkcmVuKCBcIi51aS1zdGF0ZS1hY3RpdmVcIiApLFxuXHRcdFx0XHRcdG51bGwsIFwidWktc3RhdGUtYWN0aXZlXCIgKTtcblx0XHRcdFx0dGhpcy5mb2N1cyggZXZlbnQsIHRhcmdldCApO1xuXHRcdFx0fSxcblx0XHRcdG1vdXNlbGVhdmU6IFwiY29sbGFwc2VBbGxcIixcblx0XHRcdFwibW91c2VsZWF2ZSAudWktbWVudVwiOiBcImNvbGxhcHNlQWxsXCIsXG5cdFx0XHRmb2N1czogZnVuY3Rpb24oIGV2ZW50LCBrZWVwQWN0aXZlSXRlbSApIHtcblxuXHRcdFx0XHQvLyBJZiB0aGVyZSdzIGFscmVhZHkgYW4gYWN0aXZlIGl0ZW0sIGtlZXAgaXQgYWN0aXZlXG5cdFx0XHRcdC8vIElmIG5vdCwgYWN0aXZhdGUgdGhlIGZpcnN0IGl0ZW1cblx0XHRcdFx0dmFyIGl0ZW0gPSB0aGlzLmFjdGl2ZSB8fCB0aGlzLmVsZW1lbnQuZmluZCggdGhpcy5vcHRpb25zLml0ZW1zICkuZXEoIDAgKTtcblxuXHRcdFx0XHRpZiAoICFrZWVwQWN0aXZlSXRlbSApIHtcblx0XHRcdFx0XHR0aGlzLmZvY3VzKCBldmVudCwgaXRlbSApO1xuXHRcdFx0XHR9XG5cdFx0XHR9LFxuXHRcdFx0Ymx1cjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0XHR0aGlzLl9kZWxheSggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0dmFyIG5vdENvbnRhaW5lZCA9ICEkLmNvbnRhaW5zKFxuXHRcdFx0XHRcdFx0dGhpcy5lbGVtZW50WyAwIF0sXG5cdFx0XHRcdFx0XHQkLnVpLnNhZmVBY3RpdmVFbGVtZW50KCB0aGlzLmRvY3VtZW50WyAwIF0gKVxuXHRcdFx0XHRcdCk7XG5cdFx0XHRcdFx0aWYgKCBub3RDb250YWluZWQgKSB7XG5cdFx0XHRcdFx0XHR0aGlzLmNvbGxhcHNlQWxsKCBldmVudCApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSApO1xuXHRcdFx0fSxcblx0XHRcdGtleWRvd246IFwiX2tleWRvd25cIlxuXHRcdH0gKTtcblxuXHRcdHRoaXMucmVmcmVzaCgpO1xuXG5cdFx0Ly8gQ2xpY2tzIG91dHNpZGUgb2YgYSBtZW51IGNvbGxhcHNlIGFueSBvcGVuIG1lbnVzXG5cdFx0dGhpcy5fb24oIHRoaXMuZG9jdW1lbnQsIHtcblx0XHRcdGNsaWNrOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdGlmICggdGhpcy5fY2xvc2VPbkRvY3VtZW50Q2xpY2soIGV2ZW50ICkgKSB7XG5cdFx0XHRcdFx0dGhpcy5jb2xsYXBzZUFsbCggZXZlbnQgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIFJlc2V0IHRoZSBtb3VzZUhhbmRsZWQgZmxhZ1xuXHRcdFx0XHR0aGlzLm1vdXNlSGFuZGxlZCA9IGZhbHNlO1xuXHRcdFx0fVxuXHRcdH0gKTtcblx0fSxcblxuXHRfZGVzdHJveTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGl0ZW1zID0gdGhpcy5lbGVtZW50LmZpbmQoIFwiLnVpLW1lbnUtaXRlbVwiIClcblx0XHRcdFx0LnJlbW92ZUF0dHIoIFwicm9sZSBhcmlhLWRpc2FibGVkXCIgKSxcblx0XHRcdHN1Ym1lbnVzID0gaXRlbXMuY2hpbGRyZW4oIFwiLnVpLW1lbnUtaXRlbS13cmFwcGVyXCIgKVxuXHRcdFx0XHQucmVtb3ZlVW5pcXVlSWQoKVxuXHRcdFx0XHQucmVtb3ZlQXR0ciggXCJ0YWJJbmRleCByb2xlIGFyaWEtaGFzcG9wdXBcIiApO1xuXG5cdFx0Ly8gRGVzdHJveSAoc3ViKW1lbnVzXG5cdFx0dGhpcy5lbGVtZW50XG5cdFx0XHQucmVtb3ZlQXR0ciggXCJhcmlhLWFjdGl2ZWRlc2NlbmRhbnRcIiApXG5cdFx0XHQuZmluZCggXCIudWktbWVudVwiICkuYWRkQmFjaygpXG5cdFx0XHRcdC5yZW1vdmVBdHRyKCBcInJvbGUgYXJpYS1sYWJlbGxlZGJ5IGFyaWEtZXhwYW5kZWQgYXJpYS1oaWRkZW4gYXJpYS1kaXNhYmxlZCBcIiArXG5cdFx0XHRcdFx0XCJ0YWJJbmRleFwiIClcblx0XHRcdFx0LnJlbW92ZVVuaXF1ZUlkKClcblx0XHRcdFx0LnNob3coKTtcblxuXHRcdHN1Ym1lbnVzLmNoaWxkcmVuKCkuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgZWxlbSA9ICQoIHRoaXMgKTtcblx0XHRcdGlmICggZWxlbS5kYXRhKCBcInVpLW1lbnUtc3VibWVudS1jYXJldFwiICkgKSB7XG5cdFx0XHRcdGVsZW0ucmVtb3ZlKCk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXHR9LFxuXG5cdF9rZXlkb3duOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dmFyIG1hdGNoLCBwcmV2LCBjaGFyYWN0ZXIsIHNraXAsXG5cdFx0XHRwcmV2ZW50RGVmYXVsdCA9IHRydWU7XG5cblx0XHRzd2l0Y2ggKCBldmVudC5rZXlDb2RlICkge1xuXHRcdGNhc2UgJC51aS5rZXlDb2RlLlBBR0VfVVA6XG5cdFx0XHR0aGlzLnByZXZpb3VzUGFnZSggZXZlbnQgKTtcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgJC51aS5rZXlDb2RlLlBBR0VfRE9XTjpcblx0XHRcdHRoaXMubmV4dFBhZ2UoIGV2ZW50ICk7XG5cdFx0XHRicmVhaztcblx0XHRjYXNlICQudWkua2V5Q29kZS5IT01FOlxuXHRcdFx0dGhpcy5fbW92ZSggXCJmaXJzdFwiLCBcImZpcnN0XCIsIGV2ZW50ICk7XG5cdFx0XHRicmVhaztcblx0XHRjYXNlICQudWkua2V5Q29kZS5FTkQ6XG5cdFx0XHR0aGlzLl9tb3ZlKCBcImxhc3RcIiwgXCJsYXN0XCIsIGV2ZW50ICk7XG5cdFx0XHRicmVhaztcblx0XHRjYXNlICQudWkua2V5Q29kZS5VUDpcblx0XHRcdHRoaXMucHJldmlvdXMoIGV2ZW50ICk7XG5cdFx0XHRicmVhaztcblx0XHRjYXNlICQudWkua2V5Q29kZS5ET1dOOlxuXHRcdFx0dGhpcy5uZXh0KCBldmVudCApO1xuXHRcdFx0YnJlYWs7XG5cdFx0Y2FzZSAkLnVpLmtleUNvZGUuTEVGVDpcblx0XHRcdHRoaXMuY29sbGFwc2UoIGV2ZW50ICk7XG5cdFx0XHRicmVhaztcblx0XHRjYXNlICQudWkua2V5Q29kZS5SSUdIVDpcblx0XHRcdGlmICggdGhpcy5hY3RpdmUgJiYgIXRoaXMuYWN0aXZlLmlzKCBcIi51aS1zdGF0ZS1kaXNhYmxlZFwiICkgKSB7XG5cdFx0XHRcdHRoaXMuZXhwYW5kKCBldmVudCApO1xuXHRcdFx0fVxuXHRcdFx0YnJlYWs7XG5cdFx0Y2FzZSAkLnVpLmtleUNvZGUuRU5URVI6XG5cdFx0Y2FzZSAkLnVpLmtleUNvZGUuU1BBQ0U6XG5cdFx0XHR0aGlzLl9hY3RpdmF0ZSggZXZlbnQgKTtcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgJC51aS5rZXlDb2RlLkVTQ0FQRTpcblx0XHRcdHRoaXMuY29sbGFwc2UoIGV2ZW50ICk7XG5cdFx0XHRicmVhaztcblx0XHRkZWZhdWx0OlxuXHRcdFx0cHJldmVudERlZmF1bHQgPSBmYWxzZTtcblx0XHRcdHByZXYgPSB0aGlzLnByZXZpb3VzRmlsdGVyIHx8IFwiXCI7XG5cdFx0XHRza2lwID0gZmFsc2U7XG5cblx0XHRcdC8vIFN1cHBvcnQgbnVtYmVyIHBhZCB2YWx1ZXNcblx0XHRcdGNoYXJhY3RlciA9IGV2ZW50LmtleUNvZGUgPj0gOTYgJiYgZXZlbnQua2V5Q29kZSA8PSAxMDUgP1xuXHRcdFx0XHQoIGV2ZW50LmtleUNvZGUgLSA5NiApLnRvU3RyaW5nKCkgOiBTdHJpbmcuZnJvbUNoYXJDb2RlKCBldmVudC5rZXlDb2RlICk7XG5cblx0XHRcdGNsZWFyVGltZW91dCggdGhpcy5maWx0ZXJUaW1lciApO1xuXG5cdFx0XHRpZiAoIGNoYXJhY3RlciA9PT0gcHJldiApIHtcblx0XHRcdFx0c2tpcCA9IHRydWU7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRjaGFyYWN0ZXIgPSBwcmV2ICsgY2hhcmFjdGVyO1xuXHRcdFx0fVxuXG5cdFx0XHRtYXRjaCA9IHRoaXMuX2ZpbHRlck1lbnVJdGVtcyggY2hhcmFjdGVyICk7XG5cdFx0XHRtYXRjaCA9IHNraXAgJiYgbWF0Y2guaW5kZXgoIHRoaXMuYWN0aXZlLm5leHQoKSApICE9PSAtMSA/XG5cdFx0XHRcdHRoaXMuYWN0aXZlLm5leHRBbGwoIFwiLnVpLW1lbnUtaXRlbVwiICkgOlxuXHRcdFx0XHRtYXRjaDtcblxuXHRcdFx0Ly8gSWYgbm8gbWF0Y2hlcyBvbiB0aGUgY3VycmVudCBmaWx0ZXIsIHJlc2V0IHRvIHRoZSBsYXN0IGNoYXJhY3RlciBwcmVzc2VkXG5cdFx0XHQvLyB0byBtb3ZlIGRvd24gdGhlIG1lbnUgdG8gdGhlIGZpcnN0IGl0ZW0gdGhhdCBzdGFydHMgd2l0aCB0aGF0IGNoYXJhY3RlclxuXHRcdFx0aWYgKCAhbWF0Y2gubGVuZ3RoICkge1xuXHRcdFx0XHRjaGFyYWN0ZXIgPSBTdHJpbmcuZnJvbUNoYXJDb2RlKCBldmVudC5rZXlDb2RlICk7XG5cdFx0XHRcdG1hdGNoID0gdGhpcy5fZmlsdGVyTWVudUl0ZW1zKCBjaGFyYWN0ZXIgKTtcblx0XHRcdH1cblxuXHRcdFx0aWYgKCBtYXRjaC5sZW5ndGggKSB7XG5cdFx0XHRcdHRoaXMuZm9jdXMoIGV2ZW50LCBtYXRjaCApO1xuXHRcdFx0XHR0aGlzLnByZXZpb3VzRmlsdGVyID0gY2hhcmFjdGVyO1xuXHRcdFx0XHR0aGlzLmZpbHRlclRpbWVyID0gdGhpcy5fZGVsYXkoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdGRlbGV0ZSB0aGlzLnByZXZpb3VzRmlsdGVyO1xuXHRcdFx0XHR9LCAxMDAwICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRkZWxldGUgdGhpcy5wcmV2aW91c0ZpbHRlcjtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoIHByZXZlbnREZWZhdWx0ICkge1xuXHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHR9XG5cdH0sXG5cblx0X2FjdGl2YXRlOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0aWYgKCB0aGlzLmFjdGl2ZSAmJiAhdGhpcy5hY3RpdmUuaXMoIFwiLnVpLXN0YXRlLWRpc2FibGVkXCIgKSApIHtcblx0XHRcdGlmICggdGhpcy5hY3RpdmUuY2hpbGRyZW4oIFwiW2FyaWEtaGFzcG9wdXA9J3RydWUnXVwiICkubGVuZ3RoICkge1xuXHRcdFx0XHR0aGlzLmV4cGFuZCggZXZlbnQgKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMuc2VsZWN0KCBldmVudCApO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHRyZWZyZXNoOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgbWVudXMsIGl0ZW1zLCBuZXdTdWJtZW51cywgbmV3SXRlbXMsIG5ld1dyYXBwZXJzLFxuXHRcdFx0dGhhdCA9IHRoaXMsXG5cdFx0XHRpY29uID0gdGhpcy5vcHRpb25zLmljb25zLnN1Ym1lbnUsXG5cdFx0XHRzdWJtZW51cyA9IHRoaXMuZWxlbWVudC5maW5kKCB0aGlzLm9wdGlvbnMubWVudXMgKTtcblxuXHRcdHRoaXMuX3RvZ2dsZUNsYXNzKCBcInVpLW1lbnUtaWNvbnNcIiwgbnVsbCwgISF0aGlzLmVsZW1lbnQuZmluZCggXCIudWktaWNvblwiICkubGVuZ3RoICk7XG5cblx0XHQvLyBJbml0aWFsaXplIG5lc3RlZCBtZW51c1xuXHRcdG5ld1N1Ym1lbnVzID0gc3VibWVudXMuZmlsdGVyKCBcIjpub3QoLnVpLW1lbnUpXCIgKVxuXHRcdFx0LmhpZGUoKVxuXHRcdFx0LmF0dHIoIHtcblx0XHRcdFx0cm9sZTogdGhpcy5vcHRpb25zLnJvbGUsXG5cdFx0XHRcdFwiYXJpYS1oaWRkZW5cIjogXCJ0cnVlXCIsXG5cdFx0XHRcdFwiYXJpYS1leHBhbmRlZFwiOiBcImZhbHNlXCJcblx0XHRcdH0gKVxuXHRcdFx0LmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHR2YXIgbWVudSA9ICQoIHRoaXMgKSxcblx0XHRcdFx0XHRpdGVtID0gbWVudS5wcmV2KCksXG5cdFx0XHRcdFx0c3VibWVudUNhcmV0ID0gJCggXCI8c3Bhbj5cIiApLmRhdGEoIFwidWktbWVudS1zdWJtZW51LWNhcmV0XCIsIHRydWUgKTtcblxuXHRcdFx0XHR0aGF0Ll9hZGRDbGFzcyggc3VibWVudUNhcmV0LCBcInVpLW1lbnUtaWNvblwiLCBcInVpLWljb24gXCIgKyBpY29uICk7XG5cdFx0XHRcdGl0ZW1cblx0XHRcdFx0XHQuYXR0ciggXCJhcmlhLWhhc3BvcHVwXCIsIFwidHJ1ZVwiIClcblx0XHRcdFx0XHQucHJlcGVuZCggc3VibWVudUNhcmV0ICk7XG5cdFx0XHRcdG1lbnUuYXR0ciggXCJhcmlhLWxhYmVsbGVkYnlcIiwgaXRlbS5hdHRyKCBcImlkXCIgKSApO1xuXHRcdFx0fSApO1xuXG5cdFx0dGhpcy5fYWRkQ2xhc3MoIG5ld1N1Ym1lbnVzLCBcInVpLW1lbnVcIiwgXCJ1aS13aWRnZXQgdWktd2lkZ2V0LWNvbnRlbnQgdWktZnJvbnRcIiApO1xuXG5cdFx0bWVudXMgPSBzdWJtZW51cy5hZGQoIHRoaXMuZWxlbWVudCApO1xuXHRcdGl0ZW1zID0gbWVudXMuZmluZCggdGhpcy5vcHRpb25zLml0ZW1zICk7XG5cblx0XHQvLyBJbml0aWFsaXplIG1lbnUtaXRlbXMgY29udGFpbmluZyBzcGFjZXMgYW5kL29yIGRhc2hlcyBvbmx5IGFzIGRpdmlkZXJzXG5cdFx0aXRlbXMubm90KCBcIi51aS1tZW51LWl0ZW1cIiApLmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0dmFyIGl0ZW0gPSAkKCB0aGlzICk7XG5cdFx0XHRpZiAoIHRoYXQuX2lzRGl2aWRlciggaXRlbSApICkge1xuXHRcdFx0XHR0aGF0Ll9hZGRDbGFzcyggaXRlbSwgXCJ1aS1tZW51LWRpdmlkZXJcIiwgXCJ1aS13aWRnZXQtY29udGVudFwiICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdFx0Ly8gRG9uJ3QgcmVmcmVzaCBsaXN0IGl0ZW1zIHRoYXQgYXJlIGFscmVhZHkgYWRhcHRlZFxuXHRcdG5ld0l0ZW1zID0gaXRlbXMubm90KCBcIi51aS1tZW51LWl0ZW0sIC51aS1tZW51LWRpdmlkZXJcIiApO1xuXHRcdG5ld1dyYXBwZXJzID0gbmV3SXRlbXMuY2hpbGRyZW4oKVxuXHRcdFx0Lm5vdCggXCIudWktbWVudVwiIClcblx0XHRcdFx0LnVuaXF1ZUlkKClcblx0XHRcdFx0LmF0dHIoIHtcblx0XHRcdFx0XHR0YWJJbmRleDogLTEsXG5cdFx0XHRcdFx0cm9sZTogdGhpcy5faXRlbVJvbGUoKVxuXHRcdFx0XHR9ICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIG5ld0l0ZW1zLCBcInVpLW1lbnUtaXRlbVwiIClcblx0XHRcdC5fYWRkQ2xhc3MoIG5ld1dyYXBwZXJzLCBcInVpLW1lbnUtaXRlbS13cmFwcGVyXCIgKTtcblxuXHRcdC8vIEFkZCBhcmlhLWRpc2FibGVkIGF0dHJpYnV0ZSB0byBhbnkgZGlzYWJsZWQgbWVudSBpdGVtXG5cdFx0aXRlbXMuZmlsdGVyKCBcIi51aS1zdGF0ZS1kaXNhYmxlZFwiICkuYXR0ciggXCJhcmlhLWRpc2FibGVkXCIsIFwidHJ1ZVwiICk7XG5cblx0XHQvLyBJZiB0aGUgYWN0aXZlIGl0ZW0gaGFzIGJlZW4gcmVtb3ZlZCwgYmx1ciB0aGUgbWVudVxuXHRcdGlmICggdGhpcy5hY3RpdmUgJiYgISQuY29udGFpbnMoIHRoaXMuZWxlbWVudFsgMCBdLCB0aGlzLmFjdGl2ZVsgMCBdICkgKSB7XG5cdFx0XHR0aGlzLmJsdXIoKTtcblx0XHR9XG5cdH0sXG5cblx0X2l0ZW1Sb2xlOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4ge1xuXHRcdFx0bWVudTogXCJtZW51aXRlbVwiLFxuXHRcdFx0bGlzdGJveDogXCJvcHRpb25cIlxuXHRcdH1bIHRoaXMub3B0aW9ucy5yb2xlIF07XG5cdH0sXG5cblx0X3NldE9wdGlvbjogZnVuY3Rpb24oIGtleSwgdmFsdWUgKSB7XG5cdFx0aWYgKCBrZXkgPT09IFwiaWNvbnNcIiApIHtcblx0XHRcdHZhciBpY29ucyA9IHRoaXMuZWxlbWVudC5maW5kKCBcIi51aS1tZW51LWljb25cIiApO1xuXHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIGljb25zLCBudWxsLCB0aGlzLm9wdGlvbnMuaWNvbnMuc3VibWVudSApXG5cdFx0XHRcdC5fYWRkQ2xhc3MoIGljb25zLCBudWxsLCB2YWx1ZS5zdWJtZW51ICk7XG5cdFx0fVxuXHRcdHRoaXMuX3N1cGVyKCBrZXksIHZhbHVlICk7XG5cdH0sXG5cblx0X3NldE9wdGlvbkRpc2FibGVkOiBmdW5jdGlvbiggdmFsdWUgKSB7XG5cdFx0dGhpcy5fc3VwZXIoIHZhbHVlICk7XG5cblx0XHR0aGlzLmVsZW1lbnQuYXR0ciggXCJhcmlhLWRpc2FibGVkXCIsIFN0cmluZyggdmFsdWUgKSApO1xuXHRcdHRoaXMuX3RvZ2dsZUNsYXNzKCBudWxsLCBcInVpLXN0YXRlLWRpc2FibGVkXCIsICEhdmFsdWUgKTtcblx0fSxcblxuXHRmb2N1czogZnVuY3Rpb24oIGV2ZW50LCBpdGVtICkge1xuXHRcdHZhciBuZXN0ZWQsIGZvY3VzZWQsIGFjdGl2ZVBhcmVudDtcblx0XHR0aGlzLmJsdXIoIGV2ZW50LCBldmVudCAmJiBldmVudC50eXBlID09PSBcImZvY3VzXCIgKTtcblxuXHRcdHRoaXMuX3Njcm9sbEludG9WaWV3KCBpdGVtICk7XG5cblx0XHR0aGlzLmFjdGl2ZSA9IGl0ZW0uZmlyc3QoKTtcblxuXHRcdGZvY3VzZWQgPSB0aGlzLmFjdGl2ZS5jaGlsZHJlbiggXCIudWktbWVudS1pdGVtLXdyYXBwZXJcIiApO1xuXHRcdHRoaXMuX2FkZENsYXNzKCBmb2N1c2VkLCBudWxsLCBcInVpLXN0YXRlLWFjdGl2ZVwiICk7XG5cblx0XHQvLyBPbmx5IHVwZGF0ZSBhcmlhLWFjdGl2ZWRlc2NlbmRhbnQgaWYgdGhlcmUncyBhIHJvbGVcblx0XHQvLyBvdGhlcndpc2Ugd2UgYXNzdW1lIGZvY3VzIGlzIG1hbmFnZWQgZWxzZXdoZXJlXG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMucm9sZSApIHtcblx0XHRcdHRoaXMuZWxlbWVudC5hdHRyKCBcImFyaWEtYWN0aXZlZGVzY2VuZGFudFwiLCBmb2N1c2VkLmF0dHIoIFwiaWRcIiApICk7XG5cdFx0fVxuXG5cdFx0Ly8gSGlnaGxpZ2h0IGFjdGl2ZSBwYXJlbnQgbWVudSBpdGVtLCBpZiBhbnlcblx0XHRhY3RpdmVQYXJlbnQgPSB0aGlzLmFjdGl2ZVxuXHRcdFx0LnBhcmVudCgpXG5cdFx0XHRcdC5jbG9zZXN0KCBcIi51aS1tZW51LWl0ZW1cIiApXG5cdFx0XHRcdFx0LmNoaWxkcmVuKCBcIi51aS1tZW51LWl0ZW0td3JhcHBlclwiICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIGFjdGl2ZVBhcmVudCwgbnVsbCwgXCJ1aS1zdGF0ZS1hY3RpdmVcIiApO1xuXG5cdFx0aWYgKCBldmVudCAmJiBldmVudC50eXBlID09PSBcImtleWRvd25cIiApIHtcblx0XHRcdHRoaXMuX2Nsb3NlKCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMudGltZXIgPSB0aGlzLl9kZWxheSggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHRoaXMuX2Nsb3NlKCk7XG5cdFx0XHR9LCB0aGlzLmRlbGF5ICk7XG5cdFx0fVxuXG5cdFx0bmVzdGVkID0gaXRlbS5jaGlsZHJlbiggXCIudWktbWVudVwiICk7XG5cdFx0aWYgKCBuZXN0ZWQubGVuZ3RoICYmIGV2ZW50ICYmICggL15tb3VzZS8udGVzdCggZXZlbnQudHlwZSApICkgKSB7XG5cdFx0XHR0aGlzLl9zdGFydE9wZW5pbmcoIG5lc3RlZCApO1xuXHRcdH1cblx0XHR0aGlzLmFjdGl2ZU1lbnUgPSBpdGVtLnBhcmVudCgpO1xuXG5cdFx0dGhpcy5fdHJpZ2dlciggXCJmb2N1c1wiLCBldmVudCwgeyBpdGVtOiBpdGVtIH0gKTtcblx0fSxcblxuXHRfc2Nyb2xsSW50b1ZpZXc6IGZ1bmN0aW9uKCBpdGVtICkge1xuXHRcdHZhciBib3JkZXJUb3AsIHBhZGRpbmdUb3AsIG9mZnNldCwgc2Nyb2xsLCBlbGVtZW50SGVpZ2h0LCBpdGVtSGVpZ2h0O1xuXHRcdGlmICggdGhpcy5faGFzU2Nyb2xsKCkgKSB7XG5cdFx0XHRib3JkZXJUb3AgPSBwYXJzZUZsb2F0KCAkLmNzcyggdGhpcy5hY3RpdmVNZW51WyAwIF0sIFwiYm9yZGVyVG9wV2lkdGhcIiApICkgfHwgMDtcblx0XHRcdHBhZGRpbmdUb3AgPSBwYXJzZUZsb2F0KCAkLmNzcyggdGhpcy5hY3RpdmVNZW51WyAwIF0sIFwicGFkZGluZ1RvcFwiICkgKSB8fCAwO1xuXHRcdFx0b2Zmc2V0ID0gaXRlbS5vZmZzZXQoKS50b3AgLSB0aGlzLmFjdGl2ZU1lbnUub2Zmc2V0KCkudG9wIC0gYm9yZGVyVG9wIC0gcGFkZGluZ1RvcDtcblx0XHRcdHNjcm9sbCA9IHRoaXMuYWN0aXZlTWVudS5zY3JvbGxUb3AoKTtcblx0XHRcdGVsZW1lbnRIZWlnaHQgPSB0aGlzLmFjdGl2ZU1lbnUuaGVpZ2h0KCk7XG5cdFx0XHRpdGVtSGVpZ2h0ID0gaXRlbS5vdXRlckhlaWdodCgpO1xuXG5cdFx0XHRpZiAoIG9mZnNldCA8IDAgKSB7XG5cdFx0XHRcdHRoaXMuYWN0aXZlTWVudS5zY3JvbGxUb3AoIHNjcm9sbCArIG9mZnNldCApO1xuXHRcdFx0fSBlbHNlIGlmICggb2Zmc2V0ICsgaXRlbUhlaWdodCA+IGVsZW1lbnRIZWlnaHQgKSB7XG5cdFx0XHRcdHRoaXMuYWN0aXZlTWVudS5zY3JvbGxUb3AoIHNjcm9sbCArIG9mZnNldCAtIGVsZW1lbnRIZWlnaHQgKyBpdGVtSGVpZ2h0ICk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdGJsdXI6IGZ1bmN0aW9uKCBldmVudCwgZnJvbUZvY3VzICkge1xuXHRcdGlmICggIWZyb21Gb2N1cyApIHtcblx0XHRcdGNsZWFyVGltZW91dCggdGhpcy50aW1lciApO1xuXHRcdH1cblxuXHRcdGlmICggIXRoaXMuYWN0aXZlICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0aGlzLmFjdGl2ZS5jaGlsZHJlbiggXCIudWktbWVudS1pdGVtLXdyYXBwZXJcIiApLFxuXHRcdFx0bnVsbCwgXCJ1aS1zdGF0ZS1hY3RpdmVcIiApO1xuXG5cdFx0dGhpcy5fdHJpZ2dlciggXCJibHVyXCIsIGV2ZW50LCB7IGl0ZW06IHRoaXMuYWN0aXZlIH0gKTtcblx0XHR0aGlzLmFjdGl2ZSA9IG51bGw7XG5cdH0sXG5cblx0X3N0YXJ0T3BlbmluZzogZnVuY3Rpb24oIHN1Ym1lbnUgKSB7XG5cdFx0Y2xlYXJUaW1lb3V0KCB0aGlzLnRpbWVyICk7XG5cblx0XHQvLyBEb24ndCBvcGVuIGlmIGFscmVhZHkgb3BlbiBmaXhlcyBhIEZpcmVmb3ggYnVnIHRoYXQgY2F1c2VkIGEgLjUgcGl4ZWxcblx0XHQvLyBzaGlmdCBpbiB0aGUgc3VibWVudSBwb3NpdGlvbiB3aGVuIG1vdXNpbmcgb3ZlciB0aGUgY2FyZXQgaWNvblxuXHRcdGlmICggc3VibWVudS5hdHRyKCBcImFyaWEtaGlkZGVuXCIgKSAhPT0gXCJ0cnVlXCIgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dGhpcy50aW1lciA9IHRoaXMuX2RlbGF5KCBmdW5jdGlvbigpIHtcblx0XHRcdHRoaXMuX2Nsb3NlKCk7XG5cdFx0XHR0aGlzLl9vcGVuKCBzdWJtZW51ICk7XG5cdFx0fSwgdGhpcy5kZWxheSApO1xuXHR9LFxuXG5cdF9vcGVuOiBmdW5jdGlvbiggc3VibWVudSApIHtcblx0XHR2YXIgcG9zaXRpb24gPSAkLmV4dGVuZCgge1xuXHRcdFx0b2Y6IHRoaXMuYWN0aXZlXG5cdFx0fSwgdGhpcy5vcHRpb25zLnBvc2l0aW9uICk7XG5cblx0XHRjbGVhclRpbWVvdXQoIHRoaXMudGltZXIgKTtcblx0XHR0aGlzLmVsZW1lbnQuZmluZCggXCIudWktbWVudVwiICkubm90KCBzdWJtZW51LnBhcmVudHMoIFwiLnVpLW1lbnVcIiApIClcblx0XHRcdC5oaWRlKClcblx0XHRcdC5hdHRyKCBcImFyaWEtaGlkZGVuXCIsIFwidHJ1ZVwiICk7XG5cblx0XHRzdWJtZW51XG5cdFx0XHQuc2hvdygpXG5cdFx0XHQucmVtb3ZlQXR0ciggXCJhcmlhLWhpZGRlblwiIClcblx0XHRcdC5hdHRyKCBcImFyaWEtZXhwYW5kZWRcIiwgXCJ0cnVlXCIgKVxuXHRcdFx0LnBvc2l0aW9uKCBwb3NpdGlvbiApO1xuXHR9LFxuXG5cdGNvbGxhcHNlQWxsOiBmdW5jdGlvbiggZXZlbnQsIGFsbCApIHtcblx0XHRjbGVhclRpbWVvdXQoIHRoaXMudGltZXIgKTtcblx0XHR0aGlzLnRpbWVyID0gdGhpcy5fZGVsYXkoIGZ1bmN0aW9uKCkge1xuXG5cdFx0XHQvLyBJZiB3ZSB3ZXJlIHBhc3NlZCBhbiBldmVudCwgbG9vayBmb3IgdGhlIHN1Ym1lbnUgdGhhdCBjb250YWlucyB0aGUgZXZlbnRcblx0XHRcdHZhciBjdXJyZW50TWVudSA9IGFsbCA/IHRoaXMuZWxlbWVudCA6XG5cdFx0XHRcdCQoIGV2ZW50ICYmIGV2ZW50LnRhcmdldCApLmNsb3Nlc3QoIHRoaXMuZWxlbWVudC5maW5kKCBcIi51aS1tZW51XCIgKSApO1xuXG5cdFx0XHQvLyBJZiB3ZSBmb3VuZCBubyB2YWxpZCBzdWJtZW51IGFuY2VzdG9yLCB1c2UgdGhlIG1haW4gbWVudSB0byBjbG9zZSBhbGxcblx0XHRcdC8vIHN1YiBtZW51cyBhbnl3YXlcblx0XHRcdGlmICggIWN1cnJlbnRNZW51Lmxlbmd0aCApIHtcblx0XHRcdFx0Y3VycmVudE1lbnUgPSB0aGlzLmVsZW1lbnQ7XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX2Nsb3NlKCBjdXJyZW50TWVudSApO1xuXG5cdFx0XHR0aGlzLmJsdXIoIGV2ZW50ICk7XG5cblx0XHRcdC8vIFdvcmsgYXJvdW5kIGFjdGl2ZSBpdGVtIHN0YXlpbmcgYWN0aXZlIGFmdGVyIG1lbnUgaXMgYmx1cnJlZFxuXHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIGN1cnJlbnRNZW51LmZpbmQoIFwiLnVpLXN0YXRlLWFjdGl2ZVwiICksIG51bGwsIFwidWktc3RhdGUtYWN0aXZlXCIgKTtcblxuXHRcdFx0dGhpcy5hY3RpdmVNZW51ID0gY3VycmVudE1lbnU7XG5cdFx0fSwgdGhpcy5kZWxheSApO1xuXHR9LFxuXG5cdC8vIFdpdGggbm8gYXJndW1lbnRzLCBjbG9zZXMgdGhlIGN1cnJlbnRseSBhY3RpdmUgbWVudSAtIGlmIG5vdGhpbmcgaXMgYWN0aXZlXG5cdC8vIGl0IGNsb3NlcyBhbGwgbWVudXMuICBJZiBwYXNzZWQgYW4gYXJndW1lbnQsIGl0IHdpbGwgc2VhcmNoIGZvciBtZW51cyBCRUxPV1xuXHRfY2xvc2U6IGZ1bmN0aW9uKCBzdGFydE1lbnUgKSB7XG5cdFx0aWYgKCAhc3RhcnRNZW51ICkge1xuXHRcdFx0c3RhcnRNZW51ID0gdGhpcy5hY3RpdmUgPyB0aGlzLmFjdGl2ZS5wYXJlbnQoKSA6IHRoaXMuZWxlbWVudDtcblx0XHR9XG5cblx0XHRzdGFydE1lbnUuZmluZCggXCIudWktbWVudVwiIClcblx0XHRcdC5oaWRlKClcblx0XHRcdC5hdHRyKCBcImFyaWEtaGlkZGVuXCIsIFwidHJ1ZVwiIClcblx0XHRcdC5hdHRyKCBcImFyaWEtZXhwYW5kZWRcIiwgXCJmYWxzZVwiICk7XG5cdH0sXG5cblx0X2Nsb3NlT25Eb2N1bWVudENsaWNrOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0cmV0dXJuICEkKCBldmVudC50YXJnZXQgKS5jbG9zZXN0KCBcIi51aS1tZW51XCIgKS5sZW5ndGg7XG5cdH0sXG5cblx0X2lzRGl2aWRlcjogZnVuY3Rpb24oIGl0ZW0gKSB7XG5cblx0XHQvLyBNYXRjaCBoeXBoZW4sIGVtIGRhc2gsIGVuIGRhc2hcblx0XHRyZXR1cm4gIS9bXlxcLVxcdTIwMTRcXHUyMDEzXFxzXS8udGVzdCggaXRlbS50ZXh0KCkgKTtcblx0fSxcblxuXHRjb2xsYXBzZTogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHZhciBuZXdJdGVtID0gdGhpcy5hY3RpdmUgJiZcblx0XHRcdHRoaXMuYWN0aXZlLnBhcmVudCgpLmNsb3Nlc3QoIFwiLnVpLW1lbnUtaXRlbVwiLCB0aGlzLmVsZW1lbnQgKTtcblx0XHRpZiAoIG5ld0l0ZW0gJiYgbmV3SXRlbS5sZW5ndGggKSB7XG5cdFx0XHR0aGlzLl9jbG9zZSgpO1xuXHRcdFx0dGhpcy5mb2N1cyggZXZlbnQsIG5ld0l0ZW0gKTtcblx0XHR9XG5cdH0sXG5cblx0ZXhwYW5kOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dmFyIG5ld0l0ZW0gPSB0aGlzLmFjdGl2ZSAmJlxuXHRcdFx0dGhpcy5hY3RpdmVcblx0XHRcdFx0LmNoaWxkcmVuKCBcIi51aS1tZW51IFwiIClcblx0XHRcdFx0XHQuZmluZCggdGhpcy5vcHRpb25zLml0ZW1zIClcblx0XHRcdFx0XHRcdC5maXJzdCgpO1xuXG5cdFx0aWYgKCBuZXdJdGVtICYmIG5ld0l0ZW0ubGVuZ3RoICkge1xuXHRcdFx0dGhpcy5fb3BlbiggbmV3SXRlbS5wYXJlbnQoKSApO1xuXG5cdFx0XHQvLyBEZWxheSBzbyBGaXJlZm94IHdpbGwgbm90IGhpZGUgYWN0aXZlZGVzY2VuZGFudCBjaGFuZ2UgaW4gZXhwYW5kaW5nIHN1Ym1lbnUgZnJvbSBBVFxuXHRcdFx0dGhpcy5fZGVsYXkoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHR0aGlzLmZvY3VzKCBldmVudCwgbmV3SXRlbSApO1xuXHRcdFx0fSApO1xuXHRcdH1cblx0fSxcblxuXHRuZXh0OiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dGhpcy5fbW92ZSggXCJuZXh0XCIsIFwiZmlyc3RcIiwgZXZlbnQgKTtcblx0fSxcblxuXHRwcmV2aW91czogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHRoaXMuX21vdmUoIFwicHJldlwiLCBcImxhc3RcIiwgZXZlbnQgKTtcblx0fSxcblxuXHRpc0ZpcnN0SXRlbTogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMuYWN0aXZlICYmICF0aGlzLmFjdGl2ZS5wcmV2QWxsKCBcIi51aS1tZW51LWl0ZW1cIiApLmxlbmd0aDtcblx0fSxcblxuXHRpc0xhc3RJdGVtOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5hY3RpdmUgJiYgIXRoaXMuYWN0aXZlLm5leHRBbGwoIFwiLnVpLW1lbnUtaXRlbVwiICkubGVuZ3RoO1xuXHR9LFxuXG5cdF9tb3ZlOiBmdW5jdGlvbiggZGlyZWN0aW9uLCBmaWx0ZXIsIGV2ZW50ICkge1xuXHRcdHZhciBuZXh0O1xuXHRcdGlmICggdGhpcy5hY3RpdmUgKSB7XG5cdFx0XHRpZiAoIGRpcmVjdGlvbiA9PT0gXCJmaXJzdFwiIHx8IGRpcmVjdGlvbiA9PT0gXCJsYXN0XCIgKSB7XG5cdFx0XHRcdG5leHQgPSB0aGlzLmFjdGl2ZVxuXHRcdFx0XHRcdFsgZGlyZWN0aW9uID09PSBcImZpcnN0XCIgPyBcInByZXZBbGxcIiA6IFwibmV4dEFsbFwiIF0oIFwiLnVpLW1lbnUtaXRlbVwiIClcblx0XHRcdFx0XHQuZXEoIC0xICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRuZXh0ID0gdGhpcy5hY3RpdmVcblx0XHRcdFx0XHRbIGRpcmVjdGlvbiArIFwiQWxsXCIgXSggXCIudWktbWVudS1pdGVtXCIgKVxuXHRcdFx0XHRcdC5lcSggMCApO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRpZiAoICFuZXh0IHx8ICFuZXh0Lmxlbmd0aCB8fCAhdGhpcy5hY3RpdmUgKSB7XG5cdFx0XHRuZXh0ID0gdGhpcy5hY3RpdmVNZW51LmZpbmQoIHRoaXMub3B0aW9ucy5pdGVtcyApWyBmaWx0ZXIgXSgpO1xuXHRcdH1cblxuXHRcdHRoaXMuZm9jdXMoIGV2ZW50LCBuZXh0ICk7XG5cdH0sXG5cblx0bmV4dFBhZ2U6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgaXRlbSwgYmFzZSwgaGVpZ2h0O1xuXG5cdFx0aWYgKCAhdGhpcy5hY3RpdmUgKSB7XG5cdFx0XHR0aGlzLm5leHQoIGV2ZW50ICk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGlmICggdGhpcy5pc0xhc3RJdGVtKCkgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGlmICggdGhpcy5faGFzU2Nyb2xsKCkgKSB7XG5cdFx0XHRiYXNlID0gdGhpcy5hY3RpdmUub2Zmc2V0KCkudG9wO1xuXHRcdFx0aGVpZ2h0ID0gdGhpcy5lbGVtZW50LmhlaWdodCgpO1xuXHRcdFx0dGhpcy5hY3RpdmUubmV4dEFsbCggXCIudWktbWVudS1pdGVtXCIgKS5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdFx0aXRlbSA9ICQoIHRoaXMgKTtcblx0XHRcdFx0cmV0dXJuIGl0ZW0ub2Zmc2V0KCkudG9wIC0gYmFzZSAtIGhlaWdodCA8IDA7XG5cdFx0XHR9ICk7XG5cblx0XHRcdHRoaXMuZm9jdXMoIGV2ZW50LCBpdGVtICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuZm9jdXMoIGV2ZW50LCB0aGlzLmFjdGl2ZU1lbnUuZmluZCggdGhpcy5vcHRpb25zLml0ZW1zIClcblx0XHRcdFx0WyAhdGhpcy5hY3RpdmUgPyBcImZpcnN0XCIgOiBcImxhc3RcIiBdKCkgKTtcblx0XHR9XG5cdH0sXG5cblx0cHJldmlvdXNQYWdlOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dmFyIGl0ZW0sIGJhc2UsIGhlaWdodDtcblx0XHRpZiAoICF0aGlzLmFjdGl2ZSApIHtcblx0XHRcdHRoaXMubmV4dCggZXZlbnQgKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0aWYgKCB0aGlzLmlzRmlyc3RJdGVtKCkgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGlmICggdGhpcy5faGFzU2Nyb2xsKCkgKSB7XG5cdFx0XHRiYXNlID0gdGhpcy5hY3RpdmUub2Zmc2V0KCkudG9wO1xuXHRcdFx0aGVpZ2h0ID0gdGhpcy5lbGVtZW50LmhlaWdodCgpO1xuXHRcdFx0dGhpcy5hY3RpdmUucHJldkFsbCggXCIudWktbWVudS1pdGVtXCIgKS5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdFx0aXRlbSA9ICQoIHRoaXMgKTtcblx0XHRcdFx0cmV0dXJuIGl0ZW0ub2Zmc2V0KCkudG9wIC0gYmFzZSArIGhlaWdodCA+IDA7XG5cdFx0XHR9ICk7XG5cblx0XHRcdHRoaXMuZm9jdXMoIGV2ZW50LCBpdGVtICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuZm9jdXMoIGV2ZW50LCB0aGlzLmFjdGl2ZU1lbnUuZmluZCggdGhpcy5vcHRpb25zLml0ZW1zICkuZmlyc3QoKSApO1xuXHRcdH1cblx0fSxcblxuXHRfaGFzU2Nyb2xsOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5lbGVtZW50Lm91dGVySGVpZ2h0KCkgPCB0aGlzLmVsZW1lbnQucHJvcCggXCJzY3JvbGxIZWlnaHRcIiApO1xuXHR9LFxuXG5cdHNlbGVjdDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXG5cdFx0Ly8gVE9ETzogSXQgc2hvdWxkIG5ldmVyIGJlIHBvc3NpYmxlIHRvIG5vdCBoYXZlIGFuIGFjdGl2ZSBpdGVtIGF0IHRoaXNcblx0XHQvLyBwb2ludCwgYnV0IHRoZSB0ZXN0cyBkb24ndCB0cmlnZ2VyIG1vdXNlZW50ZXIgYmVmb3JlIGNsaWNrLlxuXHRcdHRoaXMuYWN0aXZlID0gdGhpcy5hY3RpdmUgfHwgJCggZXZlbnQudGFyZ2V0ICkuY2xvc2VzdCggXCIudWktbWVudS1pdGVtXCIgKTtcblx0XHR2YXIgdWkgPSB7IGl0ZW06IHRoaXMuYWN0aXZlIH07XG5cdFx0aWYgKCAhdGhpcy5hY3RpdmUuaGFzKCBcIi51aS1tZW51XCIgKS5sZW5ndGggKSB7XG5cdFx0XHR0aGlzLmNvbGxhcHNlQWxsKCBldmVudCwgdHJ1ZSApO1xuXHRcdH1cblx0XHR0aGlzLl90cmlnZ2VyKCBcInNlbGVjdFwiLCBldmVudCwgdWkgKTtcblx0fSxcblxuXHRfZmlsdGVyTWVudUl0ZW1zOiBmdW5jdGlvbiggY2hhcmFjdGVyICkge1xuXHRcdHZhciBlc2NhcGVkQ2hhcmFjdGVyID0gY2hhcmFjdGVyLnJlcGxhY2UoIC9bXFwtXFxbXFxde30oKSorPy4sXFxcXFxcXiR8I1xcc10vZywgXCJcXFxcJCZcIiApLFxuXHRcdFx0cmVnZXggPSBuZXcgUmVnRXhwKCBcIl5cIiArIGVzY2FwZWRDaGFyYWN0ZXIsIFwiaVwiICk7XG5cblx0XHRyZXR1cm4gdGhpcy5hY3RpdmVNZW51XG5cdFx0XHQuZmluZCggdGhpcy5vcHRpb25zLml0ZW1zIClcblxuXHRcdFx0XHQvLyBPbmx5IG1hdGNoIG9uIGl0ZW1zLCBub3QgZGl2aWRlcnMgb3Igb3RoZXIgY29udGVudCAoIzEwNTcxKVxuXHRcdFx0XHQuZmlsdGVyKCBcIi51aS1tZW51LWl0ZW1cIiApXG5cdFx0XHRcdFx0LmZpbHRlciggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0XHRyZXR1cm4gcmVnZXgudGVzdChcblx0XHRcdFx0XHRcdFx0JC50cmltKCAkKCB0aGlzICkuY2hpbGRyZW4oIFwiLnVpLW1lbnUtaXRlbS13cmFwcGVyXCIgKS50ZXh0KCkgKSApO1xuXHRcdFx0XHRcdH0gKTtcblx0fVxufSApO1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIEF1dG9jb21wbGV0ZSAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogQXV0b2NvbXBsZXRlXG4vLz4+Z3JvdXA6IFdpZGdldHNcbi8vPj5kZXNjcmlwdGlvbjogTGlzdHMgc3VnZ2VzdGVkIHdvcmRzIGFzIHRoZSB1c2VyIGlzIHR5cGluZy5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9hdXRvY29tcGxldGUvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vYXV0b2NvbXBsZXRlL1xuLy8+PmNzcy5zdHJ1Y3R1cmU6IC4uLy4uL3RoZW1lcy9iYXNlL2NvcmUuY3NzXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2UvYXV0b2NvbXBsZXRlLmNzc1xuLy8+PmNzcy50aGVtZTogLi4vLi4vdGhlbWVzL2Jhc2UvdGhlbWUuY3NzXG5cblxuXG4kLndpZGdldCggXCJ1aS5hdXRvY29tcGxldGVcIiwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHRkZWZhdWx0RWxlbWVudDogXCI8aW5wdXQ+XCIsXG5cdG9wdGlvbnM6IHtcblx0XHRhcHBlbmRUbzogbnVsbCxcblx0XHRhdXRvRm9jdXM6IGZhbHNlLFxuXHRcdGRlbGF5OiAzMDAsXG5cdFx0bWluTGVuZ3RoOiAxLFxuXHRcdHBvc2l0aW9uOiB7XG5cdFx0XHRteTogXCJsZWZ0IHRvcFwiLFxuXHRcdFx0YXQ6IFwibGVmdCBib3R0b21cIixcblx0XHRcdGNvbGxpc2lvbjogXCJub25lXCJcblx0XHR9LFxuXHRcdHNvdXJjZTogbnVsbCxcblxuXHRcdC8vIENhbGxiYWNrc1xuXHRcdGNoYW5nZTogbnVsbCxcblx0XHRjbG9zZTogbnVsbCxcblx0XHRmb2N1czogbnVsbCxcblx0XHRvcGVuOiBudWxsLFxuXHRcdHJlc3BvbnNlOiBudWxsLFxuXHRcdHNlYXJjaDogbnVsbCxcblx0XHRzZWxlY3Q6IG51bGxcblx0fSxcblxuXHRyZXF1ZXN0SW5kZXg6IDAsXG5cdHBlbmRpbmc6IDAsXG5cblx0X2NyZWF0ZTogZnVuY3Rpb24oKSB7XG5cblx0XHQvLyBTb21lIGJyb3dzZXJzIG9ubHkgcmVwZWF0IGtleWRvd24gZXZlbnRzLCBub3Qga2V5cHJlc3MgZXZlbnRzLFxuXHRcdC8vIHNvIHdlIHVzZSB0aGUgc3VwcHJlc3NLZXlQcmVzcyBmbGFnIHRvIGRldGVybWluZSBpZiB3ZSd2ZSBhbHJlYWR5XG5cdFx0Ly8gaGFuZGxlZCB0aGUga2V5ZG93biBldmVudC4gIzcyNjlcblx0XHQvLyBVbmZvcnR1bmF0ZWx5IHRoZSBjb2RlIGZvciAmIGluIGtleXByZXNzIGlzIHRoZSBzYW1lIGFzIHRoZSB1cCBhcnJvdyxcblx0XHQvLyBzbyB3ZSB1c2UgdGhlIHN1cHByZXNzS2V5UHJlc3NSZXBlYXQgZmxhZyB0byBhdm9pZCBoYW5kbGluZyBrZXlwcmVzc1xuXHRcdC8vIGV2ZW50cyB3aGVuIHdlIGtub3cgdGhlIGtleWRvd24gZXZlbnQgd2FzIHVzZWQgdG8gbW9kaWZ5IHRoZVxuXHRcdC8vIHNlYXJjaCB0ZXJtLiAjNzc5OVxuXHRcdHZhciBzdXBwcmVzc0tleVByZXNzLCBzdXBwcmVzc0tleVByZXNzUmVwZWF0LCBzdXBwcmVzc0lucHV0LFxuXHRcdFx0bm9kZU5hbWUgPSB0aGlzLmVsZW1lbnRbIDAgXS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpLFxuXHRcdFx0aXNUZXh0YXJlYSA9IG5vZGVOYW1lID09PSBcInRleHRhcmVhXCIsXG5cdFx0XHRpc0lucHV0ID0gbm9kZU5hbWUgPT09IFwiaW5wdXRcIjtcblxuXHRcdC8vIFRleHRhcmVhcyBhcmUgYWx3YXlzIG11bHRpLWxpbmVcblx0XHQvLyBJbnB1dHMgYXJlIGFsd2F5cyBzaW5nbGUtbGluZSwgZXZlbiBpZiBpbnNpZGUgYSBjb250ZW50RWRpdGFibGUgZWxlbWVudFxuXHRcdC8vIElFIGFsc28gdHJlYXRzIGlucHV0cyBhcyBjb250ZW50RWRpdGFibGVcblx0XHQvLyBBbGwgb3RoZXIgZWxlbWVudCB0eXBlcyBhcmUgZGV0ZXJtaW5lZCBieSB3aGV0aGVyIG9yIG5vdCB0aGV5J3JlIGNvbnRlbnRFZGl0YWJsZVxuXHRcdHRoaXMuaXNNdWx0aUxpbmUgPSBpc1RleHRhcmVhIHx8ICFpc0lucHV0ICYmIHRoaXMuX2lzQ29udGVudEVkaXRhYmxlKCB0aGlzLmVsZW1lbnQgKTtcblxuXHRcdHRoaXMudmFsdWVNZXRob2QgPSB0aGlzLmVsZW1lbnRbIGlzVGV4dGFyZWEgfHwgaXNJbnB1dCA/IFwidmFsXCIgOiBcInRleHRcIiBdO1xuXHRcdHRoaXMuaXNOZXdNZW51ID0gdHJ1ZTtcblxuXHRcdHRoaXMuX2FkZENsYXNzKCBcInVpLWF1dG9jb21wbGV0ZS1pbnB1dFwiICk7XG5cdFx0dGhpcy5lbGVtZW50LmF0dHIoIFwiYXV0b2NvbXBsZXRlXCIsIFwib2ZmXCIgKTtcblxuXHRcdHRoaXMuX29uKCB0aGlzLmVsZW1lbnQsIHtcblx0XHRcdGtleWRvd246IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0aWYgKCB0aGlzLmVsZW1lbnQucHJvcCggXCJyZWFkT25seVwiICkgKSB7XG5cdFx0XHRcdFx0c3VwcHJlc3NLZXlQcmVzcyA9IHRydWU7XG5cdFx0XHRcdFx0c3VwcHJlc3NJbnB1dCA9IHRydWU7XG5cdFx0XHRcdFx0c3VwcHJlc3NLZXlQcmVzc1JlcGVhdCA9IHRydWU7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0c3VwcHJlc3NLZXlQcmVzcyA9IGZhbHNlO1xuXHRcdFx0XHRzdXBwcmVzc0lucHV0ID0gZmFsc2U7XG5cdFx0XHRcdHN1cHByZXNzS2V5UHJlc3NSZXBlYXQgPSBmYWxzZTtcblx0XHRcdFx0dmFyIGtleUNvZGUgPSAkLnVpLmtleUNvZGU7XG5cdFx0XHRcdHN3aXRjaCAoIGV2ZW50LmtleUNvZGUgKSB7XG5cdFx0XHRcdGNhc2Uga2V5Q29kZS5QQUdFX1VQOlxuXHRcdFx0XHRcdHN1cHByZXNzS2V5UHJlc3MgPSB0cnVlO1xuXHRcdFx0XHRcdHRoaXMuX21vdmUoIFwicHJldmlvdXNQYWdlXCIsIGV2ZW50ICk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2Uga2V5Q29kZS5QQUdFX0RPV046XG5cdFx0XHRcdFx0c3VwcHJlc3NLZXlQcmVzcyA9IHRydWU7XG5cdFx0XHRcdFx0dGhpcy5fbW92ZSggXCJuZXh0UGFnZVwiLCBldmVudCApO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIGtleUNvZGUuVVA6XG5cdFx0XHRcdFx0c3VwcHJlc3NLZXlQcmVzcyA9IHRydWU7XG5cdFx0XHRcdFx0dGhpcy5fa2V5RXZlbnQoIFwicHJldmlvdXNcIiwgZXZlbnQgKTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSBrZXlDb2RlLkRPV046XG5cdFx0XHRcdFx0c3VwcHJlc3NLZXlQcmVzcyA9IHRydWU7XG5cdFx0XHRcdFx0dGhpcy5fa2V5RXZlbnQoIFwibmV4dFwiLCBldmVudCApO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIGtleUNvZGUuRU5URVI6XG5cblx0XHRcdFx0XHQvLyB3aGVuIG1lbnUgaXMgb3BlbiBhbmQgaGFzIGZvY3VzXG5cdFx0XHRcdFx0aWYgKCB0aGlzLm1lbnUuYWN0aXZlICkge1xuXG5cdFx0XHRcdFx0XHQvLyAjNjA1NSAtIE9wZXJhIHN0aWxsIGFsbG93cyB0aGUga2V5cHJlc3MgdG8gb2NjdXJcblx0XHRcdFx0XHRcdC8vIHdoaWNoIGNhdXNlcyBmb3JtcyB0byBzdWJtaXRcblx0XHRcdFx0XHRcdHN1cHByZXNzS2V5UHJlc3MgPSB0cnVlO1xuXHRcdFx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0XHRcdHRoaXMubWVudS5zZWxlY3QoIGV2ZW50ICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIGtleUNvZGUuVEFCOlxuXHRcdFx0XHRcdGlmICggdGhpcy5tZW51LmFjdGl2ZSApIHtcblx0XHRcdFx0XHRcdHRoaXMubWVudS5zZWxlY3QoIGV2ZW50ICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIGtleUNvZGUuRVNDQVBFOlxuXHRcdFx0XHRcdGlmICggdGhpcy5tZW51LmVsZW1lbnQuaXMoIFwiOnZpc2libGVcIiApICkge1xuXHRcdFx0XHRcdFx0aWYgKCAhdGhpcy5pc011bHRpTGluZSApIHtcblx0XHRcdFx0XHRcdFx0dGhpcy5fdmFsdWUoIHRoaXMudGVybSApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0dGhpcy5jbG9zZSggZXZlbnQgKTtcblxuXHRcdFx0XHRcdFx0Ly8gRGlmZmVyZW50IGJyb3dzZXJzIGhhdmUgZGlmZmVyZW50IGRlZmF1bHQgYmVoYXZpb3IgZm9yIGVzY2FwZVxuXHRcdFx0XHRcdFx0Ly8gU2luZ2xlIHByZXNzIGNhbiBtZWFuIHVuZG8gb3IgY2xlYXJcblx0XHRcdFx0XHRcdC8vIERvdWJsZSBwcmVzcyBpbiBJRSBtZWFucyBjbGVhciB0aGUgd2hvbGUgZm9ybVxuXHRcdFx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGRlZmF1bHQ6XG5cdFx0XHRcdFx0c3VwcHJlc3NLZXlQcmVzc1JlcGVhdCA9IHRydWU7XG5cblx0XHRcdFx0XHQvLyBzZWFyY2ggdGltZW91dCBzaG91bGQgYmUgdHJpZ2dlcmVkIGJlZm9yZSB0aGUgaW5wdXQgdmFsdWUgaXMgY2hhbmdlZFxuXHRcdFx0XHRcdHRoaXMuX3NlYXJjaFRpbWVvdXQoIGV2ZW50ICk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdH1cblx0XHRcdH0sXG5cdFx0XHRrZXlwcmVzczogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0XHRpZiAoIHN1cHByZXNzS2V5UHJlc3MgKSB7XG5cdFx0XHRcdFx0c3VwcHJlc3NLZXlQcmVzcyA9IGZhbHNlO1xuXHRcdFx0XHRcdGlmICggIXRoaXMuaXNNdWx0aUxpbmUgfHwgdGhpcy5tZW51LmVsZW1lbnQuaXMoIFwiOnZpc2libGVcIiApICkge1xuXHRcdFx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmICggc3VwcHJlc3NLZXlQcmVzc1JlcGVhdCApIHtcblx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBSZXBsaWNhdGUgc29tZSBrZXkgaGFuZGxlcnMgdG8gYWxsb3cgdGhlbSB0byByZXBlYXQgaW4gRmlyZWZveCBhbmQgT3BlcmFcblx0XHRcdFx0dmFyIGtleUNvZGUgPSAkLnVpLmtleUNvZGU7XG5cdFx0XHRcdHN3aXRjaCAoIGV2ZW50LmtleUNvZGUgKSB7XG5cdFx0XHRcdGNhc2Uga2V5Q29kZS5QQUdFX1VQOlxuXHRcdFx0XHRcdHRoaXMuX21vdmUoIFwicHJldmlvdXNQYWdlXCIsIGV2ZW50ICk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2Uga2V5Q29kZS5QQUdFX0RPV046XG5cdFx0XHRcdFx0dGhpcy5fbW92ZSggXCJuZXh0UGFnZVwiLCBldmVudCApO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIGtleUNvZGUuVVA6XG5cdFx0XHRcdFx0dGhpcy5fa2V5RXZlbnQoIFwicHJldmlvdXNcIiwgZXZlbnQgKTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSBrZXlDb2RlLkRPV046XG5cdFx0XHRcdFx0dGhpcy5fa2V5RXZlbnQoIFwibmV4dFwiLCBldmVudCApO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHR9XG5cdFx0XHR9LFxuXHRcdFx0aW5wdXQ6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0aWYgKCBzdXBwcmVzc0lucHV0ICkge1xuXHRcdFx0XHRcdHN1cHByZXNzSW5wdXQgPSBmYWxzZTtcblx0XHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXHRcdFx0XHR0aGlzLl9zZWFyY2hUaW1lb3V0KCBldmVudCApO1xuXHRcdFx0fSxcblx0XHRcdGZvY3VzOiBmdW5jdGlvbigpIHtcblx0XHRcdFx0dGhpcy5zZWxlY3RlZEl0ZW0gPSBudWxsO1xuXHRcdFx0XHR0aGlzLnByZXZpb3VzID0gdGhpcy5fdmFsdWUoKTtcblx0XHRcdH0sXG5cdFx0XHRibHVyOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdGlmICggdGhpcy5jYW5jZWxCbHVyICkge1xuXHRcdFx0XHRcdGRlbGV0ZSB0aGlzLmNhbmNlbEJsdXI7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Y2xlYXJUaW1lb3V0KCB0aGlzLnNlYXJjaGluZyApO1xuXHRcdFx0XHR0aGlzLmNsb3NlKCBldmVudCApO1xuXHRcdFx0XHR0aGlzLl9jaGFuZ2UoIGV2ZW50ICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdFx0dGhpcy5faW5pdFNvdXJjZSgpO1xuXHRcdHRoaXMubWVudSA9ICQoIFwiPHVsPlwiIClcblx0XHRcdC5hcHBlbmRUbyggdGhpcy5fYXBwZW5kVG8oKSApXG5cdFx0XHQubWVudSgge1xuXG5cdFx0XHRcdC8vIGRpc2FibGUgQVJJQSBzdXBwb3J0LCB0aGUgbGl2ZSByZWdpb24gdGFrZXMgY2FyZSBvZiB0aGF0XG5cdFx0XHRcdHJvbGU6IG51bGxcblx0XHRcdH0gKVxuXHRcdFx0LmhpZGUoKVxuXHRcdFx0Lm1lbnUoIFwiaW5zdGFuY2VcIiApO1xuXG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMubWVudS5lbGVtZW50LCBcInVpLWF1dG9jb21wbGV0ZVwiLCBcInVpLWZyb250XCIgKTtcblx0XHR0aGlzLl9vbiggdGhpcy5tZW51LmVsZW1lbnQsIHtcblx0XHRcdG1vdXNlZG93bjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXG5cdFx0XHRcdC8vIHByZXZlbnQgbW92aW5nIGZvY3VzIG91dCBvZiB0aGUgdGV4dCBmaWVsZFxuXHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXG5cdFx0XHRcdC8vIElFIGRvZXNuJ3QgcHJldmVudCBtb3ZpbmcgZm9jdXMgZXZlbiB3aXRoIGV2ZW50LnByZXZlbnREZWZhdWx0KClcblx0XHRcdFx0Ly8gc28gd2Ugc2V0IGEgZmxhZyB0byBrbm93IHdoZW4gd2Ugc2hvdWxkIGlnbm9yZSB0aGUgYmx1ciBldmVudFxuXHRcdFx0XHR0aGlzLmNhbmNlbEJsdXIgPSB0cnVlO1xuXHRcdFx0XHR0aGlzLl9kZWxheSggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0ZGVsZXRlIHRoaXMuY2FuY2VsQmx1cjtcblxuXHRcdFx0XHRcdC8vIFN1cHBvcnQ6IElFIDggb25seVxuXHRcdFx0XHRcdC8vIFJpZ2h0IGNsaWNraW5nIGEgbWVudSBpdGVtIG9yIHNlbGVjdGluZyB0ZXh0IGZyb20gdGhlIG1lbnUgaXRlbXMgd2lsbFxuXHRcdFx0XHRcdC8vIHJlc3VsdCBpbiBmb2N1cyBtb3Zpbmcgb3V0IG9mIHRoZSBpbnB1dC4gSG93ZXZlciwgd2UndmUgYWxyZWFkeSByZWNlaXZlZFxuXHRcdFx0XHRcdC8vIGFuZCBpZ25vcmVkIHRoZSBibHVyIGV2ZW50IGJlY2F1c2Ugb2YgdGhlIGNhbmNlbEJsdXIgZmxhZyBzZXQgYWJvdmUuIFNvXG5cdFx0XHRcdFx0Ly8gd2UgcmVzdG9yZSBmb2N1cyB0byBlbnN1cmUgdGhhdCB0aGUgbWVudSBjbG9zZXMgcHJvcGVybHkgYmFzZWQgb24gdGhlIHVzZXInc1xuXHRcdFx0XHRcdC8vIG5leHQgYWN0aW9ucy5cblx0XHRcdFx0XHRpZiAoIHRoaXMuZWxlbWVudFsgMCBdICE9PSAkLnVpLnNhZmVBY3RpdmVFbGVtZW50KCB0aGlzLmRvY3VtZW50WyAwIF0gKSApIHtcblx0XHRcdFx0XHRcdHRoaXMuZWxlbWVudC50cmlnZ2VyKCBcImZvY3VzXCIgKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gKTtcblx0XHRcdH0sXG5cdFx0XHRtZW51Zm9jdXM6IGZ1bmN0aW9uKCBldmVudCwgdWkgKSB7XG5cdFx0XHRcdHZhciBsYWJlbCwgaXRlbTtcblxuXHRcdFx0XHQvLyBzdXBwb3J0OiBGaXJlZm94XG5cdFx0XHRcdC8vIFByZXZlbnQgYWNjaWRlbnRhbCBhY3RpdmF0aW9uIG9mIG1lbnUgaXRlbXMgaW4gRmlyZWZveCAoIzcwMjQgIzkxMTgpXG5cdFx0XHRcdGlmICggdGhpcy5pc05ld01lbnUgKSB7XG5cdFx0XHRcdFx0dGhpcy5pc05ld01lbnUgPSBmYWxzZTtcblx0XHRcdFx0XHRpZiAoIGV2ZW50Lm9yaWdpbmFsRXZlbnQgJiYgL15tb3VzZS8udGVzdCggZXZlbnQub3JpZ2luYWxFdmVudC50eXBlICkgKSB7XG5cdFx0XHRcdFx0XHR0aGlzLm1lbnUuYmx1cigpO1xuXG5cdFx0XHRcdFx0XHR0aGlzLmRvY3VtZW50Lm9uZSggXCJtb3VzZW1vdmVcIiwgZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0XHRcdCQoIGV2ZW50LnRhcmdldCApLnRyaWdnZXIoIGV2ZW50Lm9yaWdpbmFsRXZlbnQgKTtcblx0XHRcdFx0XHRcdH0gKTtcblxuXHRcdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGl0ZW0gPSB1aS5pdGVtLmRhdGEoIFwidWktYXV0b2NvbXBsZXRlLWl0ZW1cIiApO1xuXHRcdFx0XHRpZiAoIGZhbHNlICE9PSB0aGlzLl90cmlnZ2VyKCBcImZvY3VzXCIsIGV2ZW50LCB7IGl0ZW06IGl0ZW0gfSApICkge1xuXG5cdFx0XHRcdFx0Ly8gdXNlIHZhbHVlIHRvIG1hdGNoIHdoYXQgd2lsbCBlbmQgdXAgaW4gdGhlIGlucHV0LCBpZiBpdCB3YXMgYSBrZXkgZXZlbnRcblx0XHRcdFx0XHRpZiAoIGV2ZW50Lm9yaWdpbmFsRXZlbnQgJiYgL15rZXkvLnRlc3QoIGV2ZW50Lm9yaWdpbmFsRXZlbnQudHlwZSApICkge1xuXHRcdFx0XHRcdFx0dGhpcy5fdmFsdWUoIGl0ZW0udmFsdWUgKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBBbm5vdW5jZSB0aGUgdmFsdWUgaW4gdGhlIGxpdmVSZWdpb25cblx0XHRcdFx0bGFiZWwgPSB1aS5pdGVtLmF0dHIoIFwiYXJpYS1sYWJlbFwiICkgfHwgaXRlbS52YWx1ZTtcblx0XHRcdFx0aWYgKCBsYWJlbCAmJiAkLnRyaW0oIGxhYmVsICkubGVuZ3RoICkge1xuXHRcdFx0XHRcdHRoaXMubGl2ZVJlZ2lvbi5jaGlsZHJlbigpLmhpZGUoKTtcblx0XHRcdFx0XHQkKCBcIjxkaXY+XCIgKS50ZXh0KCBsYWJlbCApLmFwcGVuZFRvKCB0aGlzLmxpdmVSZWdpb24gKTtcblx0XHRcdFx0fVxuXHRcdFx0fSxcblx0XHRcdG1lbnVzZWxlY3Q6IGZ1bmN0aW9uKCBldmVudCwgdWkgKSB7XG5cdFx0XHRcdHZhciBpdGVtID0gdWkuaXRlbS5kYXRhKCBcInVpLWF1dG9jb21wbGV0ZS1pdGVtXCIgKSxcblx0XHRcdFx0XHRwcmV2aW91cyA9IHRoaXMucHJldmlvdXM7XG5cblx0XHRcdFx0Ly8gT25seSB0cmlnZ2VyIHdoZW4gZm9jdXMgd2FzIGxvc3QgKGNsaWNrIG9uIG1lbnUpXG5cdFx0XHRcdGlmICggdGhpcy5lbGVtZW50WyAwIF0gIT09ICQudWkuc2FmZUFjdGl2ZUVsZW1lbnQoIHRoaXMuZG9jdW1lbnRbIDAgXSApICkge1xuXHRcdFx0XHRcdHRoaXMuZWxlbWVudC50cmlnZ2VyKCBcImZvY3VzXCIgKTtcblx0XHRcdFx0XHR0aGlzLnByZXZpb3VzID0gcHJldmlvdXM7XG5cblx0XHRcdFx0XHQvLyAjNjEwOSAtIElFIHRyaWdnZXJzIHR3byBmb2N1cyBldmVudHMgYW5kIHRoZSBzZWNvbmRcblx0XHRcdFx0XHQvLyBpcyBhc3luY2hyb25vdXMsIHNvIHdlIG5lZWQgdG8gcmVzZXQgdGhlIHByZXZpb3VzXG5cdFx0XHRcdFx0Ly8gdGVybSBzeW5jaHJvbm91c2x5IGFuZCBhc3luY2hyb25vdXNseSA6LShcblx0XHRcdFx0XHR0aGlzLl9kZWxheSggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0XHR0aGlzLnByZXZpb3VzID0gcHJldmlvdXM7XG5cdFx0XHRcdFx0XHR0aGlzLnNlbGVjdGVkSXRlbSA9IGl0ZW07XG5cdFx0XHRcdFx0fSApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBmYWxzZSAhPT0gdGhpcy5fdHJpZ2dlciggXCJzZWxlY3RcIiwgZXZlbnQsIHsgaXRlbTogaXRlbSB9ICkgKSB7XG5cdFx0XHRcdFx0dGhpcy5fdmFsdWUoIGl0ZW0udmFsdWUgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIHJlc2V0IHRoZSB0ZXJtIGFmdGVyIHRoZSBzZWxlY3QgZXZlbnRcblx0XHRcdFx0Ly8gdGhpcyBhbGxvd3MgY3VzdG9tIHNlbGVjdCBoYW5kbGluZyB0byB3b3JrIHByb3Blcmx5XG5cdFx0XHRcdHRoaXMudGVybSA9IHRoaXMuX3ZhbHVlKCk7XG5cblx0XHRcdFx0dGhpcy5jbG9zZSggZXZlbnQgKTtcblx0XHRcdFx0dGhpcy5zZWxlY3RlZEl0ZW0gPSBpdGVtO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHRcdHRoaXMubGl2ZVJlZ2lvbiA9ICQoIFwiPGRpdj5cIiwge1xuXHRcdFx0cm9sZTogXCJzdGF0dXNcIixcblx0XHRcdFwiYXJpYS1saXZlXCI6IFwiYXNzZXJ0aXZlXCIsXG5cdFx0XHRcImFyaWEtcmVsZXZhbnRcIjogXCJhZGRpdGlvbnNcIlxuXHRcdH0gKVxuXHRcdFx0LmFwcGVuZFRvKCB0aGlzLmRvY3VtZW50WyAwIF0uYm9keSApO1xuXG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMubGl2ZVJlZ2lvbiwgbnVsbCwgXCJ1aS1oZWxwZXItaGlkZGVuLWFjY2Vzc2libGVcIiApO1xuXG5cdFx0Ly8gVHVybmluZyBvZmYgYXV0b2NvbXBsZXRlIHByZXZlbnRzIHRoZSBicm93c2VyIGZyb20gcmVtZW1iZXJpbmcgdGhlXG5cdFx0Ly8gdmFsdWUgd2hlbiBuYXZpZ2F0aW5nIHRocm91Z2ggaGlzdG9yeSwgc28gd2UgcmUtZW5hYmxlIGF1dG9jb21wbGV0ZVxuXHRcdC8vIGlmIHRoZSBwYWdlIGlzIHVubG9hZGVkIGJlZm9yZSB0aGUgd2lkZ2V0IGlzIGRlc3Ryb3llZC4gIzc3OTBcblx0XHR0aGlzLl9vbiggdGhpcy53aW5kb3csIHtcblx0XHRcdGJlZm9yZXVubG9hZDogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHRoaXMuZWxlbWVudC5yZW1vdmVBdHRyKCBcImF1dG9jb21wbGV0ZVwiICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXHR9LFxuXG5cdF9kZXN0cm95OiBmdW5jdGlvbigpIHtcblx0XHRjbGVhclRpbWVvdXQoIHRoaXMuc2VhcmNoaW5nICk7XG5cdFx0dGhpcy5lbGVtZW50LnJlbW92ZUF0dHIoIFwiYXV0b2NvbXBsZXRlXCIgKTtcblx0XHR0aGlzLm1lbnUuZWxlbWVudC5yZW1vdmUoKTtcblx0XHR0aGlzLmxpdmVSZWdpb24ucmVtb3ZlKCk7XG5cdH0sXG5cblx0X3NldE9wdGlvbjogZnVuY3Rpb24oIGtleSwgdmFsdWUgKSB7XG5cdFx0dGhpcy5fc3VwZXIoIGtleSwgdmFsdWUgKTtcblx0XHRpZiAoIGtleSA9PT0gXCJzb3VyY2VcIiApIHtcblx0XHRcdHRoaXMuX2luaXRTb3VyY2UoKTtcblx0XHR9XG5cdFx0aWYgKCBrZXkgPT09IFwiYXBwZW5kVG9cIiApIHtcblx0XHRcdHRoaXMubWVudS5lbGVtZW50LmFwcGVuZFRvKCB0aGlzLl9hcHBlbmRUbygpICk7XG5cdFx0fVxuXHRcdGlmICgga2V5ID09PSBcImRpc2FibGVkXCIgJiYgdmFsdWUgJiYgdGhpcy54aHIgKSB7XG5cdFx0XHR0aGlzLnhoci5hYm9ydCgpO1xuXHRcdH1cblx0fSxcblxuXHRfaXNFdmVudFRhcmdldEluV2lkZ2V0OiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dmFyIG1lbnVFbGVtZW50ID0gdGhpcy5tZW51LmVsZW1lbnRbIDAgXTtcblxuXHRcdHJldHVybiBldmVudC50YXJnZXQgPT09IHRoaXMuZWxlbWVudFsgMCBdIHx8XG5cdFx0XHRldmVudC50YXJnZXQgPT09IG1lbnVFbGVtZW50IHx8XG5cdFx0XHQkLmNvbnRhaW5zKCBtZW51RWxlbWVudCwgZXZlbnQudGFyZ2V0ICk7XG5cdH0sXG5cblx0X2Nsb3NlT25DbGlja091dHNpZGU6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRpZiAoICF0aGlzLl9pc0V2ZW50VGFyZ2V0SW5XaWRnZXQoIGV2ZW50ICkgKSB7XG5cdFx0XHR0aGlzLmNsb3NlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9hcHBlbmRUbzogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGVsZW1lbnQgPSB0aGlzLm9wdGlvbnMuYXBwZW5kVG87XG5cblx0XHRpZiAoIGVsZW1lbnQgKSB7XG5cdFx0XHRlbGVtZW50ID0gZWxlbWVudC5qcXVlcnkgfHwgZWxlbWVudC5ub2RlVHlwZSA/XG5cdFx0XHRcdCQoIGVsZW1lbnQgKSA6XG5cdFx0XHRcdHRoaXMuZG9jdW1lbnQuZmluZCggZWxlbWVudCApLmVxKCAwICk7XG5cdFx0fVxuXG5cdFx0aWYgKCAhZWxlbWVudCB8fCAhZWxlbWVudFsgMCBdICkge1xuXHRcdFx0ZWxlbWVudCA9IHRoaXMuZWxlbWVudC5jbG9zZXN0KCBcIi51aS1mcm9udCwgZGlhbG9nXCIgKTtcblx0XHR9XG5cblx0XHRpZiAoICFlbGVtZW50Lmxlbmd0aCApIHtcblx0XHRcdGVsZW1lbnQgPSB0aGlzLmRvY3VtZW50WyAwIF0uYm9keTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZWxlbWVudDtcblx0fSxcblxuXHRfaW5pdFNvdXJjZTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGFycmF5LCB1cmwsXG5cdFx0XHR0aGF0ID0gdGhpcztcblx0XHRpZiAoICQuaXNBcnJheSggdGhpcy5vcHRpb25zLnNvdXJjZSApICkge1xuXHRcdFx0YXJyYXkgPSB0aGlzLm9wdGlvbnMuc291cmNlO1xuXHRcdFx0dGhpcy5zb3VyY2UgPSBmdW5jdGlvbiggcmVxdWVzdCwgcmVzcG9uc2UgKSB7XG5cdFx0XHRcdHJlc3BvbnNlKCAkLnVpLmF1dG9jb21wbGV0ZS5maWx0ZXIoIGFycmF5LCByZXF1ZXN0LnRlcm0gKSApO1xuXHRcdFx0fTtcblx0XHR9IGVsc2UgaWYgKCB0eXBlb2YgdGhpcy5vcHRpb25zLnNvdXJjZSA9PT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdHVybCA9IHRoaXMub3B0aW9ucy5zb3VyY2U7XG5cdFx0XHR0aGlzLnNvdXJjZSA9IGZ1bmN0aW9uKCByZXF1ZXN0LCByZXNwb25zZSApIHtcblx0XHRcdFx0aWYgKCB0aGF0LnhociApIHtcblx0XHRcdFx0XHR0aGF0Lnhoci5hYm9ydCgpO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHRoYXQueGhyID0gJC5hamF4KCB7XG5cdFx0XHRcdFx0dXJsOiB1cmwsXG5cdFx0XHRcdFx0ZGF0YTogcmVxdWVzdCxcblx0XHRcdFx0XHRkYXRhVHlwZTogXCJqc29uXCIsXG5cdFx0XHRcdFx0c3VjY2VzczogZnVuY3Rpb24oIGRhdGEgKSB7XG5cdFx0XHRcdFx0XHRyZXNwb25zZSggZGF0YSApO1xuXHRcdFx0XHRcdH0sXG5cdFx0XHRcdFx0ZXJyb3I6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdFx0cmVzcG9uc2UoIFtdICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9ICk7XG5cdFx0XHR9O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLnNvdXJjZSA9IHRoaXMub3B0aW9ucy5zb3VyY2U7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZWFyY2hUaW1lb3V0OiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0Y2xlYXJUaW1lb3V0KCB0aGlzLnNlYXJjaGluZyApO1xuXHRcdHRoaXMuc2VhcmNoaW5nID0gdGhpcy5fZGVsYXkoIGZ1bmN0aW9uKCkge1xuXG5cdFx0XHQvLyBTZWFyY2ggaWYgdGhlIHZhbHVlIGhhcyBjaGFuZ2VkLCBvciBpZiB0aGUgdXNlciByZXR5cGVzIHRoZSBzYW1lIHZhbHVlIChzZWUgIzc0MzQpXG5cdFx0XHR2YXIgZXF1YWxWYWx1ZXMgPSB0aGlzLnRlcm0gPT09IHRoaXMuX3ZhbHVlKCksXG5cdFx0XHRcdG1lbnVWaXNpYmxlID0gdGhpcy5tZW51LmVsZW1lbnQuaXMoIFwiOnZpc2libGVcIiApLFxuXHRcdFx0XHRtb2RpZmllcktleSA9IGV2ZW50LmFsdEtleSB8fCBldmVudC5jdHJsS2V5IHx8IGV2ZW50Lm1ldGFLZXkgfHwgZXZlbnQuc2hpZnRLZXk7XG5cblx0XHRcdGlmICggIWVxdWFsVmFsdWVzIHx8ICggZXF1YWxWYWx1ZXMgJiYgIW1lbnVWaXNpYmxlICYmICFtb2RpZmllcktleSApICkge1xuXHRcdFx0XHR0aGlzLnNlbGVjdGVkSXRlbSA9IG51bGw7XG5cdFx0XHRcdHRoaXMuc2VhcmNoKCBudWxsLCBldmVudCApO1xuXHRcdFx0fVxuXHRcdH0sIHRoaXMub3B0aW9ucy5kZWxheSApO1xuXHR9LFxuXG5cdHNlYXJjaDogZnVuY3Rpb24oIHZhbHVlLCBldmVudCApIHtcblx0XHR2YWx1ZSA9IHZhbHVlICE9IG51bGwgPyB2YWx1ZSA6IHRoaXMuX3ZhbHVlKCk7XG5cblx0XHQvLyBBbHdheXMgc2F2ZSB0aGUgYWN0dWFsIHZhbHVlLCBub3QgdGhlIG9uZSBwYXNzZWQgYXMgYW4gYXJndW1lbnRcblx0XHR0aGlzLnRlcm0gPSB0aGlzLl92YWx1ZSgpO1xuXG5cdFx0aWYgKCB2YWx1ZS5sZW5ndGggPCB0aGlzLm9wdGlvbnMubWluTGVuZ3RoICkge1xuXHRcdFx0cmV0dXJuIHRoaXMuY2xvc2UoIGV2ZW50ICk7XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLl90cmlnZ2VyKCBcInNlYXJjaFwiLCBldmVudCApID09PSBmYWxzZSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcy5fc2VhcmNoKCB2YWx1ZSApO1xuXHR9LFxuXG5cdF9zZWFyY2g6IGZ1bmN0aW9uKCB2YWx1ZSApIHtcblx0XHR0aGlzLnBlbmRpbmcrKztcblx0XHR0aGlzLl9hZGRDbGFzcyggXCJ1aS1hdXRvY29tcGxldGUtbG9hZGluZ1wiICk7XG5cdFx0dGhpcy5jYW5jZWxTZWFyY2ggPSBmYWxzZTtcblxuXHRcdHRoaXMuc291cmNlKCB7IHRlcm06IHZhbHVlIH0sIHRoaXMuX3Jlc3BvbnNlKCkgKTtcblx0fSxcblxuXHRfcmVzcG9uc2U6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBpbmRleCA9ICsrdGhpcy5yZXF1ZXN0SW5kZXg7XG5cblx0XHRyZXR1cm4gJC5wcm94eSggZnVuY3Rpb24oIGNvbnRlbnQgKSB7XG5cdFx0XHRpZiAoIGluZGV4ID09PSB0aGlzLnJlcXVlc3RJbmRleCApIHtcblx0XHRcdFx0dGhpcy5fX3Jlc3BvbnNlKCBjb250ZW50ICk7XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMucGVuZGluZy0tO1xuXHRcdFx0aWYgKCAhdGhpcy5wZW5kaW5nICkge1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVDbGFzcyggXCJ1aS1hdXRvY29tcGxldGUtbG9hZGluZ1wiICk7XG5cdFx0XHR9XG5cdFx0fSwgdGhpcyApO1xuXHR9LFxuXG5cdF9fcmVzcG9uc2U6IGZ1bmN0aW9uKCBjb250ZW50ICkge1xuXHRcdGlmICggY29udGVudCApIHtcblx0XHRcdGNvbnRlbnQgPSB0aGlzLl9ub3JtYWxpemUoIGNvbnRlbnQgKTtcblx0XHR9XG5cdFx0dGhpcy5fdHJpZ2dlciggXCJyZXNwb25zZVwiLCBudWxsLCB7IGNvbnRlbnQ6IGNvbnRlbnQgfSApO1xuXHRcdGlmICggIXRoaXMub3B0aW9ucy5kaXNhYmxlZCAmJiBjb250ZW50ICYmIGNvbnRlbnQubGVuZ3RoICYmICF0aGlzLmNhbmNlbFNlYXJjaCApIHtcblx0XHRcdHRoaXMuX3N1Z2dlc3QoIGNvbnRlbnQgKTtcblx0XHRcdHRoaXMuX3RyaWdnZXIoIFwib3BlblwiICk7XG5cdFx0fSBlbHNlIHtcblxuXHRcdFx0Ly8gdXNlIC5fY2xvc2UoKSBpbnN0ZWFkIG9mIC5jbG9zZSgpIHNvIHdlIGRvbid0IGNhbmNlbCBmdXR1cmUgc2VhcmNoZXNcblx0XHRcdHRoaXMuX2Nsb3NlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdGNsb3NlOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dGhpcy5jYW5jZWxTZWFyY2ggPSB0cnVlO1xuXHRcdHRoaXMuX2Nsb3NlKCBldmVudCApO1xuXHR9LFxuXG5cdF9jbG9zZTogZnVuY3Rpb24oIGV2ZW50ICkge1xuXG5cdFx0Ly8gUmVtb3ZlIHRoZSBoYW5kbGVyIHRoYXQgY2xvc2VzIHRoZSBtZW51IG9uIG91dHNpZGUgY2xpY2tzXG5cdFx0dGhpcy5fb2ZmKCB0aGlzLmRvY3VtZW50LCBcIm1vdXNlZG93blwiICk7XG5cblx0XHRpZiAoIHRoaXMubWVudS5lbGVtZW50LmlzKCBcIjp2aXNpYmxlXCIgKSApIHtcblx0XHRcdHRoaXMubWVudS5lbGVtZW50LmhpZGUoKTtcblx0XHRcdHRoaXMubWVudS5ibHVyKCk7XG5cdFx0XHR0aGlzLmlzTmV3TWVudSA9IHRydWU7XG5cdFx0XHR0aGlzLl90cmlnZ2VyKCBcImNsb3NlXCIsIGV2ZW50ICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9jaGFuZ2U6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRpZiAoIHRoaXMucHJldmlvdXMgIT09IHRoaXMuX3ZhbHVlKCkgKSB7XG5cdFx0XHR0aGlzLl90cmlnZ2VyKCBcImNoYW5nZVwiLCBldmVudCwgeyBpdGVtOiB0aGlzLnNlbGVjdGVkSXRlbSB9ICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9ub3JtYWxpemU6IGZ1bmN0aW9uKCBpdGVtcyApIHtcblxuXHRcdC8vIGFzc3VtZSBhbGwgaXRlbXMgaGF2ZSB0aGUgcmlnaHQgZm9ybWF0IHdoZW4gdGhlIGZpcnN0IGl0ZW0gaXMgY29tcGxldGVcblx0XHRpZiAoIGl0ZW1zLmxlbmd0aCAmJiBpdGVtc1sgMCBdLmxhYmVsICYmIGl0ZW1zWyAwIF0udmFsdWUgKSB7XG5cdFx0XHRyZXR1cm4gaXRlbXM7XG5cdFx0fVxuXHRcdHJldHVybiAkLm1hcCggaXRlbXMsIGZ1bmN0aW9uKCBpdGVtICkge1xuXHRcdFx0aWYgKCB0eXBlb2YgaXRlbSA9PT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdFx0cmV0dXJuIHtcblx0XHRcdFx0XHRsYWJlbDogaXRlbSxcblx0XHRcdFx0XHR2YWx1ZTogaXRlbVxuXHRcdFx0XHR9O1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuICQuZXh0ZW5kKCB7fSwgaXRlbSwge1xuXHRcdFx0XHRsYWJlbDogaXRlbS5sYWJlbCB8fCBpdGVtLnZhbHVlLFxuXHRcdFx0XHR2YWx1ZTogaXRlbS52YWx1ZSB8fCBpdGVtLmxhYmVsXG5cdFx0XHR9ICk7XG5cdFx0fSApO1xuXHR9LFxuXG5cdF9zdWdnZXN0OiBmdW5jdGlvbiggaXRlbXMgKSB7XG5cdFx0dmFyIHVsID0gdGhpcy5tZW51LmVsZW1lbnQuZW1wdHkoKTtcblx0XHR0aGlzLl9yZW5kZXJNZW51KCB1bCwgaXRlbXMgKTtcblx0XHR0aGlzLmlzTmV3TWVudSA9IHRydWU7XG5cdFx0dGhpcy5tZW51LnJlZnJlc2goKTtcblxuXHRcdC8vIFNpemUgYW5kIHBvc2l0aW9uIG1lbnVcblx0XHR1bC5zaG93KCk7XG5cdFx0dGhpcy5fcmVzaXplTWVudSgpO1xuXHRcdHVsLnBvc2l0aW9uKCAkLmV4dGVuZCgge1xuXHRcdFx0b2Y6IHRoaXMuZWxlbWVudFxuXHRcdH0sIHRoaXMub3B0aW9ucy5wb3NpdGlvbiApICk7XG5cblx0XHRpZiAoIHRoaXMub3B0aW9ucy5hdXRvRm9jdXMgKSB7XG5cdFx0XHR0aGlzLm1lbnUubmV4dCgpO1xuXHRcdH1cblxuXHRcdC8vIExpc3RlbiBmb3IgaW50ZXJhY3Rpb25zIG91dHNpZGUgb2YgdGhlIHdpZGdldCAoIzY2NDIpXG5cdFx0dGhpcy5fb24oIHRoaXMuZG9jdW1lbnQsIHtcblx0XHRcdG1vdXNlZG93bjogXCJfY2xvc2VPbkNsaWNrT3V0c2lkZVwiXG5cdFx0fSApO1xuXHR9LFxuXG5cdF9yZXNpemVNZW51OiBmdW5jdGlvbigpIHtcblx0XHR2YXIgdWwgPSB0aGlzLm1lbnUuZWxlbWVudDtcblx0XHR1bC5vdXRlcldpZHRoKCBNYXRoLm1heChcblxuXHRcdFx0Ly8gRmlyZWZveCB3cmFwcyBsb25nIHRleHQgKHBvc3NpYmx5IGEgcm91bmRpbmcgYnVnKVxuXHRcdFx0Ly8gc28gd2UgYWRkIDFweCB0byBhdm9pZCB0aGUgd3JhcHBpbmcgKCM3NTEzKVxuXHRcdFx0dWwud2lkdGgoIFwiXCIgKS5vdXRlcldpZHRoKCkgKyAxLFxuXHRcdFx0dGhpcy5lbGVtZW50Lm91dGVyV2lkdGgoKVxuXHRcdCkgKTtcblx0fSxcblxuXHRfcmVuZGVyTWVudTogZnVuY3Rpb24oIHVsLCBpdGVtcyApIHtcblx0XHR2YXIgdGhhdCA9IHRoaXM7XG5cdFx0JC5lYWNoKCBpdGVtcywgZnVuY3Rpb24oIGluZGV4LCBpdGVtICkge1xuXHRcdFx0dGhhdC5fcmVuZGVySXRlbURhdGEoIHVsLCBpdGVtICk7XG5cdFx0fSApO1xuXHR9LFxuXG5cdF9yZW5kZXJJdGVtRGF0YTogZnVuY3Rpb24oIHVsLCBpdGVtICkge1xuXHRcdHJldHVybiB0aGlzLl9yZW5kZXJJdGVtKCB1bCwgaXRlbSApLmRhdGEoIFwidWktYXV0b2NvbXBsZXRlLWl0ZW1cIiwgaXRlbSApO1xuXHR9LFxuXG5cdF9yZW5kZXJJdGVtOiBmdW5jdGlvbiggdWwsIGl0ZW0gKSB7XG5cdFx0cmV0dXJuICQoIFwiPGxpPlwiIClcblx0XHRcdC5hcHBlbmQoICQoIFwiPGRpdj5cIiApLnRleHQoIGl0ZW0ubGFiZWwgKSApXG5cdFx0XHQuYXBwZW5kVG8oIHVsICk7XG5cdH0sXG5cblx0X21vdmU6IGZ1bmN0aW9uKCBkaXJlY3Rpb24sIGV2ZW50ICkge1xuXHRcdGlmICggIXRoaXMubWVudS5lbGVtZW50LmlzKCBcIjp2aXNpYmxlXCIgKSApIHtcblx0XHRcdHRoaXMuc2VhcmNoKCBudWxsLCBldmVudCApO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRpZiAoIHRoaXMubWVudS5pc0ZpcnN0SXRlbSgpICYmIC9ecHJldmlvdXMvLnRlc3QoIGRpcmVjdGlvbiApIHx8XG5cdFx0XHRcdHRoaXMubWVudS5pc0xhc3RJdGVtKCkgJiYgL15uZXh0Ly50ZXN0KCBkaXJlY3Rpb24gKSApIHtcblxuXHRcdFx0aWYgKCAhdGhpcy5pc011bHRpTGluZSApIHtcblx0XHRcdFx0dGhpcy5fdmFsdWUoIHRoaXMudGVybSApO1xuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLm1lbnUuYmx1cigpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHR0aGlzLm1lbnVbIGRpcmVjdGlvbiBdKCBldmVudCApO1xuXHR9LFxuXG5cdHdpZGdldDogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMubWVudS5lbGVtZW50O1xuXHR9LFxuXG5cdF92YWx1ZTogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMudmFsdWVNZXRob2QuYXBwbHkoIHRoaXMuZWxlbWVudCwgYXJndW1lbnRzICk7XG5cdH0sXG5cblx0X2tleUV2ZW50OiBmdW5jdGlvbigga2V5RXZlbnQsIGV2ZW50ICkge1xuXHRcdGlmICggIXRoaXMuaXNNdWx0aUxpbmUgfHwgdGhpcy5tZW51LmVsZW1lbnQuaXMoIFwiOnZpc2libGVcIiApICkge1xuXHRcdFx0dGhpcy5fbW92ZSgga2V5RXZlbnQsIGV2ZW50ICk7XG5cblx0XHRcdC8vIFByZXZlbnRzIG1vdmluZyBjdXJzb3IgdG8gYmVnaW5uaW5nL2VuZCBvZiB0aGUgdGV4dCBmaWVsZCBpbiBzb21lIGJyb3dzZXJzXG5cdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdH1cblx0fSxcblxuXHQvLyBTdXBwb3J0OiBDaHJvbWUgPD01MFxuXHQvLyBXZSBzaG91bGQgYmUgYWJsZSB0byBqdXN0IHVzZSB0aGlzLmVsZW1lbnQucHJvcCggXCJpc0NvbnRlbnRFZGl0YWJsZVwiIClcblx0Ly8gYnV0IGhpZGRlbiBlbGVtZW50cyBhbHdheXMgcmVwb3J0IGZhbHNlIGluIENocm9tZS5cblx0Ly8gaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC9jaHJvbWl1bS9pc3N1ZXMvZGV0YWlsP2lkPTMxMzA4MlxuXHRfaXNDb250ZW50RWRpdGFibGU6IGZ1bmN0aW9uKCBlbGVtZW50ICkge1xuXHRcdGlmICggIWVsZW1lbnQubGVuZ3RoICkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdHZhciBlZGl0YWJsZSA9IGVsZW1lbnQucHJvcCggXCJjb250ZW50RWRpdGFibGVcIiApO1xuXG5cdFx0aWYgKCBlZGl0YWJsZSA9PT0gXCJpbmhlcml0XCIgKSB7XG5cdFx0ICByZXR1cm4gdGhpcy5faXNDb250ZW50RWRpdGFibGUoIGVsZW1lbnQucGFyZW50KCkgKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZWRpdGFibGUgPT09IFwidHJ1ZVwiO1xuXHR9XG59ICk7XG5cbiQuZXh0ZW5kKCAkLnVpLmF1dG9jb21wbGV0ZSwge1xuXHRlc2NhcGVSZWdleDogZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdHJldHVybiB2YWx1ZS5yZXBsYWNlKCAvW1xcLVxcW1xcXXt9KCkqKz8uLFxcXFxcXF4kfCNcXHNdL2csIFwiXFxcXCQmXCIgKTtcblx0fSxcblx0ZmlsdGVyOiBmdW5jdGlvbiggYXJyYXksIHRlcm0gKSB7XG5cdFx0dmFyIG1hdGNoZXIgPSBuZXcgUmVnRXhwKCAkLnVpLmF1dG9jb21wbGV0ZS5lc2NhcGVSZWdleCggdGVybSApLCBcImlcIiApO1xuXHRcdHJldHVybiAkLmdyZXAoIGFycmF5LCBmdW5jdGlvbiggdmFsdWUgKSB7XG5cdFx0XHRyZXR1cm4gbWF0Y2hlci50ZXN0KCB2YWx1ZS5sYWJlbCB8fCB2YWx1ZS52YWx1ZSB8fCB2YWx1ZSApO1xuXHRcdH0gKTtcblx0fVxufSApO1xuXG4vLyBMaXZlIHJlZ2lvbiBleHRlbnNpb24sIGFkZGluZyBhIGBtZXNzYWdlc2Agb3B0aW9uXG4vLyBOT1RFOiBUaGlzIGlzIGFuIGV4cGVyaW1lbnRhbCBBUEkuIFdlIGFyZSBzdGlsbCBpbnZlc3RpZ2F0aW5nXG4vLyBhIGZ1bGwgc29sdXRpb24gZm9yIHN0cmluZyBtYW5pcHVsYXRpb24gYW5kIGludGVybmF0aW9uYWxpemF0aW9uLlxuJC53aWRnZXQoIFwidWkuYXV0b2NvbXBsZXRlXCIsICQudWkuYXV0b2NvbXBsZXRlLCB7XG5cdG9wdGlvbnM6IHtcblx0XHRtZXNzYWdlczoge1xuXHRcdFx0bm9SZXN1bHRzOiBcIk5vIHNlYXJjaCByZXN1bHRzLlwiLFxuXHRcdFx0cmVzdWx0czogZnVuY3Rpb24oIGFtb3VudCApIHtcblx0XHRcdFx0cmV0dXJuIGFtb3VudCArICggYW1vdW50ID4gMSA/IFwiIHJlc3VsdHMgYXJlXCIgOiBcIiByZXN1bHQgaXNcIiApICtcblx0XHRcdFx0XHRcIiBhdmFpbGFibGUsIHVzZSB1cCBhbmQgZG93biBhcnJvdyBrZXlzIHRvIG5hdmlnYXRlLlwiO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHRfX3Jlc3BvbnNlOiBmdW5jdGlvbiggY29udGVudCApIHtcblx0XHR2YXIgbWVzc2FnZTtcblx0XHR0aGlzLl9zdXBlckFwcGx5KCBhcmd1bWVudHMgKTtcblx0XHRpZiAoIHRoaXMub3B0aW9ucy5kaXNhYmxlZCB8fCB0aGlzLmNhbmNlbFNlYXJjaCApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0aWYgKCBjb250ZW50ICYmIGNvbnRlbnQubGVuZ3RoICkge1xuXHRcdFx0bWVzc2FnZSA9IHRoaXMub3B0aW9ucy5tZXNzYWdlcy5yZXN1bHRzKCBjb250ZW50Lmxlbmd0aCApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRtZXNzYWdlID0gdGhpcy5vcHRpb25zLm1lc3NhZ2VzLm5vUmVzdWx0cztcblx0XHR9XG5cdFx0dGhpcy5saXZlUmVnaW9uLmNoaWxkcmVuKCkuaGlkZSgpO1xuXHRcdCQoIFwiPGRpdj5cIiApLnRleHQoIG1lc3NhZ2UgKS5hcHBlbmRUbyggdGhpcy5saXZlUmVnaW9uICk7XG5cdH1cbn0gKTtcblxudmFyIHdpZGdldHNBdXRvY29tcGxldGUgPSAkLnVpLmF1dG9jb21wbGV0ZTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBDb250cm9sZ3JvdXAgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IENvbnRyb2xncm91cFxuLy8+Pmdyb3VwOiBXaWRnZXRzXG4vLz4+ZGVzY3JpcHRpb246IFZpc3VhbGx5IGdyb3VwcyBmb3JtIGNvbnRyb2wgd2lkZ2V0c1xuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2NvbnRyb2xncm91cC9cbi8vPj5kZW1vczogaHR0cDovL2pxdWVyeXVpLmNvbS9jb250cm9sZ3JvdXAvXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2UvY29yZS5jc3Ncbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9jb250cm9sZ3JvdXAuY3NzXG4vLz4+Y3NzLnRoZW1lOiAuLi8uLi90aGVtZXMvYmFzZS90aGVtZS5jc3NcblxuXG52YXIgY29udHJvbGdyb3VwQ29ybmVyUmVnZXggPSAvdWktY29ybmVyLShbYS16XSl7Miw2fS9nO1xuXG52YXIgd2lkZ2V0c0NvbnRyb2xncm91cCA9ICQud2lkZ2V0KCBcInVpLmNvbnRyb2xncm91cFwiLCB7XG5cdHZlcnNpb246IFwiMS4xMi4xXCIsXG5cdGRlZmF1bHRFbGVtZW50OiBcIjxkaXY+XCIsXG5cdG9wdGlvbnM6IHtcblx0XHRkaXJlY3Rpb246IFwiaG9yaXpvbnRhbFwiLFxuXHRcdGRpc2FibGVkOiBudWxsLFxuXHRcdG9ubHlWaXNpYmxlOiB0cnVlLFxuXHRcdGl0ZW1zOiB7XG5cdFx0XHRcImJ1dHRvblwiOiBcImlucHV0W3R5cGU9YnV0dG9uXSwgaW5wdXRbdHlwZT1zdWJtaXRdLCBpbnB1dFt0eXBlPXJlc2V0XSwgYnV0dG9uLCBhXCIsXG5cdFx0XHRcImNvbnRyb2xncm91cExhYmVsXCI6IFwiLnVpLWNvbnRyb2xncm91cC1sYWJlbFwiLFxuXHRcdFx0XCJjaGVja2JveHJhZGlvXCI6IFwiaW5wdXRbdHlwZT0nY2hlY2tib3gnXSwgaW5wdXRbdHlwZT0ncmFkaW8nXVwiLFxuXHRcdFx0XCJzZWxlY3RtZW51XCI6IFwic2VsZWN0XCIsXG5cdFx0XHRcInNwaW5uZXJcIjogXCIudWktc3Bpbm5lci1pbnB1dFwiXG5cdFx0fVxuXHR9LFxuXG5cdF9jcmVhdGU6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuX2VuaGFuY2UoKTtcblx0fSxcblxuXHQvLyBUbyBzdXBwb3J0IHRoZSBlbmhhbmNlZCBvcHRpb24gaW4galF1ZXJ5IE1vYmlsZSwgd2UgaXNvbGF0ZSBET00gbWFuaXB1bGF0aW9uXG5cdF9lbmhhbmNlOiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLmVsZW1lbnQuYXR0ciggXCJyb2xlXCIsIFwidG9vbGJhclwiICk7XG5cdFx0dGhpcy5yZWZyZXNoKCk7XG5cdH0sXG5cblx0X2Rlc3Ryb3k6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuX2NhbGxDaGlsZE1ldGhvZCggXCJkZXN0cm95XCIgKTtcblx0XHR0aGlzLmNoaWxkV2lkZ2V0cy5yZW1vdmVEYXRhKCBcInVpLWNvbnRyb2xncm91cC1kYXRhXCIgKTtcblx0XHR0aGlzLmVsZW1lbnQucmVtb3ZlQXR0ciggXCJyb2xlXCIgKTtcblx0XHRpZiAoIHRoaXMub3B0aW9ucy5pdGVtcy5jb250cm9sZ3JvdXBMYWJlbCApIHtcblx0XHRcdHRoaXMuZWxlbWVudFxuXHRcdFx0XHQuZmluZCggdGhpcy5vcHRpb25zLml0ZW1zLmNvbnRyb2xncm91cExhYmVsIClcblx0XHRcdFx0LmZpbmQoIFwiLnVpLWNvbnRyb2xncm91cC1sYWJlbC1jb250ZW50c1wiIClcblx0XHRcdFx0LmNvbnRlbnRzKCkudW53cmFwKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9pbml0V2lkZ2V0czogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHRoYXQgPSB0aGlzLFxuXHRcdFx0Y2hpbGRXaWRnZXRzID0gW107XG5cblx0XHQvLyBGaXJzdCB3ZSBpdGVyYXRlIG92ZXIgZWFjaCBvZiB0aGUgaXRlbXMgb3B0aW9uc1xuXHRcdCQuZWFjaCggdGhpcy5vcHRpb25zLml0ZW1zLCBmdW5jdGlvbiggd2lkZ2V0LCBzZWxlY3RvciApIHtcblx0XHRcdHZhciBsYWJlbHM7XG5cdFx0XHR2YXIgb3B0aW9ucyA9IHt9O1xuXG5cdFx0XHQvLyBNYWtlIHN1cmUgdGhlIHdpZGdldCBoYXMgYSBzZWxlY3RvciBzZXRcblx0XHRcdGlmICggIXNlbGVjdG9yICkge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdGlmICggd2lkZ2V0ID09PSBcImNvbnRyb2xncm91cExhYmVsXCIgKSB7XG5cdFx0XHRcdGxhYmVscyA9IHRoYXQuZWxlbWVudC5maW5kKCBzZWxlY3RvciApO1xuXHRcdFx0XHRsYWJlbHMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0dmFyIGVsZW1lbnQgPSAkKCB0aGlzICk7XG5cblx0XHRcdFx0XHRpZiAoIGVsZW1lbnQuY2hpbGRyZW4oIFwiLnVpLWNvbnRyb2xncm91cC1sYWJlbC1jb250ZW50c1wiICkubGVuZ3RoICkge1xuXHRcdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRlbGVtZW50LmNvbnRlbnRzKClcblx0XHRcdFx0XHRcdC53cmFwQWxsKCBcIjxzcGFuIGNsYXNzPSd1aS1jb250cm9sZ3JvdXAtbGFiZWwtY29udGVudHMnPjwvc3Bhbj5cIiApO1xuXHRcdFx0XHR9ICk7XG5cdFx0XHRcdHRoYXQuX2FkZENsYXNzKCBsYWJlbHMsIG51bGwsIFwidWktd2lkZ2V0IHVpLXdpZGdldC1jb250ZW50IHVpLXN0YXRlLWRlZmF1bHRcIiApO1xuXHRcdFx0XHRjaGlsZFdpZGdldHMgPSBjaGlsZFdpZGdldHMuY29uY2F0KCBsYWJlbHMuZ2V0KCkgKTtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBNYWtlIHN1cmUgdGhlIHdpZGdldCBhY3R1YWxseSBleGlzdHNcblx0XHRcdGlmICggISQuZm5bIHdpZGdldCBdICkge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdC8vIFdlIGFzc3VtZSBldmVyeXRoaW5nIGlzIGluIHRoZSBtaWRkbGUgdG8gc3RhcnQgYmVjYXVzZSB3ZSBjYW4ndCBkZXRlcm1pbmVcblx0XHRcdC8vIGZpcnN0IC8gbGFzdCBlbGVtZW50cyB1bnRpbCBhbGwgZW5oYW5jbWVudHMgYXJlIGRvbmUuXG5cdFx0XHRpZiAoIHRoYXRbIFwiX1wiICsgd2lkZ2V0ICsgXCJPcHRpb25zXCIgXSApIHtcblx0XHRcdFx0b3B0aW9ucyA9IHRoYXRbIFwiX1wiICsgd2lkZ2V0ICsgXCJPcHRpb25zXCIgXSggXCJtaWRkbGVcIiApO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0b3B0aW9ucyA9IHsgY2xhc3Nlczoge30gfTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gRmluZCBpbnN0YW5jZXMgb2YgdGhpcyB3aWRnZXQgaW5zaWRlIGNvbnRyb2xncm91cCBhbmQgaW5pdCB0aGVtXG5cdFx0XHR0aGF0LmVsZW1lbnRcblx0XHRcdFx0LmZpbmQoIHNlbGVjdG9yIClcblx0XHRcdFx0LmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdHZhciBlbGVtZW50ID0gJCggdGhpcyApO1xuXHRcdFx0XHRcdHZhciBpbnN0YW5jZSA9IGVsZW1lbnRbIHdpZGdldCBdKCBcImluc3RhbmNlXCIgKTtcblxuXHRcdFx0XHRcdC8vIFdlIG5lZWQgdG8gY2xvbmUgdGhlIGRlZmF1bHQgb3B0aW9ucyBmb3IgdGhpcyB0eXBlIG9mIHdpZGdldCB0byBhdm9pZFxuXHRcdFx0XHRcdC8vIHBvbGx1dGluZyB0aGUgdmFyaWFibGUgb3B0aW9ucyB3aGljaCBoYXMgYSB3aWRlciBzY29wZSB0aGFuIGEgc2luZ2xlIHdpZGdldC5cblx0XHRcdFx0XHR2YXIgaW5zdGFuY2VPcHRpb25zID0gJC53aWRnZXQuZXh0ZW5kKCB7fSwgb3B0aW9ucyApO1xuXG5cdFx0XHRcdFx0Ly8gSWYgdGhlIGJ1dHRvbiBpcyB0aGUgY2hpbGQgb2YgYSBzcGlubmVyIGlnbm9yZSBpdFxuXHRcdFx0XHRcdC8vIFRPRE86IEZpbmQgYSBtb3JlIGdlbmVyaWMgc29sdXRpb25cblx0XHRcdFx0XHRpZiAoIHdpZGdldCA9PT0gXCJidXR0b25cIiAmJiBlbGVtZW50LnBhcmVudCggXCIudWktc3Bpbm5lclwiICkubGVuZ3RoICkge1xuXHRcdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIENyZWF0ZSB0aGUgd2lkZ2V0IGlmIGl0IGRvZXNuJ3QgZXhpc3Rcblx0XHRcdFx0XHRpZiAoICFpbnN0YW5jZSApIHtcblx0XHRcdFx0XHRcdGluc3RhbmNlID0gZWxlbWVudFsgd2lkZ2V0IF0oKVsgd2lkZ2V0IF0oIFwiaW5zdGFuY2VcIiApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRpZiAoIGluc3RhbmNlICkge1xuXHRcdFx0XHRcdFx0aW5zdGFuY2VPcHRpb25zLmNsYXNzZXMgPVxuXHRcdFx0XHRcdFx0XHR0aGF0Ll9yZXNvbHZlQ2xhc3Nlc1ZhbHVlcyggaW5zdGFuY2VPcHRpb25zLmNsYXNzZXMsIGluc3RhbmNlICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGVsZW1lbnRbIHdpZGdldCBdKCBpbnN0YW5jZU9wdGlvbnMgKTtcblxuXHRcdFx0XHRcdC8vIFN0b3JlIGFuIGluc3RhbmNlIG9mIHRoZSBjb250cm9sZ3JvdXAgdG8gYmUgYWJsZSB0byByZWZlcmVuY2Vcblx0XHRcdFx0XHQvLyBmcm9tIHRoZSBvdXRlcm1vc3QgZWxlbWVudCBmb3IgY2hhbmdpbmcgb3B0aW9ucyBhbmQgcmVmcmVzaFxuXHRcdFx0XHRcdHZhciB3aWRnZXRFbGVtZW50ID0gZWxlbWVudFsgd2lkZ2V0IF0oIFwid2lkZ2V0XCIgKTtcblx0XHRcdFx0XHQkLmRhdGEoIHdpZGdldEVsZW1lbnRbIDAgXSwgXCJ1aS1jb250cm9sZ3JvdXAtZGF0YVwiLFxuXHRcdFx0XHRcdFx0aW5zdGFuY2UgPyBpbnN0YW5jZSA6IGVsZW1lbnRbIHdpZGdldCBdKCBcImluc3RhbmNlXCIgKSApO1xuXG5cdFx0XHRcdFx0Y2hpbGRXaWRnZXRzLnB1c2goIHdpZGdldEVsZW1lbnRbIDAgXSApO1xuXHRcdFx0XHR9ICk7XG5cdFx0fSApO1xuXG5cdFx0dGhpcy5jaGlsZFdpZGdldHMgPSAkKCAkLnVuaXF1ZSggY2hpbGRXaWRnZXRzICkgKTtcblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5jaGlsZFdpZGdldHMsIFwidWktY29udHJvbGdyb3VwLWl0ZW1cIiApO1xuXHR9LFxuXG5cdF9jYWxsQ2hpbGRNZXRob2Q6IGZ1bmN0aW9uKCBtZXRob2QgKSB7XG5cdFx0dGhpcy5jaGlsZFdpZGdldHMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgZWxlbWVudCA9ICQoIHRoaXMgKSxcblx0XHRcdFx0ZGF0YSA9IGVsZW1lbnQuZGF0YSggXCJ1aS1jb250cm9sZ3JvdXAtZGF0YVwiICk7XG5cdFx0XHRpZiAoIGRhdGEgJiYgZGF0YVsgbWV0aG9kIF0gKSB7XG5cdFx0XHRcdGRhdGFbIG1ldGhvZCBdKCk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXHR9LFxuXG5cdF91cGRhdGVDb3JuZXJDbGFzczogZnVuY3Rpb24oIGVsZW1lbnQsIHBvc2l0aW9uICkge1xuXHRcdHZhciByZW1vdmUgPSBcInVpLWNvcm5lci10b3AgdWktY29ybmVyLWJvdHRvbSB1aS1jb3JuZXItbGVmdCB1aS1jb3JuZXItcmlnaHQgdWktY29ybmVyLWFsbFwiO1xuXHRcdHZhciBhZGQgPSB0aGlzLl9idWlsZFNpbXBsZU9wdGlvbnMoIHBvc2l0aW9uLCBcImxhYmVsXCIgKS5jbGFzc2VzLmxhYmVsO1xuXG5cdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIGVsZW1lbnQsIG51bGwsIHJlbW92ZSApO1xuXHRcdHRoaXMuX2FkZENsYXNzKCBlbGVtZW50LCBudWxsLCBhZGQgKTtcblx0fSxcblxuXHRfYnVpbGRTaW1wbGVPcHRpb25zOiBmdW5jdGlvbiggcG9zaXRpb24sIGtleSApIHtcblx0XHR2YXIgZGlyZWN0aW9uID0gdGhpcy5vcHRpb25zLmRpcmVjdGlvbiA9PT0gXCJ2ZXJ0aWNhbFwiO1xuXHRcdHZhciByZXN1bHQgPSB7XG5cdFx0XHRjbGFzc2VzOiB7fVxuXHRcdH07XG5cdFx0cmVzdWx0LmNsYXNzZXNbIGtleSBdID0ge1xuXHRcdFx0XCJtaWRkbGVcIjogXCJcIixcblx0XHRcdFwiZmlyc3RcIjogXCJ1aS1jb3JuZXItXCIgKyAoIGRpcmVjdGlvbiA/IFwidG9wXCIgOiBcImxlZnRcIiApLFxuXHRcdFx0XCJsYXN0XCI6IFwidWktY29ybmVyLVwiICsgKCBkaXJlY3Rpb24gPyBcImJvdHRvbVwiIDogXCJyaWdodFwiICksXG5cdFx0XHRcIm9ubHlcIjogXCJ1aS1jb3JuZXItYWxsXCJcblx0XHR9WyBwb3NpdGlvbiBdO1xuXG5cdFx0cmV0dXJuIHJlc3VsdDtcblx0fSxcblxuXHRfc3Bpbm5lck9wdGlvbnM6IGZ1bmN0aW9uKCBwb3NpdGlvbiApIHtcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMuX2J1aWxkU2ltcGxlT3B0aW9ucyggcG9zaXRpb24sIFwidWktc3Bpbm5lclwiICk7XG5cblx0XHRvcHRpb25zLmNsYXNzZXNbIFwidWktc3Bpbm5lci11cFwiIF0gPSBcIlwiO1xuXHRcdG9wdGlvbnMuY2xhc3Nlc1sgXCJ1aS1zcGlubmVyLWRvd25cIiBdID0gXCJcIjtcblxuXHRcdHJldHVybiBvcHRpb25zO1xuXHR9LFxuXG5cdF9idXR0b25PcHRpb25zOiBmdW5jdGlvbiggcG9zaXRpb24gKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2J1aWxkU2ltcGxlT3B0aW9ucyggcG9zaXRpb24sIFwidWktYnV0dG9uXCIgKTtcblx0fSxcblxuXHRfY2hlY2tib3hyYWRpb09wdGlvbnM6IGZ1bmN0aW9uKCBwb3NpdGlvbiApIHtcblx0XHRyZXR1cm4gdGhpcy5fYnVpbGRTaW1wbGVPcHRpb25zKCBwb3NpdGlvbiwgXCJ1aS1jaGVja2JveHJhZGlvLWxhYmVsXCIgKTtcblx0fSxcblxuXHRfc2VsZWN0bWVudU9wdGlvbnM6IGZ1bmN0aW9uKCBwb3NpdGlvbiApIHtcblx0XHR2YXIgZGlyZWN0aW9uID0gdGhpcy5vcHRpb25zLmRpcmVjdGlvbiA9PT0gXCJ2ZXJ0aWNhbFwiO1xuXHRcdHJldHVybiB7XG5cdFx0XHR3aWR0aDogZGlyZWN0aW9uID8gXCJhdXRvXCIgOiBmYWxzZSxcblx0XHRcdGNsYXNzZXM6IHtcblx0XHRcdFx0bWlkZGxlOiB7XG5cdFx0XHRcdFx0XCJ1aS1zZWxlY3RtZW51LWJ1dHRvbi1vcGVuXCI6IFwiXCIsXG5cdFx0XHRcdFx0XCJ1aS1zZWxlY3RtZW51LWJ1dHRvbi1jbG9zZWRcIjogXCJcIlxuXHRcdFx0XHR9LFxuXHRcdFx0XHRmaXJzdDoge1xuXHRcdFx0XHRcdFwidWktc2VsZWN0bWVudS1idXR0b24tb3BlblwiOiBcInVpLWNvcm5lci1cIiArICggZGlyZWN0aW9uID8gXCJ0b3BcIiA6IFwidGxcIiApLFxuXHRcdFx0XHRcdFwidWktc2VsZWN0bWVudS1idXR0b24tY2xvc2VkXCI6IFwidWktY29ybmVyLVwiICsgKCBkaXJlY3Rpb24gPyBcInRvcFwiIDogXCJsZWZ0XCIgKVxuXHRcdFx0XHR9LFxuXHRcdFx0XHRsYXN0OiB7XG5cdFx0XHRcdFx0XCJ1aS1zZWxlY3RtZW51LWJ1dHRvbi1vcGVuXCI6IGRpcmVjdGlvbiA/IFwiXCIgOiBcInVpLWNvcm5lci10clwiLFxuXHRcdFx0XHRcdFwidWktc2VsZWN0bWVudS1idXR0b24tY2xvc2VkXCI6IFwidWktY29ybmVyLVwiICsgKCBkaXJlY3Rpb24gPyBcImJvdHRvbVwiIDogXCJyaWdodFwiIClcblx0XHRcdFx0fSxcblx0XHRcdFx0b25seToge1xuXHRcdFx0XHRcdFwidWktc2VsZWN0bWVudS1idXR0b24tb3BlblwiOiBcInVpLWNvcm5lci10b3BcIixcblx0XHRcdFx0XHRcInVpLXNlbGVjdG1lbnUtYnV0dG9uLWNsb3NlZFwiOiBcInVpLWNvcm5lci1hbGxcIlxuXHRcdFx0XHR9XG5cblx0XHRcdH1bIHBvc2l0aW9uIF1cblx0XHR9O1xuXHR9LFxuXG5cdF9yZXNvbHZlQ2xhc3Nlc1ZhbHVlczogZnVuY3Rpb24oIGNsYXNzZXMsIGluc3RhbmNlICkge1xuXHRcdHZhciByZXN1bHQgPSB7fTtcblx0XHQkLmVhY2goIGNsYXNzZXMsIGZ1bmN0aW9uKCBrZXkgKSB7XG5cdFx0XHR2YXIgY3VycmVudCA9IGluc3RhbmNlLm9wdGlvbnMuY2xhc3Nlc1sga2V5IF0gfHwgXCJcIjtcblx0XHRcdGN1cnJlbnQgPSAkLnRyaW0oIGN1cnJlbnQucmVwbGFjZSggY29udHJvbGdyb3VwQ29ybmVyUmVnZXgsIFwiXCIgKSApO1xuXHRcdFx0cmVzdWx0WyBrZXkgXSA9ICggY3VycmVudCArIFwiIFwiICsgY2xhc3Nlc1sga2V5IF0gKS5yZXBsYWNlKCAvXFxzKy9nLCBcIiBcIiApO1xuXHRcdH0gKTtcblx0XHRyZXR1cm4gcmVzdWx0O1xuXHR9LFxuXG5cdF9zZXRPcHRpb246IGZ1bmN0aW9uKCBrZXksIHZhbHVlICkge1xuXHRcdGlmICgga2V5ID09PSBcImRpcmVjdGlvblwiICkge1xuXHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIFwidWktY29udHJvbGdyb3VwLVwiICsgdGhpcy5vcHRpb25zLmRpcmVjdGlvbiApO1xuXHRcdH1cblxuXHRcdHRoaXMuX3N1cGVyKCBrZXksIHZhbHVlICk7XG5cdFx0aWYgKCBrZXkgPT09IFwiZGlzYWJsZWRcIiApIHtcblx0XHRcdHRoaXMuX2NhbGxDaGlsZE1ldGhvZCggdmFsdWUgPyBcImRpc2FibGVcIiA6IFwiZW5hYmxlXCIgKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHR0aGlzLnJlZnJlc2goKTtcblx0fSxcblxuXHRyZWZyZXNoOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgY2hpbGRyZW4sXG5cdFx0XHR0aGF0ID0gdGhpcztcblxuXHRcdHRoaXMuX2FkZENsYXNzKCBcInVpLWNvbnRyb2xncm91cCB1aS1jb250cm9sZ3JvdXAtXCIgKyB0aGlzLm9wdGlvbnMuZGlyZWN0aW9uICk7XG5cblx0XHRpZiAoIHRoaXMub3B0aW9ucy5kaXJlY3Rpb24gPT09IFwiaG9yaXpvbnRhbFwiICkge1xuXHRcdFx0dGhpcy5fYWRkQ2xhc3MoIG51bGwsIFwidWktaGVscGVyLWNsZWFyZml4XCIgKTtcblx0XHR9XG5cdFx0dGhpcy5faW5pdFdpZGdldHMoKTtcblxuXHRcdGNoaWxkcmVuID0gdGhpcy5jaGlsZFdpZGdldHM7XG5cblx0XHQvLyBXZSBmaWx0ZXIgaGVyZSBiZWNhdXNlIHdlIG5lZWQgdG8gdHJhY2sgYWxsIGNoaWxkV2lkZ2V0cyBub3QganVzdCB0aGUgdmlzaWJsZSBvbmVzXG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMub25seVZpc2libGUgKSB7XG5cdFx0XHRjaGlsZHJlbiA9IGNoaWxkcmVuLmZpbHRlciggXCI6dmlzaWJsZVwiICk7XG5cdFx0fVxuXG5cdFx0aWYgKCBjaGlsZHJlbi5sZW5ndGggKSB7XG5cblx0XHRcdC8vIFdlIGRvIHRoaXMgbGFzdCBiZWNhdXNlIHdlIG5lZWQgdG8gbWFrZSBzdXJlIGFsbCBlbmhhbmNtZW50IGlzIGRvbmVcblx0XHRcdC8vIGJlZm9yZSBkZXRlcm1pbmluZyBmaXJzdCBhbmQgbGFzdFxuXHRcdFx0JC5lYWNoKCBbIFwiZmlyc3RcIiwgXCJsYXN0XCIgXSwgZnVuY3Rpb24oIGluZGV4LCB2YWx1ZSApIHtcblx0XHRcdFx0dmFyIGluc3RhbmNlID0gY2hpbGRyZW5bIHZhbHVlIF0oKS5kYXRhKCBcInVpLWNvbnRyb2xncm91cC1kYXRhXCIgKTtcblxuXHRcdFx0XHRpZiAoIGluc3RhbmNlICYmIHRoYXRbIFwiX1wiICsgaW5zdGFuY2Uud2lkZ2V0TmFtZSArIFwiT3B0aW9uc1wiIF0gKSB7XG5cdFx0XHRcdFx0dmFyIG9wdGlvbnMgPSB0aGF0WyBcIl9cIiArIGluc3RhbmNlLndpZGdldE5hbWUgKyBcIk9wdGlvbnNcIiBdKFxuXHRcdFx0XHRcdFx0Y2hpbGRyZW4ubGVuZ3RoID09PSAxID8gXCJvbmx5XCIgOiB2YWx1ZVxuXHRcdFx0XHRcdCk7XG5cdFx0XHRcdFx0b3B0aW9ucy5jbGFzc2VzID0gdGhhdC5fcmVzb2x2ZUNsYXNzZXNWYWx1ZXMoIG9wdGlvbnMuY2xhc3NlcywgaW5zdGFuY2UgKTtcblx0XHRcdFx0XHRpbnN0YW5jZS5lbGVtZW50WyBpbnN0YW5jZS53aWRnZXROYW1lIF0oIG9wdGlvbnMgKTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHR0aGF0Ll91cGRhdGVDb3JuZXJDbGFzcyggY2hpbGRyZW5bIHZhbHVlIF0oKSwgdmFsdWUgKTtcblx0XHRcdFx0fVxuXHRcdFx0fSApO1xuXG5cdFx0XHQvLyBGaW5hbGx5IGNhbGwgdGhlIHJlZnJlc2ggbWV0aG9kIG9uIGVhY2ggb2YgdGhlIGNoaWxkIHdpZGdldHMuXG5cdFx0XHR0aGlzLl9jYWxsQ2hpbGRNZXRob2QoIFwicmVmcmVzaFwiICk7XG5cdFx0fVxuXHR9XG59ICk7XG5cbi8qIVxuICogalF1ZXJ5IFVJIENoZWNrYm94cmFkaW8gMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IENoZWNrYm94cmFkaW9cbi8vPj5ncm91cDogV2lkZ2V0c1xuLy8+PmRlc2NyaXB0aW9uOiBFbmhhbmNlcyBhIGZvcm0gd2l0aCBtdWx0aXBsZSB0aGVtZWFibGUgY2hlY2tib3hlcyBvciByYWRpbyBidXR0b25zLlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2NoZWNrYm94cmFkaW8vXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vY2hlY2tib3hyYWRpby9cbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9jb3JlLmNzc1xuLy8+PmNzcy5zdHJ1Y3R1cmU6IC4uLy4uL3RoZW1lcy9iYXNlL2J1dHRvbi5jc3Ncbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9jaGVja2JveHJhZGlvLmNzc1xuLy8+PmNzcy50aGVtZTogLi4vLi4vdGhlbWVzL2Jhc2UvdGhlbWUuY3NzXG5cblxuXG4kLndpZGdldCggXCJ1aS5jaGVja2JveHJhZGlvXCIsIFsgJC51aS5mb3JtUmVzZXRNaXhpbiwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHRvcHRpb25zOiB7XG5cdFx0ZGlzYWJsZWQ6IG51bGwsXG5cdFx0bGFiZWw6IG51bGwsXG5cdFx0aWNvbjogdHJ1ZSxcblx0XHRjbGFzc2VzOiB7XG5cdFx0XHRcInVpLWNoZWNrYm94cmFkaW8tbGFiZWxcIjogXCJ1aS1jb3JuZXItYWxsXCIsXG5cdFx0XHRcInVpLWNoZWNrYm94cmFkaW8taWNvblwiOiBcInVpLWNvcm5lci1hbGxcIlxuXHRcdH1cblx0fSxcblxuXHRfZ2V0Q3JlYXRlT3B0aW9uczogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGRpc2FibGVkLCBsYWJlbHM7XG5cdFx0dmFyIHRoYXQgPSB0aGlzO1xuXHRcdHZhciBvcHRpb25zID0gdGhpcy5fc3VwZXIoKSB8fCB7fTtcblxuXHRcdC8vIFdlIHJlYWQgdGhlIHR5cGUgaGVyZSwgYmVjYXVzZSBpdCBtYWtlcyBtb3JlIHNlbnNlIHRvIHRocm93IGEgZWxlbWVudCB0eXBlIGVycm9yIGZpcnN0LFxuXHRcdC8vIHJhdGhlciB0aGVuIHRoZSBlcnJvciBmb3IgbGFjayBvZiBhIGxhYmVsLiBPZnRlbiBpZiBpdHMgdGhlIHdyb25nIHR5cGUsIGl0XG5cdFx0Ly8gd29uJ3QgaGF2ZSBhIGxhYmVsIChlLmcuIGNhbGxpbmcgb24gYSBkaXYsIGJ0biwgZXRjKVxuXHRcdHRoaXMuX3JlYWRUeXBlKCk7XG5cblx0XHRsYWJlbHMgPSB0aGlzLmVsZW1lbnQubGFiZWxzKCk7XG5cblx0XHQvLyBJZiB0aGVyZSBhcmUgbXVsdGlwbGUgbGFiZWxzLCB1c2UgdGhlIGxhc3Qgb25lXG5cdFx0dGhpcy5sYWJlbCA9ICQoIGxhYmVsc1sgbGFiZWxzLmxlbmd0aCAtIDEgXSApO1xuXHRcdGlmICggIXRoaXMubGFiZWwubGVuZ3RoICkge1xuXHRcdFx0JC5lcnJvciggXCJObyBsYWJlbCBmb3VuZCBmb3IgY2hlY2tib3hyYWRpbyB3aWRnZXRcIiApO1xuXHRcdH1cblxuXHRcdHRoaXMub3JpZ2luYWxMYWJlbCA9IFwiXCI7XG5cblx0XHQvLyBXZSBuZWVkIHRvIGdldCB0aGUgbGFiZWwgdGV4dCBidXQgdGhpcyBtYXkgYWxzbyBuZWVkIHRvIG1ha2Ugc3VyZSBpdCBkb2VzIG5vdCBjb250YWluIHRoZVxuXHRcdC8vIGlucHV0IGl0c2VsZi5cblx0XHR0aGlzLmxhYmVsLmNvbnRlbnRzKCkubm90KCB0aGlzLmVsZW1lbnRbIDAgXSApLmVhY2goIGZ1bmN0aW9uKCkge1xuXG5cdFx0XHQvLyBUaGUgbGFiZWwgY29udGVudHMgY291bGQgYmUgdGV4dCwgaHRtbCwgb3IgYSBtaXguIFdlIGNvbmNhdCBlYWNoIGVsZW1lbnQgdG8gZ2V0IGFcblx0XHRcdC8vIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgbGFiZWwsIHdpdGhvdXQgdGhlIGlucHV0IGFzIHBhcnQgb2YgaXQuXG5cdFx0XHR0aGF0Lm9yaWdpbmFsTGFiZWwgKz0gdGhpcy5ub2RlVHlwZSA9PT0gMyA/ICQoIHRoaXMgKS50ZXh0KCkgOiB0aGlzLm91dGVySFRNTDtcblx0XHR9ICk7XG5cblx0XHQvLyBTZXQgdGhlIGxhYmVsIG9wdGlvbiBpZiB3ZSBmb3VuZCBsYWJlbCB0ZXh0XG5cdFx0aWYgKCB0aGlzLm9yaWdpbmFsTGFiZWwgKSB7XG5cdFx0XHRvcHRpb25zLmxhYmVsID0gdGhpcy5vcmlnaW5hbExhYmVsO1xuXHRcdH1cblxuXHRcdGRpc2FibGVkID0gdGhpcy5lbGVtZW50WyAwIF0uZGlzYWJsZWQ7XG5cdFx0aWYgKCBkaXNhYmxlZCAhPSBudWxsICkge1xuXHRcdFx0b3B0aW9ucy5kaXNhYmxlZCA9IGRpc2FibGVkO1xuXHRcdH1cblx0XHRyZXR1cm4gb3B0aW9ucztcblx0fSxcblxuXHRfY3JlYXRlOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgY2hlY2tlZCA9IHRoaXMuZWxlbWVudFsgMCBdLmNoZWNrZWQ7XG5cblx0XHR0aGlzLl9iaW5kRm9ybVJlc2V0SGFuZGxlcigpO1xuXG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMuZGlzYWJsZWQgPT0gbnVsbCApIHtcblx0XHRcdHRoaXMub3B0aW9ucy5kaXNhYmxlZCA9IHRoaXMuZWxlbWVudFsgMCBdLmRpc2FibGVkO1xuXHRcdH1cblxuXHRcdHRoaXMuX3NldE9wdGlvbiggXCJkaXNhYmxlZFwiLCB0aGlzLm9wdGlvbnMuZGlzYWJsZWQgKTtcblx0XHR0aGlzLl9hZGRDbGFzcyggXCJ1aS1jaGVja2JveHJhZGlvXCIsIFwidWktaGVscGVyLWhpZGRlbi1hY2Nlc3NpYmxlXCIgKTtcblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5sYWJlbCwgXCJ1aS1jaGVja2JveHJhZGlvLWxhYmVsXCIsIFwidWktYnV0dG9uIHVpLXdpZGdldFwiICk7XG5cblx0XHRpZiAoIHRoaXMudHlwZSA9PT0gXCJyYWRpb1wiICkge1xuXHRcdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMubGFiZWwsIFwidWktY2hlY2tib3hyYWRpby1yYWRpby1sYWJlbFwiICk7XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMubGFiZWwgJiYgdGhpcy5vcHRpb25zLmxhYmVsICE9PSB0aGlzLm9yaWdpbmFsTGFiZWwgKSB7XG5cdFx0XHR0aGlzLl91cGRhdGVMYWJlbCgpO1xuXHRcdH0gZWxzZSBpZiAoIHRoaXMub3JpZ2luYWxMYWJlbCApIHtcblx0XHRcdHRoaXMub3B0aW9ucy5sYWJlbCA9IHRoaXMub3JpZ2luYWxMYWJlbDtcblx0XHR9XG5cblx0XHR0aGlzLl9lbmhhbmNlKCk7XG5cblx0XHRpZiAoIGNoZWNrZWQgKSB7XG5cdFx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5sYWJlbCwgXCJ1aS1jaGVja2JveHJhZGlvLWNoZWNrZWRcIiwgXCJ1aS1zdGF0ZS1hY3RpdmVcIiApO1xuXHRcdFx0aWYgKCB0aGlzLmljb24gKSB7XG5cdFx0XHRcdHRoaXMuX2FkZENsYXNzKCB0aGlzLmljb24sIG51bGwsIFwidWktc3RhdGUtaG92ZXJcIiApO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMuX29uKCB7XG5cdFx0XHRjaGFuZ2U6IFwiX3RvZ2dsZUNsYXNzZXNcIixcblx0XHRcdGZvY3VzOiBmdW5jdGlvbigpIHtcblx0XHRcdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMubGFiZWwsIG51bGwsIFwidWktc3RhdGUtZm9jdXMgdWktdmlzdWFsLWZvY3VzXCIgKTtcblx0XHRcdH0sXG5cdFx0XHRibHVyOiBmdW5jdGlvbigpIHtcblx0XHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIHRoaXMubGFiZWwsIG51bGwsIFwidWktc3RhdGUtZm9jdXMgdWktdmlzdWFsLWZvY3VzXCIgKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdH0sXG5cblx0X3JlYWRUeXBlOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgbm9kZU5hbWUgPSB0aGlzLmVsZW1lbnRbIDAgXS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpO1xuXHRcdHRoaXMudHlwZSA9IHRoaXMuZWxlbWVudFsgMCBdLnR5cGU7XG5cdFx0aWYgKCBub2RlTmFtZSAhPT0gXCJpbnB1dFwiIHx8ICEvcmFkaW98Y2hlY2tib3gvLnRlc3QoIHRoaXMudHlwZSApICkge1xuXHRcdFx0JC5lcnJvciggXCJDYW4ndCBjcmVhdGUgY2hlY2tib3hyYWRpbyBvbiBlbGVtZW50Lm5vZGVOYW1lPVwiICsgbm9kZU5hbWUgK1xuXHRcdFx0XHRcIiBhbmQgZWxlbWVudC50eXBlPVwiICsgdGhpcy50eXBlICk7XG5cdFx0fVxuXHR9LFxuXG5cdC8vIFN1cHBvcnQgalF1ZXJ5IE1vYmlsZSBlbmhhbmNlZCBvcHRpb25cblx0X2VuaGFuY2U6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuX3VwZGF0ZUljb24oIHRoaXMuZWxlbWVudFsgMCBdLmNoZWNrZWQgKTtcblx0fSxcblxuXHR3aWRnZXQ6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLmxhYmVsO1xuXHR9LFxuXG5cdF9nZXRSYWRpb0dyb3VwOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgZ3JvdXA7XG5cdFx0dmFyIG5hbWUgPSB0aGlzLmVsZW1lbnRbIDAgXS5uYW1lO1xuXHRcdHZhciBuYW1lU2VsZWN0b3IgPSBcImlucHV0W25hbWU9J1wiICsgJC51aS5lc2NhcGVTZWxlY3RvciggbmFtZSApICsgXCInXVwiO1xuXG5cdFx0aWYgKCAhbmFtZSApIHtcblx0XHRcdHJldHVybiAkKCBbXSApO1xuXHRcdH1cblxuXHRcdGlmICggdGhpcy5mb3JtLmxlbmd0aCApIHtcblx0XHRcdGdyb3VwID0gJCggdGhpcy5mb3JtWyAwIF0uZWxlbWVudHMgKS5maWx0ZXIoIG5hbWVTZWxlY3RvciApO1xuXHRcdH0gZWxzZSB7XG5cblx0XHRcdC8vIE5vdCBpbnNpZGUgYSBmb3JtLCBjaGVjayBhbGwgaW5wdXRzIHRoYXQgYWxzbyBhcmUgbm90IGluc2lkZSBhIGZvcm1cblx0XHRcdGdyb3VwID0gJCggbmFtZVNlbGVjdG9yICkuZmlsdGVyKCBmdW5jdGlvbigpIHtcblx0XHRcdFx0cmV0dXJuICQoIHRoaXMgKS5mb3JtKCkubGVuZ3RoID09PSAwO1xuXHRcdFx0fSApO1xuXHRcdH1cblxuXHRcdHJldHVybiBncm91cC5ub3QoIHRoaXMuZWxlbWVudCApO1xuXHR9LFxuXG5cdF90b2dnbGVDbGFzc2VzOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgY2hlY2tlZCA9IHRoaXMuZWxlbWVudFsgMCBdLmNoZWNrZWQ7XG5cdFx0dGhpcy5fdG9nZ2xlQ2xhc3MoIHRoaXMubGFiZWwsIFwidWktY2hlY2tib3hyYWRpby1jaGVja2VkXCIsIFwidWktc3RhdGUtYWN0aXZlXCIsIGNoZWNrZWQgKTtcblxuXHRcdGlmICggdGhpcy5vcHRpb25zLmljb24gJiYgdGhpcy50eXBlID09PSBcImNoZWNrYm94XCIgKSB7XG5cdFx0XHR0aGlzLl90b2dnbGVDbGFzcyggdGhpcy5pY29uLCBudWxsLCBcInVpLWljb24tY2hlY2sgdWktc3RhdGUtY2hlY2tlZFwiLCBjaGVja2VkIClcblx0XHRcdFx0Ll90b2dnbGVDbGFzcyggdGhpcy5pY29uLCBudWxsLCBcInVpLWljb24tYmxhbmtcIiwgIWNoZWNrZWQgKTtcblx0XHR9XG5cblx0XHRpZiAoIHRoaXMudHlwZSA9PT0gXCJyYWRpb1wiICkge1xuXHRcdFx0dGhpcy5fZ2V0UmFkaW9Hcm91cCgpXG5cdFx0XHRcdC5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHR2YXIgaW5zdGFuY2UgPSAkKCB0aGlzICkuY2hlY2tib3hyYWRpbyggXCJpbnN0YW5jZVwiICk7XG5cblx0XHRcdFx0XHRpZiAoIGluc3RhbmNlICkge1xuXHRcdFx0XHRcdFx0aW5zdGFuY2UuX3JlbW92ZUNsYXNzKCBpbnN0YW5jZS5sYWJlbCxcblx0XHRcdFx0XHRcdFx0XCJ1aS1jaGVja2JveHJhZGlvLWNoZWNrZWRcIiwgXCJ1aS1zdGF0ZS1hY3RpdmVcIiApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSApO1xuXHRcdH1cblx0fSxcblxuXHRfZGVzdHJveTogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fdW5iaW5kRm9ybVJlc2V0SGFuZGxlcigpO1xuXG5cdFx0aWYgKCB0aGlzLmljb24gKSB7XG5cdFx0XHR0aGlzLmljb24ucmVtb3ZlKCk7XG5cdFx0XHR0aGlzLmljb25TcGFjZS5yZW1vdmUoKTtcblx0XHR9XG5cdH0sXG5cblx0X3NldE9wdGlvbjogZnVuY3Rpb24oIGtleSwgdmFsdWUgKSB7XG5cblx0XHQvLyBXZSBkb24ndCBhbGxvdyB0aGUgdmFsdWUgdG8gYmUgc2V0IHRvIG5vdGhpbmdcblx0XHRpZiAoIGtleSA9PT0gXCJsYWJlbFwiICYmICF2YWx1ZSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHR0aGlzLl9zdXBlcigga2V5LCB2YWx1ZSApO1xuXG5cdFx0aWYgKCBrZXkgPT09IFwiZGlzYWJsZWRcIiApIHtcblx0XHRcdHRoaXMuX3RvZ2dsZUNsYXNzKCB0aGlzLmxhYmVsLCBudWxsLCBcInVpLXN0YXRlLWRpc2FibGVkXCIsIHZhbHVlICk7XG5cdFx0XHR0aGlzLmVsZW1lbnRbIDAgXS5kaXNhYmxlZCA9IHZhbHVlO1xuXG5cdFx0XHQvLyBEb24ndCByZWZyZXNoIHdoZW4gc2V0dGluZyBkaXNhYmxlZFxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHR0aGlzLnJlZnJlc2goKTtcblx0fSxcblxuXHRfdXBkYXRlSWNvbjogZnVuY3Rpb24oIGNoZWNrZWQgKSB7XG5cdFx0dmFyIHRvQWRkID0gXCJ1aS1pY29uIHVpLWljb24tYmFja2dyb3VuZCBcIjtcblxuXHRcdGlmICggdGhpcy5vcHRpb25zLmljb24gKSB7XG5cdFx0XHRpZiAoICF0aGlzLmljb24gKSB7XG5cdFx0XHRcdHRoaXMuaWNvbiA9ICQoIFwiPHNwYW4+XCIgKTtcblx0XHRcdFx0dGhpcy5pY29uU3BhY2UgPSAkKCBcIjxzcGFuPiA8L3NwYW4+XCIgKTtcblx0XHRcdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMuaWNvblNwYWNlLCBcInVpLWNoZWNrYm94cmFkaW8taWNvbi1zcGFjZVwiICk7XG5cdFx0XHR9XG5cblx0XHRcdGlmICggdGhpcy50eXBlID09PSBcImNoZWNrYm94XCIgKSB7XG5cdFx0XHRcdHRvQWRkICs9IGNoZWNrZWQgPyBcInVpLWljb24tY2hlY2sgdWktc3RhdGUtY2hlY2tlZFwiIDogXCJ1aS1pY29uLWJsYW5rXCI7XG5cdFx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0aGlzLmljb24sIG51bGwsIGNoZWNrZWQgPyBcInVpLWljb24tYmxhbmtcIiA6IFwidWktaWNvbi1jaGVja1wiICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0b0FkZCArPSBcInVpLWljb24tYmxhbmtcIjtcblx0XHRcdH1cblx0XHRcdHRoaXMuX2FkZENsYXNzKCB0aGlzLmljb24sIFwidWktY2hlY2tib3hyYWRpby1pY29uXCIsIHRvQWRkICk7XG5cdFx0XHRpZiAoICFjaGVja2VkICkge1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVDbGFzcyggdGhpcy5pY29uLCBudWxsLCBcInVpLWljb24tY2hlY2sgdWktc3RhdGUtY2hlY2tlZFwiICk7XG5cdFx0XHR9XG5cdFx0XHR0aGlzLmljb24ucHJlcGVuZFRvKCB0aGlzLmxhYmVsICkuYWZ0ZXIoIHRoaXMuaWNvblNwYWNlICk7XG5cdFx0fSBlbHNlIGlmICggdGhpcy5pY29uICE9PSB1bmRlZmluZWQgKSB7XG5cdFx0XHR0aGlzLmljb24ucmVtb3ZlKCk7XG5cdFx0XHR0aGlzLmljb25TcGFjZS5yZW1vdmUoKTtcblx0XHRcdGRlbGV0ZSB0aGlzLmljb247XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVMYWJlbDogZnVuY3Rpb24oKSB7XG5cblx0XHQvLyBSZW1vdmUgdGhlIGNvbnRlbnRzIG9mIHRoZSBsYWJlbCAoIG1pbnVzIHRoZSBpY29uLCBpY29uIHNwYWNlLCBhbmQgaW5wdXQgKVxuXHRcdHZhciBjb250ZW50cyA9IHRoaXMubGFiZWwuY29udGVudHMoKS5ub3QoIHRoaXMuZWxlbWVudFsgMCBdICk7XG5cdFx0aWYgKCB0aGlzLmljb24gKSB7XG5cdFx0XHRjb250ZW50cyA9IGNvbnRlbnRzLm5vdCggdGhpcy5pY29uWyAwIF0gKTtcblx0XHR9XG5cdFx0aWYgKCB0aGlzLmljb25TcGFjZSApIHtcblx0XHRcdGNvbnRlbnRzID0gY29udGVudHMubm90KCB0aGlzLmljb25TcGFjZVsgMCBdICk7XG5cdFx0fVxuXHRcdGNvbnRlbnRzLnJlbW92ZSgpO1xuXG5cdFx0dGhpcy5sYWJlbC5hcHBlbmQoIHRoaXMub3B0aW9ucy5sYWJlbCApO1xuXHR9LFxuXG5cdHJlZnJlc2g6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBjaGVja2VkID0gdGhpcy5lbGVtZW50WyAwIF0uY2hlY2tlZCxcblx0XHRcdGlzRGlzYWJsZWQgPSB0aGlzLmVsZW1lbnRbIDAgXS5kaXNhYmxlZDtcblxuXHRcdHRoaXMuX3VwZGF0ZUljb24oIGNoZWNrZWQgKTtcblx0XHR0aGlzLl90b2dnbGVDbGFzcyggdGhpcy5sYWJlbCwgXCJ1aS1jaGVja2JveHJhZGlvLWNoZWNrZWRcIiwgXCJ1aS1zdGF0ZS1hY3RpdmVcIiwgY2hlY2tlZCApO1xuXHRcdGlmICggdGhpcy5vcHRpb25zLmxhYmVsICE9PSBudWxsICkge1xuXHRcdFx0dGhpcy5fdXBkYXRlTGFiZWwoKTtcblx0XHR9XG5cblx0XHRpZiAoIGlzRGlzYWJsZWQgIT09IHRoaXMub3B0aW9ucy5kaXNhYmxlZCApIHtcblx0XHRcdHRoaXMuX3NldE9wdGlvbnMoIHsgXCJkaXNhYmxlZFwiOiBpc0Rpc2FibGVkIH0gKTtcblx0XHR9XG5cdH1cblxufSBdICk7XG5cbnZhciB3aWRnZXRzQ2hlY2tib3hyYWRpbyA9ICQudWkuY2hlY2tib3hyYWRpbztcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBCdXR0b24gMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IEJ1dHRvblxuLy8+Pmdyb3VwOiBXaWRnZXRzXG4vLz4+ZGVzY3JpcHRpb246IEVuaGFuY2VzIGEgZm9ybSB3aXRoIHRoZW1lYWJsZSBidXR0b25zLlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL2J1dHRvbi9cbi8vPj5kZW1vczogaHR0cDovL2pxdWVyeXVpLmNvbS9idXR0b24vXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2UvY29yZS5jc3Ncbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9idXR0b24uY3NzXG4vLz4+Y3NzLnRoZW1lOiAuLi8uLi90aGVtZXMvYmFzZS90aGVtZS5jc3NcblxuXG5cbiQud2lkZ2V0KCBcInVpLmJ1dHRvblwiLCB7XG5cdHZlcnNpb246IFwiMS4xMi4xXCIsXG5cdGRlZmF1bHRFbGVtZW50OiBcIjxidXR0b24+XCIsXG5cdG9wdGlvbnM6IHtcblx0XHRjbGFzc2VzOiB7XG5cdFx0XHRcInVpLWJ1dHRvblwiOiBcInVpLWNvcm5lci1hbGxcIlxuXHRcdH0sXG5cdFx0ZGlzYWJsZWQ6IG51bGwsXG5cdFx0aWNvbjogbnVsbCxcblx0XHRpY29uUG9zaXRpb246IFwiYmVnaW5uaW5nXCIsXG5cdFx0bGFiZWw6IG51bGwsXG5cdFx0c2hvd0xhYmVsOiB0cnVlXG5cdH0sXG5cblx0X2dldENyZWF0ZU9wdGlvbnM6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBkaXNhYmxlZCxcblxuXHRcdFx0Ly8gVGhpcyBpcyB0byBzdXBwb3J0IGNhc2VzIGxpa2UgaW4galF1ZXJ5IE1vYmlsZSB3aGVyZSB0aGUgYmFzZSB3aWRnZXQgZG9lcyBoYXZlXG5cdFx0XHQvLyBhbiBpbXBsZW1lbnRhdGlvbiBvZiBfZ2V0Q3JlYXRlT3B0aW9uc1xuXHRcdFx0b3B0aW9ucyA9IHRoaXMuX3N1cGVyKCkgfHwge307XG5cblx0XHR0aGlzLmlzSW5wdXQgPSB0aGlzLmVsZW1lbnQuaXMoIFwiaW5wdXRcIiApO1xuXG5cdFx0ZGlzYWJsZWQgPSB0aGlzLmVsZW1lbnRbIDAgXS5kaXNhYmxlZDtcblx0XHRpZiAoIGRpc2FibGVkICE9IG51bGwgKSB7XG5cdFx0XHRvcHRpb25zLmRpc2FibGVkID0gZGlzYWJsZWQ7XG5cdFx0fVxuXG5cdFx0dGhpcy5vcmlnaW5hbExhYmVsID0gdGhpcy5pc0lucHV0ID8gdGhpcy5lbGVtZW50LnZhbCgpIDogdGhpcy5lbGVtZW50Lmh0bWwoKTtcblx0XHRpZiAoIHRoaXMub3JpZ2luYWxMYWJlbCApIHtcblx0XHRcdG9wdGlvbnMubGFiZWwgPSB0aGlzLm9yaWdpbmFsTGFiZWw7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIG9wdGlvbnM7XG5cdH0sXG5cblx0X2NyZWF0ZTogZnVuY3Rpb24oKSB7XG5cdFx0aWYgKCAhdGhpcy5vcHRpb24uc2hvd0xhYmVsICYgIXRoaXMub3B0aW9ucy5pY29uICkge1xuXHRcdFx0dGhpcy5vcHRpb25zLnNob3dMYWJlbCA9IHRydWU7XG5cdFx0fVxuXG5cdFx0Ly8gV2UgaGF2ZSB0byBjaGVjayB0aGUgb3B0aW9uIGFnYWluIGhlcmUgZXZlbiB0aG91Z2ggd2UgZGlkIGluIF9nZXRDcmVhdGVPcHRpb25zLFxuXHRcdC8vIGJlY2F1c2UgbnVsbCBtYXkgaGF2ZSBiZWVuIHBhc3NlZCBvbiBpbml0IHdoaWNoIHdvdWxkIG92ZXJyaWRlIHdoYXQgd2FzIHNldCBpblxuXHRcdC8vIF9nZXRDcmVhdGVPcHRpb25zXG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMuZGlzYWJsZWQgPT0gbnVsbCApIHtcblx0XHRcdHRoaXMub3B0aW9ucy5kaXNhYmxlZCA9IHRoaXMuZWxlbWVudFsgMCBdLmRpc2FibGVkIHx8IGZhbHNlO1xuXHRcdH1cblxuXHRcdHRoaXMuaGFzVGl0bGUgPSAhIXRoaXMuZWxlbWVudC5hdHRyKCBcInRpdGxlXCIgKTtcblxuXHRcdC8vIENoZWNrIHRvIHNlZSBpZiB0aGUgbGFiZWwgbmVlZHMgdG8gYmUgc2V0IG9yIGlmIGl0cyBhbHJlYWR5IGNvcnJlY3Rcblx0XHRpZiAoIHRoaXMub3B0aW9ucy5sYWJlbCAmJiB0aGlzLm9wdGlvbnMubGFiZWwgIT09IHRoaXMub3JpZ2luYWxMYWJlbCApIHtcblx0XHRcdGlmICggdGhpcy5pc0lucHV0ICkge1xuXHRcdFx0XHR0aGlzLmVsZW1lbnQudmFsKCB0aGlzLm9wdGlvbnMubGFiZWwgKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMuZWxlbWVudC5odG1sKCB0aGlzLm9wdGlvbnMubGFiZWwgKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIFwidWktYnV0dG9uXCIsIFwidWktd2lkZ2V0XCIgKTtcblx0XHR0aGlzLl9zZXRPcHRpb24oIFwiZGlzYWJsZWRcIiwgdGhpcy5vcHRpb25zLmRpc2FibGVkICk7XG5cdFx0dGhpcy5fZW5oYW5jZSgpO1xuXG5cdFx0aWYgKCB0aGlzLmVsZW1lbnQuaXMoIFwiYVwiICkgKSB7XG5cdFx0XHR0aGlzLl9vbigge1xuXHRcdFx0XHRcImtleXVwXCI6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0XHRpZiAoIGV2ZW50LmtleUNvZGUgPT09ICQudWkua2V5Q29kZS5TUEFDRSApIHtcblx0XHRcdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cblx0XHRcdFx0XHRcdC8vIFN1cHBvcnQ6IFBoYW50b21KUyA8PSAxLjksIElFIDggT25seVxuXHRcdFx0XHRcdFx0Ly8gSWYgYSBuYXRpdmUgY2xpY2sgaXMgYXZhaWxhYmxlIHVzZSBpdCBzbyB3ZSBhY3R1YWxseSBjYXVzZSBuYXZpZ2F0aW9uXG5cdFx0XHRcdFx0XHQvLyBvdGhlcndpc2UganVzdCB0cmlnZ2VyIGEgY2xpY2sgZXZlbnRcblx0XHRcdFx0XHRcdGlmICggdGhpcy5lbGVtZW50WyAwIF0uY2xpY2sgKSB7XG5cdFx0XHRcdFx0XHRcdHRoaXMuZWxlbWVudFsgMCBdLmNsaWNrKCk7XG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0XHR0aGlzLmVsZW1lbnQudHJpZ2dlciggXCJjbGlja1wiICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9ICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9lbmhhbmNlOiBmdW5jdGlvbigpIHtcblx0XHRpZiAoICF0aGlzLmVsZW1lbnQuaXMoIFwiYnV0dG9uXCIgKSApIHtcblx0XHRcdHRoaXMuZWxlbWVudC5hdHRyKCBcInJvbGVcIiwgXCJidXR0b25cIiApO1xuXHRcdH1cblxuXHRcdGlmICggdGhpcy5vcHRpb25zLmljb24gKSB7XG5cdFx0XHR0aGlzLl91cGRhdGVJY29uKCBcImljb25cIiwgdGhpcy5vcHRpb25zLmljb24gKTtcblx0XHRcdHRoaXMuX3VwZGF0ZVRvb2x0aXAoKTtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVRvb2x0aXA6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMudGl0bGUgPSB0aGlzLmVsZW1lbnQuYXR0ciggXCJ0aXRsZVwiICk7XG5cblx0XHRpZiAoICF0aGlzLm9wdGlvbnMuc2hvd0xhYmVsICYmICF0aGlzLnRpdGxlICkge1xuXHRcdFx0dGhpcy5lbGVtZW50LmF0dHIoIFwidGl0bGVcIiwgdGhpcy5vcHRpb25zLmxhYmVsICk7XG5cdFx0fVxuXHR9LFxuXG5cdF91cGRhdGVJY29uOiBmdW5jdGlvbiggb3B0aW9uLCB2YWx1ZSApIHtcblx0XHR2YXIgaWNvbiA9IG9wdGlvbiAhPT0gXCJpY29uUG9zaXRpb25cIixcblx0XHRcdHBvc2l0aW9uID0gaWNvbiA/IHRoaXMub3B0aW9ucy5pY29uUG9zaXRpb24gOiB2YWx1ZSxcblx0XHRcdGRpc3BsYXlCbG9jayA9IHBvc2l0aW9uID09PSBcInRvcFwiIHx8IHBvc2l0aW9uID09PSBcImJvdHRvbVwiO1xuXG5cdFx0Ly8gQ3JlYXRlIGljb25cblx0XHRpZiAoICF0aGlzLmljb24gKSB7XG5cdFx0XHR0aGlzLmljb24gPSAkKCBcIjxzcGFuPlwiICk7XG5cblx0XHRcdHRoaXMuX2FkZENsYXNzKCB0aGlzLmljb24sIFwidWktYnV0dG9uLWljb25cIiwgXCJ1aS1pY29uXCIgKTtcblxuXHRcdFx0aWYgKCAhdGhpcy5vcHRpb25zLnNob3dMYWJlbCApIHtcblx0XHRcdFx0dGhpcy5fYWRkQ2xhc3MoIFwidWktYnV0dG9uLWljb24tb25seVwiICk7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIGlmICggaWNvbiApIHtcblxuXHRcdFx0Ly8gSWYgd2UgYXJlIHVwZGF0aW5nIHRoZSBpY29uIHJlbW92ZSB0aGUgb2xkIGljb24gY2xhc3Ncblx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0aGlzLmljb24sIG51bGwsIHRoaXMub3B0aW9ucy5pY29uICk7XG5cdFx0fVxuXG5cdFx0Ly8gSWYgd2UgYXJlIHVwZGF0aW5nIHRoZSBpY29uIGFkZCB0aGUgbmV3IGljb24gY2xhc3Ncblx0XHRpZiAoIGljb24gKSB7XG5cdFx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5pY29uLCBudWxsLCB2YWx1ZSApO1xuXHRcdH1cblxuXHRcdHRoaXMuX2F0dGFjaEljb24oIHBvc2l0aW9uICk7XG5cblx0XHQvLyBJZiB0aGUgaWNvbiBpcyBvbiB0b3Agb3IgYm90dG9tIHdlIG5lZWQgdG8gYWRkIHRoZSB1aS13aWRnZXQtaWNvbi1ibG9jayBjbGFzcyBhbmQgcmVtb3ZlXG5cdFx0Ly8gdGhlIGljb25TcGFjZSBpZiB0aGVyZSBpcyBvbmUuXG5cdFx0aWYgKCBkaXNwbGF5QmxvY2sgKSB7XG5cdFx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5pY29uLCBudWxsLCBcInVpLXdpZGdldC1pY29uLWJsb2NrXCIgKTtcblx0XHRcdGlmICggdGhpcy5pY29uU3BhY2UgKSB7XG5cdFx0XHRcdHRoaXMuaWNvblNwYWNlLnJlbW92ZSgpO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cblx0XHRcdC8vIFBvc2l0aW9uIGlzIGJlZ2lubmluZyBvciBlbmQgc28gcmVtb3ZlIHRoZSB1aS13aWRnZXQtaWNvbi1ibG9jayBjbGFzcyBhbmQgYWRkIHRoZVxuXHRcdFx0Ly8gc3BhY2UgaWYgaXQgZG9lcyBub3QgZXhpc3Rcblx0XHRcdGlmICggIXRoaXMuaWNvblNwYWNlICkge1xuXHRcdFx0XHR0aGlzLmljb25TcGFjZSA9ICQoIFwiPHNwYW4+IDwvc3Bhbj5cIiApO1xuXHRcdFx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5pY29uU3BhY2UsIFwidWktYnV0dG9uLWljb24tc3BhY2VcIiApO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIHRoaXMuaWNvbiwgbnVsbCwgXCJ1aS13aWdldC1pY29uLWJsb2NrXCIgKTtcblx0XHRcdHRoaXMuX2F0dGFjaEljb25TcGFjZSggcG9zaXRpb24gKTtcblx0XHR9XG5cdH0sXG5cblx0X2Rlc3Ryb3k6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuZWxlbWVudC5yZW1vdmVBdHRyKCBcInJvbGVcIiApO1xuXG5cdFx0aWYgKCB0aGlzLmljb24gKSB7XG5cdFx0XHR0aGlzLmljb24ucmVtb3ZlKCk7XG5cdFx0fVxuXHRcdGlmICggdGhpcy5pY29uU3BhY2UgKSB7XG5cdFx0XHR0aGlzLmljb25TcGFjZS5yZW1vdmUoKTtcblx0XHR9XG5cdFx0aWYgKCAhdGhpcy5oYXNUaXRsZSApIHtcblx0XHRcdHRoaXMuZWxlbWVudC5yZW1vdmVBdHRyKCBcInRpdGxlXCIgKTtcblx0XHR9XG5cdH0sXG5cblx0X2F0dGFjaEljb25TcGFjZTogZnVuY3Rpb24oIGljb25Qb3NpdGlvbiApIHtcblx0XHR0aGlzLmljb25bIC9eKD86ZW5kfGJvdHRvbSkvLnRlc3QoIGljb25Qb3NpdGlvbiApID8gXCJiZWZvcmVcIiA6IFwiYWZ0ZXJcIiBdKCB0aGlzLmljb25TcGFjZSApO1xuXHR9LFxuXG5cdF9hdHRhY2hJY29uOiBmdW5jdGlvbiggaWNvblBvc2l0aW9uICkge1xuXHRcdHRoaXMuZWxlbWVudFsgL14oPzplbmR8Ym90dG9tKS8udGVzdCggaWNvblBvc2l0aW9uICkgPyBcImFwcGVuZFwiIDogXCJwcmVwZW5kXCIgXSggdGhpcy5pY29uICk7XG5cdH0sXG5cblx0X3NldE9wdGlvbnM6IGZ1bmN0aW9uKCBvcHRpb25zICkge1xuXHRcdHZhciBuZXdTaG93TGFiZWwgPSBvcHRpb25zLnNob3dMYWJlbCA9PT0gdW5kZWZpbmVkID9cblx0XHRcdFx0dGhpcy5vcHRpb25zLnNob3dMYWJlbCA6XG5cdFx0XHRcdG9wdGlvbnMuc2hvd0xhYmVsLFxuXHRcdFx0bmV3SWNvbiA9IG9wdGlvbnMuaWNvbiA9PT0gdW5kZWZpbmVkID8gdGhpcy5vcHRpb25zLmljb24gOiBvcHRpb25zLmljb247XG5cblx0XHRpZiAoICFuZXdTaG93TGFiZWwgJiYgIW5ld0ljb24gKSB7XG5cdFx0XHRvcHRpb25zLnNob3dMYWJlbCA9IHRydWU7XG5cdFx0fVxuXHRcdHRoaXMuX3N1cGVyKCBvcHRpb25zICk7XG5cdH0sXG5cblx0X3NldE9wdGlvbjogZnVuY3Rpb24oIGtleSwgdmFsdWUgKSB7XG5cdFx0aWYgKCBrZXkgPT09IFwiaWNvblwiICkge1xuXHRcdFx0aWYgKCB2YWx1ZSApIHtcblx0XHRcdFx0dGhpcy5fdXBkYXRlSWNvbigga2V5LCB2YWx1ZSApO1xuXHRcdFx0fSBlbHNlIGlmICggdGhpcy5pY29uICkge1xuXHRcdFx0XHR0aGlzLmljb24ucmVtb3ZlKCk7XG5cdFx0XHRcdGlmICggdGhpcy5pY29uU3BhY2UgKSB7XG5cdFx0XHRcdFx0dGhpcy5pY29uU3BhY2UucmVtb3ZlKCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoIGtleSA9PT0gXCJpY29uUG9zaXRpb25cIiApIHtcblx0XHRcdHRoaXMuX3VwZGF0ZUljb24oIGtleSwgdmFsdWUgKTtcblx0XHR9XG5cblx0XHQvLyBNYWtlIHN1cmUgd2UgY2FuJ3QgZW5kIHVwIHdpdGggYSBidXR0b24gdGhhdCBoYXMgbmVpdGhlciB0ZXh0IG5vciBpY29uXG5cdFx0aWYgKCBrZXkgPT09IFwic2hvd0xhYmVsXCIgKSB7XG5cdFx0XHRcdHRoaXMuX3RvZ2dsZUNsYXNzKCBcInVpLWJ1dHRvbi1pY29uLW9ubHlcIiwgbnVsbCwgIXZhbHVlICk7XG5cdFx0XHRcdHRoaXMuX3VwZGF0ZVRvb2x0aXAoKTtcblx0XHR9XG5cblx0XHRpZiAoIGtleSA9PT0gXCJsYWJlbFwiICkge1xuXHRcdFx0aWYgKCB0aGlzLmlzSW5wdXQgKSB7XG5cdFx0XHRcdHRoaXMuZWxlbWVudC52YWwoIHZhbHVlICk7XG5cdFx0XHR9IGVsc2Uge1xuXG5cdFx0XHRcdC8vIElmIHRoZXJlIGlzIGFuIGljb24sIGFwcGVuZCBpdCwgZWxzZSBub3RoaW5nIHRoZW4gYXBwZW5kIHRoZSB2YWx1ZVxuXHRcdFx0XHQvLyB0aGlzIGF2b2lkcyByZW1vdmFsIG9mIHRoZSBpY29uIHdoZW4gc2V0dGluZyBsYWJlbCB0ZXh0XG5cdFx0XHRcdHRoaXMuZWxlbWVudC5odG1sKCB2YWx1ZSApO1xuXHRcdFx0XHRpZiAoIHRoaXMuaWNvbiApIHtcblx0XHRcdFx0XHR0aGlzLl9hdHRhY2hJY29uKCB0aGlzLm9wdGlvbnMuaWNvblBvc2l0aW9uICk7XG5cdFx0XHRcdFx0dGhpcy5fYXR0YWNoSWNvblNwYWNlKCB0aGlzLm9wdGlvbnMuaWNvblBvc2l0aW9uICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLl9zdXBlcigga2V5LCB2YWx1ZSApO1xuXG5cdFx0aWYgKCBrZXkgPT09IFwiZGlzYWJsZWRcIiApIHtcblx0XHRcdHRoaXMuX3RvZ2dsZUNsYXNzKCBudWxsLCBcInVpLXN0YXRlLWRpc2FibGVkXCIsIHZhbHVlICk7XG5cdFx0XHR0aGlzLmVsZW1lbnRbIDAgXS5kaXNhYmxlZCA9IHZhbHVlO1xuXHRcdFx0aWYgKCB2YWx1ZSApIHtcblx0XHRcdFx0dGhpcy5lbGVtZW50LmJsdXIoKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0cmVmcmVzaDogZnVuY3Rpb24oKSB7XG5cblx0XHQvLyBNYWtlIHN1cmUgdG8gb25seSBjaGVjayBkaXNhYmxlZCBpZiBpdHMgYW4gZWxlbWVudCB0aGF0IHN1cHBvcnRzIHRoaXMgb3RoZXJ3aXNlXG5cdFx0Ly8gY2hlY2sgZm9yIHRoZSBkaXNhYmxlZCBjbGFzcyB0byBkZXRlcm1pbmUgc3RhdGVcblx0XHR2YXIgaXNEaXNhYmxlZCA9IHRoaXMuZWxlbWVudC5pcyggXCJpbnB1dCwgYnV0dG9uXCIgKSA/XG5cdFx0XHR0aGlzLmVsZW1lbnRbIDAgXS5kaXNhYmxlZCA6IHRoaXMuZWxlbWVudC5oYXNDbGFzcyggXCJ1aS1idXR0b24tZGlzYWJsZWRcIiApO1xuXG5cdFx0aWYgKCBpc0Rpc2FibGVkICE9PSB0aGlzLm9wdGlvbnMuZGlzYWJsZWQgKSB7XG5cdFx0XHR0aGlzLl9zZXRPcHRpb25zKCB7IGRpc2FibGVkOiBpc0Rpc2FibGVkIH0gKTtcblx0XHR9XG5cblx0XHR0aGlzLl91cGRhdGVUb29sdGlwKCk7XG5cdH1cbn0gKTtcblxuLy8gREVQUkVDQVRFRFxuaWYgKCAkLnVpQmFja0NvbXBhdCAhPT0gZmFsc2UgKSB7XG5cblx0Ly8gVGV4dCBhbmQgSWNvbnMgb3B0aW9uc1xuXHQkLndpZGdldCggXCJ1aS5idXR0b25cIiwgJC51aS5idXR0b24sIHtcblx0XHRvcHRpb25zOiB7XG5cdFx0XHR0ZXh0OiB0cnVlLFxuXHRcdFx0aWNvbnM6IHtcblx0XHRcdFx0cHJpbWFyeTogbnVsbCxcblx0XHRcdFx0c2Vjb25kYXJ5OiBudWxsXG5cdFx0XHR9XG5cdFx0fSxcblxuXHRcdF9jcmVhdGU6IGZ1bmN0aW9uKCkge1xuXHRcdFx0aWYgKCB0aGlzLm9wdGlvbnMuc2hvd0xhYmVsICYmICF0aGlzLm9wdGlvbnMudGV4dCApIHtcblx0XHRcdFx0dGhpcy5vcHRpb25zLnNob3dMYWJlbCA9IHRoaXMub3B0aW9ucy50ZXh0O1xuXHRcdFx0fVxuXHRcdFx0aWYgKCAhdGhpcy5vcHRpb25zLnNob3dMYWJlbCAmJiB0aGlzLm9wdGlvbnMudGV4dCApIHtcblx0XHRcdFx0dGhpcy5vcHRpb25zLnRleHQgPSB0aGlzLm9wdGlvbnMuc2hvd0xhYmVsO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCAhdGhpcy5vcHRpb25zLmljb24gJiYgKCB0aGlzLm9wdGlvbnMuaWNvbnMucHJpbWFyeSB8fFxuXHRcdFx0XHRcdHRoaXMub3B0aW9ucy5pY29ucy5zZWNvbmRhcnkgKSApIHtcblx0XHRcdFx0aWYgKCB0aGlzLm9wdGlvbnMuaWNvbnMucHJpbWFyeSApIHtcblx0XHRcdFx0XHR0aGlzLm9wdGlvbnMuaWNvbiA9IHRoaXMub3B0aW9ucy5pY29ucy5wcmltYXJ5O1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHRoaXMub3B0aW9ucy5pY29uID0gdGhpcy5vcHRpb25zLmljb25zLnNlY29uZGFyeTtcblx0XHRcdFx0XHR0aGlzLm9wdGlvbnMuaWNvblBvc2l0aW9uID0gXCJlbmRcIjtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIGlmICggdGhpcy5vcHRpb25zLmljb24gKSB7XG5cdFx0XHRcdHRoaXMub3B0aW9ucy5pY29ucy5wcmltYXJ5ID0gdGhpcy5vcHRpb25zLmljb247XG5cdFx0XHR9XG5cdFx0XHR0aGlzLl9zdXBlcigpO1xuXHRcdH0sXG5cblx0XHRfc2V0T3B0aW9uOiBmdW5jdGlvbigga2V5LCB2YWx1ZSApIHtcblx0XHRcdGlmICgga2V5ID09PSBcInRleHRcIiApIHtcblx0XHRcdFx0dGhpcy5fc3VwZXIoIFwic2hvd0xhYmVsXCIsIHZhbHVlICk7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblx0XHRcdGlmICgga2V5ID09PSBcInNob3dMYWJlbFwiICkge1xuXHRcdFx0XHR0aGlzLm9wdGlvbnMudGV4dCA9IHZhbHVlO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCBrZXkgPT09IFwiaWNvblwiICkge1xuXHRcdFx0XHR0aGlzLm9wdGlvbnMuaWNvbnMucHJpbWFyeSA9IHZhbHVlO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCBrZXkgPT09IFwiaWNvbnNcIiApIHtcblx0XHRcdFx0aWYgKCB2YWx1ZS5wcmltYXJ5ICkge1xuXHRcdFx0XHRcdHRoaXMuX3N1cGVyKCBcImljb25cIiwgdmFsdWUucHJpbWFyeSApO1xuXHRcdFx0XHRcdHRoaXMuX3N1cGVyKCBcImljb25Qb3NpdGlvblwiLCBcImJlZ2lubmluZ1wiICk7XG5cdFx0XHRcdH0gZWxzZSBpZiAoIHZhbHVlLnNlY29uZGFyeSApIHtcblx0XHRcdFx0XHR0aGlzLl9zdXBlciggXCJpY29uXCIsIHZhbHVlLnNlY29uZGFyeSApO1xuXHRcdFx0XHRcdHRoaXMuX3N1cGVyKCBcImljb25Qb3NpdGlvblwiLCBcImVuZFwiICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdHRoaXMuX3N1cGVyQXBwbHkoIGFyZ3VtZW50cyApO1xuXHRcdH1cblx0fSApO1xuXG5cdCQuZm4uYnV0dG9uID0gKCBmdW5jdGlvbiggb3JpZyApIHtcblx0XHRyZXR1cm4gZnVuY3Rpb24oKSB7XG5cdFx0XHRpZiAoICF0aGlzLmxlbmd0aCB8fCAoIHRoaXMubGVuZ3RoICYmIHRoaXNbIDAgXS50YWdOYW1lICE9PSBcIklOUFVUXCIgKSB8fFxuXHRcdFx0XHRcdCggdGhpcy5sZW5ndGggJiYgdGhpc1sgMCBdLnRhZ05hbWUgPT09IFwiSU5QVVRcIiAmJiAoXG5cdFx0XHRcdFx0XHR0aGlzLmF0dHIoIFwidHlwZVwiICkgIT09IFwiY2hlY2tib3hcIiAmJiB0aGlzLmF0dHIoIFwidHlwZVwiICkgIT09IFwicmFkaW9cIlxuXHRcdFx0XHRcdCkgKSApIHtcblx0XHRcdFx0cmV0dXJuIG9yaWcuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCAhJC51aS5jaGVja2JveHJhZGlvICkge1xuXHRcdFx0XHQkLmVycm9yKCBcIkNoZWNrYm94cmFkaW8gd2lkZ2V0IG1pc3NpbmdcIiApO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCBhcmd1bWVudHMubGVuZ3RoID09PSAwICkge1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5jaGVja2JveHJhZGlvKCB7XG5cdFx0XHRcdFx0XCJpY29uXCI6IGZhbHNlXG5cdFx0XHRcdH0gKTtcblx0XHRcdH1cblx0XHRcdHJldHVybiB0aGlzLmNoZWNrYm94cmFkaW8uYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdH07XG5cdH0gKSggJC5mbi5idXR0b24gKTtcblxuXHQkLmZuLmJ1dHRvbnNldCA9IGZ1bmN0aW9uKCkge1xuXHRcdGlmICggISQudWkuY29udHJvbGdyb3VwICkge1xuXHRcdFx0JC5lcnJvciggXCJDb250cm9sZ3JvdXAgd2lkZ2V0IG1pc3NpbmdcIiApO1xuXHRcdH1cblx0XHRpZiAoIGFyZ3VtZW50c1sgMCBdID09PSBcIm9wdGlvblwiICYmIGFyZ3VtZW50c1sgMSBdID09PSBcIml0ZW1zXCIgJiYgYXJndW1lbnRzWyAyIF0gKSB7XG5cdFx0XHRyZXR1cm4gdGhpcy5jb250cm9sZ3JvdXAuYXBwbHkoIHRoaXMsXG5cdFx0XHRcdFsgYXJndW1lbnRzWyAwIF0sIFwiaXRlbXMuYnV0dG9uXCIsIGFyZ3VtZW50c1sgMiBdIF0gKTtcblx0XHR9XG5cdFx0aWYgKCBhcmd1bWVudHNbIDAgXSA9PT0gXCJvcHRpb25cIiAmJiBhcmd1bWVudHNbIDEgXSA9PT0gXCJpdGVtc1wiICkge1xuXHRcdFx0cmV0dXJuIHRoaXMuY29udHJvbGdyb3VwLmFwcGx5KCB0aGlzLCBbIGFyZ3VtZW50c1sgMCBdLCBcIml0ZW1zLmJ1dHRvblwiIF0gKTtcblx0XHR9XG5cdFx0aWYgKCB0eXBlb2YgYXJndW1lbnRzWyAwIF0gPT09IFwib2JqZWN0XCIgJiYgYXJndW1lbnRzWyAwIF0uaXRlbXMgKSB7XG5cdFx0XHRhcmd1bWVudHNbIDAgXS5pdGVtcyA9IHtcblx0XHRcdFx0YnV0dG9uOiBhcmd1bWVudHNbIDAgXS5pdGVtc1xuXHRcdFx0fTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXMuY29udHJvbGdyb3VwLmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcblx0fTtcbn1cblxudmFyIHdpZGdldHNCdXR0b24gPSAkLnVpLmJ1dHRvbjtcblxuXG4vLyBqc2NzOmRpc2FibGUgbWF4aW11bUxpbmVMZW5ndGhcbi8qIGpzY3M6ZGlzYWJsZSByZXF1aXJlQ2FtZWxDYXNlT3JVcHBlckNhc2VJZGVudGlmaWVycyAqL1xuLyohXG4gKiBqUXVlcnkgVUkgRGF0ZXBpY2tlciAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogRGF0ZXBpY2tlclxuLy8+Pmdyb3VwOiBXaWRnZXRzXG4vLz4+ZGVzY3JpcHRpb246IERpc3BsYXlzIGEgY2FsZW5kYXIgZnJvbSBhbiBpbnB1dCBvciBpbmxpbmUgZm9yIHNlbGVjdGluZyBkYXRlcy5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9kYXRlcGlja2VyL1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL2RhdGVwaWNrZXIvXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2UvY29yZS5jc3Ncbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9kYXRlcGlja2VyLmNzc1xuLy8+PmNzcy50aGVtZTogLi4vLi4vdGhlbWVzL2Jhc2UvdGhlbWUuY3NzXG5cblxuXG4kLmV4dGVuZCggJC51aSwgeyBkYXRlcGlja2VyOiB7IHZlcnNpb246IFwiMS4xMi4xXCIgfSB9ICk7XG5cbnZhciBkYXRlcGlja2VyX2luc3RBY3RpdmU7XG5cbmZ1bmN0aW9uIGRhdGVwaWNrZXJfZ2V0WmluZGV4KCBlbGVtICkge1xuXHR2YXIgcG9zaXRpb24sIHZhbHVlO1xuXHR3aGlsZSAoIGVsZW0ubGVuZ3RoICYmIGVsZW1bIDAgXSAhPT0gZG9jdW1lbnQgKSB7XG5cblx0XHQvLyBJZ25vcmUgei1pbmRleCBpZiBwb3NpdGlvbiBpcyBzZXQgdG8gYSB2YWx1ZSB3aGVyZSB6LWluZGV4IGlzIGlnbm9yZWQgYnkgdGhlIGJyb3dzZXJcblx0XHQvLyBUaGlzIG1ha2VzIGJlaGF2aW9yIG9mIHRoaXMgZnVuY3Rpb24gY29uc2lzdGVudCBhY3Jvc3MgYnJvd3NlcnNcblx0XHQvLyBXZWJLaXQgYWx3YXlzIHJldHVybnMgYXV0byBpZiB0aGUgZWxlbWVudCBpcyBwb3NpdGlvbmVkXG5cdFx0cG9zaXRpb24gPSBlbGVtLmNzcyggXCJwb3NpdGlvblwiICk7XG5cdFx0aWYgKCBwb3NpdGlvbiA9PT0gXCJhYnNvbHV0ZVwiIHx8IHBvc2l0aW9uID09PSBcInJlbGF0aXZlXCIgfHwgcG9zaXRpb24gPT09IFwiZml4ZWRcIiApIHtcblxuXHRcdFx0Ly8gSUUgcmV0dXJucyAwIHdoZW4gekluZGV4IGlzIG5vdCBzcGVjaWZpZWRcblx0XHRcdC8vIG90aGVyIGJyb3dzZXJzIHJldHVybiBhIHN0cmluZ1xuXHRcdFx0Ly8gd2UgaWdub3JlIHRoZSBjYXNlIG9mIG5lc3RlZCBlbGVtZW50cyB3aXRoIGFuIGV4cGxpY2l0IHZhbHVlIG9mIDBcblx0XHRcdC8vIDxkaXYgc3R5bGU9XCJ6LWluZGV4OiAtMTA7XCI+PGRpdiBzdHlsZT1cInotaW5kZXg6IDA7XCI+PC9kaXY+PC9kaXY+XG5cdFx0XHR2YWx1ZSA9IHBhcnNlSW50KCBlbGVtLmNzcyggXCJ6SW5kZXhcIiApLCAxMCApO1xuXHRcdFx0aWYgKCAhaXNOYU4oIHZhbHVlICkgJiYgdmFsdWUgIT09IDAgKSB7XG5cdFx0XHRcdHJldHVybiB2YWx1ZTtcblx0XHRcdH1cblx0XHR9XG5cdFx0ZWxlbSA9IGVsZW0ucGFyZW50KCk7XG5cdH1cblxuXHRyZXR1cm4gMDtcbn1cbi8qIERhdGUgcGlja2VyIG1hbmFnZXIuXG4gICBVc2UgdGhlIHNpbmdsZXRvbiBpbnN0YW5jZSBvZiB0aGlzIGNsYXNzLCAkLmRhdGVwaWNrZXIsIHRvIGludGVyYWN0IHdpdGggdGhlIGRhdGUgcGlja2VyLlxuICAgU2V0dGluZ3MgZm9yIChncm91cHMgb2YpIGRhdGUgcGlja2VycyBhcmUgbWFpbnRhaW5lZCBpbiBhbiBpbnN0YW5jZSBvYmplY3QsXG4gICBhbGxvd2luZyBtdWx0aXBsZSBkaWZmZXJlbnQgc2V0dGluZ3Mgb24gdGhlIHNhbWUgcGFnZS4gKi9cblxuZnVuY3Rpb24gRGF0ZXBpY2tlcigpIHtcblx0dGhpcy5fY3VySW5zdCA9IG51bGw7IC8vIFRoZSBjdXJyZW50IGluc3RhbmNlIGluIHVzZVxuXHR0aGlzLl9rZXlFdmVudCA9IGZhbHNlOyAvLyBJZiB0aGUgbGFzdCBldmVudCB3YXMgYSBrZXkgZXZlbnRcblx0dGhpcy5fZGlzYWJsZWRJbnB1dHMgPSBbXTsgLy8gTGlzdCBvZiBkYXRlIHBpY2tlciBpbnB1dHMgdGhhdCBoYXZlIGJlZW4gZGlzYWJsZWRcblx0dGhpcy5fZGF0ZXBpY2tlclNob3dpbmcgPSBmYWxzZTsgLy8gVHJ1ZSBpZiB0aGUgcG9wdXAgcGlja2VyIGlzIHNob3dpbmcgLCBmYWxzZSBpZiBub3Rcblx0dGhpcy5faW5EaWFsb2cgPSBmYWxzZTsgLy8gVHJ1ZSBpZiBzaG93aW5nIHdpdGhpbiBhIFwiZGlhbG9nXCIsIGZhbHNlIGlmIG5vdFxuXHR0aGlzLl9tYWluRGl2SWQgPSBcInVpLWRhdGVwaWNrZXItZGl2XCI7IC8vIFRoZSBJRCBvZiB0aGUgbWFpbiBkYXRlcGlja2VyIGRpdmlzaW9uXG5cdHRoaXMuX2lubGluZUNsYXNzID0gXCJ1aS1kYXRlcGlja2VyLWlubGluZVwiOyAvLyBUaGUgbmFtZSBvZiB0aGUgaW5saW5lIG1hcmtlciBjbGFzc1xuXHR0aGlzLl9hcHBlbmRDbGFzcyA9IFwidWktZGF0ZXBpY2tlci1hcHBlbmRcIjsgLy8gVGhlIG5hbWUgb2YgdGhlIGFwcGVuZCBtYXJrZXIgY2xhc3Ncblx0dGhpcy5fdHJpZ2dlckNsYXNzID0gXCJ1aS1kYXRlcGlja2VyLXRyaWdnZXJcIjsgLy8gVGhlIG5hbWUgb2YgdGhlIHRyaWdnZXIgbWFya2VyIGNsYXNzXG5cdHRoaXMuX2RpYWxvZ0NsYXNzID0gXCJ1aS1kYXRlcGlja2VyLWRpYWxvZ1wiOyAvLyBUaGUgbmFtZSBvZiB0aGUgZGlhbG9nIG1hcmtlciBjbGFzc1xuXHR0aGlzLl9kaXNhYmxlQ2xhc3MgPSBcInVpLWRhdGVwaWNrZXItZGlzYWJsZWRcIjsgLy8gVGhlIG5hbWUgb2YgdGhlIGRpc2FibGVkIGNvdmVyaW5nIG1hcmtlciBjbGFzc1xuXHR0aGlzLl91bnNlbGVjdGFibGVDbGFzcyA9IFwidWktZGF0ZXBpY2tlci11bnNlbGVjdGFibGVcIjsgLy8gVGhlIG5hbWUgb2YgdGhlIHVuc2VsZWN0YWJsZSBjZWxsIG1hcmtlciBjbGFzc1xuXHR0aGlzLl9jdXJyZW50Q2xhc3MgPSBcInVpLWRhdGVwaWNrZXItY3VycmVudC1kYXlcIjsgLy8gVGhlIG5hbWUgb2YgdGhlIGN1cnJlbnQgZGF5IG1hcmtlciBjbGFzc1xuXHR0aGlzLl9kYXlPdmVyQ2xhc3MgPSBcInVpLWRhdGVwaWNrZXItZGF5cy1jZWxsLW92ZXJcIjsgLy8gVGhlIG5hbWUgb2YgdGhlIGRheSBob3ZlciBtYXJrZXIgY2xhc3Ncblx0dGhpcy5yZWdpb25hbCA9IFtdOyAvLyBBdmFpbGFibGUgcmVnaW9uYWwgc2V0dGluZ3MsIGluZGV4ZWQgYnkgbGFuZ3VhZ2UgY29kZVxuXHR0aGlzLnJlZ2lvbmFsWyBcIlwiIF0gPSB7IC8vIERlZmF1bHQgcmVnaW9uYWwgc2V0dGluZ3Ncblx0XHRjbG9zZVRleHQ6IFwiRG9uZVwiLCAvLyBEaXNwbGF5IHRleHQgZm9yIGNsb3NlIGxpbmtcblx0XHRwcmV2VGV4dDogXCJQcmV2XCIsIC8vIERpc3BsYXkgdGV4dCBmb3IgcHJldmlvdXMgbW9udGggbGlua1xuXHRcdG5leHRUZXh0OiBcIk5leHRcIiwgLy8gRGlzcGxheSB0ZXh0IGZvciBuZXh0IG1vbnRoIGxpbmtcblx0XHRjdXJyZW50VGV4dDogXCJUb2RheVwiLCAvLyBEaXNwbGF5IHRleHQgZm9yIGN1cnJlbnQgbW9udGggbGlua1xuXHRcdG1vbnRoTmFtZXM6IFsgXCJKYW51YXJ5XCIsXCJGZWJydWFyeVwiLFwiTWFyY2hcIixcIkFwcmlsXCIsXCJNYXlcIixcIkp1bmVcIixcblx0XHRcdFwiSnVseVwiLFwiQXVndXN0XCIsXCJTZXB0ZW1iZXJcIixcIk9jdG9iZXJcIixcIk5vdmVtYmVyXCIsXCJEZWNlbWJlclwiIF0sIC8vIE5hbWVzIG9mIG1vbnRocyBmb3IgZHJvcC1kb3duIGFuZCBmb3JtYXR0aW5nXG5cdFx0bW9udGhOYW1lc1Nob3J0OiBbIFwiSmFuXCIsIFwiRmViXCIsIFwiTWFyXCIsIFwiQXByXCIsIFwiTWF5XCIsIFwiSnVuXCIsIFwiSnVsXCIsIFwiQXVnXCIsIFwiU2VwXCIsIFwiT2N0XCIsIFwiTm92XCIsIFwiRGVjXCIgXSwgLy8gRm9yIGZvcm1hdHRpbmdcblx0XHRkYXlOYW1lczogWyBcIlN1bmRheVwiLCBcIk1vbmRheVwiLCBcIlR1ZXNkYXlcIiwgXCJXZWRuZXNkYXlcIiwgXCJUaHVyc2RheVwiLCBcIkZyaWRheVwiLCBcIlNhdHVyZGF5XCIgXSwgLy8gRm9yIGZvcm1hdHRpbmdcblx0XHRkYXlOYW1lc1Nob3J0OiBbIFwiU3VuXCIsIFwiTW9uXCIsIFwiVHVlXCIsIFwiV2VkXCIsIFwiVGh1XCIsIFwiRnJpXCIsIFwiU2F0XCIgXSwgLy8gRm9yIGZvcm1hdHRpbmdcblx0XHRkYXlOYW1lc01pbjogWyBcIlN1XCIsXCJNb1wiLFwiVHVcIixcIldlXCIsXCJUaFwiLFwiRnJcIixcIlNhXCIgXSwgLy8gQ29sdW1uIGhlYWRpbmdzIGZvciBkYXlzIHN0YXJ0aW5nIGF0IFN1bmRheVxuXHRcdHdlZWtIZWFkZXI6IFwiV2tcIiwgLy8gQ29sdW1uIGhlYWRlciBmb3Igd2VlayBvZiB0aGUgeWVhclxuXHRcdGRhdGVGb3JtYXQ6IFwibW0vZGQveXlcIiwgLy8gU2VlIGZvcm1hdCBvcHRpb25zIG9uIHBhcnNlRGF0ZVxuXHRcdGZpcnN0RGF5OiAwLCAvLyBUaGUgZmlyc3QgZGF5IG9mIHRoZSB3ZWVrLCBTdW4gPSAwLCBNb24gPSAxLCAuLi5cblx0XHRpc1JUTDogZmFsc2UsIC8vIFRydWUgaWYgcmlnaHQtdG8tbGVmdCBsYW5ndWFnZSwgZmFsc2UgaWYgbGVmdC10by1yaWdodFxuXHRcdHNob3dNb250aEFmdGVyWWVhcjogZmFsc2UsIC8vIFRydWUgaWYgdGhlIHllYXIgc2VsZWN0IHByZWNlZGVzIG1vbnRoLCBmYWxzZSBmb3IgbW9udGggdGhlbiB5ZWFyXG5cdFx0eWVhclN1ZmZpeDogXCJcIiAvLyBBZGRpdGlvbmFsIHRleHQgdG8gYXBwZW5kIHRvIHRoZSB5ZWFyIGluIHRoZSBtb250aCBoZWFkZXJzXG5cdH07XG5cdHRoaXMuX2RlZmF1bHRzID0geyAvLyBHbG9iYWwgZGVmYXVsdHMgZm9yIGFsbCB0aGUgZGF0ZSBwaWNrZXIgaW5zdGFuY2VzXG5cdFx0c2hvd09uOiBcImZvY3VzXCIsIC8vIFwiZm9jdXNcIiBmb3IgcG9wdXAgb24gZm9jdXMsXG5cdFx0XHQvLyBcImJ1dHRvblwiIGZvciB0cmlnZ2VyIGJ1dHRvbiwgb3IgXCJib3RoXCIgZm9yIGVpdGhlclxuXHRcdHNob3dBbmltOiBcImZhZGVJblwiLCAvLyBOYW1lIG9mIGpRdWVyeSBhbmltYXRpb24gZm9yIHBvcHVwXG5cdFx0c2hvd09wdGlvbnM6IHt9LCAvLyBPcHRpb25zIGZvciBlbmhhbmNlZCBhbmltYXRpb25zXG5cdFx0ZGVmYXVsdERhdGU6IG51bGwsIC8vIFVzZWQgd2hlbiBmaWVsZCBpcyBibGFuazogYWN0dWFsIGRhdGUsXG5cdFx0XHQvLyArLy1udW1iZXIgZm9yIG9mZnNldCBmcm9tIHRvZGF5LCBudWxsIGZvciB0b2RheVxuXHRcdGFwcGVuZFRleHQ6IFwiXCIsIC8vIERpc3BsYXkgdGV4dCBmb2xsb3dpbmcgdGhlIGlucHV0IGJveCwgZS5nLiBzaG93aW5nIHRoZSBmb3JtYXRcblx0XHRidXR0b25UZXh0OiBcIi4uLlwiLCAvLyBUZXh0IGZvciB0cmlnZ2VyIGJ1dHRvblxuXHRcdGJ1dHRvbkltYWdlOiBcIlwiLCAvLyBVUkwgZm9yIHRyaWdnZXIgYnV0dG9uIGltYWdlXG5cdFx0YnV0dG9uSW1hZ2VPbmx5OiBmYWxzZSwgLy8gVHJ1ZSBpZiB0aGUgaW1hZ2UgYXBwZWFycyBhbG9uZSwgZmFsc2UgaWYgaXQgYXBwZWFycyBvbiBhIGJ1dHRvblxuXHRcdGhpZGVJZk5vUHJldk5leHQ6IGZhbHNlLCAvLyBUcnVlIHRvIGhpZGUgbmV4dC9wcmV2aW91cyBtb250aCBsaW5rc1xuXHRcdFx0Ly8gaWYgbm90IGFwcGxpY2FibGUsIGZhbHNlIHRvIGp1c3QgZGlzYWJsZSB0aGVtXG5cdFx0bmF2aWdhdGlvbkFzRGF0ZUZvcm1hdDogZmFsc2UsIC8vIFRydWUgaWYgZGF0ZSBmb3JtYXR0aW5nIGFwcGxpZWQgdG8gcHJldi90b2RheS9uZXh0IGxpbmtzXG5cdFx0Z290b0N1cnJlbnQ6IGZhbHNlLCAvLyBUcnVlIGlmIHRvZGF5IGxpbmsgZ29lcyBiYWNrIHRvIGN1cnJlbnQgc2VsZWN0aW9uIGluc3RlYWRcblx0XHRjaGFuZ2VNb250aDogZmFsc2UsIC8vIFRydWUgaWYgbW9udGggY2FuIGJlIHNlbGVjdGVkIGRpcmVjdGx5LCBmYWxzZSBpZiBvbmx5IHByZXYvbmV4dFxuXHRcdGNoYW5nZVllYXI6IGZhbHNlLCAvLyBUcnVlIGlmIHllYXIgY2FuIGJlIHNlbGVjdGVkIGRpcmVjdGx5LCBmYWxzZSBpZiBvbmx5IHByZXYvbmV4dFxuXHRcdHllYXJSYW5nZTogXCJjLTEwOmMrMTBcIiwgLy8gUmFuZ2Ugb2YgeWVhcnMgdG8gZGlzcGxheSBpbiBkcm9wLWRvd24sXG5cdFx0XHQvLyBlaXRoZXIgcmVsYXRpdmUgdG8gdG9kYXkncyB5ZWFyICgtbm46K25uKSwgcmVsYXRpdmUgdG8gY3VycmVudGx5IGRpc3BsYXllZCB5ZWFyXG5cdFx0XHQvLyAoYy1ubjpjK25uKSwgYWJzb2x1dGUgKG5ubm46bm5ubiksIG9yIGEgY29tYmluYXRpb24gb2YgdGhlIGFib3ZlIChubm5uOi1uKVxuXHRcdHNob3dPdGhlck1vbnRoczogZmFsc2UsIC8vIFRydWUgdG8gc2hvdyBkYXRlcyBpbiBvdGhlciBtb250aHMsIGZhbHNlIHRvIGxlYXZlIGJsYW5rXG5cdFx0c2VsZWN0T3RoZXJNb250aHM6IGZhbHNlLCAvLyBUcnVlIHRvIGFsbG93IHNlbGVjdGlvbiBvZiBkYXRlcyBpbiBvdGhlciBtb250aHMsIGZhbHNlIGZvciB1bnNlbGVjdGFibGVcblx0XHRzaG93V2VlazogZmFsc2UsIC8vIFRydWUgdG8gc2hvdyB3ZWVrIG9mIHRoZSB5ZWFyLCBmYWxzZSB0byBub3Qgc2hvdyBpdFxuXHRcdGNhbGN1bGF0ZVdlZWs6IHRoaXMuaXNvODYwMVdlZWssIC8vIEhvdyB0byBjYWxjdWxhdGUgdGhlIHdlZWsgb2YgdGhlIHllYXIsXG5cdFx0XHQvLyB0YWtlcyBhIERhdGUgYW5kIHJldHVybnMgdGhlIG51bWJlciBvZiB0aGUgd2VlayBmb3IgaXRcblx0XHRzaG9ydFllYXJDdXRvZmY6IFwiKzEwXCIsIC8vIFNob3J0IHllYXIgdmFsdWVzIDwgdGhpcyBhcmUgaW4gdGhlIGN1cnJlbnQgY2VudHVyeSxcblx0XHRcdC8vID4gdGhpcyBhcmUgaW4gdGhlIHByZXZpb3VzIGNlbnR1cnksXG5cdFx0XHQvLyBzdHJpbmcgdmFsdWUgc3RhcnRpbmcgd2l0aCBcIitcIiBmb3IgY3VycmVudCB5ZWFyICsgdmFsdWVcblx0XHRtaW5EYXRlOiBudWxsLCAvLyBUaGUgZWFybGllc3Qgc2VsZWN0YWJsZSBkYXRlLCBvciBudWxsIGZvciBubyBsaW1pdFxuXHRcdG1heERhdGU6IG51bGwsIC8vIFRoZSBsYXRlc3Qgc2VsZWN0YWJsZSBkYXRlLCBvciBudWxsIGZvciBubyBsaW1pdFxuXHRcdGR1cmF0aW9uOiBcImZhc3RcIiwgLy8gRHVyYXRpb24gb2YgZGlzcGxheS9jbG9zdXJlXG5cdFx0YmVmb3JlU2hvd0RheTogbnVsbCwgLy8gRnVuY3Rpb24gdGhhdCB0YWtlcyBhIGRhdGUgYW5kIHJldHVybnMgYW4gYXJyYXkgd2l0aFxuXHRcdFx0Ly8gWzBdID0gdHJ1ZSBpZiBzZWxlY3RhYmxlLCBmYWxzZSBpZiBub3QsIFsxXSA9IGN1c3RvbSBDU1MgY2xhc3MgbmFtZShzKSBvciBcIlwiLFxuXHRcdFx0Ly8gWzJdID0gY2VsbCB0aXRsZSAob3B0aW9uYWwpLCBlLmcuICQuZGF0ZXBpY2tlci5ub1dlZWtlbmRzXG5cdFx0YmVmb3JlU2hvdzogbnVsbCwgLy8gRnVuY3Rpb24gdGhhdCB0YWtlcyBhbiBpbnB1dCBmaWVsZCBhbmRcblx0XHRcdC8vIHJldHVybnMgYSBzZXQgb2YgY3VzdG9tIHNldHRpbmdzIGZvciB0aGUgZGF0ZSBwaWNrZXJcblx0XHRvblNlbGVjdDogbnVsbCwgLy8gRGVmaW5lIGEgY2FsbGJhY2sgZnVuY3Rpb24gd2hlbiBhIGRhdGUgaXMgc2VsZWN0ZWRcblx0XHRvbkNoYW5nZU1vbnRoWWVhcjogbnVsbCwgLy8gRGVmaW5lIGEgY2FsbGJhY2sgZnVuY3Rpb24gd2hlbiB0aGUgbW9udGggb3IgeWVhciBpcyBjaGFuZ2VkXG5cdFx0b25DbG9zZTogbnVsbCwgLy8gRGVmaW5lIGEgY2FsbGJhY2sgZnVuY3Rpb24gd2hlbiB0aGUgZGF0ZXBpY2tlciBpcyBjbG9zZWRcblx0XHRudW1iZXJPZk1vbnRoczogMSwgLy8gTnVtYmVyIG9mIG1vbnRocyB0byBzaG93IGF0IGEgdGltZVxuXHRcdHNob3dDdXJyZW50QXRQb3M6IDAsIC8vIFRoZSBwb3NpdGlvbiBpbiBtdWx0aXBlIG1vbnRocyBhdCB3aGljaCB0byBzaG93IHRoZSBjdXJyZW50IG1vbnRoIChzdGFydGluZyBhdCAwKVxuXHRcdHN0ZXBNb250aHM6IDEsIC8vIE51bWJlciBvZiBtb250aHMgdG8gc3RlcCBiYWNrL2ZvcndhcmRcblx0XHRzdGVwQmlnTW9udGhzOiAxMiwgLy8gTnVtYmVyIG9mIG1vbnRocyB0byBzdGVwIGJhY2svZm9yd2FyZCBmb3IgdGhlIGJpZyBsaW5rc1xuXHRcdGFsdEZpZWxkOiBcIlwiLCAvLyBTZWxlY3RvciBmb3IgYW4gYWx0ZXJuYXRlIGZpZWxkIHRvIHN0b3JlIHNlbGVjdGVkIGRhdGVzIGludG9cblx0XHRhbHRGb3JtYXQ6IFwiXCIsIC8vIFRoZSBkYXRlIGZvcm1hdCB0byB1c2UgZm9yIHRoZSBhbHRlcm5hdGUgZmllbGRcblx0XHRjb25zdHJhaW5JbnB1dDogdHJ1ZSwgLy8gVGhlIGlucHV0IGlzIGNvbnN0cmFpbmVkIGJ5IHRoZSBjdXJyZW50IGRhdGUgZm9ybWF0XG5cdFx0c2hvd0J1dHRvblBhbmVsOiBmYWxzZSwgLy8gVHJ1ZSB0byBzaG93IGJ1dHRvbiBwYW5lbCwgZmFsc2UgdG8gbm90IHNob3cgaXRcblx0XHRhdXRvU2l6ZTogZmFsc2UsIC8vIFRydWUgdG8gc2l6ZSB0aGUgaW5wdXQgZm9yIHRoZSBkYXRlIGZvcm1hdCwgZmFsc2UgdG8gbGVhdmUgYXMgaXNcblx0XHRkaXNhYmxlZDogZmFsc2UgLy8gVGhlIGluaXRpYWwgZGlzYWJsZWQgc3RhdGVcblx0fTtcblx0JC5leHRlbmQoIHRoaXMuX2RlZmF1bHRzLCB0aGlzLnJlZ2lvbmFsWyBcIlwiIF0gKTtcblx0dGhpcy5yZWdpb25hbC5lbiA9ICQuZXh0ZW5kKCB0cnVlLCB7fSwgdGhpcy5yZWdpb25hbFsgXCJcIiBdICk7XG5cdHRoaXMucmVnaW9uYWxbIFwiZW4tVVNcIiBdID0gJC5leHRlbmQoIHRydWUsIHt9LCB0aGlzLnJlZ2lvbmFsLmVuICk7XG5cdHRoaXMuZHBEaXYgPSBkYXRlcGlja2VyX2JpbmRIb3ZlciggJCggXCI8ZGl2IGlkPSdcIiArIHRoaXMuX21haW5EaXZJZCArIFwiJyBjbGFzcz0ndWktZGF0ZXBpY2tlciB1aS13aWRnZXQgdWktd2lkZ2V0LWNvbnRlbnQgdWktaGVscGVyLWNsZWFyZml4IHVpLWNvcm5lci1hbGwnPjwvZGl2PlwiICkgKTtcbn1cblxuJC5leHRlbmQoIERhdGVwaWNrZXIucHJvdG90eXBlLCB7XG5cdC8qIENsYXNzIG5hbWUgYWRkZWQgdG8gZWxlbWVudHMgdG8gaW5kaWNhdGUgYWxyZWFkeSBjb25maWd1cmVkIHdpdGggYSBkYXRlIHBpY2tlci4gKi9cblx0bWFya2VyQ2xhc3NOYW1lOiBcImhhc0RhdGVwaWNrZXJcIixcblxuXHQvL0tlZXAgdHJhY2sgb2YgdGhlIG1heGltdW0gbnVtYmVyIG9mIHJvd3MgZGlzcGxheWVkIChzZWUgIzcwNDMpXG5cdG1heFJvd3M6IDQsXG5cblx0Ly8gVE9ETyByZW5hbWUgdG8gXCJ3aWRnZXRcIiB3aGVuIHN3aXRjaGluZyB0byB3aWRnZXQgZmFjdG9yeVxuXHRfd2lkZ2V0RGF0ZXBpY2tlcjogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMuZHBEaXY7XG5cdH0sXG5cblx0LyogT3ZlcnJpZGUgdGhlIGRlZmF1bHQgc2V0dGluZ3MgZm9yIGFsbCBpbnN0YW5jZXMgb2YgdGhlIGRhdGUgcGlja2VyLlxuXHQgKiBAcGFyYW0gIHNldHRpbmdzICBvYmplY3QgLSB0aGUgbmV3IHNldHRpbmdzIHRvIHVzZSBhcyBkZWZhdWx0cyAoYW5vbnltb3VzIG9iamVjdClcblx0ICogQHJldHVybiB0aGUgbWFuYWdlciBvYmplY3Rcblx0ICovXG5cdHNldERlZmF1bHRzOiBmdW5jdGlvbiggc2V0dGluZ3MgKSB7XG5cdFx0ZGF0ZXBpY2tlcl9leHRlbmRSZW1vdmUoIHRoaXMuX2RlZmF1bHRzLCBzZXR0aW5ncyB8fCB7fSApO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdC8qIEF0dGFjaCB0aGUgZGF0ZSBwaWNrZXIgdG8gYSBqUXVlcnkgc2VsZWN0aW9uLlxuXHQgKiBAcGFyYW0gIHRhcmdldFx0ZWxlbWVudCAtIHRoZSB0YXJnZXQgaW5wdXQgZmllbGQgb3IgZGl2aXNpb24gb3Igc3BhblxuXHQgKiBAcGFyYW0gIHNldHRpbmdzICBvYmplY3QgLSB0aGUgbmV3IHNldHRpbmdzIHRvIHVzZSBmb3IgdGhpcyBkYXRlIHBpY2tlciBpbnN0YW5jZSAoYW5vbnltb3VzKVxuXHQgKi9cblx0X2F0dGFjaERhdGVwaWNrZXI6IGZ1bmN0aW9uKCB0YXJnZXQsIHNldHRpbmdzICkge1xuXHRcdHZhciBub2RlTmFtZSwgaW5saW5lLCBpbnN0O1xuXHRcdG5vZGVOYW1lID0gdGFyZ2V0Lm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7XG5cdFx0aW5saW5lID0gKCBub2RlTmFtZSA9PT0gXCJkaXZcIiB8fCBub2RlTmFtZSA9PT0gXCJzcGFuXCIgKTtcblx0XHRpZiAoICF0YXJnZXQuaWQgKSB7XG5cdFx0XHR0aGlzLnV1aWQgKz0gMTtcblx0XHRcdHRhcmdldC5pZCA9IFwiZHBcIiArIHRoaXMudXVpZDtcblx0XHR9XG5cdFx0aW5zdCA9IHRoaXMuX25ld0luc3QoICQoIHRhcmdldCApLCBpbmxpbmUgKTtcblx0XHRpbnN0LnNldHRpbmdzID0gJC5leHRlbmQoIHt9LCBzZXR0aW5ncyB8fCB7fSApO1xuXHRcdGlmICggbm9kZU5hbWUgPT09IFwiaW5wdXRcIiApIHtcblx0XHRcdHRoaXMuX2Nvbm5lY3REYXRlcGlja2VyKCB0YXJnZXQsIGluc3QgKTtcblx0XHR9IGVsc2UgaWYgKCBpbmxpbmUgKSB7XG5cdFx0XHR0aGlzLl9pbmxpbmVEYXRlcGlja2VyKCB0YXJnZXQsIGluc3QgKTtcblx0XHR9XG5cdH0sXG5cblx0LyogQ3JlYXRlIGEgbmV3IGluc3RhbmNlIG9iamVjdC4gKi9cblx0X25ld0luc3Q6IGZ1bmN0aW9uKCB0YXJnZXQsIGlubGluZSApIHtcblx0XHR2YXIgaWQgPSB0YXJnZXRbIDAgXS5pZC5yZXBsYWNlKCAvKFteQS1aYS16MC05X1xcLV0pL2csIFwiXFxcXFxcXFwkMVwiICk7IC8vIGVzY2FwZSBqUXVlcnkgbWV0YSBjaGFyc1xuXHRcdHJldHVybiB7IGlkOiBpZCwgaW5wdXQ6IHRhcmdldCwgLy8gYXNzb2NpYXRlZCB0YXJnZXRcblx0XHRcdHNlbGVjdGVkRGF5OiAwLCBzZWxlY3RlZE1vbnRoOiAwLCBzZWxlY3RlZFllYXI6IDAsIC8vIGN1cnJlbnQgc2VsZWN0aW9uXG5cdFx0XHRkcmF3TW9udGg6IDAsIGRyYXdZZWFyOiAwLCAvLyBtb250aCBiZWluZyBkcmF3blxuXHRcdFx0aW5saW5lOiBpbmxpbmUsIC8vIGlzIGRhdGVwaWNrZXIgaW5saW5lIG9yIG5vdFxuXHRcdFx0ZHBEaXY6ICggIWlubGluZSA/IHRoaXMuZHBEaXYgOiAvLyBwcmVzZW50YXRpb24gZGl2XG5cdFx0XHRkYXRlcGlja2VyX2JpbmRIb3ZlciggJCggXCI8ZGl2IGNsYXNzPSdcIiArIHRoaXMuX2lubGluZUNsYXNzICsgXCIgdWktZGF0ZXBpY2tlciB1aS13aWRnZXQgdWktd2lkZ2V0LWNvbnRlbnQgdWktaGVscGVyLWNsZWFyZml4IHVpLWNvcm5lci1hbGwnPjwvZGl2PlwiICkgKSApIH07XG5cdH0sXG5cblx0LyogQXR0YWNoIHRoZSBkYXRlIHBpY2tlciB0byBhbiBpbnB1dCBmaWVsZC4gKi9cblx0X2Nvbm5lY3REYXRlcGlja2VyOiBmdW5jdGlvbiggdGFyZ2V0LCBpbnN0ICkge1xuXHRcdHZhciBpbnB1dCA9ICQoIHRhcmdldCApO1xuXHRcdGluc3QuYXBwZW5kID0gJCggW10gKTtcblx0XHRpbnN0LnRyaWdnZXIgPSAkKCBbXSApO1xuXHRcdGlmICggaW5wdXQuaGFzQ2xhc3MoIHRoaXMubWFya2VyQ2xhc3NOYW1lICkgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdHRoaXMuX2F0dGFjaG1lbnRzKCBpbnB1dCwgaW5zdCApO1xuXHRcdGlucHV0LmFkZENsYXNzKCB0aGlzLm1hcmtlckNsYXNzTmFtZSApLm9uKCBcImtleWRvd25cIiwgdGhpcy5fZG9LZXlEb3duICkuXG5cdFx0XHRvbiggXCJrZXlwcmVzc1wiLCB0aGlzLl9kb0tleVByZXNzICkub24oIFwia2V5dXBcIiwgdGhpcy5fZG9LZXlVcCApO1xuXHRcdHRoaXMuX2F1dG9TaXplKCBpbnN0ICk7XG5cdFx0JC5kYXRhKCB0YXJnZXQsIFwiZGF0ZXBpY2tlclwiLCBpbnN0ICk7XG5cblx0XHQvL0lmIGRpc2FibGVkIG9wdGlvbiBpcyB0cnVlLCBkaXNhYmxlIHRoZSBkYXRlcGlja2VyIG9uY2UgaXQgaGFzIGJlZW4gYXR0YWNoZWQgdG8gdGhlIGlucHV0IChzZWUgdGlja2V0ICM1NjY1KVxuXHRcdGlmICggaW5zdC5zZXR0aW5ncy5kaXNhYmxlZCApIHtcblx0XHRcdHRoaXMuX2Rpc2FibGVEYXRlcGlja2VyKCB0YXJnZXQgKTtcblx0XHR9XG5cdH0sXG5cblx0LyogTWFrZSBhdHRhY2htZW50cyBiYXNlZCBvbiBzZXR0aW5ncy4gKi9cblx0X2F0dGFjaG1lbnRzOiBmdW5jdGlvbiggaW5wdXQsIGluc3QgKSB7XG5cdFx0dmFyIHNob3dPbiwgYnV0dG9uVGV4dCwgYnV0dG9uSW1hZ2UsXG5cdFx0XHRhcHBlbmRUZXh0ID0gdGhpcy5fZ2V0KCBpbnN0LCBcImFwcGVuZFRleHRcIiApLFxuXHRcdFx0aXNSVEwgPSB0aGlzLl9nZXQoIGluc3QsIFwiaXNSVExcIiApO1xuXG5cdFx0aWYgKCBpbnN0LmFwcGVuZCApIHtcblx0XHRcdGluc3QuYXBwZW5kLnJlbW92ZSgpO1xuXHRcdH1cblx0XHRpZiAoIGFwcGVuZFRleHQgKSB7XG5cdFx0XHRpbnN0LmFwcGVuZCA9ICQoIFwiPHNwYW4gY2xhc3M9J1wiICsgdGhpcy5fYXBwZW5kQ2xhc3MgKyBcIic+XCIgKyBhcHBlbmRUZXh0ICsgXCI8L3NwYW4+XCIgKTtcblx0XHRcdGlucHV0WyBpc1JUTCA/IFwiYmVmb3JlXCIgOiBcImFmdGVyXCIgXSggaW5zdC5hcHBlbmQgKTtcblx0XHR9XG5cblx0XHRpbnB1dC5vZmYoIFwiZm9jdXNcIiwgdGhpcy5fc2hvd0RhdGVwaWNrZXIgKTtcblxuXHRcdGlmICggaW5zdC50cmlnZ2VyICkge1xuXHRcdFx0aW5zdC50cmlnZ2VyLnJlbW92ZSgpO1xuXHRcdH1cblxuXHRcdHNob3dPbiA9IHRoaXMuX2dldCggaW5zdCwgXCJzaG93T25cIiApO1xuXHRcdGlmICggc2hvd09uID09PSBcImZvY3VzXCIgfHwgc2hvd09uID09PSBcImJvdGhcIiApIHsgLy8gcG9wLXVwIGRhdGUgcGlja2VyIHdoZW4gaW4gdGhlIG1hcmtlZCBmaWVsZFxuXHRcdFx0aW5wdXQub24oIFwiZm9jdXNcIiwgdGhpcy5fc2hvd0RhdGVwaWNrZXIgKTtcblx0XHR9XG5cdFx0aWYgKCBzaG93T24gPT09IFwiYnV0dG9uXCIgfHwgc2hvd09uID09PSBcImJvdGhcIiApIHsgLy8gcG9wLXVwIGRhdGUgcGlja2VyIHdoZW4gYnV0dG9uIGNsaWNrZWRcblx0XHRcdGJ1dHRvblRleHQgPSB0aGlzLl9nZXQoIGluc3QsIFwiYnV0dG9uVGV4dFwiICk7XG5cdFx0XHRidXR0b25JbWFnZSA9IHRoaXMuX2dldCggaW5zdCwgXCJidXR0b25JbWFnZVwiICk7XG5cdFx0XHRpbnN0LnRyaWdnZXIgPSAkKCB0aGlzLl9nZXQoIGluc3QsIFwiYnV0dG9uSW1hZ2VPbmx5XCIgKSA/XG5cdFx0XHRcdCQoIFwiPGltZy8+XCIgKS5hZGRDbGFzcyggdGhpcy5fdHJpZ2dlckNsYXNzICkuXG5cdFx0XHRcdFx0YXR0ciggeyBzcmM6IGJ1dHRvbkltYWdlLCBhbHQ6IGJ1dHRvblRleHQsIHRpdGxlOiBidXR0b25UZXh0IH0gKSA6XG5cdFx0XHRcdCQoIFwiPGJ1dHRvbiB0eXBlPSdidXR0b24nPjwvYnV0dG9uPlwiICkuYWRkQ2xhc3MoIHRoaXMuX3RyaWdnZXJDbGFzcyApLlxuXHRcdFx0XHRcdGh0bWwoICFidXR0b25JbWFnZSA/IGJ1dHRvblRleHQgOiAkKCBcIjxpbWcvPlwiICkuYXR0cihcblx0XHRcdFx0XHR7IHNyYzpidXR0b25JbWFnZSwgYWx0OmJ1dHRvblRleHQsIHRpdGxlOmJ1dHRvblRleHQgfSApICkgKTtcblx0XHRcdGlucHV0WyBpc1JUTCA/IFwiYmVmb3JlXCIgOiBcImFmdGVyXCIgXSggaW5zdC50cmlnZ2VyICk7XG5cdFx0XHRpbnN0LnRyaWdnZXIub24oIFwiY2xpY2tcIiwgZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGlmICggJC5kYXRlcGlja2VyLl9kYXRlcGlja2VyU2hvd2luZyAmJiAkLmRhdGVwaWNrZXIuX2xhc3RJbnB1dCA9PT0gaW5wdXRbIDAgXSApIHtcblx0XHRcdFx0XHQkLmRhdGVwaWNrZXIuX2hpZGVEYXRlcGlja2VyKCk7XG5cdFx0XHRcdH0gZWxzZSBpZiAoICQuZGF0ZXBpY2tlci5fZGF0ZXBpY2tlclNob3dpbmcgJiYgJC5kYXRlcGlja2VyLl9sYXN0SW5wdXQgIT09IGlucHV0WyAwIF0gKSB7XG5cdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9oaWRlRGF0ZXBpY2tlcigpO1xuXHRcdFx0XHRcdCQuZGF0ZXBpY2tlci5fc2hvd0RhdGVwaWNrZXIoIGlucHV0WyAwIF0gKTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHQkLmRhdGVwaWNrZXIuX3Nob3dEYXRlcGlja2VyKCBpbnB1dFsgMCBdICk7XG5cdFx0XHRcdH1cblx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0fSApO1xuXHRcdH1cblx0fSxcblxuXHQvKiBBcHBseSB0aGUgbWF4aW11bSBsZW5ndGggZm9yIHRoZSBkYXRlIGZvcm1hdC4gKi9cblx0X2F1dG9TaXplOiBmdW5jdGlvbiggaW5zdCApIHtcblx0XHRpZiAoIHRoaXMuX2dldCggaW5zdCwgXCJhdXRvU2l6ZVwiICkgJiYgIWluc3QuaW5saW5lICkge1xuXHRcdFx0dmFyIGZpbmRNYXgsIG1heCwgbWF4SSwgaSxcblx0XHRcdFx0ZGF0ZSA9IG5ldyBEYXRlKCAyMDA5LCAxMiAtIDEsIDIwICksIC8vIEVuc3VyZSBkb3VibGUgZGlnaXRzXG5cdFx0XHRcdGRhdGVGb3JtYXQgPSB0aGlzLl9nZXQoIGluc3QsIFwiZGF0ZUZvcm1hdFwiICk7XG5cblx0XHRcdGlmICggZGF0ZUZvcm1hdC5tYXRjaCggL1tETV0vICkgKSB7XG5cdFx0XHRcdGZpbmRNYXggPSBmdW5jdGlvbiggbmFtZXMgKSB7XG5cdFx0XHRcdFx0bWF4ID0gMDtcblx0XHRcdFx0XHRtYXhJID0gMDtcblx0XHRcdFx0XHRmb3IgKCBpID0gMDsgaSA8IG5hbWVzLmxlbmd0aDsgaSsrICkge1xuXHRcdFx0XHRcdFx0aWYgKCBuYW1lc1sgaSBdLmxlbmd0aCA+IG1heCApIHtcblx0XHRcdFx0XHRcdFx0bWF4ID0gbmFtZXNbIGkgXS5sZW5ndGg7XG5cdFx0XHRcdFx0XHRcdG1heEkgPSBpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRyZXR1cm4gbWF4STtcblx0XHRcdFx0fTtcblx0XHRcdFx0ZGF0ZS5zZXRNb250aCggZmluZE1heCggdGhpcy5fZ2V0KCBpbnN0LCAoIGRhdGVGb3JtYXQubWF0Y2goIC9NTS8gKSA/XG5cdFx0XHRcdFx0XCJtb250aE5hbWVzXCIgOiBcIm1vbnRoTmFtZXNTaG9ydFwiICkgKSApICk7XG5cdFx0XHRcdGRhdGUuc2V0RGF0ZSggZmluZE1heCggdGhpcy5fZ2V0KCBpbnN0LCAoIGRhdGVGb3JtYXQubWF0Y2goIC9ERC8gKSA/XG5cdFx0XHRcdFx0XCJkYXlOYW1lc1wiIDogXCJkYXlOYW1lc1Nob3J0XCIgKSApICkgKyAyMCAtIGRhdGUuZ2V0RGF5KCkgKTtcblx0XHRcdH1cblx0XHRcdGluc3QuaW5wdXQuYXR0ciggXCJzaXplXCIsIHRoaXMuX2Zvcm1hdERhdGUoIGluc3QsIGRhdGUgKS5sZW5ndGggKTtcblx0XHR9XG5cdH0sXG5cblx0LyogQXR0YWNoIGFuIGlubGluZSBkYXRlIHBpY2tlciB0byBhIGRpdi4gKi9cblx0X2lubGluZURhdGVwaWNrZXI6IGZ1bmN0aW9uKCB0YXJnZXQsIGluc3QgKSB7XG5cdFx0dmFyIGRpdlNwYW4gPSAkKCB0YXJnZXQgKTtcblx0XHRpZiAoIGRpdlNwYW4uaGFzQ2xhc3MoIHRoaXMubWFya2VyQ2xhc3NOYW1lICkgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGRpdlNwYW4uYWRkQ2xhc3MoIHRoaXMubWFya2VyQ2xhc3NOYW1lICkuYXBwZW5kKCBpbnN0LmRwRGl2ICk7XG5cdFx0JC5kYXRhKCB0YXJnZXQsIFwiZGF0ZXBpY2tlclwiLCBpbnN0ICk7XG5cdFx0dGhpcy5fc2V0RGF0ZSggaW5zdCwgdGhpcy5fZ2V0RGVmYXVsdERhdGUoIGluc3QgKSwgdHJ1ZSApO1xuXHRcdHRoaXMuX3VwZGF0ZURhdGVwaWNrZXIoIGluc3QgKTtcblx0XHR0aGlzLl91cGRhdGVBbHRlcm5hdGUoIGluc3QgKTtcblxuXHRcdC8vSWYgZGlzYWJsZWQgb3B0aW9uIGlzIHRydWUsIGRpc2FibGUgdGhlIGRhdGVwaWNrZXIgYmVmb3JlIHNob3dpbmcgaXQgKHNlZSB0aWNrZXQgIzU2NjUpXG5cdFx0aWYgKCBpbnN0LnNldHRpbmdzLmRpc2FibGVkICkge1xuXHRcdFx0dGhpcy5fZGlzYWJsZURhdGVwaWNrZXIoIHRhcmdldCApO1xuXHRcdH1cblxuXHRcdC8vIFNldCBkaXNwbGF5OmJsb2NrIGluIHBsYWNlIG9mIGluc3QuZHBEaXYuc2hvdygpIHdoaWNoIHdvbid0IHdvcmsgb24gZGlzY29ubmVjdGVkIGVsZW1lbnRzXG5cdFx0Ly8gaHR0cDovL2J1Z3MuanF1ZXJ5dWkuY29tL3RpY2tldC83NTUyIC0gQSBEYXRlcGlja2VyIGNyZWF0ZWQgb24gYSBkZXRhY2hlZCBkaXYgaGFzIHplcm8gaGVpZ2h0XG5cdFx0aW5zdC5kcERpdi5jc3MoIFwiZGlzcGxheVwiLCBcImJsb2NrXCIgKTtcblx0fSxcblxuXHQvKiBQb3AtdXAgdGhlIGRhdGUgcGlja2VyIGluIGEgXCJkaWFsb2dcIiBib3guXG5cdCAqIEBwYXJhbSAgaW5wdXQgZWxlbWVudCAtIGlnbm9yZWRcblx0ICogQHBhcmFtICBkYXRlXHRzdHJpbmcgb3IgRGF0ZSAtIHRoZSBpbml0aWFsIGRhdGUgdG8gZGlzcGxheVxuXHQgKiBAcGFyYW0gIG9uU2VsZWN0ICBmdW5jdGlvbiAtIHRoZSBmdW5jdGlvbiB0byBjYWxsIHdoZW4gYSBkYXRlIGlzIHNlbGVjdGVkXG5cdCAqIEBwYXJhbSAgc2V0dGluZ3MgIG9iamVjdCAtIHVwZGF0ZSB0aGUgZGlhbG9nIGRhdGUgcGlja2VyIGluc3RhbmNlJ3Mgc2V0dGluZ3MgKGFub255bW91cyBvYmplY3QpXG5cdCAqIEBwYXJhbSAgcG9zIGludFsyXSAtIGNvb3JkaW5hdGVzIGZvciB0aGUgZGlhbG9nJ3MgcG9zaXRpb24gd2l0aGluIHRoZSBzY3JlZW4gb3Jcblx0ICpcdFx0XHRcdFx0ZXZlbnQgLSB3aXRoIHgveSBjb29yZGluYXRlcyBvclxuXHQgKlx0XHRcdFx0XHRsZWF2ZSBlbXB0eSBmb3IgZGVmYXVsdCAoc2NyZWVuIGNlbnRyZSlcblx0ICogQHJldHVybiB0aGUgbWFuYWdlciBvYmplY3Rcblx0ICovXG5cdF9kaWFsb2dEYXRlcGlja2VyOiBmdW5jdGlvbiggaW5wdXQsIGRhdGUsIG9uU2VsZWN0LCBzZXR0aW5ncywgcG9zICkge1xuXHRcdHZhciBpZCwgYnJvd3NlcldpZHRoLCBicm93c2VySGVpZ2h0LCBzY3JvbGxYLCBzY3JvbGxZLFxuXHRcdFx0aW5zdCA9IHRoaXMuX2RpYWxvZ0luc3Q7IC8vIGludGVybmFsIGluc3RhbmNlXG5cblx0XHRpZiAoICFpbnN0ICkge1xuXHRcdFx0dGhpcy51dWlkICs9IDE7XG5cdFx0XHRpZCA9IFwiZHBcIiArIHRoaXMudXVpZDtcblx0XHRcdHRoaXMuX2RpYWxvZ0lucHV0ID0gJCggXCI8aW5wdXQgdHlwZT0ndGV4dCcgaWQ9J1wiICsgaWQgK1xuXHRcdFx0XHRcIicgc3R5bGU9J3Bvc2l0aW9uOiBhYnNvbHV0ZTsgdG9wOiAtMTAwcHg7IHdpZHRoOiAwcHg7Jy8+XCIgKTtcblx0XHRcdHRoaXMuX2RpYWxvZ0lucHV0Lm9uKCBcImtleWRvd25cIiwgdGhpcy5fZG9LZXlEb3duICk7XG5cdFx0XHQkKCBcImJvZHlcIiApLmFwcGVuZCggdGhpcy5fZGlhbG9nSW5wdXQgKTtcblx0XHRcdGluc3QgPSB0aGlzLl9kaWFsb2dJbnN0ID0gdGhpcy5fbmV3SW5zdCggdGhpcy5fZGlhbG9nSW5wdXQsIGZhbHNlICk7XG5cdFx0XHRpbnN0LnNldHRpbmdzID0ge307XG5cdFx0XHQkLmRhdGEoIHRoaXMuX2RpYWxvZ0lucHV0WyAwIF0sIFwiZGF0ZXBpY2tlclwiLCBpbnN0ICk7XG5cdFx0fVxuXHRcdGRhdGVwaWNrZXJfZXh0ZW5kUmVtb3ZlKCBpbnN0LnNldHRpbmdzLCBzZXR0aW5ncyB8fCB7fSApO1xuXHRcdGRhdGUgPSAoIGRhdGUgJiYgZGF0ZS5jb25zdHJ1Y3RvciA9PT0gRGF0ZSA/IHRoaXMuX2Zvcm1hdERhdGUoIGluc3QsIGRhdGUgKSA6IGRhdGUgKTtcblx0XHR0aGlzLl9kaWFsb2dJbnB1dC52YWwoIGRhdGUgKTtcblxuXHRcdHRoaXMuX3BvcyA9ICggcG9zID8gKCBwb3MubGVuZ3RoID8gcG9zIDogWyBwb3MucGFnZVgsIHBvcy5wYWdlWSBdICkgOiBudWxsICk7XG5cdFx0aWYgKCAhdGhpcy5fcG9zICkge1xuXHRcdFx0YnJvd3NlcldpZHRoID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsaWVudFdpZHRoO1xuXHRcdFx0YnJvd3NlckhlaWdodCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5jbGllbnRIZWlnaHQ7XG5cdFx0XHRzY3JvbGxYID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnNjcm9sbExlZnQgfHwgZG9jdW1lbnQuYm9keS5zY3JvbGxMZWZ0O1xuXHRcdFx0c2Nyb2xsWSA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zY3JvbGxUb3AgfHwgZG9jdW1lbnQuYm9keS5zY3JvbGxUb3A7XG5cdFx0XHR0aGlzLl9wb3MgPSAvLyBzaG91bGQgdXNlIGFjdHVhbCB3aWR0aC9oZWlnaHQgYmVsb3dcblx0XHRcdFx0WyAoIGJyb3dzZXJXaWR0aCAvIDIgKSAtIDEwMCArIHNjcm9sbFgsICggYnJvd3NlckhlaWdodCAvIDIgKSAtIDE1MCArIHNjcm9sbFkgXTtcblx0XHR9XG5cblx0XHQvLyBNb3ZlIGlucHV0IG9uIHNjcmVlbiBmb3IgZm9jdXMsIGJ1dCBoaWRkZW4gYmVoaW5kIGRpYWxvZ1xuXHRcdHRoaXMuX2RpYWxvZ0lucHV0LmNzcyggXCJsZWZ0XCIsICggdGhpcy5fcG9zWyAwIF0gKyAyMCApICsgXCJweFwiICkuY3NzKCBcInRvcFwiLCB0aGlzLl9wb3NbIDEgXSArIFwicHhcIiApO1xuXHRcdGluc3Quc2V0dGluZ3Mub25TZWxlY3QgPSBvblNlbGVjdDtcblx0XHR0aGlzLl9pbkRpYWxvZyA9IHRydWU7XG5cdFx0dGhpcy5kcERpdi5hZGRDbGFzcyggdGhpcy5fZGlhbG9nQ2xhc3MgKTtcblx0XHR0aGlzLl9zaG93RGF0ZXBpY2tlciggdGhpcy5fZGlhbG9nSW5wdXRbIDAgXSApO1xuXHRcdGlmICggJC5ibG9ja1VJICkge1xuXHRcdFx0JC5ibG9ja1VJKCB0aGlzLmRwRGl2ICk7XG5cdFx0fVxuXHRcdCQuZGF0YSggdGhpcy5fZGlhbG9nSW5wdXRbIDAgXSwgXCJkYXRlcGlja2VyXCIsIGluc3QgKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHQvKiBEZXRhY2ggYSBkYXRlcGlja2VyIGZyb20gaXRzIGNvbnRyb2wuXG5cdCAqIEBwYXJhbSAgdGFyZ2V0XHRlbGVtZW50IC0gdGhlIHRhcmdldCBpbnB1dCBmaWVsZCBvciBkaXZpc2lvbiBvciBzcGFuXG5cdCAqL1xuXHRfZGVzdHJveURhdGVwaWNrZXI6IGZ1bmN0aW9uKCB0YXJnZXQgKSB7XG5cdFx0dmFyIG5vZGVOYW1lLFxuXHRcdFx0JHRhcmdldCA9ICQoIHRhcmdldCApLFxuXHRcdFx0aW5zdCA9ICQuZGF0YSggdGFyZ2V0LCBcImRhdGVwaWNrZXJcIiApO1xuXG5cdFx0aWYgKCAhJHRhcmdldC5oYXNDbGFzcyggdGhpcy5tYXJrZXJDbGFzc05hbWUgKSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRub2RlTmFtZSA9IHRhcmdldC5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpO1xuXHRcdCQucmVtb3ZlRGF0YSggdGFyZ2V0LCBcImRhdGVwaWNrZXJcIiApO1xuXHRcdGlmICggbm9kZU5hbWUgPT09IFwiaW5wdXRcIiApIHtcblx0XHRcdGluc3QuYXBwZW5kLnJlbW92ZSgpO1xuXHRcdFx0aW5zdC50cmlnZ2VyLnJlbW92ZSgpO1xuXHRcdFx0JHRhcmdldC5yZW1vdmVDbGFzcyggdGhpcy5tYXJrZXJDbGFzc05hbWUgKS5cblx0XHRcdFx0b2ZmKCBcImZvY3VzXCIsIHRoaXMuX3Nob3dEYXRlcGlja2VyICkuXG5cdFx0XHRcdG9mZiggXCJrZXlkb3duXCIsIHRoaXMuX2RvS2V5RG93biApLlxuXHRcdFx0XHRvZmYoIFwia2V5cHJlc3NcIiwgdGhpcy5fZG9LZXlQcmVzcyApLlxuXHRcdFx0XHRvZmYoIFwia2V5dXBcIiwgdGhpcy5fZG9LZXlVcCApO1xuXHRcdH0gZWxzZSBpZiAoIG5vZGVOYW1lID09PSBcImRpdlwiIHx8IG5vZGVOYW1lID09PSBcInNwYW5cIiApIHtcblx0XHRcdCR0YXJnZXQucmVtb3ZlQ2xhc3MoIHRoaXMubWFya2VyQ2xhc3NOYW1lICkuZW1wdHkoKTtcblx0XHR9XG5cblx0XHRpZiAoIGRhdGVwaWNrZXJfaW5zdEFjdGl2ZSA9PT0gaW5zdCApIHtcblx0XHRcdGRhdGVwaWNrZXJfaW5zdEFjdGl2ZSA9IG51bGw7XG5cdFx0fVxuXHR9LFxuXG5cdC8qIEVuYWJsZSB0aGUgZGF0ZSBwaWNrZXIgdG8gYSBqUXVlcnkgc2VsZWN0aW9uLlxuXHQgKiBAcGFyYW0gIHRhcmdldFx0ZWxlbWVudCAtIHRoZSB0YXJnZXQgaW5wdXQgZmllbGQgb3IgZGl2aXNpb24gb3Igc3BhblxuXHQgKi9cblx0X2VuYWJsZURhdGVwaWNrZXI6IGZ1bmN0aW9uKCB0YXJnZXQgKSB7XG5cdFx0dmFyIG5vZGVOYW1lLCBpbmxpbmUsXG5cdFx0XHQkdGFyZ2V0ID0gJCggdGFyZ2V0ICksXG5cdFx0XHRpbnN0ID0gJC5kYXRhKCB0YXJnZXQsIFwiZGF0ZXBpY2tlclwiICk7XG5cblx0XHRpZiAoICEkdGFyZ2V0Lmhhc0NsYXNzKCB0aGlzLm1hcmtlckNsYXNzTmFtZSApICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdG5vZGVOYW1lID0gdGFyZ2V0Lm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7XG5cdFx0aWYgKCBub2RlTmFtZSA9PT0gXCJpbnB1dFwiICkge1xuXHRcdFx0dGFyZ2V0LmRpc2FibGVkID0gZmFsc2U7XG5cdFx0XHRpbnN0LnRyaWdnZXIuZmlsdGVyKCBcImJ1dHRvblwiICkuXG5cdFx0XHRcdGVhY2goIGZ1bmN0aW9uKCkgeyB0aGlzLmRpc2FibGVkID0gZmFsc2U7IH0gKS5lbmQoKS5cblx0XHRcdFx0ZmlsdGVyKCBcImltZ1wiICkuY3NzKCB7IG9wYWNpdHk6IFwiMS4wXCIsIGN1cnNvcjogXCJcIiB9ICk7XG5cdFx0fSBlbHNlIGlmICggbm9kZU5hbWUgPT09IFwiZGl2XCIgfHwgbm9kZU5hbWUgPT09IFwic3BhblwiICkge1xuXHRcdFx0aW5saW5lID0gJHRhcmdldC5jaGlsZHJlbiggXCIuXCIgKyB0aGlzLl9pbmxpbmVDbGFzcyApO1xuXHRcdFx0aW5saW5lLmNoaWxkcmVuKCkucmVtb3ZlQ2xhc3MoIFwidWktc3RhdGUtZGlzYWJsZWRcIiApO1xuXHRcdFx0aW5saW5lLmZpbmQoIFwic2VsZWN0LnVpLWRhdGVwaWNrZXItbW9udGgsIHNlbGVjdC51aS1kYXRlcGlja2VyLXllYXJcIiApLlxuXHRcdFx0XHRwcm9wKCBcImRpc2FibGVkXCIsIGZhbHNlICk7XG5cdFx0fVxuXHRcdHRoaXMuX2Rpc2FibGVkSW5wdXRzID0gJC5tYXAoIHRoaXMuX2Rpc2FibGVkSW5wdXRzLFxuXHRcdFx0ZnVuY3Rpb24oIHZhbHVlICkgeyByZXR1cm4gKCB2YWx1ZSA9PT0gdGFyZ2V0ID8gbnVsbCA6IHZhbHVlICk7IH0gKTsgLy8gZGVsZXRlIGVudHJ5XG5cdH0sXG5cblx0LyogRGlzYWJsZSB0aGUgZGF0ZSBwaWNrZXIgdG8gYSBqUXVlcnkgc2VsZWN0aW9uLlxuXHQgKiBAcGFyYW0gIHRhcmdldFx0ZWxlbWVudCAtIHRoZSB0YXJnZXQgaW5wdXQgZmllbGQgb3IgZGl2aXNpb24gb3Igc3BhblxuXHQgKi9cblx0X2Rpc2FibGVEYXRlcGlja2VyOiBmdW5jdGlvbiggdGFyZ2V0ICkge1xuXHRcdHZhciBub2RlTmFtZSwgaW5saW5lLFxuXHRcdFx0JHRhcmdldCA9ICQoIHRhcmdldCApLFxuXHRcdFx0aW5zdCA9ICQuZGF0YSggdGFyZ2V0LCBcImRhdGVwaWNrZXJcIiApO1xuXG5cdFx0aWYgKCAhJHRhcmdldC5oYXNDbGFzcyggdGhpcy5tYXJrZXJDbGFzc05hbWUgKSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRub2RlTmFtZSA9IHRhcmdldC5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpO1xuXHRcdGlmICggbm9kZU5hbWUgPT09IFwiaW5wdXRcIiApIHtcblx0XHRcdHRhcmdldC5kaXNhYmxlZCA9IHRydWU7XG5cdFx0XHRpbnN0LnRyaWdnZXIuZmlsdGVyKCBcImJ1dHRvblwiICkuXG5cdFx0XHRcdGVhY2goIGZ1bmN0aW9uKCkgeyB0aGlzLmRpc2FibGVkID0gdHJ1ZTsgfSApLmVuZCgpLlxuXHRcdFx0XHRmaWx0ZXIoIFwiaW1nXCIgKS5jc3MoIHsgb3BhY2l0eTogXCIwLjVcIiwgY3Vyc29yOiBcImRlZmF1bHRcIiB9ICk7XG5cdFx0fSBlbHNlIGlmICggbm9kZU5hbWUgPT09IFwiZGl2XCIgfHwgbm9kZU5hbWUgPT09IFwic3BhblwiICkge1xuXHRcdFx0aW5saW5lID0gJHRhcmdldC5jaGlsZHJlbiggXCIuXCIgKyB0aGlzLl9pbmxpbmVDbGFzcyApO1xuXHRcdFx0aW5saW5lLmNoaWxkcmVuKCkuYWRkQ2xhc3MoIFwidWktc3RhdGUtZGlzYWJsZWRcIiApO1xuXHRcdFx0aW5saW5lLmZpbmQoIFwic2VsZWN0LnVpLWRhdGVwaWNrZXItbW9udGgsIHNlbGVjdC51aS1kYXRlcGlja2VyLXllYXJcIiApLlxuXHRcdFx0XHRwcm9wKCBcImRpc2FibGVkXCIsIHRydWUgKTtcblx0XHR9XG5cdFx0dGhpcy5fZGlzYWJsZWRJbnB1dHMgPSAkLm1hcCggdGhpcy5fZGlzYWJsZWRJbnB1dHMsXG5cdFx0XHRmdW5jdGlvbiggdmFsdWUgKSB7IHJldHVybiAoIHZhbHVlID09PSB0YXJnZXQgPyBudWxsIDogdmFsdWUgKTsgfSApOyAvLyBkZWxldGUgZW50cnlcblx0XHR0aGlzLl9kaXNhYmxlZElucHV0c1sgdGhpcy5fZGlzYWJsZWRJbnB1dHMubGVuZ3RoIF0gPSB0YXJnZXQ7XG5cdH0sXG5cblx0LyogSXMgdGhlIGZpcnN0IGZpZWxkIGluIGEgalF1ZXJ5IGNvbGxlY3Rpb24gZGlzYWJsZWQgYXMgYSBkYXRlcGlja2VyP1xuXHQgKiBAcGFyYW0gIHRhcmdldFx0ZWxlbWVudCAtIHRoZSB0YXJnZXQgaW5wdXQgZmllbGQgb3IgZGl2aXNpb24gb3Igc3BhblxuXHQgKiBAcmV0dXJuIGJvb2xlYW4gLSB0cnVlIGlmIGRpc2FibGVkLCBmYWxzZSBpZiBlbmFibGVkXG5cdCAqL1xuXHRfaXNEaXNhYmxlZERhdGVwaWNrZXI6IGZ1bmN0aW9uKCB0YXJnZXQgKSB7XG5cdFx0aWYgKCAhdGFyZ2V0ICkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRmb3IgKCB2YXIgaSA9IDA7IGkgPCB0aGlzLl9kaXNhYmxlZElucHV0cy5sZW5ndGg7IGkrKyApIHtcblx0XHRcdGlmICggdGhpcy5fZGlzYWJsZWRJbnB1dHNbIGkgXSA9PT0gdGFyZ2V0ICkge1xuXHRcdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9LFxuXG5cdC8qIFJldHJpZXZlIHRoZSBpbnN0YW5jZSBkYXRhIGZvciB0aGUgdGFyZ2V0IGNvbnRyb2wuXG5cdCAqIEBwYXJhbSAgdGFyZ2V0ICBlbGVtZW50IC0gdGhlIHRhcmdldCBpbnB1dCBmaWVsZCBvciBkaXZpc2lvbiBvciBzcGFuXG5cdCAqIEByZXR1cm4gIG9iamVjdCAtIHRoZSBhc3NvY2lhdGVkIGluc3RhbmNlIGRhdGFcblx0ICogQHRocm93cyAgZXJyb3IgaWYgYSBqUXVlcnkgcHJvYmxlbSBnZXR0aW5nIGRhdGFcblx0ICovXG5cdF9nZXRJbnN0OiBmdW5jdGlvbiggdGFyZ2V0ICkge1xuXHRcdHRyeSB7XG5cdFx0XHRyZXR1cm4gJC5kYXRhKCB0YXJnZXQsIFwiZGF0ZXBpY2tlclwiICk7XG5cdFx0fVxuXHRcdGNhdGNoICggZXJyICkge1xuXHRcdFx0dGhyb3cgXCJNaXNzaW5nIGluc3RhbmNlIGRhdGEgZm9yIHRoaXMgZGF0ZXBpY2tlclwiO1xuXHRcdH1cblx0fSxcblxuXHQvKiBVcGRhdGUgb3IgcmV0cmlldmUgdGhlIHNldHRpbmdzIGZvciBhIGRhdGUgcGlja2VyIGF0dGFjaGVkIHRvIGFuIGlucHV0IGZpZWxkIG9yIGRpdmlzaW9uLlxuXHQgKiBAcGFyYW0gIHRhcmdldCAgZWxlbWVudCAtIHRoZSB0YXJnZXQgaW5wdXQgZmllbGQgb3IgZGl2aXNpb24gb3Igc3BhblxuXHQgKiBAcGFyYW0gIG5hbWVcdG9iamVjdCAtIHRoZSBuZXcgc2V0dGluZ3MgdG8gdXBkYXRlIG9yXG5cdCAqXHRcdFx0XHRzdHJpbmcgLSB0aGUgbmFtZSBvZiB0aGUgc2V0dGluZyB0byBjaGFuZ2Ugb3IgcmV0cmlldmUsXG5cdCAqXHRcdFx0XHR3aGVuIHJldHJpZXZpbmcgYWxzbyBcImFsbFwiIGZvciBhbGwgaW5zdGFuY2Ugc2V0dGluZ3Mgb3Jcblx0ICpcdFx0XHRcdFwiZGVmYXVsdHNcIiBmb3IgYWxsIGdsb2JhbCBkZWZhdWx0c1xuXHQgKiBAcGFyYW0gIHZhbHVlICAgYW55IC0gdGhlIG5ldyB2YWx1ZSBmb3IgdGhlIHNldHRpbmdcblx0ICpcdFx0XHRcdChvbWl0IGlmIGFib3ZlIGlzIGFuIG9iamVjdCBvciB0byByZXRyaWV2ZSBhIHZhbHVlKVxuXHQgKi9cblx0X29wdGlvbkRhdGVwaWNrZXI6IGZ1bmN0aW9uKCB0YXJnZXQsIG5hbWUsIHZhbHVlICkge1xuXHRcdHZhciBzZXR0aW5ncywgZGF0ZSwgbWluRGF0ZSwgbWF4RGF0ZSxcblx0XHRcdGluc3QgPSB0aGlzLl9nZXRJbnN0KCB0YXJnZXQgKTtcblxuXHRcdGlmICggYXJndW1lbnRzLmxlbmd0aCA9PT0gMiAmJiB0eXBlb2YgbmFtZSA9PT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdHJldHVybiAoIG5hbWUgPT09IFwiZGVmYXVsdHNcIiA/ICQuZXh0ZW5kKCB7fSwgJC5kYXRlcGlja2VyLl9kZWZhdWx0cyApIDpcblx0XHRcdFx0KCBpbnN0ID8gKCBuYW1lID09PSBcImFsbFwiID8gJC5leHRlbmQoIHt9LCBpbnN0LnNldHRpbmdzICkgOlxuXHRcdFx0XHR0aGlzLl9nZXQoIGluc3QsIG5hbWUgKSApIDogbnVsbCApICk7XG5cdFx0fVxuXG5cdFx0c2V0dGluZ3MgPSBuYW1lIHx8IHt9O1xuXHRcdGlmICggdHlwZW9mIG5hbWUgPT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRzZXR0aW5ncyA9IHt9O1xuXHRcdFx0c2V0dGluZ3NbIG5hbWUgXSA9IHZhbHVlO1xuXHRcdH1cblxuXHRcdGlmICggaW5zdCApIHtcblx0XHRcdGlmICggdGhpcy5fY3VySW5zdCA9PT0gaW5zdCApIHtcblx0XHRcdFx0dGhpcy5faGlkZURhdGVwaWNrZXIoKTtcblx0XHRcdH1cblxuXHRcdFx0ZGF0ZSA9IHRoaXMuX2dldERhdGVEYXRlcGlja2VyKCB0YXJnZXQsIHRydWUgKTtcblx0XHRcdG1pbkRhdGUgPSB0aGlzLl9nZXRNaW5NYXhEYXRlKCBpbnN0LCBcIm1pblwiICk7XG5cdFx0XHRtYXhEYXRlID0gdGhpcy5fZ2V0TWluTWF4RGF0ZSggaW5zdCwgXCJtYXhcIiApO1xuXHRcdFx0ZGF0ZXBpY2tlcl9leHRlbmRSZW1vdmUoIGluc3Quc2V0dGluZ3MsIHNldHRpbmdzICk7XG5cblx0XHRcdC8vIHJlZm9ybWF0IHRoZSBvbGQgbWluRGF0ZS9tYXhEYXRlIHZhbHVlcyBpZiBkYXRlRm9ybWF0IGNoYW5nZXMgYW5kIGEgbmV3IG1pbkRhdGUvbWF4RGF0ZSBpc24ndCBwcm92aWRlZFxuXHRcdFx0aWYgKCBtaW5EYXRlICE9PSBudWxsICYmIHNldHRpbmdzLmRhdGVGb3JtYXQgIT09IHVuZGVmaW5lZCAmJiBzZXR0aW5ncy5taW5EYXRlID09PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdGluc3Quc2V0dGluZ3MubWluRGF0ZSA9IHRoaXMuX2Zvcm1hdERhdGUoIGluc3QsIG1pbkRhdGUgKTtcblx0XHRcdH1cblx0XHRcdGlmICggbWF4RGF0ZSAhPT0gbnVsbCAmJiBzZXR0aW5ncy5kYXRlRm9ybWF0ICE9PSB1bmRlZmluZWQgJiYgc2V0dGluZ3MubWF4RGF0ZSA9PT0gdW5kZWZpbmVkICkge1xuXHRcdFx0XHRpbnN0LnNldHRpbmdzLm1heERhdGUgPSB0aGlzLl9mb3JtYXREYXRlKCBpbnN0LCBtYXhEYXRlICk7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIFwiZGlzYWJsZWRcIiBpbiBzZXR0aW5ncyApIHtcblx0XHRcdFx0aWYgKCBzZXR0aW5ncy5kaXNhYmxlZCApIHtcblx0XHRcdFx0XHR0aGlzLl9kaXNhYmxlRGF0ZXBpY2tlciggdGFyZ2V0ICk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0dGhpcy5fZW5hYmxlRGF0ZXBpY2tlciggdGFyZ2V0ICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdHRoaXMuX2F0dGFjaG1lbnRzKCAkKCB0YXJnZXQgKSwgaW5zdCApO1xuXHRcdFx0dGhpcy5fYXV0b1NpemUoIGluc3QgKTtcblx0XHRcdHRoaXMuX3NldERhdGUoIGluc3QsIGRhdGUgKTtcblx0XHRcdHRoaXMuX3VwZGF0ZUFsdGVybmF0ZSggaW5zdCApO1xuXHRcdFx0dGhpcy5fdXBkYXRlRGF0ZXBpY2tlciggaW5zdCApO1xuXHRcdH1cblx0fSxcblxuXHQvLyBDaGFuZ2UgbWV0aG9kIGRlcHJlY2F0ZWRcblx0X2NoYW5nZURhdGVwaWNrZXI6IGZ1bmN0aW9uKCB0YXJnZXQsIG5hbWUsIHZhbHVlICkge1xuXHRcdHRoaXMuX29wdGlvbkRhdGVwaWNrZXIoIHRhcmdldCwgbmFtZSwgdmFsdWUgKTtcblx0fSxcblxuXHQvKiBSZWRyYXcgdGhlIGRhdGUgcGlja2VyIGF0dGFjaGVkIHRvIGFuIGlucHV0IGZpZWxkIG9yIGRpdmlzaW9uLlxuXHQgKiBAcGFyYW0gIHRhcmdldCAgZWxlbWVudCAtIHRoZSB0YXJnZXQgaW5wdXQgZmllbGQgb3IgZGl2aXNpb24gb3Igc3BhblxuXHQgKi9cblx0X3JlZnJlc2hEYXRlcGlja2VyOiBmdW5jdGlvbiggdGFyZ2V0ICkge1xuXHRcdHZhciBpbnN0ID0gdGhpcy5fZ2V0SW5zdCggdGFyZ2V0ICk7XG5cdFx0aWYgKCBpbnN0ICkge1xuXHRcdFx0dGhpcy5fdXBkYXRlRGF0ZXBpY2tlciggaW5zdCApO1xuXHRcdH1cblx0fSxcblxuXHQvKiBTZXQgdGhlIGRhdGVzIGZvciBhIGpRdWVyeSBzZWxlY3Rpb24uXG5cdCAqIEBwYXJhbSAgdGFyZ2V0IGVsZW1lbnQgLSB0aGUgdGFyZ2V0IGlucHV0IGZpZWxkIG9yIGRpdmlzaW9uIG9yIHNwYW5cblx0ICogQHBhcmFtICBkYXRlXHREYXRlIC0gdGhlIG5ldyBkYXRlXG5cdCAqL1xuXHRfc2V0RGF0ZURhdGVwaWNrZXI6IGZ1bmN0aW9uKCB0YXJnZXQsIGRhdGUgKSB7XG5cdFx0dmFyIGluc3QgPSB0aGlzLl9nZXRJbnN0KCB0YXJnZXQgKTtcblx0XHRpZiAoIGluc3QgKSB7XG5cdFx0XHR0aGlzLl9zZXREYXRlKCBpbnN0LCBkYXRlICk7XG5cdFx0XHR0aGlzLl91cGRhdGVEYXRlcGlja2VyKCBpbnN0ICk7XG5cdFx0XHR0aGlzLl91cGRhdGVBbHRlcm5hdGUoIGluc3QgKTtcblx0XHR9XG5cdH0sXG5cblx0LyogR2V0IHRoZSBkYXRlKHMpIGZvciB0aGUgZmlyc3QgZW50cnkgaW4gYSBqUXVlcnkgc2VsZWN0aW9uLlxuXHQgKiBAcGFyYW0gIHRhcmdldCBlbGVtZW50IC0gdGhlIHRhcmdldCBpbnB1dCBmaWVsZCBvciBkaXZpc2lvbiBvciBzcGFuXG5cdCAqIEBwYXJhbSAgbm9EZWZhdWx0IGJvb2xlYW4gLSB0cnVlIGlmIG5vIGRlZmF1bHQgZGF0ZSBpcyB0byBiZSB1c2VkXG5cdCAqIEByZXR1cm4gRGF0ZSAtIHRoZSBjdXJyZW50IGRhdGVcblx0ICovXG5cdF9nZXREYXRlRGF0ZXBpY2tlcjogZnVuY3Rpb24oIHRhcmdldCwgbm9EZWZhdWx0ICkge1xuXHRcdHZhciBpbnN0ID0gdGhpcy5fZ2V0SW5zdCggdGFyZ2V0ICk7XG5cdFx0aWYgKCBpbnN0ICYmICFpbnN0LmlubGluZSApIHtcblx0XHRcdHRoaXMuX3NldERhdGVGcm9tRmllbGQoIGluc3QsIG5vRGVmYXVsdCApO1xuXHRcdH1cblx0XHRyZXR1cm4gKCBpbnN0ID8gdGhpcy5fZ2V0RGF0ZSggaW5zdCApIDogbnVsbCApO1xuXHR9LFxuXG5cdC8qIEhhbmRsZSBrZXlzdHJva2VzLiAqL1xuXHRfZG9LZXlEb3duOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dmFyIG9uU2VsZWN0LCBkYXRlU3RyLCBzZWwsXG5cdFx0XHRpbnN0ID0gJC5kYXRlcGlja2VyLl9nZXRJbnN0KCBldmVudC50YXJnZXQgKSxcblx0XHRcdGhhbmRsZWQgPSB0cnVlLFxuXHRcdFx0aXNSVEwgPSBpbnN0LmRwRGl2LmlzKCBcIi51aS1kYXRlcGlja2VyLXJ0bFwiICk7XG5cblx0XHRpbnN0Ll9rZXlFdmVudCA9IHRydWU7XG5cdFx0aWYgKCAkLmRhdGVwaWNrZXIuX2RhdGVwaWNrZXJTaG93aW5nICkge1xuXHRcdFx0c3dpdGNoICggZXZlbnQua2V5Q29kZSApIHtcblx0XHRcdFx0Y2FzZSA5OiAkLmRhdGVwaWNrZXIuX2hpZGVEYXRlcGlja2VyKCk7XG5cdFx0XHRcdFx0XHRoYW5kbGVkID0gZmFsc2U7XG5cdFx0XHRcdFx0XHRicmVhazsgLy8gaGlkZSBvbiB0YWIgb3V0XG5cdFx0XHRcdGNhc2UgMTM6IHNlbCA9ICQoIFwidGQuXCIgKyAkLmRhdGVwaWNrZXIuX2RheU92ZXJDbGFzcyArIFwiOm5vdCguXCIgK1xuXHRcdFx0XHRcdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9jdXJyZW50Q2xhc3MgKyBcIilcIiwgaW5zdC5kcERpdiApO1xuXHRcdFx0XHRcdFx0aWYgKCBzZWxbIDAgXSApIHtcblx0XHRcdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9zZWxlY3REYXkoIGV2ZW50LnRhcmdldCwgaW5zdC5zZWxlY3RlZE1vbnRoLCBpbnN0LnNlbGVjdGVkWWVhciwgc2VsWyAwIF0gKTtcblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0b25TZWxlY3QgPSAkLmRhdGVwaWNrZXIuX2dldCggaW5zdCwgXCJvblNlbGVjdFwiICk7XG5cdFx0XHRcdFx0XHRpZiAoIG9uU2VsZWN0ICkge1xuXHRcdFx0XHRcdFx0XHRkYXRlU3RyID0gJC5kYXRlcGlja2VyLl9mb3JtYXREYXRlKCBpbnN0ICk7XG5cblx0XHRcdFx0XHRcdFx0Ly8gVHJpZ2dlciBjdXN0b20gY2FsbGJhY2tcblx0XHRcdFx0XHRcdFx0b25TZWxlY3QuYXBwbHkoICggaW5zdC5pbnB1dCA/IGluc3QuaW5wdXRbIDAgXSA6IG51bGwgKSwgWyBkYXRlU3RyLCBpbnN0IF0gKTtcblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdCQuZGF0ZXBpY2tlci5faGlkZURhdGVwaWNrZXIoKTtcblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0cmV0dXJuIGZhbHNlOyAvLyBkb24ndCBzdWJtaXQgdGhlIGZvcm1cblx0XHRcdFx0Y2FzZSAyNzogJC5kYXRlcGlja2VyLl9oaWRlRGF0ZXBpY2tlcigpO1xuXHRcdFx0XHRcdFx0YnJlYWs7IC8vIGhpZGUgb24gZXNjYXBlXG5cdFx0XHRcdGNhc2UgMzM6ICQuZGF0ZXBpY2tlci5fYWRqdXN0RGF0ZSggZXZlbnQudGFyZ2V0LCAoIGV2ZW50LmN0cmxLZXkgP1xuXHRcdFx0XHRcdFx0XHQtJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwic3RlcEJpZ01vbnRoc1wiICkgOlxuXHRcdFx0XHRcdFx0XHQtJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwic3RlcE1vbnRoc1wiICkgKSwgXCJNXCIgKTtcblx0XHRcdFx0XHRcdGJyZWFrOyAvLyBwcmV2aW91cyBtb250aC95ZWFyIG9uIHBhZ2UgdXAvKyBjdHJsXG5cdFx0XHRcdGNhc2UgMzQ6ICQuZGF0ZXBpY2tlci5fYWRqdXN0RGF0ZSggZXZlbnQudGFyZ2V0LCAoIGV2ZW50LmN0cmxLZXkgP1xuXHRcdFx0XHRcdFx0XHQrJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwic3RlcEJpZ01vbnRoc1wiICkgOlxuXHRcdFx0XHRcdFx0XHQrJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwic3RlcE1vbnRoc1wiICkgKSwgXCJNXCIgKTtcblx0XHRcdFx0XHRcdGJyZWFrOyAvLyBuZXh0IG1vbnRoL3llYXIgb24gcGFnZSBkb3duLysgY3RybFxuXHRcdFx0XHRjYXNlIDM1OiBpZiAoIGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSApIHtcblx0XHRcdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9jbGVhckRhdGUoIGV2ZW50LnRhcmdldCApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0aGFuZGxlZCA9IGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleTtcblx0XHRcdFx0XHRcdGJyZWFrOyAvLyBjbGVhciBvbiBjdHJsIG9yIGNvbW1hbmQgK2VuZFxuXHRcdFx0XHRjYXNlIDM2OiBpZiAoIGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSApIHtcblx0XHRcdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9nb3RvVG9kYXkoIGV2ZW50LnRhcmdldCApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0aGFuZGxlZCA9IGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleTtcblx0XHRcdFx0XHRcdGJyZWFrOyAvLyBjdXJyZW50IG9uIGN0cmwgb3IgY29tbWFuZCAraG9tZVxuXHRcdFx0XHRjYXNlIDM3OiBpZiAoIGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSApIHtcblx0XHRcdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9hZGp1c3REYXRlKCBldmVudC50YXJnZXQsICggaXNSVEwgPyArMSA6IC0xICksIFwiRFwiICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRoYW5kbGVkID0gZXZlbnQuY3RybEtleSB8fCBldmVudC5tZXRhS2V5O1xuXG5cdFx0XHRcdFx0XHQvLyAtMSBkYXkgb24gY3RybCBvciBjb21tYW5kICtsZWZ0XG5cdFx0XHRcdFx0XHRpZiAoIGV2ZW50Lm9yaWdpbmFsRXZlbnQuYWx0S2V5ICkge1xuXHRcdFx0XHRcdFx0XHQkLmRhdGVwaWNrZXIuX2FkanVzdERhdGUoIGV2ZW50LnRhcmdldCwgKCBldmVudC5jdHJsS2V5ID9cblx0XHRcdFx0XHRcdFx0XHQtJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwic3RlcEJpZ01vbnRoc1wiICkgOlxuXHRcdFx0XHRcdFx0XHRcdC0kLmRhdGVwaWNrZXIuX2dldCggaW5zdCwgXCJzdGVwTW9udGhzXCIgKSApLCBcIk1cIiApO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHQvLyBuZXh0IG1vbnRoL3llYXIgb24gYWx0ICtsZWZ0IG9uIE1hY1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgMzg6IGlmICggZXZlbnQuY3RybEtleSB8fCBldmVudC5tZXRhS2V5ICkge1xuXHRcdFx0XHRcdFx0XHQkLmRhdGVwaWNrZXIuX2FkanVzdERhdGUoIGV2ZW50LnRhcmdldCwgLTcsIFwiRFwiICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRoYW5kbGVkID0gZXZlbnQuY3RybEtleSB8fCBldmVudC5tZXRhS2V5O1xuXHRcdFx0XHRcdFx0YnJlYWs7IC8vIC0xIHdlZWsgb24gY3RybCBvciBjb21tYW5kICt1cFxuXHRcdFx0XHRjYXNlIDM5OiBpZiAoIGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSApIHtcblx0XHRcdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9hZGp1c3REYXRlKCBldmVudC50YXJnZXQsICggaXNSVEwgPyAtMSA6ICsxICksIFwiRFwiICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRoYW5kbGVkID0gZXZlbnQuY3RybEtleSB8fCBldmVudC5tZXRhS2V5O1xuXG5cdFx0XHRcdFx0XHQvLyArMSBkYXkgb24gY3RybCBvciBjb21tYW5kICtyaWdodFxuXHRcdFx0XHRcdFx0aWYgKCBldmVudC5vcmlnaW5hbEV2ZW50LmFsdEtleSApIHtcblx0XHRcdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9hZGp1c3REYXRlKCBldmVudC50YXJnZXQsICggZXZlbnQuY3RybEtleSA/XG5cdFx0XHRcdFx0XHRcdFx0KyQuZGF0ZXBpY2tlci5fZ2V0KCBpbnN0LCBcInN0ZXBCaWdNb250aHNcIiApIDpcblx0XHRcdFx0XHRcdFx0XHQrJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwic3RlcE1vbnRoc1wiICkgKSwgXCJNXCIgKTtcblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0Ly8gbmV4dCBtb250aC95ZWFyIG9uIGFsdCArcmlnaHRcblx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDQwOiBpZiAoIGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleSApIHtcblx0XHRcdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9hZGp1c3REYXRlKCBldmVudC50YXJnZXQsICs3LCBcIkRcIiApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0aGFuZGxlZCA9IGV2ZW50LmN0cmxLZXkgfHwgZXZlbnQubWV0YUtleTtcblx0XHRcdFx0XHRcdGJyZWFrOyAvLyArMSB3ZWVrIG9uIGN0cmwgb3IgY29tbWFuZCArZG93blxuXHRcdFx0XHRkZWZhdWx0OiBoYW5kbGVkID0gZmFsc2U7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIGlmICggZXZlbnQua2V5Q29kZSA9PT0gMzYgJiYgZXZlbnQuY3RybEtleSApIHsgLy8gZGlzcGxheSB0aGUgZGF0ZSBwaWNrZXIgb24gY3RybCtob21lXG5cdFx0XHQkLmRhdGVwaWNrZXIuX3Nob3dEYXRlcGlja2VyKCB0aGlzICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGhhbmRsZWQgPSBmYWxzZTtcblx0XHR9XG5cblx0XHRpZiAoIGhhbmRsZWQgKSB7XG5cdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0ZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qIEZpbHRlciBlbnRlcmVkIGNoYXJhY3RlcnMgLSBiYXNlZCBvbiBkYXRlIGZvcm1hdC4gKi9cblx0X2RvS2V5UHJlc3M6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgY2hhcnMsIGNocixcblx0XHRcdGluc3QgPSAkLmRhdGVwaWNrZXIuX2dldEluc3QoIGV2ZW50LnRhcmdldCApO1xuXG5cdFx0aWYgKCAkLmRhdGVwaWNrZXIuX2dldCggaW5zdCwgXCJjb25zdHJhaW5JbnB1dFwiICkgKSB7XG5cdFx0XHRjaGFycyA9ICQuZGF0ZXBpY2tlci5fcG9zc2libGVDaGFycyggJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwiZGF0ZUZvcm1hdFwiICkgKTtcblx0XHRcdGNociA9IFN0cmluZy5mcm9tQ2hhckNvZGUoIGV2ZW50LmNoYXJDb2RlID09IG51bGwgPyBldmVudC5rZXlDb2RlIDogZXZlbnQuY2hhckNvZGUgKTtcblx0XHRcdHJldHVybiBldmVudC5jdHJsS2V5IHx8IGV2ZW50Lm1ldGFLZXkgfHwgKCBjaHIgPCBcIiBcIiB8fCAhY2hhcnMgfHwgY2hhcnMuaW5kZXhPZiggY2hyICkgPiAtMSApO1xuXHRcdH1cblx0fSxcblxuXHQvKiBTeW5jaHJvbmlzZSBtYW51YWwgZW50cnkgYW5kIGZpZWxkL2FsdGVybmF0ZSBmaWVsZC4gKi9cblx0X2RvS2V5VXA6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgZGF0ZSxcblx0XHRcdGluc3QgPSAkLmRhdGVwaWNrZXIuX2dldEluc3QoIGV2ZW50LnRhcmdldCApO1xuXG5cdFx0aWYgKCBpbnN0LmlucHV0LnZhbCgpICE9PSBpbnN0Lmxhc3RWYWwgKSB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRkYXRlID0gJC5kYXRlcGlja2VyLnBhcnNlRGF0ZSggJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwiZGF0ZUZvcm1hdFwiICksXG5cdFx0XHRcdFx0KCBpbnN0LmlucHV0ID8gaW5zdC5pbnB1dC52YWwoKSA6IG51bGwgKSxcblx0XHRcdFx0XHQkLmRhdGVwaWNrZXIuX2dldEZvcm1hdENvbmZpZyggaW5zdCApICk7XG5cblx0XHRcdFx0aWYgKCBkYXRlICkgeyAvLyBvbmx5IGlmIHZhbGlkXG5cdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9zZXREYXRlRnJvbUZpZWxkKCBpbnN0ICk7XG5cdFx0XHRcdFx0JC5kYXRlcGlja2VyLl91cGRhdGVBbHRlcm5hdGUoIGluc3QgKTtcblx0XHRcdFx0XHQkLmRhdGVwaWNrZXIuX3VwZGF0ZURhdGVwaWNrZXIoIGluc3QgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdFx0Y2F0Y2ggKCBlcnIgKSB7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiB0cnVlO1xuXHR9LFxuXG5cdC8qIFBvcC11cCB0aGUgZGF0ZSBwaWNrZXIgZm9yIGEgZ2l2ZW4gaW5wdXQgZmllbGQuXG5cdCAqIElmIGZhbHNlIHJldHVybmVkIGZyb20gYmVmb3JlU2hvdyBldmVudCBoYW5kbGVyIGRvIG5vdCBzaG93LlxuXHQgKiBAcGFyYW0gIGlucHV0ICBlbGVtZW50IC0gdGhlIGlucHV0IGZpZWxkIGF0dGFjaGVkIHRvIHRoZSBkYXRlIHBpY2tlciBvclxuXHQgKlx0XHRcdFx0XHRldmVudCAtIGlmIHRyaWdnZXJlZCBieSBmb2N1c1xuXHQgKi9cblx0X3Nob3dEYXRlcGlja2VyOiBmdW5jdGlvbiggaW5wdXQgKSB7XG5cdFx0aW5wdXQgPSBpbnB1dC50YXJnZXQgfHwgaW5wdXQ7XG5cdFx0aWYgKCBpbnB1dC5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpICE9PSBcImlucHV0XCIgKSB7IC8vIGZpbmQgZnJvbSBidXR0b24vaW1hZ2UgdHJpZ2dlclxuXHRcdFx0aW5wdXQgPSAkKCBcImlucHV0XCIsIGlucHV0LnBhcmVudE5vZGUgKVsgMCBdO1xuXHRcdH1cblxuXHRcdGlmICggJC5kYXRlcGlja2VyLl9pc0Rpc2FibGVkRGF0ZXBpY2tlciggaW5wdXQgKSB8fCAkLmRhdGVwaWNrZXIuX2xhc3RJbnB1dCA9PT0gaW5wdXQgKSB7IC8vIGFscmVhZHkgaGVyZVxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBpbnN0LCBiZWZvcmVTaG93LCBiZWZvcmVTaG93U2V0dGluZ3MsIGlzRml4ZWQsXG5cdFx0XHRvZmZzZXQsIHNob3dBbmltLCBkdXJhdGlvbjtcblxuXHRcdGluc3QgPSAkLmRhdGVwaWNrZXIuX2dldEluc3QoIGlucHV0ICk7XG5cdFx0aWYgKCAkLmRhdGVwaWNrZXIuX2N1ckluc3QgJiYgJC5kYXRlcGlja2VyLl9jdXJJbnN0ICE9PSBpbnN0ICkge1xuXHRcdFx0JC5kYXRlcGlja2VyLl9jdXJJbnN0LmRwRGl2LnN0b3AoIHRydWUsIHRydWUgKTtcblx0XHRcdGlmICggaW5zdCAmJiAkLmRhdGVwaWNrZXIuX2RhdGVwaWNrZXJTaG93aW5nICkge1xuXHRcdFx0XHQkLmRhdGVwaWNrZXIuX2hpZGVEYXRlcGlja2VyKCAkLmRhdGVwaWNrZXIuX2N1ckluc3QuaW5wdXRbIDAgXSApO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGJlZm9yZVNob3cgPSAkLmRhdGVwaWNrZXIuX2dldCggaW5zdCwgXCJiZWZvcmVTaG93XCIgKTtcblx0XHRiZWZvcmVTaG93U2V0dGluZ3MgPSBiZWZvcmVTaG93ID8gYmVmb3JlU2hvdy5hcHBseSggaW5wdXQsIFsgaW5wdXQsIGluc3QgXSApIDoge307XG5cdFx0aWYgKCBiZWZvcmVTaG93U2V0dGluZ3MgPT09IGZhbHNlICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRkYXRlcGlja2VyX2V4dGVuZFJlbW92ZSggaW5zdC5zZXR0aW5ncywgYmVmb3JlU2hvd1NldHRpbmdzICk7XG5cblx0XHRpbnN0Lmxhc3RWYWwgPSBudWxsO1xuXHRcdCQuZGF0ZXBpY2tlci5fbGFzdElucHV0ID0gaW5wdXQ7XG5cdFx0JC5kYXRlcGlja2VyLl9zZXREYXRlRnJvbUZpZWxkKCBpbnN0ICk7XG5cblx0XHRpZiAoICQuZGF0ZXBpY2tlci5faW5EaWFsb2cgKSB7IC8vIGhpZGUgY3Vyc29yXG5cdFx0XHRpbnB1dC52YWx1ZSA9IFwiXCI7XG5cdFx0fVxuXHRcdGlmICggISQuZGF0ZXBpY2tlci5fcG9zICkgeyAvLyBwb3NpdGlvbiBiZWxvdyBpbnB1dFxuXHRcdFx0JC5kYXRlcGlja2VyLl9wb3MgPSAkLmRhdGVwaWNrZXIuX2ZpbmRQb3MoIGlucHV0ICk7XG5cdFx0XHQkLmRhdGVwaWNrZXIuX3Bvc1sgMSBdICs9IGlucHV0Lm9mZnNldEhlaWdodDsgLy8gYWRkIHRoZSBoZWlnaHRcblx0XHR9XG5cblx0XHRpc0ZpeGVkID0gZmFsc2U7XG5cdFx0JCggaW5wdXQgKS5wYXJlbnRzKCkuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRpc0ZpeGVkIHw9ICQoIHRoaXMgKS5jc3MoIFwicG9zaXRpb25cIiApID09PSBcImZpeGVkXCI7XG5cdFx0XHRyZXR1cm4gIWlzRml4ZWQ7XG5cdFx0fSApO1xuXG5cdFx0b2Zmc2V0ID0geyBsZWZ0OiAkLmRhdGVwaWNrZXIuX3Bvc1sgMCBdLCB0b3A6ICQuZGF0ZXBpY2tlci5fcG9zWyAxIF0gfTtcblx0XHQkLmRhdGVwaWNrZXIuX3BvcyA9IG51bGw7XG5cblx0XHQvL3RvIGF2b2lkIGZsYXNoZXMgb24gRmlyZWZveFxuXHRcdGluc3QuZHBEaXYuZW1wdHkoKTtcblxuXHRcdC8vIGRldGVybWluZSBzaXppbmcgb2Zmc2NyZWVuXG5cdFx0aW5zdC5kcERpdi5jc3MoIHsgcG9zaXRpb246IFwiYWJzb2x1dGVcIiwgZGlzcGxheTogXCJibG9ja1wiLCB0b3A6IFwiLTEwMDBweFwiIH0gKTtcblx0XHQkLmRhdGVwaWNrZXIuX3VwZGF0ZURhdGVwaWNrZXIoIGluc3QgKTtcblxuXHRcdC8vIGZpeCB3aWR0aCBmb3IgZHluYW1pYyBudW1iZXIgb2YgZGF0ZSBwaWNrZXJzXG5cdFx0Ly8gYW5kIGFkanVzdCBwb3NpdGlvbiBiZWZvcmUgc2hvd2luZ1xuXHRcdG9mZnNldCA9ICQuZGF0ZXBpY2tlci5fY2hlY2tPZmZzZXQoIGluc3QsIG9mZnNldCwgaXNGaXhlZCApO1xuXHRcdGluc3QuZHBEaXYuY3NzKCB7IHBvc2l0aW9uOiAoICQuZGF0ZXBpY2tlci5faW5EaWFsb2cgJiYgJC5ibG9ja1VJID9cblx0XHRcdFwic3RhdGljXCIgOiAoIGlzRml4ZWQgPyBcImZpeGVkXCIgOiBcImFic29sdXRlXCIgKSApLCBkaXNwbGF5OiBcIm5vbmVcIixcblx0XHRcdGxlZnQ6IG9mZnNldC5sZWZ0ICsgXCJweFwiLCB0b3A6IG9mZnNldC50b3AgKyBcInB4XCIgfSApO1xuXG5cdFx0aWYgKCAhaW5zdC5pbmxpbmUgKSB7XG5cdFx0XHRzaG93QW5pbSA9ICQuZGF0ZXBpY2tlci5fZ2V0KCBpbnN0LCBcInNob3dBbmltXCIgKTtcblx0XHRcdGR1cmF0aW9uID0gJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwiZHVyYXRpb25cIiApO1xuXHRcdFx0aW5zdC5kcERpdi5jc3MoIFwiei1pbmRleFwiLCBkYXRlcGlja2VyX2dldFppbmRleCggJCggaW5wdXQgKSApICsgMSApO1xuXHRcdFx0JC5kYXRlcGlja2VyLl9kYXRlcGlja2VyU2hvd2luZyA9IHRydWU7XG5cblx0XHRcdGlmICggJC5lZmZlY3RzICYmICQuZWZmZWN0cy5lZmZlY3RbIHNob3dBbmltIF0gKSB7XG5cdFx0XHRcdGluc3QuZHBEaXYuc2hvdyggc2hvd0FuaW0sICQuZGF0ZXBpY2tlci5fZ2V0KCBpbnN0LCBcInNob3dPcHRpb25zXCIgKSwgZHVyYXRpb24gKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGluc3QuZHBEaXZbIHNob3dBbmltIHx8IFwic2hvd1wiIF0oIHNob3dBbmltID8gZHVyYXRpb24gOiBudWxsICk7XG5cdFx0XHR9XG5cblx0XHRcdGlmICggJC5kYXRlcGlja2VyLl9zaG91bGRGb2N1c0lucHV0KCBpbnN0ICkgKSB7XG5cdFx0XHRcdGluc3QuaW5wdXQudHJpZ2dlciggXCJmb2N1c1wiICk7XG5cdFx0XHR9XG5cblx0XHRcdCQuZGF0ZXBpY2tlci5fY3VySW5zdCA9IGluc3Q7XG5cdFx0fVxuXHR9LFxuXG5cdC8qIEdlbmVyYXRlIHRoZSBkYXRlIHBpY2tlciBjb250ZW50LiAqL1xuXHRfdXBkYXRlRGF0ZXBpY2tlcjogZnVuY3Rpb24oIGluc3QgKSB7XG5cdFx0dGhpcy5tYXhSb3dzID0gNDsgLy9SZXNldCB0aGUgbWF4IG51bWJlciBvZiByb3dzIGJlaW5nIGRpc3BsYXllZCAoc2VlICM3MDQzKVxuXHRcdGRhdGVwaWNrZXJfaW5zdEFjdGl2ZSA9IGluc3Q7IC8vIGZvciBkZWxlZ2F0ZSBob3ZlciBldmVudHNcblx0XHRpbnN0LmRwRGl2LmVtcHR5KCkuYXBwZW5kKCB0aGlzLl9nZW5lcmF0ZUhUTUwoIGluc3QgKSApO1xuXHRcdHRoaXMuX2F0dGFjaEhhbmRsZXJzKCBpbnN0ICk7XG5cblx0XHR2YXIgb3JpZ3llYXJzaHRtbCxcblx0XHRcdG51bU1vbnRocyA9IHRoaXMuX2dldE51bWJlck9mTW9udGhzKCBpbnN0ICksXG5cdFx0XHRjb2xzID0gbnVtTW9udGhzWyAxIF0sXG5cdFx0XHR3aWR0aCA9IDE3LFxuXHRcdFx0YWN0aXZlQ2VsbCA9IGluc3QuZHBEaXYuZmluZCggXCIuXCIgKyB0aGlzLl9kYXlPdmVyQ2xhc3MgKyBcIiBhXCIgKTtcblxuXHRcdGlmICggYWN0aXZlQ2VsbC5sZW5ndGggPiAwICkge1xuXHRcdFx0ZGF0ZXBpY2tlcl9oYW5kbGVNb3VzZW92ZXIuYXBwbHkoIGFjdGl2ZUNlbGwuZ2V0KCAwICkgKTtcblx0XHR9XG5cblx0XHRpbnN0LmRwRGl2LnJlbW92ZUNsYXNzKCBcInVpLWRhdGVwaWNrZXItbXVsdGktMiB1aS1kYXRlcGlja2VyLW11bHRpLTMgdWktZGF0ZXBpY2tlci1tdWx0aS00XCIgKS53aWR0aCggXCJcIiApO1xuXHRcdGlmICggY29scyA+IDEgKSB7XG5cdFx0XHRpbnN0LmRwRGl2LmFkZENsYXNzKCBcInVpLWRhdGVwaWNrZXItbXVsdGktXCIgKyBjb2xzICkuY3NzKCBcIndpZHRoXCIsICggd2lkdGggKiBjb2xzICkgKyBcImVtXCIgKTtcblx0XHR9XG5cdFx0aW5zdC5kcERpdlsgKCBudW1Nb250aHNbIDAgXSAhPT0gMSB8fCBudW1Nb250aHNbIDEgXSAhPT0gMSA/IFwiYWRkXCIgOiBcInJlbW92ZVwiICkgK1xuXHRcdFx0XCJDbGFzc1wiIF0oIFwidWktZGF0ZXBpY2tlci1tdWx0aVwiICk7XG5cdFx0aW5zdC5kcERpdlsgKCB0aGlzLl9nZXQoIGluc3QsIFwiaXNSVExcIiApID8gXCJhZGRcIiA6IFwicmVtb3ZlXCIgKSArXG5cdFx0XHRcIkNsYXNzXCIgXSggXCJ1aS1kYXRlcGlja2VyLXJ0bFwiICk7XG5cblx0XHRpZiAoIGluc3QgPT09ICQuZGF0ZXBpY2tlci5fY3VySW5zdCAmJiAkLmRhdGVwaWNrZXIuX2RhdGVwaWNrZXJTaG93aW5nICYmICQuZGF0ZXBpY2tlci5fc2hvdWxkRm9jdXNJbnB1dCggaW5zdCApICkge1xuXHRcdFx0aW5zdC5pbnB1dC50cmlnZ2VyKCBcImZvY3VzXCIgKTtcblx0XHR9XG5cblx0XHQvLyBEZWZmZXJlZCByZW5kZXIgb2YgdGhlIHllYXJzIHNlbGVjdCAodG8gYXZvaWQgZmxhc2hlcyBvbiBGaXJlZm94KVxuXHRcdGlmICggaW5zdC55ZWFyc2h0bWwgKSB7XG5cdFx0XHRvcmlneWVhcnNodG1sID0gaW5zdC55ZWFyc2h0bWw7XG5cdFx0XHRzZXRUaW1lb3V0KCBmdW5jdGlvbigpIHtcblxuXHRcdFx0XHQvL2Fzc3VyZSB0aGF0IGluc3QueWVhcnNodG1sIGRpZG4ndCBjaGFuZ2UuXG5cdFx0XHRcdGlmICggb3JpZ3llYXJzaHRtbCA9PT0gaW5zdC55ZWFyc2h0bWwgJiYgaW5zdC55ZWFyc2h0bWwgKSB7XG5cdFx0XHRcdFx0aW5zdC5kcERpdi5maW5kKCBcInNlbGVjdC51aS1kYXRlcGlja2VyLXllYXI6Zmlyc3RcIiApLnJlcGxhY2VXaXRoKCBpbnN0LnllYXJzaHRtbCApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdG9yaWd5ZWFyc2h0bWwgPSBpbnN0LnllYXJzaHRtbCA9IG51bGw7XG5cdFx0XHR9LCAwICk7XG5cdFx0fVxuXHR9LFxuXG5cdC8vICM2Njk0IC0gZG9uJ3QgZm9jdXMgdGhlIGlucHV0IGlmIGl0J3MgYWxyZWFkeSBmb2N1c2VkXG5cdC8vIHRoaXMgYnJlYWtzIHRoZSBjaGFuZ2UgZXZlbnQgaW4gSUVcblx0Ly8gU3VwcG9ydDogSUUgYW5kIGpRdWVyeSA8MS45XG5cdF9zaG91bGRGb2N1c0lucHV0OiBmdW5jdGlvbiggaW5zdCApIHtcblx0XHRyZXR1cm4gaW5zdC5pbnB1dCAmJiBpbnN0LmlucHV0LmlzKCBcIjp2aXNpYmxlXCIgKSAmJiAhaW5zdC5pbnB1dC5pcyggXCI6ZGlzYWJsZWRcIiApICYmICFpbnN0LmlucHV0LmlzKCBcIjpmb2N1c1wiICk7XG5cdH0sXG5cblx0LyogQ2hlY2sgcG9zaXRpb25pbmcgdG8gcmVtYWluIG9uIHNjcmVlbi4gKi9cblx0X2NoZWNrT2Zmc2V0OiBmdW5jdGlvbiggaW5zdCwgb2Zmc2V0LCBpc0ZpeGVkICkge1xuXHRcdHZhciBkcFdpZHRoID0gaW5zdC5kcERpdi5vdXRlcldpZHRoKCksXG5cdFx0XHRkcEhlaWdodCA9IGluc3QuZHBEaXYub3V0ZXJIZWlnaHQoKSxcblx0XHRcdGlucHV0V2lkdGggPSBpbnN0LmlucHV0ID8gaW5zdC5pbnB1dC5vdXRlcldpZHRoKCkgOiAwLFxuXHRcdFx0aW5wdXRIZWlnaHQgPSBpbnN0LmlucHV0ID8gaW5zdC5pbnB1dC5vdXRlckhlaWdodCgpIDogMCxcblx0XHRcdHZpZXdXaWR0aCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5jbGllbnRXaWR0aCArICggaXNGaXhlZCA/IDAgOiAkKCBkb2N1bWVudCApLnNjcm9sbExlZnQoKSApLFxuXHRcdFx0dmlld0hlaWdodCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5jbGllbnRIZWlnaHQgKyAoIGlzRml4ZWQgPyAwIDogJCggZG9jdW1lbnQgKS5zY3JvbGxUb3AoKSApO1xuXG5cdFx0b2Zmc2V0LmxlZnQgLT0gKCB0aGlzLl9nZXQoIGluc3QsIFwiaXNSVExcIiApID8gKCBkcFdpZHRoIC0gaW5wdXRXaWR0aCApIDogMCApO1xuXHRcdG9mZnNldC5sZWZ0IC09ICggaXNGaXhlZCAmJiBvZmZzZXQubGVmdCA9PT0gaW5zdC5pbnB1dC5vZmZzZXQoKS5sZWZ0ICkgPyAkKCBkb2N1bWVudCApLnNjcm9sbExlZnQoKSA6IDA7XG5cdFx0b2Zmc2V0LnRvcCAtPSAoIGlzRml4ZWQgJiYgb2Zmc2V0LnRvcCA9PT0gKCBpbnN0LmlucHV0Lm9mZnNldCgpLnRvcCArIGlucHV0SGVpZ2h0ICkgKSA/ICQoIGRvY3VtZW50ICkuc2Nyb2xsVG9wKCkgOiAwO1xuXG5cdFx0Ly8gTm93IGNoZWNrIGlmIGRhdGVwaWNrZXIgaXMgc2hvd2luZyBvdXRzaWRlIHdpbmRvdyB2aWV3cG9ydCAtIG1vdmUgdG8gYSBiZXR0ZXIgcGxhY2UgaWYgc28uXG5cdFx0b2Zmc2V0LmxlZnQgLT0gTWF0aC5taW4oIG9mZnNldC5sZWZ0LCAoIG9mZnNldC5sZWZ0ICsgZHBXaWR0aCA+IHZpZXdXaWR0aCAmJiB2aWV3V2lkdGggPiBkcFdpZHRoICkgP1xuXHRcdFx0TWF0aC5hYnMoIG9mZnNldC5sZWZ0ICsgZHBXaWR0aCAtIHZpZXdXaWR0aCApIDogMCApO1xuXHRcdG9mZnNldC50b3AgLT0gTWF0aC5taW4oIG9mZnNldC50b3AsICggb2Zmc2V0LnRvcCArIGRwSGVpZ2h0ID4gdmlld0hlaWdodCAmJiB2aWV3SGVpZ2h0ID4gZHBIZWlnaHQgKSA/XG5cdFx0XHRNYXRoLmFicyggZHBIZWlnaHQgKyBpbnB1dEhlaWdodCApIDogMCApO1xuXG5cdFx0cmV0dXJuIG9mZnNldDtcblx0fSxcblxuXHQvKiBGaW5kIGFuIG9iamVjdCdzIHBvc2l0aW9uIG9uIHRoZSBzY3JlZW4uICovXG5cdF9maW5kUG9zOiBmdW5jdGlvbiggb2JqICkge1xuXHRcdHZhciBwb3NpdGlvbixcblx0XHRcdGluc3QgPSB0aGlzLl9nZXRJbnN0KCBvYmogKSxcblx0XHRcdGlzUlRMID0gdGhpcy5fZ2V0KCBpbnN0LCBcImlzUlRMXCIgKTtcblxuXHRcdHdoaWxlICggb2JqICYmICggb2JqLnR5cGUgPT09IFwiaGlkZGVuXCIgfHwgb2JqLm5vZGVUeXBlICE9PSAxIHx8ICQuZXhwci5maWx0ZXJzLmhpZGRlbiggb2JqICkgKSApIHtcblx0XHRcdG9iaiA9IG9ialsgaXNSVEwgPyBcInByZXZpb3VzU2libGluZ1wiIDogXCJuZXh0U2libGluZ1wiIF07XG5cdFx0fVxuXG5cdFx0cG9zaXRpb24gPSAkKCBvYmogKS5vZmZzZXQoKTtcblx0XHRyZXR1cm4gWyBwb3NpdGlvbi5sZWZ0LCBwb3NpdGlvbi50b3AgXTtcblx0fSxcblxuXHQvKiBIaWRlIHRoZSBkYXRlIHBpY2tlciBmcm9tIHZpZXcuXG5cdCAqIEBwYXJhbSAgaW5wdXQgIGVsZW1lbnQgLSB0aGUgaW5wdXQgZmllbGQgYXR0YWNoZWQgdG8gdGhlIGRhdGUgcGlja2VyXG5cdCAqL1xuXHRfaGlkZURhdGVwaWNrZXI6IGZ1bmN0aW9uKCBpbnB1dCApIHtcblx0XHR2YXIgc2hvd0FuaW0sIGR1cmF0aW9uLCBwb3N0UHJvY2Vzcywgb25DbG9zZSxcblx0XHRcdGluc3QgPSB0aGlzLl9jdXJJbnN0O1xuXG5cdFx0aWYgKCAhaW5zdCB8fCAoIGlucHV0ICYmIGluc3QgIT09ICQuZGF0YSggaW5wdXQsIFwiZGF0ZXBpY2tlclwiICkgKSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoIHRoaXMuX2RhdGVwaWNrZXJTaG93aW5nICkge1xuXHRcdFx0c2hvd0FuaW0gPSB0aGlzLl9nZXQoIGluc3QsIFwic2hvd0FuaW1cIiApO1xuXHRcdFx0ZHVyYXRpb24gPSB0aGlzLl9nZXQoIGluc3QsIFwiZHVyYXRpb25cIiApO1xuXHRcdFx0cG9zdFByb2Nlc3MgPSBmdW5jdGlvbigpIHtcblx0XHRcdFx0JC5kYXRlcGlja2VyLl90aWR5RGlhbG9nKCBpbnN0ICk7XG5cdFx0XHR9O1xuXG5cdFx0XHQvLyBERVBSRUNBVEVEOiBhZnRlciBCQyBmb3IgMS44LnggJC5lZmZlY3RzWyBzaG93QW5pbSBdIGlzIG5vdCBuZWVkZWRcblx0XHRcdGlmICggJC5lZmZlY3RzICYmICggJC5lZmZlY3RzLmVmZmVjdFsgc2hvd0FuaW0gXSB8fCAkLmVmZmVjdHNbIHNob3dBbmltIF0gKSApIHtcblx0XHRcdFx0aW5zdC5kcERpdi5oaWRlKCBzaG93QW5pbSwgJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwic2hvd09wdGlvbnNcIiApLCBkdXJhdGlvbiwgcG9zdFByb2Nlc3MgKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGluc3QuZHBEaXZbICggc2hvd0FuaW0gPT09IFwic2xpZGVEb3duXCIgPyBcInNsaWRlVXBcIiA6XG5cdFx0XHRcdFx0KCBzaG93QW5pbSA9PT0gXCJmYWRlSW5cIiA/IFwiZmFkZU91dFwiIDogXCJoaWRlXCIgKSApIF0oICggc2hvd0FuaW0gPyBkdXJhdGlvbiA6IG51bGwgKSwgcG9zdFByb2Nlc3MgKTtcblx0XHRcdH1cblxuXHRcdFx0aWYgKCAhc2hvd0FuaW0gKSB7XG5cdFx0XHRcdHBvc3RQcm9jZXNzKCk7XG5cdFx0XHR9XG5cdFx0XHR0aGlzLl9kYXRlcGlja2VyU2hvd2luZyA9IGZhbHNlO1xuXG5cdFx0XHRvbkNsb3NlID0gdGhpcy5fZ2V0KCBpbnN0LCBcIm9uQ2xvc2VcIiApO1xuXHRcdFx0aWYgKCBvbkNsb3NlICkge1xuXHRcdFx0XHRvbkNsb3NlLmFwcGx5KCAoIGluc3QuaW5wdXQgPyBpbnN0LmlucHV0WyAwIF0gOiBudWxsICksIFsgKCBpbnN0LmlucHV0ID8gaW5zdC5pbnB1dC52YWwoKSA6IFwiXCIgKSwgaW5zdCBdICk7XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX2xhc3RJbnB1dCA9IG51bGw7XG5cdFx0XHRpZiAoIHRoaXMuX2luRGlhbG9nICkge1xuXHRcdFx0XHR0aGlzLl9kaWFsb2dJbnB1dC5jc3MoIHsgcG9zaXRpb246IFwiYWJzb2x1dGVcIiwgbGVmdDogXCIwXCIsIHRvcDogXCItMTAwcHhcIiB9ICk7XG5cdFx0XHRcdGlmICggJC5ibG9ja1VJICkge1xuXHRcdFx0XHRcdCQudW5ibG9ja1VJKCk7XG5cdFx0XHRcdFx0JCggXCJib2R5XCIgKS5hcHBlbmQoIHRoaXMuZHBEaXYgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdFx0dGhpcy5faW5EaWFsb2cgPSBmYWxzZTtcblx0XHR9XG5cdH0sXG5cblx0LyogVGlkeSB1cCBhZnRlciBhIGRpYWxvZyBkaXNwbGF5LiAqL1xuXHRfdGlkeURpYWxvZzogZnVuY3Rpb24oIGluc3QgKSB7XG5cdFx0aW5zdC5kcERpdi5yZW1vdmVDbGFzcyggdGhpcy5fZGlhbG9nQ2xhc3MgKS5vZmYoIFwiLnVpLWRhdGVwaWNrZXItY2FsZW5kYXJcIiApO1xuXHR9LFxuXG5cdC8qIENsb3NlIGRhdGUgcGlja2VyIGlmIGNsaWNrZWQgZWxzZXdoZXJlLiAqL1xuXHRfY2hlY2tFeHRlcm5hbENsaWNrOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0aWYgKCAhJC5kYXRlcGlja2VyLl9jdXJJbnN0ICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciAkdGFyZ2V0ID0gJCggZXZlbnQudGFyZ2V0ICksXG5cdFx0XHRpbnN0ID0gJC5kYXRlcGlja2VyLl9nZXRJbnN0KCAkdGFyZ2V0WyAwIF0gKTtcblxuXHRcdGlmICggKCAoICR0YXJnZXRbIDAgXS5pZCAhPT0gJC5kYXRlcGlja2VyLl9tYWluRGl2SWQgJiZcblx0XHRcdFx0JHRhcmdldC5wYXJlbnRzKCBcIiNcIiArICQuZGF0ZXBpY2tlci5fbWFpbkRpdklkICkubGVuZ3RoID09PSAwICYmXG5cdFx0XHRcdCEkdGFyZ2V0Lmhhc0NsYXNzKCAkLmRhdGVwaWNrZXIubWFya2VyQ2xhc3NOYW1lICkgJiZcblx0XHRcdFx0ISR0YXJnZXQuY2xvc2VzdCggXCIuXCIgKyAkLmRhdGVwaWNrZXIuX3RyaWdnZXJDbGFzcyApLmxlbmd0aCAmJlxuXHRcdFx0XHQkLmRhdGVwaWNrZXIuX2RhdGVwaWNrZXJTaG93aW5nICYmICEoICQuZGF0ZXBpY2tlci5faW5EaWFsb2cgJiYgJC5ibG9ja1VJICkgKSApIHx8XG5cdFx0XHQoICR0YXJnZXQuaGFzQ2xhc3MoICQuZGF0ZXBpY2tlci5tYXJrZXJDbGFzc05hbWUgKSAmJiAkLmRhdGVwaWNrZXIuX2N1ckluc3QgIT09IGluc3QgKSApIHtcblx0XHRcdFx0JC5kYXRlcGlja2VyLl9oaWRlRGF0ZXBpY2tlcigpO1xuXHRcdH1cblx0fSxcblxuXHQvKiBBZGp1c3Qgb25lIG9mIHRoZSBkYXRlIHN1Yi1maWVsZHMuICovXG5cdF9hZGp1c3REYXRlOiBmdW5jdGlvbiggaWQsIG9mZnNldCwgcGVyaW9kICkge1xuXHRcdHZhciB0YXJnZXQgPSAkKCBpZCApLFxuXHRcdFx0aW5zdCA9IHRoaXMuX2dldEluc3QoIHRhcmdldFsgMCBdICk7XG5cblx0XHRpZiAoIHRoaXMuX2lzRGlzYWJsZWREYXRlcGlja2VyKCB0YXJnZXRbIDAgXSApICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHR0aGlzLl9hZGp1c3RJbnN0RGF0ZSggaW5zdCwgb2Zmc2V0ICtcblx0XHRcdCggcGVyaW9kID09PSBcIk1cIiA/IHRoaXMuX2dldCggaW5zdCwgXCJzaG93Q3VycmVudEF0UG9zXCIgKSA6IDAgKSwgLy8gdW5kbyBwb3NpdGlvbmluZ1xuXHRcdFx0cGVyaW9kICk7XG5cdFx0dGhpcy5fdXBkYXRlRGF0ZXBpY2tlciggaW5zdCApO1xuXHR9LFxuXG5cdC8qIEFjdGlvbiBmb3IgY3VycmVudCBsaW5rLiAqL1xuXHRfZ290b1RvZGF5OiBmdW5jdGlvbiggaWQgKSB7XG5cdFx0dmFyIGRhdGUsXG5cdFx0XHR0YXJnZXQgPSAkKCBpZCApLFxuXHRcdFx0aW5zdCA9IHRoaXMuX2dldEluc3QoIHRhcmdldFsgMCBdICk7XG5cblx0XHRpZiAoIHRoaXMuX2dldCggaW5zdCwgXCJnb3RvQ3VycmVudFwiICkgJiYgaW5zdC5jdXJyZW50RGF5ICkge1xuXHRcdFx0aW5zdC5zZWxlY3RlZERheSA9IGluc3QuY3VycmVudERheTtcblx0XHRcdGluc3QuZHJhd01vbnRoID0gaW5zdC5zZWxlY3RlZE1vbnRoID0gaW5zdC5jdXJyZW50TW9udGg7XG5cdFx0XHRpbnN0LmRyYXdZZWFyID0gaW5zdC5zZWxlY3RlZFllYXIgPSBpbnN0LmN1cnJlbnRZZWFyO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRkYXRlID0gbmV3IERhdGUoKTtcblx0XHRcdGluc3Quc2VsZWN0ZWREYXkgPSBkYXRlLmdldERhdGUoKTtcblx0XHRcdGluc3QuZHJhd01vbnRoID0gaW5zdC5zZWxlY3RlZE1vbnRoID0gZGF0ZS5nZXRNb250aCgpO1xuXHRcdFx0aW5zdC5kcmF3WWVhciA9IGluc3Quc2VsZWN0ZWRZZWFyID0gZGF0ZS5nZXRGdWxsWWVhcigpO1xuXHRcdH1cblx0XHR0aGlzLl9ub3RpZnlDaGFuZ2UoIGluc3QgKTtcblx0XHR0aGlzLl9hZGp1c3REYXRlKCB0YXJnZXQgKTtcblx0fSxcblxuXHQvKiBBY3Rpb24gZm9yIHNlbGVjdGluZyBhIG5ldyBtb250aC95ZWFyLiAqL1xuXHRfc2VsZWN0TW9udGhZZWFyOiBmdW5jdGlvbiggaWQsIHNlbGVjdCwgcGVyaW9kICkge1xuXHRcdHZhciB0YXJnZXQgPSAkKCBpZCApLFxuXHRcdFx0aW5zdCA9IHRoaXMuX2dldEluc3QoIHRhcmdldFsgMCBdICk7XG5cblx0XHRpbnN0WyBcInNlbGVjdGVkXCIgKyAoIHBlcmlvZCA9PT0gXCJNXCIgPyBcIk1vbnRoXCIgOiBcIlllYXJcIiApIF0gPVxuXHRcdGluc3RbIFwiZHJhd1wiICsgKCBwZXJpb2QgPT09IFwiTVwiID8gXCJNb250aFwiIDogXCJZZWFyXCIgKSBdID1cblx0XHRcdHBhcnNlSW50KCBzZWxlY3Qub3B0aW9uc1sgc2VsZWN0LnNlbGVjdGVkSW5kZXggXS52YWx1ZSwgMTAgKTtcblxuXHRcdHRoaXMuX25vdGlmeUNoYW5nZSggaW5zdCApO1xuXHRcdHRoaXMuX2FkanVzdERhdGUoIHRhcmdldCApO1xuXHR9LFxuXG5cdC8qIEFjdGlvbiBmb3Igc2VsZWN0aW5nIGEgZGF5LiAqL1xuXHRfc2VsZWN0RGF5OiBmdW5jdGlvbiggaWQsIG1vbnRoLCB5ZWFyLCB0ZCApIHtcblx0XHR2YXIgaW5zdCxcblx0XHRcdHRhcmdldCA9ICQoIGlkICk7XG5cblx0XHRpZiAoICQoIHRkICkuaGFzQ2xhc3MoIHRoaXMuX3Vuc2VsZWN0YWJsZUNsYXNzICkgfHwgdGhpcy5faXNEaXNhYmxlZERhdGVwaWNrZXIoIHRhcmdldFsgMCBdICkgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aW5zdCA9IHRoaXMuX2dldEluc3QoIHRhcmdldFsgMCBdICk7XG5cdFx0aW5zdC5zZWxlY3RlZERheSA9IGluc3QuY3VycmVudERheSA9ICQoIFwiYVwiLCB0ZCApLmh0bWwoKTtcblx0XHRpbnN0LnNlbGVjdGVkTW9udGggPSBpbnN0LmN1cnJlbnRNb250aCA9IG1vbnRoO1xuXHRcdGluc3Quc2VsZWN0ZWRZZWFyID0gaW5zdC5jdXJyZW50WWVhciA9IHllYXI7XG5cdFx0dGhpcy5fc2VsZWN0RGF0ZSggaWQsIHRoaXMuX2Zvcm1hdERhdGUoIGluc3QsXG5cdFx0XHRpbnN0LmN1cnJlbnREYXksIGluc3QuY3VycmVudE1vbnRoLCBpbnN0LmN1cnJlbnRZZWFyICkgKTtcblx0fSxcblxuXHQvKiBFcmFzZSB0aGUgaW5wdXQgZmllbGQgYW5kIGhpZGUgdGhlIGRhdGUgcGlja2VyLiAqL1xuXHRfY2xlYXJEYXRlOiBmdW5jdGlvbiggaWQgKSB7XG5cdFx0dmFyIHRhcmdldCA9ICQoIGlkICk7XG5cdFx0dGhpcy5fc2VsZWN0RGF0ZSggdGFyZ2V0LCBcIlwiICk7XG5cdH0sXG5cblx0LyogVXBkYXRlIHRoZSBpbnB1dCBmaWVsZCB3aXRoIHRoZSBzZWxlY3RlZCBkYXRlLiAqL1xuXHRfc2VsZWN0RGF0ZTogZnVuY3Rpb24oIGlkLCBkYXRlU3RyICkge1xuXHRcdHZhciBvblNlbGVjdCxcblx0XHRcdHRhcmdldCA9ICQoIGlkICksXG5cdFx0XHRpbnN0ID0gdGhpcy5fZ2V0SW5zdCggdGFyZ2V0WyAwIF0gKTtcblxuXHRcdGRhdGVTdHIgPSAoIGRhdGVTdHIgIT0gbnVsbCA/IGRhdGVTdHIgOiB0aGlzLl9mb3JtYXREYXRlKCBpbnN0ICkgKTtcblx0XHRpZiAoIGluc3QuaW5wdXQgKSB7XG5cdFx0XHRpbnN0LmlucHV0LnZhbCggZGF0ZVN0ciApO1xuXHRcdH1cblx0XHR0aGlzLl91cGRhdGVBbHRlcm5hdGUoIGluc3QgKTtcblxuXHRcdG9uU2VsZWN0ID0gdGhpcy5fZ2V0KCBpbnN0LCBcIm9uU2VsZWN0XCIgKTtcblx0XHRpZiAoIG9uU2VsZWN0ICkge1xuXHRcdFx0b25TZWxlY3QuYXBwbHkoICggaW5zdC5pbnB1dCA/IGluc3QuaW5wdXRbIDAgXSA6IG51bGwgKSwgWyBkYXRlU3RyLCBpbnN0IF0gKTsgIC8vIHRyaWdnZXIgY3VzdG9tIGNhbGxiYWNrXG5cdFx0fSBlbHNlIGlmICggaW5zdC5pbnB1dCApIHtcblx0XHRcdGluc3QuaW5wdXQudHJpZ2dlciggXCJjaGFuZ2VcIiApOyAvLyBmaXJlIHRoZSBjaGFuZ2UgZXZlbnRcblx0XHR9XG5cblx0XHRpZiAoIGluc3QuaW5saW5lICkge1xuXHRcdFx0dGhpcy5fdXBkYXRlRGF0ZXBpY2tlciggaW5zdCApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLl9oaWRlRGF0ZXBpY2tlcigpO1xuXHRcdFx0dGhpcy5fbGFzdElucHV0ID0gaW5zdC5pbnB1dFsgMCBdO1xuXHRcdFx0aWYgKCB0eXBlb2YoIGluc3QuaW5wdXRbIDAgXSApICE9PSBcIm9iamVjdFwiICkge1xuXHRcdFx0XHRpbnN0LmlucHV0LnRyaWdnZXIoIFwiZm9jdXNcIiApOyAvLyByZXN0b3JlIGZvY3VzXG5cdFx0XHR9XG5cdFx0XHR0aGlzLl9sYXN0SW5wdXQgPSBudWxsO1xuXHRcdH1cblx0fSxcblxuXHQvKiBVcGRhdGUgYW55IGFsdGVybmF0ZSBmaWVsZCB0byBzeW5jaHJvbmlzZSB3aXRoIHRoZSBtYWluIGZpZWxkLiAqL1xuXHRfdXBkYXRlQWx0ZXJuYXRlOiBmdW5jdGlvbiggaW5zdCApIHtcblx0XHR2YXIgYWx0Rm9ybWF0LCBkYXRlLCBkYXRlU3RyLFxuXHRcdFx0YWx0RmllbGQgPSB0aGlzLl9nZXQoIGluc3QsIFwiYWx0RmllbGRcIiApO1xuXG5cdFx0aWYgKCBhbHRGaWVsZCApIHsgLy8gdXBkYXRlIGFsdGVybmF0ZSBmaWVsZCB0b29cblx0XHRcdGFsdEZvcm1hdCA9IHRoaXMuX2dldCggaW5zdCwgXCJhbHRGb3JtYXRcIiApIHx8IHRoaXMuX2dldCggaW5zdCwgXCJkYXRlRm9ybWF0XCIgKTtcblx0XHRcdGRhdGUgPSB0aGlzLl9nZXREYXRlKCBpbnN0ICk7XG5cdFx0XHRkYXRlU3RyID0gdGhpcy5mb3JtYXREYXRlKCBhbHRGb3JtYXQsIGRhdGUsIHRoaXMuX2dldEZvcm1hdENvbmZpZyggaW5zdCApICk7XG5cdFx0XHQkKCBhbHRGaWVsZCApLnZhbCggZGF0ZVN0ciApO1xuXHRcdH1cblx0fSxcblxuXHQvKiBTZXQgYXMgYmVmb3JlU2hvd0RheSBmdW5jdGlvbiB0byBwcmV2ZW50IHNlbGVjdGlvbiBvZiB3ZWVrZW5kcy5cblx0ICogQHBhcmFtICBkYXRlICBEYXRlIC0gdGhlIGRhdGUgdG8gY3VzdG9taXNlXG5cdCAqIEByZXR1cm4gW2Jvb2xlYW4sIHN0cmluZ10gLSBpcyB0aGlzIGRhdGUgc2VsZWN0YWJsZT8sIHdoYXQgaXMgaXRzIENTUyBjbGFzcz9cblx0ICovXG5cdG5vV2Vla2VuZHM6IGZ1bmN0aW9uKCBkYXRlICkge1xuXHRcdHZhciBkYXkgPSBkYXRlLmdldERheSgpO1xuXHRcdHJldHVybiBbICggZGF5ID4gMCAmJiBkYXkgPCA2ICksIFwiXCIgXTtcblx0fSxcblxuXHQvKiBTZXQgYXMgY2FsY3VsYXRlV2VlayB0byBkZXRlcm1pbmUgdGhlIHdlZWsgb2YgdGhlIHllYXIgYmFzZWQgb24gdGhlIElTTyA4NjAxIGRlZmluaXRpb24uXG5cdCAqIEBwYXJhbSAgZGF0ZSAgRGF0ZSAtIHRoZSBkYXRlIHRvIGdldCB0aGUgd2VlayBmb3Jcblx0ICogQHJldHVybiAgbnVtYmVyIC0gdGhlIG51bWJlciBvZiB0aGUgd2VlayB3aXRoaW4gdGhlIHllYXIgdGhhdCBjb250YWlucyB0aGlzIGRhdGVcblx0ICovXG5cdGlzbzg2MDFXZWVrOiBmdW5jdGlvbiggZGF0ZSApIHtcblx0XHR2YXIgdGltZSxcblx0XHRcdGNoZWNrRGF0ZSA9IG5ldyBEYXRlKCBkYXRlLmdldFRpbWUoKSApO1xuXG5cdFx0Ly8gRmluZCBUaHVyc2RheSBvZiB0aGlzIHdlZWsgc3RhcnRpbmcgb24gTW9uZGF5XG5cdFx0Y2hlY2tEYXRlLnNldERhdGUoIGNoZWNrRGF0ZS5nZXREYXRlKCkgKyA0IC0gKCBjaGVja0RhdGUuZ2V0RGF5KCkgfHwgNyApICk7XG5cblx0XHR0aW1lID0gY2hlY2tEYXRlLmdldFRpbWUoKTtcblx0XHRjaGVja0RhdGUuc2V0TW9udGgoIDAgKTsgLy8gQ29tcGFyZSB3aXRoIEphbiAxXG5cdFx0Y2hlY2tEYXRlLnNldERhdGUoIDEgKTtcblx0XHRyZXR1cm4gTWF0aC5mbG9vciggTWF0aC5yb3VuZCggKCB0aW1lIC0gY2hlY2tEYXRlICkgLyA4NjQwMDAwMCApIC8gNyApICsgMTtcblx0fSxcblxuXHQvKiBQYXJzZSBhIHN0cmluZyB2YWx1ZSBpbnRvIGEgZGF0ZSBvYmplY3QuXG5cdCAqIFNlZSBmb3JtYXREYXRlIGJlbG93IGZvciB0aGUgcG9zc2libGUgZm9ybWF0cy5cblx0ICpcblx0ICogQHBhcmFtICBmb3JtYXQgc3RyaW5nIC0gdGhlIGV4cGVjdGVkIGZvcm1hdCBvZiB0aGUgZGF0ZVxuXHQgKiBAcGFyYW0gIHZhbHVlIHN0cmluZyAtIHRoZSBkYXRlIGluIHRoZSBhYm92ZSBmb3JtYXRcblx0ICogQHBhcmFtICBzZXR0aW5ncyBPYmplY3QgLSBhdHRyaWJ1dGVzIGluY2x1ZGU6XG5cdCAqXHRcdFx0XHRcdHNob3J0WWVhckN1dG9mZiAgbnVtYmVyIC0gdGhlIGN1dG9mZiB5ZWFyIGZvciBkZXRlcm1pbmluZyB0aGUgY2VudHVyeSAob3B0aW9uYWwpXG5cdCAqXHRcdFx0XHRcdGRheU5hbWVzU2hvcnRcdHN0cmluZ1s3XSAtIGFiYnJldmlhdGVkIG5hbWVzIG9mIHRoZSBkYXlzIGZyb20gU3VuZGF5IChvcHRpb25hbClcblx0ICpcdFx0XHRcdFx0ZGF5TmFtZXNcdFx0c3RyaW5nWzddIC0gbmFtZXMgb2YgdGhlIGRheXMgZnJvbSBTdW5kYXkgKG9wdGlvbmFsKVxuXHQgKlx0XHRcdFx0XHRtb250aE5hbWVzU2hvcnQgc3RyaW5nWzEyXSAtIGFiYnJldmlhdGVkIG5hbWVzIG9mIHRoZSBtb250aHMgKG9wdGlvbmFsKVxuXHQgKlx0XHRcdFx0XHRtb250aE5hbWVzXHRcdHN0cmluZ1sxMl0gLSBuYW1lcyBvZiB0aGUgbW9udGhzIChvcHRpb25hbClcblx0ICogQHJldHVybiAgRGF0ZSAtIHRoZSBleHRyYWN0ZWQgZGF0ZSB2YWx1ZSBvciBudWxsIGlmIHZhbHVlIGlzIGJsYW5rXG5cdCAqL1xuXHRwYXJzZURhdGU6IGZ1bmN0aW9uKCBmb3JtYXQsIHZhbHVlLCBzZXR0aW5ncyApIHtcblx0XHRpZiAoIGZvcm1hdCA9PSBudWxsIHx8IHZhbHVlID09IG51bGwgKSB7XG5cdFx0XHR0aHJvdyBcIkludmFsaWQgYXJndW1lbnRzXCI7XG5cdFx0fVxuXG5cdFx0dmFsdWUgPSAoIHR5cGVvZiB2YWx1ZSA9PT0gXCJvYmplY3RcIiA/IHZhbHVlLnRvU3RyaW5nKCkgOiB2YWx1ZSArIFwiXCIgKTtcblx0XHRpZiAoIHZhbHVlID09PSBcIlwiICkge1xuXHRcdFx0cmV0dXJuIG51bGw7XG5cdFx0fVxuXG5cdFx0dmFyIGlGb3JtYXQsIGRpbSwgZXh0cmEsXG5cdFx0XHRpVmFsdWUgPSAwLFxuXHRcdFx0c2hvcnRZZWFyQ3V0b2ZmVGVtcCA9ICggc2V0dGluZ3MgPyBzZXR0aW5ncy5zaG9ydFllYXJDdXRvZmYgOiBudWxsICkgfHwgdGhpcy5fZGVmYXVsdHMuc2hvcnRZZWFyQ3V0b2ZmLFxuXHRcdFx0c2hvcnRZZWFyQ3V0b2ZmID0gKCB0eXBlb2Ygc2hvcnRZZWFyQ3V0b2ZmVGVtcCAhPT0gXCJzdHJpbmdcIiA/IHNob3J0WWVhckN1dG9mZlRlbXAgOlxuXHRcdFx0XHRuZXcgRGF0ZSgpLmdldEZ1bGxZZWFyKCkgJSAxMDAgKyBwYXJzZUludCggc2hvcnRZZWFyQ3V0b2ZmVGVtcCwgMTAgKSApLFxuXHRcdFx0ZGF5TmFtZXNTaG9ydCA9ICggc2V0dGluZ3MgPyBzZXR0aW5ncy5kYXlOYW1lc1Nob3J0IDogbnVsbCApIHx8IHRoaXMuX2RlZmF1bHRzLmRheU5hbWVzU2hvcnQsXG5cdFx0XHRkYXlOYW1lcyA9ICggc2V0dGluZ3MgPyBzZXR0aW5ncy5kYXlOYW1lcyA6IG51bGwgKSB8fCB0aGlzLl9kZWZhdWx0cy5kYXlOYW1lcyxcblx0XHRcdG1vbnRoTmFtZXNTaG9ydCA9ICggc2V0dGluZ3MgPyBzZXR0aW5ncy5tb250aE5hbWVzU2hvcnQgOiBudWxsICkgfHwgdGhpcy5fZGVmYXVsdHMubW9udGhOYW1lc1Nob3J0LFxuXHRcdFx0bW9udGhOYW1lcyA9ICggc2V0dGluZ3MgPyBzZXR0aW5ncy5tb250aE5hbWVzIDogbnVsbCApIHx8IHRoaXMuX2RlZmF1bHRzLm1vbnRoTmFtZXMsXG5cdFx0XHR5ZWFyID0gLTEsXG5cdFx0XHRtb250aCA9IC0xLFxuXHRcdFx0ZGF5ID0gLTEsXG5cdFx0XHRkb3kgPSAtMSxcblx0XHRcdGxpdGVyYWwgPSBmYWxzZSxcblx0XHRcdGRhdGUsXG5cblx0XHRcdC8vIENoZWNrIHdoZXRoZXIgYSBmb3JtYXQgY2hhcmFjdGVyIGlzIGRvdWJsZWRcblx0XHRcdGxvb2tBaGVhZCA9IGZ1bmN0aW9uKCBtYXRjaCApIHtcblx0XHRcdFx0dmFyIG1hdGNoZXMgPSAoIGlGb3JtYXQgKyAxIDwgZm9ybWF0Lmxlbmd0aCAmJiBmb3JtYXQuY2hhckF0KCBpRm9ybWF0ICsgMSApID09PSBtYXRjaCApO1xuXHRcdFx0XHRpZiAoIG1hdGNoZXMgKSB7XG5cdFx0XHRcdFx0aUZvcm1hdCsrO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHJldHVybiBtYXRjaGVzO1xuXHRcdFx0fSxcblxuXHRcdFx0Ly8gRXh0cmFjdCBhIG51bWJlciBmcm9tIHRoZSBzdHJpbmcgdmFsdWVcblx0XHRcdGdldE51bWJlciA9IGZ1bmN0aW9uKCBtYXRjaCApIHtcblx0XHRcdFx0dmFyIGlzRG91YmxlZCA9IGxvb2tBaGVhZCggbWF0Y2ggKSxcblx0XHRcdFx0XHRzaXplID0gKCBtYXRjaCA9PT0gXCJAXCIgPyAxNCA6ICggbWF0Y2ggPT09IFwiIVwiID8gMjAgOlxuXHRcdFx0XHRcdCggbWF0Y2ggPT09IFwieVwiICYmIGlzRG91YmxlZCA/IDQgOiAoIG1hdGNoID09PSBcIm9cIiA/IDMgOiAyICkgKSApICksXG5cdFx0XHRcdFx0bWluU2l6ZSA9ICggbWF0Y2ggPT09IFwieVwiID8gc2l6ZSA6IDEgKSxcblx0XHRcdFx0XHRkaWdpdHMgPSBuZXcgUmVnRXhwKCBcIl5cXFxcZHtcIiArIG1pblNpemUgKyBcIixcIiArIHNpemUgKyBcIn1cIiApLFxuXHRcdFx0XHRcdG51bSA9IHZhbHVlLnN1YnN0cmluZyggaVZhbHVlICkubWF0Y2goIGRpZ2l0cyApO1xuXHRcdFx0XHRpZiAoICFudW0gKSB7XG5cdFx0XHRcdFx0dGhyb3cgXCJNaXNzaW5nIG51bWJlciBhdCBwb3NpdGlvbiBcIiArIGlWYWx1ZTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpVmFsdWUgKz0gbnVtWyAwIF0ubGVuZ3RoO1xuXHRcdFx0XHRyZXR1cm4gcGFyc2VJbnQoIG51bVsgMCBdLCAxMCApO1xuXHRcdFx0fSxcblxuXHRcdFx0Ly8gRXh0cmFjdCBhIG5hbWUgZnJvbSB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjb252ZXJ0IHRvIGFuIGluZGV4XG5cdFx0XHRnZXROYW1lID0gZnVuY3Rpb24oIG1hdGNoLCBzaG9ydE5hbWVzLCBsb25nTmFtZXMgKSB7XG5cdFx0XHRcdHZhciBpbmRleCA9IC0xLFxuXHRcdFx0XHRcdG5hbWVzID0gJC5tYXAoIGxvb2tBaGVhZCggbWF0Y2ggKSA/IGxvbmdOYW1lcyA6IHNob3J0TmFtZXMsIGZ1bmN0aW9uKCB2LCBrICkge1xuXHRcdFx0XHRcdFx0cmV0dXJuIFsgWyBrLCB2IF0gXTtcblx0XHRcdFx0XHR9ICkuc29ydCggZnVuY3Rpb24oIGEsIGIgKSB7XG5cdFx0XHRcdFx0XHRyZXR1cm4gLSggYVsgMSBdLmxlbmd0aCAtIGJbIDEgXS5sZW5ndGggKTtcblx0XHRcdFx0XHR9ICk7XG5cblx0XHRcdFx0JC5lYWNoKCBuYW1lcywgZnVuY3Rpb24oIGksIHBhaXIgKSB7XG5cdFx0XHRcdFx0dmFyIG5hbWUgPSBwYWlyWyAxIF07XG5cdFx0XHRcdFx0aWYgKCB2YWx1ZS5zdWJzdHIoIGlWYWx1ZSwgbmFtZS5sZW5ndGggKS50b0xvd2VyQ2FzZSgpID09PSBuYW1lLnRvTG93ZXJDYXNlKCkgKSB7XG5cdFx0XHRcdFx0XHRpbmRleCA9IHBhaXJbIDAgXTtcblx0XHRcdFx0XHRcdGlWYWx1ZSArPSBuYW1lLmxlbmd0aDtcblx0XHRcdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gKTtcblx0XHRcdFx0aWYgKCBpbmRleCAhPT0gLTEgKSB7XG5cdFx0XHRcdFx0cmV0dXJuIGluZGV4ICsgMTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHR0aHJvdyBcIlVua25vd24gbmFtZSBhdCBwb3NpdGlvbiBcIiArIGlWYWx1ZTtcblx0XHRcdFx0fVxuXHRcdFx0fSxcblxuXHRcdFx0Ly8gQ29uZmlybSB0aGF0IGEgbGl0ZXJhbCBjaGFyYWN0ZXIgbWF0Y2hlcyB0aGUgc3RyaW5nIHZhbHVlXG5cdFx0XHRjaGVja0xpdGVyYWwgPSBmdW5jdGlvbigpIHtcblx0XHRcdFx0aWYgKCB2YWx1ZS5jaGFyQXQoIGlWYWx1ZSApICE9PSBmb3JtYXQuY2hhckF0KCBpRm9ybWF0ICkgKSB7XG5cdFx0XHRcdFx0dGhyb3cgXCJVbmV4cGVjdGVkIGxpdGVyYWwgYXQgcG9zaXRpb24gXCIgKyBpVmFsdWU7XG5cdFx0XHRcdH1cblx0XHRcdFx0aVZhbHVlKys7XG5cdFx0XHR9O1xuXG5cdFx0Zm9yICggaUZvcm1hdCA9IDA7IGlGb3JtYXQgPCBmb3JtYXQubGVuZ3RoOyBpRm9ybWF0KysgKSB7XG5cdFx0XHRpZiAoIGxpdGVyYWwgKSB7XG5cdFx0XHRcdGlmICggZm9ybWF0LmNoYXJBdCggaUZvcm1hdCApID09PSBcIidcIiAmJiAhbG9va0FoZWFkKCBcIidcIiApICkge1xuXHRcdFx0XHRcdGxpdGVyYWwgPSBmYWxzZTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRjaGVja0xpdGVyYWwoKTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0c3dpdGNoICggZm9ybWF0LmNoYXJBdCggaUZvcm1hdCApICkge1xuXHRcdFx0XHRcdGNhc2UgXCJkXCI6XG5cdFx0XHRcdFx0XHRkYXkgPSBnZXROdW1iZXIoIFwiZFwiICk7XG5cdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRjYXNlIFwiRFwiOlxuXHRcdFx0XHRcdFx0Z2V0TmFtZSggXCJEXCIsIGRheU5hbWVzU2hvcnQsIGRheU5hbWVzICk7XG5cdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRjYXNlIFwib1wiOlxuXHRcdFx0XHRcdFx0ZG95ID0gZ2V0TnVtYmVyKCBcIm9cIiApO1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0Y2FzZSBcIm1cIjpcblx0XHRcdFx0XHRcdG1vbnRoID0gZ2V0TnVtYmVyKCBcIm1cIiApO1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0Y2FzZSBcIk1cIjpcblx0XHRcdFx0XHRcdG1vbnRoID0gZ2V0TmFtZSggXCJNXCIsIG1vbnRoTmFtZXNTaG9ydCwgbW9udGhOYW1lcyApO1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0Y2FzZSBcInlcIjpcblx0XHRcdFx0XHRcdHllYXIgPSBnZXROdW1iZXIoIFwieVwiICk7XG5cdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRjYXNlIFwiQFwiOlxuXHRcdFx0XHRcdFx0ZGF0ZSA9IG5ldyBEYXRlKCBnZXROdW1iZXIoIFwiQFwiICkgKTtcblx0XHRcdFx0XHRcdHllYXIgPSBkYXRlLmdldEZ1bGxZZWFyKCk7XG5cdFx0XHRcdFx0XHRtb250aCA9IGRhdGUuZ2V0TW9udGgoKSArIDE7XG5cdFx0XHRcdFx0XHRkYXkgPSBkYXRlLmdldERhdGUoKTtcblx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdGNhc2UgXCIhXCI6XG5cdFx0XHRcdFx0XHRkYXRlID0gbmV3IERhdGUoICggZ2V0TnVtYmVyKCBcIiFcIiApIC0gdGhpcy5fdGlja3NUbzE5NzAgKSAvIDEwMDAwICk7XG5cdFx0XHRcdFx0XHR5ZWFyID0gZGF0ZS5nZXRGdWxsWWVhcigpO1xuXHRcdFx0XHRcdFx0bW9udGggPSBkYXRlLmdldE1vbnRoKCkgKyAxO1xuXHRcdFx0XHRcdFx0ZGF5ID0gZGF0ZS5nZXREYXRlKCk7XG5cdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRjYXNlIFwiJ1wiOlxuXHRcdFx0XHRcdFx0aWYgKCBsb29rQWhlYWQoIFwiJ1wiICkgKSB7XG5cdFx0XHRcdFx0XHRcdGNoZWNrTGl0ZXJhbCgpO1xuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0bGl0ZXJhbCA9IHRydWU7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRkZWZhdWx0OlxuXHRcdFx0XHRcdFx0Y2hlY2tMaXRlcmFsKCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoIGlWYWx1ZSA8IHZhbHVlLmxlbmd0aCApIHtcblx0XHRcdGV4dHJhID0gdmFsdWUuc3Vic3RyKCBpVmFsdWUgKTtcblx0XHRcdGlmICggIS9eXFxzKy8udGVzdCggZXh0cmEgKSApIHtcblx0XHRcdFx0dGhyb3cgXCJFeHRyYS91bnBhcnNlZCBjaGFyYWN0ZXJzIGZvdW5kIGluIGRhdGU6IFwiICsgZXh0cmE7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKCB5ZWFyID09PSAtMSApIHtcblx0XHRcdHllYXIgPSBuZXcgRGF0ZSgpLmdldEZ1bGxZZWFyKCk7XG5cdFx0fSBlbHNlIGlmICggeWVhciA8IDEwMCApIHtcblx0XHRcdHllYXIgKz0gbmV3IERhdGUoKS5nZXRGdWxsWWVhcigpIC0gbmV3IERhdGUoKS5nZXRGdWxsWWVhcigpICUgMTAwICtcblx0XHRcdFx0KCB5ZWFyIDw9IHNob3J0WWVhckN1dG9mZiA/IDAgOiAtMTAwICk7XG5cdFx0fVxuXG5cdFx0aWYgKCBkb3kgPiAtMSApIHtcblx0XHRcdG1vbnRoID0gMTtcblx0XHRcdGRheSA9IGRveTtcblx0XHRcdGRvIHtcblx0XHRcdFx0ZGltID0gdGhpcy5fZ2V0RGF5c0luTW9udGgoIHllYXIsIG1vbnRoIC0gMSApO1xuXHRcdFx0XHRpZiAoIGRheSA8PSBkaW0gKSB7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdH1cblx0XHRcdFx0bW9udGgrKztcblx0XHRcdFx0ZGF5IC09IGRpbTtcblx0XHRcdH0gd2hpbGUgKCB0cnVlICk7XG5cdFx0fVxuXG5cdFx0ZGF0ZSA9IHRoaXMuX2RheWxpZ2h0U2F2aW5nQWRqdXN0KCBuZXcgRGF0ZSggeWVhciwgbW9udGggLSAxLCBkYXkgKSApO1xuXHRcdGlmICggZGF0ZS5nZXRGdWxsWWVhcigpICE9PSB5ZWFyIHx8IGRhdGUuZ2V0TW9udGgoKSArIDEgIT09IG1vbnRoIHx8IGRhdGUuZ2V0RGF0ZSgpICE9PSBkYXkgKSB7XG5cdFx0XHR0aHJvdyBcIkludmFsaWQgZGF0ZVwiOyAvLyBFLmcuIDMxLzAyLzAwXG5cdFx0fVxuXHRcdHJldHVybiBkYXRlO1xuXHR9LFxuXG5cdC8qIFN0YW5kYXJkIGRhdGUgZm9ybWF0cy4gKi9cblx0QVRPTTogXCJ5eS1tbS1kZFwiLCAvLyBSRkMgMzMzOSAoSVNPIDg2MDEpXG5cdENPT0tJRTogXCJELCBkZCBNIHl5XCIsXG5cdElTT184NjAxOiBcInl5LW1tLWRkXCIsXG5cdFJGQ184MjI6IFwiRCwgZCBNIHlcIixcblx0UkZDXzg1MDogXCJERCwgZGQtTS15XCIsXG5cdFJGQ18xMDM2OiBcIkQsIGQgTSB5XCIsXG5cdFJGQ18xMTIzOiBcIkQsIGQgTSB5eVwiLFxuXHRSRkNfMjgyMjogXCJELCBkIE0geXlcIixcblx0UlNTOiBcIkQsIGQgTSB5XCIsIC8vIFJGQyA4MjJcblx0VElDS1M6IFwiIVwiLFxuXHRUSU1FU1RBTVA6IFwiQFwiLFxuXHRXM0M6IFwieXktbW0tZGRcIiwgLy8gSVNPIDg2MDFcblxuXHRfdGlja3NUbzE5NzA6ICggKCAoIDE5NzAgLSAxICkgKiAzNjUgKyBNYXRoLmZsb29yKCAxOTcwIC8gNCApIC0gTWF0aC5mbG9vciggMTk3MCAvIDEwMCApICtcblx0XHRNYXRoLmZsb29yKCAxOTcwIC8gNDAwICkgKSAqIDI0ICogNjAgKiA2MCAqIDEwMDAwMDAwICksXG5cblx0LyogRm9ybWF0IGEgZGF0ZSBvYmplY3QgaW50byBhIHN0cmluZyB2YWx1ZS5cblx0ICogVGhlIGZvcm1hdCBjYW4gYmUgY29tYmluYXRpb25zIG9mIHRoZSBmb2xsb3dpbmc6XG5cdCAqIGQgIC0gZGF5IG9mIG1vbnRoIChubyBsZWFkaW5nIHplcm8pXG5cdCAqIGRkIC0gZGF5IG9mIG1vbnRoICh0d28gZGlnaXQpXG5cdCAqIG8gIC0gZGF5IG9mIHllYXIgKG5vIGxlYWRpbmcgemVyb3MpXG5cdCAqIG9vIC0gZGF5IG9mIHllYXIgKHRocmVlIGRpZ2l0KVxuXHQgKiBEICAtIGRheSBuYW1lIHNob3J0XG5cdCAqIEREIC0gZGF5IG5hbWUgbG9uZ1xuXHQgKiBtICAtIG1vbnRoIG9mIHllYXIgKG5vIGxlYWRpbmcgemVybylcblx0ICogbW0gLSBtb250aCBvZiB5ZWFyICh0d28gZGlnaXQpXG5cdCAqIE0gIC0gbW9udGggbmFtZSBzaG9ydFxuXHQgKiBNTSAtIG1vbnRoIG5hbWUgbG9uZ1xuXHQgKiB5ICAtIHllYXIgKHR3byBkaWdpdClcblx0ICogeXkgLSB5ZWFyIChmb3VyIGRpZ2l0KVxuXHQgKiBAIC0gVW5peCB0aW1lc3RhbXAgKG1zIHNpbmNlIDAxLzAxLzE5NzApXG5cdCAqICEgLSBXaW5kb3dzIHRpY2tzICgxMDBucyBzaW5jZSAwMS8wMS8wMDAxKVxuXHQgKiBcIi4uLlwiIC0gbGl0ZXJhbCB0ZXh0XG5cdCAqICcnIC0gc2luZ2xlIHF1b3RlXG5cdCAqXG5cdCAqIEBwYXJhbSAgZm9ybWF0IHN0cmluZyAtIHRoZSBkZXNpcmVkIGZvcm1hdCBvZiB0aGUgZGF0ZVxuXHQgKiBAcGFyYW0gIGRhdGUgRGF0ZSAtIHRoZSBkYXRlIHZhbHVlIHRvIGZvcm1hdFxuXHQgKiBAcGFyYW0gIHNldHRpbmdzIE9iamVjdCAtIGF0dHJpYnV0ZXMgaW5jbHVkZTpcblx0ICpcdFx0XHRcdFx0ZGF5TmFtZXNTaG9ydFx0c3RyaW5nWzddIC0gYWJicmV2aWF0ZWQgbmFtZXMgb2YgdGhlIGRheXMgZnJvbSBTdW5kYXkgKG9wdGlvbmFsKVxuXHQgKlx0XHRcdFx0XHRkYXlOYW1lc1x0XHRzdHJpbmdbN10gLSBuYW1lcyBvZiB0aGUgZGF5cyBmcm9tIFN1bmRheSAob3B0aW9uYWwpXG5cdCAqXHRcdFx0XHRcdG1vbnRoTmFtZXNTaG9ydCBzdHJpbmdbMTJdIC0gYWJicmV2aWF0ZWQgbmFtZXMgb2YgdGhlIG1vbnRocyAob3B0aW9uYWwpXG5cdCAqXHRcdFx0XHRcdG1vbnRoTmFtZXNcdFx0c3RyaW5nWzEyXSAtIG5hbWVzIG9mIHRoZSBtb250aHMgKG9wdGlvbmFsKVxuXHQgKiBAcmV0dXJuICBzdHJpbmcgLSB0aGUgZGF0ZSBpbiB0aGUgYWJvdmUgZm9ybWF0XG5cdCAqL1xuXHRmb3JtYXREYXRlOiBmdW5jdGlvbiggZm9ybWF0LCBkYXRlLCBzZXR0aW5ncyApIHtcblx0XHRpZiAoICFkYXRlICkge1xuXHRcdFx0cmV0dXJuIFwiXCI7XG5cdFx0fVxuXG5cdFx0dmFyIGlGb3JtYXQsXG5cdFx0XHRkYXlOYW1lc1Nob3J0ID0gKCBzZXR0aW5ncyA/IHNldHRpbmdzLmRheU5hbWVzU2hvcnQgOiBudWxsICkgfHwgdGhpcy5fZGVmYXVsdHMuZGF5TmFtZXNTaG9ydCxcblx0XHRcdGRheU5hbWVzID0gKCBzZXR0aW5ncyA/IHNldHRpbmdzLmRheU5hbWVzIDogbnVsbCApIHx8IHRoaXMuX2RlZmF1bHRzLmRheU5hbWVzLFxuXHRcdFx0bW9udGhOYW1lc1Nob3J0ID0gKCBzZXR0aW5ncyA/IHNldHRpbmdzLm1vbnRoTmFtZXNTaG9ydCA6IG51bGwgKSB8fCB0aGlzLl9kZWZhdWx0cy5tb250aE5hbWVzU2hvcnQsXG5cdFx0XHRtb250aE5hbWVzID0gKCBzZXR0aW5ncyA/IHNldHRpbmdzLm1vbnRoTmFtZXMgOiBudWxsICkgfHwgdGhpcy5fZGVmYXVsdHMubW9udGhOYW1lcyxcblxuXHRcdFx0Ly8gQ2hlY2sgd2hldGhlciBhIGZvcm1hdCBjaGFyYWN0ZXIgaXMgZG91YmxlZFxuXHRcdFx0bG9va0FoZWFkID0gZnVuY3Rpb24oIG1hdGNoICkge1xuXHRcdFx0XHR2YXIgbWF0Y2hlcyA9ICggaUZvcm1hdCArIDEgPCBmb3JtYXQubGVuZ3RoICYmIGZvcm1hdC5jaGFyQXQoIGlGb3JtYXQgKyAxICkgPT09IG1hdGNoICk7XG5cdFx0XHRcdGlmICggbWF0Y2hlcyApIHtcblx0XHRcdFx0XHRpRm9ybWF0Kys7XG5cdFx0XHRcdH1cblx0XHRcdFx0cmV0dXJuIG1hdGNoZXM7XG5cdFx0XHR9LFxuXG5cdFx0XHQvLyBGb3JtYXQgYSBudW1iZXIsIHdpdGggbGVhZGluZyB6ZXJvIGlmIG5lY2Vzc2FyeVxuXHRcdFx0Zm9ybWF0TnVtYmVyID0gZnVuY3Rpb24oIG1hdGNoLCB2YWx1ZSwgbGVuICkge1xuXHRcdFx0XHR2YXIgbnVtID0gXCJcIiArIHZhbHVlO1xuXHRcdFx0XHRpZiAoIGxvb2tBaGVhZCggbWF0Y2ggKSApIHtcblx0XHRcdFx0XHR3aGlsZSAoIG51bS5sZW5ndGggPCBsZW4gKSB7XG5cdFx0XHRcdFx0XHRudW0gPSBcIjBcIiArIG51bTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdFx0cmV0dXJuIG51bTtcblx0XHRcdH0sXG5cblx0XHRcdC8vIEZvcm1hdCBhIG5hbWUsIHNob3J0IG9yIGxvbmcgYXMgcmVxdWVzdGVkXG5cdFx0XHRmb3JtYXROYW1lID0gZnVuY3Rpb24oIG1hdGNoLCB2YWx1ZSwgc2hvcnROYW1lcywgbG9uZ05hbWVzICkge1xuXHRcdFx0XHRyZXR1cm4gKCBsb29rQWhlYWQoIG1hdGNoICkgPyBsb25nTmFtZXNbIHZhbHVlIF0gOiBzaG9ydE5hbWVzWyB2YWx1ZSBdICk7XG5cdFx0XHR9LFxuXHRcdFx0b3V0cHV0ID0gXCJcIixcblx0XHRcdGxpdGVyYWwgPSBmYWxzZTtcblxuXHRcdGlmICggZGF0ZSApIHtcblx0XHRcdGZvciAoIGlGb3JtYXQgPSAwOyBpRm9ybWF0IDwgZm9ybWF0Lmxlbmd0aDsgaUZvcm1hdCsrICkge1xuXHRcdFx0XHRpZiAoIGxpdGVyYWwgKSB7XG5cdFx0XHRcdFx0aWYgKCBmb3JtYXQuY2hhckF0KCBpRm9ybWF0ICkgPT09IFwiJ1wiICYmICFsb29rQWhlYWQoIFwiJ1wiICkgKSB7XG5cdFx0XHRcdFx0XHRsaXRlcmFsID0gZmFsc2U7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdG91dHB1dCArPSBmb3JtYXQuY2hhckF0KCBpRm9ybWF0ICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHN3aXRjaCAoIGZvcm1hdC5jaGFyQXQoIGlGb3JtYXQgKSApIHtcblx0XHRcdFx0XHRcdGNhc2UgXCJkXCI6XG5cdFx0XHRcdFx0XHRcdG91dHB1dCArPSBmb3JtYXROdW1iZXIoIFwiZFwiLCBkYXRlLmdldERhdGUoKSwgMiApO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdGNhc2UgXCJEXCI6XG5cdFx0XHRcdFx0XHRcdG91dHB1dCArPSBmb3JtYXROYW1lKCBcIkRcIiwgZGF0ZS5nZXREYXkoKSwgZGF5TmFtZXNTaG9ydCwgZGF5TmFtZXMgKTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlIFwib1wiOlxuXHRcdFx0XHRcdFx0XHRvdXRwdXQgKz0gZm9ybWF0TnVtYmVyKCBcIm9cIixcblx0XHRcdFx0XHRcdFx0XHRNYXRoLnJvdW5kKCAoIG5ldyBEYXRlKCBkYXRlLmdldEZ1bGxZZWFyKCksIGRhdGUuZ2V0TW9udGgoKSwgZGF0ZS5nZXREYXRlKCkgKS5nZXRUaW1lKCkgLSBuZXcgRGF0ZSggZGF0ZS5nZXRGdWxsWWVhcigpLCAwLCAwICkuZ2V0VGltZSgpICkgLyA4NjQwMDAwMCApLCAzICk7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSBcIm1cIjpcblx0XHRcdFx0XHRcdFx0b3V0cHV0ICs9IGZvcm1hdE51bWJlciggXCJtXCIsIGRhdGUuZ2V0TW9udGgoKSArIDEsIDIgKTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlIFwiTVwiOlxuXHRcdFx0XHRcdFx0XHRvdXRwdXQgKz0gZm9ybWF0TmFtZSggXCJNXCIsIGRhdGUuZ2V0TW9udGgoKSwgbW9udGhOYW1lc1Nob3J0LCBtb250aE5hbWVzICk7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSBcInlcIjpcblx0XHRcdFx0XHRcdFx0b3V0cHV0ICs9ICggbG9va0FoZWFkKCBcInlcIiApID8gZGF0ZS5nZXRGdWxsWWVhcigpIDpcblx0XHRcdFx0XHRcdFx0XHQoIGRhdGUuZ2V0RnVsbFllYXIoKSAlIDEwMCA8IDEwID8gXCIwXCIgOiBcIlwiICkgKyBkYXRlLmdldEZ1bGxZZWFyKCkgJSAxMDAgKTtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlIFwiQFwiOlxuXHRcdFx0XHRcdFx0XHRvdXRwdXQgKz0gZGF0ZS5nZXRUaW1lKCk7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSBcIiFcIjpcblx0XHRcdFx0XHRcdFx0b3V0cHV0ICs9IGRhdGUuZ2V0VGltZSgpICogMTAwMDAgKyB0aGlzLl90aWNrc1RvMTk3MDtcblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRjYXNlIFwiJ1wiOlxuXHRcdFx0XHRcdFx0XHRpZiAoIGxvb2tBaGVhZCggXCInXCIgKSApIHtcblx0XHRcdFx0XHRcdFx0XHRvdXRwdXQgKz0gXCInXCI7XG5cdFx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdFx0bGl0ZXJhbCA9IHRydWU7XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRkZWZhdWx0OlxuXHRcdFx0XHRcdFx0XHRvdXRwdXQgKz0gZm9ybWF0LmNoYXJBdCggaUZvcm1hdCApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXR1cm4gb3V0cHV0O1xuXHR9LFxuXG5cdC8qIEV4dHJhY3QgYWxsIHBvc3NpYmxlIGNoYXJhY3RlcnMgZnJvbSB0aGUgZGF0ZSBmb3JtYXQuICovXG5cdF9wb3NzaWJsZUNoYXJzOiBmdW5jdGlvbiggZm9ybWF0ICkge1xuXHRcdHZhciBpRm9ybWF0LFxuXHRcdFx0Y2hhcnMgPSBcIlwiLFxuXHRcdFx0bGl0ZXJhbCA9IGZhbHNlLFxuXG5cdFx0XHQvLyBDaGVjayB3aGV0aGVyIGEgZm9ybWF0IGNoYXJhY3RlciBpcyBkb3VibGVkXG5cdFx0XHRsb29rQWhlYWQgPSBmdW5jdGlvbiggbWF0Y2ggKSB7XG5cdFx0XHRcdHZhciBtYXRjaGVzID0gKCBpRm9ybWF0ICsgMSA8IGZvcm1hdC5sZW5ndGggJiYgZm9ybWF0LmNoYXJBdCggaUZvcm1hdCArIDEgKSA9PT0gbWF0Y2ggKTtcblx0XHRcdFx0aWYgKCBtYXRjaGVzICkge1xuXHRcdFx0XHRcdGlGb3JtYXQrKztcblx0XHRcdFx0fVxuXHRcdFx0XHRyZXR1cm4gbWF0Y2hlcztcblx0XHRcdH07XG5cblx0XHRmb3IgKCBpRm9ybWF0ID0gMDsgaUZvcm1hdCA8IGZvcm1hdC5sZW5ndGg7IGlGb3JtYXQrKyApIHtcblx0XHRcdGlmICggbGl0ZXJhbCApIHtcblx0XHRcdFx0aWYgKCBmb3JtYXQuY2hhckF0KCBpRm9ybWF0ICkgPT09IFwiJ1wiICYmICFsb29rQWhlYWQoIFwiJ1wiICkgKSB7XG5cdFx0XHRcdFx0bGl0ZXJhbCA9IGZhbHNlO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGNoYXJzICs9IGZvcm1hdC5jaGFyQXQoIGlGb3JtYXQgKTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0c3dpdGNoICggZm9ybWF0LmNoYXJBdCggaUZvcm1hdCApICkge1xuXHRcdFx0XHRcdGNhc2UgXCJkXCI6IGNhc2UgXCJtXCI6IGNhc2UgXCJ5XCI6IGNhc2UgXCJAXCI6XG5cdFx0XHRcdFx0XHRjaGFycyArPSBcIjAxMjM0NTY3ODlcIjtcblx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdGNhc2UgXCJEXCI6IGNhc2UgXCJNXCI6XG5cdFx0XHRcdFx0XHRyZXR1cm4gbnVsbDsgLy8gQWNjZXB0IGFueXRoaW5nXG5cdFx0XHRcdFx0Y2FzZSBcIidcIjpcblx0XHRcdFx0XHRcdGlmICggbG9va0FoZWFkKCBcIidcIiApICkge1xuXHRcdFx0XHRcdFx0XHRjaGFycyArPSBcIidcIjtcblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdGxpdGVyYWwgPSB0cnVlO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0ZGVmYXVsdDpcblx0XHRcdFx0XHRcdGNoYXJzICs9IGZvcm1hdC5jaGFyQXQoIGlGb3JtYXQgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXR1cm4gY2hhcnM7XG5cdH0sXG5cblx0LyogR2V0IGEgc2V0dGluZyB2YWx1ZSwgZGVmYXVsdGluZyBpZiBuZWNlc3NhcnkuICovXG5cdF9nZXQ6IGZ1bmN0aW9uKCBpbnN0LCBuYW1lICkge1xuXHRcdHJldHVybiBpbnN0LnNldHRpbmdzWyBuYW1lIF0gIT09IHVuZGVmaW5lZCA/XG5cdFx0XHRpbnN0LnNldHRpbmdzWyBuYW1lIF0gOiB0aGlzLl9kZWZhdWx0c1sgbmFtZSBdO1xuXHR9LFxuXG5cdC8qIFBhcnNlIGV4aXN0aW5nIGRhdGUgYW5kIGluaXRpYWxpc2UgZGF0ZSBwaWNrZXIuICovXG5cdF9zZXREYXRlRnJvbUZpZWxkOiBmdW5jdGlvbiggaW5zdCwgbm9EZWZhdWx0ICkge1xuXHRcdGlmICggaW5zdC5pbnB1dC52YWwoKSA9PT0gaW5zdC5sYXN0VmFsICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBkYXRlRm9ybWF0ID0gdGhpcy5fZ2V0KCBpbnN0LCBcImRhdGVGb3JtYXRcIiApLFxuXHRcdFx0ZGF0ZXMgPSBpbnN0Lmxhc3RWYWwgPSBpbnN0LmlucHV0ID8gaW5zdC5pbnB1dC52YWwoKSA6IG51bGwsXG5cdFx0XHRkZWZhdWx0RGF0ZSA9IHRoaXMuX2dldERlZmF1bHREYXRlKCBpbnN0ICksXG5cdFx0XHRkYXRlID0gZGVmYXVsdERhdGUsXG5cdFx0XHRzZXR0aW5ncyA9IHRoaXMuX2dldEZvcm1hdENvbmZpZyggaW5zdCApO1xuXG5cdFx0dHJ5IHtcblx0XHRcdGRhdGUgPSB0aGlzLnBhcnNlRGF0ZSggZGF0ZUZvcm1hdCwgZGF0ZXMsIHNldHRpbmdzICkgfHwgZGVmYXVsdERhdGU7XG5cdFx0fSBjYXRjaCAoIGV2ZW50ICkge1xuXHRcdFx0ZGF0ZXMgPSAoIG5vRGVmYXVsdCA/IFwiXCIgOiBkYXRlcyApO1xuXHRcdH1cblx0XHRpbnN0LnNlbGVjdGVkRGF5ID0gZGF0ZS5nZXREYXRlKCk7XG5cdFx0aW5zdC5kcmF3TW9udGggPSBpbnN0LnNlbGVjdGVkTW9udGggPSBkYXRlLmdldE1vbnRoKCk7XG5cdFx0aW5zdC5kcmF3WWVhciA9IGluc3Quc2VsZWN0ZWRZZWFyID0gZGF0ZS5nZXRGdWxsWWVhcigpO1xuXHRcdGluc3QuY3VycmVudERheSA9ICggZGF0ZXMgPyBkYXRlLmdldERhdGUoKSA6IDAgKTtcblx0XHRpbnN0LmN1cnJlbnRNb250aCA9ICggZGF0ZXMgPyBkYXRlLmdldE1vbnRoKCkgOiAwICk7XG5cdFx0aW5zdC5jdXJyZW50WWVhciA9ICggZGF0ZXMgPyBkYXRlLmdldEZ1bGxZZWFyKCkgOiAwICk7XG5cdFx0dGhpcy5fYWRqdXN0SW5zdERhdGUoIGluc3QgKTtcblx0fSxcblxuXHQvKiBSZXRyaWV2ZSB0aGUgZGVmYXVsdCBkYXRlIHNob3duIG9uIG9wZW5pbmcuICovXG5cdF9nZXREZWZhdWx0RGF0ZTogZnVuY3Rpb24oIGluc3QgKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3Jlc3RyaWN0TWluTWF4KCBpbnN0LFxuXHRcdFx0dGhpcy5fZGV0ZXJtaW5lRGF0ZSggaW5zdCwgdGhpcy5fZ2V0KCBpbnN0LCBcImRlZmF1bHREYXRlXCIgKSwgbmV3IERhdGUoKSApICk7XG5cdH0sXG5cblx0LyogQSBkYXRlIG1heSBiZSBzcGVjaWZpZWQgYXMgYW4gZXhhY3QgdmFsdWUgb3IgYSByZWxhdGl2ZSBvbmUuICovXG5cdF9kZXRlcm1pbmVEYXRlOiBmdW5jdGlvbiggaW5zdCwgZGF0ZSwgZGVmYXVsdERhdGUgKSB7XG5cdFx0dmFyIG9mZnNldE51bWVyaWMgPSBmdW5jdGlvbiggb2Zmc2V0ICkge1xuXHRcdFx0XHR2YXIgZGF0ZSA9IG5ldyBEYXRlKCk7XG5cdFx0XHRcdGRhdGUuc2V0RGF0ZSggZGF0ZS5nZXREYXRlKCkgKyBvZmZzZXQgKTtcblx0XHRcdFx0cmV0dXJuIGRhdGU7XG5cdFx0XHR9LFxuXHRcdFx0b2Zmc2V0U3RyaW5nID0gZnVuY3Rpb24oIG9mZnNldCApIHtcblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRyZXR1cm4gJC5kYXRlcGlja2VyLnBhcnNlRGF0ZSggJC5kYXRlcGlja2VyLl9nZXQoIGluc3QsIFwiZGF0ZUZvcm1hdFwiICksXG5cdFx0XHRcdFx0XHRvZmZzZXQsICQuZGF0ZXBpY2tlci5fZ2V0Rm9ybWF0Q29uZmlnKCBpbnN0ICkgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRjYXRjaCAoIGUgKSB7XG5cblx0XHRcdFx0XHQvLyBJZ25vcmVcblx0XHRcdFx0fVxuXG5cdFx0XHRcdHZhciBkYXRlID0gKCBvZmZzZXQudG9Mb3dlckNhc2UoKS5tYXRjaCggL15jLyApID9cblx0XHRcdFx0XHQkLmRhdGVwaWNrZXIuX2dldERhdGUoIGluc3QgKSA6IG51bGwgKSB8fCBuZXcgRGF0ZSgpLFxuXHRcdFx0XHRcdHllYXIgPSBkYXRlLmdldEZ1bGxZZWFyKCksXG5cdFx0XHRcdFx0bW9udGggPSBkYXRlLmdldE1vbnRoKCksXG5cdFx0XHRcdFx0ZGF5ID0gZGF0ZS5nZXREYXRlKCksXG5cdFx0XHRcdFx0cGF0dGVybiA9IC8oWytcXC1dP1swLTldKylcXHMqKGR8RHx3fFd8bXxNfHl8WSk/L2csXG5cdFx0XHRcdFx0bWF0Y2hlcyA9IHBhdHRlcm4uZXhlYyggb2Zmc2V0ICk7XG5cblx0XHRcdFx0d2hpbGUgKCBtYXRjaGVzICkge1xuXHRcdFx0XHRcdHN3aXRjaCAoIG1hdGNoZXNbIDIgXSB8fCBcImRcIiApIHtcblx0XHRcdFx0XHRcdGNhc2UgXCJkXCIgOiBjYXNlIFwiRFwiIDpcblx0XHRcdFx0XHRcdFx0ZGF5ICs9IHBhcnNlSW50KCBtYXRjaGVzWyAxIF0sIDEwICk7IGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSBcIndcIiA6IGNhc2UgXCJXXCIgOlxuXHRcdFx0XHRcdFx0XHRkYXkgKz0gcGFyc2VJbnQoIG1hdGNoZXNbIDEgXSwgMTAgKSAqIDc7IGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSBcIm1cIiA6IGNhc2UgXCJNXCIgOlxuXHRcdFx0XHRcdFx0XHRtb250aCArPSBwYXJzZUludCggbWF0Y2hlc1sgMSBdLCAxMCApO1xuXHRcdFx0XHRcdFx0XHRkYXkgPSBNYXRoLm1pbiggZGF5LCAkLmRhdGVwaWNrZXIuX2dldERheXNJbk1vbnRoKCB5ZWFyLCBtb250aCApICk7XG5cdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0Y2FzZSBcInlcIjogY2FzZSBcIllcIiA6XG5cdFx0XHRcdFx0XHRcdHllYXIgKz0gcGFyc2VJbnQoIG1hdGNoZXNbIDEgXSwgMTAgKTtcblx0XHRcdFx0XHRcdFx0ZGF5ID0gTWF0aC5taW4oIGRheSwgJC5kYXRlcGlja2VyLl9nZXREYXlzSW5Nb250aCggeWVhciwgbW9udGggKSApO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0bWF0Y2hlcyA9IHBhdHRlcm4uZXhlYyggb2Zmc2V0ICk7XG5cdFx0XHRcdH1cblx0XHRcdFx0cmV0dXJuIG5ldyBEYXRlKCB5ZWFyLCBtb250aCwgZGF5ICk7XG5cdFx0XHR9LFxuXHRcdFx0bmV3RGF0ZSA9ICggZGF0ZSA9PSBudWxsIHx8IGRhdGUgPT09IFwiXCIgPyBkZWZhdWx0RGF0ZSA6ICggdHlwZW9mIGRhdGUgPT09IFwic3RyaW5nXCIgPyBvZmZzZXRTdHJpbmcoIGRhdGUgKSA6XG5cdFx0XHRcdCggdHlwZW9mIGRhdGUgPT09IFwibnVtYmVyXCIgPyAoIGlzTmFOKCBkYXRlICkgPyBkZWZhdWx0RGF0ZSA6IG9mZnNldE51bWVyaWMoIGRhdGUgKSApIDogbmV3IERhdGUoIGRhdGUuZ2V0VGltZSgpICkgKSApICk7XG5cblx0XHRuZXdEYXRlID0gKCBuZXdEYXRlICYmIG5ld0RhdGUudG9TdHJpbmcoKSA9PT0gXCJJbnZhbGlkIERhdGVcIiA/IGRlZmF1bHREYXRlIDogbmV3RGF0ZSApO1xuXHRcdGlmICggbmV3RGF0ZSApIHtcblx0XHRcdG5ld0RhdGUuc2V0SG91cnMoIDAgKTtcblx0XHRcdG5ld0RhdGUuc2V0TWludXRlcyggMCApO1xuXHRcdFx0bmV3RGF0ZS5zZXRTZWNvbmRzKCAwICk7XG5cdFx0XHRuZXdEYXRlLnNldE1pbGxpc2Vjb25kcyggMCApO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcy5fZGF5bGlnaHRTYXZpbmdBZGp1c3QoIG5ld0RhdGUgKTtcblx0fSxcblxuXHQvKiBIYW5kbGUgc3dpdGNoIHRvL2Zyb20gZGF5bGlnaHQgc2F2aW5nLlxuXHQgKiBIb3VycyBtYXkgYmUgbm9uLXplcm8gb24gZGF5bGlnaHQgc2F2aW5nIGN1dC1vdmVyOlxuXHQgKiA+IDEyIHdoZW4gbWlkbmlnaHQgY2hhbmdlb3ZlciwgYnV0IHRoZW4gY2Fubm90IGdlbmVyYXRlXG5cdCAqIG1pZG5pZ2h0IGRhdGV0aW1lLCBzbyBqdW1wIHRvIDFBTSwgb3RoZXJ3aXNlIHJlc2V0LlxuXHQgKiBAcGFyYW0gIGRhdGUgIChEYXRlKSB0aGUgZGF0ZSB0byBjaGVja1xuXHQgKiBAcmV0dXJuICAoRGF0ZSkgdGhlIGNvcnJlY3RlZCBkYXRlXG5cdCAqL1xuXHRfZGF5bGlnaHRTYXZpbmdBZGp1c3Q6IGZ1bmN0aW9uKCBkYXRlICkge1xuXHRcdGlmICggIWRhdGUgKSB7XG5cdFx0XHRyZXR1cm4gbnVsbDtcblx0XHR9XG5cdFx0ZGF0ZS5zZXRIb3VycyggZGF0ZS5nZXRIb3VycygpID4gMTIgPyBkYXRlLmdldEhvdXJzKCkgKyAyIDogMCApO1xuXHRcdHJldHVybiBkYXRlO1xuXHR9LFxuXG5cdC8qIFNldCB0aGUgZGF0ZShzKSBkaXJlY3RseS4gKi9cblx0X3NldERhdGU6IGZ1bmN0aW9uKCBpbnN0LCBkYXRlLCBub0NoYW5nZSApIHtcblx0XHR2YXIgY2xlYXIgPSAhZGF0ZSxcblx0XHRcdG9yaWdNb250aCA9IGluc3Quc2VsZWN0ZWRNb250aCxcblx0XHRcdG9yaWdZZWFyID0gaW5zdC5zZWxlY3RlZFllYXIsXG5cdFx0XHRuZXdEYXRlID0gdGhpcy5fcmVzdHJpY3RNaW5NYXgoIGluc3QsIHRoaXMuX2RldGVybWluZURhdGUoIGluc3QsIGRhdGUsIG5ldyBEYXRlKCkgKSApO1xuXG5cdFx0aW5zdC5zZWxlY3RlZERheSA9IGluc3QuY3VycmVudERheSA9IG5ld0RhdGUuZ2V0RGF0ZSgpO1xuXHRcdGluc3QuZHJhd01vbnRoID0gaW5zdC5zZWxlY3RlZE1vbnRoID0gaW5zdC5jdXJyZW50TW9udGggPSBuZXdEYXRlLmdldE1vbnRoKCk7XG5cdFx0aW5zdC5kcmF3WWVhciA9IGluc3Quc2VsZWN0ZWRZZWFyID0gaW5zdC5jdXJyZW50WWVhciA9IG5ld0RhdGUuZ2V0RnVsbFllYXIoKTtcblx0XHRpZiAoICggb3JpZ01vbnRoICE9PSBpbnN0LnNlbGVjdGVkTW9udGggfHwgb3JpZ1llYXIgIT09IGluc3Quc2VsZWN0ZWRZZWFyICkgJiYgIW5vQ2hhbmdlICkge1xuXHRcdFx0dGhpcy5fbm90aWZ5Q2hhbmdlKCBpbnN0ICk7XG5cdFx0fVxuXHRcdHRoaXMuX2FkanVzdEluc3REYXRlKCBpbnN0ICk7XG5cdFx0aWYgKCBpbnN0LmlucHV0ICkge1xuXHRcdFx0aW5zdC5pbnB1dC52YWwoIGNsZWFyID8gXCJcIiA6IHRoaXMuX2Zvcm1hdERhdGUoIGluc3QgKSApO1xuXHRcdH1cblx0fSxcblxuXHQvKiBSZXRyaWV2ZSB0aGUgZGF0ZShzKSBkaXJlY3RseS4gKi9cblx0X2dldERhdGU6IGZ1bmN0aW9uKCBpbnN0ICkge1xuXHRcdHZhciBzdGFydERhdGUgPSAoICFpbnN0LmN1cnJlbnRZZWFyIHx8ICggaW5zdC5pbnB1dCAmJiBpbnN0LmlucHV0LnZhbCgpID09PSBcIlwiICkgPyBudWxsIDpcblx0XHRcdHRoaXMuX2RheWxpZ2h0U2F2aW5nQWRqdXN0KCBuZXcgRGF0ZShcblx0XHRcdGluc3QuY3VycmVudFllYXIsIGluc3QuY3VycmVudE1vbnRoLCBpbnN0LmN1cnJlbnREYXkgKSApICk7XG5cdFx0XHRyZXR1cm4gc3RhcnREYXRlO1xuXHR9LFxuXG5cdC8qIEF0dGFjaCB0aGUgb254eHggaGFuZGxlcnMuICBUaGVzZSBhcmUgZGVjbGFyZWQgc3RhdGljYWxseSBzb1xuXHQgKiB0aGV5IHdvcmsgd2l0aCBzdGF0aWMgY29kZSB0cmFuc2Zvcm1lcnMgbGlrZSBDYWphLlxuXHQgKi9cblx0X2F0dGFjaEhhbmRsZXJzOiBmdW5jdGlvbiggaW5zdCApIHtcblx0XHR2YXIgc3RlcE1vbnRocyA9IHRoaXMuX2dldCggaW5zdCwgXCJzdGVwTW9udGhzXCIgKSxcblx0XHRcdGlkID0gXCIjXCIgKyBpbnN0LmlkLnJlcGxhY2UoIC9cXFxcXFxcXC9nLCBcIlxcXFxcIiApO1xuXHRcdGluc3QuZHBEaXYuZmluZCggXCJbZGF0YS1oYW5kbGVyXVwiICkubWFwKCBmdW5jdGlvbigpIHtcblx0XHRcdHZhciBoYW5kbGVyID0ge1xuXHRcdFx0XHRwcmV2OiBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHQkLmRhdGVwaWNrZXIuX2FkanVzdERhdGUoIGlkLCAtc3RlcE1vbnRocywgXCJNXCIgKTtcblx0XHRcdFx0fSxcblx0XHRcdFx0bmV4dDogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0JC5kYXRlcGlja2VyLl9hZGp1c3REYXRlKCBpZCwgK3N0ZXBNb250aHMsIFwiTVwiICk7XG5cdFx0XHRcdH0sXG5cdFx0XHRcdGhpZGU6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdCQuZGF0ZXBpY2tlci5faGlkZURhdGVwaWNrZXIoKTtcblx0XHRcdFx0fSxcblx0XHRcdFx0dG9kYXk6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdCQuZGF0ZXBpY2tlci5fZ290b1RvZGF5KCBpZCApO1xuXHRcdFx0XHR9LFxuXHRcdFx0XHRzZWxlY3REYXk6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdCQuZGF0ZXBpY2tlci5fc2VsZWN0RGF5KCBpZCwgK3RoaXMuZ2V0QXR0cmlidXRlKCBcImRhdGEtbW9udGhcIiApLCArdGhpcy5nZXRBdHRyaWJ1dGUoIFwiZGF0YS15ZWFyXCIgKSwgdGhpcyApO1xuXHRcdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdFx0fSxcblx0XHRcdFx0c2VsZWN0TW9udGg6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdCQuZGF0ZXBpY2tlci5fc2VsZWN0TW9udGhZZWFyKCBpZCwgdGhpcywgXCJNXCIgKTtcblx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0XHRcdH0sXG5cdFx0XHRcdHNlbGVjdFllYXI6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdCQuZGF0ZXBpY2tlci5fc2VsZWN0TW9udGhZZWFyKCBpZCwgdGhpcywgXCJZXCIgKTtcblx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0XHRcdH1cblx0XHRcdH07XG5cdFx0XHQkKCB0aGlzICkub24oIHRoaXMuZ2V0QXR0cmlidXRlKCBcImRhdGEtZXZlbnRcIiApLCBoYW5kbGVyWyB0aGlzLmdldEF0dHJpYnV0ZSggXCJkYXRhLWhhbmRsZXJcIiApIF0gKTtcblx0XHR9ICk7XG5cdH0sXG5cblx0LyogR2VuZXJhdGUgdGhlIEhUTUwgZm9yIHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBkYXRlIHBpY2tlci4gKi9cblx0X2dlbmVyYXRlSFRNTDogZnVuY3Rpb24oIGluc3QgKSB7XG5cdFx0dmFyIG1heERyYXcsIHByZXZUZXh0LCBwcmV2LCBuZXh0VGV4dCwgbmV4dCwgY3VycmVudFRleHQsIGdvdG9EYXRlLFxuXHRcdFx0Y29udHJvbHMsIGJ1dHRvblBhbmVsLCBmaXJzdERheSwgc2hvd1dlZWssIGRheU5hbWVzLCBkYXlOYW1lc01pbixcblx0XHRcdG1vbnRoTmFtZXMsIG1vbnRoTmFtZXNTaG9ydCwgYmVmb3JlU2hvd0RheSwgc2hvd090aGVyTW9udGhzLFxuXHRcdFx0c2VsZWN0T3RoZXJNb250aHMsIGRlZmF1bHREYXRlLCBodG1sLCBkb3csIHJvdywgZ3JvdXAsIGNvbCwgc2VsZWN0ZWREYXRlLFxuXHRcdFx0Y29ybmVyQ2xhc3MsIGNhbGVuZGVyLCB0aGVhZCwgZGF5LCBkYXlzSW5Nb250aCwgbGVhZERheXMsIGN1clJvd3MsIG51bVJvd3MsXG5cdFx0XHRwcmludERhdGUsIGRSb3csIHRib2R5LCBkYXlTZXR0aW5ncywgb3RoZXJNb250aCwgdW5zZWxlY3RhYmxlLFxuXHRcdFx0dGVtcERhdGUgPSBuZXcgRGF0ZSgpLFxuXHRcdFx0dG9kYXkgPSB0aGlzLl9kYXlsaWdodFNhdmluZ0FkanVzdChcblx0XHRcdFx0bmV3IERhdGUoIHRlbXBEYXRlLmdldEZ1bGxZZWFyKCksIHRlbXBEYXRlLmdldE1vbnRoKCksIHRlbXBEYXRlLmdldERhdGUoKSApICksIC8vIGNsZWFyIHRpbWVcblx0XHRcdGlzUlRMID0gdGhpcy5fZ2V0KCBpbnN0LCBcImlzUlRMXCIgKSxcblx0XHRcdHNob3dCdXR0b25QYW5lbCA9IHRoaXMuX2dldCggaW5zdCwgXCJzaG93QnV0dG9uUGFuZWxcIiApLFxuXHRcdFx0aGlkZUlmTm9QcmV2TmV4dCA9IHRoaXMuX2dldCggaW5zdCwgXCJoaWRlSWZOb1ByZXZOZXh0XCIgKSxcblx0XHRcdG5hdmlnYXRpb25Bc0RhdGVGb3JtYXQgPSB0aGlzLl9nZXQoIGluc3QsIFwibmF2aWdhdGlvbkFzRGF0ZUZvcm1hdFwiICksXG5cdFx0XHRudW1Nb250aHMgPSB0aGlzLl9nZXROdW1iZXJPZk1vbnRocyggaW5zdCApLFxuXHRcdFx0c2hvd0N1cnJlbnRBdFBvcyA9IHRoaXMuX2dldCggaW5zdCwgXCJzaG93Q3VycmVudEF0UG9zXCIgKSxcblx0XHRcdHN0ZXBNb250aHMgPSB0aGlzLl9nZXQoIGluc3QsIFwic3RlcE1vbnRoc1wiICksXG5cdFx0XHRpc011bHRpTW9udGggPSAoIG51bU1vbnRoc1sgMCBdICE9PSAxIHx8IG51bU1vbnRoc1sgMSBdICE9PSAxICksXG5cdFx0XHRjdXJyZW50RGF0ZSA9IHRoaXMuX2RheWxpZ2h0U2F2aW5nQWRqdXN0KCAoICFpbnN0LmN1cnJlbnREYXkgPyBuZXcgRGF0ZSggOTk5OSwgOSwgOSApIDpcblx0XHRcdFx0bmV3IERhdGUoIGluc3QuY3VycmVudFllYXIsIGluc3QuY3VycmVudE1vbnRoLCBpbnN0LmN1cnJlbnREYXkgKSApICksXG5cdFx0XHRtaW5EYXRlID0gdGhpcy5fZ2V0TWluTWF4RGF0ZSggaW5zdCwgXCJtaW5cIiApLFxuXHRcdFx0bWF4RGF0ZSA9IHRoaXMuX2dldE1pbk1heERhdGUoIGluc3QsIFwibWF4XCIgKSxcblx0XHRcdGRyYXdNb250aCA9IGluc3QuZHJhd01vbnRoIC0gc2hvd0N1cnJlbnRBdFBvcyxcblx0XHRcdGRyYXdZZWFyID0gaW5zdC5kcmF3WWVhcjtcblxuXHRcdGlmICggZHJhd01vbnRoIDwgMCApIHtcblx0XHRcdGRyYXdNb250aCArPSAxMjtcblx0XHRcdGRyYXdZZWFyLS07XG5cdFx0fVxuXHRcdGlmICggbWF4RGF0ZSApIHtcblx0XHRcdG1heERyYXcgPSB0aGlzLl9kYXlsaWdodFNhdmluZ0FkanVzdCggbmV3IERhdGUoIG1heERhdGUuZ2V0RnVsbFllYXIoKSxcblx0XHRcdFx0bWF4RGF0ZS5nZXRNb250aCgpIC0gKCBudW1Nb250aHNbIDAgXSAqIG51bU1vbnRoc1sgMSBdICkgKyAxLCBtYXhEYXRlLmdldERhdGUoKSApICk7XG5cdFx0XHRtYXhEcmF3ID0gKCBtaW5EYXRlICYmIG1heERyYXcgPCBtaW5EYXRlID8gbWluRGF0ZSA6IG1heERyYXcgKTtcblx0XHRcdHdoaWxlICggdGhpcy5fZGF5bGlnaHRTYXZpbmdBZGp1c3QoIG5ldyBEYXRlKCBkcmF3WWVhciwgZHJhd01vbnRoLCAxICkgKSA+IG1heERyYXcgKSB7XG5cdFx0XHRcdGRyYXdNb250aC0tO1xuXHRcdFx0XHRpZiAoIGRyYXdNb250aCA8IDAgKSB7XG5cdFx0XHRcdFx0ZHJhd01vbnRoID0gMTE7XG5cdFx0XHRcdFx0ZHJhd1llYXItLTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0XHRpbnN0LmRyYXdNb250aCA9IGRyYXdNb250aDtcblx0XHRpbnN0LmRyYXdZZWFyID0gZHJhd1llYXI7XG5cblx0XHRwcmV2VGV4dCA9IHRoaXMuX2dldCggaW5zdCwgXCJwcmV2VGV4dFwiICk7XG5cdFx0cHJldlRleHQgPSAoICFuYXZpZ2F0aW9uQXNEYXRlRm9ybWF0ID8gcHJldlRleHQgOiB0aGlzLmZvcm1hdERhdGUoIHByZXZUZXh0LFxuXHRcdFx0dGhpcy5fZGF5bGlnaHRTYXZpbmdBZGp1c3QoIG5ldyBEYXRlKCBkcmF3WWVhciwgZHJhd01vbnRoIC0gc3RlcE1vbnRocywgMSApICksXG5cdFx0XHR0aGlzLl9nZXRGb3JtYXRDb25maWcoIGluc3QgKSApICk7XG5cblx0XHRwcmV2ID0gKCB0aGlzLl9jYW5BZGp1c3RNb250aCggaW5zdCwgLTEsIGRyYXdZZWFyLCBkcmF3TW9udGggKSA/XG5cdFx0XHRcIjxhIGNsYXNzPSd1aS1kYXRlcGlja2VyLXByZXYgdWktY29ybmVyLWFsbCcgZGF0YS1oYW5kbGVyPSdwcmV2JyBkYXRhLWV2ZW50PSdjbGljaydcIiArXG5cdFx0XHRcIiB0aXRsZT0nXCIgKyBwcmV2VGV4dCArIFwiJz48c3BhbiBjbGFzcz0ndWktaWNvbiB1aS1pY29uLWNpcmNsZS10cmlhbmdsZS1cIiArICggaXNSVEwgPyBcImVcIiA6IFwid1wiICkgKyBcIic+XCIgKyBwcmV2VGV4dCArIFwiPC9zcGFuPjwvYT5cIiA6XG5cdFx0XHQoIGhpZGVJZk5vUHJldk5leHQgPyBcIlwiIDogXCI8YSBjbGFzcz0ndWktZGF0ZXBpY2tlci1wcmV2IHVpLWNvcm5lci1hbGwgdWktc3RhdGUtZGlzYWJsZWQnIHRpdGxlPSdcIiArIHByZXZUZXh0ICsgXCInPjxzcGFuIGNsYXNzPSd1aS1pY29uIHVpLWljb24tY2lyY2xlLXRyaWFuZ2xlLVwiICsgKCBpc1JUTCA/IFwiZVwiIDogXCJ3XCIgKSArIFwiJz5cIiArIHByZXZUZXh0ICsgXCI8L3NwYW4+PC9hPlwiICkgKTtcblxuXHRcdG5leHRUZXh0ID0gdGhpcy5fZ2V0KCBpbnN0LCBcIm5leHRUZXh0XCIgKTtcblx0XHRuZXh0VGV4dCA9ICggIW5hdmlnYXRpb25Bc0RhdGVGb3JtYXQgPyBuZXh0VGV4dCA6IHRoaXMuZm9ybWF0RGF0ZSggbmV4dFRleHQsXG5cdFx0XHR0aGlzLl9kYXlsaWdodFNhdmluZ0FkanVzdCggbmV3IERhdGUoIGRyYXdZZWFyLCBkcmF3TW9udGggKyBzdGVwTW9udGhzLCAxICkgKSxcblx0XHRcdHRoaXMuX2dldEZvcm1hdENvbmZpZyggaW5zdCApICkgKTtcblxuXHRcdG5leHQgPSAoIHRoaXMuX2NhbkFkanVzdE1vbnRoKCBpbnN0LCArMSwgZHJhd1llYXIsIGRyYXdNb250aCApID9cblx0XHRcdFwiPGEgY2xhc3M9J3VpLWRhdGVwaWNrZXItbmV4dCB1aS1jb3JuZXItYWxsJyBkYXRhLWhhbmRsZXI9J25leHQnIGRhdGEtZXZlbnQ9J2NsaWNrJ1wiICtcblx0XHRcdFwiIHRpdGxlPSdcIiArIG5leHRUZXh0ICsgXCInPjxzcGFuIGNsYXNzPSd1aS1pY29uIHVpLWljb24tY2lyY2xlLXRyaWFuZ2xlLVwiICsgKCBpc1JUTCA/IFwid1wiIDogXCJlXCIgKSArIFwiJz5cIiArIG5leHRUZXh0ICsgXCI8L3NwYW4+PC9hPlwiIDpcblx0XHRcdCggaGlkZUlmTm9QcmV2TmV4dCA/IFwiXCIgOiBcIjxhIGNsYXNzPSd1aS1kYXRlcGlja2VyLW5leHQgdWktY29ybmVyLWFsbCB1aS1zdGF0ZS1kaXNhYmxlZCcgdGl0bGU9J1wiICsgbmV4dFRleHQgKyBcIic+PHNwYW4gY2xhc3M9J3VpLWljb24gdWktaWNvbi1jaXJjbGUtdHJpYW5nbGUtXCIgKyAoIGlzUlRMID8gXCJ3XCIgOiBcImVcIiApICsgXCInPlwiICsgbmV4dFRleHQgKyBcIjwvc3Bhbj48L2E+XCIgKSApO1xuXG5cdFx0Y3VycmVudFRleHQgPSB0aGlzLl9nZXQoIGluc3QsIFwiY3VycmVudFRleHRcIiApO1xuXHRcdGdvdG9EYXRlID0gKCB0aGlzLl9nZXQoIGluc3QsIFwiZ290b0N1cnJlbnRcIiApICYmIGluc3QuY3VycmVudERheSA/IGN1cnJlbnREYXRlIDogdG9kYXkgKTtcblx0XHRjdXJyZW50VGV4dCA9ICggIW5hdmlnYXRpb25Bc0RhdGVGb3JtYXQgPyBjdXJyZW50VGV4dCA6XG5cdFx0XHR0aGlzLmZvcm1hdERhdGUoIGN1cnJlbnRUZXh0LCBnb3RvRGF0ZSwgdGhpcy5fZ2V0Rm9ybWF0Q29uZmlnKCBpbnN0ICkgKSApO1xuXG5cdFx0Y29udHJvbHMgPSAoICFpbnN0LmlubGluZSA/IFwiPGJ1dHRvbiB0eXBlPSdidXR0b24nIGNsYXNzPSd1aS1kYXRlcGlja2VyLWNsb3NlIHVpLXN0YXRlLWRlZmF1bHQgdWktcHJpb3JpdHktcHJpbWFyeSB1aS1jb3JuZXItYWxsJyBkYXRhLWhhbmRsZXI9J2hpZGUnIGRhdGEtZXZlbnQ9J2NsaWNrJz5cIiArXG5cdFx0XHR0aGlzLl9nZXQoIGluc3QsIFwiY2xvc2VUZXh0XCIgKSArIFwiPC9idXR0b24+XCIgOiBcIlwiICk7XG5cblx0XHRidXR0b25QYW5lbCA9ICggc2hvd0J1dHRvblBhbmVsICkgPyBcIjxkaXYgY2xhc3M9J3VpLWRhdGVwaWNrZXItYnV0dG9ucGFuZSB1aS13aWRnZXQtY29udGVudCc+XCIgKyAoIGlzUlRMID8gY29udHJvbHMgOiBcIlwiICkgK1xuXHRcdFx0KCB0aGlzLl9pc0luUmFuZ2UoIGluc3QsIGdvdG9EYXRlICkgPyBcIjxidXR0b24gdHlwZT0nYnV0dG9uJyBjbGFzcz0ndWktZGF0ZXBpY2tlci1jdXJyZW50IHVpLXN0YXRlLWRlZmF1bHQgdWktcHJpb3JpdHktc2Vjb25kYXJ5IHVpLWNvcm5lci1hbGwnIGRhdGEtaGFuZGxlcj0ndG9kYXknIGRhdGEtZXZlbnQ9J2NsaWNrJ1wiICtcblx0XHRcdFwiPlwiICsgY3VycmVudFRleHQgKyBcIjwvYnV0dG9uPlwiIDogXCJcIiApICsgKCBpc1JUTCA/IFwiXCIgOiBjb250cm9scyApICsgXCI8L2Rpdj5cIiA6IFwiXCI7XG5cblx0XHRmaXJzdERheSA9IHBhcnNlSW50KCB0aGlzLl9nZXQoIGluc3QsIFwiZmlyc3REYXlcIiApLCAxMCApO1xuXHRcdGZpcnN0RGF5ID0gKCBpc05hTiggZmlyc3REYXkgKSA/IDAgOiBmaXJzdERheSApO1xuXG5cdFx0c2hvd1dlZWsgPSB0aGlzLl9nZXQoIGluc3QsIFwic2hvd1dlZWtcIiApO1xuXHRcdGRheU5hbWVzID0gdGhpcy5fZ2V0KCBpbnN0LCBcImRheU5hbWVzXCIgKTtcblx0XHRkYXlOYW1lc01pbiA9IHRoaXMuX2dldCggaW5zdCwgXCJkYXlOYW1lc01pblwiICk7XG5cdFx0bW9udGhOYW1lcyA9IHRoaXMuX2dldCggaW5zdCwgXCJtb250aE5hbWVzXCIgKTtcblx0XHRtb250aE5hbWVzU2hvcnQgPSB0aGlzLl9nZXQoIGluc3QsIFwibW9udGhOYW1lc1Nob3J0XCIgKTtcblx0XHRiZWZvcmVTaG93RGF5ID0gdGhpcy5fZ2V0KCBpbnN0LCBcImJlZm9yZVNob3dEYXlcIiApO1xuXHRcdHNob3dPdGhlck1vbnRocyA9IHRoaXMuX2dldCggaW5zdCwgXCJzaG93T3RoZXJNb250aHNcIiApO1xuXHRcdHNlbGVjdE90aGVyTW9udGhzID0gdGhpcy5fZ2V0KCBpbnN0LCBcInNlbGVjdE90aGVyTW9udGhzXCIgKTtcblx0XHRkZWZhdWx0RGF0ZSA9IHRoaXMuX2dldERlZmF1bHREYXRlKCBpbnN0ICk7XG5cdFx0aHRtbCA9IFwiXCI7XG5cblx0XHRmb3IgKCByb3cgPSAwOyByb3cgPCBudW1Nb250aHNbIDAgXTsgcm93KysgKSB7XG5cdFx0XHRncm91cCA9IFwiXCI7XG5cdFx0XHR0aGlzLm1heFJvd3MgPSA0O1xuXHRcdFx0Zm9yICggY29sID0gMDsgY29sIDwgbnVtTW9udGhzWyAxIF07IGNvbCsrICkge1xuXHRcdFx0XHRzZWxlY3RlZERhdGUgPSB0aGlzLl9kYXlsaWdodFNhdmluZ0FkanVzdCggbmV3IERhdGUoIGRyYXdZZWFyLCBkcmF3TW9udGgsIGluc3Quc2VsZWN0ZWREYXkgKSApO1xuXHRcdFx0XHRjb3JuZXJDbGFzcyA9IFwiIHVpLWNvcm5lci1hbGxcIjtcblx0XHRcdFx0Y2FsZW5kZXIgPSBcIlwiO1xuXHRcdFx0XHRpZiAoIGlzTXVsdGlNb250aCApIHtcblx0XHRcdFx0XHRjYWxlbmRlciArPSBcIjxkaXYgY2xhc3M9J3VpLWRhdGVwaWNrZXItZ3JvdXBcIjtcblx0XHRcdFx0XHRpZiAoIG51bU1vbnRoc1sgMSBdID4gMSApIHtcblx0XHRcdFx0XHRcdHN3aXRjaCAoIGNvbCApIHtcblx0XHRcdFx0XHRcdFx0Y2FzZSAwOiBjYWxlbmRlciArPSBcIiB1aS1kYXRlcGlja2VyLWdyb3VwLWZpcnN0XCI7XG5cdFx0XHRcdFx0XHRcdFx0Y29ybmVyQ2xhc3MgPSBcIiB1aS1jb3JuZXItXCIgKyAoIGlzUlRMID8gXCJyaWdodFwiIDogXCJsZWZ0XCIgKTsgYnJlYWs7XG5cdFx0XHRcdFx0XHRcdGNhc2UgbnVtTW9udGhzWyAxIF0gLSAxOiBjYWxlbmRlciArPSBcIiB1aS1kYXRlcGlja2VyLWdyb3VwLWxhc3RcIjtcblx0XHRcdFx0XHRcdFx0XHRjb3JuZXJDbGFzcyA9IFwiIHVpLWNvcm5lci1cIiArICggaXNSVEwgPyBcImxlZnRcIiA6IFwicmlnaHRcIiApOyBicmVhaztcblx0XHRcdFx0XHRcdFx0ZGVmYXVsdDogY2FsZW5kZXIgKz0gXCIgdWktZGF0ZXBpY2tlci1ncm91cC1taWRkbGVcIjsgY29ybmVyQ2xhc3MgPSBcIlwiOyBicmVhaztcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0Y2FsZW5kZXIgKz0gXCInPlwiO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGNhbGVuZGVyICs9IFwiPGRpdiBjbGFzcz0ndWktZGF0ZXBpY2tlci1oZWFkZXIgdWktd2lkZ2V0LWhlYWRlciB1aS1oZWxwZXItY2xlYXJmaXhcIiArIGNvcm5lckNsYXNzICsgXCInPlwiICtcblx0XHRcdFx0XHQoIC9hbGx8bGVmdC8udGVzdCggY29ybmVyQ2xhc3MgKSAmJiByb3cgPT09IDAgPyAoIGlzUlRMID8gbmV4dCA6IHByZXYgKSA6IFwiXCIgKSArXG5cdFx0XHRcdFx0KCAvYWxsfHJpZ2h0Ly50ZXN0KCBjb3JuZXJDbGFzcyApICYmIHJvdyA9PT0gMCA/ICggaXNSVEwgPyBwcmV2IDogbmV4dCApIDogXCJcIiApICtcblx0XHRcdFx0XHR0aGlzLl9nZW5lcmF0ZU1vbnRoWWVhckhlYWRlciggaW5zdCwgZHJhd01vbnRoLCBkcmF3WWVhciwgbWluRGF0ZSwgbWF4RGF0ZSxcblx0XHRcdFx0XHRyb3cgPiAwIHx8IGNvbCA+IDAsIG1vbnRoTmFtZXMsIG1vbnRoTmFtZXNTaG9ydCApICsgLy8gZHJhdyBtb250aCBoZWFkZXJzXG5cdFx0XHRcdFx0XCI8L2Rpdj48dGFibGUgY2xhc3M9J3VpLWRhdGVwaWNrZXItY2FsZW5kYXInPjx0aGVhZD5cIiArXG5cdFx0XHRcdFx0XCI8dHI+XCI7XG5cdFx0XHRcdHRoZWFkID0gKCBzaG93V2VlayA/IFwiPHRoIGNsYXNzPSd1aS1kYXRlcGlja2VyLXdlZWstY29sJz5cIiArIHRoaXMuX2dldCggaW5zdCwgXCJ3ZWVrSGVhZGVyXCIgKSArIFwiPC90aD5cIiA6IFwiXCIgKTtcblx0XHRcdFx0Zm9yICggZG93ID0gMDsgZG93IDwgNzsgZG93KysgKSB7IC8vIGRheXMgb2YgdGhlIHdlZWtcblx0XHRcdFx0XHRkYXkgPSAoIGRvdyArIGZpcnN0RGF5ICkgJSA3O1xuXHRcdFx0XHRcdHRoZWFkICs9IFwiPHRoIHNjb3BlPSdjb2wnXCIgKyAoICggZG93ICsgZmlyc3REYXkgKyA2ICkgJSA3ID49IDUgPyBcIiBjbGFzcz0ndWktZGF0ZXBpY2tlci13ZWVrLWVuZCdcIiA6IFwiXCIgKSArIFwiPlwiICtcblx0XHRcdFx0XHRcdFwiPHNwYW4gdGl0bGU9J1wiICsgZGF5TmFtZXNbIGRheSBdICsgXCInPlwiICsgZGF5TmFtZXNNaW5bIGRheSBdICsgXCI8L3NwYW4+PC90aD5cIjtcblx0XHRcdFx0fVxuXHRcdFx0XHRjYWxlbmRlciArPSB0aGVhZCArIFwiPC90cj48L3RoZWFkPjx0Ym9keT5cIjtcblx0XHRcdFx0ZGF5c0luTW9udGggPSB0aGlzLl9nZXREYXlzSW5Nb250aCggZHJhd1llYXIsIGRyYXdNb250aCApO1xuXHRcdFx0XHRpZiAoIGRyYXdZZWFyID09PSBpbnN0LnNlbGVjdGVkWWVhciAmJiBkcmF3TW9udGggPT09IGluc3Quc2VsZWN0ZWRNb250aCApIHtcblx0XHRcdFx0XHRpbnN0LnNlbGVjdGVkRGF5ID0gTWF0aC5taW4oIGluc3Quc2VsZWN0ZWREYXksIGRheXNJbk1vbnRoICk7XG5cdFx0XHRcdH1cblx0XHRcdFx0bGVhZERheXMgPSAoIHRoaXMuX2dldEZpcnN0RGF5T2ZNb250aCggZHJhd1llYXIsIGRyYXdNb250aCApIC0gZmlyc3REYXkgKyA3ICkgJSA3O1xuXHRcdFx0XHRjdXJSb3dzID0gTWF0aC5jZWlsKCAoIGxlYWREYXlzICsgZGF5c0luTW9udGggKSAvIDcgKTsgLy8gY2FsY3VsYXRlIHRoZSBudW1iZXIgb2Ygcm93cyB0byBnZW5lcmF0ZVxuXHRcdFx0XHRudW1Sb3dzID0gKCBpc011bHRpTW9udGggPyB0aGlzLm1heFJvd3MgPiBjdXJSb3dzID8gdGhpcy5tYXhSb3dzIDogY3VyUm93cyA6IGN1clJvd3MgKTsgLy9JZiBtdWx0aXBsZSBtb250aHMsIHVzZSB0aGUgaGlnaGVyIG51bWJlciBvZiByb3dzIChzZWUgIzcwNDMpXG5cdFx0XHRcdHRoaXMubWF4Um93cyA9IG51bVJvd3M7XG5cdFx0XHRcdHByaW50RGF0ZSA9IHRoaXMuX2RheWxpZ2h0U2F2aW5nQWRqdXN0KCBuZXcgRGF0ZSggZHJhd1llYXIsIGRyYXdNb250aCwgMSAtIGxlYWREYXlzICkgKTtcblx0XHRcdFx0Zm9yICggZFJvdyA9IDA7IGRSb3cgPCBudW1Sb3dzOyBkUm93KysgKSB7IC8vIGNyZWF0ZSBkYXRlIHBpY2tlciByb3dzXG5cdFx0XHRcdFx0Y2FsZW5kZXIgKz0gXCI8dHI+XCI7XG5cdFx0XHRcdFx0dGJvZHkgPSAoICFzaG93V2VlayA/IFwiXCIgOiBcIjx0ZCBjbGFzcz0ndWktZGF0ZXBpY2tlci13ZWVrLWNvbCc+XCIgK1xuXHRcdFx0XHRcdFx0dGhpcy5fZ2V0KCBpbnN0LCBcImNhbGN1bGF0ZVdlZWtcIiApKCBwcmludERhdGUgKSArIFwiPC90ZD5cIiApO1xuXHRcdFx0XHRcdGZvciAoIGRvdyA9IDA7IGRvdyA8IDc7IGRvdysrICkgeyAvLyBjcmVhdGUgZGF0ZSBwaWNrZXIgZGF5c1xuXHRcdFx0XHRcdFx0ZGF5U2V0dGluZ3MgPSAoIGJlZm9yZVNob3dEYXkgP1xuXHRcdFx0XHRcdFx0XHRiZWZvcmVTaG93RGF5LmFwcGx5KCAoIGluc3QuaW5wdXQgPyBpbnN0LmlucHV0WyAwIF0gOiBudWxsICksIFsgcHJpbnREYXRlIF0gKSA6IFsgdHJ1ZSwgXCJcIiBdICk7XG5cdFx0XHRcdFx0XHRvdGhlck1vbnRoID0gKCBwcmludERhdGUuZ2V0TW9udGgoKSAhPT0gZHJhd01vbnRoICk7XG5cdFx0XHRcdFx0XHR1bnNlbGVjdGFibGUgPSAoIG90aGVyTW9udGggJiYgIXNlbGVjdE90aGVyTW9udGhzICkgfHwgIWRheVNldHRpbmdzWyAwIF0gfHxcblx0XHRcdFx0XHRcdFx0KCBtaW5EYXRlICYmIHByaW50RGF0ZSA8IG1pbkRhdGUgKSB8fCAoIG1heERhdGUgJiYgcHJpbnREYXRlID4gbWF4RGF0ZSApO1xuXHRcdFx0XHRcdFx0dGJvZHkgKz0gXCI8dGQgY2xhc3M9J1wiICtcblx0XHRcdFx0XHRcdFx0KCAoIGRvdyArIGZpcnN0RGF5ICsgNiApICUgNyA+PSA1ID8gXCIgdWktZGF0ZXBpY2tlci13ZWVrLWVuZFwiIDogXCJcIiApICsgLy8gaGlnaGxpZ2h0IHdlZWtlbmRzXG5cdFx0XHRcdFx0XHRcdCggb3RoZXJNb250aCA/IFwiIHVpLWRhdGVwaWNrZXItb3RoZXItbW9udGhcIiA6IFwiXCIgKSArIC8vIGhpZ2hsaWdodCBkYXlzIGZyb20gb3RoZXIgbW9udGhzXG5cdFx0XHRcdFx0XHRcdCggKCBwcmludERhdGUuZ2V0VGltZSgpID09PSBzZWxlY3RlZERhdGUuZ2V0VGltZSgpICYmIGRyYXdNb250aCA9PT0gaW5zdC5zZWxlY3RlZE1vbnRoICYmIGluc3QuX2tleUV2ZW50ICkgfHwgLy8gdXNlciBwcmVzc2VkIGtleVxuXHRcdFx0XHRcdFx0XHQoIGRlZmF1bHREYXRlLmdldFRpbWUoKSA9PT0gcHJpbnREYXRlLmdldFRpbWUoKSAmJiBkZWZhdWx0RGF0ZS5nZXRUaW1lKCkgPT09IHNlbGVjdGVkRGF0ZS5nZXRUaW1lKCkgKSA/XG5cblx0XHRcdFx0XHRcdFx0Ly8gb3IgZGVmYXVsdERhdGUgaXMgY3VycmVudCBwcmludGVkRGF0ZSBhbmQgZGVmYXVsdERhdGUgaXMgc2VsZWN0ZWREYXRlXG5cdFx0XHRcdFx0XHRcdFwiIFwiICsgdGhpcy5fZGF5T3ZlckNsYXNzIDogXCJcIiApICsgLy8gaGlnaGxpZ2h0IHNlbGVjdGVkIGRheVxuXHRcdFx0XHRcdFx0XHQoIHVuc2VsZWN0YWJsZSA/IFwiIFwiICsgdGhpcy5fdW5zZWxlY3RhYmxlQ2xhc3MgKyBcIiB1aS1zdGF0ZS1kaXNhYmxlZFwiIDogXCJcIiApICsgIC8vIGhpZ2hsaWdodCB1bnNlbGVjdGFibGUgZGF5c1xuXHRcdFx0XHRcdFx0XHQoIG90aGVyTW9udGggJiYgIXNob3dPdGhlck1vbnRocyA/IFwiXCIgOiBcIiBcIiArIGRheVNldHRpbmdzWyAxIF0gKyAvLyBoaWdobGlnaHQgY3VzdG9tIGRhdGVzXG5cdFx0XHRcdFx0XHRcdCggcHJpbnREYXRlLmdldFRpbWUoKSA9PT0gY3VycmVudERhdGUuZ2V0VGltZSgpID8gXCIgXCIgKyB0aGlzLl9jdXJyZW50Q2xhc3MgOiBcIlwiICkgKyAvLyBoaWdobGlnaHQgc2VsZWN0ZWQgZGF5XG5cdFx0XHRcdFx0XHRcdCggcHJpbnREYXRlLmdldFRpbWUoKSA9PT0gdG9kYXkuZ2V0VGltZSgpID8gXCIgdWktZGF0ZXBpY2tlci10b2RheVwiIDogXCJcIiApICkgKyBcIidcIiArIC8vIGhpZ2hsaWdodCB0b2RheSAoaWYgZGlmZmVyZW50KVxuXHRcdFx0XHRcdFx0XHQoICggIW90aGVyTW9udGggfHwgc2hvd090aGVyTW9udGhzICkgJiYgZGF5U2V0dGluZ3NbIDIgXSA/IFwiIHRpdGxlPSdcIiArIGRheVNldHRpbmdzWyAyIF0ucmVwbGFjZSggLycvZywgXCImIzM5O1wiICkgKyBcIidcIiA6IFwiXCIgKSArIC8vIGNlbGwgdGl0bGVcblx0XHRcdFx0XHRcdFx0KCB1bnNlbGVjdGFibGUgPyBcIlwiIDogXCIgZGF0YS1oYW5kbGVyPSdzZWxlY3REYXknIGRhdGEtZXZlbnQ9J2NsaWNrJyBkYXRhLW1vbnRoPSdcIiArIHByaW50RGF0ZS5nZXRNb250aCgpICsgXCInIGRhdGEteWVhcj0nXCIgKyBwcmludERhdGUuZ2V0RnVsbFllYXIoKSArIFwiJ1wiICkgKyBcIj5cIiArIC8vIGFjdGlvbnNcblx0XHRcdFx0XHRcdFx0KCBvdGhlck1vbnRoICYmICFzaG93T3RoZXJNb250aHMgPyBcIiYjeGEwO1wiIDogLy8gZGlzcGxheSBmb3Igb3RoZXIgbW9udGhzXG5cdFx0XHRcdFx0XHRcdCggdW5zZWxlY3RhYmxlID8gXCI8c3BhbiBjbGFzcz0ndWktc3RhdGUtZGVmYXVsdCc+XCIgKyBwcmludERhdGUuZ2V0RGF0ZSgpICsgXCI8L3NwYW4+XCIgOiBcIjxhIGNsYXNzPSd1aS1zdGF0ZS1kZWZhdWx0XCIgK1xuXHRcdFx0XHRcdFx0XHQoIHByaW50RGF0ZS5nZXRUaW1lKCkgPT09IHRvZGF5LmdldFRpbWUoKSA/IFwiIHVpLXN0YXRlLWhpZ2hsaWdodFwiIDogXCJcIiApICtcblx0XHRcdFx0XHRcdFx0KCBwcmludERhdGUuZ2V0VGltZSgpID09PSBjdXJyZW50RGF0ZS5nZXRUaW1lKCkgPyBcIiB1aS1zdGF0ZS1hY3RpdmVcIiA6IFwiXCIgKSArIC8vIGhpZ2hsaWdodCBzZWxlY3RlZCBkYXlcblx0XHRcdFx0XHRcdFx0KCBvdGhlck1vbnRoID8gXCIgdWktcHJpb3JpdHktc2Vjb25kYXJ5XCIgOiBcIlwiICkgKyAvLyBkaXN0aW5ndWlzaCBkYXRlcyBmcm9tIG90aGVyIG1vbnRoc1xuXHRcdFx0XHRcdFx0XHRcIicgaHJlZj0nIyc+XCIgKyBwcmludERhdGUuZ2V0RGF0ZSgpICsgXCI8L2E+XCIgKSApICsgXCI8L3RkPlwiOyAvLyBkaXNwbGF5IHNlbGVjdGFibGUgZGF0ZVxuXHRcdFx0XHRcdFx0cHJpbnREYXRlLnNldERhdGUoIHByaW50RGF0ZS5nZXREYXRlKCkgKyAxICk7XG5cdFx0XHRcdFx0XHRwcmludERhdGUgPSB0aGlzLl9kYXlsaWdodFNhdmluZ0FkanVzdCggcHJpbnREYXRlICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGNhbGVuZGVyICs9IHRib2R5ICsgXCI8L3RyPlwiO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGRyYXdNb250aCsrO1xuXHRcdFx0XHRpZiAoIGRyYXdNb250aCA+IDExICkge1xuXHRcdFx0XHRcdGRyYXdNb250aCA9IDA7XG5cdFx0XHRcdFx0ZHJhd1llYXIrKztcblx0XHRcdFx0fVxuXHRcdFx0XHRjYWxlbmRlciArPSBcIjwvdGJvZHk+PC90YWJsZT5cIiArICggaXNNdWx0aU1vbnRoID8gXCI8L2Rpdj5cIiArXG5cdFx0XHRcdFx0XHRcdCggKCBudW1Nb250aHNbIDAgXSA+IDAgJiYgY29sID09PSBudW1Nb250aHNbIDEgXSAtIDEgKSA/IFwiPGRpdiBjbGFzcz0ndWktZGF0ZXBpY2tlci1yb3ctYnJlYWsnPjwvZGl2PlwiIDogXCJcIiApIDogXCJcIiApO1xuXHRcdFx0XHRncm91cCArPSBjYWxlbmRlcjtcblx0XHRcdH1cblx0XHRcdGh0bWwgKz0gZ3JvdXA7XG5cdFx0fVxuXHRcdGh0bWwgKz0gYnV0dG9uUGFuZWw7XG5cdFx0aW5zdC5fa2V5RXZlbnQgPSBmYWxzZTtcblx0XHRyZXR1cm4gaHRtbDtcblx0fSxcblxuXHQvKiBHZW5lcmF0ZSB0aGUgbW9udGggYW5kIHllYXIgaGVhZGVyLiAqL1xuXHRfZ2VuZXJhdGVNb250aFllYXJIZWFkZXI6IGZ1bmN0aW9uKCBpbnN0LCBkcmF3TW9udGgsIGRyYXdZZWFyLCBtaW5EYXRlLCBtYXhEYXRlLFxuXHRcdFx0c2Vjb25kYXJ5LCBtb250aE5hbWVzLCBtb250aE5hbWVzU2hvcnQgKSB7XG5cblx0XHR2YXIgaW5NaW5ZZWFyLCBpbk1heFllYXIsIG1vbnRoLCB5ZWFycywgdGhpc1llYXIsIGRldGVybWluZVllYXIsIHllYXIsIGVuZFllYXIsXG5cdFx0XHRjaGFuZ2VNb250aCA9IHRoaXMuX2dldCggaW5zdCwgXCJjaGFuZ2VNb250aFwiICksXG5cdFx0XHRjaGFuZ2VZZWFyID0gdGhpcy5fZ2V0KCBpbnN0LCBcImNoYW5nZVllYXJcIiApLFxuXHRcdFx0c2hvd01vbnRoQWZ0ZXJZZWFyID0gdGhpcy5fZ2V0KCBpbnN0LCBcInNob3dNb250aEFmdGVyWWVhclwiICksXG5cdFx0XHRodG1sID0gXCI8ZGl2IGNsYXNzPSd1aS1kYXRlcGlja2VyLXRpdGxlJz5cIixcblx0XHRcdG1vbnRoSHRtbCA9IFwiXCI7XG5cblx0XHQvLyBNb250aCBzZWxlY3Rpb25cblx0XHRpZiAoIHNlY29uZGFyeSB8fCAhY2hhbmdlTW9udGggKSB7XG5cdFx0XHRtb250aEh0bWwgKz0gXCI8c3BhbiBjbGFzcz0ndWktZGF0ZXBpY2tlci1tb250aCc+XCIgKyBtb250aE5hbWVzWyBkcmF3TW9udGggXSArIFwiPC9zcGFuPlwiO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRpbk1pblllYXIgPSAoIG1pbkRhdGUgJiYgbWluRGF0ZS5nZXRGdWxsWWVhcigpID09PSBkcmF3WWVhciApO1xuXHRcdFx0aW5NYXhZZWFyID0gKCBtYXhEYXRlICYmIG1heERhdGUuZ2V0RnVsbFllYXIoKSA9PT0gZHJhd1llYXIgKTtcblx0XHRcdG1vbnRoSHRtbCArPSBcIjxzZWxlY3QgY2xhc3M9J3VpLWRhdGVwaWNrZXItbW9udGgnIGRhdGEtaGFuZGxlcj0nc2VsZWN0TW9udGgnIGRhdGEtZXZlbnQ9J2NoYW5nZSc+XCI7XG5cdFx0XHRmb3IgKCBtb250aCA9IDA7IG1vbnRoIDwgMTI7IG1vbnRoKysgKSB7XG5cdFx0XHRcdGlmICggKCAhaW5NaW5ZZWFyIHx8IG1vbnRoID49IG1pbkRhdGUuZ2V0TW9udGgoKSApICYmICggIWluTWF4WWVhciB8fCBtb250aCA8PSBtYXhEYXRlLmdldE1vbnRoKCkgKSApIHtcblx0XHRcdFx0XHRtb250aEh0bWwgKz0gXCI8b3B0aW9uIHZhbHVlPSdcIiArIG1vbnRoICsgXCInXCIgK1xuXHRcdFx0XHRcdFx0KCBtb250aCA9PT0gZHJhd01vbnRoID8gXCIgc2VsZWN0ZWQ9J3NlbGVjdGVkJ1wiIDogXCJcIiApICtcblx0XHRcdFx0XHRcdFwiPlwiICsgbW9udGhOYW1lc1Nob3J0WyBtb250aCBdICsgXCI8L29wdGlvbj5cIjtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdFx0bW9udGhIdG1sICs9IFwiPC9zZWxlY3Q+XCI7XG5cdFx0fVxuXG5cdFx0aWYgKCAhc2hvd01vbnRoQWZ0ZXJZZWFyICkge1xuXHRcdFx0aHRtbCArPSBtb250aEh0bWwgKyAoIHNlY29uZGFyeSB8fCAhKCBjaGFuZ2VNb250aCAmJiBjaGFuZ2VZZWFyICkgPyBcIiYjeGEwO1wiIDogXCJcIiApO1xuXHRcdH1cblxuXHRcdC8vIFllYXIgc2VsZWN0aW9uXG5cdFx0aWYgKCAhaW5zdC55ZWFyc2h0bWwgKSB7XG5cdFx0XHRpbnN0LnllYXJzaHRtbCA9IFwiXCI7XG5cdFx0XHRpZiAoIHNlY29uZGFyeSB8fCAhY2hhbmdlWWVhciApIHtcblx0XHRcdFx0aHRtbCArPSBcIjxzcGFuIGNsYXNzPSd1aS1kYXRlcGlja2VyLXllYXInPlwiICsgZHJhd1llYXIgKyBcIjwvc3Bhbj5cIjtcblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Ly8gZGV0ZXJtaW5lIHJhbmdlIG9mIHllYXJzIHRvIGRpc3BsYXlcblx0XHRcdFx0eWVhcnMgPSB0aGlzLl9nZXQoIGluc3QsIFwieWVhclJhbmdlXCIgKS5zcGxpdCggXCI6XCIgKTtcblx0XHRcdFx0dGhpc1llYXIgPSBuZXcgRGF0ZSgpLmdldEZ1bGxZZWFyKCk7XG5cdFx0XHRcdGRldGVybWluZVllYXIgPSBmdW5jdGlvbiggdmFsdWUgKSB7XG5cdFx0XHRcdFx0dmFyIHllYXIgPSAoIHZhbHVlLm1hdGNoKCAvY1srXFwtXS4qLyApID8gZHJhd1llYXIgKyBwYXJzZUludCggdmFsdWUuc3Vic3RyaW5nKCAxICksIDEwICkgOlxuXHRcdFx0XHRcdFx0KCB2YWx1ZS5tYXRjaCggL1srXFwtXS4qLyApID8gdGhpc1llYXIgKyBwYXJzZUludCggdmFsdWUsIDEwICkgOlxuXHRcdFx0XHRcdFx0cGFyc2VJbnQoIHZhbHVlLCAxMCApICkgKTtcblx0XHRcdFx0XHRyZXR1cm4gKCBpc05hTiggeWVhciApID8gdGhpc1llYXIgOiB5ZWFyICk7XG5cdFx0XHRcdH07XG5cdFx0XHRcdHllYXIgPSBkZXRlcm1pbmVZZWFyKCB5ZWFyc1sgMCBdICk7XG5cdFx0XHRcdGVuZFllYXIgPSBNYXRoLm1heCggeWVhciwgZGV0ZXJtaW5lWWVhciggeWVhcnNbIDEgXSB8fCBcIlwiICkgKTtcblx0XHRcdFx0eWVhciA9ICggbWluRGF0ZSA/IE1hdGgubWF4KCB5ZWFyLCBtaW5EYXRlLmdldEZ1bGxZZWFyKCkgKSA6IHllYXIgKTtcblx0XHRcdFx0ZW5kWWVhciA9ICggbWF4RGF0ZSA/IE1hdGgubWluKCBlbmRZZWFyLCBtYXhEYXRlLmdldEZ1bGxZZWFyKCkgKSA6IGVuZFllYXIgKTtcblx0XHRcdFx0aW5zdC55ZWFyc2h0bWwgKz0gXCI8c2VsZWN0IGNsYXNzPSd1aS1kYXRlcGlja2VyLXllYXInIGRhdGEtaGFuZGxlcj0nc2VsZWN0WWVhcicgZGF0YS1ldmVudD0nY2hhbmdlJz5cIjtcblx0XHRcdFx0Zm9yICggOyB5ZWFyIDw9IGVuZFllYXI7IHllYXIrKyApIHtcblx0XHRcdFx0XHRpbnN0LnllYXJzaHRtbCArPSBcIjxvcHRpb24gdmFsdWU9J1wiICsgeWVhciArIFwiJ1wiICtcblx0XHRcdFx0XHRcdCggeWVhciA9PT0gZHJhd1llYXIgPyBcIiBzZWxlY3RlZD0nc2VsZWN0ZWQnXCIgOiBcIlwiICkgK1xuXHRcdFx0XHRcdFx0XCI+XCIgKyB5ZWFyICsgXCI8L29wdGlvbj5cIjtcblx0XHRcdFx0fVxuXHRcdFx0XHRpbnN0LnllYXJzaHRtbCArPSBcIjwvc2VsZWN0PlwiO1xuXG5cdFx0XHRcdGh0bWwgKz0gaW5zdC55ZWFyc2h0bWw7XG5cdFx0XHRcdGluc3QueWVhcnNodG1sID0gbnVsbDtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRodG1sICs9IHRoaXMuX2dldCggaW5zdCwgXCJ5ZWFyU3VmZml4XCIgKTtcblx0XHRpZiAoIHNob3dNb250aEFmdGVyWWVhciApIHtcblx0XHRcdGh0bWwgKz0gKCBzZWNvbmRhcnkgfHwgISggY2hhbmdlTW9udGggJiYgY2hhbmdlWWVhciApID8gXCImI3hhMDtcIiA6IFwiXCIgKSArIG1vbnRoSHRtbDtcblx0XHR9XG5cdFx0aHRtbCArPSBcIjwvZGl2PlwiOyAvLyBDbG9zZSBkYXRlcGlja2VyX2hlYWRlclxuXHRcdHJldHVybiBodG1sO1xuXHR9LFxuXG5cdC8qIEFkanVzdCBvbmUgb2YgdGhlIGRhdGUgc3ViLWZpZWxkcy4gKi9cblx0X2FkanVzdEluc3REYXRlOiBmdW5jdGlvbiggaW5zdCwgb2Zmc2V0LCBwZXJpb2QgKSB7XG5cdFx0dmFyIHllYXIgPSBpbnN0LnNlbGVjdGVkWWVhciArICggcGVyaW9kID09PSBcIllcIiA/IG9mZnNldCA6IDAgKSxcblx0XHRcdG1vbnRoID0gaW5zdC5zZWxlY3RlZE1vbnRoICsgKCBwZXJpb2QgPT09IFwiTVwiID8gb2Zmc2V0IDogMCApLFxuXHRcdFx0ZGF5ID0gTWF0aC5taW4oIGluc3Quc2VsZWN0ZWREYXksIHRoaXMuX2dldERheXNJbk1vbnRoKCB5ZWFyLCBtb250aCApICkgKyAoIHBlcmlvZCA9PT0gXCJEXCIgPyBvZmZzZXQgOiAwICksXG5cdFx0XHRkYXRlID0gdGhpcy5fcmVzdHJpY3RNaW5NYXgoIGluc3QsIHRoaXMuX2RheWxpZ2h0U2F2aW5nQWRqdXN0KCBuZXcgRGF0ZSggeWVhciwgbW9udGgsIGRheSApICkgKTtcblxuXHRcdGluc3Quc2VsZWN0ZWREYXkgPSBkYXRlLmdldERhdGUoKTtcblx0XHRpbnN0LmRyYXdNb250aCA9IGluc3Quc2VsZWN0ZWRNb250aCA9IGRhdGUuZ2V0TW9udGgoKTtcblx0XHRpbnN0LmRyYXdZZWFyID0gaW5zdC5zZWxlY3RlZFllYXIgPSBkYXRlLmdldEZ1bGxZZWFyKCk7XG5cdFx0aWYgKCBwZXJpb2QgPT09IFwiTVwiIHx8IHBlcmlvZCA9PT0gXCJZXCIgKSB7XG5cdFx0XHR0aGlzLl9ub3RpZnlDaGFuZ2UoIGluc3QgKTtcblx0XHR9XG5cdH0sXG5cblx0LyogRW5zdXJlIGEgZGF0ZSBpcyB3aXRoaW4gYW55IG1pbi9tYXggYm91bmRzLiAqL1xuXHRfcmVzdHJpY3RNaW5NYXg6IGZ1bmN0aW9uKCBpbnN0LCBkYXRlICkge1xuXHRcdHZhciBtaW5EYXRlID0gdGhpcy5fZ2V0TWluTWF4RGF0ZSggaW5zdCwgXCJtaW5cIiApLFxuXHRcdFx0bWF4RGF0ZSA9IHRoaXMuX2dldE1pbk1heERhdGUoIGluc3QsIFwibWF4XCIgKSxcblx0XHRcdG5ld0RhdGUgPSAoIG1pbkRhdGUgJiYgZGF0ZSA8IG1pbkRhdGUgPyBtaW5EYXRlIDogZGF0ZSApO1xuXHRcdHJldHVybiAoIG1heERhdGUgJiYgbmV3RGF0ZSA+IG1heERhdGUgPyBtYXhEYXRlIDogbmV3RGF0ZSApO1xuXHR9LFxuXG5cdC8qIE5vdGlmeSBjaGFuZ2Ugb2YgbW9udGgveWVhci4gKi9cblx0X25vdGlmeUNoYW5nZTogZnVuY3Rpb24oIGluc3QgKSB7XG5cdFx0dmFyIG9uQ2hhbmdlID0gdGhpcy5fZ2V0KCBpbnN0LCBcIm9uQ2hhbmdlTW9udGhZZWFyXCIgKTtcblx0XHRpZiAoIG9uQ2hhbmdlICkge1xuXHRcdFx0b25DaGFuZ2UuYXBwbHkoICggaW5zdC5pbnB1dCA/IGluc3QuaW5wdXRbIDAgXSA6IG51bGwgKSxcblx0XHRcdFx0WyBpbnN0LnNlbGVjdGVkWWVhciwgaW5zdC5zZWxlY3RlZE1vbnRoICsgMSwgaW5zdCBdICk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qIERldGVybWluZSB0aGUgbnVtYmVyIG9mIG1vbnRocyB0byBzaG93LiAqL1xuXHRfZ2V0TnVtYmVyT2ZNb250aHM6IGZ1bmN0aW9uKCBpbnN0ICkge1xuXHRcdHZhciBudW1Nb250aHMgPSB0aGlzLl9nZXQoIGluc3QsIFwibnVtYmVyT2ZNb250aHNcIiApO1xuXHRcdHJldHVybiAoIG51bU1vbnRocyA9PSBudWxsID8gWyAxLCAxIF0gOiAoIHR5cGVvZiBudW1Nb250aHMgPT09IFwibnVtYmVyXCIgPyBbIDEsIG51bU1vbnRocyBdIDogbnVtTW9udGhzICkgKTtcblx0fSxcblxuXHQvKiBEZXRlcm1pbmUgdGhlIGN1cnJlbnQgbWF4aW11bSBkYXRlIC0gZW5zdXJlIG5vIHRpbWUgY29tcG9uZW50cyBhcmUgc2V0LiAqL1xuXHRfZ2V0TWluTWF4RGF0ZTogZnVuY3Rpb24oIGluc3QsIG1pbk1heCApIHtcblx0XHRyZXR1cm4gdGhpcy5fZGV0ZXJtaW5lRGF0ZSggaW5zdCwgdGhpcy5fZ2V0KCBpbnN0LCBtaW5NYXggKyBcIkRhdGVcIiApLCBudWxsICk7XG5cdH0sXG5cblx0LyogRmluZCB0aGUgbnVtYmVyIG9mIGRheXMgaW4gYSBnaXZlbiBtb250aC4gKi9cblx0X2dldERheXNJbk1vbnRoOiBmdW5jdGlvbiggeWVhciwgbW9udGggKSB7XG5cdFx0cmV0dXJuIDMyIC0gdGhpcy5fZGF5bGlnaHRTYXZpbmdBZGp1c3QoIG5ldyBEYXRlKCB5ZWFyLCBtb250aCwgMzIgKSApLmdldERhdGUoKTtcblx0fSxcblxuXHQvKiBGaW5kIHRoZSBkYXkgb2YgdGhlIHdlZWsgb2YgdGhlIGZpcnN0IG9mIGEgbW9udGguICovXG5cdF9nZXRGaXJzdERheU9mTW9udGg6IGZ1bmN0aW9uKCB5ZWFyLCBtb250aCApIHtcblx0XHRyZXR1cm4gbmV3IERhdGUoIHllYXIsIG1vbnRoLCAxICkuZ2V0RGF5KCk7XG5cdH0sXG5cblx0LyogRGV0ZXJtaW5lcyBpZiB3ZSBzaG91bGQgYWxsb3cgYSBcIm5leHQvcHJldlwiIG1vbnRoIGRpc3BsYXkgY2hhbmdlLiAqL1xuXHRfY2FuQWRqdXN0TW9udGg6IGZ1bmN0aW9uKCBpbnN0LCBvZmZzZXQsIGN1clllYXIsIGN1ck1vbnRoICkge1xuXHRcdHZhciBudW1Nb250aHMgPSB0aGlzLl9nZXROdW1iZXJPZk1vbnRocyggaW5zdCApLFxuXHRcdFx0ZGF0ZSA9IHRoaXMuX2RheWxpZ2h0U2F2aW5nQWRqdXN0KCBuZXcgRGF0ZSggY3VyWWVhcixcblx0XHRcdGN1ck1vbnRoICsgKCBvZmZzZXQgPCAwID8gb2Zmc2V0IDogbnVtTW9udGhzWyAwIF0gKiBudW1Nb250aHNbIDEgXSApLCAxICkgKTtcblxuXHRcdGlmICggb2Zmc2V0IDwgMCApIHtcblx0XHRcdGRhdGUuc2V0RGF0ZSggdGhpcy5fZ2V0RGF5c0luTW9udGgoIGRhdGUuZ2V0RnVsbFllYXIoKSwgZGF0ZS5nZXRNb250aCgpICkgKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXMuX2lzSW5SYW5nZSggaW5zdCwgZGF0ZSApO1xuXHR9LFxuXG5cdC8qIElzIHRoZSBnaXZlbiBkYXRlIGluIHRoZSBhY2NlcHRlZCByYW5nZT8gKi9cblx0X2lzSW5SYW5nZTogZnVuY3Rpb24oIGluc3QsIGRhdGUgKSB7XG5cdFx0dmFyIHllYXJTcGxpdCwgY3VycmVudFllYXIsXG5cdFx0XHRtaW5EYXRlID0gdGhpcy5fZ2V0TWluTWF4RGF0ZSggaW5zdCwgXCJtaW5cIiApLFxuXHRcdFx0bWF4RGF0ZSA9IHRoaXMuX2dldE1pbk1heERhdGUoIGluc3QsIFwibWF4XCIgKSxcblx0XHRcdG1pblllYXIgPSBudWxsLFxuXHRcdFx0bWF4WWVhciA9IG51bGwsXG5cdFx0XHR5ZWFycyA9IHRoaXMuX2dldCggaW5zdCwgXCJ5ZWFyUmFuZ2VcIiApO1xuXHRcdFx0aWYgKCB5ZWFycyApIHtcblx0XHRcdFx0eWVhclNwbGl0ID0geWVhcnMuc3BsaXQoIFwiOlwiICk7XG5cdFx0XHRcdGN1cnJlbnRZZWFyID0gbmV3IERhdGUoKS5nZXRGdWxsWWVhcigpO1xuXHRcdFx0XHRtaW5ZZWFyID0gcGFyc2VJbnQoIHllYXJTcGxpdFsgMCBdLCAxMCApO1xuXHRcdFx0XHRtYXhZZWFyID0gcGFyc2VJbnQoIHllYXJTcGxpdFsgMSBdLCAxMCApO1xuXHRcdFx0XHRpZiAoIHllYXJTcGxpdFsgMCBdLm1hdGNoKCAvWytcXC1dLiovICkgKSB7XG5cdFx0XHRcdFx0bWluWWVhciArPSBjdXJyZW50WWVhcjtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoIHllYXJTcGxpdFsgMSBdLm1hdGNoKCAvWytcXC1dLiovICkgKSB7XG5cdFx0XHRcdFx0bWF4WWVhciArPSBjdXJyZW50WWVhcjtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0cmV0dXJuICggKCAhbWluRGF0ZSB8fCBkYXRlLmdldFRpbWUoKSA+PSBtaW5EYXRlLmdldFRpbWUoKSApICYmXG5cdFx0XHQoICFtYXhEYXRlIHx8IGRhdGUuZ2V0VGltZSgpIDw9IG1heERhdGUuZ2V0VGltZSgpICkgJiZcblx0XHRcdCggIW1pblllYXIgfHwgZGF0ZS5nZXRGdWxsWWVhcigpID49IG1pblllYXIgKSAmJlxuXHRcdFx0KCAhbWF4WWVhciB8fCBkYXRlLmdldEZ1bGxZZWFyKCkgPD0gbWF4WWVhciApICk7XG5cdH0sXG5cblx0LyogUHJvdmlkZSB0aGUgY29uZmlndXJhdGlvbiBzZXR0aW5ncyBmb3IgZm9ybWF0dGluZy9wYXJzaW5nLiAqL1xuXHRfZ2V0Rm9ybWF0Q29uZmlnOiBmdW5jdGlvbiggaW5zdCApIHtcblx0XHR2YXIgc2hvcnRZZWFyQ3V0b2ZmID0gdGhpcy5fZ2V0KCBpbnN0LCBcInNob3J0WWVhckN1dG9mZlwiICk7XG5cdFx0c2hvcnRZZWFyQ3V0b2ZmID0gKCB0eXBlb2Ygc2hvcnRZZWFyQ3V0b2ZmICE9PSBcInN0cmluZ1wiID8gc2hvcnRZZWFyQ3V0b2ZmIDpcblx0XHRcdG5ldyBEYXRlKCkuZ2V0RnVsbFllYXIoKSAlIDEwMCArIHBhcnNlSW50KCBzaG9ydFllYXJDdXRvZmYsIDEwICkgKTtcblx0XHRyZXR1cm4geyBzaG9ydFllYXJDdXRvZmY6IHNob3J0WWVhckN1dG9mZixcblx0XHRcdGRheU5hbWVzU2hvcnQ6IHRoaXMuX2dldCggaW5zdCwgXCJkYXlOYW1lc1Nob3J0XCIgKSwgZGF5TmFtZXM6IHRoaXMuX2dldCggaW5zdCwgXCJkYXlOYW1lc1wiICksXG5cdFx0XHRtb250aE5hbWVzU2hvcnQ6IHRoaXMuX2dldCggaW5zdCwgXCJtb250aE5hbWVzU2hvcnRcIiApLCBtb250aE5hbWVzOiB0aGlzLl9nZXQoIGluc3QsIFwibW9udGhOYW1lc1wiICkgfTtcblx0fSxcblxuXHQvKiBGb3JtYXQgdGhlIGdpdmVuIGRhdGUgZm9yIGRpc3BsYXkuICovXG5cdF9mb3JtYXREYXRlOiBmdW5jdGlvbiggaW5zdCwgZGF5LCBtb250aCwgeWVhciApIHtcblx0XHRpZiAoICFkYXkgKSB7XG5cdFx0XHRpbnN0LmN1cnJlbnREYXkgPSBpbnN0LnNlbGVjdGVkRGF5O1xuXHRcdFx0aW5zdC5jdXJyZW50TW9udGggPSBpbnN0LnNlbGVjdGVkTW9udGg7XG5cdFx0XHRpbnN0LmN1cnJlbnRZZWFyID0gaW5zdC5zZWxlY3RlZFllYXI7XG5cdFx0fVxuXHRcdHZhciBkYXRlID0gKCBkYXkgPyAoIHR5cGVvZiBkYXkgPT09IFwib2JqZWN0XCIgPyBkYXkgOlxuXHRcdFx0dGhpcy5fZGF5bGlnaHRTYXZpbmdBZGp1c3QoIG5ldyBEYXRlKCB5ZWFyLCBtb250aCwgZGF5ICkgKSApIDpcblx0XHRcdHRoaXMuX2RheWxpZ2h0U2F2aW5nQWRqdXN0KCBuZXcgRGF0ZSggaW5zdC5jdXJyZW50WWVhciwgaW5zdC5jdXJyZW50TW9udGgsIGluc3QuY3VycmVudERheSApICkgKTtcblx0XHRyZXR1cm4gdGhpcy5mb3JtYXREYXRlKCB0aGlzLl9nZXQoIGluc3QsIFwiZGF0ZUZvcm1hdFwiICksIGRhdGUsIHRoaXMuX2dldEZvcm1hdENvbmZpZyggaW5zdCApICk7XG5cdH1cbn0gKTtcblxuLypcbiAqIEJpbmQgaG92ZXIgZXZlbnRzIGZvciBkYXRlcGlja2VyIGVsZW1lbnRzLlxuICogRG9uZSB2aWEgZGVsZWdhdGUgc28gdGhlIGJpbmRpbmcgb25seSBvY2N1cnMgb25jZSBpbiB0aGUgbGlmZXRpbWUgb2YgdGhlIHBhcmVudCBkaXYuXG4gKiBHbG9iYWwgZGF0ZXBpY2tlcl9pbnN0QWN0aXZlLCBzZXQgYnkgX3VwZGF0ZURhdGVwaWNrZXIgYWxsb3dzIHRoZSBoYW5kbGVycyB0byBmaW5kIHRoZWlyIHdheSBiYWNrIHRvIHRoZSBhY3RpdmUgcGlja2VyLlxuICovXG5mdW5jdGlvbiBkYXRlcGlja2VyX2JpbmRIb3ZlciggZHBEaXYgKSB7XG5cdHZhciBzZWxlY3RvciA9IFwiYnV0dG9uLCAudWktZGF0ZXBpY2tlci1wcmV2LCAudWktZGF0ZXBpY2tlci1uZXh0LCAudWktZGF0ZXBpY2tlci1jYWxlbmRhciB0ZCBhXCI7XG5cdHJldHVybiBkcERpdi5vbiggXCJtb3VzZW91dFwiLCBzZWxlY3RvciwgZnVuY3Rpb24oKSB7XG5cdFx0XHQkKCB0aGlzICkucmVtb3ZlQ2xhc3MoIFwidWktc3RhdGUtaG92ZXJcIiApO1xuXHRcdFx0aWYgKCB0aGlzLmNsYXNzTmFtZS5pbmRleE9mKCBcInVpLWRhdGVwaWNrZXItcHJldlwiICkgIT09IC0xICkge1xuXHRcdFx0XHQkKCB0aGlzICkucmVtb3ZlQ2xhc3MoIFwidWktZGF0ZXBpY2tlci1wcmV2LWhvdmVyXCIgKTtcblx0XHRcdH1cblx0XHRcdGlmICggdGhpcy5jbGFzc05hbWUuaW5kZXhPZiggXCJ1aS1kYXRlcGlja2VyLW5leHRcIiApICE9PSAtMSApIHtcblx0XHRcdFx0JCggdGhpcyApLnJlbW92ZUNsYXNzKCBcInVpLWRhdGVwaWNrZXItbmV4dC1ob3ZlclwiICk7XG5cdFx0XHR9XG5cdFx0fSApXG5cdFx0Lm9uKCBcIm1vdXNlb3ZlclwiLCBzZWxlY3RvciwgZGF0ZXBpY2tlcl9oYW5kbGVNb3VzZW92ZXIgKTtcbn1cblxuZnVuY3Rpb24gZGF0ZXBpY2tlcl9oYW5kbGVNb3VzZW92ZXIoKSB7XG5cdGlmICggISQuZGF0ZXBpY2tlci5faXNEaXNhYmxlZERhdGVwaWNrZXIoIGRhdGVwaWNrZXJfaW5zdEFjdGl2ZS5pbmxpbmUgPyBkYXRlcGlja2VyX2luc3RBY3RpdmUuZHBEaXYucGFyZW50KClbIDAgXSA6IGRhdGVwaWNrZXJfaW5zdEFjdGl2ZS5pbnB1dFsgMCBdICkgKSB7XG5cdFx0JCggdGhpcyApLnBhcmVudHMoIFwiLnVpLWRhdGVwaWNrZXItY2FsZW5kYXJcIiApLmZpbmQoIFwiYVwiICkucmVtb3ZlQ2xhc3MoIFwidWktc3RhdGUtaG92ZXJcIiApO1xuXHRcdCQoIHRoaXMgKS5hZGRDbGFzcyggXCJ1aS1zdGF0ZS1ob3ZlclwiICk7XG5cdFx0aWYgKCB0aGlzLmNsYXNzTmFtZS5pbmRleE9mKCBcInVpLWRhdGVwaWNrZXItcHJldlwiICkgIT09IC0xICkge1xuXHRcdFx0JCggdGhpcyApLmFkZENsYXNzKCBcInVpLWRhdGVwaWNrZXItcHJldi1ob3ZlclwiICk7XG5cdFx0fVxuXHRcdGlmICggdGhpcy5jbGFzc05hbWUuaW5kZXhPZiggXCJ1aS1kYXRlcGlja2VyLW5leHRcIiApICE9PSAtMSApIHtcblx0XHRcdCQoIHRoaXMgKS5hZGRDbGFzcyggXCJ1aS1kYXRlcGlja2VyLW5leHQtaG92ZXJcIiApO1xuXHRcdH1cblx0fVxufVxuXG4vKiBqUXVlcnkgZXh0ZW5kIG5vdyBpZ25vcmVzIG51bGxzISAqL1xuZnVuY3Rpb24gZGF0ZXBpY2tlcl9leHRlbmRSZW1vdmUoIHRhcmdldCwgcHJvcHMgKSB7XG5cdCQuZXh0ZW5kKCB0YXJnZXQsIHByb3BzICk7XG5cdGZvciAoIHZhciBuYW1lIGluIHByb3BzICkge1xuXHRcdGlmICggcHJvcHNbIG5hbWUgXSA9PSBudWxsICkge1xuXHRcdFx0dGFyZ2V0WyBuYW1lIF0gPSBwcm9wc1sgbmFtZSBdO1xuXHRcdH1cblx0fVxuXHRyZXR1cm4gdGFyZ2V0O1xufVxuXG4vKiBJbnZva2UgdGhlIGRhdGVwaWNrZXIgZnVuY3Rpb25hbGl0eS5cbiAgIEBwYXJhbSAgb3B0aW9ucyAgc3RyaW5nIC0gYSBjb21tYW5kLCBvcHRpb25hbGx5IGZvbGxvd2VkIGJ5IGFkZGl0aW9uYWwgcGFyYW1ldGVycyBvclxuXHRcdFx0XHRcdE9iamVjdCAtIHNldHRpbmdzIGZvciBhdHRhY2hpbmcgbmV3IGRhdGVwaWNrZXIgZnVuY3Rpb25hbGl0eVxuICAgQHJldHVybiAgalF1ZXJ5IG9iamVjdCAqL1xuJC5mbi5kYXRlcGlja2VyID0gZnVuY3Rpb24oIG9wdGlvbnMgKSB7XG5cblx0LyogVmVyaWZ5IGFuIGVtcHR5IGNvbGxlY3Rpb24gd2Fzbid0IHBhc3NlZCAtIEZpeGVzICM2OTc2ICovXG5cdGlmICggIXRoaXMubGVuZ3RoICkge1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyogSW5pdGlhbGlzZSB0aGUgZGF0ZSBwaWNrZXIuICovXG5cdGlmICggISQuZGF0ZXBpY2tlci5pbml0aWFsaXplZCApIHtcblx0XHQkKCBkb2N1bWVudCApLm9uKCBcIm1vdXNlZG93blwiLCAkLmRhdGVwaWNrZXIuX2NoZWNrRXh0ZXJuYWxDbGljayApO1xuXHRcdCQuZGF0ZXBpY2tlci5pbml0aWFsaXplZCA9IHRydWU7XG5cdH1cblxuXHQvKiBBcHBlbmQgZGF0ZXBpY2tlciBtYWluIGNvbnRhaW5lciB0byBib2R5IGlmIG5vdCBleGlzdC4gKi9cblx0aWYgKCAkKCBcIiNcIiArICQuZGF0ZXBpY2tlci5fbWFpbkRpdklkICkubGVuZ3RoID09PSAwICkge1xuXHRcdCQoIFwiYm9keVwiICkuYXBwZW5kKCAkLmRhdGVwaWNrZXIuZHBEaXYgKTtcblx0fVxuXG5cdHZhciBvdGhlckFyZ3MgPSBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCggYXJndW1lbnRzLCAxICk7XG5cdGlmICggdHlwZW9mIG9wdGlvbnMgPT09IFwic3RyaW5nXCIgJiYgKCBvcHRpb25zID09PSBcImlzRGlzYWJsZWRcIiB8fCBvcHRpb25zID09PSBcImdldERhdGVcIiB8fCBvcHRpb25zID09PSBcIndpZGdldFwiICkgKSB7XG5cdFx0cmV0dXJuICQuZGF0ZXBpY2tlclsgXCJfXCIgKyBvcHRpb25zICsgXCJEYXRlcGlja2VyXCIgXS5cblx0XHRcdGFwcGx5KCAkLmRhdGVwaWNrZXIsIFsgdGhpc1sgMCBdIF0uY29uY2F0KCBvdGhlckFyZ3MgKSApO1xuXHR9XG5cdGlmICggb3B0aW9ucyA9PT0gXCJvcHRpb25cIiAmJiBhcmd1bWVudHMubGVuZ3RoID09PSAyICYmIHR5cGVvZiBhcmd1bWVudHNbIDEgXSA9PT0gXCJzdHJpbmdcIiApIHtcblx0XHRyZXR1cm4gJC5kYXRlcGlja2VyWyBcIl9cIiArIG9wdGlvbnMgKyBcIkRhdGVwaWNrZXJcIiBdLlxuXHRcdFx0YXBwbHkoICQuZGF0ZXBpY2tlciwgWyB0aGlzWyAwIF0gXS5jb25jYXQoIG90aGVyQXJncyApICk7XG5cdH1cblx0cmV0dXJuIHRoaXMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0dHlwZW9mIG9wdGlvbnMgPT09IFwic3RyaW5nXCIgP1xuXHRcdFx0JC5kYXRlcGlja2VyWyBcIl9cIiArIG9wdGlvbnMgKyBcIkRhdGVwaWNrZXJcIiBdLlxuXHRcdFx0XHRhcHBseSggJC5kYXRlcGlja2VyLCBbIHRoaXMgXS5jb25jYXQoIG90aGVyQXJncyApICkgOlxuXHRcdFx0JC5kYXRlcGlja2VyLl9hdHRhY2hEYXRlcGlja2VyKCB0aGlzLCBvcHRpb25zICk7XG5cdH0gKTtcbn07XG5cbiQuZGF0ZXBpY2tlciA9IG5ldyBEYXRlcGlja2VyKCk7IC8vIHNpbmdsZXRvbiBpbnN0YW5jZVxuJC5kYXRlcGlja2VyLmluaXRpYWxpemVkID0gZmFsc2U7XG4kLmRhdGVwaWNrZXIudXVpZCA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xuJC5kYXRlcGlja2VyLnZlcnNpb24gPSBcIjEuMTIuMVwiO1xuXG52YXIgd2lkZ2V0c0RhdGVwaWNrZXIgPSAkLmRhdGVwaWNrZXI7XG5cblxuXG5cbi8vIFRoaXMgZmlsZSBpcyBkZXByZWNhdGVkXG52YXIgaWUgPSAkLnVpLmllID0gISEvbXNpZSBbXFx3Ll0rLy5leGVjKCBuYXZpZ2F0b3IudXNlckFnZW50LnRvTG93ZXJDYXNlKCkgKTtcblxuLyohXG4gKiBqUXVlcnkgVUkgTW91c2UgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IE1vdXNlXG4vLz4+Z3JvdXA6IFdpZGdldHNcbi8vPj5kZXNjcmlwdGlvbjogQWJzdHJhY3RzIG1vdXNlLWJhc2VkIGludGVyYWN0aW9ucyB0byBhc3Npc3QgaW4gY3JlYXRpbmcgY2VydGFpbiB3aWRnZXRzLlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL21vdXNlL1xuXG5cblxudmFyIG1vdXNlSGFuZGxlZCA9IGZhbHNlO1xuJCggZG9jdW1lbnQgKS5vbiggXCJtb3VzZXVwXCIsIGZ1bmN0aW9uKCkge1xuXHRtb3VzZUhhbmRsZWQgPSBmYWxzZTtcbn0gKTtcblxudmFyIHdpZGdldHNNb3VzZSA9ICQud2lkZ2V0KCBcInVpLm1vdXNlXCIsIHtcblx0dmVyc2lvbjogXCIxLjEyLjFcIixcblx0b3B0aW9uczoge1xuXHRcdGNhbmNlbDogXCJpbnB1dCwgdGV4dGFyZWEsIGJ1dHRvbiwgc2VsZWN0LCBvcHRpb25cIixcblx0XHRkaXN0YW5jZTogMSxcblx0XHRkZWxheTogMFxuXHR9LFxuXHRfbW91c2VJbml0OiBmdW5jdGlvbigpIHtcblx0XHR2YXIgdGhhdCA9IHRoaXM7XG5cblx0XHR0aGlzLmVsZW1lbnRcblx0XHRcdC5vbiggXCJtb3VzZWRvd24uXCIgKyB0aGlzLndpZGdldE5hbWUsIGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0cmV0dXJuIHRoYXQuX21vdXNlRG93biggZXZlbnQgKTtcblx0XHRcdH0gKVxuXHRcdFx0Lm9uKCBcImNsaWNrLlwiICsgdGhpcy53aWRnZXROYW1lLCBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdGlmICggdHJ1ZSA9PT0gJC5kYXRhKCBldmVudC50YXJnZXQsIHRoYXQud2lkZ2V0TmFtZSArIFwiLnByZXZlbnRDbGlja0V2ZW50XCIgKSApIHtcblx0XHRcdFx0XHQkLnJlbW92ZURhdGEoIGV2ZW50LnRhcmdldCwgdGhhdC53aWRnZXROYW1lICsgXCIucHJldmVudENsaWNrRXZlbnRcIiApO1xuXHRcdFx0XHRcdGV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpO1xuXHRcdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdFx0fVxuXHRcdFx0fSApO1xuXG5cdFx0dGhpcy5zdGFydGVkID0gZmFsc2U7XG5cdH0sXG5cblx0Ly8gVE9ETzogbWFrZSBzdXJlIGRlc3Ryb3lpbmcgb25lIGluc3RhbmNlIG9mIG1vdXNlIGRvZXNuJ3QgbWVzcyB3aXRoXG5cdC8vIG90aGVyIGluc3RhbmNlcyBvZiBtb3VzZVxuXHRfbW91c2VEZXN0cm95OiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLmVsZW1lbnQub2ZmKCBcIi5cIiArIHRoaXMud2lkZ2V0TmFtZSApO1xuXHRcdGlmICggdGhpcy5fbW91c2VNb3ZlRGVsZWdhdGUgKSB7XG5cdFx0XHR0aGlzLmRvY3VtZW50XG5cdFx0XHRcdC5vZmYoIFwibW91c2Vtb3ZlLlwiICsgdGhpcy53aWRnZXROYW1lLCB0aGlzLl9tb3VzZU1vdmVEZWxlZ2F0ZSApXG5cdFx0XHRcdC5vZmYoIFwibW91c2V1cC5cIiArIHRoaXMud2lkZ2V0TmFtZSwgdGhpcy5fbW91c2VVcERlbGVnYXRlICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9tb3VzZURvd246IGZ1bmN0aW9uKCBldmVudCApIHtcblxuXHRcdC8vIGRvbid0IGxldCBtb3JlIHRoYW4gb25lIHdpZGdldCBoYW5kbGUgbW91c2VTdGFydFxuXHRcdGlmICggbW91c2VIYW5kbGVkICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHRoaXMuX21vdXNlTW92ZWQgPSBmYWxzZTtcblxuXHRcdC8vIFdlIG1heSBoYXZlIG1pc3NlZCBtb3VzZXVwIChvdXQgb2Ygd2luZG93KVxuXHRcdCggdGhpcy5fbW91c2VTdGFydGVkICYmIHRoaXMuX21vdXNlVXAoIGV2ZW50ICkgKTtcblxuXHRcdHRoaXMuX21vdXNlRG93bkV2ZW50ID0gZXZlbnQ7XG5cblx0XHR2YXIgdGhhdCA9IHRoaXMsXG5cdFx0XHRidG5Jc0xlZnQgPSAoIGV2ZW50LndoaWNoID09PSAxICksXG5cblx0XHRcdC8vIGV2ZW50LnRhcmdldC5ub2RlTmFtZSB3b3JrcyBhcm91bmQgYSBidWcgaW4gSUUgOCB3aXRoXG5cdFx0XHQvLyBkaXNhYmxlZCBpbnB1dHMgKCM3NjIwKVxuXHRcdFx0ZWxJc0NhbmNlbCA9ICggdHlwZW9mIHRoaXMub3B0aW9ucy5jYW5jZWwgPT09IFwic3RyaW5nXCIgJiYgZXZlbnQudGFyZ2V0Lm5vZGVOYW1lID9cblx0XHRcdFx0JCggZXZlbnQudGFyZ2V0ICkuY2xvc2VzdCggdGhpcy5vcHRpb25zLmNhbmNlbCApLmxlbmd0aCA6IGZhbHNlICk7XG5cdFx0aWYgKCAhYnRuSXNMZWZ0IHx8IGVsSXNDYW5jZWwgfHwgIXRoaXMuX21vdXNlQ2FwdHVyZSggZXZlbnQgKSApIHtcblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdH1cblxuXHRcdHRoaXMubW91c2VEZWxheU1ldCA9ICF0aGlzLm9wdGlvbnMuZGVsYXk7XG5cdFx0aWYgKCAhdGhpcy5tb3VzZURlbGF5TWV0ICkge1xuXHRcdFx0dGhpcy5fbW91c2VEZWxheVRpbWVyID0gc2V0VGltZW91dCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHRoYXQubW91c2VEZWxheU1ldCA9IHRydWU7XG5cdFx0XHR9LCB0aGlzLm9wdGlvbnMuZGVsYXkgKTtcblx0XHR9XG5cblx0XHRpZiAoIHRoaXMuX21vdXNlRGlzdGFuY2VNZXQoIGV2ZW50ICkgJiYgdGhpcy5fbW91c2VEZWxheU1ldCggZXZlbnQgKSApIHtcblx0XHRcdHRoaXMuX21vdXNlU3RhcnRlZCA9ICggdGhpcy5fbW91c2VTdGFydCggZXZlbnQgKSAhPT0gZmFsc2UgKTtcblx0XHRcdGlmICggIXRoaXMuX21vdXNlU3RhcnRlZCApIHtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gQ2xpY2sgZXZlbnQgbWF5IG5ldmVyIGhhdmUgZmlyZWQgKEdlY2tvICYgT3BlcmEpXG5cdFx0aWYgKCB0cnVlID09PSAkLmRhdGEoIGV2ZW50LnRhcmdldCwgdGhpcy53aWRnZXROYW1lICsgXCIucHJldmVudENsaWNrRXZlbnRcIiApICkge1xuXHRcdFx0JC5yZW1vdmVEYXRhKCBldmVudC50YXJnZXQsIHRoaXMud2lkZ2V0TmFtZSArIFwiLnByZXZlbnRDbGlja0V2ZW50XCIgKTtcblx0XHR9XG5cblx0XHQvLyBUaGVzZSBkZWxlZ2F0ZXMgYXJlIHJlcXVpcmVkIHRvIGtlZXAgY29udGV4dFxuXHRcdHRoaXMuX21vdXNlTW92ZURlbGVnYXRlID0gZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0cmV0dXJuIHRoYXQuX21vdXNlTW92ZSggZXZlbnQgKTtcblx0XHR9O1xuXHRcdHRoaXMuX21vdXNlVXBEZWxlZ2F0ZSA9IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdHJldHVybiB0aGF0Ll9tb3VzZVVwKCBldmVudCApO1xuXHRcdH07XG5cblx0XHR0aGlzLmRvY3VtZW50XG5cdFx0XHQub24oIFwibW91c2Vtb3ZlLlwiICsgdGhpcy53aWRnZXROYW1lLCB0aGlzLl9tb3VzZU1vdmVEZWxlZ2F0ZSApXG5cdFx0XHQub24oIFwibW91c2V1cC5cIiArIHRoaXMud2lkZ2V0TmFtZSwgdGhpcy5fbW91c2VVcERlbGVnYXRlICk7XG5cblx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXG5cdFx0bW91c2VIYW5kbGVkID0gdHJ1ZTtcblx0XHRyZXR1cm4gdHJ1ZTtcblx0fSxcblxuXHRfbW91c2VNb3ZlOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cblx0XHQvLyBPbmx5IGNoZWNrIGZvciBtb3VzZXVwcyBvdXRzaWRlIHRoZSBkb2N1bWVudCBpZiB5b3UndmUgbW92ZWQgaW5zaWRlIHRoZSBkb2N1bWVudFxuXHRcdC8vIGF0IGxlYXN0IG9uY2UuIFRoaXMgcHJldmVudHMgdGhlIGZpcmluZyBvZiBtb3VzZXVwIGluIHRoZSBjYXNlIG9mIElFPDksIHdoaWNoIHdpbGxcblx0XHQvLyBmaXJlIGEgbW91c2Vtb3ZlIGV2ZW50IGlmIGNvbnRlbnQgaXMgcGxhY2VkIHVuZGVyIHRoZSBjdXJzb3IuIFNlZSAjNzc3OFxuXHRcdC8vIFN1cHBvcnQ6IElFIDw5XG5cdFx0aWYgKCB0aGlzLl9tb3VzZU1vdmVkICkge1xuXG5cdFx0XHQvLyBJRSBtb3VzZXVwIGNoZWNrIC0gbW91c2V1cCBoYXBwZW5lZCB3aGVuIG1vdXNlIHdhcyBvdXQgb2Ygd2luZG93XG5cdFx0XHRpZiAoICQudWkuaWUgJiYgKCAhZG9jdW1lbnQuZG9jdW1lbnRNb2RlIHx8IGRvY3VtZW50LmRvY3VtZW50TW9kZSA8IDkgKSAmJlxuXHRcdFx0XHRcdCFldmVudC5idXR0b24gKSB7XG5cdFx0XHRcdHJldHVybiB0aGlzLl9tb3VzZVVwKCBldmVudCApO1xuXG5cdFx0XHQvLyBJZnJhbWUgbW91c2V1cCBjaGVjayAtIG1vdXNldXAgb2NjdXJyZWQgaW4gYW5vdGhlciBkb2N1bWVudFxuXHRcdFx0fSBlbHNlIGlmICggIWV2ZW50LndoaWNoICkge1xuXG5cdFx0XHRcdC8vIFN1cHBvcnQ6IFNhZmFyaSA8PTggLSA5XG5cdFx0XHRcdC8vIFNhZmFyaSBzZXRzIHdoaWNoIHRvIDAgaWYgeW91IHByZXNzIGFueSBvZiB0aGUgZm9sbG93aW5nIGtleXNcblx0XHRcdFx0Ly8gZHVyaW5nIGEgZHJhZyAoIzE0NDYxKVxuXHRcdFx0XHRpZiAoIGV2ZW50Lm9yaWdpbmFsRXZlbnQuYWx0S2V5IHx8IGV2ZW50Lm9yaWdpbmFsRXZlbnQuY3RybEtleSB8fFxuXHRcdFx0XHRcdFx0ZXZlbnQub3JpZ2luYWxFdmVudC5tZXRhS2V5IHx8IGV2ZW50Lm9yaWdpbmFsRXZlbnQuc2hpZnRLZXkgKSB7XG5cdFx0XHRcdFx0dGhpcy5pZ25vcmVNaXNzaW5nV2hpY2ggPSB0cnVlO1xuXHRcdFx0XHR9IGVsc2UgaWYgKCAhdGhpcy5pZ25vcmVNaXNzaW5nV2hpY2ggKSB7XG5cdFx0XHRcdFx0cmV0dXJuIHRoaXMuX21vdXNlVXAoIGV2ZW50ICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoIGV2ZW50LndoaWNoIHx8IGV2ZW50LmJ1dHRvbiApIHtcblx0XHRcdHRoaXMuX21vdXNlTW92ZWQgPSB0cnVlO1xuXHRcdH1cblxuXHRcdGlmICggdGhpcy5fbW91c2VTdGFydGVkICkge1xuXHRcdFx0dGhpcy5fbW91c2VEcmFnKCBldmVudCApO1xuXHRcdFx0cmV0dXJuIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLl9tb3VzZURpc3RhbmNlTWV0KCBldmVudCApICYmIHRoaXMuX21vdXNlRGVsYXlNZXQoIGV2ZW50ICkgKSB7XG5cdFx0XHR0aGlzLl9tb3VzZVN0YXJ0ZWQgPVxuXHRcdFx0XHQoIHRoaXMuX21vdXNlU3RhcnQoIHRoaXMuX21vdXNlRG93bkV2ZW50LCBldmVudCApICE9PSBmYWxzZSApO1xuXHRcdFx0KCB0aGlzLl9tb3VzZVN0YXJ0ZWQgPyB0aGlzLl9tb3VzZURyYWcoIGV2ZW50ICkgOiB0aGlzLl9tb3VzZVVwKCBldmVudCApICk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuICF0aGlzLl9tb3VzZVN0YXJ0ZWQ7XG5cdH0sXG5cblx0X21vdXNlVXA6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR0aGlzLmRvY3VtZW50XG5cdFx0XHQub2ZmKCBcIm1vdXNlbW92ZS5cIiArIHRoaXMud2lkZ2V0TmFtZSwgdGhpcy5fbW91c2VNb3ZlRGVsZWdhdGUgKVxuXHRcdFx0Lm9mZiggXCJtb3VzZXVwLlwiICsgdGhpcy53aWRnZXROYW1lLCB0aGlzLl9tb3VzZVVwRGVsZWdhdGUgKTtcblxuXHRcdGlmICggdGhpcy5fbW91c2VTdGFydGVkICkge1xuXHRcdFx0dGhpcy5fbW91c2VTdGFydGVkID0gZmFsc2U7XG5cblx0XHRcdGlmICggZXZlbnQudGFyZ2V0ID09PSB0aGlzLl9tb3VzZURvd25FdmVudC50YXJnZXQgKSB7XG5cdFx0XHRcdCQuZGF0YSggZXZlbnQudGFyZ2V0LCB0aGlzLndpZGdldE5hbWUgKyBcIi5wcmV2ZW50Q2xpY2tFdmVudFwiLCB0cnVlICk7XG5cdFx0XHR9XG5cblx0XHRcdHRoaXMuX21vdXNlU3RvcCggZXZlbnQgKTtcblx0XHR9XG5cblx0XHRpZiAoIHRoaXMuX21vdXNlRGVsYXlUaW1lciApIHtcblx0XHRcdGNsZWFyVGltZW91dCggdGhpcy5fbW91c2VEZWxheVRpbWVyICk7XG5cdFx0XHRkZWxldGUgdGhpcy5fbW91c2VEZWxheVRpbWVyO1xuXHRcdH1cblxuXHRcdHRoaXMuaWdub3JlTWlzc2luZ1doaWNoID0gZmFsc2U7XG5cdFx0bW91c2VIYW5kbGVkID0gZmFsc2U7XG5cdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0fSxcblxuXHRfbW91c2VEaXN0YW5jZU1ldDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHJldHVybiAoIE1hdGgubWF4KFxuXHRcdFx0XHRNYXRoLmFicyggdGhpcy5fbW91c2VEb3duRXZlbnQucGFnZVggLSBldmVudC5wYWdlWCApLFxuXHRcdFx0XHRNYXRoLmFicyggdGhpcy5fbW91c2VEb3duRXZlbnQucGFnZVkgLSBldmVudC5wYWdlWSApXG5cdFx0XHQpID49IHRoaXMub3B0aW9ucy5kaXN0YW5jZVxuXHRcdCk7XG5cdH0sXG5cblx0X21vdXNlRGVsYXlNZXQ6IGZ1bmN0aW9uKCAvKiBldmVudCAqLyApIHtcblx0XHRyZXR1cm4gdGhpcy5tb3VzZURlbGF5TWV0O1xuXHR9LFxuXG5cdC8vIFRoZXNlIGFyZSBwbGFjZWhvbGRlciBtZXRob2RzLCB0byBiZSBvdmVycmlkZW4gYnkgZXh0ZW5kaW5nIHBsdWdpblxuXHRfbW91c2VTdGFydDogZnVuY3Rpb24oIC8qIGV2ZW50ICovICkge30sXG5cdF9tb3VzZURyYWc6IGZ1bmN0aW9uKCAvKiBldmVudCAqLyApIHt9LFxuXHRfbW91c2VTdG9wOiBmdW5jdGlvbiggLyogZXZlbnQgKi8gKSB7fSxcblx0X21vdXNlQ2FwdHVyZTogZnVuY3Rpb24oIC8qIGV2ZW50ICovICkgeyByZXR1cm4gdHJ1ZTsgfVxufSApO1xuXG5cblxuXG4vLyAkLnVpLnBsdWdpbiBpcyBkZXByZWNhdGVkLiBVc2UgJC53aWRnZXQoKSBleHRlbnNpb25zIGluc3RlYWQuXG52YXIgcGx1Z2luID0gJC51aS5wbHVnaW4gPSB7XG5cdGFkZDogZnVuY3Rpb24oIG1vZHVsZSwgb3B0aW9uLCBzZXQgKSB7XG5cdFx0dmFyIGksXG5cdFx0XHRwcm90byA9ICQudWlbIG1vZHVsZSBdLnByb3RvdHlwZTtcblx0XHRmb3IgKCBpIGluIHNldCApIHtcblx0XHRcdHByb3RvLnBsdWdpbnNbIGkgXSA9IHByb3RvLnBsdWdpbnNbIGkgXSB8fCBbXTtcblx0XHRcdHByb3RvLnBsdWdpbnNbIGkgXS5wdXNoKCBbIG9wdGlvbiwgc2V0WyBpIF0gXSApO1xuXHRcdH1cblx0fSxcblx0Y2FsbDogZnVuY3Rpb24oIGluc3RhbmNlLCBuYW1lLCBhcmdzLCBhbGxvd0Rpc2Nvbm5lY3RlZCApIHtcblx0XHR2YXIgaSxcblx0XHRcdHNldCA9IGluc3RhbmNlLnBsdWdpbnNbIG5hbWUgXTtcblxuXHRcdGlmICggIXNldCApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoICFhbGxvd0Rpc2Nvbm5lY3RlZCAmJiAoICFpbnN0YW5jZS5lbGVtZW50WyAwIF0ucGFyZW50Tm9kZSB8fFxuXHRcdFx0XHRpbnN0YW5jZS5lbGVtZW50WyAwIF0ucGFyZW50Tm9kZS5ub2RlVHlwZSA9PT0gMTEgKSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRmb3IgKCBpID0gMDsgaSA8IHNldC5sZW5ndGg7IGkrKyApIHtcblx0XHRcdGlmICggaW5zdGFuY2Uub3B0aW9uc1sgc2V0WyBpIF1bIDAgXSBdICkge1xuXHRcdFx0XHRzZXRbIGkgXVsgMSBdLmFwcGx5KCBpbnN0YW5jZS5lbGVtZW50LCBhcmdzICk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG59O1xuXG5cblxudmFyIHNhZmVCbHVyID0gJC51aS5zYWZlQmx1ciA9IGZ1bmN0aW9uKCBlbGVtZW50ICkge1xuXG5cdC8vIFN1cHBvcnQ6IElFOSAtIDEwIG9ubHlcblx0Ly8gSWYgdGhlIDxib2R5PiBpcyBibHVycmVkLCBJRSB3aWxsIHN3aXRjaCB3aW5kb3dzLCBzZWUgIzk0MjBcblx0aWYgKCBlbGVtZW50ICYmIGVsZW1lbnQubm9kZU5hbWUudG9Mb3dlckNhc2UoKSAhPT0gXCJib2R5XCIgKSB7XG5cdFx0JCggZWxlbWVudCApLnRyaWdnZXIoIFwiYmx1clwiICk7XG5cdH1cbn07XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgRHJhZ2dhYmxlIDEuMTIuMVxuICogaHR0cDovL2pxdWVyeXVpLmNvbVxuICpcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzXG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UuXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKi9cblxuLy8+PmxhYmVsOiBEcmFnZ2FibGVcbi8vPj5ncm91cDogSW50ZXJhY3Rpb25zXG4vLz4+ZGVzY3JpcHRpb246IEVuYWJsZXMgZHJhZ2dpbmcgZnVuY3Rpb25hbGl0eSBmb3IgYW55IGVsZW1lbnQuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vZHJhZ2dhYmxlL1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL2RyYWdnYWJsZS9cbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9kcmFnZ2FibGUuY3NzXG5cblxuXG4kLndpZGdldCggXCJ1aS5kcmFnZ2FibGVcIiwgJC51aS5tb3VzZSwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHR3aWRnZXRFdmVudFByZWZpeDogXCJkcmFnXCIsXG5cdG9wdGlvbnM6IHtcblx0XHRhZGRDbGFzc2VzOiB0cnVlLFxuXHRcdGFwcGVuZFRvOiBcInBhcmVudFwiLFxuXHRcdGF4aXM6IGZhbHNlLFxuXHRcdGNvbm5lY3RUb1NvcnRhYmxlOiBmYWxzZSxcblx0XHRjb250YWlubWVudDogZmFsc2UsXG5cdFx0Y3Vyc29yOiBcImF1dG9cIixcblx0XHRjdXJzb3JBdDogZmFsc2UsXG5cdFx0Z3JpZDogZmFsc2UsXG5cdFx0aGFuZGxlOiBmYWxzZSxcblx0XHRoZWxwZXI6IFwib3JpZ2luYWxcIixcblx0XHRpZnJhbWVGaXg6IGZhbHNlLFxuXHRcdG9wYWNpdHk6IGZhbHNlLFxuXHRcdHJlZnJlc2hQb3NpdGlvbnM6IGZhbHNlLFxuXHRcdHJldmVydDogZmFsc2UsXG5cdFx0cmV2ZXJ0RHVyYXRpb246IDUwMCxcblx0XHRzY29wZTogXCJkZWZhdWx0XCIsXG5cdFx0c2Nyb2xsOiB0cnVlLFxuXHRcdHNjcm9sbFNlbnNpdGl2aXR5OiAyMCxcblx0XHRzY3JvbGxTcGVlZDogMjAsXG5cdFx0c25hcDogZmFsc2UsXG5cdFx0c25hcE1vZGU6IFwiYm90aFwiLFxuXHRcdHNuYXBUb2xlcmFuY2U6IDIwLFxuXHRcdHN0YWNrOiBmYWxzZSxcblx0XHR6SW5kZXg6IGZhbHNlLFxuXG5cdFx0Ly8gQ2FsbGJhY2tzXG5cdFx0ZHJhZzogbnVsbCxcblx0XHRzdGFydDogbnVsbCxcblx0XHRzdG9wOiBudWxsXG5cdH0sXG5cdF9jcmVhdGU6IGZ1bmN0aW9uKCkge1xuXG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMuaGVscGVyID09PSBcIm9yaWdpbmFsXCIgKSB7XG5cdFx0XHR0aGlzLl9zZXRQb3NpdGlvblJlbGF0aXZlKCk7XG5cdFx0fVxuXHRcdGlmICggdGhpcy5vcHRpb25zLmFkZENsYXNzZXMgKSB7XG5cdFx0XHR0aGlzLl9hZGRDbGFzcyggXCJ1aS1kcmFnZ2FibGVcIiApO1xuXHRcdH1cblx0XHR0aGlzLl9zZXRIYW5kbGVDbGFzc05hbWUoKTtcblxuXHRcdHRoaXMuX21vdXNlSW5pdCgpO1xuXHR9LFxuXG5cdF9zZXRPcHRpb246IGZ1bmN0aW9uKCBrZXksIHZhbHVlICkge1xuXHRcdHRoaXMuX3N1cGVyKCBrZXksIHZhbHVlICk7XG5cdFx0aWYgKCBrZXkgPT09IFwiaGFuZGxlXCIgKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVIYW5kbGVDbGFzc05hbWUoKTtcblx0XHRcdHRoaXMuX3NldEhhbmRsZUNsYXNzTmFtZSgpO1xuXHRcdH1cblx0fSxcblxuXHRfZGVzdHJveTogZnVuY3Rpb24oKSB7XG5cdFx0aWYgKCAoIHRoaXMuaGVscGVyIHx8IHRoaXMuZWxlbWVudCApLmlzKCBcIi51aS1kcmFnZ2FibGUtZHJhZ2dpbmdcIiApICkge1xuXHRcdFx0dGhpcy5kZXN0cm95T25DbGVhciA9IHRydWU7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdHRoaXMuX3JlbW92ZUhhbmRsZUNsYXNzTmFtZSgpO1xuXHRcdHRoaXMuX21vdXNlRGVzdHJveSgpO1xuXHR9LFxuXG5cdF9tb3VzZUNhcHR1cmU6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgbyA9IHRoaXMub3B0aW9ucztcblxuXHRcdC8vIEFtb25nIG90aGVycywgcHJldmVudCBhIGRyYWcgb24gYSByZXNpemFibGUtaGFuZGxlXG5cdFx0aWYgKCB0aGlzLmhlbHBlciB8fCBvLmRpc2FibGVkIHx8XG5cdFx0XHRcdCQoIGV2ZW50LnRhcmdldCApLmNsb3Nlc3QoIFwiLnVpLXJlc2l6YWJsZS1oYW5kbGVcIiApLmxlbmd0aCA+IDAgKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXG5cdFx0Ly9RdWl0IGlmIHdlJ3JlIG5vdCBvbiBhIHZhbGlkIGhhbmRsZVxuXHRcdHRoaXMuaGFuZGxlID0gdGhpcy5fZ2V0SGFuZGxlKCBldmVudCApO1xuXHRcdGlmICggIXRoaXMuaGFuZGxlICkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdHRoaXMuX2JsdXJBY3RpdmVFbGVtZW50KCBldmVudCApO1xuXG5cdFx0dGhpcy5fYmxvY2tGcmFtZXMoIG8uaWZyYW1lRml4ID09PSB0cnVlID8gXCJpZnJhbWVcIiA6IG8uaWZyYW1lRml4ICk7XG5cblx0XHRyZXR1cm4gdHJ1ZTtcblxuXHR9LFxuXG5cdF9ibG9ja0ZyYW1lczogZnVuY3Rpb24oIHNlbGVjdG9yICkge1xuXHRcdHRoaXMuaWZyYW1lQmxvY2tzID0gdGhpcy5kb2N1bWVudC5maW5kKCBzZWxlY3RvciApLm1hcCggZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgaWZyYW1lID0gJCggdGhpcyApO1xuXG5cdFx0XHRyZXR1cm4gJCggXCI8ZGl2PlwiIClcblx0XHRcdFx0LmNzcyggXCJwb3NpdGlvblwiLCBcImFic29sdXRlXCIgKVxuXHRcdFx0XHQuYXBwZW5kVG8oIGlmcmFtZS5wYXJlbnQoKSApXG5cdFx0XHRcdC5vdXRlcldpZHRoKCBpZnJhbWUub3V0ZXJXaWR0aCgpIClcblx0XHRcdFx0Lm91dGVySGVpZ2h0KCBpZnJhbWUub3V0ZXJIZWlnaHQoKSApXG5cdFx0XHRcdC5vZmZzZXQoIGlmcmFtZS5vZmZzZXQoKSApWyAwIF07XG5cdFx0fSApO1xuXHR9LFxuXG5cdF91bmJsb2NrRnJhbWVzOiBmdW5jdGlvbigpIHtcblx0XHRpZiAoIHRoaXMuaWZyYW1lQmxvY2tzICkge1xuXHRcdFx0dGhpcy5pZnJhbWVCbG9ja3MucmVtb3ZlKCk7XG5cdFx0XHRkZWxldGUgdGhpcy5pZnJhbWVCbG9ja3M7XG5cdFx0fVxuXHR9LFxuXG5cdF9ibHVyQWN0aXZlRWxlbWVudDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHZhciBhY3RpdmVFbGVtZW50ID0gJC51aS5zYWZlQWN0aXZlRWxlbWVudCggdGhpcy5kb2N1bWVudFsgMCBdICksXG5cdFx0XHR0YXJnZXQgPSAkKCBldmVudC50YXJnZXQgKTtcblxuXHRcdC8vIERvbid0IGJsdXIgaWYgdGhlIGV2ZW50IG9jY3VycmVkIG9uIGFuIGVsZW1lbnQgdGhhdCBpcyB3aXRoaW5cblx0XHQvLyB0aGUgY3VycmVudGx5IGZvY3VzZWQgZWxlbWVudFxuXHRcdC8vIFNlZSAjMTA1MjcsICMxMjQ3MlxuXHRcdGlmICggdGFyZ2V0LmNsb3Nlc3QoIGFjdGl2ZUVsZW1lbnQgKS5sZW5ndGggKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gQmx1ciBhbnkgZWxlbWVudCB0aGF0IGN1cnJlbnRseSBoYXMgZm9jdXMsIHNlZSAjNDI2MVxuXHRcdCQudWkuc2FmZUJsdXIoIGFjdGl2ZUVsZW1lbnQgKTtcblx0fSxcblxuXHRfbW91c2VTdGFydDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXG5cdFx0dmFyIG8gPSB0aGlzLm9wdGlvbnM7XG5cblx0XHQvL0NyZWF0ZSBhbmQgYXBwZW5kIHRoZSB2aXNpYmxlIGhlbHBlclxuXHRcdHRoaXMuaGVscGVyID0gdGhpcy5fY3JlYXRlSGVscGVyKCBldmVudCApO1xuXG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMuaGVscGVyLCBcInVpLWRyYWdnYWJsZS1kcmFnZ2luZ1wiICk7XG5cblx0XHQvL0NhY2hlIHRoZSBoZWxwZXIgc2l6ZVxuXHRcdHRoaXMuX2NhY2hlSGVscGVyUHJvcG9ydGlvbnMoKTtcblxuXHRcdC8vSWYgZGRtYW5hZ2VyIGlzIHVzZWQgZm9yIGRyb3BwYWJsZXMsIHNldCB0aGUgZ2xvYmFsIGRyYWdnYWJsZVxuXHRcdGlmICggJC51aS5kZG1hbmFnZXIgKSB7XG5cdFx0XHQkLnVpLmRkbWFuYWdlci5jdXJyZW50ID0gdGhpcztcblx0XHR9XG5cblx0XHQvKlxuXHRcdCAqIC0gUG9zaXRpb24gZ2VuZXJhdGlvbiAtXG5cdFx0ICogVGhpcyBibG9jayBnZW5lcmF0ZXMgZXZlcnl0aGluZyBwb3NpdGlvbiByZWxhdGVkIC0gaXQncyB0aGUgY29yZSBvZiBkcmFnZ2FibGVzLlxuXHRcdCAqL1xuXG5cdFx0Ly9DYWNoZSB0aGUgbWFyZ2lucyBvZiB0aGUgb3JpZ2luYWwgZWxlbWVudFxuXHRcdHRoaXMuX2NhY2hlTWFyZ2lucygpO1xuXG5cdFx0Ly9TdG9yZSB0aGUgaGVscGVyJ3MgY3NzIHBvc2l0aW9uXG5cdFx0dGhpcy5jc3NQb3NpdGlvbiA9IHRoaXMuaGVscGVyLmNzcyggXCJwb3NpdGlvblwiICk7XG5cdFx0dGhpcy5zY3JvbGxQYXJlbnQgPSB0aGlzLmhlbHBlci5zY3JvbGxQYXJlbnQoIHRydWUgKTtcblx0XHR0aGlzLm9mZnNldFBhcmVudCA9IHRoaXMuaGVscGVyLm9mZnNldFBhcmVudCgpO1xuXHRcdHRoaXMuaGFzRml4ZWRBbmNlc3RvciA9IHRoaXMuaGVscGVyLnBhcmVudHMoKS5maWx0ZXIoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRyZXR1cm4gJCggdGhpcyApLmNzcyggXCJwb3NpdGlvblwiICkgPT09IFwiZml4ZWRcIjtcblx0XHRcdH0gKS5sZW5ndGggPiAwO1xuXG5cdFx0Ly9UaGUgZWxlbWVudCdzIGFic29sdXRlIHBvc2l0aW9uIG9uIHRoZSBwYWdlIG1pbnVzIG1hcmdpbnNcblx0XHR0aGlzLnBvc2l0aW9uQWJzID0gdGhpcy5lbGVtZW50Lm9mZnNldCgpO1xuXHRcdHRoaXMuX3JlZnJlc2hPZmZzZXRzKCBldmVudCApO1xuXG5cdFx0Ly9HZW5lcmF0ZSB0aGUgb3JpZ2luYWwgcG9zaXRpb25cblx0XHR0aGlzLm9yaWdpbmFsUG9zaXRpb24gPSB0aGlzLnBvc2l0aW9uID0gdGhpcy5fZ2VuZXJhdGVQb3NpdGlvbiggZXZlbnQsIGZhbHNlICk7XG5cdFx0dGhpcy5vcmlnaW5hbFBhZ2VYID0gZXZlbnQucGFnZVg7XG5cdFx0dGhpcy5vcmlnaW5hbFBhZ2VZID0gZXZlbnQucGFnZVk7XG5cblx0XHQvL0FkanVzdCB0aGUgbW91c2Ugb2Zmc2V0IHJlbGF0aXZlIHRvIHRoZSBoZWxwZXIgaWYgXCJjdXJzb3JBdFwiIGlzIHN1cHBsaWVkXG5cdFx0KCBvLmN1cnNvckF0ICYmIHRoaXMuX2FkanVzdE9mZnNldEZyb21IZWxwZXIoIG8uY3Vyc29yQXQgKSApO1xuXG5cdFx0Ly9TZXQgYSBjb250YWlubWVudCBpZiBnaXZlbiBpbiB0aGUgb3B0aW9uc1xuXHRcdHRoaXMuX3NldENvbnRhaW5tZW50KCk7XG5cblx0XHQvL1RyaWdnZXIgZXZlbnQgKyBjYWxsYmFja3Ncblx0XHRpZiAoIHRoaXMuX3RyaWdnZXIoIFwic3RhcnRcIiwgZXZlbnQgKSA9PT0gZmFsc2UgKSB7XG5cdFx0XHR0aGlzLl9jbGVhcigpO1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdC8vUmVjYWNoZSB0aGUgaGVscGVyIHNpemVcblx0XHR0aGlzLl9jYWNoZUhlbHBlclByb3BvcnRpb25zKCk7XG5cblx0XHQvL1ByZXBhcmUgdGhlIGRyb3BwYWJsZSBvZmZzZXRzXG5cdFx0aWYgKCAkLnVpLmRkbWFuYWdlciAmJiAhby5kcm9wQmVoYXZpb3VyICkge1xuXHRcdFx0JC51aS5kZG1hbmFnZXIucHJlcGFyZU9mZnNldHMoIHRoaXMsIGV2ZW50ICk7XG5cdFx0fVxuXG5cdFx0Ly8gRXhlY3V0ZSB0aGUgZHJhZyBvbmNlIC0gdGhpcyBjYXVzZXMgdGhlIGhlbHBlciBub3QgdG8gYmUgdmlzaWJsZSBiZWZvcmUgZ2V0dGluZyBpdHNcblx0XHQvLyBjb3JyZWN0IHBvc2l0aW9uXG5cdFx0dGhpcy5fbW91c2VEcmFnKCBldmVudCwgdHJ1ZSApO1xuXG5cdFx0Ly8gSWYgdGhlIGRkbWFuYWdlciBpcyB1c2VkIGZvciBkcm9wcGFibGVzLCBpbmZvcm0gdGhlIG1hbmFnZXIgdGhhdCBkcmFnZ2luZyBoYXMgc3RhcnRlZFxuXHRcdC8vIChzZWUgIzUwMDMpXG5cdFx0aWYgKCAkLnVpLmRkbWFuYWdlciApIHtcblx0XHRcdCQudWkuZGRtYW5hZ2VyLmRyYWdTdGFydCggdGhpcywgZXZlbnQgKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gdHJ1ZTtcblx0fSxcblxuXHRfcmVmcmVzaE9mZnNldHM6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR0aGlzLm9mZnNldCA9IHtcblx0XHRcdHRvcDogdGhpcy5wb3NpdGlvbkFicy50b3AgLSB0aGlzLm1hcmdpbnMudG9wLFxuXHRcdFx0bGVmdDogdGhpcy5wb3NpdGlvbkFicy5sZWZ0IC0gdGhpcy5tYXJnaW5zLmxlZnQsXG5cdFx0XHRzY3JvbGw6IGZhbHNlLFxuXHRcdFx0cGFyZW50OiB0aGlzLl9nZXRQYXJlbnRPZmZzZXQoKSxcblx0XHRcdHJlbGF0aXZlOiB0aGlzLl9nZXRSZWxhdGl2ZU9mZnNldCgpXG5cdFx0fTtcblxuXHRcdHRoaXMub2Zmc2V0LmNsaWNrID0ge1xuXHRcdFx0bGVmdDogZXZlbnQucGFnZVggLSB0aGlzLm9mZnNldC5sZWZ0LFxuXHRcdFx0dG9wOiBldmVudC5wYWdlWSAtIHRoaXMub2Zmc2V0LnRvcFxuXHRcdH07XG5cdH0sXG5cblx0X21vdXNlRHJhZzogZnVuY3Rpb24oIGV2ZW50LCBub1Byb3BhZ2F0aW9uICkge1xuXG5cdFx0Ly8gcmVzZXQgYW55IG5lY2Vzc2FyeSBjYWNoZWQgcHJvcGVydGllcyAoc2VlICM1MDA5KVxuXHRcdGlmICggdGhpcy5oYXNGaXhlZEFuY2VzdG9yICkge1xuXHRcdFx0dGhpcy5vZmZzZXQucGFyZW50ID0gdGhpcy5fZ2V0UGFyZW50T2Zmc2V0KCk7XG5cdFx0fVxuXG5cdFx0Ly9Db21wdXRlIHRoZSBoZWxwZXJzIHBvc2l0aW9uXG5cdFx0dGhpcy5wb3NpdGlvbiA9IHRoaXMuX2dlbmVyYXRlUG9zaXRpb24oIGV2ZW50LCB0cnVlICk7XG5cdFx0dGhpcy5wb3NpdGlvbkFicyA9IHRoaXMuX2NvbnZlcnRQb3NpdGlvblRvKCBcImFic29sdXRlXCIgKTtcblxuXHRcdC8vQ2FsbCBwbHVnaW5zIGFuZCBjYWxsYmFja3MgYW5kIHVzZSB0aGUgcmVzdWx0aW5nIHBvc2l0aW9uIGlmIHNvbWV0aGluZyBpcyByZXR1cm5lZFxuXHRcdGlmICggIW5vUHJvcGFnYXRpb24gKSB7XG5cdFx0XHR2YXIgdWkgPSB0aGlzLl91aUhhc2goKTtcblx0XHRcdGlmICggdGhpcy5fdHJpZ2dlciggXCJkcmFnXCIsIGV2ZW50LCB1aSApID09PSBmYWxzZSApIHtcblx0XHRcdFx0dGhpcy5fbW91c2VVcCggbmV3ICQuRXZlbnQoIFwibW91c2V1cFwiLCBldmVudCApICk7XG5cdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdH1cblx0XHRcdHRoaXMucG9zaXRpb24gPSB1aS5wb3NpdGlvbjtcblx0XHR9XG5cblx0XHR0aGlzLmhlbHBlclsgMCBdLnN0eWxlLmxlZnQgPSB0aGlzLnBvc2l0aW9uLmxlZnQgKyBcInB4XCI7XG5cdFx0dGhpcy5oZWxwZXJbIDAgXS5zdHlsZS50b3AgPSB0aGlzLnBvc2l0aW9uLnRvcCArIFwicHhcIjtcblxuXHRcdGlmICggJC51aS5kZG1hbmFnZXIgKSB7XG5cdFx0XHQkLnVpLmRkbWFuYWdlci5kcmFnKCB0aGlzLCBldmVudCApO1xuXHRcdH1cblxuXHRcdHJldHVybiBmYWxzZTtcblx0fSxcblxuXHRfbW91c2VTdG9wOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cblx0XHQvL0lmIHdlIGFyZSB1c2luZyBkcm9wcGFibGVzLCBpbmZvcm0gdGhlIG1hbmFnZXIgYWJvdXQgdGhlIGRyb3Bcblx0XHR2YXIgdGhhdCA9IHRoaXMsXG5cdFx0XHRkcm9wcGVkID0gZmFsc2U7XG5cdFx0aWYgKCAkLnVpLmRkbWFuYWdlciAmJiAhdGhpcy5vcHRpb25zLmRyb3BCZWhhdmlvdXIgKSB7XG5cdFx0XHRkcm9wcGVkID0gJC51aS5kZG1hbmFnZXIuZHJvcCggdGhpcywgZXZlbnQgKTtcblx0XHR9XG5cblx0XHQvL2lmIGEgZHJvcCBjb21lcyBmcm9tIG91dHNpZGUgKGEgc29ydGFibGUpXG5cdFx0aWYgKCB0aGlzLmRyb3BwZWQgKSB7XG5cdFx0XHRkcm9wcGVkID0gdGhpcy5kcm9wcGVkO1xuXHRcdFx0dGhpcy5kcm9wcGVkID0gZmFsc2U7XG5cdFx0fVxuXG5cdFx0aWYgKCAoIHRoaXMub3B0aW9ucy5yZXZlcnQgPT09IFwiaW52YWxpZFwiICYmICFkcm9wcGVkICkgfHxcblx0XHRcdFx0KCB0aGlzLm9wdGlvbnMucmV2ZXJ0ID09PSBcInZhbGlkXCIgJiYgZHJvcHBlZCApIHx8XG5cdFx0XHRcdHRoaXMub3B0aW9ucy5yZXZlcnQgPT09IHRydWUgfHwgKCAkLmlzRnVuY3Rpb24oIHRoaXMub3B0aW9ucy5yZXZlcnQgKSAmJlxuXHRcdFx0XHR0aGlzLm9wdGlvbnMucmV2ZXJ0LmNhbGwoIHRoaXMuZWxlbWVudCwgZHJvcHBlZCApIClcblx0XHQpIHtcblx0XHRcdCQoIHRoaXMuaGVscGVyICkuYW5pbWF0ZShcblx0XHRcdFx0dGhpcy5vcmlnaW5hbFBvc2l0aW9uLFxuXHRcdFx0XHRwYXJzZUludCggdGhpcy5vcHRpb25zLnJldmVydER1cmF0aW9uLCAxMCApLFxuXHRcdFx0XHRmdW5jdGlvbigpIHtcblx0XHRcdFx0XHRpZiAoIHRoYXQuX3RyaWdnZXIoIFwic3RvcFwiLCBldmVudCApICE9PSBmYWxzZSApIHtcblx0XHRcdFx0XHRcdHRoYXQuX2NsZWFyKCk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHQpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRpZiAoIHRoaXMuX3RyaWdnZXIoIFwic3RvcFwiLCBldmVudCApICE9PSBmYWxzZSApIHtcblx0XHRcdFx0dGhpcy5fY2xlYXIoKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0X21vdXNlVXA6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR0aGlzLl91bmJsb2NrRnJhbWVzKCk7XG5cblx0XHQvLyBJZiB0aGUgZGRtYW5hZ2VyIGlzIHVzZWQgZm9yIGRyb3BwYWJsZXMsIGluZm9ybSB0aGUgbWFuYWdlciB0aGF0IGRyYWdnaW5nIGhhcyBzdG9wcGVkXG5cdFx0Ly8gKHNlZSAjNTAwMylcblx0XHRpZiAoICQudWkuZGRtYW5hZ2VyICkge1xuXHRcdFx0JC51aS5kZG1hbmFnZXIuZHJhZ1N0b3AoIHRoaXMsIGV2ZW50ICk7XG5cdFx0fVxuXG5cdFx0Ly8gT25seSBuZWVkIHRvIGZvY3VzIGlmIHRoZSBldmVudCBvY2N1cnJlZCBvbiB0aGUgZHJhZ2dhYmxlIGl0c2VsZiwgc2VlICMxMDUyN1xuXHRcdGlmICggdGhpcy5oYW5kbGVFbGVtZW50LmlzKCBldmVudC50YXJnZXQgKSApIHtcblxuXHRcdFx0Ly8gVGhlIGludGVyYWN0aW9uIGlzIG92ZXI7IHdoZXRoZXIgb3Igbm90IHRoZSBjbGljayByZXN1bHRlZCBpbiBhIGRyYWcsXG5cdFx0XHQvLyBmb2N1cyB0aGUgZWxlbWVudFxuXHRcdFx0dGhpcy5lbGVtZW50LnRyaWdnZXIoIFwiZm9jdXNcIiApO1xuXHRcdH1cblxuXHRcdHJldHVybiAkLnVpLm1vdXNlLnByb3RvdHlwZS5fbW91c2VVcC5jYWxsKCB0aGlzLCBldmVudCApO1xuXHR9LFxuXG5cdGNhbmNlbDogZnVuY3Rpb24oKSB7XG5cblx0XHRpZiAoIHRoaXMuaGVscGVyLmlzKCBcIi51aS1kcmFnZ2FibGUtZHJhZ2dpbmdcIiApICkge1xuXHRcdFx0dGhpcy5fbW91c2VVcCggbmV3ICQuRXZlbnQoIFwibW91c2V1cFwiLCB7IHRhcmdldDogdGhpcy5lbGVtZW50WyAwIF0gfSApICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX2NsZWFyKCk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cblx0fSxcblxuXHRfZ2V0SGFuZGxlOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0cmV0dXJuIHRoaXMub3B0aW9ucy5oYW5kbGUgP1xuXHRcdFx0ISEkKCBldmVudC50YXJnZXQgKS5jbG9zZXN0KCB0aGlzLmVsZW1lbnQuZmluZCggdGhpcy5vcHRpb25zLmhhbmRsZSApICkubGVuZ3RoIDpcblx0XHRcdHRydWU7XG5cdH0sXG5cblx0X3NldEhhbmRsZUNsYXNzTmFtZTogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5oYW5kbGVFbGVtZW50ID0gdGhpcy5vcHRpb25zLmhhbmRsZSA/XG5cdFx0XHR0aGlzLmVsZW1lbnQuZmluZCggdGhpcy5vcHRpb25zLmhhbmRsZSApIDogdGhpcy5lbGVtZW50O1xuXHRcdHRoaXMuX2FkZENsYXNzKCB0aGlzLmhhbmRsZUVsZW1lbnQsIFwidWktZHJhZ2dhYmxlLWhhbmRsZVwiICk7XG5cdH0sXG5cblx0X3JlbW92ZUhhbmRsZUNsYXNzTmFtZTogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIHRoaXMuaGFuZGxlRWxlbWVudCwgXCJ1aS1kcmFnZ2FibGUtaGFuZGxlXCIgKTtcblx0fSxcblxuXHRfY3JlYXRlSGVscGVyOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cblx0XHR2YXIgbyA9IHRoaXMub3B0aW9ucyxcblx0XHRcdGhlbHBlcklzRnVuY3Rpb24gPSAkLmlzRnVuY3Rpb24oIG8uaGVscGVyICksXG5cdFx0XHRoZWxwZXIgPSBoZWxwZXJJc0Z1bmN0aW9uID9cblx0XHRcdFx0JCggby5oZWxwZXIuYXBwbHkoIHRoaXMuZWxlbWVudFsgMCBdLCBbIGV2ZW50IF0gKSApIDpcblx0XHRcdFx0KCBvLmhlbHBlciA9PT0gXCJjbG9uZVwiID9cblx0XHRcdFx0XHR0aGlzLmVsZW1lbnQuY2xvbmUoKS5yZW1vdmVBdHRyKCBcImlkXCIgKSA6XG5cdFx0XHRcdFx0dGhpcy5lbGVtZW50ICk7XG5cblx0XHRpZiAoICFoZWxwZXIucGFyZW50cyggXCJib2R5XCIgKS5sZW5ndGggKSB7XG5cdFx0XHRoZWxwZXIuYXBwZW5kVG8oICggby5hcHBlbmRUbyA9PT0gXCJwYXJlbnRcIiA/XG5cdFx0XHRcdHRoaXMuZWxlbWVudFsgMCBdLnBhcmVudE5vZGUgOlxuXHRcdFx0XHRvLmFwcGVuZFRvICkgKTtcblx0XHR9XG5cblx0XHQvLyBIdHRwOi8vYnVncy5qcXVlcnl1aS5jb20vdGlja2V0Lzk0NDZcblx0XHQvLyBhIGhlbHBlciBmdW5jdGlvbiBjYW4gcmV0dXJuIHRoZSBvcmlnaW5hbCBlbGVtZW50XG5cdFx0Ly8gd2hpY2ggd291bGRuJ3QgaGF2ZSBiZWVuIHNldCB0byByZWxhdGl2ZSBpbiBfY3JlYXRlXG5cdFx0aWYgKCBoZWxwZXJJc0Z1bmN0aW9uICYmIGhlbHBlclsgMCBdID09PSB0aGlzLmVsZW1lbnRbIDAgXSApIHtcblx0XHRcdHRoaXMuX3NldFBvc2l0aW9uUmVsYXRpdmUoKTtcblx0XHR9XG5cblx0XHRpZiAoIGhlbHBlclsgMCBdICE9PSB0aGlzLmVsZW1lbnRbIDAgXSAmJlxuXHRcdFx0XHQhKCAvKGZpeGVkfGFic29sdXRlKS8gKS50ZXN0KCBoZWxwZXIuY3NzKCBcInBvc2l0aW9uXCIgKSApICkge1xuXHRcdFx0aGVscGVyLmNzcyggXCJwb3NpdGlvblwiLCBcImFic29sdXRlXCIgKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gaGVscGVyO1xuXG5cdH0sXG5cblx0X3NldFBvc2l0aW9uUmVsYXRpdmU6IGZ1bmN0aW9uKCkge1xuXHRcdGlmICggISggL14oPzpyfGF8ZikvICkudGVzdCggdGhpcy5lbGVtZW50LmNzcyggXCJwb3NpdGlvblwiICkgKSApIHtcblx0XHRcdHRoaXMuZWxlbWVudFsgMCBdLnN0eWxlLnBvc2l0aW9uID0gXCJyZWxhdGl2ZVwiO1xuXHRcdH1cblx0fSxcblxuXHRfYWRqdXN0T2Zmc2V0RnJvbUhlbHBlcjogZnVuY3Rpb24oIG9iaiApIHtcblx0XHRpZiAoIHR5cGVvZiBvYmogPT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRvYmogPSBvYmouc3BsaXQoIFwiIFwiICk7XG5cdFx0fVxuXHRcdGlmICggJC5pc0FycmF5KCBvYmogKSApIHtcblx0XHRcdG9iaiA9IHsgbGVmdDogK29ialsgMCBdLCB0b3A6ICtvYmpbIDEgXSB8fCAwIH07XG5cdFx0fVxuXHRcdGlmICggXCJsZWZ0XCIgaW4gb2JqICkge1xuXHRcdFx0dGhpcy5vZmZzZXQuY2xpY2subGVmdCA9IG9iai5sZWZ0ICsgdGhpcy5tYXJnaW5zLmxlZnQ7XG5cdFx0fVxuXHRcdGlmICggXCJyaWdodFwiIGluIG9iaiApIHtcblx0XHRcdHRoaXMub2Zmc2V0LmNsaWNrLmxlZnQgPSB0aGlzLmhlbHBlclByb3BvcnRpb25zLndpZHRoIC0gb2JqLnJpZ2h0ICsgdGhpcy5tYXJnaW5zLmxlZnQ7XG5cdFx0fVxuXHRcdGlmICggXCJ0b3BcIiBpbiBvYmogKSB7XG5cdFx0XHR0aGlzLm9mZnNldC5jbGljay50b3AgPSBvYmoudG9wICsgdGhpcy5tYXJnaW5zLnRvcDtcblx0XHR9XG5cdFx0aWYgKCBcImJvdHRvbVwiIGluIG9iaiApIHtcblx0XHRcdHRoaXMub2Zmc2V0LmNsaWNrLnRvcCA9IHRoaXMuaGVscGVyUHJvcG9ydGlvbnMuaGVpZ2h0IC0gb2JqLmJvdHRvbSArIHRoaXMubWFyZ2lucy50b3A7XG5cdFx0fVxuXHR9LFxuXG5cdF9pc1Jvb3ROb2RlOiBmdW5jdGlvbiggZWxlbWVudCApIHtcblx0XHRyZXR1cm4gKCAvKGh0bWx8Ym9keSkvaSApLnRlc3QoIGVsZW1lbnQudGFnTmFtZSApIHx8IGVsZW1lbnQgPT09IHRoaXMuZG9jdW1lbnRbIDAgXTtcblx0fSxcblxuXHRfZ2V0UGFyZW50T2Zmc2V0OiBmdW5jdGlvbigpIHtcblxuXHRcdC8vR2V0IHRoZSBvZmZzZXRQYXJlbnQgYW5kIGNhY2hlIGl0cyBwb3NpdGlvblxuXHRcdHZhciBwbyA9IHRoaXMub2Zmc2V0UGFyZW50Lm9mZnNldCgpLFxuXHRcdFx0ZG9jdW1lbnQgPSB0aGlzLmRvY3VtZW50WyAwIF07XG5cblx0XHQvLyBUaGlzIGlzIGEgc3BlY2lhbCBjYXNlIHdoZXJlIHdlIG5lZWQgdG8gbW9kaWZ5IGEgb2Zmc2V0IGNhbGN1bGF0ZWQgb24gc3RhcnQsIHNpbmNlIHRoZVxuXHRcdC8vIGZvbGxvd2luZyBoYXBwZW5lZDpcblx0XHQvLyAxLiBUaGUgcG9zaXRpb24gb2YgdGhlIGhlbHBlciBpcyBhYnNvbHV0ZSwgc28gaXQncyBwb3NpdGlvbiBpcyBjYWxjdWxhdGVkIGJhc2VkIG9uIHRoZVxuXHRcdC8vIG5leHQgcG9zaXRpb25lZCBwYXJlbnRcblx0XHQvLyAyLiBUaGUgYWN0dWFsIG9mZnNldCBwYXJlbnQgaXMgYSBjaGlsZCBvZiB0aGUgc2Nyb2xsIHBhcmVudCwgYW5kIHRoZSBzY3JvbGwgcGFyZW50IGlzbid0XG5cdFx0Ly8gdGhlIGRvY3VtZW50LCB3aGljaCBtZWFucyB0aGF0IHRoZSBzY3JvbGwgaXMgaW5jbHVkZWQgaW4gdGhlIGluaXRpYWwgY2FsY3VsYXRpb24gb2YgdGhlXG5cdFx0Ly8gb2Zmc2V0IG9mIHRoZSBwYXJlbnQsIGFuZCBuZXZlciByZWNhbGN1bGF0ZWQgdXBvbiBkcmFnXG5cdFx0aWYgKCB0aGlzLmNzc1Bvc2l0aW9uID09PSBcImFic29sdXRlXCIgJiYgdGhpcy5zY3JvbGxQYXJlbnRbIDAgXSAhPT0gZG9jdW1lbnQgJiZcblx0XHRcdFx0JC5jb250YWlucyggdGhpcy5zY3JvbGxQYXJlbnRbIDAgXSwgdGhpcy5vZmZzZXRQYXJlbnRbIDAgXSApICkge1xuXHRcdFx0cG8ubGVmdCArPSB0aGlzLnNjcm9sbFBhcmVudC5zY3JvbGxMZWZ0KCk7XG5cdFx0XHRwby50b3AgKz0gdGhpcy5zY3JvbGxQYXJlbnQuc2Nyb2xsVG9wKCk7XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLl9pc1Jvb3ROb2RlKCB0aGlzLm9mZnNldFBhcmVudFsgMCBdICkgKSB7XG5cdFx0XHRwbyA9IHsgdG9wOiAwLCBsZWZ0OiAwIH07XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHtcblx0XHRcdHRvcDogcG8udG9wICsgKCBwYXJzZUludCggdGhpcy5vZmZzZXRQYXJlbnQuY3NzKCBcImJvcmRlclRvcFdpZHRoXCIgKSwgMTAgKSB8fCAwICksXG5cdFx0XHRsZWZ0OiBwby5sZWZ0ICsgKCBwYXJzZUludCggdGhpcy5vZmZzZXRQYXJlbnQuY3NzKCBcImJvcmRlckxlZnRXaWR0aFwiICksIDEwICkgfHwgMCApXG5cdFx0fTtcblxuXHR9LFxuXG5cdF9nZXRSZWxhdGl2ZU9mZnNldDogZnVuY3Rpb24oKSB7XG5cdFx0aWYgKCB0aGlzLmNzc1Bvc2l0aW9uICE9PSBcInJlbGF0aXZlXCIgKSB7XG5cdFx0XHRyZXR1cm4geyB0b3A6IDAsIGxlZnQ6IDAgfTtcblx0XHR9XG5cblx0XHR2YXIgcCA9IHRoaXMuZWxlbWVudC5wb3NpdGlvbigpLFxuXHRcdFx0c2Nyb2xsSXNSb290Tm9kZSA9IHRoaXMuX2lzUm9vdE5vZGUoIHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0gKTtcblxuXHRcdHJldHVybiB7XG5cdFx0XHR0b3A6IHAudG9wIC0gKCBwYXJzZUludCggdGhpcy5oZWxwZXIuY3NzKCBcInRvcFwiICksIDEwICkgfHwgMCApICtcblx0XHRcdFx0KCAhc2Nyb2xsSXNSb290Tm9kZSA/IHRoaXMuc2Nyb2xsUGFyZW50LnNjcm9sbFRvcCgpIDogMCApLFxuXHRcdFx0bGVmdDogcC5sZWZ0IC0gKCBwYXJzZUludCggdGhpcy5oZWxwZXIuY3NzKCBcImxlZnRcIiApLCAxMCApIHx8IDAgKSArXG5cdFx0XHRcdCggIXNjcm9sbElzUm9vdE5vZGUgPyB0aGlzLnNjcm9sbFBhcmVudC5zY3JvbGxMZWZ0KCkgOiAwIClcblx0XHR9O1xuXG5cdH0sXG5cblx0X2NhY2hlTWFyZ2luczogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5tYXJnaW5zID0ge1xuXHRcdFx0bGVmdDogKCBwYXJzZUludCggdGhpcy5lbGVtZW50LmNzcyggXCJtYXJnaW5MZWZ0XCIgKSwgMTAgKSB8fCAwICksXG5cdFx0XHR0b3A6ICggcGFyc2VJbnQoIHRoaXMuZWxlbWVudC5jc3MoIFwibWFyZ2luVG9wXCIgKSwgMTAgKSB8fCAwICksXG5cdFx0XHRyaWdodDogKCBwYXJzZUludCggdGhpcy5lbGVtZW50LmNzcyggXCJtYXJnaW5SaWdodFwiICksIDEwICkgfHwgMCApLFxuXHRcdFx0Ym90dG9tOiAoIHBhcnNlSW50KCB0aGlzLmVsZW1lbnQuY3NzKCBcIm1hcmdpbkJvdHRvbVwiICksIDEwICkgfHwgMCApXG5cdFx0fTtcblx0fSxcblxuXHRfY2FjaGVIZWxwZXJQcm9wb3J0aW9uczogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5oZWxwZXJQcm9wb3J0aW9ucyA9IHtcblx0XHRcdHdpZHRoOiB0aGlzLmhlbHBlci5vdXRlcldpZHRoKCksXG5cdFx0XHRoZWlnaHQ6IHRoaXMuaGVscGVyLm91dGVySGVpZ2h0KClcblx0XHR9O1xuXHR9LFxuXG5cdF9zZXRDb250YWlubWVudDogZnVuY3Rpb24oKSB7XG5cblx0XHR2YXIgaXNVc2VyU2Nyb2xsYWJsZSwgYywgY2UsXG5cdFx0XHRvID0gdGhpcy5vcHRpb25zLFxuXHRcdFx0ZG9jdW1lbnQgPSB0aGlzLmRvY3VtZW50WyAwIF07XG5cblx0XHR0aGlzLnJlbGF0aXZlQ29udGFpbmVyID0gbnVsbDtcblxuXHRcdGlmICggIW8uY29udGFpbm1lbnQgKSB7XG5cdFx0XHR0aGlzLmNvbnRhaW5tZW50ID0gbnVsbDtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoIG8uY29udGFpbm1lbnQgPT09IFwid2luZG93XCIgKSB7XG5cdFx0XHR0aGlzLmNvbnRhaW5tZW50ID0gW1xuXHRcdFx0XHQkKCB3aW5kb3cgKS5zY3JvbGxMZWZ0KCkgLSB0aGlzLm9mZnNldC5yZWxhdGl2ZS5sZWZ0IC0gdGhpcy5vZmZzZXQucGFyZW50LmxlZnQsXG5cdFx0XHRcdCQoIHdpbmRvdyApLnNjcm9sbFRvcCgpIC0gdGhpcy5vZmZzZXQucmVsYXRpdmUudG9wIC0gdGhpcy5vZmZzZXQucGFyZW50LnRvcCxcblx0XHRcdFx0JCggd2luZG93ICkuc2Nyb2xsTGVmdCgpICsgJCggd2luZG93ICkud2lkdGgoKSAtXG5cdFx0XHRcdFx0dGhpcy5oZWxwZXJQcm9wb3J0aW9ucy53aWR0aCAtIHRoaXMubWFyZ2lucy5sZWZ0LFxuXHRcdFx0XHQkKCB3aW5kb3cgKS5zY3JvbGxUb3AoKSArXG5cdFx0XHRcdFx0KCAkKCB3aW5kb3cgKS5oZWlnaHQoKSB8fCBkb2N1bWVudC5ib2R5LnBhcmVudE5vZGUuc2Nyb2xsSGVpZ2h0ICkgLVxuXHRcdFx0XHRcdHRoaXMuaGVscGVyUHJvcG9ydGlvbnMuaGVpZ2h0IC0gdGhpcy5tYXJnaW5zLnRvcFxuXHRcdFx0XTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoIG8uY29udGFpbm1lbnQgPT09IFwiZG9jdW1lbnRcIiApIHtcblx0XHRcdHRoaXMuY29udGFpbm1lbnQgPSBbXG5cdFx0XHRcdDAsXG5cdFx0XHRcdDAsXG5cdFx0XHRcdCQoIGRvY3VtZW50ICkud2lkdGgoKSAtIHRoaXMuaGVscGVyUHJvcG9ydGlvbnMud2lkdGggLSB0aGlzLm1hcmdpbnMubGVmdCxcblx0XHRcdFx0KCAkKCBkb2N1bWVudCApLmhlaWdodCgpIHx8IGRvY3VtZW50LmJvZHkucGFyZW50Tm9kZS5zY3JvbGxIZWlnaHQgKSAtXG5cdFx0XHRcdFx0dGhpcy5oZWxwZXJQcm9wb3J0aW9ucy5oZWlnaHQgLSB0aGlzLm1hcmdpbnMudG9wXG5cdFx0XHRdO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGlmICggby5jb250YWlubWVudC5jb25zdHJ1Y3RvciA9PT0gQXJyYXkgKSB7XG5cdFx0XHR0aGlzLmNvbnRhaW5tZW50ID0gby5jb250YWlubWVudDtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoIG8uY29udGFpbm1lbnQgPT09IFwicGFyZW50XCIgKSB7XG5cdFx0XHRvLmNvbnRhaW5tZW50ID0gdGhpcy5oZWxwZXJbIDAgXS5wYXJlbnROb2RlO1xuXHRcdH1cblxuXHRcdGMgPSAkKCBvLmNvbnRhaW5tZW50ICk7XG5cdFx0Y2UgPSBjWyAwIF07XG5cblx0XHRpZiAoICFjZSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpc1VzZXJTY3JvbGxhYmxlID0gLyhzY3JvbGx8YXV0bykvLnRlc3QoIGMuY3NzKCBcIm92ZXJmbG93XCIgKSApO1xuXG5cdFx0dGhpcy5jb250YWlubWVudCA9IFtcblx0XHRcdCggcGFyc2VJbnQoIGMuY3NzKCBcImJvcmRlckxlZnRXaWR0aFwiICksIDEwICkgfHwgMCApICtcblx0XHRcdFx0KCBwYXJzZUludCggYy5jc3MoIFwicGFkZGluZ0xlZnRcIiApLCAxMCApIHx8IDAgKSxcblx0XHRcdCggcGFyc2VJbnQoIGMuY3NzKCBcImJvcmRlclRvcFdpZHRoXCIgKSwgMTAgKSB8fCAwICkgK1xuXHRcdFx0XHQoIHBhcnNlSW50KCBjLmNzcyggXCJwYWRkaW5nVG9wXCIgKSwgMTAgKSB8fCAwICksXG5cdFx0XHQoIGlzVXNlclNjcm9sbGFibGUgPyBNYXRoLm1heCggY2Uuc2Nyb2xsV2lkdGgsIGNlLm9mZnNldFdpZHRoICkgOiBjZS5vZmZzZXRXaWR0aCApIC1cblx0XHRcdFx0KCBwYXJzZUludCggYy5jc3MoIFwiYm9yZGVyUmlnaHRXaWR0aFwiICksIDEwICkgfHwgMCApIC1cblx0XHRcdFx0KCBwYXJzZUludCggYy5jc3MoIFwicGFkZGluZ1JpZ2h0XCIgKSwgMTAgKSB8fCAwICkgLVxuXHRcdFx0XHR0aGlzLmhlbHBlclByb3BvcnRpb25zLndpZHRoIC1cblx0XHRcdFx0dGhpcy5tYXJnaW5zLmxlZnQgLVxuXHRcdFx0XHR0aGlzLm1hcmdpbnMucmlnaHQsXG5cdFx0XHQoIGlzVXNlclNjcm9sbGFibGUgPyBNYXRoLm1heCggY2Uuc2Nyb2xsSGVpZ2h0LCBjZS5vZmZzZXRIZWlnaHQgKSA6IGNlLm9mZnNldEhlaWdodCApIC1cblx0XHRcdFx0KCBwYXJzZUludCggYy5jc3MoIFwiYm9yZGVyQm90dG9tV2lkdGhcIiApLCAxMCApIHx8IDAgKSAtXG5cdFx0XHRcdCggcGFyc2VJbnQoIGMuY3NzKCBcInBhZGRpbmdCb3R0b21cIiApLCAxMCApIHx8IDAgKSAtXG5cdFx0XHRcdHRoaXMuaGVscGVyUHJvcG9ydGlvbnMuaGVpZ2h0IC1cblx0XHRcdFx0dGhpcy5tYXJnaW5zLnRvcCAtXG5cdFx0XHRcdHRoaXMubWFyZ2lucy5ib3R0b21cblx0XHRdO1xuXHRcdHRoaXMucmVsYXRpdmVDb250YWluZXIgPSBjO1xuXHR9LFxuXG5cdF9jb252ZXJ0UG9zaXRpb25UbzogZnVuY3Rpb24oIGQsIHBvcyApIHtcblxuXHRcdGlmICggIXBvcyApIHtcblx0XHRcdHBvcyA9IHRoaXMucG9zaXRpb247XG5cdFx0fVxuXG5cdFx0dmFyIG1vZCA9IGQgPT09IFwiYWJzb2x1dGVcIiA/IDEgOiAtMSxcblx0XHRcdHNjcm9sbElzUm9vdE5vZGUgPSB0aGlzLl9pc1Jvb3ROb2RlKCB0aGlzLnNjcm9sbFBhcmVudFsgMCBdICk7XG5cblx0XHRyZXR1cm4ge1xuXHRcdFx0dG9wOiAoXG5cblx0XHRcdFx0Ly8gVGhlIGFic29sdXRlIG1vdXNlIHBvc2l0aW9uXG5cdFx0XHRcdHBvcy50b3BcdCtcblxuXHRcdFx0XHQvLyBPbmx5IGZvciByZWxhdGl2ZSBwb3NpdGlvbmVkIG5vZGVzOiBSZWxhdGl2ZSBvZmZzZXQgZnJvbSBlbGVtZW50IHRvIG9mZnNldCBwYXJlbnRcblx0XHRcdFx0dGhpcy5vZmZzZXQucmVsYXRpdmUudG9wICogbW9kICtcblxuXHRcdFx0XHQvLyBUaGUgb2Zmc2V0UGFyZW50J3Mgb2Zmc2V0IHdpdGhvdXQgYm9yZGVycyAob2Zmc2V0ICsgYm9yZGVyKVxuXHRcdFx0XHR0aGlzLm9mZnNldC5wYXJlbnQudG9wICogbW9kIC1cblx0XHRcdFx0KCAoIHRoaXMuY3NzUG9zaXRpb24gPT09IFwiZml4ZWRcIiA/XG5cdFx0XHRcdFx0LXRoaXMub2Zmc2V0LnNjcm9sbC50b3AgOlxuXHRcdFx0XHRcdCggc2Nyb2xsSXNSb290Tm9kZSA/IDAgOiB0aGlzLm9mZnNldC5zY3JvbGwudG9wICkgKSAqIG1vZCApXG5cdFx0XHQpLFxuXHRcdFx0bGVmdDogKFxuXG5cdFx0XHRcdC8vIFRoZSBhYnNvbHV0ZSBtb3VzZSBwb3NpdGlvblxuXHRcdFx0XHRwb3MubGVmdCArXG5cblx0XHRcdFx0Ly8gT25seSBmb3IgcmVsYXRpdmUgcG9zaXRpb25lZCBub2RlczogUmVsYXRpdmUgb2Zmc2V0IGZyb20gZWxlbWVudCB0byBvZmZzZXQgcGFyZW50XG5cdFx0XHRcdHRoaXMub2Zmc2V0LnJlbGF0aXZlLmxlZnQgKiBtb2QgK1xuXG5cdFx0XHRcdC8vIFRoZSBvZmZzZXRQYXJlbnQncyBvZmZzZXQgd2l0aG91dCBib3JkZXJzIChvZmZzZXQgKyBib3JkZXIpXG5cdFx0XHRcdHRoaXMub2Zmc2V0LnBhcmVudC5sZWZ0ICogbW9kXHQtXG5cdFx0XHRcdCggKCB0aGlzLmNzc1Bvc2l0aW9uID09PSBcImZpeGVkXCIgP1xuXHRcdFx0XHRcdC10aGlzLm9mZnNldC5zY3JvbGwubGVmdCA6XG5cdFx0XHRcdFx0KCBzY3JvbGxJc1Jvb3ROb2RlID8gMCA6IHRoaXMub2Zmc2V0LnNjcm9sbC5sZWZ0ICkgKSAqIG1vZCApXG5cdFx0XHQpXG5cdFx0fTtcblxuXHR9LFxuXG5cdF9nZW5lcmF0ZVBvc2l0aW9uOiBmdW5jdGlvbiggZXZlbnQsIGNvbnN0cmFpblBvc2l0aW9uICkge1xuXG5cdFx0dmFyIGNvbnRhaW5tZW50LCBjbywgdG9wLCBsZWZ0LFxuXHRcdFx0byA9IHRoaXMub3B0aW9ucyxcblx0XHRcdHNjcm9sbElzUm9vdE5vZGUgPSB0aGlzLl9pc1Jvb3ROb2RlKCB0aGlzLnNjcm9sbFBhcmVudFsgMCBdICksXG5cdFx0XHRwYWdlWCA9IGV2ZW50LnBhZ2VYLFxuXHRcdFx0cGFnZVkgPSBldmVudC5wYWdlWTtcblxuXHRcdC8vIENhY2hlIHRoZSBzY3JvbGxcblx0XHRpZiAoICFzY3JvbGxJc1Jvb3ROb2RlIHx8ICF0aGlzLm9mZnNldC5zY3JvbGwgKSB7XG5cdFx0XHR0aGlzLm9mZnNldC5zY3JvbGwgPSB7XG5cdFx0XHRcdHRvcDogdGhpcy5zY3JvbGxQYXJlbnQuc2Nyb2xsVG9wKCksXG5cdFx0XHRcdGxlZnQ6IHRoaXMuc2Nyb2xsUGFyZW50LnNjcm9sbExlZnQoKVxuXHRcdFx0fTtcblx0XHR9XG5cblx0XHQvKlxuXHRcdCAqIC0gUG9zaXRpb24gY29uc3RyYWluaW5nIC1cblx0XHQgKiBDb25zdHJhaW4gdGhlIHBvc2l0aW9uIHRvIGEgbWl4IG9mIGdyaWQsIGNvbnRhaW5tZW50LlxuXHRcdCAqL1xuXG5cdFx0Ly8gSWYgd2UgYXJlIG5vdCBkcmFnZ2luZyB5ZXQsIHdlIHdvbid0IGNoZWNrIGZvciBvcHRpb25zXG5cdFx0aWYgKCBjb25zdHJhaW5Qb3NpdGlvbiApIHtcblx0XHRcdGlmICggdGhpcy5jb250YWlubWVudCApIHtcblx0XHRcdFx0aWYgKCB0aGlzLnJlbGF0aXZlQ29udGFpbmVyICkge1xuXHRcdFx0XHRcdGNvID0gdGhpcy5yZWxhdGl2ZUNvbnRhaW5lci5vZmZzZXQoKTtcblx0XHRcdFx0XHRjb250YWlubWVudCA9IFtcblx0XHRcdFx0XHRcdHRoaXMuY29udGFpbm1lbnRbIDAgXSArIGNvLmxlZnQsXG5cdFx0XHRcdFx0XHR0aGlzLmNvbnRhaW5tZW50WyAxIF0gKyBjby50b3AsXG5cdFx0XHRcdFx0XHR0aGlzLmNvbnRhaW5tZW50WyAyIF0gKyBjby5sZWZ0LFxuXHRcdFx0XHRcdFx0dGhpcy5jb250YWlubWVudFsgMyBdICsgY28udG9wXG5cdFx0XHRcdFx0XTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRjb250YWlubWVudCA9IHRoaXMuY29udGFpbm1lbnQ7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIGV2ZW50LnBhZ2VYIC0gdGhpcy5vZmZzZXQuY2xpY2subGVmdCA8IGNvbnRhaW5tZW50WyAwIF0gKSB7XG5cdFx0XHRcdFx0cGFnZVggPSBjb250YWlubWVudFsgMCBdICsgdGhpcy5vZmZzZXQuY2xpY2subGVmdDtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoIGV2ZW50LnBhZ2VZIC0gdGhpcy5vZmZzZXQuY2xpY2sudG9wIDwgY29udGFpbm1lbnRbIDEgXSApIHtcblx0XHRcdFx0XHRwYWdlWSA9IGNvbnRhaW5tZW50WyAxIF0gKyB0aGlzLm9mZnNldC5jbGljay50b3A7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKCBldmVudC5wYWdlWCAtIHRoaXMub2Zmc2V0LmNsaWNrLmxlZnQgPiBjb250YWlubWVudFsgMiBdICkge1xuXHRcdFx0XHRcdHBhZ2VYID0gY29udGFpbm1lbnRbIDIgXSArIHRoaXMub2Zmc2V0LmNsaWNrLmxlZnQ7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKCBldmVudC5wYWdlWSAtIHRoaXMub2Zmc2V0LmNsaWNrLnRvcCA+IGNvbnRhaW5tZW50WyAzIF0gKSB7XG5cdFx0XHRcdFx0cGFnZVkgPSBjb250YWlubWVudFsgMyBdICsgdGhpcy5vZmZzZXQuY2xpY2sudG9wO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdGlmICggby5ncmlkICkge1xuXG5cdFx0XHRcdC8vQ2hlY2sgZm9yIGdyaWQgZWxlbWVudHMgc2V0IHRvIDAgdG8gcHJldmVudCBkaXZpZGUgYnkgMCBlcnJvciBjYXVzaW5nIGludmFsaWRcblx0XHRcdFx0Ly8gYXJndW1lbnQgZXJyb3JzIGluIElFIChzZWUgdGlja2V0ICM2OTUwKVxuXHRcdFx0XHR0b3AgPSBvLmdyaWRbIDEgXSA/IHRoaXMub3JpZ2luYWxQYWdlWSArIE1hdGgucm91bmQoICggcGFnZVkgLVxuXHRcdFx0XHRcdHRoaXMub3JpZ2luYWxQYWdlWSApIC8gby5ncmlkWyAxIF0gKSAqIG8uZ3JpZFsgMSBdIDogdGhpcy5vcmlnaW5hbFBhZ2VZO1xuXHRcdFx0XHRwYWdlWSA9IGNvbnRhaW5tZW50ID8gKCAoIHRvcCAtIHRoaXMub2Zmc2V0LmNsaWNrLnRvcCA+PSBjb250YWlubWVudFsgMSBdIHx8XG5cdFx0XHRcdFx0dG9wIC0gdGhpcy5vZmZzZXQuY2xpY2sudG9wID4gY29udGFpbm1lbnRbIDMgXSApID9cblx0XHRcdFx0XHRcdHRvcCA6XG5cdFx0XHRcdFx0XHQoICggdG9wIC0gdGhpcy5vZmZzZXQuY2xpY2sudG9wID49IGNvbnRhaW5tZW50WyAxIF0gKSA/XG5cdFx0XHRcdFx0XHRcdHRvcCAtIG8uZ3JpZFsgMSBdIDogdG9wICsgby5ncmlkWyAxIF0gKSApIDogdG9wO1xuXG5cdFx0XHRcdGxlZnQgPSBvLmdyaWRbIDAgXSA/IHRoaXMub3JpZ2luYWxQYWdlWCArXG5cdFx0XHRcdFx0TWF0aC5yb3VuZCggKCBwYWdlWCAtIHRoaXMub3JpZ2luYWxQYWdlWCApIC8gby5ncmlkWyAwIF0gKSAqIG8uZ3JpZFsgMCBdIDpcblx0XHRcdFx0XHR0aGlzLm9yaWdpbmFsUGFnZVg7XG5cdFx0XHRcdHBhZ2VYID0gY29udGFpbm1lbnQgPyAoICggbGVmdCAtIHRoaXMub2Zmc2V0LmNsaWNrLmxlZnQgPj0gY29udGFpbm1lbnRbIDAgXSB8fFxuXHRcdFx0XHRcdGxlZnQgLSB0aGlzLm9mZnNldC5jbGljay5sZWZ0ID4gY29udGFpbm1lbnRbIDIgXSApID9cblx0XHRcdFx0XHRcdGxlZnQgOlxuXHRcdFx0XHRcdFx0KCAoIGxlZnQgLSB0aGlzLm9mZnNldC5jbGljay5sZWZ0ID49IGNvbnRhaW5tZW50WyAwIF0gKSA/XG5cdFx0XHRcdFx0XHRcdGxlZnQgLSBvLmdyaWRbIDAgXSA6IGxlZnQgKyBvLmdyaWRbIDAgXSApICkgOiBsZWZ0O1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG8uYXhpcyA9PT0gXCJ5XCIgKSB7XG5cdFx0XHRcdHBhZ2VYID0gdGhpcy5vcmlnaW5hbFBhZ2VYO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG8uYXhpcyA9PT0gXCJ4XCIgKSB7XG5cdFx0XHRcdHBhZ2VZID0gdGhpcy5vcmlnaW5hbFBhZ2VZO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiB7XG5cdFx0XHR0b3A6IChcblxuXHRcdFx0XHQvLyBUaGUgYWJzb2x1dGUgbW91c2UgcG9zaXRpb25cblx0XHRcdFx0cGFnZVkgLVxuXG5cdFx0XHRcdC8vIENsaWNrIG9mZnNldCAocmVsYXRpdmUgdG8gdGhlIGVsZW1lbnQpXG5cdFx0XHRcdHRoaXMub2Zmc2V0LmNsaWNrLnRvcCAtXG5cblx0XHRcdFx0Ly8gT25seSBmb3IgcmVsYXRpdmUgcG9zaXRpb25lZCBub2RlczogUmVsYXRpdmUgb2Zmc2V0IGZyb20gZWxlbWVudCB0byBvZmZzZXQgcGFyZW50XG5cdFx0XHRcdHRoaXMub2Zmc2V0LnJlbGF0aXZlLnRvcCAtXG5cblx0XHRcdFx0Ly8gVGhlIG9mZnNldFBhcmVudCdzIG9mZnNldCB3aXRob3V0IGJvcmRlcnMgKG9mZnNldCArIGJvcmRlcilcblx0XHRcdFx0dGhpcy5vZmZzZXQucGFyZW50LnRvcCArXG5cdFx0XHRcdCggdGhpcy5jc3NQb3NpdGlvbiA9PT0gXCJmaXhlZFwiID9cblx0XHRcdFx0XHQtdGhpcy5vZmZzZXQuc2Nyb2xsLnRvcCA6XG5cdFx0XHRcdFx0KCBzY3JvbGxJc1Jvb3ROb2RlID8gMCA6IHRoaXMub2Zmc2V0LnNjcm9sbC50b3AgKSApXG5cdFx0XHQpLFxuXHRcdFx0bGVmdDogKFxuXG5cdFx0XHRcdC8vIFRoZSBhYnNvbHV0ZSBtb3VzZSBwb3NpdGlvblxuXHRcdFx0XHRwYWdlWCAtXG5cblx0XHRcdFx0Ly8gQ2xpY2sgb2Zmc2V0IChyZWxhdGl2ZSB0byB0aGUgZWxlbWVudClcblx0XHRcdFx0dGhpcy5vZmZzZXQuY2xpY2subGVmdCAtXG5cblx0XHRcdFx0Ly8gT25seSBmb3IgcmVsYXRpdmUgcG9zaXRpb25lZCBub2RlczogUmVsYXRpdmUgb2Zmc2V0IGZyb20gZWxlbWVudCB0byBvZmZzZXQgcGFyZW50XG5cdFx0XHRcdHRoaXMub2Zmc2V0LnJlbGF0aXZlLmxlZnQgLVxuXG5cdFx0XHRcdC8vIFRoZSBvZmZzZXRQYXJlbnQncyBvZmZzZXQgd2l0aG91dCBib3JkZXJzIChvZmZzZXQgKyBib3JkZXIpXG5cdFx0XHRcdHRoaXMub2Zmc2V0LnBhcmVudC5sZWZ0ICtcblx0XHRcdFx0KCB0aGlzLmNzc1Bvc2l0aW9uID09PSBcImZpeGVkXCIgP1xuXHRcdFx0XHRcdC10aGlzLm9mZnNldC5zY3JvbGwubGVmdCA6XG5cdFx0XHRcdFx0KCBzY3JvbGxJc1Jvb3ROb2RlID8gMCA6IHRoaXMub2Zmc2V0LnNjcm9sbC5sZWZ0ICkgKVxuXHRcdFx0KVxuXHRcdH07XG5cblx0fSxcblxuXHRfY2xlYXI6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0aGlzLmhlbHBlciwgXCJ1aS1kcmFnZ2FibGUtZHJhZ2dpbmdcIiApO1xuXHRcdGlmICggdGhpcy5oZWxwZXJbIDAgXSAhPT0gdGhpcy5lbGVtZW50WyAwIF0gJiYgIXRoaXMuY2FuY2VsSGVscGVyUmVtb3ZhbCApIHtcblx0XHRcdHRoaXMuaGVscGVyLnJlbW92ZSgpO1xuXHRcdH1cblx0XHR0aGlzLmhlbHBlciA9IG51bGw7XG5cdFx0dGhpcy5jYW5jZWxIZWxwZXJSZW1vdmFsID0gZmFsc2U7XG5cdFx0aWYgKCB0aGlzLmRlc3Ryb3lPbkNsZWFyICkge1xuXHRcdFx0dGhpcy5kZXN0cm95KCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8vIEZyb20gbm93IG9uIGJ1bGsgc3R1ZmYgLSBtYWlubHkgaGVscGVyc1xuXG5cdF90cmlnZ2VyOiBmdW5jdGlvbiggdHlwZSwgZXZlbnQsIHVpICkge1xuXHRcdHVpID0gdWkgfHwgdGhpcy5fdWlIYXNoKCk7XG5cdFx0JC51aS5wbHVnaW4uY2FsbCggdGhpcywgdHlwZSwgWyBldmVudCwgdWksIHRoaXMgXSwgdHJ1ZSApO1xuXG5cdFx0Ly8gQWJzb2x1dGUgcG9zaXRpb24gYW5kIG9mZnNldCAoc2VlICM2ODg0ICkgaGF2ZSB0byBiZSByZWNhbGN1bGF0ZWQgYWZ0ZXIgcGx1Z2luc1xuXHRcdGlmICggL14oZHJhZ3xzdGFydHxzdG9wKS8udGVzdCggdHlwZSApICkge1xuXHRcdFx0dGhpcy5wb3NpdGlvbkFicyA9IHRoaXMuX2NvbnZlcnRQb3NpdGlvblRvKCBcImFic29sdXRlXCIgKTtcblx0XHRcdHVpLm9mZnNldCA9IHRoaXMucG9zaXRpb25BYnM7XG5cdFx0fVxuXHRcdHJldHVybiAkLldpZGdldC5wcm90b3R5cGUuX3RyaWdnZXIuY2FsbCggdGhpcywgdHlwZSwgZXZlbnQsIHVpICk7XG5cdH0sXG5cblx0cGx1Z2luczoge30sXG5cblx0X3VpSGFzaDogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHtcblx0XHRcdGhlbHBlcjogdGhpcy5oZWxwZXIsXG5cdFx0XHRwb3NpdGlvbjogdGhpcy5wb3NpdGlvbixcblx0XHRcdG9yaWdpbmFsUG9zaXRpb246IHRoaXMub3JpZ2luYWxQb3NpdGlvbixcblx0XHRcdG9mZnNldDogdGhpcy5wb3NpdGlvbkFic1xuXHRcdH07XG5cdH1cblxufSApO1xuXG4kLnVpLnBsdWdpbi5hZGQoIFwiZHJhZ2dhYmxlXCIsIFwiY29ubmVjdFRvU29ydGFibGVcIiwge1xuXHRzdGFydDogZnVuY3Rpb24oIGV2ZW50LCB1aSwgZHJhZ2dhYmxlICkge1xuXHRcdHZhciB1aVNvcnRhYmxlID0gJC5leHRlbmQoIHt9LCB1aSwge1xuXHRcdFx0aXRlbTogZHJhZ2dhYmxlLmVsZW1lbnRcblx0XHR9ICk7XG5cblx0XHRkcmFnZ2FibGUuc29ydGFibGVzID0gW107XG5cdFx0JCggZHJhZ2dhYmxlLm9wdGlvbnMuY29ubmVjdFRvU29ydGFibGUgKS5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdHZhciBzb3J0YWJsZSA9ICQoIHRoaXMgKS5zb3J0YWJsZSggXCJpbnN0YW5jZVwiICk7XG5cblx0XHRcdGlmICggc29ydGFibGUgJiYgIXNvcnRhYmxlLm9wdGlvbnMuZGlzYWJsZWQgKSB7XG5cdFx0XHRcdGRyYWdnYWJsZS5zb3J0YWJsZXMucHVzaCggc29ydGFibGUgKTtcblxuXHRcdFx0XHQvLyBSZWZyZXNoUG9zaXRpb25zIGlzIGNhbGxlZCBhdCBkcmFnIHN0YXJ0IHRvIHJlZnJlc2ggdGhlIGNvbnRhaW5lckNhY2hlXG5cdFx0XHRcdC8vIHdoaWNoIGlzIHVzZWQgaW4gZHJhZy4gVGhpcyBlbnN1cmVzIGl0J3MgaW5pdGlhbGl6ZWQgYW5kIHN5bmNocm9uaXplZFxuXHRcdFx0XHQvLyB3aXRoIGFueSBjaGFuZ2VzIHRoYXQgbWlnaHQgaGF2ZSBoYXBwZW5lZCBvbiB0aGUgcGFnZSBzaW5jZSBpbml0aWFsaXphdGlvbi5cblx0XHRcdFx0c29ydGFibGUucmVmcmVzaFBvc2l0aW9ucygpO1xuXHRcdFx0XHRzb3J0YWJsZS5fdHJpZ2dlciggXCJhY3RpdmF0ZVwiLCBldmVudCwgdWlTb3J0YWJsZSApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblx0fSxcblx0c3RvcDogZnVuY3Rpb24oIGV2ZW50LCB1aSwgZHJhZ2dhYmxlICkge1xuXHRcdHZhciB1aVNvcnRhYmxlID0gJC5leHRlbmQoIHt9LCB1aSwge1xuXHRcdFx0aXRlbTogZHJhZ2dhYmxlLmVsZW1lbnRcblx0XHR9ICk7XG5cblx0XHRkcmFnZ2FibGUuY2FuY2VsSGVscGVyUmVtb3ZhbCA9IGZhbHNlO1xuXG5cdFx0JC5lYWNoKCBkcmFnZ2FibGUuc29ydGFibGVzLCBmdW5jdGlvbigpIHtcblx0XHRcdHZhciBzb3J0YWJsZSA9IHRoaXM7XG5cblx0XHRcdGlmICggc29ydGFibGUuaXNPdmVyICkge1xuXHRcdFx0XHRzb3J0YWJsZS5pc092ZXIgPSAwO1xuXG5cdFx0XHRcdC8vIEFsbG93IHRoaXMgc29ydGFibGUgdG8gaGFuZGxlIHJlbW92aW5nIHRoZSBoZWxwZXJcblx0XHRcdFx0ZHJhZ2dhYmxlLmNhbmNlbEhlbHBlclJlbW92YWwgPSB0cnVlO1xuXHRcdFx0XHRzb3J0YWJsZS5jYW5jZWxIZWxwZXJSZW1vdmFsID0gZmFsc2U7XG5cblx0XHRcdFx0Ly8gVXNlIF9zdG9yZWRDU1MgVG8gcmVzdG9yZSBwcm9wZXJ0aWVzIGluIHRoZSBzb3J0YWJsZSxcblx0XHRcdFx0Ly8gYXMgdGhpcyBhbHNvIGhhbmRsZXMgcmV2ZXJ0ICgjOTY3NSkgc2luY2UgdGhlIGRyYWdnYWJsZVxuXHRcdFx0XHQvLyBtYXkgaGF2ZSBtb2RpZmllZCB0aGVtIGluIHVuZXhwZWN0ZWQgd2F5cyAoIzg4MDkpXG5cdFx0XHRcdHNvcnRhYmxlLl9zdG9yZWRDU1MgPSB7XG5cdFx0XHRcdFx0cG9zaXRpb246IHNvcnRhYmxlLnBsYWNlaG9sZGVyLmNzcyggXCJwb3NpdGlvblwiICksXG5cdFx0XHRcdFx0dG9wOiBzb3J0YWJsZS5wbGFjZWhvbGRlci5jc3MoIFwidG9wXCIgKSxcblx0XHRcdFx0XHRsZWZ0OiBzb3J0YWJsZS5wbGFjZWhvbGRlci5jc3MoIFwibGVmdFwiIClcblx0XHRcdFx0fTtcblxuXHRcdFx0XHRzb3J0YWJsZS5fbW91c2VTdG9wKCBldmVudCApO1xuXG5cdFx0XHRcdC8vIE9uY2UgZHJhZyBoYXMgZW5kZWQsIHRoZSBzb3J0YWJsZSBzaG91bGQgcmV0dXJuIHRvIHVzaW5nXG5cdFx0XHRcdC8vIGl0cyBvcmlnaW5hbCBoZWxwZXIsIG5vdCB0aGUgc2hhcmVkIGhlbHBlciBmcm9tIGRyYWdnYWJsZVxuXHRcdFx0XHRzb3J0YWJsZS5vcHRpb25zLmhlbHBlciA9IHNvcnRhYmxlLm9wdGlvbnMuX2hlbHBlcjtcblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Ly8gUHJldmVudCB0aGlzIFNvcnRhYmxlIGZyb20gcmVtb3ZpbmcgdGhlIGhlbHBlci5cblx0XHRcdFx0Ly8gSG93ZXZlciwgZG9uJ3Qgc2V0IHRoZSBkcmFnZ2FibGUgdG8gcmVtb3ZlIHRoZSBoZWxwZXJcblx0XHRcdFx0Ly8gZWl0aGVyIGFzIGFub3RoZXIgY29ubmVjdGVkIFNvcnRhYmxlIG1heSB5ZXQgaGFuZGxlIHRoZSByZW1vdmFsLlxuXHRcdFx0XHRzb3J0YWJsZS5jYW5jZWxIZWxwZXJSZW1vdmFsID0gdHJ1ZTtcblxuXHRcdFx0XHRzb3J0YWJsZS5fdHJpZ2dlciggXCJkZWFjdGl2YXRlXCIsIGV2ZW50LCB1aVNvcnRhYmxlICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXHR9LFxuXHRkcmFnOiBmdW5jdGlvbiggZXZlbnQsIHVpLCBkcmFnZ2FibGUgKSB7XG5cdFx0JC5lYWNoKCBkcmFnZ2FibGUuc29ydGFibGVzLCBmdW5jdGlvbigpIHtcblx0XHRcdHZhciBpbm5lcm1vc3RJbnRlcnNlY3RpbmcgPSBmYWxzZSxcblx0XHRcdFx0c29ydGFibGUgPSB0aGlzO1xuXG5cdFx0XHQvLyBDb3B5IG92ZXIgdmFyaWFibGVzIHRoYXQgc29ydGFibGUncyBfaW50ZXJzZWN0c1dpdGggdXNlc1xuXHRcdFx0c29ydGFibGUucG9zaXRpb25BYnMgPSBkcmFnZ2FibGUucG9zaXRpb25BYnM7XG5cdFx0XHRzb3J0YWJsZS5oZWxwZXJQcm9wb3J0aW9ucyA9IGRyYWdnYWJsZS5oZWxwZXJQcm9wb3J0aW9ucztcblx0XHRcdHNvcnRhYmxlLm9mZnNldC5jbGljayA9IGRyYWdnYWJsZS5vZmZzZXQuY2xpY2s7XG5cblx0XHRcdGlmICggc29ydGFibGUuX2ludGVyc2VjdHNXaXRoKCBzb3J0YWJsZS5jb250YWluZXJDYWNoZSApICkge1xuXHRcdFx0XHRpbm5lcm1vc3RJbnRlcnNlY3RpbmcgPSB0cnVlO1xuXG5cdFx0XHRcdCQuZWFjaCggZHJhZ2dhYmxlLnNvcnRhYmxlcywgZnVuY3Rpb24oKSB7XG5cblx0XHRcdFx0XHQvLyBDb3B5IG92ZXIgdmFyaWFibGVzIHRoYXQgc29ydGFibGUncyBfaW50ZXJzZWN0c1dpdGggdXNlc1xuXHRcdFx0XHRcdHRoaXMucG9zaXRpb25BYnMgPSBkcmFnZ2FibGUucG9zaXRpb25BYnM7XG5cdFx0XHRcdFx0dGhpcy5oZWxwZXJQcm9wb3J0aW9ucyA9IGRyYWdnYWJsZS5oZWxwZXJQcm9wb3J0aW9ucztcblx0XHRcdFx0XHR0aGlzLm9mZnNldC5jbGljayA9IGRyYWdnYWJsZS5vZmZzZXQuY2xpY2s7XG5cblx0XHRcdFx0XHRpZiAoIHRoaXMgIT09IHNvcnRhYmxlICYmXG5cdFx0XHRcdFx0XHRcdHRoaXMuX2ludGVyc2VjdHNXaXRoKCB0aGlzLmNvbnRhaW5lckNhY2hlICkgJiZcblx0XHRcdFx0XHRcdFx0JC5jb250YWlucyggc29ydGFibGUuZWxlbWVudFsgMCBdLCB0aGlzLmVsZW1lbnRbIDAgXSApICkge1xuXHRcdFx0XHRcdFx0aW5uZXJtb3N0SW50ZXJzZWN0aW5nID0gZmFsc2U7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0cmV0dXJuIGlubmVybW9zdEludGVyc2VjdGluZztcblx0XHRcdFx0fSApO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGlubmVybW9zdEludGVyc2VjdGluZyApIHtcblxuXHRcdFx0XHQvLyBJZiBpdCBpbnRlcnNlY3RzLCB3ZSB1c2UgYSBsaXR0bGUgaXNPdmVyIHZhcmlhYmxlIGFuZCBzZXQgaXQgb25jZSxcblx0XHRcdFx0Ly8gc28gdGhhdCB0aGUgbW92ZS1pbiBzdHVmZiBnZXRzIGZpcmVkIG9ubHkgb25jZS5cblx0XHRcdFx0aWYgKCAhc29ydGFibGUuaXNPdmVyICkge1xuXHRcdFx0XHRcdHNvcnRhYmxlLmlzT3ZlciA9IDE7XG5cblx0XHRcdFx0XHQvLyBTdG9yZSBkcmFnZ2FibGUncyBwYXJlbnQgaW4gY2FzZSB3ZSBuZWVkIHRvIHJlYXBwZW5kIHRvIGl0IGxhdGVyLlxuXHRcdFx0XHRcdGRyYWdnYWJsZS5fcGFyZW50ID0gdWkuaGVscGVyLnBhcmVudCgpO1xuXG5cdFx0XHRcdFx0c29ydGFibGUuY3VycmVudEl0ZW0gPSB1aS5oZWxwZXJcblx0XHRcdFx0XHRcdC5hcHBlbmRUbyggc29ydGFibGUuZWxlbWVudCApXG5cdFx0XHRcdFx0XHQuZGF0YSggXCJ1aS1zb3J0YWJsZS1pdGVtXCIsIHRydWUgKTtcblxuXHRcdFx0XHRcdC8vIFN0b3JlIGhlbHBlciBvcHRpb24gdG8gbGF0ZXIgcmVzdG9yZSBpdFxuXHRcdFx0XHRcdHNvcnRhYmxlLm9wdGlvbnMuX2hlbHBlciA9IHNvcnRhYmxlLm9wdGlvbnMuaGVscGVyO1xuXG5cdFx0XHRcdFx0c29ydGFibGUub3B0aW9ucy5oZWxwZXIgPSBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHRcdHJldHVybiB1aS5oZWxwZXJbIDAgXTtcblx0XHRcdFx0XHR9O1xuXG5cdFx0XHRcdFx0Ly8gRmlyZSB0aGUgc3RhcnQgZXZlbnRzIG9mIHRoZSBzb3J0YWJsZSB3aXRoIG91ciBwYXNzZWQgYnJvd3NlciBldmVudCxcblx0XHRcdFx0XHQvLyBhbmQgb3VyIG93biBoZWxwZXIgKHNvIGl0IGRvZXNuJ3QgY3JlYXRlIGEgbmV3IG9uZSlcblx0XHRcdFx0XHRldmVudC50YXJnZXQgPSBzb3J0YWJsZS5jdXJyZW50SXRlbVsgMCBdO1xuXHRcdFx0XHRcdHNvcnRhYmxlLl9tb3VzZUNhcHR1cmUoIGV2ZW50LCB0cnVlICk7XG5cdFx0XHRcdFx0c29ydGFibGUuX21vdXNlU3RhcnQoIGV2ZW50LCB0cnVlLCB0cnVlICk7XG5cblx0XHRcdFx0XHQvLyBCZWNhdXNlIHRoZSBicm93c2VyIGV2ZW50IGlzIHdheSBvZmYgdGhlIG5ldyBhcHBlbmRlZCBwb3J0bGV0LFxuXHRcdFx0XHRcdC8vIG1vZGlmeSBuZWNlc3NhcnkgdmFyaWFibGVzIHRvIHJlZmxlY3QgdGhlIGNoYW5nZXNcblx0XHRcdFx0XHRzb3J0YWJsZS5vZmZzZXQuY2xpY2sudG9wID0gZHJhZ2dhYmxlLm9mZnNldC5jbGljay50b3A7XG5cdFx0XHRcdFx0c29ydGFibGUub2Zmc2V0LmNsaWNrLmxlZnQgPSBkcmFnZ2FibGUub2Zmc2V0LmNsaWNrLmxlZnQ7XG5cdFx0XHRcdFx0c29ydGFibGUub2Zmc2V0LnBhcmVudC5sZWZ0IC09IGRyYWdnYWJsZS5vZmZzZXQucGFyZW50LmxlZnQgLVxuXHRcdFx0XHRcdFx0c29ydGFibGUub2Zmc2V0LnBhcmVudC5sZWZ0O1xuXHRcdFx0XHRcdHNvcnRhYmxlLm9mZnNldC5wYXJlbnQudG9wIC09IGRyYWdnYWJsZS5vZmZzZXQucGFyZW50LnRvcCAtXG5cdFx0XHRcdFx0XHRzb3J0YWJsZS5vZmZzZXQucGFyZW50LnRvcDtcblxuXHRcdFx0XHRcdGRyYWdnYWJsZS5fdHJpZ2dlciggXCJ0b1NvcnRhYmxlXCIsIGV2ZW50ICk7XG5cblx0XHRcdFx0XHQvLyBJbmZvcm0gZHJhZ2dhYmxlIHRoYXQgdGhlIGhlbHBlciBpcyBpbiBhIHZhbGlkIGRyb3Agem9uZSxcblx0XHRcdFx0XHQvLyB1c2VkIHNvbGVseSBpbiB0aGUgcmV2ZXJ0IG9wdGlvbiB0byBoYW5kbGUgXCJ2YWxpZC9pbnZhbGlkXCIuXG5cdFx0XHRcdFx0ZHJhZ2dhYmxlLmRyb3BwZWQgPSBzb3J0YWJsZS5lbGVtZW50O1xuXG5cdFx0XHRcdFx0Ly8gTmVlZCB0byByZWZyZXNoUG9zaXRpb25zIG9mIGFsbCBzb3J0YWJsZXMgaW4gdGhlIGNhc2UgdGhhdFxuXHRcdFx0XHRcdC8vIGFkZGluZyB0byBvbmUgc29ydGFibGUgY2hhbmdlcyB0aGUgbG9jYXRpb24gb2YgdGhlIG90aGVyIHNvcnRhYmxlcyAoIzk2NzUpXG5cdFx0XHRcdFx0JC5lYWNoKCBkcmFnZ2FibGUuc29ydGFibGVzLCBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHRcdHRoaXMucmVmcmVzaFBvc2l0aW9ucygpO1xuXHRcdFx0XHRcdH0gKTtcblxuXHRcdFx0XHRcdC8vIEhhY2sgc28gcmVjZWl2ZS91cGRhdGUgY2FsbGJhY2tzIHdvcmsgKG1vc3RseSlcblx0XHRcdFx0XHRkcmFnZ2FibGUuY3VycmVudEl0ZW0gPSBkcmFnZ2FibGUuZWxlbWVudDtcblx0XHRcdFx0XHRzb3J0YWJsZS5mcm9tT3V0c2lkZSA9IGRyYWdnYWJsZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggc29ydGFibGUuY3VycmVudEl0ZW0gKSB7XG5cdFx0XHRcdFx0c29ydGFibGUuX21vdXNlRHJhZyggZXZlbnQgKTtcblxuXHRcdFx0XHRcdC8vIENvcHkgdGhlIHNvcnRhYmxlJ3MgcG9zaXRpb24gYmVjYXVzZSB0aGUgZHJhZ2dhYmxlJ3MgY2FuIHBvdGVudGlhbGx5IHJlZmxlY3Rcblx0XHRcdFx0XHQvLyBhIHJlbGF0aXZlIHBvc2l0aW9uLCB3aGlsZSBzb3J0YWJsZSBpcyBhbHdheXMgYWJzb2x1dGUsIHdoaWNoIHRoZSBkcmFnZ2VkXG5cdFx0XHRcdFx0Ly8gZWxlbWVudCBoYXMgbm93IGJlY29tZS4gKCM4ODA5KVxuXHRcdFx0XHRcdHVpLnBvc2l0aW9uID0gc29ydGFibGUucG9zaXRpb247XG5cdFx0XHRcdH1cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Ly8gSWYgaXQgZG9lc24ndCBpbnRlcnNlY3Qgd2l0aCB0aGUgc29ydGFibGUsIGFuZCBpdCBpbnRlcnNlY3RlZCBiZWZvcmUsXG5cdFx0XHRcdC8vIHdlIGZha2UgdGhlIGRyYWcgc3RvcCBvZiB0aGUgc29ydGFibGUsIGJ1dCBtYWtlIHN1cmUgaXQgZG9lc24ndCByZW1vdmVcblx0XHRcdFx0Ly8gdGhlIGhlbHBlciBieSB1c2luZyBjYW5jZWxIZWxwZXJSZW1vdmFsLlxuXHRcdFx0XHRpZiAoIHNvcnRhYmxlLmlzT3ZlciApIHtcblxuXHRcdFx0XHRcdHNvcnRhYmxlLmlzT3ZlciA9IDA7XG5cdFx0XHRcdFx0c29ydGFibGUuY2FuY2VsSGVscGVyUmVtb3ZhbCA9IHRydWU7XG5cblx0XHRcdFx0XHQvLyBDYWxsaW5nIHNvcnRhYmxlJ3MgbW91c2VTdG9wIHdvdWxkIHRyaWdnZXIgYSByZXZlcnQsXG5cdFx0XHRcdFx0Ly8gc28gcmV2ZXJ0IG11c3QgYmUgdGVtcG9yYXJpbHkgZmFsc2UgdW50aWwgYWZ0ZXIgbW91c2VTdG9wIGlzIGNhbGxlZC5cblx0XHRcdFx0XHRzb3J0YWJsZS5vcHRpb25zLl9yZXZlcnQgPSBzb3J0YWJsZS5vcHRpb25zLnJldmVydDtcblx0XHRcdFx0XHRzb3J0YWJsZS5vcHRpb25zLnJldmVydCA9IGZhbHNlO1xuXG5cdFx0XHRcdFx0c29ydGFibGUuX3RyaWdnZXIoIFwib3V0XCIsIGV2ZW50LCBzb3J0YWJsZS5fdWlIYXNoKCBzb3J0YWJsZSApICk7XG5cdFx0XHRcdFx0c29ydGFibGUuX21vdXNlU3RvcCggZXZlbnQsIHRydWUgKTtcblxuXHRcdFx0XHRcdC8vIFJlc3RvcmUgc29ydGFibGUgYmVoYXZpb3JzIHRoYXQgd2VyZSBtb2RmaWVkXG5cdFx0XHRcdFx0Ly8gd2hlbiB0aGUgZHJhZ2dhYmxlIGVudGVyZWQgdGhlIHNvcnRhYmxlIGFyZWEgKCM5NDgxKVxuXHRcdFx0XHRcdHNvcnRhYmxlLm9wdGlvbnMucmV2ZXJ0ID0gc29ydGFibGUub3B0aW9ucy5fcmV2ZXJ0O1xuXHRcdFx0XHRcdHNvcnRhYmxlLm9wdGlvbnMuaGVscGVyID0gc29ydGFibGUub3B0aW9ucy5faGVscGVyO1xuXG5cdFx0XHRcdFx0aWYgKCBzb3J0YWJsZS5wbGFjZWhvbGRlciApIHtcblx0XHRcdFx0XHRcdHNvcnRhYmxlLnBsYWNlaG9sZGVyLnJlbW92ZSgpO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIFJlc3RvcmUgYW5kIHJlY2FsY3VsYXRlIHRoZSBkcmFnZ2FibGUncyBvZmZzZXQgY29uc2lkZXJpbmcgdGhlIHNvcnRhYmxlXG5cdFx0XHRcdFx0Ly8gbWF5IGhhdmUgbW9kaWZpZWQgdGhlbSBpbiB1bmV4cGVjdGVkIHdheXMuICgjODgwOSwgIzEwNjY5KVxuXHRcdFx0XHRcdHVpLmhlbHBlci5hcHBlbmRUbyggZHJhZ2dhYmxlLl9wYXJlbnQgKTtcblx0XHRcdFx0XHRkcmFnZ2FibGUuX3JlZnJlc2hPZmZzZXRzKCBldmVudCApO1xuXHRcdFx0XHRcdHVpLnBvc2l0aW9uID0gZHJhZ2dhYmxlLl9nZW5lcmF0ZVBvc2l0aW9uKCBldmVudCwgdHJ1ZSApO1xuXG5cdFx0XHRcdFx0ZHJhZ2dhYmxlLl90cmlnZ2VyKCBcImZyb21Tb3J0YWJsZVwiLCBldmVudCApO1xuXG5cdFx0XHRcdFx0Ly8gSW5mb3JtIGRyYWdnYWJsZSB0aGF0IHRoZSBoZWxwZXIgaXMgbm8gbG9uZ2VyIGluIGEgdmFsaWQgZHJvcCB6b25lXG5cdFx0XHRcdFx0ZHJhZ2dhYmxlLmRyb3BwZWQgPSBmYWxzZTtcblxuXHRcdFx0XHRcdC8vIE5lZWQgdG8gcmVmcmVzaFBvc2l0aW9ucyBvZiBhbGwgc29ydGFibGVzIGp1c3QgaW4gY2FzZSByZW1vdmluZ1xuXHRcdFx0XHRcdC8vIGZyb20gb25lIHNvcnRhYmxlIGNoYW5nZXMgdGhlIGxvY2F0aW9uIG9mIG90aGVyIHNvcnRhYmxlcyAoIzk2NzUpXG5cdFx0XHRcdFx0JC5lYWNoKCBkcmFnZ2FibGUuc29ydGFibGVzLCBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHRcdHRoaXMucmVmcmVzaFBvc2l0aW9ucygpO1xuXHRcdFx0XHRcdH0gKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0gKTtcblx0fVxufSApO1xuXG4kLnVpLnBsdWdpbi5hZGQoIFwiZHJhZ2dhYmxlXCIsIFwiY3Vyc29yXCIsIHtcblx0c3RhcnQ6IGZ1bmN0aW9uKCBldmVudCwgdWksIGluc3RhbmNlICkge1xuXHRcdHZhciB0ID0gJCggXCJib2R5XCIgKSxcblx0XHRcdG8gPSBpbnN0YW5jZS5vcHRpb25zO1xuXG5cdFx0aWYgKCB0LmNzcyggXCJjdXJzb3JcIiApICkge1xuXHRcdFx0by5fY3Vyc29yID0gdC5jc3MoIFwiY3Vyc29yXCIgKTtcblx0XHR9XG5cdFx0dC5jc3MoIFwiY3Vyc29yXCIsIG8uY3Vyc29yICk7XG5cdH0sXG5cdHN0b3A6IGZ1bmN0aW9uKCBldmVudCwgdWksIGluc3RhbmNlICkge1xuXHRcdHZhciBvID0gaW5zdGFuY2Uub3B0aW9ucztcblx0XHRpZiAoIG8uX2N1cnNvciApIHtcblx0XHRcdCQoIFwiYm9keVwiICkuY3NzKCBcImN1cnNvclwiLCBvLl9jdXJzb3IgKTtcblx0XHR9XG5cdH1cbn0gKTtcblxuJC51aS5wbHVnaW4uYWRkKCBcImRyYWdnYWJsZVwiLCBcIm9wYWNpdHlcIiwge1xuXHRzdGFydDogZnVuY3Rpb24oIGV2ZW50LCB1aSwgaW5zdGFuY2UgKSB7XG5cdFx0dmFyIHQgPSAkKCB1aS5oZWxwZXIgKSxcblx0XHRcdG8gPSBpbnN0YW5jZS5vcHRpb25zO1xuXHRcdGlmICggdC5jc3MoIFwib3BhY2l0eVwiICkgKSB7XG5cdFx0XHRvLl9vcGFjaXR5ID0gdC5jc3MoIFwib3BhY2l0eVwiICk7XG5cdFx0fVxuXHRcdHQuY3NzKCBcIm9wYWNpdHlcIiwgby5vcGFjaXR5ICk7XG5cdH0sXG5cdHN0b3A6IGZ1bmN0aW9uKCBldmVudCwgdWksIGluc3RhbmNlICkge1xuXHRcdHZhciBvID0gaW5zdGFuY2Uub3B0aW9ucztcblx0XHRpZiAoIG8uX29wYWNpdHkgKSB7XG5cdFx0XHQkKCB1aS5oZWxwZXIgKS5jc3MoIFwib3BhY2l0eVwiLCBvLl9vcGFjaXR5ICk7XG5cdFx0fVxuXHR9XG59ICk7XG5cbiQudWkucGx1Z2luLmFkZCggXCJkcmFnZ2FibGVcIiwgXCJzY3JvbGxcIiwge1xuXHRzdGFydDogZnVuY3Rpb24oIGV2ZW50LCB1aSwgaSApIHtcblx0XHRpZiAoICFpLnNjcm9sbFBhcmVudE5vdEhpZGRlbiApIHtcblx0XHRcdGkuc2Nyb2xsUGFyZW50Tm90SGlkZGVuID0gaS5oZWxwZXIuc2Nyb2xsUGFyZW50KCBmYWxzZSApO1xuXHRcdH1cblxuXHRcdGlmICggaS5zY3JvbGxQYXJlbnROb3RIaWRkZW5bIDAgXSAhPT0gaS5kb2N1bWVudFsgMCBdICYmXG5cdFx0XHRcdGkuc2Nyb2xsUGFyZW50Tm90SGlkZGVuWyAwIF0udGFnTmFtZSAhPT0gXCJIVE1MXCIgKSB7XG5cdFx0XHRpLm92ZXJmbG93T2Zmc2V0ID0gaS5zY3JvbGxQYXJlbnROb3RIaWRkZW4ub2Zmc2V0KCk7XG5cdFx0fVxuXHR9LFxuXHRkcmFnOiBmdW5jdGlvbiggZXZlbnQsIHVpLCBpICApIHtcblxuXHRcdHZhciBvID0gaS5vcHRpb25zLFxuXHRcdFx0c2Nyb2xsZWQgPSBmYWxzZSxcblx0XHRcdHNjcm9sbFBhcmVudCA9IGkuc2Nyb2xsUGFyZW50Tm90SGlkZGVuWyAwIF0sXG5cdFx0XHRkb2N1bWVudCA9IGkuZG9jdW1lbnRbIDAgXTtcblxuXHRcdGlmICggc2Nyb2xsUGFyZW50ICE9PSBkb2N1bWVudCAmJiBzY3JvbGxQYXJlbnQudGFnTmFtZSAhPT0gXCJIVE1MXCIgKSB7XG5cdFx0XHRpZiAoICFvLmF4aXMgfHwgby5heGlzICE9PSBcInhcIiApIHtcblx0XHRcdFx0aWYgKCAoIGkub3ZlcmZsb3dPZmZzZXQudG9wICsgc2Nyb2xsUGFyZW50Lm9mZnNldEhlaWdodCApIC0gZXZlbnQucGFnZVkgPFxuXHRcdFx0XHRcdFx0by5zY3JvbGxTZW5zaXRpdml0eSApIHtcblx0XHRcdFx0XHRzY3JvbGxQYXJlbnQuc2Nyb2xsVG9wID0gc2Nyb2xsZWQgPSBzY3JvbGxQYXJlbnQuc2Nyb2xsVG9wICsgby5zY3JvbGxTcGVlZDtcblx0XHRcdFx0fSBlbHNlIGlmICggZXZlbnQucGFnZVkgLSBpLm92ZXJmbG93T2Zmc2V0LnRvcCA8IG8uc2Nyb2xsU2Vuc2l0aXZpdHkgKSB7XG5cdFx0XHRcdFx0c2Nyb2xsUGFyZW50LnNjcm9sbFRvcCA9IHNjcm9sbGVkID0gc2Nyb2xsUGFyZW50LnNjcm9sbFRvcCAtIG8uc2Nyb2xsU3BlZWQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0aWYgKCAhby5heGlzIHx8IG8uYXhpcyAhPT0gXCJ5XCIgKSB7XG5cdFx0XHRcdGlmICggKCBpLm92ZXJmbG93T2Zmc2V0LmxlZnQgKyBzY3JvbGxQYXJlbnQub2Zmc2V0V2lkdGggKSAtIGV2ZW50LnBhZ2VYIDxcblx0XHRcdFx0XHRcdG8uc2Nyb2xsU2Vuc2l0aXZpdHkgKSB7XG5cdFx0XHRcdFx0c2Nyb2xsUGFyZW50LnNjcm9sbExlZnQgPSBzY3JvbGxlZCA9IHNjcm9sbFBhcmVudC5zY3JvbGxMZWZ0ICsgby5zY3JvbGxTcGVlZDtcblx0XHRcdFx0fSBlbHNlIGlmICggZXZlbnQucGFnZVggLSBpLm92ZXJmbG93T2Zmc2V0LmxlZnQgPCBvLnNjcm9sbFNlbnNpdGl2aXR5ICkge1xuXHRcdFx0XHRcdHNjcm9sbFBhcmVudC5zY3JvbGxMZWZ0ID0gc2Nyb2xsZWQgPSBzY3JvbGxQYXJlbnQuc2Nyb2xsTGVmdCAtIG8uc2Nyb2xsU3BlZWQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdH0gZWxzZSB7XG5cblx0XHRcdGlmICggIW8uYXhpcyB8fCBvLmF4aXMgIT09IFwieFwiICkge1xuXHRcdFx0XHRpZiAoIGV2ZW50LnBhZ2VZIC0gJCggZG9jdW1lbnQgKS5zY3JvbGxUb3AoKSA8IG8uc2Nyb2xsU2Vuc2l0aXZpdHkgKSB7XG5cdFx0XHRcdFx0c2Nyb2xsZWQgPSAkKCBkb2N1bWVudCApLnNjcm9sbFRvcCggJCggZG9jdW1lbnQgKS5zY3JvbGxUb3AoKSAtIG8uc2Nyb2xsU3BlZWQgKTtcblx0XHRcdFx0fSBlbHNlIGlmICggJCggd2luZG93ICkuaGVpZ2h0KCkgLSAoIGV2ZW50LnBhZ2VZIC0gJCggZG9jdW1lbnQgKS5zY3JvbGxUb3AoKSApIDxcblx0XHRcdFx0XHRcdG8uc2Nyb2xsU2Vuc2l0aXZpdHkgKSB7XG5cdFx0XHRcdFx0c2Nyb2xsZWQgPSAkKCBkb2N1bWVudCApLnNjcm9sbFRvcCggJCggZG9jdW1lbnQgKS5zY3JvbGxUb3AoKSArIG8uc2Nyb2xsU3BlZWQgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoICFvLmF4aXMgfHwgby5heGlzICE9PSBcInlcIiApIHtcblx0XHRcdFx0aWYgKCBldmVudC5wYWdlWCAtICQoIGRvY3VtZW50ICkuc2Nyb2xsTGVmdCgpIDwgby5zY3JvbGxTZW5zaXRpdml0eSApIHtcblx0XHRcdFx0XHRzY3JvbGxlZCA9ICQoIGRvY3VtZW50ICkuc2Nyb2xsTGVmdChcblx0XHRcdFx0XHRcdCQoIGRvY3VtZW50ICkuc2Nyb2xsTGVmdCgpIC0gby5zY3JvbGxTcGVlZFxuXHRcdFx0XHRcdCk7XG5cdFx0XHRcdH0gZWxzZSBpZiAoICQoIHdpbmRvdyApLndpZHRoKCkgLSAoIGV2ZW50LnBhZ2VYIC0gJCggZG9jdW1lbnQgKS5zY3JvbGxMZWZ0KCkgKSA8XG5cdFx0XHRcdFx0XHRvLnNjcm9sbFNlbnNpdGl2aXR5ICkge1xuXHRcdFx0XHRcdHNjcm9sbGVkID0gJCggZG9jdW1lbnQgKS5zY3JvbGxMZWZ0KFxuXHRcdFx0XHRcdFx0JCggZG9jdW1lbnQgKS5zY3JvbGxMZWZ0KCkgKyBvLnNjcm9sbFNwZWVkXG5cdFx0XHRcdFx0KTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0aWYgKCBzY3JvbGxlZCAhPT0gZmFsc2UgJiYgJC51aS5kZG1hbmFnZXIgJiYgIW8uZHJvcEJlaGF2aW91ciApIHtcblx0XHRcdCQudWkuZGRtYW5hZ2VyLnByZXBhcmVPZmZzZXRzKCBpLCBldmVudCApO1xuXHRcdH1cblxuXHR9XG59ICk7XG5cbiQudWkucGx1Z2luLmFkZCggXCJkcmFnZ2FibGVcIiwgXCJzbmFwXCIsIHtcblx0c3RhcnQ6IGZ1bmN0aW9uKCBldmVudCwgdWksIGkgKSB7XG5cblx0XHR2YXIgbyA9IGkub3B0aW9ucztcblxuXHRcdGkuc25hcEVsZW1lbnRzID0gW107XG5cblx0XHQkKCBvLnNuYXAuY29uc3RydWN0b3IgIT09IFN0cmluZyA/ICggby5zbmFwLml0ZW1zIHx8IFwiOmRhdGEodWktZHJhZ2dhYmxlKVwiICkgOiBvLnNuYXAgKVxuXHRcdFx0LmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHR2YXIgJHQgPSAkKCB0aGlzICksXG5cdFx0XHRcdFx0JG8gPSAkdC5vZmZzZXQoKTtcblx0XHRcdFx0aWYgKCB0aGlzICE9PSBpLmVsZW1lbnRbIDAgXSApIHtcblx0XHRcdFx0XHRpLnNuYXBFbGVtZW50cy5wdXNoKCB7XG5cdFx0XHRcdFx0XHRpdGVtOiB0aGlzLFxuXHRcdFx0XHRcdFx0d2lkdGg6ICR0Lm91dGVyV2lkdGgoKSwgaGVpZ2h0OiAkdC5vdXRlckhlaWdodCgpLFxuXHRcdFx0XHRcdFx0dG9wOiAkby50b3AsIGxlZnQ6ICRvLmxlZnRcblx0XHRcdFx0XHR9ICk7XG5cdFx0XHRcdH1cblx0XHRcdH0gKTtcblxuXHR9LFxuXHRkcmFnOiBmdW5jdGlvbiggZXZlbnQsIHVpLCBpbnN0ICkge1xuXG5cdFx0dmFyIHRzLCBicywgbHMsIHJzLCBsLCByLCB0LCBiLCBpLCBmaXJzdCxcblx0XHRcdG8gPSBpbnN0Lm9wdGlvbnMsXG5cdFx0XHRkID0gby5zbmFwVG9sZXJhbmNlLFxuXHRcdFx0eDEgPSB1aS5vZmZzZXQubGVmdCwgeDIgPSB4MSArIGluc3QuaGVscGVyUHJvcG9ydGlvbnMud2lkdGgsXG5cdFx0XHR5MSA9IHVpLm9mZnNldC50b3AsIHkyID0geTEgKyBpbnN0LmhlbHBlclByb3BvcnRpb25zLmhlaWdodDtcblxuXHRcdGZvciAoIGkgPSBpbnN0LnNuYXBFbGVtZW50cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSApIHtcblxuXHRcdFx0bCA9IGluc3Quc25hcEVsZW1lbnRzWyBpIF0ubGVmdCAtIGluc3QubWFyZ2lucy5sZWZ0O1xuXHRcdFx0ciA9IGwgKyBpbnN0LnNuYXBFbGVtZW50c1sgaSBdLndpZHRoO1xuXHRcdFx0dCA9IGluc3Quc25hcEVsZW1lbnRzWyBpIF0udG9wIC0gaW5zdC5tYXJnaW5zLnRvcDtcblx0XHRcdGIgPSB0ICsgaW5zdC5zbmFwRWxlbWVudHNbIGkgXS5oZWlnaHQ7XG5cblx0XHRcdGlmICggeDIgPCBsIC0gZCB8fCB4MSA+IHIgKyBkIHx8IHkyIDwgdCAtIGQgfHwgeTEgPiBiICsgZCB8fFxuXHRcdFx0XHRcdCEkLmNvbnRhaW5zKCBpbnN0LnNuYXBFbGVtZW50c1sgaSBdLml0ZW0ub3duZXJEb2N1bWVudCxcblx0XHRcdFx0XHRpbnN0LnNuYXBFbGVtZW50c1sgaSBdLml0ZW0gKSApIHtcblx0XHRcdFx0aWYgKCBpbnN0LnNuYXBFbGVtZW50c1sgaSBdLnNuYXBwaW5nICkge1xuXHRcdFx0XHRcdCggaW5zdC5vcHRpb25zLnNuYXAucmVsZWFzZSAmJlxuXHRcdFx0XHRcdFx0aW5zdC5vcHRpb25zLnNuYXAucmVsZWFzZS5jYWxsKFxuXHRcdFx0XHRcdFx0XHRpbnN0LmVsZW1lbnQsXG5cdFx0XHRcdFx0XHRcdGV2ZW50LFxuXHRcdFx0XHRcdFx0XHQkLmV4dGVuZCggaW5zdC5fdWlIYXNoKCksIHsgc25hcEl0ZW06IGluc3Quc25hcEVsZW1lbnRzWyBpIF0uaXRlbSB9IClcblx0XHRcdFx0XHRcdCkgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpbnN0LnNuYXBFbGVtZW50c1sgaSBdLnNuYXBwaW5nID0gZmFsc2U7XG5cdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIG8uc25hcE1vZGUgIT09IFwiaW5uZXJcIiApIHtcblx0XHRcdFx0dHMgPSBNYXRoLmFicyggdCAtIHkyICkgPD0gZDtcblx0XHRcdFx0YnMgPSBNYXRoLmFicyggYiAtIHkxICkgPD0gZDtcblx0XHRcdFx0bHMgPSBNYXRoLmFicyggbCAtIHgyICkgPD0gZDtcblx0XHRcdFx0cnMgPSBNYXRoLmFicyggciAtIHgxICkgPD0gZDtcblx0XHRcdFx0aWYgKCB0cyApIHtcblx0XHRcdFx0XHR1aS5wb3NpdGlvbi50b3AgPSBpbnN0Ll9jb252ZXJ0UG9zaXRpb25UbyggXCJyZWxhdGl2ZVwiLCB7XG5cdFx0XHRcdFx0XHR0b3A6IHQgLSBpbnN0LmhlbHBlclByb3BvcnRpb25zLmhlaWdodCxcblx0XHRcdFx0XHRcdGxlZnQ6IDBcblx0XHRcdFx0XHR9ICkudG9wO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmICggYnMgKSB7XG5cdFx0XHRcdFx0dWkucG9zaXRpb24udG9wID0gaW5zdC5fY29udmVydFBvc2l0aW9uVG8oIFwicmVsYXRpdmVcIiwge1xuXHRcdFx0XHRcdFx0dG9wOiBiLFxuXHRcdFx0XHRcdFx0bGVmdDogMFxuXHRcdFx0XHRcdH0gKS50b3A7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKCBscyApIHtcblx0XHRcdFx0XHR1aS5wb3NpdGlvbi5sZWZ0ID0gaW5zdC5fY29udmVydFBvc2l0aW9uVG8oIFwicmVsYXRpdmVcIiwge1xuXHRcdFx0XHRcdFx0dG9wOiAwLFxuXHRcdFx0XHRcdFx0bGVmdDogbCAtIGluc3QuaGVscGVyUHJvcG9ydGlvbnMud2lkdGhcblx0XHRcdFx0XHR9ICkubGVmdDtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoIHJzICkge1xuXHRcdFx0XHRcdHVpLnBvc2l0aW9uLmxlZnQgPSBpbnN0Ll9jb252ZXJ0UG9zaXRpb25UbyggXCJyZWxhdGl2ZVwiLCB7XG5cdFx0XHRcdFx0XHR0b3A6IDAsXG5cdFx0XHRcdFx0XHRsZWZ0OiByXG5cdFx0XHRcdFx0fSApLmxlZnQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Zmlyc3QgPSAoIHRzIHx8IGJzIHx8IGxzIHx8IHJzICk7XG5cblx0XHRcdGlmICggby5zbmFwTW9kZSAhPT0gXCJvdXRlclwiICkge1xuXHRcdFx0XHR0cyA9IE1hdGguYWJzKCB0IC0geTEgKSA8PSBkO1xuXHRcdFx0XHRicyA9IE1hdGguYWJzKCBiIC0geTIgKSA8PSBkO1xuXHRcdFx0XHRscyA9IE1hdGguYWJzKCBsIC0geDEgKSA8PSBkO1xuXHRcdFx0XHRycyA9IE1hdGguYWJzKCByIC0geDIgKSA8PSBkO1xuXHRcdFx0XHRpZiAoIHRzICkge1xuXHRcdFx0XHRcdHVpLnBvc2l0aW9uLnRvcCA9IGluc3QuX2NvbnZlcnRQb3NpdGlvblRvKCBcInJlbGF0aXZlXCIsIHtcblx0XHRcdFx0XHRcdHRvcDogdCxcblx0XHRcdFx0XHRcdGxlZnQ6IDBcblx0XHRcdFx0XHR9ICkudG9wO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmICggYnMgKSB7XG5cdFx0XHRcdFx0dWkucG9zaXRpb24udG9wID0gaW5zdC5fY29udmVydFBvc2l0aW9uVG8oIFwicmVsYXRpdmVcIiwge1xuXHRcdFx0XHRcdFx0dG9wOiBiIC0gaW5zdC5oZWxwZXJQcm9wb3J0aW9ucy5oZWlnaHQsXG5cdFx0XHRcdFx0XHRsZWZ0OiAwXG5cdFx0XHRcdFx0fSApLnRvcDtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoIGxzICkge1xuXHRcdFx0XHRcdHVpLnBvc2l0aW9uLmxlZnQgPSBpbnN0Ll9jb252ZXJ0UG9zaXRpb25UbyggXCJyZWxhdGl2ZVwiLCB7XG5cdFx0XHRcdFx0XHR0b3A6IDAsXG5cdFx0XHRcdFx0XHRsZWZ0OiBsXG5cdFx0XHRcdFx0fSApLmxlZnQ7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKCBycyApIHtcblx0XHRcdFx0XHR1aS5wb3NpdGlvbi5sZWZ0ID0gaW5zdC5fY29udmVydFBvc2l0aW9uVG8oIFwicmVsYXRpdmVcIiwge1xuXHRcdFx0XHRcdFx0dG9wOiAwLFxuXHRcdFx0XHRcdFx0bGVmdDogciAtIGluc3QuaGVscGVyUHJvcG9ydGlvbnMud2lkdGhcblx0XHRcdFx0XHR9ICkubGVmdDtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoICFpbnN0LnNuYXBFbGVtZW50c1sgaSBdLnNuYXBwaW5nICYmICggdHMgfHwgYnMgfHwgbHMgfHwgcnMgfHwgZmlyc3QgKSApIHtcblx0XHRcdFx0KCBpbnN0Lm9wdGlvbnMuc25hcC5zbmFwICYmXG5cdFx0XHRcdFx0aW5zdC5vcHRpb25zLnNuYXAuc25hcC5jYWxsKFxuXHRcdFx0XHRcdFx0aW5zdC5lbGVtZW50LFxuXHRcdFx0XHRcdFx0ZXZlbnQsXG5cdFx0XHRcdFx0XHQkLmV4dGVuZCggaW5zdC5fdWlIYXNoKCksIHtcblx0XHRcdFx0XHRcdFx0c25hcEl0ZW06IGluc3Quc25hcEVsZW1lbnRzWyBpIF0uaXRlbVxuXHRcdFx0XHRcdFx0fSApICkgKTtcblx0XHRcdH1cblx0XHRcdGluc3Quc25hcEVsZW1lbnRzWyBpIF0uc25hcHBpbmcgPSAoIHRzIHx8IGJzIHx8IGxzIHx8IHJzIHx8IGZpcnN0ICk7XG5cblx0XHR9XG5cblx0fVxufSApO1xuXG4kLnVpLnBsdWdpbi5hZGQoIFwiZHJhZ2dhYmxlXCIsIFwic3RhY2tcIiwge1xuXHRzdGFydDogZnVuY3Rpb24oIGV2ZW50LCB1aSwgaW5zdGFuY2UgKSB7XG5cdFx0dmFyIG1pbixcblx0XHRcdG8gPSBpbnN0YW5jZS5vcHRpb25zLFxuXHRcdFx0Z3JvdXAgPSAkLm1ha2VBcnJheSggJCggby5zdGFjayApICkuc29ydCggZnVuY3Rpb24oIGEsIGIgKSB7XG5cdFx0XHRcdHJldHVybiAoIHBhcnNlSW50KCAkKCBhICkuY3NzKCBcInpJbmRleFwiICksIDEwICkgfHwgMCApIC1cblx0XHRcdFx0XHQoIHBhcnNlSW50KCAkKCBiICkuY3NzKCBcInpJbmRleFwiICksIDEwICkgfHwgMCApO1xuXHRcdFx0fSApO1xuXG5cdFx0aWYgKCAhZ3JvdXAubGVuZ3RoICkgeyByZXR1cm47IH1cblxuXHRcdG1pbiA9IHBhcnNlSW50KCAkKCBncm91cFsgMCBdICkuY3NzKCBcInpJbmRleFwiICksIDEwICkgfHwgMDtcblx0XHQkKCBncm91cCApLmVhY2goIGZ1bmN0aW9uKCBpICkge1xuXHRcdFx0JCggdGhpcyApLmNzcyggXCJ6SW5kZXhcIiwgbWluICsgaSApO1xuXHRcdH0gKTtcblx0XHR0aGlzLmNzcyggXCJ6SW5kZXhcIiwgKCBtaW4gKyBncm91cC5sZW5ndGggKSApO1xuXHR9XG59ICk7XG5cbiQudWkucGx1Z2luLmFkZCggXCJkcmFnZ2FibGVcIiwgXCJ6SW5kZXhcIiwge1xuXHRzdGFydDogZnVuY3Rpb24oIGV2ZW50LCB1aSwgaW5zdGFuY2UgKSB7XG5cdFx0dmFyIHQgPSAkKCB1aS5oZWxwZXIgKSxcblx0XHRcdG8gPSBpbnN0YW5jZS5vcHRpb25zO1xuXG5cdFx0aWYgKCB0LmNzcyggXCJ6SW5kZXhcIiApICkge1xuXHRcdFx0by5fekluZGV4ID0gdC5jc3MoIFwiekluZGV4XCIgKTtcblx0XHR9XG5cdFx0dC5jc3MoIFwiekluZGV4XCIsIG8uekluZGV4ICk7XG5cdH0sXG5cdHN0b3A6IGZ1bmN0aW9uKCBldmVudCwgdWksIGluc3RhbmNlICkge1xuXHRcdHZhciBvID0gaW5zdGFuY2Uub3B0aW9ucztcblxuXHRcdGlmICggby5fekluZGV4ICkge1xuXHRcdFx0JCggdWkuaGVscGVyICkuY3NzKCBcInpJbmRleFwiLCBvLl96SW5kZXggKTtcblx0XHR9XG5cdH1cbn0gKTtcblxudmFyIHdpZGdldHNEcmFnZ2FibGUgPSAkLnVpLmRyYWdnYWJsZTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBSZXNpemFibGUgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IFJlc2l6YWJsZVxuLy8+Pmdyb3VwOiBJbnRlcmFjdGlvbnNcbi8vPj5kZXNjcmlwdGlvbjogRW5hYmxlcyByZXNpemUgZnVuY3Rpb25hbGl0eSBmb3IgYW55IGVsZW1lbnQuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vcmVzaXphYmxlL1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL3Jlc2l6YWJsZS9cbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9jb3JlLmNzc1xuLy8+PmNzcy5zdHJ1Y3R1cmU6IC4uLy4uL3RoZW1lcy9iYXNlL3Jlc2l6YWJsZS5jc3Ncbi8vPj5jc3MudGhlbWU6IC4uLy4uL3RoZW1lcy9iYXNlL3RoZW1lLmNzc1xuXG5cblxuJC53aWRnZXQoIFwidWkucmVzaXphYmxlXCIsICQudWkubW91c2UsIHtcblx0dmVyc2lvbjogXCIxLjEyLjFcIixcblx0d2lkZ2V0RXZlbnRQcmVmaXg6IFwicmVzaXplXCIsXG5cdG9wdGlvbnM6IHtcblx0XHRhbHNvUmVzaXplOiBmYWxzZSxcblx0XHRhbmltYXRlOiBmYWxzZSxcblx0XHRhbmltYXRlRHVyYXRpb246IFwic2xvd1wiLFxuXHRcdGFuaW1hdGVFYXNpbmc6IFwic3dpbmdcIixcblx0XHRhc3BlY3RSYXRpbzogZmFsc2UsXG5cdFx0YXV0b0hpZGU6IGZhbHNlLFxuXHRcdGNsYXNzZXM6IHtcblx0XHRcdFwidWktcmVzaXphYmxlLXNlXCI6IFwidWktaWNvbiB1aS1pY29uLWdyaXBzbWFsbC1kaWFnb25hbC1zZVwiXG5cdFx0fSxcblx0XHRjb250YWlubWVudDogZmFsc2UsXG5cdFx0Z2hvc3Q6IGZhbHNlLFxuXHRcdGdyaWQ6IGZhbHNlLFxuXHRcdGhhbmRsZXM6IFwiZSxzLHNlXCIsXG5cdFx0aGVscGVyOiBmYWxzZSxcblx0XHRtYXhIZWlnaHQ6IG51bGwsXG5cdFx0bWF4V2lkdGg6IG51bGwsXG5cdFx0bWluSGVpZ2h0OiAxMCxcblx0XHRtaW5XaWR0aDogMTAsXG5cblx0XHQvLyBTZWUgIzc5NjBcblx0XHR6SW5kZXg6IDkwLFxuXG5cdFx0Ly8gQ2FsbGJhY2tzXG5cdFx0cmVzaXplOiBudWxsLFxuXHRcdHN0YXJ0OiBudWxsLFxuXHRcdHN0b3A6IG51bGxcblx0fSxcblxuXHRfbnVtOiBmdW5jdGlvbiggdmFsdWUgKSB7XG5cdFx0cmV0dXJuIHBhcnNlRmxvYXQoIHZhbHVlICkgfHwgMDtcblx0fSxcblxuXHRfaXNOdW1iZXI6IGZ1bmN0aW9uKCB2YWx1ZSApIHtcblx0XHRyZXR1cm4gIWlzTmFOKCBwYXJzZUZsb2F0KCB2YWx1ZSApICk7XG5cdH0sXG5cblx0X2hhc1Njcm9sbDogZnVuY3Rpb24oIGVsLCBhICkge1xuXG5cdFx0aWYgKCAkKCBlbCApLmNzcyggXCJvdmVyZmxvd1wiICkgPT09IFwiaGlkZGVuXCIgKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXG5cdFx0dmFyIHNjcm9sbCA9ICggYSAmJiBhID09PSBcImxlZnRcIiApID8gXCJzY3JvbGxMZWZ0XCIgOiBcInNjcm9sbFRvcFwiLFxuXHRcdFx0aGFzID0gZmFsc2U7XG5cblx0XHRpZiAoIGVsWyBzY3JvbGwgXSA+IDAgKSB7XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHR9XG5cblx0XHQvLyBUT0RPOiBkZXRlcm1pbmUgd2hpY2ggY2FzZXMgYWN0dWFsbHkgY2F1c2UgdGhpcyB0byBoYXBwZW5cblx0XHQvLyBpZiB0aGUgZWxlbWVudCBkb2Vzbid0IGhhdmUgdGhlIHNjcm9sbCBzZXQsIHNlZSBpZiBpdCdzIHBvc3NpYmxlIHRvXG5cdFx0Ly8gc2V0IHRoZSBzY3JvbGxcblx0XHRlbFsgc2Nyb2xsIF0gPSAxO1xuXHRcdGhhcyA9ICggZWxbIHNjcm9sbCBdID4gMCApO1xuXHRcdGVsWyBzY3JvbGwgXSA9IDA7XG5cdFx0cmV0dXJuIGhhcztcblx0fSxcblxuXHRfY3JlYXRlOiBmdW5jdGlvbigpIHtcblxuXHRcdHZhciBtYXJnaW5zLFxuXHRcdFx0byA9IHRoaXMub3B0aW9ucyxcblx0XHRcdHRoYXQgPSB0aGlzO1xuXHRcdHRoaXMuX2FkZENsYXNzKCBcInVpLXJlc2l6YWJsZVwiICk7XG5cblx0XHQkLmV4dGVuZCggdGhpcywge1xuXHRcdFx0X2FzcGVjdFJhdGlvOiAhISggby5hc3BlY3RSYXRpbyApLFxuXHRcdFx0YXNwZWN0UmF0aW86IG8uYXNwZWN0UmF0aW8sXG5cdFx0XHRvcmlnaW5hbEVsZW1lbnQ6IHRoaXMuZWxlbWVudCxcblx0XHRcdF9wcm9wb3J0aW9uYWxseVJlc2l6ZUVsZW1lbnRzOiBbXSxcblx0XHRcdF9oZWxwZXI6IG8uaGVscGVyIHx8IG8uZ2hvc3QgfHwgby5hbmltYXRlID8gby5oZWxwZXIgfHwgXCJ1aS1yZXNpemFibGUtaGVscGVyXCIgOiBudWxsXG5cdFx0fSApO1xuXG5cdFx0Ly8gV3JhcCB0aGUgZWxlbWVudCBpZiBpdCBjYW5ub3QgaG9sZCBjaGlsZCBub2Rlc1xuXHRcdGlmICggdGhpcy5lbGVtZW50WyAwIF0ubm9kZU5hbWUubWF0Y2goIC9eKGNhbnZhc3x0ZXh0YXJlYXxpbnB1dHxzZWxlY3R8YnV0dG9ufGltZykkL2kgKSApIHtcblxuXHRcdFx0dGhpcy5lbGVtZW50LndyYXAoXG5cdFx0XHRcdCQoIFwiPGRpdiBjbGFzcz0ndWktd3JhcHBlcicgc3R5bGU9J292ZXJmbG93OiBoaWRkZW47Jz48L2Rpdj5cIiApLmNzcygge1xuXHRcdFx0XHRcdHBvc2l0aW9uOiB0aGlzLmVsZW1lbnQuY3NzKCBcInBvc2l0aW9uXCIgKSxcblx0XHRcdFx0XHR3aWR0aDogdGhpcy5lbGVtZW50Lm91dGVyV2lkdGgoKSxcblx0XHRcdFx0XHRoZWlnaHQ6IHRoaXMuZWxlbWVudC5vdXRlckhlaWdodCgpLFxuXHRcdFx0XHRcdHRvcDogdGhpcy5lbGVtZW50LmNzcyggXCJ0b3BcIiApLFxuXHRcdFx0XHRcdGxlZnQ6IHRoaXMuZWxlbWVudC5jc3MoIFwibGVmdFwiIClcblx0XHRcdFx0fSApXG5cdFx0XHQpO1xuXG5cdFx0XHR0aGlzLmVsZW1lbnQgPSB0aGlzLmVsZW1lbnQucGFyZW50KCkuZGF0YShcblx0XHRcdFx0XCJ1aS1yZXNpemFibGVcIiwgdGhpcy5lbGVtZW50LnJlc2l6YWJsZSggXCJpbnN0YW5jZVwiIClcblx0XHRcdCk7XG5cblx0XHRcdHRoaXMuZWxlbWVudElzV3JhcHBlciA9IHRydWU7XG5cblx0XHRcdG1hcmdpbnMgPSB7XG5cdFx0XHRcdG1hcmdpblRvcDogdGhpcy5vcmlnaW5hbEVsZW1lbnQuY3NzKCBcIm1hcmdpblRvcFwiICksXG5cdFx0XHRcdG1hcmdpblJpZ2h0OiB0aGlzLm9yaWdpbmFsRWxlbWVudC5jc3MoIFwibWFyZ2luUmlnaHRcIiApLFxuXHRcdFx0XHRtYXJnaW5Cb3R0b206IHRoaXMub3JpZ2luYWxFbGVtZW50LmNzcyggXCJtYXJnaW5Cb3R0b21cIiApLFxuXHRcdFx0XHRtYXJnaW5MZWZ0OiB0aGlzLm9yaWdpbmFsRWxlbWVudC5jc3MoIFwibWFyZ2luTGVmdFwiIClcblx0XHRcdH07XG5cblx0XHRcdHRoaXMuZWxlbWVudC5jc3MoIG1hcmdpbnMgKTtcblx0XHRcdHRoaXMub3JpZ2luYWxFbGVtZW50LmNzcyggXCJtYXJnaW5cIiwgMCApO1xuXG5cdFx0XHQvLyBzdXBwb3J0OiBTYWZhcmlcblx0XHRcdC8vIFByZXZlbnQgU2FmYXJpIHRleHRhcmVhIHJlc2l6ZVxuXHRcdFx0dGhpcy5vcmlnaW5hbFJlc2l6ZVN0eWxlID0gdGhpcy5vcmlnaW5hbEVsZW1lbnQuY3NzKCBcInJlc2l6ZVwiICk7XG5cdFx0XHR0aGlzLm9yaWdpbmFsRWxlbWVudC5jc3MoIFwicmVzaXplXCIsIFwibm9uZVwiICk7XG5cblx0XHRcdHRoaXMuX3Byb3BvcnRpb25hbGx5UmVzaXplRWxlbWVudHMucHVzaCggdGhpcy5vcmlnaW5hbEVsZW1lbnQuY3NzKCB7XG5cdFx0XHRcdHBvc2l0aW9uOiBcInN0YXRpY1wiLFxuXHRcdFx0XHR6b29tOiAxLFxuXHRcdFx0XHRkaXNwbGF5OiBcImJsb2NrXCJcblx0XHRcdH0gKSApO1xuXG5cdFx0XHQvLyBTdXBwb3J0OiBJRTlcblx0XHRcdC8vIGF2b2lkIElFIGp1bXAgKGhhcmQgc2V0IHRoZSBtYXJnaW4pXG5cdFx0XHR0aGlzLm9yaWdpbmFsRWxlbWVudC5jc3MoIG1hcmdpbnMgKTtcblxuXHRcdFx0dGhpcy5fcHJvcG9ydGlvbmFsbHlSZXNpemUoKTtcblx0XHR9XG5cblx0XHR0aGlzLl9zZXR1cEhhbmRsZXMoKTtcblxuXHRcdGlmICggby5hdXRvSGlkZSApIHtcblx0XHRcdCQoIHRoaXMuZWxlbWVudCApXG5cdFx0XHRcdC5vbiggXCJtb3VzZWVudGVyXCIsIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdGlmICggby5kaXNhYmxlZCApIHtcblx0XHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0dGhhdC5fcmVtb3ZlQ2xhc3MoIFwidWktcmVzaXphYmxlLWF1dG9oaWRlXCIgKTtcblx0XHRcdFx0XHR0aGF0Ll9oYW5kbGVzLnNob3coKTtcblx0XHRcdFx0fSApXG5cdFx0XHRcdC5vbiggXCJtb3VzZWxlYXZlXCIsIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdGlmICggby5kaXNhYmxlZCApIHtcblx0XHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0aWYgKCAhdGhhdC5yZXNpemluZyApIHtcblx0XHRcdFx0XHRcdHRoYXQuX2FkZENsYXNzKCBcInVpLXJlc2l6YWJsZS1hdXRvaGlkZVwiICk7XG5cdFx0XHRcdFx0XHR0aGF0Ll9oYW5kbGVzLmhpZGUoKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gKTtcblx0XHR9XG5cblx0XHR0aGlzLl9tb3VzZUluaXQoKTtcblx0fSxcblxuXHRfZGVzdHJveTogZnVuY3Rpb24oKSB7XG5cblx0XHR0aGlzLl9tb3VzZURlc3Ryb3koKTtcblxuXHRcdHZhciB3cmFwcGVyLFxuXHRcdFx0X2Rlc3Ryb3kgPSBmdW5jdGlvbiggZXhwICkge1xuXHRcdFx0XHQkKCBleHAgKVxuXHRcdFx0XHRcdC5yZW1vdmVEYXRhKCBcInJlc2l6YWJsZVwiIClcblx0XHRcdFx0XHQucmVtb3ZlRGF0YSggXCJ1aS1yZXNpemFibGVcIiApXG5cdFx0XHRcdFx0Lm9mZiggXCIucmVzaXphYmxlXCIgKVxuXHRcdFx0XHRcdC5maW5kKCBcIi51aS1yZXNpemFibGUtaGFuZGxlXCIgKVxuXHRcdFx0XHRcdFx0LnJlbW92ZSgpO1xuXHRcdFx0fTtcblxuXHRcdC8vIFRPRE86IFVud3JhcCBhdCBzYW1lIERPTSBwb3NpdGlvblxuXHRcdGlmICggdGhpcy5lbGVtZW50SXNXcmFwcGVyICkge1xuXHRcdFx0X2Rlc3Ryb3koIHRoaXMuZWxlbWVudCApO1xuXHRcdFx0d3JhcHBlciA9IHRoaXMuZWxlbWVudDtcblx0XHRcdHRoaXMub3JpZ2luYWxFbGVtZW50LmNzcygge1xuXHRcdFx0XHRwb3NpdGlvbjogd3JhcHBlci5jc3MoIFwicG9zaXRpb25cIiApLFxuXHRcdFx0XHR3aWR0aDogd3JhcHBlci5vdXRlcldpZHRoKCksXG5cdFx0XHRcdGhlaWdodDogd3JhcHBlci5vdXRlckhlaWdodCgpLFxuXHRcdFx0XHR0b3A6IHdyYXBwZXIuY3NzKCBcInRvcFwiICksXG5cdFx0XHRcdGxlZnQ6IHdyYXBwZXIuY3NzKCBcImxlZnRcIiApXG5cdFx0XHR9ICkuaW5zZXJ0QWZ0ZXIoIHdyYXBwZXIgKTtcblx0XHRcdHdyYXBwZXIucmVtb3ZlKCk7XG5cdFx0fVxuXG5cdFx0dGhpcy5vcmlnaW5hbEVsZW1lbnQuY3NzKCBcInJlc2l6ZVwiLCB0aGlzLm9yaWdpbmFsUmVzaXplU3R5bGUgKTtcblx0XHRfZGVzdHJveSggdGhpcy5vcmlnaW5hbEVsZW1lbnQgKTtcblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdF9zZXRPcHRpb246IGZ1bmN0aW9uKCBrZXksIHZhbHVlICkge1xuXHRcdHRoaXMuX3N1cGVyKCBrZXksIHZhbHVlICk7XG5cblx0XHRzd2l0Y2ggKCBrZXkgKSB7XG5cdFx0Y2FzZSBcImhhbmRsZXNcIjpcblx0XHRcdHRoaXMuX3JlbW92ZUhhbmRsZXMoKTtcblx0XHRcdHRoaXMuX3NldHVwSGFuZGxlcygpO1xuXHRcdFx0YnJlYWs7XG5cdFx0ZGVmYXVsdDpcblx0XHRcdGJyZWFrO1xuXHRcdH1cblx0fSxcblxuXHRfc2V0dXBIYW5kbGVzOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgbyA9IHRoaXMub3B0aW9ucywgaGFuZGxlLCBpLCBuLCBobmFtZSwgYXhpcywgdGhhdCA9IHRoaXM7XG5cdFx0dGhpcy5oYW5kbGVzID0gby5oYW5kbGVzIHx8XG5cdFx0XHQoICEkKCBcIi51aS1yZXNpemFibGUtaGFuZGxlXCIsIHRoaXMuZWxlbWVudCApLmxlbmd0aCA/XG5cdFx0XHRcdFwiZSxzLHNlXCIgOiB7XG5cdFx0XHRcdFx0bjogXCIudWktcmVzaXphYmxlLW5cIixcblx0XHRcdFx0XHRlOiBcIi51aS1yZXNpemFibGUtZVwiLFxuXHRcdFx0XHRcdHM6IFwiLnVpLXJlc2l6YWJsZS1zXCIsXG5cdFx0XHRcdFx0dzogXCIudWktcmVzaXphYmxlLXdcIixcblx0XHRcdFx0XHRzZTogXCIudWktcmVzaXphYmxlLXNlXCIsXG5cdFx0XHRcdFx0c3c6IFwiLnVpLXJlc2l6YWJsZS1zd1wiLFxuXHRcdFx0XHRcdG5lOiBcIi51aS1yZXNpemFibGUtbmVcIixcblx0XHRcdFx0XHRudzogXCIudWktcmVzaXphYmxlLW53XCJcblx0XHRcdFx0fSApO1xuXG5cdFx0dGhpcy5faGFuZGxlcyA9ICQoKTtcblx0XHRpZiAoIHRoaXMuaGFuZGxlcy5jb25zdHJ1Y3RvciA9PT0gU3RyaW5nICkge1xuXG5cdFx0XHRpZiAoIHRoaXMuaGFuZGxlcyA9PT0gXCJhbGxcIiApIHtcblx0XHRcdFx0dGhpcy5oYW5kbGVzID0gXCJuLGUscyx3LHNlLHN3LG5lLG53XCI7XG5cdFx0XHR9XG5cblx0XHRcdG4gPSB0aGlzLmhhbmRsZXMuc3BsaXQoIFwiLFwiICk7XG5cdFx0XHR0aGlzLmhhbmRsZXMgPSB7fTtcblxuXHRcdFx0Zm9yICggaSA9IDA7IGkgPCBuLmxlbmd0aDsgaSsrICkge1xuXG5cdFx0XHRcdGhhbmRsZSA9ICQudHJpbSggblsgaSBdICk7XG5cdFx0XHRcdGhuYW1lID0gXCJ1aS1yZXNpemFibGUtXCIgKyBoYW5kbGU7XG5cdFx0XHRcdGF4aXMgPSAkKCBcIjxkaXY+XCIgKTtcblx0XHRcdFx0dGhpcy5fYWRkQ2xhc3MoIGF4aXMsIFwidWktcmVzaXphYmxlLWhhbmRsZSBcIiArIGhuYW1lICk7XG5cblx0XHRcdFx0YXhpcy5jc3MoIHsgekluZGV4OiBvLnpJbmRleCB9ICk7XG5cblx0XHRcdFx0dGhpcy5oYW5kbGVzWyBoYW5kbGUgXSA9IFwiLnVpLXJlc2l6YWJsZS1cIiArIGhhbmRsZTtcblx0XHRcdFx0dGhpcy5lbGVtZW50LmFwcGVuZCggYXhpcyApO1xuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0dGhpcy5fcmVuZGVyQXhpcyA9IGZ1bmN0aW9uKCB0YXJnZXQgKSB7XG5cblx0XHRcdHZhciBpLCBheGlzLCBwYWRQb3MsIHBhZFdyYXBwZXI7XG5cblx0XHRcdHRhcmdldCA9IHRhcmdldCB8fCB0aGlzLmVsZW1lbnQ7XG5cblx0XHRcdGZvciAoIGkgaW4gdGhpcy5oYW5kbGVzICkge1xuXG5cdFx0XHRcdGlmICggdGhpcy5oYW5kbGVzWyBpIF0uY29uc3RydWN0b3IgPT09IFN0cmluZyApIHtcblx0XHRcdFx0XHR0aGlzLmhhbmRsZXNbIGkgXSA9IHRoaXMuZWxlbWVudC5jaGlsZHJlbiggdGhpcy5oYW5kbGVzWyBpIF0gKS5maXJzdCgpLnNob3coKTtcblx0XHRcdFx0fSBlbHNlIGlmICggdGhpcy5oYW5kbGVzWyBpIF0uanF1ZXJ5IHx8IHRoaXMuaGFuZGxlc1sgaSBdLm5vZGVUeXBlICkge1xuXHRcdFx0XHRcdHRoaXMuaGFuZGxlc1sgaSBdID0gJCggdGhpcy5oYW5kbGVzWyBpIF0gKTtcblx0XHRcdFx0XHR0aGlzLl9vbiggdGhpcy5oYW5kbGVzWyBpIF0sIHsgXCJtb3VzZWRvd25cIjogdGhhdC5fbW91c2VEb3duIH0gKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggdGhpcy5lbGVtZW50SXNXcmFwcGVyICYmXG5cdFx0XHRcdFx0XHR0aGlzLm9yaWdpbmFsRWxlbWVudFsgMCBdXG5cdFx0XHRcdFx0XHRcdC5ub2RlTmFtZVxuXHRcdFx0XHRcdFx0XHQubWF0Y2goIC9eKHRleHRhcmVhfGlucHV0fHNlbGVjdHxidXR0b24pJC9pICkgKSB7XG5cdFx0XHRcdFx0YXhpcyA9ICQoIHRoaXMuaGFuZGxlc1sgaSBdLCB0aGlzLmVsZW1lbnQgKTtcblxuXHRcdFx0XHRcdHBhZFdyYXBwZXIgPSAvc3d8bmV8bnd8c2V8bnxzLy50ZXN0KCBpICkgP1xuXHRcdFx0XHRcdFx0YXhpcy5vdXRlckhlaWdodCgpIDpcblx0XHRcdFx0XHRcdGF4aXMub3V0ZXJXaWR0aCgpO1xuXG5cdFx0XHRcdFx0cGFkUG9zID0gWyBcInBhZGRpbmdcIixcblx0XHRcdFx0XHRcdC9uZXxud3xuLy50ZXN0KCBpICkgPyBcIlRvcFwiIDpcblx0XHRcdFx0XHRcdC9zZXxzd3xzLy50ZXN0KCBpICkgPyBcIkJvdHRvbVwiIDpcblx0XHRcdFx0XHRcdC9eZSQvLnRlc3QoIGkgKSA/IFwiUmlnaHRcIiA6IFwiTGVmdFwiIF0uam9pbiggXCJcIiApO1xuXG5cdFx0XHRcdFx0dGFyZ2V0LmNzcyggcGFkUG9zLCBwYWRXcmFwcGVyICk7XG5cblx0XHRcdFx0XHR0aGlzLl9wcm9wb3J0aW9uYWxseVJlc2l6ZSgpO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5faGFuZGxlcyA9IHRoaXMuX2hhbmRsZXMuYWRkKCB0aGlzLmhhbmRsZXNbIGkgXSApO1xuXHRcdFx0fVxuXHRcdH07XG5cblx0XHQvLyBUT0RPOiBtYWtlIHJlbmRlckF4aXMgYSBwcm90b3R5cGUgZnVuY3Rpb25cblx0XHR0aGlzLl9yZW5kZXJBeGlzKCB0aGlzLmVsZW1lbnQgKTtcblxuXHRcdHRoaXMuX2hhbmRsZXMgPSB0aGlzLl9oYW5kbGVzLmFkZCggdGhpcy5lbGVtZW50LmZpbmQoIFwiLnVpLXJlc2l6YWJsZS1oYW5kbGVcIiApICk7XG5cdFx0dGhpcy5faGFuZGxlcy5kaXNhYmxlU2VsZWN0aW9uKCk7XG5cblx0XHR0aGlzLl9oYW5kbGVzLm9uKCBcIm1vdXNlb3ZlclwiLCBmdW5jdGlvbigpIHtcblx0XHRcdGlmICggIXRoYXQucmVzaXppbmcgKSB7XG5cdFx0XHRcdGlmICggdGhpcy5jbGFzc05hbWUgKSB7XG5cdFx0XHRcdFx0YXhpcyA9IHRoaXMuY2xhc3NOYW1lLm1hdGNoKCAvdWktcmVzaXphYmxlLShzZXxzd3xuZXxud3xufGV8c3x3KS9pICk7XG5cdFx0XHRcdH1cblx0XHRcdFx0dGhhdC5heGlzID0gYXhpcyAmJiBheGlzWyAxIF0gPyBheGlzWyAxIF0gOiBcInNlXCI7XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdFx0aWYgKCBvLmF1dG9IaWRlICkge1xuXHRcdFx0dGhpcy5faGFuZGxlcy5oaWRlKCk7XG5cdFx0XHR0aGlzLl9hZGRDbGFzcyggXCJ1aS1yZXNpemFibGUtYXV0b2hpZGVcIiApO1xuXHRcdH1cblx0fSxcblxuXHRfcmVtb3ZlSGFuZGxlczogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5faGFuZGxlcy5yZW1vdmUoKTtcblx0fSxcblxuXHRfbW91c2VDYXB0dXJlOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dmFyIGksIGhhbmRsZSxcblx0XHRcdGNhcHR1cmUgPSBmYWxzZTtcblxuXHRcdGZvciAoIGkgaW4gdGhpcy5oYW5kbGVzICkge1xuXHRcdFx0aGFuZGxlID0gJCggdGhpcy5oYW5kbGVzWyBpIF0gKVsgMCBdO1xuXHRcdFx0aWYgKCBoYW5kbGUgPT09IGV2ZW50LnRhcmdldCB8fCAkLmNvbnRhaW5zKCBoYW5kbGUsIGV2ZW50LnRhcmdldCApICkge1xuXHRcdFx0XHRjYXB0dXJlID0gdHJ1ZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gIXRoaXMub3B0aW9ucy5kaXNhYmxlZCAmJiBjYXB0dXJlO1xuXHR9LFxuXG5cdF9tb3VzZVN0YXJ0OiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cblx0XHR2YXIgY3VybGVmdCwgY3VydG9wLCBjdXJzb3IsXG5cdFx0XHRvID0gdGhpcy5vcHRpb25zLFxuXHRcdFx0ZWwgPSB0aGlzLmVsZW1lbnQ7XG5cblx0XHR0aGlzLnJlc2l6aW5nID0gdHJ1ZTtcblxuXHRcdHRoaXMuX3JlbmRlclByb3h5KCk7XG5cblx0XHRjdXJsZWZ0ID0gdGhpcy5fbnVtKCB0aGlzLmhlbHBlci5jc3MoIFwibGVmdFwiICkgKTtcblx0XHRjdXJ0b3AgPSB0aGlzLl9udW0oIHRoaXMuaGVscGVyLmNzcyggXCJ0b3BcIiApICk7XG5cblx0XHRpZiAoIG8uY29udGFpbm1lbnQgKSB7XG5cdFx0XHRjdXJsZWZ0ICs9ICQoIG8uY29udGFpbm1lbnQgKS5zY3JvbGxMZWZ0KCkgfHwgMDtcblx0XHRcdGN1cnRvcCArPSAkKCBvLmNvbnRhaW5tZW50ICkuc2Nyb2xsVG9wKCkgfHwgMDtcblx0XHR9XG5cblx0XHR0aGlzLm9mZnNldCA9IHRoaXMuaGVscGVyLm9mZnNldCgpO1xuXHRcdHRoaXMucG9zaXRpb24gPSB7IGxlZnQ6IGN1cmxlZnQsIHRvcDogY3VydG9wIH07XG5cblx0XHR0aGlzLnNpemUgPSB0aGlzLl9oZWxwZXIgPyB7XG5cdFx0XHRcdHdpZHRoOiB0aGlzLmhlbHBlci53aWR0aCgpLFxuXHRcdFx0XHRoZWlnaHQ6IHRoaXMuaGVscGVyLmhlaWdodCgpXG5cdFx0XHR9IDoge1xuXHRcdFx0XHR3aWR0aDogZWwud2lkdGgoKSxcblx0XHRcdFx0aGVpZ2h0OiBlbC5oZWlnaHQoKVxuXHRcdFx0fTtcblxuXHRcdHRoaXMub3JpZ2luYWxTaXplID0gdGhpcy5faGVscGVyID8ge1xuXHRcdFx0XHR3aWR0aDogZWwub3V0ZXJXaWR0aCgpLFxuXHRcdFx0XHRoZWlnaHQ6IGVsLm91dGVySGVpZ2h0KClcblx0XHRcdH0gOiB7XG5cdFx0XHRcdHdpZHRoOiBlbC53aWR0aCgpLFxuXHRcdFx0XHRoZWlnaHQ6IGVsLmhlaWdodCgpXG5cdFx0XHR9O1xuXG5cdFx0dGhpcy5zaXplRGlmZiA9IHtcblx0XHRcdHdpZHRoOiBlbC5vdXRlcldpZHRoKCkgLSBlbC53aWR0aCgpLFxuXHRcdFx0aGVpZ2h0OiBlbC5vdXRlckhlaWdodCgpIC0gZWwuaGVpZ2h0KClcblx0XHR9O1xuXG5cdFx0dGhpcy5vcmlnaW5hbFBvc2l0aW9uID0geyBsZWZ0OiBjdXJsZWZ0LCB0b3A6IGN1cnRvcCB9O1xuXHRcdHRoaXMub3JpZ2luYWxNb3VzZVBvc2l0aW9uID0geyBsZWZ0OiBldmVudC5wYWdlWCwgdG9wOiBldmVudC5wYWdlWSB9O1xuXG5cdFx0dGhpcy5hc3BlY3RSYXRpbyA9ICggdHlwZW9mIG8uYXNwZWN0UmF0aW8gPT09IFwibnVtYmVyXCIgKSA/XG5cdFx0XHRvLmFzcGVjdFJhdGlvIDpcblx0XHRcdCggKCB0aGlzLm9yaWdpbmFsU2l6ZS53aWR0aCAvIHRoaXMub3JpZ2luYWxTaXplLmhlaWdodCApIHx8IDEgKTtcblxuXHRcdGN1cnNvciA9ICQoIFwiLnVpLXJlc2l6YWJsZS1cIiArIHRoaXMuYXhpcyApLmNzcyggXCJjdXJzb3JcIiApO1xuXHRcdCQoIFwiYm9keVwiICkuY3NzKCBcImN1cnNvclwiLCBjdXJzb3IgPT09IFwiYXV0b1wiID8gdGhpcy5heGlzICsgXCItcmVzaXplXCIgOiBjdXJzb3IgKTtcblxuXHRcdHRoaXMuX2FkZENsYXNzKCBcInVpLXJlc2l6YWJsZS1yZXNpemluZ1wiICk7XG5cdFx0dGhpcy5fcHJvcGFnYXRlKCBcInN0YXJ0XCIsIGV2ZW50ICk7XG5cdFx0cmV0dXJuIHRydWU7XG5cdH0sXG5cblx0X21vdXNlRHJhZzogZnVuY3Rpb24oIGV2ZW50ICkge1xuXG5cdFx0dmFyIGRhdGEsIHByb3BzLFxuXHRcdFx0c21wID0gdGhpcy5vcmlnaW5hbE1vdXNlUG9zaXRpb24sXG5cdFx0XHRhID0gdGhpcy5heGlzLFxuXHRcdFx0ZHggPSAoIGV2ZW50LnBhZ2VYIC0gc21wLmxlZnQgKSB8fCAwLFxuXHRcdFx0ZHkgPSAoIGV2ZW50LnBhZ2VZIC0gc21wLnRvcCApIHx8IDAsXG5cdFx0XHR0cmlnZ2VyID0gdGhpcy5fY2hhbmdlWyBhIF07XG5cblx0XHR0aGlzLl91cGRhdGVQcmV2UHJvcGVydGllcygpO1xuXG5cdFx0aWYgKCAhdHJpZ2dlciApIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHRkYXRhID0gdHJpZ2dlci5hcHBseSggdGhpcywgWyBldmVudCwgZHgsIGR5IF0gKTtcblxuXHRcdHRoaXMuX3VwZGF0ZVZpcnR1YWxCb3VuZGFyaWVzKCBldmVudC5zaGlmdEtleSApO1xuXHRcdGlmICggdGhpcy5fYXNwZWN0UmF0aW8gfHwgZXZlbnQuc2hpZnRLZXkgKSB7XG5cdFx0XHRkYXRhID0gdGhpcy5fdXBkYXRlUmF0aW8oIGRhdGEsIGV2ZW50ICk7XG5cdFx0fVxuXG5cdFx0ZGF0YSA9IHRoaXMuX3Jlc3BlY3RTaXplKCBkYXRhLCBldmVudCApO1xuXG5cdFx0dGhpcy5fdXBkYXRlQ2FjaGUoIGRhdGEgKTtcblxuXHRcdHRoaXMuX3Byb3BhZ2F0ZSggXCJyZXNpemVcIiwgZXZlbnQgKTtcblxuXHRcdHByb3BzID0gdGhpcy5fYXBwbHlDaGFuZ2VzKCk7XG5cblx0XHRpZiAoICF0aGlzLl9oZWxwZXIgJiYgdGhpcy5fcHJvcG9ydGlvbmFsbHlSZXNpemVFbGVtZW50cy5sZW5ndGggKSB7XG5cdFx0XHR0aGlzLl9wcm9wb3J0aW9uYWxseVJlc2l6ZSgpO1xuXHRcdH1cblxuXHRcdGlmICggISQuaXNFbXB0eU9iamVjdCggcHJvcHMgKSApIHtcblx0XHRcdHRoaXMuX3VwZGF0ZVByZXZQcm9wZXJ0aWVzKCk7XG5cdFx0XHR0aGlzLl90cmlnZ2VyKCBcInJlc2l6ZVwiLCBldmVudCwgdGhpcy51aSgpICk7XG5cdFx0XHR0aGlzLl9hcHBseUNoYW5nZXMoKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0X21vdXNlU3RvcDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXG5cdFx0dGhpcy5yZXNpemluZyA9IGZhbHNlO1xuXHRcdHZhciBwciwgaXN0YSwgc29mZnNldGgsIHNvZmZzZXR3LCBzLCBsZWZ0LCB0b3AsXG5cdFx0XHRvID0gdGhpcy5vcHRpb25zLCB0aGF0ID0gdGhpcztcblxuXHRcdGlmICggdGhpcy5faGVscGVyICkge1xuXG5cdFx0XHRwciA9IHRoaXMuX3Byb3BvcnRpb25hbGx5UmVzaXplRWxlbWVudHM7XG5cdFx0XHRpc3RhID0gcHIubGVuZ3RoICYmICggL3RleHRhcmVhL2kgKS50ZXN0KCBwclsgMCBdLm5vZGVOYW1lICk7XG5cdFx0XHRzb2Zmc2V0aCA9IGlzdGEgJiYgdGhpcy5faGFzU2Nyb2xsKCBwclsgMCBdLCBcImxlZnRcIiApID8gMCA6IHRoYXQuc2l6ZURpZmYuaGVpZ2h0O1xuXHRcdFx0c29mZnNldHcgPSBpc3RhID8gMCA6IHRoYXQuc2l6ZURpZmYud2lkdGg7XG5cblx0XHRcdHMgPSB7XG5cdFx0XHRcdHdpZHRoOiAoIHRoYXQuaGVscGVyLndpZHRoKCkgIC0gc29mZnNldHcgKSxcblx0XHRcdFx0aGVpZ2h0OiAoIHRoYXQuaGVscGVyLmhlaWdodCgpIC0gc29mZnNldGggKVxuXHRcdFx0fTtcblx0XHRcdGxlZnQgPSAoIHBhcnNlRmxvYXQoIHRoYXQuZWxlbWVudC5jc3MoIFwibGVmdFwiICkgKSArXG5cdFx0XHRcdCggdGhhdC5wb3NpdGlvbi5sZWZ0IC0gdGhhdC5vcmlnaW5hbFBvc2l0aW9uLmxlZnQgKSApIHx8IG51bGw7XG5cdFx0XHR0b3AgPSAoIHBhcnNlRmxvYXQoIHRoYXQuZWxlbWVudC5jc3MoIFwidG9wXCIgKSApICtcblx0XHRcdFx0KCB0aGF0LnBvc2l0aW9uLnRvcCAtIHRoYXQub3JpZ2luYWxQb3NpdGlvbi50b3AgKSApIHx8IG51bGw7XG5cblx0XHRcdGlmICggIW8uYW5pbWF0ZSApIHtcblx0XHRcdFx0dGhpcy5lbGVtZW50LmNzcyggJC5leHRlbmQoIHMsIHsgdG9wOiB0b3AsIGxlZnQ6IGxlZnQgfSApICk7XG5cdFx0XHR9XG5cblx0XHRcdHRoYXQuaGVscGVyLmhlaWdodCggdGhhdC5zaXplLmhlaWdodCApO1xuXHRcdFx0dGhhdC5oZWxwZXIud2lkdGgoIHRoYXQuc2l6ZS53aWR0aCApO1xuXG5cdFx0XHRpZiAoIHRoaXMuX2hlbHBlciAmJiAhby5hbmltYXRlICkge1xuXHRcdFx0XHR0aGlzLl9wcm9wb3J0aW9uYWxseVJlc2l6ZSgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdCQoIFwiYm9keVwiICkuY3NzKCBcImN1cnNvclwiLCBcImF1dG9cIiApO1xuXG5cdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIFwidWktcmVzaXphYmxlLXJlc2l6aW5nXCIgKTtcblxuXHRcdHRoaXMuX3Byb3BhZ2F0ZSggXCJzdG9wXCIsIGV2ZW50ICk7XG5cblx0XHRpZiAoIHRoaXMuX2hlbHBlciApIHtcblx0XHRcdHRoaXMuaGVscGVyLnJlbW92ZSgpO1xuXHRcdH1cblxuXHRcdHJldHVybiBmYWxzZTtcblxuXHR9LFxuXG5cdF91cGRhdGVQcmV2UHJvcGVydGllczogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5wcmV2UG9zaXRpb24gPSB7XG5cdFx0XHR0b3A6IHRoaXMucG9zaXRpb24udG9wLFxuXHRcdFx0bGVmdDogdGhpcy5wb3NpdGlvbi5sZWZ0XG5cdFx0fTtcblx0XHR0aGlzLnByZXZTaXplID0ge1xuXHRcdFx0d2lkdGg6IHRoaXMuc2l6ZS53aWR0aCxcblx0XHRcdGhlaWdodDogdGhpcy5zaXplLmhlaWdodFxuXHRcdH07XG5cdH0sXG5cblx0X2FwcGx5Q2hhbmdlczogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHByb3BzID0ge307XG5cblx0XHRpZiAoIHRoaXMucG9zaXRpb24udG9wICE9PSB0aGlzLnByZXZQb3NpdGlvbi50b3AgKSB7XG5cdFx0XHRwcm9wcy50b3AgPSB0aGlzLnBvc2l0aW9uLnRvcCArIFwicHhcIjtcblx0XHR9XG5cdFx0aWYgKCB0aGlzLnBvc2l0aW9uLmxlZnQgIT09IHRoaXMucHJldlBvc2l0aW9uLmxlZnQgKSB7XG5cdFx0XHRwcm9wcy5sZWZ0ID0gdGhpcy5wb3NpdGlvbi5sZWZ0ICsgXCJweFwiO1xuXHRcdH1cblx0XHRpZiAoIHRoaXMuc2l6ZS53aWR0aCAhPT0gdGhpcy5wcmV2U2l6ZS53aWR0aCApIHtcblx0XHRcdHByb3BzLndpZHRoID0gdGhpcy5zaXplLndpZHRoICsgXCJweFwiO1xuXHRcdH1cblx0XHRpZiAoIHRoaXMuc2l6ZS5oZWlnaHQgIT09IHRoaXMucHJldlNpemUuaGVpZ2h0ICkge1xuXHRcdFx0cHJvcHMuaGVpZ2h0ID0gdGhpcy5zaXplLmhlaWdodCArIFwicHhcIjtcblx0XHR9XG5cblx0XHR0aGlzLmhlbHBlci5jc3MoIHByb3BzICk7XG5cblx0XHRyZXR1cm4gcHJvcHM7XG5cdH0sXG5cblx0X3VwZGF0ZVZpcnR1YWxCb3VuZGFyaWVzOiBmdW5jdGlvbiggZm9yY2VBc3BlY3RSYXRpbyApIHtcblx0XHR2YXIgcE1pbldpZHRoLCBwTWF4V2lkdGgsIHBNaW5IZWlnaHQsIHBNYXhIZWlnaHQsIGIsXG5cdFx0XHRvID0gdGhpcy5vcHRpb25zO1xuXG5cdFx0YiA9IHtcblx0XHRcdG1pbldpZHRoOiB0aGlzLl9pc051bWJlciggby5taW5XaWR0aCApID8gby5taW5XaWR0aCA6IDAsXG5cdFx0XHRtYXhXaWR0aDogdGhpcy5faXNOdW1iZXIoIG8ubWF4V2lkdGggKSA/IG8ubWF4V2lkdGggOiBJbmZpbml0eSxcblx0XHRcdG1pbkhlaWdodDogdGhpcy5faXNOdW1iZXIoIG8ubWluSGVpZ2h0ICkgPyBvLm1pbkhlaWdodCA6IDAsXG5cdFx0XHRtYXhIZWlnaHQ6IHRoaXMuX2lzTnVtYmVyKCBvLm1heEhlaWdodCApID8gby5tYXhIZWlnaHQgOiBJbmZpbml0eVxuXHRcdH07XG5cblx0XHRpZiAoIHRoaXMuX2FzcGVjdFJhdGlvIHx8IGZvcmNlQXNwZWN0UmF0aW8gKSB7XG5cdFx0XHRwTWluV2lkdGggPSBiLm1pbkhlaWdodCAqIHRoaXMuYXNwZWN0UmF0aW87XG5cdFx0XHRwTWluSGVpZ2h0ID0gYi5taW5XaWR0aCAvIHRoaXMuYXNwZWN0UmF0aW87XG5cdFx0XHRwTWF4V2lkdGggPSBiLm1heEhlaWdodCAqIHRoaXMuYXNwZWN0UmF0aW87XG5cdFx0XHRwTWF4SGVpZ2h0ID0gYi5tYXhXaWR0aCAvIHRoaXMuYXNwZWN0UmF0aW87XG5cblx0XHRcdGlmICggcE1pbldpZHRoID4gYi5taW5XaWR0aCApIHtcblx0XHRcdFx0Yi5taW5XaWR0aCA9IHBNaW5XaWR0aDtcblx0XHRcdH1cblx0XHRcdGlmICggcE1pbkhlaWdodCA+IGIubWluSGVpZ2h0ICkge1xuXHRcdFx0XHRiLm1pbkhlaWdodCA9IHBNaW5IZWlnaHQ7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIHBNYXhXaWR0aCA8IGIubWF4V2lkdGggKSB7XG5cdFx0XHRcdGIubWF4V2lkdGggPSBwTWF4V2lkdGg7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIHBNYXhIZWlnaHQgPCBiLm1heEhlaWdodCApIHtcblx0XHRcdFx0Yi5tYXhIZWlnaHQgPSBwTWF4SGVpZ2h0O1xuXHRcdFx0fVxuXHRcdH1cblx0XHR0aGlzLl92Qm91bmRhcmllcyA9IGI7XG5cdH0sXG5cblx0X3VwZGF0ZUNhY2hlOiBmdW5jdGlvbiggZGF0YSApIHtcblx0XHR0aGlzLm9mZnNldCA9IHRoaXMuaGVscGVyLm9mZnNldCgpO1xuXHRcdGlmICggdGhpcy5faXNOdW1iZXIoIGRhdGEubGVmdCApICkge1xuXHRcdFx0dGhpcy5wb3NpdGlvbi5sZWZ0ID0gZGF0YS5sZWZ0O1xuXHRcdH1cblx0XHRpZiAoIHRoaXMuX2lzTnVtYmVyKCBkYXRhLnRvcCApICkge1xuXHRcdFx0dGhpcy5wb3NpdGlvbi50b3AgPSBkYXRhLnRvcDtcblx0XHR9XG5cdFx0aWYgKCB0aGlzLl9pc051bWJlciggZGF0YS5oZWlnaHQgKSApIHtcblx0XHRcdHRoaXMuc2l6ZS5oZWlnaHQgPSBkYXRhLmhlaWdodDtcblx0XHR9XG5cdFx0aWYgKCB0aGlzLl9pc051bWJlciggZGF0YS53aWR0aCApICkge1xuXHRcdFx0dGhpcy5zaXplLndpZHRoID0gZGF0YS53aWR0aDtcblx0XHR9XG5cdH0sXG5cblx0X3VwZGF0ZVJhdGlvOiBmdW5jdGlvbiggZGF0YSApIHtcblxuXHRcdHZhciBjcG9zID0gdGhpcy5wb3NpdGlvbixcblx0XHRcdGNzaXplID0gdGhpcy5zaXplLFxuXHRcdFx0YSA9IHRoaXMuYXhpcztcblxuXHRcdGlmICggdGhpcy5faXNOdW1iZXIoIGRhdGEuaGVpZ2h0ICkgKSB7XG5cdFx0XHRkYXRhLndpZHRoID0gKCBkYXRhLmhlaWdodCAqIHRoaXMuYXNwZWN0UmF0aW8gKTtcblx0XHR9IGVsc2UgaWYgKCB0aGlzLl9pc051bWJlciggZGF0YS53aWR0aCApICkge1xuXHRcdFx0ZGF0YS5oZWlnaHQgPSAoIGRhdGEud2lkdGggLyB0aGlzLmFzcGVjdFJhdGlvICk7XG5cdFx0fVxuXG5cdFx0aWYgKCBhID09PSBcInN3XCIgKSB7XG5cdFx0XHRkYXRhLmxlZnQgPSBjcG9zLmxlZnQgKyAoIGNzaXplLndpZHRoIC0gZGF0YS53aWR0aCApO1xuXHRcdFx0ZGF0YS50b3AgPSBudWxsO1xuXHRcdH1cblx0XHRpZiAoIGEgPT09IFwibndcIiApIHtcblx0XHRcdGRhdGEudG9wID0gY3Bvcy50b3AgKyAoIGNzaXplLmhlaWdodCAtIGRhdGEuaGVpZ2h0ICk7XG5cdFx0XHRkYXRhLmxlZnQgPSBjcG9zLmxlZnQgKyAoIGNzaXplLndpZHRoIC0gZGF0YS53aWR0aCApO1xuXHRcdH1cblxuXHRcdHJldHVybiBkYXRhO1xuXHR9LFxuXG5cdF9yZXNwZWN0U2l6ZTogZnVuY3Rpb24oIGRhdGEgKSB7XG5cblx0XHR2YXIgbyA9IHRoaXMuX3ZCb3VuZGFyaWVzLFxuXHRcdFx0YSA9IHRoaXMuYXhpcyxcblx0XHRcdGlzbWF4dyA9IHRoaXMuX2lzTnVtYmVyKCBkYXRhLndpZHRoICkgJiYgby5tYXhXaWR0aCAmJiAoIG8ubWF4V2lkdGggPCBkYXRhLndpZHRoICksXG5cdFx0XHRpc21heGggPSB0aGlzLl9pc051bWJlciggZGF0YS5oZWlnaHQgKSAmJiBvLm1heEhlaWdodCAmJiAoIG8ubWF4SGVpZ2h0IDwgZGF0YS5oZWlnaHQgKSxcblx0XHRcdGlzbWludyA9IHRoaXMuX2lzTnVtYmVyKCBkYXRhLndpZHRoICkgJiYgby5taW5XaWR0aCAmJiAoIG8ubWluV2lkdGggPiBkYXRhLndpZHRoICksXG5cdFx0XHRpc21pbmggPSB0aGlzLl9pc051bWJlciggZGF0YS5oZWlnaHQgKSAmJiBvLm1pbkhlaWdodCAmJiAoIG8ubWluSGVpZ2h0ID4gZGF0YS5oZWlnaHQgKSxcblx0XHRcdGR3ID0gdGhpcy5vcmlnaW5hbFBvc2l0aW9uLmxlZnQgKyB0aGlzLm9yaWdpbmFsU2l6ZS53aWR0aCxcblx0XHRcdGRoID0gdGhpcy5vcmlnaW5hbFBvc2l0aW9uLnRvcCArIHRoaXMub3JpZ2luYWxTaXplLmhlaWdodCxcblx0XHRcdGN3ID0gL3N3fG53fHcvLnRlc3QoIGEgKSwgY2ggPSAvbnd8bmV8bi8udGVzdCggYSApO1xuXHRcdGlmICggaXNtaW53ICkge1xuXHRcdFx0ZGF0YS53aWR0aCA9IG8ubWluV2lkdGg7XG5cdFx0fVxuXHRcdGlmICggaXNtaW5oICkge1xuXHRcdFx0ZGF0YS5oZWlnaHQgPSBvLm1pbkhlaWdodDtcblx0XHR9XG5cdFx0aWYgKCBpc21heHcgKSB7XG5cdFx0XHRkYXRhLndpZHRoID0gby5tYXhXaWR0aDtcblx0XHR9XG5cdFx0aWYgKCBpc21heGggKSB7XG5cdFx0XHRkYXRhLmhlaWdodCA9IG8ubWF4SGVpZ2h0O1xuXHRcdH1cblxuXHRcdGlmICggaXNtaW53ICYmIGN3ICkge1xuXHRcdFx0ZGF0YS5sZWZ0ID0gZHcgLSBvLm1pbldpZHRoO1xuXHRcdH1cblx0XHRpZiAoIGlzbWF4dyAmJiBjdyApIHtcblx0XHRcdGRhdGEubGVmdCA9IGR3IC0gby5tYXhXaWR0aDtcblx0XHR9XG5cdFx0aWYgKCBpc21pbmggJiYgY2ggKSB7XG5cdFx0XHRkYXRhLnRvcCA9IGRoIC0gby5taW5IZWlnaHQ7XG5cdFx0fVxuXHRcdGlmICggaXNtYXhoICYmIGNoICkge1xuXHRcdFx0ZGF0YS50b3AgPSBkaCAtIG8ubWF4SGVpZ2h0O1xuXHRcdH1cblxuXHRcdC8vIEZpeGluZyBqdW1wIGVycm9yIG9uIHRvcC9sZWZ0IC0gYnVnICMyMzMwXG5cdFx0aWYgKCAhZGF0YS53aWR0aCAmJiAhZGF0YS5oZWlnaHQgJiYgIWRhdGEubGVmdCAmJiBkYXRhLnRvcCApIHtcblx0XHRcdGRhdGEudG9wID0gbnVsbDtcblx0XHR9IGVsc2UgaWYgKCAhZGF0YS53aWR0aCAmJiAhZGF0YS5oZWlnaHQgJiYgIWRhdGEudG9wICYmIGRhdGEubGVmdCApIHtcblx0XHRcdGRhdGEubGVmdCA9IG51bGw7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGRhdGE7XG5cdH0sXG5cblx0X2dldFBhZGRpbmdQbHVzQm9yZGVyRGltZW5zaW9uczogZnVuY3Rpb24oIGVsZW1lbnQgKSB7XG5cdFx0dmFyIGkgPSAwLFxuXHRcdFx0d2lkdGhzID0gW10sXG5cdFx0XHRib3JkZXJzID0gW1xuXHRcdFx0XHRlbGVtZW50LmNzcyggXCJib3JkZXJUb3BXaWR0aFwiICksXG5cdFx0XHRcdGVsZW1lbnQuY3NzKCBcImJvcmRlclJpZ2h0V2lkdGhcIiApLFxuXHRcdFx0XHRlbGVtZW50LmNzcyggXCJib3JkZXJCb3R0b21XaWR0aFwiICksXG5cdFx0XHRcdGVsZW1lbnQuY3NzKCBcImJvcmRlckxlZnRXaWR0aFwiIClcblx0XHRcdF0sXG5cdFx0XHRwYWRkaW5ncyA9IFtcblx0XHRcdFx0ZWxlbWVudC5jc3MoIFwicGFkZGluZ1RvcFwiICksXG5cdFx0XHRcdGVsZW1lbnQuY3NzKCBcInBhZGRpbmdSaWdodFwiICksXG5cdFx0XHRcdGVsZW1lbnQuY3NzKCBcInBhZGRpbmdCb3R0b21cIiApLFxuXHRcdFx0XHRlbGVtZW50LmNzcyggXCJwYWRkaW5nTGVmdFwiIClcblx0XHRcdF07XG5cblx0XHRmb3IgKCA7IGkgPCA0OyBpKysgKSB7XG5cdFx0XHR3aWR0aHNbIGkgXSA9ICggcGFyc2VGbG9hdCggYm9yZGVyc1sgaSBdICkgfHwgMCApO1xuXHRcdFx0d2lkdGhzWyBpIF0gKz0gKCBwYXJzZUZsb2F0KCBwYWRkaW5nc1sgaSBdICkgfHwgMCApO1xuXHRcdH1cblxuXHRcdHJldHVybiB7XG5cdFx0XHRoZWlnaHQ6IHdpZHRoc1sgMCBdICsgd2lkdGhzWyAyIF0sXG5cdFx0XHR3aWR0aDogd2lkdGhzWyAxIF0gKyB3aWR0aHNbIDMgXVxuXHRcdH07XG5cdH0sXG5cblx0X3Byb3BvcnRpb25hbGx5UmVzaXplOiBmdW5jdGlvbigpIHtcblxuXHRcdGlmICggIXRoaXMuX3Byb3BvcnRpb25hbGx5UmVzaXplRWxlbWVudHMubGVuZ3RoICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBwcmVsLFxuXHRcdFx0aSA9IDAsXG5cdFx0XHRlbGVtZW50ID0gdGhpcy5oZWxwZXIgfHwgdGhpcy5lbGVtZW50O1xuXG5cdFx0Zm9yICggOyBpIDwgdGhpcy5fcHJvcG9ydGlvbmFsbHlSZXNpemVFbGVtZW50cy5sZW5ndGg7IGkrKyApIHtcblxuXHRcdFx0cHJlbCA9IHRoaXMuX3Byb3BvcnRpb25hbGx5UmVzaXplRWxlbWVudHNbIGkgXTtcblxuXHRcdFx0Ly8gVE9ETzogU2VlbXMgbGlrZSBhIGJ1ZyB0byBjYWNoZSB0aGlzLm91dGVyRGltZW5zaW9uc1xuXHRcdFx0Ly8gY29uc2lkZXJpbmcgdGhhdCB3ZSBhcmUgaW4gYSBsb29wLlxuXHRcdFx0aWYgKCAhdGhpcy5vdXRlckRpbWVuc2lvbnMgKSB7XG5cdFx0XHRcdHRoaXMub3V0ZXJEaW1lbnNpb25zID0gdGhpcy5fZ2V0UGFkZGluZ1BsdXNCb3JkZXJEaW1lbnNpb25zKCBwcmVsICk7XG5cdFx0XHR9XG5cblx0XHRcdHByZWwuY3NzKCB7XG5cdFx0XHRcdGhlaWdodDogKCBlbGVtZW50LmhlaWdodCgpIC0gdGhpcy5vdXRlckRpbWVuc2lvbnMuaGVpZ2h0ICkgfHwgMCxcblx0XHRcdFx0d2lkdGg6ICggZWxlbWVudC53aWR0aCgpIC0gdGhpcy5vdXRlckRpbWVuc2lvbnMud2lkdGggKSB8fCAwXG5cdFx0XHR9ICk7XG5cblx0XHR9XG5cblx0fSxcblxuXHRfcmVuZGVyUHJveHk6IGZ1bmN0aW9uKCkge1xuXG5cdFx0dmFyIGVsID0gdGhpcy5lbGVtZW50LCBvID0gdGhpcy5vcHRpb25zO1xuXHRcdHRoaXMuZWxlbWVudE9mZnNldCA9IGVsLm9mZnNldCgpO1xuXG5cdFx0aWYgKCB0aGlzLl9oZWxwZXIgKSB7XG5cblx0XHRcdHRoaXMuaGVscGVyID0gdGhpcy5oZWxwZXIgfHwgJCggXCI8ZGl2IHN0eWxlPSdvdmVyZmxvdzpoaWRkZW47Jz48L2Rpdj5cIiApO1xuXG5cdFx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5oZWxwZXIsIHRoaXMuX2hlbHBlciApO1xuXHRcdFx0dGhpcy5oZWxwZXIuY3NzKCB7XG5cdFx0XHRcdHdpZHRoOiB0aGlzLmVsZW1lbnQub3V0ZXJXaWR0aCgpLFxuXHRcdFx0XHRoZWlnaHQ6IHRoaXMuZWxlbWVudC5vdXRlckhlaWdodCgpLFxuXHRcdFx0XHRwb3NpdGlvbjogXCJhYnNvbHV0ZVwiLFxuXHRcdFx0XHRsZWZ0OiB0aGlzLmVsZW1lbnRPZmZzZXQubGVmdCArIFwicHhcIixcblx0XHRcdFx0dG9wOiB0aGlzLmVsZW1lbnRPZmZzZXQudG9wICsgXCJweFwiLFxuXHRcdFx0XHR6SW5kZXg6ICsrby56SW5kZXggLy9UT0RPOiBEb24ndCBtb2RpZnkgb3B0aW9uXG5cdFx0XHR9ICk7XG5cblx0XHRcdHRoaXMuaGVscGVyXG5cdFx0XHRcdC5hcHBlbmRUbyggXCJib2R5XCIgKVxuXHRcdFx0XHQuZGlzYWJsZVNlbGVjdGlvbigpO1xuXG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuaGVscGVyID0gdGhpcy5lbGVtZW50O1xuXHRcdH1cblxuXHR9LFxuXG5cdF9jaGFuZ2U6IHtcblx0XHRlOiBmdW5jdGlvbiggZXZlbnQsIGR4ICkge1xuXHRcdFx0cmV0dXJuIHsgd2lkdGg6IHRoaXMub3JpZ2luYWxTaXplLndpZHRoICsgZHggfTtcblx0XHR9LFxuXHRcdHc6IGZ1bmN0aW9uKCBldmVudCwgZHggKSB7XG5cdFx0XHR2YXIgY3MgPSB0aGlzLm9yaWdpbmFsU2l6ZSwgc3AgPSB0aGlzLm9yaWdpbmFsUG9zaXRpb247XG5cdFx0XHRyZXR1cm4geyBsZWZ0OiBzcC5sZWZ0ICsgZHgsIHdpZHRoOiBjcy53aWR0aCAtIGR4IH07XG5cdFx0fSxcblx0XHRuOiBmdW5jdGlvbiggZXZlbnQsIGR4LCBkeSApIHtcblx0XHRcdHZhciBjcyA9IHRoaXMub3JpZ2luYWxTaXplLCBzcCA9IHRoaXMub3JpZ2luYWxQb3NpdGlvbjtcblx0XHRcdHJldHVybiB7IHRvcDogc3AudG9wICsgZHksIGhlaWdodDogY3MuaGVpZ2h0IC0gZHkgfTtcblx0XHR9LFxuXHRcdHM6IGZ1bmN0aW9uKCBldmVudCwgZHgsIGR5ICkge1xuXHRcdFx0cmV0dXJuIHsgaGVpZ2h0OiB0aGlzLm9yaWdpbmFsU2l6ZS5oZWlnaHQgKyBkeSB9O1xuXHRcdH0sXG5cdFx0c2U6IGZ1bmN0aW9uKCBldmVudCwgZHgsIGR5ICkge1xuXHRcdFx0cmV0dXJuICQuZXh0ZW5kKCB0aGlzLl9jaGFuZ2Uucy5hcHBseSggdGhpcywgYXJndW1lbnRzICksXG5cdFx0XHRcdHRoaXMuX2NoYW5nZS5lLmFwcGx5KCB0aGlzLCBbIGV2ZW50LCBkeCwgZHkgXSApICk7XG5cdFx0fSxcblx0XHRzdzogZnVuY3Rpb24oIGV2ZW50LCBkeCwgZHkgKSB7XG5cdFx0XHRyZXR1cm4gJC5leHRlbmQoIHRoaXMuX2NoYW5nZS5zLmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKSxcblx0XHRcdFx0dGhpcy5fY2hhbmdlLncuYXBwbHkoIHRoaXMsIFsgZXZlbnQsIGR4LCBkeSBdICkgKTtcblx0XHR9LFxuXHRcdG5lOiBmdW5jdGlvbiggZXZlbnQsIGR4LCBkeSApIHtcblx0XHRcdHJldHVybiAkLmV4dGVuZCggdGhpcy5fY2hhbmdlLm4uYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApLFxuXHRcdFx0XHR0aGlzLl9jaGFuZ2UuZS5hcHBseSggdGhpcywgWyBldmVudCwgZHgsIGR5IF0gKSApO1xuXHRcdH0sXG5cdFx0bnc6IGZ1bmN0aW9uKCBldmVudCwgZHgsIGR5ICkge1xuXHRcdFx0cmV0dXJuICQuZXh0ZW5kKCB0aGlzLl9jaGFuZ2Uubi5hcHBseSggdGhpcywgYXJndW1lbnRzICksXG5cdFx0XHRcdHRoaXMuX2NoYW5nZS53LmFwcGx5KCB0aGlzLCBbIGV2ZW50LCBkeCwgZHkgXSApICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9wcm9wYWdhdGU6IGZ1bmN0aW9uKCBuLCBldmVudCApIHtcblx0XHQkLnVpLnBsdWdpbi5jYWxsKCB0aGlzLCBuLCBbIGV2ZW50LCB0aGlzLnVpKCkgXSApO1xuXHRcdCggbiAhPT0gXCJyZXNpemVcIiAmJiB0aGlzLl90cmlnZ2VyKCBuLCBldmVudCwgdGhpcy51aSgpICkgKTtcblx0fSxcblxuXHRwbHVnaW5zOiB7fSxcblxuXHR1aTogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHtcblx0XHRcdG9yaWdpbmFsRWxlbWVudDogdGhpcy5vcmlnaW5hbEVsZW1lbnQsXG5cdFx0XHRlbGVtZW50OiB0aGlzLmVsZW1lbnQsXG5cdFx0XHRoZWxwZXI6IHRoaXMuaGVscGVyLFxuXHRcdFx0cG9zaXRpb246IHRoaXMucG9zaXRpb24sXG5cdFx0XHRzaXplOiB0aGlzLnNpemUsXG5cdFx0XHRvcmlnaW5hbFNpemU6IHRoaXMub3JpZ2luYWxTaXplLFxuXHRcdFx0b3JpZ2luYWxQb3NpdGlvbjogdGhpcy5vcmlnaW5hbFBvc2l0aW9uXG5cdFx0fTtcblx0fVxuXG59ICk7XG5cbi8qXG4gKiBSZXNpemFibGUgRXh0ZW5zaW9uc1xuICovXG5cbiQudWkucGx1Z2luLmFkZCggXCJyZXNpemFibGVcIiwgXCJhbmltYXRlXCIsIHtcblxuXHRzdG9wOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dmFyIHRoYXQgPSAkKCB0aGlzICkucmVzaXphYmxlKCBcImluc3RhbmNlXCIgKSxcblx0XHRcdG8gPSB0aGF0Lm9wdGlvbnMsXG5cdFx0XHRwciA9IHRoYXQuX3Byb3BvcnRpb25hbGx5UmVzaXplRWxlbWVudHMsXG5cdFx0XHRpc3RhID0gcHIubGVuZ3RoICYmICggL3RleHRhcmVhL2kgKS50ZXN0KCBwclsgMCBdLm5vZGVOYW1lICksXG5cdFx0XHRzb2Zmc2V0aCA9IGlzdGEgJiYgdGhhdC5faGFzU2Nyb2xsKCBwclsgMCBdLCBcImxlZnRcIiApID8gMCA6IHRoYXQuc2l6ZURpZmYuaGVpZ2h0LFxuXHRcdFx0c29mZnNldHcgPSBpc3RhID8gMCA6IHRoYXQuc2l6ZURpZmYud2lkdGgsXG5cdFx0XHRzdHlsZSA9IHtcblx0XHRcdFx0d2lkdGg6ICggdGhhdC5zaXplLndpZHRoIC0gc29mZnNldHcgKSxcblx0XHRcdFx0aGVpZ2h0OiAoIHRoYXQuc2l6ZS5oZWlnaHQgLSBzb2Zmc2V0aCApXG5cdFx0XHR9LFxuXHRcdFx0bGVmdCA9ICggcGFyc2VGbG9hdCggdGhhdC5lbGVtZW50LmNzcyggXCJsZWZ0XCIgKSApICtcblx0XHRcdFx0KCB0aGF0LnBvc2l0aW9uLmxlZnQgLSB0aGF0Lm9yaWdpbmFsUG9zaXRpb24ubGVmdCApICkgfHwgbnVsbCxcblx0XHRcdHRvcCA9ICggcGFyc2VGbG9hdCggdGhhdC5lbGVtZW50LmNzcyggXCJ0b3BcIiApICkgK1xuXHRcdFx0XHQoIHRoYXQucG9zaXRpb24udG9wIC0gdGhhdC5vcmlnaW5hbFBvc2l0aW9uLnRvcCApICkgfHwgbnVsbDtcblxuXHRcdHRoYXQuZWxlbWVudC5hbmltYXRlKFxuXHRcdFx0JC5leHRlbmQoIHN0eWxlLCB0b3AgJiYgbGVmdCA/IHsgdG9wOiB0b3AsIGxlZnQ6IGxlZnQgfSA6IHt9ICksIHtcblx0XHRcdFx0ZHVyYXRpb246IG8uYW5pbWF0ZUR1cmF0aW9uLFxuXHRcdFx0XHRlYXNpbmc6IG8uYW5pbWF0ZUVhc2luZyxcblx0XHRcdFx0c3RlcDogZnVuY3Rpb24oKSB7XG5cblx0XHRcdFx0XHR2YXIgZGF0YSA9IHtcblx0XHRcdFx0XHRcdHdpZHRoOiBwYXJzZUZsb2F0KCB0aGF0LmVsZW1lbnQuY3NzKCBcIndpZHRoXCIgKSApLFxuXHRcdFx0XHRcdFx0aGVpZ2h0OiBwYXJzZUZsb2F0KCB0aGF0LmVsZW1lbnQuY3NzKCBcImhlaWdodFwiICkgKSxcblx0XHRcdFx0XHRcdHRvcDogcGFyc2VGbG9hdCggdGhhdC5lbGVtZW50LmNzcyggXCJ0b3BcIiApICksXG5cdFx0XHRcdFx0XHRsZWZ0OiBwYXJzZUZsb2F0KCB0aGF0LmVsZW1lbnQuY3NzKCBcImxlZnRcIiApIClcblx0XHRcdFx0XHR9O1xuXG5cdFx0XHRcdFx0aWYgKCBwciAmJiBwci5sZW5ndGggKSB7XG5cdFx0XHRcdFx0XHQkKCBwclsgMCBdICkuY3NzKCB7IHdpZHRoOiBkYXRhLndpZHRoLCBoZWlnaHQ6IGRhdGEuaGVpZ2h0IH0gKTtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBQcm9wYWdhdGluZyByZXNpemUsIGFuZCB1cGRhdGluZyB2YWx1ZXMgZm9yIGVhY2ggYW5pbWF0aW9uIHN0ZXBcblx0XHRcdFx0XHR0aGF0Ll91cGRhdGVDYWNoZSggZGF0YSApO1xuXHRcdFx0XHRcdHRoYXQuX3Byb3BhZ2F0ZSggXCJyZXNpemVcIiwgZXZlbnQgKTtcblxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0KTtcblx0fVxuXG59ICk7XG5cbiQudWkucGx1Z2luLmFkZCggXCJyZXNpemFibGVcIiwgXCJjb250YWlubWVudFwiLCB7XG5cblx0c3RhcnQ6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBlbGVtZW50LCBwLCBjbywgY2gsIGN3LCB3aWR0aCwgaGVpZ2h0LFxuXHRcdFx0dGhhdCA9ICQoIHRoaXMgKS5yZXNpemFibGUoIFwiaW5zdGFuY2VcIiApLFxuXHRcdFx0byA9IHRoYXQub3B0aW9ucyxcblx0XHRcdGVsID0gdGhhdC5lbGVtZW50LFxuXHRcdFx0b2MgPSBvLmNvbnRhaW5tZW50LFxuXHRcdFx0Y2UgPSAoIG9jIGluc3RhbmNlb2YgJCApID9cblx0XHRcdFx0b2MuZ2V0KCAwICkgOlxuXHRcdFx0XHQoIC9wYXJlbnQvLnRlc3QoIG9jICkgKSA/IGVsLnBhcmVudCgpLmdldCggMCApIDogb2M7XG5cblx0XHRpZiAoICFjZSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHR0aGF0LmNvbnRhaW5lckVsZW1lbnQgPSAkKCBjZSApO1xuXG5cdFx0aWYgKCAvZG9jdW1lbnQvLnRlc3QoIG9jICkgfHwgb2MgPT09IGRvY3VtZW50ICkge1xuXHRcdFx0dGhhdC5jb250YWluZXJPZmZzZXQgPSB7XG5cdFx0XHRcdGxlZnQ6IDAsXG5cdFx0XHRcdHRvcDogMFxuXHRcdFx0fTtcblx0XHRcdHRoYXQuY29udGFpbmVyUG9zaXRpb24gPSB7XG5cdFx0XHRcdGxlZnQ6IDAsXG5cdFx0XHRcdHRvcDogMFxuXHRcdFx0fTtcblxuXHRcdFx0dGhhdC5wYXJlbnREYXRhID0ge1xuXHRcdFx0XHRlbGVtZW50OiAkKCBkb2N1bWVudCApLFxuXHRcdFx0XHRsZWZ0OiAwLFxuXHRcdFx0XHR0b3A6IDAsXG5cdFx0XHRcdHdpZHRoOiAkKCBkb2N1bWVudCApLndpZHRoKCksXG5cdFx0XHRcdGhlaWdodDogJCggZG9jdW1lbnQgKS5oZWlnaHQoKSB8fCBkb2N1bWVudC5ib2R5LnBhcmVudE5vZGUuc2Nyb2xsSGVpZ2h0XG5cdFx0XHR9O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRlbGVtZW50ID0gJCggY2UgKTtcblx0XHRcdHAgPSBbXTtcblx0XHRcdCQoIFsgXCJUb3BcIiwgXCJSaWdodFwiLCBcIkxlZnRcIiwgXCJCb3R0b21cIiBdICkuZWFjaCggZnVuY3Rpb24oIGksIG5hbWUgKSB7XG5cdFx0XHRcdHBbIGkgXSA9IHRoYXQuX251bSggZWxlbWVudC5jc3MoIFwicGFkZGluZ1wiICsgbmFtZSApICk7XG5cdFx0XHR9ICk7XG5cblx0XHRcdHRoYXQuY29udGFpbmVyT2Zmc2V0ID0gZWxlbWVudC5vZmZzZXQoKTtcblx0XHRcdHRoYXQuY29udGFpbmVyUG9zaXRpb24gPSBlbGVtZW50LnBvc2l0aW9uKCk7XG5cdFx0XHR0aGF0LmNvbnRhaW5lclNpemUgPSB7XG5cdFx0XHRcdGhlaWdodDogKCBlbGVtZW50LmlubmVySGVpZ2h0KCkgLSBwWyAzIF0gKSxcblx0XHRcdFx0d2lkdGg6ICggZWxlbWVudC5pbm5lcldpZHRoKCkgLSBwWyAxIF0gKVxuXHRcdFx0fTtcblxuXHRcdFx0Y28gPSB0aGF0LmNvbnRhaW5lck9mZnNldDtcblx0XHRcdGNoID0gdGhhdC5jb250YWluZXJTaXplLmhlaWdodDtcblx0XHRcdGN3ID0gdGhhdC5jb250YWluZXJTaXplLndpZHRoO1xuXHRcdFx0d2lkdGggPSAoIHRoYXQuX2hhc1Njcm9sbCAoIGNlLCBcImxlZnRcIiApID8gY2Uuc2Nyb2xsV2lkdGggOiBjdyApO1xuXHRcdFx0aGVpZ2h0ID0gKCB0aGF0Ll9oYXNTY3JvbGwgKCBjZSApID8gY2Uuc2Nyb2xsSGVpZ2h0IDogY2ggKSA7XG5cblx0XHRcdHRoYXQucGFyZW50RGF0YSA9IHtcblx0XHRcdFx0ZWxlbWVudDogY2UsXG5cdFx0XHRcdGxlZnQ6IGNvLmxlZnQsXG5cdFx0XHRcdHRvcDogY28udG9wLFxuXHRcdFx0XHR3aWR0aDogd2lkdGgsXG5cdFx0XHRcdGhlaWdodDogaGVpZ2h0XG5cdFx0XHR9O1xuXHRcdH1cblx0fSxcblxuXHRyZXNpemU6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgd29zZXQsIGhvc2V0LCBpc1BhcmVudCwgaXNPZmZzZXRSZWxhdGl2ZSxcblx0XHRcdHRoYXQgPSAkKCB0aGlzICkucmVzaXphYmxlKCBcImluc3RhbmNlXCIgKSxcblx0XHRcdG8gPSB0aGF0Lm9wdGlvbnMsXG5cdFx0XHRjbyA9IHRoYXQuY29udGFpbmVyT2Zmc2V0LFxuXHRcdFx0Y3AgPSB0aGF0LnBvc2l0aW9uLFxuXHRcdFx0cFJhdGlvID0gdGhhdC5fYXNwZWN0UmF0aW8gfHwgZXZlbnQuc2hpZnRLZXksXG5cdFx0XHRjb3AgPSB7XG5cdFx0XHRcdHRvcDogMCxcblx0XHRcdFx0bGVmdDogMFxuXHRcdFx0fSxcblx0XHRcdGNlID0gdGhhdC5jb250YWluZXJFbGVtZW50LFxuXHRcdFx0Y29udGludWVSZXNpemUgPSB0cnVlO1xuXG5cdFx0aWYgKCBjZVsgMCBdICE9PSBkb2N1bWVudCAmJiAoIC9zdGF0aWMvICkudGVzdCggY2UuY3NzKCBcInBvc2l0aW9uXCIgKSApICkge1xuXHRcdFx0Y29wID0gY287XG5cdFx0fVxuXG5cdFx0aWYgKCBjcC5sZWZ0IDwgKCB0aGF0Ll9oZWxwZXIgPyBjby5sZWZ0IDogMCApICkge1xuXHRcdFx0dGhhdC5zaXplLndpZHRoID0gdGhhdC5zaXplLndpZHRoICtcblx0XHRcdFx0KCB0aGF0Ll9oZWxwZXIgP1xuXHRcdFx0XHRcdCggdGhhdC5wb3NpdGlvbi5sZWZ0IC0gY28ubGVmdCApIDpcblx0XHRcdFx0XHQoIHRoYXQucG9zaXRpb24ubGVmdCAtIGNvcC5sZWZ0ICkgKTtcblxuXHRcdFx0aWYgKCBwUmF0aW8gKSB7XG5cdFx0XHRcdHRoYXQuc2l6ZS5oZWlnaHQgPSB0aGF0LnNpemUud2lkdGggLyB0aGF0LmFzcGVjdFJhdGlvO1xuXHRcdFx0XHRjb250aW51ZVJlc2l6ZSA9IGZhbHNlO1xuXHRcdFx0fVxuXHRcdFx0dGhhdC5wb3NpdGlvbi5sZWZ0ID0gby5oZWxwZXIgPyBjby5sZWZ0IDogMDtcblx0XHR9XG5cblx0XHRpZiAoIGNwLnRvcCA8ICggdGhhdC5faGVscGVyID8gY28udG9wIDogMCApICkge1xuXHRcdFx0dGhhdC5zaXplLmhlaWdodCA9IHRoYXQuc2l6ZS5oZWlnaHQgK1xuXHRcdFx0XHQoIHRoYXQuX2hlbHBlciA/XG5cdFx0XHRcdFx0KCB0aGF0LnBvc2l0aW9uLnRvcCAtIGNvLnRvcCApIDpcblx0XHRcdFx0XHR0aGF0LnBvc2l0aW9uLnRvcCApO1xuXG5cdFx0XHRpZiAoIHBSYXRpbyApIHtcblx0XHRcdFx0dGhhdC5zaXplLndpZHRoID0gdGhhdC5zaXplLmhlaWdodCAqIHRoYXQuYXNwZWN0UmF0aW87XG5cdFx0XHRcdGNvbnRpbnVlUmVzaXplID0gZmFsc2U7XG5cdFx0XHR9XG5cdFx0XHR0aGF0LnBvc2l0aW9uLnRvcCA9IHRoYXQuX2hlbHBlciA/IGNvLnRvcCA6IDA7XG5cdFx0fVxuXG5cdFx0aXNQYXJlbnQgPSB0aGF0LmNvbnRhaW5lckVsZW1lbnQuZ2V0KCAwICkgPT09IHRoYXQuZWxlbWVudC5wYXJlbnQoKS5nZXQoIDAgKTtcblx0XHRpc09mZnNldFJlbGF0aXZlID0gL3JlbGF0aXZlfGFic29sdXRlLy50ZXN0KCB0aGF0LmNvbnRhaW5lckVsZW1lbnQuY3NzKCBcInBvc2l0aW9uXCIgKSApO1xuXG5cdFx0aWYgKCBpc1BhcmVudCAmJiBpc09mZnNldFJlbGF0aXZlICkge1xuXHRcdFx0dGhhdC5vZmZzZXQubGVmdCA9IHRoYXQucGFyZW50RGF0YS5sZWZ0ICsgdGhhdC5wb3NpdGlvbi5sZWZ0O1xuXHRcdFx0dGhhdC5vZmZzZXQudG9wID0gdGhhdC5wYXJlbnREYXRhLnRvcCArIHRoYXQucG9zaXRpb24udG9wO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGF0Lm9mZnNldC5sZWZ0ID0gdGhhdC5lbGVtZW50Lm9mZnNldCgpLmxlZnQ7XG5cdFx0XHR0aGF0Lm9mZnNldC50b3AgPSB0aGF0LmVsZW1lbnQub2Zmc2V0KCkudG9wO1xuXHRcdH1cblxuXHRcdHdvc2V0ID0gTWF0aC5hYnMoIHRoYXQuc2l6ZURpZmYud2lkdGggK1xuXHRcdFx0KCB0aGF0Ll9oZWxwZXIgP1xuXHRcdFx0XHR0aGF0Lm9mZnNldC5sZWZ0IC0gY29wLmxlZnQgOlxuXHRcdFx0XHQoIHRoYXQub2Zmc2V0LmxlZnQgLSBjby5sZWZ0ICkgKSApO1xuXG5cdFx0aG9zZXQgPSBNYXRoLmFicyggdGhhdC5zaXplRGlmZi5oZWlnaHQgK1xuXHRcdFx0KCB0aGF0Ll9oZWxwZXIgP1xuXHRcdFx0XHR0aGF0Lm9mZnNldC50b3AgLSBjb3AudG9wIDpcblx0XHRcdFx0KCB0aGF0Lm9mZnNldC50b3AgLSBjby50b3AgKSApICk7XG5cblx0XHRpZiAoIHdvc2V0ICsgdGhhdC5zaXplLndpZHRoID49IHRoYXQucGFyZW50RGF0YS53aWR0aCApIHtcblx0XHRcdHRoYXQuc2l6ZS53aWR0aCA9IHRoYXQucGFyZW50RGF0YS53aWR0aCAtIHdvc2V0O1xuXHRcdFx0aWYgKCBwUmF0aW8gKSB7XG5cdFx0XHRcdHRoYXQuc2l6ZS5oZWlnaHQgPSB0aGF0LnNpemUud2lkdGggLyB0aGF0LmFzcGVjdFJhdGlvO1xuXHRcdFx0XHRjb250aW51ZVJlc2l6ZSA9IGZhbHNlO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmICggaG9zZXQgKyB0aGF0LnNpemUuaGVpZ2h0ID49IHRoYXQucGFyZW50RGF0YS5oZWlnaHQgKSB7XG5cdFx0XHR0aGF0LnNpemUuaGVpZ2h0ID0gdGhhdC5wYXJlbnREYXRhLmhlaWdodCAtIGhvc2V0O1xuXHRcdFx0aWYgKCBwUmF0aW8gKSB7XG5cdFx0XHRcdHRoYXQuc2l6ZS53aWR0aCA9IHRoYXQuc2l6ZS5oZWlnaHQgKiB0aGF0LmFzcGVjdFJhdGlvO1xuXHRcdFx0XHRjb250aW51ZVJlc2l6ZSA9IGZhbHNlO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmICggIWNvbnRpbnVlUmVzaXplICkge1xuXHRcdFx0dGhhdC5wb3NpdGlvbi5sZWZ0ID0gdGhhdC5wcmV2UG9zaXRpb24ubGVmdDtcblx0XHRcdHRoYXQucG9zaXRpb24udG9wID0gdGhhdC5wcmV2UG9zaXRpb24udG9wO1xuXHRcdFx0dGhhdC5zaXplLndpZHRoID0gdGhhdC5wcmV2U2l6ZS53aWR0aDtcblx0XHRcdHRoYXQuc2l6ZS5oZWlnaHQgPSB0aGF0LnByZXZTaXplLmhlaWdodDtcblx0XHR9XG5cdH0sXG5cblx0c3RvcDogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHRoYXQgPSAkKCB0aGlzICkucmVzaXphYmxlKCBcImluc3RhbmNlXCIgKSxcblx0XHRcdG8gPSB0aGF0Lm9wdGlvbnMsXG5cdFx0XHRjbyA9IHRoYXQuY29udGFpbmVyT2Zmc2V0LFxuXHRcdFx0Y29wID0gdGhhdC5jb250YWluZXJQb3NpdGlvbixcblx0XHRcdGNlID0gdGhhdC5jb250YWluZXJFbGVtZW50LFxuXHRcdFx0aGVscGVyID0gJCggdGhhdC5oZWxwZXIgKSxcblx0XHRcdGhvID0gaGVscGVyLm9mZnNldCgpLFxuXHRcdFx0dyA9IGhlbHBlci5vdXRlcldpZHRoKCkgLSB0aGF0LnNpemVEaWZmLndpZHRoLFxuXHRcdFx0aCA9IGhlbHBlci5vdXRlckhlaWdodCgpIC0gdGhhdC5zaXplRGlmZi5oZWlnaHQ7XG5cblx0XHRpZiAoIHRoYXQuX2hlbHBlciAmJiAhby5hbmltYXRlICYmICggL3JlbGF0aXZlLyApLnRlc3QoIGNlLmNzcyggXCJwb3NpdGlvblwiICkgKSApIHtcblx0XHRcdCQoIHRoaXMgKS5jc3MoIHtcblx0XHRcdFx0bGVmdDogaG8ubGVmdCAtIGNvcC5sZWZ0IC0gY28ubGVmdCxcblx0XHRcdFx0d2lkdGg6IHcsXG5cdFx0XHRcdGhlaWdodDogaFxuXHRcdFx0fSApO1xuXHRcdH1cblxuXHRcdGlmICggdGhhdC5faGVscGVyICYmICFvLmFuaW1hdGUgJiYgKCAvc3RhdGljLyApLnRlc3QoIGNlLmNzcyggXCJwb3NpdGlvblwiICkgKSApIHtcblx0XHRcdCQoIHRoaXMgKS5jc3MoIHtcblx0XHRcdFx0bGVmdDogaG8ubGVmdCAtIGNvcC5sZWZ0IC0gY28ubGVmdCxcblx0XHRcdFx0d2lkdGg6IHcsXG5cdFx0XHRcdGhlaWdodDogaFxuXHRcdFx0fSApO1xuXHRcdH1cblx0fVxufSApO1xuXG4kLnVpLnBsdWdpbi5hZGQoIFwicmVzaXphYmxlXCIsIFwiYWxzb1Jlc2l6ZVwiLCB7XG5cblx0c3RhcnQ6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciB0aGF0ID0gJCggdGhpcyApLnJlc2l6YWJsZSggXCJpbnN0YW5jZVwiICksXG5cdFx0XHRvID0gdGhhdC5vcHRpb25zO1xuXG5cdFx0JCggby5hbHNvUmVzaXplICkuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgZWwgPSAkKCB0aGlzICk7XG5cdFx0XHRlbC5kYXRhKCBcInVpLXJlc2l6YWJsZS1hbHNvcmVzaXplXCIsIHtcblx0XHRcdFx0d2lkdGg6IHBhcnNlRmxvYXQoIGVsLndpZHRoKCkgKSwgaGVpZ2h0OiBwYXJzZUZsb2F0KCBlbC5oZWlnaHQoKSApLFxuXHRcdFx0XHRsZWZ0OiBwYXJzZUZsb2F0KCBlbC5jc3MoIFwibGVmdFwiICkgKSwgdG9wOiBwYXJzZUZsb2F0KCBlbC5jc3MoIFwidG9wXCIgKSApXG5cdFx0XHR9ICk7XG5cdFx0fSApO1xuXHR9LFxuXG5cdHJlc2l6ZTogZnVuY3Rpb24oIGV2ZW50LCB1aSApIHtcblx0XHR2YXIgdGhhdCA9ICQoIHRoaXMgKS5yZXNpemFibGUoIFwiaW5zdGFuY2VcIiApLFxuXHRcdFx0byA9IHRoYXQub3B0aW9ucyxcblx0XHRcdG9zID0gdGhhdC5vcmlnaW5hbFNpemUsXG5cdFx0XHRvcCA9IHRoYXQub3JpZ2luYWxQb3NpdGlvbixcblx0XHRcdGRlbHRhID0ge1xuXHRcdFx0XHRoZWlnaHQ6ICggdGhhdC5zaXplLmhlaWdodCAtIG9zLmhlaWdodCApIHx8IDAsXG5cdFx0XHRcdHdpZHRoOiAoIHRoYXQuc2l6ZS53aWR0aCAtIG9zLndpZHRoICkgfHwgMCxcblx0XHRcdFx0dG9wOiAoIHRoYXQucG9zaXRpb24udG9wIC0gb3AudG9wICkgfHwgMCxcblx0XHRcdFx0bGVmdDogKCB0aGF0LnBvc2l0aW9uLmxlZnQgLSBvcC5sZWZ0ICkgfHwgMFxuXHRcdFx0fTtcblxuXHRcdFx0JCggby5hbHNvUmVzaXplICkuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHZhciBlbCA9ICQoIHRoaXMgKSwgc3RhcnQgPSAkKCB0aGlzICkuZGF0YSggXCJ1aS1yZXNpemFibGUtYWxzb3Jlc2l6ZVwiICksIHN0eWxlID0ge30sXG5cdFx0XHRcdFx0Y3NzID0gZWwucGFyZW50cyggdWkub3JpZ2luYWxFbGVtZW50WyAwIF0gKS5sZW5ndGggP1xuXHRcdFx0XHRcdFx0XHRbIFwid2lkdGhcIiwgXCJoZWlnaHRcIiBdIDpcblx0XHRcdFx0XHRcdFx0WyBcIndpZHRoXCIsIFwiaGVpZ2h0XCIsIFwidG9wXCIsIFwibGVmdFwiIF07XG5cblx0XHRcdFx0JC5lYWNoKCBjc3MsIGZ1bmN0aW9uKCBpLCBwcm9wICkge1xuXHRcdFx0XHRcdHZhciBzdW0gPSAoIHN0YXJ0WyBwcm9wIF0gfHwgMCApICsgKCBkZWx0YVsgcHJvcCBdIHx8IDAgKTtcblx0XHRcdFx0XHRpZiAoIHN1bSAmJiBzdW0gPj0gMCApIHtcblx0XHRcdFx0XHRcdHN0eWxlWyBwcm9wIF0gPSBzdW0gfHwgbnVsbDtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gKTtcblxuXHRcdFx0XHRlbC5jc3MoIHN0eWxlICk7XG5cdFx0XHR9ICk7XG5cdH0sXG5cblx0c3RvcDogZnVuY3Rpb24oKSB7XG5cdFx0JCggdGhpcyApLnJlbW92ZURhdGEoIFwidWktcmVzaXphYmxlLWFsc29yZXNpemVcIiApO1xuXHR9XG59ICk7XG5cbiQudWkucGx1Z2luLmFkZCggXCJyZXNpemFibGVcIiwgXCJnaG9zdFwiLCB7XG5cblx0c3RhcnQ6IGZ1bmN0aW9uKCkge1xuXG5cdFx0dmFyIHRoYXQgPSAkKCB0aGlzICkucmVzaXphYmxlKCBcImluc3RhbmNlXCIgKSwgY3MgPSB0aGF0LnNpemU7XG5cblx0XHR0aGF0Lmdob3N0ID0gdGhhdC5vcmlnaW5hbEVsZW1lbnQuY2xvbmUoKTtcblx0XHR0aGF0Lmdob3N0LmNzcygge1xuXHRcdFx0b3BhY2l0eTogMC4yNSxcblx0XHRcdGRpc3BsYXk6IFwiYmxvY2tcIixcblx0XHRcdHBvc2l0aW9uOiBcInJlbGF0aXZlXCIsXG5cdFx0XHRoZWlnaHQ6IGNzLmhlaWdodCxcblx0XHRcdHdpZHRoOiBjcy53aWR0aCxcblx0XHRcdG1hcmdpbjogMCxcblx0XHRcdGxlZnQ6IDAsXG5cdFx0XHR0b3A6IDBcblx0XHR9ICk7XG5cblx0XHR0aGF0Ll9hZGRDbGFzcyggdGhhdC5naG9zdCwgXCJ1aS1yZXNpemFibGUtZ2hvc3RcIiApO1xuXG5cdFx0Ly8gREVQUkVDQVRFRFxuXHRcdC8vIFRPRE86IHJlbW92ZSBhZnRlciAxLjEyXG5cdFx0aWYgKCAkLnVpQmFja0NvbXBhdCAhPT0gZmFsc2UgJiYgdHlwZW9mIHRoYXQub3B0aW9ucy5naG9zdCA9PT0gXCJzdHJpbmdcIiApIHtcblxuXHRcdFx0Ly8gR2hvc3Qgb3B0aW9uXG5cdFx0XHR0aGF0Lmdob3N0LmFkZENsYXNzKCB0aGlzLm9wdGlvbnMuZ2hvc3QgKTtcblx0XHR9XG5cblx0XHR0aGF0Lmdob3N0LmFwcGVuZFRvKCB0aGF0LmhlbHBlciApO1xuXG5cdH0sXG5cblx0cmVzaXplOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgdGhhdCA9ICQoIHRoaXMgKS5yZXNpemFibGUoIFwiaW5zdGFuY2VcIiApO1xuXHRcdGlmICggdGhhdC5naG9zdCApIHtcblx0XHRcdHRoYXQuZ2hvc3QuY3NzKCB7XG5cdFx0XHRcdHBvc2l0aW9uOiBcInJlbGF0aXZlXCIsXG5cdFx0XHRcdGhlaWdodDogdGhhdC5zaXplLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IHRoYXQuc2l6ZS53aWR0aFxuXHRcdFx0fSApO1xuXHRcdH1cblx0fSxcblxuXHRzdG9wOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgdGhhdCA9ICQoIHRoaXMgKS5yZXNpemFibGUoIFwiaW5zdGFuY2VcIiApO1xuXHRcdGlmICggdGhhdC5naG9zdCAmJiB0aGF0LmhlbHBlciApIHtcblx0XHRcdHRoYXQuaGVscGVyLmdldCggMCApLnJlbW92ZUNoaWxkKCB0aGF0Lmdob3N0LmdldCggMCApICk7XG5cdFx0fVxuXHR9XG5cbn0gKTtcblxuJC51aS5wbHVnaW4uYWRkKCBcInJlc2l6YWJsZVwiLCBcImdyaWRcIiwge1xuXG5cdHJlc2l6ZTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIG91dGVyRGltZW5zaW9ucyxcblx0XHRcdHRoYXQgPSAkKCB0aGlzICkucmVzaXphYmxlKCBcImluc3RhbmNlXCIgKSxcblx0XHRcdG8gPSB0aGF0Lm9wdGlvbnMsXG5cdFx0XHRjcyA9IHRoYXQuc2l6ZSxcblx0XHRcdG9zID0gdGhhdC5vcmlnaW5hbFNpemUsXG5cdFx0XHRvcCA9IHRoYXQub3JpZ2luYWxQb3NpdGlvbixcblx0XHRcdGEgPSB0aGF0LmF4aXMsXG5cdFx0XHRncmlkID0gdHlwZW9mIG8uZ3JpZCA9PT0gXCJudW1iZXJcIiA/IFsgby5ncmlkLCBvLmdyaWQgXSA6IG8uZ3JpZCxcblx0XHRcdGdyaWRYID0gKCBncmlkWyAwIF0gfHwgMSApLFxuXHRcdFx0Z3JpZFkgPSAoIGdyaWRbIDEgXSB8fCAxICksXG5cdFx0XHRveCA9IE1hdGgucm91bmQoICggY3Mud2lkdGggLSBvcy53aWR0aCApIC8gZ3JpZFggKSAqIGdyaWRYLFxuXHRcdFx0b3kgPSBNYXRoLnJvdW5kKCAoIGNzLmhlaWdodCAtIG9zLmhlaWdodCApIC8gZ3JpZFkgKSAqIGdyaWRZLFxuXHRcdFx0bmV3V2lkdGggPSBvcy53aWR0aCArIG94LFxuXHRcdFx0bmV3SGVpZ2h0ID0gb3MuaGVpZ2h0ICsgb3ksXG5cdFx0XHRpc01heFdpZHRoID0gby5tYXhXaWR0aCAmJiAoIG8ubWF4V2lkdGggPCBuZXdXaWR0aCApLFxuXHRcdFx0aXNNYXhIZWlnaHQgPSBvLm1heEhlaWdodCAmJiAoIG8ubWF4SGVpZ2h0IDwgbmV3SGVpZ2h0ICksXG5cdFx0XHRpc01pbldpZHRoID0gby5taW5XaWR0aCAmJiAoIG8ubWluV2lkdGggPiBuZXdXaWR0aCApLFxuXHRcdFx0aXNNaW5IZWlnaHQgPSBvLm1pbkhlaWdodCAmJiAoIG8ubWluSGVpZ2h0ID4gbmV3SGVpZ2h0ICk7XG5cblx0XHRvLmdyaWQgPSBncmlkO1xuXG5cdFx0aWYgKCBpc01pbldpZHRoICkge1xuXHRcdFx0bmV3V2lkdGggKz0gZ3JpZFg7XG5cdFx0fVxuXHRcdGlmICggaXNNaW5IZWlnaHQgKSB7XG5cdFx0XHRuZXdIZWlnaHQgKz0gZ3JpZFk7XG5cdFx0fVxuXHRcdGlmICggaXNNYXhXaWR0aCApIHtcblx0XHRcdG5ld1dpZHRoIC09IGdyaWRYO1xuXHRcdH1cblx0XHRpZiAoIGlzTWF4SGVpZ2h0ICkge1xuXHRcdFx0bmV3SGVpZ2h0IC09IGdyaWRZO1xuXHRcdH1cblxuXHRcdGlmICggL14oc2V8c3xlKSQvLnRlc3QoIGEgKSApIHtcblx0XHRcdHRoYXQuc2l6ZS53aWR0aCA9IG5ld1dpZHRoO1xuXHRcdFx0dGhhdC5zaXplLmhlaWdodCA9IG5ld0hlaWdodDtcblx0XHR9IGVsc2UgaWYgKCAvXihuZSkkLy50ZXN0KCBhICkgKSB7XG5cdFx0XHR0aGF0LnNpemUud2lkdGggPSBuZXdXaWR0aDtcblx0XHRcdHRoYXQuc2l6ZS5oZWlnaHQgPSBuZXdIZWlnaHQ7XG5cdFx0XHR0aGF0LnBvc2l0aW9uLnRvcCA9IG9wLnRvcCAtIG95O1xuXHRcdH0gZWxzZSBpZiAoIC9eKHN3KSQvLnRlc3QoIGEgKSApIHtcblx0XHRcdHRoYXQuc2l6ZS53aWR0aCA9IG5ld1dpZHRoO1xuXHRcdFx0dGhhdC5zaXplLmhlaWdodCA9IG5ld0hlaWdodDtcblx0XHRcdHRoYXQucG9zaXRpb24ubGVmdCA9IG9wLmxlZnQgLSBveDtcblx0XHR9IGVsc2Uge1xuXHRcdFx0aWYgKCBuZXdIZWlnaHQgLSBncmlkWSA8PSAwIHx8IG5ld1dpZHRoIC0gZ3JpZFggPD0gMCApIHtcblx0XHRcdFx0b3V0ZXJEaW1lbnNpb25zID0gdGhhdC5fZ2V0UGFkZGluZ1BsdXNCb3JkZXJEaW1lbnNpb25zKCB0aGlzICk7XG5cdFx0XHR9XG5cblx0XHRcdGlmICggbmV3SGVpZ2h0IC0gZ3JpZFkgPiAwICkge1xuXHRcdFx0XHR0aGF0LnNpemUuaGVpZ2h0ID0gbmV3SGVpZ2h0O1xuXHRcdFx0XHR0aGF0LnBvc2l0aW9uLnRvcCA9IG9wLnRvcCAtIG95O1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0bmV3SGVpZ2h0ID0gZ3JpZFkgLSBvdXRlckRpbWVuc2lvbnMuaGVpZ2h0O1xuXHRcdFx0XHR0aGF0LnNpemUuaGVpZ2h0ID0gbmV3SGVpZ2h0O1xuXHRcdFx0XHR0aGF0LnBvc2l0aW9uLnRvcCA9IG9wLnRvcCArIG9zLmhlaWdodCAtIG5ld0hlaWdodDtcblx0XHRcdH1cblx0XHRcdGlmICggbmV3V2lkdGggLSBncmlkWCA+IDAgKSB7XG5cdFx0XHRcdHRoYXQuc2l6ZS53aWR0aCA9IG5ld1dpZHRoO1xuXHRcdFx0XHR0aGF0LnBvc2l0aW9uLmxlZnQgPSBvcC5sZWZ0IC0gb3g7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRuZXdXaWR0aCA9IGdyaWRYIC0gb3V0ZXJEaW1lbnNpb25zLndpZHRoO1xuXHRcdFx0XHR0aGF0LnNpemUud2lkdGggPSBuZXdXaWR0aDtcblx0XHRcdFx0dGhhdC5wb3NpdGlvbi5sZWZ0ID0gb3AubGVmdCArIG9zLndpZHRoIC0gbmV3V2lkdGg7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cbn0gKTtcblxudmFyIHdpZGdldHNSZXNpemFibGUgPSAkLnVpLnJlc2l6YWJsZTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBEaWFsb2cgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IERpYWxvZ1xuLy8+Pmdyb3VwOiBXaWRnZXRzXG4vLz4+ZGVzY3JpcHRpb246IERpc3BsYXlzIGN1c3RvbWl6YWJsZSBkaWFsb2cgd2luZG93cy5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9kaWFsb2cvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vZGlhbG9nL1xuLy8+PmNzcy5zdHJ1Y3R1cmU6IC4uLy4uL3RoZW1lcy9iYXNlL2NvcmUuY3NzXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2UvZGlhbG9nLmNzc1xuLy8+PmNzcy50aGVtZTogLi4vLi4vdGhlbWVzL2Jhc2UvdGhlbWUuY3NzXG5cblxuXG4kLndpZGdldCggXCJ1aS5kaWFsb2dcIiwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHRvcHRpb25zOiB7XG5cdFx0YXBwZW5kVG86IFwiYm9keVwiLFxuXHRcdGF1dG9PcGVuOiB0cnVlLFxuXHRcdGJ1dHRvbnM6IFtdLFxuXHRcdGNsYXNzZXM6IHtcblx0XHRcdFwidWktZGlhbG9nXCI6IFwidWktY29ybmVyLWFsbFwiLFxuXHRcdFx0XCJ1aS1kaWFsb2ctdGl0bGViYXJcIjogXCJ1aS1jb3JuZXItYWxsXCJcblx0XHR9LFxuXHRcdGNsb3NlT25Fc2NhcGU6IHRydWUsXG5cdFx0Y2xvc2VUZXh0OiBcIkNsb3NlXCIsXG5cdFx0ZHJhZ2dhYmxlOiB0cnVlLFxuXHRcdGhpZGU6IG51bGwsXG5cdFx0aGVpZ2h0OiBcImF1dG9cIixcblx0XHRtYXhIZWlnaHQ6IG51bGwsXG5cdFx0bWF4V2lkdGg6IG51bGwsXG5cdFx0bWluSGVpZ2h0OiAxNTAsXG5cdFx0bWluV2lkdGg6IDE1MCxcblx0XHRtb2RhbDogZmFsc2UsXG5cdFx0cG9zaXRpb246IHtcblx0XHRcdG15OiBcImNlbnRlclwiLFxuXHRcdFx0YXQ6IFwiY2VudGVyXCIsXG5cdFx0XHRvZjogd2luZG93LFxuXHRcdFx0Y29sbGlzaW9uOiBcImZpdFwiLFxuXG5cdFx0XHQvLyBFbnN1cmUgdGhlIHRpdGxlYmFyIGlzIGFsd2F5cyB2aXNpYmxlXG5cdFx0XHR1c2luZzogZnVuY3Rpb24oIHBvcyApIHtcblx0XHRcdFx0dmFyIHRvcE9mZnNldCA9ICQoIHRoaXMgKS5jc3MoIHBvcyApLm9mZnNldCgpLnRvcDtcblx0XHRcdFx0aWYgKCB0b3BPZmZzZXQgPCAwICkge1xuXHRcdFx0XHRcdCQoIHRoaXMgKS5jc3MoIFwidG9wXCIsIHBvcy50b3AgLSB0b3BPZmZzZXQgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0sXG5cdFx0cmVzaXphYmxlOiB0cnVlLFxuXHRcdHNob3c6IG51bGwsXG5cdFx0dGl0bGU6IG51bGwsXG5cdFx0d2lkdGg6IDMwMCxcblxuXHRcdC8vIENhbGxiYWNrc1xuXHRcdGJlZm9yZUNsb3NlOiBudWxsLFxuXHRcdGNsb3NlOiBudWxsLFxuXHRcdGRyYWc6IG51bGwsXG5cdFx0ZHJhZ1N0YXJ0OiBudWxsLFxuXHRcdGRyYWdTdG9wOiBudWxsLFxuXHRcdGZvY3VzOiBudWxsLFxuXHRcdG9wZW46IG51bGwsXG5cdFx0cmVzaXplOiBudWxsLFxuXHRcdHJlc2l6ZVN0YXJ0OiBudWxsLFxuXHRcdHJlc2l6ZVN0b3A6IG51bGxcblx0fSxcblxuXHRzaXplUmVsYXRlZE9wdGlvbnM6IHtcblx0XHRidXR0b25zOiB0cnVlLFxuXHRcdGhlaWdodDogdHJ1ZSxcblx0XHRtYXhIZWlnaHQ6IHRydWUsXG5cdFx0bWF4V2lkdGg6IHRydWUsXG5cdFx0bWluSGVpZ2h0OiB0cnVlLFxuXHRcdG1pbldpZHRoOiB0cnVlLFxuXHRcdHdpZHRoOiB0cnVlXG5cdH0sXG5cblx0cmVzaXphYmxlUmVsYXRlZE9wdGlvbnM6IHtcblx0XHRtYXhIZWlnaHQ6IHRydWUsXG5cdFx0bWF4V2lkdGg6IHRydWUsXG5cdFx0bWluSGVpZ2h0OiB0cnVlLFxuXHRcdG1pbldpZHRoOiB0cnVlXG5cdH0sXG5cblx0X2NyZWF0ZTogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5vcmlnaW5hbENzcyA9IHtcblx0XHRcdGRpc3BsYXk6IHRoaXMuZWxlbWVudFsgMCBdLnN0eWxlLmRpc3BsYXksXG5cdFx0XHR3aWR0aDogdGhpcy5lbGVtZW50WyAwIF0uc3R5bGUud2lkdGgsXG5cdFx0XHRtaW5IZWlnaHQ6IHRoaXMuZWxlbWVudFsgMCBdLnN0eWxlLm1pbkhlaWdodCxcblx0XHRcdG1heEhlaWdodDogdGhpcy5lbGVtZW50WyAwIF0uc3R5bGUubWF4SGVpZ2h0LFxuXHRcdFx0aGVpZ2h0OiB0aGlzLmVsZW1lbnRbIDAgXS5zdHlsZS5oZWlnaHRcblx0XHR9O1xuXHRcdHRoaXMub3JpZ2luYWxQb3NpdGlvbiA9IHtcblx0XHRcdHBhcmVudDogdGhpcy5lbGVtZW50LnBhcmVudCgpLFxuXHRcdFx0aW5kZXg6IHRoaXMuZWxlbWVudC5wYXJlbnQoKS5jaGlsZHJlbigpLmluZGV4KCB0aGlzLmVsZW1lbnQgKVxuXHRcdH07XG5cdFx0dGhpcy5vcmlnaW5hbFRpdGxlID0gdGhpcy5lbGVtZW50LmF0dHIoIFwidGl0bGVcIiApO1xuXHRcdGlmICggdGhpcy5vcHRpb25zLnRpdGxlID09IG51bGwgJiYgdGhpcy5vcmlnaW5hbFRpdGxlICE9IG51bGwgKSB7XG5cdFx0XHR0aGlzLm9wdGlvbnMudGl0bGUgPSB0aGlzLm9yaWdpbmFsVGl0bGU7XG5cdFx0fVxuXG5cdFx0Ly8gRGlhbG9ncyBjYW4ndCBiZSBkaXNhYmxlZFxuXHRcdGlmICggdGhpcy5vcHRpb25zLmRpc2FibGVkICkge1xuXHRcdFx0dGhpcy5vcHRpb25zLmRpc2FibGVkID0gZmFsc2U7XG5cdFx0fVxuXG5cdFx0dGhpcy5fY3JlYXRlV3JhcHBlcigpO1xuXG5cdFx0dGhpcy5lbGVtZW50XG5cdFx0XHQuc2hvdygpXG5cdFx0XHQucmVtb3ZlQXR0ciggXCJ0aXRsZVwiIClcblx0XHRcdC5hcHBlbmRUbyggdGhpcy51aURpYWxvZyApO1xuXG5cdFx0dGhpcy5fYWRkQ2xhc3MoIFwidWktZGlhbG9nLWNvbnRlbnRcIiwgXCJ1aS13aWRnZXQtY29udGVudFwiICk7XG5cblx0XHR0aGlzLl9jcmVhdGVUaXRsZWJhcigpO1xuXHRcdHRoaXMuX2NyZWF0ZUJ1dHRvblBhbmUoKTtcblxuXHRcdGlmICggdGhpcy5vcHRpb25zLmRyYWdnYWJsZSAmJiAkLmZuLmRyYWdnYWJsZSApIHtcblx0XHRcdHRoaXMuX21ha2VEcmFnZ2FibGUoKTtcblx0XHR9XG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMucmVzaXphYmxlICYmICQuZm4ucmVzaXphYmxlICkge1xuXHRcdFx0dGhpcy5fbWFrZVJlc2l6YWJsZSgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX2lzT3BlbiA9IGZhbHNlO1xuXG5cdFx0dGhpcy5fdHJhY2tGb2N1cygpO1xuXHR9LFxuXG5cdF9pbml0OiBmdW5jdGlvbigpIHtcblx0XHRpZiAoIHRoaXMub3B0aW9ucy5hdXRvT3BlbiApIHtcblx0XHRcdHRoaXMub3BlbigpO1xuXHRcdH1cblx0fSxcblxuXHRfYXBwZW5kVG86IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBlbGVtZW50ID0gdGhpcy5vcHRpb25zLmFwcGVuZFRvO1xuXHRcdGlmICggZWxlbWVudCAmJiAoIGVsZW1lbnQuanF1ZXJ5IHx8IGVsZW1lbnQubm9kZVR5cGUgKSApIHtcblx0XHRcdHJldHVybiAkKCBlbGVtZW50ICk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzLmRvY3VtZW50LmZpbmQoIGVsZW1lbnQgfHwgXCJib2R5XCIgKS5lcSggMCApO1xuXHR9LFxuXG5cdF9kZXN0cm95OiBmdW5jdGlvbigpIHtcblx0XHR2YXIgbmV4dCxcblx0XHRcdG9yaWdpbmFsUG9zaXRpb24gPSB0aGlzLm9yaWdpbmFsUG9zaXRpb247XG5cblx0XHR0aGlzLl91bnRyYWNrSW5zdGFuY2UoKTtcblx0XHR0aGlzLl9kZXN0cm95T3ZlcmxheSgpO1xuXG5cdFx0dGhpcy5lbGVtZW50XG5cdFx0XHQucmVtb3ZlVW5pcXVlSWQoKVxuXHRcdFx0LmNzcyggdGhpcy5vcmlnaW5hbENzcyApXG5cblx0XHRcdC8vIFdpdGhvdXQgZGV0YWNoaW5nIGZpcnN0LCB0aGUgZm9sbG93aW5nIGJlY29tZXMgcmVhbGx5IHNsb3dcblx0XHRcdC5kZXRhY2goKTtcblxuXHRcdHRoaXMudWlEaWFsb2cucmVtb3ZlKCk7XG5cblx0XHRpZiAoIHRoaXMub3JpZ2luYWxUaXRsZSApIHtcblx0XHRcdHRoaXMuZWxlbWVudC5hdHRyKCBcInRpdGxlXCIsIHRoaXMub3JpZ2luYWxUaXRsZSApO1xuXHRcdH1cblxuXHRcdG5leHQgPSBvcmlnaW5hbFBvc2l0aW9uLnBhcmVudC5jaGlsZHJlbigpLmVxKCBvcmlnaW5hbFBvc2l0aW9uLmluZGV4ICk7XG5cblx0XHQvLyBEb24ndCB0cnkgdG8gcGxhY2UgdGhlIGRpYWxvZyBuZXh0IHRvIGl0c2VsZiAoIzg2MTMpXG5cdFx0aWYgKCBuZXh0Lmxlbmd0aCAmJiBuZXh0WyAwIF0gIT09IHRoaXMuZWxlbWVudFsgMCBdICkge1xuXHRcdFx0bmV4dC5iZWZvcmUoIHRoaXMuZWxlbWVudCApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRvcmlnaW5hbFBvc2l0aW9uLnBhcmVudC5hcHBlbmQoIHRoaXMuZWxlbWVudCApO1xuXHRcdH1cblx0fSxcblxuXHR3aWRnZXQ6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLnVpRGlhbG9nO1xuXHR9LFxuXG5cdGRpc2FibGU6ICQubm9vcCxcblx0ZW5hYmxlOiAkLm5vb3AsXG5cblx0Y2xvc2U6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgdGhhdCA9IHRoaXM7XG5cblx0XHRpZiAoICF0aGlzLl9pc09wZW4gfHwgdGhpcy5fdHJpZ2dlciggXCJiZWZvcmVDbG9zZVwiLCBldmVudCApID09PSBmYWxzZSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHR0aGlzLl9pc09wZW4gPSBmYWxzZTtcblx0XHR0aGlzLl9mb2N1c2VkRWxlbWVudCA9IG51bGw7XG5cdFx0dGhpcy5fZGVzdHJveU92ZXJsYXkoKTtcblx0XHR0aGlzLl91bnRyYWNrSW5zdGFuY2UoKTtcblxuXHRcdGlmICggIXRoaXMub3BlbmVyLmZpbHRlciggXCI6Zm9jdXNhYmxlXCIgKS50cmlnZ2VyKCBcImZvY3VzXCIgKS5sZW5ndGggKSB7XG5cblx0XHRcdC8vIEhpZGluZyBhIGZvY3VzZWQgZWxlbWVudCBkb2Vzbid0IHRyaWdnZXIgYmx1ciBpbiBXZWJLaXRcblx0XHRcdC8vIHNvIGluIGNhc2Ugd2UgaGF2ZSBub3RoaW5nIHRvIGZvY3VzIG9uLCBleHBsaWNpdGx5IGJsdXIgdGhlIGFjdGl2ZSBlbGVtZW50XG5cdFx0XHQvLyBodHRwczovL2J1Z3Mud2Via2l0Lm9yZy9zaG93X2J1Zy5jZ2k/aWQ9NDcxODJcblx0XHRcdCQudWkuc2FmZUJsdXIoICQudWkuc2FmZUFjdGl2ZUVsZW1lbnQoIHRoaXMuZG9jdW1lbnRbIDAgXSApICk7XG5cdFx0fVxuXG5cdFx0dGhpcy5faGlkZSggdGhpcy51aURpYWxvZywgdGhpcy5vcHRpb25zLmhpZGUsIGZ1bmN0aW9uKCkge1xuXHRcdFx0dGhhdC5fdHJpZ2dlciggXCJjbG9zZVwiLCBldmVudCApO1xuXHRcdH0gKTtcblx0fSxcblxuXHRpc09wZW46IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLl9pc09wZW47XG5cdH0sXG5cblx0bW92ZVRvVG9wOiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLl9tb3ZlVG9Ub3AoKTtcblx0fSxcblxuXHRfbW92ZVRvVG9wOiBmdW5jdGlvbiggZXZlbnQsIHNpbGVudCApIHtcblx0XHR2YXIgbW92ZWQgPSBmYWxzZSxcblx0XHRcdHpJbmRpY2VzID0gdGhpcy51aURpYWxvZy5zaWJsaW5ncyggXCIudWktZnJvbnQ6dmlzaWJsZVwiICkubWFwKCBmdW5jdGlvbigpIHtcblx0XHRcdFx0cmV0dXJuICskKCB0aGlzICkuY3NzKCBcInotaW5kZXhcIiApO1xuXHRcdFx0fSApLmdldCgpLFxuXHRcdFx0ekluZGV4TWF4ID0gTWF0aC5tYXguYXBwbHkoIG51bGwsIHpJbmRpY2VzICk7XG5cblx0XHRpZiAoIHpJbmRleE1heCA+PSArdGhpcy51aURpYWxvZy5jc3MoIFwiei1pbmRleFwiICkgKSB7XG5cdFx0XHR0aGlzLnVpRGlhbG9nLmNzcyggXCJ6LWluZGV4XCIsIHpJbmRleE1heCArIDEgKTtcblx0XHRcdG1vdmVkID0gdHJ1ZTtcblx0XHR9XG5cblx0XHRpZiAoIG1vdmVkICYmICFzaWxlbnQgKSB7XG5cdFx0XHR0aGlzLl90cmlnZ2VyKCBcImZvY3VzXCIsIGV2ZW50ICk7XG5cdFx0fVxuXHRcdHJldHVybiBtb3ZlZDtcblx0fSxcblxuXHRvcGVuOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgdGhhdCA9IHRoaXM7XG5cdFx0aWYgKCB0aGlzLl9pc09wZW4gKSB7XG5cdFx0XHRpZiAoIHRoaXMuX21vdmVUb1RvcCgpICkge1xuXHRcdFx0XHR0aGlzLl9mb2N1c1RhYmJhYmxlKCk7XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0dGhpcy5faXNPcGVuID0gdHJ1ZTtcblx0XHR0aGlzLm9wZW5lciA9ICQoICQudWkuc2FmZUFjdGl2ZUVsZW1lbnQoIHRoaXMuZG9jdW1lbnRbIDAgXSApICk7XG5cblx0XHR0aGlzLl9zaXplKCk7XG5cdFx0dGhpcy5fcG9zaXRpb24oKTtcblx0XHR0aGlzLl9jcmVhdGVPdmVybGF5KCk7XG5cdFx0dGhpcy5fbW92ZVRvVG9wKCBudWxsLCB0cnVlICk7XG5cblx0XHQvLyBFbnN1cmUgdGhlIG92ZXJsYXkgaXMgbW92ZWQgdG8gdGhlIHRvcCB3aXRoIHRoZSBkaWFsb2csIGJ1dCBvbmx5IHdoZW5cblx0XHQvLyBvcGVuaW5nLiBUaGUgb3ZlcmxheSBzaG91bGRuJ3QgbW92ZSBhZnRlciB0aGUgZGlhbG9nIGlzIG9wZW4gc28gdGhhdFxuXHRcdC8vIG1vZGVsZXNzIGRpYWxvZ3Mgb3BlbmVkIGFmdGVyIHRoZSBtb2RhbCBkaWFsb2cgc3RhY2sgcHJvcGVybHkuXG5cdFx0aWYgKCB0aGlzLm92ZXJsYXkgKSB7XG5cdFx0XHR0aGlzLm92ZXJsYXkuY3NzKCBcInotaW5kZXhcIiwgdGhpcy51aURpYWxvZy5jc3MoIFwiei1pbmRleFwiICkgLSAxICk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fc2hvdyggdGhpcy51aURpYWxvZywgdGhpcy5vcHRpb25zLnNob3csIGZ1bmN0aW9uKCkge1xuXHRcdFx0dGhhdC5fZm9jdXNUYWJiYWJsZSgpO1xuXHRcdFx0dGhhdC5fdHJpZ2dlciggXCJmb2N1c1wiICk7XG5cdFx0fSApO1xuXG5cdFx0Ly8gVHJhY2sgdGhlIGRpYWxvZyBpbW1lZGlhdGVseSB1cG9uIG9wZW5lbmluZyBpbiBjYXNlIGEgZm9jdXMgZXZlbnRcblx0XHQvLyBzb21laG93IG9jY3VycyBvdXRzaWRlIG9mIHRoZSBkaWFsb2cgYmVmb3JlIGFuIGVsZW1lbnQgaW5zaWRlIHRoZVxuXHRcdC8vIGRpYWxvZyBpcyBmb2N1c2VkICgjMTAxNTIpXG5cdFx0dGhpcy5fbWFrZUZvY3VzVGFyZ2V0KCk7XG5cblx0XHR0aGlzLl90cmlnZ2VyKCBcIm9wZW5cIiApO1xuXHR9LFxuXG5cdF9mb2N1c1RhYmJhYmxlOiBmdW5jdGlvbigpIHtcblxuXHRcdC8vIFNldCBmb2N1cyB0byB0aGUgZmlyc3QgbWF0Y2g6XG5cdFx0Ly8gMS4gQW4gZWxlbWVudCB0aGF0IHdhcyBmb2N1c2VkIHByZXZpb3VzbHlcblx0XHQvLyAyLiBGaXJzdCBlbGVtZW50IGluc2lkZSB0aGUgZGlhbG9nIG1hdGNoaW5nIFthdXRvZm9jdXNdXG5cdFx0Ly8gMy4gVGFiYmFibGUgZWxlbWVudCBpbnNpZGUgdGhlIGNvbnRlbnQgZWxlbWVudFxuXHRcdC8vIDQuIFRhYmJhYmxlIGVsZW1lbnQgaW5zaWRlIHRoZSBidXR0b25wYW5lXG5cdFx0Ly8gNS4gVGhlIGNsb3NlIGJ1dHRvblxuXHRcdC8vIDYuIFRoZSBkaWFsb2cgaXRzZWxmXG5cdFx0dmFyIGhhc0ZvY3VzID0gdGhpcy5fZm9jdXNlZEVsZW1lbnQ7XG5cdFx0aWYgKCAhaGFzRm9jdXMgKSB7XG5cdFx0XHRoYXNGb2N1cyA9IHRoaXMuZWxlbWVudC5maW5kKCBcIlthdXRvZm9jdXNdXCIgKTtcblx0XHR9XG5cdFx0aWYgKCAhaGFzRm9jdXMubGVuZ3RoICkge1xuXHRcdFx0aGFzRm9jdXMgPSB0aGlzLmVsZW1lbnQuZmluZCggXCI6dGFiYmFibGVcIiApO1xuXHRcdH1cblx0XHRpZiAoICFoYXNGb2N1cy5sZW5ndGggKSB7XG5cdFx0XHRoYXNGb2N1cyA9IHRoaXMudWlEaWFsb2dCdXR0b25QYW5lLmZpbmQoIFwiOnRhYmJhYmxlXCIgKTtcblx0XHR9XG5cdFx0aWYgKCAhaGFzRm9jdXMubGVuZ3RoICkge1xuXHRcdFx0aGFzRm9jdXMgPSB0aGlzLnVpRGlhbG9nVGl0bGViYXJDbG9zZS5maWx0ZXIoIFwiOnRhYmJhYmxlXCIgKTtcblx0XHR9XG5cdFx0aWYgKCAhaGFzRm9jdXMubGVuZ3RoICkge1xuXHRcdFx0aGFzRm9jdXMgPSB0aGlzLnVpRGlhbG9nO1xuXHRcdH1cblx0XHRoYXNGb2N1cy5lcSggMCApLnRyaWdnZXIoIFwiZm9jdXNcIiApO1xuXHR9LFxuXG5cdF9rZWVwRm9jdXM6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRmdW5jdGlvbiBjaGVja0ZvY3VzKCkge1xuXHRcdFx0dmFyIGFjdGl2ZUVsZW1lbnQgPSAkLnVpLnNhZmVBY3RpdmVFbGVtZW50KCB0aGlzLmRvY3VtZW50WyAwIF0gKSxcblx0XHRcdFx0aXNBY3RpdmUgPSB0aGlzLnVpRGlhbG9nWyAwIF0gPT09IGFjdGl2ZUVsZW1lbnQgfHxcblx0XHRcdFx0XHQkLmNvbnRhaW5zKCB0aGlzLnVpRGlhbG9nWyAwIF0sIGFjdGl2ZUVsZW1lbnQgKTtcblx0XHRcdGlmICggIWlzQWN0aXZlICkge1xuXHRcdFx0XHR0aGlzLl9mb2N1c1RhYmJhYmxlKCk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0Y2hlY2tGb2N1cy5jYWxsKCB0aGlzICk7XG5cblx0XHQvLyBzdXBwb3J0OiBJRVxuXHRcdC8vIElFIDw9IDggZG9lc24ndCBwcmV2ZW50IG1vdmluZyBmb2N1cyBldmVuIHdpdGggZXZlbnQucHJldmVudERlZmF1bHQoKVxuXHRcdC8vIHNvIHdlIGNoZWNrIGFnYWluIGxhdGVyXG5cdFx0dGhpcy5fZGVsYXkoIGNoZWNrRm9jdXMgKTtcblx0fSxcblxuXHRfY3JlYXRlV3JhcHBlcjogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy51aURpYWxvZyA9ICQoIFwiPGRpdj5cIiApXG5cdFx0XHQuaGlkZSgpXG5cdFx0XHQuYXR0cigge1xuXG5cdFx0XHRcdC8vIFNldHRpbmcgdGFiSW5kZXggbWFrZXMgdGhlIGRpdiBmb2N1c2FibGVcblx0XHRcdFx0dGFiSW5kZXg6IC0xLFxuXHRcdFx0XHRyb2xlOiBcImRpYWxvZ1wiXG5cdFx0XHR9IClcblx0XHRcdC5hcHBlbmRUbyggdGhpcy5fYXBwZW5kVG8oKSApO1xuXG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMudWlEaWFsb2csIFwidWktZGlhbG9nXCIsIFwidWktd2lkZ2V0IHVpLXdpZGdldC1jb250ZW50IHVpLWZyb250XCIgKTtcblx0XHR0aGlzLl9vbiggdGhpcy51aURpYWxvZywge1xuXHRcdFx0a2V5ZG93bjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0XHRpZiAoIHRoaXMub3B0aW9ucy5jbG9zZU9uRXNjYXBlICYmICFldmVudC5pc0RlZmF1bHRQcmV2ZW50ZWQoKSAmJiBldmVudC5rZXlDb2RlICYmXG5cdFx0XHRcdFx0XHRldmVudC5rZXlDb2RlID09PSAkLnVpLmtleUNvZGUuRVNDQVBFICkge1xuXHRcdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdFx0dGhpcy5jbG9zZSggZXZlbnQgKTtcblx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBQcmV2ZW50IHRhYmJpbmcgb3V0IG9mIGRpYWxvZ3Ncblx0XHRcdFx0aWYgKCBldmVudC5rZXlDb2RlICE9PSAkLnVpLmtleUNvZGUuVEFCIHx8IGV2ZW50LmlzRGVmYXVsdFByZXZlbnRlZCgpICkge1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXHRcdFx0XHR2YXIgdGFiYmFibGVzID0gdGhpcy51aURpYWxvZy5maW5kKCBcIjp0YWJiYWJsZVwiICksXG5cdFx0XHRcdFx0Zmlyc3QgPSB0YWJiYWJsZXMuZmlsdGVyKCBcIjpmaXJzdFwiICksXG5cdFx0XHRcdFx0bGFzdCA9IHRhYmJhYmxlcy5maWx0ZXIoIFwiOmxhc3RcIiApO1xuXG5cdFx0XHRcdGlmICggKCBldmVudC50YXJnZXQgPT09IGxhc3RbIDAgXSB8fCBldmVudC50YXJnZXQgPT09IHRoaXMudWlEaWFsb2dbIDAgXSApICYmXG5cdFx0XHRcdFx0XHQhZXZlbnQuc2hpZnRLZXkgKSB7XG5cdFx0XHRcdFx0dGhpcy5fZGVsYXkoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdFx0Zmlyc3QudHJpZ2dlciggXCJmb2N1c1wiICk7XG5cdFx0XHRcdFx0fSApO1xuXHRcdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdH0gZWxzZSBpZiAoICggZXZlbnQudGFyZ2V0ID09PSBmaXJzdFsgMCBdIHx8XG5cdFx0XHRcdFx0XHRldmVudC50YXJnZXQgPT09IHRoaXMudWlEaWFsb2dbIDAgXSApICYmIGV2ZW50LnNoaWZ0S2V5ICkge1xuXHRcdFx0XHRcdHRoaXMuX2RlbGF5KCBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHRcdGxhc3QudHJpZ2dlciggXCJmb2N1c1wiICk7XG5cdFx0XHRcdFx0fSApO1xuXHRcdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdH1cblx0XHRcdH0sXG5cdFx0XHRtb3VzZWRvd246IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0aWYgKCB0aGlzLl9tb3ZlVG9Ub3AoIGV2ZW50ICkgKSB7XG5cdFx0XHRcdFx0dGhpcy5fZm9jdXNUYWJiYWJsZSgpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdFx0Ly8gV2UgYXNzdW1lIHRoYXQgYW55IGV4aXN0aW5nIGFyaWEtZGVzY3JpYmVkYnkgYXR0cmlidXRlIG1lYW5zXG5cdFx0Ly8gdGhhdCB0aGUgZGlhbG9nIGNvbnRlbnQgaXMgbWFya2VkIHVwIHByb3Blcmx5XG5cdFx0Ly8gb3RoZXJ3aXNlIHdlIGJydXRlIGZvcmNlIHRoZSBjb250ZW50IGFzIHRoZSBkZXNjcmlwdGlvblxuXHRcdGlmICggIXRoaXMuZWxlbWVudC5maW5kKCBcIlthcmlhLWRlc2NyaWJlZGJ5XVwiICkubGVuZ3RoICkge1xuXHRcdFx0dGhpcy51aURpYWxvZy5hdHRyKCB7XG5cdFx0XHRcdFwiYXJpYS1kZXNjcmliZWRieVwiOiB0aGlzLmVsZW1lbnQudW5pcXVlSWQoKS5hdHRyKCBcImlkXCIgKVxuXHRcdFx0fSApO1xuXHRcdH1cblx0fSxcblxuXHRfY3JlYXRlVGl0bGViYXI6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciB1aURpYWxvZ1RpdGxlO1xuXG5cdFx0dGhpcy51aURpYWxvZ1RpdGxlYmFyID0gJCggXCI8ZGl2PlwiICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMudWlEaWFsb2dUaXRsZWJhcixcblx0XHRcdFwidWktZGlhbG9nLXRpdGxlYmFyXCIsIFwidWktd2lkZ2V0LWhlYWRlciB1aS1oZWxwZXItY2xlYXJmaXhcIiApO1xuXHRcdHRoaXMuX29uKCB0aGlzLnVpRGlhbG9nVGl0bGViYXIsIHtcblx0XHRcdG1vdXNlZG93bjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXG5cdFx0XHRcdC8vIERvbid0IHByZXZlbnQgY2xpY2sgb24gY2xvc2UgYnV0dG9uICgjODgzOClcblx0XHRcdFx0Ly8gRm9jdXNpbmcgYSBkaWFsb2cgdGhhdCBpcyBwYXJ0aWFsbHkgc2Nyb2xsZWQgb3V0IG9mIHZpZXdcblx0XHRcdFx0Ly8gY2F1c2VzIHRoZSBicm93c2VyIHRvIHNjcm9sbCBpdCBpbnRvIHZpZXcsIHByZXZlbnRpbmcgdGhlIGNsaWNrIGV2ZW50XG5cdFx0XHRcdGlmICggISQoIGV2ZW50LnRhcmdldCApLmNsb3Nlc3QoIFwiLnVpLWRpYWxvZy10aXRsZWJhci1jbG9zZVwiICkgKSB7XG5cblx0XHRcdFx0XHQvLyBEaWFsb2cgaXNuJ3QgZ2V0dGluZyBmb2N1cyB3aGVuIGRyYWdnaW5nICgjODA2Mylcblx0XHRcdFx0XHR0aGlzLnVpRGlhbG9nLnRyaWdnZXIoIFwiZm9jdXNcIiApO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdFx0Ly8gU3VwcG9ydDogSUVcblx0XHQvLyBVc2UgdHlwZT1cImJ1dHRvblwiIHRvIHByZXZlbnQgZW50ZXIga2V5cHJlc3NlcyBpbiB0ZXh0Ym94ZXMgZnJvbSBjbG9zaW5nIHRoZVxuXHRcdC8vIGRpYWxvZyBpbiBJRSAoIzkzMTIpXG5cdFx0dGhpcy51aURpYWxvZ1RpdGxlYmFyQ2xvc2UgPSAkKCBcIjxidXR0b24gdHlwZT0nYnV0dG9uJz48L2J1dHRvbj5cIiApXG5cdFx0XHQuYnV0dG9uKCB7XG5cdFx0XHRcdGxhYmVsOiAkKCBcIjxhPlwiICkudGV4dCggdGhpcy5vcHRpb25zLmNsb3NlVGV4dCApLmh0bWwoKSxcblx0XHRcdFx0aWNvbjogXCJ1aS1pY29uLWNsb3NldGhpY2tcIixcblx0XHRcdFx0c2hvd0xhYmVsOiBmYWxzZVxuXHRcdFx0fSApXG5cdFx0XHQuYXBwZW5kVG8oIHRoaXMudWlEaWFsb2dUaXRsZWJhciApO1xuXG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMudWlEaWFsb2dUaXRsZWJhckNsb3NlLCBcInVpLWRpYWxvZy10aXRsZWJhci1jbG9zZVwiICk7XG5cdFx0dGhpcy5fb24oIHRoaXMudWlEaWFsb2dUaXRsZWJhckNsb3NlLCB7XG5cdFx0XHRjbGljazogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0XHR0aGlzLmNsb3NlKCBldmVudCApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHRcdHVpRGlhbG9nVGl0bGUgPSAkKCBcIjxzcGFuPlwiICkudW5pcXVlSWQoKS5wcmVwZW5kVG8oIHRoaXMudWlEaWFsb2dUaXRsZWJhciApO1xuXHRcdHRoaXMuX2FkZENsYXNzKCB1aURpYWxvZ1RpdGxlLCBcInVpLWRpYWxvZy10aXRsZVwiICk7XG5cdFx0dGhpcy5fdGl0bGUoIHVpRGlhbG9nVGl0bGUgKTtcblxuXHRcdHRoaXMudWlEaWFsb2dUaXRsZWJhci5wcmVwZW5kVG8oIHRoaXMudWlEaWFsb2cgKTtcblxuXHRcdHRoaXMudWlEaWFsb2cuYXR0cigge1xuXHRcdFx0XCJhcmlhLWxhYmVsbGVkYnlcIjogdWlEaWFsb2dUaXRsZS5hdHRyKCBcImlkXCIgKVxuXHRcdH0gKTtcblx0fSxcblxuXHRfdGl0bGU6IGZ1bmN0aW9uKCB0aXRsZSApIHtcblx0XHRpZiAoIHRoaXMub3B0aW9ucy50aXRsZSApIHtcblx0XHRcdHRpdGxlLnRleHQoIHRoaXMub3B0aW9ucy50aXRsZSApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aXRsZS5odG1sKCBcIiYjMTYwO1wiICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9jcmVhdGVCdXR0b25QYW5lOiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLnVpRGlhbG9nQnV0dG9uUGFuZSA9ICQoIFwiPGRpdj5cIiApO1xuXHRcdHRoaXMuX2FkZENsYXNzKCB0aGlzLnVpRGlhbG9nQnV0dG9uUGFuZSwgXCJ1aS1kaWFsb2ctYnV0dG9ucGFuZVwiLFxuXHRcdFx0XCJ1aS13aWRnZXQtY29udGVudCB1aS1oZWxwZXItY2xlYXJmaXhcIiApO1xuXG5cdFx0dGhpcy51aUJ1dHRvblNldCA9ICQoIFwiPGRpdj5cIiApXG5cdFx0XHQuYXBwZW5kVG8oIHRoaXMudWlEaWFsb2dCdXR0b25QYW5lICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMudWlCdXR0b25TZXQsIFwidWktZGlhbG9nLWJ1dHRvbnNldFwiICk7XG5cblx0XHR0aGlzLl9jcmVhdGVCdXR0b25zKCk7XG5cdH0sXG5cblx0X2NyZWF0ZUJ1dHRvbnM6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciB0aGF0ID0gdGhpcyxcblx0XHRcdGJ1dHRvbnMgPSB0aGlzLm9wdGlvbnMuYnV0dG9ucztcblxuXHRcdC8vIElmIHdlIGFscmVhZHkgaGF2ZSBhIGJ1dHRvbiBwYW5lLCByZW1vdmUgaXRcblx0XHR0aGlzLnVpRGlhbG9nQnV0dG9uUGFuZS5yZW1vdmUoKTtcblx0XHR0aGlzLnVpQnV0dG9uU2V0LmVtcHR5KCk7XG5cblx0XHRpZiAoICQuaXNFbXB0eU9iamVjdCggYnV0dG9ucyApIHx8ICggJC5pc0FycmF5KCBidXR0b25zICkgJiYgIWJ1dHRvbnMubGVuZ3RoICkgKSB7XG5cdFx0XHR0aGlzLl9yZW1vdmVDbGFzcyggdGhpcy51aURpYWxvZywgXCJ1aS1kaWFsb2ctYnV0dG9uc1wiICk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0JC5lYWNoKCBidXR0b25zLCBmdW5jdGlvbiggbmFtZSwgcHJvcHMgKSB7XG5cdFx0XHR2YXIgY2xpY2ssIGJ1dHRvbk9wdGlvbnM7XG5cdFx0XHRwcm9wcyA9ICQuaXNGdW5jdGlvbiggcHJvcHMgKSA/XG5cdFx0XHRcdHsgY2xpY2s6IHByb3BzLCB0ZXh0OiBuYW1lIH0gOlxuXHRcdFx0XHRwcm9wcztcblxuXHRcdFx0Ly8gRGVmYXVsdCB0byBhIG5vbi1zdWJtaXR0aW5nIGJ1dHRvblxuXHRcdFx0cHJvcHMgPSAkLmV4dGVuZCggeyB0eXBlOiBcImJ1dHRvblwiIH0sIHByb3BzICk7XG5cblx0XHRcdC8vIENoYW5nZSB0aGUgY29udGV4dCBmb3IgdGhlIGNsaWNrIGNhbGxiYWNrIHRvIGJlIHRoZSBtYWluIGVsZW1lbnRcblx0XHRcdGNsaWNrID0gcHJvcHMuY2xpY2s7XG5cdFx0XHRidXR0b25PcHRpb25zID0ge1xuXHRcdFx0XHRpY29uOiBwcm9wcy5pY29uLFxuXHRcdFx0XHRpY29uUG9zaXRpb246IHByb3BzLmljb25Qb3NpdGlvbixcblx0XHRcdFx0c2hvd0xhYmVsOiBwcm9wcy5zaG93TGFiZWwsXG5cblx0XHRcdFx0Ly8gRGVwcmVjYXRlZCBvcHRpb25zXG5cdFx0XHRcdGljb25zOiBwcm9wcy5pY29ucyxcblx0XHRcdFx0dGV4dDogcHJvcHMudGV4dFxuXHRcdFx0fTtcblxuXHRcdFx0ZGVsZXRlIHByb3BzLmNsaWNrO1xuXHRcdFx0ZGVsZXRlIHByb3BzLmljb247XG5cdFx0XHRkZWxldGUgcHJvcHMuaWNvblBvc2l0aW9uO1xuXHRcdFx0ZGVsZXRlIHByb3BzLnNob3dMYWJlbDtcblxuXHRcdFx0Ly8gRGVwcmVjYXRlZCBvcHRpb25zXG5cdFx0XHRkZWxldGUgcHJvcHMuaWNvbnM7XG5cdFx0XHRpZiAoIHR5cGVvZiBwcm9wcy50ZXh0ID09PSBcImJvb2xlYW5cIiApIHtcblx0XHRcdFx0ZGVsZXRlIHByb3BzLnRleHQ7XG5cdFx0XHR9XG5cblx0XHRcdCQoIFwiPGJ1dHRvbj48L2J1dHRvbj5cIiwgcHJvcHMgKVxuXHRcdFx0XHQuYnV0dG9uKCBidXR0b25PcHRpb25zIClcblx0XHRcdFx0LmFwcGVuZFRvKCB0aGF0LnVpQnV0dG9uU2V0IClcblx0XHRcdFx0Lm9uKCBcImNsaWNrXCIsIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdGNsaWNrLmFwcGx5KCB0aGF0LmVsZW1lbnRbIDAgXSwgYXJndW1lbnRzICk7XG5cdFx0XHRcdH0gKTtcblx0XHR9ICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMudWlEaWFsb2csIFwidWktZGlhbG9nLWJ1dHRvbnNcIiApO1xuXHRcdHRoaXMudWlEaWFsb2dCdXR0b25QYW5lLmFwcGVuZFRvKCB0aGlzLnVpRGlhbG9nICk7XG5cdH0sXG5cblx0X21ha2VEcmFnZ2FibGU6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciB0aGF0ID0gdGhpcyxcblx0XHRcdG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XG5cblx0XHRmdW5jdGlvbiBmaWx0ZXJlZFVpKCB1aSApIHtcblx0XHRcdHJldHVybiB7XG5cdFx0XHRcdHBvc2l0aW9uOiB1aS5wb3NpdGlvbixcblx0XHRcdFx0b2Zmc2V0OiB1aS5vZmZzZXRcblx0XHRcdH07XG5cdFx0fVxuXG5cdFx0dGhpcy51aURpYWxvZy5kcmFnZ2FibGUoIHtcblx0XHRcdGNhbmNlbDogXCIudWktZGlhbG9nLWNvbnRlbnQsIC51aS1kaWFsb2ctdGl0bGViYXItY2xvc2VcIixcblx0XHRcdGhhbmRsZTogXCIudWktZGlhbG9nLXRpdGxlYmFyXCIsXG5cdFx0XHRjb250YWlubWVudDogXCJkb2N1bWVudFwiLFxuXHRcdFx0c3RhcnQ6IGZ1bmN0aW9uKCBldmVudCwgdWkgKSB7XG5cdFx0XHRcdHRoYXQuX2FkZENsYXNzKCAkKCB0aGlzICksIFwidWktZGlhbG9nLWRyYWdnaW5nXCIgKTtcblx0XHRcdFx0dGhhdC5fYmxvY2tGcmFtZXMoKTtcblx0XHRcdFx0dGhhdC5fdHJpZ2dlciggXCJkcmFnU3RhcnRcIiwgZXZlbnQsIGZpbHRlcmVkVWkoIHVpICkgKTtcblx0XHRcdH0sXG5cdFx0XHRkcmFnOiBmdW5jdGlvbiggZXZlbnQsIHVpICkge1xuXHRcdFx0XHR0aGF0Ll90cmlnZ2VyKCBcImRyYWdcIiwgZXZlbnQsIGZpbHRlcmVkVWkoIHVpICkgKTtcblx0XHRcdH0sXG5cdFx0XHRzdG9wOiBmdW5jdGlvbiggZXZlbnQsIHVpICkge1xuXHRcdFx0XHR2YXIgbGVmdCA9IHVpLm9mZnNldC5sZWZ0IC0gdGhhdC5kb2N1bWVudC5zY3JvbGxMZWZ0KCksXG5cdFx0XHRcdFx0dG9wID0gdWkub2Zmc2V0LnRvcCAtIHRoYXQuZG9jdW1lbnQuc2Nyb2xsVG9wKCk7XG5cblx0XHRcdFx0b3B0aW9ucy5wb3NpdGlvbiA9IHtcblx0XHRcdFx0XHRteTogXCJsZWZ0IHRvcFwiLFxuXHRcdFx0XHRcdGF0OiBcImxlZnRcIiArICggbGVmdCA+PSAwID8gXCIrXCIgOiBcIlwiICkgKyBsZWZ0ICsgXCIgXCIgK1xuXHRcdFx0XHRcdFx0XCJ0b3BcIiArICggdG9wID49IDAgPyBcIitcIiA6IFwiXCIgKSArIHRvcCxcblx0XHRcdFx0XHRvZjogdGhhdC53aW5kb3dcblx0XHRcdFx0fTtcblx0XHRcdFx0dGhhdC5fcmVtb3ZlQ2xhc3MoICQoIHRoaXMgKSwgXCJ1aS1kaWFsb2ctZHJhZ2dpbmdcIiApO1xuXHRcdFx0XHR0aGF0Ll91bmJsb2NrRnJhbWVzKCk7XG5cdFx0XHRcdHRoYXQuX3RyaWdnZXIoIFwiZHJhZ1N0b3BcIiwgZXZlbnQsIGZpbHRlcmVkVWkoIHVpICkgKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdH0sXG5cblx0X21ha2VSZXNpemFibGU6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciB0aGF0ID0gdGhpcyxcblx0XHRcdG9wdGlvbnMgPSB0aGlzLm9wdGlvbnMsXG5cdFx0XHRoYW5kbGVzID0gb3B0aW9ucy5yZXNpemFibGUsXG5cblx0XHRcdC8vIC51aS1yZXNpemFibGUgaGFzIHBvc2l0aW9uOiByZWxhdGl2ZSBkZWZpbmVkIGluIHRoZSBzdHlsZXNoZWV0XG5cdFx0XHQvLyBidXQgZGlhbG9ncyBoYXZlIHRvIHVzZSBhYnNvbHV0ZSBvciBmaXhlZCBwb3NpdGlvbmluZ1xuXHRcdFx0cG9zaXRpb24gPSB0aGlzLnVpRGlhbG9nLmNzcyggXCJwb3NpdGlvblwiICksXG5cdFx0XHRyZXNpemVIYW5kbGVzID0gdHlwZW9mIGhhbmRsZXMgPT09IFwic3RyaW5nXCIgP1xuXHRcdFx0XHRoYW5kbGVzIDpcblx0XHRcdFx0XCJuLGUscyx3LHNlLHN3LG5lLG53XCI7XG5cblx0XHRmdW5jdGlvbiBmaWx0ZXJlZFVpKCB1aSApIHtcblx0XHRcdHJldHVybiB7XG5cdFx0XHRcdG9yaWdpbmFsUG9zaXRpb246IHVpLm9yaWdpbmFsUG9zaXRpb24sXG5cdFx0XHRcdG9yaWdpbmFsU2l6ZTogdWkub3JpZ2luYWxTaXplLFxuXHRcdFx0XHRwb3NpdGlvbjogdWkucG9zaXRpb24sXG5cdFx0XHRcdHNpemU6IHVpLnNpemVcblx0XHRcdH07XG5cdFx0fVxuXG5cdFx0dGhpcy51aURpYWxvZy5yZXNpemFibGUoIHtcblx0XHRcdGNhbmNlbDogXCIudWktZGlhbG9nLWNvbnRlbnRcIixcblx0XHRcdGNvbnRhaW5tZW50OiBcImRvY3VtZW50XCIsXG5cdFx0XHRhbHNvUmVzaXplOiB0aGlzLmVsZW1lbnQsXG5cdFx0XHRtYXhXaWR0aDogb3B0aW9ucy5tYXhXaWR0aCxcblx0XHRcdG1heEhlaWdodDogb3B0aW9ucy5tYXhIZWlnaHQsXG5cdFx0XHRtaW5XaWR0aDogb3B0aW9ucy5taW5XaWR0aCxcblx0XHRcdG1pbkhlaWdodDogdGhpcy5fbWluSGVpZ2h0KCksXG5cdFx0XHRoYW5kbGVzOiByZXNpemVIYW5kbGVzLFxuXHRcdFx0c3RhcnQ6IGZ1bmN0aW9uKCBldmVudCwgdWkgKSB7XG5cdFx0XHRcdHRoYXQuX2FkZENsYXNzKCAkKCB0aGlzICksIFwidWktZGlhbG9nLXJlc2l6aW5nXCIgKTtcblx0XHRcdFx0dGhhdC5fYmxvY2tGcmFtZXMoKTtcblx0XHRcdFx0dGhhdC5fdHJpZ2dlciggXCJyZXNpemVTdGFydFwiLCBldmVudCwgZmlsdGVyZWRVaSggdWkgKSApO1xuXHRcdFx0fSxcblx0XHRcdHJlc2l6ZTogZnVuY3Rpb24oIGV2ZW50LCB1aSApIHtcblx0XHRcdFx0dGhhdC5fdHJpZ2dlciggXCJyZXNpemVcIiwgZXZlbnQsIGZpbHRlcmVkVWkoIHVpICkgKTtcblx0XHRcdH0sXG5cdFx0XHRzdG9wOiBmdW5jdGlvbiggZXZlbnQsIHVpICkge1xuXHRcdFx0XHR2YXIgb2Zmc2V0ID0gdGhhdC51aURpYWxvZy5vZmZzZXQoKSxcblx0XHRcdFx0XHRsZWZ0ID0gb2Zmc2V0LmxlZnQgLSB0aGF0LmRvY3VtZW50LnNjcm9sbExlZnQoKSxcblx0XHRcdFx0XHR0b3AgPSBvZmZzZXQudG9wIC0gdGhhdC5kb2N1bWVudC5zY3JvbGxUb3AoKTtcblxuXHRcdFx0XHRvcHRpb25zLmhlaWdodCA9IHRoYXQudWlEaWFsb2cuaGVpZ2h0KCk7XG5cdFx0XHRcdG9wdGlvbnMud2lkdGggPSB0aGF0LnVpRGlhbG9nLndpZHRoKCk7XG5cdFx0XHRcdG9wdGlvbnMucG9zaXRpb24gPSB7XG5cdFx0XHRcdFx0bXk6IFwibGVmdCB0b3BcIixcblx0XHRcdFx0XHRhdDogXCJsZWZ0XCIgKyAoIGxlZnQgPj0gMCA/IFwiK1wiIDogXCJcIiApICsgbGVmdCArIFwiIFwiICtcblx0XHRcdFx0XHRcdFwidG9wXCIgKyAoIHRvcCA+PSAwID8gXCIrXCIgOiBcIlwiICkgKyB0b3AsXG5cdFx0XHRcdFx0b2Y6IHRoYXQud2luZG93XG5cdFx0XHRcdH07XG5cdFx0XHRcdHRoYXQuX3JlbW92ZUNsYXNzKCAkKCB0aGlzICksIFwidWktZGlhbG9nLXJlc2l6aW5nXCIgKTtcblx0XHRcdFx0dGhhdC5fdW5ibG9ja0ZyYW1lcygpO1xuXHRcdFx0XHR0aGF0Ll90cmlnZ2VyKCBcInJlc2l6ZVN0b3BcIiwgZXZlbnQsIGZpbHRlcmVkVWkoIHVpICkgKTtcblx0XHRcdH1cblx0XHR9IClcblx0XHRcdC5jc3MoIFwicG9zaXRpb25cIiwgcG9zaXRpb24gKTtcblx0fSxcblxuXHRfdHJhY2tGb2N1czogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fb24oIHRoaXMud2lkZ2V0KCksIHtcblx0XHRcdGZvY3VzaW46IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0dGhpcy5fbWFrZUZvY3VzVGFyZ2V0KCk7XG5cdFx0XHRcdHRoaXMuX2ZvY3VzZWRFbGVtZW50ID0gJCggZXZlbnQudGFyZ2V0ICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXHR9LFxuXG5cdF9tYWtlRm9jdXNUYXJnZXQ6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuX3VudHJhY2tJbnN0YW5jZSgpO1xuXHRcdHRoaXMuX3RyYWNraW5nSW5zdGFuY2VzKCkudW5zaGlmdCggdGhpcyApO1xuXHR9LFxuXG5cdF91bnRyYWNrSW5zdGFuY2U6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBpbnN0YW5jZXMgPSB0aGlzLl90cmFja2luZ0luc3RhbmNlcygpLFxuXHRcdFx0ZXhpc3RzID0gJC5pbkFycmF5KCB0aGlzLCBpbnN0YW5jZXMgKTtcblx0XHRpZiAoIGV4aXN0cyAhPT0gLTEgKSB7XG5cdFx0XHRpbnN0YW5jZXMuc3BsaWNlKCBleGlzdHMsIDEgKTtcblx0XHR9XG5cdH0sXG5cblx0X3RyYWNraW5nSW5zdGFuY2VzOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgaW5zdGFuY2VzID0gdGhpcy5kb2N1bWVudC5kYXRhKCBcInVpLWRpYWxvZy1pbnN0YW5jZXNcIiApO1xuXHRcdGlmICggIWluc3RhbmNlcyApIHtcblx0XHRcdGluc3RhbmNlcyA9IFtdO1xuXHRcdFx0dGhpcy5kb2N1bWVudC5kYXRhKCBcInVpLWRpYWxvZy1pbnN0YW5jZXNcIiwgaW5zdGFuY2VzICk7XG5cdFx0fVxuXHRcdHJldHVybiBpbnN0YW5jZXM7XG5cdH0sXG5cblx0X21pbkhlaWdodDogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XG5cblx0XHRyZXR1cm4gb3B0aW9ucy5oZWlnaHQgPT09IFwiYXV0b1wiID9cblx0XHRcdG9wdGlvbnMubWluSGVpZ2h0IDpcblx0XHRcdE1hdGgubWluKCBvcHRpb25zLm1pbkhlaWdodCwgb3B0aW9ucy5oZWlnaHQgKTtcblx0fSxcblxuXHRfcG9zaXRpb246IGZ1bmN0aW9uKCkge1xuXG5cdFx0Ly8gTmVlZCB0byBzaG93IHRoZSBkaWFsb2cgdG8gZ2V0IHRoZSBhY3R1YWwgb2Zmc2V0IGluIHRoZSBwb3NpdGlvbiBwbHVnaW5cblx0XHR2YXIgaXNWaXNpYmxlID0gdGhpcy51aURpYWxvZy5pcyggXCI6dmlzaWJsZVwiICk7XG5cdFx0aWYgKCAhaXNWaXNpYmxlICkge1xuXHRcdFx0dGhpcy51aURpYWxvZy5zaG93KCk7XG5cdFx0fVxuXHRcdHRoaXMudWlEaWFsb2cucG9zaXRpb24oIHRoaXMub3B0aW9ucy5wb3NpdGlvbiApO1xuXHRcdGlmICggIWlzVmlzaWJsZSApIHtcblx0XHRcdHRoaXMudWlEaWFsb2cuaGlkZSgpO1xuXHRcdH1cblx0fSxcblxuXHRfc2V0T3B0aW9uczogZnVuY3Rpb24oIG9wdGlvbnMgKSB7XG5cdFx0dmFyIHRoYXQgPSB0aGlzLFxuXHRcdFx0cmVzaXplID0gZmFsc2UsXG5cdFx0XHRyZXNpemFibGVPcHRpb25zID0ge307XG5cblx0XHQkLmVhY2goIG9wdGlvbnMsIGZ1bmN0aW9uKCBrZXksIHZhbHVlICkge1xuXHRcdFx0dGhhdC5fc2V0T3B0aW9uKCBrZXksIHZhbHVlICk7XG5cblx0XHRcdGlmICgga2V5IGluIHRoYXQuc2l6ZVJlbGF0ZWRPcHRpb25zICkge1xuXHRcdFx0XHRyZXNpemUgPSB0cnVlO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCBrZXkgaW4gdGhhdC5yZXNpemFibGVSZWxhdGVkT3B0aW9ucyApIHtcblx0XHRcdFx0cmVzaXphYmxlT3B0aW9uc1sga2V5IF0gPSB2YWx1ZTtcblx0XHRcdH1cblx0XHR9ICk7XG5cblx0XHRpZiAoIHJlc2l6ZSApIHtcblx0XHRcdHRoaXMuX3NpemUoKTtcblx0XHRcdHRoaXMuX3Bvc2l0aW9uKCk7XG5cdFx0fVxuXHRcdGlmICggdGhpcy51aURpYWxvZy5pcyggXCI6ZGF0YSh1aS1yZXNpemFibGUpXCIgKSApIHtcblx0XHRcdHRoaXMudWlEaWFsb2cucmVzaXphYmxlKCBcIm9wdGlvblwiLCByZXNpemFibGVPcHRpb25zICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRPcHRpb246IGZ1bmN0aW9uKCBrZXksIHZhbHVlICkge1xuXHRcdHZhciBpc0RyYWdnYWJsZSwgaXNSZXNpemFibGUsXG5cdFx0XHR1aURpYWxvZyA9IHRoaXMudWlEaWFsb2c7XG5cblx0XHRpZiAoIGtleSA9PT0gXCJkaXNhYmxlZFwiICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHRoaXMuX3N1cGVyKCBrZXksIHZhbHVlICk7XG5cblx0XHRpZiAoIGtleSA9PT0gXCJhcHBlbmRUb1wiICkge1xuXHRcdFx0dGhpcy51aURpYWxvZy5hcHBlbmRUbyggdGhpcy5fYXBwZW5kVG8oKSApO1xuXHRcdH1cblxuXHRcdGlmICgga2V5ID09PSBcImJ1dHRvbnNcIiApIHtcblx0XHRcdHRoaXMuX2NyZWF0ZUJ1dHRvbnMoKTtcblx0XHR9XG5cblx0XHRpZiAoIGtleSA9PT0gXCJjbG9zZVRleHRcIiApIHtcblx0XHRcdHRoaXMudWlEaWFsb2dUaXRsZWJhckNsb3NlLmJ1dHRvbigge1xuXG5cdFx0XHRcdC8vIEVuc3VyZSB0aGF0IHdlIGFsd2F5cyBwYXNzIGEgc3RyaW5nXG5cdFx0XHRcdGxhYmVsOiAkKCBcIjxhPlwiICkudGV4dCggXCJcIiArIHRoaXMub3B0aW9ucy5jbG9zZVRleHQgKS5odG1sKClcblx0XHRcdH0gKTtcblx0XHR9XG5cblx0XHRpZiAoIGtleSA9PT0gXCJkcmFnZ2FibGVcIiApIHtcblx0XHRcdGlzRHJhZ2dhYmxlID0gdWlEaWFsb2cuaXMoIFwiOmRhdGEodWktZHJhZ2dhYmxlKVwiICk7XG5cdFx0XHRpZiAoIGlzRHJhZ2dhYmxlICYmICF2YWx1ZSApIHtcblx0XHRcdFx0dWlEaWFsb2cuZHJhZ2dhYmxlKCBcImRlc3Ryb3lcIiApO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoICFpc0RyYWdnYWJsZSAmJiB2YWx1ZSApIHtcblx0XHRcdFx0dGhpcy5fbWFrZURyYWdnYWJsZSgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmICgga2V5ID09PSBcInBvc2l0aW9uXCIgKSB7XG5cdFx0XHR0aGlzLl9wb3NpdGlvbigpO1xuXHRcdH1cblxuXHRcdGlmICgga2V5ID09PSBcInJlc2l6YWJsZVwiICkge1xuXG5cdFx0XHQvLyBjdXJyZW50bHkgcmVzaXphYmxlLCBiZWNvbWluZyBub24tcmVzaXphYmxlXG5cdFx0XHRpc1Jlc2l6YWJsZSA9IHVpRGlhbG9nLmlzKCBcIjpkYXRhKHVpLXJlc2l6YWJsZSlcIiApO1xuXHRcdFx0aWYgKCBpc1Jlc2l6YWJsZSAmJiAhdmFsdWUgKSB7XG5cdFx0XHRcdHVpRGlhbG9nLnJlc2l6YWJsZSggXCJkZXN0cm95XCIgKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gQ3VycmVudGx5IHJlc2l6YWJsZSwgY2hhbmdpbmcgaGFuZGxlc1xuXHRcdFx0aWYgKCBpc1Jlc2l6YWJsZSAmJiB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRcdHVpRGlhbG9nLnJlc2l6YWJsZSggXCJvcHRpb25cIiwgXCJoYW5kbGVzXCIsIHZhbHVlICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEN1cnJlbnRseSBub24tcmVzaXphYmxlLCBiZWNvbWluZyByZXNpemFibGVcblx0XHRcdGlmICggIWlzUmVzaXphYmxlICYmIHZhbHVlICE9PSBmYWxzZSApIHtcblx0XHRcdFx0dGhpcy5fbWFrZVJlc2l6YWJsZSgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmICgga2V5ID09PSBcInRpdGxlXCIgKSB7XG5cdFx0XHR0aGlzLl90aXRsZSggdGhpcy51aURpYWxvZ1RpdGxlYmFyLmZpbmQoIFwiLnVpLWRpYWxvZy10aXRsZVwiICkgKTtcblx0XHR9XG5cdH0sXG5cblx0X3NpemU6IGZ1bmN0aW9uKCkge1xuXG5cdFx0Ly8gSWYgdGhlIHVzZXIgaGFzIHJlc2l6ZWQgdGhlIGRpYWxvZywgdGhlIC51aS1kaWFsb2cgYW5kIC51aS1kaWFsb2ctY29udGVudFxuXHRcdC8vIGRpdnMgd2lsbCBib3RoIGhhdmUgd2lkdGggYW5kIGhlaWdodCBzZXQsIHNvIHdlIG5lZWQgdG8gcmVzZXQgdGhlbVxuXHRcdHZhciBub25Db250ZW50SGVpZ2h0LCBtaW5Db250ZW50SGVpZ2h0LCBtYXhDb250ZW50SGVpZ2h0LFxuXHRcdFx0b3B0aW9ucyA9IHRoaXMub3B0aW9ucztcblxuXHRcdC8vIFJlc2V0IGNvbnRlbnQgc2l6aW5nXG5cdFx0dGhpcy5lbGVtZW50LnNob3coKS5jc3MoIHtcblx0XHRcdHdpZHRoOiBcImF1dG9cIixcblx0XHRcdG1pbkhlaWdodDogMCxcblx0XHRcdG1heEhlaWdodDogXCJub25lXCIsXG5cdFx0XHRoZWlnaHQ6IDBcblx0XHR9ICk7XG5cblx0XHRpZiAoIG9wdGlvbnMubWluV2lkdGggPiBvcHRpb25zLndpZHRoICkge1xuXHRcdFx0b3B0aW9ucy53aWR0aCA9IG9wdGlvbnMubWluV2lkdGg7XG5cdFx0fVxuXG5cdFx0Ly8gUmVzZXQgd3JhcHBlciBzaXppbmdcblx0XHQvLyBkZXRlcm1pbmUgdGhlIGhlaWdodCBvZiBhbGwgdGhlIG5vbi1jb250ZW50IGVsZW1lbnRzXG5cdFx0bm9uQ29udGVudEhlaWdodCA9IHRoaXMudWlEaWFsb2cuY3NzKCB7XG5cdFx0XHRoZWlnaHQ6IFwiYXV0b1wiLFxuXHRcdFx0d2lkdGg6IG9wdGlvbnMud2lkdGhcblx0XHR9IClcblx0XHRcdC5vdXRlckhlaWdodCgpO1xuXHRcdG1pbkNvbnRlbnRIZWlnaHQgPSBNYXRoLm1heCggMCwgb3B0aW9ucy5taW5IZWlnaHQgLSBub25Db250ZW50SGVpZ2h0ICk7XG5cdFx0bWF4Q29udGVudEhlaWdodCA9IHR5cGVvZiBvcHRpb25zLm1heEhlaWdodCA9PT0gXCJudW1iZXJcIiA/XG5cdFx0XHRNYXRoLm1heCggMCwgb3B0aW9ucy5tYXhIZWlnaHQgLSBub25Db250ZW50SGVpZ2h0ICkgOlxuXHRcdFx0XCJub25lXCI7XG5cblx0XHRpZiAoIG9wdGlvbnMuaGVpZ2h0ID09PSBcImF1dG9cIiApIHtcblx0XHRcdHRoaXMuZWxlbWVudC5jc3MoIHtcblx0XHRcdFx0bWluSGVpZ2h0OiBtaW5Db250ZW50SGVpZ2h0LFxuXHRcdFx0XHRtYXhIZWlnaHQ6IG1heENvbnRlbnRIZWlnaHQsXG5cdFx0XHRcdGhlaWdodDogXCJhdXRvXCJcblx0XHRcdH0gKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5lbGVtZW50LmhlaWdodCggTWF0aC5tYXgoIDAsIG9wdGlvbnMuaGVpZ2h0IC0gbm9uQ29udGVudEhlaWdodCApICk7XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLnVpRGlhbG9nLmlzKCBcIjpkYXRhKHVpLXJlc2l6YWJsZSlcIiApICkge1xuXHRcdFx0dGhpcy51aURpYWxvZy5yZXNpemFibGUoIFwib3B0aW9uXCIsIFwibWluSGVpZ2h0XCIsIHRoaXMuX21pbkhlaWdodCgpICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9ibG9ja0ZyYW1lczogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5pZnJhbWVCbG9ja3MgPSB0aGlzLmRvY3VtZW50LmZpbmQoIFwiaWZyYW1lXCIgKS5tYXAoIGZ1bmN0aW9uKCkge1xuXHRcdFx0dmFyIGlmcmFtZSA9ICQoIHRoaXMgKTtcblxuXHRcdFx0cmV0dXJuICQoIFwiPGRpdj5cIiApXG5cdFx0XHRcdC5jc3MoIHtcblx0XHRcdFx0XHRwb3NpdGlvbjogXCJhYnNvbHV0ZVwiLFxuXHRcdFx0XHRcdHdpZHRoOiBpZnJhbWUub3V0ZXJXaWR0aCgpLFxuXHRcdFx0XHRcdGhlaWdodDogaWZyYW1lLm91dGVySGVpZ2h0KClcblx0XHRcdFx0fSApXG5cdFx0XHRcdC5hcHBlbmRUbyggaWZyYW1lLnBhcmVudCgpIClcblx0XHRcdFx0Lm9mZnNldCggaWZyYW1lLm9mZnNldCgpIClbIDAgXTtcblx0XHR9ICk7XG5cdH0sXG5cblx0X3VuYmxvY2tGcmFtZXM6IGZ1bmN0aW9uKCkge1xuXHRcdGlmICggdGhpcy5pZnJhbWVCbG9ja3MgKSB7XG5cdFx0XHR0aGlzLmlmcmFtZUJsb2Nrcy5yZW1vdmUoKTtcblx0XHRcdGRlbGV0ZSB0aGlzLmlmcmFtZUJsb2Nrcztcblx0XHR9XG5cdH0sXG5cblx0X2FsbG93SW50ZXJhY3Rpb246IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRpZiAoICQoIGV2ZW50LnRhcmdldCApLmNsb3Nlc3QoIFwiLnVpLWRpYWxvZ1wiICkubGVuZ3RoICkge1xuXHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0fVxuXG5cdFx0Ly8gVE9ETzogUmVtb3ZlIGhhY2sgd2hlbiBkYXRlcGlja2VyIGltcGxlbWVudHNcblx0XHQvLyB0aGUgLnVpLWZyb250IGxvZ2ljICgjODk4OSlcblx0XHRyZXR1cm4gISEkKCBldmVudC50YXJnZXQgKS5jbG9zZXN0KCBcIi51aS1kYXRlcGlja2VyXCIgKS5sZW5ndGg7XG5cdH0sXG5cblx0X2NyZWF0ZU92ZXJsYXk6IGZ1bmN0aW9uKCkge1xuXHRcdGlmICggIXRoaXMub3B0aW9ucy5tb2RhbCApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBXZSB1c2UgYSBkZWxheSBpbiBjYXNlIHRoZSBvdmVybGF5IGlzIGNyZWF0ZWQgZnJvbSBhblxuXHRcdC8vIGV2ZW50IHRoYXQgd2UncmUgZ29pbmcgdG8gYmUgY2FuY2VsbGluZyAoIzI4MDQpXG5cdFx0dmFyIGlzT3BlbmluZyA9IHRydWU7XG5cdFx0dGhpcy5fZGVsYXkoIGZ1bmN0aW9uKCkge1xuXHRcdFx0aXNPcGVuaW5nID0gZmFsc2U7XG5cdFx0fSApO1xuXG5cdFx0aWYgKCAhdGhpcy5kb2N1bWVudC5kYXRhKCBcInVpLWRpYWxvZy1vdmVybGF5c1wiICkgKSB7XG5cblx0XHRcdC8vIFByZXZlbnQgdXNlIG9mIGFuY2hvcnMgYW5kIGlucHV0c1xuXHRcdFx0Ly8gVXNpbmcgX29uKCkgZm9yIGFuIGV2ZW50IGhhbmRsZXIgc2hhcmVkIGFjcm9zcyBtYW55IGluc3RhbmNlcyBpc1xuXHRcdFx0Ly8gc2FmZSBiZWNhdXNlIHRoZSBkaWFsb2dzIHN0YWNrIGFuZCBtdXN0IGJlIGNsb3NlZCBpbiByZXZlcnNlIG9yZGVyXG5cdFx0XHR0aGlzLl9vbiggdGhpcy5kb2N1bWVudCwge1xuXHRcdFx0XHRmb2N1c2luOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdFx0aWYgKCBpc09wZW5pbmcgKSB7XG5cdFx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYgKCAhdGhpcy5fYWxsb3dJbnRlcmFjdGlvbiggZXZlbnQgKSApIHtcblx0XHRcdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdFx0XHR0aGlzLl90cmFja2luZ0luc3RhbmNlcygpWyAwIF0uX2ZvY3VzVGFiYmFibGUoKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH0gKTtcblx0XHR9XG5cblx0XHR0aGlzLm92ZXJsYXkgPSAkKCBcIjxkaXY+XCIgKVxuXHRcdFx0LmFwcGVuZFRvKCB0aGlzLl9hcHBlbmRUbygpICk7XG5cblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5vdmVybGF5LCBudWxsLCBcInVpLXdpZGdldC1vdmVybGF5IHVpLWZyb250XCIgKTtcblx0XHR0aGlzLl9vbiggdGhpcy5vdmVybGF5LCB7XG5cdFx0XHRtb3VzZWRvd246IFwiX2tlZXBGb2N1c1wiXG5cdFx0fSApO1xuXHRcdHRoaXMuZG9jdW1lbnQuZGF0YSggXCJ1aS1kaWFsb2ctb3ZlcmxheXNcIixcblx0XHRcdCggdGhpcy5kb2N1bWVudC5kYXRhKCBcInVpLWRpYWxvZy1vdmVybGF5c1wiICkgfHwgMCApICsgMSApO1xuXHR9LFxuXG5cdF9kZXN0cm95T3ZlcmxheTogZnVuY3Rpb24oKSB7XG5cdFx0aWYgKCAhdGhpcy5vcHRpb25zLm1vZGFsICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGlmICggdGhpcy5vdmVybGF5ICkge1xuXHRcdFx0dmFyIG92ZXJsYXlzID0gdGhpcy5kb2N1bWVudC5kYXRhKCBcInVpLWRpYWxvZy1vdmVybGF5c1wiICkgLSAxO1xuXG5cdFx0XHRpZiAoICFvdmVybGF5cyApIHtcblx0XHRcdFx0dGhpcy5fb2ZmKCB0aGlzLmRvY3VtZW50LCBcImZvY3VzaW5cIiApO1xuXHRcdFx0XHR0aGlzLmRvY3VtZW50LnJlbW92ZURhdGEoIFwidWktZGlhbG9nLW92ZXJsYXlzXCIgKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMuZG9jdW1lbnQuZGF0YSggXCJ1aS1kaWFsb2ctb3ZlcmxheXNcIiwgb3ZlcmxheXMgKTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5vdmVybGF5LnJlbW92ZSgpO1xuXHRcdFx0dGhpcy5vdmVybGF5ID0gbnVsbDtcblx0XHR9XG5cdH1cbn0gKTtcblxuLy8gREVQUkVDQVRFRFxuLy8gVE9ETzogc3dpdGNoIHJldHVybiBiYWNrIHRvIHdpZGdldCBkZWNsYXJhdGlvbiBhdCB0b3Agb2YgZmlsZSB3aGVuIHRoaXMgaXMgcmVtb3ZlZFxuaWYgKCAkLnVpQmFja0NvbXBhdCAhPT0gZmFsc2UgKSB7XG5cblx0Ly8gQmFja2NvbXBhdCBmb3IgZGlhbG9nQ2xhc3Mgb3B0aW9uXG5cdCQud2lkZ2V0KCBcInVpLmRpYWxvZ1wiLCAkLnVpLmRpYWxvZywge1xuXHRcdG9wdGlvbnM6IHtcblx0XHRcdGRpYWxvZ0NsYXNzOiBcIlwiXG5cdFx0fSxcblx0XHRfY3JlYXRlV3JhcHBlcjogZnVuY3Rpb24oKSB7XG5cdFx0XHR0aGlzLl9zdXBlcigpO1xuXHRcdFx0dGhpcy51aURpYWxvZy5hZGRDbGFzcyggdGhpcy5vcHRpb25zLmRpYWxvZ0NsYXNzICk7XG5cdFx0fSxcblx0XHRfc2V0T3B0aW9uOiBmdW5jdGlvbigga2V5LCB2YWx1ZSApIHtcblx0XHRcdGlmICgga2V5ID09PSBcImRpYWxvZ0NsYXNzXCIgKSB7XG5cdFx0XHRcdHRoaXMudWlEaWFsb2dcblx0XHRcdFx0XHQucmVtb3ZlQ2xhc3MoIHRoaXMub3B0aW9ucy5kaWFsb2dDbGFzcyApXG5cdFx0XHRcdFx0LmFkZENsYXNzKCB2YWx1ZSApO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5fc3VwZXJBcHBseSggYXJndW1lbnRzICk7XG5cdFx0fVxuXHR9ICk7XG59XG5cbnZhciB3aWRnZXRzRGlhbG9nID0gJC51aS5kaWFsb2c7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgRHJvcHBhYmxlIDEuMTIuMVxuICogaHR0cDovL2pxdWVyeXVpLmNvbVxuICpcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzXG4gKiBSZWxlYXNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UuXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKi9cblxuLy8+PmxhYmVsOiBEcm9wcGFibGVcbi8vPj5ncm91cDogSW50ZXJhY3Rpb25zXG4vLz4+ZGVzY3JpcHRpb246IEVuYWJsZXMgZHJvcCB0YXJnZXRzIGZvciBkcmFnZ2FibGUgZWxlbWVudHMuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vZHJvcHBhYmxlL1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL2Ryb3BwYWJsZS9cblxuXG5cbiQud2lkZ2V0KCBcInVpLmRyb3BwYWJsZVwiLCB7XG5cdHZlcnNpb246IFwiMS4xMi4xXCIsXG5cdHdpZGdldEV2ZW50UHJlZml4OiBcImRyb3BcIixcblx0b3B0aW9uczoge1xuXHRcdGFjY2VwdDogXCIqXCIsXG5cdFx0YWRkQ2xhc3NlczogdHJ1ZSxcblx0XHRncmVlZHk6IGZhbHNlLFxuXHRcdHNjb3BlOiBcImRlZmF1bHRcIixcblx0XHR0b2xlcmFuY2U6IFwiaW50ZXJzZWN0XCIsXG5cblx0XHQvLyBDYWxsYmFja3Ncblx0XHRhY3RpdmF0ZTogbnVsbCxcblx0XHRkZWFjdGl2YXRlOiBudWxsLFxuXHRcdGRyb3A6IG51bGwsXG5cdFx0b3V0OiBudWxsLFxuXHRcdG92ZXI6IG51bGxcblx0fSxcblx0X2NyZWF0ZTogZnVuY3Rpb24oKSB7XG5cblx0XHR2YXIgcHJvcG9ydGlvbnMsXG5cdFx0XHRvID0gdGhpcy5vcHRpb25zLFxuXHRcdFx0YWNjZXB0ID0gby5hY2NlcHQ7XG5cblx0XHR0aGlzLmlzb3ZlciA9IGZhbHNlO1xuXHRcdHRoaXMuaXNvdXQgPSB0cnVlO1xuXG5cdFx0dGhpcy5hY2NlcHQgPSAkLmlzRnVuY3Rpb24oIGFjY2VwdCApID8gYWNjZXB0IDogZnVuY3Rpb24oIGQgKSB7XG5cdFx0XHRyZXR1cm4gZC5pcyggYWNjZXB0ICk7XG5cdFx0fTtcblxuXHRcdHRoaXMucHJvcG9ydGlvbnMgPSBmdW5jdGlvbiggLyogdmFsdWVUb1dyaXRlICovICkge1xuXHRcdFx0aWYgKCBhcmd1bWVudHMubGVuZ3RoICkge1xuXG5cdFx0XHRcdC8vIFN0b3JlIHRoZSBkcm9wcGFibGUncyBwcm9wb3J0aW9uc1xuXHRcdFx0XHRwcm9wb3J0aW9ucyA9IGFyZ3VtZW50c1sgMCBdO1xuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBSZXRyaWV2ZSBvciBkZXJpdmUgdGhlIGRyb3BwYWJsZSdzIHByb3BvcnRpb25zXG5cdFx0XHRcdHJldHVybiBwcm9wb3J0aW9ucyA/XG5cdFx0XHRcdFx0cHJvcG9ydGlvbnMgOlxuXHRcdFx0XHRcdHByb3BvcnRpb25zID0ge1xuXHRcdFx0XHRcdFx0d2lkdGg6IHRoaXMuZWxlbWVudFsgMCBdLm9mZnNldFdpZHRoLFxuXHRcdFx0XHRcdFx0aGVpZ2h0OiB0aGlzLmVsZW1lbnRbIDAgXS5vZmZzZXRIZWlnaHRcblx0XHRcdFx0XHR9O1xuXHRcdFx0fVxuXHRcdH07XG5cblx0XHR0aGlzLl9hZGRUb01hbmFnZXIoIG8uc2NvcGUgKTtcblxuXHRcdG8uYWRkQ2xhc3NlcyAmJiB0aGlzLl9hZGRDbGFzcyggXCJ1aS1kcm9wcGFibGVcIiApO1xuXG5cdH0sXG5cblx0X2FkZFRvTWFuYWdlcjogZnVuY3Rpb24oIHNjb3BlICkge1xuXG5cdFx0Ly8gQWRkIHRoZSByZWZlcmVuY2UgYW5kIHBvc2l0aW9ucyB0byB0aGUgbWFuYWdlclxuXHRcdCQudWkuZGRtYW5hZ2VyLmRyb3BwYWJsZXNbIHNjb3BlIF0gPSAkLnVpLmRkbWFuYWdlci5kcm9wcGFibGVzWyBzY29wZSBdIHx8IFtdO1xuXHRcdCQudWkuZGRtYW5hZ2VyLmRyb3BwYWJsZXNbIHNjb3BlIF0ucHVzaCggdGhpcyApO1xuXHR9LFxuXG5cdF9zcGxpY2U6IGZ1bmN0aW9uKCBkcm9wICkge1xuXHRcdHZhciBpID0gMDtcblx0XHRmb3IgKCA7IGkgPCBkcm9wLmxlbmd0aDsgaSsrICkge1xuXHRcdFx0aWYgKCBkcm9wWyBpIF0gPT09IHRoaXMgKSB7XG5cdFx0XHRcdGRyb3Auc3BsaWNlKCBpLCAxICk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9kZXN0cm95OiBmdW5jdGlvbigpIHtcblx0XHR2YXIgZHJvcCA9ICQudWkuZGRtYW5hZ2VyLmRyb3BwYWJsZXNbIHRoaXMub3B0aW9ucy5zY29wZSBdO1xuXG5cdFx0dGhpcy5fc3BsaWNlKCBkcm9wICk7XG5cdH0sXG5cblx0X3NldE9wdGlvbjogZnVuY3Rpb24oIGtleSwgdmFsdWUgKSB7XG5cblx0XHRpZiAoIGtleSA9PT0gXCJhY2NlcHRcIiApIHtcblx0XHRcdHRoaXMuYWNjZXB0ID0gJC5pc0Z1bmN0aW9uKCB2YWx1ZSApID8gdmFsdWUgOiBmdW5jdGlvbiggZCApIHtcblx0XHRcdFx0cmV0dXJuIGQuaXMoIHZhbHVlICk7XG5cdFx0XHR9O1xuXHRcdH0gZWxzZSBpZiAoIGtleSA9PT0gXCJzY29wZVwiICkge1xuXHRcdFx0dmFyIGRyb3AgPSAkLnVpLmRkbWFuYWdlci5kcm9wcGFibGVzWyB0aGlzLm9wdGlvbnMuc2NvcGUgXTtcblxuXHRcdFx0dGhpcy5fc3BsaWNlKCBkcm9wICk7XG5cdFx0XHR0aGlzLl9hZGRUb01hbmFnZXIoIHZhbHVlICk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fc3VwZXIoIGtleSwgdmFsdWUgKTtcblx0fSxcblxuXHRfYWN0aXZhdGU6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgZHJhZ2dhYmxlID0gJC51aS5kZG1hbmFnZXIuY3VycmVudDtcblxuXHRcdHRoaXMuX2FkZEFjdGl2ZUNsYXNzKCk7XG5cdFx0aWYgKCBkcmFnZ2FibGUgKSB7XG5cdFx0XHR0aGlzLl90cmlnZ2VyKCBcImFjdGl2YXRlXCIsIGV2ZW50LCB0aGlzLnVpKCBkcmFnZ2FibGUgKSApO1xuXHRcdH1cblx0fSxcblxuXHRfZGVhY3RpdmF0ZTogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHZhciBkcmFnZ2FibGUgPSAkLnVpLmRkbWFuYWdlci5jdXJyZW50O1xuXG5cdFx0dGhpcy5fcmVtb3ZlQWN0aXZlQ2xhc3MoKTtcblx0XHRpZiAoIGRyYWdnYWJsZSApIHtcblx0XHRcdHRoaXMuX3RyaWdnZXIoIFwiZGVhY3RpdmF0ZVwiLCBldmVudCwgdGhpcy51aSggZHJhZ2dhYmxlICkgKTtcblx0XHR9XG5cdH0sXG5cblx0X292ZXI6IGZ1bmN0aW9uKCBldmVudCApIHtcblxuXHRcdHZhciBkcmFnZ2FibGUgPSAkLnVpLmRkbWFuYWdlci5jdXJyZW50O1xuXG5cdFx0Ly8gQmFpbCBpZiBkcmFnZ2FibGUgYW5kIGRyb3BwYWJsZSBhcmUgc2FtZSBlbGVtZW50XG5cdFx0aWYgKCAhZHJhZ2dhYmxlIHx8ICggZHJhZ2dhYmxlLmN1cnJlbnRJdGVtIHx8XG5cdFx0XHRcdGRyYWdnYWJsZS5lbGVtZW50IClbIDAgXSA9PT0gdGhpcy5lbGVtZW50WyAwIF0gKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLmFjY2VwdC5jYWxsKCB0aGlzLmVsZW1lbnRbIDAgXSwgKCBkcmFnZ2FibGUuY3VycmVudEl0ZW0gfHxcblx0XHRcdFx0ZHJhZ2dhYmxlLmVsZW1lbnQgKSApICkge1xuXHRcdFx0dGhpcy5fYWRkSG92ZXJDbGFzcygpO1xuXHRcdFx0dGhpcy5fdHJpZ2dlciggXCJvdmVyXCIsIGV2ZW50LCB0aGlzLnVpKCBkcmFnZ2FibGUgKSApO1xuXHRcdH1cblxuXHR9LFxuXG5cdF9vdXQ6IGZ1bmN0aW9uKCBldmVudCApIHtcblxuXHRcdHZhciBkcmFnZ2FibGUgPSAkLnVpLmRkbWFuYWdlci5jdXJyZW50O1xuXG5cdFx0Ly8gQmFpbCBpZiBkcmFnZ2FibGUgYW5kIGRyb3BwYWJsZSBhcmUgc2FtZSBlbGVtZW50XG5cdFx0aWYgKCAhZHJhZ2dhYmxlIHx8ICggZHJhZ2dhYmxlLmN1cnJlbnRJdGVtIHx8XG5cdFx0XHRcdGRyYWdnYWJsZS5lbGVtZW50IClbIDAgXSA9PT0gdGhpcy5lbGVtZW50WyAwIF0gKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLmFjY2VwdC5jYWxsKCB0aGlzLmVsZW1lbnRbIDAgXSwgKCBkcmFnZ2FibGUuY3VycmVudEl0ZW0gfHxcblx0XHRcdFx0ZHJhZ2dhYmxlLmVsZW1lbnQgKSApICkge1xuXHRcdFx0dGhpcy5fcmVtb3ZlSG92ZXJDbGFzcygpO1xuXHRcdFx0dGhpcy5fdHJpZ2dlciggXCJvdXRcIiwgZXZlbnQsIHRoaXMudWkoIGRyYWdnYWJsZSApICk7XG5cdFx0fVxuXG5cdH0sXG5cblx0X2Ryb3A6IGZ1bmN0aW9uKCBldmVudCwgY3VzdG9tICkge1xuXG5cdFx0dmFyIGRyYWdnYWJsZSA9IGN1c3RvbSB8fCAkLnVpLmRkbWFuYWdlci5jdXJyZW50LFxuXHRcdFx0Y2hpbGRyZW5JbnRlcnNlY3Rpb24gPSBmYWxzZTtcblxuXHRcdC8vIEJhaWwgaWYgZHJhZ2dhYmxlIGFuZCBkcm9wcGFibGUgYXJlIHNhbWUgZWxlbWVudFxuXHRcdGlmICggIWRyYWdnYWJsZSB8fCAoIGRyYWdnYWJsZS5jdXJyZW50SXRlbSB8fFxuXHRcdFx0XHRkcmFnZ2FibGUuZWxlbWVudCApWyAwIF0gPT09IHRoaXMuZWxlbWVudFsgMCBdICkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdHRoaXMuZWxlbWVudFxuXHRcdFx0LmZpbmQoIFwiOmRhdGEodWktZHJvcHBhYmxlKVwiIClcblx0XHRcdC5ub3QoIFwiLnVpLWRyYWdnYWJsZS1kcmFnZ2luZ1wiIClcblx0XHRcdC5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdFx0dmFyIGluc3QgPSAkKCB0aGlzICkuZHJvcHBhYmxlKCBcImluc3RhbmNlXCIgKTtcblx0XHRcdFx0aWYgKFxuXHRcdFx0XHRcdGluc3Qub3B0aW9ucy5ncmVlZHkgJiZcblx0XHRcdFx0XHQhaW5zdC5vcHRpb25zLmRpc2FibGVkICYmXG5cdFx0XHRcdFx0aW5zdC5vcHRpb25zLnNjb3BlID09PSBkcmFnZ2FibGUub3B0aW9ucy5zY29wZSAmJlxuXHRcdFx0XHRcdGluc3QuYWNjZXB0LmNhbGwoXG5cdFx0XHRcdFx0XHRpbnN0LmVsZW1lbnRbIDAgXSwgKCBkcmFnZ2FibGUuY3VycmVudEl0ZW0gfHwgZHJhZ2dhYmxlLmVsZW1lbnQgKVxuXHRcdFx0XHRcdCkgJiZcblx0XHRcdFx0XHRpbnRlcnNlY3QoXG5cdFx0XHRcdFx0XHRkcmFnZ2FibGUsXG5cdFx0XHRcdFx0XHQkLmV4dGVuZCggaW5zdCwgeyBvZmZzZXQ6IGluc3QuZWxlbWVudC5vZmZzZXQoKSB9ICksXG5cdFx0XHRcdFx0XHRpbnN0Lm9wdGlvbnMudG9sZXJhbmNlLCBldmVudFxuXHRcdFx0XHRcdClcblx0XHRcdFx0KSB7XG5cdFx0XHRcdFx0Y2hpbGRyZW5JbnRlcnNlY3Rpb24gPSB0cnVlO1xuXHRcdFx0XHRcdHJldHVybiBmYWxzZTsgfVxuXHRcdFx0fSApO1xuXHRcdGlmICggY2hpbGRyZW5JbnRlcnNlY3Rpb24gKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLmFjY2VwdC5jYWxsKCB0aGlzLmVsZW1lbnRbIDAgXSxcblx0XHRcdFx0KCBkcmFnZ2FibGUuY3VycmVudEl0ZW0gfHwgZHJhZ2dhYmxlLmVsZW1lbnQgKSApICkge1xuXHRcdFx0dGhpcy5fcmVtb3ZlQWN0aXZlQ2xhc3MoKTtcblx0XHRcdHRoaXMuX3JlbW92ZUhvdmVyQ2xhc3MoKTtcblxuXHRcdFx0dGhpcy5fdHJpZ2dlciggXCJkcm9wXCIsIGV2ZW50LCB0aGlzLnVpKCBkcmFnZ2FibGUgKSApO1xuXHRcdFx0cmV0dXJuIHRoaXMuZWxlbWVudDtcblx0XHR9XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cblx0fSxcblxuXHR1aTogZnVuY3Rpb24oIGMgKSB7XG5cdFx0cmV0dXJuIHtcblx0XHRcdGRyYWdnYWJsZTogKCBjLmN1cnJlbnRJdGVtIHx8IGMuZWxlbWVudCApLFxuXHRcdFx0aGVscGVyOiBjLmhlbHBlcixcblx0XHRcdHBvc2l0aW9uOiBjLnBvc2l0aW9uLFxuXHRcdFx0b2Zmc2V0OiBjLnBvc2l0aW9uQWJzXG5cdFx0fTtcblx0fSxcblxuXHQvLyBFeHRlbnNpb24gcG9pbnRzIGp1c3QgdG8gbWFrZSBiYWNrY29tcGF0IHNhbmUgYW5kIGF2b2lkIGR1cGxpY2F0aW5nIGxvZ2ljXG5cdC8vIFRPRE86IFJlbW92ZSBpbiAxLjEzIGFsb25nIHdpdGggY2FsbCB0byBpdCBiZWxvd1xuXHRfYWRkSG92ZXJDbGFzczogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIFwidWktZHJvcHBhYmxlLWhvdmVyXCIgKTtcblx0fSxcblxuXHRfcmVtb3ZlSG92ZXJDbGFzczogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIFwidWktZHJvcHBhYmxlLWhvdmVyXCIgKTtcblx0fSxcblxuXHRfYWRkQWN0aXZlQ2xhc3M6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuX2FkZENsYXNzKCBcInVpLWRyb3BwYWJsZS1hY3RpdmVcIiApO1xuXHR9LFxuXG5cdF9yZW1vdmVBY3RpdmVDbGFzczogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIFwidWktZHJvcHBhYmxlLWFjdGl2ZVwiICk7XG5cdH1cbn0gKTtcblxudmFyIGludGVyc2VjdCA9ICQudWkuaW50ZXJzZWN0ID0gKCBmdW5jdGlvbigpIHtcblx0ZnVuY3Rpb24gaXNPdmVyQXhpcyggeCwgcmVmZXJlbmNlLCBzaXplICkge1xuXHRcdHJldHVybiAoIHggPj0gcmVmZXJlbmNlICkgJiYgKCB4IDwgKCByZWZlcmVuY2UgKyBzaXplICkgKTtcblx0fVxuXG5cdHJldHVybiBmdW5jdGlvbiggZHJhZ2dhYmxlLCBkcm9wcGFibGUsIHRvbGVyYW5jZU1vZGUsIGV2ZW50ICkge1xuXG5cdFx0aWYgKCAhZHJvcHBhYmxlLm9mZnNldCApIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHR2YXIgeDEgPSAoIGRyYWdnYWJsZS5wb3NpdGlvbkFicyB8fFxuXHRcdFx0XHRkcmFnZ2FibGUucG9zaXRpb24uYWJzb2x1dGUgKS5sZWZ0ICsgZHJhZ2dhYmxlLm1hcmdpbnMubGVmdCxcblx0XHRcdHkxID0gKCBkcmFnZ2FibGUucG9zaXRpb25BYnMgfHxcblx0XHRcdFx0ZHJhZ2dhYmxlLnBvc2l0aW9uLmFic29sdXRlICkudG9wICsgZHJhZ2dhYmxlLm1hcmdpbnMudG9wLFxuXHRcdFx0eDIgPSB4MSArIGRyYWdnYWJsZS5oZWxwZXJQcm9wb3J0aW9ucy53aWR0aCxcblx0XHRcdHkyID0geTEgKyBkcmFnZ2FibGUuaGVscGVyUHJvcG9ydGlvbnMuaGVpZ2h0LFxuXHRcdFx0bCA9IGRyb3BwYWJsZS5vZmZzZXQubGVmdCxcblx0XHRcdHQgPSBkcm9wcGFibGUub2Zmc2V0LnRvcCxcblx0XHRcdHIgPSBsICsgZHJvcHBhYmxlLnByb3BvcnRpb25zKCkud2lkdGgsXG5cdFx0XHRiID0gdCArIGRyb3BwYWJsZS5wcm9wb3J0aW9ucygpLmhlaWdodDtcblxuXHRcdHN3aXRjaCAoIHRvbGVyYW5jZU1vZGUgKSB7XG5cdFx0Y2FzZSBcImZpdFwiOlxuXHRcdFx0cmV0dXJuICggbCA8PSB4MSAmJiB4MiA8PSByICYmIHQgPD0geTEgJiYgeTIgPD0gYiApO1xuXHRcdGNhc2UgXCJpbnRlcnNlY3RcIjpcblx0XHRcdHJldHVybiAoIGwgPCB4MSArICggZHJhZ2dhYmxlLmhlbHBlclByb3BvcnRpb25zLndpZHRoIC8gMiApICYmIC8vIFJpZ2h0IEhhbGZcblx0XHRcdFx0eDIgLSAoIGRyYWdnYWJsZS5oZWxwZXJQcm9wb3J0aW9ucy53aWR0aCAvIDIgKSA8IHIgJiYgLy8gTGVmdCBIYWxmXG5cdFx0XHRcdHQgPCB5MSArICggZHJhZ2dhYmxlLmhlbHBlclByb3BvcnRpb25zLmhlaWdodCAvIDIgKSAmJiAvLyBCb3R0b20gSGFsZlxuXHRcdFx0XHR5MiAtICggZHJhZ2dhYmxlLmhlbHBlclByb3BvcnRpb25zLmhlaWdodCAvIDIgKSA8IGIgKTsgLy8gVG9wIEhhbGZcblx0XHRjYXNlIFwicG9pbnRlclwiOlxuXHRcdFx0cmV0dXJuIGlzT3ZlckF4aXMoIGV2ZW50LnBhZ2VZLCB0LCBkcm9wcGFibGUucHJvcG9ydGlvbnMoKS5oZWlnaHQgKSAmJlxuXHRcdFx0XHRpc092ZXJBeGlzKCBldmVudC5wYWdlWCwgbCwgZHJvcHBhYmxlLnByb3BvcnRpb25zKCkud2lkdGggKTtcblx0XHRjYXNlIFwidG91Y2hcIjpcblx0XHRcdHJldHVybiAoXG5cdFx0XHRcdCggeTEgPj0gdCAmJiB5MSA8PSBiICkgfHwgLy8gVG9wIGVkZ2UgdG91Y2hpbmdcblx0XHRcdFx0KCB5MiA+PSB0ICYmIHkyIDw9IGIgKSB8fCAvLyBCb3R0b20gZWRnZSB0b3VjaGluZ1xuXHRcdFx0XHQoIHkxIDwgdCAmJiB5MiA+IGIgKSAvLyBTdXJyb3VuZGVkIHZlcnRpY2FsbHlcblx0XHRcdCkgJiYgKFxuXHRcdFx0XHQoIHgxID49IGwgJiYgeDEgPD0gciApIHx8IC8vIExlZnQgZWRnZSB0b3VjaGluZ1xuXHRcdFx0XHQoIHgyID49IGwgJiYgeDIgPD0gciApIHx8IC8vIFJpZ2h0IGVkZ2UgdG91Y2hpbmdcblx0XHRcdFx0KCB4MSA8IGwgJiYgeDIgPiByICkgLy8gU3Vycm91bmRlZCBob3Jpem9udGFsbHlcblx0XHRcdCk7XG5cdFx0ZGVmYXVsdDpcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdH07XG59ICkoKTtcblxuLypcblx0VGhpcyBtYW5hZ2VyIHRyYWNrcyBvZmZzZXRzIG9mIGRyYWdnYWJsZXMgYW5kIGRyb3BwYWJsZXNcbiovXG4kLnVpLmRkbWFuYWdlciA9IHtcblx0Y3VycmVudDogbnVsbCxcblx0ZHJvcHBhYmxlczogeyBcImRlZmF1bHRcIjogW10gfSxcblx0cHJlcGFyZU9mZnNldHM6IGZ1bmN0aW9uKCB0LCBldmVudCApIHtcblxuXHRcdHZhciBpLCBqLFxuXHRcdFx0bSA9ICQudWkuZGRtYW5hZ2VyLmRyb3BwYWJsZXNbIHQub3B0aW9ucy5zY29wZSBdIHx8IFtdLFxuXHRcdFx0dHlwZSA9IGV2ZW50ID8gZXZlbnQudHlwZSA6IG51bGwsIC8vIHdvcmthcm91bmQgZm9yICMyMzE3XG5cdFx0XHRsaXN0ID0gKCB0LmN1cnJlbnRJdGVtIHx8IHQuZWxlbWVudCApLmZpbmQoIFwiOmRhdGEodWktZHJvcHBhYmxlKVwiICkuYWRkQmFjaygpO1xuXG5cdFx0ZHJvcHBhYmxlc0xvb3A6IGZvciAoIGkgPSAwOyBpIDwgbS5sZW5ndGg7IGkrKyApIHtcblxuXHRcdFx0Ly8gTm8gZGlzYWJsZWQgYW5kIG5vbi1hY2NlcHRlZFxuXHRcdFx0aWYgKCBtWyBpIF0ub3B0aW9ucy5kaXNhYmxlZCB8fCAoIHQgJiYgIW1bIGkgXS5hY2NlcHQuY2FsbCggbVsgaSBdLmVsZW1lbnRbIDAgXSxcblx0XHRcdFx0XHQoIHQuY3VycmVudEl0ZW0gfHwgdC5lbGVtZW50ICkgKSApICkge1xuXHRcdFx0XHRjb250aW51ZTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gRmlsdGVyIG91dCBlbGVtZW50cyBpbiB0aGUgY3VycmVudCBkcmFnZ2VkIGl0ZW1cblx0XHRcdGZvciAoIGogPSAwOyBqIDwgbGlzdC5sZW5ndGg7IGorKyApIHtcblx0XHRcdFx0aWYgKCBsaXN0WyBqIF0gPT09IG1bIGkgXS5lbGVtZW50WyAwIF0gKSB7XG5cdFx0XHRcdFx0bVsgaSBdLnByb3BvcnRpb25zKCkuaGVpZ2h0ID0gMDtcblx0XHRcdFx0XHRjb250aW51ZSBkcm9wcGFibGVzTG9vcDtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHRtWyBpIF0udmlzaWJsZSA9IG1bIGkgXS5lbGVtZW50LmNzcyggXCJkaXNwbGF5XCIgKSAhPT0gXCJub25lXCI7XG5cdFx0XHRpZiAoICFtWyBpIF0udmlzaWJsZSApIHtcblx0XHRcdFx0Y29udGludWU7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEFjdGl2YXRlIHRoZSBkcm9wcGFibGUgaWYgdXNlZCBkaXJlY3RseSBmcm9tIGRyYWdnYWJsZXNcblx0XHRcdGlmICggdHlwZSA9PT0gXCJtb3VzZWRvd25cIiApIHtcblx0XHRcdFx0bVsgaSBdLl9hY3RpdmF0ZS5jYWxsKCBtWyBpIF0sIGV2ZW50ICk7XG5cdFx0XHR9XG5cblx0XHRcdG1bIGkgXS5vZmZzZXQgPSBtWyBpIF0uZWxlbWVudC5vZmZzZXQoKTtcblx0XHRcdG1bIGkgXS5wcm9wb3J0aW9ucygge1xuXHRcdFx0XHR3aWR0aDogbVsgaSBdLmVsZW1lbnRbIDAgXS5vZmZzZXRXaWR0aCxcblx0XHRcdFx0aGVpZ2h0OiBtWyBpIF0uZWxlbWVudFsgMCBdLm9mZnNldEhlaWdodFxuXHRcdFx0fSApO1xuXG5cdFx0fVxuXG5cdH0sXG5cdGRyb3A6IGZ1bmN0aW9uKCBkcmFnZ2FibGUsIGV2ZW50ICkge1xuXG5cdFx0dmFyIGRyb3BwZWQgPSBmYWxzZTtcblxuXHRcdC8vIENyZWF0ZSBhIGNvcHkgb2YgdGhlIGRyb3BwYWJsZXMgaW4gY2FzZSB0aGUgbGlzdCBjaGFuZ2VzIGR1cmluZyB0aGUgZHJvcCAoIzkxMTYpXG5cdFx0JC5lYWNoKCAoICQudWkuZGRtYW5hZ2VyLmRyb3BwYWJsZXNbIGRyYWdnYWJsZS5vcHRpb25zLnNjb3BlIF0gfHwgW10gKS5zbGljZSgpLCBmdW5jdGlvbigpIHtcblxuXHRcdFx0aWYgKCAhdGhpcy5vcHRpb25zICkge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cdFx0XHRpZiAoICF0aGlzLm9wdGlvbnMuZGlzYWJsZWQgJiYgdGhpcy52aXNpYmxlICYmXG5cdFx0XHRcdFx0aW50ZXJzZWN0KCBkcmFnZ2FibGUsIHRoaXMsIHRoaXMub3B0aW9ucy50b2xlcmFuY2UsIGV2ZW50ICkgKSB7XG5cdFx0XHRcdGRyb3BwZWQgPSB0aGlzLl9kcm9wLmNhbGwoIHRoaXMsIGV2ZW50ICkgfHwgZHJvcHBlZDtcblx0XHRcdH1cblxuXHRcdFx0aWYgKCAhdGhpcy5vcHRpb25zLmRpc2FibGVkICYmIHRoaXMudmlzaWJsZSAmJiB0aGlzLmFjY2VwdC5jYWxsKCB0aGlzLmVsZW1lbnRbIDAgXSxcblx0XHRcdFx0XHQoIGRyYWdnYWJsZS5jdXJyZW50SXRlbSB8fCBkcmFnZ2FibGUuZWxlbWVudCApICkgKSB7XG5cdFx0XHRcdHRoaXMuaXNvdXQgPSB0cnVlO1xuXHRcdFx0XHR0aGlzLmlzb3ZlciA9IGZhbHNlO1xuXHRcdFx0XHR0aGlzLl9kZWFjdGl2YXRlLmNhbGwoIHRoaXMsIGV2ZW50ICk7XG5cdFx0XHR9XG5cblx0XHR9ICk7XG5cdFx0cmV0dXJuIGRyb3BwZWQ7XG5cblx0fSxcblx0ZHJhZ1N0YXJ0OiBmdW5jdGlvbiggZHJhZ2dhYmxlLCBldmVudCApIHtcblxuXHRcdC8vIExpc3RlbiBmb3Igc2Nyb2xsaW5nIHNvIHRoYXQgaWYgdGhlIGRyYWdnaW5nIGNhdXNlcyBzY3JvbGxpbmcgdGhlIHBvc2l0aW9uIG9mIHRoZVxuXHRcdC8vIGRyb3BwYWJsZXMgY2FuIGJlIHJlY2FsY3VsYXRlZCAoc2VlICM1MDAzKVxuXHRcdGRyYWdnYWJsZS5lbGVtZW50LnBhcmVudHNVbnRpbCggXCJib2R5XCIgKS5vbiggXCJzY3JvbGwuZHJvcHBhYmxlXCIsIGZ1bmN0aW9uKCkge1xuXHRcdFx0aWYgKCAhZHJhZ2dhYmxlLm9wdGlvbnMucmVmcmVzaFBvc2l0aW9ucyApIHtcblx0XHRcdFx0JC51aS5kZG1hbmFnZXIucHJlcGFyZU9mZnNldHMoIGRyYWdnYWJsZSwgZXZlbnQgKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdH0sXG5cdGRyYWc6IGZ1bmN0aW9uKCBkcmFnZ2FibGUsIGV2ZW50ICkge1xuXG5cdFx0Ly8gSWYgeW91IGhhdmUgYSBoaWdobHkgZHluYW1pYyBwYWdlLCB5b3UgbWlnaHQgdHJ5IHRoaXMgb3B0aW9uLiBJdCByZW5kZXJzIHBvc2l0aW9uc1xuXHRcdC8vIGV2ZXJ5IHRpbWUgeW91IG1vdmUgdGhlIG1vdXNlLlxuXHRcdGlmICggZHJhZ2dhYmxlLm9wdGlvbnMucmVmcmVzaFBvc2l0aW9ucyApIHtcblx0XHRcdCQudWkuZGRtYW5hZ2VyLnByZXBhcmVPZmZzZXRzKCBkcmFnZ2FibGUsIGV2ZW50ICk7XG5cdFx0fVxuXG5cdFx0Ly8gUnVuIHRocm91Z2ggYWxsIGRyb3BwYWJsZXMgYW5kIGNoZWNrIHRoZWlyIHBvc2l0aW9ucyBiYXNlZCBvbiBzcGVjaWZpYyB0b2xlcmFuY2Ugb3B0aW9uc1xuXHRcdCQuZWFjaCggJC51aS5kZG1hbmFnZXIuZHJvcHBhYmxlc1sgZHJhZ2dhYmxlLm9wdGlvbnMuc2NvcGUgXSB8fCBbXSwgZnVuY3Rpb24oKSB7XG5cblx0XHRcdGlmICggdGhpcy5vcHRpb25zLmRpc2FibGVkIHx8IHRoaXMuZ3JlZWR5Q2hpbGQgfHwgIXRoaXMudmlzaWJsZSApIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHR2YXIgcGFyZW50SW5zdGFuY2UsIHNjb3BlLCBwYXJlbnQsXG5cdFx0XHRcdGludGVyc2VjdHMgPSBpbnRlcnNlY3QoIGRyYWdnYWJsZSwgdGhpcywgdGhpcy5vcHRpb25zLnRvbGVyYW5jZSwgZXZlbnQgKSxcblx0XHRcdFx0YyA9ICFpbnRlcnNlY3RzICYmIHRoaXMuaXNvdmVyID9cblx0XHRcdFx0XHRcImlzb3V0XCIgOlxuXHRcdFx0XHRcdCggaW50ZXJzZWN0cyAmJiAhdGhpcy5pc292ZXIgPyBcImlzb3ZlclwiIDogbnVsbCApO1xuXHRcdFx0aWYgKCAhYyApIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHRoaXMub3B0aW9ucy5ncmVlZHkgKSB7XG5cblx0XHRcdFx0Ly8gZmluZCBkcm9wcGFibGUgcGFyZW50cyB3aXRoIHNhbWUgc2NvcGVcblx0XHRcdFx0c2NvcGUgPSB0aGlzLm9wdGlvbnMuc2NvcGU7XG5cdFx0XHRcdHBhcmVudCA9IHRoaXMuZWxlbWVudC5wYXJlbnRzKCBcIjpkYXRhKHVpLWRyb3BwYWJsZSlcIiApLmZpbHRlciggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0cmV0dXJuICQoIHRoaXMgKS5kcm9wcGFibGUoIFwiaW5zdGFuY2VcIiApLm9wdGlvbnMuc2NvcGUgPT09IHNjb3BlO1xuXHRcdFx0XHR9ICk7XG5cblx0XHRcdFx0aWYgKCBwYXJlbnQubGVuZ3RoICkge1xuXHRcdFx0XHRcdHBhcmVudEluc3RhbmNlID0gJCggcGFyZW50WyAwIF0gKS5kcm9wcGFibGUoIFwiaW5zdGFuY2VcIiApO1xuXHRcdFx0XHRcdHBhcmVudEluc3RhbmNlLmdyZWVkeUNoaWxkID0gKCBjID09PSBcImlzb3ZlclwiICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gV2UganVzdCBtb3ZlZCBpbnRvIGEgZ3JlZWR5IGNoaWxkXG5cdFx0XHRpZiAoIHBhcmVudEluc3RhbmNlICYmIGMgPT09IFwiaXNvdmVyXCIgKSB7XG5cdFx0XHRcdHBhcmVudEluc3RhbmNlLmlzb3ZlciA9IGZhbHNlO1xuXHRcdFx0XHRwYXJlbnRJbnN0YW5jZS5pc291dCA9IHRydWU7XG5cdFx0XHRcdHBhcmVudEluc3RhbmNlLl9vdXQuY2FsbCggcGFyZW50SW5zdGFuY2UsIGV2ZW50ICk7XG5cdFx0XHR9XG5cblx0XHRcdHRoaXNbIGMgXSA9IHRydWU7XG5cdFx0XHR0aGlzWyBjID09PSBcImlzb3V0XCIgPyBcImlzb3ZlclwiIDogXCJpc291dFwiIF0gPSBmYWxzZTtcblx0XHRcdHRoaXNbIGMgPT09IFwiaXNvdmVyXCIgPyBcIl9vdmVyXCIgOiBcIl9vdXRcIiBdLmNhbGwoIHRoaXMsIGV2ZW50ICk7XG5cblx0XHRcdC8vIFdlIGp1c3QgbW92ZWQgb3V0IG9mIGEgZ3JlZWR5IGNoaWxkXG5cdFx0XHRpZiAoIHBhcmVudEluc3RhbmNlICYmIGMgPT09IFwiaXNvdXRcIiApIHtcblx0XHRcdFx0cGFyZW50SW5zdGFuY2UuaXNvdXQgPSBmYWxzZTtcblx0XHRcdFx0cGFyZW50SW5zdGFuY2UuaXNvdmVyID0gdHJ1ZTtcblx0XHRcdFx0cGFyZW50SW5zdGFuY2UuX292ZXIuY2FsbCggcGFyZW50SW5zdGFuY2UsIGV2ZW50ICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdH0sXG5cdGRyYWdTdG9wOiBmdW5jdGlvbiggZHJhZ2dhYmxlLCBldmVudCApIHtcblx0XHRkcmFnZ2FibGUuZWxlbWVudC5wYXJlbnRzVW50aWwoIFwiYm9keVwiICkub2ZmKCBcInNjcm9sbC5kcm9wcGFibGVcIiApO1xuXG5cdFx0Ly8gQ2FsbCBwcmVwYXJlT2Zmc2V0cyBvbmUgZmluYWwgdGltZSBzaW5jZSBJRSBkb2VzIG5vdCBmaXJlIHJldHVybiBzY3JvbGwgZXZlbnRzIHdoZW5cblx0XHQvLyBvdmVyZmxvdyB3YXMgY2F1c2VkIGJ5IGRyYWcgKHNlZSAjNTAwMylcblx0XHRpZiAoICFkcmFnZ2FibGUub3B0aW9ucy5yZWZyZXNoUG9zaXRpb25zICkge1xuXHRcdFx0JC51aS5kZG1hbmFnZXIucHJlcGFyZU9mZnNldHMoIGRyYWdnYWJsZSwgZXZlbnQgKTtcblx0XHR9XG5cdH1cbn07XG5cbi8vIERFUFJFQ0FURURcbi8vIFRPRE86IHN3aXRjaCByZXR1cm4gYmFjayB0byB3aWRnZXQgZGVjbGFyYXRpb24gYXQgdG9wIG9mIGZpbGUgd2hlbiB0aGlzIGlzIHJlbW92ZWRcbmlmICggJC51aUJhY2tDb21wYXQgIT09IGZhbHNlICkge1xuXG5cdC8vIEJhY2tjb21wYXQgZm9yIGFjdGl2ZUNsYXNzIGFuZCBob3ZlckNsYXNzIG9wdGlvbnNcblx0JC53aWRnZXQoIFwidWkuZHJvcHBhYmxlXCIsICQudWkuZHJvcHBhYmxlLCB7XG5cdFx0b3B0aW9uczoge1xuXHRcdFx0aG92ZXJDbGFzczogZmFsc2UsXG5cdFx0XHRhY3RpdmVDbGFzczogZmFsc2Vcblx0XHR9LFxuXHRcdF9hZGRBY3RpdmVDbGFzczogZnVuY3Rpb24oKSB7XG5cdFx0XHR0aGlzLl9zdXBlcigpO1xuXHRcdFx0aWYgKCB0aGlzLm9wdGlvbnMuYWN0aXZlQ2xhc3MgKSB7XG5cdFx0XHRcdHRoaXMuZWxlbWVudC5hZGRDbGFzcyggdGhpcy5vcHRpb25zLmFjdGl2ZUNsYXNzICk7XG5cdFx0XHR9XG5cdFx0fSxcblx0XHRfcmVtb3ZlQWN0aXZlQ2xhc3M6IGZ1bmN0aW9uKCkge1xuXHRcdFx0dGhpcy5fc3VwZXIoKTtcblx0XHRcdGlmICggdGhpcy5vcHRpb25zLmFjdGl2ZUNsYXNzICkge1xuXHRcdFx0XHR0aGlzLmVsZW1lbnQucmVtb3ZlQ2xhc3MoIHRoaXMub3B0aW9ucy5hY3RpdmVDbGFzcyApO1xuXHRcdFx0fVxuXHRcdH0sXG5cdFx0X2FkZEhvdmVyQ2xhc3M6IGZ1bmN0aW9uKCkge1xuXHRcdFx0dGhpcy5fc3VwZXIoKTtcblx0XHRcdGlmICggdGhpcy5vcHRpb25zLmhvdmVyQ2xhc3MgKSB7XG5cdFx0XHRcdHRoaXMuZWxlbWVudC5hZGRDbGFzcyggdGhpcy5vcHRpb25zLmhvdmVyQ2xhc3MgKTtcblx0XHRcdH1cblx0XHR9LFxuXHRcdF9yZW1vdmVIb3ZlckNsYXNzOiBmdW5jdGlvbigpIHtcblx0XHRcdHRoaXMuX3N1cGVyKCk7XG5cdFx0XHRpZiAoIHRoaXMub3B0aW9ucy5ob3ZlckNsYXNzICkge1xuXHRcdFx0XHR0aGlzLmVsZW1lbnQucmVtb3ZlQ2xhc3MoIHRoaXMub3B0aW9ucy5ob3ZlckNsYXNzICk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9ICk7XG59XG5cbnZhciB3aWRnZXRzRHJvcHBhYmxlID0gJC51aS5kcm9wcGFibGU7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgUHJvZ3Jlc3NiYXIgMS4xMi4xXG4gKiBodHRwOi8vanF1ZXJ5dWkuY29tXG4gKlxuICogQ29weXJpZ2h0IGpRdWVyeSBGb3VuZGF0aW9uIGFuZCBvdGhlciBjb250cmlidXRvcnNcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZS5cbiAqIGh0dHA6Ly9qcXVlcnkub3JnL2xpY2Vuc2VcbiAqL1xuXG4vLz4+bGFiZWw6IFByb2dyZXNzYmFyXG4vLz4+Z3JvdXA6IFdpZGdldHNcbi8vIGpzY3M6ZGlzYWJsZSBtYXhpbXVtTGluZUxlbmd0aFxuLy8+PmRlc2NyaXB0aW9uOiBEaXNwbGF5cyBhIHN0YXR1cyBpbmRpY2F0b3IgZm9yIGxvYWRpbmcgc3RhdGUsIHN0YW5kYXJkIHBlcmNlbnRhZ2UsIGFuZCBvdGhlciBwcm9ncmVzcyBpbmRpY2F0b3JzLlxuLy8ganNjczplbmFibGUgbWF4aW11bUxpbmVMZW5ndGhcbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9wcm9ncmVzc2Jhci9cbi8vPj5kZW1vczogaHR0cDovL2pxdWVyeXVpLmNvbS9wcm9ncmVzc2Jhci9cbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9jb3JlLmNzc1xuLy8+PmNzcy5zdHJ1Y3R1cmU6IC4uLy4uL3RoZW1lcy9iYXNlL3Byb2dyZXNzYmFyLmNzc1xuLy8+PmNzcy50aGVtZTogLi4vLi4vdGhlbWVzL2Jhc2UvdGhlbWUuY3NzXG5cblxuXG52YXIgd2lkZ2V0c1Byb2dyZXNzYmFyID0gJC53aWRnZXQoIFwidWkucHJvZ3Jlc3NiYXJcIiwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHRvcHRpb25zOiB7XG5cdFx0Y2xhc3Nlczoge1xuXHRcdFx0XCJ1aS1wcm9ncmVzc2JhclwiOiBcInVpLWNvcm5lci1hbGxcIixcblx0XHRcdFwidWktcHJvZ3Jlc3NiYXItdmFsdWVcIjogXCJ1aS1jb3JuZXItbGVmdFwiLFxuXHRcdFx0XCJ1aS1wcm9ncmVzc2Jhci1jb21wbGV0ZVwiOiBcInVpLWNvcm5lci1yaWdodFwiXG5cdFx0fSxcblx0XHRtYXg6IDEwMCxcblx0XHR2YWx1ZTogMCxcblxuXHRcdGNoYW5nZTogbnVsbCxcblx0XHRjb21wbGV0ZTogbnVsbFxuXHR9LFxuXG5cdG1pbjogMCxcblxuXHRfY3JlYXRlOiBmdW5jdGlvbigpIHtcblxuXHRcdC8vIENvbnN0cmFpbiBpbml0aWFsIHZhbHVlXG5cdFx0dGhpcy5vbGRWYWx1ZSA9IHRoaXMub3B0aW9ucy52YWx1ZSA9IHRoaXMuX2NvbnN0cmFpbmVkVmFsdWUoKTtcblxuXHRcdHRoaXMuZWxlbWVudC5hdHRyKCB7XG5cblx0XHRcdC8vIE9ubHkgc2V0IHN0YXRpYyB2YWx1ZXM7IGFyaWEtdmFsdWVub3cgYW5kIGFyaWEtdmFsdWVtYXggYXJlXG5cdFx0XHQvLyBzZXQgaW5zaWRlIF9yZWZyZXNoVmFsdWUoKVxuXHRcdFx0cm9sZTogXCJwcm9ncmVzc2JhclwiLFxuXHRcdFx0XCJhcmlhLXZhbHVlbWluXCI6IHRoaXMubWluXG5cdFx0fSApO1xuXHRcdHRoaXMuX2FkZENsYXNzKCBcInVpLXByb2dyZXNzYmFyXCIsIFwidWktd2lkZ2V0IHVpLXdpZGdldC1jb250ZW50XCIgKTtcblxuXHRcdHRoaXMudmFsdWVEaXYgPSAkKCBcIjxkaXY+XCIgKS5hcHBlbmRUbyggdGhpcy5lbGVtZW50ICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMudmFsdWVEaXYsIFwidWktcHJvZ3Jlc3NiYXItdmFsdWVcIiwgXCJ1aS13aWRnZXQtaGVhZGVyXCIgKTtcblx0XHR0aGlzLl9yZWZyZXNoVmFsdWUoKTtcblx0fSxcblxuXHRfZGVzdHJveTogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5lbGVtZW50LnJlbW92ZUF0dHIoIFwicm9sZSBhcmlhLXZhbHVlbWluIGFyaWEtdmFsdWVtYXggYXJpYS12YWx1ZW5vd1wiICk7XG5cblx0XHR0aGlzLnZhbHVlRGl2LnJlbW92ZSgpO1xuXHR9LFxuXG5cdHZhbHVlOiBmdW5jdGlvbiggbmV3VmFsdWUgKSB7XG5cdFx0aWYgKCBuZXdWYWx1ZSA9PT0gdW5kZWZpbmVkICkge1xuXHRcdFx0cmV0dXJuIHRoaXMub3B0aW9ucy52YWx1ZTtcblx0XHR9XG5cblx0XHR0aGlzLm9wdGlvbnMudmFsdWUgPSB0aGlzLl9jb25zdHJhaW5lZFZhbHVlKCBuZXdWYWx1ZSApO1xuXHRcdHRoaXMuX3JlZnJlc2hWYWx1ZSgpO1xuXHR9LFxuXG5cdF9jb25zdHJhaW5lZFZhbHVlOiBmdW5jdGlvbiggbmV3VmFsdWUgKSB7XG5cdFx0aWYgKCBuZXdWYWx1ZSA9PT0gdW5kZWZpbmVkICkge1xuXHRcdFx0bmV3VmFsdWUgPSB0aGlzLm9wdGlvbnMudmFsdWU7XG5cdFx0fVxuXG5cdFx0dGhpcy5pbmRldGVybWluYXRlID0gbmV3VmFsdWUgPT09IGZhbHNlO1xuXG5cdFx0Ly8gU2FuaXRpemUgdmFsdWVcblx0XHRpZiAoIHR5cGVvZiBuZXdWYWx1ZSAhPT0gXCJudW1iZXJcIiApIHtcblx0XHRcdG5ld1ZhbHVlID0gMDtcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcy5pbmRldGVybWluYXRlID8gZmFsc2UgOlxuXHRcdFx0TWF0aC5taW4oIHRoaXMub3B0aW9ucy5tYXgsIE1hdGgubWF4KCB0aGlzLm1pbiwgbmV3VmFsdWUgKSApO1xuXHR9LFxuXG5cdF9zZXRPcHRpb25zOiBmdW5jdGlvbiggb3B0aW9ucyApIHtcblxuXHRcdC8vIEVuc3VyZSBcInZhbHVlXCIgb3B0aW9uIGlzIHNldCBhZnRlciBvdGhlciB2YWx1ZXMgKGxpa2UgbWF4KVxuXHRcdHZhciB2YWx1ZSA9IG9wdGlvbnMudmFsdWU7XG5cdFx0ZGVsZXRlIG9wdGlvbnMudmFsdWU7XG5cblx0XHR0aGlzLl9zdXBlciggb3B0aW9ucyApO1xuXG5cdFx0dGhpcy5vcHRpb25zLnZhbHVlID0gdGhpcy5fY29uc3RyYWluZWRWYWx1ZSggdmFsdWUgKTtcblx0XHR0aGlzLl9yZWZyZXNoVmFsdWUoKTtcblx0fSxcblxuXHRfc2V0T3B0aW9uOiBmdW5jdGlvbigga2V5LCB2YWx1ZSApIHtcblx0XHRpZiAoIGtleSA9PT0gXCJtYXhcIiApIHtcblxuXHRcdFx0Ly8gRG9uJ3QgYWxsb3cgYSBtYXggbGVzcyB0aGFuIG1pblxuXHRcdFx0dmFsdWUgPSBNYXRoLm1heCggdGhpcy5taW4sIHZhbHVlICk7XG5cdFx0fVxuXHRcdHRoaXMuX3N1cGVyKCBrZXksIHZhbHVlICk7XG5cdH0sXG5cblx0X3NldE9wdGlvbkRpc2FibGVkOiBmdW5jdGlvbiggdmFsdWUgKSB7XG5cdFx0dGhpcy5fc3VwZXIoIHZhbHVlICk7XG5cblx0XHR0aGlzLmVsZW1lbnQuYXR0ciggXCJhcmlhLWRpc2FibGVkXCIsIHZhbHVlICk7XG5cdFx0dGhpcy5fdG9nZ2xlQ2xhc3MoIG51bGwsIFwidWktc3RhdGUtZGlzYWJsZWRcIiwgISF2YWx1ZSApO1xuXHR9LFxuXG5cdF9wZXJjZW50YWdlOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5pbmRldGVybWluYXRlID9cblx0XHRcdDEwMCA6XG5cdFx0XHQxMDAgKiAoIHRoaXMub3B0aW9ucy52YWx1ZSAtIHRoaXMubWluICkgLyAoIHRoaXMub3B0aW9ucy5tYXggLSB0aGlzLm1pbiApO1xuXHR9LFxuXG5cdF9yZWZyZXNoVmFsdWU6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciB2YWx1ZSA9IHRoaXMub3B0aW9ucy52YWx1ZSxcblx0XHRcdHBlcmNlbnRhZ2UgPSB0aGlzLl9wZXJjZW50YWdlKCk7XG5cblx0XHR0aGlzLnZhbHVlRGl2XG5cdFx0XHQudG9nZ2xlKCB0aGlzLmluZGV0ZXJtaW5hdGUgfHwgdmFsdWUgPiB0aGlzLm1pbiApXG5cdFx0XHQud2lkdGgoIHBlcmNlbnRhZ2UudG9GaXhlZCggMCApICsgXCIlXCIgKTtcblxuXHRcdHRoaXNcblx0XHRcdC5fdG9nZ2xlQ2xhc3MoIHRoaXMudmFsdWVEaXYsIFwidWktcHJvZ3Jlc3NiYXItY29tcGxldGVcIiwgbnVsbCxcblx0XHRcdFx0dmFsdWUgPT09IHRoaXMub3B0aW9ucy5tYXggKVxuXHRcdFx0Ll90b2dnbGVDbGFzcyggXCJ1aS1wcm9ncmVzc2Jhci1pbmRldGVybWluYXRlXCIsIG51bGwsIHRoaXMuaW5kZXRlcm1pbmF0ZSApO1xuXG5cdFx0aWYgKCB0aGlzLmluZGV0ZXJtaW5hdGUgKSB7XG5cdFx0XHR0aGlzLmVsZW1lbnQucmVtb3ZlQXR0ciggXCJhcmlhLXZhbHVlbm93XCIgKTtcblx0XHRcdGlmICggIXRoaXMub3ZlcmxheURpdiApIHtcblx0XHRcdFx0dGhpcy5vdmVybGF5RGl2ID0gJCggXCI8ZGl2PlwiICkuYXBwZW5kVG8oIHRoaXMudmFsdWVEaXYgKTtcblx0XHRcdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMub3ZlcmxheURpdiwgXCJ1aS1wcm9ncmVzc2Jhci1vdmVybGF5XCIgKTtcblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5lbGVtZW50LmF0dHIoIHtcblx0XHRcdFx0XCJhcmlhLXZhbHVlbWF4XCI6IHRoaXMub3B0aW9ucy5tYXgsXG5cdFx0XHRcdFwiYXJpYS12YWx1ZW5vd1wiOiB2YWx1ZVxuXHRcdFx0fSApO1xuXHRcdFx0aWYgKCB0aGlzLm92ZXJsYXlEaXYgKSB7XG5cdFx0XHRcdHRoaXMub3ZlcmxheURpdi5yZW1vdmUoKTtcblx0XHRcdFx0dGhpcy5vdmVybGF5RGl2ID0gbnVsbDtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoIHRoaXMub2xkVmFsdWUgIT09IHZhbHVlICkge1xuXHRcdFx0dGhpcy5vbGRWYWx1ZSA9IHZhbHVlO1xuXHRcdFx0dGhpcy5fdHJpZ2dlciggXCJjaGFuZ2VcIiApO1xuXHRcdH1cblx0XHRpZiAoIHZhbHVlID09PSB0aGlzLm9wdGlvbnMubWF4ICkge1xuXHRcdFx0dGhpcy5fdHJpZ2dlciggXCJjb21wbGV0ZVwiICk7XG5cdFx0fVxuXHR9XG59ICk7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgU2VsZWN0YWJsZSAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogU2VsZWN0YWJsZVxuLy8+Pmdyb3VwOiBJbnRlcmFjdGlvbnNcbi8vPj5kZXNjcmlwdGlvbjogQWxsb3dzIGdyb3VwcyBvZiBlbGVtZW50cyB0byBiZSBzZWxlY3RlZCB3aXRoIHRoZSBtb3VzZS5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9zZWxlY3RhYmxlL1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL3NlbGVjdGFibGUvXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2Uvc2VsZWN0YWJsZS5jc3NcblxuXG5cbnZhciB3aWRnZXRzU2VsZWN0YWJsZSA9ICQud2lkZ2V0KCBcInVpLnNlbGVjdGFibGVcIiwgJC51aS5tb3VzZSwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHRvcHRpb25zOiB7XG5cdFx0YXBwZW5kVG86IFwiYm9keVwiLFxuXHRcdGF1dG9SZWZyZXNoOiB0cnVlLFxuXHRcdGRpc3RhbmNlOiAwLFxuXHRcdGZpbHRlcjogXCIqXCIsXG5cdFx0dG9sZXJhbmNlOiBcInRvdWNoXCIsXG5cblx0XHQvLyBDYWxsYmFja3Ncblx0XHRzZWxlY3RlZDogbnVsbCxcblx0XHRzZWxlY3Rpbmc6IG51bGwsXG5cdFx0c3RhcnQ6IG51bGwsXG5cdFx0c3RvcDogbnVsbCxcblx0XHR1bnNlbGVjdGVkOiBudWxsLFxuXHRcdHVuc2VsZWN0aW5nOiBudWxsXG5cdH0sXG5cdF9jcmVhdGU6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciB0aGF0ID0gdGhpcztcblxuXHRcdHRoaXMuX2FkZENsYXNzKCBcInVpLXNlbGVjdGFibGVcIiApO1xuXG5cdFx0dGhpcy5kcmFnZ2VkID0gZmFsc2U7XG5cblx0XHQvLyBDYWNoZSBzZWxlY3RlZSBjaGlsZHJlbiBiYXNlZCBvbiBmaWx0ZXJcblx0XHR0aGlzLnJlZnJlc2ggPSBmdW5jdGlvbigpIHtcblx0XHRcdHRoYXQuZWxlbWVudFBvcyA9ICQoIHRoYXQuZWxlbWVudFsgMCBdICkub2Zmc2V0KCk7XG5cdFx0XHR0aGF0LnNlbGVjdGVlcyA9ICQoIHRoYXQub3B0aW9ucy5maWx0ZXIsIHRoYXQuZWxlbWVudFsgMCBdICk7XG5cdFx0XHR0aGF0Ll9hZGRDbGFzcyggdGhhdC5zZWxlY3RlZXMsIFwidWktc2VsZWN0ZWVcIiApO1xuXHRcdFx0dGhhdC5zZWxlY3RlZXMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHZhciAkdGhpcyA9ICQoIHRoaXMgKSxcblx0XHRcdFx0XHRzZWxlY3RlZU9mZnNldCA9ICR0aGlzLm9mZnNldCgpLFxuXHRcdFx0XHRcdHBvcyA9IHtcblx0XHRcdFx0XHRcdGxlZnQ6IHNlbGVjdGVlT2Zmc2V0LmxlZnQgLSB0aGF0LmVsZW1lbnRQb3MubGVmdCxcblx0XHRcdFx0XHRcdHRvcDogc2VsZWN0ZWVPZmZzZXQudG9wIC0gdGhhdC5lbGVtZW50UG9zLnRvcFxuXHRcdFx0XHRcdH07XG5cdFx0XHRcdCQuZGF0YSggdGhpcywgXCJzZWxlY3RhYmxlLWl0ZW1cIiwge1xuXHRcdFx0XHRcdGVsZW1lbnQ6IHRoaXMsXG5cdFx0XHRcdFx0JGVsZW1lbnQ6ICR0aGlzLFxuXHRcdFx0XHRcdGxlZnQ6IHBvcy5sZWZ0LFxuXHRcdFx0XHRcdHRvcDogcG9zLnRvcCxcblx0XHRcdFx0XHRyaWdodDogcG9zLmxlZnQgKyAkdGhpcy5vdXRlcldpZHRoKCksXG5cdFx0XHRcdFx0Ym90dG9tOiBwb3MudG9wICsgJHRoaXMub3V0ZXJIZWlnaHQoKSxcblx0XHRcdFx0XHRzdGFydHNlbGVjdGVkOiBmYWxzZSxcblx0XHRcdFx0XHRzZWxlY3RlZDogJHRoaXMuaGFzQ2xhc3MoIFwidWktc2VsZWN0ZWRcIiApLFxuXHRcdFx0XHRcdHNlbGVjdGluZzogJHRoaXMuaGFzQ2xhc3MoIFwidWktc2VsZWN0aW5nXCIgKSxcblx0XHRcdFx0XHR1bnNlbGVjdGluZzogJHRoaXMuaGFzQ2xhc3MoIFwidWktdW5zZWxlY3RpbmdcIiApXG5cdFx0XHRcdH0gKTtcblx0XHRcdH0gKTtcblx0XHR9O1xuXHRcdHRoaXMucmVmcmVzaCgpO1xuXG5cdFx0dGhpcy5fbW91c2VJbml0KCk7XG5cblx0XHR0aGlzLmhlbHBlciA9ICQoIFwiPGRpdj5cIiApO1xuXHRcdHRoaXMuX2FkZENsYXNzKCB0aGlzLmhlbHBlciwgXCJ1aS1zZWxlY3RhYmxlLWhlbHBlclwiICk7XG5cdH0sXG5cblx0X2Rlc3Ryb3k6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuc2VsZWN0ZWVzLnJlbW92ZURhdGEoIFwic2VsZWN0YWJsZS1pdGVtXCIgKTtcblx0XHR0aGlzLl9tb3VzZURlc3Ryb3koKTtcblx0fSxcblxuXHRfbW91c2VTdGFydDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHZhciB0aGF0ID0gdGhpcyxcblx0XHRcdG9wdGlvbnMgPSB0aGlzLm9wdGlvbnM7XG5cblx0XHR0aGlzLm9wb3MgPSBbIGV2ZW50LnBhZ2VYLCBldmVudC5wYWdlWSBdO1xuXHRcdHRoaXMuZWxlbWVudFBvcyA9ICQoIHRoaXMuZWxlbWVudFsgMCBdICkub2Zmc2V0KCk7XG5cblx0XHRpZiAoIHRoaXMub3B0aW9ucy5kaXNhYmxlZCApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHR0aGlzLnNlbGVjdGVlcyA9ICQoIG9wdGlvbnMuZmlsdGVyLCB0aGlzLmVsZW1lbnRbIDAgXSApO1xuXG5cdFx0dGhpcy5fdHJpZ2dlciggXCJzdGFydFwiLCBldmVudCApO1xuXG5cdFx0JCggb3B0aW9ucy5hcHBlbmRUbyApLmFwcGVuZCggdGhpcy5oZWxwZXIgKTtcblxuXHRcdC8vIHBvc2l0aW9uIGhlbHBlciAobGFzc28pXG5cdFx0dGhpcy5oZWxwZXIuY3NzKCB7XG5cdFx0XHRcImxlZnRcIjogZXZlbnQucGFnZVgsXG5cdFx0XHRcInRvcFwiOiBldmVudC5wYWdlWSxcblx0XHRcdFwid2lkdGhcIjogMCxcblx0XHRcdFwiaGVpZ2h0XCI6IDBcblx0XHR9ICk7XG5cblx0XHRpZiAoIG9wdGlvbnMuYXV0b1JlZnJlc2ggKSB7XG5cdFx0XHR0aGlzLnJlZnJlc2goKTtcblx0XHR9XG5cblx0XHR0aGlzLnNlbGVjdGVlcy5maWx0ZXIoIFwiLnVpLXNlbGVjdGVkXCIgKS5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdHZhciBzZWxlY3RlZSA9ICQuZGF0YSggdGhpcywgXCJzZWxlY3RhYmxlLWl0ZW1cIiApO1xuXHRcdFx0c2VsZWN0ZWUuc3RhcnRzZWxlY3RlZCA9IHRydWU7XG5cdFx0XHRpZiAoICFldmVudC5tZXRhS2V5ICYmICFldmVudC5jdHJsS2V5ICkge1xuXHRcdFx0XHR0aGF0Ll9yZW1vdmVDbGFzcyggc2VsZWN0ZWUuJGVsZW1lbnQsIFwidWktc2VsZWN0ZWRcIiApO1xuXHRcdFx0XHRzZWxlY3RlZS5zZWxlY3RlZCA9IGZhbHNlO1xuXHRcdFx0XHR0aGF0Ll9hZGRDbGFzcyggc2VsZWN0ZWUuJGVsZW1lbnQsIFwidWktdW5zZWxlY3RpbmdcIiApO1xuXHRcdFx0XHRzZWxlY3RlZS51bnNlbGVjdGluZyA9IHRydWU7XG5cblx0XHRcdFx0Ly8gc2VsZWN0YWJsZSBVTlNFTEVDVElORyBjYWxsYmFja1xuXHRcdFx0XHR0aGF0Ll90cmlnZ2VyKCBcInVuc2VsZWN0aW5nXCIsIGV2ZW50LCB7XG5cdFx0XHRcdFx0dW5zZWxlY3Rpbmc6IHNlbGVjdGVlLmVsZW1lbnRcblx0XHRcdFx0fSApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHRcdCQoIGV2ZW50LnRhcmdldCApLnBhcmVudHMoKS5hZGRCYWNrKCkuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgZG9TZWxlY3QsXG5cdFx0XHRcdHNlbGVjdGVlID0gJC5kYXRhKCB0aGlzLCBcInNlbGVjdGFibGUtaXRlbVwiICk7XG5cdFx0XHRpZiAoIHNlbGVjdGVlICkge1xuXHRcdFx0XHRkb1NlbGVjdCA9ICggIWV2ZW50Lm1ldGFLZXkgJiYgIWV2ZW50LmN0cmxLZXkgKSB8fFxuXHRcdFx0XHRcdCFzZWxlY3RlZS4kZWxlbWVudC5oYXNDbGFzcyggXCJ1aS1zZWxlY3RlZFwiICk7XG5cdFx0XHRcdHRoYXQuX3JlbW92ZUNsYXNzKCBzZWxlY3RlZS4kZWxlbWVudCwgZG9TZWxlY3QgPyBcInVpLXVuc2VsZWN0aW5nXCIgOiBcInVpLXNlbGVjdGVkXCIgKVxuXHRcdFx0XHRcdC5fYWRkQ2xhc3MoIHNlbGVjdGVlLiRlbGVtZW50LCBkb1NlbGVjdCA/IFwidWktc2VsZWN0aW5nXCIgOiBcInVpLXVuc2VsZWN0aW5nXCIgKTtcblx0XHRcdFx0c2VsZWN0ZWUudW5zZWxlY3RpbmcgPSAhZG9TZWxlY3Q7XG5cdFx0XHRcdHNlbGVjdGVlLnNlbGVjdGluZyA9IGRvU2VsZWN0O1xuXHRcdFx0XHRzZWxlY3RlZS5zZWxlY3RlZCA9IGRvU2VsZWN0O1xuXG5cdFx0XHRcdC8vIHNlbGVjdGFibGUgKFVOKVNFTEVDVElORyBjYWxsYmFja1xuXHRcdFx0XHRpZiAoIGRvU2VsZWN0ICkge1xuXHRcdFx0XHRcdHRoYXQuX3RyaWdnZXIoIFwic2VsZWN0aW5nXCIsIGV2ZW50LCB7XG5cdFx0XHRcdFx0XHRzZWxlY3Rpbmc6IHNlbGVjdGVlLmVsZW1lbnRcblx0XHRcdFx0XHR9ICk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0dGhhdC5fdHJpZ2dlciggXCJ1bnNlbGVjdGluZ1wiLCBldmVudCwge1xuXHRcdFx0XHRcdFx0dW5zZWxlY3Rpbmc6IHNlbGVjdGVlLmVsZW1lbnRcblx0XHRcdFx0XHR9ICk7XG5cdFx0XHRcdH1cblx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHR9LFxuXG5cdF9tb3VzZURyYWc6IGZ1bmN0aW9uKCBldmVudCApIHtcblxuXHRcdHRoaXMuZHJhZ2dlZCA9IHRydWU7XG5cblx0XHRpZiAoIHRoaXMub3B0aW9ucy5kaXNhYmxlZCApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHR2YXIgdG1wLFxuXHRcdFx0dGhhdCA9IHRoaXMsXG5cdFx0XHRvcHRpb25zID0gdGhpcy5vcHRpb25zLFxuXHRcdFx0eDEgPSB0aGlzLm9wb3NbIDAgXSxcblx0XHRcdHkxID0gdGhpcy5vcG9zWyAxIF0sXG5cdFx0XHR4MiA9IGV2ZW50LnBhZ2VYLFxuXHRcdFx0eTIgPSBldmVudC5wYWdlWTtcblxuXHRcdGlmICggeDEgPiB4MiApIHsgdG1wID0geDI7IHgyID0geDE7IHgxID0gdG1wOyB9XG5cdFx0aWYgKCB5MSA+IHkyICkgeyB0bXAgPSB5MjsgeTIgPSB5MTsgeTEgPSB0bXA7IH1cblx0XHR0aGlzLmhlbHBlci5jc3MoIHsgbGVmdDogeDEsIHRvcDogeTEsIHdpZHRoOiB4MiAtIHgxLCBoZWlnaHQ6IHkyIC0geTEgfSApO1xuXG5cdFx0dGhpcy5zZWxlY3RlZXMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgc2VsZWN0ZWUgPSAkLmRhdGEoIHRoaXMsIFwic2VsZWN0YWJsZS1pdGVtXCIgKSxcblx0XHRcdFx0aGl0ID0gZmFsc2UsXG5cdFx0XHRcdG9mZnNldCA9IHt9O1xuXG5cdFx0XHQvL3ByZXZlbnQgaGVscGVyIGZyb20gYmVpbmcgc2VsZWN0ZWQgaWYgYXBwZW5kVG86IHNlbGVjdGFibGVcblx0XHRcdGlmICggIXNlbGVjdGVlIHx8IHNlbGVjdGVlLmVsZW1lbnQgPT09IHRoYXQuZWxlbWVudFsgMCBdICkge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdG9mZnNldC5sZWZ0ICAgPSBzZWxlY3RlZS5sZWZ0ICAgKyB0aGF0LmVsZW1lbnRQb3MubGVmdDtcblx0XHRcdG9mZnNldC5yaWdodCAgPSBzZWxlY3RlZS5yaWdodCAgKyB0aGF0LmVsZW1lbnRQb3MubGVmdDtcblx0XHRcdG9mZnNldC50b3AgICAgPSBzZWxlY3RlZS50b3AgICAgKyB0aGF0LmVsZW1lbnRQb3MudG9wO1xuXHRcdFx0b2Zmc2V0LmJvdHRvbSA9IHNlbGVjdGVlLmJvdHRvbSArIHRoYXQuZWxlbWVudFBvcy50b3A7XG5cblx0XHRcdGlmICggb3B0aW9ucy50b2xlcmFuY2UgPT09IFwidG91Y2hcIiApIHtcblx0XHRcdFx0aGl0ID0gKCAhKCBvZmZzZXQubGVmdCA+IHgyIHx8IG9mZnNldC5yaWdodCA8IHgxIHx8IG9mZnNldC50b3AgPiB5MiB8fFxuICAgICAgICAgICAgICAgICAgICBvZmZzZXQuYm90dG9tIDwgeTEgKSApO1xuXHRcdFx0fSBlbHNlIGlmICggb3B0aW9ucy50b2xlcmFuY2UgPT09IFwiZml0XCIgKSB7XG5cdFx0XHRcdGhpdCA9ICggb2Zmc2V0LmxlZnQgPiB4MSAmJiBvZmZzZXQucmlnaHQgPCB4MiAmJiBvZmZzZXQudG9wID4geTEgJiZcbiAgICAgICAgICAgICAgICAgICAgb2Zmc2V0LmJvdHRvbSA8IHkyICk7XG5cdFx0XHR9XG5cblx0XHRcdGlmICggaGl0ICkge1xuXG5cdFx0XHRcdC8vIFNFTEVDVFxuXHRcdFx0XHRpZiAoIHNlbGVjdGVlLnNlbGVjdGVkICkge1xuXHRcdFx0XHRcdHRoYXQuX3JlbW92ZUNsYXNzKCBzZWxlY3RlZS4kZWxlbWVudCwgXCJ1aS1zZWxlY3RlZFwiICk7XG5cdFx0XHRcdFx0c2VsZWN0ZWUuc2VsZWN0ZWQgPSBmYWxzZTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoIHNlbGVjdGVlLnVuc2VsZWN0aW5nICkge1xuXHRcdFx0XHRcdHRoYXQuX3JlbW92ZUNsYXNzKCBzZWxlY3RlZS4kZWxlbWVudCwgXCJ1aS11bnNlbGVjdGluZ1wiICk7XG5cdFx0XHRcdFx0c2VsZWN0ZWUudW5zZWxlY3RpbmcgPSBmYWxzZTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoICFzZWxlY3RlZS5zZWxlY3RpbmcgKSB7XG5cdFx0XHRcdFx0dGhhdC5fYWRkQ2xhc3MoIHNlbGVjdGVlLiRlbGVtZW50LCBcInVpLXNlbGVjdGluZ1wiICk7XG5cdFx0XHRcdFx0c2VsZWN0ZWUuc2VsZWN0aW5nID0gdHJ1ZTtcblxuXHRcdFx0XHRcdC8vIHNlbGVjdGFibGUgU0VMRUNUSU5HIGNhbGxiYWNrXG5cdFx0XHRcdFx0dGhhdC5fdHJpZ2dlciggXCJzZWxlY3RpbmdcIiwgZXZlbnQsIHtcblx0XHRcdFx0XHRcdHNlbGVjdGluZzogc2VsZWN0ZWUuZWxlbWVudFxuXHRcdFx0XHRcdH0gKTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBVTlNFTEVDVFxuXHRcdFx0XHRpZiAoIHNlbGVjdGVlLnNlbGVjdGluZyApIHtcblx0XHRcdFx0XHRpZiAoICggZXZlbnQubWV0YUtleSB8fCBldmVudC5jdHJsS2V5ICkgJiYgc2VsZWN0ZWUuc3RhcnRzZWxlY3RlZCApIHtcblx0XHRcdFx0XHRcdHRoYXQuX3JlbW92ZUNsYXNzKCBzZWxlY3RlZS4kZWxlbWVudCwgXCJ1aS1zZWxlY3RpbmdcIiApO1xuXHRcdFx0XHRcdFx0c2VsZWN0ZWUuc2VsZWN0aW5nID0gZmFsc2U7XG5cdFx0XHRcdFx0XHR0aGF0Ll9hZGRDbGFzcyggc2VsZWN0ZWUuJGVsZW1lbnQsIFwidWktc2VsZWN0ZWRcIiApO1xuXHRcdFx0XHRcdFx0c2VsZWN0ZWUuc2VsZWN0ZWQgPSB0cnVlO1xuXHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHR0aGF0Ll9yZW1vdmVDbGFzcyggc2VsZWN0ZWUuJGVsZW1lbnQsIFwidWktc2VsZWN0aW5nXCIgKTtcblx0XHRcdFx0XHRcdHNlbGVjdGVlLnNlbGVjdGluZyA9IGZhbHNlO1xuXHRcdFx0XHRcdFx0aWYgKCBzZWxlY3RlZS5zdGFydHNlbGVjdGVkICkge1xuXHRcdFx0XHRcdFx0XHR0aGF0Ll9hZGRDbGFzcyggc2VsZWN0ZWUuJGVsZW1lbnQsIFwidWktdW5zZWxlY3RpbmdcIiApO1xuXHRcdFx0XHRcdFx0XHRzZWxlY3RlZS51bnNlbGVjdGluZyA9IHRydWU7XG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdC8vIHNlbGVjdGFibGUgVU5TRUxFQ1RJTkcgY2FsbGJhY2tcblx0XHRcdFx0XHRcdHRoYXQuX3RyaWdnZXIoIFwidW5zZWxlY3RpbmdcIiwgZXZlbnQsIHtcblx0XHRcdFx0XHRcdFx0dW5zZWxlY3Rpbmc6IHNlbGVjdGVlLmVsZW1lbnRcblx0XHRcdFx0XHRcdH0gKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKCBzZWxlY3RlZS5zZWxlY3RlZCApIHtcblx0XHRcdFx0XHRpZiAoICFldmVudC5tZXRhS2V5ICYmICFldmVudC5jdHJsS2V5ICYmICFzZWxlY3RlZS5zdGFydHNlbGVjdGVkICkge1xuXHRcdFx0XHRcdFx0dGhhdC5fcmVtb3ZlQ2xhc3MoIHNlbGVjdGVlLiRlbGVtZW50LCBcInVpLXNlbGVjdGVkXCIgKTtcblx0XHRcdFx0XHRcdHNlbGVjdGVlLnNlbGVjdGVkID0gZmFsc2U7XG5cblx0XHRcdFx0XHRcdHRoYXQuX2FkZENsYXNzKCBzZWxlY3RlZS4kZWxlbWVudCwgXCJ1aS11bnNlbGVjdGluZ1wiICk7XG5cdFx0XHRcdFx0XHRzZWxlY3RlZS51bnNlbGVjdGluZyA9IHRydWU7XG5cblx0XHRcdFx0XHRcdC8vIHNlbGVjdGFibGUgVU5TRUxFQ1RJTkcgY2FsbGJhY2tcblx0XHRcdFx0XHRcdHRoYXQuX3RyaWdnZXIoIFwidW5zZWxlY3RpbmdcIiwgZXZlbnQsIHtcblx0XHRcdFx0XHRcdFx0dW5zZWxlY3Rpbmc6IHNlbGVjdGVlLmVsZW1lbnRcblx0XHRcdFx0XHRcdH0gKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9ICk7XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0X21vdXNlU3RvcDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHZhciB0aGF0ID0gdGhpcztcblxuXHRcdHRoaXMuZHJhZ2dlZCA9IGZhbHNlO1xuXG5cdFx0JCggXCIudWktdW5zZWxlY3RpbmdcIiwgdGhpcy5lbGVtZW50WyAwIF0gKS5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdHZhciBzZWxlY3RlZSA9ICQuZGF0YSggdGhpcywgXCJzZWxlY3RhYmxlLWl0ZW1cIiApO1xuXHRcdFx0dGhhdC5fcmVtb3ZlQ2xhc3MoIHNlbGVjdGVlLiRlbGVtZW50LCBcInVpLXVuc2VsZWN0aW5nXCIgKTtcblx0XHRcdHNlbGVjdGVlLnVuc2VsZWN0aW5nID0gZmFsc2U7XG5cdFx0XHRzZWxlY3RlZS5zdGFydHNlbGVjdGVkID0gZmFsc2U7XG5cdFx0XHR0aGF0Ll90cmlnZ2VyKCBcInVuc2VsZWN0ZWRcIiwgZXZlbnQsIHtcblx0XHRcdFx0dW5zZWxlY3RlZDogc2VsZWN0ZWUuZWxlbWVudFxuXHRcdFx0fSApO1xuXHRcdH0gKTtcblx0XHQkKCBcIi51aS1zZWxlY3RpbmdcIiwgdGhpcy5lbGVtZW50WyAwIF0gKS5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdHZhciBzZWxlY3RlZSA9ICQuZGF0YSggdGhpcywgXCJzZWxlY3RhYmxlLWl0ZW1cIiApO1xuXHRcdFx0dGhhdC5fcmVtb3ZlQ2xhc3MoIHNlbGVjdGVlLiRlbGVtZW50LCBcInVpLXNlbGVjdGluZ1wiIClcblx0XHRcdFx0Ll9hZGRDbGFzcyggc2VsZWN0ZWUuJGVsZW1lbnQsIFwidWktc2VsZWN0ZWRcIiApO1xuXHRcdFx0c2VsZWN0ZWUuc2VsZWN0aW5nID0gZmFsc2U7XG5cdFx0XHRzZWxlY3RlZS5zZWxlY3RlZCA9IHRydWU7XG5cdFx0XHRzZWxlY3RlZS5zdGFydHNlbGVjdGVkID0gdHJ1ZTtcblx0XHRcdHRoYXQuX3RyaWdnZXIoIFwic2VsZWN0ZWRcIiwgZXZlbnQsIHtcblx0XHRcdFx0c2VsZWN0ZWQ6IHNlbGVjdGVlLmVsZW1lbnRcblx0XHRcdH0gKTtcblx0XHR9ICk7XG5cdFx0dGhpcy5fdHJpZ2dlciggXCJzdG9wXCIsIGV2ZW50ICk7XG5cblx0XHR0aGlzLmhlbHBlci5yZW1vdmUoKTtcblxuXHRcdHJldHVybiBmYWxzZTtcblx0fVxuXG59ICk7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgU2VsZWN0bWVudSAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogU2VsZWN0bWVudVxuLy8+Pmdyb3VwOiBXaWRnZXRzXG4vLyBqc2NzOmRpc2FibGUgbWF4aW11bUxpbmVMZW5ndGhcbi8vPj5kZXNjcmlwdGlvbjogRHVwbGljYXRlcyBhbmQgZXh0ZW5kcyB0aGUgZnVuY3Rpb25hbGl0eSBvZiBhIG5hdGl2ZSBIVE1MIHNlbGVjdCBlbGVtZW50LCBhbGxvd2luZyBpdCB0byBiZSBjdXN0b21pemFibGUgaW4gYmVoYXZpb3IgYW5kIGFwcGVhcmFuY2UgZmFyIGJleW9uZCB0aGUgbGltaXRhdGlvbnMgb2YgYSBuYXRpdmUgc2VsZWN0LlxuLy8ganNjczplbmFibGUgbWF4aW11bUxpbmVMZW5ndGhcbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9zZWxlY3RtZW51L1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL3NlbGVjdG1lbnUvXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2UvY29yZS5jc3Ncbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9zZWxlY3RtZW51LmNzcywgLi4vLi4vdGhlbWVzL2Jhc2UvYnV0dG9uLmNzc1xuLy8+PmNzcy50aGVtZTogLi4vLi4vdGhlbWVzL2Jhc2UvdGhlbWUuY3NzXG5cblxuXG52YXIgd2lkZ2V0c1NlbGVjdG1lbnUgPSAkLndpZGdldCggXCJ1aS5zZWxlY3RtZW51XCIsIFsgJC51aS5mb3JtUmVzZXRNaXhpbiwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHRkZWZhdWx0RWxlbWVudDogXCI8c2VsZWN0PlwiLFxuXHRvcHRpb25zOiB7XG5cdFx0YXBwZW5kVG86IG51bGwsXG5cdFx0Y2xhc3Nlczoge1xuXHRcdFx0XCJ1aS1zZWxlY3RtZW51LWJ1dHRvbi1vcGVuXCI6IFwidWktY29ybmVyLXRvcFwiLFxuXHRcdFx0XCJ1aS1zZWxlY3RtZW51LWJ1dHRvbi1jbG9zZWRcIjogXCJ1aS1jb3JuZXItYWxsXCJcblx0XHR9LFxuXHRcdGRpc2FibGVkOiBudWxsLFxuXHRcdGljb25zOiB7XG5cdFx0XHRidXR0b246IFwidWktaWNvbi10cmlhbmdsZS0xLXNcIlxuXHRcdH0sXG5cdFx0cG9zaXRpb246IHtcblx0XHRcdG15OiBcImxlZnQgdG9wXCIsXG5cdFx0XHRhdDogXCJsZWZ0IGJvdHRvbVwiLFxuXHRcdFx0Y29sbGlzaW9uOiBcIm5vbmVcIlxuXHRcdH0sXG5cdFx0d2lkdGg6IGZhbHNlLFxuXG5cdFx0Ly8gQ2FsbGJhY2tzXG5cdFx0Y2hhbmdlOiBudWxsLFxuXHRcdGNsb3NlOiBudWxsLFxuXHRcdGZvY3VzOiBudWxsLFxuXHRcdG9wZW46IG51bGwsXG5cdFx0c2VsZWN0OiBudWxsXG5cdH0sXG5cblx0X2NyZWF0ZTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHNlbGVjdG1lbnVJZCA9IHRoaXMuZWxlbWVudC51bmlxdWVJZCgpLmF0dHIoIFwiaWRcIiApO1xuXHRcdHRoaXMuaWRzID0ge1xuXHRcdFx0ZWxlbWVudDogc2VsZWN0bWVudUlkLFxuXHRcdFx0YnV0dG9uOiBzZWxlY3RtZW51SWQgKyBcIi1idXR0b25cIixcblx0XHRcdG1lbnU6IHNlbGVjdG1lbnVJZCArIFwiLW1lbnVcIlxuXHRcdH07XG5cblx0XHR0aGlzLl9kcmF3QnV0dG9uKCk7XG5cdFx0dGhpcy5fZHJhd01lbnUoKTtcblx0XHR0aGlzLl9iaW5kRm9ybVJlc2V0SGFuZGxlcigpO1xuXG5cdFx0dGhpcy5fcmVuZGVyZWQgPSBmYWxzZTtcblx0XHR0aGlzLm1lbnVJdGVtcyA9ICQoKTtcblx0fSxcblxuXHRfZHJhd0J1dHRvbjogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGljb24sXG5cdFx0XHR0aGF0ID0gdGhpcyxcblx0XHRcdGl0ZW0gPSB0aGlzLl9wYXJzZU9wdGlvbihcblx0XHRcdFx0dGhpcy5lbGVtZW50LmZpbmQoIFwib3B0aW9uOnNlbGVjdGVkXCIgKSxcblx0XHRcdFx0dGhpcy5lbGVtZW50WyAwIF0uc2VsZWN0ZWRJbmRleFxuXHRcdFx0KTtcblxuXHRcdC8vIEFzc29jaWF0ZSBleGlzdGluZyBsYWJlbCB3aXRoIHRoZSBuZXcgYnV0dG9uXG5cdFx0dGhpcy5sYWJlbHMgPSB0aGlzLmVsZW1lbnQubGFiZWxzKCkuYXR0ciggXCJmb3JcIiwgdGhpcy5pZHMuYnV0dG9uICk7XG5cdFx0dGhpcy5fb24oIHRoaXMubGFiZWxzLCB7XG5cdFx0XHRjbGljazogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0XHR0aGlzLmJ1dHRvbi5mb2N1cygpO1xuXHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHRcdC8vIEhpZGUgb3JpZ2luYWwgc2VsZWN0IGVsZW1lbnRcblx0XHR0aGlzLmVsZW1lbnQuaGlkZSgpO1xuXG5cdFx0Ly8gQ3JlYXRlIGJ1dHRvblxuXHRcdHRoaXMuYnV0dG9uID0gJCggXCI8c3Bhbj5cIiwge1xuXHRcdFx0dGFiaW5kZXg6IHRoaXMub3B0aW9ucy5kaXNhYmxlZCA/IC0xIDogMCxcblx0XHRcdGlkOiB0aGlzLmlkcy5idXR0b24sXG5cdFx0XHRyb2xlOiBcImNvbWJvYm94XCIsXG5cdFx0XHRcImFyaWEtZXhwYW5kZWRcIjogXCJmYWxzZVwiLFxuXHRcdFx0XCJhcmlhLWF1dG9jb21wbGV0ZVwiOiBcImxpc3RcIixcblx0XHRcdFwiYXJpYS1vd25zXCI6IHRoaXMuaWRzLm1lbnUsXG5cdFx0XHRcImFyaWEtaGFzcG9wdXBcIjogXCJ0cnVlXCIsXG5cdFx0XHR0aXRsZTogdGhpcy5lbGVtZW50LmF0dHIoIFwidGl0bGVcIiApXG5cdFx0fSApXG5cdFx0XHQuaW5zZXJ0QWZ0ZXIoIHRoaXMuZWxlbWVudCApO1xuXG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMuYnV0dG9uLCBcInVpLXNlbGVjdG1lbnUtYnV0dG9uIHVpLXNlbGVjdG1lbnUtYnV0dG9uLWNsb3NlZFwiLFxuXHRcdFx0XCJ1aS1idXR0b24gdWktd2lkZ2V0XCIgKTtcblxuXHRcdGljb24gPSAkKCBcIjxzcGFuPlwiICkuYXBwZW5kVG8oIHRoaXMuYnV0dG9uICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIGljb24sIFwidWktc2VsZWN0bWVudS1pY29uXCIsIFwidWktaWNvbiBcIiArIHRoaXMub3B0aW9ucy5pY29ucy5idXR0b24gKTtcblx0XHR0aGlzLmJ1dHRvbkl0ZW0gPSB0aGlzLl9yZW5kZXJCdXR0b25JdGVtKCBpdGVtIClcblx0XHRcdC5hcHBlbmRUbyggdGhpcy5idXR0b24gKTtcblxuXHRcdGlmICggdGhpcy5vcHRpb25zLndpZHRoICE9PSBmYWxzZSApIHtcblx0XHRcdHRoaXMuX3Jlc2l6ZUJ1dHRvbigpO1xuXHRcdH1cblxuXHRcdHRoaXMuX29uKCB0aGlzLmJ1dHRvbiwgdGhpcy5fYnV0dG9uRXZlbnRzICk7XG5cdFx0dGhpcy5idXR0b24ub25lKCBcImZvY3VzaW5cIiwgZnVuY3Rpb24oKSB7XG5cblx0XHRcdC8vIERlbGF5IHJlbmRlcmluZyB0aGUgbWVudSBpdGVtcyB1bnRpbCB0aGUgYnV0dG9uIHJlY2VpdmVzIGZvY3VzLlxuXHRcdFx0Ly8gVGhlIG1lbnUgbWF5IGhhdmUgYWxyZWFkeSBiZWVuIHJlbmRlcmVkIHZpYSBhIHByb2dyYW1tYXRpYyBvcGVuLlxuXHRcdFx0aWYgKCAhdGhhdC5fcmVuZGVyZWQgKSB7XG5cdFx0XHRcdHRoYXQuX3JlZnJlc2hNZW51KCk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXHR9LFxuXG5cdF9kcmF3TWVudTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHRoYXQgPSB0aGlzO1xuXG5cdFx0Ly8gQ3JlYXRlIG1lbnVcblx0XHR0aGlzLm1lbnUgPSAkKCBcIjx1bD5cIiwge1xuXHRcdFx0XCJhcmlhLWhpZGRlblwiOiBcInRydWVcIixcblx0XHRcdFwiYXJpYS1sYWJlbGxlZGJ5XCI6IHRoaXMuaWRzLmJ1dHRvbixcblx0XHRcdGlkOiB0aGlzLmlkcy5tZW51XG5cdFx0fSApO1xuXG5cdFx0Ly8gV3JhcCBtZW51XG5cdFx0dGhpcy5tZW51V3JhcCA9ICQoIFwiPGRpdj5cIiApLmFwcGVuZCggdGhpcy5tZW51ICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMubWVudVdyYXAsIFwidWktc2VsZWN0bWVudS1tZW51XCIsIFwidWktZnJvbnRcIiApO1xuXHRcdHRoaXMubWVudVdyYXAuYXBwZW5kVG8oIHRoaXMuX2FwcGVuZFRvKCkgKTtcblxuXHRcdC8vIEluaXRpYWxpemUgbWVudSB3aWRnZXRcblx0XHR0aGlzLm1lbnVJbnN0YW5jZSA9IHRoaXMubWVudVxuXHRcdFx0Lm1lbnUoIHtcblx0XHRcdFx0Y2xhc3Nlczoge1xuXHRcdFx0XHRcdFwidWktbWVudVwiOiBcInVpLWNvcm5lci1ib3R0b21cIlxuXHRcdFx0XHR9LFxuXHRcdFx0XHRyb2xlOiBcImxpc3Rib3hcIixcblx0XHRcdFx0c2VsZWN0OiBmdW5jdGlvbiggZXZlbnQsIHVpICkge1xuXHRcdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cblx0XHRcdFx0XHQvLyBTdXBwb3J0OiBJRThcblx0XHRcdFx0XHQvLyBJZiB0aGUgaXRlbSB3YXMgc2VsZWN0ZWQgdmlhIGEgY2xpY2ssIHRoZSB0ZXh0IHNlbGVjdGlvblxuXHRcdFx0XHRcdC8vIHdpbGwgYmUgZGVzdHJveWVkIGluIElFXG5cdFx0XHRcdFx0dGhhdC5fc2V0U2VsZWN0aW9uKCk7XG5cblx0XHRcdFx0XHR0aGF0Ll9zZWxlY3QoIHVpLml0ZW0uZGF0YSggXCJ1aS1zZWxlY3RtZW51LWl0ZW1cIiApLCBldmVudCApO1xuXHRcdFx0XHR9LFxuXHRcdFx0XHRmb2N1czogZnVuY3Rpb24oIGV2ZW50LCB1aSApIHtcblx0XHRcdFx0XHR2YXIgaXRlbSA9IHVpLml0ZW0uZGF0YSggXCJ1aS1zZWxlY3RtZW51LWl0ZW1cIiApO1xuXG5cdFx0XHRcdFx0Ly8gUHJldmVudCBpbml0YWwgZm9jdXMgZnJvbSBmaXJpbmcgYW5kIGNoZWNrIGlmIGl0cyBhIG5ld2x5IGZvY3VzZWQgaXRlbVxuXHRcdFx0XHRcdGlmICggdGhhdC5mb2N1c0luZGV4ICE9IG51bGwgJiYgaXRlbS5pbmRleCAhPT0gdGhhdC5mb2N1c0luZGV4ICkge1xuXHRcdFx0XHRcdFx0dGhhdC5fdHJpZ2dlciggXCJmb2N1c1wiLCBldmVudCwgeyBpdGVtOiBpdGVtIH0gKTtcblx0XHRcdFx0XHRcdGlmICggIXRoYXQuaXNPcGVuICkge1xuXHRcdFx0XHRcdFx0XHR0aGF0Ll9zZWxlY3QoIGl0ZW0sIGV2ZW50ICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdHRoYXQuZm9jdXNJbmRleCA9IGl0ZW0uaW5kZXg7XG5cblx0XHRcdFx0XHR0aGF0LmJ1dHRvbi5hdHRyKCBcImFyaWEtYWN0aXZlZGVzY2VuZGFudFwiLFxuXHRcdFx0XHRcdFx0dGhhdC5tZW51SXRlbXMuZXEoIGl0ZW0uaW5kZXggKS5hdHRyKCBcImlkXCIgKSApO1xuXHRcdFx0XHR9XG5cdFx0XHR9IClcblx0XHRcdC5tZW51KCBcImluc3RhbmNlXCIgKTtcblxuXHRcdC8vIERvbid0IGNsb3NlIHRoZSBtZW51IG9uIG1vdXNlbGVhdmVcblx0XHR0aGlzLm1lbnVJbnN0YW5jZS5fb2ZmKCB0aGlzLm1lbnUsIFwibW91c2VsZWF2ZVwiICk7XG5cblx0XHQvLyBDYW5jZWwgdGhlIG1lbnUncyBjb2xsYXBzZUFsbCBvbiBkb2N1bWVudCBjbGlja1xuXHRcdHRoaXMubWVudUluc3RhbmNlLl9jbG9zZU9uRG9jdW1lbnRDbGljayA9IGZ1bmN0aW9uKCkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH07XG5cblx0XHQvLyBTZWxlY3RzIG9mdGVuIGNvbnRhaW4gZW1wdHkgaXRlbXMsIGJ1dCBuZXZlciBjb250YWluIGRpdmlkZXJzXG5cdFx0dGhpcy5tZW51SW5zdGFuY2UuX2lzRGl2aWRlciA9IGZ1bmN0aW9uKCkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH07XG5cdH0sXG5cblx0cmVmcmVzaDogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fcmVmcmVzaE1lbnUoKTtcblx0XHR0aGlzLmJ1dHRvbkl0ZW0ucmVwbGFjZVdpdGgoXG5cdFx0XHR0aGlzLmJ1dHRvbkl0ZW0gPSB0aGlzLl9yZW5kZXJCdXR0b25JdGVtKFxuXG5cdFx0XHRcdC8vIEZhbGwgYmFjayB0byBhbiBlbXB0eSBvYmplY3QgaW4gY2FzZSB0aGVyZSBhcmUgbm8gb3B0aW9uc1xuXHRcdFx0XHR0aGlzLl9nZXRTZWxlY3RlZEl0ZW0oKS5kYXRhKCBcInVpLXNlbGVjdG1lbnUtaXRlbVwiICkgfHwge31cblx0XHRcdClcblx0XHQpO1xuXHRcdGlmICggdGhpcy5vcHRpb25zLndpZHRoID09PSBudWxsICkge1xuXHRcdFx0dGhpcy5fcmVzaXplQnV0dG9uKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9yZWZyZXNoTWVudTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGl0ZW0sXG5cdFx0XHRvcHRpb25zID0gdGhpcy5lbGVtZW50LmZpbmQoIFwib3B0aW9uXCIgKTtcblxuXHRcdHRoaXMubWVudS5lbXB0eSgpO1xuXG5cdFx0dGhpcy5fcGFyc2VPcHRpb25zKCBvcHRpb25zICk7XG5cdFx0dGhpcy5fcmVuZGVyTWVudSggdGhpcy5tZW51LCB0aGlzLml0ZW1zICk7XG5cblx0XHR0aGlzLm1lbnVJbnN0YW5jZS5yZWZyZXNoKCk7XG5cdFx0dGhpcy5tZW51SXRlbXMgPSB0aGlzLm1lbnUuZmluZCggXCJsaVwiIClcblx0XHRcdC5ub3QoIFwiLnVpLXNlbGVjdG1lbnUtb3B0Z3JvdXBcIiApXG5cdFx0XHRcdC5maW5kKCBcIi51aS1tZW51LWl0ZW0td3JhcHBlclwiICk7XG5cblx0XHR0aGlzLl9yZW5kZXJlZCA9IHRydWU7XG5cblx0XHRpZiAoICFvcHRpb25zLmxlbmd0aCApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpdGVtID0gdGhpcy5fZ2V0U2VsZWN0ZWRJdGVtKCk7XG5cblx0XHQvLyBVcGRhdGUgdGhlIG1lbnUgdG8gaGF2ZSB0aGUgY29ycmVjdCBpdGVtIGZvY3VzZWRcblx0XHR0aGlzLm1lbnVJbnN0YW5jZS5mb2N1cyggbnVsbCwgaXRlbSApO1xuXHRcdHRoaXMuX3NldEFyaWEoIGl0ZW0uZGF0YSggXCJ1aS1zZWxlY3RtZW51LWl0ZW1cIiApICk7XG5cblx0XHQvLyBTZXQgZGlzYWJsZWQgc3RhdGVcblx0XHR0aGlzLl9zZXRPcHRpb24oIFwiZGlzYWJsZWRcIiwgdGhpcy5lbGVtZW50LnByb3AoIFwiZGlzYWJsZWRcIiApICk7XG5cdH0sXG5cblx0b3BlbjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdGlmICggdGhpcy5vcHRpb25zLmRpc2FibGVkICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIElmIHRoaXMgaXMgdGhlIGZpcnN0IHRpbWUgdGhlIG1lbnUgaXMgYmVpbmcgb3BlbmVkLCByZW5kZXIgdGhlIGl0ZW1zXG5cdFx0aWYgKCAhdGhpcy5fcmVuZGVyZWQgKSB7XG5cdFx0XHR0aGlzLl9yZWZyZXNoTWVudSgpO1xuXHRcdH0gZWxzZSB7XG5cblx0XHRcdC8vIE1lbnUgY2xlYXJzIGZvY3VzIG9uIGNsb3NlLCByZXNldCBmb2N1cyB0byBzZWxlY3RlZCBpdGVtXG5cdFx0XHR0aGlzLl9yZW1vdmVDbGFzcyggdGhpcy5tZW51LmZpbmQoIFwiLnVpLXN0YXRlLWFjdGl2ZVwiICksIG51bGwsIFwidWktc3RhdGUtYWN0aXZlXCIgKTtcblx0XHRcdHRoaXMubWVudUluc3RhbmNlLmZvY3VzKCBudWxsLCB0aGlzLl9nZXRTZWxlY3RlZEl0ZW0oKSApO1xuXHRcdH1cblxuXHRcdC8vIElmIHRoZXJlIGFyZSBubyBvcHRpb25zLCBkb24ndCBvcGVuIHRoZSBtZW51XG5cdFx0aWYgKCAhdGhpcy5tZW51SXRlbXMubGVuZ3RoICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHRoaXMuaXNPcGVuID0gdHJ1ZTtcblx0XHR0aGlzLl90b2dnbGVBdHRyKCk7XG5cdFx0dGhpcy5fcmVzaXplTWVudSgpO1xuXHRcdHRoaXMuX3Bvc2l0aW9uKCk7XG5cblx0XHR0aGlzLl9vbiggdGhpcy5kb2N1bWVudCwgdGhpcy5fZG9jdW1lbnRDbGljayApO1xuXG5cdFx0dGhpcy5fdHJpZ2dlciggXCJvcGVuXCIsIGV2ZW50ICk7XG5cdH0sXG5cblx0X3Bvc2l0aW9uOiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLm1lbnVXcmFwLnBvc2l0aW9uKCAkLmV4dGVuZCggeyBvZjogdGhpcy5idXR0b24gfSwgdGhpcy5vcHRpb25zLnBvc2l0aW9uICkgKTtcblx0fSxcblxuXHRjbG9zZTogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdGlmICggIXRoaXMuaXNPcGVuICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHRoaXMuaXNPcGVuID0gZmFsc2U7XG5cdFx0dGhpcy5fdG9nZ2xlQXR0cigpO1xuXG5cdFx0dGhpcy5yYW5nZSA9IG51bGw7XG5cdFx0dGhpcy5fb2ZmKCB0aGlzLmRvY3VtZW50ICk7XG5cblx0XHR0aGlzLl90cmlnZ2VyKCBcImNsb3NlXCIsIGV2ZW50ICk7XG5cdH0sXG5cblx0d2lkZ2V0OiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5idXR0b247XG5cdH0sXG5cblx0bWVudVdpZGdldDogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMubWVudTtcblx0fSxcblxuXHRfcmVuZGVyQnV0dG9uSXRlbTogZnVuY3Rpb24oIGl0ZW0gKSB7XG5cdFx0dmFyIGJ1dHRvbkl0ZW0gPSAkKCBcIjxzcGFuPlwiICk7XG5cblx0XHR0aGlzLl9zZXRUZXh0KCBidXR0b25JdGVtLCBpdGVtLmxhYmVsICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIGJ1dHRvbkl0ZW0sIFwidWktc2VsZWN0bWVudS10ZXh0XCIgKTtcblxuXHRcdHJldHVybiBidXR0b25JdGVtO1xuXHR9LFxuXG5cdF9yZW5kZXJNZW51OiBmdW5jdGlvbiggdWwsIGl0ZW1zICkge1xuXHRcdHZhciB0aGF0ID0gdGhpcyxcblx0XHRcdGN1cnJlbnRPcHRncm91cCA9IFwiXCI7XG5cblx0XHQkLmVhY2goIGl0ZW1zLCBmdW5jdGlvbiggaW5kZXgsIGl0ZW0gKSB7XG5cdFx0XHR2YXIgbGk7XG5cblx0XHRcdGlmICggaXRlbS5vcHRncm91cCAhPT0gY3VycmVudE9wdGdyb3VwICkge1xuXHRcdFx0XHRsaSA9ICQoIFwiPGxpPlwiLCB7XG5cdFx0XHRcdFx0dGV4dDogaXRlbS5vcHRncm91cFxuXHRcdFx0XHR9ICk7XG5cdFx0XHRcdHRoYXQuX2FkZENsYXNzKCBsaSwgXCJ1aS1zZWxlY3RtZW51LW9wdGdyb3VwXCIsIFwidWktbWVudS1kaXZpZGVyXCIgK1xuXHRcdFx0XHRcdCggaXRlbS5lbGVtZW50LnBhcmVudCggXCJvcHRncm91cFwiICkucHJvcCggXCJkaXNhYmxlZFwiICkgP1xuXHRcdFx0XHRcdFx0XCIgdWktc3RhdGUtZGlzYWJsZWRcIiA6XG5cdFx0XHRcdFx0XHRcIlwiICkgKTtcblxuXHRcdFx0XHRsaS5hcHBlbmRUbyggdWwgKTtcblxuXHRcdFx0XHRjdXJyZW50T3B0Z3JvdXAgPSBpdGVtLm9wdGdyb3VwO1xuXHRcdFx0fVxuXG5cdFx0XHR0aGF0Ll9yZW5kZXJJdGVtRGF0YSggdWwsIGl0ZW0gKTtcblx0XHR9ICk7XG5cdH0sXG5cblx0X3JlbmRlckl0ZW1EYXRhOiBmdW5jdGlvbiggdWwsIGl0ZW0gKSB7XG5cdFx0cmV0dXJuIHRoaXMuX3JlbmRlckl0ZW0oIHVsLCBpdGVtICkuZGF0YSggXCJ1aS1zZWxlY3RtZW51LWl0ZW1cIiwgaXRlbSApO1xuXHR9LFxuXG5cdF9yZW5kZXJJdGVtOiBmdW5jdGlvbiggdWwsIGl0ZW0gKSB7XG5cdFx0dmFyIGxpID0gJCggXCI8bGk+XCIgKSxcblx0XHRcdHdyYXBwZXIgPSAkKCBcIjxkaXY+XCIsIHtcblx0XHRcdFx0dGl0bGU6IGl0ZW0uZWxlbWVudC5hdHRyKCBcInRpdGxlXCIgKVxuXHRcdFx0fSApO1xuXG5cdFx0aWYgKCBpdGVtLmRpc2FibGVkICkge1xuXHRcdFx0dGhpcy5fYWRkQ2xhc3MoIGxpLCBudWxsLCBcInVpLXN0YXRlLWRpc2FibGVkXCIgKTtcblx0XHR9XG5cdFx0dGhpcy5fc2V0VGV4dCggd3JhcHBlciwgaXRlbS5sYWJlbCApO1xuXG5cdFx0cmV0dXJuIGxpLmFwcGVuZCggd3JhcHBlciApLmFwcGVuZFRvKCB1bCApO1xuXHR9LFxuXG5cdF9zZXRUZXh0OiBmdW5jdGlvbiggZWxlbWVudCwgdmFsdWUgKSB7XG5cdFx0aWYgKCB2YWx1ZSApIHtcblx0XHRcdGVsZW1lbnQudGV4dCggdmFsdWUgKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0ZWxlbWVudC5odG1sKCBcIiYjMTYwO1wiICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9tb3ZlOiBmdW5jdGlvbiggZGlyZWN0aW9uLCBldmVudCApIHtcblx0XHR2YXIgaXRlbSwgbmV4dCxcblx0XHRcdGZpbHRlciA9IFwiLnVpLW1lbnUtaXRlbVwiO1xuXG5cdFx0aWYgKCB0aGlzLmlzT3BlbiApIHtcblx0XHRcdGl0ZW0gPSB0aGlzLm1lbnVJdGVtcy5lcSggdGhpcy5mb2N1c0luZGV4ICkucGFyZW50KCBcImxpXCIgKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0aXRlbSA9IHRoaXMubWVudUl0ZW1zLmVxKCB0aGlzLmVsZW1lbnRbIDAgXS5zZWxlY3RlZEluZGV4ICkucGFyZW50KCBcImxpXCIgKTtcblx0XHRcdGZpbHRlciArPSBcIjpub3QoLnVpLXN0YXRlLWRpc2FibGVkKVwiO1xuXHRcdH1cblxuXHRcdGlmICggZGlyZWN0aW9uID09PSBcImZpcnN0XCIgfHwgZGlyZWN0aW9uID09PSBcImxhc3RcIiApIHtcblx0XHRcdG5leHQgPSBpdGVtWyBkaXJlY3Rpb24gPT09IFwiZmlyc3RcIiA/IFwicHJldkFsbFwiIDogXCJuZXh0QWxsXCIgXSggZmlsdGVyICkuZXEoIC0xICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdG5leHQgPSBpdGVtWyBkaXJlY3Rpb24gKyBcIkFsbFwiIF0oIGZpbHRlciApLmVxKCAwICk7XG5cdFx0fVxuXG5cdFx0aWYgKCBuZXh0Lmxlbmd0aCApIHtcblx0XHRcdHRoaXMubWVudUluc3RhbmNlLmZvY3VzKCBldmVudCwgbmV4dCApO1xuXHRcdH1cblx0fSxcblxuXHRfZ2V0U2VsZWN0ZWRJdGVtOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5tZW51SXRlbXMuZXEoIHRoaXMuZWxlbWVudFsgMCBdLnNlbGVjdGVkSW5kZXggKS5wYXJlbnQoIFwibGlcIiApO1xuXHR9LFxuXG5cdF90b2dnbGU6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR0aGlzWyB0aGlzLmlzT3BlbiA/IFwiY2xvc2VcIiA6IFwib3BlblwiIF0oIGV2ZW50ICk7XG5cdH0sXG5cblx0X3NldFNlbGVjdGlvbjogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHNlbGVjdGlvbjtcblxuXHRcdGlmICggIXRoaXMucmFuZ2UgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aWYgKCB3aW5kb3cuZ2V0U2VsZWN0aW9uICkge1xuXHRcdFx0c2VsZWN0aW9uID0gd2luZG93LmdldFNlbGVjdGlvbigpO1xuXHRcdFx0c2VsZWN0aW9uLnJlbW92ZUFsbFJhbmdlcygpO1xuXHRcdFx0c2VsZWN0aW9uLmFkZFJhbmdlKCB0aGlzLnJhbmdlICk7XG5cblx0XHQvLyBTdXBwb3J0OiBJRThcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5yYW5nZS5zZWxlY3QoKTtcblx0XHR9XG5cblx0XHQvLyBTdXBwb3J0OiBJRVxuXHRcdC8vIFNldHRpbmcgdGhlIHRleHQgc2VsZWN0aW9uIGtpbGxzIHRoZSBidXR0b24gZm9jdXMgaW4gSUUsIGJ1dFxuXHRcdC8vIHJlc3RvcmluZyB0aGUgZm9jdXMgZG9lc24ndCBraWxsIHRoZSBzZWxlY3Rpb24uXG5cdFx0dGhpcy5idXR0b24uZm9jdXMoKTtcblx0fSxcblxuXHRfZG9jdW1lbnRDbGljazoge1xuXHRcdG1vdXNlZG93bjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0aWYgKCAhdGhpcy5pc09wZW4gKSB7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblxuXHRcdFx0aWYgKCAhJCggZXZlbnQudGFyZ2V0ICkuY2xvc2VzdCggXCIudWktc2VsZWN0bWVudS1tZW51LCAjXCIgK1xuXHRcdFx0XHRcdCQudWkuZXNjYXBlU2VsZWN0b3IoIHRoaXMuaWRzLmJ1dHRvbiApICkubGVuZ3RoICkge1xuXHRcdFx0XHR0aGlzLmNsb3NlKCBldmVudCApO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHRfYnV0dG9uRXZlbnRzOiB7XG5cblx0XHQvLyBQcmV2ZW50IHRleHQgc2VsZWN0aW9uIGZyb20gYmVpbmcgcmVzZXQgd2hlbiBpbnRlcmFjdGluZyB3aXRoIHRoZSBzZWxlY3RtZW51ICgjMTAxNDQpXG5cdFx0bW91c2Vkb3duOiBmdW5jdGlvbigpIHtcblx0XHRcdHZhciBzZWxlY3Rpb247XG5cblx0XHRcdGlmICggd2luZG93LmdldFNlbGVjdGlvbiApIHtcblx0XHRcdFx0c2VsZWN0aW9uID0gd2luZG93LmdldFNlbGVjdGlvbigpO1xuXHRcdFx0XHRpZiAoIHNlbGVjdGlvbi5yYW5nZUNvdW50ICkge1xuXHRcdFx0XHRcdHRoaXMucmFuZ2UgPSBzZWxlY3Rpb24uZ2V0UmFuZ2VBdCggMCApO1xuXHRcdFx0XHR9XG5cblx0XHRcdC8vIFN1cHBvcnQ6IElFOFxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0dGhpcy5yYW5nZSA9IGRvY3VtZW50LnNlbGVjdGlvbi5jcmVhdGVSYW5nZSgpO1xuXHRcdFx0fVxuXHRcdH0sXG5cblx0XHRjbGljazogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0dGhpcy5fc2V0U2VsZWN0aW9uKCk7XG5cdFx0XHR0aGlzLl90b2dnbGUoIGV2ZW50ICk7XG5cdFx0fSxcblxuXHRcdGtleWRvd246IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdHZhciBwcmV2ZW50RGVmYXVsdCA9IHRydWU7XG5cdFx0XHRzd2l0Y2ggKCBldmVudC5rZXlDb2RlICkge1xuXHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuVEFCOlxuXHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuRVNDQVBFOlxuXHRcdFx0XHR0aGlzLmNsb3NlKCBldmVudCApO1xuXHRcdFx0XHRwcmV2ZW50RGVmYXVsdCA9IGZhbHNlO1xuXHRcdFx0XHRicmVhaztcblx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLkVOVEVSOlxuXHRcdFx0XHRpZiAoIHRoaXMuaXNPcGVuICkge1xuXHRcdFx0XHRcdHRoaXMuX3NlbGVjdEZvY3VzZWRJdGVtKCBldmVudCApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuVVA6XG5cdFx0XHRcdGlmICggZXZlbnQuYWx0S2V5ICkge1xuXHRcdFx0XHRcdHRoaXMuX3RvZ2dsZSggZXZlbnQgKTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHR0aGlzLl9tb3ZlKCBcInByZXZcIiwgZXZlbnQgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRicmVhaztcblx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLkRPV046XG5cdFx0XHRcdGlmICggZXZlbnQuYWx0S2V5ICkge1xuXHRcdFx0XHRcdHRoaXMuX3RvZ2dsZSggZXZlbnQgKTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHR0aGlzLl9tb3ZlKCBcIm5leHRcIiwgZXZlbnQgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRicmVhaztcblx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLlNQQUNFOlxuXHRcdFx0XHRpZiAoIHRoaXMuaXNPcGVuICkge1xuXHRcdFx0XHRcdHRoaXMuX3NlbGVjdEZvY3VzZWRJdGVtKCBldmVudCApO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHRoaXMuX3RvZ2dsZSggZXZlbnQgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRicmVhaztcblx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLkxFRlQ6XG5cdFx0XHRcdHRoaXMuX21vdmUoIFwicHJldlwiLCBldmVudCApO1xuXHRcdFx0XHRicmVhaztcblx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLlJJR0hUOlxuXHRcdFx0XHR0aGlzLl9tb3ZlKCBcIm5leHRcIiwgZXZlbnQgKTtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHRjYXNlICQudWkua2V5Q29kZS5IT01FOlxuXHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuUEFHRV9VUDpcblx0XHRcdFx0dGhpcy5fbW92ZSggXCJmaXJzdFwiLCBldmVudCApO1xuXHRcdFx0XHRicmVhaztcblx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLkVORDpcblx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLlBBR0VfRE9XTjpcblx0XHRcdFx0dGhpcy5fbW92ZSggXCJsYXN0XCIsIGV2ZW50ICk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0ZGVmYXVsdDpcblx0XHRcdFx0dGhpcy5tZW51LnRyaWdnZXIoIGV2ZW50ICk7XG5cdFx0XHRcdHByZXZlbnREZWZhdWx0ID0gZmFsc2U7XG5cdFx0XHR9XG5cblx0XHRcdGlmICggcHJldmVudERlZmF1bHQgKSB7XG5cdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9zZWxlY3RGb2N1c2VkSXRlbTogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHZhciBpdGVtID0gdGhpcy5tZW51SXRlbXMuZXEoIHRoaXMuZm9jdXNJbmRleCApLnBhcmVudCggXCJsaVwiICk7XG5cdFx0aWYgKCAhaXRlbS5oYXNDbGFzcyggXCJ1aS1zdGF0ZS1kaXNhYmxlZFwiICkgKSB7XG5cdFx0XHR0aGlzLl9zZWxlY3QoIGl0ZW0uZGF0YSggXCJ1aS1zZWxlY3RtZW51LWl0ZW1cIiApLCBldmVudCApO1xuXHRcdH1cblx0fSxcblxuXHRfc2VsZWN0OiBmdW5jdGlvbiggaXRlbSwgZXZlbnQgKSB7XG5cdFx0dmFyIG9sZEluZGV4ID0gdGhpcy5lbGVtZW50WyAwIF0uc2VsZWN0ZWRJbmRleDtcblxuXHRcdC8vIENoYW5nZSBuYXRpdmUgc2VsZWN0IGVsZW1lbnRcblx0XHR0aGlzLmVsZW1lbnRbIDAgXS5zZWxlY3RlZEluZGV4ID0gaXRlbS5pbmRleDtcblx0XHR0aGlzLmJ1dHRvbkl0ZW0ucmVwbGFjZVdpdGgoIHRoaXMuYnV0dG9uSXRlbSA9IHRoaXMuX3JlbmRlckJ1dHRvbkl0ZW0oIGl0ZW0gKSApO1xuXHRcdHRoaXMuX3NldEFyaWEoIGl0ZW0gKTtcblx0XHR0aGlzLl90cmlnZ2VyKCBcInNlbGVjdFwiLCBldmVudCwgeyBpdGVtOiBpdGVtIH0gKTtcblxuXHRcdGlmICggaXRlbS5pbmRleCAhPT0gb2xkSW5kZXggKSB7XG5cdFx0XHR0aGlzLl90cmlnZ2VyKCBcImNoYW5nZVwiLCBldmVudCwgeyBpdGVtOiBpdGVtIH0gKTtcblx0XHR9XG5cblx0XHR0aGlzLmNsb3NlKCBldmVudCApO1xuXHR9LFxuXG5cdF9zZXRBcmlhOiBmdW5jdGlvbiggaXRlbSApIHtcblx0XHR2YXIgaWQgPSB0aGlzLm1lbnVJdGVtcy5lcSggaXRlbS5pbmRleCApLmF0dHIoIFwiaWRcIiApO1xuXG5cdFx0dGhpcy5idXR0b24uYXR0cigge1xuXHRcdFx0XCJhcmlhLWxhYmVsbGVkYnlcIjogaWQsXG5cdFx0XHRcImFyaWEtYWN0aXZlZGVzY2VuZGFudFwiOiBpZFxuXHRcdH0gKTtcblx0XHR0aGlzLm1lbnUuYXR0ciggXCJhcmlhLWFjdGl2ZWRlc2NlbmRhbnRcIiwgaWQgKTtcblx0fSxcblxuXHRfc2V0T3B0aW9uOiBmdW5jdGlvbigga2V5LCB2YWx1ZSApIHtcblx0XHRpZiAoIGtleSA9PT0gXCJpY29uc1wiICkge1xuXHRcdFx0dmFyIGljb24gPSB0aGlzLmJ1dHRvbi5maW5kKCBcInNwYW4udWktaWNvblwiICk7XG5cdFx0XHR0aGlzLl9yZW1vdmVDbGFzcyggaWNvbiwgbnVsbCwgdGhpcy5vcHRpb25zLmljb25zLmJ1dHRvbiApXG5cdFx0XHRcdC5fYWRkQ2xhc3MoIGljb24sIG51bGwsIHZhbHVlLmJ1dHRvbiApO1xuXHRcdH1cblxuXHRcdHRoaXMuX3N1cGVyKCBrZXksIHZhbHVlICk7XG5cblx0XHRpZiAoIGtleSA9PT0gXCJhcHBlbmRUb1wiICkge1xuXHRcdFx0dGhpcy5tZW51V3JhcC5hcHBlbmRUbyggdGhpcy5fYXBwZW5kVG8oKSApO1xuXHRcdH1cblxuXHRcdGlmICgga2V5ID09PSBcIndpZHRoXCIgKSB7XG5cdFx0XHR0aGlzLl9yZXNpemVCdXR0b24oKTtcblx0XHR9XG5cdH0sXG5cblx0X3NldE9wdGlvbkRpc2FibGVkOiBmdW5jdGlvbiggdmFsdWUgKSB7XG5cdFx0dGhpcy5fc3VwZXIoIHZhbHVlICk7XG5cblx0XHR0aGlzLm1lbnVJbnN0YW5jZS5vcHRpb24oIFwiZGlzYWJsZWRcIiwgdmFsdWUgKTtcblx0XHR0aGlzLmJ1dHRvbi5hdHRyKCBcImFyaWEtZGlzYWJsZWRcIiwgdmFsdWUgKTtcblx0XHR0aGlzLl90b2dnbGVDbGFzcyggdGhpcy5idXR0b24sIG51bGwsIFwidWktc3RhdGUtZGlzYWJsZWRcIiwgdmFsdWUgKTtcblxuXHRcdHRoaXMuZWxlbWVudC5wcm9wKCBcImRpc2FibGVkXCIsIHZhbHVlICk7XG5cdFx0aWYgKCB2YWx1ZSApIHtcblx0XHRcdHRoaXMuYnV0dG9uLmF0dHIoIFwidGFiaW5kZXhcIiwgLTEgKTtcblx0XHRcdHRoaXMuY2xvc2UoKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5idXR0b24uYXR0ciggXCJ0YWJpbmRleFwiLCAwICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9hcHBlbmRUbzogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGVsZW1lbnQgPSB0aGlzLm9wdGlvbnMuYXBwZW5kVG87XG5cblx0XHRpZiAoIGVsZW1lbnQgKSB7XG5cdFx0XHRlbGVtZW50ID0gZWxlbWVudC5qcXVlcnkgfHwgZWxlbWVudC5ub2RlVHlwZSA/XG5cdFx0XHRcdCQoIGVsZW1lbnQgKSA6XG5cdFx0XHRcdHRoaXMuZG9jdW1lbnQuZmluZCggZWxlbWVudCApLmVxKCAwICk7XG5cdFx0fVxuXG5cdFx0aWYgKCAhZWxlbWVudCB8fCAhZWxlbWVudFsgMCBdICkge1xuXHRcdFx0ZWxlbWVudCA9IHRoaXMuZWxlbWVudC5jbG9zZXN0KCBcIi51aS1mcm9udCwgZGlhbG9nXCIgKTtcblx0XHR9XG5cblx0XHRpZiAoICFlbGVtZW50Lmxlbmd0aCApIHtcblx0XHRcdGVsZW1lbnQgPSB0aGlzLmRvY3VtZW50WyAwIF0uYm9keTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZWxlbWVudDtcblx0fSxcblxuXHRfdG9nZ2xlQXR0cjogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5idXR0b24uYXR0ciggXCJhcmlhLWV4cGFuZGVkXCIsIHRoaXMuaXNPcGVuICk7XG5cblx0XHQvLyBXZSBjYW4ndCB1c2UgdHdvIF90b2dnbGVDbGFzcygpIGNhbGxzIGhlcmUsIGJlY2F1c2Ugd2UgbmVlZCB0byBtYWtlIHN1cmVcblx0XHQvLyB3ZSBhbHdheXMgcmVtb3ZlIGNsYXNzZXMgZmlyc3QgYW5kIGFkZCB0aGVtIHNlY29uZCwgb3RoZXJ3aXNlIGlmIGJvdGggY2xhc3NlcyBoYXZlIHRoZVxuXHRcdC8vIHNhbWUgdGhlbWUgY2xhc3MsIGl0IHdpbGwgYmUgcmVtb3ZlZCBhZnRlciB3ZSBhZGQgaXQuXG5cdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIHRoaXMuYnV0dG9uLCBcInVpLXNlbGVjdG1lbnUtYnV0dG9uLVwiICtcblx0XHRcdCggdGhpcy5pc09wZW4gPyBcImNsb3NlZFwiIDogXCJvcGVuXCIgKSApXG5cdFx0XHQuX2FkZENsYXNzKCB0aGlzLmJ1dHRvbiwgXCJ1aS1zZWxlY3RtZW51LWJ1dHRvbi1cIiArXG5cdFx0XHRcdCggdGhpcy5pc09wZW4gPyBcIm9wZW5cIiA6IFwiY2xvc2VkXCIgKSApXG5cdFx0XHQuX3RvZ2dsZUNsYXNzKCB0aGlzLm1lbnVXcmFwLCBcInVpLXNlbGVjdG1lbnUtb3BlblwiLCBudWxsLCB0aGlzLmlzT3BlbiApO1xuXG5cdFx0dGhpcy5tZW51LmF0dHIoIFwiYXJpYS1oaWRkZW5cIiwgIXRoaXMuaXNPcGVuICk7XG5cdH0sXG5cblx0X3Jlc2l6ZUJ1dHRvbjogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHdpZHRoID0gdGhpcy5vcHRpb25zLndpZHRoO1xuXG5cdFx0Ly8gRm9yIGB3aWR0aDogZmFsc2VgLCBqdXN0IHJlbW92ZSBpbmxpbmUgc3R5bGUgYW5kIHN0b3Bcblx0XHRpZiAoIHdpZHRoID09PSBmYWxzZSApIHtcblx0XHRcdHRoaXMuYnV0dG9uLmNzcyggXCJ3aWR0aFwiLCBcIlwiICk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gRm9yIGB3aWR0aDogbnVsbGAsIG1hdGNoIHRoZSB3aWR0aCBvZiB0aGUgb3JpZ2luYWwgZWxlbWVudFxuXHRcdGlmICggd2lkdGggPT09IG51bGwgKSB7XG5cdFx0XHR3aWR0aCA9IHRoaXMuZWxlbWVudC5zaG93KCkub3V0ZXJXaWR0aCgpO1xuXHRcdFx0dGhpcy5lbGVtZW50LmhpZGUoKTtcblx0XHR9XG5cblx0XHR0aGlzLmJ1dHRvbi5vdXRlcldpZHRoKCB3aWR0aCApO1xuXHR9LFxuXG5cdF9yZXNpemVNZW51OiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLm1lbnUub3V0ZXJXaWR0aCggTWF0aC5tYXgoXG5cdFx0XHR0aGlzLmJ1dHRvbi5vdXRlcldpZHRoKCksXG5cblx0XHRcdC8vIFN1cHBvcnQ6IElFMTBcblx0XHRcdC8vIElFMTAgd3JhcHMgbG9uZyB0ZXh0IChwb3NzaWJseSBhIHJvdW5kaW5nIGJ1Zylcblx0XHRcdC8vIHNvIHdlIGFkZCAxcHggdG8gYXZvaWQgdGhlIHdyYXBwaW5nXG5cdFx0XHR0aGlzLm1lbnUud2lkdGgoIFwiXCIgKS5vdXRlcldpZHRoKCkgKyAxXG5cdFx0KSApO1xuXHR9LFxuXG5cdF9nZXRDcmVhdGVPcHRpb25zOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMuX3N1cGVyKCk7XG5cblx0XHRvcHRpb25zLmRpc2FibGVkID0gdGhpcy5lbGVtZW50LnByb3AoIFwiZGlzYWJsZWRcIiApO1xuXG5cdFx0cmV0dXJuIG9wdGlvbnM7XG5cdH0sXG5cblx0X3BhcnNlT3B0aW9uczogZnVuY3Rpb24oIG9wdGlvbnMgKSB7XG5cdFx0dmFyIHRoYXQgPSB0aGlzLFxuXHRcdFx0ZGF0YSA9IFtdO1xuXHRcdG9wdGlvbnMuZWFjaCggZnVuY3Rpb24oIGluZGV4LCBpdGVtICkge1xuXHRcdFx0ZGF0YS5wdXNoKCB0aGF0Ll9wYXJzZU9wdGlvbiggJCggaXRlbSApLCBpbmRleCApICk7XG5cdFx0fSApO1xuXHRcdHRoaXMuaXRlbXMgPSBkYXRhO1xuXHR9LFxuXG5cdF9wYXJzZU9wdGlvbjogZnVuY3Rpb24oIG9wdGlvbiwgaW5kZXggKSB7XG5cdFx0dmFyIG9wdGdyb3VwID0gb3B0aW9uLnBhcmVudCggXCJvcHRncm91cFwiICk7XG5cblx0XHRyZXR1cm4ge1xuXHRcdFx0ZWxlbWVudDogb3B0aW9uLFxuXHRcdFx0aW5kZXg6IGluZGV4LFxuXHRcdFx0dmFsdWU6IG9wdGlvbi52YWwoKSxcblx0XHRcdGxhYmVsOiBvcHRpb24udGV4dCgpLFxuXHRcdFx0b3B0Z3JvdXA6IG9wdGdyb3VwLmF0dHIoIFwibGFiZWxcIiApIHx8IFwiXCIsXG5cdFx0XHRkaXNhYmxlZDogb3B0Z3JvdXAucHJvcCggXCJkaXNhYmxlZFwiICkgfHwgb3B0aW9uLnByb3AoIFwiZGlzYWJsZWRcIiApXG5cdFx0fTtcblx0fSxcblxuXHRfZGVzdHJveTogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fdW5iaW5kRm9ybVJlc2V0SGFuZGxlcigpO1xuXHRcdHRoaXMubWVudVdyYXAucmVtb3ZlKCk7XG5cdFx0dGhpcy5idXR0b24ucmVtb3ZlKCk7XG5cdFx0dGhpcy5lbGVtZW50LnNob3coKTtcblx0XHR0aGlzLmVsZW1lbnQucmVtb3ZlVW5pcXVlSWQoKTtcblx0XHR0aGlzLmxhYmVscy5hdHRyKCBcImZvclwiLCB0aGlzLmlkcy5lbGVtZW50ICk7XG5cdH1cbn0gXSApO1xuXG5cbi8qIVxuICogalF1ZXJ5IFVJIFNsaWRlciAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogU2xpZGVyXG4vLz4+Z3JvdXA6IFdpZGdldHNcbi8vPj5kZXNjcmlwdGlvbjogRGlzcGxheXMgYSBmbGV4aWJsZSBzbGlkZXIgd2l0aCByYW5nZXMgYW5kIGFjY2Vzc2liaWxpdHkgdmlhIGtleWJvYXJkLlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL3NsaWRlci9cbi8vPj5kZW1vczogaHR0cDovL2pxdWVyeXVpLmNvbS9zbGlkZXIvXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2UvY29yZS5jc3Ncbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9zbGlkZXIuY3NzXG4vLz4+Y3NzLnRoZW1lOiAuLi8uLi90aGVtZXMvYmFzZS90aGVtZS5jc3NcblxuXG5cbnZhciB3aWRnZXRzU2xpZGVyID0gJC53aWRnZXQoIFwidWkuc2xpZGVyXCIsICQudWkubW91c2UsIHtcblx0dmVyc2lvbjogXCIxLjEyLjFcIixcblx0d2lkZ2V0RXZlbnRQcmVmaXg6IFwic2xpZGVcIixcblxuXHRvcHRpb25zOiB7XG5cdFx0YW5pbWF0ZTogZmFsc2UsXG5cdFx0Y2xhc3Nlczoge1xuXHRcdFx0XCJ1aS1zbGlkZXJcIjogXCJ1aS1jb3JuZXItYWxsXCIsXG5cdFx0XHRcInVpLXNsaWRlci1oYW5kbGVcIjogXCJ1aS1jb3JuZXItYWxsXCIsXG5cblx0XHRcdC8vIE5vdGU6IHVpLXdpZGdldC1oZWFkZXIgaXNuJ3QgdGhlIG1vc3QgZml0dGluZ2x5IHNlbWFudGljIGZyYW1ld29yayBjbGFzcyBmb3IgdGhpc1xuXHRcdFx0Ly8gZWxlbWVudCwgYnV0IHdvcmtlZCBiZXN0IHZpc3VhbGx5IHdpdGggYSB2YXJpZXR5IG9mIHRoZW1lc1xuXHRcdFx0XCJ1aS1zbGlkZXItcmFuZ2VcIjogXCJ1aS1jb3JuZXItYWxsIHVpLXdpZGdldC1oZWFkZXJcIlxuXHRcdH0sXG5cdFx0ZGlzdGFuY2U6IDAsXG5cdFx0bWF4OiAxMDAsXG5cdFx0bWluOiAwLFxuXHRcdG9yaWVudGF0aW9uOiBcImhvcml6b250YWxcIixcblx0XHRyYW5nZTogZmFsc2UsXG5cdFx0c3RlcDogMSxcblx0XHR2YWx1ZTogMCxcblx0XHR2YWx1ZXM6IG51bGwsXG5cblx0XHQvLyBDYWxsYmFja3Ncblx0XHRjaGFuZ2U6IG51bGwsXG5cdFx0c2xpZGU6IG51bGwsXG5cdFx0c3RhcnQ6IG51bGwsXG5cdFx0c3RvcDogbnVsbFxuXHR9LFxuXG5cdC8vIE51bWJlciBvZiBwYWdlcyBpbiBhIHNsaWRlclxuXHQvLyAoaG93IG1hbnkgdGltZXMgY2FuIHlvdSBwYWdlIHVwL2Rvd24gdG8gZ28gdGhyb3VnaCB0aGUgd2hvbGUgcmFuZ2UpXG5cdG51bVBhZ2VzOiA1LFxuXG5cdF9jcmVhdGU6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuX2tleVNsaWRpbmcgPSBmYWxzZTtcblx0XHR0aGlzLl9tb3VzZVNsaWRpbmcgPSBmYWxzZTtcblx0XHR0aGlzLl9hbmltYXRlT2ZmID0gdHJ1ZTtcblx0XHR0aGlzLl9oYW5kbGVJbmRleCA9IG51bGw7XG5cdFx0dGhpcy5fZGV0ZWN0T3JpZW50YXRpb24oKTtcblx0XHR0aGlzLl9tb3VzZUluaXQoKTtcblx0XHR0aGlzLl9jYWxjdWxhdGVOZXdNYXgoKTtcblxuXHRcdHRoaXMuX2FkZENsYXNzKCBcInVpLXNsaWRlciB1aS1zbGlkZXItXCIgKyB0aGlzLm9yaWVudGF0aW9uLFxuXHRcdFx0XCJ1aS13aWRnZXQgdWktd2lkZ2V0LWNvbnRlbnRcIiApO1xuXG5cdFx0dGhpcy5fcmVmcmVzaCgpO1xuXG5cdFx0dGhpcy5fYW5pbWF0ZU9mZiA9IGZhbHNlO1xuXHR9LFxuXG5cdF9yZWZyZXNoOiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLl9jcmVhdGVSYW5nZSgpO1xuXHRcdHRoaXMuX2NyZWF0ZUhhbmRsZXMoKTtcblx0XHR0aGlzLl9zZXR1cEV2ZW50cygpO1xuXHRcdHRoaXMuX3JlZnJlc2hWYWx1ZSgpO1xuXHR9LFxuXG5cdF9jcmVhdGVIYW5kbGVzOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgaSwgaGFuZGxlQ291bnQsXG5cdFx0XHRvcHRpb25zID0gdGhpcy5vcHRpb25zLFxuXHRcdFx0ZXhpc3RpbmdIYW5kbGVzID0gdGhpcy5lbGVtZW50LmZpbmQoIFwiLnVpLXNsaWRlci1oYW5kbGVcIiApLFxuXHRcdFx0aGFuZGxlID0gXCI8c3BhbiB0YWJpbmRleD0nMCc+PC9zcGFuPlwiLFxuXHRcdFx0aGFuZGxlcyA9IFtdO1xuXG5cdFx0aGFuZGxlQ291bnQgPSAoIG9wdGlvbnMudmFsdWVzICYmIG9wdGlvbnMudmFsdWVzLmxlbmd0aCApIHx8IDE7XG5cblx0XHRpZiAoIGV4aXN0aW5nSGFuZGxlcy5sZW5ndGggPiBoYW5kbGVDb3VudCApIHtcblx0XHRcdGV4aXN0aW5nSGFuZGxlcy5zbGljZSggaGFuZGxlQ291bnQgKS5yZW1vdmUoKTtcblx0XHRcdGV4aXN0aW5nSGFuZGxlcyA9IGV4aXN0aW5nSGFuZGxlcy5zbGljZSggMCwgaGFuZGxlQ291bnQgKTtcblx0XHR9XG5cblx0XHRmb3IgKCBpID0gZXhpc3RpbmdIYW5kbGVzLmxlbmd0aDsgaSA8IGhhbmRsZUNvdW50OyBpKysgKSB7XG5cdFx0XHRoYW5kbGVzLnB1c2goIGhhbmRsZSApO1xuXHRcdH1cblxuXHRcdHRoaXMuaGFuZGxlcyA9IGV4aXN0aW5nSGFuZGxlcy5hZGQoICQoIGhhbmRsZXMuam9pbiggXCJcIiApICkuYXBwZW5kVG8oIHRoaXMuZWxlbWVudCApICk7XG5cblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5oYW5kbGVzLCBcInVpLXNsaWRlci1oYW5kbGVcIiwgXCJ1aS1zdGF0ZS1kZWZhdWx0XCIgKTtcblxuXHRcdHRoaXMuaGFuZGxlID0gdGhpcy5oYW5kbGVzLmVxKCAwICk7XG5cblx0XHR0aGlzLmhhbmRsZXMuZWFjaCggZnVuY3Rpb24oIGkgKSB7XG5cdFx0XHQkKCB0aGlzIClcblx0XHRcdFx0LmRhdGEoIFwidWktc2xpZGVyLWhhbmRsZS1pbmRleFwiLCBpIClcblx0XHRcdFx0LmF0dHIoIFwidGFiSW5kZXhcIiwgMCApO1xuXHRcdH0gKTtcblx0fSxcblxuXHRfY3JlYXRlUmFuZ2U6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBvcHRpb25zID0gdGhpcy5vcHRpb25zO1xuXG5cdFx0aWYgKCBvcHRpb25zLnJhbmdlICkge1xuXHRcdFx0aWYgKCBvcHRpb25zLnJhbmdlID09PSB0cnVlICkge1xuXHRcdFx0XHRpZiAoICFvcHRpb25zLnZhbHVlcyApIHtcblx0XHRcdFx0XHRvcHRpb25zLnZhbHVlcyA9IFsgdGhpcy5fdmFsdWVNaW4oKSwgdGhpcy5fdmFsdWVNaW4oKSBdO1xuXHRcdFx0XHR9IGVsc2UgaWYgKCBvcHRpb25zLnZhbHVlcy5sZW5ndGggJiYgb3B0aW9ucy52YWx1ZXMubGVuZ3RoICE9PSAyICkge1xuXHRcdFx0XHRcdG9wdGlvbnMudmFsdWVzID0gWyBvcHRpb25zLnZhbHVlc1sgMCBdLCBvcHRpb25zLnZhbHVlc1sgMCBdIF07XG5cdFx0XHRcdH0gZWxzZSBpZiAoICQuaXNBcnJheSggb3B0aW9ucy52YWx1ZXMgKSApIHtcblx0XHRcdFx0XHRvcHRpb25zLnZhbHVlcyA9IG9wdGlvbnMudmFsdWVzLnNsaWNlKCAwICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0aWYgKCAhdGhpcy5yYW5nZSB8fCAhdGhpcy5yYW5nZS5sZW5ndGggKSB7XG5cdFx0XHRcdHRoaXMucmFuZ2UgPSAkKCBcIjxkaXY+XCIgKVxuXHRcdFx0XHRcdC5hcHBlbmRUbyggdGhpcy5lbGVtZW50ICk7XG5cblx0XHRcdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMucmFuZ2UsIFwidWktc2xpZGVyLXJhbmdlXCIgKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0aGlzLnJhbmdlLCBcInVpLXNsaWRlci1yYW5nZS1taW4gdWktc2xpZGVyLXJhbmdlLW1heFwiICk7XG5cblx0XHRcdFx0Ly8gSGFuZGxlIHJhbmdlIHN3aXRjaGluZyBmcm9tIHRydWUgdG8gbWluL21heFxuXHRcdFx0XHR0aGlzLnJhbmdlLmNzcygge1xuXHRcdFx0XHRcdFwibGVmdFwiOiBcIlwiLFxuXHRcdFx0XHRcdFwiYm90dG9tXCI6IFwiXCJcblx0XHRcdFx0fSApO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCBvcHRpb25zLnJhbmdlID09PSBcIm1pblwiIHx8IG9wdGlvbnMucmFuZ2UgPT09IFwibWF4XCIgKSB7XG5cdFx0XHRcdHRoaXMuX2FkZENsYXNzKCB0aGlzLnJhbmdlLCBcInVpLXNsaWRlci1yYW5nZS1cIiArIG9wdGlvbnMucmFuZ2UgKTtcblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0aWYgKCB0aGlzLnJhbmdlICkge1xuXHRcdFx0XHR0aGlzLnJhbmdlLnJlbW92ZSgpO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5yYW5nZSA9IG51bGw7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXR1cEV2ZW50czogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fb2ZmKCB0aGlzLmhhbmRsZXMgKTtcblx0XHR0aGlzLl9vbiggdGhpcy5oYW5kbGVzLCB0aGlzLl9oYW5kbGVFdmVudHMgKTtcblx0XHR0aGlzLl9ob3ZlcmFibGUoIHRoaXMuaGFuZGxlcyApO1xuXHRcdHRoaXMuX2ZvY3VzYWJsZSggdGhpcy5oYW5kbGVzICk7XG5cdH0sXG5cblx0X2Rlc3Ryb3k6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuaGFuZGxlcy5yZW1vdmUoKTtcblx0XHRpZiAoIHRoaXMucmFuZ2UgKSB7XG5cdFx0XHR0aGlzLnJhbmdlLnJlbW92ZSgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX21vdXNlRGVzdHJveSgpO1xuXHR9LFxuXG5cdF9tb3VzZUNhcHR1cmU6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgcG9zaXRpb24sIG5vcm1WYWx1ZSwgZGlzdGFuY2UsIGNsb3Nlc3RIYW5kbGUsIGluZGV4LCBhbGxvd2VkLCBvZmZzZXQsIG1vdXNlT3ZlckhhbmRsZSxcblx0XHRcdHRoYXQgPSB0aGlzLFxuXHRcdFx0byA9IHRoaXMub3B0aW9ucztcblxuXHRcdGlmICggby5kaXNhYmxlZCApIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHR0aGlzLmVsZW1lbnRTaXplID0ge1xuXHRcdFx0d2lkdGg6IHRoaXMuZWxlbWVudC5vdXRlcldpZHRoKCksXG5cdFx0XHRoZWlnaHQ6IHRoaXMuZWxlbWVudC5vdXRlckhlaWdodCgpXG5cdFx0fTtcblx0XHR0aGlzLmVsZW1lbnRPZmZzZXQgPSB0aGlzLmVsZW1lbnQub2Zmc2V0KCk7XG5cblx0XHRwb3NpdGlvbiA9IHsgeDogZXZlbnQucGFnZVgsIHk6IGV2ZW50LnBhZ2VZIH07XG5cdFx0bm9ybVZhbHVlID0gdGhpcy5fbm9ybVZhbHVlRnJvbU1vdXNlKCBwb3NpdGlvbiApO1xuXHRcdGRpc3RhbmNlID0gdGhpcy5fdmFsdWVNYXgoKSAtIHRoaXMuX3ZhbHVlTWluKCkgKyAxO1xuXHRcdHRoaXMuaGFuZGxlcy5lYWNoKCBmdW5jdGlvbiggaSApIHtcblx0XHRcdHZhciB0aGlzRGlzdGFuY2UgPSBNYXRoLmFicyggbm9ybVZhbHVlIC0gdGhhdC52YWx1ZXMoIGkgKSApO1xuXHRcdFx0aWYgKCAoIGRpc3RhbmNlID4gdGhpc0Rpc3RhbmNlICkgfHxcblx0XHRcdFx0KCBkaXN0YW5jZSA9PT0gdGhpc0Rpc3RhbmNlICYmXG5cdFx0XHRcdFx0KCBpID09PSB0aGF0Ll9sYXN0Q2hhbmdlZFZhbHVlIHx8IHRoYXQudmFsdWVzKCBpICkgPT09IG8ubWluICkgKSApIHtcblx0XHRcdFx0ZGlzdGFuY2UgPSB0aGlzRGlzdGFuY2U7XG5cdFx0XHRcdGNsb3Nlc3RIYW5kbGUgPSAkKCB0aGlzICk7XG5cdFx0XHRcdGluZGV4ID0gaTtcblx0XHRcdH1cblx0XHR9ICk7XG5cblx0XHRhbGxvd2VkID0gdGhpcy5fc3RhcnQoIGV2ZW50LCBpbmRleCApO1xuXHRcdGlmICggYWxsb3dlZCA9PT0gZmFsc2UgKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHRcdHRoaXMuX21vdXNlU2xpZGluZyA9IHRydWU7XG5cblx0XHR0aGlzLl9oYW5kbGVJbmRleCA9IGluZGV4O1xuXG5cdFx0dGhpcy5fYWRkQ2xhc3MoIGNsb3Nlc3RIYW5kbGUsIG51bGwsIFwidWktc3RhdGUtYWN0aXZlXCIgKTtcblx0XHRjbG9zZXN0SGFuZGxlLnRyaWdnZXIoIFwiZm9jdXNcIiApO1xuXG5cdFx0b2Zmc2V0ID0gY2xvc2VzdEhhbmRsZS5vZmZzZXQoKTtcblx0XHRtb3VzZU92ZXJIYW5kbGUgPSAhJCggZXZlbnQudGFyZ2V0ICkucGFyZW50cygpLmFkZEJhY2soKS5pcyggXCIudWktc2xpZGVyLWhhbmRsZVwiICk7XG5cdFx0dGhpcy5fY2xpY2tPZmZzZXQgPSBtb3VzZU92ZXJIYW5kbGUgPyB7IGxlZnQ6IDAsIHRvcDogMCB9IDoge1xuXHRcdFx0bGVmdDogZXZlbnQucGFnZVggLSBvZmZzZXQubGVmdCAtICggY2xvc2VzdEhhbmRsZS53aWR0aCgpIC8gMiApLFxuXHRcdFx0dG9wOiBldmVudC5wYWdlWSAtIG9mZnNldC50b3AgLVxuXHRcdFx0XHQoIGNsb3Nlc3RIYW5kbGUuaGVpZ2h0KCkgLyAyICkgLVxuXHRcdFx0XHQoIHBhcnNlSW50KCBjbG9zZXN0SGFuZGxlLmNzcyggXCJib3JkZXJUb3BXaWR0aFwiICksIDEwICkgfHwgMCApIC1cblx0XHRcdFx0KCBwYXJzZUludCggY2xvc2VzdEhhbmRsZS5jc3MoIFwiYm9yZGVyQm90dG9tV2lkdGhcIiApLCAxMCApIHx8IDAgKSArXG5cdFx0XHRcdCggcGFyc2VJbnQoIGNsb3Nlc3RIYW5kbGUuY3NzKCBcIm1hcmdpblRvcFwiICksIDEwICkgfHwgMCApXG5cdFx0fTtcblxuXHRcdGlmICggIXRoaXMuaGFuZGxlcy5oYXNDbGFzcyggXCJ1aS1zdGF0ZS1ob3ZlclwiICkgKSB7XG5cdFx0XHR0aGlzLl9zbGlkZSggZXZlbnQsIGluZGV4LCBub3JtVmFsdWUgKTtcblx0XHR9XG5cdFx0dGhpcy5fYW5pbWF0ZU9mZiA9IHRydWU7XG5cdFx0cmV0dXJuIHRydWU7XG5cdH0sXG5cblx0X21vdXNlU3RhcnQ6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0cnVlO1xuXHR9LFxuXG5cdF9tb3VzZURyYWc6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgcG9zaXRpb24gPSB7IHg6IGV2ZW50LnBhZ2VYLCB5OiBldmVudC5wYWdlWSB9LFxuXHRcdFx0bm9ybVZhbHVlID0gdGhpcy5fbm9ybVZhbHVlRnJvbU1vdXNlKCBwb3NpdGlvbiApO1xuXG5cdFx0dGhpcy5fc2xpZGUoIGV2ZW50LCB0aGlzLl9oYW5kbGVJbmRleCwgbm9ybVZhbHVlICk7XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0X21vdXNlU3RvcDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0aGlzLmhhbmRsZXMsIG51bGwsIFwidWktc3RhdGUtYWN0aXZlXCIgKTtcblx0XHR0aGlzLl9tb3VzZVNsaWRpbmcgPSBmYWxzZTtcblxuXHRcdHRoaXMuX3N0b3AoIGV2ZW50LCB0aGlzLl9oYW5kbGVJbmRleCApO1xuXHRcdHRoaXMuX2NoYW5nZSggZXZlbnQsIHRoaXMuX2hhbmRsZUluZGV4ICk7XG5cblx0XHR0aGlzLl9oYW5kbGVJbmRleCA9IG51bGw7XG5cdFx0dGhpcy5fY2xpY2tPZmZzZXQgPSBudWxsO1xuXHRcdHRoaXMuX2FuaW1hdGVPZmYgPSBmYWxzZTtcblxuXHRcdHJldHVybiBmYWxzZTtcblx0fSxcblxuXHRfZGV0ZWN0T3JpZW50YXRpb246IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMub3JpZW50YXRpb24gPSAoIHRoaXMub3B0aW9ucy5vcmllbnRhdGlvbiA9PT0gXCJ2ZXJ0aWNhbFwiICkgPyBcInZlcnRpY2FsXCIgOiBcImhvcml6b250YWxcIjtcblx0fSxcblxuXHRfbm9ybVZhbHVlRnJvbU1vdXNlOiBmdW5jdGlvbiggcG9zaXRpb24gKSB7XG5cdFx0dmFyIHBpeGVsVG90YWwsXG5cdFx0XHRwaXhlbE1vdXNlLFxuXHRcdFx0cGVyY2VudE1vdXNlLFxuXHRcdFx0dmFsdWVUb3RhbCxcblx0XHRcdHZhbHVlTW91c2U7XG5cblx0XHRpZiAoIHRoaXMub3JpZW50YXRpb24gPT09IFwiaG9yaXpvbnRhbFwiICkge1xuXHRcdFx0cGl4ZWxUb3RhbCA9IHRoaXMuZWxlbWVudFNpemUud2lkdGg7XG5cdFx0XHRwaXhlbE1vdXNlID0gcG9zaXRpb24ueCAtIHRoaXMuZWxlbWVudE9mZnNldC5sZWZ0IC1cblx0XHRcdFx0KCB0aGlzLl9jbGlja09mZnNldCA/IHRoaXMuX2NsaWNrT2Zmc2V0LmxlZnQgOiAwICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHBpeGVsVG90YWwgPSB0aGlzLmVsZW1lbnRTaXplLmhlaWdodDtcblx0XHRcdHBpeGVsTW91c2UgPSBwb3NpdGlvbi55IC0gdGhpcy5lbGVtZW50T2Zmc2V0LnRvcCAtXG5cdFx0XHRcdCggdGhpcy5fY2xpY2tPZmZzZXQgPyB0aGlzLl9jbGlja09mZnNldC50b3AgOiAwICk7XG5cdFx0fVxuXG5cdFx0cGVyY2VudE1vdXNlID0gKCBwaXhlbE1vdXNlIC8gcGl4ZWxUb3RhbCApO1xuXHRcdGlmICggcGVyY2VudE1vdXNlID4gMSApIHtcblx0XHRcdHBlcmNlbnRNb3VzZSA9IDE7XG5cdFx0fVxuXHRcdGlmICggcGVyY2VudE1vdXNlIDwgMCApIHtcblx0XHRcdHBlcmNlbnRNb3VzZSA9IDA7XG5cdFx0fVxuXHRcdGlmICggdGhpcy5vcmllbnRhdGlvbiA9PT0gXCJ2ZXJ0aWNhbFwiICkge1xuXHRcdFx0cGVyY2VudE1vdXNlID0gMSAtIHBlcmNlbnRNb3VzZTtcblx0XHR9XG5cblx0XHR2YWx1ZVRvdGFsID0gdGhpcy5fdmFsdWVNYXgoKSAtIHRoaXMuX3ZhbHVlTWluKCk7XG5cdFx0dmFsdWVNb3VzZSA9IHRoaXMuX3ZhbHVlTWluKCkgKyBwZXJjZW50TW91c2UgKiB2YWx1ZVRvdGFsO1xuXG5cdFx0cmV0dXJuIHRoaXMuX3RyaW1BbGlnblZhbHVlKCB2YWx1ZU1vdXNlICk7XG5cdH0sXG5cblx0X3VpSGFzaDogZnVuY3Rpb24oIGluZGV4LCB2YWx1ZSwgdmFsdWVzICkge1xuXHRcdHZhciB1aUhhc2ggPSB7XG5cdFx0XHRoYW5kbGU6IHRoaXMuaGFuZGxlc1sgaW5kZXggXSxcblx0XHRcdGhhbmRsZUluZGV4OiBpbmRleCxcblx0XHRcdHZhbHVlOiB2YWx1ZSAhPT0gdW5kZWZpbmVkID8gdmFsdWUgOiB0aGlzLnZhbHVlKClcblx0XHR9O1xuXG5cdFx0aWYgKCB0aGlzLl9oYXNNdWx0aXBsZVZhbHVlcygpICkge1xuXHRcdFx0dWlIYXNoLnZhbHVlID0gdmFsdWUgIT09IHVuZGVmaW5lZCA/IHZhbHVlIDogdGhpcy52YWx1ZXMoIGluZGV4ICk7XG5cdFx0XHR1aUhhc2gudmFsdWVzID0gdmFsdWVzIHx8IHRoaXMudmFsdWVzKCk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHVpSGFzaDtcblx0fSxcblxuXHRfaGFzTXVsdGlwbGVWYWx1ZXM6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMudmFsdWVzICYmIHRoaXMub3B0aW9ucy52YWx1ZXMubGVuZ3RoO1xuXHR9LFxuXG5cdF9zdGFydDogZnVuY3Rpb24oIGV2ZW50LCBpbmRleCApIHtcblx0XHRyZXR1cm4gdGhpcy5fdHJpZ2dlciggXCJzdGFydFwiLCBldmVudCwgdGhpcy5fdWlIYXNoKCBpbmRleCApICk7XG5cdH0sXG5cblx0X3NsaWRlOiBmdW5jdGlvbiggZXZlbnQsIGluZGV4LCBuZXdWYWwgKSB7XG5cdFx0dmFyIGFsbG93ZWQsIG90aGVyVmFsLFxuXHRcdFx0Y3VycmVudFZhbHVlID0gdGhpcy52YWx1ZSgpLFxuXHRcdFx0bmV3VmFsdWVzID0gdGhpcy52YWx1ZXMoKTtcblxuXHRcdGlmICggdGhpcy5faGFzTXVsdGlwbGVWYWx1ZXMoKSApIHtcblx0XHRcdG90aGVyVmFsID0gdGhpcy52YWx1ZXMoIGluZGV4ID8gMCA6IDEgKTtcblx0XHRcdGN1cnJlbnRWYWx1ZSA9IHRoaXMudmFsdWVzKCBpbmRleCApO1xuXG5cdFx0XHRpZiAoIHRoaXMub3B0aW9ucy52YWx1ZXMubGVuZ3RoID09PSAyICYmIHRoaXMub3B0aW9ucy5yYW5nZSA9PT0gdHJ1ZSApIHtcblx0XHRcdFx0bmV3VmFsID0gIGluZGV4ID09PSAwID8gTWF0aC5taW4oIG90aGVyVmFsLCBuZXdWYWwgKSA6IE1hdGgubWF4KCBvdGhlclZhbCwgbmV3VmFsICk7XG5cdFx0XHR9XG5cblx0XHRcdG5ld1ZhbHVlc1sgaW5kZXggXSA9IG5ld1ZhbDtcblx0XHR9XG5cblx0XHRpZiAoIG5ld1ZhbCA9PT0gY3VycmVudFZhbHVlICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGFsbG93ZWQgPSB0aGlzLl90cmlnZ2VyKCBcInNsaWRlXCIsIGV2ZW50LCB0aGlzLl91aUhhc2goIGluZGV4LCBuZXdWYWwsIG5ld1ZhbHVlcyApICk7XG5cblx0XHQvLyBBIHNsaWRlIGNhbiBiZSBjYW5jZWxlZCBieSByZXR1cm5pbmcgZmFsc2UgZnJvbSB0aGUgc2xpZGUgY2FsbGJhY2tcblx0XHRpZiAoIGFsbG93ZWQgPT09IGZhbHNlICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGlmICggdGhpcy5faGFzTXVsdGlwbGVWYWx1ZXMoKSApIHtcblx0XHRcdHRoaXMudmFsdWVzKCBpbmRleCwgbmV3VmFsICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMudmFsdWUoIG5ld1ZhbCApO1xuXHRcdH1cblx0fSxcblxuXHRfc3RvcDogZnVuY3Rpb24oIGV2ZW50LCBpbmRleCApIHtcblx0XHR0aGlzLl90cmlnZ2VyKCBcInN0b3BcIiwgZXZlbnQsIHRoaXMuX3VpSGFzaCggaW5kZXggKSApO1xuXHR9LFxuXG5cdF9jaGFuZ2U6IGZ1bmN0aW9uKCBldmVudCwgaW5kZXggKSB7XG5cdFx0aWYgKCAhdGhpcy5fa2V5U2xpZGluZyAmJiAhdGhpcy5fbW91c2VTbGlkaW5nICkge1xuXG5cdFx0XHQvL3N0b3JlIHRoZSBsYXN0IGNoYW5nZWQgdmFsdWUgaW5kZXggZm9yIHJlZmVyZW5jZSB3aGVuIGhhbmRsZXMgb3ZlcmxhcFxuXHRcdFx0dGhpcy5fbGFzdENoYW5nZWRWYWx1ZSA9IGluZGV4O1xuXHRcdFx0dGhpcy5fdHJpZ2dlciggXCJjaGFuZ2VcIiwgZXZlbnQsIHRoaXMuX3VpSGFzaCggaW5kZXggKSApO1xuXHRcdH1cblx0fSxcblxuXHR2YWx1ZTogZnVuY3Rpb24oIG5ld1ZhbHVlICkge1xuXHRcdGlmICggYXJndW1lbnRzLmxlbmd0aCApIHtcblx0XHRcdHRoaXMub3B0aW9ucy52YWx1ZSA9IHRoaXMuX3RyaW1BbGlnblZhbHVlKCBuZXdWYWx1ZSApO1xuXHRcdFx0dGhpcy5fcmVmcmVzaFZhbHVlKCk7XG5cdFx0XHR0aGlzLl9jaGFuZ2UoIG51bGwsIDAgKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcy5fdmFsdWUoKTtcblx0fSxcblxuXHR2YWx1ZXM6IGZ1bmN0aW9uKCBpbmRleCwgbmV3VmFsdWUgKSB7XG5cdFx0dmFyIHZhbHMsXG5cdFx0XHRuZXdWYWx1ZXMsXG5cdFx0XHRpO1xuXG5cdFx0aWYgKCBhcmd1bWVudHMubGVuZ3RoID4gMSApIHtcblx0XHRcdHRoaXMub3B0aW9ucy52YWx1ZXNbIGluZGV4IF0gPSB0aGlzLl90cmltQWxpZ25WYWx1ZSggbmV3VmFsdWUgKTtcblx0XHRcdHRoaXMuX3JlZnJlc2hWYWx1ZSgpO1xuXHRcdFx0dGhpcy5fY2hhbmdlKCBudWxsLCBpbmRleCApO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGlmICggYXJndW1lbnRzLmxlbmd0aCApIHtcblx0XHRcdGlmICggJC5pc0FycmF5KCBhcmd1bWVudHNbIDAgXSApICkge1xuXHRcdFx0XHR2YWxzID0gdGhpcy5vcHRpb25zLnZhbHVlcztcblx0XHRcdFx0bmV3VmFsdWVzID0gYXJndW1lbnRzWyAwIF07XG5cdFx0XHRcdGZvciAoIGkgPSAwOyBpIDwgdmFscy5sZW5ndGg7IGkgKz0gMSApIHtcblx0XHRcdFx0XHR2YWxzWyBpIF0gPSB0aGlzLl90cmltQWxpZ25WYWx1ZSggbmV3VmFsdWVzWyBpIF0gKTtcblx0XHRcdFx0XHR0aGlzLl9jaGFuZ2UoIG51bGwsIGkgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHR0aGlzLl9yZWZyZXNoVmFsdWUoKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGlmICggdGhpcy5faGFzTXVsdGlwbGVWYWx1ZXMoKSApIHtcblx0XHRcdFx0XHRyZXR1cm4gdGhpcy5fdmFsdWVzKCBpbmRleCApO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHJldHVybiB0aGlzLnZhbHVlKCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0cmV0dXJuIHRoaXMuX3ZhbHVlcygpO1xuXHRcdH1cblx0fSxcblxuXHRfc2V0T3B0aW9uOiBmdW5jdGlvbigga2V5LCB2YWx1ZSApIHtcblx0XHR2YXIgaSxcblx0XHRcdHZhbHNMZW5ndGggPSAwO1xuXG5cdFx0aWYgKCBrZXkgPT09IFwicmFuZ2VcIiAmJiB0aGlzLm9wdGlvbnMucmFuZ2UgPT09IHRydWUgKSB7XG5cdFx0XHRpZiAoIHZhbHVlID09PSBcIm1pblwiICkge1xuXHRcdFx0XHR0aGlzLm9wdGlvbnMudmFsdWUgPSB0aGlzLl92YWx1ZXMoIDAgKTtcblx0XHRcdFx0dGhpcy5vcHRpb25zLnZhbHVlcyA9IG51bGw7XG5cdFx0XHR9IGVsc2UgaWYgKCB2YWx1ZSA9PT0gXCJtYXhcIiApIHtcblx0XHRcdFx0dGhpcy5vcHRpb25zLnZhbHVlID0gdGhpcy5fdmFsdWVzKCB0aGlzLm9wdGlvbnMudmFsdWVzLmxlbmd0aCAtIDEgKTtcblx0XHRcdFx0dGhpcy5vcHRpb25zLnZhbHVlcyA9IG51bGw7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKCAkLmlzQXJyYXkoIHRoaXMub3B0aW9ucy52YWx1ZXMgKSApIHtcblx0XHRcdHZhbHNMZW5ndGggPSB0aGlzLm9wdGlvbnMudmFsdWVzLmxlbmd0aDtcblx0XHR9XG5cblx0XHR0aGlzLl9zdXBlcigga2V5LCB2YWx1ZSApO1xuXG5cdFx0c3dpdGNoICgga2V5ICkge1xuXHRcdFx0Y2FzZSBcIm9yaWVudGF0aW9uXCI6XG5cdFx0XHRcdHRoaXMuX2RldGVjdE9yaWVudGF0aW9uKCk7XG5cdFx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCBcInVpLXNsaWRlci1ob3Jpem9udGFsIHVpLXNsaWRlci12ZXJ0aWNhbFwiIClcblx0XHRcdFx0XHQuX2FkZENsYXNzKCBcInVpLXNsaWRlci1cIiArIHRoaXMub3JpZW50YXRpb24gKTtcblx0XHRcdFx0dGhpcy5fcmVmcmVzaFZhbHVlKCk7XG5cdFx0XHRcdGlmICggdGhpcy5vcHRpb25zLnJhbmdlICkge1xuXHRcdFx0XHRcdHRoaXMuX3JlZnJlc2hSYW5nZSggdmFsdWUgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIFJlc2V0IHBvc2l0aW9uaW5nIGZyb20gcHJldmlvdXMgb3JpZW50YXRpb25cblx0XHRcdFx0dGhpcy5oYW5kbGVzLmNzcyggdmFsdWUgPT09IFwiaG9yaXpvbnRhbFwiID8gXCJib3R0b21cIiA6IFwibGVmdFwiLCBcIlwiICk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0Y2FzZSBcInZhbHVlXCI6XG5cdFx0XHRcdHRoaXMuX2FuaW1hdGVPZmYgPSB0cnVlO1xuXHRcdFx0XHR0aGlzLl9yZWZyZXNoVmFsdWUoKTtcblx0XHRcdFx0dGhpcy5fY2hhbmdlKCBudWxsLCAwICk7XG5cdFx0XHRcdHRoaXMuX2FuaW1hdGVPZmYgPSBmYWxzZTtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHRjYXNlIFwidmFsdWVzXCI6XG5cdFx0XHRcdHRoaXMuX2FuaW1hdGVPZmYgPSB0cnVlO1xuXHRcdFx0XHR0aGlzLl9yZWZyZXNoVmFsdWUoKTtcblxuXHRcdFx0XHQvLyBTdGFydCBmcm9tIHRoZSBsYXN0IGhhbmRsZSB0byBwcmV2ZW50IHVucmVhY2hhYmxlIGhhbmRsZXMgKCM5MDQ2KVxuXHRcdFx0XHRmb3IgKCBpID0gdmFsc0xlbmd0aCAtIDE7IGkgPj0gMDsgaS0tICkge1xuXHRcdFx0XHRcdHRoaXMuX2NoYW5nZSggbnVsbCwgaSApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHRoaXMuX2FuaW1hdGVPZmYgPSBmYWxzZTtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHRjYXNlIFwic3RlcFwiOlxuXHRcdFx0Y2FzZSBcIm1pblwiOlxuXHRcdFx0Y2FzZSBcIm1heFwiOlxuXHRcdFx0XHR0aGlzLl9hbmltYXRlT2ZmID0gdHJ1ZTtcblx0XHRcdFx0dGhpcy5fY2FsY3VsYXRlTmV3TWF4KCk7XG5cdFx0XHRcdHRoaXMuX3JlZnJlc2hWYWx1ZSgpO1xuXHRcdFx0XHR0aGlzLl9hbmltYXRlT2ZmID0gZmFsc2U7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0Y2FzZSBcInJhbmdlXCI6XG5cdFx0XHRcdHRoaXMuX2FuaW1hdGVPZmYgPSB0cnVlO1xuXHRcdFx0XHR0aGlzLl9yZWZyZXNoKCk7XG5cdFx0XHRcdHRoaXMuX2FuaW1hdGVPZmYgPSBmYWxzZTtcblx0XHRcdFx0YnJlYWs7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRPcHRpb25EaXNhYmxlZDogZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdHRoaXMuX3N1cGVyKCB2YWx1ZSApO1xuXG5cdFx0dGhpcy5fdG9nZ2xlQ2xhc3MoIG51bGwsIFwidWktc3RhdGUtZGlzYWJsZWRcIiwgISF2YWx1ZSApO1xuXHR9LFxuXG5cdC8vaW50ZXJuYWwgdmFsdWUgZ2V0dGVyXG5cdC8vIF92YWx1ZSgpIHJldHVybnMgdmFsdWUgdHJpbW1lZCBieSBtaW4gYW5kIG1heCwgYWxpZ25lZCBieSBzdGVwXG5cdF92YWx1ZTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHZhbCA9IHRoaXMub3B0aW9ucy52YWx1ZTtcblx0XHR2YWwgPSB0aGlzLl90cmltQWxpZ25WYWx1ZSggdmFsICk7XG5cblx0XHRyZXR1cm4gdmFsO1xuXHR9LFxuXG5cdC8vaW50ZXJuYWwgdmFsdWVzIGdldHRlclxuXHQvLyBfdmFsdWVzKCkgcmV0dXJucyBhcnJheSBvZiB2YWx1ZXMgdHJpbW1lZCBieSBtaW4gYW5kIG1heCwgYWxpZ25lZCBieSBzdGVwXG5cdC8vIF92YWx1ZXMoIGluZGV4ICkgcmV0dXJucyBzaW5nbGUgdmFsdWUgdHJpbW1lZCBieSBtaW4gYW5kIG1heCwgYWxpZ25lZCBieSBzdGVwXG5cdF92YWx1ZXM6IGZ1bmN0aW9uKCBpbmRleCApIHtcblx0XHR2YXIgdmFsLFxuXHRcdFx0dmFscyxcblx0XHRcdGk7XG5cblx0XHRpZiAoIGFyZ3VtZW50cy5sZW5ndGggKSB7XG5cdFx0XHR2YWwgPSB0aGlzLm9wdGlvbnMudmFsdWVzWyBpbmRleCBdO1xuXHRcdFx0dmFsID0gdGhpcy5fdHJpbUFsaWduVmFsdWUoIHZhbCApO1xuXG5cdFx0XHRyZXR1cm4gdmFsO1xuXHRcdH0gZWxzZSBpZiAoIHRoaXMuX2hhc011bHRpcGxlVmFsdWVzKCkgKSB7XG5cblx0XHRcdC8vIC5zbGljZSgpIGNyZWF0ZXMgYSBjb3B5IG9mIHRoZSBhcnJheVxuXHRcdFx0Ly8gdGhpcyBjb3B5IGdldHMgdHJpbW1lZCBieSBtaW4gYW5kIG1heCBhbmQgdGhlbiByZXR1cm5lZFxuXHRcdFx0dmFscyA9IHRoaXMub3B0aW9ucy52YWx1ZXMuc2xpY2UoKTtcblx0XHRcdGZvciAoIGkgPSAwOyBpIDwgdmFscy5sZW5ndGg7IGkgKz0gMSApIHtcblx0XHRcdFx0dmFsc1sgaSBdID0gdGhpcy5fdHJpbUFsaWduVmFsdWUoIHZhbHNbIGkgXSApO1xuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gdmFscztcblx0XHR9IGVsc2Uge1xuXHRcdFx0cmV0dXJuIFtdO1xuXHRcdH1cblx0fSxcblxuXHQvLyBSZXR1cm5zIHRoZSBzdGVwLWFsaWduZWQgdmFsdWUgdGhhdCB2YWwgaXMgY2xvc2VzdCB0bywgYmV0d2VlbiAoaW5jbHVzaXZlKSBtaW4gYW5kIG1heFxuXHRfdHJpbUFsaWduVmFsdWU6IGZ1bmN0aW9uKCB2YWwgKSB7XG5cdFx0aWYgKCB2YWwgPD0gdGhpcy5fdmFsdWVNaW4oKSApIHtcblx0XHRcdHJldHVybiB0aGlzLl92YWx1ZU1pbigpO1xuXHRcdH1cblx0XHRpZiAoIHZhbCA+PSB0aGlzLl92YWx1ZU1heCgpICkge1xuXHRcdFx0cmV0dXJuIHRoaXMuX3ZhbHVlTWF4KCk7XG5cdFx0fVxuXHRcdHZhciBzdGVwID0gKCB0aGlzLm9wdGlvbnMuc3RlcCA+IDAgKSA/IHRoaXMub3B0aW9ucy5zdGVwIDogMSxcblx0XHRcdHZhbE1vZFN0ZXAgPSAoIHZhbCAtIHRoaXMuX3ZhbHVlTWluKCkgKSAlIHN0ZXAsXG5cdFx0XHRhbGlnblZhbHVlID0gdmFsIC0gdmFsTW9kU3RlcDtcblxuXHRcdGlmICggTWF0aC5hYnMoIHZhbE1vZFN0ZXAgKSAqIDIgPj0gc3RlcCApIHtcblx0XHRcdGFsaWduVmFsdWUgKz0gKCB2YWxNb2RTdGVwID4gMCApID8gc3RlcCA6ICggLXN0ZXAgKTtcblx0XHR9XG5cblx0XHQvLyBTaW5jZSBKYXZhU2NyaXB0IGhhcyBwcm9ibGVtcyB3aXRoIGxhcmdlIGZsb2F0cywgcm91bmRcblx0XHQvLyB0aGUgZmluYWwgdmFsdWUgdG8gNSBkaWdpdHMgYWZ0ZXIgdGhlIGRlY2ltYWwgcG9pbnQgKHNlZSAjNDEyNClcblx0XHRyZXR1cm4gcGFyc2VGbG9hdCggYWxpZ25WYWx1ZS50b0ZpeGVkKCA1ICkgKTtcblx0fSxcblxuXHRfY2FsY3VsYXRlTmV3TWF4OiBmdW5jdGlvbigpIHtcblx0XHR2YXIgbWF4ID0gdGhpcy5vcHRpb25zLm1heCxcblx0XHRcdG1pbiA9IHRoaXMuX3ZhbHVlTWluKCksXG5cdFx0XHRzdGVwID0gdGhpcy5vcHRpb25zLnN0ZXAsXG5cdFx0XHRhYm92ZU1pbiA9IE1hdGgucm91bmQoICggbWF4IC0gbWluICkgLyBzdGVwICkgKiBzdGVwO1xuXHRcdG1heCA9IGFib3ZlTWluICsgbWluO1xuXHRcdGlmICggbWF4ID4gdGhpcy5vcHRpb25zLm1heCApIHtcblxuXHRcdFx0Ly9JZiBtYXggaXMgbm90IGRpdmlzaWJsZSBieSBzdGVwLCByb3VuZGluZyBvZmYgbWF5IGluY3JlYXNlIGl0cyB2YWx1ZVxuXHRcdFx0bWF4IC09IHN0ZXA7XG5cdFx0fVxuXHRcdHRoaXMubWF4ID0gcGFyc2VGbG9hdCggbWF4LnRvRml4ZWQoIHRoaXMuX3ByZWNpc2lvbigpICkgKTtcblx0fSxcblxuXHRfcHJlY2lzaW9uOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgcHJlY2lzaW9uID0gdGhpcy5fcHJlY2lzaW9uT2YoIHRoaXMub3B0aW9ucy5zdGVwICk7XG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMubWluICE9PSBudWxsICkge1xuXHRcdFx0cHJlY2lzaW9uID0gTWF0aC5tYXgoIHByZWNpc2lvbiwgdGhpcy5fcHJlY2lzaW9uT2YoIHRoaXMub3B0aW9ucy5taW4gKSApO1xuXHRcdH1cblx0XHRyZXR1cm4gcHJlY2lzaW9uO1xuXHR9LFxuXG5cdF9wcmVjaXNpb25PZjogZnVuY3Rpb24oIG51bSApIHtcblx0XHR2YXIgc3RyID0gbnVtLnRvU3RyaW5nKCksXG5cdFx0XHRkZWNpbWFsID0gc3RyLmluZGV4T2YoIFwiLlwiICk7XG5cdFx0cmV0dXJuIGRlY2ltYWwgPT09IC0xID8gMCA6IHN0ci5sZW5ndGggLSBkZWNpbWFsIC0gMTtcblx0fSxcblxuXHRfdmFsdWVNaW46IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLm9wdGlvbnMubWluO1xuXHR9LFxuXG5cdF92YWx1ZU1heDogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMubWF4O1xuXHR9LFxuXG5cdF9yZWZyZXNoUmFuZ2U6IGZ1bmN0aW9uKCBvcmllbnRhdGlvbiApIHtcblx0XHRpZiAoIG9yaWVudGF0aW9uID09PSBcInZlcnRpY2FsXCIgKSB7XG5cdFx0XHR0aGlzLnJhbmdlLmNzcyggeyBcIndpZHRoXCI6IFwiXCIsIFwibGVmdFwiOiBcIlwiIH0gKTtcblx0XHR9XG5cdFx0aWYgKCBvcmllbnRhdGlvbiA9PT0gXCJob3Jpem9udGFsXCIgKSB7XG5cdFx0XHR0aGlzLnJhbmdlLmNzcyggeyBcImhlaWdodFwiOiBcIlwiLCBcImJvdHRvbVwiOiBcIlwiIH0gKTtcblx0XHR9XG5cdH0sXG5cblx0X3JlZnJlc2hWYWx1ZTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGxhc3RWYWxQZXJjZW50LCB2YWxQZXJjZW50LCB2YWx1ZSwgdmFsdWVNaW4sIHZhbHVlTWF4LFxuXHRcdFx0b1JhbmdlID0gdGhpcy5vcHRpb25zLnJhbmdlLFxuXHRcdFx0byA9IHRoaXMub3B0aW9ucyxcblx0XHRcdHRoYXQgPSB0aGlzLFxuXHRcdFx0YW5pbWF0ZSA9ICggIXRoaXMuX2FuaW1hdGVPZmYgKSA/IG8uYW5pbWF0ZSA6IGZhbHNlLFxuXHRcdFx0X3NldCA9IHt9O1xuXG5cdFx0aWYgKCB0aGlzLl9oYXNNdWx0aXBsZVZhbHVlcygpICkge1xuXHRcdFx0dGhpcy5oYW5kbGVzLmVhY2goIGZ1bmN0aW9uKCBpICkge1xuXHRcdFx0XHR2YWxQZXJjZW50ID0gKCB0aGF0LnZhbHVlcyggaSApIC0gdGhhdC5fdmFsdWVNaW4oKSApIC8gKCB0aGF0Ll92YWx1ZU1heCgpIC1cblx0XHRcdFx0XHR0aGF0Ll92YWx1ZU1pbigpICkgKiAxMDA7XG5cdFx0XHRcdF9zZXRbIHRoYXQub3JpZW50YXRpb24gPT09IFwiaG9yaXpvbnRhbFwiID8gXCJsZWZ0XCIgOiBcImJvdHRvbVwiIF0gPSB2YWxQZXJjZW50ICsgXCIlXCI7XG5cdFx0XHRcdCQoIHRoaXMgKS5zdG9wKCAxLCAxIClbIGFuaW1hdGUgPyBcImFuaW1hdGVcIiA6IFwiY3NzXCIgXSggX3NldCwgby5hbmltYXRlICk7XG5cdFx0XHRcdGlmICggdGhhdC5vcHRpb25zLnJhbmdlID09PSB0cnVlICkge1xuXHRcdFx0XHRcdGlmICggdGhhdC5vcmllbnRhdGlvbiA9PT0gXCJob3Jpem9udGFsXCIgKSB7XG5cdFx0XHRcdFx0XHRpZiAoIGkgPT09IDAgKSB7XG5cdFx0XHRcdFx0XHRcdHRoYXQucmFuZ2Uuc3RvcCggMSwgMSApWyBhbmltYXRlID8gXCJhbmltYXRlXCIgOiBcImNzc1wiIF0oIHtcblx0XHRcdFx0XHRcdFx0XHRsZWZ0OiB2YWxQZXJjZW50ICsgXCIlXCJcblx0XHRcdFx0XHRcdFx0fSwgby5hbmltYXRlICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRpZiAoIGkgPT09IDEgKSB7XG5cdFx0XHRcdFx0XHRcdHRoYXQucmFuZ2VbIGFuaW1hdGUgPyBcImFuaW1hdGVcIiA6IFwiY3NzXCIgXSgge1xuXHRcdFx0XHRcdFx0XHRcdHdpZHRoOiAoIHZhbFBlcmNlbnQgLSBsYXN0VmFsUGVyY2VudCApICsgXCIlXCJcblx0XHRcdFx0XHRcdFx0fSwge1xuXHRcdFx0XHRcdFx0XHRcdHF1ZXVlOiBmYWxzZSxcblx0XHRcdFx0XHRcdFx0XHRkdXJhdGlvbjogby5hbmltYXRlXG5cdFx0XHRcdFx0XHRcdH0gKTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0aWYgKCBpID09PSAwICkge1xuXHRcdFx0XHRcdFx0XHR0aGF0LnJhbmdlLnN0b3AoIDEsIDEgKVsgYW5pbWF0ZSA/IFwiYW5pbWF0ZVwiIDogXCJjc3NcIiBdKCB7XG5cdFx0XHRcdFx0XHRcdFx0Ym90dG9tOiAoIHZhbFBlcmNlbnQgKSArIFwiJVwiXG5cdFx0XHRcdFx0XHRcdH0sIG8uYW5pbWF0ZSApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0aWYgKCBpID09PSAxICkge1xuXHRcdFx0XHRcdFx0XHR0aGF0LnJhbmdlWyBhbmltYXRlID8gXCJhbmltYXRlXCIgOiBcImNzc1wiIF0oIHtcblx0XHRcdFx0XHRcdFx0XHRoZWlnaHQ6ICggdmFsUGVyY2VudCAtIGxhc3RWYWxQZXJjZW50ICkgKyBcIiVcIlxuXHRcdFx0XHRcdFx0XHR9LCB7XG5cdFx0XHRcdFx0XHRcdFx0cXVldWU6IGZhbHNlLFxuXHRcdFx0XHRcdFx0XHRcdGR1cmF0aW9uOiBvLmFuaW1hdGVcblx0XHRcdFx0XHRcdFx0fSApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0XHRsYXN0VmFsUGVyY2VudCA9IHZhbFBlcmNlbnQ7XG5cdFx0XHR9ICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHZhbHVlID0gdGhpcy52YWx1ZSgpO1xuXHRcdFx0dmFsdWVNaW4gPSB0aGlzLl92YWx1ZU1pbigpO1xuXHRcdFx0dmFsdWVNYXggPSB0aGlzLl92YWx1ZU1heCgpO1xuXHRcdFx0dmFsUGVyY2VudCA9ICggdmFsdWVNYXggIT09IHZhbHVlTWluICkgP1xuXHRcdFx0XHRcdCggdmFsdWUgLSB2YWx1ZU1pbiApIC8gKCB2YWx1ZU1heCAtIHZhbHVlTWluICkgKiAxMDAgOlxuXHRcdFx0XHRcdDA7XG5cdFx0XHRfc2V0WyB0aGlzLm9yaWVudGF0aW9uID09PSBcImhvcml6b250YWxcIiA/IFwibGVmdFwiIDogXCJib3R0b21cIiBdID0gdmFsUGVyY2VudCArIFwiJVwiO1xuXHRcdFx0dGhpcy5oYW5kbGUuc3RvcCggMSwgMSApWyBhbmltYXRlID8gXCJhbmltYXRlXCIgOiBcImNzc1wiIF0oIF9zZXQsIG8uYW5pbWF0ZSApO1xuXG5cdFx0XHRpZiAoIG9SYW5nZSA9PT0gXCJtaW5cIiAmJiB0aGlzLm9yaWVudGF0aW9uID09PSBcImhvcml6b250YWxcIiApIHtcblx0XHRcdFx0dGhpcy5yYW5nZS5zdG9wKCAxLCAxIClbIGFuaW1hdGUgPyBcImFuaW1hdGVcIiA6IFwiY3NzXCIgXSgge1xuXHRcdFx0XHRcdHdpZHRoOiB2YWxQZXJjZW50ICsgXCIlXCJcblx0XHRcdFx0fSwgby5hbmltYXRlICk7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIG9SYW5nZSA9PT0gXCJtYXhcIiAmJiB0aGlzLm9yaWVudGF0aW9uID09PSBcImhvcml6b250YWxcIiApIHtcblx0XHRcdFx0dGhpcy5yYW5nZS5zdG9wKCAxLCAxIClbIGFuaW1hdGUgPyBcImFuaW1hdGVcIiA6IFwiY3NzXCIgXSgge1xuXHRcdFx0XHRcdHdpZHRoOiAoIDEwMCAtIHZhbFBlcmNlbnQgKSArIFwiJVwiXG5cdFx0XHRcdH0sIG8uYW5pbWF0ZSApO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCBvUmFuZ2UgPT09IFwibWluXCIgJiYgdGhpcy5vcmllbnRhdGlvbiA9PT0gXCJ2ZXJ0aWNhbFwiICkge1xuXHRcdFx0XHR0aGlzLnJhbmdlLnN0b3AoIDEsIDEgKVsgYW5pbWF0ZSA/IFwiYW5pbWF0ZVwiIDogXCJjc3NcIiBdKCB7XG5cdFx0XHRcdFx0aGVpZ2h0OiB2YWxQZXJjZW50ICsgXCIlXCJcblx0XHRcdFx0fSwgby5hbmltYXRlICk7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIG9SYW5nZSA9PT0gXCJtYXhcIiAmJiB0aGlzLm9yaWVudGF0aW9uID09PSBcInZlcnRpY2FsXCIgKSB7XG5cdFx0XHRcdHRoaXMucmFuZ2Uuc3RvcCggMSwgMSApWyBhbmltYXRlID8gXCJhbmltYXRlXCIgOiBcImNzc1wiIF0oIHtcblx0XHRcdFx0XHRoZWlnaHQ6ICggMTAwIC0gdmFsUGVyY2VudCApICsgXCIlXCJcblx0XHRcdFx0fSwgby5hbmltYXRlICk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXG5cdF9oYW5kbGVFdmVudHM6IHtcblx0XHRrZXlkb3duOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHR2YXIgYWxsb3dlZCwgY3VyVmFsLCBuZXdWYWwsIHN0ZXAsXG5cdFx0XHRcdGluZGV4ID0gJCggZXZlbnQudGFyZ2V0ICkuZGF0YSggXCJ1aS1zbGlkZXItaGFuZGxlLWluZGV4XCIgKTtcblxuXHRcdFx0c3dpdGNoICggZXZlbnQua2V5Q29kZSApIHtcblx0XHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuSE9NRTpcblx0XHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuRU5EOlxuXHRcdFx0XHRjYXNlICQudWkua2V5Q29kZS5QQUdFX1VQOlxuXHRcdFx0XHRjYXNlICQudWkua2V5Q29kZS5QQUdFX0RPV046XG5cdFx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLlVQOlxuXHRcdFx0XHRjYXNlICQudWkua2V5Q29kZS5SSUdIVDpcblx0XHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuRE9XTjpcblx0XHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuTEVGVDpcblx0XHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0XHRcdGlmICggIXRoaXMuX2tleVNsaWRpbmcgKSB7XG5cdFx0XHRcdFx0XHR0aGlzLl9rZXlTbGlkaW5nID0gdHJ1ZTtcblx0XHRcdFx0XHRcdHRoaXMuX2FkZENsYXNzKCAkKCBldmVudC50YXJnZXQgKSwgbnVsbCwgXCJ1aS1zdGF0ZS1hY3RpdmVcIiApO1xuXHRcdFx0XHRcdFx0YWxsb3dlZCA9IHRoaXMuX3N0YXJ0KCBldmVudCwgaW5kZXggKTtcblx0XHRcdFx0XHRcdGlmICggYWxsb3dlZCA9PT0gZmFsc2UgKSB7XG5cdFx0XHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cblx0XHRcdHN0ZXAgPSB0aGlzLm9wdGlvbnMuc3RlcDtcblx0XHRcdGlmICggdGhpcy5faGFzTXVsdGlwbGVWYWx1ZXMoKSApIHtcblx0XHRcdFx0Y3VyVmFsID0gbmV3VmFsID0gdGhpcy52YWx1ZXMoIGluZGV4ICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRjdXJWYWwgPSBuZXdWYWwgPSB0aGlzLnZhbHVlKCk7XG5cdFx0XHR9XG5cblx0XHRcdHN3aXRjaCAoIGV2ZW50LmtleUNvZGUgKSB7XG5cdFx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLkhPTUU6XG5cdFx0XHRcdFx0bmV3VmFsID0gdGhpcy5fdmFsdWVNaW4oKTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuRU5EOlxuXHRcdFx0XHRcdG5ld1ZhbCA9IHRoaXMuX3ZhbHVlTWF4KCk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLlBBR0VfVVA6XG5cdFx0XHRcdFx0bmV3VmFsID0gdGhpcy5fdHJpbUFsaWduVmFsdWUoXG5cdFx0XHRcdFx0XHRjdXJWYWwgKyAoICggdGhpcy5fdmFsdWVNYXgoKSAtIHRoaXMuX3ZhbHVlTWluKCkgKSAvIHRoaXMubnVtUGFnZXMgKVxuXHRcdFx0XHRcdCk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLlBBR0VfRE9XTjpcblx0XHRcdFx0XHRuZXdWYWwgPSB0aGlzLl90cmltQWxpZ25WYWx1ZShcblx0XHRcdFx0XHRcdGN1clZhbCAtICggKCB0aGlzLl92YWx1ZU1heCgpIC0gdGhpcy5fdmFsdWVNaW4oKSApIC8gdGhpcy5udW1QYWdlcyApICk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgJC51aS5rZXlDb2RlLlVQOlxuXHRcdFx0XHRjYXNlICQudWkua2V5Q29kZS5SSUdIVDpcblx0XHRcdFx0XHRpZiAoIGN1clZhbCA9PT0gdGhpcy5fdmFsdWVNYXgoKSApIHtcblx0XHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0bmV3VmFsID0gdGhpcy5fdHJpbUFsaWduVmFsdWUoIGN1clZhbCArIHN0ZXAgKTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuRE9XTjpcblx0XHRcdFx0Y2FzZSAkLnVpLmtleUNvZGUuTEVGVDpcblx0XHRcdFx0XHRpZiAoIGN1clZhbCA9PT0gdGhpcy5fdmFsdWVNaW4oKSApIHtcblx0XHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0bmV3VmFsID0gdGhpcy5fdHJpbUFsaWduVmFsdWUoIGN1clZhbCAtIHN0ZXAgKTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fc2xpZGUoIGV2ZW50LCBpbmRleCwgbmV3VmFsICk7XG5cdFx0fSxcblx0XHRrZXl1cDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0dmFyIGluZGV4ID0gJCggZXZlbnQudGFyZ2V0ICkuZGF0YSggXCJ1aS1zbGlkZXItaGFuZGxlLWluZGV4XCIgKTtcblxuXHRcdFx0aWYgKCB0aGlzLl9rZXlTbGlkaW5nICkge1xuXHRcdFx0XHR0aGlzLl9rZXlTbGlkaW5nID0gZmFsc2U7XG5cdFx0XHRcdHRoaXMuX3N0b3AoIGV2ZW50LCBpbmRleCApO1xuXHRcdFx0XHR0aGlzLl9jaGFuZ2UoIGV2ZW50LCBpbmRleCApO1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVDbGFzcyggJCggZXZlbnQudGFyZ2V0ICksIG51bGwsIFwidWktc3RhdGUtYWN0aXZlXCIgKTtcblx0XHRcdH1cblx0XHR9XG5cdH1cbn0gKTtcblxuXG4vKiFcbiAqIGpRdWVyeSBVSSBTb3J0YWJsZSAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogU29ydGFibGVcbi8vPj5ncm91cDogSW50ZXJhY3Rpb25zXG4vLz4+ZGVzY3JpcHRpb246IEVuYWJsZXMgaXRlbXMgaW4gYSBsaXN0IHRvIGJlIHNvcnRlZCB1c2luZyB0aGUgbW91c2UuXG4vLz4+ZG9jczogaHR0cDovL2FwaS5qcXVlcnl1aS5jb20vc29ydGFibGUvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vc29ydGFibGUvXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2Uvc29ydGFibGUuY3NzXG5cblxuXG52YXIgd2lkZ2V0c1NvcnRhYmxlID0gJC53aWRnZXQoIFwidWkuc29ydGFibGVcIiwgJC51aS5tb3VzZSwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHR3aWRnZXRFdmVudFByZWZpeDogXCJzb3J0XCIsXG5cdHJlYWR5OiBmYWxzZSxcblx0b3B0aW9uczoge1xuXHRcdGFwcGVuZFRvOiBcInBhcmVudFwiLFxuXHRcdGF4aXM6IGZhbHNlLFxuXHRcdGNvbm5lY3RXaXRoOiBmYWxzZSxcblx0XHRjb250YWlubWVudDogZmFsc2UsXG5cdFx0Y3Vyc29yOiBcImF1dG9cIixcblx0XHRjdXJzb3JBdDogZmFsc2UsXG5cdFx0ZHJvcE9uRW1wdHk6IHRydWUsXG5cdFx0Zm9yY2VQbGFjZWhvbGRlclNpemU6IGZhbHNlLFxuXHRcdGZvcmNlSGVscGVyU2l6ZTogZmFsc2UsXG5cdFx0Z3JpZDogZmFsc2UsXG5cdFx0aGFuZGxlOiBmYWxzZSxcblx0XHRoZWxwZXI6IFwib3JpZ2luYWxcIixcblx0XHRpdGVtczogXCI+ICpcIixcblx0XHRvcGFjaXR5OiBmYWxzZSxcblx0XHRwbGFjZWhvbGRlcjogZmFsc2UsXG5cdFx0cmV2ZXJ0OiBmYWxzZSxcblx0XHRzY3JvbGw6IHRydWUsXG5cdFx0c2Nyb2xsU2Vuc2l0aXZpdHk6IDIwLFxuXHRcdHNjcm9sbFNwZWVkOiAyMCxcblx0XHRzY29wZTogXCJkZWZhdWx0XCIsXG5cdFx0dG9sZXJhbmNlOiBcImludGVyc2VjdFwiLFxuXHRcdHpJbmRleDogMTAwMCxcblxuXHRcdC8vIENhbGxiYWNrc1xuXHRcdGFjdGl2YXRlOiBudWxsLFxuXHRcdGJlZm9yZVN0b3A6IG51bGwsXG5cdFx0Y2hhbmdlOiBudWxsLFxuXHRcdGRlYWN0aXZhdGU6IG51bGwsXG5cdFx0b3V0OiBudWxsLFxuXHRcdG92ZXI6IG51bGwsXG5cdFx0cmVjZWl2ZTogbnVsbCxcblx0XHRyZW1vdmU6IG51bGwsXG5cdFx0c29ydDogbnVsbCxcblx0XHRzdGFydDogbnVsbCxcblx0XHRzdG9wOiBudWxsLFxuXHRcdHVwZGF0ZTogbnVsbFxuXHR9LFxuXG5cdF9pc092ZXJBeGlzOiBmdW5jdGlvbiggeCwgcmVmZXJlbmNlLCBzaXplICkge1xuXHRcdHJldHVybiAoIHggPj0gcmVmZXJlbmNlICkgJiYgKCB4IDwgKCByZWZlcmVuY2UgKyBzaXplICkgKTtcblx0fSxcblxuXHRfaXNGbG9hdGluZzogZnVuY3Rpb24oIGl0ZW0gKSB7XG5cdFx0cmV0dXJuICggL2xlZnR8cmlnaHQvICkudGVzdCggaXRlbS5jc3MoIFwiZmxvYXRcIiApICkgfHxcblx0XHRcdCggL2lubGluZXx0YWJsZS1jZWxsLyApLnRlc3QoIGl0ZW0uY3NzKCBcImRpc3BsYXlcIiApICk7XG5cdH0sXG5cblx0X2NyZWF0ZTogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5jb250YWluZXJDYWNoZSA9IHt9O1xuXHRcdHRoaXMuX2FkZENsYXNzKCBcInVpLXNvcnRhYmxlXCIgKTtcblxuXHRcdC8vR2V0IHRoZSBpdGVtc1xuXHRcdHRoaXMucmVmcmVzaCgpO1xuXG5cdFx0Ly9MZXQncyBkZXRlcm1pbmUgdGhlIHBhcmVudCdzIG9mZnNldFxuXHRcdHRoaXMub2Zmc2V0ID0gdGhpcy5lbGVtZW50Lm9mZnNldCgpO1xuXG5cdFx0Ly9Jbml0aWFsaXplIG1vdXNlIGV2ZW50cyBmb3IgaW50ZXJhY3Rpb25cblx0XHR0aGlzLl9tb3VzZUluaXQoKTtcblxuXHRcdHRoaXMuX3NldEhhbmRsZUNsYXNzTmFtZSgpO1xuXG5cdFx0Ly9XZSdyZSByZWFkeSB0byBnb1xuXHRcdHRoaXMucmVhZHkgPSB0cnVlO1xuXG5cdH0sXG5cblx0X3NldE9wdGlvbjogZnVuY3Rpb24oIGtleSwgdmFsdWUgKSB7XG5cdFx0dGhpcy5fc3VwZXIoIGtleSwgdmFsdWUgKTtcblxuXHRcdGlmICgga2V5ID09PSBcImhhbmRsZVwiICkge1xuXHRcdFx0dGhpcy5fc2V0SGFuZGxlQ2xhc3NOYW1lKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF9zZXRIYW5kbGVDbGFzc05hbWU6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciB0aGF0ID0gdGhpcztcblx0XHR0aGlzLl9yZW1vdmVDbGFzcyggdGhpcy5lbGVtZW50LmZpbmQoIFwiLnVpLXNvcnRhYmxlLWhhbmRsZVwiICksIFwidWktc29ydGFibGUtaGFuZGxlXCIgKTtcblx0XHQkLmVhY2goIHRoaXMuaXRlbXMsIGZ1bmN0aW9uKCkge1xuXHRcdFx0dGhhdC5fYWRkQ2xhc3MoXG5cdFx0XHRcdHRoaXMuaW5zdGFuY2Uub3B0aW9ucy5oYW5kbGUgP1xuXHRcdFx0XHRcdHRoaXMuaXRlbS5maW5kKCB0aGlzLmluc3RhbmNlLm9wdGlvbnMuaGFuZGxlICkgOlxuXHRcdFx0XHRcdHRoaXMuaXRlbSxcblx0XHRcdFx0XCJ1aS1zb3J0YWJsZS1oYW5kbGVcIlxuXHRcdFx0KTtcblx0XHR9ICk7XG5cdH0sXG5cblx0X2Rlc3Ryb3k6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuX21vdXNlRGVzdHJveSgpO1xuXG5cdFx0Zm9yICggdmFyIGkgPSB0aGlzLml0ZW1zLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tICkge1xuXHRcdFx0dGhpcy5pdGVtc1sgaSBdLml0ZW0ucmVtb3ZlRGF0YSggdGhpcy53aWRnZXROYW1lICsgXCItaXRlbVwiICk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0X21vdXNlQ2FwdHVyZTogZnVuY3Rpb24oIGV2ZW50LCBvdmVycmlkZUhhbmRsZSApIHtcblx0XHR2YXIgY3VycmVudEl0ZW0gPSBudWxsLFxuXHRcdFx0dmFsaWRIYW5kbGUgPSBmYWxzZSxcblx0XHRcdHRoYXQgPSB0aGlzO1xuXG5cdFx0aWYgKCB0aGlzLnJldmVydGluZyApIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHRpZiAoIHRoaXMub3B0aW9ucy5kaXNhYmxlZCB8fCB0aGlzLm9wdGlvbnMudHlwZSA9PT0gXCJzdGF0aWNcIiApIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHQvL1dlIGhhdmUgdG8gcmVmcmVzaCB0aGUgaXRlbXMgZGF0YSBvbmNlIGZpcnN0XG5cdFx0dGhpcy5fcmVmcmVzaEl0ZW1zKCBldmVudCApO1xuXG5cdFx0Ly9GaW5kIG91dCBpZiB0aGUgY2xpY2tlZCBub2RlIChvciBvbmUgb2YgaXRzIHBhcmVudHMpIGlzIGEgYWN0dWFsIGl0ZW0gaW4gdGhpcy5pdGVtc1xuXHRcdCQoIGV2ZW50LnRhcmdldCApLnBhcmVudHMoKS5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdGlmICggJC5kYXRhKCB0aGlzLCB0aGF0LndpZGdldE5hbWUgKyBcIi1pdGVtXCIgKSA9PT0gdGhhdCApIHtcblx0XHRcdFx0Y3VycmVudEl0ZW0gPSAkKCB0aGlzICk7XG5cdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdFx0aWYgKCAkLmRhdGEoIGV2ZW50LnRhcmdldCwgdGhhdC53aWRnZXROYW1lICsgXCItaXRlbVwiICkgPT09IHRoYXQgKSB7XG5cdFx0XHRjdXJyZW50SXRlbSA9ICQoIGV2ZW50LnRhcmdldCApO1xuXHRcdH1cblxuXHRcdGlmICggIWN1cnJlbnRJdGVtICkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRpZiAoIHRoaXMub3B0aW9ucy5oYW5kbGUgJiYgIW92ZXJyaWRlSGFuZGxlICkge1xuXHRcdFx0JCggdGhpcy5vcHRpb25zLmhhbmRsZSwgY3VycmVudEl0ZW0gKS5maW5kKCBcIipcIiApLmFkZEJhY2soKS5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdFx0aWYgKCB0aGlzID09PSBldmVudC50YXJnZXQgKSB7XG5cdFx0XHRcdFx0dmFsaWRIYW5kbGUgPSB0cnVlO1xuXHRcdFx0XHR9XG5cdFx0XHR9ICk7XG5cdFx0XHRpZiAoICF2YWxpZEhhbmRsZSApIHtcblx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMuY3VycmVudEl0ZW0gPSBjdXJyZW50SXRlbTtcblx0XHR0aGlzLl9yZW1vdmVDdXJyZW50c0Zyb21JdGVtcygpO1xuXHRcdHJldHVybiB0cnVlO1xuXG5cdH0sXG5cblx0X21vdXNlU3RhcnQ6IGZ1bmN0aW9uKCBldmVudCwgb3ZlcnJpZGVIYW5kbGUsIG5vQWN0aXZhdGlvbiApIHtcblxuXHRcdHZhciBpLCBib2R5LFxuXHRcdFx0byA9IHRoaXMub3B0aW9ucztcblxuXHRcdHRoaXMuY3VycmVudENvbnRhaW5lciA9IHRoaXM7XG5cblx0XHQvL1dlIG9ubHkgbmVlZCB0byBjYWxsIHJlZnJlc2hQb3NpdGlvbnMsIGJlY2F1c2UgdGhlIHJlZnJlc2hJdGVtcyBjYWxsIGhhcyBiZWVuIG1vdmVkIHRvXG5cdFx0Ly8gbW91c2VDYXB0dXJlXG5cdFx0dGhpcy5yZWZyZXNoUG9zaXRpb25zKCk7XG5cblx0XHQvL0NyZWF0ZSBhbmQgYXBwZW5kIHRoZSB2aXNpYmxlIGhlbHBlclxuXHRcdHRoaXMuaGVscGVyID0gdGhpcy5fY3JlYXRlSGVscGVyKCBldmVudCApO1xuXG5cdFx0Ly9DYWNoZSB0aGUgaGVscGVyIHNpemVcblx0XHR0aGlzLl9jYWNoZUhlbHBlclByb3BvcnRpb25zKCk7XG5cblx0XHQvKlxuXHRcdCAqIC0gUG9zaXRpb24gZ2VuZXJhdGlvbiAtXG5cdFx0ICogVGhpcyBibG9jayBnZW5lcmF0ZXMgZXZlcnl0aGluZyBwb3NpdGlvbiByZWxhdGVkIC0gaXQncyB0aGUgY29yZSBvZiBkcmFnZ2FibGVzLlxuXHRcdCAqL1xuXG5cdFx0Ly9DYWNoZSB0aGUgbWFyZ2lucyBvZiB0aGUgb3JpZ2luYWwgZWxlbWVudFxuXHRcdHRoaXMuX2NhY2hlTWFyZ2lucygpO1xuXG5cdFx0Ly9HZXQgdGhlIG5leHQgc2Nyb2xsaW5nIHBhcmVudFxuXHRcdHRoaXMuc2Nyb2xsUGFyZW50ID0gdGhpcy5oZWxwZXIuc2Nyb2xsUGFyZW50KCk7XG5cblx0XHQvL1RoZSBlbGVtZW50J3MgYWJzb2x1dGUgcG9zaXRpb24gb24gdGhlIHBhZ2UgbWludXMgbWFyZ2luc1xuXHRcdHRoaXMub2Zmc2V0ID0gdGhpcy5jdXJyZW50SXRlbS5vZmZzZXQoKTtcblx0XHR0aGlzLm9mZnNldCA9IHtcblx0XHRcdHRvcDogdGhpcy5vZmZzZXQudG9wIC0gdGhpcy5tYXJnaW5zLnRvcCxcblx0XHRcdGxlZnQ6IHRoaXMub2Zmc2V0LmxlZnQgLSB0aGlzLm1hcmdpbnMubGVmdFxuXHRcdH07XG5cblx0XHQkLmV4dGVuZCggdGhpcy5vZmZzZXQsIHtcblx0XHRcdGNsaWNrOiB7IC8vV2hlcmUgdGhlIGNsaWNrIGhhcHBlbmVkLCByZWxhdGl2ZSB0byB0aGUgZWxlbWVudFxuXHRcdFx0XHRsZWZ0OiBldmVudC5wYWdlWCAtIHRoaXMub2Zmc2V0LmxlZnQsXG5cdFx0XHRcdHRvcDogZXZlbnQucGFnZVkgLSB0aGlzLm9mZnNldC50b3Bcblx0XHRcdH0sXG5cdFx0XHRwYXJlbnQ6IHRoaXMuX2dldFBhcmVudE9mZnNldCgpLFxuXG5cdFx0XHQvLyBUaGlzIGlzIGEgcmVsYXRpdmUgdG8gYWJzb2x1dGUgcG9zaXRpb24gbWludXMgdGhlIGFjdHVhbCBwb3NpdGlvbiBjYWxjdWxhdGlvbiAtXG5cdFx0XHQvLyBvbmx5IHVzZWQgZm9yIHJlbGF0aXZlIHBvc2l0aW9uZWQgaGVscGVyXG5cdFx0XHRyZWxhdGl2ZTogdGhpcy5fZ2V0UmVsYXRpdmVPZmZzZXQoKVxuXHRcdH0gKTtcblxuXHRcdC8vIE9ubHkgYWZ0ZXIgd2UgZ290IHRoZSBvZmZzZXQsIHdlIGNhbiBjaGFuZ2UgdGhlIGhlbHBlcidzIHBvc2l0aW9uIHRvIGFic29sdXRlXG5cdFx0Ly8gVE9ETzogU3RpbGwgbmVlZCB0byBmaWd1cmUgb3V0IGEgd2F5IHRvIG1ha2UgcmVsYXRpdmUgc29ydGluZyBwb3NzaWJsZVxuXHRcdHRoaXMuaGVscGVyLmNzcyggXCJwb3NpdGlvblwiLCBcImFic29sdXRlXCIgKTtcblx0XHR0aGlzLmNzc1Bvc2l0aW9uID0gdGhpcy5oZWxwZXIuY3NzKCBcInBvc2l0aW9uXCIgKTtcblxuXHRcdC8vR2VuZXJhdGUgdGhlIG9yaWdpbmFsIHBvc2l0aW9uXG5cdFx0dGhpcy5vcmlnaW5hbFBvc2l0aW9uID0gdGhpcy5fZ2VuZXJhdGVQb3NpdGlvbiggZXZlbnQgKTtcblx0XHR0aGlzLm9yaWdpbmFsUGFnZVggPSBldmVudC5wYWdlWDtcblx0XHR0aGlzLm9yaWdpbmFsUGFnZVkgPSBldmVudC5wYWdlWTtcblxuXHRcdC8vQWRqdXN0IHRoZSBtb3VzZSBvZmZzZXQgcmVsYXRpdmUgdG8gdGhlIGhlbHBlciBpZiBcImN1cnNvckF0XCIgaXMgc3VwcGxpZWRcblx0XHQoIG8uY3Vyc29yQXQgJiYgdGhpcy5fYWRqdXN0T2Zmc2V0RnJvbUhlbHBlciggby5jdXJzb3JBdCApICk7XG5cblx0XHQvL0NhY2hlIHRoZSBmb3JtZXIgRE9NIHBvc2l0aW9uXG5cdFx0dGhpcy5kb21Qb3NpdGlvbiA9IHtcblx0XHRcdHByZXY6IHRoaXMuY3VycmVudEl0ZW0ucHJldigpWyAwIF0sXG5cdFx0XHRwYXJlbnQ6IHRoaXMuY3VycmVudEl0ZW0ucGFyZW50KClbIDAgXVxuXHRcdH07XG5cblx0XHQvLyBJZiB0aGUgaGVscGVyIGlzIG5vdCB0aGUgb3JpZ2luYWwsIGhpZGUgdGhlIG9yaWdpbmFsIHNvIGl0J3Mgbm90IHBsYXlpbmcgYW55IHJvbGUgZHVyaW5nXG5cdFx0Ly8gdGhlIGRyYWcsIHdvbid0IGNhdXNlIGFueXRoaW5nIGJhZCB0aGlzIHdheVxuXHRcdGlmICggdGhpcy5oZWxwZXJbIDAgXSAhPT0gdGhpcy5jdXJyZW50SXRlbVsgMCBdICkge1xuXHRcdFx0dGhpcy5jdXJyZW50SXRlbS5oaWRlKCk7XG5cdFx0fVxuXG5cdFx0Ly9DcmVhdGUgdGhlIHBsYWNlaG9sZGVyXG5cdFx0dGhpcy5fY3JlYXRlUGxhY2Vob2xkZXIoKTtcblxuXHRcdC8vU2V0IGEgY29udGFpbm1lbnQgaWYgZ2l2ZW4gaW4gdGhlIG9wdGlvbnNcblx0XHRpZiAoIG8uY29udGFpbm1lbnQgKSB7XG5cdFx0XHR0aGlzLl9zZXRDb250YWlubWVudCgpO1xuXHRcdH1cblxuXHRcdGlmICggby5jdXJzb3IgJiYgby5jdXJzb3IgIT09IFwiYXV0b1wiICkgeyAvLyBjdXJzb3Igb3B0aW9uXG5cdFx0XHRib2R5ID0gdGhpcy5kb2N1bWVudC5maW5kKCBcImJvZHlcIiApO1xuXG5cdFx0XHQvLyBTdXBwb3J0OiBJRVxuXHRcdFx0dGhpcy5zdG9yZWRDdXJzb3IgPSBib2R5LmNzcyggXCJjdXJzb3JcIiApO1xuXHRcdFx0Ym9keS5jc3MoIFwiY3Vyc29yXCIsIG8uY3Vyc29yICk7XG5cblx0XHRcdHRoaXMuc3RvcmVkU3R5bGVzaGVldCA9XG5cdFx0XHRcdCQoIFwiPHN0eWxlPip7IGN1cnNvcjogXCIgKyBvLmN1cnNvciArIFwiICFpbXBvcnRhbnQ7IH08L3N0eWxlPlwiICkuYXBwZW5kVG8oIGJvZHkgKTtcblx0XHR9XG5cblx0XHRpZiAoIG8ub3BhY2l0eSApIHsgLy8gb3BhY2l0eSBvcHRpb25cblx0XHRcdGlmICggdGhpcy5oZWxwZXIuY3NzKCBcIm9wYWNpdHlcIiApICkge1xuXHRcdFx0XHR0aGlzLl9zdG9yZWRPcGFjaXR5ID0gdGhpcy5oZWxwZXIuY3NzKCBcIm9wYWNpdHlcIiApO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5oZWxwZXIuY3NzKCBcIm9wYWNpdHlcIiwgby5vcGFjaXR5ICk7XG5cdFx0fVxuXG5cdFx0aWYgKCBvLnpJbmRleCApIHsgLy8gekluZGV4IG9wdGlvblxuXHRcdFx0aWYgKCB0aGlzLmhlbHBlci5jc3MoIFwiekluZGV4XCIgKSApIHtcblx0XHRcdFx0dGhpcy5fc3RvcmVkWkluZGV4ID0gdGhpcy5oZWxwZXIuY3NzKCBcInpJbmRleFwiICk7XG5cdFx0XHR9XG5cdFx0XHR0aGlzLmhlbHBlci5jc3MoIFwiekluZGV4XCIsIG8uekluZGV4ICk7XG5cdFx0fVxuXG5cdFx0Ly9QcmVwYXJlIHNjcm9sbGluZ1xuXHRcdGlmICggdGhpcy5zY3JvbGxQYXJlbnRbIDAgXSAhPT0gdGhpcy5kb2N1bWVudFsgMCBdICYmXG5cdFx0XHRcdHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0udGFnTmFtZSAhPT0gXCJIVE1MXCIgKSB7XG5cdFx0XHR0aGlzLm92ZXJmbG93T2Zmc2V0ID0gdGhpcy5zY3JvbGxQYXJlbnQub2Zmc2V0KCk7XG5cdFx0fVxuXG5cdFx0Ly9DYWxsIGNhbGxiYWNrc1xuXHRcdHRoaXMuX3RyaWdnZXIoIFwic3RhcnRcIiwgZXZlbnQsIHRoaXMuX3VpSGFzaCgpICk7XG5cblx0XHQvL1JlY2FjaGUgdGhlIGhlbHBlciBzaXplXG5cdFx0aWYgKCAhdGhpcy5fcHJlc2VydmVIZWxwZXJQcm9wb3J0aW9ucyApIHtcblx0XHRcdHRoaXMuX2NhY2hlSGVscGVyUHJvcG9ydGlvbnMoKTtcblx0XHR9XG5cblx0XHQvL1Bvc3QgXCJhY3RpdmF0ZVwiIGV2ZW50cyB0byBwb3NzaWJsZSBjb250YWluZXJzXG5cdFx0aWYgKCAhbm9BY3RpdmF0aW9uICkge1xuXHRcdFx0Zm9yICggaSA9IHRoaXMuY29udGFpbmVycy5sZW5ndGggLSAxOyBpID49IDA7IGktLSApIHtcblx0XHRcdFx0dGhpcy5jb250YWluZXJzWyBpIF0uX3RyaWdnZXIoIFwiYWN0aXZhdGVcIiwgZXZlbnQsIHRoaXMuX3VpSGFzaCggdGhpcyApICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly9QcmVwYXJlIHBvc3NpYmxlIGRyb3BwYWJsZXNcblx0XHRpZiAoICQudWkuZGRtYW5hZ2VyICkge1xuXHRcdFx0JC51aS5kZG1hbmFnZXIuY3VycmVudCA9IHRoaXM7XG5cdFx0fVxuXG5cdFx0aWYgKCAkLnVpLmRkbWFuYWdlciAmJiAhby5kcm9wQmVoYXZpb3VyICkge1xuXHRcdFx0JC51aS5kZG1hbmFnZXIucHJlcGFyZU9mZnNldHMoIHRoaXMsIGV2ZW50ICk7XG5cdFx0fVxuXG5cdFx0dGhpcy5kcmFnZ2luZyA9IHRydWU7XG5cblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5oZWxwZXIsIFwidWktc29ydGFibGUtaGVscGVyXCIgKTtcblxuXHRcdC8vIEV4ZWN1dGUgdGhlIGRyYWcgb25jZSAtIHRoaXMgY2F1c2VzIHRoZSBoZWxwZXIgbm90IHRvIGJlIHZpc2libGViZWZvcmUgZ2V0dGluZyBpdHNcblx0XHQvLyBjb3JyZWN0IHBvc2l0aW9uXG5cdFx0dGhpcy5fbW91c2VEcmFnKCBldmVudCApO1xuXHRcdHJldHVybiB0cnVlO1xuXG5cdH0sXG5cblx0X21vdXNlRHJhZzogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHZhciBpLCBpdGVtLCBpdGVtRWxlbWVudCwgaW50ZXJzZWN0aW9uLFxuXHRcdFx0byA9IHRoaXMub3B0aW9ucyxcblx0XHRcdHNjcm9sbGVkID0gZmFsc2U7XG5cblx0XHQvL0NvbXB1dGUgdGhlIGhlbHBlcnMgcG9zaXRpb25cblx0XHR0aGlzLnBvc2l0aW9uID0gdGhpcy5fZ2VuZXJhdGVQb3NpdGlvbiggZXZlbnQgKTtcblx0XHR0aGlzLnBvc2l0aW9uQWJzID0gdGhpcy5fY29udmVydFBvc2l0aW9uVG8oIFwiYWJzb2x1dGVcIiApO1xuXG5cdFx0aWYgKCAhdGhpcy5sYXN0UG9zaXRpb25BYnMgKSB7XG5cdFx0XHR0aGlzLmxhc3RQb3NpdGlvbkFicyA9IHRoaXMucG9zaXRpb25BYnM7XG5cdFx0fVxuXG5cdFx0Ly9EbyBzY3JvbGxpbmdcblx0XHRpZiAoIHRoaXMub3B0aW9ucy5zY3JvbGwgKSB7XG5cdFx0XHRpZiAoIHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0gIT09IHRoaXMuZG9jdW1lbnRbIDAgXSAmJlxuXHRcdFx0XHRcdHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0udGFnTmFtZSAhPT0gXCJIVE1MXCIgKSB7XG5cblx0XHRcdFx0aWYgKCAoIHRoaXMub3ZlcmZsb3dPZmZzZXQudG9wICsgdGhpcy5zY3JvbGxQYXJlbnRbIDAgXS5vZmZzZXRIZWlnaHQgKSAtXG5cdFx0XHRcdFx0XHRldmVudC5wYWdlWSA8IG8uc2Nyb2xsU2Vuc2l0aXZpdHkgKSB7XG5cdFx0XHRcdFx0dGhpcy5zY3JvbGxQYXJlbnRbIDAgXS5zY3JvbGxUb3AgPVxuXHRcdFx0XHRcdFx0c2Nyb2xsZWQgPSB0aGlzLnNjcm9sbFBhcmVudFsgMCBdLnNjcm9sbFRvcCArIG8uc2Nyb2xsU3BlZWQ7XG5cdFx0XHRcdH0gZWxzZSBpZiAoIGV2ZW50LnBhZ2VZIC0gdGhpcy5vdmVyZmxvd09mZnNldC50b3AgPCBvLnNjcm9sbFNlbnNpdGl2aXR5ICkge1xuXHRcdFx0XHRcdHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0uc2Nyb2xsVG9wID1cblx0XHRcdFx0XHRcdHNjcm9sbGVkID0gdGhpcy5zY3JvbGxQYXJlbnRbIDAgXS5zY3JvbGxUb3AgLSBvLnNjcm9sbFNwZWVkO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCAoIHRoaXMub3ZlcmZsb3dPZmZzZXQubGVmdCArIHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0ub2Zmc2V0V2lkdGggKSAtXG5cdFx0XHRcdFx0XHRldmVudC5wYWdlWCA8IG8uc2Nyb2xsU2Vuc2l0aXZpdHkgKSB7XG5cdFx0XHRcdFx0dGhpcy5zY3JvbGxQYXJlbnRbIDAgXS5zY3JvbGxMZWZ0ID0gc2Nyb2xsZWQgPVxuXHRcdFx0XHRcdFx0dGhpcy5zY3JvbGxQYXJlbnRbIDAgXS5zY3JvbGxMZWZ0ICsgby5zY3JvbGxTcGVlZDtcblx0XHRcdFx0fSBlbHNlIGlmICggZXZlbnQucGFnZVggLSB0aGlzLm92ZXJmbG93T2Zmc2V0LmxlZnQgPCBvLnNjcm9sbFNlbnNpdGl2aXR5ICkge1xuXHRcdFx0XHRcdHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0uc2Nyb2xsTGVmdCA9IHNjcm9sbGVkID1cblx0XHRcdFx0XHRcdHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0uc2Nyb2xsTGVmdCAtIG8uc2Nyb2xsU3BlZWQ7XG5cdFx0XHRcdH1cblxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHRpZiAoIGV2ZW50LnBhZ2VZIC0gdGhpcy5kb2N1bWVudC5zY3JvbGxUb3AoKSA8IG8uc2Nyb2xsU2Vuc2l0aXZpdHkgKSB7XG5cdFx0XHRcdFx0c2Nyb2xsZWQgPSB0aGlzLmRvY3VtZW50LnNjcm9sbFRvcCggdGhpcy5kb2N1bWVudC5zY3JvbGxUb3AoKSAtIG8uc2Nyb2xsU3BlZWQgKTtcblx0XHRcdFx0fSBlbHNlIGlmICggdGhpcy53aW5kb3cuaGVpZ2h0KCkgLSAoIGV2ZW50LnBhZ2VZIC0gdGhpcy5kb2N1bWVudC5zY3JvbGxUb3AoKSApIDxcblx0XHRcdFx0XHRcdG8uc2Nyb2xsU2Vuc2l0aXZpdHkgKSB7XG5cdFx0XHRcdFx0c2Nyb2xsZWQgPSB0aGlzLmRvY3VtZW50LnNjcm9sbFRvcCggdGhpcy5kb2N1bWVudC5zY3JvbGxUb3AoKSArIG8uc2Nyb2xsU3BlZWQgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggZXZlbnQucGFnZVggLSB0aGlzLmRvY3VtZW50LnNjcm9sbExlZnQoKSA8IG8uc2Nyb2xsU2Vuc2l0aXZpdHkgKSB7XG5cdFx0XHRcdFx0c2Nyb2xsZWQgPSB0aGlzLmRvY3VtZW50LnNjcm9sbExlZnQoXG5cdFx0XHRcdFx0XHR0aGlzLmRvY3VtZW50LnNjcm9sbExlZnQoKSAtIG8uc2Nyb2xsU3BlZWRcblx0XHRcdFx0XHQpO1xuXHRcdFx0XHR9IGVsc2UgaWYgKCB0aGlzLndpbmRvdy53aWR0aCgpIC0gKCBldmVudC5wYWdlWCAtIHRoaXMuZG9jdW1lbnQuc2Nyb2xsTGVmdCgpICkgPFxuXHRcdFx0XHRcdFx0by5zY3JvbGxTZW5zaXRpdml0eSApIHtcblx0XHRcdFx0XHRzY3JvbGxlZCA9IHRoaXMuZG9jdW1lbnQuc2Nyb2xsTGVmdChcblx0XHRcdFx0XHRcdHRoaXMuZG9jdW1lbnQuc2Nyb2xsTGVmdCgpICsgby5zY3JvbGxTcGVlZFxuXHRcdFx0XHRcdCk7XG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHNjcm9sbGVkICE9PSBmYWxzZSAmJiAkLnVpLmRkbWFuYWdlciAmJiAhby5kcm9wQmVoYXZpb3VyICkge1xuXHRcdFx0XHQkLnVpLmRkbWFuYWdlci5wcmVwYXJlT2Zmc2V0cyggdGhpcywgZXZlbnQgKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvL1JlZ2VuZXJhdGUgdGhlIGFic29sdXRlIHBvc2l0aW9uIHVzZWQgZm9yIHBvc2l0aW9uIGNoZWNrc1xuXHRcdHRoaXMucG9zaXRpb25BYnMgPSB0aGlzLl9jb252ZXJ0UG9zaXRpb25UbyggXCJhYnNvbHV0ZVwiICk7XG5cblx0XHQvL1NldCB0aGUgaGVscGVyIHBvc2l0aW9uXG5cdFx0aWYgKCAhdGhpcy5vcHRpb25zLmF4aXMgfHwgdGhpcy5vcHRpb25zLmF4aXMgIT09IFwieVwiICkge1xuXHRcdFx0dGhpcy5oZWxwZXJbIDAgXS5zdHlsZS5sZWZ0ID0gdGhpcy5wb3NpdGlvbi5sZWZ0ICsgXCJweFwiO1xuXHRcdH1cblx0XHRpZiAoICF0aGlzLm9wdGlvbnMuYXhpcyB8fCB0aGlzLm9wdGlvbnMuYXhpcyAhPT0gXCJ4XCIgKSB7XG5cdFx0XHR0aGlzLmhlbHBlclsgMCBdLnN0eWxlLnRvcCA9IHRoaXMucG9zaXRpb24udG9wICsgXCJweFwiO1xuXHRcdH1cblxuXHRcdC8vUmVhcnJhbmdlXG5cdFx0Zm9yICggaSA9IHRoaXMuaXRlbXMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0gKSB7XG5cblx0XHRcdC8vQ2FjaGUgdmFyaWFibGVzIGFuZCBpbnRlcnNlY3Rpb24sIGNvbnRpbnVlIGlmIG5vIGludGVyc2VjdGlvblxuXHRcdFx0aXRlbSA9IHRoaXMuaXRlbXNbIGkgXTtcblx0XHRcdGl0ZW1FbGVtZW50ID0gaXRlbS5pdGVtWyAwIF07XG5cdFx0XHRpbnRlcnNlY3Rpb24gPSB0aGlzLl9pbnRlcnNlY3RzV2l0aFBvaW50ZXIoIGl0ZW0gKTtcblx0XHRcdGlmICggIWludGVyc2VjdGlvbiApIHtcblx0XHRcdFx0Y29udGludWU7XG5cdFx0XHR9XG5cblx0XHRcdC8vIE9ubHkgcHV0IHRoZSBwbGFjZWhvbGRlciBpbnNpZGUgdGhlIGN1cnJlbnQgQ29udGFpbmVyLCBza2lwIGFsbFxuXHRcdFx0Ly8gaXRlbXMgZnJvbSBvdGhlciBjb250YWluZXJzLiBUaGlzIHdvcmtzIGJlY2F1c2Ugd2hlbiBtb3Zpbmdcblx0XHRcdC8vIGFuIGl0ZW0gZnJvbSBvbmUgY29udGFpbmVyIHRvIGFub3RoZXIgdGhlXG5cdFx0XHQvLyBjdXJyZW50Q29udGFpbmVyIGlzIHN3aXRjaGVkIGJlZm9yZSB0aGUgcGxhY2Vob2xkZXIgaXMgbW92ZWQuXG5cdFx0XHQvL1xuXHRcdFx0Ly8gV2l0aG91dCB0aGlzLCBtb3ZpbmcgaXRlbXMgaW4gXCJzdWItc29ydGFibGVzXCIgY2FuIGNhdXNlXG5cdFx0XHQvLyB0aGUgcGxhY2Vob2xkZXIgdG8gaml0dGVyIGJldHdlZW4gdGhlIG91dGVyIGFuZCBpbm5lciBjb250YWluZXIuXG5cdFx0XHRpZiAoIGl0ZW0uaW5zdGFuY2UgIT09IHRoaXMuY3VycmVudENvbnRhaW5lciApIHtcblx0XHRcdFx0Y29udGludWU7XG5cdFx0XHR9XG5cblx0XHRcdC8vIENhbm5vdCBpbnRlcnNlY3Qgd2l0aCBpdHNlbGZcblx0XHRcdC8vIG5vIHVzZWxlc3MgYWN0aW9ucyB0aGF0IGhhdmUgYmVlbiBkb25lIGJlZm9yZVxuXHRcdFx0Ly8gbm8gYWN0aW9uIGlmIHRoZSBpdGVtIG1vdmVkIGlzIHRoZSBwYXJlbnQgb2YgdGhlIGl0ZW0gY2hlY2tlZFxuXHRcdFx0aWYgKCBpdGVtRWxlbWVudCAhPT0gdGhpcy5jdXJyZW50SXRlbVsgMCBdICYmXG5cdFx0XHRcdHRoaXMucGxhY2Vob2xkZXJbIGludGVyc2VjdGlvbiA9PT0gMSA/IFwibmV4dFwiIDogXCJwcmV2XCIgXSgpWyAwIF0gIT09IGl0ZW1FbGVtZW50ICYmXG5cdFx0XHRcdCEkLmNvbnRhaW5zKCB0aGlzLnBsYWNlaG9sZGVyWyAwIF0sIGl0ZW1FbGVtZW50ICkgJiZcblx0XHRcdFx0KCB0aGlzLm9wdGlvbnMudHlwZSA9PT0gXCJzZW1pLWR5bmFtaWNcIiA/XG5cdFx0XHRcdFx0ISQuY29udGFpbnMoIHRoaXMuZWxlbWVudFsgMCBdLCBpdGVtRWxlbWVudCApIDpcblx0XHRcdFx0XHR0cnVlXG5cdFx0XHRcdClcblx0XHRcdCkge1xuXG5cdFx0XHRcdHRoaXMuZGlyZWN0aW9uID0gaW50ZXJzZWN0aW9uID09PSAxID8gXCJkb3duXCIgOiBcInVwXCI7XG5cblx0XHRcdFx0aWYgKCB0aGlzLm9wdGlvbnMudG9sZXJhbmNlID09PSBcInBvaW50ZXJcIiB8fCB0aGlzLl9pbnRlcnNlY3RzV2l0aFNpZGVzKCBpdGVtICkgKSB7XG5cdFx0XHRcdFx0dGhpcy5fcmVhcnJhbmdlKCBldmVudCwgaXRlbSApO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5fdHJpZ2dlciggXCJjaGFuZ2VcIiwgZXZlbnQsIHRoaXMuX3VpSGFzaCgpICk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vUG9zdCBldmVudHMgdG8gY29udGFpbmVyc1xuXHRcdHRoaXMuX2NvbnRhY3RDb250YWluZXJzKCBldmVudCApO1xuXG5cdFx0Ly9JbnRlcmNvbm5lY3Qgd2l0aCBkcm9wcGFibGVzXG5cdFx0aWYgKCAkLnVpLmRkbWFuYWdlciApIHtcblx0XHRcdCQudWkuZGRtYW5hZ2VyLmRyYWcoIHRoaXMsIGV2ZW50ICk7XG5cdFx0fVxuXG5cdFx0Ly9DYWxsIGNhbGxiYWNrc1xuXHRcdHRoaXMuX3RyaWdnZXIoIFwic29ydFwiLCBldmVudCwgdGhpcy5fdWlIYXNoKCkgKTtcblxuXHRcdHRoaXMubGFzdFBvc2l0aW9uQWJzID0gdGhpcy5wb3NpdGlvbkFicztcblx0XHRyZXR1cm4gZmFsc2U7XG5cblx0fSxcblxuXHRfbW91c2VTdG9wOiBmdW5jdGlvbiggZXZlbnQsIG5vUHJvcGFnYXRpb24gKSB7XG5cblx0XHRpZiAoICFldmVudCApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvL0lmIHdlIGFyZSB1c2luZyBkcm9wcGFibGVzLCBpbmZvcm0gdGhlIG1hbmFnZXIgYWJvdXQgdGhlIGRyb3Bcblx0XHRpZiAoICQudWkuZGRtYW5hZ2VyICYmICF0aGlzLm9wdGlvbnMuZHJvcEJlaGF2aW91ciApIHtcblx0XHRcdCQudWkuZGRtYW5hZ2VyLmRyb3AoIHRoaXMsIGV2ZW50ICk7XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMucmV2ZXJ0ICkge1xuXHRcdFx0dmFyIHRoYXQgPSB0aGlzLFxuXHRcdFx0XHRjdXIgPSB0aGlzLnBsYWNlaG9sZGVyLm9mZnNldCgpLFxuXHRcdFx0XHRheGlzID0gdGhpcy5vcHRpb25zLmF4aXMsXG5cdFx0XHRcdGFuaW1hdGlvbiA9IHt9O1xuXG5cdFx0XHRpZiAoICFheGlzIHx8IGF4aXMgPT09IFwieFwiICkge1xuXHRcdFx0XHRhbmltYXRpb24ubGVmdCA9IGN1ci5sZWZ0IC0gdGhpcy5vZmZzZXQucGFyZW50LmxlZnQgLSB0aGlzLm1hcmdpbnMubGVmdCArXG5cdFx0XHRcdFx0KCB0aGlzLm9mZnNldFBhcmVudFsgMCBdID09PSB0aGlzLmRvY3VtZW50WyAwIF0uYm9keSA/XG5cdFx0XHRcdFx0XHQwIDpcblx0XHRcdFx0XHRcdHRoaXMub2Zmc2V0UGFyZW50WyAwIF0uc2Nyb2xsTGVmdFxuXHRcdFx0XHRcdCk7XG5cdFx0XHR9XG5cdFx0XHRpZiAoICFheGlzIHx8IGF4aXMgPT09IFwieVwiICkge1xuXHRcdFx0XHRhbmltYXRpb24udG9wID0gY3VyLnRvcCAtIHRoaXMub2Zmc2V0LnBhcmVudC50b3AgLSB0aGlzLm1hcmdpbnMudG9wICtcblx0XHRcdFx0XHQoIHRoaXMub2Zmc2V0UGFyZW50WyAwIF0gPT09IHRoaXMuZG9jdW1lbnRbIDAgXS5ib2R5ID9cblx0XHRcdFx0XHRcdDAgOlxuXHRcdFx0XHRcdFx0dGhpcy5vZmZzZXRQYXJlbnRbIDAgXS5zY3JvbGxUb3Bcblx0XHRcdFx0XHQpO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5yZXZlcnRpbmcgPSB0cnVlO1xuXHRcdFx0JCggdGhpcy5oZWxwZXIgKS5hbmltYXRlKFxuXHRcdFx0XHRhbmltYXRpb24sXG5cdFx0XHRcdHBhcnNlSW50KCB0aGlzLm9wdGlvbnMucmV2ZXJ0LCAxMCApIHx8IDUwMCxcblx0XHRcdFx0ZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0dGhhdC5fY2xlYXIoIGV2ZW50ICk7XG5cdFx0XHRcdH1cblx0XHRcdCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX2NsZWFyKCBldmVudCwgbm9Qcm9wYWdhdGlvbiApO1xuXHRcdH1cblxuXHRcdHJldHVybiBmYWxzZTtcblxuXHR9LFxuXG5cdGNhbmNlbDogZnVuY3Rpb24oKSB7XG5cblx0XHRpZiAoIHRoaXMuZHJhZ2dpbmcgKSB7XG5cblx0XHRcdHRoaXMuX21vdXNlVXAoIG5ldyAkLkV2ZW50KCBcIm1vdXNldXBcIiwgeyB0YXJnZXQ6IG51bGwgfSApICk7XG5cblx0XHRcdGlmICggdGhpcy5vcHRpb25zLmhlbHBlciA9PT0gXCJvcmlnaW5hbFwiICkge1xuXHRcdFx0XHR0aGlzLmN1cnJlbnRJdGVtLmNzcyggdGhpcy5fc3RvcmVkQ1NTICk7XG5cdFx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0aGlzLmN1cnJlbnRJdGVtLCBcInVpLXNvcnRhYmxlLWhlbHBlclwiICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0aGlzLmN1cnJlbnRJdGVtLnNob3coKTtcblx0XHRcdH1cblxuXHRcdFx0Ly9Qb3N0IGRlYWN0aXZhdGluZyBldmVudHMgdG8gY29udGFpbmVyc1xuXHRcdFx0Zm9yICggdmFyIGkgPSB0aGlzLmNvbnRhaW5lcnMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0gKSB7XG5cdFx0XHRcdHRoaXMuY29udGFpbmVyc1sgaSBdLl90cmlnZ2VyKCBcImRlYWN0aXZhdGVcIiwgbnVsbCwgdGhpcy5fdWlIYXNoKCB0aGlzICkgKTtcblx0XHRcdFx0aWYgKCB0aGlzLmNvbnRhaW5lcnNbIGkgXS5jb250YWluZXJDYWNoZS5vdmVyICkge1xuXHRcdFx0XHRcdHRoaXMuY29udGFpbmVyc1sgaSBdLl90cmlnZ2VyKCBcIm91dFwiLCBudWxsLCB0aGlzLl91aUhhc2goIHRoaXMgKSApO1xuXHRcdFx0XHRcdHRoaXMuY29udGFpbmVyc1sgaSBdLmNvbnRhaW5lckNhY2hlLm92ZXIgPSAwO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHRpZiAoIHRoaXMucGxhY2Vob2xkZXIgKSB7XG5cblx0XHRcdC8vJCh0aGlzLnBsYWNlaG9sZGVyWzBdKS5yZW1vdmUoKTsgd291bGQgaGF2ZSBiZWVuIHRoZSBqUXVlcnkgd2F5IC0gdW5mb3J0dW5hdGVseSxcblx0XHRcdC8vIGl0IHVuYmluZHMgQUxMIGV2ZW50cyBmcm9tIHRoZSBvcmlnaW5hbCBub2RlIVxuXHRcdFx0aWYgKCB0aGlzLnBsYWNlaG9sZGVyWyAwIF0ucGFyZW50Tm9kZSApIHtcblx0XHRcdFx0dGhpcy5wbGFjZWhvbGRlclsgMCBdLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoIHRoaXMucGxhY2Vob2xkZXJbIDAgXSApO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCB0aGlzLm9wdGlvbnMuaGVscGVyICE9PSBcIm9yaWdpbmFsXCIgJiYgdGhpcy5oZWxwZXIgJiZcblx0XHRcdFx0XHR0aGlzLmhlbHBlclsgMCBdLnBhcmVudE5vZGUgKSB7XG5cdFx0XHRcdHRoaXMuaGVscGVyLnJlbW92ZSgpO1xuXHRcdFx0fVxuXG5cdFx0XHQkLmV4dGVuZCggdGhpcywge1xuXHRcdFx0XHRoZWxwZXI6IG51bGwsXG5cdFx0XHRcdGRyYWdnaW5nOiBmYWxzZSxcblx0XHRcdFx0cmV2ZXJ0aW5nOiBmYWxzZSxcblx0XHRcdFx0X25vRmluYWxTb3J0OiBudWxsXG5cdFx0XHR9ICk7XG5cblx0XHRcdGlmICggdGhpcy5kb21Qb3NpdGlvbi5wcmV2ICkge1xuXHRcdFx0XHQkKCB0aGlzLmRvbVBvc2l0aW9uLnByZXYgKS5hZnRlciggdGhpcy5jdXJyZW50SXRlbSApO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0JCggdGhpcy5kb21Qb3NpdGlvbi5wYXJlbnQgKS5wcmVwZW5kKCB0aGlzLmN1cnJlbnRJdGVtICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cblx0fSxcblxuXHRzZXJpYWxpemU6IGZ1bmN0aW9uKCBvICkge1xuXG5cdFx0dmFyIGl0ZW1zID0gdGhpcy5fZ2V0SXRlbXNBc2pRdWVyeSggbyAmJiBvLmNvbm5lY3RlZCApLFxuXHRcdFx0c3RyID0gW107XG5cdFx0byA9IG8gfHwge307XG5cblx0XHQkKCBpdGVtcyApLmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0dmFyIHJlcyA9ICggJCggby5pdGVtIHx8IHRoaXMgKS5hdHRyKCBvLmF0dHJpYnV0ZSB8fCBcImlkXCIgKSB8fCBcIlwiIClcblx0XHRcdFx0Lm1hdGNoKCBvLmV4cHJlc3Npb24gfHwgKCAvKC4rKVtcXC09X10oLispLyApICk7XG5cdFx0XHRpZiAoIHJlcyApIHtcblx0XHRcdFx0c3RyLnB1c2goXG5cdFx0XHRcdFx0KCBvLmtleSB8fCByZXNbIDEgXSArIFwiW11cIiApICtcblx0XHRcdFx0XHRcIj1cIiArICggby5rZXkgJiYgby5leHByZXNzaW9uID8gcmVzWyAxIF0gOiByZXNbIDIgXSApICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdFx0aWYgKCAhc3RyLmxlbmd0aCAmJiBvLmtleSApIHtcblx0XHRcdHN0ci5wdXNoKCBvLmtleSArIFwiPVwiICk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHN0ci5qb2luKCBcIiZcIiApO1xuXG5cdH0sXG5cblx0dG9BcnJheTogZnVuY3Rpb24oIG8gKSB7XG5cblx0XHR2YXIgaXRlbXMgPSB0aGlzLl9nZXRJdGVtc0FzalF1ZXJ5KCBvICYmIG8uY29ubmVjdGVkICksXG5cdFx0XHRyZXQgPSBbXTtcblxuXHRcdG8gPSBvIHx8IHt9O1xuXG5cdFx0aXRlbXMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRyZXQucHVzaCggJCggby5pdGVtIHx8IHRoaXMgKS5hdHRyKCBvLmF0dHJpYnV0ZSB8fCBcImlkXCIgKSB8fCBcIlwiICk7XG5cdFx0fSApO1xuXHRcdHJldHVybiByZXQ7XG5cblx0fSxcblxuXHQvKiBCZSBjYXJlZnVsIHdpdGggdGhlIGZvbGxvd2luZyBjb3JlIGZ1bmN0aW9ucyAqL1xuXHRfaW50ZXJzZWN0c1dpdGg6IGZ1bmN0aW9uKCBpdGVtICkge1xuXG5cdFx0dmFyIHgxID0gdGhpcy5wb3NpdGlvbkFicy5sZWZ0LFxuXHRcdFx0eDIgPSB4MSArIHRoaXMuaGVscGVyUHJvcG9ydGlvbnMud2lkdGgsXG5cdFx0XHR5MSA9IHRoaXMucG9zaXRpb25BYnMudG9wLFxuXHRcdFx0eTIgPSB5MSArIHRoaXMuaGVscGVyUHJvcG9ydGlvbnMuaGVpZ2h0LFxuXHRcdFx0bCA9IGl0ZW0ubGVmdCxcblx0XHRcdHIgPSBsICsgaXRlbS53aWR0aCxcblx0XHRcdHQgPSBpdGVtLnRvcCxcblx0XHRcdGIgPSB0ICsgaXRlbS5oZWlnaHQsXG5cdFx0XHRkeUNsaWNrID0gdGhpcy5vZmZzZXQuY2xpY2sudG9wLFxuXHRcdFx0ZHhDbGljayA9IHRoaXMub2Zmc2V0LmNsaWNrLmxlZnQsXG5cdFx0XHRpc092ZXJFbGVtZW50SGVpZ2h0ID0gKCB0aGlzLm9wdGlvbnMuYXhpcyA9PT0gXCJ4XCIgKSB8fCAoICggeTEgKyBkeUNsaWNrICkgPiB0ICYmXG5cdFx0XHRcdCggeTEgKyBkeUNsaWNrICkgPCBiICksXG5cdFx0XHRpc092ZXJFbGVtZW50V2lkdGggPSAoIHRoaXMub3B0aW9ucy5heGlzID09PSBcInlcIiApIHx8ICggKCB4MSArIGR4Q2xpY2sgKSA+IGwgJiZcblx0XHRcdFx0KCB4MSArIGR4Q2xpY2sgKSA8IHIgKSxcblx0XHRcdGlzT3ZlckVsZW1lbnQgPSBpc092ZXJFbGVtZW50SGVpZ2h0ICYmIGlzT3ZlckVsZW1lbnRXaWR0aDtcblxuXHRcdGlmICggdGhpcy5vcHRpb25zLnRvbGVyYW5jZSA9PT0gXCJwb2ludGVyXCIgfHxcblx0XHRcdHRoaXMub3B0aW9ucy5mb3JjZVBvaW50ZXJGb3JDb250YWluZXJzIHx8XG5cdFx0XHQoIHRoaXMub3B0aW9ucy50b2xlcmFuY2UgIT09IFwicG9pbnRlclwiICYmXG5cdFx0XHRcdHRoaXMuaGVscGVyUHJvcG9ydGlvbnNbIHRoaXMuZmxvYXRpbmcgPyBcIndpZHRoXCIgOiBcImhlaWdodFwiIF0gPlxuXHRcdFx0XHRpdGVtWyB0aGlzLmZsb2F0aW5nID8gXCJ3aWR0aFwiIDogXCJoZWlnaHRcIiBdIClcblx0XHQpIHtcblx0XHRcdHJldHVybiBpc092ZXJFbGVtZW50O1xuXHRcdH0gZWxzZSB7XG5cblx0XHRcdHJldHVybiAoIGwgPCB4MSArICggdGhpcy5oZWxwZXJQcm9wb3J0aW9ucy53aWR0aCAvIDIgKSAmJiAvLyBSaWdodCBIYWxmXG5cdFx0XHRcdHgyIC0gKCB0aGlzLmhlbHBlclByb3BvcnRpb25zLndpZHRoIC8gMiApIDwgciAmJiAvLyBMZWZ0IEhhbGZcblx0XHRcdFx0dCA8IHkxICsgKCB0aGlzLmhlbHBlclByb3BvcnRpb25zLmhlaWdodCAvIDIgKSAmJiAvLyBCb3R0b20gSGFsZlxuXHRcdFx0XHR5MiAtICggdGhpcy5oZWxwZXJQcm9wb3J0aW9ucy5oZWlnaHQgLyAyICkgPCBiICk7IC8vIFRvcCBIYWxmXG5cblx0XHR9XG5cdH0sXG5cblx0X2ludGVyc2VjdHNXaXRoUG9pbnRlcjogZnVuY3Rpb24oIGl0ZW0gKSB7XG5cdFx0dmFyIHZlcnRpY2FsRGlyZWN0aW9uLCBob3Jpem9udGFsRGlyZWN0aW9uLFxuXHRcdFx0aXNPdmVyRWxlbWVudEhlaWdodCA9ICggdGhpcy5vcHRpb25zLmF4aXMgPT09IFwieFwiICkgfHxcblx0XHRcdFx0dGhpcy5faXNPdmVyQXhpcyhcblx0XHRcdFx0XHR0aGlzLnBvc2l0aW9uQWJzLnRvcCArIHRoaXMub2Zmc2V0LmNsaWNrLnRvcCwgaXRlbS50b3AsIGl0ZW0uaGVpZ2h0ICksXG5cdFx0XHRpc092ZXJFbGVtZW50V2lkdGggPSAoIHRoaXMub3B0aW9ucy5heGlzID09PSBcInlcIiApIHx8XG5cdFx0XHRcdHRoaXMuX2lzT3ZlckF4aXMoXG5cdFx0XHRcdFx0dGhpcy5wb3NpdGlvbkFicy5sZWZ0ICsgdGhpcy5vZmZzZXQuY2xpY2subGVmdCwgaXRlbS5sZWZ0LCBpdGVtLndpZHRoICksXG5cdFx0XHRpc092ZXJFbGVtZW50ID0gaXNPdmVyRWxlbWVudEhlaWdodCAmJiBpc092ZXJFbGVtZW50V2lkdGg7XG5cblx0XHRpZiAoICFpc092ZXJFbGVtZW50ICkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdHZlcnRpY2FsRGlyZWN0aW9uID0gdGhpcy5fZ2V0RHJhZ1ZlcnRpY2FsRGlyZWN0aW9uKCk7XG5cdFx0aG9yaXpvbnRhbERpcmVjdGlvbiA9IHRoaXMuX2dldERyYWdIb3Jpem9udGFsRGlyZWN0aW9uKCk7XG5cblx0XHRyZXR1cm4gdGhpcy5mbG9hdGluZyA/XG5cdFx0XHQoICggaG9yaXpvbnRhbERpcmVjdGlvbiA9PT0gXCJyaWdodFwiIHx8IHZlcnRpY2FsRGlyZWN0aW9uID09PSBcImRvd25cIiApID8gMiA6IDEgKVxuXHRcdFx0OiAoIHZlcnRpY2FsRGlyZWN0aW9uICYmICggdmVydGljYWxEaXJlY3Rpb24gPT09IFwiZG93blwiID8gMiA6IDEgKSApO1xuXG5cdH0sXG5cblx0X2ludGVyc2VjdHNXaXRoU2lkZXM6IGZ1bmN0aW9uKCBpdGVtICkge1xuXG5cdFx0dmFyIGlzT3ZlckJvdHRvbUhhbGYgPSB0aGlzLl9pc092ZXJBeGlzKCB0aGlzLnBvc2l0aW9uQWJzLnRvcCArXG5cdFx0XHRcdHRoaXMub2Zmc2V0LmNsaWNrLnRvcCwgaXRlbS50b3AgKyAoIGl0ZW0uaGVpZ2h0IC8gMiApLCBpdGVtLmhlaWdodCApLFxuXHRcdFx0aXNPdmVyUmlnaHRIYWxmID0gdGhpcy5faXNPdmVyQXhpcyggdGhpcy5wb3NpdGlvbkFicy5sZWZ0ICtcblx0XHRcdFx0dGhpcy5vZmZzZXQuY2xpY2subGVmdCwgaXRlbS5sZWZ0ICsgKCBpdGVtLndpZHRoIC8gMiApLCBpdGVtLndpZHRoICksXG5cdFx0XHR2ZXJ0aWNhbERpcmVjdGlvbiA9IHRoaXMuX2dldERyYWdWZXJ0aWNhbERpcmVjdGlvbigpLFxuXHRcdFx0aG9yaXpvbnRhbERpcmVjdGlvbiA9IHRoaXMuX2dldERyYWdIb3Jpem9udGFsRGlyZWN0aW9uKCk7XG5cblx0XHRpZiAoIHRoaXMuZmxvYXRpbmcgJiYgaG9yaXpvbnRhbERpcmVjdGlvbiApIHtcblx0XHRcdHJldHVybiAoICggaG9yaXpvbnRhbERpcmVjdGlvbiA9PT0gXCJyaWdodFwiICYmIGlzT3ZlclJpZ2h0SGFsZiApIHx8XG5cdFx0XHRcdCggaG9yaXpvbnRhbERpcmVjdGlvbiA9PT0gXCJsZWZ0XCIgJiYgIWlzT3ZlclJpZ2h0SGFsZiApICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHJldHVybiB2ZXJ0aWNhbERpcmVjdGlvbiAmJiAoICggdmVydGljYWxEaXJlY3Rpb24gPT09IFwiZG93blwiICYmIGlzT3ZlckJvdHRvbUhhbGYgKSB8fFxuXHRcdFx0XHQoIHZlcnRpY2FsRGlyZWN0aW9uID09PSBcInVwXCIgJiYgIWlzT3ZlckJvdHRvbUhhbGYgKSApO1xuXHRcdH1cblxuXHR9LFxuXG5cdF9nZXREcmFnVmVydGljYWxEaXJlY3Rpb246IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBkZWx0YSA9IHRoaXMucG9zaXRpb25BYnMudG9wIC0gdGhpcy5sYXN0UG9zaXRpb25BYnMudG9wO1xuXHRcdHJldHVybiBkZWx0YSAhPT0gMCAmJiAoIGRlbHRhID4gMCA/IFwiZG93blwiIDogXCJ1cFwiICk7XG5cdH0sXG5cblx0X2dldERyYWdIb3Jpem9udGFsRGlyZWN0aW9uOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgZGVsdGEgPSB0aGlzLnBvc2l0aW9uQWJzLmxlZnQgLSB0aGlzLmxhc3RQb3NpdGlvbkFicy5sZWZ0O1xuXHRcdHJldHVybiBkZWx0YSAhPT0gMCAmJiAoIGRlbHRhID4gMCA/IFwicmlnaHRcIiA6IFwibGVmdFwiICk7XG5cdH0sXG5cblx0cmVmcmVzaDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHRoaXMuX3JlZnJlc2hJdGVtcyggZXZlbnQgKTtcblx0XHR0aGlzLl9zZXRIYW5kbGVDbGFzc05hbWUoKTtcblx0XHR0aGlzLnJlZnJlc2hQb3NpdGlvbnMoKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRfY29ubmVjdFdpdGg6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBvcHRpb25zID0gdGhpcy5vcHRpb25zO1xuXHRcdHJldHVybiBvcHRpb25zLmNvbm5lY3RXaXRoLmNvbnN0cnVjdG9yID09PSBTdHJpbmcgP1xuXHRcdFx0WyBvcHRpb25zLmNvbm5lY3RXaXRoIF0gOlxuXHRcdFx0b3B0aW9ucy5jb25uZWN0V2l0aDtcblx0fSxcblxuXHRfZ2V0SXRlbXNBc2pRdWVyeTogZnVuY3Rpb24oIGNvbm5lY3RlZCApIHtcblxuXHRcdHZhciBpLCBqLCBjdXIsIGluc3QsXG5cdFx0XHRpdGVtcyA9IFtdLFxuXHRcdFx0cXVlcmllcyA9IFtdLFxuXHRcdFx0Y29ubmVjdFdpdGggPSB0aGlzLl9jb25uZWN0V2l0aCgpO1xuXG5cdFx0aWYgKCBjb25uZWN0V2l0aCAmJiBjb25uZWN0ZWQgKSB7XG5cdFx0XHRmb3IgKCBpID0gY29ubmVjdFdpdGgubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0gKSB7XG5cdFx0XHRcdGN1ciA9ICQoIGNvbm5lY3RXaXRoWyBpIF0sIHRoaXMuZG9jdW1lbnRbIDAgXSApO1xuXHRcdFx0XHRmb3IgKCBqID0gY3VyLmxlbmd0aCAtIDE7IGogPj0gMDsgai0tICkge1xuXHRcdFx0XHRcdGluc3QgPSAkLmRhdGEoIGN1clsgaiBdLCB0aGlzLndpZGdldEZ1bGxOYW1lICk7XG5cdFx0XHRcdFx0aWYgKCBpbnN0ICYmIGluc3QgIT09IHRoaXMgJiYgIWluc3Qub3B0aW9ucy5kaXNhYmxlZCApIHtcblx0XHRcdFx0XHRcdHF1ZXJpZXMucHVzaCggWyAkLmlzRnVuY3Rpb24oIGluc3Qub3B0aW9ucy5pdGVtcyApID9cblx0XHRcdFx0XHRcdFx0aW5zdC5vcHRpb25zLml0ZW1zLmNhbGwoIGluc3QuZWxlbWVudCApIDpcblx0XHRcdFx0XHRcdFx0JCggaW5zdC5vcHRpb25zLml0ZW1zLCBpbnN0LmVsZW1lbnQgKVxuXHRcdFx0XHRcdFx0XHRcdC5ub3QoIFwiLnVpLXNvcnRhYmxlLWhlbHBlclwiIClcblx0XHRcdFx0XHRcdFx0XHQubm90KCBcIi51aS1zb3J0YWJsZS1wbGFjZWhvbGRlclwiICksIGluc3QgXSApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHF1ZXJpZXMucHVzaCggWyAkLmlzRnVuY3Rpb24oIHRoaXMub3B0aW9ucy5pdGVtcyApID9cblx0XHRcdHRoaXMub3B0aW9ucy5pdGVtc1xuXHRcdFx0XHQuY2FsbCggdGhpcy5lbGVtZW50LCBudWxsLCB7IG9wdGlvbnM6IHRoaXMub3B0aW9ucywgaXRlbTogdGhpcy5jdXJyZW50SXRlbSB9ICkgOlxuXHRcdFx0JCggdGhpcy5vcHRpb25zLml0ZW1zLCB0aGlzLmVsZW1lbnQgKVxuXHRcdFx0XHQubm90KCBcIi51aS1zb3J0YWJsZS1oZWxwZXJcIiApXG5cdFx0XHRcdC5ub3QoIFwiLnVpLXNvcnRhYmxlLXBsYWNlaG9sZGVyXCIgKSwgdGhpcyBdICk7XG5cblx0XHRmdW5jdGlvbiBhZGRJdGVtcygpIHtcblx0XHRcdGl0ZW1zLnB1c2goIHRoaXMgKTtcblx0XHR9XG5cdFx0Zm9yICggaSA9IHF1ZXJpZXMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0gKSB7XG5cdFx0XHRxdWVyaWVzWyBpIF1bIDAgXS5lYWNoKCBhZGRJdGVtcyApO1xuXHRcdH1cblxuXHRcdHJldHVybiAkKCBpdGVtcyApO1xuXG5cdH0sXG5cblx0X3JlbW92ZUN1cnJlbnRzRnJvbUl0ZW1zOiBmdW5jdGlvbigpIHtcblxuXHRcdHZhciBsaXN0ID0gdGhpcy5jdXJyZW50SXRlbS5maW5kKCBcIjpkYXRhKFwiICsgdGhpcy53aWRnZXROYW1lICsgXCItaXRlbSlcIiApO1xuXG5cdFx0dGhpcy5pdGVtcyA9ICQuZ3JlcCggdGhpcy5pdGVtcywgZnVuY3Rpb24oIGl0ZW0gKSB7XG5cdFx0XHRmb3IgKCB2YXIgaiA9IDA7IGogPCBsaXN0Lmxlbmd0aDsgaisrICkge1xuXHRcdFx0XHRpZiAoIGxpc3RbIGogXSA9PT0gaXRlbS5pdGVtWyAwIF0gKSB7XG5cdFx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHR9ICk7XG5cblx0fSxcblxuXHRfcmVmcmVzaEl0ZW1zOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cblx0XHR0aGlzLml0ZW1zID0gW107XG5cdFx0dGhpcy5jb250YWluZXJzID0gWyB0aGlzIF07XG5cblx0XHR2YXIgaSwgaiwgY3VyLCBpbnN0LCB0YXJnZXREYXRhLCBfcXVlcmllcywgaXRlbSwgcXVlcmllc0xlbmd0aCxcblx0XHRcdGl0ZW1zID0gdGhpcy5pdGVtcyxcblx0XHRcdHF1ZXJpZXMgPSBbIFsgJC5pc0Z1bmN0aW9uKCB0aGlzLm9wdGlvbnMuaXRlbXMgKSA/XG5cdFx0XHRcdHRoaXMub3B0aW9ucy5pdGVtcy5jYWxsKCB0aGlzLmVsZW1lbnRbIDAgXSwgZXZlbnQsIHsgaXRlbTogdGhpcy5jdXJyZW50SXRlbSB9ICkgOlxuXHRcdFx0XHQkKCB0aGlzLm9wdGlvbnMuaXRlbXMsIHRoaXMuZWxlbWVudCApLCB0aGlzIF0gXSxcblx0XHRcdGNvbm5lY3RXaXRoID0gdGhpcy5fY29ubmVjdFdpdGgoKTtcblxuXHRcdC8vU2hvdWxkbid0IGJlIHJ1biB0aGUgZmlyc3QgdGltZSB0aHJvdWdoIGR1ZSB0byBtYXNzaXZlIHNsb3ctZG93blxuXHRcdGlmICggY29ubmVjdFdpdGggJiYgdGhpcy5yZWFkeSApIHtcblx0XHRcdGZvciAoIGkgPSBjb25uZWN0V2l0aC5sZW5ndGggLSAxOyBpID49IDA7IGktLSApIHtcblx0XHRcdFx0Y3VyID0gJCggY29ubmVjdFdpdGhbIGkgXSwgdGhpcy5kb2N1bWVudFsgMCBdICk7XG5cdFx0XHRcdGZvciAoIGogPSBjdXIubGVuZ3RoIC0gMTsgaiA+PSAwOyBqLS0gKSB7XG5cdFx0XHRcdFx0aW5zdCA9ICQuZGF0YSggY3VyWyBqIF0sIHRoaXMud2lkZ2V0RnVsbE5hbWUgKTtcblx0XHRcdFx0XHRpZiAoIGluc3QgJiYgaW5zdCAhPT0gdGhpcyAmJiAhaW5zdC5vcHRpb25zLmRpc2FibGVkICkge1xuXHRcdFx0XHRcdFx0cXVlcmllcy5wdXNoKCBbICQuaXNGdW5jdGlvbiggaW5zdC5vcHRpb25zLml0ZW1zICkgP1xuXHRcdFx0XHRcdFx0XHRpbnN0Lm9wdGlvbnMuaXRlbXNcblx0XHRcdFx0XHRcdFx0XHQuY2FsbCggaW5zdC5lbGVtZW50WyAwIF0sIGV2ZW50LCB7IGl0ZW06IHRoaXMuY3VycmVudEl0ZW0gfSApIDpcblx0XHRcdFx0XHRcdFx0JCggaW5zdC5vcHRpb25zLml0ZW1zLCBpbnN0LmVsZW1lbnQgKSwgaW5zdCBdICk7XG5cdFx0XHRcdFx0XHR0aGlzLmNvbnRhaW5lcnMucHVzaCggaW5zdCApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGZvciAoIGkgPSBxdWVyaWVzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tICkge1xuXHRcdFx0dGFyZ2V0RGF0YSA9IHF1ZXJpZXNbIGkgXVsgMSBdO1xuXHRcdFx0X3F1ZXJpZXMgPSBxdWVyaWVzWyBpIF1bIDAgXTtcblxuXHRcdFx0Zm9yICggaiA9IDAsIHF1ZXJpZXNMZW5ndGggPSBfcXVlcmllcy5sZW5ndGg7IGogPCBxdWVyaWVzTGVuZ3RoOyBqKysgKSB7XG5cdFx0XHRcdGl0ZW0gPSAkKCBfcXVlcmllc1sgaiBdICk7XG5cblx0XHRcdFx0Ly8gRGF0YSBmb3IgdGFyZ2V0IGNoZWNraW5nIChtb3VzZSBtYW5hZ2VyKVxuXHRcdFx0XHRpdGVtLmRhdGEoIHRoaXMud2lkZ2V0TmFtZSArIFwiLWl0ZW1cIiwgdGFyZ2V0RGF0YSApO1xuXG5cdFx0XHRcdGl0ZW1zLnB1c2goIHtcblx0XHRcdFx0XHRpdGVtOiBpdGVtLFxuXHRcdFx0XHRcdGluc3RhbmNlOiB0YXJnZXREYXRhLFxuXHRcdFx0XHRcdHdpZHRoOiAwLCBoZWlnaHQ6IDAsXG5cdFx0XHRcdFx0bGVmdDogMCwgdG9wOiAwXG5cdFx0XHRcdH0gKTtcblx0XHRcdH1cblx0XHR9XG5cblx0fSxcblxuXHRyZWZyZXNoUG9zaXRpb25zOiBmdW5jdGlvbiggZmFzdCApIHtcblxuXHRcdC8vIERldGVybWluZSB3aGV0aGVyIGl0ZW1zIGFyZSBiZWluZyBkaXNwbGF5ZWQgaG9yaXpvbnRhbGx5XG5cdFx0dGhpcy5mbG9hdGluZyA9IHRoaXMuaXRlbXMubGVuZ3RoID9cblx0XHRcdHRoaXMub3B0aW9ucy5heGlzID09PSBcInhcIiB8fCB0aGlzLl9pc0Zsb2F0aW5nKCB0aGlzLml0ZW1zWyAwIF0uaXRlbSApIDpcblx0XHRcdGZhbHNlO1xuXG5cdFx0Ly9UaGlzIGhhcyB0byBiZSByZWRvbmUgYmVjYXVzZSBkdWUgdG8gdGhlIGl0ZW0gYmVpbmcgbW92ZWQgb3V0L2ludG8gdGhlIG9mZnNldFBhcmVudCxcblx0XHQvLyB0aGUgb2Zmc2V0UGFyZW50J3MgcG9zaXRpb24gd2lsbCBjaGFuZ2Vcblx0XHRpZiAoIHRoaXMub2Zmc2V0UGFyZW50ICYmIHRoaXMuaGVscGVyICkge1xuXHRcdFx0dGhpcy5vZmZzZXQucGFyZW50ID0gdGhpcy5fZ2V0UGFyZW50T2Zmc2V0KCk7XG5cdFx0fVxuXG5cdFx0dmFyIGksIGl0ZW0sIHQsIHA7XG5cblx0XHRmb3IgKCBpID0gdGhpcy5pdGVtcy5sZW5ndGggLSAxOyBpID49IDA7IGktLSApIHtcblx0XHRcdGl0ZW0gPSB0aGlzLml0ZW1zWyBpIF07XG5cblx0XHRcdC8vV2UgaWdub3JlIGNhbGN1bGF0aW5nIHBvc2l0aW9ucyBvZiBhbGwgY29ubmVjdGVkIGNvbnRhaW5lcnMgd2hlbiB3ZSdyZSBub3Qgb3ZlciB0aGVtXG5cdFx0XHRpZiAoIGl0ZW0uaW5zdGFuY2UgIT09IHRoaXMuY3VycmVudENvbnRhaW5lciAmJiB0aGlzLmN1cnJlbnRDb250YWluZXIgJiZcblx0XHRcdFx0XHRpdGVtLml0ZW1bIDAgXSAhPT0gdGhpcy5jdXJyZW50SXRlbVsgMCBdICkge1xuXHRcdFx0XHRjb250aW51ZTtcblx0XHRcdH1cblxuXHRcdFx0dCA9IHRoaXMub3B0aW9ucy50b2xlcmFuY2VFbGVtZW50ID9cblx0XHRcdFx0JCggdGhpcy5vcHRpb25zLnRvbGVyYW5jZUVsZW1lbnQsIGl0ZW0uaXRlbSApIDpcblx0XHRcdFx0aXRlbS5pdGVtO1xuXG5cdFx0XHRpZiAoICFmYXN0ICkge1xuXHRcdFx0XHRpdGVtLndpZHRoID0gdC5vdXRlcldpZHRoKCk7XG5cdFx0XHRcdGl0ZW0uaGVpZ2h0ID0gdC5vdXRlckhlaWdodCgpO1xuXHRcdFx0fVxuXG5cdFx0XHRwID0gdC5vZmZzZXQoKTtcblx0XHRcdGl0ZW0ubGVmdCA9IHAubGVmdDtcblx0XHRcdGl0ZW0udG9wID0gcC50b3A7XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMuY3VzdG9tICYmIHRoaXMub3B0aW9ucy5jdXN0b20ucmVmcmVzaENvbnRhaW5lcnMgKSB7XG5cdFx0XHR0aGlzLm9wdGlvbnMuY3VzdG9tLnJlZnJlc2hDb250YWluZXJzLmNhbGwoIHRoaXMgKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Zm9yICggaSA9IHRoaXMuY29udGFpbmVycy5sZW5ndGggLSAxOyBpID49IDA7IGktLSApIHtcblx0XHRcdFx0cCA9IHRoaXMuY29udGFpbmVyc1sgaSBdLmVsZW1lbnQub2Zmc2V0KCk7XG5cdFx0XHRcdHRoaXMuY29udGFpbmVyc1sgaSBdLmNvbnRhaW5lckNhY2hlLmxlZnQgPSBwLmxlZnQ7XG5cdFx0XHRcdHRoaXMuY29udGFpbmVyc1sgaSBdLmNvbnRhaW5lckNhY2hlLnRvcCA9IHAudG9wO1xuXHRcdFx0XHR0aGlzLmNvbnRhaW5lcnNbIGkgXS5jb250YWluZXJDYWNoZS53aWR0aCA9XG5cdFx0XHRcdFx0dGhpcy5jb250YWluZXJzWyBpIF0uZWxlbWVudC5vdXRlcldpZHRoKCk7XG5cdFx0XHRcdHRoaXMuY29udGFpbmVyc1sgaSBdLmNvbnRhaW5lckNhY2hlLmhlaWdodCA9XG5cdFx0XHRcdFx0dGhpcy5jb250YWluZXJzWyBpIF0uZWxlbWVudC5vdXRlckhlaWdodCgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdF9jcmVhdGVQbGFjZWhvbGRlcjogZnVuY3Rpb24oIHRoYXQgKSB7XG5cdFx0dGhhdCA9IHRoYXQgfHwgdGhpcztcblx0XHR2YXIgY2xhc3NOYW1lLFxuXHRcdFx0byA9IHRoYXQub3B0aW9ucztcblxuXHRcdGlmICggIW8ucGxhY2Vob2xkZXIgfHwgby5wbGFjZWhvbGRlci5jb25zdHJ1Y3RvciA9PT0gU3RyaW5nICkge1xuXHRcdFx0Y2xhc3NOYW1lID0gby5wbGFjZWhvbGRlcjtcblx0XHRcdG8ucGxhY2Vob2xkZXIgPSB7XG5cdFx0XHRcdGVsZW1lbnQ6IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRcdFx0dmFyIG5vZGVOYW1lID0gdGhhdC5jdXJyZW50SXRlbVsgMCBdLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCksXG5cdFx0XHRcdFx0XHRlbGVtZW50ID0gJCggXCI8XCIgKyBub2RlTmFtZSArIFwiPlwiLCB0aGF0LmRvY3VtZW50WyAwIF0gKTtcblxuXHRcdFx0XHRcdFx0dGhhdC5fYWRkQ2xhc3MoIGVsZW1lbnQsIFwidWktc29ydGFibGUtcGxhY2Vob2xkZXJcIixcblx0XHRcdFx0XHRcdFx0XHRjbGFzc05hbWUgfHwgdGhhdC5jdXJyZW50SXRlbVsgMCBdLmNsYXNzTmFtZSApXG5cdFx0XHRcdFx0XHRcdC5fcmVtb3ZlQ2xhc3MoIGVsZW1lbnQsIFwidWktc29ydGFibGUtaGVscGVyXCIgKTtcblxuXHRcdFx0XHRcdGlmICggbm9kZU5hbWUgPT09IFwidGJvZHlcIiApIHtcblx0XHRcdFx0XHRcdHRoYXQuX2NyZWF0ZVRyUGxhY2Vob2xkZXIoXG5cdFx0XHRcdFx0XHRcdHRoYXQuY3VycmVudEl0ZW0uZmluZCggXCJ0clwiICkuZXEoIDAgKSxcblx0XHRcdFx0XHRcdFx0JCggXCI8dHI+XCIsIHRoYXQuZG9jdW1lbnRbIDAgXSApLmFwcGVuZFRvKCBlbGVtZW50IClcblx0XHRcdFx0XHRcdCk7XG5cdFx0XHRcdFx0fSBlbHNlIGlmICggbm9kZU5hbWUgPT09IFwidHJcIiApIHtcblx0XHRcdFx0XHRcdHRoYXQuX2NyZWF0ZVRyUGxhY2Vob2xkZXIoIHRoYXQuY3VycmVudEl0ZW0sIGVsZW1lbnQgKTtcblx0XHRcdFx0XHR9IGVsc2UgaWYgKCBub2RlTmFtZSA9PT0gXCJpbWdcIiApIHtcblx0XHRcdFx0XHRcdGVsZW1lbnQuYXR0ciggXCJzcmNcIiwgdGhhdC5jdXJyZW50SXRlbS5hdHRyKCBcInNyY1wiICkgKTtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZiAoICFjbGFzc05hbWUgKSB7XG5cdFx0XHRcdFx0XHRlbGVtZW50LmNzcyggXCJ2aXNpYmlsaXR5XCIsIFwiaGlkZGVuXCIgKTtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRyZXR1cm4gZWxlbWVudDtcblx0XHRcdFx0fSxcblx0XHRcdFx0dXBkYXRlOiBmdW5jdGlvbiggY29udGFpbmVyLCBwICkge1xuXG5cdFx0XHRcdFx0Ly8gMS4gSWYgYSBjbGFzc05hbWUgaXMgc2V0IGFzICdwbGFjZWhvbGRlciBvcHRpb24sIHdlIGRvbid0IGZvcmNlIHNpemVzIC1cblx0XHRcdFx0XHQvLyB0aGUgY2xhc3MgaXMgcmVzcG9uc2libGUgZm9yIHRoYXRcblx0XHRcdFx0XHQvLyAyLiBUaGUgb3B0aW9uICdmb3JjZVBsYWNlaG9sZGVyU2l6ZSBjYW4gYmUgZW5hYmxlZCB0byBmb3JjZSBpdCBldmVuIGlmIGFcblx0XHRcdFx0XHQvLyBjbGFzcyBuYW1lIGlzIHNwZWNpZmllZFxuXHRcdFx0XHRcdGlmICggY2xhc3NOYW1lICYmICFvLmZvcmNlUGxhY2Vob2xkZXJTaXplICkge1xuXHRcdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vSWYgdGhlIGVsZW1lbnQgZG9lc24ndCBoYXZlIGEgYWN0dWFsIGhlaWdodCBieSBpdHNlbGYgKHdpdGhvdXQgc3R5bGVzIGNvbWluZ1xuXHRcdFx0XHRcdC8vIGZyb20gYSBzdHlsZXNoZWV0KSwgaXQgcmVjZWl2ZXMgdGhlIGlubGluZSBoZWlnaHQgZnJvbSB0aGUgZHJhZ2dlZCBpdGVtXG5cdFx0XHRcdFx0aWYgKCAhcC5oZWlnaHQoKSApIHtcblx0XHRcdFx0XHRcdHAuaGVpZ2h0KFxuXHRcdFx0XHRcdFx0XHR0aGF0LmN1cnJlbnRJdGVtLmlubmVySGVpZ2h0KCkgLVxuXHRcdFx0XHRcdFx0XHRwYXJzZUludCggdGhhdC5jdXJyZW50SXRlbS5jc3MoIFwicGFkZGluZ1RvcFwiICkgfHwgMCwgMTAgKSAtXG5cdFx0XHRcdFx0XHRcdHBhcnNlSW50KCB0aGF0LmN1cnJlbnRJdGVtLmNzcyggXCJwYWRkaW5nQm90dG9tXCIgKSB8fCAwLCAxMCApICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGlmICggIXAud2lkdGgoKSApIHtcblx0XHRcdFx0XHRcdHAud2lkdGgoXG5cdFx0XHRcdFx0XHRcdHRoYXQuY3VycmVudEl0ZW0uaW5uZXJXaWR0aCgpIC1cblx0XHRcdFx0XHRcdFx0cGFyc2VJbnQoIHRoYXQuY3VycmVudEl0ZW0uY3NzKCBcInBhZGRpbmdMZWZ0XCIgKSB8fCAwLCAxMCApIC1cblx0XHRcdFx0XHRcdFx0cGFyc2VJbnQoIHRoYXQuY3VycmVudEl0ZW0uY3NzKCBcInBhZGRpbmdSaWdodFwiICkgfHwgMCwgMTAgKSApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fTtcblx0XHR9XG5cblx0XHQvL0NyZWF0ZSB0aGUgcGxhY2Vob2xkZXJcblx0XHR0aGF0LnBsYWNlaG9sZGVyID0gJCggby5wbGFjZWhvbGRlci5lbGVtZW50LmNhbGwoIHRoYXQuZWxlbWVudCwgdGhhdC5jdXJyZW50SXRlbSApICk7XG5cblx0XHQvL0FwcGVuZCBpdCBhZnRlciB0aGUgYWN0dWFsIGN1cnJlbnQgaXRlbVxuXHRcdHRoYXQuY3VycmVudEl0ZW0uYWZ0ZXIoIHRoYXQucGxhY2Vob2xkZXIgKTtcblxuXHRcdC8vVXBkYXRlIHRoZSBzaXplIG9mIHRoZSBwbGFjZWhvbGRlciAoVE9ETzogTG9naWMgdG8gZnV6enksIHNlZSBsaW5lIDMxNi8zMTcpXG5cdFx0by5wbGFjZWhvbGRlci51cGRhdGUoIHRoYXQsIHRoYXQucGxhY2Vob2xkZXIgKTtcblxuXHR9LFxuXG5cdF9jcmVhdGVUclBsYWNlaG9sZGVyOiBmdW5jdGlvbiggc291cmNlVHIsIHRhcmdldFRyICkge1xuXHRcdHZhciB0aGF0ID0gdGhpcztcblxuXHRcdHNvdXJjZVRyLmNoaWxkcmVuKCkuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHQkKCBcIjx0ZD4mIzE2MDs8L3RkPlwiLCB0aGF0LmRvY3VtZW50WyAwIF0gKVxuXHRcdFx0XHQuYXR0ciggXCJjb2xzcGFuXCIsICQoIHRoaXMgKS5hdHRyKCBcImNvbHNwYW5cIiApIHx8IDEgKVxuXHRcdFx0XHQuYXBwZW5kVG8oIHRhcmdldFRyICk7XG5cdFx0fSApO1xuXHR9LFxuXG5cdF9jb250YWN0Q29udGFpbmVyczogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdHZhciBpLCBqLCBkaXN0LCBpdGVtV2l0aExlYXN0RGlzdGFuY2UsIHBvc1Byb3BlcnR5LCBzaXplUHJvcGVydHksIGN1ciwgbmVhckJvdHRvbSxcblx0XHRcdGZsb2F0aW5nLCBheGlzLFxuXHRcdFx0aW5uZXJtb3N0Q29udGFpbmVyID0gbnVsbCxcblx0XHRcdGlubmVybW9zdEluZGV4ID0gbnVsbDtcblxuXHRcdC8vIEdldCBpbm5lcm1vc3QgY29udGFpbmVyIHRoYXQgaW50ZXJzZWN0cyB3aXRoIGl0ZW1cblx0XHRmb3IgKCBpID0gdGhpcy5jb250YWluZXJzLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tICkge1xuXG5cdFx0XHQvLyBOZXZlciBjb25zaWRlciBhIGNvbnRhaW5lciB0aGF0J3MgbG9jYXRlZCB3aXRoaW4gdGhlIGl0ZW0gaXRzZWxmXG5cdFx0XHRpZiAoICQuY29udGFpbnMoIHRoaXMuY3VycmVudEl0ZW1bIDAgXSwgdGhpcy5jb250YWluZXJzWyBpIF0uZWxlbWVudFsgMCBdICkgKSB7XG5cdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHRoaXMuX2ludGVyc2VjdHNXaXRoKCB0aGlzLmNvbnRhaW5lcnNbIGkgXS5jb250YWluZXJDYWNoZSApICkge1xuXG5cdFx0XHRcdC8vIElmIHdlJ3ZlIGFscmVhZHkgZm91bmQgYSBjb250YWluZXIgYW5kIGl0J3MgbW9yZSBcImlubmVyXCIgdGhhbiB0aGlzLCB0aGVuIGNvbnRpbnVlXG5cdFx0XHRcdGlmICggaW5uZXJtb3N0Q29udGFpbmVyICYmXG5cdFx0XHRcdFx0XHQkLmNvbnRhaW5zKFxuXHRcdFx0XHRcdFx0XHR0aGlzLmNvbnRhaW5lcnNbIGkgXS5lbGVtZW50WyAwIF0sXG5cdFx0XHRcdFx0XHRcdGlubmVybW9zdENvbnRhaW5lci5lbGVtZW50WyAwIF0gKSApIHtcblx0XHRcdFx0XHRjb250aW51ZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlubmVybW9zdENvbnRhaW5lciA9IHRoaXMuY29udGFpbmVyc1sgaSBdO1xuXHRcdFx0XHRpbm5lcm1vc3RJbmRleCA9IGk7XG5cblx0XHRcdH0gZWxzZSB7XG5cblx0XHRcdFx0Ly8gY29udGFpbmVyIGRvZXNuJ3QgaW50ZXJzZWN0LiB0cmlnZ2VyIFwib3V0XCIgZXZlbnQgaWYgbmVjZXNzYXJ5XG5cdFx0XHRcdGlmICggdGhpcy5jb250YWluZXJzWyBpIF0uY29udGFpbmVyQ2FjaGUub3ZlciApIHtcblx0XHRcdFx0XHR0aGlzLmNvbnRhaW5lcnNbIGkgXS5fdHJpZ2dlciggXCJvdXRcIiwgZXZlbnQsIHRoaXMuX3VpSGFzaCggdGhpcyApICk7XG5cdFx0XHRcdFx0dGhpcy5jb250YWluZXJzWyBpIF0uY29udGFpbmVyQ2FjaGUub3ZlciA9IDA7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdC8vIElmIG5vIGludGVyc2VjdGluZyBjb250YWluZXJzIGZvdW5kLCByZXR1cm5cblx0XHRpZiAoICFpbm5lcm1vc3RDb250YWluZXIgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gTW92ZSB0aGUgaXRlbSBpbnRvIHRoZSBjb250YWluZXIgaWYgaXQncyBub3QgdGhlcmUgYWxyZWFkeVxuXHRcdGlmICggdGhpcy5jb250YWluZXJzLmxlbmd0aCA9PT0gMSApIHtcblx0XHRcdGlmICggIXRoaXMuY29udGFpbmVyc1sgaW5uZXJtb3N0SW5kZXggXS5jb250YWluZXJDYWNoZS5vdmVyICkge1xuXHRcdFx0XHR0aGlzLmNvbnRhaW5lcnNbIGlubmVybW9zdEluZGV4IF0uX3RyaWdnZXIoIFwib3ZlclwiLCBldmVudCwgdGhpcy5fdWlIYXNoKCB0aGlzICkgKTtcblx0XHRcdFx0dGhpcy5jb250YWluZXJzWyBpbm5lcm1vc3RJbmRleCBdLmNvbnRhaW5lckNhY2hlLm92ZXIgPSAxO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cblx0XHRcdC8vIFdoZW4gZW50ZXJpbmcgYSBuZXcgY29udGFpbmVyLCB3ZSB3aWxsIGZpbmQgdGhlIGl0ZW0gd2l0aCB0aGUgbGVhc3QgZGlzdGFuY2UgYW5kXG5cdFx0XHQvLyBhcHBlbmQgb3VyIGl0ZW0gbmVhciBpdFxuXHRcdFx0ZGlzdCA9IDEwMDAwO1xuXHRcdFx0aXRlbVdpdGhMZWFzdERpc3RhbmNlID0gbnVsbDtcblx0XHRcdGZsb2F0aW5nID0gaW5uZXJtb3N0Q29udGFpbmVyLmZsb2F0aW5nIHx8IHRoaXMuX2lzRmxvYXRpbmcoIHRoaXMuY3VycmVudEl0ZW0gKTtcblx0XHRcdHBvc1Byb3BlcnR5ID0gZmxvYXRpbmcgPyBcImxlZnRcIiA6IFwidG9wXCI7XG5cdFx0XHRzaXplUHJvcGVydHkgPSBmbG9hdGluZyA/IFwid2lkdGhcIiA6IFwiaGVpZ2h0XCI7XG5cdFx0XHRheGlzID0gZmxvYXRpbmcgPyBcInBhZ2VYXCIgOiBcInBhZ2VZXCI7XG5cblx0XHRcdGZvciAoIGogPSB0aGlzLml0ZW1zLmxlbmd0aCAtIDE7IGogPj0gMDsgai0tICkge1xuXHRcdFx0XHRpZiAoICEkLmNvbnRhaW5zKFxuXHRcdFx0XHRcdFx0dGhpcy5jb250YWluZXJzWyBpbm5lcm1vc3RJbmRleCBdLmVsZW1lbnRbIDAgXSwgdGhpcy5pdGVtc1sgaiBdLml0ZW1bIDAgXSApXG5cdFx0XHRcdCkge1xuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmICggdGhpcy5pdGVtc1sgaiBdLml0ZW1bIDAgXSA9PT0gdGhpcy5jdXJyZW50SXRlbVsgMCBdICkge1xuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Y3VyID0gdGhpcy5pdGVtc1sgaiBdLml0ZW0ub2Zmc2V0KClbIHBvc1Byb3BlcnR5IF07XG5cdFx0XHRcdG5lYXJCb3R0b20gPSBmYWxzZTtcblx0XHRcdFx0aWYgKCBldmVudFsgYXhpcyBdIC0gY3VyID4gdGhpcy5pdGVtc1sgaiBdWyBzaXplUHJvcGVydHkgXSAvIDIgKSB7XG5cdFx0XHRcdFx0bmVhckJvdHRvbSA9IHRydWU7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAoIE1hdGguYWJzKCBldmVudFsgYXhpcyBdIC0gY3VyICkgPCBkaXN0ICkge1xuXHRcdFx0XHRcdGRpc3QgPSBNYXRoLmFicyggZXZlbnRbIGF4aXMgXSAtIGN1ciApO1xuXHRcdFx0XHRcdGl0ZW1XaXRoTGVhc3REaXN0YW5jZSA9IHRoaXMuaXRlbXNbIGogXTtcblx0XHRcdFx0XHR0aGlzLmRpcmVjdGlvbiA9IG5lYXJCb3R0b20gPyBcInVwXCIgOiBcImRvd25cIjtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvL0NoZWNrIGlmIGRyb3BPbkVtcHR5IGlzIGVuYWJsZWRcblx0XHRcdGlmICggIWl0ZW1XaXRoTGVhc3REaXN0YW5jZSAmJiAhdGhpcy5vcHRpb25zLmRyb3BPbkVtcHR5ICkge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdGlmICggdGhpcy5jdXJyZW50Q29udGFpbmVyID09PSB0aGlzLmNvbnRhaW5lcnNbIGlubmVybW9zdEluZGV4IF0gKSB7XG5cdFx0XHRcdGlmICggIXRoaXMuY3VycmVudENvbnRhaW5lci5jb250YWluZXJDYWNoZS5vdmVyICkge1xuXHRcdFx0XHRcdHRoaXMuY29udGFpbmVyc1sgaW5uZXJtb3N0SW5kZXggXS5fdHJpZ2dlciggXCJvdmVyXCIsIGV2ZW50LCB0aGlzLl91aUhhc2goKSApO1xuXHRcdFx0XHRcdHRoaXMuY3VycmVudENvbnRhaW5lci5jb250YWluZXJDYWNoZS5vdmVyID0gMTtcblx0XHRcdFx0fVxuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdGl0ZW1XaXRoTGVhc3REaXN0YW5jZSA/XG5cdFx0XHRcdHRoaXMuX3JlYXJyYW5nZSggZXZlbnQsIGl0ZW1XaXRoTGVhc3REaXN0YW5jZSwgbnVsbCwgdHJ1ZSApIDpcblx0XHRcdFx0dGhpcy5fcmVhcnJhbmdlKCBldmVudCwgbnVsbCwgdGhpcy5jb250YWluZXJzWyBpbm5lcm1vc3RJbmRleCBdLmVsZW1lbnQsIHRydWUgKTtcblx0XHRcdHRoaXMuX3RyaWdnZXIoIFwiY2hhbmdlXCIsIGV2ZW50LCB0aGlzLl91aUhhc2goKSApO1xuXHRcdFx0dGhpcy5jb250YWluZXJzWyBpbm5lcm1vc3RJbmRleCBdLl90cmlnZ2VyKCBcImNoYW5nZVwiLCBldmVudCwgdGhpcy5fdWlIYXNoKCB0aGlzICkgKTtcblx0XHRcdHRoaXMuY3VycmVudENvbnRhaW5lciA9IHRoaXMuY29udGFpbmVyc1sgaW5uZXJtb3N0SW5kZXggXTtcblxuXHRcdFx0Ly9VcGRhdGUgdGhlIHBsYWNlaG9sZGVyXG5cdFx0XHR0aGlzLm9wdGlvbnMucGxhY2Vob2xkZXIudXBkYXRlKCB0aGlzLmN1cnJlbnRDb250YWluZXIsIHRoaXMucGxhY2Vob2xkZXIgKTtcblxuXHRcdFx0dGhpcy5jb250YWluZXJzWyBpbm5lcm1vc3RJbmRleCBdLl90cmlnZ2VyKCBcIm92ZXJcIiwgZXZlbnQsIHRoaXMuX3VpSGFzaCggdGhpcyApICk7XG5cdFx0XHR0aGlzLmNvbnRhaW5lcnNbIGlubmVybW9zdEluZGV4IF0uY29udGFpbmVyQ2FjaGUub3ZlciA9IDE7XG5cdFx0fVxuXG5cdH0sXG5cblx0X2NyZWF0ZUhlbHBlcjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXG5cdFx0dmFyIG8gPSB0aGlzLm9wdGlvbnMsXG5cdFx0XHRoZWxwZXIgPSAkLmlzRnVuY3Rpb24oIG8uaGVscGVyICkgP1xuXHRcdFx0XHQkKCBvLmhlbHBlci5hcHBseSggdGhpcy5lbGVtZW50WyAwIF0sIFsgZXZlbnQsIHRoaXMuY3VycmVudEl0ZW0gXSApICkgOlxuXHRcdFx0XHQoIG8uaGVscGVyID09PSBcImNsb25lXCIgPyB0aGlzLmN1cnJlbnRJdGVtLmNsb25lKCkgOiB0aGlzLmN1cnJlbnRJdGVtICk7XG5cblx0XHQvL0FkZCB0aGUgaGVscGVyIHRvIHRoZSBET00gaWYgdGhhdCBkaWRuJ3QgaGFwcGVuIGFscmVhZHlcblx0XHRpZiAoICFoZWxwZXIucGFyZW50cyggXCJib2R5XCIgKS5sZW5ndGggKSB7XG5cdFx0XHQkKCBvLmFwcGVuZFRvICE9PSBcInBhcmVudFwiID9cblx0XHRcdFx0by5hcHBlbmRUbyA6XG5cdFx0XHRcdHRoaXMuY3VycmVudEl0ZW1bIDAgXS5wYXJlbnROb2RlIClbIDAgXS5hcHBlbmRDaGlsZCggaGVscGVyWyAwIF0gKTtcblx0XHR9XG5cblx0XHRpZiAoIGhlbHBlclsgMCBdID09PSB0aGlzLmN1cnJlbnRJdGVtWyAwIF0gKSB7XG5cdFx0XHR0aGlzLl9zdG9yZWRDU1MgPSB7XG5cdFx0XHRcdHdpZHRoOiB0aGlzLmN1cnJlbnRJdGVtWyAwIF0uc3R5bGUud2lkdGgsXG5cdFx0XHRcdGhlaWdodDogdGhpcy5jdXJyZW50SXRlbVsgMCBdLnN0eWxlLmhlaWdodCxcblx0XHRcdFx0cG9zaXRpb246IHRoaXMuY3VycmVudEl0ZW0uY3NzKCBcInBvc2l0aW9uXCIgKSxcblx0XHRcdFx0dG9wOiB0aGlzLmN1cnJlbnRJdGVtLmNzcyggXCJ0b3BcIiApLFxuXHRcdFx0XHRsZWZ0OiB0aGlzLmN1cnJlbnRJdGVtLmNzcyggXCJsZWZ0XCIgKVxuXHRcdFx0fTtcblx0XHR9XG5cblx0XHRpZiAoICFoZWxwZXJbIDAgXS5zdHlsZS53aWR0aCB8fCBvLmZvcmNlSGVscGVyU2l6ZSApIHtcblx0XHRcdGhlbHBlci53aWR0aCggdGhpcy5jdXJyZW50SXRlbS53aWR0aCgpICk7XG5cdFx0fVxuXHRcdGlmICggIWhlbHBlclsgMCBdLnN0eWxlLmhlaWdodCB8fCBvLmZvcmNlSGVscGVyU2l6ZSApIHtcblx0XHRcdGhlbHBlci5oZWlnaHQoIHRoaXMuY3VycmVudEl0ZW0uaGVpZ2h0KCkgKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gaGVscGVyO1xuXG5cdH0sXG5cblx0X2FkanVzdE9mZnNldEZyb21IZWxwZXI6IGZ1bmN0aW9uKCBvYmogKSB7XG5cdFx0aWYgKCB0eXBlb2Ygb2JqID09PSBcInN0cmluZ1wiICkge1xuXHRcdFx0b2JqID0gb2JqLnNwbGl0KCBcIiBcIiApO1xuXHRcdH1cblx0XHRpZiAoICQuaXNBcnJheSggb2JqICkgKSB7XG5cdFx0XHRvYmogPSB7IGxlZnQ6ICtvYmpbIDAgXSwgdG9wOiArb2JqWyAxIF0gfHwgMCB9O1xuXHRcdH1cblx0XHRpZiAoIFwibGVmdFwiIGluIG9iaiApIHtcblx0XHRcdHRoaXMub2Zmc2V0LmNsaWNrLmxlZnQgPSBvYmoubGVmdCArIHRoaXMubWFyZ2lucy5sZWZ0O1xuXHRcdH1cblx0XHRpZiAoIFwicmlnaHRcIiBpbiBvYmogKSB7XG5cdFx0XHR0aGlzLm9mZnNldC5jbGljay5sZWZ0ID0gdGhpcy5oZWxwZXJQcm9wb3J0aW9ucy53aWR0aCAtIG9iai5yaWdodCArIHRoaXMubWFyZ2lucy5sZWZ0O1xuXHRcdH1cblx0XHRpZiAoIFwidG9wXCIgaW4gb2JqICkge1xuXHRcdFx0dGhpcy5vZmZzZXQuY2xpY2sudG9wID0gb2JqLnRvcCArIHRoaXMubWFyZ2lucy50b3A7XG5cdFx0fVxuXHRcdGlmICggXCJib3R0b21cIiBpbiBvYmogKSB7XG5cdFx0XHR0aGlzLm9mZnNldC5jbGljay50b3AgPSB0aGlzLmhlbHBlclByb3BvcnRpb25zLmhlaWdodCAtIG9iai5ib3R0b20gKyB0aGlzLm1hcmdpbnMudG9wO1xuXHRcdH1cblx0fSxcblxuXHRfZ2V0UGFyZW50T2Zmc2V0OiBmdW5jdGlvbigpIHtcblxuXHRcdC8vR2V0IHRoZSBvZmZzZXRQYXJlbnQgYW5kIGNhY2hlIGl0cyBwb3NpdGlvblxuXHRcdHRoaXMub2Zmc2V0UGFyZW50ID0gdGhpcy5oZWxwZXIub2Zmc2V0UGFyZW50KCk7XG5cdFx0dmFyIHBvID0gdGhpcy5vZmZzZXRQYXJlbnQub2Zmc2V0KCk7XG5cblx0XHQvLyBUaGlzIGlzIGEgc3BlY2lhbCBjYXNlIHdoZXJlIHdlIG5lZWQgdG8gbW9kaWZ5IGEgb2Zmc2V0IGNhbGN1bGF0ZWQgb24gc3RhcnQsIHNpbmNlIHRoZVxuXHRcdC8vIGZvbGxvd2luZyBoYXBwZW5lZDpcblx0XHQvLyAxLiBUaGUgcG9zaXRpb24gb2YgdGhlIGhlbHBlciBpcyBhYnNvbHV0ZSwgc28gaXQncyBwb3NpdGlvbiBpcyBjYWxjdWxhdGVkIGJhc2VkIG9uIHRoZVxuXHRcdC8vIG5leHQgcG9zaXRpb25lZCBwYXJlbnRcblx0XHQvLyAyLiBUaGUgYWN0dWFsIG9mZnNldCBwYXJlbnQgaXMgYSBjaGlsZCBvZiB0aGUgc2Nyb2xsIHBhcmVudCwgYW5kIHRoZSBzY3JvbGwgcGFyZW50IGlzbid0XG5cdFx0Ly8gdGhlIGRvY3VtZW50LCB3aGljaCBtZWFucyB0aGF0IHRoZSBzY3JvbGwgaXMgaW5jbHVkZWQgaW4gdGhlIGluaXRpYWwgY2FsY3VsYXRpb24gb2YgdGhlXG5cdFx0Ly8gb2Zmc2V0IG9mIHRoZSBwYXJlbnQsIGFuZCBuZXZlciByZWNhbGN1bGF0ZWQgdXBvbiBkcmFnXG5cdFx0aWYgKCB0aGlzLmNzc1Bvc2l0aW9uID09PSBcImFic29sdXRlXCIgJiYgdGhpcy5zY3JvbGxQYXJlbnRbIDAgXSAhPT0gdGhpcy5kb2N1bWVudFsgMCBdICYmXG5cdFx0XHRcdCQuY29udGFpbnMoIHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0sIHRoaXMub2Zmc2V0UGFyZW50WyAwIF0gKSApIHtcblx0XHRcdHBvLmxlZnQgKz0gdGhpcy5zY3JvbGxQYXJlbnQuc2Nyb2xsTGVmdCgpO1xuXHRcdFx0cG8udG9wICs9IHRoaXMuc2Nyb2xsUGFyZW50LnNjcm9sbFRvcCgpO1xuXHRcdH1cblxuXHRcdC8vIFRoaXMgbmVlZHMgdG8gYmUgYWN0dWFsbHkgZG9uZSBmb3IgYWxsIGJyb3dzZXJzLCBzaW5jZSBwYWdlWC9wYWdlWSBpbmNsdWRlcyB0aGlzXG5cdFx0Ly8gaW5mb3JtYXRpb24gd2l0aCBhbiB1Z2x5IElFIGZpeFxuXHRcdGlmICggdGhpcy5vZmZzZXRQYXJlbnRbIDAgXSA9PT0gdGhpcy5kb2N1bWVudFsgMCBdLmJvZHkgfHxcblx0XHRcdFx0KCB0aGlzLm9mZnNldFBhcmVudFsgMCBdLnRhZ05hbWUgJiZcblx0XHRcdFx0dGhpcy5vZmZzZXRQYXJlbnRbIDAgXS50YWdOYW1lLnRvTG93ZXJDYXNlKCkgPT09IFwiaHRtbFwiICYmICQudWkuaWUgKSApIHtcblx0XHRcdHBvID0geyB0b3A6IDAsIGxlZnQ6IDAgfTtcblx0XHR9XG5cblx0XHRyZXR1cm4ge1xuXHRcdFx0dG9wOiBwby50b3AgKyAoIHBhcnNlSW50KCB0aGlzLm9mZnNldFBhcmVudC5jc3MoIFwiYm9yZGVyVG9wV2lkdGhcIiApLCAxMCApIHx8IDAgKSxcblx0XHRcdGxlZnQ6IHBvLmxlZnQgKyAoIHBhcnNlSW50KCB0aGlzLm9mZnNldFBhcmVudC5jc3MoIFwiYm9yZGVyTGVmdFdpZHRoXCIgKSwgMTAgKSB8fCAwIClcblx0XHR9O1xuXG5cdH0sXG5cblx0X2dldFJlbGF0aXZlT2Zmc2V0OiBmdW5jdGlvbigpIHtcblxuXHRcdGlmICggdGhpcy5jc3NQb3NpdGlvbiA9PT0gXCJyZWxhdGl2ZVwiICkge1xuXHRcdFx0dmFyIHAgPSB0aGlzLmN1cnJlbnRJdGVtLnBvc2l0aW9uKCk7XG5cdFx0XHRyZXR1cm4ge1xuXHRcdFx0XHR0b3A6IHAudG9wIC0gKCBwYXJzZUludCggdGhpcy5oZWxwZXIuY3NzKCBcInRvcFwiICksIDEwICkgfHwgMCApICtcblx0XHRcdFx0XHR0aGlzLnNjcm9sbFBhcmVudC5zY3JvbGxUb3AoKSxcblx0XHRcdFx0bGVmdDogcC5sZWZ0IC0gKCBwYXJzZUludCggdGhpcy5oZWxwZXIuY3NzKCBcImxlZnRcIiApLCAxMCApIHx8IDAgKSArXG5cdFx0XHRcdFx0dGhpcy5zY3JvbGxQYXJlbnQuc2Nyb2xsTGVmdCgpXG5cdFx0XHR9O1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRyZXR1cm4geyB0b3A6IDAsIGxlZnQ6IDAgfTtcblx0XHR9XG5cblx0fSxcblxuXHRfY2FjaGVNYXJnaW5zOiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLm1hcmdpbnMgPSB7XG5cdFx0XHRsZWZ0OiAoIHBhcnNlSW50KCB0aGlzLmN1cnJlbnRJdGVtLmNzcyggXCJtYXJnaW5MZWZ0XCIgKSwgMTAgKSB8fCAwICksXG5cdFx0XHR0b3A6ICggcGFyc2VJbnQoIHRoaXMuY3VycmVudEl0ZW0uY3NzKCBcIm1hcmdpblRvcFwiICksIDEwICkgfHwgMCApXG5cdFx0fTtcblx0fSxcblxuXHRfY2FjaGVIZWxwZXJQcm9wb3J0aW9uczogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5oZWxwZXJQcm9wb3J0aW9ucyA9IHtcblx0XHRcdHdpZHRoOiB0aGlzLmhlbHBlci5vdXRlcldpZHRoKCksXG5cdFx0XHRoZWlnaHQ6IHRoaXMuaGVscGVyLm91dGVySGVpZ2h0KClcblx0XHR9O1xuXHR9LFxuXG5cdF9zZXRDb250YWlubWVudDogZnVuY3Rpb24oKSB7XG5cblx0XHR2YXIgY2UsIGNvLCBvdmVyLFxuXHRcdFx0byA9IHRoaXMub3B0aW9ucztcblx0XHRpZiAoIG8uY29udGFpbm1lbnQgPT09IFwicGFyZW50XCIgKSB7XG5cdFx0XHRvLmNvbnRhaW5tZW50ID0gdGhpcy5oZWxwZXJbIDAgXS5wYXJlbnROb2RlO1xuXHRcdH1cblx0XHRpZiAoIG8uY29udGFpbm1lbnQgPT09IFwiZG9jdW1lbnRcIiB8fCBvLmNvbnRhaW5tZW50ID09PSBcIndpbmRvd1wiICkge1xuXHRcdFx0dGhpcy5jb250YWlubWVudCA9IFtcblx0XHRcdFx0MCAtIHRoaXMub2Zmc2V0LnJlbGF0aXZlLmxlZnQgLSB0aGlzLm9mZnNldC5wYXJlbnQubGVmdCxcblx0XHRcdFx0MCAtIHRoaXMub2Zmc2V0LnJlbGF0aXZlLnRvcCAtIHRoaXMub2Zmc2V0LnBhcmVudC50b3AsXG5cdFx0XHRcdG8uY29udGFpbm1lbnQgPT09IFwiZG9jdW1lbnRcIiA/XG5cdFx0XHRcdFx0dGhpcy5kb2N1bWVudC53aWR0aCgpIDpcblx0XHRcdFx0XHR0aGlzLndpbmRvdy53aWR0aCgpIC0gdGhpcy5oZWxwZXJQcm9wb3J0aW9ucy53aWR0aCAtIHRoaXMubWFyZ2lucy5sZWZ0LFxuXHRcdFx0XHQoIG8uY29udGFpbm1lbnQgPT09IFwiZG9jdW1lbnRcIiA/XG5cdFx0XHRcdFx0KCB0aGlzLmRvY3VtZW50LmhlaWdodCgpIHx8IGRvY3VtZW50LmJvZHkucGFyZW50Tm9kZS5zY3JvbGxIZWlnaHQgKSA6XG5cdFx0XHRcdFx0dGhpcy53aW5kb3cuaGVpZ2h0KCkgfHwgdGhpcy5kb2N1bWVudFsgMCBdLmJvZHkucGFyZW50Tm9kZS5zY3JvbGxIZWlnaHRcblx0XHRcdFx0KSAtIHRoaXMuaGVscGVyUHJvcG9ydGlvbnMuaGVpZ2h0IC0gdGhpcy5tYXJnaW5zLnRvcFxuXHRcdFx0XTtcblx0XHR9XG5cblx0XHRpZiAoICEoIC9eKGRvY3VtZW50fHdpbmRvd3xwYXJlbnQpJC8gKS50ZXN0KCBvLmNvbnRhaW5tZW50ICkgKSB7XG5cdFx0XHRjZSA9ICQoIG8uY29udGFpbm1lbnQgKVsgMCBdO1xuXHRcdFx0Y28gPSAkKCBvLmNvbnRhaW5tZW50ICkub2Zmc2V0KCk7XG5cdFx0XHRvdmVyID0gKCAkKCBjZSApLmNzcyggXCJvdmVyZmxvd1wiICkgIT09IFwiaGlkZGVuXCIgKTtcblxuXHRcdFx0dGhpcy5jb250YWlubWVudCA9IFtcblx0XHRcdFx0Y28ubGVmdCArICggcGFyc2VJbnQoICQoIGNlICkuY3NzKCBcImJvcmRlckxlZnRXaWR0aFwiICksIDEwICkgfHwgMCApICtcblx0XHRcdFx0XHQoIHBhcnNlSW50KCAkKCBjZSApLmNzcyggXCJwYWRkaW5nTGVmdFwiICksIDEwICkgfHwgMCApIC0gdGhpcy5tYXJnaW5zLmxlZnQsXG5cdFx0XHRcdGNvLnRvcCArICggcGFyc2VJbnQoICQoIGNlICkuY3NzKCBcImJvcmRlclRvcFdpZHRoXCIgKSwgMTAgKSB8fCAwICkgK1xuXHRcdFx0XHRcdCggcGFyc2VJbnQoICQoIGNlICkuY3NzKCBcInBhZGRpbmdUb3BcIiApLCAxMCApIHx8IDAgKSAtIHRoaXMubWFyZ2lucy50b3AsXG5cdFx0XHRcdGNvLmxlZnQgKyAoIG92ZXIgPyBNYXRoLm1heCggY2Uuc2Nyb2xsV2lkdGgsIGNlLm9mZnNldFdpZHRoICkgOiBjZS5vZmZzZXRXaWR0aCApIC1cblx0XHRcdFx0XHQoIHBhcnNlSW50KCAkKCBjZSApLmNzcyggXCJib3JkZXJMZWZ0V2lkdGhcIiApLCAxMCApIHx8IDAgKSAtXG5cdFx0XHRcdFx0KCBwYXJzZUludCggJCggY2UgKS5jc3MoIFwicGFkZGluZ1JpZ2h0XCIgKSwgMTAgKSB8fCAwICkgLVxuXHRcdFx0XHRcdHRoaXMuaGVscGVyUHJvcG9ydGlvbnMud2lkdGggLSB0aGlzLm1hcmdpbnMubGVmdCxcblx0XHRcdFx0Y28udG9wICsgKCBvdmVyID8gTWF0aC5tYXgoIGNlLnNjcm9sbEhlaWdodCwgY2Uub2Zmc2V0SGVpZ2h0ICkgOiBjZS5vZmZzZXRIZWlnaHQgKSAtXG5cdFx0XHRcdFx0KCBwYXJzZUludCggJCggY2UgKS5jc3MoIFwiYm9yZGVyVG9wV2lkdGhcIiApLCAxMCApIHx8IDAgKSAtXG5cdFx0XHRcdFx0KCBwYXJzZUludCggJCggY2UgKS5jc3MoIFwicGFkZGluZ0JvdHRvbVwiICksIDEwICkgfHwgMCApIC1cblx0XHRcdFx0XHR0aGlzLmhlbHBlclByb3BvcnRpb25zLmhlaWdodCAtIHRoaXMubWFyZ2lucy50b3Bcblx0XHRcdF07XG5cdFx0fVxuXG5cdH0sXG5cblx0X2NvbnZlcnRQb3NpdGlvblRvOiBmdW5jdGlvbiggZCwgcG9zICkge1xuXG5cdFx0aWYgKCAhcG9zICkge1xuXHRcdFx0cG9zID0gdGhpcy5wb3NpdGlvbjtcblx0XHR9XG5cdFx0dmFyIG1vZCA9IGQgPT09IFwiYWJzb2x1dGVcIiA/IDEgOiAtMSxcblx0XHRcdHNjcm9sbCA9IHRoaXMuY3NzUG9zaXRpb24gPT09IFwiYWJzb2x1dGVcIiAmJlxuXHRcdFx0XHQhKCB0aGlzLnNjcm9sbFBhcmVudFsgMCBdICE9PSB0aGlzLmRvY3VtZW50WyAwIF0gJiZcblx0XHRcdFx0JC5jb250YWlucyggdGhpcy5zY3JvbGxQYXJlbnRbIDAgXSwgdGhpcy5vZmZzZXRQYXJlbnRbIDAgXSApICkgP1xuXHRcdFx0XHRcdHRoaXMub2Zmc2V0UGFyZW50IDpcblx0XHRcdFx0XHR0aGlzLnNjcm9sbFBhcmVudCxcblx0XHRcdHNjcm9sbElzUm9vdE5vZGUgPSAoIC8oaHRtbHxib2R5KS9pICkudGVzdCggc2Nyb2xsWyAwIF0udGFnTmFtZSApO1xuXG5cdFx0cmV0dXJuIHtcblx0XHRcdHRvcDogKFxuXG5cdFx0XHRcdC8vIFRoZSBhYnNvbHV0ZSBtb3VzZSBwb3NpdGlvblxuXHRcdFx0XHRwb3MudG9wXHQrXG5cblx0XHRcdFx0Ly8gT25seSBmb3IgcmVsYXRpdmUgcG9zaXRpb25lZCBub2RlczogUmVsYXRpdmUgb2Zmc2V0IGZyb20gZWxlbWVudCB0byBvZmZzZXQgcGFyZW50XG5cdFx0XHRcdHRoaXMub2Zmc2V0LnJlbGF0aXZlLnRvcCAqIG1vZCArXG5cblx0XHRcdFx0Ly8gVGhlIG9mZnNldFBhcmVudCdzIG9mZnNldCB3aXRob3V0IGJvcmRlcnMgKG9mZnNldCArIGJvcmRlcilcblx0XHRcdFx0dGhpcy5vZmZzZXQucGFyZW50LnRvcCAqIG1vZCAtXG5cdFx0XHRcdCggKCB0aGlzLmNzc1Bvc2l0aW9uID09PSBcImZpeGVkXCIgP1xuXHRcdFx0XHRcdC10aGlzLnNjcm9sbFBhcmVudC5zY3JvbGxUb3AoKSA6XG5cdFx0XHRcdFx0KCBzY3JvbGxJc1Jvb3ROb2RlID8gMCA6IHNjcm9sbC5zY3JvbGxUb3AoKSApICkgKiBtb2QgKVxuXHRcdFx0KSxcblx0XHRcdGxlZnQ6IChcblxuXHRcdFx0XHQvLyBUaGUgYWJzb2x1dGUgbW91c2UgcG9zaXRpb25cblx0XHRcdFx0cG9zLmxlZnQgK1xuXG5cdFx0XHRcdC8vIE9ubHkgZm9yIHJlbGF0aXZlIHBvc2l0aW9uZWQgbm9kZXM6IFJlbGF0aXZlIG9mZnNldCBmcm9tIGVsZW1lbnQgdG8gb2Zmc2V0IHBhcmVudFxuXHRcdFx0XHR0aGlzLm9mZnNldC5yZWxhdGl2ZS5sZWZ0ICogbW9kICtcblxuXHRcdFx0XHQvLyBUaGUgb2Zmc2V0UGFyZW50J3Mgb2Zmc2V0IHdpdGhvdXQgYm9yZGVycyAob2Zmc2V0ICsgYm9yZGVyKVxuXHRcdFx0XHR0aGlzLm9mZnNldC5wYXJlbnQubGVmdCAqIG1vZFx0LVxuXHRcdFx0XHQoICggdGhpcy5jc3NQb3NpdGlvbiA9PT0gXCJmaXhlZFwiID9cblx0XHRcdFx0XHQtdGhpcy5zY3JvbGxQYXJlbnQuc2Nyb2xsTGVmdCgpIDogc2Nyb2xsSXNSb290Tm9kZSA/IDAgOlxuXHRcdFx0XHRcdHNjcm9sbC5zY3JvbGxMZWZ0KCkgKSAqIG1vZCApXG5cdFx0XHQpXG5cdFx0fTtcblxuXHR9LFxuXG5cdF9nZW5lcmF0ZVBvc2l0aW9uOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cblx0XHR2YXIgdG9wLCBsZWZ0LFxuXHRcdFx0byA9IHRoaXMub3B0aW9ucyxcblx0XHRcdHBhZ2VYID0gZXZlbnQucGFnZVgsXG5cdFx0XHRwYWdlWSA9IGV2ZW50LnBhZ2VZLFxuXHRcdFx0c2Nyb2xsID0gdGhpcy5jc3NQb3NpdGlvbiA9PT0gXCJhYnNvbHV0ZVwiICYmXG5cdFx0XHRcdCEoIHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0gIT09IHRoaXMuZG9jdW1lbnRbIDAgXSAmJlxuXHRcdFx0XHQkLmNvbnRhaW5zKCB0aGlzLnNjcm9sbFBhcmVudFsgMCBdLCB0aGlzLm9mZnNldFBhcmVudFsgMCBdICkgKSA/XG5cdFx0XHRcdFx0dGhpcy5vZmZzZXRQYXJlbnQgOlxuXHRcdFx0XHRcdHRoaXMuc2Nyb2xsUGFyZW50LFxuXHRcdFx0XHRzY3JvbGxJc1Jvb3ROb2RlID0gKCAvKGh0bWx8Ym9keSkvaSApLnRlc3QoIHNjcm9sbFsgMCBdLnRhZ05hbWUgKTtcblxuXHRcdC8vIFRoaXMgaXMgYW5vdGhlciB2ZXJ5IHdlaXJkIHNwZWNpYWwgY2FzZSB0aGF0IG9ubHkgaGFwcGVucyBmb3IgcmVsYXRpdmUgZWxlbWVudHM6XG5cdFx0Ly8gMS4gSWYgdGhlIGNzcyBwb3NpdGlvbiBpcyByZWxhdGl2ZVxuXHRcdC8vIDIuIGFuZCB0aGUgc2Nyb2xsIHBhcmVudCBpcyB0aGUgZG9jdW1lbnQgb3Igc2ltaWxhciB0byB0aGUgb2Zmc2V0IHBhcmVudFxuXHRcdC8vIHdlIGhhdmUgdG8gcmVmcmVzaCB0aGUgcmVsYXRpdmUgb2Zmc2V0IGR1cmluZyB0aGUgc2Nyb2xsIHNvIHRoZXJlIGFyZSBubyBqdW1wc1xuXHRcdGlmICggdGhpcy5jc3NQb3NpdGlvbiA9PT0gXCJyZWxhdGl2ZVwiICYmICEoIHRoaXMuc2Nyb2xsUGFyZW50WyAwIF0gIT09IHRoaXMuZG9jdW1lbnRbIDAgXSAmJlxuXHRcdFx0XHR0aGlzLnNjcm9sbFBhcmVudFsgMCBdICE9PSB0aGlzLm9mZnNldFBhcmVudFsgMCBdICkgKSB7XG5cdFx0XHR0aGlzLm9mZnNldC5yZWxhdGl2ZSA9IHRoaXMuX2dldFJlbGF0aXZlT2Zmc2V0KCk7XG5cdFx0fVxuXG5cdFx0Lypcblx0XHQgKiAtIFBvc2l0aW9uIGNvbnN0cmFpbmluZyAtXG5cdFx0ICogQ29uc3RyYWluIHRoZSBwb3NpdGlvbiB0byBhIG1peCBvZiBncmlkLCBjb250YWlubWVudC5cblx0XHQgKi9cblxuXHRcdGlmICggdGhpcy5vcmlnaW5hbFBvc2l0aW9uICkgeyAvL0lmIHdlIGFyZSBub3QgZHJhZ2dpbmcgeWV0LCB3ZSB3b24ndCBjaGVjayBmb3Igb3B0aW9uc1xuXG5cdFx0XHRpZiAoIHRoaXMuY29udGFpbm1lbnQgKSB7XG5cdFx0XHRcdGlmICggZXZlbnQucGFnZVggLSB0aGlzLm9mZnNldC5jbGljay5sZWZ0IDwgdGhpcy5jb250YWlubWVudFsgMCBdICkge1xuXHRcdFx0XHRcdHBhZ2VYID0gdGhpcy5jb250YWlubWVudFsgMCBdICsgdGhpcy5vZmZzZXQuY2xpY2subGVmdDtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoIGV2ZW50LnBhZ2VZIC0gdGhpcy5vZmZzZXQuY2xpY2sudG9wIDwgdGhpcy5jb250YWlubWVudFsgMSBdICkge1xuXHRcdFx0XHRcdHBhZ2VZID0gdGhpcy5jb250YWlubWVudFsgMSBdICsgdGhpcy5vZmZzZXQuY2xpY2sudG9wO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmICggZXZlbnQucGFnZVggLSB0aGlzLm9mZnNldC5jbGljay5sZWZ0ID4gdGhpcy5jb250YWlubWVudFsgMiBdICkge1xuXHRcdFx0XHRcdHBhZ2VYID0gdGhpcy5jb250YWlubWVudFsgMiBdICsgdGhpcy5vZmZzZXQuY2xpY2subGVmdDtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoIGV2ZW50LnBhZ2VZIC0gdGhpcy5vZmZzZXQuY2xpY2sudG9wID4gdGhpcy5jb250YWlubWVudFsgMyBdICkge1xuXHRcdFx0XHRcdHBhZ2VZID0gdGhpcy5jb250YWlubWVudFsgMyBdICsgdGhpcy5vZmZzZXQuY2xpY2sudG9wO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdGlmICggby5ncmlkICkge1xuXHRcdFx0XHR0b3AgPSB0aGlzLm9yaWdpbmFsUGFnZVkgKyBNYXRoLnJvdW5kKCAoIHBhZ2VZIC0gdGhpcy5vcmlnaW5hbFBhZ2VZICkgL1xuXHRcdFx0XHRcdG8uZ3JpZFsgMSBdICkgKiBvLmdyaWRbIDEgXTtcblx0XHRcdFx0cGFnZVkgPSB0aGlzLmNvbnRhaW5tZW50ID9cblx0XHRcdFx0XHQoICggdG9wIC0gdGhpcy5vZmZzZXQuY2xpY2sudG9wID49IHRoaXMuY29udGFpbm1lbnRbIDEgXSAmJlxuXHRcdFx0XHRcdFx0dG9wIC0gdGhpcy5vZmZzZXQuY2xpY2sudG9wIDw9IHRoaXMuY29udGFpbm1lbnRbIDMgXSApID9cblx0XHRcdFx0XHRcdFx0dG9wIDpcblx0XHRcdFx0XHRcdFx0KCAoIHRvcCAtIHRoaXMub2Zmc2V0LmNsaWNrLnRvcCA+PSB0aGlzLmNvbnRhaW5tZW50WyAxIF0gKSA/XG5cdFx0XHRcdFx0XHRcdFx0dG9wIC0gby5ncmlkWyAxIF0gOiB0b3AgKyBvLmdyaWRbIDEgXSApICkgOlxuXHRcdFx0XHRcdFx0XHRcdHRvcDtcblxuXHRcdFx0XHRsZWZ0ID0gdGhpcy5vcmlnaW5hbFBhZ2VYICsgTWF0aC5yb3VuZCggKCBwYWdlWCAtIHRoaXMub3JpZ2luYWxQYWdlWCApIC9cblx0XHRcdFx0XHRvLmdyaWRbIDAgXSApICogby5ncmlkWyAwIF07XG5cdFx0XHRcdHBhZ2VYID0gdGhpcy5jb250YWlubWVudCA/XG5cdFx0XHRcdFx0KCAoIGxlZnQgLSB0aGlzLm9mZnNldC5jbGljay5sZWZ0ID49IHRoaXMuY29udGFpbm1lbnRbIDAgXSAmJlxuXHRcdFx0XHRcdFx0bGVmdCAtIHRoaXMub2Zmc2V0LmNsaWNrLmxlZnQgPD0gdGhpcy5jb250YWlubWVudFsgMiBdICkgP1xuXHRcdFx0XHRcdFx0XHRsZWZ0IDpcblx0XHRcdFx0XHRcdFx0KCAoIGxlZnQgLSB0aGlzLm9mZnNldC5jbGljay5sZWZ0ID49IHRoaXMuY29udGFpbm1lbnRbIDAgXSApID9cblx0XHRcdFx0XHRcdFx0XHRsZWZ0IC0gby5ncmlkWyAwIF0gOiBsZWZ0ICsgby5ncmlkWyAwIF0gKSApIDpcblx0XHRcdFx0XHRcdFx0XHRsZWZ0O1xuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIHtcblx0XHRcdHRvcDogKFxuXG5cdFx0XHRcdC8vIFRoZSBhYnNvbHV0ZSBtb3VzZSBwb3NpdGlvblxuXHRcdFx0XHRwYWdlWSAtXG5cblx0XHRcdFx0Ly8gQ2xpY2sgb2Zmc2V0IChyZWxhdGl2ZSB0byB0aGUgZWxlbWVudClcblx0XHRcdFx0dGhpcy5vZmZzZXQuY2xpY2sudG9wIC1cblxuXHRcdFx0XHQvLyBPbmx5IGZvciByZWxhdGl2ZSBwb3NpdGlvbmVkIG5vZGVzOiBSZWxhdGl2ZSBvZmZzZXQgZnJvbSBlbGVtZW50IHRvIG9mZnNldCBwYXJlbnRcblx0XHRcdFx0dGhpcy5vZmZzZXQucmVsYXRpdmUudG9wIC1cblxuXHRcdFx0XHQvLyBUaGUgb2Zmc2V0UGFyZW50J3Mgb2Zmc2V0IHdpdGhvdXQgYm9yZGVycyAob2Zmc2V0ICsgYm9yZGVyKVxuXHRcdFx0XHR0aGlzLm9mZnNldC5wYXJlbnQudG9wICtcblx0XHRcdFx0KCAoIHRoaXMuY3NzUG9zaXRpb24gPT09IFwiZml4ZWRcIiA/XG5cdFx0XHRcdFx0LXRoaXMuc2Nyb2xsUGFyZW50LnNjcm9sbFRvcCgpIDpcblx0XHRcdFx0XHQoIHNjcm9sbElzUm9vdE5vZGUgPyAwIDogc2Nyb2xsLnNjcm9sbFRvcCgpICkgKSApXG5cdFx0XHQpLFxuXHRcdFx0bGVmdDogKFxuXG5cdFx0XHRcdC8vIFRoZSBhYnNvbHV0ZSBtb3VzZSBwb3NpdGlvblxuXHRcdFx0XHRwYWdlWCAtXG5cblx0XHRcdFx0Ly8gQ2xpY2sgb2Zmc2V0IChyZWxhdGl2ZSB0byB0aGUgZWxlbWVudClcblx0XHRcdFx0dGhpcy5vZmZzZXQuY2xpY2subGVmdCAtXG5cblx0XHRcdFx0Ly8gT25seSBmb3IgcmVsYXRpdmUgcG9zaXRpb25lZCBub2RlczogUmVsYXRpdmUgb2Zmc2V0IGZyb20gZWxlbWVudCB0byBvZmZzZXQgcGFyZW50XG5cdFx0XHRcdHRoaXMub2Zmc2V0LnJlbGF0aXZlLmxlZnQgLVxuXG5cdFx0XHRcdC8vIFRoZSBvZmZzZXRQYXJlbnQncyBvZmZzZXQgd2l0aG91dCBib3JkZXJzIChvZmZzZXQgKyBib3JkZXIpXG5cdFx0XHRcdHRoaXMub2Zmc2V0LnBhcmVudC5sZWZ0ICtcblx0XHRcdFx0KCAoIHRoaXMuY3NzUG9zaXRpb24gPT09IFwiZml4ZWRcIiA/XG5cdFx0XHRcdFx0LXRoaXMuc2Nyb2xsUGFyZW50LnNjcm9sbExlZnQoKSA6XG5cdFx0XHRcdFx0c2Nyb2xsSXNSb290Tm9kZSA/IDAgOiBzY3JvbGwuc2Nyb2xsTGVmdCgpICkgKVxuXHRcdFx0KVxuXHRcdH07XG5cblx0fSxcblxuXHRfcmVhcnJhbmdlOiBmdW5jdGlvbiggZXZlbnQsIGksIGEsIGhhcmRSZWZyZXNoICkge1xuXG5cdFx0YSA/IGFbIDAgXS5hcHBlbmRDaGlsZCggdGhpcy5wbGFjZWhvbGRlclsgMCBdICkgOlxuXHRcdFx0aS5pdGVtWyAwIF0ucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoIHRoaXMucGxhY2Vob2xkZXJbIDAgXSxcblx0XHRcdFx0KCB0aGlzLmRpcmVjdGlvbiA9PT0gXCJkb3duXCIgPyBpLml0ZW1bIDAgXSA6IGkuaXRlbVsgMCBdLm5leHRTaWJsaW5nICkgKTtcblxuXHRcdC8vVmFyaW91cyB0aGluZ3MgZG9uZSBoZXJlIHRvIGltcHJvdmUgdGhlIHBlcmZvcm1hbmNlOlxuXHRcdC8vIDEuIHdlIGNyZWF0ZSBhIHNldFRpbWVvdXQsIHRoYXQgY2FsbHMgcmVmcmVzaFBvc2l0aW9uc1xuXHRcdC8vIDIuIG9uIHRoZSBpbnN0YW5jZSwgd2UgaGF2ZSBhIGNvdW50ZXIgdmFyaWFibGUsIHRoYXQgZ2V0J3MgaGlnaGVyIGFmdGVyIGV2ZXJ5IGFwcGVuZFxuXHRcdC8vIDMuIG9uIHRoZSBsb2NhbCBzY29wZSwgd2UgY29weSB0aGUgY291bnRlciB2YXJpYWJsZSwgYW5kIGNoZWNrIGluIHRoZSB0aW1lb3V0LFxuXHRcdC8vIGlmIGl0J3Mgc3RpbGwgdGhlIHNhbWVcblx0XHQvLyA0LiB0aGlzIGxldHMgb25seSB0aGUgbGFzdCBhZGRpdGlvbiB0byB0aGUgdGltZW91dCBzdGFjayB0aHJvdWdoXG5cdFx0dGhpcy5jb3VudGVyID0gdGhpcy5jb3VudGVyID8gKyt0aGlzLmNvdW50ZXIgOiAxO1xuXHRcdHZhciBjb3VudGVyID0gdGhpcy5jb3VudGVyO1xuXG5cdFx0dGhpcy5fZGVsYXkoIGZ1bmN0aW9uKCkge1xuXHRcdFx0aWYgKCBjb3VudGVyID09PSB0aGlzLmNvdW50ZXIgKSB7XG5cblx0XHRcdFx0Ly9QcmVjb21wdXRlIGFmdGVyIGVhY2ggRE9NIGluc2VydGlvbiwgTk9UIG9uIG1vdXNlbW92ZVxuXHRcdFx0XHR0aGlzLnJlZnJlc2hQb3NpdGlvbnMoICFoYXJkUmVmcmVzaCApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHR9LFxuXG5cdF9jbGVhcjogZnVuY3Rpb24oIGV2ZW50LCBub1Byb3BhZ2F0aW9uICkge1xuXG5cdFx0dGhpcy5yZXZlcnRpbmcgPSBmYWxzZTtcblxuXHRcdC8vIFdlIGRlbGF5IGFsbCBldmVudHMgdGhhdCBoYXZlIHRvIGJlIHRyaWdnZXJlZCB0byBhZnRlciB0aGUgcG9pbnQgd2hlcmUgdGhlIHBsYWNlaG9sZGVyXG5cdFx0Ly8gaGFzIGJlZW4gcmVtb3ZlZCBhbmQgZXZlcnl0aGluZyBlbHNlIG5vcm1hbGl6ZWQgYWdhaW5cblx0XHR2YXIgaSxcblx0XHRcdGRlbGF5ZWRUcmlnZ2VycyA9IFtdO1xuXG5cdFx0Ly8gV2UgZmlyc3QgaGF2ZSB0byB1cGRhdGUgdGhlIGRvbSBwb3NpdGlvbiBvZiB0aGUgYWN0dWFsIGN1cnJlbnRJdGVtXG5cdFx0Ly8gTm90ZTogZG9uJ3QgZG8gaXQgaWYgdGhlIGN1cnJlbnQgaXRlbSBpcyBhbHJlYWR5IHJlbW92ZWQgKGJ5IGEgdXNlciksIG9yIGl0IGdldHNcblx0XHQvLyByZWFwcGVuZGVkIChzZWUgIzQwODgpXG5cdFx0aWYgKCAhdGhpcy5fbm9GaW5hbFNvcnQgJiYgdGhpcy5jdXJyZW50SXRlbS5wYXJlbnQoKS5sZW5ndGggKSB7XG5cdFx0XHR0aGlzLnBsYWNlaG9sZGVyLmJlZm9yZSggdGhpcy5jdXJyZW50SXRlbSApO1xuXHRcdH1cblx0XHR0aGlzLl9ub0ZpbmFsU29ydCA9IG51bGw7XG5cblx0XHRpZiAoIHRoaXMuaGVscGVyWyAwIF0gPT09IHRoaXMuY3VycmVudEl0ZW1bIDAgXSApIHtcblx0XHRcdGZvciAoIGkgaW4gdGhpcy5fc3RvcmVkQ1NTICkge1xuXHRcdFx0XHRpZiAoIHRoaXMuX3N0b3JlZENTU1sgaSBdID09PSBcImF1dG9cIiB8fCB0aGlzLl9zdG9yZWRDU1NbIGkgXSA9PT0gXCJzdGF0aWNcIiApIHtcblx0XHRcdFx0XHR0aGlzLl9zdG9yZWRDU1NbIGkgXSA9IFwiXCI7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdHRoaXMuY3VycmVudEl0ZW0uY3NzKCB0aGlzLl9zdG9yZWRDU1MgKTtcblx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0aGlzLmN1cnJlbnRJdGVtLCBcInVpLXNvcnRhYmxlLWhlbHBlclwiICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuY3VycmVudEl0ZW0uc2hvdygpO1xuXHRcdH1cblxuXHRcdGlmICggdGhpcy5mcm9tT3V0c2lkZSAmJiAhbm9Qcm9wYWdhdGlvbiApIHtcblx0XHRcdGRlbGF5ZWRUcmlnZ2Vycy5wdXNoKCBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdHRoaXMuX3RyaWdnZXIoIFwicmVjZWl2ZVwiLCBldmVudCwgdGhpcy5fdWlIYXNoKCB0aGlzLmZyb21PdXRzaWRlICkgKTtcblx0XHRcdH0gKTtcblx0XHR9XG5cdFx0aWYgKCAoIHRoaXMuZnJvbU91dHNpZGUgfHxcblx0XHRcdFx0dGhpcy5kb21Qb3NpdGlvbi5wcmV2ICE9PVxuXHRcdFx0XHR0aGlzLmN1cnJlbnRJdGVtLnByZXYoKS5ub3QoIFwiLnVpLXNvcnRhYmxlLWhlbHBlclwiIClbIDAgXSB8fFxuXHRcdFx0XHR0aGlzLmRvbVBvc2l0aW9uLnBhcmVudCAhPT0gdGhpcy5jdXJyZW50SXRlbS5wYXJlbnQoKVsgMCBdICkgJiYgIW5vUHJvcGFnYXRpb24gKSB7XG5cblx0XHRcdC8vIFRyaWdnZXIgdXBkYXRlIGNhbGxiYWNrIGlmIHRoZSBET00gcG9zaXRpb24gaGFzIGNoYW5nZWRcblx0XHRcdGRlbGF5ZWRUcmlnZ2Vycy5wdXNoKCBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdHRoaXMuX3RyaWdnZXIoIFwidXBkYXRlXCIsIGV2ZW50LCB0aGlzLl91aUhhc2goKSApO1xuXHRcdFx0fSApO1xuXHRcdH1cblxuXHRcdC8vIENoZWNrIGlmIHRoZSBpdGVtcyBDb250YWluZXIgaGFzIENoYW5nZWQgYW5kIHRyaWdnZXIgYXBwcm9wcmlhdGVcblx0XHQvLyBldmVudHMuXG5cdFx0aWYgKCB0aGlzICE9PSB0aGlzLmN1cnJlbnRDb250YWluZXIgKSB7XG5cdFx0XHRpZiAoICFub1Byb3BhZ2F0aW9uICkge1xuXHRcdFx0XHRkZWxheWVkVHJpZ2dlcnMucHVzaCggZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0XHRcdHRoaXMuX3RyaWdnZXIoIFwicmVtb3ZlXCIsIGV2ZW50LCB0aGlzLl91aUhhc2goKSApO1xuXHRcdFx0XHR9ICk7XG5cdFx0XHRcdGRlbGF5ZWRUcmlnZ2Vycy5wdXNoKCAoIGZ1bmN0aW9uKCBjICkge1xuXHRcdFx0XHRcdHJldHVybiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdFx0XHRjLl90cmlnZ2VyKCBcInJlY2VpdmVcIiwgZXZlbnQsIHRoaXMuX3VpSGFzaCggdGhpcyApICk7XG5cdFx0XHRcdFx0fTtcblx0XHRcdFx0fSApLmNhbGwoIHRoaXMsIHRoaXMuY3VycmVudENvbnRhaW5lciApICk7XG5cdFx0XHRcdGRlbGF5ZWRUcmlnZ2Vycy5wdXNoKCAoIGZ1bmN0aW9uKCBjICkge1xuXHRcdFx0XHRcdHJldHVybiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdFx0XHRjLl90cmlnZ2VyKCBcInVwZGF0ZVwiLCBldmVudCwgdGhpcy5fdWlIYXNoKCB0aGlzICkgKTtcblx0XHRcdFx0XHR9O1xuXHRcdFx0XHR9ICkuY2FsbCggdGhpcywgdGhpcy5jdXJyZW50Q29udGFpbmVyICkgKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvL1Bvc3QgZXZlbnRzIHRvIGNvbnRhaW5lcnNcblx0XHRmdW5jdGlvbiBkZWxheUV2ZW50KCB0eXBlLCBpbnN0YW5jZSwgY29udGFpbmVyICkge1xuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0Y29udGFpbmVyLl90cmlnZ2VyKCB0eXBlLCBldmVudCwgaW5zdGFuY2UuX3VpSGFzaCggaW5zdGFuY2UgKSApO1xuXHRcdFx0fTtcblx0XHR9XG5cdFx0Zm9yICggaSA9IHRoaXMuY29udGFpbmVycy5sZW5ndGggLSAxOyBpID49IDA7IGktLSApIHtcblx0XHRcdGlmICggIW5vUHJvcGFnYXRpb24gKSB7XG5cdFx0XHRcdGRlbGF5ZWRUcmlnZ2Vycy5wdXNoKCBkZWxheUV2ZW50KCBcImRlYWN0aXZhdGVcIiwgdGhpcywgdGhpcy5jb250YWluZXJzWyBpIF0gKSApO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCB0aGlzLmNvbnRhaW5lcnNbIGkgXS5jb250YWluZXJDYWNoZS5vdmVyICkge1xuXHRcdFx0XHRkZWxheWVkVHJpZ2dlcnMucHVzaCggZGVsYXlFdmVudCggXCJvdXRcIiwgdGhpcywgdGhpcy5jb250YWluZXJzWyBpIF0gKSApO1xuXHRcdFx0XHR0aGlzLmNvbnRhaW5lcnNbIGkgXS5jb250YWluZXJDYWNoZS5vdmVyID0gMDtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvL0RvIHdoYXQgd2FzIG9yaWdpbmFsbHkgaW4gcGx1Z2luc1xuXHRcdGlmICggdGhpcy5zdG9yZWRDdXJzb3IgKSB7XG5cdFx0XHR0aGlzLmRvY3VtZW50LmZpbmQoIFwiYm9keVwiICkuY3NzKCBcImN1cnNvclwiLCB0aGlzLnN0b3JlZEN1cnNvciApO1xuXHRcdFx0dGhpcy5zdG9yZWRTdHlsZXNoZWV0LnJlbW92ZSgpO1xuXHRcdH1cblx0XHRpZiAoIHRoaXMuX3N0b3JlZE9wYWNpdHkgKSB7XG5cdFx0XHR0aGlzLmhlbHBlci5jc3MoIFwib3BhY2l0eVwiLCB0aGlzLl9zdG9yZWRPcGFjaXR5ICk7XG5cdFx0fVxuXHRcdGlmICggdGhpcy5fc3RvcmVkWkluZGV4ICkge1xuXHRcdFx0dGhpcy5oZWxwZXIuY3NzKCBcInpJbmRleFwiLCB0aGlzLl9zdG9yZWRaSW5kZXggPT09IFwiYXV0b1wiID8gXCJcIiA6IHRoaXMuX3N0b3JlZFpJbmRleCApO1xuXHRcdH1cblxuXHRcdHRoaXMuZHJhZ2dpbmcgPSBmYWxzZTtcblxuXHRcdGlmICggIW5vUHJvcGFnYXRpb24gKSB7XG5cdFx0XHR0aGlzLl90cmlnZ2VyKCBcImJlZm9yZVN0b3BcIiwgZXZlbnQsIHRoaXMuX3VpSGFzaCgpICk7XG5cdFx0fVxuXG5cdFx0Ly8kKHRoaXMucGxhY2Vob2xkZXJbMF0pLnJlbW92ZSgpOyB3b3VsZCBoYXZlIGJlZW4gdGhlIGpRdWVyeSB3YXkgLSB1bmZvcnR1bmF0ZWx5LFxuXHRcdC8vIGl0IHVuYmluZHMgQUxMIGV2ZW50cyBmcm9tIHRoZSBvcmlnaW5hbCBub2RlIVxuXHRcdHRoaXMucGxhY2Vob2xkZXJbIDAgXS5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKCB0aGlzLnBsYWNlaG9sZGVyWyAwIF0gKTtcblxuXHRcdGlmICggIXRoaXMuY2FuY2VsSGVscGVyUmVtb3ZhbCApIHtcblx0XHRcdGlmICggdGhpcy5oZWxwZXJbIDAgXSAhPT0gdGhpcy5jdXJyZW50SXRlbVsgMCBdICkge1xuXHRcdFx0XHR0aGlzLmhlbHBlci5yZW1vdmUoKTtcblx0XHRcdH1cblx0XHRcdHRoaXMuaGVscGVyID0gbnVsbDtcblx0XHR9XG5cblx0XHRpZiAoICFub1Byb3BhZ2F0aW9uICkge1xuXHRcdFx0Zm9yICggaSA9IDA7IGkgPCBkZWxheWVkVHJpZ2dlcnMubGVuZ3RoOyBpKysgKSB7XG5cblx0XHRcdFx0Ly8gVHJpZ2dlciBhbGwgZGVsYXllZCBldmVudHNcblx0XHRcdFx0ZGVsYXllZFRyaWdnZXJzWyBpIF0uY2FsbCggdGhpcywgZXZlbnQgKTtcblx0XHRcdH1cblx0XHRcdHRoaXMuX3RyaWdnZXIoIFwic3RvcFwiLCBldmVudCwgdGhpcy5fdWlIYXNoKCkgKTtcblx0XHR9XG5cblx0XHR0aGlzLmZyb21PdXRzaWRlID0gZmFsc2U7XG5cdFx0cmV0dXJuICF0aGlzLmNhbmNlbEhlbHBlclJlbW92YWw7XG5cblx0fSxcblxuXHRfdHJpZ2dlcjogZnVuY3Rpb24oKSB7XG5cdFx0aWYgKCAkLldpZGdldC5wcm90b3R5cGUuX3RyaWdnZXIuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApID09PSBmYWxzZSApIHtcblx0XHRcdHRoaXMuY2FuY2VsKCk7XG5cdFx0fVxuXHR9LFxuXG5cdF91aUhhc2g6IGZ1bmN0aW9uKCBfaW5zdCApIHtcblx0XHR2YXIgaW5zdCA9IF9pbnN0IHx8IHRoaXM7XG5cdFx0cmV0dXJuIHtcblx0XHRcdGhlbHBlcjogaW5zdC5oZWxwZXIsXG5cdFx0XHRwbGFjZWhvbGRlcjogaW5zdC5wbGFjZWhvbGRlciB8fCAkKCBbXSApLFxuXHRcdFx0cG9zaXRpb246IGluc3QucG9zaXRpb24sXG5cdFx0XHRvcmlnaW5hbFBvc2l0aW9uOiBpbnN0Lm9yaWdpbmFsUG9zaXRpb24sXG5cdFx0XHRvZmZzZXQ6IGluc3QucG9zaXRpb25BYnMsXG5cdFx0XHRpdGVtOiBpbnN0LmN1cnJlbnRJdGVtLFxuXHRcdFx0c2VuZGVyOiBfaW5zdCA/IF9pbnN0LmVsZW1lbnQgOiBudWxsXG5cdFx0fTtcblx0fVxuXG59ICk7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgU3Bpbm5lciAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogU3Bpbm5lclxuLy8+Pmdyb3VwOiBXaWRnZXRzXG4vLz4+ZGVzY3JpcHRpb246IERpc3BsYXlzIGJ1dHRvbnMgdG8gZWFzaWx5IGlucHV0IG51bWJlcnMgdmlhIHRoZSBrZXlib2FyZCBvciBtb3VzZS5cbi8vPj5kb2NzOiBodHRwOi8vYXBpLmpxdWVyeXVpLmNvbS9zcGlubmVyL1xuLy8+PmRlbW9zOiBodHRwOi8vanF1ZXJ5dWkuY29tL3NwaW5uZXIvXG4vLz4+Y3NzLnN0cnVjdHVyZTogLi4vLi4vdGhlbWVzL2Jhc2UvY29yZS5jc3Ncbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9zcGlubmVyLmNzc1xuLy8+PmNzcy50aGVtZTogLi4vLi4vdGhlbWVzL2Jhc2UvdGhlbWUuY3NzXG5cblxuXG5mdW5jdGlvbiBzcGlubmVyTW9kaWZlciggZm4gKSB7XG5cdHJldHVybiBmdW5jdGlvbigpIHtcblx0XHR2YXIgcHJldmlvdXMgPSB0aGlzLmVsZW1lbnQudmFsKCk7XG5cdFx0Zm4uYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdHRoaXMuX3JlZnJlc2goKTtcblx0XHRpZiAoIHByZXZpb3VzICE9PSB0aGlzLmVsZW1lbnQudmFsKCkgKSB7XG5cdFx0XHR0aGlzLl90cmlnZ2VyKCBcImNoYW5nZVwiICk7XG5cdFx0fVxuXHR9O1xufVxuXG4kLndpZGdldCggXCJ1aS5zcGlubmVyXCIsIHtcblx0dmVyc2lvbjogXCIxLjEyLjFcIixcblx0ZGVmYXVsdEVsZW1lbnQ6IFwiPGlucHV0PlwiLFxuXHR3aWRnZXRFdmVudFByZWZpeDogXCJzcGluXCIsXG5cdG9wdGlvbnM6IHtcblx0XHRjbGFzc2VzOiB7XG5cdFx0XHRcInVpLXNwaW5uZXJcIjogXCJ1aS1jb3JuZXItYWxsXCIsXG5cdFx0XHRcInVpLXNwaW5uZXItZG93blwiOiBcInVpLWNvcm5lci1iclwiLFxuXHRcdFx0XCJ1aS1zcGlubmVyLXVwXCI6IFwidWktY29ybmVyLXRyXCJcblx0XHR9LFxuXHRcdGN1bHR1cmU6IG51bGwsXG5cdFx0aWNvbnM6IHtcblx0XHRcdGRvd246IFwidWktaWNvbi10cmlhbmdsZS0xLXNcIixcblx0XHRcdHVwOiBcInVpLWljb24tdHJpYW5nbGUtMS1uXCJcblx0XHR9LFxuXHRcdGluY3JlbWVudGFsOiB0cnVlLFxuXHRcdG1heDogbnVsbCxcblx0XHRtaW46IG51bGwsXG5cdFx0bnVtYmVyRm9ybWF0OiBudWxsLFxuXHRcdHBhZ2U6IDEwLFxuXHRcdHN0ZXA6IDEsXG5cblx0XHRjaGFuZ2U6IG51bGwsXG5cdFx0c3BpbjogbnVsbCxcblx0XHRzdGFydDogbnVsbCxcblx0XHRzdG9wOiBudWxsXG5cdH0sXG5cblx0X2NyZWF0ZTogZnVuY3Rpb24oKSB7XG5cblx0XHQvLyBoYW5kbGUgc3RyaW5nIHZhbHVlcyB0aGF0IG5lZWQgdG8gYmUgcGFyc2VkXG5cdFx0dGhpcy5fc2V0T3B0aW9uKCBcIm1heFwiLCB0aGlzLm9wdGlvbnMubWF4ICk7XG5cdFx0dGhpcy5fc2V0T3B0aW9uKCBcIm1pblwiLCB0aGlzLm9wdGlvbnMubWluICk7XG5cdFx0dGhpcy5fc2V0T3B0aW9uKCBcInN0ZXBcIiwgdGhpcy5vcHRpb25zLnN0ZXAgKTtcblxuXHRcdC8vIE9ubHkgZm9ybWF0IGlmIHRoZXJlIGlzIGEgdmFsdWUsIHByZXZlbnRzIHRoZSBmaWVsZCBmcm9tIGJlaW5nIG1hcmtlZFxuXHRcdC8vIGFzIGludmFsaWQgaW4gRmlyZWZveCwgc2VlICM5NTczLlxuXHRcdGlmICggdGhpcy52YWx1ZSgpICE9PSBcIlwiICkge1xuXG5cdFx0XHQvLyBGb3JtYXQgdGhlIHZhbHVlLCBidXQgZG9uJ3QgY29uc3RyYWluLlxuXHRcdFx0dGhpcy5fdmFsdWUoIHRoaXMuZWxlbWVudC52YWwoKSwgdHJ1ZSApO1xuXHRcdH1cblxuXHRcdHRoaXMuX2RyYXcoKTtcblx0XHR0aGlzLl9vbiggdGhpcy5fZXZlbnRzICk7XG5cdFx0dGhpcy5fcmVmcmVzaCgpO1xuXG5cdFx0Ly8gVHVybmluZyBvZmYgYXV0b2NvbXBsZXRlIHByZXZlbnRzIHRoZSBicm93c2VyIGZyb20gcmVtZW1iZXJpbmcgdGhlXG5cdFx0Ly8gdmFsdWUgd2hlbiBuYXZpZ2F0aW5nIHRocm91Z2ggaGlzdG9yeSwgc28gd2UgcmUtZW5hYmxlIGF1dG9jb21wbGV0ZVxuXHRcdC8vIGlmIHRoZSBwYWdlIGlzIHVubG9hZGVkIGJlZm9yZSB0aGUgd2lkZ2V0IGlzIGRlc3Ryb3llZC4gIzc3OTBcblx0XHR0aGlzLl9vbiggdGhpcy53aW5kb3csIHtcblx0XHRcdGJlZm9yZXVubG9hZDogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHRoaXMuZWxlbWVudC5yZW1vdmVBdHRyKCBcImF1dG9jb21wbGV0ZVwiICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXHR9LFxuXG5cdF9nZXRDcmVhdGVPcHRpb25zOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMuX3N1cGVyKCk7XG5cdFx0dmFyIGVsZW1lbnQgPSB0aGlzLmVsZW1lbnQ7XG5cblx0XHQkLmVhY2goIFsgXCJtaW5cIiwgXCJtYXhcIiwgXCJzdGVwXCIgXSwgZnVuY3Rpb24oIGksIG9wdGlvbiApIHtcblx0XHRcdHZhciB2YWx1ZSA9IGVsZW1lbnQuYXR0ciggb3B0aW9uICk7XG5cdFx0XHRpZiAoIHZhbHVlICE9IG51bGwgJiYgdmFsdWUubGVuZ3RoICkge1xuXHRcdFx0XHRvcHRpb25zWyBvcHRpb24gXSA9IHZhbHVlO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHRcdHJldHVybiBvcHRpb25zO1xuXHR9LFxuXG5cdF9ldmVudHM6IHtcblx0XHRrZXlkb3duOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRpZiAoIHRoaXMuX3N0YXJ0KCBldmVudCApICYmIHRoaXMuX2tleWRvd24oIGV2ZW50ICkgKSB7XG5cdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHR9XG5cdFx0fSxcblx0XHRrZXl1cDogXCJfc3RvcFwiLFxuXHRcdGZvY3VzOiBmdW5jdGlvbigpIHtcblx0XHRcdHRoaXMucHJldmlvdXMgPSB0aGlzLmVsZW1lbnQudmFsKCk7XG5cdFx0fSxcblx0XHRibHVyOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRpZiAoIHRoaXMuY2FuY2VsQmx1ciApIHtcblx0XHRcdFx0ZGVsZXRlIHRoaXMuY2FuY2VsQmx1cjtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHR0aGlzLl9zdG9wKCk7XG5cdFx0XHR0aGlzLl9yZWZyZXNoKCk7XG5cdFx0XHRpZiAoIHRoaXMucHJldmlvdXMgIT09IHRoaXMuZWxlbWVudC52YWwoKSApIHtcblx0XHRcdFx0dGhpcy5fdHJpZ2dlciggXCJjaGFuZ2VcIiwgZXZlbnQgKTtcblx0XHRcdH1cblx0XHR9LFxuXHRcdG1vdXNld2hlZWw6IGZ1bmN0aW9uKCBldmVudCwgZGVsdGEgKSB7XG5cdFx0XHRpZiAoICFkZWx0YSApIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCAhdGhpcy5zcGlubmluZyAmJiAhdGhpcy5fc3RhcnQoIGV2ZW50ICkgKSB7XG5cdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fc3BpbiggKCBkZWx0YSA+IDAgPyAxIDogLTEgKSAqIHRoaXMub3B0aW9ucy5zdGVwLCBldmVudCApO1xuXHRcdFx0Y2xlYXJUaW1lb3V0KCB0aGlzLm1vdXNld2hlZWxUaW1lciApO1xuXHRcdFx0dGhpcy5tb3VzZXdoZWVsVGltZXIgPSB0aGlzLl9kZWxheSggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGlmICggdGhpcy5zcGlubmluZyApIHtcblx0XHRcdFx0XHR0aGlzLl9zdG9wKCBldmVudCApO1xuXHRcdFx0XHR9XG5cdFx0XHR9LCAxMDAgKTtcblx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0fSxcblx0XHRcIm1vdXNlZG93biAudWktc3Bpbm5lci1idXR0b25cIjogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0dmFyIHByZXZpb3VzO1xuXG5cdFx0XHQvLyBXZSBuZXZlciB3YW50IHRoZSBidXR0b25zIHRvIGhhdmUgZm9jdXM7IHdoZW5ldmVyIHRoZSB1c2VyIGlzXG5cdFx0XHQvLyBpbnRlcmFjdGluZyB3aXRoIHRoZSBzcGlubmVyLCB0aGUgZm9jdXMgc2hvdWxkIGJlIG9uIHRoZSBpbnB1dC5cblx0XHRcdC8vIElmIHRoZSBpbnB1dCBpcyBmb2N1c2VkIHRoZW4gdGhpcy5wcmV2aW91cyBpcyBwcm9wZXJseSBzZXQgZnJvbVxuXHRcdFx0Ly8gd2hlbiB0aGUgaW5wdXQgZmlyc3QgcmVjZWl2ZWQgZm9jdXMuIElmIHRoZSBpbnB1dCBpcyBub3QgZm9jdXNlZFxuXHRcdFx0Ly8gdGhlbiB3ZSBuZWVkIHRvIHNldCB0aGlzLnByZXZpb3VzIGJhc2VkIG9uIHRoZSB2YWx1ZSBiZWZvcmUgc3Bpbm5pbmcuXG5cdFx0XHRwcmV2aW91cyA9IHRoaXMuZWxlbWVudFsgMCBdID09PSAkLnVpLnNhZmVBY3RpdmVFbGVtZW50KCB0aGlzLmRvY3VtZW50WyAwIF0gKSA/XG5cdFx0XHRcdHRoaXMucHJldmlvdXMgOiB0aGlzLmVsZW1lbnQudmFsKCk7XG5cdFx0XHRmdW5jdGlvbiBjaGVja0ZvY3VzKCkge1xuXHRcdFx0XHR2YXIgaXNBY3RpdmUgPSB0aGlzLmVsZW1lbnRbIDAgXSA9PT0gJC51aS5zYWZlQWN0aXZlRWxlbWVudCggdGhpcy5kb2N1bWVudFsgMCBdICk7XG5cdFx0XHRcdGlmICggIWlzQWN0aXZlICkge1xuXHRcdFx0XHRcdHRoaXMuZWxlbWVudC50cmlnZ2VyKCBcImZvY3VzXCIgKTtcblx0XHRcdFx0XHR0aGlzLnByZXZpb3VzID0gcHJldmlvdXM7XG5cblx0XHRcdFx0XHQvLyBzdXBwb3J0OiBJRVxuXHRcdFx0XHRcdC8vIElFIHNldHMgZm9jdXMgYXN5bmNocm9ub3VzbHksIHNvIHdlIG5lZWQgdG8gY2hlY2sgaWYgZm9jdXNcblx0XHRcdFx0XHQvLyBtb3ZlZCBvZmYgb2YgdGhlIGlucHV0IGJlY2F1c2UgdGhlIHVzZXIgY2xpY2tlZCBvbiB0aGUgYnV0dG9uLlxuXHRcdFx0XHRcdHRoaXMuX2RlbGF5KCBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHRcdHRoaXMucHJldmlvdXMgPSBwcmV2aW91cztcblx0XHRcdFx0XHR9ICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gRW5zdXJlIGZvY3VzIGlzIG9uIChvciBzdGF5cyBvbikgdGhlIHRleHQgZmllbGRcblx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRjaGVja0ZvY3VzLmNhbGwoIHRoaXMgKTtcblxuXHRcdFx0Ly8gU3VwcG9ydDogSUVcblx0XHRcdC8vIElFIGRvZXNuJ3QgcHJldmVudCBtb3ZpbmcgZm9jdXMgZXZlbiB3aXRoIGV2ZW50LnByZXZlbnREZWZhdWx0KClcblx0XHRcdC8vIHNvIHdlIHNldCBhIGZsYWcgdG8ga25vdyB3aGVuIHdlIHNob3VsZCBpZ25vcmUgdGhlIGJsdXIgZXZlbnRcblx0XHRcdC8vIGFuZCBjaGVjayAoYWdhaW4pIGlmIGZvY3VzIG1vdmVkIG9mZiBvZiB0aGUgaW5wdXQuXG5cdFx0XHR0aGlzLmNhbmNlbEJsdXIgPSB0cnVlO1xuXHRcdFx0dGhpcy5fZGVsYXkoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRkZWxldGUgdGhpcy5jYW5jZWxCbHVyO1xuXHRcdFx0XHRjaGVja0ZvY3VzLmNhbGwoIHRoaXMgKTtcblx0XHRcdH0gKTtcblxuXHRcdFx0aWYgKCB0aGlzLl9zdGFydCggZXZlbnQgKSA9PT0gZmFsc2UgKSB7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblxuXHRcdFx0dGhpcy5fcmVwZWF0KCBudWxsLCAkKCBldmVudC5jdXJyZW50VGFyZ2V0IClcblx0XHRcdFx0Lmhhc0NsYXNzKCBcInVpLXNwaW5uZXItdXBcIiApID8gMSA6IC0xLCBldmVudCApO1xuXHRcdH0sXG5cdFx0XCJtb3VzZXVwIC51aS1zcGlubmVyLWJ1dHRvblwiOiBcIl9zdG9wXCIsXG5cdFx0XCJtb3VzZWVudGVyIC51aS1zcGlubmVyLWJ1dHRvblwiOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cblx0XHRcdC8vIGJ1dHRvbiB3aWxsIGFkZCB1aS1zdGF0ZS1hY3RpdmUgaWYgbW91c2Ugd2FzIGRvd24gd2hpbGUgbW91c2VsZWF2ZSBhbmQga2VwdCBkb3duXG5cdFx0XHRpZiAoICEkKCBldmVudC5jdXJyZW50VGFyZ2V0ICkuaGFzQ2xhc3MoIFwidWktc3RhdGUtYWN0aXZlXCIgKSApIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHRoaXMuX3N0YXJ0KCBldmVudCApID09PSBmYWxzZSApIHtcblx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5fcmVwZWF0KCBudWxsLCAkKCBldmVudC5jdXJyZW50VGFyZ2V0IClcblx0XHRcdFx0Lmhhc0NsYXNzKCBcInVpLXNwaW5uZXItdXBcIiApID8gMSA6IC0xLCBldmVudCApO1xuXHRcdH0sXG5cblx0XHQvLyBUT0RPOiBkbyB3ZSByZWFsbHkgd2FudCB0byBjb25zaWRlciB0aGlzIGEgc3RvcD9cblx0XHQvLyBzaG91bGRuJ3Qgd2UganVzdCBzdG9wIHRoZSByZXBlYXRlciBhbmQgd2FpdCB1bnRpbCBtb3VzZXVwIGJlZm9yZVxuXHRcdC8vIHdlIHRyaWdnZXIgdGhlIHN0b3AgZXZlbnQ/XG5cdFx0XCJtb3VzZWxlYXZlIC51aS1zcGlubmVyLWJ1dHRvblwiOiBcIl9zdG9wXCJcblx0fSxcblxuXHQvLyBTdXBwb3J0IG1vYmlsZSBlbmhhbmNlZCBvcHRpb24gYW5kIG1ha2UgYmFja2NvbXBhdCBtb3JlIHNhbmVcblx0X2VuaGFuY2U6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMudWlTcGlubmVyID0gdGhpcy5lbGVtZW50XG5cdFx0XHQuYXR0ciggXCJhdXRvY29tcGxldGVcIiwgXCJvZmZcIiApXG5cdFx0XHQud3JhcCggXCI8c3Bhbj5cIiApXG5cdFx0XHQucGFyZW50KClcblxuXHRcdFx0XHQvLyBBZGQgYnV0dG9uc1xuXHRcdFx0XHQuYXBwZW5kKFxuXHRcdFx0XHRcdFwiPGE+PC9hPjxhPjwvYT5cIlxuXHRcdFx0XHQpO1xuXHR9LFxuXG5cdF9kcmF3OiBmdW5jdGlvbigpIHtcblx0XHR0aGlzLl9lbmhhbmNlKCk7XG5cblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy51aVNwaW5uZXIsIFwidWktc3Bpbm5lclwiLCBcInVpLXdpZGdldCB1aS13aWRnZXQtY29udGVudFwiICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIFwidWktc3Bpbm5lci1pbnB1dFwiICk7XG5cblx0XHR0aGlzLmVsZW1lbnQuYXR0ciggXCJyb2xlXCIsIFwic3BpbmJ1dHRvblwiICk7XG5cblx0XHQvLyBCdXR0b24gYmluZGluZ3Ncblx0XHR0aGlzLmJ1dHRvbnMgPSB0aGlzLnVpU3Bpbm5lci5jaGlsZHJlbiggXCJhXCIgKVxuXHRcdFx0LmF0dHIoIFwidGFiSW5kZXhcIiwgLTEgKVxuXHRcdFx0LmF0dHIoIFwiYXJpYS1oaWRkZW5cIiwgdHJ1ZSApXG5cdFx0XHQuYnV0dG9uKCB7XG5cdFx0XHRcdGNsYXNzZXM6IHtcblx0XHRcdFx0XHRcInVpLWJ1dHRvblwiOiBcIlwiXG5cdFx0XHRcdH1cblx0XHRcdH0gKTtcblxuXHRcdC8vIFRPRE86IFJpZ2h0IG5vdyBidXR0b24gZG9lcyBub3Qgc3VwcG9ydCBjbGFzc2VzIHRoaXMgaXMgYWxyZWFkeSB1cGRhdGVkIGluIGJ1dHRvbiBQUlxuXHRcdHRoaXMuX3JlbW92ZUNsYXNzKCB0aGlzLmJ1dHRvbnMsIFwidWktY29ybmVyLWFsbFwiICk7XG5cblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5idXR0b25zLmZpcnN0KCksIFwidWktc3Bpbm5lci1idXR0b24gdWktc3Bpbm5lci11cFwiICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMuYnV0dG9ucy5sYXN0KCksIFwidWktc3Bpbm5lci1idXR0b24gdWktc3Bpbm5lci1kb3duXCIgKTtcblx0XHR0aGlzLmJ1dHRvbnMuZmlyc3QoKS5idXR0b24oIHtcblx0XHRcdFwiaWNvblwiOiB0aGlzLm9wdGlvbnMuaWNvbnMudXAsXG5cdFx0XHRcInNob3dMYWJlbFwiOiBmYWxzZVxuXHRcdH0gKTtcblx0XHR0aGlzLmJ1dHRvbnMubGFzdCgpLmJ1dHRvbigge1xuXHRcdFx0XCJpY29uXCI6IHRoaXMub3B0aW9ucy5pY29ucy5kb3duLFxuXHRcdFx0XCJzaG93TGFiZWxcIjogZmFsc2Vcblx0XHR9ICk7XG5cblx0XHQvLyBJRSA2IGRvZXNuJ3QgdW5kZXJzdGFuZCBoZWlnaHQ6IDUwJSBmb3IgdGhlIGJ1dHRvbnNcblx0XHQvLyB1bmxlc3MgdGhlIHdyYXBwZXIgaGFzIGFuIGV4cGxpY2l0IGhlaWdodFxuXHRcdGlmICggdGhpcy5idXR0b25zLmhlaWdodCgpID4gTWF0aC5jZWlsKCB0aGlzLnVpU3Bpbm5lci5oZWlnaHQoKSAqIDAuNSApICYmXG5cdFx0XHRcdHRoaXMudWlTcGlubmVyLmhlaWdodCgpID4gMCApIHtcblx0XHRcdHRoaXMudWlTcGlubmVyLmhlaWdodCggdGhpcy51aVNwaW5uZXIuaGVpZ2h0KCkgKTtcblx0XHR9XG5cdH0sXG5cblx0X2tleWRvd246IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgb3B0aW9ucyA9IHRoaXMub3B0aW9ucyxcblx0XHRcdGtleUNvZGUgPSAkLnVpLmtleUNvZGU7XG5cblx0XHRzd2l0Y2ggKCBldmVudC5rZXlDb2RlICkge1xuXHRcdGNhc2Uga2V5Q29kZS5VUDpcblx0XHRcdHRoaXMuX3JlcGVhdCggbnVsbCwgMSwgZXZlbnQgKTtcblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdGNhc2Uga2V5Q29kZS5ET1dOOlxuXHRcdFx0dGhpcy5fcmVwZWF0KCBudWxsLCAtMSwgZXZlbnQgKTtcblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdGNhc2Uga2V5Q29kZS5QQUdFX1VQOlxuXHRcdFx0dGhpcy5fcmVwZWF0KCBudWxsLCBvcHRpb25zLnBhZ2UsIGV2ZW50ICk7XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHRjYXNlIGtleUNvZGUuUEFHRV9ET1dOOlxuXHRcdFx0dGhpcy5fcmVwZWF0KCBudWxsLCAtb3B0aW9ucy5wYWdlLCBldmVudCApO1xuXHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9LFxuXG5cdF9zdGFydDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdGlmICggIXRoaXMuc3Bpbm5pbmcgJiYgdGhpcy5fdHJpZ2dlciggXCJzdGFydFwiLCBldmVudCApID09PSBmYWxzZSApIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHRpZiAoICF0aGlzLmNvdW50ZXIgKSB7XG5cdFx0XHR0aGlzLmNvdW50ZXIgPSAxO1xuXHRcdH1cblx0XHR0aGlzLnNwaW5uaW5nID0gdHJ1ZTtcblx0XHRyZXR1cm4gdHJ1ZTtcblx0fSxcblxuXHRfcmVwZWF0OiBmdW5jdGlvbiggaSwgc3RlcHMsIGV2ZW50ICkge1xuXHRcdGkgPSBpIHx8IDUwMDtcblxuXHRcdGNsZWFyVGltZW91dCggdGhpcy50aW1lciApO1xuXHRcdHRoaXMudGltZXIgPSB0aGlzLl9kZWxheSggZnVuY3Rpb24oKSB7XG5cdFx0XHR0aGlzLl9yZXBlYXQoIDQwLCBzdGVwcywgZXZlbnQgKTtcblx0XHR9LCBpICk7XG5cblx0XHR0aGlzLl9zcGluKCBzdGVwcyAqIHRoaXMub3B0aW9ucy5zdGVwLCBldmVudCApO1xuXHR9LFxuXG5cdF9zcGluOiBmdW5jdGlvbiggc3RlcCwgZXZlbnQgKSB7XG5cdFx0dmFyIHZhbHVlID0gdGhpcy52YWx1ZSgpIHx8IDA7XG5cblx0XHRpZiAoICF0aGlzLmNvdW50ZXIgKSB7XG5cdFx0XHR0aGlzLmNvdW50ZXIgPSAxO1xuXHRcdH1cblxuXHRcdHZhbHVlID0gdGhpcy5fYWRqdXN0VmFsdWUoIHZhbHVlICsgc3RlcCAqIHRoaXMuX2luY3JlbWVudCggdGhpcy5jb3VudGVyICkgKTtcblxuXHRcdGlmICggIXRoaXMuc3Bpbm5pbmcgfHwgdGhpcy5fdHJpZ2dlciggXCJzcGluXCIsIGV2ZW50LCB7IHZhbHVlOiB2YWx1ZSB9ICkgIT09IGZhbHNlICkge1xuXHRcdFx0dGhpcy5fdmFsdWUoIHZhbHVlICk7XG5cdFx0XHR0aGlzLmNvdW50ZXIrKztcblx0XHR9XG5cdH0sXG5cblx0X2luY3JlbWVudDogZnVuY3Rpb24oIGkgKSB7XG5cdFx0dmFyIGluY3JlbWVudGFsID0gdGhpcy5vcHRpb25zLmluY3JlbWVudGFsO1xuXG5cdFx0aWYgKCBpbmNyZW1lbnRhbCApIHtcblx0XHRcdHJldHVybiAkLmlzRnVuY3Rpb24oIGluY3JlbWVudGFsICkgP1xuXHRcdFx0XHRpbmNyZW1lbnRhbCggaSApIDpcblx0XHRcdFx0TWF0aC5mbG9vciggaSAqIGkgKiBpIC8gNTAwMDAgLSBpICogaSAvIDUwMCArIDE3ICogaSAvIDIwMCArIDEgKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gMTtcblx0fSxcblxuXHRfcHJlY2lzaW9uOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgcHJlY2lzaW9uID0gdGhpcy5fcHJlY2lzaW9uT2YoIHRoaXMub3B0aW9ucy5zdGVwICk7XG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMubWluICE9PSBudWxsICkge1xuXHRcdFx0cHJlY2lzaW9uID0gTWF0aC5tYXgoIHByZWNpc2lvbiwgdGhpcy5fcHJlY2lzaW9uT2YoIHRoaXMub3B0aW9ucy5taW4gKSApO1xuXHRcdH1cblx0XHRyZXR1cm4gcHJlY2lzaW9uO1xuXHR9LFxuXG5cdF9wcmVjaXNpb25PZjogZnVuY3Rpb24oIG51bSApIHtcblx0XHR2YXIgc3RyID0gbnVtLnRvU3RyaW5nKCksXG5cdFx0XHRkZWNpbWFsID0gc3RyLmluZGV4T2YoIFwiLlwiICk7XG5cdFx0cmV0dXJuIGRlY2ltYWwgPT09IC0xID8gMCA6IHN0ci5sZW5ndGggLSBkZWNpbWFsIC0gMTtcblx0fSxcblxuXHRfYWRqdXN0VmFsdWU6IGZ1bmN0aW9uKCB2YWx1ZSApIHtcblx0XHR2YXIgYmFzZSwgYWJvdmVNaW4sXG5cdFx0XHRvcHRpb25zID0gdGhpcy5vcHRpb25zO1xuXG5cdFx0Ly8gTWFrZSBzdXJlIHdlJ3JlIGF0IGEgdmFsaWQgc3RlcFxuXHRcdC8vIC0gZmluZCBvdXQgd2hlcmUgd2UgYXJlIHJlbGF0aXZlIHRvIHRoZSBiYXNlIChtaW4gb3IgMClcblx0XHRiYXNlID0gb3B0aW9ucy5taW4gIT09IG51bGwgPyBvcHRpb25zLm1pbiA6IDA7XG5cdFx0YWJvdmVNaW4gPSB2YWx1ZSAtIGJhc2U7XG5cblx0XHQvLyAtIHJvdW5kIHRvIHRoZSBuZWFyZXN0IHN0ZXBcblx0XHRhYm92ZU1pbiA9IE1hdGgucm91bmQoIGFib3ZlTWluIC8gb3B0aW9ucy5zdGVwICkgKiBvcHRpb25zLnN0ZXA7XG5cblx0XHQvLyAtIHJvdW5kaW5nIGlzIGJhc2VkIG9uIDAsIHNvIGFkanVzdCBiYWNrIHRvIG91ciBiYXNlXG5cdFx0dmFsdWUgPSBiYXNlICsgYWJvdmVNaW47XG5cblx0XHQvLyBGaXggcHJlY2lzaW9uIGZyb20gYmFkIEpTIGZsb2F0aW5nIHBvaW50IG1hdGhcblx0XHR2YWx1ZSA9IHBhcnNlRmxvYXQoIHZhbHVlLnRvRml4ZWQoIHRoaXMuX3ByZWNpc2lvbigpICkgKTtcblxuXHRcdC8vIENsYW1wIHRoZSB2YWx1ZVxuXHRcdGlmICggb3B0aW9ucy5tYXggIT09IG51bGwgJiYgdmFsdWUgPiBvcHRpb25zLm1heCApIHtcblx0XHRcdHJldHVybiBvcHRpb25zLm1heDtcblx0XHR9XG5cdFx0aWYgKCBvcHRpb25zLm1pbiAhPT0gbnVsbCAmJiB2YWx1ZSA8IG9wdGlvbnMubWluICkge1xuXHRcdFx0cmV0dXJuIG9wdGlvbnMubWluO1xuXHRcdH1cblxuXHRcdHJldHVybiB2YWx1ZTtcblx0fSxcblxuXHRfc3RvcDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdGlmICggIXRoaXMuc3Bpbm5pbmcgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Y2xlYXJUaW1lb3V0KCB0aGlzLnRpbWVyICk7XG5cdFx0Y2xlYXJUaW1lb3V0KCB0aGlzLm1vdXNld2hlZWxUaW1lciApO1xuXHRcdHRoaXMuY291bnRlciA9IDA7XG5cdFx0dGhpcy5zcGlubmluZyA9IGZhbHNlO1xuXHRcdHRoaXMuX3RyaWdnZXIoIFwic3RvcFwiLCBldmVudCApO1xuXHR9LFxuXG5cdF9zZXRPcHRpb246IGZ1bmN0aW9uKCBrZXksIHZhbHVlICkge1xuXHRcdHZhciBwcmV2VmFsdWUsIGZpcnN0LCBsYXN0O1xuXG5cdFx0aWYgKCBrZXkgPT09IFwiY3VsdHVyZVwiIHx8IGtleSA9PT0gXCJudW1iZXJGb3JtYXRcIiApIHtcblx0XHRcdHByZXZWYWx1ZSA9IHRoaXMuX3BhcnNlKCB0aGlzLmVsZW1lbnQudmFsKCkgKTtcblx0XHRcdHRoaXMub3B0aW9uc1sga2V5IF0gPSB2YWx1ZTtcblx0XHRcdHRoaXMuZWxlbWVudC52YWwoIHRoaXMuX2Zvcm1hdCggcHJldlZhbHVlICkgKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoIGtleSA9PT0gXCJtYXhcIiB8fCBrZXkgPT09IFwibWluXCIgfHwga2V5ID09PSBcInN0ZXBcIiApIHtcblx0XHRcdGlmICggdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiICkge1xuXHRcdFx0XHR2YWx1ZSA9IHRoaXMuX3BhcnNlKCB2YWx1ZSApO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRpZiAoIGtleSA9PT0gXCJpY29uc1wiICkge1xuXHRcdFx0Zmlyc3QgPSB0aGlzLmJ1dHRvbnMuZmlyc3QoKS5maW5kKCBcIi51aS1pY29uXCIgKTtcblx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCBmaXJzdCwgbnVsbCwgdGhpcy5vcHRpb25zLmljb25zLnVwICk7XG5cdFx0XHR0aGlzLl9hZGRDbGFzcyggZmlyc3QsIG51bGwsIHZhbHVlLnVwICk7XG5cdFx0XHRsYXN0ID0gdGhpcy5idXR0b25zLmxhc3QoKS5maW5kKCBcIi51aS1pY29uXCIgKTtcblx0XHRcdHRoaXMuX3JlbW92ZUNsYXNzKCBsYXN0LCBudWxsLCB0aGlzLm9wdGlvbnMuaWNvbnMuZG93biApO1xuXHRcdFx0dGhpcy5fYWRkQ2xhc3MoIGxhc3QsIG51bGwsIHZhbHVlLmRvd24gKTtcblx0XHR9XG5cblx0XHR0aGlzLl9zdXBlcigga2V5LCB2YWx1ZSApO1xuXHR9LFxuXG5cdF9zZXRPcHRpb25EaXNhYmxlZDogZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdHRoaXMuX3N1cGVyKCB2YWx1ZSApO1xuXG5cdFx0dGhpcy5fdG9nZ2xlQ2xhc3MoIHRoaXMudWlTcGlubmVyLCBudWxsLCBcInVpLXN0YXRlLWRpc2FibGVkXCIsICEhdmFsdWUgKTtcblx0XHR0aGlzLmVsZW1lbnQucHJvcCggXCJkaXNhYmxlZFwiLCAhIXZhbHVlICk7XG5cdFx0dGhpcy5idXR0b25zLmJ1dHRvbiggdmFsdWUgPyBcImRpc2FibGVcIiA6IFwiZW5hYmxlXCIgKTtcblx0fSxcblxuXHRfc2V0T3B0aW9uczogc3Bpbm5lck1vZGlmZXIoIGZ1bmN0aW9uKCBvcHRpb25zICkge1xuXHRcdHRoaXMuX3N1cGVyKCBvcHRpb25zICk7XG5cdH0gKSxcblxuXHRfcGFyc2U6IGZ1bmN0aW9uKCB2YWwgKSB7XG5cdFx0aWYgKCB0eXBlb2YgdmFsID09PSBcInN0cmluZ1wiICYmIHZhbCAhPT0gXCJcIiApIHtcblx0XHRcdHZhbCA9IHdpbmRvdy5HbG9iYWxpemUgJiYgdGhpcy5vcHRpb25zLm51bWJlckZvcm1hdCA/XG5cdFx0XHRcdEdsb2JhbGl6ZS5wYXJzZUZsb2F0KCB2YWwsIDEwLCB0aGlzLm9wdGlvbnMuY3VsdHVyZSApIDogK3ZhbDtcblx0XHR9XG5cdFx0cmV0dXJuIHZhbCA9PT0gXCJcIiB8fCBpc05hTiggdmFsICkgPyBudWxsIDogdmFsO1xuXHR9LFxuXG5cdF9mb3JtYXQ6IGZ1bmN0aW9uKCB2YWx1ZSApIHtcblx0XHRpZiAoIHZhbHVlID09PSBcIlwiICkge1xuXHRcdFx0cmV0dXJuIFwiXCI7XG5cdFx0fVxuXHRcdHJldHVybiB3aW5kb3cuR2xvYmFsaXplICYmIHRoaXMub3B0aW9ucy5udW1iZXJGb3JtYXQgP1xuXHRcdFx0R2xvYmFsaXplLmZvcm1hdCggdmFsdWUsIHRoaXMub3B0aW9ucy5udW1iZXJGb3JtYXQsIHRoaXMub3B0aW9ucy5jdWx0dXJlICkgOlxuXHRcdFx0dmFsdWU7XG5cdH0sXG5cblx0X3JlZnJlc2g6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuZWxlbWVudC5hdHRyKCB7XG5cdFx0XHRcImFyaWEtdmFsdWVtaW5cIjogdGhpcy5vcHRpb25zLm1pbixcblx0XHRcdFwiYXJpYS12YWx1ZW1heFwiOiB0aGlzLm9wdGlvbnMubWF4LFxuXG5cdFx0XHQvLyBUT0RPOiB3aGF0IHNob3VsZCB3ZSBkbyB3aXRoIHZhbHVlcyB0aGF0IGNhbid0IGJlIHBhcnNlZD9cblx0XHRcdFwiYXJpYS12YWx1ZW5vd1wiOiB0aGlzLl9wYXJzZSggdGhpcy5lbGVtZW50LnZhbCgpIClcblx0XHR9ICk7XG5cdH0sXG5cblx0aXNWYWxpZDogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHZhbHVlID0gdGhpcy52YWx1ZSgpO1xuXG5cdFx0Ly8gTnVsbCBpcyBpbnZhbGlkXG5cdFx0aWYgKCB2YWx1ZSA9PT0gbnVsbCApIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cblx0XHQvLyBJZiB2YWx1ZSBnZXRzIGFkanVzdGVkLCBpdCdzIGludmFsaWRcblx0XHRyZXR1cm4gdmFsdWUgPT09IHRoaXMuX2FkanVzdFZhbHVlKCB2YWx1ZSApO1xuXHR9LFxuXG5cdC8vIFVwZGF0ZSB0aGUgdmFsdWUgd2l0aG91dCB0cmlnZ2VyaW5nIGNoYW5nZVxuXHRfdmFsdWU6IGZ1bmN0aW9uKCB2YWx1ZSwgYWxsb3dBbnkgKSB7XG5cdFx0dmFyIHBhcnNlZDtcblx0XHRpZiAoIHZhbHVlICE9PSBcIlwiICkge1xuXHRcdFx0cGFyc2VkID0gdGhpcy5fcGFyc2UoIHZhbHVlICk7XG5cdFx0XHRpZiAoIHBhcnNlZCAhPT0gbnVsbCApIHtcblx0XHRcdFx0aWYgKCAhYWxsb3dBbnkgKSB7XG5cdFx0XHRcdFx0cGFyc2VkID0gdGhpcy5fYWRqdXN0VmFsdWUoIHBhcnNlZCApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHZhbHVlID0gdGhpcy5fZm9ybWF0KCBwYXJzZWQgKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0dGhpcy5lbGVtZW50LnZhbCggdmFsdWUgKTtcblx0XHR0aGlzLl9yZWZyZXNoKCk7XG5cdH0sXG5cblx0X2Rlc3Ryb3k6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuZWxlbWVudFxuXHRcdFx0LnByb3AoIFwiZGlzYWJsZWRcIiwgZmFsc2UgKVxuXHRcdFx0LnJlbW92ZUF0dHIoIFwiYXV0b2NvbXBsZXRlIHJvbGUgYXJpYS12YWx1ZW1pbiBhcmlhLXZhbHVlbWF4IGFyaWEtdmFsdWVub3dcIiApO1xuXG5cdFx0dGhpcy51aVNwaW5uZXIucmVwbGFjZVdpdGgoIHRoaXMuZWxlbWVudCApO1xuXHR9LFxuXG5cdHN0ZXBVcDogc3Bpbm5lck1vZGlmZXIoIGZ1bmN0aW9uKCBzdGVwcyApIHtcblx0XHR0aGlzLl9zdGVwVXAoIHN0ZXBzICk7XG5cdH0gKSxcblx0X3N0ZXBVcDogZnVuY3Rpb24oIHN0ZXBzICkge1xuXHRcdGlmICggdGhpcy5fc3RhcnQoKSApIHtcblx0XHRcdHRoaXMuX3NwaW4oICggc3RlcHMgfHwgMSApICogdGhpcy5vcHRpb25zLnN0ZXAgKTtcblx0XHRcdHRoaXMuX3N0b3AoKTtcblx0XHR9XG5cdH0sXG5cblx0c3RlcERvd246IHNwaW5uZXJNb2RpZmVyKCBmdW5jdGlvbiggc3RlcHMgKSB7XG5cdFx0dGhpcy5fc3RlcERvd24oIHN0ZXBzICk7XG5cdH0gKSxcblx0X3N0ZXBEb3duOiBmdW5jdGlvbiggc3RlcHMgKSB7XG5cdFx0aWYgKCB0aGlzLl9zdGFydCgpICkge1xuXHRcdFx0dGhpcy5fc3BpbiggKCBzdGVwcyB8fCAxICkgKiAtdGhpcy5vcHRpb25zLnN0ZXAgKTtcblx0XHRcdHRoaXMuX3N0b3AoKTtcblx0XHR9XG5cdH0sXG5cblx0cGFnZVVwOiBzcGlubmVyTW9kaWZlciggZnVuY3Rpb24oIHBhZ2VzICkge1xuXHRcdHRoaXMuX3N0ZXBVcCggKCBwYWdlcyB8fCAxICkgKiB0aGlzLm9wdGlvbnMucGFnZSApO1xuXHR9ICksXG5cblx0cGFnZURvd246IHNwaW5uZXJNb2RpZmVyKCBmdW5jdGlvbiggcGFnZXMgKSB7XG5cdFx0dGhpcy5fc3RlcERvd24oICggcGFnZXMgfHwgMSApICogdGhpcy5vcHRpb25zLnBhZ2UgKTtcblx0fSApLFxuXG5cdHZhbHVlOiBmdW5jdGlvbiggbmV3VmFsICkge1xuXHRcdGlmICggIWFyZ3VtZW50cy5sZW5ndGggKSB7XG5cdFx0XHRyZXR1cm4gdGhpcy5fcGFyc2UoIHRoaXMuZWxlbWVudC52YWwoKSApO1xuXHRcdH1cblx0XHRzcGlubmVyTW9kaWZlciggdGhpcy5fdmFsdWUgKS5jYWxsKCB0aGlzLCBuZXdWYWwgKTtcblx0fSxcblxuXHR3aWRnZXQ6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLnVpU3Bpbm5lcjtcblx0fVxufSApO1xuXG4vLyBERVBSRUNBVEVEXG4vLyBUT0RPOiBzd2l0Y2ggcmV0dXJuIGJhY2sgdG8gd2lkZ2V0IGRlY2xhcmF0aW9uIGF0IHRvcCBvZiBmaWxlIHdoZW4gdGhpcyBpcyByZW1vdmVkXG5pZiAoICQudWlCYWNrQ29tcGF0ICE9PSBmYWxzZSApIHtcblxuXHQvLyBCYWNrY29tcGF0IGZvciBzcGlubmVyIGh0bWwgZXh0ZW5zaW9uIHBvaW50c1xuXHQkLndpZGdldCggXCJ1aS5zcGlubmVyXCIsICQudWkuc3Bpbm5lciwge1xuXHRcdF9lbmhhbmNlOiBmdW5jdGlvbigpIHtcblx0XHRcdHRoaXMudWlTcGlubmVyID0gdGhpcy5lbGVtZW50XG5cdFx0XHRcdC5hdHRyKCBcImF1dG9jb21wbGV0ZVwiLCBcIm9mZlwiIClcblx0XHRcdFx0LndyYXAoIHRoaXMuX3VpU3Bpbm5lckh0bWwoKSApXG5cdFx0XHRcdC5wYXJlbnQoKVxuXG5cdFx0XHRcdFx0Ly8gQWRkIGJ1dHRvbnNcblx0XHRcdFx0XHQuYXBwZW5kKCB0aGlzLl9idXR0b25IdG1sKCkgKTtcblx0XHR9LFxuXHRcdF91aVNwaW5uZXJIdG1sOiBmdW5jdGlvbigpIHtcblx0XHRcdHJldHVybiBcIjxzcGFuPlwiO1xuXHRcdH0sXG5cblx0XHRfYnV0dG9uSHRtbDogZnVuY3Rpb24oKSB7XG5cdFx0XHRyZXR1cm4gXCI8YT48L2E+PGE+PC9hPlwiO1xuXHRcdH1cblx0fSApO1xufVxuXG52YXIgd2lkZ2V0c1NwaW5uZXIgPSAkLnVpLnNwaW5uZXI7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgVGFicyAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogVGFic1xuLy8+Pmdyb3VwOiBXaWRnZXRzXG4vLz4+ZGVzY3JpcHRpb246IFRyYW5zZm9ybXMgYSBzZXQgb2YgY29udGFpbmVyIGVsZW1lbnRzIGludG8gYSB0YWIgc3RydWN0dXJlLlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL3RhYnMvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vdGFicy9cbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9jb3JlLmNzc1xuLy8+PmNzcy5zdHJ1Y3R1cmU6IC4uLy4uL3RoZW1lcy9iYXNlL3RhYnMuY3NzXG4vLz4+Y3NzLnRoZW1lOiAuLi8uLi90aGVtZXMvYmFzZS90aGVtZS5jc3NcblxuXG5cbiQud2lkZ2V0KCBcInVpLnRhYnNcIiwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHRkZWxheTogMzAwLFxuXHRvcHRpb25zOiB7XG5cdFx0YWN0aXZlOiBudWxsLFxuXHRcdGNsYXNzZXM6IHtcblx0XHRcdFwidWktdGFic1wiOiBcInVpLWNvcm5lci1hbGxcIixcblx0XHRcdFwidWktdGFicy1uYXZcIjogXCJ1aS1jb3JuZXItYWxsXCIsXG5cdFx0XHRcInVpLXRhYnMtcGFuZWxcIjogXCJ1aS1jb3JuZXItYm90dG9tXCIsXG5cdFx0XHRcInVpLXRhYnMtdGFiXCI6IFwidWktY29ybmVyLXRvcFwiXG5cdFx0fSxcblx0XHRjb2xsYXBzaWJsZTogZmFsc2UsXG5cdFx0ZXZlbnQ6IFwiY2xpY2tcIixcblx0XHRoZWlnaHRTdHlsZTogXCJjb250ZW50XCIsXG5cdFx0aGlkZTogbnVsbCxcblx0XHRzaG93OiBudWxsLFxuXG5cdFx0Ly8gQ2FsbGJhY2tzXG5cdFx0YWN0aXZhdGU6IG51bGwsXG5cdFx0YmVmb3JlQWN0aXZhdGU6IG51bGwsXG5cdFx0YmVmb3JlTG9hZDogbnVsbCxcblx0XHRsb2FkOiBudWxsXG5cdH0sXG5cblx0X2lzTG9jYWw6ICggZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHJoYXNoID0gLyMuKiQvO1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uKCBhbmNob3IgKSB7XG5cdFx0XHR2YXIgYW5jaG9yVXJsLCBsb2NhdGlvblVybDtcblxuXHRcdFx0YW5jaG9yVXJsID0gYW5jaG9yLmhyZWYucmVwbGFjZSggcmhhc2gsIFwiXCIgKTtcblx0XHRcdGxvY2F0aW9uVXJsID0gbG9jYXRpb24uaHJlZi5yZXBsYWNlKCByaGFzaCwgXCJcIiApO1xuXG5cdFx0XHQvLyBEZWNvZGluZyBtYXkgdGhyb3cgYW4gZXJyb3IgaWYgdGhlIFVSTCBpc24ndCBVVEYtOCAoIzk1MTgpXG5cdFx0XHR0cnkge1xuXHRcdFx0XHRhbmNob3JVcmwgPSBkZWNvZGVVUklDb21wb25lbnQoIGFuY2hvclVybCApO1xuXHRcdFx0fSBjYXRjaCAoIGVycm9yICkge31cblx0XHRcdHRyeSB7XG5cdFx0XHRcdGxvY2F0aW9uVXJsID0gZGVjb2RlVVJJQ29tcG9uZW50KCBsb2NhdGlvblVybCApO1xuXHRcdFx0fSBjYXRjaCAoIGVycm9yICkge31cblxuXHRcdFx0cmV0dXJuIGFuY2hvci5oYXNoLmxlbmd0aCA+IDEgJiYgYW5jaG9yVXJsID09PSBsb2NhdGlvblVybDtcblx0XHR9O1xuXHR9ICkoKSxcblxuXHRfY3JlYXRlOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgdGhhdCA9IHRoaXMsXG5cdFx0XHRvcHRpb25zID0gdGhpcy5vcHRpb25zO1xuXG5cdFx0dGhpcy5ydW5uaW5nID0gZmFsc2U7XG5cblx0XHR0aGlzLl9hZGRDbGFzcyggXCJ1aS10YWJzXCIsIFwidWktd2lkZ2V0IHVpLXdpZGdldC1jb250ZW50XCIgKTtcblx0XHR0aGlzLl90b2dnbGVDbGFzcyggXCJ1aS10YWJzLWNvbGxhcHNpYmxlXCIsIG51bGwsIG9wdGlvbnMuY29sbGFwc2libGUgKTtcblxuXHRcdHRoaXMuX3Byb2Nlc3NUYWJzKCk7XG5cdFx0b3B0aW9ucy5hY3RpdmUgPSB0aGlzLl9pbml0aWFsQWN0aXZlKCk7XG5cblx0XHQvLyBUYWtlIGRpc2FibGluZyB0YWJzIHZpYSBjbGFzcyBhdHRyaWJ1dGUgZnJvbSBIVE1MXG5cdFx0Ly8gaW50byBhY2NvdW50IGFuZCB1cGRhdGUgb3B0aW9uIHByb3Blcmx5LlxuXHRcdGlmICggJC5pc0FycmF5KCBvcHRpb25zLmRpc2FibGVkICkgKSB7XG5cdFx0XHRvcHRpb25zLmRpc2FibGVkID0gJC51bmlxdWUoIG9wdGlvbnMuZGlzYWJsZWQuY29uY2F0KFxuXHRcdFx0XHQkLm1hcCggdGhpcy50YWJzLmZpbHRlciggXCIudWktc3RhdGUtZGlzYWJsZWRcIiApLCBmdW5jdGlvbiggbGkgKSB7XG5cdFx0XHRcdFx0cmV0dXJuIHRoYXQudGFicy5pbmRleCggbGkgKTtcblx0XHRcdFx0fSApXG5cdFx0XHQpICkuc29ydCgpO1xuXHRcdH1cblxuXHRcdC8vIENoZWNrIGZvciBsZW5ndGggYXZvaWRzIGVycm9yIHdoZW4gaW5pdGlhbGl6aW5nIGVtcHR5IGxpc3Rcblx0XHRpZiAoIHRoaXMub3B0aW9ucy5hY3RpdmUgIT09IGZhbHNlICYmIHRoaXMuYW5jaG9ycy5sZW5ndGggKSB7XG5cdFx0XHR0aGlzLmFjdGl2ZSA9IHRoaXMuX2ZpbmRBY3RpdmUoIG9wdGlvbnMuYWN0aXZlICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuYWN0aXZlID0gJCgpO1xuXHRcdH1cblxuXHRcdHRoaXMuX3JlZnJlc2goKTtcblxuXHRcdGlmICggdGhpcy5hY3RpdmUubGVuZ3RoICkge1xuXHRcdFx0dGhpcy5sb2FkKCBvcHRpb25zLmFjdGl2ZSApO1xuXHRcdH1cblx0fSxcblxuXHRfaW5pdGlhbEFjdGl2ZTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGFjdGl2ZSA9IHRoaXMub3B0aW9ucy5hY3RpdmUsXG5cdFx0XHRjb2xsYXBzaWJsZSA9IHRoaXMub3B0aW9ucy5jb2xsYXBzaWJsZSxcblx0XHRcdGxvY2F0aW9uSGFzaCA9IGxvY2F0aW9uLmhhc2guc3Vic3RyaW5nKCAxICk7XG5cblx0XHRpZiAoIGFjdGl2ZSA9PT0gbnVsbCApIHtcblxuXHRcdFx0Ly8gY2hlY2sgdGhlIGZyYWdtZW50IGlkZW50aWZpZXIgaW4gdGhlIFVSTFxuXHRcdFx0aWYgKCBsb2NhdGlvbkhhc2ggKSB7XG5cdFx0XHRcdHRoaXMudGFicy5lYWNoKCBmdW5jdGlvbiggaSwgdGFiICkge1xuXHRcdFx0XHRcdGlmICggJCggdGFiICkuYXR0ciggXCJhcmlhLWNvbnRyb2xzXCIgKSA9PT0gbG9jYXRpb25IYXNoICkge1xuXHRcdFx0XHRcdFx0YWN0aXZlID0gaTtcblx0XHRcdFx0XHRcdHJldHVybiBmYWxzZTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gQ2hlY2sgZm9yIGEgdGFiIG1hcmtlZCBhY3RpdmUgdmlhIGEgY2xhc3Ncblx0XHRcdGlmICggYWN0aXZlID09PSBudWxsICkge1xuXHRcdFx0XHRhY3RpdmUgPSB0aGlzLnRhYnMuaW5kZXgoIHRoaXMudGFicy5maWx0ZXIoIFwiLnVpLXRhYnMtYWN0aXZlXCIgKSApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBObyBhY3RpdmUgdGFiLCBzZXQgdG8gZmFsc2Vcblx0XHRcdGlmICggYWN0aXZlID09PSBudWxsIHx8IGFjdGl2ZSA9PT0gLTEgKSB7XG5cdFx0XHRcdGFjdGl2ZSA9IHRoaXMudGFicy5sZW5ndGggPyAwIDogZmFsc2U7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gSGFuZGxlIG51bWJlcnM6IG5lZ2F0aXZlLCBvdXQgb2YgcmFuZ2Vcblx0XHRpZiAoIGFjdGl2ZSAhPT0gZmFsc2UgKSB7XG5cdFx0XHRhY3RpdmUgPSB0aGlzLnRhYnMuaW5kZXgoIHRoaXMudGFicy5lcSggYWN0aXZlICkgKTtcblx0XHRcdGlmICggYWN0aXZlID09PSAtMSApIHtcblx0XHRcdFx0YWN0aXZlID0gY29sbGFwc2libGUgPyBmYWxzZSA6IDA7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gRG9uJ3QgYWxsb3cgY29sbGFwc2libGU6IGZhbHNlIGFuZCBhY3RpdmU6IGZhbHNlXG5cdFx0aWYgKCAhY29sbGFwc2libGUgJiYgYWN0aXZlID09PSBmYWxzZSAmJiB0aGlzLmFuY2hvcnMubGVuZ3RoICkge1xuXHRcdFx0YWN0aXZlID0gMDtcblx0XHR9XG5cblx0XHRyZXR1cm4gYWN0aXZlO1xuXHR9LFxuXG5cdF9nZXRDcmVhdGVFdmVudERhdGE6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB7XG5cdFx0XHR0YWI6IHRoaXMuYWN0aXZlLFxuXHRcdFx0cGFuZWw6ICF0aGlzLmFjdGl2ZS5sZW5ndGggPyAkKCkgOiB0aGlzLl9nZXRQYW5lbEZvclRhYiggdGhpcy5hY3RpdmUgKVxuXHRcdH07XG5cdH0sXG5cblx0X3RhYktleWRvd246IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgZm9jdXNlZFRhYiA9ICQoICQudWkuc2FmZUFjdGl2ZUVsZW1lbnQoIHRoaXMuZG9jdW1lbnRbIDAgXSApICkuY2xvc2VzdCggXCJsaVwiICksXG5cdFx0XHRzZWxlY3RlZEluZGV4ID0gdGhpcy50YWJzLmluZGV4KCBmb2N1c2VkVGFiICksXG5cdFx0XHRnb2luZ0ZvcndhcmQgPSB0cnVlO1xuXG5cdFx0aWYgKCB0aGlzLl9oYW5kbGVQYWdlTmF2KCBldmVudCApICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHN3aXRjaCAoIGV2ZW50LmtleUNvZGUgKSB7XG5cdFx0Y2FzZSAkLnVpLmtleUNvZGUuUklHSFQ6XG5cdFx0Y2FzZSAkLnVpLmtleUNvZGUuRE9XTjpcblx0XHRcdHNlbGVjdGVkSW5kZXgrKztcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgJC51aS5rZXlDb2RlLlVQOlxuXHRcdGNhc2UgJC51aS5rZXlDb2RlLkxFRlQ6XG5cdFx0XHRnb2luZ0ZvcndhcmQgPSBmYWxzZTtcblx0XHRcdHNlbGVjdGVkSW5kZXgtLTtcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgJC51aS5rZXlDb2RlLkVORDpcblx0XHRcdHNlbGVjdGVkSW5kZXggPSB0aGlzLmFuY2hvcnMubGVuZ3RoIC0gMTtcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgJC51aS5rZXlDb2RlLkhPTUU6XG5cdFx0XHRzZWxlY3RlZEluZGV4ID0gMDtcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgJC51aS5rZXlDb2RlLlNQQUNFOlxuXG5cdFx0XHQvLyBBY3RpdmF0ZSBvbmx5LCBubyBjb2xsYXBzaW5nXG5cdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0Y2xlYXJUaW1lb3V0KCB0aGlzLmFjdGl2YXRpbmcgKTtcblx0XHRcdHRoaXMuX2FjdGl2YXRlKCBzZWxlY3RlZEluZGV4ICk7XG5cdFx0XHRyZXR1cm47XG5cdFx0Y2FzZSAkLnVpLmtleUNvZGUuRU5URVI6XG5cblx0XHRcdC8vIFRvZ2dsZSAoY2FuY2VsIGRlbGF5ZWQgYWN0aXZhdGlvbiwgYWxsb3cgY29sbGFwc2luZylcblx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRjbGVhclRpbWVvdXQoIHRoaXMuYWN0aXZhdGluZyApO1xuXG5cdFx0XHQvLyBEZXRlcm1pbmUgaWYgd2Ugc2hvdWxkIGNvbGxhcHNlIG9yIGFjdGl2YXRlXG5cdFx0XHR0aGlzLl9hY3RpdmF0ZSggc2VsZWN0ZWRJbmRleCA9PT0gdGhpcy5vcHRpb25zLmFjdGl2ZSA/IGZhbHNlIDogc2VsZWN0ZWRJbmRleCApO1xuXHRcdFx0cmV0dXJuO1xuXHRcdGRlZmF1bHQ6XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gRm9jdXMgdGhlIGFwcHJvcHJpYXRlIHRhYiwgYmFzZWQgb24gd2hpY2gga2V5IHdhcyBwcmVzc2VkXG5cdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRjbGVhclRpbWVvdXQoIHRoaXMuYWN0aXZhdGluZyApO1xuXHRcdHNlbGVjdGVkSW5kZXggPSB0aGlzLl9mb2N1c05leHRUYWIoIHNlbGVjdGVkSW5kZXgsIGdvaW5nRm9yd2FyZCApO1xuXG5cdFx0Ly8gTmF2aWdhdGluZyB3aXRoIGNvbnRyb2wvY29tbWFuZCBrZXkgd2lsbCBwcmV2ZW50IGF1dG9tYXRpYyBhY3RpdmF0aW9uXG5cdFx0aWYgKCAhZXZlbnQuY3RybEtleSAmJiAhZXZlbnQubWV0YUtleSApIHtcblxuXHRcdFx0Ly8gVXBkYXRlIGFyaWEtc2VsZWN0ZWQgaW1tZWRpYXRlbHkgc28gdGhhdCBBVCB0aGluayB0aGUgdGFiIGlzIGFscmVhZHkgc2VsZWN0ZWQuXG5cdFx0XHQvLyBPdGhlcndpc2UgQVQgbWF5IGNvbmZ1c2UgdGhlIHVzZXIgYnkgc3RhdGluZyB0aGF0IHRoZXkgbmVlZCB0byBhY3RpdmF0ZSB0aGUgdGFiLFxuXHRcdFx0Ly8gYnV0IHRoZSB0YWIgd2lsbCBhbHJlYWR5IGJlIGFjdGl2YXRlZCBieSB0aGUgdGltZSB0aGUgYW5ub3VuY2VtZW50IGZpbmlzaGVzLlxuXHRcdFx0Zm9jdXNlZFRhYi5hdHRyKCBcImFyaWEtc2VsZWN0ZWRcIiwgXCJmYWxzZVwiICk7XG5cdFx0XHR0aGlzLnRhYnMuZXEoIHNlbGVjdGVkSW5kZXggKS5hdHRyKCBcImFyaWEtc2VsZWN0ZWRcIiwgXCJ0cnVlXCIgKTtcblxuXHRcdFx0dGhpcy5hY3RpdmF0aW5nID0gdGhpcy5fZGVsYXkoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHR0aGlzLm9wdGlvbiggXCJhY3RpdmVcIiwgc2VsZWN0ZWRJbmRleCApO1xuXHRcdFx0fSwgdGhpcy5kZWxheSApO1xuXHRcdH1cblx0fSxcblxuXHRfcGFuZWxLZXlkb3duOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0aWYgKCB0aGlzLl9oYW5kbGVQYWdlTmF2KCBldmVudCApICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIEN0cmwrdXAgbW92ZXMgZm9jdXMgdG8gdGhlIGN1cnJlbnQgdGFiXG5cdFx0aWYgKCBldmVudC5jdHJsS2V5ICYmIGV2ZW50LmtleUNvZGUgPT09ICQudWkua2V5Q29kZS5VUCApIHtcblx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHR0aGlzLmFjdGl2ZS50cmlnZ2VyKCBcImZvY3VzXCIgKTtcblx0XHR9XG5cdH0sXG5cblx0Ly8gQWx0K3BhZ2UgdXAvZG93biBtb3ZlcyBmb2N1cyB0byB0aGUgcHJldmlvdXMvbmV4dCB0YWIgKGFuZCBhY3RpdmF0ZXMpXG5cdF9oYW5kbGVQYWdlTmF2OiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0aWYgKCBldmVudC5hbHRLZXkgJiYgZXZlbnQua2V5Q29kZSA9PT0gJC51aS5rZXlDb2RlLlBBR0VfVVAgKSB7XG5cdFx0XHR0aGlzLl9hY3RpdmF0ZSggdGhpcy5fZm9jdXNOZXh0VGFiKCB0aGlzLm9wdGlvbnMuYWN0aXZlIC0gMSwgZmFsc2UgKSApO1xuXHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0fVxuXHRcdGlmICggZXZlbnQuYWx0S2V5ICYmIGV2ZW50LmtleUNvZGUgPT09ICQudWkua2V5Q29kZS5QQUdFX0RPV04gKSB7XG5cdFx0XHR0aGlzLl9hY3RpdmF0ZSggdGhpcy5fZm9jdXNOZXh0VGFiKCB0aGlzLm9wdGlvbnMuYWN0aXZlICsgMSwgdHJ1ZSApICk7XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHR9XG5cdH0sXG5cblx0X2ZpbmROZXh0VGFiOiBmdW5jdGlvbiggaW5kZXgsIGdvaW5nRm9yd2FyZCApIHtcblx0XHR2YXIgbGFzdFRhYkluZGV4ID0gdGhpcy50YWJzLmxlbmd0aCAtIDE7XG5cblx0XHRmdW5jdGlvbiBjb25zdHJhaW4oKSB7XG5cdFx0XHRpZiAoIGluZGV4ID4gbGFzdFRhYkluZGV4ICkge1xuXHRcdFx0XHRpbmRleCA9IDA7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIGluZGV4IDwgMCApIHtcblx0XHRcdFx0aW5kZXggPSBsYXN0VGFiSW5kZXg7XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gaW5kZXg7XG5cdFx0fVxuXG5cdFx0d2hpbGUgKCAkLmluQXJyYXkoIGNvbnN0cmFpbigpLCB0aGlzLm9wdGlvbnMuZGlzYWJsZWQgKSAhPT0gLTEgKSB7XG5cdFx0XHRpbmRleCA9IGdvaW5nRm9yd2FyZCA/IGluZGV4ICsgMSA6IGluZGV4IC0gMTtcblx0XHR9XG5cblx0XHRyZXR1cm4gaW5kZXg7XG5cdH0sXG5cblx0X2ZvY3VzTmV4dFRhYjogZnVuY3Rpb24oIGluZGV4LCBnb2luZ0ZvcndhcmQgKSB7XG5cdFx0aW5kZXggPSB0aGlzLl9maW5kTmV4dFRhYiggaW5kZXgsIGdvaW5nRm9yd2FyZCApO1xuXHRcdHRoaXMudGFicy5lcSggaW5kZXggKS50cmlnZ2VyKCBcImZvY3VzXCIgKTtcblx0XHRyZXR1cm4gaW5kZXg7XG5cdH0sXG5cblx0X3NldE9wdGlvbjogZnVuY3Rpb24oIGtleSwgdmFsdWUgKSB7XG5cdFx0aWYgKCBrZXkgPT09IFwiYWN0aXZlXCIgKSB7XG5cblx0XHRcdC8vIF9hY3RpdmF0ZSgpIHdpbGwgaGFuZGxlIGludmFsaWQgdmFsdWVzIGFuZCB1cGRhdGUgdGhpcy5vcHRpb25zXG5cdFx0XHR0aGlzLl9hY3RpdmF0ZSggdmFsdWUgKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHR0aGlzLl9zdXBlcigga2V5LCB2YWx1ZSApO1xuXG5cdFx0aWYgKCBrZXkgPT09IFwiY29sbGFwc2libGVcIiApIHtcblx0XHRcdHRoaXMuX3RvZ2dsZUNsYXNzKCBcInVpLXRhYnMtY29sbGFwc2libGVcIiwgbnVsbCwgdmFsdWUgKTtcblxuXHRcdFx0Ly8gU2V0dGluZyBjb2xsYXBzaWJsZTogZmFsc2Ugd2hpbGUgY29sbGFwc2VkOyBvcGVuIGZpcnN0IHBhbmVsXG5cdFx0XHRpZiAoICF2YWx1ZSAmJiB0aGlzLm9wdGlvbnMuYWN0aXZlID09PSBmYWxzZSApIHtcblx0XHRcdFx0dGhpcy5fYWN0aXZhdGUoIDAgKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoIGtleSA9PT0gXCJldmVudFwiICkge1xuXHRcdFx0dGhpcy5fc2V0dXBFdmVudHMoIHZhbHVlICk7XG5cdFx0fVxuXG5cdFx0aWYgKCBrZXkgPT09IFwiaGVpZ2h0U3R5bGVcIiApIHtcblx0XHRcdHRoaXMuX3NldHVwSGVpZ2h0U3R5bGUoIHZhbHVlICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9zYW5pdGl6ZVNlbGVjdG9yOiBmdW5jdGlvbiggaGFzaCApIHtcblx0XHRyZXR1cm4gaGFzaCA/IGhhc2gucmVwbGFjZSggL1shXCIkJSYnKCkqKywuXFwvOjs8PT4/QFxcW1xcXVxcXmB7fH1+XS9nLCBcIlxcXFwkJlwiICkgOiBcIlwiO1xuXHR9LFxuXG5cdHJlZnJlc2g6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBvcHRpb25zID0gdGhpcy5vcHRpb25zLFxuXHRcdFx0bGlzID0gdGhpcy50YWJsaXN0LmNoaWxkcmVuKCBcIjpoYXMoYVtocmVmXSlcIiApO1xuXG5cdFx0Ly8gR2V0IGRpc2FibGVkIHRhYnMgZnJvbSBjbGFzcyBhdHRyaWJ1dGUgZnJvbSBIVE1MXG5cdFx0Ly8gdGhpcyB3aWxsIGdldCBjb252ZXJ0ZWQgdG8gYSBib29sZWFuIGlmIG5lZWRlZCBpbiBfcmVmcmVzaCgpXG5cdFx0b3B0aW9ucy5kaXNhYmxlZCA9ICQubWFwKCBsaXMuZmlsdGVyKCBcIi51aS1zdGF0ZS1kaXNhYmxlZFwiICksIGZ1bmN0aW9uKCB0YWIgKSB7XG5cdFx0XHRyZXR1cm4gbGlzLmluZGV4KCB0YWIgKTtcblx0XHR9ICk7XG5cblx0XHR0aGlzLl9wcm9jZXNzVGFicygpO1xuXG5cdFx0Ly8gV2FzIGNvbGxhcHNlZCBvciBubyB0YWJzXG5cdFx0aWYgKCBvcHRpb25zLmFjdGl2ZSA9PT0gZmFsc2UgfHwgIXRoaXMuYW5jaG9ycy5sZW5ndGggKSB7XG5cdFx0XHRvcHRpb25zLmFjdGl2ZSA9IGZhbHNlO1xuXHRcdFx0dGhpcy5hY3RpdmUgPSAkKCk7XG5cblx0XHQvLyB3YXMgYWN0aXZlLCBidXQgYWN0aXZlIHRhYiBpcyBnb25lXG5cdFx0fSBlbHNlIGlmICggdGhpcy5hY3RpdmUubGVuZ3RoICYmICEkLmNvbnRhaW5zKCB0aGlzLnRhYmxpc3RbIDAgXSwgdGhpcy5hY3RpdmVbIDAgXSApICkge1xuXG5cdFx0XHQvLyBhbGwgcmVtYWluaW5nIHRhYnMgYXJlIGRpc2FibGVkXG5cdFx0XHRpZiAoIHRoaXMudGFicy5sZW5ndGggPT09IG9wdGlvbnMuZGlzYWJsZWQubGVuZ3RoICkge1xuXHRcdFx0XHRvcHRpb25zLmFjdGl2ZSA9IGZhbHNlO1xuXHRcdFx0XHR0aGlzLmFjdGl2ZSA9ICQoKTtcblxuXHRcdFx0Ly8gYWN0aXZhdGUgcHJldmlvdXMgdGFiXG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0aGlzLl9hY3RpdmF0ZSggdGhpcy5fZmluZE5leHRUYWIoIE1hdGgubWF4KCAwLCBvcHRpb25zLmFjdGl2ZSAtIDEgKSwgZmFsc2UgKSApO1xuXHRcdFx0fVxuXG5cdFx0Ly8gd2FzIGFjdGl2ZSwgYWN0aXZlIHRhYiBzdGlsbCBleGlzdHNcblx0XHR9IGVsc2Uge1xuXG5cdFx0XHQvLyBtYWtlIHN1cmUgYWN0aXZlIGluZGV4IGlzIGNvcnJlY3Rcblx0XHRcdG9wdGlvbnMuYWN0aXZlID0gdGhpcy50YWJzLmluZGV4KCB0aGlzLmFjdGl2ZSApO1xuXHRcdH1cblxuXHRcdHRoaXMuX3JlZnJlc2goKTtcblx0fSxcblxuXHRfcmVmcmVzaDogZnVuY3Rpb24oKSB7XG5cdFx0dGhpcy5fc2V0T3B0aW9uRGlzYWJsZWQoIHRoaXMub3B0aW9ucy5kaXNhYmxlZCApO1xuXHRcdHRoaXMuX3NldHVwRXZlbnRzKCB0aGlzLm9wdGlvbnMuZXZlbnQgKTtcblx0XHR0aGlzLl9zZXR1cEhlaWdodFN0eWxlKCB0aGlzLm9wdGlvbnMuaGVpZ2h0U3R5bGUgKTtcblxuXHRcdHRoaXMudGFicy5ub3QoIHRoaXMuYWN0aXZlICkuYXR0cigge1xuXHRcdFx0XCJhcmlhLXNlbGVjdGVkXCI6IFwiZmFsc2VcIixcblx0XHRcdFwiYXJpYS1leHBhbmRlZFwiOiBcImZhbHNlXCIsXG5cdFx0XHR0YWJJbmRleDogLTFcblx0XHR9ICk7XG5cdFx0dGhpcy5wYW5lbHMubm90KCB0aGlzLl9nZXRQYW5lbEZvclRhYiggdGhpcy5hY3RpdmUgKSApXG5cdFx0XHQuaGlkZSgpXG5cdFx0XHQuYXR0cigge1xuXHRcdFx0XHRcImFyaWEtaGlkZGVuXCI6IFwidHJ1ZVwiXG5cdFx0XHR9ICk7XG5cblx0XHQvLyBNYWtlIHN1cmUgb25lIHRhYiBpcyBpbiB0aGUgdGFiIG9yZGVyXG5cdFx0aWYgKCAhdGhpcy5hY3RpdmUubGVuZ3RoICkge1xuXHRcdFx0dGhpcy50YWJzLmVxKCAwICkuYXR0ciggXCJ0YWJJbmRleFwiLCAwICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuYWN0aXZlXG5cdFx0XHRcdC5hdHRyKCB7XG5cdFx0XHRcdFx0XCJhcmlhLXNlbGVjdGVkXCI6IFwidHJ1ZVwiLFxuXHRcdFx0XHRcdFwiYXJpYS1leHBhbmRlZFwiOiBcInRydWVcIixcblx0XHRcdFx0XHR0YWJJbmRleDogMFxuXHRcdFx0XHR9ICk7XG5cdFx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5hY3RpdmUsIFwidWktdGFicy1hY3RpdmVcIiwgXCJ1aS1zdGF0ZS1hY3RpdmVcIiApO1xuXHRcdFx0dGhpcy5fZ2V0UGFuZWxGb3JUYWIoIHRoaXMuYWN0aXZlIClcblx0XHRcdFx0LnNob3coKVxuXHRcdFx0XHQuYXR0cigge1xuXHRcdFx0XHRcdFwiYXJpYS1oaWRkZW5cIjogXCJmYWxzZVwiXG5cdFx0XHRcdH0gKTtcblx0XHR9XG5cdH0sXG5cblx0X3Byb2Nlc3NUYWJzOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgdGhhdCA9IHRoaXMsXG5cdFx0XHRwcmV2VGFicyA9IHRoaXMudGFicyxcblx0XHRcdHByZXZBbmNob3JzID0gdGhpcy5hbmNob3JzLFxuXHRcdFx0cHJldlBhbmVscyA9IHRoaXMucGFuZWxzO1xuXG5cdFx0dGhpcy50YWJsaXN0ID0gdGhpcy5fZ2V0TGlzdCgpLmF0dHIoIFwicm9sZVwiLCBcInRhYmxpc3RcIiApO1xuXHRcdHRoaXMuX2FkZENsYXNzKCB0aGlzLnRhYmxpc3QsIFwidWktdGFicy1uYXZcIixcblx0XHRcdFwidWktaGVscGVyLXJlc2V0IHVpLWhlbHBlci1jbGVhcmZpeCB1aS13aWRnZXQtaGVhZGVyXCIgKTtcblxuXHRcdC8vIFByZXZlbnQgdXNlcnMgZnJvbSBmb2N1c2luZyBkaXNhYmxlZCB0YWJzIHZpYSBjbGlja1xuXHRcdHRoaXMudGFibGlzdFxuXHRcdFx0Lm9uKCBcIm1vdXNlZG93blwiICsgdGhpcy5ldmVudE5hbWVzcGFjZSwgXCI+IGxpXCIsIGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0aWYgKCAkKCB0aGlzICkuaXMoIFwiLnVpLXN0YXRlLWRpc2FibGVkXCIgKSApIHtcblx0XHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0XHR9XG5cdFx0XHR9IClcblxuXHRcdFx0Ly8gU3VwcG9ydDogSUUgPDlcblx0XHRcdC8vIFByZXZlbnRpbmcgdGhlIGRlZmF1bHQgYWN0aW9uIGluIG1vdXNlZG93biBkb2Vzbid0IHByZXZlbnQgSUVcblx0XHRcdC8vIGZyb20gZm9jdXNpbmcgdGhlIGVsZW1lbnQsIHNvIGlmIHRoZSBhbmNob3IgZ2V0cyBmb2N1c2VkLCBibHVyLlxuXHRcdFx0Ly8gV2UgZG9uJ3QgaGF2ZSB0byB3b3JyeSBhYm91dCBmb2N1c2luZyB0aGUgcHJldmlvdXNseSBmb2N1c2VkXG5cdFx0XHQvLyBlbGVtZW50IHNpbmNlIGNsaWNraW5nIG9uIGEgbm9uLWZvY3VzYWJsZSBlbGVtZW50IHNob3VsZCBmb2N1c1xuXHRcdFx0Ly8gdGhlIGJvZHkgYW55d2F5LlxuXHRcdFx0Lm9uKCBcImZvY3VzXCIgKyB0aGlzLmV2ZW50TmFtZXNwYWNlLCBcIi51aS10YWJzLWFuY2hvclwiLCBmdW5jdGlvbigpIHtcblx0XHRcdFx0aWYgKCAkKCB0aGlzICkuY2xvc2VzdCggXCJsaVwiICkuaXMoIFwiLnVpLXN0YXRlLWRpc2FibGVkXCIgKSApIHtcblx0XHRcdFx0XHR0aGlzLmJsdXIoKTtcblx0XHRcdFx0fVxuXHRcdFx0fSApO1xuXG5cdFx0dGhpcy50YWJzID0gdGhpcy50YWJsaXN0LmZpbmQoIFwiPiBsaTpoYXMoYVtocmVmXSlcIiApXG5cdFx0XHQuYXR0cigge1xuXHRcdFx0XHRyb2xlOiBcInRhYlwiLFxuXHRcdFx0XHR0YWJJbmRleDogLTFcblx0XHRcdH0gKTtcblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy50YWJzLCBcInVpLXRhYnMtdGFiXCIsIFwidWktc3RhdGUtZGVmYXVsdFwiICk7XG5cblx0XHR0aGlzLmFuY2hvcnMgPSB0aGlzLnRhYnMubWFwKCBmdW5jdGlvbigpIHtcblx0XHRcdHJldHVybiAkKCBcImFcIiwgdGhpcyApWyAwIF07XG5cdFx0fSApXG5cdFx0XHQuYXR0cigge1xuXHRcdFx0XHRyb2xlOiBcInByZXNlbnRhdGlvblwiLFxuXHRcdFx0XHR0YWJJbmRleDogLTFcblx0XHRcdH0gKTtcblx0XHR0aGlzLl9hZGRDbGFzcyggdGhpcy5hbmNob3JzLCBcInVpLXRhYnMtYW5jaG9yXCIgKTtcblxuXHRcdHRoaXMucGFuZWxzID0gJCgpO1xuXG5cdFx0dGhpcy5hbmNob3JzLmVhY2goIGZ1bmN0aW9uKCBpLCBhbmNob3IgKSB7XG5cdFx0XHR2YXIgc2VsZWN0b3IsIHBhbmVsLCBwYW5lbElkLFxuXHRcdFx0XHRhbmNob3JJZCA9ICQoIGFuY2hvciApLnVuaXF1ZUlkKCkuYXR0ciggXCJpZFwiICksXG5cdFx0XHRcdHRhYiA9ICQoIGFuY2hvciApLmNsb3Nlc3QoIFwibGlcIiApLFxuXHRcdFx0XHRvcmlnaW5hbEFyaWFDb250cm9scyA9IHRhYi5hdHRyKCBcImFyaWEtY29udHJvbHNcIiApO1xuXG5cdFx0XHQvLyBJbmxpbmUgdGFiXG5cdFx0XHRpZiAoIHRoYXQuX2lzTG9jYWwoIGFuY2hvciApICkge1xuXHRcdFx0XHRzZWxlY3RvciA9IGFuY2hvci5oYXNoO1xuXHRcdFx0XHRwYW5lbElkID0gc2VsZWN0b3Iuc3Vic3RyaW5nKCAxICk7XG5cdFx0XHRcdHBhbmVsID0gdGhhdC5lbGVtZW50LmZpbmQoIHRoYXQuX3Nhbml0aXplU2VsZWN0b3IoIHNlbGVjdG9yICkgKTtcblxuXHRcdFx0Ly8gcmVtb3RlIHRhYlxuXHRcdFx0fSBlbHNlIHtcblxuXHRcdFx0XHQvLyBJZiB0aGUgdGFiIGRvZXNuJ3QgYWxyZWFkeSBoYXZlIGFyaWEtY29udHJvbHMsXG5cdFx0XHRcdC8vIGdlbmVyYXRlIGFuIGlkIGJ5IHVzaW5nIGEgdGhyb3ctYXdheSBlbGVtZW50XG5cdFx0XHRcdHBhbmVsSWQgPSB0YWIuYXR0ciggXCJhcmlhLWNvbnRyb2xzXCIgKSB8fCAkKCB7fSApLnVuaXF1ZUlkKClbIDAgXS5pZDtcblx0XHRcdFx0c2VsZWN0b3IgPSBcIiNcIiArIHBhbmVsSWQ7XG5cdFx0XHRcdHBhbmVsID0gdGhhdC5lbGVtZW50LmZpbmQoIHNlbGVjdG9yICk7XG5cdFx0XHRcdGlmICggIXBhbmVsLmxlbmd0aCApIHtcblx0XHRcdFx0XHRwYW5lbCA9IHRoYXQuX2NyZWF0ZVBhbmVsKCBwYW5lbElkICk7XG5cdFx0XHRcdFx0cGFuZWwuaW5zZXJ0QWZ0ZXIoIHRoYXQucGFuZWxzWyBpIC0gMSBdIHx8IHRoYXQudGFibGlzdCApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHBhbmVsLmF0dHIoIFwiYXJpYS1saXZlXCIsIFwicG9saXRlXCIgKTtcblx0XHRcdH1cblxuXHRcdFx0aWYgKCBwYW5lbC5sZW5ndGggKSB7XG5cdFx0XHRcdHRoYXQucGFuZWxzID0gdGhhdC5wYW5lbHMuYWRkKCBwYW5lbCApO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCBvcmlnaW5hbEFyaWFDb250cm9scyApIHtcblx0XHRcdFx0dGFiLmRhdGEoIFwidWktdGFicy1hcmlhLWNvbnRyb2xzXCIsIG9yaWdpbmFsQXJpYUNvbnRyb2xzICk7XG5cdFx0XHR9XG5cdFx0XHR0YWIuYXR0cigge1xuXHRcdFx0XHRcImFyaWEtY29udHJvbHNcIjogcGFuZWxJZCxcblx0XHRcdFx0XCJhcmlhLWxhYmVsbGVkYnlcIjogYW5jaG9ySWRcblx0XHRcdH0gKTtcblx0XHRcdHBhbmVsLmF0dHIoIFwiYXJpYS1sYWJlbGxlZGJ5XCIsIGFuY2hvcklkICk7XG5cdFx0fSApO1xuXG5cdFx0dGhpcy5wYW5lbHMuYXR0ciggXCJyb2xlXCIsIFwidGFicGFuZWxcIiApO1xuXHRcdHRoaXMuX2FkZENsYXNzKCB0aGlzLnBhbmVscywgXCJ1aS10YWJzLXBhbmVsXCIsIFwidWktd2lkZ2V0LWNvbnRlbnRcIiApO1xuXG5cdFx0Ly8gQXZvaWQgbWVtb3J5IGxlYWtzICgjMTAwNTYpXG5cdFx0aWYgKCBwcmV2VGFicyApIHtcblx0XHRcdHRoaXMuX29mZiggcHJldlRhYnMubm90KCB0aGlzLnRhYnMgKSApO1xuXHRcdFx0dGhpcy5fb2ZmKCBwcmV2QW5jaG9ycy5ub3QoIHRoaXMuYW5jaG9ycyApICk7XG5cdFx0XHR0aGlzLl9vZmYoIHByZXZQYW5lbHMubm90KCB0aGlzLnBhbmVscyApICk7XG5cdFx0fVxuXHR9LFxuXG5cdC8vIEFsbG93IG92ZXJyaWRpbmcgaG93IHRvIGZpbmQgdGhlIGxpc3QgZm9yIHJhcmUgdXNhZ2Ugc2NlbmFyaW9zICgjNzcxNSlcblx0X2dldExpc3Q6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLnRhYmxpc3QgfHwgdGhpcy5lbGVtZW50LmZpbmQoIFwib2wsIHVsXCIgKS5lcSggMCApO1xuXHR9LFxuXG5cdF9jcmVhdGVQYW5lbDogZnVuY3Rpb24oIGlkICkge1xuXHRcdHJldHVybiAkKCBcIjxkaXY+XCIgKVxuXHRcdFx0LmF0dHIoIFwiaWRcIiwgaWQgKVxuXHRcdFx0LmRhdGEoIFwidWktdGFicy1kZXN0cm95XCIsIHRydWUgKTtcblx0fSxcblxuXHRfc2V0T3B0aW9uRGlzYWJsZWQ6IGZ1bmN0aW9uKCBkaXNhYmxlZCApIHtcblx0XHR2YXIgY3VycmVudEl0ZW0sIGxpLCBpO1xuXG5cdFx0aWYgKCAkLmlzQXJyYXkoIGRpc2FibGVkICkgKSB7XG5cdFx0XHRpZiAoICFkaXNhYmxlZC5sZW5ndGggKSB7XG5cdFx0XHRcdGRpc2FibGVkID0gZmFsc2U7XG5cdFx0XHR9IGVsc2UgaWYgKCBkaXNhYmxlZC5sZW5ndGggPT09IHRoaXMuYW5jaG9ycy5sZW5ndGggKSB7XG5cdFx0XHRcdGRpc2FibGVkID0gdHJ1ZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBEaXNhYmxlIHRhYnNcblx0XHRmb3IgKCBpID0gMDsgKCBsaSA9IHRoaXMudGFic1sgaSBdICk7IGkrKyApIHtcblx0XHRcdGN1cnJlbnRJdGVtID0gJCggbGkgKTtcblx0XHRcdGlmICggZGlzYWJsZWQgPT09IHRydWUgfHwgJC5pbkFycmF5KCBpLCBkaXNhYmxlZCApICE9PSAtMSApIHtcblx0XHRcdFx0Y3VycmVudEl0ZW0uYXR0ciggXCJhcmlhLWRpc2FibGVkXCIsIFwidHJ1ZVwiICk7XG5cdFx0XHRcdHRoaXMuX2FkZENsYXNzKCBjdXJyZW50SXRlbSwgbnVsbCwgXCJ1aS1zdGF0ZS1kaXNhYmxlZFwiICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRjdXJyZW50SXRlbS5yZW1vdmVBdHRyKCBcImFyaWEtZGlzYWJsZWRcIiApO1xuXHRcdFx0XHR0aGlzLl9yZW1vdmVDbGFzcyggY3VycmVudEl0ZW0sIG51bGwsIFwidWktc3RhdGUtZGlzYWJsZWRcIiApO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMub3B0aW9ucy5kaXNhYmxlZCA9IGRpc2FibGVkO1xuXG5cdFx0dGhpcy5fdG9nZ2xlQ2xhc3MoIHRoaXMud2lkZ2V0KCksIHRoaXMud2lkZ2V0RnVsbE5hbWUgKyBcIi1kaXNhYmxlZFwiLCBudWxsLFxuXHRcdFx0ZGlzYWJsZWQgPT09IHRydWUgKTtcblx0fSxcblxuXHRfc2V0dXBFdmVudHM6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgZXZlbnRzID0ge307XG5cdFx0aWYgKCBldmVudCApIHtcblx0XHRcdCQuZWFjaCggZXZlbnQuc3BsaXQoIFwiIFwiICksIGZ1bmN0aW9uKCBpbmRleCwgZXZlbnROYW1lICkge1xuXHRcdFx0XHRldmVudHNbIGV2ZW50TmFtZSBdID0gXCJfZXZlbnRIYW5kbGVyXCI7XG5cdFx0XHR9ICk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fb2ZmKCB0aGlzLmFuY2hvcnMuYWRkKCB0aGlzLnRhYnMgKS5hZGQoIHRoaXMucGFuZWxzICkgKTtcblxuXHRcdC8vIEFsd2F5cyBwcmV2ZW50IHRoZSBkZWZhdWx0IGFjdGlvbiwgZXZlbiB3aGVuIGRpc2FibGVkXG5cdFx0dGhpcy5fb24oIHRydWUsIHRoaXMuYW5jaG9ycywge1xuXHRcdFx0Y2xpY2s6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdFx0dGhpcy5fb24oIHRoaXMuYW5jaG9ycywgZXZlbnRzICk7XG5cdFx0dGhpcy5fb24oIHRoaXMudGFicywgeyBrZXlkb3duOiBcIl90YWJLZXlkb3duXCIgfSApO1xuXHRcdHRoaXMuX29uKCB0aGlzLnBhbmVscywgeyBrZXlkb3duOiBcIl9wYW5lbEtleWRvd25cIiB9ICk7XG5cblx0XHR0aGlzLl9mb2N1c2FibGUoIHRoaXMudGFicyApO1xuXHRcdHRoaXMuX2hvdmVyYWJsZSggdGhpcy50YWJzICk7XG5cdH0sXG5cblx0X3NldHVwSGVpZ2h0U3R5bGU6IGZ1bmN0aW9uKCBoZWlnaHRTdHlsZSApIHtcblx0XHR2YXIgbWF4SGVpZ2h0LFxuXHRcdFx0cGFyZW50ID0gdGhpcy5lbGVtZW50LnBhcmVudCgpO1xuXG5cdFx0aWYgKCBoZWlnaHRTdHlsZSA9PT0gXCJmaWxsXCIgKSB7XG5cdFx0XHRtYXhIZWlnaHQgPSBwYXJlbnQuaGVpZ2h0KCk7XG5cdFx0XHRtYXhIZWlnaHQgLT0gdGhpcy5lbGVtZW50Lm91dGVySGVpZ2h0KCkgLSB0aGlzLmVsZW1lbnQuaGVpZ2h0KCk7XG5cblx0XHRcdHRoaXMuZWxlbWVudC5zaWJsaW5ncyggXCI6dmlzaWJsZVwiICkuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHZhciBlbGVtID0gJCggdGhpcyApLFxuXHRcdFx0XHRcdHBvc2l0aW9uID0gZWxlbS5jc3MoIFwicG9zaXRpb25cIiApO1xuXG5cdFx0XHRcdGlmICggcG9zaXRpb24gPT09IFwiYWJzb2x1dGVcIiB8fCBwb3NpdGlvbiA9PT0gXCJmaXhlZFwiICkge1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXHRcdFx0XHRtYXhIZWlnaHQgLT0gZWxlbS5vdXRlckhlaWdodCggdHJ1ZSApO1xuXHRcdFx0fSApO1xuXG5cdFx0XHR0aGlzLmVsZW1lbnQuY2hpbGRyZW4oKS5ub3QoIHRoaXMucGFuZWxzICkuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdG1heEhlaWdodCAtPSAkKCB0aGlzICkub3V0ZXJIZWlnaHQoIHRydWUgKTtcblx0XHRcdH0gKTtcblxuXHRcdFx0dGhpcy5wYW5lbHMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdCQoIHRoaXMgKS5oZWlnaHQoIE1hdGgubWF4KCAwLCBtYXhIZWlnaHQgLVxuXHRcdFx0XHRcdCQoIHRoaXMgKS5pbm5lckhlaWdodCgpICsgJCggdGhpcyApLmhlaWdodCgpICkgKTtcblx0XHRcdH0gKVxuXHRcdFx0XHQuY3NzKCBcIm92ZXJmbG93XCIsIFwiYXV0b1wiICk7XG5cdFx0fSBlbHNlIGlmICggaGVpZ2h0U3R5bGUgPT09IFwiYXV0b1wiICkge1xuXHRcdFx0bWF4SGVpZ2h0ID0gMDtcblx0XHRcdHRoaXMucGFuZWxzLmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRtYXhIZWlnaHQgPSBNYXRoLm1heCggbWF4SGVpZ2h0LCAkKCB0aGlzICkuaGVpZ2h0KCBcIlwiICkuaGVpZ2h0KCkgKTtcblx0XHRcdH0gKS5oZWlnaHQoIG1heEhlaWdodCApO1xuXHRcdH1cblx0fSxcblxuXHRfZXZlbnRIYW5kbGVyOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0dmFyIG9wdGlvbnMgPSB0aGlzLm9wdGlvbnMsXG5cdFx0XHRhY3RpdmUgPSB0aGlzLmFjdGl2ZSxcblx0XHRcdGFuY2hvciA9ICQoIGV2ZW50LmN1cnJlbnRUYXJnZXQgKSxcblx0XHRcdHRhYiA9IGFuY2hvci5jbG9zZXN0KCBcImxpXCIgKSxcblx0XHRcdGNsaWNrZWRJc0FjdGl2ZSA9IHRhYlsgMCBdID09PSBhY3RpdmVbIDAgXSxcblx0XHRcdGNvbGxhcHNpbmcgPSBjbGlja2VkSXNBY3RpdmUgJiYgb3B0aW9ucy5jb2xsYXBzaWJsZSxcblx0XHRcdHRvU2hvdyA9IGNvbGxhcHNpbmcgPyAkKCkgOiB0aGlzLl9nZXRQYW5lbEZvclRhYiggdGFiICksXG5cdFx0XHR0b0hpZGUgPSAhYWN0aXZlLmxlbmd0aCA/ICQoKSA6IHRoaXMuX2dldFBhbmVsRm9yVGFiKCBhY3RpdmUgKSxcblx0XHRcdGV2ZW50RGF0YSA9IHtcblx0XHRcdFx0b2xkVGFiOiBhY3RpdmUsXG5cdFx0XHRcdG9sZFBhbmVsOiB0b0hpZGUsXG5cdFx0XHRcdG5ld1RhYjogY29sbGFwc2luZyA/ICQoKSA6IHRhYixcblx0XHRcdFx0bmV3UGFuZWw6IHRvU2hvd1xuXHRcdFx0fTtcblxuXHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cblx0XHRpZiAoIHRhYi5oYXNDbGFzcyggXCJ1aS1zdGF0ZS1kaXNhYmxlZFwiICkgfHxcblxuXHRcdFx0XHQvLyB0YWIgaXMgYWxyZWFkeSBsb2FkaW5nXG5cdFx0XHRcdHRhYi5oYXNDbGFzcyggXCJ1aS10YWJzLWxvYWRpbmdcIiApIHx8XG5cblx0XHRcdFx0Ly8gY2FuJ3Qgc3dpdGNoIGR1cm5pbmcgYW4gYW5pbWF0aW9uXG5cdFx0XHRcdHRoaXMucnVubmluZyB8fFxuXG5cdFx0XHRcdC8vIGNsaWNrIG9uIGFjdGl2ZSBoZWFkZXIsIGJ1dCBub3QgY29sbGFwc2libGVcblx0XHRcdFx0KCBjbGlja2VkSXNBY3RpdmUgJiYgIW9wdGlvbnMuY29sbGFwc2libGUgKSB8fFxuXG5cdFx0XHRcdC8vIGFsbG93IGNhbmNlbGluZyBhY3RpdmF0aW9uXG5cdFx0XHRcdCggdGhpcy5fdHJpZ2dlciggXCJiZWZvcmVBY3RpdmF0ZVwiLCBldmVudCwgZXZlbnREYXRhICkgPT09IGZhbHNlICkgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0b3B0aW9ucy5hY3RpdmUgPSBjb2xsYXBzaW5nID8gZmFsc2UgOiB0aGlzLnRhYnMuaW5kZXgoIHRhYiApO1xuXG5cdFx0dGhpcy5hY3RpdmUgPSBjbGlja2VkSXNBY3RpdmUgPyAkKCkgOiB0YWI7XG5cdFx0aWYgKCB0aGlzLnhociApIHtcblx0XHRcdHRoaXMueGhyLmFib3J0KCk7XG5cdFx0fVxuXG5cdFx0aWYgKCAhdG9IaWRlLmxlbmd0aCAmJiAhdG9TaG93Lmxlbmd0aCApIHtcblx0XHRcdCQuZXJyb3IoIFwialF1ZXJ5IFVJIFRhYnM6IE1pc21hdGNoaW5nIGZyYWdtZW50IGlkZW50aWZpZXIuXCIgKTtcblx0XHR9XG5cblx0XHRpZiAoIHRvU2hvdy5sZW5ndGggKSB7XG5cdFx0XHR0aGlzLmxvYWQoIHRoaXMudGFicy5pbmRleCggdGFiICksIGV2ZW50ICk7XG5cdFx0fVxuXHRcdHRoaXMuX3RvZ2dsZSggZXZlbnQsIGV2ZW50RGF0YSApO1xuXHR9LFxuXG5cdC8vIEhhbmRsZXMgc2hvdy9oaWRlIGZvciBzZWxlY3RpbmcgdGFic1xuXHRfdG9nZ2xlOiBmdW5jdGlvbiggZXZlbnQsIGV2ZW50RGF0YSApIHtcblx0XHR2YXIgdGhhdCA9IHRoaXMsXG5cdFx0XHR0b1Nob3cgPSBldmVudERhdGEubmV3UGFuZWwsXG5cdFx0XHR0b0hpZGUgPSBldmVudERhdGEub2xkUGFuZWw7XG5cblx0XHR0aGlzLnJ1bm5pbmcgPSB0cnVlO1xuXG5cdFx0ZnVuY3Rpb24gY29tcGxldGUoKSB7XG5cdFx0XHR0aGF0LnJ1bm5pbmcgPSBmYWxzZTtcblx0XHRcdHRoYXQuX3RyaWdnZXIoIFwiYWN0aXZhdGVcIiwgZXZlbnQsIGV2ZW50RGF0YSApO1xuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHNob3coKSB7XG5cdFx0XHR0aGF0Ll9hZGRDbGFzcyggZXZlbnREYXRhLm5ld1RhYi5jbG9zZXN0KCBcImxpXCIgKSwgXCJ1aS10YWJzLWFjdGl2ZVwiLCBcInVpLXN0YXRlLWFjdGl2ZVwiICk7XG5cblx0XHRcdGlmICggdG9TaG93Lmxlbmd0aCAmJiB0aGF0Lm9wdGlvbnMuc2hvdyApIHtcblx0XHRcdFx0dGhhdC5fc2hvdyggdG9TaG93LCB0aGF0Lm9wdGlvbnMuc2hvdywgY29tcGxldGUgKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRvU2hvdy5zaG93KCk7XG5cdFx0XHRcdGNvbXBsZXRlKCk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gU3RhcnQgb3V0IGJ5IGhpZGluZywgdGhlbiBzaG93aW5nLCB0aGVuIGNvbXBsZXRpbmdcblx0XHRpZiAoIHRvSGlkZS5sZW5ndGggJiYgdGhpcy5vcHRpb25zLmhpZGUgKSB7XG5cdFx0XHR0aGlzLl9oaWRlKCB0b0hpZGUsIHRoaXMub3B0aW9ucy5oaWRlLCBmdW5jdGlvbigpIHtcblx0XHRcdFx0dGhhdC5fcmVtb3ZlQ2xhc3MoIGV2ZW50RGF0YS5vbGRUYWIuY2xvc2VzdCggXCJsaVwiICksXG5cdFx0XHRcdFx0XCJ1aS10YWJzLWFjdGl2ZVwiLCBcInVpLXN0YXRlLWFjdGl2ZVwiICk7XG5cdFx0XHRcdHNob3coKTtcblx0XHRcdH0gKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5fcmVtb3ZlQ2xhc3MoIGV2ZW50RGF0YS5vbGRUYWIuY2xvc2VzdCggXCJsaVwiICksXG5cdFx0XHRcdFwidWktdGFicy1hY3RpdmVcIiwgXCJ1aS1zdGF0ZS1hY3RpdmVcIiApO1xuXHRcdFx0dG9IaWRlLmhpZGUoKTtcblx0XHRcdHNob3coKTtcblx0XHR9XG5cblx0XHR0b0hpZGUuYXR0ciggXCJhcmlhLWhpZGRlblwiLCBcInRydWVcIiApO1xuXHRcdGV2ZW50RGF0YS5vbGRUYWIuYXR0cigge1xuXHRcdFx0XCJhcmlhLXNlbGVjdGVkXCI6IFwiZmFsc2VcIixcblx0XHRcdFwiYXJpYS1leHBhbmRlZFwiOiBcImZhbHNlXCJcblx0XHR9ICk7XG5cblx0XHQvLyBJZiB3ZSdyZSBzd2l0Y2hpbmcgdGFicywgcmVtb3ZlIHRoZSBvbGQgdGFiIGZyb20gdGhlIHRhYiBvcmRlci5cblx0XHQvLyBJZiB3ZSdyZSBvcGVuaW5nIGZyb20gY29sbGFwc2VkIHN0YXRlLCByZW1vdmUgdGhlIHByZXZpb3VzIHRhYiBmcm9tIHRoZSB0YWIgb3JkZXIuXG5cdFx0Ly8gSWYgd2UncmUgY29sbGFwc2luZywgdGhlbiBrZWVwIHRoZSBjb2xsYXBzaW5nIHRhYiBpbiB0aGUgdGFiIG9yZGVyLlxuXHRcdGlmICggdG9TaG93Lmxlbmd0aCAmJiB0b0hpZGUubGVuZ3RoICkge1xuXHRcdFx0ZXZlbnREYXRhLm9sZFRhYi5hdHRyKCBcInRhYkluZGV4XCIsIC0xICk7XG5cdFx0fSBlbHNlIGlmICggdG9TaG93Lmxlbmd0aCApIHtcblx0XHRcdHRoaXMudGFicy5maWx0ZXIoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRyZXR1cm4gJCggdGhpcyApLmF0dHIoIFwidGFiSW5kZXhcIiApID09PSAwO1xuXHRcdFx0fSApXG5cdFx0XHRcdC5hdHRyKCBcInRhYkluZGV4XCIsIC0xICk7XG5cdFx0fVxuXG5cdFx0dG9TaG93LmF0dHIoIFwiYXJpYS1oaWRkZW5cIiwgXCJmYWxzZVwiICk7XG5cdFx0ZXZlbnREYXRhLm5ld1RhYi5hdHRyKCB7XG5cdFx0XHRcImFyaWEtc2VsZWN0ZWRcIjogXCJ0cnVlXCIsXG5cdFx0XHRcImFyaWEtZXhwYW5kZWRcIjogXCJ0cnVlXCIsXG5cdFx0XHR0YWJJbmRleDogMFxuXHRcdH0gKTtcblx0fSxcblxuXHRfYWN0aXZhdGU6IGZ1bmN0aW9uKCBpbmRleCApIHtcblx0XHR2YXIgYW5jaG9yLFxuXHRcdFx0YWN0aXZlID0gdGhpcy5fZmluZEFjdGl2ZSggaW5kZXggKTtcblxuXHRcdC8vIFRyeWluZyB0byBhY3RpdmF0ZSB0aGUgYWxyZWFkeSBhY3RpdmUgcGFuZWxcblx0XHRpZiAoIGFjdGl2ZVsgMCBdID09PSB0aGlzLmFjdGl2ZVsgMCBdICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIFRyeWluZyB0byBjb2xsYXBzZSwgc2ltdWxhdGUgYSBjbGljayBvbiB0aGUgY3VycmVudCBhY3RpdmUgaGVhZGVyXG5cdFx0aWYgKCAhYWN0aXZlLmxlbmd0aCApIHtcblx0XHRcdGFjdGl2ZSA9IHRoaXMuYWN0aXZlO1xuXHRcdH1cblxuXHRcdGFuY2hvciA9IGFjdGl2ZS5maW5kKCBcIi51aS10YWJzLWFuY2hvclwiIClbIDAgXTtcblx0XHR0aGlzLl9ldmVudEhhbmRsZXIoIHtcblx0XHRcdHRhcmdldDogYW5jaG9yLFxuXHRcdFx0Y3VycmVudFRhcmdldDogYW5jaG9yLFxuXHRcdFx0cHJldmVudERlZmF1bHQ6ICQubm9vcFxuXHRcdH0gKTtcblx0fSxcblxuXHRfZmluZEFjdGl2ZTogZnVuY3Rpb24oIGluZGV4ICkge1xuXHRcdHJldHVybiBpbmRleCA9PT0gZmFsc2UgPyAkKCkgOiB0aGlzLnRhYnMuZXEoIGluZGV4ICk7XG5cdH0sXG5cblx0X2dldEluZGV4OiBmdW5jdGlvbiggaW5kZXggKSB7XG5cblx0XHQvLyBtZXRhLWZ1bmN0aW9uIHRvIGdpdmUgdXNlcnMgb3B0aW9uIHRvIHByb3ZpZGUgYSBocmVmIHN0cmluZyBpbnN0ZWFkIG9mIGEgbnVtZXJpY2FsIGluZGV4LlxuXHRcdGlmICggdHlwZW9mIGluZGV4ID09PSBcInN0cmluZ1wiICkge1xuXHRcdFx0aW5kZXggPSB0aGlzLmFuY2hvcnMuaW5kZXgoIHRoaXMuYW5jaG9ycy5maWx0ZXIoIFwiW2hyZWYkPSdcIiArXG5cdFx0XHRcdCQudWkuZXNjYXBlU2VsZWN0b3IoIGluZGV4ICkgKyBcIiddXCIgKSApO1xuXHRcdH1cblxuXHRcdHJldHVybiBpbmRleDtcblx0fSxcblxuXHRfZGVzdHJveTogZnVuY3Rpb24oKSB7XG5cdFx0aWYgKCB0aGlzLnhociApIHtcblx0XHRcdHRoaXMueGhyLmFib3J0KCk7XG5cdFx0fVxuXG5cdFx0dGhpcy50YWJsaXN0XG5cdFx0XHQucmVtb3ZlQXR0ciggXCJyb2xlXCIgKVxuXHRcdFx0Lm9mZiggdGhpcy5ldmVudE5hbWVzcGFjZSApO1xuXG5cdFx0dGhpcy5hbmNob3JzXG5cdFx0XHQucmVtb3ZlQXR0ciggXCJyb2xlIHRhYkluZGV4XCIgKVxuXHRcdFx0LnJlbW92ZVVuaXF1ZUlkKCk7XG5cblx0XHR0aGlzLnRhYnMuYWRkKCB0aGlzLnBhbmVscyApLmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0aWYgKCAkLmRhdGEoIHRoaXMsIFwidWktdGFicy1kZXN0cm95XCIgKSApIHtcblx0XHRcdFx0JCggdGhpcyApLnJlbW92ZSgpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0JCggdGhpcyApLnJlbW92ZUF0dHIoIFwicm9sZSB0YWJJbmRleCBcIiArXG5cdFx0XHRcdFx0XCJhcmlhLWxpdmUgYXJpYS1idXN5IGFyaWEtc2VsZWN0ZWQgYXJpYS1sYWJlbGxlZGJ5IGFyaWEtaGlkZGVuIGFyaWEtZXhwYW5kZWRcIiApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHRcdHRoaXMudGFicy5lYWNoKCBmdW5jdGlvbigpIHtcblx0XHRcdHZhciBsaSA9ICQoIHRoaXMgKSxcblx0XHRcdFx0cHJldiA9IGxpLmRhdGEoIFwidWktdGFicy1hcmlhLWNvbnRyb2xzXCIgKTtcblx0XHRcdGlmICggcHJldiApIHtcblx0XHRcdFx0bGlcblx0XHRcdFx0XHQuYXR0ciggXCJhcmlhLWNvbnRyb2xzXCIsIHByZXYgKVxuXHRcdFx0XHRcdC5yZW1vdmVEYXRhKCBcInVpLXRhYnMtYXJpYS1jb250cm9sc1wiICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRsaS5yZW1vdmVBdHRyKCBcImFyaWEtY29udHJvbHNcIiApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHRcdHRoaXMucGFuZWxzLnNob3coKTtcblxuXHRcdGlmICggdGhpcy5vcHRpb25zLmhlaWdodFN0eWxlICE9PSBcImNvbnRlbnRcIiApIHtcblx0XHRcdHRoaXMucGFuZWxzLmNzcyggXCJoZWlnaHRcIiwgXCJcIiApO1xuXHRcdH1cblx0fSxcblxuXHRlbmFibGU6IGZ1bmN0aW9uKCBpbmRleCApIHtcblx0XHR2YXIgZGlzYWJsZWQgPSB0aGlzLm9wdGlvbnMuZGlzYWJsZWQ7XG5cdFx0aWYgKCBkaXNhYmxlZCA9PT0gZmFsc2UgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aWYgKCBpbmRleCA9PT0gdW5kZWZpbmVkICkge1xuXHRcdFx0ZGlzYWJsZWQgPSBmYWxzZTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0aW5kZXggPSB0aGlzLl9nZXRJbmRleCggaW5kZXggKTtcblx0XHRcdGlmICggJC5pc0FycmF5KCBkaXNhYmxlZCApICkge1xuXHRcdFx0XHRkaXNhYmxlZCA9ICQubWFwKCBkaXNhYmxlZCwgZnVuY3Rpb24oIG51bSApIHtcblx0XHRcdFx0XHRyZXR1cm4gbnVtICE9PSBpbmRleCA/IG51bSA6IG51bGw7XG5cdFx0XHRcdH0gKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGRpc2FibGVkID0gJC5tYXAoIHRoaXMudGFicywgZnVuY3Rpb24oIGxpLCBudW0gKSB7XG5cdFx0XHRcdFx0cmV0dXJuIG51bSAhPT0gaW5kZXggPyBudW0gOiBudWxsO1xuXHRcdFx0XHR9ICk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHRoaXMuX3NldE9wdGlvbkRpc2FibGVkKCBkaXNhYmxlZCApO1xuXHR9LFxuXG5cdGRpc2FibGU6IGZ1bmN0aW9uKCBpbmRleCApIHtcblx0XHR2YXIgZGlzYWJsZWQgPSB0aGlzLm9wdGlvbnMuZGlzYWJsZWQ7XG5cdFx0aWYgKCBkaXNhYmxlZCA9PT0gdHJ1ZSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoIGluZGV4ID09PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRkaXNhYmxlZCA9IHRydWU7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGluZGV4ID0gdGhpcy5fZ2V0SW5kZXgoIGluZGV4ICk7XG5cdFx0XHRpZiAoICQuaW5BcnJheSggaW5kZXgsIGRpc2FibGVkICkgIT09IC0xICkge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cdFx0XHRpZiAoICQuaXNBcnJheSggZGlzYWJsZWQgKSApIHtcblx0XHRcdFx0ZGlzYWJsZWQgPSAkLm1lcmdlKCBbIGluZGV4IF0sIGRpc2FibGVkICkuc29ydCgpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0ZGlzYWJsZWQgPSBbIGluZGV4IF07XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHRoaXMuX3NldE9wdGlvbkRpc2FibGVkKCBkaXNhYmxlZCApO1xuXHR9LFxuXG5cdGxvYWQ6IGZ1bmN0aW9uKCBpbmRleCwgZXZlbnQgKSB7XG5cdFx0aW5kZXggPSB0aGlzLl9nZXRJbmRleCggaW5kZXggKTtcblx0XHR2YXIgdGhhdCA9IHRoaXMsXG5cdFx0XHR0YWIgPSB0aGlzLnRhYnMuZXEoIGluZGV4ICksXG5cdFx0XHRhbmNob3IgPSB0YWIuZmluZCggXCIudWktdGFicy1hbmNob3JcIiApLFxuXHRcdFx0cGFuZWwgPSB0aGlzLl9nZXRQYW5lbEZvclRhYiggdGFiICksXG5cdFx0XHRldmVudERhdGEgPSB7XG5cdFx0XHRcdHRhYjogdGFiLFxuXHRcdFx0XHRwYW5lbDogcGFuZWxcblx0XHRcdH0sXG5cdFx0XHRjb21wbGV0ZSA9IGZ1bmN0aW9uKCBqcVhIUiwgc3RhdHVzICkge1xuXHRcdFx0XHRpZiAoIHN0YXR1cyA9PT0gXCJhYm9ydFwiICkge1xuXHRcdFx0XHRcdHRoYXQucGFuZWxzLnN0b3AoIGZhbHNlLCB0cnVlICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGF0Ll9yZW1vdmVDbGFzcyggdGFiLCBcInVpLXRhYnMtbG9hZGluZ1wiICk7XG5cdFx0XHRcdHBhbmVsLnJlbW92ZUF0dHIoIFwiYXJpYS1idXN5XCIgKTtcblxuXHRcdFx0XHRpZiAoIGpxWEhSID09PSB0aGF0LnhociApIHtcblx0XHRcdFx0XHRkZWxldGUgdGhhdC54aHI7XG5cdFx0XHRcdH1cblx0XHRcdH07XG5cblx0XHQvLyBOb3QgcmVtb3RlXG5cdFx0aWYgKCB0aGlzLl9pc0xvY2FsKCBhbmNob3JbIDAgXSApICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHRoaXMueGhyID0gJC5hamF4KCB0aGlzLl9hamF4U2V0dGluZ3MoIGFuY2hvciwgZXZlbnQsIGV2ZW50RGF0YSApICk7XG5cblx0XHQvLyBTdXBwb3J0OiBqUXVlcnkgPDEuOFxuXHRcdC8vIGpRdWVyeSA8MS44IHJldHVybnMgZmFsc2UgaWYgdGhlIHJlcXVlc3QgaXMgY2FuY2VsZWQgaW4gYmVmb3JlU2VuZCxcblx0XHQvLyBidXQgYXMgb2YgMS44LCAkLmFqYXgoKSBhbHdheXMgcmV0dXJucyBhIGpxWEhSIG9iamVjdC5cblx0XHRpZiAoIHRoaXMueGhyICYmIHRoaXMueGhyLnN0YXR1c1RleHQgIT09IFwiY2FuY2VsZWRcIiApIHtcblx0XHRcdHRoaXMuX2FkZENsYXNzKCB0YWIsIFwidWktdGFicy1sb2FkaW5nXCIgKTtcblx0XHRcdHBhbmVsLmF0dHIoIFwiYXJpYS1idXN5XCIsIFwidHJ1ZVwiICk7XG5cblx0XHRcdHRoaXMueGhyXG5cdFx0XHRcdC5kb25lKCBmdW5jdGlvbiggcmVzcG9uc2UsIHN0YXR1cywganFYSFIgKSB7XG5cblx0XHRcdFx0XHQvLyBzdXBwb3J0OiBqUXVlcnkgPDEuOFxuXHRcdFx0XHRcdC8vIGh0dHA6Ly9idWdzLmpxdWVyeS5jb20vdGlja2V0LzExNzc4XG5cdFx0XHRcdFx0c2V0VGltZW91dCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0XHRwYW5lbC5odG1sKCByZXNwb25zZSApO1xuXHRcdFx0XHRcdFx0dGhhdC5fdHJpZ2dlciggXCJsb2FkXCIsIGV2ZW50LCBldmVudERhdGEgKTtcblxuXHRcdFx0XHRcdFx0Y29tcGxldGUoIGpxWEhSLCBzdGF0dXMgKTtcblx0XHRcdFx0XHR9LCAxICk7XG5cdFx0XHRcdH0gKVxuXHRcdFx0XHQuZmFpbCggZnVuY3Rpb24oIGpxWEhSLCBzdGF0dXMgKSB7XG5cblx0XHRcdFx0XHQvLyBzdXBwb3J0OiBqUXVlcnkgPDEuOFxuXHRcdFx0XHRcdC8vIGh0dHA6Ly9idWdzLmpxdWVyeS5jb20vdGlja2V0LzExNzc4XG5cdFx0XHRcdFx0c2V0VGltZW91dCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0XHRjb21wbGV0ZSgganFYSFIsIHN0YXR1cyApO1xuXHRcdFx0XHRcdH0sIDEgKTtcblx0XHRcdFx0fSApO1xuXHRcdH1cblx0fSxcblxuXHRfYWpheFNldHRpbmdzOiBmdW5jdGlvbiggYW5jaG9yLCBldmVudCwgZXZlbnREYXRhICkge1xuXHRcdHZhciB0aGF0ID0gdGhpcztcblx0XHRyZXR1cm4ge1xuXG5cdFx0XHQvLyBTdXBwb3J0OiBJRSA8MTEgb25seVxuXHRcdFx0Ly8gU3RyaXAgYW55IGhhc2ggdGhhdCBleGlzdHMgdG8gcHJldmVudCBlcnJvcnMgd2l0aCB0aGUgQWpheCByZXF1ZXN0XG5cdFx0XHR1cmw6IGFuY2hvci5hdHRyKCBcImhyZWZcIiApLnJlcGxhY2UoIC8jLiokLywgXCJcIiApLFxuXHRcdFx0YmVmb3JlU2VuZDogZnVuY3Rpb24oIGpxWEhSLCBzZXR0aW5ncyApIHtcblx0XHRcdFx0cmV0dXJuIHRoYXQuX3RyaWdnZXIoIFwiYmVmb3JlTG9hZFwiLCBldmVudCxcblx0XHRcdFx0XHQkLmV4dGVuZCggeyBqcVhIUjoganFYSFIsIGFqYXhTZXR0aW5nczogc2V0dGluZ3MgfSwgZXZlbnREYXRhICkgKTtcblx0XHRcdH1cblx0XHR9O1xuXHR9LFxuXG5cdF9nZXRQYW5lbEZvclRhYjogZnVuY3Rpb24oIHRhYiApIHtcblx0XHR2YXIgaWQgPSAkKCB0YWIgKS5hdHRyKCBcImFyaWEtY29udHJvbHNcIiApO1xuXHRcdHJldHVybiB0aGlzLmVsZW1lbnQuZmluZCggdGhpcy5fc2FuaXRpemVTZWxlY3RvciggXCIjXCIgKyBpZCApICk7XG5cdH1cbn0gKTtcblxuLy8gREVQUkVDQVRFRFxuLy8gVE9ETzogU3dpdGNoIHJldHVybiBiYWNrIHRvIHdpZGdldCBkZWNsYXJhdGlvbiBhdCB0b3Agb2YgZmlsZSB3aGVuIHRoaXMgaXMgcmVtb3ZlZFxuaWYgKCAkLnVpQmFja0NvbXBhdCAhPT0gZmFsc2UgKSB7XG5cblx0Ly8gQmFja2NvbXBhdCBmb3IgdWktdGFiIGNsYXNzIChub3cgdWktdGFicy10YWIpXG5cdCQud2lkZ2V0KCBcInVpLnRhYnNcIiwgJC51aS50YWJzLCB7XG5cdFx0X3Byb2Nlc3NUYWJzOiBmdW5jdGlvbigpIHtcblx0XHRcdHRoaXMuX3N1cGVyQXBwbHkoIGFyZ3VtZW50cyApO1xuXHRcdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMudGFicywgXCJ1aS10YWJcIiApO1xuXHRcdH1cblx0fSApO1xufVxuXG52YXIgd2lkZ2V0c1RhYnMgPSAkLnVpLnRhYnM7XG5cblxuLyohXG4gKiBqUXVlcnkgVUkgVG9vbHRpcCAxLjEyLjFcbiAqIGh0dHA6Ly9qcXVlcnl1aS5jb21cbiAqXG4gKiBDb3B5cmlnaHQgalF1ZXJ5IEZvdW5kYXRpb24gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlLlxuICogaHR0cDovL2pxdWVyeS5vcmcvbGljZW5zZVxuICovXG5cbi8vPj5sYWJlbDogVG9vbHRpcFxuLy8+Pmdyb3VwOiBXaWRnZXRzXG4vLz4+ZGVzY3JpcHRpb246IFNob3dzIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gZm9yIGFueSBlbGVtZW50IG9uIGhvdmVyIG9yIGZvY3VzLlxuLy8+PmRvY3M6IGh0dHA6Ly9hcGkuanF1ZXJ5dWkuY29tL3Rvb2x0aXAvXG4vLz4+ZGVtb3M6IGh0dHA6Ly9qcXVlcnl1aS5jb20vdG9vbHRpcC9cbi8vPj5jc3Muc3RydWN0dXJlOiAuLi8uLi90aGVtZXMvYmFzZS9jb3JlLmNzc1xuLy8+PmNzcy5zdHJ1Y3R1cmU6IC4uLy4uL3RoZW1lcy9iYXNlL3Rvb2x0aXAuY3NzXG4vLz4+Y3NzLnRoZW1lOiAuLi8uLi90aGVtZXMvYmFzZS90aGVtZS5jc3NcblxuXG5cbiQud2lkZ2V0KCBcInVpLnRvb2x0aXBcIiwge1xuXHR2ZXJzaW9uOiBcIjEuMTIuMVwiLFxuXHRvcHRpb25zOiB7XG5cdFx0Y2xhc3Nlczoge1xuXHRcdFx0XCJ1aS10b29sdGlwXCI6IFwidWktY29ybmVyLWFsbCB1aS13aWRnZXQtc2hhZG93XCJcblx0XHR9LFxuXHRcdGNvbnRlbnQ6IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHQvLyBzdXBwb3J0OiBJRTw5LCBPcGVyYSBpbiBqUXVlcnkgPDEuN1xuXHRcdFx0Ly8gLnRleHQoKSBjYW4ndCBhY2NlcHQgdW5kZWZpbmVkLCBzbyBjb2VyY2UgdG8gYSBzdHJpbmdcblx0XHRcdHZhciB0aXRsZSA9ICQoIHRoaXMgKS5hdHRyKCBcInRpdGxlXCIgKSB8fCBcIlwiO1xuXG5cdFx0XHQvLyBFc2NhcGUgdGl0bGUsIHNpbmNlIHdlJ3JlIGdvaW5nIGZyb20gYW4gYXR0cmlidXRlIHRvIHJhdyBIVE1MXG5cdFx0XHRyZXR1cm4gJCggXCI8YT5cIiApLnRleHQoIHRpdGxlICkuaHRtbCgpO1xuXHRcdH0sXG5cdFx0aGlkZTogdHJ1ZSxcblxuXHRcdC8vIERpc2FibGVkIGVsZW1lbnRzIGhhdmUgaW5jb25zaXN0ZW50IGJlaGF2aW9yIGFjcm9zcyBicm93c2VycyAoIzg2NjEpXG5cdFx0aXRlbXM6IFwiW3RpdGxlXTpub3QoW2Rpc2FibGVkXSlcIixcblx0XHRwb3NpdGlvbjoge1xuXHRcdFx0bXk6IFwibGVmdCB0b3ArMTVcIixcblx0XHRcdGF0OiBcImxlZnQgYm90dG9tXCIsXG5cdFx0XHRjb2xsaXNpb246IFwiZmxpcGZpdCBmbGlwXCJcblx0XHR9LFxuXHRcdHNob3c6IHRydWUsXG5cdFx0dHJhY2s6IGZhbHNlLFxuXG5cdFx0Ly8gQ2FsbGJhY2tzXG5cdFx0Y2xvc2U6IG51bGwsXG5cdFx0b3BlbjogbnVsbFxuXHR9LFxuXG5cdF9hZGREZXNjcmliZWRCeTogZnVuY3Rpb24oIGVsZW0sIGlkICkge1xuXHRcdHZhciBkZXNjcmliZWRieSA9ICggZWxlbS5hdHRyKCBcImFyaWEtZGVzY3JpYmVkYnlcIiApIHx8IFwiXCIgKS5zcGxpdCggL1xccysvICk7XG5cdFx0ZGVzY3JpYmVkYnkucHVzaCggaWQgKTtcblx0XHRlbGVtXG5cdFx0XHQuZGF0YSggXCJ1aS10b29sdGlwLWlkXCIsIGlkIClcblx0XHRcdC5hdHRyKCBcImFyaWEtZGVzY3JpYmVkYnlcIiwgJC50cmltKCBkZXNjcmliZWRieS5qb2luKCBcIiBcIiApICkgKTtcblx0fSxcblxuXHRfcmVtb3ZlRGVzY3JpYmVkQnk6IGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdHZhciBpZCA9IGVsZW0uZGF0YSggXCJ1aS10b29sdGlwLWlkXCIgKSxcblx0XHRcdGRlc2NyaWJlZGJ5ID0gKCBlbGVtLmF0dHIoIFwiYXJpYS1kZXNjcmliZWRieVwiICkgfHwgXCJcIiApLnNwbGl0KCAvXFxzKy8gKSxcblx0XHRcdGluZGV4ID0gJC5pbkFycmF5KCBpZCwgZGVzY3JpYmVkYnkgKTtcblxuXHRcdGlmICggaW5kZXggIT09IC0xICkge1xuXHRcdFx0ZGVzY3JpYmVkYnkuc3BsaWNlKCBpbmRleCwgMSApO1xuXHRcdH1cblxuXHRcdGVsZW0ucmVtb3ZlRGF0YSggXCJ1aS10b29sdGlwLWlkXCIgKTtcblx0XHRkZXNjcmliZWRieSA9ICQudHJpbSggZGVzY3JpYmVkYnkuam9pbiggXCIgXCIgKSApO1xuXHRcdGlmICggZGVzY3JpYmVkYnkgKSB7XG5cdFx0XHRlbGVtLmF0dHIoIFwiYXJpYS1kZXNjcmliZWRieVwiLCBkZXNjcmliZWRieSApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRlbGVtLnJlbW92ZUF0dHIoIFwiYXJpYS1kZXNjcmliZWRieVwiICk7XG5cdFx0fVxuXHR9LFxuXG5cdF9jcmVhdGU6IGZ1bmN0aW9uKCkge1xuXHRcdHRoaXMuX29uKCB7XG5cdFx0XHRtb3VzZW92ZXI6IFwib3BlblwiLFxuXHRcdFx0Zm9jdXNpbjogXCJvcGVuXCJcblx0XHR9ICk7XG5cblx0XHQvLyBJRHMgb2YgZ2VuZXJhdGVkIHRvb2x0aXBzLCBuZWVkZWQgZm9yIGRlc3Ryb3lcblx0XHR0aGlzLnRvb2x0aXBzID0ge307XG5cblx0XHQvLyBJRHMgb2YgcGFyZW50IHRvb2x0aXBzIHdoZXJlIHdlIHJlbW92ZWQgdGhlIHRpdGxlIGF0dHJpYnV0ZVxuXHRcdHRoaXMucGFyZW50cyA9IHt9O1xuXG5cdFx0Ly8gQXBwZW5kIHRoZSBhcmlhLWxpdmUgcmVnaW9uIHNvIHRvb2x0aXBzIGFubm91bmNlIGNvcnJlY3RseVxuXHRcdHRoaXMubGl2ZVJlZ2lvbiA9ICQoIFwiPGRpdj5cIiApXG5cdFx0XHQuYXR0cigge1xuXHRcdFx0XHRyb2xlOiBcImxvZ1wiLFxuXHRcdFx0XHRcImFyaWEtbGl2ZVwiOiBcImFzc2VydGl2ZVwiLFxuXHRcdFx0XHRcImFyaWEtcmVsZXZhbnRcIjogXCJhZGRpdGlvbnNcIlxuXHRcdFx0fSApXG5cdFx0XHQuYXBwZW5kVG8oIHRoaXMuZG9jdW1lbnRbIDAgXS5ib2R5ICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRoaXMubGl2ZVJlZ2lvbiwgbnVsbCwgXCJ1aS1oZWxwZXItaGlkZGVuLWFjY2Vzc2libGVcIiApO1xuXG5cdFx0dGhpcy5kaXNhYmxlZFRpdGxlcyA9ICQoIFtdICk7XG5cdH0sXG5cblx0X3NldE9wdGlvbjogZnVuY3Rpb24oIGtleSwgdmFsdWUgKSB7XG5cdFx0dmFyIHRoYXQgPSB0aGlzO1xuXG5cdFx0dGhpcy5fc3VwZXIoIGtleSwgdmFsdWUgKTtcblxuXHRcdGlmICgga2V5ID09PSBcImNvbnRlbnRcIiApIHtcblx0XHRcdCQuZWFjaCggdGhpcy50b29sdGlwcywgZnVuY3Rpb24oIGlkLCB0b29sdGlwRGF0YSApIHtcblx0XHRcdFx0dGhhdC5fdXBkYXRlQ29udGVudCggdG9vbHRpcERhdGEuZWxlbWVudCApO1xuXHRcdFx0fSApO1xuXHRcdH1cblx0fSxcblxuXHRfc2V0T3B0aW9uRGlzYWJsZWQ6IGZ1bmN0aW9uKCB2YWx1ZSApIHtcblx0XHR0aGlzWyB2YWx1ZSA/IFwiX2Rpc2FibGVcIiA6IFwiX2VuYWJsZVwiIF0oKTtcblx0fSxcblxuXHRfZGlzYWJsZTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIHRoYXQgPSB0aGlzO1xuXG5cdFx0Ly8gQ2xvc2Ugb3BlbiB0b29sdGlwc1xuXHRcdCQuZWFjaCggdGhpcy50b29sdGlwcywgZnVuY3Rpb24oIGlkLCB0b29sdGlwRGF0YSApIHtcblx0XHRcdHZhciBldmVudCA9ICQuRXZlbnQoIFwiYmx1clwiICk7XG5cdFx0XHRldmVudC50YXJnZXQgPSBldmVudC5jdXJyZW50VGFyZ2V0ID0gdG9vbHRpcERhdGEuZWxlbWVudFsgMCBdO1xuXHRcdFx0dGhhdC5jbG9zZSggZXZlbnQsIHRydWUgKTtcblx0XHR9ICk7XG5cblx0XHQvLyBSZW1vdmUgdGl0bGUgYXR0cmlidXRlcyB0byBwcmV2ZW50IG5hdGl2ZSB0b29sdGlwc1xuXHRcdHRoaXMuZGlzYWJsZWRUaXRsZXMgPSB0aGlzLmRpc2FibGVkVGl0bGVzLmFkZChcblx0XHRcdHRoaXMuZWxlbWVudC5maW5kKCB0aGlzLm9wdGlvbnMuaXRlbXMgKS5hZGRCYWNrKClcblx0XHRcdFx0LmZpbHRlciggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0dmFyIGVsZW1lbnQgPSAkKCB0aGlzICk7XG5cdFx0XHRcdFx0aWYgKCBlbGVtZW50LmlzKCBcIlt0aXRsZV1cIiApICkge1xuXHRcdFx0XHRcdFx0cmV0dXJuIGVsZW1lbnRcblx0XHRcdFx0XHRcdFx0LmRhdGEoIFwidWktdG9vbHRpcC10aXRsZVwiLCBlbGVtZW50LmF0dHIoIFwidGl0bGVcIiApIClcblx0XHRcdFx0XHRcdFx0LnJlbW92ZUF0dHIoIFwidGl0bGVcIiApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSApXG5cdFx0KTtcblx0fSxcblxuXHRfZW5hYmxlOiBmdW5jdGlvbigpIHtcblxuXHRcdC8vIHJlc3RvcmUgdGl0bGUgYXR0cmlidXRlc1xuXHRcdHRoaXMuZGlzYWJsZWRUaXRsZXMuZWFjaCggZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgZWxlbWVudCA9ICQoIHRoaXMgKTtcblx0XHRcdGlmICggZWxlbWVudC5kYXRhKCBcInVpLXRvb2x0aXAtdGl0bGVcIiApICkge1xuXHRcdFx0XHRlbGVtZW50LmF0dHIoIFwidGl0bGVcIiwgZWxlbWVudC5kYXRhKCBcInVpLXRvb2x0aXAtdGl0bGVcIiApICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXHRcdHRoaXMuZGlzYWJsZWRUaXRsZXMgPSAkKCBbXSApO1xuXHR9LFxuXG5cdG9wZW46IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgdGhhdCA9IHRoaXMsXG5cdFx0XHR0YXJnZXQgPSAkKCBldmVudCA/IGV2ZW50LnRhcmdldCA6IHRoaXMuZWxlbWVudCApXG5cblx0XHRcdFx0Ly8gd2UgbmVlZCBjbG9zZXN0IGhlcmUgZHVlIHRvIG1vdXNlb3ZlciBidWJibGluZyxcblx0XHRcdFx0Ly8gYnV0IGFsd2F5cyBwb2ludGluZyBhdCB0aGUgc2FtZSBldmVudCB0YXJnZXRcblx0XHRcdFx0LmNsb3Nlc3QoIHRoaXMub3B0aW9ucy5pdGVtcyApO1xuXG5cdFx0Ly8gTm8gZWxlbWVudCB0byBzaG93IGEgdG9vbHRpcCBmb3Igb3IgdGhlIHRvb2x0aXAgaXMgYWxyZWFkeSBvcGVuXG5cdFx0aWYgKCAhdGFyZ2V0Lmxlbmd0aCB8fCB0YXJnZXQuZGF0YSggXCJ1aS10b29sdGlwLWlkXCIgKSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoIHRhcmdldC5hdHRyKCBcInRpdGxlXCIgKSApIHtcblx0XHRcdHRhcmdldC5kYXRhKCBcInVpLXRvb2x0aXAtdGl0bGVcIiwgdGFyZ2V0LmF0dHIoIFwidGl0bGVcIiApICk7XG5cdFx0fVxuXG5cdFx0dGFyZ2V0LmRhdGEoIFwidWktdG9vbHRpcC1vcGVuXCIsIHRydWUgKTtcblxuXHRcdC8vIEtpbGwgcGFyZW50IHRvb2x0aXBzLCBjdXN0b20gb3IgbmF0aXZlLCBmb3IgaG92ZXJcblx0XHRpZiAoIGV2ZW50ICYmIGV2ZW50LnR5cGUgPT09IFwibW91c2VvdmVyXCIgKSB7XG5cdFx0XHR0YXJnZXQucGFyZW50cygpLmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHR2YXIgcGFyZW50ID0gJCggdGhpcyApLFxuXHRcdFx0XHRcdGJsdXJFdmVudDtcblx0XHRcdFx0aWYgKCBwYXJlbnQuZGF0YSggXCJ1aS10b29sdGlwLW9wZW5cIiApICkge1xuXHRcdFx0XHRcdGJsdXJFdmVudCA9ICQuRXZlbnQoIFwiYmx1clwiICk7XG5cdFx0XHRcdFx0Ymx1ckV2ZW50LnRhcmdldCA9IGJsdXJFdmVudC5jdXJyZW50VGFyZ2V0ID0gdGhpcztcblx0XHRcdFx0XHR0aGF0LmNsb3NlKCBibHVyRXZlbnQsIHRydWUgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoIHBhcmVudC5hdHRyKCBcInRpdGxlXCIgKSApIHtcblx0XHRcdFx0XHRwYXJlbnQudW5pcXVlSWQoKTtcblx0XHRcdFx0XHR0aGF0LnBhcmVudHNbIHRoaXMuaWQgXSA9IHtcblx0XHRcdFx0XHRcdGVsZW1lbnQ6IHRoaXMsXG5cdFx0XHRcdFx0XHR0aXRsZTogcGFyZW50LmF0dHIoIFwidGl0bGVcIiApXG5cdFx0XHRcdFx0fTtcblx0XHRcdFx0XHRwYXJlbnQuYXR0ciggXCJ0aXRsZVwiLCBcIlwiICk7XG5cdFx0XHRcdH1cblx0XHRcdH0gKTtcblx0XHR9XG5cblx0XHR0aGlzLl9yZWdpc3RlckNsb3NlSGFuZGxlcnMoIGV2ZW50LCB0YXJnZXQgKTtcblx0XHR0aGlzLl91cGRhdGVDb250ZW50KCB0YXJnZXQsIGV2ZW50ICk7XG5cdH0sXG5cblx0X3VwZGF0ZUNvbnRlbnQ6IGZ1bmN0aW9uKCB0YXJnZXQsIGV2ZW50ICkge1xuXHRcdHZhciBjb250ZW50LFxuXHRcdFx0Y29udGVudE9wdGlvbiA9IHRoaXMub3B0aW9ucy5jb250ZW50LFxuXHRcdFx0dGhhdCA9IHRoaXMsXG5cdFx0XHRldmVudFR5cGUgPSBldmVudCA/IGV2ZW50LnR5cGUgOiBudWxsO1xuXG5cdFx0aWYgKCB0eXBlb2YgY29udGVudE9wdGlvbiA9PT0gXCJzdHJpbmdcIiB8fCBjb250ZW50T3B0aW9uLm5vZGVUeXBlIHx8XG5cdFx0XHRcdGNvbnRlbnRPcHRpb24uanF1ZXJ5ICkge1xuXHRcdFx0cmV0dXJuIHRoaXMuX29wZW4oIGV2ZW50LCB0YXJnZXQsIGNvbnRlbnRPcHRpb24gKTtcblx0XHR9XG5cblx0XHRjb250ZW50ID0gY29udGVudE9wdGlvbi5jYWxsKCB0YXJnZXRbIDAgXSwgZnVuY3Rpb24oIHJlc3BvbnNlICkge1xuXG5cdFx0XHQvLyBJRSBtYXkgaW5zdGFudGx5IHNlcnZlIGEgY2FjaGVkIHJlc3BvbnNlIGZvciBhamF4IHJlcXVlc3RzXG5cdFx0XHQvLyBkZWxheSB0aGlzIGNhbGwgdG8gX29wZW4gc28gdGhlIG90aGVyIGNhbGwgdG8gX29wZW4gcnVucyBmaXJzdFxuXHRcdFx0dGhhdC5fZGVsYXkoIGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRcdC8vIElnbm9yZSBhc3luYyByZXNwb25zZSBpZiB0b29sdGlwIHdhcyBjbG9zZWQgYWxyZWFkeVxuXHRcdFx0XHRpZiAoICF0YXJnZXQuZGF0YSggXCJ1aS10b29sdGlwLW9wZW5cIiApICkge1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIEpRdWVyeSBjcmVhdGVzIGEgc3BlY2lhbCBldmVudCBmb3IgZm9jdXNpbiB3aGVuIGl0IGRvZXNuJ3Rcblx0XHRcdFx0Ly8gZXhpc3QgbmF0aXZlbHkuIFRvIGltcHJvdmUgcGVyZm9ybWFuY2UsIHRoZSBuYXRpdmUgZXZlbnRcblx0XHRcdFx0Ly8gb2JqZWN0IGlzIHJldXNlZCBhbmQgdGhlIHR5cGUgaXMgY2hhbmdlZC4gVGhlcmVmb3JlLCB3ZSBjYW4ndFxuXHRcdFx0XHQvLyByZWx5IG9uIHRoZSB0eXBlIGJlaW5nIGNvcnJlY3QgYWZ0ZXIgdGhlIGV2ZW50IGZpbmlzaGVkXG5cdFx0XHRcdC8vIGJ1YmJsaW5nLCBzbyB3ZSBzZXQgaXQgYmFjayB0byB0aGUgcHJldmlvdXMgdmFsdWUuICgjODc0MClcblx0XHRcdFx0aWYgKCBldmVudCApIHtcblx0XHRcdFx0XHRldmVudC50eXBlID0gZXZlbnRUeXBlO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHRoaXMuX29wZW4oIGV2ZW50LCB0YXJnZXQsIHJlc3BvbnNlICk7XG5cdFx0XHR9ICk7XG5cdFx0fSApO1xuXHRcdGlmICggY29udGVudCApIHtcblx0XHRcdHRoaXMuX29wZW4oIGV2ZW50LCB0YXJnZXQsIGNvbnRlbnQgKTtcblx0XHR9XG5cdH0sXG5cblx0X29wZW46IGZ1bmN0aW9uKCBldmVudCwgdGFyZ2V0LCBjb250ZW50ICkge1xuXHRcdHZhciB0b29sdGlwRGF0YSwgdG9vbHRpcCwgZGVsYXllZFNob3csIGExMXlDb250ZW50LFxuXHRcdFx0cG9zaXRpb25PcHRpb24gPSAkLmV4dGVuZCgge30sIHRoaXMub3B0aW9ucy5wb3NpdGlvbiApO1xuXG5cdFx0aWYgKCAhY29udGVudCApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBDb250ZW50IGNhbiBiZSB1cGRhdGVkIG11bHRpcGxlIHRpbWVzLiBJZiB0aGUgdG9vbHRpcCBhbHJlYWR5XG5cdFx0Ly8gZXhpc3RzLCB0aGVuIGp1c3QgdXBkYXRlIHRoZSBjb250ZW50IGFuZCBiYWlsLlxuXHRcdHRvb2x0aXBEYXRhID0gdGhpcy5fZmluZCggdGFyZ2V0ICk7XG5cdFx0aWYgKCB0b29sdGlwRGF0YSApIHtcblx0XHRcdHRvb2x0aXBEYXRhLnRvb2x0aXAuZmluZCggXCIudWktdG9vbHRpcC1jb250ZW50XCIgKS5odG1sKCBjb250ZW50ICk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gSWYgd2UgaGF2ZSBhIHRpdGxlLCBjbGVhciBpdCB0byBwcmV2ZW50IHRoZSBuYXRpdmUgdG9vbHRpcFxuXHRcdC8vIHdlIGhhdmUgdG8gY2hlY2sgZmlyc3QgdG8gYXZvaWQgZGVmaW5pbmcgYSB0aXRsZSBpZiBub25lIGV4aXN0c1xuXHRcdC8vICh3ZSBkb24ndCB3YW50IHRvIGNhdXNlIGFuIGVsZW1lbnQgdG8gc3RhcnQgbWF0Y2hpbmcgW3RpdGxlXSlcblx0XHQvL1xuXHRcdC8vIFdlIHVzZSByZW1vdmVBdHRyIG9ubHkgZm9yIGtleSBldmVudHMsIHRvIGFsbG93IElFIHRvIGV4cG9ydCB0aGUgY29ycmVjdFxuXHRcdC8vIGFjY2Vzc2libGUgYXR0cmlidXRlcy4gRm9yIG1vdXNlIGV2ZW50cywgc2V0IHRvIGVtcHR5IHN0cmluZyB0byBhdm9pZFxuXHRcdC8vIG5hdGl2ZSB0b29sdGlwIHNob3dpbmcgdXAgKGhhcHBlbnMgb25seSB3aGVuIHJlbW92aW5nIGluc2lkZSBtb3VzZW92ZXIpLlxuXHRcdGlmICggdGFyZ2V0LmlzKCBcIlt0aXRsZV1cIiApICkge1xuXHRcdFx0aWYgKCBldmVudCAmJiBldmVudC50eXBlID09PSBcIm1vdXNlb3ZlclwiICkge1xuXHRcdFx0XHR0YXJnZXQuYXR0ciggXCJ0aXRsZVwiLCBcIlwiICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0YXJnZXQucmVtb3ZlQXR0ciggXCJ0aXRsZVwiICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0dG9vbHRpcERhdGEgPSB0aGlzLl90b29sdGlwKCB0YXJnZXQgKTtcblx0XHR0b29sdGlwID0gdG9vbHRpcERhdGEudG9vbHRpcDtcblx0XHR0aGlzLl9hZGREZXNjcmliZWRCeSggdGFyZ2V0LCB0b29sdGlwLmF0dHIoIFwiaWRcIiApICk7XG5cdFx0dG9vbHRpcC5maW5kKCBcIi51aS10b29sdGlwLWNvbnRlbnRcIiApLmh0bWwoIGNvbnRlbnQgKTtcblxuXHRcdC8vIFN1cHBvcnQ6IFZvaWNlb3ZlciBvbiBPUyBYLCBKQVdTIG9uIElFIDw9IDlcblx0XHQvLyBKQVdTIGFubm91bmNlcyBkZWxldGlvbnMgZXZlbiB3aGVuIGFyaWEtcmVsZXZhbnQ9XCJhZGRpdGlvbnNcIlxuXHRcdC8vIFZvaWNlb3ZlciB3aWxsIHNvbWV0aW1lcyByZS1yZWFkIHRoZSBlbnRpcmUgbG9nIHJlZ2lvbidzIGNvbnRlbnRzIGZyb20gdGhlIGJlZ2lubmluZ1xuXHRcdHRoaXMubGl2ZVJlZ2lvbi5jaGlsZHJlbigpLmhpZGUoKTtcblx0XHRhMTF5Q29udGVudCA9ICQoIFwiPGRpdj5cIiApLmh0bWwoIHRvb2x0aXAuZmluZCggXCIudWktdG9vbHRpcC1jb250ZW50XCIgKS5odG1sKCkgKTtcblx0XHRhMTF5Q29udGVudC5yZW1vdmVBdHRyKCBcIm5hbWVcIiApLmZpbmQoIFwiW25hbWVdXCIgKS5yZW1vdmVBdHRyKCBcIm5hbWVcIiApO1xuXHRcdGExMXlDb250ZW50LnJlbW92ZUF0dHIoIFwiaWRcIiApLmZpbmQoIFwiW2lkXVwiICkucmVtb3ZlQXR0ciggXCJpZFwiICk7XG5cdFx0YTExeUNvbnRlbnQuYXBwZW5kVG8oIHRoaXMubGl2ZVJlZ2lvbiApO1xuXG5cdFx0ZnVuY3Rpb24gcG9zaXRpb24oIGV2ZW50ICkge1xuXHRcdFx0cG9zaXRpb25PcHRpb24ub2YgPSBldmVudDtcblx0XHRcdGlmICggdG9vbHRpcC5pcyggXCI6aGlkZGVuXCIgKSApIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXHRcdFx0dG9vbHRpcC5wb3NpdGlvbiggcG9zaXRpb25PcHRpb24gKTtcblx0XHR9XG5cdFx0aWYgKCB0aGlzLm9wdGlvbnMudHJhY2sgJiYgZXZlbnQgJiYgL15tb3VzZS8udGVzdCggZXZlbnQudHlwZSApICkge1xuXHRcdFx0dGhpcy5fb24oIHRoaXMuZG9jdW1lbnQsIHtcblx0XHRcdFx0bW91c2Vtb3ZlOiBwb3NpdGlvblxuXHRcdFx0fSApO1xuXG5cdFx0XHQvLyB0cmlnZ2VyIG9uY2UgdG8gb3ZlcnJpZGUgZWxlbWVudC1yZWxhdGl2ZSBwb3NpdGlvbmluZ1xuXHRcdFx0cG9zaXRpb24oIGV2ZW50ICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRvb2x0aXAucG9zaXRpb24oICQuZXh0ZW5kKCB7XG5cdFx0XHRcdG9mOiB0YXJnZXRcblx0XHRcdH0sIHRoaXMub3B0aW9ucy5wb3NpdGlvbiApICk7XG5cdFx0fVxuXG5cdFx0dG9vbHRpcC5oaWRlKCk7XG5cblx0XHR0aGlzLl9zaG93KCB0b29sdGlwLCB0aGlzLm9wdGlvbnMuc2hvdyApO1xuXG5cdFx0Ly8gSGFuZGxlIHRyYWNraW5nIHRvb2x0aXBzIHRoYXQgYXJlIHNob3duIHdpdGggYSBkZWxheSAoIzg2NDQpLiBBcyBzb29uXG5cdFx0Ly8gYXMgdGhlIHRvb2x0aXAgaXMgdmlzaWJsZSwgcG9zaXRpb24gdGhlIHRvb2x0aXAgdXNpbmcgdGhlIG1vc3QgcmVjZW50XG5cdFx0Ly8gZXZlbnQuXG5cdFx0Ly8gQWRkcyB0aGUgY2hlY2sgdG8gYWRkIHRoZSB0aW1lcnMgb25seSB3aGVuIGJvdGggZGVsYXkgYW5kIHRyYWNrIG9wdGlvbnMgYXJlIHNldCAoIzE0NjgyKVxuXHRcdGlmICggdGhpcy5vcHRpb25zLnRyYWNrICYmIHRoaXMub3B0aW9ucy5zaG93ICYmIHRoaXMub3B0aW9ucy5zaG93LmRlbGF5ICkge1xuXHRcdFx0ZGVsYXllZFNob3cgPSB0aGlzLmRlbGF5ZWRTaG93ID0gc2V0SW50ZXJ2YWwoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRpZiAoIHRvb2x0aXAuaXMoIFwiOnZpc2libGVcIiApICkge1xuXHRcdFx0XHRcdHBvc2l0aW9uKCBwb3NpdGlvbk9wdGlvbi5vZiApO1xuXHRcdFx0XHRcdGNsZWFySW50ZXJ2YWwoIGRlbGF5ZWRTaG93ICk7XG5cdFx0XHRcdH1cblx0XHRcdH0sICQuZnguaW50ZXJ2YWwgKTtcblx0XHR9XG5cblx0XHR0aGlzLl90cmlnZ2VyKCBcIm9wZW5cIiwgZXZlbnQsIHsgdG9vbHRpcDogdG9vbHRpcCB9ICk7XG5cdH0sXG5cblx0X3JlZ2lzdGVyQ2xvc2VIYW5kbGVyczogZnVuY3Rpb24oIGV2ZW50LCB0YXJnZXQgKSB7XG5cdFx0dmFyIGV2ZW50cyA9IHtcblx0XHRcdGtleXVwOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRcdGlmICggZXZlbnQua2V5Q29kZSA9PT0gJC51aS5rZXlDb2RlLkVTQ0FQRSApIHtcblx0XHRcdFx0XHR2YXIgZmFrZUV2ZW50ID0gJC5FdmVudCggZXZlbnQgKTtcblx0XHRcdFx0XHRmYWtlRXZlbnQuY3VycmVudFRhcmdldCA9IHRhcmdldFsgMCBdO1xuXHRcdFx0XHRcdHRoaXMuY2xvc2UoIGZha2VFdmVudCwgdHJ1ZSApO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fTtcblxuXHRcdC8vIE9ubHkgYmluZCByZW1vdmUgaGFuZGxlciBmb3IgZGVsZWdhdGVkIHRhcmdldHMuIE5vbi1kZWxlZ2F0ZWRcblx0XHQvLyB0b29sdGlwcyB3aWxsIGhhbmRsZSB0aGlzIGluIGRlc3Ryb3kuXG5cdFx0aWYgKCB0YXJnZXRbIDAgXSAhPT0gdGhpcy5lbGVtZW50WyAwIF0gKSB7XG5cdFx0XHRldmVudHMucmVtb3ZlID0gZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHRoaXMuX3JlbW92ZVRvb2x0aXAoIHRoaXMuX2ZpbmQoIHRhcmdldCApLnRvb2x0aXAgKTtcblx0XHRcdH07XG5cdFx0fVxuXG5cdFx0aWYgKCAhZXZlbnQgfHwgZXZlbnQudHlwZSA9PT0gXCJtb3VzZW92ZXJcIiApIHtcblx0XHRcdGV2ZW50cy5tb3VzZWxlYXZlID0gXCJjbG9zZVwiO1xuXHRcdH1cblx0XHRpZiAoICFldmVudCB8fCBldmVudC50eXBlID09PSBcImZvY3VzaW5cIiApIHtcblx0XHRcdGV2ZW50cy5mb2N1c291dCA9IFwiY2xvc2VcIjtcblx0XHR9XG5cdFx0dGhpcy5fb24oIHRydWUsIHRhcmdldCwgZXZlbnRzICk7XG5cdH0sXG5cblx0Y2xvc2U6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHR2YXIgdG9vbHRpcCxcblx0XHRcdHRoYXQgPSB0aGlzLFxuXHRcdFx0dGFyZ2V0ID0gJCggZXZlbnQgPyBldmVudC5jdXJyZW50VGFyZ2V0IDogdGhpcy5lbGVtZW50ICksXG5cdFx0XHR0b29sdGlwRGF0YSA9IHRoaXMuX2ZpbmQoIHRhcmdldCApO1xuXG5cdFx0Ly8gVGhlIHRvb2x0aXAgbWF5IGFscmVhZHkgYmUgY2xvc2VkXG5cdFx0aWYgKCAhdG9vbHRpcERhdGEgKSB7XG5cblx0XHRcdC8vIFdlIHNldCB1aS10b29sdGlwLW9wZW4gaW1tZWRpYXRlbHkgdXBvbiBvcGVuIChpbiBvcGVuKCkpLCBidXQgb25seSBzZXQgdGhlXG5cdFx0XHQvLyBhZGRpdGlvbmFsIGRhdGEgb25jZSB0aGVyZSdzIGFjdHVhbGx5IGNvbnRlbnQgdG8gc2hvdyAoaW4gX29wZW4oKSkuIFNvIGV2ZW4gaWYgdGhlXG5cdFx0XHQvLyB0b29sdGlwIGRvZXNuJ3QgaGF2ZSBmdWxsIGRhdGEsIHdlIGFsd2F5cyByZW1vdmUgdWktdG9vbHRpcC1vcGVuIGluIGNhc2Ugd2UncmUgaW5cblx0XHRcdC8vIHRoZSBwZXJpb2QgYmV0d2VlbiBvcGVuKCkgYW5kIF9vcGVuKCkuXG5cdFx0XHR0YXJnZXQucmVtb3ZlRGF0YSggXCJ1aS10b29sdGlwLW9wZW5cIiApO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHRvb2x0aXAgPSB0b29sdGlwRGF0YS50b29sdGlwO1xuXG5cdFx0Ly8gRGlzYWJsaW5nIGNsb3NlcyB0aGUgdG9vbHRpcCwgc28gd2UgbmVlZCB0byB0cmFjayB3aGVuIHdlJ3JlIGNsb3Npbmdcblx0XHQvLyB0byBhdm9pZCBhbiBpbmZpbml0ZSBsb29wIGluIGNhc2UgdGhlIHRvb2x0aXAgYmVjb21lcyBkaXNhYmxlZCBvbiBjbG9zZVxuXHRcdGlmICggdG9vbHRpcERhdGEuY2xvc2luZyApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBDbGVhciB0aGUgaW50ZXJ2YWwgZm9yIGRlbGF5ZWQgdHJhY2tpbmcgdG9vbHRpcHNcblx0XHRjbGVhckludGVydmFsKCB0aGlzLmRlbGF5ZWRTaG93ICk7XG5cblx0XHQvLyBPbmx5IHNldCB0aXRsZSBpZiB3ZSBoYWQgb25lIGJlZm9yZSAoc2VlIGNvbW1lbnQgaW4gX29wZW4oKSlcblx0XHQvLyBJZiB0aGUgdGl0bGUgYXR0cmlidXRlIGhhcyBjaGFuZ2VkIHNpbmNlIG9wZW4oKSwgZG9uJ3QgcmVzdG9yZVxuXHRcdGlmICggdGFyZ2V0LmRhdGEoIFwidWktdG9vbHRpcC10aXRsZVwiICkgJiYgIXRhcmdldC5hdHRyKCBcInRpdGxlXCIgKSApIHtcblx0XHRcdHRhcmdldC5hdHRyKCBcInRpdGxlXCIsIHRhcmdldC5kYXRhKCBcInVpLXRvb2x0aXAtdGl0bGVcIiApICk7XG5cdFx0fVxuXG5cdFx0dGhpcy5fcmVtb3ZlRGVzY3JpYmVkQnkoIHRhcmdldCApO1xuXG5cdFx0dG9vbHRpcERhdGEuaGlkaW5nID0gdHJ1ZTtcblx0XHR0b29sdGlwLnN0b3AoIHRydWUgKTtcblx0XHR0aGlzLl9oaWRlKCB0b29sdGlwLCB0aGlzLm9wdGlvbnMuaGlkZSwgZnVuY3Rpb24oKSB7XG5cdFx0XHR0aGF0Ll9yZW1vdmVUb29sdGlwKCAkKCB0aGlzICkgKTtcblx0XHR9ICk7XG5cblx0XHR0YXJnZXQucmVtb3ZlRGF0YSggXCJ1aS10b29sdGlwLW9wZW5cIiApO1xuXHRcdHRoaXMuX29mZiggdGFyZ2V0LCBcIm1vdXNlbGVhdmUgZm9jdXNvdXQga2V5dXBcIiApO1xuXG5cdFx0Ly8gUmVtb3ZlICdyZW1vdmUnIGJpbmRpbmcgb25seSBvbiBkZWxlZ2F0ZWQgdGFyZ2V0c1xuXHRcdGlmICggdGFyZ2V0WyAwIF0gIT09IHRoaXMuZWxlbWVudFsgMCBdICkge1xuXHRcdFx0dGhpcy5fb2ZmKCB0YXJnZXQsIFwicmVtb3ZlXCIgKTtcblx0XHR9XG5cdFx0dGhpcy5fb2ZmKCB0aGlzLmRvY3VtZW50LCBcIm1vdXNlbW92ZVwiICk7XG5cblx0XHRpZiAoIGV2ZW50ICYmIGV2ZW50LnR5cGUgPT09IFwibW91c2VsZWF2ZVwiICkge1xuXHRcdFx0JC5lYWNoKCB0aGlzLnBhcmVudHMsIGZ1bmN0aW9uKCBpZCwgcGFyZW50ICkge1xuXHRcdFx0XHQkKCBwYXJlbnQuZWxlbWVudCApLmF0dHIoIFwidGl0bGVcIiwgcGFyZW50LnRpdGxlICk7XG5cdFx0XHRcdGRlbGV0ZSB0aGF0LnBhcmVudHNbIGlkIF07XG5cdFx0XHR9ICk7XG5cdFx0fVxuXG5cdFx0dG9vbHRpcERhdGEuY2xvc2luZyA9IHRydWU7XG5cdFx0dGhpcy5fdHJpZ2dlciggXCJjbG9zZVwiLCBldmVudCwgeyB0b29sdGlwOiB0b29sdGlwIH0gKTtcblx0XHRpZiAoICF0b29sdGlwRGF0YS5oaWRpbmcgKSB7XG5cdFx0XHR0b29sdGlwRGF0YS5jbG9zaW5nID0gZmFsc2U7XG5cdFx0fVxuXHR9LFxuXG5cdF90b29sdGlwOiBmdW5jdGlvbiggZWxlbWVudCApIHtcblx0XHR2YXIgdG9vbHRpcCA9ICQoIFwiPGRpdj5cIiApLmF0dHIoIFwicm9sZVwiLCBcInRvb2x0aXBcIiApLFxuXHRcdFx0Y29udGVudCA9ICQoIFwiPGRpdj5cIiApLmFwcGVuZFRvKCB0b29sdGlwICksXG5cdFx0XHRpZCA9IHRvb2x0aXAudW5pcXVlSWQoKS5hdHRyKCBcImlkXCIgKTtcblxuXHRcdHRoaXMuX2FkZENsYXNzKCBjb250ZW50LCBcInVpLXRvb2x0aXAtY29udGVudFwiICk7XG5cdFx0dGhpcy5fYWRkQ2xhc3MoIHRvb2x0aXAsIFwidWktdG9vbHRpcFwiLCBcInVpLXdpZGdldCB1aS13aWRnZXQtY29udGVudFwiICk7XG5cblx0XHR0b29sdGlwLmFwcGVuZFRvKCB0aGlzLl9hcHBlbmRUbyggZWxlbWVudCApICk7XG5cblx0XHRyZXR1cm4gdGhpcy50b29sdGlwc1sgaWQgXSA9IHtcblx0XHRcdGVsZW1lbnQ6IGVsZW1lbnQsXG5cdFx0XHR0b29sdGlwOiB0b29sdGlwXG5cdFx0fTtcblx0fSxcblxuXHRfZmluZDogZnVuY3Rpb24oIHRhcmdldCApIHtcblx0XHR2YXIgaWQgPSB0YXJnZXQuZGF0YSggXCJ1aS10b29sdGlwLWlkXCIgKTtcblx0XHRyZXR1cm4gaWQgPyB0aGlzLnRvb2x0aXBzWyBpZCBdIDogbnVsbDtcblx0fSxcblxuXHRfcmVtb3ZlVG9vbHRpcDogZnVuY3Rpb24oIHRvb2x0aXAgKSB7XG5cdFx0dG9vbHRpcC5yZW1vdmUoKTtcblx0XHRkZWxldGUgdGhpcy50b29sdGlwc1sgdG9vbHRpcC5hdHRyKCBcImlkXCIgKSBdO1xuXHR9LFxuXG5cdF9hcHBlbmRUbzogZnVuY3Rpb24oIHRhcmdldCApIHtcblx0XHR2YXIgZWxlbWVudCA9IHRhcmdldC5jbG9zZXN0KCBcIi51aS1mcm9udCwgZGlhbG9nXCIgKTtcblxuXHRcdGlmICggIWVsZW1lbnQubGVuZ3RoICkge1xuXHRcdFx0ZWxlbWVudCA9IHRoaXMuZG9jdW1lbnRbIDAgXS5ib2R5O1xuXHRcdH1cblxuXHRcdHJldHVybiBlbGVtZW50O1xuXHR9LFxuXG5cdF9kZXN0cm95OiBmdW5jdGlvbigpIHtcblx0XHR2YXIgdGhhdCA9IHRoaXM7XG5cblx0XHQvLyBDbG9zZSBvcGVuIHRvb2x0aXBzXG5cdFx0JC5lYWNoKCB0aGlzLnRvb2x0aXBzLCBmdW5jdGlvbiggaWQsIHRvb2x0aXBEYXRhICkge1xuXG5cdFx0XHQvLyBEZWxlZ2F0ZSB0byBjbG9zZSBtZXRob2QgdG8gaGFuZGxlIGNvbW1vbiBjbGVhbnVwXG5cdFx0XHR2YXIgZXZlbnQgPSAkLkV2ZW50KCBcImJsdXJcIiApLFxuXHRcdFx0XHRlbGVtZW50ID0gdG9vbHRpcERhdGEuZWxlbWVudDtcblx0XHRcdGV2ZW50LnRhcmdldCA9IGV2ZW50LmN1cnJlbnRUYXJnZXQgPSBlbGVtZW50WyAwIF07XG5cdFx0XHR0aGF0LmNsb3NlKCBldmVudCwgdHJ1ZSApO1xuXG5cdFx0XHQvLyBSZW1vdmUgaW1tZWRpYXRlbHk7IGRlc3Ryb3lpbmcgYW4gb3BlbiB0b29sdGlwIGRvZXNuJ3QgdXNlIHRoZVxuXHRcdFx0Ly8gaGlkZSBhbmltYXRpb25cblx0XHRcdCQoIFwiI1wiICsgaWQgKS5yZW1vdmUoKTtcblxuXHRcdFx0Ly8gUmVzdG9yZSB0aGUgdGl0bGVcblx0XHRcdGlmICggZWxlbWVudC5kYXRhKCBcInVpLXRvb2x0aXAtdGl0bGVcIiApICkge1xuXG5cdFx0XHRcdC8vIElmIHRoZSB0aXRsZSBhdHRyaWJ1dGUgaGFzIGNoYW5nZWQgc2luY2Ugb3BlbigpLCBkb24ndCByZXN0b3JlXG5cdFx0XHRcdGlmICggIWVsZW1lbnQuYXR0ciggXCJ0aXRsZVwiICkgKSB7XG5cdFx0XHRcdFx0ZWxlbWVudC5hdHRyKCBcInRpdGxlXCIsIGVsZW1lbnQuZGF0YSggXCJ1aS10b29sdGlwLXRpdGxlXCIgKSApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGVsZW1lbnQucmVtb3ZlRGF0YSggXCJ1aS10b29sdGlwLXRpdGxlXCIgKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cdFx0dGhpcy5saXZlUmVnaW9uLnJlbW92ZSgpO1xuXHR9XG59ICk7XG5cbi8vIERFUFJFQ0FURURcbi8vIFRPRE86IFN3aXRjaCByZXR1cm4gYmFjayB0byB3aWRnZXQgZGVjbGFyYXRpb24gYXQgdG9wIG9mIGZpbGUgd2hlbiB0aGlzIGlzIHJlbW92ZWRcbmlmICggJC51aUJhY2tDb21wYXQgIT09IGZhbHNlICkge1xuXG5cdC8vIEJhY2tjb21wYXQgZm9yIHRvb2x0aXBDbGFzcyBvcHRpb25cblx0JC53aWRnZXQoIFwidWkudG9vbHRpcFwiLCAkLnVpLnRvb2x0aXAsIHtcblx0XHRvcHRpb25zOiB7XG5cdFx0XHR0b29sdGlwQ2xhc3M6IG51bGxcblx0XHR9LFxuXHRcdF90b29sdGlwOiBmdW5jdGlvbigpIHtcblx0XHRcdHZhciB0b29sdGlwRGF0YSA9IHRoaXMuX3N1cGVyQXBwbHkoIGFyZ3VtZW50cyApO1xuXHRcdFx0aWYgKCB0aGlzLm9wdGlvbnMudG9vbHRpcENsYXNzICkge1xuXHRcdFx0XHR0b29sdGlwRGF0YS50b29sdGlwLmFkZENsYXNzKCB0aGlzLm9wdGlvbnMudG9vbHRpcENsYXNzICk7XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gdG9vbHRpcERhdGE7XG5cdFx0fVxuXHR9ICk7XG59XG5cbnZhciB3aWRnZXRzVG9vbHRpcCA9ICQudWkudG9vbHRpcDtcblxuXG5cblxufSkpOyIsIihmdW5jdGlvbiBkZWZpbmVNdXN0YWNoZShnbG9iYWwsZmFjdG9yeSl7aWYodHlwZW9mIGV4cG9ydHM9PT1cIm9iamVjdFwiJiZleHBvcnRzJiZ0eXBlb2YgZXhwb3J0cy5ub2RlTmFtZSE9PVwic3RyaW5nXCIpe2ZhY3RvcnkoZXhwb3J0cyl9ZWxzZSBpZih0eXBlb2YgZGVmaW5lPT09XCJmdW5jdGlvblwiJiZkZWZpbmUuYW1kKXtkZWZpbmUoW1wiZXhwb3J0c1wiXSxmYWN0b3J5KX1lbHNle2dsb2JhbC5NdXN0YWNoZT17fTtmYWN0b3J5KGdsb2JhbC5NdXN0YWNoZSl9fSkodGhpcyxmdW5jdGlvbiBtdXN0YWNoZUZhY3RvcnkobXVzdGFjaGUpe3ZhciBvYmplY3RUb1N0cmluZz1PYmplY3QucHJvdG90eXBlLnRvU3RyaW5nO3ZhciBpc0FycmF5PUFycmF5LmlzQXJyYXl8fGZ1bmN0aW9uIGlzQXJyYXlQb2x5ZmlsbChvYmplY3Qpe3JldHVybiBvYmplY3RUb1N0cmluZy5jYWxsKG9iamVjdCk9PT1cIltvYmplY3QgQXJyYXldXCJ9O2Z1bmN0aW9uIGlzRnVuY3Rpb24ob2JqZWN0KXtyZXR1cm4gdHlwZW9mIG9iamVjdD09PVwiZnVuY3Rpb25cIn1mdW5jdGlvbiB0eXBlU3RyKG9iail7cmV0dXJuIGlzQXJyYXkob2JqKT9cImFycmF5XCI6dHlwZW9mIG9ian1mdW5jdGlvbiBlc2NhcGVSZWdFeHAoc3RyaW5nKXtyZXR1cm4gc3RyaW5nLnJlcGxhY2UoL1tcXC1cXFtcXF17fSgpKis/LixcXFxcXFxeJHwjXFxzXS9nLFwiXFxcXCQmXCIpfWZ1bmN0aW9uIGhhc1Byb3BlcnR5KG9iaixwcm9wTmFtZSl7cmV0dXJuIG9iaiE9bnVsbCYmdHlwZW9mIG9iaj09PVwib2JqZWN0XCImJnByb3BOYW1lIGluIG9ian12YXIgcmVnRXhwVGVzdD1SZWdFeHAucHJvdG90eXBlLnRlc3Q7ZnVuY3Rpb24gdGVzdFJlZ0V4cChyZSxzdHJpbmcpe3JldHVybiByZWdFeHBUZXN0LmNhbGwocmUsc3RyaW5nKX12YXIgbm9uU3BhY2VSZT0vXFxTLztmdW5jdGlvbiBpc1doaXRlc3BhY2Uoc3RyaW5nKXtyZXR1cm4hdGVzdFJlZ0V4cChub25TcGFjZVJlLHN0cmluZyl9dmFyIGVudGl0eU1hcD17XCImXCI6XCImYW1wO1wiLFwiPFwiOlwiJmx0O1wiLFwiPlwiOlwiJmd0O1wiLCdcIic6XCImcXVvdDtcIixcIidcIjpcIiYjMzk7XCIsXCIvXCI6XCImI3gyRjtcIixcImBcIjpcIiYjeDYwO1wiLFwiPVwiOlwiJiN4M0Q7XCJ9O2Z1bmN0aW9uIGVzY2FwZUh0bWwoc3RyaW5nKXtyZXR1cm4gU3RyaW5nKHN0cmluZykucmVwbGFjZSgvWyY8PlwiJ2A9XFwvXS9nLGZ1bmN0aW9uIGZyb21FbnRpdHlNYXAocyl7cmV0dXJuIGVudGl0eU1hcFtzXX0pfXZhciB3aGl0ZVJlPS9cXHMqLzt2YXIgc3BhY2VSZT0vXFxzKy87dmFyIGVxdWFsc1JlPS9cXHMqPS87dmFyIGN1cmx5UmU9L1xccypcXH0vO3ZhciB0YWdSZT0vI3xcXF58XFwvfD58XFx7fCZ8PXwhLztmdW5jdGlvbiBwYXJzZVRlbXBsYXRlKHRlbXBsYXRlLHRhZ3Mpe2lmKCF0ZW1wbGF0ZSlyZXR1cm5bXTt2YXIgc2VjdGlvbnM9W107dmFyIHRva2Vucz1bXTt2YXIgc3BhY2VzPVtdO3ZhciBoYXNUYWc9ZmFsc2U7dmFyIG5vblNwYWNlPWZhbHNlO2Z1bmN0aW9uIHN0cmlwU3BhY2UoKXtpZihoYXNUYWcmJiFub25TcGFjZSl7d2hpbGUoc3BhY2VzLmxlbmd0aClkZWxldGUgdG9rZW5zW3NwYWNlcy5wb3AoKV19ZWxzZXtzcGFjZXM9W119aGFzVGFnPWZhbHNlO25vblNwYWNlPWZhbHNlfXZhciBvcGVuaW5nVGFnUmUsY2xvc2luZ1RhZ1JlLGNsb3NpbmdDdXJseVJlO2Z1bmN0aW9uIGNvbXBpbGVUYWdzKHRhZ3NUb0NvbXBpbGUpe2lmKHR5cGVvZiB0YWdzVG9Db21waWxlPT09XCJzdHJpbmdcIil0YWdzVG9Db21waWxlPXRhZ3NUb0NvbXBpbGUuc3BsaXQoc3BhY2VSZSwyKTtpZighaXNBcnJheSh0YWdzVG9Db21waWxlKXx8dGFnc1RvQ29tcGlsZS5sZW5ndGghPT0yKXRocm93IG5ldyBFcnJvcihcIkludmFsaWQgdGFnczogXCIrdGFnc1RvQ29tcGlsZSk7b3BlbmluZ1RhZ1JlPW5ldyBSZWdFeHAoZXNjYXBlUmVnRXhwKHRhZ3NUb0NvbXBpbGVbMF0pK1wiXFxcXHMqXCIpO2Nsb3NpbmdUYWdSZT1uZXcgUmVnRXhwKFwiXFxcXHMqXCIrZXNjYXBlUmVnRXhwKHRhZ3NUb0NvbXBpbGVbMV0pKTtjbG9zaW5nQ3VybHlSZT1uZXcgUmVnRXhwKFwiXFxcXHMqXCIrZXNjYXBlUmVnRXhwKFwifVwiK3RhZ3NUb0NvbXBpbGVbMV0pKX1jb21waWxlVGFncyh0YWdzfHxtdXN0YWNoZS50YWdzKTt2YXIgc2Nhbm5lcj1uZXcgU2Nhbm5lcih0ZW1wbGF0ZSk7dmFyIHN0YXJ0LHR5cGUsdmFsdWUsY2hyLHRva2VuLG9wZW5TZWN0aW9uO3doaWxlKCFzY2FubmVyLmVvcygpKXtzdGFydD1zY2FubmVyLnBvczt2YWx1ZT1zY2FubmVyLnNjYW5VbnRpbChvcGVuaW5nVGFnUmUpO2lmKHZhbHVlKXtmb3IodmFyIGk9MCx2YWx1ZUxlbmd0aD12YWx1ZS5sZW5ndGg7aTx2YWx1ZUxlbmd0aDsrK2kpe2Nocj12YWx1ZS5jaGFyQXQoaSk7aWYoaXNXaGl0ZXNwYWNlKGNocikpe3NwYWNlcy5wdXNoKHRva2Vucy5sZW5ndGgpfWVsc2V7bm9uU3BhY2U9dHJ1ZX10b2tlbnMucHVzaChbXCJ0ZXh0XCIsY2hyLHN0YXJ0LHN0YXJ0KzFdKTtzdGFydCs9MTtpZihjaHI9PT1cIlxcblwiKXN0cmlwU3BhY2UoKX19aWYoIXNjYW5uZXIuc2NhbihvcGVuaW5nVGFnUmUpKWJyZWFrO2hhc1RhZz10cnVlO3R5cGU9c2Nhbm5lci5zY2FuKHRhZ1JlKXx8XCJuYW1lXCI7c2Nhbm5lci5zY2FuKHdoaXRlUmUpO2lmKHR5cGU9PT1cIj1cIil7dmFsdWU9c2Nhbm5lci5zY2FuVW50aWwoZXF1YWxzUmUpO3NjYW5uZXIuc2NhbihlcXVhbHNSZSk7c2Nhbm5lci5zY2FuVW50aWwoY2xvc2luZ1RhZ1JlKX1lbHNlIGlmKHR5cGU9PT1cIntcIil7dmFsdWU9c2Nhbm5lci5zY2FuVW50aWwoY2xvc2luZ0N1cmx5UmUpO3NjYW5uZXIuc2NhbihjdXJseVJlKTtzY2FubmVyLnNjYW5VbnRpbChjbG9zaW5nVGFnUmUpO3R5cGU9XCImXCJ9ZWxzZXt2YWx1ZT1zY2FubmVyLnNjYW5VbnRpbChjbG9zaW5nVGFnUmUpfWlmKCFzY2FubmVyLnNjYW4oY2xvc2luZ1RhZ1JlKSl0aHJvdyBuZXcgRXJyb3IoXCJVbmNsb3NlZCB0YWcgYXQgXCIrc2Nhbm5lci5wb3MpO3Rva2VuPVt0eXBlLHZhbHVlLHN0YXJ0LHNjYW5uZXIucG9zXTt0b2tlbnMucHVzaCh0b2tlbik7aWYodHlwZT09PVwiI1wifHx0eXBlPT09XCJeXCIpe3NlY3Rpb25zLnB1c2godG9rZW4pfWVsc2UgaWYodHlwZT09PVwiL1wiKXtvcGVuU2VjdGlvbj1zZWN0aW9ucy5wb3AoKTtpZighb3BlblNlY3Rpb24pdGhyb3cgbmV3IEVycm9yKCdVbm9wZW5lZCBzZWN0aW9uIFwiJyt2YWx1ZSsnXCIgYXQgJytzdGFydCk7aWYob3BlblNlY3Rpb25bMV0hPT12YWx1ZSl0aHJvdyBuZXcgRXJyb3IoJ1VuY2xvc2VkIHNlY3Rpb24gXCInK29wZW5TZWN0aW9uWzFdKydcIiBhdCAnK3N0YXJ0KX1lbHNlIGlmKHR5cGU9PT1cIm5hbWVcInx8dHlwZT09PVwie1wifHx0eXBlPT09XCImXCIpe25vblNwYWNlPXRydWV9ZWxzZSBpZih0eXBlPT09XCI9XCIpe2NvbXBpbGVUYWdzKHZhbHVlKX19b3BlblNlY3Rpb249c2VjdGlvbnMucG9wKCk7aWYob3BlblNlY3Rpb24pdGhyb3cgbmV3IEVycm9yKCdVbmNsb3NlZCBzZWN0aW9uIFwiJytvcGVuU2VjdGlvblsxXSsnXCIgYXQgJytzY2FubmVyLnBvcyk7cmV0dXJuIG5lc3RUb2tlbnMoc3F1YXNoVG9rZW5zKHRva2VucykpfWZ1bmN0aW9uIHNxdWFzaFRva2Vucyh0b2tlbnMpe3ZhciBzcXVhc2hlZFRva2Vucz1bXTt2YXIgdG9rZW4sbGFzdFRva2VuO2Zvcih2YXIgaT0wLG51bVRva2Vucz10b2tlbnMubGVuZ3RoO2k8bnVtVG9rZW5zOysraSl7dG9rZW49dG9rZW5zW2ldO2lmKHRva2VuKXtpZih0b2tlblswXT09PVwidGV4dFwiJiZsYXN0VG9rZW4mJmxhc3RUb2tlblswXT09PVwidGV4dFwiKXtsYXN0VG9rZW5bMV0rPXRva2VuWzFdO2xhc3RUb2tlblszXT10b2tlblszXX1lbHNle3NxdWFzaGVkVG9rZW5zLnB1c2godG9rZW4pO2xhc3RUb2tlbj10b2tlbn19fXJldHVybiBzcXVhc2hlZFRva2Vuc31mdW5jdGlvbiBuZXN0VG9rZW5zKHRva2Vucyl7dmFyIG5lc3RlZFRva2Vucz1bXTt2YXIgY29sbGVjdG9yPW5lc3RlZFRva2Vuczt2YXIgc2VjdGlvbnM9W107dmFyIHRva2VuLHNlY3Rpb247Zm9yKHZhciBpPTAsbnVtVG9rZW5zPXRva2Vucy5sZW5ndGg7aTxudW1Ub2tlbnM7KytpKXt0b2tlbj10b2tlbnNbaV07c3dpdGNoKHRva2VuWzBdKXtjYXNlXCIjXCI6Y2FzZVwiXlwiOmNvbGxlY3Rvci5wdXNoKHRva2VuKTtzZWN0aW9ucy5wdXNoKHRva2VuKTtjb2xsZWN0b3I9dG9rZW5bNF09W107YnJlYWs7Y2FzZVwiL1wiOnNlY3Rpb249c2VjdGlvbnMucG9wKCk7c2VjdGlvbls1XT10b2tlblsyXTtjb2xsZWN0b3I9c2VjdGlvbnMubGVuZ3RoPjA/c2VjdGlvbnNbc2VjdGlvbnMubGVuZ3RoLTFdWzRdOm5lc3RlZFRva2VuczticmVhaztkZWZhdWx0OmNvbGxlY3Rvci5wdXNoKHRva2VuKX19cmV0dXJuIG5lc3RlZFRva2Vuc31mdW5jdGlvbiBTY2FubmVyKHN0cmluZyl7dGhpcy5zdHJpbmc9c3RyaW5nO3RoaXMudGFpbD1zdHJpbmc7dGhpcy5wb3M9MH1TY2FubmVyLnByb3RvdHlwZS5lb3M9ZnVuY3Rpb24gZW9zKCl7cmV0dXJuIHRoaXMudGFpbD09PVwiXCJ9O1NjYW5uZXIucHJvdG90eXBlLnNjYW49ZnVuY3Rpb24gc2NhbihyZSl7dmFyIG1hdGNoPXRoaXMudGFpbC5tYXRjaChyZSk7aWYoIW1hdGNofHxtYXRjaC5pbmRleCE9PTApcmV0dXJuXCJcIjt2YXIgc3RyaW5nPW1hdGNoWzBdO3RoaXMudGFpbD10aGlzLnRhaWwuc3Vic3RyaW5nKHN0cmluZy5sZW5ndGgpO3RoaXMucG9zKz1zdHJpbmcubGVuZ3RoO3JldHVybiBzdHJpbmd9O1NjYW5uZXIucHJvdG90eXBlLnNjYW5VbnRpbD1mdW5jdGlvbiBzY2FuVW50aWwocmUpe3ZhciBpbmRleD10aGlzLnRhaWwuc2VhcmNoKHJlKSxtYXRjaDtzd2l0Y2goaW5kZXgpe2Nhc2UtMTptYXRjaD10aGlzLnRhaWw7dGhpcy50YWlsPVwiXCI7YnJlYWs7Y2FzZSAwOm1hdGNoPVwiXCI7YnJlYWs7ZGVmYXVsdDptYXRjaD10aGlzLnRhaWwuc3Vic3RyaW5nKDAsaW5kZXgpO3RoaXMudGFpbD10aGlzLnRhaWwuc3Vic3RyaW5nKGluZGV4KX10aGlzLnBvcys9bWF0Y2gubGVuZ3RoO3JldHVybiBtYXRjaH07ZnVuY3Rpb24gQ29udGV4dCh2aWV3LHBhcmVudENvbnRleHQpe3RoaXMudmlldz12aWV3O3RoaXMuY2FjaGU9e1wiLlwiOnRoaXMudmlld307dGhpcy5wYXJlbnQ9cGFyZW50Q29udGV4dH1Db250ZXh0LnByb3RvdHlwZS5wdXNoPWZ1bmN0aW9uIHB1c2godmlldyl7cmV0dXJuIG5ldyBDb250ZXh0KHZpZXcsdGhpcyl9O0NvbnRleHQucHJvdG90eXBlLmxvb2t1cD1mdW5jdGlvbiBsb29rdXAobmFtZSl7dmFyIGNhY2hlPXRoaXMuY2FjaGU7dmFyIHZhbHVlO2lmKGNhY2hlLmhhc093blByb3BlcnR5KG5hbWUpKXt2YWx1ZT1jYWNoZVtuYW1lXX1lbHNle3ZhciBjb250ZXh0PXRoaXMsbmFtZXMsaW5kZXgsbG9va3VwSGl0PWZhbHNlO3doaWxlKGNvbnRleHQpe2lmKG5hbWUuaW5kZXhPZihcIi5cIik+MCl7dmFsdWU9Y29udGV4dC52aWV3O25hbWVzPW5hbWUuc3BsaXQoXCIuXCIpO2luZGV4PTA7d2hpbGUodmFsdWUhPW51bGwmJmluZGV4PG5hbWVzLmxlbmd0aCl7aWYoaW5kZXg9PT1uYW1lcy5sZW5ndGgtMSlsb29rdXBIaXQ9aGFzUHJvcGVydHkodmFsdWUsbmFtZXNbaW5kZXhdKTt2YWx1ZT12YWx1ZVtuYW1lc1tpbmRleCsrXV19fWVsc2V7dmFsdWU9Y29udGV4dC52aWV3W25hbWVdO2xvb2t1cEhpdD1oYXNQcm9wZXJ0eShjb250ZXh0LnZpZXcsbmFtZSl9aWYobG9va3VwSGl0KWJyZWFrO2NvbnRleHQ9Y29udGV4dC5wYXJlbnR9Y2FjaGVbbmFtZV09dmFsdWV9aWYoaXNGdW5jdGlvbih2YWx1ZSkpdmFsdWU9dmFsdWUuY2FsbCh0aGlzLnZpZXcpO3JldHVybiB2YWx1ZX07ZnVuY3Rpb24gV3JpdGVyKCl7dGhpcy5jYWNoZT17fX1Xcml0ZXIucHJvdG90eXBlLmNsZWFyQ2FjaGU9ZnVuY3Rpb24gY2xlYXJDYWNoZSgpe3RoaXMuY2FjaGU9e319O1dyaXRlci5wcm90b3R5cGUucGFyc2U9ZnVuY3Rpb24gcGFyc2UodGVtcGxhdGUsdGFncyl7dmFyIGNhY2hlPXRoaXMuY2FjaGU7dmFyIHRva2Vucz1jYWNoZVt0ZW1wbGF0ZV07aWYodG9rZW5zPT1udWxsKXRva2Vucz1jYWNoZVt0ZW1wbGF0ZV09cGFyc2VUZW1wbGF0ZSh0ZW1wbGF0ZSx0YWdzKTtyZXR1cm4gdG9rZW5zfTtXcml0ZXIucHJvdG90eXBlLnJlbmRlcj1mdW5jdGlvbiByZW5kZXIodGVtcGxhdGUsdmlldyxwYXJ0aWFscyl7dmFyIHRva2Vucz10aGlzLnBhcnNlKHRlbXBsYXRlKTt2YXIgY29udGV4dD12aWV3IGluc3RhbmNlb2YgQ29udGV4dD92aWV3Om5ldyBDb250ZXh0KHZpZXcpO3JldHVybiB0aGlzLnJlbmRlclRva2Vucyh0b2tlbnMsY29udGV4dCxwYXJ0aWFscyx0ZW1wbGF0ZSl9O1dyaXRlci5wcm90b3R5cGUucmVuZGVyVG9rZW5zPWZ1bmN0aW9uIHJlbmRlclRva2Vucyh0b2tlbnMsY29udGV4dCxwYXJ0aWFscyxvcmlnaW5hbFRlbXBsYXRlKXt2YXIgYnVmZmVyPVwiXCI7dmFyIHRva2VuLHN5bWJvbCx2YWx1ZTtmb3IodmFyIGk9MCxudW1Ub2tlbnM9dG9rZW5zLmxlbmd0aDtpPG51bVRva2VuczsrK2kpe3ZhbHVlPXVuZGVmaW5lZDt0b2tlbj10b2tlbnNbaV07c3ltYm9sPXRva2VuWzBdO2lmKHN5bWJvbD09PVwiI1wiKXZhbHVlPXRoaXMucmVuZGVyU2VjdGlvbih0b2tlbixjb250ZXh0LHBhcnRpYWxzLG9yaWdpbmFsVGVtcGxhdGUpO2Vsc2UgaWYoc3ltYm9sPT09XCJeXCIpdmFsdWU9dGhpcy5yZW5kZXJJbnZlcnRlZCh0b2tlbixjb250ZXh0LHBhcnRpYWxzLG9yaWdpbmFsVGVtcGxhdGUpO2Vsc2UgaWYoc3ltYm9sPT09XCI+XCIpdmFsdWU9dGhpcy5yZW5kZXJQYXJ0aWFsKHRva2VuLGNvbnRleHQscGFydGlhbHMsb3JpZ2luYWxUZW1wbGF0ZSk7ZWxzZSBpZihzeW1ib2w9PT1cIiZcIil2YWx1ZT10aGlzLnVuZXNjYXBlZFZhbHVlKHRva2VuLGNvbnRleHQpO2Vsc2UgaWYoc3ltYm9sPT09XCJuYW1lXCIpdmFsdWU9dGhpcy5lc2NhcGVkVmFsdWUodG9rZW4sY29udGV4dCk7ZWxzZSBpZihzeW1ib2w9PT1cInRleHRcIil2YWx1ZT10aGlzLnJhd1ZhbHVlKHRva2VuKTtpZih2YWx1ZSE9PXVuZGVmaW5lZClidWZmZXIrPXZhbHVlfXJldHVybiBidWZmZXJ9O1dyaXRlci5wcm90b3R5cGUucmVuZGVyU2VjdGlvbj1mdW5jdGlvbiByZW5kZXJTZWN0aW9uKHRva2VuLGNvbnRleHQscGFydGlhbHMsb3JpZ2luYWxUZW1wbGF0ZSl7dmFyIHNlbGY9dGhpczt2YXIgYnVmZmVyPVwiXCI7dmFyIHZhbHVlPWNvbnRleHQubG9va3VwKHRva2VuWzFdKTtmdW5jdGlvbiBzdWJSZW5kZXIodGVtcGxhdGUpe3JldHVybiBzZWxmLnJlbmRlcih0ZW1wbGF0ZSxjb250ZXh0LHBhcnRpYWxzKX1pZighdmFsdWUpcmV0dXJuO2lmKGlzQXJyYXkodmFsdWUpKXtmb3IodmFyIGo9MCx2YWx1ZUxlbmd0aD12YWx1ZS5sZW5ndGg7ajx2YWx1ZUxlbmd0aDsrK2ope2J1ZmZlcis9dGhpcy5yZW5kZXJUb2tlbnModG9rZW5bNF0sY29udGV4dC5wdXNoKHZhbHVlW2pdKSxwYXJ0aWFscyxvcmlnaW5hbFRlbXBsYXRlKX19ZWxzZSBpZih0eXBlb2YgdmFsdWU9PT1cIm9iamVjdFwifHx0eXBlb2YgdmFsdWU9PT1cInN0cmluZ1wifHx0eXBlb2YgdmFsdWU9PT1cIm51bWJlclwiKXtidWZmZXIrPXRoaXMucmVuZGVyVG9rZW5zKHRva2VuWzRdLGNvbnRleHQucHVzaCh2YWx1ZSkscGFydGlhbHMsb3JpZ2luYWxUZW1wbGF0ZSl9ZWxzZSBpZihpc0Z1bmN0aW9uKHZhbHVlKSl7aWYodHlwZW9mIG9yaWdpbmFsVGVtcGxhdGUhPT1cInN0cmluZ1wiKXRocm93IG5ldyBFcnJvcihcIkNhbm5vdCB1c2UgaGlnaGVyLW9yZGVyIHNlY3Rpb25zIHdpdGhvdXQgdGhlIG9yaWdpbmFsIHRlbXBsYXRlXCIpO3ZhbHVlPXZhbHVlLmNhbGwoY29udGV4dC52aWV3LG9yaWdpbmFsVGVtcGxhdGUuc2xpY2UodG9rZW5bM10sdG9rZW5bNV0pLHN1YlJlbmRlcik7aWYodmFsdWUhPW51bGwpYnVmZmVyKz12YWx1ZX1lbHNle2J1ZmZlcis9dGhpcy5yZW5kZXJUb2tlbnModG9rZW5bNF0sY29udGV4dCxwYXJ0aWFscyxvcmlnaW5hbFRlbXBsYXRlKX1yZXR1cm4gYnVmZmVyfTtXcml0ZXIucHJvdG90eXBlLnJlbmRlckludmVydGVkPWZ1bmN0aW9uIHJlbmRlckludmVydGVkKHRva2VuLGNvbnRleHQscGFydGlhbHMsb3JpZ2luYWxUZW1wbGF0ZSl7dmFyIHZhbHVlPWNvbnRleHQubG9va3VwKHRva2VuWzFdKTtpZighdmFsdWV8fGlzQXJyYXkodmFsdWUpJiZ2YWx1ZS5sZW5ndGg9PT0wKXJldHVybiB0aGlzLnJlbmRlclRva2Vucyh0b2tlbls0XSxjb250ZXh0LHBhcnRpYWxzLG9yaWdpbmFsVGVtcGxhdGUpfTtXcml0ZXIucHJvdG90eXBlLnJlbmRlclBhcnRpYWw9ZnVuY3Rpb24gcmVuZGVyUGFydGlhbCh0b2tlbixjb250ZXh0LHBhcnRpYWxzKXtpZighcGFydGlhbHMpcmV0dXJuO3ZhciB2YWx1ZT1pc0Z1bmN0aW9uKHBhcnRpYWxzKT9wYXJ0aWFscyh0b2tlblsxXSk6cGFydGlhbHNbdG9rZW5bMV1dO2lmKHZhbHVlIT1udWxsKXJldHVybiB0aGlzLnJlbmRlclRva2Vucyh0aGlzLnBhcnNlKHZhbHVlKSxjb250ZXh0LHBhcnRpYWxzLHZhbHVlKX07V3JpdGVyLnByb3RvdHlwZS51bmVzY2FwZWRWYWx1ZT1mdW5jdGlvbiB1bmVzY2FwZWRWYWx1ZSh0b2tlbixjb250ZXh0KXt2YXIgdmFsdWU9Y29udGV4dC5sb29rdXAodG9rZW5bMV0pO2lmKHZhbHVlIT1udWxsKXJldHVybiB2YWx1ZX07V3JpdGVyLnByb3RvdHlwZS5lc2NhcGVkVmFsdWU9ZnVuY3Rpb24gZXNjYXBlZFZhbHVlKHRva2VuLGNvbnRleHQpe3ZhciB2YWx1ZT1jb250ZXh0Lmxvb2t1cCh0b2tlblsxXSk7aWYodmFsdWUhPW51bGwpcmV0dXJuIG11c3RhY2hlLmVzY2FwZSh2YWx1ZSl9O1dyaXRlci5wcm90b3R5cGUucmF3VmFsdWU9ZnVuY3Rpb24gcmF3VmFsdWUodG9rZW4pe3JldHVybiB0b2tlblsxXX07bXVzdGFjaGUubmFtZT1cIm11c3RhY2hlLmpzXCI7bXVzdGFjaGUudmVyc2lvbj1cIjIuMy4yXCI7bXVzdGFjaGUudGFncz1bXCJ7e1wiLFwifX1cIl07dmFyIGRlZmF1bHRXcml0ZXI9bmV3IFdyaXRlcjttdXN0YWNoZS5jbGVhckNhY2hlPWZ1bmN0aW9uIGNsZWFyQ2FjaGUoKXtyZXR1cm4gZGVmYXVsdFdyaXRlci5jbGVhckNhY2hlKCl9O211c3RhY2hlLnBhcnNlPWZ1bmN0aW9uIHBhcnNlKHRlbXBsYXRlLHRhZ3Mpe3JldHVybiBkZWZhdWx0V3JpdGVyLnBhcnNlKHRlbXBsYXRlLHRhZ3MpfTttdXN0YWNoZS5yZW5kZXI9ZnVuY3Rpb24gcmVuZGVyKHRlbXBsYXRlLHZpZXcscGFydGlhbHMpe2lmKHR5cGVvZiB0ZW1wbGF0ZSE9PVwic3RyaW5nXCIpe3Rocm93IG5ldyBUeXBlRXJyb3IoJ0ludmFsaWQgdGVtcGxhdGUhIFRlbXBsYXRlIHNob3VsZCBiZSBhIFwic3RyaW5nXCIgJysnYnV0IFwiJyt0eXBlU3RyKHRlbXBsYXRlKSsnXCIgd2FzIGdpdmVuIGFzIHRoZSBmaXJzdCAnK1wiYXJndW1lbnQgZm9yIG11c3RhY2hlI3JlbmRlcih0ZW1wbGF0ZSwgdmlldywgcGFydGlhbHMpXCIpfXJldHVybiBkZWZhdWx0V3JpdGVyLnJlbmRlcih0ZW1wbGF0ZSx2aWV3LHBhcnRpYWxzKX07bXVzdGFjaGUudG9faHRtbD1mdW5jdGlvbiB0b19odG1sKHRlbXBsYXRlLHZpZXcscGFydGlhbHMsc2VuZCl7dmFyIHJlc3VsdD1tdXN0YWNoZS5yZW5kZXIodGVtcGxhdGUsdmlldyxwYXJ0aWFscyk7aWYoaXNGdW5jdGlvbihzZW5kKSl7c2VuZChyZXN1bHQpfWVsc2V7cmV0dXJuIHJlc3VsdH19O211c3RhY2hlLmVzY2FwZT1lc2NhcGVIdG1sO211c3RhY2hlLlNjYW5uZXI9U2Nhbm5lcjttdXN0YWNoZS5Db250ZXh0PUNvbnRleHQ7bXVzdGFjaGUuV3JpdGVyPVdyaXRlcjtyZXR1cm4gbXVzdGFjaGV9KTtcbiJdfQ==

Youez - 2016 - github.com/yon3zu
LinuXploit