File: /home/salamatk/public_html/wp-content/plugins/woodmart-plus/assets/js/chat-bot.js
(function($) {
'use strict';
$(document).ready(function() {
const STORAGE_KEY = 'chatBotMessages';
const MAX_STORED_MESSAGES = 200;
const INPUT_DEFAULT_HEIGHT = 44;
const $toggle = $('#chatBotToggle');
const $container = $('#chatBotContainer');
const $messages = $('#chatBotMessages');
const $input = $('#chatBotInput');
const $sendBtn = $('#chatBotSend');
const $resetBtn = $('#chatBotReset');
let isOpen = false;
let storedMessages = loadMessagesFromStorage();
const initialMessageTemplate = $messages.find('.chat-initial-message').first().prop('outerHTML') || '';
restoreStoredMessages();
function toggleChat() {
isOpen = !isOpen;
$toggle.toggleClass('active', isOpen);
$container.toggleClass('active', isOpen);
if (isOpen) {
$input.focus();
const duration = storedMessages.length ? 1000 : 300;
scrollToBottom({ duration });
}
}
$toggle.on('click', function(e) {
e.preventDefault();
toggleChat();
});
function sendMessage(messageOverride) {
const message = typeof messageOverride === 'string' ? messageOverride.trim() : $input.val().trim();
if (!message) {
return;
}
$sendBtn.prop('disabled', true);
addMessage(message, 'user');
if (typeof messageOverride !== 'string') {
$input.val('');
resetInputHeight();
}
showTypingIndicator();
$.ajax({
dataType:'json',type:'post',url : chatBotAi.ajaxUrl,
data: {
action: 'ai_send_message',
message : message,
history : storedMessages,
nonce : chatBotAi.nonce
},
success:function(responses)
{
removeTypingIndicator();
addMessage(responses.message, 'bot');
$sendBtn.prop('disabled', false);
$input.focus();
},
error:function (error)
{
removeTypingIndicator();
addMessage(error.responseJSON.message, 'bot',{
save : false
});
$sendBtn.prop('disabled', false);
$input.focus();
}
});
}
$sendBtn.on('click', function(e) {
e.preventDefault();
sendMessage();
});
$input.on('keypress', function(e) {
if (e.which === 13 && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
$input.on('input', function() {
adjustInputHeight();
});
adjustInputHeight();
function addMessage(text, type, options = {}) {
const messageTime = options.time || getCurrentTime();
const messageClass = type === 'user' ? 'user-message' : 'bot-message';
const senderName = type === 'user' ? chatBotAi.chatbot_txt.user_title : chatBotAi.chatbot_txt.chatbot_name;
const enableActions = type === 'bot' && options.enableActions !== false;
const safeText = (type === 'bot') ? text : escapeHtml(text);
let actionsHTML = '';
if (enableActions) {
actionsHTML = `
<div class="message-actions">
<div class="message-action-btn" data-action="copy" title="${chatBotAi.chatbot_txt.title_copy_btn}">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5 15H4C2.89543 15 2 14.1046 2 13V4C2 2.89543 2.89543 2 4 2H13C14.1046 2 15 2.89543 15 4V5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<div class="message-action-btn" data-action="resend" title="${chatBotAi.chatbot_txt.title_resend_btn}">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 4V10H7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M23 20V14H17" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M20.49 9C19.81 4.81 16.05 1.5 11.64 1.5C6.87 1.5 2.64 5.36 2.05 10.01M3.51 15C4.19 19.19 7.95 22.5 12.36 22.5C17.13 22.5 21.36 18.64 21.95 13.99" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
`;
}
const messageHTML = `
<div class="message ${messageClass}">
<div class="message-content">
<div class="message-text">${safeText}</div>
</div>
<div class="message-footer">
${actionsHTML}
<div class="message-footer-left">
<div class="message-avatar">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 21V19C20 17.9391 19.5786 16.9217 18.8284 16.1716C18.0783 15.4214 17.0609 15 16 15H8C6.93913 15 5.92172 15.4214 5.17157 16.1716C4.42143 16.9217 4 17.9391 4 19V21" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="12" cy="7" r="4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<span class="message-sender">${senderName}</span>
<span class="message-time-header">${messageTime}</span>
</div>
</div>
</div>
`;
$messages.append(messageHTML);
if (options.scroll !== false) {
scrollToBottom();
}
if (options.save !== false) {
const meta = typeof options.meta === 'object' ? options.meta : { enableActions };
persistMessage({
text: text,
type: type,
time: messageTime,
meta: meta
});
}
}
function copyToClipboard(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
showToast(chatBotAi.chatbot_txt.when_copy);
} catch (err) {
console.error(chatBotAi.chatbot_txt.when_copy_error, err);
}
document.body.removeChild(textarea);
}
function showToast(message) {
const toast = $('<div class="toast-message">' + message + '</div>');
$('body').append(toast);
setTimeout(function() {
toast.addClass('show');
}, 10);
setTimeout(function() {
toast.removeClass('show');
setTimeout(function() {
toast.remove();
}, 300);
}, 2000);
}
function showTypingIndicator() {
const typingHTML = `
<div class="message bot-message typing-message">
<div class="message-content">
<div class="typing-indicator">
<span></span>
<span></span>
<span></span>
</div>
</div>
<div class="message-footer">
<div class="message-footer-left">
<div class="message-avatar">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 21V19C20 17.9391 19.5786 16.9217 18.8284 16.1716C18.0783 15.4214 17.0609 15 16 15H8C6.93913 15 5.92172 15.4214 5.17157 16.1716C4.42143 16.9217 4 17.9391 4 19V21" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="12" cy="7" r="4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<span class="message-sender">${chatBotAi.chatbot_txt.chatbot_name}</span>
<span class="message-time-header">${chatBotAi.chatbot_txt.when_type}</span>
</div>
</div>
</div>
`;
$messages.append(typingHTML);
scrollToBottom();
}
function removeTypingIndicator() {
$messages.find('.typing-message').remove();
}
function scrollToBottom(options = {}) {
const duration = typeof options.duration === 'number' ? options.duration : 300;
if (!duration) {
$messages.scrollTop($messages[0].scrollHeight);
return;
}
$messages.stop(true).animate({
scrollTop: $messages[0].scrollHeight
}, duration, 'swing');
}
function getCurrentTime() {
const now = new Date();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
}
function adjustInputHeight() {
const inputEl = $input[0];
if (!inputEl) {
return;
}
inputEl.style.height = 'auto';
inputEl.style.height = Math.min(inputEl.scrollHeight, 120) + 'px';
}
function resetInputHeight() {
const inputEl = $input[0];
if (!inputEl) {
return;
}
inputEl.style.height = 'auto';
inputEl.style.height = INPUT_DEFAULT_HEIGHT + 'px';
}
function loadMessagesFromStorage() {
try {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
} catch (e) {
console.error('Failed to load chat history:', e);
return [];
}
}
function persistMessage(message) {
storedMessages.push(message);
if (storedMessages.length > MAX_STORED_MESSAGES) {
storedMessages = storedMessages.slice(-MAX_STORED_MESSAGES);
}
saveMessagesToStorage();
}
function saveMessagesToStorage() {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(storedMessages));
} catch (e) {
console.error('Failed to save chat history:', e);
}
}
function removeMessageFromHistory(text, type) {
if (!text || !type) {
return;
}
let removed = false;
for (let i = storedMessages.length - 1; i >= 0; i--) {
const record = storedMessages[i];
if (record && record.text === text && record.type === type) {
storedMessages.splice(i, 1);
removed = true;
break;
}
}
if (removed) {
saveMessagesToStorage();
}
}
function removeMessageElement($element) {
if (!$element || !$element.length) {
return;
}
$element.stop(true).animate({
opacity: 0,
marginTop: 0,
marginBottom: 0
}, 150, function() {
$(this).remove();
});
}
function restoreStoredMessages() {
if (!storedMessages.length) {
return;
}
storedMessages.forEach(function(message) {
if (message && message.text && message.type) {
const enableActions = !(message.meta && message.meta.enableActions === false);
addMessage(message.text, message.type, {
time: message.time,
save: false,
scroll: false,
enableActions: enableActions,
meta: message.meta || {}
});
}
});
}
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
$(document).on('click', function(e) {
if (isOpen &&
!$container.is(e.target) &&
$container.has(e.target).length === 0 &&
!$toggle.is(e.target) &&
$toggle.has(e.target).length === 0) {
toggleChat();
}
});
$container.on('click', function(e) {
e.stopPropagation();
});
function sendQuickQuestion(question) {
if (!question) {
return;
}
$input.val('');
resetInputHeight();
$sendBtn.prop('disabled', false);
const answer = findPredefinedAnswer(question);
if (answer) {
addMessage(question, 'user');
showTypingIndicator();
setTimeout(function() {
removeTypingIndicator();
addMessage(answer, 'bot', {
enableActions: false,
meta: { enableActions: false }
});
}, 400);
return;
}
sendMessage(question);
}
function findPredefinedAnswer(rawQuestion) {
if (!rawQuestion) {
return '';
}
const question = rawQuestion.trim().toLowerCase();
const data = chatBotAi.pre_question_answer || [];
if (Array.isArray(data)) {
const match = data.find(function(item) {
if (!item || !item.question || !item.answer) {
return false;
}
return item.question.trim().toLowerCase() === question;
});
return match && match.answer ? match.answer : '';
}
if (typeof data === 'object') {
for (const key in data) {
if (!Object.prototype.hasOwnProperty.call(data, key)) {
continue;
}
if (key.trim().toLowerCase() === question) {
return data[key];
}
}
}
return '';
}
$container.on('click', '.quick-question-btn', function(e) {
e.preventDefault();
const question = $(this).text();
sendQuickQuestion(question);
});
$container.on('click', '.message-action-btn[data-action="copy"]', function(e) {
e.preventDefault();
const $message = $(this).closest('.message');
const text = $message.find('.message-text').text();
copyToClipboard(text);
});
$container.on('click', '.message-action-btn[data-action="resend"]', function(e) {
e.preventDefault();
const $botMessage = $(this).closest('.message.bot-message');
const $userMessage = $botMessage.prevAll('.message.user-message').first();
if (!$botMessage.length || !$userMessage.length) {
return;
}
const userText = $userMessage.find('.message-text').text().trim();
const botText = $botMessage.find('.message-text').text().trim();
if (!userText) {
return;
}
removeTypingIndicator();
removeMessageElement($botMessage);
removeMessageElement($userMessage);
removeMessageFromHistory(botText, 'bot');
removeMessageFromHistory(userText, 'user');
setTimeout(function() {
sendMessage(userText);
}, 180);
});
if ($resetBtn.length) {
$resetBtn.on('click', function(e) {
e.preventDefault();
resetConversation();
});
}
function resetConversation() {
if ($resetBtn.prop('disabled')) {
return;
}
removeTypingIndicator();
$resetBtn.prop('disabled', true);
storedMessages = [];
localStorage.removeItem(STORAGE_KEY);
$input.val('');
resetInputHeight();
$sendBtn.prop('disabled', false);
$messages.stop(true).animate({ opacity: 0 }, 180, function() {
$messages.empty();
if (initialMessageTemplate) {
$messages.append(initialMessageTemplate);
}
$messages.css('opacity', 0).animate({ opacity: 1 }, 220, function() {
$resetBtn.prop('disabled', false);
if (isOpen) {
scrollToBottom({ duration: 200 });
}
});
});
}
});
})(jQuery);