UNPKG

matrix-react-sdk

Version:
221 lines (209 loc) 28.8 kB
"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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