matrix-react-sdk
Version:
SDK for matrix.org using React
516 lines (502 loc) • 73.5 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 _react = _interopRequireDefault(require("react"));
var _matrix = require("matrix-js-sdk/src/matrix");
var _types = require("matrix-js-sdk/src/types");
var _languageHandler = require("../../../languageHandler");
var _FormattingUtils = require("../../../utils/FormattingUtils");
var _RoomInvite = require("../../../RoomInvite");
var _GenericEventListSummary = _interopRequireDefault(require("./GenericEventListSummary"));
var _RightPanelStorePhases = require("../../../stores/right-panel/RightPanelStorePhases");
var _ReactUtils = require("../../../utils/ReactUtils");
var _Layout = require("../../../settings/enums/Layout");
var _RightPanelStore = _interopRequireDefault(require("../../../stores/right-panel/RightPanelStore"));
var _AccessibleButton = _interopRequireDefault(require("./AccessibleButton"));
var _RoomContext = _interopRequireDefault(require("../../../contexts/RoomContext"));
/*
Copyright 2024 New Vector Ltd.
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 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 onPinnedMessagesClick = () => {
_RightPanelStore.default.instance.setCard({
phase: _RightPanelStorePhases.RightPanelPhases.PinnedMessages
}, false);
};
const TARGET_AS_DISPLAY_NAME_EVENTS = [_matrix.EventType.RoomMember];
var TransitionType = /*#__PURE__*/function (TransitionType) {
TransitionType["Joined"] = "joined";
TransitionType["Left"] = "left";
TransitionType["JoinedAndLeft"] = "joined_and_left";
TransitionType["LeftAndJoined"] = "left_and_joined";
TransitionType["InviteReject"] = "invite_reject";
TransitionType["InviteWithdrawal"] = "invite_withdrawal";
TransitionType["Invited"] = "invited";
TransitionType["Banned"] = "banned";
TransitionType["Unbanned"] = "unbanned";
TransitionType["Kicked"] = "kicked";
TransitionType["ChangedName"] = "changed_name";
TransitionType["ChangedAvatar"] = "changed_avatar";
TransitionType["NoChange"] = "no_change";
TransitionType["ServerAcl"] = "server_acl";
TransitionType["ChangedPins"] = "pinned_messages";
TransitionType["MessageRemoved"] = "message_removed";
TransitionType["HiddenEvent"] = "hidden_event";
return TransitionType;
}(TransitionType || {});
const SEP = ",";
class EventListSummary extends _react.default.Component {
shouldComponentUpdate(nextProps) {
// Update if
// - The number of summarised events has changed
// - or if the summary is about to toggle to become collapsed
// - or if there are fewEvents, meaning the child eventTiles are shown as-is
return nextProps.events.length !== this.props.events.length || nextProps.events.length < this.props.threshold || nextProps.layout !== this.props.layout;
}
/**
* Generate the text for users aggregated by their transition sequences (`eventAggregates`) where
* the sequences are ordered by `orderedTransitionSequences`.
* @param {object} eventAggregates a map of transition sequence to array of user display names
* or user IDs.
* @param {string[]} orderedTransitionSequences an array which is some ordering of
* `Object.keys(eventAggregates)`.
* @returns {string} the textual summary of the aggregated events that occurred.
*/
generateSummary(eventAggregates, orderedTransitionSequences) {
const summaries = orderedTransitionSequences.map(transitions => {
const userNames = eventAggregates[transitions];
const nameList = this.renderNameList(userNames);
const splitTransitions = transitions.split(SEP);
// Some neighbouring transitions are common, so canonicalise some into "pair"
// transitions
const canonicalTransitions = EventListSummary.getCanonicalTransitions(splitTransitions);
// Transform into consecutive repetitions of the same transition (like 5
// consecutive 'joined_and_left's)
const coalescedTransitions = EventListSummary.coalesceRepeatedTransitions(canonicalTransitions);
const descs = coalescedTransitions.map(t => {
return EventListSummary.getDescriptionForTransition(t.transitionType, userNames.length, t.repeats);
});
const desc = (0, _FormattingUtils.formatList)(descs);
return (0, _languageHandler._t)("timeline|summary|format", {
nameList,
transitionList: desc
});
});
if (!summaries) {
return null;
}
return (0, _ReactUtils.jsxJoin)(summaries, ", ");
}
/**
* @param {string[]} users an array of user display names or user IDs.
* @returns {string} a comma-separated list that ends with "and [n] others" if there are
* more items in `users` than `this.props.summaryLength`, which is the number of names
* included before "and [n] others".
*/
renderNameList(users) {
return (0, _FormattingUtils.formatList)(users, this.props.summaryLength);
}
/**
* Canonicalise an array of transitions such that some pairs of transitions become
* single transitions. For example an input ['joined','left'] would result in an output
* ['joined_and_left'].
* @param {string[]} transitions an array of transitions.
* @returns {string[]} an array of transitions.
*/
static getCanonicalTransitions(transitions) {
const modMap = {
[TransitionType.Joined]: {
after: TransitionType.Left,
newTransition: TransitionType.JoinedAndLeft
},
[TransitionType.Left]: {
after: TransitionType.Joined,
newTransition: TransitionType.LeftAndJoined
}
};
const res = [];
for (let i = 0; i < transitions.length; i++) {
const t = transitions[i];
const t2 = transitions[i + 1];
let transition = t;
if (i < transitions.length - 1 && modMap[t] && modMap[t].after === t2) {
transition = modMap[t].newTransition;
i++;
}
res.push(transition);
}
return res;
}
/**
* Transform an array of transitions into an array of transitions and how many times
* they are repeated consecutively.
*
* An array of 123 "joined_and_left" transitions, would result in:
* ```
* [{
* transitionType: "joined_and_left"
* repeats: 123
* }]
* ```
* @param {string[]} transitions the array of transitions to transform.
* @returns {object[]} an array of coalesced transitions.
*/
static coalesceRepeatedTransitions(transitions) {
const res = [];
for (const transition of transitions) {
if (res.length > 0 && res[res.length - 1].transitionType === transition) {
res[res.length - 1].repeats += 1;
} else {
res.push({
transitionType: transition,
repeats: 1
});
}
}
return res;
}
/**
* For a certain transition, t, describe what happened to the users that
* underwent the transition.
* @param {string} t the transition type.
* @param {number} userCount number of usernames
* @param {number} repeats the number of times the transition was repeated in a row.
* @returns {string} the written Human Readable equivalent of the transition.
*/
static getDescriptionForTransition(t, userCount, count) {
// The empty interpolations 'severalUsers' and 'oneUser'
// are there only to show translators to non-English languages
// that the verb is conjugated to plural or singular Subject.
let res;
switch (t) {
case TransitionType.Joined:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|joined_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|joined", {
oneUser: "",
count
});
break;
case TransitionType.Left:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|left_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|left", {
oneUser: "",
count
});
break;
case TransitionType.JoinedAndLeft:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|joined_and_left_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|joined_and_left", {
oneUser: "",
count
});
break;
case TransitionType.LeftAndJoined:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|rejoined_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|rejoined", {
oneUser: "",
count
});
break;
case TransitionType.InviteReject:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|rejected_invite_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|rejected_invite", {
oneUser: "",
count
});
break;
case TransitionType.InviteWithdrawal:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|invite_withdrawn_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|invite_withdrawn", {
oneUser: "",
count
});
break;
case TransitionType.Invited:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|invited_multiple", {
count
}) : (0, _languageHandler._t)("timeline|summary|invited", {
count
});
break;
case TransitionType.Banned:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|banned_multiple", {
count
}) : (0, _languageHandler._t)("timeline|summary|banned", {
count
});
break;
case TransitionType.Unbanned:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|unbanned_multiple", {
count
}) : (0, _languageHandler._t)("timeline|summary|unbanned", {
count
});
break;
case TransitionType.Kicked:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|kicked_multiple", {
count
}) : (0, _languageHandler._t)("timeline|summary|kicked", {
count
});
break;
case TransitionType.ChangedName:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|changed_name_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|changed_name", {
oneUser: "",
count
});
break;
case TransitionType.ChangedAvatar:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|changed_avatar_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|changed_avatar", {
oneUser: "",
count
});
break;
case TransitionType.NoChange:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|no_change_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|no_change", {
oneUser: "",
count
});
break;
case TransitionType.ServerAcl:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|server_acls_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|server_acls", {
oneUser: "",
count
});
break;
case TransitionType.ChangedPins:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|pinned_events_multiple", {
severalUsers: "",
count
}, {
a: sub => /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "link_inline",
onClick: onPinnedMessagesClick
}, sub)
}) : (0, _languageHandler._t)("timeline|summary|pinned_events", {
oneUser: "",
count
}, {
a: sub => /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "link_inline",
onClick: onPinnedMessagesClick
}, sub)
});
break;
case TransitionType.MessageRemoved:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|redacted_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|redacted", {
oneUser: "",
count
});
break;
case TransitionType.HiddenEvent:
res = userCount > 1 ? (0, _languageHandler._t)("timeline|summary|hidden_event_multiple", {
severalUsers: "",
count
}) : (0, _languageHandler._t)("timeline|summary|hidden_event", {
oneUser: "",
count
});
break;
}
return res ?? null;
}
static getTransitionSequence(events) {
return events.map(EventListSummary.getTransition);
}
/**
* Label a given membership event, `e`, where `getContent().membership` has
* changed for each transition allowed by the Matrix protocol. This attempts to
* label the membership changes that occur in `../../../TextForEvent.js`.
* @param {MatrixEvent} e the membership change event to label.
* @returns {string?} the transition type given to this event. This defaults to `null`
* if a transition is not recognised.
*/
static getTransition(e) {
if (e.mxEvent.isRedacted()) {
return TransitionType.MessageRemoved;
}
switch (e.mxEvent.getType()) {
case _matrix.EventType.RoomThirdPartyInvite:
// Handle 3pid invites the same as invites so they get bundled together
if (!(0, _RoomInvite.isValid3pidInvite)(e.mxEvent)) {
return TransitionType.InviteWithdrawal;
}
return TransitionType.Invited;
case _matrix.EventType.RoomServerAcl:
return TransitionType.ServerAcl;
case _matrix.EventType.RoomPinnedEvents:
return TransitionType.ChangedPins;
case _matrix.EventType.RoomMember:
switch (e.mxEvent.getContent().membership) {
case _types.KnownMembership.Invite:
return TransitionType.Invited;
case _types.KnownMembership.Ban:
return TransitionType.Banned;
case _types.KnownMembership.Join:
if (e.mxEvent.getPrevContent().membership === _types.KnownMembership.Join) {
if (e.mxEvent.getContent().displayname !== e.mxEvent.getPrevContent().displayname) {
return TransitionType.ChangedName;
} else if (e.mxEvent.getContent().avatar_url !== e.mxEvent.getPrevContent().avatar_url) {
return TransitionType.ChangedAvatar;
}
return TransitionType.NoChange;
} else {
return TransitionType.Joined;
}
case _types.KnownMembership.Leave:
if (e.mxEvent.getSender() === e.mxEvent.getStateKey()) {
if (e.mxEvent.getPrevContent().membership === _types.KnownMembership.Invite) {
return TransitionType.InviteReject;
}
return TransitionType.Left;
}
switch (e.mxEvent.getPrevContent().membership) {
case _types.KnownMembership.Invite:
return TransitionType.InviteWithdrawal;
case _types.KnownMembership.Ban:
return TransitionType.Unbanned;
// sender is not target and made the target leave, if not from invite/ban then this is a kick
default:
return TransitionType.Kicked;
}
default:
return null;
}
default:
// otherwise, assume this is a hidden event
return TransitionType.HiddenEvent;
}
}
getAggregate(userEvents) {
// A map of aggregate type to arrays of display names. Each aggregate type
// is a comma-delimited string of transitions, e.g. "joined,left,kicked".
// The array of display names is the array of users who went through that
// sequence during eventsToRender.
const aggregate = {
// $aggregateType : []:string
};
// A map of aggregate types to the indices that order them (the index of
// the first event for a given transition sequence)
const aggregateIndices = {
// $aggregateType : int
};
const users = Object.keys(userEvents);
users.forEach(userId => {
const firstEvent = userEvents[userId][0];
const displayName = firstEvent.displayName;
const seq = EventListSummary.getTransitionSequence(userEvents[userId]).join(SEP);
if (!aggregate[seq]) {
aggregate[seq] = [];
aggregateIndices[seq] = -1;
}
aggregate[seq].push(displayName);
if (aggregateIndices[seq] === -1 || firstEvent.index < aggregateIndices[seq]) {
aggregateIndices[seq] = firstEvent.index;
}
});
return {
names: aggregate,
indices: aggregateIndices
};
}
render() {
const eventsToRender = this.props.events;
// Map user IDs to latest Avatar Member. ES6 Maps are ordered by when the key was created,
// so this works perfectly for us to match event order whilst storing the latest Avatar Member
const latestUserAvatarMember = new Map();
// Object mapping user IDs to an array of IUserEvents
const userEvents = {};
eventsToRender.forEach((e, index) => {
const type = e.getType();
let userKey = e.getSender();
if (e.isState() && type === _matrix.EventType.RoomThirdPartyInvite) {
userKey = e.getContent().display_name;
} else if (e.isState() && type === _matrix.EventType.RoomMember) {
userKey = e.getStateKey();
} else if (e.isRedacted() && e.getUnsigned()?.redacted_because) {
userKey = e.getUnsigned().redacted_because.sender;
}
// Initialise a user's events
if (!userEvents[userKey]) {
userEvents[userKey] = [];
}
let displayName = userKey;
if (e.isRedacted()) {
const sender = this.context?.room?.getMember(userKey);
if (sender) {
displayName = sender.name;
latestUserAvatarMember.set(userKey, sender);
}
} else if (e.target && TARGET_AS_DISPLAY_NAME_EVENTS.includes(type)) {
displayName = e.target.name;
latestUserAvatarMember.set(userKey, e.target);
} else if (e.sender && type !== _matrix.EventType.RoomThirdPartyInvite) {
displayName = e.sender.name;
latestUserAvatarMember.set(userKey, e.sender);
}
userEvents[userKey].push({
mxEvent: e,
displayName,
index: index
});
});
const aggregate = this.getAggregate(userEvents);
// Sort types by order of lowest event index within sequence
const orderedTransitionSequences = Object.keys(aggregate.names).sort((seq1, seq2) => aggregate.indices[seq1] - aggregate.indices[seq2]);
return /*#__PURE__*/_react.default.createElement(_GenericEventListSummary.default, {
"data-testid": this.props["data-testid"],
events: this.props.events,
threshold: this.props.threshold,
onToggle: this.props.onToggle,
startExpanded: this.props.startExpanded,
children: this.props.children,
summaryMembers: [...latestUserAvatarMember.values()],
layout: this.props.layout,
summaryText: this.generateSummary(aggregate.names, orderedTransitionSequences)
});
}
}
exports.default = EventListSummary;
(0, _defineProperty2.default)(EventListSummary, "contextType", _RoomContext.default);
(0, _defineProperty2.default)(EventListSummary, "defaultProps", {
summaryLength: 1,
threshold: 3,
avatarsMaxLength: 5,
layout: _Layout.Layout.Group
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwicmVxdWlyZSIsIl9tYXRyaXgiLCJfdHlwZXMiLCJfbGFuZ3VhZ2VIYW5kbGVyIiwiX0Zvcm1hdHRpbmdVdGlscyIsIl9Sb29tSW52aXRlIiwiX0dlbmVyaWNFdmVudExpc3RTdW1tYXJ5IiwiX1JpZ2h0UGFuZWxTdG9yZVBoYXNlcyIsIl9SZWFjdFV0aWxzIiwiX0xheW91dCIsIl9SaWdodFBhbmVsU3RvcmUiLCJfQWNjZXNzaWJsZUJ1dHRvbiIsIl9Sb29tQ29udGV4dCIsIm9uUGlubmVkTWVzc2FnZXNDbGljayIsIlJpZ2h0UGFuZWxTdG9yZSIsImluc3RhbmNlIiwic2V0Q2FyZCIsInBoYXNlIiwiUmlnaHRQYW5lbFBoYXNlcyIsIlBpbm5lZE1lc3NhZ2VzIiwiVEFSR0VUX0FTX0RJU1BMQVlfTkFNRV9FVkVOVFMiLCJFdmVudFR5cGUiLCJSb29tTWVtYmVyIiwiVHJhbnNpdGlvblR5cGUiLCJTRVAiLCJFdmVudExpc3RTdW1tYXJ5IiwiUmVhY3QiLCJDb21wb25lbnQiLCJzaG91bGRDb21wb25lbnRVcGRhdGUiLCJuZXh0UHJvcHMiLCJldmVudHMiLCJsZW5ndGgiLCJwcm9wcyIsInRocmVzaG9sZCIsImxheW91dCIsImdlbmVyYXRlU3VtbWFyeSIsImV2ZW50QWdncmVnYXRlcyIsIm9yZGVyZWRUcmFuc2l0aW9uU2VxdWVuY2VzIiwic3VtbWFyaWVzIiwibWFwIiwidHJhbnNpdGlvbnMiLCJ1c2VyTmFtZXMiLCJuYW1lTGlzdCIsInJlbmRlck5hbWVMaXN0Iiwic3BsaXRUcmFuc2l0aW9ucyIsInNwbGl0IiwiY2Fub25pY2FsVHJhbnNpdGlvbnMiLCJnZXRDYW5vbmljYWxUcmFuc2l0aW9ucyIsImNvYWxlc2NlZFRyYW5zaXRpb25zIiwiY29hbGVzY2VSZXBlYXRlZFRyYW5zaXRpb25zIiwiZGVzY3MiLCJ0IiwiZ2V0RGVzY3JpcHRpb25Gb3JUcmFuc2l0aW9uIiwidHJhbnNpdGlvblR5cGUiLCJyZXBlYXRzIiwiZGVzYyIsImZvcm1hdExpc3QiLCJfdCIsInRyYW5zaXRpb25MaXN0IiwianN4Sm9pbiIsInVzZXJzIiwic3VtbWFyeUxlbmd0aCIsIm1vZE1hcCIsIkpvaW5lZCIsImFmdGVyIiwiTGVmdCIsIm5ld1RyYW5zaXRpb24iLCJKb2luZWRBbmRMZWZ0IiwiTGVmdEFuZEpvaW5lZCIsInJlcyIsImkiLCJ0MiIsInRyYW5zaXRpb24iLCJwdXNoIiwidXNlckNvdW50IiwiY291bnQiLCJzZXZlcmFsVXNlcnMiLCJvbmVVc2VyIiwiSW52aXRlUmVqZWN0IiwiSW52aXRlV2l0aGRyYXdhbCIsIkludml0ZWQiLCJCYW5uZWQiLCJVbmJhbm5lZCIsIktpY2tlZCIsIkNoYW5nZWROYW1lIiwiQ2hhbmdlZEF2YXRhciIsIk5vQ2hhbmdlIiwiU2VydmVyQWNsIiwiQ2hhbmdlZFBpbnMiLCJhIiwic3ViIiwiZGVmYXVsdCIsImNyZWF0ZUVsZW1lbnQiLCJraW5kIiwib25DbGljayIsIk1lc3NhZ2VSZW1vdmVkIiwiSGlkZGVuRXZlbnQiLCJnZXRUcmFuc2l0aW9uU2VxdWVuY2UiLCJnZXRUcmFuc2l0aW9uIiwiZSIsIm14RXZlbnQiLCJpc1JlZGFjdGVkIiwiZ2V0VHlwZSIsIlJvb21UaGlyZFBhcnR5SW52aXRlIiwiaXNWYWxpZDNwaWRJbnZpdGUiLCJSb29tU2VydmVyQWNsIiwiUm9vbVBpbm5lZEV2ZW50cyIsImdldENvbnRlbnQiLCJtZW1iZXJzaGlwIiwiS25vd25NZW1iZXJzaGlwIiwiSW52aXRlIiwiQmFuIiwiSm9pbiIsImdldFByZXZDb250ZW50IiwiZGlzcGxheW5hbWUiLCJhdmF0YXJfdXJsIiwiTGVhdmUiLCJnZXRTZW5kZXIiLCJnZXRTdGF0ZUtleSIsImdldEFnZ3JlZ2F0ZSIsInVzZXJFdmVudHMiLCJhZ2dyZWdhdGUiLCJhZ2dyZWdhdGVJbmRpY2VzIiwiT2JqZWN0Iiwia2V5cyIsImZvckVhY2giLCJ1c2VySWQiLCJmaXJzdEV2ZW50IiwiZGlzcGxheU5hbWUiLCJzZXEiLCJqb2luIiwiaW5kZXgiLCJuYW1lcyIsImluZGljZXMiLCJyZW5kZXIiLCJldmVudHNUb1JlbmRlciIsImxhdGVzdFVzZXJBdmF0YXJNZW1iZXIiLCJNYXAiLCJ0eXBlIiwidXNlcktleSIsImlzU3RhdGUiLCJkaXNwbGF5X25hbWUiLCJnZXRVbnNpZ25lZCIsInJlZGFjdGVkX2JlY2F1c2UiLCJzZW5kZXIiLCJjb250ZXh0Iiwicm9vbSIsImdldE1lbWJlciIsIm5hbWUiLCJzZXQiLCJ0YXJnZXQiLCJpbmNsdWRlcyIsInNvcnQiLCJzZXExIiwic2VxMiIsIm9uVG9nZ2xlIiwic3RhcnRFeHBhbmRlZCIsImNoaWxkcmVuIiwic3VtbWFyeU1lbWJlcnMiLCJ2YWx1ZXMiLCJzdW1tYXJ5VGV4dCIsImV4cG9ydHMiLCJfZGVmaW5lUHJvcGVydHkyIiwiUm9vbUNvbnRleHQiLCJhdmF0YXJzTWF4TGVuZ3RoIiwiTGF5b3V0IiwiR3JvdXAiXSwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy92aWV3cy9lbGVtZW50cy9FdmVudExpc3RTdW1tYXJ5LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuQ29weXJpZ2h0IDIwMjQgTmV3IFZlY3RvciBMdGQuXG5Db3B5cmlnaHQgMjAxOSwgMjAyMCBUaGUgTWF0cml4Lm9yZyBGb3VuZGF0aW9uIEMuSS5DLlxuQ29weXJpZ2h0IDIwMTkgTWljaGFlbCBUZWxhdHluc2tpIDw3dDNjaGd1eUBnbWFpbC5jb20+XG5Db3B5cmlnaHQgMjAxNiBPcGVuTWFya2V0IEx0ZFxuXG5TUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQUdQTC0zLjAtb25seSBPUiBHUEwtMy4wLW9ubHlcblBsZWFzZSBzZWUgTElDRU5TRSBmaWxlcyBpbiB0aGUgcmVwb3NpdG9yeSByb290IGZvciBmdWxsIGRldGFpbHMuXG4qL1xuXG5pbXBvcnQgUmVhY3QsIHsgQ29tcG9uZW50UHJvcHMsIFJlYWN0Tm9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgTWF0cml4RXZlbnQsIFJvb21NZW1iZXIsIEV2ZW50VHlwZSB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9tYXRyaXhcIjtcbmltcG9ydCB7IEtub3duTWVtYmVyc2hpcCB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy90eXBlc1wiO1xuXG5pbXBvcnQgeyBfdCB9IGZyb20gXCIuLi8uLi8uLi9sYW5ndWFnZUhhbmRsZXJcIjtcbmltcG9ydCB7IGZvcm1hdExpc3QgfSBmcm9tIFwiLi4vLi4vLi4vdXRpbHMvRm9ybWF0dGluZ1V0aWxzXCI7XG5pbXBvcnQgeyBpc1ZhbGlkM3BpZEludml0ZSB9IGZyb20gXCIuLi8uLi8uLi9Sb29tSW52aXRlXCI7XG5pbXBvcnQgR2VuZXJpY0V2ZW50TGlzdFN1bW1hcnkgZnJvbSBcIi4vR2VuZXJpY0V2ZW50TGlzdFN1bW1hcnlcIjtcbmltcG9ydCB7IFJpZ2h0UGFuZWxQaGFzZXMgfSBmcm9tIFwiLi4vLi4vLi4vc3RvcmVzL3JpZ2h0LXBhbmVsL1JpZ2h0UGFuZWxTdG9yZVBoYXNlc1wiO1xuaW1wb3J0IHsganN4Sm9pbiB9IGZyb20gXCIuLi8uLi8uLi91dGlscy9SZWFjdFV0aWxzXCI7XG5pbXBvcnQgeyBMYXlvdXQgfSBmcm9tIFwiLi4vLi4vLi4vc2V0dGluZ3MvZW51bXMvTGF5b3V0XCI7XG5pbXBvcnQgUmlnaHRQYW5lbFN0b3JlIGZyb20gXCIuLi8uLi8uLi9zdG9yZXMvcmlnaHQtcGFuZWwvUmlnaHRQYW5lbFN0b3JlXCI7XG5pbXBvcnQgQWNjZXNzaWJsZUJ1dHRvbiBmcm9tIFwiLi9BY2Nlc3NpYmxlQnV0dG9uXCI7XG5pbXBvcnQgUm9vbUNvbnRleHQgZnJvbSBcIi4uLy4uLy4uL2NvbnRleHRzL1Jvb21Db250ZXh0XCI7XG5cbmNvbnN0IG9uUGlubmVkTWVzc2FnZXNDbGljayA9ICgpOiB2b2lkID0+IHtcbiAgICBSaWdodFBhbmVsU3RvcmUuaW5zdGFuY2Uuc2V0Q2FyZCh7IHBoYXNlOiBSaWdodFBhbmVsUGhhc2VzLlBpbm5lZE1lc3NhZ2VzIH0sIGZhbHNlKTtcbn07XG5cbmNvbnN0IFRBUkdFVF9BU19ESVNQTEFZX05BTUVfRVZFTlRTID0gW0V2ZW50VHlwZS5Sb29tTWVtYmVyXTtcblxuaW50ZXJmYWNlIElQcm9wcyBleHRlbmRzIE9taXQ8Q29tcG9uZW50UHJvcHM8dHlwZW9mIEdlbmVyaWNFdmVudExpc3RTdW1tYXJ5PiwgXCJzdW1tYXJ5VGV4dFwiIHwgXCJzdW1tYXJ5TWVtYmVyc1wiPiB7XG4gICAgLy8gVGhlIG1heGltdW0gbnVtYmVyIG9mIG5hbWVzIHRvIHNob3cgaW4gZWl0aGVyIGVhY2ggc3VtbWFyeSBlLmcuIDIgd291bGQgcmVzdWx0IFwiQSwgQiBhbmQgMjM0IG90aGVycyBsZWZ0XCJcbiAgICBzdW1tYXJ5TGVuZ3RoPzogbnVtYmVyO1xuICAgIC8vIFRoZSBtYXhpbXVtIG51bWJlciBvZiBhdmF0YXJzIHRvIGRpc3BsYXkgaW4gdGhlIHN1bW1hcnlcbiAgICBhdmF0YXJzTWF4TGVuZ3RoPzogbnVtYmVyO1xuICAgIC8vIFRoZSBjdXJyZW50bHkgc2VsZWN0ZWQgbGF5b3V0XG4gICAgbGF5b3V0OiBMYXlvdXQ7XG59XG5cbmludGVyZmFjZSBJVXNlckV2ZW50cyB7XG4gICAgLy8gVGhlIG9yaWdpbmFsIGV2ZW50XG4gICAgbXhFdmVudDogTWF0cml4RXZlbnQ7XG4gICAgLy8gVGhlIGRpc3BsYXkgbmFtZSBvZiB0aGUgdXNlciAoaWYgbm90LCB0aGVuIHVzZXIgSUQpXG4gICAgZGlzcGxheU5hbWU6IHN0cmluZztcbiAgICAvLyBUaGUgb3JpZ2luYWwgaW5kZXggb2YgdGhlIGV2ZW50IGluIHRoaXMucHJvcHMuZXZlbnRzXG4gICAgaW5kZXg6IG51bWJlcjtcbn1cblxuZW51bSBUcmFuc2l0aW9uVHlwZSB7XG4gICAgSm9pbmVkID0gXCJqb2luZWRcIixcbiAgICBMZWZ0ID0gXCJsZWZ0XCIsXG4gICAgSm9pbmVkQW5kTGVmdCA9IFwiam9pbmVkX2FuZF9sZWZ0XCIsXG4gICAgTGVmdEFuZEpvaW5lZCA9IFwibGVmdF9hbmRfam9pbmVkXCIsXG4gICAgSW52aXRlUmVqZWN0ID0gXCJpbnZpdGVfcmVqZWN0XCIsXG4gICAgSW52aXRlV2l0aGRyYXdhbCA9IFwiaW52aXRlX3dpdGhkcmF3YWxcIixcbiAgICBJbnZpdGVkID0gXCJpbnZpdGVkXCIsXG4gICAgQmFubmVkID0gXCJiYW5uZWRcIixcbiAgICBVbmJhbm5lZCA9IFwidW5iYW5uZWRcIixcbiAgICBLaWNrZWQgPSBcImtpY2tlZFwiLFxuICAgIENoYW5nZWROYW1lID0gXCJjaGFuZ2VkX25hbWVcIixcbiAgICBDaGFuZ2VkQXZhdGFyID0gXCJjaGFuZ2VkX2F2YXRhclwiLFxuICAgIE5vQ2hhbmdlID0gXCJub19jaGFuZ2VcIixcbiAgICBTZXJ2ZXJBY2wgPSBcInNlcnZlcl9hY2xcIixcbiAgICBDaGFuZ2VkUGlucyA9IFwicGlubmVkX21lc3NhZ2VzXCIsXG4gICAgTWVzc2FnZVJlbW92ZWQgPSBcIm1lc3NhZ2VfcmVtb3ZlZFwiLFxuICAgIEhpZGRlbkV2ZW50ID0gXCJoaWRkZW5fZXZlbnRcIixcbn1cblxuY29uc3QgU0VQID0gXCIsXCI7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEV2ZW50TGlzdFN1bW1hcnkgZXh0ZW5kcyBSZWFjdC5Db21wb25lbnQ8XG4gICAgSVByb3BzICYgUmVxdWlyZWQ8UGljazxJUHJvcHMsIFwic3VtbWFyeUxlbmd0aFwiIHwgXCJ0aHJlc2hvbGRcIiB8IFwiYXZhdGFyc01heExlbmd0aFwiIHwgXCJsYXlvdXRcIj4+XG4+IHtcbiAgICBwdWJsaWMgc3RhdGljIGNvbnRleHRUeXBlID0gUm9vbUNvbnRleHQ7XG4gICAgcHVibGljIGRlY2xhcmUgY29udGV4dDogUmVhY3QuQ29udGV4dFR5cGU8dHlwZW9mIFJvb21Db250ZXh0PjtcblxuICAgIHB1YmxpYyBzdGF0aWMgZGVmYXVsdFByb3BzID0ge1xuICAgICAgICBzdW1tYXJ5TGVuZ3RoOiAxLFxuICAgICAgICB0aHJlc2hvbGQ6IDMsXG4gICAgICAgIGF2YXRhcnNNYXhMZW5ndGg6IDUsXG4gICAgICAgIGxheW91dDogTGF5b3V0Lkdyb3VwLFxuICAgIH07XG5cbiAgICBwdWJsaWMgc2hvdWxkQ29tcG9uZW50VXBkYXRlKG5leHRQcm9wczogSVByb3BzKTogYm9vbGVhbiB7XG4gICAgICAgIC8vIFVwZGF0ZSBpZlxuICAgICAgICAvLyAgLSBUaGUgbnVtYmVyIG9mIHN1bW1hcmlzZWQgZXZlbnRzIGhhcyBjaGFuZ2VkXG4gICAgICAgIC8vICAtIG9yIGlmIHRoZSBzdW1tYXJ5IGlzIGFib3V0IHRvIHRvZ2dsZSB0byBiZWNvbWUgY29sbGFwc2VkXG4gICAgICAgIC8vICAtIG9yIGlmIHRoZXJlIGFyZSBmZXdFdmVudHMsIG1lYW5pbmcgdGhlIGNoaWxkIGV2ZW50VGlsZXMgYXJlIHNob3duIGFzLWlzXG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICBuZXh0UHJvcHMuZXZlbnRzLmxlbmd0aCAhPT0gdGhpcy5wcm9wcy5ldmVudHMubGVuZ3RoIHx8XG4gICAgICAgICAgICBuZXh0UHJvcHMuZXZlbnRzLmxlbmd0aCA8IHRoaXMucHJvcHMudGhyZXNob2xkIHx8XG4gICAgICAgICAgICBuZXh0UHJvcHMubGF5b3V0ICE9PSB0aGlzLnByb3BzLmxheW91dFxuICAgICAgICApO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdlbmVyYXRlIHRoZSB0ZXh0IGZvciB1c2VycyBhZ2dyZWdhdGVkIGJ5IHRoZWlyIHRyYW5zaXRpb24gc2VxdWVuY2VzIChgZXZlbnRBZ2dyZWdhdGVzYCkgd2hlcmVcbiAgICAgKiB0aGUgc2VxdWVuY2VzIGFyZSBvcmRlcmVkIGJ5IGBvcmRlcmVkVHJhbnNpdGlvblNlcXVlbmNlc2AuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IGV2ZW50QWdncmVnYXRlcyBhIG1hcCBvZiB0cmFuc2l0aW9uIHNlcXVlbmNlIHRvIGFycmF5IG9mIHVzZXIgZGlzcGxheSBuYW1lc1xuICAgICAqIG9yIHVzZXIgSURzLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nW119IG9yZGVyZWRUcmFuc2l0aW9uU2VxdWVuY2VzIGFuIGFycmF5IHdoaWNoIGlzIHNvbWUgb3JkZXJpbmcgb2ZcbiAgICAgKiBgT2JqZWN0LmtleXMoZXZlbnRBZ2dyZWdhdGVzKWAuXG4gICAgICogQHJldHVybnMge3N0cmluZ30gdGhlIHRleHR1YWwgc3VtbWFyeSBvZiB0aGUgYWdncmVnYXRlZCBldmVudHMgdGhhdCBvY2N1cnJlZC5cbiAgICAgKi9cbiAgICBwcml2YXRlIGdlbmVyYXRlU3VtbWFyeShcbiAgICAgICAgZXZlbnRBZ2dyZWdhdGVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmdbXT4sXG4gICAgICAgIG9yZGVyZWRUcmFuc2l0aW9uU2VxdWVuY2VzOiBzdHJpbmdbXSxcbiAgICApOiBSZWFjdE5vZGUge1xuICAgICAgICBjb25zdCBzdW1tYXJpZXMgPSBvcmRlcmVkVHJhbnNpdGlvblNlcXVlbmNlcy5tYXAoKHRyYW5zaXRpb25zKSA9PiB7XG4gICAgICAgICAgICBjb25zdCB1c2VyTmFtZXMgPSBldmVudEFnZ3JlZ2F0ZXNbdHJhbnNpdGlvbnNdO1xuICAgICAgICAgICAgY29uc3QgbmFtZUxpc3QgPSB0aGlzLnJlbmRlck5hbWVMaXN0KHVzZXJOYW1lcyk7XG5cbiAgICAgICAgICAgIGNvbnN0IHNwbGl0VHJhbnNpdGlvbnMgPSB0cmFuc2l0aW9ucy5zcGxpdChTRVApIGFzIFRyYW5zaXRpb25UeXBlW107XG5cbiAgICAgICAgICAgIC8vIFNvbWUgbmVpZ2hib3VyaW5nIHRyYW5zaXRpb25zIGFyZSBjb21tb24sIHNvIGNhbm9uaWNhbGlzZSBzb21lIGludG8gXCJwYWlyXCJcbiAgICAgICAgICAgIC8vIHRyYW5zaXRpb25zXG4gICAgICAgICAgICBjb25zdCBjYW5vbmljYWxUcmFuc2l0aW9ucyA9IEV2ZW50TGlzdFN1bW1hcnkuZ2V0Q2Fub25pY2FsVHJhbnNpdGlvbnMoc3BsaXRUcmFuc2l0aW9ucyk7XG4gICAgICAgICAgICAvLyBUcmFuc2Zvcm0gaW50byBjb25zZWN1dGl2ZSByZXBldGl0aW9ucyBvZiB0aGUgc2FtZSB0cmFuc2l0aW9uIChsaWtlIDVcbiAgICAgICAgICAgIC8vIGNvbnNlY3V0aXZlICdqb2luZWRfYW5kX2xlZnQncylcbiAgICAgICAgICAgIGNvbnN0IGNvYWxlc2NlZFRyYW5zaXRpb25zID0gRXZlbnRMaXN0U3VtbWFyeS5jb2FsZXNjZVJlcGVhdGVkVHJhbnNpdGlvbnMoY2Fub25pY2FsVHJhbnNpdGlvbnMpO1xuXG4gICAgICAgICAgICBjb25zdCBkZXNjcyA9IGNvYWxlc2NlZFRyYW5zaXRpb25zLm1hcCgodCkgPT4ge1xuICAgICAgICAgICAgICAgIHJldHVybiBFdmVudExpc3RTdW1tYXJ5LmdldERlc2NyaXB0aW9uRm9yVHJhbnNpdGlvbih0LnRyYW5zaXRpb25UeXBlLCB1c2VyTmFtZXMubGVuZ3RoLCB0LnJlcGVhdHMpO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIGNvbnN0IGRlc2MgPSBmb3JtYXRMaXN0KGRlc2NzKTtcblxuICAgICAgICAgICAgcmV0dXJuIF90KFwidGltZWxpbmV8c3VtbWFyeXxmb3JtYXRcIiwgeyBuYW1lTGlzdCwgdHJhbnNpdGlvbkxpc3Q6IGRlc2MgfSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICghc3VtbWFyaWVzKSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBqc3hKb2luKHN1bW1hcmllcywgXCIsIFwiKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBAcGFyYW0ge3N0cmluZ1tdfSB1c2VycyBhbiBhcnJheSBvZiB1c2VyIGRpc3BsYXkgbmFtZXMgb3IgdXNlciBJRHMuXG4gICAgICogQHJldHVybnMge3N0cmluZ30gYSBjb21tYS1zZXBhcmF0ZWQgbGlzdCB0aGF0IGVuZHMgd2l0aCBcImFuZCBbbl0gb3RoZXJzXCIgaWYgdGhlcmUgYXJlXG4gICAgICogbW9yZSBpdGVtcyBpbiBgdXNlcnNgIHRoYW4gYHRoaXMucHJvcHMuc3VtbWFyeUxlbmd0aGAsIHdoaWNoIGlzIHRoZSBudW1iZXIgb2YgbmFtZXNcbiAgICAgKiBpbmNsdWRlZCBiZWZvcmUgXCJhbmQgW25dIG90aGVyc1wiLlxuICAgICAqL1xuICAgIHByaXZhdGUgcmVuZGVyTmFtZUxpc3QodXNlcnM6IHN0cmluZ1tdKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIGZvcm1hdExpc3QodXNlcnMsIHRoaXMucHJvcHMuc3VtbWFyeUxlbmd0aCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2Fub25pY2FsaXNlIGFuIGFycmF5IG9mIHRyYW5zaXRpb25zIHN1Y2ggdGhhdCBzb21lIHBhaXJzIG9mIHRyYW5zaXRpb25zIGJlY29tZVxuICAgICAqIHNpbmdsZSB0cmFuc2l0aW9ucy4gRm9yIGV4YW1wbGUgYW4gaW5wdXQgWydqb2luZWQnLCdsZWZ0J10gd291bGQgcmVzdWx0IGluIGFuIG91dHB1dFxuICAgICAqIFsnam9pbmVkX2FuZF9sZWZ0J10uXG4gICAgICogQHBhcmFtIHtzdHJpbmdbXX0gdHJhbnNpdGlvbnMgYW4gYXJyYXkgb2YgdHJhbnNpdGlvbnMuXG4gICAgICogQHJldHVybnMge3N0cmluZ1tdfSBhbiBhcnJheSBvZiB0cmFuc2l0aW9ucy5cbiAgICAgKi9cbiAgICBwcml2YXRlIHN0YXRpYyBnZXRDYW5vbmljYWxUcmFuc2l0aW9ucyh0cmFuc2l0aW9uczogVHJhbnNpdGlvblR5cGVbXSk6IFRyYW5zaXRpb25UeXBlW10ge1xuICAgICAgICBjb25zdCBtb2RNYXA6IFBhcnRpYWw8XG4gICAgICAgICAgICBSZWNvcmQ8XG4gICAgICAgICAgICAgICAgVHJhbnNpdGlvblR5cGUsXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBhZnRlcjogVHJhbnNpdGlvblR5cGU7XG4gICAgICAgICAgICAgICAgICAgIG5ld1RyYW5zaXRpb246IFRyYW5zaXRpb25UeXBlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgID5cbiAgICAgICAgPiA9IHtcbiAgICAgICAgICAgIFtUcmFuc2l0aW9uVHlwZS5Kb2luZWRdOiB7XG4gICAgICAgICAgICAgICAgYWZ0ZXI6IFRyYW5zaXRpb25UeXBlLkxlZnQsXG4gICAgICAgICAgICAgICAgbmV3VHJhbnNpdGlvbjogVHJhbnNpdGlvblR5cGUuSm9pbmVkQW5kTGVmdCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBbVHJhbnNpdGlvblR5cGUuTGVmdF06IHtcbiAgICAgICAgICAgICAgICBhZnRlcjogVHJhbnNpdGlvblR5cGUuSm9pbmVkLFxuICAgICAgICAgICAgICAgIG5ld1RyYW5zaXRpb246IFRyYW5zaXRpb25UeXBlLkxlZnRBbmRKb2luZWQsXG4gICAgICAgICAgICB9LFxuICAgICAgICB9O1xuICAgICAgICBjb25zdCByZXM6IFRyYW5zaXRpb25UeXBlW10gPSBbXTtcblxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRyYW5zaXRpb25zLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCB0ID0gdHJhbnNpdGlvbnNbaV07XG4gICAgICAgICAgICBjb25zdCB0MiA9IHRyYW5zaXRpb25zW2kgKyAxXTtcblxuICAgICAgICAgICAgbGV0IHRyYW5zaXRpb24gPSB0O1xuXG4gICAgICAgICAgICBpZiAoaSA8IHRyYW5zaXRpb25zLmxlbmd0aCAtIDEgJiYgbW9kTWFwW3RdICYmIG1vZE1hcFt0XSEuYWZ0ZXIgPT09IHQyKSB7XG4gICAgICAgICAgICAgICAgdHJhbnNpdGlvbiA9IG1vZE1hcFt0XSEubmV3VHJhbnNpdGlvbjtcbiAgICAgICAgICAgICAgICBpKys7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJlcy5wdXNoKHRyYW5zaXRpb24pO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVHJhbnNmb3JtIGFuIGFycmF5IG9mIHRyYW5zaXRpb25zIGludG8gYW4gYXJyYXkgb2YgdHJhbnNpdGlvbnMgYW5kIGhvdyBtYW55IHRpbWVzXG4gICAgICogdGhleSBhcmUgcmVwZWF0ZWQgY29uc2VjdXRpdmVseS5cbiAgICAgKlxuICAgICAqIEFuIGFycmF5IG9mIDEyMyBcImpvaW5lZF9hbmRfbGVmdFwiIHRyYW5zaXRpb25zLCB3b3VsZCByZXN1bHQgaW46XG4gICAgICogYGBgXG4gICAgICogW3tcbiAgICAgKiAgIHRyYW5zaXRpb25UeXBlOiBcImpvaW5lZF9hbmRfbGVmdFwiXG4gICAgICogICByZXBlYXRzOiAxMjNcbiAgICAgKiB9XVxuICAgICAqIGBgYFxuICAgICAqIEBwYXJhbSB7c3RyaW5nW119IHRyYW5zaXRpb25zIHRoZSBhcnJheSBvZiB0cmFuc2l0aW9ucyB0byB0cmFuc2Zvcm0uXG4gICAgICogQHJldHVybnMge29iamVjdFtdfSBhbiBhcnJheSBvZiBjb2FsZXNjZWQgdHJhbnNpdGlvbnMuXG4gICAgICovXG4gICAgcHJpdmF0ZSBzdGF0aWMgY29hbGVzY2VSZXBlYXRlZFRyYW5zaXRpb25zKHRyYW5zaXRpb25zOiBUcmFuc2l0aW9uVHlwZVtdKToge1xuICAgICAgICB0cmFuc2l0aW9uVHlwZTogVHJhbnNpdGlvblR5cGU7XG4gICAgICAgIHJlcGVhdHM6IG51bWJlcjtcbiAgICB9W10ge1xuICAgICAgICBjb25zdCByZXM6IHtcbiAgICAgICAgICAgIHRyYW5zaXRpb25UeXBlOiBUcmFuc2l0aW9uVHlwZTtcbiAgICAgICAgICAgIHJlcGVhdHM6IG51bWJlcjtcbiAgICAgICAgfVtdID0gW107XG5cbiAgICAgICAgZm9yIChjb25zdCB0cmFuc2l0aW9uIG9mIHRyYW5zaXRpb25zKSB7XG4gICAgICAgICAgICBpZiAocmVzLmxlbmd0aCA+IDAgJiYgcmVzW3Jlcy5sZW5ndGggLSAxXS50cmFuc2l0aW9uVHlwZSA9PT0gdHJhbnNpdGlvbikge1xuICAgICAgICAgICAgICAgIHJlc1tyZXMubGVuZ3RoIC0gMV0ucmVwZWF0cyArPSAxO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICByZXMucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgIHRyYW5zaXRpb25UeXBlOiB0cmFuc2l0aW9uLFxuICAgICAgICAgICAgICAgICAgICByZXBlYXRzOiAxLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRm9yIGEgY2VydGFpbiB0cmFuc2l0aW9uLCB0LCBkZXNjcmliZSB3aGF0IGhhcHBlbmVkIHRvIHRoZSB1c2VycyB0aGF0XG4gICAgICogdW5kZXJ3ZW50IHRoZSB0cmFuc2l0aW9uLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0IHRoZSB0cmFuc2l0aW9uIHR5cGUuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHVzZXJDb3VudCBudW1iZXIgb2YgdXNlcm5hbWVzXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IHJlcGVhdHMgdGhlIG51bWJlciBvZiB0aW1lcyB0aGUgdHJhbnNpdGlvbiB3YXMgcmVwZWF0ZWQgaW4gYSByb3cuXG4gICAgICogQHJldHVybnMge3N0cmluZ30gdGhlIHdyaXR0ZW4gSHVtYW4gUmVhZGFibGUgZXF1aXZhbGVudCBvZiB0aGUgdHJhbnNpdGlvbi5cbiAgICAgKi9cbiAgICBwcml2YXRlIHN0YXRpYyBnZXREZXNjcmlwdGlvbkZvclRyYW5zaXRpb24odDogVHJhbnNpdGlvblR5cGUsIHVzZXJDb3VudDogbnVtYmVyLCBjb3VudDogbnVtYmVyKTogUmVhY3ROb2RlIHwgbnVsbCB7XG4gICAgICAgIC8vIFRoZSBlbXB0eSBpbnRlcnBvbGF0aW9ucyAnc2V2ZXJhbFVzZXJzJyBhbmQgJ29uZVVzZXInXG4gICAgICAgIC8vIGFyZSB0aGVyZSBvbmx5IHRvIHNob3cgdHJhbnNsYXRvcnMgdG8gbm9uLUVuZ2xpc2ggbGFuZ3VhZ2VzXG4gICAgICAgIC8vIHRoYXQgdGhlIHZlcmIgaXMgY29uanVnYXRlZCB0byBwbHVyYWwgb3Igc2luZ3VsYXIgU3ViamVjdC5cbiAgICAgICAgbGV0IHJlczogUmVhY3ROb2RlIHwgdW5kZWZpbmVkO1xuICAgICAgICBzd2l0Y2ggKHQpIHtcbiAgICAgICAgICAgIGNhc2UgVHJhbnNpdGlvblR5cGUuSm9pbmVkOlxuICAgICAgICAgICAgICAgIHJlcyA9XG4gICAgICAgICAgICAgICAgICAgIHVzZXJDb3VudCA+IDFcbiAgICAgICAgICAgICAgICAgICAgICAgID8gX3QoXCJ0aW1lbGluZXxzdW1tYXJ5fGpvaW5lZF9tdWx0aXBsZVwiLCB7IHNldmVyYWxVc2VyczogXCJcIiwgY291bnQgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIDogX3QoXCJ0aW1lbGluZXxzdW1tYXJ5fGpvaW5lZFwiLCB7IG9uZVVzZXI6IFwiXCIsIGNvdW50IH0pO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBUcmFuc2l0aW9uVHlwZS5MZWZ0OlxuICAgICAgICAgICAgICAgIHJlcyA9XG4gICAgICAgICAgICAgICAgICAgIHVzZXJDb3VudCA+IDFcbiAgICAgICAgICAgICAgICAgICAgICAgID8gX3QoXCJ0aW1lbGluZXxzdW1tYXJ5fGxlZnRfbXVsdGlwbGVcIiwgeyBzZXZlcmFsVXNlcnM6IFwiXCIsIGNvdW50IH0pXG4gICAgICAgICAgICAgICAgICAgICAgICA6IF90KFwidGltZWxpbmV8c3VtbWFyeXxsZWZ0XCIsIHsgb25lVXNlcjogXCJcIiwgY291bnQgfSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFRyYW5zaXRpb25UeXBlLkpvaW5lZEFuZExlZnQ6XG4gICAgICAgICAgICAgICAgcmVzID1cbiAgICAgICAgICAgICAgICAgICAgdXNlckNvdW50ID4gMVxuICAgICAgICAgICAgICAgICAgICAgICAgPyBfdChcInRpbWVsaW5lfHN1bW1hcnl8am9pbmVkX2FuZF9sZWZ0X211bHRpcGxlXCIsIHsgc2V2ZXJhbFVzZXJzOiBcIlwiLCBjb3VudCB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgOiBfdChcInRpbWVsaW5lfHN1bW1hcnl8am9pbmVkX2FuZF9sZWZ0XCIsIHsgb25lVXNlcjogXCJcIiwgY291bnQgfSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFRyYW5zaXRpb25UeXBlLkxlZnRBbmRKb2luZWQ6XG4gICAgICAgICAgICAgICAgcmVzID1cbiAgICAgICAgICAgICAgICAgICAgdXNlckNvdW50ID4gMVxuICAgICAgICAgICAgICAgICAgICAgICAgPyBfdChcInRpbWVsaW5lfHN1bW1hcnl8cmVqb2luZWRfbXVsdGlwbGVcIiwgeyBzZXZlcmFsVXNlcnM6IFwiXCIsIGNvdW50IH0pXG4gICAgICAgICAgICAgICAgICAgICAgICA6IF90KFwidGltZWxpbmV8c3VtbWFyeXxyZWpvaW5lZFwiLCB7IG9uZVVzZXI6IFwiXCIsIGNvdW50IH0pO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBUcmFuc2l0aW9uVHlwZS5JbnZpdGVSZWplY3Q6XG4gICAgICAgICAgICAgICAgcmVzID1cbiAgICAgICAgICAgICAgICAgICAgdXNlckNvdW50ID4gMVxuICAgICAgICAgICAgICAgICAgICAgICAgPyBfdChcInRpbWVsaW5lfHN1bW1hcnl8cmVqZWN0ZWRfaW52aXRlX211bHRpcGxlXCIsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNldmVyYWxVc2VyczogXCJcIixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50LFxuICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgOiBfdChcInRpbWVsaW5lfHN1bW1hcnl8cmVqZWN0ZWRfaW52aXRlXCIsIHsgb25lVXNlcjogXCJcIiwgY291bnQgfSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFRyYW5zaXRpb25UeXBlLkludml0ZVdpdGhkcmF3YWw6XG4gICAgICAgICAgICAgICAgcmVzID1cbiAgICAgICAgICAgICAgICAgICAgdXNlckNvdW50ID4gMVxuICAgICAgICAgICAgICAgICAgICAgICAgPyBfdChcInRpbWVsaW5lfHN1bW1hcnl8aW52aXRlX3dpdGhkcmF3bl9tdWx0aXBsZVwiLCB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXZlcmFsVXNlcnM6IFwiXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3VudCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIDogX3QoXCJ0aW1lbGluZXxzdW1tYXJ5fGludml0ZV93aXRoZHJhd25cIiwgeyBvbmVVc2VyOiBcIlwiLCBjb3VudCB9KTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgVHJhbnNpdGlvblR5cGUuSW52aXRlZDpcbiAgICAgICAgICAgICAgICByZXMgPVxuICAgICAgICAgICAgICAgICAgICB1c2VyQ291bnQgPiAxXG4gICAgICAgICAgICAgICAgICAgICAgICA/IF90KFwidGltZWxpbmV8c3VtbWFyeXxpbnZpdGVkX211bHRpcGxlXCIsIHsgY291bnQgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIDogX3QoXCJ0aW1lbGluZXxzdW1tYXJ5fGludml0ZWRcIiwgeyBjb3VudCB9KTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgVHJhbnNpdGlvblR5cGUuQmFubmVkOlxuICAgICAgICAgICAgICAgIHJlcyA9XG4gICAgICAgICAgICAgICAgICAgIHVzZXJDb3VudCA+IDFcbiAgICAgICAgICAgICAgICAgICAgICAgID8gX3QoXCJ0aW1lbGluZXxzdW1tYXJ5fGJhbm5lZF9tdWx0aXBsZVwiLCB7IGNvdW50IH0pXG4gICAgICAgICAgICAgICAgICAgICAgICA6IF90KFwidGltZWxpbmV8c3VtbWFyeXxiYW5uZWRcIiwgeyBjb3VudCB9KTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgVHJhbnNpdGlvblR5cGUuVW5iYW5uZWQ6XG4gICAgICAgICAgICAgICAgcmVzID1cbiAgICAgICAgICAgICAgICAgICAgdXNlckNvdW50ID4gMVxuICAgICAgICAgICAgICAgICAgICAgICAgPyBfdChcInRpbWVsaW5lfHN1bW1hcnl8dW5iYW5uZWRfbXVsdGlwbGVcIiwgeyBjb3VudCB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgOiBfdChcInRpbWVsaW5lfHN1bW1hcnl8dW5iYW5uZWRcIiwgeyBjb3VudCB9KTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgVHJhbnNpdGlvblR5cGUuS2lja2VkOlxuICAgICAgICAgICAgICAgIHJlcyA9XG4gICAgICAgICAgICAgICAgICAgIHVzZXJDb3VudCA+IDFcbiAgICAgICAgICAgICAgICAgICAgICAgID8gX3QoXCJ0aW1lbGluZXxzdW1tYXJ5fGtpY2tlZF9tdWx0aXBsZVwiLCB7IGNvdW50IH0pXG4gICAgICAgICAgICAgICAgICAgICAgICA6IF90KFwidGltZWxpbmV8c3VtbWFyeXxraWNrZWRcIiwgeyBjb3VudCB9KTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgVHJhbnNpdGlvblR5cGUuQ2hhbmdlZE5hbWU6XG4gICAgICAgICAgICAgICAgcmVzID1cbiAgICAgICAgICAgICAgICAgICAgdXNlckNvdW50ID4gMVxuICAgICAgICAgICAgICAgICAgICAgICAgPyBfdChcInRpbWVsaW5lfHN1bW1hcnl8Y2hhbmdlZF9uYW1lX211bHRpcGxlXCIsIHsgc2V2ZXJhbFVzZXJzOiBcIlwiLCBjb3VudCB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgOiBfdChcInRpbWVsaW5lfHN1bW1hcnl8Y2hhbmdlZF9uYW1lXCIsIHsgb25lVXNlcjogXCJcIiwgY291bnQgfSk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFRyYW5zaXRpb25UeXBlLkNoYW5nZWRBdmF0YXI6XG4gICAgICAgICAgICAgICAgcmVzID1cbiAgICAgICAgICAgICAgICAgICAgdXNlckNvdW50ID4gMVxuICAgICAgICAgICAgICAgICAgICAgICAgPyBfdChcInRpbWVsaW5lfHN1bW1hcnl8Y2hhbmdlZF9hdmF0YXJfbXVsdGlwbGVcIiwge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V2ZXJhbFVzZXJzOiBcIlwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICA6IF90KFwidGltZWxpbmV8c3VtbWFyeXxjaGFuZ2VkX2F2YXRhclwiLCB7IG9uZVVzZXI6IFwiXCIsIGNvdW50IH0pO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBUcmFuc2l0aW9uVHlwZS5Ob0NoYW5nZTpcbiAgICAgICAgICAgICAgICByZXMgPVxuICAgICAgICAgICAgICAgICAgICB1c2VyQ291bnQgPiAxXG4gICAgICAgICAgICAgICAgICAgICAgICA/IF90KFwidGltZWxpbmV8c3VtbWFyeXxub19jaGFuZ2VfbXVsdGlwbGVcIiwgeyBzZXZlcmFsVXNlcnM6IFwiXCIsIGNvdW50IH0pXG4gICAgICAgICAgICAgICAgICAgICAgICA6IF90KFwidGltZWxpbmV8c3VtbWFyeXxub19jaGFuZ2VcIiwgeyBvbmVVc2VyOiBcIlwiLCBjb3VudCB9KTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgVHJhbnNpdGlvblR5cGUuU2VydmVyQWNsOlxuICAgICAgICAgICAgICAgIHJlcyA9XG4gICAgICAgICAgICAgICAgICAgIHVzZXJDb3VudCA+IDFcbiAgICAgICAgICAgICAgICAgICAgICAgID8gX3QoXCJ0aW1lbGluZXxzdW1tYXJ5fHNlcnZlcl9hY2xzX211bHRpcGxlXCIsIHsgc2V2ZXJhbFVzZXJzOiBcIlwiLCBjb3VudCB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgOiBfdChcInRpbWVsaW5lfHN1bW1hcnl8c2VydmVyX2FjbHNcIiwgeyBvbmVVc2VyOiBcIlwiLCBjb3VudCB9KTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgVHJhbnNpdGlvblR5cGUuQ2hhbmdlZFBpbnM6XG4gICAgICAgICAgICAgICAgcmVzID1cbiAgICAgICAgICAgICAgICAgICAgdXNlckNvdW50ID4gMVxuICAgICAgICAgICAgICAgICAgICAgICAgPyBfdChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwidGltZWxpbmV8c3VtbWFyeXxwaW5uZWRfZXZlbnRzX211bHRpcGxlXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7IHNldmVyYWxVc2VyczogXCJcIiwgY291bnQgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhOiAoc3ViKSA9PiAoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxBY2Nlc3NpYmxlQnV0dG9uIGtpbmQ9XCJsaW5rX2lubGluZVwiIG9uQ2xpY2s9e29uUGlubmVkTWVzc2FnZXNDbGlja30+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7c3VifVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L0FjY2Vzc2libGVCdXR0b24+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgICAgICAgIDogX3QoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcInRpbWVsaW5lfHN1bW1hcnl8cGlubmVkX2V2ZW50c1wiLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeyBvbmVVc2VyOiBcIlwiLCBjb3VudCB9LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGE6IChzdWIpID0+IChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPEFjY2Vzc2libGVCdXR0b24ga2luZD1cImxpbmtfaW5saW5lXCIgb25DbGljaz17b25QaW5uZWRNZXNzYWdlc0NsaWNrfT5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtzdWJ9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvQWNjZXNzaWJsZUJ1dHRvbj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgVHJhbnNpdGlvblR5cGUuTWVzc2FnZVJlbW92ZWQ6XG4gICAgICAgICAgICAgICAgcmVzID1cbiAgICAgICAgICAgICAgICAgICAgdXNlckNvdW50ID4gMVxuICAgICAgICAgICAgICAgICAgICAgICAgPyBfdChcInRpbWVsaW5lfHN1bW1hcnl8cmVkYWN0ZWRfbXVsdGlwbGVcIiwgeyBzZXZlcmFsVXNlcnM6IFwiXCIsIGNvdW50IH0pXG4gICAgICAgICAgICAgICAgICAgICAgICA6IF90KFwidGltZWxpbmV8c3VtbWFyeXxyZWRhY3RlZFwiLCB7IG9uZVVzZXI6IFwiXCIsIGNvdW50IH0pO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBUcmFuc2l0aW9uVHlwZS5IaWRkZW5FdmVudDpcbiAgICAgICAgICAgICAgICByZXMgPVxuICAgICAgICAgICAgICAgICAgICB1c2VyQ291bnQgPiAxXG4gICAgICAgICAgICAgICAgICAgICAgICA/IF90KFwidGltZWxpbmV8c3VtbWFyeXxoaWRkZW5fZXZlbnRfbXVsdGlwbGVcIiwgeyBzZXZlcmFsVXNlcnM6IFwiXCIsIGNvdW50IH0pXG4gICAgICAgICAgICAgICAgICAgICAgICA6IF90KFwidGltZWxpbmV8c3VtbWFyeXxoaWRkZW5fZXZlbnRcIiwgeyBvbmVVc2VyOiBcIlwiLCBjb3VudCB9KTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiByZXMgPz8gbnVsbDtcbiAgICB9XG5cbiAgICBwcml2YXRlIHN0YXRpYyBnZXRUcmFuc2l0aW9uU2VxdWVuY2UoZXZlbnRzOiBJVXNlckV2ZW50c1tdKTogQXJyYXk8VHJhbnNpdGlvblR5cGUgfCBudWxsPiB7XG4gICAgICAgIHJldHVybiBldmVudHMubWFwKEV2ZW50TGlzdFN1bW1hcnkuZ2V0VHJhbnNpdGlvbik7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTGFiZWwgYSBnaXZlbiBtZW1iZXJzaGlwIGV2ZW50LCBgZWAsIHdoZXJlIGBnZXRDb250ZW50KCkubWVtYmVyc2hpcGAgaGFzXG4gICAgICogY2hhbmdlZCBmb3IgZWFjaCB0cmFuc2l0aW9uIGFsbG93ZWQgYnkgdGhlIE1hdHJpeCBwcm90b2NvbC4gVGhpcyBhdHRlbXB0cyB0b1xuICAgICAqIGxhYmVsIHRoZSBtZW1iZXJzaGlwIGNoYW5nZXMgdGhhdCBvY2N1ciBpbiBgLi4vLi4vLi4vVGV4dEZvckV2ZW50LmpzYC5cbiAgICAgKiBAcGFyYW0ge01hdHJpeEV2ZW50fSBlIHRoZSBtZW1iZXJzaGlwIGNoYW5nZSBldmVudCB0byBsYWJlbC5cbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nP30gdGhlIHRyYW5zaXRpb24gdHlwZSBnaXZlbiB0byB0aGlzIGV2ZW50LiBUaGlzIGRlZmF1bHRzIHRvIGBudWxsYFxuICAgICAqIGlmIGEgdHJhbnNpdGlvbiBpcyBub3QgcmVjb2duaXNlZC5cbiAgICAgKi9cbiAgICBwcml2YXRlIHN0YXRpYyBnZXRUcmFuc2l0aW9uKGU6IElVc2VyRXZlbnRzKTogVHJhbnNpdGlvblR5cGUgfCBudWxsIHtcbiAgICAgICAgaWYgKGUubXhFdmVudC5pc1JlZGFjdGVkKCkpIHtcbiAgICAgICAgICAgIHJldHVybiBUcmFuc2l0aW9uVHlwZS5NZXNzYWdlUmVtb3ZlZDtcbiAgICAgICAgfVxuXG4gICAgICAgIHN3aXRjaCAoZS5teEV2ZW50LmdldFR5cGUoKSkge1xuICAgICAgICAgICAgY2FzZSBFdmVudFR5cGUuUm9vbVRoaXJkUGFydHlJbnZpdGU6XG4gICAgICAgICAgICAgICAgLy8gSGFuZGxlIDNwaWQgaW52aXRlcyB0aGUgc2FtZSBhcyBpbnZpdGVzIHNvIHRoZXkgZ2V0IGJ1bmRsZWQgdG9nZXRoZXJcbiAgICAgICAgICAgICAgICBpZiAoIWlzVmFsaWQzcGlkSW52aXRlKGUubXhFdmVudCkpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFRyYW5zaXRpb25UeXBlLkludml0ZVdpdGhkcmF3YWw7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybiBUcmFuc2l0aW9uVHlwZS5JbnZpdGVkO1xuXG4gICAgICAgICAgICBjYXNlIEV2ZW50VHlwZS5Sb29tU2VydmVyQWNsOlxuICAgICAgICAgICAgICAgIHJldHVybiBUcmFuc2l0aW9uVHlwZS5TZXJ2ZXJBY2w7XG5cbiAgICAgICAgICAgIGNhc2UgRXZlbnRUeXBlLlJvb21QaW5uZWRFdmVudHM6XG4gICAgICAgICAgICAgICAgcmV0dXJuIFRyYW5zaXRpb25UeXBlLkNoYW5nZWRQaW5zO1xuXG4gICAgICAgICAgICBjYXNlIEV2ZW50VHlwZS5Sb29tTWVtYmVyOlxuICAgICAgICAgICAgICAgIHN3aXRjaCAoZS5teEV2ZW50LmdldENvbnRlbnQoKS5tZW1iZXJzaGlwKSB7XG4gICAgICAgICAgICAgICAgICAgIGNhc2UgS25vd25NZW1iZXJzaGlwLkludml0ZTpcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBUcmFuc2l0aW9uVHlwZS5JbnZpdGVkO1xuICAgICAgICAgICAgICAgICAgICBjYXNlIEtub3duTWVtYmVyc2hpcC5CYW46XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gVHJhbnNpdGlvblR5cGUuQmFubmVkO1xuICAgICAgICAgICAgICAgICAgICBjYXNlIEtub3duTWVtYmVyc2hpcC5Kb2luOlxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGUubXhFdmVudC5nZXRQcmV2Q29udGVudCgpLm1lbWJlcnNoaXAgPT09IEtub3duTWVtYmVyc2hpcC5Kb2luKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGUubXhFdmVudC5nZXRDb250ZW50KCkuZGlzcGxheW5hbWUgIT09IGUubXhFdmVudC5nZXRQcmV2Q29udGVudCgpLmRpc3BsYXluYW1lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBUcmFuc2l0aW9uVHlwZS5DaGFuZ2VkTmFtZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGUubXhFdmVudC5nZXRDb250ZW50KCkuYXZhdGFyX3VybCAhPT0gZS5teEV2ZW50LmdldFByZXZDb250ZW50KCkuYXZhdGFyX3VybCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gVHJhbnNpdGlvblR5cGUuQ2hhbmdlZEF2YXRhcjtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFRyYW5zaXRpb25UeXBlLk5vQ2hhbmdlO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gVHJhbnNpdGlvblR5cGUuSm9pbmVkO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBjYXNlIEtub3duTWVtYmVyc2hpcC5MZWF2ZTpcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChlLm14RXZlbnQuZ2V0U2VuZGVyKCkgPT09IGUubXhFdmVudC5nZXRTdGF0ZUtleSgpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGUubXhFdmVudC5nZXRQcmV2Q29udGVudCgpLm1lbWJlcnNoaXAgPT09IEtub3duTWVtYmVyc2hpcC5JbnZpdGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFRyYW5zaXRpb25UeXBlLkludml0ZVJlamVjdDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFRyYW5zaXRpb25UeXBlLkxlZnQ7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKGUubXhFdmVudC5nZXRQcmV2Q29udGVudCgpLm1lbWJlcnNoaXApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXNlIEtub3duTWVtYmVyc2hpcC5JbnZpdGU6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBUcmFuc2l0aW9uVHlwZS5JbnZpdGVXaXRoZHJhd2FsO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgS25vd25NZW1iZXJzaGlwLkJhbjpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFRyYW5zaXRpb25UeXBlLlVuYmFubmVkO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHNlbmRlciBpcyBub3QgdGFyZ2V0IGFuZCBtYWRlIHRoZSB0YXJnZXQgbGVhdmUsIGlmIG5vdCBmcm9tIGludml0ZS9iYW4gdGhlbiB0aGlzIGlzIGEga2lja1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBUcmFuc2l0aW9uVHlwZS5LaWNrZWQ7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgLy8gb3RoZXJ3aXNlLCBhc3N1bWUgdGhpcyBpcyBhIGhpZGRlbiBldmVudFxuICAgICAgICAgICAgICAgIHJldHVybiBUcmFuc2l0aW9uVHlwZS5IaWRkZW5FdmVudDtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBnZXRBZ2dyZWdhdGUodXNlckV2ZW50czogUmVjb3JkPHN0cmluZywgSVVzZXJFdmVudHNbXT4pOiB7XG4gICAgICAgIG5hbWVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmdbXT47XG4gICAgICAgIGluZGljZXM6IFJlY29yZDxzdHJpbmcsIG51bWJlcj47XG4gICAgfSB7XG4gICAgICAgIC8vIEEgbWFwIG9mIGFnZ3JlZ2F0ZSB0eXBlIHRvIGFycmF5cyBvZiBkaXNwbGF5IG5hbWVzLiBFYWNoIGFnZ3JlZ2F0ZSB0eXBlXG4gICAgICAgIC8vIGlzIGEgY29tbWEtZGVsaW1pdGVkIHN0cmluZyBvZiB0cm