document.addEventListener('DOMContentLoaded', () => { // --- Configuration --- const API_URL = xtvlinkChat.rest_url; const NONCE = xtvlinkChat.nonce; const CHAT_DELAY = xtvlinkChat.chat_delay; // --- Element Refs --- const chatContainer = document.getElementById('xtvlink-chat-container'); const chatToggle = document.getElementById('xtvlink-chat-toggle'); const chatClose = document.getElementById('xtvlink-chat-close'); const chatMessages = document.getElementById('xtvlink-chat-messages'); const chatInput = document.getElementById('xtvlink-chat-input'); const chatSend = document.getElementById('xtvlink-chat-send'); // --- Initial State --- setTimeout(() => { if (chatToggle) { chatToggle.classList.remove('xtvlink-chat-hidden'); chatToggle.style.opacity = '1'; } }, CHAT_DELAY); // --- Event Listeners --- if (chatToggle) { chatToggle.addEventListener('click', () => { chatContainer.classList.remove('xtvlink-chat-hidden'); chatContainer.style.opacity = '1'; chatToggle.classList.add('xtvlink-chat-hidden'); chatToggle.style.opacity = '0'; }); } if (chatClose) { chatClose.addEventListener('click', () => { chatContainer.classList.add('xtvlink-chat-hidden'); chatContainer.style.opacity = '0'; chatToggle.classList.remove('xtvlink-chat-hidden'); chatToggle.style.opacity = '1'; }); } if (chatSend) { chatSend.addEventListener('click', sendMessage); } if (chatInput) { chatInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); sendMessage(); } }); } // --- Core Functions --- function sendMessage() { const question = chatInput.value.trim(); if (question === '') { return; } addMessage(question, 'user'); chatInput.value = ''; addMessage('...', 'ai', 'xtvlink-chat-thinking'); fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': NONCE }, body: JSON.stringify({ question: question }) }) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(data => { removeThinkingMessage(); addMessage(data.answer, 'ai'); }) .catch(error => { removeThinkingMessage(); addMessage('I apologize, but I\'m having trouble connecting at the moment. Please try again in a bit.', 'ai'); console.error('Chat Error:', error); }); } function removeThinkingMessage() { const thinkingMsg = document.getElementById('xtvlink-chat-thinking'); if (thinkingMsg) { thinkingMsg.remove(); } } /** * --- NEW PARSER (v4) --- * Safely parses AI text for display. * This version REMOVES the bare-link parser to prevent conflicts. */ function addMessage(text, sender, id = null) { const messageDiv = document.createElement('div'); messageDiv.classList.add('xtvlink-chat-message', `xtvlink-chat-${sender}`); if (id) { messageDiv.id = id; } const textSpan = document.createElement('span'); // 1. Sanitize the entire input by setting textContent. // This converts any HTML tags (like ) into safe, visible text // (e.g., <a href...>). This is the correct base. textSpan.textContent = text; // 2. Get the escaped HTML back. let safeHtml = textSpan.innerHTML; // 3. Convert Markdown links: [text](url) // This regex looks for [text](http... or www...) // We MUST run this on the escaped HTML. const mdLinkRegex = /\[(.*?)\]\(((https?:\/\/|www\.)[^\s\)]+)\)/g; safeHtml = safeHtml.replace(mdLinkRegex, (match, linkText, fullUrl, protocolOrWww) => { // Un-escape ampersands in the URL that textContent might have escaped let cleanUrl = fullUrl.replace(/&/g, '&'); const properUrl = cleanUrl.startsWith('www.') ? 'https://' + cleanUrl : cleanUrl; // linkText is already safely escaped by the textContent step. return `${linkText}`; }); // 4. --- BARE LINK PARSER (which caused the bug) IS NOW GONE --- // This simplifies the logic and removes the bug source. // If the AI fails to follow the prompt and sends a bare link, // it will just render as unclickable text. This is safe. // 5. Convert Bold: **text** safeHtml = safeHtml.replace(/\*\*(.*?)\*\*/g, '$1'); // 6. Convert Lists safeHtml = parseAdvancedLists(safeHtml); // 7. Set the final, parsed, and safe HTML textSpan.innerHTML = safeHtml; messageDiv.appendChild(textSpan); chatMessages.appendChild(messageDiv); chatMessages.scrollTop = chatMessages.scrollHeight; } /** * Fixes broken AI list formatting (e.g., orphaned bullets). */ function parseAdvancedLists(text) { // Split by newlines (which textContent converted to
) const lines = text.split(//); const processedLines = []; // Regex for bullet chars (including escaped •) const bulletChars = '[\\-\\*\\•\\🔴]|•'; const normalBulletRegex = new RegExp(`^(\\s*(${bulletChars})\\s+)(.*)`); for (let line of lines) { const trimmedLine = line.trim(); if (trimmedLine === '') { processedLines.push(''); continue; } const match = trimmedLine.match(normalBulletRegex); if (match) { // match[3] is the text *after* the bullet const textContent = match[3]; processedLines.push(' ' + textContent); } else { processedLines.push(line); } } // Join with
for HTML newlines return processedLines.join('
'); } }); buy iptv and use with vpn - XTVLink - 15K+ Premium Live Channels
×

A Little Thank You!

To help cover your transaction fees for crypto payments, we’ve automatically applied a €1.50 discount to your cart!

Happy shopping!