# blockenberg/2.0.7/blocks/faq-chat/frontend.js

Blockenberg — 600+ Advanced Gutenberg Blocks &amp; AI Agent for WordPress Block Editor, version 2.0.7. 313 lines.

- Page: https://pluginprobe.com/plugins/blockenberg/2.0.7/code/blocks/faq-chat/frontend.js
- Raw: https://pluginprobe.com/plugins/blockenberg/2.0.7/raw/blocks/faq-chat/frontend.js
- Modified: 2026-03-06T12:32:40+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/blockenberg/2.0.7/code/blocks/faq-chat/frontend.js#L10-L20`.

```javascript
(function () {
    'use strict';

    /* ── typography CSS-var helper ── */
    var _tKeys = ['font-family','font-weight','text-transform','font-style','text-decoration'];
    var _rKeys = ['font-size','line-height','letter-spacing','word-spacing'];
    function typoCssVarsForEl(typo, prefix, el) {
        if (!typo || typeof typo !== 'object') return;
        var map = {
            family:'font-family', weight:'font-weight', transform:'text-transform',
            style:'font-style', decoration:'text-decoration'
        };
        var rMap = {
            sizeDesktop:'font-size-d', sizeTablet:'font-size-t', sizeMobile:'font-size-m',
            lineHeightDesktop:'line-height-d', lineHeightTablet:'line-height-t', lineHeightMobile:'line-height-m',
            letterSpacingDesktop:'letter-spacing-d', letterSpacingTablet:'letter-spacing-t', letterSpacingMobile:'letter-spacing-m',
            wordSpacingDesktop:'word-spacing-d', wordSpacingTablet:'word-spacing-t', wordSpacingMobile:'word-spacing-m'
        };
        Object.keys(map).forEach(function (k) {
            if (typo[k]) el.style.setProperty(prefix + map[k], typo[k]);
        });
        Object.keys(rMap).forEach(function (k) {
            var v = typo[k];
            if (v !== undefined && v !== '') {
                var unit = '';
                if (k.indexOf('size') === 0) unit = typo.sizeUnit || 'px';
                else if (k.indexOf('lineHeight') === 0) unit = typo.lineHeightUnit || '';
                else if (k.indexOf('letterSpacing') === 0) unit = typo.letterSpacingUnit || 'px';
                else if (k.indexOf('wordSpacing') === 0) unit = typo.wordSpacingUnit || 'px';
                el.style.setProperty(prefix + rMap[k], v + unit);
            }
        });
    }

    /* ── helpers ── */
    function timestamp() {
        var now = new Date();
        var h = now.getHours();
        var m = now.getMinutes();
        var ampm = h >= 12 ? 'PM' : 'AM';
        h = h % 12 || 12;
        return h + ':' + (m < 10 ? '0' : '') + m + ' ' + ampm;
    }

    function scrollBottom(el) {
        el.scrollTop = el.scrollHeight;
    }

    /* ── build avatar element ── */
    function makeAvatar(botAvatar, avatarBg) {
        var av = document.createElement('div');
        av.className = 'bkbg-faqc-row-avatar';
        av.style.background = avatarBg;
        av.textContent = botAvatar;
        return av;
    }

    /* ── append a bot message row ── */
    function appendBotRow(messagesEl, text, o, isBubble, extraClass) {
        var row = document.createElement('div');
        row.className = 'bkbg-faqc-row bot-row';
        if (extraClass) row.className += ' ' + extraClass;

        var av = makeAvatar(o.botAvatar, o.avatarBg);

        var right = document.createElement('div');

        if (o.showTimestamp) {
            var meta = document.createElement('div');
            meta.className = 'bkbg-faqc-meta';
            meta.style.color = o.timestampColor;
            meta.textContent = o.botName + '  ' + timestamp();
            right.appendChild(meta);
        }

        var bubble = document.createElement('div');
        bubble.className = 'bkbg-faqc-bubble bot-bubble';
        bubble.style.background = o.botBubbleBg;
        bubble.style.color = o.botBubbleColor;

        if (isBubble) {
            /* typing indicator */
            bubble.className += ' bkbg-faqc-typing';
            ['bg1', 'bg2', 'bg3'].forEach(function () {
                var dot = document.createElement('span');
                dot.className = 'bkbg-faqc-dot';
                dot.style.background = o.botBubbleColor;
                bubble.appendChild(dot);
            });
        } else {
            bubble.textContent = text;
        }

        right.appendChild(bubble);
        row.appendChild(av);
        row.appendChild(right);

        messagesEl.appendChild(row);
        scrollBottom(messagesEl);

        return { row: row, bubble: bubble };
    }

    /* ── append a user message row ── */
    function appendUserRow(messagesEl, text, o) {
        var row = document.createElement('div');
        row.className = 'bkbg-faqc-row user-row';

        var left = document.createElement('div');

        if (o.showTimestamp) {
            var meta = document.createElement('div');
            meta.className = 'bkbg-faqc-meta';
            meta.style.color = o.timestampColor;
            meta.textContent = 'You  ' + timestamp();
            left.appendChild(meta);
        }

        var bubble = document.createElement('div');
        bubble.className = 'bkbg-faqc-bubble user-bubble';
        bubble.style.background = o.userBubbleBg;
        bubble.style.color = o.userBubbleColor;
        bubble.textContent = text;

        left.appendChild(bubble);
        row.appendChild(left);

        messagesEl.appendChild(row);
        scrollBottom(messagesEl);
    }

    /* ── bubble radius by style ── */
    function bubbleRadius(chatStyle) {
        if (chatStyle === 'bubble')  return '20px';
        if (chatStyle === 'sharp')   return '4px';
        return '12px';
    }

    /* ── build full widget DOM ── */
    function buildWidget(o) {
        var widget = document.createElement('div');
        widget.className = 'bkbg-faqc-widget bkbg-faqc-style-' + o.chatStyle;
        widget.style.borderColor = o.borderColor;
        widget.style.borderRadius = o.borderRadius + 'px';
        widget.style.maxWidth = o.maxWidth + 'px';
        widget.style.margin = '0 auto';

        /* set typography CSS custom properties */
        typoCssVarsForEl(o.typoQuestion, '--bkbg-fcht-qt-', widget);
        typoCssVarsForEl(o.typoAnswer, '--bkbg-fcht-an-', widget);

        /* ── header ── */
        var header = document.createElement('div');
        header.className = 'bkbg-faqc-header';
        header.style.background = o.headerBg;

        var av = document.createElement('div');
        av.className = 'bkbg-faqc-avatar';
        av.style.background = 'rgba(255,255,255,0.18)';
        av.textContent = o.botAvatar;

        var info = document.createElement('div');

        var botName = document.createElement('div');
        botName.className = 'bkbg-faqc-bot-name';
        botName.style.color = o.headerColor;
        botName.textContent = o.botName;

        var statusWrap = document.createElement('div');
        statusWrap.className = 'bkbg-faqc-status';
        statusWrap.style.color = o.headerColor;

        var dot = document.createElement('span');
        dot.className = 'bkbg-faqc-status-dot';

        var statusTxt = document.createElement('span');
        statusTxt.textContent = 'Online · ' + o.faqs.length + ' topics';

        statusWrap.appendChild(dot);
        statusWrap.appendChild(statusTxt);
        info.appendChild(botName);
        info.appendChild(statusWrap);
        header.appendChild(av);
        header.appendChild(info);

        /* ── messages area ── */
        var messages = document.createElement('div');
        messages.className = 'bkbg-faqc-messages';
        messages.style.background = o.chatBg;
        messages.style.maxHeight = o.maxHeight + 'px';

        /* ── questions panel ── */
        var questionsPanel = document.createElement('div');
        questionsPanel.className = 'bkbg-faqc-questions';
        questionsPanel.style.background = o.questionsBg;
        questionsPanel.style.borderTopColor = o.borderColor;

        var qLabel = document.createElement('div');
        qLabel.className = 'bkbg-faqc-questions-label';
        qLabel.style.color = o.timestampColor;
        qLabel.textContent = '📋 Frequently Asked Questions';

        var btnsWrap = document.createElement('div');
        btnsWrap.className = 'bkbg-faqc-btns';

        widget.appendChild(header);
        widget.appendChild(messages);
        questionsPanel.appendChild(qLabel);
        questionsPanel.appendChild(btnsWrap);
        widget.appendChild(questionsPanel);

        /* ── helpers for inline bubble styling ── */
        var bR = bubbleRadius(o.chatStyle);

        /* ── initial bot greeting ── */
        var initRow = appendBotRow(messages, o.initialMessage || 'Hi! Click any question.', o, false);
        initRow.bubble.style.borderRadius = bR;

        /* ── wire question buttons ── */
        o.faqs.forEach(function (faq, idx) {
            var btn = document.createElement('button');
            btn.className = 'bkbg-faqc-q-btn';
            btn.style.background = o.btnBg;
            btn.style.color = o.btnColor;
            btn.style.borderColor = o.btnBorder;
            btn.textContent = faq.question;

            btn.addEventListener('click', function () {
                if (btn.classList.contains('is-answered')) return;
                btn.classList.add('is-answered');
                btn.style.background = o.btnActiveBg;
                btn.style.color = o.btnActiveColor;
                btn.style.borderColor = o.btnActiveBg;

                /* user question bubble */
                appendUserRow(messages, faq.question, o);
                var userBubble = messages.querySelector('.user-row:last-child .user-bubble');
                if (userBubble) userBubble.style.borderRadius = bR;

                /* typing indicator */
                var typingRes = appendBotRow(messages, '', o, true);
                typingRes.bubble.style.borderRadius = bR;

                /* after delay → show answer */
                setTimeout(function () {
                    if (typingRes.row.parentNode) {
                        typingRes.row.parentNode.removeChild(typingRes.row);
                    }

                    var answerRes = appendBotRow(messages, faq.answer, o, false);
                    answerRes.bubble.style.borderRadius = bR;

                    /* scroll again after answer appears */
                    scrollBottom(messages);
                }, o.typingDelay || 700);
            });

            btnsWrap.appendChild(btn);
        });

        return widget;
    }

    /* ── main init ── */
    function init() {
        document.querySelectorAll('.bkbg-faqc-app').forEach(function (appEl) {
            if (appEl.dataset.rendered) return;
            appEl.dataset.rendered = '1';

            var opts;
            try { opts = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { opts = {}; }

            var o = Object.assign({
                faqs: [],
                botName: 'Support Bot',
                botAvatar: '🤖',
                avatarBg: '#6366f1',
                initialMessage: 'Hi! 👋 Click any question below to get started.',
                showTimestamp: true,
                typingDelay: 700,
                chatStyle: 'rounded',
                maxWidth: 620,
                maxHeight: 500,
                borderRadius: 16,
                questionSize: 14,
                answerSize: 14,
                headerBg: '#6366f1',
                headerColor: '#ffffff',
                chatBg: '#f1f5f9',
                botBubbleBg: '#ffffff',
                botBubbleColor: '#1f2937',
                userBubbleBg: '#6366f1',
                userBubbleColor: '#ffffff',
                questionsBg: '#ffffff',
                btnBg: '#f3f4f6',
                btnColor: '#374151',
                btnBorder: '#e5e7eb',
                btnActiveBg: '#e0e7ff',
                btnActiveColor: '#4338ca',
                borderColor: '#e5e7eb',
                timestampColor: '#9ca3af'
            }, opts);

            var widget = buildWidget(o);
            appEl.parentNode.insertBefore(widget, appEl);
            appEl.style.display = 'none';
        });
    }

    if (document.readyState !== 'loading') { init(); }
    else { document.addEventListener('DOMContentLoaded', init); }
})();

```
