Rev 1 | AutorÃa | Comparar con el anterior | Ultima modificación | Ver Log |
{"version":3,"file":"message_drawer_view_conversation_patcher.min.js","sources":["../src/message_drawer_view_conversation_patcher.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * This module will take 2 view states from the message_drawer_view_conversation\n * module and generate a patch that can be given to the\n * message_drawer_view_con
versation_renderer module to update the UI.\n *\n * This module should never modify either state. It's purely a read only\n * module.\n *\n * @module core_message/message_drawer_view_conversation_patcher\n * @copyright 2018 Ryan Wyllie <ryan@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/user_date',\n 'core_message/message_drawer_view_conversation_constants'\n],\nfunction(\n $,\n UserDate,\n Constants\n) {\n /**\n * Sort messages by day.\n *\n * @param {Array} messages The list of messages to sort.\n * @param {Number} midnight User's midnight timestamp.\n * @return {Array} messages sorted by day.\n */\n var sortMessagesByDay = function(messages, midnight) {\n var messagesByDay = messages.reduce(function(carry, message) {\n var timeCreated = message.timeCreated ? message.timeCreated : midnight;\n var dayTimestamp = UserDate.getUserMidnightForTimestamp(t
imeCreated, midnight);\n\n if (carry.hasOwnProperty(dayTimestamp)) {\n carry[dayTimestamp].push(message);\n } else {\n carry[dayTimestamp] = [message];\n }\n\n return carry;\n }, {});\n\n return Object.keys(messagesByDay).map(function(dayTimestamp) {\n return {\n timestamp: dayTimestamp,\n messages: messagesByDay[dayTimestamp]\n };\n });\n };\n\n /**\n * Diff 2 arrays using a match function\n *\n * @param {Array} a The first array.\n * @param {Array} b The second array.\n * @param {Function} matchFunction Function used for matching array items.\n * @return {Object} Object containing array items missing from a, array items missing from b\n * and matches\n */\n var diffArrays = function(a, b, matchFunction) {\n // Make copy of it.\n b = b.slice();\n var missingFromA = [];\n var missingFromB = []
;\n var matches = [];\n\n a.forEach(function(current) {\n var found = false;\n var index = 0;\n\n for (; index < b.length; index++) {\n var next = b[index];\n\n if (matchFunction(current, next)) {\n found = true;\n matches.push({\n a: current,\n b: next\n });\n break;\n }\n }\n\n if (found) {\n // This day has been processed so removed it from the list.\n b.splice(index, 1);\n } else {\n // If we couldn't find it in the next messages then it means\n // it needs to be added.\n missingFromB.push(current);\n }\n });\n\n missingFromA = b;\n\n return {\n missingFromA: missingFromA,\n missingFromB: missingFromB,\n matches: matches\n
};\n };\n\n /**\n * Find an element in a array based on a matching function.\n *\n * @param {array} array Array to search.\n * @param {Function} breakFunction Function to run on array item.\n * @return {*} The array item.\n */\n var findPositionInArray = function(array, breakFunction) {\n var before = null;\n\n for (var i = 0; i < array.length; i++) {\n var candidate = array[i];\n\n if (breakFunction(candidate)) {\n return candidate;\n }\n }\n\n return before;\n };\n\n /**\n * Check if 2 arrays are equal.\n *\n * @param {Array} a The first array.\n * @param {Array} b The second array.\n * @return {Boolean} Are arrays equal.\n */\n var isArrayEqual = function(a, b) {\n // Make shallow copies so that we don't mess with the array sorting.\n a = a.slice();\n b = b.slice();\n a.sort();\n b.sort();\n var aLength = a.length;
\n var bLength = b.length;\n\n if (aLength < 1 && bLength < 1) {\n return true;\n }\n\n if (aLength != bLength) {\n return false;\n }\n\n return a.every(function(item, index) {\n return item == b[index];\n });\n };\n\n /**\n * Do a shallow check to see if two objects appear to be equal. This should\n * only be used for pretty basic objects.\n *\n * @param {Object} a First object to compare.\n * @param {Object} b Second object to compare\n * @return {Bool}\n */\n var isObjectEqual = function(a, b) {\n var aKeys = Object.keys(a);\n var bKeys = Object.keys(b);\n\n if (aKeys.length != bKeys.length) {\n return false;\n }\n\n return aKeys.every(function(key) {\n var aVal = a[key];\n var bVal = b[key];\n var aType = typeof aVal;\n var bType = typeof bVal;\n aType = (aVal === null) ? 'null' : aType;\
n bType = (aVal === null) ? 'null' : bType;\n aType = (aType === 'object' && Array.isArray(aType)) ? 'array' : aType;\n bType = (bType === 'object' && Array.isArray(bType)) ? 'array' : bType;\n\n if (aType !== bType) {\n return false;\n }\n\n switch (aType) {\n case 'object':\n return isObjectEqual(aVal, bVal);\n case 'array':\n return isArrayEqual(aVal, bVal);\n default:\n return a[key] == b[key];\n }\n });\n };\n\n /**\n * Compare two messages to check if they are equal. This function only checks a subset\n * of the message properties which we know will change rather than all properties.\n *\n * @param {Object} a The first message\n * @param {Object} b The second message\n * @return {Bool}\n */\n var isMessageEqual = function(a, b) {\n return isObjectEqual(\n
{\n id: a.id,\n state: a.sendState,\n text: a.text,\n timeCreated: a.timeCreated\n },\n {\n id: b.id,\n state: b.sendState,\n text: b.text,\n timeCreated: b.timeCreated\n }\n );\n };\n\n /**\n * Build a patch based on days.\n *\n * @param {Object} current Current list current items.\n * @param {Array} remove List of days to remove.\n * @param {Array} add List of days to add.\n * @return {Object} Patch with elements to add and remove.\n */\n var buildDaysPatch = function(current, remove, add) {\n return {\n remove: remove,\n add: add.map(function(day) {\n // Any days left over in the \"next\" list weren't in the \"current\" list\n // so they will need to be added.\n var before = findPositionInArray(current, function(candidate) {\n
return day.timestamp < candidate.timestamp;\n });\n\n return {\n before: before,\n value: day\n };\n })\n };\n };\n\n /**\n * Build the messages patch for each day.\n *\n * @param {Array} matchingDays Array of old and new messages sorted by day.\n * @return {Object} patch.\n */\n var buildMessagesPatch = function(matchingDays) {\n var remove = [];\n var add = [];\n var update = [];\n\n // Iterate over the list of days and determine which messages in those days\n // have been changed.\n matchingDays.forEach(function(days) {\n var dayCurrent = days.a;\n var dayNext = days.b;\n // Find out which messages have changed in this day. This will return a list of messages\n // from the current state that couldn't be found in the next state and a list of messages in\n // the next s
tate which couldn't be count in the current state.\n var messagesDiff = diffArrays(dayCurrent.messages, dayNext.messages, isMessageEqual);\n // Take the two arrays (list of messages changed from dayNext and list of messages changed\n // from dayCurrent) any work out which messages have been added/removed from the list and\n // which messages were just updated.\n var patch = diffArrays(\n // The messages from dayCurrent.message that weren't in dayNext.messages.\n messagesDiff.missingFromB,\n // The messages from dayNext.message that weren't in dayCurrent.messages.\n messagesDiff.missingFromA,\n function(a, b) {\n // This function is going to determine if the messages were\n // added/removed from either list or if they were simply an updated.\n //\n // If the IDs match or it was a state change (i.e. message
with a temp\n // ID goes from pending to sent and receives an actual id) then they are\n // the same message which should be an update not an add/remove.\n return a.id == b.id || (a.sendState != b.sendState && a.timeAdded == b.timeAdded);\n }\n );\n\n // Any messages from the current state for this day which aren't in the next state\n // for this day (i.e. the user deleted the message) means we need to remove them from\n // the UI.\n remove = remove.concat(patch.missingFromB);\n\n // Any messages not in the current state for this day which are in the next state\n // for this day (i.e. it's a new message) means we need to add it to the UI so work\n // out where in the list of messages it should appear (it could be a new message the\n // user has sent or older messages loaded as part of the conversation scroll back).\n patch.missin
gFromA.forEach(function(message) {\n // By default a null value for before will render the message at the bottom of\n // the message UI (i.e. it's the newest message).\n var before = null;\n\n if (message.timeCreated) {\n // If this message has a time created then find where it sits in the list of\n // message to insert it into the correct position.\n before = findPositionInArray(dayCurrent.messages, function(candidate) {\n if (message.timeCreated == candidate.timeCreated) {\n return message.id < candidate.id;\n } else {\n return message.timeCreated < candidate.timeCreated;\n }\n });\n }\n\n add.push({\n before: before,\n value: message,\n day: dayCurrent\n
});\n });\n\n // Any message that appears in both the current state for this day and the next state\n // for this day means something in the message was updated.\n update = update.concat(patch.matches.map(function(message) {\n return {\n before: message.a,\n after: message.b\n };\n }));\n });\n\n return {\n add: add,\n remove: remove,\n update: update\n };\n };\n\n /**\n * Build a patch for this conversation.\n *\n * @param {Object} state The current state of this conversation.\n * @param {Object} newState The new state of this conversation.\n * @returns {Object} Patch with days and messsages for each day.\n */\n var buildConversationPatch = function(state, newState) {\n var diff = diffArrays(state.messages, newState.messages, isMessageEqual);\n\n if (diff.missingFromA.length || dif
f.missingFromB.length) {\n // Some messages have changed so let's work out which ones by sorting\n // them into their respective days.\n var current = sortMessagesByDay(state.messages, state.midnight);\n var next = sortMessagesByDay(newState.messages, newState.midnight);\n // This diffs the arrays to work out if there are any missing days that need\n // to be added (i.e. we've got some new messages on a new day) or if there\n // are any days that need to be deleted (i.e. the user has deleted some old messages).\n var daysDiff = diffArrays(current, next, function(dayCurrent, dayNext) {\n return dayCurrent.timestamp == dayNext.timestamp;\n });\n\n return {\n // Handle adding or removing whole days.\n days: buildDaysPatch(current, daysDiff.missingFromB, daysDiff.missingFromA),\n // Handle updating messages that don't require adding/removing a w
hole day.\n messages: buildMessagesPatch(daysDiff.matches)\n };\n } else {\n return null;\n }\n };\n\n /**\n * Build a patch for the header of this conversation. Check if this conversation\n * is a group conversation.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} patch\n */\n var buildHeaderPatchTypePrivate = function(state, newState) {\n var requireAddContact = buildRequireAddContact(state, newState);\n var confirmContactRequest = buildConfirmContactRequest(state, newState);\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n var requiresAddContact = requireAddContact && requireAddContact.show && !requireAddContact.hasMessages;\n var requiredAddContact = requireAddContact && !requireAddContact.show;\n // Render the header once we've got a user.\n
var shouldRenderHeader = !oldOtherUser && newOtherUser;\n // We should also re-render the header if the other user requires\n // being added as a contact or if they did but no longer do.\n shouldRenderHeader = shouldRenderHeader || requiresAddContact || requiredAddContact;\n // Finally, we should re-render if the other user has sent this user\n // a contact request that is waiting for approval or if it's been approved/declined.\n shouldRenderHeader = shouldRenderHeader || confirmContactRequest !== null;\n\n if (shouldRenderHeader) {\n return {\n type: Constants.CONVERSATION_TYPES.PRIVATE,\n // We can show controls if the other user doesn't require add contact\n // and we aren't waiting for this user to respond to a contact request.\n showControls: !requiresAddContact && !confirmContactRequest,\n context: {\n id: newState.id,\n name: n
ewState.name,\n subname: newState.subname,\n totalmembercount: newState.totalMemberCount,\n imageurl: newState.imageUrl,\n isfavourite: newState.isFavourite,\n ismuted: newState.isMuted,\n // Don't show favouriting if we don't have a conversation.\n showfavourite: newState.id !== null,\n userid: newOtherUser.id,\n showonlinestatus: newOtherUser.showonlinestatus,\n isonline: newOtherUser.isonline,\n isblocked: newOtherUser.isblocked,\n iscontact: newOtherUser.iscontact,\n cancreatecontact: newOtherUser.cancreatecontact,\n }\n };\n }\n\n return null;\n };\n\n /**\n * Build a patch for the header of this conversation. Check if this conversation\n * is a group conversation.\n *\n * @param {Object} state The
current state.\n * @param {Object} newState The new state.\n * @return {Object} patch\n */\n var buildHeaderPatchTypeSelf = function(state, newState) {\n var shouldRenderHeader = (state.name === null && newState.name !== null);\n\n if (shouldRenderHeader) {\n return {\n type: Constants.CONVERSATION_TYPES.SELF,\n // Don't display the controls for the self-conversations.\n showControls: false,\n context: {\n id: newState.id,\n name: newState.name,\n subname: newState.subname,\n imageurl: newState.imageUrl,\n isfavourite: newState.isFavourite,\n // Don't show favouriting if we don't have a conversation.\n showfavourite: newState.id !== null,\n showonlinestatus: true,\n }\n };\n }\n\n return null;\n };\n\n /**\n *
Build a patch for the header of this conversation. Check if this conversation\n * is a group conversation.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} patch\n */\n var buildHeaderPatchTypePublic = function(state, newState) {\n var oldMemberCount = state.totalMemberCount;\n var newMemberCount = newState.totalMemberCount;\n\n if (oldMemberCount != newMemberCount) {\n return {\n type: Constants.CONVERSATION_TYPES.PUBLIC,\n showControls: true,\n context: {\n id: newState.id,\n name: newState.name,\n subname: newState.subname,\n totalmembercount: newState.totalMemberCount,\n imageurl: newState.imageUrl,\n isfavourite: newState.isFavourite,\n ismuted: newState.isMuted,\n // Don't show favouri
ting if we don't have a conversation.\n showfavourite: newState.id !== null\n }\n };\n } else {\n return null;\n }\n };\n\n /**\n * Find the newest or oldest message.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Number} Oldest or newest message id.\n */\n var buildScrollToMessagePatch = function(state, newState) {\n var oldMessages = state.messages;\n var newMessages = newState.messages;\n\n if (newMessages.length < 1) {\n return null;\n }\n\n if (oldMessages.length < 1) {\n return newMessages[newMessages.length - 1].id;\n }\n\n var previousNewest = oldMessages[state.messages.length - 1];\n var currentNewest = newMessages[newMessages.length - 1];\n var previousOldest = oldMessages[0];\n var currentOldest = newMessages[0];\n\n if (previousNewest.id
!= currentNewest.id) {\n return currentNewest.id;\n } else if (previousOldest.id != currentOldest.id) {\n return previousOldest.id;\n }\n\n return null;\n };\n\n /**\n * Check if members should be loaded.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildLoadingMembersPatch = function(state, newState) {\n if (!state.loadingMembers && newState.loadingMembers) {\n return true;\n } else if (state.loadingMembers && !newState.loadingMembers) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Check if the messages are being loaded for the first time.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildLoadingFirstMessages = function(state, newState) {\n if (stat
e.hasTriedToLoadMessages === newState.hasTriedToLoadMessages) {\n return null;\n } else if (!newState.hasTriedToLoadMessages && newState.loadingMessages) {\n return true;\n } else if (newState.hasTriedToLoadMessages && !newState.loadingMessages) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Check if the messages are still being loaded\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildLoadingMessages = function(state, newState) {\n if (!state.loadingMessages && newState.loadingMessages) {\n return true;\n } else if (state.loadingMessages && !newState.loadingMessages) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Determine if we should show the emoji picker.\n *\n * @param {Object} state The current state.\n
* @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildShowEmojiPicker = function(state, newState) {\n if (!state.showEmojiPicker && newState.showEmojiPicker) {\n return true;\n } else if (state.showEmojiPicker && !newState.showEmojiPicker) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Determine if we should show the emoji auto complete.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildShowEmojiAutoComplete = function(state, newState) {\n if (!state.showEmojiAutoComplete && newState.showEmojiAutoComplete) {\n return true;\n } else if (state.showEmojiAutoComplete && !newState.showEmojiAutoComplete) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Get the user Object of user to be block
ed if pending.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object|Bool|Null} User Object if Object.\n */\n var buildConfirmBlockUser = function(state, newState) {\n if (newState.pendingBlockUserIds.length) {\n // We currently only support a single user;\n var userId = newState.pendingBlockUserIds[0];\n return newState.members[userId];\n } else if (state.pendingBlockUserIds.length) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Get the user Object of user to be unblocked if pending.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object|Bool|Null} User Object if Object.\n */\n var buildConfirmUnblockUser = function(state, newState) {\n if (newState.pendingUnblockUserIds.length) {\n // We currently only support a single user;\n
var userId = newState.pendingUnblockUserIds[0];\n return newState.members[userId];\n } else if (state.pendingUnblockUserIds.length) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Get the user Object of user to be added as contact if pending.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object|Bool|Null} User Object if Object.\n */\n var buildConfirmAddContact = function(state, newState) {\n if (newState.pendingAddContactIds.length) {\n // We currently only support a single user;\n var userId = newState.pendingAddContactIds[0];\n return newState.members[userId];\n } else if (state.pendingAddContactIds.length) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Get the user Object of user to be removed as contact if pending.\n *\n * @param {Object} state The current st
ate.\n * @param {Object} newState The new state.\n * @return {Object|Bool|Null} User Object if Object.\n */\n var buildConfirmRemoveContact = function(state, newState) {\n if (newState.pendingRemoveContactIds.length) {\n // We currently only support a single user;\n var userId = newState.pendingRemoveContactIds[0];\n return newState.members[userId];\n } else if (state.pendingRemoveContactIds.length) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Check if there are any messages to be deleted.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object|Null} The conversation type and if the user can delete the messages for all users.\n */\n var buildConfirmDeleteSelectedMessages = function(state, newState) {\n var oldPendingCount = state.pendingDeleteMessageIds.length;\n var newPendingCount = newState.pendingDelet
eMessageIds.length;\n\n if (newPendingCount && !oldPendingCount) {\n return {\n show: true,\n type: newState.type,\n canDeleteMessagesForAllUsers: newState.canDeleteMessagesForAllUsers\n };\n } else if (oldPendingCount && !newPendingCount) {\n return {\n show: false\n };\n }\n\n return null;\n };\n\n /**\n * Check if there is a conversation to be deleted.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {int|Null} The conversation type to be deleted.\n */\n var buildConfirmDeleteConversation = function(state, newState) {\n if (!state.pendingDeleteConversation && newState.pendingDeleteConversation) {\n return newState.type;\n } else if (state.pendingDeleteConversation && !newState.pendingDeleteConversation) {\n return false;\n }\n\n return n
ull;\n };\n\n /**\n * Check if there is a pending contact request to accept or decline.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildConfirmContactRequest = function(state, newState) {\n var loggedInUserId = state.loggedInUserId;\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n var oldReceivedRequests = !oldOtherUser ? [] : oldOtherUser.contactrequests.filter(function(request) {\n return request.requesteduserid == loggedInUserId && request.userid == oldOtherUser.id;\n });\n var newReceivedRequests = !newOtherUser ? [] : newOtherUser.contactrequests.filter(function(request) {\n return request.requesteduserid == loggedInUserId && request.userid == newOtherUser.id;\n });\n var oldRequest = oldReceivedRequests.length ? oldReceivedRequests[0] : null;\n
var newRequest = newReceivedRequests.length ? newReceivedRequests[0] : null;\n\n if (!oldRequest && newRequest) {\n return newOtherUser;\n } else if (oldRequest && !newRequest) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Check if there are any changes in blocked users.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildIsBlocked = function(state, newState) {\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n\n if (!oldOtherUser && !newOtherUser) {\n return null;\n } else if (!oldOtherUser && newOtherUser) {\n return newOtherUser.isblocked ? true : null;\n } else if (!newOtherUser && oldOtherUser) {\n return oldOtherUser.isblocked ? false : null;\n } else if (oldOtherUser.isblocked &&
!newOtherUser.isblocked) {\n return false;\n } else if (!oldOtherUser.isblocked && newOtherUser.isblocked) {\n return true;\n } else {\n return null;\n }\n };\n\n /**\n * Check if there are any changes the conversation favourite state.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildIsFavourite = function(state, newState) {\n var oldIsFavourite = state.isFavourite;\n var newIsFavourite = newState.isFavourite;\n\n if (state.id === null && newState.id === null) {\n // The conversation isn't yet created so don't change anything.\n return null;\n } else if (state.id === null && newState.id !== null) {\n // The conversation was created so we can show the add favourite button.\n return 'show-add';\n } else if (state.id !== null && newState.id === null) {\n
// We're changing from a created conversation to a new conversation so hide\n // the favouriting functionality for now.\n return 'hide';\n } else if (oldIsFavourite == newIsFavourite) {\n // No change.\n return null;\n } else if (!oldIsFavourite && newIsFavourite) {\n return 'show-remove';\n } else if (oldIsFavourite && !newIsFavourite) {\n return 'show-add';\n } else {\n return null;\n }\n };\n\n /**\n * Check if there are any changes the conversation muted state.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {string|null}\n */\n var buildIsMuted = function(state, newState) {\n var oldIsMuted = state.isMuted;\n var newIsMuted = newState.isMuted;\n\n if (state.id === null && newState.id === null) {\n // The conversation isn't yet created so don't change anything.\n
return null;\n } else if (state.id === null && newState.id !== null) {\n // The conversation was created so we can show the mute button.\n return 'show-mute';\n } else if (state.id !== null && newState.id === null) {\n // We're changing from a created conversation to a new conversation so hide\n // the muting functionality for now.\n return 'hide';\n } else if (oldIsMuted == newIsMuted) {\n // No change.\n return null;\n } else if (!oldIsMuted && newIsMuted) {\n return 'show-unmute';\n } else if (oldIsMuted && !newIsMuted) {\n return 'show-mute';\n } else {\n return null;\n }\n };\n\n /**\n * Check if there are any changes in the contact status of the current user\n * and other user.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var
buildIsContact = function(state, newState) {\n var loggedInUserId = state.loggedInUserId;\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n var oldContactRequests = !oldOtherUser ? [] : oldOtherUser.contactrequests.filter(function(request) {\n return (request.userid == loggedInUserId && request.requesteduserid == oldOtherUser.id) ||\n (request.userid == oldOtherUser.id && request.requesteduserid == loggedInUserId);\n });\n var newContactRequests = !newOtherUser ? [] : newOtherUser.contactrequests.filter(function(request) {\n return (request.userid == loggedInUserId && request.requesteduserid == newOtherUser.id) ||\n (request.userid == newOtherUser.id && request.requesteduserid == loggedInUserId);\n });\n var oldHasContactRequests = oldContactRequests.length > 0;\n var newHasContactRequests = newContactRequests.length > 0;\n\n if (!
oldOtherUser && !newOtherUser) {\n return null;\n } else if (oldHasContactRequests && newHasContactRequests) {\n return null;\n } else if (!oldHasContactRequests && newHasContactRequests && !newOtherUser.iscontact) {\n return 'pending-contact';\n } else if (!oldOtherUser && newOtherUser) {\n return newOtherUser.iscontact ? 'contact' : null;\n } else if (!newOtherUser && oldOtherUser) {\n return oldOtherUser.iscontact ? 'non-contact' : null;\n } else if (oldOtherUser.iscontact && !newOtherUser.iscontact) {\n return newHasContactRequests ? 'pending-contact' : 'non-contact';\n } else if (!oldOtherUser.iscontact && newOtherUser.iscontact) {\n return 'contact';\n } else {\n return null;\n }\n };\n\n /**\n * Check if a confirm action is active.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @retu
rn {Bool|Null}\n */\n var buildLoadingConfirmationAction = function(state, newState) {\n if (!state.loadingConfirmAction && newState.loadingConfirmAction) {\n return true;\n } else if (state.loadingConfirmAction && !newState.loadingConfirmAction) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Check if a edit mode is active.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildInEditMode = function(state, newState) {\n var oldHasSelectedMessages = state.selectedMessageIds.length > 0;\n var newHasSelectedMessages = newState.selectedMessageIds.length > 0;\n var numberOfMessagesHasChanged = state.messages.length != newState.messages.length;\n\n if (!oldHasSelectedMessages && newHasSelectedMessages) {\n return true;\n } else if (oldHasSelectedMessages && !newHasSelectedM
essages) {\n return false;\n } else if (oldHasSelectedMessages && numberOfMessagesHasChanged) {\n return true;\n } else {\n return null;\n }\n };\n\n /**\n * Build a patch for the messages selected.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} patch\n */\n var buildSelectedMessages = function(state, newState) {\n var oldSelectedMessages = state.selectedMessageIds;\n var newSelectedMessages = newState.selectedMessageIds;\n\n if (isArrayEqual(oldSelectedMessages, newSelectedMessages)) {\n return null;\n }\n\n var diff = diffArrays(oldSelectedMessages, newSelectedMessages, function(a, b) {\n return a == b;\n });\n\n return {\n count: newSelectedMessages.length,\n add: diff.missingFromA,\n remove: diff.missingFromB\n };\n };\n\n /**\n * G
et a list of users from the state that are not the logged in user. Use to find group\n * message members or the other user in a conversation.\n *\n * @param {Object} state State\n * @return {Array} List of users.\n */\n var getOtherUserFromState = function(state) {\n return Object.keys(state.members).reduce(function(carry, userId) {\n if (userId != state.loggedInUserId && !carry) {\n carry = state.members[userId];\n }\n\n return carry;\n }, null);\n };\n\n /**\n * Check if the given user requires a contact request from the logged in user.\n *\n * @param {Integer} loggedInUserId The logged in user id\n * @param {Object} user User record\n * @return {Bool}\n */\n var requiresContactRequest = function(loggedInUserId, user) {\n // If a user can message then no contact request is required.\n if (user.canmessage) {\n return false;\n }\n\n var contactRequest
s = user.contactrequests.filter(function(request) {\n return request.userid == loggedInUserId || request.requesteduserid;\n });\n var hasSentContactRequest = contactRequests.length > 0;\n return user.requirescontact && !user.iscontact && !hasSentContactRequest;\n };\n\n /**\n * Check if other users are required to be added as contact.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} Object controlling the required to add contact dialog variables.\n */\n var buildRequireAddContact = function(state, newState) {\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n var hadMessages = state.messages.length > 0;\n var hasMessages = newState.messages.length > 0;\n var loggedInUserId = newState.loggedInUserId;\n var prevRequiresContactRequest = oldOtherUser && requiresContactRequest(loggedInUs
erId, oldOtherUser);\n var nextRequiresContactRequest = newOtherUser && requiresContactRequest(loggedInUserId, newOtherUser);\n var confirmAddContact = buildConfirmAddContact(state, newState);\n var finishedAddContact = confirmAddContact === false;\n\n // Still doing first load.\n if (!state.hasTriedToLoadMessages && !newState.hasTriedToLoadMessages) {\n return null;\n }\n\n // No users yet.\n if (!oldOtherUser && !newOtherUser) {\n return null;\n }\n\n // We've loaded a new user and they require a contact request.\n if (!oldOtherUser && nextRequiresContactRequest) {\n return {\n show: true,\n hasMessages: hasMessages,\n user: newOtherUser\n };\n }\n\n // The logged in user has completed the confirm contact request dialogue\n // but the other user still requires a contact request which means the logged\n // in user ei
ther declined the confirmation or it failed.\n if (finishedAddContact && nextRequiresContactRequest) {\n return {\n show: true,\n hasMessages: hasMessages,\n user: newOtherUser\n };\n }\n\n // Everything is loaded.\n if (state.hasTriedToLoadMessages && newState.hasTriedToLoadMessages) {\n if (!prevRequiresContactRequest && nextRequiresContactRequest) {\n return {\n show: true,\n hasMessages: hasMessages,\n user: newOtherUser\n };\n }\n\n if (prevRequiresContactRequest && !nextRequiresContactRequest) {\n return {\n show: false,\n hasMessages: hasMessages\n };\n }\n }\n\n // First load just completed.\n if (!state.hasTriedToLoadMessages && newState.hasTriedToLoadMessages) {\n if (nextRequir
esContactRequest) {\n return {\n show: true,\n hasMessages: hasMessages,\n user: newOtherUser\n };\n }\n }\n\n // Being reset.\n if (state.hasTriedToLoadMessages && !newState.hasTriedToLoadMessages) {\n if (prevRequiresContactRequest) {\n return {\n show: false,\n hasMessages: hadMessages\n };\n }\n }\n\n return null;\n };\n\n /**\n * Check if other users are required to be unblocked.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildRequireUnblock = function(state, newState) {\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n\n if (!oldOtherUser && !newOtherUser) {\n return null;\n
} else if (oldOtherUser && !newOtherUser) {\n return oldOtherUser.isblocked ? false : null;\n } else if (!oldOtherUser && newOtherUser) {\n return newOtherUser.isblocked ? true : null;\n } else if (!oldOtherUser.isblocked && newOtherUser.isblocked) {\n return true;\n } else if (oldOtherUser.isblocked && !newOtherUser.isblocked) {\n return false;\n }\n\n return null;\n };\n\n /**\n * Check if other users can be messaged.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Bool|Null}\n */\n var buildUnableToMessage = function(state, newState) {\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n\n if (newState.type == Constants.CONVERSATION_TYPES.SELF) {\n // Users always can send message themselves on self-conversations.\n return null;\n
}\n\n if (!oldOtherUser && !newOtherUser) {\n return null;\n } else if (oldOtherUser && !newOtherUser) {\n return oldOtherUser.canmessage ? null : true;\n } else if (!oldOtherUser && newOtherUser) {\n return newOtherUser.canmessage ? null : true;\n } else if (!oldOtherUser.canmessage && newOtherUser.canmessage) {\n return false;\n } else if (oldOtherUser.canmessage && !newOtherUser.canmessage) {\n return true;\n }\n\n return null;\n };\n\n /**\n * Build patch for footer information for a private conversation.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} containing footer state type.\n */\n var buildFooterPatchTypePrivate = function(state, newState) {\n var loadingFirstMessages = buildLoadingFirstMessages(state, newState);\n var inEditMode = buildInEditMode(state, newState);\n var re
quireAddContact = buildRequireAddContact(state, newState);\n var requireUnblock = buildRequireUnblock(state, newState);\n var unableToMessage = buildUnableToMessage(state, newState);\n var showRequireAddContact = requireAddContact !== null ? requireAddContact.show && requireAddContact.hasMessages : null;\n var otherUser = getOtherUserFromState(newState);\n var generateReturnValue = function(checkValue, successReturn) {\n if (checkValue) {\n return successReturn;\n } else if (checkValue !== null && !checkValue) {\n if (!otherUser) {\n return {type: 'content'};\n } else if (otherUser.isblocked) {\n return {type: 'unblock'};\n } else if (newState.messages.length && requiresContactRequest(newState.loggedInUserId, otherUser)) {\n return {\n type: 'add-contact',\n user: otherUser\n
};\n } else if (!otherUser.canmessage && (otherUser.requirescontact && !otherUser.iscontact)) {\n return {type: 'unable-to-message'};\n }\n }\n\n return null;\n };\n\n if (\n loadingFirstMessages === null &&\n inEditMode === null &&\n requireAddContact === null &&\n requireUnblock === null\n ) {\n return null;\n }\n\n var checks = [\n [loadingFirstMessages, {type: 'placeholder'}],\n [inEditMode, {type: 'edit-mode'}],\n [unableToMessage, {type: 'unable-to-message'}],\n [requireUnblock, {type: 'unblock'}],\n [showRequireAddContact, {type: 'add-contact', user: otherUser}]\n ];\n\n for (var i = 0; i < checks.length; i++) {\n var checkValue = checks[i][0];\n var successReturn = checks[i][1];\n var result = generateReturnValue(checkValue, successReturn)
;\n\n if (result !== null) {\n return result;\n }\n }\n\n return {\n type: 'content'\n };\n };\n\n /**\n * Build patch for footer information for a public conversation.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {Object} containing footer state type.\n */\n var buildFooterPatchTypePublic = function(state, newState) {\n var loadingFirstMessages = buildLoadingFirstMessages(state, newState);\n var inEditMode = buildInEditMode(state, newState);\n\n if (loadingFirstMessages === null && inEditMode === null) {\n return null;\n }\n\n if (loadingFirstMessages) {\n return {type: 'placeholder'};\n }\n\n if (inEditMode) {\n return {type: 'edit-mode'};\n }\n\n return {\n type: 'content'\n };\n };\n\n /**\n * Check if we're viewing a dif
ferent conversation. If so then we need to\n * reset the UI.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {bool|null} If a reset needs to occur\n */\n var buildReset = function(state, newState) {\n var oldType = state.type;\n var newType = newState.type;\n var oldConversationId = state.id;\n var newConversationId = newState.id;\n var oldMemberIds = Object.keys(state.members);\n var newMemberIds = Object.keys(newState.members);\n\n oldMemberIds.sort();\n newMemberIds.sort();\n\n var membersUnchanged = oldMemberIds.every(function(id, index) {\n return id == newMemberIds[index];\n });\n\n if (oldType != newType) {\n // If we've changed conversation type then we need to reset.\n return true;\n } else if (oldConversationId && !newConversationId) {\n // We previously had a conversation id but no longe
r do. This likely means\n // the user is viewing the conversation with someone they've never spoken to\n // before.\n return true;\n } else if (oldConversationId && newConversationId && oldConversationId != newConversationId) {\n // If we had a conversation id and it's changed then we need to reset.\n return true;\n } else if (!oldConversationId && !newConversationId && !membersUnchanged) {\n // If we never had a conversation id but the members of the conversation have\n // changed then we need to reset. This can happen if the user goes from viewing\n // a user they've never had a conversation with to viewing a different user that\n // they've never had a conversation with.\n return true;\n }\n\n return null;\n };\n\n /**\n * We should show this message always, for all the self-conversations.\n *\n * The message should be hidden when it's not a self-conve
rsation.\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {bool}\n */\n var buildSelfConversationMessage = function(state, newState) {\n if (state.type != newState.type) {\n return (newState.type == Constants.CONVERSATION_TYPES.SELF);\n }\n\n return null;\n };\n\n /**\n * We should show the contact request sent message if the user just sent\n * a contact request to the other user\n *\n * @param {Object} state The current state.\n * @param {Object} newState The new state.\n * @return {string|false|null}\n */\n var buildContactRequestSent = function(state, newState) {\n var loggedInUserId = newState.loggedInUserId;\n var oldOtherUser = getOtherUserFromState(state);\n var newOtherUser = getOtherUserFromState(newState);\n var oldSentRequests = !oldOtherUser ? [] : oldOtherUser.contactrequests.filter(function(request) {\n ret
urn request.userid == loggedInUserId;\n });\n var newSentRequests = !newOtherUser ? [] : newOtherUser.contactrequests.filter(function(request) {\n return request.userid == loggedInUserId;\n });\n var oldRequest = oldSentRequests.length > 0;\n var newRequest = newSentRequests.length > 0;\n\n if (!oldRequest && newRequest && !newOtherUser.iscontact) {\n return newOtherUser.fullname;\n } else if (oldOtherUser && !oldOtherUser.iscontact && newRequest && newOtherUser.iscontact) {\n // Contact request accepted.\n return false;\n } else if (oldRequest && !newRequest) {\n return false;\n } else {\n return null;\n }\n };\n\n /**\n * Build the full patch comparing the current state and the new state. This patch is used by\n * the conversation renderer to render the UI on any update.\n *\n * @param {Object} state The current state.\n * @param {Object} newSta
te The new state.\n * @return {Object} Patch containing all information changed.\n */\n var buildPatch = function(state, newState) {\n var config = {\n all: {\n reset: buildReset,\n conversation: buildConversationPatch,\n scrollToMessage: buildScrollToMessagePatch,\n loadingMembers: buildLoadingMembersPatch,\n loadingFirstMessages: buildLoadingFirstMessages,\n loadingMessages: buildLoadingMessages,\n confirmDeleteSelectedMessages: buildConfirmDeleteSelectedMessages,\n inEditMode: buildInEditMode,\n selectedMessages: buildSelectedMessages,\n isFavourite: buildIsFavourite,\n isMuted: buildIsMuted,\n showEmojiPicker: buildShowEmojiPicker,\n showEmojiAutoComplete: buildShowEmojiAutoComplete\n }\n };\n // These build functions are only applicable to private conversatio
ns.\n config[Constants.CONVERSATION_TYPES.PRIVATE] = {\n header: buildHeaderPatchTypePrivate,\n footer: buildFooterPatchTypePrivate,\n confirmBlockUser: buildConfirmBlockUser,\n confirmUnblockUser: buildConfirmUnblockUser,\n confirmAddContact: buildConfirmAddContact,\n confirmRemoveContact: buildConfirmRemoveContact,\n confirmContactRequest: buildConfirmContactRequest,\n confirmDeleteConversation: buildConfirmDeleteConversation,\n isBlocked: buildIsBlocked,\n isContact: buildIsContact,\n loadingConfirmAction: buildLoadingConfirmationAction,\n requireAddContact: buildRequireAddContact,\n contactRequestSent: buildContactRequestSent\n };\n // These build functions are only applicable to public (group) conversations.\n config[Constants.CONVERSATION_TYPES.PUBLIC] = {\n header: buildHeaderPatchTypePublic,\n footer: buildFoot
erPatchTypePublic,\n };\n // These build functions are only applicable to self-conversations.\n config[Constants.CONVERSATION_TYPES.SELF] = {\n header: buildHeaderPatchTypeSelf,\n footer: buildFooterPatchTypePublic,\n confirmDeleteConversation: buildConfirmDeleteConversation,\n selfConversationMessage: buildSelfConversationMessage\n };\n\n var patchConfig = $.extend({}, config.all);\n if (newState.type && newState.type in config) {\n // Add the type specific builders to the patch config.\n patchConfig = $.extend(patchConfig, config[newState.type]);\n }\n\n return Object.keys(patchConfig).reduce(function(patch, key) {\n var buildFunc = patchConfig[key];\n var value = buildFunc(state, newState);\n\n if (value !== null) {\n patch[key] = value;\n }\n\n return patch;\n }, {});\n };\n\n return {\n buildPat
ch: buildPatch\n };\n});\n"],"names":["define","$","UserDate","Constants","sortMessagesByDay","messages","midnight","messagesByDay","reduce","carry","message","timeCreated","dayTimestamp","getUserMidnightForTimestamp","hasOwnProperty","push","Object","keys","map","timestamp","diffArrays","a","b","matchFunction","slice","missingFromB","matches","forEach","current","found","index","length","next","splice","missingFromA","findPositionInArray","array","breakFunction","i","candidate","isArrayEqual","sort","aLength","bLength","every","item","isObjectEqual","aKeys","bKeys","key","aVal","bVal","aType","bType","Array","isArray","isMessageEqual","id","state","sendState","text","buildDaysPatch","remove","add","day","before","value","buildConversationPatch","newState","matchingDays","update","diff","daysDiff","dayCurrent","dayNext","days","messagesDiff","patch","timeAdded","concat","after","buildHeaderPatchTypePrivate","requireAddContact","buildRequireAddContact","confirmContactRequest","buildConfirmContactRequest","
oldOtherUser","getOtherUserFromState","newOtherUser","requiresAddContact","show","hasMessages","requiredAddContact","shouldRenderHeader","type","CONVERSATION_TYPES","PRIVATE","showControls","context","name","subname","totalmembercount","totalMemberCount","imageurl","imageUrl","isfavourite","isFavourite","ismuted","isMuted","showfavourite","userid","showonlinestatus","isonline","isblocked","iscontact","cancreatecontact","buildHeaderPatchTypeSelf","SELF","buildHeaderPatchTypePublic","PUBLIC","buildScrollToMessagePatch","oldMessages","newMessages","previousNewest","currentNewest","previousOldest","currentOldest","buildLoadingMembersPatch","loadingMembers","buildLoadingFirstMessages","hasTriedToLoadMessages","loadingMessages","buildLoadingMessages","buildShowEmojiPicker","showEmojiPicker","buildShowEmojiAutoComplete","showEmojiAutoComplete","buildConfirmBlockUser","pendingBlockUserIds","userId","members","buildConfirmUnblockUser","pendingUnblockUserIds","buildConfirmAddContact","pendingAddContactIds","buildConfi
rmRemoveContact","pendingRemoveContactIds","buildConfirmDeleteSelectedMessages","oldPendingCount","pendingDeleteMessageIds","newPendingCount","canDeleteMessagesForAllUsers","buildConfirmDeleteConversation","pendingDeleteConversation","loggedInUserId","oldReceivedRequests","contactrequests","filter","request","requesteduserid","newReceivedRequests","oldRequest","newRequest","buildIsBlocked","buildIsFavourite","oldIsFavourite","newIsFavourite","buildIsMuted","oldIsMuted","newIsMuted","buildIsContact","oldContactRequests","newContactRequests","oldHasContactRequests","newHasContactRequests","buildLoadingConfirmationAction","loadingConfirmAction","buildInEditMode","oldHasSelectedMessages","selectedMessageIds","newHasSelectedMessages","numberOfMessagesHasChanged","buildSelectedMessages","oldSelectedMessages","newSelectedMessages","count","requiresContactRequest","user","canmessage","hasSentContactRequest","requirescontact","hadMessages","prevRequiresContactRequest","nextRequiresContactRequest","finishedAddContact"
,"buildFooterPatchTypePrivate","loadingFirstMessages","inEditMode","requireUnblock","buildRequireUnblock","unableToMessage","buildUnableToMessage","showRequireAddContact","otherUser","generateReturnValue","checkValue","successReturn","checks","result","buildFooterPatchTypePublic","buildReset","oldType","newType","oldConversationId","newConversationId","oldMemberIds","newMemberIds","membersUnchanged","buildSelfConversationMessage","buildContactRequestSent","oldSentRequests","newSentRequests","fullname","buildPatch","config","all","reset","conversation","scrollToMessage","confirmDeleteSelectedMessages","selectedMessages","header","footer","confirmBlockUser","confirmUnblockUser","confirmAddContact","confirmRemoveContact","confirmDeleteConversation","isBlocked","isContact","contactRequestSent","selfConversationMessage","patchConfig","extend","buildFunc"],"mappings":";;;;;;;;;;;;AA2BAA,+DACA,CACI,SACA,iBACA,4DAEJ,SACIC,EACAC,SACAC,eASIC,kBAAoB,SAASC,SAAUC,cACnCC,cAAgBF,SAASG,QAAO,SAASC,MAAOC,aAC5CC,YAAcD,QAAQC,YA
AcD,QAAQC,YAAcL,SAC1DM,aAAeV,SAASW,4BAA4BF,YAAaL,iBAEjEG,MAAMK,eAAeF,cACrBH,MAAMG,cAAcG,KAAKL,SAEzBD,MAAMG,cAAgB,CAACF,SAGpBD,QACR,WAEIO,OAAOC,KAAKV,eAAeW,KAAI,SAASN,oBACpC,CACHO,UAAWP,aACXP,SAAUE,cAAcK,mBAchCQ,WAAa,SAASC,EAAGC,EAAGC,eAE5BD,EAAIA,EAAEE,YAEFC,aAAe,GACfC,QAAU,UAEdL,EAAEM,SAAQ,SAASC,iBACXC,OAAQ,EACRC,MAAQ,EAELA,MAAQR,EAAES,OAAQD,QAAS,KAC1BE,KAAOV,EAAEQ,UAETP,cAAcK,QAASI,MAAO,CAC9BH,OAAQ,EACRH,QAAQX,KAAK,CACTM,EAAGO,QACHN,EAAGU,cAMXH,MAEAP,EAAEW,OAAOH,MAAO,GAIhBL,aAAaV,KAAKa,YAMnB,CACHM,aAHWZ,EAIXG,aAAcA,aACdC,QAASA,UAWbS,oBAAsB,SAASC,MAAOC,uBAG7BC,EAAI,EAAGA,EAAIF,MAAML,OAAQO,IAAK,KAC/BC,UAAYH,MAAME,MAElBD,cAAcE,kBACPA,iBANF,MAoBbC,aAAe,SAASnB,EAAGC,GAE3BD,EAAIA,EAAEG,QACNF,EAAIA,EAAEE,QACNH,EAAEoB,OACFnB,EAAEmB,WACEC,QAAUrB,EAAEU,OACZY,QAAUrB,EAAES,cAEZW,QAAU,GAAKC,QAAU,GAIzBD,SAAWC,SAIRtB,EAAEuB,OAAM,SAASC,KAAMf,cACnBe,MAAQvB,EAAEQ,WAYrBgB,cAAgB,SAASzB,EAAGC,OACxByB,MAAQ/B,OAAOC,KAAKI,GACpB2B,MAAQhC,OAAOC,KAAKK,UAEpByB,MAAMhB,QAAUiB,MAAMjB,QAInBgB,MAAMH,OAAM,SAASK,SACpBC,KAAO7B,EAAE4B,KACTE,K
AAO7B,EAAE2B,KACTG,aAAeF,KACfG,aAAeF,QAEnBE,MAAkB,OAATH,KAAiB,OAASG,OACnCD,MAAmB,YAFnBA,MAAkB,OAATF,KAAiB,OAASE,QAEJE,MAAMC,QAAQH,OAAU,QAAUA,UACjEC,MAAmB,WAAVA,OAAsBC,MAAMC,QAAQF,OAAU,QAAUA,cAGtD,SAGHD,WACC,gBACMN,cAAcI,KAAMC,UAC1B,eACMX,aAAaU,KAAMC,qBAEnB9B,EAAE4B,MAAQ3B,EAAE2B,UAa/BO,eAAiB,SAASnC,EAAGC,UACtBwB,cACH,CACIW,GAAIpC,EAAEoC,GACNC,MAAOrC,EAAEsC,UACTC,KAAMvC,EAAEuC,KACRjD,YAAaU,EAAEV,aAEnB,CACI8C,GAAInC,EAAEmC,GACNC,MAAOpC,EAAEqC,UACTC,KAAMtC,EAAEsC,KACRjD,YAAaW,EAAEX,eAavBkD,eAAiB,SAASjC,QAASkC,OAAQC,WACpC,CACHD,OAAQA,OACRC,IAAKA,IAAI7C,KAAI,SAAS8C,WAOX,CACHC,OALS9B,oBAAoBP,SAAS,SAASW,kBACxCyB,IAAI7C,UAAYoB,UAAUpB,aAKjC+C,MAAOF,UAsGnBG,uBAAyB,SAAST,MAAOU,cA1FXC,aAC1BP,OACAC,IACAO,OAwFAC,KAAOnD,WAAWsC,MAAMrD,SAAU+D,SAAS/D,SAAUmD,mBAErDe,KAAKrC,aAAaH,QAAUwC,KAAK9C,aAAaM,OAAQ,KAGlDH,QAAUxB,kBAAkBsD,MAAMrD,SAAUqD,MAAMpD,UAClD0B,KAAO5B,kBAAkBgE,SAAS/D,SAAU+D,SAAS9D,UAIrDkE,SAAWpD,WAAWQ,QAASI,MAAM,SAASyC,WAAYC,gBACnDD,WAAWtD,WAAauD,QAAQvD,mBAGpC,CAEHwD,KAAMd,eAAejC,QAAS4C,SAAS/C,aAAc+C,SAAStC,cAE9D7B,U
A7GsBgE,aA6GOG,SAAS9C,QA5G1CoC,OAAS,GACTC,IAAM,GACNO,OAAS,GAIbD,aAAa1C,SAAQ,SAASgD,UACtBF,WAAaE,KAAKtD,EAClBqD,QAAUC,KAAKrD,EAIfsD,aAAexD,WAAWqD,WAAWpE,SAAUqE,QAAQrE,SAAUmD,gBAIjEqB,MAAQzD,WAERwD,aAAanD,aAEbmD,aAAa1C,cACb,SAASb,EAAGC,UAODD,EAAEoC,IAAMnC,EAAEmC,IAAOpC,EAAEsC,WAAarC,EAAEqC,WAAatC,EAAEyD,WAAaxD,EAAEwD,aAO/EhB,OAASA,OAAOiB,OAAOF,MAAMpD,cAM7BoD,MAAM3C,aAAaP,SAAQ,SAASjB,aAG5BuD,OAAS,KAETvD,QAAQC,cAGRsD,OAAS9B,oBAAoBsC,WAAWpE,UAAU,SAASkC,kBACnD7B,QAAQC,aAAe4B,UAAU5B,YAC1BD,QAAQ+C,GAAKlB,UAAUkB,GAEvB/C,QAAQC,YAAc4B,UAAU5B,gBAKnDoD,IAAIhD,KAAK,CACLkD,OAAQA,OACRC,MAAOxD,QACPsD,IAAKS,gBAMbH,OAASA,OAAOS,OAAOF,MAAMnD,QAAQR,KAAI,SAASR,eACvC,CACHuD,OAAQvD,QAAQW,EAChB2D,MAAOtE,QAAQY,UAKpB,CACHyC,IAAKA,IACLD,OAAQA,OACRQ,OAAQA,iBAiCD,MAYXW,4BAA8B,SAASvB,MAAOU,cAC1Cc,kBAAoBC,uBAAuBzB,MAAOU,UAClDgB,sBAAwBC,2BAA2B3B,MAAOU,UAC1DkB,aAAeC,sBAAsB7B,OACrC8B,aAAeD,sBAAsBnB,UACrCqB,mBAAqBP,mBAAqBA,kBAAkBQ,OAASR,kBAAkBS,YACvFC,mBAAqBV,oBAAsBA,kBAAkBQ,KAE7DG,oBAAsBP,cAAgBE,oBAM1CK,oBAHAA,mBAAqBA,oBAAsBJ,oBAAsBG,qBAGI,OAA
1BR,uBAGhC,CACHU,KAAM3F,UAAU4F,mBAAmBC,QAGnCC,cAAeR,qBAAuBL,sBACtCc,QAAS,CACLzC,GAAIW,SAASX,GACb0C,KAAM/B,SAAS+B,KACfC,QAAShC,SAASgC,QAClBC,iBAAkBjC,SAASkC,iBAC3BC,SAAUnC,SAASoC,SACnBC,YAAarC,SAASsC,YACtBC,QAASvC,SAASwC,QAElBC,cAA+B,OAAhBzC,SAASX,GACxBqD,OAAQtB,aAAa/B,GACrBsD,iBAAkBvB,aAAauB,iBAC/BC,SAAUxB,aAAawB,SACvBC,UAAWzB,aAAayB,UACxBC,UAAW1B,aAAa0B,UACxBC,iBAAkB3B,aAAa2B,mBAKpC,MAWPC,yBAA2B,SAAS1D,MAAOU,iBACF,OAAfV,MAAMyC,MAAmC,OAAlB/B,SAAS+B,KAG/C,CACHL,KAAM3F,UAAU4F,mBAAmBsB,KAEnCpB,cAAc,EACdC,QAAS,CACLzC,GAAIW,SAASX,GACb0C,KAAM/B,SAAS+B,KACfC,QAAShC,SAASgC,QAClBG,SAAUnC,SAASoC,SACnBC,YAAarC,SAASsC,YAEtBG,cAA+B,OAAhBzC,SAASX,GACxBsD,kBAAkB,IAKvB,MAWPO,2BAA6B,SAAS5D,MAAOU,iBACxBV,MAAM4C,kBACNlC,SAASkC,iBAGnB,CACHR,KAAM3F,UAAU4F,mBAAmBwB,OACnCtB,cAAc,EACdC,QAAS,CACLzC,GAAIW,SAASX,GACb0C,KAAM/B,SAAS+B,KACfC,QAAShC,SAASgC,QAClBC,iBAAkBjC,SAASkC,iBAC3BC,SAAUnC,SAASoC,SACnBC,YAAarC,SAASsC,YACtBC,QAASvC,SAASwC,QAElBC,cAA+B,OAAhBzC,SAASX,KAIzB,MAWX+D,0BAA4B,SAAS9D,MAAOU,cACxCqD,YAAc/D,MAAMrD,SACpBqH,YAActD,S
AAS/D,YAEvBqH,YAAY3F,OAAS,SACd,QAGP0F,YAAY1F,OAAS,SACd2F,YAAYA,YAAY3F,OAAS,GAAG0B,OAG3CkE,eAAiBF,YAAY/D,MAAMrD,SAAS0B,OAAS,GACrD6F,cAAgBF,YAAYA,YAAY3F,OAAS,GACjD8F,eAAiBJ,YAAY,GAC7BK,cAAgBJ,YAAY,UAE5BC,eAAelE,IAAMmE,cAAcnE,GAC5BmE,cAAcnE,GACdoE,eAAepE,IAAMqE,cAAcrE,GACnCoE,eAAepE,GAGnB,MAUPsE,yBAA2B,SAASrE,MAAOU,kBACtCV,MAAMsE,iBAAkB5D,SAAS4D,mBAE3BtE,MAAMsE,iBAAmB5D,SAAS4D,iBAGlC,MAWXC,0BAA4B,SAASvE,MAAOU,iBACxCV,MAAMwE,yBAA2B9D,SAAS8D,uBACnC,OACC9D,SAAS8D,yBAA0B9D,SAAS+D,oBAE7C/D,SAAS8D,yBAA2B9D,SAAS+D,kBAG7C,MAWXC,qBAAuB,SAAS1E,MAAOU,kBAClCV,MAAMyE,kBAAmB/D,SAAS+D,oBAE5BzE,MAAMyE,kBAAoB/D,SAAS+D,kBAGnC,MAWXE,qBAAuB,SAAS3E,MAAOU,kBAClCV,MAAM4E,kBAAmBlE,SAASkE,oBAE5B5E,MAAM4E,kBAAoBlE,SAASkE,kBAGnC,MAWXC,2BAA6B,SAAS7E,MAAOU,kBACxCV,MAAM8E,wBAAyBpE,SAASoE,0BAElC9E,MAAM8E,wBAA0BpE,SAASoE,wBAGzC,MAWXC,sBAAwB,SAAS/E,MAAOU,aACpCA,SAASsE,oBAAoB3G,OAAQ,KAEjC4G,OAASvE,SAASsE,oBAAoB,UACnCtE,SAASwE,QAAQD,QACrB,OAAIjF,MAAMgF,oBAAoB3G,QAI9B,MAUP8G,wBAA0B,SAASnF,MAAOU,aACtCA,SAAS0E,sBAAsB/G,OAAQ,KAEnC4G,OAASvE,SAAS0E,sB
AAsB,UACrC1E,SAASwE,QAAQD,QACrB,OAAIjF,MAAMoF,sBAAsB/G,QAIhC,MAUPgH,uBAAyB,SAASrF,MAAOU,aACrCA,SAAS4E,qBAAqBjH,OAAQ,KAElC4G,OAASvE,SAAS4E,qBAAqB,UACpC5E,SAASwE,QAAQD,QACrB,OAAIjF,MAAMsF,qBAAqBjH,QAI/B,MAUPkH,0BAA4B,SAASvF,MAAOU,aACxCA,SAAS8E,wBAAwBnH,OAAQ,KAErC4G,OAASvE,SAAS8E,wBAAwB,UACvC9E,SAASwE,QAAQD,QACrB,OAAIjF,MAAMwF,wBAAwBnH,QAIlC,MAUPoH,mCAAqC,SAASzF,MAAOU,cACjDgF,gBAAkB1F,MAAM2F,wBAAwBtH,OAChDuH,gBAAkBlF,SAASiF,wBAAwBtH,cAEnDuH,kBAAoBF,gBACb,CACH1D,MAAM,EACNI,KAAM1B,SAAS0B,KACfyD,6BAA8BnF,SAASmF,8BAEpCH,kBAAoBE,gBACpB,CACH5D,MAAM,GAIP,MAUP8D,+BAAiC,SAAS9F,MAAOU,iBAC5CV,MAAM+F,2BAA6BrF,SAASqF,0BACtCrF,SAAS0B,OACTpC,MAAM+F,4BAA8BrF,SAASqF,4BAIjD,MAUPpE,2BAA6B,SAAS3B,MAAOU,cACzCsF,eAAiBhG,MAAMgG,eACvBpE,aAAeC,sBAAsB7B,OACrC8B,aAAeD,sBAAsBnB,UACrCuF,oBAAuBrE,aAAoBA,aAAasE,gBAAgBC,QAAO,SAASC,gBACjFA,QAAQC,iBAAmBL,gBAAkBI,QAAQhD,QAAUxB,aAAa7B,MAD7C,GAGtCuG,oBAAuBxE,aAAoBA,aAAaoE,gBAAgBC,QAAO,SAASC,gBACjFA,QAAQC,iBAAmBL,gBAAkBI,QAAQhD,QAAUtB,aAAa/B,MAD7C,GAGtCwG,WAAaN,oBAAoB5H,OAAS4H,oBAAoB,GAAK,KACnEO,WA
AaF,oBAAoBjI,OAASiI,oBAAoB,GAAK,YAElEC,YAAcC,WACR1E,eACAyE,aAAeC,aAGf,MAWXC,eAAiB,SAASzG,MAAOU,cAC7BkB,aAAeC,sBAAsB7B,OACrC8B,aAAeD,sBAAsBnB,iBAEpCkB,cAAiBE,cAEVF,cAAgBE,eACjBA,aAAayB,WAAmB,MAC/BzB,cAAgBF,cACjBA,aAAa2B,WAAoB,OACjC3B,aAAa2B,YAAczB,aAAayB,eAEvC3B,aAAa2B,YAAazB,aAAayB,YAGxC,MAVA,MAqBXmD,iBAAmB,SAAS1G,MAAOU,cAC/BiG,eAAiB3G,MAAMgD,YACvB4D,eAAiBlG,SAASsC,mBAEb,OAAbhD,MAAMD,IAA+B,OAAhBW,SAASX,GAEvB,KACa,OAAbC,MAAMD,IAA+B,OAAhBW,SAASX,GAE9B,WACa,OAAbC,MAAMD,IAA+B,OAAhBW,SAASX,GAG9B,OACA4G,gBAAkBC,eAElB,MACCD,gBAAkBC,eACnB,cACAD,iBAAmBC,eACnB,WAEA,MAWXC,aAAe,SAAS7G,MAAOU,cAC3BoG,WAAa9G,MAAMkD,QACnB6D,WAAarG,SAASwC,eAET,OAAblD,MAAMD,IAA+B,OAAhBW,SAASX,GAEvB,KACa,OAAbC,MAAMD,IAA+B,OAAhBW,SAASX,GAE9B,YACa,OAAbC,MAAMD,IAA+B,OAAhBW,SAASX,GAG9B,OACA+G,YAAcC,WAEd,MACCD,YAAcC,WACf,cACAD,aAAeC,WACf,YAEA,MAYXC,eAAiB,SAAShH,MAAOU,cAC7BsF,eAAiBhG,MAAMgG,eACvBpE,aAAeC,sBAAsB7B,OACrC8B,aAAeD,sBAAsBnB,UACrCuG,mBAAsBrF,aAAoBA,aAAasE,gBAAgBC,QAAO,SAASC,gBAC/EA,QAAQhD,QAAU4C,gBAAkBI,QAAQC,iBAAmBzE,aAAa7B,IAC/EqG,QAAQh
D,QAAUxB,aAAa7B,IAAMqG,QAAQC,iBAAmBL,kBAFhC,GAIrCkB,mBAAsBpF,aAAoBA,aAAaoE,gBAAgBC,QAAO,SAASC,gBAC/EA,QAAQhD,QAAU4C,gBAAkBI,QAAQC,iBAAmBvE,aAAa/B,IAC/EqG,QAAQhD,QAAUtB,aAAa/B,IAAMqG,QAAQC,iBAAmBL,kBAFhC,GAIrCmB,sBAAwBF,mBAAmB5I,OAAS,EACpD+I,sBAAwBF,mBAAmB7I,OAAS,SAEnDuD,cAAiBE,aAEXqF,uBAAyBC,sBACzB,KACCD,wBAAyBC,uBAA0BtF,aAAa0B,WAEhE5B,cAAgBE,aACjBA,aAAa0B,UAAY,UAAY,MACpC1B,cAAgBF,aACjBA,aAAa4B,UAAY,cAAgB,KACzC5B,aAAa4B,YAAc1B,aAAa0B,UACxC4D,sBAAwB,kBAAoB,eAC3CxF,aAAa4B,WAAa1B,aAAa0B,UACxC,UAEA,KAVA,kBAJA,MAyBX6D,+BAAiC,SAASrH,MAAOU,kBAC5CV,MAAMsH,uBAAwB5G,SAAS4G,yBAEjCtH,MAAMsH,uBAAyB5G,SAAS4G,uBAGxC,MAWXC,gBAAkB,SAASvH,MAAOU,cAC9B8G,uBAAyBxH,MAAMyH,mBAAmBpJ,OAAS,EAC3DqJ,uBAAyBhH,SAAS+G,mBAAmBpJ,OAAS,EAC9DsJ,2BAA6B3H,MAAMrD,SAAS0B,QAAUqC,SAAS/D,SAAS0B,eAEvEmJ,yBAA0BE,2BAEpBF,yBAA2BE,6BAE3BF,yBAA0BG,6BAG1B,OAWXC,sBAAwB,SAAS5H,MAAOU,cACpCmH,oBAAsB7H,MAAMyH,mBAC5BK,oBAAsBpH,SAAS+G,sBAE/B3I,aAAa+I,oBAAqBC,4BAC3B,SAGPjH,KAAOnD,WAAWmK,oBAAqBC,qBAAqB,SAASnK,EAAGC,UACjED,GAAKC,WAGT,CACHmK,MAAOD,oBAAoBzJ,OAC3BgC,IAA
KQ,KAAKrC,aACV4B,OAAQS,KAAK9C,eAWjB8D,sBAAwB,SAAS7B,cAC1B1C,OAAOC,KAAKyC,MAAMkF,SAASpI,QAAO,SAASC,MAAOkI,eACjDA,QAAUjF,MAAMgG,gBAAmBjJ,QACnCA,MAAQiD,MAAMkF,QAAQD,SAGnBlI,QACR,OAUHiL,uBAAyB,SAAShC,eAAgBiC,SAE9CA,KAAKC,kBACE,MAMPC,sBAHkBF,KAAK/B,gBAAgBC,QAAO,SAASC,gBAChDA,QAAQhD,QAAU4C,gBAAkBI,QAAQC,mBAEXhI,OAAS,SAC9C4J,KAAKG,kBAAoBH,KAAKzE,YAAc2E,uBAUnD1G,uBAAyB,SAASzB,MAAOU,cACrCkB,aAAeC,sBAAsB7B,OACrC8B,aAAeD,sBAAsBnB,UACrC2H,YAAcrI,MAAMrD,SAAS0B,OAAS,EACtC4D,YAAcvB,SAAS/D,SAAS0B,OAAS,EACzC2H,eAAiBtF,SAASsF,eAC1BsC,2BAA6B1G,cAAgBoG,uBAAuBhC,eAAgBpE,cACpF2G,2BAA6BzG,cAAgBkG,uBAAuBhC,eAAgBlE,cAEpF0G,oBAA2C,IADvBnD,uBAAuBrF,MAAOU,cAIjDV,MAAMwE,yBAA2B9D,SAAS8D,8BACpC,SAIN5C,eAAiBE,oBACX,SAINF,cAAgB2G,iCACV,CACHvG,MAAM,EACNC,YAAaA,YACbgG,KAAMnG,iBAOV0G,oBAAsBD,iCACf,CACHvG,MAAM,EACNC,YAAaA,YACbgG,KAAMnG,iBAKV9B,MAAMwE,wBAA0B9D,SAAS8D,uBAAwB,KAC5D8D,4BAA8BC,iCACxB,CACHvG,MAAM,EACNC,YAAaA,YACbgG,KAAMnG,iBAIVwG,6BAA+BC,iCACxB,CACHvG,MAAM,EACNC,YAAaA,oBAMpBjC,MAAMwE,wBAA0B9D,SAAS8D,wBACtC+D,2BACO,CACHvG,MAAM,EACNC,Y
AAaA,YACbgG,KAAMnG,cAMd9B,MAAMwE,yBAA2B9D,SAAS8D,wBACtC8D,2BACO,CACHtG,MAAM,EACNC,YAAaoG,aAKlB,MAmEPI,4BAA8B,SAASzI,MAAOU,cAC1CgI,qBAAuBnE,0BAA0BvE,MAAOU,UACxDiI,WAAapB,gBAAgBvH,MAAOU,UACpCc,kBAAoBC,uBAAuBzB,MAAOU,UAClDkI,eA7DkB,SAAS5I,MAAOU,cAClCkB,aAAeC,sBAAsB7B,OACrC8B,aAAeD,sBAAsBnB,iBAEpCkB,cAAiBE,aAEXF,eAAiBE,cACjBF,aAAa2B,WAAoB,MAChC3B,cAAgBE,eACjBA,aAAayB,WAAmB,OAC/B3B,aAAa2B,YAAazB,aAAayB,cAExC3B,aAAa2B,YAAczB,aAAayB,YAI5C,KAXI,KAwDUsF,CAAoB7I,MAAOU,UAC5CoI,gBApCmB,SAAS9I,MAAOU,cACnCkB,aAAeC,sBAAsB7B,OACrC8B,aAAeD,sBAAsBnB,iBAErCA,SAAS0B,MAAQ3F,UAAU4F,mBAAmBsB,KAEvC,KAGN/B,cAAiBE,aAEXF,eAAiBE,cACjBF,aAAasG,YAAa,MACzBtG,cAAgBE,cACjBA,aAAaoG,YAAa,QACzBtG,aAAasG,YAAcpG,aAAaoG,iBAEzCtG,aAAasG,YAAepG,aAAaoG,aAI7C,MAXI,KA0BWa,CAAqB/I,MAAOU,UAC9CsI,sBAA8C,OAAtBxH,kBAA6BA,kBAAkBQ,MAAQR,kBAAkBS,YAAc,KAC/GgH,UAAYpH,sBAAsBnB,UAClCwI,oBAAsB,SAASC,WAAYC,kBACvCD,kBACOC,cACJ,GAAmB,OAAfD,aAAwBA,WAAY,KACtCF,gBACM,CAAC7G,KAAM,WACX,GAAI6G,UAAU1F,gBACV,CAACnB,KAAM,WACX,GAAI1B,SAAS/D,SAAS0B,QAAU2J,uBAAuBtH,SAASsF,eAAgBi
D,iBAC5E,CACH7G,KAAM,cACN6F,KAAMgB,WAEP,IAAKA,UAAUf,YAAee,UAAUb,kBAAoBa,UAAUzF,gBAClE,CAACpB,KAAM,4BAIf,SAIkB,OAAzBsG,sBACe,OAAfC,YACsB,OAAtBnH,mBACmB,OAAnBoH,sBAEO,aAGPS,OAAS,CACT,CAACX,qBAAsB,CAACtG,KAAM,gBAC9B,CAACuG,WAAY,CAACvG,KAAM,cACpB,CAAC0G,gBAAiB,CAAC1G,KAAM,sBACzB,CAACwG,eAAgB,CAACxG,KAAM,YACxB,CAAC4G,sBAAuB,CAAC5G,KAAM,cAAe6F,KAAMgB,aAG/CrK,EAAI,EAAGA,EAAIyK,OAAOhL,OAAQO,IAAK,KAGhC0K,OAASJ,oBAFIG,OAAOzK,GAAG,GACPyK,OAAOzK,GAAG,OAGf,OAAX0K,cACOA,aAIR,CACHlH,KAAM,YAWVmH,2BAA6B,SAASvJ,MAAOU,cACzCgI,qBAAuBnE,0BAA0BvE,MAAOU,UACxDiI,WAAapB,gBAAgBvH,MAAOU,iBAEX,OAAzBgI,sBAAgD,OAAfC,WAC1B,KAGPD,qBACO,CAACtG,KAAM,eAGduG,WACO,CAACvG,KAAM,aAGX,CACHA,KAAM,YAYVoH,WAAa,SAASxJ,MAAOU,cACzB+I,QAAUzJ,MAAMoC,KAChBsH,QAAUhJ,SAAS0B,KACnBuH,kBAAoB3J,MAAMD,GAC1B6J,kBAAoBlJ,SAASX,GAC7B8J,aAAevM,OAAOC,KAAKyC,MAAMkF,SACjC4E,aAAexM,OAAOC,KAAKmD,SAASwE,SAExC2E,aAAa9K,OACb+K,aAAa/K,WAETgL,iBAAmBF,aAAa3K,OAAM,SAASa,GAAI3B,cAC5C2B,IAAM+J,aAAa1L,iBAG1BqL,SAAWC,aAGJC,mBAAsBC,wBAKtBD,oBAAqBC,mBAAqBD,mBAAqBC,uBAG9DD,mBAAsBC,mBAAsB
G,mBAQjD,SAYPC,6BAA+B,SAAShK,MAAOU,iBAC3CV,MAAMoC,MAAQ1B,SAAS0B,KACf1B,SAAS0B,MAAQ3F,UAAU4F,mBAAmBsB,KAGnD,MAWPsG,wBAA0B,SAASjK,MAAOU,cACtCsF,eAAiBtF,SAASsF,eAC1BpE,aAAeC,sBAAsB7B,OACrC8B,aAAeD,sBAAsBnB,UACrCwJ,gBAAmBtI,aAAoBA,aAAasE,gBAAgBC,QAAO,SAASC,gBAC7EA,QAAQhD,QAAU4C,kBADS,GAGlCmE,gBAAmBrI,aAAoBA,aAAaoE,gBAAgBC,QAAO,SAASC,gBAC7EA,QAAQhD,QAAU4C,kBADS,GAGlCO,WAAa2D,gBAAgB7L,OAAS,EACtCmI,WAAa2D,gBAAgB9L,OAAS,SAErCkI,aAAcC,YAAe1E,aAAa0B,YAEpC5B,eAAiBA,aAAa4B,WAAagD,YAAc1E,aAAa0B,eAGtE+C,aAAeC,aAGf,MAPA1E,aAAasI,gBAoFrB,CACHC,WAlEa,SAASrK,MAAOU,cACzB4J,OAAS,CACTC,IAAK,CACDC,MAAOhB,WACPiB,aAAchK,uBACdiK,gBAAiB5G,0BACjBQ,eAAgBD,yBAChBqE,qBAAsBnE,0BACtBE,gBAAiBC,qBACjBiG,8BAA+BlF,mCAC/BkD,WAAYpB,gBACZqD,iBAAkBhD,sBAClB5E,YAAa0D,iBACbxD,QAAS2D,aACTjC,gBAAiBD,qBACjBG,sBAAuBD,6BAI/ByF,OAAO7N,UAAU4F,mBAAmBC,SAAW,CAC3CuI,OAAQtJ,4BACRuJ,OAAQrC,4BACRsC,iBAAkBhG,sBAClBiG,mBAAoB7F,wBACpB8F,kBAAmB5F,uBACnB6F,qBAAsB3F,0BACtB7D,sBAAuBC,2BACvBwJ,0BAA2BrF,+BAC3BsF,UAAW3E,eACX4E,UAAWrE,eACXM,qBAAsBD,+BACtB7F,kBAAmBC,uBACnB6
J,mBAAoBrB,yBAGxBK,OAAO7N,UAAU4F,mBAAmBwB,QAAU,CAC1CgH,OAAQjH,2BACRkH,OAAQvB,4BAGZe,OAAO7N,UAAU4F,mBAAmBsB,MAAQ,CACxCkH,OAAQnH,yBACRoH,OAAQvB,2BACR4B,0BAA2BrF,+BAC3ByF,wBAAyBvB,kCAGzBwB,YAAcjP,EAAEkP,OAAO,GAAInB,OAAOC,YAClC7J,SAAS0B,MAAQ1B,SAAS0B,QAAQkI,SAElCkB,YAAcjP,EAAEkP,OAAOD,YAAalB,OAAO5J,SAAS0B,QAGjD9E,OAAOC,KAAKiO,aAAa1O,QAAO,SAASqE,MAAO5B,SAE/CiB,OAAQkL,EADIF,YAAYjM,MACNS,MAAOU,iBAEf,OAAVF,QACAW,MAAM5B,KAAOiB,OAGVW,QACR"}