matrix-react-sdk
Version:
SDK for matrix.org using React
221 lines (209 loc) • 28.8 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _dispatcher = _interopRequireDefault(require("./dispatcher/dispatcher"));
var _Timer = _interopRequireDefault(require("./utils/Timer"));
/*
Copyright 2019-2024 New Vector 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.
*/
// important these are larger than the timeouts of timers
// used with UserActivity.timeWhileActive*,
// such as READ_MARKER_INVIEW_THRESHOLD_MS (timeWhileActiveRecently),
// READ_MARKER_OUTOFVIEW_THRESHOLD_MS (timeWhileActiveRecently),
// READ_RECEIPT_INTERVAL_MS (timeWhileActiveNow) in TimelinePanel
// 'Under a few seconds'. Must be less than 'RECENTLY_ACTIVE_THRESHOLD_MS'
const CURRENTLY_ACTIVE_THRESHOLD_MS = 700;
// 'Under a few minutes'.
const RECENTLY_ACTIVE_THRESHOLD_MS = 2 * 60 * 1000;
/**
* This class watches for user activity (moving the mouse or pressing a key)
* and starts/stops attached timers while the user is active.
*
* There are two classes of 'active': 'active now' and 'active recently'
* see doc on the userActive* functions for what these mean.
*/
class UserActivity {
constructor(window, document) {
(0, _defineProperty2.default)(this, "activeNowTimeout", void 0);
(0, _defineProperty2.default)(this, "activeRecentlyTimeout", void 0);
(0, _defineProperty2.default)(this, "attachedActiveNowTimers", []);
(0, _defineProperty2.default)(this, "attachedActiveRecentlyTimers", []);
(0, _defineProperty2.default)(this, "lastScreenX", 0);
(0, _defineProperty2.default)(this, "lastScreenY", 0);
(0, _defineProperty2.default)(this, "onPageVisibilityChanged", e => {
if (this.document.visibilityState === "hidden") {
this.activeNowTimeout.abort();
this.activeRecentlyTimeout.abort();
} else {
this.onUserActivity(e);
}
});
(0, _defineProperty2.default)(this, "onWindowBlurred", () => {
this.activeNowTimeout.abort();
this.activeRecentlyTimeout.abort();
});
// XXX: exported for tests
(0, _defineProperty2.default)(this, "onUserActivity", event => {
// ignore anything if the window isn't focused
if (!this.document.hasFocus()) return;
if (event.type === "mousemove" && this.isMouseEvent(event)) {
if (event.screenX === this.lastScreenX && event.screenY === this.lastScreenY) {
// mouse hasn't actually moved
return;
}
this.lastScreenX = event.screenX;
this.lastScreenY = event.screenY;
}
_dispatcher.default.dispatch({
action: "user_activity"
});
if (!this.activeNowTimeout.isRunning()) {
this.activeNowTimeout.start();
_dispatcher.default.dispatch({
action: "user_activity_start"
});
UserActivity.runTimersUntilTimeout(this.attachedActiveNowTimers, this.activeNowTimeout);
} else {
this.activeNowTimeout.restart();
}
if (!this.activeRecentlyTimeout.isRunning()) {
this.activeRecentlyTimeout.start();
UserActivity.runTimersUntilTimeout(this.attachedActiveRecentlyTimers, this.activeRecentlyTimeout);
} else {
this.activeRecentlyTimeout.restart();
}
});
this.window = window;
this.document = document;
this.activeNowTimeout = new _Timer.default(CURRENTLY_ACTIVE_THRESHOLD_MS);
this.activeRecentlyTimeout = new _Timer.default(RECENTLY_ACTIVE_THRESHOLD_MS);
}
static sharedInstance() {
if (window.mxUserActivity === undefined) {
window.mxUserActivity = new UserActivity(window, document);
}
return window.mxUserActivity;
}
/**
* Runs the given timer while the user is 'active now', aborting when the user is no longer
* considered currently active.
* See userActiveNow() for what it means for a user to be 'active'.
* Can be called multiple times with the same already running timer, which is a NO-OP.
* Can be called before the user becomes active, in which case it is only started
* later on when the user does become active.
* @param {Timer} timer the timer to use
*/
timeWhileActiveNow(timer) {
this.timeWhile(timer, this.attachedActiveNowTimers);
if (this.userActiveNow()) {
timer.start();
}
}
/**
* Runs the given timer while the user is 'active' now or recently,
* aborting when the user becomes inactive.
* See userActiveRecently() for what it means for a user to be 'active recently'.
* Can be called multiple times with the same already running timer, which is a NO-OP.
* Can be called before the user becomes active, in which case it is only started
* later on when the user does become active.
* @param {Timer} timer the timer to use
*/
timeWhileActiveRecently(timer) {
this.timeWhile(timer, this.attachedActiveRecentlyTimers);
if (this.userActiveRecently()) {
timer.start();
}
}
timeWhile(timer, attachedTimers) {
// important this happens first
const index = attachedTimers.indexOf(timer);
if (index === -1) {
attachedTimers.push(timer);
// remove when done or aborted
timer.finished().finally(() => {
const index = attachedTimers.indexOf(timer);
if (index !== -1) {
// should never be -1
attachedTimers.splice(index, 1);
}
// as we fork the promise here,
// avoid unhandled rejection warnings
}).catch(err => {});
}
}
/**
* Start listening to user activity
*/
start() {
this.document.addEventListener("mousedown", this.onUserActivity);
this.document.addEventListener("mousemove", this.onUserActivity);
this.document.addEventListener("keydown", this.onUserActivity);
this.document.addEventListener("visibilitychange", this.onPageVisibilityChanged);
this.window.addEventListener("blur", this.onWindowBlurred);
this.window.addEventListener("focus", this.onUserActivity);
// can't use document.scroll here because that's only the document
// itself being scrolled. Need to use addEventListener's useCapture.
// also this needs to be the wheel event, not scroll, as scroll is
// fired when the view scrolls down for a new message.
this.window.addEventListener("wheel", this.onUserActivity, {
passive: true,
capture: true
});
}
/**
* Stop tracking user activity
*/
stop() {
this.document.removeEventListener("mousedown", this.onUserActivity);
this.document.removeEventListener("mousemove", this.onUserActivity);
this.document.removeEventListener("keydown", this.onUserActivity);
this.window.removeEventListener("wheel", this.onUserActivity, {
capture: true
});
this.document.removeEventListener("visibilitychange", this.onPageVisibilityChanged);
this.window.removeEventListener("blur", this.onWindowBlurred);
this.window.removeEventListener("focus", this.onUserActivity);
}
/**
* Return true if the user is currently 'active'
* A user is 'active' while they are interacting with the app and for a very short (<1s)
* time after that. This is intended to give a strong indication that the app has the
* user's attention at any given moment.
* @returns {boolean} true if user is currently 'active'
*/
userActiveNow() {
return this.activeNowTimeout.isRunning();
}
/**
* Return true if the user is currently active or has been recently
* A user is 'active recently' for a longer period of time (~2 mins) after
* they have been 'active' and while the app still has the focus. This is
* intended to indicate when the app may still have the user's attention
* (or they may have gone to make tea and left the window focused).
* @returns {boolean} true if user has been active recently
*/
userActiveRecently() {
return this.activeRecentlyTimeout.isRunning();
}
static async runTimersUntilTimeout(attachedTimers, timeout) {
attachedTimers.forEach(t => t.start());
try {
await timeout.finished();
} catch (_e) {
/* aborted */
}
attachedTimers.forEach(t => t.abort());
}
isMouseEvent(event) {
return event.type.startsWith("mouse");
}
}
exports.default = UserActivity;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfZGlzcGF0Y2hlciIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX1RpbWVyIiwiQ1VSUkVOVExZX0FDVElWRV9USFJFU0hPTERfTVMiLCJSRUNFTlRMWV9BQ1RJVkVfVEhSRVNIT0xEX01TIiwiVXNlckFjdGl2aXR5IiwiY29uc3RydWN0b3IiLCJ3aW5kb3ciLCJkb2N1bWVudCIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJkZWZhdWx0IiwiZSIsInZpc2liaWxpdHlTdGF0ZSIsImFjdGl2ZU5vd1RpbWVvdXQiLCJhYm9ydCIsImFjdGl2ZVJlY2VudGx5VGltZW91dCIsIm9uVXNlckFjdGl2aXR5IiwiZXZlbnQiLCJoYXNGb2N1cyIsInR5cGUiLCJpc01vdXNlRXZlbnQiLCJzY3JlZW5YIiwibGFzdFNjcmVlblgiLCJzY3JlZW5ZIiwibGFzdFNjcmVlblkiLCJkaXMiLCJkaXNwYXRjaCIsImFjdGlvbiIsImlzUnVubmluZyIsInN0YXJ0IiwicnVuVGltZXJzVW50aWxUaW1lb3V0IiwiYXR0YWNoZWRBY3RpdmVOb3dUaW1lcnMiLCJyZXN0YXJ0IiwiYXR0YWNoZWRBY3RpdmVSZWNlbnRseVRpbWVycyIsIlRpbWVyIiwic2hhcmVkSW5zdGFuY2UiLCJteFVzZXJBY3Rpdml0eSIsInVuZGVmaW5lZCIsInRpbWVXaGlsZUFjdGl2ZU5vdyIsInRpbWVyIiwidGltZVdoaWxlIiwidXNlckFjdGl2ZU5vdyIsInRpbWVXaGlsZUFjdGl2ZVJlY2VudGx5IiwidXNlckFjdGl2ZVJlY2VudGx5IiwiYXR0YWNoZWRUaW1lcnMiLCJpbmRleCIsImluZGV4T2YiLCJwdXNoIiwiZmluaXNoZWQiLCJmaW5hbGx5Iiwic3BsaWNlIiwiY2F0Y2giLCJlcnIiLCJhZGRFdmVudExpc3RlbmVyIiwib25QYWdlVmlzaWJpbGl0eUNoYW5nZWQiLCJvbldpbmRvd0JsdXJyZWQiLCJwYXNzaXZlIiwiY2FwdHVyZSIsInN0b3AiLCJyZW1vdmVFdmVudExpc3RlbmVyIiwidGltZW91dCIsImZvckVhY2giLCJ0IiwiX2UiLCJzdGFydHNXaXRoIiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uL3NyYy9Vc2VyQWN0aXZpdHkudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCAyMDE5LTIwMjQgTmV3IFZlY3RvciBMdGQuXG5Db3B5cmlnaHQgMjAxNSwgMjAxNiBPcGVuTWFya2V0IEx0ZFxuXG5TUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQUdQTC0zLjAtb25seSBPUiBHUEwtMy4wLW9ubHlcblBsZWFzZSBzZWUgTElDRU5TRSBmaWxlcyBpbiB0aGUgcmVwb3NpdG9yeSByb290IGZvciBmdWxsIGRldGFpbHMuXG4qL1xuXG5pbXBvcnQgZGlzIGZyb20gXCIuL2Rpc3BhdGNoZXIvZGlzcGF0Y2hlclwiO1xuaW1wb3J0IFRpbWVyIGZyb20gXCIuL3V0aWxzL1RpbWVyXCI7XG5cbi8vIGltcG9ydGFudCB0aGVzZSBhcmUgbGFyZ2VyIHRoYW4gdGhlIHRpbWVvdXRzIG9mIHRpbWVyc1xuLy8gdXNlZCB3aXRoIFVzZXJBY3Rpdml0eS50aW1lV2hpbGVBY3RpdmUqLFxuLy8gc3VjaCBhcyBSRUFEX01BUktFUl9JTlZJRVdfVEhSRVNIT0xEX01TICh0aW1lV2hpbGVBY3RpdmVSZWNlbnRseSksXG4vLyBSRUFEX01BUktFUl9PVVRPRlZJRVdfVEhSRVNIT0xEX01TICh0aW1lV2hpbGVBY3RpdmVSZWNlbnRseSksXG4vLyBSRUFEX1JFQ0VJUFRfSU5URVJWQUxfTVMgKHRpbWVXaGlsZUFjdGl2ZU5vdykgaW4gVGltZWxpbmVQYW5lbFxuXG4vLyAnVW5kZXIgYSBmZXcgc2Vjb25kcycuIE11c3QgYmUgbGVzcyB0aGFuICdSRUNFTlRMWV9BQ1RJVkVfVEhSRVNIT0xEX01TJ1xuY29uc3QgQ1VSUkVOVExZX0FDVElWRV9USFJFU0hPTERfTVMgPSA3MDA7XG5cbi8vICdVbmRlciBhIGZldyBtaW51dGVzJy5cbmNvbnN0IFJFQ0VOVExZX0FDVElWRV9USFJFU0hPTERfTVMgPSAyICogNjAgKiAxMDAwO1xuXG4vKipcbiAqIFRoaXMgY2xhc3Mgd2F0Y2hlcyBmb3IgdXNlciBhY3Rpdml0eSAobW92aW5nIHRoZSBtb3VzZSBvciBwcmVzc2luZyBhIGtleSlcbiAqIGFuZCBzdGFydHMvc3RvcHMgYXR0YWNoZWQgdGltZXJzIHdoaWxlIHRoZSB1c2VyIGlzIGFjdGl2ZS5cbiAqXG4gKiBUaGVyZSBhcmUgdHdvIGNsYXNzZXMgb2YgJ2FjdGl2ZSc6ICdhY3RpdmUgbm93JyBhbmQgJ2FjdGl2ZSByZWNlbnRseSdcbiAqIHNlZSBkb2Mgb24gdGhlIHVzZXJBY3RpdmUqIGZ1bmN0aW9ucyBmb3Igd2hhdCB0aGVzZSBtZWFuLlxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBVc2VyQWN0aXZpdHkge1xuICAgIHByaXZhdGUgcmVhZG9ubHkgYWN0aXZlTm93VGltZW91dDogVGltZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBhY3RpdmVSZWNlbnRseVRpbWVvdXQ6IFRpbWVyO1xuICAgIHByaXZhdGUgYXR0YWNoZWRBY3RpdmVOb3dUaW1lcnM6IFRpbWVyW10gPSBbXTtcbiAgICBwcml2YXRlIGF0dGFjaGVkQWN0aXZlUmVjZW50bHlUaW1lcnM6IFRpbWVyW10gPSBbXTtcbiAgICBwcml2YXRlIGxhc3RTY3JlZW5YID0gMDtcbiAgICBwcml2YXRlIGxhc3RTY3JlZW5ZID0gMDtcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSByZWFkb25seSB3aW5kb3c6IFdpbmRvdyxcbiAgICAgICAgcHJpdmF0ZSByZWFkb25seSBkb2N1bWVudDogRG9jdW1lbnQsXG4gICAgKSB7XG4gICAgICAgIHRoaXMuYWN0aXZlTm93VGltZW91dCA9IG5ldyBUaW1lcihDVVJSRU5UTFlfQUNUSVZFX1RIUkVTSE9MRF9NUyk7XG4gICAgICAgIHRoaXMuYWN0aXZlUmVjZW50bHlUaW1lb3V0ID0gbmV3IFRpbWVyKFJFQ0VOVExZX0FDVElWRV9USFJFU0hPTERfTVMpO1xuICAgIH1cblxuICAgIHB1YmxpYyBzdGF0aWMgc2hhcmVkSW5zdGFuY2UoKTogVXNlckFjdGl2aXR5IHtcbiAgICAgICAgaWYgKHdpbmRvdy5teFVzZXJBY3Rpdml0eSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICB3aW5kb3cubXhVc2VyQWN0aXZpdHkgPSBuZXcgVXNlckFjdGl2aXR5KHdpbmRvdywgZG9jdW1lbnQpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB3aW5kb3cubXhVc2VyQWN0aXZpdHk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUnVucyB0aGUgZ2l2ZW4gdGltZXIgd2hpbGUgdGhlIHVzZXIgaXMgJ2FjdGl2ZSBub3cnLCBhYm9ydGluZyB3aGVuIHRoZSB1c2VyIGlzIG5vIGxvbmdlclxuICAgICAqIGNvbnNpZGVyZWQgY3VycmVudGx5IGFjdGl2ZS5cbiAgICAgKiBTZWUgdXNlckFjdGl2ZU5vdygpIGZvciB3aGF0IGl0IG1lYW5zIGZvciBhIHVzZXIgdG8gYmUgJ2FjdGl2ZScuXG4gICAgICogQ2FuIGJlIGNhbGxlZCBtdWx0aXBsZSB0aW1lcyB3aXRoIHRoZSBzYW1lIGFscmVhZHkgcnVubmluZyB0aW1lciwgd2hpY2ggaXMgYSBOTy1PUC5cbiAgICAgKiBDYW4gYmUgY2FsbGVkIGJlZm9yZSB0aGUgdXNlciBiZWNvbWVzIGFjdGl2ZSwgaW4gd2hpY2ggY2FzZSBpdCBpcyBvbmx5IHN0YXJ0ZWRcbiAgICAgKiBsYXRlciBvbiB3aGVuIHRoZSB1c2VyIGRvZXMgYmVjb21lIGFjdGl2ZS5cbiAgICAgKiBAcGFyYW0ge1RpbWVyfSB0aW1lciB0aGUgdGltZXIgdG8gdXNlXG4gICAgICovXG4gICAgcHVibGljIHRpbWVXaGlsZUFjdGl2ZU5vdyh0aW1lcjogVGltZXIpOiB2b2lkIHtcbiAgICAgICAgdGhpcy50aW1lV2hpbGUodGltZXIsIHRoaXMuYXR0YWNoZWRBY3RpdmVOb3dUaW1lcnMpO1xuICAgICAgICBpZiAodGhpcy51c2VyQWN0aXZlTm93KCkpIHtcbiAgICAgICAgICAgIHRpbWVyLnN0YXJ0KCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSdW5zIHRoZSBnaXZlbiB0aW1lciB3aGlsZSB0aGUgdXNlciBpcyAnYWN0aXZlJyBub3cgb3IgcmVjZW50bHksXG4gICAgICogYWJvcnRpbmcgd2hlbiB0aGUgdXNlciBiZWNvbWVzIGluYWN0aXZlLlxuICAgICAqIFNlZSB1c2VyQWN0aXZlUmVjZW50bHkoKSBmb3Igd2hhdCBpdCBtZWFucyBmb3IgYSB1c2VyIHRvIGJlICdhY3RpdmUgcmVjZW50bHknLlxuICAgICAqIENhbiBiZSBjYWxsZWQgbXVsdGlwbGUgdGltZXMgd2l0aCB0aGUgc2FtZSBhbHJlYWR5IHJ1bm5pbmcgdGltZXIsIHdoaWNoIGlzIGEgTk8tT1AuXG4gICAgICogQ2FuIGJlIGNhbGxlZCBiZWZvcmUgdGhlIHVzZXIgYmVjb21lcyBhY3RpdmUsIGluIHdoaWNoIGNhc2UgaXQgaXMgb25seSBzdGFydGVkXG4gICAgICogbGF0ZXIgb24gd2hlbiB0aGUgdXNlciBkb2VzIGJlY29tZSBhY3RpdmUuXG4gICAgICogQHBhcmFtIHtUaW1lcn0gdGltZXIgdGhlIHRpbWVyIHRvIHVzZVxuICAgICAqL1xuICAgIHB1YmxpYyB0aW1lV2hpbGVBY3RpdmVSZWNlbnRseSh0aW1lcjogVGltZXIpOiB2b2lkIHtcbiAgICAgICAgdGhpcy50aW1lV2hpbGUodGltZXIsIHRoaXMuYXR0YWNoZWRBY3RpdmVSZWNlbnRseVRpbWVycyk7XG4gICAgICAgIGlmICh0aGlzLnVzZXJBY3RpdmVSZWNlbnRseSgpKSB7XG4gICAgICAgICAgICB0aW1lci5zdGFydCgpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSB0aW1lV2hpbGUodGltZXI6IFRpbWVyLCBhdHRhY2hlZFRpbWVyczogVGltZXJbXSk6IHZvaWQge1xuICAgICAgICAvLyBpbXBvcnRhbnQgdGhpcyBoYXBwZW5zIGZpcnN0XG4gICAgICAgIGNvbnN0IGluZGV4ID0gYXR0YWNoZWRUaW1lcnMuaW5kZXhPZih0aW1lcik7XG4gICAgICAgIGlmIChpbmRleCA9PT0gLTEpIHtcbiAgICAgICAgICAgIGF0dGFjaGVkVGltZXJzLnB1c2godGltZXIpO1xuICAgICAgICAgICAgLy8gcmVtb3ZlIHdoZW4gZG9uZSBvciBhYm9ydGVkXG4gICAgICAgICAgICB0aW1lclxuICAgICAgICAgICAgICAgIC5maW5pc2hlZCgpXG4gICAgICAgICAgICAgICAgLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBpbmRleCA9IGF0dGFjaGVkVGltZXJzLmluZGV4T2YodGltZXIpO1xuICAgICAgICAgICAgICAgICAgICBpZiAoaW5kZXggIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBzaG91bGQgbmV2ZXIgYmUgLTFcbiAgICAgICAgICAgICAgICAgICAgICAgIGF0dGFjaGVkVGltZXJzLnNwbGljZShpbmRleCwgMSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgLy8gYXMgd2UgZm9yayB0aGUgcHJvbWlzZSBoZXJlLFxuICAgICAgICAgICAgICAgICAgICAvLyBhdm9pZCB1bmhhbmRsZWQgcmVqZWN0aW9uIHdhcm5pbmdzXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAuY2F0Y2goKGVycikgPT4ge30pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU3RhcnQgbGlzdGVuaW5nIHRvIHVzZXIgYWN0aXZpdHlcbiAgICAgKi9cbiAgICBwdWJsaWMgc3RhcnQoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcihcIm1vdXNlZG93blwiLCB0aGlzLm9uVXNlckFjdGl2aXR5KTtcbiAgICAgICAgdGhpcy5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKFwibW91c2Vtb3ZlXCIsIHRoaXMub25Vc2VyQWN0aXZpdHkpO1xuICAgICAgICB0aGlzLmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJrZXlkb3duXCIsIHRoaXMub25Vc2VyQWN0aXZpdHkpO1xuICAgICAgICB0aGlzLmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXCJ2aXNpYmlsaXR5Y2hhbmdlXCIsIHRoaXMub25QYWdlVmlzaWJpbGl0eUNoYW5nZWQpO1xuICAgICAgICB0aGlzLndpbmRvdy5hZGRFdmVudExpc3RlbmVyKFwiYmx1clwiLCB0aGlzLm9uV2luZG93Qmx1cnJlZCk7XG4gICAgICAgIHRoaXMud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJmb2N1c1wiLCB0aGlzLm9uVXNlckFjdGl2aXR5KTtcbiAgICAgICAgLy8gY2FuJ3QgdXNlIGRvY3VtZW50LnNjcm9sbCBoZXJlIGJlY2F1c2UgdGhhdCdzIG9ubHkgdGhlIGRvY3VtZW50XG4gICAgICAgIC8vIGl0c2VsZiBiZWluZyBzY3JvbGxlZC4gTmVlZCB0byB1c2UgYWRkRXZlbnRMaXN0ZW5lcidzIHVzZUNhcHR1cmUuXG4gICAgICAgIC8vIGFsc28gdGhpcyBuZWVkcyB0byBiZSB0aGUgd2hlZWwgZXZlbnQsIG5vdCBzY3JvbGwsIGFzIHNjcm9sbCBpc1xuICAgICAgICAvLyBmaXJlZCB3aGVuIHRoZSB2aWV3IHNjcm9sbHMgZG93biBmb3IgYSBuZXcgbWVzc2FnZS5cbiAgICAgICAgdGhpcy53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcIndoZWVsXCIsIHRoaXMub25Vc2VyQWN0aXZpdHksIHtcbiAgICAgICAgICAgIHBhc3NpdmU6IHRydWUsXG4gICAgICAgICAgICBjYXB0dXJlOiB0cnVlLFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTdG9wIHRyYWNraW5nIHVzZXIgYWN0aXZpdHlcbiAgICAgKi9cbiAgICBwdWJsaWMgc3RvcCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5kb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKFwibW91c2Vkb3duXCIsIHRoaXMub25Vc2VyQWN0aXZpdHkpO1xuICAgICAgICB0aGlzLmRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJtb3VzZW1vdmVcIiwgdGhpcy5vblVzZXJBY3Rpdml0eSk7XG4gICAgICAgIHRoaXMuZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImtleWRvd25cIiwgdGhpcy5vblVzZXJBY3Rpdml0eSk7XG4gICAgICAgIHRoaXMud2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJ3aGVlbFwiLCB0aGlzLm9uVXNlckFjdGl2aXR5LCB7XG4gICAgICAgICAgICBjYXB0dXJlOiB0cnVlLFxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5kb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKFwidmlzaWJpbGl0eWNoYW5nZVwiLCB0aGlzLm9uUGFnZVZpc2liaWxpdHlDaGFuZ2VkKTtcbiAgICAgICAgdGhpcy53aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImJsdXJcIiwgdGhpcy5vbldpbmRvd0JsdXJyZWQpO1xuICAgICAgICB0aGlzLndpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKFwiZm9jdXNcIiwgdGhpcy5vblVzZXJBY3Rpdml0eSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJuIHRydWUgaWYgdGhlIHVzZXIgaXMgY3VycmVudGx5ICdhY3RpdmUnXG4gICAgICogQSB1c2VyIGlzICdhY3RpdmUnIHdoaWxlIHRoZXkgYXJlIGludGVyYWN0aW5nIHdpdGggdGhlIGFwcCBhbmQgZm9yIGEgdmVyeSBzaG9ydCAoPDFzKVxuICAgICAqIHRpbWUgYWZ0ZXIgdGhhdC4gVGhpcyBpcyBpbnRlbmRlZCB0byBnaXZlIGEgc3Ryb25nIGluZGljYXRpb24gdGhhdCB0aGUgYXBwIGhhcyB0aGVcbiAgICAgKiB1c2VyJ3MgYXR0ZW50aW9uIGF0IGFueSBnaXZlbiBtb21lbnQuXG4gICAgICogQHJldHVybnMge2Jvb2xlYW59IHRydWUgaWYgdXNlciBpcyBjdXJyZW50bHkgJ2FjdGl2ZSdcbiAgICAgKi9cbiAgICBwdWJsaWMgdXNlckFjdGl2ZU5vdygpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYWN0aXZlTm93VGltZW91dC5pc1J1bm5pbmcoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm4gdHJ1ZSBpZiB0aGUgdXNlciBpcyBjdXJyZW50bHkgYWN0aXZlIG9yIGhhcyBiZWVuIHJlY2VudGx5XG4gICAgICogQSB1c2VyIGlzICdhY3RpdmUgcmVjZW50bHknIGZvciBhIGxvbmdlciBwZXJpb2Qgb2YgdGltZSAofjIgbWlucykgYWZ0ZXJcbiAgICAgKiB0aGV5IGhhdmUgYmVlbiAnYWN0aXZlJyBhbmQgd2hpbGUgdGhlIGFwcCBzdGlsbCBoYXMgdGhlIGZvY3VzLiBUaGlzIGlzXG4gICAgICogaW50ZW5kZWQgdG8gaW5kaWNhdGUgd2hlbiB0aGUgYXBwIG1heSBzdGlsbCBoYXZlIHRoZSB1c2VyJ3MgYXR0ZW50aW9uXG4gICAgICogKG9yIHRoZXkgbWF5IGhhdmUgZ29uZSB0byBtYWtlIHRlYSBhbmQgbGVmdCB0aGUgd2luZG93IGZvY3VzZWQpLlxuICAgICAqIEByZXR1cm5zIHtib29sZWFufSB0cnVlIGlmIHVzZXIgaGFzIGJlZW4gYWN0aXZlIHJlY2VudGx5XG4gICAgICovXG4gICAgcHVibGljIHVzZXJBY3RpdmVSZWNlbnRseSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYWN0aXZlUmVjZW50bHlUaW1lb3V0LmlzUnVubmluZygpO1xuICAgIH1cblxuICAgIHByaXZhdGUgb25QYWdlVmlzaWJpbGl0eUNoYW5nZWQgPSAoZTogRXZlbnQpOiB2b2lkID0+IHtcbiAgICAgICAgaWYgKHRoaXMuZG9jdW1lbnQudmlzaWJpbGl0eVN0YXRlID09PSBcImhpZGRlblwiKSB7XG4gICAgICAgICAgICB0aGlzLmFjdGl2ZU5vd1RpbWVvdXQuYWJvcnQoKTtcbiAgICAgICAgICAgIHRoaXMuYWN0aXZlUmVjZW50bHlUaW1lb3V0LmFib3J0KCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLm9uVXNlckFjdGl2aXR5KGUpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25XaW5kb3dCbHVycmVkID0gKCk6IHZvaWQgPT4ge1xuICAgICAgICB0aGlzLmFjdGl2ZU5vd1RpbWVvdXQuYWJvcnQoKTtcbiAgICAgICAgdGhpcy5hY3RpdmVSZWNlbnRseVRpbWVvdXQuYWJvcnQoKTtcbiAgICB9O1xuXG4gICAgLy8gWFhYOiBleHBvcnRlZCBmb3IgdGVzdHNcbiAgICBwdWJsaWMgb25Vc2VyQWN0aXZpdHkgPSAoZXZlbnQ6IEV2ZW50KTogdm9pZCA9PiB7XG4gICAgICAgIC8vIGlnbm9yZSBhbnl0aGluZyBpZiB0aGUgd2luZG93IGlzbid0IGZvY3VzZWRcbiAgICAgICAgaWYgKCF0aGlzLmRvY3VtZW50Lmhhc0ZvY3VzKCkpIHJldHVybjtcblxuICAgICAgICBpZiAoZXZlbnQudHlwZSA9PT0gXCJtb3VzZW1vdmVcIiAmJiB0aGlzLmlzTW91c2VFdmVudChldmVudCkpIHtcbiAgICAgICAgICAgIGlmIChldmVudC5zY3JlZW5YID09PSB0aGlzLmxhc3RTY3JlZW5YICYmIGV2ZW50LnNjcmVlblkgPT09IHRoaXMubGFzdFNjcmVlblkpIHtcbiAgICAgICAgICAgICAgICAvLyBtb3VzZSBoYXNuJ3QgYWN0dWFsbHkgbW92ZWRcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmxhc3RTY3JlZW5YID0gZXZlbnQuc2NyZWVuWDtcbiAgICAgICAgICAgIHRoaXMubGFzdFNjcmVlblkgPSBldmVudC5zY3JlZW5ZO1xuICAgICAgICB9XG5cbiAgICAgICAgZGlzLmRpc3BhdGNoKHsgYWN0aW9uOiBcInVzZXJfYWN0aXZpdHlcIiB9KTtcbiAgICAgICAgaWYgKCF0aGlzLmFjdGl2ZU5vd1RpbWVvdXQuaXNSdW5uaW5nKCkpIHtcbiAgICAgICAgICAgIHRoaXMuYWN0aXZlTm93VGltZW91dC5zdGFydCgpO1xuICAgICAgICAgICAgZGlzLmRpc3BhdGNoKHsgYWN0aW9uOiBcInVzZXJfYWN0aXZpdHlfc3RhcnRcIiB9KTtcblxuICAgICAgICAgICAgVXNlckFjdGl2aXR5LnJ1blRpbWVyc1VudGlsVGltZW91dCh0aGlzLmF0dGFjaGVkQWN0aXZlTm93VGltZXJzLCB0aGlzLmFjdGl2ZU5vd1RpbWVvdXQpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5hY3RpdmVOb3dUaW1lb3V0LnJlc3RhcnQoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghdGhpcy5hY3RpdmVSZWNlbnRseVRpbWVvdXQuaXNSdW5uaW5nKCkpIHtcbiAgICAgICAgICAgIHRoaXMuYWN0aXZlUmVjZW50bHlUaW1lb3V0LnN0YXJ0KCk7XG5cbiAgICAgICAgICAgIFVzZXJBY3Rpdml0eS5ydW5UaW1lcnNVbnRpbFRpbWVvdXQodGhpcy5hdHRhY2hlZEFjdGl2ZVJlY2VudGx5VGltZXJzLCB0aGlzLmFjdGl2ZVJlY2VudGx5VGltZW91dCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmFjdGl2ZVJlY2VudGx5VGltZW91dC5yZXN0YXJ0KCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBzdGF0aWMgYXN5bmMgcnVuVGltZXJzVW50aWxUaW1lb3V0KGF0dGFjaGVkVGltZXJzOiBUaW1lcltdLCB0aW1lb3V0OiBUaW1lcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhdHRhY2hlZFRpbWVycy5mb3JFYWNoKCh0KSA9PiB0LnN0YXJ0KCkpO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgdGltZW91dC5maW5pc2hlZCgpO1xuICAgICAgICB9IGNhdGNoIChfZSkge1xuICAgICAgICAgICAgLyogYWJvcnRlZCAqL1xuICAgICAgICB9XG4gICAgICAgIGF0dGFjaGVkVGltZXJzLmZvckVhY2goKHQpID0+IHQuYWJvcnQoKSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBpc01vdXNlRXZlbnQoZXZlbnQ6IEV2ZW50KTogZXZlbnQgaXMgTW91c2VFdmVudCB7XG4gICAgICAgIHJldHVybiBldmVudC50eXBlLnN0YXJ0c1dpdGgoXCJtb3VzZVwiKTtcbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBUUEsSUFBQUEsV0FBQSxHQUFBQyxzQkFBQSxDQUFBQyxPQUFBO0FBQ0EsSUFBQUMsTUFBQSxHQUFBRixzQkFBQSxDQUFBQyxPQUFBO0FBVEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLE1BQU1FLDZCQUE2QixHQUFHLEdBQUc7O0FBRXpDO0FBQ0EsTUFBTUMsNEJBQTRCLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJOztBQUVsRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNlLE1BQU1DLFlBQVksQ0FBQztFQVF2QkMsV0FBV0EsQ0FDR0MsTUFBYyxFQUNkQyxRQUFrQixFQUNyQztJQUFBLElBQUFDLGdCQUFBLENBQUFDLE9BQUE7SUFBQSxJQUFBRCxnQkFBQSxDQUFBQyxPQUFBO0lBQUEsSUFBQUQsZ0JBQUEsQ0FBQUMsT0FBQSxtQ0FSeUMsRUFBRTtJQUFBLElBQUFELGdCQUFBLENBQUFDLE9BQUEsd0NBQ0csRUFBRTtJQUFBLElBQUFELGdCQUFBLENBQUFDLE9BQUEsdUJBQzVCLENBQUM7SUFBQSxJQUFBRCxnQkFBQSxDQUFBQyxPQUFBLHVCQUNELENBQUM7SUFBQSxJQUFBRCxnQkFBQSxDQUFBQyxPQUFBLG1DQWdJWUMsQ0FBUSxJQUFXO01BQ2xELElBQUksSUFBSSxDQUFDSCxRQUFRLENBQUNJLGVBQWUsS0FBSyxRQUFRLEVBQUU7UUFDNUMsSUFBSSxDQUFDQyxnQkFBZ0IsQ0FBQ0MsS0FBSyxDQUFDLENBQUM7UUFDN0IsSUFBSSxDQUFDQyxxQkFBcUIsQ0FBQ0QsS0FBSyxDQUFDLENBQUM7TUFDdEMsQ0FBQyxNQUFNO1FBQ0gsSUFBSSxDQUFDRSxjQUFjLENBQUNMLENB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