PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / faq-chat / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/faq-chat/frontend.js

313 lines 11.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 'use strict';
3
4 /* ── typography CSS-var helper ── */
5 var _tKeys = ['font-family','font-weight','text-transform','font-style','text-decoration'];
6 var _rKeys = ['font-size','line-height','letter-spacing','word-spacing'];
7 function typoCssVarsForEl(typo, prefix, el) {
8 if (!typo || typeof typo !== 'object') return;
9 var map = {
10 family:'font-family', weight:'font-weight', transform:'text-transform',
11 style:'font-style', decoration:'text-decoration'
12 };
13 var rMap = {
14 sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
15 lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
16 letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
17 wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
18 };
19 Object.keys(map).forEach(function (k) {
20 if (typo[k]) el.style.setProperty(prefix + map[k], typo[k]);
21 });
22 Object.keys(rMap).forEach(function (k) {
23 var v = typo[k];
24 if (v !== undefined && v !== '') {
25 var unit = '';
26 if (k.indexOf('size') === 0) unit = typo.sizeUnit || 'px';
27 else if (k.indexOf('lineHeight') === 0) unit = typo.lineHeightUnit || '';
28 else if (k.indexOf('letterSpacing') === 0) unit = typo.letterSpacingUnit || 'px';
29 else if (k.indexOf('wordSpacing') === 0) unit = typo.wordSpacingUnit || 'px';
30 el.style.setProperty(prefix + rMap[k], v + unit);
31 }
32 });
33 }
34
35 /* ── helpers ── */
36 function timestamp() {
37 var now = new Date();
38 var h = now.getHours();
39 var m = now.getMinutes();
40 var ampm = h >= 12 ? 'PM' : 'AM';
41 h = h % 12 || 12;
42 return h + ':' + (m < 10 ? '0' : '') + m + ' ' + ampm;
43 }
44
45 function scrollBottom(el) {
46 el.scrollTop = el.scrollHeight;
47 }
48
49 /* ── build avatar element ── */
50 function makeAvatar(botAvatar, avatarBg) {
51 var av = document.createElement('div');
52 av.className = 'bkbg-faqc-row-avatar';
53 av.style.background = avatarBg;
54 av.textContent = botAvatar;
55 return av;
56 }
57
58 /* ── append a bot message row ── */
59 function appendBotRow(messagesEl, text, o, isBubble, extraClass) {
60 var row = document.createElement('div');
61 row.className = 'bkbg-faqc-row bot-row';
62 if (extraClass) row.className += ' ' + extraClass;
63
64 var av = makeAvatar(o.botAvatar, o.avatarBg);
65
66 var right = document.createElement('div');
67
68 if (o.showTimestamp) {
69 var meta = document.createElement('div');
70 meta.className = 'bkbg-faqc-meta';
71 meta.style.color = o.timestampColor;
72 meta.textContent = o.botName + ' ' + timestamp();
73 right.appendChild(meta);
74 }
75
76 var bubble = document.createElement('div');
77 bubble.className = 'bkbg-faqc-bubble bot-bubble';
78 bubble.style.background = o.botBubbleBg;
79 bubble.style.color = o.botBubbleColor;
80
81 if (isBubble) {
82 /* typing indicator */
83 bubble.className += ' bkbg-faqc-typing';
84 ['bg1', 'bg2', 'bg3'].forEach(function () {
85 var dot = document.createElement('span');
86 dot.className = 'bkbg-faqc-dot';
87 dot.style.background = o.botBubbleColor;
88 bubble.appendChild(dot);
89 });
90 } else {
91 bubble.textContent = text;
92 }
93
94 right.appendChild(bubble);
95 row.appendChild(av);
96 row.appendChild(right);
97
98 messagesEl.appendChild(row);
99 scrollBottom(messagesEl);
100
101 return { row: row, bubble: bubble };
102 }
103
104 /* ── append a user message row ── */
105 function appendUserRow(messagesEl, text, o) {
106 var row = document.createElement('div');
107 row.className = 'bkbg-faqc-row user-row';
108
109 var left = document.createElement('div');
110
111 if (o.showTimestamp) {
112 var meta = document.createElement('div');
113 meta.className = 'bkbg-faqc-meta';
114 meta.style.color = o.timestampColor;
115 meta.textContent = 'You ' + timestamp();
116 left.appendChild(meta);
117 }
118
119 var bubble = document.createElement('div');
120 bubble.className = 'bkbg-faqc-bubble user-bubble';
121 bubble.style.background = o.userBubbleBg;
122 bubble.style.color = o.userBubbleColor;
123 bubble.textContent = text;
124
125 left.appendChild(bubble);
126 row.appendChild(left);
127
128 messagesEl.appendChild(row);
129 scrollBottom(messagesEl);
130 }
131
132 /* ── bubble radius by style ── */
133 function bubbleRadius(chatStyle) {
134 if (chatStyle === 'bubble') return '20px';
135 if (chatStyle === 'sharp') return '4px';
136 return '12px';
137 }
138
139 /* ── build full widget DOM ── */
140 function buildWidget(o) {
141 var widget = document.createElement('div');
142 widget.className = 'bkbg-faqc-widget bkbg-faqc-style-' + o.chatStyle;
143 widget.style.borderColor = o.borderColor;
144 widget.style.borderRadius = o.borderRadius + 'px';
145 widget.style.maxWidth = o.maxWidth + 'px';
146 widget.style.margin = '0 auto';
147
148 /* set typography CSS custom properties */
149 typoCssVarsForEl(o.typoQuestion, '--bkbg-fcht-qt-', widget);
150 typoCssVarsForEl(o.typoAnswer, '--bkbg-fcht-an-', widget);
151
152 /* ── header ── */
153 var header = document.createElement('div');
154 header.className = 'bkbg-faqc-header';
155 header.style.background = o.headerBg;
156
157 var av = document.createElement('div');
158 av.className = 'bkbg-faqc-avatar';
159 av.style.background = 'rgba(255,255,255,0.18)';
160 av.textContent = o.botAvatar;
161
162 var info = document.createElement('div');
163
164 var botName = document.createElement('div');
165 botName.className = 'bkbg-faqc-bot-name';
166 botName.style.color = o.headerColor;
167 botName.textContent = o.botName;
168
169 var statusWrap = document.createElement('div');
170 statusWrap.className = 'bkbg-faqc-status';
171 statusWrap.style.color = o.headerColor;
172
173 var dot = document.createElement('span');
174 dot.className = 'bkbg-faqc-status-dot';
175
176 var statusTxt = document.createElement('span');
177 statusTxt.textContent = 'Online · ' + o.faqs.length + ' topics';
178
179 statusWrap.appendChild(dot);
180 statusWrap.appendChild(statusTxt);
181 info.appendChild(botName);
182 info.appendChild(statusWrap);
183 header.appendChild(av);
184 header.appendChild(info);
185
186 /* ── messages area ── */
187 var messages = document.createElement('div');
188 messages.className = 'bkbg-faqc-messages';
189 messages.style.background = o.chatBg;
190 messages.style.maxHeight = o.maxHeight + 'px';
191
192 /* ── questions panel ── */
193 var questionsPanel = document.createElement('div');
194 questionsPanel.className = 'bkbg-faqc-questions';
195 questionsPanel.style.background = o.questionsBg;
196 questionsPanel.style.borderTopColor = o.borderColor;
197
198 var qLabel = document.createElement('div');
199 qLabel.className = 'bkbg-faqc-questions-label';
200 qLabel.style.color = o.timestampColor;
201 qLabel.textContent = '📋 Frequently Asked Questions';
202
203 var btnsWrap = document.createElement('div');
204 btnsWrap.className = 'bkbg-faqc-btns';
205
206 widget.appendChild(header);
207 widget.appendChild(messages);
208 questionsPanel.appendChild(qLabel);
209 questionsPanel.appendChild(btnsWrap);
210 widget.appendChild(questionsPanel);
211
212 /* ── helpers for inline bubble styling ── */
213 var bR = bubbleRadius(o.chatStyle);
214
215 /* ── initial bot greeting ── */
216 var initRow = appendBotRow(messages, o.initialMessage || 'Hi! Click any question.', o, false);
217 initRow.bubble.style.borderRadius = bR;
218
219 /* ── wire question buttons ── */
220 o.faqs.forEach(function (faq, idx) {
221 var btn = document.createElement('button');
222 btn.className = 'bkbg-faqc-q-btn';
223 btn.style.background = o.btnBg;
224 btn.style.color = o.btnColor;
225 btn.style.borderColor = o.btnBorder;
226 btn.textContent = faq.question;
227
228 btn.addEventListener('click', function () {
229 if (btn.classList.contains('is-answered')) return;
230 btn.classList.add('is-answered');
231 btn.style.background = o.btnActiveBg;
232 btn.style.color = o.btnActiveColor;
233 btn.style.borderColor = o.btnActiveBg;
234
235 /* user question bubble */
236 appendUserRow(messages, faq.question, o);
237 var userBubble = messages.querySelector('.user-row:last-child .user-bubble');
238 if (userBubble) userBubble.style.borderRadius = bR;
239
240 /* typing indicator */
241 var typingRes = appendBotRow(messages, '', o, true);
242 typingRes.bubble.style.borderRadius = bR;
243
244 /* after delay → show answer */
245 setTimeout(function () {
246 if (typingRes.row.parentNode) {
247 typingRes.row.parentNode.removeChild(typingRes.row);
248 }
249
250 var answerRes = appendBotRow(messages, faq.answer, o, false);
251 answerRes.bubble.style.borderRadius = bR;
252
253 /* scroll again after answer appears */
254 scrollBottom(messages);
255 }, o.typingDelay || 700);
256 });
257
258 btnsWrap.appendChild(btn);
259 });
260
261 return widget;
262 }
263
264 /* ── main init ── */
265 function init() {
266 document.querySelectorAll('.bkbg-faqc-app').forEach(function (appEl) {
267 if (appEl.dataset.rendered) return;
268 appEl.dataset.rendered = '1';
269
270 var opts;
271 try { opts = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { opts = {}; }
272
273 var o = Object.assign({
274 faqs: [],
275 botName: 'Support Bot',
276 botAvatar: '🤖',
277 avatarBg: '#6366f1',
278 initialMessage: 'Hi! 👋 Click any question below to get started.',
279 showTimestamp: true,
280 typingDelay: 700,
281 chatStyle: 'rounded',
282 maxWidth: 620,
283 maxHeight: 500,
284 borderRadius: 16,
285 questionSize: 14,
286 answerSize: 14,
287 headerBg: '#6366f1',
288 headerColor: '#ffffff',
289 chatBg: '#f1f5f9',
290 botBubbleBg: '#ffffff',
291 botBubbleColor: '#1f2937',
292 userBubbleBg: '#6366f1',
293 userBubbleColor: '#ffffff',
294 questionsBg: '#ffffff',
295 btnBg: '#f3f4f6',
296 btnColor: '#374151',
297 btnBorder: '#e5e7eb',
298 btnActiveBg: '#e0e7ff',
299 btnActiveColor: '#4338ca',
300 borderColor: '#e5e7eb',
301 timestampColor: '#9ca3af'
302 }, opts);
303
304 var widget = buildWidget(o);
305 appEl.parentNode.insertBefore(widget, appEl);
306 appEl.style.display = 'none';
307 });
308 }
309
310 if (document.readyState !== 'loading') { init(); }
311 else { document.addEventListener('DOMContentLoaded', init); }
312 })();
313