Files
rspade_system/storage-broken/rsx-build/bundles/Bootstrap5_Bundle__vendor.40cd750b.js
root 9ebcc359ae Fix code quality violations and enhance ROUTE-EXISTS-01 rule
Implement JQHTML function cache ID system and fix bundle compilation
Implement underscore prefix for system tables
Fix JS syntax linter to support decorators and grant exception to Task system
SPA: Update planning docs and wishlists with remaining features
SPA: Document Navigation API abandonment and future enhancements
Implement SPA browser integration with History API (Phase 1)
Convert contacts view page to SPA action
Convert clients pages to SPA actions and document conversion procedure
SPA: Merge GET parameters and update documentation
Implement SPA route URL generation in JavaScript and PHP
Implement SPA bootstrap controller architecture
Add SPA routing manual page (rsx:man spa)
Add SPA routing documentation to CLAUDE.md
Phase 4 Complete: Client-side SPA routing implementation
Update get_routes() consumers for unified route structure
Complete SPA Phase 3: PHP-side route type detection and is_spa flag
Restore unified routes structure and Manifest_Query class
Refactor route indexing and add SPA infrastructure
Phase 3 Complete: SPA route registration in manifest
Implement SPA Phase 2: Extract router code and test decorators
Rename Jqhtml_Component to Component and complete SPA foundation setup

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 17:48:15 +00:00

2209 lines
210 KiB
JavaScript
Executable File

