PageSourceSearch

https://sklep.polakuleczsiesam.pl/wp-content/plugins/ai24-assistan…/assets/js/AI24AI-script.js?ver=1776355343

js polakuleczsiesam.pl collected 2026-10-09 18:15:35 UTC 30,962 bytes, 705 lines download raw bytes

1function linkify(inputText) {
2    var markdownLinkPattern = /\[([^\]]+)]\((http[s]?:\/\/[^\s]+)\)/g;
3    var replacedText = inputText.replace(markdownLinkPattern, '<a href="$2" target="_blank">$1</a>');
4    var urlPattern = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])(?![^<]*>|[^<>]*<\/a>)/gim;
5    replacedText = replacedText.replace(urlPattern, '<a href="$1" target="_blank">$1</a>');
6    var pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))(?![^<]*>|[^<>]*<\/a>)/gim;
7    replacedText = replacedText.replace(pseudoUrlPattern, '$1<a href="http://$2" target="_blank">$2</a>');
8    var emailAddressPattern = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})(?![^<]*>|[^<>]*<\/a>)/gim;
9    replacedText = replacedText.replace(emailAddressPattern, '<a href="mailto:$1">$1</a>');
10    var phoneNumberPattern = /(\+1|1)?(?:\W*\d{3}\W*)\d{3}\W*\d{4}(?![^<]*>|[^<>]*<\/a>)/gim;
11    replacedText = replacedText.replace(phoneNumberPattern, '<a href="tel:$&">$&</a>');
12
13    return replacedText;
14}
15
16
17
18// Function to sanitize HTML input
19function sanitizeHtml(str) {
20    var temp = document.createElement('div');
21    temp.textContent = str;
22    return temp.innerHTML;
23}
24
25jQuery(document).ready(function($) {
26    var chatToggleSelector = '#AI24AI-chatbot-toggle';
27
28    var chatContainerSelector = '#AI24AI-chatbot-container';
29    var chatInputSelector = '#chat-input';
30    var chatMessagesSelector = '#chat-messages';
31    var chatMinimizeSelector = '#chatbox-minimize';
32    var chatCloseSelector = '#chatbox-close';
33    var confirmExitSelector = '#confirm-exit';
34    var cancelExitSelector = '#cancel-exit';
35    var isTransitioning = false;
36    var isRequestPending = false;
37    var typingAnimation;
38    var typingIndicator;
39    var userOpenedChat = false;
40    var typingTimeouts = [];
41    var welcomeMessageInProgress = false
42    var displayMessagesInstantly = false;
43    var currentMessageIndex = 0;
44    var baseHeight = 50;
45
46    /*RadicalK */
47    // Ensure BroadcastChannel is initialized at the top of AI24AI-script.js
48    const channel = new BroadcastChannel('ai24_chat');
49
50    // Handle incoming messages, including CLOSE_UI, to reset the UI when needed
51    channel.onmessage = function(event) {
52        if (event.data.type === 'CLOSE_UI') {
53    
54            // Clear chat container and reset toggle
55            $(chatContainerSelector).removeClass('active').hide();
56            $(chatToggleSelector).removeClass('hide-toggle').addClass('toggle-visible').css({
57                'opacity': '1',
58                'pointer-events': 'auto'
59            });
60    
61            // Fully clear local storage and session storage
62            localStorage.removeItem('AI24AI_chatState');
63            sessionStorage.removeItem('initialGreetingDisplayed');
64            localStorage.removeItem('AI24AI_chatThreadId');
65        }
66    };
67    /*RadicalK here ends */
68
69    // Function to fix input bar sizing
70    $('#chat-input').on('input', function() {
71        var baseHeight = 50; // This should be the height of the input field for one line of text, including padding.
72        this.style.height = baseHeight + 'px'; // Reset to base height before calculating scrollHeight
73        var newHeight = this.scrollHeight > baseHeight ? this.scrollHeight : baseHeight;
74        this.style.height = newHeight + 'px'; // Set the new height
75    }).on('keydown', function(e) {
76        if (e.keyCode === 13 && e.shiftKey) { // Shift+Enter is pressed
77            e.preventDefault();
78            var start = this.selectionStart;
79            var end = this.selectionEnd;
80
81            // Insert the new line at the current cursor position
82            this.value = this.value.substring(0, start) + '\n' + this.value.substring(end);
83
84            // Move the cursor to the new position
85            this.selectionStart = this.selectionEnd = start + 1;
86
87            // Trigger the input event to resize the textarea
88            $(this).trigger('input');
89        }
90    }).on('keyup', function(e) {
91        if (e.keyCode == 13 && !e.shiftKey) { // Enter key is pressed without Shift or input is clear
92            this.style.height = baseHeight + 'px'; // Reset to initial height
93        }
94    }); 
95
96
97    /**Radical Koala */
98    function resetChatCompletely() {
99        // function for handling chat reset when the user prompts to close the chat - IMPORTANT for cross tab modification.
100        const vaImage = AI24AI_params?.image || '';
101        const vaName = AI24AI_params?.name || '';
102        const vaDescription = AI24AI_params?.description || '';
103        
104        // 1. Clear all storage
105        localStorage.removeItem('AI24AI_chatState');
106        localStorage.removeItem('AI24AI_chatThreadId');
107        localStorage.removeItem('initialGreetingDisplayed');
108        sessionStorage.removeItem('initialGreetingDisplayed');
109        sessionStorage.removeItem('AI24AI_chatThreadId');
110        
111        // 2. Broadcast reset to other tabs
112        channel.postMessage({
113            type: 'CHAT_RESET',
114            timestamp: new Date().toISOString()
115        });
116        
117        // 3. Reset all flags
118        welcomeMessageInProgress = false;
119        displayMessagesInstantly = false;
120        userOpenedChat = false;
121        currentMessageIndex = 0;
122        
123        // 4. Clear UI
124        clearInitialMessageTimeouts();
125        hideTypingIndicator();
126        
127        // 5. Reset chat container to initial state
128        $('#chat-messages').html(`
129            <div class="va-info-section">
130                <div class="va-top-info">
131                    ${vaImage ? `<img src="${vaImage}" alt="${vaName}" class="va-image">` : ''}
132                    <div class="va-name">${vaName}</div>
133                    ${vaDescription ? `<div class="va-description">${vaDescription}</div>` : ''}
134                    <div class="va-info-separator"></div>
135                </div>
136            </div>
137            <div class="messages-content"></div>
138        `);
139
140    }
141
142     /**Radical Koala */
143
144    
145    // Function to move the typing indicator to the bottom of the chat
146    function moveTypingIndicatorToBottom() {
147        if (typingIndicator) {
148            typingIndicator.detach(); // Detach the typing indicator from its current location
149            $(chatMessagesSelector).append(typingIndicator); // Re-append it to move it to the bottom
150        }
151    }
152
153    // Function to create the typing indicator element at the bottom
154    function createTypingIndicator() {
155        if (typingIndicator) {
156            typingIndicator.remove(); // Remove any existing indicator
157        }
158
159        // Create a container for the typing indicator without message styles
160        typingIndicator = $('<div class="typing-indicator">' +
161                            '<div id="typing-animation" class="typing-animation-style"></div>' +
162                            '</div>');
163
164        // Append it to the chat messages container
165        $(chatMessagesSelector).append(typingIndicator);
166
167        // Initialize the Lottie animation within this container
168        typingAnimation = lottie.loadAnimation({
169            container: document.getElementById('typing-animation'), // Directly target the new div
170            renderer: 'svg',
171            loop: true,
172            autoplay: false,
173            path: AI24AI_params.typingAnimationPath // Use the localized path
174        });
175
176        moveTypingIndicatorToBottom(); // Move the indicator to the bottom after creation
177    }
178
179    // Call createTypingIndicator on script load to ensure it's ready when needed
180    createTypingIndicator();
181
182    // Function to show the typing indicator
183    function showTypingIndicator() {
184        if (!typingIndicator) {
185            createTypingIndicator();
186        }
187        typingIndicator.show();
188        typingAnimation.play();
189        moveTypingIndicatorToBottom(); // Ensure it is at the bottom every time it is shown
190        // Trigger a scroll after a slight delay to account for the space the indicator will occupy
191        setTimeout(function() {
192            $(chatMessagesSelector).scrollTop($(chatMessagesSelector)[0].scrollHeight);
193        }, 100); // The delay can be adjusted if necessary
194    }
195
196    // Function to hide the typing indicator
197    function hideTypingIndicator() {
198        if (typingIndicator) {
199            typingIndicator.hide();
200            typingAnimation.stop();
201        }
202    }
203
204    // Initially hide the typing indicator until needed
205    hideTypingIndicator();
206
207    //Initial Greeting
208    function displayInitialGreeting() {
209        /*RadicalK changed this ---> */
210
211        // Check localStorage instead of sessionStorage
212        if (localStorage.getItem('initialGreetingDisplayed')) {
213        return; 
214        }
215
216        localStorage.setItem('initialGreetingDisplayed', 'true');
217        welcomeMessageInProgress = true;
218        /*RadicalK changed this ---> */
219        
220            sessionStorage.setItem('initialGreetingDisplayed', 'true'); // Flag the session right away
221            welcomeMessageInProgress = true;
222            
223            // Function to display a single message with typing animation
224            function displayMessageWithTypingAnimation(messages, index = 0) {
225                if (displayMessagesInstantly) {
226                    displayAllRemainingMessages(messages);
227                    return; // Exit the function as we're done displaying messages.
228                }
229                if (index < messages.length) {
230                    showTypingIndicator();
231                    
232                    // Clear any previous timeouts for safety
233                    typingTimeouts.forEach(clearTimeout);
234                    
235                    // Create a new timeout for the typing indicator
236                    var timeoutId = setTimeout(function() {
237                        hideTypingIndicator();
238                        var message = messages[index];
239                        var currentTime = new Date();
240                        var messageTimestamp = currentTime.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
241                        
242                        // Append message to chat area
243                        $(chatMessagesSelector).append('<div class="chat-message assistant-message fade-in">' + 
244                            message + 
245                            '<span class="chat-timestamp-assistant">' + messageTimestamp + '</span>' + 
246                            '</div>');
247                        $(chatMessagesSelector).scrollTop($(chatMessagesSelector)[0].scrollHeight);
248                        
249                        currentMessageIndex = index + 1;
250                        displayMessageWithTypingAnimation(messages, index + 1);
251                    }, 1600); // Adjust this delay to manage the typing speed
252                    
253                    // Store the timeoutId to clear later if needed
254                    typingTimeouts.push(timeoutId);
255                } else {
256                    welcomeMessageInProgress = false; // Sequence complete, allow re-entry if session cleared
257                    // Clear all timeouts since the animation sequence is complete
258                    typingTimeouts.forEach(clearTimeout);
259                    typingTimeouts = []; // Clear the array for next use
260                }
261            }
262            
263            // Start the message display process after an initial delay
264            var initialDelayId = setTimeout(() => {
265                displayMessageWithTypingAnimation(AI24AI_params.contextMessages);
266            }, 750); // Adjust this initial delay as needed
267            
268        typingTimeouts.push(initialDelayId); // Store the initial delay timeoutId as well
269    }
270
271    // Function to hide the toggle button when the chatbox is active, specifically for the sidebar option
272    function handleSidebarToggle() {
273        var widgetPosition = 'sidebar'; // Replace this with the actual condition to determine if the sidebar option is active
274        var $chatToggle = $(chatToggleSelector);
275        var $chatContainer = $(chatContainerSelector);
276        
277        if (widgetPosition === 'sidebar' && $chatContainer.hasClass('active')) {
278            $chatToggle.addClass('hide-toggle'); // Hides the toggle button
279        } else {
280            $chatToggle.removeClass('hide-toggle'); // Shows the toggle button
281        }
282    }
283    
284    // Function to toggle chat
285    // function toggleChatWidget() {
286    //     if (isTransitioning) return;
287    //     isTransitioning = true;
288
289    //     var $chatContainer = $(chatContainerSelector);
290    //     var $chatToggle = $(chatToggleSelector);
291
292    //     // Check if we are on a mobile device
293    //     var isMobile = window.matchMedia("only screen and (max-width: 540px)").matches;
294
295    //     if ($chatContainer.hasClass('active')) {
296    //         // If chat is currently active, hide it
297    //         $chatContainer.removeClass('active').one('transitionend', function() {
298    //             $(this).hide();
299    //             isTransitioning = false; 
300    //             if (isMobile) {
301    //                 $chatToggle.addClass('toggle-visible').removeClass('hide-toggle'); // Make toggle reappear smoothly on mobile when chat is closed
302    //                 $chatToggle.css('pointer-events', 'auto'); // Enable pointer events
303    //             } else {
304    //                 // Handle the toggle for the sidebar when it's not a mobile device
305    //                 handleSidebarToggle(); // Call to hide or show the sidebar toggle
306    //             }
307    //             $chatToggle.prop('disabled', false);
308    //         });
309    //     } else {
310    //         // If chat is not active, show it
311    //         userOpenedChat = true;
312    //         $chatContainer.show(0, function() {
313    //             $(this).addClass('active');
314    //             isTransitioning = false;
315    //             if (isMobile) {
316    //                 $chatToggle.addClass('hide-toggle').removeClass('toggle-visible'); // Hide the toggle button when chat is opened on mobile
317    //                 $chatToggle.css('pointer-events', 'none'); // Disable pointer events
318    //             } else {
319    //                 // Handle the toggle for the sidebar when it's not a mobile device
320    //                 handleSidebarToggle(); // Call to hide or show the sidebar toggle
321    //             }
322    //             $chatToggle.prop('disabled', false);
323    //             if (!sessionStorage.getItem('initialGreetingDisplayed')) {
324    //                 displayInitialGreeting();
325    //             }
326
327    //             // Focus the input field immediately
328    //             // $(chatInputSelector).focus();
329    //         });
330    //     }
331    // }
332
333    /** WORKING FUNCTION IS BEING UPDATED::::::::::::::: */
334    function toggleChatWidget() {
335        if (isTransitioning) return;
336        isTransitioning = true;
337        //radical
338        // check chat state
339        if (localStorage.getItem('AI24AI_chatFullyClosed') === 'true') {
340            // close chat fully
341            resetChatCompletely();
342            localStorage.removeItem('AI24AI_chatFullyClosed');
343        }
344        //radical
345
346        var $chatContainer = $(chatContainerSelector);
347        var $chatToggle = $(chatToggleSelector);
348    
349        // Check if we are on a mobile device
350        var isMobile = window.matchMedia("only screen and (max-width: 540px)").matches;
351    
352        if ($chatContainer.hasClass('active')) {
353            // If chat is currently active, hide it
354            $chatContainer.removeClass('active').one('transitionend', function() {
355                $(this).hide();
356                isTransitioning = false; 
357                if (isMobile) {
358                    $chatToggle.addClass('toggle-visible').removeClass('hide-toggle');
359                    $chatToggle.css('pointer-events', 'auto');
360                } else {
361                    handleSidebarToggle();
362                }
363                $chatToggle.prop('disabled', false);
364            });
365        } else {
366            // If chat is not active, show it
367            userOpenedChat = true;
368            $chatContainer.show(0, function() {
369                $(this).addClass('active');
370                isTransitioning = false;
371                if (isMobile) {
372                    $chatToggle.addClass('hide-toggle').removeClass('toggle-visible');
373                    $chatToggle.css('pointer-events', 'none');
374                } else {
375                    handleSidebarToggle();
376                }
377                $chatToggle.prop('disabled', false);
378                if (!sessionStorage.getItem('initialGreetingDisplayed')) {
379                    displayInitialGreeting();
380                }
381            });
382        }
383    }
384    $(chatToggleSelector).on('click', toggleChatWidget);
385
386
387    function displayAllRemainingMessages(messages) {
388        if (currentMessageIndex < messages.length) {
389            messages.slice(currentMessageIndex).forEach(function(message) {
390                var currentTime = new Date();
391                var messageTimestamp = currentTime.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
392        
393                // Append the remaining messages to ensure they are in the correct position
394                $(chatMessagesSelector).append('<div class="chat-message assistant-message fade-in">' + 
395                    message + 
396                    '<span class="chat-timestamp-assistant">' + messageTimestamp + '</span>' + 
397                    '</div>');
398            });
399    
400            // After displaying all remaining messages, update the currentMessageIndex
401            currentMessageIndex = messages.length;
402    
403            // Reset the flag to indicate the welcome message sequence is complete
404            welcomeMessageInProgress = false;
405    
406            // Scroll to the newest message
407            $(chatMessagesSelector).scrollTop($(chatMessagesSelector)[0].scrollHeight);
408        }
409    }
410
411    // Minimize chat functionality
412    $(chatMinimizeSelector).on('click', function() {
413        if (isTransitioning) return;
414        isTransitioning = true;
415
416        // Toggle the chat container's visibility.
417        $(chatContainerSelector).toggleClass('active');
418
419        if (window.matchMedia("(max-width: 768px)").matches) {
420            // Mobile-specific behavior
421            if ($(chatContainerSelector).hasClass('active')) {
422                $(chatToggleSelector).addClass('hide-toggle').removeClass('toggle-visible');
423            } else {
424                $(chatToggleSelector).removeClass('hide-toggle').addClass('toggle-visible');
425                // Ensure it is clickable again
426                $(chatToggleSelector).css('pointer-events', 'auto');
427            }
428        } else {
429            // Non-mobile behavior
430            handleSidebarToggle(); // Update the toggle button visibility for sidebar
431        }
432
433        // Listen for the end of the transition to reset the isTransitioning flag.
434        $(chatContainerSelector).one('transitionend', function() {
435            isTransitioning = false;
436        });
437    });
438    
439    // Close button functionality
440    $(chatCloseSelector).on('click', function() {
441        $('#exit-confirmation-modal').addClass('active'); // Show the modal
442
443
444    });
445
446    // Cancel exit functionality
447    $(cancelExitSelector).on('click', function() {
448        $('#exit-confirmation-modal').removeClass('active'); // Hide the modal
449
450    });
451
452    function clearInitialMessageTimeouts() {
453        typingTimeouts.forEach(clearTimeout); // Clear timeouts for pending messages
454        typingTimeouts = [];
455        welcomeMessageInProgress = false;
456    }
457
458    // Function to confirm exit
459    $(confirmExitSelector).on('click', function() {
460    
461        // Send pause and close UI messages
462        channel.postMessage({
463            type: 'PAUSE_BROADCAST'
464        });
465        channel.postMessage({
466            type: 'CLOSE_UI'
467        });
468        
469        // Step 1: Broadcast close event across tabs
470        // localStorage.setItem('AI24AI_chatClosed', new Date().toISOString());
471
472        //     // Broadcast a definitive close event
473        //     const closeEvent = new CustomEvent('chatFullClose', {
474        //         detail: {
475        //             timestamp: new Date().toISOString(),
476        //             action: 'FORCE_CLOSE'
477        //         }
478        //     });
479        //     document.dispatchEvent(closeEvent);
480        localStorage.setItem('AI24AI_chatFullyClosed', 'true');  //add chat fully closed
481        // cklear chat state
482        localStorage.removeItem('AI24AI_chatState');
483        sessionStorage.removeItem('initialGreetingDisplayed'); // Allow initial greetings to re-trigger if chat is reopened
484        localStorage.removeItem('initialGreetingDisplayed'); // Allow initial greetings to re-trigger if chat is reopened
485        localStorage.removeItem('AI24AI_chatThreadId');
486
487
488
489    
490        // Try more thorough clearing - necessary to clear html within the chat
491        $('#chat-messages').html(`
492            <div class="va-info-section">
493                <div class="va-top-info">
494                    ${$('.va-info-section .va-top-info').html()}
495                </div>
496            </div>
497            <div class="messages-content"></div>
498        `);
499
500
501        if (isTransitioning) return;
502        isTransitioning = true;
503        clearThreadId();
504        // Clear timeouts and messages
505        clearInitialMessageTimeouts(); // Assuming this now also removes messages as per your update
506        // Additional steps to reset the chat state fully
507        hideTypingIndicator(); // Ensure typing indicator is hidden
508        
509        welcomeMessageInProgress = false; // Reset this flag
510        displayMessagesInstantly = false; // Reset this if you're using it to control message display logic
511        userOpenedChat = false; 
512
513        $('.messages-content').empty();
514        $('#chat-messages .chat-message').remove(); 
515       
516
517
518
519        // Handle UI transitions
520        $(chatContainerSelector).removeClass('active').one('transitionend', function() {
521            $(this).hide();
522            isTransitioning = false;
523
524            if (window.matchMedia("(max-width: 768px)").matches) {
525                setTimeout(function() {
526                    $(chatToggleSelector).removeClass('hide-toggle').addClass('toggle-visible').css({
527                        'opacity': '1',
528                        'pointer-events': 'auto'
529                    });
530                }, 20);
531            } else {
532                // Call handleSidebarToggle to slide the toggle back into view
533                handleSidebarToggle(); // Update the toggle button visibility for sidebar
534            }
535        });
536        $('#exit-confirmation-modal').removeClass('active');
537        $(chatToggleSelector).prop('disabled', false);
538    });
539
540   /*ATHARVA CHANGED THIS:: */
541
542    function storeThreadId(threadId) {
543        // Store in both session and local storage
544        sessionStorage.setItem('AI24AI_chatThreadId', threadId);
545        localStorage.setItem('AI24AI_chatThreadId', threadId);
546    }
547    
548    function retrieveThreadId() {
549        // Check session first, then local
550        return sessionStorage.getItem('AI24AI_chatThreadId') || 
551               localStorage.getItem('AI24AI_chatThreadId') || 
552               '';
553    }
554    
555    /*ATHARVA CHANGED THIS:: */
556
557function clearThreadId() {
558    sessionStorage.removeItem('AI24AI_chatThreadId');
559}
560
561// Event listener for the Enter key press in the chat input
562$(document).ready(function() {
563    function sendMessage(user_input, inputElement) {
564        if (user_input !== '') {
565            if (welcomeMessageInProgress) {
566                clearInitialMessageTimeouts();
567                displayAllRemainingMessages(AI24AI_params.contextMessages);
568            }
569
570            inputElement.style.height = '50px';
571            $(inputElement).val('');
572
573                var currentTime = new Date();
574                var userTimestamp = currentTime.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
575
576                // Sanitize and convert newlines to <br> for HTML rendering
577                var sanitizedInput = sanitizeHtml(user_input).replace(/\n/g, '<br>');
578                var formattedMessage = linkify(sanitizedInput);
579            
580                // Append user message and timestamp in the same div
581                var messageHtml = '<div class="chat-message user-message fade-in">' +
582                    formattedMessage +  // The actual message content
583                    '<span class="chat-timestamp-user">' + userTimestamp + '</span>' + // The timestamp
584                    '</div>';
585            
586                // Append the combined message and timestamp to the chat messages
587                $(chatMessagesSelector).append(messageHtml);
588                $(chatMessagesSelector).scrollTop($(chatMessagesSelector)[0].scrollHeight);
589                $(this).val('');
590            
591                isRequestPending = true;
592
593                // console.log("Nonce being sent in AJAX request:", AI24AI_params.nonce);
594                
595                var ajaxData = {
596                    action: 'AI24AI_chat',
597                    nonce: AI24AI_params.nonce,
598                    user_input: user_input,
599                    start_new_thread: !retrieveThreadId() 
600                };
601
602                if (retrieveThreadId()) {
603                    ajaxData.thread_id = retrieveThreadId(); 
604                }
605
606                $.ajax({
607                    url: AI24AI_params.ajax_url,
608                    type: 'POST',
609                    dataType: 'json',
610                    data: ajaxData,
611                    beforeSend: function() {
612                        showTypingIndicator();
613                    },
614                    success: function(response) {
615                        if (response.success) {
616                            var assistantMessages = response.data.messages.filter(function(message) {
617                                return message.role === 'assistant';
618                            });
619                            
620                            // Check if there are any assistant messages
621                            if (assistantMessages.length > 0) {
622                                var latestAssistantMessage = assistantMessages[0];
623                                var messageContentArray = latestAssistantMessage.content || latestAssistantMessage.clean_content;
624                                if (Array.isArray(messageContentArray) && messageContentArray.length > 0) {
625                                    var messageText = messageContentArray[0]['text'] && messageContentArray[0]['text']['value'] ? messageContentArray[0]['text']['value'] : 'Message content not available';
626                                } else {
627                                    console.log("No content available or content structure is incorrect", messageContentArray);
628                                }
629
630                                // Convert links in the message content
631                                messageText = linkify(messageText);
632
633                                var currentTime = new Date();
634                                var assistantTimestamp = currentTime.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
635
636                                // Ensure HTML content is rendered correctly
637                                var assistantMessageHtml = '<div class="chat-message assistant-message fade-in">' +
638                                    '<span class="message-content">' + messageText + '</span>' + 
639                                    '<span class="chat-timestamp-assistant">' + assistantTimestamp + '</span>' + 
640                                    '</div>';
641                                $(chatMessagesSelector).append(assistantMessageHtml);
642                                $(chatMessagesSelector).scrollTop($(chatMessagesSelector)[0].scrollHeight);
643                            }
644
645                            // Store the thread ID if it's provided in the response
646                            if (response.data.thread_id) {
647                                storeThreadId(response.data.thread_id);
648                            }
649                        } else {
650                           // Log removed: console.error('Error:', response.data.error);
651                        }
652                    },                    
653                    error: function(xhr, status, error) {
654                        // Log removed: console.error('AJAX error:', status, error);
655                    },
656                    complete: function() {
657                        hideTypingIndicator();
658                        isRequestPending = false;
659                        $(chatInputSelector).focus(); // Make sure to remove focus from the input field
660                    
661                        // Scroll adjustment to encourage the browser to reset zoom
662                        setTimeout(function() {
663                            if(window.scrollY != 0) {
664                                window.scrollTo({top: window.scrollY - 1});
665                                window.scrollTo({top: window.scrollY + 1});
666                            }
667                        }, 50);
668                        
669                        // Ensure focus is maintained after assistant message is appended
670                        setTimeout(function() {
671                            $(chatInputSelector).focus();
672                        }, 100);
673                    }                    
674                });
675            }
676        }
677
678        // Event listener for the Enter key press in the chat input
679        $(chatInputSelector).on('keypress', function(e) {
680            if (e.which == 13 && e.shiftKey) {
681                // Allow shift+enter to insert a newline
682                var cursorPos = this.selectionStart;
683                var value = $(this).val();
684                $(this).val(value.substring(0, cursorPos) + "\n" + value.substring(cursorPos));
685                this.selectionStart = this.selectionEnd = cursorPos + 1; // Move cursor to the next line
686                e.stopPropagation(); // Prevent the default action
687            } else if (e.which == 13 && !e.shiftKey && !isRequestPending) {
688                e.preventDefault(); // Prevent the default action (new line)
689                var user_input = $(this).val().trim();
690                sendMessage(user_input, this); // Use 'this' to reference the input element
691            }
692        });
693
694        $('#send-button').on('click touchstart', function(event) {
695            event.preventDefault(); // Prevent native handling of touch events that might cause issues.
696            if (!isRequestPending) {
697                var $inputElement = $(chatInputSelector);
698                var user_input = $inputElement.val().trim();
699                if (user_input !== '') {
700                    sendMessage(user_input, $inputElement[0]); // Use the existing sendMessage function
701                }
702            }
703        });
704    });
705}); 

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.