matrix-react-sdk
Version:
SDK for matrix.org using React
390 lines (373 loc) • 51.2 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MINUTE_MS = exports.HOUR_MS = exports.DAY_MS = void 0;
exports.formatDate = formatDate;
exports.formatDateForInput = formatDateForInput;
exports.formatDuration = formatDuration;
exports.formatFullDate = formatFullDate;
exports.formatFullDateNoDay = formatFullDateNoDay;
exports.formatFullDateNoDayISO = formatFullDateNoDayISO;
exports.formatFullDateNoDayNoTime = formatFullDateNoDayNoTime;
exports.formatFullDateNoTime = formatFullDateNoTime;
exports.formatFullTime = formatFullTime;
exports.formatLocalDateShort = void 0;
exports.formatPreciseDuration = formatPreciseDuration;
exports.formatRelativeTime = formatRelativeTime;
exports.formatSeconds = formatSeconds;
exports.formatTime = formatTime;
exports.formatTimeLeft = formatTimeLeft;
exports.getDaysArray = getDaysArray;
exports.getMonthsArray = getMonthsArray;
exports.wantsDateSeparator = wantsDateSeparator;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _languageHandler = require("./languageHandler");
var _TimezoneHandler = require("./TimezoneHandler");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /*
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
Copyright 2017 Vector Creations Ltd
Copyright 2015, 2016 OpenMarket Ltd
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
const MINUTE_MS = exports.MINUTE_MS = 60000;
const HOUR_MS = exports.HOUR_MS = MINUTE_MS * 60;
const DAY_MS = exports.DAY_MS = HOUR_MS * 24;
/**
* Returns array of 7 weekday names, from Sunday to Saturday, internationalised to the user's language.
* @param weekday - format desired "short" | "long" | "narrow"
*/
function getDaysArray(weekday = "short") {
const sunday = 1672574400000; // 2023-01-01 12:00 UTC
const {
format
} = new Intl.DateTimeFormat((0, _languageHandler.getUserLanguage)(), {
weekday,
timeZone: "UTC"
});
return [...Array(7).keys()].map(day => format(sunday + day * DAY_MS));
}
/**
* Returns array of 12 month names, from January to December, internationalised to the user's language.
* @param month - format desired "numeric" | "2-digit" | "long" | "short" | "narrow"
*/
function getMonthsArray(month = "short") {
const {
format
} = new Intl.DateTimeFormat((0, _languageHandler.getUserLanguage)(), {
month,
timeZone: "UTC"
});
return [...Array(12).keys()].map(m => format(Date.UTC(2021, m)));
}
// XXX: Ideally we could just specify `hour12: boolean` but it has issues on Chrome in the `en` locale
// https://support.google.com/chrome/thread/29828561?hl=en
function getTwelveHourOptions(showTwelveHour) {
return {
hourCycle: showTwelveHour ? "h12" : "h23"
};
}
/**
* Formats a given date to a date & time string.
*
* The output format depends on how far away the given date is from now.
* Will use the browser's default time zone.
* If the date is today it will return a time string excluding seconds. See {@formatTime}.
* If the date is within the last 6 days it will return the name of the weekday along with the time string excluding seconds.
* If the date is within the same year then it will return the weekday, month and day of the month along with the time string excluding seconds.
* Otherwise, it will return a string representing the full date & time in a human friendly manner. See {@formatFullDate}.
* @param date - date object to format
* @param showTwelveHour - whether to use 12-hour rather than 24-hour time. Defaults to `false` (24 hour mode).
* Overrides the default from the locale, whether `true` or `false`.
* @param locale - the locale string to use, in BCP 47 format, defaulting to user's selected application locale
*/
function formatDate(date, showTwelveHour = false, locale) {
const _locale = locale ?? (0, _languageHandler.getUserLanguage)();
const now = new Date();
if (date.toDateString() === now.toDateString()) {
return formatTime(date, showTwelveHour, _locale);
} else if (now.getTime() - date.getTime() < 6 * DAY_MS) {
// Time is within the last 6 days (or in the future)
return new Intl.DateTimeFormat(_locale, _objectSpread(_objectSpread({}, getTwelveHourOptions(showTwelveHour)), {}, {
weekday: "short",
hour: "numeric",
minute: "2-digit",
timeZone: (0, _TimezoneHandler.getUserTimezone)()
})).format(date);
} else if (now.getFullYear() === date.getFullYear()) {
return new Intl.DateTimeFormat(_locale, _objectSpread(_objectSpread({}, getTwelveHourOptions(showTwelveHour)), {}, {
weekday: "short",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: (0, _TimezoneHandler.getUserTimezone)()
})).format(date);
}
return formatFullDate(date, showTwelveHour, false, _locale);
}
/**
* Formats a given date to a human-friendly string with short weekday.
* Will use the browser's default time zone.
* @example "Thu, 17 Nov 2022" in en-GB locale
* @param date - date object to format
* @param locale - the locale string to use, in BCP 47 format, defaulting to user's selected application locale
*/
function formatFullDateNoTime(date, locale) {
return new Intl.DateTimeFormat(locale ?? (0, _languageHandler.getUserLanguage)(), {
weekday: "short",
month: "short",
day: "numeric",
year: "numeric",
timeZone: (0, _TimezoneHandler.getUserTimezone)()
}).format(date);
}
/**
* Formats a given date to a date & time string, optionally including seconds.
* Will use the browser's default time zone.
* @example "Thu, 17 Nov 2022, 4:58:32 pm" in en-GB locale with showTwelveHour=true and showSeconds=true
* @param date - date object to format
* @param showTwelveHour - whether to use 12-hour rather than 24-hour time. Defaults to `false` (24 hour mode).
* Overrides the default from the locale, whether `true` or `false`.
* @param showSeconds - whether to include seconds in the time portion of the string
* @param locale - the locale string to use, in BCP 47 format, defaulting to user's selected application locale
*/
function formatFullDate(date, showTwelveHour = false, showSeconds = true, locale) {
return new Intl.DateTimeFormat(locale ?? (0, _languageHandler.getUserLanguage)(), _objectSpread(_objectSpread({}, getTwelveHourOptions(showTwelveHour)), {}, {
weekday: "short",
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "2-digit",
second: showSeconds ? "2-digit" : undefined,
timeZone: (0, _TimezoneHandler.getUserTimezone)()
})).format(date);
}
/**
* Formats dates to be compatible with attributes of a `<input type="date">`. Dates
* should be formatted like "2020-06-23" (formatted according to ISO8601).
*
* @param date The date to format.
* @returns The date string in ISO8601 format ready to be used with an `<input>`
*/
function formatDateForInput(date) {
const year = `${date.getFullYear()}`.padStart(4, "0");
const month = `${date.getMonth() + 1}`.padStart(2, "0");
const day = `${date.getDate()}`.padStart(2, "0");
return `${year}-${month}-${day}`;
}
/**
* Formats a given date to a time string including seconds.
* Will use the browser's default time zone.
* @example "4:58:32 PM" in en-GB locale with showTwelveHour=true
* @example "16:58:32" in en-GB locale with showTwelveHour=false
* @param date - date object to format
* @param showTwelveHour - whether to use 12-hour rather than 24-hour time. Defaults to `false` (24 hour mode).
* Overrides the default from the locale, whether `true` or `false`.
* @param locale - the locale string to use, in BCP 47 format, defaulting to user's selected application locale
*/
function formatFullTime(date, showTwelveHour = false, locale) {
return new Intl.DateTimeFormat(locale ?? (0, _languageHandler.getUserLanguage)(), _objectSpread(_objectSpread({}, getTwelveHourOptions(showTwelveHour)), {}, {
hour: "numeric",
minute: "2-digit",
second: "2-digit",
timeZone: (0, _TimezoneHandler.getUserTimezone)()
})).format(date);
}
/**
* Formats a given date to a time string excluding seconds.
* Will use the browser's default time zone.
* @example "4:58 PM" in en-GB locale with showTwelveHour=true
* @example "16:58" in en-GB locale with showTwelveHour=false
* @param date - date object to format
* @param showTwelveHour - whether to use 12-hour rather than 24-hour time. Defaults to `false` (24 hour mode).
* Overrides the default from the locale, whether `true` or `false`.
* @param locale - the locale string to use, in BCP 47 format, defaulting to user's selected application locale
*/
function formatTime(date, showTwelveHour = false, locale) {
return new Intl.DateTimeFormat(locale ?? (0, _languageHandler.getUserLanguage)(), _objectSpread(_objectSpread({}, getTwelveHourOptions(showTwelveHour)), {}, {
hour: "numeric",
minute: "2-digit",
timeZone: (0, _TimezoneHandler.getUserTimezone)()
})).format(date);
}
function formatSeconds(inSeconds) {
const isNegative = inSeconds < 0;
inSeconds = Math.abs(inSeconds);
const hours = Math.floor(inSeconds / (60 * 60)).toFixed(0).padStart(2, "0");
const minutes = Math.floor(inSeconds % (60 * 60) / 60).toFixed(0).padStart(2, "0");
const seconds = Math.floor(inSeconds % (60 * 60) % 60).toFixed(0).padStart(2, "0");
let output = "";
if (hours !== "00") output += `${hours}:`;
output += `${minutes}:${seconds}`;
if (isNegative) {
output = "-" + output;
}
return output;
}
function formatTimeLeft(inSeconds) {
const hours = Math.floor(inSeconds / (60 * 60)).toFixed(0);
const minutes = Math.floor(inSeconds % (60 * 60) / 60).toFixed(0);
const seconds = Math.floor(inSeconds % (60 * 60) % 60).toFixed(0);
if (hours !== "0") {
return (0, _languageHandler._t)("time|hours_minutes_seconds_left", {
hours,
minutes,
seconds
});
}
if (minutes !== "0") {
return (0, _languageHandler._t)("time|minutes_seconds_left", {
minutes,
seconds
});
}
return (0, _languageHandler._t)("time|seconds_left", {
seconds
});
}
function withinPast24Hours(prevDate, nextDate) {
return Math.abs(prevDate.getTime() - nextDate.getTime()) <= DAY_MS;
}
function withinCurrentDay(prevDate, nextDate) {
return withinPast24Hours(prevDate, nextDate) && prevDate.getDay() === nextDate.getDay();
}
function withinCurrentYear(prevDate, nextDate) {
return prevDate.getFullYear() === nextDate.getFullYear();
}
function wantsDateSeparator(prevEventDate, nextEventDate) {
if (!nextEventDate || !prevEventDate) {
return false;
}
// Return early for events that are > 24h apart
if (!withinPast24Hours(prevEventDate, nextEventDate)) {
return true;
}
// Compare weekdays
return prevEventDate.getDay() !== nextEventDate.getDay();
}
function formatFullDateNoDay(date) {
const locale = (0, _languageHandler.getUserLanguage)();
return (0, _languageHandler._t)("time|date_at_time", {
date: date.toLocaleDateString(locale).replace(/\//g, "-"),
time: date.toLocaleTimeString(locale).replace(/:/g, "-")
});
}
/**
* Returns an ISO date string without textual description of the date (ie: no "Wednesday" or similar)
* @param date The date to format.
* @returns The date string in ISO format.
*/
function formatFullDateNoDayISO(date) {
return date.toISOString();
}
/**
* Formats a given date to a string.
* Will use the browser's default time zone.
* @example 17/11/2022 in en-GB locale
* @param date - date object to format
* @param locale - the locale string to use, in BCP 47 format, defaulting to user's selected application locale
*/
function formatFullDateNoDayNoTime(date, locale) {
return new Intl.DateTimeFormat(locale ?? (0, _languageHandler.getUserLanguage)(), {
year: "numeric",
month: "numeric",
day: "numeric",
timeZone: (0, _TimezoneHandler.getUserTimezone)()
}).format(date);
}
function formatRelativeTime(date, showTwelveHour = false) {
const now = new Date();
if (withinCurrentDay(date, now)) {
return formatTime(date, showTwelveHour);
} else {
const months = getMonthsArray();
let relativeDate = `${months[date.getMonth()]} ${date.getDate()}`;
if (!withinCurrentYear(date, now)) {
relativeDate += `, ${date.getFullYear()}`;
}
return relativeDate;
}
}
/**
* Formats duration in ms to human-readable string
* Returns value in the biggest possible unit (day, hour, min, second)
* Rounds values up until unit threshold
* i.e. 23:13:57 -> 23h, 24:13:57 -> 1d, 44:56:56 -> 2d
*/
function formatDuration(durationMs) {
if (durationMs >= DAY_MS) {
return (0, _languageHandler._t)("time|short_days", {
value: Math.round(durationMs / DAY_MS)
});
}
if (durationMs >= HOUR_MS) {
return (0, _languageHandler._t)("time|short_hours", {
value: Math.round(durationMs / HOUR_MS)
});
}
if (durationMs >= MINUTE_MS) {
return (0, _languageHandler._t)("time|short_minutes", {
value: Math.round(durationMs / MINUTE_MS)
});
}
return (0, _languageHandler._t)("time|short_seconds", {
value: Math.round(durationMs / 1000)
});
}
/**
* Formats duration in ms to human-readable string
* Returns precise value down to the nearest second
* i.e. 23:13:57 -> 23h 13m 57s, 44:56:56 -> 1d 20h 56m 56s
*/
function formatPreciseDuration(durationMs) {
const days = Math.floor(durationMs / DAY_MS);
const hours = Math.floor(durationMs % DAY_MS / HOUR_MS);
const minutes = Math.floor(durationMs % HOUR_MS / MINUTE_MS);
const seconds = Math.floor(durationMs % MINUTE_MS / 1000);
if (days > 0) {
return (0, _languageHandler._t)("time|short_days_hours_minutes_seconds", {
days,
hours,
minutes,
seconds
});
}
if (hours > 0) {
return (0, _languageHandler._t)("time|short_hours_minutes_seconds", {
hours,
minutes,
seconds
});
}
if (minutes > 0) {
return (0, _languageHandler._t)("time|short_minutes_seconds", {
minutes,
seconds
});
}
return (0, _languageHandler._t)("time|short_seconds", {
value: seconds
});
}
/**
* Formats a timestamp to a short date
* Similar to {@formatFullDateNoDayNoTime} but with 2-digit on day, month, year.
* @example 25/12/22 in en-GB locale
* @param timestamp - epoch timestamp
* @param locale - the locale string to use, in BCP 47 format, defaulting to user's selected application locale
* @returns {string} formattedDate
*/
const formatLocalDateShort = (timestamp, locale) => new Intl.DateTimeFormat(locale ?? (0, _languageHandler.getUserLanguage)(), {
day: "2-digit",
month: "2-digit",
year: "2-digit",
timeZone: (0, _TimezoneHandler.getUserTimezone)()
}).format(timestamp);
exports.formatLocalDateShort = formatLocalDateShort;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbGFuZ3VhZ2VIYW5kbGVyIiwicmVxdWlyZSIsIl9UaW1lem9uZUhhbmRsZXIiLCJvd25LZXlzIiwiZSIsInIiLCJ0IiwiT2JqZWN0Iiwia2V5cyIsImdldE93blByb3BlcnR5U3ltYm9scyIsIm8iLCJmaWx0ZXIiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJlbnVtZXJhYmxlIiwicHVzaCIsImFwcGx5IiwiX29iamVjdFNwcmVhZCIsImFyZ3VtZW50cyIsImxlbmd0aCIsImZvckVhY2giLCJfZGVmaW5lUHJvcGVydHkyIiwiZGVmYXVsdCIsImdldE93blByb3BlcnR5RGVzY3JpcHRvcnMiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiZGVmaW5lUHJvcGVydHkiLCJNSU5VVEVfTVMiLCJleHBvcnRzIiwiSE9VUl9NUyIsIkRBWV9NUyIsImdldERheXNBcnJheSIsIndlZWtkYXkiLCJzdW5kYXkiLCJmb3JtYXQiLCJJbnRsIiwiRGF0ZVRpbWVGb3JtYXQiLCJnZXRVc2VyTGFuZ3VhZ2UiLCJ0aW1lWm9uZSIsIkFycmF5IiwibWFwIiwiZGF5IiwiZ2V0TW9udGhzQXJyYXkiLCJtb250aCIsIm0iLCJEYXRlIiwiVVRDIiwiZ2V0VHdlbHZlSG91ck9wdGlvbnMiLCJzaG93VHdlbHZlSG91ciIsImhvdXJDeWNsZSIsImZvcm1hdERhdGUiLCJkYXRlIiwibG9jYWxlIiwiX2xvY2FsZSIsIm5vdyIsInRvRGF0ZVN0cmluZyIsImZvcm1hdFRpbWUiLCJnZXRUaW1lIiwiaG91ciIsIm1pbnV0ZSIsImdldFVzZXJUaW1lem9uZSIsImdldEZ1bGxZZWFyIiwiZm9ybWF0RnVsbERhdGUiLCJmb3JtYXRGdWxsRGF0ZU5vVGltZSIsInllYXIiLCJzaG93U2Vjb25kcyIsInNlY29uZCIsInVuZGVmaW5lZCIsImZvcm1hdERhdGVGb3JJbnB1dCIsInBhZFN0YXJ0IiwiZ2V0TW9udGgiLCJnZXREYXRlIiwiZm9ybWF0RnVsbFRpbWUiLCJmb3JtYXRTZWNvbmRzIiwiaW5TZWNvbmRzIiwiaXNOZWdhdGl2ZSIsIk1hdGgiLCJhYnMiLCJob3VycyIsImZsb29yIiwidG9GaXhlZCIsIm1pbnV0ZXMiLCJzZWNvbmRzIiwib3V0cHV0IiwiZm9ybWF0VGltZUxlZnQiLCJfdCIsIndpdGhpblBhc3QyNEhvdXJzIiwicHJldkRhdGUiLCJuZXh0RGF0ZSIsIndpdGhpbkN1cnJlbnREYXkiLCJnZXREYXkiLCJ3aXRoaW5DdXJyZW50WWVhciIsIndhbnRzRGF0ZVNlcGFyYXRvciIsInByZXZFdmVudERhdGUiLCJuZXh0RXZlbnREYXRlIiwiZm9ybWF0RnVsbERhdGVOb0RheSIsInRvTG9jYWxlRGF0ZVN0cmluZyIsInJlcGxhY2UiLCJ0aW1lIiwidG9Mb2NhbGVUaW1lU3RyaW5nIiwiZm9ybWF0RnVsbERhdGVOb0RheUlTTyIsInRvSVNPU3RyaW5nIiwiZm9ybWF0RnVsbERhdGVOb0RheU5vVGltZSIsImZvcm1hdFJlbGF0aXZlVGltZSIsIm1vbnRocyIsInJlbGF0aXZlRGF0ZSIsImZvcm1hdER1cmF0aW9uIiwiZHVyYXRpb25NcyIsInZhbHVlIiwicm91bmQiLCJmb3JtYXRQcmVjaXNlRHVyYXRpb24iLCJkYXlzIiwiZm9ybWF0TG9jYWxEYXRlU2hvcnQiLCJ0aW1lc3RhbXAiXSwic291cmNlcyI6WyIuLi9zcmMvRGF0ZVV0aWxzLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qXG5Db3B5cmlnaHQgMjAyNCBOZXcgVmVjdG9yIEx0ZC5cbkNvcHlyaWdodCAyMDIyIFRoZSBNYXRyaXgub3JnIEZvdW5kYXRpb24gQy5JLkMuXG5Db3B5cmlnaHQgMjAxNyBWZWN0b3IgQ3JlYXRpb25zIEx0ZFxuQ29weXJpZ2h0IDIwMTUsIDIwMTYgT3Blbk1hcmtldCBMdGRcblxuU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFHUEwtMy4wLW9ubHkgT1IgR1BMLTMuMC1vbmx5XG5QbGVhc2Ugc2VlIExJQ0VOU0UgZmlsZXMgaW4gdGhlIHJlcG9zaXRvcnkgcm9vdCBmb3IgZnVsbCBkZXRhaWxzLlxuKi9cblxuaW1wb3J0IHsgT3B0aW9uYWwgfSBmcm9tIFwibWF0cml4LWV2ZW50cy1zZGtcIjtcblxuaW1wb3J0IHsgX3QsIGdldFVzZXJMYW5ndWFnZSB9IGZyb20gXCIuL2xhbmd1YWdlSGFuZGxlclwiO1xuaW1wb3J0IHsgZ2V0VXNlclRpbWV6b25lIH0gZnJvbSBcIi4vVGltZXpvbmVIYW5kbGVyXCI7XG5cbmV4cG9ydCBjb25zdCBNSU5VVEVfTVMgPSA2MDAwMDtcbmV4cG9ydCBjb25zdCBIT1VSX01TID0gTUlOVVRFX01TICogNjA7XG5leHBvcnQgY29uc3QgREFZX01TID0gSE9VUl9NUyAqIDI0O1xuXG4vKipcbiAqIFJldHVybnMgYXJyYXkgb2YgNyB3ZWVrZGF5IG5hbWVzLCBmcm9tIFN1bmRheSB0byBTYXR1cmRheSwgaW50ZXJuYXRpb25hbGlzZWQgdG8gdGhlIHVzZXIncyBsYW5ndWFnZS5cbiAqIEBwYXJhbSB3ZWVrZGF5IC0gZm9ybWF0IGRlc2lyZWQgXCJzaG9ydFwiIHwgXCJsb25nXCIgfCBcIm5hcnJvd1wiXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXREYXlzQXJyYXkod2Vla2RheTogSW50bC5EYXRlVGltZUZvcm1hdE9wdGlvbnNbXCJ3ZWVrZGF5XCJdID0gXCJzaG9ydFwiKTogc3RyaW5nW10ge1xuICAgIGNvbnN0IHN1bmRheSA9IDE2NzI1NzQ0MDAwMDA7IC8vIDIwMjMtMDEtMDEgMTI6MDAgVVRDXG4gICAgY29uc3QgeyBmb3JtYXQgfSA9IG5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KGdldFVzZXJMYW5ndWFnZSgpLCB7IHdlZWtkYXksIHRpbWVab25lOiBcIlVUQ1wiIH0pO1xuICAgIHJldHVybiBbLi4uQXJyYXkoNykua2V5cygpXS5tYXAoKGRheSkgPT4gZm9ybWF0KHN1bmRheSArIGRheSAqIERBWV9NUykpO1xufVxuXG4vKipcbiAqIFJldHVybnMgYXJyYXkgb2YgMTIgbW9udGggbmFtZXMsIGZyb20gSmFudWFyeSB0byBEZWNlbWJlciwgaW50ZXJuYXRpb25hbGlzZWQgdG8gdGhlIHVzZXIncyBsYW5ndWFnZS5cbiAqIEBwYXJhbSBtb250aCAtIGZvcm1hdCBkZXNpcmVkIFwibnVtZXJpY1wiIHwgXCIyLWRpZ2l0XCIgfCBcImxvbmdcIiB8IFwic2hvcnRcIiB8IFwibmFycm93XCJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldE1vbnRoc0FycmF5KG1vbnRoOiBJbnRsLkRhdGVUaW1lRm9ybWF0T3B0aW9uc1tcIm1vbnRoXCJdID0gXCJzaG9ydFwiKTogc3RyaW5nW10ge1xuICAgIGNvbnN0IHsgZm9ybWF0IH0gPSBuZXcgSW50bC5EYXRlVGltZUZvcm1hdChnZXRVc2VyTGFuZ3VhZ2UoKSwgeyBtb250aCwgdGltZVpvbmU6IFwiVVRDXCIgfSk7XG4gICAgcmV0dXJuIFsuLi5BcnJheSgxMikua2V5cygpXS5tYXAoKG0pID0+IGZvcm1hdChEYXRlLlVUQygyMDIxLCBtKSkpO1xufVxuXG4vLyBYWFg6IElkZWFsbHkgd2UgY291bGQganVzdCBzcGVjaWZ5IGBob3VyMTI6IGJvb2xlYW5gIGJ1dCBpdCBoYXMgaXNzdWVzIG9uIENocm9tZSBpbiB0aGUgYGVuYCBsb2NhbGVcbi8vIGh0dHBzOi8vc3VwcG9ydC5nb29nbGUuY29tL2Nocm9tZS90aHJlYWQvMjk4Mjg1NjE/aGw9ZW5cbmZ1bmN0aW9uIGdldFR3ZWx2ZUhvdXJPcHRpb25zKHNob3dUd2VsdmVIb3VyOiBib29sZWFuKTogSW50bC5EYXRlVGltZUZvcm1hdE9wdGlvbnMge1xuICAgIHJldHVybiB7XG4gICAgICAgIGhvdXJDeWNsZTogc2hvd1R3ZWx2ZUhvdXIgPyBcImgxMlwiIDogXCJoMjNcIixcbiAgICB9O1xufVxuXG4vKipcbiAqIEZvcm1hdHMgYSBnaXZlbiBkYXRlIHRvIGEgZGF0ZSAmIHRpbWUgc3RyaW5nLlxuICpcbiAqIFRoZSBvdXRwdXQgZm9ybWF0IGRlcGVuZHMgb24gaG93IGZhciBhd2F5IHRoZSBnaXZlbiBkYXRlIGlzIGZyb20gbm93LlxuICogV2lsbCB1c2UgdGhlIGJyb3dzZXIncyBkZWZhdWx0IHRpbWUgem9uZS5cbiAqIElmIHRoZSBkYXRlIGlzIHRvZGF5IGl0IHdpbGwgcmV0dXJuIGEgdGltZSBzdHJpbmcgZXhjbHVkaW5nIHNlY29uZHMuIFNlZSB7QGZvcm1hdFRpbWV9LlxuICogSWYgdGhlIGRhdGUgaXMgd2l0aGluIHRoZSBsYXN0IDYgZGF5cyBpdCB3aWxsIHJldHVybiB0aGUgbmFtZSBvZiB0aGUgd2Vla2RheSBhbG9uZyB3aXRoIHRoZSB0aW1lIHN0cmluZyBleGNsdWRpbmcgc2Vjb25kcy5cbiAqIElmIHRoZSBkYXRlIGlzIHdpdGhpbiB0aGUgc2FtZSB5ZWFyIHRoZW4gaXQgd2lsbCByZXR1cm4gdGhlIHdlZWtkYXksIG1vbnRoIGFuZCBkYXkgb2YgdGhlIG1vbnRoIGFsb25nIHdpdGggdGhlIHRpbWUgc3RyaW5nIGV4Y2x1ZGluZyBzZWNvbmRzLlxuICogT3RoZXJ3aXNlLCBpdCB3aWxsIHJldHVybiBhIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIGZ1bGwgZGF0ZSAmIHRpbWUgaW4gYSBodW1hbiBmcmllbmRseSBtYW5uZXIuIFNlZSB7QGZvcm1hdEZ1bGxEYXRlfS5cbiAqIEBwYXJhbSBkYXRlIC0gZGF0ZSBvYmplY3QgdG8gZm9ybWF0XG4gKiBAcGFyYW0gc2hvd1R3ZWx2ZUhvdXIgLSB3aGV0aGVyIHRvIHVzZSAxMi1ob3VyIHJhdGhlciB0aGFuIDI0LWhvdXIgdGltZS4gRGVmYXVsdHMgdG8gYGZhbHNlYCAoMjQgaG91ciBtb2RlKS5cbiAqICAgICAgICBPdmVycmlkZXMgdGhlIGRlZmF1bHQgZnJvbSB0aGUgbG9jYWxlLCB3aGV0aGVyIGB0cnVlYCBvciBgZmFsc2VgLlxuICogQHBhcmFtIGxvY2FsZSAtIHRoZSBsb2NhbGUgc3RyaW5nIHRvIHVzZSwgaW4gQkNQIDQ3IGZvcm1hdCwgZGVmYXVsdGluZyB0byB1c2VyJ3Mgc2VsZWN0ZWQgYXBwbGljYXRpb24gbG9jYWxlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXREYXRlKGRhdGU6IERhdGUsIHNob3dUd2VsdmVIb3VyID0gZmFsc2UsIGxvY2FsZT86IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgX2xvY2FsZSA9IGxvY2FsZSA/PyBnZXRVc2VyTGFuZ3VhZ2UoKTtcbiAgICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpO1xuICAgIGlmIChkYXRlLnRvRGF0ZVN0cmluZygpID09PSBub3cudG9EYXRlU3RyaW5nKCkpIHtcbiAgICAgICAgcmV0dXJuIGZvcm1hdFRpbWUoZGF0ZSwgc2hvd1R3ZWx2ZUhvdXIsIF9sb2NhbGUpO1xuICAgIH0gZWxzZSBpZiAobm93LmdldFRpbWUoKSAtIGRhdGUuZ2V0VGltZSgpIDwgNiAqIERBWV9NUykge1xuICAgICAgICAvLyBUaW1lIGlzIHdpdGhpbiB0aGUgbGFzdCA2IGRheXMgKG9yIGluIHRoZSBmdXR1cmUpXG4gICAgICAgIHJldHVybiBuZXcgSW50bC5EYXRlVGltZUZvcm1hdChfbG9jYWxlLCB7XG4gICAgICAgICAgICAuLi5nZXRUd2VsdmVIb3VyT3B0aW9ucyhzaG93VHdlbHZlSG91ciksXG4gICAgICAgICAgICB3ZWVrZGF5OiBcInNob3J0XCIsXG4gICAgICAgICAgICBob3VyOiBcIm51bWVyaWNcIixcbiAgICAgICAgICAgIG1pbnV0ZTogXCIyLWRpZ2l0XCIsXG4gICAgICAgICAgICB0aW1lWm9uZTogZ2V0VXNlclRpbWV6b25lKCksXG4gICAgICAgIH0pLmZvcm1hdChkYXRlKTtcbiAgICB9IGVsc2UgaWYgKG5vdy5nZXRGdWxsWWVhcigpID09PSBkYXRlLmdldEZ1bGxZZWFyKCkpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KF9sb2NhbGUsIHtcbiAgICAgICAgICAgIC4uLmdldFR3ZWx2ZUhvdXJPcHRpb25zKHNob3dUd2VsdmVIb3VyKSxcbiAgICAgICAgICAgIHdlZWtkYXk6IFwic2hvcnRcIixcbiAgICAgICAgICAgIG1vbnRoOiBcInNob3J0XCIsXG4gICAgICAgICAgICBkYXk6IFwibnVtZXJpY1wiLFxuICAgICAgICAgICAgaG91cjogXCJudW1lcmljXCIsXG4gICAgICAgICAgICBtaW51dGU6IFwiMi1kaWdpdFwiLFxuICAgICAgICAgICAgdGltZVpvbmU6IGdldFVzZXJUaW1lem9uZSgpLFxuICAgICAgICB9KS5mb3JtYXQoZGF0ZSk7XG4gICAgfVxuICAgIHJldHVybiBmb3JtYXRGdWxsRGF0ZShkYXRlLCBzaG93VHdlbHZlSG91ciwgZmFsc2UsIF9sb2NhbGUpO1xufVxuXG4vKipcbiAqIEZvcm1hdHMgYSBnaXZlbiBkYXRlIHRvIGEgaHVtYW4tZnJpZW5kbHkgc3RyaW5nIHdpdGggc2hvcnQgd2Vla2RheS5cbiAqIFdpbGwgdXNlIHRoZSBicm93c2VyJ3MgZGVmYXVsdCB0aW1lIHpvbmUuXG4gKiBAZXhhbXBsZSBcIlRodSwgMTcgTm92IDIwMjJcIiBpbiBlbi1HQiBsb2NhbGVcbiAqIEBwYXJhbSBkYXRlIC0gZGF0ZSBvYmplY3QgdG8gZm9ybWF0XG4gKiBAcGFyYW0gbG9jYWxlIC0gdGhlIGxvY2FsZSBzdHJpbmcgdG8gdXNlLCBpbiBCQ1AgNDcgZm9ybWF0LCBkZWZhdWx0aW5nIHRvIHVzZXIncyBzZWxlY3RlZCBhcHBsaWNhdGlvbiBsb2NhbGVcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEZ1bGxEYXRlTm9UaW1lKGRhdGU6IERhdGUsIGxvY2FsZT86IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIG5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KGxvY2FsZSA/PyBnZXRVc2VyTGFuZ3VhZ2UoKSwge1xuICAgICAgICB3ZWVrZGF5OiBcInNob3J0XCIsXG4gICAgICAgIG1vbnRoOiBcInNob3J0XCIsXG4gICAgICAgIGRheTogXCJudW1lcmljXCIsXG4gICAgICAgIHllYXI6IFwibnVtZXJpY1wiLFxuICAgICAgICB0aW1lWm9uZTogZ2V0VXNlclRpbWV6b25lKCksXG4gICAgfSkuZm9ybWF0KGRhdGUpO1xufVxuXG4vKipcbiAqIEZvcm1hdHMgYSBnaXZlbiBkYXRlIHRvIGEgZGF0ZSAmIHRpbWUgc3RyaW5nLCBvcHRpb25hbGx5IGluY2x1ZGluZyBzZWNvbmRzLlxuICogV2lsbCB1c2UgdGhlIGJyb3dzZXIncyBkZWZhdWx0IHRpbWUgem9uZS5cbiAqIEBleGFtcGxlIFwiVGh1LCAxNyBOb3YgMjAyMiwgNDo1ODozMiBwbVwiIGluIGVuLUdCIGxvY2FsZSB3aXRoIHNob3dUd2VsdmVIb3VyPXRydWUgYW5kIHNob3dTZWNvbmRzPXRydWVcbiAqIEBwYXJhbSBkYXRlIC0gZGF0ZSBvYmplY3QgdG8gZm9ybWF0XG4gKiBAcGFyYW0gc2hvd1R3ZWx2ZUhvdXIgLSB3aGV0aGVyIHRvIHVzZSAxMi1ob3VyIHJhdGhlciB0aGFuIDI0LWhvdXIgdGltZS4gRGVmYXVsdHMgdG8gYGZhbHNlYCAoMjQgaG91ciBtb2RlKS5cbiAqICAgICAgICBPdmVycmlkZXMgdGhlIGRlZmF1bHQgZnJvbSB0aGUgbG9jYWxlLCB3aGV0aGVyIGB0cnVlYCBvciBgZmFsc2VgLlxuICogQHBhcmFtIHNob3dTZWNvbmRzIC0gd2hldGhlciB0byBpbmNsdWRlIHNlY29uZHMgaW4gdGhlIHRpbWUgcG9ydGlvbiBvZiB0aGUgc3RyaW5nXG4gKiBAcGFyYW0gbG9jYWxlIC0gdGhlIGxvY2FsZSBzdHJpbmcgdG8gdXNlLCBpbiBCQ1AgNDcgZm9ybWF0LCBkZWZhdWx0aW5nIHRvIHVzZXIncyBzZWxlY3RlZCBhcHBsaWNhdGlvbiBsb2NhbGVcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEZ1bGxEYXRlKGRhdGU6IERhdGUsIHNob3dUd2VsdmVIb3VyID0gZmFsc2UsIHNob3dTZWNvbmRzID0gdHJ1ZSwgbG9jYWxlPzogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gbmV3IEludGwuRGF0ZVRpbWVGb3JtYXQobG9jYWxlID8/IGdldFVzZXJMYW5ndWFnZSgpLCB7XG4gICAgICAgIC4uLmdldFR3ZWx2ZUhvdXJPcHRpb25zKHNob3dUd2VsdmVIb3VyKSxcbiAgICAgICAgd2Vla2RheTogXCJzaG9ydFwiLFxuICAgICAgICBtb250aDogXCJzaG9ydFwiLFxuICAgICAgICBkYXk6IFwibnVtZXJpY1wiLFxuICAgICAgICB5ZWFyOiBcIm51bWVyaWNcIixcbiAgICAgICAgaG91cjogXCJudW1lcmljXCIsXG4gICAgICAgIG1pbnV0ZTogXCIyLWRpZ2l0XCIsXG4gICAgICAgIHNlY29uZDogc2hvd1NlY29uZHMgPyBcIjItZGlnaXRcIiA6IHVuZGVmaW5lZCxcbiAgICAgICAgdGltZVpvbmU6IGdldFVzZXJUaW1lem9uZSgpLFxuICAgIH0pLmZvcm1hdChkYXRlKTtcbn1cblxuLyoqXG4gKiBGb3JtYXRzIGRhdGVzIHRvIGJlIGNvbXBhdGlibGUgd2l0aCBhdHRyaWJ1dGVzIG9mIGEgYDxpbnB1dCB0eXBlPVwiZGF0ZVwiPmAuIERhdGVzXG4gKiBzaG91bGQgYmUgZm9ybWF0dGVkIGxpa2UgXCIyMDIwLTA2LTIzXCIgKGZvcm1hdHRlZCBhY2NvcmRpbmcgdG8gSVNPODYwMSkuXG4gKlxuICogQHBhcmFtIGRhdGUgVGhlIGRhdGUgdG8gZm9ybWF0LlxuICogQHJldHVybnMgVGhlIGRhdGUgc3RyaW5nIGluIElTTzg2MDEgZm9ybWF0IHJlYWR5IHRvIGJlIHVzZWQgd2l0aCBhbiBgPGlucHV0PmBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdERhdGVGb3JJbnB1dChkYXRlOiBEYXRlKTogc3RyaW5nIHtcbiAgICBjb25zdCB5ZWFyID0gYCR7ZGF0ZS5nZXRGdWxsWWVhcigpfWAucGFkU3RhcnQoNCwgXCIwXCIpO1xuICAgIGNvbnN0IG1vbnRoID0gYCR7ZGF0ZS5nZXRNb250aCgpICsgMX1gLnBhZFN0YXJ0KDIsIFwiMFwiKTtcbiAgICBjb25zdCBkYXkgPSBgJHtkYXRlLmdldERhdGUoKX1gLnBhZFN0YXJ0KDIsIFwiMFwiKTtcbiAgICByZXR1cm4gYCR7eWVhcn0tJHttb250aH0tJHtkYXl9YDtcbn1cblxuLyoqXG4gKiBGb3JtYXRzIGEgZ2l2ZW4gZGF0ZSB0byBhIHRpbWUgc3RyaW5nIGluY2x1ZGluZyBzZWNvbmRzLlxuICogV2lsbCB1c2UgdGhlIGJyb3dzZXIncyBkZWZhdWx0IHRpbWUgem9uZS5cbiAqIEBleGFtcGxlIFwiNDo1ODozMiBQTVwiIGluIGVuLUdCIGxvY2FsZSB3aXRoIHNob3dUd2VsdmVIb3VyPXRydWVcbiAqIEBleGFtcGxlIFwiMTY6NTg6MzJcIiBpbiBlbi1HQiBsb2NhbGUgd2l0aCBzaG93VHdlbHZlSG91cj1mYWxzZVxuICogQHBhcmFtIGRhdGUgLSBkYXRlIG9iamVjdCB0byBmb3JtYXRcbiAqIEBwYXJhbSBzaG93VHdlbHZlSG91ciAtIHdoZXRoZXIgdG8gdXNlIDEyLWhvdXIgcmF0aGVyIHRoYW4gMjQtaG91ciB0aW1lLiBEZWZhdWx0cyB0byBgZmFsc2VgICgyNCBob3VyIG1vZGUpLlxuICogICAgICAgIE92ZXJyaWRlcyB0aGUgZGVmYXVsdCBmcm9tIHRoZSBsb2NhbGUsIHdoZXRoZXIgYHRydWVgIG9yIGBmYWxzZWAuXG4gKiBAcGFyYW0gbG9jYWxlIC0gdGhlIGxvY2FsZSBzdHJpbmcgdG8gdXNlLCBpbiBCQ1AgNDcgZm9ybWF0LCBkZWZhdWx0aW5nIHRvIHVzZXIncyBzZWxlY3RlZCBhcHBsaWNhdGlvbiBsb2NhbGVcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEZ1bGxUaW1lKGRhdGU6IERhdGUsIHNob3dUd2VsdmVIb3VyID0gZmFsc2UsIGxvY2FsZT86IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIG5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KGxvY2FsZSA/PyBnZXRVc2VyTGFuZ3VhZ2UoKSwge1xuICAgICAgICAuLi5nZXRUd2VsdmVIb3VyT3B0aW9ucyhzaG93VHdlbHZlSG91ciksXG4gICAgICAgIGhvdXI6IFwibnVtZXJpY1wiLFxuICAgICAgICBtaW51dGU6IFwiMi1kaWdpdFwiLFxuICAgICAgICBzZWNvbmQ6IFwiMi1kaWdpdFwiLFxuICAgICAgICB0aW1lWm9uZTogZ2V0VXNlclRpbWV6b25lKCksXG4gICAgfSkuZm9ybWF0KGRhdGUpO1xufVxuXG4vKipcbiAqIEZvcm1hdHMgYSBnaXZlbiBkYXRlIHRvIGEgdGltZSBzdHJpbmcgZXhjbHVkaW5nIHNlY29uZHMuXG4gKiBXaWxsIHVzZSB0aGUgYnJvd3NlcidzIGRlZmF1bHQgdGltZSB6b25lLlxuICogQGV4YW1wbGUgXCI0OjU4IFBNXCIgaW4gZW4tR0IgbG9jYWxlIHdpdGggc2hvd1R3ZWx2ZUhvdXI9dHJ1ZVxuICogQGV4YW1wbGUgXCIxNjo1OFwiIGluIGVuLUdCIGxvY2FsZSB3aXRoIHNob3dUd2VsdmVIb3VyPWZhbHNlXG4gKiBAcGFyYW0gZGF0ZSAtIGRhdGUgb2JqZWN0IHRvIGZvcm1hdFxuICogQHBhcmFtIHNob3dUd2VsdmVIb3VyIC0gd2hldGhlciB0byB1c2UgMTItaG91ciByYXRoZXIgdGhhbiAyNC1ob3VyIHRpbWUuIERlZmF1bHRzIHRvIGBmYWxzZWAgKDI0IGhvdXIgbW9kZSkuXG4gKiAgICAgICAgT3ZlcnJpZGVzIHRoZSBkZWZhdWx0IGZyb20gdGhlIGxvY2FsZSwgd2hldGhlciBgdHJ1ZWAgb3IgYGZhbHNlYC5cbiAqIEBwYXJhbSBsb2NhbGUgLSB0aGUgbG9jYWxlIHN0cmluZyB0byB1c2UsIGluIEJDUCA0NyBmb3JtYXQsIGRlZmF1bHRpbmcgdG8gdXNlcidzIHNlbGVjdGVkIGFwcGxpY2F0aW9uIGxvY2FsZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0VGltZShkYXRlOiBEYXRlLCBzaG93VHdlbHZlSG91ciA9IGZhbHNlLCBsb2NhbGU/OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBuZXcgSW50bC5EYXRlVGltZUZvcm1hdChsb2NhbGUgPz8gZ2V0VXNlckxhbmd1YWdlKCksIHtcbiAgICAgICAgLi4uZ2V0VHdlbHZlSG91ck9wdGlvbnMoc2hvd1R3ZWx2ZUhvdXIpLFxuICAgICAgICBob3VyOiBcIm51bWVyaWNcIixcbiAgICAgICAgbWludXRlOiBcIjItZGlnaXRcIixcbiAgICAgICAgdGltZVpvbmU6IGdldFVzZXJUaW1lem9uZSgpLFxuICAgIH0pLmZvcm1hdChkYXRlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFNlY29uZHMoaW5TZWNvbmRzOiBudW1iZXIpOiBzdHJpbmcge1xuICAgIGNvbnN0IGlzTmVnYXRpdmUgPSBpblNlY29uZHMgPCAwO1xuICAgIGluU2Vjb25kcyA9IE1hdGguYWJzKGluU2Vjb25kcyk7XG5cbiAgICBjb25zdCBob3VycyA9IE1hdGguZmxvb3IoaW5TZWNvbmRzIC8gKDYwICogNjApKVxuICAgICAgICAudG9GaXhlZCgwKVxuICAgICAgICAucGFkU3RhcnQoMiwgXCIwXCIpO1xuICAgIGNvbnN0IG1pbnV0ZXMgPSBNYXRoLmZsb29yKChpblNlY29uZHMgJSAoNjAgKiA2MCkpIC8gNjApXG4gICAgICAgIC50b0ZpeGVkKDApXG4gICAgICAgIC5wYWRTdGFydCgyLCBcIjBcIik7XG4gICAgY29uc3Qgc2Vjb25kcyA9IE1hdGguZmxvb3IoKGluU2Vjb25kcyAlICg2MCAqIDYwKSkgJSA2MClcbiAgICAgICAgLnRvRml4ZWQoMClcbiAgICAgICAgLnBhZFN0YXJ0KDIsIFwiMFwiKTtcblxuICAgIGxldCBvdXRwdXQgPSBcIlwiO1xuICAgIGlmIChob3VycyAhPT0gXCIwMFwiKSBvdXRwdXQgKz0gYCR7aG91cnN9OmA7XG4gICAgb3V0cHV0ICs9IGAke21pbnV0ZXN9OiR7c2Vjb25kc31gO1xuXG4gICAgaWYgKGlzTmVnYXRpdmUpIHtcbiAgICAgICAgb3V0cHV0ID0gXCItXCIgKyBvdXRwdXQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIG91dHB1dDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFRpbWVMZWZ0KGluU2Vjb25kczogbnVtYmVyKTogc3RyaW5nIHtcbiAgICBjb25zdCBob3VycyA9IE1hdGguZmxvb3IoaW5TZWNvbmRzIC8gKDYwICogNjApKS50b0ZpeGVkKDApO1xuICAgIGNvbnN0IG1pbnV0ZXMgPSBNYXRoLmZsb29yKChpblNlY29uZHMgJSAoNjAgKiA2MCkpIC8gNjApLnRvRml4ZWQoMCk7XG4gICAgY29uc3Qgc2Vjb25kcyA9IE1hdGguZmxvb3IoKGluU2Vjb25kcyAlICg2MCAqIDYwKSkgJSA2MCkudG9GaXhlZCgwKTtcblxuICAgIGlmIChob3VycyAhPT0gXCIwXCIpIHtcbiAgICAgICAgcmV0dXJuIF90KFwidGltZXxob3Vyc19taW51dGVzX3NlY29uZHNfbGVmdFwiLCB7XG4gICAgICAgICAgICBob3VycyxcbiAgICAgICAgICAgIG1pbnV0ZXMsXG4gICAgICAgICAgICBzZWNvbmRzLFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBpZiAobWludXRlcyAhPT0gXCIwXCIpIHtcbiAgICAgICAgcmV0dXJuIF90KFwidGltZXxtaW51dGVzX3NlY29uZHNfbGVmdFwiLCB7XG4gICAgICAgICAgICBtaW51dGVzLFxuICAgICAgICAgICAgc2Vjb25kcyxcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIF90KFwidGltZXxzZWNvbmRzX2xlZnRcIiwge1xuICAgICAgICBzZWNvbmRzLFxuICAgIH0pO1xufVxuXG5mdW5jdGlvbiB3aXRoaW5QYXN0MjRIb3VycyhwcmV2RGF0ZTogRGF0ZSwgbmV4dERhdGU6IERhdGUpOiBib29sZWFuIHtcbiAgICByZXR1cm4gTWF0aC5hYnMocHJldkRhdGUuZ2V0VGltZSgpIC0gbmV4dERhdGUuZ2V0VGltZSgpKSA8PSBEQVlfTVM7XG59XG5cbmZ1bmN0aW9uIHdpdGhpbkN1cnJlbnREYXkocHJldkRhdGU6IERhdGUsIG5leHREYXRlOiBEYXRlKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHdpdGhpblBhc3QyNEhvdXJzKHByZXZEYXRlLCBuZXh0RGF0ZSkgJiYgcHJldkRhdGUuZ2V0RGF5KCkgPT09IG5leHREYXRlLmdldERheSgpO1xufVxuXG5mdW5jdGlvbiB3aXRoaW5DdXJyZW50WWVhcihwcmV2RGF0ZTogRGF0ZSwgbmV4dERhdGU6IERhdGUpOiBib29sZWFuIHtcbiAgICByZXR1cm4gcHJldkRhdGUuZ2V0RnVsbFllYXIoKSA9PT0gbmV4dERhdGUuZ2V0RnVsbFllYXIoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHdhbnRzRGF0ZVNlcGFyYXRvcihwcmV2RXZlbnREYXRlOiBPcHRpb25hbDxEYXRlPiwgbmV4dEV2ZW50RGF0ZTogT3B0aW9uYWw8RGF0ZT4pOiBib29sZWFuIHtcbiAgICBpZiAoIW5leHRFdmVudERhdGUgfHwgIXByZXZFdmVudERhdGUpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICAvLyBSZXR1cm4gZWFybHkgZm9yIGV2ZW50cyB0aGF0IGFyZSA+IDI0aCBhcGFydFxuICAgIGlmICghd2l0aGluUGFzdDI0SG91cnMocHJldkV2ZW50RGF0ZSwgbmV4dEV2ZW50RGF0ZSkpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgLy8gQ29tcGFyZSB3ZWVrZGF5c1xuICAgIHJldHVybiBwcmV2RXZlbnREYXRlLmdldERheSgpICE9PSBuZXh0RXZlbnREYXRlLmdldERheSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0RnVsbERhdGVOb0RheShkYXRlOiBEYXRlKTogc3RyaW5nIHtcbiAgICBjb25zdCBsb2NhbGUgPSBnZXRVc2VyTGFuZ3VhZ2UoKTtcbiAgICByZXR1cm4gX3QoXCJ0aW1lfGRhdGVfYXRfdGltZVwiLCB7XG4gICAgICAgIGRhdGU6IGRhdGUudG9Mb2NhbGVEYXRlU3RyaW5nKGxvY2FsZSkucmVwbGFjZSgvXFwvL2csIFwiLVwiKSxcbiAgICAgICAgdGltZTogZGF0ZS50b0xvY2FsZVRpbWVTdHJpbmcobG9jYWxlKS5yZXBsYWNlKC86L2csIFwiLVwiKSxcbiAgICB9KTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIGFuIElTTyBkYXRlIHN0cmluZyB3aXRob3V0IHRleHR1YWwgZGVzY3JpcHRpb24gb2YgdGhlIGRhdGUgKGllOiBubyBcIldlZG5lc2RheVwiIG9yIHNpbWlsYXIpXG4gKiBAcGFyYW0gZGF0ZSBUaGUgZGF0ZSB0byBmb3JtYXQuXG4gKiBAcmV0dXJucyBUaGUgZGF0ZSBzdHJpbmcgaW4gSVNPIGZvcm1hdC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdEZ1bGxEYXRlTm9EYXlJU08oZGF0ZTogRGF0ZSk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGRhdGUudG9JU09TdHJpbmcoKTtcbn1cblxuLyoqXG4gKiBGb3JtYXRzIGEgZ2l2ZW4gZGF0ZSB0byBhIHN0cmluZy5cbiAqIFdpbGwgdXNlIHRoZSBicm93c2VyJ3MgZGVmYXVsdCB0aW1lIHpvbmUuXG4gKiBAZXhhbXBsZSAxNy8xMS8yMDIyIGluIGVuLUdCIGxvY2FsZVxuICogQHBhcmFtIGRhdGUgLSBkYXRlIG9iamVjdCB0byBmb3JtYXRcbiAqIEBwYXJhbSBsb2NhbGUgLSB0aGUgbG9jYWxlIHN0cmluZyB0byB1c2UsIGluIEJDUCA0NyBmb3JtYXQsIGRlZmF1bHRpbmcgdG8gdXNlcidzIHNlbGVjdGVkIGFwcGxpY2F0aW9uIGxvY2FsZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0RnVsbERhdGVOb0RheU5vVGltZShkYXRlOiBEYXRlLCBsb2NhbGU/OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBuZXcgSW50bC5EYXRlVGltZUZvcm1hdChsb2NhbGUgPz8gZ2V0VXNlckxhbmd1YWdlKCksIHtcbiAgICAgICAgeWVhcjogXCJudW1lcmljXCIsXG4gICAgICAgIG1vbnRoOiBcIm51bWVyaWNcIixcbiAgICAgICAgZGF5OiBcIm51bWVyaWNcIixcbiAgICAgICAgdGltZVpvbmU6IGdldFVzZXJUaW1lem9uZSgpLFxuICAgIH0pLmZvcm1hdChkYXRlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdFJlbGF0aXZlVGltZShkYXRlOiBEYXRlLCBzaG93VHdlbHZlSG91ciA9IGZhbHNlKTogc3RyaW5nIHtcbiAgICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpO1xuICAgIGlmICh3aXRoaW5DdXJyZW50RGF5KGRhdGUsIG5vdykpIHtcbiAgICAgICAgcmV0dXJuIGZvcm1hdFRpbWUoZGF0ZSwgc2hvd1R3ZWx2ZUhvdXIpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IG1vbnRocyA9IGdldE1vbnRoc0FycmF5KCk7XG4gICAgICAgIGxldCByZWxhdGl2ZURhdGUgPSBgJHttb250aHNbZGF0ZS5nZXRNb250aCgpXX0gJHtkYXRlLmdldERhdGUoKX1gO1xuXG4gICAgICAgIGlmICghd2l0aGluQ3VycmVudFllYXIoZGF0ZSwgbm93KSkge1xuICAgICAgICAgICAgcmVsYXRpdmVEYXRlICs9IGAsICR7ZGF0ZS5nZXRGdWxsWWVhcigpfWA7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlbGF0aXZlRGF0ZTtcbiAgICB9XG59XG5cbi8qKlxuICogRm9ybWF0cyBkdXJhdGlvbiBpbiBtcyB0byBodW1hbi1yZWFkYWJsZSBzdHJpbmdcbiAqIFJldHVybnMgdmFsdWUgaW4gdGhlIGJpZ2dlc3QgcG9zc2libGUgdW5pdCAoZGF5LCBob3VyLCBtaW4sIHNlY29uZClcbiAqIFJvdW5kcyB2YWx1ZXMgdXAgdW50aWwgdW5pdCB0aHJlc2hvbGRcbiAqIGkuZS4gMjM6MTM6NTcgLT4gMjNoLCAyNDoxMzo1NyAtPiAxZCwgNDQ6NTY6NTYgLT4gMmRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvcm1hdER1cmF0aW9uKGR1cmF0aW9uTXM6IG51bWJlcik6IHN0cmluZyB7XG4gICAgaWYgKGR1cmF0aW9uTXMgPj0gREFZX01TKSB7XG4gICAgICAgIHJldHVybiBfdChcInRpbWV8c2hvcnRfZGF5c1wiLCB7IHZhbHVlOiBNYXRoLnJvdW5kKGR1cmF0aW9uTXMgLyBEQVlfTVMpIH0pO1xuICAgIH1cbiAgICBpZiAoZHVyYXRpb25NcyA+PSBIT1VSX01TKSB7XG4gICAgICAgIHJldHVybiBfdChcInRpbWV8c2hvcnRfaG91cnNcIiwgeyB2YWx1ZTogTWF0aC5yb3VuZChkdXJhdGlvbk1zIC8gSE9VUl9NUykgfSk7XG4gICAgfVxuICAgIGlmIChkdXJhdGlvbk1zID49IE1JTlVURV9NUykge1xuICAgICAgICByZXR1cm4gX3QoXCJ0aW1lfHNob3J0X21pbnV0ZXNcIiwgeyB2YWx1ZTogTWF0aC5yb3VuZChkdXJhdGlvbk1zIC8gTUlOVVRFX01TKSB9KTtcbiAgICB9XG4gICAgcmV0dXJuIF90KFwidGltZXxzaG9ydF9zZWNvbmRzXCIsIHsgdmFsdWU6IE1hdGgucm91bmQoZHVyYXRpb25NcyAvIDEwMDApIH0pO1xufVxuXG4vKipcbiAqIEZvcm1hdHMgZHVyYXRpb24gaW4gbXMgdG8gaHVtYW4tcmVhZGFibGUgc3RyaW5nXG4gKiBSZXR1cm5zIHByZWNpc2UgdmFsdWUgZG93biB0byB0aGUgbmVhcmVzdCBzZWNvbmRcbiAqIGkuZS4gMjM6MTM6NTcgLT4gMjNoIDEzbSA1N3MsIDQ0OjU2OjU2IC0+IDFkIDIwaCA1Nm0gNTZzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRQcmVjaXNlRHVyYXRpb24oZHVyYXRpb25NczogbnVtYmVyKTogc3RyaW5nIHtcbiAgICBjb25zdCBkYXlzID0gTWF0aC5mbG9vcihkdXJhdGlvbk1zIC8gREFZX01TKTtcbiAgICBjb25zdCBob3VycyA9IE1hdGguZmxvb3IoKGR1cmF0aW9uTXMgJSBEQVlfTVMpIC8gSE9VUl9NUyk7XG4gICAgY29uc3QgbWludXRlcyA9IE1hdGguZmxvb3IoKGR1cmF0aW9uTXMgJSBIT1VSX01TKSAvIE1JTlVURV9NUyk7XG4gICAgY29uc3Qgc2Vjb25kcyA9IE1hdGguZmxvb3IoKGR1cmF0aW9uTXMgJSBNSU5VVEVfTVMpIC8gMTAwMCk7XG5cbiAgICBpZiAoZGF5cyA+IDApIHtcbiAgICAgICAgcmV0dXJuIF90KFwidGltZXxzaG9ydF9kYXlzX2hvdXJzX21pbnV0ZXNfc2Vjb25kc1wiLCB7IGRheXMsIGhvdXJzLCBtaW51dGVzLCBzZWNvbmRzIH0pO1xuICAgIH1cbiAgICBpZiAoaG91cnMgPiAwKSB7XG4gICAgICAgIHJldHVybiBfdChcInRpbWV8c2hvcnRfaG91cnNfbWludXRlc19zZWNvbmRzXCIsIHsgaG91cnMsIG1pbnV0ZXMsIHNlY29uZHMgfSk7XG4gICAgfVxuICAgIGlmIChtaW51dGVzID4gMCkge1xuICAgICAgICByZXR1cm4gX3QoXCJ0aW1lfHNob3J0X21pbnV0ZXNfc2Vjb25kc1wiLCB7IG1pbnV0ZXMsIHNlY29uZHMgfSk7XG4gICAgfVxuICAgIHJldHVybiBfdChcInRpbWV8c2hvcnRfc2Vjb25kc1wiLCB7IHZhbHVlOiBzZWNvbmRzIH0pO1xufVxuXG4vKipcbiAqIEZvcm1hdHMgYSB0aW1lc3RhbXAgdG8gYSBzaG9ydCBkYXRlXG4gKiBTaW1pbGFyIHRvIHtAZm9ybWF0RnVsbERhdGVOb0RheU5vVGltZX0gYnV0IHdpdGggMi1kaWdpdCBvbiBkYXksIG1vbnRoLCB5ZWFyLlxuICogQGV4YW1wbGUgMjUvMTIvMjIgaW4gZW4tR0IgbG9jYWxlXG4gKiBAcGFyYW0gdGltZXN0YW1wIC0gZXBvY2ggdGltZXN0YW1wXG4gKiBAcGFyYW0gbG9jYWxlIC0gdGhlIGxvY2FsZSBzdHJpbmcgdG8gdXNlLCBpbiBCQ1AgNDcgZm9ybWF0LCBkZWZhdWx0aW5nIHRvIHVzZXIncyBzZWxlY3RlZCBhcHBsaWNhdGlvbiBsb2NhbGVcbiAqIEByZXR1cm5zIHtzdHJpbmd9IGZvcm1hdHRlZERhdGVcbiAqL1xuZXhwb3J0IGNvbnN0IGZvcm1hdExvY2FsRGF0ZVNob3J0ID0gKHRpbWVzdGFtcDogbnVtYmVyLCBsb2NhbGU/OiBzdHJpbmcpOiBzdHJpbmcgPT5cbiAgICBuZXcgSW50bC5EYXRlVGltZUZvcm1hdChsb2NhbGUgPz8gZ2V0VXNlckxhbmd1YWdlKCksIHtcbiAgICAgICAgZGF5OiBcIjItZGlnaXRcIixcbiAgICAgICAgbW9udGg6IFwiMi1kaWdpdFwiLFxuICAgICAgICB5ZWFyOiBcIjItZGlnaXRcIixcbiAgICAgICAgdGltZVpvbmU6IGdldFVzZXJUaW1lem9uZSgpLFxuICAgIH0pLmZvcm1hdCh0aW1lc3RhbXApO1xuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQVlBLElBQUFBLGdCQUFBLEdBQUFDLE9BQUE7QUFD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