1/** 2 * Shortcode AI Chat 3 * 4 * @package ThemeREX Addons 5 * @since v2.22.0 6 */ 7 8/* global jQuery, TRX_ADDONS_STORAGE */ 9 10 11jQuery( document ).ready( function() { 12 13 "use strict"; 14 15 var $window = jQuery( window ), 16 $document = jQuery( document ), 17 $body = jQuery( 'body' ); 18 19 $document.on( 'action.init_hidden_elements', function(e, container) { 20 21 if ( container === undefined ) { 22 container = $body; 23 } 24 25 // Init AI Chat 26 container.find( '.sc_chat:not(.sc_chat_inited)' ).each( function() { 27 28 var $sc = jQuery( this ).addClass( 'sc_chat_inited' ), 29 sc_id = $sc.attr('id') || '', 30 $form = $sc.find( '.sc_chat_form' ), 31 $prompt = $sc.find( '.sc_chat_form_field_prompt_text' ), 32 $button = $sc.find( '.sc_chat_form_field_prompt_button' ), 33 $attachments_field = $sc.find( '.sc_chat_form_field_upload_attachments_field' ), 34 $attachments_list = $sc.find( '.sc_chat_form_field_upload_attachments_list' ), 35 $result = $sc.find( '.sc_chat_list' ), 36 $start_new = $sc.find( '.sc_chat_form_start_new' ), 37 chat = [], 38 chat_position = 0; 39 40 // Return an attachment type by file name 41 var attachment_type = function( file ) { 42 var ext = trx_addons_get_file_ext( file ).toLowerCase(); 43 return ['ai', 'avi', 'css', 'csv', 'dbf', 'doc', 'docx', 'dwg', 'gif', 'htm', 'html', 44 'jpg', 'jpeg', 'js', 'json', 'mp3', 'mp4', 'pdf', 'png', 'ppt', 'pptx', 45 'psd', 'rtf', 'svg', 'txt', 'xls', 'xlsx', 'xml', 'zip' ].indexOf( ext ) >= 0 ? ext : 'unknown'; 46 }; 47 48 // Update the list of attachments on change the file field 49 if ( $attachments_field.length ) { 50 $attachments_field.on( 'change', function(e) { 51 var files = e.target.files; 52 if ( files && files.length ) { 53 var clear_icon = $attachments_list.data( 'clear-icon' ) || 'trx_addons_icon-cancel-2'; 54 $attachments_list.empty(); 55 for ( var i = 0; i < files.length; i++ ) { 56 $attachments_list.append( 57 '<span class="sc_chat_form_field_upload_attachments_list_item" title="' + files[i].name + '">' 58 + '<span class="sc_chat_form_field_upload_attachments_list_item_icon" style="background-image: url(' + TRX_ADDONS_STORAGE['plugin_url'] + '/css/file-types/' + attachment_type( files[i].name ) + '.png)"></span>' 59 + '<span class="sc_chat_form_field_upload_attachments_list_item_name">' + files[i].name + '</span>' 60 + '</span>' 61 ); 62 } 63 $attachments_list.append( 64 '<a href="#" class="sc_chat_form_field_upload_attachments_list_clear">' 65 + '<span class="sc_chat_form_field_upload_attachments_list_clear_icon ' + clear_icon + '"></span>' 66 + '<span class="sc_chat_form_field_upload_attachments_list_clear_name">' + TRX_ADDONS_STORAGE['msg_ai_helper_sc_chat_clear'] + '</span>' 67 + '</a>' 68 ); 69 $sc.addClass( 'sc_chat_with_attachments_selected' ); 70 } else { 71 $attachments_list.empty(); 72 $sc.removeClass( 'sc_chat_with_attachments_selected' ); 73 } 74 } ); 75 } 76 77 // Remove attachments on click on the clear link 78 if ( $attachments_list.length ) { 79 $attachments_list.on( 'click', '.sc_chat_form_field_upload_attachments_list_clear', function(e) { 80 e.preventDefault(); 81 $attachments_field.val( '' ).trigger( 'change' ); 82 return false; 83 } ); 84 } 85 86 // Open/Close chat in the popup 87 if ( $sc.hasClass( 'sc_chat_popup' ) ) { 88 var $popup_content = $sc.find( '.sc_chat_content' ), 89 $popup_button = $sc.find( '.sc_chat_popup_button' ); 90 $popup_button.on( 'click', function(e) { 91 e.preventDefault(); 92 $sc.addClass( 'sc_chat_opening' ); 93 $popup_content.slideToggle( function() { 94 $sc.removeClass( 'sc_chat_opening' ); 95 $sc.toggleClass( 'sc_chat_opened' ); 96 if ( $sc.hasClass( 'sc_chat_opened' ) ) { 97 if ( $popup_button.data( 'chat-opened-svg' ) ) { 98 $popup_button.find( '.sc_chat_popup_button_svg' ).html( $popup_button.data( 'chat-opened-svg' ) ); 99 } else if ( $popup_button.data( 'chat-opened-image' ) ) { 100 $popup_button.find( '.sc_chat_popup_button_image' ).attr( 'src', $popup_button.data( 'chat-opened-image' ) ); 101 } else if ( $popup_button.data( 'chat-opened-icon' ) ) { 102 $popup_button.find( '.sc_chat_popup_button_icon' ).removeClass( $popup_button.data( 'chat-icon' ) ).addClass( $popup_button.data( 'chat-opened-icon' ) ); 103 } 104 $document.trigger( 'action.sc_chat_popup_opened', [ $popup_content ] ); 105 } else { 106 if ( $popup_button.data( 'chat-svg' ) ) { 107 $popup_button.find( '.sc_chat_popup_button_svg' ).html( $popup_button.data( 'chat-svg' ) ); 108 } else if ( $popup_button.data( 'chat-image' ) ) { 109 $popup_button.find( '.sc_chat_popup_button_image' ).attr( 'src', $popup_button.data( 'chat-image' ) ); 110 } else if ( $popup_button.data( 'chat-icon' ) ) { 111 $popup_button.find( '.sc_chat_popup_button_icon' ).removeClass( $popup_button.data( 'chat-opened-icon' ) ).addClass( $popup_button.data( 'chat-icon' ) ); 112 } 113 $document.trigger( 'action.sc_chat_popup_closed', [ $popup_content ] ); 114 } 115 } ); 116 return false;
117 } ); 118 // Open on load 119 if ( $sc.hasClass( 'sc_chat_open_on_load' ) ) { 120 $popup_button.trigger( 'click' ); 121 } 122 } 123 124 // Enable/disable button "Generate" 125 $prompt.on( 'change keyup', function(e) { 126 $button.toggleClass( 'sc_chat_form_field_prompt_button_disabled', $prompt.val() == '' ); 127 } ) 128 .trigger( 'change' ); 129 130 // Set padding for the prompt field to avoid overlapping the button 131 if ( $button.css( 'position' ) == 'absolute' ) { 132 var set_prompt_padding = function() { 133 var button_size = Math.ceil( $button.outerWidth() ) + 10; 134 if ( button_size > 0 ) { 135 $prompt.css( 'padding-right', button_size + 'px' ); 136 } 137 }; 138 set_prompt_padding(); 139 $window.on( 'resize', set_prompt_padding ); 140 $document.on( 'action.sc_chat_popup_opened', set_prompt_padding ); 141 } 142 143 // Close a message popup on click on the close button 144 $sc.on( 'click', '.sc_chat_message_close', function(e) { 145 e.preventDefault(); 146 $form.find( '.sc_chat_message' ).slideUp(); 147 return false; 148 } ); 149 150 // Set a new list of messages 151 $sc.on( 'trx_addons_action_sc_chat_update', function(e, messages) { 152 if ( ! messages || ! messages.length ) { 153 return; 154 } 155 // Clear previous chat 156 chat = []; 157 chat_position = 0; 158 $result.empty(); 159 $form.data( 'chat-thread-id', '' ); 160 $form.data( 'chat-response-id', '' ); 161 // Hide the link "Start new chat" 162 $start_new.removeClass( 'trx_addons_hidden' ); 163 // Hide the message popup 164 $form.find( '.sc_chat_message' ).slideUp(); 165 // Add new messages to the chat 166 for ( var i = 0; i < messages.length; i++ ) { 167 add_to_chat( messages[ i ].role, messages[ i ].content, messages[ i ].attachments ); 168 } 169 // Clear the prompt field and set focus to it 170 $prompt.val( '' ).trigger( 'change' ); 171 $prompt.get(0).focus(); 172 } ); 173 174 // Start a new chat 175 $start_new.on( 'click', function(e) { 176 e.preventDefault(); 177 // Clear previous chat 178 chat = []; 179 chat_position = 0; 180 $result.empty(); 181 $form.data( 'chat-thread-id', '' ); 182 $form.data( 'chat-response-id', '' ); 183 trx_addons_do_action( 'trx_addons_action_sc_chat_updated', chat, $sc ); 184 // Hide the link "Start new chat" 185 $start_new.addClass( 'trx_addons_hidden' ); 186 // Hide the message popup 187 $form.find( '.sc_chat_message' ).slideUp(); 188 // Clear the attachments field and list 189 $attachments_field.val( '' ).trigger( 'change' ); 190 // Clear the prompt field and set focus to it 191 $prompt.val( '' ).trigger( 'change' ); 192 $prompt.get(0).focus(); 193 return false; 194 } ); 195 196 // Change the prompt text on click on the tag 197 $sc.on( 'click', '.sc_chat_form_field_tags_item', function(e) { 198 e.preventDefault(); 199 var $self = jQuery( this ); 200 if ( ! $prompt.attr( 'disabled' ) ) {
201 $prompt.val( $self.data( 'tag-prompt' ) ).trigger( 'change' ).get(0).focus(); 202 } 203 return false; 204 } ); 205 206 // Show previous/next message 207 $prompt.on( 'keydown', function(e) { 208 var i; 209 if ( e.keyCode == 38 ) { 210 e.preventDefault(); 211 if ( chat_position > 0 ) { 212 for ( i = chat_position - 1; i >= 0; i-- ) { 213 if ( chat[i].role == 'user' ) { 214 $prompt.val( chat[i].content ).trigger( 'change' ); 215 chat_position = i; 216 break; 217 } 218 } 219 } 220 } else if ( e.keyCode == 40 ) { 221 e.preventDefault(); 222 if ( chat_position < chat.length - 1 ) { 223 for ( i = chat_position + 1; i <= chat.length; i++ ) { 224 if ( i == chat.length ) { 225 $prompt.val( '' ).trigger( 'change' ); 226 chat_position = i; 227 break; 228 } else if ( chat[i].role == 'user' ) { 229 $prompt.val( chat[i].content ).trigger( 'change' ); 230 chat_position = i; 231 break; 232 } 233 } 234 } 235 } 236 } ); 237 238 // Generate answer 239 $prompt.on( 'keypress', function(e) { 240 if ( e.keyCode == 13 ) { 241 e.preventDefault(); 242 $button.trigger( 'click' ); 243 } 244 } ); 245 246 $button.on( 'click', function(e) { 247 e.preventDefault(); 248 var prompt = $prompt.val(), 249 settings = $form.data( 'chat-settings' ); 250 251 if ( ! prompt || ! checkLimits() ) { 252 return; 253 } 254 255 // Add prompt to the chat 256 add_to_chat( 'user', prompt, $attachments_field.length ? $attachments_field.get(0).files : [] ); 257 258 // Display loading animation 259 show_loading(); 260 261 // Display the link "Start new chat" 262 $start_new.removeClass( 'trx_addons_hidden' ); 263 264 // Send request via AJAX 265 var attachments_present = $attachments_field.length && $attachments_field.get(0).files.length > 0, 266 args = { 267 nonce: TRX_ADDONS_STORAGE['ajax_nonce'], 268 action: 'trx_addons_ai_helper_chat', 269 count: ( trx_addons_get_cookie( 'trx_addons_ai_helper_chat_count' ) || 0 ) * 1 + 1, 270 chat: attachments_present ? chat : JSON.stringify( chat ), 271 settings: settings, 272 thread_id: $form.data( 'chat-thread-id' ) || '', 273 response_id: $form.data( 'chat-response-id' ) || '' 274 }; 275 if ( attachments_present ) { 276 args = prepare_chat_args_for_ajax( args ); 277 jQuery.ajax( { 278 url: TRX_ADDONS_STORAGE['ajax_url'], 279 type: "POST", 280 data: args, 281 processData: false, // Don't process fields to the string 282 contentType: false, // Prevent content type header 283 success: show_answer 284 } ); 285 } else { 286 jQuery.post( TRX_ADDONS_STORAGE['ajax_url'], args, show_answer ); 287 } 288 } ); 289 290 // Fetch answer 291 function fetch_answer( data ) { 292 jQuery.post( TRX_ADDONS_STORAGE['ajax_url'], { 293 nonce: TRX_ADDONS_STORAGE['ajax_nonce'], 294 action: 'trx_addons_ai_helper_chat_fetch', 295 thread_id: data.thread_id || '', 296 run_id: data.run_id || '', 297 response_id: data.response_id || '', 298 settings: $form.data( 'chat-settings' ) 299 }, show_answer ); 300 } 301 302 // Show answer 303 function show_answer( response ) { 304 // Prepare response 305 var rez = trx_addons_parse_ajax_response( response, TRX_ADDONS_STORAGE['msg_ai_helper_error'] ); 306 307 // Save thread ID 308 if ( rez.thread_id ) { 309 $form.data( 'chat-thread-id', rez.thread_id ); 310 } 311 // Save response ID 312 if ( rez.response_id ) { 313 $form.data( 'chat-response-id', rez.response_id ); 314 } 315 // If queued - fetch answer again 316 if ( rez.finish_reason == 'queued' ) { 317 var time = rez.fetch_time ? rez.fetch_time : 4000; 318 setTimeout( function() { 319 fetch_answer( rez ); 320 }, time ); 321 } else { 322 // Hide loading animation 323 hide_loading(); 324 // Set focus to the prompt field 325 $prompt.get(0).focus(); 326 // Show result 327 if ( ! rez.error && rez.data.text ) { 328 add_to_chat( 'assistant', rez.data.text, rez.data.attachments ); 329 $prompt.val( '' ).trigger( 'change' ); 330 if ( $attachments_field.length ) { 331 $attachments_field.val( '' ).trigger( 'change' ); 332 } 333 updateLimitsCounter(); 334 updateRequestsCounter(); 335 trx_addons_do_action( 'trx_addons_action_sc_chat_updated', chat, $sc ); 336 } 337 if ( rez.error ) { 338 showMessage( rez.error, 'error' ); 339 } else if ( rez.data.message ) { 340 showMessage( rez.data.message, 'info' ); 341 } 342 } 343 } 344 345 // Prepare arguments for the AJAX request. If last message in the param 'chat' contain attachments - send all arguments as FormData 346 function prepare_chat_args_for_ajax( args, parent_key, data ) { 347 if ( parent_key === undefined ) { 348 parent_key = ''; 349 if ( typeof args.chat == 'object' && args.chat.length > 1 ) { 350 // Remove all attachments from old messages (leave attachments in the last user message only) 351 for ( var i = args.chat.length - 2; i >= 0; i-- ) { 352 if ( args.chat[i].attachments ) { 353 delete args.chat[i].attachments; 354 } 355 } 356 } 357 } 358 if ( data === undefined ) { 359 data = new FormData(); 360 } 361 var new_key = ''; 362 for ( var key in args ) {
363 new_key = parent_key ? parent_key + '[' + key + ']' : key; 364 if ( key == 'attachments' && typeof args[ key ] == 'object' && args[ key ].length > 0 ) { 365 for ( var i = 0; i < args[ key ].length; i++ ) { 366 if ( typeof args[ key ][ i ] == 'object' ) { 367 data.append( new_key + '[]', args[ key ][ i ].name ); 368 data.append( new_key + '[files][]', args[ key ][ i ], args[ key ][ i ].name ); 369 } else { 370 data.append( new_key + '[]', args[ key ][ i ] ); 371 } 372 } 373 } else if ( typeof args[ key ] == 'object' ) { 374 data = prepare_chat_args_for_ajax( args[ key ], new_key, data ); 375 } else { 376 data.append( new_key, args[ key ] ); 377 } 378 } 379 return data; 380 } 381 382 // Return a layout of the chat list item 383 function get_chat_list_item( role, message, attachments ) { 384 var dt = new Date(), 385 hours = dt.getHours(), 386 minutes = dt.getMinutes() < 10 ? '0' + dt.getMinutes() : dt.getMinutes(), 387 am = hours < 12 ? 'AM' : 'PM', 388 use_am = trx_addons_apply_filters( 'trx_addons_filter_sc_chat_time_use_am', true ), 389 hours = use_am && hours > 12 ? hours - 12 : hours; 390 var style = $form.data( 'chat-style' ); 391 var id = 'sc_chat_list_item_' + trx_addons_get_unique_id(), 392 name = [ 'assistant', 'loading' ].indexOf( role ) >= 0 && style.assistant_name 393 ? style.assistant_name 394 : ( role == 'user' && style.user_name 395 ? style.user_name 396 : '' 397 ), 398 icon = [ 'assistant', 'loading' ].indexOf( role ) >= 0 && style.assistant_icon 399 ? style.assistant_icon 400 : ( role == 'user' && style.user_icon 401 ? style.user_icon 402 : '' 403 ), 404 image = [ 'assistant', 'loading' ].indexOf( role ) >= 0 && style.assistant_image 405 ? style.assistant_image 406 : ( role == 'user' && style.user_image 407 ? style.user_image 408 : '' 409 ), 410 image_type = image ? trx_addons_get_file_ext( image ) : '', 411 image_svg = image_type == 'svg' 412 ? ( [ 'assistant', 'loading' ].indexOf( role ) >= 0 && style.assistant_svg 413 ? style.assistant_svg 414 : ( role == 'user' && style.user_svg 415 ? style.user_svg 416 : trx_addons_get_inline_svg( image, { 417 render: function( html ) { 418 if ( html ) { 419 jQuery( '#' + id + ' .sc_chat_list_item_svg' ).html( html ); 420 } 421 } 422 } ) 423 ) 424 ) 425 : ''; 426 var attachments_list = ''; 427 if ( attachments && attachments.length > 0 ) { 428 attachments_list = '<span class="sc_chat_list_item_attachments_list">'; 429 for ( var i = 0; i < attachments.length; i++ ) { 430 var attachments_name = typeof attachments[i] == 'object' ? attachments[i].name : attachments[i]; 431 attachments_list += '<span class="sc_chat_list_item_attachments_list_item" title="' + attachments_name + '">' 432 + '<span class="sc_chat_list_item_attachments_list_item_icon" style="background-image: url(' + TRX_ADDONS_STORAGE['plugin_url'] + '/css/file-types/' + attachment_type( attachments_name ) + '.png)"></span>' 433 + '<span class="sc_chat_list_item_attachments_list_item_name">' + attachments_name + '</span>' 434 + '</span>'; 435 } 436 attachments_list += '</span>'; 437 } 438 return trx_addons_apply_filters( 439 'trx_addons_filter_sc_chat_list_item', 440 '<li id="' + id + '" class="sc_chat_list_item sc_chat_list_item_' + role 441 + ( role == 'loading' ? ' sc_chat_list_item_assistant' : '' ) 442 + ( image || icon ? ' sc_chat_list_item_with_avatar' : ' sc_chat_list_item_without_avatar' ) 443 + ( attachments_list ? ' sc_chat_list_item_with_attachments' : '' ) 444 + '">' 445 + ( image || icon 446 ? '<span class="sc_chat_list_item_avatar">' 447 + ( image 448 ? ( image_type == 'svg' 449 ? '<span class="sc_chat_list_item_svg">' + image_svg + '</span>'
450 : '<img src="' + image + '" alt="' + name + '" class="sc_chat_list_item_image">' 451 ) 452 : '<span class="sc_chat_list_item_icon ' + icon + '"></span>' 453 ) 454 + '</span>' 455 : '' 456 ) 457 + '<span class="sc_chat_list_item_wrap">' 458 + '<span class="sc_chat_list_item_content">' + message + '</span>' 459 + ( role != 'loading' ? '<span class="sc_chat_list_item_time">' + hours + ':' + minutes + ( use_am ? ' ' + am : '' ) + '</span>' : '' ) 460 + attachments_list 461 + '</span>' 462 + '</li>', 463 role, message, attachments 464 ); 465 } 466 467 // Add a new message to the chat and display it 468 function add_to_chat( role, message, attachments ) { 469 // Add to the list of messages 470 if ( chat.length === 0 || chat[chat.length-1].role != role || chat[chat.length-1].content != message ) { 471 var msg_obj = { 472 'role': role, 473 'content': message 474 }; 475 if ( attachments && attachments.length > 0 ) { 476 msg_obj.attachments = attachments; 477 } 478 chat.push( msg_obj ); 479 chat_position = chat.length; 480 // Display message 481 $result.append( get_chat_list_item( role, message, attachments ) ); 482 // Display chat if it's hidden 483 if ( chat.length == 1 ) { 484 $result.parent().slideDown( function() { 485 scroll_to_bottom(); 486 }); 487 } else { 488 scroll_to_bottom(); 489 } 490 } 491 } 492 493 // Show loading animation 494 function show_loading() { 495 $form.addClass( 'sc_chat_form_loading' ); 496 // Add loading animation to the chat 497 $result.append( get_chat_list_item( 'loading', '<span class="sc_chat_list_item_loading_dot"></span><span class="sc_chat_list_item_loading_dot"></span><span class="sc_chat_list_item_loading_dot"></span>' ) ); 498 // Scroll chat to the bottom 499 scroll_to_bottom(); 500 } 501 502 // Hide loading animation 503 function hide_loading() { 504 $form.removeClass( 'sc_chat_form_loading' ); 505 $result.find( '.sc_chat_list_item_loading' ).remove(); 506 } 507 508 // Scroll the chat to the bottom 509 function scroll_to_bottom() { 510 $result.parent().animate( { scrollTop: $result.parent().prop( 'scrollHeight' ) }, 500 ); 511 } 512 513 // Show message 514 function showMessage( msg, type ) { 515 if ( msg.indexOf( '<p>' ) == -1 ) { 516 msg = '<p>' + msg + '</p>'; 517 } 518 $form 519 .find( '.sc_chat_message_inner' ) 520 .html( msg ) 521 .parent() 522 .toggleClass( 'sc_chat_message_type_error', type == 'error' ) 523 .toggleClass( 'sc_chat_message_type_info', type == 'info' ) 524 .toggleClass( 'sc_chat_message_type_success', type == 'success' ) 525 .addClass( 'sc_chat_message_show' ) 526 .slideDown(); 527 } 528 529 // Check limits for generation images 530 function checkLimits() { 531 // Block the button if the limits are exceeded 532 var total, used; 533 // Check limits for the generation requests from all users 534 var $limit_total = $form.find( '.sc_chat_limits_total_value' ), 535 $limit_used = $form.find( '.sc_chat_limits_used_value' ); 536 if ( $limit_total.length && $limit_used.length ) { 537 total = parseInt( $limit_total.text(), 10 ); 538 used = parseInt( $limit_used.text(), 10 ); 539 if ( ! isNaN( total ) && ! isNaN( used ) ) { 540 if ( used >= total ) { 541 $button.toggleClass( 'sc_chat_form_field_prompt_button_disabled', true ); 542 $prompt.attr( 'disabled', 'disabled' ); 543 showMessage( $form.data( 'chat-limit-exceed' ), 'error' ); 544 return false; 545 } 546 } 547 } 548 // Check limits for the generation requests from the current user 549 var $requests_total = $form.find( '.sc_chat_limits_total_requests' ), 550 $requests_used = $form.find( '.sc_chat_limits_used_requests' ); 551 if ( $requests_total.length && $requests_used.length ) { 552 total = parseInt( $requests_total.text(), 10 ); 553 //used = parseInt( $requests_used.text(), 10 ); 554 used = ( trx_addons_get_cookie( 'trx_addons_ai_helper_chat_count' ) || 0 ) * 1; 555 if ( ! isNaN( total ) && ! isNaN( used ) ) { 556 if ( used >= total ) { 557 $button.toggleClass( 'sc_chat_form_field_prompt_button_disabled', true ); 558 $prompt.attr( 'disabled', 'disabled' ); 559 showMessage( $form.data( 'chat-limit-exceed' ), 'error' ); 560 return false;
561 } 562 } 563 } 564 return true; 565 } 566 567 // Update a counter of requests inside a limits text 568 function updateLimitsCounter() { 569 var total, used; 570 // Update a counter of the total requests 571 var $limit_total = $form.find( '.sc_chat_limits_total_value' ), 572 $limit_used = $form.find( '.sc_chat_limits_used_value' ); 573 if ( $limit_total.length && $limit_used.length ) { 574 total = parseInt( $limit_total.text(), 10 ); 575 used = parseInt( $limit_used.text(), 10 ); 576 if ( ! isNaN( total ) && ! isNaN( used ) ) { 577 if ( used < total ) { 578 used = Math.min( used + 1, total ); 579 $limit_used.text( used ); 580 } 581 } 582 } 583 // Update a counter of the user requests 584 var $requests_total = $form.find( '.sc_chat_limits_total_requests' ), 585 $requests_used = $form.find( '.sc_chat_limits_used_requests' ); 586 if ( $requests_total.length && $requests_used.length ) { 587 total = parseInt( $requests_total.text(), 10 ); 588 // used = parseInt( $requests_used.text(), 10 ); 589 used = ( trx_addons_get_cookie( 'trx_addons_ai_helper_chat_count' ) || 0 ) * 1; 590 if ( ! isNaN( total ) && ! isNaN( used ) ) { 591 if ( used < total ) { 592 used = Math.min( used + 1, total ); 593 $requests_used.text( used ); 594 } 595 } 596 } 597 } 598 599 // Update a counter of the generation requests 600 function updateRequestsCounter() { 601 // Save a number of requests to the client storage 602 var count = trx_addons_get_cookie( 'trx_addons_ai_helper_chat_count' ) || 0, 603 limit = 60 * 60 * 1000 * 1, // 1 hour 604 expired = limit - ( new Date().getTime() % limit ); 605 606 trx_addons_set_cookie( 'trx_addons_ai_helper_chat_count', ++count, expired ); 607 } 608 609 // Load/Save a chat messages to the client storage 610 if ( $form.data( 'chat-save-history' ) ) { 611 612 // Load a chat messages from the client storage 613 var loadChatStorage = function() { 614 // Load saved chats from the client storage 615 var storage = trx_addons_get_storage( 'trx_addons_sc_chats' ); 616 if ( storage && storage.charAt(0) == '{' ) { 617 storage = JSON.parse( storage ); 618 } else { 619 storage = {}; 620 } 621 // Remove old chats (more than 1 day) 622 for ( var id in storage ) { 623 if ( storage[id].date < ( new Date().getTime() - 24 * 60 * 60 * 1000 ) ) { 624 delete storage[id]; 625 } 626 } 627 return storage; 628 }; 629 630 // Save a chat messages to the client storage 631 var saveChatStorage = function( storage ) { 632 trx_addons_set_storage( 'trx_addons_sc_chats', JSON.stringify( storage ) ); 633 }; 634 635 // Update a chat messages in the client storage 636 var updateChatStorage = function( chat, $sc ) { 637 if ( ! trx_addons_is_local_storage_exists() ) { 638 return; 639 } 640 // Get saved chats from the client storage 641 var storage = loadChatStorage(); 642 // Add new chat to the storage 643 if ( sc_id ) { 644 if ( ! chat || chat.length == 0 ) { 645 delete storage[sc_id]; 646 } else { 647 storage[sc_id] = { 648 date: new Date().getTime(), 649 chat: chat, 650 thread_id: $form.data( 'chat-thread-id' ) || '', 651 response_id: $form.data( 'chat-response-id' ) || '' 652 }; 653 } 654 } 655 // Save storage to the client storage 656 saveChatStorage( storage ); 657 }; 658 659 // Restore a chat messages from the client storage on page load 660 var storage = loadChatStorage(); 661 if ( sc_id && storage[ sc_id ] ) { 662 $sc.trigger( 'trx_addons_action_sc_chat_update', [ storage[ sc_id ].chat ] ); 663 // Thread ID should be restored after the chat messages are updated because the thread ID is set to empty value while this action 664 $form.data( 'chat-thread-id', storage[sc_id].thread_id ); 665 // Response ID should be restored after the chat messages are updated because the response ID is set to empty value while this action 666 $form.data( 'chat-response-id', storage[sc_id].response_id ); 667 } 668 669 // Save a chat messages to the client storage after the chat messages are updated 670 $sc.on( 'trx_addons_action_sc_chat_update', function( e, messages ) { 671 updateChatStorage( messages ); 672 } ); 673 trx_addons_add_action( 'trx_addons_action_sc_chat_updated', updateChatStorage ); 674 } 675 676 } ); 677 678 } ); 679 680} );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.