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_Quill_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,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0b3JhZ2UvcnN4LWJ1aWxkL2J1bmRsZXMvbnBtX1F1aWxsX0J1bmRsZV82NDU5ZThlZDBmNjBiZGE0ZjEyMTQyMDc2NjAxMmQ1My5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiYnVuZGxlX291dHB1dF9RdWlsbF9CdW5kbGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKCgpID0+IHtcbiAgLy8gbm9kZV9tb2R1bGVzL0BqcWh0bWwvY29yZS9kaXN0L2luZGV4LmpzXG4gIHZhciBMaWZlY3ljbGVNYW5hZ2VyID0gY2xhc3MgX0xpZmVjeWNsZU1hbmFnZXIge1xuICAgIHN0YXRpYyBnZXRfaW5zdGFuY2UoKSB7XG4gICAgICBpZiAoIV9MaWZlY3ljbGVNYW5hZ2VyLmluc3RhbmNlKSB7XG4gICAgICAgIF9MaWZlY3ljbGVNYW5hZ2VyLmluc3RhbmNlID0gbmV3IF9MaWZlY3ljbGVNYW5hZ2VyKCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gX0xpZmVjeWNsZU1hbmFnZXIuaW5zdGFuY2U7XG4gICAgfVxuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgdGhpcy5hY3RpdmVfY29tcG9uZW50cyA9IC8qIEBfX1BVUkVfXyAqLyBuZXcgU2V0KCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEJvb3QgYSBjb21wb25lbnQgLSBydW4gaXRzIGZ1bGwgbGlmZWN5Y2xlXG4gICAgICogQ2FsbGVkIHdoZW4gY29tcG9uZW50IGlzIGNyZWF0ZWRcbiAgICAgKi9cbiAgICBhc3luYyBib290X2NvbXBvbmVudChjb21wb25lbnQpIHtcbiAgICAgIHRoaXMuYWN0aXZlX2NvbXBvbmVudHMuYWRkKGNvbXBvbmVudCk7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBjb21wb25lbnQuY3JlYXRlKCk7XG4gICAgICAgIGlmIChjb21wb25lbnQuX3N0b3BwZWQpXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICBjb21wb25lbnQudHJpZ2dlcihcImNyZWF0ZVwiKTtcbiAgICAgICAgbGV0IHJlbmRlcl9pZCA9IGNvbXBvbmVudC5fcmVuZGVyKCk7XG4gICAgICAgIGlmIChjb21wb25lbnQuX3N0b3BwZWQpXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICBhd2FpdCBjb21wb25lbnQubG9hZCgpO1xuICAgICAgICBpZiAoY29tcG9uZW50Ll9zdG9wcGVkKVxuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgaWYgKGNvbXBvbmVudC5zaG91bGRfcmVyZW5kZXIoKSkge1xuICAgICAgICAgIHJlbmRlcl9pZCA9IGNvbXBvbmVudC5fcmVuZGVyKCk7XG4gICAgICAgICAgaWYgKGNvbXBvbmVudC5fc3RvcHBlZClcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoY29tcG9uZW50Ll9yZW5kZXJfY291bnQgIT09IHJlbmRlcl9pZCkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCBjb21wb25lbnQucmVhZHkoKTtcbiAgICAgICAgaWYgKGNvbXBvbmVudC5fc3RvcHBlZClcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIGF3YWl0IGNvbXBvbmVudC50cmlnZ2VyKFwicmVhZHlcIik7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBjb25zb2xlLmVycm9yKGBFcnJvciBib290aW5nIGNvbXBvbmVudCAke2NvbXBvbmVudC5jb21wb25lbnRfbmFtZSgpfTpgLCBlcnJvcik7XG4gICAgICAgIHRocm93IGVycm9yO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBVbnJlZ2lzdGVyIGEgY29tcG9uZW50IChjYWxsZWQgb24gZGVzdHJveSlcbiAgICAgKi9cbiAgICB1bnJlZ2lzdGVyX2NvbXBvbmVudChjb21wb25lbnQpIHtcbiAgICAgIHRoaXMuYWN0aXZlX2NvbXBvbmVudHMuZGVsZXRlKGNvbXBvbmVudCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFdhaXQgZm9yIGFsbCBhY3RpdmUgY29tcG9uZW50cyB0byByZWFjaCByZWFkeSBzdGF0ZVxuICAgICAqL1xuICAgIGFzeW5jIHdhaXRfZm9yX3JlYWR5KCkge1xuICAgICAgY29uc3QgcmVhZHlfcHJvbWlzZXMgPSBbXTtcbiAgICAgIGZvciAoY29uc3QgY29tcG9uZW50IG9mIHRoaXMuYWN0aXZlX2NvbXBvbmVudHMpIHtcbiAgICAgICAgaWYgKGNvbXBvbmVudC5fcmVhZHlfc3RhdGUgPCA0KSB7XG4gICAgICAgICAgcmVhZHlfcHJvbWlzZXMucHVzaChuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICAgICAgY29tcG9uZW50Lm9uKFwicmVhZHlcIiwgKCkgPT4gcmVzb2x2ZSgpKTtcbiAgICAgICAgICB9KSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKHJlYWR5X3Byb21pc2VzKTtcbiAgICB9XG4gIH07XG4gIHZhciBjb21wb25lbnRfY2xhc3NlcyA9IC8qIEBfX1BVUkVfXyAqLyBuZXcgTWFwKCk7XG4gIHZhciBjb21wb25lbnRfdGVtcGxhdGVzID0gLyogQF9fUFVSRV9fICovIG5ldyBNYXAoKTtcbiAgdmFyIHdhcm5lZF9jb21wb25lbnRzID0gLyogQF9fUFVSRV9fICovIG5ldyBTZXQoKTtcbiAgdmFyIERFRkFVTFRfVEVNUExBVEUgPSB7XG4gICAgbmFtZTogXCJKcWh0bWxfQ29tcG9uZW50XCIsXG4gICAgLy8gRGVmYXVsdCBuYW1lXG4gICAgdGFnOiBcImRpdlwiLFxuICAgIHJlbmRlcjogZnVuY3Rpb24oZGF0YSwgYXJncywgY29udGVudCkge1xuICAgICAgY29uc3QgX291dHB1dCA9IFtdO1xuICAgICAgaWYgKGFyZ3MuX2lubmVyX2h0bWwpIHtcbiAgICAgICAgX291dHB1dC5wdXNoKGFyZ3MuX2lubmVyX2h0bWwpO1xuICAgICAgICByZXR1cm4gW19vdXRwdXQsIHRoaXNdO1xuICAgICAgfVxuICAgICAgaWYgKGNvbnRlbnQgJiYgdHlwZW9mIGNvbnRlbnQgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICBjb25zdCByZXN1bHQgPSBjb250ZW50KHRoaXMpO1xuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShyZXN1bHQpICYmIHJlc3VsdC5sZW5ndGggPT09IDIpIHtcbiAgICAgICAgICBfb3V0cHV0LnB1c2goLi4ucmVzdWx0WzBdKTtcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgcmVzdWx0ID09PSBcInN0cmluZ1wiKSB7XG4gICAgICAgICAgX291dHB1dC5wdXNoKHJlc3VsdCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBbX291dHB1dCwgdGhpc107XG4gICAgfVxuICB9O1xuICBmdW5jdGlvbiByZWdpc3Rlcl9jb21wb25lbnQobmFtZU9yQ2xhc3MsIGNvbXBvbmVudF9jbGFzcywgdGVtcGxhdGUpIHtcbiAgICBpZiAodHlwZW9mIG5hbWVPckNsYXNzID09PSBcInN0cmluZ1wiKSB7XG4gICAgICBjb25zdCBuYW1lID0gbmFtZU9yQ2xhc3M7XG4gICAgICBpZiAoIWNvbXBvbmVudF9jbGFzcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDb21wb25lbnQgY2xhc3MgaXMgcmVxdWlyZWQgd2hlbiByZWdpc3RlcmluZyBieSBuYW1lXCIpO1xuICAgICAgfVxuICAgICAgaWYgKCEvXltBLVpdLy50ZXN0KG5hbWUpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgQ29tcG9uZW50IG5hbWUgJyR7bmFtZX0nIG11c3Qgc3RhcnQgd2l0aCBhIGNhcGl0YWwgbGV0dGVyLiBDb252ZW50aW9uIGlzIEZpcnN0X0xldHRlcl9XaXRoX1VuZGVyc2NvcmVzLmApO1xuICAgICAgfVxuICAgICAgY29tcG9uZW50X2NsYXNzZXMuc2V0KG5hbWUsIGNvbXBvbmVudF9jbGFzcyk7XG4gICAgICBpZiAodGVtcGxhdGUpIHtcbiAgICAgICAgaWYgKHRlbXBsYXRlLm5hbWUgIT09IG5hbWUpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFRlbXBsYXRlIG5hbWUgJyR7dGVtcGxhdGUubmFtZX0nIG11c3QgbWF0Y2ggY29tcG9uZW50IG5hbWUgJyR7bmFtZX0nYCk7XG4gICAgICAgIH1cbiAgICAgICAgcmVnaXN0ZXJfdGVtcGxhdGUodGVtcGxhdGUpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBjb21wb25lbnRfY2xhc3MyID0gbmFtZU9yQ2xhc3M7XG4gICAgICBjb25zdCBuYW1lID0gY29tcG9uZW50X2NsYXNzMi5uYW1lO1xuICAgICAgaWYgKCFuYW1lIHx8IG5hbWUgPT09IFwiSnFodG1sX0NvbXBvbmVudFwiKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkNvbXBvbmVudCBjbGFzcyBtdXN0IGhhdmUgYSBuYW1lIHdoZW4gcmVnaXN0ZXJpbmcgd2l0aG91dCBleHBsaWNpdCBuYW1lXCIpO1xuICAgICAgfVxuICAgICAgY29tcG9uZW50X2NsYXNzZXMuc2V0KG5hbWUsIGNvbXBvbmVudF9jbGFzczIpO1xuICAgIH1cbiAgfVxuICBmdW5jdGlvbiBnZXRfY29tcG9uZW50X2NsYXNzKG5hbWUpIHtcbiAgICBjb25zdCBkaXJlY3RDbGFzcyA9IGNvbXBvbmVudF9jbGFzc2VzLmdldChuYW1lKTtcbiAgICBpZiAoZGlyZWN0Q2xhc3MpIHtcbiAgICAgIHJldHVybiBkaXJlY3RDbGFzcztcbiAgICB9XG4gICAgY29uc3QgdGVtcGxhdGUgPSBjb21wb25lbnRfdGVtcGxhdGVzLmdldChuYW1lKTtcbiAgICBpZiAodGVtcGxhdGUgJiYgdGVtcGxhdGUuZXh0ZW5kcykge1xuICAgICAgY29uc3QgdmlzaXRlZCA9IC8qIEBfX1BVUkVfXyAqLyBuZXcgU2V0KFtuYW1lXSk7XG4gICAgICBsZXQgY3VycmVudFRlbXBsYXRlTmFtZSA9IHRlbXBsYXRlLmV4dGVuZHM7XG4gICAgICB3aGlsZSAoY3VycmVudFRlbXBsYXRlTmFtZSAmJiAhdmlzaXRlZC5oYXMoY3VycmVudFRlbXBsYXRlTmFtZSkpIHtcbiAgICAgICAgdmlzaXRlZC5hZGQoY3VycmVudFRlbXBsYXRlTmFtZSk7XG4gICAgICAgIGNvbnN0IHBhcmVudENsYXNzID0gY29tcG9uZW50X2NsYXNzZXMuZ2V0KGN1cnJlbnRUZW1wbGF0ZU5hbWUpO1xuICAgICAgICBpZiAocGFyZW50Q2xhc3MpIHtcbiAgICAgICAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LmVuYWJsZWQpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSBDb21wb25lbnQgJyR7bmFtZX0nIHVzaW5nIGNsYXNzIGZyb20gcGFyZW50ICcke2N1cnJlbnRUZW1wbGF0ZU5hbWV9JyB2aWEgZXh0ZW5kcyBjaGFpbmApO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gcGFyZW50Q2xhc3M7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcGFyZW50VGVtcGxhdGUgPSBjb21wb25lbnRfdGVtcGxhdGVzLmdldChjdXJyZW50VGVtcGxhdGVOYW1lKTtcbiAgICAgICAgaWYgKHBhcmVudFRlbXBsYXRlICYmIHBhcmVudFRlbXBsYXRlLmV4dGVuZHMpIHtcbiAgICAgICAgICBjdXJyZW50VGVtcGxhdGVOYW1lID0gcGFyZW50VGVtcGxhdGUuZXh0ZW5kcztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdm9pZCAwO1xuICB9XG4gIGZ1bmN0aW9uIHJlZ2lzdGVyX3RlbXBsYXRlKHRlbXBsYXRlX2RlZikge1xuICAgIGNvbnN0IG5hbWUgPSB0ZW1wbGF0ZV9kZWYubmFtZTtcbiAgICBpZiAoIW5hbWUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlRlbXBsYXRlIG11c3QgaGF2ZSBhIG5hbWUgcHJvcGVydHlcIik7XG4gICAgfVxuICAgIGlmICghL15bQS1aXS8udGVzdChuYW1lKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBUZW1wbGF0ZSBuYW1lICcke25hbWV9JyBtdXN0IHN0YXJ0IHdpdGggYSBjYXBpdGFsIGxldHRlci4gQ29udmVudGlvbiBpcyBGaXJzdF9MZXR0ZXJfV2l0aF9VbmRlcnNjb3Jlcy5gKTtcbiAgICB9XG4gICAgaWYgKGNvbXBvbmVudF90ZW1wbGF0ZXMuaGFzKG5hbWUpKSB7XG4gICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIFRlbXBsYXRlICcke25hbWV9JyBhbHJlYWR5IHJlZ2lzdGVyZWQsIHNraXBwaW5nIGR1cGxpY2F0ZSByZWdpc3RyYXRpb25gKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgY29tcG9uZW50X3RlbXBsYXRlcy5zZXQobmFtZSwgdGVtcGxhdGVfZGVmKTtcbiAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LmVuYWJsZWQpIHtcbiAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSBTdWNjZXNzZnVsbHkgcmVnaXN0ZXJlZCB0ZW1wbGF0ZTogJHtuYW1lfWApO1xuICAgIH1cbiAgICBjb25zdCBjb21wb25lbnRfY2xhc3MgPSBjb21wb25lbnRfY2xhc3Nlcy5nZXQobmFtZSk7XG4gICAgaWYgKGNvbXBvbmVudF9jbGFzcykge1xuICAgICAgY29tcG9uZW50X2NsYXNzLl9qcWh0bWxfbWV0YWRhdGEgPSB7XG4gICAgICAgIHRhZzogdGVtcGxhdGVfZGVmLnRhZyxcbiAgICAgICAgZGVmYXVsdEF0dHJpYnV0ZXM6IHRlbXBsYXRlX2RlZi5kZWZhdWx0QXR0cmlidXRlcyB8fCB7fVxuICAgICAgfTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cbiAgZnVuY3Rpb24gZ2V0X3RlbXBsYXRlKG5hbWUpIHtcbiAgICBjb25zdCB0ZW1wbGF0ZSA9IGNvbXBvbmVudF90ZW1wbGF0ZXMuZ2V0KG5hbWUpO1xuICAgIGlmICghdGVtcGxhdGUpIHtcbiAgICAgIGNvbnN0IGNvbXBvbmVudF9jbGFzcyA9IGNvbXBvbmVudF9jbGFzc2VzLmdldChuYW1lKTtcbiAgICAgIGlmIChjb21wb25lbnRfY2xhc3MpIHtcbiAgICAgICAgY29uc3QgaW5oZXJpdGVkX3RlbXBsYXRlID0gZ2V0X3RlbXBsYXRlX2J5X2NsYXNzKGNvbXBvbmVudF9jbGFzcyk7XG4gICAgICAgIGlmIChpbmhlcml0ZWRfdGVtcGxhdGUgIT09IERFRkFVTFRfVEVNUExBVEUpIHtcbiAgICAgICAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LmVuYWJsZWQpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSBDb21wb25lbnQgJyR7bmFtZX0nIGhhcyBubyB0ZW1wbGF0ZSwgdXNpbmcgdGVtcGxhdGUgZnJvbSBwcm90b3R5cGUgY2hhaW5gKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIGluaGVyaXRlZF90ZW1wbGF0ZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LmVuYWJsZWQgJiYgIXdhcm5lZF9jb21wb25lbnRzLmhhcyhuYW1lKSkge1xuICAgICAgICAgIHdhcm5lZF9jb21wb25lbnRzLmFkZChuYW1lKTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gTm8gdGVtcGxhdGUgZm91bmQgZm9yIGNsYXNzOiAke25hbWV9LCB1c2luZyBkZWZhdWx0IGRpdiB0ZW1wbGF0ZWApO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpZiAobmFtZSAhPT0gXCJfSnFodG1sX0NvbXBvbmVudFwiICYmIG5hbWUgIT09IFwiUmVkcmF3YWJsZVwiICYmICF3YXJuZWRfY29tcG9uZW50cy5oYXMobmFtZSkpIHtcbiAgICAgICAgICB3YXJuZWRfY29tcG9uZW50cy5hZGQobmFtZSk7XG4gICAgICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBDcmVhdGluZyAke25hbWV9IHdpdGggZGVmYXVsdHMgLSBubyB0ZW1wbGF0ZSBvciBjbGFzcyBkZWZpbmVkYCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8udmVyYm9zZSkge1xuICAgICAgICBjb25zdCByZWdpc3RlcmVkID0gQXJyYXkuZnJvbShjb21wb25lbnRfdGVtcGxhdGVzLmtleXMoKSk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSBMb29raW5nIGZvciB0ZW1wbGF0ZSAnJHtuYW1lfScgaW46IFske3JlZ2lzdGVyZWQuam9pbihcIiwgXCIpfV1gKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBERUZBVUxUX1RFTVBMQVRFO1xuICAgIH1cbiAgICByZXR1cm4gdGVtcGxhdGU7XG4gIH1cbiAgZnVuY3Rpb24gZ2V0X3RlbXBsYXRlX2J5X2NsYXNzKGNvbXBvbmVudF9jbGFzcykge1xuICAgIGlmIChjb21wb25lbnRfY2xhc3MudGVtcGxhdGUpIHtcbiAgICAgIHJldHVybiBjb21wb25lbnRfY2xhc3MudGVtcGxhdGU7XG4gICAgfVxuICAgIGxldCBjdXJyZW50Q2xhc3MgPSBjb21wb25lbnRfY2xhc3M7XG4gICAgd2hpbGUgKGN1cnJlbnRDbGFzcyAmJiBjdXJyZW50Q2xhc3MubmFtZSAhPT0gXCJPYmplY3RcIikge1xuICAgICAgbGV0IG5vcm1hbGl6ZWROYW1lID0gY3VycmVudENsYXNzLm5hbWU7XG4gICAgICBpZiAobm9ybWFsaXplZE5hbWUgPT09IFwiX0pxaHRtbF9Db21wb25lbnRcIiB8fCBub3JtYWxpemVkTmFtZSA9PT0gXCJfQmFzZV9KcWh0bWxfQ29tcG9uZW50XCIpIHtcbiAgICAgICAgbm9ybWFsaXplZE5hbWUgPSBcIkpxaHRtbF9Db21wb25lbnRcIjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHRlbXBsYXRlID0gY29tcG9uZW50X3RlbXBsYXRlcy5nZXQobm9ybWFsaXplZE5hbWUpO1xuICAgICAgaWYgKHRlbXBsYXRlKSB7XG4gICAgICAgIHJldHVybiB0ZW1wbGF0ZTtcbiAgICAgIH1cbiAgICAgIGN1cnJlbnRDbGFzcyA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihjdXJyZW50Q2xhc3MpO1xuICAgIH1cbiAgICByZXR1cm4gREVGQVVMVF9URU1QTEFURTtcbiAgfVxuICBmdW5jdGlvbiBjcmVhdGVfY29tcG9uZW50KG5hbWUsIGVsZW1lbnQsIGFyZ3MgPSB7fSkge1xuICAgIGNvbnN0IENvbXBvbmVudENsYXNzID0gZ2V0X2NvbXBvbmVudF9jbGFzcyhuYW1lKSB8fCBKcWh0bWxfQ29tcG9uZW50O1xuICAgIHJldHVybiBuZXcgQ29tcG9uZW50Q2xhc3MoZWxlbWVudCwgYXJncyk7XG4gIH1cbiAgZnVuY3Rpb24gaGFzX2NvbXBvbmVudChuYW1lKSB7XG4gICAgcmV0dXJuIGNvbXBvbmVudF9jbGFzc2VzLmhhcyhuYW1lKTtcbiAgfVxuICBmdW5jdGlvbiBnZXRfY29tcG9uZW50X25hbWVzKCkge1xuICAgIHJldHVybiBBcnJheS5mcm9tKGNvbXBvbmVudF9jbGFzc2VzLmtleXMoKSk7XG4gIH1cbiAgZnVuY3Rpb24gZ2V0X3JlZ2lzdGVyZWRfdGVtcGxhdGVzKCkge1xuICAgIHJldHVybiBBcnJheS5mcm9tKGNvbXBvbmVudF90ZW1wbGF0ZXMua2V5cygpKTtcbiAgfVxuICBmdW5jdGlvbiBsaXN0X2NvbXBvbmVudHMoKSB7XG4gICAgY29uc3QgcmVzdWx0ID0ge307XG4gICAgZm9yIChjb25zdCBuYW1lIG9mIGNvbXBvbmVudF9jbGFzc2VzLmtleXMoKSkge1xuICAgICAgcmVzdWx0W25hbWVdID0ge1xuICAgICAgICBoYXNfY2xhc3M6IHRydWUsXG4gICAgICAgIGhhc190ZW1wbGF0ZTogY29tcG9uZW50X3RlbXBsYXRlcy5oYXMobmFtZSlcbiAgICAgIH07XG4gICAgfVxuICAgIGZvciAoY29uc3QgbmFtZSBvZiBjb21wb25lbnRfdGVtcGxhdGVzLmtleXMoKSkge1xuICAgICAgaWYgKCFyZXN1bHRbbmFtZV0pIHtcbiAgICAgICAgcmVzdWx0W25hbWVdID0ge1xuICAgICAgICAgIGhhc19jbGFzczogZmFsc2UsXG4gICAgICAgICAgaGFzX3RlbXBsYXRlOiB0cnVlXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgdmFyIF9jaWRfaW5jcmVtZW50ID0gXCJhYVwiO1xuICBmdW5jdGlvbiB1aWQoKSB7XG4gICAgY29uc3QgY3VycmVudCA9IF9jaWRfaW5jcmVtZW50O1xuICAgIGNvbnN0IGNoYXJzID0gX2NpZF9pbmNyZW1lbnQuc3BsaXQoXCJcIik7XG4gICAgbGV0IGNhcnJ5ID0gdHJ1ZTtcbiAgICBmb3IgKGxldCBpID0gY2hhcnMubGVuZ3RoIC0gMTsgaSA+PSAwICYmIGNhcnJ5OyBpLS0pIHtcbiAgICAgIGNvbnN0IGNoYXIgPSBjaGFyc1tpXTtcbiAgICAgIGlmIChjaGFyID49IFwiYVwiICYmIGNoYXIgPCBcInpcIikge1xuICAgICAgICBjaGFyc1tpXSA9IFN0cmluZy5mcm9tQ2hhckNvZGUoY2hhci5jaGFyQ29kZUF0KDApICsgMSk7XG4gICAgICAgIGNhcnJ5ID0gZmFsc2U7XG4gICAgICB9IGVsc2UgaWYgKGNoYXIgPT09IFwielwiKSB7XG4gICAgICAgIGNoYXJzW2ldID0gXCIwXCI7XG4gICAgICAgIGNhcnJ5ID0gZmFsc2U7XG4gICAgICB9IGVsc2UgaWYgKGNoYXIgPj0gXCIwXCIgJiYgY2hhciA8IFwiOVwiKSB7XG4gICAgICAgIGNoYXJzW2ldID0gU3RyaW5nLmZyb21DaGFyQ29kZShjaGFyLmNoYXJDb2RlQXQoMCkgKyAxKTtcbiAgICAgICAgY2FycnkgPSBmYWxzZTtcbiAgICAgIH0gZWxzZSBpZiAoY2hhciA9PT0gXCI5XCIpIHtcbiAgICAgICAgY2hhcnNbaV0gPSBcImFcIjtcbiAgICAgICAgY2FycnkgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoY2FycnkpIHtcbiAgICAgIGNoYXJzLnVuc2hpZnQoXCJhXCIpO1xuICAgIH1cbiAgICBpZiAoY2hhcnNbMF0gPj0gXCIwXCIgJiYgY2hhcnNbMF0gPD0gXCI5XCIpIHtcbiAgICAgIGNoYXJzWzBdID0gXCJhXCI7XG4gICAgICBjaGFycy51bnNoaWZ0KFwiYVwiKTtcbiAgICB9XG4gICAgX2NpZF9pbmNyZW1lbnQgPSBjaGFycy5qb2luKFwiXCIpO1xuICAgIHJldHVybiBjdXJyZW50O1xuICB9XG4gIGZ1bmN0aW9uIHByb2Nlc3NfaW5zdHJ1Y3Rpb25zKGluc3RydWN0aW9ucywgdGFyZ2V0LCBjb250ZXh0LCBzbG90cykge1xuICAgIGNvbnN0IGh0bWwgPSBbXTtcbiAgICBjb25zdCB0YWdFbGVtZW50cyA9IHt9O1xuICAgIGNvbnN0IGNvbXBvbmVudHMgPSB7fTtcbiAgICBmb3IgKGNvbnN0IGluc3RydWN0aW9uIG9mIGluc3RydWN0aW9ucykge1xuICAgICAgcHJvY2Vzc19pbnN0cnVjdGlvbl90b19odG1sKGluc3RydWN0aW9uLCBodG1sLCB0YWdFbGVtZW50cywgY29tcG9uZW50cywgY29udGV4dCwgc2xvdHMpO1xuICAgIH1cbiAgICB0YXJnZXRbMF0uaW5uZXJIVE1MID0gaHRtbC5qb2luKFwiXCIpO1xuICAgIGZvciAoY29uc3QgW3RpZCwgdGFnRGF0YV0gb2YgT2JqZWN0LmVudHJpZXModGFnRWxlbWVudHMpKSB7XG4gICAgICBjb25zdCBlbCA9IHRhcmdldFswXS5xdWVyeVNlbGVjdG9yKGBbZGF0YS10aWQ9XCIke3RpZH1cIl1gKTtcbiAgICAgIGlmIChlbCkge1xuICAgICAgICBjb25zdCBlbGVtZW50ID0gJChlbCk7XG4gICAgICAgIGVsLnJlbW92ZUF0dHJpYnV0ZShcImRhdGEtdGlkXCIpO1xuICAgICAgICBhcHBseV9hdHRyaWJ1dGVzKGVsZW1lbnQsIHRhZ0RhdGEuYXR0cnMsIGNvbnRleHQpO1xuICAgICAgfVxuICAgIH1cbiAgICBmb3IgKGNvbnN0IFtjaWQsIGNvbXBEYXRhXSBvZiBPYmplY3QuZW50cmllcyhjb21wb25lbnRzKSkge1xuICAgICAgY29uc3QgZWwgPSB0YXJnZXRbMF0ucXVlcnlTZWxlY3RvcihgW2RhdGEtY2lkPVwiJHtjaWR9XCJdYCk7XG4gICAgICBpZiAoZWwpIHtcbiAgICAgICAgY29uc3QgZWxlbWVudCA9ICQoZWwpO1xuICAgICAgICBlbC5yZW1vdmVBdHRyaWJ1dGUoXCJkYXRhLWNpZFwiKTtcbiAgICAgICAgaW5pdGlhbGl6ZV9jb21wb25lbnQoZWxlbWVudCwgY29tcERhdGEpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBmdW5jdGlvbiBwcm9jZXNzX2luc3RydWN0aW9uX3RvX2h0bWwoaW5zdHJ1Y3Rpb24sIGh0bWwsIHRhZ0VsZW1lbnRzLCBjb21wb25lbnRzLCBjb250ZXh0LCBzbG90cykge1xuICAgIGlmICh0eXBlb2YgaW5zdHJ1Y3Rpb24gPT09IFwic3RyaW5nXCIpIHtcbiAgICAgIGh0bWwucHVzaChpbnN0cnVjdGlvbik7XG4gICAgfSBlbHNlIGlmIChcInRhZ1wiIGluIGluc3RydWN0aW9uKSB7XG4gICAgICBwcm9jZXNzX3RhZ190b19odG1sKGluc3RydWN0aW9uLCBodG1sLCB0YWdFbGVtZW50cywgY29tcG9uZW50cywgY29udGV4dCk7XG4gICAgfSBlbHNlIGlmIChcImNvbXBcIiBpbiBpbnN0cnVjdGlvbikge1xuICAgICAgcHJvY2Vzc19jb21wb25lbnRfdG9faHRtbChpbnN0cnVjdGlvbiwgaHRtbCwgY29tcG9uZW50cywgY29udGV4dCk7XG4gICAgfSBlbHNlIGlmIChcInNsb3RcIiBpbiBpbnN0cnVjdGlvbikge1xuICAgICAgcHJvY2Vzc19zbG90X3RvX2h0bWwoaW5zdHJ1Y3Rpb24sIGh0bWwsIHRhZ0VsZW1lbnRzLCBjb21wb25lbnRzLCBjb250ZXh0LCBzbG90cyk7XG4gICAgfSBlbHNlIGlmIChcInJhd3RhZ1wiIGluIGluc3RydWN0aW9uKSB7XG4gICAgICBwcm9jZXNzX3Jhd3RhZ190b19odG1sKGluc3RydWN0aW9uLCBodG1sKTtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gcHJvY2Vzc190YWdfdG9faHRtbChpbnN0cnVjdGlvbiwgaHRtbCwgdGFnRWxlbWVudHMsIGNvbXBvbmVudHMsIGNvbnRleHQpIHtcbiAgICBjb25zdCBbdGFnTmFtZSwgYXR0cnMsIHNlbGZDbG9zaW5nXSA9IGluc3RydWN0aW9uLnRhZztcbiAgICBjb25zdCBuZWVkc1RyYWNraW5nID0gT2JqZWN0LmtleXMoYXR0cnMpLnNvbWUoKGtleSkgPT4ga2V5ID09PSBcIiRpZFwiIHx8IGtleS5zdGFydHNXaXRoKFwiJFwiKSB8fCBrZXkuc3RhcnRzV2l0aChcIkBcIikgfHwga2V5LnN0YXJ0c1dpdGgoXCJvblwiKSB8fCBrZXkuc3RhcnRzV2l0aChcImRhdGEtYmluZC1cIikgfHwga2V5LnN0YXJ0c1dpdGgoXCJkYXRhLW9uLVwiKSk7XG4gICAgaHRtbC5wdXNoKGA8JHt0YWdOYW1lfWApO1xuICAgIGxldCB0aWQgPSBudWxsO1xuICAgIGlmIChuZWVkc1RyYWNraW5nKSB7XG4gICAgICB0aWQgPSB1aWQoKTtcbiAgICAgIGh0bWwucHVzaChgIGRhdGEtdGlkPVwiJHt0aWR9XCJgKTtcbiAgICAgIHRhZ0VsZW1lbnRzW3RpZF0gPSB7IGF0dHJzLCBjb250ZXh0IH07XG4gICAgfVxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGF0dHJzKSkge1xuICAgICAgaWYgKCFrZXkuc3RhcnRzV2l0aChcIiRcIikgJiYgIWtleS5zdGFydHNXaXRoKFwib25cIikgJiYgIWtleS5zdGFydHNXaXRoKFwiQFwiKSAmJiAha2V5LnN0YXJ0c1dpdGgoXCJkYXRhLWJpbmQtXCIpICYmICFrZXkuc3RhcnRzV2l0aChcImRhdGEtb24tXCIpICYmICh0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgfHwgdHlwZW9mIHZhbHVlID09PSBcIm51bWJlclwiKSkge1xuICAgICAgICBpZiAoa2V5ID09PSBcImlkXCIgJiYgdGlkKSB7XG4gICAgICAgICAgaHRtbC5wdXNoKGAgaWQ9XCIke3ZhbHVlfToke2NvbnRleHQuX2NpZH1cImApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGh0bWwucHVzaChgICR7a2V5fT1cIiR7dmFsdWV9XCJgKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBpZiAoc2VsZkNsb3NpbmcpIHtcbiAgICAgIGh0bWwucHVzaChcIiAvPlwiKTtcbiAgICB9IGVsc2Uge1xuICAgICAgaHRtbC5wdXNoKFwiPlwiKTtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gcHJvY2Vzc19jb21wb25lbnRfdG9faHRtbChpbnN0cnVjdGlvbiwgaHRtbCwgY29tcG9uZW50cywgY29udGV4dCkge1xuICAgIGNvbnN0IFtjb21wb25lbnROYW1lLCBwcm9wcywgY29udGVudEZuXSA9IGluc3RydWN0aW9uLmNvbXA7XG4gICAgY29uc3QgY2lkID0gdWlkKCk7XG4gICAgZ2V0X2NvbXBvbmVudF9jbGFzcyhjb21wb25lbnROYW1lKSB8fCBKcWh0bWxfQ29tcG9uZW50O1xuICAgIGNvbnN0IHRlbXBsYXRlID0gZ2V0X3RlbXBsYXRlKGNvbXBvbmVudE5hbWUpO1xuICAgIGNvbnN0IHRhZ05hbWUgPSBwcm9wcy5fdGFnIHx8IHRlbXBsYXRlLnRhZyB8fCBcImRpdlwiO1xuICAgIGh0bWwucHVzaChgPCR7dGFnTmFtZX0gZGF0YS1jaWQ9XCIke2NpZH1cImApO1xuICAgIGlmIChwcm9wc1tcImRhdGEtaWRcIl0pIHtcbiAgICAgIGNvbnN0IGJhc2VJZCA9IHByb3BzW1wiZGF0YS1pZFwiXTtcbiAgICAgIGh0bWwucHVzaChgIGlkPVwiJHtwcm9wc1tcImlkXCJdfVwiIGRhdGEtaWQ9XCIke2Jhc2VJZH1cImApO1xuICAgIH0gZWxzZSBpZiAocHJvcHNbXCJpZFwiXSkge1xuICAgICAgaHRtbC5wdXNoKGAgaWQ9XCIke3Byb3BzW1wiaWRcIl19XCJgKTtcbiAgICB9XG4gICAgaHRtbC5wdXNoKFwiPjwvXCIgKyB0YWdOYW1lICsgXCI+XCIpO1xuICAgIGNvbXBvbmVudHNbY2lkXSA9IHtcbiAgICAgIG5hbWU6IGNvbXBvbmVudE5hbWUsXG4gICAgICBwcm9wcyxcbiAgICAgIGNvbnRlbnRGbixcbiAgICAgIGNvbnRleHRcbiAgICB9O1xuICB9XG4gIGZ1bmN0aW9uIHByb2Nlc3Nfc2xvdF90b19odG1sKGluc3RydWN0aW9uLCBodG1sLCB0YWdFbGVtZW50cywgY29tcG9uZW50cywgY29udGV4dCwgcGFyZW50U2xvdHMpIHtcbiAgICBjb25zdCBbc2xvdE5hbWVdID0gaW5zdHJ1Y3Rpb24uc2xvdDtcbiAgICBpZiAocGFyZW50U2xvdHMgJiYgc2xvdE5hbWUgaW4gcGFyZW50U2xvdHMpIHtcbiAgICAgIGNvbnN0IHBhcmVudFNsb3QgPSBwYXJlbnRTbG90c1tzbG90TmFtZV07XG4gICAgICBjb25zdCBbLCBzbG90UHJvcHMsIGNvbnRlbnRGbl0gPSBwYXJlbnRTbG90LnNsb3Q7XG4gICAgICBjb25zdCBbY29udGVudF0gPSBjb250ZW50Rm4uY2FsbChjb250ZXh0LCBzbG90UHJvcHMpO1xuICAgICAgZm9yIChjb25zdCBpdGVtIG9mIGNvbnRlbnQpIHtcbiAgICAgICAgcHJvY2Vzc19pbnN0cnVjdGlvbl90b19odG1sKGl0ZW0sIGh0bWwsIHRhZ0VsZW1lbnRzLCBjb21wb25lbnRzLCBjb250ZXh0KTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHNsb3ROYW1lID09PSBcImRlZmF1bHRcIiAmJiBpbnN0cnVjdGlvbi5zbG90WzJdKSB7XG4gICAgICBjb25zdCBbLCAsIGRlZmF1bHRGbl0gPSBpbnN0cnVjdGlvbi5zbG90O1xuICAgICAgY29uc3QgW2NvbnRlbnRdID0gZGVmYXVsdEZuLmNhbGwoY29udGV4dCwge30pO1xuICAgICAgZm9yIChjb25zdCBpdGVtIG9mIGNvbnRlbnQpIHtcbiAgICAgICAgcHJvY2Vzc19pbnN0cnVjdGlvbl90b19odG1sKGl0ZW0sIGh0bWwsIHRhZ0VsZW1lbnRzLCBjb21wb25lbnRzLCBjb250ZXh0KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gcHJvY2Vzc19yYXd0YWdfdG9faHRtbChpbnN0cnVjdGlvbiwgaHRtbCkge1xuICAgIGNvbnN0IFt0YWdOYW1lLCBhdHRycywgcmF3Q29udGVudF0gPSBpbnN0cnVjdGlvbi5yYXd0YWc7XG4gICAgaHRtbC5wdXNoKGA8JHt0YWdOYW1lfWApO1xuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGF0dHJzKSkge1xuICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiB8fCB0eXBlb2YgdmFsdWUgPT09IFwibnVtYmVyXCIpIHtcbiAgICAgICAgY29uc3QgZXNjYXBlZF92YWx1ZSA9IFN0cmluZyh2YWx1ZSkucmVwbGFjZSgvXCIvZywgXCImcXVvdDtcIik7XG4gICAgICAgIGh0bWwucHVzaChgICR7a2V5fT1cIiR7ZXNjYXBlZF92YWx1ZX1cImApO1xuICAgICAgfSBlbHNlIGlmICh0eXBlb2YgdmFsdWUgPT09IFwiYm9vbGVhblwiICYmIHZhbHVlKSB7XG4gICAgICAgIGh0bWwucHVzaChgICR7a2V5fWApO1xuICAgICAgfVxuICAgIH1cbiAgICBodG1sLnB1c2goXCI+XCIpO1xuICAgIGNvbnN0IGVzY2FwZWRfY29udGVudCA9IHJhd0NvbnRlbnQucmVwbGFjZSgvJi9nLCBcIiZhbXA7XCIpLnJlcGxhY2UoLzwvZywgXCImbHQ7XCIpLnJlcGxhY2UoLz4vZywgXCImZ3Q7XCIpO1xuICAgIGh0bWwucHVzaChlc2NhcGVkX2NvbnRlbnQpO1xuICAgIGh0bWwucHVzaChgPC8ke3RhZ05hbWV9PmApO1xuICB9XG4gIGZ1bmN0aW9uIGFwcGx5X2F0dHJpYnV0ZXMoZWxlbWVudCwgYXR0cnMsIGNvbnRleHQpIHtcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhhdHRycykpIHtcbiAgICAgIGlmIChrZXkgPT09IFwiJGlkXCIgfHwga2V5ID09PSBcImlkXCIpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9IGVsc2UgaWYgKGtleS5zdGFydHNXaXRoKFwiJFwiKSkge1xuICAgICAgICBjb25zdCBkYXRhS2V5ID0ga2V5LnN1YnN0cmluZygxKTtcbiAgICAgICAgZWxlbWVudC5kYXRhKGRhdGFLZXksIHZhbHVlKTtcbiAgICAgICAgY29udGV4dC5hcmdzW2RhdGFLZXldID0gdmFsdWU7XG4gICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT0gXCJzdHJpbmdcIiB8fCB0eXBlb2YgdmFsdWUgPT0gXCJudW1iZXJcIikge1xuICAgICAgICAgIGNvbnN0IGF0dHJWYWx1ZSA9IHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiA/IHZhbHVlLnRyaW0oKSA6IHZhbHVlO1xuICAgICAgICAgIGVsZW1lbnQuYXR0cihgZGF0YS0ke2RhdGFLZXl9YCwgYXR0clZhbHVlKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChrZXkuc3RhcnRzV2l0aChcImRhdGEtb24tXCIpKSB7XG4gICAgICAgIGNvbnN0IGV2ZW50TmFtZSA9IGtleS5zdWJzdHJpbmcoOCk7XG4gICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgIGVsZW1lbnQub24oZXZlbnROYW1lLCBmdW5jdGlvbihlKSB7XG4gICAgICAgICAgICB2YWx1ZS5iaW5kKGNvbnRleHQpKGUsIGVsZW1lbnQpO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbnNvbGUud2FybihcIihKUUhUTUwpIFRyaWVkIHRvIGFzc2lnbiBhIG5vbiBmdW5jdGlvbiB0byBvbiBldmVudCBoYW5kbGVyIFwiICsga2V5KTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChrZXkuc3RhcnRzV2l0aChcIm9uXCIpKSB7XG4gICAgICAgIGNvbnN0IGV2ZW50TmFtZSA9IGtleS5zdWJzdHJpbmcoMik7XG4gICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgIGVsZW1lbnQub24oZXZlbnROYW1lLCBmdW5jdGlvbihlKSB7XG4gICAgICAgICAgICB2YWx1ZS5iaW5kKGNvbnRleHQpKGUsIGVsZW1lbnQpO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbnNvbGUud2FybihcIihKUUhUTUwpIFRyaWVkIHRvIGFzc2lnbiBhIG5vbiBmdW5jdGlvbiB0byBvbiBldmVudCBoYW5kbGVyIFwiICsga2V5KTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChrZXkuc3RhcnRzV2l0aChcImRhdGEtXCIpKSB7XG4gICAgICAgIGNvbnN0IGF0dHJWYWx1ZSA9IHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiA/IHZhbHVlLnRyaW0oKSA6IHZhbHVlO1xuICAgICAgICBlbGVtZW50LmF0dHIoa2V5LCBhdHRyVmFsdWUpO1xuICAgICAgICBjb25zdCBkYXRhS2V5ID0ga2V5LnN1YnN0cmluZyg1KTtcbiAgICAgICAgZWxlbWVudC5kYXRhKGRhdGFLZXksIHZhbHVlKTtcbiAgICAgICAgY29udGV4dC5hcmdzW2RhdGFLZXldID0gdmFsdWU7XG4gICAgICB9IGVsc2UgaWYgKGtleSA9PT0gXCJjbGFzc1wiKSB7XG4gICAgICAgIGNvbnN0IGV4aXN0aW5nQ2xhc3NlcyA9IGVsZW1lbnQuYXR0cihcImNsYXNzXCIpO1xuICAgICAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LmVuYWJsZWQpIHtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW0luc3RydWN0aW9uUHJvY2Vzc29yXSBNZXJnaW5nIGNsYXNzIGF0dHJpYnV0ZTpgLCB7XG4gICAgICAgICAgICBleGlzdGluZzogZXhpc3RpbmdDbGFzc2VzLFxuICAgICAgICAgICAgbmV3OiB2YWx1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIGlmICghZXhpc3RpbmdDbGFzc2VzKSB7XG4gICAgICAgICAgY29uc3QgYXR0clZhbHVlID0gdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiID8gdmFsdWUudHJpbSgpIDogdmFsdWU7XG4gICAgICAgICAgZWxlbWVudC5hdHRyKFwiY2xhc3NcIiwgYXR0clZhbHVlKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCBleGlzdGluZyA9IGV4aXN0aW5nQ2xhc3Nlcy5zcGxpdCgvXFxzKy8pLmZpbHRlcigoYykgPT4gYyk7XG4gICAgICAgICAgY29uc3QgbmV3Q2xhc3NlcyA9IFN0cmluZyh2YWx1ZSkuc3BsaXQoL1xccysvKS5maWx0ZXIoKGMpID0+IGMpO1xuICAgICAgICAgIGZvciAoY29uc3QgbmV3Q2xhc3Mgb2YgbmV3Q2xhc3Nlcykge1xuICAgICAgICAgICAgaWYgKCFleGlzdGluZy5pbmNsdWRlcyhuZXdDbGFzcykpIHtcbiAgICAgICAgICAgICAgZXhpc3RpbmcucHVzaChuZXdDbGFzcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGVsZW1lbnQuYXR0cihcImNsYXNzXCIsIGV4aXN0aW5nLmpvaW4oXCIgXCIpKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LmVuYWJsZWQpIHtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW0luc3RydWN0aW9uUHJvY2Vzc29yXSBDbGFzcyBhZnRlciBtZXJnZTpgLCBlbGVtZW50LmF0dHIoXCJjbGFzc1wiKSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoa2V5ID09PSBcInN0eWxlXCIpIHtcbiAgICAgICAgY29uc3QgZXhpc3RpbmdTdHlsZSA9IGVsZW1lbnQuYXR0cihcInN0eWxlXCIpO1xuICAgICAgICBpZiAoIWV4aXN0aW5nU3R5bGUpIHtcbiAgICAgICAgICBjb25zdCBhdHRyVmFsdWUgPSB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgPyB2YWx1ZS50cmltKCkgOiB2YWx1ZTtcbiAgICAgICAgICBlbGVtZW50LmF0dHIoXCJzdHlsZVwiLCBhdHRyVmFsdWUpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbnN0IHN0eWxlTWFwID0ge307XG4gICAgICAgICAgZXhpc3RpbmdTdHlsZS5zcGxpdChcIjtcIikuZm9yRWFjaCgocnVsZSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgW3Byb3AsIHZhbF0gPSBydWxlLnNwbGl0KFwiOlwiKS5tYXAoKHMpID0+IHMudHJpbSgpKTtcbiAgICAgICAgICAgIGlmIChwcm9wICYmIHZhbCkge1xuICAgICAgICAgICAgICBzdHlsZU1hcFtwcm9wXSA9IHZhbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgICBTdHJpbmcodmFsdWUpLnNwbGl0KFwiO1wiKS5mb3JFYWNoKChydWxlKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBbcHJvcCwgdmFsXSA9IHJ1bGUuc3BsaXQoXCI6XCIpLm1hcCgocykgPT4gcy50cmltKCkpO1xuICAgICAgICAgICAgaWYgKHByb3AgJiYgdmFsKSB7XG4gICAgICAgICAgICAgIHN0eWxlTWFwW3Byb3BdID0gdmFsO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGNvbnN0IG1lcmdlZFN0eWxlID0gT2JqZWN0LmVudHJpZXMoc3R5bGVNYXApLm1hcCgoW3Byb3AsIHZhbF0pID0+IGAke3Byb3B9OiAke3ZhbH1gKS5qb2luKFwiOyBcIik7XG4gICAgICAgICAgZWxlbWVudC5hdHRyKFwic3R5bGVcIiwgbWVyZ2VkU3R5bGUpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiIHx8IHR5cGVvZiB2YWx1ZSA9PT0gXCJudW1iZXJcIiB8fCB0eXBlb2YgdmFsdWUgPT09IFwiYm9vbGVhblwiKSB7XG4gICAgICAgICAgY29uc3QgYXR0clZhbHVlID0gdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiID8gdmFsdWUudHJpbSgpIDogU3RyaW5nKHZhbHVlKTtcbiAgICAgICAgICBlbGVtZW50LmF0dHIoa2V5LCBhdHRyVmFsdWUpO1xuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJvYmplY3RcIikge1xuICAgICAgICAgIGNvbnNvbGUud2FybihgKEpRSFRNTCkgVW5leHBlY3RlZCB2YWx1ZSBmb3IgJyR7a2V5fScgb25gLCBlbGVtZW50KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuICBhc3luYyBmdW5jdGlvbiBpbml0aWFsaXplX2NvbXBvbmVudChlbGVtZW50LCBjb21wRGF0YSkge1xuICAgIGNvbnN0IHsgbmFtZSwgcHJvcHMsIGNvbnRlbnRGbiwgY29udGV4dCB9ID0gY29tcERhdGE7XG4gICAgY29uc3QgQ29tcG9uZW50Q2xhc3MgPSBnZXRfY29tcG9uZW50X2NsYXNzKG5hbWUpIHx8IEpxaHRtbF9Db21wb25lbnQ7XG4gICAgY29uc3QgaW52b2NhdGlvbkF0dHJzID0ge307XG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocHJvcHMpKSB7XG4gICAgICBpZiAoIWtleS5zdGFydHNXaXRoKFwiX1wiKSkge1xuICAgICAgICBpbnZvY2F0aW9uQXR0cnNba2V5XSA9IHZhbHVlO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LmVuYWJsZWQpIHtcbiAgICAgIGNvbnNvbGUubG9nKGBbSW5zdHJ1Y3Rpb25Qcm9jZXNzb3JdIEFwcGx5aW5nIGludm9jYXRpb24gYXR0cmlidXRlcyBmb3IgJHtuYW1lfTpgLCBpbnZvY2F0aW9uQXR0cnMpO1xuICAgIH1cbiAgICBhcHBseV9hdHRyaWJ1dGVzKGVsZW1lbnQsIGludm9jYXRpb25BdHRycywgY29udGV4dCk7XG4gICAgY29uc3Qgb3B0aW9ucyA9IHt9O1xuICAgIGlmIChjb250ZW50Rm4pIHtcbiAgICAgIG9wdGlvbnMuX2lubmVyaHRtbF9mdW5jdGlvbiA9IGNvbnRlbnRGbjtcbiAgICB9XG4gICAgaWYgKENvbXBvbmVudENsYXNzLm5hbWUgIT09IG5hbWUpIHtcbiAgICAgIG9wdGlvbnMuX2NvbXBvbmVudF9uYW1lID0gbmFtZTtcbiAgICB9XG4gICAgY29uc3QgaW5zdGFuY2UgPSBuZXcgQ29tcG9uZW50Q2xhc3MoZWxlbWVudCwgb3B0aW9ucyk7XG4gICAgaW5zdGFuY2UuX2luc3RhbnRpYXRvciA9IGNvbnRleHQ7XG4gICAgYXdhaXQgaW5zdGFuY2UuYm9vdCgpO1xuICB9XG4gIGZ1bmN0aW9uIGV4dHJhY3Rfc2xvdHMoaW5zdHJ1Y3Rpb25zKSB7XG4gICAgY29uc3Qgc2xvdHMgPSB7fTtcbiAgICBmb3IgKGNvbnN0IGluc3RydWN0aW9uIG9mIGluc3RydWN0aW9ucykge1xuICAgICAgaWYgKHR5cGVvZiBpbnN0cnVjdGlvbiA9PT0gXCJvYmplY3RcIiAmJiBcInNsb3RcIiBpbiBpbnN0cnVjdGlvbikge1xuICAgICAgICBjb25zdCBbbmFtZV0gPSBpbnN0cnVjdGlvbi5zbG90O1xuICAgICAgICBzbG90c1tuYW1lXSA9IGluc3RydWN0aW9uO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gc2xvdHM7XG4gIH1cbiAgdmFyIHBlcmZvcm1hbmNlTWV0cmljcyA9IC8qIEBfX1BVUkVfXyAqLyBuZXcgTWFwKCk7XG4gIGZ1bmN0aW9uIGRldldhcm4obWVzc2FnZSkge1xuICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmIHdpbmRvdy5KUUhUTUxfU1VQUFJFU1NfV0FSTklOR1MpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHR5cGVvZiBwcm9jZXNzICE9PSBcInVuZGVmaW5lZFwiICYmIHByb2Nlc3MuZW52ICYmIGZhbHNlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnNvbGUud2FybihgW0pRSFRNTCBEZXYgV2FybmluZ10gJHttZXNzYWdlfWApO1xuICB9XG4gIGZ1bmN0aW9uIGdldEpxaHRtbCQxKCkge1xuICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmIHdpbmRvdy5qcWh0bWwpIHtcbiAgICAgIHJldHVybiB3aW5kb3cuanFodG1sO1xuICAgIH1cbiAgICBpZiAodHlwZW9mIGdsb2JhbFRoaXMgIT09IFwidW5kZWZpbmVkXCIgJiYgZ2xvYmFsVGhpcy5qcWh0bWwpIHtcbiAgICAgIHJldHVybiBnbG9iYWxUaGlzLmpxaHRtbDtcbiAgICB9XG4gICAgdGhyb3cgbmV3IEVycm9yKFwiRkFUQUw6IHdpbmRvdy5qcWh0bWwgaXMgbm90IGRlZmluZWQuIFRoZSBKUUhUTUwgcnVudGltZSBtdXN0IGJlIGxvYWRlZCBiZWZvcmUgdXNpbmcgZGVidWcgZmVhdHVyZXMuIEltcG9ydCBhbmQgaW5pdGlhbGl6ZSBAanFodG1sL2NvcmUgYmVmb3JlIGF0dGVtcHRpbmcgdG8gdXNlIGRlYnVnIGZ1bmN0aW9uYWxpdHkuXCIpO1xuICB9XG4gIGZ1bmN0aW9uIGZsYXNoQ29tcG9uZW50KGNvbXBvbmVudCwgZXZlbnRUeXBlKSB7XG4gICAgY29uc3QganFodG1sMiA9IGdldEpxaHRtbCQxKCk7XG4gICAgaWYgKCFqcWh0bWwyPy5kZWJ1Zz8uZmxhc2hDb21wb25lbnRzKVxuICAgICAgcmV0dXJuO1xuICAgIGNvbnN0IGR1cmF0aW9uID0ganFodG1sMi5kZWJ1Zy5mbGFzaER1cmF0aW9uIHx8IDUwMDtcbiAgICBjb25zdCBjb2xvcnMgPSBqcWh0bWwyLmRlYnVnLmZsYXNoQ29sb3JzIHx8IHt9O1xuICAgIGNvbnN0IGNvbG9yID0gY29sb3JzW2V2ZW50VHlwZV0gfHwgKGV2ZW50VHlwZSA9PT0gXCJjcmVhdGVcIiA/IFwiIzM0OThkYlwiIDogZXZlbnRUeXBlID09PSBcInJlbmRlclwiID8gXCIjMjdhZTYwXCIgOiBcIiM5YjU5YjZcIik7XG4gICAgY29uc3Qgb3JpZ2luYWxCb3JkZXIgPSBjb21wb25lbnQuJC5jc3MoXCJib3JkZXJcIik7XG4gICAgY29tcG9uZW50LiQuY3NzKHtcbiAgICAgIFwiYm9yZGVyXCI6IGAycHggc29saWQgJHtjb2xvcn1gLFxuICAgICAgXCJ0cmFuc2l0aW9uXCI6IGBib3JkZXIgJHtkdXJhdGlvbn1tcyBlYXNlLW91dGBcbiAgICB9KTtcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGNvbXBvbmVudC4kLmNzcyhcImJvcmRlclwiLCBvcmlnaW5hbEJvcmRlciB8fCBcIlwiKTtcbiAgICB9LCBkdXJhdGlvbik7XG4gIH1cbiAgZnVuY3Rpb24gbG9nTGlmZWN5Y2xlKGNvbXBvbmVudCwgcGhhc2UsIHN0YXR1cykge1xuICAgIGNvbnN0IGpxaHRtbDIgPSBnZXRKcWh0bWwkMSgpO1xuICAgIGlmICghanFodG1sMj8uZGVidWcpXG4gICAgICByZXR1cm47XG4gICAgY29uc3Qgc2hvdWxkTG9nID0ganFodG1sMi5kZWJ1Zy5sb2dGdWxsTGlmZWN5Y2xlIHx8IGpxaHRtbDIuZGVidWcubG9nQ3JlYXRpb25SZWFkeSAmJiAocGhhc2UgPT09IFwiY3JlYXRlXCIgfHwgcGhhc2UgPT09IFwicmVhZHlcIik7XG4gICAgaWYgKCFzaG91bGRMb2cpXG4gICAgICByZXR1cm47XG4gICAgY29uc3QgY29tcG9uZW50TmFtZSA9IGNvbXBvbmVudC5jb25zdHJ1Y3Rvci5uYW1lO1xuICAgIGNvbnN0IHRpbWVzdGFtcCA9ICgvKiBAX19QVVJFX18gKi8gbmV3IERhdGUoKSkudG9JU09TdHJpbmcoKTtcbiAgICBjb25zdCBwcmVmaXggPSBgW0pRSFRNTCAke3RpbWVzdGFtcH1dYDtcbiAgICBpZiAoc3RhdHVzID09PSBcInN0YXJ0XCIpIHtcbiAgICAgIGNvbnNvbGUubG9nKGAke3ByZWZpeH0gJHtjb21wb25lbnROYW1lfSMke2NvbXBvbmVudC5fY2lkfSBcXHUyMTkyICR7cGhhc2V9IHN0YXJ0aW5nLi4uYCk7XG4gICAgICBpZiAoanFodG1sMi5kZWJ1Zy5wcm9maWxlUGVyZm9ybWFuY2UpIHtcbiAgICAgICAgcGVyZm9ybWFuY2VNZXRyaWNzLnNldChgJHtjb21wb25lbnQuX2NpZH1fJHtwaGFzZX1gLCBEYXRlLm5vdygpKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgbGV0IG1lc3NhZ2UgPSBgJHtwcmVmaXh9ICR7Y29tcG9uZW50TmFtZX0jJHtjb21wb25lbnQuX2NpZH0gXFx1MjcxMyAke3BoYXNlfSBjb21wbGV0ZWA7XG4gICAgICBpZiAoanFodG1sMi5kZWJ1Zy5wcm9maWxlUGVyZm9ybWFuY2UpIHtcbiAgICAgICAgY29uc3Qgc3RhcnRUaW1lID0gcGVyZm9ybWFuY2VNZXRyaWNzLmdldChgJHtjb21wb25lbnQuX2NpZH1fJHtwaGFzZX1gKTtcbiAgICAgICAgaWYgKHN0YXJ0VGltZSkge1xuICAgICAgICAgIGNvbnN0IGR1cmF0aW9uID0gRGF0ZS5ub3coKSAtIHN0YXJ0VGltZTtcbiAgICAgICAgICBtZXNzYWdlICs9IGAgKCR7ZHVyYXRpb259bXMpYDtcbiAgICAgICAgICBpZiAocGhhc2UgPT09IFwicmVuZGVyXCIgJiYganFodG1sMi5kZWJ1Zy5oaWdobGlnaHRTbG93UmVuZGVycyAmJiBkdXJhdGlvbiA+IGpxaHRtbDIuZGVidWcuaGlnaGxpZ2h0U2xvd1JlbmRlcnMpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihgJHtwcmVmaXh9IFNMT1cgUkVOREVSOiAke2NvbXBvbmVudE5hbWV9IyR7Y29tcG9uZW50Ll9jaWR9IHRvb2sgJHtkdXJhdGlvbn1tc2ApO1xuICAgICAgICAgICAgY29tcG9uZW50LiQuY3NzKFwib3V0bGluZVwiLCBcIjJweCBkYXNoZWQgcmVkXCIpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgY29uc29sZS5sb2cobWVzc2FnZSk7XG4gICAgICBpZiAoanFodG1sMi5kZWJ1Zy5mbGFzaENvbXBvbmVudHMgJiYgKHBoYXNlID09PSBcImNyZWF0ZVwiIHx8IHBoYXNlID09PSBcInJlbmRlclwiIHx8IHBoYXNlID09PSBcInJlYWR5XCIpKSB7XG4gICAgICAgIGZsYXNoQ29tcG9uZW50KGNvbXBvbmVudCwgcGhhc2UpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoanFodG1sMi5kZWJ1Zy5zaG93Q29tcG9uZW50VHJlZSkge1xuICAgICAgdXBkYXRlQ29tcG9uZW50VHJlZSgpO1xuICAgIH1cbiAgfVxuICBmdW5jdGlvbiBhcHBseURlYnVnRGVsYXkocGhhc2UpIHtcbiAgICBjb25zdCBqcWh0bWwyID0gZ2V0SnFodG1sJDEoKTtcbiAgICBpZiAoIWpxaHRtbDI/LmRlYnVnKVxuICAgICAgcmV0dXJuO1xuICAgIGxldCBkZWxheU1zID0gMDtcbiAgICBzd2l0Y2ggKHBoYXNlKSB7XG4gICAgICBjYXNlIFwiY29tcG9uZW50XCI6XG4gICAgICAgIGRlbGF5TXMgPSBqcWh0bWwyLmRlYnVnLmRlbGF5QWZ0ZXJDb21wb25lbnQgfHwgMDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIFwicmVuZGVyXCI6XG4gICAgICAgIGRlbGF5TXMgPSBqcWh0bWwyLmRlYnVnLmRlbGF5QWZ0ZXJSZW5kZXIgfHwgMDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIFwicmVyZW5kZXJcIjpcbiAgICAgICAgZGVsYXlNcyA9IGpxaHRtbDIuZGVidWcuZGVsYXlBZnRlclJlcmVuZGVyIHx8IDA7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgICBpZiAoZGVsYXlNcyA+IDApIHtcbiAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MIERlYnVnXSBBcHBseWluZyAke2RlbGF5TXN9bXMgZGVsYXkgYWZ0ZXIgJHtwaGFzZX1gKTtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gdXBkYXRlQ29tcG9uZW50VHJlZSgpIHtcbiAgICBjb25zb2xlLmxvZyhcIltKUUhUTUwgVHJlZV0gQ29tcG9uZW50IGhpZXJhcmNoeSB1cGRhdGVkXCIpO1xuICB9XG4gIHZhciBKcWh0bWxfQ29tcG9uZW50ID0gY2xhc3MgX0pxaHRtbF9Db21wb25lbnQge1xuICAgIGNvbnN0cnVjdG9yKGVsZW1lbnQsIGFyZ3MgPSB7fSkge1xuICAgICAgdGhpcy5kYXRhID0ge307XG4gICAgICB0aGlzLl9yZWFkeV9zdGF0ZSA9IDA7XG4gICAgICB0aGlzLl9pbnN0YW50aWF0b3IgPSBudWxsO1xuICAgICAgdGhpcy5fZG9tX3BhcmVudCA9IG51bGw7XG4gICAgICB0aGlzLl9kb21fY2hpbGRyZW4gPSAvKiBAX19QVVJFX18gKi8gbmV3IFNldCgpO1xuICAgICAgdGhpcy5fdXNlX2RvbV9mYWxsYmFjayA9IGZhbHNlO1xuICAgICAgdGhpcy5fc3RvcHBlZCA9IGZhbHNlO1xuICAgICAgdGhpcy5fYm9vdGVkID0gZmFsc2U7XG4gICAgICB0aGlzLl9kYXRhX2JlZm9yZV9yZW5kZXIgPSBudWxsO1xuICAgICAgdGhpcy5fbGlmZWN5Y2xlX2NhbGxiYWNrcyA9IC8qIEBfX1BVUkVfXyAqLyBuZXcgTWFwKCk7XG4gICAgICB0aGlzLl9saWZlY3ljbGVfc3RhdGVzID0gLyogQF9fUFVSRV9fICovIG5ldyBTZXQoKTtcbiAgICAgIHRoaXMuX19sb2FkaW5nID0gZmFsc2U7XG4gICAgICB0aGlzLl9kaWRfZmlyc3RfcmVuZGVyID0gZmFsc2U7XG4gICAgICB0aGlzLl9yZW5kZXJfY291bnQgPSAwO1xuICAgICAgdGhpcy5fY2lkID0gdGhpcy5fZ2VuZXJhdGVfY2lkKCk7XG4gICAgICB0aGlzLl9saWZlY3ljbGVfbWFuYWdlciA9IExpZmVjeWNsZU1hbmFnZXIuZ2V0X2luc3RhbmNlKCk7XG4gICAgICBpZiAoZWxlbWVudCkge1xuICAgICAgICB0aGlzLiQgPSAkKGVsZW1lbnQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcbiAgICAgICAgdGhpcy4kID0gJChkaXYpO1xuICAgICAgfVxuICAgICAgY29uc3QgZGF0YUF0dHJzID0ge307XG4gICAgICBpZiAodGhpcy4kLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3QgZGF0YXNldCA9IHRoaXMuJFswXS5kYXRhc2V0IHx8IHt9O1xuICAgICAgICBmb3IgKGNvbnN0IGtleSBpbiBkYXRhc2V0KSB7XG4gICAgICAgICAgaWYgKGtleSAhPT0gXCJjaWRcIiAmJiBrZXkgIT09IFwidGlkXCIgJiYga2V5ICE9PSBcImNvbXBvbmVudE5hbWVcIiAmJiBrZXkgIT09IFwicmVhZHlTdGF0ZVwiKSB7XG4gICAgICAgICAgICBjb25zdCBkYXRhVmFsdWUgPSB0aGlzLiQuZGF0YShrZXkpO1xuICAgICAgICAgICAgaWYgKGRhdGFWYWx1ZSAhPT0gdm9pZCAwICYmIGRhdGFWYWx1ZSAhPT0gZGF0YXNldFtrZXldKSB7XG4gICAgICAgICAgICAgIGRhdGFBdHRyc1trZXldID0gZGF0YVZhbHVlO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgZGF0YUF0dHJzW2tleV0gPSBkYXRhc2V0W2tleV07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBsZXQgdGVtcGxhdGVfZm9yX2FyZ3M7XG4gICAgICBpZiAoYXJncy5fY29tcG9uZW50X25hbWUpIHtcbiAgICAgICAgdGVtcGxhdGVfZm9yX2FyZ3MgPSBnZXRfdGVtcGxhdGUoYXJncy5fY29tcG9uZW50X25hbWUpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGVtcGxhdGVfZm9yX2FyZ3MgPSBnZXRfdGVtcGxhdGVfYnlfY2xhc3ModGhpcy5jb25zdHJ1Y3Rvcik7XG4gICAgICB9XG4gICAgICBjb25zdCBkZWZpbmVBcmdzID0gdGVtcGxhdGVfZm9yX2FyZ3M/LmRlZmluZUFyZ3MgfHwge307XG4gICAgICB0aGlzLmFyZ3MgPSB7IC4uLmRlZmluZUFyZ3MsIC4uLmRhdGFBdHRycywgLi4uYXJncyB9O1xuICAgICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXModGhpcy5hcmdzKSkge1xuICAgICAgICBpZiAoa2V5ID09PSBcImNpZFwiIHx8IGtleSA9PT0gXCJ0aWRcIiB8fCBrZXkgPT09IFwiY29tcG9uZW50TmFtZVwiIHx8IGtleSA9PT0gXCJyZWFkeVN0YXRlXCIgfHwga2V5LnN0YXJ0c1dpdGgoXCJfXCIpKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiB8fCB0eXBlb2YgdmFsdWUgPT09IFwibnVtYmVyXCIpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgY3VycmVudEF0dHIgPSB0aGlzLiQuYXR0cihgZGF0YS0ke2tleX1gKTtcbiAgICAgICAgICAgIGlmIChjdXJyZW50QXR0ciAhPSB2YWx1ZSkge1xuICAgICAgICAgICAgICB0aGlzLiQuYXR0cihgZGF0YS0ke2tleX1gLCBTdHJpbmcodmFsdWUpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLiQuZGF0YShcIl9jb21wb25lbnRcIiwgdGhpcyk7XG4gICAgICB0aGlzLl9hcHBseV9jc3NfY2xhc3NlcygpO1xuICAgICAgdGhpcy5fYXBwbHlfZGVmYXVsdF9hdHRyaWJ1dGVzKCk7XG4gICAgICB0aGlzLl9zZXRfYXR0cmlidXRlcygpO1xuICAgICAgdGhpcy5fZmluZF9kb21fcGFyZW50KCk7XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwiY29uc3RydWN0XCIsIFwiY29tcGxldGVcIik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEJvb3QgLSBTdGFydCB0aGUgZnVsbCBjb21wb25lbnQgbGlmZWN5Y2xlXG4gICAgICogQ2FsbGVkIGltbWVkaWF0ZWx5IGFmdGVyIGNvbnN0cnVjdGlvbiBieSBpbnN0cnVjdGlvbiBwcm9jZXNzb3JcbiAgICAgKi9cbiAgICBhc3luYyBib290KCkge1xuICAgICAgaWYgKHRoaXMuX2Jvb3RlZClcbiAgICAgICAgcmV0dXJuO1xuICAgICAgdGhpcy5fYm9vdGVkID0gdHJ1ZTtcbiAgICAgIGF3YWl0IHRoaXMuX2xpZmVjeWNsZV9tYW5hZ2VyLmJvb3RfY29tcG9uZW50KHRoaXMpO1xuICAgIH1cbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLy8gTGlmZWN5Y2xlIE1ldGhvZHMgKGNhbGxlZCBieSBMaWZlY3ljbGVNYW5hZ2VyKVxuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvKipcbiAgICAgKiBJbnRlcm5hbCByZW5kZXIgcGhhc2UgLSBDcmVhdGUgRE9NIHN0cnVjdHVyZVxuICAgICAqIENhbGxlZCB0b3AtZG93biAocGFyZW50IGJlZm9yZSBjaGlsZHJlbikgd2hlbiBwYXJ0IG9mIGxpZmVjeWNsZVxuICAgICAqIFRoaXMgaXMgYW4gaW50ZXJuYWwgbWV0aG9kIC0gdXNlcnMgc2hvdWxkIGNhbGwgcmVuZGVyKCkgaW5zdGVhZFxuICAgICAqXG4gICAgICogQHBhcmFtIGlkIE9wdGlvbmFsIHNjb3BlZCBJRCAtIGlmIHByb3ZpZGVkLCBkZWxlZ2F0ZXMgdG8gY2hpbGQgY29tcG9uZW50J3MgX3JlbmRlcigpXG4gICAgICogQHJldHVybnMgVGhlIGN1cnJlbnQgX3JlbmRlcl9jb3VudCBhZnRlciBpbmNyZW1lbnRpbmcgKHVzZWQgdG8gZGV0ZWN0IHN0YWxlIHJlbmRlcnMpXG4gICAgICogQHByaXZhdGVcbiAgICAgKi9cbiAgICBfcmVuZGVyKGlkID0gbnVsbCkge1xuICAgICAgdGhpcy5fcmVuZGVyX2NvdW50Kys7XG4gICAgICBjb25zdCBjdXJyZW50X3JlbmRlcl9pZCA9IHRoaXMuX3JlbmRlcl9jb3VudDtcbiAgICAgIGlmICh0aGlzLl9zdG9wcGVkKVxuICAgICAgICByZXR1cm4gY3VycmVudF9yZW5kZXJfaWQ7XG4gICAgICBpZiAoaWQpIHtcbiAgICAgICAgY29uc3QgJGVsZW1lbnQgPSB0aGlzLiRpZChpZCk7XG4gICAgICAgIGlmICgkZWxlbWVudC5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFtKUUhUTUxdIHJlbmRlcihcIiR7aWR9XCIpIC0gbm8gc3VjaCBpZC5cbkNvbXBvbmVudCBcIiR7dGhpcy5jb21wb25lbnRfbmFtZSgpfVwiIGhhcyBubyBjaGlsZCBlbGVtZW50IHdpdGggJGlkPVwiJHtpZH1cIi5gKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBjaGlsZCA9ICRlbGVtZW50LmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICBpZiAoIWNoaWxkKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBbSlFIVE1MXSByZW5kZXIoXCIke2lkfVwiKSAtIGVsZW1lbnQgaXMgbm90IGEgY29tcG9uZW50IG9yIGRvZXMgbm90IGhhdmUgJHJlZHJhd2FibGUgYXR0cmlidXRlIHNldC5cbkVsZW1lbnQgd2l0aCAkaWQ9XCIke2lkfVwiIGV4aXN0cyBidXQgaXMgbm90IGluaXRpYWxpemVkIGFzIGEgY29tcG9uZW50LlxuQWRkICRyZWRyYXdhYmxlIGF0dHJpYnV0ZSBvciBtYWtlIGl0IGEgcHJvcGVyIGNvbXBvbmVudC5gKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gY2hpbGQuX3JlbmRlcigpO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuX19sb2FkaW5nKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgW0pRSFRNTF0gQ29tcG9uZW50IFwiJHt0aGlzLmNvbXBvbmVudF9uYW1lKCl9XCIgYXR0ZW1wdGVkIHRvIGNhbGwgcmVuZGVyKCkgZHVyaW5nIG9uX2xvYWQoKS5cbm9uX2xvYWQoKSBzaG91bGQgT05MWSBtb2RpZnkgdGhpcy5kYXRhLiBET00gdXBkYXRlcyBoYXBwZW4gYXV0b21hdGljYWxseSBhZnRlciBvbl9sb2FkKCkgY29tcGxldGVzLlxuXG5GaXg6IFJlbW92ZSB0aGUgdGhpcy5yZW5kZXIoKSBjYWxsIGZyb20gb25fbG9hZCgpLlxuVGhlIGZyYW1ld29yayB3aWxsIGF1dG9tYXRpY2FsbHkgcmUtcmVuZGVyIGlmIHRoaXMuZGF0YSBjaGFuZ2VzIGR1cmluZyBvbl9sb2FkKCkuYCk7XG4gICAgICB9XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwicmVuZGVyXCIsIFwic3RhcnRcIik7XG4gICAgICBpZiAoISQuY29udGFpbnMoZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LCB0aGlzLiRbMF0pKSB7XG4gICAgICAgIHRoaXMuX3VzZV9kb21fZmFsbGJhY2sgPSB0cnVlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5fdXNlX2RvbV9mYWxsYmFjayA9IGZhbHNlO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuX2RpZF9maXJzdF9yZW5kZXIpIHtcbiAgICAgICAgdGhpcy4kLmZpbmQoXCIuSnFodG1sX0NvbXBvbmVudFwiKS5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICAgIGNvbnN0IGNoaWxkID0gJCh0aGlzKS5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgICBpZiAoY2hpbGQgJiYgIWNoaWxkLl9zdG9wcGVkKSB7XG4gICAgICAgICAgICBjaGlsZC5fc3RvcCgpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuJFswXS5pbm5lckhUTUwgPSBcIlwiO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5fZGlkX2ZpcnN0X3JlbmRlciA9IHRydWU7XG4gICAgICB9XG4gICAgICB0aGlzLiQucmVtb3ZlQ2xhc3MoXCJfQ29tcG9uZW50X1N0b3BwZWRcIik7XG4gICAgICBpZiAodGhpcy5fZGF0YV9iZWZvcmVfcmVuZGVyID09PSBudWxsKSB7XG4gICAgICAgIHRoaXMuX2RhdGFfYmVmb3JlX3JlbmRlciA9IEpTT04uc3RyaW5naWZ5KHRoaXMuZGF0YSk7XG4gICAgICB9XG4gICAgICB0aGlzLl9kb21fY2hpbGRyZW4uY2xlYXIoKTtcbiAgICAgIGxldCB0ZW1wbGF0ZV9kZWY7XG4gICAgICBpZiAodGhpcy5hcmdzLl9jb21wb25lbnRfbmFtZSkge1xuICAgICAgICB0ZW1wbGF0ZV9kZWYgPSBnZXRfdGVtcGxhdGUodGhpcy5hcmdzLl9jb21wb25lbnRfbmFtZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0ZW1wbGF0ZV9kZWYgPSBnZXRfdGVtcGxhdGVfYnlfY2xhc3ModGhpcy5jb25zdHJ1Y3Rvcik7XG4gICAgICB9XG4gICAgICBpZiAodGVtcGxhdGVfZGVmICYmIHRlbXBsYXRlX2RlZi5yZW5kZXIpIHtcbiAgICAgICAgY29uc3QganFodG1sMiA9IHtcbiAgICAgICAgICBlc2NhcGVfaHRtbDogKHN0cikgPT4ge1xuICAgICAgICAgICAgY29uc3QgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcbiAgICAgICAgICAgIGRpdi50ZXh0Q29udGVudCA9IFN0cmluZyhzdHIpO1xuICAgICAgICAgICAgcmV0dXJuIGRpdi5pbm5lckhUTUw7XG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgICAgICBjb25zdCBkZWZhdWx0Q29udGVudCA9ICgpID0+IFwiXCI7XG4gICAgICAgIGxldCBbaW5zdHJ1Y3Rpb25zLCBjb250ZXh0XSA9IHRlbXBsYXRlX2RlZi5yZW5kZXIuYmluZCh0aGlzKShcbiAgICAgICAgICB0aGlzLmRhdGEsXG4gICAgICAgICAgdGhpcy5hcmdzLFxuICAgICAgICAgIHRoaXMuYXJncy5faW5uZXJodG1sX2Z1bmN0aW9uIHx8IGRlZmF1bHRDb250ZW50LFxuICAgICAgICAgIC8vIENvbnRlbnQgZnVuY3Rpb24gd2l0aCBmYWxsYmFja1xuICAgICAgICAgIGpxaHRtbDJcbiAgICAgICAgICAvLyBVdGlsaXRpZXMgb2JqZWN0XG4gICAgICAgICk7XG4gICAgICAgIGlmIChpbnN0cnVjdGlvbnMgJiYgdHlwZW9mIGluc3RydWN0aW9ucyA9PT0gXCJvYmplY3RcIiAmJiBpbnN0cnVjdGlvbnMuX3Nsb3RzICYmICFBcnJheS5pc0FycmF5KGluc3RydWN0aW9ucykpIHtcbiAgICAgICAgICBjb25zdCBjb21wb25lbnROYW1lID0gdGVtcGxhdGVfZGVmLm5hbWUgfHwgdGhpcy5hcmdzLl9jb21wb25lbnRfbmFtZSB8fCB0aGlzLmNvbnN0cnVjdG9yLm5hbWU7XG4gICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdIFNsb3Qtb25seSB0ZW1wbGF0ZSBkZXRlY3RlZCBmb3IgJHtjb21wb25lbnROYW1lfWApO1xuICAgICAgICAgIGxldCBwYXJlbnRUZW1wbGF0ZSA9IG51bGw7XG4gICAgICAgICAgbGV0IHBhcmVudFRlbXBsYXRlTmFtZSA9IG51bGw7XG4gICAgICAgICAgaWYgKHRlbXBsYXRlX2RlZi5leHRlbmRzKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gICBVc2luZyBleHBsaWNpdCBleHRlbmRzOiAke3RlbXBsYXRlX2RlZi5leHRlbmRzfWApO1xuICAgICAgICAgICAgcGFyZW50VGVtcGxhdGUgPSBnZXRfdGVtcGxhdGUodGVtcGxhdGVfZGVmLmV4dGVuZHMpO1xuICAgICAgICAgICAgcGFyZW50VGVtcGxhdGVOYW1lID0gdGVtcGxhdGVfZGVmLmV4dGVuZHM7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICghcGFyZW50VGVtcGxhdGUpIHtcbiAgICAgICAgICAgIGxldCBjdXJyZW50Q2xhc3MgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YodGhpcy5jb25zdHJ1Y3Rvcik7XG4gICAgICAgICAgICB3aGlsZSAoY3VycmVudENsYXNzICYmIGN1cnJlbnRDbGFzcy5uYW1lICE9PSBcIk9iamVjdFwiICYmIGN1cnJlbnRDbGFzcy5uYW1lICE9PSBcIkpxaHRtbF9Db21wb25lbnRcIikge1xuICAgICAgICAgICAgICBjb25zdCBjbGFzc05hbWUgPSBjdXJyZW50Q2xhc3MubmFtZTtcbiAgICAgICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdICAgQ2hlY2tpbmcgcGFyZW50OiAke2NsYXNzTmFtZX1gKTtcbiAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zdCBjbGFzc1RlbXBsYXRlID0gZ2V0X3RlbXBsYXRlKGNsYXNzTmFtZSk7XG4gICAgICAgICAgICAgICAgaWYgKGNsYXNzVGVtcGxhdGUgJiYgY2xhc3NUZW1wbGF0ZS5uYW1lICE9PSBcIkpxaHRtbF9Db21wb25lbnRcIikge1xuICAgICAgICAgICAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdICAgRm91bmQgcGFyZW50IHRlbXBsYXRlOiAke2NsYXNzTmFtZX1gKTtcbiAgICAgICAgICAgICAgICAgIHBhcmVudFRlbXBsYXRlID0gY2xhc3NUZW1wbGF0ZTtcbiAgICAgICAgICAgICAgICAgIHBhcmVudFRlbXBsYXRlTmFtZSA9IGNsYXNzTmFtZTtcbiAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIEVycm9yIGZpbmRpbmcgcGFyZW50IHRlbXBsYXRlICR7Y2xhc3NOYW1lfTpgLCBlcnJvcik7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgY3VycmVudENsYXNzID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKGN1cnJlbnRDbGFzcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChwYXJlbnRUZW1wbGF0ZSkge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgY29uc3QgY2hpbGRTbG90cyA9IGluc3RydWN0aW9ucy5fc2xvdHM7XG4gICAgICAgICAgICAgIGNvbnN0IGNvbnRlbnRGdW5jdGlvbiA9IChzbG90TmFtZSwgZGF0YSkgPT4ge1xuICAgICAgICAgICAgICAgIGlmIChjaGlsZFNsb3RzW3Nsb3ROYW1lXSAmJiB0eXBlb2YgY2hpbGRTbG90c1tzbG90TmFtZV0gPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgICAgICAgICAgY29uc3QgW3Nsb3RJbnN0cnVjdGlvbnMsIHNsb3RDb250ZXh0XSA9IGNoaWxkU2xvdHNbc2xvdE5hbWVdKGRhdGEpO1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIFtzbG90SW5zdHJ1Y3Rpb25zLCBzbG90Q29udGV4dF07XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybiBcIlwiO1xuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICBjb25zdCBbcGFyZW50SW5zdHJ1Y3Rpb25zLCBwYXJlbnRDb250ZXh0XSA9IHBhcmVudFRlbXBsYXRlLnJlbmRlci5iaW5kKHRoaXMpKFxuICAgICAgICAgICAgICAgIHRoaXMuZGF0YSxcbiAgICAgICAgICAgICAgICB0aGlzLmFyZ3MsXG4gICAgICAgICAgICAgICAgY29udGVudEZ1bmN0aW9uLFxuICAgICAgICAgICAgICAgIC8vIFBhc3MgY29udGVudCBmdW5jdGlvbiB0aGF0IGludm9rZXMgY2hpbGQgc2xvdHNcbiAgICAgICAgICAgICAgICBqcWh0bWwyXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSAgIFBhcmVudCB0ZW1wbGF0ZSBpbnZva2VkIHN1Y2Nlc3NmdWxseWApO1xuICAgICAgICAgICAgICBpbnN0cnVjdGlvbnMgPSBwYXJlbnRJbnN0cnVjdGlvbnM7XG4gICAgICAgICAgICAgIGNvbnRleHQgPSBwYXJlbnRDb250ZXh0O1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBFcnJvciBpbnZva2luZyBwYXJlbnQgdGVtcGxhdGUgJHtwYXJlbnRUZW1wbGF0ZU5hbWV9OmAsIGVycm9yKTtcbiAgICAgICAgICAgICAgaW5zdHJ1Y3Rpb25zID0gW107XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihgW0pRSFRNTF0gTm8gcGFyZW50IHRlbXBsYXRlIGZvdW5kIGZvciAke3RoaXMuY29uc3RydWN0b3IubmFtZX0sIHJlbmRlcmluZyBlbXB0eWApO1xuICAgICAgICAgICAgaW5zdHJ1Y3Rpb25zID0gW107XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGZsYXR0ZW5lZEluc3RydWN0aW9ucyA9IHRoaXMuX2ZsYXR0ZW5faW5zdHJ1Y3Rpb25zKGluc3RydWN0aW9ucyk7XG4gICAgICAgIHByb2Nlc3NfaW5zdHJ1Y3Rpb25zKGZsYXR0ZW5lZEluc3RydWN0aW9ucywgdGhpcy4kLCB0aGlzKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuX3VwZGF0ZV9kZWJ1Z19hdHRycygpO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcInJlbmRlclwiLCBcImNvbXBsZXRlXCIpO1xuICAgICAgY29uc3QgcmVuZGVyUmVzdWx0ID0gdGhpcy5vbl9yZW5kZXIoKTtcbiAgICAgIGlmIChyZW5kZXJSZXN1bHQgJiYgdHlwZW9mIHJlbmRlclJlc3VsdC50aGVuID09PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBDb21wb25lbnQgXCIke3RoaXMuY29tcG9uZW50X25hbWUoKX1cIiByZXR1cm5lZCBhIFByb21pc2UgZnJvbSBvbl9yZW5kZXIoKS4gb25fcmVuZGVyKCkgbXVzdCBiZSBzeW5jaHJvbm91cyBjb2RlLiBSZW1vdmUgJ2FzeW5jJyBmcm9tIHRoZSBmdW5jdGlvbiBkZWNsYXJhdGlvbi5gKTtcbiAgICAgIH1cbiAgICAgIHRoaXMudHJpZ2dlcihcInJlbmRlclwiKTtcbiAgICAgIGNvbnN0IGlzUmVyZW5kZXIgPSB0aGlzLl9yZWFkeV9zdGF0ZSA+PSAzO1xuICAgICAgYXBwbHlEZWJ1Z0RlbGF5KGlzUmVyZW5kZXIgPyBcInJlcmVuZGVyXCIgOiBcInJlbmRlclwiKTtcbiAgICAgIHJldHVybiBjdXJyZW50X3JlbmRlcl9pZDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUHVibGljIHJlbmRlciBtZXRob2QgLSByZS1yZW5kZXJzIGNvbXBvbmVudCBhbmQgY29tcGxldGVzIGxpZmVjeWNsZVxuICAgICAqIFRoaXMgaXMgd2hhdCB1c2VycyBzaG91bGQgY2FsbCB3aGVuIHRoZXkgd2FudCB0byB1cGRhdGUgYSBjb21wb25lbnQuXG4gICAgICpcbiAgICAgKiBMaWZlY3ljbGUgc2VxdWVuY2U6XG4gICAgICogMS4gX3JlbmRlcigpIC0gVXBkYXRlcyBET00gc3luY2hyb25vdXNseSwgY2FsbHMgb25fcmVuZGVyKCksIGZpcmVzICdyZW5kZXInIGV2ZW50XG4gICAgICogMi4gQXN5bmMgY29udGludWF0aW9uIChmaXJlIGFuZCBmb3JnZXQpOlxuICAgICAqICAgIC0gX3dhaXRfZm9yX2NoaWxkcmVuX3JlYWR5KCkgLSBXYWl0cyBmb3IgYWxsIGNoaWxkcmVuIHRvIHJlYWNoIHJlYWR5IHN0YXRlXG4gICAgICogICAgLSBvbl9yZWFkeSgpIC0gQ2FsbHMgdXNlcidzIHJlYWR5IGhvb2tcbiAgICAgKiAgICAtIHRyaWdnZXIoJ3JlYWR5JykgLSBGaXJlcyByZWFkeSBldmVudFxuICAgICAqXG4gICAgICogUmV0dXJucyBpbW1lZGlhdGVseSBhZnRlciBfcmVuZGVyKCkgY29tcGxldGVzIC0gZG9lcyBOT1Qgd2FpdCBmb3IgY2hpbGRyZW5cbiAgICAgKi9cbiAgICByZW5kZXIoaWQgPSBudWxsKSB7XG4gICAgICBpZiAodGhpcy5fc3RvcHBlZClcbiAgICAgICAgcmV0dXJuO1xuICAgICAgaWYgKGlkKSB7XG4gICAgICAgIGNvbnN0ICRlbGVtZW50ID0gdGhpcy4kaWQoaWQpO1xuICAgICAgICBpZiAoJGVsZW1lbnQubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBbSlFIVE1MXSByZW5kZXIoXCIke2lkfVwiKSAtIG5vIHN1Y2ggaWQuXG5Db21wb25lbnQgXCIke3RoaXMuY29tcG9uZW50X25hbWUoKX1cIiBoYXMgbm8gY2hpbGQgZWxlbWVudCB3aXRoICRpZD1cIiR7aWR9XCIuYCk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgY2hpbGQgPSAkZWxlbWVudC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgaWYgKCFjaGlsZCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgW0pRSFRNTF0gcmVuZGVyKFwiJHtpZH1cIikgLSBlbGVtZW50IGlzIG5vdCBhIGNvbXBvbmVudCBvciBkb2VzIG5vdCBoYXZlICRyZWRyYXdhYmxlIGF0dHJpYnV0ZSBzZXQuXG5FbGVtZW50IHdpdGggJGlkPVwiJHtpZH1cIiBleGlzdHMgYnV0IGlzIG5vdCBpbml0aWFsaXplZCBhcyBhIGNvbXBvbmVudC5cbkFkZCAkcmVkcmF3YWJsZSBhdHRyaWJ1dGUgb3IgbWFrZSBpdCBhIHByb3BlciBjb21wb25lbnQuYCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNoaWxkLnJlbmRlcigpO1xuICAgICAgfVxuICAgICAgY29uc3QgcmVuZGVyX2lkID0gdGhpcy5fcmVuZGVyKCk7XG4gICAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgICBhd2FpdCB0aGlzLl93YWl0X2Zvcl9jaGlsZHJlbl9yZWFkeSgpO1xuICAgICAgICBpZiAodGhpcy5fcmVuZGVyX2NvdW50ICE9PSByZW5kZXJfaWQpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgdGhpcy5vbl9yZWFkeSgpO1xuICAgICAgICBhd2FpdCB0aGlzLnRyaWdnZXIoXCJyZWFkeVwiKTtcbiAgICAgIH0pKCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEFsaWFzIGZvciByZW5kZXIoKSAtIHJlLXJlbmRlcnMgY29tcG9uZW50IHdpdGggY3VycmVudCBkYXRhXG4gICAgICogUHJvdmlkZWQgZm9yIEFQSSBjb25zaXN0ZW5jeSBhbmQgY2xhcml0eVxuICAgICAqL1xuICAgIHJlZHJhdyhpZCA9IG51bGwpIHtcbiAgICAgIHJldHVybiB0aGlzLnJlbmRlcihpZCk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIENyZWF0ZSBwaGFzZSAtIFF1aWNrIHNldHVwLCBwcmVwYXJlIFVJXG4gICAgICogQ2FsbGVkIGJvdHRvbS11cCAoY2hpbGRyZW4gYmVmb3JlIHBhcmVudClcbiAgICAgKi9cbiAgICBhc3luYyBjcmVhdGUoKSB7XG4gICAgICBpZiAodGhpcy5fc3RvcHBlZCB8fCB0aGlzLl9yZWFkeV9zdGF0ZSA+PSAxKVxuICAgICAgICByZXR1cm47XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwiY3JlYXRlXCIsIFwic3RhcnRcIik7XG4gICAgICBjb25zdCByZXN1bHQgPSB0aGlzLm9uX2NyZWF0ZSgpO1xuICAgICAgaWYgKHJlc3VsdCAmJiB0eXBlb2YgcmVzdWx0LnRoZW4gPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIENvbXBvbmVudCBcIiR7dGhpcy5jb21wb25lbnRfbmFtZSgpfVwiIHJldHVybmVkIGEgUHJvbWlzZSBmcm9tIG9uX2NyZWF0ZSgpLiBvbl9jcmVhdGUoKSBtdXN0IGJlIHN5bmNocm9ub3VzIGNvZGUuIFJlbW92ZSAnYXN5bmMnIGZyb20gdGhlIGZ1bmN0aW9uIGRlY2xhcmF0aW9uLmApO1xuICAgICAgICBhd2FpdCByZXN1bHQ7XG4gICAgICB9XG4gICAgICB0aGlzLl9yZWFkeV9zdGF0ZSA9IDE7XG4gICAgICB0aGlzLl91cGRhdGVfZGVidWdfYXR0cnMoKTtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJjcmVhdGVcIiwgXCJjb21wbGV0ZVwiKTtcbiAgICAgIHRoaXMudHJpZ2dlcihcImNyZWF0ZVwiKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogTG9hZCBwaGFzZSAtIEZldGNoIGRhdGEgZnJvbSBBUElzXG4gICAgICogQ2FsbGVkIGJvdHRvbS11cCwgZnVsbHkgcGFyYWxsZWxcbiAgICAgKiBOTyBET00gTU9ESUZJQ0FUSU9OUyBBTExPV0VEIElOIFRISVMgUEhBU0VcbiAgICAgKi9cbiAgICBhc3luYyBsb2FkKCkge1xuICAgICAgaWYgKHRoaXMuX3N0b3BwZWQgfHwgdGhpcy5fcmVhZHlfc3RhdGUgPj0gMilcbiAgICAgICAgcmV0dXJuO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcImxvYWRcIiwgXCJzdGFydFwiKTtcbiAgICAgIGNvbnN0IGFyZ3NCZWZvcmVMb2FkID0gSlNPTi5zdHJpbmdpZnkodGhpcy5hcmdzKTtcbiAgICAgIGNvbnN0IHByb3BlcnRpZXNCZWZvcmVMb2FkID0gbmV3IFNldChPYmplY3Qua2V5cyh0aGlzKSk7XG4gICAgICB0aGlzLl9fbG9hZGluZyA9IHRydWU7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCB0aGlzLm9uX2xvYWQoKTtcbiAgICAgIH0gZmluYWxseSB7XG4gICAgICAgIHRoaXMuX19sb2FkaW5nID0gZmFsc2U7XG4gICAgICB9XG4gICAgICBjb25zdCBhcmdzQWZ0ZXJMb2FkID0gSlNPTi5zdHJpbmdpZnkodGhpcy5hcmdzKTtcbiAgICAgIGNvbnN0IHByb3BlcnRpZXNBZnRlckxvYWQgPSBPYmplY3Qua2V5cyh0aGlzKTtcbiAgICAgIGlmIChhcmdzQmVmb3JlTG9hZCAhPT0gYXJnc0FmdGVyTG9hZCkge1xuICAgICAgICBjb25zb2xlLmVycm9yKGBbSlFIVE1MXSBXQVJOSU5HOiBDb21wb25lbnQgXCIke3RoaXMuY29tcG9uZW50X25hbWUoKX1cIiBtb2RpZmllZCB0aGlzLmFyZ3MgaW4gb25fbG9hZCgpLlxub25fbG9hZCgpIHNob3VsZCBPTkxZIG1vZGlmeSB0aGlzLmRhdGEuIFRoZSB0aGlzLmFyZ3MgcHJvcGVydHkgaXMgcmVhZC1vbmx5LlxuXG5CZWZvcmU6ICR7YXJnc0JlZm9yZUxvYWR9XG5BZnRlcjogICR7YXJnc0FmdGVyTG9hZH1cblxuRml4OiBNb3ZlIHlvdXIgbW9kaWZpY2F0aW9ucyB0byB0aGlzLmRhdGEgaW5zdGVhZC5gKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IG5ld1Byb3BlcnRpZXMgPSBwcm9wZXJ0aWVzQWZ0ZXJMb2FkLmZpbHRlcigocHJvcCkgPT4gIXByb3BlcnRpZXNCZWZvcmVMb2FkLmhhcyhwcm9wKSAmJiBwcm9wICE9PSBcImRhdGFcIik7XG4gICAgICBpZiAobmV3UHJvcGVydGllcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYFtKUUhUTUxdIFdBUk5JTkc6IENvbXBvbmVudCBcIiR7dGhpcy5jb21wb25lbnRfbmFtZSgpfVwiIGFkZGVkIG5ldyBwcm9wZXJ0aWVzIGluIG9uX2xvYWQoKS5cbm9uX2xvYWQoKSBzaG91bGQgT05MWSBtb2RpZnkgdGhpcy5kYXRhLiBOZXcgcHJvcGVydGllcyBkZXRlY3RlZDogJHtuZXdQcm9wZXJ0aWVzLmpvaW4oXCIsIFwiKX1cblxuRml4OiBTdG9yZSB5b3VyIGRhdGEgaW4gdGhpcy5kYXRhIGluc3RlYWQ6XG4gIFxcdTI3NEMgdGhpcy4ke25ld1Byb3BlcnRpZXNbMF19ID0gdmFsdWU7XG4gIFxcdTI3MDUgdGhpcy5kYXRhLiR7bmV3UHJvcGVydGllc1swXX0gPSB2YWx1ZTtgKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuX3JlYWR5X3N0YXRlID0gMjtcbiAgICAgIHRoaXMuX3VwZGF0ZV9kZWJ1Z19hdHRycygpO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcImxvYWRcIiwgXCJjb21wbGV0ZVwiKTtcbiAgICAgIHRoaXMudHJpZ2dlcihcImxvYWRcIik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlYWR5IHBoYXNlIC0gQ29tcG9uZW50IGZ1bGx5IGluaXRpYWxpemVkXG4gICAgICogQ2FsbGVkIGJvdHRvbS11cCAoY2hpbGRyZW4gYmVmb3JlIHBhcmVudClcbiAgICAgKi9cbiAgICBhc3luYyByZWFkeSgpIHtcbiAgICAgIGlmICh0aGlzLl9zdG9wcGVkIHx8IHRoaXMuX3JlYWR5X3N0YXRlID49IDQpXG4gICAgICAgIHJldHVybjtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJyZWFkeVwiLCBcInN0YXJ0XCIpO1xuICAgICAgYXdhaXQgdGhpcy5fd2FpdF9mb3JfY2hpbGRyZW5fcmVhZHkoKTtcbiAgICAgIGF3YWl0IHRoaXMub25fcmVhZHkoKTtcbiAgICAgIHRoaXMuX3JlYWR5X3N0YXRlID0gNDtcbiAgICAgIHRoaXMuX3VwZGF0ZV9kZWJ1Z19hdHRycygpO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcInJlYWR5XCIsIFwiY29tcGxldGVcIik7XG4gICAgICB0aGlzLnRyaWdnZXIoXCJyZWFkeVwiKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogV2FpdCBmb3IgYWxsIGNoaWxkIGNvbXBvbmVudHMgdG8gcmVhY2ggcmVhZHkgc3RhdGVcbiAgICAgKiBFbnN1cmVzIGJvdHRvbS11cCBvcmRlcmluZyAoY2hpbGRyZW4gcmVhZHkgYmVmb3JlIHBhcmVudClcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqL1xuICAgIGFzeW5jIF93YWl0X2Zvcl9jaGlsZHJlbl9yZWFkeSgpIHtcbiAgICAgIGNvbnN0IGNoaWxkcmVuID0gdGhpcy5fZ2V0X2RvbV9jaGlsZHJlbigpO1xuICAgICAgaWYgKGNoaWxkcmVuLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCByZWFkeV9wcm9taXNlcyA9IFtdO1xuICAgICAgZm9yIChjb25zdCBjaGlsZCBvZiBjaGlsZHJlbikge1xuICAgICAgICBpZiAoY2hpbGQuX3JlYWR5X3N0YXRlID49IDQpIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCByZWFkeV9wcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICBjaGlsZC5vbihcInJlYWR5XCIsICgpID0+IHJlc29sdmUoKSk7XG4gICAgICAgIH0pO1xuICAgICAgICByZWFkeV9wcm9taXNlcy5wdXNoKHJlYWR5X3Byb21pc2UpO1xuICAgICAgfVxuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwocmVhZHlfcHJvbWlzZXMpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWluaXRpYWxpemUgdGhlIGNvbXBvbmVudCAtIGZ1bGwgcmVzZXQgYW5kIHJlLWluaXRpYWxpemF0aW9uXG4gICAgICogV2lwZXMgdGhlIGlubmVySFRNTCwgcmVzZXRzIGRhdGEgdG8gZW1wdHksIGFuZCBydW5zIGZ1bGwgbGlmZWN5Y2xlXG4gICAgICovXG4gICAgYXN5bmMgcmVpbml0aWFsaXplKCkge1xuICAgICAgaWYgKHRoaXMuX3N0b3BwZWQpXG4gICAgICAgIHJldHVybjtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJyZWluaXRpYWxpemVcIiwgXCJzdGFydFwiKTtcbiAgICAgIHRoaXMuJFswXS5pbm5lckhUTUwgPSBcIlwiO1xuICAgICAgdGhpcy5kYXRhID0ge307XG4gICAgICB0aGlzLl9yZWFkeV9zdGF0ZSA9IDA7XG4gICAgICB0aGlzLl9kYXRhX2JlZm9yZV9yZW5kZXIgPSBudWxsO1xuICAgICAgdGhpcy5fZG9tX2NoaWxkcmVuLmNsZWFyKCk7XG4gICAgICBhd2FpdCB0aGlzLl9yZW5kZXIoKTtcbiAgICAgIGF3YWl0IHRoaXMuY3JlYXRlKCk7XG4gICAgICBhd2FpdCB0aGlzLmxvYWQoKTtcbiAgICAgIGlmICh0aGlzLnNob3VsZF9yZXJlbmRlcigpKSB7XG4gICAgICAgIGF3YWl0IHRoaXMuX3JlbmRlcigpO1xuICAgICAgfVxuICAgICAgYXdhaXQgdGhpcy5yZWFkeSgpO1xuICAgICAgdGhpcy5fbG9nX2xpZmVjeWNsZShcInJlaW5pdGlhbGl6ZVwiLCBcImNvbXBsZXRlXCIpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZWxvYWQgY29tcG9uZW50IC0gcmUtZmV0Y2ggZGF0YSBhbmQgcmUtcmVuZGVyXG4gICAgICogUmUtcnVucyBvbl9sb2FkKCksIGFsd2F5cyByZW5kZXJzLCBhbmQgY2FsbHMgb25fcmVhZHkoKVxuICAgICAqL1xuICAgIGFzeW5jIHJlbG9hZCgpIHtcbiAgICAgIGlmICh0aGlzLl9zdG9wcGVkKVxuICAgICAgICByZXR1cm47XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwicmVsb2FkXCIsIFwic3RhcnRcIik7XG4gICAgICBjb25zdCBoYXNfY3VzdG9tX29uX2xvYWQgPSB0aGlzLm9uX2xvYWQgIT09IF9KcWh0bWxfQ29tcG9uZW50LnByb3RvdHlwZS5vbl9sb2FkO1xuICAgICAgaWYgKGhhc19jdXN0b21fb25fbG9hZCkge1xuICAgICAgICBjb25zdCBhcmdzQmVmb3JlTG9hZCA9IEpTT04uc3RyaW5naWZ5KHRoaXMuYXJncyk7XG4gICAgICAgIGNvbnN0IHByb3BlcnRpZXNCZWZvcmVMb2FkID0gbmV3IFNldChPYmplY3Qua2V5cyh0aGlzKSk7XG4gICAgICAgIHRoaXMuX19sb2FkaW5nID0gdHJ1ZTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCB0aGlzLm9uX2xvYWQoKTtcbiAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICB0aGlzLl9fbG9hZGluZyA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGFyZ3NBZnRlckxvYWQgPSBKU09OLnN0cmluZ2lmeSh0aGlzLmFyZ3MpO1xuICAgICAgICBjb25zdCBwcm9wZXJ0aWVzQWZ0ZXJMb2FkID0gT2JqZWN0LmtleXModGhpcyk7XG4gICAgICAgIGlmIChhcmdzQmVmb3JlTG9hZCAhPT0gYXJnc0FmdGVyTG9hZCkge1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYFtKUUhUTUxdIFdBUk5JTkc6IENvbXBvbmVudCBcIiR7dGhpcy5jb21wb25lbnRfbmFtZSgpfVwiIG1vZGlmaWVkIHRoaXMuYXJncyBpbiBvbl9sb2FkKCkuXG5vbl9sb2FkKCkgc2hvdWxkIE9OTFkgbW9kaWZ5IHRoaXMuZGF0YS4gVGhlIHRoaXMuYXJncyBwcm9wZXJ0eSBpcyByZWFkLW9ubHkuXG5cbkJlZm9yZTogJHthcmdzQmVmb3JlTG9hZH1cbkFmdGVyOiAgJHthcmdzQWZ0ZXJMb2FkfVxuXG5GaXg6IE1vdmUgeW91ciBtb2RpZmljYXRpb25zIHRvIHRoaXMuZGF0YSBpbnN0ZWFkLmApO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IG5ld1Byb3BlcnRpZXMgPSBwcm9wZXJ0aWVzQWZ0ZXJMb2FkLmZpbHRlcigocHJvcCkgPT4gIXByb3BlcnRpZXNCZWZvcmVMb2FkLmhhcyhwcm9wKSAmJiBwcm9wICE9PSBcImRhdGFcIik7XG4gICAgICAgIGlmIChuZXdQcm9wZXJ0aWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBjb25zb2xlLmVycm9yKGBbSlFIVE1MXSBXQVJOSU5HOiBDb21wb25lbnQgXCIke3RoaXMuY29tcG9uZW50X25hbWUoKX1cIiBhZGRlZCBuZXcgcHJvcGVydGllcyBpbiBvbl9sb2FkKCkuXG5vbl9sb2FkKCkgc2hvdWxkIE9OTFkgbW9kaWZ5IHRoaXMuZGF0YS4gTmV3IHByb3BlcnRpZXMgZGV0ZWN0ZWQ6ICR7bmV3UHJvcGVydGllcy5qb2luKFwiLCBcIil9XG5cbkZpeDogU3RvcmUgeW91ciBkYXRhIGluIHRoaXMuZGF0YSBpbnN0ZWFkOlxuICBcXHUyNzRDIHRoaXMuJHtuZXdQcm9wZXJ0aWVzWzBdfSA9IHZhbHVlO1xuICBcXHUyNzA1IHRoaXMuZGF0YS4ke25ld1Byb3BlcnRpZXNbMF19ID0gdmFsdWU7YCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGF3YWl0IHRoaXMucmVuZGVyKCk7XG4gICAgICB0aGlzLl9sb2dfbGlmZWN5Y2xlKFwicmVsb2FkXCIsIFwiY29tcGxldGVcIik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIERlc3Ryb3kgdGhlIGNvbXBvbmVudCBhbmQgY2xlYW51cFxuICAgICAqIENhbGxlZCBhdXRvbWF0aWNhbGx5IGJ5IE11dGF0aW9uT2JzZXJ2ZXIgd2hlbiBjb21wb25lbnQgaXMgcmVtb3ZlZCBmcm9tIERPTVxuICAgICAqIENhbiBhbHNvIGJlIGNhbGxlZCBtYW51YWxseSBmb3IgZXhwbGljaXQgY2xlYW51cFxuICAgICAqL1xuICAgIC8qKlxuICAgICAqIEludGVybmFsIHN0b3AgbWV0aG9kIC0gc3RvcHMganVzdCB0aGlzIGNvbXBvbmVudCAobm8gY2hpbGRyZW4pXG4gICAgICogU2V0cyBzdG9wcGVkIGZsYWcsIGNhbGxzIGxpZmVjeWNsZSBob29rcywgYnV0IGxlYXZlcyBET00gaW50YWN0XG4gICAgICogQHByaXZhdGVcbiAgICAgKi9cbiAgICBfc3RvcCgpIHtcbiAgICAgIGlmICh0aGlzLl9zdG9wcGVkKVxuICAgICAgICByZXR1cm47XG4gICAgICB0aGlzLl9zdG9wcGVkID0gdHJ1ZTtcbiAgICAgIGNvbnN0IGhhc19jdXN0b21fZGVzdHJveSA9IHRoaXMub25fZGVzdHJveSAhPT0gX0pxaHRtbF9Db21wb25lbnQucHJvdG90eXBlLm9uX2Rlc3Ryb3k7XG4gICAgICBjb25zdCBoYXNfZGVzdHJveV9jYWxsYmFja3MgPSB0aGlzLl9vbl9yZWdpc3RlcmVkKFwiZGVzdHJveVwiKTtcbiAgICAgIGlmICghaGFzX2N1c3RvbV9kZXN0cm95ICYmICFoYXNfZGVzdHJveV9jYWxsYmFja3MpIHtcbiAgICAgICAgdGhpcy5fbGlmZWN5Y2xlX21hbmFnZXIudW5yZWdpc3Rlcl9jb21wb25lbnQodGhpcyk7XG4gICAgICAgIHRoaXMuX3JlYWR5X3N0YXRlID0gOTk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJkZXN0cm95XCIsIFwic3RhcnRcIik7XG4gICAgICB0aGlzLiQuYWRkQ2xhc3MoXCJfQ29tcG9uZW50X1N0b3BwZWRcIik7XG4gICAgICB0aGlzLl9saWZlY3ljbGVfbWFuYWdlci51bnJlZ2lzdGVyX2NvbXBvbmVudCh0aGlzKTtcbiAgICAgIGNvbnN0IGRlc3Ryb3lSZXN1bHQgPSB0aGlzLm9uX2Rlc3Ryb3koKTtcbiAgICAgIGlmIChkZXN0cm95UmVzdWx0ICYmIHR5cGVvZiBkZXN0cm95UmVzdWx0LnRoZW4gPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIENvbXBvbmVudCBcIiR7dGhpcy5jb21wb25lbnRfbmFtZSgpfVwiIHJldHVybmVkIGEgUHJvbWlzZSBmcm9tIG9uX2Rlc3Ryb3koKS4gb25fZGVzdHJveSgpIG11c3QgYmUgc3luY2hyb25vdXMgY29kZS4gUmVtb3ZlICdhc3luYycgZnJvbSB0aGUgZnVuY3Rpb24gZGVjbGFyYXRpb24uYCk7XG4gICAgICB9XG4gICAgICB0aGlzLnRyaWdnZXIoXCJkZXN0cm95XCIpO1xuICAgICAgdGhpcy4kLnRyaWdnZXIoXCJkZXN0cm95XCIpO1xuICAgICAgaWYgKHRoaXMuX2RvbV9wYXJlbnQpIHtcbiAgICAgICAgdGhpcy5fZG9tX3BhcmVudC5fZG9tX2NoaWxkcmVuLmRlbGV0ZSh0aGlzKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuX3JlYWR5X3N0YXRlID0gOTk7XG4gICAgICB0aGlzLl91cGRhdGVfZGVidWdfYXR0cnMoKTtcbiAgICAgIHRoaXMuX2xvZ19saWZlY3ljbGUoXCJkZXN0cm95XCIsIFwiY29tcGxldGVcIik7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFN0b3AgY29tcG9uZW50IGxpZmVjeWNsZSAtIHN0b3BzIGFsbCBkZXNjZW5kYW50IGNvbXBvbmVudHMgdGhlbiBzZWxmXG4gICAgICogTGVhdmVzIERPTSBpbnRhY3QsIGp1c3Qgc3RvcHMgbGlmZWN5Y2xlIGVuZ2luZSBhbmQgZmlyZXMgY2xlYW51cCBob29rc1xuICAgICAqL1xuICAgIHN0b3AoKSB7XG4gICAgICB0aGlzLiQuZmluZChcIi5KcWh0bWxfQ29tcG9uZW50XCIpLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgIGNvbnN0IGNoaWxkID0gJCh0aGlzKS5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgaWYgKGNoaWxkICYmICFjaGlsZC5fc3RvcHBlZCkge1xuICAgICAgICAgIGNoaWxkLl9zdG9wKCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgdGhpcy5fc3RvcCgpO1xuICAgIH1cbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLy8gT3ZlcnJpZGFibGUgTGlmZWN5Y2xlIEhvb2tzXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIG9uX3JlbmRlcigpIHtcbiAgICB9XG4gICAgb25fY3JlYXRlKCkge1xuICAgIH1cbiAgICBhc3luYyBvbl9sb2FkKCkge1xuICAgIH1cbiAgICBhc3luYyBvbl9yZWFkeSgpIHtcbiAgICB9XG4gICAgb25fZGVzdHJveSgpIHtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU2hvdWxkIGNvbXBvbmVudCByZS1yZW5kZXIgYWZ0ZXIgbG9hZD9cbiAgICAgKiBCeSBkZWZhdWx0LCBvbmx5IHJlLXJlbmRlcnMgaWYgZGF0YSBoYXMgY2hhbmdlZFxuICAgICAqIE92ZXJyaWRlIHRvIGNvbnRyb2wgcmUtcmVuZGVyaW5nIGJlaGF2aW9yXG4gICAgICovXG4gICAgc2hvdWxkX3JlcmVuZGVyKCkge1xuICAgICAgY29uc3QgY3VycmVudERhdGFTdGF0ZSA9IEpTT04uc3RyaW5naWZ5KHRoaXMuZGF0YSk7XG4gICAgICBjb25zdCBkYXRhQ2hhbmdlZCA9IHRoaXMuX2RhdGFfYmVmb3JlX3JlbmRlciAhPT0gY3VycmVudERhdGFTdGF0ZTtcbiAgICAgIGlmIChkYXRhQ2hhbmdlZCkge1xuICAgICAgICB0aGlzLl9kYXRhX2JlZm9yZV9yZW5kZXIgPSBjdXJyZW50RGF0YVN0YXRlO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGRhdGFDaGFuZ2VkO1xuICAgIH1cbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLy8gUHVibGljIEFQSVxuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvKipcbiAgICAgKiBHZXQgY29tcG9uZW50IG5hbWUgZm9yIGRlYnVnZ2luZ1xuICAgICAqL1xuICAgIGNvbXBvbmVudF9uYW1lKCkge1xuICAgICAgcmV0dXJuIHRoaXMuY29uc3RydWN0b3IubmFtZTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRW1pdCBhIGpRdWVyeSBldmVudCBmcm9tIGNvbXBvbmVudCByb290XG4gICAgICovXG4gICAgZW1pdChldmVudF9uYW1lLCBkYXRhKSB7XG4gICAgICB0aGlzLl9sb2dfZGVidWcoXCJlbWl0XCIsIGV2ZW50X25hbWUsIGRhdGEpO1xuICAgICAgdGhpcy4kLnRyaWdnZXIoZXZlbnRfbmFtZSwgZGF0YSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlZ2lzdGVyIGxpZmVjeWNsZSBldmVudCBjYWxsYmFja1xuICAgICAqIEFsbG93ZWQgZXZlbnRzOiAncmVuZGVyJywgJ2NyZWF0ZScsICdsb2FkJywgJ3JlYWR5JywgJ2Rlc3Ryb3knXG4gICAgICogQ2FsbGJhY2tzIGZpcmUgYWZ0ZXIgdGhlIGxpZmVjeWNsZSBtZXRob2QgY29tcGxldGVzXG4gICAgICogSWYgdGhlIGV2ZW50IGhhcyBhbHJlYWR5IG9jY3VycmVkLCB0aGUgY2FsbGJhY2sgZmlyZXMgaW1tZWRpYXRlbHkgQU5EIHJlZ2lzdGVycyBmb3IgZnV0dXJlIG9jY3VycmVuY2VzXG4gICAgICovXG4gICAgb24oZXZlbnRfbmFtZSwgY2FsbGJhY2spIHtcbiAgICAgIGNvbnN0IGFsbG93ZWRfZXZlbnRzID0gW1wicmVuZGVyXCIsIFwiY3JlYXRlXCIsIFwibG9hZFwiLCBcInJlYWR5XCIsIFwiZGVzdHJveVwiXTtcbiAgICAgIGlmICghYWxsb3dlZF9ldmVudHMuaW5jbHVkZXMoZXZlbnRfbmFtZSkpIHtcbiAgICAgICAgY29uc29sZS5lcnJvcihgW0pRSFRNTF0gQ29tcG9uZW50Lm9uKCkgb25seSBzdXBwb3J0cyBsaWZlY3ljbGUgZXZlbnRzOiAke2FsbG93ZWRfZXZlbnRzLmpvaW4oXCIsIFwiKX0uIFJlY2VpdmVkOiAke2V2ZW50X25hbWV9YCk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgICAgfVxuICAgICAgaWYgKCF0aGlzLl9saWZlY3ljbGVfY2FsbGJhY2tzLmhhcyhldmVudF9uYW1lKSkge1xuICAgICAgICB0aGlzLl9saWZlY3ljbGVfY2FsbGJhY2tzLnNldChldmVudF9uYW1lLCBbXSk7XG4gICAgICB9XG4gICAgICB0aGlzLl9saWZlY3ljbGVfY2FsbGJhY2tzLmdldChldmVudF9uYW1lKS5wdXNoKGNhbGxiYWNrKTtcbiAgICAgIGlmICh0aGlzLl9saWZlY3ljbGVfc3RhdGVzLmhhcyhldmVudF9uYW1lKSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNhbGxiYWNrKHRoaXMpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoYFtKUUhUTUxdIEVycm9yIGluICR7ZXZlbnRfbmFtZX0gY2FsbGJhY2s6YCwgZXJyb3IpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgLyoqXG4gICAgICogVHJpZ2dlciBhIGxpZmVjeWNsZSBldmVudCAtIGZpcmVzIGFsbCByZWdpc3RlcmVkIGNhbGxiYWNrc1xuICAgICAqIE1hcmtzIGV2ZW50IGFzIG9jY3VycmVkIHNvIGZ1dHVyZSAub24oKSBjYWxscyBmaXJlIGltbWVkaWF0ZWx5XG4gICAgICovXG4gICAgdHJpZ2dlcihldmVudF9uYW1lKSB7XG4gICAgICB0aGlzLl9saWZlY3ljbGVfc3RhdGVzLmFkZChldmVudF9uYW1lKTtcbiAgICAgIGNvbnN0IGNhbGxiYWNrcyA9IHRoaXMuX2xpZmVjeWNsZV9jYWxsYmFja3MuZ2V0KGV2ZW50X25hbWUpO1xuICAgICAgaWYgKGNhbGxiYWNrcykge1xuICAgICAgICBmb3IgKGNvbnN0IGNhbGxiYWNrIG9mIGNhbGxiYWNrcykge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjYWxsYmFjay5iaW5kKHRoaXMpKHRoaXMpO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKGBbSlFIVE1MXSBFcnJvciBpbiAke2V2ZW50X25hbWV9IGNhbGxiYWNrOmAsIGVycm9yKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogQ2hlY2sgaWYgYW55IGNhbGxiYWNrcyBhcmUgcmVnaXN0ZXJlZCBmb3IgYSBnaXZlbiBldmVudFxuICAgICAqIFVzZWQgdG8gZGV0ZXJtaW5lIGlmIGNsZWFudXAgbG9naWMgbmVlZHMgdG8gcnVuXG4gICAgICovXG4gICAgX29uX3JlZ2lzdGVyZWQoZXZlbnRfbmFtZSkge1xuICAgICAgY29uc3QgY2FsbGJhY2tzID0gdGhpcy5fbGlmZWN5Y2xlX2NhbGxiYWNrcy5nZXQoZXZlbnRfbmFtZSk7XG4gICAgICByZXR1cm4gISEoY2FsbGJhY2tzICYmIGNhbGxiYWNrcy5sZW5ndGggPiAwKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRmluZCBlbGVtZW50IGJ5IHNjb3BlZCBJRFxuICAgICAqXG4gICAgICogU2VhcmNoZXMgZm9yIGVsZW1lbnRzIHdpdGggaWQ9XCJsb2NhbF9pZDpUSElTX0NPTVBPTkVOVF9DSURcIlxuICAgICAqXG4gICAgICogRXhhbXBsZTpcbiAgICAgKiAgIFRlbXBsYXRlOiA8YnV0dG9uICRpZD1cInNhdmVfYnRuXCI+U2F2ZTwvYnV0dG9uPlxuICAgICAqICAgUmVuZGVyZWQ6IDxidXR0b24gaWQ9XCJzYXZlX2J0bjphYmMxMjNcIiBkYXRhLWlkPVwic2F2ZV9idG5cIj5TYXZlPC9idXR0b24+XG4gICAgICogICBBY2Nlc3M6ICAgdGhpcy4kaWQoJ3NhdmVfYnRuJykgIC8vIFJldHVybnMgalF1ZXJ5IGVsZW1lbnRcbiAgICAgKlxuICAgICAqIFBlcmZvcm1hbmNlOiBVc2VzIG5hdGl2ZSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgpIHdoZW4gY29tcG9uZW50IGlzIGluIERPTSxcbiAgICAgKiBmYWxscyBiYWNrIHRvIGpRdWVyeS5maW5kKCkgZm9yIGNvbXBvbmVudHMgbm90IHlldCBhdHRhY2hlZCB0byBET00uXG4gICAgICpcbiAgICAgKiBAcGFyYW0gbG9jYWxfaWQgVGhlIGxvY2FsIElEICh3aXRob3V0IF9jaWQgc3VmZml4KVxuICAgICAqIEByZXR1cm5zIGpRdWVyeSBlbGVtZW50IHdpdGggaWQ9XCJsb2NhbF9pZDpfY2lkXCIsIG9yIGVtcHR5IGpRdWVyeSBvYmplY3QgaWYgbm90IGZvdW5kXG4gICAgICovXG4gICAgJGlkKGxvY2FsX2lkKSB7XG4gICAgICBjb25zdCBzY29wZWRJZCA9IGAke2xvY2FsX2lkfToke3RoaXMuX2NpZH1gO1xuICAgICAgY29uc3QgZWwgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChzY29wZWRJZCk7XG4gICAgICBpZiAoZWwpIHtcbiAgICAgICAgcmV0dXJuICQoZWwpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuJC5maW5kKGAjJHskLmVzY2FwZVNlbGVjdG9yKHNjb3BlZElkKX1gKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IGNvbXBvbmVudCBpbnN0YW5jZSBieSBzY29wZWQgSURcbiAgICAgKlxuICAgICAqIENvbnZlbmllbmNlIG1ldGhvZCB0aGF0IGZpbmRzIGVsZW1lbnQgYnkgc2NvcGVkIElEIGFuZCByZXR1cm5zIHRoZSBjb21wb25lbnQgaW5zdGFuY2UuXG4gICAgICpcbiAgICAgKiBFeGFtcGxlOlxuICAgICAqICAgVGVtcGxhdGU6IDxVc2VyX0NhcmQgJGlkPVwiYWN0aXZlX3VzZXJcIiAvPlxuICAgICAqICAgQWNjZXNzOiAgIGNvbnN0IHVzZXIgPSB0aGlzLmlkKCdhY3RpdmVfdXNlcicpOyAgLy8gUmV0dXJucyBVc2VyX0NhcmQgaW5zdGFuY2VcbiAgICAgKiAgICAgICAgICAgICB1c2VyLmRhdGEubmFtZSAgLy8gQWNjZXNzIGNvbXBvbmVudCdzIGRhdGFcbiAgICAgKlxuICAgICAqIEBwYXJhbSBsb2NhbF9pZCBUaGUgbG9jYWwgSUQgKHdpdGhvdXQgX2NpZCBzdWZmaXgpXG4gICAgICogQHJldHVybnMgQ29tcG9uZW50IGluc3RhbmNlIG9yIG51bGwgaWYgbm90IGZvdW5kIG9yIG5vdCBhIGNvbXBvbmVudFxuICAgICAqL1xuICAgIGlkKGxvY2FsX2lkKSB7XG4gICAgICBjb25zdCBlbGVtZW50ID0gdGhpcy4kaWQobG9jYWxfaWQpO1xuICAgICAgY29uc3QgY29tcG9uZW50ID0gZWxlbWVudC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgIGlmICghY29tcG9uZW50ICYmIGVsZW1lbnQubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zb2xlLndhcm4oYENvbXBvbmVudCAke3RoaXMuY29uc3RydWN0b3IubmFtZX0gdHJpZWQgdG8gY2FsbCAuaWQoJyR7bG9jYWxfaWR9JykgLSAke2xvY2FsX2lkfSBleGlzdHMsIGhvd2V2ZXIsIGl0IGlzIG5vdCBhIGNvbXBvbmVudCBvciAkcmVkcmF3YWJsZS4gRGlkIHlvdSBmb3JnZXQgdG8gYWRkICRyZWRyYXdhYmxlIHRvIHRoZSB0YWc/YCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gY29tcG9uZW50IHx8IG51bGw7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgY29tcG9uZW50IHRoYXQgaW5zdGFudGlhdGVkIHRoaXMgY29tcG9uZW50IChyZW5kZXJlZCBpdCBpbiB0aGVpciB0ZW1wbGF0ZSlcbiAgICAgKiBSZXR1cm5zIG51bGwgaWYgY29tcG9uZW50IHdhcyBjcmVhdGVkIHByb2dyYW1tYXRpY2FsbHkgdmlhICQoKS5jb21wb25lbnQoKVxuICAgICAqL1xuICAgIGluc3RhbnRpYXRvcigpIHtcbiAgICAgIHJldHVybiB0aGlzLl9pbnN0YW50aWF0b3I7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEZpbmQgZGVzY2VuZGFudCBjb21wb25lbnRzIGJ5IENTUyBzZWxlY3RvclxuICAgICAqL1xuICAgIGZpbmQoc2VsZWN0b3IpIHtcbiAgICAgIGNvbnN0IGNvbXBvbmVudHMgPSBbXTtcbiAgICAgIHRoaXMuJC5maW5kKHNlbGVjdG9yKS5lYWNoKChfLCBlbCkgPT4ge1xuICAgICAgICBjb25zdCBjb21wID0gJChlbCkuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgIGlmIChjb21wIGluc3RhbmNlb2YgX0pxaHRtbF9Db21wb25lbnQpIHtcbiAgICAgICAgICBjb21wb25lbnRzLnB1c2goY29tcCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIGNvbXBvbmVudHM7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEZpbmQgY2xvc2VzdCBhbmNlc3RvciBjb21wb25lbnQgbWF0Y2hpbmcgc2VsZWN0b3JcbiAgICAgKi9cbiAgICBjbG9zZXN0KHNlbGVjdG9yKSB7XG4gICAgICBsZXQgY3VycmVudCA9IHRoaXMuJC5wYXJlbnQoKTtcbiAgICAgIHdoaWxlIChjdXJyZW50Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgaWYgKGN1cnJlbnQuaXMoc2VsZWN0b3IpKSB7XG4gICAgICAgICAgY29uc3QgY29tcCA9IGN1cnJlbnQuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgICAgaWYgKGNvbXAgaW5zdGFuY2VvZiBfSnFodG1sX0NvbXBvbmVudCkge1xuICAgICAgICAgICAgcmV0dXJuIGNvbXA7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGN1cnJlbnQgPSBjdXJyZW50LnBhcmVudCgpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvLyBTdGF0aWMgTWV0aG9kc1xuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvKipcbiAgICAgKiBHZXQgQ1NTIGNsYXNzIGhpZXJhcmNoeSBmb3IgdGhpcyBjb21wb25lbnQgdHlwZVxuICAgICAqL1xuICAgIHN0YXRpYyBnZXRfY2xhc3NfaGllcmFyY2h5KCkge1xuICAgICAgY29uc3QgY2xhc3NlcyA9IFtdO1xuICAgICAgbGV0IGN0b3IgPSB0aGlzO1xuICAgICAgd2hpbGUgKGN0b3IpIHtcbiAgICAgICAgaWYgKCFjdG9yLm5hbWUgfHwgdHlwZW9mIGN0b3IubmFtZSAhPT0gXCJzdHJpbmdcIikge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjdG9yLm5hbWUgIT09IFwiT2JqZWN0XCIgJiYgY3Rvci5uYW1lICE9PSBcIlwiKSB7XG4gICAgICAgICAgbGV0IG5vcm1hbGl6ZWROYW1lID0gY3Rvci5uYW1lO1xuICAgICAgICAgIGlmIChub3JtYWxpemVkTmFtZSA9PT0gXCJfSnFodG1sX0NvbXBvbmVudFwiIHx8IG5vcm1hbGl6ZWROYW1lID09PSBcIl9CYXNlX0pxaHRtbF9Db21wb25lbnRcIikge1xuICAgICAgICAgICAgbm9ybWFsaXplZE5hbWUgPSBcIkpxaHRtbF9Db21wb25lbnRcIjtcbiAgICAgICAgICB9XG4gICAgICAgICAgY2xhc3Nlcy5wdXNoKG5vcm1hbGl6ZWROYW1lKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBuZXh0UHJvdG8gPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YoY3Rvcik7XG4gICAgICAgIGlmICghbmV4dFByb3RvIHx8IG5leHRQcm90byA9PT0gT2JqZWN0LnByb3RvdHlwZSB8fCBuZXh0UHJvdG8uY29uc3RydWN0b3IgPT09IE9iamVjdCkge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGN0b3IgPSBuZXh0UHJvdG87XG4gICAgICB9XG4gICAgICByZXR1cm4gY2xhc3NlcztcbiAgICB9XG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIFByaXZhdGUgSW1wbGVtZW50YXRpb25cbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgX2dlbmVyYXRlX2NpZCgpIHtcbiAgICAgIHJldHVybiB1aWQoKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogRmxhdHRlbiBpbnN0cnVjdGlvbiBhcnJheSAtIGNvbnZlcnRzIFsnX2NvbnRlbnQnLCBbLi4uXV0gbWFya2VycyB0byBmbGF0IGFycmF5XG4gICAgICogUmVjdXJzaXZlbHkgZmxhdHRlbnMgbmVzdGVkIGNvbnRlbnQgZnJvbSBjb250ZW50KCkgY2FsbHNcbiAgICAgKi9cbiAgICBfZmxhdHRlbl9pbnN0cnVjdGlvbnMoaW5zdHJ1Y3Rpb25zKSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBbXTtcbiAgICAgIGZvciAoY29uc3QgaW5zdHJ1Y3Rpb24gb2YgaW5zdHJ1Y3Rpb25zKSB7XG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KGluc3RydWN0aW9uKSAmJiBpbnN0cnVjdGlvblswXSA9PT0gXCJfY29udGVudFwiICYmIEFycmF5LmlzQXJyYXkoaW5zdHJ1Y3Rpb25bMV0pKSB7XG4gICAgICAgICAgY29uc3QgY29udGVudEluc3RydWN0aW9ucyA9IHRoaXMuX2ZsYXR0ZW5faW5zdHJ1Y3Rpb25zKGluc3RydWN0aW9uWzFdKTtcbiAgICAgICAgICByZXN1bHQucHVzaCguLi5jb250ZW50SW5zdHJ1Y3Rpb25zKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXN1bHQucHVzaChpbnN0cnVjdGlvbik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIF9hcHBseV9jc3NfY2xhc3NlcygpIHtcbiAgICAgIGNvbnN0IGhpZXJhcmNoeSA9IHRoaXMuY29uc3RydWN0b3IuZ2V0X2NsYXNzX2hpZXJhcmNoeSgpO1xuICAgICAgY29uc3QgY2xhc3Nlc1RvQWRkID0gWy4uLmhpZXJhcmNoeV07XG4gICAgICBpZiAodGhpcy5hcmdzLl9jb21wb25lbnRfbmFtZSAmJiB0aGlzLmFyZ3MuX2NvbXBvbmVudF9uYW1lICE9PSB0aGlzLmNvbnN0cnVjdG9yLm5hbWUpIHtcbiAgICAgICAgY2xhc3Nlc1RvQWRkLnVuc2hpZnQodGhpcy5hcmdzLl9jb21wb25lbnRfbmFtZSk7XG4gICAgICB9XG4gICAgICBjb25zdCBwdWJsaWNDbGFzc2VzID0gY2xhc3Nlc1RvQWRkLmZpbHRlcigoY2xhc3NOYW1lKSA9PiB7XG4gICAgICAgIGlmICghY2xhc3NOYW1lIHx8IHR5cGVvZiBjbGFzc05hbWUgIT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICBjb25zb2xlLndhcm4oXCJbSlFIVE1MXSBGaWx0ZXJlZCBvdXQgaW52YWxpZCBjbGFzcyBuYW1lOlwiLCBjbGFzc05hbWUpO1xuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gIWNsYXNzTmFtZS5zdGFydHNXaXRoKFwiX1wiKTtcbiAgICAgIH0pO1xuICAgICAgaWYgKHB1YmxpY0NsYXNzZXMubGVuZ3RoID4gMCkge1xuICAgICAgICB0aGlzLiQuYWRkQ2xhc3MocHVibGljQ2xhc3Nlcy5qb2luKFwiIFwiKSk7XG4gICAgICB9XG4gICAgfVxuICAgIF9hcHBseV9kZWZhdWx0X2F0dHJpYnV0ZXMoKSB7XG4gICAgICBsZXQgdGVtcGxhdGU7XG4gICAgICBpZiAodGhpcy5hcmdzLl9jb21wb25lbnRfbmFtZSkge1xuICAgICAgICB0ZW1wbGF0ZSA9IGdldF90ZW1wbGF0ZSh0aGlzLmFyZ3MuX2NvbXBvbmVudF9uYW1lKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRlbXBsYXRlID0gZ2V0X3RlbXBsYXRlX2J5X2NsYXNzKHRoaXMuY29uc3RydWN0b3IpO1xuICAgICAgfVxuICAgICAgaWYgKHRlbXBsYXRlICYmIHRlbXBsYXRlLmRlZmF1bHRBdHRyaWJ1dGVzKSB7XG4gICAgICAgIGNvbnN0IGRlZmluZUF0dHJzID0geyAuLi50ZW1wbGF0ZS5kZWZhdWx0QXR0cmlidXRlcyB9O1xuICAgICAgICBkZWxldGUgZGVmaW5lQXR0cnMudGFnO1xuICAgICAgICBpZiAod2luZG93LmpxaHRtbD8uZGVidWc/LmVuYWJsZWQpIHtcbiAgICAgICAgICBjb25zdCBjb21wb25lbnROYW1lID0gdGVtcGxhdGUubmFtZSB8fCB0aGlzLmFyZ3MuX2NvbXBvbmVudF9uYW1lIHx8IHRoaXMuY29uc3RydWN0b3IubmFtZTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW0NvbXBvbmVudF0gQXBwbHlpbmcgZGVmYXVsdEF0dHJpYnV0ZXMgZm9yICR7Y29tcG9uZW50TmFtZX06YCwgZGVmaW5lQXR0cnMpO1xuICAgICAgICB9XG4gICAgICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGRlZmluZUF0dHJzKSkge1xuICAgICAgICAgIGlmIChrZXkgPT09IFwiY2xhc3NcIikge1xuICAgICAgICAgICAgY29uc3QgZXhpc3RpbmdDbGFzc2VzID0gdGhpcy4kLmF0dHIoXCJjbGFzc1wiKTtcbiAgICAgICAgICAgIGlmIChleGlzdGluZ0NsYXNzZXMpIHtcbiAgICAgICAgICAgICAgY29uc3QgZXhpc3RpbmcgPSBleGlzdGluZ0NsYXNzZXMuc3BsaXQoL1xccysvKS5maWx0ZXIoKGMpID0+IGMpO1xuICAgICAgICAgICAgICBjb25zdCBuZXdDbGFzc2VzID0gU3RyaW5nKHZhbHVlKS5zcGxpdCgvXFxzKy8pLmZpbHRlcigoYykgPT4gYyk7XG4gICAgICAgICAgICAgIGZvciAoY29uc3QgbmV3Q2xhc3Mgb2YgbmV3Q2xhc3Nlcykge1xuICAgICAgICAgICAgICAgIGlmICghZXhpc3RpbmcuaW5jbHVkZXMobmV3Q2xhc3MpKSB7XG4gICAgICAgICAgICAgICAgICBleGlzdGluZy5wdXNoKG5ld0NsYXNzKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgdGhpcy4kLmF0dHIoXCJjbGFzc1wiLCBleGlzdGluZy5qb2luKFwiIFwiKSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICB0aGlzLiQuYXR0cihcImNsYXNzXCIsIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2UgaWYgKGtleSA9PT0gXCJzdHlsZVwiKSB7XG4gICAgICAgICAgICBjb25zdCBleGlzdGluZ1N0eWxlID0gdGhpcy4kLmF0dHIoXCJzdHlsZVwiKTtcbiAgICAgICAgICAgIGlmIChleGlzdGluZ1N0eWxlKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGV4aXN0aW5nUnVsZXMgPSAvKiBAX19QVVJFX18gKi8gbmV3IE1hcCgpO1xuICAgICAgICAgICAgICBleGlzdGluZ1N0eWxlLnNwbGl0KFwiO1wiKS5mb3JFYWNoKChydWxlKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgW3Byb3AsIHZhbF0gPSBydWxlLnNwbGl0KFwiOlwiKS5tYXAoKHMpID0+IHMudHJpbSgpKTtcbiAgICAgICAgICAgICAgICBpZiAocHJvcCAmJiB2YWwpXG4gICAgICAgICAgICAgICAgICBleGlzdGluZ1J1bGVzLnNldChwcm9wLCB2YWwpO1xuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgU3RyaW5nKHZhbHVlKS5zcGxpdChcIjtcIikuZm9yRWFjaCgocnVsZSkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IFtwcm9wLCB2YWxdID0gcnVsZS5zcGxpdChcIjpcIikubWFwKChzKSA9PiBzLnRyaW0oKSk7XG4gICAgICAgICAgICAgICAgaWYgKHByb3AgJiYgdmFsKVxuICAgICAgICAgICAgICAgICAgZXhpc3RpbmdSdWxlcy5zZXQocHJvcCwgdmFsKTtcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgIGNvbnN0IG1lcmdlZCA9IEFycmF5LmZyb20oZXhpc3RpbmdSdWxlcy5lbnRyaWVzKCkpLm1hcCgoW3Byb3AsIHZhbF0pID0+IGAke3Byb3B9OiAke3ZhbH1gKS5qb2luKFwiOyBcIik7XG4gICAgICAgICAgICAgIHRoaXMuJC5hdHRyKFwic3R5bGVcIiwgbWVyZ2VkKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHRoaXMuJC5hdHRyKFwic3R5bGVcIiwgdmFsdWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSBpZiAoa2V5LnN0YXJ0c1dpdGgoXCIkXCIpIHx8IGtleS5zdGFydHNXaXRoKFwiZGF0YS1cIikpIHtcbiAgICAgICAgICAgIGNvbnN0IGRhdGFLZXkgPSBrZXkuc3RhcnRzV2l0aChcIiRcIikgPyBrZXkuc3Vic3RyaW5nKDEpIDoga2V5LnN0YXJ0c1dpdGgoXCJkYXRhLVwiKSA/IGtleS5zdWJzdHJpbmcoNSkgOiBrZXk7XG4gICAgICAgICAgICBpZiAoIShkYXRhS2V5IGluIHRoaXMuYXJncykpIHtcbiAgICAgICAgICAgICAgdGhpcy5hcmdzW2RhdGFLZXldID0gdmFsdWU7XG4gICAgICAgICAgICAgIHRoaXMuJC5kYXRhKGRhdGFLZXksIHZhbHVlKTtcbiAgICAgICAgICAgICAgdGhpcy4kLmF0dHIoa2V5LnN0YXJ0c1dpdGgoXCIkXCIpID8gYGRhdGEtJHtkYXRhS2V5fWAgOiBrZXksIFN0cmluZyh2YWx1ZSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBpZiAoIXRoaXMuJC5hdHRyKGtleSkpIHtcbiAgICAgICAgICAgICAgdGhpcy4kLmF0dHIoa2V5LCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIF9zZXRfYXR0cmlidXRlcygpIHtcbiAgICAgIHRoaXMuJC5hdHRyKFwiZGF0YS1jaWRcIiwgdGhpcy5fY2lkKTtcbiAgICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8udmVyYm9zZSkge1xuICAgICAgICB0aGlzLiQuYXR0cihcImRhdGEtX2xpZmVjeWNsZS1zdGF0ZVwiLCB0aGlzLl9yZWFkeV9zdGF0ZS50b1N0cmluZygpKTtcbiAgICAgIH1cbiAgICB9XG4gICAgX3VwZGF0ZV9kZWJ1Z19hdHRycygpIHtcbiAgICAgIGlmICh3aW5kb3cuanFodG1sPy5kZWJ1Zz8udmVyYm9zZSkge1xuICAgICAgICB0aGlzLiQuYXR0cihcImRhdGEtX2xpZmVjeWNsZS1zdGF0ZVwiLCB0aGlzLl9yZWFkeV9zdGF0ZS50b1N0cmluZygpKTtcbiAgICAgIH1cbiAgICB9XG4gICAgX2ZpbmRfZG9tX3BhcmVudCgpIHtcbiAgICAgIGxldCBjdXJyZW50ID0gdGhpcy4kLnBhcmVudCgpO1xuICAgICAgd2hpbGUgKGN1cnJlbnQubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zdCBwYXJlbnQgPSBjdXJyZW50LmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICBpZiAocGFyZW50IGluc3RhbmNlb2YgX0pxaHRtbF9Db21wb25lbnQpIHtcbiAgICAgICAgICB0aGlzLl9kb21fcGFyZW50ID0gcGFyZW50O1xuICAgICAgICAgIHBhcmVudC5fZG9tX2NoaWxkcmVuLmFkZCh0aGlzKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBjdXJyZW50ID0gY3VycmVudC5wYXJlbnQoKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogR2V0IERPTSBjaGlsZHJlbiAoY29tcG9uZW50cyBpbiBET00gc3VidHJlZSlcbiAgICAgKiBVc2VzIGZhc3QgX2RvbV9jaGlsZHJlbiByZWdpc3RyeSB3aGVuIHBvc3NpYmxlLCBmYWxscyBiYWNrIHRvIERPTSB0cmF2ZXJzYWwgZm9yIG9mZi1ET00gY29tcG9uZW50c1xuICAgICAqIEBwcml2YXRlIC0gVXNlZCBpbnRlcm5hbGx5IGZvciBsaWZlY3ljbGUgY29vcmRpbmF0aW9uXG4gICAgICovXG4gICAgX2dldF9kb21fY2hpbGRyZW4oKSB7XG4gICAgICBpZiAodGhpcy5fdXNlX2RvbV9mYWxsYmFjaykge1xuICAgICAgICBjb25zdCBkaXJlY3RDaGlsZHJlbiA9IFtdO1xuICAgICAgICB0aGlzLiQuZmluZChcIi5KcWh0bWxfQ29tcG9uZW50XCIpLmVhY2goKF8sIGVsKSA9PiB7XG4gICAgICAgICAgY29uc3QgJGVsID0gJChlbCk7XG4gICAgICAgICAgY29uc3QgY29tcCA9ICRlbC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgICBpZiAoY29tcCBpbnN0YW5jZW9mIF9KcWh0bWxfQ29tcG9uZW50KSB7XG4gICAgICAgICAgICBjb25zdCBjbG9zZXN0UGFyZW50ID0gJGVsLnBhcmVudCgpLmNsb3Nlc3QoXCIuSnFodG1sX0NvbXBvbmVudFwiKTtcbiAgICAgICAgICAgIGlmIChjbG9zZXN0UGFyZW50Lmxlbmd0aCA9PT0gMCB8fCBjbG9zZXN0UGFyZW50LmRhdGEoXCJfY29tcG9uZW50XCIpID09PSB0aGlzKSB7XG4gICAgICAgICAgICAgIGRpcmVjdENoaWxkcmVuLnB1c2goY29tcCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIGRpcmVjdENoaWxkcmVuO1xuICAgICAgfVxuICAgICAgY29uc3QgY2hpbGRyZW4gPSBBcnJheS5mcm9tKHRoaXMuX2RvbV9jaGlsZHJlbik7XG4gICAgICByZXR1cm4gY2hpbGRyZW4uZmlsdGVyKChjaGlsZCkgPT4ge1xuICAgICAgICByZXR1cm4gJC5jb250YWlucyhkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQsIGNoaWxkLiRbMF0pO1xuICAgICAgfSk7XG4gICAgfVxuICAgIF9sb2dfbGlmZWN5Y2xlKHBoYXNlLCBzdGF0dXMpIHtcbiAgICAgIGxvZ0xpZmVjeWNsZSh0aGlzLCBwaGFzZSwgc3RhdHVzKTtcbiAgICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmIHdpbmRvdy5KUUhUTUxfREVCVUcpIHtcbiAgICAgICAgd2luZG93LkpRSFRNTF9ERUJVRy5sb2codGhpcy5jb21wb25lbnRfbmFtZSgpLCBwaGFzZSwgc3RhdHVzLCB7XG4gICAgICAgICAgY2lkOiB0aGlzLl9jaWQsXG4gICAgICAgICAgcmVhZHlfc3RhdGU6IHRoaXMuX3JlYWR5X3N0YXRlLFxuICAgICAgICAgIGFyZ3M6IHRoaXMuYXJnc1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG4gICAgX2xvZ19kZWJ1ZyhhY3Rpb24sIC4uLmFyZ3MpIHtcbiAgICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmIHdpbmRvdy5KUUhUTUxfREVCVUcpIHtcbiAgICAgICAgd2luZG93LkpRSFRNTF9ERUJVRy5sb2codGhpcy5jb21wb25lbnRfbmFtZSgpLCBcImRlYnVnXCIsIGAke2FjdGlvbn06ICR7YXJncy5tYXAoKGEpID0+IEpTT04uc3RyaW5naWZ5KGEpKS5qb2luKFwiLCBcIil9YCk7XG4gICAgICB9XG4gICAgfVxuICB9O1xuICBhc3luYyBmdW5jdGlvbiBwcm9jZXNzX3Nsb3RfaW5oZXJpdGFuY2UoY29tcG9uZW50LCBjaGlsZFNsb3RzKSB7XG4gICAgbGV0IGN1cnJlbnRDbGFzcyA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihjb21wb25lbnQuY29uc3RydWN0b3IpO1xuICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSBXYWxraW5nIHByb3RvdHlwZSBjaGFpbiBmb3IgJHtjb21wb25lbnQuY29uc3RydWN0b3IubmFtZX1gKTtcbiAgICB3aGlsZSAoY3VycmVudENsYXNzICYmIGN1cnJlbnRDbGFzcyAhPT0gSnFodG1sX0NvbXBvbmVudCAmJiBjdXJyZW50Q2xhc3MubmFtZSAhPT0gXCJPYmplY3RcIikge1xuICAgICAgY29uc3QgY2xhc3NOYW1lID0gY3VycmVudENsYXNzLm5hbWU7XG4gICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gICBDaGVja2luZyBwYXJlbnQgY2xhc3M6ICR7Y2xhc3NOYW1lfWApO1xuICAgICAgaWYgKGNsYXNzTmFtZSA9PT0gXCJfSnFodG1sX0NvbXBvbmVudFwiIHx8IGNsYXNzTmFtZSA9PT0gXCJfQmFzZV9KcWh0bWxfQ29tcG9uZW50XCIpIHtcbiAgICAgICAgY3VycmVudENsYXNzID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKGN1cnJlbnRDbGFzcyk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcGFyZW50VGVtcGxhdGUgPSBnZXRfdGVtcGxhdGUoY2xhc3NOYW1lKTtcbiAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdICAgVGVtcGxhdGUgZm91bmQgZm9yICR7Y2xhc3NOYW1lfTpgLCBwYXJlbnRUZW1wbGF0ZSA/IHBhcmVudFRlbXBsYXRlLm5hbWUgOiBcIm51bGxcIik7XG4gICAgICAgIGlmIChwYXJlbnRUZW1wbGF0ZSAmJiBwYXJlbnRUZW1wbGF0ZS5uYW1lICE9PSBcIkpxaHRtbF9Db21wb25lbnRcIikge1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbSlFIVE1MXSAgIEludm9raW5nIHBhcmVudCB0ZW1wbGF0ZSAke2NsYXNzTmFtZX1gKTtcbiAgICAgICAgICBjb25zdCBbcGFyZW50SW5zdHJ1Y3Rpb25zLCBwYXJlbnRDb250ZXh0XSA9IHBhcmVudFRlbXBsYXRlLnJlbmRlci5jYWxsKFxuICAgICAgICAgICAgY29tcG9uZW50LFxuICAgICAgICAgICAgY29tcG9uZW50LmRhdGEsXG4gICAgICAgICAgICBjb21wb25lbnQuYXJncyxcbiAgICAgICAgICAgIGNoaWxkU2xvdHNcbiAgICAgICAgICAgIC8vIFBhc3MgY2hpbGQgc2xvdHMgYXMgY29udGVudCBwYXJhbWV0ZXJcbiAgICAgICAgICApO1xuICAgICAgICAgIGlmIChwYXJlbnRJbnN0cnVjdGlvbnMgJiYgdHlwZW9mIHBhcmVudEluc3RydWN0aW9ucyA9PT0gXCJvYmplY3RcIiAmJiBwYXJlbnRJbnN0cnVjdGlvbnMuX3Nsb3RzKSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gICBQYXJlbnQgYWxzbyBzbG90LW9ubHksIHJlY3Vyc2luZ2ApO1xuICAgICAgICAgICAgcmV0dXJuIGF3YWl0IHByb2Nlc3Nfc2xvdF9pbmhlcml0YW5jZShjb21wb25lbnQsIHBhcmVudEluc3RydWN0aW9ucy5fc2xvdHMpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zb2xlLmxvZyhgW0pRSFRNTF0gICBQYXJlbnQgcmV0dXJuZWQgaW5zdHJ1Y3Rpb25zLCBpbmhlcml0YW5jZSBjb21wbGV0ZWApO1xuICAgICAgICAgIHJldHVybiBbcGFyZW50SW5zdHJ1Y3Rpb25zLCBwYXJlbnRDb250ZXh0XTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBFcnJvciBsb29raW5nIHVwIHBhcmVudCB0ZW1wbGF0ZSBmb3IgJHtjbGFzc05hbWV9OmAsIGVycm9yKTtcbiAgICAgIH1cbiAgICAgIGN1cnJlbnRDbGFzcyA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihjdXJyZW50Q2xhc3MpO1xuICAgIH1cbiAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIE5vIHBhcmVudCB0ZW1wbGF0ZSBmb3VuZCBhZnRlciB3YWxraW5nIGNoYWluYCk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgYXN5bmMgZnVuY3Rpb24gcmVuZGVyX3RlbXBsYXRlKGNvbXBvbmVudCwgdGVtcGxhdGVfZm4pIHtcbiAgICBsZXQgcmVuZGVyX2ZuID0gdGVtcGxhdGVfZm47XG4gICAgaWYgKCFyZW5kZXJfZm4pIHtcbiAgICAgIGNvbnN0IHRlbXBsYXRlX2RlZiA9IGdldF90ZW1wbGF0ZV9ieV9jbGFzcyhjb21wb25lbnQuY29uc3RydWN0b3IpO1xuICAgICAgcmVuZGVyX2ZuID0gdGVtcGxhdGVfZGVmLnJlbmRlcjtcbiAgICB9XG4gICAgaWYgKCFyZW5kZXJfZm4pIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29tcG9uZW50LiQuZW1wdHkoKTtcbiAgICBjb25zdCBkZWZhdWx0Q29udGVudCA9ICgpID0+IFwiXCI7XG4gICAgbGV0IFtpbnN0cnVjdGlvbnMsIGNvbnRleHRdID0gcmVuZGVyX2ZuLmNhbGwoXG4gICAgICBjb21wb25lbnQsXG4gICAgICBjb21wb25lbnQuZGF0YSxcbiAgICAgIGNvbXBvbmVudC5hcmdzLFxuICAgICAgZGVmYXVsdENvbnRlbnRcbiAgICAgIC8vIERlZmF1bHQgY29udGVudCBmdW5jdGlvbiB0aGF0IHJldHVybnMgZW1wdHkgc3RyaW5nXG4gICAgKTtcbiAgICBpZiAoaW5zdHJ1Y3Rpb25zICYmIHR5cGVvZiBpbnN0cnVjdGlvbnMgPT09IFwib2JqZWN0XCIgJiYgaW5zdHJ1Y3Rpb25zLl9zbG90cykge1xuICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdIFNsb3Qtb25seSB0ZW1wbGF0ZSBkZXRlY3RlZCBmb3IgJHtjb21wb25lbnQuY29uc3RydWN0b3IubmFtZX0sIGludm9raW5nIGluaGVyaXRhbmNlYCk7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBwcm9jZXNzX3Nsb3RfaW5oZXJpdGFuY2UoY29tcG9uZW50LCBpbnN0cnVjdGlvbnMuX3Nsb3RzKTtcbiAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgY29uc29sZS5sb2coYFtKUUhUTUxdIFBhcmVudCB0ZW1wbGF0ZSBmb3VuZCwgdXNpbmcgcGFyZW50IGluc3RydWN0aW9uc2ApO1xuICAgICAgICBpbnN0cnVjdGlvbnMgPSByZXN1bHRbMF07XG4gICAgICAgIGNvbnRleHQgPSByZXN1bHRbMV07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLndhcm4oYFtKUUhUTUxdIE5vIHBhcmVudCB0ZW1wbGF0ZSBmb3VuZCBmb3IgJHtjb21wb25lbnQuY29uc3RydWN0b3IubmFtZX0sIHJlbmRlcmluZyBlbXB0eWApO1xuICAgICAgICBpbnN0cnVjdGlvbnMgPSBbXTtcbiAgICAgIH1cbiAgICB9XG4gICAgYXdhaXQgcHJvY2Vzc19pbnN0cnVjdGlvbnMoaW5zdHJ1Y3Rpb25zLCBjb21wb25lbnQuJCwgY29tcG9uZW50KTtcbiAgICBhd2FpdCBwcm9jZXNzX2JpbmRpbmdzKGNvbXBvbmVudCk7XG4gICAgYXdhaXQgYXR0YWNoX2V2ZW50X2hhbmRsZXJzKGNvbXBvbmVudCk7XG4gIH1cbiAgYXN5bmMgZnVuY3Rpb24gcHJvY2Vzc19iaW5kaW5ncyhjb21wb25lbnQpIHtcbiAgICBjb21wb25lbnQuJC5maW5kKFwiW2RhdGEtYmluZC1wcm9wXSwgW2RhdGEtYmluZC12YWx1ZV0sIFtkYXRhLWJpbmQtdGV4dF0sIFtkYXRhLWJpbmQtaHRtbF0sIFtkYXRhLWJpbmQtY2xhc3NdLCBbZGF0YS1iaW5kLXN0eWxlXVwiKS5lYWNoKChfLCBlbGVtZW50KSA9PiB7XG4gICAgICBjb25zdCBlbCA9ICQoZWxlbWVudCk7XG4gICAgICBjb25zdCBhdHRycyA9IGVsZW1lbnQuYXR0cmlidXRlcztcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYXR0cnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgYXR0ciA9IGF0dHJzW2ldO1xuICAgICAgICBpZiAoYXR0ci5uYW1lLnN0YXJ0c1dpdGgoXCJkYXRhLWJpbmQtXCIpKSB7XG4gICAgICAgICAgY29uc3QgYmluZGluZ190eXBlID0gYXR0ci5uYW1lLnN1YnN0cmluZygxMCk7XG4gICAgICAgICAgY29uc3QgZXhwcmVzc2lvbiA9IGF0dHIudmFsdWU7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHZhbHVlID0gZXZhbHVhdGVfZXhwcmVzc2lvbihleHByZXNzaW9uLCBjb21wb25lbnQpO1xuICAgICAgICAgICAgc3dpdGNoIChiaW5kaW5nX3R5cGUpIHtcbiAgICAgICAgICAgICAgY2FzZSBcInByb3BcIjpcbiAgICAgICAgICAgICAgICBjb25zdCBwcm9wX25hbWUgPSBlbC5hdHRyKFwiZGF0YS1iaW5kLXByb3AtbmFtZVwiKSB8fCBcInZhbHVlXCI7XG4gICAgICAgICAgICAgICAgZWwucHJvcChwcm9wX25hbWUsIHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgY2FzZSBcInZhbHVlXCI6XG4gICAgICAgICAgICAgICAgZWwudmFsKHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgY2FzZSBcInRleHRcIjpcbiAgICAgICAgICAgICAgICBlbC50ZXh0KHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgY2FzZSBcImh0bWxcIjpcbiAgICAgICAgICAgICAgICBlbC5odG1sKHZhbHVlKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgY2FzZSBcImNsYXNzXCI6XG4gICAgICAgICAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJvYmplY3RcIikge1xuICAgICAgICAgICAgICAgICAgT2JqZWN0LmVudHJpZXModmFsdWUpLmZvckVhY2goKFtjbGFzc05hbWUsIGVuYWJsZWRdKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGVsLnRvZ2dsZUNsYXNzKGNsYXNzTmFtZSwgISFlbmFibGVkKTtcbiAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICBlbC5hZGRDbGFzcyhTdHJpbmcodmFsdWUpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGNhc2UgXCJzdHlsZVwiOlxuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgdmFsdWUgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgICAgICAgICAgICAgIGVsLmNzcyh2YWx1ZSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgIGVsLmF0dHIoXCJzdHlsZVwiLCBTdHJpbmcodmFsdWUpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgZWwuYXR0cihiaW5kaW5nX3R5cGUsIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihgRXJyb3IgZXZhbHVhdGluZyBiaW5kaW5nIFwiJHtleHByZXNzaW9ufVwiOmAsIGVycm9yKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuICBhc3luYyBmdW5jdGlvbiBhdHRhY2hfZXZlbnRfaGFuZGxlcnMoY29tcG9uZW50KSB7XG4gICAgY29tcG9uZW50LiQuZmluZChcIltkYXRhLW9uLWNsaWNrXSwgW2RhdGEtb24tY2hhbmdlXSwgW2RhdGEtb24tc3VibWl0XSwgW2RhdGEtb24ta2V5dXBdLCBbZGF0YS1vbi1rZXlkb3duXSwgW2RhdGEtb24tZm9jdXNdLCBbZGF0YS1vbi1ibHVyXVwiKS5lYWNoKChfLCBlbGVtZW50KSA9PiB7XG4gICAgICBjb25zdCBlbCA9ICQoZWxlbWVudCk7XG4gICAgICBjb25zdCBhdHRycyA9IGVsZW1lbnQuYXR0cmlidXRlcztcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYXR0cnMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgYXR0ciA9IGF0dHJzW2ldO1xuICAgICAgICBpZiAoYXR0ci5uYW1lLnN0YXJ0c1dpdGgoXCJkYXRhLW9uLVwiKSkge1xuICAgICAgICAgIGNvbnN0IGV2ZW50X25hbWUgPSBhdHRyLm5hbWUuc3Vic3RyaW5nKDgpO1xuICAgICAgICAgIGNvbnN0IGhhbmRsZXJfZXhwciA9IGF0dHIudmFsdWU7XG4gICAgICAgICAgZWwucmVtb3ZlQXR0cihhdHRyLm5hbWUpO1xuICAgICAgICAgIGVsLm9uKGV2ZW50X25hbWUsIGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBjb25zdCBoYW5kbGVyID0gZXZhbHVhdGVfaGFuZGxlcihoYW5kbGVyX2V4cHIsIGNvbXBvbmVudCk7XG4gICAgICAgICAgICAgIGlmICh0eXBlb2YgaGFuZGxlciA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgICAgICAgaGFuZGxlci5jYWxsKGNvbXBvbmVudCwgZXZlbnQpO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGV2YWx1YXRlX2V4cHJlc3Npb24oaGFuZGxlcl9leHByLCBjb21wb25lbnQsIHsgJGV2ZW50OiBldmVudCB9KTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihgRXJyb3IgaW4gJHtldmVudF9uYW1lfSBoYW5kbGVyIFwiJHtoYW5kbGVyX2V4cHJ9XCI6YCwgZXJyb3IpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgZnVuY3Rpb24gZXZhbHVhdGVfZXhwcmVzc2lvbihleHByZXNzaW9uLCBjb21wb25lbnQsIGxvY2FscyA9IHt9KSB7XG4gICAgY29uc3QgY29udGV4dCA9IHtcbiAgICAgIC8vIENvbXBvbmVudCBwcm9wZXJ0aWVzXG4gICAgICBkYXRhOiBjb21wb25lbnQuZGF0YSxcbiAgICAgIGFyZ3M6IGNvbXBvbmVudC5hcmdzLFxuICAgICAgJDogY29tcG9uZW50LiQsXG4gICAgICAvLyBDb21wb25lbnQgbWV0aG9kc1xuICAgICAgZW1pdDogY29tcG9uZW50LmVtaXQuYmluZChjb21wb25lbnQpLFxuICAgICAgJGlkOiBjb21wb25lbnQuJGlkLmJpbmQoY29tcG9uZW50KSxcbiAgICAgIC8vIExvY2FscyAobGlrZSAkZXZlbnQpXG4gICAgICAuLi5sb2NhbHNcbiAgICB9O1xuICAgIGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyhjb250ZXh0KTtcbiAgICBjb25zdCB2YWx1ZXMgPSBPYmplY3QudmFsdWVzKGNvbnRleHQpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBmbiA9IG5ldyBGdW5jdGlvbiguLi5rZXlzLCBgcmV0dXJuICgke2V4cHJlc3Npb259KWApO1xuICAgICAgcmV0dXJuIGZuKC4uLnZhbHVlcyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYEludmFsaWQgZXhwcmVzc2lvbjogJHtleHByZXNzaW9ufWAsIGVycm9yKTtcbiAgICAgIHJldHVybiB2b2lkIDA7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIGV2YWx1YXRlX2hhbmRsZXIoZXhwcmVzc2lvbiwgY29tcG9uZW50KSB7XG4gICAgaWYgKGV4cHJlc3Npb24gaW4gY29tcG9uZW50ICYmIHR5cGVvZiBjb21wb25lbnRbZXhwcmVzc2lvbl0gPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgcmV0dXJuIGNvbXBvbmVudFtleHByZXNzaW9uXTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBuZXcgRnVuY3Rpb24oXCIkZXZlbnRcIiwgYFxuICAgICAgY29uc3QgeyBkYXRhLCBhcmdzLCAkLCBlbWl0LCAkaWQgfSA9IHRoaXM7XG4gICAgICAke2V4cHJlc3Npb259XG4gICAgYCkuYmluZChjb21wb25lbnQpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKGBJbnZhbGlkIGhhbmRsZXI6ICR7ZXhwcmVzc2lvbn1gLCBlcnJvcik7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gZXNjYXBlX2h0bWwoc3RyKSB7XG4gICAgY29uc3QgZGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcbiAgICBkaXYudGV4dENvbnRlbnQgPSBzdHI7XG4gICAgcmV0dXJuIGRpdi5pbm5lckhUTUw7XG4gIH1cbiAgZnVuY3Rpb24gZ2V0SlF1ZXJ5KCkge1xuICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmIHdpbmRvdy4kKSB7XG4gICAgICByZXR1cm4gd2luZG93LiQ7XG4gICAgfVxuICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmIHdpbmRvdy5qUXVlcnkpIHtcbiAgICAgIHJldHVybiB3aW5kb3cualF1ZXJ5O1xuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZBVEFMOiBqUXVlcnkgaXMgbm90IGRlZmluZWQuIGpRdWVyeSBtdXN0IGJlIGxvYWRlZCBiZWZvcmUgdXNpbmcgSlFIVE1MLiBBZGQgPHNjcmlwdCBzcmM9XCJodHRwczovL2NvZGUuanF1ZXJ5LmNvbS9qcXVlcnktMy43LjEubWluLmpzXCI+PFxcL3NjcmlwdD4gYmVmb3JlIGxvYWRpbmcgSlFIVE1MLicpO1xuICB9XG4gIGZ1bmN0aW9uIGdldEpxaHRtbCgpIHtcbiAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB3aW5kb3cuanFodG1sKSB7XG4gICAgICByZXR1cm4gd2luZG93LmpxaHRtbDtcbiAgICB9XG4gICAgaWYgKHR5cGVvZiBnbG9iYWxUaGlzICE9PSBcInVuZGVmaW5lZFwiICYmIGdsb2JhbFRoaXMuanFodG1sKSB7XG4gICAgICByZXR1cm4gZ2xvYmFsVGhpcy5qcWh0bWw7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihcIkZBVEFMOiB3aW5kb3cuanFodG1sIGlzIG5vdCBkZWZpbmVkLiBUaGUgSlFIVE1MIHJ1bnRpbWUgbXVzdCBiZSBsb2FkZWQgYmVmb3JlIHVzaW5nIEpRSFRNTCBjb21wb25lbnRzLiBFbnN1cmUgQGpxaHRtbC9jb3JlIGlzIGltcG9ydGVkIGFuZCBpbml0aWFsaXplZCBiZWZvcmUgYXR0ZW1wdGluZyB0byB1c2UgZGVidWcgZmVhdHVyZXMuXCIpO1xuICB9XG4gIHZhciBEZWJ1Z092ZXJsYXkgPSBjbGFzcyBfRGVidWdPdmVybGF5IHtcbiAgICBjb25zdHJ1Y3RvcihvcHRpb25zID0ge30pIHtcbiAgICAgIHRoaXMuJGNvbnRhaW5lciA9IG51bGw7XG4gICAgICB0aGlzLiRzdGF0dXNJbmRpY2F0b3IgPSBudWxsO1xuICAgICAgdGhpcy4kID0gZ2V0SlF1ZXJ5KCk7XG4gICAgICBpZiAoIXRoaXMuJCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJqUXVlcnkgaXMgcmVxdWlyZWQgZm9yIERlYnVnT3ZlcmxheVwiKTtcbiAgICAgIH1cbiAgICAgIHRoaXMub3B0aW9ucyA9IHtcbiAgICAgICAgcG9zaXRpb246IFwiYm90dG9tXCIsXG4gICAgICAgIHRoZW1lOiBcImRhcmtcIixcbiAgICAgICAgY29tcGFjdDogZmFsc2UsXG4gICAgICAgIHNob3dTdGF0dXM6IHRydWUsXG4gICAgICAgIGF1dG9IaWRlOiBmYWxzZSxcbiAgICAgICAgLi4ub3B0aW9uc1xuICAgICAgfTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU3RhdGljIG1ldGhvZCB0byBzaG93IGRlYnVnIG92ZXJsYXkgKHNpbmdsZXRvbiBwYXR0ZXJuKVxuICAgICAqL1xuICAgIHN0YXRpYyBzaG93KG9wdGlvbnMpIHtcbiAgICAgIGlmICghX0RlYnVnT3ZlcmxheS5pbnN0YW5jZSkge1xuICAgICAgICBfRGVidWdPdmVybGF5Lmluc3RhbmNlID0gbmV3IF9EZWJ1Z092ZXJsYXkob3B0aW9ucyk7XG4gICAgICB9XG4gICAgICBfRGVidWdPdmVybGF5Lmluc3RhbmNlLmRpc3BsYXkoKTtcbiAgICAgIHJldHVybiBfRGVidWdPdmVybGF5Lmluc3RhbmNlO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBTdGF0aWMgbWV0aG9kIHRvIGhpZGUgZGVidWcgb3ZlcmxheVxuICAgICAqL1xuICAgIHN0YXRpYyBoaWRlKCkge1xuICAgICAgaWYgKF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UpIHtcbiAgICAgICAgX0RlYnVnT3ZlcmxheS5pbnN0YW5jZS5oaWRlKCk7XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFN0YXRpYyBtZXRob2QgdG8gdG9nZ2xlIGRlYnVnIG92ZXJsYXkgdmlzaWJpbGl0eVxuICAgICAqL1xuICAgIHN0YXRpYyB0b2dnbGUoKSB7XG4gICAgICBpZiAoX0RlYnVnT3ZlcmxheS5pbnN0YW5jZSAmJiBfRGVidWdPdmVybGF5Lmluc3RhbmNlLiRjb250YWluZXIpIHtcbiAgICAgICAgaWYgKF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UuJGNvbnRhaW5lci5pcyhcIjp2aXNpYmxlXCIpKSB7XG4gICAgICAgICAgX0RlYnVnT3ZlcmxheS5oaWRlKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgX0RlYnVnT3ZlcmxheS5pbnN0YW5jZS5kaXNwbGF5KCk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIF9EZWJ1Z092ZXJsYXkuc2hvdygpO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBTdGF0aWMgbWV0aG9kIHRvIGRlc3Ryb3kgZGVidWcgb3ZlcmxheVxuICAgICAqL1xuICAgIHN0YXRpYyBkZXN0cm95KCkge1xuICAgICAgaWYgKF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UpIHtcbiAgICAgICAgX0RlYnVnT3ZlcmxheS5pbnN0YW5jZS5kZXN0cm95KCk7XG4gICAgICAgIF9EZWJ1Z092ZXJsYXkuaW5zdGFuY2UgPSBudWxsO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBEaXNwbGF5IHRoZSBkZWJ1ZyBvdmVybGF5XG4gICAgICovXG4gICAgZGlzcGxheSgpIHtcbiAgICAgIGlmICh0aGlzLiRjb250YWluZXIpIHtcbiAgICAgICAgdGhpcy4kY29udGFpbmVyLnNob3coKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy5jcmVhdGVPdmVybGF5KCk7XG4gICAgICBpZiAodGhpcy5vcHRpb25zLnNob3dTdGF0dXMpIHtcbiAgICAgICAgdGhpcy5jcmVhdGVTdGF0dXNJbmRpY2F0b3IoKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLyoqXG4gICAgICogSGlkZSB0aGUgZGVidWcgb3ZlcmxheVxuICAgICAqL1xuICAgIGhpZGUoKSB7XG4gICAgICBpZiAodGhpcy4kY29udGFpbmVyKSB7XG4gICAgICAgIHRoaXMuJGNvbnRhaW5lci5oaWRlKCk7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy4kc3RhdHVzSW5kaWNhdG9yKSB7XG4gICAgICAgIHRoaXMuJHN0YXR1c0luZGljYXRvci5oaWRlKCk7XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFJlbW92ZSB0aGUgZGVidWcgb3ZlcmxheSBjb21wbGV0ZWx5XG4gICAgICovXG4gICAgZGVzdHJveSgpIHtcbiAgICAgIGlmICh0aGlzLiRjb250YWluZXIpIHtcbiAgICAgICAgdGhpcy4kY29udGFpbmVyLnJlbW92ZSgpO1xuICAgICAgICB0aGlzLiRjb250YWluZXIgPSBudWxsO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuJHN0YXR1c0luZGljYXRvcikge1xuICAgICAgICB0aGlzLiRzdGF0dXNJbmRpY2F0b3IucmVtb3ZlKCk7XG4gICAgICAgIHRoaXMuJHN0YXR1c0luZGljYXRvciA9IG51bGw7XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFVwZGF0ZSB0aGUgc3RhdHVzIGluZGljYXRvclxuICAgICAqL1xuICAgIHVwZGF0ZVN0YXR1cyhtb2RlKSB7XG4gICAgICBpZiAoIXRoaXMuJHN0YXR1c0luZGljYXRvcilcbiAgICAgICAgcmV0dXJuO1xuICAgICAgdGhpcy4kc3RhdHVzSW5kaWNhdG9yLnRleHQoXCJEZWJ1ZzogXCIgKyBtb2RlKTtcbiAgICAgIHRoaXMuJHN0YXR1c0luZGljYXRvci5hdHRyKFwiY2xhc3NcIiwgXCJqcWh0bWwtZGVidWctc3RhdHVzXCIgKyAobW9kZSAhPT0gXCJPZmZcIiA/IFwiIGFjdGl2ZVwiIDogXCJcIikpO1xuICAgIH1cbiAgICBjcmVhdGVPdmVybGF5KCkge1xuICAgICAgdGhpcy5hZGRTdHlsZXMoKTtcbiAgICAgIHRoaXMuJGNvbnRhaW5lciA9IHRoaXMuJChcIjxkaXY+XCIpLmFkZENsYXNzKGBqcWh0bWwtZGVidWctb3ZlcmxheSAke3RoaXMub3B0aW9ucy50aGVtZX0gJHt0aGlzLm9wdGlvbnMucG9zaXRpb259YCk7XG4gICAgICBjb25zdCAkY29udGVudCA9IHRoaXMuJChcIjxkaXY+XCIpLmFkZENsYXNzKFwianFodG1sLWRlYnVnLWNvbnRlbnRcIik7XG4gICAgICBjb25zdCAkY29udHJvbHMgPSB0aGlzLiQoXCI8ZGl2PlwiKS5hZGRDbGFzcyhcImpxaHRtbC1kZWJ1Zy1jb250cm9sc1wiKTtcbiAgICAgIGNvbnN0ICR0aXRsZSA9IHRoaXMuJChcIjxzcGFuPlwiKS5hZGRDbGFzcyhcImpxaHRtbC1kZWJ1Zy10aXRsZVwiKS5odG1sKFwiPHN0cm9uZz5cXHV7MUY0MUJ9IEpRSFRNTCBEZWJ1Zzo8L3N0cm9uZz5cIik7XG4gICAgICAkY29udHJvbHMuYXBwZW5kKCR0aXRsZSk7XG4gICAgICBjb25zdCBidXR0b25zID0gW1xuICAgICAgICB7IHRleHQ6IFwiU2xvdyBNb3Rpb24gKyBGbGFzaFwiLCBhY3Rpb246IFwiZW5hYmxlU2xvd01vdGlvbkRlYnVnXCIsIGNsYXNzOiBcInN1Y2Nlc3NcIiB9LFxuICAgICAgICB7IHRleHQ6IFwiQmFzaWMgRGVidWdcIiwgYWN0aW9uOiBcImVuYWJsZUJhc2ljRGVidWdcIiwgY2xhc3M6IFwiXCIgfSxcbiAgICAgICAgeyB0ZXh0OiBcIkZ1bGwgRGVidWdcIiwgYWN0aW9uOiBcImVuYWJsZUZ1bGxEZWJ1Z1wiLCBjbGFzczogXCJcIiB9LFxuICAgICAgICB7IHRleHQ6IFwiU2VxdWVudGlhbFwiLCBhY3Rpb246IFwiZW5hYmxlU2VxdWVudGlhbE1vZGVcIiwgY2xhc3M6IFwiXCIgfSxcbiAgICAgICAgeyB0ZXh0OiBcIkNsZWFyIERlYnVnXCIsIGFjdGlvbjogXCJjbGVhckFsbERlYnVnXCIsIGNsYXNzOiBcImRhbmdlclwiIH0sXG4gICAgICAgIHsgdGV4dDogXCJTZXR0aW5nc1wiLCBhY3Rpb246IFwic2hvd0RlYnVnSW5mb1wiLCBjbGFzczogXCJcIiB9XG4gICAgICBdO1xuICAgICAgYnV0dG9ucy5mb3JFYWNoKChidG4pID0+IHtcbiAgICAgICAgY29uc3QgJGJ1dHRvbiA9IHRoaXMuJChcIjxidXR0b24+XCIpLnRleHQoYnRuLnRleHQpLmFkZENsYXNzKFwianFodG1sLWRlYnVnLWJ0blwiICsgKGJ0bi5jbGFzcyA/IGAgJHtidG4uY2xhc3N9YCA6IFwiXCIpKS5vbihcImNsaWNrXCIsICgpID0+IHRoaXMuZXhlY3V0ZUFjdGlvbihidG4uYWN0aW9uKSk7XG4gICAgICAgICRjb250cm9scy5hcHBlbmQoJGJ1dHRvbik7XG4gICAgICB9KTtcbiAgICAgIGNvbnN0ICR0b2dnbGVCdG4gPSB0aGlzLiQoXCI8YnV0dG9uPlwiKS50ZXh0KHRoaXMub3B0aW9ucy5jb21wYWN0ID8gXCJcXHUyNUJDXCIgOiBcIlxcdTI1QjJcIikuYWRkQ2xhc3MoXCJqcWh0bWwtZGVidWctdG9nZ2xlXCIpLm9uKFwiY2xpY2tcIiwgKCkgPT4gdGhpcy50b2dnbGUoKSk7XG4gICAgICAkY29udHJvbHMuYXBwZW5kKCR0b2dnbGVCdG4pO1xuICAgICAgJGNvbnRlbnQuYXBwZW5kKCRjb250cm9scyk7XG4gICAgICB0aGlzLiRjb250YWluZXIuYXBwZW5kKCRjb250ZW50KTtcbiAgICAgIHRoaXMuJChcImJvZHlcIikuYXBwZW5kKHRoaXMuJGNvbnRhaW5lcik7XG4gICAgfVxuICAgIGNyZWF0ZVN0YXR1c0luZGljYXRvcigpIHtcbiAgICAgIHRoaXMuJHN0YXR1c0luZGljYXRvciA9IHRoaXMuJChcIjxkaXY+XCIpLmFkZENsYXNzKFwianFodG1sLWRlYnVnLXN0YXR1c1wiKS50ZXh0KFwiRGVidWc6IE9mZlwiKS5jc3Moe1xuICAgICAgICBwb3NpdGlvbjogXCJmaXhlZFwiLFxuICAgICAgICB0b3A6IFwiMTBweFwiLFxuICAgICAgICByaWdodDogXCIxMHB4XCIsXG4gICAgICAgIGJhY2tncm91bmQ6IFwiIzJjM2U1MFwiLFxuICAgICAgICBjb2xvcjogXCJ3aGl0ZVwiLFxuICAgICAgICBwYWRkaW5nOiBcIjVweCAxMHB4XCIsXG4gICAgICAgIGJvcmRlclJhZGl1czogXCI0cHhcIixcbiAgICAgICAgZm9udFNpemU6IFwiMC43NXJlbVwiLFxuICAgICAgICB6SW5kZXg6IFwiMTAwMDFcIixcbiAgICAgICAgb3BhY2l0eTogXCIwLjhcIixcbiAgICAgICAgZm9udEZhbWlseTogXCJtb25vc3BhY2VcIlxuICAgICAgfSk7XG4gICAgICB0aGlzLiQoXCJib2R5XCIpLmFwcGVuZCh0aGlzLiRzdGF0dXNJbmRpY2F0b3IpO1xuICAgIH1cbiAgICBhZGRTdHlsZXMoKSB7XG4gICAgICBpZiAodGhpcy4kKFwiI2pxaHRtbC1kZWJ1Zy1zdHlsZXNcIikubGVuZ3RoID4gMClcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY29uc3QgJHN0eWxlID0gdGhpcy4kKFwiPHN0eWxlPlwiKS5hdHRyKFwiaWRcIiwgXCJqcWh0bWwtZGVidWctc3R5bGVzXCIpLnRleHQoJy5qcWh0bWwtZGVidWctb3ZlcmxheSB7cG9zaXRpb246IGZpeGVkO2xlZnQ6IDA7cmlnaHQ6IDA7ei1pbmRleDogMTAwMDA7Zm9udC1mYW1pbHk6IC1hcHBsZS1zeXN0ZW0sIEJsaW5rTWFjU3lzdGVtRm9udCwgXCJTZWdvZSBVSVwiLCBSb2JvdG8sIG1vbm9zcGFjZTtmb250LXNpemU6IDAuOHJlbTtib3gtc2hhZG93OiAwIDJweCAxMHB4IHJnYmEoMCwwLDAsMC4yKTt9LmpxaHRtbC1kZWJ1Zy1vdmVybGF5LnRvcCB7dG9wOiAwO30uanFodG1sLWRlYnVnLW92ZXJsYXkuYm90dG9tIHtib3R0b206IDA7fS5qcWh0bWwtZGVidWctb3ZlcmxheS5kYXJrIHtiYWNrZ3JvdW5kOiAjMzQ0OTVlO2NvbG9yOiAjZWNmMGYxO30uanFodG1sLWRlYnVnLW92ZXJsYXkubGlnaHQge2JhY2tncm91bmQ6ICNmOGY5ZmE7Y29sb3I6ICMzMzM7Ym9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICNkZWUyZTY7fS5qcWh0bWwtZGVidWctY29udGVudCB7cGFkZGluZzogMC41cmVtIDFyZW07fS5qcWh0bWwtZGVidWctY29udHJvbHMge2Rpc3BsYXk6IGZsZXg7ZmxleC13cmFwOiB3cmFwO2dhcDogOHB4O2FsaWduLWl0ZW1zOiBjZW50ZXI7fS5qcWh0bWwtZGVidWctdGl0bGUge21hcmdpbi1yaWdodDogMTBweDtmb250LXdlaWdodDogYm9sZDt9LmpxaHRtbC1kZWJ1Zy1idG4ge3BhZGRpbmc6IDRweCA4cHg7Ym9yZGVyOiBub25lO2JvcmRlci1yYWRpdXM6IDNweDtiYWNrZ3JvdW5kOiAjMzQ5OGRiO2NvbG9yOiB3aGl0ZTtjdXJzb3I6IHBvaW50ZXI7Zm9udC1zaXplOiAwLjc1cmVtO3RyYW5zaXRpb246IGJhY2tncm91bmQgMC4yczt9LmpxaHRtbC1kZWJ1Zy1idG46aG92ZXIge2JhY2tncm91bmQ6ICMyOTgwYjk7fS5qcWh0bWwtZGVidWctYnRuLnN1Y2Nlc3Mge2JhY2tncm91bmQ6ICMyN2FlNjA7fS5qcWh0bWwtZGVidWctYnRuLnN1Y2Nlc3M6aG92ZXIge2JhY2tncm91bmQ6ICMyMjk5NTQ7fS5qcWh0bWwtZGVidWctYnRuLmRhbmdlciB7YmFja2dyb3VuZDogI2U3NGMzYzt9LmpxaHRtbC1kZWJ1Zy1idG4uZGFuZ2VyOmhvdmVyIHtiYWNrZ3JvdW5kOiAjYzAzOTJiO30uanFodG1sLWRlYnVnLXRvZ2dsZSB7cGFkZGluZzogNHB4IDhweDtib3JkZXI6IG5vbmU7Ym9yZGVyLXJhZGl1czogM3B4O2JhY2tncm91bmQ6ICM3ZjhjOGQ7Y29sb3I6IHdoaXRlO2N1cnNvcjogcG9pbnRlcjtmb250LXNpemU6IDAuNzVyZW07bWFyZ2luLWxlZnQ6IGF1dG87fS5qcWh0bWwtZGVidWctdG9nZ2xlOmhvdmVyIHtiYWNrZ3JvdW5kOiAjNmM3YjdkO30uanFodG1sLWRlYnVnLXN0YXR1cy5hY3RpdmUge2JhY2tncm91bmQ6ICMyN2FlNjAgIWltcG9ydGFudDt9QG1lZGlhIChtYXgtd2lkdGg6IDc2OHB4KSB7LmpxaHRtbC1kZWJ1Zy1jb250cm9scyB7ZmxleC1kaXJlY3Rpb246IGNvbHVtbjthbGlnbi1pdGVtczogZmxleC1zdGFydDt9LmpxaHRtbC1kZWJ1Zy10aXRsZSB7bWFyZ2luLWJvdHRvbTogNXB4O319Jyk7XG4gICAgICB0aGlzLiQoXCJoZWFkXCIpLmFwcGVuZCgkc3R5bGUpO1xuICAgIH1cbiAgICB0b2dnbGUoKSB7XG4gICAgICB0aGlzLm9wdGlvbnMuY29tcGFjdCA9ICF0aGlzLm9wdGlvbnMuY29tcGFjdDtcbiAgICAgIGNvbnN0ICR0b2dnbGVCdG4gPSB0aGlzLiRjb250YWluZXIuZmluZChcIi5qcWh0bWwtZGVidWctdG9nZ2xlXCIpO1xuICAgICAgJHRvZ2dsZUJ0bi50ZXh0KHRoaXMub3B0aW9ucy5jb21wYWN0ID8gXCJcXHUyNUJDXCIgOiBcIlxcdTI1QjJcIik7XG4gICAgICBjb25zdCAkYnV0dG9ucyA9IHRoaXMuJGNvbnRhaW5lci5maW5kKFwiLmpxaHRtbC1kZWJ1Zy1idG5cIik7XG4gICAgICBpZiAodGhpcy5vcHRpb25zLmNvbXBhY3QpIHtcbiAgICAgICAgJGJ1dHRvbnMuaGlkZSgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgJGJ1dHRvbnMuc2hvdygpO1xuICAgICAgfVxuICAgIH1cbiAgICBleGVjdXRlQWN0aW9uKGFjdGlvbikge1xuICAgICAgY29uc3QganFodG1sMiA9IGdldEpxaHRtbCgpO1xuICAgICAgaWYgKCFqcWh0bWwyKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihcIkpRSFRNTCBub3QgYXZhaWxhYmxlIC0gbWFrZSBzdXJlIGl0J3MgbG9hZGVkIGFuZCBleHBvc2VkIGdsb2JhbGx5XCIpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBzd2l0Y2ggKGFjdGlvbikge1xuICAgICAgICBjYXNlIFwiZW5hYmxlU2xvd01vdGlvbkRlYnVnXCI6XG4gICAgICAgICAganFodG1sMi5zZXREZWJ1Z1NldHRpbmdzKHtcbiAgICAgICAgICAgIGxvZ0Z1bGxMaWZlY3ljbGU6IHRydWUsXG4gICAgICAgICAgICBzZXF1ZW50aWFsUHJvY2Vzc2luZzogdHJ1ZSxcbiAgICAgICAgICAgIGRlbGF5QWZ0ZXJDb21wb25lbnQ6IDE1MCxcbiAgICAgICAgICAgIGRlbGF5QWZ0ZXJSZW5kZXI6IDIwMCxcbiAgICAgICAgICAgIGRlbGF5QWZ0ZXJSZXJlbmRlcjogMjUwLFxuICAgICAgICAgICAgZmxhc2hDb21wb25lbnRzOiB0cnVlLFxuICAgICAgICAgICAgZmxhc2hEdXJhdGlvbjogODAwLFxuICAgICAgICAgICAgZmxhc2hDb2xvcnM6IHtcbiAgICAgICAgICAgICAgY3JlYXRlOiBcIiMzNDk4ZGJcIixcbiAgICAgICAgICAgICAgcmVuZGVyOiBcIiMyN2FlNjBcIixcbiAgICAgICAgICAgICAgcmVhZHk6IFwiIzliNTliNlwiXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgcHJvZmlsZVBlcmZvcm1hbmNlOiB0cnVlLFxuICAgICAgICAgICAgaGlnaGxpZ2h0U2xvd1JlbmRlcnM6IDMwLFxuICAgICAgICAgICAgbG9nRGlzcGF0Y2g6IHRydWVcbiAgICAgICAgICB9KTtcbiAgICAgICAgICB0aGlzLnVwZGF0ZVN0YXR1cyhcIlNsb3cgTW90aW9uXCIpO1xuICAgICAgICAgIGNvbnNvbGUubG9nKFwiXFx1ezFGNDFCfSBTbG93IE1vdGlvbiBEZWJ1ZyBNb2RlIEVuYWJsZWRcIik7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJlbmFibGVCYXNpY0RlYnVnXCI6XG4gICAgICAgICAganFodG1sMi5lbmFibGVEZWJ1Z01vZGUoXCJiYXNpY1wiKTtcbiAgICAgICAgICB0aGlzLnVwZGF0ZVN0YXR1cyhcIkJhc2ljXCIpO1xuICAgICAgICAgIGNvbnNvbGUubG9nKFwiXFx1ezFGNDFCfSBCYXNpYyBEZWJ1ZyBNb2RlIEVuYWJsZWRcIik7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJlbmFibGVGdWxsRGVidWdcIjpcbiAgICAgICAgICBqcWh0bWwyLmVuYWJsZURlYnVnTW9kZShcImZ1bGxcIik7XG4gICAgICAgICAgdGhpcy51cGRhdGVTdGF0dXMoXCJGdWxsXCIpO1xuICAgICAgICAgIGNvbnNvbGUubG9nKFwiXFx1ezFGNDFCfSBGdWxsIERlYnVnIE1vZGUgRW5hYmxlZFwiKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcImVuYWJsZVNlcXVlbnRpYWxNb2RlXCI6XG4gICAgICAgICAganFodG1sMi5zZXREZWJ1Z1NldHRpbmdzKHtcbiAgICAgICAgICAgIGxvZ0NyZWF0aW9uUmVhZHk6IHRydWUsXG4gICAgICAgICAgICBzZXF1ZW50aWFsUHJvY2Vzc2luZzogdHJ1ZSxcbiAgICAgICAgICAgIGZsYXNoQ29tcG9uZW50czogdHJ1ZSxcbiAgICAgICAgICAgIHByb2ZpbGVQZXJmb3JtYW5jZTogdHJ1ZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHRoaXMudXBkYXRlU3RhdHVzKFwiU2VxdWVudGlhbFwiKTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhcIlxcdXsxRjQxQn0gU2VxdWVudGlhbCBQcm9jZXNzaW5nIE1vZGUgRW5hYmxlZFwiKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcImNsZWFyQWxsRGVidWdcIjpcbiAgICAgICAgICBqcWh0bWwyLmNsZWFyRGVidWdTZXR0aW5ncygpO1xuICAgICAgICAgIHRoaXMudXBkYXRlU3RhdHVzKFwiT2ZmXCIpO1xuICAgICAgICAgIGNvbnNvbGUubG9nKFwiXFx1ezFGNDFCfSBBbGwgRGVidWcgTW9kZXMgRGlzYWJsZWRcIik7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJzaG93RGVidWdJbmZvXCI6XG4gICAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBKU09OLnN0cmluZ2lmeShqcWh0bWwyLmRlYnVnLCBudWxsLCAyKTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhcIlxcdXsxRjQxQn0gQ3VycmVudCBEZWJ1ZyBTZXR0aW5nczpcIiwgc2V0dGluZ3MpO1xuICAgICAgICAgIGFsZXJ0KFwiRGVidWcgc2V0dGluZ3MgbG9nZ2VkIHRvIGNvbnNvbGU6XFxuXFxuXCIgKyAoT2JqZWN0LmtleXMoanFodG1sMi5kZWJ1ZykubGVuZ3RoID4gMCA/IHNldHRpbmdzIDogXCJObyBkZWJ1ZyBzZXR0aW5ncyBhY3RpdmVcIikpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfTtcbiAgRGVidWdPdmVybGF5Lmluc3RhbmNlID0gbnVsbDtcbiAgaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICBjb25zdCB1cmxQYXJhbXMgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKHdpbmRvdy5sb2NhdGlvbi5zZWFyY2gpO1xuICAgIGlmICh1cmxQYXJhbXMuZ2V0KFwiZGVidWdcIikgPT09IFwidHJ1ZVwiIHx8IHVybFBhcmFtcy5nZXQoXCJqcWh0bWwtZGVidWdcIikgPT09IFwidHJ1ZVwiKSB7XG4gICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKFwiRE9NQ29udGVudExvYWRlZFwiLCAoKSA9PiB7XG4gICAgICAgIERlYnVnT3ZlcmxheS5zaG93KCk7XG4gICAgICB9KTtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gaW5pdF9qcXVlcnlfcGx1Z2luKGpRdWVyeSkge1xuICAgIGlmICghalF1ZXJ5IHx8ICFqUXVlcnkuZm4pIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcImpRdWVyeSBpcyByZXF1aXJlZCBmb3IgSlFIVE1MLiBQbGVhc2UgZW5zdXJlIGpRdWVyeSBpcyBsb2FkZWQgYmVmb3JlIGluaXRpYWxpemluZyBKUUhUTUwuXCIpO1xuICAgIH1cbiAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB3aW5kb3cuJCAhPT0galF1ZXJ5ICYmICFqUXVlcnkuX19qcWh0bWxfY2hlY2tlZCkge1xuICAgICAgZGV2V2FybignalF1ZXJ5IGluc3RhbmNlIGFwcGVhcnMgdG8gYmUgYnVuZGxlZCB3aXRoIHdlYnBhY2svbW9kdWxlcyByYXRoZXIgdGhhbiBsb2FkZWQgZ2xvYmFsbHkuXFxuRm9yIGJlc3QgY29tcGF0aWJpbGl0eSwgaXQgaXMgcmVjb21tZW5kZWQgdG86XFxuMS4gSW5jbHVkZSBqUXVlcnkgdmlhIDxzY3JpcHQ+IHRhZyBmcm9tIGEgQ0ROIChVTUQgZm9ybWF0KVxcbjIuIENvbmZpZ3VyZSB3ZWJwYWNrIHdpdGg6IGV4dGVybmFsczogeyBqcXVlcnk6IFwiJFwiIH1cXG4zLiBSZW1vdmUganF1ZXJ5IGZyb20gcGFja2FnZS5qc29uIGRlcGVuZGVuY2llc1xcblxcblRvIHN1cHByZXNzIHRoaXMgd2FybmluZywgc2V0OiB3aW5kb3cuSlFIVE1MX1NVUFBSRVNTX1dBUk5JTkdTID0gdHJ1ZScpO1xuICAgICAgalF1ZXJ5Ll9fanFodG1sX2NoZWNrZWQgPSB0cnVlO1xuICAgIH1cbiAgICBjb25zdCBfanFodG1sX29yaWdpbmFsX2pxdWVyeSA9IGpRdWVyeTtcbiAgICBjb25zdCBKUXVlcnlXaXRoQ29tcG9uZW50U3VwcG9ydCA9IGZ1bmN0aW9uKHNlbGVjdG9yLCBjb250ZXh0KSB7XG4gICAgICBpZiAoc2VsZWN0b3IgJiYgdHlwZW9mIHNlbGVjdG9yID09PSBcIm9iamVjdFwiICYmIHNlbGVjdG9yLiQgJiYgdHlwZW9mIHNlbGVjdG9yLiRpZCA9PT0gXCJmdW5jdGlvblwiICYmIHR5cGVvZiBzZWxlY3Rvci5pZCA9PT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgIHJldHVybiBzZWxlY3Rvci4kO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG5ldyBfanFodG1sX29yaWdpbmFsX2pxdWVyeShzZWxlY3RvciwgY29udGV4dCk7XG4gICAgfTtcbiAgICBPYmplY3Quc2V0UHJvdG90eXBlT2YoSlF1ZXJ5V2l0aENvbXBvbmVudFN1cHBvcnQsIF9qcWh0bWxfb3JpZ2luYWxfanF1ZXJ5KTtcbiAgICBmb3IgKGNvbnN0IGtleSBpbiBfanFodG1sX29yaWdpbmFsX2pxdWVyeSkge1xuICAgICAgaWYgKF9qcWh0bWxfb3JpZ2luYWxfanF1ZXJ5Lmhhc093blByb3BlcnR5KGtleSkpIHtcbiAgICAgICAgSlF1ZXJ5V2l0aENvbXBvbmVudFN1cHBvcnRba2V5XSA9IF9qcWh0bWxfb3JpZ2luYWxfanF1ZXJ5W2tleV07XG4gICAgICB9XG4gICAgfVxuICAgIEpRdWVyeVdpdGhDb21wb25lbnRTdXBwb3J0LnByb3RvdHlwZSA9IF9qcWh0bWxfb3JpZ2luYWxfanF1ZXJ5LnByb3RvdHlwZTtcbiAgICBKUXVlcnlXaXRoQ29tcG9uZW50U3VwcG9ydC5mbiA9IF9qcWh0bWxfb3JpZ2luYWxfanF1ZXJ5LmZuO1xuICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICB3aW5kb3cualF1ZXJ5ID0gSlF1ZXJ5V2l0aENvbXBvbmVudFN1cHBvcnQ7XG4gICAgICB3aW5kb3cuJCA9IEpRdWVyeVdpdGhDb21wb25lbnRTdXBwb3J0O1xuICAgIH1cbiAgICBqUXVlcnkgPSBKUXVlcnlXaXRoQ29tcG9uZW50U3VwcG9ydDtcbiAgICBjb25zdCBvcmlnaW5hbFZhbCA9IGpRdWVyeS5mbi52YWw7XG4gICAgalF1ZXJ5LmZuLnZhbCA9IGZ1bmN0aW9uKHZhbHVlKSB7XG4gICAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICBjb25zdCBmaXJzdEVsID0gdGhpcy5maXJzdCgpO1xuICAgICAgICBpZiAoZmlyc3RFbC5sZW5ndGggPT09IDApXG4gICAgICAgICAgcmV0dXJuIHZvaWQgMDtcbiAgICAgICAgY29uc3QgY29tcG9uZW50ID0gZmlyc3RFbC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgY29uc3QgdGFnTmFtZSA9IGZpcnN0RWwucHJvcChcInRhZ05hbWVcIik7XG4gICAgICAgIGlmIChjb21wb25lbnQgJiYgdHlwZW9mIGNvbXBvbmVudC52YWwgPT09IFwiZnVuY3Rpb25cIiAmJiB0YWdOYW1lICE9PSBcIklOUFVUXCIgJiYgdGFnTmFtZSAhPT0gXCJURVhUQVJFQVwiKSB7XG4gICAgICAgICAgcmV0dXJuIGNvbXBvbmVudC52YWwoKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gb3JpZ2luYWxWYWwuY2FsbCh0aGlzKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgICBjb25zdCAkZWwgPSBqUXVlcnkodGhpcyk7XG4gICAgICAgICAgY29uc3QgY29tcG9uZW50ID0gJGVsLmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICAgIGNvbnN0IHRhZ05hbWUgPSAkZWwucHJvcChcInRhZ05hbWVcIik7XG4gICAgICAgICAgaWYgKGNvbXBvbmVudCAmJiB0eXBlb2YgY29tcG9uZW50LnZhbCA9PT0gXCJmdW5jdGlvblwiICYmIHRhZ05hbWUgIT09IFwiSU5QVVRcIiAmJiB0YWdOYW1lICE9PSBcIlRFWFRBUkVBXCIpIHtcbiAgICAgICAgICAgIGNvbXBvbmVudC52YWwodmFsdWUpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBvcmlnaW5hbFZhbC5jYWxsKCRlbCwgdmFsdWUpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgICAgfVxuICAgIH07XG4gICAgalF1ZXJ5LmZuLmNvbXBvbmVudCA9IGZ1bmN0aW9uKGNvbXBvbmVudE9yTmFtZSwgYXJncyA9IHt9KSB7XG4gICAgICBjb25zdCBlbGVtZW50ID0gdGhpcy5maXJzdCA/IHRoaXMuZmlyc3QoKSA6IHRoaXM7XG4gICAgICBpZiAoIWNvbXBvbmVudE9yTmFtZSkge1xuICAgICAgICBpZiAoZWxlbWVudC5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBjb21wID0gZWxlbWVudC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgICAgcmV0dXJuIGNvbXAgfHwgbnVsbDtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGV4aXN0aW5nQ29tcG9uZW50ID0gZWxlbWVudC5kYXRhKFwiX2NvbXBvbmVudFwiKTtcbiAgICAgIGlmIChleGlzdGluZ0NvbXBvbmVudCkge1xuICAgICAgICByZXR1cm4gZXhpc3RpbmdDb21wb25lbnQ7XG4gICAgICB9XG4gICAgICBsZXQgQ29tcG9uZW50Q2xhc3M7XG4gICAgICBsZXQgY29tcG9uZW50TmFtZTtcbiAgICAgIGlmICh0eXBlb2YgY29tcG9uZW50T3JOYW1lID09PSBcInN0cmluZ1wiKSB7XG4gICAgICAgIGNvbXBvbmVudE5hbWUgPSBjb21wb25lbnRPck5hbWU7XG4gICAgICAgIGNvbnN0IGZvdW5kID0gZ2V0X2NvbXBvbmVudF9jbGFzcyhjb21wb25lbnRPck5hbWUpO1xuICAgICAgICBhcmdzID0geyAuLi5hcmdzLCBfY29tcG9uZW50X25hbWU6IGNvbXBvbmVudE5hbWUgfTtcbiAgICAgICAgaWYgKCFmb3VuZCkge1xuICAgICAgICAgIENvbXBvbmVudENsYXNzID0gSnFodG1sX0NvbXBvbmVudDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBDb21wb25lbnRDbGFzcyA9IGZvdW5kO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBDb21wb25lbnRDbGFzcyA9IGNvbXBvbmVudE9yTmFtZTtcbiAgICAgIH1cbiAgICAgIGxldCB0YXJnZXRFbGVtZW50ID0gZWxlbWVudDtcbiAgICAgIGlmIChjb21wb25lbnROYW1lKSB7XG4gICAgICAgIGNvbnN0IHRlbXBsYXRlID0gZ2V0X3RlbXBsYXRlKGNvbXBvbmVudE5hbWUpO1xuICAgICAgICBjb25zdCBleHBlY3RlZFRhZyA9IGFyZ3MuX3RhZyB8fCB0ZW1wbGF0ZS50YWcgfHwgXCJkaXZcIjtcbiAgICAgICAgY29uc3QgY3VycmVudFRhZyA9IGVsZW1lbnQucHJvcChcInRhZ05hbWVcIikudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgaWYgKGN1cnJlbnRUYWcgIT09IGV4cGVjdGVkVGFnLnRvTG93ZXJDYXNlKCkpIHtcbiAgICAgICAgICBpZiAoYXJncy5faW5uZXJfaHRtbCkge1xuICAgICAgICAgICAgY29uc3QgbmV3RWxlbWVudCA9IGpRdWVyeShgPCR7ZXhwZWN0ZWRUYWd9PjwvJHtleHBlY3RlZFRhZ30+YCk7XG4gICAgICAgICAgICBjb25zdCBvbGRFbCA9IGVsZW1lbnRbMF07XG4gICAgICAgICAgICBpZiAob2xkRWwgJiYgb2xkRWwuYXR0cmlidXRlcykge1xuICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG9sZEVsLmF0dHJpYnV0ZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICBjb25zdCBhdHRyID0gb2xkRWwuYXR0cmlidXRlc1tpXTtcbiAgICAgICAgICAgICAgICBuZXdFbGVtZW50LmF0dHIoYXR0ci5uYW1lLCBhdHRyLnZhbHVlKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbmV3RWxlbWVudC5odG1sKGVsZW1lbnQuaHRtbCgpKTtcbiAgICAgICAgICAgIGVsZW1lbnQucmVwbGFjZVdpdGgobmV3RWxlbWVudCk7XG4gICAgICAgICAgICB0YXJnZXRFbGVtZW50ID0gbmV3RWxlbWVudDtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGBbSlFIVE1MXSBDb21wb25lbnQgJyR7Y29tcG9uZW50TmFtZX0nIGV4cGVjdHMgdGFnICc8JHtleHBlY3RlZFRhZ30+JyBidXQgZWxlbWVudCBpcyAnPCR7Y3VycmVudFRhZ30+Jy4gRWxlbWVudCB0YWcgd2lsbCBub3QgYmUgY2hhbmdlZC4gQ29uc2lkZXIgdXNpbmcgdGhlIGNvcnJlY3QgdGFnLmApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgY29uc3QgY29tcG9uZW50ID0gbmV3IENvbXBvbmVudENsYXNzKHRhcmdldEVsZW1lbnQsIGFyZ3MpO1xuICAgICAgY29tcG9uZW50LmJvb3QoKTtcbiAgICAgIGFwcGx5RGVidWdEZWxheShcImNvbXBvbmVudFwiKTtcbiAgICAgIHJldHVybiBjb21wb25lbnQ7XG4gICAgfTtcbiAgICBjb25zdCBfanFodG1sX2pxdWVyeV9vdmVycmlkZXMgPSB7fTtcbiAgICBjb25zdCBkb21faW5zZXJ0aW9uX21ldGhvZHMgPSBbXCJhcHBlbmRcIiwgXCJwcmVwZW5kXCIsIFwiYmVmb3JlXCIsIFwiYWZ0ZXJcIiwgXCJyZXBsYWNlV2l0aFwiXTtcbiAgICBmb3IgKGNvbnN0IGZubmFtZSBvZiBkb21faW5zZXJ0aW9uX21ldGhvZHMpIHtcbiAgICAgIF9qcWh0bWxfanF1ZXJ5X292ZXJyaWRlc1tmbm5hbWVdID0galF1ZXJ5LmZuW2ZubmFtZV07XG4gICAgICBqUXVlcnkuZm5bZm5uYW1lXSA9IGZ1bmN0aW9uKC4uLmFyZ3MpIHtcbiAgICAgICAgY29uc3QgcmVzb2x2ZWRBcmdzID0gYXJncy5tYXAoKGFyZykgPT4ge1xuICAgICAgICAgIGlmIChhcmcgJiYgdHlwZW9mIGFyZyA9PT0gXCJvYmplY3RcIiAmJiBhcmcgaW5zdGFuY2VvZiBKcWh0bWxfQ29tcG9uZW50KSB7XG4gICAgICAgICAgICByZXR1cm4gYXJnLiQ7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBhcmc7XG4gICAgICAgIH0pO1xuICAgICAgICBjb25zdCAkZWxlbWVudHMgPSByZXNvbHZlZEFyZ3MuZmlsdGVyKChhcmcpID0+IGFyZyBpbnN0YW5jZW9mIGpRdWVyeSk7XG4gICAgICAgIGNvbnN0IHJldCA9IF9qcWh0bWxfanF1ZXJ5X292ZXJyaWRlc1tmbm5hbWVdLmFwcGx5KHRoaXMsIHJlc29sdmVkQXJncyk7XG4gICAgICAgIGZvciAoY29uc3QgJGUgb2YgJGVsZW1lbnRzKSB7XG4gICAgICAgICAgaWYgKCRlLmNsb3Nlc3QoXCJodG1sXCIpLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICRlLmZpbmQoXCIuSnFodG1sX0NvbXBvbmVudFwiKS5hZGRCYWNrKFwiLkpxaHRtbF9Db21wb25lbnRcIikuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgICAgICAgY29uc3QgJGNvbXAgPSBqUXVlcnkodGhpcyk7XG4gICAgICAgICAgICAgIGNvbnN0IGNvbXBvbmVudCA9ICRjb21wLmRhdGEoXCJfY29tcG9uZW50XCIpO1xuICAgICAgICAgICAgICBpZiAoY29tcG9uZW50ICYmICFjb21wb25lbnQuX3JlYWR5X3N0YXRlKSB7XG4gICAgICAgICAgICAgICAgY29tcG9uZW50LmJvb3QoKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXQ7XG4gICAgICB9O1xuICAgIH1cbiAgICBqUXVlcnkuZm4uc2hhbGxvd0ZpbmQgPSBmdW5jdGlvbihzZWxlY3Rvcikge1xuICAgICAgY29uc3QgcmVzdWx0cyA9IFtdO1xuICAgICAgdGhpcy5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICBjb25zdCB0cmF2ZXJzZSA9IChwYXJlbnQpID0+IHtcbiAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBhcmVudC5jaGlsZHJlbi5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgY29uc3QgY2hpbGQgPSBwYXJlbnQuY2hpbGRyZW5baV07XG4gICAgICAgICAgICBpZiAoalF1ZXJ5KGNoaWxkKS5pcyhzZWxlY3RvcikpIHtcbiAgICAgICAgICAgICAgcmVzdWx0cy5wdXNoKGNoaWxkKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHRyYXZlcnNlKGNoaWxkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIHRyYXZlcnNlKHRoaXMpO1xuICAgICAgfSk7XG4gICAgICByZXR1cm4galF1ZXJ5KHJlc3VsdHMpO1xuICAgIH07XG4gICAgY29uc3Qgb3JpZ2luYWxFbXB0eSA9IGpRdWVyeS5mbi5lbXB0eTtcbiAgICBjb25zdCBvcmlnaW5hbEh0bWwgPSBqUXVlcnkuZm4uaHRtbDtcbiAgICBjb25zdCBvcmlnaW5hbFRleHQgPSBqUXVlcnkuZm4udGV4dDtcbiAgICBqUXVlcnkuZm4uZW1wdHkgPSBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG4gICAgICAgIGpRdWVyeSh0aGlzKS5maW5kKFwiLkpxaHRtbF9Db21wb25lbnRcIikuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgICBjb25zdCBjb21wb25lbnQgPSBqUXVlcnkodGhpcykuZGF0YShcIl9jb21wb25lbnRcIik7XG4gICAgICAgICAgaWYgKGNvbXBvbmVudCAmJiAhY29tcG9uZW50Ll9zdG9wcGVkKSB7XG4gICAgICAgICAgICBjb21wb25lbnQuX3N0b3AoKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICBvcmlnaW5hbEVtcHR5LmNhbGwoalF1ZXJ5KHRoaXMpKTtcbiAgICAgIH0pO1xuICAgIH07XG4gICAgalF1ZXJ5LmZuLmh0bWwgPSBmdW5jdGlvbih2YWx1ZSkge1xuICAgICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsSHRtbC5jYWxsKHRoaXMpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpIHtcbiAgICAgICAgalF1ZXJ5KHRoaXMpLmVtcHR5KCk7XG4gICAgICAgIG9yaWdpbmFsSHRtbC5jYWxsKGpRdWVyeSh0aGlzKSwgdmFsdWUpO1xuICAgICAgfSk7XG4gICAgfTtcbiAgICBqUXVlcnkuZm4udGV4dCA9IGZ1bmN0aW9uKHZhbHVlKSB7XG4gICAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICByZXR1cm4gb3JpZ2luYWxUZXh0LmNhbGwodGhpcyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCkge1xuICAgICAgICBqUXVlcnkodGhpcykuZW1wdHkoKTtcbiAgICAgICAgb3JpZ2luYWxUZXh0LmNhbGwoalF1ZXJ5KHRoaXMpLCB2YWx1ZSk7XG4gICAgICB9KTtcbiAgICB9O1xuICB9XG4gIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmIHdpbmRvdy5qUXVlcnkpIHtcbiAgICBpbml0X2pxdWVyeV9wbHVnaW4od2luZG93LmpRdWVyeSk7XG4gIH1cbiAgdmFyIHZlcnNpb24gPSBcIjIuMi4xODVcIjtcbiAgdmFyIGpxaHRtbCA9IHtcbiAgICAvLyBDb3JlXG4gICAgSnFodG1sX0NvbXBvbmVudCxcbiAgICBMaWZlY3ljbGVNYW5hZ2VyLFxuICAgIC8vIFJlZ2lzdHJ5XG4gICAgcmVnaXN0ZXJfY29tcG9uZW50LFxuICAgIGdldF9jb21wb25lbnRfY2xhc3MsXG4gICAgcmVnaXN0ZXJfdGVtcGxhdGUsXG4gICAgZ2V0X3RlbXBsYXRlLFxuICAgIGdldF90ZW1wbGF0ZV9ieV9jbGFzcyxcbiAgICBjcmVhdGVfY29tcG9uZW50LFxuICAgIGhhc19jb21wb25lbnQsXG4gICAgZ2V0X2NvbXBvbmVudF9uYW1lcyxcbiAgICBnZXRfcmVnaXN0ZXJlZF90ZW1wbGF0ZXMsXG4gICAgbGlzdF9jb21wb25lbnRzLFxuICAgIC8vIFRlbXBsYXRlIHN5c3RlbVxuICAgIHByb2Nlc3NfaW5zdHJ1Y3Rpb25zLFxuICAgIGV4dHJhY3Rfc2xvdHMsXG4gICAgcmVuZGVyX3RlbXBsYXRlLFxuICAgIGVzY2FwZV9odG1sLFxuICAgIC8vIFZlcnNpb24gcHJvcGVydHkgLSBpbnRlcm5hbFxuICAgIF9fdmVyc2lvbjogdmVyc2lvbixcbiAgICAvLyBEZWJ1ZyBzZXR0aW5nc1xuICAgIGRlYnVnOiB7XG4gICAgICBlbmFibGVkOiBmYWxzZSxcbiAgICAgIHZlcmJvc2U6IGZhbHNlXG4gICAgfSxcbiAgICAvLyBEZWJ1ZyBoZWxwZXIgZnVuY3Rpb25zIChtYWlubHkgZm9yIGludGVybmFsIHVzZSBidXQgZXhwb3NlZCBmb3IgYWR2YW5jZWQgZGVidWdnaW5nKVxuICAgIHNldERlYnVnU2V0dGluZ3Moc2V0dGluZ3MpIHtcbiAgICAgIE9iamVjdC5hc3NpZ24odGhpcy5kZWJ1Zywgc2V0dGluZ3MpO1xuICAgIH0sXG4gICAgZW5hYmxlRGVidWdNb2RlKGxldmVsID0gXCJiYXNpY1wiKSB7XG4gICAgICBpZiAobGV2ZWwgPT09IFwiYmFzaWNcIikge1xuICAgICAgICB0aGlzLmRlYnVnLmxvZ0NyZWF0aW9uUmVhZHkgPSB0cnVlO1xuICAgICAgICB0aGlzLmRlYnVnLmxvZ0Rpc3BhdGNoID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5kZWJ1Zy5mbGFzaENvbXBvbmVudHMgPSB0cnVlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5kZWJ1Zy5sb2dGdWxsTGlmZWN5Y2xlID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5kZWJ1Zy5sb2dEaXNwYXRjaFZlcmJvc2UgPSB0cnVlO1xuICAgICAgICB0aGlzLmRlYnVnLmZsYXNoQ29tcG9uZW50cyA9IHRydWU7XG4gICAgICAgIHRoaXMuZGVidWcucHJvZmlsZVBlcmZvcm1hbmNlID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5kZWJ1Zy50cmFjZURhdGFGbG93ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9LFxuICAgIGNsZWFyRGVidWdTZXR0aW5ncygpIHtcbiAgICAgIHRoaXMuZGVidWcgPSB7fTtcbiAgICB9LFxuICAgIC8vIERlYnVnIG92ZXJsYXkgbWV0aG9kc1xuICAgIHNob3dEZWJ1Z092ZXJsYXkob3B0aW9ucykge1xuICAgICAgcmV0dXJuIERlYnVnT3ZlcmxheS5zaG93KG9wdGlvbnMpO1xuICAgIH0sXG4gICAgaGlkZURlYnVnT3ZlcmxheSgpIHtcbiAgICAgIHJldHVybiBEZWJ1Z092ZXJsYXkuaGlkZSgpO1xuICAgIH0sXG4gICAgLy8gRXhwb3J0IERlYnVnT3ZlcmxheSBjbGFzcyBmb3IgZGlyZWN0IGFjY2Vzc1xuICAgIERlYnVnT3ZlcmxheSxcbiAgICAvLyBJbnN0YWxsIGdsb2JhbHMgZnVuY3Rpb25cbiAgICBpbnN0YWxsR2xvYmFscygpIHtcbiAgICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgIHdpbmRvdy5qcWh0bWwgPSB0aGlzO1xuICAgICAgICB3aW5kb3cuSnFodG1sX0NvbXBvbmVudCA9IEpxaHRtbF9Db21wb25lbnQ7XG4gICAgICAgIHdpbmRvdy5KcWh0bWxfTGlmZWN5Y2xlTWFuYWdlciA9IExpZmVjeWNsZU1hbmFnZXI7XG4gICAgICB9XG4gICAgfSxcbiAgICAvLyBWZXJzaW9uIGRpc3BsYXkgZnVuY3Rpb24gLSBzaG93cyB2ZXJzaW9uIG9mIGNvcmUgbGlicmFyeSBhbmQgYWxsIHJlZ2lzdGVyZWQgdGVtcGxhdGVzXG4gICAgX3ZlcnNpb24oKSB7XG4gICAgICBjb25zb2xlLmxvZyhgSlFIVE1MIENvcmUgdiR7dGhpcy5fX3ZlcnNpb259YCk7XG4gICAgICBjb25zb2xlLmxvZyhcIlJlZ2lzdGVyZWQgVGVtcGxhdGVzOlwiKTtcbiAgICAgIGNvbnN0IHRlbXBsYXRlTmFtZXMgPSBnZXRfY29tcG9uZW50X25hbWVzKCk7XG4gICAgICBpZiAodGVtcGxhdGVOYW1lcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgY29uc29sZS5sb2coXCIgIChubyB0ZW1wbGF0ZXMgcmVnaXN0ZXJlZClcIik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBmb3IgKGNvbnN0IG5hbWUgb2YgdGVtcGxhdGVOYW1lcykge1xuICAgICAgICAgIGNvbnN0IHRlbXBsYXRlID0gZ2V0X3RlbXBsYXRlKG5hbWUpO1xuICAgICAgICAgIGNvbnN0IHRlbXBsYXRlVmVyc2lvbiA9IHRlbXBsYXRlID8gdGVtcGxhdGUuX2pxaHRtbF92ZXJzaW9uIHx8IFwidW5rbm93blwiIDogXCJ1bmtub3duXCI7XG4gICAgICAgICAgY29uc29sZS5sb2coYCAgLSAke25hbWV9OiB2JHt0ZW1wbGF0ZVZlcnNpb259YCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLl9fdmVyc2lvbjtcbiAgICB9LFxuICAgIC8vIFB1YmxpYyB2ZXJzaW9uIGZ1bmN0aW9uIC0gcmV0dXJucyB0aGUgc3RhbXBlZCB2ZXJzaW9uIG51bWJlclxuICAgIHZlcnNpb24oKSB7XG4gICAgICByZXR1cm4gdmVyc2lvbjtcbiAgICB9XG4gIH07XG4gIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmICF3aW5kb3cuanFodG1sKSB7XG4gICAgd2luZG93LmpxaHRtbCA9IGpxaHRtbDtcbiAgICB3aW5kb3cuSnFodG1sX0NvbXBvbmVudCA9IEpxaHRtbF9Db21wb25lbnQ7XG4gICAgd2luZG93LkNvbXBvbmVudCA9IEpxaHRtbF9Db21wb25lbnQ7XG4gICAgd2luZG93LkpxaHRtbF9MaWZlY3ljbGVNYW5hZ2VyID0gTGlmZWN5Y2xlTWFuYWdlcjtcbiAgICBpZiAoanFodG1sLmRlYnVnPy5lbmFibGVkKSB7XG4gICAgICBjb25zb2xlLmxvZyhcIltKUUhUTUxdIEF1dG8tcmVnaXN0ZXJlZCB3aW5kb3cuanFodG1sIGdsb2JhbCBmb3IgdGVtcGxhdGUgY29tcGF0aWJpbGl0eVwiKTtcbiAgICB9XG4gIH1cblxuICAvLyBzdG9yYWdlL3JzeC10bXAvbnBtLWNvbXBpbGUvZW50cnlfNjQ1OWU4ZWQwZjYwYmRhNGYxMjE0MjA3NjYwMTJkNTMuanNcbiAgd2luZG93Ll9yc3hfbnBtID0gd2luZG93Ll9yc3hfbnBtIHx8IHt9O1xuICB3aW5kb3cuX3JzeF9ucG0uanFodG1sID0ganFodG1sO1xuICB3aW5kb3cuX3JzeF9ucG0uX0Jhc2VfSnFodG1sX0NvbXBvbmVudCA9IEpxaHRtbF9Db21wb25lbnQ7XG59KSgpO1xuIl19
|