AutorÃa | Ultima modificación | Ver Log |
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/*! JsRender v1.0.7: http://jsviews.com/#jsrender */
/*! **VERSION FOR WEB** (For NODE.JS see http://jsviews.com/download/jsrender-node.js) */
/*
* Best-of-breed templating in browser or on Node.js.
* Does not require jQuery, or HTML DOM
* Integrates with JsViews (http://jsviews.com/#jsviews)
*
* Copyright 2020, Boris Moore
* Released under the MIT License.
*/
//jshint -W018, -W041, -W120
(function(factory, global) {
// global var is the this object, which is window when running in the usual browser environment
var $ = global.jQuery;
if (typeof exports === "object") { // CommonJS e.g. Browserify
module.exports = $
? factory(global, $)
: function($) { // If no global jQuery, take optional jQuery passed as parameter: require('jsrender')(jQuery)
if ($ && !$.fn) {
throw "Provide jQuery or null";
}
return factory(global, $);
};
} else if (typeof define === "function" && define.amd) { // AMD script loader, e.g. RequireJS
define(function() {
return factory(global);
});
} else { // Browser using plain <script> tag
factory(global, false);
}
} (
// factory (for jsrender.js)
function(global, $) {
"use strict";
//========================== Top-level vars ==========================
// global var is the this object, which is window when running in the usual browser environment
var setGlobals = $ === false; // Only set globals if script block in browser (not AMD and not CommonJS)
$ = $ && $.fn ? $ : global.jQuery; // $ is jQuery passed in by CommonJS loader (Browserify), or global jQuery.
var versionNumber = "v1.0.7",
jsvStoreName, rTag, rTmplString, topView, $views, $expando,
_ocp = "_ocp", // Observable contextual parameter
$isFunction, $isArray, $templates, $converters, $helpers, $tags, $sub, $subSettings, $subSettingsAdvanced, $viewsSettings,
delimOpenChar0, delimOpenChar1, delimCloseChar0, delimCloseChar1, linkChar, setting, baseOnError,
isRenderCall,
rNewLine = /[ \t]*(\r\n|\n|\r)/g,
rUnescapeQuotes = /\\(['"\\])/g, // Unescape quotes and trim
rEscapeQuotes = /['"\\]/g, // Escape quotes and \ character
rBuildHash = /(?:\x08|^)(onerror:)?(?:(~?)(([\w$.]+):)?([^\x08]+))\x08(,)?([^\x08]+)/gi,
rTestElseIf = /^if\s/,
rFirstElem = /<(\w+)[>\s]/,
rAttrEncode = /[\x00`><"'&=]/g, // Includes > encoding since rConvertMarkers in JsViews does not skip > characters in attribute strings
rIsHtml = /[\x00`><\"'&=]/,
rHasHandlers = /^on[A-Z]|^convert(Back)?$/,
rWrappedInViewMarker = /^\#\d+_`[\s\S]*\/\d+_`$/,
rHtmlEncode = rAttrEncode,
rDataEncode = /[&<>]/g,
rDataUnencode = /&(amp|gt|lt);/g,
rBracketQuote = /\[['"]?|['"]?\]/g,
viewId = 0,
charEntities = {
"&": "&",
"<": "<",
">": ">",
"\x00": "�",
"'": "'",
'"': """,
"`": "`",
"=": "="
},
charsFromEntities = {
amp: "&",
gt: ">",
lt: "<"
},
HTML = "html",
OBJECT = "object",
tmplAttr = "data-jsv-tmpl",
jsvTmpl = "jsvTmpl",
indexStr = "For #index in nested block use #getIndex().",
cpFnStore = {}, // Compiled furnctions for computed values in template expressions (properties, methods, helpers)
$render = {},
jsr = global.jsrender,
jsrToJq = jsr && $ && !$.render, // JsRender already loaded, without jQuery. but we will re-load it now to attach to jQuery
jsvStores = {
template: {
compile: compileTmpl
},
tag: {
compile: compileTag
},
viewModel: {
compile: compileViewModel
},
helper: {},
converter: {}
};
// views object ($.views if jQuery is loaded, jsrender.views if no jQuery, e.g. in Node.js)
$views = {
jsviews: versionNumber,
sub: {
// subscription, e.g. JsViews integration
rPath: /^(!*?)(?:null|true|false|\d[\d.]*|([\w$]+|\.|~([\w$]+)|#(view|([\w$]+))?)([\w$.^]*?)(?:[.[^]([\w$]+)\]?)?)$/g,
// not object helper view viewProperty pathTokens leafToken
rPrm: /(\()(?=\s*\()|(?:([([])\s*)?(?:(\^?)(~?[\w$.^]+)?\s*((\+\+|--)|\+|-|~(?![\w$])|&&|\|\||===|!==|==|!=|<=|>=|[<>%*:?\/]|(=))\s*|(!*?(@)?[#~]?[\w$.^]+)([([])?)|(,\s*)|(?:(\()\s*)?\\?(?:(')|("))|(?:\s*(([)\]])(?=[.^]|\s*$|[^([])|[)\]])([([]?))|(\s+)/g,
// lftPrn0 lftPrn bound path operator err eq path2 late prn comma lftPrn2 apos quot rtPrn rtPrnDot prn2 space
View: View,
Err: JsViewsError,
tmplFn: tmplFn,
parse: parseParams,
extend: $extend,
extendCtx: extendCtx,
syntaxErr: syntaxError,
onStore: {
template: function(name, item) {
if (item === null) {
delete $render[name];
} else if (name) {
$render[name] = item;
}
}
},
addSetting: addSetting,
settings: {
allowCode: false
},
advSet: noop, // Update advanced settings
_thp: tagHandlersFromProps,
_gm: getMethod,
_tg: function() {}, // Constructor for tagDef
_cnvt: convertVal,
_tag: renderTag,
_er: error,
_err: onRenderError,
_cp: retVal, // Get observable contextual parameters (or properties) ~foo=expr. In JsRender, simply returns val.
_sq: function(token) {
if (token === "constructor") {
syntaxError("");
}
return token;
}
},
settings: {
delimiters: $viewsDelimiters,
advanced: function(value) {
return value
? (
$extend($subSettingsAdvanced, value),
$sub.advSet(),
$viewsSettings
)
: $subSettingsAdvanced;
}
},
map: dataMap // If jsObservable loaded first, use that definition of dataMap
};
function getDerivedMethod(baseMethod, method) {
return function() {
var ret,
tag = this,
prevBase = tag.base;
tag.base = baseMethod; // Within method call, calling this.base will call the base method
ret = method.apply(tag, arguments); // Call the method
tag.base = prevBase; // Replace this.base to be the base method of the previous call, for chained calls
return ret;
};
}
function getMethod(baseMethod, method) {
// For derived methods (or handlers declared declaratively as in {{:foo onChange=~fooChanged}} replace by a derived method, to allow using this.base(...)
// or this.baseApply(arguments) to call the base implementation. (Equivalent to this._super(...) and this._superApply(arguments) in jQuery UI)
if ($isFunction(method)) {
method = getDerivedMethod(
!baseMethod
? noop // no base method implementation, so use noop as base method
: baseMethod._d
? baseMethod // baseMethod is a derived method, so use it
: getDerivedMethod(noop, baseMethod), // baseMethod is not derived so make its base method be the noop method
method
);
method._d = (baseMethod && baseMethod._d || 0) + 1; // Add flag for derived method (incremented for derived of derived...)
}
return method;
}
function tagHandlersFromProps(tag, tagCtx) {
var prop,
props = tagCtx.props;
for (prop in props) {
if (rHasHandlers.test(prop) && !(tag[prop] && tag[prop].fix)) { // Don't override handlers with fix expando (used in datepicker and spinner)
tag[prop] = prop !== "convert" ? getMethod(tag.constructor.prototype[prop], props[prop]) : props[prop];
// Copy over the onFoo props, convert and convertBack from tagCtx.props to tag (overrides values in tagDef).
// Note: unsupported scenario: if handlers are dynamically added ^onFoo=expression this will work, but dynamically removing will not work.
}
}
}
function retVal(val) {
return val;
}
function noop() {
return "";
}
function dbgBreak(val) {
// Usage examples: {{dbg:...}}, {{:~dbg(...)}}, {{dbg .../}}, {^{for ... onAfterLink=~dbg}} etc.
try {
console.log("JsRender dbg breakpoint: " + val);
throw "dbg breakpoint"; // To break here, stop on caught exceptions.
}
catch (e) {}
return this.base ? this.baseApply(arguments) : val;
}
function JsViewsError(message) {
// Error exception type for JsViews/JsRender
// Override of $.views.sub.Error is possible
this.name = ($.link ? "JsViews" : "JsRender") + " Error";
this.message = message || this.name;
}
function $extend(target, source) {
if (target) {
for (var name in source) {
target[name] = source[name];
}
return target;
}
}
(JsViewsError.prototype = new Error()).constructor = JsViewsError;
//========================== Top-level functions ==========================
//===================
// views.delimiters
//===================
/**
* Set the tag opening and closing delimiters and 'link' character. Default is "{{", "}}" and "^"
* openChars, closeChars: opening and closing strings, each with two characters
* $.views.settings.delimiters(...)
*
* @param {string} openChars
* @param {string} [closeChars]
* @param {string} [link]
* @returns {Settings}
*
* Get delimiters
* delimsArray = $.views.settings.delimiters()
*
* @returns {string[]}
*/
function $viewsDelimiters(openChars, closeChars, link) {
if (!openChars) {
return $subSettings.delimiters;
}
if ($isArray(openChars)) {
return $viewsDelimiters.apply($views, openChars);
}
linkChar = link ? link[0] : linkChar;
if (!/^(\W|_){5}$/.test(openChars + closeChars + linkChar)) {
error("Invalid delimiters"); // Must be non-word characters, and openChars and closeChars must each be length 2
}
delimOpenChar0 = openChars[0];
delimOpenChar1 = openChars[1];
delimCloseChar0 = closeChars[0];
delimCloseChar1 = closeChars[1];
$subSettings.delimiters = [delimOpenChar0 + delimOpenChar1, delimCloseChar0 + delimCloseChar1, linkChar];
// Escape the characters - since they could be regex special characters
openChars = "\\" + delimOpenChar0 + "(\\" + linkChar + ")?\\" + delimOpenChar1; // Default is "{^{"
closeChars = "\\" + delimCloseChar0 + "\\" + delimCloseChar1; // Default is "}}"
// Build regex with new delimiters
// [tag (followed by / space or }) or cvtr+colon or html or code] followed by space+params then convertBack?
rTag = "(?:(\\w+(?=[\\/\\s\\" + delimCloseChar0 + "]))|(\\w+)?(:)|(>)|(\\*))\\s*((?:[^\\"
+ delimCloseChar0 + "]|\\" + delimCloseChar0 + "(?!\\" + delimCloseChar1 + "))*?)";
// Make rTag available to JsViews (or other components) for parsing binding expressions
$sub.rTag = "(?:" + rTag + ")";
// { ^? { tag+params slash? or closingTag or comment
rTag = new RegExp("(?:" + openChars + rTag + "(\\/)?|\\" + delimOpenChar0 + "(\\" + linkChar + ")?\\" + delimOpenChar1 + "(?:(?:\\/(\\w+))\\s*|!--[\\s\\S]*?--))" + closeChars, "g");
// Default: bind tagName cvt cln html code params slash bind2 closeBlk comment
// /(?:{(\^)?{(?:(\w+(?=[\/\s}]))|(\w+)?(:)|(>)|(\*))\s*((?:[^}]|}(?!}))*?)(\/)?|{(\^)?{(?:(?:\/(\w+))\s*|!--[\s\S]*?--))}}
$sub.rTmpl = new RegExp("^\\s|\\s$|<.*>|([^\\\\]|^)[{}]|" + openChars + ".*" + closeChars);
// $sub.rTmpl looks for initial or final white space, html tags or { or } char not preceded by \\, or JsRender tags {{xxx}}.
// Each of these strings are considered NOT to be jQuery selectors
return $viewsSettings;
}
//=========
// View.get
//=========
function getView(inner, type) { //view.get(inner, type)
if (!type && inner !== true) {
// view.get(type)
type = inner;
inner = undefined;
}
var views, i, l, found,
view = this,
root = type === "root";
// view.get("root") returns view.root, view.get() returns view.parent, view.get(true) returns view.views[0].
if (inner) {
// Go through views - this one, and all nested ones, depth-first - and return first one with given type.
// If type is undefined, i.e. view.get(true), return first child view.
found = type && view.type === type && view;
if (!found) {
views = view.views;
if (view._.useKey) {
for (i in views) {
if (found = type ? views[i].get(inner, type) : views[i]) {
break;
}
}
} else {
for (i = 0, l = views.length; !found && i < l; i++) {
found = type ? views[i].get(inner, type) : views[i];
}
}
}
} else if (root) {
// Find root view. (view whose parent is top view)
found = view.root;
} else if (type) {
while (view && !found) {
// Go through views - this one, and all parent ones - and return first one with given type.
found = view.type === type ? view : undefined;
view = view.parent;
}
} else {
found = view.parent;
}
return found || undefined;
}
function getNestedIndex() {
var view = this.get("item");
return view ? view.index : undefined;
}
getNestedIndex.depends = function() {
return [this.get("item"), "index"];
};
function getIndex() {
return this.index;
}
getIndex.depends = "index";
//==================
// View.ctxPrm, etc.
//==================
/* Internal private: view._getOb() */
function getPathObject(ob, path, ltOb, fn) {
// Iterate through path to late paths: @a.b.c paths
// Return "" (or noop if leaf is a function @a.b.c(...) ) if intermediate object not yet available
var prevOb, tokens, l,
i = 0;
if (ltOb === 1) {
fn = 1;
ltOb = undefined;
}
// Paths like ^a^b^c or ~^a^b^c will not throw if an object in path is undefined.
if (path) {
tokens = path.split(".");
l = tokens.length;
for (; ob && i < l; i++) {
prevOb = ob;
ob = tokens[i] ? ob[tokens[i]] : ob;
}
}
if (ltOb) {
ltOb.lt = ltOb.lt || i<l; // If i < l there was an object in the path not yet available
}
return ob === undefined
? fn ? noop : ""
: fn ? function() {
return ob.apply(prevOb, arguments);
} : ob;
}
function contextParameter(key, value, get) {
// Helper method called as view.ctxPrm(key) for helpers or template parameters ~foo - from compiled template or from context callback
var wrapped, deps, res, obsCtxPrm, tagElse, callView, newRes,
storeView = this,
isUpdate = !isRenderCall && arguments.length > 1,
store = storeView.ctx;
if (key) {
if (!storeView._) { // tagCtx.ctxPrm() call
tagElse = storeView.index;
storeView = storeView.tag;
}
callView = storeView;
if (store && store.hasOwnProperty(key) || (store = $helpers).hasOwnProperty(key)) {
res = store[key];
if (key === "tag" || key === "tagCtx" || key === "root" || key === "parentTags") {
return res;
}
} else {
store = undefined;
}
if (!isRenderCall && storeView.tagCtx || storeView.linked) { // Data-linked view, or tag instance
if (!res || !res._cxp) {
// Not a contextual parameter
// Set storeView to tag (if this is a tag.ctxPrm() call) or to root view ("data" view of linked template)
storeView = storeView.tagCtx || $isFunction(res)
? storeView // Is a tag, not a view, or is a computed contextual parameter, so scope to the callView, no the 'scope view'
: (storeView = storeView.scope || storeView,
!storeView.isTop && storeView.ctx.tag // If this view is in a tag, set storeView to the tag
|| storeView);
if (res !== undefined && storeView.tagCtx) {
// If storeView is a tag, but the contextual parameter has been set at at higher level (e.g. helpers)...
storeView = storeView.tagCtx.view.scope; // then move storeView to the outer level (scope of tag container view)
}
store = storeView._ocps;
res = store && store.hasOwnProperty(key) && store[key] || res;
if (!(res && res._cxp) && (get || isUpdate)) {
// Create observable contextual parameter
(store || (storeView._ocps = storeView._ocps || {}))[key]
= res
= [{
_ocp: res, // The observable contextual parameter value
_vw: callView,
_key: key
}];
res._cxp = {
path: _ocp,
ind: 0,
updateValue: function(val, path) {
$.observable(res[0]).setProperty(_ocp, val); // Set the value (res[0]._ocp)
return this;
}
};
}
}
if (obsCtxPrm = res && res._cxp) {
// If this helper resource is an observable contextual parameter
if (arguments.length > 2) {
deps = res[1] ? $sub._ceo(res[1].deps) : [_ocp]; // fn deps (with any exprObs cloned using $sub._ceo)
deps.unshift(res[0]); // view
deps._cxp = obsCtxPrm;
// In a context callback for a contextual param, we set get = true, to get ctxPrm [view, dependencies...] array - needed for observe call
return deps;
}
tagElse = obsCtxPrm.tagElse;
newRes = res[1] // linkFn for compiled expression
? obsCtxPrm.tag && obsCtxPrm.tag.cvtArgs
? obsCtxPrm.tag.cvtArgs(tagElse, 1)[obsCtxPrm.ind] // = tag.bndArgs() - for tag contextual parameter
: res[1](res[0].data, res[0], $sub) // = fn(data, view, $sub) for compiled binding expression
: res[0]._ocp; // Observable contextual parameter (uninitialized, or initialized as static expression, so no path dependencies)
if (isUpdate) {
$sub._ucp(key, value, storeView, obsCtxPrm); // Update observable contextual parameter
return storeView;
}
res = newRes;
}
}
if (res && $isFunction(res)) {
// If a helper is of type function we will wrap it, so if called with no this pointer it will be called with the
// view as 'this' context. If the helper ~foo() was in a data-link expression, the view will have a 'temporary' linkCtx property too.
// Note that helper functions on deeper paths will have specific this pointers, from the preceding path.
// For example, ~util.foo() will have the ~util object as 'this' pointer
wrapped = function() {
return res.apply((!this || this === global) ? callView : this, arguments);
};
$extend(wrapped, res); // Attach same expandos (if any) to the wrapped function
}
return wrapped || res;
}
}
/* Internal private: view._getTmpl() */
function getTemplate(tmpl) {
return tmpl && (tmpl.fn
? tmpl
: this.getRsc("templates", tmpl) || $templates(tmpl)); // not yet compiled
}
//==============
// views._cnvt
//==============
function convertVal(converter, view, tagCtx, onError) {
// Called from compiled template code for {{:}}
// self is template object or linkCtx object
var tag, linkCtx, value, argsLen, bindTo,
// If tagCtx is an integer, then it is the key for the compiled function to return the boundTag tagCtx
boundTag = typeof tagCtx === "number" && view.tmpl.bnds[tagCtx-1];
if (onError === undefined && boundTag && boundTag._lr) { // lateRender
onError = "";
}
if (onError !== undefined) {
tagCtx = onError = {props: {}, args: [onError]};
} else if (boundTag) {
tagCtx = boundTag(view.data, view, $sub);
}
boundTag = boundTag._bd && boundTag;
if (converter || boundTag) {
linkCtx = view._lc; // For data-link="{cvt:...}"... See onDataLinkedTagChange
tag = linkCtx && linkCtx.tag;
tagCtx.view = view;
if (!tag) {
tag = $extend(new $sub._tg(), {
_: {
bnd: boundTag,
unlinked: true,
lt: tagCtx.lt // If a late path @some.path has not returned @some object, mark tag as late
},
inline: !linkCtx,
tagName: ":",
convert: converter,
onArrayChange: true,
flow: true,
tagCtx: tagCtx,
tagCtxs: [tagCtx],
_is: "tag"
});
argsLen = tagCtx.args.length;
if (argsLen>1) {
bindTo = tag.bindTo = [];
while (argsLen--) {
bindTo.unshift(argsLen); // Bind to all the arguments - generate bindTo array: [0,1,2...]
}
}
if (linkCtx) {
linkCtx.tag = tag;
tag.linkCtx = linkCtx;
}
tagCtx.ctx = extendCtx(tagCtx.ctx, (linkCtx ? linkCtx.view : view).ctx);
tagHandlersFromProps(tag, tagCtx);
}
tag._er = onError && value;
tag.ctx = tagCtx.ctx || tag.ctx || {};
tagCtx.ctx = undefined;
value = tag.cvtArgs()[0]; // If there is a convertBack but no convert, converter will be "true"
tag._er = onError && value;
} else {
value = tagCtx.args[0];
}
// Call onRender (used by JsViews if present, to add binding annotations around rendered content)
value = boundTag && view._.onRender
? view._.onRender(value, view, tag)
: value;
return value != undefined ? value : "";
}
function convertArgs(tagElse, bound) { // tag.cvtArgs() or tag.cvtArgs(tagElse?, true?)
var l, key, boundArgs, args, bindFrom, tag, converter,
tagCtx = this;
if (tagCtx.tagName) {
tag = tagCtx;
tagCtx = (tag.tagCtxs || [tagCtx])[tagElse||0];
if (!tagCtx) {
return;
}
} else {
tag = tagCtx.tag;
}
bindFrom = tag.bindFrom;
args = tagCtx.args;
if ((converter = tag.convert) && "" + converter === converter) {
converter = converter === "true"
? undefined
: (tagCtx.view.getRsc("converters", converter) || error("Unknown converter: '" + converter + "'"));
}
if (converter && !bound) { // If there is a converter, use a copy of the tagCtx.args array for rendering, and replace the args[0] in
args = args.slice(); // the copied array with the converted value. But we do not modify the value of tag.tagCtx.args[0] (the original args array)
}
if (bindFrom) { // Get the values of the boundArgs
boundArgs = [];
l = bindFrom.length;
while (l--) {
key = bindFrom[l];
boundArgs.unshift(argOrProp(tagCtx, key));
}
if (bound) {
args = boundArgs; // Call to bndArgs() - returns the boundArgs
}
}
if (converter) {
converter = converter.apply(tag, boundArgs || args);
if (converter === undefined) {
return args; // Returning undefined from a converter is equivalent to not having a converter.
}
bindFrom = bindFrom || [0];
l = bindFrom.length;
if (!$isArray(converter) || converter.length !== l) {
converter = [converter];
bindFrom = [0];
l = 1;
}
if (bound) { // Call to bndArgs() - so apply converter to all boundArgs
args = converter; // The array of values returned from the converter
} else { // Call to cvtArgs()
while (l--) {
key = bindFrom[l];
if (+key === key) {
args[key] = converter[l];
}
}
}
}
return args;
}
function argOrProp(context, key) {
context = context[+key === key ? "args" : "props"];
return context && context[key];
}
function convertBoundArgs(tagElse) { // tag.bndArgs()
return this.cvtArgs(tagElse, 1);
}
//=============
// views.tag
//=============
/* view.getRsc() */
function getResource(resourceType, itemName) {
var res, store,
view = this;
if ("" + itemName === itemName) {
while ((res === undefined) && view) {
store = view.tmpl && view.tmpl[resourceType];
res = store && store[itemName];
view = view.parent;
}
return res || $views[resourceType][itemName];
}
}
function renderTag(tagName, parentView, tmpl, tagCtxs, isUpdate, onError) {
function bindToOrBindFrom(type) {
var bindArray = tag[type];
if (bindArray !== undefined) {
bindArray = $isArray(bindArray) ? bindArray : [bindArray];
m = bindArray.length;
while (m--) {
key = bindArray[m];
if (!isNaN(parseInt(key))) {
bindArray[m] = parseInt(key); // Convert "0" to 0, etc.
}
}
}
return bindArray || [0];
}
parentView = parentView || topView;
var tag, tagDef, template, tags, attr, parentTag, l, m, n, itemRet, tagCtx, tagCtxCtx, ctxPrm, bindTo, bindFrom, initVal,
content, callInit, mapDef, thisMap, args, bdArgs, props, tagDataMap, contentCtx, key, bindFromLength, bindToLength, linkedElement, defaultCtx,
i = 0,
ret = "",
linkCtx = parentView._lc || false, // For data-link="{myTag...}"... See onDataLinkedTagChange
ctx = parentView.ctx,
parentTmpl = tmpl || parentView.tmpl,
// If tagCtxs is an integer, then it is the key for the compiled function to return the boundTag tagCtxs
boundTag = typeof tagCtxs === "number" && parentView.tmpl.bnds[tagCtxs-1];
if (tagName._is === "tag") {
tag = tagName;
tagName = tag.tagName;
tagCtxs = tag.tagCtxs;
template = tag.template;
} else {
tagDef = parentView.getRsc("tags", tagName) || error("Unknown tag: {{" + tagName + "}} ");
template = tagDef.template;
}
if (onError === undefined && boundTag && (boundTag._lr = (tagDef.lateRender && boundTag._lr!== false || boundTag._lr))) {
onError = ""; // If lateRender, set temporary onError, to skip initial rendering (and render just "")
}
if (onError !== undefined) {
ret += onError;
tagCtxs = onError = [{props: {}, args: [], params: {props:{}}}];
} else if (boundTag) {
tagCtxs = boundTag(parentView.data, parentView, $sub);
}
l = tagCtxs.length;
for (; i < l; i++) {
tagCtx = tagCtxs[i];
content = tagCtx.tmpl;
if (!linkCtx || !linkCtx.tag || i && !linkCtx.tag.inline || tag._er || content && +content===content) {
// Initialize tagCtx
// For block tags, tagCtx.tmpl is an integer > 0
if (content && parentTmpl.tmpls) {
tagCtx.tmpl = tagCtx.content = parentTmpl.tmpls[content - 1]; // Set the tmpl property to the content of the block tag
}
tagCtx.index = i;
tagCtx.ctxPrm = contextParameter;
tagCtx.render = renderContent;
tagCtx.cvtArgs = convertArgs;
tagCtx.bndArgs = convertBoundArgs;
tagCtx.view = parentView;
tagCtx.ctx = extendCtx(extendCtx(tagCtx.ctx, tagDef && tagDef.ctx), ctx); // Clone and extend parentView.ctx
}
if (tmpl = tagCtx.props.tmpl) {
// If the tmpl property is overridden, set the value (when initializing, or, in case of binding: ^tmpl=..., when updating)
tagCtx.tmpl = parentView._getTmpl(tmpl);
tagCtx.content = tagCtx.content || tagCtx.tmpl;
}
if (!tag) {
// This will only be hit for initial tagCtx (not for {{else}}) - if the tag instance does not exist yet
// If the tag has not already been instantiated, we will create a new instance.
// ~tag will access the tag, even within the rendering of the template content of this tag.
// From child/descendant tags, can access using ~tag.parent, or ~parentTags.tagName
tag = new tagDef._ctr();
callInit = !!tag.init;
tag.parent = parentTag = ctx && ctx.tag;
tag.tagCtxs = tagCtxs;
if (linkCtx) {
tag.inline = false;
linkCtx.tag = tag;
}
tag.linkCtx = linkCtx;
if (tag._.bnd = boundTag || linkCtx.fn) {
// Bound if {^{tag...}} or data-link="{tag...}"
tag._.ths = tagCtx.params.props.this; // Tag has a this=expr binding, to get javascript reference to tag instance
tag._.lt = tagCtxs.lt; // If a late path @some.path has not returned @some object, mark tag as late
tag._.arrVws = {};
} else if (tag.dataBoundOnly) {
error(tagName + " must be data-bound:\n{^{" + tagName + "}}");
}
//TODO better perf for childTags() - keep child tag.tags array, (and remove child, when disposed)
// tag.tags = [];
} else if (linkCtx && linkCtx.fn._lr) {
callInit = !!tag.init;
}
tagDataMap = tag.dataMap;
tagCtx.tag = tag;
if (tagDataMap && tagCtxs) {
tagCtx.map = tagCtxs[i].map; // Copy over the compiled map instance from the previous tagCtxs to the refreshed ones
}
if (!tag.flow) {
tagCtxCtx = tagCtx.ctx = tagCtx.ctx || {};
// tags hash: tag.ctx.tags, merged with parentView.ctx.tags,
tags = tag.parents = tagCtxCtx.parentTags = ctx && extendCtx(tagCtxCtx.parentTags, ctx.parentTags) || {};
if (parentTag) {
tags[parentTag.tagName] = parentTag;
//TODO better perf for childTags: parentTag.tags.push(tag);
}
tags[tag.tagName] = tagCtxCtx.tag = tag;
tagCtxCtx.tagCtx = tagCtx;
}
}
if (!(tag._er = onError)) {
tagHandlersFromProps(tag, tagCtxs[0]);
tag.rendering = {rndr: tag.rendering}; // Provide object for state during render calls to tag and elses. (Used by {{if}} and {{for}}...)
for (i = 0; i < l; i++) { // Iterate tagCtx for each {{else}} block
tagCtx = tag.tagCtx = tagCtxs[i];
props = tagCtx.props;
tag.ctx = tagCtx.ctx;
if (!i) {
if (callInit) {
tag.init(tagCtx, linkCtx, tag.ctx);
callInit = undefined;
}
if (!tagCtx.args.length && tagCtx.argDefault !== false && tag.argDefault !== false) {
tagCtx.args = args = [tagCtx.view.data]; // Missing first arg defaults to the current data context
tagCtx.params.args = ["#data"];
}
bindTo = bindToOrBindFrom("bindTo");
if (tag.bindTo !== undefined) {
tag.bindTo = bindTo;
}
if (tag.bindFrom !== undefined) {
tag.bindFrom = bindToOrBindFrom("bindFrom");
} else if (tag.bindTo) {
tag.bindFrom = tag.bindTo = bindTo;
}
bindFrom = tag.bindFrom || bindTo;
bindToLength = bindTo.length;
bindFromLength = bindFrom.length;
if (tag._.bnd && (linkedElement = tag.linkedElement)) {
tag.linkedElement = linkedElement = $isArray(linkedElement) ? linkedElement: [linkedElement];
if (bindToLength !== linkedElement.length) {
error("linkedElement not same length as bindTo");
}
}
if (linkedElement = tag.linkedCtxParam) {
tag.linkedCtxParam = linkedElement = $isArray(linkedElement) ? linkedElement: [linkedElement];
if (bindFromLength !== linkedElement.length) {
error("linkedCtxParam not same length as bindFrom/bindTo");
}
}
if (bindFrom) {
tag._.fromIndex = {}; // Hash of bindFrom index which has same path value as bindTo index. fromIndex = tag._.fromIndex[toIndex]
tag._.toIndex = {}; // Hash of bindFrom index which has same path value as bindTo index. fromIndex = tag._.fromIndex[toIndex]
n = bindFromLength;
while (n--) {
key = bindFrom[n];
m = bindToLength;
while (m--) {
if (key === bindTo[m]) {
tag._.fromIndex[m] = n;
tag._.toIndex[n] = m;
}
}
}
}
if (linkCtx) {
// Set attr on linkCtx to ensure outputting to the correct target attribute.
// Setting either linkCtx.attr or this.attr in the init() allows per-instance choice of target attrib.
linkCtx.attr = tag.attr = linkCtx.attr || tag.attr || linkCtx._dfAt;
}
attr = tag.attr;
tag._.noVws = attr && attr !== HTML;
}
args = tag.cvtArgs(i);
if (tag.linkedCtxParam) {
bdArgs = tag.cvtArgs(i, 1);
m = bindFromLength;
defaultCtx = tag.constructor.prototype.ctx;
while (m--) {
if (ctxPrm = tag.linkedCtxParam[m]) {
key = bindFrom[m];
initVal = bdArgs[m];
// Create tag contextual parameter
tagCtx.ctx[ctxPrm] = $sub._cp(
defaultCtx && initVal === undefined ? defaultCtx[ctxPrm]: initVal,
initVal !== undefined && argOrProp(tagCtx.params, key),
tagCtx.view,
tag._.bnd && {tag: tag, cvt: tag.convert, ind: m, tagElse: i}
);
}
}
}
if ((mapDef = props.dataMap || tagDataMap) && (args.length || props.dataMap)) {
thisMap = tagCtx.map;
if (!thisMap || thisMap.src !== args[0] || isUpdate) {
if (thisMap && thisMap.src) {
thisMap.unmap(); // only called if observable map - not when only used in JsRender, e.g. by {{props}}
}
mapDef.map(args[0], tagCtx, thisMap, !tag._.bnd);
thisMap = tagCtx.map;
}
args = [thisMap.tgt];
}
itemRet = undefined;
if (tag.render) {
itemRet = tag.render.apply(tag, args);
if (parentView.linked && itemRet && !rWrappedInViewMarker.test(itemRet)) {
// When a tag renders content from the render method, with data linking then we need to wrap with view markers, if absent,
// to provide a contentView for the tag, which will correctly dispose bindings if deleted. The 'tmpl' for this view will
// be a dumbed-down template which will always return the itemRet string (no matter what the data is). The itemRet string
// is not compiled as template markup, so can include "{{" or "}}" without triggering syntax errors
tmpl = { // 'Dumbed-down' template which always renders 'static' itemRet string
links: []
};
tmpl.render = tmpl.fn = function() {
return itemRet;
};
itemRet = renderWithViews(tmpl, parentView.data, undefined, true, parentView, undefined, undefined, tag);
}
}
if (!args.length) {
args = [parentView]; // no arguments - (e.g. {{else}}) get data context from view.
}
if (itemRet === undefined) {
contentCtx = args[0]; // Default data context for wrapped block content is the first argument
if (tag.contentCtx) { // Set tag.contentCtx to true, to inherit parent context, or to a function to provide alternate context.
contentCtx = tag.contentCtx === true ? parentView : tag.contentCtx(contentCtx);
}
itemRet = tagCtx.render(contentCtx, true) || (isUpdate ? undefined : "");
}
ret = ret
? ret + (itemRet || "")
: itemRet !== undefined
? "" + itemRet
: undefined; // If no return value from render, and no template/content tagCtx.render(...), return undefined
}
tag.rendering = tag.rendering.rndr; // Remove tag.rendering object (if this is outermost render call. (In case of nested calls)
}
tag.tagCtx = tagCtxs[0];
tag.ctx = tag.tagCtx.ctx;
if (tag._.noVws && tag.inline) {
// inline tag with attr set to "text" will insert HTML-encoded content - as if it was element-based innerText
ret = attr === "text"
? $converters.html(ret)
: "";
}
return boundTag && parentView._.onRender
// Call onRender (used by JsViews if present, to add binding annotations around rendered content)
? parentView._.onRender(ret, parentView, tag)
: ret;
}
//=================
// View constructor
//=================
function View(context, type, parentView, data, template, key, onRender, contentTmpl) {
// Constructor for view object in view hierarchy. (Augmented by JsViews if JsViews is loaded)
var views, parentView_, tag, self_,
self = this,
isArray = type === "array";
// If the data is an array, this is an 'array view' with a views array for each child 'item view'
// If the data is not an array, this is an 'item view' with a views 'hash' object for any child nested views
self.content = contentTmpl;
self.views = isArray ? [] : {};
self.data = data;
self.tmpl = template;
self_ = self._ = {
key: 0,
// ._.useKey is non zero if is not an 'array view' (owning a data array). Use this as next key for adding to child views hash
useKey: isArray ? 0 : 1,
id: "" + viewId++,
onRender: onRender,
bnds: {}
};
self.linked = !!onRender;
self.type = type || "top";
if (type) {
self.cache = {_ct: $subSettings._cchCt}; // Used for caching results of computed properties and helpers (view.getCache)
}
if (!parentView || parentView.type === "top") {
(self.ctx = context || {}).root = self.data;
}
if (self.parent = parentView) {
self.root = parentView.root || self; // view whose parent is top view
views = parentView.views;
parentView_ = parentView._;
self.isTop = parentView_.scp; // Is top content view of a link("#container", ...) call
self.scope = (!context.tag || context.tag === parentView.ctx.tag) && !self.isTop && parentView.scope || self;
// Scope for contextParams - closest non flow tag ancestor or root view
if (parentView_.useKey) {
// Parent is not an 'array view'. Add this view to its views object
// self._key = is the key in the parent view hash
views[self_.key = "_" + parentView_.useKey++] = self;
self.index = indexStr;
self.getIndex = getNestedIndex;
} else if (views.length === (self_.key = self.index = key)) { // Parent is an 'array view'. Add this view to its views array
views.push(self); // Adding to end of views array. (Using push when possible - better perf than splice)
} else {
views.splice(key, 0, self); // Inserting in views array
}
// If no context was passed in, use parent context
// If context was passed in, it should have been merged already with parent context
self.ctx = context || parentView.ctx;
} else if (type) {
self.root = self; // view whose parent is top view
}
}
View.prototype = {
get: getView,
getIndex: getIndex,
ctxPrm: contextParameter,
getRsc: getResource,
_getTmpl: getTemplate,
_getOb: getPathObject,
getCache: function(key) { // Get cached value of computed value
if ($subSettings._cchCt > this.cache._ct) {
this.cache = {_ct: $subSettings._cchCt};
}
return this.cache[key] || (this.cache[key] = cpFnStore[key](this.data, this, $sub));
},
_is: "view"
};
//====================================================
// Registration
//====================================================
function compileChildResources(parentTmpl) {
var storeName, storeNames, resources;
for (storeName in jsvStores) {
storeNames = storeName + "s";
if (parentTmpl[storeNames]) {
resources = parentTmpl[storeNames]; // Resources not yet compiled
parentTmpl[storeNames] = {}; // Remove uncompiled resources
$views[storeNames](resources, parentTmpl); // Add back in the compiled resources
}
}
}
//===============
// compileTag
//===============
function compileTag(name, tagDef, parentTmpl) {
var tmpl, baseTag, prop,
compiledDef = new $sub._tg();
function Tag() {
var tag = this;
tag._ = {
unlinked: true
};
tag.inline = true;
tag.tagName = name;
}
if ($isFunction(tagDef)) {
// Simple tag declared as function. No presenter instantation.
tagDef = {
depends: tagDef.depends,
render: tagDef
};
} else if ("" + tagDef === tagDef) {
tagDef = {template: tagDef};
}
if (baseTag = tagDef.baseTag) {
tagDef.flow = !!tagDef.flow; // Set flow property, so defaults to false even if baseTag has flow=true
baseTag = "" + baseTag === baseTag
? (parentTmpl && parentTmpl.tags[baseTag] || $tags[baseTag])
: baseTag;
if (!baseTag) {
error('baseTag: "' + tagDef.baseTag + '" not found');
}
compiledDef = $extend(compiledDef, baseTag);
for (prop in tagDef) {
compiledDef[prop] = getMethod(baseTag[prop], tagDef[prop]);
}
} else {
compiledDef = $extend(compiledDef, tagDef);
}
// Tag declared as object, used as the prototype for tag instantiation (control/presenter)
if ((tmpl = compiledDef.template) !== undefined) {
compiledDef.template = "" + tmpl === tmpl ? ($templates[tmpl] || $templates(tmpl)) : tmpl;
}
(Tag.prototype = compiledDef).constructor = compiledDef._ctr = Tag;
if (parentTmpl) {
compiledDef._parentTmpl = parentTmpl;
}
return compiledDef;
}
function baseApply(args) {
// In derived method (or handler declared declaratively as in {{:foo onChange=~fooChanged}} can call base method,
// using this.baseApply(arguments) (Equivalent to this._superApply(arguments) in jQuery UI)
return this.base.apply(this, args);
}
//===============
// compileTmpl
//===============
function compileTmpl(name, tmpl, parentTmpl, options) {
// tmpl is either a template object, a selector for a template script block, or the name of a compiled template
//==== nested functions ====
function lookupTemplate(value) {
// If value is of type string - treat as selector, or name of compiled template
// Return the template object, if already compiled, or the markup string
var currentName, tmpl;
if (("" + value === value) || value.nodeType > 0 && (elem = value)) {
if (!elem) {
if (/^\.?\/[^\\:*?"<>]*$/.test(value)) {
// value="./some/file.html" (or "/some/file.html")
// If the template is not named, use "./some/file.html" as name.
if (tmpl = $templates[name = name || value]) {
value = tmpl;
} else {
// BROWSER-SPECIFIC CODE (not on Node.js):
// Look for server-generated script block with id "./some/file.html"
elem = document.getElementById(value);
}
} else if ($.fn && !$sub.rTmpl.test(value)) {
try {
elem = $(value, document)[0]; // if jQuery is loaded, test for selector returning elements, and get first element
} catch (e) {}
}// END BROWSER-SPECIFIC CODE
} //BROWSER-SPECIFIC CODE
if (elem) {
if (elem.tagName !== "SCRIPT") {
error(value + ": Use script block, not " + elem.tagName);
}
if (options) {
// We will compile a new template using the markup in the script element
value = elem.innerHTML;
} else {
// We will cache a single copy of the compiled template, and associate it with the name
// (renaming from a previous name if there was one).
currentName = elem.getAttribute(tmplAttr);
if (currentName) {
if (currentName !== jsvTmpl) {
value = $templates[currentName];
delete $templates[currentName];
} else if ($.fn) {
value = $.data(elem)[jsvTmpl]; // Get cached compiled template
}
}
if (!currentName || !value) { // Not yet compiled, or cached version lost
name = name || ($.fn ? jsvTmpl : value);
value = compileTmpl(name, elem.innerHTML, parentTmpl, options);
}
value.tmplName = name = name || currentName;
if (name !== jsvTmpl) {
$templates[name] = value;
}
elem.setAttribute(tmplAttr, name);
if ($.fn) {
$.data(elem, jsvTmpl, value);
}
}
} // END BROWSER-SPECIFIC CODE
elem = undefined;
} else if (!value.fn) {
value = undefined;
// If value is not a string. HTML element, or compiled template, return undefined
}
return value;
}
var elem, compiledTmpl,
tmplOrMarkup = tmpl = tmpl || "";
$sub._html = $converters.html;
//==== Compile the template ====
if (options === 0) {
options = undefined;
tmplOrMarkup = lookupTemplate(tmplOrMarkup); // Top-level compile so do a template lookup
}
// If options, then this was already compiled from a (script) element template declaration.
// If not, then if tmpl is a template object, use it for options
options = options || (tmpl.markup
? tmpl.bnds
? $extend({}, tmpl)
: tmpl
: {}
);
options.tmplName = options.tmplName || name || "unnamed";
if (parentTmpl) {
options._parentTmpl = parentTmpl;
}
// If tmpl is not a markup string or a selector string, then it must be a template object
// In that case, get it from the markup property of the object
if (!tmplOrMarkup && tmpl.markup && (tmplOrMarkup = lookupTemplate(tmpl.markup)) && tmplOrMarkup.fn) {
// If the string references a compiled template object, need to recompile to merge any modified options
tmplOrMarkup = tmplOrMarkup.markup;
}
if (tmplOrMarkup !== undefined) {
if (tmplOrMarkup.render || tmpl.render) {
// tmpl is already compiled, so use it
if (tmplOrMarkup.tmpls) {
compiledTmpl = tmplOrMarkup;
}
} else {
// tmplOrMarkup is a markup string, not a compiled template
// Create template object
tmpl = tmplObject(tmplOrMarkup, options);
// Compile to AST and then to compiled function
tmplFn(tmplOrMarkup.replace(rEscapeQuotes, "\\$&"), tmpl);
}
if (!compiledTmpl) {
compiledTmpl = $extend(function() {
return compiledTmpl.render.apply(compiledTmpl, arguments);
}, tmpl);
compileChildResources(compiledTmpl);
}
return compiledTmpl;
}
}
//==== /end of function compileTmpl ====
//=================
// compileViewModel
//=================
function getDefaultVal(defaultVal, data) {
return $isFunction(defaultVal)
? defaultVal.call(data)
: defaultVal;
}
function addParentRef(ob, ref, parent) {
Object.defineProperty(ob, ref, {
value: parent,
configurable: true
});
}
function compileViewModel(name, type) {
var i, constructor, parent,
viewModels = this,
getters = type.getters,
extend = type.extend,
id = type.id,
proto = $.extend({
_is: name || "unnamed",
unmap: unmap,
merge: merge
}, extend),
args = "",
cnstr = "",
getterCount = getters ? getters.length : 0,
$observable = $.observable,
getterNames = {};
function JsvVm(args) {
constructor.apply(this, args);
}
function vm() {
return new JsvVm(arguments);
}
function iterate(data, action) {
var getterType, defaultVal, prop, ob, parentRef,
j = 0;
for (; j < getterCount; j++) {
prop = getters[j];
getterType = undefined;
if (prop + "" !== prop) {
getterType = prop;
prop = getterType.getter;
parentRef = getterType.parentRef;
}
if ((ob = data[prop]) === undefined && getterType && (defaultVal = getterType.defaultVal) !== undefined) {
ob = getDefaultVal(defaultVal, data);
}
action(ob, getterType && viewModels[getterType.type], prop, parentRef);
}
}
function map(data) {
data = data + "" === data
? JSON.parse(data) // Accept JSON string
: data; // or object/array
var l, prop, childOb, parentRef,
j = 0,
ob = data,
arr = [];
if ($isArray(data)) {
data = data || [];
l = data.length;
for (; j<l; j++) {
arr.push(this.map(data[j]));
}
arr._is = name;
arr.unmap = unmap;
arr.merge = merge;
return arr;
}
if (data) {
iterate(data, function(ob, viewModel) {
if (viewModel) { // Iterate to build getters arg array (value, or mapped value)
ob = viewModel.map(ob);
}
arr.push(ob);
});
ob = this.apply(this, arr); // Instantiate this View Model, passing getters args array to constructor
j = getterCount;
while (j--) {
childOb = arr[j];
parentRef = getters[j].parentRef;
if (parentRef && childOb && childOb.unmap) {
if ($isArray(childOb)) {
l = childOb.length;
while (l--) {
addParentRef(childOb[l], parentRef, ob);
}
} else {
addParentRef(childOb, parentRef, ob);
}
}
}
for (prop in data) { // Copy over any other properties. that are not get/set properties
if (prop !== $expando && !getterNames[prop]) {
ob[prop] = data[prop];
}
}
}
return ob;
}
function merge(data, parent, parentRef) {
data = data + "" === data
? JSON.parse(data) // Accept JSON string
: data; // or object/array
var j, l, m, prop, mod, found, assigned, ob, newModArr, childOb,
k = 0,
model = this;
if ($isArray(model)) {
assigned = {};
newModArr = [];
l = data.length;
m = model.length;
for (; k<l; k++) {
ob = data[k];
found = false;
for (j=0; j<m && !found; j++) {
if (assigned[j]) {
continue;
}
mod = model[j];
if (id) {
assigned[j] = found = id + "" === id
? (ob[id] && (getterNames[id] ? mod[id]() : mod[id]) === ob[id])
: id(mod, ob);
}
}
if (found) {
mod.merge(ob);
newModArr.push(mod);
} else {
newModArr.push(childOb = vm.map(ob));
if (parentRef) {
addParentRef(childOb, parentRef, parent);
}
}
}
if ($observable) {
$observable(model).refresh(newModArr, true);
} else {
model.splice.apply(model, [0, model.length].concat(newModArr));
}
return;
}
iterate(data, function(ob, viewModel, getter, parentRef) {
if (viewModel) {
model[getter]().merge(ob, model, parentRef); // Update typed property
} else if (model[getter]() !== ob) {
model[getter](ob); // Update non-typed property
}
});
for (prop in data) {
if (prop !== $expando && !getterNames[prop]) {
model[prop] = data[prop];
}
}
}
function unmap() {
var ob, prop, getterType, arr, value,
k = 0,
model = this;
function unmapArray(modelArr) {
var arr = [],
i = 0,
l = modelArr.length;
for (; i<l; i++) {
arr.push(modelArr[i].unmap());
}
return arr;
}
if ($isArray(model)) {
return unmapArray(model);
}
ob = {};
for (; k < getterCount; k++) {
prop = getters[k];
getterType = undefined;
if (prop + "" !== prop) {
getterType = prop;
prop = getterType.getter;
}
value = model[prop]();
ob[prop] = getterType && value && viewModels[getterType.type]
? $isArray(value)
? unmapArray(value)
: value.unmap()
: value;
}
for (prop in model) {
if (model.hasOwnProperty(prop) && (prop.charAt(0) !== "_" || !getterNames[prop.slice(1)]) && prop !== $expando && !$isFunction(model[prop])) {
ob[prop] = model[prop];
}
}
return ob;
}
JsvVm.prototype = proto;
for (i=0; i < getterCount; i++) {
(function(getter) {
getter = getter.getter || getter;
getterNames[getter] = i+1;
var privField = "_" + getter;
args += (args ? "," : "") + getter;
cnstr += "this." + privField + " = " + getter + ";\n";
proto[getter] = proto[getter] || function(val) {
if (!arguments.length) {
return this[privField]; // If there is no argument, use as a getter
}
if ($observable) {
$observable(this).setProperty(getter, val);
} else {
this[privField] = val;
}
};
if ($observable) {
proto[getter].set = proto[getter].set || function(val) {
this[privField] = val; // Setter called by observable property change
};
}
})(getters[i]);
}
// Constructor for new viewModel instance.
cnstr = new Function(args, cnstr);
constructor = function() {
cnstr.apply(this, arguments);
// Pass additional parentRef str and parent obj to have a parentRef pointer on instance
if (parent = arguments[getterCount + 1]) {
addParentRef(this, arguments[getterCount], parent);
}
};
constructor.prototype = proto;
proto.constructor = constructor;
vm.map = map;
vm.getters = getters;
vm.extend = extend;
vm.id = id;
return vm;
}
function tmplObject(markup, options) {
// Template object constructor
var htmlTag,
wrapMap = $subSettingsAdvanced._wm || {}, // Only used in JsViews. Otherwise empty: {}
tmpl = {
tmpls: [],
links: {}, // Compiled functions for link expressions
bnds: [],
_is: "template",
render: renderContent
};
if (options) {
tmpl = $extend(tmpl, options);
}
tmpl.markup = markup;
if (!tmpl.htmlTag) {
// Set tmpl.tag to the top-level HTML tag used in the template, if any...
htmlTag = rFirstElem.exec(markup);
tmpl.htmlTag = htmlTag ? htmlTag[1].toLowerCase() : "";
}
htmlTag = wrapMap[tmpl.htmlTag];
if (htmlTag && htmlTag !== wrapMap.div) {
// When using JsViews, we trim templates which are inserted into HTML contexts where text nodes are not rendered (i.e. not 'Phrasing Content').
// Currently not trimmed for <li> tag. (Not worth adding perf cost)
tmpl.markup = $.trim(tmpl.markup);
}
return tmpl;
}
//==============
// registerStore
//==============
/**
* Internal. Register a store type (used for template, tags, helpers, converters)
*/
function registerStore(storeName, storeSettings) {
/**
* Generic store() function to register item, named item, or hash of items
* Also used as hash to store the registered items
* Used as implementation of $.templates(), $.views.templates(), $.views.tags(), $.views.helpers() and $.views.converters()
*
* @param {string|hash} name name - or selector, in case of $.templates(). Or hash of items
* @param {any} [item] (e.g. markup for named template)
* @param {template} [parentTmpl] For item being registered as private resource of template
* @returns {any|$.views} item, e.g. compiled template - or $.views in case of registering hash of items
*/
function theStore(name, item, parentTmpl) {
// The store is also the function used to add items to the store. e.g. $.templates, or $.views.tags
// For store of name 'thing', Call as:
// $.views.things(items[, parentTmpl]),
// or $.views.things(name[, item, parentTmpl])
var compile, itemName, thisStore, cnt,
onStore = $sub.onStore[storeName];
if (name && typeof name === OBJECT && !name.nodeType && !name.markup && !name.getTgt && !(storeName === "viewModel" && name.getters || name.extend)) {
// Call to $.views.things(items[, parentTmpl]),
// Adding items to the store
// If name is a hash, then item is parentTmpl. Iterate over hash and call store for key.
for (itemName in name) {
theStore(itemName, name[itemName], item);
}
return item || $views;
}
// Adding a single unnamed item to the store
if (name && "" + name !== name) { // name must be a string
parentTmpl = item;
item = name;
name = undefined;
}
thisStore = parentTmpl
? storeName === "viewModel"
? parentTmpl
: (parentTmpl[storeNames] = parentTmpl[storeNames] || {})
: theStore;
compile = storeSettings.compile;
if (item === undefined) {
item = compile ? name : thisStore[name];
name = undefined;
}
if (item === null) {
// If item is null, delete this entry
if (name) {
delete thisStore[name];
}
} else {
if (compile) {
item = compile.call(thisStore, name, item, parentTmpl, 0) || {};
item._is = storeName; // Only do this for compiled objects (tags, templates...)
}
if (name) {
thisStore[name] = item;
}
}
if (onStore) {
// e.g. JsViews integration
onStore(name, item, parentTmpl, compile);
}
return item;
}
var storeNames = storeName + "s";
$views[storeNames] = theStore;
}
/**
* Add settings such as:
* $.views.settings.allowCode(true)
* @param {boolean} value
* @returns {Settings}
*
* allowCode = $.views.settings.allowCode()
* @returns {boolean}
*/
function addSetting(st) {
$viewsSettings[st] = $viewsSettings[st] || function(value) {
return arguments.length
? ($subSettings[st] = value, $viewsSettings)
: $subSettings[st];
};
}
//========================
// dataMap for render only
//========================
function dataMap(mapDef) {
function Map(source, options) {
this.tgt = mapDef.getTgt(source, options);
options.map = this;
}
if ($isFunction(mapDef)) {
// Simple map declared as function
mapDef = {
getTgt: mapDef
};
}
if (mapDef.baseMap) {
mapDef = $extend($extend({}, mapDef.baseMap), mapDef);
}
mapDef.map = function(source, options) {
return new Map(source, options);
};
return mapDef;
}
//==============
// renderContent
//==============
/** Render the template as a string, using the specified data and helpers/context
* $("#tmpl").render(), tmpl.render(), tagCtx.render(), $.render.namedTmpl()
*
* @param {any} data
* @param {hash} [context] helpers or context
* @param {boolean} [noIteration]
* @param {View} [parentView] internal
* @param {string} [key] internal
* @param {function} [onRender] internal
* @returns {string} rendered template internal
*/
function renderContent(data, context, noIteration, parentView, key, onRender) {
var i, l, tag, tmpl, tagCtx, isTopRenderCall, prevData, prevIndex,
view = parentView,
result = "";
if (context === true) {
noIteration = context; // passing boolean as second param - noIteration
context = undefined;
} else if (typeof context !== OBJECT) {
context = undefined; // context must be a boolean (noIteration) or a plain object
}
if (tag = this.tag) {
// This is a call from renderTag or tagCtx.render(...)
tagCtx = this;
view = view || tagCtx.view;
tmpl = view._getTmpl(tag.template || tagCtx.tmpl);
if (!arguments.length) {
data = tag.contentCtx && $isFunction(tag.contentCtx)
? data = tag.contentCtx(data)
: view; // Default data context for wrapped block content is the first argument
}
} else {
// This is a template.render(...) call
tmpl = this;
}
if (tmpl) {
if (!parentView && data && data._is === "view") {
view = data; // When passing in a view to render or link (and not passing in a parent view) use the passed-in view as parentView
}
if (view && data === view) {
// Inherit the data from the parent view.
data = view.data;
}
isTopRenderCall = !view;
isRenderCall = isRenderCall || isTopRenderCall;
if (isTopRenderCall) {
(context = context || {}).root = data; // Provide ~root as shortcut to top-level data.
}
if (!isRenderCall || $subSettingsAdvanced.useViews || tmpl.useViews || view && view !== topView) {
result = renderWithViews(tmpl, data, context, noIteration, view, key, onRender, tag);
} else {
if (view) { // In a block
prevData = view.data;
prevIndex = view.index;
view.index = indexStr;
} else {
view = topView;
prevData = view.data;
view.data = data;
view.ctx = context;
}
if ($isArray(data) && !noIteration) {
// Create a view for the array, whose child views correspond to each data item. (Note: if key and parentView are passed in
// along with parent view, treat as insert -e.g. from view.addViews - so parentView is already the view item for array)
for (i = 0, l = data.length; i < l; i++) {
view.index = i;
view.data = data[i];
result += tmpl.fn(data[i], view, $sub);
}
} else {
view.data = data;
result += tmpl.fn(data, view, $sub);
}
view.data = prevData;
view.index = prevIndex;
}
if (isTopRenderCall) {
isRenderCall = undefined;
}
}
return result;
}
function renderWithViews(tmpl, data, context, noIteration, view, key, onRender, tag) {
// Render template against data as a tree of subviews (nested rendered template instances), or as a string (top-level template).
// If the data is the parent view, treat as noIteration, re-render with the same data context.
// tmpl can be a string (e.g. rendered by a tag.render() method), or a compiled template.
var i, l, newView, childView, itemResult, swapContent, contentTmpl, outerOnRender, tmplName, itemVar, newCtx, tagCtx, noLinking,
result = "";
if (tag) {
// This is a call from renderTag or tagCtx.render(...)
tmplName = tag.tagName;
tagCtx = tag.tagCtx;
context = context ? extendCtx(context, tag.ctx) : tag.ctx;
if (tmpl === view.content) { // {{xxx tmpl=#content}}
contentTmpl = tmpl !== view.ctx._wrp // We are rendering the #content
? view.ctx._wrp // #content was the tagCtx.props.tmpl wrapper of the block content - so within this view, #content will now be the view.ctx._wrp block content
: undefined; // #content was the view.ctx._wrp block content - so within this view, there is no longer any #content to wrap.
} else if (tmpl !== tagCtx.content) {
if (tmpl === tag.template) { // Rendering {{tag}} tag.template, replacing block content.
contentTmpl = tagCtx.tmpl; // Set #content to block content (or wrapped block content if tagCtx.props.tmpl is set)
context._wrp = tagCtx.content; // Pass wrapped block content to nested views
} else { // Rendering tagCtx.props.tmpl wrapper
contentTmpl = tagCtx.content || view.content; // Set #content to wrapped block content
}
} else {
contentTmpl = view.content; // Nested views inherit same wrapped #content property
}
if (tagCtx.props.link === false) {
// link=false setting on block tag
// We will override inherited value of link by the explicit setting link=false taken from props
// The child views of an unlinked view are also unlinked. So setting child back to true will not have any effect.
context = context || {};
context.link = false;
}
}
if (view) {
onRender = onRender || view._.onRender;
noLinking = context && context.link === false;
if (noLinking && view._.nl) {
onRender = undefined;
}
context = extendCtx(context, view.ctx);
tagCtx = !tag && view.tag
? view.tag.tagCtxs[view.tagElse]
: tagCtx;
}
if (itemVar = tagCtx && tagCtx.props.itemVar) {
if (itemVar[0] !== "~") {
syntaxError("Use itemVar='~myItem'");
}
itemVar = itemVar.slice(1);
}
if (key === true) {
swapContent = true;
key = 0;
}
// If link===false, do not call onRender, so no data-linking marker nodes
if (onRender && tag && tag._.noVws) {
onRender = undefined;
}
outerOnRender = onRender;
if (onRender === true) {
// Used by view.refresh(). Don't create a new wrapper view.
outerOnRender = undefined;
onRender = view._.onRender;
}
// Set additional context on views created here, (as modified context inherited from the parent, and to be inherited by child views)
context = tmpl.helpers
? extendCtx(tmpl.helpers, context)
: context;
newCtx = context;
if ($isArray(data) && !noIteration) {
// Create a view for the array, whose child views correspond to each data item. (Note: if key and view are passed in
// along with parent view, treat as insert -e.g. from view.addViews - so view is already the view item for array)
newView = swapContent
? view
: (key !== undefined && view)
|| new View(context, "array", view, data, tmpl, key, onRender, contentTmpl);
newView._.nl= noLinking;
if (view && view._.useKey) {
// Parent is not an 'array view'
newView._.bnd = !tag || tag._.bnd && tag; // For array views that are data bound for collection change events, set the
// view._.bnd property to true for top-level link() or data-link="{for}", or to the tag instance for a data-bound tag, e.g. {^{for ...}}
newView.tag = tag;
}
for (i = 0, l = data.length; i < l; i++) {
// Create a view for each data item.
childView = new View(newCtx, "item", newView, data[i], tmpl, (key || 0) + i, onRender, newView.content);
if (itemVar) {
(childView.ctx = $extend({}, newCtx))[itemVar] = $sub._cp(data[i], "#data", childView);
}
itemResult = tmpl.fn(data[i], childView, $sub);
result += newView._.onRender ? newView._.onRender(itemResult, childView) : itemResult;
}
} else {
// Create a view for singleton data object. The type of the view will be the tag name, e.g. "if" or "mytag" except for
// "item", "array" and "data" views. A "data" view is from programmatic render(object) against a 'singleton'.
newView = swapContent ? view : new View(newCtx, tmplName || "data", view, data, tmpl, key, onRender, contentTmpl);
if (itemVar) {
(newView.ctx = $extend({}, newCtx))[itemVar] = $sub._cp(data, "#data", newView);
}
newView.tag = tag;
newView._.nl = noLinking;
result += tmpl.fn(data, newView, $sub);
}
if (tag) {
newView.tagElse = tagCtx.index;
tagCtx.contentView = newView;
}
return outerOnRender ? outerOnRender(result, newView) : result;
}
//===========================
// Build and compile template
//===========================
// Generate a reusable function that will serve to render a template against data
// (Compile AST then build template function)
function onRenderError(e, view, fallback) {
var message = fallback !== undefined
? $isFunction(fallback)
? fallback.call(view.data, e, view)
: fallback || ""
: "{Error: " + (e.message||e) + "}";
if ($subSettings.onError && (fallback = $subSettings.onError.call(view.data, e, fallback && message, view)) !== undefined) {
message = fallback; // There is a settings.debugMode(handler) onError override. Call it, and use return value (if any) to replace message
}
return view && !view._lc ? $converters.html(message) : message; // For data-link=\"{... onError=...}"... See onDataLinkedTagChange
}
function error(message) {
throw new $sub.Err(message);
}
function syntaxError(message) {
error("Syntax error\n" + message);
}
function tmplFn(markup, tmpl, isLinkExpr, convertBack, hasElse) {
// Compile markup to AST (abtract syntax tree) then build the template function code from the AST nodes
// Used for compiling templates, and also by JsViews to build functions for data link expressions
//==== nested functions ====
function pushprecedingContent(shift) {
shift -= loc;
if (shift) {
content.push(markup.substr(loc, shift).replace(rNewLine, "\\n"));
}
}
function blockTagCheck(tagName, block) {
if (tagName) {
tagName += '}}';
// '{{include}} block has {{/for}} with no open {{for}}'
syntaxError((
block
? '{{' + block + '}} block has {{/' + tagName + ' without {{' + tagName
: 'Unmatched or missing {{/' + tagName) + ', in template:\n' + markup);
}
}
function parseTag(all, bind, tagName, converter, colon, html, codeTag, params, slash, bind2, closeBlock, index) {
/*
bind tagName cvt cln html code params slash bind2 closeBlk comment
/(?:{(\^)?{(?:(\w+(?=[\/\s}]))|(\w+)?(:)|(>)|(\*))\s*((?:[^}]|}(?!}))*?)(\/)?|{(\^)?{(?:(?:\/(\w+))\s*|!--[\s\S]*?--))}}/g
(?:
{(\^)?{ bind
(?:
(\w+ tagName
(?=[\/\s}])
)
|
(\w+)?(:) converter colon
|
(>) html
|
(\*) codeTag
)
\s*
( params
(?:[^}]|}(?!}))*?
)
(\/)? slash
|
{(\^)?{ bind2
(?:
(?:\/(\w+))\s* closeBlock
|
!--[\s\S]*?-- comment
)
)
}}/g
*/
if (codeTag && bind || slash && !tagName || params && params.slice(-1) === ":" || bind2) {
syntaxError(all);
}
// Build abstract syntax tree (AST): [tagName, converter, params, content, hash, bindings, contentMarkup]
if (html) {
colon = ":";
converter = HTML;
}
slash = slash || isLinkExpr && !hasElse;
var late, openTagName, isLateOb,
pathBindings = (bind || isLinkExpr) && [[]], // pathBindings is an array of arrays for arg bindings and a hash of arrays for prop bindings
props = "",
args = "",
ctxProps = "",
paramsArgs = "",
paramsProps = "",
paramsCtxProps = "",
onError = "",
useTrigger = "",
// Block tag if not self-closing and not {{:}} or {{>}} (special case) and not a data-link expression
block = !slash && !colon;
//==== nested helper function ====
tagName = tagName || (params = params || "#data", colon); // {{:}} is equivalent to {{:#data}}
pushprecedingContent(index);
loc = index + all.length; // location marker - parsed up to here
if (codeTag) {
if (allowCode) {
content.push(["*", "\n" + params.replace(/^:/, "ret+= ").replace(rUnescapeQuotes, "$1") + ";\n"]);
}
} else if (tagName) {
if (tagName === "else") {
if (rTestElseIf.test(params)) {
syntaxError('For "{{else if expr}}" use "{{else expr}}"');
}
pathBindings = current[9] && [[]];
current[10] = markup.substring(current[10], index); // contentMarkup for block tag
openTagName = current[11] || current[0] || syntaxError("Mismatched: " + all);
// current[0] is tagName, but for {{else}} nodes, current[11] is tagName of preceding open tag
current = stack.pop();
content = current[2];
block = true;
}
if (params) {
// remove newlines from the params string, to avoid compiled code errors for unterminated strings
parseParams(params.replace(rNewLine, " "), pathBindings, tmpl, isLinkExpr)
.replace(rBuildHash, function(all, onerror, isCtxPrm, key, keyToken, keyValue, arg, param) {
if (key === "this:") {
keyValue = "undefined"; // this=some.path is always a to parameter (one-way), so don't need to compile/evaluate some.path initialization
}
if (param) {
isLateOb = isLateOb || param[0] === "@";
}
key = "'" + keyToken + "':";
if (arg) {
args += isCtxPrm + keyValue + ",";
paramsArgs += "'" + param + "',";
} else if (isCtxPrm) { // Contextual parameter, ~foo=expr
ctxProps += key + 'j._cp(' + keyValue + ',"' + param + '",view),';
// Compiled code for evaluating tagCtx on a tag will have: ctx:{'foo':j._cp(compiledExpr, "expr", view)}
paramsCtxProps += key + "'" + param + "',";
} else if (onerror) {
onError += keyValue;
} else {
if (keyToken === "trigger") {
useTrigger += keyValue;
}
if (keyToken === "lateRender") {
late = param !== "false"; // Render after first pass
}
props += key + keyValue + ",";
paramsProps += key + "'" + param + "',";
hasHandlers = hasHandlers || rHasHandlers.test(keyToken);
}
return "";
}).slice(0, -1);
}
if (pathBindings && pathBindings[0]) {
pathBindings.pop(); // Remove the binding that was prepared for next arg. (There is always an extra one ready).
}
newNode = [
tagName,
converter || !!convertBack || hasHandlers || "",
block && [],
parsedParam(paramsArgs || (tagName === ":" ? "'#data'," : ""), paramsProps, paramsCtxProps), // {{:}} equivalent to {{:#data}}
parsedParam(args || (tagName === ":" ? "data," : ""), props, ctxProps),
onError,
useTrigger,
late,
isLateOb,
pathBindings || 0
];
content.push(newNode);
if (block) {
stack.push(current);
current = newNode;
current[10] = loc; // Store current location of open tag, to be able to add contentMarkup when we reach closing tag
current[11] = openTagName; // Used for checking syntax (matching close tag)
}
} else if (closeBlock) {
blockTagCheck(closeBlock !== current[0] && closeBlock !== current[11] && closeBlock, current[0]); // Check matching close tag name
current[10] = markup.substring(current[10], index); // contentMarkup for block tag
current = stack.pop();
}
blockTagCheck(!current && closeBlock);
content = current[2];
}
//==== /end of nested functions ====
var i, result, newNode, hasHandlers, bindings,
allowCode = $subSettings.allowCode || tmpl && tmpl.allowCode
|| $viewsSettings.allowCode === true, // include direct setting of settings.allowCode true for backward compat only
astTop = [],
loc = 0,
stack = [],
content = astTop,
current = [,,astTop];
if (allowCode && tmpl._is) {
tmpl.allowCode = allowCode;
}
//TODO result = tmplFnsCache[markup]; // Only cache if template is not named and markup length < ...,
//and there are no bindings or subtemplates?? Consider standard optimization for data-link="a.b.c"
// if (result) {
// tmpl.fn = result;
// } else {
// result = markup;
if (isLinkExpr) {
if (convertBack !== undefined) {
markup = markup.slice(0, -convertBack.length - 2) + delimCloseChar0;
}
markup = delimOpenChar0 + markup + delimCloseChar1;
}
blockTagCheck(stack[0] && stack[0][2].pop()[0]);
// Build the AST (abstract syntax tree) under astTop
markup.replace(rTag, parseTag);
pushprecedingContent(markup.length);
if (loc = astTop[astTop.length - 1]) {
blockTagCheck("" + loc !== loc && (+loc[10] === loc[10]) && loc[0]);
}
// result = tmplFnsCache[markup] = buildCode(astTop, tmpl);
// }
if (isLinkExpr) {
result = buildCode(astTop, markup, isLinkExpr);
bindings = [];
i = astTop.length;
while (i--) {
bindings.unshift(astTop[i][9]); // With data-link expressions, pathBindings array for tagCtx[i] is astTop[i][9]
}
setPaths(result, bindings);
} else {
result = buildCode(astTop, tmpl);
}
return result;
}
function setPaths(fn, pathsArr) {
var key, paths,
i = 0,
l = pathsArr.length;
fn.deps = [];
fn.paths = []; // The array of path binding (array/dictionary)s for each tag/else block's args and props
for (; i < l; i++) {
fn.paths.push(paths = pathsArr[i]);
for (key in paths) {
if (key !== "_jsvto" && paths.hasOwnProperty(key) && paths[key].length && !paths[key].skp) {
fn.deps = fn.deps.concat(paths[key]); // deps is the concatenation of the paths arrays for the different bindings
}
}
}
}
function parsedParam(args, props, ctx) {
return [args.slice(0, -1), props.slice(0, -1), ctx.slice(0, -1)];
}
function paramStructure(paramCode, paramVals) {
return '\n\tparams:{args:[' + paramCode[0] + '],\n\tprops:{' + paramCode[1] + '}'
+ (paramCode[2] ? ',\n\tctx:{' + paramCode[2] + '}' : "")
+ '},\n\targs:[' + paramVals[0] + '],\n\tprops:{' + paramVals[1] + '}'
+ (paramVals[2] ? ',\n\tctx:{' + paramVals[2] + '}' : "");
}
function parseParams(params, pathBindings, tmpl, isLinkExpr) {
function parseTokens(all, lftPrn0, lftPrn, bound, path, operator, err, eq, path2, late, prn,
comma, lftPrn2, apos, quot, rtPrn, rtPrnDot, prn2, space, index, full) {
// /(\()(?=\s*\()|(?:([([])\s*)?(?:(\^?)(~?[\w$.^]+)?\s*((\+\+|--)|\+|-|~(?![\w$])|&&|\|\||===|!==|==|!=|<=|>=|[<>%*:?\/]|(=))\s*|(!*?(@)?[#~]?[\w$.^]+)([([])?)|(,\s*)|(?:(\()\s*)?\\?(?:(')|("))|(?:\s*(([)\]])(?=[.^]|\s*$|[^([])|[)\]])([([]?))|(\s+)/g,
//lftPrn0 lftPrn bound path operator err eq path2 late prn comma lftPrn2 apos quot rtPrn rtPrnDot prn2 space
// (left paren? followed by (path? followed by operator) or (path followed by paren?)) or comma or apos or quot or right paren or space
function parsePath(allPath, not, object, helper, view, viewProperty, pathTokens, leafToken) {
// /^(!*?)(?:null|true|false|\d[\d.]*|([\w$]+|\.|~([\w$]+)|#(view|([\w$]+))?)([\w$.^]*?)(?:[.[^]([\w$]+)\]?)?)$/g,
// not object helper view viewProperty pathTokens leafToken
subPath = object === ".";
if (object) {
path = path.slice(not.length);
if (/^\.?constructor$/.test(leafToken||path)) {
syntaxError(allPath);
}
if (!subPath) {
allPath = (late // late path @a.b.c: not throw on 'property of undefined' if a undefined, and will use _getOb() after linking to resolve late.
? (isLinkExpr ? '' : '(ltOb.lt=ltOb.lt||') + '(ob='
: ""
)
+ (helper
? 'view.ctxPrm("' + helper + '")'
: view
? "view"
: "data")
+ (late
? ')===undefined' + (isLinkExpr ? '' : ')') + '?"":view._getOb(ob,"'
: ""
)
+ (leafToken
? (viewProperty
? "." + viewProperty
: helper
? ""
: (view ? "" : "." + object)
) + (pathTokens || "")
: (leafToken = helper ? "" : view ? viewProperty || "" : object, ""));
allPath = allPath + (leafToken ? "." + leafToken : "");
allPath = not + (allPath.slice(0, 9) === "view.data"
? allPath.slice(5) // convert #view.data... to data...
: allPath)
+ (late
? (isLinkExpr ? '"': '",ltOb') + (prn ? ',1)':')')
: ""
);
}
if (bindings) {
binds = named === "_linkTo" ? (bindto = pathBindings._jsvto = pathBindings._jsvto || []) : bndCtx.bd;
if (theOb = subPath && binds[binds.length-1]) {
if (theOb._cpfn) { // Computed property exprOb
while (theOb.sb) {
theOb = theOb.sb;
}
if (theOb.prm) {
if (theOb.bnd) {
path = "^" + path.slice(1);
}
theOb.sb = path;
theOb.bnd = theOb.bnd || path[0] === "^";
}
}
} else {
binds.push(path);
}
if (prn && !subPath) {
pathStart[fnDp] = ind;
compiledPathStart[fnDp] = compiledPath[fnDp].length;
}
}
}
return allPath;
}
//bound = bindings && bound;
if (bound && !eq) {
path = bound + path; // e.g. some.fn(...)^some.path - so here path is "^some.path"
}
operator = operator || "";
lftPrn2 = lftPrn2 || "";
lftPrn = lftPrn || lftPrn0 || lftPrn2;
path = path || path2;
if (late && (late = !/\)|]/.test(full[index-1]))) {
path = path.slice(1).split(".").join("^"); // Late path @z.b.c. Use "^" rather than "." to ensure that deep binding will be used
}
// Could do this - but not worth perf cost?? :-
// if (!path.lastIndexOf("#data.", 0)) { path = path.slice(6); } // If path starts with "#data.", remove that.
prn = prn || prn2 || "";
var expr, binds, theOb, newOb, subPath, lftPrnFCall, ret,
ind = index;
if (!aposed && !quoted) {
if (err) {
syntaxError(params);
}
if (rtPrnDot && bindings) {
// This is a binding to a path in which an object is returned by a helper/data function/expression, e.g. foo()^x.y or (a?b:c)^x.y
// We create a compiled function to get the object instance (which will be called when the dependent data of the subexpression changes, to return the new object, and trigger re-binding of the subsequent path)
expr = pathStart[fnDp-1];
if (full.length - 1 > ind - (expr || 0)) { // We need to compile a subexpression
expr = $.trim(full.slice(expr, ind + all.length));
binds = bindto || bndStack[fnDp-1].bd;
// Insert exprOb object, to be used during binding to return the computed object
theOb = binds[binds.length-1];
if (theOb && theOb.prm) {
while (theOb.sb && theOb.sb.prm) {
theOb = theOb.sb;
}
newOb = theOb.sb = {path: theOb.sb, bnd: theOb.bnd};
} else {
binds.push(newOb = {path: binds.pop()}); // Insert exprOb object, to be used during binding to return the computed object
}
if (theOb && theOb.sb === newOb) {
compiledPath[fnDp] = compiledPath[fnDp-1].slice(theOb._cpPthSt) + compiledPath[fnDp];
compiledPath[fnDp-1] = compiledPath[fnDp-1].slice(0, theOb._cpPthSt);
}
newOb._cpPthSt = compiledPathStart[fnDp-1];
newOb._cpKey = expr;
compiledPath[fnDp] += full.slice(prevIndex, index);
prevIndex = index;
newOb._cpfn = cpFnStore[expr] = cpFnStore[expr] || // Compiled function for computed value: get from store, or compile and store
new Function("data,view,j", // Compiled function for computed value in template
"//" + expr + "\nvar v;\nreturn ((v=" + compiledPath[fnDp] + (rtPrn === "]" ? ")]" : rtPrn) + ")!=null?v:null);");
compiledPath[fnDp-1] += (fnCall[prnDp] && $subSettingsAdvanced.cache ? "view.getCache(\"" + expr.replace(rEscapeQuotes, "\\$&") + "\"" : compiledPath[fnDp]);
newOb.prm = bndCtx.bd;
newOb.bnd = newOb.bnd || newOb.path && newOb.path.indexOf("^") >= 0;
}
compiledPath[fnDp] = "";
}
if (prn === "[") {
prn = "[j._sq(";
}
if (lftPrn === "[") {
lftPrn = "[j._sq(";
}
}
ret = (aposed
// within single-quoted string
? (aposed = !apos, (aposed ? all : lftPrn2 + '"'))
: quoted
// within double-quoted string
? (quoted = !quot, (quoted ? all : lftPrn2 + '"'))
:
(
(lftPrn
? (
prnStack[++prnDp] = true,
prnInd[prnDp] = 0,
bindings && (
pathStart[fnDp++] = ind++,
bndCtx = bndStack[fnDp] = {bd: []},
compiledPath[fnDp] = "",
compiledPathStart[fnDp] = 1
),
lftPrn) // Left paren, (not a function call paren)
: "")
+ (space
? (prnDp
? "" // A space within parens or within function call parens, so not a separator for tag args
// New arg or prop - so insert backspace \b (\x08) as separator for named params, used subsequently by rBuildHash, and prepare new bindings array
: (paramIndex = full.slice(paramIndex, ind), named
? (named = boundName = bindto = false, "\b")
: "\b,") + paramIndex + (paramIndex = ind + all.length, bindings && pathBindings.push(bndCtx.bd = []), "\b")
)
: eq
// named param. Remove bindings for arg and create instead bindings array for prop
? (fnDp && syntaxError(params), bindings && pathBindings.pop(), named = "_" + path, boundName = bound, paramIndex = ind + all.length,
bindings && ((bindings = bndCtx.bd = pathBindings[named] = []), bindings.skp = !bound), path + ':')
: path
// path
? (path.split("^").join(".").replace($sub.rPath, parsePath)
+ (prn || operator)
)
: operator
// operator
? operator
: rtPrn
// function
? rtPrn === "]" ? ")]" : ")"
: comma
? (fnCall[prnDp] || syntaxError(params), ",") // We don't allow top-level literal arrays or objects
: lftPrn0
? ""
: (aposed = apos, quoted = quot, '"')
))
);
if (!aposed && !quoted) {
if (rtPrn) {
fnCall[prnDp] = false;
prnDp--;
}
}
if (bindings) {
if (!aposed && !quoted) {
if (rtPrn) {
if (prnStack[prnDp+1]) {
bndCtx = bndStack[--fnDp];
prnStack[prnDp+1] = false;
}
prnStart = prnInd[prnDp+1];
}
if (prn) {
prnInd[prnDp+1] = compiledPath[fnDp].length + (lftPrn ? 1 : 0);
if (path || rtPrn) {
bndCtx = bndStack[++fnDp] = {bd: []};
prnStack[prnDp+1] = true;
}
}
}
compiledPath[fnDp] = (compiledPath[fnDp]||"") + full.slice(prevIndex, index);
prevIndex = index+all.length;
if (!aposed && !quoted) {
if (lftPrnFCall = lftPrn && prnStack[prnDp+1]) {
compiledPath[fnDp-1] += lftPrn;
compiledPathStart[fnDp-1]++;
}
if (prn === "(" && subPath && !newOb) {
compiledPath[fnDp] = compiledPath[fnDp-1].slice(prnStart) + compiledPath[fnDp];
compiledPath[fnDp-1] = compiledPath[fnDp-1].slice(0, prnStart);
}
}
compiledPath[fnDp] += lftPrnFCall ? ret.slice(1) : ret;
}
if (!aposed && !quoted && prn) {
prnDp++;
if (path && prn === "(") {
fnCall[prnDp] = true;
}
}
if (!aposed && !quoted && prn2) {
if (bindings) {
compiledPath[fnDp] += prn;
}
ret += prn;
}
return ret;
}
var named, bindto, boundName, result,
quoted, // boolean for string content in double quotes
aposed, // or in single quotes
bindings = pathBindings && pathBindings[0], // bindings array for the first arg
bndCtx = {bd: bindings},
bndStack = {0: bndCtx},
paramIndex = 0, // list,
// The following are used for tracking path parsing including nested paths, such as "a.b(c^d + (e))^f", and chained computed paths such as
// "a.b().c^d().e.f().g" - which has four chained paths, "a.b()", "^c.d()", ".e.f()" and ".g"
prnDp = 0, // For tracking paren depth (not function call parens)
fnDp = 0, // For tracking depth of function call parens
prnInd = {}, // We are in a function call
prnStart = 0, // tracks the start of the current path such as c^d() in the above example
prnStack = {}, // tracks parens which are not function calls, and so are associated with new bndStack contexts
fnCall = {}, // We are in a function call
pathStart = {},// tracks the start of the current path such as c^d() in the above example
compiledPathStart = {0: 0},
compiledPath = {0:""},
prevIndex = 0;
if (params[0] === "@") {
params = params.replace(rBracketQuote, ".");
}
result = (params + (tmpl ? " " : "")).replace($sub.rPrm, parseTokens);
if (bindings) {
result = compiledPath[0];
}
return !prnDp && result || syntaxError(params); // Syntax error if unbalanced parens in params expression
}
function buildCode(ast, tmpl, isLinkExpr) {
// Build the template function code from the AST nodes, and set as property on the passed-in template object
// Used for compiling templates, and also by JsViews to build functions for data link expressions
var i, node, tagName, converter, tagCtx, hasTag, hasEncoder, getsVal, hasCnvt, useCnvt, tmplBindings, pathBindings, params, boundOnErrStart,
boundOnErrEnd, tagRender, nestedTmpls, tmplName, nestedTmpl, tagAndElses, content, markup, nextIsElse, oldCode, isElse, isGetVal, tagCtxFn,
onError, tagStart, trigger, lateRender, retStrOpen, retStrClose,
tmplBindingKey = 0,
useViews = $subSettingsAdvanced.useViews || tmpl.useViews || tmpl.tags || tmpl.templates || tmpl.helpers || tmpl.converters,
code = "",
tmplOptions = {},
l = ast.length;
if ("" + tmpl === tmpl) {
tmplName = isLinkExpr ? 'data-link="' + tmpl.replace(rNewLine, " ").slice(1, -1) + '"' : tmpl;
tmpl = 0;
} else {
tmplName = tmpl.tmplName || "unnamed";
if (tmpl.allowCode) {
tmplOptions.allowCode = true;
}
if (tmpl.debug) {
tmplOptions.debug = true;
}
tmplBindings = tmpl.bnds;
nestedTmpls = tmpl.tmpls;
}
for (i = 0; i < l; i++) {
// AST nodes: [0: tagName, 1: converter, 2: content, 3: params, 4: code, 5: onError, 6: trigger, 7:pathBindings, 8: contentMarkup]
node = ast[i];
// Add newline for each callout to t() c() etc. and each markup string
if ("" + node === node) {
// a markup string to be inserted
code += '+"' + node + '"';
} else {
// a compiled tag expression to be inserted
tagName = node[0];
if (tagName === "*") {
// Code tag: {{* }}
code += ";\n" + node[1] + "\nret=ret";
} else {
converter = node[1];
content = !isLinkExpr && node[2];
tagCtx = paramStructure(node[3], params = node[4]);
trigger = node[6];
lateRender = node[7];
if (node[8]) { // latePath @a.b.c or @~a.b.c
retStrOpen = "\nvar ob,ltOb={},ctxs=";
retStrClose = ";\nctxs.lt=ltOb.lt;\nreturn ctxs;";
} else {
retStrOpen = "\nreturn ";
retStrClose = "";
}
markup = node[10] && node[10].replace(rUnescapeQuotes, "$1");
if (isElse = tagName === "else") {
if (pathBindings) {
pathBindings.push(node[9]);
}
} else {
onError = node[5] || $subSettings.debugMode !== false && "undefined"; // If debugMode not false, set default onError handler on tag to "undefined" (see onRenderError)
if (tmplBindings && (pathBindings = node[9])) { // Array of paths, or false if not data-bound
pathBindings = [pathBindings];
tmplBindingKey = tmplBindings.push(1); // Add placeholder in tmplBindings for compiled function
}
}
useViews = useViews || params[1] || params[2] || pathBindings || /view.(?!index)/.test(params[0]);
// useViews is for perf optimization. For render() we only use views if necessary - for the more advanced scenarios.
// We use views if there are props, contextual properties or args with #... (other than #index) - but you can force
// using the full view infrastructure, (and pay a perf price) by opting in: Set useViews: true on the template, manually...
if (isGetVal = tagName === ":") {
if (converter) {
tagName = converter === HTML ? ">" : converter + tagName;
}
} else {
if (content) { // TODO optimize - if content.length === 0 or if there is a tmpl="..." specified - set content to null / don't run this compilation code - since content won't get used!!
// Create template object for nested template
nestedTmpl = tmplObject(markup, tmplOptions);
nestedTmpl.tmplName = tmplName + "/" + tagName;
// Compile to AST and then to compiled function
nestedTmpl.useViews = nestedTmpl.useViews || useViews;
buildCode(content, nestedTmpl);
useViews = nestedTmpl.useViews;
nestedTmpls.push(nestedTmpl);
}
if (!isElse) {
// This is not an else tag.
tagAndElses = tagName;
useViews = useViews || tagName && (!$tags[tagName] || !$tags[tagName].flow);
// Switch to a new code string for this bound tag (and its elses, if it has any) - for returning the tagCtxs array
oldCode = code;
code = "";
}
nextIsElse = ast[i + 1];
nextIsElse = nextIsElse && nextIsElse[0] === "else";
}
tagStart = onError ? ";\ntry{\nret+=" : "\n+";
boundOnErrStart = "";
boundOnErrEnd = "";
if (isGetVal && (pathBindings || trigger || converter && converter !== HTML || lateRender)) {
// For convertVal we need a compiled function to return the new tagCtx(s)
tagCtxFn = new Function("data,view,j", "// " + tmplName + " " + (++tmplBindingKey) + " " + tagName
+ retStrOpen + "{" + tagCtx + "};" + retStrClose);
tagCtxFn._er = onError;
tagCtxFn._tag = tagName;
tagCtxFn._bd = !!pathBindings; // data-linked tag {^{.../}}
tagCtxFn._lr = lateRender;
if (isLinkExpr) {
return tagCtxFn;
}
setPaths(tagCtxFn, pathBindings);
tagRender = 'c("' + converter + '",view,';
useCnvt = true;
boundOnErrStart = tagRender + tmplBindingKey + ",";
boundOnErrEnd = ")";
}
code += (isGetVal
? (isLinkExpr ? (onError ? "try{\n" : "") + "return " : tagStart) + (useCnvt // Call _cnvt if there is a converter: {{cnvt: ... }} or {^{cnvt: ... }}
? (useCnvt = undefined, useViews = hasCnvt = true, tagRender + (tagCtxFn
? ((tmplBindings[tmplBindingKey - 1] = tagCtxFn), tmplBindingKey) // Store the compiled tagCtxFn in tmpl.bnds, and pass the key to convertVal()
: "{" + tagCtx + "}") + ")")
: tagName === ">"
? (hasEncoder = true, "h(" + params[0] + ")")
: (getsVal = true, "((v=" + params[0] + ')!=null?v:' + (isLinkExpr ? 'null)' : '"")'))
// Non strict equality so data-link="title{:expr}" with expr=null/undefined removes title attribute
)
: (hasTag = true, "\n{view:view,content:false,tmpl:" // Add this tagCtx to the compiled code for the tagCtxs to be passed to renderTag()
+ (content ? nestedTmpls.length : "false") + "," // For block tags, pass in the key (nestedTmpls.length) to the nested content template
+ tagCtx + "},"));
if (tagAndElses && !nextIsElse) {
// This is a data-link expression or an inline tag without any elses, or the last {{else}} of an inline tag
// We complete the code for returning the tagCtxs array
code = "[" + code.slice(0, -1) + "]";
tagRender = 't("' + tagAndElses + '",view,this,';
if (isLinkExpr || pathBindings) {
// This is a bound tag (data-link expression or inline bound tag {^{tag ...}}) so we store a compiled tagCtxs function in tmp.bnds
code = new Function("data,view,j", " // " + tmplName + " " + tmplBindingKey + " " + tagAndElses + retStrOpen + code
+ retStrClose);
code._er = onError;
code._tag = tagAndElses;
if (pathBindings) {
setPaths(tmplBindings[tmplBindingKey - 1] = code, pathBindings);
}
code._lr = lateRender;
if (isLinkExpr) {
return code; // For a data-link expression we return the compiled tagCtxs function
}
boundOnErrStart = tagRender + tmplBindingKey + ",undefined,";
boundOnErrEnd = ")";
}
// This is the last {{else}} for an inline tag.
// For a bound tag, pass the tagCtxs fn lookup key to renderTag.
// For an unbound tag, include the code directly for evaluating tagCtxs array
code = oldCode + tagStart + tagRender + (pathBindings && tmplBindingKey || code) + ")";
pathBindings = 0;
tagAndElses = 0;
}
if (onError && !nextIsElse) {
useViews = true;
code += ';\n}catch(e){ret' + (isLinkExpr ? "urn " : "+=") + boundOnErrStart + 'j._err(e,view,' + onError + ')' + boundOnErrEnd + ';}' + (isLinkExpr ? "" : '\nret=ret');
}
}
}
}
// Include only the var references that are needed in the code
code = "// " + tmplName
+ (tmplOptions.debug ? "\ndebugger;" : "")
+ "\nvar v"
+ (hasTag ? ",t=j._tag" : "") // has tag
+ (hasCnvt ? ",c=j._cnvt" : "") // converter
+ (hasEncoder ? ",h=j._html" : "") // html converter
+ (isLinkExpr
? (node[8] // late @... path?
? ", ob"
: ""
) + ";\n"
: ',ret=""')
+ code
+ (isLinkExpr ? "\n" : ";\nreturn ret;");
try {
code = new Function("data,view,j", code);
} catch (e) {
syntaxError("Compiled template code:\n\n" + code + '\n: "' + (e.message||e) + '"');
}
if (tmpl) {
tmpl.fn = code;
tmpl.useViews = !!useViews;
}
return code;
}
//==========
// Utilities
//==========
// Merge objects, in particular contexts which inherit from parent contexts
function extendCtx(context, parentContext) {
// Return copy of parentContext, unless context is defined and is different, in which case return a new merged context
// If neither context nor parentContext are defined, return undefined
return context && context !== parentContext
? (parentContext
? $extend($extend({}, parentContext), context)
: context)
: parentContext && $extend({}, parentContext);
}
function getTargetProps(source, tagCtx) {
// this pointer is theMap - which has tagCtx.props too
// arguments: tagCtx.args.
var key, prop,
map = tagCtx.map,
propsArr = map && map.propsArr;
if (!propsArr) { // map.propsArr is the full array of {key:..., prop:...} objects
propsArr = [];
if (typeof source === OBJECT || $isFunction(source)) {
for (key in source) {
prop = source[key];
if (key !== $expando && source.hasOwnProperty(key) && (!tagCtx.props.noFunctions || !$.isFunction(prop))) {
propsArr.push({key: key, prop: prop});
}
}
}
if (map) {
map.propsArr = map.options && propsArr; // If bound {^{props}} and not isRenderCall, store propsArr on map (map.options is defined only for bound, && !isRenderCall)
}
}
return getTargetSorted(propsArr, tagCtx); // Obtains map.tgt, by filtering, sorting and splicing the full propsArr
}
function getTargetSorted(value, tagCtx) {
// getTgt
var mapped, start, end,
tag = tagCtx.tag,
props = tagCtx.props,
propParams = tagCtx.params.props,
filter = props.filter,
sort = props.sort,
directSort = sort === true,
step = parseInt(props.step),
reverse = props.reverse ? -1 : 1;
if (!$isArray(value)) {
return value;
}
if (directSort || sort && "" + sort === sort) {
// Temporary mapped array holds objects with index and sort-value
mapped = value.map(function(item, i) {
item = directSort ? item : getPathObject(item, sort);
return {i: i, v: "" + item === item ? item.toLowerCase() : item};
});
// Sort mapped array
mapped.sort(function(a, b) {
return a.v > b.v ? reverse : a.v < b.v ? -reverse : 0;
});
// Map to new array with resulting order
value = mapped.map(function(item){
return value[item.i];
});
} else if ((sort || reverse < 0) && !tag.dataMap) {
value = value.slice(); // Clone array first if not already a new array
}
if ($isFunction(sort)) {
value = value.sort(function() { // Wrap the sort function to provide tagCtx as 'this' pointer
return sort.apply(tagCtx, arguments);
});
}
if (reverse < 0 && (!sort || $isFunction(sort))) { // Reverse result if not already reversed in sort
value = value.reverse();
}
if (value.filter && filter) { // IE8 does not support filter
value = value.filter(filter, tagCtx);
if (tagCtx.tag.onFilter) {
tagCtx.tag.onFilter(tagCtx);
}
}
if (propParams.sorted) {
mapped = (sort || reverse < 0) ? value : value.slice();
if (tag.sorted) {
$.observable(tag.sorted).refresh(mapped); // Note that this might cause the start and end props to be modified - e.g. by pager tag control
} else {
tagCtx.map.sorted = mapped;
}
}
start = props.start; // Get current value - after possible changes triggered by tag.sorted refresh() above
end = props.end;
if (propParams.start && start === undefined || propParams.end && end === undefined) {
start = end = 0;
}
if (!isNaN(start) || !isNaN(end)) { // start or end specified, but not the auto-create Number array scenario of {{for start=xxx end=yyy}}
start = +start || 0;
end = end === undefined || end > value.length ? value.length : +end;
value = value.slice(start, end);
}
if (step > 1) {
start = 0;
end = value.length;
mapped = [];
for (; start<end; start+=step) {
mapped.push(value[start]);
}
value = mapped;
}
if (propParams.paged && tag.paged) {
$observable(tag.paged).refresh(value);
}
return value;
}
/** Render the template as a string, using the specified data and helpers/context
* $("#tmpl").render()
*
* @param {any} data
* @param {hash} [helpersOrContext]
* @param {boolean} [noIteration]
* @returns {string} rendered template
*/
function $fnRender(data, context, noIteration) {
var tmplElem = this.jquery && (this[0] || error('Unknown template')), // Targeted element not found for jQuery template selector such as "#myTmpl"
tmpl = tmplElem.getAttribute(tmplAttr);
return renderContent.call(tmpl && $.data(tmplElem)[jsvTmpl] || $templates(tmplElem),
data, context, noIteration);
}
//========================== Register converters ==========================
function getCharEntity(ch) {
// Get character entity for HTML, Attribute and optional data encoding
return charEntities[ch] || (charEntities[ch] = "&#" + ch.charCodeAt(0) + ";");
}
function getCharFromEntity(match, token) {
// Get character from HTML entity, for optional data unencoding
return charsFromEntities[token] || "";
}
function htmlEncode(text) {
// HTML encode: Replace < > & ' " ` etc. by corresponding entities.
return text != undefined ? rIsHtml.test(text) && ("" + text).replace(rHtmlEncode, getCharEntity) || text : "";
}
function dataEncode(text) {
// Encode just < > and & - intended for 'safe data' along with {{:}} rather than {{>}}
return "" + text === text ? text.replace(rDataEncode, getCharEntity) : text;
}
function dataUnencode(text) {
// Unencode just < > and & - intended for 'safe data' along with {{:}} rather than {{>}}
return "" + text === text ? text.replace(rDataUnencode, getCharFromEntity) : text;
}
//========================== Initialize ==========================
$sub = $views.sub;
$viewsSettings = $views.settings;
if (!(jsr || $ && $.render)) {
// JsRender/JsViews not already loaded (or loaded without jQuery, and we are now moving from jsrender namespace to jQuery namepace)
for (jsvStoreName in jsvStores) {
registerStore(jsvStoreName, jsvStores[jsvStoreName]);
}
$converters = $views.converters;
$helpers = $views.helpers;
$tags = $views.tags;
$sub._tg.prototype = {
baseApply: baseApply,
cvtArgs: convertArgs,
bndArgs: convertBoundArgs,
ctxPrm: contextParameter
};
topView = $sub.topView = new View();
//BROWSER-SPECIFIC CODE
if ($) {
////////////////////////////////////////////////////////////////////////////////////////////////
// jQuery (= $) is loaded
$.fn.render = $fnRender;
$expando = $.expando;
if ($.observable) {
if (versionNumber !== (versionNumber = $.views.jsviews)) {
// Different version of jsRender was loaded
throw "jquery.observable.js requires jsrender.js " + versionNumber;
}
$extend($sub, $.views.sub); // jquery.observable.js was loaded before jsrender.js
$views.map = $.views.map;
}
} else {
////////////////////////////////////////////////////////////////////////////////////////////////
// jQuery is not loaded.
$ = {};
if (setGlobals) {
global.jsrender = $; // We are loading jsrender.js from a script element, not AMD or CommonJS, so set global
}
// Error warning if jsrender.js is used as template engine on Node.js (e.g. Express or Hapi...)
// Use jsrender-node.js instead...
$.renderFile = $.__express = $.compile = function() { throw "Node.js: use npm jsrender, or jsrender-node.js"; };
//END BROWSER-SPECIFIC CODE
$.isFunction = function(ob) {
return typeof ob === "function";
};
$.isArray = Array.isArray || function(obj) {
return ({}.toString).call(obj) === "[object Array]";
};
$sub._jq = function(jq) { // private method to move from JsRender APIs from jsrender namespace to jQuery namespace
if (jq !== $) {
$extend(jq, $); // map over from jsrender namespace to jQuery namespace
$ = jq;
$.fn.render = $fnRender;
delete $.jsrender;
$expando = $.expando;
}
};
$.jsrender = versionNumber;
}
$subSettings = $sub.settings;
$subSettings.allowCode = false;
$isFunction = $.isFunction;
$.render = $render;
$.views = $views;
$.templates = $templates = $views.templates;
for (setting in $subSettings) {
addSetting(setting);
}
/**
* $.views.settings.debugMode(true)
* @param {boolean} debugMode
* @returns {Settings}
*
* debugMode = $.views.settings.debugMode()
* @returns {boolean}
*/
($viewsSettings.debugMode = function(debugMode) {
return debugMode === undefined
? $subSettings.debugMode
: (
$subSettings._clFns && $subSettings._clFns(), // Clear linkExprStore (cached compiled expressions), since debugMode setting affects compilation for expressions
$subSettings.debugMode = debugMode,
$subSettings.onError = debugMode + "" === debugMode
? function() { return debugMode; }
: $isFunction(debugMode)
? debugMode
: undefined,
$viewsSettings);
})(false); // jshint ignore:line
$subSettingsAdvanced = $subSettings.advanced = {
cache: true, // By default use cached values of computed values (Otherwise, set advanced cache setting to false)
useViews: false,
_jsv: false // For global access to JsViews store
};
//========================== Register tags ==========================
$tags({
"if": {
render: function(val) {
// This function is called once for {{if}} and once for each {{else}}.
// We will use the tag.rendering object for carrying rendering state across the calls.
// If not done (a previous block has not been rendered), look at expression for this block and render the block if expression is truthy
// Otherwise return ""
var self = this,
tagCtx = self.tagCtx,
ret = (self.rendering.done || !val && (tagCtx.args.length || !tagCtx.index))
? ""
: (self.rendering.done = true,
self.selected = tagCtx.index,
undefined); // Test is satisfied, so render content on current context
return ret;
},
contentCtx: true, // Inherit parent view data context
flow: true
},
"for": {
sortDataMap: dataMap(getTargetSorted),
init: function(val, cloned) {
this.setDataMap(this.tagCtxs);
},
render: function(val) {
// This function is called once for {{for}} and once for each {{else}}.
// We will use the tag.rendering object for carrying rendering state across the calls.
var value, filter, srtField, isArray, i, sorted, end, step,
self = this,
tagCtx = self.tagCtx,
range = tagCtx.argDefault === false,
props = tagCtx.props,
iterate = range || tagCtx.args.length, // Not final else and not auto-create range
result = "",
done = 0;
if (!self.rendering.done) {
value = iterate ? val : tagCtx.view.data; // For the final else, defaults to current data without iteration.
if (range) {
range = props.reverse ? "unshift" : "push";
end = +props.end;
step = +props.step || 1;
value = []; // auto-create integer array scenario of {{for start=xxx end=yyy}}
for (i = +props.start || 0; (end - i) * step > 0; i += step) {
value[range](i);
}
}
if (value !== undefined) {
isArray = $isArray(value);
result += tagCtx.render(value, !iterate || props.noIteration);
// Iterates if data is an array, except on final else - or if noIteration property
// set to true. (Use {{include}} to compose templates without array iteration)
done += isArray ? value.length : 1;
}
if (self.rendering.done = done) {
self.selected = tagCtx.index;
}
// If nothing was rendered we will look at the next {{else}}. Otherwise, we are done.
}
return result;
},
setDataMap: function(tagCtxs) {
var tagCtx, props, paramsProps,
self = this,
l = tagCtxs.length;
while (l--) {
tagCtx = tagCtxs[l];
props = tagCtx.props;
paramsProps = tagCtx.params.props;
tagCtx.argDefault = props.end === undefined || tagCtx.args.length > 0; // Default to #data except for auto-create range scenario {{for start=xxx end=yyy step=zzz}}
props.dataMap = (tagCtx.argDefault !== false && $isArray(tagCtx.args[0]) &&
(paramsProps.sort || paramsProps.start || paramsProps.end || paramsProps.step || paramsProps.filter || paramsProps.reverse
|| props.sort || props.start || props.end || props.step || props.filter || props.reverse))
&& self.sortDataMap;
}
},
flow: true
},
props: {
baseTag: "for",
dataMap: dataMap(getTargetProps),
init: noop, // Don't execute the base init() of the "for" tag
flow: true
},
include: {
flow: true
},
"*": {
// {{* code... }} - Ignored if template.allowCode and $.views.settings.allowCode are false. Otherwise include code in compiled template
render: retVal,
flow: true
},
":*": {
// {{:* returnedExpression }} - Ignored if template.allowCode and $.views.settings.allowCode are false. Otherwise include code in compiled template
render: retVal,
flow: true
},
dbg: $helpers.dbg = $converters.dbg = dbgBreak // Register {{dbg/}}, {{dbg:...}} and ~dbg() to throw and catch, as breakpoints for debugging.
});
$converters({
html: htmlEncode,
attr: htmlEncode, // Includes > encoding since rConvertMarkers in JsViews does not skip > characters in attribute strings
encode: dataEncode,
unencode: dataUnencode, // Includes > encoding since rConvertMarkers in JsViews does not skip > characters in attribute strings
url: function(text) {
// URL encoding helper.
return text != undefined ? encodeURI("" + text) : text === null ? text : ""; // null returns null, e.g. to remove attribute. undefined returns ""
}
});
}
//========================== Define default delimiters ==========================
$subSettings = $sub.settings;
$isArray = ($||jsr).isArray;
$viewsSettings.delimiters("{{", "}}", "^");
if (jsrToJq) { // Moving from jsrender namespace to jQuery namepace - copy over the stored items (templates, converters, helpers...)
jsr.views.sub._jq($);
}
return $ || jsr;
}, window));
},{}],2:[function(require,module,exports){
(function (global){
/*global QUnit, test, equal, ok*/
(function(undefined) {
"use strict";
browserify.done.twelve = true;
QUnit.module("Browserify - client code");
var isIE8 = window.attachEvent && !window.addEventListener;
if (!isIE8) {
test("No jQuery global: require('jsrender')() nested template", function() {
// ............................... Hide QUnit global jQuery and any previous global jsrender.................................
var jQuery = global.jQuery, jsr = global.jsrender;
global.jQuery = global.jsrender = undefined;
// =============================== Arrange ===============================
var data = {name: "Jo"};
// ................................ Act ..................................
var jsrender = require('../../')(); // Not passing in jQuery, so returns the jsrender namespace
// Use require to get server template, thanks to Browserify bundle that used jsrender/tmplify transform
var tmpl = require('../templates/outer.html')(jsrender); // Provide jsrender
var result = tmpl(data);
result += " " + (jsrender !== jQuery);
// ............................... Assert .................................
equal(result, "Name: Jo (outer.html) Name: Jo (inner.html) true", "result: No jQuery global: require('jsrender')(), nested templates");
// ............................... Reset .................................
global.jQuery = jQuery; // Replace QUnit global jQuery
global.jsrender = jsr; // Replace any previous global jsrender
});
}
})();
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"../../":1,"../templates/outer.html":4}],3:[function(require,module,exports){
(function (global){
var tmplRefs = [],
mkup = 'Name: {{:name}} (inner.html)',
$ = global.jsrender || global.jQuery;
module.exports = $ ? $.templates("./test/templates/inner.html", mkup) :
function($) {
if (!$ || !$.views) {throw "Requires jsrender/jQuery";}
while (tmplRefs.length) {
tmplRefs.pop()($); // compile nested template
}
return $.templates("./test/templates/inner.html", mkup)
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],4:[function(require,module,exports){
(function (global){
var tmplRefs = [],
mkup = 'Name: {{:name}} (outer.html) {{include tmpl=\"./test/templates/inner.html\"/}}',
$ = global.jsrender || global.jQuery;
tmplRefs.push(require("./inner.html"));
module.exports = $ ? $.templates("./test/templates/outer.html", mkup) :
function($) {
if (!$ || !$.views) {throw "Requires jsrender/jQuery";}
while (tmplRefs.length) {
tmplRefs.pop()($); // compile nested template
}
return $.templates("./test/templates/outer.html", mkup)
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./inner.html":3}]},{},[2])
//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJqc3JlbmRlci5qcyIsInRlc3QvYnJvd3NlcmlmeS8xMi1uZXN0ZWQtdW5pdC10ZXN0cy5qcyIsInRlc3QvdGVtcGxhdGVzL2lubmVyLmh0bWwiLCJ0ZXN0L3RlbXBsYXRlcy9vdXRlci5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E
7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QU
FDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQ
TtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB
QUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0
FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ
0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7
QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF
DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQT
tBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ
UNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB
O0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0F
BQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0
E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Q
UFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFD
QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTt
BQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU
NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO
0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7
QUN6OEZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQ3ZDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7QUNaQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c31
2YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qISBKc1JlbmRlciB2MS4wLjc6IGh0dHA6Ly9qc3ZpZXdzLmNvbS8janNyZW5kZXIgKi9cbi8qISAqKlZFUlNJT04gRk9SIFdFQioqIChGb3IgTk9ERS5KUyBzZWUgaHR0cDovL2pzdmlld3MuY29tL2Rvd25sb2FkL2pzcmVuZGVyLW5vZGUuanMpICovXG4vKlxuICogQmVzdC1vZi1icmVlZCB0ZW1wbGF0aW5nIGluIGJyb3dzZXIgb3Igb24gTm9kZS5qcy5cbiAqIERvZXMgbm90IHJlcXVpcmUgalF1ZXJ5LCBvciBIVE1MIERPTVxuICogSW50ZWdyYXRlcyB3aXRoIEpzVmlld3MgKGh0dHA6Ly9qc3ZpZXdzLmNvbS8janN2aWV3cylcbiAqXG4gKiBDb3B5cmlnaHQgMjAyMCwgQm9yaXMgTW9vcmVcbiAqIFJlbGVhc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS5cbiAqL1xuXG4vL2pzaGludCAtVzAxOCwgLVcwNDEsIC1XMTIwXG5cbihmdW5jdGlvbihmYWN0b3J5LCBnbG9iYWwpIHtcblx0Ly8gZ2xvYmFsIHZhciBpcyB0aGUgdGhpcyBvYmplY3QsIHdoaWNoIGlzIHdpbmRvdyB3aGVuIHJ1bm5pbmcgaW4gdGhlIHVzdWFsIGJyb3dzZXIgZW52aXJvbm1lbnRcblx0dmFyICQgPSBnbG9iYWwualF1ZXJ5O1xuXG5cdGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gXCJvYmplY3RcIikgeyAvLyBDb21tb25KUyBlLmcuIEJyb3dzZXJpZnlcblx0XHRtb2R1bGUuZXhwb3J0cyA9ICRcblx0XHRcdD8gZmFjdG
9yeShnbG9iYWwsICQpXG5cdFx0XHQ6IGZ1bmN0aW9uKCQpIHsgLy8gSWYgbm8gZ2xvYmFsIGpRdWVyeSwgdGFrZSBvcHRpb25hbCBqUXVlcnkgcGFzc2VkIGFzIHBhcmFtZXRlcjogcmVxdWlyZSgnanNyZW5kZXInKShqUXVlcnkpXG5cdFx0XHRcdGlmICgkICYmICEkLmZuKSB7XG5cdFx0XHRcdFx0dGhyb3cgXCJQcm92aWRlIGpRdWVyeSBvciBudWxsXCI7XG5cdFx0XHRcdH1cblx0XHRcdFx0cmV0dXJuIGZhY3RvcnkoZ2xvYmFsLCAkKTtcblx0XHRcdH07XG5cdH0gZWxzZSBpZiAodHlwZW9mIGRlZmluZSA9PT0gXCJmdW5jdGlvblwiICYmIGRlZmluZS5hbWQpIHsgLy8gQU1EIHNjcmlwdCBsb2FkZXIsIGUuZy4gUmVxdWlyZUpTXG5cdFx0ZGVmaW5lKGZ1bmN0aW9uKCkge1xuXHRcdFx0cmV0dXJuIGZhY3RvcnkoZ2xvYmFsKTtcblx0XHR9KTtcblx0fSBlbHNlIHsgLy8gQnJvd3NlciB1c2luZyBwbGFpbiA8c2NyaXB0PiB0YWdcblx0XHRmYWN0b3J5KGdsb2JhbCwgZmFsc2UpO1xuXHR9XG59IChcblxuLy8gZmFjdG9yeSAoZm9yIGpzcmVuZGVyLmpzKVxuZnVuY3Rpb24oZ2xvYmFsLCAkKSB7XG5cInVzZSBzdHJpY3RcIjtcblxuLy89PT09PT09PT09PT09PT09PT09PT09PT09PSBUb3AtbGV2ZWwgdmFycyA9PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vLyBnbG9iYWwgdmFyIGlzIHRoZSB0aGlzIG9iamVjdCwgd2hpY2ggaXMgd2luZG93IHdoZW4gcnVubmluZyBpbiB0aGUgdXN1YWwgYnJvd3NlciBlbnZpcm9ubWVudFxudmFyIHNldEdsb
2JhbHMgPSAkID09PSBmYWxzZTsgLy8gT25seSBzZXQgZ2xvYmFscyBpZiBzY3JpcHQgYmxvY2sgaW4gYnJvd3NlciAobm90IEFNRCBhbmQgbm90IENvbW1vbkpTKVxuXG4kID0gJCAmJiAkLmZuID8gJCA6IGdsb2JhbC5qUXVlcnk7IC8vICQgaXMgalF1ZXJ5IHBhc3NlZCBpbiBieSBDb21tb25KUyBsb2FkZXIgKEJyb3dzZXJpZnkpLCBvciBnbG9iYWwgalF1ZXJ5LlxuXG52YXIgdmVyc2lvbk51bWJlciA9IFwidjEuMC43XCIsXG5cdGpzdlN0b3JlTmFtZSwgclRhZywgclRtcGxTdHJpbmcsIHRvcFZpZXcsICR2aWV3cywgJGV4cGFuZG8sXG5cdF9vY3AgPSBcIl9vY3BcIiwgICAgICAvLyBPYnNlcnZhYmxlIGNvbnRleHR1YWwgcGFyYW1ldGVyXG5cblx0JGlzRnVuY3Rpb24sICRpc0FycmF5LCAkdGVtcGxhdGVzLCAkY29udmVydGVycywgJGhlbHBlcnMsICR0YWdzLCAkc3ViLCAkc3ViU2V0dGluZ3MsICRzdWJTZXR0aW5nc0FkdmFuY2VkLCAkdmlld3NTZXR0aW5ncyxcblx0ZGVsaW1PcGVuQ2hhcjAsIGRlbGltT3BlbkNoYXIxLCBkZWxpbUNsb3NlQ2hhcjAsIGRlbGltQ2xvc2VDaGFyMSwgbGlua0NoYXIsIHNldHRpbmcsIGJhc2VPbkVycm9yLFxuXG5cdGlzUmVuZGVyQ2FsbCxcblx0ck5ld0xpbmUgPSAvWyBcXHRdKihcXHJcXG58XFxufFxccikvZyxcblx0clVuZXNjYXBlUXVvdGVzID0gL1xcXFwoWydcIlxcXFxdKS9nLCAvLyBVbmVzY2FwZSBxdW90ZXMgYW5kIHRyaW1cblx0ckVzY2FwZVF1b3RlcyA9IC9bJ1wiXFxcXF0vZywgLy8gRXNjYXBl
IHF1b3RlcyBhbmQgXFwgY2hhcmFjdGVyXG5cdHJCdWlsZEhhc2ggPSAvKD86XFx4MDh8Xikob25lcnJvcjopPyg/Oih+PykoKFtcXHckLl0rKTopPyhbXlxceDA4XSspKVxceDA4KCwpPyhbXlxceDA4XSspL2dpLFxuXHRyVGVzdEVsc2VJZiA9IC9eaWZcXHMvLFxuXHRyRmlyc3RFbGVtID0gLzwoXFx3KylbPlxcc10vLFxuXHRyQXR0ckVuY29kZSA9IC9bXFx4MDBgPjxcIicmPV0vZywgLy8gSW5jbHVkZXMgPiBlbmNvZGluZyBzaW5jZSByQ29udmVydE1hcmtlcnMgaW4gSnNWaWV3cyBkb2VzIG5vdCBza2lwID4gY2hhcmFjdGVycyBpbiBhdHRyaWJ1dGUgc3RyaW5nc1xuXHRySXNIdG1sID0gL1tcXHgwMGA+PFxcXCInJj1dLyxcblx0ckhhc0hhbmRsZXJzID0gL15vbltBLVpdfF5jb252ZXJ0KEJhY2spPyQvLFxuXHRyV3JhcHBlZEluVmlld01hcmtlciA9IC9eXFwjXFxkK19gW1xcc1xcU10qXFwvXFxkK19gJC8sXG5cdHJIdG1sRW5jb2RlID0gckF0dHJFbmNvZGUsXG5cdHJEYXRhRW5jb2RlID0gL1smPD5dL2csXG5cdHJEYXRhVW5lbmNvZGUgPSAvJihhbXB8Z3R8bHQpOy9nLFxuXHRyQnJhY2tldFF1b3RlID0gL1xcW1snXCJdP3xbJ1wiXT9cXF0vZyxcblx0dmlld0lkID0gMCxcblx0Y2hhckVudGl0aWVzID0ge1xuXHRcdFwiJlwiOiBcIiZhbXA7XCIsXG5cdFx0XCI8XCI6IFwiJmx0O1wiLFxuXHRcdFwiPlwiOiBcIiZndDtcIixcblx0XHRcIlxceDAwXCI6IFwiJiMwO1wiLFxuXHRcdFwiJ1wiOiBcIiYjMzk7XCIsXG5cdFx0J1wiJzogXCImIzM
0O1wiLFxuXHRcdFwiYFwiOiBcIiYjOTY7XCIsXG5cdFx0XCI9XCI6IFwiJiM2MTtcIlxuXHR9LFxuXHRjaGFyc0Zyb21FbnRpdGllcyA9IHtcblx0XHRhbXA6IFwiJlwiLFxuXHRcdGd0OiBcIj5cIixcblx0XHRsdDogXCI8XCJcblx0fSxcblx0SFRNTCA9IFwiaHRtbFwiLFxuXHRPQkpFQ1QgPSBcIm9iamVjdFwiLFxuXHR0bXBsQXR0ciA9IFwiZGF0YS1qc3YtdG1wbFwiLFxuXHRqc3ZUbXBsID0gXCJqc3ZUbXBsXCIsXG5cdGluZGV4U3RyID0gXCJGb3IgI2luZGV4IGluIG5lc3RlZCBibG9jayB1c2UgI2dldEluZGV4KCkuXCIsXG5cdGNwRm5TdG9yZSA9IHt9LCAgICAgLy8gQ29tcGlsZWQgZnVybmN0aW9ucyBmb3IgY29tcHV0ZWQgdmFsdWVzIGluIHRlbXBsYXRlIGV4cHJlc3Npb25zIChwcm9wZXJ0aWVzLCBtZXRob2RzLCBoZWxwZXJzKVxuXHQkcmVuZGVyID0ge30sXG5cblx0anNyID0gZ2xvYmFsLmpzcmVuZGVyLFxuXHRqc3JUb0pxID0ganNyICYmICQgJiYgISQucmVuZGVyLCAvLyBKc1JlbmRlciBhbHJlYWR5IGxvYWRlZCwgd2l0aG91dCBqUXVlcnkuIGJ1dCB3ZSB3aWxsIHJlLWxvYWQgaXQgbm93IHRvIGF0dGFjaCB0byBqUXVlcnlcblxuXHRqc3ZTdG9yZXMgPSB7XG5cdFx0dGVtcGxhdGU6IHtcblx0XHRcdGNvbXBpbGU6IGNvbXBpbGVUbXBsXG5cdFx0fSxcblx0XHR0YWc6IHtcblx0XHRcdGNvbXBpbGU6IGNvbXBpbGVUYWdcblx0XHR9LFxuXHRcdHZpZXdNb2RlbDoge1xuXHRcdFx0Y29tcGlsZTogY29tcGlsZVZpZXdNb2RlbFxuXH
RcdH0sXG5cdFx0aGVscGVyOiB7fSxcblx0XHRjb252ZXJ0ZXI6IHt9XG5cdH07XG5cblx0Ly8gdmlld3Mgb2JqZWN0ICgkLnZpZXdzIGlmIGpRdWVyeSBpcyBsb2FkZWQsIGpzcmVuZGVyLnZpZXdzIGlmIG5vIGpRdWVyeSwgZS5nLiBpbiBOb2RlLmpzKVxuXHQkdmlld3MgPSB7XG5cdFx0anN2aWV3czogdmVyc2lvbk51bWJlcixcblx0XHRzdWI6IHtcblx0XHRcdC8vIHN1YnNjcmlwdGlvbiwgZS5nLiBKc1ZpZXdzIGludGVncmF0aW9uXG5cdFx0XHRyUGF0aDogL14oISo/KSg/Om51bGx8dHJ1ZXxmYWxzZXxcXGRbXFxkLl0qfChbXFx3JF0rfFxcLnx+KFtcXHckXSspfCModmlld3woW1xcdyRdKykpPykoW1xcdyQuXl0qPykoPzpbLlteXShbXFx3JF0rKVxcXT8pPykkL2csXG5cdFx0XHQvLyAgICAgICAgbm90ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9iamVjdCAgICAgaGVscGVyICAgIHZpZXcgIHZpZXdQcm9wZXJ0eSBwYXRoVG9rZW5zICAgICAgbGVhZlRva2VuXG5cblx0XHRcdHJQcm06IC8oXFwoKSg/PVxccypcXCgpfCg/OihbKFtdKVxccyopPyg/OihcXF4/KSh+P1tcXHckLl5dKyk/XFxzKigoXFwrXFwrfC0tKXxcXCt8LXx+KD8hW1xcdyRdKXwmJnxcXHxcXHx8PT09fCE9PXw9PXwhPXw8PXw+PXxbPD4lKjo/XFwvXXwoPSkpXFxzKnwoISo/KEApP1sjfl0/W1xcdyQuXl0rKShbKFtdKT8pfCgsXFxzKil8KD86KFxcKClcXHMqKT9cXFxcPyg/OignKXwoXCIpKXwoPzpcXHMqKChbKVxcXV0pKD89Wy5eXXxcXHMqJHxbXihbXSl8W
ylcXF1dKShbKFtdPykpfChcXHMrKS9nLFxuXHRcdFx0Ly8gICBsZnRQcm4wICAgICAgICAgICBsZnRQcm4gICAgICAgICBib3VuZCAgICAgcGF0aCAgICAgICAgICAgICAgIG9wZXJhdG9yICAgICBlcnIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcSAgICAgIHBhdGgyIGxhdGUgICAgICAgICAgICBwcm4gICAgICBjb21tYSAgbGZ0UHJuMiAgICAgICAgICBhcG9zIHF1b3QgICAgICAgIHJ0UHJuICBydFBybkRvdCAgICAgICAgICAgICAgICAgIHBybjIgICAgIHNwYWNlXG5cblx0XHRcdFZpZXc6IFZpZXcsXG5cdFx0XHRFcnI6IEpzVmlld3NFcnJvcixcblx0XHRcdHRtcGxGbjogdG1wbEZuLFxuXHRcdFx0cGFyc2U6IHBhcnNlUGFyYW1zLFxuXHRcdFx0ZXh0ZW5kOiAkZXh0ZW5kLFxuXHRcdFx0ZXh0ZW5kQ3R4OiBleHRlbmRDdHgsXG5cdFx0XHRzeW50YXhFcnI6IHN5bnRheEVycm9yLFxuXHRcdFx0b25TdG9yZToge1xuXHRcdFx0XHR0ZW1wbGF0ZTogZnVuY3Rpb24obmFtZSwgaXRlbSkge1xuXHRcdFx0XHRcdGlmIChpdGVtID09PSBudWxsKSB7XG5cdFx0XHRcdFx0XHRkZWxldGUgJHJlbmRlcltuYW1lXTtcblx0XHRcdFx0XHR9IGVsc2UgaWYgKG5hbWUpIHtcblx0XHRcdFx0XHRcdCRyZW5kZXJbbmFtZV0gPSBpdGVtO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fSxcblx0XHRcdGFkZFNldHRpbmc6IGFkZFNldHRpbmcsXG5cdFx0XHRzZXR0aW5nczoge1xuXHRcdFx0XHRhbGxvd0NvZGU6IGZhbHNl
XG5cdFx0XHR9LFxuXHRcdFx0YWR2U2V0OiBub29wLCAvLyBVcGRhdGUgYWR2YW5jZWQgc2V0dGluZ3Ncblx0XHRcdF90aHA6IHRhZ0hhbmRsZXJzRnJvbVByb3BzLFxuXHRcdFx0X2dtOiBnZXRNZXRob2QsXG5cdFx0XHRfdGc6IGZ1bmN0aW9uKCkge30sIC8vIENvbnN0cnVjdG9yIGZvciB0YWdEZWZcblx0XHRcdF9jbnZ0OiBjb252ZXJ0VmFsLFxuXHRcdFx0X3RhZzogcmVuZGVyVGFnLFxuXHRcdFx0X2VyOiBlcnJvcixcblx0XHRcdF9lcnI6IG9uUmVuZGVyRXJyb3IsXG5cdFx0XHRfY3A6IHJldFZhbCwgLy8gR2V0IG9ic2VydmFibGUgY29udGV4dHVhbCBwYXJhbWV0ZXJzIChvciBwcm9wZXJ0aWVzKSB+Zm9vPWV4cHIuIEluIEpzUmVuZGVyLCBzaW1wbHkgcmV0dXJucyB2YWwuXG5cdFx0XHRfc3E6IGZ1bmN0aW9uKHRva2VuKSB7XG5cdFx0XHRcdGlmICh0b2tlbiA9PT0gXCJjb25zdHJ1Y3RvclwiKSB7XG5cdFx0XHRcdFx0c3ludGF4RXJyb3IoXCJcIik7XG5cdFx0XHRcdH1cblx0XHRcdFx0cmV0dXJuIHRva2VuO1xuXHRcdFx0fVxuXHRcdH0sXG5cdFx0c2V0dGluZ3M6IHtcblx0XHRcdGRlbGltaXRlcnM6ICR2aWV3c0RlbGltaXRlcnMsXG5cdFx0XHRhZHZhbmNlZDogZnVuY3Rpb24odmFsdWUpIHtcblx0XHRcdFx0cmV0dXJuIHZhbHVlXG5cdFx0XHRcdFx0PyAoXG5cdFx0XHRcdFx0XHRcdCRleHRlbmQoJHN1YlNldHRpbmdzQWR2YW5jZWQsIHZhbHVlKSxcblx0XHRcdFx0XHRcdFx0JHN1Yi5hZHZTZXQoKSxcblx0XHRcdFx0XHRcdFx0JHZ
pZXdzU2V0dGluZ3Ncblx0XHRcdFx0XHRcdClcblx0XHRcdFx0XHRcdDogJHN1YlNldHRpbmdzQWR2YW5jZWQ7XG5cdFx0XHRcdH1cblx0XHR9LFxuXHRcdG1hcDogZGF0YU1hcCAvLyBJZiBqc09ic2VydmFibGUgbG9hZGVkIGZpcnN0LCB1c2UgdGhhdCBkZWZpbml0aW9uIG9mIGRhdGFNYXBcblx0fTtcblxuZnVuY3Rpb24gZ2V0RGVyaXZlZE1ldGhvZChiYXNlTWV0aG9kLCBtZXRob2QpIHtcblx0cmV0dXJuIGZ1bmN0aW9uKCkge1xuXHRcdHZhciByZXQsXG5cdFx0XHR0YWcgPSB0aGlzLFxuXHRcdFx0cHJldkJhc2UgPSB0YWcuYmFzZTtcblxuXHRcdHRhZy5iYXNlID0gYmFzZU1ldGhvZDsgLy8gV2l0aGluIG1ldGhvZCBjYWxsLCBjYWxsaW5nIHRoaXMuYmFzZSB3aWxsIGNhbGwgdGhlIGJhc2UgbWV0aG9kXG5cdFx0cmV0ID0gbWV0aG9kLmFwcGx5KHRhZywgYXJndW1lbnRzKTsgLy8gQ2FsbCB0aGUgbWV0aG9kXG5cdFx0dGFnLmJhc2UgPSBwcmV2QmFzZTsgLy8gUmVwbGFjZSB0aGlzLmJhc2UgdG8gYmUgdGhlIGJhc2UgbWV0aG9kIG9mIHRoZSBwcmV2aW91cyBjYWxsLCBmb3IgY2hhaW5lZCBjYWxsc1xuXHRcdHJldHVybiByZXQ7XG5cdH07XG59XG5cbmZ1bmN0aW9uIGdldE1ldGhvZChiYXNlTWV0aG9kLCBtZXRob2QpIHtcblx0Ly8gRm9yIGRlcml2ZWQgbWV0aG9kcyAob3IgaGFuZGxlcnMgZGVjbGFyZWQgZGVjbGFyYXRpdmVseSBhcyBpbiB7ezpmb28gb25DaGFuZ2U9fmZvb0NoYW5nZWR9fSByZXBsYWNlIGJ5IGEgZGVyaXZlZCBtZXRob2
QsIHRvIGFsbG93IHVzaW5nIHRoaXMuYmFzZSguLi4pXG5cdC8vIG9yIHRoaXMuYmFzZUFwcGx5KGFyZ3VtZW50cykgdG8gY2FsbCB0aGUgYmFzZSBpbXBsZW1lbnRhdGlvbi4gKEVxdWl2YWxlbnQgdG8gdGhpcy5fc3VwZXIoLi4uKSBhbmQgdGhpcy5fc3VwZXJBcHBseShhcmd1bWVudHMpIGluIGpRdWVyeSBVSSlcblx0aWYgKCRpc0Z1bmN0aW9uKG1ldGhvZCkpIHtcblx0XHRtZXRob2QgPSBnZXREZXJpdmVkTWV0aG9kKFxuXHRcdFx0XHQhYmFzZU1ldGhvZFxuXHRcdFx0XHRcdD8gbm9vcCAvLyBubyBiYXNlIG1ldGhvZCBpbXBsZW1lbnRhdGlvbiwgc28gdXNlIG5vb3AgYXMgYmFzZSBtZXRob2Rcblx0XHRcdFx0XHQ6IGJhc2VNZXRob2QuX2Rcblx0XHRcdFx0XHRcdD8gYmFzZU1ldGhvZCAvLyBiYXNlTWV0aG9kIGlzIGEgZGVyaXZlZCBtZXRob2QsIHNvIHVzZSBpdFxuXHRcdFx0XHRcdFx0OiBnZXREZXJpdmVkTWV0aG9kKG5vb3AsIGJhc2VNZXRob2QpLCAvLyBiYXNlTWV0aG9kIGlzIG5vdCBkZXJpdmVkIHNvIG1ha2UgaXRzIGJhc2UgbWV0aG9kIGJlIHRoZSBub29wIG1ldGhvZFxuXHRcdFx0XHRtZXRob2Rcblx0XHRcdCk7XG5cdFx0bWV0aG9kLl9kID0gKGJhc2VNZXRob2QgJiYgYmFzZU1ldGhvZC5fZCB8fCAwKSArIDE7IC8vIEFkZCBmbGFnIGZvciBkZXJpdmVkIG1ldGhvZCAoaW5jcmVtZW50ZWQgZm9yIGRlcml2ZWQgb2YgZGVyaXZlZC4uLilcblx0fVxuXHRyZXR1cm4gbWV0aG9kO1xufVxuXG5mdW5jdGlvbiB0YWdIYW5kbGVyc0Zyb
21Qcm9wcyh0YWcsIHRhZ0N0eCkge1xuXHR2YXIgcHJvcCxcblx0XHRwcm9wcyA9IHRhZ0N0eC5wcm9wcztcblx0Zm9yIChwcm9wIGluIHByb3BzKSB7XG5cdFx0aWYgKHJIYXNIYW5kbGVycy50ZXN0KHByb3ApICYmICEodGFnW3Byb3BdICYmIHRhZ1twcm9wXS5maXgpKSB7IC8vIERvbid0IG92ZXJyaWRlIGhhbmRsZXJzIHdpdGggZml4IGV4cGFuZG8gKHVzZWQgaW4gZGF0ZXBpY2tlciBhbmQgc3Bpbm5lcilcblx0XHRcdHRhZ1twcm9wXSA9IHByb3AgIT09IFwiY29udmVydFwiID8gZ2V0TWV0aG9kKHRhZy5jb25zdHJ1Y3Rvci5wcm90b3R5cGVbcHJvcF0sIHByb3BzW3Byb3BdKSA6IHByb3BzW3Byb3BdO1xuXHRcdFx0Ly8gQ29weSBvdmVyIHRoZSBvbkZvbyBwcm9wcywgY29udmVydCBhbmQgY29udmVydEJhY2sgZnJvbSB0YWdDdHgucHJvcHMgdG8gdGFnIChvdmVycmlkZXMgdmFsdWVzIGluIHRhZ0RlZikuXG5cdFx0XHQvLyBOb3RlOiB1bnN1cHBvcnRlZCBzY2VuYXJpbzogaWYgaGFuZGxlcnMgYXJlIGR5bmFtaWNhbGx5IGFkZGVkIF5vbkZvbz1leHByZXNzaW9uIHRoaXMgd2lsbCB3b3JrLCBidXQgZHluYW1pY2FsbHkgcmVtb3Zpbmcgd2lsbCBub3Qgd29yay5cblx0XHR9XG5cdH1cbn1cblxuZnVuY3Rpb24gcmV0VmFsKHZhbCkge1xuXHRyZXR1cm4gdmFsO1xufVxuXG5mdW5jdGlvbiBub29wKCkge1xuXHRyZXR1cm4gXCJcIjtcbn1cblxuZnVuY3Rpb24gZGJnQnJlYWsodmFsKSB7XG5cdC8vIFVzYWdlIGV4YW1wbGVzOiB7e2RiZzouLi59
fSwge3s6fmRiZyguLi4pfX0sIHt7ZGJnIC4uLi99fSwge157Zm9yIC4uLiBvbkFmdGVyTGluaz1+ZGJnfX0gZXRjLlxuXHR0cnkge1xuXHRcdGNvbnNvbGUubG9nKFwiSnNSZW5kZXIgZGJnIGJyZWFrcG9pbnQ6IFwiICsgdmFsKTtcblx0XHR0aHJvdyBcImRiZyBicmVha3BvaW50XCI7IC8vIFRvIGJyZWFrIGhlcmUsIHN0b3Agb24gY2F1Z2h0IGV4Y2VwdGlvbnMuXG5cdH1cblx0Y2F0Y2ggKGUpIHt9XG5cdHJldHVybiB0aGlzLmJhc2UgPyB0aGlzLmJhc2VBcHBseShhcmd1bWVudHMpIDogdmFsO1xufVxuXG5mdW5jdGlvbiBKc1ZpZXdzRXJyb3IobWVzc2FnZSkge1xuXHQvLyBFcnJvciBleGNlcHRpb24gdHlwZSBmb3IgSnNWaWV3cy9Kc1JlbmRlclxuXHQvLyBPdmVycmlkZSBvZiAkLnZpZXdzLnN1Yi5FcnJvciBpcyBwb3NzaWJsZVxuXHR0aGlzLm5hbWUgPSAoJC5saW5rID8gXCJKc1ZpZXdzXCIgOiBcIkpzUmVuZGVyXCIpICsgXCIgRXJyb3JcIjtcblx0dGhpcy5tZXNzYWdlID0gbWVzc2FnZSB8fCB0aGlzLm5hbWU7XG59XG5cbmZ1bmN0aW9uICRleHRlbmQodGFyZ2V0LCBzb3VyY2UpIHtcblx0aWYgKHRhcmdldCkge1xuXHRcdGZvciAodmFyIG5hbWUgaW4gc291cmNlKSB7XG5cdFx0XHR0YXJnZXRbbmFtZV0gPSBzb3VyY2VbbmFtZV07XG5cdFx0fVxuXHRcdHJldHVybiB0YXJnZXQ7XG5cdH1cbn1cblxuKEpzVmlld3NFcnJvci5wcm90b3R5cGUgPSBuZXcgRXJyb3IoKSkuY29uc3RydWN0b3IgPSBKc1ZpZXdzRXJyb3I7XG5cbi8vPT09PT0
9PT09PT09PT09PT09PT09PT09PT0gVG9wLWxldmVsIGZ1bmN0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vLz09PT09PT09PT09PT09PT09PT1cbi8vIHZpZXdzLmRlbGltaXRlcnNcbi8vPT09PT09PT09PT09PT09PT09PVxuXG5cdC8qKlxuXHQqIFNldCB0aGUgdGFnIG9wZW5pbmcgYW5kIGNsb3NpbmcgZGVsaW1pdGVycyBhbmQgJ2xpbmsnIGNoYXJhY3Rlci4gRGVmYXVsdCBpcyBcInt7XCIsIFwifX1cIiBhbmQgXCJeXCJcblx0KiBvcGVuQ2hhcnMsIGNsb3NlQ2hhcnM6IG9wZW5pbmcgYW5kIGNsb3Npbmcgc3RyaW5ncywgZWFjaCB3aXRoIHR3byBjaGFyYWN0ZXJzXG5cdCogJC52aWV3cy5zZXR0aW5ncy5kZWxpbWl0ZXJzKC4uLilcblx0KlxuXHQqIEBwYXJhbSB7c3RyaW5nfSAgIG9wZW5DaGFyc1xuXHQqIEBwYXJhbSB7c3RyaW5nfSAgIFtjbG9zZUNoYXJzXVxuXHQqIEBwYXJhbSB7c3RyaW5nfSAgIFtsaW5rXVxuXHQqIEByZXR1cm5zIHtTZXR0aW5nc31cblx0KlxuXHQqIEdldCBkZWxpbWl0ZXJzXG5cdCogZGVsaW1zQXJyYXkgPSAkLnZpZXdzLnNldHRpbmdzLmRlbGltaXRlcnMoKVxuXHQqXG5cdCogQHJldHVybnMge3N0cmluZ1tdfVxuXHQqL1xuZnVuY3Rpb24gJHZpZXdzRGVsaW1pdGVycyhvcGVuQ2hhcnMsIGNsb3NlQ2hhcnMsIGxpbmspIHtcblx0aWYgKCFvcGVuQ2hhcnMpIHtcblx0XHRyZXR1cm4gJHN1YlNldHRpbmdzLmRlbGltaXRlcnM7XG5cdH1cblx0aWYgKCRpc0FycmF5KG9wZW5DaGFycykpIHtcblx0XH
RyZXR1cm4gJHZpZXdzRGVsaW1pdGVycy5hcHBseSgkdmlld3MsIG9wZW5DaGFycyk7XG5cdH1cblx0bGlua0NoYXIgPSBsaW5rID8gbGlua1swXSA6IGxpbmtDaGFyO1xuXHRpZiAoIS9eKFxcV3xfKXs1fSQvLnRlc3Qob3BlbkNoYXJzICsgY2xvc2VDaGFycyArIGxpbmtDaGFyKSkge1xuXHRcdGVycm9yKFwiSW52YWxpZCBkZWxpbWl0ZXJzXCIpOyAvLyBNdXN0IGJlIG5vbi13b3JkIGNoYXJhY3RlcnMsIGFuZCBvcGVuQ2hhcnMgYW5kIGNsb3NlQ2hhcnMgbXVzdCBlYWNoIGJlIGxlbmd0aCAyXG5cdH1cblx0ZGVsaW1PcGVuQ2hhcjAgPSBvcGVuQ2hhcnNbMF07XG5cdGRlbGltT3BlbkNoYXIxID0gb3BlbkNoYXJzWzFdO1xuXHRkZWxpbUNsb3NlQ2hhcjAgPSBjbG9zZUNoYXJzWzBdO1xuXHRkZWxpbUNsb3NlQ2hhcjEgPSBjbG9zZUNoYXJzWzFdO1xuXG5cdCRzdWJTZXR0aW5ncy5kZWxpbWl0ZXJzID0gW2RlbGltT3BlbkNoYXIwICsgZGVsaW1PcGVuQ2hhcjEsIGRlbGltQ2xvc2VDaGFyMCArIGRlbGltQ2xvc2VDaGFyMSwgbGlua0NoYXJdO1xuXG5cdC8vIEVzY2FwZSB0aGUgY2hhcmFjdGVycyAtIHNpbmNlIHRoZXkgY291bGQgYmUgcmVnZXggc3BlY2lhbCBjaGFyYWN0ZXJzXG5cdG9wZW5DaGFycyA9IFwiXFxcXFwiICsgZGVsaW1PcGVuQ2hhcjAgKyBcIihcXFxcXCIgKyBsaW5rQ2hhciArIFwiKT9cXFxcXCIgKyBkZWxpbU9wZW5DaGFyMTsgLy8gRGVmYXVsdCBpcyBcIntee1wiXG5cdGNsb3NlQ2hhcnMgPSBcIlxcXFxcIiArIGRlbGltQ2xvc
2VDaGFyMCArIFwiXFxcXFwiICsgZGVsaW1DbG9zZUNoYXIxOyAgICAgICAgICAgICAgICAgICAvLyBEZWZhdWx0IGlzIFwifX1cIlxuXHQvLyBCdWlsZCByZWdleCB3aXRoIG5ldyBkZWxpbWl0ZXJzXG5cdC8vICAgICAgICAgIFt0YWcgICAgKGZvbGxvd2VkIGJ5IC8gc3BhY2Ugb3IgfSkgIG9yIGN2dHIrY29sb24gb3IgaHRtbCBvciBjb2RlXSBmb2xsb3dlZCBieSBzcGFjZStwYXJhbXMgdGhlbiBjb252ZXJ0QmFjaz9cblx0clRhZyA9IFwiKD86KFxcXFx3Kyg/PVtcXFxcL1xcXFxzXFxcXFwiICsgZGVsaW1DbG9zZUNoYXIwICsgXCJdKSl8KFxcXFx3Kyk/KDopfCg+KXwoXFxcXCopKVxcXFxzKigoPzpbXlxcXFxcIlxuXHRcdCsgZGVsaW1DbG9zZUNoYXIwICsgXCJdfFxcXFxcIiArIGRlbGltQ2xvc2VDaGFyMCArIFwiKD8hXFxcXFwiICsgZGVsaW1DbG9zZUNoYXIxICsgXCIpKSo/KVwiO1xuXG5cdC8vIE1ha2UgclRhZyBhdmFpbGFibGUgdG8gSnNWaWV3cyAob3Igb3RoZXIgY29tcG9uZW50cykgZm9yIHBhcnNpbmcgYmluZGluZyBleHByZXNzaW9uc1xuXHQkc3ViLnJUYWcgPSBcIig/OlwiICsgclRhZyArIFwiKVwiO1xuXHQvLyAgICAgICAgICAgICAgICAgICAgICAgIHsgXj8geyAgIHRhZytwYXJhbXMgc2xhc2g/ICBvciBjbG9zaW5nVGFnICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3IgY29tbWVudFxuXHRyVGFnID0gbmV3IFJlZ0V4cChcIig/OlwiICsgb3BlbkNoYXJzICsgclRhZyArIFwi
KFxcXFwvKT98XFxcXFwiICsgZGVsaW1PcGVuQ2hhcjAgKyBcIihcXFxcXCIgKyBsaW5rQ2hhciArIFwiKT9cXFxcXCIgKyBkZWxpbU9wZW5DaGFyMSArIFwiKD86KD86XFxcXC8oXFxcXHcrKSlcXFxccyp8IS0tW1xcXFxzXFxcXFNdKj8tLSkpXCIgKyBjbG9zZUNoYXJzLCBcImdcIik7XG5cblx0Ly8gRGVmYXVsdDogIGJpbmQgICAgIHRhZ05hbWUgICAgICAgICBjdnQgICBjbG4gaHRtbCBjb2RlICAgIHBhcmFtcyAgICAgICAgICAgIHNsYXNoICAgYmluZDIgICAgICAgICBjbG9zZUJsayAgY29tbWVudFxuXHQvLyAgICAgIC8oPzp7KFxcXik/eyg/OihcXHcrKD89W1xcL1xcc31dKSl8KFxcdyspPyg6KXwoPil8KFxcKikpXFxzKigoPzpbXn1dfH0oPyF9KSkqPykoXFwvKT98eyhcXF4pP3soPzooPzpcXC8oXFx3KykpXFxzKnwhLS1bXFxzXFxTXSo/LS0pKX19XG5cblx0JHN1Yi5yVG1wbCA9IG5ldyBSZWdFeHAoXCJeXFxcXHN8XFxcXHMkfDwuKj58KFteXFxcXFxcXFxdfF4pW3t9XXxcIiArIG9wZW5DaGFycyArIFwiLipcIiArIGNsb3NlQ2hhcnMpO1xuXHQvLyAkc3ViLnJUbXBsIGxvb2tzIGZvciBpbml0aWFsIG9yIGZpbmFsIHdoaXRlIHNwYWNlLCBodG1sIHRhZ3Mgb3IgeyBvciB9IGNoYXIgbm90IHByZWNlZGVkIGJ5IFxcXFwsIG9yIEpzUmVuZGVyIHRhZ3Mge3t4eHh9fS5cblx0Ly8gRWFjaCBvZiB0aGVzZSBzdHJpbmdzIGFyZSBjb25zaWRlcmVkIE5PVCB0byBiZSBqUXVlcnkgc2VsZWN0b3JzXG5cdHJldHVybiAkdmlld3NTZXR0aW5nczt
cbn1cblxuLy89PT09PT09PT1cbi8vIFZpZXcuZ2V0XG4vLz09PT09PT09PVxuXG5mdW5jdGlvbiBnZXRWaWV3KGlubmVyLCB0eXBlKSB7IC8vdmlldy5nZXQoaW5uZXIsIHR5cGUpXG5cdGlmICghdHlwZSAmJiBpbm5lciAhPT0gdHJ1ZSkge1xuXHRcdC8vIHZpZXcuZ2V0KHR5cGUpXG5cdFx0dHlwZSA9IGlubmVyO1xuXHRcdGlubmVyID0gdW5kZWZpbmVkO1xuXHR9XG5cblx0dmFyIHZpZXdzLCBpLCBsLCBmb3VuZCxcblx0XHR2aWV3ID0gdGhpcyxcblx0XHRyb290ID0gdHlwZSA9PT0gXCJyb290XCI7XG5cdFx0Ly8gdmlldy5nZXQoXCJyb290XCIpIHJldHVybnMgdmlldy5yb290LCB2aWV3LmdldCgpIHJldHVybnMgdmlldy5wYXJlbnQsIHZpZXcuZ2V0KHRydWUpIHJldHVybnMgdmlldy52aWV3c1swXS5cblxuXHRpZiAoaW5uZXIpIHtcblx0XHQvLyBHbyB0aHJvdWdoIHZpZXdzIC0gdGhpcyBvbmUsIGFuZCBhbGwgbmVzdGVkIG9uZXMsIGRlcHRoLWZpcnN0IC0gYW5kIHJldHVybiBmaXJzdCBvbmUgd2l0aCBnaXZlbiB0eXBlLlxuXHRcdC8vIElmIHR5cGUgaXMgdW5kZWZpbmVkLCBpLmUuIHZpZXcuZ2V0KHRydWUpLCByZXR1cm4gZmlyc3QgY2hpbGQgdmlldy5cblx0XHRmb3VuZCA9IHR5cGUgJiYgdmlldy50eXBlID09PSB0eXBlICYmIHZpZXc7XG5cdFx0aWYgKCFmb3VuZCkge1xuXHRcdFx0dmlld3MgPSB2aWV3LnZpZXdzO1xuXHRcdFx0aWYgKHZpZXcuXy51c2VLZXkpIHtcblx0XHRcdFx0Zm9yIChpIGluIHZpZXdzKSB7XG5cdFx0XHRcdF
x0aWYgKGZvdW5kID0gdHlwZSA/IHZpZXdzW2ldLmdldChpbm5lciwgdHlwZSkgOiB2aWV3c1tpXSkge1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRmb3IgKGkgPSAwLCBsID0gdmlld3MubGVuZ3RoOyAhZm91bmQgJiYgaSA8IGw7IGkrKykge1xuXHRcdFx0XHRcdGZvdW5kID0gdHlwZSA/IHZpZXdzW2ldLmdldChpbm5lciwgdHlwZSkgOiB2aWV3c1tpXTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fSBlbHNlIGlmIChyb290KSB7XG5cdFx0Ly8gRmluZCByb290IHZpZXcuICh2aWV3IHdob3NlIHBhcmVudCBpcyB0b3Agdmlldylcblx0XHRmb3VuZCA9IHZpZXcucm9vdDtcblx0fSBlbHNlIGlmICh0eXBlKSB7XG5cdFx0d2hpbGUgKHZpZXcgJiYgIWZvdW5kKSB7XG5cdFx0XHQvLyBHbyB0aHJvdWdoIHZpZXdzIC0gdGhpcyBvbmUsIGFuZCBhbGwgcGFyZW50IG9uZXMgLSBhbmQgcmV0dXJuIGZpcnN0IG9uZSB3aXRoIGdpdmVuIHR5cGUuXG5cdFx0XHRmb3VuZCA9IHZpZXcudHlwZSA9PT0gdHlwZSA/IHZpZXcgOiB1bmRlZmluZWQ7XG5cdFx0XHR2aWV3ID0gdmlldy5wYXJlbnQ7XG5cdFx0fVxuXHR9IGVsc2Uge1xuXHRcdGZvdW5kID0gdmlldy5wYXJlbnQ7XG5cdH1cblx0cmV0dXJuIGZvdW5kIHx8IHVuZGVmaW5lZDtcbn1cblxuZnVuY3Rpb24gZ2V0TmVzdGVkSW5kZXgoKSB7XG5cdHZhciB2aWV3ID0gdGhpcy5nZXQoXCJpdGVtXCIpO1xuXHRyZXR1cm4gd
mlldyA/IHZpZXcuaW5kZXggOiB1bmRlZmluZWQ7XG59XG5cbmdldE5lc3RlZEluZGV4LmRlcGVuZHMgPSBmdW5jdGlvbigpIHtcblx0cmV0dXJuIFt0aGlzLmdldChcIml0ZW1cIiksIFwiaW5kZXhcIl07XG59O1xuXG5mdW5jdGlvbiBnZXRJbmRleCgpIHtcblx0cmV0dXJuIHRoaXMuaW5kZXg7XG59XG5cbmdldEluZGV4LmRlcGVuZHMgPSBcImluZGV4XCI7XG5cbi8vPT09PT09PT09PT09PT09PT09XG4vLyBWaWV3LmN0eFBybSwgZXRjLlxuLy89PT09PT09PT09PT09PT09PT1cblxuLyogSW50ZXJuYWwgcHJpdmF0ZTogdmlldy5fZ2V0T2IoKSAqL1xuZnVuY3Rpb24gZ2V0UGF0aE9iamVjdChvYiwgcGF0aCwgbHRPYiwgZm4pIHtcblx0Ly8gSXRlcmF0ZSB0aHJvdWdoIHBhdGggdG8gbGF0ZSBwYXRoczogQGEuYi5jIHBhdGhzXG5cdC8vIFJldHVybiBcIlwiIChvciBub29wIGlmIGxlYWYgaXMgYSBmdW5jdGlvbiBAYS5iLmMoLi4uKSApIGlmIGludGVybWVkaWF0ZSBvYmplY3Qgbm90IHlldCBhdmFpbGFibGVcblx0dmFyIHByZXZPYiwgdG9rZW5zLCBsLFxuXHRcdGkgPSAwO1xuXHRpZiAobHRPYiA9PT0gMSkge1xuXHRcdGZuID0gMTtcblx0XHRsdE9iID0gdW5kZWZpbmVkO1xuXHR9XG5cdC8vIFBhdGhzIGxpa2UgXmFeYl5jIG9yIH5eYV5iXmMgd2lsbCBub3QgdGhyb3cgaWYgYW4gb2JqZWN0IGluIHBhdGggaXMgdW5kZWZpbmVkLlxuXHRpZiAocGF0aCkge1xuXHRcdHRva2VucyA9IHBhdGguc3BsaXQoXCIuXCIpO1xuXHRcdGwgPSB0b2tlbnMu
bGVuZ3RoO1xuXG5cdFx0Zm9yICg7IG9iICYmIGkgPCBsOyBpKyspIHtcblx0XHRcdHByZXZPYiA9IG9iO1xuXHRcdFx0b2IgPSB0b2tlbnNbaV0gPyBvYlt0b2tlbnNbaV1dIDogb2I7XG5cdFx0fVxuXHR9XG5cdGlmIChsdE9iKSB7XG5cdFx0bHRPYi5sdCA9IGx0T2IubHQgfHwgaTxsOyAvLyBJZiBpIDwgbCB0aGVyZSB3YXMgYW4gb2JqZWN0IGluIHRoZSBwYXRoIG5vdCB5ZXQgYXZhaWxhYmxlXG5cdH1cblx0cmV0dXJuIG9iID09PSB1bmRlZmluZWRcblx0XHQ/IGZuID8gbm9vcCA6IFwiXCJcblx0XHQ6IGZuID8gZnVuY3Rpb24oKSB7XG5cdFx0XHRyZXR1cm4gb2IuYXBwbHkocHJldk9iLCBhcmd1bWVudHMpO1xuXHRcdH0gOiBvYjtcbn1cblxuZnVuY3Rpb24gY29udGV4dFBhcmFtZXRlcihrZXksIHZhbHVlLCBnZXQpIHtcblx0Ly8gSGVscGVyIG1ldGhvZCBjYWxsZWQgYXMgdmlldy5jdHhQcm0oa2V5KSBmb3IgaGVscGVycyBvciB0ZW1wbGF0ZSBwYXJhbWV0ZXJzIH5mb28gLSBmcm9tIGNvbXBpbGVkIHRlbXBsYXRlIG9yIGZyb20gY29udGV4dCBjYWxsYmFja1xuXHR2YXIgd3JhcHBlZCwgZGVwcywgcmVzLCBvYnNDdHhQcm0sIHRhZ0Vsc2UsIGNhbGxWaWV3LCBuZXdSZXMsXG5cdFx0c3RvcmVWaWV3ID0gdGhpcyxcblx0XHRpc1VwZGF0ZSA9ICFpc1JlbmRlckNhbGwgJiYgYXJndW1lbnRzLmxlbmd0aCA+IDEsXG5cdFx0c3RvcmUgPSBzdG9yZVZpZXcuY3R4O1xuXHRpZiAoa2V5KSB7XG5cdFx0aWYgKCFzdG9yZVZpZXcuXykgeyAvLyB0YWd
DdHguY3R4UHJtKCkgY2FsbFxuXHRcdFx0dGFnRWxzZSA9IHN0b3JlVmlldy5pbmRleDtcblx0XHRcdHN0b3JlVmlldyA9IHN0b3JlVmlldy50YWc7XG5cdFx0fVxuXHRcdGNhbGxWaWV3ID0gc3RvcmVWaWV3O1xuXHRcdGlmIChzdG9yZSAmJiBzdG9yZS5oYXNPd25Qcm9wZXJ0eShrZXkpIHx8IChzdG9yZSA9ICRoZWxwZXJzKS5oYXNPd25Qcm9wZXJ0eShrZXkpKSB7XG5cdFx0XHRyZXMgPSBzdG9yZVtrZXldO1xuXHRcdFx0aWYgKGtleSA9PT0gXCJ0YWdcIiB8fCBrZXkgPT09IFwidGFnQ3R4XCIgfHwga2V5ID09PSBcInJvb3RcIiB8fCBrZXkgPT09IFwicGFyZW50VGFnc1wiKSB7XG5cdFx0XHRcdHJldHVybiByZXM7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdHN0b3JlID0gdW5kZWZpbmVkO1xuXHRcdH1cblx0XHRpZiAoIWlzUmVuZGVyQ2FsbCAmJiBzdG9yZVZpZXcudGFnQ3R4IHx8IHN0b3JlVmlldy5saW5rZWQpIHsgLy8gRGF0YS1saW5rZWQgdmlldywgb3IgdGFnIGluc3RhbmNlXG5cdFx0XHRpZiAoIXJlcyB8fCAhcmVzLl9jeHApIHtcblx0XHRcdFx0Ly8gTm90IGEgY29udGV4dHVhbCBwYXJhbWV0ZXJcblx0XHRcdFx0Ly8gU2V0IHN0b3JlVmlldyB0byB0YWcgKGlmIHRoaXMgaXMgYSB0YWcuY3R4UHJtKCkgY2FsbCkgb3IgdG8gcm9vdCB2aWV3IChcImRhdGFcIiB2aWV3IG9mIGxpbmtlZCB0ZW1wbGF0ZSlcblx0XHRcdFx0c3RvcmVWaWV3ID0gc3RvcmVWaWV3LnRhZ0N0eCB8fCAkaXNGdW5jdGlvbihyZXMpXG5cdFx0XHRcdF
x0PyBzdG9yZVZpZXcgLy8gSXMgYSB0YWcsIG5vdCBhIHZpZXcsIG9yIGlzIGEgY29tcHV0ZWQgY29udGV4dHVhbCBwYXJhbWV0ZXIsIHNvIHNjb3BlIHRvIHRoZSBjYWxsVmlldywgbm8gdGhlICdzY29wZSB2aWV3J1xuXHRcdFx0XHRcdDogKHN0b3JlVmlldyA9IHN0b3JlVmlldy5zY29wZSB8fCBzdG9yZVZpZXcsXG5cdFx0XHRcdFx0XHQhc3RvcmVWaWV3LmlzVG9wICYmIHN0b3JlVmlldy5jdHgudGFnIC8vIElmIHRoaXMgdmlldyBpcyBpbiBhIHRhZywgc2V0IHN0b3JlVmlldyB0byB0aGUgdGFnXG5cdFx0XHRcdFx0XHRcdHx8IHN0b3JlVmlldyk7XG5cdFx0XHRcdGlmIChyZXMgIT09IHVuZGVmaW5lZCAmJiBzdG9yZVZpZXcudGFnQ3R4KSB7XG5cdFx0XHRcdFx0Ly8gSWYgc3RvcmVWaWV3IGlzIGEgdGFnLCBidXQgdGhlIGNvbnRleHR1YWwgcGFyYW1ldGVyIGhhcyBiZWVuIHNldCBhdCBhdCBoaWdoZXIgbGV2ZWwgKGUuZy4gaGVscGVycykuLi5cblx0XHRcdFx0XHRzdG9yZVZpZXcgPSBzdG9yZVZpZXcudGFnQ3R4LnZpZXcuc2NvcGU7IC8vIHRoZW4gbW92ZSBzdG9yZVZpZXcgdG8gdGhlIG91dGVyIGxldmVsIChzY29wZSBvZiB0YWcgY29udGFpbmVyIHZpZXcpXG5cdFx0XHRcdH1cblx0XHRcdFx0c3RvcmUgPSBzdG9yZVZpZXcuX29jcHM7XG5cdFx0XHRcdHJlcyA9IHN0b3JlICYmIHN0b3JlLmhhc093blByb3BlcnR5KGtleSkgJiYgc3RvcmVba2V5XSB8fCByZXM7XG5cdFx0XHRcdGlmICghKHJlcyAmJiByZXMuX2N4cCkgJiYgKGdldCB8f
CBpc1VwZGF0ZSkpIHtcblx0XHRcdFx0XHQvLyBDcmVhdGUgb2JzZXJ2YWJsZSBjb250ZXh0dWFsIHBhcmFtZXRlclxuXHRcdFx0XHRcdChzdG9yZSB8fCAoc3RvcmVWaWV3Ll9vY3BzID0gc3RvcmVWaWV3Ll9vY3BzIHx8IHt9KSlba2V5XVxuXHRcdFx0XHRcdFx0PSByZXNcblx0XHRcdFx0XHRcdD0gW3tcblx0XHRcdFx0XHRcdFx0X29jcDogcmVzLCAvLyBUaGUgb2JzZXJ2YWJsZSBjb250ZXh0dWFsIHBhcmFtZXRlciB2YWx1ZVxuXHRcdFx0XHRcdFx0XHRfdnc6IGNhbGxWaWV3LFxuXHRcdFx0XHRcdFx0XHRfa2V5OiBrZXlcblx0XHRcdFx0XHRcdH1dO1xuXHRcdFx0XHRcdHJlcy5fY3hwID0ge1xuXHRcdFx0XHRcdFx0cGF0aDogX29jcCxcblx0XHRcdFx0XHRcdGluZDogMCxcblx0XHRcdFx0XHRcdHVwZGF0ZVZhbHVlOiBmdW5jdGlvbih2YWwsIHBhdGgpIHtcblx0XHRcdFx0XHRcdFx0JC5vYnNlcnZhYmxlKHJlc1swXSkuc2V0UHJvcGVydHkoX29jcCwgdmFsKTsgLy8gU2V0IHRoZSB2YWx1ZSAocmVzWzBdLl9vY3ApXG5cdFx0XHRcdFx0XHRcdHJldHVybiB0aGlzO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH07XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGlmIChvYnNDdHhQcm0gPSByZXMgJiYgcmVzLl9jeHApIHtcblx0XHRcdFx0Ly8gSWYgdGhpcyBoZWxwZXIgcmVzb3VyY2UgaXMgYW4gb2JzZXJ2YWJsZSBjb250ZXh0dWFsIHBhcmFtZXRlclxuXHRcdFx0XHRpZiAoYXJndW1lbnRzLmxlbmd0aCA+IDIpIHtcblx0XHRcdFx0XHRk
ZXBzID0gcmVzWzFdID8gJHN1Yi5fY2VvKHJlc1sxXS5kZXBzKSA6IFtfb2NwXTsgLy8gZm4gZGVwcyAod2l0aCBhbnkgZXhwck9icyBjbG9uZWQgdXNpbmcgJHN1Yi5fY2VvKVxuXHRcdFx0XHRcdGRlcHMudW5zaGlmdChyZXNbMF0pOyAvLyB2aWV3XG5cdFx0XHRcdFx0ZGVwcy5fY3hwID0gb2JzQ3R4UHJtO1xuXHRcdFx0XHRcdC8vIEluIGEgY29udGV4dCBjYWxsYmFjayBmb3IgYSBjb250ZXh0dWFsIHBhcmFtLCB3ZSBzZXQgZ2V0ID0gdHJ1ZSwgdG8gZ2V0IGN0eFBybSBbdmlldywgZGVwZW5kZW5jaWVzLi4uXSBhcnJheSAtIG5lZWRlZCBmb3Igb2JzZXJ2ZSBjYWxsXG5cdFx0XHRcdFx0cmV0dXJuIGRlcHM7XG5cdFx0XHRcdH1cblx0XHRcdFx0dGFnRWxzZSA9IG9ic0N0eFBybS50YWdFbHNlO1xuXHRcdFx0XHRuZXdSZXMgPSByZXNbMV0gLy8gbGlua0ZuIGZvciBjb21waWxlZCBleHByZXNzaW9uXG5cdFx0XHRcdFx0PyBvYnNDdHhQcm0udGFnICYmIG9ic0N0eFBybS50YWcuY3Z0QXJnc1xuXHRcdFx0XHRcdFx0PyBvYnNDdHhQcm0udGFnLmN2dEFyZ3ModGFnRWxzZSwgMSlbb2JzQ3R4UHJtLmluZF0gLy8gPSB0YWcuYm5kQXJncygpIC0gZm9yIHRhZyBjb250ZXh0dWFsIHBhcmFtZXRlclxuXHRcdFx0XHRcdFx0OiByZXNbMV0ocmVzWzBdLmRhdGEsIHJlc1swXSwgJHN1YikgLy8gPSBmbihkYXRhLCB2aWV3LCAkc3ViKSBmb3IgY29tcGlsZWQgYmluZGluZyBleHByZXNzaW9uXG5cdFx0XHRcdFx0OiByZXNbMF0uX29jcDsgLy8gT2JzZXJ2YWJ
sZSBjb250ZXh0dWFsIHBhcmFtZXRlciAodW5pbml0aWFsaXplZCwgb3IgaW5pdGlhbGl6ZWQgYXMgc3RhdGljIGV4cHJlc3Npb24sIHNvIG5vIHBhdGggZGVwZW5kZW5jaWVzKVxuXHRcdFx0XHRpZiAoaXNVcGRhdGUpIHtcblx0XHRcdFx0XHQkc3ViLl91Y3Aoa2V5LCB2YWx1ZSwgc3RvcmVWaWV3LCBvYnNDdHhQcm0pOyAvLyBVcGRhdGUgb2JzZXJ2YWJsZSBjb250ZXh0dWFsIHBhcmFtZXRlclxuXHRcdFx0XHRcdHJldHVybiBzdG9yZVZpZXc7XG5cdFx0XHRcdH1cblx0XHRcdFx0cmVzID0gbmV3UmVzO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRpZiAocmVzICYmICRpc0Z1bmN0aW9uKHJlcykpIHtcblx0XHRcdC8vIElmIGEgaGVscGVyIGlzIG9mIHR5cGUgZnVuY3Rpb24gd2Ugd2lsbCB3cmFwIGl0LCBzbyBpZiBjYWxsZWQgd2l0aCBubyB0aGlzIHBvaW50ZXIgaXQgd2lsbCBiZSBjYWxsZWQgd2l0aCB0aGVcblx0XHRcdC8vIHZpZXcgYXMgJ3RoaXMnIGNvbnRleHQuIElmIHRoZSBoZWxwZXIgfmZvbygpIHdhcyBpbiBhIGRhdGEtbGluayBleHByZXNzaW9uLCB0aGUgdmlldyB3aWxsIGhhdmUgYSAndGVtcG9yYXJ5JyBsaW5rQ3R4IHByb3BlcnR5IHRvby5cblx0XHRcdC8vIE5vdGUgdGhhdCBoZWxwZXIgZnVuY3Rpb25zIG9uIGRlZXBlciBwYXRocyB3aWxsIGhhdmUgc3BlY2lmaWMgdGhpcyBwb2ludGVycywgZnJvbSB0aGUgcHJlY2VkaW5nIHBhdGguXG5cdFx0XHQvLyBGb3IgZXhhbXBsZSwgfnV0aWwuZm9vKCkgd2lsbCBoYXZlIHRoZSB+dXRpbC
BvYmplY3QgYXMgJ3RoaXMnIHBvaW50ZXJcblx0XHRcdHdyYXBwZWQgPSBmdW5jdGlvbigpIHtcblx0XHRcdFx0cmV0dXJuIHJlcy5hcHBseSgoIXRoaXMgfHwgdGhpcyA9PT0gZ2xvYmFsKSA/IGNhbGxWaWV3IDogdGhpcywgYXJndW1lbnRzKTtcblx0XHRcdH07XG5cdFx0XHQkZXh0ZW5kKHdyYXBwZWQsIHJlcyk7IC8vIEF0dGFjaCBzYW1lIGV4cGFuZG9zIChpZiBhbnkpIHRvIHRoZSB3cmFwcGVkIGZ1bmN0aW9uXG5cdFx0fVxuXHRcdHJldHVybiB3cmFwcGVkIHx8IHJlcztcblx0fVxufVxuXG4vKiBJbnRlcm5hbCBwcml2YXRlOiB2aWV3Ll9nZXRUbXBsKCkgKi9cbmZ1bmN0aW9uIGdldFRlbXBsYXRlKHRtcGwpIHtcblx0cmV0dXJuIHRtcGwgJiYgKHRtcGwuZm5cblx0XHQ/IHRtcGxcblx0XHQ6IHRoaXMuZ2V0UnNjKFwidGVtcGxhdGVzXCIsIHRtcGwpIHx8ICR0ZW1wbGF0ZXModG1wbCkpOyAvLyBub3QgeWV0IGNvbXBpbGVkXG59XG5cbi8vPT09PT09PT09PT09PT1cbi8vIHZpZXdzLl9jbnZ0XG4vLz09PT09PT09PT09PT09XG5cbmZ1bmN0aW9uIGNvbnZlcnRWYWwoY29udmVydGVyLCB2aWV3LCB0YWdDdHgsIG9uRXJyb3IpIHtcblx0Ly8gQ2FsbGVkIGZyb20gY29tcGlsZWQgdGVtcGxhdGUgY29kZSBmb3Ige3s6fX1cblx0Ly8gc2VsZiBpcyB0ZW1wbGF0ZSBvYmplY3Qgb3IgbGlua0N0eCBvYmplY3Rcblx0dmFyIHRhZywgbGlua0N0eCwgdmFsdWUsIGFyZ3NMZW4sIGJpbmRUbyxcblx0XHQvLyBJZiB0YWdDdHggaXMgYW4gaW50ZWdlciwgd
GhlbiBpdCBpcyB0aGUga2V5IGZvciB0aGUgY29tcGlsZWQgZnVuY3Rpb24gdG8gcmV0dXJuIHRoZSBib3VuZFRhZyB0YWdDdHhcblx0XHRib3VuZFRhZyA9IHR5cGVvZiB0YWdDdHggPT09IFwibnVtYmVyXCIgJiYgdmlldy50bXBsLmJuZHNbdGFnQ3R4LTFdO1xuXG5cdGlmIChvbkVycm9yID09PSB1bmRlZmluZWQgJiYgYm91bmRUYWcgJiYgYm91bmRUYWcuX2xyKSB7IC8vIGxhdGVSZW5kZXJcblx0XHRvbkVycm9yID0gXCJcIjtcblx0fVxuXHRpZiAob25FcnJvciAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0dGFnQ3R4ID0gb25FcnJvciA9IHtwcm9wczoge30sIGFyZ3M6IFtvbkVycm9yXX07XG5cdH0gZWxzZSBpZiAoYm91bmRUYWcpIHtcblx0XHR0YWdDdHggPSBib3VuZFRhZyh2aWV3LmRhdGEsIHZpZXcsICRzdWIpO1xuXHR9XG5cdGJvdW5kVGFnID0gYm91bmRUYWcuX2JkICYmIGJvdW5kVGFnO1xuXHRpZiAoY29udmVydGVyIHx8IGJvdW5kVGFnKSB7XG5cdFx0bGlua0N0eCA9IHZpZXcuX2xjOyAvLyBGb3IgZGF0YS1saW5rPVwie2N2dDouLi59XCIuLi4gU2VlIG9uRGF0YUxpbmtlZFRhZ0NoYW5nZVxuXHRcdHRhZyA9IGxpbmtDdHggJiYgbGlua0N0eC50YWc7XG5cdFx0dGFnQ3R4LnZpZXcgPSB2aWV3O1xuXHRcdGlmICghdGFnKSB7XG5cdFx0XHR0YWcgPSAkZXh0ZW5kKG5ldyAkc3ViLl90ZygpLCB7XG5cdFx0XHRcdF86IHtcblx0XHRcdFx0XHRibmQ6IGJvdW5kVGFnLFxuXHRcdFx0XHRcdHVubGlua2VkOiB0cnVlLFxuXHRcdFx0XHRcdGx0
OiB0YWdDdHgubHQgLy8gSWYgYSBsYXRlIHBhdGggQHNvbWUucGF0aCBoYXMgbm90IHJldHVybmVkIEBzb21lIG9iamVjdCwgbWFyayB0YWcgYXMgbGF0ZVxuXHRcdFx0XHR9LFxuXHRcdFx0XHRpbmxpbmU6ICFsaW5rQ3R4LFxuXHRcdFx0XHR0YWdOYW1lOiBcIjpcIixcblx0XHRcdFx0Y29udmVydDogY29udmVydGVyLFxuXHRcdFx0XHRvbkFycmF5Q2hhbmdlOiB0cnVlLFxuXHRcdFx0XHRmbG93OiB0cnVlLFxuXHRcdFx0XHR0YWdDdHg6IHRhZ0N0eCxcblx0XHRcdFx0dGFnQ3R4czogW3RhZ0N0eF0sXG5cdFx0XHRcdF9pczogXCJ0YWdcIlxuXHRcdFx0fSk7XG5cdFx0XHRhcmdzTGVuID0gdGFnQ3R4LmFyZ3MubGVuZ3RoO1xuXHRcdFx0aWYgKGFyZ3NMZW4+MSkge1xuXHRcdFx0XHRiaW5kVG8gPSB0YWcuYmluZFRvID0gW107XG5cdFx0XHRcdHdoaWxlIChhcmdzTGVuLS0pIHtcblx0XHRcdFx0XHRiaW5kVG8udW5zaGlmdChhcmdzTGVuKTsgLy8gQmluZCB0byBhbGwgdGhlIGFyZ3VtZW50cyAtIGdlbmVyYXRlIGJpbmRUbyBhcnJheTogWzAsMSwyLi4uXVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0XHRpZiAobGlua0N0eCkge1xuXHRcdFx0XHRsaW5rQ3R4LnRhZyA9IHRhZztcblx0XHRcdFx0dGFnLmxpbmtDdHggPSBsaW5rQ3R4O1xuXHRcdFx0fVxuXHRcdFx0dGFnQ3R4LmN0eCA9IGV4dGVuZEN0eCh0YWdDdHguY3R4LCAobGlua0N0eCA/IGxpbmtDdHgudmlldyA6IHZpZXcpLmN0eCk7XG5cdFx0XHR0YWdIYW5kbGVyc0Zyb21Qcm9wcyh0YWc
sIHRhZ0N0eCk7XG5cdFx0fVxuXHRcdHRhZy5fZXIgPSBvbkVycm9yICYmIHZhbHVlO1xuXHRcdHRhZy5jdHggPSB0YWdDdHguY3R4IHx8IHRhZy5jdHggfHwge307XG5cdFx0dGFnQ3R4LmN0eCA9IHVuZGVmaW5lZDtcblx0XHR2YWx1ZSA9IHRhZy5jdnRBcmdzKClbMF07IC8vIElmIHRoZXJlIGlzIGEgY29udmVydEJhY2sgYnV0IG5vIGNvbnZlcnQsIGNvbnZlcnRlciB3aWxsIGJlIFwidHJ1ZVwiXG5cdFx0dGFnLl9lciA9IG9uRXJyb3IgJiYgdmFsdWU7XG5cdH0gZWxzZSB7XG5cdFx0dmFsdWUgPSB0YWdDdHguYXJnc1swXTtcblx0fVxuXG5cdC8vIENhbGwgb25SZW5kZXIgKHVzZWQgYnkgSnNWaWV3cyBpZiBwcmVzZW50LCB0byBhZGQgYmluZGluZyBhbm5vdGF0aW9ucyBhcm91bmQgcmVuZGVyZWQgY29udGVudClcblx0dmFsdWUgPSBib3VuZFRhZyAmJiB2aWV3Ll8ub25SZW5kZXJcblx0XHQ/IHZpZXcuXy5vblJlbmRlcih2YWx1ZSwgdmlldywgdGFnKVxuXHRcdDogdmFsdWU7XG5cdHJldHVybiB2YWx1ZSAhPSB1bmRlZmluZWQgPyB2YWx1ZSA6IFwiXCI7XG59XG5cbmZ1bmN0aW9uIGNvbnZlcnRBcmdzKHRhZ0Vsc2UsIGJvdW5kKSB7IC8vIHRhZy5jdnRBcmdzKCkgb3IgdGFnLmN2dEFyZ3ModGFnRWxzZT8sIHRydWU/KVxuXHR2YXIgbCwga2V5LCBib3VuZEFyZ3MsIGFyZ3MsIGJpbmRGcm9tLCB0YWcsIGNvbnZlcnRlcixcblx0XHR0YWdDdHggPSB0aGlzO1xuXG5cdGlmICh0YWdDdHgudGFnTmFtZSkge1xuXHRcdHRhZyA9IHRhZ0N0eDtcbl
x0XHR0YWdDdHggPSAodGFnLnRhZ0N0eHMgfHwgW3RhZ0N0eF0pW3RhZ0Vsc2V8fDBdO1xuXHRcdGlmICghdGFnQ3R4KSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHR9IGVsc2Uge1xuXHRcdHRhZyA9IHRhZ0N0eC50YWc7XG5cdH1cblxuXHRiaW5kRnJvbSA9IHRhZy5iaW5kRnJvbTtcblx0YXJncyA9IHRhZ0N0eC5hcmdzO1xuXG5cdGlmICgoY29udmVydGVyID0gdGFnLmNvbnZlcnQpICYmIFwiXCIgKyBjb252ZXJ0ZXIgPT09IGNvbnZlcnRlcikge1xuXHRcdGNvbnZlcnRlciA9IGNvbnZlcnRlciA9PT0gXCJ0cnVlXCJcblx0XHRcdD8gdW5kZWZpbmVkXG5cdFx0XHQ6ICh0YWdDdHgudmlldy5nZXRSc2MoXCJjb252ZXJ0ZXJzXCIsIGNvbnZlcnRlcikgfHwgZXJyb3IoXCJVbmtub3duIGNvbnZlcnRlcjogJ1wiICsgY29udmVydGVyICsgXCInXCIpKTtcblx0fVxuXG5cdGlmIChjb252ZXJ0ZXIgJiYgIWJvdW5kKSB7IC8vIElmIHRoZXJlIGlzIGEgY29udmVydGVyLCB1c2UgYSBjb3B5IG9mIHRoZSB0YWdDdHguYXJncyBhcnJheSBmb3IgcmVuZGVyaW5nLCBhbmQgcmVwbGFjZSB0aGUgYXJnc1swXSBpblxuXHRcdGFyZ3MgPSBhcmdzLnNsaWNlKCk7IC8vIHRoZSBjb3BpZWQgYXJyYXkgd2l0aCB0aGUgY29udmVydGVkIHZhbHVlLiBCdXQgd2UgZG8gbm90IG1vZGlmeSB0aGUgdmFsdWUgb2YgdGFnLnRhZ0N0eC5hcmdzWzBdICh0aGUgb3JpZ2luYWwgYXJncyBhcnJheSlcblx0fVxuXHRpZiAoYmluZEZyb20pIHsgLy8gR2V0IHRoZSB2YWx1ZXMgb
2YgdGhlIGJvdW5kQXJnc1xuXHRcdGJvdW5kQXJncyA9IFtdO1xuXHRcdGwgPSBiaW5kRnJvbS5sZW5ndGg7XG5cdFx0d2hpbGUgKGwtLSkge1xuXHRcdFx0a2V5ID0gYmluZEZyb21bbF07XG5cdFx0XHRib3VuZEFyZ3MudW5zaGlmdChhcmdPclByb3AodGFnQ3R4LCBrZXkpKTtcblx0XHR9XG5cdFx0aWYgKGJvdW5kKSB7XG5cdFx0XHRhcmdzID0gYm91bmRBcmdzOyAvLyBDYWxsIHRvIGJuZEFyZ3MoKSAtIHJldHVybnMgdGhlIGJvdW5kQXJnc1xuXHRcdH1cblx0fVxuXHRpZiAoY29udmVydGVyKSB7XG5cdFx0Y29udmVydGVyID0gY29udmVydGVyLmFwcGx5KHRhZywgYm91bmRBcmdzIHx8IGFyZ3MpO1xuXHRcdGlmIChjb252ZXJ0ZXIgPT09IHVuZGVmaW5lZCkge1xuXHRcdFx0cmV0dXJuIGFyZ3M7IC8vIFJldHVybmluZyB1bmRlZmluZWQgZnJvbSBhIGNvbnZlcnRlciBpcyBlcXVpdmFsZW50IHRvIG5vdCBoYXZpbmcgYSBjb252ZXJ0ZXIuXG5cdFx0fVxuXHRcdGJpbmRGcm9tID0gYmluZEZyb20gfHwgWzBdO1xuXHRcdGwgPSBiaW5kRnJvbS5sZW5ndGg7XG5cdFx0aWYgKCEkaXNBcnJheShjb252ZXJ0ZXIpIHx8IGNvbnZlcnRlci5sZW5ndGggIT09IGwpIHtcblx0XHRcdGNvbnZlcnRlciA9IFtjb252ZXJ0ZXJdO1xuXHRcdFx0YmluZEZyb20gPSBbMF07XG5cdFx0XHRsID0gMTtcblx0XHR9XG5cdFx0aWYgKGJvdW5kKSB7ICAgICAgICAvLyBDYWxsIHRvIGJuZEFyZ3MoKSAtIHNvIGFwcGx5IGNvbnZlcnRlciB0byBhbGwgYm91bmRBcmdzXG5c
dFx0XHRhcmdzID0gY29udmVydGVyOyAvLyBUaGUgYXJyYXkgb2YgdmFsdWVzIHJldHVybmVkIGZyb20gdGhlIGNvbnZlcnRlclxuXHRcdH0gZWxzZSB7ICAgICAgICAgICAgLy8gQ2FsbCB0byBjdnRBcmdzKClcblx0XHRcdHdoaWxlIChsLS0pIHtcblx0XHRcdFx0a2V5ID0gYmluZEZyb21bbF07XG5cdFx0XHRcdGlmICgra2V5ID09PSBrZXkpIHtcblx0XHRcdFx0XHRhcmdzW2tleV0gPSBjb252ZXJ0ZXJbbF07XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH1cblx0cmV0dXJuIGFyZ3M7XG59XG5cbmZ1bmN0aW9uIGFyZ09yUHJvcChjb250ZXh0LCBrZXkpIHtcblx0Y29udGV4dCA9IGNvbnRleHRbK2tleSA9PT0ga2V5ID8gXCJhcmdzXCIgOiBcInByb3BzXCJdO1xuXHRyZXR1cm4gY29udGV4dCAmJiBjb250ZXh0W2tleV07XG59XG5cbmZ1bmN0aW9uIGNvbnZlcnRCb3VuZEFyZ3ModGFnRWxzZSkgeyAvLyB0YWcuYm5kQXJncygpXG5cdHJldHVybiB0aGlzLmN2dEFyZ3ModGFnRWxzZSwgMSk7XG59XG5cbi8vPT09PT09PT09PT09PVxuLy8gdmlld3MudGFnXG4vLz09PT09PT09PT09PT1cblxuLyogdmlldy5nZXRSc2MoKSAqL1xuZnVuY3Rpb24gZ2V0UmVzb3VyY2UocmVzb3VyY2VUeXBlLCBpdGVtTmFtZSkge1xuXHR2YXIgcmVzLCBzdG9yZSxcblx0XHR2aWV3ID0gdGhpcztcblx0aWYgKFwiXCIgKyBpdGVtTmFtZSA9PT0gaXRlbU5hbWUpIHtcblx0XHR3aGlsZSAoKHJlcyA9PT0gdW5kZWZpbmVkKSAmJiB2aWV3KSB7XG5cdFx0XHRzdG9
yZSA9IHZpZXcudG1wbCAmJiB2aWV3LnRtcGxbcmVzb3VyY2VUeXBlXTtcblx0XHRcdHJlcyA9IHN0b3JlICYmIHN0b3JlW2l0ZW1OYW1lXTtcblx0XHRcdHZpZXcgPSB2aWV3LnBhcmVudDtcblx0XHR9XG5cdFx0cmV0dXJuIHJlcyB8fCAkdmlld3NbcmVzb3VyY2VUeXBlXVtpdGVtTmFtZV07XG5cdH1cbn1cblxuZnVuY3Rpb24gcmVuZGVyVGFnKHRhZ05hbWUsIHBhcmVudFZpZXcsIHRtcGwsIHRhZ0N0eHMsIGlzVXBkYXRlLCBvbkVycm9yKSB7XG5cdGZ1bmN0aW9uIGJpbmRUb09yQmluZEZyb20odHlwZSkge1xuXHRcdHZhciBiaW5kQXJyYXkgPSB0YWdbdHlwZV07XG5cblx0XHRpZiAoYmluZEFycmF5ICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdGJpbmRBcnJheSA9ICRpc0FycmF5KGJpbmRBcnJheSkgPyBiaW5kQXJyYXkgOiBbYmluZEFycmF5XTtcblx0XHRcdG0gPSBiaW5kQXJyYXkubGVuZ3RoO1xuXHRcdFx0d2hpbGUgKG0tLSkge1xuXHRcdFx0XHRrZXkgPSBiaW5kQXJyYXlbbV07XG5cdFx0XHRcdGlmICghaXNOYU4ocGFyc2VJbnQoa2V5KSkpIHtcblx0XHRcdFx0XHRiaW5kQXJyYXlbbV0gPSBwYXJzZUludChrZXkpOyAvLyBDb252ZXJ0IFwiMFwiIHRvIDAsIGV0Yy5cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiBiaW5kQXJyYXkgfHwgWzBdO1xuXHR9XG5cblx0cGFyZW50VmlldyA9IHBhcmVudFZpZXcgfHwgdG9wVmlldztcblx0dmFyIHRhZywgdGFnRGVmLCB0ZW1wbGF0ZSwgdGFncywgYXR0ciwgcGFyZW50VG
FnLCBsLCBtLCBuLCBpdGVtUmV0LCB0YWdDdHgsIHRhZ0N0eEN0eCwgY3R4UHJtLCBiaW5kVG8sIGJpbmRGcm9tLCBpbml0VmFsLFxuXHRcdGNvbnRlbnQsIGNhbGxJbml0LCBtYXBEZWYsIHRoaXNNYXAsIGFyZ3MsIGJkQXJncywgcHJvcHMsIHRhZ0RhdGFNYXAsIGNvbnRlbnRDdHgsIGtleSwgYmluZEZyb21MZW5ndGgsIGJpbmRUb0xlbmd0aCwgbGlua2VkRWxlbWVudCwgZGVmYXVsdEN0eCxcblx0XHRpID0gMCxcblx0XHRyZXQgPSBcIlwiLFxuXHRcdGxpbmtDdHggPSBwYXJlbnRWaWV3Ll9sYyB8fCBmYWxzZSwgLy8gRm9yIGRhdGEtbGluaz1cIntteVRhZy4uLn1cIi4uLiBTZWUgb25EYXRhTGlua2VkVGFnQ2hhbmdlXG5cdFx0Y3R4ID0gcGFyZW50Vmlldy5jdHgsXG5cdFx0cGFyZW50VG1wbCA9IHRtcGwgfHwgcGFyZW50Vmlldy50bXBsLFxuXHRcdC8vIElmIHRhZ0N0eHMgaXMgYW4gaW50ZWdlciwgdGhlbiBpdCBpcyB0aGUga2V5IGZvciB0aGUgY29tcGlsZWQgZnVuY3Rpb24gdG8gcmV0dXJuIHRoZSBib3VuZFRhZyB0YWdDdHhzXG5cdFx0Ym91bmRUYWcgPSB0eXBlb2YgdGFnQ3R4cyA9PT0gXCJudW1iZXJcIiAmJiBwYXJlbnRWaWV3LnRtcGwuYm5kc1t0YWdDdHhzLTFdO1xuXG5cdGlmICh0YWdOYW1lLl9pcyA9PT0gXCJ0YWdcIikge1xuXHRcdHRhZyA9IHRhZ05hbWU7XG5cdFx0dGFnTmFtZSA9IHRhZy50YWdOYW1lO1xuXHRcdHRhZ0N0eHMgPSB0YWcudGFnQ3R4cztcblx0XHR0ZW1wbGF0ZSA9IHRhZy50ZW1wbGF0ZTtcblx0fSBlbHNlI
Htcblx0XHR0YWdEZWYgPSBwYXJlbnRWaWV3LmdldFJzYyhcInRhZ3NcIiwgdGFnTmFtZSkgfHwgZXJyb3IoXCJVbmtub3duIHRhZzoge3tcIiArIHRhZ05hbWUgKyBcIn19IFwiKTtcblx0XHR0ZW1wbGF0ZSA9IHRhZ0RlZi50ZW1wbGF0ZTtcblx0fVxuXHRpZiAob25FcnJvciA9PT0gdW5kZWZpbmVkICYmIGJvdW5kVGFnICYmIChib3VuZFRhZy5fbHIgPSAodGFnRGVmLmxhdGVSZW5kZXIgJiYgYm91bmRUYWcuX2xyIT09IGZhbHNlIHx8IGJvdW5kVGFnLl9scikpKSB7XG5cdFx0b25FcnJvciA9IFwiXCI7IC8vIElmIGxhdGVSZW5kZXIsIHNldCB0ZW1wb3Jhcnkgb25FcnJvciwgdG8gc2tpcCBpbml0aWFsIHJlbmRlcmluZyAoYW5kIHJlbmRlciBqdXN0IFwiXCIpXG5cdH1cblx0aWYgKG9uRXJyb3IgIT09IHVuZGVmaW5lZCkge1xuXHRcdHJldCArPSBvbkVycm9yO1xuXHRcdHRhZ0N0eHMgPSBvbkVycm9yID0gW3twcm9wczoge30sIGFyZ3M6IFtdLCBwYXJhbXM6IHtwcm9wczp7fX19XTtcblx0fSBlbHNlIGlmIChib3VuZFRhZykge1xuXHRcdHRhZ0N0eHMgPSBib3VuZFRhZyhwYXJlbnRWaWV3LmRhdGEsIHBhcmVudFZpZXcsICRzdWIpO1xuXHR9XG5cblx0bCA9IHRhZ0N0eHMubGVuZ3RoO1xuXHRmb3IgKDsgaSA8IGw7IGkrKykge1xuXHRcdHRhZ0N0eCA9IHRhZ0N0eHNbaV07XG5cdFx0Y29udGVudCA9IHRhZ0N0eC50bXBsO1xuXHRcdGlmICghbGlua0N0eCB8fCAhbGlua0N0eC50YWcgfHwgaSAmJiAhbGlua0N0eC50YWcuaW5saW5lIHx8IHRh
Zy5fZXIgfHwgY29udGVudCAmJiArY29udGVudD09PWNvbnRlbnQpIHtcblx0XHRcdC8vIEluaXRpYWxpemUgdGFnQ3R4XG5cdFx0XHQvLyBGb3IgYmxvY2sgdGFncywgdGFnQ3R4LnRtcGwgaXMgYW4gaW50ZWdlciA+IDBcblx0XHRcdGlmIChjb250ZW50ICYmIHBhcmVudFRtcGwudG1wbHMpIHtcblx0XHRcdFx0dGFnQ3R4LnRtcGwgPSB0YWdDdHguY29udGVudCA9IHBhcmVudFRtcGwudG1wbHNbY29udGVudCAtIDFdOyAvLyBTZXQgdGhlIHRtcGwgcHJvcGVydHkgdG8gdGhlIGNvbnRlbnQgb2YgdGhlIGJsb2NrIHRhZ1xuXHRcdFx0fVxuXHRcdFx0dGFnQ3R4LmluZGV4ID0gaTtcblx0XHRcdHRhZ0N0eC5jdHhQcm0gPSBjb250ZXh0UGFyYW1ldGVyO1xuXHRcdFx0dGFnQ3R4LnJlbmRlciA9IHJlbmRlckNvbnRlbnQ7XG5cdFx0XHR0YWdDdHguY3Z0QXJncyA9IGNvbnZlcnRBcmdzO1xuXHRcdFx0dGFnQ3R4LmJuZEFyZ3MgPSBjb252ZXJ0Qm91bmRBcmdzO1xuXHRcdFx0dGFnQ3R4LnZpZXcgPSBwYXJlbnRWaWV3O1xuXHRcdFx0dGFnQ3R4LmN0eCA9IGV4dGVuZEN0eChleHRlbmRDdHgodGFnQ3R4LmN0eCwgdGFnRGVmICYmIHRhZ0RlZi5jdHgpLCBjdHgpOyAvLyBDbG9uZSBhbmQgZXh0ZW5kIHBhcmVudFZpZXcuY3R4XG5cdFx0fVxuXHRcdGlmICh0bXBsID0gdGFnQ3R4LnByb3BzLnRtcGwpIHtcblx0XHRcdC8vIElmIHRoZSB0bXBsIHByb3BlcnR5IGlzIG92ZXJyaWRkZW4sIHNldCB0aGUgdmFsdWUgKHdoZW4gaW5pdGlhbGl6aW5nLCBvciwgaW4
gY2FzZSBvZiBiaW5kaW5nOiBedG1wbD0uLi4sIHdoZW4gdXBkYXRpbmcpXG5cdFx0XHR0YWdDdHgudG1wbCA9IHBhcmVudFZpZXcuX2dldFRtcGwodG1wbCk7XG5cdFx0XHR0YWdDdHguY29udGVudCA9IHRhZ0N0eC5jb250ZW50IHx8IHRhZ0N0eC50bXBsO1xuXHRcdH1cblxuXHRcdGlmICghdGFnKSB7XG5cdFx0XHQvLyBUaGlzIHdpbGwgb25seSBiZSBoaXQgZm9yIGluaXRpYWwgdGFnQ3R4IChub3QgZm9yIHt7ZWxzZX19KSAtIGlmIHRoZSB0YWcgaW5zdGFuY2UgZG9lcyBub3QgZXhpc3QgeWV0XG5cdFx0XHQvLyBJZiB0aGUgdGFnIGhhcyBub3QgYWxyZWFkeSBiZWVuIGluc3RhbnRpYXRlZCwgd2Ugd2lsbCBjcmVhdGUgYSBuZXcgaW5zdGFuY2UuXG5cdFx0XHQvLyB+dGFnIHdpbGwgYWNjZXNzIHRoZSB0YWcsIGV2ZW4gd2l0aGluIHRoZSByZW5kZXJpbmcgb2YgdGhlIHRlbXBsYXRlIGNvbnRlbnQgb2YgdGhpcyB0YWcuXG5cdFx0XHQvLyBGcm9tIGNoaWxkL2Rlc2NlbmRhbnQgdGFncywgY2FuIGFjY2VzcyB1c2luZyB+dGFnLnBhcmVudCwgb3IgfnBhcmVudFRhZ3MudGFnTmFtZVxuXHRcdFx0dGFnID0gbmV3IHRhZ0RlZi5fY3RyKCk7XG5cdFx0XHRjYWxsSW5pdCA9ICEhdGFnLmluaXQ7XG5cblx0XHRcdHRhZy5wYXJlbnQgPSBwYXJlbnRUYWcgPSBjdHggJiYgY3R4LnRhZztcblx0XHRcdHRhZy50YWdDdHhzID0gdGFnQ3R4cztcblxuXHRcdFx0aWYgKGxpbmtDdHgpIHtcblx0XHRcdFx0dGFnLmlubGluZSA9IGZhbHNlO1xuXHRcdFx0XHRsaW
5rQ3R4LnRhZyA9IHRhZztcblx0XHRcdH1cblx0XHRcdHRhZy5saW5rQ3R4ID0gbGlua0N0eDtcblx0XHRcdGlmICh0YWcuXy5ibmQgPSBib3VuZFRhZyB8fCBsaW5rQ3R4LmZuKSB7XG5cdFx0XHRcdC8vIEJvdW5kIGlmIHtee3RhZy4uLn19IG9yIGRhdGEtbGluaz1cInt0YWcuLi59XCJcblx0XHRcdFx0dGFnLl8udGhzID0gdGFnQ3R4LnBhcmFtcy5wcm9wcy50aGlzOyAvLyBUYWcgaGFzIGEgdGhpcz1leHByIGJpbmRpbmcsIHRvIGdldCBqYXZhc2NyaXB0IHJlZmVyZW5jZSB0byB0YWcgaW5zdGFuY2Vcblx0XHRcdFx0dGFnLl8ubHQgPSB0YWdDdHhzLmx0OyAvLyBJZiBhIGxhdGUgcGF0aCBAc29tZS5wYXRoIGhhcyBub3QgcmV0dXJuZWQgQHNvbWUgb2JqZWN0LCBtYXJrIHRhZyBhcyBsYXRlXG5cdFx0XHRcdHRhZy5fLmFyclZ3cyA9IHt9O1xuXHRcdFx0fSBlbHNlIGlmICh0YWcuZGF0YUJvdW5kT25seSkge1xuXHRcdFx0XHRlcnJvcih0YWdOYW1lICsgXCIgbXVzdCBiZSBkYXRhLWJvdW5kOlxcbntee1wiICsgdGFnTmFtZSArIFwifX1cIik7XG5cdFx0XHR9XG5cdFx0XHQvL1RPRE8gYmV0dGVyIHBlcmYgZm9yIGNoaWxkVGFncygpIC0ga2VlcCBjaGlsZCB0YWcudGFncyBhcnJheSwgKGFuZCByZW1vdmUgY2hpbGQsIHdoZW4gZGlzcG9zZWQpXG5cdFx0XHQvLyB0YWcudGFncyA9IFtdO1xuXHRcdH0gZWxzZSBpZiAobGlua0N0eCAmJiBsaW5rQ3R4LmZuLl9scikge1xuXHRcdFx0Y2FsbEluaXQgPSAhIXRhZy5pbml0O1xuXHRcdH1cblx0XHR0Y
WdEYXRhTWFwID0gdGFnLmRhdGFNYXA7XG5cblx0XHR0YWdDdHgudGFnID0gdGFnO1xuXHRcdGlmICh0YWdEYXRhTWFwICYmIHRhZ0N0eHMpIHtcblx0XHRcdHRhZ0N0eC5tYXAgPSB0YWdDdHhzW2ldLm1hcDsgLy8gQ29weSBvdmVyIHRoZSBjb21waWxlZCBtYXAgaW5zdGFuY2UgZnJvbSB0aGUgcHJldmlvdXMgdGFnQ3R4cyB0byB0aGUgcmVmcmVzaGVkIG9uZXNcblx0XHR9XG5cdFx0aWYgKCF0YWcuZmxvdykge1xuXHRcdFx0dGFnQ3R4Q3R4ID0gdGFnQ3R4LmN0eCA9IHRhZ0N0eC5jdHggfHwge307XG5cblx0XHRcdC8vIHRhZ3MgaGFzaDogdGFnLmN0eC50YWdzLCBtZXJnZWQgd2l0aCBwYXJlbnRWaWV3LmN0eC50YWdzLFxuXHRcdFx0dGFncyA9IHRhZy5wYXJlbnRzID0gdGFnQ3R4Q3R4LnBhcmVudFRhZ3MgPSBjdHggJiYgZXh0ZW5kQ3R4KHRhZ0N0eEN0eC5wYXJlbnRUYWdzLCBjdHgucGFyZW50VGFncykgfHwge307XG5cdFx0XHRpZiAocGFyZW50VGFnKSB7XG5cdFx0XHRcdHRhZ3NbcGFyZW50VGFnLnRhZ05hbWVdID0gcGFyZW50VGFnO1xuXHRcdFx0XHQvL1RPRE8gYmV0dGVyIHBlcmYgZm9yIGNoaWxkVGFnczogcGFyZW50VGFnLnRhZ3MucHVzaCh0YWcpO1xuXHRcdFx0fVxuXHRcdFx0dGFnc1t0YWcudGFnTmFtZV0gPSB0YWdDdHhDdHgudGFnID0gdGFnO1xuXHRcdFx0dGFnQ3R4Q3R4LnRhZ0N0eCA9IHRhZ0N0eDtcblx0XHR9XG5cdH1cblx0aWYgKCEodGFnLl9lciA9IG9uRXJyb3IpKSB7XG5cdFx0dGFnSGFuZGxlcnNGcm9tUHJvcHMo
dGFnLCB0YWdDdHhzWzBdKTtcblx0XHR0YWcucmVuZGVyaW5nID0ge3JuZHI6IHRhZy5yZW5kZXJpbmd9OyAvLyBQcm92aWRlIG9iamVjdCBmb3Igc3RhdGUgZHVyaW5nIHJlbmRlciBjYWxscyB0byB0YWcgYW5kIGVsc2VzLiAoVXNlZCBieSB7e2lmfX0gYW5kIHt7Zm9yfX0uLi4pXG5cdFx0Zm9yIChpID0gMDsgaSA8IGw7IGkrKykgeyAvLyBJdGVyYXRlIHRhZ0N0eCBmb3IgZWFjaCB7e2Vsc2V9fSBibG9ja1xuXHRcdFx0dGFnQ3R4ID0gdGFnLnRhZ0N0eCA9IHRhZ0N0eHNbaV07XG5cdFx0XHRwcm9wcyA9IHRhZ0N0eC5wcm9wcztcblx0XHRcdHRhZy5jdHggPSB0YWdDdHguY3R4O1xuXG5cdFx0XHRpZiAoIWkpIHtcblx0XHRcdFx0aWYgKGNhbGxJbml0KSB7XG5cdFx0XHRcdFx0dGFnLmluaXQodGFnQ3R4LCBsaW5rQ3R4LCB0YWcuY3R4KTtcblx0XHRcdFx0XHRjYWxsSW5pdCA9IHVuZGVmaW5lZDtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoIXRhZ0N0eC5hcmdzLmxlbmd0aCAmJiB0YWdDdHguYXJnRGVmYXVsdCAhPT0gZmFsc2UgJiYgdGFnLmFyZ0RlZmF1bHQgIT09IGZhbHNlKSB7XG5cdFx0XHRcdFx0dGFnQ3R4LmFyZ3MgPSBhcmdzID0gW3RhZ0N0eC52aWV3LmRhdGFdOyAvLyBNaXNzaW5nIGZpcnN0IGFyZyBkZWZhdWx0cyB0byB0aGUgY3VycmVudCBkYXRhIGNvbnRleHRcblx0XHRcdFx0XHR0YWdDdHgucGFyYW1zLmFyZ3MgPSBbXCIjZGF0YVwiXTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGJpbmRUbyA9IGJpbmRUb09yQmluZEZyb20oXCJ
iaW5kVG9cIik7XG5cblx0XHRcdFx0aWYgKHRhZy5iaW5kVG8gIT09IHVuZGVmaW5lZCkge1xuXHRcdFx0XHRcdHRhZy5iaW5kVG8gPSBiaW5kVG87XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAodGFnLmJpbmRGcm9tICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdFx0XHR0YWcuYmluZEZyb20gPSBiaW5kVG9PckJpbmRGcm9tKFwiYmluZEZyb21cIik7XG5cdFx0XHRcdH0gZWxzZSBpZiAodGFnLmJpbmRUbykge1xuXHRcdFx0XHRcdHRhZy5iaW5kRnJvbSA9IHRhZy5iaW5kVG8gPSBiaW5kVG87XG5cdFx0XHRcdH1cblx0XHRcdFx0YmluZEZyb20gPSB0YWcuYmluZEZyb20gfHwgYmluZFRvO1xuXG5cdFx0XHRcdGJpbmRUb0xlbmd0aCA9IGJpbmRUby5sZW5ndGg7XG5cdFx0XHRcdGJpbmRGcm9tTGVuZ3RoID0gYmluZEZyb20ubGVuZ3RoO1xuXG5cdFx0XHRcdGlmICh0YWcuXy5ibmQgJiYgKGxpbmtlZEVsZW1lbnQgPSB0YWcubGlua2VkRWxlbWVudCkpIHtcblx0XHRcdFx0XHR0YWcubGlua2VkRWxlbWVudCA9IGxpbmtlZEVsZW1lbnQgPSAkaXNBcnJheShsaW5rZWRFbGVtZW50KSA/IGxpbmtlZEVsZW1lbnQ6IFtsaW5rZWRFbGVtZW50XTtcblxuXHRcdFx0XHRcdGlmIChiaW5kVG9MZW5ndGggIT09IGxpbmtlZEVsZW1lbnQubGVuZ3RoKSB7XG5cdFx0XHRcdFx0XHRlcnJvcihcImxpbmtlZEVsZW1lbnQgbm90IHNhbWUgbGVuZ3RoIGFzIGJpbmRUb1wiKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKGxpbmtlZEVsZW1lbnQgPS
B0YWcubGlua2VkQ3R4UGFyYW0pIHtcblx0XHRcdFx0XHR0YWcubGlua2VkQ3R4UGFyYW0gPSBsaW5rZWRFbGVtZW50ID0gJGlzQXJyYXkobGlua2VkRWxlbWVudCkgPyBsaW5rZWRFbGVtZW50OiBbbGlua2VkRWxlbWVudF07XG5cblx0XHRcdFx0XHRpZiAoYmluZEZyb21MZW5ndGggIT09IGxpbmtlZEVsZW1lbnQubGVuZ3RoKSB7XG5cdFx0XHRcdFx0XHRlcnJvcihcImxpbmtlZEN0eFBhcmFtIG5vdCBzYW1lIGxlbmd0aCBhcyBiaW5kRnJvbS9iaW5kVG9cIik7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKGJpbmRGcm9tKSB7XG5cdFx0XHRcdFx0dGFnLl8uZnJvbUluZGV4ID0ge307IC8vIEhhc2ggb2YgYmluZEZyb20gaW5kZXggd2hpY2ggaGFzIHNhbWUgcGF0aCB2YWx1ZSBhcyBiaW5kVG8gaW5kZXguIGZyb21JbmRleCA9IHRhZy5fLmZyb21JbmRleFt0b0luZGV4XVxuXHRcdFx0XHRcdHRhZy5fLnRvSW5kZXggPSB7fTsgLy8gSGFzaCBvZiBiaW5kRnJvbSBpbmRleCB3aGljaCBoYXMgc2FtZSBwYXRoIHZhbHVlIGFzIGJpbmRUbyBpbmRleC4gZnJvbUluZGV4ID0gdGFnLl8uZnJvbUluZGV4W3RvSW5kZXhdXG5cdFx0XHRcdFx0biA9IGJpbmRGcm9tTGVuZ3RoO1xuXHRcdFx0XHRcdHdoaWxlIChuLS0pIHtcblx0XHRcdFx0XHRcdGtleSA9IGJpbmRGcm9tW25dO1xuXHRcdFx0XHRcdFx0bSA9IGJpbmRUb0xlbmd0aDtcblx0XHRcdFx0XHRcdHdoaWxlIChtLS0pIHtcblx0XHRcdFx0XHRcdFx0aWYgKGtleSA9PT0gYmluZ
FRvW21dKSB7XG5cdFx0XHRcdFx0XHRcdFx0dGFnLl8uZnJvbUluZGV4W21dID0gbjtcblx0XHRcdFx0XHRcdFx0XHR0YWcuXy50b0luZGV4W25dID0gbTtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmIChsaW5rQ3R4KSB7XG5cdFx0XHRcdFx0Ly8gU2V0IGF0dHIgb24gbGlua0N0eCB0byBlbnN1cmUgb3V0cHV0dGluZyB0byB0aGUgY29ycmVjdCB0YXJnZXQgYXR0cmlidXRlLlxuXHRcdFx0XHRcdC8vIFNldHRpbmcgZWl0aGVyIGxpbmtDdHguYXR0ciBvciB0aGlzLmF0dHIgaW4gdGhlIGluaXQoKSBhbGxvd3MgcGVyLWluc3RhbmNlIGNob2ljZSBvZiB0YXJnZXQgYXR0cmliLlxuXHRcdFx0XHRcdGxpbmtDdHguYXR0ciA9IHRhZy5hdHRyID0gbGlua0N0eC5hdHRyIHx8IHRhZy5hdHRyIHx8IGxpbmtDdHguX2RmQXQ7XG5cdFx0XHRcdH1cblx0XHRcdFx0YXR0ciA9IHRhZy5hdHRyO1xuXHRcdFx0XHR0YWcuXy5ub1Z3cyA9IGF0dHIgJiYgYXR0ciAhPT0gSFRNTDtcblx0XHRcdH1cblx0XHRcdGFyZ3MgPSB0YWcuY3Z0QXJncyhpKTtcblx0XHRcdGlmICh0YWcubGlua2VkQ3R4UGFyYW0pIHtcblx0XHRcdFx0YmRBcmdzID0gdGFnLmN2dEFyZ3MoaSwgMSk7XG5cdFx0XHRcdG0gPSBiaW5kRnJvbUxlbmd0aDtcblx0XHRcdFx0ZGVmYXVsdEN0eCA9IHRhZy5jb25zdHJ1Y3Rvci5wcm90b3R5cGUuY3R4O1xuXHRcdFx0XHR3aGlsZSAobS0tKSB7XG5cdFx0XHRcdFx0aWYgKGN0
eFBybSA9IHRhZy5saW5rZWRDdHhQYXJhbVttXSkge1xuXHRcdFx0XHRcdFx0a2V5ID0gYmluZEZyb21bbV07XG5cdFx0XHRcdFx0XHRpbml0VmFsID0gYmRBcmdzW21dO1xuXHRcdFx0XHRcdFx0Ly8gQ3JlYXRlIHRhZyBjb250ZXh0dWFsIHBhcmFtZXRlclxuXHRcdFx0XHRcdFx0dGFnQ3R4LmN0eFtjdHhQcm1dID0gJHN1Yi5fY3AoXG5cdFx0XHRcdFx0XHRcdGRlZmF1bHRDdHggJiYgaW5pdFZhbCA9PT0gdW5kZWZpbmVkID8gZGVmYXVsdEN0eFtjdHhQcm1dOiBpbml0VmFsLFxuXHRcdFx0XHRcdFx0XHRpbml0VmFsICE9PSB1bmRlZmluZWQgJiYgYXJnT3JQcm9wKHRhZ0N0eC5wYXJhbXMsIGtleSksXG5cdFx0XHRcdFx0XHRcdHRhZ0N0eC52aWV3LFxuXHRcdFx0XHRcdFx0XHR0YWcuXy5ibmQgJiYge3RhZzogdGFnLCBjdnQ6IHRhZy5jb252ZXJ0LCBpbmQ6IG0sIHRhZ0Vsc2U6IGl9XG5cdFx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdFx0aWYgKChtYXBEZWYgPSBwcm9wcy5kYXRhTWFwIHx8IHRhZ0RhdGFNYXApICYmIChhcmdzLmxlbmd0aCB8fCBwcm9wcy5kYXRhTWFwKSkge1xuXHRcdFx0XHR0aGlzTWFwID0gdGFnQ3R4Lm1hcDtcblx0XHRcdFx0aWYgKCF0aGlzTWFwIHx8IHRoaXNNYXAuc3JjICE9PSBhcmdzWzBdIHx8IGlzVXBkYXRlKSB7XG5cdFx0XHRcdFx0aWYgKHRoaXNNYXAgJiYgdGhpc01hcC5zcmMpIHtcblx0XHRcdFx0XHRcdHRoaXNNYXAudW5tYXAoKTsgLy8gb25seSBjYWxsZWQgaWY
gb2JzZXJ2YWJsZSBtYXAgLSBub3Qgd2hlbiBvbmx5IHVzZWQgaW4gSnNSZW5kZXIsIGUuZy4gYnkge3twcm9wc319XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdG1hcERlZi5tYXAoYXJnc1swXSwgdGFnQ3R4LCB0aGlzTWFwLCAhdGFnLl8uYm5kKTtcblx0XHRcdFx0XHR0aGlzTWFwID0gdGFnQ3R4Lm1hcDtcblx0XHRcdFx0fVxuXHRcdFx0XHRhcmdzID0gW3RoaXNNYXAudGd0XTtcblx0XHRcdH1cblxuXHRcdFx0aXRlbVJldCA9IHVuZGVmaW5lZDtcblx0XHRcdGlmICh0YWcucmVuZGVyKSB7XG5cdFx0XHRcdGl0ZW1SZXQgPSB0YWcucmVuZGVyLmFwcGx5KHRhZywgYXJncyk7XG5cdFx0XHRcdGlmIChwYXJlbnRWaWV3LmxpbmtlZCAmJiBpdGVtUmV0ICYmICFyV3JhcHBlZEluVmlld01hcmtlci50ZXN0KGl0ZW1SZXQpKSB7XG5cdFx0XHRcdFx0Ly8gV2hlbiBhIHRhZyByZW5kZXJzIGNvbnRlbnQgZnJvbSB0aGUgcmVuZGVyIG1ldGhvZCwgd2l0aCBkYXRhIGxpbmtpbmcgdGhlbiB3ZSBuZWVkIHRvIHdyYXAgd2l0aCB2aWV3IG1hcmtlcnMsIGlmIGFic2VudCxcblx0XHRcdFx0XHQvLyB0byBwcm92aWRlIGEgY29udGVudFZpZXcgZm9yIHRoZSB0YWcsIHdoaWNoIHdpbGwgY29ycmVjdGx5IGRpc3Bvc2UgYmluZGluZ3MgaWYgZGVsZXRlZC4gVGhlICd0bXBsJyBmb3IgdGhpcyB2aWV3IHdpbGxcblx0XHRcdFx0XHQvLyBiZSBhIGR1bWJlZC1kb3duIHRlbXBsYXRlIHdoaWNoIHdpbGwgYWx3YXlzIHJldHVybiB0aGUgaXRlbVJldCBzdHJpbmcgKG5vIG
1hdHRlciB3aGF0IHRoZSBkYXRhIGlzKS4gVGhlIGl0ZW1SZXQgc3RyaW5nXG5cdFx0XHRcdFx0Ly8gaXMgbm90IGNvbXBpbGVkIGFzIHRlbXBsYXRlIG1hcmt1cCwgc28gY2FuIGluY2x1ZGUgXCJ7e1wiIG9yIFwifX1cIiB3aXRob3V0IHRyaWdnZXJpbmcgc3ludGF4IGVycm9yc1xuXHRcdFx0XHRcdHRtcGwgPSB7IC8vICdEdW1iZWQtZG93bicgdGVtcGxhdGUgd2hpY2ggYWx3YXlzIHJlbmRlcnMgJ3N0YXRpYycgaXRlbVJldCBzdHJpbmdcblx0XHRcdFx0XHRcdGxpbmtzOiBbXVxuXHRcdFx0XHRcdH07XG5cdFx0XHRcdFx0dG1wbC5yZW5kZXIgPSB0bXBsLmZuID0gZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0XHRyZXR1cm4gaXRlbVJldDtcblx0XHRcdFx0XHR9O1xuXHRcdFx0XHRcdGl0ZW1SZXQgPSByZW5kZXJXaXRoVmlld3ModG1wbCwgcGFyZW50Vmlldy5kYXRhLCB1bmRlZmluZWQsIHRydWUsIHBhcmVudFZpZXcsIHVuZGVmaW5lZCwgdW5kZWZpbmVkLCB0YWcpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0XHRpZiAoIWFyZ3MubGVuZ3RoKSB7XG5cdFx0XHRcdGFyZ3MgPSBbcGFyZW50Vmlld107IC8vIG5vIGFyZ3VtZW50cyAtIChlLmcuIHt7ZWxzZX19KSBnZXQgZGF0YSBjb250ZXh0IGZyb20gdmlldy5cblx0XHRcdH1cblx0XHRcdGlmIChpdGVtUmV0ID09PSB1bmRlZmluZWQpIHtcblx0XHRcdFx0Y29udGVudEN0eCA9IGFyZ3NbMF07IC8vIERlZmF1bHQgZGF0YSBjb250ZXh0IGZvciB3cmFwcGVkIGJsb2NrIGNvbnRlbnQgaXMgdGhlI
GZpcnN0IGFyZ3VtZW50XG5cdFx0XHRcdGlmICh0YWcuY29udGVudEN0eCkgeyAvLyBTZXQgdGFnLmNvbnRlbnRDdHggdG8gdHJ1ZSwgdG8gaW5oZXJpdCBwYXJlbnQgY29udGV4dCwgb3IgdG8gYSBmdW5jdGlvbiB0byBwcm92aWRlIGFsdGVybmF0ZSBjb250ZXh0LlxuXHRcdFx0XHRcdGNvbnRlbnRDdHggPSB0YWcuY29udGVudEN0eCA9PT0gdHJ1ZSA/IHBhcmVudFZpZXcgOiB0YWcuY29udGVudEN0eChjb250ZW50Q3R4KTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpdGVtUmV0ID0gdGFnQ3R4LnJlbmRlcihjb250ZW50Q3R4LCB0cnVlKSB8fCAoaXNVcGRhdGUgPyB1bmRlZmluZWQgOiBcIlwiKTtcblx0XHRcdH1cblx0XHRcdHJldCA9IHJldFxuXHRcdFx0XHQ/IHJldCArIChpdGVtUmV0IHx8IFwiXCIpXG5cdFx0XHRcdDogaXRlbVJldCAhPT0gdW5kZWZpbmVkXG5cdFx0XHRcdFx0PyBcIlwiICsgaXRlbVJldFxuXHRcdFx0XHRcdDogdW5kZWZpbmVkOyAvLyBJZiBubyByZXR1cm4gdmFsdWUgZnJvbSByZW5kZXIsIGFuZCBubyB0ZW1wbGF0ZS9jb250ZW50IHRhZ0N0eC5yZW5kZXIoLi4uKSwgcmV0dXJuIHVuZGVmaW5lZFxuXHRcdH1cblx0XHR0YWcucmVuZGVyaW5nID0gdGFnLnJlbmRlcmluZy5ybmRyOyAvLyBSZW1vdmUgdGFnLnJlbmRlcmluZyBvYmplY3QgKGlmIHRoaXMgaXMgb3V0ZXJtb3N0IHJlbmRlciBjYWxsLiAoSW4gY2FzZSBvZiBuZXN0ZWQgY2FsbHMpXG5cdH1cblx0dGFnLnRhZ0N0eCA9IHRhZ0N0eHNbMF07XG5cdHRhZy5jdHggPSB0
YWcudGFnQ3R4LmN0eDtcblxuXHRpZiAodGFnLl8ubm9Wd3MgJiYgdGFnLmlubGluZSkge1xuXHRcdC8vIGlubGluZSB0YWcgd2l0aCBhdHRyIHNldCB0byBcInRleHRcIiB3aWxsIGluc2VydCBIVE1MLWVuY29kZWQgY29udGVudCAtIGFzIGlmIGl0IHdhcyBlbGVtZW50LWJhc2VkIGlubmVyVGV4dFxuXHRcdHJldCA9IGF0dHIgPT09IFwidGV4dFwiXG5cdFx0XHQ/ICRjb252ZXJ0ZXJzLmh0bWwocmV0KVxuXHRcdFx0OiBcIlwiO1xuXHR9XG5cdHJldHVybiBib3VuZFRhZyAmJiBwYXJlbnRWaWV3Ll8ub25SZW5kZXJcblx0XHQvLyBDYWxsIG9uUmVuZGVyICh1c2VkIGJ5IEpzVmlld3MgaWYgcHJlc2VudCwgdG8gYWRkIGJpbmRpbmcgYW5ub3RhdGlvbnMgYXJvdW5kIHJlbmRlcmVkIGNvbnRlbnQpXG5cdFx0PyBwYXJlbnRWaWV3Ll8ub25SZW5kZXIocmV0LCBwYXJlbnRWaWV3LCB0YWcpXG5cdFx0OiByZXQ7XG59XG5cbi8vPT09PT09PT09PT09PT09PT1cbi8vIFZpZXcgY29uc3RydWN0b3Jcbi8vPT09PT09PT09PT09PT09PT1cblxuZnVuY3Rpb24gVmlldyhjb250ZXh0LCB0eXBlLCBwYXJlbnRWaWV3LCBkYXRhLCB0ZW1wbGF0ZSwga2V5LCBvblJlbmRlciwgY29udGVudFRtcGwpIHtcblx0Ly8gQ29uc3RydWN0b3IgZm9yIHZpZXcgb2JqZWN0IGluIHZpZXcgaGllcmFyY2h5LiAoQXVnbWVudGVkIGJ5IEpzVmlld3MgaWYgSnNWaWV3cyBpcyBsb2FkZWQpXG5cdHZhciB2aWV3cywgcGFyZW50Vmlld18sIHRhZywgc2VsZl8sXG5cdFx0c2VsZiA9IHR
oaXMsXG5cdFx0aXNBcnJheSA9IHR5cGUgPT09IFwiYXJyYXlcIjtcblx0XHQvLyBJZiB0aGUgZGF0YSBpcyBhbiBhcnJheSwgdGhpcyBpcyBhbiAnYXJyYXkgdmlldycgd2l0aCBhIHZpZXdzIGFycmF5IGZvciBlYWNoIGNoaWxkICdpdGVtIHZpZXcnXG5cdFx0Ly8gSWYgdGhlIGRhdGEgaXMgbm90IGFuIGFycmF5LCB0aGlzIGlzIGFuICdpdGVtIHZpZXcnIHdpdGggYSB2aWV3cyAnaGFzaCcgb2JqZWN0IGZvciBhbnkgY2hpbGQgbmVzdGVkIHZpZXdzXG5cblx0c2VsZi5jb250ZW50ID0gY29udGVudFRtcGw7XG5cdHNlbGYudmlld3MgPSBpc0FycmF5ID8gW10gOiB7fTtcblx0c2VsZi5kYXRhID0gZGF0YTtcblx0c2VsZi50bXBsID0gdGVtcGxhdGU7XG5cdHNlbGZfID0gc2VsZi5fID0ge1xuXHRcdGtleTogMCxcblx0XHQvLyAuXy51c2VLZXkgaXMgbm9uIHplcm8gaWYgaXMgbm90IGFuICdhcnJheSB2aWV3JyAob3duaW5nIGEgZGF0YSBhcnJheSkuIFVzZSB0aGlzIGFzIG5leHQga2V5IGZvciBhZGRpbmcgdG8gY2hpbGQgdmlld3MgaGFzaFxuXHRcdHVzZUtleTogaXNBcnJheSA/IDAgOiAxLFxuXHRcdGlkOiBcIlwiICsgdmlld0lkKyssXG5cdFx0b25SZW5kZXI6IG9uUmVuZGVyLFxuXHRcdGJuZHM6IHt9XG5cdH07XG5cdHNlbGYubGlua2VkID0gISFvblJlbmRlcjtcblx0c2VsZi50eXBlID0gdHlwZSB8fCBcInRvcFwiO1xuXHRpZiAodHlwZSkge1xuXHRcdHNlbGYuY2FjaGUgPSB7X2N0OiAkc3ViU2V0dGluZ3MuX2NjaEN0fTsgLy8gVXNlZC
Bmb3IgY2FjaGluZyByZXN1bHRzIG9mIGNvbXB1dGVkIHByb3BlcnRpZXMgYW5kIGhlbHBlcnMgKHZpZXcuZ2V0Q2FjaGUpXG5cdH1cblxuXHRpZiAoIXBhcmVudFZpZXcgfHwgcGFyZW50Vmlldy50eXBlID09PSBcInRvcFwiKSB7XG5cdFx0KHNlbGYuY3R4ID0gY29udGV4dCB8fCB7fSkucm9vdCA9IHNlbGYuZGF0YTtcblx0fVxuXG5cdGlmIChzZWxmLnBhcmVudCA9IHBhcmVudFZpZXcpIHtcblx0XHRzZWxmLnJvb3QgPSBwYXJlbnRWaWV3LnJvb3QgfHwgc2VsZjsgLy8gdmlldyB3aG9zZSBwYXJlbnQgaXMgdG9wIHZpZXdcblx0XHR2aWV3cyA9IHBhcmVudFZpZXcudmlld3M7XG5cdFx0cGFyZW50Vmlld18gPSBwYXJlbnRWaWV3Ll87XG5cdFx0c2VsZi5pc1RvcCA9IHBhcmVudFZpZXdfLnNjcDsgLy8gSXMgdG9wIGNvbnRlbnQgdmlldyBvZiBhIGxpbmsoXCIjY29udGFpbmVyXCIsIC4uLikgY2FsbFxuXHRcdHNlbGYuc2NvcGUgPSAoIWNvbnRleHQudGFnIHx8IGNvbnRleHQudGFnID09PSBwYXJlbnRWaWV3LmN0eC50YWcpICYmICFzZWxmLmlzVG9wICYmIHBhcmVudFZpZXcuc2NvcGUgfHwgc2VsZjtcblx0XHQvLyBTY29wZSBmb3IgY29udGV4dFBhcmFtcyAtIGNsb3Nlc3Qgbm9uIGZsb3cgdGFnIGFuY2VzdG9yIG9yIHJvb3Qgdmlld1xuXHRcdGlmIChwYXJlbnRWaWV3Xy51c2VLZXkpIHtcblx0XHRcdC8vIFBhcmVudCBpcyBub3QgYW4gJ2FycmF5IHZpZXcnLiBBZGQgdGhpcyB2aWV3IHRvIGl0cyB2aWV3cyBvYmplY3Rcblx0XHRcdC8vIHNlb
GYuX2tleSA9IGlzIHRoZSBrZXkgaW4gdGhlIHBhcmVudCB2aWV3IGhhc2hcblx0XHRcdHZpZXdzW3NlbGZfLmtleSA9IFwiX1wiICsgcGFyZW50Vmlld18udXNlS2V5KytdID0gc2VsZjtcblx0XHRcdHNlbGYuaW5kZXggPSBpbmRleFN0cjtcblx0XHRcdHNlbGYuZ2V0SW5kZXggPSBnZXROZXN0ZWRJbmRleDtcblx0XHR9IGVsc2UgaWYgKHZpZXdzLmxlbmd0aCA9PT0gKHNlbGZfLmtleSA9IHNlbGYuaW5kZXggPSBrZXkpKSB7IC8vIFBhcmVudCBpcyBhbiAnYXJyYXkgdmlldycuIEFkZCB0aGlzIHZpZXcgdG8gaXRzIHZpZXdzIGFycmF5XG5cdFx0XHR2aWV3cy5wdXNoKHNlbGYpOyAvLyBBZGRpbmcgdG8gZW5kIG9mIHZpZXdzIGFycmF5LiAoVXNpbmcgcHVzaCB3aGVuIHBvc3NpYmxlIC0gYmV0dGVyIHBlcmYgdGhhbiBzcGxpY2UpXG5cdFx0fSBlbHNlIHtcblx0XHRcdHZpZXdzLnNwbGljZShrZXksIDAsIHNlbGYpOyAvLyBJbnNlcnRpbmcgaW4gdmlld3MgYXJyYXlcblx0XHR9XG5cdFx0Ly8gSWYgbm8gY29udGV4dCB3YXMgcGFzc2VkIGluLCB1c2UgcGFyZW50IGNvbnRleHRcblx0XHQvLyBJZiBjb250ZXh0IHdhcyBwYXNzZWQgaW4sIGl0IHNob3VsZCBoYXZlIGJlZW4gbWVyZ2VkIGFscmVhZHkgd2l0aCBwYXJlbnQgY29udGV4dFxuXHRcdHNlbGYuY3R4ID0gY29udGV4dCB8fCBwYXJlbnRWaWV3LmN0eDtcblx0fSBlbHNlIGlmICh0eXBlKSB7XG5cdFx0c2VsZi5yb290ID0gc2VsZjsgLy8gdmlldyB3aG9zZSBwYXJlbnQgaXMgdG9wIHZpZXdc
blx0fVxufVxuXG5WaWV3LnByb3RvdHlwZSA9IHtcblx0Z2V0OiBnZXRWaWV3LFxuXHRnZXRJbmRleDogZ2V0SW5kZXgsXG5cdGN0eFBybTogY29udGV4dFBhcmFtZXRlcixcblx0Z2V0UnNjOiBnZXRSZXNvdXJjZSxcblx0X2dldFRtcGw6IGdldFRlbXBsYXRlLFxuXHRfZ2V0T2I6IGdldFBhdGhPYmplY3QsXG5cdGdldENhY2hlOiBmdW5jdGlvbihrZXkpIHsgLy8gR2V0IGNhY2hlZCB2YWx1ZSBvZiBjb21wdXRlZCB2YWx1ZVxuXHRcdGlmICgkc3ViU2V0dGluZ3MuX2NjaEN0ID4gdGhpcy5jYWNoZS5fY3QpIHtcblx0XHRcdHRoaXMuY2FjaGUgPSB7X2N0OiAkc3ViU2V0dGluZ3MuX2NjaEN0fTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXMuY2FjaGVba2V5XSB8fCAodGhpcy5jYWNoZVtrZXldID0gY3BGblN0b3JlW2tleV0odGhpcy5kYXRhLCB0aGlzLCAkc3ViKSk7XG5cdH0sXG5cdF9pczogXCJ2aWV3XCJcbn07XG5cbi8vPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gUmVnaXN0cmF0aW9uXG4vLz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZnVuY3Rpb24gY29tcGlsZUNoaWxkUmVzb3VyY2VzKHBhcmVudFRtcGwpIHtcblx0dmFyIHN0b3JlTmFtZSwgc3RvcmVOYW1lcywgcmVzb3VyY2VzO1xuXHRmb3IgKHN0b3JlTmFtZSBpbiBqc3ZTdG9yZXMpIHtcblx0XHRzdG9yZU5hbWVzID0gc3RvcmVOYW1lICsgXCJzXCI7XG5cdFx0aWYgKHBhcmV
udFRtcGxbc3RvcmVOYW1lc10pIHtcblx0XHRcdHJlc291cmNlcyA9IHBhcmVudFRtcGxbc3RvcmVOYW1lc107ICAgICAgICAvLyBSZXNvdXJjZXMgbm90IHlldCBjb21waWxlZFxuXHRcdFx0cGFyZW50VG1wbFtzdG9yZU5hbWVzXSA9IHt9OyAgICAgICAgICAgICAgIC8vIFJlbW92ZSB1bmNvbXBpbGVkIHJlc291cmNlc1xuXHRcdFx0JHZpZXdzW3N0b3JlTmFtZXNdKHJlc291cmNlcywgcGFyZW50VG1wbCk7IC8vIEFkZCBiYWNrIGluIHRoZSBjb21waWxlZCByZXNvdXJjZXNcblx0XHR9XG5cdH1cbn1cblxuLy89PT09PT09PT09PT09PT1cbi8vIGNvbXBpbGVUYWdcbi8vPT09PT09PT09PT09PT09XG5cbmZ1bmN0aW9uIGNvbXBpbGVUYWcobmFtZSwgdGFnRGVmLCBwYXJlbnRUbXBsKSB7XG5cdHZhciB0bXBsLCBiYXNlVGFnLCBwcm9wLFxuXHRcdGNvbXBpbGVkRGVmID0gbmV3ICRzdWIuX3RnKCk7XG5cblx0ZnVuY3Rpb24gVGFnKCkge1xuXHRcdHZhciB0YWcgPSB0aGlzO1xuXHRcdHRhZy5fID0ge1xuXHRcdFx0dW5saW5rZWQ6IHRydWVcblx0XHR9O1xuXHRcdHRhZy5pbmxpbmUgPSB0cnVlO1xuXHRcdHRhZy50YWdOYW1lID0gbmFtZTtcblx0fVxuXG5cdGlmICgkaXNGdW5jdGlvbih0YWdEZWYpKSB7XG5cdFx0Ly8gU2ltcGxlIHRhZyBkZWNsYXJlZCBhcyBmdW5jdGlvbi4gTm8gcHJlc2VudGVyIGluc3RhbnRhdGlvbi5cblx0XHR0YWdEZWYgPSB7XG5cdFx0XHRkZXBlbmRzOiB0YWdEZWYuZGVwZW5kcyxcblx0XHRcdHJlbmRlcjogdGFnRGVmXG
5cdFx0fTtcblx0fSBlbHNlIGlmIChcIlwiICsgdGFnRGVmID09PSB0YWdEZWYpIHtcblx0XHR0YWdEZWYgPSB7dGVtcGxhdGU6IHRhZ0RlZn07XG5cdH1cblxuXHRpZiAoYmFzZVRhZyA9IHRhZ0RlZi5iYXNlVGFnKSB7XG5cdFx0dGFnRGVmLmZsb3cgPSAhIXRhZ0RlZi5mbG93OyAvLyBTZXQgZmxvdyBwcm9wZXJ0eSwgc28gZGVmYXVsdHMgdG8gZmFsc2UgZXZlbiBpZiBiYXNlVGFnIGhhcyBmbG93PXRydWVcblx0XHRiYXNlVGFnID0gXCJcIiArIGJhc2VUYWcgPT09IGJhc2VUYWdcblx0XHRcdD8gKHBhcmVudFRtcGwgJiYgcGFyZW50VG1wbC50YWdzW2Jhc2VUYWddIHx8ICR0YWdzW2Jhc2VUYWddKVxuXHRcdFx0OiBiYXNlVGFnO1xuXHRcdGlmICghYmFzZVRhZykge1xuXHRcdFx0ZXJyb3IoJ2Jhc2VUYWc6IFwiJyArIHRhZ0RlZi5iYXNlVGFnICsgJ1wiIG5vdCBmb3VuZCcpO1xuXHRcdH1cblx0XHRjb21waWxlZERlZiA9ICRleHRlbmQoY29tcGlsZWREZWYsIGJhc2VUYWcpO1xuXG5cdFx0Zm9yIChwcm9wIGluIHRhZ0RlZikge1xuXHRcdFx0Y29tcGlsZWREZWZbcHJvcF0gPSBnZXRNZXRob2QoYmFzZVRhZ1twcm9wXSwgdGFnRGVmW3Byb3BdKTtcblx0XHR9XG5cdH0gZWxzZSB7XG5cdFx0Y29tcGlsZWREZWYgPSAkZXh0ZW5kKGNvbXBpbGVkRGVmLCB0YWdEZWYpO1xuXHR9XG5cblx0Ly8gVGFnIGRlY2xhcmVkIGFzIG9iamVjdCwgdXNlZCBhcyB0aGUgcHJvdG90eXBlIGZvciB0YWcgaW5zdGFudGlhdGlvbiAoY29udHJvbC9wcmVzZW50ZXIpX
G5cdGlmICgodG1wbCA9IGNvbXBpbGVkRGVmLnRlbXBsYXRlKSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0Y29tcGlsZWREZWYudGVtcGxhdGUgPSBcIlwiICsgdG1wbCA9PT0gdG1wbCA/ICgkdGVtcGxhdGVzW3RtcGxdIHx8ICR0ZW1wbGF0ZXModG1wbCkpIDogdG1wbDtcblx0fVxuXHQoVGFnLnByb3RvdHlwZSA9IGNvbXBpbGVkRGVmKS5jb25zdHJ1Y3RvciA9IGNvbXBpbGVkRGVmLl9jdHIgPSBUYWc7XG5cblx0aWYgKHBhcmVudFRtcGwpIHtcblx0XHRjb21waWxlZERlZi5fcGFyZW50VG1wbCA9IHBhcmVudFRtcGw7XG5cdH1cblx0cmV0dXJuIGNvbXBpbGVkRGVmO1xufVxuXG5mdW5jdGlvbiBiYXNlQXBwbHkoYXJncykge1xuXHQvLyBJbiBkZXJpdmVkIG1ldGhvZCAob3IgaGFuZGxlciBkZWNsYXJlZCBkZWNsYXJhdGl2ZWx5IGFzIGluIHt7OmZvbyBvbkNoYW5nZT1+Zm9vQ2hhbmdlZH19IGNhbiBjYWxsIGJhc2UgbWV0aG9kLFxuXHQvLyB1c2luZyB0aGlzLmJhc2VBcHBseShhcmd1bWVudHMpIChFcXVpdmFsZW50IHRvIHRoaXMuX3N1cGVyQXBwbHkoYXJndW1lbnRzKSBpbiBqUXVlcnkgVUkpXG5cdHJldHVybiB0aGlzLmJhc2UuYXBwbHkodGhpcywgYXJncyk7XG59XG5cbi8vPT09PT09PT09PT09PT09XG4vLyBjb21waWxlVG1wbFxuLy89PT09PT09PT09PT09PT1cblxuZnVuY3Rpb24gY29tcGlsZVRtcGwobmFtZSwgdG1wbCwgcGFyZW50VG1wbCwgb3B0aW9ucykge1xuXHQvLyB0bXBsIGlzIGVpdGhlciBhIHRlbXBsYXRlIG9iamVjdCwgYSBz
ZWxlY3RvciBmb3IgYSB0ZW1wbGF0ZSBzY3JpcHQgYmxvY2ssIG9yIHRoZSBuYW1lIG9mIGEgY29tcGlsZWQgdGVtcGxhdGVcblxuXHQvLz09PT0gbmVzdGVkIGZ1bmN0aW9ucyA9PT09XG5cdGZ1bmN0aW9uIGxvb2t1cFRlbXBsYXRlKHZhbHVlKSB7XG5cdFx0Ly8gSWYgdmFsdWUgaXMgb2YgdHlwZSBzdHJpbmcgLSB0cmVhdCBhcyBzZWxlY3Rvciwgb3IgbmFtZSBvZiBjb21waWxlZCB0ZW1wbGF0ZVxuXHRcdC8vIFJldHVybiB0aGUgdGVtcGxhdGUgb2JqZWN0LCBpZiBhbHJlYWR5IGNvbXBpbGVkLCBvciB0aGUgbWFya3VwIHN0cmluZ1xuXHRcdHZhciBjdXJyZW50TmFtZSwgdG1wbDtcblx0XHRpZiAoKFwiXCIgKyB2YWx1ZSA9PT0gdmFsdWUpIHx8IHZhbHVlLm5vZGVUeXBlID4gMCAmJiAoZWxlbSA9IHZhbHVlKSkge1xuXHRcdFx0aWYgKCFlbGVtKSB7XG5cdFx0XHRcdGlmICgvXlxcLj9cXC9bXlxcXFw6Kj9cIjw+XSokLy50ZXN0KHZhbHVlKSkge1xuXHRcdFx0XHRcdC8vIHZhbHVlPVwiLi9zb21lL2ZpbGUuaHRtbFwiIChvciBcIi9zb21lL2ZpbGUuaHRtbFwiKVxuXHRcdFx0XHRcdC8vIElmIHRoZSB0ZW1wbGF0ZSBpcyBub3QgbmFtZWQsIHVzZSBcIi4vc29tZS9maWxlLmh0bWxcIiBhcyBuYW1lLlxuXHRcdFx0XHRcdGlmICh0bXBsID0gJHRlbXBsYXRlc1tuYW1lID0gbmFtZSB8fCB2YWx1ZV0pIHtcblx0XHRcdFx0XHRcdHZhbHVlID0gdG1wbDtcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0Ly8gQlJPV1NFUi1TUEVDSUZJQyB
DT0RFIChub3Qgb24gTm9kZS5qcyk6XG5cdFx0XHRcdFx0XHQvLyBMb29rIGZvciBzZXJ2ZXItZ2VuZXJhdGVkIHNjcmlwdCBibG9jayB3aXRoIGlkIFwiLi9zb21lL2ZpbGUuaHRtbFwiXG5cdFx0XHRcdFx0XHRlbGVtID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodmFsdWUpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSBlbHNlIGlmICgkLmZuICYmICEkc3ViLnJUbXBsLnRlc3QodmFsdWUpKSB7XG5cdFx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRcdGVsZW0gPSAkKHZhbHVlLCBkb2N1bWVudClbMF07IC8vIGlmIGpRdWVyeSBpcyBsb2FkZWQsIHRlc3QgZm9yIHNlbGVjdG9yIHJldHVybmluZyBlbGVtZW50cywgYW5kIGdldCBmaXJzdCBlbGVtZW50XG5cdFx0XHRcdFx0fSBjYXRjaCAoZSkge31cblx0XHRcdFx0fS8vIEVORCBCUk9XU0VSLVNQRUNJRklDIENPREVcblx0XHRcdH0gLy9CUk9XU0VSLVNQRUNJRklDIENPREVcblx0XHRcdGlmIChlbGVtKSB7XG5cdFx0XHRcdGlmIChlbGVtLnRhZ05hbWUgIT09IFwiU0NSSVBUXCIpIHtcblx0XHRcdFx0XHRlcnJvcih2YWx1ZSArIFwiOiBVc2Ugc2NyaXB0IGJsb2NrLCBub3QgXCIgKyBlbGVtLnRhZ05hbWUpO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmIChvcHRpb25zKSB7XG5cdFx0XHRcdFx0Ly8gV2Ugd2lsbCBjb21waWxlIGEgbmV3IHRlbXBsYXRlIHVzaW5nIHRoZSBtYXJrdXAgaW4gdGhlIHNjcmlwdCBlbGVtZW50XG5cdFx0XHRcdFx0dmFsdWUgPSBlbGVtLmlubmVySFRNTDtcblx0XHRcdFx0fS
BlbHNlIHtcblx0XHRcdFx0XHQvLyBXZSB3aWxsIGNhY2hlIGEgc2luZ2xlIGNvcHkgb2YgdGhlIGNvbXBpbGVkIHRlbXBsYXRlLCBhbmQgYXNzb2NpYXRlIGl0IHdpdGggdGhlIG5hbWVcblx0XHRcdFx0XHQvLyAocmVuYW1pbmcgZnJvbSBhIHByZXZpb3VzIG5hbWUgaWYgdGhlcmUgd2FzIG9uZSkuXG5cdFx0XHRcdFx0Y3VycmVudE5hbWUgPSBlbGVtLmdldEF0dHJpYnV0ZSh0bXBsQXR0cik7XG5cdFx0XHRcdFx0aWYgKGN1cnJlbnROYW1lKSB7XG5cdFx0XHRcdFx0XHRpZiAoY3VycmVudE5hbWUgIT09IGpzdlRtcGwpIHtcblx0XHRcdFx0XHRcdFx0dmFsdWUgPSAkdGVtcGxhdGVzW2N1cnJlbnROYW1lXTtcblx0XHRcdFx0XHRcdFx0ZGVsZXRlICR0ZW1wbGF0ZXNbY3VycmVudE5hbWVdO1xuXHRcdFx0XHRcdFx0fSBlbHNlIGlmICgkLmZuKSB7XG5cdFx0XHRcdFx0XHRcdHZhbHVlID0gJC5kYXRhKGVsZW0pW2pzdlRtcGxdOyAvLyBHZXQgY2FjaGVkIGNvbXBpbGVkIHRlbXBsYXRlXG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGlmICghY3VycmVudE5hbWUgfHwgIXZhbHVlKSB7IC8vIE5vdCB5ZXQgY29tcGlsZWQsIG9yIGNhY2hlZCB2ZXJzaW9uIGxvc3Rcblx0XHRcdFx0XHRcdG5hbWUgPSBuYW1lIHx8ICgkLmZuID8ganN2VG1wbCA6IHZhbHVlKTtcblx0XHRcdFx0XHRcdHZhbHVlID0gY29tcGlsZVRtcGwobmFtZSwgZWxlbS5pbm5lckhUTUwsIHBhcmVudFRtcGwsIG9wdGlvbnMpO1xuXHRcdFx0XHRcdH1cblx0XHRcd
Fx0XHR2YWx1ZS50bXBsTmFtZSA9IG5hbWUgPSBuYW1lIHx8IGN1cnJlbnROYW1lO1xuXHRcdFx0XHRcdGlmIChuYW1lICE9PSBqc3ZUbXBsKSB7XG5cdFx0XHRcdFx0XHQkdGVtcGxhdGVzW25hbWVdID0gdmFsdWU7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGVsZW0uc2V0QXR0cmlidXRlKHRtcGxBdHRyLCBuYW1lKTtcblx0XHRcdFx0XHRpZiAoJC5mbikge1xuXHRcdFx0XHRcdFx0JC5kYXRhKGVsZW0sIGpzdlRtcGwsIHZhbHVlKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH0gLy8gRU5EIEJST1dTRVItU1BFQ0lGSUMgQ09ERVxuXHRcdFx0ZWxlbSA9IHVuZGVmaW5lZDtcblx0XHR9IGVsc2UgaWYgKCF2YWx1ZS5mbikge1xuXHRcdFx0dmFsdWUgPSB1bmRlZmluZWQ7XG5cdFx0XHQvLyBJZiB2YWx1ZSBpcyBub3QgYSBzdHJpbmcuIEhUTUwgZWxlbWVudCwgb3IgY29tcGlsZWQgdGVtcGxhdGUsIHJldHVybiB1bmRlZmluZWRcblx0XHR9XG5cdFx0cmV0dXJuIHZhbHVlO1xuXHR9XG5cblx0dmFyIGVsZW0sIGNvbXBpbGVkVG1wbCxcblx0XHR0bXBsT3JNYXJrdXAgPSB0bXBsID0gdG1wbCB8fCBcIlwiO1xuXHQkc3ViLl9odG1sID0gJGNvbnZlcnRlcnMuaHRtbDtcblxuXHQvLz09PT0gQ29tcGlsZSB0aGUgdGVtcGxhdGUgPT09PVxuXHRpZiAob3B0aW9ucyA9PT0gMCkge1xuXHRcdG9wdGlvbnMgPSB1bmRlZmluZWQ7XG5cdFx0dG1wbE9yTWFya3VwID0gbG9va3VwVGVtcGxhdGUodG1wbE9yTWFya3VwKTsgLy8gVG9wLWxldmVsIGNv
bXBpbGUgc28gZG8gYSB0ZW1wbGF0ZSBsb29rdXBcblx0fVxuXG5cdC8vIElmIG9wdGlvbnMsIHRoZW4gdGhpcyB3YXMgYWxyZWFkeSBjb21waWxlZCBmcm9tIGEgKHNjcmlwdCkgZWxlbWVudCB0ZW1wbGF0ZSBkZWNsYXJhdGlvbi5cblx0Ly8gSWYgbm90LCB0aGVuIGlmIHRtcGwgaXMgYSB0ZW1wbGF0ZSBvYmplY3QsIHVzZSBpdCBmb3Igb3B0aW9uc1xuXHRvcHRpb25zID0gb3B0aW9ucyB8fCAodG1wbC5tYXJrdXBcblx0XHQ/IHRtcGwuYm5kc1xuXHRcdFx0PyAkZXh0ZW5kKHt9LCB0bXBsKVxuXHRcdFx0OiB0bXBsXG5cdFx0OiB7fVxuXHQpO1xuXG5cdG9wdGlvbnMudG1wbE5hbWUgPSBvcHRpb25zLnRtcGxOYW1lIHx8IG5hbWUgfHwgXCJ1bm5hbWVkXCI7XG5cdGlmIChwYXJlbnRUbXBsKSB7XG5cdFx0b3B0aW9ucy5fcGFyZW50VG1wbCA9IHBhcmVudFRtcGw7XG5cdH1cblx0Ly8gSWYgdG1wbCBpcyBub3QgYSBtYXJrdXAgc3RyaW5nIG9yIGEgc2VsZWN0b3Igc3RyaW5nLCB0aGVuIGl0IG11c3QgYmUgYSB0ZW1wbGF0ZSBvYmplY3Rcblx0Ly8gSW4gdGhhdCBjYXNlLCBnZXQgaXQgZnJvbSB0aGUgbWFya3VwIHByb3BlcnR5IG9mIHRoZSBvYmplY3Rcblx0aWYgKCF0bXBsT3JNYXJrdXAgJiYgdG1wbC5tYXJrdXAgJiYgKHRtcGxPck1hcmt1cCA9IGxvb2t1cFRlbXBsYXRlKHRtcGwubWFya3VwKSkgJiYgdG1wbE9yTWFya3VwLmZuKSB7XG5cdFx0Ly8gSWYgdGhlIHN0cmluZyByZWZlcmVuY2VzIGEgY29tcGlsZWQgdGVtcGxhdGUgb2JqZWN0LCB
uZWVkIHRvIHJlY29tcGlsZSB0byBtZXJnZSBhbnkgbW9kaWZpZWQgb3B0aW9uc1xuXHRcdHRtcGxPck1hcmt1cCA9IHRtcGxPck1hcmt1cC5tYXJrdXA7XG5cdH1cblx0aWYgKHRtcGxPck1hcmt1cCAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0aWYgKHRtcGxPck1hcmt1cC5yZW5kZXIgfHwgdG1wbC5yZW5kZXIpIHtcblx0XHRcdC8vIHRtcGwgaXMgYWxyZWFkeSBjb21waWxlZCwgc28gdXNlIGl0XG5cdFx0XHRpZiAodG1wbE9yTWFya3VwLnRtcGxzKSB7XG5cdFx0XHRcdGNvbXBpbGVkVG1wbCA9IHRtcGxPck1hcmt1cDtcblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gdG1wbE9yTWFya3VwIGlzIGEgbWFya3VwIHN0cmluZywgbm90IGEgY29tcGlsZWQgdGVtcGxhdGVcblx0XHRcdC8vIENyZWF0ZSB0ZW1wbGF0ZSBvYmplY3Rcblx0XHRcdHRtcGwgPSB0bXBsT2JqZWN0KHRtcGxPck1hcmt1cCwgb3B0aW9ucyk7XG5cdFx0XHQvLyBDb21waWxlIHRvIEFTVCBhbmQgdGhlbiB0byBjb21waWxlZCBmdW5jdGlvblxuXHRcdFx0dG1wbEZuKHRtcGxPck1hcmt1cC5yZXBsYWNlKHJFc2NhcGVRdW90ZXMsIFwiXFxcXCQmXCIpLCB0bXBsKTtcblx0XHR9XG5cdFx0aWYgKCFjb21waWxlZFRtcGwpIHtcblx0XHRcdGNvbXBpbGVkVG1wbCA9ICRleHRlbmQoZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHJldHVybiBjb21waWxlZFRtcGwucmVuZGVyLmFwcGx5KGNvbXBpbGVkVG1wbCwgYXJndW1lbnRzKTtcblx0XHRcdH0sIHRtcGwpO1xuXG5cdFx0XHRjb21waW
xlQ2hpbGRSZXNvdXJjZXMoY29tcGlsZWRUbXBsKTtcblx0XHR9XG5cdFx0cmV0dXJuIGNvbXBpbGVkVG1wbDtcblx0fVxufVxuXG4vLz09PT0gL2VuZCBvZiBmdW5jdGlvbiBjb21waWxlVG1wbCA9PT09XG5cbi8vPT09PT09PT09PT09PT09PT1cbi8vIGNvbXBpbGVWaWV3TW9kZWxcbi8vPT09PT09PT09PT09PT09PT1cblxuZnVuY3Rpb24gZ2V0RGVmYXVsdFZhbChkZWZhdWx0VmFsLCBkYXRhKSB7XG5cdHJldHVybiAkaXNGdW5jdGlvbihkZWZhdWx0VmFsKVxuXHRcdD8gZGVmYXVsdFZhbC5jYWxsKGRhdGEpXG5cdFx0OiBkZWZhdWx0VmFsO1xufVxuXG5mdW5jdGlvbiBhZGRQYXJlbnRSZWYob2IsIHJlZiwgcGFyZW50KSB7XG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShvYiwgcmVmLCB7XG5cdFx0dmFsdWU6IHBhcmVudCxcblx0XHRjb25maWd1cmFibGU6IHRydWVcblx0fSk7XG59XG5cbmZ1bmN0aW9uIGNvbXBpbGVWaWV3TW9kZWwobmFtZSwgdHlwZSkge1xuXHR2YXIgaSwgY29uc3RydWN0b3IsIHBhcmVudCxcblx0XHR2aWV3TW9kZWxzID0gdGhpcyxcblx0XHRnZXR0ZXJzID0gdHlwZS5nZXR0ZXJzLFxuXHRcdGV4dGVuZCA9IHR5cGUuZXh0ZW5kLFxuXHRcdGlkID0gdHlwZS5pZCxcblx0XHRwcm90byA9ICQuZXh0ZW5kKHtcblx0XHRcdF9pczogbmFtZSB8fCBcInVubmFtZWRcIixcblx0XHRcdHVubWFwOiB1bm1hcCxcblx0XHRcdG1lcmdlOiBtZXJnZVxuXHRcdH0sIGV4dGVuZCksXG5cdFx0YXJncyA9IFwiXCIsXG5cdFx0Y25zdHIgPSBcI
lwiLFxuXHRcdGdldHRlckNvdW50ID0gZ2V0dGVycyA/IGdldHRlcnMubGVuZ3RoIDogMCxcblx0XHQkb2JzZXJ2YWJsZSA9ICQub2JzZXJ2YWJsZSxcblx0XHRnZXR0ZXJOYW1lcyA9IHt9O1xuXG5cdGZ1bmN0aW9uIEpzdlZtKGFyZ3MpIHtcblx0XHRjb25zdHJ1Y3Rvci5hcHBseSh0aGlzLCBhcmdzKTtcblx0fVxuXG5cdGZ1bmN0aW9uIHZtKCkge1xuXHRcdHJldHVybiBuZXcgSnN2Vm0oYXJndW1lbnRzKTtcblx0fVxuXG5cdGZ1bmN0aW9uIGl0ZXJhdGUoZGF0YSwgYWN0aW9uKSB7XG5cdFx0dmFyIGdldHRlclR5cGUsIGRlZmF1bHRWYWwsIHByb3AsIG9iLCBwYXJlbnRSZWYsXG5cdFx0XHRqID0gMDtcblx0XHRmb3IgKDsgaiA8IGdldHRlckNvdW50OyBqKyspIHtcblx0XHRcdHByb3AgPSBnZXR0ZXJzW2pdO1xuXHRcdFx0Z2V0dGVyVHlwZSA9IHVuZGVmaW5lZDtcblx0XHRcdGlmIChwcm9wICsgXCJcIiAhPT0gcHJvcCkge1xuXHRcdFx0XHRnZXR0ZXJUeXBlID0gcHJvcDtcblx0XHRcdFx0cHJvcCA9IGdldHRlclR5cGUuZ2V0dGVyO1xuXHRcdFx0XHRwYXJlbnRSZWYgPSBnZXR0ZXJUeXBlLnBhcmVudFJlZjtcblx0XHRcdH1cblx0XHRcdGlmICgob2IgPSBkYXRhW3Byb3BdKSA9PT0gdW5kZWZpbmVkICYmIGdldHRlclR5cGUgJiYgKGRlZmF1bHRWYWwgPSBnZXR0ZXJUeXBlLmRlZmF1bHRWYWwpICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdFx0b2IgPSBnZXREZWZhdWx0VmFsKGRlZmF1bHRWYWwsIGRhdGEpO1xuXHRcdFx0fVxuXHRcdFx0YWN0
aW9uKG9iLCBnZXR0ZXJUeXBlICYmIHZpZXdNb2RlbHNbZ2V0dGVyVHlwZS50eXBlXSwgcHJvcCwgcGFyZW50UmVmKTtcblx0XHR9XG5cdH1cblxuXHRmdW5jdGlvbiBtYXAoZGF0YSkge1xuXHRcdGRhdGEgPSBkYXRhICsgXCJcIiA9PT0gZGF0YVxuXHRcdFx0PyBKU09OLnBhcnNlKGRhdGEpIC8vIEFjY2VwdCBKU09OIHN0cmluZ1xuXHRcdFx0OiBkYXRhOyAgICAgICAgICAgIC8vIG9yIG9iamVjdC9hcnJheVxuXHRcdHZhciBsLCBwcm9wLCBjaGlsZE9iLCBwYXJlbnRSZWYsXG5cdFx0XHRqID0gMCxcblx0XHRcdG9iID0gZGF0YSxcblx0XHRcdGFyciA9IFtdO1xuXG5cdFx0aWYgKCRpc0FycmF5KGRhdGEpKSB7XG5cdFx0XHRkYXRhID0gZGF0YSB8fCBbXTtcblx0XHRcdGwgPSBkYXRhLmxlbmd0aDtcblx0XHRcdGZvciAoOyBqPGw7IGorKykge1xuXHRcdFx0XHRhcnIucHVzaCh0aGlzLm1hcChkYXRhW2pdKSk7XG5cdFx0XHR9XG5cdFx0XHRhcnIuX2lzID0gbmFtZTtcblx0XHRcdGFyci51bm1hcCA9IHVubWFwO1xuXHRcdFx0YXJyLm1lcmdlID0gbWVyZ2U7XG5cdFx0XHRyZXR1cm4gYXJyO1xuXHRcdH1cblxuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRpdGVyYXRlKGRhdGEsIGZ1bmN0aW9uKG9iLCB2aWV3TW9kZWwpIHtcblx0XHRcdFx0aWYgKHZpZXdNb2RlbCkgeyAvLyBJdGVyYXRlIHRvIGJ1aWxkIGdldHRlcnMgYXJnIGFycmF5ICh2YWx1ZSwgb3IgbWFwcGVkIHZhbHVlKVxuXHRcdFx0XHRcdG9iID0gdmlld01vZGVsLm1hcChvYik7XG5cdFx
0XHRcdH1cblx0XHRcdFx0YXJyLnB1c2gob2IpO1xuXHRcdFx0fSk7XG5cdFx0XHRvYiA9IHRoaXMuYXBwbHkodGhpcywgYXJyKTsgLy8gSW5zdGFudGlhdGUgdGhpcyBWaWV3IE1vZGVsLCBwYXNzaW5nIGdldHRlcnMgYXJncyBhcnJheSB0byBjb25zdHJ1Y3RvclxuXHRcdFx0aiA9IGdldHRlckNvdW50O1xuXHRcdFx0d2hpbGUgKGotLSkge1xuXHRcdFx0XHRjaGlsZE9iID0gYXJyW2pdO1xuXHRcdFx0XHRwYXJlbnRSZWYgPSBnZXR0ZXJzW2pdLnBhcmVudFJlZjtcblx0XHRcdFx0aWYgKHBhcmVudFJlZiAmJiBjaGlsZE9iICYmIGNoaWxkT2IudW5tYXApIHtcblx0XHRcdFx0XHRpZiAoJGlzQXJyYXkoY2hpbGRPYikpIHtcblx0XHRcdFx0XHRcdGwgPSBjaGlsZE9iLmxlbmd0aDtcblx0XHRcdFx0XHRcdHdoaWxlIChsLS0pIHtcblx0XHRcdFx0XHRcdFx0YWRkUGFyZW50UmVmKGNoaWxkT2JbbF0sIHBhcmVudFJlZiwgb2IpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRhZGRQYXJlbnRSZWYoY2hpbGRPYiwgcGFyZW50UmVmLCBvYik7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0XHRmb3IgKHByb3AgaW4gZGF0YSkgeyAvLyBDb3B5IG92ZXIgYW55IG90aGVyIHByb3BlcnRpZXMuIHRoYXQgYXJlIG5vdCBnZXQvc2V0IHByb3BlcnRpZXNcblx0XHRcdFx0aWYgKHByb3AgIT09ICRleHBhbmRvICYmICFnZXR0ZXJOYW1lc1twcm9wXSkge1xuXHRcdFx0XHRcdG9iW3Byb3BdID0gZGF0YV
twcm9wXTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXR1cm4gb2I7XG5cdH1cblxuXHRmdW5jdGlvbiBtZXJnZShkYXRhLCBwYXJlbnQsIHBhcmVudFJlZikge1xuXHRcdGRhdGEgPSBkYXRhICsgXCJcIiA9PT0gZGF0YVxuXHRcdFx0PyBKU09OLnBhcnNlKGRhdGEpIC8vIEFjY2VwdCBKU09OIHN0cmluZ1xuXHRcdFx0OiBkYXRhOyAgICAgICAgICAgIC8vIG9yIG9iamVjdC9hcnJheVxuXG5cdFx0dmFyIGosIGwsIG0sIHByb3AsIG1vZCwgZm91bmQsIGFzc2lnbmVkLCBvYiwgbmV3TW9kQXJyLCBjaGlsZE9iLFxuXHRcdFx0ayA9IDAsXG5cdFx0XHRtb2RlbCA9IHRoaXM7XG5cblx0XHRpZiAoJGlzQXJyYXkobW9kZWwpKSB7XG5cdFx0XHRhc3NpZ25lZCA9IHt9O1xuXHRcdFx0bmV3TW9kQXJyID0gW107XG5cdFx0XHRsID0gZGF0YS5sZW5ndGg7XG5cdFx0XHRtID0gbW9kZWwubGVuZ3RoO1xuXHRcdFx0Zm9yICg7IGs8bDsgaysrKSB7XG5cdFx0XHRcdG9iID0gZGF0YVtrXTtcblx0XHRcdFx0Zm91bmQgPSBmYWxzZTtcblx0XHRcdFx0Zm9yIChqPTA7IGo8bSAmJiAhZm91bmQ7IGorKykge1xuXHRcdFx0XHRcdGlmIChhc3NpZ25lZFtqXSkge1xuXHRcdFx0XHRcdFx0Y29udGludWU7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdG1vZCA9IG1vZGVsW2pdO1xuXG5cdFx0XHRcdFx0aWYgKGlkKSB7XG5cdFx0XHRcdFx0XHRhc3NpZ25lZFtqXSA9IGZvdW5kID0gaWQgKyBcIlwiID09PSBpZFxuXHRcdFx0XHRcdFx0PyAob2JbaWRdICYmI
ChnZXR0ZXJOYW1lc1tpZF0gPyBtb2RbaWRdKCkgOiBtb2RbaWRdKSA9PT0gb2JbaWRdKVxuXHRcdFx0XHRcdFx0OiBpZChtb2QsIG9iKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKGZvdW5kKSB7XG5cdFx0XHRcdFx0bW9kLm1lcmdlKG9iKTtcblx0XHRcdFx0XHRuZXdNb2RBcnIucHVzaChtb2QpO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdG5ld01vZEFyci5wdXNoKGNoaWxkT2IgPSB2bS5tYXAob2IpKTtcblx0XHRcdFx0XHRpZiAocGFyZW50UmVmKSB7XG5cdFx0XHRcdFx0XHRhZGRQYXJlbnRSZWYoY2hpbGRPYiwgcGFyZW50UmVmLCBwYXJlbnQpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdFx0aWYgKCRvYnNlcnZhYmxlKSB7XG5cdFx0XHRcdCRvYnNlcnZhYmxlKG1vZGVsKS5yZWZyZXNoKG5ld01vZEFyciwgdHJ1ZSk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRtb2RlbC5zcGxpY2UuYXBwbHkobW9kZWwsIFswLCBtb2RlbC5sZW5ndGhdLmNvbmNhdChuZXdNb2RBcnIpKTtcblx0XHRcdH1cblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0aXRlcmF0ZShkYXRhLCBmdW5jdGlvbihvYiwgdmlld01vZGVsLCBnZXR0ZXIsIHBhcmVudFJlZikge1xuXHRcdFx0aWYgKHZpZXdNb2RlbCkge1xuXHRcdFx0XHRtb2RlbFtnZXR0ZXJdKCkubWVyZ2Uob2IsIG1vZGVsLCBwYXJlbnRSZWYpOyAvLyBVcGRhdGUgdHlwZWQgcHJvcGVydHlcblx0XHRcdH0gZWxzZSBpZiAobW9kZWxbZ2V0dGVyXSgp
ICE9PSBvYikge1xuXHRcdFx0XHRtb2RlbFtnZXR0ZXJdKG9iKTsgLy8gVXBkYXRlIG5vbi10eXBlZCBwcm9wZXJ0eVxuXHRcdFx0fVxuXHRcdH0pO1xuXHRcdGZvciAocHJvcCBpbiBkYXRhKSB7XG5cdFx0XHRpZiAocHJvcCAhPT0gJGV4cGFuZG8gJiYgIWdldHRlck5hbWVzW3Byb3BdKSB7XG5cdFx0XHRcdG1vZGVsW3Byb3BdID0gZGF0YVtwcm9wXTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHRmdW5jdGlvbiB1bm1hcCgpIHtcblx0XHR2YXIgb2IsIHByb3AsIGdldHRlclR5cGUsIGFyciwgdmFsdWUsXG5cdFx0XHRrID0gMCxcblx0XHRcdG1vZGVsID0gdGhpcztcblxuXHRcdGZ1bmN0aW9uIHVubWFwQXJyYXkobW9kZWxBcnIpIHtcblx0XHRcdHZhciBhcnIgPSBbXSxcblx0XHRcdFx0aSA9IDAsXG5cdFx0XHRcdGwgPSBtb2RlbEFyci5sZW5ndGg7XG5cdFx0XHRmb3IgKDsgaTxsOyBpKyspIHtcblx0XHRcdFx0YXJyLnB1c2gobW9kZWxBcnJbaV0udW5tYXAoKSk7XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gYXJyO1xuXHRcdH1cblxuXHRcdGlmICgkaXNBcnJheShtb2RlbCkpIHtcblx0XHRcdHJldHVybiB1bm1hcEFycmF5KG1vZGVsKTtcblx0XHR9XG5cdFx0b2IgPSB7fTtcblx0XHRmb3IgKDsgayA8IGdldHRlckNvdW50OyBrKyspIHtcblx0XHRcdHByb3AgPSBnZXR0ZXJzW2tdO1xuXHRcdFx0Z2V0dGVyVHlwZSA9IHVuZGVmaW5lZDtcblx0XHRcdGlmIChwcm9wICsgXCJcIiAhPT0gcHJvcCkge1xuXHRcdFx0XHRnZXR0ZXJUeXBlID0gcHJ
vcDtcblx0XHRcdFx0cHJvcCA9IGdldHRlclR5cGUuZ2V0dGVyO1xuXHRcdFx0fVxuXHRcdFx0dmFsdWUgPSBtb2RlbFtwcm9wXSgpO1xuXHRcdFx0b2JbcHJvcF0gPSBnZXR0ZXJUeXBlICYmIHZhbHVlICYmIHZpZXdNb2RlbHNbZ2V0dGVyVHlwZS50eXBlXVxuXHRcdFx0XHQ/ICRpc0FycmF5KHZhbHVlKVxuXHRcdFx0XHRcdD8gdW5tYXBBcnJheSh2YWx1ZSlcblx0XHRcdFx0XHQ6IHZhbHVlLnVubWFwKClcblx0XHRcdFx0OiB2YWx1ZTtcblx0XHR9XG5cdFx0Zm9yIChwcm9wIGluIG1vZGVsKSB7XG5cdFx0XHRpZiAobW9kZWwuaGFzT3duUHJvcGVydHkocHJvcCkgJiYgKHByb3AuY2hhckF0KDApICE9PSBcIl9cIiB8fCAhZ2V0dGVyTmFtZXNbcHJvcC5zbGljZSgxKV0pICYmIHByb3AgIT09ICRleHBhbmRvICYmICEkaXNGdW5jdGlvbihtb2RlbFtwcm9wXSkpIHtcblx0XHRcdFx0b2JbcHJvcF0gPSBtb2RlbFtwcm9wXTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIG9iO1xuXHR9XG5cblx0SnN2Vm0ucHJvdG90eXBlID0gcHJvdG87XG5cblx0Zm9yIChpPTA7IGkgPCBnZXR0ZXJDb3VudDsgaSsrKSB7XG5cdFx0KGZ1bmN0aW9uKGdldHRlcikge1xuXHRcdFx0Z2V0dGVyID0gZ2V0dGVyLmdldHRlciB8fCBnZXR0ZXI7XG5cdFx0XHRnZXR0ZXJOYW1lc1tnZXR0ZXJdID0gaSsxO1xuXHRcdFx0dmFyIHByaXZGaWVsZCA9IFwiX1wiICsgZ2V0dGVyO1xuXG5cdFx0XHRhcmdzICs9IChhcmdzID8gXCIsXCIgOiBcIlwiKSArIGdldHRlcjtcblx0XH
RcdGNuc3RyICs9IFwidGhpcy5cIiArIHByaXZGaWVsZCArIFwiID0gXCIgKyBnZXR0ZXIgKyBcIjtcXG5cIjtcblx0XHRcdHByb3RvW2dldHRlcl0gPSBwcm90b1tnZXR0ZXJdIHx8IGZ1bmN0aW9uKHZhbCkge1xuXHRcdFx0XHRpZiAoIWFyZ3VtZW50cy5sZW5ndGgpIHtcblx0XHRcdFx0XHRyZXR1cm4gdGhpc1twcml2RmllbGRdOyAvLyBJZiB0aGVyZSBpcyBubyBhcmd1bWVudCwgdXNlIGFzIGEgZ2V0dGVyXG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKCRvYnNlcnZhYmxlKSB7XG5cdFx0XHRcdFx0JG9ic2VydmFibGUodGhpcykuc2V0UHJvcGVydHkoZ2V0dGVyLCB2YWwpO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHRoaXNbcHJpdkZpZWxkXSA9IHZhbDtcblx0XHRcdFx0fVxuXHRcdFx0fTtcblxuXHRcdFx0aWYgKCRvYnNlcnZhYmxlKSB7XG5cdFx0XHRcdHByb3RvW2dldHRlcl0uc2V0ID0gcHJvdG9bZ2V0dGVyXS5zZXQgfHwgZnVuY3Rpb24odmFsKSB7XG5cdFx0XHRcdFx0dGhpc1twcml2RmllbGRdID0gdmFsOyAvLyBTZXR0ZXIgY2FsbGVkIGJ5IG9ic2VydmFibGUgcHJvcGVydHkgY2hhbmdlXG5cdFx0XHRcdH07XG5cdFx0XHR9XG5cdFx0fSkoZ2V0dGVyc1tpXSk7XG5cdH1cblxuXHQvLyBDb25zdHJ1Y3RvciBmb3IgbmV3IHZpZXdNb2RlbCBpbnN0YW5jZS5cblx0Y25zdHIgPSBuZXcgRnVuY3Rpb24oYXJncywgY25zdHIpO1xuXG5cdGNvbnN0cnVjdG9yID0gZnVuY3Rpb24oKSB7XG5cdFx0Y25zdHIuYXBwbHkodGhpcywgYXJnd
W1lbnRzKTtcblx0XHQvLyBQYXNzIGFkZGl0aW9uYWwgcGFyZW50UmVmIHN0ciBhbmQgcGFyZW50IG9iaiB0byBoYXZlIGEgcGFyZW50UmVmIHBvaW50ZXIgb24gaW5zdGFuY2Vcblx0XHRpZiAocGFyZW50ID0gYXJndW1lbnRzW2dldHRlckNvdW50ICsgMV0pIHtcblx0XHRcdGFkZFBhcmVudFJlZih0aGlzLCBhcmd1bWVudHNbZ2V0dGVyQ291bnRdLCBwYXJlbnQpO1xuXHRcdH1cblx0fTtcblxuXHRjb25zdHJ1Y3Rvci5wcm90b3R5cGUgPSBwcm90bztcblx0cHJvdG8uY29uc3RydWN0b3IgPSBjb25zdHJ1Y3RvcjtcblxuXHR2bS5tYXAgPSBtYXA7XG5cdHZtLmdldHRlcnMgPSBnZXR0ZXJzO1xuXHR2bS5leHRlbmQgPSBleHRlbmQ7XG5cdHZtLmlkID0gaWQ7XG5cdHJldHVybiB2bTtcbn1cblxuZnVuY3Rpb24gdG1wbE9iamVjdChtYXJrdXAsIG9wdGlvbnMpIHtcblx0Ly8gVGVtcGxhdGUgb2JqZWN0IGNvbnN0cnVjdG9yXG5cdHZhciBodG1sVGFnLFxuXHRcdHdyYXBNYXAgPSAkc3ViU2V0dGluZ3NBZHZhbmNlZC5fd20gfHwge30sIC8vIE9ubHkgdXNlZCBpbiBKc1ZpZXdzLiBPdGhlcndpc2UgZW1wdHk6IHt9XG5cdFx0dG1wbCA9IHtcblx0XHRcdHRtcGxzOiBbXSxcblx0XHRcdGxpbmtzOiB7fSwgLy8gQ29tcGlsZWQgZnVuY3Rpb25zIGZvciBsaW5rIGV4cHJlc3Npb25zXG5cdFx0XHRibmRzOiBbXSxcblx0XHRcdF9pczogXCJ0ZW1wbGF0ZVwiLFxuXHRcdFx0cmVuZGVyOiByZW5kZXJDb250ZW50XG5cdFx0fTtcblxuXHRpZiAob3B0aW9ucykg
e1xuXHRcdHRtcGwgPSAkZXh0ZW5kKHRtcGwsIG9wdGlvbnMpO1xuXHR9XG5cblx0dG1wbC5tYXJrdXAgPSBtYXJrdXA7XG5cdGlmICghdG1wbC5odG1sVGFnKSB7XG5cdFx0Ly8gU2V0IHRtcGwudGFnIHRvIHRoZSB0b3AtbGV2ZWwgSFRNTCB0YWcgdXNlZCBpbiB0aGUgdGVtcGxhdGUsIGlmIGFueS4uLlxuXHRcdGh0bWxUYWcgPSByRmlyc3RFbGVtLmV4ZWMobWFya3VwKTtcblx0XHR0bXBsLmh0bWxUYWcgPSBodG1sVGFnID8gaHRtbFRhZ1sxXS50b0xvd2VyQ2FzZSgpIDogXCJcIjtcblx0fVxuXHRodG1sVGFnID0gd3JhcE1hcFt0bXBsLmh0bWxUYWddO1xuXHRpZiAoaHRtbFRhZyAmJiBodG1sVGFnICE9PSB3cmFwTWFwLmRpdikge1xuXHRcdC8vIFdoZW4gdXNpbmcgSnNWaWV3cywgd2UgdHJpbSB0ZW1wbGF0ZXMgd2hpY2ggYXJlIGluc2VydGVkIGludG8gSFRNTCBjb250ZXh0cyB3aGVyZSB0ZXh0IG5vZGVzIGFyZSBub3QgcmVuZGVyZWQgKGkuZS4gbm90ICdQaHJhc2luZyBDb250ZW50JykuXG5cdFx0Ly8gQ3VycmVudGx5IG5vdCB0cmltbWVkIGZvciA8bGk+IHRhZy4gKE5vdCB3b3J0aCBhZGRpbmcgcGVyZiBjb3N0KVxuXHRcdHRtcGwubWFya3VwID0gJC50cmltKHRtcGwubWFya3VwKTtcblx0fVxuXG5cdHJldHVybiB0bXBsO1xufVxuXG4vLz09PT09PT09PT09PT09XG4vLyByZWdpc3RlclN0b3JlXG4vLz09PT09PT09PT09PT09XG5cbi8qKlxuKiBJbnRlcm5hbC4gUmVnaXN0ZXIgYSBzdG9yZSB0eXBlICh1c2VkIGZvciB0ZW1wbGF0ZSw
gdGFncywgaGVscGVycywgY29udmVydGVycylcbiovXG5mdW5jdGlvbiByZWdpc3RlclN0b3JlKHN0b3JlTmFtZSwgc3RvcmVTZXR0aW5ncykge1xuXG4vKipcbiogR2VuZXJpYyBzdG9yZSgpIGZ1bmN0aW9uIHRvIHJlZ2lzdGVyIGl0ZW0sIG5hbWVkIGl0ZW0sIG9yIGhhc2ggb2YgaXRlbXNcbiogQWxzbyB1c2VkIGFzIGhhc2ggdG8gc3RvcmUgdGhlIHJlZ2lzdGVyZWQgaXRlbXNcbiogVXNlZCBhcyBpbXBsZW1lbnRhdGlvbiBvZiAkLnRlbXBsYXRlcygpLCAkLnZpZXdzLnRlbXBsYXRlcygpLCAkLnZpZXdzLnRhZ3MoKSwgJC52aWV3cy5oZWxwZXJzKCkgYW5kICQudmlld3MuY29udmVydGVycygpXG4qXG4qIEBwYXJhbSB7c3RyaW5nfGhhc2h9IG5hbWUgICAgICAgICBuYW1lIC0gb3Igc2VsZWN0b3IsIGluIGNhc2Ugb2YgJC50ZW1wbGF0ZXMoKS4gT3IgaGFzaCBvZiBpdGVtc1xuKiBAcGFyYW0ge2FueX0gICAgICAgICBbaXRlbV0gICAgICAgKGUuZy4gbWFya3VwIGZvciBuYW1lZCB0ZW1wbGF0ZSlcbiogQHBhcmFtIHt0ZW1wbGF0ZX0gICAgW3BhcmVudFRtcGxdIEZvciBpdGVtIGJlaW5nIHJlZ2lzdGVyZWQgYXMgcHJpdmF0ZSByZXNvdXJjZSBvZiB0ZW1wbGF0ZVxuKiBAcmV0dXJucyB7YW55fCQudmlld3N9IGl0ZW0sIGUuZy4gY29tcGlsZWQgdGVtcGxhdGUgLSBvciAkLnZpZXdzIGluIGNhc2Ugb2YgcmVnaXN0ZXJpbmcgaGFzaCBvZiBpdGVtc1xuKi9cblx0ZnVuY3Rpb24gdGhlU3RvcmUobmFtZSwgaXRlbSwgcGFyZW50VG1wbCkge1xuXH
RcdC8vIFRoZSBzdG9yZSBpcyBhbHNvIHRoZSBmdW5jdGlvbiB1c2VkIHRvIGFkZCBpdGVtcyB0byB0aGUgc3RvcmUuIGUuZy4gJC50ZW1wbGF0ZXMsIG9yICQudmlld3MudGFnc1xuXG5cdFx0Ly8gRm9yIHN0b3JlIG9mIG5hbWUgJ3RoaW5nJywgQ2FsbCBhczpcblx0XHQvLyAgICAkLnZpZXdzLnRoaW5ncyhpdGVtc1ssIHBhcmVudFRtcGxdKSxcblx0XHQvLyBvciAkLnZpZXdzLnRoaW5ncyhuYW1lWywgaXRlbSwgcGFyZW50VG1wbF0pXG5cblx0XHR2YXIgY29tcGlsZSwgaXRlbU5hbWUsIHRoaXNTdG9yZSwgY250LFxuXHRcdFx0b25TdG9yZSA9ICRzdWIub25TdG9yZVtzdG9yZU5hbWVdO1xuXG5cdFx0aWYgKG5hbWUgJiYgdHlwZW9mIG5hbWUgPT09IE9CSkVDVCAmJiAhbmFtZS5ub2RlVHlwZSAmJiAhbmFtZS5tYXJrdXAgJiYgIW5hbWUuZ2V0VGd0ICYmICEoc3RvcmVOYW1lID09PSBcInZpZXdNb2RlbFwiICYmIG5hbWUuZ2V0dGVycyB8fCBuYW1lLmV4dGVuZCkpIHtcblx0XHRcdC8vIENhbGwgdG8gJC52aWV3cy50aGluZ3MoaXRlbXNbLCBwYXJlbnRUbXBsXSksXG5cblx0XHRcdC8vIEFkZGluZyBpdGVtcyB0byB0aGUgc3RvcmVcblx0XHRcdC8vIElmIG5hbWUgaXMgYSBoYXNoLCB0aGVuIGl0ZW0gaXMgcGFyZW50VG1wbC4gSXRlcmF0ZSBvdmVyIGhhc2ggYW5kIGNhbGwgc3RvcmUgZm9yIGtleS5cblx0XHRcdGZvciAoaXRlbU5hbWUgaW4gbmFtZSkge1xuXHRcdFx0XHR0aGVTdG9yZShpdGVtTmFtZSwgbmFtZVtpdGVtTmFtZV0sIGl0ZW0pO
1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIGl0ZW0gfHwgJHZpZXdzO1xuXHRcdH1cblx0XHQvLyBBZGRpbmcgYSBzaW5nbGUgdW5uYW1lZCBpdGVtIHRvIHRoZSBzdG9yZVxuXHRcdGlmIChuYW1lICYmIFwiXCIgKyBuYW1lICE9PSBuYW1lKSB7IC8vIG5hbWUgbXVzdCBiZSBhIHN0cmluZ1xuXHRcdFx0cGFyZW50VG1wbCA9IGl0ZW07XG5cdFx0XHRpdGVtID0gbmFtZTtcblx0XHRcdG5hbWUgPSB1bmRlZmluZWQ7XG5cdFx0fVxuXHRcdHRoaXNTdG9yZSA9IHBhcmVudFRtcGxcblx0XHRcdD8gc3RvcmVOYW1lID09PSBcInZpZXdNb2RlbFwiXG5cdFx0XHRcdD8gcGFyZW50VG1wbFxuXHRcdFx0XHQ6IChwYXJlbnRUbXBsW3N0b3JlTmFtZXNdID0gcGFyZW50VG1wbFtzdG9yZU5hbWVzXSB8fCB7fSlcblx0XHRcdDogdGhlU3RvcmU7XG5cdFx0Y29tcGlsZSA9IHN0b3JlU2V0dGluZ3MuY29tcGlsZTtcblxuXHRcdGlmIChpdGVtID09PSB1bmRlZmluZWQpIHtcblx0XHRcdGl0ZW0gPSBjb21waWxlID8gbmFtZSA6IHRoaXNTdG9yZVtuYW1lXTtcblx0XHRcdG5hbWUgPSB1bmRlZmluZWQ7XG5cdFx0fVxuXHRcdGlmIChpdGVtID09PSBudWxsKSB7XG5cdFx0XHQvLyBJZiBpdGVtIGlzIG51bGwsIGRlbGV0ZSB0aGlzIGVudHJ5XG5cdFx0XHRpZiAobmFtZSkge1xuXHRcdFx0XHRkZWxldGUgdGhpc1N0b3JlW25hbWVdO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRpZiAoY29tcGlsZSkge1xuXHRcdFx0XHRpdGVtID0gY29tcGlsZS5jYWxsKHRoaXNT
dG9yZSwgbmFtZSwgaXRlbSwgcGFyZW50VG1wbCwgMCkgfHwge307XG5cdFx0XHRcdGl0ZW0uX2lzID0gc3RvcmVOYW1lOyAvLyBPbmx5IGRvIHRoaXMgZm9yIGNvbXBpbGVkIG9iamVjdHMgKHRhZ3MsIHRlbXBsYXRlcy4uLilcblx0XHRcdH1cblx0XHRcdGlmIChuYW1lKSB7XG5cdFx0XHRcdHRoaXNTdG9yZVtuYW1lXSA9IGl0ZW07XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGlmIChvblN0b3JlKSB7XG5cdFx0XHQvLyBlLmcuIEpzVmlld3MgaW50ZWdyYXRpb25cblx0XHRcdG9uU3RvcmUobmFtZSwgaXRlbSwgcGFyZW50VG1wbCwgY29tcGlsZSk7XG5cdFx0fVxuXHRcdHJldHVybiBpdGVtO1xuXHR9XG5cblx0dmFyIHN0b3JlTmFtZXMgPSBzdG9yZU5hbWUgKyBcInNcIjtcblx0JHZpZXdzW3N0b3JlTmFtZXNdID0gdGhlU3RvcmU7XG59XG5cbi8qKlxuKiBBZGQgc2V0dGluZ3Mgc3VjaCBhczpcbiogJC52aWV3cy5zZXR0aW5ncy5hbGxvd0NvZGUodHJ1ZSlcbiogQHBhcmFtIHtib29sZWFufSB2YWx1ZVxuKiBAcmV0dXJucyB7U2V0dGluZ3N9XG4qXG4qIGFsbG93Q29kZSA9ICQudmlld3Muc2V0dGluZ3MuYWxsb3dDb2RlKClcbiogQHJldHVybnMge2Jvb2xlYW59XG4qL1xuZnVuY3Rpb24gYWRkU2V0dGluZyhzdCkge1xuXHQkdmlld3NTZXR0aW5nc1tzdF0gPSAkdmlld3NTZXR0aW5nc1tzdF0gfHwgZnVuY3Rpb24odmFsdWUpIHtcblx0XHRyZXR1cm4gYXJndW1lbnRzLmxlbmd0aFxuXHRcdFx0PyAoJHN1YlNldHRpbmdzW3N0XSA9IHZhbHVlLCAkdml
ld3NTZXR0aW5ncylcblx0XHRcdDogJHN1YlNldHRpbmdzW3N0XTtcblx0fTtcbn1cblxuLy89PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIGRhdGFNYXAgZm9yIHJlbmRlciBvbmx5XG4vLz09PT09PT09PT09PT09PT09PT09PT09PVxuXG5mdW5jdGlvbiBkYXRhTWFwKG1hcERlZikge1xuXHRmdW5jdGlvbiBNYXAoc291cmNlLCBvcHRpb25zKSB7XG5cdFx0dGhpcy50Z3QgPSBtYXBEZWYuZ2V0VGd0KHNvdXJjZSwgb3B0aW9ucyk7XG5cdFx0b3B0aW9ucy5tYXAgPSB0aGlzO1xuXHR9XG5cblx0aWYgKCRpc0Z1bmN0aW9uKG1hcERlZikpIHtcblx0XHQvLyBTaW1wbGUgbWFwIGRlY2xhcmVkIGFzIGZ1bmN0aW9uXG5cdFx0bWFwRGVmID0ge1xuXHRcdFx0Z2V0VGd0OiBtYXBEZWZcblx0XHR9O1xuXHR9XG5cblx0aWYgKG1hcERlZi5iYXNlTWFwKSB7XG5cdFx0bWFwRGVmID0gJGV4dGVuZCgkZXh0ZW5kKHt9LCBtYXBEZWYuYmFzZU1hcCksIG1hcERlZik7XG5cdH1cblxuXHRtYXBEZWYubWFwID0gZnVuY3Rpb24oc291cmNlLCBvcHRpb25zKSB7XG5cdFx0cmV0dXJuIG5ldyBNYXAoc291cmNlLCBvcHRpb25zKTtcblx0fTtcblx0cmV0dXJuIG1hcERlZjtcbn1cblxuLy89PT09PT09PT09PT09PVxuLy8gcmVuZGVyQ29udGVudFxuLy89PT09PT09PT09PT09PVxuXG4vKiogUmVuZGVyIHRoZSB0ZW1wbGF0ZSBhcyBhIHN0cmluZywgdXNpbmcgdGhlIHNwZWNpZmllZCBkYXRhIGFuZCBoZWxwZXJzL2NvbnRleHRcbiogJChcIiN0bXBsXCIpLnJlbmRlci
gpLCB0bXBsLnJlbmRlcigpLCB0YWdDdHgucmVuZGVyKCksICQucmVuZGVyLm5hbWVkVG1wbCgpXG4qXG4qIEBwYXJhbSB7YW55fSAgICAgICAgZGF0YVxuKiBAcGFyYW0ge2hhc2h9ICAgICAgIFtjb250ZXh0XSAgICAgICAgICAgaGVscGVycyBvciBjb250ZXh0XG4qIEBwYXJhbSB7Ym9vbGVhbn0gICAgW25vSXRlcmF0aW9uXVxuKiBAcGFyYW0ge1ZpZXd9ICAgICAgIFtwYXJlbnRWaWV3XSAgICAgICAgaW50ZXJuYWxcbiogQHBhcmFtIHtzdHJpbmd9ICAgICBba2V5XSAgICAgICAgICAgICAgIGludGVybmFsXG4qIEBwYXJhbSB7ZnVuY3Rpb259ICAgW29uUmVuZGVyXSAgICAgICAgICBpbnRlcm5hbFxuKiBAcmV0dXJucyB7c3RyaW5nfSAgIHJlbmRlcmVkIHRlbXBsYXRlICAgaW50ZXJuYWxcbiovXG5mdW5jdGlvbiByZW5kZXJDb250ZW50KGRhdGEsIGNvbnRleHQsIG5vSXRlcmF0aW9uLCBwYXJlbnRWaWV3LCBrZXksIG9uUmVuZGVyKSB7XG5cdHZhciBpLCBsLCB0YWcsIHRtcGwsIHRhZ0N0eCwgaXNUb3BSZW5kZXJDYWxsLCBwcmV2RGF0YSwgcHJldkluZGV4LFxuXHRcdHZpZXcgPSBwYXJlbnRWaWV3LFxuXHRcdHJlc3VsdCA9IFwiXCI7XG5cblx0aWYgKGNvbnRleHQgPT09IHRydWUpIHtcblx0XHRub0l0ZXJhdGlvbiA9IGNvbnRleHQ7IC8vIHBhc3NpbmcgYm9vbGVhbiBhcyBzZWNvbmQgcGFyYW0gLSBub0l0ZXJhdGlvblxuXHRcdGNvbnRleHQgPSB1bmRlZmluZWQ7XG5cdH0gZWxzZSBpZiAodHlwZW9mIGNvbnRleHQgIT09IE9CSkVDVCkge
1xuXHRcdGNvbnRleHQgPSB1bmRlZmluZWQ7IC8vIGNvbnRleHQgbXVzdCBiZSBhIGJvb2xlYW4gKG5vSXRlcmF0aW9uKSBvciBhIHBsYWluIG9iamVjdFxuXHR9XG5cblx0aWYgKHRhZyA9IHRoaXMudGFnKSB7XG5cdFx0Ly8gVGhpcyBpcyBhIGNhbGwgZnJvbSByZW5kZXJUYWcgb3IgdGFnQ3R4LnJlbmRlciguLi4pXG5cdFx0dGFnQ3R4ID0gdGhpcztcblx0XHR2aWV3ID0gdmlldyB8fCB0YWdDdHgudmlldztcblx0XHR0bXBsID0gdmlldy5fZ2V0VG1wbCh0YWcudGVtcGxhdGUgfHwgdGFnQ3R4LnRtcGwpO1xuXHRcdGlmICghYXJndW1lbnRzLmxlbmd0aCkge1xuXHRcdFx0ZGF0YSA9IHRhZy5jb250ZW50Q3R4ICYmICRpc0Z1bmN0aW9uKHRhZy5jb250ZW50Q3R4KVxuXHRcdFx0XHQ/IGRhdGEgPSB0YWcuY29udGVudEN0eChkYXRhKVxuXHRcdFx0XHQ6IHZpZXc7IC8vIERlZmF1bHQgZGF0YSBjb250ZXh0IGZvciB3cmFwcGVkIGJsb2NrIGNvbnRlbnQgaXMgdGhlIGZpcnN0IGFyZ3VtZW50XG5cdFx0fVxuXHR9IGVsc2Uge1xuXHRcdC8vIFRoaXMgaXMgYSB0ZW1wbGF0ZS5yZW5kZXIoLi4uKSBjYWxsXG5cdFx0dG1wbCA9IHRoaXM7XG5cdH1cblxuXHRpZiAodG1wbCkge1xuXHRcdGlmICghcGFyZW50VmlldyAmJiBkYXRhICYmIGRhdGEuX2lzID09PSBcInZpZXdcIikge1xuXHRcdFx0dmlldyA9IGRhdGE7IC8vIFdoZW4gcGFzc2luZyBpbiBhIHZpZXcgdG8gcmVuZGVyIG9yIGxpbmsgKGFuZCBub3QgcGFzc2luZyBpbiBhIHBhcmVudCB2aWV3KSB1
c2UgdGhlIHBhc3NlZC1pbiB2aWV3IGFzIHBhcmVudFZpZXdcblx0XHR9XG5cblx0XHRpZiAodmlldyAmJiBkYXRhID09PSB2aWV3KSB7XG5cdFx0XHQvLyBJbmhlcml0IHRoZSBkYXRhIGZyb20gdGhlIHBhcmVudCB2aWV3LlxuXHRcdFx0ZGF0YSA9IHZpZXcuZGF0YTtcblx0XHR9XG5cblx0XHRpc1RvcFJlbmRlckNhbGwgPSAhdmlldztcblx0XHRpc1JlbmRlckNhbGwgPSBpc1JlbmRlckNhbGwgfHwgaXNUb3BSZW5kZXJDYWxsO1xuXHRcdGlmIChpc1RvcFJlbmRlckNhbGwpIHtcblx0XHRcdChjb250ZXh0ID0gY29udGV4dCB8fCB7fSkucm9vdCA9IGRhdGE7IC8vIFByb3ZpZGUgfnJvb3QgYXMgc2hvcnRjdXQgdG8gdG9wLWxldmVsIGRhdGEuXG5cdFx0fVxuXHRcdGlmICghaXNSZW5kZXJDYWxsIHx8ICRzdWJTZXR0aW5nc0FkdmFuY2VkLnVzZVZpZXdzIHx8IHRtcGwudXNlVmlld3MgfHwgdmlldyAmJiB2aWV3ICE9PSB0b3BWaWV3KSB7XG5cdFx0XHRyZXN1bHQgPSByZW5kZXJXaXRoVmlld3ModG1wbCwgZGF0YSwgY29udGV4dCwgbm9JdGVyYXRpb24sIHZpZXcsIGtleSwgb25SZW5kZXIsIHRhZyk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGlmICh2aWV3KSB7IC8vIEluIGEgYmxvY2tcblx0XHRcdFx0cHJldkRhdGEgPSB2aWV3LmRhdGE7XG5cdFx0XHRcdHByZXZJbmRleCA9IHZpZXcuaW5kZXg7XG5cdFx0XHRcdHZpZXcuaW5kZXggPSBpbmRleFN0cjtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHZpZXcgPSB0b3BWaWV3O1xuXHRcdFx0XHRwcmV
2RGF0YSA9IHZpZXcuZGF0YTtcblx0XHRcdFx0dmlldy5kYXRhID0gZGF0YTtcblx0XHRcdFx0dmlldy5jdHggPSBjb250ZXh0O1xuXHRcdFx0fVxuXHRcdFx0aWYgKCRpc0FycmF5KGRhdGEpICYmICFub0l0ZXJhdGlvbikge1xuXHRcdFx0XHQvLyBDcmVhdGUgYSB2aWV3IGZvciB0aGUgYXJyYXksIHdob3NlIGNoaWxkIHZpZXdzIGNvcnJlc3BvbmQgdG8gZWFjaCBkYXRhIGl0ZW0uIChOb3RlOiBpZiBrZXkgYW5kIHBhcmVudFZpZXcgYXJlIHBhc3NlZCBpblxuXHRcdFx0XHQvLyBhbG9uZyB3aXRoIHBhcmVudCB2aWV3LCB0cmVhdCBhcyBpbnNlcnQgLWUuZy4gZnJvbSB2aWV3LmFkZFZpZXdzIC0gc28gcGFyZW50VmlldyBpcyBhbHJlYWR5IHRoZSB2aWV3IGl0ZW0gZm9yIGFycmF5KVxuXHRcdFx0XHRmb3IgKGkgPSAwLCBsID0gZGF0YS5sZW5ndGg7IGkgPCBsOyBpKyspIHtcblx0XHRcdFx0XHR2aWV3LmluZGV4ID0gaTtcblx0XHRcdFx0XHR2aWV3LmRhdGEgPSBkYXRhW2ldO1xuXHRcdFx0XHRcdHJlc3VsdCArPSB0bXBsLmZuKGRhdGFbaV0sIHZpZXcsICRzdWIpO1xuXHRcdFx0XHR9XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR2aWV3LmRhdGEgPSBkYXRhO1xuXHRcdFx0XHRyZXN1bHQgKz0gdG1wbC5mbihkYXRhLCB2aWV3LCAkc3ViKTtcblx0XHRcdH1cblx0XHRcdHZpZXcuZGF0YSA9IHByZXZEYXRhO1xuXHRcdFx0dmlldy5pbmRleCA9IHByZXZJbmRleDtcblx0XHR9XG5cdFx0aWYgKGlzVG9wUmVuZGVyQ2FsbCkge1xuXHRcdFx0aXNSZW
5kZXJDYWxsID0gdW5kZWZpbmVkO1xuXHRcdH1cblx0fVxuXHRyZXR1cm4gcmVzdWx0O1xufVxuXG5mdW5jdGlvbiByZW5kZXJXaXRoVmlld3ModG1wbCwgZGF0YSwgY29udGV4dCwgbm9JdGVyYXRpb24sIHZpZXcsIGtleSwgb25SZW5kZXIsIHRhZykge1xuXHQvLyBSZW5kZXIgdGVtcGxhdGUgYWdhaW5zdCBkYXRhIGFzIGEgdHJlZSBvZiBzdWJ2aWV3cyAobmVzdGVkIHJlbmRlcmVkIHRlbXBsYXRlIGluc3RhbmNlcyksIG9yIGFzIGEgc3RyaW5nICh0b3AtbGV2ZWwgdGVtcGxhdGUpLlxuXHQvLyBJZiB0aGUgZGF0YSBpcyB0aGUgcGFyZW50IHZpZXcsIHRyZWF0IGFzIG5vSXRlcmF0aW9uLCByZS1yZW5kZXIgd2l0aCB0aGUgc2FtZSBkYXRhIGNvbnRleHQuXG5cdC8vIHRtcGwgY2FuIGJlIGEgc3RyaW5nIChlLmcuIHJlbmRlcmVkIGJ5IGEgdGFnLnJlbmRlcigpIG1ldGhvZCksIG9yIGEgY29tcGlsZWQgdGVtcGxhdGUuXG5cdHZhciBpLCBsLCBuZXdWaWV3LCBjaGlsZFZpZXcsIGl0ZW1SZXN1bHQsIHN3YXBDb250ZW50LCBjb250ZW50VG1wbCwgb3V0ZXJPblJlbmRlciwgdG1wbE5hbWUsIGl0ZW1WYXIsIG5ld0N0eCwgdGFnQ3R4LCBub0xpbmtpbmcsXG5cdFx0cmVzdWx0ID0gXCJcIjtcblxuXHRpZiAodGFnKSB7XG5cdFx0Ly8gVGhpcyBpcyBhIGNhbGwgZnJvbSByZW5kZXJUYWcgb3IgdGFnQ3R4LnJlbmRlciguLi4pXG5cdFx0dG1wbE5hbWUgPSB0YWcudGFnTmFtZTtcblx0XHR0YWdDdHggPSB0YWcudGFnQ3R4O1xuXHRcdGNvbnRleHQgPSBjb
250ZXh0ID8gZXh0ZW5kQ3R4KGNvbnRleHQsIHRhZy5jdHgpIDogdGFnLmN0eDtcblxuXHRcdGlmICh0bXBsID09PSB2aWV3LmNvbnRlbnQpIHsgLy8ge3t4eHggdG1wbD0jY29udGVudH19XG5cdFx0XHRjb250ZW50VG1wbCA9IHRtcGwgIT09IHZpZXcuY3R4Ll93cnAgLy8gV2UgYXJlIHJlbmRlcmluZyB0aGUgI2NvbnRlbnRcblx0XHRcdFx0PyB2aWV3LmN0eC5fd3JwIC8vICNjb250ZW50IHdhcyB0aGUgdGFnQ3R4LnByb3BzLnRtcGwgd3JhcHBlciBvZiB0aGUgYmxvY2sgY29udGVudCAtIHNvIHdpdGhpbiB0aGlzIHZpZXcsICNjb250ZW50IHdpbGwgbm93IGJlIHRoZSB2aWV3LmN0eC5fd3JwIGJsb2NrIGNvbnRlbnRcblx0XHRcdFx0OiB1bmRlZmluZWQ7IC8vICNjb250ZW50IHdhcyB0aGUgdmlldy5jdHguX3dycCBibG9jayBjb250ZW50IC0gc28gd2l0aGluIHRoaXMgdmlldywgdGhlcmUgaXMgbm8gbG9uZ2VyIGFueSAjY29udGVudCB0byB3cmFwLlxuXHRcdH0gZWxzZSBpZiAodG1wbCAhPT0gdGFnQ3R4LmNvbnRlbnQpIHtcblx0XHRcdGlmICh0bXBsID09PSB0YWcudGVtcGxhdGUpIHsgLy8gUmVuZGVyaW5nIHt7dGFnfX0gdGFnLnRlbXBsYXRlLCByZXBsYWNpbmcgYmxvY2sgY29udGVudC5cblx0XHRcdFx0Y29udGVudFRtcGwgPSB0YWdDdHgudG1wbDsgLy8gU2V0ICNjb250ZW50IHRvIGJsb2NrIGNvbnRlbnQgKG9yIHdyYXBwZWQgYmxvY2sgY29udGVudCBpZiB0YWdDdHgucHJvcHMudG1wbCBpcyBzZXQpXG5cdFx0XHRcdGNvbnRleHQu
X3dycCA9IHRhZ0N0eC5jb250ZW50OyAvLyBQYXNzIHdyYXBwZWQgYmxvY2sgY29udGVudCB0byBuZXN0ZWQgdmlld3Ncblx0XHRcdH0gZWxzZSB7IC8vIFJlbmRlcmluZyB0YWdDdHgucHJvcHMudG1wbCB3cmFwcGVyXG5cdFx0XHRcdGNvbnRlbnRUbXBsID0gdGFnQ3R4LmNvbnRlbnQgfHwgdmlldy5jb250ZW50OyAvLyBTZXQgI2NvbnRlbnQgdG8gd3JhcHBlZCBibG9jayBjb250ZW50XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnRlbnRUbXBsID0gdmlldy5jb250ZW50OyAvLyBOZXN0ZWQgdmlld3MgaW5oZXJpdCBzYW1lIHdyYXBwZWQgI2NvbnRlbnQgcHJvcGVydHlcblx0XHR9XG5cblx0XHRpZiAodGFnQ3R4LnByb3BzLmxpbmsgPT09IGZhbHNlKSB7XG5cdFx0XHQvLyBsaW5rPWZhbHNlIHNldHRpbmcgb24gYmxvY2sgdGFnXG5cdFx0XHQvLyBXZSB3aWxsIG92ZXJyaWRlIGluaGVyaXRlZCB2YWx1ZSBvZiBsaW5rIGJ5IHRoZSBleHBsaWNpdCBzZXR0aW5nIGxpbms9ZmFsc2UgdGFrZW4gZnJvbSBwcm9wc1xuXHRcdFx0Ly8gVGhlIGNoaWxkIHZpZXdzIG9mIGFuIHVubGlua2VkIHZpZXcgYXJlIGFsc28gdW5saW5rZWQuIFNvIHNldHRpbmcgY2hpbGQgYmFjayB0byB0cnVlIHdpbGwgbm90IGhhdmUgYW55IGVmZmVjdC5cblx0XHRcdGNvbnRleHQgPSBjb250ZXh0IHx8IHt9O1xuXHRcdFx0Y29udGV4dC5saW5rID0gZmFsc2U7XG5cdFx0fVxuXHR9XG5cblx0aWYgKHZpZXcpIHtcblx0XHRvblJlbmRlciA9IG9uUmVuZGVyIHx8IHZ
pZXcuXy5vblJlbmRlcjtcblx0XHRub0xpbmtpbmcgPSBjb250ZXh0ICYmIGNvbnRleHQubGluayA9PT0gZmFsc2U7XG5cblx0XHRpZiAobm9MaW5raW5nICYmIHZpZXcuXy5ubCkge1xuXHRcdFx0b25SZW5kZXIgPSB1bmRlZmluZWQ7XG5cdFx0fVxuXG5cdFx0Y29udGV4dCA9IGV4dGVuZEN0eChjb250ZXh0LCB2aWV3LmN0eCk7XG5cdFx0dGFnQ3R4ID0gIXRhZyAmJiB2aWV3LnRhZ1xuXHRcdFx0PyB2aWV3LnRhZy50YWdDdHhzW3ZpZXcudGFnRWxzZV1cblx0XHRcdDogdGFnQ3R4O1xuXHR9XG5cblx0aWYgKGl0ZW1WYXIgPSB0YWdDdHggJiYgdGFnQ3R4LnByb3BzLml0ZW1WYXIpIHtcblx0XHRpZiAoaXRlbVZhclswXSAhPT0gXCJ+XCIpIHtcblx0XHRcdHN5bnRheEVycm9yKFwiVXNlIGl0ZW1WYXI9J35teUl0ZW0nXCIpO1xuXHRcdH1cblx0XHRpdGVtVmFyID0gaXRlbVZhci5zbGljZSgxKTtcblx0fVxuXG5cdGlmIChrZXkgPT09IHRydWUpIHtcblx0XHRzd2FwQ29udGVudCA9IHRydWU7XG5cdFx0a2V5ID0gMDtcblx0fVxuXG5cdC8vIElmIGxpbms9PT1mYWxzZSwgZG8gbm90IGNhbGwgb25SZW5kZXIsIHNvIG5vIGRhdGEtbGlua2luZyBtYXJrZXIgbm9kZXNcblx0aWYgKG9uUmVuZGVyICYmIHRhZyAmJiB0YWcuXy5ub1Z3cykge1xuXHRcdG9uUmVuZGVyID0gdW5kZWZpbmVkO1xuXHR9XG5cdG91dGVyT25SZW5kZXIgPSBvblJlbmRlcjtcblx0aWYgKG9uUmVuZGVyID09PSB0cnVlKSB7XG5cdFx0Ly8gVXNlZCBieSB2aWV3LnJlZnJlc2goKS
4gRG9uJ3QgY3JlYXRlIGEgbmV3IHdyYXBwZXIgdmlldy5cblx0XHRvdXRlck9uUmVuZGVyID0gdW5kZWZpbmVkO1xuXHRcdG9uUmVuZGVyID0gdmlldy5fLm9uUmVuZGVyO1xuXHR9XG5cdC8vIFNldCBhZGRpdGlvbmFsIGNvbnRleHQgb24gdmlld3MgY3JlYXRlZCBoZXJlLCAoYXMgbW9kaWZpZWQgY29udGV4dCBpbmhlcml0ZWQgZnJvbSB0aGUgcGFyZW50LCBhbmQgdG8gYmUgaW5oZXJpdGVkIGJ5IGNoaWxkIHZpZXdzKVxuXHRjb250ZXh0ID0gdG1wbC5oZWxwZXJzXG5cdFx0PyBleHRlbmRDdHgodG1wbC5oZWxwZXJzLCBjb250ZXh0KVxuXHRcdDogY29udGV4dDtcblxuXHRuZXdDdHggPSBjb250ZXh0O1xuXHRpZiAoJGlzQXJyYXkoZGF0YSkgJiYgIW5vSXRlcmF0aW9uKSB7XG5cdFx0Ly8gQ3JlYXRlIGEgdmlldyBmb3IgdGhlIGFycmF5LCB3aG9zZSBjaGlsZCB2aWV3cyBjb3JyZXNwb25kIHRvIGVhY2ggZGF0YSBpdGVtLiAoTm90ZTogaWYga2V5IGFuZCB2aWV3IGFyZSBwYXNzZWQgaW5cblx0XHQvLyBhbG9uZyB3aXRoIHBhcmVudCB2aWV3LCB0cmVhdCBhcyBpbnNlcnQgLWUuZy4gZnJvbSB2aWV3LmFkZFZpZXdzIC0gc28gdmlldyBpcyBhbHJlYWR5IHRoZSB2aWV3IGl0ZW0gZm9yIGFycmF5KVxuXHRcdG5ld1ZpZXcgPSBzd2FwQ29udGVudFxuXHRcdFx0PyB2aWV3XG5cdFx0XHQ6IChrZXkgIT09IHVuZGVmaW5lZCAmJiB2aWV3KVxuXHRcdFx0XHR8fCBuZXcgVmlldyhjb250ZXh0LCBcImFycmF5XCIsIHZpZXcsIGRhdGEsIHRtcGwsIGtle
Swgb25SZW5kZXIsIGNvbnRlbnRUbXBsKTtcblx0XHRuZXdWaWV3Ll8ubmw9IG5vTGlua2luZztcblx0XHRpZiAodmlldyAmJiB2aWV3Ll8udXNlS2V5KSB7XG5cdFx0XHQvLyBQYXJlbnQgaXMgbm90IGFuICdhcnJheSB2aWV3J1xuXHRcdFx0bmV3Vmlldy5fLmJuZCA9ICF0YWcgfHwgdGFnLl8uYm5kICYmIHRhZzsgLy8gRm9yIGFycmF5IHZpZXdzIHRoYXQgYXJlIGRhdGEgYm91bmQgZm9yIGNvbGxlY3Rpb24gY2hhbmdlIGV2ZW50cywgc2V0IHRoZVxuXHRcdFx0Ly8gdmlldy5fLmJuZCBwcm9wZXJ0eSB0byB0cnVlIGZvciB0b3AtbGV2ZWwgbGluaygpIG9yIGRhdGEtbGluaz1cIntmb3J9XCIsIG9yIHRvIHRoZSB0YWcgaW5zdGFuY2UgZm9yIGEgZGF0YS1ib3VuZCB0YWcsIGUuZy4ge157Zm9yIC4uLn19XG5cdFx0XHRuZXdWaWV3LnRhZyA9IHRhZztcblx0XHR9XG5cdFx0Zm9yIChpID0gMCwgbCA9IGRhdGEubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG5cdFx0XHQvLyBDcmVhdGUgYSB2aWV3IGZvciBlYWNoIGRhdGEgaXRlbS5cblx0XHRcdGNoaWxkVmlldyA9IG5ldyBWaWV3KG5ld0N0eCwgXCJpdGVtXCIsIG5ld1ZpZXcsIGRhdGFbaV0sIHRtcGwsIChrZXkgfHwgMCkgKyBpLCBvblJlbmRlciwgbmV3Vmlldy5jb250ZW50KTtcblx0XHRcdGlmIChpdGVtVmFyKSB7XG5cdFx0XHRcdChjaGlsZFZpZXcuY3R4ID0gJGV4dGVuZCh7fSwgbmV3Q3R4KSlbaXRlbVZhcl0gPSAkc3ViLl9jcChkYXRhW2ldLCBcIiNkYXRhXCIsIGNoaWxkVmlldyk7XG5cdFx0
XHR9XG5cdFx0XHRpdGVtUmVzdWx0ID0gdG1wbC5mbihkYXRhW2ldLCBjaGlsZFZpZXcsICRzdWIpO1xuXHRcdFx0cmVzdWx0ICs9IG5ld1ZpZXcuXy5vblJlbmRlciA/IG5ld1ZpZXcuXy5vblJlbmRlcihpdGVtUmVzdWx0LCBjaGlsZFZpZXcpIDogaXRlbVJlc3VsdDtcblx0XHR9XG5cdH0gZWxzZSB7XG5cdFx0Ly8gQ3JlYXRlIGEgdmlldyBmb3Igc2luZ2xldG9uIGRhdGEgb2JqZWN0LiBUaGUgdHlwZSBvZiB0aGUgdmlldyB3aWxsIGJlIHRoZSB0YWcgbmFtZSwgZS5nLiBcImlmXCIgb3IgXCJteXRhZ1wiIGV4Y2VwdCBmb3Jcblx0XHQvLyBcIml0ZW1cIiwgXCJhcnJheVwiIGFuZCBcImRhdGFcIiB2aWV3cy4gQSBcImRhdGFcIiB2aWV3IGlzIGZyb20gcHJvZ3JhbW1hdGljIHJlbmRlcihvYmplY3QpIGFnYWluc3QgYSAnc2luZ2xldG9uJy5cblx0XHRuZXdWaWV3ID0gc3dhcENvbnRlbnQgPyB2aWV3IDogbmV3IFZpZXcobmV3Q3R4LCB0bXBsTmFtZSB8fCBcImRhdGFcIiwgdmlldywgZGF0YSwgdG1wbCwga2V5LCBvblJlbmRlciwgY29udGVudFRtcGwpO1xuXG5cdFx0aWYgKGl0ZW1WYXIpIHtcblx0XHRcdChuZXdWaWV3LmN0eCA9ICRleHRlbmQoe30sIG5ld0N0eCkpW2l0ZW1WYXJdID0gJHN1Yi5fY3AoZGF0YSwgXCIjZGF0YVwiLCBuZXdWaWV3KTtcblx0XHR9XG5cblx0XHRuZXdWaWV3LnRhZyA9IHRhZztcblx0XHRuZXdWaWV3Ll8ubmwgPSBub0xpbmtpbmc7XG5cdFx0cmVzdWx0ICs9IHRtcGwuZm4oZGF0YSwgbmV3VmlldywgJHN1Yik7XG5
cdH1cblx0aWYgKHRhZykge1xuXHRcdG5ld1ZpZXcudGFnRWxzZSA9IHRhZ0N0eC5pbmRleDtcblx0XHR0YWdDdHguY29udGVudFZpZXcgPSBuZXdWaWV3O1xuXHR9XG5cdHJldHVybiBvdXRlck9uUmVuZGVyID8gb3V0ZXJPblJlbmRlcihyZXN1bHQsIG5ld1ZpZXcpIDogcmVzdWx0O1xufVxuXG4vLz09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gQnVpbGQgYW5kIGNvbXBpbGUgdGVtcGxhdGVcbi8vPT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8vIEdlbmVyYXRlIGEgcmV1c2FibGUgZnVuY3Rpb24gdGhhdCB3aWxsIHNlcnZlIHRvIHJlbmRlciBhIHRlbXBsYXRlIGFnYWluc3QgZGF0YVxuLy8gKENvbXBpbGUgQVNUIHRoZW4gYnVpbGQgdGVtcGxhdGUgZnVuY3Rpb24pXG5cbmZ1bmN0aW9uIG9uUmVuZGVyRXJyb3IoZSwgdmlldywgZmFsbGJhY2spIHtcblx0dmFyIG1lc3NhZ2UgPSBmYWxsYmFjayAhPT0gdW5kZWZpbmVkXG5cdFx0PyAkaXNGdW5jdGlvbihmYWxsYmFjaylcblx0XHRcdD8gZmFsbGJhY2suY2FsbCh2aWV3LmRhdGEsIGUsIHZpZXcpXG5cdFx0XHQ6IGZhbGxiYWNrIHx8IFwiXCJcblx0XHQ6IFwie0Vycm9yOiBcIiArIChlLm1lc3NhZ2V8fGUpICsgXCJ9XCI7XG5cblx0aWYgKCRzdWJTZXR0aW5ncy5vbkVycm9yICYmIChmYWxsYmFjayA9ICRzdWJTZXR0aW5ncy5vbkVycm9yLmNhbGwodmlldy5kYXRhLCBlLCBmYWxsYmFjayAmJiBtZXNzYWdlLCB2aWV3KSkgIT09IHVuZGVmaW5lZCkge1xuXHRcdG1lc3NhZ2UgPS
BmYWxsYmFjazsgLy8gVGhlcmUgaXMgYSBzZXR0aW5ncy5kZWJ1Z01vZGUoaGFuZGxlcikgb25FcnJvciBvdmVycmlkZS4gQ2FsbCBpdCwgYW5kIHVzZSByZXR1cm4gdmFsdWUgKGlmIGFueSkgdG8gcmVwbGFjZSBtZXNzYWdlXG5cdH1cblx0cmV0dXJuIHZpZXcgJiYgIXZpZXcuX2xjID8gJGNvbnZlcnRlcnMuaHRtbChtZXNzYWdlKSA6IG1lc3NhZ2U7IC8vIEZvciBkYXRhLWxpbms9XFxcInsuLi4gb25FcnJvcj0uLi59XCIuLi4gU2VlIG9uRGF0YUxpbmtlZFRhZ0NoYW5nZVxufVxuXG5mdW5jdGlvbiBlcnJvcihtZXNzYWdlKSB7XG5cdHRocm93IG5ldyAkc3ViLkVycihtZXNzYWdlKTtcbn1cblxuZnVuY3Rpb24gc3ludGF4RXJyb3IobWVzc2FnZSkge1xuXHRlcnJvcihcIlN5bnRheCBlcnJvclxcblwiICsgbWVzc2FnZSk7XG59XG5cbmZ1bmN0aW9uIHRtcGxGbihtYXJrdXAsIHRtcGwsIGlzTGlua0V4cHIsIGNvbnZlcnRCYWNrLCBoYXNFbHNlKSB7XG5cdC8vIENvbXBpbGUgbWFya3VwIHRvIEFTVCAoYWJ0cmFjdCBzeW50YXggdHJlZSkgdGhlbiBidWlsZCB0aGUgdGVtcGxhdGUgZnVuY3Rpb24gY29kZSBmcm9tIHRoZSBBU1Qgbm9kZXNcblx0Ly8gVXNlZCBmb3IgY29tcGlsaW5nIHRlbXBsYXRlcywgYW5kIGFsc28gYnkgSnNWaWV3cyB0byBidWlsZCBmdW5jdGlvbnMgZm9yIGRhdGEgbGluayBleHByZXNzaW9uc1xuXG5cdC8vPT09PSBuZXN0ZWQgZnVuY3Rpb25zID09PT1cblx0ZnVuY3Rpb24gcHVzaHByZWNlZGluZ0NvbnRlbnQoc2hpZnQpI
Htcblx0XHRzaGlmdCAtPSBsb2M7XG5cdFx0aWYgKHNoaWZ0KSB7XG5cdFx0XHRjb250ZW50LnB1c2gobWFya3VwLnN1YnN0cihsb2MsIHNoaWZ0KS5yZXBsYWNlKHJOZXdMaW5lLCBcIlxcXFxuXCIpKTtcblx0XHR9XG5cdH1cblxuXHRmdW5jdGlvbiBibG9ja1RhZ0NoZWNrKHRhZ05hbWUsIGJsb2NrKSB7XG5cdFx0aWYgKHRhZ05hbWUpIHtcblx0XHRcdHRhZ05hbWUgKz0gJ319Jztcblx0XHRcdC8vXHRcdFx0J3t7aW5jbHVkZX19IGJsb2NrIGhhcyB7ey9mb3J9fSB3aXRoIG5vIG9wZW4ge3tmb3J9fSdcblx0XHRcdHN5bnRheEVycm9yKChcblx0XHRcdFx0YmxvY2tcblx0XHRcdFx0XHQ/ICd7eycgKyBibG9jayArICd9fSBibG9jayBoYXMge3svJyArIHRhZ05hbWUgKyAnIHdpdGhvdXQge3snICsgdGFnTmFtZVxuXHRcdFx0XHRcdDogJ1VubWF0Y2hlZCBvciBtaXNzaW5nIHt7LycgKyB0YWdOYW1lKSArICcsIGluIHRlbXBsYXRlOlxcbicgKyBtYXJrdXApO1xuXHRcdH1cblx0fVxuXG5cdGZ1bmN0aW9uIHBhcnNlVGFnKGFsbCwgYmluZCwgdGFnTmFtZSwgY29udmVydGVyLCBjb2xvbiwgaHRtbCwgY29kZVRhZywgcGFyYW1zLCBzbGFzaCwgYmluZDIsIGNsb3NlQmxvY2ssIGluZGV4KSB7XG4vKlxuXG4gICAgIGJpbmQgICAgIHRhZ05hbWUgICAgICAgICBjdnQgICBjbG4gaHRtbCBjb2RlICAgIHBhcmFtcyAgICAgICAgICAgIHNsYXNoICAgYmluZDIgICAgICAgICBjbG9zZUJsayAgY29tbWVudFxuLyg/OnsoXFxeKT97KD86KFxcdysoPz1bXFwv
XFxzfV0pKXwoXFx3Kyk/KDopfCg+KXwoXFwqKSlcXHMqKCg/OltefV18fSg/IX0pKSo/KShcXC8pP3x7KFxcXik/eyg/Oig/OlxcLyhcXHcrKSlcXHMqfCEtLVtcXHNcXFNdKj8tLSkpfX0vZ1xuXG4oPzpcbiAgeyhcXF4pP3sgICAgICAgICAgICBiaW5kXG4gICg/OlxuICAgIChcXHcrICAgICAgICAgICAgIHRhZ05hbWVcbiAgICAgICg/PVtcXC9cXHN9XSlcbiAgICApXG4gICAgfFxuICAgIChcXHcrKT8oOikgICAgICAgIGNvbnZlcnRlciBjb2xvblxuICAgIHxcbiAgICAoPikgICAgICAgICAgICAgIGh0bWxcbiAgICB8XG4gICAgKFxcKikgICAgICAgICAgICAgY29kZVRhZ1xuICApXG4gIFxccypcbiAgKCAgICAgICAgICAgICAgICAgIHBhcmFtc1xuICAgICg/OltefV18fSg/IX0pKSo/XG4gIClcbiAgKFxcLyk/ICAgICAgICAgICAgICBzbGFzaFxuICB8XG4gIHsoXFxeKT97ICAgICAgICAgICAgYmluZDJcbiAgKD86XG4gICAgKD86XFwvKFxcdyspKVxccyogICBjbG9zZUJsb2NrXG4gICAgfFxuICAgICEtLVtcXHNcXFNdKj8tLSAgICBjb21tZW50XG4gIClcbilcbn19L2dcblxuKi9cblx0XHRpZiAoY29kZVRhZyAmJiBiaW5kIHx8IHNsYXNoICYmICF0YWdOYW1lIHx8IHBhcmFtcyAmJiBwYXJhbXMuc2xpY2UoLTEpID09PSBcIjpcIiB8fCBiaW5kMikge1xuXHRcdFx0c3ludGF4RXJyb3IoYWxsKTtcblx0XHR9XG5cblx0XHQvLyBCdWlsZCBhYnN0cmFjdCBzeW50YXggdHJlZSAoQVNUKTogW3RhZ05hbWUsIGNvbnZlcnRlciwgcGFyYW1zLCB
jb250ZW50LCBoYXNoLCBiaW5kaW5ncywgY29udGVudE1hcmt1cF1cblx0XHRpZiAoaHRtbCkge1xuXHRcdFx0Y29sb24gPSBcIjpcIjtcblx0XHRcdGNvbnZlcnRlciA9IEhUTUw7XG5cdFx0fVxuXHRcdHNsYXNoID0gc2xhc2ggfHwgaXNMaW5rRXhwciAmJiAhaGFzRWxzZTtcblxuXHRcdHZhciBsYXRlLCBvcGVuVGFnTmFtZSwgaXNMYXRlT2IsXG5cdFx0XHRwYXRoQmluZGluZ3MgPSAoYmluZCB8fCBpc0xpbmtFeHByKSAmJiBbW11dLCAvLyBwYXRoQmluZGluZ3MgaXMgYW4gYXJyYXkgb2YgYXJyYXlzIGZvciBhcmcgYmluZGluZ3MgYW5kIGEgaGFzaCBvZiBhcnJheXMgZm9yIHByb3AgYmluZGluZ3Ncblx0XHRcdHByb3BzID0gXCJcIixcblx0XHRcdGFyZ3MgPSBcIlwiLFxuXHRcdFx0Y3R4UHJvcHMgPSBcIlwiLFxuXHRcdFx0cGFyYW1zQXJncyA9IFwiXCIsXG5cdFx0XHRwYXJhbXNQcm9wcyA9IFwiXCIsXG5cdFx0XHRwYXJhbXNDdHhQcm9wcyA9IFwiXCIsXG5cdFx0XHRvbkVycm9yID0gXCJcIixcblx0XHRcdHVzZVRyaWdnZXIgPSBcIlwiLFxuXHRcdFx0Ly8gQmxvY2sgdGFnIGlmIG5vdCBzZWxmLWNsb3NpbmcgYW5kIG5vdCB7ezp9fSBvciB7ez59fSAoc3BlY2lhbCBjYXNlKSBhbmQgbm90IGEgZGF0YS1saW5rIGV4cHJlc3Npb25cblx0XHRcdGJsb2NrID0gIXNsYXNoICYmICFjb2xvbjtcblxuXHRcdC8vPT09PSBuZXN0ZWQgaGVscGVyIGZ1bmN0aW9uID09PT1cblx0XHR0YWdOYW1lID0gdGFnTmFtZSB8fCAocGFyYW1zID0gcGFyYW1zIH
x8IFwiI2RhdGFcIiwgY29sb24pOyAvLyB7ezp9fSBpcyBlcXVpdmFsZW50IHRvIHt7OiNkYXRhfX1cblx0XHRwdXNocHJlY2VkaW5nQ29udGVudChpbmRleCk7XG5cdFx0bG9jID0gaW5kZXggKyBhbGwubGVuZ3RoOyAvLyBsb2NhdGlvbiBtYXJrZXIgLSBwYXJzZWQgdXAgdG8gaGVyZVxuXHRcdGlmIChjb2RlVGFnKSB7XG5cdFx0XHRpZiAoYWxsb3dDb2RlKSB7XG5cdFx0XHRcdGNvbnRlbnQucHVzaChbXCIqXCIsIFwiXFxuXCIgKyBwYXJhbXMucmVwbGFjZSgvXjovLCBcInJldCs9IFwiKS5yZXBsYWNlKHJVbmVzY2FwZVF1b3RlcywgXCIkMVwiKSArIFwiO1xcblwiXSk7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIGlmICh0YWdOYW1lKSB7XG5cdFx0XHRpZiAodGFnTmFtZSA9PT0gXCJlbHNlXCIpIHtcblx0XHRcdFx0aWYgKHJUZXN0RWxzZUlmLnRlc3QocGFyYW1zKSkge1xuXHRcdFx0XHRcdHN5bnRheEVycm9yKCdGb3IgXCJ7e2Vsc2UgaWYgZXhwcn19XCIgdXNlIFwie3tlbHNlIGV4cHJ9fVwiJyk7XG5cdFx0XHRcdH1cblx0XHRcdFx0cGF0aEJpbmRpbmdzID0gY3VycmVudFs5XSAmJiBbW11dO1xuXHRcdFx0XHRjdXJyZW50WzEwXSA9IG1hcmt1cC5zdWJzdHJpbmcoY3VycmVudFsxMF0sIGluZGV4KTsgLy8gY29udGVudE1hcmt1cCBmb3IgYmxvY2sgdGFnXG5cdFx0XHRcdG9wZW5UYWdOYW1lID0gY3VycmVudFsxMV0gfHwgY3VycmVudFswXSB8fCBzeW50YXhFcnJvcihcIk1pc21hdGNoZWQ6IFwiICsgYWxsKTtcblx0XHRcdFx0Ly8gY3VycmVud
FswXSBpcyB0YWdOYW1lLCBidXQgZm9yIHt7ZWxzZX19IG5vZGVzLCBjdXJyZW50WzExXSBpcyB0YWdOYW1lIG9mIHByZWNlZGluZyBvcGVuIHRhZ1xuXHRcdFx0XHRjdXJyZW50ID0gc3RhY2sucG9wKCk7XG5cdFx0XHRcdGNvbnRlbnQgPSBjdXJyZW50WzJdO1xuXHRcdFx0XHRibG9jayA9IHRydWU7XG5cdFx0XHR9XG5cdFx0XHRpZiAocGFyYW1zKSB7XG5cdFx0XHRcdC8vIHJlbW92ZSBuZXdsaW5lcyBmcm9tIHRoZSBwYXJhbXMgc3RyaW5nLCB0byBhdm9pZCBjb21waWxlZCBjb2RlIGVycm9ycyBmb3IgdW50ZXJtaW5hdGVkIHN0cmluZ3Ncblx0XHRcdFx0cGFyc2VQYXJhbXMocGFyYW1zLnJlcGxhY2Uock5ld0xpbmUsIFwiIFwiKSwgcGF0aEJpbmRpbmdzLCB0bXBsLCBpc0xpbmtFeHByKVxuXHRcdFx0XHRcdC5yZXBsYWNlKHJCdWlsZEhhc2gsIGZ1bmN0aW9uKGFsbCwgb25lcnJvciwgaXNDdHhQcm0sIGtleSwga2V5VG9rZW4sIGtleVZhbHVlLCBhcmcsIHBhcmFtKSB7XG5cdFx0XHRcdFx0XHRpZiAoa2V5ID09PSBcInRoaXM6XCIpIHtcblx0XHRcdFx0XHRcdFx0a2V5VmFsdWUgPSBcInVuZGVmaW5lZFwiOyAvLyB0aGlzPXNvbWUucGF0aCBpcyBhbHdheXMgYSB0byBwYXJhbWV0ZXIgKG9uZS13YXkpLCBzbyBkb24ndCBuZWVkIHRvIGNvbXBpbGUvZXZhbHVhdGUgc29tZS5wYXRoIGluaXRpYWxpemF0aW9uXG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRpZiAocGFyYW0pIHtcblx0XHRcdFx0XHRcdFx0aXNMYXRlT2IgPSBpc0xhdGVPYiB8
fCBwYXJhbVswXSA9PT0gXCJAXCI7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRrZXkgPSBcIidcIiArIGtleVRva2VuICsgXCInOlwiO1xuXHRcdFx0XHRcdFx0aWYgKGFyZykge1xuXHRcdFx0XHRcdFx0XHRhcmdzICs9IGlzQ3R4UHJtICsga2V5VmFsdWUgKyBcIixcIjtcblx0XHRcdFx0XHRcdFx0cGFyYW1zQXJncyArPSBcIidcIiArIHBhcmFtICsgXCInLFwiO1xuXHRcdFx0XHRcdFx0fSBlbHNlIGlmIChpc0N0eFBybSkgeyAvLyBDb250ZXh0dWFsIHBhcmFtZXRlciwgfmZvbz1leHByXG5cdFx0XHRcdFx0XHRcdGN0eFByb3BzICs9IGtleSArICdqLl9jcCgnICsga2V5VmFsdWUgKyAnLFwiJyArIHBhcmFtICsgJ1wiLHZpZXcpLCc7XG5cdFx0XHRcdFx0XHRcdC8vIENvbXBpbGVkIGNvZGUgZm9yIGV2YWx1YXRpbmcgdGFnQ3R4IG9uIGEgdGFnIHdpbGwgaGF2ZTogY3R4OnsnZm9vJzpqLl9jcChjb21waWxlZEV4cHIsIFwiZXhwclwiLCB2aWV3KX1cblx0XHRcdFx0XHRcdFx0cGFyYW1zQ3R4UHJvcHMgKz0ga2V5ICsgXCInXCIgKyBwYXJhbSArIFwiJyxcIjtcblx0XHRcdFx0XHRcdH0gZWxzZSBpZiAob25lcnJvcikge1xuXHRcdFx0XHRcdFx0XHRvbkVycm9yICs9IGtleVZhbHVlO1xuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0aWYgKGtleVRva2VuID09PSBcInRyaWdnZXJcIikge1xuXHRcdFx0XHRcdFx0XHRcdHVzZVRyaWdnZXIgKz0ga2V5VmFsdWU7XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdFx0aWY
gKGtleVRva2VuID09PSBcImxhdGVSZW5kZXJcIikge1xuXHRcdFx0XHRcdFx0XHRcdGxhdGUgPSBwYXJhbSAhPT0gXCJmYWxzZVwiOyAvLyBSZW5kZXIgYWZ0ZXIgZmlyc3QgcGFzc1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdHByb3BzICs9IGtleSArIGtleVZhbHVlICsgXCIsXCI7XG5cdFx0XHRcdFx0XHRcdHBhcmFtc1Byb3BzICs9IGtleSArIFwiJ1wiICsgcGFyYW0gKyBcIicsXCI7XG5cdFx0XHRcdFx0XHRcdGhhc0hhbmRsZXJzID0gaGFzSGFuZGxlcnMgfHwgckhhc0hhbmRsZXJzLnRlc3Qoa2V5VG9rZW4pO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0cmV0dXJuIFwiXCI7XG5cdFx0XHRcdFx0fSkuc2xpY2UoMCwgLTEpO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAocGF0aEJpbmRpbmdzICYmIHBhdGhCaW5kaW5nc1swXSkge1xuXHRcdFx0XHRwYXRoQmluZGluZ3MucG9wKCk7IC8vIFJlbW92ZSB0aGUgYmluZGluZyB0aGF0IHdhcyBwcmVwYXJlZCBmb3IgbmV4dCBhcmcuIChUaGVyZSBpcyBhbHdheXMgYW4gZXh0cmEgb25lIHJlYWR5KS5cblx0XHRcdH1cblxuXHRcdFx0bmV3Tm9kZSA9IFtcblx0XHRcdFx0XHR0YWdOYW1lLFxuXHRcdFx0XHRcdGNvbnZlcnRlciB8fCAhIWNvbnZlcnRCYWNrIHx8IGhhc0hhbmRsZXJzIHx8IFwiXCIsXG5cdFx0XHRcdFx0YmxvY2sgJiYgW10sXG5cdFx0XHRcdFx0cGFyc2VkUGFyYW0ocGFyYW1zQXJncyB8fCAodGFnTmFtZSA9PT0gXCI6XCIgPyBcIicjZGF0YScsXCIgOiBcIlwiKSwgcG
FyYW1zUHJvcHMsIHBhcmFtc0N0eFByb3BzKSwgLy8ge3s6fX0gZXF1aXZhbGVudCB0byB7ezojZGF0YX19XG5cdFx0XHRcdFx0cGFyc2VkUGFyYW0oYXJncyB8fCAodGFnTmFtZSA9PT0gXCI6XCIgPyBcImRhdGEsXCIgOiBcIlwiKSwgcHJvcHMsIGN0eFByb3BzKSxcblx0XHRcdFx0XHRvbkVycm9yLFxuXHRcdFx0XHRcdHVzZVRyaWdnZXIsXG5cdFx0XHRcdFx0bGF0ZSxcblx0XHRcdFx0XHRpc0xhdGVPYixcblx0XHRcdFx0XHRwYXRoQmluZGluZ3MgfHwgMFxuXHRcdFx0XHRdO1xuXHRcdFx0Y29udGVudC5wdXNoKG5ld05vZGUpO1xuXHRcdFx0aWYgKGJsb2NrKSB7XG5cdFx0XHRcdHN0YWNrLnB1c2goY3VycmVudCk7XG5cdFx0XHRcdGN1cnJlbnQgPSBuZXdOb2RlO1xuXHRcdFx0XHRjdXJyZW50WzEwXSA9IGxvYzsgLy8gU3RvcmUgY3VycmVudCBsb2NhdGlvbiBvZiBvcGVuIHRhZywgdG8gYmUgYWJsZSB0byBhZGQgY29udGVudE1hcmt1cCB3aGVuIHdlIHJlYWNoIGNsb3NpbmcgdGFnXG5cdFx0XHRcdGN1cnJlbnRbMTFdID0gb3BlblRhZ05hbWU7IC8vIFVzZWQgZm9yIGNoZWNraW5nIHN5bnRheCAobWF0Y2hpbmcgY2xvc2UgdGFnKVxuXHRcdFx0fVxuXHRcdH0gZWxzZSBpZiAoY2xvc2VCbG9jaykge1xuXHRcdFx0YmxvY2tUYWdDaGVjayhjbG9zZUJsb2NrICE9PSBjdXJyZW50WzBdICYmIGNsb3NlQmxvY2sgIT09IGN1cnJlbnRbMTFdICYmIGNsb3NlQmxvY2ssIGN1cnJlbnRbMF0pOyAvLyBDaGVjayBtYXRjaGluZyBjbG9zZSB0YWcgbmFtZ
VxuXHRcdFx0Y3VycmVudFsxMF0gPSBtYXJrdXAuc3Vic3RyaW5nKGN1cnJlbnRbMTBdLCBpbmRleCk7IC8vIGNvbnRlbnRNYXJrdXAgZm9yIGJsb2NrIHRhZ1xuXHRcdFx0Y3VycmVudCA9IHN0YWNrLnBvcCgpO1xuXHRcdH1cblx0XHRibG9ja1RhZ0NoZWNrKCFjdXJyZW50ICYmIGNsb3NlQmxvY2spO1xuXHRcdGNvbnRlbnQgPSBjdXJyZW50WzJdO1xuXHR9XG5cdC8vPT09PSAvZW5kIG9mIG5lc3RlZCBmdW5jdGlvbnMgPT09PVxuXG5cdHZhciBpLCByZXN1bHQsIG5ld05vZGUsIGhhc0hhbmRsZXJzLCBiaW5kaW5ncyxcblx0XHRhbGxvd0NvZGUgPSAkc3ViU2V0dGluZ3MuYWxsb3dDb2RlIHx8IHRtcGwgJiYgdG1wbC5hbGxvd0NvZGVcblx0XHRcdHx8ICR2aWV3c1NldHRpbmdzLmFsbG93Q29kZSA9PT0gdHJ1ZSwgLy8gaW5jbHVkZSBkaXJlY3Qgc2V0dGluZyBvZiBzZXR0aW5ncy5hbGxvd0NvZGUgdHJ1ZSBmb3IgYmFja3dhcmQgY29tcGF0IG9ubHlcblx0XHRhc3RUb3AgPSBbXSxcblx0XHRsb2MgPSAwLFxuXHRcdHN0YWNrID0gW10sXG5cdFx0Y29udGVudCA9IGFzdFRvcCxcblx0XHRjdXJyZW50ID0gWywsYXN0VG9wXTtcblxuXHRpZiAoYWxsb3dDb2RlICYmIHRtcGwuX2lzKSB7XG5cdFx0dG1wbC5hbGxvd0NvZGUgPSBhbGxvd0NvZGU7XG5cdH1cblxuLy9UT0RPXHRyZXN1bHQgPSB0bXBsRm5zQ2FjaGVbbWFya3VwXTsgLy8gT25seSBjYWNoZSBpZiB0ZW1wbGF0ZSBpcyBub3QgbmFtZWQgYW5kIG1hcmt1cCBsZW5ndGggPCAuLi4sXG4vL2Fu
ZCB0aGVyZSBhcmUgbm8gYmluZGluZ3Mgb3Igc3VidGVtcGxhdGVzPz8gQ29uc2lkZXIgc3RhbmRhcmQgb3B0aW1pemF0aW9uIGZvciBkYXRhLWxpbms9XCJhLmIuY1wiXG4vL1x0XHRpZiAocmVzdWx0KSB7XG4vL1x0XHRcdHRtcGwuZm4gPSByZXN1bHQ7XG4vL1x0XHR9IGVsc2Uge1xuXG4vL1x0XHRyZXN1bHQgPSBtYXJrdXA7XG5cdGlmIChpc0xpbmtFeHByKSB7XG5cdFx0aWYgKGNvbnZlcnRCYWNrICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdG1hcmt1cCA9IG1hcmt1cC5zbGljZSgwLCAtY29udmVydEJhY2subGVuZ3RoIC0gMikgKyBkZWxpbUNsb3NlQ2hhcjA7XG5cdFx0fVxuXHRcdG1hcmt1cCA9IGRlbGltT3BlbkNoYXIwICsgbWFya3VwICsgZGVsaW1DbG9zZUNoYXIxO1xuXHR9XG5cblx0YmxvY2tUYWdDaGVjayhzdGFja1swXSAmJiBzdGFja1swXVsyXS5wb3AoKVswXSk7XG5cdC8vIEJ1aWxkIHRoZSBBU1QgKGFic3RyYWN0IHN5bnRheCB0cmVlKSB1bmRlciBhc3RUb3Bcblx0bWFya3VwLnJlcGxhY2UoclRhZywgcGFyc2VUYWcpO1xuXG5cdHB1c2hwcmVjZWRpbmdDb250ZW50KG1hcmt1cC5sZW5ndGgpO1xuXG5cdGlmIChsb2MgPSBhc3RUb3BbYXN0VG9wLmxlbmd0aCAtIDFdKSB7XG5cdFx0YmxvY2tUYWdDaGVjayhcIlwiICsgbG9jICE9PSBsb2MgJiYgKCtsb2NbMTBdID09PSBsb2NbMTBdKSAmJiBsb2NbMF0pO1xuXHR9XG4vL1x0XHRcdHJlc3VsdCA9IHRtcGxGbnNDYWNoZVttYXJrdXBdID0gYnVpbGRDb2RlKGFzdFRvcCwgdG1wbCk
7XG4vL1x0XHR9XG5cblx0aWYgKGlzTGlua0V4cHIpIHtcblx0XHRyZXN1bHQgPSBidWlsZENvZGUoYXN0VG9wLCBtYXJrdXAsIGlzTGlua0V4cHIpO1xuXHRcdGJpbmRpbmdzID0gW107XG5cdFx0aSA9IGFzdFRvcC5sZW5ndGg7XG5cdFx0d2hpbGUgKGktLSkge1xuXHRcdFx0YmluZGluZ3MudW5zaGlmdChhc3RUb3BbaV1bOV0pOyAvLyBXaXRoIGRhdGEtbGluayBleHByZXNzaW9ucywgcGF0aEJpbmRpbmdzIGFycmF5IGZvciB0YWdDdHhbaV0gaXMgYXN0VG9wW2ldWzldXG5cdFx0fVxuXHRcdHNldFBhdGhzKHJlc3VsdCwgYmluZGluZ3MpO1xuXHR9IGVsc2Uge1xuXHRcdHJlc3VsdCA9IGJ1aWxkQ29kZShhc3RUb3AsIHRtcGwpO1xuXHR9XG5cdHJldHVybiByZXN1bHQ7XG59XG5cbmZ1bmN0aW9uIHNldFBhdGhzKGZuLCBwYXRoc0Fycikge1xuXHR2YXIga2V5LCBwYXRocyxcblx0XHRpID0gMCxcblx0XHRsID0gcGF0aHNBcnIubGVuZ3RoO1xuXHRmbi5kZXBzID0gW107XG5cdGZuLnBhdGhzID0gW107IC8vIFRoZSBhcnJheSBvZiBwYXRoIGJpbmRpbmcgKGFycmF5L2RpY3Rpb25hcnkpcyBmb3IgZWFjaCB0YWcvZWxzZSBibG9jaydzIGFyZ3MgYW5kIHByb3BzXG5cdGZvciAoOyBpIDwgbDsgaSsrKSB7XG5cdFx0Zm4ucGF0aHMucHVzaChwYXRocyA9IHBhdGhzQXJyW2ldKTtcblx0XHRmb3IgKGtleSBpbiBwYXRocykge1xuXHRcdFx0aWYgKGtleSAhPT0gXCJfanN2dG9cIiAmJiBwYXRocy5oYXNPd25Qcm9wZXJ0eShrZXkpICYmIHBhdGhzW2tleV
0ubGVuZ3RoICYmICFwYXRoc1trZXldLnNrcCkge1xuXHRcdFx0XHRmbi5kZXBzID0gZm4uZGVwcy5jb25jYXQocGF0aHNba2V5XSk7IC8vIGRlcHMgaXMgdGhlIGNvbmNhdGVuYXRpb24gb2YgdGhlIHBhdGhzIGFycmF5cyBmb3IgdGhlIGRpZmZlcmVudCBiaW5kaW5nc1xuXHRcdFx0fVxuXHRcdH1cblx0fVxufVxuXG5mdW5jdGlvbiBwYXJzZWRQYXJhbShhcmdzLCBwcm9wcywgY3R4KSB7XG5cdHJldHVybiBbYXJncy5zbGljZSgwLCAtMSksIHByb3BzLnNsaWNlKDAsIC0xKSwgY3R4LnNsaWNlKDAsIC0xKV07XG59XG5cbmZ1bmN0aW9uIHBhcmFtU3RydWN0dXJlKHBhcmFtQ29kZSwgcGFyYW1WYWxzKSB7XG5cdHJldHVybiAnXFxuXFx0cGFyYW1zOnthcmdzOlsnICsgcGFyYW1Db2RlWzBdICsgJ10sXFxuXFx0cHJvcHM6eycgKyBwYXJhbUNvZGVbMV0gKyAnfSdcblx0XHQrIChwYXJhbUNvZGVbMl0gPyAnLFxcblxcdGN0eDp7JyArIHBhcmFtQ29kZVsyXSArICd9JyA6IFwiXCIpXG5cdFx0KyAnfSxcXG5cXHRhcmdzOlsnICsgcGFyYW1WYWxzWzBdICsgJ10sXFxuXFx0cHJvcHM6eycgKyBwYXJhbVZhbHNbMV0gKyAnfSdcblx0XHQrIChwYXJhbVZhbHNbMl0gPyAnLFxcblxcdGN0eDp7JyArIHBhcmFtVmFsc1syXSArICd9JyA6IFwiXCIpO1xufVxuXG5mdW5jdGlvbiBwYXJzZVBhcmFtcyhwYXJhbXMsIHBhdGhCaW5kaW5ncywgdG1wbCwgaXNMaW5rRXhwcikge1xuXG5cdGZ1bmN0aW9uIHBhcnNlVG9rZW5zKGFsbCwgbGZ0UHJuMCwgbGZ0UHJuLCBib
3VuZCwgcGF0aCwgb3BlcmF0b3IsIGVyciwgZXEsIHBhdGgyLCBsYXRlLCBwcm4sXG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRjb21tYSwgbGZ0UHJuMiwgYXBvcywgcXVvdCwgcnRQcm4sIHJ0UHJuRG90LCBwcm4yLCBzcGFjZSwgaW5kZXgsIGZ1bGwpIHtcblx0Ly8gLyhcXCgpKD89XFxzKlxcKCl8KD86KFsoW10pXFxzKik/KD86KFxcXj8pKH4/W1xcdyQuXl0rKT9cXHMqKChcXCtcXCt8LS0pfFxcK3wtfH4oPyFbXFx3JF0pfCYmfFxcfFxcfHw9PT18IT09fD09fCE9fDw9fD49fFs8PiUqOj9cXC9dfCg9KSlcXHMqfCghKj8oQCk/WyN+XT9bXFx3JC5eXSspKFsoW10pPyl8KCxcXHMqKXwoPzooXFwoKVxccyopP1xcXFw/KD86KCcpfChcIikpfCg/OlxccyooKFspXFxdXSkoPz1bLl5dfFxccyokfFteKFtdKXxbKVxcXV0pKFsoW10/KSl8KFxccyspL2csXG5cdC8vbGZ0UHJuMCAgICAgICAgICAgbGZ0UHJuICAgICAgICAgYm91bmQgICAgIHBhdGggICAgICAgICAgICAgICBvcGVyYXRvciAgICAgZXJyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXEgICAgICBwYXRoMiBsYXRlICAgICAgICAgICAgcHJuICAgICAgY29tbWEgIGxmdFBybjIgICAgICAgICAgYXBvcyBxdW90ICAgICAgICBydFBybiAgcnRQcm5Eb3QgICAgICAgICAgICAgICAgICBwcm4yICAgICBzcGFjZVxuXHQvLyAobGVmdCBwYXJlbj8gZm9sbG93ZWQgYnkgKHBhdGg/IGZvbGxvd2VkIGJ5IG9wZXJhdG9yKSBvciAocGF0aCBmb2xsb3dlZCBieSBwYXJl
bj8pKSBvciBjb21tYSBvciBhcG9zIG9yIHF1b3Qgb3IgcmlnaHQgcGFyZW4gb3Igc3BhY2VcblxuXHRcdGZ1bmN0aW9uIHBhcnNlUGF0aChhbGxQYXRoLCBub3QsIG9iamVjdCwgaGVscGVyLCB2aWV3LCB2aWV3UHJvcGVydHksIHBhdGhUb2tlbnMsIGxlYWZUb2tlbikge1xuXHRcdFx0Ly8gL14oISo/KSg/Om51bGx8dHJ1ZXxmYWxzZXxcXGRbXFxkLl0qfChbXFx3JF0rfFxcLnx+KFtcXHckXSspfCModmlld3woW1xcdyRdKykpPykoW1xcdyQuXl0qPykoPzpbLlteXShbXFx3JF0rKVxcXT8pPykkL2csXG5cdFx0XHQvLyAgICBub3QgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb2JqZWN0ICAgICBoZWxwZXIgICAgdmlldyAgdmlld1Byb3BlcnR5IHBhdGhUb2tlbnMgICAgICBsZWFmVG9rZW5cblx0XHRcdHN1YlBhdGggPSBvYmplY3QgPT09IFwiLlwiO1xuXHRcdFx0aWYgKG9iamVjdCkge1xuXHRcdFx0XHRwYXRoID0gcGF0aC5zbGljZShub3QubGVuZ3RoKTtcblx0XHRcdFx0aWYgKC9eXFwuP2NvbnN0cnVjdG9yJC8udGVzdChsZWFmVG9rZW58fHBhdGgpKSB7XG5cdFx0XHRcdFx0c3ludGF4RXJyb3IoYWxsUGF0aCk7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKCFzdWJQYXRoKSB7XG5cdFx0XHRcdFx0YWxsUGF0aCA9IChsYXRlIC8vIGxhdGUgcGF0aCBAYS5iLmM6IG5vdCB0aHJvdyBvbiAncHJvcGVydHkgb2YgdW5kZWZpbmVkJyBpZiBhIHVuZGVmaW5lZCwgYW5kIHdpbGwgdXNlIF9nZXRPYigpIGFmdGVyIGxpbmtpbmcgdG8gcmVzb2x
2ZSBsYXRlLlxuXHRcdFx0XHRcdFx0XHQ/IChpc0xpbmtFeHByID8gJycgOiAnKGx0T2IubHQ9bHRPYi5sdHx8JykgKyAnKG9iPSdcblx0XHRcdFx0XHRcdFx0OiBcIlwiXG5cdFx0XHRcdFx0XHQpXG5cdFx0XHRcdFx0XHQrIChoZWxwZXJcblx0XHRcdFx0XHRcdFx0PyAndmlldy5jdHhQcm0oXCInICsgaGVscGVyICsgJ1wiKSdcblx0XHRcdFx0XHRcdFx0OiB2aWV3XG5cdFx0XHRcdFx0XHRcdFx0PyBcInZpZXdcIlxuXHRcdFx0XHRcdFx0XHRcdDogXCJkYXRhXCIpXG5cdFx0XHRcdFx0XHQrIChsYXRlXG5cdFx0XHRcdFx0XHRcdD8gJyk9PT11bmRlZmluZWQnICsgKGlzTGlua0V4cHIgPyAnJyA6ICcpJykgKyAnP1wiXCI6dmlldy5fZ2V0T2Iob2IsXCInXG5cdFx0XHRcdFx0XHRcdDogXCJcIlxuXHRcdFx0XHRcdFx0KVxuXHRcdFx0XHRcdFx0KyAobGVhZlRva2VuXG5cdFx0XHRcdFx0XHRcdD8gKHZpZXdQcm9wZXJ0eVxuXHRcdFx0XHRcdFx0XHRcdD8gXCIuXCIgKyB2aWV3UHJvcGVydHlcblx0XHRcdFx0XHRcdFx0XHQ6IGhlbHBlclxuXHRcdFx0XHRcdFx0XHRcdFx0PyBcIlwiXG5cdFx0XHRcdFx0XHRcdFx0XHQ6ICh2aWV3ID8gXCJcIiA6IFwiLlwiICsgb2JqZWN0KVxuXHRcdFx0XHRcdFx0XHRcdCkgKyAocGF0aFRva2VucyB8fCBcIlwiKVxuXHRcdFx0XHRcdFx0XHQ6IChsZWFmVG9rZW4gPSBoZWxwZXIgPyBcIlwiIDogdmlldyA/IHZpZXdQcm9wZXJ0eSB8fCBcIlwiIDogb2JqZWN0LCBcIlwiKSk7XG5cdFx0XHRcdFx0YWxsUGF0aCA9IG
FsbFBhdGggKyAobGVhZlRva2VuID8gXCIuXCIgKyBsZWFmVG9rZW4gOiBcIlwiKTtcblxuXHRcdFx0XHRcdGFsbFBhdGggPSBub3QgKyAoYWxsUGF0aC5zbGljZSgwLCA5KSA9PT0gXCJ2aWV3LmRhdGFcIlxuXHRcdFx0XHRcdFx0PyBhbGxQYXRoLnNsaWNlKDUpIC8vIGNvbnZlcnQgI3ZpZXcuZGF0YS4uLiB0byBkYXRhLi4uXG5cdFx0XHRcdFx0XHQ6IGFsbFBhdGgpXG5cdFx0XHRcdFx0KyAobGF0ZVxuXHRcdFx0XHRcdFx0XHQ/IChpc0xpbmtFeHByID8gJ1wiJzogJ1wiLGx0T2InKSArIChwcm4gPyAnLDEpJzonKScpXG5cdFx0XHRcdFx0XHRcdDogXCJcIlxuXHRcdFx0XHRcdFx0KTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoYmluZGluZ3MpIHtcblx0XHRcdFx0XHRiaW5kcyA9IG5hbWVkID09PSBcIl9saW5rVG9cIiA/IChiaW5kdG8gPSBwYXRoQmluZGluZ3MuX2pzdnRvID0gcGF0aEJpbmRpbmdzLl9qc3Z0byB8fCBbXSkgOiBibmRDdHguYmQ7XG5cdFx0XHRcdFx0aWYgKHRoZU9iID0gc3ViUGF0aCAmJiBiaW5kc1tiaW5kcy5sZW5ndGgtMV0pIHtcblx0XHRcdFx0XHRcdGlmICh0aGVPYi5fY3BmbikgeyAvLyBDb21wdXRlZCBwcm9wZXJ0eSBleHByT2Jcblx0XHRcdFx0XHRcdFx0d2hpbGUgKHRoZU9iLnNiKSB7XG5cdFx0XHRcdFx0XHRcdFx0dGhlT2IgPSB0aGVPYi5zYjtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0XHRpZiAodGhlT2IucHJtKSB7XG5cdFx0XHRcdFx0XHRcdFx0aWYgKHRoZU9iLmJuZCkge1xuXHRcdFx0XHRcd
Fx0XHRcdFx0cGF0aCA9IFwiXlwiICsgcGF0aC5zbGljZSgxKTtcblx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdFx0dGhlT2Iuc2IgPSBwYXRoO1xuXHRcdFx0XHRcdFx0XHRcdHRoZU9iLmJuZCA9IHRoZU9iLmJuZCB8fCBwYXRoWzBdID09PSBcIl5cIjtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRiaW5kcy5wdXNoKHBhdGgpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRpZiAocHJuICYmICFzdWJQYXRoKSB7XG5cdFx0XHRcdFx0XHRwYXRoU3RhcnRbZm5EcF0gPSBpbmQ7XG5cdFx0XHRcdFx0XHRjb21waWxlZFBhdGhTdGFydFtmbkRwXSA9IGNvbXBpbGVkUGF0aFtmbkRwXS5sZW5ndGg7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gYWxsUGF0aDtcblx0XHR9XG5cblx0XHQvL2JvdW5kID0gYmluZGluZ3MgJiYgYm91bmQ7XG5cdFx0aWYgKGJvdW5kICYmICFlcSkge1xuXHRcdFx0cGF0aCA9IGJvdW5kICsgcGF0aDsgLy8gZS5nLiBzb21lLmZuKC4uLilec29tZS5wYXRoIC0gc28gaGVyZSBwYXRoIGlzIFwiXnNvbWUucGF0aFwiXG5cdFx0fVxuXHRcdG9wZXJhdG9yID0gb3BlcmF0b3IgfHwgXCJcIjtcblx0XHRsZnRQcm4yID0gbGZ0UHJuMiB8fCBcIlwiO1xuXHRcdGxmdFBybiA9IGxmdFBybiB8fCBsZnRQcm4wIHx8IGxmdFBybjI7XG5cdFx0cGF0aCA9IHBhdGggfHwgcGF0aDI7XG5cblx0XHRpZiAobGF0ZSAmJiAobGF0
ZSA9ICEvXFwpfF0vLnRlc3QoZnVsbFtpbmRleC0xXSkpKSB7XG5cdFx0XHRwYXRoID0gcGF0aC5zbGljZSgxKS5zcGxpdChcIi5cIikuam9pbihcIl5cIik7IC8vIExhdGUgcGF0aCBAei5iLmMuIFVzZSBcIl5cIiByYXRoZXIgdGhhbiBcIi5cIiB0byBlbnN1cmUgdGhhdCBkZWVwIGJpbmRpbmcgd2lsbCBiZSB1c2VkXG5cdFx0fVxuXHRcdC8vIENvdWxkIGRvIHRoaXMgLSBidXQgbm90IHdvcnRoIHBlcmYgY29zdD8/IDotXG5cdFx0Ly8gaWYgKCFwYXRoLmxhc3RJbmRleE9mKFwiI2RhdGEuXCIsIDApKSB7IHBhdGggPSBwYXRoLnNsaWNlKDYpOyB9IC8vIElmIHBhdGggc3RhcnRzIHdpdGggXCIjZGF0YS5cIiwgcmVtb3ZlIHRoYXQuXG5cdFx0cHJuID0gcHJuIHx8IHBybjIgfHwgXCJcIjtcblx0XHR2YXIgZXhwciwgYmluZHMsIHRoZU9iLCBuZXdPYiwgc3ViUGF0aCwgbGZ0UHJuRkNhbGwsIHJldCxcblx0XHRcdGluZCA9IGluZGV4O1xuXG5cdFx0aWYgKCFhcG9zZWQgJiYgIXF1b3RlZCkge1xuXHRcdFx0aWYgKGVycikge1xuXHRcdFx0XHRzeW50YXhFcnJvcihwYXJhbXMpO1xuXHRcdFx0fVxuXHRcdFx0aWYgKHJ0UHJuRG90ICYmIGJpbmRpbmdzKSB7XG5cdFx0XHRcdC8vIFRoaXMgaXMgYSBiaW5kaW5nIHRvIGEgcGF0aCBpbiB3aGljaCBhbiBvYmplY3QgaXMgcmV0dXJuZWQgYnkgYSBoZWxwZXIvZGF0YSBmdW5jdGlvbi9leHByZXNzaW9uLCBlLmcuIGZvbygpXngueSBvciAoYT9iOmMpXngueVxuXHRcdFx0XHQvLyBXZSBjcmVhdGUgYSBjb21
waWxlZCBmdW5jdGlvbiB0byBnZXQgdGhlIG9iamVjdCBpbnN0YW5jZSAod2hpY2ggd2lsbCBiZSBjYWxsZWQgd2hlbiB0aGUgZGVwZW5kZW50IGRhdGEgb2YgdGhlIHN1YmV4cHJlc3Npb24gY2hhbmdlcywgdG8gcmV0dXJuIHRoZSBuZXcgb2JqZWN0LCBhbmQgdHJpZ2dlciByZS1iaW5kaW5nIG9mIHRoZSBzdWJzZXF1ZW50IHBhdGgpXG5cdFx0XHRcdGV4cHIgPSBwYXRoU3RhcnRbZm5EcC0xXTtcblx0XHRcdFx0aWYgKGZ1bGwubGVuZ3RoIC0gMSA+IGluZCAtIChleHByIHx8IDApKSB7IC8vIFdlIG5lZWQgdG8gY29tcGlsZSBhIHN1YmV4cHJlc3Npb25cblx0XHRcdFx0XHRleHByID0gJC50cmltKGZ1bGwuc2xpY2UoZXhwciwgaW5kICsgYWxsLmxlbmd0aCkpO1xuXHRcdFx0XHRcdGJpbmRzID0gYmluZHRvIHx8IGJuZFN0YWNrW2ZuRHAtMV0uYmQ7XG5cdFx0XHRcdFx0Ly8gSW5zZXJ0IGV4cHJPYiBvYmplY3QsIHRvIGJlIHVzZWQgZHVyaW5nIGJpbmRpbmcgdG8gcmV0dXJuIHRoZSBjb21wdXRlZCBvYmplY3Rcblx0XHRcdFx0XHR0aGVPYiA9IGJpbmRzW2JpbmRzLmxlbmd0aC0xXTtcblx0XHRcdFx0XHRpZiAodGhlT2IgJiYgdGhlT2IucHJtKSB7XG5cdFx0XHRcdFx0XHR3aGlsZSAodGhlT2Iuc2IgJiYgdGhlT2Iuc2IucHJtKSB7XG5cdFx0XHRcdFx0XHRcdHRoZU9iID0gdGhlT2Iuc2I7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRuZXdPYiA9IHRoZU9iLnNiID0ge3BhdGg6IHRoZU9iLnNiLCBibmQ6IHRoZU9iLmJuZH07XG5cdFx0XH
RcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdGJpbmRzLnB1c2gobmV3T2IgPSB7cGF0aDogYmluZHMucG9wKCl9KTsgLy8gSW5zZXJ0IGV4cHJPYiBvYmplY3QsIHRvIGJlIHVzZWQgZHVyaW5nIGJpbmRpbmcgdG8gcmV0dXJuIHRoZSBjb21wdXRlZCBvYmplY3Rcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0aWYgKHRoZU9iICYmIHRoZU9iLnNiID09PSBuZXdPYikge1xuXHRcdFx0XHRcdFx0Y29tcGlsZWRQYXRoW2ZuRHBdID0gY29tcGlsZWRQYXRoW2ZuRHAtMV0uc2xpY2UodGhlT2IuX2NwUHRoU3QpICsgY29tcGlsZWRQYXRoW2ZuRHBdO1xuXHRcdFx0XHRcdFx0Y29tcGlsZWRQYXRoW2ZuRHAtMV0gPSBjb21waWxlZFBhdGhbZm5EcC0xXS5zbGljZSgwLCB0aGVPYi5fY3BQdGhTdCk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdG5ld09iLl9jcFB0aFN0ID0gY29tcGlsZWRQYXRoU3RhcnRbZm5EcC0xXTtcblx0XHRcdFx0XHRuZXdPYi5fY3BLZXkgPSBleHByO1xuXG5cdFx0XHRcdFx0Y29tcGlsZWRQYXRoW2ZuRHBdICs9IGZ1bGwuc2xpY2UocHJldkluZGV4LCBpbmRleCk7XG5cdFx0XHRcdFx0cHJldkluZGV4ID0gaW5kZXg7XG5cblx0XHRcdFx0XHRuZXdPYi5fY3BmbiA9IGNwRm5TdG9yZVtleHByXSA9IGNwRm5TdG9yZVtleHByXSB8fCAvLyBDb21waWxlZCBmdW5jdGlvbiBmb3IgY29tcHV0ZWQgdmFsdWU6IGdldCBmcm9tIHN0b3JlLCBvciBjb21waWxlIGFuZCBzdG9yZVxuXHRcdFx0XHRcdFx0bmV3IEZ1bmN0aW9uKFwiZGF0YSx2aWV3LGpcI
iwgLy8gQ29tcGlsZWQgZnVuY3Rpb24gZm9yIGNvbXB1dGVkIHZhbHVlIGluIHRlbXBsYXRlXG5cdFx0XHRcdFx0XCIvL1wiICsgZXhwciArIFwiXFxudmFyIHY7XFxucmV0dXJuICgodj1cIiArIGNvbXBpbGVkUGF0aFtmbkRwXSArIChydFBybiA9PT0gXCJdXCIgPyBcIildXCIgOiBydFBybikgKyBcIikhPW51bGw/djpudWxsKTtcIik7XG5cblx0XHRcdFx0XHRjb21waWxlZFBhdGhbZm5EcC0xXSArPSAoZm5DYWxsW3BybkRwXSAmJiAkc3ViU2V0dGluZ3NBZHZhbmNlZC5jYWNoZSA/IFwidmlldy5nZXRDYWNoZShcXFwiXCIgKyBleHByLnJlcGxhY2UockVzY2FwZVF1b3RlcywgXCJcXFxcJCZcIikgKyBcIlxcXCJcIiA6IGNvbXBpbGVkUGF0aFtmbkRwXSk7XG5cblx0XHRcdFx0XHRuZXdPYi5wcm0gPSBibmRDdHguYmQ7XG5cdFx0XHRcdFx0bmV3T2IuYm5kID0gbmV3T2IuYm5kIHx8IG5ld09iLnBhdGggJiYgbmV3T2IucGF0aC5pbmRleE9mKFwiXlwiKSA+PSAwO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGNvbXBpbGVkUGF0aFtmbkRwXSA9IFwiXCI7XG5cdFx0XHR9XG5cdFx0XHRpZiAocHJuID09PSBcIltcIikge1xuXHRcdFx0XHRwcm4gPSBcIltqLl9zcShcIjtcblx0XHRcdH1cblx0XHRcdGlmIChsZnRQcm4gPT09IFwiW1wiKSB7XG5cdFx0XHRcdGxmdFBybiA9IFwiW2ouX3NxKFwiO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXQgPSAoYXBvc2VkXG5cdFx0XHQvLyB3aXRoaW4gc2luZ2xlLXF1b3RlZCBzdHJpbmdcblx0XHRcdD8gKGFwb3NlZCA9
ICFhcG9zLCAoYXBvc2VkID8gYWxsIDogbGZ0UHJuMiArICdcIicpKVxuXHRcdFx0OiBxdW90ZWRcblx0XHRcdC8vIHdpdGhpbiBkb3VibGUtcXVvdGVkIHN0cmluZ1xuXHRcdFx0XHQ/IChxdW90ZWQgPSAhcXVvdCwgKHF1b3RlZCA/IGFsbCA6IGxmdFBybjIgKyAnXCInKSlcblx0XHRcdFx0OlxuXHRcdFx0KFxuXHRcdFx0XHQobGZ0UHJuXG5cdFx0XHRcdFx0PyAoXG5cdFx0XHRcdFx0XHRwcm5TdGFja1srK3BybkRwXSA9IHRydWUsXG5cdFx0XHRcdFx0XHRwcm5JbmRbcHJuRHBdID0gMCxcblx0XHRcdFx0XHRcdGJpbmRpbmdzICYmIChcblx0XHRcdFx0XHRcdFx0cGF0aFN0YXJ0W2ZuRHArK10gPSBpbmQrKyxcblx0XHRcdFx0XHRcdFx0Ym5kQ3R4ID0gYm5kU3RhY2tbZm5EcF0gPSB7YmQ6IFtdfSxcblx0XHRcdFx0XHRcdFx0Y29tcGlsZWRQYXRoW2ZuRHBdID0gXCJcIixcblx0XHRcdFx0XHRcdFx0Y29tcGlsZWRQYXRoU3RhcnRbZm5EcF0gPSAxXG5cdFx0XHRcdFx0XHQpLFxuXHRcdFx0XHRcdFx0bGZ0UHJuKSAvLyBMZWZ0IHBhcmVuLCAobm90IGEgZnVuY3Rpb24gY2FsbCBwYXJlbilcblx0XHRcdFx0XHQ6IFwiXCIpXG5cdFx0XHRcdCsgKHNwYWNlXG5cdFx0XHRcdFx0PyAocHJuRHBcblx0XHRcdFx0XHRcdD8gXCJcIiAvLyBBIHNwYWNlIHdpdGhpbiBwYXJlbnMgb3Igd2l0aGluIGZ1bmN0aW9uIGNhbGwgcGFyZW5zLCBzbyBub3QgYSBzZXBhcmF0b3IgZm9yIHRhZyBhcmdzXG5cdFx0XHQvLyBOZXcgYXJnIG9yIHByb3AgLSBzbyBpbnNlcnQ
gYmFja3NwYWNlIFxcYiAoXFx4MDgpIGFzIHNlcGFyYXRvciBmb3IgbmFtZWQgcGFyYW1zLCB1c2VkIHN1YnNlcXVlbnRseSBieSByQnVpbGRIYXNoLCBhbmQgcHJlcGFyZSBuZXcgYmluZGluZ3MgYXJyYXlcblx0XHRcdFx0XHRcdDogKHBhcmFtSW5kZXggPSBmdWxsLnNsaWNlKHBhcmFtSW5kZXgsIGluZCksIG5hbWVkXG5cdFx0XHRcdFx0XHRcdD8gKG5hbWVkID0gYm91bmROYW1lID0gYmluZHRvID0gZmFsc2UsIFwiXFxiXCIpXG5cdFx0XHRcdFx0XHRcdDogXCJcXGIsXCIpICsgcGFyYW1JbmRleCArIChwYXJhbUluZGV4ID0gaW5kICsgYWxsLmxlbmd0aCwgYmluZGluZ3MgJiYgcGF0aEJpbmRpbmdzLnB1c2goYm5kQ3R4LmJkID0gW10pLCBcIlxcYlwiKVxuXHRcdFx0XHRcdClcblx0XHRcdFx0XHQ6IGVxXG5cdFx0XHQvLyBuYW1lZCBwYXJhbS4gUmVtb3ZlIGJpbmRpbmdzIGZvciBhcmcgYW5kIGNyZWF0ZSBpbnN0ZWFkIGJpbmRpbmdzIGFycmF5IGZvciBwcm9wXG5cdFx0XHRcdFx0XHQ/IChmbkRwICYmIHN5bnRheEVycm9yKHBhcmFtcyksIGJpbmRpbmdzICYmIHBhdGhCaW5kaW5ncy5wb3AoKSwgbmFtZWQgPSBcIl9cIiArIHBhdGgsIGJvdW5kTmFtZSA9IGJvdW5kLCBwYXJhbUluZGV4ID0gaW5kICsgYWxsLmxlbmd0aCxcblx0XHRcdFx0XHRcdFx0XHRiaW5kaW5ncyAmJiAoKGJpbmRpbmdzID0gYm5kQ3R4LmJkID0gcGF0aEJpbmRpbmdzW25hbWVkXSA9IFtdKSwgYmluZGluZ3Muc2twID0gIWJvdW5kKSwgcGF0aCArICc6Jylcblx0XHRcdF
x0XHRcdDogcGF0aFxuXHRcdFx0Ly8gcGF0aFxuXHRcdFx0XHRcdFx0XHQ/IChwYXRoLnNwbGl0KFwiXlwiKS5qb2luKFwiLlwiKS5yZXBsYWNlKCRzdWIuclBhdGgsIHBhcnNlUGF0aClcblx0XHRcdFx0XHRcdFx0XHQrIChwcm4gfHwgb3BlcmF0b3IpXG5cdFx0XHRcdFx0XHRcdClcblx0XHRcdFx0XHRcdFx0OiBvcGVyYXRvclxuXHRcdFx0Ly8gb3BlcmF0b3Jcblx0XHRcdFx0XHRcdFx0XHQ/IG9wZXJhdG9yXG5cdFx0XHRcdFx0XHRcdFx0OiBydFByblxuXHRcdFx0Ly8gZnVuY3Rpb25cblx0XHRcdFx0XHRcdFx0XHRcdD8gcnRQcm4gPT09IFwiXVwiID8gXCIpXVwiIDogXCIpXCJcblx0XHRcdFx0XHRcdFx0XHRcdDogY29tbWFcblx0XHRcdFx0XHRcdFx0XHRcdFx0PyAoZm5DYWxsW3BybkRwXSB8fCBzeW50YXhFcnJvcihwYXJhbXMpLCBcIixcIikgLy8gV2UgZG9uJ3QgYWxsb3cgdG9wLWxldmVsIGxpdGVyYWwgYXJyYXlzIG9yIG9iamVjdHNcblx0XHRcdFx0XHRcdFx0XHRcdFx0OiBsZnRQcm4wXG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0PyBcIlwiXG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0OiAoYXBvc2VkID0gYXBvcywgcXVvdGVkID0gcXVvdCwgJ1wiJylcblx0XHRcdCkpXG5cdFx0KTtcblxuXHRcdGlmICghYXBvc2VkICYmICFxdW90ZWQpIHtcblx0XHRcdGlmIChydFBybikge1xuXHRcdFx0XHRmbkNhbGxbcHJuRHBdID0gZmFsc2U7XG5cdFx0XHRcdHBybkRwLS07XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKGJpbmRpbmdzKSB7XG5cd
Fx0XHRpZiAoIWFwb3NlZCAmJiAhcXVvdGVkKSB7XG5cdFx0XHRcdGlmIChydFBybikge1xuXHRcdFx0XHRcdGlmIChwcm5TdGFja1twcm5EcCsxXSkge1xuXHRcdFx0XHRcdFx0Ym5kQ3R4ID0gYm5kU3RhY2tbLS1mbkRwXTtcblx0XHRcdFx0XHRcdHByblN0YWNrW3BybkRwKzFdID0gZmFsc2U7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdHByblN0YXJ0ID0gcHJuSW5kW3BybkRwKzFdO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmIChwcm4pIHtcblx0XHRcdFx0XHRwcm5JbmRbcHJuRHArMV0gPSBjb21waWxlZFBhdGhbZm5EcF0ubGVuZ3RoICsgKGxmdFBybiA/IDEgOiAwKTtcblx0XHRcdFx0XHRpZiAocGF0aCB8fCBydFBybikge1xuXHRcdFx0XHRcdFx0Ym5kQ3R4ID0gYm5kU3RhY2tbKytmbkRwXSA9IHtiZDogW119O1xuXHRcdFx0XHRcdFx0cHJuU3RhY2tbcHJuRHArMV0gPSB0cnVlO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHRjb21waWxlZFBhdGhbZm5EcF0gPSAoY29tcGlsZWRQYXRoW2ZuRHBdfHxcIlwiKSArIGZ1bGwuc2xpY2UocHJldkluZGV4LCBpbmRleCk7XG5cdFx0XHRwcmV2SW5kZXggPSBpbmRleCthbGwubGVuZ3RoO1xuXG5cdFx0XHRpZiAoIWFwb3NlZCAmJiAhcXVvdGVkKSB7XG5cdFx0XHRcdGlmIChsZnRQcm5GQ2FsbCA9IGxmdFBybiAmJiBwcm5TdGFja1twcm5EcCsxXSkge1xuXHRcdFx0XHRcdGNvbXBpbGVkUGF0aFtmbkRwLTFdICs9IGxmdFBybjtcblx0XHRcdFx0XHRjb21waWxlZFBhdGhT
dGFydFtmbkRwLTFdKys7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKHBybiA9PT0gXCIoXCIgJiYgc3ViUGF0aCAmJiAhbmV3T2IpIHtcblx0XHRcdFx0XHRjb21waWxlZFBhdGhbZm5EcF0gPSBjb21waWxlZFBhdGhbZm5EcC0xXS5zbGljZShwcm5TdGFydCkgKyBjb21waWxlZFBhdGhbZm5EcF07XG5cdFx0XHRcdFx0Y29tcGlsZWRQYXRoW2ZuRHAtMV0gPSBjb21waWxlZFBhdGhbZm5EcC0xXS5zbGljZSgwLCBwcm5TdGFydCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGNvbXBpbGVkUGF0aFtmbkRwXSArPSBsZnRQcm5GQ2FsbCA/IHJldC5zbGljZSgxKSA6IHJldDtcblx0XHR9XG5cblx0XHRpZiAoIWFwb3NlZCAmJiAhcXVvdGVkICYmIHBybikge1xuXHRcdFx0cHJuRHArKztcblx0XHRcdGlmIChwYXRoICYmIHBybiA9PT0gXCIoXCIpIHtcblx0XHRcdFx0Zm5DYWxsW3BybkRwXSA9IHRydWU7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYgKCFhcG9zZWQgJiYgIXF1b3RlZCAmJiBwcm4yKSB7XG5cdFx0XHRpZiAoYmluZGluZ3MpIHtcblx0XHRcdFx0Y29tcGlsZWRQYXRoW2ZuRHBdICs9IHBybjtcblx0XHRcdH1cblx0XHRcdHJldCArPSBwcm47XG5cdFx0fVxuXHRcdHJldHVybiByZXQ7XG5cdH1cblxuXHR2YXIgbmFtZWQsIGJpbmR0bywgYm91bmROYW1lLCByZXN1bHQsXG5cdFx0cXVvdGVkLCAvLyBib29sZWFuIGZvciBzdHJpbmcgY29udGVudCBpbiBkb3VibGUgcXVvdGVzXG5cdFx0YXBvc2VkLCAvLyBvciBpbiBzaW5nbGUgcXVvdGV
zXG5cdFx0YmluZGluZ3MgPSBwYXRoQmluZGluZ3MgJiYgcGF0aEJpbmRpbmdzWzBdLCAvLyBiaW5kaW5ncyBhcnJheSBmb3IgdGhlIGZpcnN0IGFyZ1xuXHRcdGJuZEN0eCA9IHtiZDogYmluZGluZ3N9LFxuXHRcdGJuZFN0YWNrID0gezA6IGJuZEN0eH0sXG5cdFx0cGFyYW1JbmRleCA9IDAsIC8vIGxpc3QsXG5cdFx0Ly8gVGhlIGZvbGxvd2luZyBhcmUgdXNlZCBmb3IgdHJhY2tpbmcgcGF0aCBwYXJzaW5nIGluY2x1ZGluZyBuZXN0ZWQgcGF0aHMsIHN1Y2ggYXMgXCJhLmIoY15kICsgKGUpKV5mXCIsIGFuZCBjaGFpbmVkIGNvbXB1dGVkIHBhdGhzIHN1Y2ggYXNcblx0XHQvLyBcImEuYigpLmNeZCgpLmUuZigpLmdcIiAtIHdoaWNoIGhhcyBmb3VyIGNoYWluZWQgcGF0aHMsIFwiYS5iKClcIiwgXCJeYy5kKClcIiwgXCIuZS5mKClcIiBhbmQgXCIuZ1wiXG5cdFx0cHJuRHAgPSAwLCAgICAgLy8gRm9yIHRyYWNraW5nIHBhcmVuIGRlcHRoIChub3QgZnVuY3Rpb24gY2FsbCBwYXJlbnMpXG5cdFx0Zm5EcCA9IDAsICAgICAgLy8gRm9yIHRyYWNraW5nIGRlcHRoIG9mIGZ1bmN0aW9uIGNhbGwgcGFyZW5zXG5cdFx0cHJuSW5kID0ge30sICAgLy8gV2UgYXJlIGluIGEgZnVuY3Rpb24gY2FsbFxuXHRcdHByblN0YXJ0ID0gMCwgIC8vIHRyYWNrcyB0aGUgc3RhcnQgb2YgdGhlIGN1cnJlbnQgcGF0aCBzdWNoIGFzIGNeZCgpIGluIHRoZSBhYm92ZSBleGFtcGxlXG5cdFx0cHJuU3RhY2sgPSB7fSwgLy8gdHJhY2tzIHBhcmVucyB3aGljaCBhcmUgbm90IG
Z1bmN0aW9uIGNhbGxzLCBhbmQgc28gYXJlIGFzc29jaWF0ZWQgd2l0aCBuZXcgYm5kU3RhY2sgY29udGV4dHNcblx0XHRmbkNhbGwgPSB7fSwgICAvLyBXZSBhcmUgaW4gYSBmdW5jdGlvbiBjYWxsXG5cdFx0cGF0aFN0YXJ0ID0ge30sLy8gdHJhY2tzIHRoZSBzdGFydCBvZiB0aGUgY3VycmVudCBwYXRoIHN1Y2ggYXMgY15kKCkgaW4gdGhlIGFib3ZlIGV4YW1wbGVcblx0XHRjb21waWxlZFBhdGhTdGFydCA9IHswOiAwfSxcblx0XHRjb21waWxlZFBhdGggPSB7MDpcIlwifSxcblx0XHRwcmV2SW5kZXggPSAwO1xuXG5cdGlmIChwYXJhbXNbMF0gPT09IFwiQFwiKSB7XG5cdFx0cGFyYW1zID0gcGFyYW1zLnJlcGxhY2UockJyYWNrZXRRdW90ZSwgXCIuXCIpO1xuXHR9XG5cdHJlc3VsdCA9IChwYXJhbXMgKyAodG1wbCA/IFwiIFwiIDogXCJcIikpLnJlcGxhY2UoJHN1Yi5yUHJtLCBwYXJzZVRva2Vucyk7XG5cblx0aWYgKGJpbmRpbmdzKSB7XG5cdFx0cmVzdWx0ID0gY29tcGlsZWRQYXRoWzBdO1xuXHR9XG5cblx0cmV0dXJuICFwcm5EcCAmJiByZXN1bHQgfHwgc3ludGF4RXJyb3IocGFyYW1zKTsgLy8gU3ludGF4IGVycm9yIGlmIHVuYmFsYW5jZWQgcGFyZW5zIGluIHBhcmFtcyBleHByZXNzaW9uXG59XG5cbmZ1bmN0aW9uIGJ1aWxkQ29kZShhc3QsIHRtcGwsIGlzTGlua0V4cHIpIHtcblx0Ly8gQnVpbGQgdGhlIHRlbXBsYXRlIGZ1bmN0aW9uIGNvZGUgZnJvbSB0aGUgQVNUIG5vZGVzLCBhbmQgc2V0IGFzIHByb3BlcnR5IG9uIHRoZSBwYXNzZ
WQtaW4gdGVtcGxhdGUgb2JqZWN0XG5cdC8vIFVzZWQgZm9yIGNvbXBpbGluZyB0ZW1wbGF0ZXMsIGFuZCBhbHNvIGJ5IEpzVmlld3MgdG8gYnVpbGQgZnVuY3Rpb25zIGZvciBkYXRhIGxpbmsgZXhwcmVzc2lvbnNcblx0dmFyIGksIG5vZGUsIHRhZ05hbWUsIGNvbnZlcnRlciwgdGFnQ3R4LCBoYXNUYWcsIGhhc0VuY29kZXIsIGdldHNWYWwsIGhhc0NudnQsIHVzZUNudnQsIHRtcGxCaW5kaW5ncywgcGF0aEJpbmRpbmdzLCBwYXJhbXMsIGJvdW5kT25FcnJTdGFydCxcblx0XHRib3VuZE9uRXJyRW5kLCB0YWdSZW5kZXIsIG5lc3RlZFRtcGxzLCB0bXBsTmFtZSwgbmVzdGVkVG1wbCwgdGFnQW5kRWxzZXMsIGNvbnRlbnQsIG1hcmt1cCwgbmV4dElzRWxzZSwgb2xkQ29kZSwgaXNFbHNlLCBpc0dldFZhbCwgdGFnQ3R4Rm4sXG5cdFx0b25FcnJvciwgdGFnU3RhcnQsIHRyaWdnZXIsIGxhdGVSZW5kZXIsIHJldFN0ck9wZW4sIHJldFN0ckNsb3NlLFxuXHRcdHRtcGxCaW5kaW5nS2V5ID0gMCxcblx0XHR1c2VWaWV3cyA9ICRzdWJTZXR0aW5nc0FkdmFuY2VkLnVzZVZpZXdzIHx8IHRtcGwudXNlVmlld3MgfHwgdG1wbC50YWdzIHx8IHRtcGwudGVtcGxhdGVzIHx8IHRtcGwuaGVscGVycyB8fCB0bXBsLmNvbnZlcnRlcnMsXG5cdFx0Y29kZSA9IFwiXCIsXG5cdFx0dG1wbE9wdGlvbnMgPSB7fSxcblx0XHRsID0gYXN0Lmxlbmd0aDtcblxuXHRpZiAoXCJcIiArIHRtcGwgPT09IHRtcGwpIHtcblx0XHR0bXBsTmFtZSA9IGlzTGlua0V4cHIgPyAnZGF0YS1s
aW5rPVwiJyArIHRtcGwucmVwbGFjZShyTmV3TGluZSwgXCIgXCIpLnNsaWNlKDEsIC0xKSArICdcIicgOiB0bXBsO1xuXHRcdHRtcGwgPSAwO1xuXHR9IGVsc2Uge1xuXHRcdHRtcGxOYW1lID0gdG1wbC50bXBsTmFtZSB8fCBcInVubmFtZWRcIjtcblx0XHRpZiAodG1wbC5hbGxvd0NvZGUpIHtcblx0XHRcdHRtcGxPcHRpb25zLmFsbG93Q29kZSA9IHRydWU7XG5cdFx0fVxuXHRcdGlmICh0bXBsLmRlYnVnKSB7XG5cdFx0XHR0bXBsT3B0aW9ucy5kZWJ1ZyA9IHRydWU7XG5cdFx0fVxuXHRcdHRtcGxCaW5kaW5ncyA9IHRtcGwuYm5kcztcblx0XHRuZXN0ZWRUbXBscyA9IHRtcGwudG1wbHM7XG5cdH1cblx0Zm9yIChpID0gMDsgaSA8IGw7IGkrKykge1xuXHRcdC8vIEFTVCBub2RlczogWzA6IHRhZ05hbWUsIDE6IGNvbnZlcnRlciwgMjogY29udGVudCwgMzogcGFyYW1zLCA0OiBjb2RlLCA1OiBvbkVycm9yLCA2OiB0cmlnZ2VyLCA3OnBhdGhCaW5kaW5ncywgODogY29udGVudE1hcmt1cF1cblx0XHRub2RlID0gYXN0W2ldO1xuXG5cdFx0Ly8gQWRkIG5ld2xpbmUgZm9yIGVhY2ggY2FsbG91dCB0byB0KCkgYygpIGV0Yy4gYW5kIGVhY2ggbWFya3VwIHN0cmluZ1xuXHRcdGlmIChcIlwiICsgbm9kZSA9PT0gbm9kZSkge1xuXHRcdFx0Ly8gYSBtYXJrdXAgc3RyaW5nIHRvIGJlIGluc2VydGVkXG5cdFx0XHRjb2RlICs9ICcrXCInICsgbm9kZSArICdcIic7XG5cdFx0fSBlbHNlIHtcblx0XHRcdC8vIGEgY29tcGlsZWQgdGFnIGV4cHJlc3Npb24gdG8
gYmUgaW5zZXJ0ZWRcblx0XHRcdHRhZ05hbWUgPSBub2RlWzBdO1xuXHRcdFx0aWYgKHRhZ05hbWUgPT09IFwiKlwiKSB7XG5cdFx0XHRcdC8vIENvZGUgdGFnOiB7eyogfX1cblx0XHRcdFx0Y29kZSArPSBcIjtcXG5cIiArIG5vZGVbMV0gKyBcIlxcbnJldD1yZXRcIjtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGNvbnZlcnRlciA9IG5vZGVbMV07XG5cdFx0XHRcdGNvbnRlbnQgPSAhaXNMaW5rRXhwciAmJiBub2RlWzJdO1xuXHRcdFx0XHR0YWdDdHggPSBwYXJhbVN0cnVjdHVyZShub2RlWzNdLCBwYXJhbXMgPSBub2RlWzRdKTtcblx0XHRcdFx0dHJpZ2dlciA9IG5vZGVbNl07XG5cdFx0XHRcdGxhdGVSZW5kZXIgPSBub2RlWzddO1xuXHRcdFx0XHRpZiAobm9kZVs4XSkgeyAvLyBsYXRlUGF0aCBAYS5iLmMgb3IgQH5hLmIuY1xuXHRcdFx0XHRcdHJldFN0ck9wZW4gPSBcIlxcbnZhciBvYixsdE9iPXt9LGN0eHM9XCI7XG5cdFx0XHRcdFx0cmV0U3RyQ2xvc2UgPSBcIjtcXG5jdHhzLmx0PWx0T2IubHQ7XFxucmV0dXJuIGN0eHM7XCI7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0cmV0U3RyT3BlbiA9IFwiXFxucmV0dXJuIFwiO1xuXHRcdFx0XHRcdHJldFN0ckNsb3NlID0gXCJcIjtcblx0XHRcdFx0fVxuXHRcdFx0XHRtYXJrdXAgPSBub2RlWzEwXSAmJiBub2RlWzEwXS5yZXBsYWNlKHJVbmVzY2FwZVF1b3RlcywgXCIkMVwiKTtcblx0XHRcdFx0aWYgKGlzRWxzZSA9IHRhZ05hbWUgPT09IFwiZWxzZVwiKSB7XG5cdFx0XHRcdFx0aW
YgKHBhdGhCaW5kaW5ncykge1xuXHRcdFx0XHRcdFx0cGF0aEJpbmRpbmdzLnB1c2gobm9kZVs5XSk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdG9uRXJyb3IgPSBub2RlWzVdIHx8ICRzdWJTZXR0aW5ncy5kZWJ1Z01vZGUgIT09IGZhbHNlICYmIFwidW5kZWZpbmVkXCI7IC8vIElmIGRlYnVnTW9kZSBub3QgZmFsc2UsIHNldCBkZWZhdWx0IG9uRXJyb3IgaGFuZGxlciBvbiB0YWcgdG8gXCJ1bmRlZmluZWRcIiAoc2VlIG9uUmVuZGVyRXJyb3IpXG5cdFx0XHRcdFx0aWYgKHRtcGxCaW5kaW5ncyAmJiAocGF0aEJpbmRpbmdzID0gbm9kZVs5XSkpIHsgLy8gQXJyYXkgb2YgcGF0aHMsIG9yIGZhbHNlIGlmIG5vdCBkYXRhLWJvdW5kXG5cdFx0XHRcdFx0XHRwYXRoQmluZGluZ3MgPSBbcGF0aEJpbmRpbmdzXTtcblx0XHRcdFx0XHRcdHRtcGxCaW5kaW5nS2V5ID0gdG1wbEJpbmRpbmdzLnB1c2goMSk7IC8vIEFkZCBwbGFjZWhvbGRlciBpbiB0bXBsQmluZGluZ3MgZm9yIGNvbXBpbGVkIGZ1bmN0aW9uXG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHRcdHVzZVZpZXdzID0gdXNlVmlld3MgfHwgcGFyYW1zWzFdIHx8IHBhcmFtc1syXSB8fCBwYXRoQmluZGluZ3MgfHwgL3ZpZXcuKD8haW5kZXgpLy50ZXN0KHBhcmFtc1swXSk7XG5cdFx0XHRcdC8vIHVzZVZpZXdzIGlzIGZvciBwZXJmIG9wdGltaXphdGlvbi4gRm9yIHJlbmRlcigpIHdlIG9ubHkgdXNlIHZpZXdzIGlmIG5lY2Vzc2FyeSAtIGZvciB0aGUgbW9yZ
SBhZHZhbmNlZCBzY2VuYXJpb3MuXG5cdFx0XHRcdC8vIFdlIHVzZSB2aWV3cyBpZiB0aGVyZSBhcmUgcHJvcHMsIGNvbnRleHR1YWwgcHJvcGVydGllcyBvciBhcmdzIHdpdGggIy4uLiAob3RoZXIgdGhhbiAjaW5kZXgpIC0gYnV0IHlvdSBjYW4gZm9yY2Vcblx0XHRcdFx0Ly8gdXNpbmcgdGhlIGZ1bGwgdmlldyBpbmZyYXN0cnVjdHVyZSwgKGFuZCBwYXkgYSBwZXJmIHByaWNlKSBieSBvcHRpbmcgaW46IFNldCB1c2VWaWV3czogdHJ1ZSBvbiB0aGUgdGVtcGxhdGUsIG1hbnVhbGx5Li4uXG5cdFx0XHRcdGlmIChpc0dldFZhbCA9IHRhZ05hbWUgPT09IFwiOlwiKSB7XG5cdFx0XHRcdFx0aWYgKGNvbnZlcnRlcikge1xuXHRcdFx0XHRcdFx0dGFnTmFtZSA9IGNvbnZlcnRlciA9PT0gSFRNTCA/IFwiPlwiIDogY29udmVydGVyICsgdGFnTmFtZTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0aWYgKGNvbnRlbnQpIHsgLy8gVE9ETyBvcHRpbWl6ZSAtIGlmIGNvbnRlbnQubGVuZ3RoID09PSAwIG9yIGlmIHRoZXJlIGlzIGEgdG1wbD1cIi4uLlwiIHNwZWNpZmllZCAtIHNldCBjb250ZW50IHRvIG51bGwgLyBkb24ndCBydW4gdGhpcyBjb21waWxhdGlvbiBjb2RlIC0gc2luY2UgY29udGVudCB3b24ndCBnZXQgdXNlZCEhXG5cdFx0XHRcdFx0XHQvLyBDcmVhdGUgdGVtcGxhdGUgb2JqZWN0IGZvciBuZXN0ZWQgdGVtcGxhdGVcblx0XHRcdFx0XHRcdG5lc3RlZFRtcGwgPSB0bXBsT2JqZWN0KG1hcmt1cCwgdG1wbE9wdGlv
bnMpO1xuXHRcdFx0XHRcdFx0bmVzdGVkVG1wbC50bXBsTmFtZSA9IHRtcGxOYW1lICsgXCIvXCIgKyB0YWdOYW1lO1xuXHRcdFx0XHRcdFx0Ly8gQ29tcGlsZSB0byBBU1QgYW5kIHRoZW4gdG8gY29tcGlsZWQgZnVuY3Rpb25cblx0XHRcdFx0XHRcdG5lc3RlZFRtcGwudXNlVmlld3MgPSBuZXN0ZWRUbXBsLnVzZVZpZXdzIHx8IHVzZVZpZXdzO1xuXHRcdFx0XHRcdFx0YnVpbGRDb2RlKGNvbnRlbnQsIG5lc3RlZFRtcGwpO1xuXHRcdFx0XHRcdFx0dXNlVmlld3MgPSBuZXN0ZWRUbXBsLnVzZVZpZXdzO1xuXHRcdFx0XHRcdFx0bmVzdGVkVG1wbHMucHVzaChuZXN0ZWRUbXBsKTtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZiAoIWlzRWxzZSkge1xuXHRcdFx0XHRcdFx0Ly8gVGhpcyBpcyBub3QgYW4gZWxzZSB0YWcuXG5cdFx0XHRcdFx0XHR0YWdBbmRFbHNlcyA9IHRhZ05hbWU7XG5cdFx0XHRcdFx0XHR1c2VWaWV3cyA9IHVzZVZpZXdzIHx8IHRhZ05hbWUgJiYgKCEkdGFnc1t0YWdOYW1lXSB8fCAhJHRhZ3NbdGFnTmFtZV0uZmxvdyk7XG5cdFx0XHRcdFx0XHQvLyBTd2l0Y2ggdG8gYSBuZXcgY29kZSBzdHJpbmcgZm9yIHRoaXMgYm91bmQgdGFnIChhbmQgaXRzIGVsc2VzLCBpZiBpdCBoYXMgYW55KSAtIGZvciByZXR1cm5pbmcgdGhlIHRhZ0N0eHMgYXJyYXlcblx0XHRcdFx0XHRcdG9sZENvZGUgPSBjb2RlO1xuXHRcdFx0XHRcdFx0Y29kZSA9IFwiXCI7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdG5leHRJc0Vsc2UgPSBhc3RbaSArIDF
dO1xuXHRcdFx0XHRcdG5leHRJc0Vsc2UgPSBuZXh0SXNFbHNlICYmIG5leHRJc0Vsc2VbMF0gPT09IFwiZWxzZVwiO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHRhZ1N0YXJ0ID0gb25FcnJvciA/IFwiO1xcbnRyeXtcXG5yZXQrPVwiIDogXCJcXG4rXCI7XG5cdFx0XHRcdGJvdW5kT25FcnJTdGFydCA9IFwiXCI7XG5cdFx0XHRcdGJvdW5kT25FcnJFbmQgPSBcIlwiO1xuXG5cdFx0XHRcdGlmIChpc0dldFZhbCAmJiAocGF0aEJpbmRpbmdzIHx8IHRyaWdnZXIgfHwgY29udmVydGVyICYmIGNvbnZlcnRlciAhPT0gSFRNTCB8fCBsYXRlUmVuZGVyKSkge1xuXHRcdFx0XHRcdC8vIEZvciBjb252ZXJ0VmFsIHdlIG5lZWQgYSBjb21waWxlZCBmdW5jdGlvbiB0byByZXR1cm4gdGhlIG5ldyB0YWdDdHgocylcblx0XHRcdFx0XHR0YWdDdHhGbiA9IG5ldyBGdW5jdGlvbihcImRhdGEsdmlldyxqXCIsIFwiLy8gXCIgKyB0bXBsTmFtZSArIFwiIFwiICsgKCsrdG1wbEJpbmRpbmdLZXkpICsgXCIgXCIgKyB0YWdOYW1lXG5cdFx0XHRcdFx0XHQrIHJldFN0ck9wZW4gKyBcIntcIiArIHRhZ0N0eCArIFwifTtcIiArIHJldFN0ckNsb3NlKTtcblx0XHRcdFx0XHR0YWdDdHhGbi5fZXIgPSBvbkVycm9yO1xuXHRcdFx0XHRcdHRhZ0N0eEZuLl90YWcgPSB0YWdOYW1lO1xuXHRcdFx0XHRcdHRhZ0N0eEZuLl9iZCA9ICEhcGF0aEJpbmRpbmdzOyAvLyBkYXRhLWxpbmtlZCB0YWcge157Li4uL319XG5cdFx0XHRcdFx0dGFnQ3R4Rm4uX2xyID0gbGF0ZVJlbmRlcjtcblxuXH
RcdFx0XHRcdGlmIChpc0xpbmtFeHByKSB7XG5cdFx0XHRcdFx0XHRyZXR1cm4gdGFnQ3R4Rm47XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0c2V0UGF0aHModGFnQ3R4Rm4sIHBhdGhCaW5kaW5ncyk7XG5cdFx0XHRcdFx0dGFnUmVuZGVyID0gJ2MoXCInICsgY29udmVydGVyICsgJ1wiLHZpZXcsJztcblx0XHRcdFx0XHR1c2VDbnZ0ID0gdHJ1ZTtcblx0XHRcdFx0XHRib3VuZE9uRXJyU3RhcnQgPSB0YWdSZW5kZXIgKyB0bXBsQmluZGluZ0tleSArIFwiLFwiO1xuXHRcdFx0XHRcdGJvdW5kT25FcnJFbmQgPSBcIilcIjtcblx0XHRcdFx0fVxuXHRcdFx0XHRjb2RlICs9IChpc0dldFZhbFxuXHRcdFx0XHRcdD8gKGlzTGlua0V4cHIgPyAob25FcnJvciA/IFwidHJ5e1xcblwiIDogXCJcIikgKyBcInJldHVybiBcIiA6IHRhZ1N0YXJ0KSArICh1c2VDbnZ0IC8vIENhbGwgX2NudnQgaWYgdGhlcmUgaXMgYSBjb252ZXJ0ZXI6IHt7Y252dDogLi4uIH19IG9yIHtee2NudnQ6IC4uLiB9fVxuXHRcdFx0XHRcdFx0PyAodXNlQ252dCA9IHVuZGVmaW5lZCwgdXNlVmlld3MgPSBoYXNDbnZ0ID0gdHJ1ZSwgdGFnUmVuZGVyICsgKHRhZ0N0eEZuXG5cdFx0XHRcdFx0XHRcdD8gKCh0bXBsQmluZGluZ3NbdG1wbEJpbmRpbmdLZXkgLSAxXSA9IHRhZ0N0eEZuKSwgdG1wbEJpbmRpbmdLZXkpIC8vIFN0b3JlIHRoZSBjb21waWxlZCB0YWdDdHhGbiBpbiB0bXBsLmJuZHMsIGFuZCBwYXNzIHRoZSBrZXkgdG8gY29udmVydFZhbCgpXG5cdFx0XHRcdFx0XHRcdDogX
CJ7XCIgKyB0YWdDdHggKyBcIn1cIikgKyBcIilcIilcblx0XHRcdFx0XHRcdDogdGFnTmFtZSA9PT0gXCI+XCJcblx0XHRcdFx0XHRcdFx0PyAoaGFzRW5jb2RlciA9IHRydWUsIFwiaChcIiArIHBhcmFtc1swXSArIFwiKVwiKVxuXHRcdFx0XHRcdFx0XHQ6IChnZXRzVmFsID0gdHJ1ZSwgXCIoKHY9XCIgKyBwYXJhbXNbMF0gKyAnKSE9bnVsbD92OicgKyAoaXNMaW5rRXhwciA/ICdudWxsKScgOiAnXCJcIiknKSlcblx0XHRcdFx0XHRcdFx0Ly8gTm9uIHN0cmljdCBlcXVhbGl0eSBzbyBkYXRhLWxpbms9XCJ0aXRsZXs6ZXhwcn1cIiB3aXRoIGV4cHI9bnVsbC91bmRlZmluZWQgcmVtb3ZlcyB0aXRsZSBhdHRyaWJ1dGVcblx0XHRcdFx0XHQpXG5cdFx0XHRcdFx0OiAoaGFzVGFnID0gdHJ1ZSwgXCJcXG57dmlldzp2aWV3LGNvbnRlbnQ6ZmFsc2UsdG1wbDpcIiAvLyBBZGQgdGhpcyB0YWdDdHggdG8gdGhlIGNvbXBpbGVkIGNvZGUgZm9yIHRoZSB0YWdDdHhzIHRvIGJlIHBhc3NlZCB0byByZW5kZXJUYWcoKVxuXHRcdFx0XHRcdFx0KyAoY29udGVudCA/IG5lc3RlZFRtcGxzLmxlbmd0aCA6IFwiZmFsc2VcIikgKyBcIixcIiAvLyBGb3IgYmxvY2sgdGFncywgcGFzcyBpbiB0aGUga2V5IChuZXN0ZWRUbXBscy5sZW5ndGgpIHRvIHRoZSBuZXN0ZWQgY29udGVudCB0ZW1wbGF0ZVxuXHRcdFx0XHRcdFx0KyB0YWdDdHggKyBcIn0sXCIpKTtcblxuXHRcdFx0XHRpZiAodGFnQW5kRWxzZXMgJiYgIW5leHRJc0Vsc2UpIHtcblx0XHRcdFx0XHQvLyBUaGlz
IGlzIGEgZGF0YS1saW5rIGV4cHJlc3Npb24gb3IgYW4gaW5saW5lIHRhZyB3aXRob3V0IGFueSBlbHNlcywgb3IgdGhlIGxhc3Qge3tlbHNlfX0gb2YgYW4gaW5saW5lIHRhZ1xuXHRcdFx0XHRcdC8vIFdlIGNvbXBsZXRlIHRoZSBjb2RlIGZvciByZXR1cm5pbmcgdGhlIHRhZ0N0eHMgYXJyYXlcblx0XHRcdFx0XHRjb2RlID0gXCJbXCIgKyBjb2RlLnNsaWNlKDAsIC0xKSArIFwiXVwiO1xuXHRcdFx0XHRcdHRhZ1JlbmRlciA9ICd0KFwiJyArIHRhZ0FuZEVsc2VzICsgJ1wiLHZpZXcsdGhpcywnO1xuXHRcdFx0XHRcdGlmIChpc0xpbmtFeHByIHx8IHBhdGhCaW5kaW5ncykge1xuXHRcdFx0XHRcdFx0Ly8gVGhpcyBpcyBhIGJvdW5kIHRhZyAoZGF0YS1saW5rIGV4cHJlc3Npb24gb3IgaW5saW5lIGJvdW5kIHRhZyB7Xnt0YWcgLi4ufX0pIHNvIHdlIHN0b3JlIGEgY29tcGlsZWQgdGFnQ3R4cyBmdW5jdGlvbiBpbiB0bXAuYm5kc1xuXHRcdFx0XHRcdFx0Y29kZSA9IG5ldyBGdW5jdGlvbihcImRhdGEsdmlldyxqXCIsIFwiIC8vIFwiICsgdG1wbE5hbWUgKyBcIiBcIiArIHRtcGxCaW5kaW5nS2V5ICsgXCIgXCIgKyB0YWdBbmRFbHNlcyArIHJldFN0ck9wZW4gKyBjb2RlXG5cdFx0XHRcdFx0XHRcdCsgcmV0U3RyQ2xvc2UpO1xuXHRcdFx0XHRcdFx0Y29kZS5fZXIgPSBvbkVycm9yO1xuXHRcdFx0XHRcdFx0Y29kZS5fdGFnID0gdGFnQW5kRWxzZXM7XG5cdFx0XHRcdFx0XHRpZiAocGF0aEJpbmRpbmdzKSB7XG5cdFx0XHRcdFx0XHRcdHNldFBhdGh
zKHRtcGxCaW5kaW5nc1t0bXBsQmluZGluZ0tleSAtIDFdID0gY29kZSwgcGF0aEJpbmRpbmdzKTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGNvZGUuX2xyID0gbGF0ZVJlbmRlcjtcblx0XHRcdFx0XHRcdGlmIChpc0xpbmtFeHByKSB7XG5cdFx0XHRcdFx0XHRcdHJldHVybiBjb2RlOyAvLyBGb3IgYSBkYXRhLWxpbmsgZXhwcmVzc2lvbiB3ZSByZXR1cm4gdGhlIGNvbXBpbGVkIHRhZ0N0eHMgZnVuY3Rpb25cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGJvdW5kT25FcnJTdGFydCA9IHRhZ1JlbmRlciArIHRtcGxCaW5kaW5nS2V5ICsgXCIsdW5kZWZpbmVkLFwiO1xuXHRcdFx0XHRcdFx0Ym91bmRPbkVyckVuZCA9IFwiKVwiO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIFRoaXMgaXMgdGhlIGxhc3Qge3tlbHNlfX0gZm9yIGFuIGlubGluZSB0YWcuXG5cdFx0XHRcdFx0Ly8gRm9yIGEgYm91bmQgdGFnLCBwYXNzIHRoZSB0YWdDdHhzIGZuIGxvb2t1cCBrZXkgdG8gcmVuZGVyVGFnLlxuXHRcdFx0XHRcdC8vIEZvciBhbiB1bmJvdW5kIHRhZywgaW5jbHVkZSB0aGUgY29kZSBkaXJlY3RseSBmb3IgZXZhbHVhdGluZyB0YWdDdHhzIGFycmF5XG5cdFx0XHRcdFx0Y29kZSA9IG9sZENvZGUgKyB0YWdTdGFydCArIHRhZ1JlbmRlciArIChwYXRoQmluZGluZ3MgJiYgdG1wbEJpbmRpbmdLZXkgfHwgY29kZSkgKyBcIilcIjtcblx0XHRcdFx0XHRwYXRoQmluZGluZ3MgPSAwO1xuXHRcdFx0XHRcdHRhZ0FuZEVsc2VzID0gMDtcblx0XH
RcdFx0fVxuXHRcdFx0XHRpZiAob25FcnJvciAmJiAhbmV4dElzRWxzZSkge1xuXHRcdFx0XHRcdHVzZVZpZXdzID0gdHJ1ZTtcblx0XHRcdFx0XHRjb2RlICs9ICc7XFxufWNhdGNoKGUpe3JldCcgKyAoaXNMaW5rRXhwciA/IFwidXJuIFwiIDogXCIrPVwiKSArIGJvdW5kT25FcnJTdGFydCArICdqLl9lcnIoZSx2aWV3LCcgKyBvbkVycm9yICsgJyknICsgYm91bmRPbkVyckVuZCArICc7fScgKyAoaXNMaW5rRXhwciA/IFwiXCIgOiAnXFxucmV0PXJldCcpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cdC8vIEluY2x1ZGUgb25seSB0aGUgdmFyIHJlZmVyZW5jZXMgdGhhdCBhcmUgbmVlZGVkIGluIHRoZSBjb2RlXG5cdGNvZGUgPSBcIi8vIFwiICsgdG1wbE5hbWVcblx0XHQrICh0bXBsT3B0aW9ucy5kZWJ1ZyA/IFwiXFxuZGVidWdnZXI7XCIgOiBcIlwiKVxuXHRcdCsgXCJcXG52YXIgdlwiXG5cdFx0KyAoaGFzVGFnID8gXCIsdD1qLl90YWdcIiA6IFwiXCIpICAgICAgICAgICAgICAgIC8vIGhhcyB0YWdcblx0XHQrIChoYXNDbnZ0ID8gXCIsYz1qLl9jbnZ0XCIgOiBcIlwiKSAgICAgICAgICAgICAgLy8gY29udmVydGVyXG5cdFx0KyAoaGFzRW5jb2RlciA/IFwiLGg9ai5faHRtbFwiIDogXCJcIikgICAgICAgICAgIC8vIGh0bWwgY29udmVydGVyXG5cdFx0KyAoaXNMaW5rRXhwclxuXHRcdFx0XHQ/IChub2RlWzhdIC8vIGxhdGUgQC4uLiBwYXRoP1xuXHRcdFx0XHRcdFx0PyBcIiwgb2JcIlxuXHRcdFx0XHRcdFx0OiBcIlwiX
G5cdFx0XHRcdFx0KSArIFwiO1xcblwiXG5cdFx0XHRcdDogJyxyZXQ9XCJcIicpXG5cdFx0KyBjb2RlXG5cdFx0KyAoaXNMaW5rRXhwciA/IFwiXFxuXCIgOiBcIjtcXG5yZXR1cm4gcmV0O1wiKTtcblxuXHR0cnkge1xuXHRcdGNvZGUgPSBuZXcgRnVuY3Rpb24oXCJkYXRhLHZpZXcsalwiLCBjb2RlKTtcblx0fSBjYXRjaCAoZSkge1xuXHRcdHN5bnRheEVycm9yKFwiQ29tcGlsZWQgdGVtcGxhdGUgY29kZTpcXG5cXG5cIiArIGNvZGUgKyAnXFxuOiBcIicgKyAoZS5tZXNzYWdlfHxlKSArICdcIicpO1xuXHR9XG5cdGlmICh0bXBsKSB7XG5cdFx0dG1wbC5mbiA9IGNvZGU7XG5cdFx0dG1wbC51c2VWaWV3cyA9ICEhdXNlVmlld3M7XG5cdH1cblx0cmV0dXJuIGNvZGU7XG59XG5cbi8vPT09PT09PT09PVxuLy8gVXRpbGl0aWVzXG4vLz09PT09PT09PT1cblxuLy8gTWVyZ2Ugb2JqZWN0cywgaW4gcGFydGljdWxhciBjb250ZXh0cyB3aGljaCBpbmhlcml0IGZyb20gcGFyZW50IGNvbnRleHRzXG5mdW5jdGlvbiBleHRlbmRDdHgoY29udGV4dCwgcGFyZW50Q29udGV4dCkge1xuXHQvLyBSZXR1cm4gY29weSBvZiBwYXJlbnRDb250ZXh0LCB1bmxlc3MgY29udGV4dCBpcyBkZWZpbmVkIGFuZCBpcyBkaWZmZXJlbnQsIGluIHdoaWNoIGNhc2UgcmV0dXJuIGEgbmV3IG1lcmdlZCBjb250ZXh0XG5cdC8vIElmIG5laXRoZXIgY29udGV4dCBub3IgcGFyZW50Q29udGV4dCBhcmUgZGVmaW5lZCwgcmV0dXJuIHVuZGVmaW5lZFxuXHRyZXR1cm4gY29udGV4dCAm
JiBjb250ZXh0ICE9PSBwYXJlbnRDb250ZXh0XG5cdFx0PyAocGFyZW50Q29udGV4dFxuXHRcdFx0PyAkZXh0ZW5kKCRleHRlbmQoe30sIHBhcmVudENvbnRleHQpLCBjb250ZXh0KVxuXHRcdFx0OiBjb250ZXh0KVxuXHRcdDogcGFyZW50Q29udGV4dCAmJiAkZXh0ZW5kKHt9LCBwYXJlbnRDb250ZXh0KTtcbn1cblxuZnVuY3Rpb24gZ2V0VGFyZ2V0UHJvcHMoc291cmNlLCB0YWdDdHgpIHtcblx0Ly8gdGhpcyBwb2ludGVyIGlzIHRoZU1hcCAtIHdoaWNoIGhhcyB0YWdDdHgucHJvcHMgdG9vXG5cdC8vIGFyZ3VtZW50czogdGFnQ3R4LmFyZ3MuXG5cdHZhciBrZXksIHByb3AsXG5cdFx0bWFwID0gdGFnQ3R4Lm1hcCxcblx0XHRwcm9wc0FyciA9IG1hcCAmJiBtYXAucHJvcHNBcnI7XG5cblx0aWYgKCFwcm9wc0FycikgeyAvLyBtYXAucHJvcHNBcnIgaXMgdGhlIGZ1bGwgYXJyYXkgb2Yge2tleTouLi4sIHByb3A6Li4ufSBvYmplY3RzXG5cdFx0cHJvcHNBcnIgPSBbXTtcblx0XHRpZiAodHlwZW9mIHNvdXJjZSA9PT0gT0JKRUNUIHx8ICRpc0Z1bmN0aW9uKHNvdXJjZSkpIHtcblx0XHRcdGZvciAoa2V5IGluIHNvdXJjZSkge1xuXHRcdFx0XHRwcm9wID0gc291cmNlW2tleV07XG5cdFx0XHRcdGlmIChrZXkgIT09ICRleHBhbmRvICYmIHNvdXJjZS5oYXNPd25Qcm9wZXJ0eShrZXkpICYmICghdGFnQ3R4LnByb3BzLm5vRnVuY3Rpb25zIHx8ICEkLmlzRnVuY3Rpb24ocHJvcCkpKSB7XG5cdFx0XHRcdFx0cHJvcHNBcnIucHVzaCh7a2V5OiBrZXksIHB
yb3A6IHByb3B9KTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0XHRpZiAobWFwKSB7XG5cdFx0XHRtYXAucHJvcHNBcnIgPSBtYXAub3B0aW9ucyAmJiBwcm9wc0FycjsgLy8gSWYgYm91bmQge157cHJvcHN9fSBhbmQgbm90IGlzUmVuZGVyQ2FsbCwgc3RvcmUgcHJvcHNBcnIgb24gbWFwIChtYXAub3B0aW9ucyBpcyBkZWZpbmVkIG9ubHkgZm9yIGJvdW5kLCAmJiAhaXNSZW5kZXJDYWxsKVxuXHRcdH1cblx0fVxuXHRyZXR1cm4gZ2V0VGFyZ2V0U29ydGVkKHByb3BzQXJyLCB0YWdDdHgpOyAvLyBPYnRhaW5zIG1hcC50Z3QsIGJ5IGZpbHRlcmluZywgc29ydGluZyBhbmQgc3BsaWNpbmcgdGhlIGZ1bGwgcHJvcHNBcnJcbn1cblxuZnVuY3Rpb24gZ2V0VGFyZ2V0U29ydGVkKHZhbHVlLCB0YWdDdHgpIHtcblx0Ly8gZ2V0VGd0XG5cdHZhciBtYXBwZWQsIHN0YXJ0LCBlbmQsXG5cdFx0dGFnID0gdGFnQ3R4LnRhZyxcblx0XHRwcm9wcyA9IHRhZ0N0eC5wcm9wcyxcblx0XHRwcm9wUGFyYW1zID0gdGFnQ3R4LnBhcmFtcy5wcm9wcyxcblx0XHRmaWx0ZXIgPSBwcm9wcy5maWx0ZXIsXG5cdFx0c29ydCA9IHByb3BzLnNvcnQsXG5cdFx0ZGlyZWN0U29ydCA9IHNvcnQgPT09IHRydWUsXG5cdFx0c3RlcCA9IHBhcnNlSW50KHByb3BzLnN0ZXApLFxuXHRcdHJldmVyc2UgPSBwcm9wcy5yZXZlcnNlID8gLTEgOiAxO1xuXG5cdGlmICghJGlzQXJyYXkodmFsdWUpKSB7XG5cdFx0cmV0dXJuIHZhbHVlO1xuXHR9XG5cdGlmIChkaXJlY3RTb3J0IHx8IH
NvcnQgJiYgXCJcIiArIHNvcnQgPT09IHNvcnQpIHtcblx0XHQvLyBUZW1wb3JhcnkgbWFwcGVkIGFycmF5IGhvbGRzIG9iamVjdHMgd2l0aCBpbmRleCBhbmQgc29ydC12YWx1ZVxuXHRcdG1hcHBlZCA9IHZhbHVlLm1hcChmdW5jdGlvbihpdGVtLCBpKSB7XG5cdFx0XHRpdGVtID0gZGlyZWN0U29ydCA/IGl0ZW0gOiBnZXRQYXRoT2JqZWN0KGl0ZW0sIHNvcnQpO1xuXHRcdFx0cmV0dXJuIHtpOiBpLCB2OiBcIlwiICsgaXRlbSA9PT0gaXRlbSA/IGl0ZW0udG9Mb3dlckNhc2UoKSA6IGl0ZW19O1xuXHRcdH0pO1xuXHRcdC8vIFNvcnQgbWFwcGVkIGFycmF5XG5cdFx0bWFwcGVkLnNvcnQoZnVuY3Rpb24oYSwgYikge1xuXHRcdFx0cmV0dXJuIGEudiA+IGIudiA/IHJldmVyc2UgOiBhLnYgPCBiLnYgPyAtcmV2ZXJzZSA6IDA7XG5cdFx0fSk7XG5cdFx0Ly8gTWFwIHRvIG5ldyBhcnJheSB3aXRoIHJlc3VsdGluZyBvcmRlclxuXHRcdHZhbHVlID0gbWFwcGVkLm1hcChmdW5jdGlvbihpdGVtKXtcblx0XHRcdHJldHVybiB2YWx1ZVtpdGVtLmldO1xuXHRcdH0pO1xuXHR9IGVsc2UgaWYgKChzb3J0IHx8IHJldmVyc2UgPCAwKSAmJiAhdGFnLmRhdGFNYXApIHtcblx0XHR2YWx1ZSA9IHZhbHVlLnNsaWNlKCk7IC8vIENsb25lIGFycmF5IGZpcnN0IGlmIG5vdCBhbHJlYWR5IGEgbmV3IGFycmF5XG5cdH1cblx0aWYgKCRpc0Z1bmN0aW9uKHNvcnQpKSB7XG5cdFx0dmFsdWUgPSB2YWx1ZS5zb3J0KGZ1bmN0aW9uKCkgeyAvLyBXcmFwIHRoZSBzb3J0IGZ1b
mN0aW9uIHRvIHByb3ZpZGUgdGFnQ3R4IGFzICd0aGlzJyBwb2ludGVyXG5cdFx0XHRyZXR1cm4gc29ydC5hcHBseSh0YWdDdHgsIGFyZ3VtZW50cyk7XG5cdFx0fSk7XG5cdH1cblx0aWYgKHJldmVyc2UgPCAwICYmICghc29ydCB8fCAkaXNGdW5jdGlvbihzb3J0KSkpIHsgLy8gUmV2ZXJzZSByZXN1bHQgaWYgbm90IGFscmVhZHkgcmV2ZXJzZWQgaW4gc29ydFxuXHRcdHZhbHVlID0gdmFsdWUucmV2ZXJzZSgpO1xuXHR9XG5cblx0aWYgKHZhbHVlLmZpbHRlciAmJiBmaWx0ZXIpIHsgLy8gSUU4IGRvZXMgbm90IHN1cHBvcnQgZmlsdGVyXG5cdFx0dmFsdWUgPSB2YWx1ZS5maWx0ZXIoZmlsdGVyLCB0YWdDdHgpO1xuXHRcdGlmICh0YWdDdHgudGFnLm9uRmlsdGVyKSB7XG5cdFx0XHR0YWdDdHgudGFnLm9uRmlsdGVyKHRhZ0N0eCk7XG5cdFx0fVxuXHR9XG5cblx0aWYgKHByb3BQYXJhbXMuc29ydGVkKSB7XG5cdFx0bWFwcGVkID0gKHNvcnQgfHwgcmV2ZXJzZSA8IDApID8gdmFsdWUgOiB2YWx1ZS5zbGljZSgpO1xuXHRcdGlmICh0YWcuc29ydGVkKSB7XG5cdFx0XHQkLm9ic2VydmFibGUodGFnLnNvcnRlZCkucmVmcmVzaChtYXBwZWQpOyAvLyBOb3RlIHRoYXQgdGhpcyBtaWdodCBjYXVzZSB0aGUgc3RhcnQgYW5kIGVuZCBwcm9wcyB0byBiZSBtb2RpZmllZCAtIGUuZy4gYnkgcGFnZXIgdGFnIGNvbnRyb2xcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGFnQ3R4Lm1hcC5zb3J0ZWQgPSBtYXBwZWQ7XG5cdFx0fVxuXHR9XG5cblx0c3RhcnQgPSBwcm9w
cy5zdGFydDsgLy8gR2V0IGN1cnJlbnQgdmFsdWUgLSBhZnRlciBwb3NzaWJsZSBjaGFuZ2VzIHRyaWdnZXJlZCBieSB0YWcuc29ydGVkIHJlZnJlc2goKSBhYm92ZVxuXHRlbmQgPSBwcm9wcy5lbmQ7XG5cdGlmIChwcm9wUGFyYW1zLnN0YXJ0ICYmIHN0YXJ0ID09PSB1bmRlZmluZWQgfHwgcHJvcFBhcmFtcy5lbmQgJiYgZW5kID09PSB1bmRlZmluZWQpIHtcblx0XHRzdGFydCA9IGVuZCA9IDA7XG5cdH1cblx0aWYgKCFpc05hTihzdGFydCkgfHwgIWlzTmFOKGVuZCkpIHsgLy8gc3RhcnQgb3IgZW5kIHNwZWNpZmllZCwgYnV0IG5vdCB0aGUgYXV0by1jcmVhdGUgTnVtYmVyIGFycmF5IHNjZW5hcmlvIG9mIHt7Zm9yIHN0YXJ0PXh4eCBlbmQ9eXl5fX1cblx0XHRzdGFydCA9ICtzdGFydCB8fCAwO1xuXHRcdGVuZCA9IGVuZCA9PT0gdW5kZWZpbmVkIHx8IGVuZCA+IHZhbHVlLmxlbmd0aCA/IHZhbHVlLmxlbmd0aCA6ICtlbmQ7XG5cdFx0dmFsdWUgPSB2YWx1ZS5zbGljZShzdGFydCwgZW5kKTtcblx0fVxuXHRpZiAoc3RlcCA+IDEpIHtcblx0XHRzdGFydCA9IDA7XG5cdFx0ZW5kID0gdmFsdWUubGVuZ3RoO1xuXHRcdG1hcHBlZCA9IFtdO1xuXHRcdGZvciAoOyBzdGFydDxlbmQ7IHN0YXJ0Kz1zdGVwKSB7XG5cdFx0XHRtYXBwZWQucHVzaCh2YWx1ZVtzdGFydF0pO1xuXHRcdH1cblx0XHR2YWx1ZSA9IG1hcHBlZDtcblx0fVxuXHRpZiAocHJvcFBhcmFtcy5wYWdlZCAmJiB0YWcucGFnZWQpIHtcblx0XHQkb2JzZXJ2YWJsZSh0YWcucGFnZWQpLnJ
lZnJlc2godmFsdWUpO1xuXHR9XG5cblx0cmV0dXJuIHZhbHVlO1xufVxuXG4vKiogUmVuZGVyIHRoZSB0ZW1wbGF0ZSBhcyBhIHN0cmluZywgdXNpbmcgdGhlIHNwZWNpZmllZCBkYXRhIGFuZCBoZWxwZXJzL2NvbnRleHRcbiogJChcIiN0bXBsXCIpLnJlbmRlcigpXG4qXG4qIEBwYXJhbSB7YW55fSAgICAgICAgZGF0YVxuKiBAcGFyYW0ge2hhc2h9ICAgICAgIFtoZWxwZXJzT3JDb250ZXh0XVxuKiBAcGFyYW0ge2Jvb2xlYW59ICAgIFtub0l0ZXJhdGlvbl1cbiogQHJldHVybnMge3N0cmluZ30gICByZW5kZXJlZCB0ZW1wbGF0ZVxuKi9cbmZ1bmN0aW9uICRmblJlbmRlcihkYXRhLCBjb250ZXh0LCBub0l0ZXJhdGlvbikge1xuXHR2YXIgdG1wbEVsZW0gPSB0aGlzLmpxdWVyeSAmJiAodGhpc1swXSB8fCBlcnJvcignVW5rbm93biB0ZW1wbGF0ZScpKSwgLy8gVGFyZ2V0ZWQgZWxlbWVudCBub3QgZm91bmQgZm9yIGpRdWVyeSB0ZW1wbGF0ZSBzZWxlY3RvciBzdWNoIGFzIFwiI215VG1wbFwiXG5cdFx0dG1wbCA9IHRtcGxFbGVtLmdldEF0dHJpYnV0ZSh0bXBsQXR0cik7XG5cblx0cmV0dXJuIHJlbmRlckNvbnRlbnQuY2FsbCh0bXBsICYmICQuZGF0YSh0bXBsRWxlbSlbanN2VG1wbF0gfHwgJHRlbXBsYXRlcyh0bXBsRWxlbSksXG5cdFx0ZGF0YSwgY29udGV4dCwgbm9JdGVyYXRpb24pO1xufVxuXG4vLz09PT09PT09PT09PT09PT09PT09PT09PT09IFJlZ2lzdGVyIGNvbnZlcnRlcnMgPT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZnVuY3
Rpb24gZ2V0Q2hhckVudGl0eShjaCkge1xuXHQvLyBHZXQgY2hhcmFjdGVyIGVudGl0eSBmb3IgSFRNTCwgQXR0cmlidXRlIGFuZCBvcHRpb25hbCBkYXRhIGVuY29kaW5nXG5cdHJldHVybiBjaGFyRW50aXRpZXNbY2hdIHx8IChjaGFyRW50aXRpZXNbY2hdID0gXCImI1wiICsgY2guY2hhckNvZGVBdCgwKSArIFwiO1wiKTtcbn1cblxuZnVuY3Rpb24gZ2V0Q2hhckZyb21FbnRpdHkobWF0Y2gsIHRva2VuKSB7XG5cdC8vIEdldCBjaGFyYWN0ZXIgZnJvbSBIVE1MIGVudGl0eSwgZm9yIG9wdGlvbmFsIGRhdGEgdW5lbmNvZGluZ1xuXHRyZXR1cm4gY2hhcnNGcm9tRW50aXRpZXNbdG9rZW5dIHx8IFwiXCI7XG59XG5cbmZ1bmN0aW9uIGh0bWxFbmNvZGUodGV4dCkge1xuXHQvLyBIVE1MIGVuY29kZTogUmVwbGFjZSA8ID4gJiAnIFwiIGAgZXRjLiBieSBjb3JyZXNwb25kaW5nIGVudGl0aWVzLlxuXHRyZXR1cm4gdGV4dCAhPSB1bmRlZmluZWQgPyBySXNIdG1sLnRlc3QodGV4dCkgJiYgKFwiXCIgKyB0ZXh0KS5yZXBsYWNlKHJIdG1sRW5jb2RlLCBnZXRDaGFyRW50aXR5KSB8fCB0ZXh0IDogXCJcIjtcbn1cblxuZnVuY3Rpb24gZGF0YUVuY29kZSh0ZXh0KSB7XG5cdC8vIEVuY29kZSBqdXN0IDwgPiBhbmQgJiAtIGludGVuZGVkIGZvciAnc2FmZSBkYXRhJyBhbG9uZyB3aXRoIHt7On19IHJhdGhlciB0aGFuIHt7Pn19XG4gIHJldHVybiBcIlwiICsgdGV4dCA9PT0gdGV4dCA/IHRleHQucmVwbGFjZShyRGF0YUVuY29kZSwgZ2V0Q2hhckVudGl0eSkgO
iB0ZXh0O1xufVxuXG5mdW5jdGlvbiBkYXRhVW5lbmNvZGUodGV4dCkge1xuICAvLyBVbmVuY29kZSBqdXN0IDwgPiBhbmQgJiAtIGludGVuZGVkIGZvciAnc2FmZSBkYXRhJyBhbG9uZyB3aXRoIHt7On19IHJhdGhlciB0aGFuIHt7Pn19XG4gIHJldHVybiBcIlwiICsgdGV4dCA9PT0gdGV4dCA/IHRleHQucmVwbGFjZShyRGF0YVVuZW5jb2RlLCBnZXRDaGFyRnJvbUVudGl0eSkgOiB0ZXh0O1xufVxuXG4vLz09PT09PT09PT09PT09PT09PT09PT09PT09IEluaXRpYWxpemUgPT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuJHN1YiA9ICR2aWV3cy5zdWI7XG4kdmlld3NTZXR0aW5ncyA9ICR2aWV3cy5zZXR0aW5ncztcblxuaWYgKCEoanNyIHx8ICQgJiYgJC5yZW5kZXIpKSB7XG5cdC8vIEpzUmVuZGVyL0pzVmlld3Mgbm90IGFscmVhZHkgbG9hZGVkIChvciBsb2FkZWQgd2l0aG91dCBqUXVlcnksIGFuZCB3ZSBhcmUgbm93IG1vdmluZyBmcm9tIGpzcmVuZGVyIG5hbWVzcGFjZSB0byBqUXVlcnkgbmFtZXBhY2UpXG5cdGZvciAoanN2U3RvcmVOYW1lIGluIGpzdlN0b3Jlcykge1xuXHRcdHJlZ2lzdGVyU3RvcmUoanN2U3RvcmVOYW1lLCBqc3ZTdG9yZXNbanN2U3RvcmVOYW1lXSk7XG5cdH1cblxuXHQkY29udmVydGVycyA9ICR2aWV3cy5jb252ZXJ0ZXJzO1xuXHQkaGVscGVycyA9ICR2aWV3cy5oZWxwZXJzO1xuXHQkdGFncyA9ICR2aWV3cy50YWdzO1xuXG5cdCRzdWIuX3RnLnByb3RvdHlwZSA9IHtcblx0XHRiYXNlQXBwbHk6IGJhc2VBcHBs
eSxcblx0XHRjdnRBcmdzOiBjb252ZXJ0QXJncyxcblx0XHRibmRBcmdzOiBjb252ZXJ0Qm91bmRBcmdzLFxuXHRcdGN0eFBybTogY29udGV4dFBhcmFtZXRlclxuXHR9O1xuXG5cdHRvcFZpZXcgPSAkc3ViLnRvcFZpZXcgPSBuZXcgVmlldygpO1xuXG5cdC8vQlJPV1NFUi1TUEVDSUZJQyBDT0RFXG5cdGlmICgkKSB7XG5cblx0XHQvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cblx0XHQvLyBqUXVlcnkgKD0gJCkgaXMgbG9hZGVkXG5cblx0XHQkLmZuLnJlbmRlciA9ICRmblJlbmRlcjtcblx0XHQkZXhwYW5kbyA9ICQuZXhwYW5kbztcblx0XHRpZiAoJC5vYnNlcnZhYmxlKSB7XG5cdFx0XHRpZiAodmVyc2lvbk51bWJlciAhPT0gKHZlcnNpb25OdW1iZXIgPSAkLnZpZXdzLmpzdmlld3MpKSB7XG5cdFx0XHRcdC8vIERpZmZlcmVudCB2ZXJzaW9uIG9mIGpzUmVuZGVyIHdhcyBsb2FkZWRcblx0XHRcdFx0dGhyb3cgXCJqcXVlcnkub2JzZXJ2YWJsZS5qcyByZXF1aXJlcyBqc3JlbmRlci5qcyBcIiArIHZlcnNpb25OdW1iZXI7XG5cdFx0XHR9XG5cdFx0XHQkZXh0ZW5kKCRzdWIsICQudmlld3Muc3ViKTsgLy8ganF1ZXJ5Lm9ic2VydmFibGUuanMgd2FzIGxvYWRlZCBiZWZvcmUganNyZW5kZXIuanNcblx0XHRcdCR2aWV3cy5tYXAgPSAkLnZpZXdzLm1hcDtcblx0XHR9XG5cblx0fSBlbHNlIHtcblx0XHQvLy8vLy8vLy8vLy8
vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cblx0XHQvLyBqUXVlcnkgaXMgbm90IGxvYWRlZC5cblxuXHRcdCQgPSB7fTtcblxuXHRcdGlmIChzZXRHbG9iYWxzKSB7XG5cdFx0XHRnbG9iYWwuanNyZW5kZXIgPSAkOyAvLyBXZSBhcmUgbG9hZGluZyBqc3JlbmRlci5qcyBmcm9tIGEgc2NyaXB0IGVsZW1lbnQsIG5vdCBBTUQgb3IgQ29tbW9uSlMsIHNvIHNldCBnbG9iYWxcblx0XHR9XG5cblx0XHQvLyBFcnJvciB3YXJuaW5nIGlmIGpzcmVuZGVyLmpzIGlzIHVzZWQgYXMgdGVtcGxhdGUgZW5naW5lIG9uIE5vZGUuanMgKGUuZy4gRXhwcmVzcyBvciBIYXBpLi4uKVxuXHRcdC8vIFVzZSBqc3JlbmRlci1ub2RlLmpzIGluc3RlYWQuLi5cblx0XHQkLnJlbmRlckZpbGUgPSAkLl9fZXhwcmVzcyA9ICQuY29tcGlsZSA9IGZ1bmN0aW9uKCkgeyB0aHJvdyBcIk5vZGUuanM6IHVzZSBucG0ganNyZW5kZXIsIG9yIGpzcmVuZGVyLW5vZGUuanNcIjsgfTtcblxuXHRcdC8vRU5EIEJST1dTRVItU1BFQ0lGSUMgQ09ERVxuXHRcdCQuaXNGdW5jdGlvbiA9IGZ1bmN0aW9uKG9iKSB7XG5cdFx0XHRyZXR1cm4gdHlwZW9mIG9iID09PSBcImZ1bmN0aW9uXCI7XG5cdFx0fTtcblxuXHRcdCQuaXNBcnJheSA9IEFycmF5LmlzQXJyYXkgfHwgZnVuY3Rpb24ob2JqKSB7XG5cdFx0XHRyZXR1cm4gKHt9LnRvU3RyaW5nKS5jYWxsKG9iaikgPT09IFwiW29iamVjdCBBcn
JheV1cIjtcblx0XHR9O1xuXG5cdFx0JHN1Yi5fanEgPSBmdW5jdGlvbihqcSkgeyAvLyBwcml2YXRlIG1ldGhvZCB0byBtb3ZlIGZyb20gSnNSZW5kZXIgQVBJcyBmcm9tIGpzcmVuZGVyIG5hbWVzcGFjZSB0byBqUXVlcnkgbmFtZXNwYWNlXG5cdFx0XHRpZiAoanEgIT09ICQpIHtcblx0XHRcdFx0JGV4dGVuZChqcSwgJCk7IC8vIG1hcCBvdmVyIGZyb20ganNyZW5kZXIgbmFtZXNwYWNlIHRvIGpRdWVyeSBuYW1lc3BhY2Vcblx0XHRcdFx0JCA9IGpxO1xuXHRcdFx0XHQkLmZuLnJlbmRlciA9ICRmblJlbmRlcjtcblx0XHRcdFx0ZGVsZXRlICQuanNyZW5kZXI7XG5cdFx0XHRcdCRleHBhbmRvID0gJC5leHBhbmRvO1xuXHRcdFx0fVxuXHRcdH07XG5cblx0XHQkLmpzcmVuZGVyID0gdmVyc2lvbk51bWJlcjtcblx0fVxuXHQkc3ViU2V0dGluZ3MgPSAkc3ViLnNldHRpbmdzO1xuXHQkc3ViU2V0dGluZ3MuYWxsb3dDb2RlID0gZmFsc2U7XG5cdCRpc0Z1bmN0aW9uID0gJC5pc0Z1bmN0aW9uO1xuXHQkLnJlbmRlciA9ICRyZW5kZXI7XG5cdCQudmlld3MgPSAkdmlld3M7XG5cdCQudGVtcGxhdGVzID0gJHRlbXBsYXRlcyA9ICR2aWV3cy50ZW1wbGF0ZXM7XG5cblx0Zm9yIChzZXR0aW5nIGluICRzdWJTZXR0aW5ncykge1xuXHRcdGFkZFNldHRpbmcoc2V0dGluZyk7XG5cdH1cblxuXHQvKipcblx0KiAkLnZpZXdzLnNldHRpbmdzLmRlYnVnTW9kZSh0cnVlKVxuXHQqIEBwYXJhbSB7Ym9vbGVhbn0gZGVidWdNb2RlXG5cdCogQHJldHVybnMge1NldHRpb
mdzfVxuXHQqXG5cdCogZGVidWdNb2RlID0gJC52aWV3cy5zZXR0aW5ncy5kZWJ1Z01vZGUoKVxuXHQqIEByZXR1cm5zIHtib29sZWFufVxuXHQqL1xuXHQoJHZpZXdzU2V0dGluZ3MuZGVidWdNb2RlID0gZnVuY3Rpb24oZGVidWdNb2RlKSB7XG5cdFx0cmV0dXJuIGRlYnVnTW9kZSA9PT0gdW5kZWZpbmVkXG5cdFx0XHQ/ICRzdWJTZXR0aW5ncy5kZWJ1Z01vZGVcblx0XHRcdDogKFxuXHRcdFx0XHQkc3ViU2V0dGluZ3MuX2NsRm5zICYmICRzdWJTZXR0aW5ncy5fY2xGbnMoKSwgLy8gQ2xlYXIgbGlua0V4cHJTdG9yZSAoY2FjaGVkIGNvbXBpbGVkIGV4cHJlc3Npb25zKSwgc2luY2UgZGVidWdNb2RlIHNldHRpbmcgYWZmZWN0cyBjb21waWxhdGlvbiBmb3IgZXhwcmVzc2lvbnNcblx0XHRcdFx0JHN1YlNldHRpbmdzLmRlYnVnTW9kZSA9IGRlYnVnTW9kZSxcblx0XHRcdFx0JHN1YlNldHRpbmdzLm9uRXJyb3IgPSBkZWJ1Z01vZGUgKyBcIlwiID09PSBkZWJ1Z01vZGVcblx0XHRcdFx0XHQ/IGZ1bmN0aW9uKCkgeyByZXR1cm4gZGVidWdNb2RlOyB9XG5cdFx0XHRcdFx0OiAkaXNGdW5jdGlvbihkZWJ1Z01vZGUpXG5cdFx0XHRcdFx0XHQ/IGRlYnVnTW9kZVxuXHRcdFx0XHRcdFx0OiB1bmRlZmluZWQsXG5cdFx0XHRcdCR2aWV3c1NldHRpbmdzKTtcblx0fSkoZmFsc2UpOyAvLyBqc2hpbnQgaWdub3JlOmxpbmVcblxuXHQkc3ViU2V0dGluZ3NBZHZhbmNlZCA9ICRzdWJTZXR0aW5ncy5hZHZhbmNlZCA9IHtcblx0XHRjYWNoZTogdHJ1ZSwgLy8gQnkg
ZGVmYXVsdCB1c2UgY2FjaGVkIHZhbHVlcyBvZiBjb21wdXRlZCB2YWx1ZXMgKE90aGVyd2lzZSwgc2V0IGFkdmFuY2VkIGNhY2hlIHNldHRpbmcgdG8gZmFsc2UpXG5cdFx0dXNlVmlld3M6IGZhbHNlLFxuXHRcdF9qc3Y6IGZhbHNlIC8vIEZvciBnbG9iYWwgYWNjZXNzIHRvIEpzVmlld3Mgc3RvcmVcblx0fTtcblxuXHQvLz09PT09PT09PT09PT09PT09PT09PT09PT09IFJlZ2lzdGVyIHRhZ3MgPT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuXHQkdGFncyh7XG5cdFx0XCJpZlwiOiB7XG5cdFx0XHRyZW5kZXI6IGZ1bmN0aW9uKHZhbCkge1xuXHRcdFx0XHQvLyBUaGlzIGZ1bmN0aW9uIGlzIGNhbGxlZCBvbmNlIGZvciB7e2lmfX0gYW5kIG9uY2UgZm9yIGVhY2gge3tlbHNlfX0uXG5cdFx0XHRcdC8vIFdlIHdpbGwgdXNlIHRoZSB0YWcucmVuZGVyaW5nIG9iamVjdCBmb3IgY2FycnlpbmcgcmVuZGVyaW5nIHN0YXRlIGFjcm9zcyB0aGUgY2FsbHMuXG5cdFx0XHRcdC8vIElmIG5vdCBkb25lIChhIHByZXZpb3VzIGJsb2NrIGhhcyBub3QgYmVlbiByZW5kZXJlZCksIGxvb2sgYXQgZXhwcmVzc2lvbiBmb3IgdGhpcyBibG9jayBhbmQgcmVuZGVyIHRoZSBibG9jayBpZiBleHByZXNzaW9uIGlzIHRydXRoeVxuXHRcdFx0XHQvLyBPdGhlcndpc2UgcmV0dXJuIFwiXCJcblx0XHRcdFx0dmFyIHNlbGYgPSB0aGlzLFxuXHRcdFx0XHRcdHRhZ0N0eCA9IHNlbGYudGFnQ3R4LFxuXHRcdFx0XHRcdHJldCA9IChzZWxmLnJlbmRlcmluZy5kb25lIHx8ICF
2YWwgJiYgKHRhZ0N0eC5hcmdzLmxlbmd0aCB8fCAhdGFnQ3R4LmluZGV4KSlcblx0XHRcdFx0XHRcdD8gXCJcIlxuXHRcdFx0XHRcdFx0OiAoc2VsZi5yZW5kZXJpbmcuZG9uZSA9IHRydWUsXG5cdFx0XHRcdFx0XHRcdHNlbGYuc2VsZWN0ZWQgPSB0YWdDdHguaW5kZXgsXG5cdFx0XHRcdFx0XHRcdHVuZGVmaW5lZCk7IC8vIFRlc3QgaXMgc2F0aXNmaWVkLCBzbyByZW5kZXIgY29udGVudCBvbiBjdXJyZW50IGNvbnRleHRcblx0XHRcdFx0cmV0dXJuIHJldDtcblx0XHRcdH0sXG5cdFx0XHRjb250ZW50Q3R4OiB0cnVlLCAvLyBJbmhlcml0IHBhcmVudCB2aWV3IGRhdGEgY29udGV4dFxuXHRcdFx0ZmxvdzogdHJ1ZVxuXHRcdH0sXG5cdFx0XCJmb3JcIjoge1xuXHRcdFx0c29ydERhdGFNYXA6IGRhdGFNYXAoZ2V0VGFyZ2V0U29ydGVkKSxcblx0XHRcdGluaXQ6IGZ1bmN0aW9uKHZhbCwgY2xvbmVkKSB7XG5cdFx0XHRcdHRoaXMuc2V0RGF0YU1hcCh0aGlzLnRhZ0N0eHMpO1xuXHRcdFx0fSxcblx0XHRcdHJlbmRlcjogZnVuY3Rpb24odmFsKSB7XG5cdFx0XHRcdC8vIFRoaXMgZnVuY3Rpb24gaXMgY2FsbGVkIG9uY2UgZm9yIHt7Zm9yfX0gYW5kIG9uY2UgZm9yIGVhY2gge3tlbHNlfX0uXG5cdFx0XHRcdC8vIFdlIHdpbGwgdXNlIHRoZSB0YWcucmVuZGVyaW5nIG9iamVjdCBmb3IgY2FycnlpbmcgcmVuZGVyaW5nIHN0YXRlIGFjcm9zcyB0aGUgY2FsbHMuXG5cdFx0XHRcdHZhciB2YWx1ZSwgZmlsdGVyLCBzcnRGaWVsZCwgaXNBcnJheSwgaSwgc2
9ydGVkLCBlbmQsIHN0ZXAsXG5cdFx0XHRcdFx0c2VsZiA9IHRoaXMsXG5cdFx0XHRcdFx0dGFnQ3R4ID0gc2VsZi50YWdDdHgsXG5cdFx0XHRcdFx0cmFuZ2UgPSB0YWdDdHguYXJnRGVmYXVsdCA9PT0gZmFsc2UsXG5cdFx0XHRcdFx0cHJvcHMgPSB0YWdDdHgucHJvcHMsXG5cdFx0XHRcdFx0aXRlcmF0ZSA9IHJhbmdlIHx8IHRhZ0N0eC5hcmdzLmxlbmd0aCwgLy8gTm90IGZpbmFsIGVsc2UgYW5kIG5vdCBhdXRvLWNyZWF0ZSByYW5nZVxuXHRcdFx0XHRcdHJlc3VsdCA9IFwiXCIsXG5cdFx0XHRcdFx0ZG9uZSA9IDA7XG5cblx0XHRcdFx0aWYgKCFzZWxmLnJlbmRlcmluZy5kb25lKSB7XG5cdFx0XHRcdFx0dmFsdWUgPSBpdGVyYXRlID8gdmFsIDogdGFnQ3R4LnZpZXcuZGF0YTsgLy8gRm9yIHRoZSBmaW5hbCBlbHNlLCBkZWZhdWx0cyB0byBjdXJyZW50IGRhdGEgd2l0aG91dCBpdGVyYXRpb24uXG5cblx0XHRcdFx0XHRpZiAocmFuZ2UpIHtcblx0XHRcdFx0XHRcdHJhbmdlID0gcHJvcHMucmV2ZXJzZSA/IFwidW5zaGlmdFwiIDogXCJwdXNoXCI7XG5cdFx0XHRcdFx0XHRlbmQgPSArcHJvcHMuZW5kO1xuXHRcdFx0XHRcdFx0c3RlcCA9ICtwcm9wcy5zdGVwIHx8IDE7XG5cdFx0XHRcdFx0XHR2YWx1ZSA9IFtdOyAvLyBhdXRvLWNyZWF0ZSBpbnRlZ2VyIGFycmF5IHNjZW5hcmlvIG9mIHt7Zm9yIHN0YXJ0PXh4eCBlbmQ9eXl5fX1cblx0XHRcdFx0XHRcdGZvciAoaSA9ICtwcm9wcy5zdGFydCB8fCAwOyAoZW5kIC0gaSkgKiBzdGVwID4gMDsga
SArPSBzdGVwKSB7XG5cdFx0XHRcdFx0XHRcdHZhbHVlW3JhbmdlXShpKTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0aWYgKHZhbHVlICE9PSB1bmRlZmluZWQpIHtcblx0XHRcdFx0XHRcdGlzQXJyYXkgPSAkaXNBcnJheSh2YWx1ZSk7XG5cdFx0XHRcdFx0XHRyZXN1bHQgKz0gdGFnQ3R4LnJlbmRlcih2YWx1ZSwgIWl0ZXJhdGUgfHwgcHJvcHMubm9JdGVyYXRpb24pO1xuXHRcdFx0XHRcdFx0Ly8gSXRlcmF0ZXMgaWYgZGF0YSBpcyBhbiBhcnJheSwgZXhjZXB0IG9uIGZpbmFsIGVsc2UgLSBvciBpZiBub0l0ZXJhdGlvbiBwcm9wZXJ0eVxuXHRcdFx0XHRcdFx0Ly8gc2V0IHRvIHRydWUuIChVc2Uge3tpbmNsdWRlfX0gdG8gY29tcG9zZSB0ZW1wbGF0ZXMgd2l0aG91dCBhcnJheSBpdGVyYXRpb24pXG5cdFx0XHRcdFx0XHRkb25lICs9IGlzQXJyYXkgPyB2YWx1ZS5sZW5ndGggOiAxO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRpZiAoc2VsZi5yZW5kZXJpbmcuZG9uZSA9IGRvbmUpIHtcblx0XHRcdFx0XHRcdHNlbGYuc2VsZWN0ZWQgPSB0YWdDdHguaW5kZXg7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdC8vIElmIG5vdGhpbmcgd2FzIHJlbmRlcmVkIHdlIHdpbGwgbG9vayBhdCB0aGUgbmV4dCB7e2Vsc2V9fS4gT3RoZXJ3aXNlLCB3ZSBhcmUgZG9uZS5cblx0XHRcdFx0fVxuXHRcdFx0XHRyZXR1cm4gcmVzdWx0O1xuXHRcdFx0fSxcblx0XHRcdHNldERhdGFNYXA6IGZ1bmN0aW9uKHRhZ0N0eHMpIHtcblx0XHRcdFx0
dmFyIHRhZ0N0eCwgcHJvcHMsIHBhcmFtc1Byb3BzLFxuXHRcdFx0XHRcdHNlbGYgPSB0aGlzLFxuXHRcdFx0XHRcdGwgPSB0YWdDdHhzLmxlbmd0aDtcblx0XHRcdFx0d2hpbGUgKGwtLSkge1xuXHRcdFx0XHRcdHRhZ0N0eCA9IHRhZ0N0eHNbbF07XG5cdFx0XHRcdFx0cHJvcHMgPSB0YWdDdHgucHJvcHM7XG5cdFx0XHRcdFx0cGFyYW1zUHJvcHMgPSB0YWdDdHgucGFyYW1zLnByb3BzO1xuXHRcdFx0XHRcdHRhZ0N0eC5hcmdEZWZhdWx0ID0gcHJvcHMuZW5kID09PSB1bmRlZmluZWQgfHwgdGFnQ3R4LmFyZ3MubGVuZ3RoID4gMDsgLy8gRGVmYXVsdCB0byAjZGF0YSBleGNlcHQgZm9yIGF1dG8tY3JlYXRlIHJhbmdlIHNjZW5hcmlvIHt7Zm9yIHN0YXJ0PXh4eCBlbmQ9eXl5IHN0ZXA9enp6fX1cblx0XHRcdFx0XHRwcm9wcy5kYXRhTWFwID0gKHRhZ0N0eC5hcmdEZWZhdWx0ICE9PSBmYWxzZSAmJiAkaXNBcnJheSh0YWdDdHguYXJnc1swXSkgJiZcblx0XHRcdFx0XHRcdChwYXJhbXNQcm9wcy5zb3J0IHx8IHBhcmFtc1Byb3BzLnN0YXJ0IHx8IHBhcmFtc1Byb3BzLmVuZCB8fCBwYXJhbXNQcm9wcy5zdGVwIHx8IHBhcmFtc1Byb3BzLmZpbHRlciB8fCBwYXJhbXNQcm9wcy5yZXZlcnNlXG5cdFx0XHRcdFx0XHR8fCBwcm9wcy5zb3J0IHx8IHByb3BzLnN0YXJ0IHx8IHByb3BzLmVuZCB8fCBwcm9wcy5zdGVwIHx8IHByb3BzLmZpbHRlciB8fCBwcm9wcy5yZXZlcnNlKSlcblx0XHRcdFx0XHRcdCYmIHNlbGYuc29ydERhdGFNYXA7XG5cdFx0XHRcdH1
cblx0XHRcdH0sXG5cdFx0XHRmbG93OiB0cnVlXG5cdFx0fSxcblx0XHRwcm9wczoge1xuXHRcdFx0YmFzZVRhZzogXCJmb3JcIixcblx0XHRcdGRhdGFNYXA6IGRhdGFNYXAoZ2V0VGFyZ2V0UHJvcHMpLFxuXHRcdFx0aW5pdDogbm9vcCwgLy8gRG9uJ3QgZXhlY3V0ZSB0aGUgYmFzZSBpbml0KCkgb2YgdGhlIFwiZm9yXCIgdGFnXG5cdFx0XHRmbG93OiB0cnVlXG5cdFx0fSxcblx0XHRpbmNsdWRlOiB7XG5cdFx0XHRmbG93OiB0cnVlXG5cdFx0fSxcblx0XHRcIipcIjoge1xuXHRcdFx0Ly8ge3sqIGNvZGUuLi4gfX0gLSBJZ25vcmVkIGlmIHRlbXBsYXRlLmFsbG93Q29kZSBhbmQgJC52aWV3cy5zZXR0aW5ncy5hbGxvd0NvZGUgYXJlIGZhbHNlLiBPdGhlcndpc2UgaW5jbHVkZSBjb2RlIGluIGNvbXBpbGVkIHRlbXBsYXRlXG5cdFx0XHRyZW5kZXI6IHJldFZhbCxcblx0XHRcdGZsb3c6IHRydWVcblx0XHR9LFxuXHRcdFwiOipcIjoge1xuXHRcdFx0Ly8ge3s6KiByZXR1cm5lZEV4cHJlc3Npb24gfX0gLSBJZ25vcmVkIGlmIHRlbXBsYXRlLmFsbG93Q29kZSBhbmQgJC52aWV3cy5zZXR0aW5ncy5hbGxvd0NvZGUgYXJlIGZhbHNlLiBPdGhlcndpc2UgaW5jbHVkZSBjb2RlIGluIGNvbXBpbGVkIHRlbXBsYXRlXG5cdFx0XHRyZW5kZXI6IHJldFZhbCxcblx0XHRcdGZsb3c6IHRydWVcblx0XHR9LFxuXHRcdGRiZzogJGhlbHBlcnMuZGJnID0gJGNvbnZlcnRlcnMuZGJnID0gZGJnQnJlYWsgLy8gUmVnaXN0ZXIge3tkYmcvfX0sIHt7ZGJnOi4uLn19IG
FuZCB+ZGJnKCkgdG8gdGhyb3cgYW5kIGNhdGNoLCBhcyBicmVha3BvaW50cyBmb3IgZGVidWdnaW5nLlxuXHR9KTtcblxuXHQkY29udmVydGVycyh7XG5cdFx0aHRtbDogaHRtbEVuY29kZSxcblx0XHRhdHRyOiBodG1sRW5jb2RlLCAvLyBJbmNsdWRlcyA+IGVuY29kaW5nIHNpbmNlIHJDb252ZXJ0TWFya2VycyBpbiBKc1ZpZXdzIGRvZXMgbm90IHNraXAgPiBjaGFyYWN0ZXJzIGluIGF0dHJpYnV0ZSBzdHJpbmdzXG5cdFx0ZW5jb2RlOiBkYXRhRW5jb2RlLFxuXHRcdHVuZW5jb2RlOiBkYXRhVW5lbmNvZGUsIC8vIEluY2x1ZGVzID4gZW5jb2Rpbmcgc2luY2UgckNvbnZlcnRNYXJrZXJzIGluIEpzVmlld3MgZG9lcyBub3Qgc2tpcCA+IGNoYXJhY3RlcnMgaW4gYXR0cmlidXRlIHN0cmluZ3Ncblx0XHR1cmw6IGZ1bmN0aW9uKHRleHQpIHtcblx0XHRcdC8vIFVSTCBlbmNvZGluZyBoZWxwZXIuXG5cdFx0XHRyZXR1cm4gdGV4dCAhPSB1bmRlZmluZWQgPyBlbmNvZGVVUkkoXCJcIiArIHRleHQpIDogdGV4dCA9PT0gbnVsbCA/IHRleHQgOiBcIlwiOyAvLyBudWxsIHJldHVybnMgbnVsbCwgZS5nLiB0byByZW1vdmUgYXR0cmlidXRlLiB1bmRlZmluZWQgcmV0dXJucyBcIlwiXG5cdFx0fVxuXHR9KTtcbn1cbi8vPT09PT09PT09PT09PT09PT09PT09PT09PT0gRGVmaW5lIGRlZmF1bHQgZGVsaW1pdGVycyA9PT09PT09PT09PT09PT09PT09PT09PT09PVxuJHN1YlNldHRpbmdzID0gJHN1Yi5zZXR0aW5ncztcbiRpc0FycmF5ID0gKCR8fGpzcikuaXNBc
nJheTtcbiR2aWV3c1NldHRpbmdzLmRlbGltaXRlcnMoXCJ7e1wiLCBcIn19XCIsIFwiXlwiKTtcblxuaWYgKGpzclRvSnEpIHsgLy8gTW92aW5nIGZyb20ganNyZW5kZXIgbmFtZXNwYWNlIHRvIGpRdWVyeSBuYW1lcGFjZSAtIGNvcHkgb3ZlciB0aGUgc3RvcmVkIGl0ZW1zICh0ZW1wbGF0ZXMsIGNvbnZlcnRlcnMsIGhlbHBlcnMuLi4pXG5cdGpzci52aWV3cy5zdWIuX2pxKCQpO1xufVxucmV0dXJuICQgfHwganNyO1xufSwgd2luZG93KSk7XG4iLCIvKmdsb2JhbCBRVW5pdCwgdGVzdCwgZXF1YWwsIG9rKi9cbihmdW5jdGlvbih1bmRlZmluZWQpIHtcblwidXNlIHN0cmljdFwiO1xuXG5icm93c2VyaWZ5LmRvbmUudHdlbHZlID0gdHJ1ZTtcblxuUVVuaXQubW9kdWxlKFwiQnJvd3NlcmlmeSAtIGNsaWVudCBjb2RlXCIpO1xuXG52YXIgaXNJRTggPSB3aW5kb3cuYXR0YWNoRXZlbnQgJiYgIXdpbmRvdy5hZGRFdmVudExpc3RlbmVyO1xuXG5pZiAoIWlzSUU4KSB7XG5cbnRlc3QoXCJObyBqUXVlcnkgZ2xvYmFsOiByZXF1aXJlKCdqc3JlbmRlcicpKCkgbmVzdGVkIHRlbXBsYXRlXCIsIGZ1bmN0aW9uKCkge1xuXHQvLyAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uIEhpZGUgUVVuaXQgZ2xvYmFsIGpRdWVyeSBhbmQgYW55IHByZXZpb3VzIGdsb2JhbCBqc3JlbmRlci4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLlxuXHR2YXIgalF1ZXJ5ID0gZ2xvYmFsLmpRdWVyeSwganNyID0gZ2xvYmFsLmpzcmVuZGVyO1xuXHRnbG9iYWwu
alF1ZXJ5ID0gZ2xvYmFsLmpzcmVuZGVyID0gdW5kZWZpbmVkO1xuXG5cdC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gQXJyYW5nZSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cdHZhciBkYXRhID0ge25hbWU6IFwiSm9cIn07XG5cblx0Ly8gLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4gQWN0IC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi5cblx0dmFyIGpzcmVuZGVyID0gcmVxdWlyZSgnLi4vLi4vJykoKTsgLy8gTm90IHBhc3NpbmcgaW4galF1ZXJ5LCBzbyByZXR1cm5zIHRoZSBqc3JlbmRlciBuYW1lc3BhY2VcblxuXHQvLyBVc2UgcmVxdWlyZSB0byBnZXQgc2VydmVyIHRlbXBsYXRlLCB0aGFua3MgdG8gQnJvd3NlcmlmeSBidW5kbGUgdGhhdCB1c2VkIGpzcmVuZGVyL3RtcGxpZnkgdHJhbnNmb3JtXG5cdHZhciB0bXBsID0gcmVxdWlyZSgnLi4vdGVtcGxhdGVzL291dGVyLmh0bWwnKShqc3JlbmRlcik7IC8vIFByb3ZpZGUganNyZW5kZXJcblxuXHR2YXIgcmVzdWx0ID0gdG1wbChkYXRhKTtcblxuXHRyZXN1bHQgKz0gXCIgXCIgKyAoanNyZW5kZXIgIT09IGpRdWVyeSk7XG5cblx0Ly8gLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLiBBc3NlcnQgLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uXG5cdGVxdWFsKHJlc3VsdCwgXCJOYW1lOiBKbyAob3V0ZXIuaHRtbCkgTmFtZTogSm8gKGlubmVyLmh0bWwpIHRydWVcIiwgXCJyZXN1bHQ6IE5vIGpRdWVyeSBnbG9
iYWw6IHJlcXVpcmUoJ2pzcmVuZGVyJykoKSwgbmVzdGVkIHRlbXBsYXRlc1wiKTtcblxuXHQvLyAuLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uIFJlc2V0IC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLlxuXHRnbG9iYWwualF1ZXJ5ID0galF1ZXJ5OyAvLyBSZXBsYWNlIFFVbml0IGdsb2JhbCBqUXVlcnlcblx0Z2xvYmFsLmpzcmVuZGVyID0ganNyOyAvLyBSZXBsYWNlIGFueSBwcmV2aW91cyBnbG9iYWwganNyZW5kZXJcbn0pO1xufVxufSkoKTtcbiIsInZhciB0bXBsUmVmcyA9IFtdLFxuICBta3VwID0gJ05hbWU6IHt7Om5hbWV9fSAoaW5uZXIuaHRtbCknLFxuICAkID0gZ2xvYmFsLmpzcmVuZGVyIHx8IGdsb2JhbC5qUXVlcnk7XG5cbm1vZHVsZS5leHBvcnRzID0gJCA/ICQudGVtcGxhdGVzKFwiLi90ZXN0L3RlbXBsYXRlcy9pbm5lci5odG1sXCIsIG1rdXApIDpcbiAgZnVuY3Rpb24oJCkge1xuICAgIGlmICghJCB8fCAhJC52aWV3cykge3Rocm93IFwiUmVxdWlyZXMganNyZW5kZXIvalF1ZXJ5XCI7fVxuICAgIHdoaWxlICh0bXBsUmVmcy5sZW5ndGgpIHtcbiAgICAgIHRtcGxSZWZzLnBvcCgpKCQpOyAvLyBjb21waWxlIG5lc3RlZCB0ZW1wbGF0ZVxuICAgIH1cblxuICAgIHJldHVybiAkLnRlbXBsYXRlcyhcIi4vdGVzdC90ZW1wbGF0ZXMvaW5uZXIuaHRtbFwiLCBta3VwKVxuICB9OyIsInZhciB0bXBsUmVmcyA9IFtdLFxuICBta3VwID0gJ05hbWU6IHt7Om5hbWV9fSAob3V0ZXIuaHRtbCkge3tpbmNsdWRlIHRtcG
w9XFxcIi4vdGVzdC90ZW1wbGF0ZXMvaW5uZXIuaHRtbFxcXCIvfX0nLFxuICAkID0gZ2xvYmFsLmpzcmVuZGVyIHx8IGdsb2JhbC5qUXVlcnk7XG5cbnRtcGxSZWZzLnB1c2gocmVxdWlyZShcIi4vaW5uZXIuaHRtbFwiKSk7XG5tb2R1bGUuZXhwb3J0cyA9ICQgPyAkLnRlbXBsYXRlcyhcIi4vdGVzdC90ZW1wbGF0ZXMvb3V0ZXIuaHRtbFwiLCBta3VwKSA6XG4gIGZ1bmN0aW9uKCQpIHtcbiAgICBpZiAoISQgfHwgISQudmlld3MpIHt0aHJvdyBcIlJlcXVpcmVzIGpzcmVuZGVyL2pRdWVyeVwiO31cbiAgICB3aGlsZSAodG1wbFJlZnMubGVuZ3RoKSB7XG4gICAgICB0bXBsUmVmcy5wb3AoKSgkKTsgLy8gY29tcGlsZSBuZXN0ZWQgdGVtcGxhdGVcbiAgICB9XG5cbiAgICByZXR1cm4gJC50ZW1wbGF0ZXMoXCIuL3Rlc3QvdGVtcGxhdGVzL291dGVyLmh0bWxcIiwgbWt1cClcbiAgfTsiXX0=