PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 3.16.4
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v3.16.4
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
profile-builder / assets / js / wppb-support-chat.js

wppb-support-chat.js in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 3.16.4, at assets/js/wppb-support-chat.js

209 lines 7.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Support Chat Widget
3 *
4 * @package ProfileBuilder
5 * @since 3.15.4
6 */
7
8 (function() {
9 'use strict';
10
11 // Wait for DOM ready
12 if (document.readyState === 'loading') {
13 document.addEventListener('DOMContentLoaded', init);
14 } else {
15 init();
16 }
17
18 function init() {
19 var widget = document.getElementById('wppb-support-chat-widget');
20 if (!widget || typeof wppbSupportChat === 'undefined') {
21 return;
22 }
23
24 var config = wppbSupportChat;
25 var strings = config.strings;
26
27 // Elements
28 var toggle = widget.querySelector('.wppb-support-chat__toggle');
29 var chatWindow = widget.querySelector('.wppb-support-chat__window');
30 var closeBtn = widget.querySelector('.wppb-support-chat__close');
31 var postsContainer = widget.querySelector('.wppb-support-chat__posts');
32 var loadingContainer = widget.querySelector('.wppb-support-chat__loading');
33 var sectionLabel = widget.querySelector('.wppb-support-chat__section-label');
34 var badge = widget.querySelector('.wppb-support-chat__badge');
35
36 // Set text content from localized strings
37 widget.querySelector('.wppb-support-chat__title').textContent = strings.title;
38 widget.querySelector('.wppb-support-chat__subtitle').textContent = strings.subtitle;
39 widget.querySelector('.wppb-support-chat__loading span').textContent = strings.loading;
40 widget.querySelector('.wppb-support-chat__encourage-title').textContent = strings.encourageTitle;
41 widget.querySelector('.wppb-support-chat__encourage-text').textContent = strings.encourageText;
42 widget.querySelector('.wppb-support-chat__tip-title').textContent = strings.tipTitle;
43 widget.querySelector('.wppb-support-chat__tip-text').textContent = strings.tipText;
44 widget.querySelector('.wppb-support-chat__btn--primary span').textContent = strings.askQuestion;
45 widget.querySelector('.wppb-support-chat__btn--secondary span').textContent = strings.viewAll;
46
47 // Set section label
48 if (sectionLabel) {
49 sectionLabel.textContent = strings.subtitle;
50 }
51
52 // State
53 var isOpen = false;
54 var postsLoaded = false;
55
56 // Show widget
57 widget.style.display = 'block';
58
59 // Initialize badge based on server-computed new count.
60 var newCount = parseInt(config.newCount, 10) || 0;
61 updateBadge(newCount);
62
63 // Add attention animation after a delay only if there are new posts.
64 if (newCount > 0) {
65 setTimeout(function() {
66 toggle.classList.add('wppb-support-chat__toggle--attention');
67 }, 2000);
68 }
69
70 // Toggle chat window
71 toggle.addEventListener('click', function(e) {
72 e.preventDefault();
73 isOpen = !isOpen;
74 widget.classList.toggle('wppb-support-chat--open', isOpen);
75 toggle.classList.remove('wppb-support-chat__toggle--attention');
76
77 if (isOpen) {
78 // Mark posts as read and hide badge.
79 markPostsAsRead();
80 updateBadge(0);
81
82 if (!postsLoaded) {
83 loadPosts();
84 }
85 }
86 });
87
88 // Close button
89 closeBtn.addEventListener('click', function(e) {
90 e.preventDefault();
91 e.stopPropagation();
92 isOpen = false;
93 widget.classList.remove('wppb-support-chat--open');
94 });
95
96 // Close chat when clicking outside
97 document.addEventListener('click', function(e) {
98 if (isOpen && !widget.contains(e.target)) {
99 isOpen = false;
100 widget.classList.remove('wppb-support-chat--open');
101 }
102 });
103
104 // Prevent closing when clicking inside chat window
105 chatWindow.addEventListener('click', function(e) {
106 e.stopPropagation();
107 });
108
109 // Keyboard navigation
110 document.addEventListener('keydown', function(e) {
111 if (e.key === 'Escape' && isOpen) {
112 isOpen = false;
113 widget.classList.remove('wppb-support-chat--open');
114 toggle.focus();
115 }
116 });
117
118 function updateBadge(count) {
119 if (count > 0) {
120 badge.textContent = count;
121 badge.style.display = '';
122 badge.classList.add('wppb-support-chat__badge--pulse');
123 } else {
124 badge.textContent = '';
125 badge.style.display = 'none';
126 badge.classList.remove('wppb-support-chat__badge--pulse');
127 }
128 }
129
130 function markPostsAsRead() {
131 var formData = new FormData();
132 formData.append('action', 'wppb_mark_forum_posts_read');
133 formData.append('nonce', config.nonce);
134
135 fetch(config.ajaxUrl, {
136 method: 'POST',
137 credentials: 'same-origin',
138 body: formData
139 });
140 }
141
142 function loadPosts() {
143 var formData = new FormData();
144 formData.append('action', 'wppb_get_forum_posts');
145 formData.append('nonce', config.nonce);
146
147 fetch(config.ajaxUrl, {
148 method: 'POST',
149 credentials: 'same-origin',
150 body: formData
151 })
152 .then(function(response) {
153 return response.json();
154 })
155 .then(function(data) {
156 if (data.success && data.data.posts) {
157 renderPosts(data.data.posts);
158 postsLoaded = true;
159 } else {
160 showError(data.data ? data.data.message : strings.error);
161 }
162 })
163 .catch(function(error) {
164 console.error('Support Chat Error:', error);
165 showError(strings.error);
166 });
167 }
168
169 function renderPosts(posts) {
170 if (!posts.length) {
171 showError(strings.error);
172 return;
173 }
174
175 var html = '';
176 posts.forEach(function(post) {
177 html += '<a href="' + escapeHtml(post.link) + '" target="_blank" rel="noopener" class="wppb-support-chat__post">';
178 html += '<h5 class="wppb-support-chat__post-title">' + escapeHtml(post.title) + '</h5>';
179 html += '<div class="wppb-support-chat__post-meta">';
180 if (post.author) {
181 html += '<span class="wppb-support-chat__post-author">' + strings.postedBy + ' ' + escapeHtml(post.author) + '</span>';
182 }
183 if (post.date) {
184 html += '<span class="wppb-support-chat__post-date">' + escapeHtml(post.date) + '</span>';
185 }
186 html += '</div>';
187 html += '</a>';
188 });
189
190 postsContainer.innerHTML = html;
191 postsContainer.classList.add('wppb-support-chat__posts--loaded');
192 loadingContainer.classList.add('wppb-support-chat__loading--hidden');
193 }
194
195 function showError(message) {
196 loadingContainer.innerHTML = '<div class="wppb-support-chat__error">' +
197 '<div class="wppb-support-chat__error-icon">!</div>' +
198 '<p>' + escapeHtml(message) + '</p>' +
199 '</div>';
200 }
201
202 function escapeHtml(text) {
203 var div = document.createElement('div');
204 div.textContent = text;
205 return div.innerHTML;
206 }
207 }
208 })();
209