Extracted `messageEdit` and `messageEditCancel` from `.mes_edit` and `.mes_edit_cancel` (#4633) * Extracted `messageEdit` and `messageEditCancel` from `.mes_edit` and `.mes_edit_cancel` * Fixed. https://github.com/SillyTavern/SillyTavern/pull/4633#discussion_r241505 https://github.com/SillyTavern/SillyTavern/pull/4633#pullrequestreview-3316588180 * Fixed.. https://github.com/SillyTavern/SillyTavern/pull/4633#issuecomment-3383321321 https://github.com/SillyTavern/SillyTavern/pull/4633#pullrequestreview-3316602505 * Fixed... * Fixed.... https://github.com/SillyTavern/SillyTavern/pull/4633#discussion_r2415124843 https://github.com/SillyTavern/SillyTavern/pull/4633#discussion_r2415124845 * Fix type conversion errors * Fix type error * Fix being unable to close message edit on first message with escape * Fixed. https://github.com/SillyTavern/SillyTavern/pull/4633#issuecomment-3412364573 Another bug found (sorry): Open edit on the last message, move it up, then move it back down. Swipe buttons appear, as far as I understand your intention, they shan't. * showSwipeButtons: return early if message edit is open --------- Co-authored-by: user <user@exmaple.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
Signed| @@ -554,9 +554,10 @@ export let is_send_press = false; //Send generation | |||
| 554 | 554 | ||
| 555 | let this_del_mes = -1; | 555 | let this_del_mes = -1; |
| 556 | 556 | ||
| 557 | //message editing | 557 | /** @type {string} */ |
| 558 | var this_edit_mes_chname = ''; | 558 | let this_edit_mes_chname = ''; |
| 559 | var this_edit_mes_id; | 559 | /** @type {number|undefined} */ |
| 560 | let this_edit_mes_id = undefined; | ||
| 560 | 561 | ||
| 561 | //settings | 562 | //settings |
| 562 | export let settings; | 563 | export let settings; |
| @@ -1494,7 +1495,7 @@ export async function reloadCurrentChat() { | |||
| 1494 | export async function sendTextareaMessage() { | 1495 | export async function sendTextareaMessage() { |
| 1495 | if (is_send_press) return; | 1496 | if (is_send_press) return; |
| 1496 | if (isExecutingCommandsFromChatInput) return; | 1497 | if (isExecutingCommandsFromChatInput) return; |
| 1497 | if (this_edit_mes_id) return; // don't proceed if editing a message | 1498 | if (this_edit_mes_id >= 0) return; // don't proceed if editing a message |
| 1498 | 1499 | ||
| 1499 | let generateType; | 1500 | let generateType; |
| 1500 | // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last | 1501 | // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last |
| @@ -7210,6 +7211,117 @@ function messageEditAuto(div) { | |||
| 7210 | saveChatDebounced(); | 7211 | saveChatDebounced(); |
| 7211 | } | 7212 | } |
| 7212 | 7213 | ||
| 7214 | /** | ||
| 7215 | * Create the message edit UI. | ||
| 7216 | * @param {number} edit_mes_id | ||
| 7217 | */ | ||
| 7218 | async function messageEdit(edit_mes_id) { | ||
| 7219 | hideSwipeButtons(); | ||
| 7220 | let chatScrollPosition = chatElement.scrollTop(); | ||
| 7221 | |||
| 7222 | this_edit_mes_id = edit_mes_id; | ||
| 7223 | |||
| 7224 | let thisMesDiv = chatElement.children().filter(`.mes[mesid="${edit_mes_id}"]`); | ||
| 7225 | |||
| 7226 | let thisMesBlock = thisMesDiv.find('.mes_block'); | ||
| 7227 | let thisMesText = thisMesBlock.find('.mes_text'); | ||
| 7228 | |||
| 7229 | thisMesText.empty(); | ||
| 7230 | thisMesBlock.find('.mes_buttons').css('display', 'none'); | ||
| 7231 | thisMesBlock.find('.mes_edit_buttons').css('display', 'inline-flex'); | ||
| 7232 | |||
| 7233 | // Also edit reasoning, if it exists | ||
| 7234 | const reasoningEdit = thisMesBlock.find('.mes_reasoning_edit:visible'); | ||
| 7235 | if (reasoningEdit.length > 0) { | ||
| 7236 | reasoningEdit.trigger('click'); | ||
| 7237 | } | ||
| 7238 | |||
| 7239 | let text = chat[edit_mes_id]['mes']; | ||
| 7240 | if (chat[edit_mes_id]['is_user']) { | ||
| 7241 | this_edit_mes_chname = name1; | ||
| 7242 | } else if (chat[edit_mes_id]['force_avatar']) { | ||
| 7243 | this_edit_mes_chname = chat[edit_mes_id]['name']; | ||
| 7244 | } else { | ||
| 7245 | this_edit_mes_chname = name2; | ||
| 7246 | } | ||
| 7247 | if (power_user.trim_spaces) { | ||
| 7248 | text = text.trim(); | ||
| 7249 | } | ||
| 7250 | thisMesText.append( | ||
| 7251 | '<textarea id=\'curEditTextarea\' class=\'edit_textarea mdHotkeys\'></textarea>', | ||
| 7252 | ); | ||
| 7253 | |||
| 7254 | let edit_textarea = thisMesBlock.find('.edit_textarea'); | ||
| 7255 | edit_textarea.val(text); | ||
| 7256 | |||
| 7257 | const cssAutofit = CSS.supports('field-sizing', 'content'); | ||
| 7258 | if (!cssAutofit) { | ||
| 7259 | edit_textarea.height(0); | ||
| 7260 | edit_textarea.height(edit_textarea[0].scrollHeight); | ||
| 7261 | } | ||
| 7262 | edit_textarea.trigger('focus'); | ||
| 7263 | const textAreaElement = /** @type {HTMLTextAreaElement} */ (edit_textarea[0]); | ||
| 7264 | // Sets the cursor at the end of the text | ||
| 7265 | textAreaElement.setSelectionRange( | ||
| 7266 | String(edit_textarea.val()).length, | ||
| 7267 | String(edit_textarea.val()).length, | ||
| 7268 | ); | ||
| 7269 | if (Number(this_edit_mes_id) === chat.length - 1) { | ||
| 7270 | chatElement.scrollTop(chatScrollPosition); | ||
| 7271 | } | ||
| 7272 | |||
| 7273 | updateEditArrowClasses(); | ||
| 7274 | } | ||
| 7275 | |||
| 7276 | /** | ||
| 7277 | * Close the open message editor. | ||
| 7278 | * This deletes the user's unsaved changes. | ||
| 7279 | * @param {number} [messageId=this_edit_mes_id] | ||
| 7280 | */ | ||
| 7281 | async function messageEditCancel(messageId = this_edit_mes_id) { | ||
| 7282 | let text = chat[messageId]['mes']; | ||
| 7283 | let thisMesDiv; | ||
| 7284 | // If this is the button then select it's parent. Otherwise, select by messageId. | ||
| 7285 | if (this?.classList?.contains('mes_edit_cancel')) { | ||
| 7286 | thisMesDiv = $(this).closest('.mes'); | ||
| 7287 | } else | ||
| 7288 | { | ||
| 7289 | thisMesDiv = chatElement.children().filter(`[mesid="${messageId}"]`); | ||
| 7290 | } | ||
| 7291 | |||
| 7292 | const thisMesBlock = thisMesDiv.find('.mes_block'); | ||
| 7293 | thisMesBlock.find('.mes_text').empty(); | ||
| 7294 | thisMesDiv.find('.mes_edit_buttons').css('display', 'none'); | ||
| 7295 | thisMesBlock.find('.mes_buttons').css('display', ''); | ||
| 7296 | thisMesBlock.find('.mes_text') | ||
| 7297 | .append(messageFormatting( | ||
| 7298 | text, | ||
| 7299 | this_edit_mes_chname, | ||
| 7300 | chat[messageId].is_system, | ||
| 7301 | chat[messageId].is_user, | ||
| 7302 | messageId, | ||
| 7303 | {}, | ||
| 7304 | false, | ||
| 7305 | )); | ||
| 7306 | appendMediaToMessage(chat[messageId], thisMesDiv); | ||
| 7307 | addCopyToCodeBlocks(thisMesDiv); | ||
| 7308 | |||
| 7309 | const reasoningEditDone = thisMesBlock.find('.mes_reasoning_edit_cancel:visible'); | ||
| 7310 | if (reasoningEditDone.length > 0) { | ||
| 7311 | reasoningEditDone.trigger('click'); | ||
| 7312 | } | ||
| 7313 | |||
| 7314 | await eventSource.emit(event_types.MESSAGE_UPDATED, messageId); | ||
| 7315 | if (messageId == this_edit_mes_id) { | ||
| 7316 | this_edit_mes_id = undefined; | ||
| 7317 | } | ||
| 7318 | else { | ||
| 7319 | console.warn(`The message editor was closed on message #${messageId} while #${this_edit_mes_id} is being edited.`); | ||
| 7320 | } | ||
| 7321 | |||
| 7322 | showSwipeButtons(); | ||
| 7323 | } | ||
| 7324 | |||
| 7213 | async function messageEditDone(div) { | 7325 | async function messageEditDone(div) { |
| 7214 | let { mesBlock, text, mes, bias } = updateMessage(div); | 7326 | let { mesBlock, text, mes, bias } = updateMessage(div); |
| 7215 | if (this_edit_mes_id == 0) { | 7327 | if (this_edit_mes_id == 0) { |
| @@ -7245,6 +7357,7 @@ async function messageEditDone(div) { | |||
| 7245 | await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id); | 7357 | await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id); |
| 7246 | this_edit_mes_id = undefined; | 7358 | this_edit_mes_id = undefined; |
| 7247 | await saveChatConditional(); | 7359 | await saveChatConditional(); |
| 7360 | showSwipeButtons(); | ||
| 7248 | } | 7361 | } |
| 7249 | 7362 | ||
| 7250 | /** | 7363 | /** |
| @@ -7901,7 +8014,7 @@ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, w | |||
| 7901 | } | 8014 | } |
| 7902 | 8015 | ||
| 7903 | export function showSwipeButtons() { | 8016 | export function showSwipeButtons() { |
| 7904 | if (chat.length === 0) { | 8017 | if (chat.length === 0 || this_edit_mes_id >= 0) { |
| 7905 | return; | 8018 | return; |
| 7906 | } | 8019 | } |
| 7907 | 8020 | ||
| @@ -8105,7 +8218,7 @@ function updateEditArrowClasses() { | |||
| 8105 | chatElement.find('.mes .mes_edit_up').removeClass('disabled'); | 8218 | chatElement.find('.mes .mes_edit_up').removeClass('disabled'); |
| 8106 | chatElement.find('.mes .mes_edit_down').removeClass('disabled'); | 8219 | chatElement.find('.mes .mes_edit_down').removeClass('disabled'); |
| 8107 | 8220 | ||
| 8108 | if (this_edit_mes_id !== undefined) { | 8221 | if (this_edit_mes_id >= 0) { |
| 8109 | const down = chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_down`); | 8222 | const down = chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_down`); |
| 8110 | const up = chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_up`); | 8223 | const up = chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_up`); |
| 8111 | const lastId = Number(chatElement.find('.mes').last().attr('mesid')); | 8224 | const lastId = Number(chatElement.find('.mes').last().attr('mesid')); |
| @@ -8127,7 +8240,7 @@ function updateEditArrowClasses() { | |||
| 8127 | */ | 8240 | */ |
| 8128 | export function closeMessageEditor(what = 'all') { | 8241 | export function closeMessageEditor(what = 'all') { |
| 8129 | if (what === 'message' || what === 'all') { | 8242 | if (what === 'message' || what === 'all') { |
| 8130 | if (this_edit_mes_id) { | 8243 | if (this_edit_mes_id >= 0) { |
| 8131 | chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).trigger('click'); | 8244 | chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).trigger('click'); |
| 8132 | } | 8245 | } |
| 8133 | } | 8246 | } |
| @@ -10046,7 +10159,7 @@ jQuery(async function () { | |||
| 10046 | } | 10159 | } |
| 10047 | 10160 | ||
| 10048 | else if (id == 'option_continue') { | 10161 | else if (id == 'option_continue') { |
| 10049 | if (this_edit_mes_id) return; // don't proceed if editing a message | 10162 | if (this_edit_mes_id >= 0) return; // don't proceed if editing a message |
| 10050 | 10163 | ||
| 10051 | if (is_send_press == false || fromSlashCommand) { | 10164 | if (is_send_press == false || fromSlashCommand) { |
| 10052 | is_send_press = true; | 10165 | is_send_press = true; |
| @@ -10236,8 +10349,7 @@ jQuery(async function () { | |||
| 10236 | return; | 10349 | return; |
| 10237 | }*/ | 10350 | }*/ |
| 10238 | 10351 | ||
| 10239 | let chatScrollPosition = chatElement.scrollTop(); | 10352 | if (this_edit_mes_id >= 0) { |
| 10240 | if (this_edit_mes_id !== undefined) { | ||
| 10241 | let mes_edited = chatElement.find(`[mesid="${this_edit_mes_id}"]`).find('.mes_edit_done'); | 10353 | let mes_edited = chatElement.find(`[mesid="${this_edit_mes_id}"]`).find('.mes_edit_done'); |
| 10242 | if (Number(edit_mes_id) == chat.length - 1) { //if the generating swipe (...) | 10354 | if (Number(edit_mes_id) == chat.length - 1) { //if the generating swipe (...) |
| 10243 | let run_edit = true; | 10355 | let run_edit = true; |
| @@ -10252,55 +10364,9 @@ jQuery(async function () { | |||
| 10252 | } | 10364 | } |
| 10253 | await messageEditDone(mes_edited); | 10365 | await messageEditDone(mes_edited); |
| 10254 | } | 10366 | } |
| 10255 | $(this).closest('.mes_block').find('.mes_text').empty(); | 10367 | var edit_mes_id = Number($(this).closest('.mes').attr('mesid')); |
| 10256 | $(this).closest('.mes_block').find('.mes_buttons').css('display', 'none'); | ||
| 10257 | $(this).closest('.mes_block').find('.mes_edit_buttons').css('display', 'inline-flex'); | ||
| 10258 | var edit_mes_id = $(this).closest('.mes').attr('mesid'); | ||
| 10259 | this_edit_mes_id = edit_mes_id; | ||
| 10260 | |||
| 10261 | // Also edit reasoning, if it exists | ||
| 10262 | const reasoningEdit = $(this).closest('.mes_block').find('.mes_reasoning_edit:visible'); | ||
| 10263 | if (reasoningEdit.length > 0) { | ||
| 10264 | reasoningEdit.trigger('click'); | ||
| 10265 | } | ||
| 10266 | 10368 | ||
| 10267 | var text = chat[edit_mes_id]['mes']; | 10369 | await messageEdit(edit_mes_id); |
| 10268 | if (chat[edit_mes_id]['is_user']) { | ||
| 10269 | this_edit_mes_chname = name1; | ||
| 10270 | } else if (chat[edit_mes_id]['force_avatar']) { | ||
| 10271 | this_edit_mes_chname = chat[edit_mes_id]['name']; | ||
| 10272 | } else { | ||
| 10273 | this_edit_mes_chname = name2; | ||
| 10274 | } | ||
| 10275 | if (power_user.trim_spaces) { | ||
| 10276 | text = text.trim(); | ||
| 10277 | } | ||
| 10278 | $(this) | ||
| 10279 | .closest('.mes_block') | ||
| 10280 | .find('.mes_text') | ||
| 10281 | .append( | ||
| 10282 | '<textarea id=\'curEditTextarea\' class=\'edit_textarea mdHotkeys\'></textarea>', | ||
| 10283 | ); | ||
| 10284 | $('#curEditTextarea').val(text); | ||
| 10285 | let edit_textarea = $(this) | ||
| 10286 | .closest('.mes_block') | ||
| 10287 | .find('.edit_textarea'); | ||
| 10288 | if (!cssAutofit) { | ||
| 10289 | edit_textarea.height(0); | ||
| 10290 | edit_textarea.height(edit_textarea[0].scrollHeight); | ||
| 10291 | } | ||
| 10292 | edit_textarea.trigger('focus'); | ||
| 10293 | const textAreaElement = /** @type {HTMLTextAreaElement} */ (edit_textarea[0]); | ||
| 10294 | // Sets the cursor at the end of the text | ||
| 10295 | textAreaElement.setSelectionRange( | ||
| 10296 | String(edit_textarea.val()).length, | ||
| 10297 | String(edit_textarea.val()).length, | ||
| 10298 | ); | ||
| 10299 | if (Number(this_edit_mes_id) === chat.length - 1) { | ||
| 10300 | chatElement.scrollTop(chatScrollPosition); | ||
| 10301 | } | ||
| 10302 | |||
| 10303 | updateEditArrowClasses(); | ||
| 10304 | } | 10370 | } |
| 10305 | }); | 10371 | }); |
| 10306 | 10372 | ||
| @@ -10379,33 +10445,7 @@ jQuery(async function () { | |||
| 10379 | }); | 10445 | }); |
| 10380 | 10446 | ||
| 10381 | $(document).on('click', '.mes_edit_cancel', async function () { | 10447 | $(document).on('click', '.mes_edit_cancel', async function () { |
| 10382 | let text = chat[this_edit_mes_id]['mes']; | 10448 | await messageEditCancel.call(this, this_edit_mes_id); |
| 10383 | |||
| 10384 | $(this).closest('.mes_block').find('.mes_text').empty(); | ||
| 10385 | $(this).closest('.mes_edit_buttons').css('display', 'none'); | ||
| 10386 | $(this).closest('.mes_block').find('.mes_buttons').css('display', ''); | ||
| 10387 | $(this) | ||
| 10388 | .closest('.mes_block') | ||
| 10389 | .find('.mes_text') | ||
| 10390 | .append(messageFormatting( | ||
| 10391 | text, | ||
| 10392 | this_edit_mes_chname, | ||
| 10393 | chat[this_edit_mes_id].is_system, | ||
| 10394 | chat[this_edit_mes_id].is_user, | ||
| 10395 | this_edit_mes_id, | ||
| 10396 | {}, | ||
| 10397 | false, | ||
| 10398 | )); | ||
| 10399 | appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes')); | ||
| 10400 | addCopyToCodeBlocks($(this).closest('.mes')); | ||
| 10401 | |||
| 10402 | const reasoningEditDone = $(this).closest('.mes_block').find('.mes_reasoning_edit_cancel:visible'); | ||
| 10403 | if (reasoningEditDone.length > 0) { | ||
| 10404 | reasoningEditDone.trigger('click'); | ||
| 10405 | } | ||
| 10406 | |||
| 10407 | await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id); | ||
| 10408 | this_edit_mes_id = undefined; | ||
| 10409 | }); | 10449 | }); |
| 10410 | 10450 | ||
| 10411 | $(document).on('click', '.mes_edit_up', async function () { | 10451 | $(document).on('click', '.mes_edit_up', async function () { |
| @@ -10413,7 +10453,6 @@ jQuery(async function () { | |||
| 10413 | return; | 10453 | return; |
| 10414 | } | 10454 | } |
| 10415 | 10455 | ||
| 10416 | hideSwipeButtons(); | ||
| 10417 | const targetId = Number(this_edit_mes_id) - 1; | 10456 | const targetId = Number(this_edit_mes_id) - 1; |
| 10418 | const target = chatElement.find(`.mes[mesid="${targetId}"]`); | 10457 | const target = chatElement.find(`.mes[mesid="${targetId}"]`); |
| 10419 | const root = $(this).closest('.mes'); | 10458 | const root = $(this).closest('.mes'); |
| @@ -10434,7 +10473,6 @@ jQuery(async function () { | |||
| 10434 | this_edit_mes_id = targetId; | 10473 | this_edit_mes_id = targetId; |
| 10435 | updateViewMessageIds(); | 10474 | updateViewMessageIds(); |
| 10436 | await saveChatConditional(); | 10475 | await saveChatConditional(); |
| 10437 | showSwipeButtons(); | ||
| 10438 | }); | 10476 | }); |
| 10439 | 10477 | ||
| 10440 | $(document).on('click', '.mes_edit_down', async function () { | 10478 | $(document).on('click', '.mes_edit_down', async function () { |
| @@ -10442,7 +10480,6 @@ jQuery(async function () { | |||
| 10442 | return; | 10480 | return; |
| 10443 | } | 10481 | } |
| 10444 | 10482 | ||
| 10445 | hideSwipeButtons(); | ||
| 10446 | const targetId = Number(this_edit_mes_id) + 1; | 10483 | const targetId = Number(this_edit_mes_id) + 1; |
| 10447 | const target = chatElement.find(`.mes[mesid="${targetId}"]`); | 10484 | const target = chatElement.find(`.mes[mesid="${targetId}"]`); |
| 10448 | const root = $(this).closest('.mes'); | 10485 | const root = $(this).closest('.mes'); |
| @@ -10463,7 +10500,6 @@ jQuery(async function () { | |||
| 10463 | this_edit_mes_id = targetId; | 10500 | this_edit_mes_id = targetId; |
| 10464 | updateViewMessageIds(); | 10501 | updateViewMessageIds(); |
| 10465 | await saveChatConditional(); | 10502 | await saveChatConditional(); |
| 10466 | showSwipeButtons(); | ||
| 10467 | }); | 10503 | }); |
| 10468 | 10504 | ||
| 10469 | $(document).on('click', '.mes_edit_copy', async function () { | 10505 | $(document).on('click', '.mes_edit_copy', async function () { |
| @@ -10493,7 +10529,7 @@ jQuery(async function () { | |||
| 10493 | 10529 | ||
| 10494 | $(document).on('click', '.mes_edit_delete', async function (event, customData) { | 10530 | $(document).on('click', '.mes_edit_delete', async function (event, customData) { |
| 10495 | const fromSlashCommand = customData?.fromSlashCommand || false; | 10531 | const fromSlashCommand = customData?.fromSlashCommand || false; |
| 10496 | const canDeleteSwipe = (Array.isArray(chat[this_edit_mes_id].swipes) && chat[this_edit_mes_id].swipes.length > 1 && !chat[this_edit_mes_id].is_user && parseInt(this_edit_mes_id) === chat.length - 1); | 10532 | const canDeleteSwipe = (Array.isArray(chat[this_edit_mes_id].swipes) && chat[this_edit_mes_id].swipes.length > 1 && !chat[this_edit_mes_id].is_user && Number(this_edit_mes_id) === chat.length - 1); |
| 10497 | 10533 | ||
| 10498 | let deleteOnlySwipe = false; | 10534 | let deleteOnlySwipe = false; |
| 10499 | if (power_user.confirm_message_delete && fromSlashCommand !== true) { | 10535 | if (power_user.confirm_message_delete && fromSlashCommand !== true) { |
| @@ -10886,7 +10922,7 @@ jQuery(async function () { | |||
| 10886 | $('#send_textarea').trigger('focus'); | 10922 | $('#send_textarea').trigger('focus'); |
| 10887 | return; | 10923 | return; |
| 10888 | } | 10924 | } |
| 10889 | if (!this_edit_mes_id && $('#mes_stop').is(':visible')) { | 10925 | if (this_edit_mes_id === undefined && $('#mes_stop').is(':visible')) { |
| 10890 | $('#mes_stop').trigger('click'); | 10926 | $('#mes_stop').trigger('click'); |
| 10891 | if (chat.length && Array.isArray(chat[chat.length - 1].swipes) && chat[chat.length - 1].swipe_id == chat[chat.length - 1].swipes.length) { | 10927 | if (chat.length && Array.isArray(chat[chat.length - 1].swipes) && chat[chat.length - 1].swipe_id == chat[chat.length - 1].swipes.length) { |
| 10892 | $('.last_mes .swipe_left').trigger('click'); | 10928 | $('.last_mes .swipe_left').trigger('click'); |