matrix-react-sdk
Version:
SDK for matrix.org using React
327 lines (313 loc) • 46.2 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DEFAULT_THEME = void 0;
exports.enumerateThemes = enumerateThemes;
exports.findHighContrastTheme = findHighContrastTheme;
exports.findNonHighContrastTheme = findNonHighContrastTheme;
exports.getCustomTheme = getCustomTheme;
exports.getOrderedThemes = getOrderedThemes;
exports.isHighContrastTheme = isHighContrastTheme;
exports.setTheme = setTheme;
var _logger = require("matrix-js-sdk/src/logger");
var _languageHandler = require("./languageHandler");
var _SettingsStore = _interopRequireDefault(require("./settings/SettingsStore"));
var _ThemeWatcher = _interopRequireDefault(require("./settings/watchers/ThemeWatcher"));
/*
Copyright 2024 New Vector Ltd.
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2019 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
const DEFAULT_THEME = exports.DEFAULT_THEME = "light";
const HIGH_CONTRAST_THEMES = {
light: "light-high-contrast"
};
/**
* Given a non-high-contrast theme, find the corresponding high-contrast one
* if it exists, or return undefined if not.
*/
function findHighContrastTheme(theme) {
return HIGH_CONTRAST_THEMES[theme];
}
/**
* Given a high-contrast theme, find the corresponding non-high-contrast one
* if it exists, or return undefined if not.
*/
function findNonHighContrastTheme(hcTheme) {
for (const theme in HIGH_CONTRAST_THEMES) {
if (HIGH_CONTRAST_THEMES[theme] === hcTheme) {
return theme;
}
}
}
/**
* Decide whether the supplied theme is high contrast.
*/
function isHighContrastTheme(theme) {
return Object.values(HIGH_CONTRAST_THEMES).includes(theme);
}
function enumerateThemes() {
const BUILTIN_THEMES = {
"light": (0, _languageHandler._t)("common|light"),
"light-high-contrast": (0, _languageHandler._t)("theme|light_high_contrast"),
"dark": (0, _languageHandler._t)("common|dark")
};
const customThemes = _SettingsStore.default.getValue("custom_themes") || [];
const customThemeNames = {};
try {
for (const {
name
} of customThemes) {
customThemeNames[`custom-${name}`] = name;
}
} catch (err) {
_logger.logger.warn("Error loading custom themes", {
err,
customThemes
});
}
return Object.assign({}, customThemeNames, BUILTIN_THEMES);
}
function getOrderedThemes() {
const themes = Object.entries(enumerateThemes()).map(p => ({
id: p[0],
name: p[1]
})) // convert pairs to objects for code readability
.filter(p => !isHighContrastTheme(p.id));
const builtInThemes = themes.filter(p => !p.id.startsWith("custom-"));
const collator = new Intl.Collator();
const customThemes = themes.filter(p => !builtInThemes.includes(p)).sort((a, b) => collator.compare(a.name, b.name));
return [...builtInThemes, ...customThemes];
}
function clearCustomTheme() {
// remove all css variables, we assume these are there because of the custom theme
const inlineStyleProps = Object.values(document.body.style);
for (const prop of inlineStyleProps) {
if (prop.startsWith("--")) {
document.body.style.removeProperty(prop);
}
}
// remove the custom style sheets
document.querySelector("head > style[title='custom-theme-font-faces']")?.remove();
document.querySelector("head > style[title='custom-theme-compound']")?.remove();
}
const allowedFontFaceProps = ["font-display", "font-family", "font-stretch", "font-style", "font-weight", "font-variant", "font-feature-settings", "font-variation-settings", "src", "unicode-range"];
function generateCustomFontFaceCSS(faces) {
return faces.map(face => {
const src = face.src?.map(srcElement => {
let format = "";
if (srcElement.format) {
format = `format("${srcElement.format}")`;
}
if (srcElement.url) {
return `url("${srcElement.url}") ${format}`;
} else if (srcElement.local) {
return `local("${srcElement.local}") ${format}`;
}
return "";
}).join(", ");
const props = Object.keys(face).filter(prop => allowedFontFaceProps.includes(prop));
const body = props.map(prop => {
let value;
if (prop === "src") {
value = src;
} else if (prop === "font-family") {
value = `"${face[prop]}"`;
} else {
value = face[prop];
}
return `${prop}: ${value}`;
}).join(";");
return `@font-face {${body}}`;
}).join("\n");
}
const COMPOUND_TOKEN = /^--cpd-[a-z0-9-]+$/;
/**
* Generates a style sheet to override Compound design tokens as specified in
* the given theme.
*/
function generateCustomCompoundCSS(theme) {
const properties = [];
for (const [token, value] of Object.entries(theme)) if (COMPOUND_TOKEN.test(token)) properties.push(`${token}: ${value};`);else _logger.logger.warn(`'${token}' is not a valid Compound token`);
// Insert the design token overrides into the 'custom' cascade layer as
// documented at https://compound.element.io/?path=/docs/develop-theming--docs
return `@layer compound.custom { :root, [class*="cpd-theme-"] { ${properties.join(" ")} } }`;
}
function setCustomThemeVars(customTheme) {
const {
style
} = document.body;
function setCSSColorVariable(name, hexColor, doPct = true) {
style.setProperty(`--${name}`, hexColor);
if (doPct) {
// uses #rrggbbaa to define the color with alpha values at 0%, 15% and 50%
style.setProperty(`--${name}-0pct`, hexColor + "00");
style.setProperty(`--${name}-15pct`, hexColor + "26");
style.setProperty(`--${name}-50pct`, hexColor + "7F");
}
}
if (customTheme.colors) {
for (const [name, value] of Object.entries(customTheme.colors)) {
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i += 1) {
setCSSColorVariable(`${name}_${i}`, value[i], false);
}
} else {
setCSSColorVariable(name, value);
}
}
}
if (customTheme.fonts) {
const {
fonts
} = customTheme;
if (fonts.faces) {
const css = generateCustomFontFaceCSS(fonts.faces);
const style = document.createElement("style");
style.setAttribute("title", "custom-theme-font-faces");
style.setAttribute("type", "text/css");
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
}
if (fonts.general) {
style.setProperty("--font-family", fonts.general);
}
if (fonts.monospace) {
style.setProperty("--font-family-monospace", fonts.monospace);
}
}
if (customTheme.compound) {
const css = generateCustomCompoundCSS(customTheme.compound);
const style = document.createElement("style");
style.setAttribute("title", "custom-theme-compound");
style.setAttribute("type", "text/css");
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
}
}
function getCustomTheme(themeName) {
// set css variables
const customThemes = _SettingsStore.default.getValue("custom_themes");
if (!customThemes) {
throw new Error(`No custom themes set, can't set custom theme "${themeName}"`);
}
const customTheme = customThemes.find(t => t.name === themeName);
if (!customTheme) {
const knownNames = customThemes.map(t => t.name).join(", ");
throw new Error(`Can't find custom theme "${themeName}", only know ${knownNames}`);
}
return customTheme;
}
/**
* Called whenever someone changes the theme
* Async function that returns once the theme has been set
* (ie. the CSS has been loaded)
*
* @param {string} theme new theme
*/
async function setTheme(theme) {
if (!theme) {
const themeWatcher = new _ThemeWatcher.default();
theme = themeWatcher.getEffectiveTheme();
}
clearCustomTheme();
let stylesheetName = theme;
if (theme.startsWith("custom-")) {
const customTheme = getCustomTheme(theme.slice(7));
stylesheetName = customTheme.is_dark ? "dark-custom" : "light-custom";
setCustomThemeVars(customTheme);
}
// look for the stylesheet elements.
// styleElements is a map from style name to HTMLLinkElement.
const styleElements = new Map();
const themes = Array.from(document.querySelectorAll("[data-mx-theme]"));
themes.forEach(theme => {
styleElements.set(theme.dataset.mxTheme.toLowerCase(), theme);
});
if (!styleElements.has(stylesheetName)) {
throw new Error("Unknown theme " + stylesheetName);
}
// disable all of them first, then enable the one we want. Chrome only
// bothers to do an update on a true->false transition, so this ensures
// that we get exactly one update, at the right time.
//
// ^ This comment was true when we used to use alternative stylesheets
// for the CSS. Nowadays we just set them all as disabled in index.html
// and enable them as needed. It might be cleaner to disable them all
// at the same time to prevent loading two themes simultaneously and
// having them interact badly... but this causes a flash of unstyled app
// which is even uglier. So we don't.
const styleSheet = styleElements.get(stylesheetName);
styleSheet.disabled = false;
/**
* Adds the Compound theme class to the top-most element in the document
* This will automatically refresh the colour scales based on the OS or user
* preferences
*/
document.body.classList.remove("cpd-theme-light", "cpd-theme-dark", "cpd-theme-light-hc", "cpd-theme-dark-hc");
let compoundThemeClassName = `cpd-theme-` + (stylesheetName.includes("light") ? "light" : "dark");
// Always respect user OS preference!
if (isHighContrastTheme(theme) || window.matchMedia("(prefers-contrast: more)").matches) {
compoundThemeClassName += "-hc";
}
document.body.classList.add(compoundThemeClassName);
return new Promise((resolve, reject) => {
const switchTheme = function () {
// we re-enable our theme here just in case we raced with another
// theme set request as per https://github.com/vector-im/element-web/issues/5601.
// We could alternatively lock or similar to stop the race, but
// this is probably good enough for now.
styleSheet.disabled = false;
styleElements.forEach(a => {
if (a == styleSheet) return;
a.disabled = true;
});
const bodyStyles = global.getComputedStyle(document.body);
if (bodyStyles.backgroundColor) {
const metaElement = document.querySelector('meta[name="theme-color"]');
metaElement.content = bodyStyles.backgroundColor;
}
resolve();
};
const isStyleSheetLoaded = () => Boolean([...document.styleSheets].find(_styleSheet => _styleSheet?.href === styleSheet.href));
function waitForStyleSheetLoading() {
// turns out that Firefox preloads the CSS for link elements with
// the disabled attribute, but Chrome doesn't.
if (isStyleSheetLoaded()) {
switchTheme();
return;
}
let counter = 0;
// In case of theme toggling (white => black => white)
// Chrome doesn't fire the `load` event when the white theme is selected the second times
const intervalId = window.setInterval(() => {
if (isStyleSheetLoaded()) {
clearInterval(intervalId);
styleSheet.onload = null;
styleSheet.onerror = null;
switchTheme();
}
// Avoid to be stuck in an endless loop if there is an issue in the stylesheet loading
counter++;
if (counter === 10) {
clearInterval(intervalId);
reject();
}
}, 200);
styleSheet.onload = () => {
clearInterval(intervalId);
switchTheme();
};
styleSheet.onerror = e => {
clearInterval(intervalId);
reject(e);
};
}
waitForStyleSheetLoading();
});
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbG9nZ2VyIiwicmVxdWlyZSIsIl9sYW5ndWFnZUhhbmRsZXIiLCJfU2V0dGluZ3NTdG9yZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJfVGhlbWVXYXRjaGVyIiwiREVGQVVMVF9USEVNRSIsImV4cG9ydHMiLCJISUdIX0NPTlRSQVNUX1RIRU1FUyIsImxpZ2h0IiwiZmluZEhpZ2hDb250cmFzdFRoZW1lIiwidGhlbWUiLCJmaW5kTm9uSGlnaENvbnRyYXN0VGhlbWUiLCJoY1RoZW1lIiwiaXNIaWdoQ29udHJhc3RUaGVtZSIsIk9iamVjdCIsInZhbHVlcyIsImluY2x1ZGVzIiwiZW51bWVyYXRlVGhlbWVzIiwiQlVJTFRJTl9USEVNRVMiLCJfdCIsImN1c3RvbVRoZW1lcyIsIlNldHRpbmdzU3RvcmUiLCJnZXRWYWx1ZSIsImN1c3RvbVRoZW1lTmFtZXMiLCJuYW1lIiwiZXJyIiwibG9nZ2VyIiwid2FybiIsImFzc2lnbiIsImdldE9yZGVyZWRUaGVtZXMiLCJ0aGVtZXMiLCJlbnRyaWVzIiwibWFwIiwicCIsImlkIiwiZmlsdGVyIiwiYnVpbHRJblRoZW1lcyIsInN0YXJ0c1dpdGgiLCJjb2xsYXRvciIsIkludGwiLCJDb2xsYXRvciIsInNvcnQiLCJhIiwiYiIsImNvbXBhcmUiLCJjbGVhckN1c3RvbVRoZW1lIiwiaW5saW5lU3R5bGVQcm9wcyIsImRvY3VtZW50IiwiYm9keSIsInN0eWxlIiwicHJvcCIsInJlbW92ZVByb3BlcnR5IiwicXVlcnlTZWxlY3RvciIsInJlbW92ZSIsImFsbG93ZWRGb250RmFjZVByb3BzIiwiZ2VuZXJhdGVDdXN0b21Gb250RmFjZUNTUyIsImZhY2VzIiwiZmFjZSIsInNyYyIsInNyY0VsZW1lbnQiLCJmb3JtYXQiLCJ1cmwiLCJsb2NhbCIsImpvaW4iLCJwcm9wcyIsImtleXMiLCJ2YWx1ZSIsIkNPTVBPVU5EX1RPS0VOIiwiZ2VuZXJhdGVDdXN0b21Db21wb3VuZENTUyIsInByb3BlcnRpZXMiLCJ0b2tlbiIsInRlc3QiLCJwdXNoIiwic2V0Q3VzdG9tVGhlbWVWYXJzIiwiY3VzdG9tVGhlbWUiLCJzZXRDU1NDb2xvclZhcmlhYmxlIiwiaGV4Q29sb3IiLCJkb1BjdCIsInNldFByb3BlcnR5IiwiY29sb3JzIiwiQXJyYXkiLCJpc0FycmF5IiwiaSIsImxlbmd0aCIsImZvbnRzIiwiY3NzIiwiY3JlYXRlRWxlbWVudCIsInNldEF0dHJpYnV0ZSIsImFwcGVuZENoaWxkIiwiY3JlYXRlVGV4dE5vZGUiLCJoZWFkIiwiZ2VuZXJhbCIsIm1vbm9zcGFjZSIsImNvbXBvdW5kIiwiZ2V0Q3VzdG9tVGhlbWUiLCJ0aGVtZU5hbWUiLCJFcnJvciIsImZpbmQiLCJ0Iiwia25vd25OYW1lcyIsInNldFRoZW1lIiwidGhlbWVXYXRjaGVyIiwiVGhlbWVXYXRjaGVyIiwiZ2V0RWZmZWN0aXZlVGhlbWUiLCJzdHlsZXNoZWV0TmFtZSIsInNsaWNlIiwiaXNfZGFyayIsInN0eWxlRWxlbWVudHMiLCJNYXAiLCJmcm9tIiwicXVlcnlTZWxlY3RvckFsbCIsImZvckVhY2giLCJzZXQiLCJkYXRhc2V0IiwibXhUaGVtZSIsInRvTG93ZXJDYXNlIiwiaGFzIiwic3R5bGVTaGVldCIsImdldCIsImRpc2FibGVkIiwiY2xhc3NMaXN0IiwiY29tcG91bmRUaGVtZUNsYXNzTmFtZSIsIndpbmRvdyIsIm1hdGNoTWVkaWEiLCJtYXRjaGVzIiwiYWRkIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJzd2l0Y2hUaGVtZSIsImJvZHlTdHlsZXMiLCJnbG9iYWwiLCJnZXRDb21wdXRlZFN0eWxlIiwiYmFja2dyb3VuZENvbG9yIiwibWV0YUVsZW1lbnQiLCJjb250ZW50IiwiaXNTdHlsZVNoZWV0TG9hZGVkIiwiQm9vbGVhbiIsInN0eWxlU2hlZXRzIiwiX3N0eWxlU2hlZXQiLCJocmVmIiwid2FpdEZvclN0eWxlU2hlZXRMb2FkaW5nIiwiY291bnRlciIsImludGVydmFsSWQiLCJzZXRJbnRlcnZhbCIsImNsZWFySW50ZXJ2YWwiLCJvbmxvYWQiLCJvbmVycm9yIiwiZSJdLCJzb3VyY2VzIjpbIi4uL3NyYy90aGVtZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuQ29weXJpZ2h0IDIwMjQgTmV3IFZlY3RvciBMdGQuXG5Db3B5cmlnaHQgMjAxOSBNaWNoYWVsIFRlbGF0eW5za2kgPDd0M2NoZ3V5QGdtYWlsLmNvbT5cbkNvcHlyaWdodCAyMDE5IFRoZSBNYXRyaXgub3JnIEZvdW5kYXRpb24gQy5JLkMuXG5cblNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBR1BMLTMuMC1vbmx5IE9SIEdQTC0zLjAtb25seVxuUGxlYXNlIHNlZSBMSUNFTlNFIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IHJvb3QgZm9yIGZ1bGwgZGV0YWlscy5cbiovXG5cbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9sb2dnZXJcIjtcblxuaW1wb3J0IHsgX3QgfSBmcm9tIFwiLi9sYW5ndWFnZUhhbmRsZXJcIjtcbmltcG9ydCBTZXR0aW5nc1N0b3JlIGZyb20gXCIuL3NldHRpbmdzL1NldHRpbmdzU3RvcmVcIjtcbmltcG9ydCBUaGVtZVdhdGNoZXIgZnJvbSBcIi4vc2V0dGluZ3Mvd2F0Y2hlcnMvVGhlbWVXYXRjaGVyXCI7XG5cbmV4cG9ydCBjb25zdCBERUZBVUxUX1RIRU1FID0gXCJsaWdodFwiO1xuY29uc3QgSElHSF9DT05UUkFTVF9USEVNRVM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgbGlnaHQ6IFwibGlnaHQtaGlnaC1jb250cmFzdFwiLFxufTtcblxuaW50ZXJmYWNlIElGb250RmFjZXMgZXh0ZW5kcyBPbWl0PFJlY29yZDwodHlwZW9mIGFsbG93ZWRGb250RmFjZVByb3BzKVtudW1iZXJdLCBzdHJpbmc+LCBcInNyY1wiPiB7XG4gICAgc3JjOiB7XG4gICAgICAgIGZvcm1hdDogc3RyaW5nO1xuICAgICAgICB1cmw6IHN0cmluZztcbiAgICAgICAgbG9jYWw6IHN0cmluZztcbiAgICB9W107XG59XG5cbmludGVyZmFjZSBDb21wb3VuZFRoZW1lIHtcbiAgICBbdG9rZW46IHN0cmluZ106IHN0cmluZztcbn1cblxuZXhwb3J0IHR5cGUgQ3VzdG9tVGhlbWUgPSB7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIGlzX2Rhcms/OiBib29sZWFuOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIGNhbWVsY2FzZVxuICAgIGNvbG9ycz86IHtcbiAgICAgICAgW2tleTogc3RyaW5nXTogc3RyaW5nO1xuICAgIH07XG4gICAgZm9udHM/OiB7XG4gICAgICAgIGZhY2VzOiBJRm9udEZhY2VzW107XG4gICAgICAgIGdlbmVyYWw6IHN0cmluZztcbiAgICAgICAgbW9ub3NwYWNlOiBzdHJpbmc7XG4gICAgfTtcbiAgICBjb21wb3VuZD86IENvbXBvdW5kVGhlbWU7XG59O1xuXG4vKipcbiAqIEdpdmVuIGEgbm9uLWhpZ2gtY29udHJhc3QgdGhlbWUsIGZpbmQgdGhlIGNvcnJlc3BvbmRpbmcgaGlnaC1jb250cmFzdCBvbmVcbiAqIGlmIGl0IGV4aXN0cywgb3IgcmV0dXJuIHVuZGVmaW5lZCBpZiBub3QuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaW5kSGlnaENvbnRyYXN0VGhlbWUodGhlbWU6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIEhJR0hfQ09OVFJBU1RfVEhFTUVTW3RoZW1lXTtcbn1cblxuLyoqXG4gKiBHaXZlbiBhIGhpZ2gtY29udHJhc3QgdGhlbWUsIGZpbmQgdGhlIGNvcnJlc3BvbmRpbmcgbm9uLWhpZ2gtY29udHJhc3Qgb25lXG4gKiBpZiBpdCBleGlzdHMsIG9yIHJldHVybiB1bmRlZmluZWQgaWYgbm90LlxuICovXG5leHBvcnQgZnVuY3Rpb24gZmluZE5vbkhpZ2hDb250cmFzdFRoZW1lKGhjVGhlbWU6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgZm9yIChjb25zdCB0aGVtZSBpbiBISUdIX0NPTlRSQVNUX1RIRU1FUykge1xuICAgICAgICBpZiAoSElHSF9DT05UUkFTVF9USEVNRVNbdGhlbWVdID09PSBoY1RoZW1lKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhlbWU7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbi8qKlxuICogRGVjaWRlIHdoZXRoZXIgdGhlIHN1cHBsaWVkIHRoZW1lIGlzIGhpZ2ggY29udHJhc3QuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc0hpZ2hDb250cmFzdFRoZW1lKHRoZW1lOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gT2JqZWN0LnZhbHVlcyhISUdIX0NPTlRSQVNUX1RIRU1FUykuaW5jbHVkZXModGhlbWUpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZW51bWVyYXRlVGhlbWVzKCk6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIH0ge1xuICAgIGNvbnN0IEJVSUxUSU5fVEhFTUVTID0ge1xuICAgICAgICBcImxpZ2h0XCI6IF90KFwiY29tbW9ufGxpZ2h0XCIpLFxuICAgICAgICBcImxpZ2h0LWhpZ2gtY29udHJhc3RcIjogX3QoXCJ0aGVtZXxsaWdodF9oaWdoX2NvbnRyYXN0XCIpLFxuICAgICAgICBcImRhcmtcIjogX3QoXCJjb21tb258ZGFya1wiKSxcbiAgICB9O1xuICAgIGNvbnN0IGN1c3RvbVRoZW1lcyA9IFNldHRpbmdzU3RvcmUuZ2V0VmFsdWUoXCJjdXN0b21fdGhlbWVzXCIpIHx8IFtdO1xuICAgIGNvbnN0IGN1c3RvbVRoZW1lTmFtZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblxuICAgIHRyeSB7XG4gICAgICAgIGZvciAoY29uc3QgeyBuYW1lIH0gb2YgY3VzdG9tVGhlbWVzKSB7XG4gICAgICAgICAgICBjdXN0b21UaGVtZU5hbWVzW2BjdXN0b20tJHtuYW1lfWBdID0gbmFtZTtcbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBsb2dnZXIud2FybihcIkVycm9yIGxvYWRpbmcgY3VzdG9tIHRoZW1lc1wiLCB7XG4gICAgICAgICAgICBlcnIsXG4gICAgICAgICAgICBjdXN0b21UaGVtZXMsXG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiBPYmplY3QuYXNzaWduKHt9LCBjdXN0b21UaGVtZU5hbWVzLCBCVUlMVElOX1RIRU1FUyk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSVRoZW1lIHtcbiAgICBpZDogc3RyaW5nO1xuICAgIG5hbWU6IHN0cmluZztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldE9yZGVyZWRUaGVtZXMoKTogSVRoZW1lW10ge1xuICAgIGNvbnN0IHRoZW1lcyA9IE9iamVjdC5lbnRyaWVzKGVudW1lcmF0ZVRoZW1lcygpKVxuICAgICAgICAubWFwKChwKSA9PiAoeyBpZDogcFswXSwgbmFtZTogcFsxXSB9KSkgLy8gY29udmVydCBwYWlycyB0byBvYmplY3RzIGZvciBjb2RlIHJlYWRhYmlsaXR5XG4gICAgICAgIC5maWx0ZXIoKHApID0+ICFpc0hpZ2hDb250cmFzdFRoZW1lKHAuaWQpKTtcbiAgICBjb25zdCBidWlsdEluVGhlbWVzID0gdGhlbWVzLmZpbHRlcigocCkgPT4gIXAuaWQuc3RhcnRzV2l0aChcImN1c3RvbS1cIikpO1xuICAgIGNvbnN0IGNvbGxhdG9yID0gbmV3IEludGwuQ29sbGF0b3IoKTtcbiAgICBjb25zdCBjdXN0b21UaGVtZXMgPSB0aGVtZXNcbiAgICAgICAgLmZpbHRlcigocCkgPT4gIWJ1aWx0SW5UaGVtZXMuaW5jbHVkZXMocCkpXG4gICAgICAgIC5zb3J0KChhLCBiKSA9PiBjb2xsYXRvci5jb21wYXJlKGEubmFtZSwgYi5uYW1lKSk7XG4gICAgcmV0dXJuIFsuLi5idWlsdEluVGhlbWVzLCAuLi5jdXN0b21UaGVtZXNdO1xufVxuXG5mdW5jdGlvbiBjbGVhckN1c3RvbVRoZW1lKCk6IHZvaWQge1xuICAgIC8vIHJlbW92ZSBhbGwgY3NzIHZhcmlhYmxlcywgd2UgYXNzdW1lIHRoZXNlIGFyZSB0aGVyZSBiZWNhdXNlIG9mIHRoZSBjdXN0b20gdGhlbWVcbiAgICBjb25zdCBpbmxpbmVTdHlsZVByb3BzID0gT2JqZWN0LnZhbHVlcyhkb2N1bWVudC5ib2R5LnN0eWxlKTtcbiAgICBmb3IgKGNvbnN0IHByb3Agb2YgaW5saW5lU3R5bGVQcm9wcykge1xuICAgICAgICBpZiAocHJvcC5zdGFydHNXaXRoKFwiLS1cIikpIHtcbiAgICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUucmVtb3ZlUHJvcGVydHkocHJvcCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyByZW1vdmUgdGhlIGN1c3RvbSBzdHlsZSBzaGVldHNcbiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFwiaGVhZCA+IHN0eWxlW3RpdGxlPSdjdXN0b20tdGhlbWUtZm9udC1mYWNlcyddXCIpPy5yZW1vdmUoKTtcbiAgICBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFwiaGVhZCA+IHN0eWxlW3RpdGxlPSdjdXN0b20tdGhlbWUtY29tcG91bmQnXVwiKT8ucmVtb3ZlKCk7XG59XG5cbmNvbnN0IGFsbG93ZWRGb250RmFjZVByb3BzID0gW1xuICAgIFwiZm9udC1kaXNwbGF5XCIsXG4gICAgXCJmb250LWZhbWlseVwiLFxuICAgIFwiZm9udC1zdHJldGNoXCIsXG4gICAgXCJmb250LXN0eWxlXCIsXG4gICAgXCJmb250LXdlaWdodFwiLFxuICAgIFwiZm9udC12YXJpYW50XCIsXG4gICAgXCJmb250LWZlYXR1cmUtc2V0dGluZ3NcIixcbiAgICBcImZvbnQtdmFyaWF0aW9uLXNldHRpbmdzXCIsXG4gICAgXCJzcmNcIixcbiAgICBcInVuaWNvZGUtcmFuZ2VcIixcbl0gYXMgY29uc3Q7XG5cbmZ1bmN0aW9uIGdlbmVyYXRlQ3VzdG9tRm9udEZhY2VDU1MoZmFjZXM6IElGb250RmFjZXNbXSk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGZhY2VzXG4gICAgICAgIC5tYXAoKGZhY2UpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHNyYyA9IGZhY2Uuc3JjXG4gICAgICAgICAgICAgICAgPy5tYXAoKHNyY0VsZW1lbnQpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgbGV0IGZvcm1hdCA9IFwiXCI7XG4gICAgICAgICAgICAgICAgICAgIGlmIChzcmNFbGVtZW50LmZvcm1hdCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgZm9ybWF0ID0gYGZvcm1hdChcIiR7c3JjRWxlbWVudC5mb3JtYXR9XCIpYDtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBpZiAoc3JjRWxlbWVudC51cmwpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBgdXJsKFwiJHtzcmNFbGVtZW50LnVybH1cIikgJHtmb3JtYXR9YDtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChzcmNFbGVtZW50LmxvY2FsKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gYGxvY2FsKFwiJHtzcmNFbGVtZW50LmxvY2FsfVwiKSAke2Zvcm1hdH1gO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBcIlwiO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmpvaW4oXCIsIFwiKTtcbiAgICAgICAgICAgIGNvbnN0IHByb3BzID0gT2JqZWN0LmtleXMoZmFjZSkuZmlsdGVyKChwcm9wKSA9PlxuICAgICAgICAgICAgICAgIGFsbG93ZWRGb250RmFjZVByb3BzLmluY2x1ZGVzKHByb3AgYXMgKHR5cGVvZiBhbGxvd2VkRm9udEZhY2VQcm9wcylbbnVtYmVyXSksXG4gICAgICAgICAgICApIGFzIEFycmF5PCh0eXBlb2YgYWxsb3dlZEZvbnRGYWNlUHJvcHMpW251bWJlcl0+O1xuICAgICAgICAgICAgY29uc3QgYm9keSA9IHByb3BzXG4gICAgICAgICAgICAgICAgLm1hcCgocHJvcCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBsZXQgdmFsdWU6IHN0cmluZztcbiAgICAgICAgICAgICAgICAgICAgaWYgKHByb3AgPT09IFwic3JjXCIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gc3JjO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHByb3AgPT09IFwiZm9udC1mYW1pbHlcIikge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBgXCIke2ZhY2VbcHJvcF19XCJgO1xuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBmYWNlW3Byb3BdO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBgJHtwcm9wfTogJHt2YWx1ZX1gO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmpvaW4oXCI7XCIpO1xuICAgICAgICAgICAgcmV0dXJuIGBAZm9udC1mYWNlIHske2JvZHl9fWA7XG4gICAgICAgIH0pXG4gICAgICAgIC5qb2luKFwiXFxuXCIpO1xufVxuXG5jb25zdCBDT01QT1VORF9UT0tFTiA9IC9eLS1jcGQtW2EtejAtOS1dKyQvO1xuXG4vKipcbiAqIEdlbmVyYXRlcyBhIHN0eWxlIHNoZWV0IHRvIG92ZXJyaWRlIENvbXBvdW5kIGRlc2lnbiB0b2tlbnMgYXMgc3BlY2lmaWVkIGluXG4gKiB0aGUgZ2l2ZW4gdGhlbWUuXG4gKi9cbmZ1bmN0aW9uIGdlbmVyYXRlQ3VzdG9tQ29tcG91bmRDU1ModGhlbWU6IENvbXBvdW5kVGhlbWUpOiBzdHJpbmcge1xuICAgIGNvbnN0IHByb3BlcnRpZXM6IHN0cmluZ1tdID0gW107XG4gICAgZm9yIChjb25zdCBbdG9rZW4sIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyh0aGVtZSkpXG4gICAgICAgIGlmIChDT01QT1VORF9UT0tFTi50ZXN0KHRva2VuKSkgcHJvcGVydGllcy5wdXNoKGAke3Rva2VufTogJHt2YWx1ZX07YCk7XG4gICAgICAgIGVsc2UgbG9nZ2VyLndhcm4oYCcke3Rva2VufScgaXMgbm90IGEgdmFsaWQgQ29tcG91bmQgdG9rZW5gKTtcbiAgICAvLyBJbnNlcnQgdGhlIGRlc2lnbiB0b2tlbiBvdmVycmlkZXMgaW50byB0aGUgJ2N1c3RvbScgY2FzY2FkZSBsYXllciBhc1xuICAgIC8vIGRvY3VtZW50ZWQgYXQgaHR0cHM6Ly9jb21wb3VuZC5lbGVtZW50LmlvLz9wYXRoPS9kb2NzL2RldmVsb3AtdGhlbWluZy0tZG9jc1xuICAgIHJldHVybiBgQGxheWVyIGNvbXBvdW5kLmN1c3RvbSB7IDpyb290LCBbY2xhc3MqPVwiY3BkLXRoZW1lLVwiXSB7ICR7cHJvcGVydGllcy5qb2luKFwiIFwiKX0gfSB9YDtcbn1cblxuZnVuY3Rpb24gc2V0Q3VzdG9tVGhlbWVWYXJzKGN1c3RvbVRoZW1lOiBDdXN0b21UaGVtZSk6IHZvaWQge1xuICAgIGNvbnN0IHsgc3R5bGUgfSA9IGRvY3VtZW50LmJvZHk7XG5cbiAgICBmdW5jdGlvbiBzZXRDU1NDb2xvclZhcmlhYmxlKG5hbWU6IHN0cmluZywgaGV4Q29sb3I6IHN0cmluZywgZG9QY3QgPSB0cnVlKTogdm9pZCB7XG4gICAgICAgIHN0eWxlLnNldFByb3BlcnR5KGAtLSR7bmFtZX1gLCBoZXhDb2xvcik7XG4gICAgICAgIGlmIChkb1BjdCkge1xuICAgICAgICAgICAgLy8gdXNlcyAjcnJnZ2JiYWEgdG8gZGVmaW5lIHRoZSBjb2xvciB3aXRoIGFscGhhIHZhbHVlcyBhdCAwJSwgMTUlIGFuZCA1MCVcbiAgICAgICAgICAgIHN0eWxlLnNldFByb3BlcnR5KGAtLSR7bmFtZX0tMHBjdGAsIGhleENvbG9yICsgXCIwMFwiKTtcbiAgICAgICAgICAgIHN0eWxlLnNldFByb3BlcnR5KGAtLSR7bmFtZX0tMTVwY3RgLCBoZXhDb2xvciArIFwiMjZcIik7XG4gICAgICAgICAgICBzdHlsZS5zZXRQcm9wZXJ0eShgLS0ke25hbWV9LTUwcGN0YCwgaGV4Q29sb3IgKyBcIjdGXCIpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgaWYgKGN1c3RvbVRoZW1lLmNvbG9ycykge1xuICAgICAgICBmb3IgKGNvbnN0IFtuYW1lLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoY3VzdG9tVGhlbWUuY29sb3JzKSkge1xuICAgICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZS5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgICAgICAgICAgICAgICBzZXRDU1NDb2xvclZhcmlhYmxlKGAke25hbWV9XyR7aX1gLCB2YWx1ZVtpXSwgZmFsc2UpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc2V0Q1NTQ29sb3JWYXJpYWJsZShuYW1lLCB2YWx1ZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG4gICAgaWYgKGN1c3RvbVRoZW1lLmZvbnRzKSB7XG4gICAgICAgIGNvbnN0IHsgZm9udHMgfSA9IGN1c3RvbVRoZW1lO1xuICAgICAgICBpZiAoZm9udHMuZmFjZXMpIHtcbiAgICAgICAgICAgIGNvbnN0IGNzcyA9IGdlbmVyYXRlQ3VzdG9tRm9udEZhY2VDU1MoZm9udHMuZmFjZXMpO1xuICAgICAgICAgICAgY29uc3Qgc3R5bGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwic3R5bGVcIik7XG4gICAgICAgICAgICBzdHlsZS5zZXRBdHRyaWJ1dGUoXCJ0aXRsZVwiLCBcImN1c3RvbS10aGVtZS1mb250LWZhY2VzXCIpO1xuICAgICAgICAgICAgc3R5bGUuc2V0QXR0cmlidXRlKFwidHlwZVwiLCBcInRleHQvY3NzXCIpO1xuICAgICAgICAgICAgc3R5bGUuYXBwZW5kQ2hpbGQoZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoY3NzKSk7XG4gICAgICAgICAgICBkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKHN0eWxlKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZm9udHMuZ2VuZXJhbCkge1xuICAgICAgICAgICAgc3R5bGUuc2V0UHJvcGVydHkoXCItLWZvbnQtZmFtaWx5XCIsIGZvbnRzLmdlbmVyYWwpO1xuICAgICAgICB9XG4gICAgICAgIGlmIChmb250cy5tb25vc3BhY2UpIHtcbiAgICAgICAgICAgIHN0eWxlLnNldFByb3BlcnR5KFwiLS1mb250LWZhbWlseS1tb25vc3BhY2VcIiwgZm9udHMubW9ub3NwYWNlKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBpZiAoY3VzdG9tVGhlbWUuY29tcG91bmQpIHtcbiAgICAgICAgY29uc3QgY3NzID0gZ2VuZXJhdGVDdXN0b21Db21wb3VuZENTUyhjdXN0b21UaGVtZS5jb21wb3VuZCk7XG4gICAgICAgIGNvbnN0IHN0eWxlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInN0eWxlXCIpO1xuICAgICAgICBzdHlsZS5zZXRBdHRyaWJ1dGUoXCJ0aXRsZVwiLCBcImN1c3RvbS10aGVtZS1jb21wb3VuZFwiKTtcbiAgICAgICAgc3R5bGUuc2V0QXR0cmlidXRlKFwidHlwZVwiLCBcInRleHQvY3NzXCIpO1xuICAgICAgICBzdHlsZS5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShjc3MpKTtcbiAgICAgICAgZG9jdW1lbnQuaGVhZC5hcHBlbmRDaGlsZChzdHlsZSk7XG4gICAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q3VzdG9tVGhlbWUodGhlbWVOYW1lOiBzdHJpbmcpOiBDdXN0b21UaGVtZSB7XG4gICAgLy8gc2V0IGNzcyB2YXJpYWJsZXNcbiAgICBjb25zdCBjdXN0b21UaGVtZXMgPSBTZXR0aW5nc1N0b3JlLmdldFZhbHVlKFwiY3VzdG9tX3RoZW1lc1wiKTtcbiAgICBpZiAoIWN1c3RvbVRoZW1lcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE5vIGN1c3RvbSB0aGVtZXMgc2V0LCBjYW4ndCBzZXQgY3VzdG9tIHRoZW1lIFwiJHt0aGVtZU5hbWV9XCJgKTtcbiAgICB9XG4gICAgY29uc3QgY3VzdG9tVGhlbWUgPSBjdXN0b21UaGVtZXMuZmluZCgodDogSVRoZW1lKSA9PiB0Lm5hbWUgPT09IHRoZW1lTmFtZSk7XG4gICAgaWYgKCFjdXN0b21UaGVtZSkge1xuICAgICAgICBjb25zdCBrbm93bk5hbWVzID0gY3VzdG9tVGhlbWVzLm1hcCgodDogSVRoZW1lKSA9PiB0Lm5hbWUpLmpvaW4oXCIsIFwiKTtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBDYW4ndCBmaW5kIGN1c3RvbSB0aGVtZSBcIiR7dGhlbWVOYW1lfVwiLCBvbmx5IGtub3cgJHtrbm93bk5hbWVzfWApO1xuICAgIH1cbiAgICByZXR1cm4gY3VzdG9tVGhlbWU7XG59XG5cbi8qKlxuICogQ2FsbGVkIHdoZW5ldmVyIHNvbWVvbmUgY2hhbmdlcyB0aGUgdGhlbWVcbiAqIEFzeW5jIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBvbmNlIHRoZSB0aGVtZSBoYXMgYmVlbiBzZXRcbiAqIChpZS4gdGhlIENTUyBoYXMgYmVlbiBsb2FkZWQpXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHRoZW1lIG5ldyB0aGVtZVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2V0VGhlbWUodGhlbWU/OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoZW1lKSB7XG4gICAgICAgIGNvbnN0IHRoZW1lV2F0Y2hlciA9IG5ldyBUaGVtZVdhdGNoZXIoKTtcbiAgICAgICAgdGhlbWUgPSB0aGVtZVdhdGNoZXIuZ2V0RWZmZWN0aXZlVGhlbWUoKTtcbiAgICB9XG4gICAgY2xlYXJDdXN0b21UaGVtZSgpO1xuICAgIGxldCBzdHlsZXNoZWV0TmFtZSA9IHRoZW1lO1xuICAgIGlmICh0aGVtZS5zdGFydHNXaXRoKFwiY3VzdG9tLVwiKSkge1xuICAgICAgICBjb25zdCBjdXN0b21UaGVtZSA9IGdldEN1c3RvbVRoZW1lKHRoZW1lLnNsaWNlKDcpKTtcbiAgICAgICAgc3R5bGVzaGVldE5hbWUgPSBjdXN0b21UaGVtZS5pc19kYXJrID8gXCJkYXJrLWN1c3RvbVwiIDogXCJsaWdodC1jdXN0b21cIjtcbiAgICAgICAgc2V0Q3VzdG9tVGhlbWVWYXJzKGN1c3RvbVRoZW1lKTtcbiAgICB9XG5cbiAgICAvLyBsb29rIGZvciB0aGUgc3R5bGVzaGVldCBlbGVtZW50cy5cbiAgICAvLyBzdHlsZUVsZW1lbnRzIGlzIGEgbWFwIGZyb20gc3R5bGUgbmFtZSB0byBIVE1MTGlua0VsZW1lbnQuXG4gICAgY29uc3Qgc3R5bGVFbGVtZW50cyA9IG5ldyBNYXA8c3RyaW5nLCBIVE1MTGlua0VsZW1lbnQ+KCk7XG4gICAgY29uc3QgdGhlbWVzID0gQXJyYXkuZnJvbShkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsPEhUTUxMaW5rRWxlbWVudD4oXCJbZGF0YS1teC10aGVtZV1cIikpO1xuICAgIHRoZW1lcy5mb3JFYWNoKCh0aGVtZSkgPT4ge1xuICAgICAgICBzdHlsZUVsZW1lbnRzLnNldCh0aGVtZS5kYXRhc2V0Lm14VGhlbWUhLnRvTG93ZXJDYXNlKCksIHRoZW1lKTtcbiAgICB9KTtcblxuICAgIGlmICghc3R5bGVFbGVtZW50cy5oYXMoc3R5bGVzaGVldE5hbWUpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIlVua25vd24gdGhlbWUgXCIgKyBzdHlsZXNoZWV0TmFtZSk7XG4gICAgfVxuXG4gICAgLy8gZGlzYWJsZSBhbGwgb2YgdGhlbSBmaXJzdCwgdGhlbiBlbmFibGUgdGhlIG9uZSB3ZSB3YW50LiBDaHJvbWUgb25seVxuICAgIC8vIGJvdGhlcnMgdG8gZG8gYW4gdXBkYXRlIG9uIGEgdHJ1ZS0+ZmFsc2UgdHJhbnNpdGlvbiwgc28gdGhpcyBlbnN1cmVzXG4gICAgLy8gdGhhdCB3ZSBnZXQgZXhhY3RseSBvbmUgdXBkYXRlLCBhdCB0aGUgcmlnaHQgdGltZS5cbiAgICAvL1xuICAgIC8vIF4gVGhpcyBjb21tZW50IHdhcyB0cnVlIHdoZW4gd2UgdXNlZCB0byB1c2UgYWx0ZXJuYXRpdmUgc3R5bGVzaGVldHNcbiAgICAvLyBmb3IgdGhlIENTUy4gIE5vd2FkYXlzIHdlIGp1c3Qgc2V0IHRoZW0gYWxsIGFzIGRpc2FibGVkIGluIGluZGV4Lmh0bWxcbiAgICAvLyBhbmQgZW5hYmxlIHRoZW0gYXMgbmVlZGVkLiAgSXQgbWlnaHQgYmUgY2xlYW5lciB0byBkaXNhYmxlIHRoZW0gYWxsXG4gICAgLy8gYXQgdGhlIHNhbWUgdGltZSB0byBwcmV2ZW50IGxvYWRpbmcgdHdvIHRoZW1lcyBzaW11bHRhbmVvdXNseSBhbmRcbiAgICAvLyBoYXZpbmcgdGhlbSBpbnRlcmFjdCBiYWRseS4uLiBidXQgdGhpcyBjYXVzZXMgYSBmbGFzaCBvZiB1bnN0eWxlZCBhcHBcbiAgICAvLyB3aGljaCBpcyBldmVuIHVnbGllci4gIFNvIHdlIGRvbid0LlxuXG4gICAgY29uc3Qgc3R5bGVTaGVldCA9IHN0eWxlRWxlbWVudHMuZ2V0KHN0eWxlc2hlZXROYW1lKSE7XG4gICAgc3R5bGVTaGVldC5kaXNhYmxlZCA9IGZhbHNlO1xuXG4gICAgLyoqXG4gICAgICogQWRkcyB0aGUgQ29tcG91bmQgdGhlbWUgY2xhc3MgdG8gdGhlIHRvcC1tb3N0IGVsZW1lbnQgaW4gdGhlIGRvY3VtZW50XG4gICAgICogVGhpcyB3aWxsIGF1dG9tYXRpY2FsbHkgcmVmcmVzaCB0aGUgY29sb3VyIHNjYWxlcyBiYXNlZCBvbiB0aGUgT1Mgb3IgdXNlclxuICAgICAqIHByZWZlcmVuY2VzXG4gICAgICovXG4gICAgZG9jdW1lbnQuYm9keS5jbGFzc0xpc3QucmVtb3ZlKFwiY3BkLXRoZW1lLWxpZ2h0XCIsIFwiY3BkLXRoZW1lLWRhcmtcIiwgXCJjcGQtdGhlbWUtbGlnaHQtaGNcIiwgXCJjcGQtdGhlbWUtZGFyay1oY1wiKTtcblxuICAgIGxldCBjb21wb3VuZFRoZW1lQ2xhc3NOYW1lID0gYGNwZC10aGVtZS1gICsgKHN0eWxlc2hlZXROYW1lLmluY2x1ZGVzKFwibGlnaHRcIikgPyBcImxpZ2h0XCIgOiBcImRhcmtcIik7XG4gICAgLy8gQWx3YXlzIHJlc3BlY3QgdXNlciBPUyBwcmVmZXJlbmNlIVxuICAgIGlmIChpc0hpZ2hDb250cmFzdFRoZW1lKHRoZW1lKSB8fCB3aW5kb3cubWF0Y2hNZWRpYShcIihwcmVmZXJzLWNvbnRyYXN0OiBtb3JlKVwiKS5tYXRjaGVzKSB7XG4gICAgICAgIGNvbXBvdW5kVGhlbWVDbGFzc05hbWUgKz0gXCItaGNcIjtcbiAgICB9XG5cbiAgICBkb2N1bWVudC5ib2R5LmNsYXNzTGlzdC5hZGQoY29tcG91bmRUaGVtZUNsYXNzTmFtZSk7XG5cbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICBjb25zdCBzd2l0Y2hUaGVtZSA9IGZ1bmN0aW9uICgpOiB2b2lkIHtcbiAgICAgICAgICAgIC8vIHdlIHJlLWVuYWJsZSBvdXIgdGhlbWUgaGVyZSBqdXN0IGluIGNhc2Ugd2UgcmFjZWQgd2l0aCBhbm90aGVyXG4gICAgICAgICAgICAvLyB0aGVtZSBzZXQgcmVxdWVzdCBhcyBwZXIgaHR0cHM6Ly9naXRodWIuY29tL3ZlY3Rvci1pbS9lbGVtZW50LXdlYi9pc3N1ZXMvNTYwMS5cbiAgICAgICAgICAgIC8vIFdlIGNvdWxkIGFsdGVybmF0aXZlbHkgbG9jayBvciBzaW1pbGFyIHRvIHN0b3AgdGhlIHJhY2UsIGJ1dFxuICAgICAgICAgICAgLy8gdGhpcyBpcyBwcm9iYWJseSBnb29kIGVub3VnaCBmb3Igbm93LlxuICAgICAgICAgICAgc3R5bGVTaGVldC5kaXNhYmxlZCA9IGZhbHNlO1xuICAgICAgICAgICAgc3R5bGVFbGVtZW50cy5mb3JFYWNoKChhKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKGEgPT0gc3R5bGVTaGVldCkgcmV0dXJuO1xuICAgICAgICAgICAgICAgIGEuZGlzYWJsZWQgPSB0cnVlO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBjb25zdCBib2R5U3R5bGVzID0gZ2xvYmFsLmdldENvbXB1dGVkU3R5bGUoZG9jdW1lbnQuYm9keSk7XG4gICAgICAgICAgICBpZiAoYm9keVN0eWxlcy5iYWNrZ3JvdW5kQ29sb3IpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBtZXRhRWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3I8SFRNTE1ldGFFbGVtZW50PignbWV0YVtuYW1lPVwidGhlbWUtY29sb3JcIl0nKSE7XG4gICAgICAgICAgICAgICAgbWV0YUVsZW1lbnQuY29udGVudCA9IGJvZHlTdHlsZXMuYmFja2dyb3VuZENvbG9yO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgICB9O1xuXG4gICAgICAgIGNvbnN0IGlzU3R5bGVTaGVldExvYWRlZCA9ICgpOiBib29sZWFuID0+XG4gICAgICAgICAgICBCb29sZWFuKFsuLi5kb2N1bWVudC5zdHlsZVNoZWV0c10uZmluZCgoX3N0eWxlU2hlZXQpID0+IF9zdHlsZVNoZWV0Py5ocmVmID09PSBzdHlsZVNoZWV0LmhyZWYpKTtcblxuICAgICAgICBmdW5jdGlvbiB3YWl0Rm9yU3R5bGVTaGVldExvYWRpbmcoKTogdm9pZCB7XG4gICAgICAgICAgICAvLyB0dXJucyBvdXQgdGhhdCBGaXJlZm94IHByZWxvYWRzIHRoZSBDU1MgZm9yIGxpbmsgZWxlbWVudHMgd2l0aFxuICAgICAgICAgICAgLy8gdGhlIGRpc2FibGVkIGF0dHJpYnV0ZSwgYnV0IENocm9tZSBkb2Vzbid0LlxuICAgICAgICAgICAgaWYgKGlzU3R5bGVTaGVldExvYWRlZCgpKSB7XG4gICAgICAgICAgICAgICAgc3dpdGNoVGhlbWUoKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGxldCBjb3VudGVyID0gMDtcblxuICAgICAgICAgICAgLy8gSW4gY2FzZSBvZiB0aGVtZSB0b2dnbGluZyAod2hpdGUgPT4gYmxhY2sgPT4gd2hpdGUpXG4gICAgICAgICAgICAvLyBDaHJvbWUgZG9lc24ndCBmaXJlIHRoZSBgbG9hZGAgZXZlbnQgd2hlbiB0aGUgd2hpdGUgdGhlbWUgaXMgc2VsZWN0ZWQgdGhlIHNlY29uZCB0aW1lc1xuICAgICAgICAgICAgY29uc3QgaW50ZXJ2YWxJZCA9IHdpbmRvdy5zZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKGlzU3R5bGVTaGVldExvYWRlZCgpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwoaW50ZXJ2YWxJZCk7XG4gICAgICAgICAgICAgICAgICAgIHN0eWxlU2hlZXQub25sb2FkID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgc3R5bGVTaGVldC5vbmVycm9yID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgc3dpdGNoVGhlbWUoKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBBdm9pZCB0byBiZSBzdHVjayBpbiBhbiBlbmRsZXNzIGxvb3AgaWYgdGhlcmUgaXMgYW4gaXNzdWUgaW4gdGhlIHN0eWxlc2hlZXQgbG9hZGluZ1xuICAgICAgICAgICAgICAgIGNvdW50ZXIrKztcbiAgICAgICAgICAgICAgICBpZiAoY291bnRlciA9PT0gMTApIHtcbiAgICAgICAgICAgICAgICAgICAgY2xlYXJJbnRlcnZhbChpbnRlcnZhbElkKTtcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSwgMjAwKTtcblxuICAgICAgICAgICAgc3R5bGVTaGVldC5vbmxvYWQgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY2xlYXJJbnRlcnZhbChpbnRlcnZhbElkKTtcbiAgICAgICAgICAgICAgICBzd2l0Y2hUaGVtZSgpO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgc3R5bGVTaGVldC5vbmVycm9yID0gKGUpID0+IHtcbiAgICAgICAgICAgICAgICBjbGVhckludGVydmFsKGludGVydmFsSWQpO1xuICAgICAgICAgICAgICAgIHJlamVjdChlKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH1cblxuICAgICAgICB3YWl0Rm9y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