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_Jquery_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,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0b3JhZ2UvcnN4LWJ1aWxkL2J1bmRsZXMvbnBtX0pxdWVyeV9CdW5kbGVfNjQ1OWU4ZWQwZjYwYmRhNGYxMjE0MjA3NjYwMTJkNTMuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6ImJ1bmRsZV9vdXRwdXRfSnF1ZXJ5X0J1bmRsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoKCkgPT4ge1xuICAvLyBub2RlX21vZHVsZXMvQGpxaHRtbC9jb3JlL2Rpc3QvaW5kZXguanNcbiAgdmFyIExpZmVjeWNsZU1hbmFnZXIgPSBjbGFzcyBfTGlmZWN5Y2xlTWFuYWdlciB7XG4gICAgc3RhdGljIGdldF9pbnN0YW5jZSgpIHtcbiAgICAgIGlmICghX0xpZmVjeWNsZU1hbmFnZXIuaW5zdGFuY2UpIHtcbiAgICAgICAgX0xpZmVjeWNsZU1hbmFnZXIuaW5zdGFuY2UgPSBuZXcgX0xpZmVjeWNsZU1hbmFnZXIoKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBfTGlmZWN5Y2xlTWFuYWdlci5pbnN0YW5jZTtcbiAgICB9XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICB0aGlzLmFjdGl2ZV9jb21wb25lbnRzID0gLyogQF9fUFVSRV9fICovIG5ldyBTZXQoKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQm9vdCBhIGNvbXBvbmVudCAtIHJ1biBpdHMgZnVsbCBsaWZlY3ljbGVcbiAgICAgKiBDYWxsZWQgd2hlbiBjb21wb25lbnQgaXMgY3JlYXRlZFxuICAgICAqL1xuICAgIGFzeW5jIGJvb3RfY29tcG9uZW50KGNvbXBvbmVudCkge1xuICAgICAgdGhpcy5hY3RpdmVfY29tcG9uZW50cy5hZGQoY29tcG9uZW50KTtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGNvbXBvbmVudC5jcmVhdGUoKTtcbiAgICAgICAgaWYgKGNvbXBvbmVudC5fc3RvcHBlZClcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIGNvbXBvbmVudC50cmlnZ2VyKFwiY3JlYXRlXCIpO1xuICAgICAgICBsZXQgcmVuZGVyX2lkID0gY29tcG9uZW50Ll9yZW5kZXIoKTtcbiAgICAgICAgaWYgKGNvbXBvbmVudC5fc3RvcHBlZClcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIGF3YWl0IGNvbXBvbmVudC5sb2FkKCk7XG4gICAgICAgIGlmIChjb21wb25lbnQuX3N0b3BwZWQpXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICBpZiAoY29tcG9uZW50LnNob3VsZF9yZXJlbmRlcigpKSB7XG4gICAgICAgICAgcmVuZGVyX2lkID0gY29tcG9uZW50Ll9yZW5kZXIoKTtcbiAgICAgICAgICBpZiAoY29tcG9uZW50Ll9zdG9wcGVkKVxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjb21wb25lbnQuX3JlbmRlcl9jb3VudCAhPT0gcmVuZGVyX2lkKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IGNvbXBvbmVudC5yZWFkeSgpO1xuICAgICAgICBpZiAoY29tcG9uZW50Ll9zdG9wcGVkKVxuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgYXdhaXQgY29tcG9uZW50LnRyaWdnZXIoXCJyZWFkeVwiKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYEVycm9yIGJvb3RpbmcgY29tcG9uZW50ICR7Y29tcG9uZW50LmNvbXBvbmVudF9uYW1lKCl9OmAsIGVycm9yKTtcbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFVucmVnaXN0ZXIgYSBjb21wb25lbnQgKGNhbGxlZCBvbiBkZXN0cm95KVxuICAgICAqL1xuICAgIHVucmVnaXN0ZXJfY29tcG9uZW50KGNvbXBvbmVudCkge1xuICAgICAgdGhpcy5hY3RpdmVfY29tcG9uZW50cy5kZWxldGUoY29tcG9uZW50KTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogV2FpdCBmb3IgYWxsIGFjdGl2ZSBjb21wb25lbnRzIHRvIHJlYWNoIHJlYWR5IHN0YXRlXG4gICAgICovXG4gICAgYXN5bmMgd2FpdF9mb3JfcmVhZHkoKSB7XG4gICAgICBjb25zdCByZWFkeV9wcm9taXNlcyA9IFtdO1xuICAgICAgZm9yIChjb25zdCBjb21wb25lbnQgb2YgdGhpcy5hY3RpdmVfY29tcG9uZW50cykge1xuICAgICAgICBpZiAoY29tcG9uZW50Ll9yZWFkeV9zdGF0ZSA8IDQpIHtcbiAgICAgICAgICByZWFkeV9wcm9taXNlcy5wdXNoKG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBjb21wb25lbnQub24oXCJyZWFkeVwiLCAoKSA9PiByZXNvbHZlKCkpO1xuICAgICAgICAgIH0pKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwocmVhZHlfcHJvbWlzZXMpO1xuICAgIH1cbiAgfTtcbiAgdmFyIGNvbXBvbmVudF9jbGFzc2VzID0gLyogQF9fUFVSRV9fICovIG5ldyBNYXAoKTtcbiAgdmFyIGNvbXBvbmVudF90ZW1wbGF0ZXMgPSAvKiBAX19QVVJFX18gKi8gbmV3IE1hcCgpO1xuICB2YXIgd2FybmVkX2NvbXBvbmVudHMgPSAvKiBAX19QVVJFX18gKi8gbmV3IFNldCgpO1xuICB2YXIgREVGQVVMVF9URU1QTEFURSA9IHtcbiAgICBuYW1lOiBcIkpxaHRtbF9Db21wb25lbnRcIixcbiAgICAvLyBEZWZhdWx0IG5hbWVcbiAgICB0YWc6IFwiZGl2XCIsXG4gICAgcmVuZGVyOiBmdW5jdGlvbihkYXRhLCBhcmdzLCBjb250ZW50KSB7XG4gICAgICBjb25zdCBfb3V0cHV0ID0gW107XG4gICAgICBpZiAoYXJncy5faW5uZXJfaHRtbCkge1xuICAgICAgICBfb3V0cHV0LnB1c2goYXJncy5faW5uZXJfaHRtbCk7XG4gICAgICAgIHJldHVybiBbX291dHB1dCwgdGhpc107XG4gICAgICB9XG4gICAgICBpZiAoY29udGVudCAmJiB0eXBlb2YgY29udGVudCA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGNvbnRlbnQodGhpcyk7XG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KHJlc3VsdCkgJiYgcmVzdWx0Lmxlbmd0aCA9PT0gMikge1xuICAgICAgICAgIF9vdXRwdXQucHVzaCguLi5yZXN1bHRbMF0pO1xuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiByZXN1bHQgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICBfb3V0cHV0LnB1c2gocmVzdWx0KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIFtfb3V0cHV0LCB0aGlzXTtcbiAgICB9XG4gIH07XG4gIGZ1bmN0aW9uIHJlZ2lzdGVyX2NvbXBvbmVudChuYW1lT3JDbGFzcywgY29tcG9uZW50X2NsYXNzLCB0ZW1wbGF0ZSkge1xuICAgIGlmICh0eXBlb2YgbmFtZU9yQ2xhc3MgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgIGNvbnN0IG5hbWUgPSBuYW1lT3JDbGFzcztcbiAgICAgIGlmICghY29tcG9uZW50X2NsYXNzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkNvbXBvbmVudCBjbGFzcyBpcyByZXF1aXJlZCB3aGVuIHJlZ2lzdGVyaW5nIGJ5IG5hbWVcIik7XG4gICAgICB9XG4gICAgICBpZiAoIS9eW0EtWl0vLnRlc3QobmFtZSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBDb21wb25lbnQgbmFtZSAnJHtuYW1lfScgbXVzdCBzdGFydCB3aXRoIGEgY2FwaXRhbCBsZXR0ZXIuIENvbnZlbnRpb24gaXMgRmlyc3RfTGV0dGVyX1dpdGhfVW5kZXJzY29yZXMuYCk7XG4gICAgICB9XG4gICAgICBjb21wb25lbnRfY2xhc3Nlcy5zZXQobmFtZSwgY29tcG9uZW50X2NsYXNzKTtcbiAgICAgIGlmICh0ZW1wbGF0ZSkge1xuICAgICAgICBpZiAodGVtcGxhdGUubmFtZSAhPT0gbmFtZSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgVGVtcGxhdGUgbmFtZSAnJHt0ZW1wbGF0ZS5uYW1lfScgbXVzdCBtYXRjaCBjb21wb25lbnQgbmFtZSAnJHtuYW1lfSdgKTtcbiAgICAgICAgfVxuICAgICAgICByZWdpc3Rlcl90ZW1wbGF0ZSh0ZW1wbGF0ZSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGNvbXBvbmVudF9jbGFzczIgPSBuYW1lT3JDbGFzcztcbiAgICAgIGNvbnN0IG5hbWUgPSBjb21wb25lbnRfY2xhc3MyLm5hbWU7XG4gICAgICBpZiAoIW5hbWUgfHwgbmFtZSA9PT0gXCJKcWh0bWxfQ29tcG9uZW50XCIpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiQ29tcG9uZW50IGNsYXNzIG11c3QgaGF2ZSBhIG5hbWUgd2hlbiByZWdpc3RlcmluZyB3aXRob3V0IGV4cGxpY2l0IG5hbWVcIik7XG4gICAgICB9XG4gICAgICBjb21wb25lbnRfY2xhc3Nlcy5zZXQobmFtZSwgY29tcG9uZW50X2NsYXNzMik7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIGdldF9jb21wb25lbnRfY2xhc3MobmFtZSkge1xuICAgIGNvbnN0IGRpcmVjdENsYXNzID0gY29tcG9uZW50X2NsYXNzZXMuZ2V0KG5hbWUpO1xuICAgIGlmIChkaXJlY3RDbGFzcykge1xuICAgICAgcmV0dXJuIGRpcmVjdENsYXNzO1xuICAgIH1cbiAgICBjb25zdCB0ZW1wbGF0ZSA9IGNvbXBvbmVudF90ZW1wbGF0ZXMuZ2V0KG5hbWUpO1xuICAgIGlmICh0ZW1wbGF0ZSAmJiB0ZW1wbGF0ZS5leHRlbmRzKSB7XG4gICAgICBjb25zdCB2aXNpdGVkID0gLyogQF9fUFVSRV9fICovIG5ldyBTZXQoW25hbWVdKTtcbiAgICAgIGxldCBjdXJyZW50VGVtcGxhdGVOYW1lID0gdGVtcGxhdGUuZXh0ZW5kcztcbiAgICAgIHdoaWxlIChjdXJyZW50VGVtcGxhdGVOYW1lICYmICF2aXNpdGVkLmhhcyhjdXJyZW50VGVtcGxhdGVOYW1lKSkge1xuICAgICAgICB2aXNpdGVkLmFkZChjdXJyZW50VGVtcGxhdGVOYW1lKTtcbiAgICAgICAgY29uc3QgcGFyZW50Q2xhc3MgPSBjb21wb25lbnRfY2xhc3Nlcy5nZXQoY3VycmVudFRlbXBsYXRlTmFtZSk7XG4gICAgICAgIGlmIChwYXJlbnRDbGFzcykge1xuICAgICAgICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8uZW5hYmxlZCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdIENvbXBvbmVudCAnJHtuYW1lfScgdXNpbmcgY2xhc3MgZnJvbSBwYXJlbnQgJyR7Y3VycmVudFRlbXBsYXRlTmFtZX0nIHZpYSBleHRlbmRzIGNoYWluYCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBwYXJlbnRDbGFzcztcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBwYXJlbnRUZW1wbGF0ZSA9IGNvbXBvbmVudF90ZW1wbGF0ZXMuZ2V0KGN1cnJlbnRUZW1wbGF0ZU5hbWUpO1xuICAgICAgICBpZiAocGFyZW50VGVtcGxhdGUgJiYgcGFyZW50VGVtcGxhdGUuZXh0ZW5kcykge1xuICAgICAgICAgIGN1cnJlbnRUZW1wbGF0ZU5hbWUgPSBwYXJlbnRUZW1wbGF0ZS5leHRlbmRzO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB2b2lkIDA7XG4gIH1cbiAgZnVuY3Rpb24gcmVnaXN0ZXJfdGVtcGxhdGUodGVtcGxhdGVfZGVmKSB7XG4gICAgY29uc3QgbmFtZSA9IHRlbXBsYXRlX2RlZi5uYW1lO1xuICAgIGlmICghbmFtZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiVGVtcGxhdGUgbXVzdCBoYXZlIGEgbmFtZSBwcm9wZXJ0eVwiKTtcbiAgICB9XG4gICAgaWYgKCEvXltBLVpdLy50ZXN0KG5hbWUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFRlbXBsYXRlIG5hbWUgJyR7bmFtZX0nIG11c3Qgc3RhcnQgd2l0aCBhIGNhcGl0YWwgbGV0dGVyLiBDb252ZW50aW9uIGlzIEZpcnN0X0xldHRlcl9XaXRoX1VuZGVyc2NvcmVzLmApO1xuICAgIH1cbiAgICBpZiAoY29tcG9uZW50X3RlbXBsYXRlcy5oYXMobmFtZSkpIHtcbiAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gVGVtcGxhdGUgJyR7bmFtZX0nIGFscmVhZHkgcmVnaXN0ZXJlZCwgc2tpcHBpbmcgZHVwbGljYXRlIHJlZ2lzdHJhdGlvbmApO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBjb21wb25lbnRfdGVtcGxhdGVzLnNldChuYW1lLCB0ZW1wbGF0ZV9kZWYpO1xuICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8uZW5hYmxlZCkge1xuICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdIFN1Y2Nlc3NmdWxseSByZWdpc3RlcmVkIHRlbXBsYXRlOiAke25hbWV9YCk7XG4gICAgfVxuICAgIGNvbnN0IGNvbXBvbmVudF9jbGFzcyA9IGNvbXBvbmVudF9jbGFzc2VzLmdldChuYW1lKTtcbiAgICBpZiAoY29tcG9uZW50X2NsYXNzKSB7XG4gICAgICBjb21wb25lbnRfY2xhc3MuX2pxaHRtbF9tZXRhZGF0YSA9IHtcbiAgICAgICAgdGFnOiB0ZW1wbGF0ZV9kZWYudGFnLFxuICAgICAgICBkZWZhdWx0QXR0cmlidXRlczogdGVtcGxhdGVfZGVmLmRlZmF1bHRBdHRyaWJ1dGVzIHx8IHt9XG4gICAgICB9O1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICBmdW5jdGlvbiBnZXRfdGVtcGxhdGUobmFtZSkge1xuICAgIGNvbnN0IHRlbXBsYXRlID0gY29tcG9uZW50X3RlbXBsYXRlcy5nZXQobmFtZSk7XG4gICAgaWYgKCF0ZW1wbGF0ZSkge1xuICAgICAgY29uc3QgY29tcG9uZW50X2NsYXNzID0gY29tcG9uZW50X2NsYXNzZXMuZ2V0KG5hbWUpO1xuICAgICAgaWYgKGNvbXBvbmVudF9jbGFzcykge1xuICAgICAgICBjb25zdCBpbmhlcml0ZWRfdGVtcGxhdGUgPSBnZXRfdGVtcGxhdGVfYnlfY2xhc3MoY29tcG9uZW50X2NsYXNzKTtcbiAgICAgICAgaWYgKGluaGVyaXRlZF90ZW1wbGF0ZSAhPT0gREVGQVVMVF9URU1QTEFURSkge1xuICAgICAgICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8uZW5hYmxlZCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdIENvbXBvbmVudCAnJHtuYW1lfScgaGFzIG5vIHRlbXBsYXRlLCB1c2luZyB0ZW1wbGF0ZSBmcm9tIHByb3RvdHlwZSBjaGFpbmApO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gaW5oZXJpdGVkX3RlbXBsYXRlO1xuICAgICAgICB9XG4gICAgICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8uZW5hYmxlZCAmJiAhd2FybmVkX2NvbXBvbmVudHMuaGFzKG5hbWUpKSB7XG4gICAgICAgICAgd2FybmVkX2NvbXBvbmVudHMuYWRkKG5hbWUpO1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSBObyB0ZW1wbGF0ZSBmb3VuZCBmb3IgY2xhc3M6ICR7bmFtZX0sIHVzaW5nIGRlZmF1bHQgZGl2IHRlbXBsYXRlYCk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmIChuYW1lICE9PSBcIl9KcWh0bWxfQ29tcG9uZW50XCIgJiYgbmFtZSAhPT0gXCJSZWRyYXdhYmxlXCIgJiYgIXdhcm5lZF9jb21wb25lbnRzLmhhcyhuYW1lKSkge1xuICAgICAgICAgIHdhcm5lZF9jb21wb25lbnRzLmFkZChuYW1lKTtcbiAgICAgICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIENyZWF0aW5nICR7bmFtZX0gd2l0aCBkZWZhdWx0cyAtIG5vIHRlbXBsYXRlIG9yIGNsYXNzIGRlZmluZWRgKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy52ZXJib3NlKSB7XG4gICAgICAgIGNvbnN0IHJlZ2lzdGVyZWQgPSBBcnJheS5mcm9tKGNvbXBvbmVudF90ZW1wbGF0ZXMua2V5cygpKTtcbiAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdIExvb2tpbmcgZm9yIHRlbXBsYXRlICcke25hbWV9JyBpbjogWyR7cmVnaXN0ZXJlZC5qb2luKFwiLCBcIil9XWApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIERFRkFVTFRfVEVNUExBVEU7XG4gICAgfVxuICAgIHJldHVybiB0ZW1wbGF0ZTtcbiAgfVxuICBmdW5jdGlvbiBnZXRfdGVtcGxhdGVfYnlfY2xhc3MoY29tcG9uZW50X2NsYXNzKSB7XG4gICAgaWYgKGNvbXBvbmVudF9jbGFzcy50ZW1wbGF0ZSkge1xuICAgICAgcmV0dXJuIGNvbXBvbmVudF9jbGFzcy50ZW1wbGF0ZTtcbiAgICB9XG4gICAgbGV0IGN1cnJlbnRDbGFzcyA9IGNvbXBvbmVudF9jbGFzcztcbiAgICB3aGlsZSAoY3VycmVudENsYXNzICYmIGN1cnJlbnRDbGFzcy5uYW1lICE9PSBcIk9iamVjdFwiKSB7XG4gICAgICBsZXQgbm9ybWFsaXplZE5hbWUgPSBjdXJyZW50Q2xhc3MubmFtZTtcbiAgICAgIGlmIChub3JtYWxpemVkTmFtZSA9PT0gXCJfSnFodG1sX0NvbXBvbmVudFwiIHx8IG5vcm1hbGl6ZWROYW1lID09PSBcIl9CYXNlX0pxaHRtbF9Db21wb25lbnRcIikge1xuICAgICAgICBub3JtYWxpemVkTmFtZSA9IFwiSnFodG1sX0NvbXBvbmVudFwiO1xuICAgICAgfVxuICAgICAgY29uc3QgdGVtcGxhdGUgPSBjb21wb25lbnRfdGVtcGxhdGVzLmdldChub3JtYWxpemVkTmFtZSk7XG4gICAgICBpZiAodGVtcGxhdGUpIHtcbiAgICAgICAgcmV0dXJuIHRlbXBsYXRlO1xuICAgICAgfVxuICAgICAgY3VycmVudENsYXNzID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKGN1cnJlbnRDbGFzcyk7XG4gICAgfVxuICAgIHJldHVybiBERUZBVUxUX1RFTVBMQVRFO1xuICB9XG4gIGZ1bmN0aW9uIGNyZWF0ZV9jb21wb25lbnQobmFtZSwgZWxlbWVudCwgYXJncyA9IHt9KSB7XG4gICAgY29uc3QgQ29tcG9uZW50Q2xhc3MgPSBnZXRfY29tcG9uZW50X2NsYXNzKG5hbWUpIHx8IEpxaHRtbF9Db21wb25lbnQ7XG4gICAgcmV0dXJuIG5ldyBDb21wb25lbnRDbGFzcyhlbGVtZW50LCBhcmdzKTtcbiAgfVxuICBmdW5jdGlvbiBoYXNfY29tcG9uZW50KG5hbWUpIHtcbiAgICByZXR1cm4gY29tcG9uZW50X2NsYXNzZXMuaGFzKG5hbWUpO1xuICB9XG4gIGZ1bmN0aW9uIGdldF9jb21wb25lbnRfbmFtZXMoKSB7XG4gICAgcmV0dXJuIEFycmF5LmZyb20oY29tcG9uZW50X2NsYXNzZXMua2V5cygpKTtcbiAgfVxuICBmdW5jdGlvbiBnZXRfcmVnaXN0ZXJlZF90ZW1wbGF0ZXMoKSB7XG4gICAgcmV0dXJuIEFycmF5LmZyb20oY29tcG9uZW50X3RlbXBsYXRlcy5rZXlzKCkpO1xuICB9XG4gIGZ1bmN0aW9uIGxpc3RfY29tcG9uZW50cygpIHtcbiAgICBjb25zdCByZXN1bHQgPSB7fTtcbiAgICBmb3IgKGNvbnN0IG5hbWUgb2YgY29tcG9uZW50X2NsYXNzZXMua2V5cygpKSB7XG4gICAgICByZXN1bHRbbmFtZV0gPSB7XG4gICAgICAgIGhhc19jbGFzczogdHJ1ZSxcbiAgICAgICAgaGFzX3RlbXBsYXRlOiBjb21wb25lbnRfdGVtcGxhdGVzLmhhcyhuYW1lKVxuICAgICAgfTtcbiAgICB9XG4gICAgZm9yIChjb25zdCBuYW1lIG9mIGNvbXBvbmVudF90ZW1wbGF0ZXMua2V5cygpKSB7XG4gICAgICBpZiAoIXJlc3VsdFtuYW1lXSkge1xuICAgICAgICByZXN1bHRbbmFtZV0gPSB7XG4gICAgICAgICAgaGFzX2NsYXNzOiBmYWxzZSxcbiAgICAgICAgICBoYXNfdGVtcGxhdGU6IHRydWVcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuICB2YXIgX2NpZF9pbmNyZW1lbnQgPSBcImFhXCI7XG4gIGZ1bmN0aW9uIHVpZCgpIHtcbiAgICBjb25zdCBjdXJyZW50ID0gX2NpZF9pbmNyZW1lbnQ7XG4gICAgY29uc3QgY2hhcnMgPSBfY2lkX2luY3JlbWVudC5zcGxpdChcIlwiKTtcbiAgICBsZXQgY2FycnkgPSB0cnVlO1xuICAgIGZvciAobGV0IGkgPSBjaGFycy5sZW5ndGggLSAxOyBpID49IDAgJiYgY2Fycnk7IGktLSkge1xuICAgICAgY29uc3QgY2hhciA9IGNoYXJzW2ldO1xuICAgICAgaWYgKGNoYXIgPj0gXCJhXCIgJiYgY2hhciA8IFwielwiKSB7XG4gICAgICAgIGNoYXJzW2ldID0gU3RyaW5nLmZyb21DaGFyQ29kZShjaGFyLmNoYXJDb2RlQXQoMCkgKyAxKTtcbiAgICAgICAgY2FycnkgPSBmYWxzZTtcbiAgICAgIH0gZWxzZSBpZiAoY2hhciA9PT0gXCJ6XCIpIHtcbiAgICAgICAgY2hhcnNbaV0gPSBcIjBcIjtcbiAgICAgICAgY2FycnkgPSBmYWxzZTtcbiAgICAgIH0gZWxzZSBpZiAoY2hhciA+PSBcIjBcIiAmJiBjaGFyIDwgXCI5XCIpIHtcbiAgICAgICAgY2hhcnNbaV0gPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGNoYXIuY2hhckNvZGVBdCgwKSArIDEpO1xuICAgICAgICBjYXJyeSA9IGZhbHNlO1xuICAgICAgfSBlbHNlIGlmIChjaGFyID09PSBcIjlcIikge1xuICAgICAgICBjaGFyc1tpXSA9IFwiYVwiO1xuICAgICAgICBjYXJyeSA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChjYXJyeSkge1xuICAgICAgY2hhcnMudW5zaGlmdChcImFcIik7XG4gICAgfVxuICAgIGlmIChjaGFyc1swXSA+PSBcIjBcIiAmJiBjaGFyc1swXSA8PSBcIjlcIikge1xuICAgICAgY2hhcnNbMF0gPSBcImFcIjtcbiAgICAgIGNoYXJzLnVuc2hpZnQoXCJhXCIpO1xuICAgIH1cbiAgICBfY2lkX2luY3JlbWVudCA9IGNoYXJzLmpvaW4oXCJcIik7XG4gICAgcmV0dXJuIGN1cnJlbnQ7XG4gIH1cbiAgZnVuY3Rpb24gcHJvY2Vzc19pbnN0cnVjdGlvbnMoaW5zdHJ1Y3Rpb25zLCB0YXJnZXQsIGNvbnRleHQsIHNsb3RzKSB7XG4gICAgY29uc3QgaHRtbCA9IFtdO1xuICAgIGNvbnN0IHRhZ0VsZW1lbnRzID0ge307XG4gICAgY29uc3QgY29tcG9uZW50cyA9IHt9O1xuICAgIGZvciAoY29uc3QgaW5zdHJ1Y3Rpb24gb2YgaW5zdHJ1Y3Rpb25zKSB7XG4gICAgICBwcm9jZXNzX2luc3RydWN0aW9uX3RvX2h0bWwoaW5zdHJ1Y3Rpb24sIGh0bWwsIHRhZ0VsZW1lbnRzLCBjb21wb25lbnRzLCBjb250ZXh0LCBzbG90cyk7XG4gICAgfVxuICAgIHRhcmdldFswXS5pbm5lckhUTUwgPSBodG1sLmpvaW4oXCJcIik7XG4gICAgZm9yIChjb25zdCBbdGlkLCB0YWdEYXRhXSBvZiBPYmplY3QuZW50cmllcyh0YWdFbGVtZW50cykpIHtcbiAgICAgIGNvbnN0IGVsID0gdGFyZ2V0WzBdLnF1ZXJ5U2VsZWN0b3IoYFtkYXRhLXRpZD1cIiR7dGlkfVwiXWApO1xuICAgICAgaWYgKGVsKSB7XG4gICAgICAgIGNvbnN0IGVsZW1lbnQgPSAkKGVsKTtcbiAgICAgICAgZWwucmVtb3ZlQXR0cmlidXRlKFwiZGF0YS10aWRcIik7XG4gICAgICAgIGFwcGx5X2F0dHJpYnV0ZXMoZWxlbWVudCwgdGFnRGF0YS5hdHRycywgY29udGV4dCk7XG4gICAgICB9XG4gICAgfVxuICAgIGZvciAoY29uc3QgW2NpZCwgY29tcERhdGFdIG9mIE9iamVjdC5lbnRyaWVzKGNvbXBvbmVudHMpKSB7XG4gICAgICBjb25zdCBlbCA9IHRhcmdldFswXS5xdWVyeVNlbGVjdG9yKGBbZGF0YS1jaWQ9XCIke2NpZH1cIl1gKTtcbiAgICAgIGlmIChlbCkge1xuICAgICAgICBjb25zdCBlbGVtZW50ID0gJChlbCk7XG4gICAgICAgIGVsLnJlbW92ZUF0dHJpYnV0ZShcImRhdGEtY2lkXCIpO1xuICAgICAgICBpbml0aWFsaXplX2NvbXBvbmVudChlbGVtZW50LCBjb21wRGF0YSk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIHByb2Nlc3NfaW5zdHJ1Y3Rpb25fdG9faHRtbChpbnN0cnVjdGlvbiwgaHRtbCwgdGFnRWxlbWVudHMsIGNvbXBvbmVudHMsIGNvbnRleHQsIHNsb3RzKSB7XG4gICAgaWYgKHR5cGVvZiBpbnN0cnVjdGlvbiA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgaHRtbC5wdXNoKGluc3RydWN0aW9uKTtcbiAgICB9IGVsc2UgaWYgKFwidGFnXCIgaW4gaW5zdHJ1Y3Rpb24pIHtcbiAgICAgIHByb2Nlc3NfdGFnX3RvX2h0bWwoaW5zdHJ1Y3Rpb24sIGh0bWwsIHRhZ0VsZW1lbnRzLCBjb21wb25lbnRzLCBjb250ZXh0KTtcbiAgICB9IGVsc2UgaWYgKFwiY29tcFwiIGluIGluc3RydWN0aW9uKSB7XG4gICAgICBwcm9jZXNzX2NvbXBvbmVudF90b19odG1sKGluc3RydWN0aW9uLCBodG1sLCBjb21wb25lbnRzLCBjb250ZXh0KTtcbiAgICB9IGVsc2UgaWYgKFwic2xvdFwiIGluIGluc3RydWN0aW9uKSB7XG4gICAgICBwcm9jZXNzX3Nsb3RfdG9faHRtbChpbnN0cnVjdGlvbiwgaHRtbCwgdGFnRWxlbWVudHMsIGNvbXBvbmVudHMsIGNvbnRleHQsIHNsb3RzKTtcbiAgICB9IGVsc2UgaWYgKFwicmF3dGFnXCIgaW4gaW5zdHJ1Y3Rpb24pIHtcbiAgICAgIHByb2Nlc3NfcmF3dGFnX3RvX2h0bWwoaW5zdHJ1Y3Rpb24sIGh0bWwpO1xuICAgIH1cbiAgfVxuICBmdW5jdGlvbiBwcm9jZXNzX3RhZ190b19odG1sKGluc3RydWN0aW9uLCBodG1sLCB0YWdFbGVtZW50cywgY29tcG9uZW50cywgY29udGV4dCkge1xuICAgIGNvbnN0IFt0YWdOYW1lLCBhdHRycywgc2VsZkNsb3NpbmddID0gaW5zdHJ1Y3Rpb24udGFnO1xuICAgIGNvbnN0IG5lZWRzVHJhY2tpbmcgPSBPYmplY3Qua2V5cyhhdHRycykuc29tZSgoa2V5KSA9PiBrZXkgPT09IFwiJGlkXCIgfHwga2V5LnN0YXJ0c1dpdGgoXCIkXCIpIHx8IGtleS5zdGFydHNXaXRoKFwiQFwiKSB8fCBrZXkuc3RhcnRzV2l0aChcIm9uXCIpIHx8IGtleS5zdGFydHNXaXRoKFwiZGF0YS1iaW5kLVwiKSB8fCBrZXkuc3RhcnRzV2l0aChcImRhdGEtb24tXCIpKTtcbiAgICBodG1sLnB1c2goYDwke3RhZ05hbWV9YCk7XG4gICAgbGV0IHRpZCA9IG51bGw7XG4gICAgaWYgKG5lZWRzVHJhY2tpbmcpIHtcbiAgICAgIHRpZCA9IHVpZCgpO1xuICAgICAgaHRtbC5wdXNoKGAgZGF0YS10aWQ9XCIke3RpZH1cImApO1xuICAgICAgdGFnRWxlbWVudHNbdGlkXSA9IHsgYXR0cnMsIGNvbnRleHQgfTtcbiAgICB9XG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoYXR0cnMpKSB7XG4gICAgICBpZiAoIWtleS5zdGFydHNXaXRoKFwiJFwiKSAmJiAha2V5LnN0YXJ0c1dpdGgoXCJvblwiKSAmJiAha2V5LnN0YXJ0c1dpdGgoXCJAXCIpICYmICFrZXkuc3RhcnRzV2l0aChcImRhdGEtYmluZC1cIikgJiYgIWtleS5zdGFydHNXaXRoKFwiZGF0YS1vbi1cIikgJiYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiB8fCB0eXBlb2YgdmFsdWUgPT09IFwibnVtYmVyXCIpKSB7XG4gICAgICAgIGlmIChrZXkgPT09IFwiaWRcIiAmJiB0aWQpIHtcbiAgICAgICAgICBodG1sLnB1c2goYCBpZD1cIiR7dmFsdWV9OiR7Y29udGV4dC5fY2lkfVwiYCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaHRtbC5wdXNoKGAgJHtrZXl9PVwiJHt2YWx1ZX1cImApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChzZWxmQ2xvc2luZykge1xuICAgICAgaHRtbC5wdXNoKFwiIC8+XCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBodG1sLnB1c2goXCI+XCIpO1xuICAgIH1cbiAgfVxuICBmdW5jdGlvbiBwcm9jZXNzX2NvbXBvbmVudF90b19odG1sKGluc3RydWN0aW9uLCBodG1sLCBjb21wb25lbnRzLCBjb250ZXh0KSB7XG4gICAgY29uc3QgW2NvbXBvbmVudE5hbWUsIHByb3BzLCBjb250ZW50Rm5dID0gaW5zdHJ1Y3Rpb24uY29tcDtcbiAgICBjb25zdCBjaWQgPSB1aWQoKTtcbiAgICBnZXRfY29tcG9uZW50X2NsYXNzKGNvbXBvbmVudE5hbWUpIHx8IEpxaHRtbF9Db21wb25lbnQ7XG4gICAgY29uc3QgdGVtcGxhdGUgPSBnZXRfdGVtcGxhdGUoY29tcG9uZW50TmFtZSk7XG4gICAgY29uc3QgdGFnTmFtZSA9IHByb3BzLl90YWcgfHwgdGVtcGxhdGUudGFnIHx8IFwiZGl2XCI7XG4gICAgaHRtbC5wdXNoKGA8JHt0YWdOYW1lfSBkYXRhLWNpZD1cIiR7Y2lkfVwiYCk7XG4gICAgaWYgKHByb3BzW1wiZGF0YS1pZFwiXSkge1xuICAgICAgY29uc3QgYmFzZUlkID0gcHJvcHNbXCJkYXRhLWlkXCJdO1xuICAgICAgaHRtbC5wdXNoKGAgaWQ9XCIke3Byb3BzW1wiaWRcIl19XCIgZGF0YS1pZD1cIiR7YmFzZUlkfVwiYCk7XG4gICAgfSBlbHNlIGlmIChwcm9wc1tcImlkXCJdKSB7XG4gICAgICBodG1sLnB1c2goYCBpZD1cIiR7cHJvcHNbXCJpZFwiXX1cImApO1xuICAgIH1cbiAgICBodG1sLnB1c2goXCI+PC9cIiArIHRhZ05hbWUgKyBcIj5cIik7XG4gICAgY29tcG9uZW50c1tjaWRdID0ge1xuICAgICAgbmFtZTogY29tcG9uZW50TmFtZSxcbiAgICAgIHByb3BzLFxuICAgICAgY29udGVudEZuLFxuICAgICAgY29udGV4dFxuICAgIH07XG4gIH1cbiAgZnVuY3Rpb24gcHJvY2Vzc19zbG90X3RvX2h0bWwoaW5zdHJ1Y3Rpb24sIGh0bWwsIHRhZ0VsZW1lbnRzLCBjb21wb25lbnRzLCBjb250ZXh0LCBwYXJlbnRTbG90cykge1xuICAgIGNvbnN0IFtzbG90TmFtZV0gPSBpbnN0cnVjdGlvbi5zbG90O1xuICAgIGlmIChwYXJlbnRTbG90cyAmJiBzbG90TmFtZSBpbiBwYXJlbnRTbG90cykge1xuICAgICAgY29uc3QgcGFyZW50U2xvdCA9IHBhcmVudFNsb3RzW3Nsb3ROYW1lXTtcbiAgICAgIGNvbnN0IFssIHNsb3RQcm9wcywgY29udGVudEZuXSA9IHBhcmVudFNsb3Quc2xvdDtcbiAgICAgIGNvbnN0IFtjb250ZW50XSA9IGNvbnRlbnRGbi5jYWxsKGNvbnRleHQsIHNsb3RQcm9wcyk7XG4gICAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgY29udGVudCkge1xuICAgICAgICBwcm9jZXNzX2luc3RydWN0aW9uX3RvX2h0bWwoaXRlbSwgaHRtbCwgdGFnRWxlbWVudHMsIGNvbXBvbmVudHMsIGNvbnRleHQpO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoc2xvdE5hbWUgPT09IFwiZGVmYXVsdFwiICYmIGluc3RydWN0aW9uLnNsb3RbMl0pIHtcbiAgICAgIGNvbnN0IFssICwgZGVmYXVsdEZuXSA9IGluc3RydWN0aW9uLnNsb3Q7XG4gICAgICBjb25zdCBbY29udGVudF0gPSBkZWZhdWx0Rm4uY2FsbChjb250ZXh0LCB7fSk7XG4gICAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgY29udGVudCkge1xuICAgICAgICBwcm9jZXNzX2luc3RydWN0aW9uX3RvX2h0bWwoaXRlbSwgaHRtbCwgdGFnRWxlbWVudHMsIGNvbXBvbmVudHMsIGNvbnRleHQpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBmdW5jdGlvbiBwcm9jZXNzX3Jhd3RhZ190b19odG1sKGluc3RydWN0aW9uLCBodG1sKSB7XG4gICAgY29uc3QgW3RhZ05hbWUsIGF0dHJzLCByYXdDb250ZW50XSA9IGluc3RydWN0aW9uLnJhd3RhZztcbiAgICBodG1sLnB1c2goYDwke3RhZ05hbWV9YCk7XG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoYXR0cnMpKSB7XG4gICAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiIHx8IHR5cGVvZiB2YWx1ZSA9PT0gXCJudW1iZXJcIikge1xuICAgICAgICBjb25zdCBlc2NhcGVkX3ZhbHVlID0gU3RyaW5nKHZhbHVlKS5yZXBsYWNlKC9cIi9nLCBcIiZxdW90O1wiKTtcbiAgICAgICAgaHRtbC5wdXNoKGAgJHtrZXl9PVwiJHtlc2NhcGVkX3ZhbHVlfVwiYCk7XG4gICAgICB9IGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJib29sZWFuXCIgJiYgdmFsdWUpIHtcbiAgICAgICAgaHRtbC5wdXNoKGAgJHtrZXl9YCk7XG4gICAgICB9XG4gICAgfVxuICAgIGh0bWwucHVzaChcIj5cIik7XG4gICAgY29uc3QgZXNjYXBlZF9jb250ZW50ID0gcmF3Q29udGVudC5yZXBsYWNlKC8mL2csIFwiJmFtcDtcIikucmVwbGFjZSgvPC9nLCBcIiZsdDtcIikucmVwbGFjZSgvPi9nLCBcIiZndDtcIik7XG4gICAgaHRtbC5wdXNoKGVzY2FwZWRfY29udGVudCk7XG4gICAgaHRtbC5wdXNoKGA8LyR7dGFnTmFtZX0+YCk7XG4gIH1cbiAgZnVuY3Rpb24gYXBwbHlfYXR0cmlidXRlcyhlbGVtZW50LCBhdHRycywgY29udGV4dCkge1xuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGF0dHJzKSkge1xuICAgICAgaWYgKGtleSA9PT0gXCIkaWRcIiB8fCBrZXkgPT09IFwiaWRcIikge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH0gZWxzZSBpZiAoa2V5LnN0YXJ0c1dpdGgoXCIkXCIpKSB7XG4gICAgICAgIGNvbnN0IGRhdGFLZXkgPSBrZXkuc3Vic3RyaW5nKDEpO1xuICAgICAgICBlbGVtZW50LmRhdGEoZGF0YUtleSwgdmFsdWUpO1xuICAgICAgICBjb250ZXh0LmFyZ3NbZGF0YUtleV0gPSB2YWx1ZTtcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PSBcInN0cmluZ1wiIHx8IHR5cGVvZiB2YWx1ZSA9PSBcIm51bWJlclwiKSB7XG4gICAgICAgICAgY29uc3QgYXR0clZhbHVlID0gdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiID8gdmFsdWUudHJpbSgpIDogdmFsdWU7XG4gICAgICAgICAgZWxlbWVudC5hdHRyKGBkYXRhLSR7ZGF0YUtleX1gLCBhdHRyVmFsdWUpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGtleS5zdGFydHNXaXRoKFwiZGF0YS1vbi1cIikpIHtcbiAgICAgICAgY29uc3QgZXZlbnROYW1lID0ga2V5LnN1YnN0cmluZyg4KTtcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgZWxlbWVudC5vbihldmVudE5hbWUsIGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgICAgIHZhbHVlLmJpbmQoY29udGV4dCkoZSwgZWxlbWVudCk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc29sZS53YXJuKFwiKEpRSFRNTCkgVHJpZWQgdG8gYXNzaWduIGEgbm9uIGZ1bmN0aW9uIHRvIG9uIGV2ZW50IGhhbmRsZXIgXCIgKyBrZXkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGtleS5zdGFydHNXaXRoKFwib25cIikpIHtcbiAgICAgICAgY29uc3QgZXZlbnROYW1lID0ga2V5LnN1YnN0cmluZygyKTtcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgZWxlbWVudC5vbihldmVudE5hbWUsIGZ1bmN0aW9uKGUpIHtcbiAgICAgICAgICAgIHZhbHVlLmJpbmQoY29udGV4dCkoZSwgZWxlbWVudCk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc29sZS53YXJuKFwiKEpRSFRNTCkgVHJpZWQgdG8gYXNzaWduIGEgbm9uIGZ1bmN0aW9uIHRvIG9uIGV2ZW50IGhhbmRsZXIgXCIgKyBrZXkpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGtleS5zdGFydHNXaXRoKFwiZGF0YS1cIikpIHtcbiAgICAgICAgY29uc3QgYXR0clZhbHVlID0gdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiID8gdmFsdWUudHJpbSgpIDogdmFsdWU7XG4gICAgICAgIGVsZW1lbnQuYXR0cihrZXksIGF0dHJWYWx1ZSk7XG4gICAgICAgIGNvbnN0IGRhdGFLZXkgPSBrZXkuc3Vic3RyaW5nKDUpO1xuICAgICAgICBlbGVtZW50LmRhdGEoZGF0YUtleSwgdmFsdWUpO1xuICAgICAgICBjb250ZXh0LmFyZ3NbZGF0YUtleV0gPSB2YWx1ZTtcbiAgICAgIH0gZWxzZSBpZiAoa2V5ID09PSBcImNsYXNzXCIpIHtcbiAgICAgICAgY29uc3QgZXhpc3RpbmdDbGFzc2VzID0gZWxlbWVudC5hdHRyKFwiY2xhc3NcIik7XG4gICAgICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8uZW5hYmxlZCkge1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbSW5zdHJ1Y3Rpb25Qcm9jZXNzb3JdIE1lcmdpbmcgY2xhc3MgYXR0cmlidXRlOmAsIHtcbiAgICAgICAgICAgIGV4aXN0aW5nOiBleGlzdGluZ0NsYXNzZXMsXG4gICAgICAgICAgICBuZXc6IHZhbHVlXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFleGlzdGluZ0NsYXNzZXMpIHtcbiAgICAgICAgICBjb25zdCBhdHRyVmFsdWUgPSB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgPyB2YWx1ZS50cmltKCkgOiB2YWx1ZTtcbiAgICAgICAgICBlbGVtZW50LmF0dHIoXCJjbGFzc1wiLCBhdHRyVmFsdWUpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbnN0IGV4aXN0aW5nID0gZXhpc3RpbmdDbGFzc2VzLnNwbGl0KC9cXHMrLykuZmlsdGVyKChjKSA9PiBjKTtcbiAgICAgICAgICBjb25zdCBuZXdDbGFzc2VzID0gU3RyaW5nKHZhbHVlKS5zcGxpdCgvXFxzKy8pLmZpbHRlcigoYykgPT4gYyk7XG4gICAgICAgICAgZm9yIChjb25zdCBuZXdDbGFzcyBvZiBuZXdDbGFzc2VzKSB7XG4gICAgICAgICAgICBpZiAoIWV4aXN0aW5nLmluY2x1ZGVzKG5ld0NsYXNzKSkge1xuICAgICAgICAgICAgICBleGlzdGluZy5wdXNoKG5ld0NsYXNzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgZWxlbWVudC5hdHRyKFwiY2xhc3NcIiwgZXhpc3Rpbmcuam9pbihcIiBcIikpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8uZW5hYmxlZCkge1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbSW5zdHJ1Y3Rpb25Qcm9jZXNzb3JdIENsYXNzIGFmdGVyIG1lcmdlOmAsIGVsZW1lbnQuYXR0cihcImNsYXNzXCIpKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChrZXkgPT09IFwic3R5bGVcIikge1xuICAgICAgICBjb25zdCBleGlzdGluZ1N0eWxlID0gZWxlbWVudC5hdHRyKFwic3R5bGVcIik7XG4gICAgICAgIGlmICghZXhpc3RpbmdTdHlsZSkge1xuICAgICAgICAgIGNvbnN0IGF0dHJWYWx1ZSA9IHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiA/IHZhbHVlLnRyaW0oKSA6IHZhbHVlO1xuICAgICAgICAgIGVsZW1lbnQuYXR0cihcInN0eWxlXCIsIGF0dHJWYWx1ZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc3Qgc3R5bGVNYXAgPSB7fTtcbiAgICAgICAgICBleGlzdGluZ1N0eWxlLnNwbGl0KFwiO1wiKS5mb3JFYWNoKChydWxlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBbcHJvcCwgdmFsXSA9IHJ1bGUuc3BsaXQoXCI6XCIpLm1hcCgocykgPT4gcy50cmltKCkpO1xuICAgICAgICAgICAgaWYgKHByb3AgJiYgdmFsKSB7XG4gICAgICAgICAgICAgIHN0eWxlTWFwW3Byb3BdID0gdmFsO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIFN0cmluZyh2YWx1ZSkuc3BsaXQoXCI7XCIpLmZvckVhY2goKHJ1bGUpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IFtwcm9wLCB2YWxdID0gcnVsZS5zcGxpdChcIjpcIikubWFwKChzKSA9PiBzLnRyaW0oKSk7XG4gICAgICAgICAgICBpZiAocHJvcCAmJiB2YWwpIHtcbiAgICAgICAgICAgICAgc3R5bGVNYXBbcHJvcF0gPSB2YWw7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgY29uc3QgbWVyZ2VkU3R5bGUgPSBPYmplY3QuZW50cmllcyhzdHlsZU1hcCkubWFwKChbcHJvcCwgdmFsXSkgPT4gYCR7cHJvcH06ICR7dmFsfWApLmpvaW4oXCI7IFwiKTtcbiAgICAgICAgICBlbGVtZW50LmF0dHIoXCJzdHlsZVwiLCBtZXJnZWRTdHlsZSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgfHwgdHlwZW9mIHZhbHVlID09PSBcIm51bWJlclwiIHx8IHR5cGVvZiB2YWx1ZSA9PT0gXCJib29sZWFuXCIpIHtcbiAgICAgICAgICBjb25zdCBhdHRyVmFsdWUgPSB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgPyB2YWx1ZS50cmltKCkgOiBTdHJpbmcodmFsdWUpO1xuICAgICAgICAgIGVsZW1lbnQuYXR0cihrZXksIGF0dHJWYWx1ZSk7XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHZhbHVlID09PSBcIm9iamVjdFwiKSB7XG4gICAgICAgICAgY29uc29sZS53YXJuKGAoSlFIVE1MKSBVbmV4cGVjdGVkIHZhbHVlIGZvciAnJHtrZXl9JyBvbmAsIGVsZW1lbnQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGFzeW5jIGZ1bmN0aW9uIGluaXRpYWxpemVfY29tcG9uZW50KGVsZW1lbnQsIGNvbXBEYXRhKSB7XG4gICAgY29uc3QgeyBuYW1lLCBwcm9wcywgY29udGVudEZuLCBjb250ZXh0IH0gPSBjb21wRGF0YTtcbiAgICBjb25zdCBDb21wb25lbnRDbGFzcyA9IGdldF9jb21wb25lbnRfY2xhc3MobmFtZSkgfHwgSnFodG1sX0NvbXBvbmVudDtcbiAgICBjb25zdCBpbnZvY2F0aW9uQXR0cnMgPSB7fTtcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwcm9wcykpIHtcbiAgICAgIGlmICgha2V5LnN0YXJ0c1dpdGgoXCJfXCIpKSB7XG4gICAgICAgIGludm9jYXRpb25BdHRyc1trZXldID0gdmFsdWU7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8uZW5hYmxlZCkge1xuICAgICAgY29uc29sZS5sb2coYFtJbnN0cnVjdGlvblByb2Nlc3Nvcl0gQXBwbHlpbmcgaW52b2NhdGlvbiBhdHRyaWJ1dGVzIGZvciAke25hbWV9OmAsIGludm9jYXRpb25BdHRycyk7XG4gICAgfVxuICAgIGFwcGx5X2F0dHJpYnV0ZXMoZWxlbWVudCwgaW52b2NhdGlvbkF0dHJzLCBjb250ZXh0KTtcbiAgICBjb25zdCBvcHRpb25zID0ge307XG4gICAgaWYgKGNvbnRlbnRGbikge1xuICAgICAgb3B0aW9ucy5faW5uZXJodG1sX2Z1bmN0aW9uID0gY29udGVudEZuO1xuICAgIH1cbiAgICBpZiAoQ29tcG9uZW50Q2xhc3MubmFtZSAhPT0gbmFtZSkge1xuICAgICAgb3B0aW9ucy5fY29tcG9uZW50X25hbWUgPSBuYW1lO1xuICAgIH1cbiAgICBjb25zdCBpbnN0YW5jZSA9IG5ldyBDb21wb25lbnRDbGFzcyhlbGVtZW50LCBvcHRpb25zKTtcbiAgICBpbnN0YW5jZS5faW5zdGFudGlhdG9yID0gY29udGV4dDtcbiAgICBhd2FpdCBpbnN0YW5jZS5ib290KCk7XG4gIH1cbiAgZnVuY3Rpb24gZXh0cmFjdF9zbG90cyhpbnN0cnVjdGlvbnMpIHtcbiAgICBjb25zdCBzbG90cyA9IHt9O1xuICAgIGZvciAoY29uc3QgaW5zdHJ1Y3Rpb24gb2YgaW5zdHJ1Y3Rpb25zKSB7XG4gICAgICBpZiAodHlwZW9mIGluc3RydWN0aW9uID09PSBcIm9iamVjdFwiICYmIFwic2xvdFwiIGluIGluc3RydWN0aW9uKSB7XG4gICAgICAgIGNvbnN0IFtuYW1lXSA9IGluc3RydWN0aW9uLnNsb3Q7XG4gICAgICAgIHNsb3RzW25hbWVdID0gaW5zdHJ1Y3Rpb247XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBzbG90cztcbiAgfVxuICB2YXIgcGVyZm9ybWFuY2VNZXRyaWNzID0gLyogQF9fUFVSRV9fICovIG5ldyBNYXAoKTtcbiAgZnVuY3Rpb24gZGV2V2FybihtZXNzYWdlKSB7XG4gICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgd2luZG93LkpRSFRNTF9TVVBQUkVTU19XQVJOSU5HUykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAodHlwZW9mIHByb2Nlc3MgIT09IFwidW5kZWZpbmVkXCIgJiYgcHJvY2Vzcy5lbnYgJiYgZmFsc2UpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc29sZS53YXJuKGBbSlFIVE1MIERldiBXYXJuaW5nXSAke21lc3NhZ2V9YCk7XG4gIH1cbiAgZnVuY3Rpb24gZ2V0SnFodG1sJDEoKSB7XG4gICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgd2luZG93LmpxaHRtbCkge1xuICAgICAgcmV0dXJuIHdpbmRvdy5qcWh0bWw7XG4gICAgfVxuICAgIGlmICh0eXBlb2YgZ2xvYmFsVGhpcyAhPT0gXCJ1bmRlZmluZWRcIiAmJiBnbG9iYWxUaGlzLmpxaHRtbCkge1xuICAgICAgcmV0dXJuIGdsb2JhbFRoaXMuanFodG1sO1xuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoXCJGQVRBTDogd2luZG93LmpxaHRtbCBpcyBub3QgZGVmaW5lZC4gVGhlIEpRSFRNTCBydW50aW1lIG11c3QgYmUgbG9hZGVkIGJlZm9yZSB1c2luZyBkZWJ1ZyBmZWF0dXJlcy4gSW1wb3J0IGFuZCBpbml0aWFsaXplIEBqcWh0bWwvY29yZSBiZWZvcmUgYXR0ZW1wdGluZyB0byB1c2UgZGVidWcgZnVuY3Rpb25hbGl0eS5cIik7XG4gIH1cbiAgZnVuY3Rpb24gZmxhc2hDb21wb25lbnQoY29tcG9uZW50LCBldmVudFR5cGUpIHtcbiAgICBjb25zdCBqcWh0bWwyID0gZ2V0SnFodG1sJDEoKTtcbiAgICBpZiAoIWpxaHRtbDI/LmRlYnVnPy5mbGFzaENvbXBvbmVudHMpXG4gICAgICByZXR1cm47XG4gICAgY29uc3QgZHVyYXRpb24gPSBqcWh0bWwyLmRlYnVnLmZsYXNoRHVyYXRpb24gfHwgNTAwO1xuICAgIGNvbnN0IGNvbG9ycyA9IGpxaHRtbDIuZGVidWcuZmxhc2hDb2xvcnMgfHwge307XG4gICAgY29uc3QgY29sb3IgPSBjb2xvcnNbZXZlbnRUeXBlXSB8fCAoZXZlbnRUeXBlID09PSBcImNyZWF0ZVwiID8gXCIjMzQ5OGRiXCIgOiBldmVudFR5cGUgPT09IFwicmVuZGVyXCIgPyBcIiMyN2FlNjBcIiA6IFwiIzliNTliNlwiKTtcbiAgICBjb25zdCBvcmlnaW5hbEJvcmRlciA9IGNvbXBvbmVudC4kLmNzcyhcImJvcmRlclwiKTtcbiAgICBjb21wb25lbnQuJC5jc3Moe1xuICAgICAgXCJib3JkZXJcIjogYDJweCBzb2xpZCAke2NvbG9yfWAsXG4gICAgICBcInRyYW5zaXRpb25cIjogYGJvcmRlciAke2R1cmF0aW9ufW1zIGVhc2Utb3V0YFxuICAgIH0pO1xuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgY29tcG9uZW50LiQuY3NzKFwiYm9yZGVyXCIsIG9yaWdpbmFsQm9yZGVyIHx8IFwiXCIpO1xuICAgIH0sIGR1cmF0aW9uKTtcbiAgfVxuICBmdW5jdGlvbiBsb2dMaWZlY3ljbGUoY29tcG9uZW50LCBwaGFzZSwgc3RhdHVzKSB7XG4gICAgY29uc3QganFodG1sMiA9IGdldEpxaHRtbCQxKCk7XG4gICAgaWYgKCFqcWh0bWwyPy5kZWJ1ZylcbiAgICAgIHJldHVybjtcbiAgICBjb25zdCBzaG91bGRMb2cgPSBqcWh0bWwyLmRlYnVnLmxvZ0Z1bGxMaWZlY3ljbGUgfHwganFodG1sMi5kZWJ1Zy5sb2dDcmVhdGlvblJlYWR5ICYmIChwaGFzZSA9PT0gXCJjcmVhdGVcIiB8fCBwaGFzZSA9PT0gXCJyZWFkeVwiKTtcbiAgICBpZiAoIXNob3VsZExvZylcbiAgICAgIHJldHVybjtcbiAgICBjb25zdCBjb21wb25lbnROYW1lID0gY29tcG9uZW50LmNvbnN0cnVjdG9yLm5hbWU7XG4gICAgY29uc3QgdGltZXN0YW1wID0gKC8qIEBfX1BVUkVfXyAqLyBuZXcgRGF0ZSgpKS50b0lTT1N0cmluZygpO1xuICAgIGNvbnN0IHByZWZpeCA9IGBbSlFIVE1MICR7dGltZXN0YW1wfV1gO1xuICAgIGlmIChzdGF0dXMgPT09IFwic3RhcnRcIikge1xuICAgICAgY29uc29sZS5sb2coYCR7cHJlZml4fSAke2NvbXBvbmVudE5hbWV9IyR7Y29tcG9uZW50Ll9jaWR9IFxcdTIxOTIgJHtwaGFzZX0gc3RhcnRpbmcuLi5gKTtcbiAgICAgIGlmIChqcWh0bWwyLmRlYnVnLnByb2ZpbGVQZXJmb3JtYW5jZSkge1xuICAgICAgICBwZXJmb3JtYW5jZU1ldHJpY3Muc2V0KGAke2NvbXBvbmVudC5fY2lkfV8ke3BoYXNlfWAsIERhdGUubm93KCkpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgbWVzc2FnZSA9IGAke3ByZWZpeH0gJHtjb21wb25lbnROYW1lfSMke2NvbXBvbmVudC5fY2lkfSBcXHUyNzEzICR7cGhhc2V9IGNvbXBsZXRlYDtcbiAgICAgIGlmIChqcWh0bWwyLmRlYnVnLnByb2ZpbGVQZXJmb3JtYW5jZSkge1xuICAgICAgICBjb25zdCBzdGFydFRpbWUgPSBwZXJmb3JtYW5jZU1ldHJpY3MuZ2V0KGAke2NvbXBvbmVudC5fY2lkfV8ke3BoYXNlfWApO1xuICAgICAgICBpZiAoc3RhcnRUaW1lKSB7XG4gICAgICAgICAgY29uc3QgZHVyYXRpb24gPSBEYXRlLm5vdygpIC0gc3RhcnRUaW1lO1xuICAgICAgICAgIG1lc3NhZ2UgKz0gYCAoJHtkdXJhdGlvbn1tcylgO1xuICAgICAgICAgIGlmIChwaGFzZSA9PT0gXCJyZW5kZXJcIiAmJiBqcWh0bWwyLmRlYnVnLmhpZ2hsaWdodFNsb3dSZW5kZXJzICYmIGR1cmF0aW9uID4ganFodG1sMi5kZWJ1Zy5oaWdobGlnaHRTbG93UmVuZGVycykge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGAke3ByZWZpeH0gU0xPVyBSRU5ERVI6ICR7Y29tcG9uZW50TmFtZX0jJHtjb21wb25lbnQuX2NpZH0gdG9vayAke2R1cmF0aW9ufW1zYCk7XG4gICAgICAgICAgICBjb21wb25lbnQuJC5jc3MoXCJvdXRsaW5lXCIsIFwiMnB4IGRhc2hlZCByZWRcIik7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjb25zb2xlLmxvZyhtZXNzYWdlKTtcbiAgICAgIGlmIChqcWh0bWwyLmRlYnVnLmZsYXNoQ29tcG9uZW50cyAmJiAocGhhc2UgPT09IFwiY3JlYXRlXCIgfHwgcGhhc2UgPT09IFwicmVuZGVyXCIgfHwgcGhhc2UgPT09IFwicmVhZHlcIikpIHtcbiAgICAgICAgZmxhc2hDb21wb25lbnQoY29tcG9uZW50LCBwaGFzZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChqcWh0bWwyLmRlYnVnLnNob3dDb21wb25lbnRUcmVlKSB7XG4gICAgICB1cGRhdGVDb21wb25lbnRUcmVlKCk7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIGFwcGx5RGVidWdEZWxheShwaGFzZSkge1xuICAgIGNvbnN0IGpxaHRtbDIgPSBnZXRKcWh0bWwkMSgpO1xuICAgIGlmICghanFodG1sMj8uZGVidWcpXG4gICAgICByZXR1cm47XG4gICAgbGV0IGRlbGF5TXMgPSAwO1xuICAgIHN3aXRjaCAocGhhc2UpIHtcbiAgICAgIGNhc2UgXCJjb21wb25lbnRcIjpcbiAgICAgICAgZGVsYXlNcyA9IGpxaHRtbDIuZGVidWcuZGVsYXlBZnRlckNvbXBvbmVudCB8fCAwO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgXCJyZW5kZXJcIjpcbiAgICAgICAgZGVsYXlNcyA9IGpxaHRtbDIuZGVidWcuZGVsYXlBZnRlclJlbmRlciB8fCAwO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgXCJyZXJlbmRlclwiOlxuICAgICAgICBkZWxheU1zID0ganFodG1sMi5kZWJ1Zy5kZWxheUFmdGVyUmVyZW5kZXIgfHwgMDtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICAgIGlmIChkZWxheU1zID4gMCkge1xuICAgICAgY29uc29sZS5sb2coYFtKUUhUTUwgRGVidWddIEFwcGx5aW5nICR7ZGVsYXlNc31tcyBkZWxheSBhZnRlciAke3BoYXNlfWApO1xuICAgIH1cbiAgfVxuICBmdW5jdGlvbiB1cGRhdGVDb21wb25lbnRUcmVlKCkge1xuICAgIGNvbnNvbGUubG9nKFwiW0pRSFRNTCBUcmVlXSBDb21wb25lbnQgaGllcmFyY2h5IHVwZGF0ZWRcIik7XG4gIH1cbiAgdmFyIEpxaHRtbF9Db21wb25lbnQgPSBjbGFzcyBfSnFodG1sX0NvbXBvbmVudCB7XG4gICAgY29uc3RydWN0b3IoZWxlbWVudCwgYXJncyA9IHt9KSB7XG4gICAgICB0aGlzLmRhdGEgPSB7fTtcbiAgICAgIHRoaXMuX3JlYWR5X3N0YXRlID0gMDtcbiAgICAgIHRoaXMuX2luc3RhbnRpYXRvciA9IG51bGw7XG4gICAgICB0aGlzLl9kb21fcGFyZW50ID0gbnVsbDtcbiAgICAgIHRoaXMuX2RvbV9jaGlsZHJlbiA9IC8qIEBfX1BVUkVfXyAqLyBuZXcgU2V0KCk7XG4gICAgICB0aGlzLl91c2VfZG9tX2ZhbGxiYWNrID0gZmFsc2U7XG4gICAgICB0aGlzLl9zdG9wcGVkID0gZmFsc2U7XG4gICAgICB0aGlzLl9ib290ZWQgPSBmYWxzZTtcbiAgICAgIHRoaXMuX2RhdGFfYmVmb3JlX3JlbmRlciA9IG51bGw7XG4gICAgICB0aGlzLl9saWZlY3ljbGVfY2FsbGJhY2tzID0gLyogQF9fUFVSRV9fICovIG5ldyBNYXAoKTtcbiAgICAgIHRoaXMuX2xpZmVjeWNsZV9zdGF0ZXMgPSAvKiBAX19QVVJFX18gKi8gbmV3IFNldCgpO1xuICAgICAgdGhpcy5fX2xvYWRpbmcgPSBmYWxzZTtcbiAgICAgIHRoaXMuX2RpZF9maXJzdF9yZW5kZXIgPSBmYWxzZTtcbiAgICAgIHRoaXMuX3JlbmRlcl9jb3VudCA9IDA7XG4gICAgICB0aGlzLl9jaWQgPSB0aGlzLl9nZW5lcmF0ZV9jaWQoKTtcbiAgICAgIHRoaXMuX2xpZmVjeWNsZV9tYW5hZ2VyID0gTGlmZWN5Y2xlTWFuYWdlci5nZXRfaW5zdGFuY2UoKTtcbiAgICAgIGlmIChlbGVtZW50KSB7XG4gICAgICAgIHRoaXMuJCA9ICQoZWxlbWVudCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpO1xuICAgICAgICB0aGlzLiQgPSAkKGRpdik7XG4gICAgICB9XG4gICAgICBjb25zdCBkYXRhQXR0cnMgPSB7fTtcbiAgICAgIGlmICh0aGlzLiQubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zdCBkYXRhc2V0ID0gdGhpcy4kWzBdLmRhdGFzZXQgfHwge307XG4gICAgICAgIGZvciAoY29uc3Qga2V5IGluIGRhdGFzZXQpIHtcbiAgICAgICAgICBpZiAoa2V5ICE9PSBcImNpZFwiICYmIGtleSAhPT0gXCJ0aWRcIiAmJiBrZXkgIT09IFwiY29tcG9uZW50TmFtZVwiICYmIGtleSAhPT0gXCJyZWFkeVN0YXRlXCIpIHtcbiAgICAgICAgICAgIGNvbnN0IGRhdGFWYWx1ZSA9IHRoaXMuJC5kYXRhKGtleSk7XG4gICAgICAgICAgICBpZiAoZGF0YVZhbHVlICE9PSB2b2lkIDAgJiYgZGF0YVZhbHVlICE9PSBkYXRhc2V0W2tleV0pIHtcbiAgICAgICAgICAgICAgZGF0YUF0dHJzW2tleV0gPSBkYXRhVmFsdWU7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBkYXRhQXR0cnNba2V5XSA9IGRhdGFzZXRba2V5XTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGxldCB0ZW1wbGF0ZV9mb3JfYXJncztcbiAgICAgIGlmIChhcmdzLl9jb21wb25lbnRfbmFtZSkge1xuICAgICAgICB0ZW1wbGF0ZV9mb3JfYXJncyA9IGdldF90ZW1wbGF0ZShhcmdzLl9jb21wb25lbnRfbmFtZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0ZW1wbGF0ZV9mb3JfYXJncyA9IGdldF90ZW1wbGF0ZV9ieV9jbGFzcyh0aGlzLmNvbnN0cnVjdG9yKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGRlZmluZUFyZ3MgPSB0ZW1wbGF0ZV9mb3JfYXJncz8uZGVmaW5lQXJncyB8fCB7fTtcbiAgICAgIHRoaXMuYXJncyA9IHsgLi4uZGVmaW5lQXJncywgLi4uZGF0YUF0dHJzLCAuLi5hcmdzIH07XG4gICAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyh0aGlzLmFyZ3MpKSB7XG4gICAgICAgIGlmIChrZXkgPT09IFwiY2lkXCIgfHwga2V5ID09PSBcInRpZFwiIHx8IGtleSA9PT0gXCJjb21wb25lbnROYW1lXCIgfHwga2V5ID09PSBcInJlYWR5U3RhdGVcIiB8fCBrZXkuc3RhcnRzV2l0aChcIl9cIikpIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiIHx8IHR5cGVvZiB2YWx1ZSA9PT0gXCJudW1iZXJcIikge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBjdXJyZW50QXR0ciA9IHRoaXMuJC5hdHRyKGBkYXRhLSR7a2V5fWApO1xuICAgICAgICAgICAgaWYgKGN1cnJlbnRBdHRyICE9IHZhbHVlKSB7XG4gICAgICAgICAgICAgIHRoaXMuJC5hdHRyKGBkYXRhLSR7a2V5fWAsIFN0cmluZyh2YWx1ZSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHRoaXMuJC5kYXRhKFwiX2NvbXBvbmVudFwiLCB0aGlzKTtcbiAgICAgIHRoaXMuX2FwcGx5X2Nzc19jbGFzc2VzKCk7XG4gICAgICB0aGlzLl9hcHBseV9kZWZhdWx0X2F0dHJpYnV0ZXMoKTtcbiAgICAgIHRoaXMuX3NldF9hdHRyaWJ1dGVzKCk7XG4gICAgICB0aGlzLl9maW5kX2RvbV9wYXJlbnQoKTtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJjb25zdHJ1Y3RcIiwgXCJjb21wbGV0ZVwiKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQm9vdCAtIFN0YXJ0IHRoZSBmdWxsIGNvbXBvbmVudCBsaWZlY3ljbGVcbiAgICAgKiBDYWxsZWQgaW1tZWRpYXRlbHkgYWZ0ZXIgY29uc3RydWN0aW9uIGJ5IGluc3RydWN0aW9uIHByb2Nlc3NvclxuICAgICAqL1xuICAgIGFzeW5jIGJvb3QoKSB7XG4gICAgICBpZiAodGhpcy5fYm9vdGVkKVxuICAgICAgICByZXR1cm47XG4gICAgICB0aGlzLl9ib290ZWQgPSB0cnVlO1xuICAgICAgYXdhaXQgdGhpcy5fbGlmZWN5Y2xlX21hbmFnZXIuYm9vdF9jb21wb25lbnQodGhpcyk7XG4gICAgfVxuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvLyBMaWZlY3ljbGUgTWV0aG9kcyAoY2FsbGVkIGJ5IExpZmVjeWNsZU1hbmFnZXIpXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8qKlxuICAgICAqIEludGVybmFsIHJlbmRlciBwaGFzZSAtIENyZWF0ZSBET00gc3RydWN0dXJlXG4gICAgICogQ2FsbGVkIHRvcC1kb3duIChwYXJlbnQgYmVmb3JlIGNoaWxkcmVuKSB3aGVuIHBhcnQgb2YgbGlmZWN5Y2xlXG4gICAgICogVGhpcyBpcyBhbiBpbnRlcm5hbCBtZXRob2QgLSB1c2VycyBzaG91bGQgY2FsbCByZW5kZXIoKSBpbnN0ZWFkXG4gICAgICpcbiAgICAgKiBAcGFyYW0gaWQgT3B0aW9uYWwgc2NvcGVkIElEIC0gaWYgcHJvdmlkZWQsIGRlbGVnYXRlcyB0byBjaGlsZCBjb21wb25lbnQncyBfcmVuZGVyKClcbiAgICAgKiBAcmV0dXJucyBUaGUgY3VycmVudCBfcmVuZGVyX2NvdW50IGFmdGVyIGluY3JlbWVudGluZyAodXNlZCB0byBkZXRlY3Qgc3RhbGUgcmVuZGVycylcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqL1xuICAgIF9yZW5kZXIoaWQgPSBudWxsKSB7XG4gICAgICB0aGlzLl9yZW5kZXJfY291bnQrKztcbiAgICAgIGNvbnN0IGN1cnJlbnRfcmVuZGVyX2lkID0gdGhpcy5fcmVuZGVyX2NvdW50O1xuICAgICAgaWYgKHRoaXMuX3N0b3BwZWQpXG4gICAgICAgIHJldHVybiBjdXJyZW50X3JlbmRlcl9pZDtcbiAgICAgIGlmIChpZCkge1xuICAgICAgICBjb25zdCAkZWxlbWVudCA9IHRoaXMuJGlkKGlkKTtcbiAgICAgICAgaWYgKCRlbGVtZW50Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgW0pRSFRNTF0gcmVuZGVyKFwiJHtpZH1cIikgLSBubyBzdWNoIGlkLlxuQ29tcG9uZW50IFwiJHt0aGlzLmNvbXBvbmVudF9uYW1lKCl9XCIgaGFzIG5vIGNoaWxkIGVsZW1lbnQgd2l0aCAkaWQ9XCIke2lkfVwiLmApO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGNoaWxkID0gJGVsZW1lbnQuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgIGlmICghY2hpbGQpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFtKUUhUTUxdIHJlbmRlcihcIiR7aWR9XCIpIC0gZWxlbWVudCBpcyBub3QgYSBjb21wb25lbnQgb3IgZG9lcyBub3QgaGF2ZSAkcmVkcmF3YWJsZSBhdHRyaWJ1dGUgc2V0LlxuRWxlbWVudCB3aXRoICRpZD1cIiR7aWR9XCIgZXhpc3RzIGJ1dCBpcyBub3QgaW5pdGlhbGl6ZWQgYXMgYSBjb21wb25lbnQuXG5BZGQgJHJlZHJhd2FibGUgYXR0cmlidXRlIG9yIG1ha2UgaXQgYSBwcm9wZXIgY29tcG9uZW50LmApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBjaGlsZC5fcmVuZGVyKCk7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5fX2xvYWRpbmcpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBbSlFIVE1MXSBDb21wb25lbnQgXCIke3RoaXMuY29tcG9uZW50X25hbWUoKX1cIiBhdHRlbXB0ZWQgdG8gY2FsbCByZW5kZXIoKSBkdXJpbmcgb25fbG9hZCgpLlxub25fbG9hZCgpIHNob3VsZCBPTkxZIG1vZGlmeSB0aGlzLmRhdGEuIERPTSB1cGRhdGVzIGhhcHBlbiBhdXRvbWF0aWNhbGx5IGFmdGVyIG9uX2xvYWQoKSBjb21wbGV0ZXMuXG5cbkZpeDogUmVtb3ZlIHRoZSB0aGlzLnJlbmRlcigpIGNhbGwgZnJvbSBvbl9sb2FkKCkuXG5UaGUgZnJhbWV3b3JrIHdpbGwgYXV0b21hdGljYWxseSByZS1yZW5kZXIgaWYgdGhpcy5kYXRhIGNoYW5nZXMgZHVyaW5nIG9uX2xvYWQoKS5gKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJyZW5kZXJcIiwgXCJzdGFydFwiKTtcbiAgICAgIGlmICghJC5jb250YWlucyhkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQsIHRoaXMuJFswXSkpIHtcbiAgICAgICAgdGhpcy5fdXNlX2RvbV9mYWxsYmFjayA9IHRydWU7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl91c2VfZG9tX2ZhbGxiYWNrID0gZmFsc2U7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5fZGlkX2ZpcnN0X3JlbmRlcikge1xuICAgICAgICB0aGlzLiQuZmluZChcIi5KcWh0bWxfQ29tcG9uZW50XCIpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgICAgY29uc3QgY2hpbGQgPSAkKHRoaXMpLmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICAgIGlmIChjaGlsZCAmJiAhY2hpbGQuX3N0b3BwZWQpIHtcbiAgICAgICAgICAgIGNoaWxkLl9zdG9wKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy4kWzBdLmlubmVySFRNTCA9IFwiXCI7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl9kaWRfZmlyc3RfcmVuZGVyID0gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIHRoaXMuJC5yZW1vdmVDbGFzcyhcIl9Db21wb25lbnRfU3RvcHBlZFwiKTtcbiAgICAgIGlmICh0aGlzLl9kYXRhX2JlZm9yZV9yZW5kZXIgPT09IG51bGwpIHtcbiAgICAgICAgdGhpcy5fZGF0YV9iZWZvcmVfcmVuZGVyID0gSlNPTi5zdHJpbmdpZnkodGhpcy5kYXRhKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuX2RvbV9jaGlsZHJlbi5jbGVhcigpO1xuICAgICAgbGV0IHRlbXBsYXRlX2RlZjtcbiAgICAgIGlmICh0aGlzLmFyZ3MuX2NvbXBvbmVudF9uYW1lKSB7XG4gICAgICAgIHRlbXBsYXRlX2RlZiA9IGdldF90ZW1wbGF0ZSh0aGlzLmFyZ3MuX2NvbXBvbmVudF9uYW1lKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRlbXBsYXRlX2RlZiA9IGdldF90ZW1wbGF0ZV9ieV9jbGFzcyh0aGlzLmNvbnN0cnVjdG9yKTtcbiAgICAgIH1cbiAgICAgIGlmICh0ZW1wbGF0ZV9kZWYgJiYgdGVtcGxhdGVfZGVmLnJlbmRlcikge1xuICAgICAgICBjb25zdCBqcWh0bWwyID0ge1xuICAgICAgICAgIGVzY2FwZV9odG1sOiAoc3RyKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpO1xuICAgICAgICAgICAgZGl2LnRleHRDb250ZW50ID0gU3RyaW5nKHN0cik7XG4gICAgICAgICAgICByZXR1cm4gZGl2LmlubmVySFRNTDtcbiAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIGNvbnN0IGRlZmF1bHRDb250ZW50ID0gKCkgPT4gXCJcIjtcbiAgICAgICAgbGV0IFtpbnN0cnVjdGlvbnMsIGNvbnRleHRdID0gdGVtcGxhdGVfZGVmLnJlbmRlci5iaW5kKHRoaXMpKFxuICAgICAgICAgIHRoaXMuZGF0YSxcbiAgICAgICAgICB0aGlzLmFyZ3MsXG4gICAgICAgICAgdGhpcy5hcmdzLl9pbm5lcmh0bWxfZnVuY3Rpb24gfHwgZGVmYXVsdENvbnRlbnQsXG4gICAgICAgICAgLy8gQ29udGVudCBmdW5jdGlvbiB3aXRoIGZhbGxiYWNrXG4gICAgICAgICAganFodG1sMlxuICAgICAgICAgIC8vIFV0aWxpdGllcyBvYmplY3RcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKGluc3RydWN0aW9ucyAmJiB0eXBlb2YgaW5zdHJ1Y3Rpb25zID09PSBcIm9iamVjdFwiICYmIGluc3RydWN0aW9ucy5fc2xvdHMgJiYgIUFycmF5LmlzQXJyYXkoaW5zdHJ1Y3Rpb25zKSkge1xuICAgICAgICAgIGNvbnN0IGNvbXBvbmVudE5hbWUgPSB0ZW1wbGF0ZV9kZWYubmFtZSB8fCB0aGlzLmFyZ3MuX2NvbXBvbmVudF9uYW1lIHx8IHRoaXMuY29uc3RydWN0b3IubmFtZTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gU2xvdC1vbmx5IHRlbXBsYXRlIGRldGVjdGVkIGZvciAke2NvbXBvbmVudE5hbWV9YCk7XG4gICAgICAgICAgbGV0IHBhcmVudFRlbXBsYXRlID0gbnVsbDtcbiAgICAgICAgICBsZXQgcGFyZW50VGVtcGxhdGVOYW1lID0gbnVsbDtcbiAgICAgICAgICBpZiAodGVtcGxhdGVfZGVmLmV4dGVuZHMpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSAgIFVzaW5nIGV4cGxpY2l0IGV4dGVuZHM6ICR7dGVtcGxhdGVfZGVmLmV4dGVuZHN9YCk7XG4gICAgICAgICAgICBwYXJlbnRUZW1wbGF0ZSA9IGdldF90ZW1wbGF0ZSh0ZW1wbGF0ZV9kZWYuZXh0ZW5kcyk7XG4gICAgICAgICAgICBwYXJlbnRUZW1wbGF0ZU5hbWUgPSB0ZW1wbGF0ZV9kZWYuZXh0ZW5kcztcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKCFwYXJlbnRUZW1wbGF0ZSkge1xuICAgICAgICAgICAgbGV0IGN1cnJlbnRDbGFzcyA9IE9iamVjdC5nZXRQcm90b3R5cGVPZih0aGlzLmNvbnN0cnVjdG9yKTtcbiAgICAgICAgICAgIHdoaWxlIChjdXJyZW50Q2xhc3MgJiYgY3VycmVudENsYXNzLm5hbWUgIT09IFwiT2JqZWN0XCIgJiYgY3VycmVudENsYXNzLm5hbWUgIT09IFwiSnFodG1sX0NvbXBvbmVudFwiKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGNsYXNzTmFtZSA9IGN1cnJlbnRDbGFzcy5uYW1lO1xuICAgICAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gICBDaGVja2luZyBwYXJlbnQ6ICR7Y2xhc3NOYW1lfWApO1xuICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGNsYXNzVGVtcGxhdGUgPSBnZXRfdGVtcGxhdGUoY2xhc3NOYW1lKTtcbiAgICAgICAgICAgICAgICBpZiAoY2xhc3NUZW1wbGF0ZSAmJiBjbGFzc1RlbXBsYXRlLm5hbWUgIT09IFwiSnFodG1sX0NvbXBvbmVudFwiKSB7XG4gICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gICBGb3VuZCBwYXJlbnQgdGVtcGxhdGU6ICR7Y2xhc3NOYW1lfWApO1xuICAgICAgICAgICAgICAgICAgcGFyZW50VGVtcGxhdGUgPSBjbGFzc1RlbXBsYXRlO1xuICAgICAgICAgICAgICAgICAgcGFyZW50VGVtcGxhdGVOYW1lID0gY2xhc3NOYW1lO1xuICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gRXJyb3IgZmluZGluZyBwYXJlbnQgdGVtcGxhdGUgJHtjbGFzc05hbWV9OmAsIGVycm9yKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjdXJyZW50Q2xhc3MgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YoY3VycmVudENsYXNzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHBhcmVudFRlbXBsYXRlKSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBjb25zdCBjaGlsZFNsb3RzID0gaW5zdHJ1Y3Rpb25zLl9zbG90cztcbiAgICAgICAgICAgICAgY29uc3QgY29udGVudEZ1bmN0aW9uID0gKHNsb3ROYW1lLCBkYXRhKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKGNoaWxkU2xvdHNbc2xvdE5hbWVdICYmIHR5cGVvZiBjaGlsZFNsb3RzW3Nsb3ROYW1lXSA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCBbc2xvdEluc3RydWN0aW9ucywgc2xvdENvbnRleHRdID0gY2hpbGRTbG90c1tzbG90TmFtZV0oZGF0YSk7XG4gICAgICAgICAgICAgICAgICByZXR1cm4gW3Nsb3RJbnN0cnVjdGlvbnMsIHNsb3RDb250ZXh0XTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIFwiXCI7XG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIGNvbnN0IFtwYXJlbnRJbnN0cnVjdGlvbnMsIHBhcmVudENvbnRleHRdID0gcGFyZW50VGVtcGxhdGUucmVuZGVyLmJpbmQodGhpcykoXG4gICAgICAgICAgICAgICAgdGhpcy5kYXRhLFxuICAgICAgICAgICAgICAgIHRoaXMuYXJncyxcbiAgICAgICAgICAgICAgICBjb250ZW50RnVuY3Rpb24sXG4gICAgICAgICAgICAgICAgLy8gUGFzcyBjb250ZW50IGZ1bmN0aW9uIHRoYXQgaW52b2tlcyBjaGlsZCBzbG90c1xuICAgICAgICAgICAgICAgIGpxaHRtbDJcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdICAgUGFyZW50IHRlbXBsYXRlIGludm9rZWQgc3VjY2Vzc2Z1bGx5YCk7XG4gICAgICAgICAgICAgIGluc3RydWN0aW9ucyA9IHBhcmVudEluc3RydWN0aW9ucztcbiAgICAgICAgICAgICAgY29udGV4dCA9IHBhcmVudENvbnRleHQ7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIEVycm9yIGludm9raW5nIHBhcmVudCB0ZW1wbGF0ZSAke3BhcmVudFRlbXBsYXRlTmFtZX06YCwgZXJyb3IpO1xuICAgICAgICAgICAgICBpbnN0cnVjdGlvbnMgPSBbXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBObyBwYXJlbnQgdGVtcGxhdGUgZm91bmQgZm9yICR7dGhpcy5jb25zdHJ1Y3Rvci5uYW1lfSwgcmVuZGVyaW5nIGVtcHR5YCk7XG4gICAgICAgICAgICBpbnN0cnVjdGlvbnMgPSBbXTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgZmxhdHRlbmVkSW5zdHJ1Y3Rpb25zID0gdGhpcy5fZmxhdHRlbl9pbnN0cnVjdGlvbnMoaW5zdHJ1Y3Rpb25zKTtcbiAgICAgICAgcHJvY2Vzc19pbnN0cnVjdGlvbnMoZmxhdHRlbmVkSW5zdHJ1Y3Rpb25zLCB0aGlzLiQsIHRoaXMpO1xuICAgICAgfVxuICAgICAgdGhpcy5fdXBkYXRlX2RlYnVnX2F0dHJzKCk7XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwicmVuZGVyXCIsIFwiY29tcGxldGVcIik7XG4gICAgICBjb25zdCByZW5kZXJSZXN1bHQgPSB0aGlzLm9uX3JlbmRlcigpO1xuICAgICAgaWYgKHJlbmRlclJlc3VsdCAmJiB0eXBlb2YgcmVuZGVyUmVzdWx0LnRoZW4gPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIENvbXBvbmVudCBcIiR7dGhpcy5jb21wb25lbnRfbmFtZSgpfVwiIHJldHVybmVkIGEgUHJvbWlzZSBmcm9tIG9uX3JlbmRlcigpLiBvbl9yZW5kZXIoKSBtdXN0IGJlIHN5bmNocm9ub3VzIGNvZGUuIFJlbW92ZSAnYXN5bmMnIGZyb20gdGhlIGZ1bmN0aW9uIGRlY2xhcmF0aW9uLmApO1xuICAgICAgfVxuICAgICAgdGhpcy50cmlnZ2VyKFwicmVuZGVyXCIpO1xuICAgICAgY29uc3QgaXNSZXJlbmRlciA9IHRoaXMuX3JlYWR5X3N0YXRlID49IDM7XG4gICAgICBhcHBseURlYnVnRGVsYXkoaXNSZXJlbmRlciA/IFwicmVyZW5kZXJcIiA6IFwicmVuZGVyXCIpO1xuICAgICAgcmV0dXJuIGN1cnJlbnRfcmVuZGVyX2lkO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBQdWJsaWMgcmVuZGVyIG1ldGhvZCAtIHJlLXJlbmRlcnMgY29tcG9uZW50IGFuZCBjb21wbGV0ZXMgbGlmZWN5Y2xlXG4gICAgICogVGhpcyBpcyB3aGF0IHVzZXJzIHNob3VsZCBjYWxsIHdoZW4gdGhleSB3YW50IHRvIHVwZGF0ZSBhIGNvbXBvbmVudC5cbiAgICAgKlxuICAgICAqIExpZmVjeWNsZSBzZXF1ZW5jZTpcbiAgICAgKiAxLiBfcmVuZGVyKCkgLSBVcGRhdGVzIERPTSBzeW5jaHJvbm91c2x5LCBjYWxscyBvbl9yZW5kZXIoKSwgZmlyZXMgJ3JlbmRlcicgZXZlbnRcbiAgICAgKiAyLiBBc3luYyBjb250aW51YXRpb24gKGZpcmUgYW5kIGZvcmdldCk6XG4gICAgICogICAgLSBfd2FpdF9mb3JfY2hpbGRyZW5fcmVhZHkoKSAtIFdhaXRzIGZvciBhbGwgY2hpbGRyZW4gdG8gcmVhY2ggcmVhZHkgc3RhdGVcbiAgICAgKiAgICAtIG9uX3JlYWR5KCkgLSBDYWxscyB1c2VyJ3MgcmVhZHkgaG9va1xuICAgICAqICAgIC0gdHJpZ2dlcigncmVhZHknKSAtIEZpcmVzIHJlYWR5IGV2ZW50XG4gICAgICpcbiAgICAgKiBSZXR1cm5zIGltbWVkaWF0ZWx5IGFmdGVyIF9yZW5kZXIoKSBjb21wbGV0ZXMgLSBkb2VzIE5PVCB3YWl0IGZvciBjaGlsZHJlblxuICAgICAqL1xuICAgIHJlbmRlcihpZCA9IG51bGwpIHtcbiAgICAgIGlmICh0aGlzLl9zdG9wcGVkKVxuICAgICAgICByZXR1cm47XG4gICAgICBpZiAoaWQpIHtcbiAgICAgICAgY29uc3QgJGVsZW1lbnQgPSB0aGlzLiRpZChpZCk7XG4gICAgICAgIGlmICgkZWxlbWVudC5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFtKUUhUTUxdIHJlbmRlcihcIiR7aWR9XCIpIC0gbm8gc3VjaCBpZC5cbkNvbXBvbmVudCBcIiR7dGhpcy5jb21wb25lbnRfbmFtZSgpfVwiIGhhcyBubyBjaGlsZCBlbGVtZW50IHdpdGggJGlkPVwiJHtpZH1cIi5gKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBjaGlsZCA9ICRlbGVtZW50LmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICBpZiAoIWNoaWxkKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBbSlFIVE1MXSByZW5kZXIoXCIke2lkfVwiKSAtIGVsZW1lbnQgaXMgbm90IGEgY29tcG9uZW50IG9yIGRvZXMgbm90IGhhdmUgJHJlZHJhd2FibGUgYXR0cmlidXRlIHNldC5cbkVsZW1lbnQgd2l0aCAkaWQ9XCIke2lkfVwiIGV4aXN0cyBidXQgaXMgbm90IGluaXRpYWxpemVkIGFzIGEgY29tcG9uZW50LlxuQWRkICRyZWRyYXdhYmxlIGF0dHJpYnV0ZSBvciBtYWtlIGl0IGEgcHJvcGVyIGNvbXBvbmVudC5gKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gY2hpbGQucmVuZGVyKCk7XG4gICAgICB9XG4gICAgICBjb25zdCByZW5kZXJfaWQgPSB0aGlzLl9yZW5kZXIoKTtcbiAgICAgIChhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IHRoaXMuX3dhaXRfZm9yX2NoaWxkcmVuX3JlYWR5KCk7XG4gICAgICAgIGlmICh0aGlzLl9yZW5kZXJfY291bnQgIT09IHJlbmRlcl9pZCkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCB0aGlzLm9uX3JlYWR5KCk7XG4gICAgICAgIGF3YWl0IHRoaXMudHJpZ2dlcihcInJlYWR5XCIpO1xuICAgICAgfSkoKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQWxpYXMgZm9yIHJlbmRlcigpIC0gcmUtcmVuZGVycyBjb21wb25lbnQgd2l0aCBjdXJyZW50IGRhdGFcbiAgICAgKiBQcm92aWRlZCBmb3IgQVBJIGNvbnNpc3RlbmN5IGFuZCBjbGFyaXR5XG4gICAgICovXG4gICAgcmVkcmF3KGlkID0gbnVsbCkge1xuICAgICAgcmV0dXJuIHRoaXMucmVuZGVyKGlkKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ3JlYXRlIHBoYXNlIC0gUXVpY2sgc2V0dXAsIHByZXBhcmUgVUlcbiAgICAgKiBDYWxsZWQgYm90dG9tLXVwIChjaGlsZHJlbiBiZWZvcmUgcGFyZW50KVxuICAgICAqL1xuICAgIGFzeW5jIGNyZWF0ZSgpIHtcbiAgICAgIGlmICh0aGlzLl9zdG9wcGVkIHx8IHRoaXMuX3JlYWR5X3N0YXRlID49IDEpXG4gICAgICAgIHJldHVybjtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJjcmVhdGVcIiwgXCJzdGFydFwiKTtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMub25fY3JlYXRlKCk7XG4gICAgICBpZiAocmVzdWx0ICYmIHR5cGVvZiByZXN1bHQudGhlbiA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gQ29tcG9uZW50IFwiJHt0aGlzLmNvbXBvbmVudF9uYW1lKCl9XCIgcmV0dXJuZWQgYSBQcm9taXNlIGZyb20gb25fY3JlYXRlKCkuIG9uX2NyZWF0ZSgpIG11c3QgYmUgc3luY2hyb25vdXMgY29kZS4gUmVtb3ZlICdhc3luYycgZnJvbSB0aGUgZnVuY3Rpb24gZGVjbGFyYXRpb24uYCk7XG4gICAgICAgIGF3YWl0IHJlc3VsdDtcbiAgICAgIH1cbiAgICAgIHRoaXMuX3JlYWR5X3N0YXRlID0gMTtcbiAgICAgIHRoaXMuX3VwZGF0ZV9kZWJ1Z19hdHRycygpO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcImNyZWF0ZVwiLCBcImNvbXBsZXRlXCIpO1xuICAgICAgdGhpcy50cmlnZ2VyKFwiY3JlYXRlXCIpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBMb2FkIHBoYXNlIC0gRmV0Y2ggZGF0YSBmcm9tIEFQSXNcbiAgICAgKiBDYWxsZWQgYm90dG9tLXVwLCBmdWxseSBwYXJhbGxlbFxuICAgICAqIE5PIERPTSBNT0RJRklDQVRJT05TIEFMTE9XRUQgSU4gVEhJUyBQSEFTRVxuICAgICAqL1xuICAgIGFzeW5jIGxvYWQoKSB7XG4gICAgICBpZiAodGhpcy5fc3RvcHBlZCB8fCB0aGlzLl9yZWFkeV9zdGF0ZSA+PSAyKVxuICAgICAgICByZXR1cm47XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwibG9hZFwiLCBcInN0YXJ0XCIpO1xuICAgICAgY29uc3QgYXJnc0JlZm9yZUxvYWQgPSBKU09OLnN0cmluZ2lmeSh0aGlzLmFyZ3MpO1xuICAgICAgY29uc3QgcHJvcGVydGllc0JlZm9yZUxvYWQgPSBuZXcgU2V0KE9iamVjdC5rZXlzKHRoaXMpKTtcbiAgICAgIHRoaXMuX19sb2FkaW5nID0gdHJ1ZTtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMub25fbG9hZCgpO1xuICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgdGhpcy5fX2xvYWRpbmcgPSBmYWxzZTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGFyZ3NBZnRlckxvYWQgPSBKU09OLnN0cmluZ2lmeSh0aGlzLmFyZ3MpO1xuICAgICAgY29uc3QgcHJvcGVydGllc0FmdGVyTG9hZCA9IE9iamVjdC5rZXlzKHRoaXMpO1xuICAgICAgaWYgKGFyZ3NCZWZvcmVMb2FkICE9PSBhcmdzQWZ0ZXJMb2FkKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYFtKUUhUTUxdIFdBUk5JTkc6IENvbXBvbmVudCBcIiR7dGhpcy5jb21wb25lbnRfbmFtZSgpfVwiIG1vZGlmaWVkIHRoaXMuYXJncyBpbiBvbl9sb2FkKCkuXG5vbl9sb2FkKCkgc2hvdWxkIE9OTFkgbW9kaWZ5IHRoaXMuZGF0YS4gVGhlIHRoaXMuYXJncyBwcm9wZXJ0eSBpcyByZWFkLW9ubHkuXG5cbkJlZm9yZTogJHthcmdzQmVmb3JlTG9hZH1cbkFmdGVyOiAgJHthcmdzQWZ0ZXJMb2FkfVxuXG5GaXg6IE1vdmUgeW91ciBtb2RpZmljYXRpb25zIHRvIHRoaXMuZGF0YSBpbnN0ZWFkLmApO1xuICAgICAgfVxuICAgICAgY29uc3QgbmV3UHJvcGVydGllcyA9IHByb3BlcnRpZXNBZnRlckxvYWQuZmlsdGVyKChwcm9wKSA9PiAhcHJvcGVydGllc0JlZm9yZUxvYWQuaGFzKHByb3ApICYmIHByb3AgIT09IFwiZGF0YVwiKTtcbiAgICAgIGlmIChuZXdQcm9wZXJ0aWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihgW0pRSFRNTF0gV0FSTklORzogQ29tcG9uZW50IFwiJHt0aGlzLmNvbXBvbmVudF9uYW1lKCl9XCIgYWRkZWQgbmV3IHByb3BlcnRpZXMgaW4gb25fbG9hZCgpLlxub25fbG9hZCgpIHNob3VsZCBPTkxZIG1vZGlmeSB0aGlzLmRhdGEuIE5ldyBwcm9wZXJ0aWVzIGRldGVjdGVkOiAke25ld1Byb3BlcnRpZXMuam9pbihcIiwgXCIpfVxuXG5GaXg6IFN0b3JlIHlvdXIgZGF0YSBpbiB0aGlzLmRhdGEgaW5zdGVhZDpcbiAgXFx1Mjc0QyB0aGlzLiR7bmV3UHJvcGVydGllc1swXX0gPSB2YWx1ZTtcbiAgXFx1MjcwNSB0aGlzLmRhdGEuJHtuZXdQcm9wZXJ0aWVzWzBdfSA9IHZhbHVlO2ApO1xuICAgICAgfVxuICAgICAgdGhpcy5fcmVhZHlfc3RhdGUgPSAyO1xuICAgICAgdGhpcy5fdXBkYXRlX2RlYnVnX2F0dHJzKCk7XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwibG9hZFwiLCBcImNvbXBsZXRlXCIpO1xuICAgICAgdGhpcy50cmlnZ2VyKFwibG9hZFwiKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVhZHkgcGhhc2UgLSBDb21wb25lbnQgZnVsbHkgaW5pdGlhbGl6ZWRcbiAgICAgKiBDYWxsZWQgYm90dG9tLXVwIChjaGlsZHJlbiBiZWZvcmUgcGFyZW50KVxuICAgICAqL1xuICAgIGFzeW5jIHJlYWR5KCkge1xuICAgICAgaWYgKHRoaXMuX3N0b3BwZWQgfHwgdGhpcy5fcmVhZHlfc3RhdGUgPj0gNClcbiAgICAgICAgcmV0dXJuO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcInJlYWR5XCIsIFwic3RhcnRcIik7XG4gICAgICBhd2FpdCB0aGlzLl93YWl0X2Zvcl9jaGlsZHJlbl9yZWFkeSgpO1xuICAgICAgYXdhaXQgdGhpcy5vbl9yZWFkeSgpO1xuICAgICAgdGhpcy5fcmVhZHlfc3RhdGUgPSA0O1xuICAgICAgdGhpcy5fdXBkYXRlX2RlYnVnX2F0dHJzKCk7XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwicmVhZHlcIiwgXCJjb21wbGV0ZVwiKTtcbiAgICAgIHRoaXMudHJpZ2dlcihcInJlYWR5XCIpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBXYWl0IGZvciBhbGwgY2hpbGQgY29tcG9uZW50cyB0byByZWFjaCByZWFkeSBzdGF0ZVxuICAgICAqIEVuc3VyZXMgYm90dG9tLXVwIG9yZGVyaW5nIChjaGlsZHJlbiByZWFkeSBiZWZvcmUgcGFyZW50KVxuICAgICAqIEBwcml2YXRlXG4gICAgICovXG4gICAgYXN5bmMgX3dhaXRfZm9yX2NoaWxkcmVuX3JlYWR5KCkge1xuICAgICAgY29uc3QgY2hpbGRyZW4gPSB0aGlzLl9nZXRfZG9tX2NoaWxkcmVuKCk7XG4gICAgICBpZiAoY2hpbGRyZW4ubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHJlYWR5X3Byb21pc2VzID0gW107XG4gICAgICBmb3IgKGNvbnN0IGNoaWxkIG9mIGNoaWxkcmVuKSB7XG4gICAgICAgIGlmIChjaGlsZC5fcmVhZHlfc3RhdGUgPj0gNCkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHJlYWR5X3Byb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgIGNoaWxkLm9uKFwicmVhZHlcIiwgKCkgPT4gcmVzb2x2ZSgpKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJlYWR5X3Byb21pc2VzLnB1c2gocmVhZHlfcHJvbWlzZSk7XG4gICAgICB9XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChyZWFkeV9wcm9taXNlcyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlaW5pdGlhbGl6ZSB0aGUgY29tcG9uZW50IC0gZnVsbCByZXNldCBhbmQgcmUtaW5pdGlhbGl6YXRpb25cbiAgICAgKiBXaXBlcyB0aGUgaW5uZXJIVE1MLCByZXNldHMgZGF0YSB0byBlbXB0eSwgYW5kIHJ1bnMgZnVsbCBsaWZlY3ljbGVcbiAgICAgKi9cbiAgICBhc3luYyByZWluaXRpYWxpemUoKSB7XG4gICAgICBpZiAodGhpcy5fc3RvcHBlZClcbiAgICAgICAgcmV0dXJuO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcInJlaW5pdGlhbGl6ZVwiLCBcInN0YXJ0XCIpO1xuICAgICAgdGhpcy4kWzBdLmlubmVySFRNTCA9IFwiXCI7XG4gICAgICB0aGlzLmRhdGEgPSB7fTtcbiAgICAgIHRoaXMuX3JlYWR5X3N0YXRlID0gMDtcbiAgICAgIHRoaXMuX2RhdGFfYmVmb3JlX3JlbmRlciA9IG51bGw7XG4gICAgICB0aGlzLl9kb21fY2hpbGRyZW4uY2xlYXIoKTtcbiAgICAgIGF3YWl0IHRoaXMuX3JlbmRlcigpO1xuICAgICAgYXdhaXQgdGhpcy5jcmVhdGUoKTtcbiAgICAgIGF3YWl0IHRoaXMubG9hZCgpO1xuICAgICAgaWYgKHRoaXMuc2hvdWxkX3JlcmVuZGVyKCkpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5fcmVuZGVyKCk7XG4gICAgICB9XG4gICAgICBhd2FpdCB0aGlzLnJlYWR5KCk7XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwicmVpbml0aWFsaXplXCIsIFwiY29tcGxldGVcIik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlbG9hZCBjb21wb25lbnQgLSByZS1mZXRjaCBkYXRhIGFuZCByZS1yZW5kZXJcbiAgICAgKiBSZS1ydW5zIG9uX2xvYWQoKSwgYWx3YXlzIHJlbmRlcnMsIGFuZCBjYWxscyBvbl9yZWFkeSgpXG4gICAgICovXG4gICAgYXN5bmMgcmVsb2FkKCkge1xuICAgICAgaWYgKHRoaXMuX3N0b3BwZWQpXG4gICAgICAgIHJldHVybjtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJyZWxvYWRcIiwgXCJzdGFydFwiKTtcbiAgICAgIGNvbnN0IGhhc19jdXN0b21fb25fbG9hZCA9IHRoaXMub25fbG9hZCAhPT0gX0pxaHRtbF9Db21wb25lbnQucHJvdG90eXBlLm9uX2xvYWQ7XG4gICAgICBpZiAoaGFzX2N1c3RvbV9vbl9sb2FkKSB7XG4gICAgICAgIGNvbnN0IGFyZ3NCZWZvcmVMb2FkID0gSlNPTi5zdHJpbmdpZnkodGhpcy5hcmdzKTtcbiAgICAgICAgY29uc3QgcHJvcGVydGllc0JlZm9yZUxvYWQgPSBuZXcgU2V0KE9iamVjdC5rZXlzKHRoaXMpKTtcbiAgICAgICAgdGhpcy5fX2xvYWRpbmcgPSB0cnVlO1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IHRoaXMub25fbG9hZCgpO1xuICAgICAgICB9IGZpbmFsbHkge1xuICAgICAgICAgIHRoaXMuX19sb2FkaW5nID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgYXJnc0FmdGVyTG9hZCA9IEpTT04uc3RyaW5naWZ5KHRoaXMuYXJncyk7XG4gICAgICAgIGNvbnN0IHByb3BlcnRpZXNBZnRlckxvYWQgPSBPYmplY3Qua2V5cyh0aGlzKTtcbiAgICAgICAgaWYgKGFyZ3NCZWZvcmVMb2FkICE9PSBhcmdzQWZ0ZXJMb2FkKSB7XG4gICAgICAgICAgY29uc29sZS5lcnJvcihgW0pRSFRNTF0gV0FSTklORzogQ29tcG9uZW50IFwiJHt0aGlzLmNvbXBvbmVudF9uYW1lKCl9XCIgbW9kaWZpZWQgdGhpcy5hcmdzIGluIG9uX2xvYWQoKS5cbm9uX2xvYWQoKSBzaG91bGQgT05MWSBtb2RpZnkgdGhpcy5kYXRhLiBUaGUgdGhpcy5hcmdzIHByb3BlcnR5IGlzIHJlYWQtb25seS5cblxuQmVmb3JlOiAke2FyZ3NCZWZvcmVMb2FkfVxuQWZ0ZXI6ICAke2FyZ3NBZnRlckxvYWR9XG5cbkZpeDogTW92ZSB5b3VyIG1vZGlmaWNhdGlvbnMgdG8gdGhpcy5kYXRhIGluc3RlYWQuYCk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgbmV3UHJvcGVydGllcyA9IHByb3BlcnRpZXNBZnRlckxvYWQuZmlsdGVyKChwcm9wKSA9PiAhcHJvcGVydGllc0JlZm9yZUxvYWQuaGFzKHByb3ApICYmIHByb3AgIT09IFwiZGF0YVwiKTtcbiAgICAgICAgaWYgKG5ld1Byb3BlcnRpZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYFtKUUhUTUxdIFdBUk5JTkc6IENvbXBvbmVudCBcIiR7dGhpcy5jb21wb25lbnRfbmFtZSgpfVwiIGFkZGVkIG5ldyBwcm9wZXJ0aWVzIGluIG9uX2xvYWQoKS5cbm9uX2xvYWQoKSBzaG91bGQgT05MWSBtb2RpZnkgdGhpcy5kYXRhLiBOZXcgcHJvcGVydGllcyBkZXRlY3RlZDogJHtuZXdQcm9wZXJ0aWVzLmpvaW4oXCIsIFwiKX1cblxuRml4OiBTdG9yZSB5b3VyIGRhdGEgaW4gdGhpcy5kYXRhIGluc3RlYWQ6XG4gIFxcdTI3NEMgdGhpcy4ke25ld1Byb3BlcnRpZXNbMF19ID0gdmFsdWU7XG4gIFxcdTI3MDUgdGhpcy5kYXRhLiR7bmV3UHJvcGVydGllc1swXX0gPSB2YWx1ZTtgKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgYXdhaXQgdGhpcy5yZW5kZXIoKTtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJyZWxvYWRcIiwgXCJjb21wbGV0ZVwiKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRGVzdHJveSB0aGUgY29tcG9uZW50IGFuZCBjbGVhbnVwXG4gICAgICogQ2FsbGVkIGF1dG9tYXRpY2FsbHkgYnkgTXV0YXRpb25PYnNlcnZlciB3aGVuIGNvbXBvbmVudCBpcyByZW1vdmVkIGZyb20gRE9NXG4gICAgICogQ2FuIGFsc28gYmUgY2FsbGVkIG1hbnVhbGx5IGZvciBleHBsaWNpdCBjbGVhbnVwXG4gICAgICovXG4gICAgLyoqXG4gICAgICogSW50ZXJuYWwgc3RvcCBtZXRob2QgLSBzdG9wcyBqdXN0IHRoaXMgY29tcG9uZW50IChubyBjaGlsZHJlbilcbiAgICAgKiBTZXRzIHN0b3BwZWQgZmxhZywgY2FsbHMgbGlmZWN5Y2xlIGhvb2tzLCBidXQgbGVhdmVzIERPTSBpbnRhY3RcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqL1xuICAgIF9zdG9wKCkge1xuICAgICAgaWYgKHRoaXMuX3N0b3BwZWQpXG4gICAgICAgIHJldHVybjtcbiAgICAgIHRoaXMuX3N0b3BwZWQgPSB0cnVlO1xuICAgICAgY29uc3QgaGFzX2N1c3RvbV9kZXN0cm95ID0gdGhpcy5vbl9kZXN0cm95ICE9PSBfSnFodG1sX0NvbXBvbmVudC5wcm90b3R5cGUub25fZGVzdHJveTtcbiAgICAgIGNvbnN0IGhhc19kZXN0cm95X2NhbGxiYWNrcyA9IHRoaXMuX29uX3JlZ2lzdGVyZWQoXCJkZXN0cm95XCIpO1xuICAgICAgaWYgKCFoYXNfY3VzdG9tX2Rlc3Ryb3kgJiYgIWhhc19kZXN0cm95X2NhbGxiYWNrcykge1xuICAgICAgICB0aGlzLl9saWZlY3ljbGVfbWFuYWdlci51bnJlZ2lzdGVyX2NvbXBvbmVudCh0aGlzKTtcbiAgICAgICAgdGhpcy5fcmVhZHlfc3RhdGUgPSA5OTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcImRlc3Ryb3lcIiwgXCJzdGFydFwiKTtcbiAgICAgIHRoaXMuJC5hZGRDbGFzcyhcIl9Db21wb25lbnRfU3RvcHBlZFwiKTtcbiAgICAgIHRoaXMuX2xpZmVjeWNsZV9tYW5hZ2VyLnVucmVnaXN0ZXJfY29tcG9uZW50KHRoaXMpO1xuICAgICAgY29uc3QgZGVzdHJveVJlc3VsdCA9IHRoaXMub25fZGVzdHJveSgpO1xuICAgICAgaWYgKGRlc3Ryb3lSZXN1bHQgJiYgdHlwZW9mIGRlc3Ryb3lSZXN1bHQudGhlbiA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gQ29tcG9uZW50IFwiJHt0aGlzLmNvbXBvbmVudF9uYW1lKCl9XCIgcmV0dXJuZWQgYSBQcm9taXNlIGZyb20gb25fZGVzdHJveSgpLiBvbl9kZXN0cm95KCkgbXVzdCBiZSBzeW5jaHJvbm91cyBjb2RlLiBSZW1vdmUgJ2FzeW5jJyBmcm9tIHRoZSBmdW5jdGlvbiBkZWNsYXJhdGlvbi5gKTtcbiAgICAgIH1cbiAgICAgIHRoaXMudHJpZ2dlcihcImRlc3Ryb3lcIik7XG4gICAgICB0aGlzLiQudHJpZ2dlcihcImRlc3Ryb3lcIik7XG4gICAgICBpZiAodGhpcy5fZG9tX3BhcmVudCkge1xuICAgICAgICB0aGlzLl9kb21fcGFyZW50Ll9kb21fY2hpbGRyZW4uZGVsZXRlKHRoaXMpO1xuICAgICAgfVxuICAgICAgdGhpcy5fcmVhZHlfc3RhdGUgPSA5OTtcbiAgICAgIHRoaXMuX3VwZGF0ZV9kZWJ1Z19hdHRycygpO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcImRlc3Ryb3lcIiwgXCJjb21wbGV0ZVwiKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU3RvcCBjb21wb25lbnQgbGlmZWN5Y2xlIC0gc3RvcHMgYWxsIGRlc2NlbmRhbnQgY29tcG9uZW50cyB0aGVuIHNlbGZcbiAgICAgKiBMZWF2ZXMgRE9NIGludGFjdCwganVzdCBzdG9wcyBsaWZlY3ljbGUgZW5naW5lIGFuZCBmaXJlcyBjbGVhbnVwIGhvb2tzXG4gICAgICovXG4gICAgc3RvcCgpIHtcbiAgICAgIHRoaXMuJC5maW5kKFwiLkpxaHRtbF9Db21wb25lbnRcIikuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgY29uc3QgY2hpbGQgPSAkKHRoaXMpLmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICBpZiAoY2hpbGQgJiYgIWNoaWxkLl9zdG9wcGVkKSB7XG4gICAgICAgICAgY2hpbGQuX3N0b3AoKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICB0aGlzLl9zdG9wKCk7XG4gICAgfVxuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvLyBPdmVycmlkYWJsZSBMaWZlY3ljbGUgSG9va3NcbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgb25fcmVuZGVyKCkge1xuICAgIH1cbiAgICBvbl9jcmVhdGUoKSB7XG4gICAgfVxuICAgIGFzeW5jIG9uX2xvYWQoKSB7XG4gICAgfVxuICAgIGFzeW5jIG9uX3JlYWR5KCkge1xuICAgIH1cbiAgICBvbl9kZXN0cm95KCkge1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTaG91bGQgY29tcG9uZW50IHJlLXJlbmRlciBhZnRlciBsb2FkP1xuICAgICAqIEJ5IGRlZmF1bHQsIG9ubHkgcmUtcmVuZGVycyBpZiBkYXRhIGhhcyBjaGFuZ2VkXG4gICAgICogT3ZlcnJpZGUgdG8gY29udHJvbCByZS1yZW5kZXJpbmcgYmVoYXZpb3JcbiAgICAgKi9cbiAgICBzaG91bGRfcmVyZW5kZXIoKSB7XG4gICAgICBjb25zdCBjdXJyZW50RGF0YVN0YXRlID0gSlNPTi5zdHJpbmdpZnkodGhpcy5kYXRhKTtcbiAgICAgIGNvbnN0IGRhdGFDaGFuZ2VkID0gdGhpcy5fZGF0YV9iZWZvcmVfcmVuZGVyICE9PSBjdXJyZW50RGF0YVN0YXRlO1xuICAgICAgaWYgKGRhdGFDaGFuZ2VkKSB7XG4gICAgICAgIHRoaXMuX2RhdGFfYmVmb3JlX3JlbmRlciA9IGN1cnJlbnREYXRhU3RhdGU7XG4gICAgICB9XG4gICAgICByZXR1cm4gZGF0YUNoYW5nZWQ7XG4gICAgfVxuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvLyBQdWJsaWMgQVBJXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8qKlxuICAgICAqIEdldCBjb21wb25lbnQgbmFtZSBmb3IgZGVidWdnaW5nXG4gICAgICovXG4gICAgY29tcG9uZW50X25hbWUoKSB7XG4gICAgICByZXR1cm4gdGhpcy5jb25zdHJ1Y3Rvci5uYW1lO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBFbWl0IGEgalF1ZXJ5IGV2ZW50IGZyb20gY29tcG9uZW50IHJvb3RcbiAgICAgKi9cbiAgICBlbWl0KGV2ZW50X25hbWUsIGRhdGEpIHtcbiAgICAgIHRoaXMuX2xvZ19kZWJ1ZyhcImVtaXRcIiwgZXZlbnRfbmFtZSwgZGF0YSk7XG4gICAgICB0aGlzLiQudHJpZ2dlcihldmVudF9uYW1lLCBkYXRhKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVnaXN0ZXIgbGlmZWN5Y2xlIGV2ZW50IGNhbGxiYWNrXG4gICAgICogQWxsb3dlZCBldmVudHM6ICdyZW5kZXInLCAnY3JlYXRlJywgJ2xvYWQnLCAncmVhZHknLCAnZGVzdHJveSdcbiAgICAgKiBDYWxsYmFja3MgZmlyZSBhZnRlciB0aGUgbGlmZWN5Y2xlIG1ldGhvZCBjb21wbGV0ZXNcbiAgICAgKiBJZiB0aGUgZXZlbnQgaGFzIGFscmVhZHkgb2NjdXJyZWQsIHRoZSBjYWxsYmFjayBmaXJlcyBpbW1lZGlhdGVseSBBTkQgcmVnaXN0ZXJzIGZvciBmdXR1cmUgb2NjdXJyZW5jZXNcbiAgICAgKi9cbiAgICBvbihldmVudF9uYW1lLCBjYWxsYmFjaykge1xuICAgICAgY29uc3QgYWxsb3dlZF9ldmVudHMgPSBbXCJyZW5kZXJcIiwgXCJjcmVhdGVcIiwgXCJsb2FkXCIsIFwicmVhZHlcIiwgXCJkZXN0cm95XCJdO1xuICAgICAgaWYgKCFhbGxvd2VkX2V2ZW50cy5pbmNsdWRlcyhldmVudF9uYW1lKSkge1xuICAgICAgICBjb25zb2xlLmVycm9yKGBbSlFIVE1MXSBDb21wb25lbnQub24oKSBvbmx5IHN1cHBvcnRzIGxpZmVjeWNsZSBldmVudHM6ICR7YWxsb3dlZF9ldmVudHMuam9pbihcIiwgXCIpfS4gUmVjZWl2ZWQ6ICR7ZXZlbnRfbmFtZX1gKTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICB9XG4gICAgICBpZiAoIXRoaXMuX2xpZmVjeWNsZV9jYWxsYmFja3MuaGFzKGV2ZW50X25hbWUpKSB7XG4gICAgICAgIHRoaXMuX2xpZmVjeWNsZV9jYWxsYmFja3Muc2V0KGV2ZW50X25hbWUsIFtdKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuX2xpZmVjeWNsZV9jYWxsYmFja3MuZ2V0KGV2ZW50X25hbWUpLnB1c2goY2FsbGJhY2spO1xuICAgICAgaWYgKHRoaXMuX2xpZmVjeWNsZV9zdGF0ZXMuaGFzKGV2ZW50X25hbWUpKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY2FsbGJhY2sodGhpcyk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgY29uc29sZS5lcnJvcihgW0pRSFRNTF0gRXJyb3IgaW4gJHtldmVudF9uYW1lfSBjYWxsYmFjazpgLCBlcnJvcik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBUcmlnZ2VyIGEgbGlmZWN5Y2xlIGV2ZW50IC0gZmlyZXMgYWxsIHJlZ2lzdGVyZWQgY2FsbGJhY2tzXG4gICAgICogTWFya3MgZXZlbnQgYXMgb2NjdXJyZWQgc28gZnV0dXJlIC5vbigpIGNhbGxzIGZpcmUgaW1tZWRpYXRlbHlcbiAgICAgKi9cbiAgICB0cmlnZ2VyKGV2ZW50X25hbWUpIHtcbiAgICAgIHRoaXMuX2xpZmVjeWNsZV9zdGF0ZXMuYWRkKGV2ZW50X25hbWUpO1xuICAgICAgY29uc3QgY2FsbGJhY2tzID0gdGhpcy5fbGlmZWN5Y2xlX2NhbGxiYWNrcy5nZXQoZXZlbnRfbmFtZSk7XG4gICAgICBpZiAoY2FsbGJhY2tzKSB7XG4gICAgICAgIGZvciAoY29uc3QgY2FsbGJhY2sgb2YgY2FsbGJhY2tzKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNhbGxiYWNrLmJpbmQodGhpcykodGhpcyk7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYFtKUUhUTUxdIEVycm9yIGluICR7ZXZlbnRfbmFtZX0gY2FsbGJhY2s6YCwgZXJyb3IpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBDaGVjayBpZiBhbnkgY2FsbGJhY2tzIGFyZSByZWdpc3RlcmVkIGZvciBhIGdpdmVuIGV2ZW50XG4gICAgICogVXNlZCB0byBkZXRlcm1pbmUgaWYgY2xlYW51cCBsb2dpYyBuZWVkcyB0byBydW5cbiAgICAgKi9cbiAgICBfb25fcmVnaXN0ZXJlZChldmVudF9uYW1lKSB7XG4gICAgICBjb25zdCBjYWxsYmFja3MgPSB0aGlzLl9saWZlY3ljbGVfY2FsbGJhY2tzLmdldChldmVudF9uYW1lKTtcbiAgICAgIHJldHVybiAhIShjYWxsYmFja3MgJiYgY2FsbGJhY2tzLmxlbmd0aCA+IDApO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBGaW5kIGVsZW1lbnQgYnkgc2NvcGVkIElEXG4gICAgICpcbiAgICAgKiBTZWFyY2hlcyBmb3IgZWxlbWVudHMgd2l0aCBpZD1cImxvY2FsX2lkOlRISVNfQ09NUE9ORU5UX0NJRFwiXG4gICAgICpcbiAgICAgKiBFeGFtcGxlOlxuICAgICAqICAgVGVtcGxhdGU6IDxidXR0b24gJGlkPVwic2F2ZV9idG5cIj5TYXZlPC9idXR0b24+XG4gICAgICogICBSZW5kZXJlZDogPGJ1dHRvbiBpZD1cInNhdmVfYnRuOmFiYzEyM1wiIGRhdGEtaWQ9XCJzYXZlX2J0blwiPlNhdmU8L2J1dHRvbj5cbiAgICAgKiAgIEFjY2VzczogICB0aGlzLiRpZCgnc2F2ZV9idG4nKSAgLy8gUmV0dXJucyBqUXVlcnkgZWxlbWVudFxuICAgICAqXG4gICAgICogUGVyZm9ybWFuY2U6IFVzZXMgbmF0aXZlIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCkgd2hlbiBjb21wb25lbnQgaXMgaW4gRE9NLFxuICAgICAqIGZhbGxzIGJhY2sgdG8galF1ZXJ5LmZpbmQoKSBmb3IgY29tcG9uZW50cyBub3QgeWV0IGF0dGFjaGVkIHRvIERPTS5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBsb2NhbF9pZCBUaGUgbG9jYWwgSUQgKHdpdGhvdXQgX2NpZCBzdWZmaXgpXG4gICAgICogQHJldHVybnMgalF1ZXJ5IGVsZW1lbnQgd2l0aCBpZD1cImxvY2FsX2lkOl9jaWRcIiwgb3IgZW1wdHkgalF1ZXJ5IG9iamVjdCBpZiBub3QgZm91bmRcbiAgICAgKi9cbiAgICAkaWQobG9jYWxfaWQpIHtcbiAgICAgIGNvbnN0IHNjb3BlZElkID0gYCR7bG9jYWxfaWR9OiR7dGhpcy5fY2lkfWA7XG4gICAgICBjb25zdCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKHNjb3BlZElkKTtcbiAgICAgIGlmIChlbCkge1xuICAgICAgICByZXR1cm4gJChlbCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy4kLmZpbmQoYCMkeyQuZXNjYXBlU2VsZWN0b3Ioc2NvcGVkSWQpfWApO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgY29tcG9uZW50IGluc3RhbmNlIGJ5IHNjb3BlZCBJRFxuICAgICAqXG4gICAgICogQ29udmVuaWVuY2UgbWV0aG9kIHRoYXQgZmluZHMgZWxlbWVudCBieSBzY29wZWQgSUQgYW5kIHJldHVybnMgdGhlIGNvbXBvbmVudCBpbnN0YW5jZS5cbiAgICAgKlxuICAgICAqIEV4YW1wbGU6XG4gICAgICogICBUZW1wbGF0ZTogPFVzZXJfQ2FyZCAkaWQ9XCJhY3RpdmVfdXNlclwiIC8+XG4gICAgICogICBBY2Nlc3M6ICAgY29uc3QgdXNlciA9IHRoaXMuaWQoJ2FjdGl2ZV91c2VyJyk7ICAvLyBSZXR1cm5zIFVzZXJfQ2FyZCBpbnN0YW5jZVxuICAgICAqICAgICAgICAgICAgIHVzZXIuZGF0YS5uYW1lICAvLyBBY2Nlc3MgY29tcG9uZW50J3MgZGF0YVxuICAgICAqXG4gICAgICogQHBhcmFtIGxvY2FsX2lkIFRoZSBsb2NhbCBJRCAod2l0aG91dCBfY2lkIHN1ZmZpeClcbiAgICAgKiBAcmV0dXJucyBDb21wb25lbnQgaW5zdGFuY2Ugb3IgbnVsbCBpZiBub3QgZm91bmQgb3Igbm90IGEgY29tcG9uZW50XG4gICAgICovXG4gICAgaWQobG9jYWxfaWQpIHtcbiAgICAgIGNvbnN0IGVsZW1lbnQgPSB0aGlzLiRpZChsb2NhbF9pZCk7XG4gICAgICBjb25zdCBjb21wb25lbnQgPSBlbGVtZW50LmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgaWYgKCFjb21wb25lbnQgJiYgZWxlbWVudC5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihgQ29tcG9uZW50ICR7dGhpcy5jb25zdHJ1Y3Rvci5uYW1lfSB0cmllZCB0byBjYWxsIC5pZCgnJHtsb2NhbF9pZH0nKSAtICR7bG9jYWxfaWR9IGV4aXN0cywgaG93ZXZlciwgaXQgaXMgbm90IGEgY29tcG9uZW50IG9yICRyZWRyYXdhYmxlLiBEaWQgeW91IGZvcmdldCB0byBhZGQgJHJlZHJhd2FibGUgdG8gdGhlIHRhZz9gKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBjb21wb25lbnQgfHwgbnVsbDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IHRoZSBjb21wb25lbnQgdGhhdCBpbnN0YW50aWF0ZWQgdGhpcyBjb21wb25lbnQgKHJlbmRlcmVkIGl0IGluIHRoZWlyIHRlbXBsYXRlKVxuICAgICAqIFJldHVybnMgbnVsbCBpZiBjb21wb25lbnQgd2FzIGNyZWF0ZWQgcHJvZ3JhbW1hdGljYWxseSB2aWEgJCgpLmNvbXBvbmVudCgpXG4gICAgICovXG4gICAgaW5zdGFudGlhdG9yKCkge1xuICAgICAgcmV0dXJuIHRoaXMuX2luc3RhbnRpYXRvcjtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRmluZCBkZXNjZW5kYW50IGNvbXBvbmVudHMgYnkgQ1NTIHNlbGVjdG9yXG4gICAgICovXG4gICAgZmluZChzZWxlY3Rvcikge1xuICAgICAgY29uc3QgY29tcG9uZW50cyA9IFtdO1xuICAgICAgdGhpcy4kLmZpbmQoc2VsZWN0b3IpLmVhY2goKF8sIGVsKSA9PiB7XG4gICAgICAgIGNvbnN0IGNvbXAgPSAkKGVsKS5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgaWYgKGNvbXAgaW5zdGFuY2VvZiBfSnFodG1sX0NvbXBvbmVudCkge1xuICAgICAgICAgIGNvbXBvbmVudHMucHVzaChjb21wKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICByZXR1cm4gY29tcG9uZW50cztcbiAgICB9XG4gICAgLyoqXG4gICAgICogRmluZCBjbG9zZXN0IGFuY2VzdG9yIGNvbXBvbmVudCBtYXRjaGluZyBzZWxlY3RvclxuICAgICAqL1xuICAgIGNsb3Nlc3Qoc2VsZWN0b3IpIHtcbiAgICAgIGxldCBjdXJyZW50ID0gdGhpcy4kLnBhcmVudCgpO1xuICAgICAgd2hpbGUgKGN1cnJlbnQubGVuZ3RoID4gMCkge1xuICAgICAgICBpZiAoY3VycmVudC5pcyhzZWxlY3RvcikpIHtcbiAgICAgICAgICBjb25zdCBjb21wID0gY3VycmVudC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgICBpZiAoY29tcCBpbnN0YW5jZW9mIF9KcWh0bWxfQ29tcG9uZW50KSB7XG4gICAgICAgICAgICByZXR1cm4gY29tcDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY3VycmVudCA9IGN1cnJlbnQucGFyZW50KCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIFN0YXRpYyBNZXRob2RzXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8qKlxuICAgICAqIEdldCBDU1MgY2xhc3MgaGllcmFyY2h5IGZvciB0aGlzIGNvbXBvbmVudCB0eXBlXG4gICAgICovXG4gICAgc3RhdGljIGdldF9jbGFzc19oaWVyYXJjaHkoKSB7XG4gICAgICBjb25zdCBjbGFzc2VzID0gW107XG4gICAgICBsZXQgY3RvciA9IHRoaXM7XG4gICAgICB3aGlsZSAoY3Rvcikge1xuICAgICAgICBpZiAoIWN0b3IubmFtZSB8fCB0eXBlb2YgY3Rvci5uYW1lICE9PSBcInN0cmluZ1wiKSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGN0b3IubmFtZSAhPT0gXCJPYmplY3RcIiAmJiBjdG9yLm5hbWUgIT09IFwiXCIpIHtcbiAgICAgICAgICBsZXQgbm9ybWFsaXplZE5hbWUgPSBjdG9yLm5hbWU7XG4gICAgICAgICAgaWYgKG5vcm1hbGl6ZWROYW1lID09PSBcIl9KcWh0bWxfQ29tcG9uZW50XCIgfHwgbm9ybWFsaXplZE5hbWUgPT09IFwiX0Jhc2VfSnFodG1sX0NvbXBvbmVudFwiKSB7XG4gICAgICAgICAgICBub3JtYWxpemVkTmFtZSA9IFwiSnFodG1sX0NvbXBvbmVudFwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjbGFzc2VzLnB1c2gobm9ybWFsaXplZE5hbWUpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IG5leHRQcm90byA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihjdG9yKTtcbiAgICAgICAgaWYgKCFuZXh0UHJvdG8gfHwgbmV4dFByb3RvID09PSBPYmplY3QucHJvdG90eXBlIHx8IG5leHRQcm90by5jb25zdHJ1Y3RvciA9PT0gT2JqZWN0KSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgY3RvciA9IG5leHRQcm90bztcbiAgICAgIH1cbiAgICAgIHJldHVybiBjbGFzc2VzO1xuICAgIH1cbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLy8gUHJpdmF0ZSBJbXBsZW1lbnRhdGlvblxuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICBfZ2VuZXJhdGVfY2lkKCkge1xuICAgICAgcmV0dXJuIHVpZCgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBGbGF0dGVuIGluc3RydWN0aW9uIGFycmF5IC0gY29udmVydHMgWydfY29udGVudCcsIFsuLi5dXSBtYXJrZXJzIHRvIGZsYXQgYXJyYXlcbiAgICAgKiBSZWN1cnNpdmVseSBmbGF0dGVucyBuZXN0ZWQgY29udGVudCBmcm9tIGNvbnRlbnQoKSBjYWxsc1xuICAgICAqL1xuICAgIF9mbGF0dGVuX2luc3RydWN0aW9ucyhpbnN0cnVjdGlvbnMpIHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IFtdO1xuICAgICAgZm9yIChjb25zdCBpbnN0cnVjdGlvbiBvZiBpbnN0cnVjdGlvbnMpIHtcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkoaW5zdHJ1Y3Rpb24pICYmIGluc3RydWN0aW9uWzBdID09PSBcIl9jb250ZW50XCIgJiYgQXJyYXkuaXNBcnJheShpbnN0cnVjdGlvblsxXSkpIHtcbiAgICAgICAgICBjb25zdCBjb250ZW50SW5zdHJ1Y3Rpb25zID0gdGhpcy5fZmxhdHRlbl9pbnN0cnVjdGlvbnMoaW5zdHJ1Y3Rpb25bMV0pO1xuICAgICAgICAgIHJlc3VsdC5wdXNoKC4uLmNvbnRlbnRJbnN0cnVjdGlvbnMpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJlc3VsdC5wdXNoKGluc3RydWN0aW9uKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICAgX2FwcGx5X2Nzc19jbGFzc2VzKCkge1xuICAgICAgY29uc3QgaGllcmFyY2h5ID0gdGhpcy5jb25zdHJ1Y3Rvci5nZXRfY2xhc3NfaGllcmFyY2h5KCk7XG4gICAgICBjb25zdCBjbGFzc2VzVG9BZGQgPSBbLi4uaGllcmFyY2h5XTtcbiAgICAgIGlmICh0aGlzLmFyZ3MuX2NvbXBvbmVudF9uYW1lICYmIHRoaXMuYXJncy5fY29tcG9uZW50X25hbWUgIT09IHRoaXMuY29uc3RydWN0b3IubmFtZSkge1xuICAgICAgICBjbGFzc2VzVG9BZGQudW5zaGlmdCh0aGlzLmFyZ3MuX2NvbXBvbmVudF9uYW1lKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHB1YmxpY0NsYXNzZXMgPSBjbGFzc2VzVG9BZGQuZmlsdGVyKChjbGFzc05hbWUpID0+IHtcbiAgICAgICAgaWYgKCFjbGFzc05hbWUgfHwgdHlwZW9mIGNsYXNzTmFtZSAhPT0gXCJzdHJpbmdcIikge1xuICAgICAgICAgIGNvbnNvbGUud2FybihcIltKUUhUTUxdIEZpbHRlcmVkIG91dCBpbnZhbGlkIGNsYXNzIG5hbWU6XCIsIGNsYXNzTmFtZSk7XG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiAhY2xhc3NOYW1lLnN0YXJ0c1dpdGgoXCJfXCIpO1xuICAgICAgfSk7XG4gICAgICBpZiAocHVibGljQ2xhc3Nlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIHRoaXMuJC5hZGRDbGFzcyhwdWJsaWNDbGFzc2VzLmpvaW4oXCIgXCIpKTtcbiAgICAgIH1cbiAgICB9XG4gICAgX2FwcGx5X2RlZmF1bHRfYXR0cmlidXRlcygpIHtcbiAgICAgIGxldCB0ZW1wbGF0ZTtcbiAgICAgIGlmICh0aGlzLmFyZ3MuX2NvbXBvbmVudF9uYW1lKSB7XG4gICAgICAgIHRlbXBsYXRlID0gZ2V0X3RlbXBsYXRlKHRoaXMuYXJncy5fY29tcG9uZW50X25hbWUpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGVtcGxhdGUgPSBnZXRfdGVtcGxhdGVfYnlfY2xhc3ModGhpcy5jb25zdHJ1Y3Rvcik7XG4gICAgICB9XG4gICAgICBpZiAodGVtcGxhdGUgJiYgdGVtcGxhdGUuZGVmYXVsdEF0dHJpYnV0ZXMpIHtcbiAgICAgICAgY29uc3QgZGVmaW5lQXR0cnMgPSB7IC4uLnRlbXBsYXRlLmRlZmF1bHRBdHRyaWJ1dGVzIH07XG4gICAgICAgIGRlbGV0ZSBkZWZpbmVBdHRycy50YWc7XG4gICAgICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8uZW5hYmxlZCkge1xuICAgICAgICAgIGNvbnN0IGNvbXBvbmVudE5hbWUgPSB0ZW1wbGF0ZS5uYW1lIHx8IHRoaXMuYXJncy5fY29tcG9uZW50X25hbWUgfHwgdGhpcy5jb25zdHJ1Y3Rvci5uYW1lO1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbQ29tcG9uZW50XSBBcHBseWluZyBkZWZhdWx0QXR0cmlidXRlcyBmb3IgJHtjb21wb25lbnROYW1lfTpgLCBkZWZpbmVBdHRycyk7XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoZGVmaW5lQXR0cnMpKSB7XG4gICAgICAgICAgaWYgKGtleSA9PT0gXCJjbGFzc1wiKSB7XG4gICAgICAgICAgICBjb25zdCBleGlzdGluZ0NsYXNzZXMgPSB0aGlzLiQuYXR0cihcImNsYXNzXCIpO1xuICAgICAgICAgICAgaWYgKGV4aXN0aW5nQ2xhc3Nlcykge1xuICAgICAgICAgICAgICBjb25zdCBleGlzdGluZyA9IGV4aXN0aW5nQ2xhc3Nlcy5zcGxpdCgvXFxzKy8pLmZpbHRlcigoYykgPT4gYyk7XG4gICAgICAgICAgICAgIGNvbnN0IG5ld0NsYXNzZXMgPSBTdHJpbmcodmFsdWUpLnNwbGl0KC9cXHMrLykuZmlsdGVyKChjKSA9PiBjKTtcbiAgICAgICAgICAgICAgZm9yIChjb25zdCBuZXdDbGFzcyBvZiBuZXdDbGFzc2VzKSB7XG4gICAgICAgICAgICAgICAgaWYgKCFleGlzdGluZy5pbmNsdWRlcyhuZXdDbGFzcykpIHtcbiAgICAgICAgICAgICAgICAgIGV4aXN0aW5nLnB1c2gobmV3Q2xhc3MpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB0aGlzLiQuYXR0cihcImNsYXNzXCIsIGV4aXN0aW5nLmpvaW4oXCIgXCIpKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHRoaXMuJC5hdHRyKFwiY2xhc3NcIiwgdmFsdWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSBpZiAoa2V5ID09PSBcInN0eWxlXCIpIHtcbiAgICAgICAgICAgIGNvbnN0IGV4aXN0aW5nU3R5bGUgPSB0aGlzLiQuYXR0cihcInN0eWxlXCIpO1xuICAgICAgICAgICAgaWYgKGV4aXN0aW5nU3R5bGUpIHtcbiAgICAgICAgICAgICAgY29uc3QgZXhpc3RpbmdSdWxlcyA9IC8qIEBfX1BVUkVfXyAqLyBuZXcgTWFwKCk7XG4gICAgICAgICAgICAgIGV4aXN0aW5nU3R5bGUuc3BsaXQoXCI7XCIpLmZvckVhY2goKHJ1bGUpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBbcHJvcCwgdmFsXSA9IHJ1bGUuc3BsaXQoXCI6XCIpLm1hcCgocykgPT4gcy50cmltKCkpO1xuICAgICAgICAgICAgICAgIGlmIChwcm9wICYmIHZhbClcbiAgICAgICAgICAgICAgICAgIGV4aXN0aW5nUnVsZXMuc2V0KHByb3AsIHZhbCk7XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICBTdHJpbmcodmFsdWUpLnNwbGl0KFwiO1wiKS5mb3JFYWNoKChydWxlKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgW3Byb3AsIHZhbF0gPSBydWxlLnNwbGl0KFwiOlwiKS5tYXAoKHMpID0+IHMudHJpbSgpKTtcbiAgICAgICAgICAgICAgICBpZiAocHJvcCAmJiB2YWwpXG4gICAgICAgICAgICAgICAgICBleGlzdGluZ1J1bGVzLnNldChwcm9wLCB2YWwpO1xuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgY29uc3QgbWVyZ2VkID0gQXJyYXkuZnJvbShleGlzdGluZ1J1bGVzLmVudHJpZXMoKSkubWFwKChbcHJvcCwgdmFsXSkgPT4gYCR7cHJvcH06ICR7dmFsfWApLmpvaW4oXCI7IFwiKTtcbiAgICAgICAgICAgICAgdGhpcy4kLmF0dHIoXCJzdHlsZVwiLCBtZXJnZWQpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdGhpcy4kLmF0dHIoXCJzdHlsZVwiLCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGlmIChrZXkuc3RhcnRzV2l0aChcIiRcIikgfHwga2V5LnN0YXJ0c1dpdGgoXCJkYXRhLVwiKSkge1xuICAgICAgICAgICAgY29uc3QgZGF0YUtleSA9IGtleS5zdGFydHNXaXRoKFwiJFwiKSA/IGtleS5zdWJzdHJpbmcoMSkgOiBrZXkuc3RhcnRzV2l0aChcImRhdGEtXCIpID8ga2V5LnN1YnN0cmluZyg1KSA6IGtleTtcbiAgICAgICAgICAgIGlmICghKGRhdGFLZXkgaW4gdGhpcy5hcmdzKSkge1xuICAgICAgICAgICAgICB0aGlzLmFyZ3NbZGF0YUtleV0gPSB2YWx1ZTtcbiAgICAgICAgICAgICAgdGhpcy4kLmRhdGEoZGF0YUtleSwgdmFsdWUpO1xuICAgICAgICAgICAgICB0aGlzLiQuYXR0cihrZXkuc3RhcnRzV2l0aChcIiRcIikgPyBgZGF0YS0ke2RhdGFLZXl9YCA6IGtleSwgU3RyaW5nKHZhbHVlKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGlmICghdGhpcy4kLmF0dHIoa2V5KSkge1xuICAgICAgICAgICAgICB0aGlzLiQuYXR0cihrZXksIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgX3NldF9hdHRyaWJ1dGVzKCkge1xuICAgICAgdGhpcy4kLmF0dHIoXCJkYXRhLWNpZFwiLCB0aGlzLl9jaWQpO1xuICAgICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy52ZXJib3NlKSB7XG4gICAgICAgIHRoaXMuJC5hdHRyKFwiZGF0YS1fbGlmZWN5Y2xlLXN0YXRlXCIsIHRoaXMuX3JlYWR5X3N0YXRlLnRvU3RyaW5nKCkpO1xuICAgICAgfVxuICAgIH1cbiAgICBfdXBkYXRlX2RlYnVnX2F0dHJzKCkge1xuICAgICAgaWYgKHdpbmRvdy5qcWh0bWw/LmRlYnVnPy52ZXJib3NlKSB7XG4gICAgICAgIHRoaXMuJC5hdHRyKFwiZGF0YS1fbGlmZWN5Y2xlLXN0YXRlXCIsIHRoaXMuX3JlYWR5X3N0YXRlLnRvU3RyaW5nKCkpO1xuICAgICAgfVxuICAgIH1cbiAgICBfZmluZF9kb21fcGFyZW50KCkge1xuICAgICAgbGV0IGN1cnJlbnQgPSB0aGlzLiQucGFyZW50KCk7XG4gICAgICB3aGlsZSAoY3VycmVudC5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IHBhcmVudCA9IGN1cnJlbnQuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgIGlmIChwYXJlbnQgaW5zdGFuY2VvZiBfSnFodG1sX0NvbXBvbmVudCkge1xuICAgICAgICAgIHRoaXMuX2RvbV9wYXJlbnQgPSBwYXJlbnQ7XG4gICAgICAgICAgcGFyZW50Ll9kb21fY2hpbGRyZW4uYWRkKHRoaXMpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGN1cnJlbnQgPSBjdXJyZW50LnBhcmVudCgpO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBHZXQgRE9NIGNoaWxkcmVuIChjb21wb25lbnRzIGluIERPTSBzdWJ0cmVlKVxuICAgICAqIFVzZXMgZmFzdCBfZG9tX2NoaWxkcmVuIHJlZ2lzdHJ5IHdoZW4gcG9zc2libGUsIGZhbGxzIGJhY2sgdG8gRE9NIHRyYXZlcnNhbCBmb3Igb2ZmLURPTSBjb21wb25lbnRzXG4gICAgICogQHByaXZhdGUgLSBVc2VkIGludGVybmFsbHkgZm9yIGxpZmVjeWNsZSBjb29yZGluYXRpb25cbiAgICAgKi9cbiAgICBfZ2V0X2RvbV9jaGlsZHJlbigpIHtcbiAgICAgIGlmICh0aGlzLl91c2VfZG9tX2ZhbGxiYWNrKSB7XG4gICAgICAgIGNvbnN0IGRpcmVjdENoaWxkcmVuID0gW107XG4gICAgICAgIHRoaXMuJC5maW5kKFwiLkpxaHRtbF9Db21wb25lbnRcIikuZWFjaCgoXywgZWwpID0+IHtcbiAgICAgICAgICBjb25zdCAkZWwgPSAkKGVsKTtcbiAgICAgICAgICBjb25zdCBjb21wID0gJGVsLmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICAgIGlmIChjb21wIGluc3RhbmNlb2YgX0pxaHRtbF9Db21wb25lbnQpIHtcbiAgICAgICAgICAgIGNvbnN0IGNsb3Nlc3RQYXJlbnQgPSAkZWwucGFyZW50KCkuY2xvc2VzdChcIi5KcWh0bWxfQ29tcG9uZW50XCIpO1xuICAgICAgICAgICAgaWYgKGNsb3Nlc3RQYXJlbnQubGVuZ3RoID09PSAwIHx8IGNsb3Nlc3RQYXJlbnQuZGF0YShcIl9jb21wb25lbnRcIikgPT09IHRoaXMpIHtcbiAgICAgICAgICAgICAgZGlyZWN0Q2hpbGRyZW4ucHVzaChjb21wKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gZGlyZWN0Q2hpbGRyZW47XG4gICAgICB9XG4gICAgICBjb25zdCBjaGlsZHJlbiA9IEFycmF5LmZyb20odGhpcy5fZG9tX2NoaWxkcmVuKTtcbiAgICAgIHJldHVybiBjaGlsZHJlbi5maWx0ZXIoKGNoaWxkKSA9PiB7XG4gICAgICAgIHJldHVybiAkLmNvbnRhaW5zKGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCwgY2hpbGQuJFswXSk7XG4gICAgICB9KTtcbiAgICB9XG4gICAgX2xvZ19saWZlY3ljbGUocGhhc2UsIHN0YXR1cykge1xuICAgICAgbG9nTGlmZWN5Y2xlKHRoaXMsIHBoYXNlLCBzdGF0dXMpO1xuICAgICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgd2luZG93LkpRSFRNTF9ERUJVRykge1xuICAgICAgICB3aW5kb3cuSlFIVE1MX0RFQlVHLmxvZyh0aGlzLmNvbXBvbmVudF9uYW1lKCksIHBoYXNlLCBzdGF0dXMsIHtcbiAgICAgICAgICBjaWQ6IHRoaXMuX2NpZCxcbiAgICAgICAgICByZWFkeV9zdGF0ZTogdGhpcy5fcmVhZHlfc3RhdGUsXG4gICAgICAgICAgYXJnczogdGhpcy5hcmdzXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgICBfbG9nX2RlYnVnKGFjdGlvbiwgLi4uYXJncykge1xuICAgICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgd2luZG93LkpRSFRNTF9ERUJVRykge1xuICAgICAgICB3aW5kb3cuSlFIVE1MX0RFQlVHLmxvZyh0aGlzLmNvbXBvbmVudF9uYW1lKCksIFwiZGVidWdcIiwgYCR7YWN0aW9ufTogJHthcmdzLm1hcCgoYSkgPT4gSlNPTi5zdHJpbmdpZnkoYSkpLmpvaW4oXCIsIFwiKX1gKTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG4gIGFzeW5jIGZ1bmN0aW9uIHByb2Nlc3Nfc2xvdF9pbmhlcml0YW5jZShjb21wb25lbnQsIGNoaWxkU2xvdHMpIHtcbiAgICBsZXQgY3VycmVudENsYXNzID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKGNvbXBvbmVudC5jb25zdHJ1Y3Rvcik7XG4gICAgY29uc29sZS5sb2coYFtKUUhUTUxdIFdhbGtpbmcgcHJvdG90eXBlIGNoYWluIGZvciAke2NvbXBvbmVudC5jb25zdHJ1Y3Rvci5uYW1lfWApO1xuICAgIHdoaWxlIChjdXJyZW50Q2xhc3MgJiYgY3VycmVudENsYXNzICE9PSBKcWh0bWxfQ29tcG9uZW50ICYmIGN1cnJlbnRDbGFzcy5uYW1lICE9PSBcIk9iamVjdFwiKSB7XG4gICAgICBjb25zdCBjbGFzc05hbWUgPSBjdXJyZW50Q2xhc3MubmFtZTtcbiAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSAgIENoZWNraW5nIHBhcmVudCBjbGFzczogJHtjbGFzc05hbWV9YCk7XG4gICAgICBpZiAoY2xhc3NOYW1lID09PSBcIl9KcWh0bWxfQ29tcG9uZW50XCIgfHwgY2xhc3NOYW1lID09PSBcIl9CYXNlX0pxaHRtbF9Db21wb25lbnRcIikge1xuICAgICAgICBjdXJyZW50Q2xhc3MgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YoY3VycmVudENsYXNzKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBwYXJlbnRUZW1wbGF0ZSA9IGdldF90ZW1wbGF0ZShjbGFzc05hbWUpO1xuICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gICBUZW1wbGF0ZSBmb3VuZCBmb3IgJHtjbGFzc05hbWV9OmAsIHBhcmVudFRlbXBsYXRlID8gcGFyZW50VGVtcGxhdGUubmFtZSA6IFwibnVsbFwiKTtcbiAgICAgICAgaWYgKHBhcmVudFRlbXBsYXRlICYmIHBhcmVudFRlbXBsYXRlLm5hbWUgIT09IFwiSnFodG1sX0NvbXBvbmVudFwiKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdICAgSW52b2tpbmcgcGFyZW50IHRlbXBsYXRlICR7Y2xhc3NOYW1lfWApO1xuICAgICAgICAgIGNvbnN0IFtwYXJlbnRJbnN0cnVjdGlvbnMsIHBhcmVudENvbnRleHRdID0gcGFyZW50VGVtcGxhdGUucmVuZGVyLmNhbGwoXG4gICAgICAgICAgICBjb21wb25lbnQsXG4gICAgICAgICAgICBjb21wb25lbnQuZGF0YSxcbiAgICAgICAgICAgIGNvbXBvbmVudC5hcmdzLFxuICAgICAgICAgICAgY2hpbGRTbG90c1xuICAgICAgICAgICAgLy8gUGFzcyBjaGlsZCBzbG90cyBhcyBjb250ZW50IHBhcmFtZXRlclxuICAgICAgICAgICk7XG4gICAgICAgICAgaWYgKHBhcmVudEluc3RydWN0aW9ucyAmJiB0eXBlb2YgcGFyZW50SW5zdHJ1Y3Rpb25zID09PSBcIm9iamVjdFwiICYmIHBhcmVudEluc3RydWN0aW9ucy5fc2xvdHMpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSAgIFBhcmVudCBhbHNvIHNsb3Qtb25seSwgcmVjdXJzaW5nYCk7XG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgcHJvY2Vzc19zbG90X2luaGVyaXRhbmNlKGNvbXBvbmVudCwgcGFyZW50SW5zdHJ1Y3Rpb25zLl9zbG90cyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSAgIFBhcmVudCByZXR1cm5lZCBpbnN0cnVjdGlvbnMsIGluaGVyaXRhbmNlIGNvbXBsZXRlYCk7XG4gICAgICAgICAgcmV0dXJuIFtwYXJlbnRJbnN0cnVjdGlvbnMsIHBhcmVudENvbnRleHRdO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIEVycm9yIGxvb2tpbmcgdXAgcGFyZW50IHRlbXBsYXRlIGZvciAke2NsYXNzTmFtZX06YCwgZXJyb3IpO1xuICAgICAgfVxuICAgICAgY3VycmVudENsYXNzID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKGN1cnJlbnRDbGFzcyk7XG4gICAgfVxuICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gTm8gcGFyZW50IHRlbXBsYXRlIGZvdW5kIGFmdGVyIHdhbGtpbmcgY2hhaW5gKTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBhc3luYyBmdW5jdGlvbiByZW5kZXJfdGVtcGxhdGUoY29tcG9uZW50LCB0ZW1wbGF0ZV9mbikge1xuICAgIGxldCByZW5kZXJfZm4gPSB0ZW1wbGF0ZV9mbjtcbiAgICBpZiAoIXJlbmRlcl9mbikge1xuICAgICAgY29uc3QgdGVtcGxhdGVfZGVmID0gZ2V0X3RlbXBsYXRlX2J5X2NsYXNzKGNvbXBvbmVudC5jb25zdHJ1Y3Rvcik7XG4gICAgICByZW5kZXJfZm4gPSB0ZW1wbGF0ZV9kZWYucmVuZGVyO1xuICAgIH1cbiAgICBpZiAoIXJlbmRlcl9mbikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb21wb25lbnQuJC5lbXB0eSgpO1xuICAgIGNvbnN0IGRlZmF1bHRDb250ZW50ID0gKCkgPT4gXCJcIjtcbiAgICBsZXQgW2luc3RydWN0aW9ucywgY29udGV4dF0gPSByZW5kZXJfZm4uY2FsbChcbiAgICAgIGNvbXBvbmVudCxcbiAgICAgIGNvbXBvbmVudC5kYXRhLFxuICAgICAgY29tcG9uZW50LmFyZ3MsXG4gICAgICBkZWZhdWx0Q29udGVudFxuICAgICAgLy8gRGVmYXVsdCBjb250ZW50IGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBlbXB0eSBzdHJpbmdcbiAgICApO1xuICAgIGlmIChpbnN0cnVjdGlvbnMgJiYgdHlwZW9mIGluc3RydWN0aW9ucyA9PT0gXCJvYmplY3RcIiAmJiBpbnN0cnVjdGlvbnMuX3Nsb3RzKSB7XG4gICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gU2xvdC1vbmx5IHRlbXBsYXRlIGRldGVjdGVkIGZvciAke2NvbXBvbmVudC5jb25zdHJ1Y3Rvci5uYW1lfSwgaW52b2tpbmcgaW5oZXJpdGFuY2VgKTtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHByb2Nlc3Nfc2xvdF9pbmhlcml0YW5jZShjb21wb25lbnQsIGluc3RydWN0aW9ucy5fc2xvdHMpO1xuICAgICAgaWYgKHJlc3VsdCkge1xuICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gUGFyZW50IHRlbXBsYXRlIGZvdW5kLCB1c2luZyBwYXJlbnQgaW5zdHJ1Y3Rpb25zYCk7XG4gICAgICAgIGluc3RydWN0aW9ucyA9IHJlc3VsdFswXTtcbiAgICAgICAgY29udGV4dCA9IHJlc3VsdFsxXTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gTm8gcGFyZW50IHRlbXBsYXRlIGZvdW5kIGZvciAke2NvbXBvbmVudC5jb25zdHJ1Y3Rvci5uYW1lfSwgcmVuZGVyaW5nIGVtcHR5YCk7XG4gICAgICAgIGluc3RydWN0aW9ucyA9IFtdO1xuICAgICAgfVxuICAgIH1cbiAgICBhd2FpdCBwcm9jZXNzX2luc3RydWN0aW9ucyhpbnN0cnVjdGlvbnMsIGNvbXBvbmVudC4kLCBjb21wb25lbnQpO1xuICAgIGF3YWl0IHByb2Nlc3NfYmluZGluZ3MoY29tcG9uZW50KTtcbiAgICBhd2FpdCBhdHRhY2hfZXZlbnRfaGFuZGxlcnMoY29tcG9uZW50KTtcbiAgfVxuICBhc3luYyBmdW5jdGlvbiBwcm9jZXNzX2JpbmRpbmdzKGNvbXBvbmVudCkge1xuICAgIGNvbXBvbmVudC4kLmZpbmQoXCJbZGF0YS1iaW5kLXByb3BdLCBbZGF0YS1iaW5kLXZhbHVlXSwgW2RhdGEtYmluZC10ZXh0XSwgW2RhdGEtYmluZC1odG1sXSwgW2RhdGEtYmluZC1jbGFzc10sIFtkYXRhLWJpbmQtc3R5bGVdXCIpLmVhY2goKF8sIGVsZW1lbnQpID0+IHtcbiAgICAgIGNvbnN0IGVsID0gJChlbGVtZW50KTtcbiAgICAgIGNvbnN0IGF0dHJzID0gZWxlbWVudC5hdHRyaWJ1dGVzO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhdHRycy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCBhdHRyID0gYXR0cnNbaV07XG4gICAgICAgIGlmIChhdHRyLm5hbWUuc3RhcnRzV2l0aChcImRhdGEtYmluZC1cIikpIHtcbiAgICAgICAgICBjb25zdCBiaW5kaW5nX3R5cGUgPSBhdHRyLm5hbWUuc3Vic3RyaW5nKDEwKTtcbiAgICAgICAgICBjb25zdCBleHByZXNzaW9uID0gYXR0ci52YWx1ZTtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgdmFsdWUgPSBldmFsdWF0ZV9leHByZXNzaW9uKGV4cHJlc3Npb24sIGNvbXBvbmVudCk7XG4gICAgICAgICAgICBzd2l0Y2ggKGJpbmRpbmdfdHlwZSkge1xuICAgICAgICAgICAgICBjYXNlIFwicHJvcFwiOlxuICAgICAgICAgICAgICAgIGNvbnN0IHByb3BfbmFtZSA9IGVsLmF0dHIoXCJkYXRhLWJpbmQtcHJvcC1uYW1lXCIpIHx8IFwidmFsdWVcIjtcbiAgICAgICAgICAgICAgICBlbC5wcm9wKHByb3BfbmFtZSwgdmFsdWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBjYXNlIFwidmFsdWVcIjpcbiAgICAgICAgICAgICAgICBlbC52YWwodmFsdWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBjYXNlIFwidGV4dFwiOlxuICAgICAgICAgICAgICAgIGVsLnRleHQodmFsdWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBjYXNlIFwiaHRtbFwiOlxuICAgICAgICAgICAgICAgIGVsLmh0bWwodmFsdWUpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICBjYXNlIFwiY2xhc3NcIjpcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcIm9iamVjdFwiKSB7XG4gICAgICAgICAgICAgICAgICBPYmplY3QuZW50cmllcyh2YWx1ZSkuZm9yRWFjaCgoW2NsYXNzTmFtZSwgZW5hYmxlZF0pID0+IHtcbiAgICAgICAgICAgICAgICAgICAgZWwudG9nZ2xlQ2xhc3MoY2xhc3NOYW1lLCAhIWVuYWJsZWQpO1xuICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgIGVsLmFkZENsYXNzKFN0cmluZyh2YWx1ZSkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgY2FzZSBcInN0eWxlXCI6XG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJvYmplY3RcIikge1xuICAgICAgICAgICAgICAgICAgZWwuY3NzKHZhbHVlKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgZWwuYXR0cihcInN0eWxlXCIsIFN0cmluZyh2YWx1ZSkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICBlbC5hdHRyKGJpbmRpbmdfdHlwZSwgdmFsdWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGBFcnJvciBldmFsdWF0aW5nIGJpbmRpbmcgXCIke2V4cHJlc3Npb259XCI6YCwgZXJyb3IpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIGFzeW5jIGZ1bmN0aW9uIGF0dGFjaF9ldmVudF9oYW5kbGVycyhjb21wb25lbnQpIHtcbiAgICBjb21wb25lbnQuJC5maW5kKFwiW2RhdGEtb24tY2xpY2tdLCBbZGF0YS1vbi1jaGFuZ2VdLCBbZGF0YS1vbi1zdWJtaXRdLCBbZGF0YS1vbi1rZXl1cF0sIFtkYXRhLW9uLWtleWRvd25dLCBbZGF0YS1vbi1mb2N1c10sIFtkYXRhLW9uLWJsdXJdXCIpLmVhY2goKF8sIGVsZW1lbnQpID0+IHtcbiAgICAgIGNvbnN0IGVsID0gJChlbGVtZW50KTtcbiAgICAgIGNvbnN0IGF0dHJzID0gZWxlbWVudC5hdHRyaWJ1dGVzO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhdHRycy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCBhdHRyID0gYXR0cnNbaV07XG4gICAgICAgIGlmIChhdHRyLm5hbWUuc3RhcnRzV2l0aChcImRhdGEtb24tXCIpKSB7XG4gICAgICAgICAgY29uc3QgZXZlbnRfbmFtZSA9IGF0dHIubmFtZS5zdWJzdHJpbmcoOCk7XG4gICAgICAgICAgY29uc3QgaGFuZGxlcl9leHByID0gYXR0ci52YWx1ZTtcbiAgICAgICAgICBlbC5yZW1vdmVBdHRyKGF0dHIubmFtZSk7XG4gICAgICAgICAgZWwub24oZXZlbnRfbmFtZSwgZnVuY3Rpb24oZXZlbnQpIHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIGNvbnN0IGhhbmRsZXIgPSBldmFsdWF0ZV9oYW5kbGVyKGhhbmRsZXJfZXhwciwgY29tcG9uZW50KTtcbiAgICAgICAgICAgICAgaWYgKHR5cGVvZiBoYW5kbGVyID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgICAgICAgICBoYW5kbGVyLmNhbGwoY29tcG9uZW50LCBldmVudCk7XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZXZhbHVhdGVfZXhwcmVzc2lvbihoYW5kbGVyX2V4cHIsIGNvbXBvbmVudCwgeyAkZXZlbnQ6IGV2ZW50IH0pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKGBFcnJvciBpbiAke2V2ZW50X25hbWV9IGhhbmRsZXIgXCIke2hhbmRsZXJfZXhwcn1cIjpgLCBlcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuICBmdW5jdGlvbiBldmFsdWF0ZV9leHByZXNzaW9uKGV4cHJlc3Npb24sIGNvbXBvbmVudCwgbG9jYWxzID0ge30pIHtcbiAgICBjb25zdCBjb250ZXh0ID0ge1xuICAgICAgLy8gQ29tcG9uZW50IHByb3BlcnRpZXNcbiAgICAgIGRhdGE6IGNvbXBvbmVudC5kYXRhLFxuICAgICAgYXJnczogY29tcG9uZW50LmFyZ3MsXG4gICAgICAkOiBjb21wb25lbnQuJCxcbiAgICAgIC8vIENvbXBvbmVudCBtZXRob2RzXG4gICAgICBlbWl0OiBjb21wb25lbnQuZW1pdC5iaW5kKGNvbXBvbmVudCksXG4gICAgICAkaWQ6IGNvbXBvbmVudC4kaWQuYmluZChjb21wb25lbnQpLFxuICAgICAgLy8gTG9jYWxzIChsaWtlICRldmVudClcbiAgICAgIC4uLmxvY2Fsc1xuICAgIH07XG4gICAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKGNvbnRleHQpO1xuICAgIGNvbnN0IHZhbHVlcyA9IE9iamVjdC52YWx1ZXMoY29udGV4dCk7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGZuID0gbmV3IEZ1bmN0aW9uKC4uLmtleXMsIGByZXR1cm4gKCR7ZXhwcmVzc2lvbn0pYCk7XG4gICAgICByZXR1cm4gZm4oLi4udmFsdWVzKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcihgSW52YWxpZCBleHByZXNzaW9uOiAke2V4cHJlc3Npb259YCwgZXJyb3IpO1xuICAgICAgcmV0dXJuIHZvaWQgMDtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gZXZhbHVhdGVfaGFuZGxlcihleHByZXNzaW9uLCBjb21wb25lbnQpIHtcbiAgICBpZiAoZXhwcmVzc2lvbiBpbiBjb21wb25lbnQgJiYgdHlwZW9mIGNvbXBvbmVudFtleHByZXNzaW9uXSA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICByZXR1cm4gY29tcG9uZW50W2V4cHJlc3Npb25dO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgcmV0dXJuIG5ldyBGdW5jdGlvbihcIiRldmVudFwiLCBgXG4gICAgICBjb25zdCB7IGRhdGEsIGFyZ3MsICQsIGVtaXQsICRpZCB9ID0gdGhpcztcbiAgICAgICR7ZXhwcmVzc2lvbn1cbiAgICBgKS5iaW5kKGNvbXBvbmVudCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYEludmFsaWQgaGFuZGxlcjogJHtleHByZXNzaW9ufWAsIGVycm9yKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuICBmdW5jdGlvbiBlc2NhcGVfaHRtbChzdHIpIHtcbiAgICBjb25zdCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpO1xuICAgIGRpdi50ZXh0Q29udGVudCA9IHN0cjtcbiAgICByZXR1cm4gZGl2LmlubmVySFRNTDtcbiAgfVxuICBmdW5jdGlvbiBnZXRKUXVlcnkoKSB7XG4gICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgd2luZG93LiQpIHtcbiAgICAgIHJldHVybiB3aW5kb3cuJDtcbiAgICB9XG4gICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgd2luZG93LmpRdWVyeSkge1xuICAgICAgcmV0dXJuIHdpbmRvdy5qUXVlcnk7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignRkFUQUw6IGpRdWVyeSBpcyBub3QgZGVmaW5lZC4galF1ZXJ5IG11c3QgYmUgbG9hZGVkIGJlZm9yZSB1c2luZyBKUUhUTUwuIEFkZCA8c2NyaXB0IHNyYz1cImh0dHBzOi8vY29kZS5qcXVlcnkuY29tL2pxdWVyeS0zLjcuMS5taW4uanNcIj48XFwvc2NyaXB0PiBiZWZvcmUgbG9hZGluZyBKUUhUTUwuJyk7XG4gIH1cbiAgZnVuY3Rpb24gZ2V0SnFodG1sKCkge1xuICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmIHdpbmRvdy5qcWh0bWwpIHtcbiAgICAgIHJldHVybiB3aW5kb3cuanFodG1sO1xuICAgIH1cbiAgICBpZiAodHlwZW9mIGdsb2JhbFRoaXMgIT09IFwidW5kZWZpbmVkXCIgJiYgZ2xvYmFsVGhpcy5qcWh0bWwpIHtcbiAgICAgIHJldHVybiBnbG9iYWxUaGlzLmpxaHRtbDtcbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKFwiRkFUQUw6IHdpbmRvdy5qcWh0bWwgaXMgbm90IGRlZmluZWQuIFRoZSBKUUhUTUwgcnVudGltZSBtdXN0IGJlIGxvYWRlZCBiZWZvcmUgdXNpbmcgSlFIVE1MIGNvbXBvbmVudHMuIEVuc3VyZSBAanFodG1sL2NvcmUgaXMgaW1wb3J0ZWQgYW5kIGluaXRpYWxpemVkIGJlZm9yZSBhdHRlbXB0aW5nIHRvIHVzZSBkZWJ1ZyBmZWF0dXJlcy5cIik7XG4gIH1cbiAgdmFyIERlYnVnT3ZlcmxheSA9IGNsYXNzIF9EZWJ1Z092ZXJsYXkge1xuICAgIGNvbnN0cnVjdG9yKG9wdGlvbnMgPSB7fSkge1xuICAgICAgdGhpcy4kY29udGFpbmVyID0gbnVsbDtcbiAgICAgIHRoaXMuJHN0YXR1c0luZGljYXRvciA9IG51bGw7XG4gICAgICB0aGlzLiQgPSBnZXRKUXVlcnkoKTtcbiAgICAgIGlmICghdGhpcy4kKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcImpRdWVyeSBpcyByZXF1aXJlZCBmb3IgRGVidWdPdmVybGF5XCIpO1xuICAgICAgfVxuICAgICAgdGhpcy5vcHRpb25zID0ge1xuICAgICAgICBwb3NpdGlvbjogXCJib3R0b21cIixcbiAgICAgICAgdGhlbWU6IFwiZGFya1wiLFxuICAgICAgICBjb21wYWN0OiBmYWxzZSxcbiAgICAgICAgc2hvd1N0YXR1czogdHJ1ZSxcbiAgICAgICAgYXV0b0hpZGU6IGZhbHNlLFxuICAgICAgICAuLi5vcHRpb25zXG4gICAgICB9O1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTdGF0aWMgbWV0aG9kIHRvIHNob3cgZGVidWcgb3ZlcmxheSAoc2luZ2xldG9uIHBhdHRlcm4pXG4gICAgICovXG4gICAgc3RhdGljIHNob3cob3B0aW9ucykge1xuICAgICAgaWYgKCFfRGVidWdPdmVybGF5Lmluc3RhbmNlKSB7XG4gICAgICAgIF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UgPSBuZXcgX0RlYnVnT3ZlcmxheShvcHRpb25zKTtcbiAgICAgIH1cbiAgICAgIF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UuZGlzcGxheSgpO1xuICAgICAgcmV0dXJuIF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2U7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFN0YXRpYyBtZXRob2QgdG8gaGlkZSBkZWJ1ZyBvdmVybGF5XG4gICAgICovXG4gICAgc3RhdGljIGhpZGUoKSB7XG4gICAgICBpZiAoX0RlYnVnT3ZlcmxheS5pbnN0YW5jZSkge1xuICAgICAgICBfRGVidWdPdmVybGF5Lmluc3RhbmNlLmhpZGUoKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogU3RhdGljIG1ldGhvZCB0byB0b2dnbGUgZGVidWcgb3ZlcmxheSB2aXNpYmlsaXR5XG4gICAgICovXG4gICAgc3RhdGljIHRvZ2dsZSgpIHtcbiAgICAgIGlmIChfRGVidWdPdmVybGF5Lmluc3RhbmNlICYmIF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UuJGNvbnRhaW5lcikge1xuICAgICAgICBpZiAoX0RlYnVnT3ZlcmxheS5pbnN0YW5jZS4kY29udGFpbmVyLmlzKFwiOnZpc2libGVcIikpIHtcbiAgICAgICAgICBfRGVidWdPdmVybGF5LmhpZGUoKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBfRGVidWdPdmVybGF5Lmluc3RhbmNlLmRpc3BsYXkoKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgX0RlYnVnT3ZlcmxheS5zaG93KCk7XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFN0YXRpYyBtZXRob2QgdG8gZGVzdHJveSBkZWJ1ZyBvdmVybGF5XG4gICAgICovXG4gICAgc3RhdGljIGRlc3Ryb3koKSB7XG4gICAgICBpZiAoX0RlYnVnT3ZlcmxheS5pbnN0YW5jZSkge1xuICAgICAgICBfRGVidWdPdmVybGF5Lmluc3RhbmNlLmRlc3Ryb3koKTtcbiAgICAgICAgX0RlYnVnT3ZlcmxheS5pbnN0YW5jZSA9IG51bGw7XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIERpc3BsYXkgdGhlIGRlYnVnIG92ZXJsYXlcbiAgICAgKi9cbiAgICBkaXNwbGF5KCkge1xuICAgICAgaWYgKHRoaXMuJGNvbnRhaW5lcikge1xuICAgICAgICB0aGlzLiRjb250YWluZXIuc2hvdygpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLmNyZWF0ZU92ZXJsYXkoKTtcbiAgICAgIGlmICh0aGlzLm9wdGlvbnMuc2hvd1N0YXR1cykge1xuICAgICAgICB0aGlzLmNyZWF0ZVN0YXR1c0luZGljYXRvcigpO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBIaWRlIHRoZSBkZWJ1ZyBvdmVybGF5XG4gICAgICovXG4gICAgaGlkZSgpIHtcbiAgICAgIGlmICh0aGlzLiRjb250YWluZXIpIHtcbiAgICAgICAgdGhpcy4kY29udGFpbmVyLmhpZGUoKTtcbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLiRzdGF0dXNJbmRpY2F0b3IpIHtcbiAgICAgICAgdGhpcy4kc3RhdHVzSW5kaWNhdG9yLmhpZGUoKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogUmVtb3ZlIHRoZSBkZWJ1ZyBvdmVybGF5IGNvbXBsZXRlbHlcbiAgICAgKi9cbiAgICBkZXN0cm95KCkge1xuICAgICAgaWYgKHRoaXMuJGNvbnRhaW5lcikge1xuICAgICAgICB0aGlzLiRjb250YWluZXIucmVtb3ZlKCk7XG4gICAgICAgIHRoaXMuJGNvbnRhaW5lciA9IG51bGw7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy4kc3RhdHVzSW5kaWNhdG9yKSB7XG4gICAgICAgIHRoaXMuJHN0YXR1c0luZGljYXRvci5yZW1vdmUoKTtcbiAgICAgICAgdGhpcy4kc3RhdHVzSW5kaWNhdG9yID0gbnVsbDtcbiAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogVXBkYXRlIHRoZSBzdGF0dXMgaW5kaWNhdG9yXG4gICAgICovXG4gICAgdXBkYXRlU3RhdHVzKG1vZGUpIHtcbiAgICAgIGlmICghdGhpcy4kc3RhdHVzSW5kaWNhdG9yKVxuICAgICAgICByZXR1cm47XG4gICAgICB0aGlzLiRzdGF0dXNJbmRpY2F0b3IudGV4dChcIkRlYnVnOiBcIiArIG1vZGUpO1xuICAgICAgdGhpcy4kc3RhdHVzSW5kaWNhdG9yLmF0dHIoXCJjbGFzc1wiLCBcImpxaHRtbC1kZWJ1Zy1zdGF0dXNcIiArIChtb2RlICE9PSBcIk9mZlwiID8gXCIgYWN0aXZlXCIgOiBcIlwiKSk7XG4gICAgfVxuICAgIGNyZWF0ZU92ZXJsYXkoKSB7XG4gICAgICB0aGlzLmFkZFN0eWxlcygpO1xuICAgICAgdGhpcy4kY29udGFpbmVyID0gdGhpcy4kKFwiPGRpdj5cIikuYWRkQ2xhc3MoYGpxaHRtbC1kZWJ1Zy1vdmVybGF5ICR7dGhpcy5vcHRpb25zLnRoZW1lfSAke3RoaXMub3B0aW9ucy5wb3NpdGlvbn1gKTtcbiAgICAgIGNvbnN0ICRjb250ZW50ID0gdGhpcy4kKFwiPGRpdj5cIikuYWRkQ2xhc3MoXCJqcWh0bWwtZGVidWctY29udGVudFwiKTtcbiAgICAgIGNvbnN0ICRjb250cm9scyA9IHRoaXMuJChcIjxkaXY+XCIpLmFkZENsYXNzKFwianFodG1sLWRlYnVnLWNvbnRyb2xzXCIpO1xuICAgICAgY29uc3QgJHRpdGxlID0gdGhpcy4kKFwiPHNwYW4+XCIpLmFkZENsYXNzKFwianFodG1sLWRlYnVnLXRpdGxlXCIpLmh0bWwoXCI8c3Ryb25nPlxcdXsxRjQxQn0gSlFIVE1MIERlYnVnOjwvc3Ryb25nPlwiKTtcbiAgICAgICRjb250cm9scy5hcHBlbmQoJHRpdGxlKTtcbiAgICAgIGNvbnN0IGJ1dHRvbnMgPSBbXG4gICAgICAgIHsgdGV4dDogXCJTbG93IE1vdGlvbiArIEZsYXNoXCIsIGFjdGlvbjogXCJlbmFibGVTbG93TW90aW9uRGVidWdcIiwgY2xhc3M6IFwic3VjY2Vzc1wiIH0sXG4gICAgICAgIHsgdGV4dDogXCJCYXNpYyBEZWJ1Z1wiLCBhY3Rpb246IFwiZW5hYmxlQmFzaWNEZWJ1Z1wiLCBjbGFzczogXCJcIiB9LFxuICAgICAgICB7IHRleHQ6IFwiRnVsbCBEZWJ1Z1wiLCBhY3Rpb246IFwiZW5hYmxlRnVsbERlYnVnXCIsIGNsYXNzOiBcIlwiIH0sXG4gICAgICAgIHsgdGV4dDogXCJTZXF1ZW50aWFsXCIsIGFjdGlvbjogXCJlbmFibGVTZXF1ZW50aWFsTW9kZVwiLCBjbGFzczogXCJcIiB9LFxuICAgICAgICB7IHRleHQ6IFwiQ2xlYXIgRGVidWdcIiwgYWN0aW9uOiBcImNsZWFyQWxsRGVidWdcIiwgY2xhc3M6IFwiZGFuZ2VyXCIgfSxcbiAgICAgICAgeyB0ZXh0OiBcIlNldHRpbmdzXCIsIGFjdGlvbjogXCJzaG93RGVidWdJbmZvXCIsIGNsYXNzOiBcIlwiIH1cbiAgICAgIF07XG4gICAgICBidXR0b25zLmZvckVhY2goKGJ0bikgPT4ge1xuICAgICAgICBjb25zdCAkYnV0dG9uID0gdGhpcy4kKFwiPGJ1dHRvbj5cIikudGV4dChidG4udGV4dCkuYWRkQ2xhc3MoXCJqcWh0bWwtZGVidWctYnRuXCIgKyAoYnRuLmNsYXNzID8gYCAke2J0bi5jbGFzc31gIDogXCJcIikpLm9uKFwiY2xpY2tcIiwgKCkgPT4gdGhpcy5leGVjdXRlQWN0aW9uKGJ0bi5hY3Rpb24pKTtcbiAgICAgICAgJGNvbnRyb2xzLmFwcGVuZCgkYnV0dG9uKTtcbiAgICAgIH0pO1xuICAgICAgY29uc3QgJHRvZ2dsZUJ0biA9IHRoaXMuJChcIjxidXR0b24+XCIpLnRleHQodGhpcy5vcHRpb25zLmNvbXBhY3QgPyBcIlxcdTI1QkNcIiA6IFwiXFx1MjVCMlwiKS5hZGRDbGFzcyhcImpxaHRtbC1kZWJ1Zy10b2dnbGVcIikub24oXCJjbGlja1wiLCAoKSA9PiB0aGlzLnRvZ2dsZSgpKTtcbiAgICAgICRjb250cm9scy5hcHBlbmQoJHRvZ2dsZUJ0bik7XG4gICAgICAkY29udGVudC5hcHBlbmQoJGNvbnRyb2xzKTtcbiAgICAgIHRoaXMuJGNvbnRhaW5lci5hcHBlbmQoJGNvbnRlbnQpO1xuICAgICAgdGhpcy4kKFwiYm9keVwiKS5hcHBlbmQodGhpcy4kY29udGFpbmVyKTtcbiAgICB9XG4gICAgY3JlYXRlU3RhdHVzSW5kaWNhdG9yKCkge1xuICAgICAgdGhpcy4kc3RhdHVzSW5kaWNhdG9yID0gdGhpcy4kKFwiPGRpdj5cIikuYWRkQ2xhc3MoXCJqcWh0bWwtZGVidWctc3RhdHVzXCIpLnRleHQoXCJEZWJ1ZzogT2ZmXCIpLmNzcyh7XG4gICAgICAgIHBvc2l0aW9uOiBcImZpeGVkXCIsXG4gICAgICAgIHRvcDogXCIxMHB4XCIsXG4gICAgICAgIHJpZ2h0OiBcIjEwcHhcIixcbiAgICAgICAgYmFja2dyb3VuZDogXCIjMmMzZTUwXCIsXG4gICAgICAgIGNvbG9yOiBcIndoaXRlXCIsXG4gICAgICAgIHBhZGRpbmc6IFwiNXB4IDEwcHhcIixcbiAgICAgICAgYm9yZGVyUmFkaXVzOiBcIjRweFwiLFxuICAgICAgICBmb250U2l6ZTogXCIwLjc1cmVtXCIsXG4gICAgICAgIHpJbmRleDogXCIxMDAwMVwiLFxuICAgICAgICBvcGFjaXR5OiBcIjAuOFwiLFxuICAgICAgICBmb250RmFtaWx5OiBcIm1vbm9zcGFjZVwiXG4gICAgICB9KTtcbiAgICAgIHRoaXMuJChcImJvZHlcIikuYXBwZW5kKHRoaXMuJHN0YXR1c0luZGljYXRvcik7XG4gICAgfVxuICAgIGFkZFN0eWxlcygpIHtcbiAgICAgIGlmICh0aGlzLiQoXCIjanFodG1sLWRlYnVnLXN0eWxlc1wiKS5sZW5ndGggPiAwKVxuICAgICAgICByZXR1cm47XG4gICAgICBjb25zdCAkc3R5bGUgPSB0aGlzLiQoXCI8c3R5bGU+XCIpLmF0dHIoXCJpZFwiLCBcImpxaHRtbC1kZWJ1Zy1zdHlsZXNcIikudGV4dCgnLmpxaHRtbC1kZWJ1Zy1vdmVybGF5IHtwb3NpdGlvbjogZml4ZWQ7bGVmdDogMDtyaWdodDogMDt6LWluZGV4OiAxMDAwMDtmb250LWZhbWlseTogLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCBcIlNlZ29lIFVJXCIsIFJvYm90bywgbW9ub3NwYWNlO2ZvbnQtc2l6ZTogMC44cmVtO2JveC1zaGFkb3c6IDAgMnB4IDEwcHggcmdiYSgwLDAsMCwwLjIpO30uanFodG1sLWRlYnVnLW92ZXJsYXkudG9wIHt0b3A6IDA7fS5qcWh0bWwtZGVidWctb3ZlcmxheS5ib3R0b20ge2JvdHRvbTogMDt9LmpxaHRtbC1kZWJ1Zy1vdmVybGF5LmRhcmsge2JhY2tncm91bmQ6ICMzNDQ5NWU7Y29sb3I6ICNlY2YwZjE7fS5qcWh0bWwtZGVidWctb3ZlcmxheS5saWdodCB7YmFja2dyb3VuZDogI2Y4ZjlmYTtjb2xvcjogIzMzMztib3JkZXItYm90dG9tOiAxcHggc29saWQgI2RlZTJlNjt9LmpxaHRtbC1kZWJ1Zy1jb250ZW50IHtwYWRkaW5nOiAwLjVyZW0gMXJlbTt9LmpxaHRtbC1kZWJ1Zy1jb250cm9scyB7ZGlzcGxheTogZmxleDtmbGV4LXdyYXA6IHdyYXA7Z2FwOiA4cHg7YWxpZ24taXRlbXM6IGNlbnRlcjt9LmpxaHRtbC1kZWJ1Zy10aXRsZSB7bWFyZ2luLXJpZ2h0OiAxMHB4O2ZvbnQtd2VpZ2h0OiBib2xkO30uanFodG1sLWRlYnVnLWJ0biB7cGFkZGluZzogNHB4IDhweDtib3JkZXI6IG5vbmU7Ym9yZGVyLXJhZGl1czogM3B4O2JhY2tncm91bmQ6ICMzNDk4ZGI7Y29sb3I6IHdoaXRlO2N1cnNvcjogcG9pbnRlcjtmb250LXNpemU6IDAuNzVyZW07dHJhbnNpdGlvbjogYmFja2dyb3VuZCAwLjJzO30uanFodG1sLWRlYnVnLWJ0bjpob3ZlciB7YmFja2dyb3VuZDogIzI5ODBiOTt9LmpxaHRtbC1kZWJ1Zy1idG4uc3VjY2VzcyB7YmFja2dyb3VuZDogIzI3YWU2MDt9LmpxaHRtbC1kZWJ1Zy1idG4uc3VjY2Vzczpob3ZlciB7YmFja2dyb3VuZDogIzIyOTk1NDt9LmpxaHRtbC1kZWJ1Zy1idG4uZGFuZ2VyIHtiYWNrZ3JvdW5kOiAjZTc0YzNjO30uanFodG1sLWRlYnVnLWJ0bi5kYW5nZXI6aG92ZXIge2JhY2tncm91bmQ6ICNjMDM5MmI7fS5qcWh0bWwtZGVidWctdG9nZ2xlIHtwYWRkaW5nOiA0cHggOHB4O2JvcmRlcjogbm9uZTtib3JkZXItcmFkaXVzOiAzcHg7YmFja2dyb3VuZDogIzdmOGM4ZDtjb2xvcjogd2hpdGU7Y3Vyc29yOiBwb2ludGVyO2ZvbnQtc2l6ZTogMC43NXJlbTttYXJnaW4tbGVmdDogYXV0bzt9LmpxaHRtbC1kZWJ1Zy10b2dnbGU6aG92ZXIge2JhY2tncm91bmQ6ICM2YzdiN2Q7fS5qcWh0bWwtZGVidWctc3RhdHVzLmFjdGl2ZSB7YmFja2dyb3VuZDogIzI3YWU2MCAhaW1wb3J0YW50O31AbWVkaWEgKG1heC13aWR0aDogNzY4cHgpIHsuanFodG1sLWRlYnVnLWNvbnRyb2xzIHtmbGV4LWRpcmVjdGlvbjogY29sdW1uO2FsaWduLWl0ZW1zOiBmbGV4LXN0YXJ0O30uanFodG1sLWRlYnVnLXRpdGxlIHttYXJnaW4tYm90dG9tOiA1cHg7fX0nKTtcbiAgICAgIHRoaXMuJChcImhlYWRcIikuYXBwZW5kKCRzdHlsZSk7XG4gICAgfVxuICAgIHRvZ2dsZSgpIHtcbiAgICAgIHRoaXMub3B0aW9ucy5jb21wYWN0ID0gIXRoaXMub3B0aW9ucy5jb21wYWN0O1xuICAgICAgY29uc3QgJHRvZ2dsZUJ0biA9IHRoaXMuJGNvbnRhaW5lci5maW5kKFwiLmpxaHRtbC1kZWJ1Zy10b2dnbGVcIik7XG4gICAgICAkdG9nZ2xlQnRuLnRleHQodGhpcy5vcHRpb25zLmNvbXBhY3QgPyBcIlxcdTI1QkNcIiA6IFwiXFx1MjVCMlwiKTtcbiAgICAgIGNvbnN0ICRidXR0b25zID0gdGhpcy4kY29udGFpbmVyLmZpbmQoXCIuanFodG1sLWRlYnVnLWJ0blwiKTtcbiAgICAgIGlmICh0aGlzLm9wdGlvbnMuY29tcGFjdCkge1xuICAgICAgICAkYnV0dG9ucy5oaWRlKCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAkYnV0dG9ucy5zaG93KCk7XG4gICAgICB9XG4gICAgfVxuICAgIGV4ZWN1dGVBY3Rpb24oYWN0aW9uKSB7XG4gICAgICBjb25zdCBqcWh0bWwyID0gZ2V0SnFodG1sKCk7XG4gICAgICBpZiAoIWpxaHRtbDIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKFwiSlFIVE1MIG5vdCBhdmFpbGFibGUgLSBtYWtlIHN1cmUgaXQncyBsb2FkZWQgYW5kIGV4cG9zZWQgZ2xvYmFsbHlcIik7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHN3aXRjaCAoYWN0aW9uKSB7XG4gICAgICAgIGNhc2UgXCJlbmFibGVTbG93TW90aW9uRGVidWdcIjpcbiAgICAgICAgICBqcWh0bWwyLnNldERlYnVnU2V0dGluZ3Moe1xuICAgICAgICAgICAgbG9nRnVsbExpZmVjeWNsZTogdHJ1ZSxcbiAgICAgICAgICAgIHNlcXVlbnRpYWxQcm9jZXNzaW5nOiB0cnVlLFxuICAgICAgICAgICAgZGVsYXlBZnRlckNvbXBvbmVudDogMTUwLFxuICAgICAgICAgICAgZGVsYXlBZnRlclJlbmRlcjogMjAwLFxuICAgICAgICAgICAgZGVsYXlBZnRlclJlcmVuZGVyOiAyNTAsXG4gICAgICAgICAgICBmbGFzaENvbXBvbmVudHM6IHRydWUsXG4gICAgICAgICAgICBmbGFzaER1cmF0aW9uOiA4MDAsXG4gICAgICAgICAgICBmbGFzaENvbG9yczoge1xuICAgICAgICAgICAgICBjcmVhdGU6IFwiIzM0OThkYlwiLFxuICAgICAgICAgICAgICByZW5kZXI6IFwiIzI3YWU2MFwiLFxuICAgICAgICAgICAgICByZWFkeTogXCIjOWI1OWI2XCJcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBwcm9maWxlUGVyZm9ybWFuY2U6IHRydWUsXG4gICAgICAgICAgICBoaWdobGlnaHRTbG93UmVuZGVyczogMzAsXG4gICAgICAgICAgICBsb2dEaXNwYXRjaDogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHRoaXMudXBkYXRlU3RhdHVzKFwiU2xvdyBNb3Rpb25cIik7XG4gICAgICAgICAgY29uc29sZS5sb2coXCJcXHV7MUY0MUJ9IFNsb3cgTW90aW9uIERlYnVnIE1vZGUgRW5hYmxlZFwiKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcImVuYWJsZUJhc2ljRGVidWdcIjpcbiAgICAgICAgICBqcWh0bWwyLmVuYWJsZURlYnVnTW9kZShcImJhc2ljXCIpO1xuICAgICAgICAgIHRoaXMudXBkYXRlU3RhdHVzKFwiQmFzaWNcIik7XG4gICAgICAgICAgY29uc29sZS5sb2coXCJcXHV7MUY0MUJ9IEJhc2ljIERlYnVnIE1vZGUgRW5hYmxlZFwiKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcImVuYWJsZUZ1bGxEZWJ1Z1wiOlxuICAgICAgICAgIGpxaHRtbDIuZW5hYmxlRGVidWdNb2RlKFwiZnVsbFwiKTtcbiAgICAgICAgICB0aGlzLnVwZGF0ZVN0YXR1cyhcIkZ1bGxcIik7XG4gICAgICAgICAgY29uc29sZS5sb2coXCJcXHV7MUY0MUJ9IEZ1bGwgRGVidWcgTW9kZSBFbmFibGVkXCIpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZW5hYmxlU2VxdWVudGlhbE1vZGVcIjpcbiAgICAgICAgICBqcWh0bWwyLnNldERlYnVnU2V0dGluZ3Moe1xuICAgICAgICAgICAgbG9nQ3JlYXRpb25SZWFkeTogdHJ1ZSxcbiAgICAgICAgICAgIHNlcXVlbnRpYWxQcm9jZXNzaW5nOiB0cnVlLFxuICAgICAgICAgICAgZmxhc2hDb21wb25lbnRzOiB0cnVlLFxuICAgICAgICAgICAgcHJvZmlsZVBlcmZvcm1hbmNlOiB0cnVlXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgdGhpcy51cGRhdGVTdGF0dXMoXCJTZXF1ZW50aWFsXCIpO1xuICAgICAgICAgIGNvbnNvbGUubG9nKFwiXFx1ezFGNDFCfSBTZXF1ZW50aWFsIFByb2Nlc3NpbmcgTW9kZSBFbmFibGVkXCIpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiY2xlYXJBbGxEZWJ1Z1wiOlxuICAgICAgICAgIGpxaHRtbDIuY2xlYXJEZWJ1Z1NldHRpbmdzKCk7XG4gICAgICAgICAgdGhpcy51cGRhdGVTdGF0dXMoXCJPZmZcIik7XG4gICAgICAgICAgY29uc29sZS5sb2coXCJcXHV7MUY0MUJ9IEFsbCBEZWJ1ZyBNb2RlcyBEaXNhYmxlZFwiKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcInNob3dEZWJ1Z0luZm9cIjpcbiAgICAgICAgICBjb25zdCBzZXR0aW5ncyA9IEpTT04uc3RyaW5naWZ5KGpxaHRtbDIuZGVidWcsIG51bGwsIDIpO1xuICAgICAgICAgIGNvbnNvbGUubG9nKFwiXFx1ezFGNDFCfSBDdXJyZW50IERlYnVnIFNldHRpbmdzOlwiLCBzZXR0aW5ncyk7XG4gICAgICAgICAgYWxlcnQoXCJEZWJ1ZyBzZXR0aW5ncyBsb2dnZWQgdG8gY29uc29sZTpcXG5cXG5cIiArIChPYmplY3Qua2V5cyhqcWh0bWwyLmRlYnVnKS5sZW5ndGggPiAwID8gc2V0dGluZ3MgOiBcIk5vIGRlYnVnIHNldHRpbmdzIGFjdGl2ZVwiKSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9O1xuICBEZWJ1Z092ZXJsYXkuaW5zdGFuY2UgPSBudWxsO1xuICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIikge1xuICAgIGNvbnN0IHVybFBhcmFtcyA9IG5ldyBVUkxTZWFyY2hQYXJhbXMod2luZG93LmxvY2F0aW9uLnNlYXJjaCk7XG4gICAgaWYgKHVybFBhcmFtcy5nZXQoXCJkZWJ1Z1wiKSA9PT0gXCJ0cnVlXCIgfHwgdXJsUGFyYW1zLmdldChcImpxaHRtbC1kZWJ1Z1wiKSA9PT0gXCJ0cnVlXCIpIHtcbiAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJET01Db250ZW50TG9hZGVkXCIsICgpID0+IHtcbiAgICAgICAgRGVidWdPdmVybGF5LnNob3coKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICBmdW5jdGlvbiBpbml0X2pxdWVyeV9wbHVnaW4oalF1ZXJ5KSB7XG4gICAgaWYgKCFqUXVlcnkgfHwgIWpRdWVyeS5mbikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwialF1ZXJ5IGlzIHJlcXVpcmVkIGZvciBKUUhUTUwuIFBsZWFzZSBlbnN1cmUgalF1ZXJ5IGlzIGxvYWRlZCBiZWZvcmUgaW5pdGlhbGl6aW5nIEpRSFRNTC5cIik7XG4gICAgfVxuICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmIHdpbmRvdy4kICE9PSBqUXVlcnkgJiYgIWpRdWVyeS5fX2pxaHRtbF9jaGVja2VkKSB7XG4gICAgICBkZXZXYXJuKCdqUXVlcnkgaW5zdGFuY2UgYXBwZWFycyB0byBiZSBidW5kbGVkIHdpdGggd2VicGFjay9tb2R1bGVzIHJhdGhlciB0aGFuIGxvYWRlZCBnbG9iYWxseS5cXG5Gb3IgYmVzdCBjb21wYXRpYmlsaXR5LCBpdCBpcyByZWNvbW1lbmRlZCB0bzpcXG4xLiBJbmNsdWRlIGpRdWVyeSB2aWEgPHNjcmlwdD4gdGFnIGZyb20gYSBDRE4gKFVNRCBmb3JtYXQpXFxuMi4gQ29uZmlndXJlIHdlYnBhY2sgd2l0aDogZXh0ZXJuYWxzOiB7IGpxdWVyeTogXCIkXCIgfVxcbjMuIFJlbW92ZSBqcXVlcnkgZnJvbSBwYWNrYWdlLmpzb24gZGVwZW5kZW5jaWVzXFxuXFxuVG8gc3VwcHJlc3MgdGhpcyB3YXJuaW5nLCBzZXQ6IHdpbmRvdy5KUUhUTUxfU1VQUFJFU1NfV0FSTklOR1MgPSB0cnVlJyk7XG4gICAgICBqUXVlcnkuX19qcWh0bWxfY2hlY2tlZCA9IHRydWU7XG4gICAgfVxuICAgIGNvbnN0IF9qcWh0bWxfb3JpZ2luYWxfanF1ZXJ5ID0galF1ZXJ5O1xuICAgIGNvbnN0IEpRdWVyeVdpdGhDb21wb25lbnRTdXBwb3J0ID0gZnVuY3Rpb24oc2VsZWN0b3IsIGNvbnRleHQpIHtcbiAgICAgIGlmIChzZWxlY3RvciAmJiB0eXBlb2Ygc2VsZWN0b3IgPT09IFwib2JqZWN0XCIgJiYgc2VsZWN0b3IuJCAmJiB0eXBlb2Ygc2VsZWN0b3IuJGlkID09PSBcImZ1bmN0aW9uXCIgJiYgdHlwZW9mIHNlbGVjdG9yLmlkID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgcmV0dXJuIHNlbGVjdG9yLiQ7XG4gICAgICB9XG4gICAgICByZXR1cm4gbmV3IF9qcWh0bWxfb3JpZ2luYWxfanF1ZXJ5KHNlbGVjdG9yLCBjb250ZXh0KTtcbiAgICB9O1xuICAgIE9iamVjdC5zZXRQcm90b3R5cGVPZihKUXVlcnlXaXRoQ29tcG9uZW50U3VwcG9ydCwgX2pxaHRtbF9vcmlnaW5hbF9qcXVlcnkpO1xuICAgIGZvciAoY29uc3Qga2V5IGluIF9qcWh0bWxfb3JpZ2luYWxfanF1ZXJ5KSB7XG4gICAgICBpZiAoX2pxaHRtbF9vcmlnaW5hbF9qcXVlcnkuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgICBKUXVlcnlXaXRoQ29tcG9uZW50U3VwcG9ydFtrZXldID0gX2pxaHRtbF9vcmlnaW5hbF9qcXVlcnlba2V5XTtcbiAgICAgIH1cbiAgICB9XG4gICAgSlF1ZXJ5V2l0aENvbXBvbmVudFN1cHBvcnQucHJvdG90eXBlID0gX2pxaHRtbF9vcmlnaW5hbF9qcXVlcnkucHJvdG90eXBlO1xuICAgIEpRdWVyeVdpdGhDb21wb25lbnRTdXBwb3J0LmZuID0gX2pxaHRtbF9vcmlnaW5hbF9qcXVlcnkuZm47XG4gICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICAgIHdpbmRvdy5qUXVlcnkgPSBKUXVlcnlXaXRoQ29tcG9uZW50U3VwcG9ydDtcbiAgICAgIHdpbmRvdy4kID0gSlF1ZXJ5V2l0aENvbXBvbmVudFN1cHBvcnQ7XG4gICAgfVxuICAgIGpRdWVyeSA9IEpRdWVyeVdpdGhDb21wb25lbnRTdXBwb3J0O1xuICAgIGNvbnN0IG9yaWdpbmFsVmFsID0galF1ZXJ5LmZuLnZhbDtcbiAgICBqUXVlcnkuZm4udmFsID0gZnVuY3Rpb24odmFsdWUpIHtcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIGNvbnN0IGZpcnN0RWwgPSB0aGlzLmZpcnN0KCk7XG4gICAgICAgIGlmIChmaXJzdEVsLmxlbmd0aCA9PT0gMClcbiAgICAgICAgICByZXR1cm4gdm9pZCAwO1xuICAgICAgICBjb25zdCBjb21wb25lbnQgPSBmaXJzdEVsLmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICBjb25zdCB0YWdOYW1lID0gZmlyc3RFbC5wcm9wKFwidGFnTmFtZVwiKTtcbiAgICAgICAgaWYgKGNvbXBvbmVudCAmJiB0eXBlb2YgY29tcG9uZW50LnZhbCA9PT0gXCJmdW5jdGlvblwiICYmIHRhZ05hbWUgIT09IFwiSU5QVVRcIiAmJiB0YWdOYW1lICE9PSBcIlRFWFRBUkVBXCIpIHtcbiAgICAgICAgICByZXR1cm4gY29tcG9uZW50LnZhbCgpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBvcmlnaW5hbFZhbC5jYWxsKHRoaXMpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICAgIGNvbnN0ICRlbCA9IGpRdWVyeSh0aGlzKTtcbiAgICAgICAgICBjb25zdCBjb21wb25lbnQgPSAkZWwuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgICAgY29uc3QgdGFnTmFtZSA9ICRlbC5wcm9wKFwidGFnTmFtZVwiKTtcbiAgICAgICAgICBpZiAoY29tcG9uZW50ICYmIHR5cGVvZiBjb21wb25lbnQudmFsID09PSBcImZ1bmN0aW9uXCIgJiYgdGFnTmFtZSAhPT0gXCJJTlBVVFwiICYmIHRhZ05hbWUgIT09IFwiVEVYVEFSRUFcIikge1xuICAgICAgICAgICAgY29tcG9uZW50LnZhbCh2YWx1ZSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIG9yaWdpbmFsVmFsLmNhbGwoJGVsLCB2YWx1ZSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICB9XG4gICAgfTtcbiAgICBqUXVlcnkuZm4uY29tcG9uZW50ID0gZnVuY3Rpb24oY29tcG9uZW50T3JOYW1lLCBhcmdzID0ge30pIHtcbiAgICAgIGNvbnN0IGVsZW1lbnQgPSB0aGlzLmZpcnN0ID8gdGhpcy5maXJzdCgpIDogdGhpcztcbiAgICAgIGlmICghY29tcG9uZW50T3JOYW1lKSB7XG4gICAgICAgIGlmIChlbGVtZW50Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGNvbXAgPSBlbGVtZW50LmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICByZXR1cm4gY29tcCB8fCBudWxsO1xuICAgICAgfVxuICAgICAgY29uc3QgZXhpc3RpbmdDb21wb25lbnQgPSBlbGVtZW50LmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgaWYgKGV4aXN0aW5nQ29tcG9uZW50KSB7XG4gICAgICAgIHJldHVybiBleGlzdGluZ0NvbXBvbmVudDtcbiAgICAgIH1cbiAgICAgIGxldCBDb21wb25lbnRDbGFzcztcbiAgICAgIGxldCBjb21wb25lbnROYW1lO1xuICAgICAgaWYgKHR5cGVvZiBjb21wb25lbnRPck5hbWUgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgY29tcG9uZW50TmFtZSA9IGNvbXBvbmVudE9yTmFtZTtcbiAgICAgICAgY29uc3QgZm91bmQgPSBnZXRfY29tcG9uZW50X2NsYXNzKGNvbXBvbmVudE9yTmFtZSk7XG4gICAgICAgIGFyZ3MgPSB7IC4uLmFyZ3MsIF9jb21wb25lbnRfbmFtZTogY29tcG9uZW50TmFtZSB9O1xuICAgICAgICBpZiAoIWZvdW5kKSB7XG4gICAgICAgICAgQ29tcG9uZW50Q2xhc3MgPSBKcWh0bWxfQ29tcG9uZW50O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIENvbXBvbmVudENsYXNzID0gZm91bmQ7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIENvbXBvbmVudENsYXNzID0gY29tcG9uZW50T3JOYW1lO1xuICAgICAgfVxuICAgICAgbGV0IHRhcmdldEVsZW1lbnQgPSBlbGVtZW50O1xuICAgICAgaWYgKGNvbXBvbmVudE5hbWUpIHtcbiAgICAgICAgY29uc3QgdGVtcGxhdGUgPSBnZXRfdGVtcGxhdGUoY29tcG9uZW50TmFtZSk7XG4gICAgICAgIGNvbnN0IGV4cGVjdGVkVGFnID0gYXJncy5fdGFnIHx8IHRlbXBsYXRlLnRhZyB8fCBcImRpdlwiO1xuICAgICAgICBjb25zdCBjdXJyZW50VGFnID0gZWxlbWVudC5wcm9wKFwidGFnTmFtZVwiKS50b0xvd2VyQ2FzZSgpO1xuICAgICAgICBpZiAoY3VycmVudFRhZyAhPT0gZXhwZWN0ZWRUYWcudG9Mb3dlckNhc2UoKSkge1xuICAgICAgICAgIGlmIChhcmdzLl9pbm5lcl9odG1sKSB7XG4gICAgICAgICAgICBjb25zdCBuZXdFbGVtZW50ID0galF1ZXJ5KGA8JHtleHBlY3RlZFRhZ30+PC8ke2V4cGVjdGVkVGFnfT5gKTtcbiAgICAgICAgICAgIGNvbnN0IG9sZEVsID0gZWxlbWVudFswXTtcbiAgICAgICAgICAgIGlmIChvbGRFbCAmJiBvbGRFbC5hdHRyaWJ1dGVzKSB7XG4gICAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgb2xkRWwuYXR0cmlidXRlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIGNvbnN0IGF0dHIgPSBvbGRFbC5hdHRyaWJ1dGVzW2ldO1xuICAgICAgICAgICAgICAgIG5ld0VsZW1lbnQuYXR0cihhdHRyLm5hbWUsIGF0dHIudmFsdWUpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBuZXdFbGVtZW50Lmh0bWwoZWxlbWVudC5odG1sKCkpO1xuICAgICAgICAgICAgZWxlbWVudC5yZXBsYWNlV2l0aChuZXdFbGVtZW50KTtcbiAgICAgICAgICAgIHRhcmdldEVsZW1lbnQgPSBuZXdFbGVtZW50O1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIENvbXBvbmVudCAnJHtjb21wb25lbnROYW1lfScgZXhwZWN0cyB0YWcgJzwke2V4cGVjdGVkVGFnfT4nIGJ1dCBlbGVtZW50IGlzICc8JHtjdXJyZW50VGFnfT4nLiBFbGVtZW50IHRhZyB3aWxsIG5vdCBiZSBjaGFuZ2VkLiBDb25zaWRlciB1c2luZyB0aGUgY29ycmVjdCB0YWcuYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjb25zdCBjb21wb25lbnQgPSBuZXcgQ29tcG9uZW50Q2xhc3ModGFyZ2V0RWxlbWVudCwgYXJncyk7XG4gICAgICBjb21wb25lbnQuYm9vdCgpO1xuICAgICAgYXBwbHlEZWJ1Z0RlbGF5KFwiY29tcG9uZW50XCIpO1xuICAgICAgcmV0dXJuIGNvbXBvbmVudDtcbiAgICB9O1xuICAgIGNvbnN0IF9qcWh0bWxfanF1ZXJ5X292ZXJyaWRlcyA9IHt9O1xuICAgIGNvbnN0IGRvbV9pbnNlcnRpb25fbWV0aG9kcyA9IFtcImFwcGVuZFwiLCBcInByZXBlbmRcIiwgXCJiZWZvcmVcIiwgXCJhZnRlclwiLCBcInJlcGxhY2VXaXRoXCJdO1xuICAgIGZvciAoY29uc3QgZm5uYW1lIG9mIGRvbV9pbnNlcnRpb25fbWV0aG9kcykge1xuICAgICAgX2pxaHRtbF9qcXVlcnlfb3ZlcnJpZGVzW2ZubmFtZV0gPSBqUXVlcnkuZm5bZm5uYW1lXTtcbiAgICAgIGpRdWVyeS5mbltmbm5hbWVdID0gZnVuY3Rpb24oLi4uYXJncykge1xuICAgICAgICBjb25zdCByZXNvbHZlZEFyZ3MgPSBhcmdzLm1hcCgoYXJnKSA9PiB7XG4gICAgICAgICAgaWYgKGFyZyAmJiB0eXBlb2YgYXJnID09PSBcIm9iamVjdFwiICYmIGFyZyBpbnN0YW5jZW9mIEpxaHRtbF9Db21wb25lbnQpIHtcbiAgICAgICAgICAgIHJldHVybiBhcmcuJDtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIGFyZztcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0ICRlbGVtZW50cyA9IHJlc29sdmVkQXJncy5maWx0ZXIoKGFyZykgPT4gYXJnIGluc3RhbmNlb2YgalF1ZXJ5KTtcbiAgICAgICAgY29uc3QgcmV0ID0gX2pxaHRtbF9qcXVlcnlfb3ZlcnJpZGVzW2ZubmFtZV0uYXBwbHkodGhpcywgcmVzb2x2ZWRBcmdzKTtcbiAgICAgICAgZm9yIChjb25zdCAkZSBvZiAkZWxlbWVudHMpIHtcbiAgICAgICAgICBpZiAoJGUuY2xvc2VzdChcImh0bWxcIikubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgJGUuZmluZChcIi5KcWh0bWxfQ29tcG9uZW50XCIpLmFkZEJhY2soXCIuSnFodG1sX0NvbXBvbmVudFwiKS5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgICBjb25zdCAkY29tcCA9IGpRdWVyeSh0aGlzKTtcbiAgICAgICAgICAgICAgY29uc3QgY29tcG9uZW50ID0gJGNvbXAuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgICAgICAgIGlmIChjb21wb25lbnQgJiYgIWNvbXBvbmVudC5fcmVhZHlfc3RhdGUpIHtcbiAgICAgICAgICAgICAgICBjb21wb25lbnQuYm9vdCgpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJldDtcbiAgICAgIH07XG4gICAgfVxuICAgIGpRdWVyeS5mbi5zaGFsbG93RmluZCA9IGZ1bmN0aW9uKHNlbGVjdG9yKSB7XG4gICAgICBjb25zdCByZXN1bHRzID0gW107XG4gICAgICB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgIGNvbnN0IHRyYXZlcnNlID0gKHBhcmVudCkgPT4ge1xuICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGFyZW50LmNoaWxkcmVuLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCBjaGlsZCA9IHBhcmVudC5jaGlsZHJlbltpXTtcbiAgICAgICAgICAgIGlmIChqUXVlcnkoY2hpbGQpLmlzKHNlbGVjdG9yKSkge1xuICAgICAgICAgICAgICByZXN1bHRzLnB1c2goY2hpbGQpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdHJhdmVyc2UoY2hpbGQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgdHJhdmVyc2UodGhpcyk7XG4gICAgICB9KTtcbiAgICAgIHJldHVybiBqUXVlcnkocmVzdWx0cyk7XG4gICAgfTtcbiAgICBjb25zdCBvcmlnaW5hbEVtcHR5ID0galF1ZXJ5LmZuLmVtcHR5O1xuICAgIGNvbnN0IG9yaWdpbmFsSHRtbCA9IGpRdWVyeS5mbi5odG1sO1xuICAgIGNvbnN0IG9yaWdpbmFsVGV4dCA9IGpRdWVyeS5mbi50ZXh0O1xuICAgIGpRdWVyeS5mbi5lbXB0eSA9IGZ1bmN0aW9uKCkge1xuICAgICAgcmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgalF1ZXJ5KHRoaXMpLmZpbmQoXCIuSnFodG1sX0NvbXBvbmVudFwiKS5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICAgIGNvbnN0IGNvbXBvbmVudCA9IGpRdWVyeSh0aGlzKS5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgICBpZiAoY29tcG9uZW50ICYmICFjb21wb25lbnQuX3N0b3BwZWQpIHtcbiAgICAgICAgICAgIGNvbXBvbmVudC5fc3RvcCgpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIG9yaWdpbmFsRW1wdHkuY2FsbChqUXVlcnkodGhpcykpO1xuICAgICAgfSk7XG4gICAgfTtcbiAgICBqUXVlcnkuZm4uaHRtbCA9IGZ1bmN0aW9uKHZhbHVlKSB7XG4gICAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICByZXR1cm4gb3JpZ2luYWxIdG1sLmNhbGwodGhpcyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICBqUXVlcnkodGhpcykuZW1wdHkoKTtcbiAgICAgICAgb3JpZ2luYWxIdG1sLmNhbGwoalF1ZXJ5KHRoaXMpLCB2YWx1ZSk7XG4gICAgICB9KTtcbiAgICB9O1xuICAgIGpRdWVyeS5mbi50ZXh0ID0gZnVuY3Rpb24odmFsdWUpIHtcbiAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHJldHVybiBvcmlnaW5hbFRleHQuY2FsbCh0aGlzKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgIGpRdWVyeSh0aGlzKS5lbXB0eSgpO1xuICAgICAgICBvcmlnaW5hbFRleHQuY2FsbChqUXVlcnkodGhpcyksIHZhbHVlKTtcbiAgICAgIH0pO1xuICAgIH07XG4gIH1cbiAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgd2luZG93LmpRdWVyeSkge1xuICAgIGluaXRfanF1ZXJ5X3BsdWdpbih3aW5kb3cualF1ZXJ5KTtcbiAgfVxuICB2YXIgdmVyc2lvbiA9IFwiMi4yLjE4NVwiO1xuICB2YXIganFodG1sID0ge1xuICAgIC8vIENvcmVcbiAgICBKcWh0bWxfQ29tcG9uZW50LFxuICAgIExpZmVjeWNsZU1hbmFnZXIsXG4gICAgLy8gUmVnaXN0cnlcbiAgICByZWdpc3Rlcl9jb21wb25lbnQsXG4gICAgZ2V0X2NvbXBvbmVudF9jbGFzcyxcbiAgICByZWdpc3Rlcl90ZW1wbGF0ZSxcbiAgICBnZXRfdGVtcGxhdGUsXG4gICAgZ2V0X3RlbXBsYXRlX2J5X2NsYXNzLFxuICAgIGNyZWF0ZV9jb21wb25lbnQsXG4gICAgaGFzX2NvbXBvbmVudCxcbiAgICBnZXRfY29tcG9uZW50X25hbWVzLFxuICAgIGdldF9yZWdpc3RlcmVkX3RlbXBsYXRlcyxcbiAgICBsaXN0X2NvbXBvbmVudHMsXG4gICAgLy8gVGVtcGxhdGUgc3lzdGVtXG4gICAgcHJvY2Vzc19pbnN0cnVjdGlvbnMsXG4gICAgZXh0cmFjdF9zbG90cyxcbiAgICByZW5kZXJfdGVtcGxhdGUsXG4gICAgZXNjYXBlX2h0bWwsXG4gICAgLy8gVmVyc2lvbiBwcm9wZXJ0eSAtIGludGVybmFsXG4gICAgX192ZXJzaW9uOiB2ZXJzaW9uLFxuICAgIC8vIERlYnVnIHNldHRpbmdzXG4gICAgZGVidWc6IHtcbiAgICAgIGVuYWJsZWQ6IGZhbHNlLFxuICAgICAgdmVyYm9zZTogZmFsc2VcbiAgICB9LFxuICAgIC8vIERlYnVnIGhlbHBlciBmdW5jdGlvbnMgKG1haW5seSBmb3IgaW50ZXJuYWwgdXNlIGJ1dCBleHBvc2VkIGZvciBhZHZhbmNlZCBkZWJ1Z2dpbmcpXG4gICAgc2V0RGVidWdTZXR0aW5ncyhzZXR0aW5ncykge1xuICAgICAgT2JqZWN0LmFzc2lnbih0aGlzLmRlYnVnLCBzZXR0aW5ncyk7XG4gICAgfSxcbiAgICBlbmFibGVEZWJ1Z01vZGUobGV2ZWwgPSBcImJhc2ljXCIpIHtcbiAgICAgIGlmIChsZXZlbCA9PT0gXCJiYXNpY1wiKSB7XG4gICAgICAgIHRoaXMuZGVidWcubG9nQ3JlYXRpb25SZWFkeSA9IHRydWU7XG4gICAgICAgIHRoaXMuZGVidWcubG9nRGlzcGF0Y2ggPSB0cnVlO1xuICAgICAgICB0aGlzLmRlYnVnLmZsYXNoQ29tcG9uZW50cyA9IHRydWU7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmRlYnVnLmxvZ0Z1bGxMaWZlY3ljbGUgPSB0cnVlO1xuICAgICAgICB0aGlzLmRlYnVnLmxvZ0Rpc3BhdGNoVmVyYm9zZSA9IHRydWU7XG4gICAgICAgIHRoaXMuZGVidWcuZmxhc2hDb21wb25lbnRzID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5kZWJ1Zy5wcm9maWxlUGVyZm9ybWFuY2UgPSB0cnVlO1xuICAgICAgICB0aGlzLmRlYnVnLnRyYWNlRGF0YUZsb3cgPSB0cnVlO1xuICAgICAgfVxuICAgIH0sXG4gICAgY2xlYXJEZWJ1Z1NldHRpbmdzKCkge1xuICAgICAgdGhpcy5kZWJ1ZyA9IHt9O1xuICAgIH0sXG4gICAgLy8gRGVidWcgb3ZlcmxheSBtZXRob2RzXG4gICAgc2hvd0RlYnVnT3ZlcmxheShvcHRpb25zKSB7XG4gICAgICByZXR1cm4gRGVidWdPdmVybGF5LnNob3cob3B0aW9ucyk7XG4gICAgfSxcbiAgICBoaWRlRGVidWdPdmVybGF5KCkge1xuICAgICAgcmV0dXJuIERlYnVnT3ZlcmxheS5oaWRlKCk7XG4gICAgfSxcbiAgICAvLyBFeHBvcnQgRGVidWdPdmVybGF5IGNsYXNzIGZvciBkaXJlY3QgYWNjZXNzXG4gICAgRGVidWdPdmVybGF5LFxuICAgIC8vIEluc3RhbGwgZ2xvYmFscyBmdW5jdGlvblxuICAgIGluc3RhbGxHbG9iYWxzKCkge1xuICAgICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICAgICAgd2luZG93LmpxaHRtbCA9IHRoaXM7XG4gICAgICAgIHdpbmRvdy5KcWh0bWxfQ29tcG9uZW50ID0gSnFodG1sX0NvbXBvbmVudDtcbiAgICAgICAgd2luZG93LkpxaHRtbF9MaWZlY3ljbGVNYW5hZ2VyID0gTGlmZWN5Y2xlTWFuYWdlcjtcbiAgICAgIH1cbiAgICB9LFxuICAgIC8vIFZlcnNpb24gZGlzcGxheSBmdW5jdGlvbiAtIHNob3dzIHZlcnNpb24gb2YgY29yZSBsaWJyYXJ5IGFuZCBhbGwgcmVnaXN0ZXJlZCB0ZW1wbGF0ZXNcbiAgICBfdmVyc2lvbigpIHtcbiAgICAgIGNvbnNvbGUubG9nKGBKUUhUTUwgQ29yZSB2JHt0aGlzLl9fdmVyc2lvbn1gKTtcbiAgICAgIGNvbnNvbGUubG9nKFwiUmVnaXN0ZXJlZCBUZW1wbGF0ZXM6XCIpO1xuICAgICAgY29uc3QgdGVtcGxhdGVOYW1lcyA9IGdldF9jb21wb25lbnRfbmFtZXMoKTtcbiAgICAgIGlmICh0ZW1wbGF0ZU5hbWVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICBjb25zb2xlLmxvZyhcIiAgKG5vIHRlbXBsYXRlcyByZWdpc3RlcmVkKVwiKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGZvciAoY29uc3QgbmFtZSBvZiB0ZW1wbGF0ZU5hbWVzKSB7XG4gICAgICAgICAgY29uc3QgdGVtcGxhdGUgPSBnZXRfdGVtcGxhdGUobmFtZSk7XG4gICAgICAgICAgY29uc3QgdGVtcGxhdGVWZXJzaW9uID0gdGVtcGxhdGUgPyB0ZW1wbGF0ZS5fanFodG1sX3ZlcnNpb24gfHwgXCJ1bmtub3duXCIgOiBcInVua25vd25cIjtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgICAtICR7bmFtZX06IHYke3RlbXBsYXRlVmVyc2lvbn1gKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuX192ZXJzaW9uO1xuICAgIH0sXG4gICAgLy8gUHVibGljIHZlcnNpb24gZnVuY3Rpb24gLSByZXR1cm5zIHRoZSBzdGFtcGVkIHZlcnNpb24gbnVtYmVyXG4gICAgdmVyc2lvbigpIHtcbiAgICAgIHJldHVybiB2ZXJzaW9uO1xuICAgIH1cbiAgfTtcbiAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIgJiYgIXdpbmRvdy5qcWh0bWwpIHtcbiAgICB3aW5kb3cuanFodG1sID0ganFodG1sO1xuICAgIHdpbmRvdy5KcWh0bWxfQ29tcG9uZW50ID0gSnFodG1sX0NvbXBvbmVudDtcbiAgICB3aW5kb3cuQ29tcG9uZW50ID0gSnFodG1sX0NvbXBvbmVudDtcbiAgICB3aW5kb3cuSnFodG1sX0xpZmVjeWNsZU1hbmFnZXIgPSBMaWZlY3ljbGVNYW5hZ2VyO1xuICAgIGlmIChqcWh0bWwuZGVidWc/LmVuYWJsZWQpIHtcbiAgICAgIGNvbnNvbGUubG9nKFwiW0pRSFRNTF0gQXV0by1yZWdpc3RlcmVkIHdpbmRvdy5qcWh0bWwgZ2xvYmFsIGZvciB0ZW1wbGF0ZSBjb21wYXRpYmlsaXR5XCIpO1xuICAgIH1cbiAgfVxuXG4gIC8vIHN0b3JhZ2UvcnN4LXRtcC9ucG0tY29tcGlsZS9lbnRyeV82NDU5ZThlZDBmNjBiZGE0ZjEyMTQyMDc2NjAxMmQ1My5qc1xuICB3aW5kb3cuX3JzeF9ucG0gPSB3aW5kb3cuX3JzeF9ucG0gfHwge307XG4gIHdpbmRvdy5fcnN4X25wbS5qcWh0bWwgPSBqcWh0bWw7XG4gIHdpbmRvdy5fcnN4X25wbS5fQmFzZV9KcWh0bWxfQ29tcG9uZW50ID0gSnFodG1sX0NvbXBvbmVudDtcbn0pKCk7XG4iXX0=
|