Dos and Don’ts of An IPTV Service Before Buying
Dos and don’ts of an IPTV service before buying IPTV is fast becoming the norm in entertainment. It is revolutionizing the wa …
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!