| 1 |
/* |
| 2 |
* Chatbot JS (Premium Sync) |
| 3 |
*/ |
| 4 |
|
| 5 |
jQuery(document).ready(function ($) { |
| 6 |
const chatbotState = { |
| 7 |
conversationMessages: [], |
| 8 |
apiKeyValidated: false, |
| 9 |
currentEditor: null, |
| 10 |
editors: { |
| 11 |
custom: null, |
| 12 |
css: null, |
| 13 |
js: null |
| 14 |
} |
| 15 |
}; |
| 16 |
|
| 17 |
const icons = { |
| 18 |
ai: `<div class="msg-icon ai-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg></div>`, |
| 19 |
user: `<div class="msg-icon user-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg></div>`, |
| 20 |
warning: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>`, |
| 21 |
success: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`, |
| 22 |
book: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1-2.5-2.5Z"/><path d="M6.5 2H20v20H6.5"/></svg>` |
| 23 |
}; |
| 24 |
|
| 25 |
|
| 26 |
|
| 27 |
initializeChatbot(); |
| 28 |
|
| 29 |
function initializeChatbot() { |
| 30 |
$('#chatbot-wrapper').hide(); |
| 31 |
bindEventHandlers(); |
| 32 |
initializeCodeMirrorDetection(); |
| 33 |
} |
| 34 |
|
| 35 |
function bindEventHandlers() { |
| 36 |
$('#chatbot-toggle').on('click', handleChatbotToggle); |
| 37 |
$('#chatbot-close').on('click', () => $('#chatbot-wrapper').fadeOut('fast')); |
| 38 |
$('#chatbot-send').on('click', handleSendMessage); |
| 39 |
$('#chatbot-input').on('keypress', handleInputKeypress); |
| 40 |
$(document).on('click', '#chatbot-input-container', () => $('#chatbot-input').focus()); |
| 41 |
$('#chatbot-input').on('input', handleInputResize); |
| 42 |
$(document).on('click', '.apply-snippet-btn', handleApplySnippet); |
| 43 |
$(document).on('click', '.read-code-btn', handleReadCode); |
| 44 |
} |
| 45 |
|
| 46 |
function handleChatbotToggle() { |
| 47 |
const wrapper = $('#chatbot-wrapper'); |
| 48 |
if (wrapper.is(':visible')) { |
| 49 |
wrapper.fadeOut('fast'); |
| 50 |
} else { |
| 51 |
wrapper.css('display', 'flex').hide().fadeIn('fast'); |
| 52 |
if (!chatbotState.apiKeyValidated) { |
| 53 |
checkApiKey(); |
| 54 |
} |
| 55 |
} |
| 56 |
} |
| 57 |
|
| 58 |
function handleClearChat() { |
| 59 |
if (confirm('Are you sure you want to clear this conversation?')) { |
| 60 |
chatbotState.conversationMessages = []; |
| 61 |
$('#chatbot-messages').empty(); |
| 62 |
showWelcomeMessage(); |
| 63 |
} |
| 64 |
} |
| 65 |
|
| 66 |
function handleSendMessage(e) { |
| 67 |
e.preventDefault(); |
| 68 |
sendMessage(); |
| 69 |
} |
| 70 |
|
| 71 |
function handleInputKeypress(e) { |
| 72 |
if (e.which === 13 && !e.shiftKey) { |
| 73 |
e.preventDefault(); |
| 74 |
sendMessage(); |
| 75 |
} |
| 76 |
} |
| 77 |
|
| 78 |
function handleInputResize() { |
| 79 |
this.style.height = 'auto'; |
| 80 |
this.style.height = Math.min(this.scrollHeight, 120) + 'px'; |
| 81 |
} |
| 82 |
|
| 83 |
function handleApplySnippet() { |
| 84 |
const codeId = $(this).data('code-id'); |
| 85 |
const language = $(this).data('language'); |
| 86 |
const codeElement = $('#' + codeId); |
| 87 |
|
| 88 |
if (codeElement.length) { |
| 89 |
const rawCode = decodeURIComponent(codeElement.attr('data-raw') || ''); |
| 90 |
const code = rawCode || codeElement.text(); |
| 91 |
|
| 92 |
applyCodeToEditor(code, language); |
| 93 |
showButtonFeedback($(this), 'Applied!'); |
| 94 |
} |
| 95 |
} |
| 96 |
|
| 97 |
function handleReadCode() { |
| 98 |
const editorType = $(this).data('editor-type'); |
| 99 |
const code = readCodeFromEditor(editorType); |
| 100 |
|
| 101 |
if (code) { |
| 102 |
const messages = $('#chatbot-messages'); |
| 103 |
messages.append(createReadCodeMessage(editorType, code)); |
| 104 |
scrollToBottom(); |
| 105 |
} |
| 106 |
} |
| 107 |
|
| 108 |
function checkApiKey() { |
| 109 |
$.ajax({ |
| 110 |
type: 'POST', |
| 111 |
url: PSchatbot.ajaxurl, |
| 112 |
data: { |
| 113 |
action: 'check_api_key', |
| 114 |
nonce: PSchatbot.nonce |
| 115 |
}, |
| 116 |
success: function (response) { |
| 117 |
if (response.success) { |
| 118 |
chatbotState.apiKeyValidated = true; |
| 119 |
showWelcomeMessage(); |
| 120 |
} else { |
| 121 |
showApiKeyError(); |
| 122 |
} |
| 123 |
}, |
| 124 |
error: function () { |
| 125 |
showApiKeyError(); |
| 126 |
} |
| 127 |
}); |
| 128 |
} |
| 129 |
|
| 130 |
function showWelcomeMessage() { |
| 131 |
const messages = $('#chatbot-messages'); |
| 132 |
if (messages.children().length === 0) { |
| 133 |
messages.append(` |
| 134 |
<div class="welcome-message"> |
| 135 |
<div class="message-content"> |
| 136 |
<strong>AI Assistant</strong> Hello! I'm your AI coding assistant. I can help you with PHP, JavaScript, CSS, and general programming questions. I can also read and analyze your current code. What would you like to work on today? |
| 137 |
</div> |
| 138 |
</div> |
| 139 |
`); |
| 140 |
} |
| 141 |
} |
| 142 |
|
| 143 |
function showApiKeyError() { |
| 144 |
const messages = $('#chatbot-messages'); |
| 145 |
messages.html(` |
| 146 |
<div class="error-message"> |
| 147 |
<div class="message-content"> |
| 148 |
<strong>Configuration Required</strong> |
| 149 |
Please add your OpenAI API key in the |
| 150 |
<a href="${PSchatbot.settings_url}" target="_blank" style="color: inherit; text-decoration: underline;"> |
| 151 |
AI Chatbot settings |
| 152 |
</a> |
| 153 |
before starting the chat. |
| 154 |
</div> |
| 155 |
</div> |
| 156 |
`); |
| 157 |
$('#chatbot-input').prop('disabled', true); |
| 158 |
$('#chatbot-send').prop('disabled', true); |
| 159 |
} |
| 160 |
|
| 161 |
function sendMessage() { |
| 162 |
if (!chatbotState.apiKeyValidated) { |
| 163 |
checkApiKey(); |
| 164 |
return; |
| 165 |
} |
| 166 |
|
| 167 |
const input = $('#chatbot-input'); |
| 168 |
const userMessage = $.trim(input.val()); |
| 169 |
|
| 170 |
if (!userMessage) return; |
| 171 |
|
| 172 |
const codeAnalysisKeywords = ['syntax error', 'fix', 'debug', 'error in my file', 'check my code', 'analyze']; |
| 173 |
const needsCodeAnalysis = codeAnalysisKeywords.some(keyword => |
| 174 |
userMessage.toLowerCase().includes(keyword) |
| 175 |
); |
| 176 |
|
| 177 |
let messageToSend = userMessage; |
| 178 |
let currentCode = ''; |
| 179 |
|
| 180 |
if (needsCodeAnalysis) { |
| 181 |
const detectedEditor = detectActiveEditor(); |
| 182 |
if (detectedEditor) { |
| 183 |
currentCode = readCodeFromEditor(detectedEditor); |
| 184 |
if (currentCode) { |
| 185 |
messageToSend = `${userMessage}\n\nCurrent code from ${detectedEditor} editor:\n\`\`\`${getLanguageFromEditor(detectedEditor)}\n${currentCode}\n\`\`\``; |
| 186 |
} |
| 187 |
} |
| 188 |
} |
| 189 |
|
| 190 |
displayUserMessage(userMessage); |
| 191 |
input.val(''); |
| 192 |
scrollToBottom(); |
| 193 |
|
| 194 |
const loadingId = showLoadingMessage(); |
| 195 |
disableSendButton(); |
| 196 |
|
| 197 |
sendAjaxRequest(messageToSend, loadingId); |
| 198 |
} |
| 199 |
|
| 200 |
function detectActiveEditor() { |
| 201 |
const editors = ['custom', 'css', 'js']; |
| 202 |
|
| 203 |
for (const editorType of editors) { |
| 204 |
const editor = getEditorByType(editorType); |
| 205 |
if (editor && editor.hasFocus && editor.hasFocus()) { |
| 206 |
return editorType; |
| 207 |
} |
| 208 |
} |
| 209 |
|
| 210 |
for (const editorType of editors) { |
| 211 |
const code = readCodeFromEditor(editorType); |
| 212 |
if (code && code.trim().length > 0) { |
| 213 |
return editorType; |
| 214 |
} |
| 215 |
} |
| 216 |
|
| 217 |
return 'custom'; |
| 218 |
} |
| 219 |
|
| 220 |
function getLanguageFromEditor(editorType) { |
| 221 |
const languageMap = { |
| 222 |
'custom': 'php', |
| 223 |
'css': 'css', |
| 224 |
'js': 'javascript' |
| 225 |
}; |
| 226 |
return languageMap[editorType] || 'text'; |
| 227 |
} |
| 228 |
|
| 229 |
function displayUserMessage(message) { |
| 230 |
const messages = $('#chatbot-messages'); |
| 231 |
messages.append(` |
| 232 |
<div class="user-message"> |
| 233 |
${icons.user} |
| 234 |
<div class="message-content"> |
| 235 |
<strong>You</strong> ${escapeHtml(message)} |
| 236 |
</div> |
| 237 |
</div> |
| 238 |
`); |
| 239 |
} |
| 240 |
|
| 241 |
function showLoadingMessage() { |
| 242 |
const loadingId = 'loading-' + Date.now(); |
| 243 |
const messages = $('#chatbot-messages'); |
| 244 |
messages.append(` |
| 245 |
<div id="${loadingId}" class="ai-message loading"> |
| 246 |
${icons.ai} |
| 247 |
<div class="message-content"> |
| 248 |
<strong>AI Assistant</strong> |
| 249 |
<span class="typing-indicator"> |
| 250 |
<span></span> |
| 251 |
<span></span> |
| 252 |
<span></span> |
| 253 |
</span> |
| 254 |
</div> |
| 255 |
</div> |
| 256 |
`); |
| 257 |
scrollToBottom(); |
| 258 |
return loadingId; |
| 259 |
} |
| 260 |
|
| 261 |
function disableSendButton() { |
| 262 |
$('#chatbot-send').prop('disabled', true); |
| 263 |
} |
| 264 |
|
| 265 |
function enableSendButton() { |
| 266 |
$('#chatbot-send').prop('disabled', false); |
| 267 |
$('#chatbot-input').focus(); |
| 268 |
} |
| 269 |
|
| 270 |
function sendAjaxRequest(message, loadingId) { |
| 271 |
$.ajax({ |
| 272 |
type: 'POST', |
| 273 |
url: PSchatbot.ajaxurl, |
| 274 |
data: { |
| 275 |
action: 'chatbot_ask', |
| 276 |
prompt: message, |
| 277 |
messages: JSON.stringify(chatbotState.conversationMessages), |
| 278 |
nonce: PSchatbot.nonce |
| 279 |
}, |
| 280 |
timeout: 60000, |
| 281 |
success: function (response) { |
| 282 |
handleAjaxSuccess(response, loadingId); |
| 283 |
}, |
| 284 |
error: function (xhr, status, error) { |
| 285 |
handleAjaxError(xhr, status, error, loadingId); |
| 286 |
}, |
| 287 |
complete: function () { |
| 288 |
enableSendButton(); |
| 289 |
} |
| 290 |
}); |
| 291 |
} |
| 292 |
|
| 293 |
function handleAjaxSuccess(response, loadingId) { |
| 294 |
$('#' + loadingId).remove(); |
| 295 |
|
| 296 |
if (response.success && response.data) { |
| 297 |
const content = response.data.content || response.data; |
| 298 |
const processedContent = processAIResponse(content); |
| 299 |
|
| 300 |
displayAIMessage(processedContent); |
| 301 |
|
| 302 |
if (response.data.messages) { |
| 303 |
chatbotState.conversationMessages = response.data.messages; |
| 304 |
} |
| 305 |
} else { |
| 306 |
const errorMsg = response.data ? response.data : 'Unknown error occurred'; |
| 307 |
showErrorMessage(errorMsg); |
| 308 |
} |
| 309 |
|
| 310 |
scrollToBottom(); |
| 311 |
} |
| 312 |
|
| 313 |
function handleAjaxError(xhr, status, error, loadingId) { |
| 314 |
$('#' + loadingId).remove(); |
| 315 |
|
| 316 |
let errorMessage; |
| 317 |
if (status === 'timeout') { |
| 318 |
errorMessage = 'Request timed out. Please try again with a shorter message.'; |
| 319 |
} else if (xhr.responseJSON && xhr.responseJSON.data) { |
| 320 |
errorMessage = xhr.responseJSON.data; |
| 321 |
} else if (xhr.responseText) { |
| 322 |
try { |
| 323 |
const errorData = JSON.parse(xhr.responseText); |
| 324 |
errorMessage = errorData.data || 'Server error occurred'; |
| 325 |
} catch (e) { |
| 326 |
errorMessage = 'Server error occurred. Please try again.'; |
| 327 |
} |
| 328 |
} else { |
| 329 |
errorMessage = 'Network error. Please check your connection and try again.'; |
| 330 |
} |
| 331 |
|
| 332 |
showErrorMessage(errorMessage); |
| 333 |
scrollToBottom(); |
| 334 |
} |
| 335 |
|
| 336 |
function displayAIMessage(content) { |
| 337 |
const messages = $('#chatbot-messages'); |
| 338 |
messages.append(` |
| 339 |
<div class="ai-message"> |
| 340 |
${icons.ai} |
| 341 |
<div class="message-content"> |
| 342 |
<strong>AI Assistant</strong> |
| 343 |
<div class="ai-response">${content}</div> |
| 344 |
</div> |
| 345 |
</div> |
| 346 |
`); |
| 347 |
} |
| 348 |
|
| 349 |
function processAIResponse(content) { |
| 350 |
content = content.replace(/```(\w+)?\n([\s\S]*?)```/g, function (match, language, code) { |
| 351 |
const lang = language || 'text'; |
| 352 |
const escapedCode = escapeHtml(code.trim()); |
| 353 |
const rawCode = code.trim(); |
| 354 |
const uniqueId = 'code-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9); |
| 355 |
|
| 356 |
return `<div class="code-block" data-language="${lang}"> |
| 357 |
<div class="code-header"> |
| 358 |
<span class="language-label">${lang.toUpperCase()}</span> |
| 359 |
</div> |
| 360 |
<pre><code class="language-${lang}" id="${uniqueId}" data-raw="${encodeURIComponent(rawCode)}">${escapedCode}</code></pre> |
| 361 |
<div class="code-actions"> |
| 362 |
<button class="apply-snippet-btn" data-code-id="${uniqueId}" data-language="${lang}">Apply Snippet</button> |
| 363 |
<button class="copy-code-btn" data-code-id="${uniqueId}">Copy Code</button> |
| 364 |
</div> |
| 365 |
</div>`; |
| 366 |
}); |
| 367 |
|
| 368 |
content = content.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>'); |
| 369 |
content = content.replace(/\n/g, '<br>'); |
| 370 |
|
| 371 |
return content; |
| 372 |
} |
| 373 |
|
| 374 |
function readCodeFromEditor(editorType) { |
| 375 |
const editor = getEditorByType(editorType); |
| 376 |
|
| 377 |
if (editor) { |
| 378 |
try { |
| 379 |
return editor.getValue(); |
| 380 |
} catch (error) { |
| 381 |
console.error('Error reading from CodeMirror:', error); |
| 382 |
} |
| 383 |
} |
| 384 |
|
| 385 |
const textarea = getTextareaByType(editorType); |
| 386 |
if (textarea.length) { |
| 387 |
return textarea.val(); |
| 388 |
} |
| 389 |
|
| 390 |
return ''; |
| 391 |
} |
| 392 |
|
| 393 |
function getEditorByType(editorType) { |
| 394 |
if (chatbotState.editors[editorType]) { |
| 395 |
return chatbotState.editors[editorType]; |
| 396 |
} |
| 397 |
|
| 398 |
const selectors = { |
| 399 |
'custom': '.post-snippets-edit:not(.post-snippets-edit-css):not(.post-snippets-edit-js)', |
| 400 |
'css': '.post-snippets-edit-css', |
| 401 |
'js': '.post-snippets-edit-js' |
| 402 |
}; |
| 403 |
|
| 404 |
const selector = selectors[editorType]; |
| 405 |
if (selector) { |
| 406 |
const editorElement = $(selector).closest('.CodeMirror')[0]; |
| 407 |
if (editorElement && editorElement.CodeMirror) { |
| 408 |
chatbotState.editors[editorType] = editorElement.CodeMirror; |
| 409 |
return editorElement.CodeMirror; |
| 410 |
} |
| 411 |
} |
| 412 |
|
| 413 |
return null; |
| 414 |
} |
| 415 |
|
| 416 |
function getTextareaByType(editorType) { |
| 417 |
const textareaSelectors = { |
| 418 |
'custom': 'textarea[name="snippet_code"], textarea#snippet_code', |
| 419 |
'css': 'textarea[name="snippet_css"], textarea#snippet_css', |
| 420 |
'js': 'textarea[name="snippet_js"], textarea#snippet_js' |
| 421 |
}; |
| 422 |
|
| 423 |
const selector = textareaSelectors[editorType]; |
| 424 |
return selector ? $(selector) : $(); |
| 425 |
} |
| 426 |
|
| 427 |
function createReadCodeMessage(editorType, code) { |
| 428 |
const truncatedCode = code.length > 500 ? code.substring(0, 500) + '...' : code; |
| 429 |
return ` |
| 430 |
<div class="system-message"> |
| 431 |
<div class="message-content"> |
| 432 |
<strong>Editor Snippet Read</strong> |
| 433 |
<pre class="code-preview"><code>${escapeHtml(truncatedCode)}</code></pre> |
| 434 |
<small>Total characters: ${code.length}</small> |
| 435 |
</div> |
| 436 |
</div> |
| 437 |
`; |
| 438 |
} |
| 439 |
|
| 440 |
function applyCodeToEditor(code, language) { |
| 441 |
const editorType = detectEditorTypeFromLanguage(language); |
| 442 |
const editor = getEditorByType(editorType); |
| 443 |
|
| 444 |
if (editor) { |
| 445 |
try { |
| 446 |
// If there's no selection, this will insert at the cursor. |
| 447 |
// If there is a selection, it will replace it. |
| 448 |
editor.replaceSelection(code); |
| 449 |
editor.focus(); |
| 450 |
editor.refresh(); |
| 451 |
|
| 452 |
$(editor.getWrapperElement()).trigger('change'); |
| 453 |
const textarea = $(editor.getTextArea()); |
| 454 |
if (textarea.length) { |
| 455 |
textarea.trigger('change'); |
| 456 |
} |
| 457 |
|
| 458 |
showSuccessMessage(`Code applied to ${editorType} editor successfully!`); |
| 459 |
return true; |
| 460 |
} catch (error) { |
| 461 |
console.error('Error applying code to CodeMirror:', error); |
| 462 |
} |
| 463 |
} |
| 464 |
|
| 465 |
const textarea = getTextareaByType(editorType); |
| 466 |
if (textarea.length) { |
| 467 |
insertAtCaret(textarea, code); |
| 468 |
showSuccessMessage(`Code applied to ${editorType} editor successfully!`); |
| 469 |
return true; |
| 470 |
} |
| 471 |
|
| 472 |
copyToClipboard(code); |
| 473 |
showSuccessMessage('Code copied to clipboard! Please paste it into your editor.'); |
| 474 |
return false; |
| 475 |
} |
| 476 |
|
| 477 |
function insertAtCaret(textarea, text) { |
| 478 |
const el = textarea[0]; |
| 479 |
const scrollPos = el.scrollTop; |
| 480 |
const strPos = el.selectionStart; |
| 481 |
const front = (el.value).substring(0, strPos); |
| 482 |
const back = (el.value).substring(el.selectionEnd, el.value.length); |
| 483 |
|
| 484 |
el.value = front + text + back; |
| 485 |
textarea.trigger('change').focus(); |
| 486 |
el.selectionStart = strPos + text.length; |
| 487 |
el.selectionEnd = strPos + text.length; |
| 488 |
el.scrollTop = scrollPos; |
| 489 |
} |
| 490 |
|
| 491 |
function detectEditorTypeFromLanguage(language) { |
| 492 |
const languageMap = { |
| 493 |
'php': 'custom', |
| 494 |
'html': 'custom', |
| 495 |
'css': 'css', |
| 496 |
'javascript': 'js', |
| 497 |
'js': 'js' |
| 498 |
}; |
| 499 |
return languageMap[language] || 'custom'; |
| 500 |
} |
| 501 |
|
| 502 |
function showButtonFeedback(button, text) { |
| 503 |
const originalText = button.text(); |
| 504 |
button.text(text).addClass('applied'); |
| 505 |
|
| 506 |
setTimeout(() => { |
| 507 |
button.text(originalText).removeClass('applied'); |
| 508 |
}, 2000); |
| 509 |
} |
| 510 |
|
| 511 |
function copyToClipboard(text) { |
| 512 |
const tempTextarea = document.createElement('textarea'); |
| 513 |
tempTextarea.value = text; |
| 514 |
document.body.appendChild(tempTextarea); |
| 515 |
tempTextarea.select(); |
| 516 |
tempTextarea.setSelectionRange(0, 99999); |
| 517 |
|
| 518 |
try { |
| 519 |
document.execCommand('copy'); |
| 520 |
} catch (err) { |
| 521 |
console.error('Failed to copy text:', err); |
| 522 |
} |
| 523 |
|
| 524 |
document.body.removeChild(tempTextarea); |
| 525 |
} |
| 526 |
|
| 527 |
function showSuccessMessage(message) { |
| 528 |
const messages = $('#chatbot-messages'); |
| 529 |
const successDiv = $(` |
| 530 |
<div class="success-message"> |
| 531 |
<div class="message-content"> |
| 532 |
<strong>Success</strong> ${escapeHtml(message)} |
| 533 |
</div> |
| 534 |
</div> |
| 535 |
`); |
| 536 |
|
| 537 |
messages.append(successDiv); |
| 538 |
scrollToBottom(); |
| 539 |
|
| 540 |
setTimeout(() => { |
| 541 |
successDiv.fadeOut(() => successDiv.remove()); |
| 542 |
}, 3000); |
| 543 |
} |
| 544 |
|
| 545 |
function showErrorMessage(message) { |
| 546 |
const messages = $('#chatbot-messages'); |
| 547 |
messages.append(` |
| 548 |
<div class="error-message"> |
| 549 |
<div class="message-content"> |
| 550 |
<strong>Error</strong> ${escapeHtml(message)} |
| 551 |
</div> |
| 552 |
</div> |
| 553 |
`); |
| 554 |
} |
| 555 |
|
| 556 |
function scrollToBottom() { |
| 557 |
const messages = $('#chatbot-messages'); |
| 558 |
messages.scrollTop(messages[0].scrollHeight); |
| 559 |
} |
| 560 |
|
| 561 |
function escapeHtml(text) { |
| 562 |
const div = document.createElement('div'); |
| 563 |
div.textContent = text; |
| 564 |
return div.innerHTML; |
| 565 |
} |
| 566 |
|
| 567 |
function initializeCodeMirrorDetection() { |
| 568 |
setTimeout(() => { |
| 569 |
detectAllEditors(); |
| 570 |
}, 1000); |
| 571 |
|
| 572 |
const observer = new MutationObserver((mutations) => { |
| 573 |
mutations.forEach((mutation) => { |
| 574 |
if (mutation.type === 'childList') { |
| 575 |
mutation.addedNodes.forEach((node) => { |
| 576 |
if (node.nodeType === 1 && node.classList && node.classList.contains('CodeMirror')) { |
| 577 |
if (node.CodeMirror) { |
| 578 |
cacheEditorInstance(node.CodeMirror); |
| 579 |
} |
| 580 |
} |
| 581 |
}); |
| 582 |
} |
| 583 |
}); |
| 584 |
}); |
| 585 |
|
| 586 |
observer.observe(document.body, { |
| 587 |
childList: true, |
| 588 |
subtree: true |
| 589 |
}); |
| 590 |
|
| 591 |
setTimeout(() => observer.disconnect(), 10000); |
| 592 |
} |
| 593 |
|
| 594 |
function detectAllEditors() { |
| 595 |
const editorSelectors = { |
| 596 |
'custom': '.post-snippets-edit:not(.post-snippets-edit-css):not(.post-snippets-edit-js)', |
| 597 |
'css': '.post-snippets-edit-css', |
| 598 |
'js': '.post-snippets-edit-js' |
| 599 |
}; |
| 600 |
|
| 601 |
Object.keys(editorSelectors).forEach(editorType => { |
| 602 |
const selector = editorSelectors[editorType]; |
| 603 |
$(selector).each(function () { |
| 604 |
const codeMirrorElement = $(this).closest('.CodeMirror')[0]; |
| 605 |
if (codeMirrorElement && codeMirrorElement.CodeMirror) { |
| 606 |
chatbotState.editors[editorType] = codeMirrorElement.CodeMirror; |
| 607 |
} |
| 608 |
}); |
| 609 |
}); |
| 610 |
} |
| 611 |
|
| 612 |
function cacheEditorInstance(editor) { |
| 613 |
const wrapper = $(editor.getWrapperElement()); |
| 614 |
|
| 615 |
if (wrapper.find('.post-snippets-edit-css').length) { |
| 616 |
chatbotState.editors.css = editor; |
| 617 |
} else if (wrapper.find('.post-snippets-edit-js').length) { |
| 618 |
chatbotState.editors.js = editor; |
| 619 |
} else { |
| 620 |
chatbotState.editors.custom = editor; |
| 621 |
} |
| 622 |
} |
| 623 |
|
| 624 |
$(document).on('click', '.copy-code-btn', function () { |
| 625 |
const codeId = $(this).data('code-id'); |
| 626 |
const codeElement = $('#' + codeId); |
| 627 |
|
| 628 |
if (codeElement.length) { |
| 629 |
const code = codeElement.text(); |
| 630 |
copyToClipboard(code); |
| 631 |
showButtonFeedback($(this), 'Copied!'); |
| 632 |
} |
| 633 |
}); |
| 634 |
}); |