brave
Version:
Old school web application library
898 lines (777 loc) • 61.3 kB
JavaScript
(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){
var Dom = require('../..')
var template = require('./login-box.html')
var app = {
initialize: function () {
console.log('initialize app')
document.title = this.data.name
},
somethingApp: function () {},
on: {
'click:a': function (e) {
console.log(e.target)
}
}
}
var loginBox = {
template: template
}
var archive = {
initialize: function (el, data) {
console.log('initialize archive', el, data)
},
changeLinkText: function (el) {
var res = window.prompt('What text should I be?', el.textContent)
if (res !== null) { // noop if `cancel` is clicked
el.textContent = res || '<Click to edit link text>'
}
},
on: {
'click:a': function (e) {
e.preventDefault()
this.changeLinkText(e.target)
}
}
}
var external = {
initialize: function () {
console.log('initialize external')
},
somethingExternal: function () {},
on: {
'click:a': function (e) {
e.preventDefault()
window.alert('External clicked')
}
}
}
var links = {
initialize: function () {
console.log('initialize links')
},
template: function () {
var s = '<ul>'
for (var i = 0; i < this.data.length; i++) {
s += '<li link="[' + i + ']"></li>'
}
s += '</ul>'
return s
},
on: {
'click:a': function (e) {
e.preventDefault()
window.alert('Links clicked')
}
}
}
var link = {
template: function () {
return '<a as="anchor">' + this.data + '</a>'
},
on: {
'click': function (e) {
e.preventDefault()
window.alert('Link clicked')
}
}
}
Dom.register({
'login-box': loginBox,
'root': {
on: {
'click': function (e) {
console.log('clicked html root')
}
}
},
'app': app,
'archive-history': archive,
'external-links': external,
'archive-history-list': {
somethingAHL: function () {
},
on: {
'click:a': function (e) {
e.preventDefault()
console.log('ahl click')
}
}
},
'links': links,
'link': link
})
window.onload = function () {
var data = {
name: 'Blog Example',
links: [1, 2, 3],
user: {
email: 'a@b.com',
password: 'secret'
}
}
Dom.scan(document.documentElement, data)
}
},{"../..":3,"./login-box.html":2}],2:[function(require,module,exports){
module.exports = function anonymous(obj
/**/) {
var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('<form> <legend>Login form</legend> Email: <input as="email"value="', data.email ,'" type="text" required><br> Password: <input as="password" value="', data.password ,'" type="password" required><br> <button as="button" type="submit">Login</button> </form> ');}return p.join('');
};
},{}],3:[function(require,module,exports){
var get = require('get-object-path')
var Delegate = require('dom-delegate').Delegate
var onEvents = Object.getOwnPropertyNames(document).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document)))).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(window))).filter(function (i) {return !i.indexOf('on') && (document[i] == null || typeof document[i] == 'function');}).filter(function (elem, pos, self) {return self.indexOf(elem) == pos;})
var onEventsSelector = onEvents.map(function (key) {
return '[' + key + ']:not([' + key + '=""])'
}).join(',')
function Register () {}
Object.defineProperties(Register.prototype, {
selector: {
get: function () {
var keys = Object.keys(this)
return keys.map(function (key) {
return '[' + key + ']'
}).join(', ')
}
},
keys: {
get: function () {
return Object.keys(this)
}
}
})
var register = new Register()
function createContext (el, data, component, parent) {
var ctx = Object.create(component.isolate ? {} : parent || {})
var info = Object.create({}, {
component: {
value: component
}
})
Object.defineProperties(ctx, {
__: {
value: info
},
el: {
value: el
}
})
ctx.data = data
return ctx
}
var ignore = ['on', 'template', 'initialize', 'isolate']
function extend (obj) {
Array.prototype.slice.call(arguments, 1).forEach(function (source) {
var descriptor, prop
if (source) {
for (prop in source) {
if (source.hasOwnProperty(prop) && ignore.indexOf(prop) === -1) {
descriptor = Object.getOwnPropertyDescriptor(source, prop)
Object.defineProperty(obj, prop, descriptor)
}
}
}
})
return obj
}
function getElementComponent (el) {
var registerKeys = register.keys
for (var i = 0; i < el.attributes.length; i++) {
var idx = registerKeys.indexOf(el.attributes[i].name)
if (idx > -1) {
return {
key: registerKeys[idx],
component: register[registerKeys[idx]]
}
}
}
}
function createElementDelegate (el, ctx, component) {
var del = new Delegate(el)
// Add event listeners
var proxy = function (fn) {
return function (e) {
fn.call(ctx, e)
}
}
for (var event in component.on) {
if (component.on.hasOwnProperty(event)) {
var colon = event.indexOf(':')
var name, selector
if (colon === -1) {
name = event
del.on(name, proxy(component.on[event]))
} else {
name = event.substr(0, colon)
selector = event.substr(colon + 1)
del.on(name, selector, proxy(component.on[event]))
}
}
}
return del
}
function getElementData (el, componentName, parent) {
var attr = el.getAttribute(componentName)
return attr && get(parent, attr)
}
function registerComponent (name, obj) {
if (typeof name === 'object') {
for (var key in name) {
if (name.hasOwnProperty(key)) {
register[key] = name[key]
}
}
} else {
register[name] = obj
}
}
function nodeListToArray (nodeList) {
var nodeArray = []
for (var i = 0; i < nodeList.length; i++) {
nodeArray.push(nodeList[i])
}
return nodeArray
}
function getMatchingElements (el, childrenOnly) {
var selector = Dom._register.selector
var matches = nodeListToArray(el.querySelectorAll(selector))
if (!childrenOnly) {
var component = getElementComponent(el)
if (component) {
matches.unshift(el)
}
}
return matches
}
function findParentContext (el, contexts) {
do {
el = el.parentNode
if (el) {
for (var i = contexts.length - 1; i > -1; i--) {
if (contexts[i].ctx.el === el) {
return contexts[i].ctx
}
}
}
} while (el)
}
function setHtml (el, component, ctx) {
var html = (typeof component.template === 'function')
? component.template.call(ctx, ctx)
: component.template
el.innerHTML = html
}
function renderer (currEl, component, ctx) {
return function () {
setHtml(currEl, component, ctx)
Dom.scan(currEl, ctx.data, ctx, true)
}
}
function scan (el, data, parent, childrenOnly) {
var matches = getMatchingElements(el, childrenOnly)
var contexts = []
if (parent) {
contexts.push({ctx: parent})
}
var currEl
while (matches.length) {
currEl = matches.shift()
var ref = getElementComponent(currEl)
var component = ref.component
var parentContext = findParentContext(currEl, contexts) || parent
var parentData = parentContext ? parentContext.data : data
var elData = getElementData(currEl, ref.key, parentData) || parentData
var ctx = createContext(currEl, elData, component, parentContext)
var del = createElementDelegate(currEl, ctx, component)
Object.defineProperty(ctx.__, 'del', { value: del })
extend(ctx, component)
contexts.push({
key: ref.key, ctx: ctx, initialize: component.initialize,
template: component.template, component: component, el: currEl
})
}
var i, j
var processed = []
for (i = contexts.length - 1; i >= 0; i--) {
var aliasContext = contexts[i].ctx
var aliasEl = aliasContext.el
var aliases = aliasEl.querySelectorAll('[as]:not([as=""])')
for (j = 0; j < aliases.length; j++) {
if (processed.indexOf(aliases[j]) < 0) {
var attr = aliases[j].getAttribute('as')
aliasContext[attr] = aliases[j]
processed.push(aliases[j])
}
}
}
// processed = []
// for (i = contexts.length - 1; i >= 0; i--) {
// var onContext = contexts[i].ctx
// var onEl = onContext.el
// var ons = onEl.querySelectorAll('[onclick]:not([onclick=""])')
// for (j = 0; j < ons.length; j++) {
// if (processed.indexOf(ons[j]) < 0) {
// attr = ons[j].getAttribute('onclick')
// // var fn = ons[j].onclick
// var fn = new Function('with (this) {\n\treturn ' + attr + '\n}')
// ons[j].onclick = fn.bind(onContext)
// processed.push(ons[j])
// }
// }
// }
processed = []
for (i = contexts.length - 1; i >= 0; i--) {
var onContext = contexts[i].ctx
var onEl = onContext.el
var ons = nodeListToArray(onEl.querySelectorAll(onEventsSelector))
ons.unshift(onEl)
for (j = 0; j < ons.length; j++) {
if (processed.indexOf(ons[j]) < 0) {
processed.push(ons[j])
for (var k = 0; k < onEvents.length; k++) {
if (ons[j].attributes[onEvents[k]]) {
attr = ons[j].attributes[onEvents[k]].value
// var fn = ons[j].onclick
// var fn = new Function('e', 'with (this) {\n\treturn ' + attr + '\n}')
// ons[j][onEvents[k]] = fn.bind(onContext)
function handler (fn, ctx) {
return function (e) {
return fn.call(this, e, ctx)
}
}
var fn = new Function('e, ctx', 'with (ctx) {\n\treturn ' + attr + '\n}')
ons[j][onEvents[k]] = handler(fn, onContext)
}
}
}
}
}
for (i = 0; i < contexts.length; i++) {
if (contexts[i].initialize) {
contexts[i].initialize.call(contexts[i].ctx)
}
}
for (i = 0; i < contexts.length; i++) {
if (contexts[i].template) {
var render = renderer(contexts[i].ctx.el, contexts[i].component, contexts[i].ctx)
render()
contexts[i].ctx.render = render
}
}
}
var Dom = Object.create({}, {
_register: { value: register },
register: { value: registerComponent },
scan: { value: scan }
})
module.exports = Dom
},{"dom-delegate":5,"get-object-path":6}],4:[function(require,module,exports){
/*jshint browser:true, node:true*/
'use strict';
module.exports = Delegate;
/**
* DOM event delegator
*
* The delegator will listen
* for events that bubble up
* to the root node.
*
* @constructor
* @param {Node|string} [root] The root node or a selector string matching the root node
*/
function Delegate(root) {
/**
* Maintain a map of listener
* lists, keyed by event name.
*
* @type Object
*/
this.listenerMap = [{}, {}];
if (root) {
this.root(root);
}
/** @type function() */
this.handle = Delegate.prototype.handle.bind(this);
}
/**
* Start listening for events
* on the provided DOM element
*
* @param {Node|string} [root] The root node or a selector string matching the root node
* @returns {Delegate} This method is chainable
*/
Delegate.prototype.root = function(root) {
var listenerMap = this.listenerMap;
var eventType;
// Remove master event listeners
if (this.rootElement) {
for (eventType in listenerMap[1]) {
if (listenerMap[1].hasOwnProperty(eventType)) {
this.rootElement.removeEventListener(eventType, this.handle, true);
}
}
for (eventType in listenerMap[0]) {
if (listenerMap[0].hasOwnProperty(eventType)) {
this.rootElement.removeEventListener(eventType, this.handle, false);
}
}
}
// If no root or root is not
// a dom node, then remove internal
// root reference and exit here
if (!root || !root.addEventListener) {
if (this.rootElement) {
delete this.rootElement;
}
return this;
}
/**
* The root node at which
* listeners are attached.
*
* @type Node
*/
this.rootElement = root;
// Set up master event listeners
for (eventType in listenerMap[1]) {
if (listenerMap[1].hasOwnProperty(eventType)) {
this.rootElement.addEventListener(eventType, this.handle, true);
}
}
for (eventType in listenerMap[0]) {
if (listenerMap[0].hasOwnProperty(eventType)) {
this.rootElement.addEventListener(eventType, this.handle, false);
}
}
return this;
};
/**
* @param {string} eventType
* @returns boolean
*/
Delegate.prototype.captureForType = function(eventType) {
return ['blur', 'error', 'focus', 'load', 'resize', 'scroll'].indexOf(eventType) !== -1;
};
/**
* Attach a handler to one
* event for all elements
* that match the selector,
* now or in the future
*
* The handler function receives
* three arguments: the DOM event
* object, the node that matched
* the selector while the event
* was bubbling and a reference
* to itself. Within the handler,
* 'this' is equal to the second
* argument.
*
* The node that actually received
* the event can be accessed via
* 'event.target'.
*
* @param {string} eventType Listen for these events
* @param {string|undefined} selector Only handle events on elements matching this selector, if undefined match root element
* @param {function()} handler Handler function - event data passed here will be in event.data
* @param {Object} [eventData] Data to pass in event.data
* @returns {Delegate} This method is chainable
*/
Delegate.prototype.on = function(eventType, selector, handler, useCapture) {
var root, listenerMap, matcher, matcherParam;
if (!eventType) {
throw new TypeError('Invalid event type: ' + eventType);
}
// handler can be passed as
// the second or third argument
if (typeof selector === 'function') {
useCapture = handler;
handler = selector;
selector = null;
}
// Fallback to sensible defaults
// if useCapture not set
if (useCapture === undefined) {
useCapture = this.captureForType(eventType);
}
if (typeof handler !== 'function') {
throw new TypeError('Handler must be a type of Function');
}
root = this.rootElement;
listenerMap = this.listenerMap[useCapture ? 1 : 0];
// Add master handler for type if not created yet
if (!listenerMap[eventType]) {
if (root) {
root.addEventListener(eventType, this.handle, useCapture);
}
listenerMap[eventType] = [];
}
if (!selector) {
matcherParam = null;
// COMPLEX - matchesRoot needs to have access to
// this.rootElement, so bind the function to this.
matcher = matchesRoot.bind(this);
// Compile a matcher for the given selector
} else if (/^[a-z]+$/i.test(selector)) {
matcherParam = selector;
matcher = matchesTag;
} else if (/^#[a-z0-9\-_]+$/i.test(selector)) {
matcherParam = selector.slice(1);
matcher = matchesId;
} else {
matcherParam = selector;
matcher = matches;
}
// Add to the list of listeners
listenerMap[eventType].push({
selector: selector,
handler: handler,
matcher: matcher,
matcherParam: matcherParam
});
return this;
};
/**
* Remove an event handler
* for elements that match
* the selector, forever
*
* @param {string} [eventType] Remove handlers for events matching this type, considering the other parameters
* @param {string} [selector] If this parameter is omitted, only handlers which match the other two will be removed
* @param {function()} [handler] If this parameter is omitted, only handlers which match the previous two will be removed
* @returns {Delegate} This method is chainable
*/
Delegate.prototype.off = function(eventType, selector, handler, useCapture) {
var i, listener, listenerMap, listenerList, singleEventType;
// Handler can be passed as
// the second or third argument
if (typeof selector === 'function') {
useCapture = handler;
handler = selector;
selector = null;
}
// If useCapture not set, remove
// all event listeners
if (useCapture === undefined) {
this.off(eventType, selector, handler, true);
this.off(eventType, selector, handler, false);
return this;
}
listenerMap = this.listenerMap[useCapture ? 1 : 0];
if (!eventType) {
for (singleEventType in listenerMap) {
if (listenerMap.hasOwnProperty(singleEventType)) {
this.off(singleEventType, selector, handler);
}
}
return this;
}
listenerList = listenerMap[eventType];
if (!listenerList || !listenerList.length) {
return this;
}
// Remove only parameter matches
// if specified
for (i = listenerList.length - 1; i >= 0; i--) {
listener = listenerList[i];
if ((!selector || selector === listener.selector) && (!handler || handler === listener.handler)) {
listenerList.splice(i, 1);
}
}
// All listeners removed
if (!listenerList.length) {
delete listenerMap[eventType];
// Remove the main handler
if (this.rootElement) {
this.rootElement.removeEventListener(eventType, this.handle, useCapture);
}
}
return this;
};
/**
* Handle an arbitrary event.
*
* @param {Event} event
*/
Delegate.prototype.handle = function(event) {
var i, l, type = event.type, root, phase, listener, returned, listenerList = [], target, /** @const */ EVENTIGNORE = 'ftLabsDelegateIgnore';
if (event[EVENTIGNORE] === true) {
return;
}
target = event.target;
// Hardcode value of Node.TEXT_NODE
// as not defined in IE8
if (target.nodeType === 3) {
target = target.parentNode;
}
root = this.rootElement;
phase = event.eventPhase || ( event.target !== event.currentTarget ? 3 : 2 );
switch (phase) {
case 1: //Event.CAPTURING_PHASE:
listenerList = this.listenerMap[1][type];
break;
case 2: //Event.AT_TARGET:
if (this.listenerMap[0] && this.listenerMap[0][type]) listenerList = listenerList.concat(this.listenerMap[0][type]);
if (this.listenerMap[1] && this.listenerMap[1][type]) listenerList = listenerList.concat(this.listenerMap[1][type]);
break;
case 3: //Event.BUBBLING_PHASE:
listenerList = this.listenerMap[0][type];
break;
}
// Need to continuously check
// that the specific list is
// still populated in case one
// of the callbacks actually
// causes the list to be destroyed.
l = listenerList.length;
while (target && l) {
for (i = 0; i < l; i++) {
listener = listenerList[i];
// Bail from this loop if
// the length changed and
// no more listeners are
// defined between i and l.
if (!listener) {
break;
}
// Check for match and fire
// the event if there's one
//
// TODO:MCG:20120117: Need a way
// to check if event#stopImmediatePropagation
// was called. If so, break both loops.
if (listener.matcher.call(target, listener.matcherParam, target)) {
returned = this.fire(event, target, listener);
}
// Stop propagation to subsequent
// callbacks if the callback returned
// false
if (returned === false) {
event[EVENTIGNORE] = true;
event.preventDefault();
return;
}
}
// TODO:MCG:20120117: Need a way to
// check if event#stopPropagation
// was called. If so, break looping
// through the DOM. Stop if the
// delegation root has been reached
if (target === root) {
break;
}
l = listenerList.length;
target = target.parentElement;
}
};
/**
* Fire a listener on a target.
*
* @param {Event} event
* @param {Node} target
* @param {Object} listener
* @returns {boolean}
*/
Delegate.prototype.fire = function(event, target, listener) {
return listener.handler.call(target, event, target);
};
/**
* Check whether an element
* matches a generic selector.
*
* @type function()
* @param {string} selector A CSS selector
*/
var matches = (function(el) {
if (!el) return;
var p = el.prototype;
return (p.matches || p.matchesSelector || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector);
}(Element));
/**
* Check whether an element
* matches a tag selector.
*
* Tags are NOT case-sensitive,
* except in XML (and XML-based
* languages such as XHTML).
*
* @param {string} tagName The tag name to test against
* @param {Element} element The element to test with
* @returns boolean
*/
function matchesTag(tagName, element) {
return tagName.toLowerCase() === element.tagName.toLowerCase();
}
/**
* Check whether an element
* matches the root.
*
* @param {?String} selector In this case this is always passed through as null and not used
* @param {Element} element The element to test with
* @returns boolean
*/
function matchesRoot(selector, element) {
/*jshint validthis:true*/
if (this.rootElement === window) return element === document;
return this.rootElement === element;
}
/**
* Check whether the ID of
* the element in 'this'
* matches the given ID.
*
* IDs are case-sensitive.
*
* @param {string} id The ID to test against
* @param {Element} element The element to test with
* @returns boolean
*/
function matchesId(id, element) {
return id === element.id;
}
/**
* Short hand for off()
* and root(), ie both
* with no parameters
*
* @return void
*/
Delegate.prototype.destroy = function() {
this.off();
this.root();
};
},{}],5:[function(require,module,exports){
/*jshint browser:true, node:true*/
'use strict';
/**
* @preserve Create and manage a DOM event delegator.
*
* @version 0.3.0
* @codingstandard ftlabs-jsv2
* @copyright The Financial Times Limited [All Rights Reserved]
* @license MIT License (see LICENSE.txt)
*/
var Delegate = require('./delegate');
module.exports = function(root) {
return new Delegate(root);
};
module.exports.Delegate = Delegate;
},{"./delegate":4}],6:[function(require,module,exports){
module.exports = get;
function get (context, path) {
if (path.indexOf('.') == -1 && path.indexOf('[') == -1) {
return context[path];
}
var crumbs = path.split(/\.|\[|\]/g);
var i = -1;
var len = crumbs.length;
var result;
while (++i < len) {
if (i == 0) result = context;
if (!crumbs[i]) continue;
if (result == undefined) break;
result = result[crumbs[i]];
}
return result;
}
},{}]},{},[1])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJleGFtcGxlcy9ibG9nL2luZGV4LmpzIiwiZXhhbXBsZXMvYmxvZy9sb2dpbi1ib3guaHRtbCIsImluZGV4LmpzIiwibm9kZV9tb2R1bGVzL2RvbS1kZWxlZ2F0ZS9saWIvZGVsZWdhdGUuanMiLCJub2RlX21vZHVsZXMvZG9tLWRlbGVnYXRlL2xpYi9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9nZXQtb2JqZWN0LXBhdGgvaW5kZXguanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDaElBO0FBQ0E7QUFDQTtBQUNBOztBQ0hBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1UkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN2FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsInZhciBEb20gPSByZXF1aXJlKCcuLi8uLicpXG52YXIgdGVtcGxhdGUgPSByZXF1aXJlKCcuL2xvZ2luLWJveC5odG1sJylcblxudmFyIGFwcCA9IHtcbiAgaW5pdGlhbGl6ZTogZnVuY3Rpb24gKCkge1xuICAgIGNvbnNvbGUubG9nKCdpbml0aWFsaXplIGFwcCcpXG4gICAgZG9jdW1lbnQudGl0bGUgPSB0aGlzLmRhdGEubmFtZVxuICB9LFxuXG4gIHNvbWV0aGluZ0FwcDogZnVuY3Rpb24gKCkge30sXG5cbiAgb246IHtcbiAgICAnY2xpY2s6YSc6IGZ1bmN0aW9uIChlKSB7XG4gICAgICBjb25zb2xlLmxvZyhlLnRhcmdldClcbiAgICB9XG4gIH1cbn1cblxudmFyIGxvZ2luQm94ID0ge1xuICB0ZW1wbGF0ZTogdGVtcGxhdGVcbn1cblxudmFyIGFyY2hpdmUgPSB7XG4gIGluaXRpYWxpemU6IGZ1bmN0aW9uIChlbCwgZGF0YSkge1xuICAgIGNvbnNvbGUubG9nKCdpbml0aWFsaXplIGFyY2hpdmUnLCBlbCwgZGF0YSlcbiAgfSxcbiAgY2hhbmdlTGlua1RleHQ6IGZ1bmN0aW9uIChlbCkge1xuICAgIHZhciByZXMgPSB3aW5kb3cucHJvbXB0KCdXaGF0IHRleHQgc2hvdWxkIEkgYmU/JywgZWwudGV4dENvbnRlbnQpXG4gICAgaWYgKHJlcyAhPT0gbnVsbCkgeyAvLyBub29wIGlmIGBjYW5jZWxgIGlzIGNsaWNrZWRcbiAgICAgIGVsLnRleHRDb250ZW50ID0gcmVzIHx8ICc8Q2xpY2sgdG8gZWRpdCBsaW5rIHRleHQ+J1xuICAgIH1cbiAgfSxcbiAgb246IHtcbiAgICAnY2xpY2s6YSc6IGZ1bmN0aW9uIChlKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIHRoaXMuY2hhbmdlTGlua1RleHQoZS50YXJnZXQpXG4gICAgfVxuICB9XG59XG5cbnZhciBleHRlcm5hbCA9IHtcbiAgaW5pdGlhbGl6ZTogZnVuY3Rpb24gKCkge1xuICAgIGNvbnNvbGUubG9nKCdpbml0aWFsaXplIGV4dGVybmFsJylcbiAgfSxcblxuICBzb21ldGhpbmdFeHRlcm5hbDogZnVuY3Rpb24gKCkge30sXG5cbiAgb246IHtcbiAgICAnY2xpY2s6YSc6IGZ1bmN0aW9uIChlKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIHdpbmRvdy5hbGVydCgnRXh0ZXJuYWwgY2xpY2tlZCcpXG4gICAgfVxuICB9XG59XG5cbnZhciBsaW5rcyA9IHtcbiAgaW5pdGlhbGl6ZTogZnVuY3Rpb24gKCkge1xuICAgIGNvbnNvbGUubG9nKCdpbml0aWFsaXplIGxpbmtzJylcbiAgfSxcblxuICB0ZW1wbGF0ZTogZnVuY3Rpb24gKCkge1xuICAgIHZhciBzID0gJzx1bD4nXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLmRhdGEubGVuZ3RoOyBpKyspIHtcbiAgICAgIHMgKz0gJzxsaSBsaW5rPVwiWycgKyBpICsgJ11cIj48L2xpPidcbiAgICB9XG4gICAgcyArPSAnPC91bD4nXG4gICAgcmV0dXJuIHNcbiAgfSxcblxuICBvbjoge1xuICAgICdjbGljazphJzogZnVuY3Rpb24gKGUpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgd2luZG93LmFsZXJ0KCdMaW5rcyBjbGlja2VkJylcbiAgICB9XG4gIH1cbn1cblxudmFyIGxpbmsgPSB7XG4gIHRlbXBsYXRlOiBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuICc8YSBhcz1cImFuY2hvclwiPicgKyB0aGlzLmRhdGEgKyAnPC9hPidcbiAgfSxcblxuICBvbjoge1xuICAgICdjbGljayc6IGZ1bmN0aW9uIChlKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIHdpbmRvdy5hbGVydCgnTGluayBjbGlja2VkJylcbiAgICB9XG4gIH1cbn1cblxuRG9tLnJlZ2lzdGVyKHtcbiAgJ2xvZ2luLWJveCc6IGxvZ2luQm94LFxuICAncm9vdCc6IHtcbiAgICBvbjoge1xuICAgICAgJ2NsaWNrJzogZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgY29uc29sZS5sb2coJ2NsaWNrZWQgaHRtbCByb290JylcbiAgICAgIH1cbiAgICB9XG4gIH0sXG4gICdhcHAnOiBhcHAsXG4gICdhcmNoaXZlLWhpc3RvcnknOiBhcmNoaXZlLFxuICAnZXh0ZXJuYWwtbGlua3MnOiBleHRlcm5hbCxcbiAgJ2FyY2hpdmUtaGlzdG9yeS1saXN0Jzoge1xuICAgIHNvbWV0aGluZ0FITDogZnVuY3Rpb24gKCkge1xuICAgIH0sXG4gICAgb246IHtcbiAgICAgICdjbGljazphJzogZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgIGNvbnNvbGUubG9nKCdhaGwgY2xpY2snKVxuICAgICAgfVxuICAgIH1cbiAgfSxcbiAgJ2xpbmtzJzogbGlua3MsXG4gICdsaW5rJzogbGlua1xufSlcblxud2luZG93Lm9ubG9hZCA9IGZ1bmN0aW9uICgpIHtcbiAgdmFyIGRhdGEgPSB7XG4gICAgbmFtZTogJ0Jsb2cgRXhhbXBsZScsXG4gICAgbGlua3M6IFsxLCAyLCAzXSxcbiAgICB1c2VyOiB7XG4gICAgICBlbWFpbDogJ2FAYi5jb20nLFxuICAgICAgcGFzc3dvcmQ6ICdzZWNyZXQnXG4gICAgfVxuICB9XG5cbiAgRG9tLnNjYW4oZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LCBkYXRhKVxufVxuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBhbm9ueW1vdXMob2JqXG4vKiovKSB7XG52YXIgcD1bXSxwcmludD1mdW5jdGlvbigpe3AucHVzaC5hcHBseShwLGFyZ3VtZW50cyk7fTt3aXRoKG9iail7cC5wdXNoKCc8Zm9ybT4gICA8bGVnZW5kPkxvZ2luIGZvcm08L2xlZ2VuZD4gICBFbWFpbDogPGlucHV0IGFzPVwiZW1haWxcInZhbHVlPVwiJywgZGF0YS5lbWFpbCAsJ1wiIHR5cGU9XCJ0ZXh0XCIgcmVxdWlyZWQ+PGJyPiAgIFBhc3N3b3JkOiA8aW5wdXQgYXM9XCJwYXNzd29yZFwiIHZhbHVlPVwiJywgZGF0YS5wYXNzd29yZCAsJ1wiIHR5cGU9XCJwYXNzd29yZFwiIHJlcXVpcmVkPjxicj4gICA8YnV0dG9uIGFzPVwiYnV0dG9uXCIgdHlwZT1cInN1Ym1pdFwiPkxvZ2luPC9idXR0b24+IDwvZm9ybT4gJyk7fXJldHVybiBwLmpvaW4oJycpO1xufTsiLCJ2YXIgZ2V0ID0gcmVxdWlyZSgnZ2V0LW9iamVjdC1wYXRoJylcbnZhciBEZWxlZ2F0ZSA9IHJlcXVpcmUoJ2RvbS1kZWxlZ2F0ZScpLkRlbGVnYXRlXG52YXIgb25FdmVudHMgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyhkb2N1bWVudCkuY29uY2F0KE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKE9iamVjdC5nZXRQcm90b3R5cGVPZihPYmplY3QuZ2V0UHJvdG90eXBlT2YoZG9jdW1lbnQpKSkpLmNvbmNhdChPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyhPYmplY3QuZ2V0UHJvdG90eXBlT2Yod2luZG93KSkpLmZpbHRlcihmdW5jdGlvbiAoaSkge3JldHVybiAhaS5pbmRleE9mKCdvbicpICYmIChkb2N1bWVudFtpXSA9PSBudWxsIHx8IHR5cGVvZiBkb2N1bWVudFtpXSA9PSAnZnVuY3Rpb24nKTt9KS5maWx0ZXIoZnVuY3Rpb24gKGVsZW0sIHBvcywgc2VsZikge3JldHVybiBzZWxmLmluZGV4T2YoZWxlbSkgPT0gcG9zO30pXG52YXIgb25FdmVudHNTZWxlY3RvciA9IG9uRXZlbnRzLm1hcChmdW5jdGlvbiAoa2V5KSB7XG4gIHJldHVybiAnWycgKyBrZXkgKyAnXTpub3QoWycgKyBrZXkgKyAnPVwiXCJdKSdcbn0pLmpvaW4oJywnKVxuXG5mdW5jdGlvbiBSZWdpc3RlciAoKSB7fVxuT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoUmVnaXN0ZXIucHJvdG90eXBlLCB7XG4gIHNlbGVjdG9yOiB7XG4gICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICB2YXIga2V5cyA9IE9iamVjdC5rZXlzKHRoaXMpXG4gICAgICByZXR1cm4ga2V5cy5tYXAoZnVuY3Rpb24gKGtleSkge1xuICAgICAgICByZXR1cm4gJ1snICsga2V5ICsgJ10nXG4gICAgICB9KS5qb2luKCcsICcpXG4gICAgfVxuICB9LFxuICBrZXlzOiB7XG4gICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICByZXR1cm4gT2JqZWN0LmtleXModGhpcylcbiAgICB9XG4gIH1cbn0pXG5cbnZhciByZWdpc3RlciA9IG5ldyBSZWdpc3RlcigpXG5cbmZ1bmN0aW9uIGNyZWF0ZUNvbnRleHQgKGVsLCBkYXRhLCBjb21wb25lbnQsIHBhcmVudCkge1xuICB2YXIgY3R4ID0gT2JqZWN0LmNyZWF0ZShjb21wb25lbnQuaXNvbGF0ZSA/IHt9IDogcGFyZW50IHx8IHt9KVxuXG4gIHZhciBpbmZvID0gT2JqZWN0LmNyZWF0ZSh7fSwge1xuICAgIGNvbXBvbmVudDoge1xuICAgICAgdmFsdWU6IGNvbXBvbmVudFxuICAgIH1cbiAgfSlcblxuICBPYmplY3QuZGVmaW5lUHJvcGVydGllcyhjdHgsIHtcbiAgICBfXzoge1xuICAgICAgdmFsdWU6IGluZm9cbiAgICB9LFxuICAgIGVsOiB7XG4gICAgICB2YWx1ZTogZWxcbiAgICB9XG4gIH0pXG5cbiAgY3R4LmRhdGEgPSBkYXRhXG5cbiAgcmV0dXJuIGN0eFxufVxuXG52YXIgaWdub3JlID0gWydvbicsICd0ZW1wbGF0ZScsICdpbml0aWFsaXplJywgJ2lzb2xhdGUnXVxuZnVuY3Rpb24gZXh0ZW5kIChvYmopIHtcbiAgQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKS5mb3JFYWNoKGZ1bmN0aW9uIChzb3VyY2UpIHtcbiAgICB2YXIgZGVzY3JpcHRvciwgcHJvcFxuICAgIGlmIChzb3VyY2UpIHtcbiAgICAgIGZvciAocHJvcCBpbiBzb3VyY2UpIHtcbiAgICAgICAgaWYgKHNvdXJjZS5oYXNPd25Qcm9wZXJ0eShwcm9wKSAmJiBpZ25vcmUuaW5kZXhPZihwcm9wKSA9PT0gLTEpIHtcbiAgICAgICAgICBkZXNjcmlwdG9yID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihzb3VyY2UsIHByb3ApXG4gICAgICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KG9iaiwgcHJvcCwgZGVzY3JpcHRvcilcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSlcbiAgcmV0dXJuIG9ialxufVxuXG5mdW5jdGlvbiBnZXRFbGVtZW50Q29tcG9uZW50IChlbCkge1xuICB2YXIgcmVnaXN0ZXJLZXlzID0gcmVnaXN0ZXIua2V5c1xuICBmb3IgKHZhciBpID0gMDsgaSA8IGVsLmF0dHJpYnV0ZXMubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgaWR4ID0gcmVnaXN0ZXJLZXlzLmluZGV4T2YoZWwuYXR0cmlidXRlc1tpXS5uYW1lKVxuICAgIGlmIChpZHggPiAtMSkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAga2V5OiByZWdpc3RlcktleXNbaWR4XSxcbiAgICAgICAgY29tcG9uZW50OiByZWdpc3RlcltyZWdpc3RlcktleXNbaWR4XV1cbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gY3JlYXRlRWxlbWVudERlbGVnYXRlIChlbCwgY3R4LCBjb21wb25lbnQpIHtcbiAgdmFyIGRlbCA9IG5ldyBEZWxlZ2F0ZShlbClcblxuICAvLyBBZGQgZXZlbnQgbGlzdGVuZXJzXG4gIHZhciBwcm94eSA9IGZ1bmN0aW9uIChmbikge1xuICAgIHJldHVybiBmdW5jdGlvbiAoZSkge1xuICAgICAgZm4uY2FsbChjdHgsIGUpXG4gICAgfVxuICB9XG5cbiAgZm9yICh2YXIgZXZlbnQgaW4gY29tcG9uZW50Lm9uKSB7XG4gICAgaWYgKGNvbXBvbmVudC5vbi5oYXNPd25Qcm9wZXJ0eShldmVudCkpIHtcbiAgICAgIHZhciBjb2xvbiA9IGV2ZW50LmluZGV4T2YoJzonKVxuICAgICAgdmFyIG5hbWUsIHNlbGVjdG9yXG4gICAgICBpZiAoY29sb24gPT09IC0xKSB7XG4gICAgICAgIG5hbWUgPSBldmVudFxuICAgICAgICBkZWwub24obmFtZSwgcHJveHkoY29tcG9uZW50Lm9uW2V2ZW50XSkpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBuYW1lID0gZXZlbnQuc3Vic3RyKDAsIGNvbG9uKVxuICAgICAgICBzZWxlY3RvciA9IGV2ZW50LnN1YnN0cihjb2xvbiArIDEpXG4gICAgICAgIGRlbC5vbihuYW1lLCBzZWxlY3RvciwgcHJveHkoY29tcG9uZW50Lm9uW2V2ZW50XSkpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGRlbFxufVxuXG5mdW5jdGlvbiBnZXRFbGVtZW50RGF0YSAoZWwsIGNvbXBvbmVudE5hbWUsIHBhcmVudCkge1xuICB2YXIgYXR0ciA9IGVsLmdldEF0dHJpYnV0ZShjb21wb25lbnROYW1lKVxuICByZXR1cm4gYXR0ciAmJiBnZXQocGFyZW50LCBhdHRyKVxufVxuXG5mdW5jdGlvbiByZWdpc3RlckNvbXBvbmVudCAobmFtZSwgb2JqKSB7XG4gIGlmICh0eXBlb2YgbmFtZSA9PT0gJ29iamVjdCcpIHtcbiAgICBmb3IgKHZhciBrZXkgaW4gbmFtZSkge1xuICAgICAgaWYgKG5hbWUuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgICByZWdpc3RlcltrZXldID0gbmFtZVtrZXldXG4gICAgICB9XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIHJlZ2lzdGVyW25hbWVdID0gb2JqXG4gIH1cbn1cblxuZnVuY3Rpb24gbm9kZUxpc3RUb0FycmF5IChub2RlTGlzdCkge1xuICB2YXIgbm9kZUFycmF5ID0gW11cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBub2RlTGlzdC5sZW5ndGg7IGkrKykge1xuICAgIG5vZGVBcnJheS5wdXNoKG5vZGVMaXN0W2ldKVxuICB9XG5cbiAgcmV0dXJuIG5vZGVBcnJheVxufVxuXG5mdW5jdGlvbiBnZXRNYXRjaGluZ0VsZW1lbnRzIChlbCwgY2hpbGRyZW5Pbmx5KSB7XG4gIHZhciBzZWxlY3RvciA9IERvbS5fcmVnaXN0ZXIuc2VsZWN0b3JcbiAgdmFyIG1hdGNoZXMgPSBub2RlTGlzdFRvQXJyYXkoZWwucXVlcnlTZWxlY3RvckFsbChzZWxlY3RvcikpXG5cbiAgaWYgKCFjaGlsZHJlbk9ubHkpIHtcbiAgICB2YXIgY29tcG9uZW50ID0gZ2V0RWxlbWVudENvbXBvbmVudChlbClcblxuICAgIGlmIChjb21wb25lbnQpIHtcbiAgICAgIG1hdGNoZXMudW5zaGlmdChlbClcbiAgICB9XG4gIH1cblxuICByZXR1cm4gbWF0Y2hlc1xufVxuXG5mdW5jdGlvbiBmaW5kUGFyZW50Q29udGV4dCAoZWwsIGNvbnRleHRzKSB7XG4gIGRvIHtcbiAgICBlbCA9IGVsLnBhcmVudE5vZGVcbiAgICBpZiAoZWwpIHtcbiAgICAgIGZvciAodmFyIGkgPSBjb250ZXh0cy5sZW5ndGggLSAxOyBpID4gLTE7IGktLSkge1xuICAgICAgICBpZiAoY29udGV4dHNbaV0uY3R4LmVsID09PSBlbCkge1xuICAgICAgICAgIHJldHVybiBjb250ZXh0c1tpXS5jdHhcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSB3aGlsZSAoZWwpXG4gIH1cblxuICBmdW5jdGlvbiBzZXRIdG1sIChlbCwgY29tcG9uZW50LCBjdHgpIHtcbiAgICB2YXIgaHRtbCA9ICh0eXBlb2YgY29tcG9uZW50LnRlbXBsYXRlID09PSAnZnVuY3Rpb24nKVxuICAgICAgPyBjb21wb25lbnQudGVtcGxhdGUuY2FsbChjdHgsIGN0eClcbiAgICAgIDogY29tcG9uZW50LnRlbXBsYXRlXG5cbiAgICBlbC5pbm5lckhUTUwgPSBodG1sXG4gIH1cblxuICBmdW5jdGlvbiByZW5kZXJlciAoY3VyckVsLCBjb21wb25lbnQsIGN0eCkge1xuICAgIHJldHVybiBmdW5jdGlvbiAoKSB7XG4gICAgICBzZXRIdG1sKGN1cnJFbCwgY29tcG9uZW50LCBjdHgpXG4gICAgICBEb20uc2NhbihjdXJyRWwsIGN0eC5kYXRhLCBjdHgsIHRydWUpXG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gc2NhbiAoZWwsIGRhdGEsIHBhcmVudCwgY2hpbGRyZW5Pbmx5KSB7XG4gICAgdmFyIG1hdGNoZXMgPSBnZXRNYXRjaGluZ0VsZW1lbnRzKGVsLCBjaGlsZHJlbk9ubHkpXG4gICAgdmFyIGNvbnRleHRzID0gW11cbiAgICBpZiAocGFyZW50KSB7XG4gICAgICBjb250ZXh0cy5wdXNoKHtjdHg6IHBhcmVudH0pXG4gICAgfVxuXG4gICAgdmFyIGN1cnJFbFxuICAgIHdoaWxlIChtYXRjaGVzLmxlbmd0aCkge1xuICAgICAgY3VyckVsID0gbWF0Y2hlcy5zaGlmdCgpXG4gICAgICB2YXIgcmVmID0gZ2V0RWxlbWVudENvbXBvbmVudChjdXJyRWwpXG4gICAgICB2YXIgY29tcG9uZW50ID0gcmVmLmNvbXBvbmVudFxuICAgICAgdmFyIHBhcmVudENvbnRleHQgPSBmaW5kUGFyZW50Q29udGV4dChjdXJyRWwsIGNvbnRleHRzKSB8fCBwYXJlbnRcbiAgICAgIHZhciBwYXJlbnREYXRhID0gcGFyZW50Q29udGV4dCA/IHBhcmVudENvbnRleHQuZGF0YSA6IGRhdGFcbiAgICAgIHZhciBlbERhdGEgPSBnZXRFbGVtZW50RGF0YShjdXJyRWwsIHJlZi5rZXksIHBhcmVudERhdGEpIHx8IHBhcmVudERhdGFcbiAgICAgIHZhciBjdHggPSBjcmVhdGVDb250ZXh0KGN1cnJFbCwgZWxEYXRhLCBjb21wb25lbnQsIHBhcmVudENvbnRleHQpXG4gICAgICB2YXIgZGVsID0gY3JlYXRlRWxlbWVudERlbGVnYXRlKGN1cnJFbCwgY3R4LCBjb21wb25lbnQpXG5cbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdHguX18sICdkZWwnLCB7IHZhbHVlOiBkZWwgfSlcblxuICAgICAgZXh0ZW5kKGN0eCwgY29tcG9uZW50KVxuXG4gICAgICBjb250ZXh0cy5wdXNoKHtcbiAgICAgICAga2V5OiByZWYua2V5LCBjdHg6IGN0eCwgaW5pdGlhbGl6ZTogY29tcG9uZW50LmluaXRpYWxpemUsXG4gICAgICAgIHRlbXBsYXRlOiBjb21wb25lbnQudGVtcGxhdGUsIGNvbXBvbmVudDogY29tcG9uZW50LCBlbDogY3VyckVsXG4gICAgICB9KVxuICAgIH1cblxuICAgIHZhciBpLCBqXG4gICAgdmFyIHByb2Nlc3NlZCA9IFtdXG4gICAgZm9yIChpID0gY29udGV4dHMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHtcbiAgICAgIHZhciBhbGlhc0NvbnRleHQgPSBjb250ZXh0c1tpXS5jdHhcbiAgICAgIHZhciBhbGlhc0VsID0gYWxpYXNDb250ZXh0LmVsXG4gICAgICB2YXIgYWxpYXNlcyA9IGFsaWFzRWwucXVlcnlTZWxlY3RvckFsbCgnW2FzXTpub3QoW2FzPVwiXCJdKScpXG4gICAgICBmb3IgKGogPSAwOyBqIDwgYWxpYXNlcy5sZW5ndGg7IGorKykge1xuICAgICAgICBpZiAocHJvY2Vzc2VkLmluZGV4T2YoYWxpYXNlc1tqXSkgPCAwKSB7XG4gICAgICAgICAgdmFyIGF0dHIgPSBhbGlhc2VzW2pdLmdldEF0dHJpYnV0ZSgnYXMnKVxuICAgICAgICAgIGFsaWFzQ29udGV4dFthdHRyXSA9IGFsaWFzZXNbal1cbiAgICAgICAgICBwcm9jZXNzZWQucHVzaChhbGlhc2VzW2pdKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gcHJvY2Vzc2VkID0gW11cbiAgICAvLyBmb3IgKGkgPSBjb250ZXh0cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xuICAgIC8vICAgdmFyIG9uQ29udGV4dCA9IGNvbnRleHRzW2ldLmN0eFxuICAgIC8vICAgdmFyIG9uRWwgPSBvbkNvbnRleHQuZWxcbiAgICAvLyAgIHZhciBvbnMgPSBvbkVsLnF1ZXJ5U2VsZWN0b3JBbGwoJ1tvbmNsaWNrXTpub3QoW29uY2xpY2s9XCJcIl0pJylcbiAgICAvLyAgIGZvciAoaiA9IDA7IGogPCBvbnMubGVuZ3RoOyBqKyspIHtcbiAgICAvLyAgICAgaWYgKHByb2Nlc3NlZC5pbmRleE9mKG9uc1tqXSkgPCAwKSB7XG4gICAgLy8gICAgICAgYXR0ciA9IG9uc1tqXS5nZXRBdHRyaWJ1dGUoJ29uY2xpY2snKVxuICAgIC8vICAgICAgIC8vIHZhciBmbiA9IG9uc1tqXS5vbmNsaWNrXG4gICAgLy8gICAgICAgdmFyIGZuID0gbmV3IEZ1bmN0aW9uKCd3aXRoICh0aGlzKSB7XFxuXFx0cmV0dXJuICcgKyBhdHRyICsgJ1xcbn0nKVxuICAgIC8vICAgICAgIG9uc1tqXS5vbmNsaWNrID0gZm4uYmluZChvbkNvbnRleHQpXG4gICAgLy8gICAgICAgcHJvY2Vzc2VkLnB1c2gob25zW2pdKVxuICAgIC8vICAgICB9XG4gICAgLy8gICB9XG4gICAgLy8gfVxuICAgIHByb2Nlc3NlZCA9IFtdXG4gICAgZm9yIChpID0gY29udGV4dHMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHtcbiAgICAgIHZhciBvbkNvbnRleHQgPSBjb250ZXh0c1tpXS5jdHhcbiAgICAgIHZhciBvbkVsID0gb25Db250ZXh0LmVsXG4gICAgICB2YXIgb25zID0gbm9kZUxpc3RUb0FycmF5KG9uRWwucXVlcnlTZWxlY3RvckFsbChvbkV2ZW50c1NlbGVjdG9yKSlcbiAgICAgIG9ucy51bnNoaWZ0KG9uRWwpXG4gICAgICBmb3IgKGogPSAwOyBqIDwgb25zLmxlbmd0aDsgaisrKSB7XG4gICAgICAgIGlmIChwcm9jZXNzZWQuaW5kZXhPZihvbnNbal0pIDwgMCkge1xuICAgICAgICAgIHByb2Nlc3NlZC5wdXNoKG9uc1tqXSlcbiAgICAgICAgICBmb3IgKHZhciBrID0gMDsgayA8IG9uRXZlbnRzLmxlbmd0aDsgaysrKSB7XG4gICAgICAgICAgICBpZiAob25zW2pdLmF0dHJpYnV0ZXNbb25FdmVudHNba11dKSB7XG4gICAgICAgICAgICAgIGF0dHIgPSBvbnNbal0uYXR0cmlidXRlc1tvbkV2ZW50c1trXV0udmFsdWVcbiAgICAgICAgICAgICAgLy8gdmFyIGZuID0gb25zW2pdLm9uY2xpY2tcbiAgICAgICAgICAgICAgLy8gdmFyIGZuID0gbmV3IEZ1bmN0aW9uKCdlJywgJ3dpdGggKHRoaXMpIHtcXG5cXHRyZXR1cm4gJyArIGF0dHIgKyAnXFxufScpXG4gICAgICAgICAgICAgIC8vIG9uc1tqXVtvbkV2ZW50c1trXV0gPSBmbi5iaW5kKG9uQ29udGV4dClcbiAgICAgICAgICAgICAgZnVuY3Rpb24gaGFuZGxlciAoZm4sIGN0eCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBmdW5jdGlvbiAoZSkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIGZuLmNhbGwodGhpcywgZSwgY3R4KVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHZhciBmbiA9IG5ldyBGdW5jdGlvbignZSwgY3R4JywgJ3dpdGggKGN0eCkge1xcblxcdHJldHVybiAnICsgYXR0ciArICdcXG59JylcbiAgICAgICAgICAgICAgb25zW2pdW29uRXZlbnRzW2tdXSA9IGhhbmRsZXIoZm4sIG9uQ29udGV4dClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgY29udGV4dHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChjb250ZXh0c1tpXS5pbml0aWFsaXplKSB7XG4gICAgICAgIGNvbnRleHRzW2ldLmluaXRpYWxpemUuY2FsbChjb250ZXh0c1tpXS5jdHgpXG4gICAgICB9XG4gICAgfVxuXG4gICAgZm9yIChpID0gMDsgaSA8IGNvbnRleHRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAoY29udGV4dHNbaV0udGVtcGxhdGUpIHtcbiAgICAgICAgdmFyIHJlbmRlciA9IHJlbmRlcmVyKGNvbnRleHRzW2ldLmN0eC5lbCwgY29udGV4dHNbaV0uY29tcG9uZW50LCBjb250ZXh0c1tpXS5jdHgpXG4gICAgICAgIHJlbmRlcigpXG4gICAgICAgIGNvbnRleHRzW2ldLmN0eC5yZW5kZXIgPSByZW5kZXJcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICB2YXIgRG9tID0gT2JqZWN0LmNyZWF0ZSh7fSwge1xuICAgIF9yZWdpc3RlcjogeyB2YWx1ZTogcmVnaXN0ZXIgfSxcbiAgICByZWdpc3RlcjogeyB2YWx1ZTogcmVnaXN0ZXJDb21wb25lbnQgfSxcbiAgICBzY2FuOiB7IHZhbHVlOiBzY2FuIH1cbiAgfSlcblxuICBtb2R1bGUuZXhwb3J0cyA9IERvbVxuIiwiLypqc2hpbnQgYnJvd3Nlcjp0cnVlLCBub2RlOnRydWUqL1xuXG4ndXNlIHN0cmljdCc7XG5cbm1vZHVsZS5leHBvcnRzID0gRGVsZWdhdGU7XG5cbi8qKlxuICogRE9NIGV2ZW50IGRlbGVnYXRvclxuICpcbiAqIFRoZSBkZWxlZ2F0b3Igd2lsbCBsaXN0ZW5cbiAqIGZvciBldmVudHMgdGhhdCBidWJibGUgdXBcbiAqIHRvIHRoZSByb290IG5vZGUuXG4gKlxuICogQGNvbnN0cnVjdG9yXG4gKiBAcGFyYW0ge05vZGV8c3RyaW5nfSBbcm9vdF0gVGhlIHJvb3Qgbm9kZSBvciBhIHNlbGVjdG9yIHN0cmluZyBtYXRjaGluZyB0aGUgcm9vdCBub2RlXG4gKi9cbmZ1bmN0aW9uIERlbGVnYXRlKHJvb3QpIHtcblxuICAvKipcbiAgICogTWFpbnRhaW4gYSBtYXAgb2YgbGlzdGVuZXJcbiAgICogbGlzdHMsIGtleWVkIGJ5IGV2ZW50IG5hbWUuXG4gICAqXG4gICAqIEB0eXBlIE9iamVjdFxuICAgKi9cbiAgdGhpcy5saXN0ZW5lck1hcCA9IFt7fSwge31dO1xuICBpZiAocm9vdCkge1xuICAgIHRoaXMucm9vdChyb290KTtcbiAgfVxuXG4gIC8qKiBAdHlwZSBmdW5jdGlvbigpICovXG4gIHRoaXMuaGFuZGxlID0gRGVsZWdhdGUucHJvdG90eXBlLmhhbmRsZS5iaW5kKHRoaXMpO1xufVxuXG4vKipcbiAqIFN0YXJ0IGxpc3RlbmluZyBmb3IgZXZlbnRzXG4gKiBvbiB0aGUgcHJvdmlkZWQgRE9NIGVsZW1lbnRcbiAqXG4gKiBAcGFyYW0gIHtOb2RlfHN0cmluZ30gW3Jvb3RdIFRoZSByb290IG5vZGUgb3IgYSBzZWxlY3RvciBzdHJpbmcgbWF0Y2hpbmcgdGhlIHJvb3Qgbm9kZVxuICogQHJldHVybnMge0RlbGVnYXRlfSBUaGlzIG1ldGhvZCBpcyBjaGFpbmFibGVcbiAqL1xuRGVsZWdhdGUucHJvdG90eXBlLnJvb3QgPSBmdW5jdGlvbihyb290KSB7XG4gIHZhciBsaXN0ZW5lck1hcCA9IHRoaXMubGlzdGVuZXJNYXA7XG4gIHZhciBldmVudFR5cGU7XG5cbiAgLy8gUmVtb3ZlIG1hc3RlciBldmVudCBsaXN0ZW5lcnNcbiAgaWYgKHRoaXMucm9vdEVsZW1lbnQpIHtcbiAgICBmb3IgKGV2ZW50VHlwZSBpbiBsaXN0ZW5lck1hcFsxXSkge1xuICAgICAgaWYgKGxpc3RlbmVyTWFwWzFdLmhhc093blByb3BlcnR5KGV2ZW50VHlwZSkpIHtcbiAgICAgICAgdGhpcy5yb290RWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKGV2ZW50VHlwZSwgdGhpcy5oYW5kbGUsIHRydWUpO1xuICAgICAgfVxuICAgIH1cbiAgICBmb3IgKGV2ZW50VHlwZSBpbiBsaXN0ZW5lck1hcFswXSkge1xuICAgICAgaWYgKGxpc3RlbmVyTWFwWzBdLmhhc093blByb3BlcnR5KGV2ZW50VHlwZSkpIHtcbiAgICAgICAgdGhpcy5yb290RWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKGV2ZW50VHlwZSwgdGhpcy5oYW5kbGUsIGZhbHNlKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBJZiBubyByb290IG9yIHJvb3QgaXMgbm90XG4gIC8vIGEgZG9tIG5vZGUsIHRoZW4gcmVtb3ZlIGludGVybmFsXG4gIC8vIHJvb3QgcmVmZXJlbmNlIGFuZCBleGl0IGhlcmVcbiAgaWYgKCFyb290IHx8ICFyb290LmFkZEV2ZW50TGlzdGVuZXIpIHtcbiAgICBpZiAodGhpcy5yb290RWxlbWVudCkge1xuICAgICAgZGVsZXRlIHRoaXMucm9vdEVsZW1lbnQ7XG4gICAgfVxuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSByb290IG5vZGUgYXQgd2hpY2hcbiAgICogbGlzdGVuZXJzIGFyZSBhdHRhY2hlZC5cbiAgICpcbiAgICogQHR5cGUgTm9kZVxuICAgKi9cbiAgdGhpcy5yb290RWxlbWVudCA9IHJvb3Q7XG5cbiAgLy8gU2V0IHVwIG1hc3RlciBldmVudCBsaXN0ZW5lcnNcbiAgZm9yIChldmVudFR5cGUgaW4gbGlzdGVuZXJNYXBbMV0pIHtcbiAgICBpZiAobGlzdGVuZXJNYXBbMV0uaGFzT3duUHJvcGVydHkoZXZlbnRUeXBlKSkge1xuICAgICAgdGhpcy5yb290RWxlbWVudC5hZGRFdmVudExpc3RlbmVyKGV2ZW50VHlwZSwgdGhpcy5oYW5kbGUsIHRydWUpO1xuICAgIH1cbiAgfVxuICBmb3IgKGV2ZW50VHlwZSBpbiBsaXN0ZW5lck1hcFswXSkge1xuICAgIGlmIChsaXN0ZW5lck1hcFswXS5oYXNPd25Qcm9wZXJ0eShldmVudFR5cGUpKSB7XG4gICAgICB0aGlzLnJvb3RFbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoZXZlbnRUeXBlLCB0aGlzLmhhbmRsZSwgZmFsc2UpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4gKiBAcGFyYW0ge3N0cmluZ30gZXZlbnRUeXBlXG4gKiBAcmV0dXJucyBib29sZWFuXG4gKi9cbkRlbGVnYXRlLnByb3RvdHlwZS5jYXB0dXJlRm9yVHlwZSA9IGZ1bmN0aW9uKGV2ZW50VHlwZSkge1xuICByZXR1cm4gWydibHVyJywgJ2Vycm9yJywgJ2ZvY3VzJywgJ2xvYWQnLCAncmVzaXplJywgJ3Njcm9sbCddLmluZGV4T2YoZXZlbnRUeXBlKSAhPT0gLTE7XG59O1xuXG4vKipcbiAqIEF0dGFjaCBhIGhhbmRsZXIgdG8gb25lXG4gKiBldmVudCBmb3IgYWxsIGVsZW1lbnRzXG4gKiB0aGF0IG1hdGNoIHRoZSBzZWxlY3RvcixcbiAqIG5vdyBvciBpbiB0aGUgZnV0dXJlXG4gKlxuICogVGhlIGhhbmRsZXIgZnVuY3Rpb24gcmVjZWl2ZXNcbiAqIHRocmVlIGFyZ3VtZW50czogdGhlIERPTSBldmVudFxuICogb2JqZWN0LCB0aGUgbm9kZSB0aGF0IG1hdGNoZWRcbiAqIHRoZSBzZWxlY3RvciB3aGlsZSB0aGUgZXZlbnRcbiAqIHdhcyBidWJibGluZyBhbmQgYSByZWZlcmVuY2VcbiAqIHRvIGl0c2VsZi4gV2l0aGluIHRoZSBoYW5kbGVyLFxuICogJ3RoaXMnIGlzIGVxdWFsIHRvIHRoZSBzZWNvbmRcbiAqIGFyZ3VtZW50LlxuICpcbiAqIFRoZSBub2RlIHRoYXQgYWN0dWFsbHkgcmVjZWl2ZWRcbiAqIHRoZSBldmVudCBjYW4gYmUgYWNjZXNzZWQgdmlhXG4gKiAnZXZlbnQudGFyZ2V0Jy5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gZXZlbnRUeXBlIExpc3RlbiBmb3IgdGhlc2UgZXZlbnRzXG4gKiBAcGFyYW0ge3N0cmluZ3x1bmRlZmluZWR9IHNlbGVjdG9yIE9ubHkgaGFuZGxlIGV2ZW50cyBvbiBlbGVtZW50cyBtYXRjaGluZyB0aGlzIHNlbGVjdG9yLCBpZiB1bmRlZmluZWQgbWF0Y2ggcm9vdCBlbGVtZW50XG4gKiBAcGFyYW0ge2Z1bmN0aW9uKCl9IGhhbmRsZXIgSGFuZGxlciBmdW5jdGlvbiAtIGV2ZW50IGRhdGEgcGFzc2VkIGhlcmUgd2lsbCBiZSBpbiBldmVudC5kYXRhXG4gKiBAcGFyYW0ge09iamVjdH0gW2V2ZW50RGF0YV0gRGF0YSB0byBwYXNzIGluIGV2ZW50LmRhdGFcbiAqIEByZXR1cm5zIHtEZWxlZ2F0ZX0gVGhpcyBtZXRob2QgaXMgY2hhaW5hYmxlXG4gKi9cbkRlbGVnYXRlLnByb3RvdHlwZS5vbiA9IGZ1bmN0aW9uKGV2ZW50VHlwZSwgc2VsZWN0b3IsIGhhbmRsZXIsIHVzZUNhcHR1cmUpIHtcbiAgdmFyIHJvb3QsIGxpc3RlbmVyTWFwLCBtYXRjaGVyLCBtYXRjaGVyUGFyYW07XG5cbiAgaWYgKCFldmVudFR5cGUpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdJbnZhbGlkIGV2ZW50IHR5cGU6ICcgKyBldmVudFR5cGUpO1xuICB9XG5cbiAgLy8gaGFuZGxlciBjYW4gYmUgcGFzc2VkIGFzXG4gIC8vIHRoZSBzZWNvbmQgb3IgdGhpcmQgYXJndW1lbnRcbiAgaWYgKHR5cGVvZiBzZWxlY3RvciA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIHVzZUNhcHR1cmUgPSBoYW5kbGVyO1xuICAgIGhhbmRsZXIgPSBzZWxlY3RvcjtcbiAgICBzZWxlY3RvciA9IG51bGw7XG4gIH1cblxuICAvLyBGYWxsYmFjayB0byBzZW5zaWJsZSBkZWZhdWx0c1xuICAvLyBpZiB1c2VDYXB0dXJlIG5vdCBzZXRcbiAgaWYgKHVzZUNhcHR1cmUgPT09IHVuZGVmaW5lZCkge1xuICAgIHVzZUNhcHR1cmUgPSB0aGlzLmNhcHR1cmVGb3JUeXBlKGV2ZW50VHlwZSk7XG4gIH1cblxuICBpZiAodHlwZW9mIGhhbmRsZXIgIT09ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yK