1// EngageAI Frontend JavaScript 2 3(function($) { 4 'use strict'; 5 6 // Main frontend object 7 const EngageAI_Frontend = { 8 init: function() { 9 this.bindEvents(); 10 this.initComponents(); 11 this.loadStoredData(); 12 }, 13 14 bindEvents: function() { 15 // Activity feed interactions 16 $(document).on('click', '.engageai-welcome-message', this.trackWelcomeInteraction); 17 $(document).on('click', '.engageai-discussion-prompt', this.trackDiscussionInteraction); 18 $(document).on('click', '.engageai-motivational-content', this.trackMotivationalInteraction); 19 20 // Comment form enhancements 21 $(document).on('focus', '.ac-reply-content', this.enhanceCommentForm); 22 $(document).on('submit', '.ac-form', this.trackCommentSubmission); 23 24 // Engagement tracking 25 $(document).on('click', '.activity-item .activity-content a', this.trackLinkClick); 26 $(document).on('click', '.acomment-reply', this.trackReplyClick); 27 28 // Gamification interactions 29 $(document).on('click', '.engageai-points-earned', this.showPointsDetails); 30 $(document).on('click', '.engageai-badge-earned', this.showBadgeDetails); 31 32 // Infinite scroll for activity feed 33 $(window).on('scroll', this.handleInfiniteScroll); 34 35 // Real-time notifications 36 this.initNotifications(); 37 }, 38 39 initComponents: function() { 40 this.initLazyLoading(); 41 this.initAnimations(); 42 this.initTooltips(); 43 this.initAccessibility(); 44 }, 45 46 // Track welcome message interactions 47 trackWelcomeInteraction: function(e) { 48 const $message = $(this); 49 const groupId = $message.data('group-id'); 50 const messageId = $message.data('message-id'); 51 52 EngageAI_Frontend.trackEvent('welcome_message_clicked', { 53 group_id: groupId, 54 message_id: messageId 55 }); 56 }, 57 58 // Track discussion prompt interactions 59 trackDiscussionInteraction: function(e) { 60 const $prompt = $(this); 61 const promptId = $prompt.data('prompt-id'); 62 const groupId = $prompt.data('group-id'); 63 64 EngageAI_Frontend.trackEvent('discussion_prompt_clicked', { 65 prompt_id: promptId, 66 group_id: groupId 67 }); 68 69 // Focus on comment form if it exists 70 const $commentForm = $('.ac-reply-content'); 71 if ($commentForm.length) { 72 setTimeout(function() { 73 $commentForm.focus(); 74 }, 300); 75 } 76 }, 77 78 // Track motivational content interactions 79 trackMotivationalInteraction: function(e) { 80 const $content = $(this); 81 const contentId = $content.data('content-id'); 82 83 EngageAI_Frontend.trackEvent('motivational_content_clicked', { 84 content_id: contentId 85 }); 86 }, 87 88 // Enhance comment form with suggestions 89 enhanceCommentForm: function(e) { 90 const $form = $(this).closest('.ac-form'); 91 const $activityItem = $form.closest('.activity-item'); 92 93 if ($activityItem.hasClass('engageai-automated')) { 94 EngageAI_Frontend.addCommentSuggestions($form); 95 } 96 }, 97 98 // Add comment suggestions 99 addCommentSuggestions: function($form) { 100 if ($form.find('.engageai-suggestions').length > 0) { 101 return; // Already added 102 } 103 104 const suggestions = [ 105 'Thanks for sharing this!', 106 'This is really helpful.', 107 'I have a similar experience...', 108 'Great point!', 109 'I\'d love to hear more about this.' 110 ]; 111
112 const $suggestionsContainer = $('<div class="engageai-suggestions">'); 113 const $suggestionsTitle = $('<div class="suggestions-title">Quick responses:</div>'); 114 const $suggestionsList = $('<div class="suggestions-list">'); 115 116 suggestions.forEach(function(suggestion) { 117 const $suggestion = $('<button type="button" class="suggestion-btn">' + suggestion + '</button>'); 118 $suggestion.on('click', function() { 119 const $textarea = $form.find('textarea'); 120 const currentText = $textarea.val(); 121 const newText = currentText ? currentText + ' ' + suggestion : suggestion; 122 $textarea.val(newText).focus(); 123 }); 124 $suggestionsList.append($suggestion); 125 }); 126 127 $suggestionsContainer.append($suggestionsTitle, $suggestionsList); 128 $form.find('textarea').after($suggestionsContainer); 129 }, 130 131 // Track comment submissions 132 trackCommentSubmission: function(e) { 133 const $form = $(this); 134 const $activityItem = $form.closest('.activity-item'); 135 136 if ($activityItem.hasClass('engageai-automated')) { 137 const automationType = $activityItem.data('automation-type'); 138 139 EngageAI_Frontend.trackEvent('comment_on_automated_content', { 140 automation_type: automationType, 141 activity_id: $activityItem.data('activity-id') 142 }); 143 } 144 }, 145 146 // Track link clicks 147 trackLinkClick: function(e) { 148 const $link = $(this); 149 const $activityItem = $link.closest('.activity-item'); 150 151 if ($activityItem.hasClass('engageai-automated')) { 152 EngageAI_Frontend.trackEvent('link_clicked_in_automated_content', { 153 url: $link.attr('href'), 154 automation_type: $activityItem.data('automation-type') 155 }); 156 } 157 }, 158 159 // Track reply clicks 160 trackReplyClick: function(e) { 161 const $button = $(this); 162 const $activityItem = $button.closest('.activity-item'); 163 164 if ($activityItem.hasClass('engageai-automated')) { 165 EngageAI_Frontend.trackEvent('reply_to_automated_content', { 166 automation_type: $activityItem.data('automation-type') 167 }); 168 } 169 }, 170 171 // Show points details 172 showPointsDetails: function(e) { 173 e.preventDefault(); 174 175 const $points = $(this); 176 const points = $points.data('points'); 177 const reason = $points.data('reason'); 178 179 EngageAI_Frontend.showTooltip($points, `+${points} points for ${reason}`); 180 }, 181 182 // Show badge details 183 showBadgeDetails: function(e) { 184 e.preventDefault(); 185 186 const $badge = $(this); 187 const badgeName = $badge.data('badge-name'); 188 const description = $badge.data('description'); 189 190 EngageAI_Frontend.showTooltip($badge, `${badgeName}: ${description}`); 191 }, 192 193 // Handle infinite scroll 194 handleInfiniteScroll: function() { 195 if ($(window).scrollTop() + $(window).height() >= $(document).height() - 100) {
196 EngageAI_Frontend.loadMoreActivities(); 197 } 198 }, 199 200 // Load more activities 201 loadMoreActivities: function() { 202 if (this.loadingMore) return; 203 204 this.loadingMore = true; 205 206 // Implementation would depend on BuddyBoss's activity loading system 207 // This is a placeholder for the infinite scroll functionality 208 209 setTimeout(() => { 210 this.loadingMore = false; 211 }, 1000); 212 }, 213 214 // Initialize lazy loading for images 215 initLazyLoading: function() { 216 if ('IntersectionObserver' in window) { 217 const imageObserver = new IntersectionObserver((entries, observer) => { 218 entries.forEach(entry => { 219 if (entry.isIntersecting) { 220 const img = entry.target; 221 img.src = img.dataset.src; 222 img.classList.remove('lazy'); 223 imageObserver.unobserve(img); 224 } 225 }); 226 }); 227 228 document.querySelectorAll('img[data-src]').forEach(img => { 229 imageObserver.observe(img); 230 }); 231 } 232 }, 233 234 // Initialize animations 235 initAnimations: function() { 236 // Animate elements when they come into view 237 if ('IntersectionObserver' in window) { 238 const animationObserver = new IntersectionObserver((entries) => { 239 entries.forEach(entry => { 240 if (entry.isIntersecting) { 241 entry.target.classList.add('animate-in'); 242 } 243 }); 244 }, { 245 threshold: 0.1 246 }); 247 248 document.querySelectorAll('.engageai-welcome-message, .engageai-discussion-prompt, .engageai-motivational-content').forEach(el => { 249 animationObserver.observe(el); 250 }); 251 } 252 }, 253 254 // Initialize tooltips 255 initTooltips: function() { 256 $('[data-tooltip]').each(function() { 257 const $element = $(this); 258 const tooltipText = $element.data('tooltip'); 259 260 $element.on('mouseenter', function() { 261 EngageAI_Frontend.showTooltip($element, tooltipText); 262 }); 263 264 $element.on('mouseleave', function() { 265 EngageAI_Frontend.hideTooltip(); 266 }); 267 }); 268 }, 269 270 // Initialize accessibility features 271 initAccessibility: function() { 272 // Add ARIA labels to automated content 273 $('.engageai-automated').each(function() { 274 const $item = $(this); 275 const automationType = $item.data('automation-type'); 276 277 $item.attr('aria-label', `Automated ${automationType} content`); 278 }); 279 280 // Add keyboard navigation 281 $(document).on('keydown', '.engageai-automated', function(e) { 282 if (e.key === 'Enter' || e.key === ' ') { 283 e.preventDefault(); 284 $(this).trigger('click'); 285 } 286 }); 287 }, 288 289 // Initialize real-time notifications 290 initNotifications: function() { 291 if ('Notification' in window) { 292 // Request permission for notifications 293 if (Notification.permission === 'default') { 294 Notification.requestPermission(); 295 } 296 } 297 298 // Set up event source for real-time updates (if available) 299 this.setupEventSource(); 300 }, 301 302 // Setup event source for real-time updates 303 setupEventSource: function() { 304 // This would connect to a server-sent events endpoint 305 // for real-time notifications about new automated content 306 // Implementation depends on server setup 307 }, 308 309 // Show tooltip 310 showTooltip: function($element, text) { 311 this.hideTooltip(); 312 313 const $tooltip = $('<div class="engageai-tooltip">
313' + text + '</div>'); 314 $('body').append($tooltip); 315 316 const elementOffset = $element.offset(); 317 const elementHeight = $element.outerHeight(); 318 319 $tooltip.css({ 320 top: elementOffset.top + elementHeight + 10, 321 left: elementOffset.left + ($element.outerWidth() / 2) - ($tooltip.outerWidth() / 2) 322 }).fadeIn(200); 323 324 this.currentTooltip = $tooltip; 325 }, 326 327 // Hide tooltip 328 hideTooltip: function() { 329 if (this.currentTooltip) { 330 this.currentTooltip.fadeOut(200, function() { 331 $(this).remove(); 332 }); 333 this.currentTooltip = null; 334 } 335 }, 336 337 // Track events 338 trackEvent: function(eventType, data) { 339 if (typeof gtag !== 'undefined') { 340 gtag('event', eventType, { 341 custom_parameter: data 342 }); 343 } 344 345 // Send to backend for internal analytics 346 $.ajax({ 347 url: engageai_frontend.ajax_url, 348 type: 'POST', 349 data: { 350 action: 'engageai_track_event', 351 event_type: eventType, 352 event_data: data, 353 nonce: engageai_frontend.nonce 354 } 355 }); 356 }, 357 358 // Load stored data 359 loadStoredData: function() { 360 // Load user preferences and engagement history 361 const preferences = localStorage.getItem('engageai_preferences'); 362 if (preferences) { 363 this.preferences = JSON.parse(preferences); 364 } 365 }, 366 367 // Save user preferences 368 savePreferences: function() { 369 localStorage.setItem('engageai_preferences', JSON.stringify(this.preferences)); 370 }, 371 372 // Show notification 373 showNotification: function(title, message, type = 'info') { 374 if ('Notification' in window && Notification.permission === 'granted') { 375 new Notification(title, { 376 body: message, 377 icon: engageai_frontend.plugin_url + 'assets/images/icon.png' 378 }); 379 } else { 380 // Fallback to in-page notification 381 this.showInPageNotification(title, message, type); 382 } 383 }, 384 385 // Show in-page notification 386 showInPageNotification: function(title, message, type) { 387 const $notification = $(` 388 <div class="engageai-notification ${type}"> 389 <div class="notification-header"> 390 <strong>${title}</strong> 391 <button class="notification-close">×</button> 392 </div> 393 <div class="notification-body">${message}</div> 394 </div> 395 `); 396 397 $('body').append($notification); 398 399 $notification.slideDown(300); 400 401 // Auto-hide after 5 seconds 402 setTimeout(() => { 403 $notification.slideUp(300, function() { 404 $(this).remove(); 405 }); 406 }, 5000); 407 408 // Manual close 409 $notification.find('.notification-close').on('click', function() { 410 $notification.slideUp(300, function() { 411 $(this).remove(); 412 }); 413 }); 414 }, 415 416 // Utilities 417 utils: { 418 // Debounce function 419 debounce: function(func, wait, immediate) { 420 let timeout; 421 return function executedFunction() { 422 const context = this; 423 const args = arguments; 424 const later = function() { 425 timeout = null; 426 if (!immediate) func.apply(context, args); 427 }; 428 const callNow = immediate && !timeout; 429 clearTimeout(timeout); 430 timeout = setTimeout(later, wait);
431 if (callNow) func.apply(context, args); 432 }; 433 }, 434 435 // Format time ago 436 timeAgo: function(date) { 437 const now = new Date(); 438 const diff = now - new Date(date); 439 const seconds = Math.floor(diff / 1000); 440 const minutes = Math.floor(seconds / 60); 441 const hours = Math.floor(minutes / 60); 442 const days = Math.floor(hours / 24); 443 444 if (days > 0) return `${days}d ago`; 445 if (hours > 0) return `${hours}h ago`; 446 if (minutes > 0) return `${minutes}m ago`; 447 return 'Just now'; 448 } 449 }, 450 451 // Initialize properties 452 loadingMore: false, 453 currentTooltip: null, 454 preferences: {} 455 }; 456 457 // Initialize when document is ready 458 $(document).ready(function() { 459 EngageAI_Frontend.init(); 460 }); 461 462 // Make EngageAI_Frontend globally available 463 window.EngageAI_Frontend = EngageAI_Frontend; 464 465})(jQuery); 466 467// CSS for tooltips and notifications 468const engageaiStyles = ` 469<style> 470.engageai-tooltip { 471 position: absolute; 472 background: rgba(0,0,0,0.9); 473 color: white; 474 padding: 8px 12px; 475 border-radius: 4px; 476 font-size: 12px; 477 white-space: nowrap; 478 z-index: 10000; 479 display: none; 480} 481 482.engageai-notification { 483 position: fixed; 484 top: 20px; 485 right: 20px; 486 background: white; 487 border-radius: 8px; 488 box-shadow: 0 4px 12px rgba(0,0,0,0.15); 489 max-width: 300px; 490 z-index: 9999; 491 display: none; 492} 493 494.engageai-notification.info { border-left: 4px solid #2271b1; } 495.engageai-notification.success { border-left: 4px solid #46b450; } 496.engageai-notification.warning { border-left: 4px solid #ffb900; } 497.engageai-notification.error { border-left: 4px solid #dc3232; } 498 499.notification-header { 500 padding: 12px 16px 8px; 501 display: flex; 502 justify-content: space-between; 503 align-items: center; 504 border-bottom: 1px solid #eee; 505} 506 507.notification-close { 508 background: none; 509 border: none; 510 font-size: 18px; 511 cursor: pointer; 512 color: #666; 513} 514 515.notification-body { 516 padding: 8px 16px 12px; 517 font-size: 14px; 518 color: #666; 519} 520 521.engageai-suggestions { 522 margin-top: 10px; 523 padding: 10px; 524 background: #f9f9f9; 525 border-radius: 4px; 526} 527 528.suggestions-title { 529 font-size: 12px; 530 color: #666; 531 margin-bottom: 8px; 532} 533 534.suggestions-list { 535 display: flex; 536 flex-wrap: wrap; 537 gap: 5px; 538} 539 540.suggestion-btn { 541 background: #e7f3ff; 542 border: 1px solid #cee4fd; 543 color: #0073aa; 544 padding: 4px 8px; 545 border-radius: 12px; 546 font-size: 11px; 547 cursor: pointer; 548 transition: background-color 0.2s; 549} 550 551.suggestion-btn:hover { 552 background: #cee4fd; 553} 554 555.animate-in { 556 animation: slideInUp 0.5s ease-out; 557} 558 559@keyframes slideInUp { 560 from { 561 opacity: 0; 562 transform: translateY(20px); 563 } 564 to { 565 opacity: 1; 566 transform: translateY(0); 567 } 568} 569</style> 570`; 571 572// Inject styles 573if (document.head) { 574 document.head.insertAdjacentHTML('beforeend', engageaiStyles); 575} else { 576 document.addEventListener('DOMContentLoaded', function() { 577 document.head.insertAdjacentHTML('beforeend', engageaiStyles); 578 }); 579}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.