NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name instagram-dm-unsender
// @license MIT
// @copyright Copyright (c) 2023, Romain Lebesle <oss@thoughtsunificator.me> (https://thoughtsunificator.me)
// @namespace https://thoughtsunificator.me/
// @author Romain Lebesle <oss@thoughtsunificator.me> (https://thoughtsunificator.me)
// @homepageURL https://thoughtsunificator.me/
// @supportURL https://thoughtsunificator.me/
// @contributionURL https://thoughtsunificator.me/
// @icon https://www.instagram.com/favicon.ico
// @version 0.7.2-patched
// @description Simple script to unsend all DMs in a thread on instagram.com (patched 2026-05 for new Messages-in-conversation aria-label, profile-link sender heuristic, and DOM-verification fix)
// @run-at document-end
// @include /^https://(www\.)?instagram\.com/*/
// ==/UserScript==
(function (exports) {
'use strict';
/** @module instagram Helpers to mimick Instagram's look and feel */
const BUTTON_STYLE = {
"PRIMARY": "primary",
"SECONDARY": "secondary",
};
/**
*
* @param {HTMLButtonElement} buttonElement
* @param {string} styleName
*/
function applyButtonStyle(buttonElement, styleName) {
buttonElement.style.fontSize = "var(--system-14-font-size)";
buttonElement.style.color = "white";
buttonElement.style.border = "0px";
buttonElement.style.borderRadius = "8px";
buttonElement.style.padding = "8px";
buttonElement.style.fontWeight = "bold";
buttonElement.style.cursor = "pointer";
buttonElement.style.lineHeight = "var(--system-14-line-height)";
if(styleName) {
buttonElement.style.backgroundColor = `rgb(var(--ig-${styleName}-button))`;
}
}
/** @module menu-button Helpers to create buttons that can be used in IDMU's menu */
/**
*
* @param {Document} document
* @param {string} text
* @param {string} styleName
* @returns {HTMLButtonElement}
*/
function createMenuButtonElement(document, text, styleName) {
const buttonElement = document.createElement("button");
buttonElement.textContent = text;
applyButtonStyle(buttonElement, styleName);
buttonElement.addEventListener("mouseover", () => {
buttonElement.style.filter = `brightness(1.15)`;
});
buttonElement.addEventListener("mouseout", () => {
buttonElement.style.filter = ``;
});
return buttonElement
}
/** @module menu IDMU's main menu */
/**
* @param {Document} document
* @returns {HTMLButtonElement}
*/
function createMenuElement(document) {
const menuElement = document.createElement("div");
menuElement.id = "idmu-menu";
menuElement.style.top = "20px";
menuElement.style.right = "430px";
menuElement.style.position = "fixed";
menuElement.style.zIndex = 999;
menuElement.style.display = "flex";
menuElement.style.gap = "10px";
menuElement.style.placeItems = "center";
return menuElement
}
/** @module async-events Utils module for finding elements asynchronously in the DOM */
/**
*
* @callback getElement
* @returns {Element}
*/
/**
*
* @param {Element} target
* @param {getElement} getElement
* @param {AbortController} abortController
* @returns {Promise<Element>}
*/
function waitForElement(target, getElement, abortController) {
return new Promise((resolve, reject) => {
let mutationObserver;
const abortHandler = () => {
if(mutationObserver) {
reject(new DOMException("Aborted: Disconnecting mutation observer...", "AbortError"));
mutationObserver.disconnect();
} else {
reject(new DOMException("Aborted", "AbortError"));
}
};
abortController.signal.addEventListener("abort", abortHandler);
let element = getElement();
if(element) {
resolve(element);
abortController.signal.removeEventListener("abort", abortHandler);
} else {
mutationObserver = new MutationObserver((mutations, observer) => {
element = getElement(mutations);
if(element) {
observer.disconnect();
resolve(element);
abortController.signal.removeEventListener("abort", abortHandler);
}
});
mutationObserver.observe(target, { subtree: true, childList:true });
}
})
}
/**
*
* @param {Element} clickTarget
* @param {Element} target
* @param {getElement} getElement
* @param {AbortController} abortController
* @returns {Element|Promise<Element>}
*/
function clickElementAndWaitFor(clickTarget, target, getElement, abortController) {
const promise = waitForElement(target, getElement, abortController);
clickTarget.click();
return getElement() || promise
}
/** @module ui-component Base class for any element that is a part of the UI. */
/**
*
* @abstract
*/
class UIComponent {
/**
*
* @param {Element} root
* @param {object} identifier
*/
constructor(root, identifier={}) {
this.root = root;
this.identifier = identifier;
}
/**
*
* @param {Element} target
* @param {function} getElement
* @param {AbortController} abortController
* @returns {Promise<Element>}
*/
waitForElement(target, getElement, abortController) {
return getElement() || waitForElement(target, getElement, abortController)
}
/**
*
* @param {Element} clickTarget
* @param {Element} target
* @param {function} getElement
* @param {AbortController} abortController
* @returns {Promise<Element>}
*/
clickElementAndWaitFor(clickTarget, target, getElement, abortController) {
return clickElementAndWaitFor(clickTarget, target, getElement, abortController)
}
}
/** @module ui-message UI element representing a message */
/** Locale-independent patterns for the "Unsend" menu item */
const UNSEND_TEXT_VARIANTS = [
"unsend", // English
"annulla invio", // Italian
"retirar", // Portuguese
"deshacer", // Spanish
"retirer", // French
"zurücknehmen", // German
];
/**
* Dispatches pointer and mouse hover events on a target element.
* Instagram's React uses pointer events internally; mouse events alone are insufficient.
*
* @param {Element} target
*/
function dispatchHoverIn(target) {
const rect = target.getBoundingClientRect();
const opts = {
bubbles: true,
cancelable: true,
clientX: rect.x + rect.width / 2,
clientY: rect.y + rect.height / 2,
pointerId: 1,
pointerType: "mouse",
};
target.dispatchEvent(new PointerEvent("pointerenter", { ...opts, bubbles: false }));
target.dispatchEvent(new PointerEvent("pointerover", opts));
target.dispatchEvent(new PointerEvent("pointermove", opts));
target.dispatchEvent(new MouseEvent("mouseenter", { ...opts, bubbles: false }));
target.dispatchEvent(new MouseEvent("mouseover", opts));
target.dispatchEvent(new MouseEvent("mousemove", opts));
}
/**
* Dispatches pointer and mouse leave events on a target element.
*
* @param {Element} target
*/
function dispatchHoverOut(target) {
const rect = target.getBoundingClientRect();
const opts = {
bubbles: true,
cancelable: true,
clientX: rect.x + rect.width / 2,
clientY: rect.y + rect.height / 2,
pointerId: 1,
pointerType: "mouse",
};
target.dispatchEvent(new PointerEvent("pointerout", opts));
target.dispatchEvent(new PointerEvent("pointerleave", { ...opts, bubbles: false }));
target.dispatchEvent(new MouseEvent("mouseout", opts));
target.dispatchEvent(new MouseEvent("mouseleave", { ...opts, bubbles: false }));
}
class UIMessage extends UIComponent {
/**
* Dismiss any stale dialog or dropdown left from a previous failed workflow.
*/
_dismissStaleOverlays() {
const doc = this.root.ownerDocument;
// Close stale confirmation dialogs
const staleDialog = doc.querySelector("[role=dialog]");
if (staleDialog) {
console.debug("Dismissing stale dialog");
const closeBtn = staleDialog.querySelector("button");
if (closeBtn) closeBtn.click();
}
// Close stale dropdown menus by pressing Escape
const activeMenu = doc.querySelector("[role=menu], [role=listbox]");
if (activeMenu) {
console.debug("Dismissing stale menu via Escape");
doc.body.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }));
}
}
/**
* Find the action button within the message row.
* Instagram moved aria-label from the button div to a nested SVG/title.
* Any match (SVG or div) is walked up to the nearest [role=button] ancestor.
*
* @param {Element} scope
* @returns {Element|null}
*/
_findActionButton(scope) {
const LABEL_PATTERNS = [
"[aria-label^='See more options for message']",
"[aria-label*='more options']",
"[aria-label*='More']",
"[aria-label*='Altre opzioni']",
"[aria-label*='opzioni']",
"[aria-label*='opciones']",
"[aria-label*='options']",
];
for (const sel of LABEL_PATTERNS) {
const el = scope.querySelector(sel);
if (el) {
// Always resolve to a clickable button container
const btn = el.closest("[role=button]") || el.closest("button");
if (btn && scope.contains(btn)) return btn
// el itself is already a button-like element
if (el.tagName === "BUTTON" || el.getAttribute("role") === "button") return el
}
}
// Fallback: any role=button with aria-haspopup=menu inside the message row
return scope.querySelector("[role=button][aria-haspopup=menu]")
}
/**
* @param {AbortController} abortController
* @returns {Promise<HTMLButtonElement>}
*/
async showActionsMenuButton(abortController) {
console.debug("Workflow step 1 : showActionsMenuButton", this.root);
this._dismissStaleOverlays();
// Collect all hoverable ancestors from root down to the message bubble.
// Instagram React listens at intermediate levels (role=group, flex-end wrapper).
const hoverTargets = [this.root];
const collectTargets = (el, depth) => {
if (depth > 8) return
for (const child of el.children) {
hoverTargets.push(child);
collectTargets(child, depth + 1);
}
};
collectTargets(this.root, 0);
// Try up to 3 times — hover events can be flaky
for (let attempt = 0; attempt < 3; attempt++) {
if (abortController.signal.aborted) return null
for (const target of hoverTargets) {
dispatchHoverIn(target);
}
await new Promise(resolve => setTimeout(resolve, 100));
const btn = this._findActionButton(this.root);
if (btn) {
console.debug("Workflow step 1 : found action button on attempt", attempt, btn);
return btn
}
console.debug("Workflow step 1 : attempt", attempt, "no button found, retrying...");
dispatchHoverOut(this.root);
await new Promise(resolve => setTimeout(resolve, 50));
}
// Final fallback: use waitForElement with extended timeout
const waitAbortController = new AbortController();
let promiseTimeout;
const abortHandler = () => {
waitAbortController.abort();
clearTimeout(promiseTimeout);
};
abortController.signal.addEventListener("abort", abortHandler);
for (const target of hoverTargets) {
dispatchHoverIn(target);
}
try {
const actionButton = await Promise.race([
this.waitForElement(
this.root,
() => this._findActionButton(this.root),
waitAbortController
),
new Promise((resolve, reject) => {
promiseTimeout = setTimeout(() => reject("Timeout showActionsMenuButton"), 3000);
})
]);
if (actionButton) {
return actionButton
}
return actionButton
} finally {
waitAbortController.abort();
clearTimeout(promiseTimeout);
abortController.signal.removeEventListener("abort", abortHandler);
}
}
/**
* @param {AbortController} abortController
* @returns {Promise<boolean>}
*/
async hideActionMenuButton(abortController) {
console.debug("hideActionMenuButton", this.root);
dispatchHoverOut(this.root);
const noneEl = this.root.querySelector("[role=none]");
if (noneEl) {
dispatchHoverOut(noneEl);
}
const waitAbortController = new AbortController();
let promiseTimeout;
let resolveTimeout;
const abortHandler = () => {
waitAbortController.abort();
clearTimeout(promiseTimeout);
if (resolveTimeout) {
resolveTimeout();
}
};
abortController.signal.addEventListener("abort", abortHandler);
try {
const result = await Promise.race([
this.waitForElement(
this.root,
() => this._findActionButton(this.root) === null,
waitAbortController
),
new Promise((resolve, reject) => {
resolveTimeout = resolve;
promiseTimeout = setTimeout(() => reject("Timeout hideActionMenuButton"), 500);
})
]);
return result
} finally {
waitAbortController.abort();
clearTimeout(promiseTimeout);
abortController.signal.removeEventListener("abort", abortHandler);
}
}
/**
* Opens the actions menu by clicking the action button and waiting for the "Unsend" item.
*
* @param {HTMLButtonElement} actionButton
* @param {AbortController} abortController
* @returns {Promise}
*/
async openActionsMenu(actionButton, abortController) {
console.debug("Workflow step 2 : Clicking actionButton and waiting for unsend menu item to appear", actionButton);
const waitAbortController = new AbortController();
let promiseTimeout;
const abortHandler = () => {
waitAbortController.abort();
clearTimeout(promiseTimeout);
};
abortController.signal.addEventListener("abort", abortHandler);
/** Check if text matches any known "Unsend" variant */
const isUnsendText = (text) => {
const normalized = text.trim().toLocaleLowerCase();
return UNSEND_TEXT_VARIANTS.some(v => normalized === v)
};
try {
const unsendButton = await Promise.race([
this.clickElementAndWaitFor(
actionButton,
this.root.ownerDocument.body,
(mutations) => {
if (mutations) {
const addedNodes = [...mutations.map(mutation => [...mutation.addedNodes])].flat().filter(node => node.nodeType === 1);
for (const addedNode of addedNodes) {
const node = [...addedNode.querySelectorAll("span,div")].find(node => isUnsendText(node.textContent) && node.firstChild?.nodeType === 3);
if (node) {
console.debug("Workflow step 2 : found unsend node via mutation", node);
return node
}
}
}
// Fallback: scan the whole document for an unsend menu item already present
const allSpans = this.root.ownerDocument.querySelectorAll("[role=menu] span, [role=menu] div, [role=menuitem] span, [role=menuitem] div");
for (const span of allSpans) {
if (isUnsendText(span.textContent) && span.firstChild?.nodeType === 3) {
console.debug("Workflow step 2 : found unsend node via document scan", span);
return span
}
}
},
waitAbortController
),
new Promise((resolve, reject) => {
promiseTimeout = setTimeout(() => reject("Timeout openActionsMenu"), 3000);
})
]);
console.debug("Workflow step 2 : Found unsendButton", unsendButton);
return unsendButton
} finally {
waitAbortController.abort();
clearTimeout(promiseTimeout);
abortController.signal.removeEventListener("abort", abortHandler);
}
}
/**
* Closes the actions menu.
*
* @param {HTMLButtonElement} actionButton
* @param {HTMLDivElement} actionsMenuElement
* @param {AbortController} abortController
* @returns {Promise<boolean>}
*/
async closeActionsMenu(actionButton, actionsMenuElement, abortController) {
console.debug("closeActionsMenu");
const waitAbortController = new AbortController();
let promiseTimeout;
const abortHandler = () => {
waitAbortController.abort();
clearTimeout(promiseTimeout);
};
abortController.signal.addEventListener("abort", abortHandler);
try {
const result = await Promise.race([
this.clickElementAndWaitFor(
actionButton,
this.root.ownerDocument.body,
() => this.root.ownerDocument.body.contains(actionsMenuElement) === false,
abortController
),
new Promise((resolve, reject) => {
promiseTimeout = setTimeout(() => reject("Timeout closeActionsMenu"), 500);
})
]);
return result !== null
} finally {
waitAbortController.abort();
clearTimeout(promiseTimeout);
abortController.signal.removeEventListener("abort", abortHandler);
}
}
/**
* Click unsend button and wait for the confirmation dialog.
*
* @param {HTMLSpanElement} unsendButton
* @param {AbortController} abortController
* @returns {Promise<HTMLButtonElement>|Promise<Error>}
*/
openConfirmUnsendModal(unsendButton, abortController) {
console.debug("Workflow step 3 : Clicking unsendButton and waiting for dialog to appear...");
return this.clickElementAndWaitFor(
unsendButton,
this.root.ownerDocument.body,
() => this.root.ownerDocument.querySelector("[role=dialog] button"),
abortController
)
}
/**
* Click unsend confirm button in the modal dialog.
*
* @param {HTMLButtonElement} dialogButton
* @param {AbortController} abortController
* @returns {Promise}
*/
async confirmUnsend(dialogButton, abortController) {
console.debug("Workflow final step : confirmUnsend", dialogButton);
await this.clickElementAndWaitFor(
dialogButton,
this.root.ownerDocument.body,
() => this.root.ownerDocument.querySelector("[role=dialog] button") === null,
abortController
);
}
}
/** @module uipi-message API for UIMessage */
class FailedWorkflowException extends Error {}
class UIPIMessage {
/**
* @param {UIMessage} uiMessage
*/
constructor(uiMessage) {
this._uiMessage = uiMessage;
}
/**
* @param {AbortController} abortController
* @returns {Promise<boolean>}
*/
async unsend(abortController) {
console.debug("UIPIMessage unsend");
let actionButton;
let unsendButton;
try {
actionButton = await this.uiMessage.showActionsMenuButton(abortController);
unsendButton = await this.uiMessage.openActionsMenu(actionButton, abortController);
console.debug("unsendButton", unsendButton);
const dialogButton = await this.uiMessage.openConfirmUnsendModal(unsendButton, abortController);
await this.uiMessage.confirmUnsend(dialogButton, abortController);
this.uiMessage.root.setAttribute("data-idmu-unsent", "");
return true
} catch(ex) {
console.error(ex);
this.uiMessage.root.setAttribute("data-idmu-ignore", "");
// Dismiss any open overlay so the next message starts clean
try {
const doc = this.uiMessage.root.ownerDocument;
doc.body.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }));
await new Promise(resolve => setTimeout(resolve, 200));
// If dialog is still open, press Escape again
if (doc.querySelector("[role=dialog]")) {
doc.body.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }));
await new Promise(resolve => setTimeout(resolve, 200));
}
} catch (_) { /* best-effort cleanup */ }
throw new FailedWorkflowException("Failed to execute workflow for this message", ex)
}
}
/**
* @type {UIMessage}
*/
get uiMessage() {
return this._uiMessage
}
}
/**
*
* @abstract
*/
class UI extends UIComponent {
/**
*
* @abstract
* @returns {UI}
*/
static create() {
}
/**
*
* @abstract
* @param {AbortController} abortController
* @returns {Promise}
*/
/* eslint-disable-next-line no-unused-vars */
async fetchAndRenderThreadNextMessagePage(abortController) {
}
/**
*
* @abstract
* @returns {Promise<UIPIMessage>}
*/
async getNextUIPIMessage() {
}
}
/** @module dom-lookup Utils module for looking up elements on the default UI */
/**
* Returns true when the element looks like the messages scroll container
* (vertically scrollable AND large enough to be the main thread pane).
*
* @param {Element} el
* @param {Window} window
* @returns {boolean}
*/
function isMessagesScrollable(el, window) {
if (!el || el.nodeType !== 1) return false
const style = window.getComputedStyle(el);
if (style.overflowY !== "auto" && style.overflowY !== "scroll") return false
if (el.scrollHeight <= el.clientHeight) return false
const rect = el.getBoundingClientRect();
// Ignore narrow side rails (chats list is typically 320–400 px) and
// tiny widgets. The right-hand thread pane is almost always wider.
return rect.width >= 420 && rect.height >= 200
}
/**
* Returns true when `el` lives in the same visual column as the composer
* (so we don't accidentally pick the chats sidebar) AND sits above it
* (the message composer is always at the bottom of the thread pane).
*
* @param {Element} el
* @param {DOMRect} composerRect
* @returns {boolean}
*/
function isAlignedWithComposer(el, composerRect) {
const r = el.getBoundingClientRect();
const sameColumn = Math.abs(r.left - composerRect.left) <= 80
&& Math.abs(r.right - composerRect.right) <= 80;
const aboveComposer = r.bottom <= composerRect.top + 10;
return sameColumn && aboveComposer
}
/**
* Finds the message composer ([contenteditable=true] textbox at the
* bottom of the thread pane). The composer is the most stable landmark
* in Instagram's DM page — its aria-label changes locale-by-locale but
* the contenteditable+textbox shape doesn't.
*
* @param {Document} doc
* @returns {Element|null}
*/
function findComposer(doc) {
const candidates = [
"[contenteditable=true][aria-label^='Message ']",
"[contenteditable=true][role=textbox][aria-label]",
"div[role=textbox][contenteditable=true]",
"[contenteditable=true][aria-label]",
"[contenteditable=true][role=textbox]",
"[contenteditable=true]",
];
for (const sel of candidates) {
const el = doc.querySelector(sel);
if (el) {
console.debug("findComposer: matched", sel, el);
return el
}
}
return null
}
/**
* Walks up from the composer and finds a scrollable that:
* - isn't an ancestor or descendant of the composer
* - is in the same visual column as the composer
* - sits above the composer
*
* @param {Element} composer
* @param {Window} window
* @returns {Element|null}
*/
function findMessagesScrollableNearby(composer, window) {
const composerRect = composer.getBoundingClientRect();
let cursor = composer.parentElement;
while (cursor && cursor !== cursor.ownerDocument.body) {
for (const el of cursor.querySelectorAll("div")) {
if (el === composer || el.contains(composer) || composer.contains(el)) continue
if (!isMessagesScrollable(el, window)) continue
if (!isAlignedWithComposer(el, composerRect)) continue
return el
}
cursor = cursor.parentElement;
}
return null
}
/**
* Finds the scrollable messages container inside the conversation panel.
*
* Strategy ladder (each step is a fallback for the previous one):
* 1. Locate the message composer and walk up to find a scrollable
* that sits in the SAME visual column as the composer and ABOVE
* it. This is the most reliable signal because:
* - the composer is always in the right thread pane,
* - the chats list (left sidebar) is in a different column.
* 2. Match a known wrapper aria-label (specific patterns only — the
* bare "Conversation" prefix is intentionally NOT used because it
* also matches the plural "Conversations" of the chats list).
* 3. As a last resort, pick the largest scrollable element inside
* <main> that is also aligned with the composer's column.
*
* If everything fails we dump diagnostics to the console so the user
* can paste them back and we can extend the matchers.
*
* @param {Window} window
* @returns {HTMLDivElement|null}
*/
function findMessagesWrapper(window) {
const doc = window.document;
const composer = findComposer(doc);
// 1) composer-anchored search (primary)
if (composer) {
const found = findMessagesScrollableNearby(composer, window);
if (found) {
console.debug("findMessagesWrapper [step 1]: scrollable in composer's column", found);
return found
}
}
// 2) aria-label match — only patterns that uniquely identify the
// open thread (NOT the chats list).
const ariaSelectors = [
"[aria-label^='Messages in conversation with']",
"[aria-label*='Messages in conversation with']",
"[aria-label^='Conversation with ']",
"[aria-label^='Thread with ']",
];
for (const sel of ariaSelectors) {
const conv = doc.querySelector(sel);
if (!conv) continue
console.debug("findMessagesWrapper [step 2]: matched", sel, conv);
const scrollable = findScrollableChild(conv, window);
if (scrollable && (!composer ||
isAlignedWithComposer(scrollable, composer.getBoundingClientRect()))) {
return scrollable
}
if (isMessagesScrollable(conv, window) && (!composer ||
isAlignedWithComposer(conv, composer.getBoundingClientRect()))) {
return conv
}
}
// 3) largest column-aligned scrollable inside <main>
if (composer) {
const composerRect = composer.getBoundingClientRect();
const main = doc.querySelector("main, [role=main]") || doc.body;
let best = null;
let bestArea = 0;
for (const el of main.querySelectorAll("div")) {
if (!isMessagesScrollable(el, window)) continue
if (!isAlignedWithComposer(el, composerRect)) continue
const r = el.getBoundingClientRect();
const area = r.width * r.height;
if (area > bestArea) {
best = el;
bestArea = area;
}
}
if (best) {
console.debug("findMessagesWrapper [step 3]: largest column-aligned scrollable", best);
return best
}
}
// All strategies failed — emit diagnostics for the user to share.
console.error("findMessagesWrapper: no candidate found. Diagnostics follow:");
console.error("URL:", window.location.href);
console.error("composer (raw):", composer);
if (composer) {
console.error("composer outerHTML (first 600 chars):",
(composer.outerHTML || "").slice(0, 600));
console.error("composer rect:", composer.getBoundingClientRect());
console.error("composer ancestor tags up to <main>:",
(function () {
const path = [];
let c = composer;
while (c && c.tagName && c.tagName !== "BODY") {
path.push({
tag: c.tagName,
id: c.id || null,
role: c.getAttribute("role"),
ariaLabel: c.getAttribute("aria-label"),
classCount: (c.className && typeof c.className === "string") ? c.className.split(" ").length : 0,
});
c = c.parentElement;
}
return path
})());
}
console.table([...doc.querySelectorAll("[aria-label]")]
.slice(0, 80)
.map(el => ({
tag: el.tagName,
role: el.getAttribute("role") || "",
label: el.getAttribute("aria-label"),
})));
console.error("[role=row] count:", doc.querySelectorAll("[role=row]").length);
console.error("[role=grid] count:", doc.querySelectorAll("[role=grid]").length);
console.error("[role=gridcell] count:", doc.querySelectorAll("[role=gridcell]").length);
console.error("[contenteditable=true] count:", doc.querySelectorAll("[contenteditable=true]").length);
// Enumerate every scrollable on the page so we can see why none
// passed the column-alignment check.
const allScrollables = [];
for (const el of doc.querySelectorAll("div")) {
const s = window.getComputedStyle(el);
if ((s.overflowY === "auto" || s.overflowY === "scroll") && el.scrollHeight > el.clientHeight) {
const r = el.getBoundingClientRect();
allScrollables.push({
width: Math.round(r.width),
height: Math.round(r.height),
left: Math.round(r.left),
top: Math.round(r.top),
scrollH: el.scrollHeight,
ariaLabel: el.getAttribute("aria-label") || "",
});
}
}
console.error("All scrollable <div>s on the page:");
console.table(allScrollables);
return null
}
/**
* Recursively finds the first scrollable descendant of a given element.
*
* @param {Element} parent
* @param {Window} window
* @returns {HTMLDivElement|null}
*/
function findScrollableChild(parent, window) {
for (const child of parent.children) {
const style = window.getComputedStyle(child);
if (
(style.overflowY === "auto" || style.overflowY === "scroll") &&
child.scrollHeight > child.clientHeight
) {
return child
}
const found = findScrollableChild(child, window);
if (found) {
return found
}
}
return null
}
/**
* Returns the inner container that holds individual message row divs.
*
* Strategy ladder:
* 1. If the scrollable contains [role=row] elements, return their
* nearest common parent.
* 2. Find the deepest descendant whose direct children look like
* message rows (every child is a div, and there are several of
* them — signature of a virtualised message list).
* 3. Fall back to the legacy "div with the most children" heuristic.
*
* @param {Element} scrollable
* @returns {HTMLDivElement}
*/
function getMessagesInnerContainer(scrollable) {
const rows = scrollable.querySelectorAll("[role=row]");
if (rows.length > 0) {
const parent = rows[0].parentElement;
if (parent && scrollable.contains(parent)) {
console.debug("getMessagesInnerContainer: using [role=row] parent,", rows.length, "rows");
return parent
}
}
// Heuristic: a message-list container has many div-only children,
// each of which holds visible content (text/img/svg).
let best = null;
let bestScore = 0;
const visit = (el, depth) => {
if (depth > 6) return
const children = [...el.children];
if (children.length >= 3) {
const divChildren = children.filter(c => c.tagName === "DIV").length;
const richChildren = children.filter(c =>
c.querySelector && (c.querySelector("img,svg,video,canvas") || (c.textContent || "").trim().length > 0)
).length;
const score = divChildren + richChildren;
if (divChildren >= 3 && score > bestScore) {
best = el;
bestScore = score;
}
}
for (const c of children) visit(c, depth + 1);
};
visit(scrollable, 0);
if (best) {
console.debug("getMessagesInnerContainer: heuristic container", best, "score", bestScore);
return best
}
// Last resort: the original "div with most children" pick.
let fallback = scrollable;
let fallbackCount = scrollable.children.length;
const search = (el, depth) => {
if (depth > 3) return
for (const child of el.children) {
if (child.children.length > fallbackCount) {
fallback = child;
fallbackCount = child.children.length;
}
search(child, depth + 1);
}
};
search(scrollable, 0);
return fallback
}
/**
* Determines whether a message element was sent by the current user.
*
* Detection signals, evaluated in order:
* 1. Filter out system rows ("Unsupported message", "You unsent a
* message", "Suggested", "Active now", typing indicators, etc.).
* 2. If the row contains an "Open the profile page of …" link, it's
* an incoming message → false.
* 3. If the bubble's bounding rect right edge is close to the row's
* right edge (and clearly to the right of horizontal centre), the
* bubble is right-aligned → outgoing. This is a layout-driven
* signal that doesn't depend on any aria/role attributes.
* 4. Fall back to the legacy `justify-content: flex-end` walk.
*
* @param {Element} element
* @param {Window} window
* @returns {boolean}
*/
function isSentByCurrentUser(element, window) {
const text = (element.textContent || "").trim();
if (!text && !element.querySelector("img,video,canvas,svg")) return false
if (text.includes("Unsupported message")) return false
if (text.includes("You unsent a message")) return false
// Skip system rows that have negligible structure (date dividers,
// "Active now" banners, typing indicator, etc.).
const ROW_BLOCKLIST = [
"Active now",
"Active ",
"Seen ",
"You sent an attachment",
"sent an attachment",
];
for (const phrase of ROW_BLOCKLIST) {
if (text === phrase || text.startsWith(phrase)) return false
}
const profileLink = element.querySelector("a[aria-label^='Open the profile page of'], a[role=link][href^='/']");
if (profileLink) return false
// Layout-driven right-alignment: pick the deepest descendant that
// actually paints (bubble container) and compare its right edge to
// the row's right edge.
const rowRect = element.getBoundingClientRect();
if (rowRect.width > 100) {
const candidates = element.querySelectorAll("div");
for (const c of candidates) {
const r = c.getBoundingClientRect();
if (r.width < 20 || r.height < 16) continue
if (r.width > rowRect.width * 0.85) continue
// Bubble is right-aligned when its right edge hugs the row's
// right edge AND its left edge sits past the row centre.
const hugsRight = (rowRect.right - r.right) < 24;
const pastCentre = r.left > rowRect.left + rowRect.width * 0.35;
if (hugsRight && pastCentre) {
return true
}
}
}
const queue = [{ el: element, depth: 0 }];
while (queue.length > 0) {
const { el, depth } = queue.shift();
const s = window.getComputedStyle(el);
if (s.justifyContent === "flex-end") {
return true
}
if (depth < 8) {
for (const child of el.children) {
queue.push({ el: child, depth: depth + 1 });
}
}
}
return false
}
/**
* Gets the first visible message sent by the current user that hasn't been processed yet.
*
* @param {Element} root - The scrollable messages wrapper
* @param {AbortController} abortController
* @param {Window} window
* @returns {Element|undefined}
*/
function getFirstVisibleMessage(root, abortController, window) {
const innerContainer = getMessagesInnerContainer(root);
if (!innerContainer) {
console.debug("getFirstVisibleMessage: no inner container found");
return
}
// Prefer [role=row] children when present, else direct children.
let candidates = [...innerContainer.querySelectorAll(":scope > [role=row]")];
if (candidates.length === 0) {
candidates = [...innerContainer.children];
}
console.debug("getFirstVisibleMessage: scanning", candidates.length, "rows");
// Diagnostic counter so we can see why all candidates were filtered.
let rejectedNoMarkers = 0;
let rejectedNotOurs = 0;
const elements = candidates.filter(d => {
if (d.hasAttribute("data-idmu-ignore")) return false
if (d.hasAttribute("data-idmu-unsent")) return false
// Permissive content check: accept anything with text, an image,
// or recognised role markers. We DO NOT require a role marker
// because some IG variants ship rows without role attributes.
const hasContent =
d.querySelector("[role=gridcell]") ||
d.querySelector("[role=none]") ||
d.querySelector("[role=presentation]") ||
d.querySelector("img,svg,video,canvas") ||
(d.textContent || "").trim().length > 0;
if (!hasContent) {
rejectedNoMarkers++;
return false
}
if (!isSentByCurrentUser(d, window)) {
rejectedNotOurs++;
return false
}
return true
});
elements.reverse();
if (elements.length >= 1) {
console.debug("getFirstVisibleMessage:", elements.length, "candidates after filter",
"(rejected: empty=" + rejectedNoMarkers + ", incoming/system=" + rejectedNotOurs + ")");
} else if (candidates.length > 0) {
console.debug("getFirstVisibleMessage: 0 candidates (rejected: empty=" +
rejectedNoMarkers + ", incoming/system=" + rejectedNotOurs + ")");
} else {
console.debug("getFirstVisibleMessage: 0 candidate rows in inner container");
}
for (const element of elements) {
if (abortController.signal.aborted) {
console.debug("abortController interupted the message filtering process: stopping...");
break
}
const visibilityCheck = element.checkVisibility({
visibilityProperty: true,
contentVisibilityAuto: true,
opacityProperty: true,
});
if (visibilityCheck === false) {
console.debug("visibilityCheck failed");
continue
}
const rect = element.getBoundingClientRect();
if (rect.y + rect.height < 0 || rect.height === 0) {
console.debug("isInView failed", rect.y, rect.height);
continue
}
element.setAttribute("data-idmu-ignore", "");
console.debug("Message in view, testing workflow...", element);
return element
}
}
/**
* Scrolls to top to trigger loading of older messages.
* Handles both normal and column-reverse layouts.
*
* In column-reverse (Instagram's current layout):
* scrollTop=0 is the BOTTOM (newest messages)
* scrollTop=-(scrollHeight-clientHeight) is the TOP (oldest messages)
*
* @param {Element} root
* @param {AbortController} abortController
* @returns {Promise<boolean>}
*/
async function loadMoreMessages(root, abortController) {
console.debug("loadMoreMessages looking for loader... ");
const scrollAbortController = new AbortController();
let findLoaderTimeout;
let resolveTimeout;
const abortHandler = () => {
scrollAbortController.abort();
clearTimeout(findLoaderTimeout);
if (resolveTimeout) {
resolveTimeout();
}
};
abortController.signal.addEventListener("abort", abortHandler);
// Detect column-reverse layout
const style = root.ownerDocument.defaultView.getComputedStyle(root);
const isReversed = style.flexDirection === "column-reverse";
// In column-reverse, "scroll to top" means most negative scrollTop
const scrollToTopValue = isReversed
? -(root.scrollHeight - root.clientHeight)
: 0;
// In column-reverse, "at top" means scrollTop is at or near minimum
const isAtTop = () => isReversed
? root.scrollTop <= scrollToTopValue + 5
: root.scrollTop === 0;
const beforeScroll = root.scrollTop;
const beforeHeight = root.scrollHeight;
root.scrollTop = scrollToTopValue;
// Helper: find a visible loader within the scrollable root's viewport
const findVisibleLoader = () => {
const bars = root.querySelectorAll("[role=progressbar]");
for (const bar of bars) {
const rect = bar.getBoundingClientRect();
const rootRect = root.getBoundingClientRect();
// Must be within root's horizontal+vertical bounds and have dimensions
if (rect.height > 0 && rect.y >= rootRect.y - 100 && rect.y <= rootRect.y + rootRect.height + 100) {
return bar
}
}
return null
};
// Short chat: everything fits in viewport, nothing to load
const noScrollNeeded = isReversed
? beforeScroll === 0 && root.scrollHeight <= root.clientHeight + 50
: beforeScroll === 0 && root.scrollHeight <= root.clientHeight + 50;
if (noScrollNeeded) {
console.debug("loadMoreMessages: chat fits in viewport, marking as done");
abortController.signal.removeEventListener("abort", abortHandler);
return true
}
// Already at top after scrolling: wait briefly for new content, then check
if (isAtTop()) {
// Give Instagram a moment to start loading older messages
await new Promise(resolve => setTimeout(resolve, 500));
// Check if a visible loader appeared
const loader = findVisibleLoader();
if (loader) {
console.debug("loadMoreMessages: Found visible loader after scroll; waiting for removal (max 5s)");
await Promise.race([
waitForElement(root, () => findVisibleLoader() === null, abortController),
new Promise(resolve => setTimeout(resolve, 5000))
]);
abortController.signal.removeEventListener("abort", abortHandler);
const grew = root.scrollHeight > beforeHeight;
console.debug(`loadMoreMessages: loader phase done, content ${grew ? "grew" : "did not grow"}`);
return !grew
}
// No loader appeared — check if scrollHeight grew (new content loaded without spinner)
const grew = root.scrollHeight > beforeHeight;
if (!grew) {
console.debug("loadMoreMessages: at top, no loader, no new content — reached last page");
abortController.signal.removeEventListener("abort", abortHandler);
return true
}
}
// Fallback: wait for progressbar to appear (with shorter timeout)
let loadingElement;
try {
loadingElement = await Promise.race([
waitForElement(root, () => {
if (findVisibleLoader() === null) {
root.scrollTop = scrollToTopValue;
}
return findVisibleLoader()
}, scrollAbortController),
new Promise(resolve => {
resolveTimeout = resolve;
findLoaderTimeout = setTimeout(() => {
resolve();
}, 3000);
})
]);
} catch (ex) {
console.error(ex);
}
scrollAbortController.abort();
abortController.signal.removeEventListener("abort", abortHandler);
clearTimeout(findLoaderTimeout);
if (loadingElement && loadingElement !== true) {
console.debug("loadMoreMessages: Found loader; Stand-by until it is removed (max 5s)");
await Promise.race([
waitForElement(root, () => findVisibleLoader() === null, abortController),
new Promise(resolve => setTimeout(resolve, 5000))
]);
}
const atTop = isAtTop();
console.debug(`loadMoreMessages: scrollTop is ${root.scrollTop} — ${atTop ? "reached last page" : "not last page"}`);
return atTop
}
/** @module ui-messages-wrapper UI element representing the messages wrapper */
class UIMessagesWrapper extends UIComponent {
/**
* @param {AbortController} abortController
* @returns {Promise}
*/
fetchAndRenderThreadNextMessagePage(abortController) {
return loadMoreMessages(this.root, abortController)
}
}
/** @module default-ui Default UI / English UI */
class DefaultUI extends UI {
constructor(root, identifier = {}) {
super(root, identifier);
this.lastScrollTop = null;
}
/**
* @param {Window} window
* @returns {DefaultUI}
*/
static create(window) {
console.debug("UI create: Looking for messagesWrapperElement");
const messagesWrapperElement = findMessagesWrapper(window);
if (messagesWrapperElement !== null) {
console.debug("Found messagesWrapperElement", messagesWrapperElement);
const uiMessagesWrapper = new UIMessagesWrapper(messagesWrapperElement);
return new DefaultUI(window, { uiMessagesWrapper })
} else {
throw new Error("Unable to find messagesWrapperElement. The query selector might be out of date.")
}
}
/**
* @param {AbortController} abortController
* @returns {Promise}
*/
async fetchAndRenderThreadNextMessagePage(abortController) {
console.debug("UI fetchAndRenderThreadNextMessagePage");
return await this.identifier.uiMessagesWrapper.fetchAndRenderThreadNextMessagePage(abortController)
}
/**
* Scroll until a (visible) message is found and return it.
*
* Instagram uses flex-direction: column-reverse on the messages container.
* This means scrollTop=0 is the BOTTOM (newest messages) and scrolling to
* older messages requires NEGATIVE scrollTop values.
* In normal (non-reversed) layouts, scrollTop=0 is the top and the max is positive.
*
* This method detects the layout direction and scrolls accordingly.
*
* @param {AbortController} abortController
* @returns {Promise<UIPIMessage|false>}
*/
async getNextUIPIMessage(abortController) {
console.debug("UI getNextUIPIMessage", this.lastScrollTop);
const uiMessagesWrapperRoot = this.identifier.uiMessagesWrapper.root;
const style = this.root.getComputedStyle
? this.root.getComputedStyle(uiMessagesWrapperRoot)
: uiMessagesWrapperRoot.ownerDocument.defaultView.getComputedStyle(uiMessagesWrapperRoot);
const isReversed = style.flexDirection === "column-reverse";
// Pre-check: try the current scroll position first. After an unsend the DOM
// shrinks and the next eligible message may already be in viewport without
// any scrolling needed. This is also the only path that works for short
// threads where the entire conversation fits on screen.
try {
const messageElement = getFirstVisibleMessage(uiMessagesWrapperRoot, abortController, this.root);
if (messageElement) {
console.debug("getNextUIPIMessage: found message without scrolling");
const uiMessage = new UIMessage(messageElement);
return new UIPIMessage(uiMessage)
}
} catch (ex) {
console.error(ex);
}
// Allow up to 3 full passes; covers cases where DOM shrinks after unsends
for (let pass = 0; pass < 3; pass++) {
if (abortController.signal.aborted) {
console.debug("abortController interupted the scrolling: stopping...");
return false
}
if (isReversed) {
const minScroll = -(uiMessagesWrapperRoot.scrollHeight - uiMessagesWrapperRoot.clientHeight);
const startPos = (pass === 0 && this.lastScrollTop !== null)
? Math.max(this.lastScrollTop, minScroll)
: 0;
// Use small steps for short conversations to avoid overshooting partially-visible messages.
const totalRange = Math.abs(minScroll);
const step = totalRange < 500 ? 30 : 150;
console.debug(`getNextUIPIMessage [reversed] pass=${pass}, startPos=${startPos}, minScroll=${minScroll}, step=${step}`);
for (let i = startPos; i >= minScroll; i = i - step) {
if (abortController.signal.aborted) {
console.debug("abortController interupted the scrolling: stopping...");
return false
}
this.lastScrollTop = i;
uiMessagesWrapperRoot.scrollTop = i;
uiMessagesWrapperRoot.dispatchEvent(new this.root.Event("scroll"));
await new Promise(resolve => setTimeout(resolve, 5));
try {
const messageElement = getFirstVisibleMessage(uiMessagesWrapperRoot, abortController, this.root);
if (messageElement) {
const uiMessage = new UIMessage(messageElement);
return new UIPIMessage(uiMessage)
}
} catch (ex) {
console.error(ex);
}
}
} else {
const maxScroll = uiMessagesWrapperRoot.scrollHeight - uiMessagesWrapperRoot.clientHeight;
const startScrollTop = (pass === 0 && this.lastScrollTop !== null)
? Math.min(this.lastScrollTop, maxScroll)
: maxScroll;
const step = maxScroll < 500 ? 30 : 150;
console.debug(`getNextUIPIMessage pass=${pass}, startScrollTop=${startScrollTop}, maxScroll=${maxScroll}, step=${step}`);
for (let i = Math.max(1, startScrollTop); i > 0; i = i - step) {
if (abortController.signal.aborted) {
console.debug("abortController interupted the scrolling: stopping...");
return false
}
this.lastScrollTop = i;
uiMessagesWrapperRoot.scrollTop = i;
uiMessagesWrapperRoot.dispatchEvent(new this.root.Event("scroll"));
await new Promise(resolve => setTimeout(resolve, 5));
try {
const messageElement = getFirstVisibleMessage(uiMessagesWrapperRoot, abortController, this.root);
if (messageElement) {
const uiMessage = new UIMessage(messageElement);
return new UIPIMessage(uiMessage)
}
} catch (ex) {
console.error(ex);
}
}
}
this.lastScrollTop = null;
console.debug(`getNextUIPIMessage: pass ${pass} found nothing, retrying`);
}
console.debug("getNextUIPIMessage: exhausted all passes, no messages left");
return false
}
}
/** @module get-ui UI loader module. Allow loading of a certain UI based on a given strategy (locale etc..)
* There might be need for multiple UI as Instagram might serve different apps based on location for example.
* There is also a need to internationalize each ui so that it doesn't fail if we change the language.
*/
/**
*
* @returns {UI}
*/
function getUI() {
return DefaultUI
}
/** @module uipi API for UI */
/**
* UI Interface API
*/
class UIPI {
/**
*
* @param {UI} ui
*/
constructor(ui) {
this._ui = ui;
}
/**
*
* @param {Window} window
* @returns {UIPI}
*/
static create(window) {
console.debug("UIPI.create");
const ui = getUI().create(window);
return new UIPI(ui)
}
/**
* @param {AbortController} abortController
* @returns {Promise}
*/
fetchAndRenderThreadNextMessagePage(abortController) {
console.debug("UIPI fetchAndRenderThreadNextMessagePage");
return this.ui.fetchAndRenderThreadNextMessagePage(abortController)
}
/**
* @param {AbortController} abortController
* @returns {Promise<UIPIMessage>}
*/
getNextUIPIMessage(abortController) {
console.debug("UIPI getNextUIPIMessage");
return this.ui.getNextUIPIMessage(abortController)
}
/**
*
* @type {UI}
*/
get ui() {
return this._ui
}
}
/** @module idmu Global/Main API for interacting with the UI */
class IDMU {
/**
*
* @param {Window} window
* @param {callback} onStatusText
*/
constructor(window, onStatusText) {
this.window = window;
this.uipi = null;
this.onStatusText = onStatusText;
}
/**
* @param {AbortController} abortController
* @returns {Promise<UIPIMessage>}
*/
getNextUIPIMessage(abortController) {
return this.uipi.getNextUIPIMessage(abortController)
}
/**
*
* @param {string} text
*/
setStatusText(text) {
this.onStatusText(text);
}
/**
*
* @param {AbortController} abortController
* @returns {Promise}
*/
fetchAndRenderThreadNextMessagePage(abortController) {
return this.uipi.fetchAndRenderThreadNextMessagePage(abortController)
}
/**
* Map Instagram UI
*/
loadUIPI() {
console.debug("loadUIPI");
this.uipi = UIPI.create(this.window);
}
}
/** @module unsend-strategy Various strategies for unsending messages */
/**
*
* @abstract
*/
class UnsendStrategy {
/**
*
* @param {IDMU} idmu
*/
constructor(idmu) {
this._idmu = idmu;
}
/**
*
* @abstract
* @returns {boolean}
*/
isRunning() {
}
/**
*
* @abstract
*/
stop() {
}
/**
*
* @abstract
*/
reset() {
}
/**
*
* @abstract
*/
async run() {
}
/**
* @readonly
* @type {IDMU}
*/
get idmu() {
return this._idmu
}
}
/** @module unsend-strategy Various strategies for unsending messages */
/**
* Loads all pages first, then unsends messages from bottom to top.
* For short conversations (all messages fit in viewport), skips page loading entirely.
*/
class DefaultStrategy extends UnsendStrategy {
/**
* @param {IDMU} idmu
*/
constructor(idmu) {
super(idmu);
this._allPagesLoaded = false;
this._unsentCount = 0;
this._pagesLoadedCount = 0;
this._running = false;
this._abortController = null;
this._lastUnsendDate = null;
this._consecutiveFailures = 0;
}
/**
* @returns {boolean}
*/
isRunning() {
return this._running && this._abortController && this._abortController.signal.aborted === false
}
stop() {
console.debug("DefaultStrategy stop");
this.idmu.setStatusText("Stopping...");
this._abortController.abort();
}
reset() {
this._allPagesLoaded = false;
this._unsentCount = 0;
this._lastUnsendDate = null;
this._pagesLoadedCount = 0;
this._consecutiveFailures = 0;
this.idmu.setStatusText("Ready");
}
/**
* @returns {Promise}
*/
async run() {
console.debug("DefaultStrategy.run()");
this._unsentCount = 0;
this._pagesLoadedCount = 0;
this._consecutiveFailures = 0;
this._running = true;
this._abortController = new AbortController();
// Clear stale ignore markers from previous runs so messages can be retried
this.idmu.window.document.querySelectorAll("[data-idmu-ignore]").forEach(el => {
el.removeAttribute("data-idmu-ignore");
});
this.idmu.loadUIPI();
try {
if (this._allPagesLoaded) {
await this.#unsendNextMessage();
} else {
await this.#loadNextPage();
}
// Race condition: on first page load, Instagram's React may not have
// finished hydrating message components (role attributes missing).
// If we found nothing, wait and re-scan up to 3 times.
if (this._unsentCount === 0 && !this._abortController.signal.aborted) {
for (let retry = 1; retry <= 3; retry++) {
this.idmu.setStatusText(`No messages detected, retrying (${retry}/3)...`);
console.debug(`DefaultStrategy: 0 messages found, retry ${retry}/3`);
await new Promise(resolve => setTimeout(resolve, 2000));
if (this._abortController.signal.aborted) break
// Reset for fresh scan
this._allPagesLoaded = false;
this._consecutiveFailures = 0;
this.idmu.window.document.querySelectorAll("[data-idmu-ignore]").forEach(el => {
el.removeAttribute("data-idmu-ignore");
});
this.idmu.loadUIPI();
await this.#loadNextPage();
if (this._unsentCount > 0 || this._abortController.signal.aborted) break
}
}
if (this._abortController.signal.aborted) {
this.idmu.setStatusText(`Aborted. ${this._unsentCount} message(s) unsent.`);
console.debug("DefaultStrategy aborted");
} else {
this.idmu.setStatusText(`Done. ${this._unsentCount} message(s) unsent.`);
console.debug("DefaultStrategy done");
}
} catch (ex) {
console.error(ex);
this.idmu.setStatusText(`Errored. ${this._unsentCount} message(s) unsent.`);
console.debug("DefaultStrategy errored");
}
this._running = false;
}
/**
* Tries to load the thread next page.
* If loadMoreMessages returns true (no more pages), moves to unsending.
*/
async #loadNextPage() {
if (this._abortController.signal.aborted) {
console.debug("abortController interupted the loading of next page: stopping...");
return
}
this.idmu.setStatusText("Loading next page...");
try {
const done = await this.idmu.fetchAndRenderThreadNextMessagePage(this._abortController);
if (this._abortController.signal.aborted === false) {
if (done) {
this.idmu.setStatusText(`All pages loaded (${this._pagesLoadedCount} in total). Unsending...`);
this._allPagesLoaded = true;
await this.#unsendNextMessage();
} else {
this._pagesLoadedCount++;
await this.#loadNextPage();
}
} else {
console.debug("abortController interupted the loading of next page: stopping...");
}
} catch (ex) {
console.error(ex);
}
}
/**
* Unsend first message in viewport.
* Uses human-like randomized delays and exponential backoff to avoid Instagram rate limits.
*/
async #unsendNextMessage() {
if (this._abortController.signal.aborted) {
console.debug("abortController interupted the unsending of next message: stopping...");
return
}
if (this._consecutiveFailures >= 5) {
this.idmu.setStatusText(`Stopped: ${this._consecutiveFailures} consecutive failures. ${this._unsentCount} message(s) unsent.`);
console.debug("DefaultStrategy stopping due to consecutive failures");
return
}
let canScroll = true;
let msgElement = null;
try {
this.idmu.setStatusText(`Retrieving next message... (${this._unsentCount} unsent so far)`);
const uipiMessage = await this.idmu.getNextUIPIMessage(this._abortController);
canScroll = uipiMessage !== false;
if (uipiMessage) {
this.idmu.setStatusText(`Unsending message... (${this._unsentCount + 1})`);
// Adaptive baseline delay: 1-2s randomized between unsends.
// Exponential backoff (below) only kicks in when a rate limit is
// actually detected, keeping normal-case throughput high.
if (this._lastUnsendDate !== null) {
const elapsed = Date.now() - this._lastUnsendDate.getTime();
const minDelay = 1000 + Math.floor(Math.random() * 1000);
if (elapsed < minDelay) {
const waitMs = minDelay - elapsed;
this.idmu.setStatusText(`Waiting ${(waitMs / 1000).toFixed(1)}s... (${this._unsentCount} unsent so far)`);
await new Promise(resolve => setTimeout(resolve, waitMs));
}
}
if (this._abortController.signal.aborted) return
msgElement = uipiMessage.uiMessage.root;
const unsent = await uipiMessage.unsend(this._abortController);
if (unsent) {
// Verify the message actually disappeared from DOM (server accepted the mutation).
// On a successful unsend, Instagram either removes the row entirely (isConnected=false)
// or replaces the bubble with a "You unsent a message" placeholder (no [role=gridcell]
// / [role=presentation] / [role=none] descendants left). On a server-rejected unsend
// (rate limit), the original bubble snaps back into place.
await new Promise(resolve => setTimeout(resolve, 800));
const bubbleStillPresent = msgElement.isConnected && (
msgElement.querySelector("[role=gridcell]") ||
msgElement.querySelector("[role=presentation]") ||
msgElement.querySelector("[role=none]")
) && !(msgElement.textContent || "").includes("You unsent a message");
const stillInDOM = !!bubbleStillPresent;
if (stillInDOM) {
// Server likely rejected — the message reappeared after optimistic removal
console.debug("DefaultStrategy: message still in DOM after unsend, possible rate limit");
msgElement.removeAttribute("data-idmu-ignore");
this._consecutiveFailures++;
const backoffMs = Math.min(60000, 5000 * Math.pow(2, this._consecutiveFailures - 1));
this.idmu.setStatusText(`Server may have rejected unsend. Backing off ${(backoffMs / 1000).toFixed(0)}s... (${this._unsentCount} unsent)`);
await new Promise(resolve => setTimeout(resolve, backoffMs));
} else {
this._lastUnsendDate = new Date();
this._unsentCount++;
this._consecutiveFailures = 0;
// DOM shrunk after removal; reset scroll for fresh scan
if (this.idmu.uipi && this.idmu.uipi.ui) {
this.idmu.uipi.ui.lastScrollTop = null;
}
}
} else {
// Unsend workflow returned false — allow retry on next pass
console.debug("DefaultStrategy: unsend returned false, removing ignore marker for retry");
msgElement.removeAttribute("data-idmu-ignore");
this._consecutiveFailures++;
}
}
} catch (ex) {
console.error(ex);
// Remove ignore marker so this message can be retried
if (msgElement) {
msgElement.removeAttribute("data-idmu-ignore");
}
this._consecutiveFailures++;
const backoffMs = Math.min(60000, 3000 * Math.pow(2, this._consecutiveFailures - 1));
this.idmu.setStatusText(`Workflow failed (${this._consecutiveFailures}/5), retrying in ${(backoffMs / 1000).toFixed(0)}s... (${this._unsentCount} unsent)`);
await new Promise(resolve => setTimeout(resolve, backoffMs));
} finally {
if (canScroll && this._abortController && !this._abortController.signal.aborted) {
await this.#unsendNextMessage();
}
}
}
}
/** @module alert Alert UI */
/**
*
* @param {Document} document
* @returns {HTMLButtonElement}
*/
function createAlertsWrapperElement(document) {
const alertsWrapperElement = document.createElement("div");
alertsWrapperElement.id = "idmu-alerts";
alertsWrapperElement.style.position = "fixed";
alertsWrapperElement.style.top = "20px";
alertsWrapperElement.style.right = "20px";
alertsWrapperElement.style.display = "grid";
return alertsWrapperElement
}
/** @module overlay IDMU's overlay */
/**
* @param {Document} document
* @returns {HTMLDivElement}
*/
function createOverlayElement(document) {
const overlayElement = document.createElement("div");
overlayElement.id = "idmu-overlay";
overlayElement.tabIndex = 0;
overlayElement.style.top = "0";
overlayElement.style.right = "0";
overlayElement.style.position = "fixed";
overlayElement.style.width = "100vw";
overlayElement.style.height = "100vh";
overlayElement.style.zIndex = "998";
overlayElement.style.backgroundColor = "#000000d6";
overlayElement.style.display = "none";
return overlayElement
}
/** @module ui IDMU's own ui/overlay
* Provide a button to unsend messages
*/
class OSD {
/**
*
* @param {Document} document
* @param {HTMLDivElement} root
* @param {HTMLDivElement} overlayElement
* @param {HTMLDivElement} menuElement
* @param {HTMLButtonElement} unsendThreadMessagesButton
* @param {HTMLDivElement} statusElement
*/
constructor(document, root, overlayElement, menuElement, unsendThreadMessagesButton, statusElement) {
this._document = document;
this._root = root;
this._overlayElement = overlayElement;
this._menuElement = menuElement;
this._statusElement = statusElement;
this._unsendThreadMessagesButton = unsendThreadMessagesButton;
this._idmu = new IDMU(this.window, this.onStatusText.bind(this));
this._strategy = new DefaultStrategy(this._idmu); // TODO move out
}
/**
*
* @param {window} window
* @returns {OSD}
*/
static render(window) {
console.debug("render");
const ui = OSD.create(window.document);
window.document.body.appendChild(ui.root);
return ui
}
/**
*
* @param {Document} document
* @returns {OSD}
*/
static create(document) {
const root = document.createElement("div");
root.id = "idmu-root";
const menuElement = createMenuElement(document);
const overlayElement = createOverlayElement(document);
const alertsWrapperElement = createAlertsWrapperElement(document);
const unsendThreadMessagesButton = createMenuButtonElement(document, "Unsend all DMs", BUTTON_STYLE.PRIMARY);
const statusElement = document.createElement("div");
statusElement.textContent = "Ready";
statusElement.id = "idmu-status";
statusElement.style = "width: 200px";
document.body.appendChild(overlayElement);
document.body.appendChild(alertsWrapperElement);
menuElement.appendChild(unsendThreadMessagesButton);
menuElement.appendChild(statusElement);
root.appendChild(menuElement);
const ui = new OSD(document, root, overlayElement, menuElement, unsendThreadMessagesButton, statusElement);
document.addEventListener("keydown", (event) => ui.#onWindowKeyEvent(event)); // TODO test
document.addEventListener("keyup", (event) => ui.#onWindowKeyEvent(event)); // TODO test
unsendThreadMessagesButton.addEventListener("click", (event) => ui.#onUnsendThreadMessagesButtonClick(event));
ui._mutationObserver = new MutationObserver((mutations) => ui.#onMutations(ui, mutations));
ui._mutationObserver.observe(document.body, { childList: true }); // TODO test
unsendThreadMessagesButton.dataTextContent = unsendThreadMessagesButton.textContent;
unsendThreadMessagesButton.dataBackgroundColor = unsendThreadMessagesButton.style.backgroundColor;
return ui
}
/**
*
* @param {string} text
*/
onStatusText(text) {
this.statusElement.textContent = text;
}
async #startUnsending() {
[...this.menuElement.querySelectorAll("button")].filter(button => button !== this.unsendThreadMessagesButton).forEach(button => {
button.style.visibility = "hidden";
button.disabled = true;
});
this.overlayElement.style.display = "";
this.overlayElement.focus();
this.unsendThreadMessagesButton.textContent = "Stop processing";
this.unsendThreadMessagesButton.style.backgroundColor = "#FA383E";
this.statusElement.style.color = "white";
this._mutationObserver.disconnect();
try {
await this.strategy.run();
} catch(error) {
console.error(error);
if(this.strategy.isRunning()) {
this.strategy.stop();
}
this.statusElement.innerHTML = `<span style="color: red">An error occured, <a href="https://github.com/thoughtsunificator/instagram-dm-unsender/issues/new?template=bug_report.md">please open an issue</a></span>`;
} finally {
this.#onUnsendingFinished();
}
}
/**
*
* @param {OSD} ui
*/
#onMutations(ui) {
if(ui.root.ownerDocument.querySelector("[id^=mount] > div > div > div") !== null && ui) {
if(this._mutationObserver) {
this._mutationObserver.disconnect();
}
this._mutationObserver = new MutationObserver(ui.#onMutations.bind(this, ui));
this._mutationObserver.observe(ui.root.ownerDocument.querySelector("[id^=mount] > div > div > div"), { childList: true, attributes: true });
}
if(this.window.location.pathname.startsWith("/direct/t/")) {
if(!this.strategy.isRunning()) {
this.strategy.reset();
}
this.root.style.display = "";
} else {
this.root.style.display = "none";
if(this.strategy.isRunning()) {
this.strategy.stop();
}
}
}
/**
*
* @param {OSD} ui
* @param {Event} event
*/
#onUnsendThreadMessagesButtonClick() {
if(this.strategy.isRunning()) {
console.debug("User asked for messages unsending to stop");
this.strategy.stop();
this.#onUnsendingFinished();
} else {
console.debug("User asked for messages unsending to start; UI interaction will be disabled in the meantime");
this.#startUnsending();
}
}
/**
*
* @param {Event} event
* @returns {boolean}
*/
#onWindowKeyEvent(event) {
if(this.strategy.isRunning()) {
console.log("User interaction is disabled as the unsending is still running; Please stop the execution first.");
event.stopImmediatePropagation();
event.preventDefault();
event.stopPropagation();
this.overlayElement.focus();
return false
}
}
#onUnsendingFinished() {
console.debug("render onUnsendingFinished")
;[...this.menuElement.querySelectorAll("button")].filter(button => button !== this.unsendThreadMessagesButton).forEach(button => {
button.style.visibility = "";
button.disabled = false;
});
this.unsendThreadMessagesButton.textContent = this.unsendThreadMessagesButton.dataTextContent;
this.unsendThreadMessagesButton.style.backgroundColor = this.unsendThreadMessagesButton.dataBackgroundColor;
this.overlayElement.style.display = "none";
this.statusElement.style.color = "";
this._mutationObserver.observe(this._document.body, { childList: true }); // TODO test
}
/**
* @readonly
* @type {Document}
*/
get document() {
return this._document
}
/**
* @readonly
* @type {Window}
*/
get window() {
return this._document.defaultView
}
/**
* @readonly
* @type {HTMLDivElement}
*/
get root() {
return this._root
}
/**
* @readonly
* @type {HTMLDivElement}
*/
get overlayElement() {
return this._overlayElement
}
/**
* @readonly
* @type {HTMLDivElement}
*/
get menuElement() {
return this._menuElement
}
/**
* @readonly
* @type {HTMLButtonElement}
*/
get unsendThreadMessagesButton() {
return this._unsendThreadMessagesButton
}
/**
* @readonly
* @type {HTMLDivElement}
*/
get statusElement() {
return this._statusElement
}
/**
* @readonly
* @type {UnsendStrategy}
*/
get strategy() { // TODO move out
return this._strategy
}
/**
* @readonly
* @type {IDMU}
*/
get idmu() {
return this._idmu
}
}
/** @module main Main module */
/**
* @param {Window} window
*/
function main(window) {
OSD.render(window);
}
if(typeof window !== "undefined") {
main(window);
}
exports.main = main;
return exports;
})({});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWRtdS51c2VyLmpzIiwic291cmNlcyI6WyIuLi9zcmMvcnVudGltZS91c2Vyc2NyaXB0L29zZC9zdHlsZS9pbnN0YWdyYW0uanMiLCIuLi9zcmMvcnVudGltZS91c2Vyc2NyaXB0L29zZC9tZW51LWJ1dHRvbi5qcyIsIi4uL3NyYy9ydW50aW1lL3VzZXJzY3JpcHQvb3NkL21lbnUuanMiLCIuLi9zcmMvZG9tL2FzeW5jLWV2ZW50cy5qcyIsIi4uL3NyYy91aS91aS1jb21wb25lbnQuanMiLCIuLi9zcmMvdWkvZGVmYXVsdC91aS1tZXNzYWdlLmpzIiwiLi4vc3JjL3VpcGkvdWlwaS1tZXNzYWdlLmpzIiwiLi4vc3JjL3VpL3VpLmpzIiwiLi4vc3JjL3VpL2RlZmF1bHQvZG9tLWxvb2t1cC5qcyIsIi4uL3NyYy91aS9kZWZhdWx0L3VpLW1lc3NhZ2VzLXdyYXBwZXIuanMiLCIuLi9zcmMvdWkvZGVmYXVsdC9kZWZhdWx0LXVpLmpzIiwiLi4vc3JjL3VpL2dldC11aS5qcyIsIi4uL3NyYy91aXBpL3VpcGkuanMiLCIuLi9zcmMvaWRtdS9pZG11LmpzIiwiLi4vc3JjL3VpL3Vuc2VuZC1zdHJhdGVneS5qcyIsIi4uL3NyYy91aS9kZWZhdWx0L3Vuc2VuZC1zdHJhdGVneS5qcyIsIi4uL3NyYy9ydW50aW1lL3VzZXJzY3JpcHQvb3NkL2FsZXJ0LmpzIiwiLi4vc3JjL3J1bnRpbWUvdXNlcnNjcmlwdC9vc2Qvb3ZlcmxheS5qcyIsIi4uL3NyYy9ydW50aW1lL3VzZXJzY3JpcHQvb3NkL29zZC5qcyIsIi4uL3NyYy9ydW50aW1lL3VzZXJzY3JpcHQvbWFpbi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiogQG1vZHVsZSBpbnN0YWdyYW0gSGVscGVycyB0byBtaW1pY2sgSW5zdGFncmFtJ3MgbG9vayBhbmQgZmVlbCAqL1xuXG5leHBvcnQgY29uc3QgQlVUVE9OX1NUWUxFID0ge1xuXHRcIlBSSU1BUllcIjogXCJwcmltYXJ5XCIsXG5cdFwiU0VDT05EQVJZXCI6IFwic2Vjb25kYXJ5XCIsXG59XG5cbi8qKlxuICpcbiAqIEBwYXJhbSB7SFRNTEJ1dHRvbkVsZW1lbnR9IGJ1dHRvbkVsZW1lbnRcbiAqIEBwYXJhbSB7c3RyaW5nfSAgICAgICAgICAgIHN0eWxlTmFtZVxuICovXG5leHBvcnQgZnVuY3Rpb24gYXBwbHlCdXR0b25TdHlsZShidXR0b25FbGVtZW50LCBzdHlsZU5hbWUpIHtcblx0YnV0dG9uRWxlbWVudC5zdHlsZS5mb250U2l6ZSA9IFwidmFyKC0tc3lzdGVtLTE0LWZvbnQtc2l6ZSlcIlxuXHRidXR0b25FbGVtZW50LnN0eWxlLmNvbG9yID0gXCJ3aGl0ZVwiXG5cdGJ1dHRvbkVsZW1lbnQuc3R5bGUuYm9yZGVyID0gXCIwcHhcIlxuXHRidXR0b25FbGVtZW50LnN0eWxlLmJvcmRlclJhZGl1cyA9IFwiOHB4XCJcblx0YnV0dG9uRWxlbWVudC5zdHlsZS5wYWRkaW5nID0gXCI4cHhcIlxuXHRidXR0b25FbGVtZW50LnN0eWxlLmZvbnRXZWlnaHQgPSBcImJvbGRcIlxuXHRidXR0b25FbGVtZW50LnN0eWxlLmN1cnNvciA9IFwicG9pbnRlclwiXG5cdGJ1dHRvbkVsZW1lbnQuc3R5bGUubGluZUhlaWdodCA9IFwidmFyKC0tc3lzdGVtLTE0LWxpbmUtaGVpZ2h0KVwiXG5cdGlmKHN0eWxlTmFtZSkge1xuXHRcdGJ1dHRvbkVsZW1lbnQuc3R5bGUuYmFja2dyb3VuZENvbG9yID0gYHJnYih2YXIoLS1pZy0ke3N0eWxlTmFtZX0tYnV0dG9uKSlgXG5cdH1cbn1cbiIsIi8qKiBAbW9kdWxlIG1lbnUtYnV0dG9uIEhlbHBlcnMgdG8gY3JlYXRlIGJ1dHRvbnMgdGhhdCBjYW4gYmUgdXNlZCBpbiBJRE1VJ3MgbWVudSAqL1xuXG5pbXBvcnQgeyBhcHBseUJ1dHRvblN0eWxlIH0gZnJvbSBcIi4vc3R5bGUvaW5zdGFncmFtLmpzXCJcblxuLyoqXG4gKlxuICogQHBhcmFtIHtEb2N1bWVudH0gZG9jdW1lbnRcbiAqIEBwYXJhbSB7c3RyaW5nfSAgIHRleHRcbiAqIEBwYXJhbSB7c3RyaW5nfSAgIHN0eWxlTmFtZVxuICogQHJldHVybnMge0hUTUxCdXR0b25FbGVtZW50fVxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlTWVudUJ1dHRvbkVsZW1lbnQoZG9jdW1lbnQsIHRleHQsIHN0eWxlTmFtZSkge1xuXHRjb25zdCBidXR0b25FbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImJ1dHRvblwiKVxuXHRidXR0b25FbGVtZW50LnRleHRDb250ZW50ID0gdGV4dFxuXHRhcHBseUJ1dHRvblN0eWxlKGJ1dHRvbkVsZW1lbnQsIHN0eWxlTmFtZSlcblx0YnV0dG9uRWxlbWVudC5hZGRFdmVudExpc3RlbmVyKFwibW91c2VvdmVyXCIsICgpID0+IHtcblx0XHRidXR0b25FbGVtZW50LnN0eWxlLmZpbHRlciA9IGBicmlnaHRuZXNzKDEuMTUpYFxuXHR9KVxuXHRidXR0b25FbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJtb3VzZW91dFwiLCAoKSA9PiB7XG5cdFx0YnV0dG9uRWxlbWVudC5zdHlsZS5maWx0ZXIgPSBgYFxuXHR9KVxuXHRyZXR1cm4gYnV0dG9uRWxlbWVudFxufVxuIiwiLyoqIEBtb2R1bGUgbWVudSBJRE1VJ3MgbWFpbiBtZW51ICovXG5cbi8qKlxuICogQHBhcmFtIHtEb2N1bWVudH0gZG9jdW1lbnRcbiAqIEByZXR1cm5zIHtIVE1MQnV0dG9uRWxlbWVudH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU1lbnVFbGVtZW50KGRvY3VtZW50KSB7XG5cdGNvbnN0IG1lbnVFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKVxuXHRtZW51RWxlbWVudC5pZCA9IFwiaWRtdS1tZW51XCJcblx0bWVudUVsZW1lbnQuc3R5bGUudG9wID0gXCIyMHB4XCJcblx0bWVudUVsZW1lbnQuc3R5bGUucmlnaHQgPSBcIjQzMHB4XCJcblx0bWVudUVsZW1lbnQuc3R5bGUucG9zaXRpb24gPSBcImZpeGVkXCJcblx0bWVudUVsZW1lbnQuc3R5bGUuekluZGV4ID0gOTk5XG5cdG1lbnVFbGVtZW50LnN0eWxlLmRpc3BsYXkgPSBcImZsZXhcIlxuXHRtZW51RWxlbWVudC5zdHlsZS5nYXAgPSBcIjEwcHhcIlxuXHRtZW51RWxlbWVudC5zdHlsZS5wbGFjZUl0ZW1zID0gXCJjZW50ZXJcIlxuXHRyZXR1cm4gbWVudUVsZW1lbnRcbn1cbiIsIi8qKiBAbW9kdWxlIGFzeW5jLWV2ZW50cyBVdGlscyBtb2R1bGUgZm9yIGZpbmRpbmcgZWxlbWVudHMgYXN5bmNocm9ub3VzbHkgaW4gdGhlIERPTSAqL1xuXG4vKipcbiAqXG4gKiBAY2FsbGJhY2sgZ2V0RWxlbWVudFxuICogQHJldHVybnMge0VsZW1lbnR9XG4gKi9cblxuLyoqXG4gKlxuICogQHBhcmFtIHtFbGVtZW50fSB0YXJnZXRcbiAqIEBwYXJhbSB7Z2V0RWxlbWVudH0gZ2V0RWxlbWVudFxuICogQHBhcmFtIHtBYm9ydENvbnRyb2xsZXJ9IGFib3J0Q29udHJvbGxlclxuICogQHJldHVybnMge1Byb21pc2U8RWxlbWVudD59XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3YWl0Rm9yRWxlbWVudCh0YXJnZXQsIGdldEVsZW1lbnQsIGFib3J0Q29udHJvbGxlcikge1xuXHRyZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuXHRcdGxldCBtdXRhdGlvbk9ic2VydmVyXG5cdFx0Y29uc3QgYWJvcnRIYW5kbGVyID0gKCkgPT4ge1xuXHRcdFx0aWYobXV0YXRpb25PYnNlcnZlcikge1xuXHRcdFx0XHRyZWplY3QobmV3IERPTUV4Y2VwdGlvbihcIkFib3J0ZWQ6IERpc2Nvbm5lY3RpbmcgbXV0YXRpb24gb2JzZXJ2ZXIuLi5cIiwgXCJBYm9ydEVycm9yXCIpKVxuXHRcdFx0XHRtdXRhdGlvbk9ic2VydmVyLmRpc2Nvbm5lY3QoKVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0cmVqZWN0KG5ldyBET01FeGNlcHRpb24oXCJBYm9ydGVkXCIsIFwiQWJvcnRFcnJvclwiKSlcblx0XHRcdH1cblx0XHR9XG5cdFx0YWJvcnRDb250cm9sbGVyLnNpZ25hbC5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgYWJvcnRIYW5kbGVyKVxuXHRcdGxldCBlbGVtZW50ID0gZ2V0RWxlbWVudCgpXG5cdFx0aWYoZWxlbWVudCkge1xuXHRcdFx0cmVzb2x2ZShlbGVtZW50KVxuXHRcdFx0YWJvcnRDb250cm9sbGVyLnNpZ25hbC5yZW1vdmVFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgYWJvcnRIYW5kbGVyKVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRtdXRhdGlvbk9ic2VydmVyID0gbmV3IE11dGF0aW9uT2JzZXJ2ZXIoKG11dGF0aW9ucywgb2JzZXJ2ZXIpID0+IHtcblx0XHRcdFx0ZWxlbWVudCA9IGdldEVsZW1lbnQobXV0YXRpb25zKVxuXHRcdFx0XHRpZihlbGVtZW50KSB7XG5cdFx0XHRcdFx0b2JzZXJ2ZXIuZGlzY29ubmVjdCgpXG5cdFx0XHRcdFx0cmVzb2x2ZShlbGVtZW50KVxuXHRcdFx0XHRcdGFib3J0Q29udHJvbGxlci5zaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIGFib3J0SGFuZGxlcilcblx0XHRcdFx0fVxuXHRcdFx0fSlcblx0XHRcdG11dGF0aW9uT2JzZXJ2ZXIub2JzZXJ2ZSh0YXJnZXQsIHsgc3VidHJlZTogdHJ1ZSwgY2hpbGRMaXN0OnRydWUgfSlcblx0XHR9XG5cdH0pXG59XG5cbi8qKlxuICpcbiAqIEBwYXJhbSB7RWxlbWVudH0gY2xpY2tUYXJnZXRcbiAqIEBwYXJhbSB7RWxlbWVudH0gdGFyZ2V0XG4gKiBAcGFyYW0ge2dldEVsZW1lbnR9IGdldEVsZW1lbnRcbiAqIEBwYXJhbSB7QWJvcnRDb250cm9sbGVyfSBhYm9ydENvbnRyb2xsZXJcbiAqIEByZXR1cm5zIHtFbGVtZW50fFByb21pc2U8RWxlbWVudD59XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjbGlja0VsZW1lbnRBbmRXYWl0Rm9yKGNsaWNrVGFyZ2V0LCB0YXJnZXQsIGdldEVsZW1lbnQsIGFib3J0Q29udHJvbGxlcikge1xuXHRjb25zdCBwcm9taXNlID0gd2FpdEZvckVsZW1lbnQodGFyZ2V0LCBnZXRFbGVtZW50LCBhYm9ydENvbnRyb2xsZXIpXG5cdGNsaWNrVGFyZ2V0LmNsaWNrKClcblx0cmV0dXJuIGdldEVsZW1lbnQoKSB8fCBwcm9taXNlXG59XG4iLCIvKiogQG1vZHVsZSB1aS1jb21wb25lbnQgQmFzZSBjbGFzcyBmb3IgYW55IGVsZW1lbnQgdGhhdCBpcyBhIHBhcnQgb2YgdGhlIFVJLiAqL1xuXG5pbXBvcnQgeyB3YWl0Rm9yRWxlbWVudCwgY2xpY2tFbGVtZW50QW5kV2FpdEZvciB9IGZyb20gXCIuLi9kb20vYXN5bmMtZXZlbnRzLmpzXCJcblxuLyoqXG4gKlxuICogQGFic3RyYWN0XG4gKi9cbmNsYXNzIFVJQ29tcG9uZW50IHtcblx0LyoqXG5cdCAqXG5cdCAqIEBwYXJhbSB7RWxlbWVudH0gcm9vdFxuXHQgKiBAcGFyYW0ge29iamVjdH0gaWRlbnRpZmllclxuXHQgKi9cblx0Y29uc3RydWN0b3Iocm9vdCwgaWRlbnRpZmllcj17fSkge1xuXHRcdHRoaXMucm9vdCA9IHJvb3Rcblx0XHR0aGlzLmlkZW50aWZpZXIgPSBpZGVudGlmaWVyXG5cdH1cblxuXHQvKipcblx0ICpcblx0ICogQHBhcmFtIHtFbGVtZW50fSB0YXJnZXRcblx0ICogQHBhcmFtIHtmdW5jdGlvbn0gZ2V0RWxlbWVudFxuXHQgKiBAcGFyYW0ge0Fib3J0Q29udHJvbGxlcn0gYWJvcnRDb250cm9sbGVyXG5cdCAqIEByZXR1cm5zIHtQcm9taXNlPEVsZW1lbnQ+fVxuXHQgKi9cblx0d2FpdEZvckVsZW1lbnQodGFyZ2V0LCBnZXRFbGVtZW50LCBhYm9ydENvbnRyb2xsZXIpIHtcblx0XHRyZXR1cm4gZ2V0RWxlbWVudCgpIHx8IHdhaXRGb3JFbGVtZW50KHRhcmdldCwgZ2V0RWxlbWVudCwgYWJvcnRDb250cm9sbGVyKVxuXHR9XG5cblx0LyoqXG5cdCAqXG5cdCAqIEBwYXJhbSB7RWxlbWVudH0gY2xpY2tUYXJnZXRcblx0ICogQHBhcmFtIHtFbGVtZW50fSB0YXJnZXRcblx0ICogQHBhcmFtIHtmdW5jdGlvbn0gZ2V0RWxlbWVudFxuXHQgKiBAcGFyYW0ge0Fib3J0Q29udHJvbGxlcn0gYWJvcnRDb250cm9sbGVyXG5cdCAqIEByZXR1cm5zIHtQcm9taXNlPEVsZW1lbnQ+fVxuXHQgKi9cblx0Y2xpY2tFbGVtZW50QW5kV2FpdEZvcihjbGlja1RhcmdldCwgdGFyZ2V0LCBnZXRFbGVtZW50LCBhYm9ydENvbnRyb2xsZXIpIHtcblx0XHRyZXR1cm4gY2xpY2tFbGVtZW50QW5kV2FpdEZvcihjbGlja1RhcmdldCwgdGFyZ2V0LCBnZXRFbGVtZW50LCBhYm9ydENvbnRyb2xsZXIpXG5cdH1cblxufVxuXG5leHBvcnQgZGVmYXVsdCBVSUNvbXBvbmVudFxuIiwiLyoqIEBtb2R1bGUgdWktbWVzc2FnZSBVSSBlbGVtZW50IHJlcHJlc2VudGluZyBhIG1lc3NhZ2UgKi9cblxuaW1wb3J0IFVJQ29tcG9uZW50IGZyb20gXCIuLi91aS1jb21wb25lbnQuanNcIlxuXG4vKiogTG9jYWxlLWluZGVwZW5kZW50IHBhdHRlcm5zIGZvciB0aGUgXCJVbnNlbmRcIiBtZW51IGl0ZW0gKi9cbmNvbnN0IFVOU0VORF9URVhUX1ZBUklBTlRTID0gW1xuXHRcInVuc2VuZFwiLCAgICAgICAgLy8gRW5nbGlzaFxuXHRcImFubnVsbGEgaW52aW9cIiwgLy8gSXRhbGlhblxuXHRcInJldGlyYXJcIiwgICAgICAgLy8gUG9ydHVndWVzZVxuXHRcImRlc2hhY2VyXCIsICAgICAgLy8gU3BhbmlzaFxuXHRcInJldGlyZXJcIiwgICAgICAgLy8gRnJlbmNoXG5cdFwienVyw7xja25laG1lblwiLCAgLy8gR2VybWFuXG5dXG5cbi8qKlxuICogRGlzcGF0Y2hlcyBwb2ludGVyIGFuZCBtb3VzZSBob3ZlciBldmVudHMgb24gYSB0YXJnZXQgZWxlbWVudC5cbiAqIEluc3RhZ3JhbSdzIFJlYWN0IHVzZXMgcG9pbnRlciBldmVudHMgaW50ZXJuYWxseTsgbW91c2UgZXZlbnRzIGFsb25lIGFyZSBpbnN1ZmZpY2llbnQuXG4gKlxuICogQHBhcmFtIHtFbGVtZW50fSB0YXJnZXRcbiAqL1xuZnVuY3Rpb24gZGlzcGF0Y2hIb3ZlckluKHRhcmdldCkge1xuXHRjb25zdCByZWN0ID0gdGFyZ2V0LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpXG5cdGNvbnN0IG9wdHMgPSB7XG5cdFx0YnViYmxlczogdHJ1ZSxcblx0XHRjYW5jZWxhYmxlOiB0cnVlLFxuXHRcdGNsaWVudFg6IHJlY3QueCArIHJlY3Qud2lkdGggLyAyLFxuXHRcdGNsaWVudFk6IHJlY3QueSArIHJlY3QuaGVpZ2h0IC8gMixcblx0XHRwb2ludGVySWQ6IDEsXG5cdFx0cG9pbnRlclR5cGU6IFwibW91c2VcIixcblx0fVxuXHR0YXJnZXQuZGlzcGF0Y2hFdmVudChuZXcgUG9pbnRlckV2ZW50KFwicG9pbnRlcmVudGVyXCIsIHsgLi4ub3B0cywgYnViYmxlczogZmFsc2UgfSkpXG5cdHRhcmdldC5kaXNwYXRjaEV2ZW50KG5ldyBQb2ludGVyRXZlbnQoXCJwb2ludGVyb3ZlclwiLCBvcHRzKSlcblx0dGFyZ2V0LmRpc3BhdGNoRXZlbnQobmV3IFBvaW50ZXJFdmVudChcInBvaW50ZXJtb3ZlXCIsIG9wdHMpKVxuXHR0YXJnZXQuZGlzcGF0Y2hFdmVudChuZXcgTW91c2VFdmVudChcIm1vdXNlZW50ZXJcIiwgeyAuLi5vcHRzLCBidWJibGVzOiBmYWxzZSB9KSlcblx0dGFyZ2V0LmRpc3BhdGNoRXZlbnQobmV3IE1vdXNlRXZlbnQoXCJtb3VzZW92ZXJcIiwgb3B0cykpXG5cdHRhcmdldC5kaXNwYXRjaEV2ZW50KG5ldyBNb3VzZUV2ZW50KFwibW91c2Vtb3ZlXCIsIG9wdHMpKVxufVxuXG4vKipcbiAqIERpc3BhdGNoZXMgcG9pbnRlciBhbmQgbW91c2UgbGVhdmUgZXZlbnRzIG9uIGEgdGFyZ2V0IGVsZW1lbnQuXG4gKlxuICogQHBhcmFtIHtFbGVtZW50fSB0YXJnZXRcbiAqL1xuZnVuY3Rpb24gZGlzcGF0Y2hIb3Zlck91dCh0YXJnZXQpIHtcblx0Y29uc3QgcmVjdCA9IHRhcmdldC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuXHRjb25zdCBvcHRzID0ge1xuXHRcdGJ1YmJsZXM6IHRydWUsXG5cdFx0Y2FuY2VsYWJsZTogdHJ1ZSxcblx0XHRjbGllbnRYOiByZWN0LnggKyByZWN0LndpZHRoIC8gMixcblx0XHRjbGllbnRZOiByZWN0LnkgKyByZWN0LmhlaWdodCAvIDIsXG5cdFx0cG9pbnRlcklkOiAxLFxuXHRcdHBvaW50ZXJUeXBlOiBcIm1vdXNlXCIsXG5cdH1cblx0dGFyZ2V0LmRpc3BhdGNoRXZlbnQobmV3IFBvaW50ZXJFdmVudChcInBvaW50ZXJvdXRcIiwgb3B0cykpXG5cdHRhcmdldC5kaXNwYXRjaEV2ZW50KG5ldyBQb2ludGVyRXZlbnQoXCJwb2ludGVybGVhdmVcIiwgeyAuLi5vcHRzLCBidWJibGVzOiBmYWxzZSB9KSlcblx0dGFyZ2V0LmRpc3BhdGNoRXZlbnQobmV3IE1vdXNlRXZlbnQoXCJtb3VzZW91dFwiLCBvcHRzKSlcblx0dGFyZ2V0LmRpc3BhdGNoRXZlbnQobmV3IE1vdXNlRXZlbnQoXCJtb3VzZWxlYXZlXCIsIHsgLi4ub3B0cywgYnViYmxlczogZmFsc2UgfSkpXG59XG5cbmNsYXNzIFVJTWVzc2FnZSBleHRlbmRzIFVJQ29tcG9uZW50IHtcblxuXHQvKipcblx0ICogRGlzbWlzcyBhbnkgc3RhbGUgZGlhbG9nIG9yIGRyb3Bkb3duIGxlZnQgZnJvbSBhIHByZXZpb3VzIGZhaWxlZCB3b3JrZmxvdy5cblx0ICovXG5cdF9kaXNtaXNzU3RhbGVPdmVybGF5cygpIHtcblx0XHRjb25zdCBkb2MgPSB0aGlzLnJvb3Qub3duZXJEb2N1bWVudFxuXHRcdC8vIENsb3NlIHN0YWxlIGNvbmZpcm1hdGlvbiBkaWFsb2dzXG5cdFx0Y29uc3Qgc3RhbGVEaWFsb2cgPSBkb2MucXVlcnlTZWxlY3RvcihcIltyb2xlPWRpYWxvZ11cIilcblx0XHRpZiAoc3RhbGVEaWFsb2cpIHtcblx0XHRcdGNvbnNvbGUuZGVidWcoXCJEaXNtaXNzaW5nIHN0YWxlIGRpYWxvZ1wiKVxuXHRcdFx0Y29uc3QgY2xvc2VCdG4gPSBzdGFsZURpYWxvZy5xdWVyeVNlbGVjdG9yKFwiYnV0dG9uXCIpXG5cdFx0XHRpZiAoY2xvc2VCdG4pIGNsb3NlQnRuLmNsaWNrKClcblx0XHR9XG5cdFx0Ly8gQ2xvc2Ugc3RhbGUgZHJvcGRvd24gbWVudXMgYnkgcHJlc3NpbmcgRXNjYXBlXG5cdFx0Y29uc3QgYWN0aXZlTWVudSA9IGRvYy5xdWVyeVNlbGVjdG9yKFwiW3JvbGU9bWVudV0sIFtyb2xlPWxpc3Rib3hdXCIpXG5cdFx0aWYgKGFjdGl2ZU1lbnUpIHtcblx0XHRcdGNvbnNvbGUuZGVidWcoXCJEaXNtaXNzaW5nIHN0YWxlIG1lbnUgdmlhIEVzY2FwZVwiKVxuXHRcdFx0ZG9jLmJvZHkuZGlzcGF0Y2hFdmVudChuZXcgS2V5Ym9hcmRFdmVudChcImtleWRvd25cIiwgeyBrZXk6IFwiRXNjYXBlXCIsIGJ1YmJsZXM6IHRydWUgfSkpXG5cdFx0fVxuXHR9XG5cblx0LyoqXG5cdCAqIEZpbmQgdGhlIGFjdGlvbiBidXR0b24gd2l0aGluIHRoZSBtZXNzYWdlIHJvdy5cblx0ICogSW5zdGFncmFtIG1vdmVkIGFyaWEtbGFiZWwgZnJvbSB0aGUgYnV0dG9uIGRpdiB0byBhIG5lc3RlZCBTVkcvdGl0bGUuXG5cdCAqIEFueSBtYXRjaCAoU1ZHIG9yIGRpdikgaXMgd2Fsa2VkIHVwIHRvIHRoZSBuZWFyZXN0IFtyb2xlPWJ1dHRvbl0gYW5jZXN0b3IuXG5cdCAqXG5cdCAqIEBwYXJhbSB7RWxlbWVudH0gc2NvcGVcblx0ICogQHJldHVybnMge0VsZW1lbnR8bnVsbH1cblx0ICovXG5cdF9maW5kQWN0aW9uQnV0dG9uKHNjb3BlKSB7XG5cdFx0Y29uc3QgTEFCRUxfUEFUVEVSTlMgPSBbXG5cdFx0XHRcIlthcmlhLWxhYmVsXj0nU2VlIG1vcmUgb3B0aW9ucyBmb3IgbWVzc2FnZSddXCIsXG5cdFx0XHRcIlthcmlhLWxhYmVsKj0nbW9yZSBvcHRpb25zJ11cIixcblx0XHRcdFwiW2FyaWEtbGFiZWwqPSdNb3JlJ11cIixcblx0XHRcdFwiW2FyaWEtbGFiZWwqPSdBbHRyZSBvcHppb25pJ11cIixcblx0XHRcdFwiW2FyaWEtbGFiZWwqPSdvcHppb25pJ11cIixcblx0XHRcdFwiW2FyaWEtbGFiZWwqPSdvcGNpb25lcyddXCIsXG5cdFx0XHRcIlthcmlhLWxhYmVsKj0nb3B0aW9ucyddXCIsXG5cdFx0XVxuXG5cdFx0Zm9yIChjb25zdCBzZWwgb2YgTEFCRUxfUEFUVEVSTlMpIHtcblx0XHRcdGNvbnN0IGVsID0gc2NvcGUucXVlcnlTZWxlY3RvcihzZWwpXG5cdFx0XHRpZiAoZWwpIHtcblx0XHRcdFx0Ly8gQWx3YXlzIHJlc29sdmUgdG8gYSBjbGlja2FibGUgYnV0dG9uIGNvbnRhaW5lclxuXHRcdFx0XHRjb25zdCBidG4gPSBlbC5jbG9zZXN0KFwiW3JvbGU9YnV0dG9uXVwiKSB8fCBlbC5jbG9zZXN0KFwiYnV0dG9uXCIpXG5cdFx0XHRcdGlmIChidG4gJiYgc2NvcGUuY29udGFpbnMoYnRuKSkgcmV0dXJuIGJ0blxuXHRcdFx0XHQvLyBlbCBpdHNlbGYgaXMgYWxyZWFkeSBhIGJ1dHRvbi1saWtlIGVsZW1lbnRcblx0XHRcdFx0aWYgKGVsLnRhZ05hbWUgPT09IFwiQlVUVE9OXCIgfHwgZWwuZ2V0QXR0cmlidXRlKFwicm9sZVwiKSA9PT0gXCJidXR0b25cIikgcmV0dXJuIGVsXG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gRmFsbGJhY2s6IGFueSByb2xlPWJ1dHRvbiB3aXRoIGFyaWEtaGFzcG9wdXA9bWVudSBpbnNpZGUgdGhlIG1lc3NhZ2Ugcm93XG5cdFx0cmV0dXJuIHNjb3BlLnF1ZXJ5U2VsZWN0b3IoXCJbcm9sZT1idXR0b25dW2FyaWEtaGFzcG9wdXA9bWVudV1cIilcblx0fVxuXG5cdC8qKlxuXHQgKiBAcGFyYW0ge0Fib3J0Q29udHJvbGxlcn0gYWJvcnRDb250cm9sbGVyXG5cdCAqIEByZXR1cm5zIHtQcm9taXNlPEhUTUxCdXR0b25FbGVtZW50Pn1cblx0ICovXG5cdGFzeW5jIHNob3dBY3Rpb25zTWVudUJ1dHRvbihhYm9ydENvbnRyb2xsZXIpIHtcblx0XHRjb25zb2xlLmRlYnVnKFwiV29ya2Zsb3cgc3RlcCAxIDogc2hvd0FjdGlvbnNNZW51QnV0dG9uXCIsIHRoaXMucm9vdClcblx0XHR0aGlzLl9kaXNtaXNzU3RhbGVPdmVybGF5cygpXG5cblx0XHQvLyBDb2xsZWN0IGFsbCBob3ZlcmFibGUgYW5jZXN0b3JzIGZyb20gcm9vdCBkb3duIHRvIHRoZSBtZXNzYWdlIGJ1YmJsZS5cblx0XHQvLyBJbnN0YWdyYW0gUmVhY3QgbGlzdGVucyBhdCBpbnRlcm1lZGlhdGUgbGV2ZWxzIChyb2xlPWdyb3VwLCBmbGV4LWVuZCB3cmFwcGVyKS5cblx0XHRjb25zdCBob3ZlclRhcmdldHMgPSBbdGhpcy5yb290XVxuXHRcdGNvbnN0IGNvbGxlY3RUYXJnZXRzID0gKGVsLCBkZXB0aCkgPT4ge1xuXHRcdFx0aWYgKGRlcHRoID4gOCkgcmV0dXJuXG5cdFx0XHRmb3IgKGNvbnN0IGNoaWxkIG9mIGVsLmNoaWxkcmVuKSB7XG5cdFx0XHRcdGhvdmVyVGFyZ2V0cy5wdXNoKGNoaWxkKVxuXHRcdFx0XHRjb2xsZWN0VGFyZ2V0cyhjaGlsZCwgZGVwdGggKyAxKVxuXHRcdFx0fVxuXHRcdH1cblx0XHRjb2xsZWN0VGFyZ2V0cyh0aGlzLnJvb3QsIDApXG5cblx0XHQvLyBUcnkgdXAgdG8gMyB0aW1lcyDigJQgaG92ZXIgZXZlbnRzIGNhbiBiZSBmbGFreVxuXHRcdGZvciAobGV0IGF0dGVtcHQgPSAwOyBhdHRlbXB0IDwgMzsgYXR0ZW1wdCsrKSB7XG5cdFx0XHRpZiAoYWJvcnRDb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSByZXR1cm4gbnVsbFxuXG5cdFx0XHRmb3IgKGNvbnN0IHRhcmdldCBvZiBob3ZlclRhcmdldHMpIHtcblx0XHRcdFx0ZGlzcGF0Y2hIb3ZlckluKHRhcmdldClcblx0XHRcdH1cblxuXHRcdFx0YXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDEwMCkpXG5cblx0XHRcdGNvbnN0IGJ0biA9IHRoaXMuX2ZpbmRBY3Rpb25CdXR0b24odGhpcy5yb290KVxuXHRcdFx0aWYgKGJ0bikge1xuXHRcdFx0XHRjb25zb2xlLmRlYnVnKFwiV29ya2Zsb3cgc3RlcCAxIDogZm91bmQgYWN0aW9uIGJ1dHRvbiBvbiBhdHRlbXB0XCIsIGF0dGVtcHQsIGJ0bilcblx0XHRcdFx0cmV0dXJuIGJ0blxuXHRcdFx0fVxuXG5cdFx0XHRjb25zb2xlLmRlYnVnKFwiV29ya2Zsb3cgc3RlcCAxIDogYXR0ZW1wdFwiLCBhdHRlbXB0LCBcIm5vIGJ1dHRvbiBmb3VuZCwgcmV0cnlpbmcuLi5cIilcblx0XHRcdGRpc3BhdGNoSG92ZXJPdXQodGhpcy5yb290KVxuXHRcdFx0YXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDUwKSlcblx0XHR9XG5cblx0XHQvLyBGaW5hbCBmYWxsYmFjazogdXNlIHdhaXRGb3JFbGVtZW50IHdpdGggZXh0ZW5kZWQgdGltZW91dFxuXHRcdGNvbnN0IHdhaXRBYm9ydENvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKClcblx0XHRsZXQgcHJvbWlzZVRpbWVvdXRcblx0XHRjb25zdCBhYm9ydEhhbmRsZXIgPSAoKSA9PiB7XG5cdFx0XHR3YWl0QWJvcnRDb250cm9sbGVyLmFib3J0KClcblx0XHRcdGNsZWFyVGltZW91dChwcm9taXNlVGltZW91dClcblx0XHR9XG5cdFx0YWJvcnRDb250cm9sbGVyLnNpZ25hbC5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgYWJvcnRIYW5kbGVyKVxuXG5cdFx0Zm9yIChjb25zdCB0YXJnZXQgb2YgaG92ZXJUYXJnZXRzKSB7XG5cdFx0XHRkaXNwYXRjaEhvdmVySW4odGFyZ2V0KVxuXHRcdH1cblxuXHRcdHRyeSB7XG5cdFx0XHRjb25zdCBhY3Rpb25CdXR0b24gPSBhd2FpdCBQcm9taXNlLnJhY2UoW1xuXHRcdFx0XHR0aGlzLndhaXRGb3JFbGVtZW50KFxuXHRcdFx0XHRcdHRoaXMucm9vdCxcblx0XHRcdFx0XHQoKSA9PiB0aGlzLl9maW5kQWN0aW9uQnV0dG9uKHRoaXMucm9vdCksXG5cdFx0XHRcdFx0d2FpdEFib3J0Q29udHJvbGxlclxuXHRcdFx0XHQpLFxuXHRcdFx0XHRuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG5cdFx0XHRcdFx0cHJvbWlzZVRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHJlamVjdChcIlRpbWVvdXQgc2hvd0FjdGlvbnNNZW51QnV0dG9uXCIpLCAzMDAwKVxuXHRcdFx0XHR9KVxuXHRcdFx0XSlcblxuXHRcdFx0aWYgKGFjdGlvbkJ1dHRvbikge1xuXHRcdFx0XHRyZXR1cm4gYWN0aW9uQnV0dG9uXG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gYWN0aW9uQnV0dG9uXG5cdFx0fSBmaW5hbGx5IHtcblx0XHRcdHdhaXRBYm9ydENvbnRyb2xsZXIuYWJvcnQoKVxuXHRcdFx0Y2xlYXJUaW1lb3V0KHByb21pc2VUaW1lb3V0KVxuXHRcdFx0YWJvcnRDb250cm9sbGVyLnNpZ25hbC5yZW1vdmVFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgYWJvcnRIYW5kbGVyKVxuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBAcGFyYW0ge0Fib3J0Q29udHJvbGxlcn0gYWJvcnRDb250cm9sbGVyXG5cdCAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fVxuXHQgKi9cblx0YXN5bmMgaGlkZUFjdGlvbk1lbnVCdXR0b24oYWJvcnRDb250cm9sbGVyKSB7XG5cdFx0Y29uc29sZS5kZWJ1ZyhcImhpZGVBY3Rpb25NZW51QnV0dG9uXCIsIHRoaXMucm9vdClcblx0XHRkaXNwYXRjaEhvdmVyT3V0KHRoaXMucm9vdClcblxuXHRcdGNvbnN0IG5vbmVFbCA9IHRoaXMucm9vdC5xdWVyeVNlbGVjdG9yKFwiW3JvbGU9bm9uZV1cIilcblx0XHRpZiAobm9uZUVsKSB7XG5cdFx0XHRkaXNwYXRjaEhvdmVyT3V0KG5vbmVFbClcblx0XHR9XG5cblx0XHRjb25zdCB3YWl0QWJvcnRDb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpXG5cdFx0bGV0IHByb21pc2VUaW1lb3V0XG5cdFx0bGV0IHJlc29sdmVUaW1lb3V0XG5cdFx0Y29uc3QgYWJvcnRIYW5kbGVyID0gKCkgPT4ge1xuXHRcdFx0d2FpdEFib3J0Q29udHJvbGxlci5hYm9ydCgpXG5cdFx0XHRjbGVhclRpbWVvdXQocHJvbWlzZVRpbWVvdXQpXG5cdFx0XHRpZiAocmVzb2x2ZVRpbWVvdXQpIHtcblx0XHRcdFx0cmVzb2x2ZVRpbWVvdXQoKVxuXHRcdFx0fVxuXHRcdH1cblx0XHRhYm9ydENvbnRyb2xsZXIuc2lnbmFsLmFkZEV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBhYm9ydEhhbmRsZXIpXG5cblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgcmVzdWx0ID0gYXdhaXQgUHJvbWlzZS5yYWNlKFtcblx0XHRcdFx0dGhpcy53YWl0Rm9yRWxlbWVudChcblx0XHRcdFx0XHR0aGlzLnJvb3QsXG5cdFx0XHRcdFx0KCkgPT4gdGhpcy5fZmluZEFjdGlvbkJ1dHRvbih0aGlzLnJvb3QpID09PSBudWxsLFxuXHRcdFx0XHRcdHdhaXRBYm9ydENvbnRyb2xsZXJcblx0XHRcdFx0KSxcblx0XHRcdFx0bmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuXHRcdFx0XHRcdHJlc29sdmVUaW1lb3V0ID0gcmVzb2x2ZVxuXHRcdFx0XHRcdHByb21pc2VUaW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiByZWplY3QoXCJUaW1lb3V0IGhpZGVBY3Rpb25NZW51QnV0dG9uXCIpLCA1MDApXG5cdFx0XHRcdH0pXG5cdFx0XHRdKVxuXHRcdFx0cmV0dXJuIHJlc3VsdFxuXHRcdH0gZmluYWxseSB7XG5cdFx0XHR3YWl0QWJvcnRDb250cm9sbGVyLmFib3J0KClcblx0XHRcdGNsZWFyVGltZW91dChwcm9taXNlVGltZW91dClcblx0XHRcdGFib3J0Q29udHJvbGxlci5zaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIGFib3J0SGFuZGxlcilcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogT3BlbnMgdGhlIGFjdGlvbnMgbWVudSBieSBjbGlja2luZyB0aGUgYWN0aW9uIGJ1dHRvbiBhbmQgd2FpdGluZyBmb3IgdGhlIFwiVW5zZW5kXCIgaXRlbS5cblx0ICpcblx0ICogQHBhcmFtIHtIVE1MQnV0dG9uRWxlbWVudH0gYWN0aW9uQnV0dG9uXG5cdCAqIEBwYXJhbSB7QWJvcnRDb250cm9sbGVyfSBhYm9ydENvbnRyb2xsZXJcblx0ICogQHJldHVybnMge1Byb21pc2V9XG5cdCAqL1xuXHRhc3luYyBvcGVuQWN0aW9uc01lbnUoYWN0aW9uQnV0dG9uLCBhYm9ydENvbnRyb2xsZXIpIHtcblx0XHRjb25zb2xlLmRlYnVnKFwiV29ya2Zsb3cgc3RlcCAyIDogQ2xpY2tpbmcgYWN0aW9uQnV0dG9uIGFuZCB3YWl0aW5nIGZvciB1bnNlbmQgbWVudSBpdGVtIHRvIGFwcGVhclwiLCBhY3Rpb25CdXR0b24pXG5cdFx0Y29uc3Qgd2FpdEFib3J0Q29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKVxuXHRcdGxldCBwcm9taXNlVGltZW91dFxuXHRcdGxldCByZXNvbHZlVGltZW91dFxuXHRcdGNvbnN0IGFib3J0SGFuZGxlciA9ICgpID0+IHtcblx0XHRcdHdhaXRBYm9ydENvbnRyb2xsZXIuYWJvcnQoKVxuXHRcdFx0Y2xlYXJUaW1lb3V0KHByb21pc2VUaW1lb3V0KVxuXHRcdFx0aWYgKHJlc29sdmVUaW1lb3V0KSB7XG5cdFx0XHRcdHJlc29sdmVUaW1lb3V0KClcblx0XHRcdH1cblx0XHR9XG5cdFx0YWJvcnRDb250cm9sbGVyLnNpZ25hbC5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgYWJvcnRIYW5kbGVyKVxuXG5cdFx0LyoqIENoZWNrIGlmIHRleHQgbWF0Y2hlcyBhbnkga25vd24gXCJVbnNlbmRcIiB2YXJpYW50ICovXG5cdFx0Y29uc3QgaXNVbnNlbmRUZXh0ID0gKHRleHQpID0+IHtcblx0XHRcdGNvbnN0IG5vcm1hbGl6ZWQgPSB0ZXh0LnRyaW0oKS50b0xvY2FsZUxvd2VyQ2FzZSgpXG5cdFx0XHRyZXR1cm4gVU5TRU5EX1RFWFRfVkFSSUFOVFMuc29tZSh2ID0+IG5vcm1hbGl6ZWQgPT09IHYpXG5cdFx0fVxuXG5cdFx0dHJ5IHtcblx0XHRcdGNvbnN0IHVuc2VuZEJ1dHRvbiA9IGF3YWl0IFByb21pc2UucmFjZShbXG5cdFx0XHRcdHRoaXMuY2xpY2tFbGVtZW50QW5kV2FpdEZvcihcblx0XHRcdFx0XHRhY3Rpb25CdXR0b24sXG5cdFx0XHRcdFx0dGhpcy5yb290Lm93bmVyRG9jdW1lbnQuYm9keSxcblx0XHRcdFx0XHQobXV0YXRpb25zKSA9PiB7XG5cdFx0XHRcdFx0XHRpZiAobXV0YXRpb25zKSB7XG5cdFx0XHRcdFx0XHRcdGNvbnN0IGFkZGVkTm9kZXMgPSBbLi4ubXV0YXRpb25zLm1hcChtdXRhdGlvbiA9PiBbLi4ubXV0YXRpb24uYWRkZWROb2Rlc10pXS5mbGF0KCkuZmlsdGVyKG5vZGUgPT4gbm9kZS5ub2RlVHlwZSA9PT0gMSlcblx0XHRcdFx0XHRcdFx0Zm9yIChjb25zdCBhZGRlZE5vZGUgb2YgYWRkZWROb2Rlcykge1xuXHRcdFx0XHRcdFx0XHRcdGNvbnN0IG5vZGUgPSBbLi4uYWRkZWROb2RlLnF1ZXJ5U2VsZWN0b3JBbGwoXCJzcGFuLGRpdlwiKV0uZmluZChub2RlID0+IGlzVW5zZW5kVGV4dChub2RlLnRleHRDb250ZW50KSAmJiBub2RlLmZpcnN0Q2hpbGQ/Lm5vZGVUeXBlID09PSAzKVxuXHRcdFx0XHRcdFx0XHRcdGlmIChub2RlKSB7XG5cdFx0XHRcdFx0XHRcdFx0XHRjb25zb2xlLmRlYnVnKFwiV29ya2Zsb3cgc3RlcCAyIDogZm91bmQgdW5zZW5kIG5vZGUgdmlhIG11dGF0aW9uXCIsIG5vZGUpXG5cdFx0XHRcdFx0XHRcdFx0XHRyZXR1cm4gbm9kZVxuXHRcdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0Ly8gRmFsbGJhY2s6IHNjYW4gdGhlIHdob2xlIGRvY3VtZW50IGZvciBhbiB1bnNlbmQgbWVudSBpdGVtIGFscmVhZHkgcHJlc2VudFxuXHRcdFx0XHRcdFx0Y29uc3QgYWxsU3BhbnMgPSB0aGlzLnJvb3Qub3duZXJEb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKFwiW3JvbGU9bWVudV0gc3BhbiwgW3JvbGU9bWVudV0gZGl2LCBbcm9sZT1tZW51aXRlbV0gc3BhbiwgW3JvbGU9bWVudWl0ZW1dIGRpdlwiKVxuXHRcdFx0XHRcdFx0Zm9yIChjb25zdCBzcGFuIG9mIGFsbFNwYW5zKSB7XG5cdFx0XHRcdFx0XHRcdGlmIChpc1Vuc2VuZFRleHQoc3Bhbi50ZXh0Q29udGVudCkgJiYgc3Bhbi5maXJzdENoaWxkPy5ub2RlVHlwZSA9PT0gMykge1xuXHRcdFx0XHRcdFx0XHRcdGNvbnNvbGUuZGVidWcoXCJXb3JrZmxvdyBzdGVwIDIgOiBmb3VuZCB1bnNlbmQgbm9kZSB2aWEgZG9jdW1lbnQgc2NhblwiLCBzcGFuKVxuXHRcdFx0XHRcdFx0XHRcdHJldHVybiBzcGFuXG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdHdhaXRBYm9ydENvbnRyb2xsZXJcblx0XHRcdFx0KSxcblx0XHRcdFx0bmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuXHRcdFx0XHRcdHByb21pc2VUaW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiByZWplY3QoXCJUaW1lb3V0IG9wZW5BY3Rpb25zTWVudVwiKSwgMzAwMClcblx0XHRcdFx0fSlcblx0XHRcdF0pXG5cblx0XHRcdGNvbnNvbGUuZGVidWcoXCJXb3JrZmxvdyBzdGVwIDIgOiBGb3VuZCB1bnNlbmRCdXR0b25cIiwgdW5zZW5kQnV0dG9uKVxuXHRcdFx0cmV0dXJuIHVuc2VuZEJ1dHRvblxuXHRcdH0gZmluYWxseSB7XG5cdFx0XHR3YWl0QWJvcnRDb250cm9sbGVyLmFib3J0KClcblx0XHRcdGNsZWFyVGltZW91dChwcm9taXNlVGltZW91dClcblx0XHRcdGFib3J0Q29udHJvbGxlci5zaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIGFib3J0SGFuZGxlcilcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogQ2xvc2VzIHRoZSBhY3Rpb25zIG1lbnUuXG5cdCAqXG5cdCAqIEBwYXJhbSB7SFRNTEJ1dHRvbkVsZW1lbnR9IGFjdGlvbkJ1dHRvblxuXHQgKiBAcGFyYW0ge0hUTUxEaXZFbGVtZW50fSBhY3Rpb25zTWVudUVsZW1lbnRcblx0ICogQHBhcmFtIHtBYm9ydENvbnRyb2xsZXJ9IGFib3J0Q29udHJvbGxlclxuXHQgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn1cblx0ICovXG5cdGFzeW5jIGNsb3NlQWN0aW9uc01lbnUoYWN0aW9uQnV0dG9uLCBhY3Rpb25zTWVudUVsZW1lbnQsIGFib3J0Q29udHJvbGxlcikge1xuXHRcdGNvbnNvbGUuZGVidWcoXCJjbG9zZUFjdGlvbnNNZW51XCIpXG5cdFx0Y29uc3Qgd2FpdEFib3J0Q29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKVxuXHRcdGxldCBwcm9taXNlVGltZW91dFxuXHRcdGxldCByZXNvbHZlVGltZW91dFxuXHRcdGNvbnN0IGFib3J0SGFuZGxlciA9ICgpID0+IHtcblx0XHRcdHdhaXRBYm9ydENvbnRyb2xsZXIuYWJvcnQoKVxuXHRcdFx0Y2xlYXJUaW1lb3V0KHByb21pc2VUaW1lb3V0KVxuXHRcdFx0aWYgKHJlc29sdmVUaW1lb3V0KSB7XG5cdFx0XHRcdHJlc29sdmVUaW1lb3V0KClcblx0XHRcdH1cblx0XHR9XG5cdFx0YWJvcnRDb250cm9sbGVyLnNpZ25hbC5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgYWJvcnRIYW5kbGVyKVxuXG5cdFx0dHJ5IHtcblx0XHRcdGNvbnN0IHJlc3VsdCA9IGF3YWl0IFByb21pc2UucmFjZShbXG5cdFx0XHRcdHRoaXMuY2xpY2tFbGVtZW50QW5kV2FpdEZvcihcblx0XHRcdFx0XHRhY3Rpb25CdXR0b24sXG5cdFx0XHRcdFx0dGhpcy5yb290Lm93bmVyRG9jdW1lbnQuYm9keSxcblx0XHRcdFx0XHQoKSA9PiB0aGlzLnJvb3Qub3duZXJEb2N1bWVudC5ib2R5LmNvbnRhaW5zKGFjdGlvbnNNZW51RWxlbWVudCkgPT09IGZhbHNlLFxuXHRcdFx0XHRcdGFib3J0Q29udHJvbGxlclxuXHRcdFx0XHQpLFxuXHRcdFx0XHRuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG5cdFx0XHRcdFx0cHJvbWlzZVRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHJlamVjdChcIlRpbWVvdXQgY2xvc2VBY3Rpb25zTWVudVwiKSwgNTAwKVxuXHRcdFx0XHR9KVxuXHRcdFx0XSlcblx0XHRcdHJldHVybiByZXN1bHQgIT09IG51bGxcblx0XHR9IGZpbmFsbHkge1xuXHRcdFx0d2FpdEFib3J0Q29udHJvbGxlci5hYm9ydCgpXG5cdFx0XHRjbGVhclRpbWVvdXQocHJvbWlzZVRpbWVvdXQpXG5cdFx0XHRhYm9ydENvbnRyb2xsZXIuc2lnbmFsLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBhYm9ydEhhbmRsZXIpXG5cdFx0fVxuXHR9XG5cblx0LyoqXG5cdCAqIENsaWNrIHVuc2VuZCBidXR0b24gYW5kIHdhaXQgZm9yIHRoZSBjb25maXJtYXRpb24gZGlhbG9nLlxuXHQgKlxuXHQgKiBAcGFyYW0ge0hUTUxTcGFuRWxlbWVudH0gdW5zZW5kQnV0dG9uXG5cdCAqIEBwYXJhbSB7QWJvcnRDb250cm9sbGVyfSBhYm9ydENvbnRyb2xsZXJcblx0ICogQHJldHVybnMge1Byb21pc2U8SFRNTEJ1dHRvbkVsZW1lbnQ+fFByb21pc2U8RXJyb3I+fVxuXHQgKi9cblx0b3BlbkNvbmZpcm1VbnNlbmRNb2RhbCh1bnNlbmRCdXR0b24sIGFib3J0Q29udHJvbGxlcikge1xuXHRcdGNvbnNvbGUuZGVidWcoXCJXb3JrZmxvdyBzdGVwIDMgOiBDbGlja2luZyB1bnNlbmRCdXR0b24gYW5kIHdhaXRpbmcgZm9yIGRpYWxvZyB0byBhcHBlYXIuLi5cIilcblx0XHRyZXR1cm4gdGhpcy5jbGlja0VsZW1lbnRBbmRXYWl0Rm9yKFxuXHRcdFx0dW5zZW5kQnV0dG9uLFxuXHRcdFx0dGhpcy5yb290Lm93bmVyRG9jdW1lbnQuYm9keSxcblx0XHRcdCgpID0+IHRoaXMucm9vdC5vd25lckRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXCJbcm9sZT1kaWFsb2ddIGJ1dHRvblwiKSxcblx0XHRcdGFib3J0Q29udHJvbGxlclxuXHRcdClcblx0fVxuXG5cdC8qKlxuXHQgKiBDbGljayB1bnNlbmQgY29uZmlybSBidXR0b24gaW4gdGhlIG1vZGFsIGRpYWxvZy5cblx0ICpcblx0ICogQHBhcmFtIHtIVE1MQnV0dG9uRWxlbWVudH0gZGlhbG9nQnV0dG9uXG5cdCAqIEBwYXJhbSB7QWJvcnRDb250cm9sbGVyfSBhYm9ydENvbnRyb2xsZXJcblx0ICogQHJldHVybnMge1Byb21pc2V9XG5cdCAqL1xuXHRhc3luYyBjb25maXJtVW5zZW5kKGRpYWxvZ0J1dHRvbiwgYWJvcnRDb250cm9sbGVyKSB7XG5cdFx0Y29uc29sZS5kZWJ1ZyhcIldvcmtmbG93IGZpbmFsIHN0ZXAgOiBjb25maXJtVW5zZW5kXCIsIGRpYWxvZ0J1dHRvbilcblx0XHRhd2FpdCB0aGlzLmNsaWNrRWxlbWVudEFuZFdhaXRGb3IoXG5cdFx0XHRkaWFsb2dCdXR0b24sXG5cdFx0XHR0aGlzLnJvb3Qub3duZXJEb2N1bWVudC5ib2R5LFxuXHRcdFx0KCkgPT4gdGhpcy5yb290Lm93bmVyRG9jdW1lbnQucXVlcnlTZWxlY3RvcihcIltyb2xlPWRpYWxvZ10gYnV0dG9uXCIpID09PSBudWxsLFxuXHRcdFx0YWJvcnRDb250cm9sbGVyXG5cdFx0KVxuXHR9XG5cbn1cblxuZXhwb3J0IGRlZmF1bHQgVUlNZXNzYWdlXG4iLCIvKiogQG1vZHVsZSB1aXBpLW1lc3NhZ2UgQVBJIGZvciBVSU1lc3NhZ2UgKi9cblxuLyogZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzICovXG5pbXBvcnQgVUlNZXNzYWdlIGZyb20gXCIuLi91aS9kZWZhdWx0L3VpLW1lc3NhZ2UuanNcIlxuXG5jbGFzcyBGYWlsZWRXb3JrZmxvd0V4Y2VwdGlvbiBleHRlbmRzIEVycm9yIHt9XG5cbmNsYXNzIFVJUElNZXNzYWdlIHtcblxuXHQvKipcblx0ICogQHBhcmFtIHtVSU1lc3NhZ2V9IHVpTWVzc2FnZVxuXHQgKi9cblx0Y29uc3RydWN0b3IodWlNZXNzYWdlKSB7XG5cdFx0dGhpcy5fdWlNZXNzYWdlID0gdWlNZXNzYWdlXG5cdH1cblxuXHQvKipcblx0ICogQHBhcmFtIHtBYm9ydENvbnRyb2xsZXJ9IGFib3J0Q29udHJvbGxlclxuXHQgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn1cblx0ICovXG5cdGFzeW5jIHVuc2VuZChhYm9ydENvbnRyb2xsZXIpIHtcblx0XHRjb25zb2xlLmRlYnVnKFwiVUlQSU1lc3NhZ2UgdW5zZW5kXCIpXG5cdFx0bGV0IGFjdGlvbkJ1dHRvblxuXHRcdGxldCB1bnNlbmRCdXR0b25cblx0XHR0cnkge1xuXHRcdFx0YWN0aW9uQnV0dG9uID0gYXdhaXQgdGhpcy51aU1lc3NhZ2Uuc2hvd0FjdGlvbnNNZW51QnV0dG9uKGFib3J0Q29udHJvbGxlcilcblx0XHRcdHVuc2VuZEJ1dHRvbiA9IGF3YWl0IHRoaXMudWlNZXNzYWdlLm9wZW5BY3Rpb25zTWVudShhY3Rpb25CdXR0b24sIGFib3J0Q29udHJvbGxlcilcblx0XHRcdGNvbnNvbGUuZGVidWcoXCJ1bnNlbmRCdXR0b25cIiwgdW5zZW5kQnV0dG9uKVxuXHRcdFx0Y29uc3QgZGlhbG9nQnV0dG9uID0gYXdhaXQgdGhpcy51aU1lc3NhZ2Uub3BlbkNvbmZpcm1VbnNlbmRNb2RhbCh1bnNlbmRCdXR0b24sIGFib3J0Q29udHJvbGxlcilcblx0XHRcdGF3YWl0IHRoaXMudWlNZXNzYWdlLmNvbmZpcm1VbnNlbmQoZGlhbG9nQnV0dG9uLCBhYm9ydENvbnRyb2xsZXIpXG5cdFx0XHR0aGlzLnVpTWVzc2FnZS5yb290LnNldEF0dHJpYnV0ZShcImRhdGEtaWRtdS11bnNlbnRcIiwgXCJcIilcblx0XHRcdHJldHVybiB0cnVlXG5cdFx0fSBjYXRjaChleCkge1xuXHRcdFx0Y29uc29sZS5lcnJvcihleClcblx0XHRcdHRoaXMudWlNZXNzYWdlLnJvb3Quc2V0QXR0cmlidXRlKFwiZGF0YS1pZG11LWlnbm9yZVwiLCBcIlwiKVxuXHRcdFx0Ly8gRGlzbWlzcyBhbnkgb3BlbiBvdmVybGF5IHNvIHRoZSBuZXh0IG1lc3NhZ2Ugc3RhcnRzIGNsZWFuXG5cdFx0XHR0cnkge1xuXHRcdFx0XHRjb25zdCBkb2MgPSB0aGlzLnVpTWVzc2FnZS5yb290Lm93bmVyRG9jdW1lbnRcblx0XHRcdFx0ZG9jLmJvZHkuZGlzcGF0Y2hFdmVudChuZXcgS2V5Ym9hcmRFdmVudChcImtleWRvd25cIiwgeyBrZXk6IFwiRXNjYXBlXCIsIGJ1YmJsZXM6IHRydWUgfSkpXG5cdFx0XHRcdGF3YWl0IG5ldyBQcm9taXNlKHJlc29sdmUgPT4gc2V0VGltZW91dChyZXNvbHZlLCAyMDApKVxuXHRcdFx0XHQvLyBJZiBkaWFsb2cgaXMgc3RpbGwgb3BlbiwgcHJlc3MgRXNjYXBlIGFnYWluXG5cdFx0XHRcdGlmIChkb2MucXVlcnlTZWxlY3RvcihcIltyb2xlPWRpYWxvZ11cIikpIHtcblx0XHRcdFx0XHRkb2MuYm9keS5kaXNwYXRjaEV2ZW50KG5ldyBLZXlib2FyZEV2ZW50KFwia2V5ZG93blwiLCB7IGtleTogXCJFc2NhcGVcIiwgYnViYmxlczogdHJ1ZSB9KSlcblx0XHRcdFx0XHRhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMjAwKSlcblx0XHRcdFx0fVxuXHRcdFx0fSBjYXRjaCAoXykgeyAvKiBiZXN0LWVmZm9ydCBjbGVhbnVwICovIH1cblx0XHRcdHRocm93IG5ldyBGYWlsZWRXb3JrZmxvd0V4Y2VwdGlvbihcIkZhaWxlZCB0byBleGVjdXRlIHdvcmtmbG93IGZvciB0aGlzIG1lc3NhZ2VcIiwgZXgpXG5cdFx0fVxuXHR9XG5cblx0LyoqXG5cdCAqIEB0eXBlIHtVSU1lc3NhZ2V9XG5cdCAqL1xuXHRnZXQgdWlNZXNzYWdlKCkge1xuXHRcdHJldHVybiB0aGlzLl91aU1lc3NhZ2Vcblx0fVxuXG59XG5leHBvcnQgeyBGYWlsZWRXb3JrZmxvd0V4Y2VwdGlvbiB9XG5leHBvcnQgZGVmYXVsdCBVSVBJTWVzc2FnZVxuIiwiaW1wb3J0IFVJQ29tcG9uZW50IGZyb20gXCIuL3VpLWNvbXBvbmVudC5qc1wiXG5cbi8qIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFycyAqL1xuaW1wb3J0IFVJUElNZXNzYWdlIGZyb20gXCIuLi91aXBpL3VpcGktbWVzc2FnZS5qc1wiXG5cbi8qKlxuICpcbiAqIEBhYnN0cmFjdFxuICovXG5jbGFzcyBVSSBleHRlbmRzIFVJQ29tcG9uZW50IHtcblxuXHQvKipcblx0ICpcblx0ICogQGFic3RyYWN0XG5cdCAqIEByZXR1cm5zIHtVSX1cblx0ICovXG5cdHN0YXRpYyBjcmVhdGUoKSB7XG5cdH1cblxuXHQvKipcblx0ICpcblx0ICogQGFic3RyYWN0XG5cdCAqIEBwYXJhbSB7QWJvcnRDb250cm9sbGVyfSBhYm9ydENvbnRyb2xsZXJcblx0ICogQHJldHVybnMge1Byb21pc2V9XG5cdCAqL1xuXHQvKiBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnMgKi9cblx0YXN5bmMgZmV0Y2hBbmRSZW5kZXJUaHJlYWROZXh0TWVzc2FnZVBhZ2UoYWJvcnRDb250cm9sbGVyKSB7XG5cdH1cblxuXHQvKipcblx0ICpcblx0ICogQGFic3RyYWN0XG5cdCAqIEByZXR1cm5zIHtQcm9taXNlPFVJUElNZXNzYWdlPn1cblx0ICovXG5cdGFzeW5jIGdldE5leHRVSVBJTWVzc2FnZSgpIHtcblx0fVxuXG59XG5cbmV4cG9ydCBkZWZhdWx0IFVJXG4iLCIvKiogQG1vZHVsZSBkb20tbG9va3VwIFV0aWxzIG1vZHVsZSBmb3IgbG9va2luZyB1cCBlbGVtZW50cyBvbiB0aGUgZGVmYXVsdCBVSSAqL1xuXG5pbXBvcnQgeyB3YWl0Rm9yRWxlbWVudCB9IGZyb20gXCIuLi8uLi9kb20vYXN5bmMtZXZlbnRzLmpzXCJcblxuLyoqXG4gKiBGaW5kcyB0aGUgc2Nyb2xsYWJsZSBtZXNzYWdlcyBjb250YWluZXIgaW5zaWRlIHRoZSBjb252ZXJzYXRpb24gcGFuZWwuXG4gKiBJbnN0YWdyYW0gcmVtb3ZlZCByb2xlPVwiZ3JpZFwiIOKAlCB3ZSBub3cgbG9jYXRlIHRoZSBjb250YWluZXIgdmlhIGFyaWEtbGFiZWxcbiAqIGFuZCB3YWxrIGludG8gaXRzIHNjcm9sbGFibGUgY2hpbGQuXG4gKlxuICogQHBhcmFtIHtXaW5kb3d9IHdpbmRvd1xuICogQHJldHVybnMge0hUTUxEaXZFbGVtZW50fG51bGx9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaW5kTWVzc2FnZXNXcmFwcGVyKHdpbmRvdykge1xuXHRjb25zdCBjb252ZXJzYXRpb24gPSB3aW5kb3cuZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcIlthcmlhLWxhYmVsXj0nQ29udmVyc2F0aW9uJ11cIilcblx0aWYgKCFjb252ZXJzYXRpb24pIHtcblx0XHRyZXR1cm4gbnVsbFxuXHR9XG5cdGNvbnN0IHNjcm9sbGFibGUgPSBmaW5kU2Nyb2xsYWJsZUNoaWxkKGNvbnZlcnNhdGlvbiwgd2luZG93KVxuXHRpZiAoIXNjcm9sbGFibGUpIHtcblx0XHRyZXR1cm4gbnVsbFxuXHR9XG5cdHJldHVybiBzY3JvbGxhYmxlXG59XG5cbi8qKlxuICogUmVjdXJzaXZlbHkgZmluZHMgdGhlIGZpcnN0IHNjcm9sbGFibGUgZGVzY2VuZGFudCBvZiBhIGdpdmVuIGVsZW1lbnQuXG4gKlxuICogQHBhcmFtIHtFbGVtZW50fSBwYXJlbnRcbiAqIEBwYXJhbSB7V2luZG93fSB3aW5kb3dcbiAqIEByZXR1cm5zIHtIVE1MRGl2RWxlbWVudHxudWxsfVxuICovXG5mdW5jdGlvbiBmaW5kU2Nyb2xsYWJsZUNoaWxkKHBhcmVudCwgd2luZG93KSB7XG5cdGZvciAoY29uc3QgY2hpbGQgb2YgcGFyZW50LmNoaWxkcmVuKSB7XG5cdFx0Y29uc3Qgc3R5bGUgPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShjaGlsZClcblx0XHRpZiAoXG5cdFx0XHQoc3R5bGUub3ZlcmZsb3dZID09PSBcImF1dG9cIiB8fCBzdHlsZS5vdmVyZmxvd1kgPT09IFwic2Nyb2xsXCIpICYmXG5cdFx0XHRjaGlsZC5zY3JvbGxIZWlnaHQgPiBjaGlsZC5jbGllbnRIZWlnaHRcblx0XHQpIHtcblx0XHRcdHJldHVybiBjaGlsZFxuXHRcdH1cblx0XHRjb25zdCBmb3VuZCA9IGZpbmRTY3JvbGxhYmxlQ2hpbGQoY2hpbGQsIHdpbmRvdylcblx0XHRpZiAoZm91bmQpIHtcblx0XHRcdHJldHVybiBmb3VuZFxuXHRcdH1cblx0fVxuXHRyZXR1cm4gbnVsbFxufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIGlubmVyIGNvbnRhaW5lciB0aGF0IGhvbGRzIGluZGl2aWR1YWwgbWVzc2FnZSByb3cgZGl2cy5cbiAqIFRyYXZlcnNlcyB3cmFwcGVyIGxheWVycyB0byBmaW5kIHRoZSBkaXYgd2l0aCB0aGUgbW9zdCBjaGlsZHJlbiAodGhlIG1lc3NhZ2UgbGlzdCkuXG4gKlxuICogQHBhcmFtIHtFbGVtZW50fSBzY3JvbGxhYmxlXG4gKiBAcmV0dXJucyB7SFRNTERpdkVsZW1lbnR9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRNZXNzYWdlc0lubmVyQ29udGFpbmVyKHNjcm9sbGFibGUpIHtcblx0Ly8gSW5zdGFncmFtIHdyYXBzIG1lc3NhZ2VzIGluIHNldmVyYWwgbmVzdGVkIGRpdnMuXG5cdC8vIFN0cmF0ZWd5OiBmaW5kIHRoZSBkZWVwZXN0IGRlc2NlbmRhbnQgKHdpdGhpbiAzIGxldmVscykgdGhhdCBoYXMgdGhlIG1vc3QgY2hpbGRyZW4sXG5cdC8vIHNpbmNlIHRoZSBhY3R1YWwgbWVzc2FnZXMgY29udGFpbmVyIGhhcyBtYW55IGRpcmVjdCBjaGlsZHJlbiAob25lIHBlciBtZXNzYWdlIHJvdykuXG5cdGxldCBiZXN0ID0gc2Nyb2xsYWJsZVxuXHRsZXQgYmVzdENvdW50ID0gc2Nyb2xsYWJsZS5jaGlsZHJlbi5sZW5ndGhcblxuXHRmdW5jdGlvbiBzZWFyY2goZWwsIGRlcHRoKSB7XG5cdFx0aWYgKGRlcHRoID4gMykgcmV0dXJuXG5cdFx0Zm9yIChjb25zdCBjaGlsZCBvZiBlbC5jaGlsZHJlbikge1xuXHRcdFx0aWYgKGNoaWxkLmNoaWxkcmVuLmxlbmd0aCA+IGJlc3RDb3VudCkge1xuXHRcdFx0XHRiZXN0ID0gY2hpbGRcblx0XHRcdFx0YmVzdENvdW50ID0gY2hpbGQuY2hpbGRyZW4ubGVuZ3RoXG5cdFx0XHR9XG5cdFx0XHRzZWFyY2goY2hpbGQsIGRlcHRoICsgMSlcblx0XHR9XG5cdH1cblxuXHRzZWFyY2goc2Nyb2xsYWJsZSwgMClcblx0cmV0dXJuIGJlc3Rcbn1cblxuLyoqXG4gKiBEZXRlcm1pbmVzIHdoZXRoZXIgYSBtZXNzYWdlIGVsZW1lbnQgd2FzIHNlbnQgYnkgdGhlIGN1cnJlbnQgdXNlci5cbiAqIEluc3RhZ3JhbSBhbGlnbnMgc2VudCBtZXNzYWdlcyB0byB0aGUgcmlnaHQgdXNpbmcgZmxleGJveCAoanVzdGlmeS1jb250ZW50OiBmbGV4LWVuZCkuXG4gKlxuICogQHBhcmFtIHtFbGVtZW50fSBlbGVtZW50XG4gKiBAcGFyYW0ge1dpbmRvd30gd2luZG93XG4gKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzU2VudEJ5Q3VycmVudFVzZXIoZWxlbWVudCwgd2luZG93KSB7XG5cdC8vIEJGUyB0aHJvdWdoIGFsbCBkZXNjZW5kYW50cyB1cCB0byBkZXB0aCA4LlxuXHQvLyBJbnN0YWdyYW0gcGxhY2VzIGp1c3RpZnktY29udGVudDogZmxleC1lbmQgb24gYSBuZXN0ZWQgZGl2IChkZXB0aCB+NSlcblx0Ly8gdGhhdCBtYXkgYmUgb24gYW55IGNoaWxkIGJyYW5jaCwgbm90IGp1c3QgdGhlIGZpcnN0LWNoaWxkIHBhdGguXG5cdGNvbnN0IHF1ZXVlID0gW3sgZWw6IGVsZW1lbnQsIGRlcHRoOiAwIH1dXG5cdHdoaWxlIChxdWV1ZS5sZW5ndGggPiAwKSB7XG5cdFx0Y29uc3QgeyBlbCwgZGVwdGggfSA9IHF1ZXVlLnNoaWZ0KClcblx0XHRjb25zdCBzID0gd2luZG93LmdldENvbXB1dGVkU3R5bGUoZWwpXG5cdFx0aWYgKHMuanVzdGlmeUNvbnRlbnQgPT09IFwiZmxleC1lbmRcIikge1xuXHRcdFx0cmV0dXJuIHRydWVcblx0XHR9XG5cdFx0aWYgKGRlcHRoIDwgOCkge1xuXHRcdFx0Zm9yIChjb25zdCBjaGlsZCBvZiBlbC5jaGlsZHJlbikge1xuXHRcdFx0XHRxdWV1ZS5wdXNoKHsgZWw6IGNoaWxkLCBkZXB0aDogZGVwdGggKyAxIH0pXG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cdHJldHVybiBmYWxzZVxufVxuXG4vKipcbiAqIEdldHMgdGhlIGZpcnN0IHZpc2libGUgbWVzc2FnZSBzZW50IGJ5IHRoZSBjdXJyZW50IHVzZXIgdGhhdCBoYXNuJ3QgYmVlbiBwcm9jZXNzZWQgeWV0LlxuICpcbiAqIEBwYXJhbSB7RWxlbWVudH0gcm9vdCAtIFRoZSBzY3JvbGxhYmxlIG1lc3NhZ2VzIHdyYXBwZXJcbiAqIEBwYXJhbSB7QWJvcnRDb250cm9sbGVyfSBhYm9ydENvbnRyb2xsZXJcbiAqIEBwYXJhbSB7V2luZG93fSB3aW5kb3dcbiAqIEByZXR1cm5zIHtFbGVtZW50fHVuZGVmaW5lZH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEZpcnN0VmlzaWJsZU1lc3NhZ2Uocm9vdCwgYWJvcnRDb250cm9sbGVyLCB3aW5kb3cpIHtcblx0Y29uc3QgaW5uZXJDb250YWluZXIgPSBnZXRNZXNzYWdlc0lubmVyQ29udGFpbmVyKHJvb3QpXG5cdGlmICghaW5uZXJDb250YWluZXIpIHtcblx0XHRjb25zb2xlLmRlYnVnKFwiZ2V0Rmlyc3RWaXNpYmxlTWVzc2FnZTogbm8gaW5uZXIgY29udGFpbmVyIGZvdW5kXCIpXG5cdFx0cmV0dXJuXG5cdH1cblxuXHRjb25zdCBlbGVtZW50cyA9IFsuLi5pbm5lckNvbnRhaW5lci5jaGlsZHJlbl1cblx0XHQuZmlsdGVyKGQgPT4ge1xuXHRcdFx0aWYgKGQuaGFzQXR0cmlidXRlKFwiZGF0YS1pZG11LWlnbm9yZVwiKSkgcmV0dXJuIGZhbHNlXG5cdFx0XHRpZiAoZC5oYXNBdHRyaWJ1dGUoXCJkYXRhLWlkbXUtdW5zZW50XCIpKSByZXR1cm4gZmFsc2Vcblx0XHRcdC8vIE11c3QgY29udGFpbiBtZXNzYWdlIGNvbnRlbnQgaW5kaWNhdG9yc1xuXHRcdFx0Y29uc3QgaGFzTWVzc2FnZUNvbnRlbnQgPSBkLnF1ZXJ5U2VsZWN0b3IoXCJbcm9sZT1ub25lXVwiKSB8fCBkLnF1ZXJ5U2VsZWN0b3IoXCJbcm9sZT1wcmVzZW50YXRpb25dXCIpXG5cdFx0XHRpZiAoIWhhc01lc3NhZ2VDb250ZW50KSByZXR1cm4gZmFsc2Vcblx0XHRcdHJldHVybiBpc1NlbnRCeUN1cnJlbnRVc2VyKGQsIHdpbmRvdylcblx0XHR9KVxuXG5cdGVsZW1lbnRzLnJldmVyc2UoKVxuXHRpZihlbGVtZW50cy5sZW5ndGggPj0gMSkge1xuXHRcdGNvbnNvbGUuZGVidWcoXCJnZXRGaXJzdFZpc2libGVNZXNzYWdlXCIsIGVsZW1lbnRzLmxlbmd0aCwgXCJjYW5kaWRhdGUgZWxlbWVudHNcIilcblx0fSBlbHNlIHtcblx0XHRjb25zb2xlLmVycm9yKFwiZ2V0Rmlyc3RWaXNpYmxlTWVzc2FnZSBjb3VsZCBub3QgZmluZCBhbnkgZWxlbWVudHMuIElmIHRoZXJlIGFjdHVhbGx5IGFyZSBtZXNzYWdlcyBvbiB0aGUgcGFnZSB0aGF0IG1lYW5zIHRoZSBxdWVyeSBzZWxlY3RvciBtaWdodCBiZSBvdXQgb2YgZGF0ZS5cIilcblx0fVxuXG5cdGZvciAoY29uc3QgZWxlbWVudCBvZiBlbGVtZW50cykge1xuXHRcdGlmIChhYm9ydENvbnRyb2xsZXIuc2lnbmFsLmFib3J0ZWQpIHtcblx0XHRcdGNvbnNvbGUuZGVidWcoXCJhYm9ydENvbnRyb2xsZXIgaW50ZXJ1cHRlZCB0aGUgbWVzc2FnZSBmaWx0ZXJpbmcgcHJvY2Vzczogc3RvcHBpbmcuLi5cIilcblx0XHRcdGJyZWFrXG5cdFx0fVxuXHRcdGNvbnN0IHZpc2liaWxpdHlDaGVjayA9IGVsZW1lbnQuY2hlY2tWaXNpYmlsaXR5KHtcblx0XHRcdHZpc2liaWxpdHlQcm9wZXJ0eTogdHJ1ZSxcblx0XHRcdGNvbnRlbnRWaXNpYmlsaXR5QXV0bzogdHJ1ZSxcblx0XHRcdG9wYWNpdHlQcm9wZXJ0eTogdHJ1ZSxcblx0XHR9KVxuXHRcdGlmICh2aXNpYmlsaXR5Q2hlY2sgPT09IGZhbHNlKSB7XG5cdFx0XHRjb25zb2xlLmRlYnVnKFwidmlzaWJpbGl0eUNoZWNrXCIsIHZpc2liaWxpdHlDaGVjaylcblx0XHRcdGNvbnRpbnVlXG5cdFx0fVxuXHRcdGNvbnN0IHJlY3QgPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpXG5cdFx0Ly8gQ2hlY2sgaWYgZWxlbWVudCBpcyBhdCBsZWFzdCBwYXJ0aWFsbHkgaW4gdmlld3BvcnQuXG5cdFx0Ly8gRm9yIHRhbGwgZWxlbWVudHMgKGltYWdlcywgbG9uZyB0ZXh0KSwgcmVjdC55IGNhbiBiZSBuZWdhdGl2ZVxuXHRcdC8vIHdoaWxlIHRoZSBlbGVtZW50IGlzIHN0aWxsIHZpc2libGUuIFVzZSBib3R0b20gZWRnZSBpbnN0ZWFkLlxuXHRcdGlmIChyZWN0LnkgKyByZWN0LmhlaWdodCA8IDUwIHx8IHJlY3QuaGVpZ2h0ID09PSAwKSB7XG5cdFx0XHRjb25zb2xlLmRlYnVnKFwiaXNJblZpZXcgZmFpbGVkXCIsIHJlY3QueSwgcmVjdC5oZWlnaHQpXG5cdFx0XHRjb250aW51ZVxuXHRcdH1cblx0XHRlbGVtZW50LnNldEF0dHJpYnV0ZShcImRhdGEtaWRtdS1pZ25vcmVcIiwgXCJcIilcblx0XHRjb25zb2xlLmRlYnVnKFwiTWVzc2FnZSBpbiB2aWV3LCB0ZXN0aW5nIHdvcmtmbG93Li4uXCIsIGVsZW1lbnQpXG5cdFx0cmV0dXJuIGVsZW1lbnRcblx0fVxufVxuXG4vKipcbiAqIFNjcm9sbHMgdG8gdG9wIHRvIHRyaWdnZXIgbG9hZGluZyBvZiBvbGRlciBtZXNzYWdlcy5cbiAqIEhhbmRsZXMgYm90aCBub3JtYWwgYW5kIGNvbHVtbi1yZXZlcnNlIGxheW91dHMuXG4gKlxuICogSW4gY29sdW1uLXJldmVyc2UgKEluc3RhZ3JhbSdzIGN1cnJlbnQgbGF5b3V0KTpcbiAqICAgc2Nyb2xsVG9wPTAgaXMgdGhlIEJPVFRPTSAobmV3ZXN0IG1lc3NhZ2VzKVxuICogICBzY3JvbGxUb3A9LShzY3JvbGxIZWlnaHQtY2xpZW50SGVpZ2h0KSBpcyB0aGUgVE9QIChvbGRlc3QgbWVzc2FnZXMpXG4gKlxuICogQHBhcmFtIHtFbGVtZW50fSByb290XG4gKiBAcGFyYW0ge0Fib3J0Q29udHJvbGxlcn0gYWJvcnRDb250cm9sbGVyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn1cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRNb3JlTWVzc2FnZXMocm9vdCwgYWJvcnRDb250cm9sbGVyKSB7XG5cdGNvbnNvbGUuZGVidWcoXCJsb2FkTW9yZU1lc3NhZ2VzIGxvb2tpbmcgZm9yIGxvYWRlci4uLiBcIilcblx0Y29uc3Qgc2Nyb2xsQWJvcnRDb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpXG5cdGxldCBmaW5kTG9hZGVyVGltZW91dFxuXHRsZXQgcmVzb2x2ZVRpbWVvdXRcblx0Y29uc3QgYWJvcnRIYW5kbGVyID0gKCkgPT4ge1xuXHRcdHNjcm9sbEFib3J0Q29udHJvbGxlci5hYm9ydCgpXG5cdFx0Y2xlYXJUaW1lb3V0KGZpbmRMb2FkZXJUaW1lb3V0KVxuXHRcdGlmIChyZXNvbHZlVGltZW91dCkge1xuXHRcdFx0cmVzb2x2ZVRpbWVvdXQoKVxuXHRcdH1cblx0fVxuXHRhYm9ydENvbnRyb2xsZXIuc2lnbmFsLmFkZEV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBhYm9ydEhhbmRsZXIpXG5cblx0Ly8gRGV0ZWN0IGNvbHVtbi1yZXZlcnNlIGxheW91dFxuXHRjb25zdCBzdHlsZSA9IHJvb3Qub3duZXJEb2N1bWVudC5kZWZhdWx0Vmlldy5nZXRDb21wdXRlZFN0eWxlKHJvb3QpXG5cdGNvbnN0IGlzUmV2ZXJzZWQgPSBzdHlsZS5mbGV4RGlyZWN0aW9uID09PSBcImNvbHVtbi1yZXZlcnNlXCJcblx0Ly8gSW4gY29sdW1uLXJldmVyc2UsIFwic2Nyb2xsIHRvIHRvcFwiIG1lYW5zIG1vc3QgbmVnYXRpdmUgc2Nyb2xsVG9wXG5cdGNvbnN0IHNjcm9sbFRvVG9wVmFsdWUgPSBpc1JldmVyc2VkXG5cdFx0PyAtKHJvb3Quc2Nyb2xsSGVpZ2h0IC0gcm9vdC5jbGllbnRIZWlnaHQpXG5cdFx0OiAwXG5cdC8vIEluIGNvbHVtbi1yZXZlcnNlLCBcImF0IHRvcFwiIG1lYW5zIHNjcm9sbFRvcCBpcyBhdCBvciBuZWFyIG1pbmltdW1cblx0Y29uc3QgaXNBdFRvcCA9ICgpID0+IGlzUmV2ZXJzZWRcblx0XHQ/IHJvb3Quc2Nyb2xsVG9wIDw9IHNjcm9sbFRvVG9wVmFsdWUgKyA1XG5cdFx0OiByb290LnNjcm9sbFRvcCA9PT0gMFxuXG5cdGNvbnN0IGJlZm9yZVNjcm9sbCA9IHJvb3Quc2Nyb2xsVG9wXG5cdGNvbnN0IGJlZm9yZUhlaWdodCA9IHJvb3Quc2Nyb2xsSGVpZ2h0XG5cdHJvb3Quc2Nyb2xsVG9wID0gc2Nyb2xsVG9Ub3BWYWx1ZVxuXG5cdC8vIEhlbHBlcjogZmluZCBhIHZpc2libGUgbG9hZGVyIHdpdGhpbiB0aGUgc2Nyb2xsYWJsZSByb290J3Mgdmlld3BvcnRcblx0Y29uc3QgZmluZFZpc2libGVMb2FkZXIgPSAoKSA9PiB7XG5cdFx0Y29uc3QgYmFycyA9IHJvb3QucXVlcnlTZWxlY3RvckFsbChcIltyb2xlPXByb2dyZXNzYmFyXVwiKVxuXHRcdGZvciAoY29uc3QgYmFyIG9mIGJhcnMpIHtcblx0XHRcdGNvbnN0IHJlY3QgPSBiYXIuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcblx0XHRcdGNvbnN0IHJvb3RSZWN0ID0gcm9vdC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuXHRcdFx0Ly8gTXVzdCBiZSB3aXRoaW4gcm9vdCdzIGhvcml6b250YWwrdmVydGljYWwgYm91bmRzIGFuZCBoYXZlIGRpbWVuc2lvbnNcblx0XHRcdGlmIChyZWN0LmhlaWdodCA+IDAgJiYgcmVjdC55ID49IHJvb3RSZWN0LnkgLSAxMDAgJiYgcmVjdC55IDw9IHJvb3RSZWN0LnkgKyByb290UmVjdC5oZWlnaHQgKyAxMDApIHtcblx0XHRcdFx0cmV0dXJuIGJhclxuXHRcdFx0fVxuXHRcdH1cblx0XHRyZXR1cm4gbnVsbFxuXHR9XG5cblx0Ly8gU2hvcnQgY2hhdDogZXZlcnl0aGluZyBmaXRzIGluIHZpZXdwb3J0LCBub3RoaW5nIHRvIGxvYWRcblx0Y29uc3Qgbm9TY3JvbGxOZWVkZWQgPSBpc1JldmVyc2VkXG5cdFx0PyBiZWZvcmVTY3JvbGwgPT09IDAgJiYgcm9vdC5zY3JvbGxIZWlnaHQgPD0gcm9vdC5jbGllbnRIZWlnaHQgKyA1MFxuXHRcdDogYmVmb3JlU2Nyb2xsID09PSAwICYmIHJvb3Quc2Nyb2xsSGVpZ2h0IDw9IHJvb3QuY2xpZW50SGVpZ2h0ICsgNTBcblx0aWYgKG5vU2Nyb2xsTmVlZGVkKSB7XG5cdFx0Y29uc29sZS5kZWJ1ZyhcImxvYWRNb3JlTWVzc2FnZXM6IGNoYXQgZml0cyBpbiB2aWV3cG9ydCwgbWFya2luZyBhcyBkb25lXCIpXG5cdFx0YWJvcnRDb250cm9sbGVyLnNpZ25hbC5yZW1vdmVFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgYWJvcnRIYW5kbGVyKVxuXHRcdHJldHVybiB0cnVlXG5cdH1cblxuXHQvLyBBbHJlYWR5IGF0IHRvcCBhZnRlciBzY3JvbGxpbmc6IHdhaXQgYnJpZWZseSBmb3IgbmV3IGNvbnRlbnQsIHRoZW4gY2hlY2tcblx0aWYgKGlzQXRUb3AoKSkge1xuXHRcdC8vIEdpdmUgSW5zdGFncmFtIGEgbW9tZW50IHRvIHN0YXJ0IGxvYWRpbmcgb2xkZXIgbWVzc2FnZXNcblx0XHRhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgNTAwKSlcblxuXHRcdC8vIENoZWNrIGlmIGEgdmlzaWJsZSBsb2FkZXIgYXBwZWFyZWRcblx0XHRjb25zdCBsb2FkZXIgPSBmaW5kVmlzaWJsZUxvYWRlcigpXG5cdFx0aWYgKGxvYWRlcikge1xuXHRcdFx0Y29uc29sZS5kZWJ1ZyhcImxvYWRNb3JlTWVzc2FnZXM6IEZvdW5kIHZpc2libGUgbG9hZGVyIGFmdGVyIHNjcm9sbDsgd2FpdGluZyBmb3IgcmVtb3ZhbCAobWF4IDVzKVwiKVxuXHRcdFx0YXdhaXQgUHJvbWlzZS5yYWNlKFtcblx0XHRcdFx0d2FpdEZvckVsZW1lbnQocm9vdCwgKCkgPT4gZmluZFZpc2libGVMb2FkZXIoKSA9PT0gbnVsbCwgYWJvcnRDb250cm9sbGVyKSxcblx0XHRcdFx0bmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDUwMDApKVxuXHRcdFx0XSlcblx0XHRcdGFib3J0Q29udHJvbGxlci5zaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIGFib3J0SGFuZGxlcilcblx0XHRcdGNvbnN0IGdyZXcgPSByb290LnNjcm9sbEhlaWdodCA+IGJlZm9yZUhlaWdodFxuXHRcdFx0Y29uc29sZS5kZWJ1ZyhgbG9hZE1vcmVNZXNzYWdlczogbG9hZGVyIHBoYXNlIGRvbmUsIGNvbnRlbnQgJHtncmV3ID8gXCJncmV3XCIgOiBcImRpZCBub3QgZ3Jvd1wifWApXG5cdFx0XHRyZXR1cm4gIWdyZXdcblx0XHR9XG5cblx0XHQvLyBObyBsb2FkZXIgYXBwZWFyZWQg4oCUIGNoZWNrIGlmIHNjcm9sbEhlaWdodCBncmV3IChuZXcgY29udGVudCBsb2FkZWQgd2l0aG91dCBzcGlubmVyKVxuXHRcdGNvbnN0IGdyZXcgPSByb290LnNjcm9sbEhlaWdodCA+IGJlZm9yZUhlaWdodFxuXHRcdGlmICghZ3Jldykge1xuXHRcdFx0Y29uc29sZS5kZWJ1ZyhcImxvYWRNb3JlTWVzc2FnZXM6IGF0IHRvcCwgbm8gbG9hZGVyLCBubyBuZXcgY29udGVudCDigJQgcmVhY2hlZCBsYXN0IHBhZ2VcIilcblx0XHRcdGFib3J0Q29udHJvbGxlci5zaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIGFib3J0SGFuZGxlcilcblx0XHRcdHJldHVybiB0cnVlXG5cdFx0fVxuXHR9XG5cblx0Ly8gRmFsbGJhY2s6IHdhaXQgZm9yIHByb2dyZXNzYmFyIHRvIGFwcGVhciAod2l0aCBzaG9ydGVyIHRpbWVvdXQpXG5cdGxldCBsb2FkaW5nRWxlbWVudFxuXHR0cnkge1xuXHRcdGxvYWRpbmdFbGVtZW50ID0gYXdhaXQgUHJvbWlzZS5yYWNlKFtcblx0XHRcdHdhaXRGb3JFbGVtZW50KHJvb3QsICgpID0+IHtcblx0XHRcdFx0aWYgKGZpbmRWaXNpYmxlTG9hZGVyKCkgPT09IG51bGwpIHtcblx0XHRcdFx0XHRyb290LnNjcm9sbFRvcCA9IHNjcm9sbFRvVG9wVmFsdWVcblx0XHRcdFx0fVxuXHRcdFx0XHRyZXR1cm4gZmluZFZpc2libGVMb2FkZXIoKVxuXHRcdFx0fSwgc2Nyb2xsQWJvcnRDb250cm9sbGVyKSxcblx0XHRcdG5ldyBQcm9taXNlKHJlc29sdmUgPT4ge1xuXHRcdFx0XHRyZXNvbHZlVGltZW91dCA9IHJlc29sdmVcblx0XHRcdFx0ZmluZExvYWRlclRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcblx0XHRcdFx0XHRyZXNvbHZlKClcblx0XHRcdFx0fSwgMzAwMClcblx0XHRcdH0pXG5cdFx0XSlcblx0fSBjYXRjaCAoZXgpIHtcblx0XHRjb25zb2xlLmVycm9yKGV4KVxuXHR9XG5cdHNjcm9sbEFib3J0Q29udHJvbGxlci5hYm9ydCgpXG5cdGFib3J0Q29udHJvbGxlci5zaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIGFib3J0SGFuZGxlcilcblx0Y2xlYXJUaW1lb3V0KGZpbmRMb2FkZXJUaW1lb3V0KVxuXHRpZiAobG9hZGluZ0VsZW1lbnQgJiYgbG9hZGluZ0VsZW1lbnQgIT09IHRydWUpIHtcblx0XHRjb25zb2xlLmRlYnVnKFwibG9hZE1vcmVNZXNzYWdlczogRm91bmQgbG9hZGVyOyBTdGFuZC1ieSB1bnRpbCBpdCBpcyByZW1vdmVkIChtYXggNXMpXCIpXG5cdFx0YXdhaXQgUHJvbWlzZS5yYWNlKFtcblx0XHRcdHdhaXRGb3JFbGVtZW50KHJvb3QsICgpID0+IGZpbmRWaXNpYmxlTG9hZGVyKCkgPT09IG51bGwsIGFib3J0Q29udHJvbGxlciksXG5cdFx0XHRuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgNTAwMCkpXG5cdFx0XSlcblx0fVxuXHRjb25zdCBhdFRvcCA9IGlzQXRUb3AoKVxuXHRjb25zb2xlLmRlYnVnKGBsb2FkTW9yZU1lc3NhZ2VzOiBzY3JvbGxUb3AgaXMgJHtyb290LnNjcm9sbFRvcH0g4oCUICR7YXRUb3AgPyBcInJlYWNoZWQgbGFzdCBwYWdlXCIgOiBcIm5vdCBsYXN0IHBhZ2VcIn1gKVxuXHRyZXR1cm4gYXRUb3Bcbn1cblxuIiwiLyoqIEBtb2R1bGUgdWktbWVzc2FnZXMtd3JhcHBlciBVSSBlbGVtZW50IHJlcHJlc2VudGluZyB0aGUgbWVzc2FnZXMgd3JhcHBlciAqL1xuXG5pbXBvcnQgeyBsb2FkTW9yZU1lc3NhZ2VzIH0gZnJvbSBcIi4vZG9tLWxvb2t1cC5qc1wiXG5pbXBvcnQgVUlDb21wb25lbnQgZnJvbSBcIi4uL3VpLWNvbXBvbmVudC5qc1wiXG5cbmNsYXNzIFVJTWVzc2FnZXNXcmFwcGVyIGV4dGVuZHMgVUlDb21wb25lbnQge1xuXG5cdC8qKlxuXHQgKiBAcGFyYW0ge0Fib3J0Q29udHJvbGxlcn0gYWJvcnRDb250cm9sbGVyXG5cdCAqIEByZXR1cm5zIHtQcm9taXNlfVxuXHQgKi9cblx0ZmV0Y2hBbmRSZW5kZXJUaHJlYWROZXh0TWVzc2FnZVBhZ2UoYWJvcnRDb250cm9sbGVyKSB7XG5cdFx0cmV0dXJuIGxvYWRNb3JlTWVzc2FnZXModGhpcy5yb290LCBhYm9ydENvbnRyb2xsZXIpXG5cdH1cblxufVxuXG5leHBvcnQgZGVmYXVsdCBVSU1lc3NhZ2VzV3JhcHBlclxuIiwiLyoqIEBtb2R1bGUgZGVmYXVsdC11aSBEZWZhdWx0IFVJIC8gRW5nbGlzaCBVSSAqL1xuXG5pbXBvcnQgVUkgZnJvbSBcIi4uL3VpLmpzXCJcbmltcG9ydCB7IGZpbmRNZXNzYWdlc1dyYXBwZXIsIGdldEZpcnN0VmlzaWJsZU1lc3NhZ2UgfSBmcm9tIFwiLi9kb20tbG9va3VwLmpzXCJcbmltcG9ydCBVSVBJTWVzc2FnZSBmcm9tIFwiLi4vLi4vdWlwaS91aXBpLW1lc3NhZ2UuanNcIlxuaW1wb3J0IFVJTWVzc2FnZSBmcm9tIFwiLi91aS1tZXNzYWdlLmpzXCJcbmltcG9ydCBVSU1lc3NhZ2VzV3JhcHBlciBmcm9tIFwiLi91aS1tZXNzYWdlcy13cmFwcGVyLmpzXCJcblxuY2xhc3MgRGVmYXVsdFVJIGV4dGVuZHMgVUkge1xuXG5cdGNvbnN0cnVjdG9yKHJvb3QsIGlkZW50aWZpZXIgPSB7fSkge1xuXHRcdHN1cGVyKHJvb3QsIGlkZW50aWZpZXIpXG5cdFx0dGhpcy5sYXN0U2Nyb2xsVG9wID0gbnVsbFxuXHR9XG5cblx0LyoqXG5cdCAqIEBwYXJhbSB7V2luZG93fSB3aW5kb3dcblx0ICogQHJldHVybnMge0RlZmF1bHRVSX1cblx0ICovXG5cdHN0YXRpYyBjcmVhdGUod2luZG93KSB7XG5cdFx0Y29uc29sZS5kZWJ1ZyhcIlVJIGNyZWF0ZTogTG9va2luZyBmb3IgbWVzc2FnZXNXcmFwcGVyRWxlbWVudFwiKVxuXHRcdGNvbnN0IG1lc3NhZ2VzV3JhcHBlckVsZW1lbnQgPSBmaW5kTWVzc2FnZXNXcmFwcGVyKHdpbmRvdylcblx0XHRpZiAobWVzc2FnZXNXcmFwcGVyRWxlbWVudCAhPT0gbnVsbCkge1xuXHRcdFx0Y29uc29sZS5kZWJ1ZyhcIkZvdW5kIG1lc3NhZ2VzV3JhcHBlckVsZW1lbnRcIiwgbWVzc2FnZXNXcmFwcGVyRWxlbWVudClcblx0XHRcdGNvbnN0IHVpTWVzc2FnZXNXcmFwcGVyID0gbmV3IFVJTWVzc2FnZXNXcmFwcGVyKG1lc3NhZ2VzV3JhcHBlckVsZW1lbnQpXG5cdFx0XHRyZXR1cm4gbmV3IERlZmF1bHRVSSh3aW5kb3csIHsgdWlNZXNzYWdlc1dyYXBwZXIgfSlcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhyb3cgbmV3IEVycm9yKFwiVW5hYmxlIHRvIGZpbmQgbWVzc2FnZXNXcmFwcGVyRWxlbWVudC4gVGhlIHF1ZXJ5IHNlbGVjdG9yIG1pZ2h0IGJlIG91dCBvZiBkYXRlLlwiKVxuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBAcGFyYW0ge0Fib3J0Q29udHJvbGxlcn0gYWJvcnRDb250cm9sbGVyXG5cdCAqIEByZXR1cm5zIHtQcm9taXNlfVxuXHQgKi9cblx0YXN5bmMgZmV0Y2hBbmRSZW5kZXJUaHJlYWROZXh0TWVzc2FnZVBhZ2UoYWJvcnRDb250cm9sbGVyKSB7XG5cdFx0Y29uc29sZS5kZWJ1ZyhcIlVJIGZldGNoQW5kUmVuZGVyVGhyZWFkTmV4dE1lc3NhZ2VQYWdlXCIpXG5cdFx0cmV0dXJuIGF3YWl0IHRoaXMuaWRlbnRpZmllci51aU1lc3NhZ2VzV3JhcHBlci5mZXRjaEFuZFJlbmRlclRocmVhZE5leHRNZXNzYWdlUGFnZShhYm9ydENvbnRyb2xsZXIpXG5cdH1cblxuXHQvKipcblx0ICogU2Nyb2xsIHVudGlsIGEgKHZpc2libGUpIG1lc3NhZ2UgaXMgZm91bmQgYW5kIHJldHVybiBpdC5cblx0ICpcblx0ICogSW5zdGFncmFtIHVzZXMgZmxleC1kaXJlY3Rpb246IGNvbHVtbi1yZXZlcnNlIG9uIHRoZSBtZXNzYWdlcyBjb250YWluZXIuXG5cdCAqIFRoaXMgbWVhbnMgc2Nyb2xsVG9wPTAgaXMgdGhlIEJPVFRPTSAobmV3ZXN0IG1lc3NhZ2VzKSBhbmQgc2Nyb2xsaW5nIHRvXG5cdCAqIG9sZGVyIG1lc3NhZ2VzIHJlcXVpcmVzIE5FR0FUSVZFIHNjcm9sbFRvcCB2YWx1ZXMuXG5cdCAqIEluIG5vcm1hbCAobm9uLXJldmVyc2VkKSBsYXlvdXRzLCBzY3JvbGxUb3A9MCBpcyB0aGUgdG9wIGFuZCB0aGUgbWF4IGlzIHBvc2l0aXZlLlxuXHQgKlxuXHQgKiBUaGlzIG1ldGhvZCBkZXRlY3RzIHRoZSBsYXlvdXQgZGlyZWN0aW9uIGFuZCBzY3JvbGxzIGFjY29yZGluZ2x5LlxuXHQgKlxuXHQgKiBAcGFyYW0ge0Fib3J0Q29udHJvbGxlcn0gYWJvcnRDb250cm9sbGVyXG5cdCAqIEByZXR1cm5zIHtQcm9taXNlPFVJUElNZXNzYWdlfGZhbHNlPn1cblx0ICovXG5cdGFzeW5jIGdldE5leHRVSVBJTWVzc2FnZShhYm9ydENvbnRyb2xsZXIpIHtcblx0XHRjb25zb2xlLmRlYnVnKFwiVUkgZ2V0TmV4dFVJUElNZXNzYWdlXCIsIHRoaXMubGFzdFNjcm9sbFRvcClcblx0XHRjb25zdCB1aU1lc3NhZ2VzV3JhcHBlclJvb3QgPSB0aGlzLmlkZW50aWZpZXIudWlNZXNzYWdlc1dyYXBwZXIucm9vdFxuXG5cdFx0Ly8gRGV0ZWN0IGNvbHVtbi1yZXZlcnNlOiBzY3JvbGxUb3AgY2FuIGdvIG5lZ2F0aXZlXG5cdFx0Y29uc3Qgc3R5bGUgPSB0aGlzLnJvb3QuZ2V0Q29tcHV0ZWRTdHlsZVxuXHRcdFx0PyB0aGlzLnJvb3QuZ2V0Q29tcHV0ZWRTdHlsZSh1aU1lc3NhZ2VzV3JhcHBlclJvb3QpXG5cdFx0XHQ6IHVpTWVzc2FnZXNXcmFwcGVyUm9vdC5vd25lckRvY3VtZW50LmRlZmF1bHRWaWV3LmdldENvbXB1dGVkU3R5bGUodWlNZXNzYWdlc1dyYXBwZXJSb290KVxuXHRcdGNvbnN0IGlzUmV2ZXJzZWQgPSBzdHlsZS5mbGV4RGlyZWN0aW9uID09PSBcImNvbHVtbi1yZXZlcnNlXCJcblxuXHRcdC8vIEFsbG93IHVwIHRvIDMgZnVsbCBwYXNzZXM7IGNvdmVycyBjYXNlcyB3aGVyZSBET00gc2hyaW5rcyBhZnRlciB1bnNlbmRzXG5cdFx0Zm9yIChsZXQgcGFzcyA9IDA7IHBhc3MgPCAzOyBwYXNzKyspIHtcblx0XHRcdGlmIChpc1JldmVyc2VkKSB7XG5cdFx0XHRcdC8vIGNvbHVtbi1yZXZlcnNlOiBzY3JvbGxUb3AgcmFuZ2VzIGZyb20gMCAoYm90dG9tL25ld2VzdCkgdG8gbmVnYXRpdmUgKHRvcC9vbGRlc3QpXG5cdFx0XHRcdC8vIG1pblNjcm9sbCBpcyB0aGUgbW9zdCBuZWdhdGl2ZSB2YWx1ZSAoZnVydGhlc3QgYmFjayBpbiBoaXN0b3J5KVxuXHRcdFx0XHRjb25zdCBtaW5TY3JvbGwgPSAtKHVpTWVzc2FnZXNXcmFwcGVyUm9vdC5zY3JvbGxIZWlnaHQgLSB1aU1lc3NhZ2VzV3JhcHBlclJvb3QuY2xpZW50SGVpZ2h0KVxuXHRcdFx0XHRjb25zdCBzdGFydFBvcyA9IChwYXNzID09PSAwICYmIHRoaXMubGFzdFNjcm9sbFRvcCAhPT0gbnVsbClcblx0XHRcdFx0XHQ/IE1hdGgubWF4KHRoaXMubGFzdFNjcm9sbFRvcCwgbWluU2Nyb2xsKVxuXHRcdFx0XHRcdDogMCAvLyBTdGFydCBmcm9tIGJvdHRvbSAobmV3ZXN0KVxuXHRcdFx0XHRjb25zb2xlLmRlYnVnKGBnZXROZXh0VUlQSU1lc3NhZ2UgW3JldmVyc2VkXSBwYXNzPSR7cGFzc30sIHN0YXJ0UG9zPSR7c3RhcnRQb3N9LCBtaW5TY3JvbGw9JHttaW5TY3JvbGx9YClcblxuXHRcdFx0XHQvLyBTY3JvbGwgZnJvbSBzdGFydFBvcyB0b3dhcmQgbWluU2Nyb2xsIChtb3JlIG5lZ2F0aXZlID0gb2xkZXIgbWVzc2FnZXMpXG5cdFx0XHRcdGZvciAobGV0IGkgPSBzdGFydFBvczsgaSA+PSBtaW5TY3JvbGw7IGkgPSBpIC0gMTUwKSB7XG5cdFx0XHRcdFx0aWYgKGFib3J0Q29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkge1xuXHRcdFx0XHRcdFx0Y29uc29sZS5kZWJ1ZyhcImFib3J0Q29udHJvbGxlciBpbnRlcnVwdGVkIHRoZSBzY3JvbGxpbmc6IHN0b3BwaW5nLi4uXCIpXG5cdFx0XHRcdFx0XHRyZXR1cm4gZmFsc2Vcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0dGhpcy5sYXN0U2Nyb2xsVG9wID0gaVxuXHRcdFx0XHRcdHVpTWVzc2FnZXNXcmFwcGVyUm9vdC5zY3JvbGxUb3AgPSBpXG5cdFx0XHRcdFx0dWlNZXNzYWdlc1dyYXBwZXJSb290LmRpc3BhdGNoRXZlbnQobmV3IHRoaXMucm9vdC5FdmVudChcInNjcm9sbFwiKSlcblx0XHRcdFx0XHRhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgNSkpXG5cdFx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRcdGNvbnN0IG1lc3NhZ2VFbGVtZW50ID0gZ2V0Rmlyc3RWaXNpYmxlTWVzc2FnZSh1aU1lc3NhZ2VzV3JhcHBlclJvb3QsIGFib3J0Q29udHJvbGxlciwgdGhpcy5yb290KVxuXHRcdFx0XHRcdFx0aWYgKG1lc3NhZ2VFbGVtZW50KSB7XG5cdFx0XHRcdFx0XHRcdGNvbnN0IHVpTWVzc2FnZSA9IG5ldyBVSU1lc3NhZ2UobWVzc2FnZUVsZW1lbnQpXG5cdFx0XHRcdFx0XHRcdHJldHVybiBuZXcgVUlQSU1lc3NhZ2UodWlNZXNzYWdlKVxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0gY2F0Y2ggKGV4KSB7XG5cdFx0XHRcdFx0XHRjb25zb2xlLmVycm9yKGV4KVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Ly8gTm9ybWFsIGxheW91dDogc2Nyb2xsVG9wIHJhbmdlcyBmcm9tIDAgKHRvcCkgdG8gcG9zaXRpdmUgbWF4IChib3R0b20pXG5cdFx0XHRcdGNvbnN0IG1heFNjcm9sbCA9IHVpTWVzc2FnZXNXcmFwcGVyUm9vdC5zY3JvbGxIZWlnaHQgLSB1aU1lc3NhZ2VzV3JhcHBlclJvb3QuY2xpZW50SGVpZ2h0XG5cdFx0XHRcdGNvbnN0IHN0YXJ0U2Nyb2xsVG9wID0gKHBhc3MgPT09IDAgJiYgdGhpcy5sYXN0U2Nyb2xsVG9wICE9PSBudWxsKVxuXHRcdFx0XHRcdD8gTWF0aC5taW4odGhpcy5sYXN0U2Nyb2xsVG9wLCBtYXhTY3JvbGwpXG5cdFx0XHRcdFx0OiBtYXhTY3JvbGxcblx0XHRcdFx0Y29uc29sZS5kZWJ1ZyhgZ2V0TmV4dFVJUElNZXNzYWdlIHBhc3M9JHtwYXNzfSwgc3RhcnRTY3JvbGxUb3A9JHtzdGFydFNjcm9sbFRvcH0sIG1heFNjcm9sbD0ke21heFNjcm9sbH1gKVxuXG5cdFx0XHRcdGZvciAobGV0IGkgPSBNYXRoLm1heCgxLCBzdGFydFNjcm9sbFRvcCk7IGkgPiAwOyBpID0gaSAtIDE1MCkge1xuXHRcdFx0XHRcdGlmIChhYm9ydENvbnRyb2xsZXIuc2lnbmFsLmFib3J0ZWQpIHtcblx0XHRcdFx0XHRcdGNvbnNvbGUuZGVidWcoXCJhYm9ydENvbnRyb2xsZXIgaW50ZXJ1cHRlZCB0aGUgc2Nyb2xsaW5nOiBzdG9wcGluZy4uLlwiKVxuXHRcdFx0XHRcdFx0cmV0dXJuIGZhbHNlXG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdHRoaXMubGFzdFNjcm9sbFRvcCA9IGlcblx0XHRcdFx0XHR1aU1lc3NhZ2VzV3JhcHBlclJvb3Quc2Nyb2xsVG9wID0gaVxuXHRcdFx0XHRcdHVpTWVzc2FnZXNXcmFwcGVyUm9vdC5kaXNwYXRjaEV2ZW50KG5ldyB0aGlzLnJvb3QuRXZlbnQoXCJzY3JvbGxcIikpXG5cdFx0XHRcdFx0YXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDUpKVxuXHRcdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0XHRjb25zdCBtZXNzYWdlRWxlbWVudCA9IGdldEZpcnN0VmlzaWJsZU1lc3NhZ2UodWlNZXNzYWdlc1dyYXBwZXJSb290LCBhYm9ydENvbnRyb2xsZXIsIHRoaXMucm9vdClcblx0XHRcdFx0XHRcdGlmIChtZXNzYWdlRWxlbWVudCkge1xuXHRcdFx0XHRcdFx0XHRjb25zdCB1aU1lc3NhZ2UgPSBuZXcgVUlNZXNzYWdlKG1lc3NhZ2VFbGVtZW50KVxuXHRcdFx0XHRcdFx0XHRyZXR1cm4gbmV3IFVJUElNZXNzYWdlKHVpTWVzc2FnZSlcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9IGNhdGNoIChleCkge1xuXHRcdFx0XHRcdFx0Y29uc29sZS5lcnJvcihleClcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gUmVhY2hlZCB0aGUgZW5kIHdpdGhvdXQgZmluZGluZyBhIG1lc3NhZ2UuXG5cdFx0XHQvLyBSZXNldCBmb3IgYSBmcmVzaCBwYXNzIChET00gbWF5IGhhdmUgc2hydW5rIGFmdGVyIHVuc2VuZHMpLlxuXHRcdFx0dGhpcy5sYXN0U2Nyb2xsVG9wID0gbnVsbFxuXHRcdFx0Y29uc29sZS5kZWJ1ZyhgZ2V0TmV4dFVJUElNZXNzYWdlOiBwYXNzICR7cGFzc30gZm91bmQgbm90aGluZywgcmV0cnlpbmdgKVxuXHRcdH1cblxuXHRcdGNvbnNvbGUuZGVidWcoXCJnZXROZXh0VUlQSU1lc3NhZ2U6IGV4aGF1c3RlZCBhbGwgcGFzc2VzLCBubyBtZXNzYWdlcyBsZWZ0XCIpXG5cdFx0cmV0dXJuIGZhbHNlXG5cdH1cblxufVxuXG5leHBvcnQgZGVmYXVsdCBEZWZhdWx0VUlcbiIsIi8qKiBAbW9kdWxlIGdldC11aSBVSSBsb2FkZXIgbW9kdWxlLiBBbGxvdyBsb2FkaW5nIG9mIGEgY2VydGFpbiBVSSBiYXNlZCBvbiBhIGdpdmVuIHN0cmF0ZWd5IChsb2NhbGUgZXRjLi4pXG4gKiBUaGVyZSBtaWdodCBiZSBuZWVkIGZvciBtdWx0aXBsZSBVSSBhcyBJbnN0YWdyYW0gbWlnaHQgc2VydmUgZGlmZmVyZW50IGFwcHMgYmFzZWQgb24gbG9jYXRpb24gZm9yIGV4YW1wbGUuXG4gKiBUaGVyZSBpcyBhbHNvIGEgbmVlZCB0byBpbnRlcm5hdGlvbmFsaXplIGVhY2ggdWkgc28gdGhhdCBpdCBkb2Vzbid0IGZhaWwgaWYgd2UgY2hhbmdlIHRoZSBsYW5ndWFnZS5cbiAqL1xuXG5pbXBvcnQgRGVmYXVsdFVJIGZyb20gXCIuL2RlZmF1bHQvZGVmYXVsdC11aS5qc1wiXG4vKiBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnMgKi9cbmltcG9ydCBVSSBmcm9tIFwiLi91aS5qc1wiXG5cbi8qKlxuICpcbiAqIEByZXR1cm5zIHtVSX1cbiAqL1xuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gZ2V0VUkoKSB7XG5cdHJldHVybiBEZWZhdWx0VUlcbn1cbiIsIi8qKiBAbW9kdWxlIHVpcGkgQVBJIGZvciBVSSAqL1xuXG5pbXBvcnQgZ2V0VUkgZnJvbSBcIi4uL3VpL2dldC11aS5qc1wiXG5cbi8qIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFycyAqL1xuaW1wb3J0IFVJIGZyb20gXCIuLi91aS91aS5qc1wiXG4vKiBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW51c2VkLXZhcnMgKi9cbmltcG9ydCBVSVBJTWVzc2FnZSBmcm9tIFwiLi91aXBpLW1lc3NhZ2UuanNcIlxuXG4vKipcbiAqIFVJIEludGVyZmFjZSBBUElcbiAqL1xuY2xhc3MgVUlQSSB7XG5cblx0LyoqXG5cdCAqXG5cdCAqIEBwYXJhbSB7VUl9IHVpXG5cdCAqL1xuXHRjb25zdHJ1Y3Rvcih1aSkge1xuXHRcdHRoaXMuX3VpID0gdWlcblx0fVxuXG5cdC8qKlxuXHQgKlxuXHQgKiBAcGFyYW0ge1dpbmRvd30gd2luZG93XG5cdCAqIEByZXR1cm5zIHtVSVBJfVxuXHQgKi9cblx0c3RhdGljIGNyZWF0ZSh3aW5kb3cpIHtcblx0XHRjb25zb2xlLmRlYnVnKFwiVUlQSS5jcmVhdGVcIilcblx0XHRjb25zdCB1aSA9IGdldFVJKCkuY3JlYXRlKHdpbmRvdylcblx0XHRyZXR1cm4gbmV3IFVJUEkodWkpXG5cdH1cblxuXHQvKipcblx0ICogQHBhcmFtIHtBYm9ydENvbnRyb2xsZXJ9IGFib3J0Q29udHJvbGxlclxuXHQgKiBAcmV0dXJucyB7UHJvbWlzZX1cblx0ICovXG5cdGZldGNoQW5kUmVuZGVyVGhyZWFkTmV4dE1lc3NhZ2VQYWdlKGFib3J0Q29udHJvbGxlcikge1xuXHRcdGNvbnNvbGUuZGVidWcoXCJVSVBJIGZldGNoQW5kUmVuZGVyVGhyZWFkTmV4dE1lc3NhZ2VQYWdlXCIpXG5cdFx0cmV0dXJuIHRoaXMudWkuZmV0Y2hBbmRSZW5kZXJUaHJlYWROZXh0TWVzc2FnZVBhZ2UoYWJvcnRDb250cm9sbGVyKVxuXHR9XG5cblx0LyoqXG5cdCAqIEBwYXJhbSB7QWJvcnRDb250cm9sbGVyfSBhYm9ydENvbnRyb2xsZXJcblx0ICogQHJldHVybnMge1Byb21pc2U8VUlQSU1lc3NhZ2U+fVxuXHQgKi9cblx0Z2V0TmV4dFVJUElNZXNzYWdlKGFib3J0Q29udHJvbGxlcikge1xuXHRcdGNvbnNvbGUuZGVidWcoXCJVSVBJIGdldE5leHRVSVBJTWVzc2FnZVwiKVxuXHRcdHJldHVybiB0aGlzLnVpLmdldE5leHRVSVBJTWVzc2FnZShhYm9ydENvbnRyb2xsZXIpXG5cdH1cblxuXHQvKipcblx0ICpcblx0ICogQHR5cGUge1VJfVxuXHQgKi9cblx0Z2V0IHVpKCkge1xuXHRcdHJldHVybiB0aGlzLl91aVxuXHR9XG5cbn1cblxuZXhwb3J0IGRlZmF1bHQgVUlQSVxuIiwiLyoqIEBtb2R1bGUgaWRtdSBHbG9iYWwvTWFpbiBBUEkgZm9yIGludGVyYWN0aW5nIHdpdGggdGhlIFVJICovXG5cbmltcG9ydCBVSVBJIGZyb20gXCIuLi91aXBpL3VpcGkuanNcIlxuLyogZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzICovXG5pbXBvcnQgVUlQSU1lc3NhZ2UgZnJvbSBcIi4uL3VpcGkvdWlwaS1tZXNzYWdlLmpzXCJcblxuY2xhc3MgSURNVSB7XG5cblx0LyoqXG5cdCAqXG5cdCAqIEBwYXJhbSB7V2luZG93fSB3aW5kb3dcblx0ICogQHBhcmFtIHtjYWxsYmFja30gb25TdGF0dXNUZXh0XG5cdCAqL1xuXHRjb25zdHJ1Y3Rvcih3aW5kb3csIG9uU3RhdHVzVGV4dCkge1xuXHRcdHRoaXMud2luZG93ID0gd2luZG93XG5cdFx0dGhpcy51aXBpID0gbnVsbFxuXHRcdHRoaXMub25TdGF0dXNUZXh0ID0gb25TdGF0dXNUZXh0XG5cdH1cblxuXHQvKipcblx0ICogQHBhcmFtIHtBYm9ydENvbnRyb2xsZXJ9IGFib3J0Q29udHJvbGxlclxuXHQgKiBAcmV0dXJucyB7UHJvbWlzZTxVSVBJTWVzc2FnZT59XG5cdCAqL1xuXHRnZXROZXh0VUlQSU1lc3NhZ2UoYWJvcnRDb250cm9sbGVyKSB7XG5cdFx0cmV0dXJuIHRoaXMudWlwaS5nZXROZXh0VUlQSU1lc3NhZ2UoYWJvcnRDb250cm9sbGVyKVxuXHR9XG5cblx0LyoqXG5cdCAqXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0XG5cdCAqL1xuXHRzZXRTdGF0dXNUZXh0KHRleHQpIHtcblx0XHR0aGlzLm9uU3RhdHVzVGV4dCh0ZXh0KVxuXHR9XG5cblxuXHQvKipcblx0ICpcblx0ICogQHBhcmFtIHtBYm9ydENvbnRyb2xsZXJ9IGFib3J0Q29udHJvbGxlclxuXHQgKiBAcmV0dXJucyB7UHJvbWlzZX1cblx0ICovXG5cdGZldGNoQW5kUmVuZGVyVGhyZWFkTmV4dE1lc3NhZ2VQYWdlKGFib3J0Q29udHJvbGxlcikge1xuXHRcdHJldHVybiB0aGlzLnVpcGkuZmV0Y2hBbmRSZW5kZXJUaHJlYWROZXh0TWVzc2FnZVBhZ2UoYWJvcnRDb250cm9sbGVyKVxuXHR9XG5cblx0LyoqXG5cdCAqIE1hcCBJbnN0YWdyYW0gVUlcblx0ICovXG5cdGxvYWRVSVBJKCkge1xuXHRcdGNvbnNvbGUuZGVidWcoXCJsb2FkVUlQSVwiKVxuXHRcdHRoaXMudWlwaSA9IFVJUEkuY3JlYXRlKHRoaXMud2luZG93KVxuXHR9XG5cblxufVxuZXhwb3J0IGRlZmF1bHQgSURNVVxuIiwiLyoqIEBtb2R1bGUgdW5zZW5kLXN0cmF0ZWd5IFZhcmlvdXMgc3RyYXRlZ2llcyBmb3IgdW5zZW5kaW5nIG1lc3NhZ2VzICovXG5cbi8qIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFycyAqL1xuaW1wb3J0IElETVUgZnJvbSBcIi4uL2lkbXUvaWRtdS5qc1wiXG5cbi8qKlxuICpcbiAqIEBhYnN0cmFjdFxuICovXG5jbGFzcyBVbnNlbmRTdHJhdGVneSB7XG5cblx0LyoqXG5cdCAqXG5cdCAqIEBwYXJhbSB7SURNVX0gaWRtdVxuXHQgKi9cblx0Y29uc3RydWN0b3IoaWRtdSkge1xuXHRcdHRoaXMuX2lkbXUgPSBpZG11XG5cdH1cblxuXHQvKipcblx0ICpcblx0ICogQGFic3RyYWN0XG5cdCAqIEByZXR1cm5zIHtib29sZWFufVxuXHQgKi9cblx0aXNSdW5uaW5nKCkge1xuXHR9XG5cblx0LyoqXG5cdCAqXG5cdCAqIEBhYnN0cmFjdFxuXHQgKi9cblx0c3RvcCgpIHtcblx0fVxuXG5cdC8qKlxuXHQgKlxuXHQgKiBAYWJzdHJhY3Rcblx0ICovXG5cdHJlc2V0KCkge1xuXHR9XG5cblx0LyoqXG5cdCAqXG5cdCAqIEBhYnN0cmFjdFxuXHQgKi9cblx0YXN5bmMgcnVuKCkge1xuXHR9XG5cblx0LyoqXG5cdCAqIEByZWFkb25seVxuXHQgKiBAdHlwZSB7SURNVX1cblx0ICovXG5cdGdldCBpZG11KCkge1xuXHRcdHJldHVybiB0aGlzLl9pZG11XG5cdH1cblxufVxuXG5leHBvcnQgeyBVbnNlbmRTdHJhdGVneSB9XG4iLCIvKiogQG1vZHVsZSB1bnNlbmQtc3RyYXRlZ3kgVmFyaW91cyBzdHJhdGVnaWVzIGZvciB1bnNlbmRpbmcgbWVzc2FnZXMgKi9cblxuLyogZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzICovXG5pbXBvcnQgSURNVSBmcm9tIFwiLi4vLi4vaWRtdS9pZG11LmpzXCJcbmltcG9ydCB7IFVuc2VuZFN0cmF0ZWd5IH0gZnJvbSBcIi4uL3Vuc2VuZC1zdHJhdGVneS5qc1wiXG5cbi8qKlxuICogTG9hZHMgYWxsIHBhZ2VzIGZpcnN0LCB0aGVuIHVuc2VuZHMgbWVzc2FnZXMgZnJvbSBib3R0b20gdG8gdG9wLlxuICogRm9yIHNob3J0IGNvbnZlcnNhdGlvbnMgKGFsbCBtZXNzYWdlcyBmaXQgaW4gdmlld3BvcnQpLCBza2lwcyBwYWdlIGxvYWRpbmcgZW50aXJlbHkuXG4gKi9cbmNsYXNzIERlZmF1bHRTdHJhdGVneSBleHRlbmRzIFVuc2VuZFN0cmF0ZWd5IHtcblxuXHQvKipcblx0ICogQHBhcmFtIHtJRE1VfSBpZG11XG5cdCAqL1xuXHRjb25zdHJ1Y3RvcihpZG11KSB7XG5cdFx0c3VwZXIoaWRtdSlcblx0XHR0aGlzLl9hbGxQYWdlc0xvYWRlZCA9IGZhbHNlXG5cdFx0dGhpcy5fdW5zZW50Q291bnQgPSAwXG5cdFx0dGhpcy5fcGFnZXNMb2FkZWRDb3VudCA9IDBcblx0XHR0aGlzLl9ydW5uaW5nID0gZmFsc2Vcblx0XHR0aGlzLl9hYm9ydENvbnRyb2xsZXIgPSBudWxsXG5cdFx0dGhpcy5fbGFzdFVuc2VuZERhdGUgPSBudWxsXG5cdFx0dGhpcy5fY29uc2VjdXRpdmVGYWlsdXJlcyA9IDBcblx0fVxuXG5cdC8qKlxuXHQgKiBAcmV0dXJucyB7Ym9vbGVhbn1cblx0ICovXG5cdGlzUnVubmluZygpIHtcblx0XHRyZXR1cm4gdGhpcy5fcnVubmluZyAmJiB0aGlzLl9hYm9ydENvbnRyb2xsZXIgJiYgdGhpcy5fYWJvcnRDb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkID09PSBmYWxzZVxuXHR9XG5cblx0c3RvcCgpIHtcblx0XHRjb25zb2xlLmRlYnVnKFwiRGVmYXVsdFN0cmF0ZWd5IHN0b3BcIilcblx0XHR0aGlzLmlkbXUuc2V0U3RhdHVzVGV4dChcIlN0b3BwaW5nLi4uXCIpXG5cdFx0dGhpcy5fYWJvcnRDb250cm9sbGVyLmFib3J0KClcblx0fVxuXG5cdHJlc2V0KCkge1xuXHRcdHRoaXMuX2FsbFBhZ2VzTG9hZGVkID0gZmFsc2Vcblx0XHR0aGlzLl91bnNlbnRDb3VudCA9IDBcblx0XHR0aGlzLl9sYXN0VW5zZW5kRGF0ZSA9IG51bGxcblx0XHR0aGlzLl9wYWdlc0xvYWRlZENvdW50ID0gMFxuXHRcdHRoaXMuX2NvbnNlY3V0aXZlRmFpbHVyZXMgPSAwXG5cdFx0dGhpcy5pZG11LnNldFN0YXR1c1RleHQoXCJSZWFkeVwiKVxuXHR9XG5cblx0LyoqXG5cdCAqIEByZXR1cm5zIHtQcm9taXNlfVxuXHQgKi9cblx0YXN5bmMgcnVuKCkge1xuXHRcdGNvbnNvbGUuZGVidWcoXCJEZWZhdWx0U3RyYXRlZ3kucnVuKClcIilcblx0XHR0aGlzLl91bnNlbnRDb3VudCA9IDBcblx0XHR0aGlzLl9wYWdlc0xvYWRlZENvdW50ID0gMFxuXHRcdHRoaXMuX2NvbnNlY3V0aXZlRmFpbHVyZXMgPSAwXG5cdFx0dGhpcy5fcnVubmluZyA9IHRydWVcblx0XHR0aGlzLl9hYm9ydENvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKClcblx0XHQvLyBDbGVhciBzdGFsZSBpZ25vcmUgbWFya2VycyBmcm9tIHByZXZpb3VzIHJ1bnMgc28gbWVzc2FnZXMgY2FuIGJlIHJldHJpZWRcblx0XHR0aGlzLmlkbXUud2luZG93LmRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoXCJbZGF0YS1pZG11LWlnbm9yZV1cIikuZm9yRWFjaChlbCA9PiB7XG5cdFx0XHRlbC5yZW1vdmVBdHRyaWJ1dGUoXCJkYXRhLWlkbXUtaWdub3JlXCIpXG5cdFx0fSlcblx0XHR0aGlzLmlkbXUubG9hZFVJUEkoKVxuXHRcdHRyeSB7XG5cdFx0XHRpZiAodGhpcy5fYWxsUGFnZXNMb2FkZWQpIHtcblx0XHRcdFx0YXdhaXQgdGhpcy4jdW5zZW5kTmV4dE1lc3NhZ2UoKVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0YXdhaXQgdGhpcy4jbG9hZE5leHRQYWdlKClcblx0XHRcdH1cblxuXHRcdFx0Ly8gUmFjZSBjb25kaXRpb246IG9uIGZpcnN0IHBhZ2UgbG9hZCwgSW5zdGFncmFtJ3MgUmVhY3QgbWF5IG5vdCBoYXZlXG5cdFx0XHQvLyBmaW5pc2hlZCBoeWRyYXRpbmcgbWVzc2FnZSBjb21wb25lbnRzIChyb2xlIGF0dHJpYnV0ZXMgbWlzc2luZykuXG5cdFx0XHQvLyBJZiB3ZSBmb3VuZCBub3RoaW5nLCB3YWl0IGFuZCByZS1zY2FuIHVwIHRvIDMgdGltZXMuXG5cdFx0XHRpZiAodGhpcy5fdW5zZW50Q291bnQgPT09IDAgJiYgIXRoaXMuX2Fib3J0Q29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkge1xuXHRcdFx0XHRmb3IgKGxldCByZXRyeSA9IDE7IHJldHJ5IDw9IDM7IHJldHJ5KyspIHtcblx0XHRcdFx0XHR0aGlzLmlkbXUuc2V0U3RhdHVzVGV4dChgTm8gbWVzc2FnZXMgZGV0ZWN0ZWQsIHJldHJ5aW5nICgke3JldHJ5fS8zKS4uLmApXG5cdFx0XHRcdFx0Y29uc29sZS5kZWJ1ZyhgRGVmYXVsdFN0cmF0ZWd5OiAwIG1lc3NhZ2VzIGZvdW5kLCByZXRyeSAke3JldHJ5fS8zYClcblx0XHRcdFx0XHRhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMjAwMCkpXG5cdFx0XHRcdFx0aWYgKHRoaXMuX2Fib3J0Q29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkgYnJlYWtcblx0XHRcdFx0XHQvLyBSZXNldCBmb3IgZnJlc2ggc2NhblxuXHRcdFx0XHRcdHRoaXMuX2FsbFBhZ2VzTG9hZGVkID0gZmFsc2Vcblx0XHRcdFx0XHR0aGlzLl9jb25zZWN1dGl2ZUZhaWx1cmVzID0gMFxuXHRcdFx0XHRcdHRoaXMuaWRtdS53aW5kb3cuZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbChcIltkYXRhLWlkbXUtaWdub3JlXVwiKS5mb3JFYWNoKGVsID0+IHtcblx0XHRcdFx0XHRcdGVsLnJlbW92ZUF0dHJpYnV0ZShcImRhdGEtaWRtdS1pZ25vcmVcIilcblx0XHRcdFx0XHR9KVxuXHRcdFx0XHRcdHRoaXMuaWRtdS5sb2FkVUlQSSgpXG5cdFx0XHRcdFx0YXdhaXQgdGhpcy4jbG9hZE5leHRQYWdlKClcblx0XHRcdFx0XHRpZiAodGhpcy5fdW5zZW50Q291bnQgPiAwIHx8IHRoaXMuX2Fib3J0Q29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkgYnJlYWtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHRpZiAodGhpcy5fYWJvcnRDb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSB7XG5cdFx0XHRcdHRoaXMuaWRtdS5zZXRTdGF0dXNUZXh0KGBBYm9ydGVkLiAke3RoaXMuX3Vuc2VudENvdW50fSBtZXNzYWdlKHMpIHVuc2VudC5gKVxuXHRcdFx0XHRjb25zb2xlLmRlYnVnKFwiRGVmYXVsdFN0cmF0ZWd5IGFib3J0ZWRcIilcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHRoaXMuaWRtdS5zZXRTdGF0dXNUZXh0KGBEb25lLiAke3RoaXMuX3Vuc2VudENvdW50fSBtZXNzYWdlKHMpIHVuc2VudC5gKVxuXHRcdFx0XHRjb25zb2xlLmRlYnVnKFwiRGVmYXVsdFN0cmF0ZWd5IGRvbmVcIilcblx0XHRcdH1cblx0XHR9IGNhdGNoIChleCkge1xuXHRcdFx0Y29uc29sZS5lcnJvcihleClcblx0XHRcdHRoaXMuaWRtdS5zZXRTdGF0dXNUZXh0KGBFcnJvcmVkLiAke3RoaXMuX3Vuc2VudENvdW50fSBtZXNzYWdlKHMpIHVuc2VudC5gKVxuXHRcdFx0Y29uc29sZS5kZWJ1ZyhcIkRlZmF1bHRTdHJhdGVneSBlcnJvcmVkXCIpXG5cdFx0fVxuXHRcdHRoaXMuX3J1bm5pbmcgPSBmYWxzZVxuXHR9XG5cblx0LyoqXG5cdCAqIFRyaWVzIHRvIGxvYWQgdGhlIHRocmVhZCBuZXh0IHBhZ2UuXG5cdCAqIElmIGxvYWRNb3JlTWVzc2FnZXMgcmV0dXJucyB0cnVlIChubyBtb3JlIHBhZ2VzKSwgbW92ZXMgdG8gdW5zZW5kaW5nLlxuXHQgKi9cblx0YXN5bmMgI2xvYWROZXh0UGFnZSgpIHtcblx0XHRpZiAodGhpcy5fYWJvcnRDb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSB7XG5cdFx0XHRjb25zb2xlLmRlYnVnKFwiYWJvcnRDb250cm9sbGVyIGludGVydXB0ZWQgdGhlIGxvYWRpbmcgb2YgbmV4dCBwYWdlOiBzdG9wcGluZy4uLlwiKVxuXHRcdFx0cmV0dXJuXG5cdFx0fVxuXHRcdHRoaXMuaWRtdS5zZXRTdGF0dXNUZXh0KFwiTG9hZGluZyBuZXh0IHBhZ2UuLi5cIilcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgZG9uZSA9IGF3YWl0IHRoaXMuaWRtdS5mZXRjaEFuZFJlbmRlclRocmVhZE5leHRNZXNzYWdlUGFnZSh0aGlzLl9hYm9ydENvbnRyb2xsZXIpXG5cdFx0XHRpZiAodGhpcy5fYWJvcnRDb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkID09PSBmYWxzZSkge1xuXHRcdFx0XHRpZiAoZG9uZSkge1xuXHRcdFx0XHRcdHRoaXMuaWRtdS5zZXRTdGF0dXNUZXh0KGBBbGwgcGFnZXMgbG9hZGVkICgke3RoaXMuX3BhZ2VzTG9hZGVkQ291bnR9IGluIHRvdGFsKS4gVW5zZW5kaW5nLi4uYClcblx0XHRcdFx0XHR0aGlzLl9hbGxQYWdlc0xvYWRlZCA9IHRydWVcblx0XHRcdFx0XHRhd2FpdCB0aGlzLiN1bnNlbmROZXh0TWVzc2FnZSgpXG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0dGhpcy5fcGFnZXNMb2FkZWRDb3VudCsrXG5cdFx0XHRcdFx0YXdhaXQgdGhpcy4jbG9hZE5leHRQYWdlKClcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Y29uc29sZS5kZWJ1ZyhcImFib3J0Q29udHJvbGxlciBpbnRlcnVwdGVkIHRoZSBsb2FkaW5nIG9mIG5leHQgcGFnZTogc3RvcHBpbmcuLi5cIilcblx0XHRcdH1cblx0XHR9IGNhdGNoIChleCkge1xuXHRcdFx0Y29uc29sZS5lcnJvcihleClcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogVW5zZW5kIGZpcnN0IG1lc3NhZ2UgaW4gdmlld3BvcnQuXG5cdCAqIFVzZXMgaHVtYW4tbGlrZSByYW5kb21pemVkIGRlbGF5cyBhbmQgZXhwb25lbnRpYWwgYmFja29mZiB0byBhdm9pZCBJbnN0YWdyYW0gcmF0ZSBsaW1pdHMuXG5cdCAqL1xuXHRhc3luYyAjdW5zZW5kTmV4dE1lc3NhZ2UoKSB7XG5cdFx0aWYgKHRoaXMuX2Fib3J0Q29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkge1xuXHRcdFx0Y29uc29sZS5kZWJ1ZyhcImFib3J0Q29udHJvbGxlciBpbnRlcnVwdGVkIHRoZSB1bnNlbmRpbmcgb2YgbmV4dCBtZXNzYWdlOiBzdG9wcGluZy4uLlwiKVxuXHRcdFx0cmV0dXJuXG5cdFx0fVxuXHRcdGlmICh0aGlzLl9jb25zZWN1dGl2ZUZhaWx1cmVzID49IDUpIHtcblx0XHRcdHRoaXMuaWRtdS5zZXRTdGF0dXNUZXh0KGBTdG9wcGVkOiAke3RoaXMuX2NvbnNlY3V0aXZlRmFpbHVyZXN9IGNvbnNlY3V0aXZlIGZhaWx1cmVzLiAke3RoaXMuX3Vuc2VudENvdW50fSBtZXNzYWdlKHMpIHVuc2VudC5gKVxuXHRcdFx0Y29uc29sZS5kZWJ1ZyhcIkRlZmF1bHRTdHJhdGVneSBzdG9wcGluZyBkdWUgdG8gY29uc2VjdXRpdmUgZmFpbHVyZXNcIilcblx0XHRcdHJldHVyblxuXHRcdH1cblx0XHRsZXQgY2FuU2Nyb2xsID0gdHJ1ZVxuXHRcdGxldCBtc2dFbGVtZW50ID0gbnVsbFxuXHRcdHRyeSB7XG5cdFx0XHR0aGlzLmlkbXUuc2V0U3RhdHVzVGV4dChgUmV0cmlldmluZyBuZXh0IG1lc3NhZ2UuLi4gKCR7dGhpcy5fdW5zZW50Q291bnR9IHVuc2VudCBzbyBmYXIpYClcblx0XHRcdGNvbnN0IHVpcGlNZXNzYWdlID0gYXdhaXQgdGhpcy5pZG11LmdldE5leHRVSVBJTWVzc2FnZSh0aGlzLl9hYm9ydENvbnRyb2xsZXIpXG5cdFx0XHRjYW5TY3JvbGwgPSB1aXBpTWVzc2FnZSAhPT0gZmFsc2Vcblx0XHRcdGlmICh1aXBpTWVzc2FnZSkge1xuXHRcdFx0XHR0aGlzLmlkbXUuc2V0U3RhdHVzVGV4dChgVW5zZW5kaW5nIG1lc3NhZ2UuLi4gKCR7dGhpcy5fdW5zZW50Q291bnQgKyAxfSlgKVxuXG5cdFx0XHRcdC8vIEh1bWFuLWxpa2UgZGVsYXkgYmV0d2VlbiB1bnNlbmRzOiAzLTZzIHJhbmRvbWl6ZWRcblx0XHRcdFx0aWYgKHRoaXMuX2xhc3RVbnNlbmREYXRlICE9PSBudWxsKSB7XG5cdFx0XHRcdFx0Y29uc3QgZWxhcHNlZCA9IERhdGUubm93KCkgLSB0aGlzLl9sYXN0VW5zZW5kRGF0ZS5nZXRUaW1lKClcblx0XHRcdFx0XHRjb25zdCBtaW5EZWxheSA9IDQwMDAgKyBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAyMDAwKSAvLyA0LTZzICh+NXMgYXZnKVxuXHRcdFx0XHRcdGlmIChlbGFwc2VkIDwgbWluRGVsYXkpIHtcblx0XHRcdFx0XHRcdGNvbnN0IHdhaXRNcyA9IG1pbkRlbGF5IC0gZWxhcHNlZFxuXHRcdFx0XHRcdFx0dGhpcy5pZG11LnNldFN0YXR1c1RleHQoYFdhaXRpbmcgJHsod2FpdE1zIC8gMTAwMCkudG9GaXhlZCgxKX1zLi4uICgke3RoaXMuX3Vuc2VudENvdW50fSB1bnNlbnQgc28gZmFyKWApXG5cdFx0XHRcdFx0XHRhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgd2FpdE1zKSlcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiAodGhpcy5fYWJvcnRDb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSByZXR1cm5cblxuXHRcdFx0XHRtc2dFbGVtZW50ID0gdWlwaU1lc3NhZ2UudWlNZXNzYWdlLnJvb3Rcblx0XHRcdFx0Y29uc3QgdW5zZW50ID0gYXdhaXQgdWlwaU1lc3NhZ2UudW5zZW5kKHRoaXMuX2Fib3J0Q29udHJvbGxlcilcblxuXHRcdFx0XHRpZiAodW5zZW50KSB7XG5cdFx0XHRcdFx0Ly8gVmVyaWZ5IHRoZSBtZXNzYWdlIGFjdHVhbGx5IGRpc2FwcGVhcmVkIGZyb20gRE9NIChzZXJ2ZXIgYWNjZXB0ZWQgdGhlIG11dGF0aW9uKVxuXHRcdFx0XHRcdGF3YWl0IG5ldyBQcm9taXNlKHJlc29sdmUgPT4gc2V0VGltZW91dChyZXNvbHZlLCA4MDApKVxuXHRcdFx0XHRcdGNvbnN0IHN0aWxsSW5ET00gPSBtc2dFbGVtZW50LmlzQ29ubmVjdGVkICYmICFtc2dFbGVtZW50Lmhhc0F0dHJpYnV0ZShcImRhdGEtaWRtdS11bnNlbnRcIilcblx0XHRcdFx0XHRpZiAoc3RpbGxJbkRPTSkge1xuXHRcdFx0XHRcdFx0Ly8gU2VydmVyIGxpa2VseSByZWplY3RlZCDigJQgdGhlIG1lc3NhZ2UgcmVhcHBlYXJlZCBhZnRlciBvcHRpbWlzdGljIHJlbW92YWxcblx0XHRcdFx0XHRcdGNvbnNvbGUuZGVidWcoXCJEZWZhdWx0U3RyYXRlZ3k6IG1lc3NhZ2Ugc3RpbGwgaW4gRE9NIGFmdGVyIHVuc2VuZCwgcG9zc2libGUgcmF0ZSBsaW1pdFwiKVxuXHRcdFx0XHRcdFx0bXNnRWxlbWVudC5yZW1vdmVBdHRyaWJ1dGUoXCJkYXRhLWlkbXUtaWdub3JlXCIpXG5cdFx0XHRcdFx0XHR0aGlzLl9jb25zZWN1dGl2ZUZhaWx1cmVzKytcblx0XHRcdFx0XHRcdGNvbnN0IGJhY2tvZmZNcyA9IE1hdGgubWluKDYwMDAwLCA1MDAwICogTWF0aC5wb3coMiwgdGhpcy5fY29uc2VjdXRpdmVGYWlsdXJlcyAtIDEpKVxuXHRcdFx0XHRcdFx0dGhpcy5pZG11LnNldFN0YXR1c1RleHQoYFNlcnZlciBtYXkgaGF2ZSByZWplY3RlZCB1bnNlbmQuIEJhY2tpbmcgb2ZmICR7KGJhY2tvZmZNcyAvIDEwMDApLnRvRml4ZWQoMCl9cy4uLiAoJHt0aGlzLl91bnNlbnRDb3VudH0gdW5zZW50KWApXG5cdFx0XHRcdFx0XHRhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgYmFja29mZk1zKSlcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0dGhpcy5fbGFzdFVuc2VuZERhdGUgPSBuZXcgRGF0ZSgpXG5cdFx0XHRcdFx0XHR0aGlzLl91bnNlbnRDb3VudCsrXG5cdFx0XHRcdFx0XHR0aGlzLl9jb25zZWN1dGl2ZUZhaWx1cmVzID0gMFxuXHRcdFx0XHRcdFx0Ly8gRE9NIHNocnVuayBhZnRlciByZW1vdmFsOyByZXNldCBzY3JvbGwgZm9yIGZyZXNoIHNjYW5cblx0XHRcdFx0XHRcdGlmICh0aGlzLmlkbXUudWlwaSAmJiB0aGlzLmlkbXUudWlwaS51aSkge1xuXHRcdFx0XHRcdFx0XHR0aGlzLmlkbXUudWlwaS51aS5sYXN0U2Nyb2xsVG9wID0gbnVsbFxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHQvLyBVbnNlbmQgd29ya2Zsb3cgcmV0dXJuZWQgZmFsc2Ug4oCUIGFsbG93IHJldHJ5IG9uIG5leHQgcGFzc1xuXHRcdFx0XHRcdGNvbnNvbGUuZGVidWcoXCJEZWZhdWx0U3RyYXRlZ3k6IHVuc2VuZCByZXR1cm5lZCBmYWxzZSwgcmVtb3ZpbmcgaWdub3JlIG1hcmtlciBmb3IgcmV0cnlcIilcblx0XHRcdFx0XHRtc2dFbGVtZW50LnJlbW92ZUF0dHJpYnV0ZShcImRhdGEtaWRtdS1pZ25vcmVcIilcblx0XHRcdFx0XHR0aGlzLl9jb25zZWN1dGl2ZUZhaWx1cmVzKytcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0gY2F0Y2ggKGV4KSB7XG5cdFx0XHRjb25zb2xlLmVycm9yKGV4KVxuXHRcdFx0Ly8gUmVtb3ZlIGlnbm9yZSBtYXJrZXIgc28gdGhpcyBtZXNzYWdlIGNhbiBiZSByZXRyaWVkXG5cdFx0XHRpZiAobXNnRWxlbWVudCkge1xuXHRcdFx0XHRtc2dFbGVtZW50LnJlbW92ZUF0dHJpYnV0ZShcImRhdGEtaWRtdS1pZ25vcmVcIilcblx0XHRcdH1cblx0XHRcdHRoaXMuX2NvbnNlY3V0aXZlRmFpbHVyZXMrK1xuXHRcdFx0Y29uc3QgYmFja29mZk1zID0gTWF0aC5taW4oNjAwMDAsIDMwMDAgKiBNYXRoLnBvdygyLCB0aGlzLl9jb25zZWN1dGl2ZUZhaWx1cmVzIC0gMSkpXG5cdFx0XHR0aGlzLmlkbXUuc2V0U3RhdHVzVGV4dChgV29ya2Zsb3cgZmFpbGVkICgke3RoaXMuX2NvbnNlY3V0aXZlRmFpbHVyZXN9LzUpLCByZXRyeWluZyBpbiAkeyhiYWNrb2ZmTXMgLyAxMDAwKS50b0ZpeGVkKDApfXMuLi4gKCR7dGhpcy5fdW5zZW50Q291bnR9IHVuc2VudClgKVxuXHRcdFx0YXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIGJhY2tvZmZNcykpXG5cdFx0fSBmaW5hbGx5IHtcblx0XHRcdGlmIChjYW5TY3JvbGwgJiYgdGhpcy5fYWJvcnRDb250cm9sbGVyICYmICF0aGlzLl9hYm9ydENvbnRyb2xsZXIuc2lnbmFsLmFib3J0ZWQpIHtcblx0XHRcdFx0YXdhaXQgdGhpcy4jdW5zZW5kTmV4dE1lc3NhZ2UoKVxuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG59XG5cbmV4cG9ydCB7IERlZmF1bHRTdHJhdGVneSB9XG4iLCIvKiogQG1vZHVsZSBhbGVydCBBbGVydCBVSSAqL1xuXG4vKipcbiAqXG4gKiBAcGFyYW0ge0RvY3VtZW50fSBkb2N1bWVudFxuICogQHJldHVybnMge0hUTUxCdXR0b25FbGVtZW50fVxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlQWxlcnRzV3JhcHBlckVsZW1lbnQoZG9jdW1lbnQpIHtcblx0Y29uc3QgYWxlcnRzV3JhcHBlckVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpXG5cdGFsZXJ0c1dyYXBwZXJFbGVtZW50LmlkID0gXCJpZG11LWFsZXJ0c1wiXG5cdGFsZXJ0c1dyYXBwZXJFbGVtZW50LnN0eWxlLnBvc2l0aW9uID0gXCJmaXhlZFwiXG5cdGFsZXJ0c1dyYXBwZXJFbGVtZW50LnN0eWxlLnRvcCA9IFwiMjBweFwiXG5cdGFsZXJ0c1dyYXBwZXJFbGVtZW50LnN0eWxlLnJpZ2h0ID0gXCIyMHB4XCJcblx0YWxlcnRzV3JhcHBlckVsZW1lbnQuc3R5bGUuZGlzcGxheSA9IFwiZ3JpZFwiXG5cdHJldHVybiBhbGVydHNXcmFwcGVyRWxlbWVudFxufVxuXG4vKipcbiAqXG4gKiBAcGFyYW0ge0RvY3VtZW50fSBkb2N1bWVudFxuICogQHBhcmFtIHtzdHJpbmd9ICAgdGV4dFxuICogQHJldHVybnMge0hUTUxCdXR0b25FbGVtZW50fVxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlQWxlcnRFbGVtZW50KGRvY3VtZW50LCB0ZXh0KSB7XG5cdGNvbnN0IGFsZXJ0RWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIilcblx0YWxlcnRFbGVtZW50LnRleHRDb250ZW50ID0gdGV4dFxuXHRyZXR1cm4gYWxlcnRFbGVtZW50XG59XG4iLCIvKiogQG1vZHVsZSBvdmVybGF5IElETVUncyBvdmVybGF5ICovXG5cbi8qKlxuICogQHBhcmFtIHtEb2N1bWVudH0gZG9jdW1lbnRcbiAqIEByZXR1cm5zIHtIVE1MRGl2RWxlbWVudH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU92ZXJsYXlFbGVtZW50KGRvY3VtZW50KSB7XG5cdGNvbnN0IG92ZXJsYXlFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKVxuXHRvdmVybGF5RWxlbWVudC5pZCA9IFwiaWRtdS1vdmVybGF5XCJcblx0b3ZlcmxheUVsZW1lbnQudGFiSW5kZXggPSAwXG5cdG92ZXJsYXlFbGVtZW50LnN0eWxlLnRvcCA9IFwiMFwiXG5cdG92ZXJsYXlFbGVtZW50LnN0eWxlLnJpZ2h0ID0gXCIwXCJcblx0b3ZlcmxheUVsZW1lbnQuc3R5bGUucG9zaXRpb24gPSBcImZpeGVkXCJcblx0b3ZlcmxheUVsZW1lbnQuc3R5bGUud2lkdGggPSBcIjEwMHZ3XCJcblx0b3ZlcmxheUVsZW1lbnQuc3R5bGUuaGVpZ2h0ID0gXCIxMDB2aFwiXG5cdG92ZXJsYXlFbGVtZW50LnN0eWxlLnpJbmRleCA9IFwiOTk4XCJcblx0b3ZlcmxheUVsZW1lbnQuc3R5bGUuYmFja2dyb3VuZENvbG9yID0gXCIjMDAwMDAwZDZcIlxuXHRvdmVybGF5RWxlbWVudC5zdHlsZS5kaXNwbGF5ID0gXCJub25lXCJcblx0cmV0dXJuIG92ZXJsYXlFbGVtZW50XG59XG4iLCIvKiogQG1vZHVsZSB1aSBJRE1VJ3Mgb3duIHVpL292ZXJsYXlcbiAqIFByb3ZpZGUgYSBidXR0b24gdG8gdW5zZW5kIG1lc3NhZ2VzXG4gKi9cblxuaW1wb3J0IHsgY3JlYXRlTWVudUJ1dHRvbkVsZW1lbnQgfSBmcm9tIFwiLi9tZW51LWJ1dHRvbi5qc1wiXG5pbXBvcnQgeyBjcmVhdGVNZW51RWxlbWVudCB9IGZyb20gXCIuL21lbnUuanNcIlxuaW1wb3J0IElETVUgZnJvbSBcIi4uLy4uLy4uL2lkbXUvaWRtdS5qc1wiXG5pbXBvcnQgeyBEZWZhdWx0U3RyYXRlZ3kgfSBmcm9tIFwiLi4vLi4vLi4vdWkvZGVmYXVsdC91bnNlbmQtc3RyYXRlZ3kuanNcIlxuaW1wb3J0IHsgY3JlYXRlQWxlcnRzV3JhcHBlckVsZW1lbnQgfSBmcm9tIFwiLi9hbGVydC5qc1wiXG5pbXBvcnQgeyBjcmVhdGVPdmVybGF5RWxlbWVudCB9IGZyb20gXCIuL292ZXJsYXkuanNcIlxuaW1wb3J0IHsgQlVUVE9OX1NUWUxFIH0gZnJvbSBcIi4vc3R5bGUvaW5zdGFncmFtLmpzXCJcbi8qIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFycyAqL1xuaW1wb3J0IHsgVW5zZW5kU3RyYXRlZ3kgfSBmcm9tIFwiLi4vLi4vLi4vdWkvdW5zZW5kLXN0cmF0ZWd5LmpzXCJcblxuY2xhc3MgT1NEIHtcblx0LyoqXG5cdCAqXG5cdCAqIEBwYXJhbSB7RG9jdW1lbnR9IGRvY3VtZW50XG5cdCAqIEBwYXJhbSB7SFRNTERpdkVsZW1lbnR9IHJvb3Rcblx0ICogQHBhcmFtIHtIVE1MRGl2RWxlbWVudH0gb3ZlcmxheUVsZW1lbnRcblx0ICogQHBhcmFtIHtIVE1MRGl2RWxlbWVudH0gbWVudUVsZW1lbnRcblx0ICogQHBhcmFtIHtIVE1MQnV0dG9uRWxlbWVudH0gdW5zZW5kVGhyZWFkTWVzc2FnZXNCdXR0b25cblx0ICogQHBhcmFtIHtIVE1MRGl2RWxlbWVudH0gc3RhdHVzRWxlbWVudFxuXHQgKi9cblx0Y29uc3RydWN0b3IoZG9jdW1lbnQsIHJvb3QsIG92ZXJsYXlFbGVtZW50LCBtZW51RWxlbWVudCwgdW5zZW5kVGhyZWFkTWVzc2FnZXNCdXR0b24sIHN0YXR1c0VsZW1lbnQpIHtcblx0XHR0aGlzLl9kb2N1bWVudCA9IGRvY3VtZW50XG5cdFx0dGhpcy5fcm9vdCA9IHJvb3Rcblx0XHR0aGlzLl9vdmVybGF5RWxlbWVudCA9IG92ZXJsYXlFbGVtZW50XG5cdFx0dGhpcy5fbWVudUVsZW1lbnQgPSBtZW51RWxlbWVudFxuXHRcdHRoaXMuX3N0YXR1c0VsZW1lbnQgPSBzdGF0dXNFbGVtZW50XG5cdFx0dGhpcy5fdW5zZW5kVGhyZWFkTWVzc2FnZXNCdXR0b24gPSB1bnNlbmRUaHJlYWRNZXNzYWdlc0J1dHRvblxuXHRcdHRoaXMuX2lkbXUgPSBuZXcgSURNVSh0aGlzLndpbmRvdywgdGhpcy5vblN0YXR1c1RleHQuYmluZCh0aGlzKSlcblx0XHR0aGlzLl9zdHJhdGVneSA9IG5ldyBEZWZhdWx0U3RyYXRlZ3kodGhpcy5faWRtdSkgLy8gVE9ETyBtb3ZlIG91dFxuXHR9XG5cblx0LyoqXG5cdCAqXG5cdCAqIEBwYXJhbSB7d2luZG93fSB3aW5kb3dcblx0ICogQHJldHVybnMge09TRH1cblx0ICovXG5cdHN0YXRpYyByZW5kZXIod2luZG93KSB7XG5cdFx0Y29uc29sZS5kZWJ1ZyhcInJlbmRlclwiKVxuXHRcdGNvbnN0IHVpID0gT1NELmNyZWF0ZSh3aW5kb3cuZG9jdW1lbnQpXG5cdFx0d2luZG93LmRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQodWkucm9vdClcblx0XHRyZXR1cm4gdWlcblx0fVxuXG5cdC8qKlxuXHQgKlxuXHQgKiBAcGFyYW0gICB7RG9jdW1lbnR9IGRvY3VtZW50XG5cdCAqIEByZXR1cm5zIHtPU0R9XG5cdCAqL1xuXHRzdGF0aWMgY3JlYXRlKGRvY3VtZW50KSB7XG5cdFx0Y29uc3Qgcm9vdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIilcblx0XHRyb290LmlkID0gXCJpZG11LXJvb3RcIlxuXHRcdGNvbnN0IG1lbnVFbGVtZW50ID0gY3JlYXRlTWVudUVsZW1lbnQoZG9jdW1lbnQpXG5cdFx0Y29uc3Qgb3ZlcmxheUVsZW1lbnQgPSBjcmVhdGVPdmVybGF5RWxlbWVudChkb2N1bWVudClcblx0XHRjb25zdCBhbGVydHNXcmFwcGVyRWxlbWVudCA9IGNyZWF0ZUFsZXJ0c1dyYXBwZXJFbGVtZW50KGRvY3VtZW50KVxuXHRcdGNvbnN0IHVuc2VuZFRocmVhZE1lc3NhZ2VzQnV0dG9uID0gY3JlYXRlTWVudUJ1dHRvbkVsZW1lbnQoZG9jdW1lbnQsIFwiVW5zZW5kIGFsbCBETXNcIiwgQlVUVE9OX1NUWUxFLlBSSU1BUlkpXG5cdFx0Y29uc3Qgc3RhdHVzRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIilcblx0XHRzdGF0dXNFbGVtZW50LnRleHRDb250ZW50ID0gXCJSZWFkeVwiXG5cdFx0c3RhdHVzRWxlbWVudC5pZCA9IFwiaWRtdS1zdGF0dXNcIlxuXHRcdHN0YXR1c0VsZW1lbnQuc3R5bGUgPSBcIndpZHRoOiAyMDBweFwiXG5cdFx0ZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChvdmVybGF5RWxlbWVudClcblx0XHRkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGFsZXJ0c1dyYXBwZXJFbGVtZW50KVxuXHRcdG1lbnVFbGVtZW50LmFwcGVuZENoaWxkKHVuc2VuZFRocmVhZE1lc3NhZ2VzQnV0dG9uKVxuXHRcdG1lbnVFbGVtZW50LmFwcGVuZENoaWxkKHN0YXR1c0VsZW1lbnQpXG5cdFx0cm9vdC5hcHBlbmRDaGlsZChtZW51RWxlbWVudClcblx0XHRjb25zdCB1aSA9IG5ldyBPU0QoZG9jdW1lbnQsIHJvb3QsIG92ZXJsYXlFbGVtZW50LCBtZW51RWxlbWVudCwgdW5zZW5kVGhyZWFkTWVzc2FnZXNCdXR0b24sIHN0YXR1c0VsZW1lbnQpXG5cdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcImtleWRvd25cIiwgKGV2ZW50KSA9PiB1aS4jb25XaW5kb3dLZXlFdmVudChldmVudCkpIC8vIFRPRE8gdGVzdFxuXHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJrZXl1cFwiLCAoZXZlbnQpID0+IHVpLiNvbldpbmRvd0tleUV2ZW50KGV2ZW50KSkgLy8gVE9ETyB0ZXN0XG5cdFx0dW5zZW5kVGhyZWFkTWVzc2FnZXNCdXR0b24uYWRkRXZlbnRMaXN0ZW5lcihcImNsaWNrXCIsIChldmVudCkgPT4gdWkuI29uVW5zZW5kVGhyZWFkTWVzc2FnZXNCdXR0b25DbGljayhldmVudCkpXG5cdFx0dWkuX211dGF0aW9uT2JzZXJ2ZXIgPSBuZXcgTXV0YXRpb25PYnNlcnZlcigobXV0YXRpb25zKSA9PiB1aS4jb25NdXRhdGlvbnModWksIG11dGF0aW9ucykpXG5cdFx0dWkuX211dGF0aW9uT2JzZXJ2ZXIub2JzZXJ2ZShkb2N1bWVudC5ib2R5LCB7IGNoaWxkTGlzdDogdHJ1ZSB9KSAvLyBUT0RPIHRlc3Rcblx0XHR1bnNlbmRUaHJlYWRNZXNzYWdlc0J1dHRvbi5kYXRhVGV4dENvbnRlbnQgPSB1bnNlbmRUaHJlYWRNZXNzYWdlc0J1dHRvbi50ZXh0Q29udGVudFxuXHRcdHVuc2VuZFRocmVhZE1lc3NhZ2VzQnV0dG9uLmRhdGFCYWNrZ3JvdW5kQ29sb3IgPSB1bnNlbmRUaHJlYWRNZXNzYWdlc0J1dHRvbi5zdHlsZS5iYWNrZ3JvdW5kQ29sb3Jcblx0XHRyZXR1cm4gdWlcblx0fVxuXG5cdC8qKlxuXHQgKlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdGV4dFxuXHQgKi9cblx0b25TdGF0dXNUZXh0KHRleHQpIHtcblx0XHR0aGlzLnN0YXR1c0VsZW1lbnQudGV4dENvbnRlbnQgPSB0ZXh0XG5cdH1cblxuXHRhc3luYyAjc3RhcnRVbnNlbmRpbmcoKSB7XG5cdFx0O1suLi50aGlzLm1lbnVFbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoXCJidXR0b25cIildLmZpbHRlcihidXR0b24gPT4gYnV0dG9uICE9PSB0aGlzLnVuc2VuZFRocmVhZE1lc3NhZ2VzQnV0dG9uKS5mb3JFYWNoKGJ1dHRvbiA9PiB7XG5cdFx0XHRidXR0b24uc3R5bGUudmlzaWJpbGl0eSA9IFwiaGlkZGVuXCJcblx0XHRcdGJ1dHRvbi5kaXNhYmxlZCA9IHRydWVcblx0XHR9KVxuXHRcdHRoaXMub3ZlcmxheUVsZW1lbnQuc3R5bGUuZGlzcGxheSA9IFwiXCJcblx0XHR0aGlzLm92ZXJsYXlFbGVtZW50LmZvY3VzKClcblx0XHR0aGlzLnVuc2VuZFRocmVhZE1lc3NhZ2VzQnV0dG9uLnRleHRDb250ZW50ID0gXCJTdG9wIHByb2Nlc3NpbmdcIlxuXHRcdHRoaXMudW5zZW5kVGhyZWFkTWVzc2FnZXNCdXR0b24uc3R5bGUuYmFja2dyb3VuZENvbG9yID0gXCIjRkEzODNFXCJcblx0XHR0aGlzLnN0YXR1c0VsZW1lbnQuc3R5bGUuY29sb3IgPSBcIndoaXRlXCJcblx0XHR0aGlzLl9tdXRhdGlvbk9ic2VydmVyLmRpc2Nvbm5lY3QoKVxuXHRcdHRyeSB7XG5cdFx0XHRhd2FpdCB0aGlzLnN0cmF0ZWd5LnJ1bigpXG5cdFx0fSBjYXRjaChlcnJvcikge1xuXHRcdFx0Y29uc29sZS5lcnJvcihlcnJvcilcblx0XHRcdGlmKHRoaXMuc3RyYXRlZ3kuaXNSdW5uaW5nKCkpIHtcblx0XHRcdFx0dGhpcy5zdHJhdGVneS5zdG9wKClcblx0XHRcdH1cblx0XHRcdHRoaXMuc3RhdHVzRWxlbWVudC5pbm5lckhUTUwgPSBgPHNwYW4gc3R5bGU9XCJjb2xvcjogcmVkXCI+QW4gZXJyb3Igb2NjdXJlZCwgPGEgaHJlZj1cImh0dHBzOi8vZ2l0aHViLmNvbS90aG91Z2h0c3VuaWZpY2F0b3IvaW5zdGFncmFtLWRtLXVuc2VuZGVyL2lzc3Vlcy9uZXc/dGVtcGxhdGU9YnVnX3JlcG9ydC5tZFwiPnBsZWFzZSBvcGVuIGFuIGlzc3VlPC9hPjwvc3Bhbj5gXG5cdFx0fSBmaW5hbGx5IHtcblx0XHRcdHRoaXMuI29uVW5zZW5kaW5nRmluaXNoZWQoKVxuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKlxuXHQgKiBAcGFyYW0ge09TRH0gdWlcblx0ICovXG5cdCNvbk11dGF0aW9ucyh1aSkge1xuXHRcdGlmKHVpLnJvb3Qub3duZXJEb2N1bWVudC5xdWVyeVNlbGVjdG9yKFwiW2lkXj1tb3VudF0gPiBkaXYgPiBkaXYgPiBkaXZcIikgIT09IG51bGwgJiYgdWkpIHtcblx0XHRcdGlmKHRoaXMuX211dGF0aW9uT2JzZXJ2ZXIpIHtcblx0XHRcdFx0dGhpcy5fbXV0YXRpb25PYnNlcnZlci5kaXNjb25uZWN0KClcblx0XHRcdH1cblx0XHRcdHRoaXMuX211dGF0aW9uT2JzZXJ2ZXIgPSBuZXcgTXV0YXRpb25PYnNlcnZlcih1aS4jb25NdXRhdGlvbnMuYmluZCh0aGlzLCB1aSkpXG5cdFx0XHR0aGlzLl9tdXRhdGlvbk9ic2VydmVyLm9ic2VydmUodWkucm9vdC5vd25lckRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXCJbaWRePW1vdW50XSA+IGRpdiA+IGRpdiA+IGRpdlwiKSwgeyBjaGlsZExpc3Q6IHRydWUsIGF0dHJpYnV0ZXM6IHRydWUgfSlcblx0XHR9XG5cdFx0aWYodGhpcy53aW5kb3cubG9jYXRpb24ucGF0aG5hbWUuc3RhcnRzV2l0aChcIi9kaXJlY3QvdC9cIikpIHtcblx0XHRcdGlmKCF0aGlzLnN0cmF0ZWd5LmlzUnVubmluZygpKSB7XG5cdFx0XHRcdHRoaXMuc3RyYXRlZ3kucmVzZXQoKVxuXHRcdFx0fVxuXHRcdFx0dGhpcy5yb290LnN0eWxlLmRpc3BsYXkgPSBcIlwiXG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMucm9vdC5zdHlsZS5kaXNwbGF5ID0gXCJub25lXCJcblx0XHRcdGlmKHRoaXMuc3RyYXRlZ3kuaXNSdW5uaW5nKCkpIHtcblx0XHRcdFx0dGhpcy5zdHJhdGVneS5zdG9wKClcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICpcblx0ICogQHBhcmFtIHtPU0R9IHVpXG5cdCAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50XG5cdCAqL1xuXHQjb25VbnNlbmRUaHJlYWRNZXNzYWdlc0J1dHRvbkNsaWNrKCkge1xuXHRcdGlmKHRoaXMuc3RyYXRlZ3kuaXNSdW5uaW5nKCkpIHtcblx0XHRcdGNvbnNvbGUuZGVidWcoXCJVc2VyIGFza2VkIGZvciBtZXNzYWdlcyB1bnNlbmRpbmcgdG8gc3RvcFwiKVxuXHRcdFx0dGhpcy5zdHJhdGVneS5zdG9wKClcblx0XHRcdHRoaXMuI29uVW5zZW5kaW5nRmluaXNoZWQoKVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRjb25zb2xlLmRlYnVnKFwiVXNlciBhc2tlZCBmb3IgbWVzc2FnZXMgdW5zZW5kaW5nIHRvIHN0YXJ0OyBVSSBpbnRlcmFjdGlvbiB3aWxsIGJlIGRpc2FibGVkIGluIHRoZSBtZWFudGltZVwiKVxuXHRcdFx0dGhpcy4jc3RhcnRVbnNlbmRpbmcoKVxuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKlxuXHQgKiBAcGFyYW0ge0V2ZW50fSBldmVudFxuXHQgKiBAcmV0dXJucyB7Ym9vbGVhbn1cblx0ICovXG5cdCNvbldpbmRvd0tleUV2ZW50KGV2ZW50KSB7XG5cdFx0aWYodGhpcy5zdHJhdGVneS5pc1J1bm5pbmcoKSkge1xuXHRcdFx0Y29uc29sZS5sb2coXCJVc2VyIGludGVyYWN0aW9uIGlzIGRpc2FibGVkIGFzIHRoZSB1bnNlbmRpbmcgaXMgc3RpbGwgcnVubmluZzsgUGxlYXNlIHN0b3AgdGhlIGV4ZWN1dGlvbiBmaXJzdC5cIilcblx0XHRcdGV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpXG5cdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpXG5cdFx0XHRldmVudC5zdG9wUHJvcGFnYXRpb24oKVxuXHRcdFx0dGhpcy5vdmVybGF5RWxlbWVudC5mb2N1cygpXG5cdFx0XHRyZXR1cm4gZmFsc2Vcblx0XHR9XG5cdH1cblxuXHQjb25VbnNlbmRpbmdGaW5pc2hlZCgpIHtcblx0XHRjb25zb2xlLmRlYnVnKFwicmVuZGVyIG9uVW5zZW5kaW5nRmluaXNoZWRcIilcblx0XHQ7Wy4uLnRoaXMubWVudUVsZW1lbnQucXVlcnlTZWxlY3RvckFsbChcImJ1dHRvblwiKV0uZmlsdGVyKGJ1dHRvbiA9PiBidXR0b24gIT09IHRoaXMudW5zZW5kVGhyZWFkTWVzc2FnZXNCdXR0b24pLmZvckVhY2goYnV0dG9uID0+IHtcblx0XHRcdGJ1dHRvbi5zdHlsZS52aXNpYmlsaXR5ID0gXCJcIlxuXHRcdFx0YnV0dG9uLmRpc2FibGVkID0gZmFsc2Vcblx0XHR9KVxuXHRcdHRoaXMudW5zZW5kVGhyZWFkTWVzc2FnZXNCdXR0b24udGV4dENvbnRlbnQgPSB0aGlzLnVuc2VuZFRocmVhZE1lc3NhZ2VzQnV0dG9uLmRhdGFUZXh0Q29udGVudFxuXHRcdHRoaXMudW5zZW5kVGhyZWFkTWVzc2FnZXNCdXR0b24uc3R5bGUuYmFja2dyb3VuZENvbG9yID0gdGhpcy51bnNlbmRUaHJlYWRNZXNzYWdlc0J1dHRvbi5kYXRhQmFja2dyb3VuZENvbG9yXG5cdFx0dGhpcy5vdmVybGF5RWxlbWVudC5zdHlsZS5kaXNwbGF5ID0gXCJub25lXCJcblx0XHR0aGlzLnN0YXR1c0VsZW1lbnQuc3R5bGUuY29sb3IgPSBcIlwiXG5cdFx0dGhpcy5fbXV0YXRpb25PYnNlcnZlci5vYnNlcnZlKHRoaXMuX2RvY3VtZW50LmJvZHksIHsgY2hpbGRMaXN0OiB0cnVlIH0pIC8vIFRPRE8gdGVzdFxuXHR9XG5cblx0LyoqXG5cdCAqIEByZWFkb25seVxuXHQgKiBAdHlwZSB7RG9jdW1lbnR9XG5cdCAqL1xuXHRnZXQgZG9jdW1lbnQoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX2RvY3VtZW50XG5cdH1cblxuXHQvKipcblx0ICogQHJlYWRvbmx5XG5cdCAqIEB0eXBlIHtXaW5kb3d9XG5cdCAqL1xuXHRnZXQgd2luZG93KCkge1xuXHRcdHJldHVybiB0aGlzLl9kb2N1bWVudC5kZWZhdWx0Vmlld1xuXHR9XG5cblx0LyoqXG5cdCAqIEByZWFkb25seVxuXHQgKiBAdHlwZSB7SFRNTERpdkVsZW1lbnR9XG5cdCAqL1xuXHRnZXQgcm9vdCgpIHtcblx0XHRyZXR1cm4gdGhpcy5fcm9vdFxuXHR9XG5cblx0LyoqXG5cdCAqIEByZWFkb25seVxuXHQgKiBAdHlwZSB7SFRNTERpdkVsZW1lbnR9XG5cdCAqL1xuXHRnZXQgb3ZlcmxheUVsZW1lbnQoKSB7XG5cdFx0cmV0dXJuIHRoaXMuX292ZXJsYXlFbGVtZW50XG5cdH1cblxuXHQvKipcblx0ICogQHJlYWRvbmx5XG5cdCAqIEB0eXBlIHtIVE1MRGl2RWxlbWVudH1cblx0ICovXG5cdGdldCBtZW51RWxlbWVudCgpIHtcblx0XHRyZXR1cm4gdGhpcy5fbWVudUVsZW1lbnRcblx0fVxuXG5cdC8qKlxuXHQgKiBAcmVhZG9ubHlcblx0ICogQHR5cGUge0hUTUxCdXR0b25FbGVtZW50fVxuXHQgKi9cblx0Z2V0IHVuc2VuZFRocmVhZE1lc3NhZ2VzQnV0dG9uKCkge1xuXHRcdHJldHVybiB0aGlzLl91bnNlbmRUaHJlYWRNZXNzYWdlc0J1dHRvblxuXHR9XG5cblx0LyoqXG5cdCAqIEByZWFkb25seVxuXHQgKiBAdHlwZSB7SFRNTERpdkVsZW1lbnR9XG5cdCAqL1xuXHRnZXQgc3RhdHVzRWxlbWVudCgpIHtcblx0XHRyZXR1cm4gdGhpcy5fc3RhdHVzRWxlbWVudFxuXHR9XG5cblx0LyoqXG5cdCAqIEByZWFkb25seVxuXHQgKiBAdHlwZSB7VW5zZW5kU3RyYXRlZ3l9XG5cdCAqL1xuXHRnZXQgc3RyYXRlZ3koKSB7IC8vIFRPRE8gbW92ZSBvdXRcblx0XHRyZXR1cm4gdGhpcy5fc3RyYXRlZ3lcblx0fVxuXG5cdC8qKlxuXHQgKiBAcmVhZG9ubHlcblx0ICogQHR5cGUge0lETVV9XG5cdCAqL1xuXHRnZXQgaWRtdSgpIHtcblx0XHRyZXR1cm4gdGhpcy5faWRtdVxuXHR9XG5cbn1cblxuZXhwb3J0IGRlZmF1bHQgT1NEXG4iLCIvKiogQG1vZHVsZSBtYWluIE1haW4gbW9kdWxlICovXG5cbmltcG9ydCBPU0QgZnJvbSBcIi4vb3NkL29zZC5qc1wiXG5cbi8qKlxuICogQHBhcmFtIHtXaW5kb3d9IHdpbmRvd1xuICovXG5leHBvcnQgZnVuY3Rpb24gbWFpbih3aW5kb3cpIHtcblx0T1NELnJlbmRlcih3aW5kb3cpXG59XG5cbmlmKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIpIHtcblx0bWFpbih3aW5kb3cpXG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztDQUFBO0FBQ0E7Q0FDTyxNQUFNLFlBQVksR0FBRztDQUM1QixDQUFDLFNBQVMsRUFBRSxTQUFTO0NBQ3JCLENBQUMsV0FBVyxFQUFFLFdBQVc7Q0FDekIsRUFBQztBQUNEO0NBQ0E7Q0FDQTtDQUNBO0NBQ0E7Q0FDQTtDQUNPLFNBQVMsZ0JBQWdCLENBQUMsYUFBYSxFQUFFLFNBQVMsRUFBRTtDQUMzRCxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsUUFBUSxHQUFHLDZCQUE0QjtDQUM1RCxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsS0FBSyxHQUFHLFFBQU87Q0FDcEMsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxNQUFLO0NBQ25DLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxZQUFZLEdBQUcsTUFBSztDQUN6QyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsT0FBTyxHQUFHLE1BQUs7Q0FDcEMsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLFVBQVUsR0FBRyxPQUFNO0NBQ3hDLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxNQUFNLEdBQUcsVUFBUztDQUN2QyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsVUFBVSxHQUFHLCtCQUE4QjtDQUNoRSxDQUFDLEdBQUcsU0FBUyxFQUFFO0NBQ2YsRUFBRSxh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