matrix-react-sdk
Version:
SDK for matrix.org using React
473 lines (375 loc) • 53.6 kB
JavaScript
"use strict";
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.Notifier = void 0;
var _MatrixClientPeg = require("./MatrixClientPeg");
var _SdkConfig = _interopRequireDefault(require("./SdkConfig"));
var _PlatformPeg = _interopRequireDefault(require("./PlatformPeg"));
var TextForEvent = _interopRequireWildcard(require("./TextForEvent"));
var _Analytics = _interopRequireDefault(require("./Analytics"));
var Avatar = _interopRequireWildcard(require("./Avatar"));
var _dispatcher = _interopRequireDefault(require("./dispatcher/dispatcher"));
var sdk = _interopRequireWildcard(require("./index"));
var _languageHandler = require("./languageHandler");
var _Modal = _interopRequireDefault(require("./Modal"));
var _SettingsStore = _interopRequireDefault(require("./settings/SettingsStore"));
var _DesktopNotificationsToast = require("./toasts/DesktopNotificationsToast");
var _SettingLevel = require("./settings/SettingLevel");
var _NotificationControllers = require("./settings/controllers/NotificationControllers");
var _RoomViewStore = _interopRequireDefault(require("./stores/RoomViewStore"));
var _UserActivity = _interopRequireDefault(require("./UserActivity"));
var _Media = require("./customisations/Media");
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
* Dispatches:
* {
* action: "notifier_enabled",
* value: boolean
* }
*/
const MAX_PENDING_ENCRYPTED = 20;
/*
Override both the content body and the TextForEvent handler for specific msgtypes, in notifications.
This is useful when the content body contains fallback text that would explain that the client can't handle a particular
type of tile.
*/
const typehandlers = {
"m.key.verification.request": event => {
const name = (event.sender || {}).name;
return (0, _languageHandler._t)("%(name)s is requesting verification", {
name
});
}
};
const Notifier = {
notifsByRoom: {},
// A list of event IDs that we've received but need to wait until
// they're decrypted until we decide whether to notify for them
// or not
pendingEncryptedEventIds: [],
notificationMessageForEvent: function (ev
/*: MatrixEvent*/
) {
if (typehandlers.hasOwnProperty(ev.getContent().msgtype)) {
return typehandlers[ev.getContent().msgtype](ev);
}
return TextForEvent.textForEvent(ev);
},
_displayPopupNotification: function (ev
/*: MatrixEvent*/
, room
/*: Room*/
) {
const plaf = _PlatformPeg.default.get();
if (!plaf) {
return;
}
if (!plaf.supportsNotifications() || !plaf.maySendNotifications()) {
return;
}
if (global.document.hasFocus()) {
return;
}
let msg = this.notificationMessageForEvent(ev);
if (!msg) return;
let title;
if (!ev.sender || room.name === ev.sender.name) {
title = room.name; // notificationMessageForEvent includes sender,
// but we already have the sender here
if (ev.getContent().body && !typehandlers.hasOwnProperty(ev.getContent().msgtype)) {
msg = ev.getContent().body;
}
} else if (ev.getType() === 'm.room.member') {
// context is all in the message here, we don't need
// to display sender info
title = room.name;
} else if (ev.sender) {
title = ev.sender.name + " (" + room.name + ")"; // notificationMessageForEvent includes sender,
// but we've just out sender in the title
if (ev.getContent().body && !typehandlers.hasOwnProperty(ev.getContent().msgtype)) {
msg = ev.getContent().body;
}
}
if (!this.isBodyEnabled()) {
msg = '';
}
let avatarUrl = null;
if (ev.sender && !_SettingsStore.default.getValue("lowBandwidth")) {
avatarUrl = Avatar.avatarUrlForMember(ev.sender, 40, 40, 'crop');
}
const notif = plaf.displayNotification(title, msg, avatarUrl, room); // if displayNotification returns non-null, the platform supports
// clearing notifications later, so keep track of this.
if (notif) {
if (this.notifsByRoom[ev.getRoomId()] === undefined) this.notifsByRoom[ev.getRoomId()] = [];
this.notifsByRoom[ev.getRoomId()].push(notif);
}
},
getSoundForRoom: function (roomId
/*: string*/
) {
// We do no caching here because the SDK caches setting
// and the browser will cache the sound.
const content = _SettingsStore.default.getValue("notificationSound", roomId);
if (!content) {
return null;
}
if (!content.url) {
console.warn(`${roomId} has custom notification sound event, but no url key`);
return null;
}
if (!content.url.startsWith("mxc://")) {
console.warn(`${roomId} has custom notification sound event, but url is not a mxc url`);
return null;
} // Ideally in here we could use MSC1310 to detect the type of file, and reject it.
return {
url: (0, _Media.mediaFromMxc)(content.url).srcHttp,
name: content.name,
type: content.type,
size: content.size
};
},
_playAudioNotification: async function (ev
/*: MatrixEvent*/
, room
/*: Room*/
) {
const sound = this.getSoundForRoom(room.roomId);
console.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`);
try {
const selector = document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio");
let audioElement = selector;
if (!selector) {
if (!sound) {
console.error("No audio element or sound to play for notification");
return;
}
audioElement = new Audio(sound.url);
if (sound.type) {
audioElement.type = sound.type;
}
document.body.appendChild(audioElement);
}
await audioElement.play();
} catch (ex) {
console.warn("Caught error when trying to fetch room notification sound:", ex);
}
},
start: function () {
// do not re-bind in the case of repeated call
this.boundOnEvent = this.boundOnEvent || this.onEvent.bind(this);
this.boundOnSyncStateChange = this.boundOnSyncStateChange || this.onSyncStateChange.bind(this);
this.boundOnRoomReceipt = this.boundOnRoomReceipt || this.onRoomReceipt.bind(this);
this.boundOnEventDecrypted = this.boundOnEventDecrypted || this.onEventDecrypted.bind(this);
_MatrixClientPeg.MatrixClientPeg.get().on('event', this.boundOnEvent);
_MatrixClientPeg.MatrixClientPeg.get().on('Room.receipt', this.boundOnRoomReceipt);
_MatrixClientPeg.MatrixClientPeg.get().on('Event.decrypted', this.boundOnEventDecrypted);
_MatrixClientPeg.MatrixClientPeg.get().on("sync", this.boundOnSyncStateChange);
this.toolbarHidden = false;
this.isSyncing = false;
},
stop: function () {
if (_MatrixClientPeg.MatrixClientPeg.get()) {
_MatrixClientPeg.MatrixClientPeg.get().removeListener('Event', this.boundOnEvent);
_MatrixClientPeg.MatrixClientPeg.get().removeListener('Room.receipt', this.boundOnRoomReceipt);
_MatrixClientPeg.MatrixClientPeg.get().removeListener('Event.decrypted', this.boundOnEventDecrypted);
_MatrixClientPeg.MatrixClientPeg.get().removeListener('sync', this.boundOnSyncStateChange);
}
this.isSyncing = false;
},
supportsDesktopNotifications: function () {
const plaf = _PlatformPeg.default.get();
return plaf && plaf.supportsNotifications();
},
setEnabled: function (enable
/*: boolean*/
, callback
/*: () => void*/
) {
const plaf = _PlatformPeg.default.get();
if (!plaf) return; // Dev note: We don't set the "notificationsEnabled" setting to true here because it is a
// calculated value. It is determined based upon whether or not the master rule is enabled
// and other flags. Setting it here would cause a circular reference.
_Analytics.default.trackEvent('Notifier', 'Set Enabled', String(enable)); // make sure that we persist the current setting audio_enabled setting
// before changing anything
if (_SettingsStore.default.isLevelSupported(_SettingLevel.SettingLevel.DEVICE)) {
_SettingsStore.default.setValue("audioNotificationsEnabled", null, _SettingLevel.SettingLevel.DEVICE, this.isEnabled());
}
if (enable) {
// Attempt to get permission from user
plaf.requestNotificationPermission().then(result => {
if (result !== 'granted') {
// The permission request was dismissed or denied
// TODO: Support alternative branding in messaging
const brand = _SdkConfig.default.get().brand;
const description = result === 'denied' ? (0, _languageHandler._t)('%(brand)s does not have permission to send you notifications - ' + 'please check your browser settings', {
brand
}) : (0, _languageHandler._t)('%(brand)s was not given permission to send notifications - please try again', {
brand
});
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
_Modal.default.createTrackedDialog('Unable to enable Notifications', result, ErrorDialog, {
title: (0, _languageHandler._t)('Unable to enable Notifications'),
description
});
return;
}
if (callback) callback();
_dispatcher.default.dispatch({
action: "notifier_enabled",
value: true
});
});
} else {
_dispatcher.default.dispatch({
action: "notifier_enabled",
value: false
});
} // set the notifications_hidden flag, as the user has knowingly interacted
// with the setting we shouldn't nag them any further
this.setPromptHidden(true);
},
isEnabled: function () {
return this.isPossible() && _SettingsStore.default.getValue("notificationsEnabled");
},
isPossible: function () {
const plaf = _PlatformPeg.default.get();
if (!plaf) return false;
if (!plaf.supportsNotifications()) return false;
if (!plaf.maySendNotifications()) return false;
return true; // possible, but not necessarily enabled
},
isBodyEnabled: function () {
return this.isEnabled() && _SettingsStore.default.getValue("notificationBodyEnabled");
},
isAudioEnabled: function () {
// We don't route Audio via the HTML Notifications API so it is possible regardless of other things
return _SettingsStore.default.getValue("audioNotificationsEnabled");
},
setPromptHidden: function (hidden
/*: boolean*/
, persistent = true) {
this.toolbarHidden = hidden;
_Analytics.default.trackEvent('Notifier', 'Set Toolbar Hidden', String(hidden));
(0, _DesktopNotificationsToast.hideToast)(); // update the info to localStorage for persistent settings
if (persistent && global.localStorage) {
global.localStorage.setItem("notifications_hidden", String(hidden));
}
},
shouldShowPrompt: function () {
const client = _MatrixClientPeg.MatrixClientPeg.get();
if (!client) {
return false;
}
const isGuest = client.isGuest();
return !isGuest && this.supportsDesktopNotifications() && !(0, _NotificationControllers.isPushNotifyDisabled)() && !this.isEnabled() && !this._isPromptHidden();
},
_isPromptHidden: function () {
// Check localStorage for any such meta data
if (global.localStorage) {
return global.localStorage.getItem("notifications_hidden") === "true";
}
return this.toolbarHidden;
},
onSyncStateChange: function (state
/*: string*/
) {
if (state === "SYNCING") {
this.isSyncing = true;
} else if (state === "STOPPED" || state === "ERROR") {
this.isSyncing = false;
}
},
onEvent: function (ev
/*: MatrixEvent*/
) {
if (!this.isSyncing) return; // don't alert for any messages initially
if (ev.sender && ev.sender.userId === _MatrixClientPeg.MatrixClientPeg.get().credentials.userId) return;
_MatrixClientPeg.MatrixClientPeg.get().decryptEventIfNeeded(ev); // If it's an encrypted event and the type is still 'm.room.encrypted',
// it hasn't yet been decrypted, so wait until it is.
if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) {
this.pendingEncryptedEventIds.push(ev.getId()); // don't let the list fill up indefinitely
while (this.pendingEncryptedEventIds.length > MAX_PENDING_ENCRYPTED) {
this.pendingEncryptedEventIds.shift();
}
return;
}
this._evaluateEvent(ev);
},
onEventDecrypted: function (ev
/*: MatrixEvent*/
) {
// 'decrypted' means the decryption process has finished: it may have failed,
// in which case it might decrypt soon if the keys arrive
if (ev.isDecryptionFailure()) return;
const idx = this.pendingEncryptedEventIds.indexOf(ev.getId());
if (idx === -1) return;
this.pendingEncryptedEventIds.splice(idx, 1);
this._evaluateEvent(ev);
},
onRoomReceipt: function (ev
/*: MatrixEvent*/
, room
/*: Room*/
) {
if (room.getUnreadNotificationCount() === 0) {
// ideally we would clear each notification when it was read,
// but we have no way, given a read receipt, to know whether
// the receipt comes before or after an event, so we can't
// do this. Instead, clear all notifications for a room once
// there are no notifs left in that room., which is not quite
// as good but it's something.
const plaf = _PlatformPeg.default.get();
if (!plaf) return;
if (this.notifsByRoom[room.roomId] === undefined) return;
for (const notif of this.notifsByRoom[room.roomId]) {
plaf.clearNotification(notif);
}
delete this.notifsByRoom[room.roomId];
}
},
_evaluateEvent: function (ev) {
const room = _MatrixClientPeg.MatrixClientPeg.get().getRoom(ev.getRoomId());
const actions = _MatrixClientPeg.MatrixClientPeg.get().getPushActionsForEvent(ev);
if (actions && actions.notify) {
if (_RoomViewStore.default.getRoomId() === room.roomId && _UserActivity.default.sharedInstance().userActiveRecently()) {
// don't bother notifying as user was recently active in this room
return;
}
if (_SettingsStore.default.getValue("doNotDisturb")) {
// Don't bother the user if they didn't ask to be bothered
return;
}
if (this.isEnabled()) {
this._displayPopupNotification(ev, room);
}
if (actions.tweaks.sound && this.isAudioEnabled()) {
_PlatformPeg.default.get().loudNotification(ev, room);
this._playAudioNotification(ev, room);
}
}
}
};
exports.Notifier = Notifier;
if (!window.mxNotifier) {
window.mxNotifier = Notifier;
}
var _default = window.mxNotifier;
exports.default = _default;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9Ob3RpZmllci50cyJdLCJuYW1lcyI6WyJNQVhfUEVORElOR19FTkNSWVBURUQiLCJ0eXBlaGFuZGxlcnMiLCJldmVudCIsIm5hbWUiLCJzZW5kZXIiLCJOb3RpZmllciIsIm5vdGlmc0J5Um9vbSIsInBlbmRpbmdFbmNyeXB0ZWRFdmVudElkcyIsIm5vdGlmaWNhdGlvbk1lc3NhZ2VGb3JFdmVudCIsImV2IiwiaGFzT3duUHJvcGVydHkiLCJnZXRDb250ZW50IiwibXNndHlwZSIsIlRleHRGb3JFdmVudCIsInRleHRGb3JFdmVudCIsIl9kaXNwbGF5UG9wdXBOb3RpZmljYXRpb24iLCJyb29tIiwicGxhZiIsIlBsYXRmb3JtUGVnIiwiZ2V0Iiwic3VwcG9ydHNOb3RpZmljYXRpb25zIiwibWF5U2VuZE5vdGlmaWNhdGlvbnMiLCJnbG9iYWwiLCJkb2N1bWVudCIsImhhc0ZvY3VzIiwibXNnIiwidGl0bGUiLCJib2R5IiwiZ2V0VHlwZSIsImlzQm9keUVuYWJsZWQiLCJhdmF0YXJVcmwiLCJTZXR0aW5nc1N0b3JlIiwiZ2V0VmFsdWUiLCJBdmF0YXIiLCJhdmF0YXJVcmxGb3JNZW1iZXIiLCJub3RpZiIsImRpc3BsYXlOb3RpZmljYXRpb24iLCJnZXRSb29tSWQiLCJ1bmRlZmluZWQiLCJwdXNoIiwiZ2V0U291bmRGb3JSb29tIiwicm9vbUlkIiwiY29udGVudCIsInVybCIsImNvbnNvbGUiLCJ3YXJuIiwic3RhcnRzV2l0aCIsInNyY0h0dHAiLCJ0eXBlIiwic2l6ZSIsIl9wbGF5QXVkaW9Ob3RpZmljYXRpb24iLCJzb3VuZCIsImxvZyIsInNlbGVjdG9yIiwicXVlcnlTZWxlY3RvciIsImF1ZGlvRWxlbWVudCIsImVycm9yIiwiQXVkaW8iLCJhcHBlbmRDaGlsZCIsInBsYXkiLCJleCIsInN0YXJ0IiwiYm91bmRPbkV2ZW50Iiwib25FdmVudCIsImJpbmQiLCJib3VuZE9uU3luY1N0YXRlQ2hhbmdlIiwib25TeW5jU3RhdGVDaGFuZ2UiLCJib3VuZE9uUm9vbVJlY2VpcHQiLCJvblJvb21SZWNlaXB0IiwiYm91bmRPbkV2ZW50RGVjcnlwdGVkIiwib25FdmVudERlY3J5cHRlZCIsIk1hdHJpeENsaWVudFBlZyIsIm9uIiwidG9vbGJhckhpZGRlbiIsImlzU3luY2luZyIsInN0b3AiLCJyZW1vdmVMaXN0ZW5lciIsInN1cHBvcnRzRGVza3RvcE5vdGlmaWNhdGlvbnMiLCJzZXRFbmFibGVkIiwiZW5hYmxlIiwiY2FsbGJhY2siLCJBbmFseXRpY3MiLCJ0cmFja0V2ZW50IiwiU3RyaW5nIiwiaXNMZXZlbFN1cHBvcnRlZCIsIlNldHRpbmdMZXZlbCIsIkRFVklDRSIsInNldFZhbHVlIiwiaXNFbmFibGVkIiwicmVxdWVzdE5vdGlmaWNhdGlvblBlcm1pc3Npb24iLCJ0aGVuIiwicmVzdWx0IiwiYnJhbmQiLCJTZGtDb25maWciLCJkZXNjcmlwdGlvbiIsIkVycm9yRGlhbG9nIiwic2RrIiwiZ2V0Q29tcG9uZW50IiwiTW9kYWwiLCJjcmVhdGVUcmFja2VkRGlhbG9nIiwiZGlzIiwiZGlzcGF0Y2giLCJhY3Rpb24iLCJ2YWx1ZSIsInNldFByb21wdEhpZGRlbiIsImlzUG9zc2libGUiLCJpc0F1ZGlvRW5hYmxlZCIsImhpZGRlbiIsInBlcnNpc3RlbnQiLCJsb2NhbFN0b3JhZ2UiLCJzZXRJdGVtIiwic2hvdWxkU2hvd1Byb21wdCIsImNsaWVudCIsImlzR3Vlc3QiLCJfaXNQcm9tcHRIaWRkZW4iLCJnZXRJdGVtIiwic3RhdGUiLCJ1c2VySWQiLCJjcmVkZW50aWFscyIsImRlY3J5cHRFdmVudElmTmVlZGVkIiwiaXNCZWluZ0RlY3J5cHRlZCIsImlzRGVjcnlwdGlvbkZhaWx1cmUiLCJnZXRJZCIsImxlbmd0aCIsInNoaWZ0IiwiX2V2YWx1YXRlRXZlbnQiLCJpZHgiLCJpbmRleE9mIiwic3BsaWNlIiwiZ2V0VW5yZWFkTm90aWZpY2F0aW9uQ291bnQiLCJjbGVhck5vdGlmaWNhdGlvbiIsImdldFJvb20iLCJhY3Rpb25zIiwiZ2V0UHVzaEFjdGlvbnNGb3JFdmVudCIsIm5vdGlmeSIsIlJvb21WaWV3U3RvcmUiLCJVc2VyQWN0aXZpdHkiLCJzaGFyZWRJbnN0YW5jZSIsInVzZXJBY3RpdmVSZWNlbnRseSIsInR3ZWFrcyIsImxvdWROb3RpZmljYXRpb24iLCJ3aW5kb3ciLCJteE5vdGlmaWVyIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQXNCQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUF0Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQXVCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUVBLE1BQU1BLHFCQUFxQixHQUFHLEVBQTlCO0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFDQSxNQUFNQyxZQUFZLEdBQUc7QUFDakIsZ0NBQStCQyxLQUFELElBQVc7QUFDckMsVUFBTUMsSUFBSSxHQUFHLENBQUNELEtBQUssQ0FBQ0UsTUFBTixJQUFnQixFQUFqQixFQUFxQkQsSUFBbEM7QUFDQSxXQUFPLHlCQUFHLHFDQUFILEVBQTBDO0FBQUVBLE1BQUFBO0FBQUYsS0FBMUMsQ0FBUDtBQUNIO0FBSmdCLENBQXJCO0FBT08sTUFBTUUsUUFBUSxHQUFHO0FBQ3BCQyxFQUFBQSxZQUFZLEVBQUUsRUFETTtBQUdwQjtBQUNBO0FBQ0E7QUFDQUMsRUFBQUEsd0JBQXdCLEVBQUUsRUFOTjtBQVFwQkMsRUFBQUEsMkJBQTJCLEVBQUUsVUFBU0M7QUFBVDtBQUFBLElBQTBCO0FBQ25ELFFBQUlSLFlBQVksQ0FBQ1MsY0FBYixDQUE0QkQsRUFBRSxDQUFDRSxVQUFILEdBQWdCQyxPQUE1QyxDQUFKLEVBQTBEO0FBQ3RELGFBQU9YLFlBQVksQ0FBQ1EsRUFBRSxDQUFDRSxVQUFILEdBQWdC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Q0FBaEI7O0FBQ0EsUUFBSTJILE9BQU8sSUFBSUEsT0FBTyxDQUFDRSxNQUF2QixFQUErQjtBQUMzQixVQUFJQyx1QkFBY2xHLFNBQWQsT0FBOEJyQixJQUFJLENBQUN5QixNQUFuQyxJQUE2QytGLHNCQUFhQyxjQUFiLEdBQThCQyxrQkFBOUIsRUFBakQsRUFBcUc7QUFDakc7QUFDQTtBQUNIOztBQUNELFVBQUkzRyx1QkFBY0MsUUFBZCxDQUF1QixjQUF2QixDQUFKLEVBQTRDO0FBQ3hDO0FBQ0E7QUFDSDs7QUFFRCxVQUFJLEtBQUt3RCxTQUFMLEVBQUosRUFBc0I7QUFDbEIsYUFBS3pFLHlCQUFMLENBQStCTixFQUEvQixFQUFtQ08sSUFBbkM7QUFDSDs7QUFDRCxVQUFJb0gsT0FBTyxDQUFDTyxNQUFSLENBQWV4RixLQUFmLElBQXdCLEtBQUt1RCxjQUFMLEVBQTVCLEVBQW1EO0FBQy9DeEYsNkJBQVlDLEdBQVosR0FBa0J5SCxnQkFBbEIsQ0FBbUNuSSxFQUFuQyxFQUF1Q08sSUFBdkM7O0FBQ0EsYUFBS2tDLHNCQUFMLENBQTRCekMsRUFBNUIsRUFBZ0NPLElBQWhDO0FBQ0g7QUFDSjtBQUNKO0FBbFZtQixDQUFqQjs7O0FBcVZQLElBQUksQ0FBQzZILE1BQU0sQ0FBQ0MsVUFBWixFQUF3QjtBQUNwQkQsRUFBQUEsTUFBTSxDQUFDQyxVQUFQLEdBQW9CekksUUFBcEI7QUFDSDs7ZUFFY3dJLE1BQU0sQ0FBQ0MsVSIsInNvdXJjZXNDb250ZW50IjpbIi8qXG5Db3B5cmlnaHQgMjAxNSwgMjAxNiBPcGVuTWFya2V0IEx0ZFxuQ29weXJpZ2h0IDIwMTcgVmVjdG9yIENyZWF0aW9ucyBMdGRcbkNvcHlyaWdodCAyMDE3IE5ldyBWZWN0b3IgTHRkXG5Db3B5cmlnaHQgMjAyMCBUaGUgTWF0cml4Lm9yZyBGb3VuZGF0aW9uIEMuSS5DLlxuXG5MaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xueW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG5cbiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcblxuVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG5TZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG5saW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiovXG5cbmltcG9ydCB7IE1hdHJpeEV2ZW50IH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL21vZGVscy9ldmVudFwiO1xuaW1wb3J0IHsgUm9vbSB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9tb2RlbHMvcm9vbVwiO1xuXG5pbXBvcnQgeyBNYXRyaXhDbGllbnRQZWcgfSBmcm9tICcuL01hdHJpeENsaWVudFBlZyc7XG5pbXBvcnQgU2RrQ29uZmlnIGZyb20gJy4vU2RrQ29uZmlnJztcbmltcG9ydCBQbGF0Zm9ybVBlZyBmcm9tICcuL1BsYXRmb3JtUGVnJztcbmltcG9ydCAqIGFzIFRleHRGb3JFdmVudCBmcm9tICcuL1RleHRGb3JFdmVudCc7XG5pbXBvcnQgQW5hbHl0aWNzIGZyb20gJy4vQW5hbHl0aWNzJztcbmltcG9ydCAqIGFzIEF2YXRhciBmcm9tICcuL0F2YXRhcic7XG5pbXBvcnQgZGlzIGZyb20gJy4vZGlzcGF0Y2hlci9kaXNwYXRjaGVyJztcbmltcG9ydCAqIGFzIHNkayBmcm9tICcuL2luZGV4JztcbmltcG9ydCB7IF90IH0gZnJvbSAnLi9sYW5ndWFnZUhhbmRsZXInO1xuaW1wb3J0IE1vZGFsIGZyb20gJy4vTW9kYWwnO1xuaW1wb3J0IFNldHRpbmdzU3RvcmUgZnJvbSBcIi4vc2V0dGluZ3MvU2V0dGluZ3NTdG9yZVwiO1xuaW1wb3J0IHsgaGlkZVRvYXN0IGFzIGhpZGVOb3RpZmljYXRpb25zVG9hc3QgfSBmcm9tIFwiLi90b2FzdHMvRGVza3RvcE5vdGlmaWNhdGlvbnNUb2FzdFwiO1xuaW1wb3J0IHtTZXR0aW5nTGV2ZWx9IGZyb20gXCIuL3NldHRpbmdzL1NldHRpbmdMZXZlbFwiO1xuaW1wb3J0IHtpc1B1c2hOb3RpZnlEaXNhYmxlZH0gZnJvbSBcIi4vc2V0dGluZ3MvY29udHJvbGxlcnMvTm90aWZpY2F0aW9uQ29udHJvbGxlcnNcIjtcbmltcG9ydCBSb29tVmlld1N0b3JlIGZyb20gXCIuL3N0b3Jlcy9Sb29tVmlld1N0b3JlXCI7XG5pbXBvcnQgVXNlckFjdGl2aXR5IGZyb20gXCIuL1VzZXJBY3Rpdml0eVwiO1xuaW1wb3J0IHttZWRpYUZyb21NeGN9IGZyb20gXCIuL2N1c3RvbWlzYXRpb25zL01lZGlhXCI7XG5cbi8qXG4gKiBEaXNwYXRjaGVzOlxuICoge1xuICogICBhY3Rpb246IFwibm90aWZpZXJfZW5hYmxlZFwiLFxuICogICB2YWx1ZTogYm9vbGVhblxuICogfVxuICovXG5cbmNvbnN0IE1BWF9QRU5ESU5HX0VOQ1JZUFRFRCA9IDIwO1xuXG4vKlxuT3ZlcnJpZGUgYm90aCB0aGUgY29udGVudCBib2R5IGFuZCB0aGUgVGV4dEZvckV2ZW50IGhhbmRsZXIgZm9yIHNwZWNpZmljIG1zZ3R5cGVzLCBpbiBub3RpZmljYXRpb25zLlxuVGhpcyBpcyB1c2VmdWwgd2hlbiB0aGUgY29udGVudCBib2R5IGNvbnRhaW5zIGZhbGxiYWNrIHRleHQgdGhhdCB3b3VsZCBleHBsYWluIHRoYXQgdGhlIGNsaWVudCBjYW4ndCBoYW5kbGUgYSBwYXJ0aWN1bGFyXG50eXBlIG9mIHRpbGUuXG4qL1xuY29uc3QgdHlwZWhhbmRsZXJzID0ge1xuICAgIFwibS5rZXkudmVyaWZpY2F0aW9uLnJlcXVlc3RcIjogKGV2ZW50KSA9PiB7XG4gICAgICAgIGNvbnN0IG5hbWUgPSAoZXZlbnQuc2VuZGVyIHx8IHt9KS5uYW1lO1xuICAgICAgICByZXR1cm4gX3QoXCIlKG5hbWUpcyBpcyByZXF1ZXN0aW5nIHZlcmlmaWNhdGlvblwiLCB7IG5hbWUgfSk7XG4gICAgfSxcbn07XG5cbmV4cG9ydCBjb25zdCBOb3RpZmllciA9IHtcbiAgICBub3RpZnNCeVJvb206IHt9LFxuXG4gICAgLy8gQSBsaXN0IG9mIGV2ZW50IElEcyB0aGF0IHdlJ3ZlIHJlY2VpdmVkIGJ1dCBuZWVkIHRvIHdhaXQgdW50aWxcbiAgICAvLyB0aGV5J3JlIGRlY3J5cHRlZCB1bnRpbCB3ZSBkZWNpZGUgd2hldGhlciB0byBub3RpZnkgZm9yIHRoZW1cbiAgICAvLyBvciBub3RcbiAgICBwZW5kaW5nRW5jcnlwdGVkRXZlbnRJZHM6IFtdLFxuXG4gICAgbm90aWZpY2F0aW9uTWVzc2FnZUZvckV2ZW50OiBmdW5jdGlvbihldjogTWF0cml4RXZlbnQpIHtcbiAgICAgICAgaWYgKHR5cGVoYW5kbGVycy5oYXNPd25Qcm9wZXJ0eShldi5nZXRDb250ZW50KCkubXNndHlwZSkpIHtcbiAgICAgICAgICAgIHJldHVybiB0eXBlaGFuZGxlcnNbZXYuZ2V0Q29udGVudCgpLm1zZ3R5cGVdKGV2KTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gVGV4dEZvckV2ZW50LnRleHRGb3JFdmVudChldik7XG4gICAgfSxcblxuICAgIF9kaXNwbGF5UG9wdXBOb3RpZmljYXRpb246IGZ1bmN0aW9uKGV2OiBNYXRyaXhFdmVudCwgcm9vbTogUm9vbSkge1xuICAgICAgICBjb25zdCBwbGFmID0gUGxhdGZvcm1QZWcuZ2V0KCk7XG4gICAgICAgIGlmICghcGxhZikge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmICghcGxhZi5zdXBwb3J0c05vdGlmaWNhdGlvbnMoKSB8fCAhcGxhZi5tYXlTZW5kTm90aWZpY2F0aW9ucygpKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGdsb2JhbC5kb2N1bWVudC5oYXNGb2N1cygpKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBsZXQgbXNnID0gdGhpcy5ub3RpZmljYXRpb25NZXNzYWdlRm9yRXZlbnQoZXYpO1xuICAgICAgICBpZiAoIW1zZykgcmV0dXJuO1xuXG4gICAgICAgIGxldCB0aXRsZTtcbiAgICAgICAgaWYgKCFldi5zZW5kZXIgfHwgcm9vbS5uYW1lID09PSBldi5zZW5kZXIubmFtZSkge1xuICAgICAgICAgICAgdGl0bGUgPSByb29tLm5hbWU7XG4gICAgICAgICAgICAvLyBub3RpZmljYXRpb25NZXNzYWdlRm9yRXZlbnQgaW5jbHVkZXMgc2VuZGVyLFxuICAgICAgICAgICAgLy8gYnV0IHdlIGFscmVhZHkgaGF2ZSB0aGUgc2VuZGVyIGhlcmVcbiAgICAgICAgICAgIGlmIChldi5nZXRDb250ZW50KCkuYm9keSAmJiAhdHlwZWhhbmRsZXJzLmhhc093blByb3BlcnR5KGV2LmdldENvbnRlbnQoKS5tc2d0eXBlKSkge1xuICAgICAgICAgICAgICAgIG1zZyA9IGV2LmdldENvbnRlbnQoKS5ib2R5O1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKGV2LmdldFR5cGUoKSA9PT0gJ20ucm9vbS5tZW1iZXInKSB7XG4gICAgICAgICAgICAvLyBjb250ZXh0IGlzIGFsbCBpbiB0aGUgbWVzc2FnZSBoZXJlLCB3ZSBkb24ndCBuZWVkXG4gICAgICAgICAgICAvLyB0byBkaXNwbGF5IHNlbmRlciBpbmZvXG4gICAgICAgICAgICB0aXRsZSA9IHJvb20ubmFtZTtcbiAgICAgICAgfSBlbHNlIGlmIChldi5zZW5kZXIpIHtcbiAgICAgICAgICAgIHRpdGxlID0gZXYuc2VuZGVyLm5hbWUgKyBcIiAoXCIgKyByb29tLm5hbWUgKyBcIilcIjtcbiAgICAgICAgICAgIC8vIG5vdGlmaWNhdGlvbk1lc3NhZ2VGb3JFdmVudCBpbmNsdWRlcyBzZW5kZXIsXG4gICAgICAgICAgICAvLyBidXQgd2UndmUganVzdCBvdXQgc2VuZGVyIGluIHRoZSB0aXRsZVxuICAgICAgICAgICAgaWYgKGV2LmdldENvbnRlbnQoKS5ib2R5ICYmICF0eXBlaGFuZGxlcnMuaGFzT3duUHJvcGVydHkoZXYuZ2V0Q29udGVudCgpLm1zZ3R5cGUpKSB7XG4gICAgICAgICAgICAgICAgbXNnID0gZXYuZ2V0Q29udGVudCgpLmJvZHk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXRoaXMuaXNCb2R5RW5hYmxlZCgpKSB7XG4gICAgICAgICAgICBtc2cgPSAnJztcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBhdmF0YXJVcmwgPSBudWxsO1xuICAgICAgICBpZiAoZXYuc2VuZGVyICYmICFTZXR0aW5nc1N0b3JlLmdldFZhbHVlKFwibG93QmFuZHdpZHRoXCIpKSB7XG4gICAgICAgICAgICBhdmF0YXJVcmwgPSBBdmF0YXIuYXZhdGFyVXJsRm9yTWVtYmVyKGV2LnNlbmRlciwgNDAsIDQwLCAnY3JvcCcpO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3Qgbm90aWYgPSBwbGFmLmRpc3BsYXlOb3RpZmljYXRpb24odGl0bGUsIG1zZywgYXZhdGFyVXJsLCByb29tKTtcblxuICAgICAgICAvLyBpZiBkaXNwbGF5Tm90aWZpY2F0aW9uIHJldHVybnMgbm9uLW51bGwsICB0aGUgcGxhdGZvcm0gc3VwcG9ydHNcbiAgICAgICAgLy8gY2xlYXJpbmcgbm90aWZpY2F0aW9ucyBsYXRlciwgc28ga2VlcCB0cmFjayBvZiB0aGlzLlxuICAgICAgICBpZiAobm90aWYpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLm5vdGlmc0J5Um9vbVtldi5nZXRSb29tSWQoKV0gPT09IHVuZGVmaW5lZCkgdGhpcy5ub3RpZnNCeVJvb21bZXYuZ2V0Um9vbUlkKCldID0gW107XG4gICAgICAgICAgICB0aGlzLm5vdGlmc0J5Um9vbVtldi5nZXRSb29tSWQoKV0ucHVzaChub3RpZik7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgZ2V0U291bmRGb3JSb29tOiBmdW5jdGlvbihyb29tSWQ6IHN0cmluZykge1xuICAgICAgICAvLyBXZSBkbyBubyBjYWNoaW5nIGhlcmUgYmVjYXVzZSB0aGUgU0RLIGNhY2hlcyBzZXR0aW5nXG4gICAgICAgIC8vIGFuZCB0aGUgYnJvd3NlciB3aWxsIGNhY2hlIHRoZSBzb3VuZC5cbiAgICAgICAgY29uc3QgY29udGVudCA9IFNldHRpbmdzU3RvcmUuZ2V0VmFsdWUoXCJub3RpZmljYXRpb25Tb3VuZFwiLCByb29tSWQpO1xuICAgICAgICBpZiAoIWNvbnRlbnQpIHtcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFjb250ZW50LnVybCkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGAke3Jvb21JZH0gaGFzIGN1c3RvbSBub3RpZmljYXRpb24gc291bmQgZXZlbnQsIGJ1dCBubyB1cmwga2V5YCk7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghY29udGVudC51cmwuc3RhcnRzV2l0aChcIm14YzovL1wiKSkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKGAke3Jvb21JZH0gaGFzIGN1c3RvbSBub3RpZmljYXRpb24gc291bmQgZXZlbnQsIGJ1dCB1cmwgaXMgbm90IGEgbXhjIHVybGApO1xuICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBJZGVhbGx5IGluIGhlcmUgd2UgY291bGQgdXNlIE1TQzEzMTAgdG8gZGV0ZWN0IHRoZSB0eXBlIG9mIGZpbGUsIGFuZCByZWplY3QgaXQuXG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHVybDogbWVkaWFGcm9tTXhjKGNvbnRlbnQudXJsKS5zcmNIdHRwLFxuICAgICAgICAgICAgbmFtZTogY29udGVudC5uYW1lLFxuICAgICAgICAgICAgdHlwZTogY29udGVudC50eXBlLFxuICAgICAgICAgICAgc2l6ZTogY29udGVudC5zaXplLFxuICAgICAgICB9O1xuICAgIH0sXG5cbiAgICBfcGxheUF1ZGlvTm90aWZpY2F0aW9uOiBhc3luYyBmdW5jdGlvbihldjogTWF0cml4RXZlbnQsIHJvb206IFJvb20pIHtcbiAgICAgICAgY29uc3Qgc291bmQgPSB0aGlzLmdldFNvdW5kRm9yUm9vbShyb29tLnJvb21JZCk7XG4gICAgICAgIGNvbnNvbGUubG9nKGBHb3Qgc291bmQgJHtzb3VuZCAmJiBzb3VuZC5uYW1lIHx8IFwiZGVmYXVsdFwifSBmb3IgJHtyb29tLnJvb21JZH1gKTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3Qgc2VsZWN0b3IgPVxuICAgICAgICAgICAgICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3I8SFRNTEF1ZGlvRWxlbWVudD4oc291bmQgPyBgYXVkaW9bc3JjPScke3NvdW5kLnVybH0nXWAgOiBcIiNtZXNzYWdlQXVkaW9cIik7XG4gICAgICAgICAgICBsZXQgYXVkaW9FbGVtZW50ID0gc2VsZWN0b3I7XG4gICAgICAgICAgICBpZiAoIXNlbGVjdG9yKSB7XG4gICAgICAgICAgICAgICAgaWYgKCFzb3VuZCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFwiTm8gYXVkaW8gZWxlbWVudCBvciBzb3VuZCB0byBwbGF5IGZvciBub3RpZmljYXRpb25cIik7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYXVkaW9FbGVtZW50ID0gbmV3IEF1ZGlvKHNvdW5kLnVybCk7XG4gICAgICAgICAgICAgICAgaWYgKHNvdW5kLnR5cGUpIHtcbiAgICAgICAgICAgICAgICAgICAgYXVkaW9FbGVtZW50LnR5cGUgPSBzb3VuZC50eXBlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGF1ZGlvRWxlbWVudCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBhd2FpdCBhdWRpb0VsZW1lbnQucGxheSgpO1xuICAgICAgICB9IGNhdGNoIChleCkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKFwiQ2F1Z2h0IGVycm9yIHdoZW4gdHJ5aW5nIHRvIGZldGNoIHJvb20gbm90aWZpY2F0aW9uIHNvdW5kOlwiLCBleCk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgc3RhcnQ6IGZ1bmN0aW9uKCkge1xuICAgICAgICAvLyBkbyBub3QgcmUtYmluZCBpbiB0aGUgY2FzZSBvZiByZXBlYXRlZCBjYWxsXG4gICAgICAgIHRoaXMuYm91bmRPbkV2ZW50ID0gdGhpcy5ib3VuZE9uRXZlbnQgfHwgdGhpcy5vbkV2ZW50LmJpbmQodGhpcyk7XG4gICAgICAgIHRoaXMuYm91bmRPblN5bmNTdGF0ZUNoYW5nZSA9IHRoaXMuYm91bmRPblN5bmNTdGF0ZUNoYW5nZSB8fCB0aGlzLm9uU3luY1N0YXRlQ2hhbmdlLmJpbmQodGhpcyk7XG4gICAgICAgIHRoaXMuYm91bmRPblJvb21SZWNlaXB0ID0gdGhpcy5ib3VuZE9uUm9vbVJlY2VpcHQgfHwgdGhpcy5vblJvb21SZWNlaXB0LmJpbmQodGhpcyk7XG4gICAgICAgIHRoaXMuYm91bmRPbkV2ZW50RGVjcnlwdGVkID0gdGhpcy5ib3VuZE9uRXZlbnREZWNyeXB0ZWQgfHwgdGhpcy5vbkV2ZW50RGVjcnlwdGVkLmJpbmQodGhpcyk7XG5cbiAgICAgICAgTWF0cml4Q2xpZW50UGVnLmdldCgpLm9uKCdldmVudCcsIHRoaXMuYm91bmRPbkV2ZW50KTtcbiAgICAgICAgTWF0cml4Q2xpZW50UGVnLmdldCgpLm9uKCdSb29tLnJlY2VpcHQnLCB0aGlzLmJvdW5kT25Sb29tUmVjZWlwdCk7XG4gICAgICAgIE1hdHJpeENsaWVudFBlZy5nZXQoKS5vbignRXZlbnQuZGVjcnlwdGVkJywgdGhpcy5ib3VuZE9uRXZlbnREZWNyeXB0ZWQpO1xuICAgICAgICBNYXRyaXhDbGllbnRQZWcuZ2V0KCkub24oXCJzeW5jXCIsIHRoaXMuYm91bmRPblN5bmNTdGF0ZUNoYW5nZSk7XG4gICAgICAgIHRoaXMudG9vbGJhckhpZGRlbiA9IGZhbHNlO1xuICAgICAgICB0aGlzLmlzU3luY2luZyA9IGZhbHNlO1xuICAgIH0sXG5cbiAgICBzdG9wOiBmdW5jdGlvbigpIHtcbiAgICAgICAgaWYgKE1hdHJpeENsaWVudFBlZy5nZXQoKSkge1xuICAgICAgICAgICAgTWF0cml4Q2xpZW50UGVnLmdldCgpLnJlbW92ZUxpc3RlbmVyKCdFdmVudCcsIHRoaXMuYm91bmRPbkV2ZW50KTtcbiAgICAgICAgICAgIE1hdHJpeENsaWVudFBlZy5nZXQoKS5yZW1vdmVMaXN0ZW5lcignUm9vbS5yZWNlaXB0JywgdGhpcy5ib3VuZE9uUm9vbVJlY2VpcHQpO1xuICAgICAgICAgICAgTWF0cml4Q2xpZW50UGVnLmdldCgpLnJlbW92ZUxpc3RlbmVyKCdFdmVudC5kZWNyeXB0ZWQnLCB0aGlzLmJvdW5kT25FdmVudERlY3J5cHRlZCk7XG4gICAgICAgICAgICBNYXRyaXhDbGllbnRQZWcuZ2V0KCkucmVtb3ZlTGlzdGVuZXIoJ3N5bmMnLCB0aGlzLmJvdW5kT25TeW5jU3RhdGVDaGFuZ2UpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuaXNTeW5jaW5nID0gZmFsc2U7XG4gICAgfSxcblxuICAgIHN1cHBvcnRzRGVza3RvcE5vdGlmaWNhdGlvbnM6IGZ1bmN0aW9uKCkge1xuICAgICAgICBjb25zdCBwbGFmID0gUGxhdGZvcm1QZWcuZ2V0KCk7XG4gICAgICAgIHJldHVybiBwbGFmICYmIHBsYWYuc3VwcG9ydHNOb3RpZmljYXRpb25zKCk7XG4gICAgfSxcblxuICAgIHNldEVuYWJsZWQ6IGZ1bmN0aW9uKGVuYWJsZTogYm9vbGVhbiwgY2FsbGJhY2s/OiAoKSA9PiB2b2lkKSB7XG4gICAgICAgIGNvbnN0IHBsYWYgPSBQbGF0Zm9ybVBlZy5nZXQoKTtcbiAgICAgICAgaWYgKCFwbGFmKSByZXR1cm47XG5cbiAgICAgICAgLy8gRGV2IG5vdGU6IFdlIGRvbid0IHNldCB0aGUgXCJub3RpZmljYXRpb25zRW5hYmxlZFwiIHNldHRpbmcgdG8gdHJ1ZSBoZXJlIGJlY2F1c2UgaXQgaXMgYVxuICAgICAgICAvLyBjYWxjdWxhdGVkIHZhbHVlLiBJdCBpcyBkZXRlcm1pbmVkIGJhc2VkIHVwb24gd2hldGhlciBvciBub3QgdGhlIG1hc3RlciBydWxlIGlzIGVuYWJsZWRcbiAgICAgICAgLy8gYW5kIG90aGVyIGZsYWdzLiBTZXR0aW5nIGl0IGhlcmUgd291bGQgY2F1c2UgYSBjaXJjdWxhciByZWZlcmVuY2UuXG5cbiAgICAgICAgQW5hbHl0aWNzLnRyYWNrRXZlbnQoJ05vdGlmaWVyJywgJ1NldCBFbmFibGVkJywgU3RyaW5nKGVuYWJsZSkpO1xuXG4gICAgICAgIC8vIG1ha2Ugc3VyZSB0aGF0IHdlIHBlcnNpc3QgdGhlIGN1cnJlbnQgc2V0dGluZyBhdWRpb19lbmFibGVkIHNldHRpbmdcbiAgICAgICAgLy8gYmVmb3JlIGNoYW5naW5nIGFueXRoaW5nXG4gICAgICAgIGlmIChTZXR0aW5nc1N0b3JlLmlzTGV2ZWxTdXBwb3J0ZWQoU2V0dGluZ0xldmVsLkRFVklDRSkpIHtcbiAgICAgICAgICAgIFNldHRpbmdzU3RvcmUuc2V0VmFsdWUoXCJhdWRpb05vdGlmaWNhdGlvbnNFbmFibGVkXCIsIG51bGwsIFNldHRpbmdMZXZlbC5ERVZJQ0UsIHRoaXMuaXNFbmFibGVkKCkpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGVuYWJsZSkge1xuICAgICAgICAgICAgLy8gQXR0ZW1wdCB0byBnZXQgcGVybWlzc2lvbiBmcm9tIHVzZXJcbiAgICAgICAgICAgIHBsYWYucmVxdWVzdE5vdGlmaWNhdGlvblBlcm1pc3Npb24oKS50aGVuKChyZXN1bHQpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAocmVzdWx0ICE9PSAnZ3JhbnRlZCcpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gVGhlIHBlcm1pc3Npb24gcmVxdWVzdCB3YXMgZGlzbWlzc2VkIG9yIGRlbmllZFxuICAgICAgICAgICAgICAgICAgICAvLyBUT0RPOiBTdXBwb3J0IGFsdGVybmF0aXZlIGJyYW5kaW5nIGluIG1lc3NhZ2luZ1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBicmFuZCA9IFNka0NvbmZpZy5nZXQoKS5icmFuZDtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgZGVzY3JpcHRpb24gPSByZXN1bHQgPT09ICdkZW5pZWQnXG4gICAgICAgICAgICAgICAgICAgICAgICA/IF90KCclKGJyYW5kKXMgZG9lcyBub3QgaGF2ZSBwZXJtaXNzaW9uIHRvIHNlbmQgeW91IG5vdGlmaWNhdGlvbnMgLSAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAncGxlYXNlIGNoZWNrIHlvdXIgYnJvd3NlciBzZXR0aW5ncycsIHsgYnJhbmQgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIDogX3QoJyUoYnJhbmQpcyB3YXMgbm90IGdpdmVuIHBlcm1pc3Npb24gdG8gc2VuZCBub3RpZmljYXRpb25zIC0gcGxlYXNlIHRyeSBhZ2FpbicsIHsgYnJhbmQgfSk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IEVycm9yRGlhbG9nID0gc2RrLmdldENvbXBvbmVudCgnZGlhbG9ncy5FcnJvckRpYWxvZycpO1xuICAgICAgICAgICAgICAgICAgICBNb2RhbC5jcmVhdGVUcmFja2VkRGlhbG9nKCdVbmFibGUgdG8gZW5hYmxlIE5vdGlmaWNhdGlvbnMnLCByZXN1bHQsIEVycm9yRGlhbG9nLCB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aXRsZTogX3QoJ1VuYWJsZSB0byBlbmFibGUgTm90aWZpY2F0aW9ucycpLFxuICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb24sXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGNhbGxiYWNrKSBjYWxsYmFjaygpO1xuICAgICAgICAgICAgICAgIGRpcy5kaXNwYXRjaCh7XG4gICAgICAgICAgICAgICAgICAgIGFjdGlvbjogXCJub3RpZmllcl9lbmFibGVkXCIsXG4gICAgICAgICAgICAgICAgICAgIHZhbHVlOiB0cnVlLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBkaXMuZGlzcGF0Y2goe1xuICAgICAgICAgICAgICAgIGFjdGlvbjogXCJub3RpZmllcl9lbmFibGVkXCIsXG4gICAgICAgICAgICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gc2V0IHRoZSBub3RpZmljYXRpb25zX2hpZGRlbiBmbGFnLCBhcyB0aGUgdXNlciBoYXMga25vd2luZ2x5IGludGVyYWN0ZWRcbiAgICAgICAgLy8gd2l0aCB0aGUgc2V0dGluZyB3ZSBzaG91bGRuJ3QgbmFnIHRoZW0gYW55IGZ1cnRoZXJcbiAgICAgICAgdGhpcy5zZXRQcm9tcHRIaWRkZW4odHJ1ZSk7XG4gICAgfSxcblxuICAgIGlzRW5hYmxlZDogZnVuY3Rpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmlzUG9zc2libGUoKSAmJiBTZXR0aW5nc1N0b3JlLmdldFZhbHVlKFwibm90aWZpY2F0aW9uc0VuYWJsZWRcIik7XG4gICAgfSxcblxuICAgIGlzUG9zc2libGU6IGZ1bmN0aW9uKCkge1xuICAgICAgICBjb25zdCBwbGFmID0gUGxhdGZvcm1QZWcuZ2V0KCk7XG4gICAgICAgIGlmICghcGxhZikgcmV0dXJuIGZhbHNlO1xuICAgICAgICBpZiAoIXBsYWYuc3VwcG9ydHNOb3RpZmljYXRpb25zKCkpIHJldHVybiBmYWxzZTtcbiAgICAgICAgaWYgKCFwbGFmLm1heVNlbmROb3RpZmljYXRpb25zKCkpIHJldHVybiBmYWxzZTtcblxuICAgICAgICByZXR1cm4gdHJ1ZTsgLy8gcG9zc2libGUsIGJ1dCBub3QgbmVjZXNzYXJpbHkgZW5hYmxlZFxuICAgIH0sXG5cbiAgICBpc0JvZHlFbmFibGVkOiBmdW5jdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuaXNFbmFibGVkKCkgJiYgU2V0dGluZ3NTdG9yZS5nZXRWYWx1ZShcIm5vdGlmaWNhdGlvbkJvZHlFbmFibGVkXCIpO1xuICAgIH0sXG5cbiAgICBpc0F1ZGlvRW5hYmxlZDogZnVuY3Rpb24oKSB7XG4gICAgICAgIC8vIFdlIGRvbid0IHJvdXRlIEF1ZGlvIHZpYSB0aGUgSFRNTCBOb3RpZmljYXRpb25zIEFQSSBzbyBpdCBpcyBwb3NzaWJsZSByZWdhcmRsZXNzIG9mIG90aGVyIHRoaW5nc1xuICAgICAgICByZXR1cm4gU2V0dGluZ3NTdG9yZS5nZXRWYWx1ZShcImF1ZGlvTm90aWZpY2F0aW9uc0VuYWJsZWRcIik7XG4gICAgfSxcblxuICAgIHNldFByb21wdEhpZGRlbjogZnVuY3Rpb24oaGlkZGVuOiBib29sZWFuLCBwZXJzaXN0ZW50ID0gdHJ1ZSkge1xuICAgICAgICB0aGlzLnRvb2xiYXJIaWRkZW4gPSBoaWRkZW47XG5cbiAgICAgICAgQW5hbHl0aWNzLnRyYWNrRXZlbnQoJ05vdGlmaWVyJywgJ1NldCBUb29sYmFyIEhpZGRlbicsIFN0cmluZyhoaWRkZW4pKTtcblxuICAgICAgICBoaWRlTm90aWZpY2F0aW9uc1RvYXN0KCk7XG5cbiAgICAgICAgLy8gdXBkYXRlIHRoZSBpbmZvIHRvIGxvY2FsU3RvcmFnZSBmb3IgcGVyc2lzdGVudCBzZXR0aW5nc1xuICAgICAgICBpZiAocGVyc2lzdGVudCAmJiBnbG9iYWwubG9jYWxTdG9yYWdlKSB7XG4gICAgICAgICAgICBnbG9iYWwubG9jYWxTdG9yYWdlLnNldEl0ZW0oXCJub3RpZmljYXRpb25zX2hpZGRlblwiLCBTdHJpbmcoaGlkZGVuKSk7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgc2hvdWxkU2hvd1Byb21wdDogZnVuY3Rpb24oKSB7XG4gICAgICAgIGNvbnN0IGNsaWVudCA9IE1hdHJpeENsaWVudFBlZy5nZXQoKTtcbiAgICAgICAgaWYgKCFjbGllbnQpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBpc0d1ZXN0ID0gY2xpZW50LmlzR3Vlc3QoKTtcbiAgICAgICAgcmV0dXJuICFpc0d1ZXN0ICYmIHRoaXMuc3VwcG9ydHNEZXNrdG9wTm90aWZpY2F0aW9ucygpICYmICFpc1B1c2hOb3RpZnlEaXNhYmxlZCgpICYmXG4gICAgICAgICAgICAhdGhpcy5pc0VuYWJsZWQoKSAmJiAhdGhpcy5faXNQcm9tcHRIaWRkZW4oKTtcbiAgICB9LFxuXG4gICAgX2lzUHJvbXB0SGlkZGVuOiBmdW5jdGlvbigpIHtcbiAgICAgICAgLy8gQ2hlY2sgbG9jYWxTdG9yYWdlIGZvciBhbnkgc3VjaCBtZXRhIGRhdGFcbiAgICAgICAgaWYgKGdsb2JhbC5sb2NhbFN0b3JhZ2UpIHtcbiAgICAgICAgICAgIHJldHVybiBnbG9iYWwubG9jYWxTdG9yYWdlLmdldEl0ZW0oXCJub3RpZmljYXRpb25zX2hpZGRlblwiKSA9PT0gXCJ0cnVlXCI7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGhpcy50b29sYmFySGlkZGVuO1xuICAgIH0sXG5cbiAgICBvblN5bmNTdGF0ZUNoYW5nZTogZnVuY3Rpb24oc3RhdGU6IHN0cmluZykge1xuICAgICAgICBpZiAoc3RhdGUgPT09IFwiU1lOQ0lOR1wiKSB7XG4gICAgICAgICAgICB0aGlzLmlzU3luY2luZyA9IHRydWU7XG4gICAgICAgIH0gZWxzZSBpZiAoc3RhdGUgPT09IFwiU1RPUFBFRFwiIHx8IHN0YXRlID09PSBcIkVSUk9SXCIpIHtcbiAgICAgICAgICAgIHRoaXMuaXNTeW5jaW5nID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9LFxuXG4gICAgb25FdmVudDogZnVuY3Rpb24oZXY6IE1hdHJpeEV2ZW50KSB7XG4gICAgICAgIGlmICghdGhpcy5pc1N5bmNpbmcpIHJldHVybjsgLy8gZG9uJ3QgYWxlcnQgZm9yIGFueSBtZXNzYWdlcyBpbml0aWFsbHlcbiAgICAgICAgaWYgKGV2LnNlbmRlciAmJiBldi5zZW5kZXIudXNlcklkID09PSBNYXRyaXhDbGllbnRQZWcuZ2V0KCkuY3JlZGVudGlhbHMudXNlcklkKSByZXR1cm47XG5cbiAgICAgICAgTWF0cml4Q2xpZW50UGVnLmdldCgpLmRlY3J5cHRFdmVudElmTmVlZGVkKGV2KTtcblxuICAgICAgICAvLyBJZiBpdCdzIGFuIGVuY3J5cHRlZCBldmVudCBhbmQgdGhlIHR5cGUgaXMgc3RpbGwgJ20ucm9vbS5lbmNyeXB0ZWQnLFxuICAgICAgICAvLyBpdCBoYXNuJ3QgeWV0IGJlZW4gZGVjcnlwdGVkLCBzbyB3YWl0IHVudGlsIGl0IGlzLlxuICAgICAgICBpZiAoZXYuaXNCZWluZ0RlY3J5cHRlZCgpIHx8IGV2LmlzRGVjcnlwdGlvbkZhaWx1cmUoKSkge1xuICAgICAgICAgICAgdGhpcy5wZW5kaW5nRW5jcnlwdGVkRXZlbnRJZHMucHVzaChldi5nZXRJZCgpKTtcbiAgICAgICAgI