/* === storage/rsx-build/bundles/npm_Bootstrap5_Bundle_6459e8ed0f60bda4f121420766012d53.js === */
(() => {
// node_modules/@jqhtml/core/dist/index.js
var LifecycleManager = class _LifecycleManager {
static get_instance() {
if (!_LifecycleManager.instance) {
_LifecycleManager.instance = new _LifecycleManager();
}
return _LifecycleManager.instance;
}
constructor() {
this.active_components = /* @__PURE__ */ new Set();
}
/**
* Boot a component - run its full lifecycle
* Called when component is created
*/
async boot_component(component) {
this.active_components.add(component);
try {
await component.create();
if (component._stopped)
return;
component.trigger("create");
let render_id = component._render();
if (component._stopped)
return;
await component.load();
if (component._stopped)
return;
if (component.should_rerender()) {
render_id = component._render();
if (component._stopped)
return;
}
if (component._render_count !== render_id) {
return;
}
await component.ready();
if (component._stopped)
return;
await component.trigger("ready");
} catch (error) {
console.error(`Error booting component ${component.component_name()}:`, error);
throw error;
}
}
/**
* Unregister a component (called on destroy)
*/
unregister_component(component) {
this.active_components.delete(component);
}
/**
* Wait for all active components to reach ready state
*/
async wait_for_ready() {
const ready_promises = [];
for (const component of this.active_components) {
if (component._ready_state < 4) {
ready_promises.push(new Promise((resolve) => {
component.on("ready", () => resolve());
}));
}
}
await Promise.all(ready_promises);
}
};
var component_classes = /* @__PURE__ */ new Map();
var component_templates = /* @__PURE__ */ new Map();
var warned_components = /* @__PURE__ */ new Set();
var DEFAULT_TEMPLATE = {
name: "Component",
// Default name
tag: "div",
render: function(data, args, content) {
const _output = [];
if (args._inner_html) {
_output.push(args._inner_html);
return [_output, this];
}
if (content && typeof content === "function") {
const result = content(this);
if (Array.isArray(result) && result.length === 2) {
_output.push(...result[0]);
} else if (typeof result === "string") {
_output.push(result);
}
}
return [_output, this];
}
};
function register_component(nameOrClass, component_class, template) {
if (typeof nameOrClass === "string") {
const name = nameOrClass;
if (!component_class) {
throw new Error("Component class is required when registering by name");
}
if (!/^[A-Z]/.test(name)) {
throw new Error(`Component name '${name}' must start with a capital letter. Convention is First_Letter_With_Underscores.`);
}
component_classes.set(name, component_class);
if (template) {
if (template.name !== name) {
throw new Error(`Template name '${template.name}' must match component name '${name}'`);
}
register_template(template);
}
} else {
const component_class2 = nameOrClass;
const name = component_class2.name;
if (!name || name === "Component") {
throw new Error("Component class must have a name when registering without explicit name");
}
component_classes.set(name, component_class2);
}
}
function get_component_class(name) {
const directClass = component_classes.get(name);
if (directClass) {
return directClass;
}
const template = component_templates.get(name);
if (template && template.extends) {
const visited = /* @__PURE__ */ new Set([name]);
let currentTemplateName = template.extends;
while (currentTemplateName && !visited.has(currentTemplateName)) {
visited.add(currentTemplateName);
const parentClass = component_classes.get(currentTemplateName);
if (parentClass) {
if (window.jqhtml?.debug?.enabled) {
console.log(`[JQHTML] Component '${name}' using class from parent '${currentTemplateName}' via extends chain`);
}
return parentClass;
}
const parentTemplate = component_templates.get(currentTemplateName);
if (parentTemplate && parentTemplate.extends) {
currentTemplateName = parentTemplate.extends;
} else {
break;
}
}
}
return void 0;
}
function register_template(template_def) {
const name = template_def.name;
if (!name) {
throw new Error("Template must have a name property");
}
if (!/^[A-Z]/.test(name)) {
throw new Error(`Template name '${name}' must start with a capital letter. Convention is First_Letter_With_Underscores.`);
}
if (component_templates.has(name)) {
console.warn(`[JQHTML] Template '${name}' already registered, skipping duplicate registration`);
return false;
}
component_templates.set(name, template_def);
if (window.jqhtml?.debug?.enabled) {
console.log(`[JQHTML] Successfully registered template: ${name}`);
}
const component_class = component_classes.get(name);
if (component_class) {
component_class._jqhtml_metadata = {
tag: template_def.tag,
defaultAttributes: template_def.defaultAttributes || {}
};
}
return true;
}
function get_template(name) {
const template = component_templates.get(name);
if (!template) {
const component_class = component_classes.get(name);
if (component_class) {
const inherited_template = get_template_by_class(component_class);
if (inherited_template !== DEFAULT_TEMPLATE) {
if (window.jqhtml?.debug?.enabled) {
console.log(`[JQHTML] Component '${name}' has no template, using template from prototype chain`);
}
return inherited_template;
}
if (window.jqhtml?.debug?.enabled && !warned_components.has(name)) {
warned_components.add(name);
console.log(`[JQHTML] No template found for class: ${name}, using default div template`);
}
} else {
if (name !== "_Jqhtml_Component" && name !== "Redrawable" && !warned_components.has(name)) {
warned_components.add(name);
console.warn(`[JQHTML] Creating ${name} with defaults - no template or class defined`);
}
}
if (window.jqhtml?.debug?.verbose) {
const registered = Array.from(component_templates.keys());
console.log(`[JQHTML] Looking for template '${name}' in: [${registered.join(", ")}]`);
}
return DEFAULT_TEMPLATE;
}
return template;
}
function get_template_by_class(component_class) {
if (component_class.template) {
return component_class.template;
}
let currentClass = component_class;
while (currentClass && currentClass.name !== "Object") {
let normalizedName = currentClass.name;
if (normalizedName === "_Jqhtml_Component" || normalizedName === "_Base_Jqhtml_Component") {
normalizedName = "Component";
}
const template = component_templates.get(normalizedName);
if (template) {
return template;
}
currentClass = Object.getPrototypeOf(currentClass);
}
return DEFAULT_TEMPLATE;
}
function create_component(name, element, args = {}) {
const ComponentClass = get_component_class(name) || Component;
return new ComponentClass(element, args);
}
function has_component(name) {
return component_classes.has(name);
}
function get_component_names() {
return Array.from(component_classes.keys());
}
function get_registered_templates() {
return Array.from(component_templates.keys());
}
function list_components() {
const result = {};
for (const name of component_classes.keys()) {
result[name] = {
has_class: true,
has_template: component_templates.has(name)
};
}
for (const name of component_templates.keys()) {
if (!result[name]) {
result[name] = {
has_class: false,
has_template: true
};
}
}
return result;
}
var _cid_increment = "aa";
function uid() {
const current = _cid_increment;
const chars = _cid_increment.split("");
let carry = true;
for (let i = chars.length - 1; i >= 0 && carry; i--) {
const char = chars[i];
if (char >= "a" && char < "z") {
chars[i] = String.fromCharCode(char.charCodeAt(0) + 1);
carry = false;
} else if (char === "z") {
chars[i] = "0";
carry = false;
} else if (char >= "0" && char < "9") {
chars[i] = String.fromCharCode(char.charCodeAt(0) + 1);
carry = false;
} else if (char === "9") {
chars[i] = "a";
carry = true;
}
}
if (carry) {
chars.unshift("a");
}
if (chars[0] >= "0" && chars[0] <= "9") {
chars[0] = "a";
chars.unshift("a");
}
_cid_increment = chars.join("");
return current;
}
function process_instructions(instructions, target, context, slots) {
const html = [];
const tagElements = {};
const components = {};
for (const instruction of instructions) {
process_instruction_to_html(instruction, html, tagElements, components, context, slots);
}
target[0].innerHTML = html.join("");
for (const [tid, tagData] of Object.entries(tagElements)) {
const el = target[0].querySelector(`[data-tid="${tid}"]`);
if (el) {
const element = $(el);
el.removeAttribute("data-tid");
apply_attributes(element, tagData.attrs, context);
}
}
for (const [cid, compData] of Object.entries(components)) {
const el = target[0].querySelector(`[data-cid="${cid}"]`);
if (el) {
const element = $(el);
el.removeAttribute("data-cid");
initialize_component(element, compData);
}
}
}
function process_instruction_to_html(instruction, html, tagElements, components, context, slots) {
if (typeof instruction === "string") {
html.push(instruction);
} else if ("tag" in instruction) {
process_tag_to_html(instruction, html, tagElements, components, context);
} else if ("comp" in instruction) {
process_component_to_html(instruction, html, components, context);
} else if ("slot" in instruction) {
process_slot_to_html(instruction, html, tagElements, components, context, slots);
} else if ("rawtag" in instruction) {
process_rawtag_to_html(instruction, html);
}
}
function process_tag_to_html(instruction, html, tagElements, components, context) {
const [tagName, attrs, selfClosing] = instruction.tag;
const needsTracking = Object.keys(attrs).some((key) => key === "$id" || key.startsWith("$") || key.startsWith("@") || key.startsWith("on") || key.startsWith("data-bind-") || key.startsWith("data-on-"));
html.push(`<${tagName}`);
let tid = null;
if (needsTracking) {
tid = uid();
html.push(` data-tid="${tid}"`);
tagElements[tid] = { attrs, context };
}
for (const [key, value] of Object.entries(attrs)) {
if (!key.startsWith("$") && !key.startsWith("on") && !key.startsWith("@") && !key.startsWith("data-bind-") && !key.startsWith("data-on-") && (typeof value === "string" || typeof value === "number")) {
if (key === "id" && tid) {
html.push(` id="${value}:${context._cid}"`);
} else {
html.push(` ${key}="${value}"`);
}
}
}
if (selfClosing) {
html.push(" />");
} else {
html.push(">");
}
}
function process_component_to_html(instruction, html, components, context) {
const [componentName, props, contentFn] = instruction.comp;
const cid = uid();
get_component_class(componentName) || Component;
const template = get_template(componentName);
const tagName = props._tag || template.tag || "div";
html.push(`<${tagName} data-cid="${cid}"`);
if (props["data-id"]) {
const baseId = props["data-id"];
html.push(` id="${props["id"]}" data-id="${baseId}"`);
} else if (props["id"]) {
html.push(` id="${props["id"]}"`);
}
html.push("></" + tagName + ">");
components[cid] = {
name: componentName,
props,
contentFn,
context
};
}
function process_slot_to_html(instruction, html, tagElements, components, context, parentSlots) {
const [slotName] = instruction.slot;
if (parentSlots && slotName in parentSlots) {
const parentSlot = parentSlots[slotName];
const [, slotProps, contentFn] = parentSlot.slot;
const [content] = contentFn.call(context, slotProps);
for (const item of content) {
process_instruction_to_html(item, html, tagElements, components, context);
}
} else if (slotName === "default" && instruction.slot[2]) {
const [, , defaultFn] = instruction.slot;
const [content] = defaultFn.call(context, {});
for (const item of content) {
process_instruction_to_html(item, html, tagElements, components, context);
}
}
}
function process_rawtag_to_html(instruction, html) {
const [tagName, attrs, rawContent] = instruction.rawtag;
html.push(`<${tagName}`);
for (const [key, value] of Object.entries(attrs)) {
if (typeof value === "string" || typeof value === "number") {
const escaped_value = String(value).replace(/"/g, "&quot;");
html.push(` ${key}="${escaped_value}"`);
} else if (typeof value === "boolean" && value) {
html.push(` ${key}`);
}
}
html.push(">");
const escaped_content = rawContent.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
html.push(escaped_content);
html.push(`</${tagName}>`);
}
function apply_attributes(element, attrs, context) {
for (const [key, value] of Object.entries(attrs)) {
if (key === "$id" || key === "id") {
continue;
} else if (key.startsWith("$")) {
const dataKey = key.substring(1);
element.data(dataKey, value);
context.args[dataKey] = value;
if (typeof value == "string" || typeof value == "number") {
const attrValue = typeof value === "string" ? value.trim() : value;
element.attr(`data-${dataKey}`, attrValue);
}
} else if (key.startsWith("data-on-")) {
const eventName = key.substring(8);
if (typeof value === "function") {
element.on(eventName, function(e) {
value.bind(context)(e, element);
});
} else {
console.warn("(JQHTML) Tried to assign a non function to on event handler " + key);
}
} else if (key.startsWith("on")) {
const eventName = key.substring(2);
if (typeof value === "function") {
element.on(eventName, function(e) {
value.bind(context)(e, element);
});
} else {
console.warn("(JQHTML) Tried to assign a non function to on event handler " + key);
}
} else if (key.startsWith("data-")) {
const attrValue = typeof value === "string" ? value.trim() : value;
element.attr(key, attrValue);
const dataKey = key.substring(5);
element.data(dataKey, value);
context.args[dataKey] = value;
} else if (key === "class") {
const existingClasses = element.attr("class");
if (window.jqhtml?.debug?.enabled) {
console.log(`[InstructionProcessor] Merging class attribute:`, {
existing: existingClasses,
new: value
});
}
if (!existingClasses) {
const attrValue = typeof value === "string" ? value.trim() : value;
element.attr("class", attrValue);
} else {
const existing = existingClasses.split(/\s+/).filter((c) => c);
const newClasses = String(value).split(/\s+/).filter((c) => c);
for (const newClass of newClasses) {
if (!existing.includes(newClass)) {
existing.push(newClass);
}
}
element.attr("class", existing.join(" "));
}
if (window.jqhtml?.debug?.enabled) {
console.log(`[InstructionProcessor] Class after merge:`, element.attr("class"));
}
} else if (key === "style") {
const existingStyle = element.attr("style");
if (!existingStyle) {
const attrValue = typeof value === "string" ? value.trim() : value;
element.attr("style", attrValue);
} else {
const styleMap = {};
existingStyle.split(";").forEach((rule) => {
const [prop, val] = rule.split(":").map((s) => s.trim());
if (prop && val) {
styleMap[prop] = val;
}
});
String(value).split(";").forEach((rule) => {
const [prop, val] = rule.split(":").map((s) => s.trim());
if (prop && val) {
styleMap[prop] = val;
}
});
const mergedStyle = Object.entries(styleMap).map(([prop, val]) => `${prop}: ${val}`).join("; ");
element.attr("style", mergedStyle);
}
} else {
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
const attrValue = typeof value === "string" ? value.trim() : String(value);
element.attr(key, attrValue);
} else if (typeof value === "object") {
console.warn(`(JQHTML) Unexpected value for '${key}' on`, element);
}
}
}
}
async function initialize_component(element, compData) {
const { name, props, contentFn, context } = compData;
const ComponentClass = get_component_class(name) || Component;
const invocationAttrs = {};
for (const [key, value] of Object.entries(props)) {
if (!key.startsWith("_")) {
invocationAttrs[key] = value;
}
}
if (window.jqhtml?.debug?.enabled) {
console.log(`[InstructionProcessor] Applying invocation attributes for ${name}:`, invocationAttrs);
}
apply_attributes(element, invocationAttrs, context);
const options = {};
if (contentFn) {
options._innerhtml_function = contentFn;
}
if (ComponentClass.name !== name) {
options._component_name = name;
}
const instance = new ComponentClass(element, options);
instance._instantiator = context;
await instance.boot();
}
function extract_slots(instructions) {
const slots = {};
for (const instruction of instructions) {
if (typeof instruction === "object" && "slot" in instruction) {
const [name] = instruction.slot;
slots[name] = instruction;
}
}
return slots;
}
var performanceMetrics = /* @__PURE__ */ new Map();
function devWarn(message) {
if (typeof window !== "undefined" && window.JQHTML_SUPPRESS_WARNINGS) {
return;
}
if (typeof process !== "undefined" && process.env && false) {
return;
}
console.warn(`[JQHTML Dev Warning] ${message}`);
}
function getJqhtml$1() {
if (typeof window !== "undefined" && window.jqhtml) {
return window.jqhtml;
}
if (typeof globalThis !== "undefined" && globalThis.jqhtml) {
return globalThis.jqhtml;
}
throw new Error("FATAL: window.jqhtml is not defined. The JQHTML runtime must be loaded before using debug features. Import and initialize @jqhtml/core before attempting to use debug functionality.");
}
function flashComponent(component, eventType) {
const jqhtml2 = getJqhtml$1();
if (!jqhtml2?.debug?.flashComponents)
return;
const duration = jqhtml2.debug.flashDuration || 500;
const colors = jqhtml2.debug.flashColors || {};
const color = colors[eventType] || (eventType === "create" ? "#3498db" : eventType === "render" ? "#27ae60" : "#9b59b6");
const originalBorder = component.$.css("border");
component.$.css({
"border": `2px solid ${color}`,
"transition": `border ${duration}ms ease-out`
});
setTimeout(() => {
component.$.css("border", originalBorder || "");
}, duration);
}
function logLifecycle(component, phase, status) {
const jqhtml2 = getJqhtml$1();
if (!jqhtml2?.debug)
return;
const shouldLog = jqhtml2.debug.logFullLifecycle || jqhtml2.debug.logCreationReady && (phase === "create" || phase === "ready");
if (!shouldLog)
return;
const componentName = component.constructor.name;
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
const prefix = `[JQHTML ${timestamp}]`;
if (status === "start") {
console.log(`${prefix} ${componentName}#${component._cid} \u2192 ${phase} starting...`);
if (jqhtml2.debug.profilePerformance) {
performanceMetrics.set(`${component._cid}_${phase}`, Date.now());
}
} else {
let message = `${prefix} ${componentName}#${component._cid} \u2713 ${phase} complete`;
if (jqhtml2.debug.profilePerformance) {
const startTime = performanceMetrics.get(`${component._cid}_${phase}`);
if (startTime) {
const duration = Date.now() - startTime;
message += ` (${duration}ms)`;
if (phase === "render" && jqhtml2.debug.highlightSlowRenders && duration > jqhtml2.debug.highlightSlowRenders) {
console.warn(`${prefix} SLOW RENDER: ${componentName}#${component._cid} took ${duration}ms`);
component.$.css("outline", "2px dashed red");
}
}
}
console.log(message);
if (jqhtml2.debug.flashComponents && (phase === "create" || phase === "render" || phase === "ready")) {
flashComponent(component, phase);
}
}
if (jqhtml2.debug.showComponentTree) {
updateComponentTree();
}
}
function applyDebugDelay(phase) {
const jqhtml2 = getJqhtml$1();
if (!jqhtml2?.debug)
return;
let delayMs = 0;
switch (phase) {
case "component":
delayMs = jqhtml2.debug.delayAfterComponent || 0;
break;
case "render":
delayMs = jqhtml2.debug.delayAfterRender || 0;
break;
case "rerender":
delayMs = jqhtml2.debug.delayAfterRerender || 0;
break;
}
if (delayMs > 0) {
console.log(`[JQHTML Debug] Applying ${delayMs}ms delay after ${phase}`);
}
}
function updateComponentTree() {
console.log("[JQHTML Tree] Component hierarchy updated");
}
var Component = class _Jqhtml_Component {
constructor(element, args = {}) {
this.data = {};
this._ready_state = 0;
this._instantiator = null;
this._dom_parent = null;
this._dom_children = /* @__PURE__ */ new Set();
this._use_dom_fallback = false;
this._stopped = false;
this._booted = false;
this._data_before_render = null;
this._lifecycle_callbacks = /* @__PURE__ */ new Map();
this._lifecycle_states = /* @__PURE__ */ new Set();
this.__loading = false;
this._did_first_render = false;
this._render_count = 0;
this._cid = this._generate_cid();
this._lifecycle_manager = LifecycleManager.get_instance();
if (element) {
this.$ = $(element);
} else {
const div = document.createElement("div");
this.$ = $(div);
}
const dataAttrs = {};
if (this.$.length > 0) {
const dataset = this.$[0].dataset || {};
for (const key in dataset) {
if (key !== "cid" && key !== "tid" && key !== "componentName" && key !== "readyState") {
const dataValue = this.$.data(key);
if (dataValue !== void 0 && dataValue !== dataset[key]) {
dataAttrs[key] = dataValue;
} else {
dataAttrs[key] = dataset[key];
}
}
}
}
let template_for_args;
if (args._component_name) {
template_for_args = get_template(args._component_name);
} else {
template_for_args = get_template_by_class(this.constructor);
}
const defineArgs = template_for_args?.defineArgs || {};
this.args = { ...defineArgs, ...dataAttrs, ...args };
for (const [key, value] of Object.entries(this.args)) {
if (key === "cid" || key === "tid" || key === "componentName" || key === "readyState" || key.startsWith("_")) {
continue;
}
if (typeof value === "string" || typeof value === "number") {
try {
const currentAttr = this.$.attr(`data-${key}`);
if (currentAttr != value) {
this.$.attr(`data-${key}`, String(value));
}
} catch (e) {
}
}
}
this.$.data("_component", this);
this._apply_css_classes();
this._apply_default_attributes();
this._set_attributes();
this._find_dom_parent();
this._log_lifecycle("construct", "complete");
}
/**
* Boot - Start the full component lifecycle
* Called immediately after construction by instruction processor
*/
async boot() {
if (this._booted)
return;
this._booted = true;
await this._lifecycle_manager.boot_component(this);
}
// -------------------------------------------------------------------------
// Lifecycle Methods (called by LifecycleManager)
// -------------------------------------------------------------------------
/**
* Internal render phase - Create DOM structure
* Called top-down (parent before children) when part of lifecycle
* This is an internal method - users should call render() instead
*
* @param id Optional scoped ID - if provided, delegates to child component's _render()
* @returns The current _render_count after incrementing (used to detect stale renders)
* @private
*/
_render(id = null) {
this._render_count++;
const current_render_id = this._render_count;
if (this._stopped)
return current_render_id;
if (id) {
const $element = this.$id(id);
if ($element.length === 0) {
throw new Error(`[JQHTML] render("${id}") - no such id.
Component "${this.component_name()}" has no child element with $id="${id}".`);
}
const child = $element.data("_component");
if (!child) {
throw new Error(`[JQHTML] render("${id}") - element is not a component or does not have $redrawable attribute set.
Element with $id="${id}" exists but is not initialized as a component.
Add $redrawable attribute or make it a proper component.`);
}
return child._render();
}
if (this.__loading) {
throw new Error(`[JQHTML] Component "${this.component_name()}" attempted to call render() during on_load().
on_load() should ONLY modify this.data. DOM updates happen automatically after on_load() completes.
Fix: Remove the this.render() call from on_load().
The framework will automatically re-render if this.data changes during on_load().`);
}
this._log_lifecycle("render", "start");
if (!$.contains(document.documentElement, this.$[0])) {
this._use_dom_fallback = true;
} else {
this._use_dom_fallback = false;
}
if (this._did_first_render) {
this.$.find(".Component").each(function() {
const child = $(this).data("_component");
if (child && !child._stopped) {
child._stop();
}
});
this.$[0].innerHTML = "";
} else {
this._did_first_render = true;
}
this.$.removeClass("_Component_Stopped");
if (this._data_before_render === null) {
this._data_before_render = JSON.stringify(this.data);
}
this._dom_children.clear();
let template_def;
if (this.args._component_name) {
template_def = get_template(this.args._component_name);
} else {
template_def = get_template_by_class(this.constructor);
}
if (template_def && template_def.render) {
const jqhtml2 = {
escape_html: (str) => {
const div = document.createElement("div");
div.textContent = String(str);
return div.innerHTML;
}
};
const defaultContent = () => "";
let [instructions, context] = template_def.render.bind(this)(
this.data,
this.args,
this.args._innerhtml_function || defaultContent,
// Content function with fallback
jqhtml2
// Utilities object
);
if (instructions && typeof instructions === "object" && instructions._slots && !Array.isArray(instructions)) {
const componentName = template_def.name || this.args._component_name || this.constructor.name;
console.log(`[JQHTML] Slot-only template detected for ${componentName}`);
let parentTemplate = null;
let parentTemplateName = null;
if (template_def.extends) {
console.log(`[JQHTML] Using explicit extends: ${template_def.extends}`);
parentTemplate = get_template(template_def.extends);
parentTemplateName = template_def.extends;
}
if (!parentTemplate) {
let currentClass = Object.getPrototypeOf(this.constructor);
while (currentClass && currentClass.name !== "Object" && currentClass.name !== "Component") {
const className = currentClass.name;
console.log(`[JQHTML] Checking parent: ${className}`);
try {
const classTemplate = get_template(className);
if (classTemplate && classTemplate.name !== "Component") {
console.log(`[JQHTML] Found parent template: ${className}`);
parentTemplate = classTemplate;
parentTemplateName = className;
break;
}
} catch (error) {
console.warn(`[JQHTML] Error finding parent template ${className}:`, error);
}
currentClass = Object.getPrototypeOf(currentClass);
}
}
if (parentTemplate) {
try {
const childSlots = instructions._slots;
const contentFunction = (slotName, data) => {
if (childSlots[slotName] && typeof childSlots[slotName] === "function") {
const [slotInstructions, slotContext] = childSlots[slotName](data);
return [slotInstructions, slotContext];
}
return "";
};
const [parentInstructions, parentContext] = parentTemplate.render.bind(this)(
this.data,
this.args,
contentFunction,
// Pass content function that invokes child slots
jqhtml2
);
console.log(`[JQHTML] Parent template invoked successfully`);
instructions = parentInstructions;
context = parentContext;
} catch (error) {
console.warn(`[JQHTML] Error invoking parent template ${parentTemplateName}:`, error);
instructions = [];
}
} else {
console.warn(`[JQHTML] No parent template found for ${this.constructor.name}, rendering empty`);
instructions = [];
}
}
const flattenedInstructions = this._flatten_instructions(instructions);
process_instructions(flattenedInstructions, this.$, this);
}
this._update_debug_attrs();
this._log_lifecycle("render", "complete");
const renderResult = this.on_render();
if (renderResult && typeof renderResult.then === "function") {
console.warn(`[JQHTML] Component "${this.component_name()}" returned a Promise from on_render(). on_render() must be synchronous code. Remove 'async' from the function declaration.`);
}
this.trigger("render");
const isRerender = this._ready_state >= 3;
applyDebugDelay(isRerender ? "rerender" : "render");
return current_render_id;
}
/**
* Public render method - re-renders component and completes lifecycle
* This is what users should call when they want to update a component.
*
* Lifecycle sequence:
* 1. _render() - Updates DOM synchronously, calls on_render(), fires 'render' event
* 2. Async continuation (fire and forget):
* - _wait_for_children_ready() - Waits for all children to reach ready state
* - on_ready() - Calls user's ready hook
* - trigger('ready') - Fires ready event
*
* Returns immediately after _render() completes - does NOT wait for children
*/
render(id = null) {
if (this._stopped)
return;
if (id) {
const $element = this.$id(id);
if ($element.length === 0) {
throw new Error(`[JQHTML] render("${id}") - no such id.
Component "${this.component_name()}" has no child element with $id="${id}".`);
}
const child = $element.data("_component");
if (!child) {
throw new Error(`[JQHTML] render("${id}") - element is not a component or does not have $redrawable attribute set.
Element with $id="${id}" exists but is not initialized as a component.
Add $redrawable attribute or make it a proper component.`);
}
return child.render();
}
const render_id = this._render();
(async () => {
await this._wait_for_children_ready();
if (this._render_count !== render_id) {
return;
}
await this.on_ready();
await this.trigger("ready");
})();
}
/**
* Alias for render() - re-renders component with current data
* Provided for API consistency and clarity
*/
redraw(id = null) {
return this.render(id);
}
/**
* Create phase - Quick setup, prepare UI
* Called bottom-up (children before parent)
*/
async create() {
if (this._stopped || this._ready_state >= 1)
return;
this._log_lifecycle("create", "start");
const result = this.on_create();
if (result && typeof result.then === "function") {
console.warn(`[JQHTML] Component "${this.component_name()}" returned a Promise from on_create(). on_create() must be synchronous code. Remove 'async' from the function declaration.`);
await result;
}
this._ready_state = 1;
this._update_debug_attrs();
this._log_lifecycle("create", "complete");
this.trigger("create");
}
/**
* Load phase - Fetch data from APIs
* Called bottom-up, fully parallel
* NO DOM MODIFICATIONS ALLOWED IN THIS PHASE
*/
async load() {
if (this._stopped || this._ready_state >= 2)
return;
this._log_lifecycle("load", "start");
const argsBeforeLoad = JSON.stringify(this.args);
const propertiesBeforeLoad = new Set(Object.keys(this));
this.__loading = true;
try {
await this.on_load();
} finally {
this.__loading = false;
}
const argsAfterLoad = JSON.stringify(this.args);
const propertiesAfterLoad = Object.keys(this);
if (argsBeforeLoad !== argsAfterLoad) {
console.error(`[JQHTML] WARNING: Component "${this.component_name()}" modified this.args in on_load().
on_load() should ONLY modify this.data. The this.args property is read-only.
Before: ${argsBeforeLoad}
After: ${argsAfterLoad}
Fix: Move your modifications to this.data instead.`);
}
const newProperties = propertiesAfterLoad.filter((prop) => !propertiesBeforeLoad.has(prop) && prop !== "data");
if (newProperties.length > 0) {
console.error(`[JQHTML] WARNING: Component "${this.component_name()}" added new properties in on_load().
on_load() should ONLY modify this.data. New properties detected: ${newProperties.join(", ")}
Fix: Store your data in this.data instead:
\u274C this.${newProperties[0]} = value;
\u2705 this.data.${newProperties[0]} = value;`);
}
this._ready_state = 2;
this._update_debug_attrs();
this._log_lifecycle("load", "complete");
this.trigger("load");
}
/**
* Ready phase - Component fully initialized
* Called bottom-up (children before parent)
*/
async ready() {
if (this._stopped || this._ready_state >= 4)
return;
this._log_lifecycle("ready", "start");
await this._wait_for_children_ready();
await this.on_ready();
this._ready_state = 4;
this._update_debug_attrs();
this._log_lifecycle("ready", "complete");
this.trigger("ready");
}
/**
* Wait for all child components to reach ready state
* Ensures bottom-up ordering (children ready before parent)
* @private
*/
async _wait_for_children_ready() {
const children = this._get_dom_children();
if (children.length === 0) {
return;
}
const ready_promises = [];
for (const child of children) {
if (child._ready_state >= 4) {
continue;
}
const ready_promise = new Promise((resolve) => {
child.on("ready", () => resolve());
});
ready_promises.push(ready_promise);
}
await Promise.all(ready_promises);
}
/**
* Reinitialize the component - full reset and re-initialization
* Wipes the innerHTML, resets data to empty, and runs full lifecycle
*/
async reinitialize() {
if (this._stopped)
return;
this._log_lifecycle("reinitialize", "start");
this.$[0].innerHTML = "";
this.data = {};
this._ready_state = 0;
this._data_before_render = null;
this._dom_children.clear();
await this._render();
await this.create();
await this.load();
if (this.should_rerender()) {
await this._render();
}
await this.ready();
this._log_lifecycle("reinitialize", "complete");
}
/**
* Reload component - re-fetch data and re-render
* Re-runs on_load(), always renders, and calls on_ready()
*/
async reload() {
if (this._stopped)
return;
this._log_lifecycle("reload", "start");
const has_custom_on_load = this.on_load !== _Jqhtml_Component.prototype.on_load;
if (has_custom_on_load) {
const argsBeforeLoad = JSON.stringify(this.args);
const propertiesBeforeLoad = new Set(Object.keys(this));
this.__loading = true;
try {
await this.on_load();
} finally {
this.__loading = false;
}
const argsAfterLoad = JSON.stringify(this.args);
const propertiesAfterLoad = Object.keys(this);
if (argsBeforeLoad !== argsAfterLoad) {
console.error(`[JQHTML] WARNING: Component "${this.component_name()}" modified this.args in on_load().
on_load() should ONLY modify this.data. The this.args property is read-only.
Before: ${argsBeforeLoad}
After: ${argsAfterLoad}
Fix: Move your modifications to this.data instead.`);
}
const newProperties = propertiesAfterLoad.filter((prop) => !propertiesBeforeLoad.has(prop) && prop !== "data");
if (newProperties.length > 0) {
console.error(`[JQHTML] WARNING: Component "${this.component_name()}" added new properties in on_load().
on_load() should ONLY modify this.data. New properties detected: ${newProperties.join(", ")}
Fix: Store your data in this.data instead:
\u274C this.${newProperties[0]} = value;
\u2705 this.data.${newProperties[0]} = value;`);
}
}
await this.render();
this._log_lifecycle("reload", "complete");
}
/**
* Destroy the component and cleanup
* Called automatically by MutationObserver when component is removed from DOM
* Can also be called manually for explicit cleanup
*/
/**
* Internal stop method - stops just this component (no children)
* Sets stopped flag, calls lifecycle hooks, but leaves DOM intact
* @private
*/
_stop() {
if (this._stopped)
return;
this._stopped = true;
const has_custom_destroy = this.on_destroy !== _Jqhtml_Component.prototype.on_destroy;
const has_destroy_callbacks = this._on_registered("destroy");
if (!has_custom_destroy && !has_destroy_callbacks) {
this._lifecycle_manager.unregister_component(this);
this._ready_state = 99;
return;
}
this._log_lifecycle("destroy", "start");
this.$.addClass("_Component_Stopped");
this._lifecycle_manager.unregister_component(this);
const destroyResult = this.on_destroy();
if (destroyResult && typeof destroyResult.then === "function") {
console.warn(`[JQHTML] Component "${this.component_name()}" returned a Promise from on_destroy(). on_destroy() must be synchronous code. Remove 'async' from the function declaration.`);
}
this.trigger("destroy");
this.$.trigger("destroy");
if (this._dom_parent) {
this._dom_parent._dom_children.delete(this);
}
this._ready_state = 99;
this._update_debug_attrs();
this._log_lifecycle("destroy", "complete");
}
/**
* Stop component lifecycle - stops all descendant components then self
* Leaves DOM intact, just stops lifecycle engine and fires cleanup hooks
*/
stop() {
this.$.find(".Component").each(function() {
const child = $(this).data("_component");
if (child && !child._stopped) {
child._stop();
}
});
this._stop();
}
// -------------------------------------------------------------------------
// Overridable Lifecycle Hooks
// -------------------------------------------------------------------------
on_render() {
}
on_create() {
}
async on_load() {
}
async on_ready() {
}
on_destroy() {
}
/**
* Should component re-render after load?
* By default, only re-renders if data has changed
* Override to control re-rendering behavior
*/
should_rerender() {
const currentDataState = JSON.stringify(this.data);
const dataChanged = this._data_before_render !== currentDataState;
if (dataChanged) {
this._data_before_render = currentDataState;
}
return dataChanged;
}
// -------------------------------------------------------------------------
// Public API
// -------------------------------------------------------------------------
/**
* Get component name for debugging
*/
component_name() {
return this.constructor.name;
}
/**
* Emit a jQuery event from component root
*/
emit(event_name, data) {
this._log_debug("emit", event_name, data);
this.$.trigger(event_name, data);
}
/**
* Register lifecycle event callback
* Allowed events: 'render', 'create', 'load', 'ready', 'destroy'
* Callbacks fire after the lifecycle method completes
* If the event has already occurred, the callback fires immediately AND registers for future occurrences
*/
on(event_name, callback) {
const allowed_events = ["render", "create", "load", "ready", "destroy"];
if (!allowed_events.includes(event_name)) {
console.error(`[JQHTML] Component.on() only supports lifecycle events: ${allowed_events.join(", ")}. Received: ${event_name}`);
return this;
}
if (!this._lifecycle_callbacks.has(event_name)) {
this._lifecycle_callbacks.set(event_name, []);
}
this._lifecycle_callbacks.get(event_name).push(callback);
if (this._lifecycle_states.has(event_name)) {
try {
callback(this);
} catch (error) {
console.error(`[JQHTML] Error in ${event_name} callback:`, error);
}
}
return this;
}
/**
* Trigger a lifecycle event - fires all registered callbacks
* Marks event as occurred so future .on() calls fire immediately
*/
trigger(event_name) {
this._lifecycle_states.add(event_name);
const callbacks = this._lifecycle_callbacks.get(event_name);
if (callbacks) {
for (const callback of callbacks) {
try {
callback.bind(this)(this);
} catch (error) {
console.error(`[JQHTML] Error in ${event_name} callback:`, error);
}
}
}
}
/**
* Check if any callbacks are registered for a given event
* Used to determine if cleanup logic needs to run
*/
_on_registered(event_name) {
const callbacks = this._lifecycle_callbacks.get(event_name);
return !!(callbacks && callbacks.length > 0);
}
/**
* Find element by scoped ID
*
* Searches for elements with id="local_id:THIS_COMPONENT_CID"
*
* Example:
* Template: <button $id="save_btn">Save</button>
* Rendered: <button id="save_btn:abc123" data-id="save_btn">Save</button>
* Access: this.$id('save_btn') // Returns jQuery element
*
* Performance: Uses native document.getElementById() when component is in DOM,
* falls back to jQuery.find() for components not yet attached to DOM.
*
* @param local_id The local ID (without _cid suffix)
* @returns jQuery element with id="local_id:_cid", or empty jQuery object if not found
*/
$id(local_id) {
const scopedId = `${local_id}:${this._cid}`;
const el = document.getElementById(scopedId);
if (el) {
return $(el);
}
return this.$.find(`#${$.escapeSelector(scopedId)}`);
}
/**
* Get component instance by scoped ID
*
* Convenience method that finds element by scoped ID and returns the component instance.
*
* Example:
* Template: <User_Card $id="active_user" />
* Access: const user = this.id('active_user'); // Returns User_Card instance
* user.data.name // Access component's data
*
* @param local_id The local ID (without _cid suffix)
* @returns Component instance or null if not found or not a component
*/
id(local_id) {
const element = this.$id(local_id);
const component = element.data("_component");
if (!component && element.length > 0) {
console.warn(`Component ${this.constructor.name} tried to call .id('${local_id}') - ${local_id} exists, however, it is not a component or $redrawable. Did you forget to add $redrawable to the tag?`);
}
return component || null;
}
/**
* Get the component that instantiated this component (rendered it in their template)
* Returns null if component was created programmatically via $().component()
*/
instantiator() {
return this._instantiator;
}
/**
* Find descendant components by CSS selector
*/
find(selector) {
const components = [];
this.$.find(selector).each((_, el) => {
const comp = $(el).data("_component");
if (comp instanceof _Jqhtml_Component) {
components.push(comp);
}
});
return components;
}
/**
* Find closest ancestor component matching selector
*/
closest(selector) {
let current = this.$.parent();
while (current.length > 0) {
if (current.is(selector)) {
const comp = current.data("_component");
if (comp instanceof _Jqhtml_Component) {
return comp;
}
}
current = current.parent();
}
return null;
}
// -------------------------------------------------------------------------
// Static Methods
// -------------------------------------------------------------------------
/**
* Get CSS class hierarchy for this component type
*/
static get_class_hierarchy() {
const classes = [];
let ctor = this;
while (ctor) {
if (!ctor.name || typeof ctor.name !== "string") {
break;
}
if (ctor.name !== "Object" && ctor.name !== "") {
let normalizedName = ctor.name;
if (normalizedName === "_Jqhtml_Component" || normalizedName === "_Base_Jqhtml_Component") {
normalizedName = "Component";
}
classes.push(normalizedName);
}
const nextProto = Object.getPrototypeOf(ctor);
if (!nextProto || nextProto === Object.prototype || nextProto.constructor === Object) {
break;
}
ctor = nextProto;
}
return classes;
}
// -------------------------------------------------------------------------
// Private Implementation
// -------------------------------------------------------------------------
_generate_cid() {
return uid();
}
/**
* Flatten instruction array - converts ['_content', [...]] markers to flat array
* Recursively flattens nested content from content() calls
*/
_flatten_instructions(instructions) {
const result = [];
for (const instruction of instructions) {
if (Array.isArray(instruction) && instruction[0] === "_content" && Array.isArray(instruction[1])) {
const contentInstructions = this._flatten_instructions(instruction[1]);
result.push(...contentInstructions);
} else {
result.push(instruction);
}
}
return result;
}
_apply_css_classes() {
const hierarchy = this.constructor.get_class_hierarchy();
const classesToAdd = [...hierarchy];
if (this.args._component_name && this.args._component_name !== this.constructor.name) {
classesToAdd.unshift(this.args._component_name);
}
const publicClasses = classesToAdd.filter((className) => {
if (!className || typeof className !== "string") {
console.warn("[JQHTML] Filtered out invalid class name:", className);
return false;
}
return !className.startsWith("_");
});
if (publicClasses.length > 0) {
this.$.addClass(publicClasses.join(" "));
}
}
_apply_default_attributes() {
let template;
if (this.args._component_name) {
template = get_template(this.args._component_name);
} else {
template = get_template_by_class(this.constructor);
}
if (template && template.defaultAttributes) {
const defineAttrs = { ...template.defaultAttributes };
delete defineAttrs.tag;
if (window.jqhtml?.debug?.enabled) {
const componentName = template.name || this.args._component_name || this.constructor.name;
console.log(`[Component] Applying defaultAttributes for ${componentName}:`, defineAttrs);
}
for (const [key, value] of Object.entries(defineAttrs)) {
if (key === "class") {
const existingClasses = this.$.attr("class");
if (existingClasses) {
const existing = existingClasses.split(/\s+/).filter((c) => c);
const newClasses = String(value).split(/\s+/).filter((c) => c);
for (const newClass of newClasses) {
if (!existing.includes(newClass)) {
existing.push(newClass);
}
}
this.$.attr("class", existing.join(" "));
} else {
this.$.attr("class", value);
}
} else if (key === "style") {
const existingStyle = this.$.attr("style");
if (existingStyle) {
const existingRules = /* @__PURE__ */ new Map();
existingStyle.split(";").forEach((rule) => {
const [prop, val] = rule.split(":").map((s) => s.trim());
if (prop && val)
existingRules.set(prop, val);
});
String(value).split(";").forEach((rule) => {
const [prop, val] = rule.split(":").map((s) => s.trim());
if (prop && val)
existingRules.set(prop, val);
});
const merged = Array.from(existingRules.entries()).map(([prop, val]) => `${prop}: ${val}`).join("; ");
this.$.attr("style", merged);
} else {
this.$.attr("style", value);
}
} else if (key.startsWith("$") || key.startsWith("data-")) {
const dataKey = key.startsWith("$") ? key.substring(1) : key.startsWith("data-") ? key.substring(5) : key;
if (!(dataKey in this.args)) {
this.args[dataKey] = value;
this.$.data(dataKey, value);
this.$.attr(key.startsWith("$") ? `data-${dataKey}` : key, String(value));
}
} else {
if (!this.$.attr(key)) {
this.$.attr(key, value);
}
}
}
}
}
_set_attributes() {
this.$.attr("data-cid", this._cid);
if (window.jqhtml?.debug?.verbose) {
this.$.attr("data-_lifecycle-state", this._ready_state.toString());
}
}
_update_debug_attrs() {
if (window.jqhtml?.debug?.verbose) {
this.$.attr("data-_lifecycle-state", this._ready_state.toString());
}
}
_find_dom_parent() {
let current = this.$.parent();
while (current.length > 0) {
const parent = current.data("_component");
if (parent instanceof _Jqhtml_Component) {
this._dom_parent = parent;
parent._dom_children.add(this);
break;
}
current = current.parent();
}
}
/**
* Get DOM children (components in DOM subtree)
* Uses fast _dom_children registry when possible, falls back to DOM traversal for off-DOM components
* @private - Used internally for lifecycle coordination
*/
_get_dom_children() {
if (this._use_dom_fallback) {
const directChildren = [];
this.$.find(".Component").each((_, el) => {
const $el = $(el);
const comp = $el.data("_component");
if (comp instanceof _Jqhtml_Component) {
const closestParent = $el.parent().closest(".Component");
if (closestParent.length === 0 || closestParent.data("_component") === this) {
directChildren.push(comp);
}
}
});
return directChildren;
}
const children = Array.from(this._dom_children);
return children.filter((child) => {
return $.contains(document.documentElement, child.$[0]);
});
}
_log_lifecycle(phase, status) {
logLifecycle(this, phase, status);
if (typeof window !== "undefined" && window.JQHTML_DEBUG) {
window.JQHTML_DEBUG.log(this.component_name(), phase, status, {
cid: this._cid,
ready_state: this._ready_state,
args: this.args
});
}
}
_log_debug(action, ...args) {
if (typeof window !== "undefined" && window.JQHTML_DEBUG) {
window.JQHTML_DEBUG.log(this.component_name(), "debug", `${action}: ${args.map((a) => JSON.stringify(a)).join(", ")}`);
}
}
};
async function process_slot_inheritance(component, childSlots) {
let currentClass = Object.getPrototypeOf(component.constructor);
console.log(`[JQHTML] Walking prototype chain for ${component.constructor.name}`);
while (currentClass && currentClass !== Component && currentClass.name !== "Object") {
const className = currentClass.name;
console.log(`[JQHTML] Checking parent class: ${className}`);
if (className === "_Jqhtml_Component" || className === "_Base_Jqhtml_Component") {
currentClass = Object.getPrototypeOf(currentClass);
continue;
}
try {
const parentTemplate = get_template(className);
console.log(`[JQHTML] Template found for ${className}:`, parentTemplate ? parentTemplate.name : "null");
if (parentTemplate && parentTemplate.name !== "Component") {
console.log(`[JQHTML] Invoking parent template ${className}`);
const [parentInstructions, parentContext] = parentTemplate.render.call(
component,
component.data,
component.args,
childSlots
// Pass child slots as content parameter
);
if (parentInstructions && typeof parentInstructions === "object" && parentInstructions._slots) {
console.log(`[JQHTML] Parent also slot-only, recursing`);
return await process_slot_inheritance(component, parentInstructions._slots);
}
console.log(`[JQHTML] Parent returned instructions, inheritance complete`);
return [parentInstructions, parentContext];
}
} catch (error) {
console.warn(`[JQHTML] Error looking up parent template for ${className}:`, error);
}
currentClass = Object.getPrototypeOf(currentClass);
}
console.warn(`[JQHTML] No parent template found after walking chain`);
return null;
}
async function render_template(component, template_fn) {
let render_fn = template_fn;
if (!render_fn) {
const template_def = get_template_by_class(component.constructor);
render_fn = template_def.render;
}
if (!render_fn) {
return;
}
component.$.empty();
const defaultContent = () => "";
let [instructions, context] = render_fn.call(
component,
component.data,
component.args,
defaultContent
// Default content function that returns empty string
);
if (instructions && typeof instructions === "object" && instructions._slots) {
console.log(`[JQHTML] Slot-only template detected for ${component.constructor.name}, invoking inheritance`);
const result = await process_slot_inheritance(component, instructions._slots);
if (result) {
console.log(`[JQHTML] Parent template found, using parent instructions`);
instructions = result[0];
context = result[1];
} else {
console.warn(`[JQHTML] No parent template found for ${component.constructor.name}, rendering empty`);
instructions = [];
}
}
await process_instructions(instructions, component.$, component);
await process_bindings(component);
await attach_event_handlers(component);
}
async function process_bindings(component) {
component.$.find("[data-bind-prop], [data-bind-value], [data-bind-text], [data-bind-html], [data-bind-class], [data-bind-style]").each((_, element) => {
const el = $(element);
const attrs = element.attributes;
for (let i = 0; i < attrs.length; i++) {
const attr = attrs[i];
if (attr.name.startsWith("data-bind-")) {
const binding_type = attr.name.substring(10);
const expression = attr.value;
try {
const value = evaluate_expression(expression, component);
switch (binding_type) {
case "prop":
const prop_name = el.attr("data-bind-prop-name") || "value";
el.prop(prop_name, value);
break;
case "value":
el.val(value);
break;
case "text":
el.text(value);
break;
case "html":
el.html(value);
break;
case "class":
if (typeof value === "object") {
Object.entries(value).forEach(([className, enabled]) => {
el.toggleClass(className, !!enabled);
});
} else {
el.addClass(String(value));
}
break;
case "style":
if (typeof value === "object") {
el.css(value);
} else {
el.attr("style", String(value));
}
break;
default:
el.attr(binding_type, value);
}
} catch (error) {
console.error(`Error evaluating binding "${expression}":`, error);
}
}
}
});
}
async function attach_event_handlers(component) {
component.$.find("[data-on-click], [data-on-change], [data-on-submit], [data-on-keyup], [data-on-keydown], [data-on-focus], [data-on-blur]").each((_, element) => {
const el = $(element);
const attrs = element.attributes;
for (let i = 0; i < attrs.length; i++) {
const attr = attrs[i];
if (attr.name.startsWith("data-on-")) {
const event_name = attr.name.substring(8);
const handler_expr = attr.value;
el.removeAttr(attr.name);
el.on(event_name, function(event) {
try {
const handler = evaluate_handler(handler_expr, component);
if (typeof handler === "function") {
handler.call(component, event);
} else {
evaluate_expression(handler_expr, component, { $event: event });
}
} catch (error) {
console.error(`Error in ${event_name} handler "${handler_expr}":`, error);
}
});
}
}
});
}
function evaluate_expression(expression, component, locals = {}) {
const context = {
// Component properties
data: component.data,
args: component.args,
$: component.$,
// Component methods
emit: component.emit.bind(component),
$id: component.$id.bind(component),
// Locals (like $event)
...locals
};
const keys = Object.keys(context);
const values = Object.values(context);
try {
const fn = new Function(...keys, `return (${expression})`);
return fn(...values);
} catch (error) {
console.error(`Invalid expression: ${expression}`, error);
return void 0;
}
}
function evaluate_handler(expression, component) {
if (expression in component && typeof component[expression] === "function") {
return component[expression];
}
try {
return new Function("$event", `
const { data, args, $, emit, $id } = this;
${expression}
`).bind(component);
} catch (error) {
console.error(`Invalid handler: ${expression}`, error);
return null;
}
}
function escape_html(str) {
const div = document.createElement("div");
div.textContent = str;
return div.innerHTML;
}
function getJQuery() {
if (typeof window !== "undefined" && window.$) {
return window.$;
}
if (typeof window !== "undefined" && window.jQuery) {
return window.jQuery;
}
throw new Error('FATAL: jQuery is not defined. jQuery must be loaded before using JQHTML. Add <script src="https://code.jquery.com/jquery-3.7.1.min.js"><\/script> before loading JQHTML.');
}
function getJqhtml() {
if (typeof window !== "undefined" && window.jqhtml) {
return window.jqhtml;
}
if (typeof globalThis !== "undefined" && globalThis.jqhtml) {
return globalThis.jqhtml;
}
throw new Error("FATAL: window.jqhtml is not defined. The JQHTML runtime must be loaded before using JQHTML components. Ensure @jqhtml/core is imported and initialized before attempting to use debug features.");
}
var DebugOverlay = class _DebugOverlay {
constructor(options = {}) {
this.$container = null;
this.$statusIndicator = null;
this.$ = getJQuery();
if (!this.$) {
throw new Error("jQuery is required for DebugOverlay");
}
this.options = {
position: "bottom",
theme: "dark",
compact: false,
showStatus: true,
autoHide: false,
...options
};
}
/**
* Static method to show debug overlay (singleton pattern)
*/
static show(options) {
if (!_DebugOverlay.instance) {
_DebugOverlay.instance = new _DebugOverlay(options);
}
_DebugOverlay.instance.display();
return _DebugOverlay.instance;
}
/**
* Static method to hide debug overlay
*/
static hide() {
if (_DebugOverlay.instance) {
_DebugOverlay.instance.hide();
}
}
/**
* Static method to toggle debug overlay visibility
*/
static toggle() {
if (_DebugOverlay.instance && _DebugOverlay.instance.$container) {
if (_DebugOverlay.instance.$container.is(":visible")) {
_DebugOverlay.hide();
} else {
_DebugOverlay.instance.display();
}
} else {
_DebugOverlay.show();
}
}
/**
* Static method to destroy debug overlay
*/
static destroy() {
if (_DebugOverlay.instance) {
_DebugOverlay.instance.destroy();
_DebugOverlay.instance = null;
}
}
/**
* Display the debug overlay
*/
display() {
if (this.$container) {
this.$container.show();
return;
}
this.createOverlay();
if (this.options.showStatus) {
this.createStatusIndicator();
}
}
/**
* Hide the debug overlay
*/
hide() {
if (this.$container) {
this.$container.hide();
}
if (this.$statusIndicator) {
this.$statusIndicator.hide();
}
}
/**
* Remove the debug overlay completely
*/
destroy() {
if (this.$container) {
this.$container.remove();
this.$container = null;
}
if (this.$statusIndicator) {
this.$statusIndicator.remove();
this.$statusIndicator = null;
}
}
/**
* Update the status indicator
*/
updateStatus(mode) {
if (!this.$statusIndicator)
return;
this.$statusIndicator.text("Debug: " + mode);
this.$statusIndicator.attr("class", "jqhtml-debug-status" + (mode !== "Off" ? " active" : ""));
}
createOverlay() {
this.addStyles();
this.$container = this.$("<div>").addClass(`jqhtml-debug-overlay ${this.options.theme} ${this.options.position}`);
const $content = this.$("<div>").addClass("jqhtml-debug-content");
const $controls = this.$("<div>").addClass("jqhtml-debug-controls");
const $title = this.$("<span>").addClass("jqhtml-debug-title").html("<strong>\u{1F41B} JQHTML Debug:</strong>");
$controls.append($title);
const buttons = [
{ text: "Slow Motion + Flash", action: "enableSlowMotionDebug", class: "success" },
{ text: "Basic Debug", action: "enableBasicDebug", class: "" },
{ text: "Full Debug", action: "enableFullDebug", class: "" },
{ text: "Sequential", action: "enableSequentialMode", class: "" },
{ text: "Clear Debug", action: "clearAllDebug", class: "danger" },
{ text: "Settings", action: "showDebugInfo", class: "" }
];
buttons.forEach((btn) => {
const $button = this.$("<button>").text(btn.text).addClass("jqhtml-debug-btn" + (btn.class ? ` ${btn.class}` : "")).on("click", () => this.executeAction(btn.action));
$controls.append($button);
});
const $toggleBtn = this.$("<button>").text(this.options.compact ? "\u25BC" : "\u25B2").addClass("jqhtml-debug-toggle").on("click", () => this.toggle());
$controls.append($toggleBtn);
$content.append($controls);
this.$container.append($content);
this.$("body").append(this.$container);
}
createStatusIndicator() {
this.$statusIndicator = this.$("<div>").addClass("jqhtml-debug-status").text("Debug: Off").css({
position: "fixed",
top: "10px",
right: "10px",
background: "#2c3e50",
color: "white",
padding: "5px 10px",
borderRadius: "4px",
fontSize: "0.75rem",
zIndex: "10001",
opacity: "0.8",
fontFamily: "monospace"
});
this.$("body").append(this.$statusIndicator);
}
addStyles() {
if (this.$("#jqhtml-debug-styles").length > 0)
return;
const $style = this.$("<style>").attr("id", "jqhtml-debug-styles").text('.jqhtml-debug-overlay {position: fixed;left: 0;right: 0;z-index: 10000;font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;font-size: 0.8rem;box-shadow: 0 2px 10px rgba(0,0,0,0.2);}.jqhtml-debug-overlay.top {top: 0;}.jqhtml-debug-overlay.bottom {bottom: 0;}.jqhtml-debug-overlay.dark {background: #34495e;color: #ecf0f1;}.jqhtml-debug-overlay.light {background: #f8f9fa;color: #333;border-bottom: 1px solid #dee2e6;}.jqhtml-debug-content {padding: 0.5rem 1rem;}.jqhtml-debug-controls {display: flex;flex-wrap: wrap;gap: 8px;align-items: center;}.jqhtml-debug-title {margin-right: 10px;font-weight: bold;}.jqhtml-debug-btn {padding: 4px 8px;border: none;border-radius: 3px;background: #3498db;color: white;cursor: pointer;font-size: 0.75rem;transition: background 0.2s;}.jqhtml-debug-btn:hover {background: #2980b9;}.jqhtml-debug-btn.success {background: #27ae60;}.jqhtml-debug-btn.success:hover {background: #229954;}.jqhtml-debug-btn.danger {background: #e74c3c;}.jqhtml-debug-btn.danger:hover {background: #c0392b;}.jqhtml-debug-toggle {padding: 4px 8px;border: none;border-radius: 3px;background: #7f8c8d;color: white;cursor: pointer;font-size: 0.75rem;margin-left: auto;}.jqhtml-debug-toggle:hover {background: #6c7b7d;}.jqhtml-debug-status.active {background: #27ae60 !important;}@media (max-width: 768px) {.jqhtml-debug-controls {flex-direction: column;align-items: flex-start;}.jqhtml-debug-title {margin-bottom: 5px;}}');
this.$("head").append($style);
}
toggle() {
this.options.compact = !this.options.compact;
const $toggleBtn = this.$container.find(".jqhtml-debug-toggle");
$toggleBtn.text(this.options.compact ? "\u25BC" : "\u25B2");
const $buttons = this.$container.find(".jqhtml-debug-btn");
if (this.options.compact) {
$buttons.hide();
} else {
$buttons.show();
}
}
executeAction(action) {
const jqhtml2 = getJqhtml();
if (!jqhtml2) {
console.warn("JQHTML not available - make sure it's loaded and exposed globally");
return;
}
switch (action) {
case "enableSlowMotionDebug":
jqhtml2.setDebugSettings({
logFullLifecycle: true,
sequentialProcessing: true,
delayAfterComponent: 150,
delayAfterRender: 200,
delayAfterRerender: 250,
flashComponents: true,
flashDuration: 800,
flashColors: {
create: "#3498db",
render: "#27ae60",
ready: "#9b59b6"
},
profilePerformance: true,
highlightSlowRenders: 30,
logDispatch: true
});
this.updateStatus("Slow Motion");
console.log("\u{1F41B} Slow Motion Debug Mode Enabled");
break;
case "enableBasicDebug":
jqhtml2.enableDebugMode("basic");
this.updateStatus("Basic");
console.log("\u{1F41B} Basic Debug Mode Enabled");
break;
case "enableFullDebug":
jqhtml2.enableDebugMode("full");
this.updateStatus("Full");
console.log("\u{1F41B} Full Debug Mode Enabled");
break;
case "enableSequentialMode":
jqhtml2.setDebugSettings({
logCreationReady: true,
sequentialProcessing: true,
flashComponents: true,
profilePerformance: true
});
this.updateStatus("Sequential");
console.log("\u{1F41B} Sequential Processing Mode Enabled");
break;
case "clearAllDebug":
jqhtml2.clearDebugSettings();
this.updateStatus("Off");
console.log("\u{1F41B} All Debug Modes Disabled");
break;
case "showDebugInfo":
const settings = JSON.stringify(jqhtml2.debug, null, 2);
console.log("\u{1F41B} Current Debug Settings:", settings);
alert("Debug settings logged to console:\n\n" + (Object.keys(jqhtml2.debug).length > 0 ? settings : "No debug settings active"));
break;
}
}
};
DebugOverlay.instance = null;
if (typeof window !== "undefined") {
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get("debug") === "true" || urlParams.get("jqhtml-debug") === "true") {
document.addEventListener("DOMContentLoaded", () => {
DebugOverlay.show();
});
}
}
function init_jquery_plugin(jQuery) {
if (!jQuery || !jQuery.fn) {
throw new Error("jQuery is required for JQHTML. Please ensure jQuery is loaded before initializing JQHTML.");
}
if (typeof window !== "undefined" && window.$ !== jQuery && !jQuery.__jqhtml_checked) {
devWarn('jQuery instance appears to be bundled with webpack/modules rather than loaded globally.\nFor best compatibility, it is recommended to:\n1. Include jQuery via <script> tag from a CDN (UMD format)\n2. Configure webpack with: externals: { jquery: "$" }\n3. Remove jquery from package.json dependencies\n\nTo suppress this warning, set: window.JQHTML_SUPPRESS_WARNINGS = true');
jQuery.__jqhtml_checked = true;
}
const _jqhtml_original_jquery = jQuery;
const JQueryWithComponentSupport = function(selector, context) {
if (selector && typeof selector === "object" && selector.$ && typeof selector.$id === "function" && typeof selector.id === "function") {
return selector.$;
}
return new _jqhtml_original_jquery(selector, context);
};
Object.setPrototypeOf(JQueryWithComponentSupport, _jqhtml_original_jquery);
for (const key in _jqhtml_original_jquery) {
if (_jqhtml_original_jquery.hasOwnProperty(key)) {
JQueryWithComponentSupport[key] = _jqhtml_original_jquery[key];
}
}
JQueryWithComponentSupport.prototype = _jqhtml_original_jquery.prototype;
JQueryWithComponentSupport.fn = _jqhtml_original_jquery.fn;
if (typeof window !== "undefined") {
window.jQuery = JQueryWithComponentSupport;
window.$ = JQueryWithComponentSupport;
}
jQuery = JQueryWithComponentSupport;
const originalVal = jQuery.fn.val;
jQuery.fn.val = function(value) {
if (arguments.length === 0) {
const firstEl = this.first();
if (firstEl.length === 0)
return void 0;
const component = firstEl.data("_component");
const tagName = firstEl.prop("tagName");
if (component && typeof component.val === "function" && tagName !== "INPUT" && tagName !== "TEXTAREA") {
return component.val();
}
return originalVal.call(this);
} else {
this.each(function() {
const $el = jQuery(this);
const component = $el.data("_component");
const tagName = $el.prop("tagName");
if (component && typeof component.val === "function" && tagName !== "INPUT" && tagName !== "TEXTAREA") {
component.val(value);
} else {
originalVal.call($el, value);
}
});
return this;
}
};
jQuery.fn.component = function(componentOrName, args = {}) {
const element = this.first ? this.first() : this;
if (!componentOrName) {
if (element.length === 0) {
return null;
}
const comp = element.data("_component");
return comp || null;
}
const existingComponent = element.data("_component");
if (existingComponent) {
return existingComponent;
}
let ComponentClass;
let componentName;
if (typeof componentOrName === "string") {
componentName = componentOrName;
const found = get_component_class(componentOrName);
args = { ...args, _component_name: componentName };
if (!found) {
ComponentClass = Component;
} else {
ComponentClass = found;
}
} else {
ComponentClass = componentOrName;
}
let targetElement = element;
if (componentName) {
const template = get_template(componentName);
const expectedTag = args._tag || template.tag || "div";
const currentTag = element.prop("tagName").toLowerCase();
if (currentTag !== expectedTag.toLowerCase()) {
if (args._inner_html) {
const newElement = jQuery(`<${expectedTag}></${expectedTag}>`);
const oldEl = element[0];
if (oldEl && oldEl.attributes) {
for (let i = 0; i < oldEl.attributes.length; i++) {
const attr = oldEl.attributes[i];
newElement.attr(attr.name, attr.value);
}
}
newElement.html(element.html());
element.replaceWith(newElement);
targetElement = newElement;
} else {
console.warn(`[JQHTML] Component '${componentName}' expects tag '<${expectedTag}>' but element is '<${currentTag}>'. Element tag will not be changed. Consider using the correct tag.`);
}
}
}
const component = new ComponentClass(targetElement, args);
component.boot();
applyDebugDelay("component");
return component;
};
const _jqhtml_jquery_overrides = {};
const dom_insertion_methods = ["append", "prepend", "before", "after", "replaceWith"];
for (const fnname of dom_insertion_methods) {
_jqhtml_jquery_overrides[fnname] = jQuery.fn[fnname];
jQuery.fn[fnname] = function(...args) {
const resolvedArgs = args.map((arg) => {
if (arg && typeof arg === "object" && arg instanceof Component) {
return arg.$;
}
return arg;
});
const $elements = resolvedArgs.filter((arg) => arg instanceof jQuery);
const ret = _jqhtml_jquery_overrides[fnname].apply(this, resolvedArgs);
for (const $e of $elements) {
if ($e.closest("html").length > 0) {
$e.find(".Component").addBack(".Component").each(function() {
const $comp = jQuery(this);
const component = $comp.data("_component");
if (component && !component._ready_state) {
component.boot();
}
});
}
}
return ret;
};
}
jQuery.fn.shallowFind = function(selector) {
const results = [];
this.each(function() {
const traverse = (parent) => {
for (let i = 0; i < parent.children.length; i++) {
const child = parent.children[i];
if (jQuery(child).is(selector)) {
results.push(child);
} else {
traverse(child);
}
}
};
traverse(this);
});
return jQuery(results);
};
const originalEmpty = jQuery.fn.empty;
const originalHtml = jQuery.fn.html;
const originalText = jQuery.fn.text;
jQuery.fn.empty = function() {
return this.each(function() {
jQuery(this).find(".Component").each(function() {
const component = jQuery(this).data("_component");
if (component && !component._stopped) {
component._stop();
}
});
originalEmpty.call(jQuery(this));
});
};
jQuery.fn.html = function(value) {
if (arguments.length === 0) {
return originalHtml.call(this);
}
return this.each(function() {
jQuery(this).empty();
originalHtml.call(jQuery(this), value);
});
};
jQuery.fn.text = function(value) {
if (arguments.length === 0) {
return originalText.call(this);
}
return this.each(function() {
jQuery(this).empty();
originalText.call(jQuery(this), value);
});
};
}
if (typeof window !== "undefined" && window.jQuery) {
init_jquery_plugin(window.jQuery);
}
var version = "2.2.185";
var jqhtml = {
// Core
Component,
LifecycleManager,
// Registry
register_component,
get_component_class,
register_template,
get_template,
get_template_by_class,
create_component,
has_component,
get_component_names,
get_registered_templates,
list_components,
// Template system
process_instructions,
extract_slots,
render_template,
escape_html,
// Version property - internal
__version: version,
// Debug settings
debug: {
enabled: false,
verbose: false
},
// Debug helper functions (mainly for internal use but exposed for advanced debugging)
setDebugSettings(settings) {
Object.assign(this.debug, settings);
},
enableDebugMode(level = "basic") {
if (level === "basic") {
this.debug.logCreationReady = true;
this.debug.logDispatch = true;
this.debug.flashComponents = true;
} else {
this.debug.logFullLifecycle = true;
this.debug.logDispatchVerbose = true;
this.debug.flashComponents = true;
this.debug.profilePerformance = true;
this.debug.traceDataFlow = true;
}
},
clearDebugSettings() {
this.debug = {};
},
// Debug overlay methods
showDebugOverlay(options) {
return DebugOverlay.show(options);
},
hideDebugOverlay() {
return DebugOverlay.hide();
},
// Export DebugOverlay class for direct access
DebugOverlay,
// Install globals function
installGlobals() {
if (typeof window !== "undefined") {
window.jqhtml = this;
window.Component = Component;
window.Jqhtml_LifecycleManager = LifecycleManager;
}
},
// Version display function - shows version of core library and all registered templates
_version() {
console.log(`JQHTML Core v${this.__version}`);
console.log("Registered Templates:");
const templateNames = get_component_names();
if (templateNames.length === 0) {
console.log(" (no templates registered)");
} else {
for (const name of templateNames) {
const template = get_template(name);
const templateVersion = template ? template._jqhtml_version || "unknown" : "unknown";
console.log(` - ${name}: v${templateVersion}`);
}
}
return this.__version;
},
// Public version function - returns the stamped version number
version() {
return version;
}
};
if (typeof window !== "undefined" && !window.jqhtml) {
window.jqhtml = jqhtml;
window.Component = Component;
window.Component = Component;
window.Jqhtml_LifecycleManager = LifecycleManager;
if (jqhtml.debug?.enabled) {
console.log("[JQHTML] Auto-registered window.jqhtml global for template compatibility");
}
}
// storage/rsx-tmp/npm-compile/entry_6459e8ed0f60bda4f121420766012d53.js
window._rsx_npm = window._rsx_npm || {};
window._rsx_npm.jqhtml = jqhtml;
window._rsx_npm._Base_Jqhtml_Component = Component;
})();
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0b3JhZ2UvcnN4LWJ1aWxkL2J1bmRsZXMvbnBtX0Jvb3RzdHJhcDVfQnVuZGxlXzY0NTllOGVkMGY2MGJkYTRmMTIxNDIwNzY2MDEyZDUzLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsImZpbGUiOiJidW5kbGVfb3V0cHV0X0Jvb3RzdHJhcDVfQnVuZGxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIigoKSA9PiB7XG4gIC8vIG5vZGVfbW9kdWxlcy9AanFodG1sL2NvcmUvZGlzdC9pbmRleC5qc1xuICB2YXIgTGlmZWN5Y2xlTWFuYWdlciA9IGNsYXNzIF9MaWZlY3ljbGVNYW5hZ2VyIHtcbiAgICBzdGF0aWMgZ2V0X2luc3RhbmNlKCkge1xuICAgICAgaWYgKCFfTGlmZWN5Y2xlTWFuYWdlci5pbnN0YW5jZSkge1xuICAgICAgICBfTGlmZWN5Y2xlTWFuYWdlci5pbnN0YW5jZSA9IG5ldyBfTGlmZWN5Y2xlTWFuYWdlcigpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIF9MaWZlY3ljbGVNYW5hZ2VyLmluc3RhbmNlO1xuICAgIH1cbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgIHRoaXMuYWN0aXZlX2NvbXBvbmVudHMgPSAvKiBAX19QVVJFX18gKi8gbmV3IFNldCgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBCb290IGEgY29tcG9uZW50IC0gcnVuIGl0cyBmdWxsIGxpZmVjeWNsZVxuICAgICAqIENhbGxlZCB3aGVuIGNvbXBvbmVudCBpcyBjcmVhdGVkXG4gICAgICovXG4gICAgYXN5bmMgYm9vdF9jb21wb25lbnQoY29tcG9uZW50KSB7XG4gICAgICB0aGlzLmFjdGl2ZV9jb21wb25lbnRzLmFkZChjb21wb25lbnQpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgY29tcG9uZW50LmNyZWF0ZSgpO1xuICAgICAgICBpZiAoY29tcG9uZW50Ll9zdG9wcGVkKVxuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgY29tcG9uZW50LnRyaWdnZXIoXCJjcmVhdGVcIik7XG4gICAgICAgIGxldCByZW5kZXJfaWQgPSBjb21wb25lbnQuX3JlbmRlcigpO1xuICAgICAgICBpZiAoY29tcG9uZW50Ll9zdG9wcGVkKVxuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgYXdhaXQgY29tcG9uZW50LmxvYWQoKTtcbiAgICAgICAgaWYgKGNvbXBvbmVudC5fc3RvcHBlZClcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIGlmIChjb21wb25lbnQuc2hvdWxkX3JlcmVuZGVyKCkpIHtcbiAgICAgICAgICByZW5kZXJfaWQgPSBjb21wb25lbnQuX3JlbmRlcigpO1xuICAgICAgICAgIGlmIChjb21wb25lbnQuX3N0b3BwZWQpXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvbXBvbmVudC5fcmVuZGVyX2NvdW50ICE9PSByZW5kZXJfaWQpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgY29tcG9uZW50LnJlYWR5KCk7XG4gICAgICAgIGlmIChjb21wb25lbnQuX3N0b3BwZWQpXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICBhd2FpdCBjb21wb25lbnQudHJpZ2dlcihcInJlYWR5XCIpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihgRXJyb3IgYm9vdGluZyBjb21wb25lbnQgJHtjb21wb25lbnQuY29tcG9uZW50X25hbWUoKX06YCwgZXJyb3IpO1xuICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogVW5yZWdpc3RlciBhIGNvbXBvbmVudCAoY2FsbGVkIG9uIGRlc3Ryb3kpXG4gICAgICovXG4gICAgdW5yZWdpc3Rlcl9jb21wb25lbnQoY29tcG9uZW50KSB7XG4gICAgICB0aGlzLmFjdGl2ZV9jb21wb25lbnRzLmRlbGV0ZShjb21wb25lbnQpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBXYWl0IGZvciBhbGwgYWN0aXZlIGNvbXBvbmVudHMgdG8gcmVhY2ggcmVhZHkgc3RhdGVcbiAgICAgKi9cbiAgICBhc3luYyB3YWl0X2Zvcl9yZWFkeSgpIHtcbiAgICAgIGNvbnN0IHJlYWR5X3Byb21pc2VzID0gW107XG4gICAgICBmb3IgKGNvbnN0IGNvbXBvbmVudCBvZiB0aGlzLmFjdGl2ZV9jb21wb25lbnRzKSB7XG4gICAgICAgIGlmIChjb21wb25lbnQuX3JlYWR5X3N0YXRlIDwgNCkge1xuICAgICAgICAgIHJlYWR5X3Byb21pc2VzLnB1c2gobmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgIGNvbXBvbmVudC5vbihcInJlYWR5XCIsICgpID0+IHJlc29sdmUoKSk7XG4gICAgICAgICAgfSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChyZWFkeV9wcm9taXNlcyk7XG4gICAgfVxuICB9O1xuICB2YXIgY29tcG9uZW50X2NsYXNzZXMgPSAvKiBAX19QVVJFX18gKi8gbmV3IE1hcCgpO1xuICB2YXIgY29tcG9uZW50X3RlbXBsYXRlcyA9IC8qIEBfX1BVUkVfXyAqLyBuZXcgTWFwKCk7XG4gIHZhciB3YXJuZWRfY29tcG9uZW50cyA9IC8qIEBfX1BVUkVfXyAqLyBuZXcgU2V0KCk7XG4gIHZhciBERUZBVUxUX1RFTVBMQVRFID0ge1xuICAgIG5hbWU6IFwiSnFodG1sX0NvbXBvbmVudFwiLFxuICAgIC8vIERlZmF1bHQgbmFtZVxuICAgIHRhZzogXCJkaXZcIixcbiAgICByZW5kZXI6IGZ1bmN0aW9uKGRhdGEsIGFyZ3MsIGNvbnRlbnQpIHtcbiAgICAgIGNvbnN0IF9vdXRwdXQgPSBbXTtcbiAgICAgIGlmIChhcmdzLl9pbm5lcl9odG1sKSB7XG4gICAgICAgIF9vdXRwdXQucHVzaChhcmdzLl9pbm5lcl9odG1sKTtcbiAgICAgICAgcmV0dXJuIFtfb3V0cHV0LCB0aGlzXTtcbiAgICAgIH1cbiAgICAgIGlmIChjb250ZW50ICYmIHR5cGVvZiBjb250ZW50ID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gY29udGVudCh0aGlzKTtcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkocmVzdWx0KSAmJiByZXN1bHQubGVuZ3RoID09PSAyKSB7XG4gICAgICAgICAgX291dHB1dC5wdXNoKC4uLnJlc3VsdFswXSk7XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHJlc3VsdCA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgICAgIF9vdXRwdXQucHVzaChyZXN1bHQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gW19vdXRwdXQsIHRoaXNdO1xuICAgIH1cbiAgfTtcbiAgZnVuY3Rpb24gcmVnaXN0ZXJfY29tcG9uZW50KG5hbWVPckNsYXNzLCBjb21wb25lbnRfY2xhc3MsIHRlbXBsYXRlKSB7XG4gICAgaWYgKHR5cGVvZiBuYW1lT3JDbGFzcyA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgY29uc3QgbmFtZSA9IG5hbWVPckNsYXNzO1xuICAgICAgaWYgKCFjb21wb25lbnRfY2xhc3MpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiQ29tcG9uZW50IGNsYXNzIGlzIHJlcXVpcmVkIHdoZW4gcmVnaXN0ZXJpbmcgYnkgbmFtZVwiKTtcbiAgICAgIH1cbiAgICAgIGlmICghL15bQS1aXS8udGVzdChuYW1lKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYENvbXBvbmVudCBuYW1lICcke25hbWV9JyBtdXN0IHN0YXJ0IHdpdGggYSBjYXBpdGFsIGxldHRlci4gQ29udmVudGlvbiBpcyBGaXJzdF9MZXR0ZXJfV2l0aF9VbmRlcnNjb3Jlcy5gKTtcbiAgICAgIH1cbiAgICAgIGNvbXBvbmVudF9jbGFzc2VzLnNldChuYW1lLCBjb21wb25lbnRfY2xhc3MpO1xuICAgICAgaWYgKHRlbXBsYXRlKSB7XG4gICAgICAgIGlmICh0ZW1wbGF0ZS5uYW1lICE9PSBuYW1lKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBUZW1wbGF0ZSBuYW1lICcke3RlbXBsYXRlLm5hbWV9JyBtdXN0IG1hdGNoIGNvbXBvbmVudCBuYW1lICcke25hbWV9J2ApO1xuICAgICAgICB9XG4gICAgICAgIHJlZ2lzdGVyX3RlbXBsYXRlKHRlbXBsYXRlKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgY29tcG9uZW50X2NsYXNzMiA9IG5hbWVPckNsYXNzO1xuICAgICAgY29uc3QgbmFtZSA9IGNvbXBvbmVudF9jbGFzczIubmFtZTtcbiAgICAgIGlmICghbmFtZSB8fCBuYW1lID09PSBcIkpxaHRtbF9Db21wb25lbnRcIikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDb21wb25lbnQgY2xhc3MgbXVzdCBoYXZlIGEgbmFtZSB3aGVuIHJlZ2lzdGVyaW5nIHdpdGhvdXQgZXhwbGljaXQgbmFtZVwiKTtcbiAgICAgIH1cbiAgICAgIGNvbXBvbmVudF9jbGFzc2VzLnNldChuYW1lLCBjb21wb25lbnRfY2xhc3MyKTtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gZ2V0X2NvbXBvbmVudF9jbGFzcyhuYW1lKSB7XG4gICAgY29uc3QgZGlyZWN0Q2xhc3MgPSBjb21wb25lbnRfY2xhc3Nlcy5nZXQobmFtZSk7XG4gICAgaWYgKGRpcmVjdENsYXNzKSB7XG4gICAgICByZXR1cm4gZGlyZWN0Q2xhc3M7XG4gICAgfVxuICAgIGNvbnN0IHRlbXBsYXRlID0gY29tcG9uZW50X3RlbXBsYXRlcy5nZXQobmFtZSk7XG4gICAgaWYgKHRlbXBsYXRlICYmIHRlbXBsYXRlLmV4dGVuZHMpIHtcbiAgICAgIGNvbnN0IHZpc2l0ZWQgPSAvKiBAX19QVVJFX18gKi8gbmV3IFNldChbbmFtZV0pO1xuICAgICAgbGV0IGN1cnJlbnRUZW1wbGF0ZU5hbWUgPSB0ZW1wbGF0ZS5leHRlbmRzO1xuICAgICAgd2hpbGUgKGN1cnJlbnRUZW1wbGF0ZU5hbWUgJiYgIXZpc2l0ZWQuaGFzKGN1cnJlbnRUZW1wbGF0ZU5hbWUpKSB7XG4gICAgICAgIHZpc2l0ZWQuYWRkKGN1cnJlbnRUZW1wbGF0ZU5hbWUpO1xuICAgICAgICBjb25zdCBwYXJlbnRDbGFzcyA9IGNvbXBvbmVudF9jbGFzc2VzLmdldChjdXJyZW50VGVtcGxhdGVOYW1lKTtcbiAgICAgICAgaWYgKHBhcmVudENsYXNzKSB7XG4gICAgICAgICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy5lbmFibGVkKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gQ29tcG9uZW50ICcke25hbWV9JyB1c2luZyBjbGFzcyBmcm9tIHBhcmVudCAnJHtjdXJyZW50VGVtcGxhdGVOYW1lfScgdmlhIGV4dGVuZHMgY2hhaW5gKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHBhcmVudENsYXNzO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHBhcmVudFRlbXBsYXRlID0gY29tcG9uZW50X3RlbXBsYXRlcy5nZXQoY3VycmVudFRlbXBsYXRlTmFtZSk7XG4gICAgICAgIGlmIChwYXJlbnRUZW1wbGF0ZSAmJiBwYXJlbnRUZW1wbGF0ZS5leHRlbmRzKSB7XG4gICAgICAgICAgY3VycmVudFRlbXBsYXRlTmFtZSA9IHBhcmVudFRlbXBsYXRlLmV4dGVuZHM7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHZvaWQgMDtcbiAgfVxuICBmdW5jdGlvbiByZWdpc3Rlcl90ZW1wbGF0ZSh0ZW1wbGF0ZV9kZWYpIHtcbiAgICBjb25zdCBuYW1lID0gdGVtcGxhdGVfZGVmLm5hbWU7XG4gICAgaWYgKCFuYW1lKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJUZW1wbGF0ZSBtdXN0IGhhdmUgYSBuYW1lIHByb3BlcnR5XCIpO1xuICAgIH1cbiAgICBpZiAoIS9eW0EtWl0vLnRlc3QobmFtZSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVGVtcGxhdGUgbmFtZSAnJHtuYW1lfScgbXVzdCBzdGFydCB3aXRoIGEgY2FwaXRhbCBsZXR0ZXIuIENvbnZlbnRpb24gaXMgRmlyc3RfTGV0dGVyX1dpdGhfVW5kZXJzY29yZXMuYCk7XG4gICAgfVxuICAgIGlmIChjb21wb25lbnRfdGVtcGxhdGVzLmhhcyhuYW1lKSkge1xuICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBUZW1wbGF0ZSAnJHtuYW1lfScgYWxyZWFkeSByZWdpc3RlcmVkLCBza2lwcGluZyBkdXBsaWNhdGUgcmVnaXN0cmF0aW9uYCk7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGNvbXBvbmVudF90ZW1wbGF0ZXMuc2V0KG5hbWUsIHRlbXBsYXRlX2RlZik7XG4gICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy5lbmFibGVkKSB7XG4gICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gU3VjY2Vzc2Z1bGx5IHJlZ2lzdGVyZWQgdGVtcGxhdGU6ICR7bmFtZX1gKTtcbiAgICB9XG4gICAgY29uc3QgY29tcG9uZW50X2NsYXNzID0gY29tcG9uZW50X2NsYXNzZXMuZ2V0KG5hbWUpO1xuICAgIGlmIChjb21wb25lbnRfY2xhc3MpIHtcbiAgICAgIGNvbXBvbmVudF9jbGFzcy5fanFodG1sX21ldGFkYXRhID0ge1xuICAgICAgICB0YWc6IHRlbXBsYXRlX2RlZi50YWcsXG4gICAgICAgIGRlZmF1bHRBdHRyaWJ1dGVzOiB0ZW1wbGF0ZV9kZWYuZGVmYXVsdEF0dHJpYnV0ZXMgfHwge31cbiAgICAgIH07XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xuICB9XG4gIGZ1bmN0aW9uIGdldF90ZW1wbGF0ZShuYW1lKSB7XG4gICAgY29uc3QgdGVtcGxhdGUgPSBjb21wb25lbnRfdGVtcGxhdGVzLmdldChuYW1lKTtcbiAgICBpZiAoIXRlbXBsYXRlKSB7XG4gICAgICBjb25zdCBjb21wb25lbnRfY2xhc3MgPSBjb21wb25lbnRfY2xhc3Nlcy5nZXQobmFtZSk7XG4gICAgICBpZiAoY29tcG9uZW50X2NsYXNzKSB7XG4gICAgICAgIGNvbnN0IGluaGVyaXRlZF90ZW1wbGF0ZSA9IGdldF90ZW1wbGF0ZV9ieV9jbGFzcyhjb21wb25lbnRfY2xhc3MpO1xuICAgICAgICBpZiAoaW5oZXJpdGVkX3RlbXBsYXRlICE9PSBERUZBVUxUX1RFTVBMQVRFKSB7XG4gICAgICAgICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy5lbmFibGVkKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gQ29tcG9uZW50ICcke25hbWV9JyBoYXMgbm8gdGVtcGxhdGUsIHVzaW5nIHRlbXBsYXRlIGZyb20gcHJvdG90eXBlIGNoYWluYCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBpbmhlcml0ZWRfdGVtcGxhdGU7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy5lbmFibGVkICYmICF3YXJuZWRfY29tcG9uZW50cy5oYXMobmFtZSkpIHtcbiAgICAgICAgICB3YXJuZWRfY29tcG9uZW50cy5hZGQobmFtZSk7XG4gICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdIE5vIHRlbXBsYXRlIGZvdW5kIGZvciBjbGFzczogJHtuYW1lfSwgdXNpbmcgZGVmYXVsdCBkaXYgdGVtcGxhdGVgKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKG5hbWUgIT09IFwiX0pxaHRtbF9Db21wb25lbnRcIiAmJiBuYW1lICE9PSBcIlJlZHJhd2FibGVcIiAmJiAhd2FybmVkX2NvbXBvbmVudHMuaGFzKG5hbWUpKSB7XG4gICAgICAgICAgd2FybmVkX2NvbXBvbmVudHMuYWRkKG5hbWUpO1xuICAgICAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gQ3JlYXRpbmcgJHtuYW1lfSB3aXRoIGRlZmF1bHRzIC0gbm8gdGVtcGxhdGUgb3IgY2xhc3MgZGVmaW5lZGApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LnZlcmJvc2UpIHtcbiAgICAgICAgY29uc3QgcmVnaXN0ZXJlZCA9IEFycmF5LmZyb20oY29tcG9uZW50X3RlbXBsYXRlcy5rZXlzKCkpO1xuICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gTG9va2luZyBmb3IgdGVtcGxhdGUgJyR7bmFtZX0nIGluOiBbJHtyZWdpc3RlcmVkLmpvaW4oXCIsIFwiKX1dYCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gREVGQVVMVF9URU1QTEFURTtcbiAgICB9XG4gICAgcmV0dXJuIHRlbXBsYXRlO1xuICB9XG4gIGZ1bmN0aW9uIGdldF90ZW1wbGF0ZV9ieV9jbGFzcyhjb21wb25lbnRfY2xhc3MpIHtcbiAgICBpZiAoY29tcG9uZW50X2NsYXNzLnRlbXBsYXRlKSB7XG4gICAgICByZXR1cm4gY29tcG9uZW50X2NsYXNzLnRlbXBsYXRlO1xuICAgIH1cbiAgICBsZXQgY3VycmVudENsYXNzID0gY29tcG9uZW50X2NsYXNzO1xuICAgIHdoaWxlIChjdXJyZW50Q2xhc3MgJiYgY3VycmVudENsYXNzLm5hbWUgIT09IFwiT2JqZWN0XCIpIHtcbiAgICAgIGxldCBub3JtYWxpemVkTmFtZSA9IGN1cnJlbnRDbGFzcy5uYW1lO1xuICAgICAgaWYgKG5vcm1hbGl6ZWROYW1lID09PSBcIl9KcWh0bWxfQ29tcG9uZW50XCIgfHwgbm9ybWFsaXplZE5hbWUgPT09IFwiX0Jhc2VfSnFodG1sX0NvbXBvbmVudFwiKSB7XG4gICAgICAgIG5vcm1hbGl6ZWROYW1lID0gXCJKcWh0bWxfQ29tcG9uZW50XCI7XG4gICAgICB9XG4gICAgICBjb25zdCB0ZW1wbGF0ZSA9IGNvbXBvbmVudF90ZW1wbGF0ZXMuZ2V0KG5vcm1hbGl6ZWROYW1lKTtcbiAgICAgIGlmICh0ZW1wbGF0ZSkge1xuICAgICAgICByZXR1cm4gdGVtcGxhdGU7XG4gICAgICB9XG4gICAgICBjdXJyZW50Q2xhc3MgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YoY3VycmVudENsYXNzKTtcbiAgICB9XG4gICAgcmV0dXJuIERFRkFVTFRfVEVNUExBVEU7XG4gIH1cbiAgZnVuY3Rpb24gY3JlYXRlX2NvbXBvbmVudChuYW1lLCBlbGVtZW50LCBhcmdzID0ge30pIHtcbiAgICBjb25zdCBDb21wb25lbnRDbGFzcyA9IGdldF9jb21wb25lbnRfY2xhc3MobmFtZSkgfHwgSnFodG1sX0NvbXBvbmVudDtcbiAgICByZXR1cm4gbmV3IENvbXBvbmVudENsYXNzKGVsZW1lbnQsIGFyZ3MpO1xuICB9XG4gIGZ1bmN0aW9uIGhhc19jb21wb25lbnQobmFtZSkge1xuICAgIHJldHVybiBjb21wb25lbnRfY2xhc3Nlcy5oYXMobmFtZSk7XG4gIH1cbiAgZnVuY3Rpb24gZ2V0X2NvbXBvbmVudF9uYW1lcygpIHtcbiAgICByZXR1cm4gQXJyYXkuZnJvbShjb21wb25lbnRfY2xhc3Nlcy5rZXlzKCkpO1xuICB9XG4gIGZ1bmN0aW9uIGdldF9yZWdpc3RlcmVkX3RlbXBsYXRlcygpIHtcbiAgICByZXR1cm4gQXJyYXkuZnJvbShjb21wb25lbnRfdGVtcGxhdGVzLmtleXMoKSk7XG4gIH1cbiAgZnVuY3Rpb24gbGlzdF9jb21wb25lbnRzKCkge1xuICAgIGNvbnN0IHJlc3VsdCA9IHt9O1xuICAgIGZvciAoY29uc3QgbmFtZSBvZiBjb21wb25lbnRfY2xhc3Nlcy5rZXlzKCkpIHtcbiAgICAgIHJlc3VsdFtuYW1lXSA9IHtcbiAgICAgICAgaGFzX2NsYXNzOiB0cnVlLFxuICAgICAgICBoYXNfdGVtcGxhdGU6IGNvbXBvbmVudF90ZW1wbGF0ZXMuaGFzKG5hbWUpXG4gICAgICB9O1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IG5hbWUgb2YgY29tcG9uZW50X3RlbXBsYXRlcy5rZXlzKCkpIHtcbiAgICAgIGlmICghcmVzdWx0W25hbWVdKSB7XG4gICAgICAgIHJlc3VsdFtuYW1lXSA9IHtcbiAgICAgICAgICBoYXNfY2xhc3M6IGZhbHNlLFxuICAgICAgICAgIGhhc190ZW1wbGF0ZTogdHJ1ZVxuICAgICAgICB9O1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIHZhciBfY2lkX2luY3JlbWVudCA9IFwiYWFcIjtcbiAgZnVuY3Rpb24gdWlkKCkge1xuICAgIGNvbnN0IGN1cnJlbnQgPSBfY2lkX2luY3JlbWVudDtcbiAgICBjb25zdCBjaGFycyA9IF9jaWRfaW5jcmVtZW50LnNwbGl0KFwiXCIpO1xuICAgIGxldCBjYXJyeSA9IHRydWU7XG4gICAgZm9yIChsZXQgaSA9IGNoYXJzLmxlbmd0aCAtIDE7IGkgPj0gMCAmJiBjYXJyeTsgaS0tKSB7XG4gICAgICBjb25zdCBjaGFyID0gY2hhcnNbaV07XG4gICAgICBpZiAoY2hhciA+PSBcImFcIiAmJiBjaGFyIDwgXCJ6XCIpIHtcbiAgICAgICAgY2hhcnNbaV0gPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGNoYXIuY2hhckNvZGVBdCgwKSArIDEpO1xuICAgICAgICBjYXJyeSA9IGZhbHNlO1xuICAgICAgfSBlbHNlIGlmIChjaGFyID09PSBcInpcIikge1xuICAgICAgICBjaGFyc1tpXSA9IFwiMFwiO1xuICAgICAgICBjYXJyeSA9IGZhbHNlO1xuICAgICAgfSBlbHNlIGlmIChjaGFyID49IFwiMFwiICYmIGNoYXIgPCBcIjlcIikge1xuICAgICAgICBjaGFyc1tpXSA9IFN0cmluZy5mcm9tQ2hhckNvZGUoY2hhci5jaGFyQ29kZUF0KDApICsgMSk7XG4gICAgICAgIGNhcnJ5ID0gZmFsc2U7XG4gICAgICB9IGVsc2UgaWYgKGNoYXIgPT09IFwiOVwiKSB7XG4gICAgICAgIGNoYXJzW2ldID0gXCJhXCI7XG4gICAgICAgIGNhcnJ5ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGNhcnJ5KSB7XG4gICAgICBjaGFycy51bnNoaWZ0KFwiYVwiKTtcbiAgICB9XG4gICAgaWYgKGNoYXJzWzBdID49IFwiMFwiICYmIGNoYXJzWzBdIDw9IFwiOVwiKSB7XG4gICAgICBjaGFyc1swXSA9IFwiYVwiO1xuICAgICAgY2hhcnMudW5zaGlmdChcImFcIik7XG4gICAgfVxuICAgIF9jaWRfaW5jcmVtZW50ID0gY2hhcnMuam9pbihcIlwiKTtcbiAgICByZXR1cm4gY3VycmVudDtcbiAgfVxuICBmdW5jdGlvbiBwcm9jZXNzX2luc3RydWN0aW9ucyhpbnN0cnVjdGlvbnMsIHRhcmdldCwgY29udGV4dCwgc2xvdHMpIHtcbiAgICBjb25zdCBodG1sID0gW107XG4gICAgY29uc3QgdGFnRWxlbWVudHMgPSB7fTtcbiAgICBjb25zdCBjb21wb25lbnRzID0ge307XG4gICAgZm9yIChjb25zdCBpbnN0cnVjdGlvbiBvZiBpbnN0cnVjdGlvbnMpIHtcbiAgICAgIHByb2Nlc3NfaW5zdHJ1Y3Rpb25fdG9faHRtbChpbnN0cnVjdGlvbiwgaHRtbCwgdGFnRWxlbWVudHMsIGNvbXBvbmVudHMsIGNvbnRleHQsIHNsb3RzKTtcbiAgICB9XG4gICAgdGFyZ2V0WzBdLmlubmVySFRNTCA9IGh0bWwuam9pbihcIlwiKTtcbiAgICBmb3IgKGNvbnN0IFt0aWQsIHRhZ0RhdGFdIG9mIE9iamVjdC5lbnRyaWVzKHRhZ0VsZW1lbnRzKSkge1xuICAgICAgY29uc3QgZWwgPSB0YXJnZXRbMF0ucXVlcnlTZWxlY3RvcihgW2RhdGEtdGlkPVwiJHt0aWR9XCJdYCk7XG4gICAgICBpZiAoZWwpIHtcbiAgICAgICAgY29uc3QgZWxlbWVudCA9ICQoZWwpO1xuICAgICAgICBlbC5yZW1vdmVBdHRyaWJ1dGUoXCJkYXRhLXRpZFwiKTtcbiAgICAgICAgYXBwbHlfYXR0cmlidXRlcyhlbGVtZW50LCB0YWdEYXRhLmF0dHJzLCBjb250ZXh0KTtcbiAgICAgIH1cbiAgICB9XG4gICAgZm9yIChjb25zdCBbY2lkLCBjb21wRGF0YV0gb2YgT2JqZWN0LmVudHJpZXMoY29tcG9uZW50cykpIHtcbiAgICAgIGNvbnN0IGVsID0gdGFyZ2V0WzBdLnF1ZXJ5U2VsZWN0b3IoYFtkYXRhLWNpZD1cIiR7Y2lkfVwiXWApO1xuICAgICAgaWYgKGVsKSB7XG4gICAgICAgIGNvbnN0IGVsZW1lbnQgPSAkKGVsKTtcbiAgICAgICAgZWwucmVtb3ZlQXR0cmlidXRlKFwiZGF0YS1jaWRcIik7XG4gICAgICAgIGluaXRpYWxpemVfY29tcG9uZW50KGVsZW1lbnQsIGNvbXBEYXRhKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gcHJvY2Vzc19pbnN0cnVjdGlvbl90b19odG1sKGluc3RydWN0aW9uLCBodG1sLCB0YWdFbGVtZW50cywgY29tcG9uZW50cywgY29udGV4dCwgc2xvdHMpIHtcbiAgICBpZiAodHlwZW9mIGluc3RydWN0aW9uID09PSBcInN0cmluZ1wiKSB7XG4gICAgICBodG1sLnB1c2goaW5zdHJ1Y3Rpb24pO1xuICAgIH0gZWxzZSBpZiAoXCJ0YWdcIiBpbiBpbnN0cnVjdGlvbikge1xuICAgICAgcHJvY2Vzc190YWdfdG9faHRtbChpbnN0cnVjdGlvbiwgaHRtbCwgdGFnRWxlbWVudHMsIGNvbXBvbmVudHMsIGNvbnRleHQpO1xuICAgIH0gZWxzZSBpZiAoXCJjb21wXCIgaW4gaW5zdHJ1Y3Rpb24pIHtcbiAgICAgIHByb2Nlc3NfY29tcG9uZW50X3RvX2h0bWwoaW5zdHJ1Y3Rpb24sIGh0bWwsIGNvbXBvbmVudHMsIGNvbnRleHQpO1xuICAgIH0gZWxzZSBpZiAoXCJzbG90XCIgaW4gaW5zdHJ1Y3Rpb24pIHtcbiAgICAgIHByb2Nlc3Nfc2xvdF90b19odG1sKGluc3RydWN0aW9uLCBodG1sLCB0YWdFbGVtZW50cywgY29tcG9uZW50cywgY29udGV4dCwgc2xvdHMpO1xuICAgIH0gZWxzZSBpZiAoXCJyYXd0YWdcIiBpbiBpbnN0cnVjdGlvbikge1xuICAgICAgcHJvY2Vzc19yYXd0YWdfdG9faHRtbChpbnN0cnVjdGlvbiwgaHRtbCk7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIHByb2Nlc3NfdGFnX3RvX2h0bWwoaW5zdHJ1Y3Rpb24sIGh0bWwsIHRhZ0VsZW1lbnRzLCBjb21wb25lbnRzLCBjb250ZXh0KSB7XG4gICAgY29uc3QgW3RhZ05hbWUsIGF0dHJzLCBzZWxmQ2xvc2luZ10gPSBpbnN0cnVjdGlvbi50YWc7XG4gICAgY29uc3QgbmVlZHNUcmFja2luZyA9IE9iamVjdC5rZXlzKGF0dHJzKS5zb21lKChrZXkpID0+IGtleSA9PT0gXCIkaWRcIiB8fCBrZXkuc3RhcnRzV2l0aChcIiRcIikgfHwga2V5LnN0YXJ0c1dpdGgoXCJAXCIpIHx8IGtleS5zdGFydHNXaXRoKFwib25cIikgfHwga2V5LnN0YXJ0c1dpdGgoXCJkYXRhLWJpbmQtXCIpIHx8IGtleS5zdGFydHNXaXRoKFwiZGF0YS1vbi1cIikpO1xuICAgIGh0bWwucHVzaChgPCR7dGFnTmFtZX1gKTtcbiAgICBsZXQgdGlkID0gbnVsbDtcbiAgICBpZiAobmVlZHNUcmFja2luZykge1xuICAgICAgdGlkID0gdWlkKCk7XG4gICAgICBodG1sLnB1c2goYCBkYXRhLXRpZD1cIiR7dGlkfVwiYCk7XG4gICAgICB0YWdFbGVtZW50c1t0aWRdID0geyBhdHRycywgY29udGV4dCB9O1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhhdHRycykpIHtcbiAgICAgIGlmICgha2V5LnN0YXJ0c1dpdGgoXCIkXCIpICYmICFrZXkuc3RhcnRzV2l0aChcIm9uXCIpICYmICFrZXkuc3RhcnRzV2l0aChcIkBcIikgJiYgIWtleS5zdGFydHNXaXRoKFwiZGF0YS1iaW5kLVwiKSAmJiAha2V5LnN0YXJ0c1dpdGgoXCJkYXRhLW9uLVwiKSAmJiAodHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiIHx8IHR5cGVvZiB2YWx1ZSA9PT0gXCJudW1iZXJcIikpIHtcbiAgICAgICAgaWYgKGtleSA9PT0gXCJpZFwiICYmIHRpZCkge1xuICAgICAgICAgIGh0bWwucHVzaChgIGlkPVwiJHt2YWx1ZX06JHtjb250ZXh0Ll9jaWR9XCJgKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBodG1sLnB1c2goYCAke2tleX09XCIke3ZhbHVlfVwiYCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHNlbGZDbG9zaW5nKSB7XG4gICAgICBodG1sLnB1c2goXCIgLz5cIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIGh0bWwucHVzaChcIj5cIik7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIHByb2Nlc3NfY29tcG9uZW50X3RvX2h0bWwoaW5zdHJ1Y3Rpb24sIGh0bWwsIGNvbXBvbmVudHMsIGNvbnRleHQpIHtcbiAgICBjb25zdCBbY29tcG9uZW50TmFtZSwgcHJvcHMsIGNvbnRlbnRGbl0gPSBpbnN0cnVjdGlvbi5jb21wO1xuICAgIGNvbnN0IGNpZCA9IHVpZCgpO1xuICAgIGdldF9jb21wb25lbnRfY2xhc3MoY29tcG9uZW50TmFtZSkgfHwgSnFodG1sX0NvbXBvbmVudDtcbiAgICBjb25zdCB0ZW1wbGF0ZSA9IGdldF90ZW1wbGF0ZShjb21wb25lbnROYW1lKTtcbiAgICBjb25zdCB0YWdOYW1lID0gcHJvcHMuX3RhZyB8fCB0ZW1wbGF0ZS50YWcgfHwgXCJkaXZcIjtcbiAgICBodG1sLnB1c2goYDwke3RhZ05hbWV9IGRhdGEtY2lkPVwiJHtjaWR9XCJgKTtcbiAgICBpZiAocHJvcHNbXCJkYXRhLWlkXCJdKSB7XG4gICAgICBjb25zdCBiYXNlSWQgPSBwcm9wc1tcImRhdGEtaWRcIl07XG4gICAgICBodG1sLnB1c2goYCBpZD1cIiR7cHJvcHNbXCJpZFwiXX1cIiBkYXRhLWlkPVwiJHtiYXNlSWR9XCJgKTtcbiAgICB9IGVsc2UgaWYgKHByb3BzW1wiaWRcIl0pIHtcbiAgICAgIGh0bWwucHVzaChgIGlkPVwiJHtwcm9wc1tcImlkXCJdfVwiYCk7XG4gICAgfVxuICAgIGh0bWwucHVzaChcIj48L1wiICsgdGFnTmFtZSArIFwiPlwiKTtcbiAgICBjb21wb25lbnRzW2NpZF0gPSB7XG4gICAgICBuYW1lOiBjb21wb25lbnROYW1lLFxuICAgICAgcHJvcHMsXG4gICAgICBjb250ZW50Rm4sXG4gICAgICBjb250ZXh0XG4gICAgfTtcbiAgfVxuICBmdW5jdGlvbiBwcm9jZXNzX3Nsb3RfdG9faHRtbChpbnN0cnVjdGlvbiwgaHRtbCwgdGFnRWxlbWVudHMsIGNvbXBvbmVudHMsIGNvbnRleHQsIHBhcmVudFNsb3RzKSB7XG4gICAgY29uc3QgW3Nsb3ROYW1lXSA9IGluc3RydWN0aW9uLnNsb3Q7XG4gICAgaWYgKHBhcmVudFNsb3RzICYmIHNsb3ROYW1lIGluIHBhcmVudFNsb3RzKSB7XG4gICAgICBjb25zdCBwYXJlbnRTbG90ID0gcGFyZW50U2xvdHNbc2xvdE5hbWVdO1xuICAgICAgY29uc3QgWywgc2xvdFByb3BzLCBjb250ZW50Rm5dID0gcGFyZW50U2xvdC5zbG90O1xuICAgICAgY29uc3QgW2NvbnRlbnRdID0gY29udGVudEZuLmNhbGwoY29udGV4dCwgc2xvdFByb3BzKTtcbiAgICAgIGZvciAoY29uc3QgaXRlbSBvZiBjb250ZW50KSB7XG4gICAgICAgIHByb2Nlc3NfaW5zdHJ1Y3Rpb25fdG9faHRtbChpdGVtLCBodG1sLCB0YWdFbGVtZW50cywgY29tcG9uZW50cywgY29udGV4dCk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChzbG90TmFtZSA9PT0gXCJkZWZhdWx0XCIgJiYgaW5zdHJ1Y3Rpb24uc2xvdFsyXSkge1xuICAgICAgY29uc3QgWywgLCBkZWZhdWx0Rm5dID0gaW5zdHJ1Y3Rpb24uc2xvdDtcbiAgICAgIGNvbnN0IFtjb250ZW50XSA9IGRlZmF1bHRGbi5jYWxsKGNvbnRleHQsIHt9KTtcbiAgICAgIGZvciAoY29uc3QgaXRlbSBvZiBjb250ZW50KSB7XG4gICAgICAgIHByb2Nlc3NfaW5zdHJ1Y3Rpb25fdG9faHRtbChpdGVtLCBodG1sLCB0YWdFbGVtZW50cywgY29tcG9uZW50cywgY29udGV4dCk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIHByb2Nlc3NfcmF3dGFnX3RvX2h0bWwoaW5zdHJ1Y3Rpb24sIGh0bWwpIHtcbiAgICBjb25zdCBbdGFnTmFtZSwgYXR0cnMsIHJhd0NvbnRlbnRdID0gaW5zdHJ1Y3Rpb24ucmF3dGFnO1xuICAgIGh0bWwucHVzaChgPCR7dGFnTmFtZX1gKTtcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhhdHRycykpIHtcbiAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgfHwgdHlwZW9mIHZhbHVlID09PSBcIm51bWJlclwiKSB7XG4gICAgICAgIGNvbnN0IGVzY2FwZWRfdmFsdWUgPSBTdHJpbmcodmFsdWUpLnJlcGxhY2UoL1wiL2csIFwiJnF1b3Q7XCIpO1xuICAgICAgICBodG1sLnB1c2goYCAke2tleX09XCIke2VzY2FwZWRfdmFsdWV9XCJgKTtcbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSBcImJvb2xlYW5cIiAmJiB2YWx1ZSkge1xuICAgICAgICBodG1sLnB1c2goYCAke2tleX1gKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaHRtbC5wdXNoKFwiPlwiKTtcbiAgICBjb25zdCBlc2NhcGVkX2NvbnRlbnQgPSByYXdDb250ZW50LnJlcGxhY2UoLyYvZywgXCImYW1wO1wiKS5yZXBsYWNlKC88L2csIFwiJmx0O1wiKS5yZXBsYWNlKC8+L2csIFwiJmd0O1wiKTtcbiAgICBodG1sLnB1c2goZXNjYXBlZF9jb250ZW50KTtcbiAgICBodG1sLnB1c2goYDwvJHt0YWdOYW1lfT5gKTtcbiAgfVxuICBmdW5jdGlvbiBhcHBseV9hdHRyaWJ1dGVzKGVsZW1lbnQsIGF0dHJzLCBjb250ZXh0KSB7XG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoYXR0cnMpKSB7XG4gICAgICBpZiAoa2V5ID09PSBcIiRpZFwiIHx8IGtleSA9PT0gXCJpZFwiKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfSBlbHNlIGlmIChrZXkuc3RhcnRzV2l0aChcIiRcIikpIHtcbiAgICAgICAgY29uc3QgZGF0YUtleSA9IGtleS5zdWJzdHJpbmcoMSk7XG4gICAgICAgIGVsZW1lbnQuZGF0YShkYXRhS2V5LCB2YWx1ZSk7XG4gICAgICAgIGNvbnRleHQuYXJnc1tkYXRhS2V5XSA9IHZhbHVlO1xuICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09IFwic3RyaW5nXCIgfHwgdHlwZW9mIHZhbHVlID09IFwibnVtYmVyXCIpIHtcbiAgICAgICAgICBjb25zdCBhdHRyVmFsdWUgPSB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgPyB2YWx1ZS50cmltKCkgOiB2YWx1ZTtcbiAgICAgICAgICBlbGVtZW50LmF0dHIoYGRhdGEtJHtkYXRhS2V5fWAsIGF0dHJWYWx1ZSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoa2V5LnN0YXJ0c1dpdGgoXCJkYXRhLW9uLVwiKSkge1xuICAgICAgICBjb25zdCBldmVudE5hbWUgPSBrZXkuc3Vic3RyaW5nKDgpO1xuICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICBlbGVtZW50Lm9uKGV2ZW50TmFtZSwgZnVuY3Rpb24oZSkge1xuICAgICAgICAgICAgdmFsdWUuYmluZChjb250ZXh0KShlLCBlbGVtZW50KTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zb2xlLndhcm4oXCIoSlFIVE1MKSBUcmllZCB0byBhc3NpZ24gYSBub24gZnVuY3Rpb24gdG8gb24gZXZlbnQgaGFuZGxlciBcIiArIGtleSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoa2V5LnN0YXJ0c1dpdGgoXCJvblwiKSkge1xuICAgICAgICBjb25zdCBldmVudE5hbWUgPSBrZXkuc3Vic3RyaW5nKDIpO1xuICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICBlbGVtZW50Lm9uKGV2ZW50TmFtZSwgZnVuY3Rpb24oZSkge1xuICAgICAgICAgICAgdmFsdWUuYmluZChjb250ZXh0KShlLCBlbGVtZW50KTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zb2xlLndhcm4oXCIoSlFIVE1MKSBUcmllZCB0byBhc3NpZ24gYSBub24gZnVuY3Rpb24gdG8gb24gZXZlbnQgaGFuZGxlciBcIiArIGtleSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoa2V5LnN0YXJ0c1dpdGgoXCJkYXRhLVwiKSkge1xuICAgICAgICBjb25zdCBhdHRyVmFsdWUgPSB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgPyB2YWx1ZS50cmltKCkgOiB2YWx1ZTtcbiAgICAgICAgZWxlbWVudC5hdHRyKGtleSwgYXR0clZhbHVlKTtcbiAgICAgICAgY29uc3QgZGF0YUtleSA9IGtleS5zdWJzdHJpbmcoNSk7XG4gICAgICAgIGVsZW1lbnQuZGF0YShkYXRhS2V5LCB2YWx1ZSk7XG4gICAgICAgIGNvbnRleHQuYXJnc1tkYXRhS2V5XSA9IHZhbHVlO1xuICAgICAgfSBlbHNlIGlmIChrZXkgPT09IFwiY2xhc3NcIikge1xuICAgICAgICBjb25zdCBleGlzdGluZ0NsYXNzZXMgPSBlbGVtZW50LmF0dHIoXCJjbGFzc1wiKTtcbiAgICAgICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy5lbmFibGVkKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coYFtJbnN0cnVjdGlvblByb2Nlc3Nvcl0gTWVyZ2luZyBjbGFzcyBhdHRyaWJ1dGU6YCwge1xuICAgICAgICAgICAgZXhpc3Rpbmc6IGV4aXN0aW5nQ2xhc3NlcyxcbiAgICAgICAgICAgIG5ldzogdmFsdWVcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWV4aXN0aW5nQ2xhc3Nlcykge1xuICAgICAgICAgIGNvbnN0IGF0dHJWYWx1ZSA9IHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiA/IHZhbHVlLnRyaW0oKSA6IHZhbHVlO1xuICAgICAgICAgIGVsZW1lbnQuYXR0cihcImNsYXNzXCIsIGF0dHJWYWx1ZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc3QgZXhpc3RpbmcgPSBleGlzdGluZ0NsYXNzZXMuc3BsaXQoL1xccysvKS5maWx0ZXIoKGMpID0+IGMpO1xuICAgICAgICAgIGNvbnN0IG5ld0NsYXNzZXMgPSBTdHJpbmcodmFsdWUpLnNwbGl0KC9cXHMrLykuZmlsdGVyKChjKSA9PiBjKTtcbiAgICAgICAgICBmb3IgKGNvbnN0IG5ld0NsYXNzIG9mIG5ld0NsYXNzZXMpIHtcbiAgICAgICAgICAgIGlmICghZXhpc3RpbmcuaW5jbHVkZXMobmV3Q2xhc3MpKSB7XG4gICAgICAgICAgICAgIGV4aXN0aW5nLnB1c2gobmV3Q2xhc3MpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBlbGVtZW50LmF0dHIoXCJjbGFzc1wiLCBleGlzdGluZy5qb2luKFwiIFwiKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy5lbmFibGVkKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coYFtJbnN0cnVjdGlvblByb2Nlc3Nvcl0gQ2xhc3MgYWZ0ZXIgbWVyZ2U6YCwgZWxlbWVudC5hdHRyKFwiY2xhc3NcIikpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGtleSA9PT0gXCJzdHlsZVwiKSB7XG4gICAgICAgIGNvbnN0IGV4aXN0aW5nU3R5bGUgPSBlbGVtZW50LmF0dHIoXCJzdHlsZVwiKTtcbiAgICAgICAgaWYgKCFleGlzdGluZ1N0eWxlKSB7XG4gICAgICAgICAgY29uc3QgYXR0clZhbHVlID0gdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiID8gdmFsdWUudHJpbSgpIDogdmFsdWU7XG4gICAgICAgICAgZWxlbWVudC5hdHRyKFwic3R5bGVcIiwgYXR0clZhbHVlKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCBzdHlsZU1hcCA9IHt9O1xuICAgICAgICAgIGV4aXN0aW5nU3R5bGUuc3BsaXQoXCI7XCIpLmZvckVhY2goKHJ1bGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IFtwcm9wLCB2YWxdID0gcnVsZS5zcGxpdChcIjpcIikubWFwKChzKSA9PiBzLnRyaW0oKSk7XG4gICAgICAgICAgICBpZiAocHJvcCAmJiB2YWwpIHtcbiAgICAgICAgICAgICAgc3R5bGVNYXBbcHJvcF0gPSB2YWw7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgU3RyaW5nKHZhbHVlKS5zcGxpdChcIjtcIikuZm9yRWFjaCgocnVsZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgW3Byb3AsIHZhbF0gPSBydWxlLnNwbGl0KFwiOlwiKS5tYXAoKHMpID0+IHMudHJpbSgpKTtcbiAgICAgICAgICAgIGlmIChwcm9wICYmIHZhbCkge1xuICAgICAgICAgICAgICBzdHlsZU1hcFtwcm9wXSA9IHZhbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgICBjb25zdCBtZXJnZWRTdHlsZSA9IE9iamVjdC5lbnRyaWVzKHN0eWxlTWFwKS5tYXAoKFtwcm9wLCB2YWxdKSA9PiBgJHtwcm9wfTogJHt2YWx9YCkuam9pbihcIjsgXCIpO1xuICAgICAgICAgIGVsZW1lbnQuYXR0cihcInN0eWxlXCIsIG1lcmdlZFN0eWxlKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiB8fCB0eXBlb2YgdmFsdWUgPT09IFwibnVtYmVyXCIgfHwgdHlwZW9mIHZhbHVlID09PSBcImJvb2xlYW5cIikge1xuICAgICAgICAgIGNvbnN0IGF0dHJWYWx1ZSA9IHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiA/IHZhbHVlLnRyaW0oKSA6IFN0cmluZyh2YWx1ZSk7XG4gICAgICAgICAgZWxlbWVudC5hdHRyKGtleSwgYXR0clZhbHVlKTtcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgdmFsdWUgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgICAgICBjb25zb2xlLndhcm4oYChKUUhUTUwpIFVuZXhwZWN0ZWQgdmFsdWUgZm9yICcke2tleX0nIG9uYCwgZWxlbWVudCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgYXN5bmMgZnVuY3Rpb24gaW5pdGlhbGl6ZV9jb21wb25lbnQoZWxlbWVudCwgY29tcERhdGEpIHtcbiAgICBjb25zdCB7IG5hbWUsIHByb3BzLCBjb250ZW50Rm4sIGNvbnRleHQgfSA9IGNvbXBEYXRhO1xuICAgIGNvbnN0IENvbXBvbmVudENsYXNzID0gZ2V0X2NvbXBvbmVudF9jbGFzcyhuYW1lKSB8fCBKcWh0bWxfQ29tcG9uZW50O1xuICAgIGNvbnN0IGludm9jYXRpb25BdHRycyA9IHt9O1xuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHByb3BzKSkge1xuICAgICAgaWYgKCFrZXkuc3RhcnRzV2l0aChcIl9cIikpIHtcbiAgICAgICAgaW52b2NhdGlvbkF0dHJzW2tleV0gPSB2YWx1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy5lbmFibGVkKSB7XG4gICAgICBjb25zb2xlLmxvZyhgW0luc3RydWN0aW9uUHJvY2Vzc29yXSBBcHBseWluZyBpbnZvY2F0aW9uIGF0dHJpYnV0ZXMgZm9yICR7bmFtZX06YCwgaW52b2NhdGlvbkF0dHJzKTtcbiAgICB9XG4gICAgYXBwbHlfYXR0cmlidXRlcyhlbGVtZW50LCBpbnZvY2F0aW9uQXR0cnMsIGNvbnRleHQpO1xuICAgIGNvbnN0IG9wdGlvbnMgPSB7fTtcbiAgICBpZiAoY29udGVudEZuKSB7XG4gICAgICBvcHRpb25zLl9pbm5lcmh0bWxfZnVuY3Rpb24gPSBjb250ZW50Rm47XG4gICAgfVxuICAgIGlmIChDb21wb25lbnRDbGFzcy5uYW1lICE9PSBuYW1lKSB7XG4gICAgICBvcHRpb25zLl9jb21wb25lbnRfbmFtZSA9IG5hbWU7XG4gICAgfVxuICAgIGNvbnN0IGluc3RhbmNlID0gbmV3IENvbXBvbmVudENsYXNzKGVsZW1lbnQsIG9wdGlvbnMpO1xuICAgIGluc3RhbmNlLl9pbnN0YW50aWF0b3IgPSBjb250ZXh0O1xuICAgIGF3YWl0IGluc3RhbmNlLmJvb3QoKTtcbiAgfVxuICBmdW5jdGlvbiBleHRyYWN0X3Nsb3RzKGluc3RydWN0aW9ucykge1xuICAgIGNvbnN0IHNsb3RzID0ge307XG4gICAgZm9yIChjb25zdCBpbnN0cnVjdGlvbiBvZiBpbnN0cnVjdGlvbnMpIHtcbiAgICAgIGlmICh0eXBlb2YgaW5zdHJ1Y3Rpb24gPT09IFwib2JqZWN0XCIgJiYgXCJzbG90XCIgaW4gaW5zdHJ1Y3Rpb24pIHtcbiAgICAgICAgY29uc3QgW25hbWVdID0gaW5zdHJ1Y3Rpb24uc2xvdDtcbiAgICAgICAgc2xvdHNbbmFtZV0gPSBpbnN0cnVjdGlvbjtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHNsb3RzO1xuICB9XG4gIHZhciBwZXJmb3JtYW5jZU1ldHJpY3MgPSAvKiBAX19QVVJFX18gKi8gbmV3IE1hcCgpO1xuICBmdW5jdGlvbiBkZXZXYXJuKG1lc3NhZ2UpIHtcbiAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB3aW5kb3cuSlFIVE1MX1NVUFBSRVNTX1dBUk5JTkdTKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICh0eXBlb2YgcHJvY2VzcyAhPT0gXCJ1bmRlZmluZWRcIiAmJiBwcm9jZXNzLmVudiAmJiBmYWxzZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zb2xlLndhcm4oYFtKUUhUTUwgRGV2IFdhcm5pbmddICR7bWVzc2FnZX1gKTtcbiAgfVxuICBmdW5jdGlvbiBnZXRKcWh0bWwkMSgpIHtcbiAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB3aW5kb3cuanFodG1sKSB7XG4gICAgICByZXR1cm4gd2luZG93LmpxaHRtbDtcbiAgICB9XG4gICAgaWYgKHR5cGVvZiBnbG9iYWxUaGlzICE9PSBcInVuZGVmaW5lZFwiICYmIGdsb2JhbFRoaXMuanFodG1sKSB7XG4gICAgICByZXR1cm4gZ2xvYmFsVGhpcy5qcWh0bWw7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihcIkZBVEFMOiB3aW5kb3cuanFodG1sIGlzIG5vdCBkZWZpbmVkLiBUaGUgSlFIVE1MIHJ1bnRpbWUgbXVzdCBiZSBsb2FkZWQgYmVmb3JlIHVzaW5nIGRlYnVnIGZlYXR1cmVzLiBJbXBvcnQgYW5kIGluaXRpYWxpemUgQGpxaHRtbC9jb3JlIGJlZm9yZSBhdHRlbXB0aW5nIHRvIHVzZSBkZWJ1ZyBmdW5jdGlvbmFsaXR5LlwiKTtcbiAgfVxuICBmdW5jdGlvbiBmbGFzaENvbXBvbmVudChjb21wb25lbnQsIGV2ZW50VHlwZSkge1xuICAgIGNvbnN0IGpxaHRtbDIgPSBnZXRKcWh0bWwkMSgpO1xuICAgIGlmICghanFodG1sMj8uZGVidWc/LmZsYXNoQ29tcG9uZW50cylcbiAgICAgIHJldHVybjtcbiAgICBjb25zdCBkdXJhdGlvbiA9IGpxaHRtbDIuZGVidWcuZmxhc2hEdXJhdGlvbiB8fCA1MDA7XG4gICAgY29uc3QgY29sb3JzID0ganFodG1sMi5kZWJ1Zy5mbGFzaENvbG9ycyB8fCB7fTtcbiAgICBjb25zdCBjb2xvciA9IGNvbG9yc1tldmVudFR5cGVdIHx8IChldmVudFR5cGUgPT09IFwiY3JlYXRlXCIgPyBcIiMzNDk4ZGJcIiA6IGV2ZW50VHlwZSA9PT0gXCJyZW5kZXJcIiA/IFwiIzI3YWU2MFwiIDogXCIjOWI1OWI2XCIpO1xuICAgIGNvbnN0IG9yaWdpbmFsQm9yZGVyID0gY29tcG9uZW50LiQuY3NzKFwiYm9yZGVyXCIpO1xuICAgIGNvbXBvbmVudC4kLmNzcyh7XG4gICAgICBcImJvcmRlclwiOiBgMnB4IHNvbGlkICR7Y29sb3J9YCxcbiAgICAgIFwidHJhbnNpdGlvblwiOiBgYm9yZGVyICR7ZHVyYXRpb259bXMgZWFzZS1vdXRgXG4gICAgfSk7XG4gICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBjb21wb25lbnQuJC5jc3MoXCJib3JkZXJcIiwgb3JpZ2luYWxCb3JkZXIgfHwgXCJcIik7XG4gICAgfSwgZHVyYXRpb24pO1xuICB9XG4gIGZ1bmN0aW9uIGxvZ0xpZmVjeWNsZShjb21wb25lbnQsIHBoYXNlLCBzdGF0dXMpIHtcbiAgICBjb25zdCBqcWh0bWwyID0gZ2V0SnFodG1sJDEoKTtcbiAgICBpZiAoIWpxaHRtbDI/LmRlYnVnKVxuICAgICAgcmV0dXJuO1xuICAgIGNvbnN0IHNob3VsZExvZyA9IGpxaHRtbDIuZGVidWcubG9nRnVsbExpZmVjeWNsZSB8fCBqcWh0bWwyLmRlYnVnLmxvZ0NyZWF0aW9uUmVhZHkgJiYgKHBoYXNlID09PSBcImNyZWF0ZVwiIHx8IHBoYXNlID09PSBcInJlYWR5XCIpO1xuICAgIGlmICghc2hvdWxkTG9nKVxuICAgICAgcmV0dXJuO1xuICAgIGNvbnN0IGNvbXBvbmVudE5hbWUgPSBjb21wb25lbnQuY29uc3RydWN0b3IubmFtZTtcbiAgICBjb25zdCB0aW1lc3RhbXAgPSAoLyogQF9fUFVSRV9fICovIG5ldyBEYXRlKCkpLnRvSVNPU3RyaW5nKCk7XG4gICAgY29uc3QgcHJlZml4ID0gYFtKUUhUTUwgJHt0aW1lc3RhbXB9XWA7XG4gICAgaWYgKHN0YXR1cyA9PT0gXCJzdGFydFwiKSB7XG4gICAgICBjb25zb2xlLmxvZyhgJHtwcmVmaXh9ICR7Y29tcG9uZW50TmFtZX0jJHtjb21wb25lbnQuX2NpZH0gXFx1MjE5MiAke3BoYXNlfSBzdGFydGluZy4uLmApO1xuICAgICAgaWYgKGpxaHRtbDIuZGVidWcucHJvZmlsZVBlcmZvcm1hbmNlKSB7XG4gICAgICAgIHBlcmZvcm1hbmNlTWV0cmljcy5zZXQoYCR7Y29tcG9uZW50Ll9jaWR9XyR7cGhhc2V9YCwgRGF0ZS5ub3coKSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGxldCBtZXNzYWdlID0gYCR7cHJlZml4fSAke2NvbXBvbmVudE5hbWV9IyR7Y29tcG9uZW50Ll9jaWR9IFxcdTI3MTMgJHtwaGFzZX0gY29tcGxldGVgO1xuICAgICAgaWYgKGpxaHRtbDIuZGVidWcucHJvZmlsZVBlcmZvcm1hbmNlKSB7XG4gICAgICAgIGNvbnN0IHN0YXJ0VGltZSA9IHBlcmZvcm1hbmNlTWV0cmljcy5nZXQoYCR7Y29tcG9uZW50Ll9jaWR9XyR7cGhhc2V9YCk7XG4gICAgICAgIGlmIChzdGFydFRpbWUpIHtcbiAgICAgICAgICBjb25zdCBkdXJhdGlvbiA9IERhdGUubm93KCkgLSBzdGFydFRpbWU7XG4gICAgICAgICAgbWVzc2FnZSArPSBgICgke2R1cmF0aW9ufW1zKWA7XG4gICAgICAgICAgaWYgKHBoYXNlID09PSBcInJlbmRlclwiICYmIGpxaHRtbDIuZGVidWcuaGlnaGxpZ2h0U2xvd1JlbmRlcnMgJiYgZHVyYXRpb24gPiBqcWh0bWwyLmRlYnVnLmhpZ2hsaWdodFNsb3dSZW5kZXJzKSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oYCR7cHJlZml4fSBTTE9XIFJFTkRFUjogJHtjb21wb25lbnROYW1lfSMke2NvbXBvbmVudC5fY2lkfSB0b29rICR7ZHVyYXRpb259bXNgKTtcbiAgICAgICAgICAgIGNvbXBvbmVudC4kLmNzcyhcIm91dGxpbmVcIiwgXCIycHggZGFzaGVkIHJlZFwiKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGNvbnNvbGUubG9nKG1lc3NhZ2UpO1xuICAgICAgaWYgKGpxaHRtbDIuZGVidWcuZmxhc2hDb21wb25lbnRzICYmIChwaGFzZSA9PT0gXCJjcmVhdGVcIiB8fCBwaGFzZSA9PT0gXCJyZW5kZXJcIiB8fCBwaGFzZSA9PT0gXCJyZWFkeVwiKSkge1xuICAgICAgICBmbGFzaENvbXBvbmVudChjb21wb25lbnQsIHBoYXNlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGpxaHRtbDIuZGVidWcuc2hvd0NvbXBvbmVudFRyZWUpIHtcbiAgICAgIHVwZGF0ZUNvbXBvbmVudFRyZWUoKTtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gYXBwbHlEZWJ1Z0RlbGF5KHBoYXNlKSB7XG4gICAgY29uc3QganFodG1sMiA9IGdldEpxaHRtbCQxKCk7XG4gICAgaWYgKCFqcWh0bWwyPy5kZWJ1ZylcbiAgICAgIHJldHVybjtcbiAgICBsZXQgZGVsYXlNcyA9IDA7XG4gICAgc3dpdGNoIChwaGFzZSkge1xuICAgICAgY2FzZSBcImNvbXBvbmVudFwiOlxuICAgICAgICBkZWxheU1zID0ganFodG1sMi5kZWJ1Zy5kZWxheUFmdGVyQ29tcG9uZW50IHx8IDA7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBcInJlbmRlclwiOlxuICAgICAgICBkZWxheU1zID0ganFodG1sMi5kZWJ1Zy5kZWxheUFmdGVyUmVuZGVyIHx8IDA7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBcInJlcmVuZGVyXCI6XG4gICAgICAgIGRlbGF5TXMgPSBqcWh0bWwyLmRlYnVnLmRlbGF5QWZ0ZXJSZXJlbmRlciB8fCAwO1xuICAgICAgICBicmVhaztcbiAgICB9XG4gICAgaWYgKGRlbGF5TXMgPiAwKSB7XG4gICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTCBEZWJ1Z10gQXBwbHlpbmcgJHtkZWxheU1zfW1zIGRlbGF5IGFmdGVyICR7cGhhc2V9YCk7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIHVwZGF0ZUNvbXBvbmVudFRyZWUoKSB7XG4gICAgY29uc29sZS5sb2coXCJbSlFIVE1MIFRyZWVdIENvbXBvbmVudCBoaWVyYXJjaHkgdXBkYXRlZFwiKTtcbiAgfVxuICB2YXIgSnFodG1sX0NvbXBvbmVudCA9IGNsYXNzIF9KcWh0bWxfQ29tcG9uZW50IHtcbiAgICBjb25zdHJ1Y3RvcihlbGVtZW50LCBhcmdzID0ge30pIHtcbiAgICAgIHRoaXMuZGF0YSA9IHt9O1xuICAgICAgdGhpcy5fcmVhZHlfc3RhdGUgPSAwO1xuICAgICAgdGhpcy5faW5zdGFudGlhdG9yID0gbnVsbDtcbiAgICAgIHRoaXMuX2RvbV9wYXJlbnQgPSBudWxsO1xuICAgICAgdGhpcy5fZG9tX2NoaWxkcmVuID0gLyogQF9fUFVSRV9fICovIG5ldyBTZXQoKTtcbiAgICAgIHRoaXMuX3VzZV9kb21fZmFsbGJhY2sgPSBmYWxzZTtcbiAgICAgIHRoaXMuX3N0b3BwZWQgPSBmYWxzZTtcbiAgICAgIHRoaXMuX2Jvb3RlZCA9IGZhbHNlO1xuICAgICAgdGhpcy5fZGF0YV9iZWZvcmVfcmVuZGVyID0gbnVsbDtcbiAgICAgIHRoaXMuX2xpZmVjeWNsZV9jYWxsYmFja3MgPSAvKiBAX19QVVJFX18gKi8gbmV3IE1hcCgpO1xuICAgICAgdGhpcy5fbGlmZWN5Y2xlX3N0YXRlcyA9IC8qIEBfX1BVUkVfXyAqLyBuZXcgU2V0KCk7XG4gICAgICB0aGlzLl9fbG9hZGluZyA9IGZhbHNlO1xuICAgICAgdGhpcy5fZGlkX2ZpcnN0X3JlbmRlciA9IGZhbHNlO1xuICAgICAgdGhpcy5fcmVuZGVyX2NvdW50ID0gMDtcbiAgICAgIHRoaXMuX2NpZCA9IHRoaXMuX2dlbmVyYXRlX2NpZCgpO1xuICAgICAgdGhpcy5fbGlmZWN5Y2xlX21hbmFnZXIgPSBMaWZlY3ljbGVNYW5hZ2VyLmdldF9pbnN0YW5jZSgpO1xuICAgICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgdGhpcy4kID0gJChlbGVtZW50KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IGRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIik7XG4gICAgICAgIHRoaXMuJCA9ICQoZGl2KTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGRhdGFBdHRycyA9IHt9O1xuICAgICAgaWYgKHRoaXMuJC5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IGRhdGFzZXQgPSB0aGlzLiRbMF0uZGF0YXNldCB8fCB7fTtcbiAgICAgICAgZm9yIChjb25zdCBrZXkgaW4gZGF0YXNldCkge1xuICAgICAgICAgIGlmIChrZXkgIT09IFwiY2lkXCIgJiYga2V5ICE9PSBcInRpZFwiICYmIGtleSAhPT0gXCJjb21wb25lbnROYW1lXCIgJiYga2V5ICE9PSBcInJlYWR5U3RhdGVcIikge1xuICAgICAgICAgICAgY29uc3QgZGF0YVZhbHVlID0gdGhpcy4kLmRhdGEoa2V5KTtcbiAgICAgICAgICAgIGlmIChkYXRhVmFsdWUgIT09IHZvaWQgMCAmJiBkYXRhVmFsdWUgIT09IGRhdGFzZXRba2V5XSkge1xuICAgICAgICAgICAgICBkYXRhQXR0cnNba2V5XSA9IGRhdGFWYWx1ZTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGRhdGFBdHRyc1trZXldID0gZGF0YXNldFtrZXldO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgbGV0IHRlbXBsYXRlX2Zvcl9hcmdzO1xuICAgICAgaWYgKGFyZ3MuX2NvbXBvbmVudF9uYW1lKSB7XG4gICAgICAgIHRlbXBsYXRlX2Zvcl9hcmdzID0gZ2V0X3RlbXBsYXRlKGFyZ3MuX2NvbXBvbmVudF9uYW1lKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRlbXBsYXRlX2Zvcl9hcmdzID0gZ2V0X3RlbXBsYXRlX2J5X2NsYXNzKHRoaXMuY29uc3RydWN0b3IpO1xuICAgICAgfVxuICAgICAgY29uc3QgZGVmaW5lQXJncyA9IHRlbXBsYXRlX2Zvcl9hcmdzPy5kZWZpbmVBcmdzIHx8IHt9O1xuICAgICAgdGhpcy5hcmdzID0geyAuLi5kZWZpbmVBcmdzLCAuLi5kYXRhQXR0cnMsIC4uLmFyZ3MgfTtcbiAgICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHRoaXMuYXJncykpIHtcbiAgICAgICAgaWYgKGtleSA9PT0gXCJjaWRcIiB8fCBrZXkgPT09IFwidGlkXCIgfHwga2V5ID09PSBcImNvbXBvbmVudE5hbWVcIiB8fCBrZXkgPT09IFwicmVhZHlTdGF0ZVwiIHx8IGtleS5zdGFydHNXaXRoKFwiX1wiKSkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgfHwgdHlwZW9mIHZhbHVlID09PSBcIm51bWJlclwiKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IGN1cnJlbnRBdHRyID0gdGhpcy4kLmF0dHIoYGRhdGEtJHtrZXl9YCk7XG4gICAgICAgICAgICBpZiAoY3VycmVudEF0dHIgIT0gdmFsdWUpIHtcbiAgICAgICAgICAgICAgdGhpcy4kLmF0dHIoYGRhdGEtJHtrZXl9YCwgU3RyaW5nKHZhbHVlKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdGhpcy4kLmRhdGEoXCJfY29tcG9uZW50XCIsIHRoaXMpO1xuICAgICAgdGhpcy5fYXBwbHlfY3NzX2NsYXNzZXMoKTtcbiAgICAgIHRoaXMuX2FwcGx5X2RlZmF1bHRfYXR0cmlidXRlcygpO1xuICAgICAgdGhpcy5fc2V0X2F0dHJpYnV0ZXMoKTtcbiAgICAgIHRoaXMuX2ZpbmRfZG9tX3BhcmVudCgpO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcImNvbnN0cnVjdFwiLCBcImNvbXBsZXRlXCIpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBCb290IC0gU3RhcnQgdGhlIGZ1bGwgY29tcG9uZW50IGxpZmVjeWNsZVxuICAgICAqIENhbGxlZCBpbW1lZGlhdGVseSBhZnRlciBjb25zdHJ1Y3Rpb24gYnkgaW5zdHJ1Y3Rpb24gcHJvY2Vzc29yXG4gICAgICovXG4gICAgYXN5bmMgYm9vdCgpIHtcbiAgICAgIGlmICh0aGlzLl9ib290ZWQpXG4gICAgICAgIHJldHVybjtcbiAgICAgIHRoaXMuX2Jvb3RlZCA9IHRydWU7XG4gICAgICBhd2FpdCB0aGlzLl9saWZlY3ljbGVfbWFuYWdlci5ib290X2NvbXBvbmVudCh0aGlzKTtcbiAgICB9XG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIExpZmVjeWNsZSBNZXRob2RzIChjYWxsZWQgYnkgTGlmZWN5Y2xlTWFuYWdlcilcbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLyoqXG4gICAgICogSW50ZXJuYWwgcmVuZGVyIHBoYXNlIC0gQ3JlYXRlIERPTSBzdHJ1Y3R1cmVcbiAgICAgKiBDYWxsZWQgdG9wLWRvd24gKHBhcmVudCBiZWZvcmUgY2hpbGRyZW4pIHdoZW4gcGFydCBvZiBsaWZlY3ljbGVcbiAgICAgKiBUaGlzIGlzIGFuIGludGVybmFsIG1ldGhvZCAtIHVzZXJzIHNob3VsZCBjYWxsIHJlbmRlcigpIGluc3RlYWRcbiAgICAgKlxuICAgICAqIEBwYXJhbSBpZCBPcHRpb25hbCBzY29wZWQgSUQgLSBpZiBwcm92aWRlZCwgZGVsZWdhdGVzIHRvIGNoaWxkIGNvbXBvbmVudCdzIF9yZW5kZXIoKVxuICAgICAqIEByZXR1cm5zIFRoZSBjdXJyZW50IF9yZW5kZXJfY291bnQgYWZ0ZXIgaW5jcmVtZW50aW5nICh1c2VkIHRvIGRldGVjdCBzdGFsZSByZW5kZXJzKVxuICAgICAqIEBwcml2YXRlXG4gICAgICovXG4gICAgX3JlbmRlcihpZCA9IG51bGwpIHtcbiAgICAgIHRoaXMuX3JlbmRlcl9jb3VudCsrO1xuICAgICAgY29uc3QgY3VycmVudF9yZW5kZXJfaWQgPSB0aGlzLl9yZW5kZXJfY291bnQ7XG4gICAgICBpZiAodGhpcy5fc3RvcHBlZClcbiAgICAgICAgcmV0dXJuIGN1cnJlbnRfcmVuZGVyX2lkO1xuICAgICAgaWYgKGlkKSB7XG4gICAgICAgIGNvbnN0ICRlbGVtZW50ID0gdGhpcy4kaWQoaWQpO1xuICAgICAgICBpZiAoJGVsZW1lbnQubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBbSlFIVE1MXSByZW5kZXIoXCIke2lkfVwiKSAtIG5vIHN1Y2ggaWQuXG5Db21wb25lbnQgXCIke3RoaXMuY29tcG9uZW50X25hbWUoKX1cIiBoYXMgbm8gY2hpbGQgZWxlbWVudCB3aXRoICRpZD1cIiR7aWR9XCIuYCk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgY2hpbGQgPSAkZWxlbWVudC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgaWYgKCFjaGlsZCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgW0pRSFRNTF0gcmVuZGVyKFwiJHtpZH1cIikgLSBlbGVtZW50IGlzIG5vdCBhIGNvbXBvbmVudCBvciBkb2VzIG5vdCBoYXZlICRyZWRyYXdhYmxlIGF0dHJpYnV0ZSBzZXQuXG5FbGVtZW50IHdpdGggJGlkPVwiJHtpZH1cIiBleGlzdHMgYnV0IGlzIG5vdCBpbml0aWFsaXplZCBhcyBhIGNvbXBvbmVudC5cbkFkZCAkcmVkcmF3YWJsZSBhdHRyaWJ1dGUgb3IgbWFrZSBpdCBhIHByb3BlciBjb21wb25lbnQuYCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNoaWxkLl9yZW5kZXIoKTtcbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLl9fbG9hZGluZykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFtKUUhUTUxdIENvbXBvbmVudCBcIiR7dGhpcy5jb21wb25lbnRfbmFtZSgpfVwiIGF0dGVtcHRlZCB0byBjYWxsIHJlbmRlcigpIGR1cmluZyBvbl9sb2FkKCkuXG5vbl9sb2FkKCkgc2hvdWxkIE9OTFkgbW9kaWZ5IHRoaXMuZGF0YS4gRE9NIHVwZGF0ZXMgaGFwcGVuIGF1dG9tYXRpY2FsbHkgYWZ0ZXIgb25fbG9hZCgpIGNvbXBsZXRlcy5cblxuRml4OiBSZW1vdmUgdGhlIHRoaXMucmVuZGVyKCkgY2FsbCBmcm9tIG9uX2xvYWQoKS5cblRoZSBmcmFtZXdvcmsgd2lsbCBhdXRvbWF0aWNhbGx5IHJlLXJlbmRlciBpZiB0aGlzLmRhdGEgY2hhbmdlcyBkdXJpbmcgb25fbG9hZCgpLmApO1xuICAgICAgfVxuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcInJlbmRlclwiLCBcInN0YXJ0XCIpO1xuICAgICAgaWYgKCEkLmNvbnRhaW5zKGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCwgdGhpcy4kWzBdKSkge1xuICAgICAgICB0aGlzLl91c2VfZG9tX2ZhbGxiYWNrID0gdHJ1ZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuX3VzZV9kb21fZmFsbGJhY2sgPSBmYWxzZTtcbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLl9kaWRfZmlyc3RfcmVuZGVyKSB7XG4gICAgICAgIHRoaXMuJC5maW5kKFwiLkpxaHRtbF9Db21wb25lbnRcIikuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgICBjb25zdCBjaGlsZCA9ICQodGhpcykuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgICAgaWYgKGNoaWxkICYmICFjaGlsZC5fc3RvcHBlZCkge1xuICAgICAgICAgICAgY2hpbGQuX3N0b3AoKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLiRbMF0uaW5uZXJIVE1MID0gXCJcIjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuX2RpZF9maXJzdF9yZW5kZXIgPSB0cnVlO1xuICAgICAgfVxuICAgICAgdGhpcy4kLnJlbW92ZUNsYXNzKFwiX0NvbXBvbmVudF9TdG9wcGVkXCIpO1xuICAgICAgaWYgKHRoaXMuX2RhdGFfYmVmb3JlX3JlbmRlciA9PT0gbnVsbCkge1xuICAgICAgICB0aGlzLl9kYXRhX2JlZm9yZV9yZW5kZXIgPSBKU09OLnN0cmluZ2lmeSh0aGlzLmRhdGEpO1xuICAgICAgfVxuICAgICAgdGhpcy5fZG9tX2NoaWxkcmVuLmNsZWFyKCk7XG4gICAgICBsZXQgdGVtcGxhdGVfZGVmO1xuICAgICAgaWYgKHRoaXMuYXJncy5fY29tcG9uZW50X25hbWUpIHtcbiAgICAgICAgdGVtcGxhdGVfZGVmID0gZ2V0X3RlbXBsYXRlKHRoaXMuYXJncy5fY29tcG9uZW50X25hbWUpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGVtcGxhdGVfZGVmID0gZ2V0X3RlbXBsYXRlX2J5X2NsYXNzKHRoaXMuY29uc3RydWN0b3IpO1xuICAgICAgfVxuICAgICAgaWYgKHRlbXBsYXRlX2RlZiAmJiB0ZW1wbGF0ZV9kZWYucmVuZGVyKSB7XG4gICAgICAgIGNvbnN0IGpxaHRtbDIgPSB7XG4gICAgICAgICAgZXNjYXBlX2h0bWw6IChzdHIpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIik7XG4gICAgICAgICAgICBkaXYudGV4dENvbnRlbnQgPSBTdHJpbmcoc3RyKTtcbiAgICAgICAgICAgIHJldHVybiBkaXYuaW5uZXJIVE1MO1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgY29uc3QgZGVmYXVsdENvbnRlbnQgPSAoKSA9PiBcIlwiO1xuICAgICAgICBsZXQgW2luc3RydWN0aW9ucywgY29udGV4dF0gPSB0ZW1wbGF0ZV9kZWYucmVuZGVyLmJpbmQodGhpcykoXG4gICAgICAgICAgdGhpcy5kYXRhLFxuICAgICAgICAgIHRoaXMuYXJncyxcbiAgICAgICAgICB0aGlzLmFyZ3MuX2lubmVyaHRtbF9mdW5jdGlvbiB8fCBkZWZhdWx0Q29udGVudCxcbiAgICAgICAgICAvLyBDb250ZW50IGZ1bmN0aW9uIHdpdGggZmFsbGJhY2tcbiAgICAgICAgICBqcWh0bWwyXG4gICAgICAgICAgLy8gVXRpbGl0aWVzIG9iamVjdFxuICAgICAgICApO1xuICAgICAgICBpZiAoaW5zdHJ1Y3Rpb25zICYmIHR5cGVvZiBpbnN0cnVjdGlvbnMgPT09IFwib2JqZWN0XCIgJiYgaW5zdHJ1Y3Rpb25zLl9zbG90cyAmJiAhQXJyYXkuaXNBcnJheShpbnN0cnVjdGlvbnMpKSB7XG4gICAgICAgICAgY29uc3QgY29tcG9uZW50TmFtZSA9IHRlbXBsYXRlX2RlZi5uYW1lIHx8IHRoaXMuYXJncy5fY29tcG9uZW50X25hbWUgfHwgdGhpcy5jb25zdHJ1Y3Rvci5uYW1lO1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSBTbG90LW9ubHkgdGVtcGxhdGUgZGV0ZWN0ZWQgZm9yICR7Y29tcG9uZW50TmFtZX1gKTtcbiAgICAgICAgICBsZXQgcGFyZW50VGVtcGxhdGUgPSBudWxsO1xuICAgICAgICAgIGxldCBwYXJlbnRUZW1wbGF0ZU5hbWUgPSBudWxsO1xuICAgICAgICAgIGlmICh0ZW1wbGF0ZV9kZWYuZXh0ZW5kcykge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdICAgVXNpbmcgZXhwbGljaXQgZXh0ZW5kczogJHt0ZW1wbGF0ZV9kZWYuZXh0ZW5kc31gKTtcbiAgICAgICAgICAgIHBhcmVudFRlbXBsYXRlID0gZ2V0X3RlbXBsYXRlKHRlbXBsYXRlX2RlZi5leHRlbmRzKTtcbiAgICAgICAgICAgIHBhcmVudFRlbXBsYXRlTmFtZSA9IHRlbXBsYXRlX2RlZi5leHRlbmRzO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoIXBhcmVudFRlbXBsYXRlKSB7XG4gICAgICAgICAgICBsZXQgY3VycmVudENsYXNzID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKHRoaXMuY29uc3RydWN0b3IpO1xuICAgICAgICAgICAgd2hpbGUgKGN1cnJlbnRDbGFzcyAmJiBjdXJyZW50Q2xhc3MubmFtZSAhPT0gXCJPYmplY3RcIiAmJiBjdXJyZW50Q2xhc3MubmFtZSAhPT0gXCJKcWh0bWxfQ29tcG9uZW50XCIpIHtcbiAgICAgICAgICAgICAgY29uc3QgY2xhc3NOYW1lID0gY3VycmVudENsYXNzLm5hbWU7XG4gICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSAgIENoZWNraW5nIHBhcmVudDogJHtjbGFzc05hbWV9YCk7XG4gICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3QgY2xhc3NUZW1wbGF0ZSA9IGdldF90ZW1wbGF0ZShjbGFzc05hbWUpO1xuICAgICAgICAgICAgICAgIGlmIChjbGFzc1RlbXBsYXRlICYmIGNsYXNzVGVtcGxhdGUubmFtZSAhPT0gXCJKcWh0bWxfQ29tcG9uZW50XCIpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSAgIEZvdW5kIHBhcmVudCB0ZW1wbGF0ZTogJHtjbGFzc05hbWV9YCk7XG4gICAgICAgICAgICAgICAgICBwYXJlbnRUZW1wbGF0ZSA9IGNsYXNzVGVtcGxhdGU7XG4gICAgICAgICAgICAgICAgICBwYXJlbnRUZW1wbGF0ZU5hbWUgPSBjbGFzc05hbWU7XG4gICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBFcnJvciBmaW5kaW5nIHBhcmVudCB0ZW1wbGF0ZSAke2NsYXNzTmFtZX06YCwgZXJyb3IpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGN1cnJlbnRDbGFzcyA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihjdXJyZW50Q2xhc3MpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAocGFyZW50VGVtcGxhdGUpIHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIGNvbnN0IGNoaWxkU2xvdHMgPSBpbnN0cnVjdGlvbnMuX3Nsb3RzO1xuICAgICAgICAgICAgICBjb25zdCBjb250ZW50RnVuY3Rpb24gPSAoc2xvdE5hbWUsIGRhdGEpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoY2hpbGRTbG90c1tzbG90TmFtZV0gJiYgdHlwZW9mIGNoaWxkU2xvdHNbc2xvdE5hbWVdID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IFtzbG90SW5zdHJ1Y3Rpb25zLCBzbG90Q29udGV4dF0gPSBjaGlsZFNsb3RzW3Nsb3ROYW1lXShkYXRhKTtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBbc2xvdEluc3RydWN0aW9ucywgc2xvdENvbnRleHRdO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4gXCJcIjtcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgY29uc3QgW3BhcmVudEluc3RydWN0aW9ucywgcGFyZW50Q29udGV4dF0gPSBwYXJlbnRUZW1wbGF0ZS5yZW5kZXIuYmluZCh0aGlzKShcbiAgICAgICAgICAgICAgICB0aGlzLmRhdGEsXG4gICAgICAgICAgICAgICAgdGhpcy5hcmdzLFxuICAgICAgICAgICAgICAgIGNvbnRlbnRGdW5jdGlvbixcbiAgICAgICAgICAgICAgICAvLyBQYXNzIGNvbnRlbnQgZnVuY3Rpb24gdGhhdCBpbnZva2VzIGNoaWxkIHNsb3RzXG4gICAgICAgICAgICAgICAganFodG1sMlxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gICBQYXJlbnQgdGVtcGxhdGUgaW52b2tlZCBzdWNjZXNzZnVsbHlgKTtcbiAgICAgICAgICAgICAgaW5zdHJ1Y3Rpb25zID0gcGFyZW50SW5zdHJ1Y3Rpb25zO1xuICAgICAgICAgICAgICBjb250ZXh0ID0gcGFyZW50Q29udGV4dDtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gRXJyb3IgaW52b2tpbmcgcGFyZW50IHRlbXBsYXRlICR7cGFyZW50VGVtcGxhdGVOYW1lfTpgLCBlcnJvcik7XG4gICAgICAgICAgICAgIGluc3RydWN0aW9ucyA9IFtdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIE5vIHBhcmVudCB0ZW1wbGF0ZSBmb3VuZCBmb3IgJHt0aGlzLmNvbnN0cnVjdG9yLm5hbWV9LCByZW5kZXJpbmcgZW1wdHlgKTtcbiAgICAgICAgICAgIGluc3RydWN0aW9ucyA9IFtdO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjb25zdCBmbGF0dGVuZWRJbnN0cnVjdGlvbnMgPSB0aGlzLl9mbGF0dGVuX2luc3RydWN0aW9ucyhpbnN0cnVjdGlvbnMpO1xuICAgICAgICBwcm9jZXNzX2luc3RydWN0aW9ucyhmbGF0dGVuZWRJbnN0cnVjdGlvbnMsIHRoaXMuJCwgdGhpcyk7XG4gICAgICB9XG4gICAgICB0aGlzLl91cGRhdGVfZGVidWdfYXR0cnMoKTtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJyZW5kZXJcIiwgXCJjb21wbGV0ZVwiKTtcbiAgICAgIGNvbnN0IHJlbmRlclJlc3VsdCA9IHRoaXMub25fcmVuZGVyKCk7XG4gICAgICBpZiAocmVuZGVyUmVzdWx0ICYmIHR5cGVvZiByZW5kZXJSZXN1bHQudGhlbiA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gQ29tcG9uZW50IFwiJHt0aGlzLmNvbXBvbmVudF9uYW1lKCl9XCIgcmV0dXJuZWQgYSBQcm9taXNlIGZyb20gb25fcmVuZGVyKCkuIG9uX3JlbmRlcigpIG11c3QgYmUgc3luY2hyb25vdXMgY29kZS4gUmVtb3ZlICdhc3luYycgZnJvbSB0aGUgZnVuY3Rpb24gZGVjbGFyYXRpb24uYCk7XG4gICAgICB9XG4gICAgICB0aGlzLnRyaWdnZXIoXCJyZW5kZXJcIik7XG4gICAgICBjb25zdCBpc1JlcmVuZGVyID0gdGhpcy5fcmVhZHlfc3RhdGUgPj0gMztcbiAgICAgIGFwcGx5RGVidWdEZWxheShpc1JlcmVuZGVyID8gXCJyZXJlbmRlclwiIDogXCJyZW5kZXJcIik7XG4gICAgICByZXR1cm4gY3VycmVudF9yZW5kZXJfaWQ7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFB1YmxpYyByZW5kZXIgbWV0aG9kIC0gcmUtcmVuZGVycyBjb21wb25lbnQgYW5kIGNvbXBsZXRlcyBsaWZlY3ljbGVcbiAgICAgKiBUaGlzIGlzIHdoYXQgdXNlcnMgc2hvdWxkIGNhbGwgd2hlbiB0aGV5IHdhbnQgdG8gdXBkYXRlIGEgY29tcG9uZW50LlxuICAgICAqXG4gICAgICogTGlmZWN5Y2xlIHNlcXVlbmNlOlxuICAgICAqIDEuIF9yZW5kZXIoKSAtIFVwZGF0ZXMgRE9NIHN5bmNocm9ub3VzbHksIGNhbGxzIG9uX3JlbmRlcigpLCBmaXJlcyAncmVuZGVyJyBldmVudFxuICAgICAqIDIuIEFzeW5jIGNvbnRpbnVhdGlvbiAoZmlyZSBhbmQgZm9yZ2V0KTpcbiAgICAgKiAgICAtIF93YWl0X2Zvcl9jaGlsZHJlbl9yZWFkeSgpIC0gV2FpdHMgZm9yIGFsbCBjaGlsZHJlbiB0byByZWFjaCByZWFkeSBzdGF0ZVxuICAgICAqICAgIC0gb25fcmVhZHkoKSAtIENhbGxzIHVzZXIncyByZWFkeSBob29rXG4gICAgICogICAgLSB0cmlnZ2VyKCdyZWFkeScpIC0gRmlyZXMgcmVhZHkgZXZlbnRcbiAgICAgKlxuICAgICAqIFJldHVybnMgaW1tZWRpYXRlbHkgYWZ0ZXIgX3JlbmRlcigpIGNvbXBsZXRlcyAtIGRvZXMgTk9UIHdhaXQgZm9yIGNoaWxkcmVuXG4gICAgICovXG4gICAgcmVuZGVyKGlkID0gbnVsbCkge1xuICAgICAgaWYgKHRoaXMuX3N0b3BwZWQpXG4gICAgICAgIHJldHVybjtcbiAgICAgIGlmIChpZCkge1xuICAgICAgICBjb25zdCAkZWxlbWVudCA9IHRoaXMuJGlkKGlkKTtcbiAgICAgICAgaWYgKCRlbGVtZW50Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgW0pRSFRNTF0gcmVuZGVyKFwiJHtpZH1cIikgLSBubyBzdWNoIGlkLlxuQ29tcG9uZW50IFwiJHt0aGlzLmNvbXBvbmVudF9uYW1lKCl9XCIgaGFzIG5vIGNoaWxkIGVsZW1lbnQgd2l0aCAkaWQ9XCIke2lkfVwiLmApO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGNoaWxkID0gJGVsZW1lbnQuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgIGlmICghY2hpbGQpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFtKUUhUTUxdIHJlbmRlcihcIiR7aWR9XCIpIC0gZWxlbWVudCBpcyBub3QgYSBjb21wb25lbnQgb3IgZG9lcyBub3QgaGF2ZSAkcmVkcmF3YWJsZSBhdHRyaWJ1dGUgc2V0LlxuRWxlbWVudCB3aXRoICRpZD1cIiR7aWR9XCIgZXhpc3RzIGJ1dCBpcyBub3QgaW5pdGlhbGl6ZWQgYXMgYSBjb21wb25lbnQuXG5BZGQgJHJlZHJhd2FibGUgYXR0cmlidXRlIG9yIG1ha2UgaXQgYSBwcm9wZXIgY29tcG9uZW50LmApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBjaGlsZC5yZW5kZXIoKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHJlbmRlcl9pZCA9IHRoaXMuX3JlbmRlcigpO1xuICAgICAgKGFzeW5jICgpID0+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5fd2FpdF9mb3JfY2hpbGRyZW5fcmVhZHkoKTtcbiAgICAgICAgaWYgKHRoaXMuX3JlbmRlcl9jb3VudCAhPT0gcmVuZGVyX2lkKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHRoaXMub25fcmVhZHkoKTtcbiAgICAgICAgYXdhaXQgdGhpcy50cmlnZ2VyKFwicmVhZHlcIik7XG4gICAgICB9KSgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBBbGlhcyBmb3IgcmVuZGVyKCkgLSByZS1yZW5kZXJzIGNvbXBvbmVudCB3aXRoIGN1cnJlbnQgZGF0YVxuICAgICAqIFByb3ZpZGVkIGZvciBBUEkgY29uc2lzdGVuY3kgYW5kIGNsYXJpdHlcbiAgICAgKi9cbiAgICByZWRyYXcoaWQgPSBudWxsKSB7XG4gICAgICByZXR1cm4gdGhpcy5yZW5kZXIoaWQpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBDcmVhdGUgcGhhc2UgLSBRdWljayBzZXR1cCwgcHJlcGFyZSBVSVxuICAgICAqIENhbGxlZCBib3R0b20tdXAgKGNoaWxkcmVuIGJlZm9yZSBwYXJlbnQpXG4gICAgICovXG4gICAgYXN5bmMgY3JlYXRlKCkge1xuICAgICAgaWYgKHRoaXMuX3N0b3BwZWQgfHwgdGhpcy5fcmVhZHlfc3RhdGUgPj0gMSlcbiAgICAgICAgcmV0dXJuO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcImNyZWF0ZVwiLCBcInN0YXJ0XCIpO1xuICAgICAgY29uc3QgcmVzdWx0ID0gdGhpcy5vbl9jcmVhdGUoKTtcbiAgICAgIGlmIChyZXN1bHQgJiYgdHlwZW9mIHJlc3VsdC50aGVuID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBDb21wb25lbnQgXCIke3RoaXMuY29tcG9uZW50X25hbWUoKX1cIiByZXR1cm5lZCBhIFByb21pc2UgZnJvbSBvbl9jcmVhdGUoKS4gb25fY3JlYXRlKCkgbXVzdCBiZSBzeW5jaHJvbm91cyBjb2RlLiBSZW1vdmUgJ2FzeW5jJyBmcm9tIHRoZSBmdW5jdGlvbiBkZWNsYXJhdGlvbi5gKTtcbiAgICAgICAgYXdhaXQgcmVzdWx0O1xuICAgICAgfVxuICAgICAgdGhpcy5fcmVhZHlfc3RhdGUgPSAxO1xuICAgICAgdGhpcy5fdXBkYXRlX2RlYnVnX2F0dHJzKCk7XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwiY3JlYXRlXCIsIFwiY29tcGxldGVcIik7XG4gICAgICB0aGlzLnRyaWdnZXIoXCJjcmVhdGVcIik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIExvYWQgcGhhc2UgLSBGZXRjaCBkYXRhIGZyb20gQVBJc1xuICAgICAqIENhbGxlZCBib3R0b20tdXAsIGZ1bGx5IHBhcmFsbGVsXG4gICAgICogTk8gRE9NIE1PRElGSUNBVElPTlMgQUxMT1dFRCBJTiBUSElTIFBIQVNFXG4gICAgICovXG4gICAgYXN5bmMgbG9hZCgpIHtcbiAgICAgIGlmICh0aGlzLl9zdG9wcGVkIHx8IHRoaXMuX3JlYWR5X3N0YXRlID49IDIpXG4gICAgICAgIHJldHVybjtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJsb2FkXCIsIFwic3RhcnRcIik7XG4gICAgICBjb25zdCBhcmdzQmVmb3JlTG9hZCA9IEpTT04uc3RyaW5naWZ5KHRoaXMuYXJncyk7XG4gICAgICBjb25zdCBwcm9wZXJ0aWVzQmVmb3JlTG9hZCA9IG5ldyBTZXQoT2JqZWN0LmtleXModGhpcykpO1xuICAgICAgdGhpcy5fX2xvYWRpbmcgPSB0cnVlO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgdGhpcy5vbl9sb2FkKCk7XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICB0aGlzLl9fbG9hZGluZyA9IGZhbHNlO1xuICAgICAgfVxuICAgICAgY29uc3QgYXJnc0FmdGVyTG9hZCA9IEpTT04uc3RyaW5naWZ5KHRoaXMuYXJncyk7XG4gICAgICBjb25zdCBwcm9wZXJ0aWVzQWZ0ZXJMb2FkID0gT2JqZWN0LmtleXModGhpcyk7XG4gICAgICBpZiAoYXJnc0JlZm9yZUxvYWQgIT09IGFyZ3NBZnRlckxvYWQpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihgW0pRSFRNTF0gV0FSTklORzogQ29tcG9uZW50IFwiJHt0aGlzLmNvbXBvbmVudF9uYW1lKCl9XCIgbW9kaWZpZWQgdGhpcy5hcmdzIGluIG9uX2xvYWQoKS5cbm9uX2xvYWQoKSBzaG91bGQgT05MWSBtb2RpZnkgdGhpcy5kYXRhLiBUaGUgdGhpcy5hcmdzIHByb3BlcnR5IGlzIHJlYWQtb25seS5cblxuQmVmb3JlOiAke2FyZ3NCZWZvcmVMb2FkfVxuQWZ0ZXI6ICAke2FyZ3NBZnRlckxvYWR9XG5cbkZpeDogTW92ZSB5b3VyIG1vZGlmaWNhdGlvbnMgdG8gdGhpcy5kYXRhIGluc3RlYWQuYCk7XG4gICAgICB9XG4gICAgICBjb25zdCBuZXdQcm9wZXJ0aWVzID0gcHJvcGVydGllc0FmdGVyTG9hZC5maWx0ZXIoKHByb3ApID0+ICFwcm9wZXJ0aWVzQmVmb3JlTG9hZC5oYXMocHJvcCkgJiYgcHJvcCAhPT0gXCJkYXRhXCIpO1xuICAgICAgaWYgKG5ld1Byb3BlcnRpZXMubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zb2xlLmVycm9yKGBbSlFIVE1MXSBXQVJOSU5HOiBDb21wb25lbnQgXCIke3RoaXMuY29tcG9uZW50X25hbWUoKX1cIiBhZGRlZCBuZXcgcHJvcGVydGllcyBpbiBvbl9sb2FkKCkuXG5vbl9sb2FkKCkgc2hvdWxkIE9OTFkgbW9kaWZ5IHRoaXMuZGF0YS4gTmV3IHByb3BlcnRpZXMgZGV0ZWN0ZWQ6ICR7bmV3UHJvcGVydGllcy5qb2luKFwiLCBcIil9XG5cbkZpeDogU3RvcmUgeW91ciBkYXRhIGluIHRoaXMuZGF0YSBpbnN0ZWFkOlxuICBcXHUyNzRDIHRoaXMuJHtuZXdQcm9wZXJ0aWVzWzBdfSA9IHZhbHVlO1xuICBcXHUyNzA1IHRoaXMuZGF0YS4ke25ld1Byb3BlcnRpZXNbMF19ID0gdmFsdWU7YCk7XG4gICAgICB9XG4gICAgICB0aGlzLl9yZWFkeV9zdGF0ZSA9IDI7XG4gICAgICB0aGlzLl91cGRhdGVfZGVidWdfYXR0cnMoKTtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJsb2FkXCIsIFwiY29tcGxldGVcIik7XG4gICAgICB0aGlzLnRyaWdnZXIoXCJsb2FkXCIpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWFkeSBwaGFzZSAtIENvbXBvbmVudCBmdWxseSBpbml0aWFsaXplZFxuICAgICAqIENhbGxlZCBib3R0b20tdXAgKGNoaWxkcmVuIGJlZm9yZSBwYXJlbnQpXG4gICAgICovXG4gICAgYXN5bmMgcmVhZHkoKSB7XG4gICAgICBpZiAodGhpcy5fc3RvcHBlZCB8fCB0aGlzLl9yZWFkeV9zdGF0ZSA+PSA0KVxuICAgICAgICByZXR1cm47XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwicmVhZHlcIiwgXCJzdGFydFwiKTtcbiAgICAgIGF3YWl0IHRoaXMuX3dhaXRfZm9yX2NoaWxkcmVuX3JlYWR5KCk7XG4gICAgICBhd2FpdCB0aGlzLm9uX3JlYWR5KCk7XG4gICAgICB0aGlzLl9yZWFkeV9zdGF0ZSA9IDQ7XG4gICAgICB0aGlzLl91cGRhdGVfZGVidWdfYXR0cnMoKTtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJyZWFkeVwiLCBcImNvbXBsZXRlXCIpO1xuICAgICAgdGhpcy50cmlnZ2VyKFwicmVhZHlcIik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFdhaXQgZm9yIGFsbCBjaGlsZCBjb21wb25lbnRzIHRvIHJlYWNoIHJlYWR5IHN0YXRlXG4gICAgICogRW5zdXJlcyBib3R0b20tdXAgb3JkZXJpbmcgKGNoaWxkcmVuIHJlYWR5IGJlZm9yZSBwYXJlbnQpXG4gICAgICogQHByaXZhdGVcbiAgICAgKi9cbiAgICBhc3luYyBfd2FpdF9mb3JfY2hpbGRyZW5fcmVhZHkoKSB7XG4gICAgICBjb25zdCBjaGlsZHJlbiA9IHRoaXMuX2dldF9kb21fY2hpbGRyZW4oKTtcbiAgICAgIGlmIChjaGlsZHJlbi5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3QgcmVhZHlfcHJvbWlzZXMgPSBbXTtcbiAgICAgIGZvciAoY29uc3QgY2hpbGQgb2YgY2hpbGRyZW4pIHtcbiAgICAgICAgaWYgKGNoaWxkLl9yZWFkeV9zdGF0ZSA+PSA0KSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcmVhZHlfcHJvbWlzZSA9IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgY2hpbGQub24oXCJyZWFkeVwiLCAoKSA9PiByZXNvbHZlKCkpO1xuICAgICAgICB9KTtcbiAgICAgICAgcmVhZHlfcHJvbWlzZXMucHVzaChyZWFkeV9wcm9taXNlKTtcbiAgICAgIH1cbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKHJlYWR5X3Byb21pc2VzKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVpbml0aWFsaXplIHRoZSBjb21wb25lbnQgLSBmdWxsIHJlc2V0IGFuZCByZS1pbml0aWFsaXphdGlvblxuICAgICAqIFdpcGVzIHRoZSBpbm5lckhUTUwsIHJlc2V0cyBkYXRhIHRvIGVtcHR5LCBhbmQgcnVucyBmdWxsIGxpZmVjeWNsZVxuICAgICAqL1xuICAgIGFzeW5jIHJlaW5pdGlhbGl6ZSgpIHtcbiAgICAgIGlmICh0aGlzLl9zdG9wcGVkKVxuICAgICAgICByZXR1cm47XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwicmVpbml0aWFsaXplXCIsIFwic3RhcnRcIik7XG4gICAgICB0aGlzLiRbMF0uaW5uZXJIVE1MID0gXCJcIjtcbiAgICAgIHRoaXMuZGF0YSA9IHt9O1xuICAgICAgdGhpcy5fcmVhZHlfc3RhdGUgPSAwO1xuICAgICAgdGhpcy5fZGF0YV9iZWZvcmVfcmVuZGVyID0gbnVsbDtcbiAgICAgIHRoaXMuX2RvbV9jaGlsZHJlbi5jbGVhcigpO1xuICAgICAgYXdhaXQgdGhpcy5fcmVuZGVyKCk7XG4gICAgICBhd2FpdCB0aGlzLmNyZWF0ZSgpO1xuICAgICAgYXdhaXQgdGhpcy5sb2FkKCk7XG4gICAgICBpZiAodGhpcy5zaG91bGRfcmVyZW5kZXIoKSkge1xuICAgICAgICBhd2FpdCB0aGlzLl9yZW5kZXIoKTtcbiAgICAgIH1cbiAgICAgIGF3YWl0IHRoaXMucmVhZHkoKTtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJyZWluaXRpYWxpemVcIiwgXCJjb21wbGV0ZVwiKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVsb2FkIGNvbXBvbmVudCAtIHJlLWZldGNoIGRhdGEgYW5kIHJlLXJlbmRlclxuICAgICAqIFJlLXJ1bnMgb25fbG9hZCgpLCBhbHdheXMgcmVuZGVycywgYW5kIGNhbGxzIG9uX3JlYWR5KClcbiAgICAgKi9cbiAgICBhc3luYyByZWxvYWQoKSB7XG4gICAgICBpZiAodGhpcy5fc3RvcHBlZClcbiAgICAgICAgcmV0dXJuO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcInJlbG9hZFwiLCBcInN0YXJ0XCIpO1xuICAgICAgY29uc3QgaGFzX2N1c3RvbV9vbl9sb2FkID0gdGhpcy5vbl9sb2FkICE9PSBfSnFodG1sX0NvbXBvbmVudC5wcm90b3R5cGUub25fbG9hZDtcbiAgICAgIGlmIChoYXNfY3VzdG9tX29uX2xvYWQpIHtcbiAgICAgICAgY29uc3QgYXJnc0JlZm9yZUxvYWQgPSBKU09OLnN0cmluZ2lmeSh0aGlzLmFyZ3MpO1xuICAgICAgICBjb25zdCBwcm9wZXJ0aWVzQmVmb3JlTG9hZCA9IG5ldyBTZXQoT2JqZWN0LmtleXModGhpcykpO1xuICAgICAgICB0aGlzLl9fbG9hZGluZyA9IHRydWU7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5vbl9sb2FkKCk7XG4gICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAgdGhpcy5fX2xvYWRpbmcgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBhcmdzQWZ0ZXJMb2FkID0gSlNPTi5zdHJpbmdpZnkodGhpcy5hcmdzKTtcbiAgICAgICAgY29uc3QgcHJvcGVydGllc0FmdGVyTG9hZCA9IE9iamVjdC5rZXlzKHRoaXMpO1xuICAgICAgICBpZiAoYXJnc0JlZm9yZUxvYWQgIT09IGFyZ3NBZnRlckxvYWQpIHtcbiAgICAgICAgICBjb25zb2xlLmVycm9yKGBbSlFIVE1MXSBXQVJOSU5HOiBDb21wb25lbnQgXCIke3RoaXMuY29tcG9uZW50X25hbWUoKX1cIiBtb2RpZmllZCB0aGlzLmFyZ3MgaW4gb25fbG9hZCgpLlxub25fbG9hZCgpIHNob3VsZCBPTkxZIG1vZGlmeSB0aGlzLmRhdGEuIFRoZSB0aGlzLmFyZ3MgcHJvcGVydHkgaXMgcmVhZC1vbmx5LlxuXG5CZWZvcmU6ICR7YXJnc0JlZm9yZUxvYWR9XG5BZnRlcjogICR7YXJnc0FmdGVyTG9hZH1cblxuRml4OiBNb3ZlIHlvdXIgbW9kaWZpY2F0aW9ucyB0byB0aGlzLmRhdGEgaW5zdGVhZC5gKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBuZXdQcm9wZXJ0aWVzID0gcHJvcGVydGllc0FmdGVyTG9hZC5maWx0ZXIoKHByb3ApID0+ICFwcm9wZXJ0aWVzQmVmb3JlTG9hZC5oYXMocHJvcCkgJiYgcHJvcCAhPT0gXCJkYXRhXCIpO1xuICAgICAgICBpZiAobmV3UHJvcGVydGllcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgY29uc29sZS5lcnJvcihgW0pRSFRNTF0gV0FSTklORzogQ29tcG9uZW50IFwiJHt0aGlzLmNvbXBvbmVudF9uYW1lKCl9XCIgYWRkZWQgbmV3IHByb3BlcnRpZXMgaW4gb25fbG9hZCgpLlxub25fbG9hZCgpIHNob3VsZCBPTkxZIG1vZGlmeSB0aGlzLmRhdGEuIE5ldyBwcm9wZXJ0aWVzIGRldGVjdGVkOiAke25ld1Byb3BlcnRpZXMuam9pbihcIiwgXCIpfVxuXG5GaXg6IFN0b3JlIHlvdXIgZGF0YSBpbiB0aGlzLmRhdGEgaW5zdGVhZDpcbiAgXFx1Mjc0QyB0aGlzLiR7bmV3UHJvcGVydGllc1swXX0gPSB2YWx1ZTtcbiAgXFx1MjcwNSB0aGlzLmRhdGEuJHtuZXdQcm9wZXJ0aWVzWzBdfSA9IHZhbHVlO2ApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBhd2FpdCB0aGlzLnJlbmRlcigpO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcInJlbG9hZFwiLCBcImNvbXBsZXRlXCIpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBEZXN0cm95IHRoZSBjb21wb25lbnQgYW5kIGNsZWFudXBcbiAgICAgKiBDYWxsZWQgYXV0b21hdGljYWxseSBieSBNdXRhdGlvbk9ic2VydmVyIHdoZW4gY29tcG9uZW50IGlzIHJlbW92ZWQgZnJvbSBET01cbiAgICAgKiBDYW4gYWxzbyBiZSBjYWxsZWQgbWFudWFsbHkgZm9yIGV4cGxpY2l0IGNsZWFudXBcbiAgICAgKi9cbiAgICAvKipcbiAgICAgKiBJbnRlcm5hbCBzdG9wIG1ldGhvZCAtIHN0b3BzIGp1c3QgdGhpcyBjb21wb25lbnQgKG5vIGNoaWxkcmVuKVxuICAgICAqIFNldHMgc3RvcHBlZCBmbGFnLCBjYWxscyBsaWZlY3ljbGUgaG9va3MsIGJ1dCBsZWF2ZXMgRE9NIGludGFjdFxuICAgICAqIEBwcml2YXRlXG4gICAgICovXG4gICAgX3N0b3AoKSB7XG4gICAgICBpZiAodGhpcy5fc3RvcHBlZClcbiAgICAgICAgcmV0dXJuO1xuICAgICAgdGhpcy5fc3RvcHBlZCA9IHRydWU7XG4gICAgICBjb25zdCBoYXNfY3VzdG9tX2Rlc3Ryb3kgPSB0aGlzLm9uX2Rlc3Ryb3kgIT09IF9KcWh0bWxfQ29tcG9uZW50LnByb3RvdHlwZS5vbl9kZXN0cm95O1xuICAgICAgY29uc3QgaGFzX2Rlc3Ryb3lfY2FsbGJhY2tzID0gdGhpcy5fb25fcmVnaXN0ZXJlZChcImRlc3Ryb3lcIik7XG4gICAgICBpZiAoIWhhc19jdXN0b21fZGVzdHJveSAmJiAhaGFzX2Rlc3Ryb3lfY2FsbGJhY2tzKSB7XG4gICAgICAgIHRoaXMuX2xpZmVjeWNsZV9tYW5hZ2VyLnVucmVnaXN0ZXJfY29tcG9uZW50KHRoaXMpO1xuICAgICAgICB0aGlzLl9yZWFkeV9zdGF0ZSA9IDk5O1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwiZGVzdHJveVwiLCBcInN0YXJ0XCIpO1xuICAgICAgdGhpcy4kLmFkZENsYXNzKFwiX0NvbXBvbmVudF9TdG9wcGVkXCIpO1xuICAgICAgdGhpcy5fbGlmZWN5Y2xlX21hbmFnZXIudW5yZWdpc3Rlcl9jb21wb25lbnQodGhpcyk7XG4gICAgICBjb25zdCBkZXN0cm95UmVzdWx0ID0gdGhpcy5vbl9kZXN0cm95KCk7XG4gICAgICBpZiAoZGVzdHJveVJlc3VsdCAmJiB0eXBlb2YgZGVzdHJveVJlc3VsdC50aGVuID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBDb21wb25lbnQgXCIke3RoaXMuY29tcG9uZW50X25hbWUoKX1cIiByZXR1cm5lZCBhIFByb21pc2UgZnJvbSBvbl9kZXN0cm95KCkuIG9uX2Rlc3Ryb3koKSBtdXN0IGJlIHN5bmNocm9ub3VzIGNvZGUuIFJlbW92ZSAnYXN5bmMnIGZyb20gdGhlIGZ1bmN0aW9uIGRlY2xhcmF0aW9uLmApO1xuICAgICAgfVxuICAgICAgdGhpcy50cmlnZ2VyKFwiZGVzdHJveVwiKTtcbiAgICAgIHRoaXMuJC50cmlnZ2VyKFwiZGVzdHJveVwiKTtcbiAgICAgIGlmICh0aGlzLl9kb21fcGFyZW50KSB7XG4gICAgICAgIHRoaXMuX2RvbV9wYXJlbnQuX2RvbV9jaGlsZHJlbi5kZWxldGUodGhpcyk7XG4gICAgICB9XG4gICAgICB0aGlzLl9yZWFkeV9zdGF0ZSA9IDk5O1xuICAgICAgdGhpcy5fdXBkYXRlX2RlYnVnX2F0dHJzKCk7XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwiZGVzdHJveVwiLCBcImNvbXBsZXRlXCIpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTdG9wIGNvbXBvbmVudCBsaWZlY3ljbGUgLSBzdG9wcyBhbGwgZGVzY2VuZGFudCBjb21wb25lbnRzIHRoZW4gc2VsZlxuICAgICAqIExlYXZlcyBET00gaW50YWN0LCBqdXN0IHN0b3BzIGxpZmVjeWNsZSBlbmdpbmUgYW5kIGZpcmVzIGNsZWFudXAgaG9va3NcbiAgICAgKi9cbiAgICBzdG9wKCkge1xuICAgICAgdGhpcy4kLmZpbmQoXCIuSnFodG1sX0NvbXBvbmVudFwiKS5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICBjb25zdCBjaGlsZCA9ICQodGhpcykuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgIGlmIChjaGlsZCAmJiAhY2hpbGQuX3N0b3BwZWQpIHtcbiAgICAgICAgICBjaGlsZC5fc3RvcCgpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHRoaXMuX3N0b3AoKTtcbiAgICB9XG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIE92ZXJyaWRhYmxlIExpZmVjeWNsZSBIb29rc1xuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICBvbl9yZW5kZXIoKSB7XG4gICAgfVxuICAgIG9uX2NyZWF0ZSgpIHtcbiAgICB9XG4gICAgYXN5bmMgb25fbG9hZCgpIHtcbiAgICB9XG4gICAgYXN5bmMgb25fcmVhZHkoKSB7XG4gICAgfVxuICAgIG9uX2Rlc3Ryb3koKSB7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNob3VsZCBjb21wb25lbnQgcmUtcmVuZGVyIGFmdGVyIGxvYWQ/XG4gICAgICogQnkgZGVmYXVsdCwgb25seSByZS1yZW5kZXJzIGlmIGRhdGEgaGFzIGNoYW5nZWRcbiAgICAgKiBPdmVycmlkZSB0byBjb250cm9sIHJlLXJlbmRlcmluZyBiZWhhdmlvclxuICAgICAqL1xuICAgIHNob3VsZF9yZXJlbmRlcigpIHtcbiAgICAgIGNvbnN0IGN1cnJlbnREYXRhU3RhdGUgPSBKU09OLnN0cmluZ2lmeSh0aGlzLmRhdGEpO1xuICAgICAgY29uc3QgZGF0YUNoYW5nZWQgPSB0aGlzLl9kYXRhX2JlZm9yZV9yZW5kZXIgIT09IGN1cnJlbnREYXRhU3RhdGU7XG4gICAgICBpZiAoZGF0YUNoYW5nZWQpIHtcbiAgICAgICAgdGhpcy5fZGF0YV9iZWZvcmVfcmVuZGVyID0gY3VycmVudERhdGFTdGF0ZTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBkYXRhQ2hhbmdlZDtcbiAgICB9XG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIFB1YmxpYyBBUElcbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLyoqXG4gICAgICogR2V0IGNvbXBvbmVudCBuYW1lIGZvciBkZWJ1Z2dpbmdcbiAgICAgKi9cbiAgICBjb21wb25lbnRfbmFtZSgpIHtcbiAgICAgIHJldHVybiB0aGlzLmNvbnN0cnVjdG9yLm5hbWU7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEVtaXQgYSBqUXVlcnkgZXZlbnQgZnJvbSBjb21wb25lbnQgcm9vdFxuICAgICAqL1xuICAgIGVtaXQoZXZlbnRfbmFtZSwgZGF0YSkge1xuICAgICAgdGhpcy5fbG9nX2RlYnVnKFwiZW1pdFwiLCBldmVudF9uYW1lLCBkYXRhKTtcbiAgICAgIHRoaXMuJC50cmlnZ2VyKGV2ZW50X25hbWUsIGRhdGEpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWdpc3RlciBsaWZlY3ljbGUgZXZlbnQgY2FsbGJhY2tcbiAgICAgKiBBbGxvd2VkIGV2ZW50czogJ3JlbmRlcicsICdjcmVhdGUnLCAnbG9hZCcsICdyZWFkeScsICdkZXN0cm95J1xuICAgICAqIENhbGxiYWNrcyBmaXJlIGFmdGVyIHRoZSBsaWZlY3ljbGUgbWV0aG9kIGNvbXBsZXRlc1xuICAgICAqIElmIHRoZSBldmVudCBoYXMgYWxyZWFkeSBvY2N1cnJlZCwgdGhlIGNhbGxiYWNrIGZpcmVzIGltbWVkaWF0ZWx5IEFORCByZWdpc3RlcnMgZm9yIGZ1dHVyZSBvY2N1cnJlbmNlc1xuICAgICAqL1xuICAgIG9uKGV2ZW50X25hbWUsIGNhbGxiYWNrKSB7XG4gICAgICBjb25zdCBhbGxvd2VkX2V2ZW50cyA9IFtcInJlbmRlclwiLCBcImNyZWF0ZVwiLCBcImxvYWRcIiwgXCJyZWFkeVwiLCBcImRlc3Ryb3lcIl07XG4gICAgICBpZiAoIWFsbG93ZWRfZXZlbnRzLmluY2x1ZGVzKGV2ZW50X25hbWUpKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYFtKUUhUTUxdIENvbXBvbmVudC5vbigpIG9ubHkgc3VwcG9ydHMgbGlmZWN5Y2xlIGV2ZW50czogJHthbGxvd2VkX2V2ZW50cy5qb2luKFwiLCBcIil9LiBSZWNlaXZlZDogJHtldmVudF9uYW1lfWApO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgIH1cbiAgICAgIGlmICghdGhpcy5fbGlmZWN5Y2xlX2NhbGxiYWNrcy5oYXMoZXZlbnRfbmFtZSkpIHtcbiAgICAgICAgdGhpcy5fbGlmZWN5Y2xlX2NhbGxiYWNrcy5zZXQoZXZlbnRfbmFtZSwgW10pO1xuICAgICAgfVxuICAgICAgdGhpcy5fbGlmZWN5Y2xlX2NhbGxiYWNrcy5nZXQoZXZlbnRfbmFtZSkucHVzaChjYWxsYmFjayk7XG4gICAgICBpZiAodGhpcy5fbGlmZWN5Y2xlX3N0YXRlcy5oYXMoZXZlbnRfbmFtZSkpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjYWxsYmFjayh0aGlzKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBjb25zb2xlLmVycm9yKGBbSlFIVE1MXSBFcnJvciBpbiAke2V2ZW50X25hbWV9IGNhbGxiYWNrOmAsIGVycm9yKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFRyaWdnZXIgYSBsaWZlY3ljbGUgZXZlbnQgLSBmaXJlcyBhbGwgcmVnaXN0ZXJlZCBjYWxsYmFja3NcbiAgICAgKiBNYXJrcyBldmVudCBhcyBvY2N1cnJlZCBzbyBmdXR1cmUgLm9uKCkgY2FsbHMgZmlyZSBpbW1lZGlhdGVseVxuICAgICAqL1xuICAgIHRyaWdnZXIoZXZlbnRfbmFtZSkge1xuICAgICAgdGhpcy5fbGlmZWN5Y2xlX3N0YXRlcy5hZGQoZXZlbnRfbmFtZSk7XG4gICAgICBjb25zdCBjYWxsYmFja3MgPSB0aGlzLl9saWZlY3ljbGVfY2FsbGJhY2tzLmdldChldmVudF9uYW1lKTtcbiAgICAgIGlmIChjYWxsYmFja3MpIHtcbiAgICAgICAgZm9yIChjb25zdCBjYWxsYmFjayBvZiBjYWxsYmFja3MpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY2FsbGJhY2suYmluZCh0aGlzKSh0aGlzKTtcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihgW0pRSFRNTF0gRXJyb3IgaW4gJHtldmVudF9uYW1lfSBjYWxsYmFjazpgLCBlcnJvcik7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENoZWNrIGlmIGFueSBjYWxsYmFja3MgYXJlIHJlZ2lzdGVyZWQgZm9yIGEgZ2l2ZW4gZXZlbnRcbiAgICAgKiBVc2VkIHRvIGRldGVybWluZSBpZiBjbGVhbnVwIGxvZ2ljIG5lZWRzIHRvIHJ1blxuICAgICAqL1xuICAgIF9vbl9yZWdpc3RlcmVkKGV2ZW50X25hbWUpIHtcbiAgICAgIGNvbnN0IGNhbGxiYWNrcyA9IHRoaXMuX2xpZmVjeWNsZV9jYWxsYmFja3MuZ2V0KGV2ZW50X25hbWUpO1xuICAgICAgcmV0dXJuICEhKGNhbGxiYWNrcyAmJiBjYWxsYmFja3MubGVuZ3RoID4gMCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEZpbmQgZWxlbWVudCBieSBzY29wZWQgSURcbiAgICAgKlxuICAgICAqIFNlYXJjaGVzIGZvciBlbGVtZW50cyB3aXRoIGlkPVwibG9jYWxfaWQ6VEhJU19DT01QT05FTlRfQ0lEXCJcbiAgICAgKlxuICAgICAqIEV4YW1wbGU6XG4gICAgICogICBUZW1wbGF0ZTogPGJ1dHRvbiAkaWQ9XCJzYXZlX2J0blwiPlNhdmU8L2J1dHRvbj5cbiAgICAgKiAgIFJlbmRlcmVkOiA8YnV0dG9uIGlkPVwic2F2ZV9idG46YWJjMTIzXCIgZGF0YS1pZD1cInNhdmVfYnRuXCI+U2F2ZTwvYnV0dG9uPlxuICAgICAqICAgQWNjZXNzOiAgIHRoaXMuJGlkKCdzYXZlX2J0bicpICAvLyBSZXR1cm5zIGpRdWVyeSBlbGVtZW50XG4gICAgICpcbiAgICAgKiBQZXJmb3JtYW5jZTogVXNlcyBuYXRpdmUgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoKSB3aGVuIGNvbXBvbmVudCBpcyBpbiBET00sXG4gICAgICogZmFsbHMgYmFjayB0byBqUXVlcnkuZmluZCgpIGZvciBjb21wb25lbnRzIG5vdCB5ZXQgYXR0YWNoZWQgdG8gRE9NLlxuICAgICAqXG4gICAgICogQHBhcmFtIGxvY2FsX2lkIFRoZSBsb2NhbCBJRCAod2l0aG91dCBfY2lkIHN1ZmZpeClcbiAgICAgKiBAcmV0dXJucyBqUXVlcnkgZWxlbWVudCB3aXRoIGlkPVwibG9jYWxfaWQ6X2NpZFwiLCBvciBlbXB0eSBqUXVlcnkgb2JqZWN0IGlmIG5vdCBmb3VuZFxuICAgICAqL1xuICAgICRpZChsb2NhbF9pZCkge1xuICAgICAgY29uc3Qgc2NvcGVkSWQgPSBgJHtsb2NhbF9pZH06JHt0aGlzLl9jaWR9YDtcbiAgICAgIGNvbnN0IGVsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoc2NvcGVkSWQpO1xuICAgICAgaWYgKGVsKSB7XG4gICAgICAgIHJldHVybiAkKGVsKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLiQuZmluZChgIyR7JC5lc2NhcGVTZWxlY3RvcihzY29wZWRJZCl9YCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBjb21wb25lbnQgaW5zdGFuY2UgYnkgc2NvcGVkIElEXG4gICAgICpcbiAgICAgKiBDb252ZW5pZW5jZSBtZXRob2QgdGhhdCBmaW5kcyBlbGVtZW50IGJ5IHNjb3BlZCBJRCBhbmQgcmV0dXJucyB0aGUgY29tcG9uZW50IGluc3RhbmNlLlxuICAgICAqXG4gICAgICogRXhhbXBsZTpcbiAgICAgKiAgIFRlbXBsYXRlOiA8VXNlcl9DYXJkICRpZD1cImFjdGl2ZV91c2VyXCIgLz5cbiAgICAgKiAgIEFjY2VzczogICBjb25zdCB1c2VyID0gdGhpcy5pZCgnYWN0aXZlX3VzZXInKTsgIC8vIFJldHVybnMgVXNlcl9DYXJkIGluc3RhbmNlXG4gICAgICogICAgICAgICAgICAgdXNlci5kYXRhLm5hbWUgIC8vIEFjY2VzcyBjb21wb25lbnQncyBkYXRhXG4gICAgICpcbiAgICAgKiBAcGFyYW0gbG9jYWxfaWQgVGhlIGxvY2FsIElEICh3aXRob3V0IF9jaWQgc3VmZml4KVxuICAgICAqIEByZXR1cm5zIENvbXBvbmVudCBpbnN0YW5jZSBvciBudWxsIGlmIG5vdCBmb3VuZCBvciBub3QgYSBjb21wb25lbnRcbiAgICAgKi9cbiAgICBpZChsb2NhbF9pZCkge1xuICAgICAgY29uc3QgZWxlbWVudCA9IHRoaXMuJGlkKGxvY2FsX2lkKTtcbiAgICAgIGNvbnN0IGNvbXBvbmVudCA9IGVsZW1lbnQuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICBpZiAoIWNvbXBvbmVudCAmJiBlbGVtZW50Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc29sZS53YXJuKGBDb21wb25lbnQgJHt0aGlzLmNvbnN0cnVjdG9yLm5hbWV9IHRyaWVkIHRvIGNhbGwgLmlkKCcke2xvY2FsX2lkfScpIC0gJHtsb2NhbF9pZH0gZXhpc3RzLCBob3dldmVyLCBpdCBpcyBub3QgYSBjb21wb25lbnQgb3IgJHJlZHJhd2FibGUuIERpZCB5b3UgZm9yZ2V0IHRvIGFkZCAkcmVkcmF3YWJsZSB0byB0aGUgdGFnP2ApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGNvbXBvbmVudCB8fCBudWxsO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgdGhlIGNvbXBvbmVudCB0aGF0IGluc3RhbnRpYXRlZCB0aGlzIGNvbXBvbmVudCAocmVuZGVyZWQgaXQgaW4gdGhlaXIgdGVtcGxhdGUpXG4gICAgICogUmV0dXJucyBudWxsIGlmIGNvbXBvbmVudCB3YXMgY3JlYXRlZCBwcm9ncmFtbWF0aWNhbGx5IHZpYSAkKCkuY29tcG9uZW50KClcbiAgICAgKi9cbiAgICBpbnN0YW50aWF0b3IoKSB7XG4gICAgICByZXR1cm4gdGhpcy5faW5zdGFudGlhdG9yO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBGaW5kIGRlc2NlbmRhbnQgY29tcG9uZW50cyBieSBDU1Mgc2VsZWN0b3JcbiAgICAgKi9cbiAgICBmaW5kKHNlbGVjdG9yKSB7XG4gICAgICBjb25zdCBjb21wb25lbnRzID0gW107XG4gICAgICB0aGlzLiQuZmluZChzZWxlY3RvcikuZWFjaCgoXywgZWwpID0+IHtcbiAgICAgICAgY29uc3QgY29tcCA9ICQoZWwpLmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICBpZiAoY29tcCBpbnN0YW5jZW9mIF9KcWh0bWxfQ29tcG9uZW50KSB7XG4gICAgICAgICAgY29tcG9uZW50cy5wdXNoKGNvbXApO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHJldHVybiBjb21wb25lbnRzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBGaW5kIGNsb3Nlc3QgYW5jZXN0b3IgY29tcG9uZW50IG1hdGNoaW5nIHNlbGVjdG9yXG4gICAgICovXG4gICAgY2xvc2VzdChzZWxlY3Rvcikge1xuICAgICAgbGV0IGN1cnJlbnQgPSB0aGlzLiQucGFyZW50KCk7XG4gICAgICB3aGlsZSAoY3VycmVudC5sZW5ndGggPiAwKSB7XG4gICAgICAgIGlmIChjdXJyZW50LmlzKHNlbGVjdG9yKSkge1xuICAgICAgICAgIGNvbnN0IGNvbXAgPSBjdXJyZW50LmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICAgIGlmIChjb21wIGluc3RhbmNlb2YgX0pxaHRtbF9Db21wb25lbnQpIHtcbiAgICAgICAgICAgIHJldHVybiBjb21wO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjdXJyZW50ID0gY3VycmVudC5wYXJlbnQoKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLy8gU3RhdGljIE1ldGhvZHNcbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLyoqXG4gICAgICogR2V0IENTUyBjbGFzcyBoaWVyYXJjaHkgZm9yIHRoaXMgY29tcG9uZW50IHR5cGVcbiAgICAgKi9cbiAgICBzdGF0aWMgZ2V0X2NsYXNzX2hpZXJhcmNoeSgpIHtcbiAgICAgIGNvbnN0IGNsYXNzZXMgPSBbXTtcbiAgICAgIGxldCBjdG9yID0gdGhpcztcbiAgICAgIHdoaWxlIChjdG9yKSB7XG4gICAgICAgIGlmICghY3Rvci5uYW1lIHx8IHR5cGVvZiBjdG9yLm5hbWUgIT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBpZiAoY3Rvci5uYW1lICE9PSBcIk9iamVjdFwiICYmIGN0b3IubmFtZSAhPT0gXCJcIikge1xuICAgICAgICAgIGxldCBub3JtYWxpemVkTmFtZSA9IGN0b3IubmFtZTtcbiAgICAgICAgICBpZiAobm9ybWFsaXplZE5hbWUgPT09IFwiX0pxaHRtbF9Db21wb25lbnRcIiB8fCBub3JtYWxpemVkTmFtZSA9PT0gXCJfQmFzZV9KcWh0bWxfQ29tcG9uZW50XCIpIHtcbiAgICAgICAgICAgIG5vcm1hbGl6ZWROYW1lID0gXCJKcWh0bWxfQ29tcG9uZW50XCI7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNsYXNzZXMucHVzaChub3JtYWxpemVkTmFtZSk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgbmV4dFByb3RvID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKGN0b3IpO1xuICAgICAgICBpZiAoIW5leHRQcm90byB8fCBuZXh0UHJvdG8gPT09IE9iamVjdC5wcm90b3R5cGUgfHwgbmV4dFByb3RvLmNvbnN0cnVjdG9yID09PSBPYmplY3QpIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBjdG9yID0gbmV4dFByb3RvO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGNsYXNzZXM7XG4gICAgfVxuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvLyBQcml2YXRlIEltcGxlbWVudGF0aW9uXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIF9nZW5lcmF0ZV9jaWQoKSB7XG4gICAgICByZXR1cm4gdWlkKCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEZsYXR0ZW4gaW5zdHJ1Y3Rpb24gYXJyYXkgLSBjb252ZXJ0cyBbJ19jb250ZW50JywgWy4uLl1dIG1hcmtlcnMgdG8gZmxhdCBhcnJheVxuICAgICAqIFJlY3Vyc2l2ZWx5IGZsYXR0ZW5zIG5lc3RlZCBjb250ZW50IGZyb20gY29udGVudCgpIGNhbGxzXG4gICAgICovXG4gICAgX2ZsYXR0ZW5faW5zdHJ1Y3Rpb25zKGluc3RydWN0aW9ucykge1xuICAgICAgY29uc3QgcmVzdWx0ID0gW107XG4gICAgICBmb3IgKGNvbnN0IGluc3RydWN0aW9uIG9mIGluc3RydWN0aW9ucykge1xuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShpbnN0cnVjdGlvbikgJiYgaW5zdHJ1Y3Rpb25bMF0gPT09IFwiX2NvbnRlbnRcIiAmJiBBcnJheS5pc0FycmF5KGluc3RydWN0aW9uWzFdKSkge1xuICAgICAgICAgIGNvbnN0IGNvbnRlbnRJbnN0cnVjdGlvbnMgPSB0aGlzLl9mbGF0dGVuX2luc3RydWN0aW9ucyhpbnN0cnVjdGlvblsxXSk7XG4gICAgICAgICAgcmVzdWx0LnB1c2goLi4uY29udGVudEluc3RydWN0aW9ucyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmVzdWx0LnB1c2goaW5zdHJ1Y3Rpb24pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICBfYXBwbHlfY3NzX2NsYXNzZXMoKSB7XG4gICAgICBjb25zdCBoaWVyYXJjaHkgPSB0aGlzLmNvbnN0cnVjdG9yLmdldF9jbGFzc19oaWVyYXJjaHkoKTtcbiAgICAgIGNvbnN0IGNsYXNzZXNUb0FkZCA9IFsuLi5oaWVyYXJjaHldO1xuICAgICAgaWYgKHRoaXMuYXJncy5fY29tcG9uZW50X25hbWUgJiYgdGhpcy5hcmdzLl9jb21wb25lbnRfbmFtZSAhPT0gdGhpcy5jb25zdHJ1Y3Rvci5uYW1lKSB7XG4gICAgICAgIGNsYXNzZXNUb0FkZC51bnNoaWZ0KHRoaXMuYXJncy5fY29tcG9uZW50X25hbWUpO1xuICAgICAgfVxuICAgICAgY29uc3QgcHVibGljQ2xhc3NlcyA9IGNsYXNzZXNUb0FkZC5maWx0ZXIoKGNsYXNzTmFtZSkgPT4ge1xuICAgICAgICBpZiAoIWNsYXNzTmFtZSB8fCB0eXBlb2YgY2xhc3NOYW1lICE9PSBcInN0cmluZ1wiKSB7XG4gICAgICAgICAgY29uc29sZS53YXJuKFwiW0pRSFRNTF0gRmlsdGVyZWQgb3V0IGludmFsaWQgY2xhc3MgbmFtZTpcIiwgY2xhc3NOYW1lKTtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuICFjbGFzc05hbWUuc3RhcnRzV2l0aChcIl9cIik7XG4gICAgICB9KTtcbiAgICAgIGlmIChwdWJsaWNDbGFzc2VzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgdGhpcy4kLmFkZENsYXNzKHB1YmxpY0NsYXNzZXMuam9pbihcIiBcIikpO1xuICAgICAgfVxuICAgIH1cbiAgICBfYXBwbHlfZGVmYXVsdF9hdHRyaWJ1dGVzKCkge1xuICAgICAgbGV0IHRlbXBsYXRlO1xuICAgICAgaWYgKHRoaXMuYXJncy5fY29tcG9uZW50X25hbWUpIHtcbiAgICAgICAgdGVtcGxhdGUgPSBnZXRfdGVtcGxhdGUodGhpcy5hcmdzLl9jb21wb25lbnRfbmFtZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0ZW1wbGF0ZSA9IGdldF90ZW1wbGF0ZV9ieV9jbGFzcyh0aGlzLmNvbnN0cnVjdG9yKTtcbiAgICAgIH1cbiAgICAgIGlmICh0ZW1wbGF0ZSAmJiB0ZW1wbGF0ZS5kZWZhdWx0QXR0cmlidXRlcykge1xuICAgICAgICBjb25zdCBkZWZpbmVBdHRycyA9IHsgLi4udGVtcGxhdGUuZGVmYXVsdEF0dHJpYnV0ZXMgfTtcbiAgICAgICAgZGVsZXRlIGRlZmluZUF0dHJzLnRhZztcbiAgICAgICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy5lbmFibGVkKSB7XG4gICAgICAgICAgY29uc3QgY29tcG9uZW50TmFtZSA9IHRlbXBsYXRlLm5hbWUgfHwgdGhpcy5hcmdzLl9jb21wb25lbnRfbmFtZSB8fCB0aGlzLmNvbnN0cnVjdG9yLm5hbWU7XG4gICAgICAgICAgY29uc29sZS5sb2coYFtDb21wb25lbnRdIEFwcGx5aW5nIGRlZmF1bHRBdHRyaWJ1dGVzIGZvciAke2NvbXBvbmVudE5hbWV9OmAsIGRlZmluZUF0dHJzKTtcbiAgICAgICAgfVxuICAgICAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhkZWZpbmVBdHRycykpIHtcbiAgICAgICAgICBpZiAoa2V5ID09PSBcImNsYXNzXCIpIHtcbiAgICAgICAgICAgIGNvbnN0IGV4aXN0aW5nQ2xhc3NlcyA9IHRoaXMuJC5hdHRyKFwiY2xhc3NcIik7XG4gICAgICAgICAgICBpZiAoZXhpc3RpbmdDbGFzc2VzKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGV4aXN0aW5nID0gZXhpc3RpbmdDbGFzc2VzLnNwbGl0KC9cXHMrLykuZmlsdGVyKChjKSA9PiBjKTtcbiAgICAgICAgICAgICAgY29uc3QgbmV3Q2xhc3NlcyA9IFN0cmluZyh2YWx1ZSkuc3BsaXQoL1xccysvKS5maWx0ZXIoKGMpID0+IGMpO1xuICAgICAgICAgICAgICBmb3IgKGNvbnN0IG5ld0NsYXNzIG9mIG5ld0NsYXNzZXMpIHtcbiAgICAgICAgICAgICAgICBpZiAoIWV4aXN0aW5nLmluY2x1ZGVzKG5ld0NsYXNzKSkge1xuICAgICAgICAgICAgICAgICAgZXhpc3RpbmcucHVzaChuZXdDbGFzcyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHRoaXMuJC5hdHRyKFwiY2xhc3NcIiwgZXhpc3Rpbmcuam9pbihcIiBcIikpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdGhpcy4kLmF0dHIoXCJjbGFzc1wiLCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGlmIChrZXkgPT09IFwic3R5bGVcIikge1xuICAgICAgICAgICAgY29uc3QgZXhpc3RpbmdTdHlsZSA9IHRoaXMuJC5hdHRyKFwic3R5bGVcIik7XG4gICAgICAgICAgICBpZiAoZXhpc3RpbmdTdHlsZSkge1xuICAgICAgICAgICAgICBjb25zdCBleGlzdGluZ1J1bGVzID0gLyogQF9fUFVSRV9fICovIG5ldyBNYXAoKTtcbiAgICAgICAgICAgICAgZXhpc3RpbmdTdHlsZS5zcGxpdChcIjtcIikuZm9yRWFjaCgocnVsZSkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IFtwcm9wLCB2YWxdID0gcnVsZS5zcGxpdChcIjpcIikubWFwKChzKSA9PiBzLnRyaW0oKSk7XG4gICAgICAgICAgICAgICAgaWYgKHByb3AgJiYgdmFsKVxuICAgICAgICAgICAgICAgICAgZXhpc3RpbmdSdWxlcy5zZXQocHJvcCwgdmFsKTtcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgIFN0cmluZyh2YWx1ZSkuc3BsaXQoXCI7XCIpLmZvckVhY2goKHJ1bGUpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBbcHJvcCwgdmFsXSA9IHJ1bGUuc3BsaXQoXCI6XCIpLm1hcCgocykgPT4gcy50cmltKCkpO1xuICAgICAgICAgICAgICAgIGlmIChwcm9wICYmIHZhbClcbiAgICAgICAgICAgICAgICAgIGV4aXN0aW5nUnVsZXMuc2V0KHByb3AsIHZhbCk7XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICBjb25zdCBtZXJnZWQgPSBBcnJheS5mcm9tKGV4aXN0aW5nUnVsZXMuZW50cmllcygpKS5tYXAoKFtwcm9wLCB2YWxdKSA9PiBgJHtwcm9wfTogJHt2YWx9YCkuam9pbihcIjsgXCIpO1xuICAgICAgICAgICAgICB0aGlzLiQuYXR0cihcInN0eWxlXCIsIG1lcmdlZCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICB0aGlzLiQuYXR0cihcInN0eWxlXCIsIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2UgaWYgKGtleS5zdGFydHNXaXRoKFwiJFwiKSB8fCBrZXkuc3RhcnRzV2l0aChcImRhdGEtXCIpKSB7XG4gICAgICAgICAgICBjb25zdCBkYXRhS2V5ID0ga2V5LnN0YXJ0c1dpdGgoXCIkXCIpID8ga2V5LnN1YnN0cmluZygxKSA6IGtleS5zdGFydHNXaXRoKFwiZGF0YS1cIikgPyBrZXkuc3Vic3RyaW5nKDUpIDoga2V5O1xuICAgICAgICAgICAgaWYgKCEoZGF0YUtleSBpbiB0aGlzLmFyZ3MpKSB7XG4gICAgICAgICAgICAgIHRoaXMuYXJnc1tkYXRhS2V5XSA9IHZhbHVlO1xuICAgICAgICAgICAgICB0aGlzLiQuZGF0YShkYXRhS2V5LCB2YWx1ZSk7XG4gICAgICAgICAgICAgIHRoaXMuJC5hdHRyKGtleS5zdGFydHNXaXRoKFwiJFwiKSA/IGBkYXRhLSR7ZGF0YUtleX1gIDoga2V5LCBTdHJpbmcodmFsdWUpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKCF0aGlzLiQuYXR0cihrZXkpKSB7XG4gICAgICAgICAgICAgIHRoaXMuJC5hdHRyKGtleSwgdmFsdWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBfc2V0X2F0dHJpYnV0ZXMoKSB7XG4gICAgICB0aGlzLiQuYXR0cihcImRhdGEtY2lkXCIsIHRoaXMuX2NpZCk7XG4gICAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LnZlcmJvc2UpIHtcbiAgICAgICAgdGhpcy4kLmF0dHIoXCJkYXRhLV9saWZlY3ljbGUtc3RhdGVcIiwgdGhpcy5fcmVhZHlfc3RhdGUudG9TdHJpbmcoKSk7XG4gICAgICB9XG4gICAgfVxuICAgIF91cGRhdGVfZGVidWdfYXR0cnMoKSB7XG4gICAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LnZlcmJvc2UpIHtcbiAgICAgICAgdGhpcy4kLmF0dHIoXCJkYXRhLV9saWZlY3ljbGUtc3RhdGVcIiwgdGhpcy5fcmVhZHlfc3RhdGUudG9TdHJpbmcoKSk7XG4gICAgICB9XG4gICAgfVxuICAgIF9maW5kX2RvbV9wYXJlbnQoKSB7XG4gICAgICBsZXQgY3VycmVudCA9IHRoaXMuJC5wYXJlbnQoKTtcbiAgICAgIHdoaWxlIChjdXJyZW50Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3QgcGFyZW50ID0gY3VycmVudC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgaWYgKHBhcmVudCBpbnN0YW5jZW9mIF9KcWh0bWxfQ29tcG9uZW50KSB7XG4gICAgICAgICAgdGhpcy5fZG9tX3BhcmVudCA9IHBhcmVudDtcbiAgICAgICAgICBwYXJlbnQuX2RvbV9jaGlsZHJlbi5hZGQodGhpcyk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgY3VycmVudCA9IGN1cnJlbnQucGFyZW50KCk7XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCBET00gY2hpbGRyZW4gKGNvbXBvbmVudHMgaW4gRE9NIHN1YnRyZWUpXG4gICAgICogVXNlcyBmYXN0IF9kb21fY2hpbGRyZW4gcmVnaXN0cnkgd2hlbiBwb3NzaWJsZSwgZmFsbHMgYmFjayB0byBET00gdHJhdmVyc2FsIGZvciBvZmYtRE9NIGNvbXBvbmVudHNcbiAgICAgKiBAcHJpdmF0ZSAtIFVzZWQgaW50ZXJuYWxseSBmb3IgbGlmZWN5Y2xlIGNvb3JkaW5hdGlvblxuICAgICAqL1xuICAgIF9nZXRfZG9tX2NoaWxkcmVuKCkge1xuICAgICAgaWYgKHRoaXMuX3VzZV9kb21fZmFsbGJhY2spIHtcbiAgICAgICAgY29uc3QgZGlyZWN0Q2hpbGRyZW4gPSBbXTtcbiAgICAgICAgdGhpcy4kLmZpbmQoXCIuSnFodG1sX0NvbXBvbmVudFwiKS5lYWNoKChfLCBlbCkgPT4ge1xuICAgICAgICAgIGNvbnN0ICRlbCA9ICQoZWwpO1xuICAgICAgICAgIGNvbnN0IGNvbXAgPSAkZWwuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgICAgaWYgKGNvbXAgaW5zdGFuY2VvZiBfSnFodG1sX0NvbXBvbmVudCkge1xuICAgICAgICAgICAgY29uc3QgY2xvc2VzdFBhcmVudCA9ICRlbC5wYXJlbnQoKS5jbG9zZXN0KFwiLkpxaHRtbF9Db21wb25lbnRcIik7XG4gICAgICAgICAgICBpZiAoY2xvc2VzdFBhcmVudC5sZW5ndGggPT09IDAgfHwgY2xvc2VzdFBhcmVudC5kYXRhKFwiX2NvbXBvbmVudFwiKSA9PT0gdGhpcykge1xuICAgICAgICAgICAgICBkaXJlY3RDaGlsZHJlbi5wdXNoKGNvbXApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBkaXJlY3RDaGlsZHJlbjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGNoaWxkcmVuID0gQXJyYXkuZnJvbSh0aGlzLl9kb21fY2hpbGRyZW4pO1xuICAgICAgcmV0dXJuIGNoaWxkcmVuLmZpbHRlcigoY2hpbGQpID0+IHtcbiAgICAgICAgcmV0dXJuICQuY29udGFpbnMoZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LCBjaGlsZC4kWzBdKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICBfbG9nX2xpZmVjeWNsZShwaGFzZSwgc3RhdHVzKSB7XG4gICAgICBsb2dMaWZlY3ljbGUodGhpcywgcGhhc2UsIHN0YXR1cyk7XG4gICAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB3aW5kb3cuSlFIVE1MX0RFQlVHKSB7XG4gICAgICAgIHdpbmRvdy5KUUhUTUxfREVCVUcubG9nKHRoaXMuY29tcG9uZW50X25hbWUoKSwgcGhhc2UsIHN0YXR1cywge1xuICAgICAgICAgIGNpZDogdGhpcy5fY2lkLFxuICAgICAgICAgIHJlYWR5X3N0YXRlOiB0aGlzLl9yZWFkeV9zdGF0ZSxcbiAgICAgICAgICBhcmdzOiB0aGlzLmFyZ3NcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICAgIF9sb2dfZGVidWcoYWN0aW9uLCAuLi5hcmdzKSB7XG4gICAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB3aW5kb3cuSlFIVE1MX0RFQlVHKSB7XG4gICAgICAgIHdpbmRvdy5KUUhUTUxfREVCVUcubG9nKHRoaXMuY29tcG9uZW50X25hbWUoKSwgXCJkZWJ1Z1wiLCBgJHthY3Rpb259OiAke2FyZ3MubWFwKChhKSA9PiBKU09OLnN0cmluZ2lmeShhKSkuam9pbihcIiwgXCIpfWApO1xuICAgICAgfVxuICAgIH1cbiAgfTtcbiAgYXN5bmMgZnVuY3Rpb24gcHJvY2Vzc19zbG90X2luaGVyaXRhbmNlKGNvbXBvbmVudCwgY2hpbGRTbG90cykge1xuICAgIGxldCBjdXJyZW50Q2xhc3MgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YoY29tcG9uZW50LmNvbnN0cnVjdG9yKTtcbiAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gV2Fsa2luZyBwcm90b3R5cGUgY2hhaW4gZm9yICR7Y29tcG9uZW50LmNvbnN0cnVjdG9yLm5hbWV9YCk7XG4gICAgd2hpbGUgKGN1cnJlbnRDbGFzcyAmJiBjdXJyZW50Q2xhc3MgIT09IEpxaHRtbF9Db21wb25lbnQgJiYgY3VycmVudENsYXNzLm5hbWUgIT09IFwiT2JqZWN0XCIpIHtcbiAgICAgIGNvbnN0IGNsYXNzTmFtZSA9IGN1cnJlbnRDbGFzcy5uYW1lO1xuICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdICAgQ2hlY2tpbmcgcGFyZW50IGNsYXNzOiAke2NsYXNzTmFtZX1gKTtcbiAgICAgIGlmIChjbGFzc05hbWUgPT09IFwiX0pxaHRtbF9Db21wb25lbnRcIiB8fCBjbGFzc05hbWUgPT09IFwiX0Jhc2VfSnFodG1sX0NvbXBvbmVudFwiKSB7XG4gICAgICAgIGN1cnJlbnRDbGFzcyA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihjdXJyZW50Q2xhc3MpO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHBhcmVudFRlbXBsYXRlID0gZ2V0X3RlbXBsYXRlKGNsYXNzTmFtZSk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSAgIFRlbXBsYXRlIGZvdW5kIGZvciAke2NsYXNzTmFtZX06YCwgcGFyZW50VGVtcGxhdGUgPyBwYXJlbnRUZW1wbGF0ZS5uYW1lIDogXCJudWxsXCIpO1xuICAgICAgICBpZiAocGFyZW50VGVtcGxhdGUgJiYgcGFyZW50VGVtcGxhdGUubmFtZSAhPT0gXCJKcWh0bWxfQ29tcG9uZW50XCIpIHtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gICBJbnZva2luZyBwYXJlbnQgdGVtcGxhdGUgJHtjbGFzc05hbWV9YCk7XG4gICAgICAgICAgY29uc3QgW3BhcmVudEluc3RydWN0aW9ucywgcGFyZW50Q29udGV4dF0gPSBwYXJlbnRUZW1wbGF0ZS5yZW5kZXIuY2FsbChcbiAgICAgICAgICAgIGNvbXBvbmVudCxcbiAgICAgICAgICAgIGNvbXBvbmVudC5kYXRhLFxuICAgICAgICAgICAgY29tcG9uZW50LmFyZ3MsXG4gICAgICAgICAgICBjaGlsZFNsb3RzXG4gICAgICAgICAgICAvLyBQYXNzIGNoaWxkIHNsb3RzIGFzIGNvbnRlbnQgcGFyYW1ldGVyXG4gICAgICAgICAgKTtcbiAgICAgICAgICBpZiAocGFyZW50SW5zdHJ1Y3Rpb25zICYmIHR5cGVvZiBwYXJlbnRJbnN0cnVjdGlvbnMgPT09IFwib2JqZWN0XCIgJiYgcGFyZW50SW5zdHJ1Y3Rpb25zLl9zbG90cykge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdICAgUGFyZW50IGFsc28gc2xvdC1vbmx5LCByZWN1cnNpbmdgKTtcbiAgICAgICAgICAgIHJldHVybiBhd2FpdCBwcm9jZXNzX3Nsb3RfaW5oZXJpdGFuY2UoY29tcG9uZW50LCBwYXJlbnRJbnN0cnVjdGlvbnMuX3Nsb3RzKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdICAgUGFyZW50IHJldHVybmVkIGluc3RydWN0aW9ucywgaW5oZXJpdGFuY2UgY29tcGxldGVgKTtcbiAgICAgICAgICByZXR1cm4gW3BhcmVudEluc3RydWN0aW9ucywgcGFyZW50Q29udGV4dF07XG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gRXJyb3IgbG9va2luZyB1cCBwYXJlbnQgdGVtcGxhdGUgZm9yICR7Y2xhc3NOYW1lfTpgLCBlcnJvcik7XG4gICAgICB9XG4gICAgICBjdXJyZW50Q2xhc3MgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YoY3VycmVudENsYXNzKTtcbiAgICB9XG4gICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBObyBwYXJlbnQgdGVtcGxhdGUgZm91bmQgYWZ0ZXIgd2Fsa2luZyBjaGFpbmApO1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGFzeW5jIGZ1bmN0aW9uIHJlbmRlcl90ZW1wbGF0ZShjb21wb25lbnQsIHRlbXBsYXRlX2ZuKSB7XG4gICAgbGV0IHJlbmRlcl9mbiA9IHRlbXBsYXRlX2ZuO1xuICAgIGlmICghcmVuZGVyX2ZuKSB7XG4gICAgICBjb25zdCB0ZW1wbGF0ZV9kZWYgPSBnZXRfdGVtcGxhdGVfYnlfY2xhc3MoY29tcG9uZW50LmNvbnN0cnVjdG9yKTtcbiAgICAgIHJlbmRlcl9mbiA9IHRlbXBsYXRlX2RlZi5yZW5kZXI7XG4gICAgfVxuICAgIGlmICghcmVuZGVyX2ZuKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbXBvbmVudC4kLmVtcHR5KCk7XG4gICAgY29uc3QgZGVmYXVsdENvbnRlbnQgPSAoKSA9PiBcIlwiO1xuICAgIGxldCBbaW5zdHJ1Y3Rpb25zLCBjb250ZXh0XSA9IHJlbmRlcl9mbi5jYWxsKFxuICAgICAgY29tcG9uZW50LFxuICAgICAgY29tcG9uZW50LmRhdGEsXG4gICAgICBjb21wb25lbnQuYXJncyxcbiAgICAgIGRlZmF1bHRDb250ZW50XG4gICAgICAvLyBEZWZhdWx0IGNvbnRlbnQgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGVtcHR5IHN0cmluZ1xuICAgICk7XG4gICAgaWYgKGluc3RydWN0aW9ucyAmJiB0eXBlb2YgaW5zdHJ1Y3Rpb25zID09PSBcIm9iamVjdFwiICYmIGluc3RydWN0aW9ucy5fc2xvdHMpIHtcbiAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSBTbG90LW9ubHkgdGVtcGxhdGUgZGV0ZWN0ZWQgZm9yICR7Y29tcG9uZW50LmNvbnN0cnVjdG9yLm5hbWV9LCBpbnZva2luZyBpbmhlcml0YW5jZWApO1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHJvY2Vzc19zbG90X2luaGVyaXRhbmNlKGNvbXBvbmVudCwgaW5zdHJ1Y3Rpb25zLl9zbG90cyk7XG4gICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSBQYXJlbnQgdGVtcGxhdGUgZm91bmQsIHVzaW5nIHBhcmVudCBpbnN0cnVjdGlvbnNgKTtcbiAgICAgICAgaW5zdHJ1Y3Rpb25zID0gcmVzdWx0WzBdO1xuICAgICAgICBjb250ZXh0ID0gcmVzdWx0WzFdO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBObyBwYXJlbnQgdGVtcGxhdGUgZm91bmQgZm9yICR7Y29tcG9uZW50LmNvbnN0cnVjdG9yLm5hbWV9LCByZW5kZXJpbmcgZW1wdHlgKTtcbiAgICAgICAgaW5zdHJ1Y3Rpb25zID0gW107XG4gICAgICB9XG4gICAgfVxuICAgIGF3YWl0IHByb2Nlc3NfaW5zdHJ1Y3Rpb25zKGluc3RydWN0aW9ucywgY29tcG9uZW50LiQsIGNvbXBvbmVudCk7XG4gICAgYXdhaXQgcHJvY2Vzc19iaW5kaW5ncyhjb21wb25lbnQpO1xuICAgIGF3YWl0IGF0dGFjaF9ldmVudF9oYW5kbGVycyhjb21wb25lbnQpO1xuICB9XG4gIGFzeW5jIGZ1bmN0aW9uIHByb2Nlc3NfYmluZGluZ3MoY29tcG9uZW50KSB7XG4gICAgY29tcG9uZW50LiQuZmluZChcIltkYXRhLWJpbmQtcHJvcF0sIFtkYXRhLWJpbmQtdmFsdWVdLCBbZGF0YS1iaW5kLXRleHRdLCBbZGF0YS1iaW5kLWh0bWxdLCBbZGF0YS1iaW5kLWNsYXNzXSwgW2RhdGEtYmluZC1zdHlsZV1cIikuZWFjaCgoXywgZWxlbWVudCkgPT4ge1xuICAgICAgY29uc3QgZWwgPSAkKGVsZW1lbnQpO1xuICAgICAgY29uc3QgYXR0cnMgPSBlbGVtZW50LmF0dHJpYnV0ZXM7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGF0dHJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IGF0dHIgPSBhdHRyc1tpXTtcbiAgICAgICAgaWYgKGF0dHIubmFtZS5zdGFydHNXaXRoKFwiZGF0YS1iaW5kLVwiKSkge1xuICAgICAgICAgIGNvbnN0IGJpbmRpbmdfdHlwZSA9IGF0dHIubmFtZS5zdWJzdHJpbmcoMTApO1xuICAgICAgICAgIGNvbnN0IGV4cHJlc3Npb24gPSBhdHRyLnZhbHVlO1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB2YWx1ZSA9IGV2YWx1YXRlX2V4cHJlc3Npb24oZXhwcmVzc2lvbiwgY29tcG9uZW50KTtcbiAgICAgICAgICAgIHN3aXRjaCAoYmluZGluZ190eXBlKSB7XG4gICAgICAgICAgICAgIGNhc2UgXCJwcm9wXCI6XG4gICAgICAgICAgICAgICAgY29uc3QgcHJvcF9uYW1lID0gZWwuYXR0cihcImRhdGEtYmluZC1wcm9wLW5hbWVcIikgfHwgXCJ2YWx1ZVwiO1xuICAgICAgICAgICAgICAgIGVsLnByb3AocHJvcF9uYW1lLCB2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgXCJ2YWx1ZVwiOlxuICAgICAgICAgICAgICAgIGVsLnZhbCh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgXCJ0ZXh0XCI6XG4gICAgICAgICAgICAgICAgZWwudGV4dCh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgXCJodG1sXCI6XG4gICAgICAgICAgICAgICAgZWwuaHRtbCh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgXCJjbGFzc1wiOlxuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgICAgICAgICAgICAgIE9iamVjdC5lbnRyaWVzKHZhbHVlKS5mb3JFYWNoKChbY2xhc3NOYW1lLCBlbmFibGVkXSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBlbC50b2dnbGVDbGFzcyhjbGFzc05hbWUsICEhZW5hYmxlZCk7XG4gICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgZWwuYWRkQ2xhc3MoU3RyaW5nKHZhbHVlKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBjYXNlIFwic3R5bGVcIjpcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcIm9iamVjdFwiKSB7XG4gICAgICAgICAgICAgICAgICBlbC5jc3ModmFsdWUpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICBlbC5hdHRyKFwic3R5bGVcIiwgU3RyaW5nKHZhbHVlKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgIGVsLmF0dHIoYmluZGluZ190eXBlLCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYEVycm9yIGV2YWx1YXRpbmcgYmluZGluZyBcIiR7ZXhwcmVzc2lvbn1cIjpgLCBlcnJvcik7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgYXN5bmMgZnVuY3Rpb24gYXR0YWNoX2V2ZW50X2hhbmRsZXJzKGNvbXBvbmVudCkge1xuICAgIGNvbXBvbmVudC4kLmZpbmQoXCJbZGF0YS1vbi1jbGlja10sIFtkYXRhLW9uLWNoYW5nZV0sIFtkYXRhLW9uLXN1Ym1pdF0sIFtkYXRhLW9uLWtleXVwXSwgW2RhdGEtb24ta2V5ZG93bl0sIFtkYXRhLW9uLWZvY3VzXSwgW2RhdGEtb24tYmx1cl1cIikuZWFjaCgoXywgZWxlbWVudCkgPT4ge1xuICAgICAgY29uc3QgZWwgPSAkKGVsZW1lbnQpO1xuICAgICAgY29uc3QgYXR0cnMgPSBlbGVtZW50LmF0dHJpYnV0ZXM7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGF0dHJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IGF0dHIgPSBhdHRyc1tpXTtcbiAgICAgICAgaWYgKGF0dHIubmFtZS5zdGFydHNXaXRoKFwiZGF0YS1vbi1cIikpIHtcbiAgICAgICAgICBjb25zdCBldmVudF9uYW1lID0gYXR0ci5uYW1lLnN1YnN0cmluZyg4KTtcbiAgICAgICAgICBjb25zdCBoYW5kbGVyX2V4cHIgPSBhdHRyLnZhbHVlO1xuICAgICAgICAgIGVsLnJlbW92ZUF0dHIoYXR0ci5uYW1lKTtcbiAgICAgICAgICBlbC5vbihldmVudF9uYW1lLCBmdW5jdGlvbihldmVudCkge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgY29uc3QgaGFuZGxlciA9IGV2YWx1YXRlX2hhbmRsZXIoaGFuZGxlcl9leHByLCBjb21wb25lbnQpO1xuICAgICAgICAgICAgICBpZiAodHlwZW9mIGhhbmRsZXIgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgICAgICAgIGhhbmRsZXIuY2FsbChjb21wb25lbnQsIGV2ZW50KTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBldmFsdWF0ZV9leHByZXNzaW9uKGhhbmRsZXJfZXhwciwgY29tcG9uZW50LCB7ICRldmVudDogZXZlbnQgfSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYEVycm9yIGluICR7ZXZlbnRfbmFtZX0gaGFuZGxlciBcIiR7aGFuZGxlcl9leHByfVwiOmAsIGVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIGZ1bmN0aW9uIGV2YWx1YXRlX2V4cHJlc3Npb24oZXhwcmVzc2lvbiwgY29tcG9uZW50LCBsb2NhbHMgPSB7fSkge1xuICAgIGNvbnN0IGNvbnRleHQgPSB7XG4gICAgICAvLyBDb21wb25lbnQgcHJvcGVydGllc1xuICAgICAgZGF0YTogY29tcG9uZW50LmRhdGEsXG4gICAgICBhcmdzOiBjb21wb25lbnQuYXJncyxcbiAgICAgICQ6IGNvbXBvbmVudC4kLFxuICAgICAgLy8gQ29tcG9uZW50IG1ldGhvZHNcbiAgICAgIGVtaXQ6IGNvbXBvbmVudC5lbWl0LmJpbmQoY29tcG9uZW50KSxcbiAgICAgICRpZDogY29tcG9uZW50LiRpZC5iaW5kKGNvbXBvbmVudCksXG4gICAgICAvLyBMb2NhbHMgKGxpa2UgJGV2ZW50KVxuICAgICAgLi4ubG9jYWxzXG4gICAgfTtcbiAgICBjb25zdCBrZXlzID0gT2JqZWN0LmtleXMoY29udGV4dCk7XG4gICAgY29uc3QgdmFsdWVzID0gT2JqZWN0LnZhbHVlcyhjb250ZXh0KTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgZm4gPSBuZXcgRnVuY3Rpb24oLi4ua2V5cywgYHJldHVybiAoJHtleHByZXNzaW9ufSlgKTtcbiAgICAgIHJldHVybiBmbiguLi52YWx1ZXMpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGBJbnZhbGlkIGV4cHJlc3Npb246ICR7ZXhwcmVzc2lvbn1gLCBlcnJvcik7XG4gICAgICByZXR1cm4gdm9pZCAwO1xuICAgIH1cbiAgfVxuICBmdW5jdGlvbiBldmFsdWF0ZV9oYW5kbGVyKGV4cHJlc3Npb24sIGNvbXBvbmVudCkge1xuICAgIGlmIChleHByZXNzaW9uIGluIGNvbXBvbmVudCAmJiB0eXBlb2YgY29tcG9uZW50W2V4cHJlc3Npb25dID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgIHJldHVybiBjb21wb25lbnRbZXhwcmVzc2lvbl07XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICByZXR1cm4gbmV3IEZ1bmN0aW9uKFwiJGV2ZW50XCIsIGBcbiAgICAgIGNvbnN0IHsgZGF0YSwgYXJncywgJCwgZW1pdCwgJGlkIH0gPSB0aGlzO1xuICAgICAgJHtleHByZXNzaW9ufVxuICAgIGApLmJpbmQoY29tcG9uZW50KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcihgSW52YWxpZCBoYW5kbGVyOiAke2V4cHJlc3Npb259YCwgZXJyb3IpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIGVzY2FwZV9odG1sKHN0cikge1xuICAgIGNvbnN0IGRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIik7XG4gICAgZGl2LnRleHRDb250ZW50ID0gc3RyO1xuICAgIHJldHVybiBkaXYuaW5uZXJIVE1MO1xuICB9XG4gIGZ1bmN0aW9uIGdldEpRdWVyeSgpIHtcbiAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB3aW5kb3cuJCkge1xuICAgICAgcmV0dXJuIHdpbmRvdy4kO1xuICAgIH1cbiAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB3aW5kb3cualF1ZXJ5KSB7XG4gICAgICByZXR1cm4gd2luZG93LmpRdWVyeTtcbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKCdGQVRBTDogalF1ZXJ5IGlzIG5vdCBkZWZpbmVkLiBqUXVlcnkgbXVzdCBiZSBsb2FkZWQgYmVmb3JlIHVzaW5nIEpRSFRNTC4gQWRkIDxzY3JpcHQgc3JjPVwiaHR0cHM6Ly9jb2RlLmpxdWVyeS5jb20vanF1ZXJ5LTMuNy4xLm1pbi5qc1wiPjxcXC9zY3JpcHQ+IGJlZm9yZSBsb2FkaW5nIEpRSFRNTC4nKTtcbiAgfVxuICBmdW5jdGlvbiBnZXRKcWh0bWwoKSB7XG4gICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgd2luZG93LmpxaHRtbCkge1xuICAgICAgcmV0dXJuIHdpbmRvdy5qcWh0bWw7XG4gICAgfVxuICAgIGlmICh0eXBlb2YgZ2xvYmFsVGhpcyAhPT0gXCJ1bmRlZmluZWRcIiAmJiBnbG9iYWxUaGlzLmpxaHRtbCkge1xuICAgICAgcmV0dXJuIGdsb2JhbFRoaXMuanFodG1sO1xuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoXCJGQVRBTDogd2luZG93LmpxaHRtbCBpcyBub3QgZGVmaW5lZC4gVGhlIEpRSFRNTCBydW50aW1lIG11c3QgYmUgbG9hZGVkIGJlZm9yZSB1c2luZyBKUUhUTUwgY29tcG9uZW50cy4gRW5zdXJlIEBqcWh0bWwvY29yZSBpcyBpbXBvcnRlZCBhbmQgaW5pdGlhbGl6ZWQgYmVmb3JlIGF0dGVtcHRpbmcgdG8gdXNlIGRlYnVnIGZlYXR1cmVzLlwiKTtcbiAgfVxuICB2YXIgRGVidWdPdmVybGF5ID0gY2xhc3MgX0RlYnVnT3ZlcmxheSB7XG4gICAgY29uc3RydWN0b3Iob3B0aW9ucyA9IHt9KSB7XG4gICAgICB0aGlzLiRjb250YWluZXIgPSBudWxsO1xuICAgICAgdGhpcy4kc3RhdHVzSW5kaWNhdG9yID0gbnVsbDtcbiAgICAgIHRoaXMuJCA9IGdldEpRdWVyeSgpO1xuICAgICAgaWYgKCF0aGlzLiQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwialF1ZXJ5IGlzIHJlcXVpcmVkIGZvciBEZWJ1Z092ZXJsYXlcIik7XG4gICAgICB9XG4gICAgICB0aGlzLm9wdGlvbnMgPSB7XG4gICAgICAgIHBvc2l0aW9uOiBcImJvdHRvbVwiLFxuICAgICAgICB0aGVtZTogXCJkYXJrXCIsXG4gICAgICAgIGNvbXBhY3Q6IGZhbHNlLFxuICAgICAgICBzaG93U3RhdHVzOiB0cnVlLFxuICAgICAgICBhdXRvSGlkZTogZmFsc2UsXG4gICAgICAgIC4uLm9wdGlvbnNcbiAgICAgIH07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFN0YXRpYyBtZXRob2QgdG8gc2hvdyBkZWJ1ZyBvdmVybGF5IChzaW5nbGV0b24gcGF0dGVybilcbiAgICAgKi9cbiAgICBzdGF0aWMgc2hvdyhvcHRpb25zKSB7XG4gICAgICBpZiAoIV9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UpIHtcbiAgICAgICAgX0RlYnVnT3ZlcmxheS5pbnN0YW5jZSA9IG5ldyBfRGVidWdPdmVybGF5KG9wdGlvbnMpO1xuICAgICAgfVxuICAgICAgX0RlYnVnT3ZlcmxheS5pbnN0YW5jZS5kaXNwbGF5KCk7XG4gICAgICByZXR1cm4gX0RlYnVnT3ZlcmxheS5pbnN0YW5jZTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU3RhdGljIG1ldGhvZCB0byBoaWRlIGRlYnVnIG92ZXJsYXlcbiAgICAgKi9cbiAgICBzdGF0aWMgaGlkZSgpIHtcbiAgICAgIGlmIChfRGVidWdPdmVybGF5Lmluc3RhbmNlKSB7XG4gICAgICAgIF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UuaGlkZSgpO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBTdGF0aWMgbWV0aG9kIHRvIHRvZ2dsZSBkZWJ1ZyBvdmVybGF5IHZpc2liaWxpdHlcbiAgICAgKi9cbiAgICBzdGF0aWMgdG9nZ2xlKCkge1xuICAgICAgaWYgKF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UgJiYgX0RlYnVnT3ZlcmxheS5pbnN0YW5jZS4kY29udGFpbmVyKSB7XG4gICAgICAgIGlmIChfRGVidWdPdmVybGF5Lmluc3RhbmNlLiRjb250YWluZXIuaXMoXCI6dmlzaWJsZVwiKSkge1xuICAgICAgICAgIF9EZWJ1Z092ZXJsYXkuaGlkZSgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UuZGlzcGxheSgpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBfRGVidWdPdmVybGF5LnNob3coKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogU3RhdGljIG1ldGhvZCB0byBkZXN0cm95IGRlYnVnIG92ZXJsYXlcbiAgICAgKi9cbiAgICBzdGF0aWMgZGVzdHJveSgpIHtcbiAgICAgIGlmIChfRGVidWdPdmVybGF5Lmluc3RhbmNlKSB7XG4gICAgICAgIF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UuZGVzdHJveSgpO1xuICAgICAgICBfRGVidWdPdmVybGF5Lmluc3RhbmNlID0gbnVsbDtcbiAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogRGlzcGxheSB0aGUgZGVidWcgb3ZlcmxheVxuICAgICAqL1xuICAgIGRpc3BsYXkoKSB7XG4gICAgICBpZiAodGhpcy4kY29udGFpbmVyKSB7XG4gICAgICAgIHRoaXMuJGNvbnRhaW5lci5zaG93KCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMuY3JlYXRlT3ZlcmxheSgpO1xuICAgICAgaWYgKHRoaXMub3B0aW9ucy5zaG93U3RhdHVzKSB7XG4gICAgICAgIHRoaXMuY3JlYXRlU3RhdHVzSW5kaWNhdG9yKCk7XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEhpZGUgdGhlIGRlYnVnIG92ZXJsYXlcbiAgICAgKi9cbiAgICBoaWRlKCkge1xuICAgICAgaWYgKHRoaXMuJGNvbnRhaW5lcikge1xuICAgICAgICB0aGlzLiRjb250YWluZXIuaGlkZSgpO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuJHN0YXR1c0luZGljYXRvcikge1xuICAgICAgICB0aGlzLiRzdGF0dXNJbmRpY2F0b3IuaGlkZSgpO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZW1vdmUgdGhlIGRlYnVnIG92ZXJsYXkgY29tcGxldGVseVxuICAgICAqL1xuICAgIGRlc3Ryb3koKSB7XG4gICAgICBpZiAodGhpcy4kY29udGFpbmVyKSB7XG4gICAgICAgIHRoaXMuJGNvbnRhaW5lci5yZW1vdmUoKTtcbiAgICAgICAgdGhpcy4kY29udGFpbmVyID0gbnVsbDtcbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLiRzdGF0dXNJbmRpY2F0b3IpIHtcbiAgICAgICAgdGhpcy4kc3RhdHVzSW5kaWNhdG9yLnJlbW92ZSgpO1xuICAgICAgICB0aGlzLiRzdGF0dXNJbmRpY2F0b3IgPSBudWxsO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBVcGRhdGUgdGhlIHN0YXR1cyBpbmRpY2F0b3JcbiAgICAgKi9cbiAgICB1cGRhdGVTdGF0dXMobW9kZSkge1xuICAgICAgaWYgKCF0aGlzLiRzdGF0dXNJbmRpY2F0b3IpXG4gICAgICAgIHJldHVybjtcbiAgICAgIHRoaXMuJHN0YXR1c0luZGljYXRvci50ZXh0KFwiRGVidWc6IFwiICsgbW9kZSk7XG4gICAgICB0aGlzLiRzdGF0dXNJbmRpY2F0b3IuYXR0cihcImNsYXNzXCIsIFwianFodG1sLWRlYnVnLXN0YXR1c1wiICsgKG1vZGUgIT09IFwiT2ZmXCIgPyBcIiBhY3RpdmVcIiA6IFwiXCIpKTtcbiAgICB9XG4gICAgY3JlYXRlT3ZlcmxheSgpIHtcbiAgICAgIHRoaXMuYWRkU3R5bGVzKCk7XG4gICAgICB0aGlzLiRjb250YWluZXIgPSB0aGlzLiQoXCI8ZGl2PlwiKS5hZGRDbGFzcyhganFodG1sLWRlYnVnLW92ZXJsYXkgJHt0aGlzLm9wdGlvbnMudGhlbWV9ICR7dGhpcy5vcHRpb25zLnBvc2l0aW9ufWApO1xuICAgICAgY29uc3QgJGNvbnRlbnQgPSB0aGlzLiQoXCI8ZGl2PlwiKS5hZGRDbGFzcyhcImpxaHRtbC1kZWJ1Zy1jb250ZW50XCIpO1xuICAgICAgY29uc3QgJGNvbnRyb2xzID0gdGhpcy4kKFwiPGRpdj5cIikuYWRkQ2xhc3MoXCJqcWh0bWwtZGVidWctY29udHJvbHNcIik7XG4gICAgICBjb25zdCAkdGl0bGUgPSB0aGlzLiQoXCI8c3Bhbj5cIikuYWRkQ2xhc3MoXCJqcWh0bWwtZGVidWctdGl0bGVcIikuaHRtbChcIjxzdHJvbmc+XFx1ezFGNDFCfSBKUUhUTUwgRGVidWc6PC9zdHJvbmc+XCIpO1xuICAgICAgJGNvbnRyb2xzLmFwcGVuZCgkdGl0bGUpO1xuICAgICAgY29uc3QgYnV0dG9ucyA9IFtcbiAgICAgICAgeyB0ZXh0OiBcIlNsb3cgTW90aW9uICsgRmxhc2hcIiwgYWN0aW9uOiBcImVuYWJsZVNsb3dNb3Rpb25EZWJ1Z1wiLCBjbGFzczogXCJzdWNjZXNzXCIgfSxcbiAgICAgICAgeyB0ZXh0OiBcIkJhc2ljIERlYnVnXCIsIGFjdGlvbjogXCJlbmFibGVCYXNpY0RlYnVnXCIsIGNsYXNzOiBcIlwiIH0sXG4gICAgICAgIHsgdGV4dDogXCJGdWxsIERlYnVnXCIsIGFjdGlvbjogXCJlbmFibGVGdWxsRGVidWdcIiwgY2xhc3M6IFwiXCIgfSxcbiAgICAgICAgeyB0ZXh0OiBcIlNlcXVlbnRpYWxcIiwgYWN0aW9uOiBcImVuYWJsZVNlcXVlbnRpYWxNb2RlXCIsIGNsYXNzOiBcIlwiIH0sXG4gICAgICAgIHsgdGV4dDogXCJDbGVhciBEZWJ1Z1wiLCBhY3Rpb246IFwiY2xlYXJBbGxEZWJ1Z1wiLCBjbGFzczogXCJkYW5nZXJcIiB9LFxuICAgICAgICB7IHRleHQ6IFwiU2V0dGluZ3NcIiwgYWN0aW9uOiBcInNob3dEZWJ1Z0luZm9cIiwgY2xhc3M6IFwiXCIgfVxuICAgICAgXTtcbiAgICAgIGJ1dHRvbnMuZm9yRWFjaCgoYnRuKSA9PiB7XG4gICAgICAgIGNvbnN0ICRidXR0b24gPSB0aGlzLiQoXCI8YnV0dG9uPlwiKS50ZXh0KGJ0bi50ZXh0KS5hZGRDbGFzcyhcImpxaHRtbC1kZWJ1Zy1idG5cIiArIChidG4uY2xhc3MgPyBgICR7YnRuLmNsYXNzfWAgOiBcIlwiKSkub24oXCJjbGlja1wiLCAoKSA9PiB0aGlzLmV4ZWN1dGVBY3Rpb24oYnRuLmFjdGlvbikpO1xuICAgICAgICAkY29udHJvbHMuYXBwZW5kKCRidXR0b24pO1xuICAgICAgfSk7XG4gICAgICBjb25zdCAkdG9nZ2xlQnRuID0gdGhpcy4kKFwiPGJ1dHRvbj5cIikudGV4dCh0aGlzLm9wdGlvbnMuY29tcGFjdCA/IFwiXFx1MjVCQ1wiIDogXCJcXHUyNUIyXCIpLmFkZENsYXNzKFwianFodG1sLWRlYnVnLXRvZ2dsZVwiKS5vbihcImNsaWNrXCIsICgpID0+IHRoaXMudG9nZ2xlKCkpO1xuICAgICAgJGNvbnRyb2xzLmFwcGVuZCgkdG9nZ2xlQnRuKTtcbiAgICAgICRjb250ZW50LmFwcGVuZCgkY29udHJvbHMpO1xuICAgICAgdGhpcy4kY29udGFpbmVyLmFwcGVuZCgkY29udGVudCk7XG4gICAgICB0aGlzLiQoXCJib2R5XCIpLmFwcGVuZCh0aGlzLiRjb250YWluZXIpO1xuICAgIH1cbiAgICBjcmVhdGVTdGF0dXNJbmRpY2F0b3IoKSB7XG4gICAgICB0aGlzLiRzdGF0dXNJbmRpY2F0b3IgPSB0aGlzLiQoXCI8ZGl2PlwiKS5hZGRDbGFzcyhcImpxaHRtbC1kZWJ1Zy1zdGF0dXNcIikudGV4dChcIkRlYnVnOiBPZmZcIikuY3NzKHtcbiAgICAgICAgcG9zaXRpb246IFwiZml4ZWRcIixcbiAgICAgICAgdG9wOiBcIjEwcHhcIixcbiAgICAgICAgcmlnaHQ6IFwiMTBweFwiLFxuICAgICAgICBiYWNrZ3JvdW5kOiBcIiMyYzNlNTBcIixcbiAgICAgICAgY29sb3I6IFwid2hpdGVcIixcbiAgICAgICAgcGFkZGluZzogXCI1cHggMTBweFwiLFxuICAgICAgICBib3JkZXJSYWRpdXM6IFwiNHB4XCIsXG4gICAgICAgIGZvbnRTaXplOiBcIjAuNzVyZW1cIixcbiAgICAgICAgekluZGV4OiBcIjEwMDAxXCIsXG4gICAgICAgIG9wYWNpdHk6IFwiMC44XCIsXG4gICAgICAgIGZvbnRGYW1pbHk6IFwibW9ub3NwYWNlXCJcbiAgICAgIH0pO1xuICAgICAgdGhpcy4kKFwiYm9keVwiKS5hcHBlbmQodGhpcy4kc3RhdHVzSW5kaWNhdG9yKTtcbiAgICB9XG4gICAgYWRkU3R5bGVzKCkge1xuICAgICAgaWYgKHRoaXMuJChcIiNqcWh0bWwtZGVidWctc3R5bGVzXCIpLmxlbmd0aCA+IDApXG4gICAgICAgIHJldHVybjtcbiAgICAgIGNvbnN0ICRzdHlsZSA9IHRoaXMuJChcIjxzdHlsZT5cIikuYXR0cihcImlkXCIsIFwianFodG1sLWRlYnVnLXN0eWxlc1wiKS50ZXh0KCcuanFodG1sLWRlYnVnLW92ZXJsYXkge3Bvc2l0aW9uOiBmaXhlZDtsZWZ0OiAwO3JpZ2h0OiAwO3otaW5kZXg6IDEwMDAwO2ZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsIFwiU2Vnb2UgVUlcIiwgUm9ib3RvLCBtb25vc3BhY2U7Zm9udC1zaXplOiAwLjhyZW07Ym94LXNoYWRvdzogMCAycHggMTBweCByZ2JhKDAsMCwwLDAuMik7fS5qcWh0bWwtZGVidWctb3ZlcmxheS50b3Age3RvcDogMDt9LmpxaHRtbC1kZWJ1Zy1vdmVybGF5LmJvdHRvbSB7Ym90dG9tOiAwO30uanFodG1sLWRlYnVnLW92ZXJsYXkuZGFyayB7YmFja2dyb3VuZDogIzM0NDk1ZTtjb2xvcjogI2VjZjBmMTt9LmpxaHRtbC1kZWJ1Zy1vdmVybGF5LmxpZ2h0IHtiYWNrZ3JvdW5kOiAjZjhmOWZhO2NvbG9yOiAjMzMzO2JvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZGVlMmU2O30uanFodG1sLWRlYnVnLWNvbnRlbnQge3BhZGRpbmc6IDAuNXJlbSAxcmVtO30uanFodG1sLWRlYnVnLWNvbnRyb2xzIHtkaXNwbGF5OiBmbGV4O2ZsZXgtd3JhcDogd3JhcDtnYXA6IDhweDthbGlnbi1pdGVtczogY2VudGVyO30uanFodG1sLWRlYnVnLXRpdGxlIHttYXJnaW4tcmlnaHQ6IDEwcHg7Zm9udC13ZWlnaHQ6IGJvbGQ7fS5qcWh0bWwtZGVidWctYnRuIHtwYWRkaW5nOiA0cHggOHB4O2JvcmRlcjogbm9uZTtib3JkZXItcmFkaXVzOiAzcHg7YmFja2dyb3VuZDogIzM0OThkYjtjb2xvcjogd2hpdGU7Y3Vyc29yOiBwb2ludGVyO2ZvbnQtc2l6ZTogMC43NXJlbTt0cmFuc2l0aW9uOiBiYWNrZ3JvdW5kIDAuMnM7fS5qcWh0bWwtZGVidWctYnRuOmhvdmVyIHtiYWNrZ3JvdW5kOiAjMjk4MGI5O30uanFodG1sLWRlYnVnLWJ0bi5zdWNjZXNzIHtiYWNrZ3JvdW5kOiAjMjdhZTYwO30uanFodG1sLWRlYnVnLWJ0bi5zdWNjZXNzOmhvdmVyIHtiYWNrZ3JvdW5kOiAjMjI5OTU0O30uanFodG1sLWRlYnVnLWJ0bi5kYW5nZXIge2JhY2tncm91bmQ6ICNlNzRjM2M7fS5qcWh0bWwtZGVidWctYnRuLmRhbmdlcjpob3ZlciB7YmFja2dyb3VuZDogI2MwMzkyYjt9LmpxaHRtbC1kZWJ1Zy10b2dnbGUge3BhZGRpbmc6IDRweCA4cHg7Ym9yZGVyOiBub25lO2JvcmRlci1yYWRpdXM6IDNweDtiYWNrZ3JvdW5kOiAjN2Y4YzhkO2NvbG9yOiB3aGl0ZTtjdXJzb3I6IHBvaW50ZXI7Zm9udC1zaXplOiAwLjc1cmVtO21hcmdpbi1sZWZ0OiBhdXRvO30uanFodG1sLWRlYnVnLXRvZ2dsZTpob3ZlciB7YmFja2dyb3VuZDogIzZjN2I3ZDt9LmpxaHRtbC1kZWJ1Zy1zdGF0dXMuYWN0aXZlIHtiYWNrZ3JvdW5kOiAjMjdhZTYwICFpbXBvcnRhbnQ7fUBtZWRpYSAobWF4LXdpZHRoOiA3NjhweCkgey5qcWh0bWwtZGVidWctY29udHJvbHMge2ZsZXgtZGlyZWN0aW9uOiBjb2x1bW47YWxpZ24taXRlbXM6IGZsZXgtc3RhcnQ7fS5qcWh0bWwtZGVidWctdGl0bGUge21hcmdpbi1ib3R0b206IDVweDt9fScpO1xuICAgICAgdGhpcy4kKFwiaGVhZFwiKS5hcHBlbmQoJHN0eWxlKTtcbiAgICB9XG4gICAgdG9nZ2xlKCkge1xuICAgICAgdGhpcy5vcHRpb25zLmNvbXBhY3QgPSAhdGhpcy5vcHRpb25zLmNvbXBhY3Q7XG4gICAgICBjb25zdCAkdG9nZ2xlQnRuID0gdGhpcy4kY29udGFpbmVyLmZpbmQoXCIuanFodG1sLWRlYnVnLXRvZ2dsZVwiKTtcbiAgICAgICR0b2dnbGVCdG4udGV4dCh0aGlzLm9wdGlvbnMuY29tcGFjdCA/IFwiXFx1MjVCQ1wiIDogXCJcXHUyNUIyXCIpO1xuICAgICAgY29uc3QgJGJ1dHRvbnMgPSB0aGlzLiRjb250YWluZXIuZmluZChcIi5qcWh0bWwtZGVidWctYnRuXCIpO1xuICAgICAgaWYgKHRoaXMub3B0aW9ucy5jb21wYWN0KSB7XG4gICAgICAgICRidXR0b25zLmhpZGUoKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgICRidXR0b25zLnNob3coKTtcbiAgICAgIH1cbiAgICB9XG4gICAgZXhlY3V0ZUFjdGlvbihhY3Rpb24pIHtcbiAgICAgIGNvbnN0IGpxaHRtbDIgPSBnZXRKcWh0bWwoKTtcbiAgICAgIGlmICghanFodG1sMikge1xuICAgICAgICBjb25zb2xlLndhcm4oXCJKUUhUTUwgbm90IGF2YWlsYWJsZSAtIG1ha2Ugc3VyZSBpdCdzIGxvYWRlZCBhbmQgZXhwb3NlZCBnbG9iYWxseVwiKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgc3dpdGNoIChhY3Rpb24pIHtcbiAgICAgICAgY2FzZSBcImVuYWJsZVNsb3dNb3Rpb25EZWJ1Z1wiOlxuICAgICAgICAgIGpxaHRtbDIuc2V0RGVidWdTZXR0aW5ncyh7XG4gICAgICAgICAgICBsb2dGdWxsTGlmZWN5Y2xlOiB0cnVlLFxuICAgICAgICAgICAgc2VxdWVudGlhbFByb2Nlc3Npbmc6IHRydWUsXG4gICAgICAgICAgICBkZWxheUFmdGVyQ29tcG9uZW50OiAxNTAsXG4gICAgICAgICAgICBkZWxheUFmdGVyUmVuZGVyOiAyMDAsXG4gICAgICAgICAgICBkZWxheUFmdGVyUmVyZW5kZXI6IDI1MCxcbiAgICAgICAgICAgIGZsYXNoQ29tcG9uZW50czogdHJ1ZSxcbiAgICAgICAgICAgIGZsYXNoRHVyYXRpb246IDgwMCxcbiAgICAgICAgICAgIGZsYXNoQ29sb3JzOiB7XG4gICAgICAgICAgICAgIGNyZWF0ZTogXCIjMzQ5OGRiXCIsXG4gICAgICAgICAgICAgIHJlbmRlcjogXCIjMjdhZTYwXCIsXG4gICAgICAgICAgICAgIHJlYWR5OiBcIiM5YjU5YjZcIlxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHByb2ZpbGVQZXJmb3JtYW5jZTogdHJ1ZSxcbiAgICAgICAgICAgIGhpZ2hsaWdodFNsb3dSZW5kZXJzOiAzMCxcbiAgICAgICAgICAgIGxvZ0Rpc3BhdGNoOiB0cnVlXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgdGhpcy51cGRhdGVTdGF0dXMoXCJTbG93IE1vdGlvblwiKTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhcIlxcdXsxRjQxQn0gU2xvdyBNb3Rpb24gRGVidWcgTW9kZSBFbmFibGVkXCIpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZW5hYmxlQmFzaWNEZWJ1Z1wiOlxuICAgICAgICAgIGpxaHRtbDIuZW5hYmxlRGVidWdNb2RlKFwiYmFzaWNcIik7XG4gICAgICAgICAgdGhpcy51cGRhdGVTdGF0dXMoXCJCYXNpY1wiKTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhcIlxcdXsxRjQxQn0gQmFzaWMgRGVidWcgTW9kZSBFbmFibGVkXCIpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZW5hYmxlRnVsbERlYnVnXCI6XG4gICAgICAgICAganFodG1sMi5lbmFibGVEZWJ1Z01vZGUoXCJmdWxsXCIpO1xuICAgICAgICAgIHRoaXMudXBkYXRlU3RhdHVzKFwiRnVsbFwiKTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhcIlxcdXsxRjQxQn0gRnVsbCBEZWJ1ZyBNb2RlIEVuYWJsZWRcIik7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJlbmFibGVTZXF1ZW50aWFsTW9kZVwiOlxuICAgICAgICAgIGpxaHRtbDIuc2V0RGVidWdTZXR0aW5ncyh7XG4gICAgICAgICAgICBsb2dDcmVhdGlvblJlYWR5OiB0cnVlLFxuICAgICAgICAgICAgc2VxdWVudGlhbFByb2Nlc3Npbmc6IHRydWUsXG4gICAgICAgICAgICBmbGFzaENvbXBvbmVudHM6IHRydWUsXG4gICAgICAgICAgICBwcm9maWxlUGVyZm9ybWFuY2U6IHRydWVcbiAgICAgICAgICB9KTtcbiAgICAgICAgICB0aGlzLnVwZGF0ZVN0YXR1cyhcIlNlcXVlbnRpYWxcIik7XG4gICAgICAgICAgY29uc29sZS5sb2coXCJcXHV7MUY0MUJ9IFNlcXVlbnRpYWwgUHJvY2Vzc2luZyBNb2RlIEVuYWJsZWRcIik7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJjbGVhckFsbERlYnVnXCI6XG4gICAgICAgICAganFodG1sMi5jbGVhckRlYnVnU2V0dGluZ3MoKTtcbiAgICAgICAgICB0aGlzLnVwZGF0ZVN0YXR1cyhcIk9mZlwiKTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhcIlxcdXsxRjQxQn0gQWxsIERlYnVnIE1vZGVzIERpc2FibGVkXCIpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwic2hvd0RlYnVnSW5mb1wiOlxuICAgICAgICAgIGNvbnN0IHNldHRpbmdzID0gSlNPTi5zdHJpbmdpZnkoanFodG1sMi5kZWJ1ZywgbnVsbCwgMik7XG4gICAgICAgICAgY29uc29sZS5sb2coXCJcXHV7MUY0MUJ9IEN1cnJlbnQgRGVidWcgU2V0dGluZ3M6XCIsIHNldHRpbmdzKTtcbiAgICAgICAgICBhbGVydChcIkRlYnVnIHNldHRpbmdzIGxvZ2dlZCB0byBjb25zb2xlOlxcblxcblwiICsgKE9iamVjdC5rZXlzKGpxaHRtbDIuZGVidWcpLmxlbmd0aCA+IDAgPyBzZXR0aW5ncyA6IFwiTm8gZGVidWcgc2V0dGluZ3MgYWN0aXZlXCIpKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH07XG4gIERlYnVnT3ZlcmxheS5pbnN0YW5jZSA9IG51bGw7XG4gIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgY29uc3QgdXJsUGFyYW1zID0gbmV3IFVSTFNlYXJjaFBhcmFtcyh3aW5kb3cubG9jYXRpb24uc2VhcmNoKTtcbiAgICBpZiAodXJsUGFyYW1zLmdldChcImRlYnVnXCIpID09PSBcInRydWVcIiB8fCB1cmxQYXJhbXMuZ2V0KFwianFodG1sLWRlYnVnXCIpID09PSBcInRydWVcIikge1xuICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcIkRPTUNvbnRlbnRMb2FkZWRcIiwgKCkgPT4ge1xuICAgICAgICBEZWJ1Z092ZXJsYXkuc2hvdygpO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIGluaXRfanF1ZXJ5X3BsdWdpbihqUXVlcnkpIHtcbiAgICBpZiAoIWpRdWVyeSB8fCAhalF1ZXJ5LmZuKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJqUXVlcnkgaXMgcmVxdWlyZWQgZm9yIEpRSFRNTC4gUGxlYXNlIGVuc3VyZSBqUXVlcnkgaXMgbG9hZGVkIGJlZm9yZSBpbml0aWFsaXppbmcgSlFIVE1MLlwiKTtcbiAgICB9XG4gICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgd2luZG93LiQgIT09IGpRdWVyeSAmJiAhalF1ZXJ5Ll9fanFodG1sX2NoZWNrZWQpIHtcbiAgICAgIGRldldhcm4oJ2pRdWVyeSBpbnN0YW5jZSBhcHBlYXJzIHRvIGJlIGJ1bmRsZWQgd2l0aCB3ZWJwYWNrL21vZHVsZXMgcmF0aGVyIHRoYW4gbG9hZGVkIGdsb2JhbGx5LlxcbkZvciBiZXN0IGNvbXBhdGliaWxpdHksIGl0IGlzIHJlY29tbWVuZGVkIHRvOlxcbjEuIEluY2x1ZGUgalF1ZXJ5IHZpYSA8c2NyaXB0PiB0YWcgZnJvbSBhIENETiAoVU1EIGZvcm1hdClcXG4yLiBDb25maWd1cmUgd2VicGFjayB3aXRoOiBleHRlcm5hbHM6IHsganF1ZXJ5OiBcIiRcIiB9XFxuMy4gUmVtb3ZlIGpxdWVyeSBmcm9tIHBhY2thZ2UuanNvbiBkZXBlbmRlbmNpZXNcXG5cXG5UbyBzdXBwcmVzcyB0aGlzIHdhcm5pbmcsIHNldDogd2luZG93LkpRSFRNTF9TVVBQUkVTU19XQVJOSU5HUyA9IHRydWUnKTtcbiAgICAgIGpRdWVyeS5fX2pxaHRtbF9jaGVja2VkID0gdHJ1ZTtcbiAgICB9XG4gICAgY29uc3QgX2pxaHRtbF9vcmlnaW5hbF9qcXVlcnkgPSBqUXVlcnk7XG4gICAgY29uc3QgSlF1ZXJ5V2l0aENvbXBvbmVudFN1cHBvcnQgPSBmdW5jdGlvbihzZWxlY3RvciwgY29udGV4dCkge1xuICAgICAgaWYgKHNlbGVjdG9yICYmIHR5cGVvZiBzZWxlY3RvciA9PT0gXCJvYmplY3RcIiAmJiBzZWxlY3Rvci4kICYmIHR5cGVvZiBzZWxlY3Rvci4kaWQgPT09IFwiZnVuY3Rpb25cIiAmJiB0eXBlb2Ygc2VsZWN0b3IuaWQgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICByZXR1cm4gc2VsZWN0b3IuJDtcbiAgICAgIH1cbiAgICAgIHJldHVybiBuZXcgX2pxaHRtbF9vcmlnaW5hbF9qcXVlcnkoc2VsZWN0b3IsIGNvbnRleHQpO1xuICAgIH07XG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKEpRdWVyeVdpdGhDb21wb25lbnRTdXBwb3J0LCBfanFodG1sX29yaWdpbmFsX2pxdWVyeSk7XG4gICAgZm9yIChjb25zdCBrZXkgaW4gX2pxaHRtbF9vcmlnaW5hbF9qcXVlcnkpIHtcbiAgICAgIGlmIChfanFodG1sX29yaWdpbmFsX2pxdWVyeS5oYXNPd25Qcm9wZXJ0eShrZXkpKSB7XG4gICAgICAgIEpRdWVyeVdpdGhDb21wb25lbnRTdXBwb3J0W2tleV0gPSBfanFodG1sX29yaWdpbmFsX2pxdWVyeVtrZXldO1xuICAgICAgfVxuICAgIH1cbiAgICBKUXVlcnlXaXRoQ29tcG9uZW50U3VwcG9ydC5wcm90b3R5cGUgPSBfanFodG1sX29yaWdpbmFsX2pxdWVyeS5wcm90b3R5cGU7XG4gICAgSlF1ZXJ5V2l0aENvbXBvbmVudFN1cHBvcnQuZm4gPSBfanFodG1sX29yaWdpbmFsX2pxdWVyeS5mbjtcbiAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIikge1xuICAgICAgd2luZG93LmpRdWVyeSA9IEpRdWVyeVdpdGhDb21wb25lbnRTdXBwb3J0O1xuICAgICAgd2luZG93LiQgPSBKUXVlcnlXaXRoQ29tcG9uZW50U3VwcG9ydDtcbiAgICB9XG4gICAgalF1ZXJ5ID0gSlF1ZXJ5V2l0aENvbXBvbmVudFN1cHBvcnQ7XG4gICAgY29uc3Qgb3JpZ2luYWxWYWwgPSBqUXVlcnkuZm4udmFsO1xuICAgIGpRdWVyeS5mbi52YWwgPSBmdW5jdGlvbih2YWx1ZSkge1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgY29uc3QgZmlyc3RFbCA9IHRoaXMuZmlyc3QoKTtcbiAgICAgICAgaWYgKGZpcnN0RWwubGVuZ3RoID09PSAwKVxuICAgICAgICAgIHJldHVybiB2b2lkIDA7XG4gICAgICAgIGNvbnN0IGNvbXBvbmVudCA9IGZpcnN0RWwuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgIGNvbnN0IHRhZ05hbWUgPSBmaXJzdEVsLnByb3AoXCJ0YWdOYW1lXCIpO1xuICAgICAgICBpZiAoY29tcG9uZW50ICYmIHR5cGVvZiBjb21wb25lbnQudmFsID09PSBcImZ1bmN0aW9uXCIgJiYgdGFnTmFtZSAhPT0gXCJJTlBVVFwiICYmIHRhZ05hbWUgIT09IFwiVEVYVEFSRUFcIikge1xuICAgICAgICAgIHJldHVybiBjb21wb25lbnQudmFsKCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsVmFsLmNhbGwodGhpcyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgICAgY29uc3QgJGVsID0galF1ZXJ5KHRoaXMpO1xuICAgICAgICAgIGNvbnN0IGNvbXBvbmVudCA9ICRlbC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgICBjb25zdCB0YWdOYW1lID0gJGVsLnByb3AoXCJ0YWdOYW1lXCIpO1xuICAgICAgICAgIGlmIChjb21wb25lbnQgJiYgdHlwZW9mIGNvbXBvbmVudC52YWwgPT09IFwiZnVuY3Rpb25cIiAmJiB0YWdOYW1lICE9PSBcIklOUFVUXCIgJiYgdGFnTmFtZSAhPT0gXCJURVhUQVJFQVwiKSB7XG4gICAgICAgICAgICBjb21wb25lbnQudmFsKHZhbHVlKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgb3JpZ2luYWxWYWwuY2FsbCgkZWwsIHZhbHVlKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgIH1cbiAgICB9O1xuICAgIGpRdWVyeS5mbi5jb21wb25lbnQgPSBmdW5jdGlvbihjb21wb25lbnRPck5hbWUsIGFyZ3MgPSB7fSkge1xuICAgICAgY29uc3QgZWxlbWVudCA9IHRoaXMuZmlyc3QgPyB0aGlzLmZpcnN0KCkgOiB0aGlzO1xuICAgICAgaWYgKCFjb21wb25lbnRPck5hbWUpIHtcbiAgICAgICAgaWYgKGVsZW1lbnQubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgY29tcCA9IGVsZW1lbnQuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgIHJldHVybiBjb21wIHx8IG51bGw7XG4gICAgICB9XG4gICAgICBjb25zdCBleGlzdGluZ0NvbXBvbmVudCA9IGVsZW1lbnQuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICBpZiAoZXhpc3RpbmdDb21wb25lbnQpIHtcbiAgICAgICAgcmV0dXJuIGV4aXN0aW5nQ29tcG9uZW50O1xuICAgICAgfVxuICAgICAgbGV0IENvbXBvbmVudENsYXNzO1xuICAgICAgbGV0IGNvbXBvbmVudE5hbWU7XG4gICAgICBpZiAodHlwZW9mIGNvbXBvbmVudE9yTmFtZSA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgICBjb21wb25lbnROYW1lID0gY29tcG9uZW50T3JOYW1lO1xuICAgICAgICBjb25zdCBmb3VuZCA9IGdldF9jb21wb25lbnRfY2xhc3MoY29tcG9uZW50T3JOYW1lKTtcbiAgICAgICAgYXJncyA9IHsgLi4uYXJncywgX2NvbXBvbmVudF9uYW1lOiBjb21wb25lbnROYW1lIH07XG4gICAgICAgIGlmICghZm91bmQpIHtcbiAgICAgICAgICBDb21wb25lbnRDbGFzcyA9IEpxaHRtbF9Db21wb25lbnQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgQ29tcG9uZW50Q2xhc3MgPSBmb3VuZDtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgQ29tcG9uZW50Q2xhc3MgPSBjb21wb25lbnRPck5hbWU7XG4gICAgICB9XG4gICAgICBsZXQgdGFyZ2V0RWxlbWVudCA9IGVsZW1lbnQ7XG4gICAgICBpZiAoY29tcG9uZW50TmFtZSkge1xuICAgICAgICBjb25zdCB0ZW1wbGF0ZSA9IGdldF90ZW1wbGF0ZShjb21wb25lbnROYW1lKTtcbiAgICAgICAgY29uc3QgZXhwZWN0ZWRUYWcgPSBhcmdzLl90YWcgfHwgdGVtcGxhdGUudGFnIHx8IFwiZGl2XCI7XG4gICAgICAgIGNvbnN0IGN1cnJlbnRUYWcgPSBlbGVtZW50LnByb3AoXCJ0YWdOYW1lXCIpLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGlmIChjdXJyZW50VGFnICE9PSBleHBlY3RlZFRhZy50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgICAgaWYgKGFyZ3MuX2lubmVyX2h0bWwpIHtcbiAgICAgICAgICAgIGNvbnN0IG5ld0VsZW1lbnQgPSBqUXVlcnkoYDwke2V4cGVjdGVkVGFnfT48LyR7ZXhwZWN0ZWRUYWd9PmApO1xuICAgICAgICAgICAgY29uc3Qgb2xkRWwgPSBlbGVtZW50WzBdO1xuICAgICAgICAgICAgaWYgKG9sZEVsICYmIG9sZEVsLmF0dHJpYnV0ZXMpIHtcbiAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBvbGRFbC5hdHRyaWJ1dGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgYXR0ciA9IG9sZEVsLmF0dHJpYnV0ZXNbaV07XG4gICAgICAgICAgICAgICAgbmV3RWxlbWVudC5hdHRyKGF0dHIubmFtZSwgYXR0ci52YWx1ZSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG5ld0VsZW1lbnQuaHRtbChlbGVtZW50Lmh0bWwoKSk7XG4gICAgICAgICAgICBlbGVtZW50LnJlcGxhY2VXaXRoKG5ld0VsZW1lbnQpO1xuICAgICAgICAgICAgdGFyZ2V0RWxlbWVudCA9IG5ld0VsZW1lbnQ7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gQ29tcG9uZW50ICcke2NvbXBvbmVudE5hbWV9JyBleHBlY3RzIHRhZyAnPCR7ZXhwZWN0ZWRUYWd9PicgYnV0IGVsZW1lbnQgaXMgJzwke2N1cnJlbnRUYWd9PicuIEVsZW1lbnQgdGFnIHdpbGwgbm90IGJlIGNoYW5nZWQuIENvbnNpZGVyIHVzaW5nIHRoZSBjb3JyZWN0IHRhZy5gKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGNvbnN0IGNvbXBvbmVudCA9IG5ldyBDb21wb25lbnRDbGFzcyh0YXJnZXRFbGVtZW50LCBhcmdzKTtcbiAgICAgIGNvbXBvbmVudC5ib290KCk7XG4gICAgICBhcHBseURlYnVnRGVsYXkoXCJjb21wb25lbnRcIik7XG4gICAgICByZXR1cm4gY29tcG9uZW50O1xuICAgIH07XG4gICAgY29uc3QgX2pxaHRtbF9qcXVlcnlfb3ZlcnJpZGVzID0ge307XG4gICAgY29uc3QgZG9tX2luc2VydGlvbl9tZXRob2RzID0gW1wiYXBwZW5kXCIsIFwicHJlcGVuZFwiLCBcImJlZm9yZVwiLCBcImFmdGVyXCIsIFwicmVwbGFjZVdpdGhcIl07XG4gICAgZm9yIChjb25zdCBmbm5hbWUgb2YgZG9tX2luc2VydGlvbl9tZXRob2RzKSB7XG4gICAgICBfanFodG1sX2pxdWVyeV9vdmVycmlkZXNbZm5uYW1lXSA9IGpRdWVyeS5mbltmbm5hbWVdO1xuICAgICAgalF1ZXJ5LmZuW2ZubmFtZV0gPSBmdW5jdGlvbiguLi5hcmdzKSB7XG4gICAgICAgIGNvbnN0IHJlc29sdmVkQXJncyA9IGFyZ3MubWFwKChhcmcpID0+IHtcbiAgICAgICAgICBpZiAoYXJnICYmIHR5cGVvZiBhcmcgPT09IFwib2JqZWN0XCIgJiYgYXJnIGluc3RhbmNlb2YgSnFodG1sX0NvbXBvbmVudCkge1xuICAgICAgICAgICAgcmV0dXJuIGFyZy4kO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gYXJnO1xuICAgICAgICB9KTtcbiAgICAgICAgY29uc3QgJGVsZW1lbnRzID0gcmVzb2x2ZWRBcmdzLmZpbHRlcigoYXJnKSA9PiBhcmcgaW5zdGFuY2VvZiBqUXVlcnkpO1xuICAgICAgICBjb25zdCByZXQgPSBfanFodG1sX2pxdWVyeV9vdmVycmlkZXNbZm5uYW1lXS5hcHBseSh0aGlzLCByZXNvbHZlZEFyZ3MpO1xuICAgICAgICBmb3IgKGNvbnN0ICRlIG9mICRlbGVtZW50cykge1xuICAgICAgICAgIGlmICgkZS5jbG9zZXN0KFwiaHRtbFwiKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAkZS5maW5kKFwiLkpxaHRtbF9Db21wb25lbnRcIikuYWRkQmFjayhcIi5KcWh0bWxfQ29tcG9uZW50XCIpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICAgIGNvbnN0ICRjb21wID0galF1ZXJ5KHRoaXMpO1xuICAgICAgICAgICAgICBjb25zdCBjb21wb25lbnQgPSAkY29tcC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgICAgICAgaWYgKGNvbXBvbmVudCAmJiAhY29tcG9uZW50Ll9yZWFkeV9zdGF0ZSkge1xuICAgICAgICAgICAgICAgIGNvbXBvbmVudC5ib290KCk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmV0O1xuICAgICAgfTtcbiAgICB9XG4gICAgalF1ZXJ5LmZuLnNoYWxsb3dGaW5kID0gZnVuY3Rpb24oc2VsZWN0b3IpIHtcbiAgICAgIGNvbnN0IHJlc3VsdHMgPSBbXTtcbiAgICAgIHRoaXMuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgY29uc3QgdHJhdmVyc2UgPSAocGFyZW50KSA9PiB7XG4gICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwYXJlbnQuY2hpbGRyZW4ubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGNvbnN0IGNoaWxkID0gcGFyZW50LmNoaWxkcmVuW2ldO1xuICAgICAgICAgICAgaWYgKGpRdWVyeShjaGlsZCkuaXMoc2VsZWN0b3IpKSB7XG4gICAgICAgICAgICAgIHJlc3VsdHMucHVzaChjaGlsZCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICB0cmF2ZXJzZShjaGlsZCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICB0cmF2ZXJzZSh0aGlzKTtcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIGpRdWVyeShyZXN1bHRzKTtcbiAgICB9O1xuICAgIGNvbnN0IG9yaWdpbmFsRW1wdHkgPSBqUXVlcnkuZm4uZW1wdHk7XG4gICAgY29uc3Qgb3JpZ2luYWxIdG1sID0galF1ZXJ5LmZuLmh0bWw7XG4gICAgY29uc3Qgb3JpZ2luYWxUZXh0ID0galF1ZXJ5LmZuLnRleHQ7XG4gICAgalF1ZXJ5LmZuLmVtcHR5ID0gZnVuY3Rpb24oKSB7XG4gICAgICByZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICBqUXVlcnkodGhpcykuZmluZChcIi5KcWh0bWxfQ29tcG9uZW50XCIpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgICAgY29uc3QgY29tcG9uZW50ID0galF1ZXJ5KHRoaXMpLmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICAgIGlmIChjb21wb25lbnQgJiYgIWNvbXBvbmVudC5fc3RvcHBlZCkge1xuICAgICAgICAgICAgY29tcG9uZW50Ll9zdG9wKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgb3JpZ2luYWxFbXB0eS5jYWxsKGpRdWVyeSh0aGlzKSk7XG4gICAgICB9KTtcbiAgICB9O1xuICAgIGpRdWVyeS5mbi5odG1sID0gZnVuY3Rpb24odmFsdWUpIHtcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHJldHVybiBvcmlnaW5hbEh0bWwuY2FsbCh0aGlzKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgIGpRdWVyeSh0aGlzKS5lbXB0eSgpO1xuICAgICAgICBvcmlnaW5hbEh0bWwuY2FsbChqUXVlcnkodGhpcyksIHZhbHVlKTtcbiAgICAgIH0pO1xuICAgIH07XG4gICAgalF1ZXJ5LmZuLnRleHQgPSBmdW5jdGlvbih2YWx1ZSkge1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsVGV4dC5jYWxsKHRoaXMpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgalF1ZXJ5KHRoaXMpLmVtcHR5KCk7XG4gICAgICAgIG9yaWdpbmFsVGV4dC5jYWxsKGpRdWVyeSh0aGlzKSwgdmFsdWUpO1xuICAgICAgfSk7XG4gICAgfTtcbiAgfVxuICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB3aW5kb3cualF1ZXJ5KSB7XG4gICAgaW5pdF9qcXVlcnlfcGx1Z2luKHdpbmRvdy5qUXVlcnkpO1xuICB9XG4gIHZhciB2ZXJzaW9uID0gXCIyLjIuMTg1XCI7XG4gIHZhciBqcWh0bWwgPSB7XG4gICAgLy8gQ29yZVxuICAgIEpxaHRtbF9Db21wb25lbnQsXG4gICAgTGlmZWN5Y2xlTWFuYWdlcixcbiAgICAvLyBSZWdpc3RyeVxuICAgIHJlZ2lzdGVyX2NvbXBvbmVudCxcbiAgICBnZXRfY29tcG9uZW50X2NsYXNzLFxuICAgIHJlZ2lzdGVyX3RlbXBsYXRlLFxuICAgIGdldF90ZW1wbGF0ZSxcbiAgICBnZXRfdGVtcGxhdGVfYnlfY2xhc3MsXG4gICAgY3JlYXRlX2NvbXBvbmVudCxcbiAgICBoYXNfY29tcG9uZW50LFxuICAgIGdldF9jb21wb25lbnRfbmFtZXMsXG4gICAgZ2V0X3JlZ2lzdGVyZWRfdGVtcGxhdGVzLFxuICAgIGxpc3RfY29tcG9uZW50cyxcbiAgICAvLyBUZW1wbGF0ZSBzeXN0ZW1cbiAgICBwcm9jZXNzX2luc3RydWN0aW9ucyxcbiAgICBleHRyYWN0X3Nsb3RzLFxuICAgIHJlbmRlcl90ZW1wbGF0ZSxcbiAgICBlc2NhcGVfaHRtbCxcbiAgICAvLyBWZXJzaW9uIHByb3BlcnR5IC0gaW50ZXJuYWxcbiAgICBfX3ZlcnNpb246IHZlcnNpb24sXG4gICAgLy8gRGVidWcgc2V0dGluZ3NcbiAgICBkZWJ1Zzoge1xuICAgICAgZW5hYmxlZDogZmFsc2UsXG4gICAgICB2ZXJib3NlOiBmYWxzZVxuICAgIH0sXG4gICAgLy8gRGVidWcgaGVscGVyIGZ1bmN0aW9ucyAobWFpbmx5IGZvciBpbnRlcm5hbCB1c2UgYnV0IGV4cG9zZWQgZm9yIGFkdmFuY2VkIGRlYnVnZ2luZylcbiAgICBzZXREZWJ1Z1NldHRpbmdzKHNldHRpbmdzKSB7XG4gICAgICBPYmplY3QuYXNzaWduKHRoaXMuZGVidWcsIHNldHRpbmdzKTtcbiAgICB9LFxuICAgIGVuYWJsZURlYnVnTW9kZShsZXZlbCA9IFwiYmFzaWNcIikge1xuICAgICAgaWYgKGxldmVsID09PSBcImJhc2ljXCIpIHtcbiAgICAgICAgdGhpcy5kZWJ1Zy5sb2dDcmVhdGlvblJlYWR5ID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5kZWJ1Zy5sb2dEaXNwYXRjaCA9IHRydWU7XG4gICAgICAgIHRoaXMuZGVidWcuZmxhc2hDb21wb25lbnRzID0gdHJ1ZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuZGVidWcubG9nRnVsbExpZmVjeWNsZSA9IHRydWU7XG4gICAgICAgIHRoaXMuZGVidWcubG9nRGlzcGF0Y2hWZXJib3NlID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5kZWJ1Zy5mbGFzaENvbXBvbmVudHMgPSB0cnVlO1xuICAgICAgICB0aGlzLmRlYnVnLnByb2ZpbGVQZXJmb3JtYW5jZSA9IHRydWU7XG4gICAgICAgIHRoaXMuZGVidWcudHJhY2VEYXRhRmxvdyA9IHRydWU7XG4gICAgICB9XG4gICAgfSxcbiAgICBjbGVhckRlYnVnU2V0dGluZ3MoKSB7XG4gICAgICB0aGlzLmRlYnVnID0ge307XG4gICAgfSxcbiAgICAvLyBEZWJ1ZyBvdmVybGF5IG1ldGhvZHNcbiAgICBzaG93RGVidWdPdmVybGF5KG9wdGlvbnMpIHtcbiAgICAgIHJldHVybiBEZWJ1Z092ZXJsYXkuc2hvdyhvcHRpb25zKTtcbiAgICB9LFxuICAgIGhpZGVEZWJ1Z092ZXJsYXkoKSB7XG4gICAgICByZXR1cm4gRGVidWdPdmVybGF5LmhpZGUoKTtcbiAgICB9LFxuICAgIC8vIEV4cG9ydCBEZWJ1Z092ZXJsYXkgY2xhc3MgZm9yIGRpcmVjdCBhY2Nlc3NcbiAgICBEZWJ1Z092ZXJsYXksXG4gICAgLy8gSW5zdGFsbCBnbG9iYWxzIGZ1bmN0aW9uXG4gICAgaW5zdGFsbEdsb2JhbHMoKSB7XG4gICAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIikge1xuICAgICAgICB3aW5kb3cuanFodG1sID0gdGhpcztcbiAgICAgICAgd2luZG93LkpxaHRtbF9Db21wb25lbnQgPSBKcWh0bWxfQ29tcG9uZW50O1xuICAgICAgICB3aW5kb3cuSnFodG1sX0xpZmVjeWNsZU1hbmFnZXIgPSBMaWZlY3ljbGVNYW5hZ2VyO1xuICAgICAgfVxuICAgIH0sXG4gICAgLy8gVmVyc2lvbiBkaXNwbGF5IGZ1bmN0aW9uIC0gc2hvd3MgdmVyc2lvbiBvZiBjb3JlIGxpYnJhcnkgYW5kIGFsbCByZWdpc3RlcmVkIHRlbXBsYXRlc1xuICAgIF92ZXJzaW9uKCkge1xuICAgICAgY29uc29sZS5sb2coYEpRSFRNTCBDb3JlIHYke3RoaXMuX192ZXJzaW9ufWApO1xuICAgICAgY29uc29sZS5sb2coXCJSZWdpc3RlcmVkIFRlbXBsYXRlczpcIik7XG4gICAgICBjb25zdCB0ZW1wbGF0ZU5hbWVzID0gZ2V0X2NvbXBvbmVudF9uYW1lcygpO1xuICAgICAgaWYgKHRlbXBsYXRlTmFtZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKFwiICAobm8gdGVtcGxhdGVzIHJlZ2lzdGVyZWQpXCIpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZm9yIChjb25zdCBuYW1lIG9mIHRlbXBsYXRlTmFtZXMpIHtcbiAgICAgICAgICBjb25zdCB0ZW1wbGF0ZSA9IGdldF90ZW1wbGF0ZShuYW1lKTtcbiAgICAgICAgICBjb25zdCB0ZW1wbGF0ZVZlcnNpb24gPSB0ZW1wbGF0ZSA/IHRlbXBsYXRlLl9qcWh0bWxfdmVyc2lvbiB8fCBcInVua25vd25cIiA6IFwidW5rbm93blwiO1xuICAgICAgICAgIGNvbnNvbGUubG9nKGAgIC0gJHtuYW1lfTogdiR7dGVtcGxhdGVWZXJzaW9ufWApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5fX3ZlcnNpb247XG4gICAgfSxcbiAgICAvLyBQdWJsaWMgdmVyc2lvbiBmdW5jdGlvbiAtIHJldHVybnMgdGhlIHN0YW1wZWQgdmVyc2lvbiBudW1iZXJcbiAgICB2ZXJzaW9uKCkge1xuICAgICAgcmV0dXJuIHZlcnNpb247XG4gICAgfVxuICB9O1xuICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiAhd2luZG93LmpxaHRtbCkge1xuICAgIHdpbmRvdy5qcWh0bWwgPSBqcWh0bWw7XG4gICAgd2luZG93LkpxaHRtbF9Db21wb25lbnQgPSBKcWh0bWxfQ29tcG9uZW50O1xuICAgIHdpbmRvdy5Db21wb25lbnQgPSBKcWh0bWxfQ29tcG9uZW50O1xuICAgIHdpbmRvdy5KcWh0bWxfTGlmZWN5Y2xlTWFuYWdlciA9IExpZmVjeWNsZU1hbmFnZXI7XG4gICAgaWYgKGpxaHRtbC5kZWJ1Zz8uZW5hYmxlZCkge1xuICAgICAgY29uc29sZS5sb2coXCJbSlFIVE1MXSBBdXRvLXJlZ2lzdGVyZWQgd2luZG93LmpxaHRtbCBnbG9iYWwgZm9yIHRlbXBsYXRlIGNvbXBhdGliaWxpdHlcIik7XG4gICAgfVxuICB9XG5cbiAgLy8gc3RvcmFnZS9yc3gtdG1wL25wbS1jb21waWxlL2VudHJ5XzY0NTllOGVkMGY2MGJkYTRmMTIxNDIwNzY2MDEyZDUzLmpzXG4gIHdpbmRvdy5fcnN4X25wbSA9IHdpbmRvdy5fcnN4X25wbSB8fCB7fTtcbiAgd2luZG93Ll9yc3hfbnBtLmpxaHRtbCA9IGpxaHRtbDtcbiAgd2luZG93Ll9yc3hfbnBtLl9CYXNlX0pxaHRtbF9Db21wb25lbnQgPSBKcWh0bWxfQ29tcG9uZW50O1xufSkoKTtcbiJdfQ==