Remove unused blade settings pages not linked from UI Convert remaining frontend pages to SPA actions Convert settings user_settings and general to SPA actions Convert settings profile pages to SPA actions Convert contacts and projects add/edit pages to SPA actions Convert clients add/edit page to SPA action with loading pattern Refactor component scoped IDs from $id to $sid Fix jqhtml comment syntax and implement universal error component system Update all application code to use new unified error system Remove all backwards compatibility - unified error system complete Phase 5: Remove old response classes Phase 3-4: Ajax response handler sends new format, old helpers deprecated Phase 2: Add client-side unified error foundation Phase 1: Add server-side unified error foundation Add unified Ajax error response system with constants 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2209 lines
210 KiB
JavaScript
Executable File
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, """);
|
|
html.push(` ${key}="${escaped_value}"`);
|
|
} else if (typeof value === "boolean" && value) {
|
|
html.push(` ${key}`);
|
|
}
|
|
}
|
|
html.push(">");
|
|
const escaped_content = rawContent.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
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.$sid(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.$sid(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.$sid('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.$sid(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==
|