What Are Blockchain Confirmations
What is Blockchain? If this technology is so complex, why call it “blockchain?” At its most basic level, blockchain is literally ju …
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('
');
}
});
To help cover your transaction fees for crypto payments, we’ve automatically applied a €1.50 discount to your cart!
Happy shopping!