');
resultsWrap.show();
moreBtn.hide();
}
});
});
// More Results button
wpforo_wrap.on('click', '.wpf-ai-more-btn', function () {
var btn = $(this);
var resultsWrap = btn.closest('.wpf-ai-results');
var resultsList = resultsWrap.find('.wpf-ai-results-list');
// Show loading
btn.prop('disabled', true).text(wpforo_phrase('Loading...'));
wpforoAiSearch(aiSearchQuery, aiSearchLimit, aiSearchOffset, function (response) {
btn.prop('disabled', false).text(wpforo_phrase('More Results'));
if (response.success && response.data.results.length > 0) {
resultsList.append(wpforoRenderAiResults(response.data.results));
aiSearchOffset += aiSearchLimit;
if (!response.data.has_more) {
btn.parent().hide();
}
}
});
});
// AI Search AJAX function
function wpforoAiSearch(query, limit, offset, callback) {
$.ajax({
url: wpforo.ajax_url,
type: 'POST',
data: {
action: 'wpforo_ai_public_search',
query: query,
limit: limit,
offset: offset,
language: aiSearchLanguage,
_wpnonce: wpforo.nonces.wpforo_ai_public_search
},
success: callback,
error: function (xhr) {
// Try to get error message from response (handles 429 rate limit errors)
var errorMsg = wpforo_phrase('Request failed');
try {
var response = JSON.parse(xhr.responseText);
if (response.data && response.data.message) {
errorMsg = response.data.message;
}
} catch (e) {
// Keep default error message
}
callback({ success: false, data: { message: errorMsg } });
}
});
}
// =========================================================================
// TYPEWRITER EFFECT
// =========================================================================
// Typewriter effect for AI summary text
// onComplete callback fires when typing is finished
function wpforoTypewriterEffect(element, html, speed, onComplete) {
if (!element || !html) {
if (onComplete) onComplete();
return;
}
speed = speed || 15; // milliseconds per character
// Parse HTML to extract text nodes and tags
var tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
element.innerHTML = '';
element.style.visibility = 'visible';
// Recursive function to type through DOM nodes
function typeNode(node, callback) {
if (node.nodeType === Node.TEXT_NODE) {
// Text node - type character by character
var text = node.textContent;
var textNode = document.createTextNode('');
element.appendChild(textNode);
var charIndex = 0;
function typeChar() {
if (charIndex < text.length) {
textNode.textContent += text[charIndex];
charIndex++;
setTimeout(typeChar, speed);
} else {
callback();
}
}
typeChar();
} else if (node.nodeType === Node.ELEMENT_NODE) {
// Element node - clone and append, then process children
var clone = node.cloneNode(false);
element.appendChild(clone);
var children = Array.from(node.childNodes);
var childIndex = 0;
function processNextChild() {
if (childIndex < children.length) {
// Temporarily change element to append to clone
var originalElement = element;
element = clone;
typeNode(children[childIndex], function() {
element = originalElement;
childIndex++;
processNextChild();
});
} else {
callback();
}
}
processNextChild();
} else {
callback();
}
}
// Process all top-level nodes
var topNodes = Array.from(tempDiv.childNodes);
var nodeIndex = 0;
function processNextTopNode() {
if (nodeIndex < topNodes.length) {
typeNode(topNodes[nodeIndex], function() {
nodeIndex++;
processNextTopNode();
});
} else {
// All nodes processed - call onComplete callback
if (onComplete) onComplete();
}
}
processNextTopNode();
}
// =========================================================================
// RENDER FUNCTIONS
// =========================================================================
// Render AI Enhancement sections (Summary and Recommendations)
// All HTML is pre-rendered by PHP - JavaScript only inserts it
function wpforoRenderAiEnhancement(enhancement, results) {
if (!enhancement) return '';
var html = '';
// AI Search Summary Section
// PHP already converts [[#N]] and [[#N:Title]] to HTML links
if (enhancement.summary || enhancement.quick_answer) {
html += '
';
html += '
' + wpforo_phrase('AI Search Summary') + '
';
if (enhancement.quick_answer) {
// Output directly - PHP has already processed link markers
html += '
' + enhancement.quick_answer + '
';
}
if (enhancement.summary) {
// Store summary in data attribute for typewriter effect
var encodedSummary = enhancement.summary.replace(/"/g, '"');
html += '';
}
html += '
';
}
// AI Recommendations Section - use pre-rendered HTML from PHP
if (enhancement.recommendations_html) {
html += enhancement.recommendations_html;
}
return html;
}
// Render AI search results HTML
function wpforoRenderAiResults(results) {
var html = '';
// Add "AI Search Results" header before real results
html += '
';
html += '
' + wpforo_phrase('AI Search Results') + '
';
for (var i = 0; i < results.length; i++) {
var r = results[i];
html += '
';
var postIdBadge = r.post_id ? ' [ ' + r.post_id + ' ]' : '';
// Render title as link only if URL exists, otherwise plain text
if (r.url) {
html += '
';
if (r.content_source === 'custom_knowledge') {
var kbLabel = r.post_type_label || 'Knowledge Base';
html += ' ' + wpforoEscapeHtml(kbLabel) + '';
} else if (r.content_source === 'wordpress') {
var typeLabel = r.post_type_label || 'Post';
html += ' ' + wpforoEscapeHtml(typeLabel) + '';
} else if (r.forum_title) {
html += ' ' + wpforoEscapeHtml(r.forum_title) + '';
}
// Only show author if exists (custom_knowledge has no author)
if (r.author_name) {
html += ' ' + wpforoEscapeHtml(r.author_name) + '';
}
// Only show date if exists (custom_knowledge has no date)
if (r.created_ago) {
html += ' ' + r.created_ago + '';
}
html += ' ' + r.score + '%';
html += '
';
if (r.content) {
var formattedContent = wpforoFormatAiContent(r.content);
var lineCount = (r.content.match(/[\r\n]+/g) || []).length + 1;
var isLong = r.content.length > 500 || lineCount > 5;
html += '
';
html += '
' + formattedContent + '
';
if (isLong) {
html += '
' + wpforo_phrase('Show more') + '
';
}
html += '
';
}
html += '
';
}
html += '
';
return html;
}
// Format AI content: escape HTML and convert line breaks to
function wpforoFormatAiContent(text) {
if (!text) return '';
// First escape HTML
var escaped = wpforoEscapeHtml(text);
// Convert \r\n, \r, \n to for proper line breaks
escaped = escaped.replace(/\r\n/g, ' ');
escaped = escaped.replace(/\r/g, ' ');
escaped = escaped.replace(/\n/g, ' ');
// Convert multiple to paragraph breaks
escaped = escaped.replace(/( ){3,}/g, '
');
return escaped;
}
// Toggle AI search result content expand/collapse
wpforo_wrap.on('click', '.wpf-ai-toggle-btn', function() {
var $btn = $(this);
var $wrap = $btn.closest('.wpf-ai-result-content-wrap');
var isExpanded = $btn.data('expanded');
if (isExpanded) {
$wrap.addClass('wpf-ai-collapsed');
$btn.data('expanded', false);
$btn.find('.wpf-ai-toggle-text').text(wpforo_phrase('Show more'));
$btn.find('i').removeClass('fa-chevron-up').addClass('fa-chevron-down');
} else {
$wrap.removeClass('wpf-ai-collapsed');
$btn.data('expanded', true);
$btn.find('.wpf-ai-toggle-text').text(wpforo_phrase('Show less'));
$btn.find('i').removeClass('fa-chevron-down').addClass('fa-chevron-up');
}
});
// =========================================================================
// HELPER FUNCTIONS
// =========================================================================
// Escape HTML helper
function wpforoEscapeHtml(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// =========================================================================
// AI TRANSLATION
// =========================================================================
// Toggle translation dropdown
wpforo_wrap.on('click', '.wpf-ai-translate-btn', function (e) {
e.stopPropagation();
var wrapper = $(this).closest('.wpf-ai-translate-wrapper');
var dropdown = wrapper.find('.wpf-ai-translate-dropdown');
// Close other dropdowns
$('.wpf-ai-translate-dropdown').not(dropdown).removeClass('wpf-ai-translate-dropdown-open');
// Toggle this dropdown
dropdown.toggleClass('wpf-ai-translate-dropdown-open');
});
// Close dropdown when clicking outside
$(document).on('click', function () {
$('.wpf-ai-translate-dropdown').removeClass('wpf-ai-translate-dropdown-open');
});
// Prevent dropdown from closing when clicking inside it
wpforo_wrap.on('click', '.wpf-ai-translate-dropdown', function (e) {
e.stopPropagation();
});
// Handle language selection for translation
wpforo_wrap.on('click', '.wpf-ai-translate-option', function () {
var option = $(this);
var wrapper = option.closest('.wpf-ai-translate-wrapper');
var postId = wrapper.data('postid');
var language = option.data('lang');
var dropdown = wrapper.find('.wpf-ai-translate-dropdown');
var translateBtn = wrapper.find('.wpf-ai-translate-btn');
var originalBtn = wrapper.find('.wpf-ai-translate-original');
var loadingEl = wrapper.find('.wpf-ai-translate-loading');
// Close dropdown
dropdown.removeClass('wpf-ai-translate-dropdown-open');
// Find the post/comment content element
// Structure for Post: .post-wrap > .wpforo-post > .wpforo-post-content
// Structure for Q&A Layout Comment: .comment-wrap > .wpforo-comment-content > .wpforo-comment-text
var postElement = wrapper.closest('.wpforo-post').find('.wpforo-post-content');
if (!postElement.length) {
postElement = wrapper.closest('.post-wrap').find('.wpforo-post-content');
}
if (!postElement.length) {
postElement = wrapper.closest('.wpforo-comment').find('.wpforo-comment-text');
}
if (!postElement.length) {
postElement = wrapper.closest('.comment-wrap').find('.wpforo-comment-text');
}
if (!postElement.length) {
console.error('wpForo AI: Could not find post content element');
return;
}
// Store original content if not already stored
if (!postElement.data('original-content')) {
postElement.data('original-content', postElement.html());
}
// Show loading state
translateBtn.hide();
loadingEl.show();
// Make AJAX request
$.ajax({
url: wpforo.ajax_url,
type: 'POST',
data: {
action: 'wpforo_ai_translate',
post_id: postId,
language: language,
nonce: wpforo.nonces.wpforo_ai_translate
},
success: function (response) {
loadingEl.hide();
if (response.success && response.data.translated_content) {
// Replace content with translated version
postElement.html(response.data.translated_content);
postElement.addClass('wpf-ai-translated');
// Add RTL class for right-to-left languages (Arabic, Hebrew)
var rtlLanguages = ['Arabic', 'Hebrew', 'ar', 'he'];
if (rtlLanguages.indexOf(language) !== -1) {
postElement.addClass('wpf-ai-translated-rtl');
}
// Show "Show Original" button
originalBtn.show();
} else {
// Show error
translateBtn.show();
var errorMsg = response.data && response.data.message ? response.data.message : wpforo_phrase('Translation failed');
alert(errorMsg);
}
},
error: function (xhr) {
loadingEl.hide();
translateBtn.show();
// Try to get error message from response (handles 429 rate limit errors)
var errorMsg = wpforo_phrase('Network error. Please try again.');
try {
var response = JSON.parse(xhr.responseText);
if (response.data && response.data.message) {
errorMsg = response.data.message;
}
} catch (e) {
// Keep default error message
}
alert(errorMsg);
}
});
});
// Handle "Show Original" button click
wpforo_wrap.on('click', '.wpf-ai-translate-original', function () {
var wrapper = $(this).closest('.wpf-ai-translate-wrapper');
var originalBtn = wrapper.find('.wpf-ai-translate-original');
var translateBtn = wrapper.find('.wpf-ai-translate-btn');
// Find the post/comment content element
var postElement = wrapper.closest('.wpforo-post').find('.wpforo-post-content');
if (!postElement.length) {
postElement = wrapper.closest('.post-wrap').find('.wpforo-post-content');
}
if (!postElement.length) {
postElement = wrapper.closest('.wpforo-comment').find('.wpforo-comment-text');
}
if (!postElement.length) {
postElement = wrapper.closest('.comment-wrap').find('.wpforo-comment-text');
}
// Restore original content
var originalContent = postElement.data('original-content');
if (originalContent) {
postElement.html(originalContent);
postElement.removeClass('wpf-ai-translated wpf-ai-translated-rtl');
}
// Show translate button, hide original button
originalBtn.hide();
translateBtn.show();
});
// =========================================================================
// AI TOPIC SUMMARIZATION
// =========================================================================
var summaryLoadingInterval = null;
var summaryLoadingMessages = [
'Reading topic posts...',
'Analyzing discussion...',
'Generating summary...',
'Almost ready...'
];
// Show loading animation for topic summary
function wpforoShowSummaryLoading(container) {
var loadingHtml = '
' +
'
' +
'
' +
'' +
'' +
'' +
'
' +
'
' +
'
' + wpforo_phrase(summaryLoadingMessages[0]) + '
' +
'
';
container.html(loadingHtml);
// Rotate through loading messages
var msgIndex = 0;
summaryLoadingInterval = setInterval(function() {
msgIndex = (msgIndex + 1) % summaryLoadingMessages.length;
container.find('.wpf-ai-loading-text').text(wpforo_phrase(summaryLoadingMessages[msgIndex]));
}, 2500);
}
function wpforoHideSummaryLoading() {
if (summaryLoadingInterval) {
clearInterval(summaryLoadingInterval);
summaryLoadingInterval = null;
}
}
// Topic Summary Button Click Handler
wpforo_wrap.on('click', '.wpf-ai-summarize-btn', function (e) {
e.preventDefault();
wpforo_load_hide();
var btn = $(this);
var topicId = btn.data('topicid');
var nonce = btn.data('nonce');
var container = $('#wpf-ai-summary-' + topicId);
var contentArea = container.find('.wpf-ai-summary-content');
// If container is already visible and has content, just toggle it
if (container.is(':visible') && contentArea.find('.wpf-ai-summary-result').length > 0) {
container.slideUp(350);
return;
}
// Show container with loading
container.slideDown(350);
wpforoShowSummaryLoading(contentArea);
// Disable button during request
btn.addClass('wpf-ai-loading-btn');
// Make AJAX request
$.ajax({
url: wpforo.ajax_url,
type: 'POST',
data: {
action: 'wpforo_ai_summarize_topic',
topicid: topicId,
nonce: nonce
},
success: function (response) {
wpforoHideSummaryLoading();
btn.removeClass('wpf-ai-loading-btn');
if (response.success && response.data.summary) {
// Build posts info (show notice when posts were limited)
var postsInfo = '';
if (response.data.posts_limited && response.data.total_posts_count) {
postsInfo = '' +
response.data.reply_count + ' ' + wpforo_phrase('of') + ' ' + response.data.total_posts_count + ' ' + wpforo_phrase('posts') +
'';
}
// Render summary with close button in header (like AI Assistant)
var summaryHtml = '
';
contentArea.html(summaryHtml);
} else {
// Show error with close button
var errorMsg = response.data && response.data.message ? response.data.message : wpforo_phrase('Failed to generate summary');
contentArea.html('
' +
' ' + wpforoEscapeHtml(errorMsg) +
'
' +
'' +
'' + wpforo_phrase('Close') + '' +
'
' +
'
');
}
},
error: function (xhr) {
wpforoHideSummaryLoading();
btn.removeClass('wpf-ai-loading-btn');
// Try to get error message from response (handles 429 rate limit errors)
var errorMsg = wpforo_phrase('Network error. Please try again.');
try {
var response = JSON.parse(xhr.responseText);
if (response.data && response.data.message) {
errorMsg = response.data.message;
}
} catch (e) {
// Keep default error message
}
contentArea.html('
' +
' ' + wpforoEscapeHtml(errorMsg) +
'
' +
'' +
'' + wpforo_phrase('Close') + '' +
'
' +
'
');
}
});
});
// Topic Summary Close Button Handler (handles both footer close and header close button)
wpforo_wrap.on('click', '.wpf-ai-summary-close, .wpf-ai-summary-close-btn', function () {
var container = $(this).closest('.wpf-ai-summary-container');
container.slideUp(350);
});
// =========================================================================
// AI TOPIC SUGGESTIONS (Smart Topic Suggestions)
// =========================================================================
var suggestionCallCount = 0;
var suggestionConfig = null;
var suggestionLastQuery = '';
// Initialize suggestion config from data attributes
function wpforoInitSuggestionConfig() {
var panel = $('.wpf-ai-suggestions-panel');
if (panel.length && panel.data('suggestion-config')) {
suggestionConfig = panel.data('suggestion-config');
} else {
// Default config - disabled if panel not found
suggestionConfig = {
enabled: false,
min_words: 3,
max_calls: 2,
show_related: true,
show_answer: true,
quality: 'balanced'
};
}
}
// Count words in a string
function wpforoCountWords(str) {
if (!str) return 0;
return str.trim().split(/\s+/).filter(function(w) { return w.length > 0; }).length;
}
// Topic title input handler - triggers on blur (when user leaves the title field)
// Topic title field has name="thread[title]" and id="thread_title" in wpForo
wpforo_wrap.on('blur', '#thread_title, input[name="thread[title]"]', function (e) {
// Initialize config if not done
if (!suggestionConfig) {
wpforoInitSuggestionConfig();
}
// Check if suggestions are enabled
if (!suggestionConfig || !suggestionConfig.enabled) {
return;
}
var input = $(this);
var title = input.val().trim();
var wordCount = wpforoCountWords(title);
// Check minimum words (from config)
if (wordCount < suggestionConfig.min_words) {
return;
}
// Skip if same query as last time
if (title === suggestionLastQuery) {
return;
}
// Check max API calls per topic creation session (from config)
if (suggestionCallCount >= suggestionConfig.max_calls) {
return;
}
// Fetch suggestions immediately on blur (no debounce needed)
wpforoFetchSuggestions(title, input);
});
// Fetch suggestions from API
function wpforoFetchSuggestions(title, inputElement) {
if (!suggestionConfig || !suggestionConfig.enabled) return;
suggestionLastQuery = title;
suggestionCallCount++;
var form = inputElement.closest('form');
var panel = form.find('.wpf-ai-suggestions-panel');
var contentArea = panel.find('.wpf-ai-suggestions-content');
// Show panel with loading immediately on blur
panel.slideDown(300);
wpforoShowSuggestionLoading(contentArea);
// Make AJAX request
$.ajax({
url: wpforo.ajax_url,
type: 'POST',
data: {
action: 'wpforo_ai_get_topic_suggestions',
title: title,
quality: suggestionConfig.quality || 'balanced',
include_similar: 1, // Always include similar topics - required for the feature
include_related: suggestionConfig.show_related ? 1 : 0,
include_answer: suggestionConfig.show_answer ? 1 : 0,
nonce: wpforo.nonces.wpforo_ai_get_topic_suggestions
},
success: function (response) {
wpforoHideSuggestionLoading();
if (response.success && response.data.has_suggestions) {
wpforoRenderSuggestions(contentArea, response.data);
} else if (response.success && !response.data.has_suggestions) {
// No similar topics found - show message briefly then hide
contentArea.html('
' +
' ' + wpforo_phrase('No similar topics have been found.') +
'
');
}
},
error: function (xhr) {
wpforoHideSuggestionLoading();
// Try to get error message from response (handles 429 rate limit errors)
var errorMsg = wpforo_phrase('Network error. Please try again.');
try {
var response = JSON.parse(xhr.responseText);
if (response.data && response.data.message) {
errorMsg = response.data.message;
}
} catch (e) {
// Keep default error message
}
contentArea.html('
' +
' ' + wpforoEscapeHtml(errorMsg) +
'
');
}
});
}
// Loading animation for suggestions - compact single line
var suggestionLoadingInterval = null;
var suggestionLoadingMessages = [
'Searching similar topics...',
'Analyzing your question...',
'Finding relevant discussions...',
'Almost ready...'
];
function wpforoShowSuggestionLoading(container) {
var loadingHtml = '
';
for (var j = 0; j < data.related_topics.length; j++) {
var related = data.related_topics[j];
// Use the URL from API if available, fallback to search
var topicUrl = related.url || (wpforo_url + '?foro=search&wpfkeyword=' + encodeURIComponent(related.title));
html += '
' +
wpforo_phrase('This is an AI-generated answer based on existing forum content. Post your topic for more accurate human responses.') +
'
' +
'
' +
'
';
}
container.html(html);
}
// Close suggestions panel
wpforo_wrap.on('click', '.wpf-ai-suggestions-close', function () {
var panel = $(this).closest('.wpf-ai-suggestions-panel');
panel.slideUp(300);
});
// Toggle show more/less for quick answer
wpforo_wrap.on('click', '.wpf-ai-show-more-answer', function () {
var btn = $(this);
var answerText = btn.closest('.wpf-ai-suggestions-section-content').find('.wpf-ai-quick-answer-text');
var fullAnswer = btn.data('full');
var isExpanded = btn.data('expanded');
if (isExpanded) {
// Collapse - would need original stored, for now just hide
btn.html(' ' + wpforo_phrase('Show more'));
btn.data('expanded', false);
} else {
answerText.html(wpforoFormatAiContent(fullAnswer));
btn.html(' ' + wpforo_phrase('Show less'));
btn.data('expanded', true);
}
});
// Initialize suggestion config on page load
wpforoInitSuggestionConfig();
// Re-initialize suggestion config when topic form is loaded via AJAX
// wpForo triggers 'wpforo_topic_portable_form' event after AJAX form load
$(document).on('wpforo_topic_portable_form', function(event, formElement) {
// Reset suggestion state for new form
suggestionCallCount = 0;
suggestionLastQuery = '';
suggestionConfig = null;
// Re-initialize config from the new form's panel
if (formElement && formElement.length) {
var panel = formElement.find('.wpf-ai-suggestions-panel');
if (panel.length && panel.data('suggestion-config')) {
suggestionConfig = panel.data('suggestion-config');
}
}
// Fallback to global search if not found in form element
if (!suggestionConfig) {
wpforoInitSuggestionConfig();
}
});
// =========================================================================
// AI BOT REPLY
// =========================================================================
/**
* AI Bot Reply button click handler
* Creates a bot-generated reply to the post
*/
wpforo_wrap.on('click', '.wpf-ai-bot-reply', function (e) {
e.preventDefault();
e.stopPropagation();
var btn = $(this);
var postId = btn.data('postid');
var topicId = btn.data('topicid');
if (!postId || !topicId) {
console.error('AI Bot Reply: Missing post or topic ID');
return;
}
// Prevent double-clicks (check for wpf-processing spinning class)
if (btn.hasClass('wpf-processing')) {
return;
}
// Check if nonce is available
var nonce = wpforo.nonces && wpforo.nonces.wpforo_ai_bot_reply;
if (!nonce) {
alert(wpforo_phrase('AI Bot Reply is not properly configured. Please refresh the page and try again.'));
return;
}
// Show loading state - spinning icon (like wpforo-aibot plugin)
btn.addClass('wpf-processing');
if (typeof wpforo_load_show === 'function') {
wpforo_load_show();
}
// Make AJAX request
$.ajax({
url: wpforo.ajax_url,
type: 'POST',
data: {
action: 'wpforo_ai_bot_reply',
_wpnonce: nonce,
post_id: postId,
topic_id: topicId
}
}).done(function (response) {
if (response.success) {
// Reload page to show new reply (with anchor to new post)
var newPostId = response.data.post_id;
setTimeout(function() {
if (newPostId) {
window.location.href = window.location.pathname + window.location.search + '#post-' + newPostId;
window.location.reload();
} else {
window.location.reload();
}
}, 500);
} else {
btn.removeClass('wpf-processing');
if (typeof wpforo_load_hide === 'function') {
wpforo_load_hide();
}
var errorMsg = response.data && response.data.message
? response.data.message
: wpforo_phrase('Failed to generate bot reply');
// Provide helpful message for common configuration issues
if (errorMsg.indexOf('Bot user not configured') !== -1) {
errorMsg = wpforo_phrase('Bot user not configured') + '.\n\n' +
wpforo_phrase('Please go to') + ' wpForo > Settings > AI Features > AI Bot Reply ' +
wpforo_phrase('and select a WordPress user for the bot.');
}
alert(errorMsg);
}
}).fail(function (xhr, status, error) {
btn.removeClass('wpf-processing');
if (typeof wpforo_load_hide === 'function') {
wpforo_load_hide();
}
console.error('AI Bot Reply error:', status, error, xhr.responseText);
// Try to parse error message from response
var errorMsg = wpforo_phrase('Network error. Please try again.');
try {
var jsonResponse = JSON.parse(xhr.responseText);
if (jsonResponse.data && jsonResponse.data.message) {
errorMsg = jsonResponse.data.message;
// Add helpful message for bot user not configured
if (errorMsg.indexOf('Bot user not configured') !== -1) {
errorMsg = wpforo_phrase('Bot user not configured') + '.\n\n' +
wpforo_phrase('Please go to') + ' wpForo > Settings > AI Features > AI Bot Reply ' +
wpforo_phrase('and select a WordPress user for the bot.');
}
}
} catch (e) {
// Keep default error message
}
alert(errorMsg);
});
});
/**
* AI Suggest Reply button click handler
* Generates AI reply suggestion and inserts into TinyMCE editor
*/
wpforo_wrap.on('click', '.wpf-ai-suggest-reply', function (e) {
e.preventDefault();
var btn = $(this);
var topicId = btn.data('topicid');
if (!topicId) {
console.error('AI Suggest Reply: Missing topic ID');
return;
}
// Prevent double-clicks
if (btn.hasClass('wpf-ai-loading')) {
return;
}
// Check if nonce is available
var nonce = wpforo.nonces && wpforo.nonces.wpforo_ai_suggest_reply;
if (!nonce) {
alert(wpforo_phrase('AI Suggest Reply is not properly configured. Please refresh the page and try again.'));
return;
}
// Find the form and get parent post ID if available (for threaded replies)
var form = btn.closest('form');
var parentId = 0;
if (form.length) {
var parentInput = form.find('input[name="parentid"]');
if (parentInput.length) {
parentId = parseInt(parentInput.val(), 10) || 0;
}
}
// Show loading state (CSS handles icon visibility via .wpf-ai-suggest-loading class)
btn.addClass('wpf-ai-suggest-loading');
btn.find('span').text(wpforo_phrase('Processing...'));
// Make AJAX request
$.ajax({
url: wpforo.ajax_url,
type: 'POST',
data: {
action: 'wpforo_ai_suggest_reply',
_wpnonce: nonce,
topic_id: topicId,
parent_id: parentId
},
success: function (response) {
// Reset button state (CSS handles icon visibility)
btn.removeClass('wpf-ai-suggest-loading');
btn.find('span').text(wpforo_phrase('Suggest Reply'));
if (response.success) {
var content = response.data.content || '';
if (!content) {
alert(wpforo_phrase('AI generated an empty reply'));
return;
}
// Append content to TinyMCE editor
var inserted = wpforoInsertIntoEditor(content);
if (!inserted) {
// Fallback: try to append to textarea
var textarea = form.find('textarea[name="postbody"]');
if (textarea.length) {
var existingVal = textarea.val().trim();
if (existingVal) {
textarea.val(existingVal + '\n\n' + content);
} else {
textarea.val(content);
}
} else {
alert(wpforo_phrase('Could not insert content into editor'));
}
}
// Show credits used info
var credits = response.data.credits_used || 0;
if (credits > 0) {
console.log('AI Suggest Reply: ' + credits + ' credits used');
}
} else {
var errorMsg = response.data && response.data.message
? response.data.message
: wpforo_phrase('Failed to generate reply suggestion');
alert(errorMsg);
}
},
error: function (xhr, status, error) {
// Reset button state (CSS handles icon visibility)
btn.removeClass('wpf-ai-suggest-loading');
btn.find('span').text(wpforo_phrase('Suggest Reply'));
console.error('AI Suggest Reply error:', error);
// Try to get error message from response (handles 429 rate limit errors)
var errorMsg = wpforo_phrase('Network error. Please try again.');
try {
var response = JSON.parse(xhr.responseText);
if (response.data && response.data.message) {
errorMsg = response.data.message;
}
} catch (e) {
// Keep default error message
}
alert(errorMsg);
}
});
});
/**
* Append content to TinyMCE editor
* @param {string} content HTML content to append
* @returns {boolean} True if successful
*/
function wpforoInsertIntoEditor(content) {
// Try to find the active TinyMCE editor
if (typeof tinyMCE !== 'undefined' && tinyMCE.activeEditor) {
var editor = tinyMCE.activeEditor;
// Append content to existing content with line break
var existingContent = editor.getContent().trim();
if (existingContent) {
editor.setContent(existingContent + '
' + content);
} else {
editor.setContent(content);
}
// Focus the editor
editor.focus();
return true;
}
// Try by ID (wpForo's default editor ID)
if (typeof tinyMCE !== 'undefined') {
var editorIds = ['postbody', 'wpf_editor_postbody'];
for (var i = 0; i < editorIds.length; i++) {
var ed = tinyMCE.get(editorIds[i]);
if (ed) {
var existingContent = ed.getContent().trim();
if (existingContent) {
ed.setContent(existingContent + '
' + content);
} else {
ed.setContent(content);
}
ed.focus();
return true;
}
}
}
return false;
}
// =========================================================================
// AI BUTTON VISIBILITY ANIMATIONS
// =========================================================================
// Trigger animations when AI buttons become visible on screen
if ('IntersectionObserver' in window) {
var aiButtonObserver = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
// Add visible class to trigger animation
entry.target.classList.add('wpf-ai-visible');
// Stop observing after animation triggered
aiButtonObserver.unobserve(entry.target);
}
});
}, {
threshold: 0.5 // Trigger when 50% visible
});
// Observe AI Helper Toggle buttons
document.querySelectorAll('.wpf-ai-helper-toggle').forEach(function(el) {
aiButtonObserver.observe(el);
});
// Observe AI Summarize buttons
document.querySelectorAll('.wpf-ai-summarize-btn').forEach(function(el) {
aiButtonObserver.observe(el);
});
}
});