# king-addons/51.1.84/includes/extensions/Live_Chat/assets/frontend.js

King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder, version 51.1.84. 896 lines.

- Page: https://pluginprobe.com/plugins/king-addons/51.1.84/code/includes/extensions/Live_Chat/assets/frontend.js
- Raw: https://pluginprobe.com/plugins/king-addons/51.1.84/raw/includes/extensions/Live_Chat/assets/frontend.js
- Modified: 2026-09-21T18:08: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/king-addons/51.1.84/code/includes/extensions/Live_Chat/assets/frontend.js#L10-L20`.

```javascript
/**
 * Live Chat Frontend JavaScript
 *
 * Handles chat widget functionality, messaging, and polling.
 *
 * @package King_Addons
 */

(function() {
    'use strict';

    /**
     * Live Chat Widget Module
     */
    const KingLiveChat = {
        /**
         * Current state
         */
        state: {
            isOpen: false,
            isChatting: false,
            conversationId: null,
            visitorId: null,
            visitorName: '',
            visitorEmail: '',
            messages: [],
            lastMessageId: 0,
            pollInterval: null,
            isLoading: false,
            hasError: false,
            mode: 'live_chat' // live_chat or contact_form
        },

        /**
         * Configuration
         */
        config: {},

        /**
         * Strings for i18n
         */
        strings: {},

        /**
         * DOM elements cache
         */
        elements: {},

        /**
         * Initialize the widget
         */
        init: function() {
            // Get config from global
            if (typeof kingLiveChat === 'undefined') {
                // console.error('KingLiveChat: Config not found');
                return;
            }

            this.config = kingLiveChat;
            this.strings = kingLiveChat.strings || {};
            this.state.visitorId = kingLiveChat.visitorId;
            this.state.mode = kingLiveChat.widgetMode || 'live_chat';

            this.cacheElements();
            
            if (!this.elements.container) {
                // console.error('KingLiveChat: Widget container not found');
                return;
            }

            this.bindEvents();
            this.initVisitorId();
            
            // Only restore session for live chat mode
            if (this.state.mode === 'live_chat') {
                this.restoreSession();
            }
        },

        /**
         * Cache DOM elements
         */
        cacheElements: function() {
            this.elements = {
                container: document.getElementById('ka-live-chat'),
                button: document.querySelector('.ka-live-chat__button'),
                panel: document.querySelector('.ka-live-chat__panel'),
                closeBtn: document.querySelector('.ka-live-chat__close'),
                prechat: document.querySelector('.ka-live-chat__prechat'),
                nameInput: document.getElementById('ka-chat-name'),
                emailInput: document.getElementById('ka-chat-email'),
                startBtn: document.querySelector('.ka-live-chat__start'),
                messagesContainer: document.querySelector('.ka-live-chat__messages'),
                messagesList: document.querySelector('.ka-live-chat__messages-list'),
                inputArea: document.querySelector('.ka-live-chat__input'),
                textarea: document.querySelector('.ka-live-chat__input textarea'),
                sendBtn: document.querySelector('.ka-live-chat__send'),
                badge: document.querySelector('.ka-live-chat__badge'),
                honeypot: document.querySelector('.ka-live-chat__hp input'),
                // Contact Form elements
                contactForm: document.querySelector('.ka-live-chat__contact-form'),
                subjectInput: document.getElementById('ka-chat-subject'),
                messageTextarea: document.getElementById('ka-chat-message'),
                submitBtn: document.querySelector('.ka-live-chat__submit'),
                successScreen: document.querySelector('.ka-live-chat__success'),
                newMessageBtn: document.querySelector('.ka-live-chat__new-message')
            };
        },

        /**
         * Bind event handlers
         */
        bindEvents: function() {
            const self = this;

            // Toggle chat
            this.elements.button.addEventListener('click', function() {
                self.toggle();
            });

            // Close button
            if (this.elements.closeBtn) {
                this.elements.closeBtn.addEventListener('click', function() {
                    self.close();
                });
            }

            // Start chat (Live Chat mode)
            if (this.elements.startBtn) {
                this.elements.startBtn.addEventListener('click', function() {
                    self.startChat();
                });
            }

            // Send message (Live Chat mode)
            if (this.elements.sendBtn) {
                this.elements.sendBtn.addEventListener('click', function() {
                    self.sendMessage();
                });
            }

            // Enter to send (Live Chat mode)
            if (this.elements.textarea) {
                this.elements.textarea.addEventListener('keydown', function(e) {
                    if (e.key === 'Enter' && !e.shiftKey) {
                        e.preventDefault();
                        self.sendMessage();
                    }
                });

                // Auto-resize textarea
                this.elements.textarea.addEventListener('input', function() {
                    this.style.height = 'auto';
                    this.style.height = Math.min(this.scrollHeight, 100) + 'px';
                });
            }

            // Pre-chat form validation (Live Chat mode)
            if (this.elements.nameInput) {
                this.elements.nameInput.addEventListener('input', function() {
                    if (self.state.mode === 'live_chat') {
                        self.validatePrechat();
                    } else {
                        self.validateContactForm();
                    }
                });
            }

            if (this.elements.emailInput) {
                this.elements.emailInput.addEventListener('input', function() {
                    if (self.state.mode === 'live_chat') {
                        self.validatePrechat();
                    } else {
                        self.validateContactForm();
                    }
                });
            }

            // Contact Form mode events
            if (this.elements.submitBtn) {
                this.elements.submitBtn.addEventListener('click', function() {
                    self.submitContactForm();
                });
            }

            if (this.elements.messageTextarea) {
                this.elements.messageTextarea.addEventListener('input', function() {
                    self.validateContactForm();
                });
            }

            if (this.elements.newMessageBtn) {
                this.elements.newMessageBtn.addEventListener('click', function() {
                    self.resetContactForm();
                });
            }

            // Close on escape
            document.addEventListener('keydown', function(e) {
                if (e.key === 'Escape' && self.state.isOpen) {
                    self.close();
                }
            });
        },

        /**
         * Initialize or get visitor ID from cookie
         */
        initVisitorId: function() {
            const cookieName = 'king_support_vid';
            let visitorId = this.getCookie(cookieName);

            if (!visitorId) {
                visitorId = this.state.visitorId || this.generateUUID();
                this.setCookie(cookieName, visitorId, 365);
            }

            this.state.visitorId = visitorId;
        },

        /**
         * Restore session from previous visit
         */
        restoreSession: function() {
            const self = this;
            const sessionData = localStorage.getItem('ka_chat_session');

            // Avoid background REST calls on every page load.
            // Only attempt to restore if a session already exists.
            if (!sessionData) {
                return;
            }

            if (sessionData) {
                try {
                    const data = JSON.parse(sessionData);
                    this.state.visitorName = data.name || '';
                    this.state.visitorEmail = data.email || '';
                    
                    if (data.conversationId) {
                        this.state.conversationId = data.conversationId;
                    }
                } catch (e) {
                    localStorage.removeItem('ka_chat_session');
                }
            }

            // Try to init conversation
            this.initConversation().then(function(data) {
                if (data.conversation_id) {
                    self.state.conversationId = data.conversation_id;
                    self.state.isChatting = true;
                    self.state.messages = data.messages || [];
                    self.state.lastMessageId = data.messages.length ? data.messages[data.messages.length - 1].id : 0;
                    
                    self.elements.container.classList.add('ka-live-chat--chatting');
                    self.renderMessages();
                    self.updateBadge(data.unread || 0);
                }
            });
        },

        /**
         * Toggle chat panel
         */
        toggle: function() {
            if (this.state.isOpen) {
                this.close();
            } else {
                this.open();
            }
        },

        /**
         * Open chat panel
         */
        open: function() {
            this.state.isOpen = true;
            this.elements.container.classList.add('ka-live-chat--open');

            // Mark messages as read
            if (this.state.conversationId) {
                this.markAsRead();
            }

            // Start polling
            this.startPolling();

            // Focus appropriate element
            if (this.state.isChatting) {
                this.elements.textarea.focus();
                this.scrollToBottom();
            } else if (this.elements.nameInput) {
                this.elements.nameInput.focus();
            }
        },

        /**
         * Close chat panel
         */
        close: function() {
            this.state.isOpen = false;
            this.elements.container.classList.remove('ka-live-chat--open');
            this.stopPolling();
        },

        /**
         * Validate pre-chat form
         */
        validatePrechat: function() {
            const requireName = this.config.options.requireName;
            const requireEmail = this.config.options.requireEmail;
            
            let isValid = true;

            if (requireName && this.elements.nameInput) {
                isValid = isValid && this.elements.nameInput.value.trim().length > 0;
            }

            if (requireEmail && this.elements.emailInput) {
                const email = this.elements.emailInput.value.trim();
                isValid = isValid && this.isValidEmail(email);
            }

            this.elements.startBtn.disabled = !isValid;
        },

        /**
         * Start chat (after pre-chat form)
         */
        startChat: function() {
            const self = this;

            // Get values
            if (this.elements.nameInput) {
                this.state.visitorName = this.elements.nameInput.value.trim();
            }
            if (this.elements.emailInput) {
                this.state.visitorEmail = this.elements.emailInput.value.trim();
            }

            // Save to session
            this.saveSession();

            // Show chat UI
            this.state.isChatting = true;
            this.elements.container.classList.add('ka-live-chat--chatting');

            // Add welcome message
            if (this.strings.welcomeMessage) {
                this.addMessage({
                    type: 'welcome',
                    text: this.strings.welcomeMessage,
                    time: new Date().toISOString()
                });
            }

            // Focus textarea
            this.elements.textarea.focus();
        },

        /**
         * Send message
         */
        sendMessage: function() {
            const self = this;
            const text = this.elements.textarea.value.trim();

            if (!text || this.state.isLoading) {
                return;
            }

            // Check honeypot
            if (this.elements.honeypot && this.elements.honeypot.value) {
                // console.warn('KingLiveChat: Spam detected');
                return;
            }

            this.state.isLoading = true;
            this.elements.sendBtn.disabled = true;

            // Optimistically add message
            const tempMsg = {
                id: 'temp-' + Date.now(),
                type: 'visitor',
                text: text,
                time: new Date().toISOString(),
                pending: true
            };
            this.addMessage(tempMsg);
            this.elements.textarea.value = '';
            this.elements.textarea.style.height = 'auto';

            // Send to server
            this.apiRequest('message/send', {
                visitor_id: this.state.visitorId,
                conversation_id: this.state.conversationId,
                message: text,
                name: this.state.visitorName,
                email: this.state.visitorEmail,
                page_url: window.location.href,
                referrer: document.referrer,
                website: this.elements.honeypot ? this.elements.honeypot.value : ''
            }).then(function(data) {
                if (data.success) {
                    // Update conversation ID if new
                    if (!self.state.conversationId) {
                        self.state.conversationId = data.conversation_id;
                        self.saveSession();
                    }

                    // Update temp message
                    const tempEl = self.elements.messagesList.querySelector('[data-id="' + tempMsg.id + '"]');
                    if (tempEl) {
                        tempEl.dataset.id = data.message_id;
                        tempEl.classList.remove('ka-live-chat__message--pending');
                    }

                    self.state.lastMessageId = data.message_id;
                } else if (data.error === 'rate_limit') {
                    self.showError(self.strings.errorRateLimit);
                    // Remove temp message
                    self.removeMessage(tempMsg.id);
                } else {
                    self.showError(self.strings.errorNetwork);
                    self.removeMessage(tempMsg.id);
                }
            }).catch(function() {
                self.showError(self.strings.errorNetwork);
                self.removeMessage(tempMsg.id);
            }).finally(function() {
                self.state.isLoading = false;
                self.elements.sendBtn.disabled = false;
                self.elements.textarea.focus();
            });
        },

        /**
         * Add message to UI
         *
         * @param {Object} msg Message object
         */
        addMessage: function(msg) {
            this.state.messages.push(msg);

            const msgEl = document.createElement('div');
            msgEl.className = 'ka-live-chat__message ka-live-chat__message--' + msg.type;
            if (msg.pending) {
                msgEl.classList.add('ka-live-chat__message--pending');
            }
            msgEl.dataset.id = msg.id;

            const textEl = document.createElement('div');
            textEl.className = 'ka-live-chat__message-text';
            textEl.textContent = msg.text;
            msgEl.appendChild(textEl);

            const timeEl = document.createElement('div');
            timeEl.className = 'ka-live-chat__message-time';
            timeEl.textContent = this.formatTime(msg.time);
            msgEl.appendChild(timeEl);

            this.elements.messagesList.appendChild(msgEl);
            this.scrollToBottom();
        },

        /**
         * Remove message from UI
         *
         * @param {string|number} id Message ID
         */
        removeMessage: function(id) {
            const el = this.elements.messagesList.querySelector('[data-id="' + id + '"]');
            if (el) {
                el.remove();
            }
            this.state.messages = this.state.messages.filter(function(m) {
                return m.id !== id;
            });
        },

        /**
         * Render all messages
         */
        renderMessages: function() {
            const self = this;
            this.elements.messagesList.innerHTML = '';

            // Add welcome message first if chatting
            if (this.state.isChatting && this.strings.welcomeMessage && !this.state.messages.length) {
                this.addMessage({
                    type: 'welcome',
                    text: this.strings.welcomeMessage,
                    time: new Date().toISOString()
                });
            }

            this.state.messages.forEach(function(msg) {
                self.addMessage(msg);
            });
        },

        /**
         * Scroll messages to bottom
         */
        scrollToBottom: function() {
            const container = this.elements.messagesContainer;
            if (container) {
                container.scrollTop = container.scrollHeight;
            }
        },

        /**
         * Start polling for new messages
         */
        startPolling: function() {
            if (this.state.pollInterval) {
                return;
            }

            const self = this;
            const interval = this.config.pollInterval || 4000;

            this.state.pollInterval = setInterval(function() {
                if (self.state.isOpen && self.state.conversationId) {
                    self.pollMessages();
                }
            }, interval);
        },

        /**
         * Stop polling
         */
        stopPolling: function() {
            if (this.state.pollInterval) {
                clearInterval(this.state.pollInterval);
                this.state.pollInterval = null;
            }
        },

        /**
         * Poll for new messages
         */
        pollMessages: function() {
            const self = this;

            this.apiRequest('messages/poll', {
                visitor_id: this.state.visitorId,
                conversation_id: this.state.conversationId,
                after_id: this.state.lastMessageId
            }, 'GET').then(function(data) {
                if (data.messages && data.messages.length) {
                    data.messages.forEach(function(msg) {
                        // Don't duplicate
                        const exists = self.state.messages.some(function(m) {
                            return m.id === msg.id;
                        });

                        if (!exists) {
                            self.addMessage(msg);
                            self.state.lastMessageId = msg.id;

                            // Update badge if closed
                            if (!self.state.isOpen && msg.type === 'admin') {
                                self.updateBadge((parseInt(self.elements.badge.textContent, 10) || 0) + 1);
                            }
                        }
                    });
                }
            });
        },

        /**
         * Mark messages as read
         */
        markAsRead: function() {
            this.updateBadge(0);

            this.apiRequest('messages/read', {
                visitor_id: this.state.visitorId,
                conversation_id: this.state.conversationId
            });
        },

        /**
         * Update unread badge
         *
         * @param {number} count Unread count
         */
        updateBadge: function(count) {
            if (this.elements.badge) {
                this.elements.badge.textContent = count;
                this.elements.badge.style.display = count > 0 ? 'flex' : 'none';
            }
        },

        /**
         * Initialize conversation via API
         *
         * @returns {Promise}
         */
        initConversation: function() {
            return this.apiRequest('conversation/init', {
                visitor_id: this.state.visitorId,
                name: this.state.visitorName,
                email: this.state.visitorEmail,
                page_url: window.location.href,
                referrer: document.referrer
            });
        },

        /**
         * Make API request
         *
         * @param {string} endpoint API endpoint
         * @param {Object} data Request data
         * @param {string} method HTTP method
         * @returns {Promise}
         */
        apiRequest: function(endpoint, data, method) {
            const self = this;
            method = method || 'POST';

            let url = this.config.restUrl + '/' + endpoint;

            const options = {
                method: method,
                headers: {
                    'Content-Type': 'application/json',
                    'X-WP-Nonce': this.config.nonce
                }
            };

            if (method === 'GET' && data) {
                const params = new URLSearchParams();
                Object.keys(data).forEach(function(key) {
                    if (data[key] !== undefined && data[key] !== null) {
                        params.append(key, data[key]);
                    }
                });
                url += '?' + params.toString();
            } else if (data) {
                options.body = JSON.stringify(data);
            }

            return fetch(url, options)
                .then(function(response) {
                    return response.json();
                })
                .catch(function(error) {
                    // console.error('KingLiveChat API Error:', error);
                    throw error;
                });
        },

        /**
         * Show error message
         *
         * @param {string} message Error message
         */
        showError: function(message) {
            // Simple alert for now, could be improved
            // console.error('KingLiveChat:', message);
        },

        /**
         * Save session to localStorage
         */
        saveSession: function() {
            const sessionData = {
                name: this.state.visitorName,
                email: this.state.visitorEmail,
                conversationId: this.state.conversationId
            };
            localStorage.setItem('ka_chat_session', JSON.stringify(sessionData));
        },

        /**
         * Format time for display
         *
         * @param {string} dateStr ISO date string
         * @returns {string}
         */
        formatTime: function(dateStr) {
            if (!dateStr) return '';
            
            const date = new Date(dateStr);
            const now = new Date();
            const diff = Math.floor((now - date) / 1000);

            if (diff < 60) {
                return this.strings.justNow || 'Just now';
            }

            if (diff < 3600) {
                return Math.floor(diff / 60) + 'm ago';
            }

            if (diff < 86400 && date.getDate() === now.getDate()) {
                return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
            }

            return date.toLocaleDateString([], { month: 'short', day: 'numeric' }) + 
                   ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
        },

        /**
         * Validate email format
         *
         * @param {string} email Email address
         * @returns {boolean}
         */
        isValidEmail: function(email) {
            return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
        },

        /**
         * Generate UUID v4
         *
         * @returns {string}
         */
        generateUUID: function() {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                const r = Math.random() * 16 | 0;
                const v = c === 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        },

        /**
         * Get cookie value
         *
         * @param {string} name Cookie name
         * @returns {string|null}
         */
        getCookie: function(name) {
            const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
            return match ? match[2] : null;
        },

        /**
         * Set cookie
         *
         * @param {string} name Cookie name
         * @param {string} value Cookie value
         * @param {number} days Days until expiry
         */
        setCookie: function(name, value, days) {
            const date = new Date();
            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
            document.cookie = name + '=' + value + ';expires=' + date.toUTCString() + ';path=/;SameSite=Lax';
        },

        /**
         * Validate contact form
         */
        validateContactForm: function() {
            const requireName = this.config.options.requireName;
            const requireEmail = this.config.options.requireEmail;
            
            let isValid = true;

            if (requireName && this.elements.nameInput) {
                isValid = isValid && this.elements.nameInput.value.trim().length > 0;
            }

            if (requireEmail && this.elements.emailInput) {
                const email = this.elements.emailInput.value.trim();
                isValid = isValid && this.isValidEmail(email);
            }

            if (this.elements.messageTextarea) {
                isValid = isValid && this.elements.messageTextarea.value.trim().length > 0;
            }

            if (this.elements.submitBtn) {
                this.elements.submitBtn.disabled = !isValid;
            }
        },

        /**
         * Submit contact form
         */
        submitContactForm: function() {
            const self = this;

            // Check honeypot
            if (this.elements.honeypot && this.elements.honeypot.value) {
                return;
            }

            // Get values
            const name = this.elements.nameInput ? this.elements.nameInput.value.trim() : '';
            const email = this.elements.emailInput ? this.elements.emailInput.value.trim() : '';
            const subject = this.elements.subjectInput ? this.elements.subjectInput.value.trim() : '';
            const message = this.elements.messageTextarea ? this.elements.messageTextarea.value.trim() : '';

            if (!message) {
                return;
            }

            // Disable submit
            if (this.elements.submitBtn) {
                this.elements.submitBtn.disabled = true;
                this.elements.submitBtn.textContent = this.strings.sending || 'Sending...';
            }

            // Send via REST API
            this.apiCall('/support/contact', 'POST', {
                visitor_id: this.state.visitorId,
                name: name,
                email: email,
                subject: subject,
                message: message,
                page_url: window.location.href,
                referrer: document.referrer
            }).then(function(data) {
                if (data.success) {
                    self.showSuccessScreen();
                } else {
                    self.showFormError(data.message || self.strings.errorNetwork);
                }
            }).catch(function(error) {
                self.showFormError(self.strings.errorNetwork);
            }).finally(function() {
                if (self.elements.submitBtn) {
                    self.elements.submitBtn.disabled = false;
                    self.elements.submitBtn.textContent = self.strings.submitButton || 'Send Message';
                }
            });
        },

        /**
         * Show success screen
         */
        showSuccessScreen: function() {
            if (this.elements.contactForm) {
                this.elements.contactForm.style.display = 'none';
            }
            if (this.elements.successScreen) {
                this.elements.successScreen.style.display = 'flex';
            }
        },

        /**
         * Reset contact form
         */
        resetContactForm: function() {
            if (this.elements.nameInput) {
                this.elements.nameInput.value = '';
            }
            if (this.elements.emailInput) {
                this.elements.emailInput.value = '';
            }
            if (this.elements.subjectInput) {
                this.elements.subjectInput.value = '';
            }
            if (this.elements.messageTextarea) {
                this.elements.messageTextarea.value = '';
            }
            
            if (this.elements.successScreen) {
                this.elements.successScreen.style.display = 'none';
            }
            if (this.elements.contactForm) {
                this.elements.contactForm.style.display = 'flex';
            }
            
            this.validateContactForm();
        },

        /**
         * Show form error
         *
         * @param {string} message Error message
         */
        showFormError: function(message) {
            // For now just alert, could be improved
            alert(message);
        }
    };

    // Initialize on DOM ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() {
            KingLiveChat.init();
        });
    } else {
        KingLiveChat.init();
    }

    // Expose globally for debugging
    window.KingLiveChat = KingLiveChat;

})();

```
